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/.gitattributes b/.gitattributes new file mode 100644 index 000000000..c28c5b804 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +Server/data/cache/* filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 4daf3c49b..6ecd66985 100644 --- a/.gitignore +++ b/.gitignore @@ -1,46 +1,44 @@ -/Management-Server/build/ -/Management-Server/build/classes/ -/Management-Server/build/tmp/ -/Management-Server/build/kotlin/ -/Management-Server/build/generated/ -/Client/build/ -/Client/build/classes/ -/Client/build/tmp/ -/Client/build/kotlin/ -/Client/build/generated/ -/Server/build/ -/Server/build/classes/ -/Server/build/tmp/ -/Server/build/kotlin/ -/Server/build/generated/ -/Server/data/eco/bot_offers.json -/File-Server/build/ -/File-Server/build/classes/ -/File-Server/build/tmp/ -/File-Server/build/kotlin/ -/File-Server/build/generated/ -**/.idea/workspace.xml -**/.idea/tasks.xml -Server/**/*.class -CompiledServer/* -CompiledServer/ -Server/gradle/ -Server/build/ -Server/.gradle/ -Client/gradle/ -Client/build/ -Client/.gradle/ +builddir +logs +out/ +Management-Server/target/* Management-Server/.gradle Management-Server/build/ Management-Server/org/ +Server/target/* +Server/data/eco/bot_offers.json +Server/gradle/ +Server/**/*.class +Server/build/ +Server/.gradle/ Server/data/eco/grand_exchange_db.emp -Single-Player/* -Single-Player/ +Server/data/eco/grandexchange.db +Server/data/serverstore/ +Server/data/global_kill_stats.json +Server/ge_test.db +Server/latestdump.txt +Management-Server/managementprops/ +Server/worldprops/* +!Server/worldprops/default.conf + +**/.idea/workspace.xml +**/.idea/tasks.xml docker .gradle gradle .idea .idea/ -Server/worldprops/ -Management-Server/managementprops/ -/Development-Client/ \ No newline at end of file +**/*.swp +**/*.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 new file mode 100644 index 000000000..03a4e5b25 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,41 @@ +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 -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" + +image: maven:3-openjdk-11-slim + +workflow: + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + +before_script: + - 'free -mh' + - 'apt-get update -q' + +# Cache downloaded dependencies and plugins between builds. +# To keep cache across branches add 'key: "$CI_JOB_NAME"' +cache: + paths: + - .m2/repository + +verify:jdk11: + stage: build + script: + - 'cd Server' + - 'mvn $MAVEN_CLI_OPTS verify' + except: + variables: + - $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + +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: + - $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH diff --git a/.gitlab/merge_request_templates/.gitkeep b/.gitlab/.gitkeep similarity index 100% rename from .gitlab/merge_request_templates/.gitkeep rename to .gitlab/.gitkeep diff --git a/.gitlab/issue_templates/Bug Report.md b/.gitlab/issue_templates/Bug Report.md deleted file mode 100644 index 4bfda573a..000000000 --- a/.gitlab/issue_templates/Bug Report.md +++ /dev/null @@ -1,8 +0,0 @@ -### Description of issue -Describe in detail what exactly happened. No "it no workie" will be accepted. Clear and concise definitions of what *exactly* the problem is. - -### What should have happened -Write out what the expected behavior/etc is. - -### Reproduction steps (if applicable) -Inform us how to reproduce this issue so we can effectively debug it. 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/.gitlab/issue_templates/Proposal.md b/.gitlab/issue_templates/Proposal.md deleted file mode 100644 index 48b42c06a..000000000 --- a/.gitlab/issue_templates/Proposal.md +++ /dev/null @@ -1,8 +0,0 @@ -### What is being proposed -Write a clear and verbose description here of what exactly is being proposed. - -### Pros -* List out bullet points here of pros - -### Cons -* List out bullet points here of potential cons diff --git a/.gitlab/merge_request_templates/default_mr.md b/.gitlab/merge_request_templates/default_mr.md deleted file mode 100644 index 44377a2f0..000000000 --- a/.gitlab/merge_request_templates/default_mr.md +++ /dev/null @@ -1,7 +0,0 @@ -Put a brief and concise description of the changes you made here. - -* [ ] Detailed Changes added to CHANGELOG (Required to be merged) - -* [ ] Changes tested to ensure they function as expected (Required to be merged) - -* [x] I acknowledge that this contribution will be released under the AGPL license. diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 7916c5182..000000000 --- a/CHANGELOG +++ /dev/null @@ -1,68 +0,0 @@ -- Trees regrow from their stumps over time, or from Hydra's regrowth scrolls. -- Diseased trees can be cured with secateurs. -- Willow branches can be harvested from willow trees. -- Giant ent improves yields of certain patch types by 50%. -- Replaced all Druids with their RSHD counterparts, released the same time as Summoning and the Taverley Graphical Update. -- This also means the Druidess is now correctly added to the game, and can be attacked. -- Druid and Druidess added to Druid's Cricle (5 Druid/Druidess) -- All HD Druids/Druidess' now have the right drop table assigned (all have identical drops to the original Druid NPC) -- All Druid and Druidess NPCs animations corrected -- All Druid and Druidess NPCs attack speed corrected -- All Druid and Druidess NPCs stats corrected -- All Druid and Druidess NPCs correct respawn rate of 25 ticks (15 seconds) -- Pen (Taverley/Yanille Pet Shop) (ID 28341) examine added -- Counter (Taverley/Yanille Pet Shop) (IDs 28357,28358,28364) examine added -- The puppy spawns wandering inside the Yanille Pet Shop's pen have been corrected to the right NPCs -- The puppy spawns wandering isnide the Taverley Pet Shop's pen have been corrected to the right NPCs -- Gaius' Two Handed Shop (Taverley) name correction -- Gaius' Two Handed Shop (Taverley) stock correction -- Relocated Bernald (Burthorpe Vineyard) to his grapevines -- Bernald (NPC ID 2580) examine added -- Sanfew (NPC ID 454) examine added -- Jatix (NPC ID 587) examine added -- Kaqemeex (NPC ID 455) examine added -- Pikkupstix (NPC ID 6971) examine added -- Gaius (NPC ID 586) examine added -- Sylas (NPC ID 5987) examine added -- Druid's robe bottom (ID 538) examine corrected -- Druid's robe top (ID 540) examine corrected -- removed erroneous runite items from Ice Warrior droptable -- relocated one Imp spawn near Ardougne Monastery a few tiles to resolve a poor pathing issue -- removed [REDACTED] trapdoor -- Removed a whole bunch of Dragon gear, including dragon boots, and combat bracelets that were erroneously on the Revenant ork, Revenant knight, Revenant cyclops, Revenant hellhound, Revenant demon, Revenant dark beast and Revenant dragon -- Added Clue Scroll (easy) drop to Revenant imp, Revenant goblin and Revenant icefiend -- Added Clue Scroll (medium) drop to Revenant werewolf, Revenant hobgoblin, Revenant vampire, Revenant pyrefiend and Revenant cyclops -- Added Clue Scroll (hard) drop to Revenant ork, Revenant knight, Revenant hellhound, Revenant demon, Revenant dark beast and Revenant dragon -- Removed all Rune platebody drops across all Revenants -- Removed all Rune pickaxe drops across all Revenants -- Removed all Rune axe drops across all Revenants -- Removed all Rune scimitar drops across all Revenants -- Removed ridiculous noted Manta ray drop across all Revenants -- Reverted the absurd 50-100 noted Sharks drop to a more believable number of 4 across all Revenants -- Reverted the noted tuna potato drop to a maximum of 5 across all Revenants -- Fixed a potential exploit with defensive brews and titan effects -- Fixed a potential exploit with chinchompas -- Addressed a potentially serious issue with charm droprates -< ---- ABOVE Released NOVEMBER 1, 2021 https://gitlab.com/2009scape/2009scape/-/tags/Nov-1-2021 ---- > -- Removed halloween decorations -- Alkharid Upstairs locations can now be entered exited correctly -- Fix Yak Hide Armor crafting level requirements -- Spiky Vambraces crafing added -- Fixed Typo in Professor Oddenstein's dialogue -- Random Events can no longer spawn in the wilderness. -- Fairy rings emit a sound when teleporting. -- Void set effects now work more uniformly. -- Summoning familiars now grant combat xp when attacking -- Canoe travel interface now animates correctly -- Fix how the attack skill cape perk is calculated. It should now have a much more noticeable effect -- Properly track leaf-bladed sword drops in ::stats command -- Make broad bolts tradeable and fix their ranged strength -< ---- ABOVE Released NOVEMBER 22, 2021 https://gitlab.com/2009scape/2009scape/-/tags/Nov-22-2021 ---- > -- Animations for passing through Isafdar Dense Forest added -- Implement Wire Traps, Leaf Traps, and Stick Traps in Isafdar -- Quest point cape/hood no longer unequip on login if wearing QP hood -- Random Event Genie lamps now scale exp correctly -- Void mace now consumes runes correctly -- Add remote kicking support to the Management Server -- Added Christmas Event :) -< --- ABOVE Released December 4, 2021 https://gitlab.com/2009scape/2009scape/-/tags/Dec-4-2021 ---- > \ No newline at end of file diff --git a/Client/.gitignore b/Client/.gitignore deleted file mode 100644 index 11fa5ede3..000000000 --- a/Client/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -bin/** -out/** -data/logs/** -data/profile/** -.idea/** -/bin -.DS_Store** -.project** -.classpath** -.class** -/bin/ -/out/ -data/cache/ -*.iml -*.eml -*.userlibraries -data/players/** -/build/ -/build/classes/ -/build/resources/ -/build/tmp/ -/build/generated/ -/build/libs/ -/build/kotlin/ diff --git a/Client/build.gradle b/Client/build.gradle deleted file mode 100644 index 93a61c6b5..000000000 --- a/Client/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -apply plugin: 'application' - -archivesBaseName = 'client' - -mainClassName = 'org.rs09.client.GameLaunch' - -repositories { - mavenCentral() -} - -dependencies { - implementation 'com.googlecode.json-simple:json-simple:1.1.1' - implementation("com.github.ajalt.mordant:mordant:2.0.0-alpha2") - implementation "org.jetbrains:markdown:0.2.4" - implementation files('libs/clientlibs.jar', 'libs/discord-rpc.jar') -} - -jar { - manifest { - attributes 'Main-Class': 'org.rs09.client.GameLaunch' - } - from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } } -} diff --git a/Client/config.json b/Client/config.json deleted file mode 100644 index d8dbca06a..000000000 --- a/Client/config.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "ip_address": "localhost", - "ip_management" : "localhost", - "server_port": 43594, - "wl_port": 5555, - "js5_port": 43593, - "world": 1, - - "customization": { - "login_theme": "newbie melody", - - "right_click_menu": { - "background": { - "color": "#0fab00", - "opacity": "70" - }, - "title_bar": { - "color": "#000000", - "opacity": "255", - "font_color": "#FFFFFF" - }, - "border": { - "color": "#FFFFFF", - "opacity": "255" - }, - "styles": { - "Presets provide default customizations.": "rs3, classic, or custom. custom allows you to define your own values above. Classic is standard 2009.", - "presets": "classic", - "rs3border": false - } - }, - "rendering_options": { - "technical": { - "render_distance_increase": true - }, - "skybox": { - "skybox_color" : "Coming in a future update..." - } - } - }, - - "debug": { - "item_debug": false, - "object_debug": false, - "npc_debug": false, - "hd_login_region_debug": false, - "hd_login_region_debug_verbose": false, - "cache_debug": false, - "world_map_debug": false - } -} \ No newline at end of file diff --git a/Client/hs_err_pid23465.log b/Client/hs_err_pid23465.log deleted file mode 100644 index a187b9e27..000000000 --- a/Client/hs_err_pid23465.log +++ /dev/null @@ -1,1070 +0,0 @@ -# -# A fatal error has been detected by the Java Runtime Environment: -# -# SIGSEGV (0xb) at pc=0x0000000000013776, pid=23465, tid=0x00007f557726c700 -# -# JRE version: OpenJDK Runtime Environment (8.0_275-b01) (build 1.8.0_275-8u275-b01-0ubuntu1~18.04-b01) -# Java VM: OpenJDK 64-Bit Server VM (25.275-b01 mixed mode linux-amd64 compressed oops) -# Problematic frame: -# C 0x0000000000013776 -# -# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again -# -# If you would like to submit a bug report, please visit: -# http://bugreport.java.com/bugreport/crash.jsp -# - ---------------- T H R E A D --------------- - -Current thread is native thread - -siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000013776 - -Registers: -RAX=0x000000000000006e, RBX=0x00007f556408bf90, RCX=0x0000000000000004, RDX=0x0000000000000000 -RSP=0x00007f557726bd38, RBP=0x00007f557726bda0, RSI=0x0000000000000000, RDI=0x0000000000000002 -R8 =0x00007f556408bbf8, R9 =0x00007f556408bc20, R10=0x00007f557726bd60, R11=0x0000000000000000 -R12=0x00007f557726bfc0, R13=0x0000000000000000, R14=0x00007f556408bfa8, R15=0x00007f5577aee5f0 -RIP=0x0000000000013776, EFLAGS=0x0000000000010206, CSGSFS=0x002b000000000033, ERR=0x0000000000000014 - TRAPNO=0x000000000000000e - -Top of Stack: (sp=0x00007f557726bd38) -0x00007f557726bd38: 00007f5577287fda 0000006e7726bd90 -0x00007f557726bd48: 00007f556408bc20 00007f557726bd80 -0x00007f557726bd58: 0000000060258e9f 0000000060258e9f -0x00007f557726bd68: 00000000256819a5 00000000256819a5 -0x00007f557726bd78: 00000000256819a5 0000000060258e9f -0x00007f557726bd88: 00007f557726be00 00007f557726be58 -0x00007f557726bd98: 00007f556408bc20 00007f557726bdd0 -0x00007f557726bda8: 00007f5577287f35 1662ca5438978fa5 -0x00007f557726bdb8: 00007f557726be00 00007f557726be58 -0x00007f557726bdc8: 00007f556408bc20 00007f557726be20 -0x00007f557726bdd8: 00007f5577286fcf 00007f557726be58 -0x00007f557726bde8: 00007f557726be58 00007f556408bc20 -0x00007f557726bdf8: 1662ca541aca2aa5 1662ca5438978fa5 -0x00007f557726be08: 00007f5577293fb8 00007f557726be58 -0x00007f557726be18: 00007f556408bc20 00007f557726be70 -0x00007f557726be28: 00007f5577286e9d 000000017726be50 -0x00007f557726be38: 00007f557726be58 00007f556408bbf0 -0x00007f557726be48: 00007f556408bfc0 00007f557726be70 -0x00007f557726be58: 00007f556408bbf8 00007f556408bf01 -0x00007f557726be68: 00007f556408bfc0 00007f557726be90 -0x00007f557726be78: 00007f5577286e05 00007f556408bfc0 -0x00007f557726be88: 00007f5564066740 00007f557726beb0 -0x00007f557726be98: 00007f5577286dd5 00007f556408bf90 -0x00007f557726bea8: 00007f556408bfc0 00007f557726bed0 -0x00007f557726beb8: 00007f5577286bc9 0000000000000000 -0x00007f557726bec8: 00007f556408bfa8 0000000000000000 -0x00007f557726bed8: 00007f55e3289a50 0000000000000000 -0x00007f557726bee8: 0000000000000000 0000000000000000 -0x00007f557726bef8: 00007f55e45bb6db 0000000000000000 -0x00007f557726bf08: 00007f557726c700 00007f557726c700 -0x00007f557726bf18: 3645f94e10e25851 00007f557726bfc0 -0x00007f557726bf28: 0000000000000000 00007f556408bfa8 - -Instructions: (pc=0x0000000000013776) -0x0000000000013756: -[error occurred during error reporting (printing registers, top of stack, instructions near pc), id 0xb] - -Register to memory mapping: - -RAX=0x000000000000006e is an unknown value -RBX=0x00007f556408bf90 is an unknown value -RCX=0x0000000000000004 is an unknown value -RDX=0x0000000000000000 is an unknown value -RSP=0x00007f557726bd38 is an unknown value -RBP=0x00007f557726bda0 is an unknown value -RSI=0x0000000000000000 is an unknown value -RDI=0x0000000000000002 is an unknown value -R8 =0x00007f556408bbf8 is an unknown value -R9 =0x00007f556408bc20 is an unknown value -R10=0x00007f557726bd60 is an unknown value -R11=0x0000000000000000 is an unknown value -R12=0x00007f557726bfc0 is an unknown value -R13=0x0000000000000000 is an unknown value -R14=0x00007f556408bfa8 is an unknown value -R15=0x00007f5577aee5f0 is pointing into the stack for thread: 0x00007f556c001000 - - -Stack: [0x00007f5576a6d000,0x00007f557726d000], sp=0x00007f557726bd38, free space=8187k -Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) -C 0x0000000000013776 - -[error occurred during error reporting (printing native stack), id 0xb] - - ---------------- P R O C E S S --------------- - -VM state:not at safepoint (normal execution) - -VM Mutex/Monitor currently owned by a thread: None - -heap address: 0x0000000085600000, size: 1962 MB, Compressed Oops mode: 32-bit -Narrow klass base: 0x0000000000000000, Narrow klass shift: 3 -Compressed class space size: 1073741824 Address: 0x0000000100000000 - -Heap: - PSYoungGen total 23040K, used 18631K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 82% used [0x00000000d7200000,0x00000000d8411e70,0x00000000d8800000) - from space 512K, 25% used [0x00000000da100000,0x00000000da120000,0x00000000da180000) - to space 512K, 0% used [0x00000000da080000,0x00000000da080000,0x00000000da100000) - ParOldGen total 74752K, used 51620K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 69% used [0x0000000085600000,0x0000000088869058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K - -Card table byte_map: [0x00007f55e13ee000,0x00007f55e17c4000] byte_map_base: 0x00007f55e0fc3000 - -Marking Bits: (ParMarkBitMap*) 0x00007f55e4597680 - Begin Bits: [0x00007f55c92b0000, 0x00007f55cb158000) - End Bits: [0x00007f55cb158000, 0x00007f55cd000000) - -Polling page: 0x00007f55e541a000 - -CodeCache: size=245760Kb used=20700Kb max_used=21592Kb free=225059Kb - bounds [0x00007f55cd000000, 0x00007f55ce560000, 0x00007f55dc000000] - total_blobs=4020 nmethods=3358 adapters=575 - compilation: enabled - -Compilation events (10 events): -Event: 13195.495 Thread 0x00007f55dc0bc800 5336 % 4 org.runite.client.Model_Sub1::method2017 @ 770 (1484 bytes) -Event: 13195.667 Thread 0x00007f55dc0bc800 nmethod 5336% 0x00007f55cdd1d210 code [0x00007f55cdd1d4e0, 0x00007f55cdd21cb8] -Event: 13199.043 Thread 0x00007f55dc0c1000 5337 ! 3 java.awt.KeyboardFocusManager::getGlobalFocusedWindow (20 bytes) -Event: 13199.043 Thread 0x00007f55dc0c1000 nmethod 5337 0x00007f55ce02a210 code [0x00007f55ce02a3c0, 0x00007f55ce02a848] -Event: 13199.043 Thread 0x00007f55dc0c1000 5338 3 sun.awt.X11.XInputMethod::getParent (5 bytes) -Event: 13199.043 Thread 0x00007f55dc0c1000 nmethod 5338 0x00007f55cd129b50 code [0x00007f55cd129cc0, 0x00007f55cd129ee8] -Event: 13206.002 Thread 0x00007f55dc0c1000 5339 3 java.awt.Window::eventEnabled (138 bytes) -Event: 13206.003 Thread 0x00007f55dc0c1000 nmethod 5339 0x00007f55cd5aa750 code [0x00007f55cd5aa8e0, 0x00007f55cd5aace8] -Event: 13211.227 Thread 0x00007f55dc0c1000 5340 3 org.runite.client.Class3_Sub28_Sub16_Sub2:: (44 bytes) -Event: 13211.227 Thread 0x00007f55dc0c1000 nmethod 5340 0x00007f55cd5b0050 code [0x00007f55cd5b01e0, 0x00007f55cd5b0570] - -GC Heap History (10 events): -Event: 13214.142 GC heap before -{Heap before GC invocations=39930 (full 22): - PSYoungGen total 23040K, used 21608K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 95% used [0x00000000d7200000,0x00000000d8702028,0x00000000d8800000) - from space 512K, 18% used [0x00000000da080000,0x00000000da098000,0x00000000da100000) - to space 512K, 0% used [0x00000000da100000,0x00000000da100000,0x00000000da180000) - ParOldGen total 74752K, used 51540K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 68% used [0x0000000085600000,0x0000000088855058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -Event: 13214.143 GC heap after -Heap after GC invocations=39930 (full 22): - PSYoungGen total 23040K, used 128K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 0% used [0x00000000d7200000,0x00000000d7200000,0x00000000d8800000) - from space 512K, 25% used [0x00000000da100000,0x00000000da120000,0x00000000da180000) - to space 512K, 0% used [0x00000000da080000,0x00000000da080000,0x00000000da100000) - ParOldGen total 74752K, used 51556K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 68% used [0x0000000085600000,0x0000000088859058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -} -Event: 13214.415 GC heap before -{Heap before GC invocations=39931 (full 22): - PSYoungGen total 23040K, used 21646K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 95% used [0x00000000d7200000,0x00000000d87038a0,0x00000000d8800000) - from space 512K, 25% used [0x00000000da100000,0x00000000da120000,0x00000000da180000) - to space 512K, 0% used [0x00000000da080000,0x00000000da080000,0x00000000da100000) - ParOldGen total 74752K, used 51556K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 68% used [0x0000000085600000,0x0000000088859058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -Event: 13214.418 GC heap after -Heap after GC invocations=39931 (full 22): - PSYoungGen total 23040K, used 96K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 0% used [0x00000000d7200000,0x00000000d7200000,0x00000000d8800000) - from space 512K, 18% used [0x00000000da080000,0x00000000da098000,0x00000000da100000) - to space 512K, 0% used [0x00000000da100000,0x00000000da100000,0x00000000da180000) - ParOldGen total 74752K, used 51564K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 68% used [0x0000000085600000,0x000000008885b058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -} -Event: 13214.695 GC heap before -{Heap before GC invocations=39932 (full 22): - PSYoungGen total 23040K, used 21607K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 95% used [0x00000000d7200000,0x00000000d8701fb8,0x00000000d8800000) - from space 512K, 18% used [0x00000000da080000,0x00000000da098000,0x00000000da100000) - to space 512K, 0% used [0x00000000da100000,0x00000000da100000,0x00000000da180000) - ParOldGen total 74752K, used 51564K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 68% used [0x0000000085600000,0x000000008885b058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -Event: 13214.696 GC heap after -Heap after GC invocations=39932 (full 22): - PSYoungGen total 23040K, used 160K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 0% used [0x00000000d7200000,0x00000000d7200000,0x00000000d8800000) - from space 512K, 31% used [0x00000000da100000,0x00000000da128000,0x00000000da180000) - to space 512K, 0% used [0x00000000da080000,0x00000000da080000,0x00000000da100000) - ParOldGen total 74752K, used 51596K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 69% used [0x0000000085600000,0x0000000088863058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -} -Event: 13215.055 GC heap before -{Heap before GC invocations=39933 (full 22): - PSYoungGen total 23040K, used 21671K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 95% used [0x00000000d7200000,0x00000000d8701fc0,0x00000000d8800000) - from space 512K, 31% used [0x00000000da100000,0x00000000da128000,0x00000000da180000) - to space 512K, 0% used [0x00000000da080000,0x00000000da080000,0x00000000da100000) - ParOldGen total 74752K, used 51596K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 69% used [0x0000000085600000,0x0000000088863058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -Event: 13215.057 GC heap after -Heap after GC invocations=39933 (full 22): - PSYoungGen total 23040K, used 64K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 0% used [0x00000000d7200000,0x00000000d7200000,0x00000000d8800000) - from space 512K, 12% used [0x00000000da080000,0x00000000da090000,0x00000000da100000) - to space 512K, 0% used [0x00000000da100000,0x00000000da100000,0x00000000da180000) - ParOldGen total 74752K, used 51604K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 69% used [0x0000000085600000,0x0000000088865058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -} -Event: 13215.376 GC heap before -{Heap before GC invocations=39934 (full 22): - PSYoungGen total 23040K, used 21577K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 95% used [0x00000000d7200000,0x00000000d87027e0,0x00000000d8800000) - from space 512K, 12% used [0x00000000da080000,0x00000000da090000,0x00000000da100000) - to space 512K, 0% used [0x00000000da100000,0x00000000da100000,0x00000000da180000) - ParOldGen total 74752K, used 51604K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 69% used [0x0000000085600000,0x0000000088865058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -Event: 13215.377 GC heap after -Heap after GC invocations=39934 (full 22): - PSYoungGen total 23040K, used 128K [0x00000000d7200000, 0x00000000da180000, 0x0000000100000000) - eden space 22528K, 0% used [0x00000000d7200000,0x00000000d7200000,0x00000000d8800000) - from space 512K, 25% used [0x00000000da100000,0x00000000da120000,0x00000000da180000) - to space 512K, 0% used [0x00000000da080000,0x00000000da080000,0x00000000da100000) - ParOldGen total 74752K, used 51620K [0x0000000085600000, 0x0000000089f00000, 0x00000000d7200000) - object space 74752K, 69% used [0x0000000085600000,0x0000000088869058,0x0000000089f00000) - Metaspace used 21058K, capacity 21358K, committed 21632K, reserved 1069056K - class space used 1825K, capacity 1914K, committed 1920K, reserved 1048576K -} - -Deoptimization events (10 events): -Event: 9077.071 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55cdfd0ed8 method=org.runite.client.LinkableRSString.method729(BII)I @ 25 -Event: 9089.701 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55ce510604 method=org.runite.client.Class145.method2073(Lorg/runite/client/Class3_Sub2;Z)V @ 2339 -Event: 9089.722 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55ce4c119c method=org.runite.client.Class145.method2073(Lorg/runite/client/Class3_Sub2;Z)V @ 2339 -Event: 9362.755 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55ce38e4fc method=org.runite.client.Class110.method1683(IZIZLorg/runite/client/Class91;IIIII)V @ 195 -Event: 11578.468 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55cdc103dc method=org.runite.client.Class163_Sub1.method2211(I)V @ 67 -Event: 12675.849 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55cd6ec030 method=org.runite.client.Class20.method907(IIIIILorg/runite/client/GameObject;IJZ)V @ 34 -Event: 12675.885 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55ce10da20 method=org.runite.client.Class140_Sub3.method1957(IZLorg/runite/client/SequenceDefinition;IIIIILorg/runite/client/Model;IIII)Lorg/runite/client/Model; @ 670 -Event: 12675.901 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55cdf35cd8 method=org.runite.client.Class20.method907(IIIIILorg/runite/client/GameObject;IJZ)V @ 34 -Event: 12677.100 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55ce3c5588 method=org.runite.client.Class52.method1165([Lorg/runite/client/Class145;ILorg/runite/client/SequenceDefinition;Lorg/runite/client/SequenceDefinition;IIIIII)Lorg/runite/client/Mode -Event: 12677.100 Thread 0x00007f556c001000 Uncommon trap: reason=unstable_if action=reinterpret pc=0x00007f55cdc6803c method=org.runite.client.Model.method1875(Lorg/runite/client/Class3_Sub17;Lorg/runite/client/AnimationHeader;Lorg/runite/client/AnimationHeader;II[ZZZI[I)V @ 328 - -Classes redefined (0 events): -No events - -Internal exceptions (10 events): -Event: 8854.849 Thread 0x00007f556c001000 Implicit null exception at 0x00007f55ce452661 to 0x00007f55ce45ac5d -Event: 9003.685 Thread 0x00007f556c010800 Exception (0x00000000d81a0748) thrown at [/build/openjdk-8-ElpxQ7/openjdk-8-8u275-b01/src/hotspot/src/share/vm/prims/jni.cpp, line 711] -Event: 9004.727 Thread 0x00007f55dc48b800 Implicit null exception at 0x00007f55cd5e9ffe to 0x00007f55cd5eb329 -Event: 9004.727 Thread 0x00007f55dc48b800 Exception (0x00000000d745f630) thrown at [/build/openjdk-8-ElpxQ7/openjdk-8-8u275-b01/src/hotspot/src/share/vm/prims/jni.cpp, line 711] -Event: 9004.765 Thread 0x00007f55dc48b800 Exception (0x00000000d745ff60) thrown at [/build/openjdk-8-ElpxQ7/openjdk-8-8u275-b01/src/hotspot/src/share/vm/prims/jni.cpp, line 711] -Event: 9089.701 Thread 0x00007f556c001000 Implicit null exception at 0x00007f55ce505d85 to 0x00007f55ce5105e5 -Event: 9089.722 Thread 0x00007f556c001000 Implicit null exception at 0x00007f55ce4b7fed to 0x00007f55ce4c1191 -Event: 9321.599 Thread 0x00007f556c008800 Exception (0x00000000d85b7958) thrown at [/build/openjdk-8-ElpxQ7/openjdk-8-8u275-b01/src/hotspot/src/share/vm/prims/jni.cpp, line 711] -Event: 9322.643 Thread 0x00007f55dc48b800 Exception (0x00000000d78790f8) thrown at [/build/openjdk-8-ElpxQ7/openjdk-8-8u275-b01/src/hotspot/src/share/vm/prims/jni.cpp, line 711] -Event: 9322.680 Thread 0x00007f55dc48b800 Exception (0x00000000d7879a28) thrown at [/build/openjdk-8-ElpxQ7/openjdk-8-8u275-b01/src/hotspot/src/share/vm/prims/jni.cpp, line 711] - -Events (10 events): -Event: 13214.142 Executing VM operation: ParallelGCFailedAllocation -Event: 13214.143 Executing VM operation: ParallelGCFailedAllocation done -Event: 13214.415 Executing VM operation: ParallelGCFailedAllocation -Event: 13214.418 Executing VM operation: ParallelGCFailedAllocation done -Event: 13214.695 Executing VM operation: ParallelGCFailedAllocation -Event: 13214.696 Executing VM operation: ParallelGCFailedAllocation done -Event: 13215.055 Executing VM operation: ParallelGCFailedAllocation -Event: 13215.057 Executing VM operation: ParallelGCFailedAllocation done -Event: 13215.376 Executing VM operation: ParallelGCFailedAllocation -Event: 13215.377 Executing VM operation: ParallelGCFailedAllocation done - - -Dynamic libraries: -85600000-89f00000 rw-p 00000000 00:00 0 -89f00000-d7200000 ---p 00000000 00:00 0 -d7200000-da180000 rw-p 00000000 00:00 0 -da180000-100000000 ---p 00000000 00:00 0 -100000000-1001e0000 rw-p 00000000 00:00 0 -1001e0000-140000000 ---p 00000000 00:00 0 -55a7d472c000-55a7d472d000 r-xp 00000000 08:06 13375076 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java -55a7d492c000-55a7d492d000 r--p 00000000 08:06 13375076 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java -55a7d492d000-55a7d492e000 rw-p 00001000 08:06 13375076 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java -55a7d4d24000-55a7d4d45000 rw-p 00000000 00:00 0 [heap] -7f552c000000-7f552c021000 rw-p 00000000 00:00 0 -7f552c021000-7f5530000000 ---p 00000000 00:00 0 -7f5530000000-7f5530021000 rw-p 00000000 00:00 0 -7f5530021000-7f5534000000 ---p 00000000 00:00 0 -7f5534000000-7f5534021000 rw-p 00000000 00:00 0 -7f5534021000-7f5538000000 ---p 00000000 00:00 0 -7f5538000000-7f5538032000 rw-p 00000000 00:00 0 -7f5538032000-7f553c000000 ---p 00000000 00:00 0 -7f5540000000-7f5544000000 rw-s 00000000 00:01 111831 /memfd:pulseaudio (deleted) -7f5544000000-7f5548000000 rw-s 00000000 00:01 50104 /memfd:pulseaudio (deleted) -7f5548000000-7f554c000000 rw-s 00000000 00:01 109480 /memfd:pulseaudio (deleted) -7f554c000000-7f5550000000 rw-s 00000000 00:01 109480 /memfd:pulseaudio (deleted) -7f5550000000-7f5550021000 rw-p 00000000 00:00 0 -7f5550021000-7f5554000000 ---p 00000000 00:00 0 -7f55575f8000-7f55575f9000 ---p 00000000 00:00 0 -7f55575f9000-7f5557df9000 rw-p 00000000 00:00 0 -7f555b6eb000-7f555b82b000 rw-p 00000000 00:00 0 -7f555b82b000-7f555b8eb000 ---p 00000000 00:00 0 -7f555b8eb000-7f555b8ee000 ---p 00000000 00:00 0 -7f555b8ee000-7f555bbec000 rw-p 00000000 00:00 0 -7f555bbec000-7f555bbf8000 r-xp 00000000 08:06 13380751 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libicedtea-sound.so -7f555bbf8000-7f555bdf7000 ---p 0000c000 08:06 13380751 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libicedtea-sound.so -7f555bdf7000-7f555bdf8000 r--p 0000b000 08:06 13380751 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libicedtea-sound.so -7f555bdf8000-7f555bdf9000 rw-p 0000c000 08:06 13380751 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libicedtea-sound.so -7f555bdf9000-7f555bdff000 r-xp 00000000 08:06 13501817 /usr/lib/x86_64-linux-gnu/alsa-lib/libasound_module_pcm_pulse.so -7f555bdff000-7f555bffe000 ---p 00006000 08:06 13501817 /usr/lib/x86_64-linux-gnu/alsa-lib/libasound_module_pcm_pulse.so -7f555bffe000-7f555bfff000 r--p 00005000 08:06 13501817 /usr/lib/x86_64-linux-gnu/alsa-lib/libasound_module_pcm_pulse.so -7f555bfff000-7f555c000000 rw-p 00006000 08:06 13501817 /usr/lib/x86_64-linux-gnu/alsa-lib/libasound_module_pcm_pulse.so -7f555c000000-7f555c021000 rw-p 00000000 00:00 0 -7f555c021000-7f5560000000 ---p 00000000 00:00 0 -7f5560000000-7f5560021000 rw-p 00000000 00:00 0 -7f5560021000-7f5564000000 ---p 00000000 00:00 0 -7f5564000000-7f5564794000 rw-p 00000000 00:00 0 -7f5564794000-7f5568000000 ---p 00000000 00:00 0 -7f5568000000-7f5568021000 rw-p 00000000 00:00 0 -7f5568021000-7f556c000000 ---p 00000000 00:00 0 -7f556c000000-7f556c021000 rw-p 00000000 00:00 0 -7f556c021000-7f5570000000 ---p 00000000 00:00 0 -7f5570000000-7f557003b000 rw-p 00000000 00:00 0 -7f557003b000-7f5574000000 ---p 00000000 00:00 0 -7f5574087000-7f55741ff000 rw-s 00000000 00:01 589851 /SYSV00000000 (deleted) -7f55741ff000-7f5574216000 r-xp 00000000 08:06 11802674 /lib/x86_64-linux-gnu/libresolv-2.27.so -7f5574216000-7f5574415000 ---p 00017000 08:06 11802674 /lib/x86_64-linux-gnu/libresolv-2.27.so -7f5574415000-7f5574416000 r--p 00016000 08:06 11802674 /lib/x86_64-linux-gnu/libresolv-2.27.so -7f5574416000-7f5574417000 rw-p 00017000 08:06 11802674 /lib/x86_64-linux-gnu/libresolv-2.27.so -7f5574417000-7f5574419000 rw-p 00000000 00:00 0 -7f5574419000-7f55744a6000 r-xp 00000000 08:06 13376032 /usr/lib/x86_64-linux-gnu/libvorbisenc.so.2.0.11 -7f55744a6000-7f55746a5000 ---p 0008d000 08:06 13376032 /usr/lib/x86_64-linux-gnu/libvorbisenc.so.2.0.11 -7f55746a5000-7f55746c1000 r--p 0008c000 08:06 13376032 /usr/lib/x86_64-linux-gnu/libvorbisenc.so.2.0.11 -7f55746c1000-7f55746c2000 rw-p 000a8000 08:06 13376032 /usr/lib/x86_64-linux-gnu/libvorbisenc.so.2.0.11 -7f55746c2000-7f55746ec000 r-xp 00000000 08:06 13376030 /usr/lib/x86_64-linux-gnu/libvorbis.so.0.4.8 -7f55746ec000-7f55748eb000 ---p 0002a000 08:06 13376030 /usr/lib/x86_64-linux-gnu/libvorbis.so.0.4.8 -7f55748eb000-7f55748ec000 r--p 00029000 08:06 13376030 /usr/lib/x86_64-linux-gnu/libvorbis.so.0.4.8 -7f55748ec000-7f55748ed000 rw-p 0002a000 08:06 13376030 /usr/lib/x86_64-linux-gnu/libvorbis.so.0.4.8 -7f55748ed000-7f55748f4000 r-xp 00000000 08:06 13375709 /usr/lib/x86_64-linux-gnu/libogg.so.0.8.2 -7f55748f4000-7f5574af4000 ---p 00007000 08:06 13375709 /usr/lib/x86_64-linux-gnu/libogg.so.0.8.2 -7f5574af4000-7f5574af5000 r--p 00007000 08:06 13375709 /usr/lib/x86_64-linux-gnu/libogg.so.0.8.2 -7f5574af5000-7f5574af6000 rw-p 00008000 08:06 13375709 /usr/lib/x86_64-linux-gnu/libogg.so.0.8.2 -7f5574af6000-7f5574b6b000 r-xp 00000000 08:06 13374772 /usr/lib/x86_64-linux-gnu/libFLAC.so.8.3.0 -7f5574b6b000-7f5574d6b000 ---p 00075000 08:06 13374772 /usr/lib/x86_64-linux-gnu/libFLAC.so.8.3.0 -7f5574d6b000-7f5574d6c000 r--p 00075000 08:06 13374772 /usr/lib/x86_64-linux-gnu/libFLAC.so.8.3.0 -7f5574d6c000-7f5574d6d000 rw-p 00076000 08:06 13374772 /usr/lib/x86_64-linux-gnu/libFLAC.so.8.3.0 -7f5574d6d000-7f5574d7c000 r-xp 00000000 08:06 11801675 /lib/x86_64-linux-gnu/libapparmor.so.1.4.2 -7f5574d7c000-7f5574f7b000 ---p 0000f000 08:06 11801675 /lib/x86_64-linux-gnu/libapparmor.so.1.4.2 -7f5574f7b000-7f5574f7c000 r--p 0000e000 08:06 11801675 /lib/x86_64-linux-gnu/libapparmor.so.1.4.2 -7f5574f7c000-7f5574f7d000 rw-p 0000f000 08:06 11801675 /lib/x86_64-linux-gnu/libapparmor.so.1.4.2 -7f5574f7d000-7f5574f82000 r-xp 00000000 08:06 13374943 /usr/lib/x86_64-linux-gnu/libasyncns.so.0.3.1 -7f5574f82000-7f5575181000 ---p 00005000 08:06 13374943 /usr/lib/x86_64-linux-gnu/libasyncns.so.0.3.1 -7f5575181000-7f5575182000 r--p 00004000 08:06 13374943 /usr/lib/x86_64-linux-gnu/libasyncns.so.0.3.1 -7f5575182000-7f5575183000 rw-p 00005000 08:06 13374943 /usr/lib/x86_64-linux-gnu/libasyncns.so.0.3.1 -7f5575183000-7f55751f7000 r-xp 00000000 08:06 13375907 /usr/lib/x86_64-linux-gnu/libsndfile.so.1.0.28 -7f55751f7000-7f55753f6000 ---p 00074000 08:06 13375907 /usr/lib/x86_64-linux-gnu/libsndfile.so.1.0.28 -7f55753f6000-7f55753f9000 r--p 00073000 08:06 13375907 /usr/lib/x86_64-linux-gnu/libsndfile.so.1.0.28 -7f55753f9000-7f55753fa000 rw-p 00076000 08:06 13375907 /usr/lib/x86_64-linux-gnu/libsndfile.so.1.0.28 -7f55753fa000-7f55753fc000 rw-p 00000000 00:00 0 -7f55753fc000-7f5575404000 r-xp 00000000 08:06 11801864 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 -7f5575404000-7f5575604000 ---p 00008000 08:06 11801864 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 -7f5575604000-7f5575605000 r--p 00008000 08:06 11801864 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 -7f5575605000-7f5575606000 rw-p 00009000 08:06 11801864 /lib/x86_64-linux-gnu/libwrap.so.0.7.6 -7f5575606000-7f5575683000 r-xp 00000000 08:06 13501232 /usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-11.1.so -7f5575683000-7f5575882000 ---p 0007d000 08:06 13501232 /usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-11.1.so -7f5575882000-7f5575884000 r--p 0007c000 08:06 13501232 /usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-11.1.so -7f5575884000-7f5575885000 rw-p 0007e000 08:06 13501232 /usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-11.1.so -7f5575885000-7f55758d3000 r-xp 00000000 08:06 13372284 /usr/lib/x86_64-linux-gnu/libpulse.so.0.20.2 -7f55758d3000-7f5575ad2000 ---p 0004e000 08:06 13372284 /usr/lib/x86_64-linux-gnu/libpulse.so.0.20.2 -7f5575ad2000-7f5575ad4000 r--p 0004d000 08:06 13372284 /usr/lib/x86_64-linux-gnu/libpulse.so.0.20.2 -7f5575ad4000-7f5575ad5000 rw-p 0004f000 08:06 13372284 /usr/lib/x86_64-linux-gnu/libpulse.so.0.20.2 -7f5575ad5000-7f5575ad8000 ---p 00000000 00:00 0 -7f5575ad8000-7f5575bd6000 rw-p 00000000 00:00 0 -7f5575bd6000-7f5575bd9000 ---p 00000000 00:00 0 -7f5575bd9000-7f5575cd7000 rw-p 00000000 00:00 0 -7f5575cd7000-7f5575dd6000 r-xp 00000000 08:06 13374935 /usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 -7f5575dd6000-7f5575fd6000 ---p 000ff000 08:06 13374935 /usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 -7f5575fd6000-7f5575fdd000 r--p 000ff000 08:06 13374935 /usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 -7f5575fdd000-7f5575fde000 rw-p 00106000 08:06 13374935 /usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 -7f5576065000-7f5576074000 r-xp 00000000 08:06 13380752 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsoundalsa.so -7f5576074000-7f5576273000 ---p 0000f000 08:06 13380752 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsoundalsa.so -7f5576273000-7f5576274000 r--p 0000e000 08:06 13380752 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsoundalsa.so -7f5576274000-7f5576275000 rw-p 0000f000 08:06 13380752 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsoundalsa.so -7f5576275000-7f557627c000 r--s 00000000 08:06 17040393 /var/cache/fontconfig/f4ffd49aa80a0ccc205b41ea829259d4-le64.cache-7 -7f557627c000-7f5576283000 r--s 00000000 08:06 17039428 /var/cache/fontconfig/4be9850f182b35c1350b6bbf2e42601c-le64.cache-7 -7f5576283000-7f5576285000 r--s 00000000 08:06 17040391 /var/cache/fontconfig/6f2223e5edf04fbe83e635cfdbc9e8e3-le64.cache-7 -7f5576285000-7f5576290000 r--s 00000000 08:06 17040390 /var/cache/fontconfig/feb3b6ac46fa77cc6301a5ed3a5b0e40-le64.cache-7 -7f5576290000-7f5576295000 r--s 00000000 08:06 17040389 /var/cache/fontconfig/a5aa37d70fce65bda8b671f2e718c575-le64.cache-7 -7f5576295000-7f5576299000 r--s 00000000 08:06 17040388 /var/cache/fontconfig/528cfaa298957e7f374bef939e1d9f6c-le64.cache-7 -7f5576299000-7f557629b000 r--s 00000000 08:06 17040386 /var/cache/fontconfig/4f799d4f62cbdb7117d96620a361af99-le64.cache-7 -7f557629b000-7f557629f000 r--s 00000000 08:06 17040385 /var/cache/fontconfig/8e44b5c2eaa2fdadfe5c6fe9a38b7f2b-le64.cache-7 -7f557629f000-7f55762a2000 r--s 00000000 08:06 17040384 /var/cache/fontconfig/c355ee2b09f937783b5336cddb29f5df-le64.cache-7 -7f55762a2000-7f55762a8000 r--s 00000000 08:06 17040381 /var/cache/fontconfig/70119ed9faf785a7d1728c848ab99deb-le64.cache-7 -7f55762a8000-7f55762f4000 r--s 00000000 08:06 17040380 /var/cache/fontconfig/ff3b5010a9fba1773c8f88a6b255f56f-le64.cache-7 -7f557634d000-7f5576350000 ---p 00000000 00:00 0 -7f5576350000-7f557644e000 rw-p 00000000 00:00 0 -7f557644e000-7f5576450000 r--s 00011000 08:06 10093548 /home/ceik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.1/860340562250678d1a344907ac75754e259cdb14/hamcrest-core-1.1.jar -7f5576450000-7f5576452000 r--s 00003000 08:06 10093084 /home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar -7f5576452000-7f557645a000 r--s 00027000 08:06 10093082 /home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.10/6229be3465805c99db1142ad75e6c6ddeac0b04c/kotlin-stdlib-common-1.4.10.jar -7f557645a000-7f5576461000 r--s 00037000 08:06 10093550 /home/ceik/.gradle/caches/modules-2/files-2.1/junit/junit/4.10/e4f1766ce7404a08f45d859fb9c226fc9e41a861/junit-4.10.jar -7f5576461000-7f5576468000 r--s 00000000 08:06 13506147 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache -7f5576468000-7f5576469000 r-xp 00000000 08:06 13380592 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsound.so -7f5576469000-7f5576668000 ---p 00001000 08:06 13380592 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsound.so -7f5576668000-7f5576669000 r--p 00000000 08:06 13380592 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsound.so -7f5576669000-7f557666a000 rw-p 00001000 08:06 13380592 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsound.so -7f557666a000-7f557666d000 ---p 00000000 00:00 0 -7f557666d000-7f557696b000 rw-p 00000000 00:00 0 -7f557696b000-7f557696e000 ---p 00000000 00:00 0 -7f557696e000-7f5576a6c000 rw-p 00000000 00:00 0 -7f5576a6c000-7f5576a6d000 ---p 00000000 00:00 0 -7f5576a6d000-7f557726d000 rw-p 00000000 00:00 0 -7f557726d000-7f557729e000 r-xp 00000000 08:06 9701439 /home/ceik/.discord-rpc/libdiscord-rpc.so -7f557729e000-7f557749d000 ---p 00031000 08:06 9701439 /home/ceik/.discord-rpc/libdiscord-rpc.so -7f557749d000-7f557749e000 r--p 00030000 08:06 9701439 /home/ceik/.discord-rpc/libdiscord-rpc.so -7f557749e000-7f55774a0000 rw-p 00031000 08:06 9701439 /home/ceik/.discord-rpc/libdiscord-rpc.so -7f55774a0000-7f55774d6000 rw-p 00000000 00:00 0 -7f55774d6000-7f55774ee000 r-xp 00000000 08:06 19142392 /tmp/jna-3049732/jna346119487822175448.tmp (deleted) -7f55774ee000-7f55776ee000 ---p 00018000 08:06 19142392 /tmp/jna-3049732/jna346119487822175448.tmp (deleted) -7f55776ee000-7f55776ef000 rw-p 00018000 08:06 19142392 /tmp/jna-3049732/jna346119487822175448.tmp (deleted) -7f55776ef000-7f55776f2000 ---p 00000000 00:00 0 -7f55776f2000-7f55779f0000 rw-p 00000000 00:00 0 -7f55779f0000-7f55779f3000 ---p 00000000 00:00 0 -7f55779f3000-7f5577af1000 rw-p 00000000 00:00 0 -7f5577af1000-7f5577af4000 ---p 00000000 00:00 0 -7f5577af4000-7f5577bf2000 rw-p 00000000 00:00 0 -7f5577bf2000-7f5577bf4000 r-xp 00000000 08:06 13374855 /usr/lib/x86_64-linux-gnu/libXcomposite.so.1.0.0 -7f5577bf4000-7f5577df3000 ---p 00002000 08:06 13374855 /usr/lib/x86_64-linux-gnu/libXcomposite.so.1.0.0 -7f5577df3000-7f5577df4000 r--p 00001000 08:06 13374855 /usr/lib/x86_64-linux-gnu/libXcomposite.so.1.0.0 -7f5577df4000-7f5577df5000 rw-p 00002000 08:06 13374855 /usr/lib/x86_64-linux-gnu/libXcomposite.so.1.0.0 -7f5577df5000-7f5577dff000 r-xp 00000000 08:06 13374881 /usr/lib/x86_64-linux-gnu/libXrandr.so.2.2.0 -7f5577dff000-7f5577ffe000 ---p 0000a000 08:06 13374881 /usr/lib/x86_64-linux-gnu/libXrandr.so.2.2.0 -7f5577ffe000-7f5577fff000 r--p 00009000 08:06 13374881 /usr/lib/x86_64-linux-gnu/libXrandr.so.2.2.0 -7f5577fff000-7f5578000000 rw-p 0000a000 08:06 13374881 /usr/lib/x86_64-linux-gnu/libXrandr.so.2.2.0 -7f5578000000-7f5578021000 rw-p 00000000 00:00 0 -7f5578021000-7f557c000000 ---p 00000000 00:00 0 -7f557c000000-7f557c021000 rw-p 00000000 00:00 0 -7f557c021000-7f5580000000 ---p 00000000 00:00 0 -7f5580000000-7f5580021000 rw-p 00000000 00:00 0 -7f5580021000-7f5584000000 ---p 00000000 00:00 0 -7f5584000000-7f5584021000 rw-p 00000000 00:00 0 -7f5584021000-7f5588000000 ---p 00000000 00:00 0 -7f5588000000-7f5588021000 rw-p 00000000 00:00 0 -7f5588021000-7f558c000000 ---p 00000000 00:00 0 -7f558c000000-7f558c021000 rw-p 00000000 00:00 0 -7f558c021000-7f5590000000 ---p 00000000 00:00 0 -7f5590000000-7f5590002000 r--s 00000000 08:06 17040383 /var/cache/fontconfig/511690a04b25677670627778fd28e538-le64.cache-7 -7f5590002000-7f5590003000 r--s 00000000 08:06 17040382 /var/cache/fontconfig/39d58c487a2b698a485cbc3338412786-le64.cache-7 -7f5590003000-7f5590004000 r--s 00000000 08:06 17040379 /var/cache/fontconfig/5dc337c4d6f7e377db915cf8b2f8f360-le64.cache-7 -7f5590004000-7f5590005000 r--s 00000000 08:06 17040378 /var/cache/fontconfig/8a486f81e4c235da4683bbadeaf36764-le64.cache-7 -7f5590005000-7f559000b000 r--s 00000000 08:06 17040377 /var/cache/fontconfig/f0dcabaa770e5892e8b1bf50caa4d718-le64.cache-7 -7f559000b000-7f559000c000 r--s 00000000 08:06 17040376 /var/cache/fontconfig/657346adeb7ee6e1e270022ed45f3b3c-le64.cache-7 -7f559000c000-7f5590012000 r--s 00000000 08:06 17040375 /var/cache/fontconfig/e590d564dbbcb1616f18ec41673142ab-le64.cache-7 -7f5590012000-7f5590013000 r--s 00000000 08:06 17040374 /var/cache/fontconfig/f7fa73f2099ae251bc77fd626f3e0174-le64.cache-7 -7f5590013000-7f5590014000 r--s 00000000 08:06 17040373 /var/cache/fontconfig/8ad0cd72269f17dffdedb6f2e355311b-le64.cache-7 -7f5590014000-7f5590015000 r--s 00000000 08:06 17040372 /var/cache/fontconfig/470613ecaf9186df9d7a2dd5f528a5aa-le64.cache-7 -7f5590015000-7f5590021000 r--s 00000000 08:06 17040371 /var/cache/fontconfig/9b3861222e9b48ce410e7512f19354df-le64.cache-7 -7f5590021000-7f5590023000 r--s 00000000 08:06 17040370 /var/cache/fontconfig/9632cce8d60b9d317fc261cafebbed09-le64.cache-7 -7f5590023000-7f5590028000 r--s 00000000 08:06 17040369 /var/cache/fontconfig/692aa46cdcb6f2010e72f7715c416726-le64.cache-7 -7f5590028000-7f559002a000 r--s 00000000 08:06 17040368 /var/cache/fontconfig/980323017da4c4d21c1825b16079d14c-le64.cache-7 -7f559002a000-7f5590037000 r--s 00000000 08:06 17040367 /var/cache/fontconfig/a574ef10a92e55f10a01bdbd966a646e-le64.cache-7 -7f5590037000-7f559003b000 r--s 00000000 08:06 17040366 /var/cache/fontconfig/1c8b083178c302b675902d1cdee2872f-le64.cache-7 -7f559003b000-7f559004a000 r--s 00000000 08:06 17040365 /var/cache/fontconfig/d84f04e9645d87563cfd5ec96e42391e-le64.cache-7 -7f559004a000-7f559004d000 r--s 00000000 08:06 17040364 /var/cache/fontconfig/4649881a9652ae0880079bb74c0cb52b-le64.cache-7 -7f559004d000-7f5590050000 r--s 00000000 08:06 17040355 /var/cache/fontconfig/0b60c6307ef26009fb0aa79daf86e3af-le64.cache-7 -7f5590050000-7f5590052000 r--s 00000000 08:06 17039426 /var/cache/fontconfig/3bd6a00a1059ca9725da3f0d7afa935d-le64.cache-7 -7f5590052000-7f559005d000 r--s 00000000 08:06 17039440 /var/cache/fontconfig/945677eb7aeaf62f1d50efc3fb3ec7d8-le64.cache-7 -7f559005d000-7f5590062000 r--s 00000000 08:06 17039422 /var/cache/fontconfig/2300eef321c393bfd76478a5c0e95b23-le64.cache-7 -7f5590062000-7f5590066000 r--s 00000000 08:06 17039459 /var/cache/fontconfig/f0b0891f3a988e97b2aa7c076a1430f3-le64.cache-7 -7f5590066000-7f5590068000 r--s 00000000 08:06 17039435 /var/cache/fontconfig/6adb16404e170b9523f41c0701dd4690-le64.cache-7 -7f5590068000-7f559006c000 r--s 00000000 08:06 17039438 /var/cache/fontconfig/7b79abe4499501fcd4e4826394083a53-le64.cache-7 -7f559006c000-7f559006f000 r--s 00000000 08:06 17039425 /var/cache/fontconfig/3a9914ff62a69b32234685a84fdc926d-le64.cache-7 -7f559006f000-7f5590071000 r--s 00000000 08:06 17039437 /var/cache/fontconfig/767a8244fc0220cfb567a839d0392e0b-le64.cache-7 -7f5590071000-7f5590072000 r--s 00000000 08:06 17039418 /var/cache/fontconfig/0b41ebd278d9388cf5d83f81d476c672-le64.cache-7 -7f5590072000-7f5590077000 r--s 00000000 08:06 17039449 /var/cache/fontconfig/bbd3eebb6912613e2596f2fd46535a95-le64.cache-7 -7f5590077000-7f55900c3000 r--s 00000000 08:06 17039419 /var/cache/fontconfig/0bd3dc0958fa2205aaaa8ebb13e2872b-le64.cache-7 -7f55900c3000-7f55900c4000 r--s 00000000 08:06 17039444 /var/cache/fontconfig/b036df7bbb72844893df6bcf5570ea7f-le64.cache-7 -7f55900c4000-7f55900c5000 r--s 00000000 08:06 17039431 /var/cache/fontconfig/575107dd7452b4b2b2b1361909925bb4-le64.cache-7 -7f55900c5000-7f55900cb000 r--s 00000000 08:06 17053813 /var/cache/fontconfig/3047814df9a2f067bd2d96a2b9c36e5a-le64.cache-7 -7f55900cb000-7f55902cb000 rw-p 00000000 00:00 0 -7f55902cb000-7f55902ce000 ---p 00000000 00:00 0 -7f55902ce000-7f55903cc000 rw-p 00000000 00:00 0 -7f55903cc000-7f55903cf000 ---p 00000000 00:00 0 -7f55903cf000-7f55904cd000 rw-p 00000000 00:00 0 -7f55904cd000-7f55904ce000 ---p 00000000 00:00 0 -7f55904ce000-7f55904d1000 ---p 00000000 00:00 0 -7f55904d1000-7f5590cce000 rw-p 00000000 00:00 0 -7f5590cce000-7f5590cd7000 r-xp 00000000 08:06 13380593 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libmanagement.so -7f5590cd7000-7f5590ed6000 ---p 00009000 08:06 13380593 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libmanagement.so -7f5590ed6000-7f5590ed7000 r--p 00008000 08:06 13380593 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libmanagement.so -7f5590ed7000-7f5590ed8000 rw-p 00009000 08:06 13380593 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libmanagement.so -7f5590ed8000-7f5590eec000 r-xp 00000000 08:06 11801734 /lib/x86_64-linux-gnu/libgpg-error.so.0.22.0 -7f5590eec000-7f55910eb000 ---p 00014000 08:06 11801734 /lib/x86_64-linux-gnu/libgpg-error.so.0.22.0 -7f55910eb000-7f55910ec000 r--p 00013000 08:06 11801734 /lib/x86_64-linux-gnu/libgpg-error.so.0.22.0 -7f55910ec000-7f55910ed000 rw-p 00014000 08:06 11801734 /lib/x86_64-linux-gnu/libgpg-error.so.0.22.0 -7f55910ed000-7f5591201000 r-xp 00000000 08:06 11801732 /lib/x86_64-linux-gnu/libgcrypt.so.20.2.1 -7f5591201000-7f5591401000 ---p 00114000 08:06 11801732 /lib/x86_64-linux-gnu/libgcrypt.so.20.2.1 -7f5591401000-7f5591403000 r--p 00114000 08:06 11801732 /lib/x86_64-linux-gnu/libgcrypt.so.20.2.1 -7f5591403000-7f5591408000 rw-p 00116000 08:06 11801732 /lib/x86_64-linux-gnu/libgcrypt.so.20.2.1 -7f5591408000-7f5591409000 rw-p 00000000 00:00 0 -7f5591409000-7f5591424000 r-xp 00000000 08:06 13375612 /usr/lib/x86_64-linux-gnu/liblz4.so.1.7.1 -7f5591424000-7f5591623000 ---p 0001b000 08:06 13375612 /usr/lib/x86_64-linux-gnu/liblz4.so.1.7.1 -7f5591623000-7f5591624000 r--p 0001a000 08:06 13375612 /usr/lib/x86_64-linux-gnu/liblz4.so.1.7.1 -7f5591624000-7f5591625000 rw-p 0001b000 08:06 13375612 /usr/lib/x86_64-linux-gnu/liblz4.so.1.7.1 -7f5591625000-7f5591649000 r-xp 00000000 08:06 11801753 /lib/x86_64-linux-gnu/liblzma.so.5.2.2 -7f5591649000-7f5591849000 ---p 00024000 08:06 11801753 /lib/x86_64-linux-gnu/liblzma.so.5.2.2 -7f5591849000-7f559184a000 r--p 00024000 08:06 11801753 /lib/x86_64-linux-gnu/liblzma.so.5.2.2 -7f559184a000-7f559184b000 rw-p 00025000 08:06 11801753 /lib/x86_64-linux-gnu/liblzma.so.5.2.2 -7f559184b000-7f55918cb000 r-xp 00000000 08:06 11798768 /lib/x86_64-linux-gnu/libsystemd.so.0.21.0 -7f55918cb000-7f5591aca000 ---p 00080000 08:06 11798768 /lib/x86_64-linux-gnu/libsystemd.so.0.21.0 -7f5591aca000-7f5591acd000 r--p 0007f000 08:06 11798768 /lib/x86_64-linux-gnu/libsystemd.so.0.21.0 -7f5591acd000-7f5591ace000 rw-p 00082000 08:06 11798768 /lib/x86_64-linux-gnu/libsystemd.so.0.21.0 -7f5591ace000-7f5591acf000 rw-p 00000000 00:00 0 -7f5591acf000-7f5591b3f000 r-xp 00000000 08:06 11801814 /lib/x86_64-linux-gnu/libpcre.so.3.13.3 -7f5591b3f000-7f5591d3f000 ---p 00070000 08:06 11801814 /lib/x86_64-linux-gnu/libpcre.so.3.13.3 -7f5591d3f000-7f5591d40000 r--p 00070000 08:06 11801814 /lib/x86_64-linux-gnu/libpcre.so.3.13.3 -7f5591d40000-7f5591d41000 rw-p 00071000 08:06 11801814 /lib/x86_64-linux-gnu/libpcre.so.3.13.3 -7f5591d41000-7f5591d48000 r-xp 00000000 08:06 13375198 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 -7f5591d48000-7f5591f47000 ---p 00007000 08:06 13375198 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 -7f5591f47000-7f5591f48000 r--p 00006000 08:06 13375198 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 -7f5591f48000-7f5591f49000 rw-p 00007000 08:06 13375198 /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4 -7f5591f49000-7f5591f75000 r-xp 00000000 08:06 13374951 /usr/lib/x86_64-linux-gnu/libatspi.so.0.0.1 -7f5591f75000-7f5592175000 ---p 0002c000 08:06 13374951 /usr/lib/x86_64-linux-gnu/libatspi.so.0.0.1 -7f5592175000-7f5592178000 r--p 0002c000 08:06 13374951 /usr/lib/x86_64-linux-gnu/libatspi.so.0.0.1 -7f5592178000-7f5592179000 rw-p 0002f000 08:06 13374951 /usr/lib/x86_64-linux-gnu/libatspi.so.0.0.1 -7f5592179000-7f55921c4000 r-xp 00000000 08:06 11801708 /lib/x86_64-linux-gnu/libdbus-1.so.3.19.4 -7f55921c4000-7f55923c4000 ---p 0004b000 08:06 11801708 /lib/x86_64-linux-gnu/libdbus-1.so.3.19.4 -7f55923c4000-7f55923c5000 r--p 0004b000 08:06 11801708 /lib/x86_64-linux-gnu/libdbus-1.so.3.19.4 -7f55923c5000-7f55923c6000 rw-p 0004c000 08:06 11801708 /lib/x86_64-linux-gnu/libdbus-1.so.3.19.4 -7f55923c6000-7f55924da000 r-xp 00000000 08:06 13375330 /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.5600.4 -7f55924da000-7f55926da000 ---p 00114000 08:06 13375330 /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.5600.4 -7f55926da000-7f55926db000 r--p 00114000 08:06 13375330 /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.5600.4 -7f55926db000-7f55926dc000 rw-p 00115000 08:06 13375330 /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.5600.4 -7f55926dc000-7f55926dd000 rw-p 00000000 00:00 0 -7f55926dd000-7f559272f000 r-xp 00000000 08:06 13375358 /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4 -7f559272f000-7f559292f000 ---p 00052000 08:06 13375358 /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4 -7f559292f000-7f5592930000 r--p 00052000 08:06 13375358 /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4 -7f5592930000-7f5592931000 rw-p 00053000 08:06 13375358 /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4 -7f5592931000-7f559295f000 r-xp 00000000 08:06 13374947 /usr/lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0.0.0 -7f559295f000-7f5592b5f000 ---p 0002e000 08:06 13374947 /usr/lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0.0.0 -7f5592b5f000-7f5592b60000 r--p 0002e000 08:06 13374947 /usr/lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0.0.0 -7f5592b60000-7f5592b61000 rw-p 0002f000 08:06 13374947 /usr/lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0.0.0 -7f5592b61000-7f5592b62000 rw-p 00000000 00:00 0 -7f5592b62000-7f5592b84000 r-xp 00000000 08:06 13374945 /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0.22810.1 -7f5592b84000-7f5592d84000 ---p 00022000 08:06 13374945 /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0.22810.1 -7f5592d84000-7f5592d87000 r--p 00022000 08:06 13374945 /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0.22810.1 -7f5592d87000-7f5592d88000 rw-p 00025000 08:06 13374945 /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0.22810.1 -7f5592d88000-7f5592d9f000 r-xp 00000000 08:06 13506617 /usr/lib/x86_64-linux-gnu/jni/libatk-wrapper.so.6.0.0 -7f5592d9f000-7f5592f9f000 ---p 00017000 08:06 13506617 /usr/lib/x86_64-linux-gnu/jni/libatk-wrapper.so.6.0.0 -7f5592f9f000-7f5592fa0000 r--p 00017000 08:06 13506617 /usr/lib/x86_64-linux-gnu/jni/libatk-wrapper.so.6.0.0 -7f5592fa0000-7f5592fa1000 rw-p 00018000 08:06 13506617 /usr/lib/x86_64-linux-gnu/jni/libatk-wrapper.so.6.0.0 -7f5592fa1000-7f55931a1000 rw-p 00000000 00:00 0 -7f55931a1000-7f55931a4000 ---p 00000000 00:00 0 -7f55931a4000-7f55932a2000 rw-p 00000000 00:00 0 -7f55932a2000-7f55932a7000 r-xp 00000000 08:06 13374865 /usr/lib/x86_64-linux-gnu/libXfixes.so.3.1.0 -7f55932a7000-7f55934a6000 ---p 00005000 08:06 13374865 /usr/lib/x86_64-linux-gnu/libXfixes.so.3.1.0 -7f55934a6000-7f55934a7000 r--p 00004000 08:06 13374865 /usr/lib/x86_64-linux-gnu/libXfixes.so.3.1.0 -7f55934a7000-7f55934a8000 rw-p 00005000 08:06 13374865 /usr/lib/x86_64-linux-gnu/libXfixes.so.3.1.0 -7f55934a8000-7f55934b1000 r-xp 00000000 08:06 13374857 /usr/lib/x86_64-linux-gnu/libXcursor.so.1.0.2 -7f55934b1000-7f55936b0000 ---p 00009000 08:06 13374857 /usr/lib/x86_64-linux-gnu/libXcursor.so.1.0.2 -7f55936b0000-7f55936b1000 r--p 00008000 08:06 13374857 /usr/lib/x86_64-linux-gnu/libXcursor.so.1.0.2 -7f55936b1000-7f55936b2000 rw-p 00009000 08:06 13374857 /usr/lib/x86_64-linux-gnu/libXcursor.so.1.0.2 -7f55936b2000-7f55936e3000 r-xp 00000000 08:06 13375792 /usr/lib/x86_64-linux-gnu/libpng16.so.16.34.0 -7f55936e3000-7f55938e2000 ---p 00031000 08:06 13375792 /usr/lib/x86_64-linux-gnu/libpng16.so.16.34.0 -7f55938e2000-7f55938e3000 r--p 00030000 08:06 13375792 /usr/lib/x86_64-linux-gnu/libpng16.so.16.34.0 -7f55938e3000-7f55938e4000 rw-p 00031000 08:06 13375792 /usr/lib/x86_64-linux-gnu/libpng16.so.16.34.0 -7f55938e4000-7f5593991000 r-xp 00000000 08:06 13379440 /usr/lib/x86_64-linux-gnu/libfreetype.so.6.15.0 -7f5593991000-7f5593b90000 ---p 000ad000 08:06 13379440 /usr/lib/x86_64-linux-gnu/libfreetype.so.6.15.0 -7f5593b90000-7f5593b97000 r--p 000ac000 08:06 13379440 /usr/lib/x86_64-linux-gnu/libfreetype.so.6.15.0 -7f5593b97000-7f5593b98000 rw-p 000b3000 08:06 13379440 /usr/lib/x86_64-linux-gnu/libfreetype.so.6.15.0 -7f5593b98000-7f5593be8000 r-xp 00000000 08:06 13375786 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libfontmanager.so -7f5593be8000-7f5593de8000 ---p 00050000 08:06 13375786 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libfontmanager.so -7f5593de8000-7f5593dea000 r--p 00050000 08:06 13375786 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libfontmanager.so -7f5593dea000-7f5593deb000 rw-p 00052000 08:06 13375786 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libfontmanager.so -7f5593deb000-7f5593dfe000 r-xp 00000000 08:06 11801689 /lib/x86_64-linux-gnu/libbsd.so.0.8.7 -7f5593dfe000-7f5593ffd000 ---p 00013000 08:06 11801689 /lib/x86_64-linux-gnu/libbsd.so.0.8.7 -7f5593ffd000-7f5593ffe000 r--p 00012000 08:06 11801689 /lib/x86_64-linux-gnu/libbsd.so.0.8.7 -7f5593ffe000-7f5593fff000 rw-p 00013000 08:06 11801689 /lib/x86_64-linux-gnu/libbsd.so.0.8.7 -7f5593fff000-7f5594000000 rw-p 00000000 00:00 0 -7f5594000000-7f5595203000 rw-p 00000000 00:00 0 -7f5595203000-7f5598000000 ---p 00000000 00:00 0 -7f5598000000-7f5598021000 rw-p 00000000 00:00 0 -7f5598021000-7f559c000000 ---p 00000000 00:00 0 -7f559c000000-7f559e4eb000 rw-p 00000000 00:00 0 -7f559e4eb000-7f55a0000000 ---p 00000000 00:00 0 -7f55a0000000-7f55a2c9e000 rw-p 00000000 00:00 0 -7f55a2c9e000-7f55a4000000 ---p 00000000 00:00 0 -7f55a4000000-7f55a4021000 rw-p 00000000 00:00 0 -7f55a4021000-7f55a8000000 ---p 00000000 00:00 0 -7f55a8000000-7f55a8021000 rw-p 00000000 00:00 0 -7f55a8021000-7f55ac000000 ---p 00000000 00:00 0 -7f55ac000000-7f55ac021000 rw-p 00000000 00:00 0 -7f55ac021000-7f55b0000000 ---p 00000000 00:00 0 -7f55b0000000-7f55b0001000 r--s 00000000 08:06 17039448 /var/cache/fontconfig/b9d506c9ac06c20b433354fa67a72993-le64.cache-7 -7f55b0001000-7f55b0007000 r--s 00000000 08:06 17039447 /var/cache/fontconfig/b47c4e1ecd0709278f4910c18777a504-le64.cache-7 -7f55b0007000-7f55b0008000 r--s 00000000 08:06 17039417 /var/cache/fontconfig/0b3f2427388044a01a5738fa4b8cc9d1-le64.cache-7 -7f55b0008000-7f55b0009000 r--s 00000000 08:06 17039416 /var/cache/fontconfig/0941b6d3777f1e64883d36fd6d84a52f-le64.cache-7 -7f55b0009000-7f55b0015000 r--s 00000000 08:06 17040354 /var/cache/fontconfig/d589a48862398ed80a3d6066f4f56f4c-le64.cache-7 -7f55b0015000-7f55b0017000 r--s 00000000 08:06 17039458 /var/cache/fontconfig/e8d703157d3641d20c7e900be2e8d091-le64.cache-7 -7f55b0017000-7f55b001c000 r--s 00000000 08:06 17039445 /var/cache/fontconfig/b25010134a2e462c779c1c1d5ccbff7b-le64.cache-7 -7f55b001c000-7f55b001f000 ---p 00000000 00:00 0 -7f55b001f000-7f55b011d000 rw-p 00000000 00:00 0 -7f55b011d000-7f55b0122000 r-xp 00000000 08:06 13374861 /usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0 -7f55b0122000-7f55b0321000 ---p 00005000 08:06 13374861 /usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0 -7f55b0321000-7f55b0322000 r--p 00004000 08:06 13374861 /usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0 -7f55b0322000-7f55b0323000 rw-p 00005000 08:06 13374861 /usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0 -7f55b0323000-7f55b0325000 r-xp 00000000 08:06 13374850 /usr/lib/x86_64-linux-gnu/libXau.so.6.0.0 -7f55b0325000-7f55b0525000 ---p 00002000 08:06 13374850 /usr/lib/x86_64-linux-gnu/libXau.so.6.0.0 -7f55b0525000-7f55b0526000 r--p 00002000 08:06 13374850 /usr/lib/x86_64-linux-gnu/libXau.so.6.0.0 -7f55b0526000-7f55b0527000 rw-p 00003000 08:06 13374850 /usr/lib/x86_64-linux-gnu/libXau.so.6.0.0 -7f55b0527000-7f55b054d000 r-xp 00000000 08:06 13376122 /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0 -7f55b054d000-7f55b074d000 ---p 00026000 08:06 13376122 /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0 -7f55b074d000-7f55b074e000 r--p 00026000 08:06 13376122 /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0 -7f55b074e000-7f55b074f000 rw-p 00027000 08:06 13376122 /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0 -7f55b074f000-7f55b075e000 r-xp 00000000 08:06 13374871 /usr/lib/x86_64-linux-gnu/libXi.so.6.1.0 -7f55b075e000-7f55b095d000 ---p 0000f000 08:06 13374871 /usr/lib/x86_64-linux-gnu/libXi.so.6.1.0 -7f55b095d000-7f55b095e000 r--p 0000e000 08:06 13374871 /usr/lib/x86_64-linux-gnu/libXi.so.6.1.0 -7f55b095e000-7f55b095f000 rw-p 0000f000 08:06 13374871 /usr/lib/x86_64-linux-gnu/libXi.so.6.1.0 -7f55b095f000-7f55b0964000 r-xp 00000000 08:06 13374889 /usr/lib/x86_64-linux-gnu/libXtst.so.6.1.0 -7f55b0964000-7f55b0b63000 ---p 00005000 08:06 13374889 /usr/lib/x86_64-linux-gnu/libXtst.so.6.1.0 -7f55b0b63000-7f55b0b64000 r--p 00004000 08:06 13374889 /usr/lib/x86_64-linux-gnu/libXtst.so.6.1.0 -7f55b0b64000-7f55b0b65000 rw-p 00005000 08:06 13374889 /usr/lib/x86_64-linux-gnu/libXtst.so.6.1.0 -7f55b0b65000-7f55b0b6e000 r-xp 00000000 08:06 13374883 /usr/lib/x86_64-linux-gnu/libXrender.so.1.3.0 -7f55b0b6e000-7f55b0d6d000 ---p 00009000 08:06 13374883 /usr/lib/x86_64-linux-gnu/libXrender.so.1.3.0 -7f55b0d6d000-7f55b0d6e000 r--p 00008000 08:06 13374883 /usr/lib/x86_64-linux-gnu/libXrender.so.1.3.0 -7f55b0d6e000-7f55b0d6f000 rw-p 00009000 08:06 13374883 /usr/lib/x86_64-linux-gnu/libXrender.so.1.3.0 -7f55b0d6f000-7f55b0ea2000 r-xp 00000000 08:06 13369804 /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0 -7f55b0ea2000-7f55b10a2000 ---p 00133000 08:06 13369804 /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0 -7f55b10a2000-7f55b10a3000 r--p 00133000 08:06 13369804 /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0 -7f55b10a3000-7f55b10a7000 rw-p 00134000 08:06 13369804 /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0 -7f55b10a7000-7f55b10b8000 r-xp 00000000 08:06 13374863 /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0 -7f55b10b8000-7f55b12b7000 ---p 00011000 08:06 13374863 /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0 -7f55b12b7000-7f55b12b8000 r--p 00010000 08:06 13374863 /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0 -7f55b12b8000-7f55b12b9000 rw-p 00011000 08:06 13374863 /usr/lib/x86_64-linux-gnu/libXext.so.6.4.0 -7f55b12b9000-7f55b1315000 r-xp 00000000 08:06 13375703 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libawt_xawt.so -7f55b1315000-7f55b1514000 ---p 0005c000 08:06 13375703 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libawt_xawt.so -7f55b1514000-7f55b1515000 r--p 0005b000 08:06 13375703 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libawt_xawt.so -7f55b1515000-7f55b1518000 rw-p 0005c000 08:06 13375703 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libawt_xawt.so -7f55b1518000-7f55b151a000 rw-p 00000000 00:00 0 -7f55b151a000-7f55b15b9000 r-xp 00000000 08:06 13375652 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libawt.so -7f55b15b9000-7f55b17b8000 ---p 0009f000 08:06 13375652 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libawt.so -7f55b17b8000-7f55b17b9000 r--p 0009e000 08:06 13375652 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libawt.so -7f55b17b9000-7f55b17c4000 rw-p 0009f000 08:06 13375652 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libawt.so -7f55b17c4000-7f55b17e9000 rw-p 00000000 00:00 0 -7f55b17e9000-7f55b1801000 r-xp 00000000 08:06 13380595 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnet.so -7f55b1801000-7f55b1a00000 ---p 00018000 08:06 13380595 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnet.so -7f55b1a00000-7f55b1a01000 r--p 00017000 08:06 13380595 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnet.so -7f55b1a01000-7f55b1a02000 rw-p 00018000 08:06 13380595 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnet.so -7f55b1a02000-7f55b1a14000 r-xp 00000000 08:06 13380596 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnio.so -7f55b1a14000-7f55b1c13000 ---p 00012000 08:06 13380596 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnio.so -7f55b1c13000-7f55b1c14000 r--p 00011000 08:06 13380596 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnio.so -7f55b1c14000-7f55b1c15000 rw-p 00012000 08:06 13380596 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnio.so -7f55b1c15000-7f55b1c16000 ---p 00000000 00:00 0 -7f55b1c16000-7f55b1d16000 rw-p 00000000 00:00 0 -7f55b1d16000-7f55b1d19000 ---p 00000000 00:00 0 -7f55b1d19000-7f55b1e17000 rw-p 00000000 00:00 0 -7f55b1e17000-7f55b1e18000 ---p 00000000 00:00 0 -7f55b1e18000-7f55b1e1b000 ---p 00000000 00:00 0 -7f55b1e1b000-7f55b1f18000 rw-p 00000000 00:00 0 -7f55b1f18000-7f55b1f19000 ---p 00000000 00:00 0 -7f55b1f19000-7f55b1f1c000 ---p 00000000 00:00 0 -7f55b1f1c000-7f55b2019000 rw-p 00000000 00:00 0 -7f55b2019000-7f55b201a000 ---p 00000000 00:00 0 -7f55b201a000-7f55b201d000 ---p 00000000 00:00 0 -7f55b201d000-7f55b211a000 rw-p 00000000 00:00 0 -7f55b211a000-7f55b211d000 ---p 00000000 00:00 0 -7f55b211d000-7f55b221b000 rw-p 00000000 00:00 0 -7f55b221b000-7f55b3dfe000 r--p 00000000 08:06 13378100 /usr/lib/locale/locale-archive -7f55b3dfe000-7f55b3e01000 ---p 00000000 00:00 0 -7f55b3e01000-7f55b3eff000 rw-p 00000000 00:00 0 -7f55b3eff000-7f55b3f02000 ---p 00000000 00:00 0 -7f55b3f02000-7f55b4000000 rw-p 00000000 00:00 0 -7f55b4000000-7f55b4024000 rw-p 00000000 00:00 0 -7f55b4024000-7f55b8000000 ---p 00000000 00:00 0 -7f55b8000000-7f55b80fc000 rw-p 00000000 00:00 0 -7f55b80fc000-7f55bc000000 ---p 00000000 00:00 0 -7f55bc000000-7f55bc021000 rw-p 00000000 00:00 0 -7f55bc021000-7f55c0000000 ---p 00000000 00:00 0 -7f55c0000000-7f55c0021000 rw-p 00000000 00:00 0 -7f55c0021000-7f55c4000000 ---p 00000000 00:00 0 -7f55c4000000-7f55c4064000 rw-p 00000000 00:00 0 -7f55c4064000-7f55c8000000 ---p 00000000 00:00 0 -7f55c8000000-7f55c8001000 r--s 00000000 08:06 17039427 /var/cache/fontconfig/3f7329c5293ffd510edef78f73874cfd-le64.cache-7 -7f55c8001000-7f55c8003000 r--s 00000000 08:06 17039434 /var/cache/fontconfig/6a6dfed4a7d1e4367ef225093de82110-le64.cache-7 -7f55c8003000-7f55c8010000 r--s 00000000 08:06 17039415 /var/cache/fontconfig/089dead882dea3570ffc31a9898cfb69-le64.cache-7 -7f55c8010000-7f55c8014000 r--s 00000000 08:06 17039414 /var/cache/fontconfig/0843ca893a93b5446bad221815920e23-le64.cache-7 -7f55c8014000-7f55c8023000 r--s 00000000 08:06 17039442 /var/cache/fontconfig/9b89f8e3dae116d678bbf48e5f21f69b-le64.cache-7 -7f55c8023000-7f55c8026000 r--s 00000000 08:06 17039446 /var/cache/fontconfig/b2d60264172c9fafc2f82e9237d8d959-le64.cache-7 -7f55c8026000-7f55c8029000 r--s 00000000 08:06 17039456 /var/cache/fontconfig/e13b20fdb08344e0e664864cc2ede53d-le64.cache-7 -7f55c8029000-7f55c802a000 rwxp 00000000 00:00 0 -7f55c802a000-7f55c803b000 r--s 001bc000 08:06 13380632 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar -7f55c803b000-7f55c803e000 ---p 00000000 00:00 0 -7f55c803e000-7f55c8074000 rw-p 00000000 00:00 0 -7f55c8074000-7f55c8087000 r--s 0034b000 08:06 13380636 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar -7f55c8087000-7f55c809c000 r--s 00157000 08:06 10093046 /home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.10/ea29e063d2bbe695be13e9d044dcfb0c7add398e/kotlin-stdlib-1.4.10.jar -7f55c809c000-7f55c809d000 r--s 00000000 08:06 10093494 /home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.10/30e46450b0bb3dbf43898d2f461be4a942784780/kotlin-stdlib-jdk7-1.4.10.jar -7f55c809d000-7f55c809e000 r--s 00005000 08:06 10093546 /home/ceik/.gradle/caches/modules-2/files-2.1/com.googlecode.json-simple/json-simple/1.1.1/c9ad4a0850ab676c5c64461a05ca524cdfff59f1/json-simple-1.1.1.jar -7f55c809e000-7f55c80cf000 rw-p 00000000 00:00 0 -7f55c80cf000-7f55c80d0000 ---p 00000000 00:00 0 -7f55c80d0000-7f55c90df000 rw-p 00000000 00:00 0 -7f55c90df000-7f55c92b0000 r--s 03c94000 08:06 13380637 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar -7f55c92b0000-7f55cd000000 rw-p 00000000 00:00 0 -7f55cd000000-7f55ce560000 rwxp 00000000 00:00 0 -7f55ce560000-7f55dc000000 ---p 00000000 00:00 0 -7f55dc000000-7f55dc4fd000 rw-p 00000000 00:00 0 -7f55dc4fd000-7f55e0000000 ---p 00000000 00:00 0 -7f55e0000000-7f55e0005000 r--s 001f4000 08:06 10093552 /home/ceik/.gradle/caches/modules-2/files-2.1/com.github.Vatuu/discord-rpc/1.6.2/233ee898e37dddd97ceaa9e7fd31a3eb8f10a128/discord-rpc-1.6.2.jar -7f55e0005000-7f55e0009000 r--s 0002d000 08:06 9961565 /home/ceik/IdeaProjects/2009scape/Client/libs/clientlibs.jar -7f55e0009000-7f55e0024000 r--s 00394000 08:06 13380617 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/cldrdata.jar -7f55e0024000-7f55e003f000 r--s 001d6000 08:06 13380622 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/nashorn.jar -7f55e003f000-7f55e0d5c000 rw-p 00000000 00:00 0 -7f55e0d5c000-7f55e0d5d000 ---p 00000000 00:00 0 -7f55e0d5d000-7f55e0e5d000 rw-p 00000000 00:00 0 -7f55e0e5d000-7f55e0e5e000 ---p 00000000 00:00 0 -7f55e0e5e000-7f55e0f5e000 rw-p 00000000 00:00 0 -7f55e0f5e000-7f55e0f5f000 ---p 00000000 00:00 0 -7f55e0f5f000-7f55e105f000 rw-p 00000000 00:00 0 -7f55e105f000-7f55e1060000 ---p 00000000 00:00 0 -7f55e1060000-7f55e1185000 rw-p 00000000 00:00 0 -7f55e1185000-7f55e13ee000 ---p 00000000 00:00 0 -7f55e13ee000-7f55e1413000 rw-p 00000000 00:00 0 -7f55e1413000-7f55e167c000 ---p 00000000 00:00 0 -7f55e167c000-7f55e1694000 rw-p 00000000 00:00 0 -7f55e1694000-7f55e17c3000 ---p 00000000 00:00 0 -7f55e17c3000-7f55e181a000 rw-p 00000000 00:00 0 -7f55e181a000-7f55e1b84000 ---p 00000000 00:00 0 -7f55e1b84000-7f55e1b8c000 r-xp 00000000 08:06 13380603 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libzip.so -7f55e1b8c000-7f55e1d8b000 ---p 00008000 08:06 13380603 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libzip.so -7f55e1d8b000-7f55e1d8c000 r--p 00007000 08:06 13380603 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libzip.so -7f55e1d8c000-7f55e1d8d000 rw-p 00008000 08:06 13380603 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libzip.so -7f55e1d8d000-7f55e1d98000 r-xp 00000000 08:06 11802668 /lib/x86_64-linux-gnu/libnss_files-2.27.so -7f55e1d98000-7f55e1f97000 ---p 0000b000 08:06 11802668 /lib/x86_64-linux-gnu/libnss_files-2.27.so -7f55e1f97000-7f55e1f98000 r--p 0000a000 08:06 11802668 /lib/x86_64-linux-gnu/libnss_files-2.27.so -7f55e1f98000-7f55e1f99000 rw-p 0000b000 08:06 11802668 /lib/x86_64-linux-gnu/libnss_files-2.27.so -7f55e1f99000-7f55e1f9f000 rw-p 00000000 00:00 0 -7f55e1f9f000-7f55e1fb6000 r-xp 00000000 08:06 11802665 /lib/x86_64-linux-gnu/libnsl-2.27.so -7f55e1fb6000-7f55e21b5000 ---p 00017000 08:06 11802665 /lib/x86_64-linux-gnu/libnsl-2.27.so -7f55e21b5000-7f55e21b6000 r--p 00016000 08:06 11802665 /lib/x86_64-linux-gnu/libnsl-2.27.so -7f55e21b6000-7f55e21b7000 rw-p 00017000 08:06 11802665 /lib/x86_64-linux-gnu/libnsl-2.27.so -7f55e21b7000-7f55e21b9000 rw-p 00000000 00:00 0 -7f55e21b9000-7f55e21c4000 r-xp 00000000 08:06 11802670 /lib/x86_64-linux-gnu/libnss_nis-2.27.so -7f55e21c4000-7f55e23c3000 ---p 0000b000 08:06 11802670 /lib/x86_64-linux-gnu/libnss_nis-2.27.so -7f55e23c3000-7f55e23c4000 r--p 0000a000 08:06 11802670 /lib/x86_64-linux-gnu/libnss_nis-2.27.so -7f55e23c4000-7f55e23c5000 rw-p 0000b000 08:06 11802670 /lib/x86_64-linux-gnu/libnss_nis-2.27.so -7f55e23c5000-7f55e23cd000 r-xp 00000000 08:06 11802666 /lib/x86_64-linux-gnu/libnss_compat-2.27.so -7f55e23cd000-7f55e25cd000 ---p 00008000 08:06 11802666 /lib/x86_64-linux-gnu/libnss_compat-2.27.so -7f55e25cd000-7f55e25ce000 r--p 00008000 08:06 11802666 /lib/x86_64-linux-gnu/libnss_compat-2.27.so -7f55e25ce000-7f55e25cf000 rw-p 00009000 08:06 11802666 /lib/x86_64-linux-gnu/libnss_compat-2.27.so -7f55e25cf000-7f55e25f9000 r-xp 00000000 08:06 13376055 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjava.so -7f55e25f9000-7f55e27f9000 ---p 0002a000 08:06 13376055 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjava.so -7f55e27f9000-7f55e27fa000 r--p 0002a000 08:06 13376055 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjava.so -7f55e27fa000-7f55e27fc000 rw-p 0002b000 08:06 13376055 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjava.so -7f55e27fc000-7f55e280c000 r-xp 00000000 08:06 13380602 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libverify.so -7f55e280c000-7f55e2a0b000 ---p 00010000 08:06 13380602 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libverify.so -7f55e2a0b000-7f55e2a0d000 r--p 0000f000 08:06 13380602 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libverify.so -7f55e2a0d000-7f55e2a0e000 rw-p 00011000 08:06 13380602 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libverify.so -7f55e2a0e000-7f55e2a15000 r-xp 00000000 08:06 11802675 /lib/x86_64-linux-gnu/librt-2.27.so -7f55e2a15000-7f55e2c14000 ---p 00007000 08:06 11802675 /lib/x86_64-linux-gnu/librt-2.27.so -7f55e2c14000-7f55e2c15000 r--p 00006000 08:06 11802675 /lib/x86_64-linux-gnu/librt-2.27.so -7f55e2c15000-7f55e2c16000 rw-p 00007000 08:06 11802675 /lib/x86_64-linux-gnu/librt-2.27.so -7f55e2c16000-7f55e2c2d000 r-xp 00000000 08:06 11801730 /lib/x86_64-linux-gnu/libgcc_s.so.1 -7f55e2c2d000-7f55e2e2c000 ---p 00017000 08:06 11801730 /lib/x86_64-linux-gnu/libgcc_s.so.1 -7f55e2e2c000-7f55e2e2d000 r--p 00016000 08:06 11801730 /lib/x86_64-linux-gnu/libgcc_s.so.1 -7f55e2e2d000-7f55e2e2e000 rw-p 00017000 08:06 11801730 /lib/x86_64-linux-gnu/libgcc_s.so.1 -7f55e2e2e000-7f55e2fcb000 r-xp 00000000 08:06 11802662 /lib/x86_64-linux-gnu/libm-2.27.so -7f55e2fcb000-7f55e31ca000 ---p 0019d000 08:06 11802662 /lib/x86_64-linux-gnu/libm-2.27.so -7f55e31ca000-7f55e31cb000 r--p 0019c000 08:06 11802662 /lib/x86_64-linux-gnu/libm-2.27.so -7f55e31cb000-7f55e31cc000 rw-p 0019d000 08:06 11802662 /lib/x86_64-linux-gnu/libm-2.27.so -7f55e31cc000-7f55e3345000 r-xp 00000000 08:06 13375942 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 -7f55e3345000-7f55e3545000 ---p 00179000 08:06 13375942 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 -7f55e3545000-7f55e354f000 r--p 00179000 08:06 13375942 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 -7f55e354f000-7f55e3551000 rw-p 00183000 08:06 13375942 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 -7f55e3551000-7f55e3555000 rw-p 00000000 00:00 0 -7f55e3555000-7f55e42c3000 r-xp 00000000 08:06 13380606 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so -7f55e42c3000-7f55e44c2000 ---p 00d6e000 08:06 13380606 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so -7f55e44c2000-7f55e4558000 r--p 00d6d000 08:06 13380606 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so -7f55e4558000-7f55e4581000 rw-p 00e03000 08:06 13380606 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjvm.so -7f55e4581000-7f55e45b4000 rw-p 00000000 00:00 0 -7f55e45b4000-7f55e45ce000 r-xp 00000000 08:06 11802673 /lib/x86_64-linux-gnu/libpthread-2.27.so -7f55e45ce000-7f55e47cd000 ---p 0001a000 08:06 11802673 /lib/x86_64-linux-gnu/libpthread-2.27.so -7f55e47cd000-7f55e47ce000 r--p 00019000 08:06 11802673 /lib/x86_64-linux-gnu/libpthread-2.27.so -7f55e47ce000-7f55e47cf000 rw-p 0001a000 08:06 11802673 /lib/x86_64-linux-gnu/libpthread-2.27.so -7f55e47cf000-7f55e47d3000 rw-p 00000000 00:00 0 -7f55e47d3000-7f55e47d6000 r-xp 00000000 08:06 11802661 /lib/x86_64-linux-gnu/libdl-2.27.so -7f55e47d6000-7f55e49d5000 ---p 00003000 08:06 11802661 /lib/x86_64-linux-gnu/libdl-2.27.so -7f55e49d5000-7f55e49d6000 r--p 00002000 08:06 11802661 /lib/x86_64-linux-gnu/libdl-2.27.so -7f55e49d6000-7f55e49d7000 rw-p 00003000 08:06 11802661 /lib/x86_64-linux-gnu/libdl-2.27.so -7f55e49d7000-7f55e49f3000 r-xp 00000000 08:06 11801866 /lib/x86_64-linux-gnu/libz.so.1.2.11 -7f55e49f3000-7f55e4bf2000 ---p 0001c000 08:06 11801866 /lib/x86_64-linux-gnu/libz.so.1.2.11 -7f55e4bf2000-7f55e4bf3000 r--p 0001b000 08:06 11801866 /lib/x86_64-linux-gnu/libz.so.1.2.11 -7f55e4bf3000-7f55e4bf4000 rw-p 0001c000 08:06 11801866 /lib/x86_64-linux-gnu/libz.so.1.2.11 -7f55e4bf4000-7f55e4ddb000 r-xp 00000000 08:06 11802658 /lib/x86_64-linux-gnu/libc-2.27.so -7f55e4ddb000-7f55e4fdb000 ---p 001e7000 08:06 11802658 /lib/x86_64-linux-gnu/libc-2.27.so -7f55e4fdb000-7f55e4fdf000 r--p 001e7000 08:06 11802658 /lib/x86_64-linux-gnu/libc-2.27.so -7f55e4fdf000-7f55e4fe1000 rw-p 001eb000 08:06 11802658 /lib/x86_64-linux-gnu/libc-2.27.so -7f55e4fe1000-7f55e4fe5000 rw-p 00000000 00:00 0 -7f55e4fe5000-7f55e4ff3000 r-xp 00000000 08:06 13375581 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/jli/libjli.so -7f55e4ff3000-7f55e51f2000 ---p 0000e000 08:06 13375581 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/jli/libjli.so -7f55e51f2000-7f55e51f3000 r--p 0000d000 08:06 13375581 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/jli/libjli.so -7f55e51f3000-7f55e51f4000 rw-p 0000e000 08:06 13375581 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/jli/libjli.so -7f55e51f4000-7f55e521d000 r-xp 00000000 08:06 11802653 /lib/x86_64-linux-gnu/ld-2.27.so -7f55e521d000-7f55e521e000 r--s 00003000 08:06 10093496 /home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.10/998caa30623f73223194a8b657abd2baec4880ea/kotlin-stdlib-jdk8-1.4.10.jar -7f55e521e000-7f55e5220000 r--s 00001000 08:06 13380618 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/dnsns.jar -7f55e5220000-7f55e5222000 r--s 00007000 08:06 13632766 /usr/share/java/java-atk-wrapper.jar -7f55e5222000-7f55e5224000 r--s 00010000 08:06 13380626 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/zipfs.jar -7f55e5224000-7f55e522a000 r--s 0003d000 08:06 13380624 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunjce_provider.jar -7f55e522a000-7f55e522e000 r--s 00040000 08:06 13380625 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunpkcs11.jar -7f55e522e000-7f55e522f000 r--s 0000d000 08:06 13380623 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunec.jar -7f55e522f000-7f55e5232000 r--s 0000f000 08:06 13380619 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/icedtea-sound.jar -7f55e5232000-7f55e5233000 r--s 0000a000 08:06 13380620 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/jaccess.jar -7f55e5233000-7f55e52fe000 rw-p 00000000 00:00 0 -7f55e52fe000-7f55e52ff000 ---p 00000000 00:00 0 -7f55e52ff000-7f55e5302000 ---p 00000000 00:00 0 -7f55e5302000-7f55e5401000 rw-p 00000000 00:00 0 -7f55e5401000-7f55e540b000 r--s 00117000 08:06 13380621 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/localedata.jar -7f55e540b000-7f55e5411000 r--s 000d3000 08:06 13380631 /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jfr.jar -7f55e5411000-7f55e5419000 rw-s 00000000 08:06 16648955 /tmp/hsperfdata_ceik/23465 -7f55e5419000-7f55e541a000 rw-p 00000000 00:00 0 -7f55e541a000-7f55e541b000 r--p 00000000 00:00 0 -7f55e541b000-7f55e541d000 rw-p 00000000 00:00 0 -7f55e541d000-7f55e541e000 r--p 00029000 08:06 11802653 /lib/x86_64-linux-gnu/ld-2.27.so -7f55e541e000-7f55e541f000 rw-p 0002a000 08:06 11802653 /lib/x86_64-linux-gnu/ld-2.27.so -7f55e541f000-7f55e5420000 rw-p 00000000 00:00 0 -7ffd3adec000-7ffd3ae0d000 rw-p 00000000 00:00 0 [stack] -7ffd3af4d000-7ffd3af50000 r--p 00000000 00:00 0 [vvar] -7ffd3af50000-7ffd3af51000 r-xp 00000000 00:00 0 [vdso] -ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] - -VM Arguments: -jvm_args: -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -java_command: org.rs09.client.GameLaunch -java_class_path (initial): /home/ceik/IdeaProjects/2009scape/Client/build/classes/java/main:/home/ceik/IdeaProjects/2009scape/Client/build/classes/kotlin/main:/home/ceik/IdeaProjects/2009scape/Client/build/resources/main:/home/ceik/IdeaProjects/2009scape/Client/libs/clientlibs.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.10/998caa30623f73223194a8b657abd2baec4880ea/kotlin-stdlib-jdk8-1.4.10.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/com.github.Vatuu/discord-rpc/1.6.2/233ee898e37dddd97ceaa9e7fd31a3eb8f10a128/discord-rpc-1.6.2.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/com.googlecode.json-simple/json-simple/1.1.1/c9ad4a0850ab676c5c64461a05ca524cdfff59f1/json-simple-1.1.1.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.10/30e46450b0bb3dbf43898d2f461be4a942784780/kotlin-stdlib-jdk7-1.4.10.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.10/ea29e063d2bbe695be13e9d044dcfb0c7add398e/kotlin-stdlib-1.4.10.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/junit/junit/4.10/e4f1766ce7404a08f45d859fb9c226fc9e41a861/junit-4.10.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.10/6229be3465805c99db1142ad75e6c6ddeac0b04c/kotlin-stdlib-common-1.4.10.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar:/home/ceik/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.1/860340562250678d1a344907ac75754e259cdb14/hamcrest-core-1.1.jar -Launcher Type: SUN_STANDARD - -Environment Variables: -PATH=/bedrock/cross/pin/bin:/bedrock/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/games:/usr/games:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/bedrock/cross/bin -SHELL=/bedrock/cross/bin/bash -DISPLAY=:0 - -Signal Handlers: -SIGSEGV: [libjvm.so+0xb6c690], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGBUS: [libjvm.so+0xb6c690], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGFPE: [libjvm.so+0x98c990], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGPIPE: [libjvm.so+0x98c990], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGXFSZ: [libjvm.so+0x98c990], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGILL: [libjvm.so+0x98c990], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGUSR1: SIG_DFL, sa_mask[0]=00000000000000000000000000000000, sa_flags=none -SIGUSR2: [libjvm.so+0x98c840], sa_mask[0]=00100000000000000000000000000000, sa_flags=SA_RESTART|SA_SIGINFO -SIGHUP: [libjvm.so+0x98ceb0], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGINT: [libjvm.so+0x98ceb0], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGTERM: [libjvm.so+0x98ceb0], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO -SIGQUIT: [libjvm.so+0x98ceb0], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO - - ---------------- S Y S T E M --------------- - -OS:DISTRIB_ID=elementary -DISTRIB_RELEASE=5.1.7 -DISTRIB_CODENAME=hera -DISTRIB_DESCRIPTION="elementary OS 5.1.7 Hera" - -uname:Linux 5.4.0-65-generic #73~18.04.1-Ubuntu SMP Tue Jan 19 09:02:24 UTC 2021 x86_64 -libc:glibc 2.27 NPTL 2.27 -rlimit: STACK 8192k, CORE 0k, NPROC 31096, NOFILE 1048576, AS infinity -load average:1.90 1.82 1.74 - -/proc/meminfo: -MemTotal: 8035576 kB -MemFree: 135340 kB -MemAvailable: 459952 kB -Buffers: 34784 kB -Cached: 851484 kB -SwapCached: 14640 kB -Active: 6497300 kB -Inactive: 1109580 kB -Active(anon): 6190188 kB -Inactive(anon): 898032 kB -Active(file): 307112 kB -Inactive(file): 211548 kB -Unevictable: 27488 kB -Mlocked: 64 kB -SwapTotal: 2097148 kB -SwapFree: 1694560 kB -Dirty: 4556 kB -Writeback: 0 kB -AnonPages: 6741496 kB -Mapped: 545228 kB -Shmem: 369688 kB -KReclaimable: 60404 kB -Slab: 127116 kB -SReclaimable: 60404 kB -SUnreclaim: 66712 kB -KernelStack: 19512 kB -PageTables: 65788 kB -NFS_Unstable: 0 kB -Bounce: 0 kB -WritebackTmp: 0 kB -CommitLimit: 6114936 kB -Committed_AS: 13096776 kB -VmallocTotal: 34359738367 kB -VmallocUsed: 34552 kB -VmallocChunk: 0 kB -Percpu: 1312 kB -HardwareCorrupted: 0 kB -AnonHugePages: 0 kB -ShmemHugePages: 0 kB -ShmemPmdMapped: 0 kB -FileHugePages: 0 kB -FilePmdMapped: 0 kB -CmaTotal: 0 kB -CmaFree: 0 kB -HugePages_Total: 0 -HugePages_Free: 0 -HugePages_Rsvd: 0 -HugePages_Surp: 0 -Hugepagesize: 2048 kB -Hugetlb: 0 kB -DirectMap4k: 233076 kB -DirectMap2M: 8038400 kB -DirectMap1G: 0 kB - -container (cgroup) information: -container_type: cgroupv1 -cpu_cpuset_cpus: 0-3 -cpu_memory_nodes: 0 -active_processor_count: 4 -cpu_quota: -1 -cpu_period: 100000 -cpu_shares: -1 -memory_limit_in_bytes: -1 -memory_and_swap_limit_in_bytes: -2 -memory_soft_limit_in_bytes: -1 -memory_usage_in_bytes: 7811866624 -memory_max_usage_in_bytes: 0 - - -CPU:total 4 (initial active 4) (2 cores per cpu, 2 threads per core) family 6 model 142 stepping 9, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, avx, avx2, aes, clmul, erms, 3dnowpref, lzcnt, ht, tsc, tscinvbit, bmi1, bmi2, adx - -/proc/cpuinfo: -processor : 0 -vendor_id : GenuineIntel -cpu family : 6 -model : 142 -model name : Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz -stepping : 9 -microcode : 0xde -cpu MHz : 2953.682 -cache size : 4096 KB -physical id : 0 -siblings : 4 -core id : 0 -cpu cores : 2 -apicid : 0 -initial apicid : 0 -fpu : yes -fpu_exception : yes -cpuid level : 22 -wp : yes -flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d -bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds -bogomips : 5799.77 -clflush size : 64 -cache_alignment : 64 -address sizes : 39 bits physical, 48 bits virtual -power management: - -processor : 1 -vendor_id : GenuineIntel -cpu family : 6 -model : 142 -model name : Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz -stepping : 9 -microcode : 0xde -cpu MHz : 2960.209 -cache size : 4096 KB -physical id : 0 -siblings : 4 -core id : 1 -cpu cores : 2 -apicid : 2 -initial apicid : 2 -fpu : yes -fpu_exception : yes -cpuid level : 22 -wp : yes -flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp -bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds -bogomips : 5799.77 -clflush size : 64 -cache_alignment : 64 -address sizes : 39 bits physical, 48 bits virtual -power management: - -processor : 2 -vendor_id : GenuineIntel -cpu family : 6 -model : 142 -model name : Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz -stepping : 9 -microcode : 0xde -cpu MHz : 2716.595 -cache size : 4096 KB -physical id : 0 -siblings : 4 -core id : 0 -cpu cores : 2 -apicid : 1 -initial apicid : 1 -fpu : yes -fpu_exception : yes -cpuid level : 22 -wp : yes -flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d -bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds -bogomips : 5799.77 -clflush size : 64 -cache_alignment : 64 -address sizes : 39 bits physical, 48 bits virtual -power management: - -processor : 3 -vendor_id : GenuineIntel -cpu family : 6 -model : 142 -model name : Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz -stepping : 9 -microcode : 0xde -cpu MHz : 2934.120 -cache size : 4096 KB -physical id : 0 -siblings : 4 -core id : 1 -cpu cores : 2 -apicid : 3 -initial apicid : 3 -fpu : yes -fpu_exception : yes -cpuid level : 22 -wp : yes -flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d -bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds -bogomips : 5799.77 -clflush size : 64 -cache_alignment : 64 -address sizes : 39 bits physical, 48 bits virtual -power management: - - - -Memory: 4k page, physical 8035576k(134836k free), swap 2097148k(1694560k free) - -vm_info: OpenJDK 64-Bit Server VM (25.275-b01) for linux-amd64 JRE (1.8.0_275-8u275-b01-0ubuntu1~18.04-b01), built on Nov 9 2020 02:38:58 by "buildd" with gcc 7.5.0 - -time: Thu Feb 11 14:07:59 2021 -timezone: CST -elapsed time: 13215.746486 seconds (0d 3h 40m 15s) - diff --git a/Client/hs_err_pid750.log b/Client/hs_err_pid750.log deleted file mode 100644 index 50df383a8..000000000 --- a/Client/hs_err_pid750.log +++ /dev/null @@ -1,9 +0,0 @@ -# -# A fatal error has been detected by the Java Runtime Environment: -# -# SIGBUS (0x7) at pc=0x00007f8aacc26730, pid=750, tid=0x00007f8aad494700 -# -# JRE version: OpenJDK Runtime Environment (8.0_275-b01) (build 1.8.0_275-8u275-b01-0ubuntu1~18.04-b01) -# Java VM: OpenJDK 64-Bit Server VM (25.275-b01 mixed mode linux-amd64 compressed oops) -# Problematic frame: -# \ No newline at end of file diff --git a/Client/libs/clientlibs.jar b/Client/libs/clientlibs.jar deleted file mode 100644 index 03c398fbc..000000000 Binary files a/Client/libs/clientlibs.jar and /dev/null differ diff --git a/Client/libs/discord-rpc.jar b/Client/libs/discord-rpc.jar deleted file mode 100644 index cee7079fb..000000000 Binary files a/Client/libs/discord-rpc.jar and /dev/null differ diff --git a/Client/src/main/java/org/runite/client/AbstractIndexedSprite.java b/Client/src/main/java/org/runite/client/AbstractIndexedSprite.java deleted file mode 100644 index 6dadc3283..000000000 --- a/Client/src/main/java/org/runite/client/AbstractIndexedSprite.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.runite.client; - -public abstract class AbstractIndexedSprite { - - public int width; - int anInt1464; - int anInt1467; - int height; - int anInt1469; - int anInt1470; - - - abstract void method1666(int var1, int var2, int var3); - - public abstract void method1667(int var1, int var2); - -} diff --git a/Client/src/main/java/org/runite/client/AbstractSprite.java b/Client/src/main/java/org/runite/client/AbstractSprite.java deleted file mode 100644 index 482d2dc76..000000000 --- a/Client/src/main/java/org/runite/client/AbstractSprite.java +++ /dev/null @@ -1,147 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; -import org.rs09.client.rendering.Toolkit; - -import java.util.Objects; - -public abstract class AbstractSprite extends Node { - - public int height; - int anInt3697; - int anInt3698; - public static int anInt3699 = 0; - int anInt3701; - static int anInt3704; - int anInt3706; - public int width; - static RSInterface aClass11_3708 = null; - - public static AbstractSprite constructItemSprite(int var0, boolean useHDSprite, int itemID, boolean var4, int outlineType, int itemCount, boolean shrinkInSprite) { - try { - ItemDefinition item = ItemDefinition.getItemDefinition(itemID); - if (itemCount > 1 && item.anIntArray804 != null) { - int var9 = -1; - - for (int var10 = 0; var10 < 10; ++var10) { - if (item.anIntArray766[var10] <= itemCount && item.anIntArray766[var10] != 0) { - var9 = item.anIntArray804[var10]; - } - } - - if (var9 != -1) { - item = ItemDefinition.getItemDefinition(var9); - } - } - - Class140_Sub1_Sub2 var21 = item.method1120(); - if (null == var21) { - return null; - } else { - SoftwareSprite var22 = null; - if (item.anInt791 == -1) { - if (item.anInt762 != -1) { - var22 = (SoftwareSprite) constructItemSprite(var0, true, item.anInt795, false, outlineType, itemCount, false); - if (null == var22) { - return null; - } - } - } else { - var22 = (SoftwareSprite) constructItemSprite(0, true, item.noteID, false, 1, 10, true); - if (null == var22) { - return null; - } - } - - int[] var11 = Toolkit.JAVA_TOOLKIT.getBuffer(); - int var12 = Toolkit.JAVA_TOOLKIT.width; - int var13 = Toolkit.JAVA_TOOLKIT.height; - int[] var14 = new int[4]; - Class74.method1325(var14); - SoftwareSprite var15 = new SoftwareSprite(36, 32); - Class74.setBuffer(var15.anIntArray4081, 36, 32); - Class51.method1134(); - Class51.method1145(16, 16); - int var16 = item.anInt810; - Class51.aBoolean843 = false; - if (shrinkInSprite) { - var16 = (int) ((double) var16 * 1.5D); - } else if (outlineType == 2) { - var16 = (int) (1.04D * (double) var16); - } - - int var18 = Class51.anIntArray851[item.anInt786] * var16 >> 16; - int var17 = Class51.anIntArray840[item.anInt786] * var16 >> 16; - var21.method1893(item.anInt799, item.anInt768, item.anInt786, item.anInt792, var17 - (var21.method1871() / 2 + -item.anInt754), item.anInt754 + var18); - if (outlineType >= 1) { - var15.method657(1); - if (outlineType >= 2) { - var15.method657(16777215); - } - - Class74.setBuffer(var15.anIntArray4081, 36, 32); - } - - if (var0 != 0) { - var15.method668(var0); - } - - if (item.anInt791 != -1) { - Objects.requireNonNull(var22).drawAt(0, 0); - } else if (-1 != item.anInt762) { - Class74.setBuffer(Objects.requireNonNull(var22).anIntArray4081, 36, 32); - var15.drawAt(0, 0); - var15 = var22; - } - - if (var4 && (item.stackingType == 1 || itemCount != 1) && itemCount != -1) { - TextureOperation10.aClass3_Sub28_Sub17_Sub1_3440.method681(Class3_Sub7.itemStackColor(1000, itemCount), 0, 9, 16776960, 1); - } - - Class74.setBuffer(var11, var12, var13); - Class74.setClipping(var14); - Class51.method1134(); - Class51.aBoolean843 = true; - return HDToolKit.highDetail && !useHDSprite ? new HDSprite(var15) : var15; - } - } catch (RuntimeException var20) { - throw ClientErrorException.clientError(var20, "na.WA(" + var0 + ',' + useHDSprite + ',' + itemID + ',' + var4 + ',' + outlineType + ',' + itemCount + ',' + shrinkInSprite + ')'); - } - } - - abstract void method635(int var1, int var2); - - abstract void method636(int var1, int var2, int var3, int var4, int var5, int var6); - - abstract void method637(int var1, int var2, int var3); - - public static RSInterface method638(int var1, int var2) { - try { - RSInterface var3 = Unsorted.getRSInterface(var1); - return var2 == -1 ?var3: var3 != null && var3.aClass11Array262 != null && var2 < var3.aClass11Array262.length ?var3.aClass11Array262[var2]:null; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "qf.P(" + (byte) -19 + ',' + var1 + ',' + var2 + ')'); - } - } - - public abstract void method639(int var1, int var2, int var3, int var4); - - public final void drawScaledOrRotated(int x, int y, int angle, int scale) { - try { - int var6 = this.anInt3697 << 3; - int var7 = this.anInt3706 << 3; - scale = (scale << 4) + (var6 & 15); - x = (x << 4) + (15 & var7); - this.method636(var6, var7, scale, x, y, angle); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "qf.F(" + x + ',' + y + ',' + angle + ',' + scale + ',' + -1470985020 + ')'); - } - } - - abstract void method641(int var1, int var2); - - abstract void method642(int var1, int var2, int var3, int var4, int var5); - - public abstract void drawAt(int var1, int var2); - -} diff --git a/Client/src/main/java/org/runite/client/AnimationHeader.java b/Client/src/main/java/org/runite/client/AnimationHeader.java deleted file mode 100644 index 8a832305a..000000000 --- a/Client/src/main/java/org/runite/client/AnimationHeader.java +++ /dev/null @@ -1,210 +0,0 @@ -package org.runite.client; - - -final class AnimationHeader { - - boolean aBoolean1382 = false; - int anInt1383; - Class3_Sub17 skins; - short[] aShortArray1385; - boolean aBoolean1386 = false; - short[] aShortArray1387; - short[] aShortArray1388; - private static final byte[] aByteArray1389 = new byte[500]; - private static final short[] aShortArray1390 = new short[500]; - private static final short[] aShortArray1391 = new short[500]; - private static final short[] aShortArray1392 = new short[500]; - byte[] aByteArray1393; - private static final short[] aShortArray1394 = new short[500]; - short[] aShortArray1395; - short[] aShortArray1396; - private static final short[] aShortArray1397 = new short[500]; - - - // AnimationHeader(byte[] bs, Class3_Sub17 skin) { -// this.skins = skin; -// RSByteBuffer buffer = new RSByteBuffer(bs); -// RSByteBuffer parent = new RSByteBuffer(bs); -// buffer.index = 2; -// int size = buffer.getByte((byte)-86); -// int var6 = 0; -// int var7 = -1; -// parent.index = buffer.index + size; -// -// int i; -// for(i = 0; i < size; ++i) { -// -// int var11 = buffer.getByte((byte)-55); -// if(var11 > 0) { -// int var10 = this.skins.anIntArray2466[i]; -// if (skins.anIntArray2466[i] != 0) { -// for (int j = i - 1; j > var7; j--) { -// if (skins.anIntArray2466[j] == 0) { -// aShortArray1397[var7] = (short) j; -// aShortArray1394[var7] = 0; -// aShortArray1391[var7] = 0; -// aShortArray1390[var7] = 0; -// var6++; -// break; -// } -// } -// aShortArray1397[var6] = (short)i; -// short var12 = 0; -// if(var10 == 3) { -// var12 = 128; -// } -// -// if((var11 & 1) != 0) { -// aShortArray1394[var6] = (short)parent.getSmart(-21208); -// } else { -// aShortArray1394[var6] = var12; -// } -// -// if((var11 & 2) != 0) { -// aShortArray1391[var6] = (short)parent.getSmart(-21208); -// } else { -// aShortArray1391[var6] = var12; -// } -// -// if((var11 & 4) != 0) { -// aShortArray1390[var6] = (short)parent.getSmart(-21208); -// } else { -// aShortArray1390[var6] = var12; -// } -// -// aByteArray1389[var6] = (byte)(var11 >>> 3 & 3); -// if(var10 == 2) { -// aShortArray1394[var6] = (short)(((aShortArray1394[var6] & 0xFF) << 3) + (aShortArray1394[var6] >> 8 & 7)); -// aShortArray1391[var6] = (short)(((aShortArray1391[var6] & 0xFF) << 3) + (aShortArray1391[var6] >> 8 & 7)); -// aShortArray1390[var6] = (short)(((aShortArray1390[var6] & 0xFF) << 3) + (aShortArray1390[var6] >> 8 & 7)); -// } -// var7 = i; -// if(var10 != 1 && var10 != 2 && var10 != 3) { -// if(var10 == 5) { -// this.aBoolean1386 = true; -// } else if(var10 == 7) { -// this.aBoolean1382 = true; -// } -// } -// -// ++var6; -// } -// } -// -// if(parent.index != bs.length) { -// throw new RuntimeException(); -// } else { -// this.anInt1383 = var6; -// this.aShortArray1385 = new short[var6]; -// this.aShortArray1388 = new short[var6]; -// this.aShortArray1396 = new short[var6]; -// this.aShortArray1395 = new short[var6]; -// this.aShortArray1387 = new short[var6]; -// this.aByteArray1393 = new byte[var6]; -// -// for(i = 0; i < var6; ++i) { -// this.aShortArray1385[i] = aShortArray1397[i]; -// this.aShortArray1388[i] = aShortArray1394[i]; -// this.aShortArray1396[i] = aShortArray1391[i]; -// this.aShortArray1395[i] = aShortArray1390[i]; -// this.aShortArray1387[i] = aShortArray1392[i]; -// this.aByteArray1393[i] = aByteArray1389[i]; -// } -// -// } -// } -// } - AnimationHeader(byte[] bs, Class3_Sub17 skin) { - this.skins = skin; - DataBuffer buffer = new DataBuffer(bs); - DataBuffer parent = new DataBuffer(bs); - buffer.index = 2; - int size = buffer.readUnsignedByte(); - int var6 = 0; - int var7 = -1; - int var8 = -1; - parent.index = buffer.index + size; - - int i; - for(i = 0; i < size; ++i) { - int var10 = this.skins.anIntArray2466[i]; - if(var10 == 0) { - var7 = i; - } - int var11 = buffer.readUnsignedByte(); - if(var11 > 0) { - if(var10 == 0) { - var8 = i; - } - - aShortArray1397[var6] = (short)i; - short var12 = 0; - if(var10 == 3) { - var12 = 128; - } - - if((var11 & 1) == 0) { - aShortArray1394[var6] = var12; - } else { - aShortArray1394[var6] = (short)parent.getByteOrShort(); - } - - if((var11 & 2) == 0) { - aShortArray1391[var6] = var12; - } else { - aShortArray1391[var6] = (short)parent.getByteOrShort(); - } - - if((var11 & 4) == 0) { - aShortArray1390[var6] = var12; - } else { - aShortArray1390[var6] = (short)parent.getByteOrShort(); - } - - aByteArray1389[var6] = (byte)(var11 >>> 3 & 3); - if(var10 == 2) { - aShortArray1394[var6] = (short)(((aShortArray1394[var6] & 0xFF) << 3) + (aShortArray1394[var6] >> 8 & 7)); - aShortArray1391[var6] = (short)(((aShortArray1391[var6] & 0xFF) << 3) + (aShortArray1391[var6] >> 8 & 7)); - aShortArray1390[var6] = (short)(((aShortArray1390[var6] & 0xFF) << 3) + (aShortArray1390[var6] >> 8 & 7)); - } - - aShortArray1392[var6] = -1; - if(var10 != 1 && var10 != 2 && var10 != 3) { - if(var10 == 5) { - this.aBoolean1386 = true; - } else if(var10 == 7) { - this.aBoolean1382 = true; - } - } else if(var7 > var8) { - aShortArray1392[var6] = (short)var7; - var8 = var7; - } - - ++var6; - } - } - - if(parent.index == bs.length) { - this.anInt1383 = var6; - this.aShortArray1385 = new short[var6]; - this.aShortArray1388 = new short[var6]; - this.aShortArray1396 = new short[var6]; - this.aShortArray1395 = new short[var6]; - this.aShortArray1387 = new short[var6]; - this.aByteArray1393 = new byte[var6]; - - for(i = 0; i < var6; ++i) { - this.aShortArray1385[i] = aShortArray1397[i]; - this.aShortArray1388[i] = aShortArray1394[i]; - this.aShortArray1396[i] = aShortArray1391[i]; - this.aShortArray1395[i] = aShortArray1390[i]; - this.aShortArray1387[i] = aShortArray1392[i]; - this.aByteArray1393[i] = aByteArray1389[i]; - } - - } else { - throw new RuntimeException(); - } - } - - } diff --git a/Client/src/main/java/org/runite/client/AssembledMethod.java b/Client/src/main/java/org/runite/client/AssembledMethod.java deleted file mode 100644 index 057a4bd8f..000000000 --- a/Client/src/main/java/org/runite/client/AssembledMethod.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; -import org.rs09.client.data.HashTable; - -final class AssembledMethod extends Node { - - // The ints are loaded in this order - int numberOfIntsToCopy; - int numberOfRSStringsToCopy; - int numberOfIntArguments; - int numberOfStringArguments; - - int[] assemblyInstructions; - int[] instructionOperands; - HashTable[] switchHashTable; - RSString[] stringInstructionOperands; -} diff --git a/Client/src/main/java/org/runite/client/AssembledMethodContainer.java b/Client/src/main/java/org/runite/client/AssembledMethodContainer.java deleted file mode 100644 index 4940cabbc..000000000 --- a/Client/src/main/java/org/runite/client/AssembledMethodContainer.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.runite.client; - -final class AssembledMethodContainer { - int[] intArguments; - RSString[] stringArguments; - AssembledMethod assembledMethod; - int currentProgramCounter = -1; -} diff --git a/Client/src/main/java/org/runite/client/AtmosphereParser.java b/Client/src/main/java/org/runite/client/AtmosphereParser.java deleted file mode 100644 index 9fd08d924..000000000 --- a/Client/src/main/java/org/runite/client/AtmosphereParser.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.HashTable; - -public final class AtmosphereParser { - - static HashTable aHashTable_3679 = new HashTable(16); - static AtmosphereParser[][] aAtmosphereParserArrayArray1581 = new AtmosphereParser[13][13]; - int anInt1175; - int anInt1177; - int anInt1178; - int anInt1181; - static Class91[] aClass91Array1182 = new Class91[4]; - int anInt1184; - int anInt1185; - static Class41 aClass41_1186; - float aFloat1187; - float aFloat1189; - float aFloat1190; - static int anInt1191; - static int screenLowerY; - - static void method1428(int var0, int var2) { - try { - ItemDefinition.ram[var0] = var2; - Class3_Sub7 var3 = (Class3_Sub7) aHashTable_3679.get(var0); - if (var3 == null) { - var3 = new Class3_Sub7(TimeUtils.time() - -500L); - aHashTable_3679.put(var0, var3); - } else { - var3.aLong2295 = 500L + TimeUtils.time(); - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "li.D(" + var0 + ',' + var2 + ')'); - } - } - - static Class3_Sub28_Sub17_Sub1 method1430(int var1) { - try { - Class3_Sub28_Sub17_Sub1 var2 = (Class3_Sub28_Sub17_Sub1) Unsorted.aReferenceCache_1135.get(var1); - if (var2 == null) { - byte[] var3 = CacheIndex.fontsIndex.getFile(var1, 0); - var2 = new Class3_Sub28_Sub17_Sub1(var3); - var2.method697(Sprites.nameIconsSpriteArray, null); - Unsorted.aReferenceCache_1135.put(var2, var1); - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "li.A(" + -28922 + ',' + var1 + ')'); - } - } - - public AtmosphereParser() { - try { - this.anInt1177 = Class92.defaultScreenColorRgb; - this.aFloat1189 = 1.2F; - this.anInt1178 = -50; - this.aFloat1187 = 1.1523438F; - this.anInt1175 = Class92.defaultRegionAmbientRGB; - this.anInt1181 = -60; - this.aFloat1190 = 0.69921875F; - this.anInt1184 = 0; - this.anInt1185 = -50; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "li.()"); - } - } - - AtmosphereParser(DataBuffer var1) { - try { - int var2 = var1.readUnsignedByte(); - if ((var2 & 1) == 0) { - this.anInt1177 = Class92.defaultScreenColorRgb; - } else { - this.anInt1177 = var1.readInt(); - } - - if ((2 & var2) == 0) { - this.aFloat1187 = 1.1523438F; - } else { - this.aFloat1187 = (float) var1.readUnsignedShort() / 256.0F; - } - - if ((var2 & 4) == 0) { - this.aFloat1190 = 0.69921875F; - } else { - this.aFloat1190 = (float) var1.readUnsignedShort() / 256.0F; - } - - if ((var2 & 8) == 0) { - this.aFloat1189 = 1.2F; - } else { - this.aFloat1189 = (float) var1.readUnsignedShort() / 256.0F; - } - - if ((16 & var2) == 0) { - this.anInt1178 = -50; - this.anInt1185 = -50; - this.anInt1181 = -60; - } else { - this.anInt1185 = var1.readSignedShort(); - this.anInt1181 = var1.readSignedShort(); - this.anInt1178 = var1.readSignedShort(); - } - - if ((32 & var2) == 0) { - this.anInt1175 = Class92.defaultRegionAmbientRGB; - } else { - this.anInt1175 = var1.readInt(); - } - - if ((64 & var2) == 0) { - this.anInt1184 = 0; - } else { - this.anInt1184 = var1.readUnsignedShort(); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "li.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/AudioChannel.java b/Client/src/main/java/org/runite/client/AudioChannel.java deleted file mode 100644 index d08fe52f8..000000000 --- a/Client/src/main/java/org/runite/client/AudioChannel.java +++ /dev/null @@ -1,439 +0,0 @@ -package org.runite.client; - -import org.rs09.client.util.ArrayUtils; - -import java.awt.*; - -class AudioChannel { - - public static boolean stereo; - static int[] anIntArray1969 = new int[100]; - static int threadPriority; - static AudioThread aAudioThread_2613; - private final Class3_Sub24[] aClass3_Sub24Array1980 = new Class3_Sub24[8]; - private final Class3_Sub24[] aClass3_Sub24Array1983 = new Class3_Sub24[8]; - int[] samples; - int sampleRate; - int bufferSize; - private long aLong1972 = TimeUtils.time(); - private Class3_Sub24 aClass3_Sub24_1973; - private long aLong1979 = 0L; - private int anInt1981 = 0; - private long aLong1982 = 0L; - private boolean aBoolean1984 = true; - private int anInt1985 = 0; - private int anInt1986; - private int anInt1987 = 0; - private int anInt1988 = 0; - private int anInt1968 = 32; - - static void method2162(GameObject var0, int var1, int var2, int var3) { - Class3_Sub2 var4; - if (var2 < Unsorted.anInt1234) { - var4 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var1][var2 + 1][var3]; - if (var4 != null && var4.aClass12_2230 != null && var4.aClass12_2230.object.method1865()) { - var0.method1866(var4.aClass12_2230.object, 128, 0, 0, true); - } - } - - if (var3 < Unsorted.anInt1234) { - var4 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var1][var2][var3 + 1]; - if (var4 != null && var4.aClass12_2230 != null && var4.aClass12_2230.object.method1865()) { - var0.method1866(var4.aClass12_2230.object, 0, 0, 128, true); - } - } - - if (var2 < Unsorted.anInt1234 && var3 < TextureOperation17.anInt3179) { - var4 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var1][var2 + 1][var3 + 1]; - if (var4 != null && var4.aClass12_2230 != null && var4.aClass12_2230.object.method1865()) { - var0.method1866(var4.aClass12_2230.object, 128, 0, 128, true); - } - } - - if (var2 < Unsorted.anInt1234 && var3 > 0) { - var4 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var1][var2 + 1][var3 - 1]; - if (var4 != null && var4.aClass12_2230 != null && var4.aClass12_2230.object.method1865()) { - var0.method1866(var4.aClass12_2230.object, 128, 0, -128, true); - } - } - - } - - static AudioChannel method1195(int sampleRate, Signlink signlink, Component component, int channelID) { - try { - if (Class21.sampleRate == 0) { - throw new IllegalStateException(); - } else if (0 <= channelID && 2 > channelID) { - if (sampleRate < 256) { - sampleRate = 256; - } - - try { - AudioChannel channel = new JavaAudioChannel(); - channel.sampleRate = sampleRate; - channel.samples = new int[(!stereo ? 1 : 2) * 256]; - channel.init(component); - channel.bufferSize = (sampleRate & 0xfffffc00) + 1024; - if (channel.bufferSize > 16384) { - channel.bufferSize = 16384; - } - - channel.open(channel.bufferSize); - if (threadPriority > 0 && null == aAudioThread_2613) { - aAudioThread_2613 = new AudioThread(); - aAudioThread_2613.aClass87_350 = signlink; - signlink.startThread(threadPriority, aAudioThread_2613); - } - - if (aAudioThread_2613 != null) { - if (null != aAudioThread_2613.channels[channelID]) { - throw new IllegalArgumentException(); - } - - aAudioThread_2613.channels[channelID] = channel; - } - - return channel; - } catch (Throwable var7) { - try { - SignLinkAudioChannel signLinkAudioChannel = new SignLinkAudioChannel(signlink, channelID); - signLinkAudioChannel.samples = new int[256 * (stereo ? 2 : 1)]; - signLinkAudioChannel.sampleRate = sampleRate; - signLinkAudioChannel.init(component); - signLinkAudioChannel.bufferSize = 16384; - signLinkAudioChannel.open(signLinkAudioChannel.bufferSize); - if (threadPriority > 0 && null == aAudioThread_2613) { - aAudioThread_2613 = new AudioThread(); - aAudioThread_2613.aClass87_350 = signlink; - signlink.startThread(threadPriority, aAudioThread_2613); - } - - if (aAudioThread_2613 != null) { - if (aAudioThread_2613.channels[channelID] != null) { - throw new IllegalArgumentException(); - } - - aAudioThread_2613.channels[channelID] = signLinkAudioChannel; - } - - return signLinkAudioChannel; - } catch (Throwable var6) { - return new AudioChannel(); - } - } - } else { - throw new IllegalArgumentException(); - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "id.D(" + sampleRate + ',' + (signlink != null ? "{...}" : "null") + ',' + (component != null ? "{...}" : "null") + ',' + channelID + ',' + 14 + ')'); - } - } - - void write() { - } - - void open(int var1) throws Exception { - } - - void flush() throws Exception { - } - - private void method2152(int[] var1) { - int var3 = 256; - if (stereo) { - var3 = 256 << 1; - } - - ArrayUtils.zero(var1, 0, var3); - this.anInt1987 -= 256; - if (this.aClass3_Sub24_1973 != null && this.anInt1987 <= 0) { - this.anInt1987 += Class21.sampleRate >> 4; - Class97.method1591(this.aClass3_Sub24_1973); - this.method2155(this.aClass3_Sub24_1973, this.aClass3_Sub24_1973.method412()); - int var4 = 0; - int var5 = 255; - - int var6; - label101: - for (var6 = 7; var5 != 0; --var6) { - int var7; - int var8; - if (var6 < 0) { - var7 = var6 & 0x3; - var8 = -(var6 >> 2); - } else { - var7 = var6; - var8 = 0; - } - - for (int var9 = var5 >>> var7 & 0x11111111; var9 != 0; var9 >>>= 4) { - if ((var9 & 0x1) != 0) { - var5 &= ~(1 << var7); - Class3_Sub24 var10 = null; - Class3_Sub24 var11 = this.aClass3_Sub24Array1980[var7]; - - while (var11 != null) { - Sound var12 = var11.aSound_2544; - if (var12 != null && var12.anInt2374 > var8) { - var5 |= 1 << var7; - var10 = var11; - var11 = var11.aClass3_Sub24_2546; - } else { - var11.aBoolean2545 = true; - int var13 = var11.method409(); - var4 += var13; - if (var12 != null) { - var12.anInt2374 += var13; - } - - if (var4 >= anInt1968) { - break label101; - } - - Class3_Sub24 var14 = var11.method411(); - if (var14 != null) { - for (int var15 = var11.anInt2543; var14 != null; var14 = var11.method414()) { - this.method2155(var14, var15 * var14.method412() >> 8); - } - } - - Class3_Sub24 var18 = var11.aClass3_Sub24_2546; - var11.aClass3_Sub24_2546 = null; - if (var10 == null) { - this.aClass3_Sub24Array1980[var7] = var18; - } else { - var10.aClass3_Sub24_2546 = var18; - } - - if (var18 == null) { - this.aClass3_Sub24Array1983[var7] = var10; - } - - var11 = var18; - } - } - } - - var7 += 4; - ++var8; - } - } - - for (var6 = 0; var6 < 8; ++var6) { - Class3_Sub24 var16 = this.aClass3_Sub24Array1980[var6]; - - Class3_Sub24 var17; - for (this.aClass3_Sub24Array1980[var6] = this.aClass3_Sub24Array1983[var6] = null; var16 != null; var16 = var17) { - var17 = var16.aClass3_Sub24_2546; - var16.aClass3_Sub24_2546 = null; - } - } - } - - if (this.anInt1987 < 0) { - this.anInt1987 = 0; - } - - if (this.aClass3_Sub24_1973 != null) { - this.aClass3_Sub24_1973.method413(var1, 0, 256); - } - - this.aLong1972 = TimeUtils.time(); - } - - final synchronized void method2153() { - try { - if (null != this.samples) { - long var2 = TimeUtils.time(); - - try { - if (0L != this.aLong1982) { - if (var2 < this.aLong1982) { - return; - } - - this.open(this.bufferSize); - this.aBoolean1984 = true; - this.aLong1982 = 0L; - } - - int var4 = this.method2157(); - if (-var4 + this.anInt1985 > this.anInt1981) { - this.anInt1981 = -var4 + this.anInt1985; - } - - int var5 = this.sampleRate - -this.anInt1986; - if (256 + var5 > 16384) { - var5 = 16128; - } - - if (this.bufferSize < var5 + 256) { - this.bufferSize += 1024; - if (this.bufferSize > 16384) { - this.bufferSize = 16384; - } - - this.close(); - var4 = 0; - this.open(this.bufferSize); - if (this.bufferSize < 256 + var5) { - var5 = -256 + this.bufferSize; - this.anInt1986 = -this.sampleRate + var5; - } - - this.aBoolean1984 = true; - } - - while (var4 < var5) { - var4 += 256; - this.method2152(this.samples); - this.write(); - } - - if (this.aLong1979 < var2) { - if (this.aBoolean1984) { - this.aBoolean1984 = false; - } else { - if (this.anInt1981 == 0 && this.anInt1988 == 0) { - this.close(); - this.aLong1982 = var2 - -2000L; - return; - } - - this.anInt1986 = Math.min(this.anInt1988, this.anInt1981); - this.anInt1988 = this.anInt1981; - } - - this.aLong1979 = 2000L + var2; - this.anInt1981 = 0; - } - - this.anInt1985 = var4; - } catch (Exception var7) { - this.close(); - this.aLong1982 = var2 + 2000L; - } - - try { - if (var2 > 500000L + this.aLong1972) { - var2 = this.aLong1972; - } - - while (var2 > this.aLong1972 + 5000L) { - this.method2161(); - this.aLong1972 += 256000 / Class21.sampleRate; - } - } catch (Exception var6) { - this.aLong1972 = var2; - } - - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "vh.Q(" + (byte) -34 + ')'); - } - } - - final synchronized void method2154(Class3_Sub24 var2) { - this.aClass3_Sub24_1973 = var2; - } - - private void method2155(Class3_Sub24 var1, int var2) { - try { - int var4 = var2 >> 5; - Class3_Sub24 var5 = this.aClass3_Sub24Array1983[var4]; - if (null == var5) { - this.aClass3_Sub24Array1980[var4] = var1; - } else { - var5.aClass3_Sub24_2546 = var1; - } - - this.aClass3_Sub24Array1983[var4] = var1; - var1.anInt2543 = var2; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "vh.H(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + (byte) -24 + ')'); - } - } - - int method2157() { - try { - return this.bufferSize; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "vh.B()"); - } - } - - final synchronized void method2158() { - try { - this.aBoolean1984 = true; - - try { - this.flush(); - } catch (Exception var3) { - this.close(); - this.aLong1982 = TimeUtils.time() + 2000L; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "vh.L(" + (byte) -78 + ')'); - } - } - - final void method2159() { - this.aBoolean1984 = true; - } - - void close() { - } - - private void method2161() { - try { - this.anInt1987 -= 256; - if (0 > this.anInt1987) { - this.anInt1987 = 0; - } - - if (null != this.aClass3_Sub24_1973) { - this.aClass3_Sub24_1973.method415(256); - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "vh.K(" + 256 + ',' + 1 + ')'); - } - } - - final synchronized void method2163() { - try { - if (null != aAudioThread_2613) { - boolean var2 = true; - - for (int var3 = 0; var3 < 2; ++var3) { - if (this == aAudioThread_2613.channels[var3]) { - aAudioThread_2613.channels[var3] = null; - } - - if (null != aAudioThread_2613.channels[var3]) { - var2 = false; - } - } - - if (var2) { - aAudioThread_2613.aBoolean345 = true; - - while (aAudioThread_2613.aBoolean353) { - TimeUtils.sleep(50L); - } - - aAudioThread_2613 = null; - } - } - - this.close(); - this.samples = null; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "vh.P(" + false + ')'); - } - } - - void init(Component var1) throws Exception { - } - -} diff --git a/Client/src/main/java/org/runite/client/AudioHandler.java b/Client/src/main/java/org/runite/client/AudioHandler.java deleted file mode 100644 index 6ac7860d2..000000000 --- a/Client/src/main/java/org/runite/client/AudioHandler.java +++ /dev/null @@ -1,173 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -public final class AudioHandler { - - - public static int[] soundEffectDelayArray = new int[50]; - public static int musicVolume = 255; - static int currentSoundEffectCount = 0; - static int soundEffectVolume = 127; - static int currentTrack = -1; - static boolean musicEffectPlaying = false; - static int[] soundEffectVolumeArray = new int[50]; - static int[] soundEffectIDs = new int[50]; - static SynthSound[] soundEffects = new SynthSound[50]; - static int[] soundEffectCoordinates = new int[50]; - - public static void musicHandler(int var1) { - try { - if (-1 == var1 && !musicEffectPlaying) { - GameObject.method1870(); - } else if (var1 != -1 && (currentTrack != var1 || CSConfigCachefile.method1391(-1)) && musicVolume != 0 && !musicEffectPlaying) { - method2099(var1, CacheIndex.musicIndex, musicVolume); - } - currentTrack = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "li.B(" + true + ',' + var1 + ')'); - } - } - - public static void soundEffectHandler(int soundEffectVolume, int soundEffectID, int soundEffectDelay) { - try { - System.out.println("Playing " + soundEffectID + " DELAY: " + soundEffectDelay + " VOLUME: " + soundEffectVolume); - if (soundEffectDelay != -1 && soundEffectVolume != 0 && currentSoundEffectCount < 50 && soundEffectID != -1) { - soundEffectVolumeArray[currentSoundEffectCount] = soundEffectVolume; - soundEffectIDs[currentSoundEffectCount] = soundEffectID; - soundEffectDelayArray[currentSoundEffectCount] = soundEffectDelay; - soundEffects[currentSoundEffectCount] = null; - soundEffectCoordinates[currentSoundEffectCount] = 0; - currentSoundEffectCount++; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ca.C(" + soundEffectVolume + ',' + soundEffectID + ',' + soundEffectDelay + ',' + -799 + ')'); - } - } - - static void method132() { - try { - for (int var1 = 0; var1 < currentSoundEffectCount; ++var1) { - --soundEffectDelayArray[var1]; - if (soundEffectDelayArray[var1] < -10) { - --currentSoundEffectCount; - - for (int var2 = var1; var2 < currentSoundEffectCount; ++var2) { - soundEffectIDs[var2] = soundEffectIDs[var2 + 1]; - soundEffects[var2] = soundEffects[var2 + 1]; - soundEffectVolumeArray[var2] = soundEffectVolumeArray[1 + var2]; - soundEffectDelayArray[var2] = soundEffectDelayArray[1 + var2]; - soundEffectCoordinates[var2] = soundEffectCoordinates[var2 + 1]; - } - - --var1; - } else { - SynthSound var11 = soundEffects[var1]; - if (null == var11) { - var11 = SynthSound.create(CacheIndex.soundFXIndex, soundEffectIDs[var1], 0); - if (null == var11) { - continue; - } - - soundEffectDelayArray[var1] += var11.getStart(); - soundEffects[var1] = var11; - } - - if (0 > soundEffectDelayArray[var1]) { - int var3; - if (soundEffectCoordinates[var1] == 0) { - var3 = soundEffectVolume; - } else { - int var4 = (soundEffectCoordinates[var1] & 0xFF) * 128; - int var7 = soundEffectCoordinates[var1] >> 8 & 0xFF; - int var5 = soundEffectCoordinates[var1] >> 16 & 0xFF; - int var8 = -Class102.player.zAxis + 64 + 128 * var7; - if (var8 < 0) { - var8 = -var8; - } - - int var6 = -Class102.player.xAxis + 64 + var5 * 128; - if (0 > var6) { - var6 = -var6; - } - - int var9 = -128 + var6 + var8; - if (var9 > var4) { - soundEffectDelayArray[var1] = -100; - continue; - } - - if (var9 < 0) { - var9 = 0; - } - - var3 = Sprites.ambientVolume * (var4 + -var9) / var4; - } - - if (var3 > 0) { - PcmSound var12 = var11.toPCMSound().method151(Class27.resampler); - Class3_Sub24_Sub1 var13 = Class3_Sub24_Sub1.method437(var12, var3); - Objects.requireNonNull(var13).method429(soundEffectVolumeArray[var1] - 1); - Class3_Sub26.aClass3_Sub24_Sub2_2563.method457(var13); - } - - soundEffectDelayArray[var1] = -100; - } - } - } - - if (musicEffectPlaying && CSConfigCachefile.method1391(-1)) { - if (0 != musicVolume && currentTrack != -1) { - Class70.method1285(CacheIndex.musicIndex, currentTrack, musicVolume); - } - - musicEffectPlaying = false; - } else if (musicVolume != 0 && currentTrack != -1 && CSConfigCachefile.method1391((byte) -92 + 91)) { - TextureOperation12.outgoingBuffer.putOpcode(137); - TextureOperation12.outgoingBuffer.writeInt(currentTrack); - currentTrack = -1; - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "ed.C(" + (byte) -92 + ')'); - } - } - - public static void method2099(int var1, CacheIndex var3, int var5) { - try { - Class101.aClass153_1423 = var3; - Class132.anInt1741 = 0; - TextureOperation8.anInt3463 = var1; - Unsorted.aBoolean2311 = false; - Unsorted.anInt154 = 1; - GraphicDefinition.anInt546 = 2; - - TextureOperation36.anInt3423 = var5; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "v.Q(" + true + ',' + var1 + ',' + 0 + ',' + (var3 != null ? "{...}" : "null") + ',' + false + ',' + var5 + ',' + 2 + ')'); - } - } - - static void method897(Class3_Sub24_Sub4 var1, CacheIndex var2, CacheIndex var3, CacheIndex var4) { - try { - Class124.aClass153_1661 = var2; - Class40.aClass153_679 = var4; - Class3_Sub28_Sub20.aClass153_3786 = var3; - Class101.aClass3_Sub24_Sub4_1421 = var1; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ck.C(" + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ')'); - } - } - - public static void musicEffectHandler(int var1) { - try { - if (musicVolume != 0 && var1 != -1) { - Class70.method1285(CacheIndex.music2Index, var1, musicVolume); - musicEffectPlaying = true; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "wj.D(" + ',' + var1 + ',' + (byte) -1 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/AudioThread.java b/Client/src/main/java/org/runite/client/AudioThread.java deleted file mode 100644 index 9d0a24c93..000000000 --- a/Client/src/main/java/org/runite/client/AudioThread.java +++ /dev/null @@ -1,159 +0,0 @@ -package org.runite.client; - -import java.util.Calendar; -import java.util.Date; - -public final class AudioThread implements Runnable { - - static short[][] aShortArrayArray344; - static int anInt4034 = -8 + (int) (17.0D * Math.random()); - static boolean aBoolean346; - public static int[] localNPCIndexes = new int[32768]; - static Class64 aClass64_351; - volatile boolean aBoolean345 = false; - Signlink aClass87_350; - volatile AudioChannel[] channels = new AudioChannel[2]; - volatile boolean aBoolean353 = false; - - - static boolean method888(int var0, ObjectDefinition var1, int var3, int var5, int var6) { - try { - MapSceneDefinition var7 = InterfaceWidget.c(var1.anInt1516); - if (var7.sprite == -1) { - return false; - } else { - if (var1.aBoolean1537) { - var6 += var1.anInt1478; - var6 &= 3; - } else { - var6 = 0; - } - - LDIndexedSprite var8 = var7.getSprite(var6); - if (var8 == null) { - return true; - } else { - int var9 = var1.SizeX; - int var10 = var1.SizeY; - if (1 == (1 & var6)) { - var9 = var1.SizeY; - var10 = var1.SizeX; - } - - int var11 = var8.anInt1469; - int var12 = var8.anInt1467; - if (var7.aBoolean69) { - var12 = 4 * var10; - var11 = 4 * var9; - } - - if (var7.color == 0) { - var8.method1677(var0 * 4 + 48, 48 + 4 * (-var10 + -var5 + 104), var11, var12); - } else { - var8.method1669(48 + 4 * var0, 4 * (-var10 + -var5 + 104) + 48, var11, var12, var7.color); - } - - return false; - } - } - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "cj.D(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ',' + false + ',' + var3 + ',' + 0 + ',' + var5 + ',' + var6 + ')'); - } - } - - static AbstractSprite[] method891(int var0) { - try { - AbstractSprite[] var1 = new AbstractSprite[Class95.anInt1338]; - if (var0 != -5) { - method894(113L); - } - - for (int var2 = 0; var2 < Class95.anInt1338; ++var2) { - int var3 = Class140_Sub7.anIntArray2931[var2] * Unsorted.anIntArray3076[var2]; - byte[] var4 = Class163_Sub1.aByteArrayArray2987[var2]; - int[] var5 = new int[var3]; - - for (int var6 = 0; var6 < var3; ++var6) { - var5[var6] = TextureOperation38.spritePalette[Unsorted.bitwiseAnd(255, var4[var6])]; - } - - if (HDToolKit.highDetail) { - var1[var2] = new HDSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], var5); - } else { - var1[var2] = new SoftwareSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], var5); - } - } - - Class39.method1035((byte) 116); - return var1; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "cj.C(" + var0 + ')'); - } - } - - static RSString method894(long var0) { - try { - Class3_Sub28_Sub5.aCalendar3581.setTime(new Date(var0)); - int var3 = Class3_Sub28_Sub5.aCalendar3581.get(Calendar.DAY_OF_WEEK);//Day of the week - int var4 = Class3_Sub28_Sub5.aCalendar3581.get(Calendar.DATE); - int var5 = Class3_Sub28_Sub5.aCalendar3581.get(Calendar.MONTH); - int var6 = Class3_Sub28_Sub5.aCalendar3581.get(Calendar.YEAR); - int var7 = Class3_Sub28_Sub5.aCalendar3581.get(Calendar.HOUR_OF_DAY); - int var8 = Class3_Sub28_Sub5.aCalendar3581.get(Calendar.MINUTE); - int var9 = Class3_Sub28_Sub5.aCalendar3581.get(Calendar.SECOND); - return RSString.stringCombiner(new RSString[]{TextCore.DaysOfTheWeek[var3 + -1], RSString.parse(")1 "), RSString.stringAnimator(var4 / 10), RSString.stringAnimator(var4 % 10), TextCore.aClass94_2025, TextCore.MonthsOfTheYear[var5], TextCore.aClass94_2025, RSString.stringAnimator(var6), RSString.parse(" "), RSString.stringAnimator(var7 / 10), RSString.stringAnimator(var7 % 10), RSString.parse(":"), RSString.stringAnimator(var8 / 10), RSString.stringAnimator(var8 % 10), RSString.parse(":"), RSString.stringAnimator(var9 / 10), RSString.stringAnimator(var9 % 10), RSString.parse(" GMT")}); - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "cj.F(" + var0 + ')'); - } - } - - static int[][] method895(int var4) { - try { - int[][] var9 = new int[256][64]; - TextureOperation34 var10 = new TextureOperation34(); - var10.anInt3062 = (int) ((float) 0.4 * 4096.0F); - var10.anInt3058 = 3; - var10.anInt3056 = 4; - var10.aBoolean3065 = false; - var10.anInt3060 = 8; - var10.postDecode(); - TextureOperation33.method180(122, 256, 64); - - for (int var11 = 0; var11 < 256; ++var11) { - var10.method186(var11, var9[var11]); - } - - return var9; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "cj.B(" + false + ',' + 3 + ',' + 64 + ',' + 256 + ',' + var4 + ',' + 4 + ',' + 8 + ',' + (float) 0.4 + ')'); - } - } - - public final void run() { - try { - this.aBoolean353 = true; - - try { - while (!this.aBoolean345) { - for (int var1 = 0; var1 < 2; ++var1) { - AudioChannel var2 = this.channels[var1]; - if (var2 != null) { - var2.method2153(); - } - } - - TimeUtils.sleep(10L); - Class81.method1400(this.aClass87_350, null, -71); - } - } catch (Exception var7) { - Class49.reportError(null, var7); - } finally { - this.aBoolean353 = false; - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "cj.run()"); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/BufferedDataStream.java b/Client/src/main/java/org/runite/client/BufferedDataStream.java deleted file mode 100644 index 44f9cc4de..000000000 --- a/Client/src/main/java/org/runite/client/BufferedDataStream.java +++ /dev/null @@ -1,1064 +0,0 @@ -package org.runite.client; - -import org.rs09.SystemLogger; - -import java.util.Objects; - -public final class BufferedDataStream extends DataBuffer { - - public static BufferedDataStream incomingBuffer = new BufferedDataStream(); - static RSString[] aClass94Array3802; - static int anInt872; - static int anInt2330 = 0; - static float aFloat3044; - static int anInt4037; - static int anInt1345; - static int anInt1407; - static int anInt1473; - static float aFloat1475; - static int anInt1736; - static int[] anIntArray2709 = new int[]{0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, Integer.MAX_VALUE, -1}; - static int anInt1971; - static int anInt3652; - private ISAACCipher isaacCipher; - static int[] anIntArray3804 = new int[256]; - static int[] anIntArray3805; - private int headiconsPrayerSpriteArchive6; - - - static void addChatMessage(RSString var0, int type, RSString message, int var3) { - try { - MessageManager.sendGameMessage(var3, type, message, null, var0); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "i.W(" + (var0 != null ? "{...}" : "null") + ',' + type + ',' + (message != null ? "{...}" : "null") + ',' + var3 + ')'); - } - } - - static void method806(int var1) { - try { - if (var1 >= 0) { - int var2 = Class117.anIntArray1613[var1]; - int var3 = Class27.anIntArray512[var1]; - int var4 = TextureOperation27.aShortArray3095[var1]; - if (var4 >= 2000) { - var4 -= 2000; - } - - long var6 = Unsorted.aLongArray3271[var1]; - int var5 = (int) Unsorted.aLongArray3271[var1]; - Player var8; - if (31 == var4) { - var8 = Unsorted.players[var5]; - if (null != var8) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class36.anInt638 = 2; - Unsorted.anInt2958 = 0; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - TextureOperation12.outgoingBuffer.putOpcode(71); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - } - - if (var4 == 46) { - Class163_Sub2_Sub1.method2224(var6, var3, var2); - TextureOperation12.outgoingBuffer.putOpcode(247); - TextureOperation12.outgoingBuffer.writeShortLE(Texture.anInt1152 + var3); - TextureOperation12.outgoingBuffer.writeShort128LE(var2 + Class131.anInt1716); - TextureOperation12.outgoingBuffer.writeShort(Integer.MAX_VALUE & (int) (var6 >>> 32)); - } - - if (var4 == 40) { - TextureOperation12.outgoingBuffer.putOpcode(27); - TextureOperation12.outgoingBuffer.writeShort(anInt1473); - TextureOperation12.outgoingBuffer.writeIntLE2(var3); - TextureOperation12.outgoingBuffer.writeShortLE(var2); - TextureOperation12.outgoingBuffer.writeIntLE2(Class3_Sub28_Sub18.anInt3764); - TextureOperation12.outgoingBuffer.writeShort128LE(Class164.anInt2050); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - System.out.println(anInt1473 + ", " + var3 + ", " + var2 + ", " + Class3_Sub28_Sub18.anInt3764 + ", " + Class164.anInt2050 + ", " + var5); - } - - NPC var11; - if (var4 == 19) { - var11 = NPC.npcs[var5]; - if (null != var11) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var11.anIntArray2767[0], 1, 0, 2, var11.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class70.anInt1053 = Class163_Sub1.anInt2993; - Class36.anInt638 = 2; - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - TextureOperation12.outgoingBuffer.putOpcode(30); - TextureOperation12.outgoingBuffer.writeShort(var5); - } - } - - if (17 == var4) { - var11 = NPC.npcs[var5]; - if (var11 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var11.anIntArray2767[0], 1, 0, 2, var11.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - TextureOperation12.outgoingBuffer.putOpcode(78); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - } - } - - if (var4 == 44) { - var8 = Unsorted.players[var5]; - if (null != var8) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class70.anInt1053 = Class163_Sub1.anInt2993; - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Unsorted.anInt2958 = 0; - TextureOperation12.outgoingBuffer.putOpcode(133); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - } - } - - if (var4 == 58) { - TextureOperation12.outgoingBuffer.putOpcode(135); - TextureOperation12.outgoingBuffer.putShortA(var5); - TextureOperation12.outgoingBuffer.putShortA(var2); - TextureOperation12.outgoingBuffer.writeIntV2(var3); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 42) { - Class163_Sub2_Sub1.method2224(var6, var3, var2); - TextureOperation12.outgoingBuffer.putOpcode(254); - TextureOperation12.outgoingBuffer.writeShortLE(var2 + Class131.anInt1716); - TextureOperation12.outgoingBuffer.putShortA((int) (var6 >>> 32) & Integer.MAX_VALUE); - TextureOperation12.outgoingBuffer.writeShort(var3 - -Texture.anInt1152); - } - - if (28 == var4) { - TextureOperation4.method264((byte) 122); - } - - if (var4 == 45) { - var11 = NPC.npcs[var5]; - if (var11 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var11.anIntArray2767[0], 1, 0, 2, var11.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class70.anInt1053 = Class163_Sub1.anInt2993; - Class36.anInt638 = 2; - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - TextureOperation12.outgoingBuffer.putOpcode(239); - TextureOperation12.outgoingBuffer.writeIntLE2(anInt872); - TextureOperation12.outgoingBuffer.putShortA(RSInterface.anInt278); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - } - - boolean var14; - if (18 == var4) { - if (Class158.paramGameTypeID == 1) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } else { - var14 = Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, false, 0, 2597 ^ 2599, var2, 0, 0, 2, var3, Class102.player.anIntArray2767[0]); - if (!var14) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } - } - - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - TextureOperation12.outgoingBuffer.putOpcode(66); - TextureOperation12.outgoingBuffer.writeShortLE(Class131.anInt1716 + var2); - TextureOperation12.outgoingBuffer.writeShort(var5); - TextureOperation12.outgoingBuffer.writeShort128LE(var3 - -Texture.anInt1152); - } - - if (var4 == 1001) { - Class163_Sub2_Sub1.method2224(var6, var3, var2); - TextureOperation12.outgoingBuffer.putOpcode(170); - TextureOperation12.outgoingBuffer.writeShort128LE(Integer.MAX_VALUE & (int) (var6 >>> 32)); - TextureOperation12.outgoingBuffer.writeShort128LE(var2 - -Class131.anInt1716); - TextureOperation12.outgoingBuffer.writeShort128LE(var3 - -Texture.anInt1152); - } - - if (var4 == 1002) { - Class36.anInt638 = 2; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Unsorted.anInt2958 = 0; - TextureOperation12.outgoingBuffer.putOpcode(92); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - - RSInterface var13; - if (var4 == 1006) { - var13 = Unsorted.getRSInterface(var3); - if (null != var13 && var13.itemIds[var2] >= 100000) { - addChatMessage(RSString.parse(""), 0, RSString.stringCombiner(new RSString[]{RSString.stringAnimator(var13.itemIds[var2]), TextCore.aClass94_3777, ItemDefinition.getItemDefinition(var5).name}), -1); - } else { - TextureOperation12.outgoingBuffer.putOpcode(92); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 60) { - if (var5 == 0) { - Unsorted.method589(WorldListCountry.localPlane, var2, var3); - } else if (var5 == 1) { - if (0 < Player.rights && ObjectDefinition.aBooleanArray1490[82] && ObjectDefinition.aBooleanArray1490[81]) { - Class30.method979(Class131.anInt1716 + var2, Texture.anInt1152 + var3, WorldListCountry.localPlane); - } else if (Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, true, 0, 2, var2, 0, 0, 1, var3, Class102.player.anIntArray2767[0])) { - TextureOperation12.outgoingBuffer.writeByte(Class1.anInt56); - TextureOperation12.outgoingBuffer.writeByte(Class58.anInt916); - TextureOperation12.outgoingBuffer.writeShort(GraphicDefinition.CAMERA_DIRECTION); - TextureOperation12.outgoingBuffer.writeByte(57); - TextureOperation12.outgoingBuffer.writeByte(TextureOperation9.anInt3102); - TextureOperation12.outgoingBuffer.writeByte(Class164_Sub2.anInt3020); - TextureOperation12.outgoingBuffer.writeByte(89); - TextureOperation12.outgoingBuffer.writeShort(Class102.player.xAxis); - TextureOperation12.outgoingBuffer.writeShort(Class102.player.zAxis); - TextureOperation12.outgoingBuffer.writeByte(Class129.anInt1692); - TextureOperation12.outgoingBuffer.writeByte(63); - } - } - } - - if (1007 == var4) { - Unsorted.anInt2958 = 0; - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - var11 = NPC.npcs[var5]; - if (var11 != null) { - NPCDefinition var9 = var11.definition; - if (var9.childNPCs != null) { - var9 = var9.method1471((byte) 80); - } - - if (null != var9) { - TextureOperation12.outgoingBuffer.putOpcode(72); - TextureOperation12.outgoingBuffer.writeShort(var9.npcId); - } - } - } - - if (var4 == 47) { - if (MouseWheel.shiftDown) { - - TextureOperation12.outgoingBuffer.putOpcode(135); - TextureOperation12.outgoingBuffer.putShortA(var5); - TextureOperation12.outgoingBuffer.putShortA(var2); - TextureOperation12.outgoingBuffer.writeIntV2(var3); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - - - } else { - TextureOperation12.outgoingBuffer.putOpcode(156); - TextureOperation12.outgoingBuffer.writeShort128LE(var2); - TextureOperation12.outgoingBuffer.putShortA(var5); - TextureOperation12.outgoingBuffer.writeIntLE2(var3); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - } - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 3) { - TextureOperation12.outgoingBuffer.putOpcode(253); - TextureOperation12.outgoingBuffer.writeIntLE2(anInt872); - TextureOperation12.outgoingBuffer.writeShort128LE(var2); - TextureOperation12.outgoingBuffer.writeIntLE2(var3); - TextureOperation12.outgoingBuffer.putShortA(var5); - TextureOperation12.outgoingBuffer.writeShortLE(RSInterface.anInt278); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 10) { - var8 = Unsorted.players[var5]; - if (var8 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - TextureOperation12.outgoingBuffer.putOpcode(4); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - } - } - - if (41 == var4 && TextureOperation27.aClass11_3087 == null) { - Unsorted.method78(var2, var3); - TextureOperation27.aClass11_3087 = AbstractSprite.method638(var3, var2); - Class20.method909(TextureOperation27.aClass11_3087); - } - - if (49 == var4) { - Class163_Sub2_Sub1.method2224(var6, var3, var2); - TextureOperation12.outgoingBuffer.putOpcode(84); - TextureOperation12.outgoingBuffer.writeShort128LE(Integer.MAX_VALUE & (int) (var6 >>> 32)); - TextureOperation12.outgoingBuffer.writeShort128LE(Texture.anInt1152 + var3); - TextureOperation12.outgoingBuffer.writeShortLE(var2 - -Class131.anInt1716); - } - - if (var4 == 23) { - TextureOperation12.outgoingBuffer.putOpcode(206); - TextureOperation12.outgoingBuffer.putShortA(var5);//itemId - TextureOperation12.outgoingBuffer.writeShortLE(var2);//data - TextureOperation12.outgoingBuffer.writeIntLE2(var3);//slot - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 14) { - Class163_Sub2_Sub1.method2224(var6, var3, var2); - TextureOperation12.outgoingBuffer.putOpcode(134); - TextureOperation12.outgoingBuffer.putShortA(Class131.anInt1716 + var2); - TextureOperation12.outgoingBuffer.writeShort(Class164.anInt2050); - TextureOperation12.outgoingBuffer.writeShortLE(var3 - -Texture.anInt1152); - TextureOperation12.outgoingBuffer.writeShort(anInt1473); - TextureOperation12.outgoingBuffer.writeIntV2(Class3_Sub28_Sub18.anInt3764); - TextureOperation12.outgoingBuffer.putShortA((int) (var6 >>> 32) & Integer.MAX_VALUE); - } - - if (var4 == 37) { - var8 = Unsorted.players[var5]; - if (var8 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Unsorted.anInt2958 = 0; - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - TextureOperation12.outgoingBuffer.putOpcode(114); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - } - - if (var4 == 9 || 1003 == var4) { - Unsorted.method66(Class163_Sub2_Sub1.aClass94Array4016[var1], var2, var5, (byte) -19, var3); - } - - if (var4 == 5) { - if (MouseWheel.shiftDown) { - TextureOperation12.outgoingBuffer.putOpcode(135); - TextureOperation12.outgoingBuffer.putShortA(var5); - TextureOperation12.outgoingBuffer.putShortA(var2); - TextureOperation12.outgoingBuffer.writeIntV2(var3); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - - } else { - TextureOperation12.outgoingBuffer.putOpcode(55); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - TextureOperation12.outgoingBuffer.putShortA(var2); - TextureOperation12.outgoingBuffer.writeIntV1(var3); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - - } - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 21) { - if (Class158.paramGameTypeID == 1) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2597 + -2595, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } else { - var14 = Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, false, 0, 2597 + -2595, var2, 0, 0, 2, var3, Class102.player.anIntArray2767[0]); - if (!var14) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2597 + -2595, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } - } - - Class36.anInt638 = 2; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - TextureOperation12.outgoingBuffer.putOpcode(228); - TextureOperation12.outgoingBuffer.writeShort(var5); - TextureOperation12.outgoingBuffer.writeShortLE(Class131.anInt1716 + var2); - TextureOperation12.outgoingBuffer.writeShort128LE(Texture.anInt1152 + var3); - } - - if (var4 == 4) { - var11 = NPC.npcs[var5]; - if (var11 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var11.anIntArray2767[0], 1, 0, 2, var11.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class36.anInt638 = 2; - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - TextureOperation12.outgoingBuffer.putOpcode(148); - TextureOperation12.outgoingBuffer.putShortA(var5); - } - } - - if (32 == var4) { - var13 = AbstractSprite.method638(var3, var2); - if (null != var13) { - Class25.method958((byte) -126); - Class3_Sub1 var16 = Client.method44(var13); - Class145.method2074(var3, var2, var16.method101(), var16.anInt2202, var13.anInt266, var13.anInt238); - Class164_Sub1.anInt3012 = 0; - Class3_Sub28_Sub9.aClass94_3621 = Class53.method1174(var13, (byte) -94); - if (Class3_Sub28_Sub9.aClass94_3621 == null) { - Class3_Sub28_Sub9.aClass94_3621 = TextCore.aClass94_1915; - } - - if (var13.usingScripts) { - TextCore.aClass94_676 = RSString.stringCombiner(new RSString[]{var13.aClass94_277, ColorCore.ContextColor}); - } else { - TextCore.aClass94_676 = RSString.stringCombiner(new RSString[]{RSString.parse(""), var13.aClass94_243, ColorCore.ContextColor}); - } - } - - } else { - if (29 == var4) { - var8 = Unsorted.players[var5]; - if (null != var8) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Unsorted.anInt2958 = 0; - Class36.anInt638 = 2; - Class70.anInt1053 = Class163_Sub1.anInt2993; - TextureOperation12.outgoingBuffer.putOpcode(180); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - } - - if (var4 == 35) { - TextureOperation12.outgoingBuffer.putOpcode(161); - TextureOperation12.outgoingBuffer.writeIntLE2(var3); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - TextureOperation12.outgoingBuffer.writeShort128LE(var2); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (15 == var4) { - var8 = Unsorted.players[var5]; - if (var8 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Unsorted.anInt2958 = 0; - Class36.anInt638 = 2; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - TextureOperation12.outgoingBuffer.putOpcode(195); - TextureOperation12.outgoingBuffer.putShortA(RSInterface.anInt278); - TextureOperation12.outgoingBuffer.writeIntLE2(anInt872); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - } - - if (34 == var4) { - if (Class158.paramGameTypeID == 1) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } else { - var14 = Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, false, 0, 2597 ^ 2599, var2, 0, 0, 2, var3, Class102.player.anIntArray2767[0]); - if (!var14) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } - } - - Class70.anInt1053 = Class163_Sub1.anInt2993; - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Unsorted.anInt2958 = 0; - TextureOperation12.outgoingBuffer.putOpcode(109); - TextureOperation12.outgoingBuffer.writeShortLE(var3 - -Texture.anInt1152); - TextureOperation12.outgoingBuffer.writeShort(var2 + Class131.anInt1716); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - - if (var4 == 25) { - TextureOperation12.outgoingBuffer.putOpcode(81); - TextureOperation12.outgoingBuffer.putShortA(var2); - TextureOperation12.outgoingBuffer.writeShort(var5); - TextureOperation12.outgoingBuffer.writeIntV1(var3); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 2) { - var11 = NPC.npcs[var5]; - if (var11 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2597 + -2595, var11.anIntArray2767[0], 1, 0, 2, var11.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class36.anInt638 = 2; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - TextureOperation12.outgoingBuffer.putOpcode(218); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - } - } - - int var12; - if (var4 == 51) { - TextureOperation12.outgoingBuffer.putOpcode(10); - TextureOperation12.outgoingBuffer.writeInt(var3); - var13 = Unsorted.getRSInterface(var3); - if (Objects.requireNonNull(var13).childDataBuffers != null && var13.childDataBuffers[0][0] == 5) { - var12 = var13.childDataBuffers[0][1]; - if (ItemDefinition.ram[var12] != var13.anIntArray307[0]) { - ItemDefinition.ram[var12] = var13.anIntArray307[0]; - Class46.method1087(98, var12); - } - } - } - - if (var4 == 26) { - var11 = NPC.npcs[var5]; - if (var11 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var11.anIntArray2767[0], 1, 0, 2, var11.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class36.anInt638 = 2; - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - TextureOperation12.outgoingBuffer.putOpcode(115);//Item on NPC - TextureOperation12.outgoingBuffer.writeIntV2(Class3_Sub28_Sub18.anInt3764); - TextureOperation12.outgoingBuffer.writeShortLE(anInt1473); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - TextureOperation12.outgoingBuffer.writeShort128LE(Class164.anInt2050); -// System.out.println(Class3_Sub28_Sub18.anInt3764 + ", " + Class110.anInt1473 + ", " + var5 + ", " + Class164.anInt2050); - } - } - - if (59 == var4) { - TextureOperation12.outgoingBuffer.putOpcode(10); - TextureOperation12.outgoingBuffer.writeInt(var3); - var13 = Unsorted.getRSInterface(var3); - if (Objects.requireNonNull(var13).childDataBuffers != null && var13.childDataBuffers[0][0] == 5) { - var12 = var13.childDataBuffers[0][1]; - ItemDefinition.ram[var12] = -ItemDefinition.ram[var12] + 1; - Class46.method1087(68, var12); - } - } - - if (var4 == 33) { - var14 = Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, false, 0, 2, var2, 0, 0, 2, var3, Class102.player.anIntArray2767[0]); - if (!var14) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2597 + -2595, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } - - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class36.anInt638 = 2; - TextureOperation12.outgoingBuffer.putOpcode(101); - TextureOperation12.outgoingBuffer.writeShort128LE(var2 - -Class131.anInt1716); - TextureOperation12.outgoingBuffer.writeShortLE(anInt1473); - TextureOperation12.outgoingBuffer.writeShortLE(Class164.anInt2050); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - TextureOperation12.outgoingBuffer.writeShort128LE(Texture.anInt1152 + var3); - TextureOperation12.outgoingBuffer.writeIntV2(Class3_Sub28_Sub18.anInt3764); - } - - if (var4 == 1004) { - Unsorted.anInt2958 = 0; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - TextureOperation12.outgoingBuffer.putOpcode(94); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - - if (11 == var4) { - if (var5 == 0) { - CS2Script.anInt2440 = 1; - Unsorted.method589(WorldListCountry.localPlane, var2, var3); - } else if (1 == var5) { - TextureOperation12.outgoingBuffer.putOpcode(131); - TextureOperation12.outgoingBuffer.writeIntV2(anInt872); - TextureOperation12.outgoingBuffer.putShortA(Class131.anInt1716 + var2); - TextureOperation12.outgoingBuffer.writeShort128LE(RSInterface.anInt278); - TextureOperation12.outgoingBuffer.putShortA(var3 + Texture.anInt1152); - } - } - - if (8 == var4) { - var13 = Unsorted.getRSInterface(var3); - boolean var15 = true; - if (0 < Objects.requireNonNull(var13).anInt189) { - var15 = method715(var13); - } - - if (var15) { - TextureOperation12.outgoingBuffer.putOpcode(10); - TextureOperation12.outgoingBuffer.writeInt(var3); - } - } - - if (var4 == 1) { - var8 = Unsorted.players[var5]; - if (var8 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class36.anInt638 = 2; - Class70.anInt1053 = Class163_Sub1.anInt2993; - TextureOperation12.outgoingBuffer.putOpcode(248); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - TextureOperation12.outgoingBuffer.writeShort(Class164.anInt2050); - TextureOperation12.outgoingBuffer.writeShort(anInt1473); - TextureOperation12.outgoingBuffer.writeIntV2(Class3_Sub28_Sub18.anInt3764); - } - } - - if (var4 == 7) { - TextureOperation12.outgoingBuffer.putOpcode(85); - TextureOperation12.outgoingBuffer.writeIntV1(var3); - TextureOperation12.outgoingBuffer.writeShort(var2); - TextureOperation12.outgoingBuffer.putShortA(var5); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 24) { - if (Class158.paramGameTypeID == 1) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2597 + -2595, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } else { - var14 = Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, false, 0, 2, var2, 0, 0, 2, var3, Class102.player.anIntArray2767[0]); - if (!var14) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } - } - - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - TextureOperation12.outgoingBuffer.putOpcode(48); - TextureOperation12.outgoingBuffer.putShortA(var2 - -Class131.anInt1716); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - TextureOperation12.outgoingBuffer.writeShortLE(Texture.anInt1152 + var3); - } - - if (var4 == 38) { - Class163_Sub2_Sub1.method2224(var6, var3, var2); - TextureOperation12.outgoingBuffer.putOpcode(233); - TextureOperation12.outgoingBuffer.writeShort128LE(var3 + Texture.anInt1152); - TextureOperation12.outgoingBuffer.putShortA(Class131.anInt1716 + var2); - TextureOperation12.outgoingBuffer.writeShort128LE(RSInterface.anInt278); - TextureOperation12.outgoingBuffer.writeIntV1(anInt872); - TextureOperation12.outgoingBuffer.putShortA((int) (var6 >>> 32) & Integer.MAX_VALUE); - } - - if (var4 == 13) { - TextureOperation12.outgoingBuffer.putOpcode(6); - TextureOperation12.outgoingBuffer.writeInt(var3); - TextureOperation12.outgoingBuffer.putShortA(var2); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (57 == var4) { - var8 = Unsorted.players[var5]; - if (null != var8) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class36.anInt638 = 2; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - TextureOperation12.outgoingBuffer.putOpcode(175); - TextureOperation12.outgoingBuffer.putShortA(var5); - } - } - - if (var4 == 22) { - - if (MouseWheel.shiftDown) { - TextureOperation12.outgoingBuffer.putOpcode(135); - TextureOperation12.outgoingBuffer.putShortA(var5); - TextureOperation12.outgoingBuffer.putShortA(var2); - TextureOperation12.outgoingBuffer.writeIntV2(var3); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - - } else { - - Class25.method958((byte) -86); - var13 = Unsorted.getRSInterface(var3); - Class3_Sub28_Sub18.anInt3764 = var3; - anInt1473 = var2; - Class164_Sub1.anInt3012 = 1; - Class164.anInt2050 = var5; - Class20.method909(var13); - RenderAnimationDefinition.aClass94_378 = RSString.stringCombiner(new RSString[]{ - ColorCore.ContextColor2, ItemDefinition.getItemDefinition(var5).name, ColorCore.ContextColor - }); - - } - } else { - if (var4 == 50) { - Class163_Sub2_Sub1.method2224(var6, var3, var2); - TextureOperation12.outgoingBuffer.putOpcode(194); - TextureOperation12.outgoingBuffer.writeShort128LE(var3 + Texture.anInt1152); - TextureOperation12.outgoingBuffer.writeShortLE(Class131.anInt1716 + var2); - TextureOperation12.outgoingBuffer.writeShort((int) (var6 >>> 32) & Integer.MAX_VALUE); - } - - if (var4 == 48) { - TextureOperation12.outgoingBuffer.putOpcode(154); - TextureOperation12.outgoingBuffer.writeShortLE(var2); - TextureOperation12.outgoingBuffer.writeIntV1(var3); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 30) { - var8 = Unsorted.players[var5]; - if (null != var8) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Unsorted.anInt2958 = 0; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class36.anInt638 = 2; - TextureOperation12.outgoingBuffer.putOpcode(68); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - } - - if (var4 == 43) { - TextureOperation12.outgoingBuffer.putOpcode(153); - TextureOperation12.outgoingBuffer.writeIntLE2(var3); - TextureOperation12.outgoingBuffer.writeShortLE(var2); - TextureOperation12.outgoingBuffer.writeShortLE(var5); - anInt2330 = 0; - Unsorted.aClass11_1933 = Unsorted.getRSInterface(var3); - KeyboardListener.anInt1918 = var2; - } - - if (var4 == 39) { - var14 = Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, false, 0, 2, var2, 0, 0, 2, var3, Class102.player.anIntArray2767[0]); - if (!var14) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } - - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Class36.anInt638 = 2; - Unsorted.anInt2958 = 0; - TextureOperation12.outgoingBuffer.putOpcode(73); - TextureOperation12.outgoingBuffer.writeIntV1(anInt872); - TextureOperation12.outgoingBuffer.writeShort(Texture.anInt1152 + var3); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - TextureOperation12.outgoingBuffer.writeShort128LE(var2 + Class131.anInt1716); - TextureOperation12.outgoingBuffer.writeShortLE(RSInterface.anInt278); - } - - if (var4 == 12) { - TextureOperation12.outgoingBuffer.putOpcode(82); - TextureOperation12.outgoingBuffer.writeShort(RSInterface.anInt278); - TextureOperation12.outgoingBuffer.writeIntV1(var3); - TextureOperation12.outgoingBuffer.writeInt(anInt872); - TextureOperation12.outgoingBuffer.writeShort128LE(var2); - } - - if (var4 == 36) { - if (var5 == 0) { - ObjectDefinition.anInt1521 = 1; - Unsorted.method589(WorldListCountry.localPlane, var2, var3); - } else if (Player.rights > 0 && ObjectDefinition.aBooleanArray1490[82] && ObjectDefinition.aBooleanArray1490[81]) { - Class30.method979(var2 + Class131.anInt1716, Texture.anInt1152 - -var3, WorldListCountry.localPlane); - } else { - TextureOperation12.outgoingBuffer.putOpcode(179); - TextureOperation12.outgoingBuffer.writeShort(var3 + Texture.anInt1152); - TextureOperation12.outgoingBuffer.writeShort(var2 + Class131.anInt1716); - } - } - - if (6 == var4) { - var8 = Unsorted.players[var5]; - if (var8 != null) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var8.anIntArray2767[0], 1, 0, 2, var8.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Unsorted.anInt2958 = 0; - Class36.anInt638 = 2; - Class70.anInt1053 = Class163_Sub1.anInt2993; - TextureOperation12.outgoingBuffer.putOpcode(106); - TextureOperation12.outgoingBuffer.writeShort(var5); - } - } - - if (var4 == 20) { - if (1 == Class158.paramGameTypeID) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } else { - var14 = Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, false, 0, 2, var2, 0, 0, 2, var3, Class102.player.anIntArray2767[0]); - if (!var14) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var2, 1, 0, 2, var3, Class102.player.anIntArray2767[0]); - } - } - - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Unsorted.anInt2958 = 0; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Class36.anInt638 = 2; - TextureOperation12.outgoingBuffer.putOpcode(33); - TextureOperation12.outgoingBuffer.writeShort(var5); - TextureOperation12.outgoingBuffer.writeShort(Class131.anInt1716 + var2); - TextureOperation12.outgoingBuffer.writeShortLE(Texture.anInt1152 + var3); - } - - if (var4 == 16) { - var11 = NPC.npcs[var5]; - if (null != var11) { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var11.anIntArray2767[0], 1, 0, 2, var11.anIntArray2755[0], Class102.player.anIntArray2767[0]); - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class36.anInt638 = 2; - TextureOperation12.outgoingBuffer.putOpcode(3); - TextureOperation12.outgoingBuffer.writeShort128LE(var5); - } - } - - if (Class164_Sub1.anInt3012 != 0) { - Class164_Sub1.anInt3012 = 0; - Class20.method909(Unsorted.getRSInterface(Class3_Sub28_Sub18.anInt3764)); - } - - if (GameObject.aBoolean1837) { - Class25.method958((byte) -36); - } - - if (Unsorted.aClass11_1933 != null && anInt2330 == 0) { - Class20.method909(Unsorted.aClass11_1933); - } - - } - } - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "i.E(" + 2597 + ',' + var1 + ')'); - } - } - - static boolean method715(RSInterface var1) { - try { - if (205 == var1.anInt189) { - Class159.anInt2023 = 250; - return true; - } else { - return false; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ud.B(" + 205 + ',' + "null" + ')'); - } - } - - final void setBitAccess() { - try { - this.headiconsPrayerSpriteArchive6 = this.index * 8; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "i.R(" + ')'); - } - } - - public BufferedDataStream() { - super(5000); - } - - static int method809(int var0, int var1, int var2, int var3) { - try { - if (TextureOperation14.aBoolean3387) { - var0 = 1000000; - TextureOperation14.aBoolean3387 = false; - } - - AtmosphereParser var5 = AtmosphereParser.aAtmosphereParserArrayArray1581[var3][var1]; - float var7 = ((float) var2 * 0.1F + 0.7F) * var5.aFloat1187; - float var8 = var5.aFloat1190; - int var6 = var5.anInt1177; - int var11 = var5.anInt1184; - int var10 = var5.anInt1175; - if (!Class38.aBoolean661) { - var11 = 0; - } - - float var9 = var5.aFloat1189; - if (var6 != Class60.anInt932 || Class3_Sub17.aFloat2457 != var7 || aFloat3044 != var8 || var9 != RSInterface.aFloat246 || anInt1345 != var10 || anInt1736 != var11) { - Class3_Sub17.aFloat2457 = var7; - TextureOperation36.aFloat3435 = TextureOperation36.aFloat3424; - TextureOperation9.aFloat3105 = Class30.aFloat578; - Class60.anInt932 = var6; - anInt1971 = anInt3652; - anInt1407 = Unsorted.anInt689; - RSInterface.aFloat246 = var9; - Unsorted.anInt72 = 0; - anInt4037 = Unsorted.anInt1950; - anInt1736 = var11; - aFloat3044 = var8; - anInt1345 = var10; - aFloat1475 = Class12.aFloat319; - } - - if (65536 > Unsorted.anInt72) { - Unsorted.anInt72 += 250 * var0; - if (Unsorted.anInt72 >= 65536) { - Unsorted.anInt72 = 65536; - } - - float var15 = (float) Unsorted.anInt72 / 65536.0F; - int var13 = Unsorted.anInt72 >> 8; - int var12 = -Unsorted.anInt72 + 65536 >> 8; - anInt3652 = (-16711936 & var13 * (anInt1345 & 16711935) + (16711935 & anInt1971) * var12) + (16711680 & var12 * (anInt1971 & 65280) + (65280 & anInt1345) * var13) >> 8; - float var14 = (float) (65536 - Unsorted.anInt72) / 65536.0F; - Class30.aFloat578 = var14 * TextureOperation9.aFloat3105 + var15 * Class3_Sub17.aFloat2457; - TextureOperation36.aFloat3424 = TextureOperation36.aFloat3435 * var14 + var15 * aFloat3044; - Class12.aFloat319 = var15 * RSInterface.aFloat246 + var14 * aFloat1475; - Unsorted.anInt1950 = (16711680 & (Class60.anInt932 & 65280) * var13 + var12 * (anInt4037 & 65280)) + ((16711935 & anInt4037) * var12 - -((Class60.anInt932 & 16711935) * var13) & -16711936) >> 8; - Unsorted.anInt689 = var13 * anInt1736 + var12 * anInt1407 >> 8; - } - - Class92.setLightParams(Unsorted.anInt1950, Class30.aFloat578, TextureOperation36.aFloat3424, Class12.aFloat319); - Class92.setFogValues(anInt3652, Unsorted.anInt689); - - Class92.setLightPosition((float) Class46.anInt741, (float) TextureOperation1.anInt3274, (float) AtmosphereParser.anInt1191); - Class92.method1504(); - return anInt3652; - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "i.F(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + 1 + ')'); - } - } - - static int method810(int var1) { - return 255 & var1; - } - - final void method811(byte var1, int var2, byte[] var3, int var4) { - try { - if (var1 < 16) { - addChatMessage(null, 126, null, -28); - } - - for (int var5 = 0; var5 < var4; ++var5) { - var3[var2 + var5] = (byte) (this.buffer[this.index++] + -this.isaacCipher.nextOpcode()); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "i.S(" + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ')'); - } - } - - final int getBits(int var2) { - try { - int var3 = this.headiconsPrayerSpriteArchive6 >> 3; - int var4 = 8 + -(7 & this.headiconsPrayerSpriteArchive6); - int var5 = 0; - this.headiconsPrayerSpriteArchive6 += var2; - while (var2 > var4) { - var5 += (anIntArray2709[var4] & this.buffer[var3++]) << -var4 + var2; - var2 -= var4; - var4 = 8; - } - - if (var2 == var4) { - var5 += this.buffer[var3] & anIntArray2709[var4]; - } else { - var5 += this.buffer[var3] >> var4 - var2 & anIntArray2709[var2]; - } - - return var5; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "i.C(" + (byte) -11 + ',' + var2 + ')'); - } - } - - final void method814(int[] var1) { - try { - this.isaacCipher = new ISAACCipher(var1); - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "i.T(" + (var1 != null ? "{...}" : "null") + ',' + false + ')'); - } - } - - final int method815(int var1) { - try { - - return var1 * 8 - this.headiconsPrayerSpriteArchive6; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "i.U(" + var1 + ',' + 32666 + ')'); - } - } - - public final void putOpcode(int opcode) { - if (buffer == null || isaacCipher == null) { - SystemLogger.logInfo("Buffer/cipher null. ISAAC likely disabled or not implemented."); - return; - } - this.buffer[this.index++] = (byte) (opcode + this.isaacCipher.nextOpcode()); - } - - public final int getOpcode() { - try { - return 255 & this.buffer[this.index++] - this.isaacCipher.nextOpcode(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "i.P(" + 0 + ')'); - } - } - - final void method818() { - try { - this.index = (this.headiconsPrayerSpriteArchive6 + 7) / 8; - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "i.Q(" + false + ')'); - } - } - - static void method819() { - try { - Class3_Sub31 var1 = TextureOperation23.aHashTable_3208.first(); - for (; var1 != null; var1 = TextureOperation23.aHashTable_3208.next()) { - int var2 = var1.anInt2602; - if (Unsorted.loadInterface(var2)) { - boolean var3 = true; - RSInterface[] var4 = GameObject.aClass11ArrayArray1834[var2]; - - int var5; - for (var5 = 0; var5 < var4.length; ++var5) { - if (var4[var5] != null) { - var3 = var4[var5].usingScripts; - break; - } - } - - if (!var3) { - var5 = (int) var1.linkableKey; - RSInterface var6 = Unsorted.getRSInterface(var5); - if (null != var6) { - Class20.method909(var6); - } - } - } - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "i.A(" + false + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Bzip2Context.java b/Client/src/main/java/org/runite/client/Bzip2Context.java deleted file mode 100644 index 8d835a05f..000000000 --- a/Client/src/main/java/org/runite/client/Bzip2Context.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.runite.client; - -final class Bzip2Context { - int[][] anIntArrayArray108 = new int[6][258]; - byte[] aByteArray109 = new byte[4096]; - int[][] anIntArrayArray110 = new int[6][258]; - byte aByte111; - boolean[] aBooleanArray112 = new boolean[256]; - byte[] aByteArray114 = new byte[256]; - int[][] anIntArrayArray115 = new int[6][258]; - int anInt116 = 0; - byte[] aByteArray117; - int anInt118 = 0; - int anInt121; - int[] anIntArray122 = new int[257]; - int[] anIntArray123 = new int[16]; - boolean[] aBooleanArray124 = new boolean[16]; - int anInt126; - byte[] aByteArray127; - int anInt128; - int anInt129; - byte[] aByteArray130 = new byte[18002]; - int anInt131; - int anInt133; - int[] anIntArray134 = new int[256]; - int anInt135; - int anInt137; - int[] anIntArray138 = new int[6]; - byte[] aByteArray139 = new byte[18002]; - int anInt140; - int anInt141; - int anInt142; - int anInt143; - byte[][] aByteArrayArray146 = new byte[6][258]; - int anInt147; -} diff --git a/Client/src/main/java/org/runite/client/Bzip2Decompressor.java b/Client/src/main/java/org/runite/client/Bzip2Decompressor.java deleted file mode 100644 index fdb618077..000000000 --- a/Client/src/main/java/org/runite/client/Bzip2Decompressor.java +++ /dev/null @@ -1,554 +0,0 @@ -package org.runite.client; - -public final class Bzip2Decompressor { - - private static final Bzip2Context context = new Bzip2Context(); - - - private static int method1633(int var0, Bzip2Context var1) { - while(var1.anInt128 < var0) { - var1.anInt140 = var1.anInt140 << 8 | var1.aByteArray127[var1.anInt116] & 0xFF; - var1.anInt128 += 8; - ++var1.anInt116; - ++var1.anInt147; - } - - int var3 = var1.anInt140 >> var1.anInt128 - var0 & (1 << var0) - 1; - var1.anInt128 -= var0; - return var3; - } - - private static void method1634(Bzip2Context var0) { - byte var2 = var0.aByte111; - int var3 = var0.anInt143; - int var4 = var0.anInt131; - int var5 = var0.anInt129; - int[] var6 = Class129.anIntArray1690; - int var7 = var0.anInt133; - byte[] var8 = var0.aByteArray117; - int var9 = var0.anInt118; - int var10 = var0.anInt126; - int var12 = var0.anInt121 + 1; - - label67: - while(true) { - if(var3 > 0) { - while(true) { - if(var10 == 0) { - break label67; - } - - if(var3 == 1) { - - var8[var9] = var2; - ++var9; - --var10; - break; - } - - var8[var9] = var2; - --var3; - ++var9; - --var10; - } - } - - boolean var14 = true; - - byte var1; - while(var14) { - var14 = false; - if(var4 == var12) { - var3 = 0; - break label67; - } - - var2 = (byte)var5; - var7 = var6[var7]; - var1 = (byte)(var7 & 0xFF); - var7 >>= 8; - ++var4; - if(var1 != var5) { - var5 = var1; - if(var10 == 0) { - var3 = 1; - break label67; - } - - var8[var9] = var2; - ++var9; - --var10; - var14 = true; - } else if(var4 == var12) { - if(var10 == 0) { - var3 = 1; - break label67; - } - - var8[var9] = var2; - ++var9; - --var10; - var14 = true; - } - } - - var3 = 2; - var7 = var6[var7]; - var1 = (byte)(var7 & 0xFF); - var7 >>= 8; - ++var4; - if(var4 != var12) { - if(var1 == var5) { - var3 = 3; - var7 = var6[var7]; - var1 = (byte)(var7 & 0xFF); - var7 >>= 8; - ++var4; - if(var4 != var12) { - if(var1 == var5) { - var7 = var6[var7]; - var1 = (byte)(var7 & 0xFF); - var7 >>= 8; - ++var4; - var3 = (var1 & 0xFF) + 4; - var7 = var6[var7]; - var5 = (byte)(var7 & 0xFF); - var7 >>= 8; - ++var4; - } else { - var5 = var1; - } - } - } else { - var5 = var1; - } - } - } - - var0.aByte111 = var2; - var0.anInt143 = var3; - var0.anInt131 = var4; - var0.anInt129 = var5; - Class129.anIntArray1690 = var6; - var0.anInt133 = var7; - var0.aByteArray117 = var8; - var0.anInt118 = var9; - var0.anInt126 = var10; - } - - private static void method1635(int[] var0, int[] var1, int[] var2, byte[] var3, int var4, int var5, int var6) { - int var7 = 0; - - int var8; - for(var8 = var4; var8 <= var5; ++var8) { - for(int var9 = 0; var9 < var6; ++var9) { - if(var3[var9] == var8) { - var2[var7] = var9; - ++var7; - } - } - } - - for(var8 = 0; var8 < 23; ++var8) { - var1[var8] = 0; - } - - for(var8 = 0; var8 < var6; ++var8) { - ++var1[var3[var8] + 1]; - } - - for(var8 = 1; var8 < 23; ++var8) { - var1[var8] += var1[var8 - 1]; - } - - for(var8 = 0; var8 < 23; ++var8) { - var0[var8] = 0; - } - - int var10 = 0; - - for(var8 = var4; var8 <= var5; ++var8) { - var10 += var1[var8 + 1] - var1[var8]; - var0[var8] = var10 - 1; - var10 <<= 1; - } - - for(var8 = var4 + 1; var8 <= var5; ++var8) { - var1[var8] = (var0[var8 - 1] + 1 << 1) - var1[var8]; - } - - } - - private static void method1636(Bzip2Context var0) { - var0.anInt137 = 0; - - for(int var1 = 0; var1 < 256; ++var1) { - if(var0.aBooleanArray112[var1]) { - var0.aByteArray114[var0.anInt137] = (byte)var1; - ++var0.anInt137; - } - } - - } - - private static byte method1637(Bzip2Context var0) { - return (byte)method1633(8, var0); - } - - private static void method1638(Bzip2Context var0) { - int var22; - int[] var23; - int[] var24; - int[] var25; - var0.anInt142 = 1; - if(Class129.anIntArray1690 == null) { - Class129.anIntArray1690 = new int[var0.anInt142 * 100000]; - } - - boolean var26 = true; - - while(var26) { - byte var1 = method1637(var0); - if(var1 == 23) { - return; - } - - var1 = method1637(var0); - var1 = method1637(var0); - var1 = method1637(var0); - var1 = method1637(var0); - var1 = method1637(var0); - var1 = method1637(var0); - var1 = method1637(var0); - var1 = method1637(var0); - var1 = method1637(var0); - var1 = method1639(var0); - - var0.anInt135 = 0; - var1 = method1637(var0); - var0.anInt135 = var0.anInt135 << 8 | var1 & 0xFF; - var1 = method1637(var0); - var0.anInt135 = var0.anInt135 << 8 | var1 & 0xFF; - var1 = method1637(var0); - var0.anInt135 = var0.anInt135 << 8 | var1 & 0xFF; - - int var35; - for(var35 = 0; var35 < 16; ++var35) { - var1 = method1639(var0); - var0.aBooleanArray124[var35] = var1 == 1; - } - - for(var35 = 0; var35 < 256; ++var35) { - var0.aBooleanArray112[var35] = false; - } - - int var36; - for(var35 = 0; var35 < 16; ++var35) { - if(var0.aBooleanArray124[var35]) { - for(var36 = 0; var36 < 16; ++var36) { - var1 = method1639(var0); - if(var1 == 1) { - var0.aBooleanArray112[var35 * 16 + var36] = true; - } - } - } - } - - method1636(var0); - int var38 = var0.anInt137 + 2; - int var39 = method1633(3, var0); - int var40 = method1633(15, var0); - var35 = 0; - - while(var35 < var40) { - var36 = 0; - - while(true) { - var1 = method1639(var0); - if(var1 == 0) { - var0.aByteArray139[var35] = (byte)var36; - ++var35; - break; - } - - ++var36; - } - } - - byte[] var27 = new byte[6]; - - byte var29; - for(var29 = 0; var29 < var39; var27[var29] = var29++) { - } - - for(var35 = 0; var35 < var40; ++var35) { - var29 = var0.aByteArray139[var35]; - - byte var28; - for(var28 = var27[var29]; var29 > 0; --var29) { - var27[var29] = var27[var29 - 1]; - } - - var27[0] = var28; - var0.aByteArray130[var35] = var28; - } - - int var37; - for(var37 = 0; var37 < var39; ++var37) { - int var49 = method1633(5, var0); - var35 = 0; - - while(var35 < var38) { - while(true) { - var1 = method1639(var0); - if(var1 == 0) { - var0.aByteArrayArray146[var37][var35] = (byte)var49; - ++var35; - break; - } - - var1 = method1639(var0); - if(var1 == 0) { - ++var49; - } else { - --var49; - } - } - } - } - - for(var37 = 0; var37 < var39; ++var37) { - byte var2 = 32; - byte var3 = 0; - - for(var35 = 0; var35 < var38; ++var35) { - if(var0.aByteArrayArray146[var37][var35] > var3) { - var3 = var0.aByteArrayArray146[var37][var35]; - } - - if(var0.aByteArrayArray146[var37][var35] < var2) { - var2 = var0.aByteArrayArray146[var37][var35]; - } - } - - method1635(var0.anIntArrayArray115[var37], var0.anIntArrayArray108[var37], var0.anIntArrayArray110[var37], var0.aByteArrayArray146[var37], var2, var3, var38); - var0.anIntArray138[var37] = var2; - } - - int var42 = var0.anInt137 + 1; - int var41 = -1; - byte var43; - - for(var35 = 0; var35 <= 255; ++var35) { - var0.anIntArray134[var35] = 0; - } - - int var56 = 4095; - - int var55; - int var54; - for(var55 = 15; var55 >= 0; --var55) { - for(var54 = 15; var54 >= 0; --var54) { - var0.aByteArray109[var56] = (byte)(var55 * 16 + var54); - --var56; - } - - var0.anIntArray123[var55] = var56 + 1; - } - - int var47 = 0; - byte var53; - ++var41; - var43 = 50; - var53 = var0.aByteArray130[var41]; - var22 = var0.anIntArray138[var53]; - var23 = var0.anIntArrayArray115[var53]; - var25 = var0.anIntArrayArray110[var53]; - var24 = var0.anIntArrayArray108[var53]; - - int var45 = var43 - 1; - int var51 = var22; - - int var50; - byte var52; - for(var50 = method1633(var22, var0); var50 > var23[var51]; var50 = var50 << 1 | var52) { - ++var51; - var52 = method1639(var0); - } - - int var44 = var25[var50 - var24[var51]]; - - while(var44 != var42) { - if(var44 == 0 || var44 == 1) { - int var46 = -1; - int var48 = 1; - - do { - if(var44 == 0) { - var46 += var48; - } else { - var46 += 2 * var48; - } - - var48 *= 2; - if(var45 == 0) { - ++var41; - var45 = 50; - var53 = var0.aByteArray130[var41]; - var22 = var0.anIntArray138[var53]; - var23 = var0.anIntArrayArray115[var53]; - var25 = var0.anIntArrayArray110[var53]; - var24 = var0.anIntArrayArray108[var53]; - } - - --var45; - var51 = var22; - - for(var50 = method1633(var22, var0); var50 > var23[var51]; var50 = var50 << 1 | var52) { - ++var51; - var52 = method1639(var0); - } - - var44 = var25[var50 - var24[var51]]; - } while(var44 == 0 || var44 == 1); - - ++var46; - var1 = var0.aByteArray114[var0.aByteArray109[var0.anIntArray123[0]] & 0xFF]; - - for(var0.anIntArray134[var1 & 0xFF] += var46; var46 > 0; --var46) { - Class129.anIntArray1690[var47] = var1 & 0xFF; - ++var47; - } - } else { - int var33 = var44 - 1; - int var30; - if(var33 < 16) { - var30 = var0.anIntArray123[0]; - - for(var1 = var0.aByteArray109[var30 + var33]; var33 > 3; var33 -= 4) { - int var34 = var30 + var33; - var0.aByteArray109[var34] = var0.aByteArray109[var34 - 1]; - var0.aByteArray109[var34 - 1] = var0.aByteArray109[var34 - 2]; - var0.aByteArray109[var34 - 2] = var0.aByteArray109[var34 - 3]; - var0.aByteArray109[var34 - 3] = var0.aByteArray109[var34 - 4]; - } - - while(var33 > 0) { - var0.aByteArray109[var30 + var33] = var0.aByteArray109[var30 + var33 - 1]; - --var33; - } - - var0.aByteArray109[var30] = var1; - } else { - int var31 = var33 / 16; - int var32 = var33 % 16; - var30 = var0.anIntArray123[var31] + var32; - - for(var1 = var0.aByteArray109[var30]; var30 > var0.anIntArray123[var31]; --var30) { - var0.aByteArray109[var30] = var0.aByteArray109[var30 - 1]; - } - - ++var0.anIntArray123[var31]; - - while(var31 > 0) { - --var0.anIntArray123[var31]; - var0.aByteArray109[var0.anIntArray123[var31]] = var0.aByteArray109[var0.anIntArray123[var31 - 1] + 16 - 1]; - --var31; - } - - --var0.anIntArray123[0]; - var0.aByteArray109[var0.anIntArray123[0]] = var1; - if(var0.anIntArray123[0] == 0) { - var56 = 4095; - - for(var55 = 15; var55 >= 0; --var55) { - for(var54 = 15; var54 >= 0; --var54) { - var0.aByteArray109[var56] = var0.aByteArray109[var0.anIntArray123[var55] + var54]; - --var56; - } - - var0.anIntArray123[var55] = var56 + 1; - } - } - } - - ++var0.anIntArray134[var0.aByteArray114[var1 & 0xFF] & 0xFF]; - Class129.anIntArray1690[var47] = var0.aByteArray114[var1 & 0xFF] & 0xFF; - ++var47; - if(var45 == 0) { - ++var41; - var45 = 50; - var53 = var0.aByteArray130[var41]; - var22 = var0.anIntArray138[var53]; - var23 = var0.anIntArrayArray115[var53]; - var25 = var0.anIntArrayArray110[var53]; - var24 = var0.anIntArrayArray108[var53]; - } - - --var45; - var51 = var22; - - for(var50 = method1633(var22, var0); var50 > var23[var51]; var50 = var50 << 1 | var52) { - ++var51; - var52 = method1639(var0); - } - - var44 = var25[var50 - var24[var51]]; - } - } - - var0.anInt143 = 0; - var0.aByte111 = 0; - var0.anIntArray122[0] = 0; - - for(var35 = 1; var35 <= 256; ++var35) { - var0.anIntArray122[var35] = var0.anIntArray134[var35 - 1]; - } - - for(var35 = 1; var35 <= 256; ++var35) { - var0.anIntArray122[var35] += var0.anIntArray122[var35 - 1]; - } - - for(var35 = 0; var35 < var47; ++var35) { - var1 = (byte)(Class129.anIntArray1690[var35] & 0xFF); - Class129.anIntArray1690[var0.anIntArray122[var1 & 0xFF]] |= var35 << 8; - ++var0.anIntArray122[var1 & 0xFF]; - } - - var0.anInt133 = Class129.anIntArray1690[var0.anInt135] >> 8; - var0.anInt131 = 0; - var0.anInt133 = Class129.anIntArray1690[var0.anInt133]; - var0.anInt129 = (byte)(var0.anInt133 & 0xFF); - var0.anInt133 >>= 8; - ++var0.anInt131; - var0.anInt121 = var47; - method1634(var0); - var26 = var0.anInt131 == var0.anInt121 + 1 && var0.anInt143 == 0; - } - - } - - private static byte method1639(Bzip2Context var0) { - return (byte)method1633(1, var0); - } - - public static void decompress(byte[] var0, int var1, byte[] var2) { - Bzip2Context context = Bzip2Decompressor.context; - synchronized(context) { - Bzip2Decompressor.context.aByteArray127 = var2; - Bzip2Decompressor.context.anInt116 = 9; - Bzip2Decompressor.context.aByteArray117 = var0; - Bzip2Decompressor.context.anInt118 = 0; - Bzip2Decompressor.context.anInt126 = var1; - Bzip2Decompressor.context.anInt128 = 0; - Bzip2Decompressor.context.anInt140 = 0; - Bzip2Decompressor.context.anInt147 = 0; - Bzip2Decompressor.context.anInt141 = 0; - method1638(Bzip2Decompressor.context); - var1 -= Bzip2Decompressor.context.anInt126; - Bzip2Decompressor.context.aByteArray127 = null; - Bzip2Decompressor.context.aByteArray117 = null; - } - } - -} diff --git a/Client/src/main/java/org/runite/client/CS2AsmOpcodes.java b/Client/src/main/java/org/runite/client/CS2AsmOpcodes.java deleted file mode 100644 index d914780ae..000000000 --- a/Client/src/main/java/org/runite/client/CS2AsmOpcodes.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.runite.client; - -public enum CS2AsmOpcodes { - // Copies the operand specified by the instruction to the int stack - PUSH_INT(0), - // Copies the int from ram onto the int stack specified by the operand - PUSH_INT_FROM_RAM(1), - // Copies an int from the stack onto ram, using the ram address specified by the operand - POP_INT_TO_RAM(2), - // Copies the operand specified by the instruction to the string stack - PUSH_STR(3), - // Jump to the relative location given by the operand. EG an op of 4 will jump ahead 4 instructions. - JUMP(6), - // Jumps to the relative location given by the operand IFF the last two values pushed on the int stack are NOT equal - // THIS ALSO POPS THE STACK TWICE - BRANCH_NOT_EQUAL(7), - // Jumps to the relative location given by the operand IFF the last two values on the int stack are equal - // THIS ALSO POPS THE STACK TWICE - BRANCH_EQUAL(8), - // Jumps to the relative location given by operand IFF the most recent value on the int stack > the second most recent one - // THIS ALSO POPS THE STACK TWICE - BRANCH_GREATER_THAN(9), - // See above but in reverse - BRANCH_LESS_THAN(10), - // Returns from the current method, completely analogous to returning from a method in any other programming language. - // It places you back on the previous method and resumes execution right after the call. - // IT DOES NOT TOUCH THE INT OR STRING STACK OR THE RAM. - RETURN(21), - // TODO: Opcode 25, opcode 27. I suspect these are tied together - - // See 9 and 10 - BRANCH_GREATER_OR_EQUAL(31), - BRANCH_LESS_OR_EQUAL(32), - - // Copies the int from the int args array onto the stack specified by the operand. - // eg if the operand is 2 it copies the second int arg onto the stack - PUSH_INT_FROM_ARGS(33), - // Copies an int from the stack onto the int args array. It copies it onto the argument array index specified - // by the operand. - POP_INT_TO_ARGS(34), - // See above but for the string array and string args. - PUSH_STRING_FROM_ARGS(35), - POP_STRING_TO_ARGS(36), - // TODO: Opcode 37 - - // Pops int/string from the stack without using it. Effectively throwing it away. - POP_INT(38), - POP_STRING(39), - - // Calls the method specified by the operand. The method's arguments are LOADED from the int and string stack - // If it has 4 int arguments, for instance, it will pop the int stack four times and assign these values in - // REVERSE order. That is, the first popped value becomes the LAST argument. Number of arguments are given in - // AssembledMethod.NumberOf[X]Arguments - // This effectively "resets" the program counter. It DOES NOT RESET THE INT OR STRING STACK OR THE RAM. - // Also It adds the previous method to the method stack. SEE RETURN(21) - CALL(40), - - // TODO: Opcodes 42 and 43. 42 saves to a special different kind of ram that only 43 can load from. 43 - // does some weird shit with this number - - // allocates one of the five paged ram pages as selected by the top 16 bits of the operand. - // If the bottom 16 bits equal 105, this will be allocated as all zeroes. - // Otherwise it will be allocated with all -1s. Why?? Jagex is fucking stupid idk. - // THE NUMBER OF INTS ALLOCATED IS BASED ON THE VALUE ON INT STACK. - // POPS THE INT STACK - ALLOCATE_PAGED_RAM(44), - - // Replaces the value at the end of the stack with the value in the ram page specified by the operand - // and the page entry specified by the last value of the int stack - REPLACE_STACK_PAGED_RAM(45), - - // Copies an int from the int stack to paged ram. The page is given by the operand and the - // page entry is given by the NEXT value on the int stack. - // Thus this pops the int stack TWICE. - POP_TO_PAGED_RAM(46); - - - private final int op; - - public int getOp() { - return this.op; - } - - CS2AsmOpcodes(int op) { - this.op = op; - } -} diff --git a/Client/src/main/java/org/runite/client/CS2Methods.java b/Client/src/main/java/org/runite/client/CS2Methods.java deleted file mode 100644 index 3f7ea8467..000000000 --- a/Client/src/main/java/org/runite/client/CS2Methods.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.runite.client; - -public class CS2Methods { - static QuickChat aQuickChat_1056; - - static RSString method27(RSString var0) { - try { - - int var2 = Unsorted.method1602(var0); - return var2 != -1 ? Class119.aClass131_1624.aClass94Array1721[var2].method1560(RSString.parse(" "), TextCore.aClass94_4066) : TextCore.aClass94_4049; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "rc.V(" + (var0 != null ? "{...}" : "null") + ',' + true + ')'); - } - } - - static void method28() { - try { - Class143.aReferenceCache_1874.clear(); - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rc.Q(" + true + ')'); - } - } - - static void method852(int var1) { - try { - Class3_Sub25 var2 = (Class3_Sub25) Class3_Sub2.aHashTable_2220.get(var1); - if (var2 != null) { - - for (int var3 = 0; var2.anIntArray2547.length > var3; ++var3) { - var2.anIntArray2547[var3] = -1; - var2.anIntArray2551[var3] = 0; - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bd.C(" + (byte) 114 + ',' + var1 + ')'); - } - } - - static void method532(int var0) { - try { - Class3_Sub25 var2 = (Class3_Sub25) Class3_Sub2.aHashTable_2220.get(var0); - if (null != var2) { - var2.unlink(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bc.A(" + var0 + ',' + -28236 + ')'); - } - } - - static void method2280(int var1) { - try { - - InterfaceWidget var2 = InterfaceWidget.getWidget(11, var1); - var2.a(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wl.B(" + 2714 + ',' + var1 + ')'); - } - } - - /* * * * INTERFACE SPECIFIC SEPARATE CORRECTLY * * * */ - static void method225(RSInterface iface) { - RSInterface var2 = method2273(iface); - - int windowWidth; - int windowHeight; - if (var2 == null) { - windowHeight = Class140_Sub7.canvasHeight; - windowWidth = Class23.canvasWidth; - } else { - windowHeight = var2.height; - windowWidth = var2.width; - } - - Unsorted.calculateInterfaceSize(iface, windowWidth, windowHeight, false); - Unsorted.calculateInterfacePosition(iface, windowWidth, windowHeight); - } - - //INTERFACE SPECIFIC RENAME - static RSInterface method2273(RSInterface iface) { - if (iface.parentId != -1) { - return Unsorted.getRSInterface(iface.parentId); - } - - int var3 = iface.componentHash >>> 16; - Class80 var4 = new Class80<>(TextureOperation23.aHashTable_3208); - - for (Class3_Sub31 var2 = var4.method1393(); null != var2; var2 = var4.method1392()) { - if (var2.anInt2602 == var3) { - return Unsorted.getRSInterface((int) var2.linkableKey); - } - } - - return null; - } - /* * * * END INTERFACE * * * */ - - -} diff --git a/Client/src/main/java/org/runite/client/CS2Script.java b/Client/src/main/java/org/runite/client/CS2Script.java deleted file mode 100644 index db95bc72b..000000000 --- a/Client/src/main/java/org/runite/client/CS2Script.java +++ /dev/null @@ -1,3994 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.LinkableInt; -import org.rs09.client.data.HashTable; -import org.rs09.client.data.Queue; -import org.rs09.client.data.ReferenceCache; -import org.rs09.client.filestore.resources.configs.enums.EnumDefinition; -import org.rs09.client.filestore.resources.configs.enums.EnumDefinitionProvider; -import org.rs09.client.filestore.resources.configs.structs.StructDefinitionProvider; - -import java.nio.charset.StandardCharsets; -import java.util.Calendar; -import java.util.Date; - -public final class CS2Script extends Linkable { - - public static int userCurrentWorldID = -1; - static short aShort3052 = 205; - static int anInt3101 = 0; - static int[] anIntArray3228 = new int[]{7, 8, 9, 10, 11, 12, 13, 15}; - static short aShort3241 = 1; - static int anInt1357 = 0; - static short aShort1444 = 256; - static RSInterface aClass11_1749; - static boolean aBoolean2705 = true; - static int anInt3775 = 0; - static int anInt2440 = 0; - static ReferenceCache aReferenceCache_2442 = new ReferenceCache(50); - static byte[][][] aByteArrayArrayArray2452; - RSInterface aClass11_2438; - RSString aClass94_2439; - int scrollbarScrollAmount; - int anInt2443; - int inputTextCode; - int interfaceButtons; - boolean aBoolean2446; - int worldSelectCursorPositionX; - Object[] arguments; - RSInterface aClass11_2449; - - static void sendRegistryRequest(int year, int country, int day, int month) { - try { - // System.out.println("CS2Script year=" + year + ", country=" + country + ", day=" + day + ", month=" + month + ", stage=" + stage + ", " + System.currentTimeMillis()); - TextureOperation12.outgoingBuffer.index = 0; - TextureOperation12.outgoingBuffer.writeByte(147);//Handshake opcode - TextureOperation12.outgoingBuffer.writeByte(day); - TextureOperation12.outgoingBuffer.writeByte(month); - TextureOperation12.outgoingBuffer.writeShort(year); - TextureOperation12.outgoingBuffer.writeShort(country); - Class132.anInt1734 = 0; - GraphicDefinition.anInt548 = 0; - Unsorted.registryStage = 1; - Unsorted.anInt1711 = -3; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "jl.C(" + year + ',' + country + ',' + day + ',' + month + ',' + 1 + ')'); - } - } - - static void method379() { - try { - int var2 = Class146.anInt1904 * 128 - -64; - int var1 = 128 * Unsorted.anInt30 + 64; - int var3 = Class121.method1736(WorldListCountry.localPlane, 1, var1, var2) - TextureOperation25.anInt3414; - if (100 <= Unsorted.anInt3631) { - NPC.anInt3995 = 64 + Unsorted.anInt30 * 128; - Class77.anInt1111 = 64 + Class146.anInt1904 * 128; - Class7.anInt2162 = Class121.method1736(WorldListCountry.localPlane, 1024 + -1023, NPC.anInt3995, Class77.anInt1111) + -TextureOperation25.anInt3414; - } else { - if (NPC.anInt3995 < var1) { - NPC.anInt3995 += Class163_Sub2_Sub1.anInt4021 + Unsorted.anInt3631 * (-NPC.anInt3995 + var1) / 1000; - if (var1 < NPC.anInt3995) { - NPC.anInt3995 = var1; - } - } - - if (var3 > Class7.anInt2162) { - Class7.anInt2162 += (-Class7.anInt2162 + var3) * Unsorted.anInt3631 / 1000 + Class163_Sub2_Sub1.anInt4021; - if (Class7.anInt2162 > var3) { - Class7.anInt2162 = var3; - } - } - - if (var1 < NPC.anInt3995) { - NPC.anInt3995 -= Class163_Sub2_Sub1.anInt4021 + (NPC.anInt3995 + -var1) * Unsorted.anInt3631 / 1000; - if (NPC.anInt3995 < var1) { - NPC.anInt3995 = var1; - } - } - - if (Class77.anInt1111 < var2) { - Class77.anInt1111 += Class163_Sub2_Sub1.anInt4021 + Unsorted.anInt3631 * (var2 - Class77.anInt1111) / 1000; - if (Class77.anInt1111 > var2) { - Class77.anInt1111 = var2; - } - } - - if (var3 < Class7.anInt2162) { - Class7.anInt2162 -= (Class7.anInt2162 - var3) * Unsorted.anInt3631 / 1000 + Class163_Sub2_Sub1.anInt4021; - if (Class7.anInt2162 < var3) { - Class7.anInt2162 = var3; - } - } - - if (var2 < Class77.anInt1111) { - Class77.anInt1111 -= Class163_Sub2_Sub1.anInt4021 - -((-var2 + Class77.anInt1111) * Unsorted.anInt3631 / 1000); - if (Class77.anInt1111 < var2) { - Class77.anInt1111 = var2; - } - } - } - - var2 = Class157.anInt1996 * 128 - -64; - var1 = MouseListeningClass.anInt1923 * 128 + 64; - var3 = Class121.method1736(WorldListCountry.localPlane, 1, var1, var2) + -GraphicDefinition.anInt529; - int var5 = var3 + -Class7.anInt2162; - int var6 = -Class77.anInt1111 + var2; - int var4 = -NPC.anInt3995 + var1; - int var7 = (int) Math.sqrt(var4 * var4 + var6 * var6); - int var8 = (int) (325.949D * Math.atan2(var5, var7)) & 0x7FF; - if (128 > var8) { - var8 = 128; - } - - if (var8 > 383) { - var8 = 383; - } - - int var9 = (int) (-325.949D * Math.atan2(var4, var6)) & 0x7FF; - if (var8 > Class139.anInt1823) { - Class139.anInt1823 += Class75.anInt1105 + Class163_Sub2_Sub1.anInt4014 * (-Class139.anInt1823 + var8) / 1000; - if (Class139.anInt1823 > var8) { - Class139.anInt1823 = var8; - } - } - - if (Class139.anInt1823 > var8) { - Class139.anInt1823 -= (Class139.anInt1823 - var8) * Class163_Sub2_Sub1.anInt4014 / 1000 + Class75.anInt1105; - if (var8 > Class139.anInt1823) { - Class139.anInt1823 = var8; - } - } - - int var10 = -TextureOperation28.anInt3315 + var9; - if (var10 > 1024) { - var10 -= 2048; - } - - if (-1024 > var10) { - var10 += 2048; - } - - if (var10 > 0) { - TextureOperation28.anInt3315 += var10 * Class163_Sub2_Sub1.anInt4014 / 1000 + Class75.anInt1105; - TextureOperation28.anInt3315 &= 2047; - } - - if (var10 < 0) { - TextureOperation28.anInt3315 -= Class163_Sub2_Sub1.anInt4014 * -var10 / 1000 + Class75.anInt1105; - TextureOperation28.anInt3315 &= 2047; - } - - int var11 = -TextureOperation28.anInt3315 + var9; - if (1024 < var11) { - var11 -= 2048; - } - - if (var11 < -1024) { - var11 += 2048; - } - - if (var11 < 0 && var10 > 0 || var11 > 0 && var10 < 0) { - TextureOperation28.anInt3315 = var9; - } - - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "jl.B(" + 1024 + ')'); - } - } - - /* - * This is probably the MOST important function in the entire - * client. It basically runs a script, which is: - * a bunch of args, and a special integer pointing to the script entry method (script.arguments[0]) - * This integer refers to one of the game's methods which it loads from various files. (TODO Figure out how this is handled) - * - * All scripts are compiled into a pseudo assembly language. This language consists of - * a bunch of opcodes, int and string operands for each opcode, an int and string stack - * - * There is also ram (for saving variables) which persists as you go in and out of methods. - * - * Opcodes refer to instructions which are executed sequentially, except for when a branch opcode is encountered. - * opcode bound ram can be assigned to anything but can only be read from the current opcode. - * - * Opcode bound ram will often refer to a location in non-opcode bound ram as a way to access arbitrary values. - * - * - * This is my best understanding of this function. MOST of the above text is PROBABLY a lie. - * Poke around with it yourself and see what you discover - Your friendly neighborhood moth - * - */ - static void runAssembledScript(int maxIterations, CS2Script script) { - try { - Object[] aobj = script.arguments; - int j = ((Integer) aobj[0]).intValue(); - AssembledMethod currentMethod = ItemDefinition.getMethodByID(j); - // System.out.printf("Method id: %d\n", j); - if (null == currentMethod) - return; - ItemDefinition.scriptHeapCounter = 0; - int sStackCounter = 0; - int iStackCounter = 0; - int programCounter = -1; - int[] instructionOperands = currentMethod.instructionOperands; - int[] instructions = currentMethod.assemblyInstructions; - /* - * Scan method arguments. args can either be - * custom strings, custom ints, or integer opcodes (0x80000001-0x80000009) which represent - * various ints found elsewhere in the code for things like keyboard input. - * - * OPCODES 35 and 40 act on the string args - * OPCODES 33 and 40 act on the int args - * - * These arguments are tied to the specific method you are calling, just like arguments - * in a real programming language. In this case imagine this as arguments that you pass - * to the main() function of java. - */ - - ItemDefinition.intArguments = new int[currentMethod.numberOfIntsToCopy]; - ItemDefinition.stringArguments = new RSString[currentMethod.numberOfRSStringsToCopy]; - int stringArgIter = 0; - int intArgIter = 0; - for (int i2 = 1; aobj.length > i2; i2++) { - if (aobj[i2] instanceof Integer) { - int k2 = ((Integer) aobj[i2]).intValue(); - boolean printK2 = false; - if (k2 == 0x80000001) - k2 = script.worldSelectCursorPositionX; // Why does this matter? - if (k2 == 0x80000002) - k2 = script.scrollbarScrollAmount; - if (k2 == 0x80000003) - k2 = null == script.aClass11_2449 ? -1 : script.aClass11_2449.componentHash; - if (k2 == 0x80000004) - k2 = script.interfaceButtons; - if (k2 == 0x80000005) - k2 = null == script.aClass11_2449 ? -1 : script.aClass11_2449.anInt191; - if (k2 == 0x80000006) - k2 = null == script.aClass11_2438 ? -1 : script.aClass11_2438.componentHash; - if (k2 == 0x80000007) - k2 = script.aClass11_2438 != null ? script.aClass11_2438.anInt191 : -1; - if (k2 == 0x80000008) - k2 = script.inputTextCode; - if (k2 == 0x80000009) - k2 = script.anInt2443; - ItemDefinition.intArguments[intArgIter++] = k2; - continue; - } - if (!(aobj[i2] instanceof RSString)) - continue; - RSString class94 = (RSString) aobj[i2]; - if (class94.equalsString(TextCore.aClass94_209)) - class94 = script.aClass94_2439; - ItemDefinition.stringArguments[stringArgIter++] = class94; - // System.out.println("Item Definition line 168 " + class94.toString()); - } - - int j2 = 0; - label0: - do { - j2++; - if (maxIterations < j2) - throw new RuntimeException("Script exceeded max iterations"); - int opcode = instructions[++programCounter]; - // System.out.println("Instruction: " + programCounter + ". opcode is: " + opcode); - if (opcode < 100) { - if (opcode == CS2AsmOpcodes.PUSH_INT.getOp()) { - ItemDefinition.intsStack[iStackCounter++] = instructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.PUSH_INT_FROM_RAM.getOp()) { - int l2 = instructionOperands[programCounter]; - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.ram[l2]; - continue; - } - if (opcode == CS2AsmOpcodes.POP_INT_TO_RAM.getOp()) { - int i3 = instructionOperands[programCounter]; - AtmosphereParser.method1428(i3, ItemDefinition.intsStack[--iStackCounter]); - continue; - } - if (opcode == CS2AsmOpcodes.PUSH_STR.getOp()) { - ItemDefinition.stringsStack[sStackCounter++] = currentMethod.stringInstructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.JUMP.getOp()) { - programCounter += instructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.BRANCH_NOT_EQUAL.getOp()) { - iStackCounter -= 2; - if (ItemDefinition.intsStack[iStackCounter] != ItemDefinition.intsStack[1 + iStackCounter]) - programCounter += instructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.BRANCH_EQUAL.getOp()) { - iStackCounter -= 2; - if (ItemDefinition.intsStack[iStackCounter] == ItemDefinition.intsStack[iStackCounter + 1]) - programCounter += instructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.BRANCH_GREATER_THAN.getOp()) { - iStackCounter -= 2; - if (ItemDefinition.intsStack[iStackCounter + 1] > ItemDefinition.intsStack[iStackCounter]) - programCounter += instructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.BRANCH_LESS_THAN.getOp()) { - iStackCounter -= 2; - if (ItemDefinition.intsStack[iStackCounter + 1] < ItemDefinition.intsStack[iStackCounter]) - programCounter += instructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.RETURN.getOp()) { - if (ItemDefinition.scriptHeapCounter == 0) - return; - AssembledMethodContainer assembledMethodContainer = ItemDefinition.methodStack[--ItemDefinition.scriptHeapCounter]; - currentMethod = assembledMethodContainer.assembledMethod; - instructions = currentMethod.assemblyInstructions; - programCounter = assembledMethodContainer.currentProgramCounter; - ItemDefinition.intArguments = assembledMethodContainer.intArguments; - ItemDefinition.stringArguments = assembledMethodContainer.stringArguments; - instructionOperands = currentMethod.instructionOperands; - continue; - } - if (opcode == 25) { - int j3 = instructionOperands[programCounter]; - ItemDefinition.intsStack[iStackCounter++] = NPCDefinition.method1484(j3); - // System.out.printf("varp lookup: %s %s\n", j3, ItemDefinition.intsStack[iStackCounter-1]); - continue; - } - if (opcode == 27) { - int k3 = instructionOperands[programCounter]; - TextureOperation3.method306(k3, ItemDefinition.intsStack[--iStackCounter]); - continue; - } - if (opcode == CS2AsmOpcodes.BRANCH_GREATER_OR_EQUAL.getOp()) { - iStackCounter -= 2; - if (ItemDefinition.intsStack[1 + iStackCounter] >= ItemDefinition.intsStack[iStackCounter]) - programCounter += instructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.BRANCH_LESS_OR_EQUAL.getOp()) { - iStackCounter -= 2; - if (ItemDefinition.intsStack[1 + iStackCounter] <= ItemDefinition.intsStack[iStackCounter]) - programCounter += instructionOperands[programCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.PUSH_INT_FROM_ARGS.getOp()) { - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.intArguments[instructionOperands[programCounter]]; - continue; - } - if (opcode == CS2AsmOpcodes.POP_INT_TO_ARGS.getOp()) { - ItemDefinition.intArguments[instructionOperands[programCounter]] = ItemDefinition.intsStack[--iStackCounter]; - continue; - } - if (opcode == CS2AsmOpcodes.PUSH_STRING_FROM_ARGS.getOp()) { - ItemDefinition.stringsStack[sStackCounter++] = ItemDefinition.stringArguments[instructionOperands[programCounter]]; - continue; - } - if (opcode == CS2AsmOpcodes.POP_STRING_TO_ARGS.getOp()) { - ItemDefinition.stringArguments[instructionOperands[programCounter]] = ItemDefinition.stringsStack[--sStackCounter]; - continue; - } - if (opcode == 37) { - int l3 = instructionOperands[programCounter]; - sStackCounter -= l3; - RSString class94_2 = Class67.method1261(sStackCounter, l3, ItemDefinition.stringsStack); - ItemDefinition.stringsStack[sStackCounter++] = class94_2; - continue; - } - if (opcode == CS2AsmOpcodes.POP_INT.getOp()) { - iStackCounter--; - continue; - } - if (opcode == CS2AsmOpcodes.POP_STRING.getOp()) { - sStackCounter--; - continue; - } - if (opcode == CS2AsmOpcodes.CALL.getOp()) { - int op = instructionOperands[programCounter]; - AssembledMethod assembledMethod_1 = ItemDefinition.getMethodByID(op); - int[] ai2 = new int[assembledMethod_1.numberOfIntsToCopy]; - RSString[] aclass94 = new RSString[assembledMethod_1.numberOfRSStringsToCopy]; - if (assembledMethod_1.numberOfIntArguments >= 0) - System.arraycopy(ItemDefinition.intsStack, (iStackCounter - assembledMethod_1.numberOfIntArguments), ai2, 0, assembledMethod_1.numberOfIntArguments); - - for (int i76 = 0; assembledMethod_1.numberOfStringArguments > i76; i76++) - aclass94[i76] = ItemDefinition.stringsStack[i76 + -assembledMethod_1.numberOfStringArguments + sStackCounter]; - - iStackCounter -= assembledMethod_1.numberOfIntArguments; - sStackCounter -= assembledMethod_1.numberOfStringArguments; - AssembledMethodContainer assembledMethodContainer_1 = new AssembledMethodContainer(); - assembledMethodContainer_1.stringArguments = ItemDefinition.stringArguments; - assembledMethodContainer_1.intArguments = ItemDefinition.intArguments; - assembledMethodContainer_1.currentProgramCounter = programCounter; - assembledMethodContainer_1.assembledMethod = currentMethod; - if (ItemDefinition.methodStack.length <= ItemDefinition.scriptHeapCounter) - throw new RuntimeException(); - currentMethod = assembledMethod_1; - programCounter = -1; - ItemDefinition.methodStack[ItemDefinition.scriptHeapCounter++] = assembledMethodContainer_1; - ItemDefinition.intArguments = ai2; - instructionOperands = currentMethod.instructionOperands; - instructions = currentMethod.assemblyInstructions; - ItemDefinition.stringArguments = aclass94; - continue; - } - if (42 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = NPCDefinition.anIntArray1277[instructionOperands[programCounter]]; - continue; - } - if (opcode == 43) { - int j4 = instructionOperands[programCounter]; - NPCDefinition.anIntArray1277[j4] = ItemDefinition.intsStack[--iStackCounter]; - PacketParser.method825(j4); - continue; - } - if (opcode == CS2AsmOpcodes.ALLOCATE_PAGED_RAM.getOp()) { - int k4 = instructionOperands[programCounter] >> 16; // Get upper 16 bits of operand. - int bytesWritten = ItemDefinition.intsStack[--iStackCounter]; // pop stack - int k5 = 0xffff & instructionOperands[programCounter]; // Get lower 16 bits of operand to use as byte - if (bytesWritten < 0 || bytesWritten > 5000) - throw new RuntimeException(); - ItemDefinition.pagedRamPageSize[k4] = bytesWritten; - byte byte2 = -1; - if (k5 == 105) - byte2 = 0; - for (int i = 0; i < bytesWritten; i++) { - ItemDefinition.pagedRam[k4][i] = byte2; - } - continue; - } - if (opcode == CS2AsmOpcodes.REPLACE_STACK_PAGED_RAM.getOp()) { - int l4 = instructionOperands[programCounter]; - int l5 = ItemDefinition.intsStack[--iStackCounter]; - if (l5 < 0 || l5 >= ItemDefinition.pagedRamPageSize[l4]) - throw new RuntimeException(); - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.pagedRam[l4][l5]; - continue; - } - if (opcode == CS2AsmOpcodes.POP_TO_PAGED_RAM.getOp()) { - int i5 = instructionOperands[programCounter]; - iStackCounter -= 2; - int i6 = ItemDefinition.intsStack[iStackCounter]; - if (i6 < 0 || ItemDefinition.pagedRamPageSize[i5] <= i6) - throw new RuntimeException(); - ItemDefinition.pagedRam[i5][i6] = ItemDefinition.intsStack[1 + iStackCounter]; - continue; - } - if (47 == opcode) { - RSString class94_1 = Class132.aClass94Array1739[instructionOperands[programCounter]]; - if (null == class94_1) - class94_1 = TextCore.aClass94_2928; - ItemDefinition.stringsStack[sStackCounter++] = class94_1; - continue; - } - if (opcode == 48) { - int j5 = instructionOperands[programCounter]; - Class132.aClass94Array1739[j5] = ItemDefinition.stringsStack[--sStackCounter]; - Class49.method1126(j5); - continue; - } - if (opcode == 51) { - HashTable hashTable = currentMethod.switchHashTable[instructionOperands[programCounter]]; - LinkableInt linkableInt = (LinkableInt) hashTable.get(ItemDefinition.intsStack[--iStackCounter]); - if (null != linkableInt) - programCounter += linkableInt.value; - continue; - } - } - boolean flag; - flag = 1 == instructionOperands[programCounter]; - if (opcode < 300) { - if (opcode == 100) { - iStackCounter -= 3; - int j6 = ItemDefinition.intsStack[iStackCounter]; - int i44 = ItemDefinition.intsStack[1 + iStackCounter]; - int k66 = ItemDefinition.intsStack[2 + iStackCounter]; - if (i44 == 0) - throw new RuntimeException(); - RSInterface class11_21 = Unsorted.getRSInterface(j6); - if (null == class11_21.aClass11Array262) - class11_21.aClass11Array262 = new RSInterface[k66 + 1]; - if (k66 >= class11_21.aClass11Array262.length) { - RSInterface[] aclass11 = new RSInterface[k66 + 1]; - if (class11_21.aClass11Array262.length >= 0) - System.arraycopy(class11_21.aClass11Array262, 0, aclass11, 0, class11_21.aClass11Array262.length); - - class11_21.aClass11Array262 = aclass11; - } - if (0 < k66 && class11_21.aClass11Array262[-1 + k66] == null) - throw new RuntimeException("Gap at:" + (-1 + k66)); - RSInterface class11_23 = new RSInterface(); - class11_23.usingScripts = true; - class11_23.anInt191 = k66; - class11_23.parentId = class11_23.componentHash = class11_21.componentHash; - class11_23.type = i44; - class11_21.aClass11Array262[k66] = class11_23; - if (flag) - Class164.aClass11_2055 = class11_23; - else - aClass11_1749 = class11_23; - Class20.method909(class11_21); - continue; - } - if (opcode == 101) { - RSInterface class11 = flag ? Class164.aClass11_2055 : aClass11_1749; - if (class11.anInt191 == -1) - if (!flag) - throw new RuntimeException("Tried to cc_delete static active-component!"); - else - throw new RuntimeException("Tried to .cc_delete static .active-component!"); - RSInterface class11_17 = Unsorted.getRSInterface(class11.componentHash); - class11_17.aClass11Array262[class11.anInt191] = null; - Class20.method909(class11_17); - continue; - } - if (opcode == 102) { - RSInterface class11_1 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - class11_1.aClass11Array262 = null; - Class20.method909(class11_1); - continue; - } - if (opcode == 200) { - iStackCounter -= 2; - int k6 = ItemDefinition.intsStack[iStackCounter]; - int j44 = ItemDefinition.intsStack[iStackCounter - -1]; - RSInterface class11_19 = AbstractSprite.method638(k6, j44); - if (null == class11_19 || j44 == -1) { - ItemDefinition.intsStack[iStackCounter++] = 0; - } else { - ItemDefinition.intsStack[iStackCounter++] = 1; - if (!flag) - aClass11_1749 = class11_19; - else - Class164.aClass11_2055 = class11_19; - } - continue; - } - if (opcode != 201) - break; - int l6 = ItemDefinition.intsStack[--iStackCounter]; - RSInterface class11_18 = Unsorted.getRSInterface(l6); - if (null == class11_18) { - ItemDefinition.intsStack[iStackCounter++] = 0; - } else { - ItemDefinition.intsStack[iStackCounter++] = 1; - if (flag) - Class164.aClass11_2055 = class11_18; - else - aClass11_1749 = class11_18; - } - continue; - } - if (500 <= opcode) { - if (1000 <= opcode && opcode < 1100 || 2000 <= opcode && opcode < 2100) { - RSInterface class11_2; - if (opcode < 2000) { - class11_2 = flag ? Class164.aClass11_2055 : aClass11_1749; - } else { - class11_2 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - opcode -= 1000; - } - if (opcode == 1000) { - iStackCounter -= 4; - class11_2.defX = ItemDefinition.intsStack[iStackCounter]; - class11_2.defY = ItemDefinition.intsStack[iStackCounter + 1]; - int l66 = ItemDefinition.intsStack[3 + iStackCounter]; - if (l66 < 0) - l66 = 0; - else if (l66 > 5) - l66 = 5; - int k44 = ItemDefinition.intsStack[iStackCounter + 2]; - if (k44 >= 0) { - if (k44 > 5) - k44 = 5; - } else { - k44 = 0; - } - class11_2.verticalPos = (byte) l66; - class11_2.horizontalPos = (byte) k44; - Class20.method909(class11_2); - CS2Methods.method225(class11_2); - if (class11_2.anInt191 == -1) - CS2Methods.method2280(class11_2.componentHash); - continue; - } - if (opcode == 1001) { - iStackCounter -= 4; - class11_2.defWidth = ItemDefinition.intsStack[iStackCounter]; - class11_2.defHeight = ItemDefinition.intsStack[1 + iStackCounter]; - class11_2.anInt184 = 0; - class11_2.anInt312 = 0; - int l44 = ItemDefinition.intsStack[iStackCounter + 2]; - int i67 = ItemDefinition.intsStack[3 + iStackCounter]; - if (i67 >= 0) { - if (i67 > 4) - i67 = 4; - } else { - i67 = 0; - } - class11_2.verticalResize = (byte) i67; - if (l44 < 0) - l44 = 0; - else if (l44 > 4) - l44 = 4; - class11_2.horizontalResize = (byte) l44; - Class20.method909(class11_2); - CS2Methods.method225(class11_2); - if (class11_2.type == 0) - Unsorted.method2104(class11_2, false, 32); - continue; - } - if (opcode == 1003) { - boolean flag3 = ItemDefinition.intsStack[--iStackCounter] == 1; - if (flag3 == (!class11_2.hidden)) { - class11_2.hidden = flag3; - Class20.method909(class11_2); - } - if (-1 == class11_2.anInt191) - Unsorted.method569(class11_2.componentHash); - continue; - } - if (opcode == 1004) { - iStackCounter -= 2; - class11_2.anInt216 = ItemDefinition.intsStack[iStackCounter]; - class11_2.anInt160 = ItemDefinition.intsStack[iStackCounter - -1]; - Class20.method909(class11_2); - CS2Methods.method225(class11_2); - if (class11_2.type == 0) - Unsorted.method2104(class11_2, false, -127); - continue; - } - if (opcode != 1005) - break; - class11_2.aBoolean219 = ItemDefinition.intsStack[--iStackCounter] == 1; - continue; - } - if ((opcode < 1100 || 1200 <= opcode) && (opcode < 2100 || 2200 <= opcode)) { - if ((opcode < 1200 || 1300 <= opcode) && (2200 > opcode || opcode >= 2300)) { - if (opcode >= 1300 && opcode < 1400 || opcode >= 2300 && opcode < 2400) { - RSInterface class11_3; - if (2000 <= opcode) { - class11_3 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - opcode -= 1000; - } else { - class11_3 = flag ? Class164.aClass11_2055 : aClass11_1749; - } - if (opcode == 1300) { - int i45 = ItemDefinition.intsStack[--iStackCounter] + -1; - if (0 > i45 || i45 > 9) - sStackCounter--; - else - class11_3.method857(ItemDefinition.stringsStack[--sStackCounter], i45); - continue; - } - if (opcode == 1301) { - iStackCounter -= 2; - int j67 = ItemDefinition.intsStack[1 + iStackCounter]; - int j45 = ItemDefinition.intsStack[iStackCounter]; - class11_3.aClass11_302 = AbstractSprite.method638(j45, j67); - continue; - } - if (opcode == 1302) { - class11_3.aBoolean200 = ItemDefinition.intsStack[--iStackCounter] == 1; - continue; - } - if (opcode == 1303) { - class11_3.anInt214 = ItemDefinition.intsStack[--iStackCounter]; - continue; - } - if (opcode == 1304) { - class11_3.anInt179 = ItemDefinition.intsStack[--iStackCounter]; - continue; - } - if (1305 == opcode) { - class11_3.aClass94_277 = ItemDefinition.stringsStack[--sStackCounter]; - continue; - } - if (opcode == 1306) { - class11_3.aClass94_245 = ItemDefinition.stringsStack[--sStackCounter]; - continue; - } - if (opcode == 1307) { - class11_3.aClass94Array171 = null; - continue; - } - if (opcode == 1308) { - class11_3.anInt238 = ItemDefinition.intsStack[--iStackCounter]; - class11_3.anInt266 = ItemDefinition.intsStack[--iStackCounter]; - continue; - } - if (1309 != opcode) - break; - int k45 = ItemDefinition.intsStack[--iStackCounter]; - int k67 = ItemDefinition.intsStack[--iStackCounter]; - if (k67 >= 1 && k67 <= 10) - class11_3.method854(k67 + -1, k45); - continue; - } - if ((opcode < 1400 || opcode >= 1500) && (2400 > opcode || opcode >= 2500)) { - if (1600 > opcode) { - RSInterface class11_4 = flag ? Class164.aClass11_2055 : aClass11_1749; - if (opcode == 1500) { - ItemDefinition.intsStack[iStackCounter++] = class11_4.anInt306; - continue; - } - if (opcode == 1501) { - ItemDefinition.intsStack[iStackCounter++] = class11_4.anInt210; - continue; - } - if (opcode == 1502) { - ItemDefinition.intsStack[iStackCounter++] = class11_4.width; - continue; - } - if (opcode == 1503) { - ItemDefinition.intsStack[iStackCounter++] = class11_4.height; - continue; - } - if (opcode == 1504) { - ItemDefinition.intsStack[iStackCounter++] = class11_4.hidden ? 1 : 0; - continue; - } - if (opcode != 1505) - break; - ItemDefinition.intsStack[iStackCounter++] = class11_4.parentId; - continue; - } - if (opcode < 1700) { - RSInterface class11_5 = flag ? Class164.aClass11_2055 : aClass11_1749; - if (opcode == 1600) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt247; - continue; - } - if (opcode == 1601) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt208; - continue; - } - if (opcode == 1602) { - ItemDefinition.stringsStack[sStackCounter++] = class11_5.text; - continue; - } - if (opcode == 1603) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt240; - continue; - } - if (opcode == 1604) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt252; - continue; - } - if (opcode == 1605) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt164; - continue; - } - if (opcode == 1606) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt182; - continue; - } - if (1607 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt280; - continue; - } - if (opcode == 1608) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt308; - continue; - } - if (opcode == 1609) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt223; - continue; - } - if (1610 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt258; - continue; - } - if (opcode == 1611) { - ItemDefinition.intsStack[iStackCounter++] = class11_5.anInt264; - continue; - } - if (opcode != 1612) - break; - ItemDefinition.intsStack[iStackCounter++] = class11_5.spriteArchiveId; - continue; - } - if (opcode >= 1800) { - if (opcode < 1900) { - RSInterface class11_6 = flag ? Class164.aClass11_2055 : aClass11_1749; - if (1800 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Client.method44(class11_6).method101(); - continue; - } - if (1801 == opcode) { - int l45 = ItemDefinition.intsStack[--iStackCounter]; - l45--; - if (null == class11_6.aClass94Array171 || class11_6.aClass94Array171.length <= l45 || null == class11_6.aClass94Array171[l45]) - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - else - ItemDefinition.stringsStack[sStackCounter++] = class11_6.aClass94Array171[l45]; - continue; - } - if (opcode != 1802) - break; - if (null != class11_6.aClass94_277) - ItemDefinition.stringsStack[sStackCounter++] = class11_6.aClass94_277; - else - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - continue; - } - if (2600 > opcode) { - RSInterface class11_7 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - if (opcode == 2500) { - ItemDefinition.intsStack[iStackCounter++] = class11_7.anInt306; - continue; - } - if (opcode == 2501) { - ItemDefinition.intsStack[iStackCounter++] = class11_7.anInt210; - continue; - } - if (2502 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = class11_7.width; - continue; - } - if (opcode == 2503) { - ItemDefinition.intsStack[iStackCounter++] = class11_7.height; - continue; - } - if (2504 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = class11_7.hidden ? 1 : 0; - continue; - } - if (opcode != 2505) - break; - ItemDefinition.intsStack[iStackCounter++] = class11_7.parentId; - continue; - } - if (opcode >= 2700) { - if (2800 <= opcode) { - if (opcode >= 2900) { - if (opcode < 3200) { - if (opcode == 3100) { - RSString class94_3 = ItemDefinition.stringsStack[--sStackCounter]; - BufferedDataStream.addChatMessage(TextCore.aClass94_2331, 0, class94_3, -1); - continue; - } - if (opcode == 3101) { - iStackCounter -= 2; - PlayerRendering.method628(ItemDefinition.intsStack[iStackCounter - -1], ItemDefinition.intsStack[iStackCounter], Class102.player); - continue; - } - if (opcode == 3103) { - TextureOperation4.method264((byte) 87); - continue; - } - if (opcode == 3104) { - RSString class94_4 = ItemDefinition.stringsStack[--sStackCounter]; - int i46 = 0; - if (class94_4.isInteger()) - i46 = class94_4.parseInt(); - TextureOperation12.outgoingBuffer.putOpcode(23); - TextureOperation12.outgoingBuffer.writeInt(i46); - continue; - } - if (opcode == 3105) { - RSString class94_5 = ItemDefinition.stringsStack[--sStackCounter]; - TextureOperation12.outgoingBuffer.putOpcode(244); - TextureOperation12.outgoingBuffer.writeLong(class94_5.toLong()); - continue; - } - if (opcode == 3106) { - RSString class94_6 = ItemDefinition.stringsStack[--sStackCounter]; - TextureOperation12.outgoingBuffer.putOpcode(65); - TextureOperation12.outgoingBuffer.writeByte(1 + class94_6.length()); - TextureOperation12.outgoingBuffer.writeString(class94_6); - continue; - } - if (opcode == 3107) { - int i7 = ItemDefinition.intsStack[--iStackCounter]; - RSString class94_44 = ItemDefinition.stringsStack[--sStackCounter]; - Class166.method2258(i7, class94_44); - continue; - } - if (opcode == 3108) { - iStackCounter -= 3; - int j46 = ItemDefinition.intsStack[iStackCounter - -1]; - int j7 = ItemDefinition.intsStack[iStackCounter]; - int l67 = ItemDefinition.intsStack[2 + iStackCounter]; - RSInterface class11_22 = Unsorted.getRSInterface(l67); - InterfaceWidget.a(j46, j7, 115, class11_22); - continue; - } - if (opcode == 3109) { - iStackCounter -= 2; - int k7 = ItemDefinition.intsStack[iStackCounter]; - RSInterface class11_20 = flag ? Class164.aClass11_2055 : aClass11_1749; - int k46 = ItemDefinition.intsStack[1 + iStackCounter]; - InterfaceWidget.a(k46, k7, 79, class11_20); - continue; - } - if (opcode != 3110) - break; - int l7 = ItemDefinition.intsStack[--iStackCounter]; - TextureOperation12.outgoingBuffer.putOpcode(111); - TextureOperation12.outgoingBuffer.writeShort(l7); - continue; - } - if (opcode < 3300) { - if (opcode == 3200) { - iStackCounter -= 3; - AudioHandler.soundEffectHandler(ItemDefinition.intsStack[iStackCounter + 1], ItemDefinition.intsStack[iStackCounter], ItemDefinition.intsStack[iStackCounter + 2]); - continue; - } - if (opcode == 3201) { - AudioHandler.musicHandler(ItemDefinition.intsStack[--iStackCounter]); - continue; - } - if (opcode != 3202) - break; - iStackCounter -= 2; - AudioHandler.musicEffectHandler(ItemDefinition.intsStack[iStackCounter]); - continue; - } - if (opcode < 3400) { - if (opcode == 3300) { - ItemDefinition.intsStack[iStackCounter++] = Class44.anInt719; - continue; - } - if (opcode == 3301) { - iStackCounter -= 2; - int i8 = ItemDefinition.intsStack[iStackCounter]; - int l46 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = RSInterface.method861(i8, 89, l46); - continue; - } - if (opcode == 3302) { - iStackCounter -= 2; - int i47 = ItemDefinition.intsStack[iStackCounter + 1]; - int j8 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class12.method872(j8, i47); - continue; - } - if (3303 == opcode) { - iStackCounter -= 2; - int j47 = ItemDefinition.intsStack[iStackCounter - -1]; - int k8 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class167.method2268((byte) -107, k8, j47); - continue; - } - if (3304 == opcode) { - int l8 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = ConfigInventoryDefinition.retrieveConfigurationInventoryFile(l8).size; - continue; - } - if (opcode == 3305) { //Skill update listener (mostly spams health value) - int i9 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation17.anIntArray3185[i9]; - continue; - } - if (opcode == 3306) { //Another Skill update listener (spams 10? Possible TOTAL hp) - int j9 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub20.anIntArray2480[j9]; - //System.out.printf("3306 -> %s: %s\n", j9, ItemDefinition.intsStack[iStackCounter - 1]); - continue; - } - if (3307 == opcode) { //Hover tooltip for Skill Interface, total xp for selected skill - int k9 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class133.anIntArray1743[k9]; - continue; - } - if (opcode == 3308) { - int l9 = WorldListCountry.localPlane; - int k47 = Class131.anInt1716 + (Class102.player.xAxis >> 7); - int i68 = (Class102.player.zAxis >> 7) - -Texture.anInt1152; - ItemDefinition.intsStack[iStackCounter++] = (l9 << 28) - (-(k47 << 14) - i68); - continue; - } - if (opcode == 3309) { - int i10 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Unsorted.bitwiseAnd(16383, i10 >> 14); - continue; - } - if (3310 == opcode) { - int j10 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = j10 >> 28; - continue; - } - if (opcode == 3311) { - int k10 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Unsorted.bitwiseAnd(k10, 16383); - continue; - } - if (opcode == 3312) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.isMember ? 1 : 0; - continue; - } - if (3313 == opcode) { - iStackCounter -= 2; - int l10 = 32768 + ItemDefinition.intsStack[iStackCounter]; - int l47 = ItemDefinition.intsStack[iStackCounter - -1]; - ItemDefinition.intsStack[iStackCounter++] = RSInterface.method861(l10, 118, l47); - continue; - } - if (3314 == opcode) { - iStackCounter -= 2; - int i11 = ItemDefinition.intsStack[iStackCounter] - -32768; - int i48 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class12.method872(i11, i48); - continue; - } - if (3315 == opcode) { - iStackCounter -= 2; - int j11 = 32768 + ItemDefinition.intsStack[iStackCounter]; - int j48 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class167.method2268((byte) -52, j11, j48); - continue; - } - if (opcode == 3316) { - if (Player.rights < 2) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = Player.rights; - continue; - } - if (opcode == 3317) { - ItemDefinition.intsStack[iStackCounter++] = Class38_Sub1.anInt2617; - continue; - } - if (3318 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = userCurrentWorldID; - continue; - } - if (3321 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt136; - continue; - } - if (opcode == 3322) { - ItemDefinition.intsStack[iStackCounter++] = MouseListeningClass.anInt1925; - continue; - } - if (3323 == opcode) { - if (anInt3775 >= 5 && anInt3775 <= 9) - ItemDefinition.intsStack[iStackCounter++] = 1; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 3324) { - if (anInt3775 < 5 || anInt3775 > 9) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = anInt3775; - continue; - } - if (3325 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = TextureOperation3.disableGEBoxes ? 1 : 0; - continue; - } - if (3326 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Class102.player.COMBAT_LEVEL; - continue; - } - if (3327 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Class102.player.class52.aBoolean864 ? 1 : 0; - continue; - } - if (3328 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = !Class3_Sub15.aBoolean2433 || Class121.aBoolean1641 ? 0 : 1; - continue; - } - if (3329 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = TextureOperation31.aBoolean3166 ? 1 : 0; - continue; - } - if (opcode == 3330) { - int k11 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = WorldListEntry.method1079(k11); - continue; - } - if (opcode == 3331) { - iStackCounter -= 2; - int k48 = ItemDefinition.intsStack[1 + iStackCounter]; - int l11 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = method1643(false, l11, k48); - continue; - } - if (3332 == opcode) { - iStackCounter -= 2; - int i12 = ItemDefinition.intsStack[iStackCounter]; - int l48 = ItemDefinition.intsStack[iStackCounter + 1]; - ItemDefinition.intsStack[iStackCounter++] = method1643(true, i12, l48); - continue; - } - if (3333 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Class7.anInt2161; - continue; - } - if (3335 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub20.paramLanguage; - continue; - } - if (opcode == 3336) { - iStackCounter -= 4; - int i49 = ItemDefinition.intsStack[iStackCounter - -1]; - int j12 = ItemDefinition.intsStack[iStackCounter]; - j12 += i49 << 14; - int k76 = ItemDefinition.intsStack[3 + iStackCounter]; - int j68 = ItemDefinition.intsStack[2 + iStackCounter]; - j12 += j68 << 28; - j12 += k76; - ItemDefinition.intsStack[iStackCounter++] = j12; - continue; - } - if (opcode != 3337) - break; - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub26.paramAffid; - continue; - } - if (opcode < 3500) { - if (opcode == 3400) { - iStackCounter -= 2; - int k12 = ItemDefinition.intsStack[iStackCounter]; - int j49 = ItemDefinition.intsStack[1 + iStackCounter]; - EnumDefinition enumDefinition_1 = EnumDefinitionProvider.provide(k12); - ItemDefinition.stringsStack[sStackCounter++] = enumDefinition_1.getString(j49); - continue; - } - if (3408 == opcode) { - iStackCounter -= 4; - int l12 = ItemDefinition.intsStack[iStackCounter]; - int k49 = ItemDefinition.intsStack[1 + iStackCounter]; - int l76 = ItemDefinition.intsStack[3 + iStackCounter]; - int k68 = ItemDefinition.intsStack[iStackCounter - -2]; - EnumDefinition enumDefinition_4 = EnumDefinitionProvider.provide(k68); - if (enumDefinition_4.getKeyType() != l12 || k49 != enumDefinition_4.getValueType()) - throw new RuntimeException("C3408-1"); - if (k49 != 115) - ItemDefinition.intsStack[iStackCounter++] = enumDefinition_4.getInt(l76); - else - ItemDefinition.stringsStack[sStackCounter++] = enumDefinition_4.getString(l76); - continue; - } - if (opcode == 3409) { - iStackCounter -= 3; - int l49 = ItemDefinition.intsStack[iStackCounter - -1]; - int l68 = ItemDefinition.intsStack[iStackCounter + 2]; - int i13 = ItemDefinition.intsStack[iStackCounter]; - if (l49 == -1) - throw new RuntimeException("C3409-2"); - EnumDefinition class3_sub28_sub13_3 = EnumDefinitionProvider.provide(l49); - if (i13 != class3_sub28_sub13_3.getValueType()) - throw new RuntimeException("C3409-1"); - ItemDefinition.intsStack[iStackCounter++] = class3_sub28_sub13_3.containsValue(l68) ? 1 : 0; - continue; - } - if (opcode == 3410) { - int j13 = ItemDefinition.intsStack[--iStackCounter]; - RSString class94_45 = ItemDefinition.stringsStack[--sStackCounter]; - if (j13 == -1) - throw new RuntimeException("C3410-2"); - EnumDefinition enumDefinition_2 = EnumDefinitionProvider.provide(j13); - if (enumDefinition_2.getValueType() != 115) - throw new RuntimeException("C3410-1"); - ItemDefinition.intsStack[iStackCounter++] = enumDefinition_2.containsValue(class94_45) ? 1 : 0; - continue; - } - if (opcode != 3411) - break; - int k13 = ItemDefinition.intsStack[--iStackCounter]; - EnumDefinition enumDefinition = EnumDefinitionProvider.provide(k13); - ItemDefinition.intsStack[iStackCounter++] = enumDefinition.getValues().size(); - continue; - } - if (3700 > opcode) { - if (3600 == opcode) { - if (anInt1357 == 0) - ItemDefinition.intsStack[iStackCounter++] = -2; - else if (anInt1357 != 1) - ItemDefinition.intsStack[iStackCounter++] = Class8.anInt104; - else - ItemDefinition.intsStack[iStackCounter++] = -1; - continue; - } - if (3601 == opcode) { - int l13 = ItemDefinition.intsStack[--iStackCounter]; - if (anInt1357 != 2 || Class8.anInt104 <= l13) - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - else - ItemDefinition.stringsStack[sStackCounter++] = Class70.aClass94Array1046[l13]; - continue; - } - if (opcode == 3602) { - int i14 = ItemDefinition.intsStack[--iStackCounter]; - if (anInt1357 != 2 || i14 >= Class8.anInt104) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anIntArray882[i14]; - continue; - } - if (opcode == 3603) { - int j14 = ItemDefinition.intsStack[--iStackCounter]; - if (2 == anInt1357 && Class8.anInt104 > j14) - ItemDefinition.intsStack[iStackCounter++] = Class57.anIntArray904[j14]; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (3604 == opcode) { - int i50 = ItemDefinition.intsStack[--iStackCounter]; - RSString class94_7 = ItemDefinition.stringsStack[--sStackCounter]; - PacketParser.method1605(class94_7, i50); - continue; - } - if (opcode == 3605) { - RSString class94_8 = ItemDefinition.stringsStack[--sStackCounter]; - Class163_Sub3.method2229(class94_8.toLong()); - continue; - } - if (opcode == 3606) { - RSString class94_9 = ItemDefinition.stringsStack[--sStackCounter]; - TextureOperation7.method297(class94_9.toLong(), 1); - continue; - } - if (opcode == 3607) { - RSString class94_10 = ItemDefinition.stringsStack[--sStackCounter]; - Class81.friendsIgnoreListAlerts(class94_10.toLong()); - continue; - } - if (opcode == 3608) { - RSString class94_11 = ItemDefinition.stringsStack[--sStackCounter]; - TextureOperation30.method212(class94_11.toLong()); - continue; - } - if (opcode == 3609) { - RSString class94_12 = ItemDefinition.stringsStack[--sStackCounter]; - if (class94_12.startsWith(TextCore.aClass94_2323) || class94_12.startsWith(RSString.parse(""))) - class94_12 = class94_12.substring(7); - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.method1176(class94_12) ? 1 : 0; - continue; - } - if (opcode == 3610) { - int k14 = ItemDefinition.intsStack[--iStackCounter]; - if (anInt1357 == 2 && Class8.anInt104 > k14) - ItemDefinition.stringsStack[sStackCounter++] = Unsorted.aClass94Array2566[k14]; - else - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - continue; - } - if (opcode == 3611) { - if (RSInterface.aClass94_251 != null) - ItemDefinition.stringsStack[sStackCounter++] = RSInterface.aClass94_251.longToRSString(); - else - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - continue; - } - if (opcode == 3612) { - if (null != RSInterface.aClass94_251) - ItemDefinition.intsStack[iStackCounter++] = Unsorted.clanSize; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 3613) { - int l14 = ItemDefinition.intsStack[--iStackCounter]; - if (RSInterface.aClass94_251 == null || l14 >= Unsorted.clanSize) - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - else - ItemDefinition.stringsStack[sStackCounter++] = PacketParser.aClass3_Sub19Array3694[l14].aClass94_2476.longToRSString(); - continue; - } - if (opcode == 3614) { - int i15 = ItemDefinition.intsStack[--iStackCounter]; - if (RSInterface.aClass94_251 == null || i15 >= Unsorted.clanSize) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = PacketParser.aClass3_Sub19Array3694[i15].anInt2478; - continue; - } - if (3615 == opcode) { - int j15 = ItemDefinition.intsStack[--iStackCounter]; - if (null == RSInterface.aClass94_251 || j15 >= Unsorted.clanSize) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = PacketParser.aClass3_Sub19Array3694[j15].aByte2472; - continue; - } - if (3616 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Player.aByte3953; - continue; - } - if (opcode == 3617) { - RSString class94_13 = ItemDefinition.stringsStack[--sStackCounter]; - Class106.method1642(class94_13); - continue; - } - if (opcode == 3618) { - ItemDefinition.intsStack[iStackCounter++] = Class91.aByte1308; - continue; - } - if (opcode == 3619) { - RSString class94_14 = ItemDefinition.stringsStack[--sStackCounter]; - Class3_Sub22.method400(class94_14.toLong()); - continue; - } - if (opcode == 3620) { - Class77.method1368(); - continue; - } - if (opcode == 3621) { - if (anInt1357 == 0) - ItemDefinition.intsStack[iStackCounter++] = -1; - else - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub28_Sub5.anInt3591; - continue; - } - if (3622 == opcode) { - int k15 = ItemDefinition.intsStack[--iStackCounter]; - if (anInt1357 == 0 || Class3_Sub28_Sub5.anInt3591 <= k15) - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - else - ItemDefinition.stringsStack[sStackCounter++] = Unsorted.method1052(Class114.ignores[k15]).longToRSString(); - continue; - } - if (3623 == opcode) { - RSString class94_15 = ItemDefinition.stringsStack[--sStackCounter]; - if (class94_15.startsWith(TextCore.aClass94_2323) || class94_15.startsWith(RSString.parse(""))) - class94_15 = class94_15.substring(7); - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub24_Sub3.method467(class94_15) ? 1 : 0; - continue; - } - if (opcode == 3624) { - int l15 = ItemDefinition.intsStack[--iStackCounter]; - if (null != PacketParser.aClass3_Sub19Array3694 && l15 < Unsorted.clanSize && PacketParser.aClass3_Sub19Array3694[l15].aClass94_2476.equalsStringIgnoreCase(Class102.player.displayName)) - ItemDefinition.intsStack[iStackCounter++] = 1; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 3625) { - if (Class161.aClass94_2035 == null) - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - else - ItemDefinition.stringsStack[sStackCounter++] = Class161.aClass94_2035.longToRSString(); - continue; - } - if (3626 == opcode) { - int i16 = ItemDefinition.intsStack[--iStackCounter]; - if (RSInterface.aClass94_251 == null || i16 >= Unsorted.clanSize) - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - else - ItemDefinition.stringsStack[sStackCounter++] = PacketParser.aClass3_Sub19Array3694[i16].aClass94_2473; - continue; - } - if (opcode == 3627) { - int j16 = ItemDefinition.intsStack[--iStackCounter]; - if (anInt1357 != 2 || 0 > j16 || Class8.anInt104 <= j16) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = Unsorted.aBooleanArray73[j16] ? 1 : 0; - continue; - } - if (opcode == 3628) { - RSString class94_16 = ItemDefinition.stringsStack[--sStackCounter]; - if (class94_16.startsWith(TextCore.aClass94_2323) || class94_16.startsWith(RSString.parse(""))) - class94_16 = class94_16.substring(7); - ItemDefinition.intsStack[iStackCounter++] = PacketParser.method826(class94_16, -1); - continue; - } - if (opcode != 3629) - break; - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub31.paramCountryID; - continue; - } - if (opcode < 4000) { - if (opcode == 3903) { - int k16 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation29.aClass133Array3393[k16].method1805(); - continue; - } - if (opcode == 3904) { - int l16 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation29.aClass133Array3393[l16].anInt1752; - continue; - } - if (opcode == 3905) { - int i17 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation29.aClass133Array3393[i17].anInt1757; - continue; - } - if (opcode == 3906) { - int j17 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation29.aClass133Array3393[j17].anInt1747; - continue; - } - if (opcode == 3907) { - int k17 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation29.aClass133Array3393[k17].anInt1746; - continue; - } - if (3908 == opcode) { - int l17 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation29.aClass133Array3393[l17].anInt1750; - continue; - } - if (3910 == opcode) { - int i18 = ItemDefinition.intsStack[--iStackCounter]; - int j50 = TextureOperation29.aClass133Array3393[i18].method1804(); - ItemDefinition.intsStack[iStackCounter++] = j50 == 0 ? 1 : 0; - continue; - } - if (3911 == opcode) { - int j18 = ItemDefinition.intsStack[--iStackCounter]; - int k50 = TextureOperation29.aClass133Array3393[j18].method1804(); - ItemDefinition.intsStack[iStackCounter++] = k50 != 2 ? 0 : 1; - continue; - } - if (opcode == 3912) { - int k18 = ItemDefinition.intsStack[--iStackCounter]; - int l50 = TextureOperation29.aClass133Array3393[k18].method1804(); - ItemDefinition.intsStack[iStackCounter++] = l50 == 5 ? 1 : 0; - continue; - } - if (opcode != 3913) - break; - int l18 = ItemDefinition.intsStack[--iStackCounter]; - int i51 = TextureOperation29.aClass133Array3393[l18].method1804(); - ItemDefinition.intsStack[iStackCounter++] = 1 == i51 ? 1 : 0; - continue; - } - if (opcode < 4100) { - if (opcode == 4000) { - iStackCounter -= 2; - int i19 = ItemDefinition.intsStack[iStackCounter]; - int j51 = ItemDefinition.intsStack[iStackCounter - -1]; - ItemDefinition.intsStack[iStackCounter++] = j51 + i19; - continue; - } - if (opcode == 4001) { - iStackCounter -= 2; - int j19 = ItemDefinition.intsStack[iStackCounter]; - int k51 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = -k51 + j19; - continue; - } - if (4002 == opcode) { - iStackCounter -= 2; - int k19 = ItemDefinition.intsStack[iStackCounter]; - int l51 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = l51 * k19; - continue; - } - if (4003 == opcode) { - iStackCounter -= 2; - int l19 = ItemDefinition.intsStack[iStackCounter]; - int i52 = ItemDefinition.intsStack[iStackCounter - -1]; - ItemDefinition.intsStack[iStackCounter++] = l19 / i52; - continue; - } - if (opcode == 4004) { - int i20 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = (int) ((double) i20 * Math.random()); - continue; - } - if (4005 == opcode) { - int j20 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = (int) (Math.random() * (double) (1 + j20)); - continue; - } - if (4006 == opcode) { - iStackCounter -= 5; - int k20 = ItemDefinition.intsStack[iStackCounter]; - int j52 = ItemDefinition.intsStack[iStackCounter - -1]; - int i77 = ItemDefinition.intsStack[iStackCounter - -3]; - int i69 = ItemDefinition.intsStack[2 + iStackCounter]; - int j79 = ItemDefinition.intsStack[iStackCounter + 4]; - ItemDefinition.intsStack[iStackCounter++] = ((-k20 + j52) * (j79 + -i69)) / (-i69 + i77) + k20; - continue; - } - if (opcode == 4007) { - iStackCounter -= 2; - long l20 = ItemDefinition.intsStack[iStackCounter]; - long l69 = ItemDefinition.intsStack[iStackCounter + 1]; - ItemDefinition.intsStack[iStackCounter++] = (int) ((l20 * l69) / 100L + l20); - continue; - } - if (opcode == 4008) { - iStackCounter -= 2; - int i21 = ItemDefinition.intsStack[iStackCounter]; - int k52 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation3.bitwiseOr(i21, 1 << k52); - continue; - } - if (4009 == opcode) { - iStackCounter -= 2; - int j21 = ItemDefinition.intsStack[iStackCounter]; - int l52 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Unsorted.bitwiseAnd(-1 - (1 << l52), j21); - continue; - } - if (opcode == 4010) { - iStackCounter -= 2; - int k21 = ItemDefinition.intsStack[iStackCounter]; - int i53 = ItemDefinition.intsStack[iStackCounter - -1]; - ItemDefinition.intsStack[iStackCounter++] = Unsorted.bitwiseAnd(k21, 1 << i53) != 0 ? 1 : 0; - continue; - } - if (opcode == 4011) { - iStackCounter -= 2; - int j53 = ItemDefinition.intsStack[iStackCounter - -1]; - int l21 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = l21 % j53; - continue; - } - if (opcode == 4012) { - iStackCounter -= 2; - int k53 = ItemDefinition.intsStack[iStackCounter + 1]; - int i22 = ItemDefinition.intsStack[iStackCounter]; - if (0 != i22) - ItemDefinition.intsStack[iStackCounter++] = (int) Math.pow(i22, k53); - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 4013) { - iStackCounter -= 2; - int l53 = ItemDefinition.intsStack[iStackCounter - -1]; - int j22 = ItemDefinition.intsStack[iStackCounter]; - if (j22 == 0) { - ItemDefinition.intsStack[iStackCounter++] = 0; - } else if (l53 == 0) - ItemDefinition.intsStack[iStackCounter++] = 0x7fffffff; - else - ItemDefinition.intsStack[iStackCounter++] = (int) Math.pow(j22, 1.0D / (double) l53); - continue; - } - if (opcode == 4014) { - iStackCounter -= 2; - int i54 = ItemDefinition.intsStack[iStackCounter + 1]; - int k22 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Unsorted.bitwiseAnd(i54, k22); - continue; - } - if (opcode == 4015) { - iStackCounter -= 2; - int l22 = ItemDefinition.intsStack[iStackCounter]; - int j54 = ItemDefinition.intsStack[iStackCounter + 1]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation3.bitwiseOr(l22, j54); - continue; - } - if (opcode == 4016) { - iStackCounter -= 2; - int i23 = ItemDefinition.intsStack[iStackCounter]; - int k54 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = k54 <= i23 ? k54 : i23; - continue; - } - if (opcode == 4017) { - iStackCounter -= 2; - int l54 = ItemDefinition.intsStack[1 + iStackCounter]; - int j23 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = j23 > l54 ? j23 : l54; - continue; - } - if (opcode != 4018) - break; - iStackCounter -= 3; - long l23 = ItemDefinition.intsStack[iStackCounter]; - long l70 = ItemDefinition.intsStack[iStackCounter + 1]; - long l79 = ItemDefinition.intsStack[2 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = (int) ((l23 * l79) / l70); - continue; - } - if (4200 <= opcode) { - if (opcode >= 4300) { - if (opcode < 4400) { - if (4300 != opcode) - break; - iStackCounter -= 2; - int k23 = ItemDefinition.intsStack[iStackCounter]; - int i55 = ItemDefinition.intsStack[1 + iStackCounter]; - Class3_Sub28_Sub9 class3_sub28_sub9 = LinkedList.method1210(i55); - if (!class3_sub28_sub9.method585()) - ItemDefinition.intsStack[iStackCounter++] = NPCDefinition.getNPCDefinition(k23).method1475(i55, class3_sub28_sub9.anInt3614); - else - ItemDefinition.stringsStack[sStackCounter++] = NPCDefinition.getNPCDefinition(k23).method1477(i55, class3_sub28_sub9.aClass94_3619); - continue; - } - if (opcode >= 4500) { - if (opcode >= 4600) { - if (opcode < 5100) { - if (opcode == 5000) { - ItemDefinition.intsStack[iStackCounter++] = anInt3101; - continue; - } - if (opcode == 5001) { - iStackCounter -= 3; - anInt3101 = ItemDefinition.intsStack[iStackCounter]; - Class24.anInt467 = ItemDefinition.intsStack[1 + iStackCounter]; - Class45.anInt734 = ItemDefinition.intsStack[2 + iStackCounter]; - TextureOperation12.outgoingBuffer.putOpcode(157); - TextureOperation12.outgoingBuffer.writeByte(anInt3101); - TextureOperation12.outgoingBuffer.writeByte(Class24.anInt467); - TextureOperation12.outgoingBuffer.writeByte(Class45.anInt734); - continue; - } - if (opcode == 5002) { - RSString class94_17 = ItemDefinition.stringsStack[--sStackCounter]; - iStackCounter -= 2; - int j55 = ItemDefinition.intsStack[iStackCounter]; - int j69 = ItemDefinition.intsStack[1 + iStackCounter]; - TextureOperation12.outgoingBuffer.putOpcode(99); - TextureOperation12.outgoingBuffer.writeLong(class94_17.toLong()); - TextureOperation12.outgoingBuffer.writeByte(j55 - 1); - TextureOperation12.outgoingBuffer.writeByte(j69); - continue; - } - if (opcode == 5003) { - RSString class94_46 = null; - int i24 = ItemDefinition.intsStack[--iStackCounter]; - if (i24 < 100) - class94_46 = LinkableRSString.aClass94Array2580[i24]; - if (class94_46 == null) - class94_46 = TextCore.aClass94_2331; - ItemDefinition.stringsStack[sStackCounter++] = class94_46; - continue; - } - if (opcode == 5004) { - int j24 = ItemDefinition.intsStack[--iStackCounter]; - int k55 = -1; - if (j24 < 100 && null != LinkableRSString.aClass94Array2580[j24]) - k55 = MessageManager.anIntArray3082[j24]; - ItemDefinition.intsStack[iStackCounter++] = k55; - continue; - } - if (opcode == 5005) { - ItemDefinition.intsStack[iStackCounter++] = Class24.anInt467; - continue; - } - if (opcode == 5008) {//Used for a lot of things involving :: || More prefixes can be added by using || and listing said added way, ie ;; can be used instead of :: - RSString class94_18 = ItemDefinition.stringsStack[--sStackCounter]; - if (class94_18.startsWith(TextCore.aClass94_132) || class94_18.startsWith(RSString.parse(";;"))) - ClientCommands.ClientCommands(class94_18); - else if (Player.rights != 0 || (!Class3_Sub15.aBoolean2433 || Class121.aBoolean1641) && !TextureOperation31.aBoolean3166) { - RSString class94_47 = class94_18.toLowercase(); - byte byte3 = 0; - if (class94_47.startsWith(TextCore.TextColorYellow)) { - byte3 = 0; - class94_18 = class94_18.substring(TextCore.TextColorYellow.length()); - } else if (class94_47.startsWith(TextCore.TextColorRed)) { - class94_18 = class94_18.substring(TextCore.TextColorRed.length()); - byte3 = 1; - } else if (class94_47.startsWith(TextCore.TextColorGreen)) { - class94_18 = class94_18.substring(TextCore.TextColorGreen.length()); - byte3 = 2; - } else if (class94_47.startsWith(TextCore.TextColorCyan)) { - byte3 = 3; - class94_18 = class94_18.substring(TextCore.TextColorCyan.length()); - } else if (class94_47.startsWith(TextCore.TextColorPurple)) { - class94_18 = class94_18.substring(TextCore.TextColorPurple.length()); - byte3 = 4; - } else if (class94_47.startsWith(TextCore.TextColorWhite)) { - class94_18 = class94_18.substring(TextCore.TextColorWhite.length()); - byte3 = 5; - } else if (class94_47.startsWith(TextCore.TextFlashOne)) { - byte3 = 6; - class94_18 = class94_18.substring(TextCore.TextFlashOne.length()); - } else if (class94_47.startsWith(TextCore.TextFlashTwo)) { - byte3 = 7; - class94_18 = class94_18.substring(TextCore.TextFlashTwo.length()); - } else if (class94_47.startsWith(TextCore.TextFlashThree)) { - class94_18 = class94_18.substring(TextCore.TextFlashThree.length()); - byte3 = 8; - } else if (class94_47.startsWith(TextCore.TextGlowOne)) { - byte3 = 9; - class94_18 = class94_18.substring(TextCore.TextGlowOne.length()); - } else if (class94_47.startsWith(TextCore.TextGlowTwo)) { - byte3 = 10; - class94_18 = class94_18.substring(TextCore.TextGlowTwo.length()); - } else if (class94_47.startsWith(TextCore.TextGlowThree)) { - class94_18 = class94_18.substring(TextCore.TextGlowThree.length()); - byte3 = 11; - } else if (0 != Class3_Sub20.paramLanguage) - if (class94_47.startsWith(TextCore.TextColorYellow)) { - byte3 = 0; - class94_18 = class94_18.substring(TextCore.TextColorYellow.length()); - } else if (class94_47.startsWith(TextCore.TextColorRed)) { - class94_18 = class94_18.substring(TextCore.TextColorRed.length()); - byte3 = 1; - } else if (class94_47.startsWith(TextCore.TextColorGreen)) { - class94_18 = class94_18.substring(TextCore.TextColorGreen.length()); - byte3 = 2; - } else if (class94_47.startsWith(TextCore.TextColorCyan)) { - class94_18 = class94_18.substring(TextCore.TextColorCyan.length()); - byte3 = 3; - } else if (class94_47.startsWith(TextCore.TextColorPurple)) { - class94_18 = class94_18.substring(TextCore.TextColorPurple.length()); - byte3 = 4; - } else if (class94_47.startsWith(TextCore.TextColorWhite)) { - byte3 = 5; - class94_18 = class94_18.substring(TextCore.TextColorWhite.length()); - } else if (class94_47.startsWith(TextCore.TextFlashOne)) { - class94_18 = class94_18.substring(TextCore.TextFlashOne.length()); - byte3 = 6; - } else if (class94_47.startsWith(TextCore.TextFlashTwo)) { - byte3 = 7; - class94_18 = class94_18.substring(TextCore.TextFlashTwo.length()); - } else if (class94_47.startsWith(TextCore.TextFlashThree)) { - byte3 = 8; - class94_18 = class94_18.substring(TextCore.TextFlashThree.length()); - } else if (class94_47.startsWith(TextCore.TextGlowOne)) { - byte3 = 9; - class94_18 = class94_18.substring(TextCore.TextGlowOne.length()); - } else if (class94_47.startsWith(TextCore.TextGlowTwo)) { - class94_18 = class94_18.substring(TextCore.TextGlowTwo.length()); - byte3 = 10; - } else if (class94_47.startsWith(TextCore.TextGlowThree)) { - class94_18 = class94_18.substring(TextCore.TextGlowThree.length()); - byte3 = 11; - } - byte byte4 = 0; - class94_47 = class94_18.toLowercase(); - if (class94_47.startsWith(TextCore.TextWave)) { - class94_18 = class94_18.substring(TextCore.TextWave.length()); - byte4 = 1; - } else if (class94_47.startsWith(TextCore.TextWaveTwo)) { - byte4 = 2; - class94_18 = class94_18.substring(TextCore.TextWaveTwo.length()); - } else if (class94_47.startsWith(TextCore.TextShake)) { - class94_18 = class94_18.substring(TextCore.TextShake.length()); - byte4 = 3; - } else if (class94_47.startsWith(TextCore.HasScroll)) { - byte4 = 4; - class94_18 = class94_18.substring(TextCore.HasScroll.length()); - } else if (class94_47.startsWith(TextCore.TextSlide)) { - byte4 = 5; - class94_18 = class94_18.substring(TextCore.TextSlide.length()); - } else if (0 != Class3_Sub20.paramLanguage) - if (class94_47.startsWith(TextCore.TextWave)) { - class94_18 = class94_18.substring(TextCore.TextWave.length()); - byte4 = 1; - } else if (class94_47.startsWith(TextCore.TextWaveTwo)) { - byte4 = 2; - class94_18 = class94_18.substring(TextCore.TextWaveTwo.length()); - } else if (class94_47.startsWith(TextCore.TextShake)) { - byte4 = 3; - class94_18 = class94_18.substring(TextCore.TextShake.length()); - } else if (class94_47.startsWith(TextCore.HasScroll)) { - byte4 = 4; - class94_18 = class94_18.substring(TextCore.HasScroll.length()); - } else if (class94_47.startsWith(TextCore.TextSlide)) { - class94_18 = class94_18.substring(TextCore.TextSlide.length()); - byte4 = 5; - } - TextureOperation12.outgoingBuffer.putOpcode(237); - TextureOperation12.outgoingBuffer.writeByte(0); - int k79 = TextureOperation12.outgoingBuffer.index; - TextureOperation12.outgoingBuffer.writeByte(byte3); - TextureOperation12.outgoingBuffer.writeByte(byte4); - Class85.method1423(TextureOperation12.outgoingBuffer, class94_18); - TextureOperation12.outgoingBuffer.method769(-k79 + TextureOperation12.outgoingBuffer.index); - } - continue; - } - if (opcode == 5009) { - sStackCounter -= 2; - RSString class94_48 = ItemDefinition.stringsStack[sStackCounter + 1]; - RSString class94_19 = ItemDefinition.stringsStack[sStackCounter]; - if (Player.rights != 0 || (!Class3_Sub15.aBoolean2433 || Class121.aBoolean1641) && !TextureOperation31.aBoolean3166) { - TextureOperation12.outgoingBuffer.putOpcode(201); - TextureOperation12.outgoingBuffer.writeByte(0); - int k69 = TextureOperation12.outgoingBuffer.index; - TextureOperation12.outgoingBuffer.writeLong(class94_19.toLong()); - Class85.method1423(TextureOperation12.outgoingBuffer, class94_48); - TextureOperation12.outgoingBuffer.method769(TextureOperation12.outgoingBuffer.index - k69); - } - continue; - } - if (opcode == 5010) { - int k24 = ItemDefinition.intsStack[--iStackCounter]; - RSString class94_49 = null; - if (k24 < 100) - class94_49 = MessageManager.aClass94Array3226[k24]; - if (null == class94_49) - class94_49 = TextCore.aClass94_2331; - ItemDefinition.stringsStack[sStackCounter++] = class94_49; - continue; - } - if (opcode == 5011) { - int l24 = ItemDefinition.intsStack[--iStackCounter]; - RSString class94_50 = null; - if (l24 < 100) - class94_50 = Class163_Sub3.aClass94Array3003[l24]; - if (class94_50 == null) - class94_50 = TextCore.aClass94_2331; - ItemDefinition.stringsStack[sStackCounter++] = class94_50; - continue; - } - if (opcode == 5012) { - int i25 = ItemDefinition.intsStack[--iStackCounter]; - int l55 = -1; - if (i25 < 100) - l55 = MessageManager.anIntArray1835[i25]; - ItemDefinition.intsStack[iStackCounter++] = l55; - continue; - } - if (opcode == 5015) { - RSString class94_20; - if (Class102.player == null || null == Class102.player.displayName) - class94_20 = Class131.username; - else - class94_20 = Class102.player.getName(); - ItemDefinition.stringsStack[sStackCounter++] = class94_20; - continue; - } - if (opcode == 5016) { - ItemDefinition.intsStack[iStackCounter++] = Class45.anInt734; - continue; - } - if (opcode == 5017) { - ItemDefinition.intsStack[iStackCounter++] = TextureOperation16.anInt3114; - continue; - } - if (5050 == opcode) { - int j25 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = QuickChat.getQuickChatMessage(j25).quickChatMenu; - continue; - } - if (opcode == 5051) { - int k25 = ItemDefinition.intsStack[--iStackCounter]; - Class3_Sub28_Sub1 class3_sub28_sub1 = QuickChat.getQuickChatMessage(k25); - if (class3_sub28_sub1.anIntArray3534 != null) - ItemDefinition.intsStack[iStackCounter++] = class3_sub28_sub1.anIntArray3534.length; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 5052) { - iStackCounter -= 2; - int l25 = ItemDefinition.intsStack[iStackCounter]; - int i56 = ItemDefinition.intsStack[iStackCounter - -1]; - Class3_Sub28_Sub1 class3_sub28_sub1_2 = QuickChat.getQuickChatMessage(l25); - int j77 = class3_sub28_sub1_2.anIntArray3534[i56]; - ItemDefinition.intsStack[iStackCounter++] = j77; - continue; - } - if (opcode == 5053) { - int i26 = ItemDefinition.intsStack[--iStackCounter]; - Class3_Sub28_Sub1 class3_sub28_sub1_1 = QuickChat.getQuickChatMessage(i26); - if (class3_sub28_sub1_1.anIntArray3540 != null) - ItemDefinition.intsStack[iStackCounter++] = class3_sub28_sub1_1.anIntArray3540.length; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 5054) { - iStackCounter -= 2; - int j56 = ItemDefinition.intsStack[1 + iStackCounter]; - int j26 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = QuickChat.getQuickChatMessage(j26).anIntArray3540[j56]; - continue; - } - if (opcode == 5055) { - int k26 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = QuickChat.method733(k26).method554(); - continue; - } - if (opcode == 5056) { - int l26 = ItemDefinition.intsStack[--iStackCounter]; - QuickChatDefinition quickChatDefinition = QuickChat.method733(l26); - if (null != quickChatDefinition.anIntArray3567) - ItemDefinition.intsStack[iStackCounter++] = quickChatDefinition.anIntArray3567.length; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 5057) { - iStackCounter -= 2; - int k56 = ItemDefinition.intsStack[1 + iStackCounter]; - int i27 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = QuickChat.method733(i27).anIntArray3567[k56]; - continue; - } - if (opcode == 5058) { - CS2Methods.aQuickChat_1056 = new QuickChat(); - CS2Methods.aQuickChat_1056.anInt149 = ItemDefinition.intsStack[--iStackCounter]; - CS2Methods.aQuickChat_1056.aQuickChatDefinition_151 = QuickChat.method733(CS2Methods.aQuickChat_1056.anInt149); - CS2Methods.aQuickChat_1056.anIntArray153 = new int[CS2Methods.aQuickChat_1056.aQuickChatDefinition_151.method552()]; - continue; - } - if (5059 == opcode) { - TextureOperation12.outgoingBuffer.putOpcode(167); - TextureOperation12.outgoingBuffer.writeByte(0); - int j27 = TextureOperation12.outgoingBuffer.index; - TextureOperation12.outgoingBuffer.writeByte(0); - TextureOperation12.outgoingBuffer.writeShort(CS2Methods.aQuickChat_1056.anInt149); - CS2Methods.aQuickChat_1056.aQuickChatDefinition_151.method545(TextureOperation12.outgoingBuffer, CS2Methods.aQuickChat_1056.anIntArray153); - TextureOperation12.outgoingBuffer.method769(-j27 + TextureOperation12.outgoingBuffer.index); - continue; - } - if (5060 == opcode) { - RSString class94_21 = ItemDefinition.stringsStack[--sStackCounter]; - TextureOperation12.outgoingBuffer.putOpcode(178); - TextureOperation12.outgoingBuffer.writeByte(0); - int l56 = TextureOperation12.outgoingBuffer.index; - TextureOperation12.outgoingBuffer.writeLong(class94_21.toLong()); - TextureOperation12.outgoingBuffer.writeShort(CS2Methods.aQuickChat_1056.anInt149); - CS2Methods.aQuickChat_1056.aQuickChatDefinition_151.method545(TextureOperation12.outgoingBuffer, CS2Methods.aQuickChat_1056.anIntArray153); - TextureOperation12.outgoingBuffer.method769(TextureOperation12.outgoingBuffer.index + -l56); - continue; - } - if (opcode == 5061) { - TextureOperation12.outgoingBuffer.putOpcode(167); - TextureOperation12.outgoingBuffer.writeByte(0); - int k27 = TextureOperation12.outgoingBuffer.index; - TextureOperation12.outgoingBuffer.writeByte(1); - TextureOperation12.outgoingBuffer.writeShort(CS2Methods.aQuickChat_1056.anInt149); - CS2Methods.aQuickChat_1056.aQuickChatDefinition_151.method545(TextureOperation12.outgoingBuffer, CS2Methods.aQuickChat_1056.anIntArray153); - TextureOperation12.outgoingBuffer.method769(-k27 + TextureOperation12.outgoingBuffer.index); - continue; - } - if (opcode == 5062) { - iStackCounter -= 2; - int i57 = ItemDefinition.intsStack[1 + iStackCounter]; - int l27 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = QuickChat.getQuickChatMessage(l27).anIntArray3535[i57]; - continue; - } - if (opcode == 5063) { - iStackCounter -= 2; - int j57 = ItemDefinition.intsStack[iStackCounter - -1]; - int i28 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = QuickChat.getQuickChatMessage(i28).anIntArray3533[j57]; - continue; - } - if (5064 == opcode) { - iStackCounter -= 2; - int k57 = ItemDefinition.intsStack[1 + iStackCounter]; - int j28 = ItemDefinition.intsStack[iStackCounter]; - if (k57 != -1) - ItemDefinition.intsStack[iStackCounter++] = QuickChat.getQuickChatMessage(j28).method529(k57); - else - ItemDefinition.intsStack[iStackCounter++] = -1; - continue; - } - if (opcode == 5065) { - iStackCounter -= 2; - int k28 = ItemDefinition.intsStack[iStackCounter]; - int l57 = ItemDefinition.intsStack[iStackCounter + 1]; - if (l57 != -1) - ItemDefinition.intsStack[iStackCounter++] = QuickChat.getQuickChatMessage(k28).method526(l57); - else - ItemDefinition.intsStack[iStackCounter++] = -1; - continue; - } - if (opcode == 5066) { - int l28 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = QuickChat.method733(l28).method552(); - continue; - } - if (opcode == 5067) { - iStackCounter -= 2; - int i58 = ItemDefinition.intsStack[iStackCounter + 1]; - int i29 = ItemDefinition.intsStack[iStackCounter]; - int i70 = QuickChat.method733(i29).method550(49, i58); - ItemDefinition.intsStack[iStackCounter++] = i70; - continue; - } - if (5068 == opcode) { - iStackCounter -= 2; - int j29 = ItemDefinition.intsStack[iStackCounter]; - int j58 = ItemDefinition.intsStack[1 + iStackCounter]; - CS2Methods.aQuickChat_1056.anIntArray153[j29] = j58; - continue; - } - if (opcode == 5069) { - iStackCounter -= 2; - int k29 = ItemDefinition.intsStack[iStackCounter]; - int k58 = ItemDefinition.intsStack[iStackCounter + 1]; - CS2Methods.aQuickChat_1056.anIntArray153[k29] = k58; - continue; - } - if (opcode == 5070) { - iStackCounter -= 3; - int l29 = ItemDefinition.intsStack[iStackCounter]; - int j70 = ItemDefinition.intsStack[iStackCounter - -2]; - int l58 = ItemDefinition.intsStack[iStackCounter + 1]; - QuickChatDefinition quickChatDefinition_1 = QuickChat.method733(l29); - if (0 != quickChatDefinition_1.method550(73, l58)) - throw new RuntimeException("bad command"); - ItemDefinition.intsStack[iStackCounter++] = quickChatDefinition_1.method549(j70, l58); - continue; - } - if (opcode == 5071) { - RSString class94_22 = ItemDefinition.stringsStack[--sStackCounter]; - boolean flag4 = 1 == ItemDefinition.intsStack[--iStackCounter]; - Class3_Sub28_Sub3.method541(flag4, class94_22); - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt952; - continue; - } - if (5072 == opcode) { - if (Class99.aShortArray1398 == null || Unsorted.anInt952 <= Class140_Sub4.anInt2756) - ItemDefinition.intsStack[iStackCounter++] = -1; - else - ItemDefinition.intsStack[iStackCounter++] = Unsorted.bitwiseAnd(Class99.aShortArray1398[Class140_Sub4.anInt2756++], 65535); - continue; - } - if (opcode != 5073) - break; - Class140_Sub4.anInt2756 = 0; - continue; - } - if (5200 > opcode) { - if (5100 == opcode) { - if (!ObjectDefinition.aBooleanArray1490[86]) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = 1; - continue; - } - if (5101 == opcode) { - if (ObjectDefinition.aBooleanArray1490[82]) - ItemDefinition.intsStack[iStackCounter++] = 1; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (5102 != opcode) - break; - if (ObjectDefinition.aBooleanArray1490[81]) - ItemDefinition.intsStack[iStackCounter++] = 1; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode < 5300) { - if (opcode == 5200) { - NPCDefinition.method1479(ItemDefinition.intsStack[--iStackCounter]); - continue; - } - if (5201 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub28_Sub8.method571(); - continue; - } - if (opcode == 5202) { - Class3_Sub24_Sub4.method503(ItemDefinition.intsStack[--iStackCounter]); - continue; - } - if (5203 == opcode) { - Unsorted.method84(ItemDefinition.stringsStack[--sStackCounter], -801); - continue; - } - if (5204 == opcode) { - ItemDefinition.stringsStack[sStackCounter - 1] = CS2Methods.method27(ItemDefinition.stringsStack[sStackCounter - 1]); - continue; - } - if (5205 == opcode) { - Class3_Sub10.method138(ItemDefinition.stringsStack[--sStackCounter]); - continue; - } - if (opcode == 5206) { - int i30 = ItemDefinition.intsStack[--iStackCounter]; - Class3_Sub28_Sub3 class3_sub28_sub3_4 = Unsorted.method884(0x3fff & i30 >> 14, (byte) 111, 0x3fff & i30); - if (class3_sub28_sub3_4 != null) - ItemDefinition.stringsStack[sStackCounter++] = class3_sub28_sub3_4.aClass94_3561; - else - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - continue; - } - if (opcode == 5207) { - Class3_Sub28_Sub3 class3_sub28_sub3 = Class3_Sub15.method371(ItemDefinition.stringsStack[--sStackCounter]); - if (null != class3_sub28_sub3 && class3_sub28_sub3.aClass94_3554 != null) - ItemDefinition.stringsStack[sStackCounter++] = class3_sub28_sub3.aClass94_3554; - else - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - continue; - } - if (5208 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Class49.anInt817; - ItemDefinition.intsStack[iStackCounter++] = Class17.anInt410; - continue; - } - if (5209 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = TextureOperation37.anInt3256 + Class3_Sub28_Sub1.anInt3536; - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt65 + -Class3_Sub4.anInt2251 + (-1 + Class108.anInt1460); - continue; - } - if (opcode == 5210) { - Class3_Sub28_Sub3 class3_sub28_sub3_1 = Unsorted.method520((byte) -82); - if (class3_sub28_sub3_1 == null) { - ItemDefinition.intsStack[iStackCounter++] = 0; - ItemDefinition.intsStack[iStackCounter++] = 0; - } else { - ItemDefinition.intsStack[iStackCounter++] = class3_sub28_sub3_1.anInt3558 * 64; - ItemDefinition.intsStack[iStackCounter++] = 64 * class3_sub28_sub3_1.anInt3556; - } - continue; - } - if (opcode == 5211) { - Class3_Sub28_Sub3 class3_sub28_sub3_2 = Unsorted.method520((byte) -121); - if (class3_sub28_sub3_2 == null) { - ItemDefinition.intsStack[iStackCounter++] = 0; - ItemDefinition.intsStack[iStackCounter++] = 0; - } else { - ItemDefinition.intsStack[iStackCounter++] = class3_sub28_sub3_2.anInt3559 - class3_sub28_sub3_2.anInt3555; - ItemDefinition.intsStack[iStackCounter++] = -class3_sub28_sub3_2.anInt3562 + class3_sub28_sub3_2.anInt3549; - } - continue; - } - if (opcode == 5212) { - int j30 = Class67.method1258((byte) -53); - int k70 = 0; - RSString class94_51; - if (j30 == -1) { - class94_51 = TextCore.aClass94_2331; - } else { - class94_51 = Class119.aClass131_1624.aClass94Array1721[j30]; - k70 = Class119.aClass131_1624.method1791(j30, 8); - } - class94_51 = class94_51.method1560(TextCore.aClass94_2765, TextCore.aClass94_2168); - ItemDefinition.stringsStack[sStackCounter++] = class94_51; - ItemDefinition.intsStack[iStackCounter++] = k70; - continue; - } - if (opcode == 5213) { - int i71 = 0; - int k30 = TextureOperation23.method251(); - RSString class94_52; - if (k30 == -1) { - class94_52 = TextCore.aClass94_2331; - } else { - class94_52 = Class119.aClass131_1624.aClass94Array1721[k30]; - i71 = Class119.aClass131_1624.method1791(k30, 8); - } - class94_52 = class94_52.method1560(TextCore.aClass94_2765, TextCore.aClass94_2168); - ItemDefinition.stringsStack[sStackCounter++] = class94_52; - ItemDefinition.intsStack[iStackCounter++] = i71; - continue; - } - if (opcode == 5214) { - int l30 = ItemDefinition.intsStack[--iStackCounter]; - Unsorted.method565(0x3fff & l30 >> 14, 0x3fff & l30); - continue; - } - if (opcode == 5215) { - int i31 = ItemDefinition.intsStack[--iStackCounter]; - RSString class94_53 = ItemDefinition.stringsStack[--sStackCounter]; - boolean flag10 = false; - Queue class13 = method1664(0x3fff & i31 >> 14, 0x3fff & i31); - Class3_Sub28_Sub3 class3_sub28_sub3_5 = (Class3_Sub28_Sub3) class13.getFront(); - do { - if (class3_sub28_sub3_5 == null) - break; - if (class3_sub28_sub3_5.aClass94_3561.equalsStringIgnoreCase(class94_53)) { - flag10 = true; - break; - } - class3_sub28_sub3_5 = (Class3_Sub28_Sub3) class13.next(); - } while (true); - if (!flag10) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = 1; - continue; - } - if (opcode == 5216) { - int j31 = ItemDefinition.intsStack[--iStackCounter]; - TextureOperation36.method344(j31, 4); - continue; - } - if (opcode == 5217) { - int k31 = ItemDefinition.intsStack[--iStackCounter]; - if (!Class3_Sub10.method140(k31)) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = 1; - continue; - } - if (opcode == 5218) { - Class3_Sub28_Sub3 class3_sub28_sub3_3 = Unsorted.method520((byte) -124); - if (null != class3_sub28_sub3_3) - ItemDefinition.intsStack[iStackCounter++] = class3_sub28_sub3_3.anInt3563; - else - ItemDefinition.intsStack[iStackCounter++] = -1; - continue; - } - if (opcode == 5219) { - Class21.method915(ItemDefinition.stringsStack[--sStackCounter]); - continue; - } - if (opcode != 5220) - break; - ItemDefinition.intsStack[iStackCounter++] = WorldMap.anInt2737 != 100 ? 0 : 1; - continue; - } - if (opcode < 5400) { - if (opcode == 5300) { - iStackCounter -= 2; - int i59 = ItemDefinition.intsStack[1 + iStackCounter]; - int l31 = ItemDefinition.intsStack[iStackCounter]; - GameObject.graphicsSettings(false, 3, l31, i59); - ItemDefinition.intsStack[iStackCounter++] = null != TextureOperation30.fullScreenFrame ? 1 : 0; - continue; - } - if (opcode == 5301) { - if (null != TextureOperation30.fullScreenFrame) - GameObject.graphicsSettings(false, Unsorted.anInt2577, -1, -1); - continue; - } - if (5302 == opcode) { - Class106[] aclass106 = Unsorted.method88(); - ItemDefinition.intsStack[iStackCounter++] = aclass106.length; - continue; - } - if (5303 == opcode) { - int i32 = ItemDefinition.intsStack[--iStackCounter]; - Class106[] aclass106_1 = Unsorted.method88(); - ItemDefinition.intsStack[iStackCounter++] = aclass106_1[i32].anInt1447; - ItemDefinition.intsStack[iStackCounter++] = aclass106_1[i32].anInt1449; - continue; - } - if (opcode == 5305) { - int j59 = Unsorted.anInt3071; - int j32 = TextureOperation.anInt2378; - int j71 = -1; - Class106[] aclass106_2 = Unsorted.method88(); - int i80 = 0; - do { - if (aclass106_2.length <= i80) - break; - Class106 class106 = aclass106_2[i80]; - if (j32 == class106.anInt1447 && class106.anInt1449 == j59) { - j71 = i80; - break; - } - i80++; - } while (true); - ItemDefinition.intsStack[iStackCounter++] = j71; - continue; - } - if (opcode == 5306) { - ItemDefinition.intsStack[iStackCounter++] = Class83.getWindowType(); - continue; - } - if (opcode == 5307) { - int k32 = ItemDefinition.intsStack[--iStackCounter]; - if (k32 < 0 || k32 > 2) - k32 = 0; - GameObject.graphicsSettings(false, k32, -1, -1); - continue; - } - if (5308 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt2577; - continue; - } - if (5309 != opcode) - break; - int l32 = ItemDefinition.intsStack[--iStackCounter]; - if (l32 < 0 || l32 > 2) - l32 = 0; - Unsorted.anInt2577 = l32; - Class119.method1730(Class38.gameSignlink); - continue; - } - if (5500 > opcode) { - if (opcode == 5400) { - sStackCounter -= 2; - RSString class94_23 = ItemDefinition.stringsStack[sStackCounter]; - RSString class94_54 = ItemDefinition.stringsStack[sStackCounter - -1]; - int k71 = ItemDefinition.intsStack[--iStackCounter]; - TextureOperation12.outgoingBuffer.putOpcode(117); - TextureOperation12.outgoingBuffer.writeByte(TextureOperation29.method326((byte) 39, class94_23) - (-TextureOperation29.method326((byte) 102, class94_54) + -1)); - TextureOperation12.outgoingBuffer.writeString(class94_23); - TextureOperation12.outgoingBuffer.writeString(class94_54); - TextureOperation12.outgoingBuffer.writeByte(k71); - continue; - } - if (opcode == 5401) { - iStackCounter -= 2; - TextureOperation38.aShortArray3455[ItemDefinition.intsStack[iStackCounter]] = (short) Class56.method1186(ItemDefinition.intsStack[iStackCounter + 1]); - CS2Methods.method28(); - Unsorted.method746((byte) -29); - Class167.method2265(); - WorldListEntry.method1076(); - Unsorted.method1093(false); - continue; - } - if (opcode == 5405) { - iStackCounter -= 2; - int i33 = ItemDefinition.intsStack[iStackCounter]; - int k59 = ItemDefinition.intsStack[1 + iStackCounter]; - if (i33 >= 0 && i33 < 2) - Class58.anIntArrayArrayArray911[i33] = new int[k59 << 1][4]; - continue; - } - if (opcode == 5406) { - iStackCounter -= 7; - int j33 = ItemDefinition.intsStack[iStackCounter]; - int l59 = ItemDefinition.intsStack[1 + iStackCounter] << 1; - int k77 = ItemDefinition.intsStack[iStackCounter - -3]; - int l71 = ItemDefinition.intsStack[2 + iStackCounter]; - int j80 = ItemDefinition.intsStack[4 + iStackCounter]; - int j82 = ItemDefinition.intsStack[6 + iStackCounter]; - int l81 = ItemDefinition.intsStack[5 + iStackCounter]; - if (j33 >= 0 && j33 < 2 && null != Class58.anIntArrayArrayArray911[j33] && l59 >= 0 && Class58.anIntArrayArrayArray911[j33].length > l59) { - Class58.anIntArrayArrayArray911[j33][l59] = (new int[]{ - (Unsorted.bitwiseAnd(0xfffc3b9, l71) >> 14) * 128, k77, 128 * Unsorted.bitwiseAnd(l71, 16383), j82 - }); - Class58.anIntArrayArrayArray911[j33][l59 + 1] = (new int[]{ - 128 * (Unsorted.bitwiseAnd(j80, 0xfffed27) >> 14), l81, 128 * Unsorted.bitwiseAnd(j80, 16383) - }); - } - continue; - } - if (opcode == 5407) { - int k33 = Class58.anIntArrayArrayArray911[ItemDefinition.intsStack[--iStackCounter]].length >> 1; - ItemDefinition.intsStack[iStackCounter++] = k33; - continue; - } - if (opcode == 5411) { - if (TextureOperation30.fullScreenFrame != null) - GameObject.graphicsSettings(false, Unsorted.anInt2577, -1, -1); - if (null == GameShell.frame) - System.exit(0); - //Class99.method1596(RSInterface.method856(), (byte) 126, false); - else - System.exit(0); - continue; - } - if (opcode == 5419) { - RSString class94_24 = TextCore.aClass94_2331; - if (null != Class136.aClass64_1778) { - class94_24 = Class108.method1653(Class136.aClass64_1778.anInt979); - if (Class136.aClass64_1778.anObject974 != null) { - byte[] abyte0 = null; - abyte0 = ((String) Class136.aClass64_1778.anObject974).getBytes(StandardCharsets.ISO_8859_1); - class94_24 = TextureOperation33.bufferToString(abyte0, abyte0.length, 0); - } - } - ItemDefinition.stringsStack[sStackCounter++] = class94_24; - continue; - } - if (opcode == 5420) { - ItemDefinition.intsStack[iStackCounter++] = Signlink.anInt1214 != 3 ? 0 : 1; - continue; - } - if (opcode == 5421) { - if (null != TextureOperation30.fullScreenFrame) - GameObject.graphicsSettings(false, Unsorted.anInt2577, -1, -1); - boolean flag5 = 1 == ItemDefinition.intsStack[--iStackCounter]; - RSString class94_25 = ItemDefinition.stringsStack[--sStackCounter]; - RSString class94_64 = RSString.stringCombiner(new RSString[]{ - RSInterface.method856(), class94_25 - }); - if (null == GameShell.frame && (!flag5 || Signlink.anInt1214 == 3 || !Signlink.osName.startsWith("win") || Class106.paramUserUsingInternetExplorer)) { - Class99.method1596(class94_64, (byte) 127, flag5); - } else { - Unsorted.aBoolean2154 = flag5; - TextureOperation5.aClass94_3295 = class94_64; - AudioThread.aClass64_351 = Class38.gameSignlink.method1452(new String(class94_64.method1568(), StandardCharsets.ISO_8859_1), true); - } - continue; - } - if (5422 == opcode) { - int i72 = ItemDefinition.intsStack[--iStackCounter]; - sStackCounter -= 2; - RSString class94_55 = ItemDefinition.stringsStack[1 + sStackCounter]; - RSString class94_26 = ItemDefinition.stringsStack[sStackCounter]; - if (class94_26.length() > 0) { - if (null == BufferedDataStream.aClass94Array3802) - BufferedDataStream.aClass94Array3802 = new RSString[TextureOperation19.anIntArray3218[Class158.paramGameTypeID]]; - BufferedDataStream.aClass94Array3802[i72] = class94_26; - } - if (class94_55.length() > 0) { - if (Unsorted.aClass94Array45 == null) - Unsorted.aClass94Array45 = new RSString[TextureOperation19.anIntArray3218[Class158.paramGameTypeID]]; - Unsorted.aClass94Array45[i72] = class94_55; - } - continue; - } - if (opcode == 5423) { - continue; - } - if (5424 == opcode) { - iStackCounter -= 11; - InterfaceWidget.anInt3600 = ItemDefinition.intsStack[iStackCounter]; - Unsorted.anInt963 = ItemDefinition.intsStack[iStackCounter - -1]; - MouseListeningClass.anInt1926 = ItemDefinition.intsStack[iStackCounter + 2]; - Class136.anInt1771 = ItemDefinition.intsStack[3 + iStackCounter]; - WorldListEntry.archiveID = ItemDefinition.intsStack[4 + iStackCounter]; - WorldListEntry.anInt1400 = ItemDefinition.intsStack[5 + iStackCounter]; - WorldListEntry.anInt739 = ItemDefinition.intsStack[6 + iStackCounter]; - WorldListEntry.anInt1126 = ItemDefinition.intsStack[7 + iStackCounter]; - WorldListEntry.anInt2937 = ItemDefinition.intsStack[8 + iStackCounter]; - WorldListEntry.anInt3351 = ItemDefinition.intsStack[iStackCounter + 9]; - Class154.anInt1957 = ItemDefinition.intsStack[10 + iStackCounter]; - CacheIndex.spritesIndex.retrieveSpriteFile(WorldListEntry.archiveID); - CacheIndex.spritesIndex.retrieveSpriteFile(WorldListEntry.anInt1400); - CacheIndex.spritesIndex.retrieveSpriteFile(WorldListEntry.anInt739); - CacheIndex.spritesIndex.retrieveSpriteFile(WorldListEntry.anInt1126); - CacheIndex.spritesIndex.retrieveSpriteFile(WorldListEntry.anInt2937); - Unsorted.aBoolean1951 = true; - continue; - } - if (opcode == 5425) { - InvalidateData.method165(); - Unsorted.aBoolean1951 = false; - continue; - } - if (opcode == 5426) { - Class161.anInt2027 = ItemDefinition.intsStack[--iStackCounter]; - continue; - } - if (opcode != 5427) - break; - iStackCounter -= 2; - Class99.anInt1403 = ItemDefinition.intsStack[iStackCounter]; - Class131.anInt1719 = ItemDefinition.intsStack[iStackCounter + 1]; - continue; - } - if (5600 > opcode) { - if (5500 == opcode) { - iStackCounter -= 4; - int l33 = ItemDefinition.intsStack[iStackCounter]; - int l77 = ItemDefinition.intsStack[iStackCounter - -3]; - int j72 = ItemDefinition.intsStack[iStackCounter - -2]; - int i60 = ItemDefinition.intsStack[iStackCounter + 1]; - Class3_Sub20.method390(false, j72, i60, l77, (byte) -128, -Texture.anInt1152 + (0x3fff & l33), ((0xffffe30 & l33) >> 14) - Class131.anInt1716); - continue; - } - if (opcode == 5501) { - iStackCounter -= 4; - int j60 = ItemDefinition.intsStack[1 + iStackCounter]; - int i34 = ItemDefinition.intsStack[iStackCounter]; - int i78 = ItemDefinition.intsStack[iStackCounter - -3]; - int k72 = ItemDefinition.intsStack[iStackCounter + 2]; - Class164_Sub1.method2238(j60, (0x3fff & i34) - Texture.anInt1152, k72, -Class131.anInt1716 + ((0xffff221 & i34) >> 14), i78); - continue; - } - if (opcode == 5502) { - iStackCounter -= 6; - int j34 = ItemDefinition.intsStack[iStackCounter]; - if (j34 >= 2) - throw new RuntimeException(); - NPCDefinition.anInt1252 = j34; - int k60 = ItemDefinition.intsStack[iStackCounter - -1]; - if (1 + k60 >= Class58.anIntArrayArrayArray911[NPCDefinition.anInt1252].length >> 1) - throw new RuntimeException(); - Unsorted.anInt1081 = k60; - Class163_Sub2_Sub1.anInt4020 = 0; - Class134.anInt1759 = ItemDefinition.intsStack[iStackCounter + 2]; - TextureOperation.anInt2383 = ItemDefinition.intsStack[iStackCounter + 3]; - int l72 = ItemDefinition.intsStack[iStackCounter + 4]; - if (2 <= l72) - throw new RuntimeException(); - InterfaceWidget.anInt2293 = l72; - int j78 = ItemDefinition.intsStack[5 + iStackCounter]; - if (Class58.anIntArrayArrayArray911[InterfaceWidget.anInt2293].length >> 1 <= 1 + j78) - throw new RuntimeException(); - Class39.anInt670 = j78; - Class133.anInt1753 = 3; - continue; - } - if (opcode == 5503) { - Class3_Sub28_Sub5.method560(); - continue; - } - if (5504 == opcode) { - iStackCounter -= 2; - Unsorted.anInt2309 = ItemDefinition.intsStack[iStackCounter]; - GraphicDefinition.CAMERA_DIRECTION = ItemDefinition.intsStack[iStackCounter + 1]; - if (Class133.anInt1753 == 2) { - TextureOperation28.anInt3315 = GraphicDefinition.CAMERA_DIRECTION; - Class139.anInt1823 = Unsorted.anInt2309; - } - Unsorted.method1098((byte) -74); - continue; - } - if (opcode == 5505) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt2309; - continue; - } - if (5506 != opcode) - break; - ItemDefinition.intsStack[iStackCounter++] = GraphicDefinition.CAMERA_DIRECTION; - continue; - } - if (opcode >= 5700) { - if (6100 > opcode) { - if (opcode == 6001) { - int k34 = ItemDefinition.intsStack[--iStackCounter]; - if (k34 < 1) - k34 = 1; - if (k34 > 4) - k34 = 4; - Unsorted.anInt3625 = k34; - if (!HDToolKit.highDetail || !Class106.aBoolean1441) { - if (Unsorted.anInt3625 == 1) - Class51.method1137(0.9F); - if (Unsorted.anInt3625 == 2) - Class51.method1137(0.8F); - if (3 == Unsorted.anInt3625) - Class51.method1137(0.7F); - if (Unsorted.anInt3625 == 4) - Class51.method1137(0.6F); - } - if (HDToolKit.highDetail) { - TextureOperation31.method236(); - if (!Class106.aBoolean1441) - Class84.method1417(); - } - Unsorted.method746((byte) -29); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6002) { - Class25.method957(1 == ItemDefinition.intsStack[--iStackCounter]); - Class3_Sub10.method139(66); - Class84.method1417(); - Unsorted.method792(); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6003) { - Unsorted.aBoolean3604 = ItemDefinition.intsStack[--iStackCounter] == 1; - Unsorted.method792(); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6005) { - KeyboardListener.aBoolean1905 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class84.method1417(); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6006) { - Class25.aBoolean488 = ItemDefinition.intsStack[--iStackCounter] == 1; - ((Class102) Class51.anInterface2_838).method1616(!Class25.aBoolean488); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6007) { - RSInterface.aBoolean236 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6008) { - WorldListEntry.aBoolean2623 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6009) { - Unsorted.aBoolean3275 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6010) { - Class140_Sub6.aBoolean2910 = 1 == ItemDefinition.intsStack[--iStackCounter]; - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6011) { - int l34 = ItemDefinition.intsStack[--iStackCounter]; - if (l34 < 0 || l34 > 2) - l34 = 0; - Unsorted.anInt1137 = l34; - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (6012 == opcode) { - if (HDToolKit.highDetail) - Unsorted.method551(0, 0); - Class106.aBoolean1441 = ItemDefinition.intsStack[--iStackCounter] == 1; - if (HDToolKit.highDetail && Class106.aBoolean1441) { - Class51.method1137(0.7F); - } else { - if (Unsorted.anInt3625 == 1) - Class51.method1137(0.9F); - if (Unsorted.anInt3625 == 2) - Class51.method1137(0.8F); - if (Unsorted.anInt3625 == 3) - Class51.method1137(0.7F); - if (Unsorted.anInt3625 == 4) - Class51.method1137(0.6F); - } - Class84.method1417(); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6014) { - Class128.aBoolean1685 = ItemDefinition.intsStack[--iStackCounter] == 1; - if (HDToolKit.highDetail) - Class84.method1417(); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6015) { - Class38.aBoolean661 = ItemDefinition.intsStack[--iStackCounter] == 1; - if (HDToolKit.highDetail) - TextureOperation31.method236(); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (6016 == opcode) { - int i35 = ItemDefinition.intsStack[--iStackCounter]; - if (HDToolKit.highDetail) - Class3_Sub28_Sub5.forceReplaceCanvasEnable = true; - if (0 > i35 || i35 > 2) - i35 = 0; - Unsorted.anInt3671 = i35; - continue; - } - if (opcode == 6017) { - TextureOperation17.stereoSound = ItemDefinition.intsStack[--iStackCounter] == 1; - GameShell.method34(); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6018) { - int j35 = ItemDefinition.intsStack[--iStackCounter]; - if (j35 < 0) - j35 = 0; - if (j35 > 127) - j35 = 127; - AudioHandler.soundEffectVolume = j35; - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6019) { - int k35 = ItemDefinition.intsStack[--iStackCounter]; - if (k35 < 0) - k35 = 0; - if (k35 > 255) - k35 = 255; - if (AudioHandler.musicVolume != k35) { - if (AudioHandler.musicVolume == 0 && AudioHandler.currentTrack != -1) { - Class70.method1285(CacheIndex.musicIndex, AudioHandler.currentTrack, k35); - AudioHandler.musicEffectPlaying = false; - } else if (k35 == 0) { - GameObject.method1870(); - AudioHandler.musicEffectPlaying = false; - } else { - LinkableRSString.method736(k35, 115); - } - AudioHandler.musicVolume = k35; - } - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6020) { - int l35 = ItemDefinition.intsStack[--iStackCounter]; - if (l35 < 0) - l35 = 0; - if (127 < l35) - l35 = 127; - Sprites.ambientVolume = l35; - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - continue; - } - if (opcode == 6021) { - Unsorted.aBoolean1084 = ItemDefinition.intsStack[--iStackCounter] == 1; - Unsorted.method792(); - continue; - } - if (opcode == 6023) { - int i36 = ItemDefinition.intsStack[--iStackCounter]; - if (0 > i36) - i36 = 0; - if (i36 > 2) - i36 = 2; - boolean flag6 = false; - if (96 > Class3_Sub24_Sub3.maxClientMemory) { - flag6 = true; - i36 = 0; - } - Class127_Sub1.method1758(i36); - Class119.method1730(Class38.gameSignlink); - aBoolean2705 = false; - ItemDefinition.intsStack[iStackCounter++] = flag6 ? 0 : 1; - continue; - } - if (opcode == 6024) { - int j36 = ItemDefinition.intsStack[--iStackCounter]; - if (j36 < 0 || 2 < j36) - j36 = 0; - Class3_Sub28_Sub9.anInt3622 = j36; - Class119.method1730(Class38.gameSignlink); - continue; - } - if (opcode != 6028) - break; - Class163_Sub3.aBoolean3004 = ItemDefinition.intsStack[--iStackCounter] != 0; - Class119.method1730(Class38.gameSignlink); - continue; - } - if (opcode < 6200) { - if (opcode == 6101) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt3625; - continue; - } - if (opcode == 6102) { - ItemDefinition.intsStack[iStackCounter++] = NPC.method1986(109) ? 1 : 0; - continue; - } - if (opcode == 6103) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.aBoolean3604 ? 1 : 0; - continue; - } - if (opcode == 6105) { - ItemDefinition.intsStack[iStackCounter++] = KeyboardListener.aBoolean1905 ? 1 : 0; - continue; - } - if (opcode == 6106) { - ItemDefinition.intsStack[iStackCounter++] = Class25.aBoolean488 ? 1 : 0; - continue; - } - if (opcode == 6107) { - ItemDefinition.intsStack[iStackCounter++] = RSInterface.aBoolean236 ? 1 : 0; - continue; - } - if (opcode == 6108) { - ItemDefinition.intsStack[iStackCounter++] = WorldListEntry.aBoolean2623 ? 1 : 0; - continue; - } - if (6109 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.aBoolean3275 ? 1 : 0; - continue; - } - if (opcode == 6110) { - ItemDefinition.intsStack[iStackCounter++] = Class140_Sub6.aBoolean2910 ? 1 : 0; - continue; - } - if (opcode == 6111) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt1137; - continue; - } - if (6112 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Class106.aBoolean1441 ? 1 : 0; - continue; - } - if (6114 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Class128.aBoolean1685 ? 1 : 0; - continue; - } - if (opcode == 6115) { - ItemDefinition.intsStack[iStackCounter++] = Class38.aBoolean661 ? 1 : 0; - continue; - } - if (opcode == 6116) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt3671; - continue; - } - if (6117 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = TextureOperation17.stereoSound ? 1 : 0; - continue; - } - if (opcode == 6118) { - ItemDefinition.intsStack[iStackCounter++] = AudioHandler.soundEffectVolume; - continue; - } - if (6119 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = AudioHandler.musicVolume; - continue; - } - if (opcode == 6120) { - ItemDefinition.intsStack[iStackCounter++] = Sprites.ambientVolume; - continue; - } - if (opcode == 6121) { - if (HDToolKit.highDetail) - ItemDefinition.intsStack[iStackCounter++] = HDToolKit.supportMultisample ? 1 : 0; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 6123) { - ItemDefinition.intsStack[iStackCounter++] = Class127_Sub1.method1757(); - continue; - } - if (opcode == 6124) { - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub28_Sub9.anInt3622; - continue; - } - if (opcode != 6128) - break; - ItemDefinition.intsStack[iStackCounter++] = Class163_Sub3.aBoolean3004 ? 1 : 0; - continue; - } - if (opcode >= 6300) { - if (opcode < 6400) { - if (opcode == 6300) { - ItemDefinition.intsStack[iStackCounter++] = (int) (TimeUtils.time() / 60000L); - continue; - } - if (opcode == 6301) { - ItemDefinition.intsStack[iStackCounter++] = -11745 + (int) (TimeUtils.time() / 0x5265c00L); - continue; - } - if (opcode == 6302) { - iStackCounter -= 3; - int i73 = ItemDefinition.intsStack[iStackCounter + 2]; - int l60 = ItemDefinition.intsStack[iStackCounter - -1]; - int k36 = ItemDefinition.intsStack[iStackCounter]; - Class3_Sub28_Sub9.aCalendar3616.clear(); - Class3_Sub28_Sub9.aCalendar3616.set(Calendar.HOUR_OF_DAY, 12); - Class3_Sub28_Sub9.aCalendar3616.set(i73, l60, k36); - ItemDefinition.intsStack[iStackCounter++] = -11745 + (int) (Class3_Sub28_Sub9.aCalendar3616.getTime().getTime() / 0x5265c00L); - continue; - } - if (6303 == opcode) { - Class3_Sub28_Sub9.aCalendar3616.clear(); - Class3_Sub28_Sub9.aCalendar3616.setTime(new Date(TimeUtils.time())); - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub28_Sub9.aCalendar3616.get(Calendar.YEAR); - continue; - } - if (opcode != 6304) - break; - boolean flag7 = true; - int l36 = ItemDefinition.intsStack[--iStackCounter]; - if (l36 >= 0) { - if (l36 >= 1582) { - if (l36 % 4 == 0) { - if (l36 % 100 != 0) - flag7 = true; - else if (0 != l36 % 400) - flag7 = false; - } else { - flag7 = false; - } - } else { - flag7 = l36 % 4 == 0; - } - } else { - flag7 = (1 + l36) % 4 == 0; - } - ItemDefinition.intsStack[iStackCounter++] = flag7 ? 1 : 0; - continue; - } - if (opcode >= 6500) { - if (opcode < 6600) { - if (opcode == 6500) { - if (Class143.gameStage != 10 || Class163_Sub1_Sub1.adminLoginStage != 0 || 0 != LoginHandler.loginStage || 0 != Unsorted.registryStage) - ItemDefinition.intsStack[iStackCounter++] = 1; - else - ItemDefinition.intsStack[iStackCounter++] = Class121.method1735() == -1 ? 0 : 1; - continue; - } - if (opcode == 6501) { - WorldListEntry worldEntry = Class140_Sub2.method1953(); - if (worldEntry == null) { - ItemDefinition.intsStack[iStackCounter++] = -1; - ItemDefinition.intsStack[iStackCounter++] = 0; - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - ItemDefinition.intsStack[iStackCounter++] = 0; - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - ItemDefinition.intsStack[iStackCounter++] = 0; - } else { - ItemDefinition.intsStack[iStackCounter++] = worldEntry.worldId; - ItemDefinition.intsStack[iStackCounter++] = worldEntry.settings; - ItemDefinition.stringsStack[sStackCounter++] = worldEntry.activity; - WorldListCountry class26 = worldEntry.method1078(60); - ItemDefinition.intsStack[iStackCounter++] = class26.flagId; - ItemDefinition.stringsStack[sStackCounter++] = class26.name; - ItemDefinition.intsStack[iStackCounter++] = worldEntry.anInt722; - } - continue; - } - if (opcode == 6502) { - WorldListEntry class44_sub1_1 = ItemDefinition.method1107(5422); - if (null == class44_sub1_1) { - ItemDefinition.intsStack[iStackCounter++] = -1; - ItemDefinition.intsStack[iStackCounter++] = 0; - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - ItemDefinition.intsStack[iStackCounter++] = 0; - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - ItemDefinition.intsStack[iStackCounter++] = 0; - } else { - ItemDefinition.intsStack[iStackCounter++] = class44_sub1_1.worldId; - ItemDefinition.intsStack[iStackCounter++] = class44_sub1_1.settings; - ItemDefinition.stringsStack[sStackCounter++] = class44_sub1_1.activity; - WorldListCountry class26_1 = class44_sub1_1.method1078(70); - ItemDefinition.intsStack[iStackCounter++] = class26_1.flagId; - ItemDefinition.stringsStack[sStackCounter++] = class26_1.name; - ItemDefinition.intsStack[iStackCounter++] = class44_sub1_1.anInt722; - } - continue; - } - if (opcode == 6503) { - int i37 = ItemDefinition.intsStack[--iStackCounter]; - if (Class143.gameStage != 10 || Class163_Sub1_Sub1.adminLoginStage != 0 || LoginHandler.loginStage != 0 || Unsorted.registryStage != 0) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = WaterfallShader.method1627(i37, (byte) -7) ? 1 : 0; - continue; - } - if (opcode == 6504) { - Unsorted.anInt2148 = ItemDefinition.intsStack[--iStackCounter]; - Class119.method1730(Class38.gameSignlink); - continue; - } - if (6505 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt2148; - continue; - } - if (opcode == 6506) { - int j37 = ItemDefinition.intsStack[--iStackCounter]; - WorldListEntry class44_sub1_2 = Class3_Sub8.getWorld(120, j37); - if (class44_sub1_2 == null) { - ItemDefinition.intsStack[iStackCounter++] = -1; - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - ItemDefinition.intsStack[iStackCounter++] = 0; - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - ItemDefinition.intsStack[iStackCounter++] = 0; - } else { - ItemDefinition.intsStack[iStackCounter++] = class44_sub1_2.settings; - ItemDefinition.stringsStack[sStackCounter++] = class44_sub1_2.activity; - WorldListCountry class26_2 = class44_sub1_2.method1078(-87); - ItemDefinition.intsStack[iStackCounter++] = class26_2.flagId; - ItemDefinition.stringsStack[sStackCounter++] = class26_2.name; - ItemDefinition.intsStack[iStackCounter++] = class44_sub1_2.anInt722; - } - continue; - } - if (opcode != 6507) - break; - iStackCounter -= 4; - int j73 = ItemDefinition.intsStack[iStackCounter + 2]; - int k37 = ItemDefinition.intsStack[iStackCounter]; - boolean flag11 = ItemDefinition.intsStack[iStackCounter - -3] == 1; - boolean flag8 = ItemDefinition.intsStack[1 + iStackCounter] == 1; - Class134.method1808(j73, flag8, k37, flag11); - continue; - } - if (opcode >= 6700) - break; - if (6600 == opcode) { - AudioThread.aBoolean346 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class119.method1730(Class38.gameSignlink); - continue; - } - if (opcode != 6601) - break; - ItemDefinition.intsStack[iStackCounter++] = AudioThread.aBoolean346 ? 1 : 0; - continue; - } - if (6405 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.method1088(false) ? 1 : 0; - continue; - } - if (opcode != 6406) - break; - ItemDefinition.intsStack[iStackCounter++] = Class159.method2194() ? 1 : 0; - continue; - } - if (opcode == 6200) { - iStackCounter -= 2; - aShort1444 = (short) ItemDefinition.intsStack[iStackCounter]; - if (0 >= aShort1444) - aShort1444 = 256; - aShort3052 = (short) ItemDefinition.intsStack[1 + iStackCounter]; - if (aShort3052 <= 0) - aShort3052 = 205; - continue; - } - if (opcode == 6201) { - iStackCounter -= 2; - Unsorted.aShort46 = (short) ItemDefinition.intsStack[iStackCounter]; - if (Unsorted.aShort46 <= 0) - Unsorted.aShort46 = 256; - ObjectDefinition.aShort1535 = (short) ItemDefinition.intsStack[1 + iStackCounter]; - if (ObjectDefinition.aShort1535 <= 0) - ObjectDefinition.aShort1535 = 320; - continue; - } - if (opcode == 6202) { - iStackCounter -= 4; - aShort3241 = (short) ItemDefinition.intsStack[iStackCounter]; - if (aShort3241 <= 0) - aShort3241 = 1; - PacketParser.aShort83 = (short) ItemDefinition.intsStack[1 + iStackCounter]; - if (PacketParser.aShort83 > 0) { - if (aShort3241 > PacketParser.aShort83) - PacketParser.aShort83 = aShort3241; - } else { - PacketParser.aShort83 = 32767; - } - ItemDefinition.aShort505 = (short) ItemDefinition.intsStack[2 + iStackCounter]; - if (ItemDefinition.aShort505 <= 0) - ItemDefinition.aShort505 = 1; - TextureOperation18.aShort4038 = (short) ItemDefinition.intsStack[iStackCounter - -3]; - if (TextureOperation18.aShort4038 > 0) { - if (ItemDefinition.aShort505 > TextureOperation18.aShort4038) - TextureOperation18.aShort4038 = ItemDefinition.aShort505; - } else { - TextureOperation18.aShort4038 = 32767; - } - continue; - } - if (opcode == 6203) { - Class65.method1239(Unsorted.aClass11_2091.width, 81, 0, Unsorted.aClass11_2091.height, 0, false); - ItemDefinition.intsStack[iStackCounter++] = Class96.anInt1358; - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt31; - continue; - } - if (6204 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.aShort46; - ItemDefinition.intsStack[iStackCounter++] = ObjectDefinition.aShort1535; - continue; - } - if (opcode != 6205) - break; - ItemDefinition.intsStack[iStackCounter++] = aShort1444; - ItemDefinition.intsStack[iStackCounter++] = aShort3052; - continue; - } - if (opcode == 5600) { - sStackCounter -= 2; - RSString class94_27 = ItemDefinition.stringsStack[sStackCounter]; - RSString class94_56 = ItemDefinition.stringsStack[sStackCounter + 1]; - int k73 = ItemDefinition.intsStack[--iStackCounter]; - if (Class143.gameStage == 10 && Class163_Sub1_Sub1.adminLoginStage == 0 && LoginHandler.loginStage == 0 && Unsorted.registryStage == 0 && Class43.worldListStage == 0) - Class131.method1793(class94_27, class94_56, k73); - continue; - } - if (opcode == 5601) { - Class110.method1681(-1); - continue; - } - if (opcode == 5602) { - if (0 == LoginHandler.loginStage) - Client.messageToDisplay = -2; - continue; - } - if (opcode == 5603) { - iStackCounter -= 4; - if (Class143.gameStage == 10 && 0 == Class163_Sub1_Sub1.adminLoginStage && LoginHandler.loginStage == 0 && Unsorted.registryStage == 0 && Class43.worldListStage == 0) - sendRegistryRequest(ItemDefinition.intsStack[iStackCounter - -2], ItemDefinition.intsStack[iStackCounter + 3], ItemDefinition.intsStack[iStackCounter], ItemDefinition.intsStack[iStackCounter + 1]); - continue; - } - if (opcode == 5604) { - sStackCounter--; - if (Class143.gameStage == 10 && Class163_Sub1_Sub1.adminLoginStage == 0 && LoginHandler.loginStage == 0 && Unsorted.registryStage == 0 && Class43.worldListStage == 0) { - Class40.method1041(ItemDefinition.stringsStack[sStackCounter].toLong(), ItemDefinition.stringsStack[sStackCounter]); - continue; - } - } - if (opcode == 5605) { - iStackCounter -= 4; - sStackCounter -= 2; - if (Class143.gameStage == 10 && 0 == Class163_Sub1_Sub1.adminLoginStage && LoginHandler.loginStage == 0 && Unsorted.registryStage == 0 && Class43.worldListStage == 0) - InterfaceWidget.a(ItemDefinition.intsStack[iStackCounter], ItemDefinition.intsStack[iStackCounter - -3], ItemDefinition.intsStack[1 + iStackCounter], ItemDefinition.stringsStack[1 + sStackCounter], ItemDefinition.stringsStack[sStackCounter].toLong(), ItemDefinition.intsStack[2 + iStackCounter], ItemDefinition.stringsStack[sStackCounter]); - continue; - } - if (opcode == 5606) { - if (Unsorted.registryStage == 0) - Unsorted.anInt1711 = -2; - continue; - } - if (opcode == 5607) { - ItemDefinition.intsStack[iStackCounter++] = Client.messageToDisplay; - continue; - } - if (opcode == 5608) { - ItemDefinition.intsStack[iStackCounter++] = TextureOperation25.anInt3413; - continue; - } - if (5609 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt1711; - continue; - } - if (opcode == 5610) { - for (int l37 = 0; l37 < 5; l37++) - ItemDefinition.stringsStack[sStackCounter++] = TextureOperation29.aClass94Array3391.length <= l37 ? TextCore.aClass94_2331 : TextureOperation29.aClass94Array3391[l37].longToRSString(); - - TextureOperation29.aClass94Array3391 = null; - continue; - } - if (opcode != 5611) - break; - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub26.anInt2561; - continue; - } - if (4500 != opcode) - break; - iStackCounter -= 2; - int i38 = ItemDefinition.intsStack[iStackCounter]; - int i61 = ItemDefinition.intsStack[iStackCounter - -1]; - Class3_Sub28_Sub9 class3_sub28_sub9_1 = LinkedList.method1210(i61); - if (!class3_sub28_sub9_1.method585()) - ItemDefinition.intsStack[iStackCounter++] = StructDefinitionProvider.provide(i38).getInt(i61, class3_sub28_sub9_1.anInt3614); - else - ItemDefinition.stringsStack[sStackCounter++] = StructDefinitionProvider.provide(i38).getString(i61, class3_sub28_sub9_1.aClass94_3619); - continue; - } - if (opcode != 4400) - break; - iStackCounter -= 2; - int j61 = ItemDefinition.intsStack[iStackCounter - -1]; - int j38 = ItemDefinition.intsStack[iStackCounter]; - Class3_Sub28_Sub9 class3_sub28_sub9_2 = LinkedList.method1210(j61); - if (!class3_sub28_sub9_2.method585()) - ItemDefinition.intsStack[iStackCounter++] = ObjectDefinition.getObjectDefinition(j38).method1691(class3_sub28_sub9_2.anInt3614, j61, (byte) 105); - else - ItemDefinition.stringsStack[sStackCounter++] = ObjectDefinition.getObjectDefinition(j38).method1698(class3_sub28_sub9_2.aClass94_3619, j61); - continue; - } - if (opcode == 4200) { - int k38 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = ItemDefinition.getItemDefinition(k38).name; - continue; - } - if (opcode == 4201) { - iStackCounter -= 2; - int l38 = ItemDefinition.intsStack[iStackCounter]; - int k61 = ItemDefinition.intsStack[iStackCounter - -1]; - ItemDefinition class48_2 = ItemDefinition.getItemDefinition(l38); - if (k61 < 1 || k61 > 5 || class48_2.groundOptions[-1 + k61] == null) - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - else - ItemDefinition.stringsStack[sStackCounter++] = class48_2.groundOptions[k61 - 1]; - continue; - } - if (opcode == 4202) { - iStackCounter -= 2; - int i39 = ItemDefinition.intsStack[iStackCounter]; - int l61 = ItemDefinition.intsStack[iStackCounter + 1]; - ItemDefinition class48_3 = ItemDefinition.getItemDefinition(i39); - if (l61 >= 1 && l61 <= 5 && null != class48_3.inventoryOptions[l61 + -1]) { - ItemDefinition.stringsStack[sStackCounter++] = class48_3.inventoryOptions[-1 + l61]; - } else { - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - } - continue; - } - if (opcode == 4203) { - int j39 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.getItemDefinition(j39).value; - continue; - } - if (opcode == 4204) { - int k39 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.getItemDefinition(k39).stackingType == 1 ? 1 : 0; - continue; - } - if (4205 == opcode) { - int l39 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition class48 = ItemDefinition.getItemDefinition(l39); - if (-1 == class48.anInt791 && class48.noteID >= 0) - ItemDefinition.intsStack[iStackCounter++] = class48.noteID; - else - ItemDefinition.intsStack[iStackCounter++] = l39; - continue; - } - if (opcode == 4206) { - int i40 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition class48_1 = ItemDefinition.getItemDefinition(i40); - if (0 <= class48_1.anInt791 && class48_1.noteID >= 0) - ItemDefinition.intsStack[iStackCounter++] = class48_1.noteID; - else - ItemDefinition.intsStack[iStackCounter++] = i40; - continue; - } - if (opcode == 4207) { - int j40 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.getItemDefinition(j40).membersItem ? 1 : 0; - continue; - } - if (opcode == 4208) { - iStackCounter -= 2; - int k40 = ItemDefinition.intsStack[iStackCounter]; - int i62 = ItemDefinition.intsStack[iStackCounter - -1]; - Class3_Sub28_Sub9 class3_sub28_sub9_3 = LinkedList.method1210(i62); - if (class3_sub28_sub9_3.method585()) - ItemDefinition.stringsStack[sStackCounter++] = ItemDefinition.getItemDefinition(k40).method1105(class3_sub28_sub9_3.aClass94_3619, i62); - else - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.getItemDefinition(k40).method1115(class3_sub28_sub9_3.anInt3614, -119, i62); - continue; - } - if (4210 == opcode) { - RSString class94_28 = ItemDefinition.stringsStack[--sStackCounter]; - int j62 = ItemDefinition.intsStack[--iStackCounter]; - NPCDefinition.method1480(j62 == 1, class94_28); - ItemDefinition.intsStack[iStackCounter++] = Unsorted.anInt952; - continue; - } - if (opcode == 4211) { - if (null == Class99.aShortArray1398 || Class140_Sub4.anInt2756 >= Unsorted.anInt952) - ItemDefinition.intsStack[iStackCounter++] = -1; - else - ItemDefinition.intsStack[iStackCounter++] = Unsorted.bitwiseAnd(Class99.aShortArray1398[Class140_Sub4.anInt2756++], 65535); - continue; - } - if (4212 != opcode) - break; - Class140_Sub4.anInt2756 = 0; - continue; - } - if (4100 == opcode) { - RSString class94_29 = ItemDefinition.stringsStack[--sStackCounter]; - int k62 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = RSString.stringCombiner(new RSString[]{ - class94_29, RSString.stringAnimator(k62) - }); - continue; - } - if (opcode == 4101) { - sStackCounter -= 2; - RSString class94_57 = ItemDefinition.stringsStack[sStackCounter + 1]; - RSString class94_30 = ItemDefinition.stringsStack[sStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = RSString.stringCombiner(new RSString[]{ - class94_30, class94_57 - }); - continue; - } - if (4102 == opcode) { - RSString class94_31 = ItemDefinition.stringsStack[--sStackCounter]; - int l62 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = RSString.stringCombiner(new RSString[]{ - class94_31, LinkedList.method1218(l62) - }); - continue; - } - if (opcode == 4103) { - RSString class94_32 = ItemDefinition.stringsStack[--sStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = class94_32.toLowercase(); - continue; - } - if (4104 == opcode) { - int l40 = ItemDefinition.intsStack[--iStackCounter]; - long l63 = 0xec44e2dc00L + (long) l40 * 0x5265c00L; - Class3_Sub28_Sub9.aCalendar3616.setTime(new Date(l63)); - int k78 = Class3_Sub28_Sub9.aCalendar3616.get(Calendar.DATE); - int k80 = Class3_Sub28_Sub9.aCalendar3616.get(Calendar.MONTH); - int i82 = Class3_Sub28_Sub9.aCalendar3616.get(Calendar.YEAR); - ItemDefinition.stringsStack[sStackCounter++] = RSString.stringCombiner(new RSString[]{ - RSString.stringAnimator(k78), TextCore.aClass94_1326, TextCore.MonthsOfTheYear[k80], TextCore.aClass94_1326, RSString.stringAnimator(i82) - }); - continue; - } - if (4105 == opcode) { - sStackCounter -= 2; - RSString class94_58 = ItemDefinition.stringsStack[sStackCounter + 1]; - RSString class94_33 = ItemDefinition.stringsStack[sStackCounter]; - if (Class102.player.class52 == null || !Class102.player.class52.aBoolean864) - ItemDefinition.stringsStack[sStackCounter++] = class94_33; - else - ItemDefinition.stringsStack[sStackCounter++] = class94_58; - continue; - } - if (opcode == 4106) { - int i41 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = RSString.stringAnimator(i41); - continue; - } - if (opcode == 4107) { - sStackCounter -= 2; - ItemDefinition.intsStack[iStackCounter++] = ItemDefinition.stringsStack[sStackCounter].method1546(ItemDefinition.stringsStack[sStackCounter - -1]); - continue; - } - if (4108 == opcode) { - RSString class94_34 = ItemDefinition.stringsStack[--sStackCounter]; - iStackCounter -= 2; - int l73 = ItemDefinition.intsStack[iStackCounter - -1]; - int i63 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = AtmosphereParser.method1430(l73).method684(class94_34, i63); - continue; - } - if (opcode == 4109) { - iStackCounter -= 2; - RSString class94_35 = ItemDefinition.stringsStack[--sStackCounter]; - int i74 = ItemDefinition.intsStack[1 + iStackCounter]; - int j63 = ItemDefinition.intsStack[iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = AtmosphereParser.method1430(i74).method680(class94_35, j63); - continue; - } - if (opcode == 4110) { - sStackCounter -= 2; - RSString class94_36 = ItemDefinition.stringsStack[sStackCounter]; - RSString class94_59 = ItemDefinition.stringsStack[sStackCounter - -1]; - if (1 == ItemDefinition.intsStack[--iStackCounter]) - ItemDefinition.stringsStack[sStackCounter++] = class94_36; - else - ItemDefinition.stringsStack[sStackCounter++] = class94_59; - continue; - } - if (4111 == opcode) { - RSString class94_37 = ItemDefinition.stringsStack[--sStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = Font.method686(class94_37); - continue; - } - if (4112 == opcode) { - RSString class94_38 = ItemDefinition.stringsStack[--sStackCounter]; - int k63 = ItemDefinition.intsStack[--iStackCounter]; - if (k63 == -1) - throw new RuntimeException("null char"); - ItemDefinition.stringsStack[sStackCounter++] = class94_38.method1548(k63); - continue; - } - if (opcode == 4113) { - int j41 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class164_Sub2.method2248(j41) ? 1 : 0; - continue; - } - if (opcode == 4114) { - int k41 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class44.method1066(k41) ? 1 : 0; - continue; - } - if (opcode == 4115) { - int l41 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub24_Sub4.method487(l41, (byte) -85) ? 1 : 0; - continue; - } - if (4116 == opcode) { - int i42 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = Class3_Sub28_Sub3.method544(i42) ? 1 : 0; - continue; - } - if (opcode == 4117) { - RSString class94_39 = ItemDefinition.stringsStack[--sStackCounter]; - if (class94_39 != null) - ItemDefinition.intsStack[iStackCounter++] = class94_39.length(); - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 4118) { - iStackCounter -= 2; - RSString class94_40 = ItemDefinition.stringsStack[--sStackCounter]; - int i64 = ItemDefinition.intsStack[iStackCounter]; - int j74 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = class94_40.substring(i64, j74, 0); - continue; - } - if (opcode == 4119) { - RSString class94_41 = ItemDefinition.stringsStack[--sStackCounter]; - RSString class94_60 = Unsorted.emptyString(class94_41.length()); - boolean flag9 = false; - for (int l78 = 0; class94_41.length() > l78; l78++) { - int l80 = class94_41.charAt(l78, (byte) -40); - if (l80 == 60) { - flag9 = true; - continue; - } - if (l80 == 62) { - flag9 = false; - } else if (!flag9) - class94_60.appendCharacter(l80); - } - - class94_60.method1576(); - ItemDefinition.stringsStack[sStackCounter++] = class94_60; - continue; - } - if (opcode == 4120) { - iStackCounter -= 2; - RSString class94_42 = ItemDefinition.stringsStack[--sStackCounter]; - int j64 = ItemDefinition.intsStack[iStackCounter]; - int k74 = ItemDefinition.intsStack[1 + iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = class94_42.method1555(j64, k74); - continue; - } - if (opcode == 4121) { - sStackCounter -= 2; - RSString class94_43 = ItemDefinition.stringsStack[sStackCounter]; - RSString class94_61 = ItemDefinition.stringsStack[1 + sStackCounter]; - int l74 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = class94_43.method1566(class94_61, l74); - continue; - } - if (opcode == 4122) { - int j42 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = TextureOperation25.method332(2, j42); - continue; - } - if (opcode == 4123) { - int k42 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.intsStack[iStackCounter++] = ClientErrorException.method2287(k42, (byte) 59); - continue; - } - if (opcode != 4124) - break; - boolean flag1 = ItemDefinition.intsStack[--iStackCounter] != 0; - int k64 = ItemDefinition.intsStack[--iStackCounter]; - ItemDefinition.stringsStack[sStackCounter++] = Class3_Sub23.method407(Class3_Sub20.paramLanguage, flag1, 0, k64); - continue; - } - RSInterface class11_8 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - if (opcode == 2800) { - ItemDefinition.intsStack[iStackCounter++] = Client.method44(class11_8).method101(); - continue; - } - if (opcode == 2801) { - int l64 = ItemDefinition.intsStack[--iStackCounter]; - l64--; - if (class11_8.aClass94Array171 != null && class11_8.aClass94Array171.length > l64 && null != class11_8.aClass94Array171[l64]) - ItemDefinition.stringsStack[sStackCounter++] = class11_8.aClass94Array171[l64]; - else - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - continue; - } - if (opcode != 2802) - break; - if (class11_8.aClass94_277 != null) - ItemDefinition.stringsStack[sStackCounter++] = class11_8.aClass94_277; - else - ItemDefinition.stringsStack[sStackCounter++] = TextCore.aClass94_2331; - continue; - } - if (opcode == 2700) { - RSInterface class11_9 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - ItemDefinition.intsStack[iStackCounter++] = class11_9.anInt192; - continue; - } - if (opcode == 2701) { - RSInterface class11_10 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - if (-1 != class11_10.anInt192) - ItemDefinition.intsStack[iStackCounter++] = class11_10.anInt271; - else - ItemDefinition.intsStack[iStackCounter++] = 0; - continue; - } - if (opcode == 2702) { - int l42 = ItemDefinition.intsStack[--iStackCounter]; - Class3_Sub31 class3_sub31 = TextureOperation23.aHashTable_3208.get(l42); - if (class3_sub31 == null) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = 1; - continue; - } - if (opcode == 2703) { - RSInterface class11_11 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - if (null == class11_11.aClass11Array262) { - ItemDefinition.intsStack[iStackCounter++] = 0; - } else { - int i65 = class11_11.aClass11Array262.length; - int i75 = 0; - do { - if (class11_11.aClass11Array262.length <= i75) - break; - if (null == class11_11.aClass11Array262[i75]) { - i65 = i75; - break; - } - i75++; - } while (true); - ItemDefinition.intsStack[iStackCounter++] = i65; - } - continue; - } - if (opcode != 2704 && 2705 != opcode) - break; - iStackCounter -= 2; - int i43 = ItemDefinition.intsStack[iStackCounter]; - int j65 = ItemDefinition.intsStack[iStackCounter + 1]; - Class3_Sub31 class3_sub31_1 = TextureOperation23.aHashTable_3208.get(i43); - if (class3_sub31_1 == null || class3_sub31_1.anInt2602 != j65) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = 1; - continue; - } - RSInterface class11_12 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - if (2600 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt247; - continue; - } - if (opcode == 2601) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt208; - continue; - } - if (opcode == 2602) { - ItemDefinition.stringsStack[sStackCounter++] = class11_12.text; - continue; - } - if (opcode == 2603) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt240; - continue; - } - if (opcode == 2604) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt252; - continue; - } - if (opcode == 2605) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt164; - continue; - } - if (opcode == 2606) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt182; - continue; - } - if (opcode == 2607) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt280; - continue; - } - if (2608 == opcode) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt308; - continue; - } - if (opcode == 2609) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt223; - continue; - } - if (opcode == 2610) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt258; - continue; - } - if (opcode == 2611) { - ItemDefinition.intsStack[iStackCounter++] = class11_12.anInt264; - continue; - } - if (2612 != opcode) - break; - ItemDefinition.intsStack[iStackCounter++] = class11_12.spriteArchiveId; - continue; - } - RSInterface class11_13 = flag ? Class164.aClass11_2055 : aClass11_1749; - if (opcode == 1700) { - ItemDefinition.intsStack[iStackCounter++] = class11_13.anInt192; - continue; - } - if (1701 == opcode) { - if (class11_13.anInt192 == -1) - ItemDefinition.intsStack[iStackCounter++] = 0; - else - ItemDefinition.intsStack[iStackCounter++] = class11_13.anInt271; - continue; - } - if (opcode != 1702) - break; - ItemDefinition.intsStack[iStackCounter++] = class11_13.anInt191; - } else { - RSInterface class11_14; - if (opcode < 2000) { - class11_14 = flag ? Class164.aClass11_2055 : aClass11_1749; - } else { - opcode -= 1000; - class11_14 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - } - int[] ai3 = null; - RSString class94_62 = ItemDefinition.stringsStack[--sStackCounter]; - if (class94_62.length() > 0 && class94_62.charAt(class94_62.length() + -1, (byte) -96) == 89) { - int i79 = ItemDefinition.intsStack[--iStackCounter]; - if (i79 > 0) { - ai3 = new int[i79]; - while (i79-- > 0) - ai3[i79] = ItemDefinition.intsStack[--iStackCounter]; - } - class94_62 = class94_62.substring(0, class94_62.length() - 1, 0); - } - Object[] aobj1 = new Object[class94_62.length() - -1]; - for (int i81 = -1 + aobj1.length; 1 <= i81; i81--) - if (115 != class94_62.charAt(i81 + -1, (byte) -43)) - aobj1[i81] = new Integer(ItemDefinition.intsStack[--iStackCounter]); - else - aobj1[i81] = ItemDefinition.stringsStack[--sStackCounter]; - - int j81 = ItemDefinition.intsStack[--iStackCounter]; - if (j81 == -1) - aobj1 = null; - else - aobj1[0] = new Integer(j81); - class11_14.aBoolean195 = true; - if (1400 == opcode) - class11_14.anObjectArray165 = aobj1; - else if (opcode == 1401) - class11_14.anObjectArray180 = aobj1; - else if (opcode == 1402) { - class11_14.anObjectArray239 = aobj1; - } else if (opcode == 1403) { - class11_14.anObjectArray248 = aobj1; - } else if (opcode == 1404) { - class11_14.anObjectArray281 = aobj1; - } else if (1405 == opcode) - class11_14.anObjectArray295 = aobj1; - else if (1406 == opcode) - class11_14.anObjectArray303 = aobj1; - else if (1407 == opcode) { - class11_14.anIntArray286 = ai3; - class11_14.anObjectArray282 = aobj1; - } else if (opcode == 1408) - class11_14.anObjectArray269 = aobj1; - else if (opcode == 1409) { - class11_14.anObjectArray314 = aobj1; - } else if (1410 == opcode) { - class11_14.anObjectArray229 = aobj1; - } else if (opcode == 1411) { - class11_14.anObjectArray170 = aobj1; - } else if (opcode == 1412) - class11_14.anObjectArray276 = aobj1; - else if (opcode == 1414) { - class11_14.anIntArray175 = ai3; - class11_14.anObjectArray174 = aobj1; - } else if (1415 == opcode) { - class11_14.anIntArray274 = ai3; - class11_14.anObjectArray158 = aobj1; - } else if (1416 == opcode) - class11_14.anObjectArray203 = aobj1; - else if (1417 == opcode) { - class11_14.anObjectArray183 = aobj1; - } else if (opcode == 1418) - class11_14.anObjectArray256 = aobj1; - else if (opcode == 1419) - class11_14.anObjectArray220 = aobj1; - else if (opcode == 1420) - class11_14.anObjectArray156 = aobj1; - else if (opcode == 1421) { - class11_14.anObjectArray313 = aobj1; - } else if (1422 == opcode) - class11_14.anObjectArray315 = aobj1; - else if (1423 == opcode) { - class11_14.anObjectArray206 = aobj1; - } else if (opcode == 1424) - class11_14.anObjectArray176 = aobj1; - else if (opcode == 1425) - class11_14.anObjectArray268 = aobj1; - else if (opcode == 1426) { - class11_14.anObjectArray217 = aobj1; - } else if (1427 == opcode) { - class11_14.anObjectArray235 = aobj1; - } else if (opcode == 1428) { - class11_14.anObjectArray161 = aobj1; - class11_14.anIntArray211 = ai3; - } else if (opcode == 1429) { - class11_14.anIntArray185 = ai3; - class11_14.anObjectArray221 = aobj1; - } - } - continue; - } - RSInterface class11_15; - if (opcode < 2000) { - class11_15 = flag ? Class164.aClass11_2055 : aClass11_1749; - } else { - class11_15 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - opcode -= 1000; - } - Class20.method909(class11_15); - if (opcode == 1200 || 1205 == opcode) { - iStackCounter -= 2; - int j75 = ItemDefinition.intsStack[1 + iStackCounter]; - int k65 = ItemDefinition.intsStack[iStackCounter]; - if (-1 == class11_15.anInt191) { - MouseListeningClass.method2092(class11_15.componentHash); - TextureOperation4.method265(class11_15.componentHash); - Class107.method1649(class11_15.componentHash, -101); - } - if (-1 == k65) { - class11_15.itemId = -1; - class11_15.modelType = 1; - class11_15.anInt192 = -1; - } else { - class11_15.anInt192 = k65; - class11_15.anInt271 = j75; - ItemDefinition class48_4 = ItemDefinition.getItemDefinition(k65); - class11_15.anInt280 = class48_4.anInt768; - class11_15.anInt258 = class48_4.anInt792; - class11_15.anInt182 = class48_4.anInt786; - class11_15.anInt264 = class48_4.anInt754; - class11_15.anInt308 = class48_4.anInt799; - class11_15.anInt164 = class48_4.anInt810; - if (class11_15.anInt184 > 0) - class11_15.anInt164 = (class11_15.anInt164 * 32) / class11_15.anInt184; - else if (class11_15.defWidth > 0) - class11_15.anInt164 = (class11_15.anInt164 * 32) / class11_15.defWidth; - class11_15.aBoolean227 = 1205 != opcode; - } - continue; - } - if (opcode == 1201) { - class11_15.modelType = 2; - class11_15.itemId = ItemDefinition.intsStack[--iStackCounter]; - if (class11_15.anInt191 == -1) - Class162.method2206(class11_15.componentHash); - continue; - } - if (opcode == 1202) { - class11_15.modelType = 3; - class11_15.itemId = Class102.player.class52.method1163(); - if (class11_15.anInt191 == -1) - Class162.method2206(class11_15.componentHash); - continue; - } - if (1203 == opcode) { - class11_15.modelType = 6; - class11_15.itemId = ItemDefinition.intsStack[--iStackCounter]; - if (class11_15.anInt191 == -1) - Class162.method2206(class11_15.componentHash); - continue; - } - if (opcode != 1204) - break; - class11_15.modelType = 5; - class11_15.itemId = ItemDefinition.intsStack[--iStackCounter]; - if (class11_15.anInt191 == -1) - Class162.method2206(class11_15.componentHash); - continue; - } - RSInterface class11_16; - if (opcode < 2000) { - class11_16 = flag ? Class164.aClass11_2055 : aClass11_1749; - } else { - opcode -= 1000; - class11_16 = Unsorted.getRSInterface(ItemDefinition.intsStack[--iStackCounter]); - } - if (opcode == 1100) { - iStackCounter -= 2; - class11_16.anInt247 = ItemDefinition.intsStack[iStackCounter]; - if (class11_16.anInt240 + -class11_16.width < class11_16.anInt247) - class11_16.anInt247 = class11_16.anInt240 + -class11_16.width; - if (class11_16.anInt247 < 0) - class11_16.anInt247 = 0; - class11_16.anInt208 = ItemDefinition.intsStack[iStackCounter + 1]; - if (class11_16.anInt208 > class11_16.anInt252 + -class11_16.height) - class11_16.anInt208 = class11_16.anInt252 + -class11_16.height; - if (class11_16.anInt208 < 0) - class11_16.anInt208 = 0; - Class20.method909(class11_16); - if (-1 == class11_16.anInt191) - Class67.method1259(class11_16.componentHash); - continue; - } - if (1101 == opcode) { - class11_16.anInt218 = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - if (class11_16.anInt191 == -1) - Unsorted.method56(class11_16.componentHash); - continue; - } - if (opcode == 1102) { - class11_16.aBoolean226 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class20.method909(class11_16); - continue; - } - if (1103 == opcode) { - class11_16.anInt223 = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (opcode == 1104) { - class11_16.anInt250 = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (opcode == 1105) { - class11_16.spriteArchiveId = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (1106 == opcode) { - class11_16.anInt301 = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (1107 == opcode) { - class11_16.aBoolean186 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class20.method909(class11_16); - continue; - } - if (opcode == 1108) { - class11_16.modelType = 1; - class11_16.itemId = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - if (class11_16.anInt191 == -1) - Class162.method2206(class11_16.componentHash); - continue; - } - if (opcode == 1109) { - iStackCounter -= 6; - class11_16.anInt258 = ItemDefinition.intsStack[iStackCounter]; - class11_16.anInt264 = ItemDefinition.intsStack[iStackCounter + 1]; - class11_16.anInt182 = ItemDefinition.intsStack[2 + iStackCounter]; - class11_16.anInt308 = ItemDefinition.intsStack[iStackCounter - -3]; - class11_16.anInt280 = ItemDefinition.intsStack[iStackCounter - -4]; - class11_16.anInt164 = ItemDefinition.intsStack[5 + iStackCounter]; - Class20.method909(class11_16); - if (class11_16.anInt191 == -1) { - TextureOperation4.method265(class11_16.componentHash); - Class107.method1649(class11_16.componentHash, -106); - } - continue; - } - if (opcode == 1110) { - int l65 = ItemDefinition.intsStack[--iStackCounter]; - if (class11_16.animationId != l65) { - class11_16.animationId = l65; - class11_16.anInt283 = 0; - class11_16.anInt267 = 0; - class11_16.anInt260 = 1; - Class20.method909(class11_16); - } - if (class11_16.anInt191 == -1) - Class108.method1657(class11_16.componentHash); - continue; - } - if (opcode == 1111) { - class11_16.aBoolean181 = 1 == ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (1112 == opcode) { - RSString newText = ItemDefinition.stringsStack[--sStackCounter]; - if (!newText.equalsString(class11_16.text)) { - class11_16.text = newText; - Class20.method909(class11_16); - } - if (class11_16.anInt191 == -1) - Unsorted.method1516(class11_16.componentHash, 91); - continue; - } - if (opcode == 1113) { - class11_16.anInt270 = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (opcode == 1114) { - iStackCounter -= 3; - class11_16.anInt194 = ItemDefinition.intsStack[iStackCounter]; - class11_16.anInt225 = ItemDefinition.intsStack[1 + iStackCounter]; - class11_16.anInt205 = ItemDefinition.intsStack[2 + iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (1115 == opcode) { - class11_16.aBoolean215 = 1 == ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (opcode == 1116) { - class11_16.anInt288 = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (opcode == 1117) { - class11_16.anInt287 = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - continue; - } - if (opcode == 1118) { - class11_16.aBoolean178 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class20.method909(class11_16); - continue; - } - if (opcode == 1119) { - class11_16.aBoolean199 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class20.method909(class11_16); - continue; - } - if (opcode == 1120) { - iStackCounter -= 2; - class11_16.anInt240 = ItemDefinition.intsStack[iStackCounter]; - class11_16.anInt252 = ItemDefinition.intsStack[1 + iStackCounter]; - Class20.method909(class11_16); - if (class11_16.type == 0) - Unsorted.method2104(class11_16, false, -116); - continue; - } - if (opcode == 1121) { - iStackCounter -= 2; - class11_16.aShort293 = (short) ItemDefinition.intsStack[iStackCounter]; - class11_16.aShort169 = (short) ItemDefinition.intsStack[iStackCounter + 1]; - Class20.method909(class11_16); - continue; - } - if (1122 == opcode) { - class11_16.aBoolean157 = ItemDefinition.intsStack[--iStackCounter] == 1; - Class20.method909(class11_16); - continue; - } - if (opcode != 1123) - break; - class11_16.anInt164 = ItemDefinition.intsStack[--iStackCounter]; - Class20.method909(class11_16); - if (class11_16.anInt191 == -1) - TextureOperation4.method265(class11_16.componentHash); - continue; - } - if (opcode == 403) { - iStackCounter -= 2; - int i66 = ItemDefinition.intsStack[iStackCounter + 1]; - int j43 = ItemDefinition.intsStack[iStackCounter]; - int k75 = 0; - while (Class3_Sub26.anIntArray2559.length > k75) { - if (j43 == Class3_Sub26.anIntArray2559[k75]) { - Class102.player.class52.method1164(k75, i66); - continue label0; - } - k75++; - } - k75 = 0; - do { - if (anIntArray3228.length <= k75) - continue label0; - if (anIntArray3228[k75] == j43) { - Class102.player.class52.method1164(k75, i66); - continue label0; - } - k75++; - } while (true); - } - if (404 == opcode) { - iStackCounter -= 2; - int k43 = ItemDefinition.intsStack[iStackCounter]; - int j66 = ItemDefinition.intsStack[1 + iStackCounter]; - Class102.player.class52.method1162(k43, j66); - continue; - } - if (opcode != 410) - break; - try { - boolean flag2 = 0 != ItemDefinition.intsStack[--iStackCounter]; - Class102.player.class52.method1159(flag2); - } catch (Exception e) { - e.printStackTrace(); - } - } while (true); - throw new IllegalStateException(); - } catch (Exception e) { - System.out.println("Exception in item defs " + e); - } - } - - - static Queue method1664(int var0, int var1) { - try { - Queue var3 = new Queue(); - - for (Class3_Sub28_Sub3 var4 = (Class3_Sub28_Sub3) Class134.aLinkedList_1758.method1222(); var4 != null; var4 = (Class3_Sub28_Sub3) Class134.aLinkedList_1758.method1221()) { - if (var4.aBoolean3553 && var4.method537(var1, var0)) { - var3.offer(var4); - } - } - - return var3; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ok.B(" + var0 + ',' + var1 + ',' + (byte) -118 + ')'); - } - } - - static int method1643(boolean var1, int var2, int var3) { - try { - - Class3_Sub25 var4 = (Class3_Sub25) Class3_Sub2.aHashTable_2220.get(var2); - if (null == var4) { - return 0; - } else { - int var5 = 0; - - for (int var6 = 0; var6 < var4.anIntArray2547.length; ++var6) { - if (var4.anIntArray2547[var6] >= 0 && TextureOperation39.itemDefinitionSize > var4.anIntArray2547[var6]) { - ItemDefinition var7 = ItemDefinition.getItemDefinition(var4.anIntArray2547[var6]); - if (null != var7.aHashTable_798) { - LinkableInt var8 = (LinkableInt) var7.aHashTable_798.get(var3); - if (null != var8) { - if (var1) { - var5 += var4.anIntArray2551[var6] * var8.value; - } else { - var5 += var8.value; - } - } - } - } - } - - return var5; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "od.B(" + 10131 + ',' + var1 + ',' + var2 + ',' + var3 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/CSConfigCachefile.java b/Client/src/main/java/org/runite/client/CSConfigCachefile.java deleted file mode 100644 index b07173151..000000000 --- a/Client/src/main/java/org/runite/client/CSConfigCachefile.java +++ /dev/null @@ -1,228 +0,0 @@ -package org.runite.client; - -import org.rs09.SystemLogger; - -import java.awt.event.KeyEvent; - -final class CSConfigCachefile { - - static int anInt1124 = -1; - static int anInt1127 = 0; - int lowerBound; - int upperBound; - int parentVarpIndex; - - - static void method1385(int var0, int var1) { - try { - InterfaceWidget var3 = InterfaceWidget.getWidget(6, var1); - var3.flagUpdate(); - var3.anInt3598 = var0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "kk.E(" + var0 + ',' + var1 + ',' + (byte) -127 + ')'); - } - } - - static int method1386(KeyEvent var1) { - try { - int var2 = var1.getKeyChar(); - if (8364 == var2) { - return 128; - } else { - if (var2 <= 0 || 256 <= var2) { - var2 = -1; - } - - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "kk.C(" + true + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method1390(DataBuffer var0) { - try { - if (-var0.index + var0.buffer.length >= 1) { - int var2 = var0.readUnsignedByte(); - if (var2 >= 0 && var2 <= 11) { - byte var3; - if (var2 == 11) { - var3 = 33; - } else if (var2 == 10) { - var3 = 32; - } else if (var2 == 9) { - var3 = 31; - } else if (var2 == 8) { - var3 = 30; - } else if (var2 == 7) { - var3 = 29; - } else if (var2 == 6) { - var3 = 28; - } else if (var2 == 5) { - var3 = 28; - } else if (var2 == 4) { - var3 = 24; - } else if (var2 == 3) { - var3 = 23; - } else if (var2 == 2) { - var3 = 22; - } else if (1 == var2) { - var3 = 23; - } else { - var3 = 19; - } - - if (var3 <= var0.buffer.length - var0.index) { - Unsorted.anInt3625 = var0.readUnsignedByte(); - if (Unsorted.anInt3625 >= 1) { - if (Unsorted.anInt3625 > 4) { - Unsorted.anInt3625 = 4; - } - } else { - Unsorted.anInt3625 = 1; - } - - Class25.method957(1 == var0.readUnsignedByte()); - Unsorted.aBoolean3604 = var0.readUnsignedByte() == 1; - KeyboardListener.aBoolean1905 = 1 == var0.readUnsignedByte(); - Class25.aBoolean488 = 1 == var0.readUnsignedByte(); - RSInterface.aBoolean236 = var0.readUnsignedByte() == 1; - WorldListEntry.aBoolean2623 = var0.readUnsignedByte() == 1; - Unsorted.aBoolean3275 = var0.readUnsignedByte() == 1; - Class140_Sub6.aBoolean2910 = 1 == var0.readUnsignedByte(); - Unsorted.anInt1137 = var0.readUnsignedByte(); - if (2 < Unsorted.anInt1137) { - Unsorted.anInt1137 = 2; - } - - if (var2 < 2) { - Class106.aBoolean1441 = var0.readUnsignedByte() == 1; - var0.readUnsignedByte(); - } else { - Class106.aBoolean1441 = var0.readUnsignedByte() == 1; - } - - Class128.aBoolean1685 = 1 == var0.readUnsignedByte(); - Class38.aBoolean661 = var0.readUnsignedByte() == 1; - Class3_Sub28_Sub9.anInt3622 = var0.readUnsignedByte(); - if (Class3_Sub28_Sub9.anInt3622 > 2) { - Class3_Sub28_Sub9.anInt3622 = 2; - } - - Unsorted.anInt3671 = Class3_Sub28_Sub9.anInt3622; - TextureOperation17.stereoSound = var0.readUnsignedByte() == 1; - AudioHandler.soundEffectVolume = var0.readUnsignedByte(); - if (AudioHandler.soundEffectVolume > 127) { - AudioHandler.soundEffectVolume = 127; - } - - AudioHandler.musicVolume = var0.readUnsignedByte(); - Sprites.ambientVolume = var0.readUnsignedByte(); - if (Sprites.ambientVolume > 127) { - Sprites.ambientVolume = 127; - } - - if (var2 >= 1) { - TextureOperation.anInt2378 = var0.readUnsignedShort(); - Unsorted.anInt3071 = var0.readUnsignedShort(); - } - - if (var2 >= 3 && var2 < 6) { - var0.readUnsignedByte(); - } - - if (var2 >= 4) { - int var4 = var0.readUnsignedByte(); - if (Class3_Sub24_Sub3.maxClientMemory < 96) { - var4 = 0; - } - - Class127_Sub1.method1758(var4); - } - - if (var2 >= 5) { - Unsorted.anInt2148 = var0.readInt(); - } - - if (6 <= var2) { - Unsorted.anInt2577 = var0.readUnsignedByte(); - } - - if (var2 >= 7) { - Unsorted.aBoolean2146 = 1 == var0.readUnsignedByte(); - } - - if (8 <= var2) { - AudioThread.aBoolean346 = var0.readUnsignedByte() == 1; - } - - if (9 <= var2) { - Class3_Sub20.anInt2488 = var0.readUnsignedByte(); - } - - if (10 <= var2) { - Unsorted.aBoolean1080 = 0 != var0.readUnsignedByte(); - } - - if (var2 >= 11) { - Class163_Sub3.aBoolean3004 = var0.readUnsignedByte() != 0; - } - - } - } - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "kk.F(" + (var0 != null ? "{...}" : "null") + ',' + -1 + ')'); - } - } - - static boolean method1391(int var0) { - try { - return var0 == ~Unsorted.anInt154 && !Class101.aClass3_Sub24_Sub4_1421.method473(-128); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "kk.A(" + var0 + ')'); - } - } - - static CSConfigCachefile getCSConfigFileFromVarbitID(int varbitID) { - try { - CSConfigCachefile cacheFile = (CSConfigCachefile) VarpHelpers.varbitLookup.get(varbitID); - if (cacheFile == null) { - byte[] fileData = Class101.csConfigFileRAM.getFile(varbitID >>> 10, varbitID & 1023); - cacheFile = new CSConfigCachefile(); - if (fileData != null) { - cacheFile.tryParseConfigFile(new DataBuffer(fileData)); - } - - VarpHelpers.varbitLookup.put(cacheFile, varbitID); - } - return cacheFile; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "jl.A(" + varbitID + ',' + (byte) 127 + ')'); - } - } - - final void tryParseConfigFile(DataBuffer fileData) { - try { - boolean end = fileData.readUnsignedByte() == 0; - while (!end) { - this.parseConfigFile(fileData); - end = fileData.readUnsignedByte() == 0; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "kk.G(" + (fileData != null ? "{...}" : "null") + ',' + -111 + ')'); - } - } - - private void parseConfigFile(DataBuffer fileData) { - try { - this.parentVarpIndex = fileData.readUnsignedShort(); - this.lowerBound = fileData.readUnsignedByte(); - this.upperBound = fileData.readUnsignedByte(); - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "kk.B(" + (fileData != null ? "{...}" : "null") + ',' + 1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/CacheIndex.java b/Client/src/main/java/org/runite/client/CacheIndex.java deleted file mode 100644 index 973166518..000000000 --- a/Client/src/main/java/org/runite/client/CacheIndex.java +++ /dev/null @@ -1,653 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; -import org.rs09.client.filestore.ReferenceTable; -import org.rs09.client.filestore.ResourceProvider; -import org.rs09.client.filestore.compression.Container; -import org.rs09.client.util.ArrayUtils; -import org.rs09.client.util.CRC; - -import java.util.Objects; - -public final class CacheIndex { - - /** - * These Indexes listed below are mostly used for CRC checks - */ - public static CacheIndex fontsIndex; - public static CacheIndex musicIndex; - public static CacheIndex spritesIndex; - static CacheIndex skeletonsIndex; - static CacheIndex skinsIndex; - static CacheIndex configurationsIndex; - static CacheIndex interfacesIndex; - public static CacheIndex soundFXIndex; - static CacheIndex landscapesIndex; - static CacheIndex modelsIndex; - static CacheIndex texturesIndex; - static CacheIndex huffmanEncodingIndex; - static CacheIndex music2Index; - static CacheIndex interfaceScriptsIndex; - static CacheIndex soundFX2Index; - static CacheIndex soundFX3Index; - static CacheIndex objectConfigIndex; - static CacheIndex clientscriptMaskIndex; - static CacheIndex npcConfigIndex; - static CacheIndex itemConfigIndex; - static CacheIndex animationIndex; - static CacheIndex graphicFXIndex; - static CacheIndex clientScriptConfigIndex; - static CacheIndex worldmapIndex; - static CacheIndex quickchatMessagesIndex; - static CacheIndex quickchatMenusIndex; - static CacheIndex materialsIndex; - static CacheIndex particlesConfigIndex; - static CacheIndex libIndex; - /** - * - */ - - - private final boolean discardPacked; - private final boolean aBoolean1946; - private final ResourceProvider provider; - private ReferenceTable referenceTable = null; - private Object[] packed; - private Object[][] unpacked; - - public CacheIndex(ResourceProvider provider, boolean var2, boolean var3) { - this.provider = provider; - this.discardPacked = var2; - this.aBoolean1946 = var3; - } - - private static Object wrap(byte[] data) { - if (data == null) { - return null; - } - - if (data.length > 136 && !GameConfig.CACHE_DEBUG) { - try { - Class144 buffer = new Class144_Sub1(); - buffer.setBytes(data); - return buffer; - } catch (Throwable var4) { - GameConfig.CACHE_DEBUG = true; - } - } - - return data; - } - - final boolean method2113() { - try { - if (this.isReady()) { - boolean var2 = true; - - for (int var3 = 0; this.referenceTable.validArchiveIds.length > var3; ++var3) { - int var4 = this.referenceTable.validArchiveIds[var3]; - if (null == this.packed[var4]) { - this.load(var4); - if (null == this.packed[var4]) { - var2 = false; - } - } - } - - return var2; - } else { - return false; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ve.IA()"); - } - } - - private int method2114(int var1) { - if (this.isValidArchive(var1)) { - return this.packed[var1] != null ? 100 : this.provider.percentComplete(var1); - } else { - return 0; - } - } - - final void method2115(int var1, boolean var2) { - try { - if (this.isReady()) { - if (var2) { - this.referenceTable.archiveNameHash = null; - this.referenceTable.setALookupTable_949(null); - } - - this.referenceTable.setALookupTableArray962(null); - this.referenceTable.setFileNameHashes(null); - - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ve.R(" + var1 + ',' + var2 + ',' + true + ')'); - } - } - - final int method2116(RSString var2) { - try { - if (this.isReady()) { - var2 = var2.toLowercase(); - int var3 = this.referenceTable.getALookupTable_949().get(var2.method1574()); - return this.method2114(var3); - } else { - return 0; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ve.P(" + 22813 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final boolean method2117(int var2) { - if (this.isValidArchive(var2)) { - if (null == this.packed[var2]) { - this.load(var2); - return null != this.packed[var2]; - } else { - return true; - } - } else { - return false; - } - } - - final int getReferenceTableCrc() { - if (this.isReady()) { - return this.referenceTable.getCrc(); - } else { - throw new IllegalStateException(""); - } - } - - public final int getArchiveForName(RSString name) { - try { - if (this.isReady()) { - name = name.toLowercase(); - int var3 = this.referenceTable.getALookupTable_949().get(name.method1574()); - return this.isValidArchive(var3) ? var3 : -1; - } else { - return -1; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ve.EA(" + (name != null ? "{...}" : "null") + ',' + (byte) -30 + ')'); - } - } - - final int method2121() { - try { - if (this.isReady()) { - - return this.referenceTable.archiveLengths.length; - } else { - return -1; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ve.D(" + 0 + ')'); - } - } - - private boolean isReady() { - if (this.referenceTable == null) { - this.referenceTable = this.provider.getReferenceTable(); - if (this.referenceTable == null) { - return false; - } - - this.unpacked = new Object[this.referenceTable.getArchiveAmount()][]; - this.packed = new Object[this.referenceTable.getArchiveAmount()]; - } - - return true; - } - - final byte[] method2123(RSString var2, RSString var3) { - try { - if (this.isReady()) { - var3 = var3.toLowercase(); - var2 = var2.toLowercase(); - int var4 = this.referenceTable.getALookupTable_949().get(var3.method1574()); - - if (this.isValidArchive(var4)) { - int var5 = this.referenceTable.getALookupTableArray962()[var4].get(var2.method1574()); - return this.getFile(var4, var5); - } else { - return null; - } - } else { - return null; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ve.C(" + 0 + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - final void method2124(int var1, RSString var2) { - try { - if (this.isReady()) { - var2 = var2.toLowercase(); - int var3 = this.referenceTable.getALookupTable_949().get(var2.method1574()); - this.request(var3); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ve.V(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final boolean method2125(RSString var1, RSString var3) { - try { - if (this.isReady()) { - var3 = var3.toLowercase(); - var1 = var1.toLowercase(); - int var4 = this.referenceTable.getALookupTable_949().get(var3.method1574()); - if (this.isValidArchive(var4)) { - int var5 = this.referenceTable.getALookupTableArray962()[var4].get(var1.method1574()); - - return this.method2129((byte) 70, var5, var4); - } else { - return false; - } - } else { - return false; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ve.DA(" + (var1 != null ? "{...}" : "null") + ',' + (byte) 116 + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - final byte[] getFile(int archive, int file, int[] xtea) { - if (!this.isValidFile(archive, file)) { - return null; - } - - if (this.unpacked[archive] == null || this.unpacked[archive][file] == null) { - boolean loaded = this.unpack(archive, xtea); - - if (!loaded) { - this.load(archive); - loaded = this.unpack(archive, xtea); - if (!loaded) { - return null; - } - } - } - - byte[] var7 = NPC.method1985(this.unpacked[archive][file], false); - if (this.aBoolean1946) { - this.unpacked[archive][file] = null; - if (this.referenceTable.archiveLengths[archive] == 1) { - this.unpacked[archive] = null; - } - } - - return var7; - } - - final boolean method2127(RSString var2) { - try { - if (this.isReady()) { - var2 = var2.toLowercase(); - int var3 = this.referenceTable.getALookupTable_949().get(var2.method1574()); - return this.method2117(var3); - } else { - return false; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ve.O(" + (byte) -83 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final void method2128(int var2) { - try { - if (this.isValidArchive(var2)) { - if (null != this.unpacked) { - this.unpacked[var2] = null; - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ve.B(" + 7561 + ',' + var2 + ')'); - } - } - - final boolean method2129(byte var1, int var2, int var3) { - try { - if (this.isValidFile(var3, var2)) { - if (this.unpacked[var3] != null && null != this.unpacked[var3][var2]) { - return true; - } else if (this.packed[var3] == null) { - this.load(var3); - return this.packed[var3] != null; - } else { - return true; - } - } else { - return false; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ve.FA(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - private boolean isValidArchive(int archiveId) { - if (!this.isReady()) { - return false; - } - - if (archiveId >= 0 - && this.referenceTable.archiveLengths.length > archiveId - && this.referenceTable.archiveLengths[archiveId] != 0) - return true; - - if (GameConfig.CACHE_DEBUG) - throw new IllegalArgumentException(Integer.toString(archiveId)); - return false; - } - - private void request(int archive) { - this.provider.request(archive); - } - - private boolean unpack(int archive, int[] xtea) { - if (!isValidArchive(archive)) return false; - if (packed[archive] == null) return false; - - int count = this.referenceTable.archiveFileLengths[archive]; - int[] indices = this.referenceTable.validFileIds[archive]; - if (this.unpacked[archive] == null) { - this.unpacked[archive] = new Object[this.referenceTable.archiveLengths[archive]]; - } - - boolean finished = true; - Object[] objects = this.unpacked[archive]; - - for (int i = 0; i < count; ++i) { - int index; - if (indices == null) { - index = i; - } else { - index = indices[i]; - } - - if (objects[index] == null) { - finished = false; - break; - } - } - - if (finished) return true; - - byte[] decrypted; - if (xtea != null && (xtea[0] != 0 || xtea[1] != 0 || xtea[2] != 0 || xtea[3] != 0)) { - decrypted = NPC.method1985(this.packed[archive], true); - DataBuffer buffer = new DataBuffer(decrypted); - buffer.method770(xtea, buffer.buffer.length); - } else { - decrypted = NPC.method1985(this.packed[archive], false); - } - - byte[] data = new byte[0]; - try { - data = Container.INSTANCE.decode(decrypted);//Class3_Sub28_Sub13.decodeContainer(var21); - } catch (Throwable e) { - System.out.println("Tried to go into a null area"); - System.out.println("T3 - " + (xtea != null) + "," + archive + "," + Objects.requireNonNull(decrypted).length + "," + CRC.INSTANCE.crc32(decrypted, decrypted.length) + "," + CRC.INSTANCE.crc32(decrypted, decrypted.length - 2) + "," + this.referenceTable.archiveCRCs[archive] + "," + this.referenceTable.getCrc()); - //throw ClientErrorException.clientError(e, "T3 - " + (xtea != null) + "," + archive + "," + Objects.requireNonNull(decrypted).length + "," + CRC.INSTANCE.crc32(decrypted, decrypted.length) + "," + CRC.INSTANCE.crc32(decrypted, decrypted.length - 2) + "," + this.referenceTable.archiveCRCs[archive] + "," + this.referenceTable.getCrc()); - } - - if (this.discardPacked) { - this.packed[archive] = null; - } - - int start; - if (count > 1) { - start = data.length; - start--; - int blockCount = data[start] & 0xff; - start -= count * blockCount * 4; - - DataBuffer buffer = new DataBuffer(data); - buffer.index = start; - int[] var13 = new int[count]; - - int var15; - int var16; - for (int var14 = 0; blockCount > var14; ++var14) { - var15 = 0; - - for (var16 = 0; var16 < count; ++var16) { - var15 += buffer.readInt(); - - var13[var16] += var15; - } - } - - byte[][] var24 = new byte[count][]; - - for (var15 = 0; count > var15; ++var15) { - var24[var15] = new byte[var13[var15]]; - var13[var15] = 0; - } - - buffer.index = start; - var15 = 0; - - int var17; - for (var16 = 0; blockCount > var16; ++var16) { - var17 = 0; - - for (int var18 = 0; var18 < count; ++var18) { - var17 += buffer.readInt(); - ArrayUtils.arraycopy(data, var15, var24[var18], var13[var18], var17); - var15 += var17; - var13[var18] += var17; - } - } - - for (var16 = 0; var16 < count; ++var16) { - if (indices == null) { - var17 = var16; - } else { - var17 = indices[var16]; - } - - if (this.aBoolean1946) { - objects[var17] = var24[var16]; - } else { - objects[var17] = wrap(var24[var16]); - } - } - } else { - if (null == indices) { - start = 0; - } else { - start = indices[0]; - } - - if (this.aBoolean1946) { - objects[start] = data; - } else { - objects[start] = wrap(data); - } - } - return true; - } - - public final byte[] getFile(int archive, int file) { - return this.getFile(archive, file, null); - } - - private void load(int archive) { - if (this.discardPacked) { - this.packed[archive] = this.provider.get(archive); - } else { - this.packed[archive] = wrap(this.provider.get(archive)); - } - } - - final boolean method2135(RSString var1) { - try { - if (this.isReady()) { - var1 = var1.toLowercase(); - int var3 = this.referenceTable.getALookupTable_949().get(var1.method1574()); - return var3 >= 0; - } else { - return false; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ve.S(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - final int method2136(byte var1) { - try { - if (var1 > -121) { - Unsorted.anInt1950 = -3; - } - - if (this.isReady()) { - int var2 = 0; - int var3 = 0; - - int var4; - for (var4 = 0; var4 < this.packed.length; ++var4) { - if (0 < this.referenceTable.archiveFileLengths[var4]) { - var2 += 100; - var3 += this.method2114(var4); - } - } - - if (var2 == 0) { - return 100; - } else { - var4 = var3 * 100 / var2; - return var4; - } - } else { - return 0; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ve.N(" + var1 + ')'); - } - } - - final void method2137() { - try { - if (this.unpacked != null) { - for (int var2 = 0; this.unpacked.length > var2; ++var2) { - this.unpacked[var2] = null; - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ve.Q(" + ')'); - } - } - - final byte[] method2138(int var1) { - try { - if (!this.isReady()) { - return null; - } else if (this.referenceTable.archiveLengths.length == 1) { - return this.getFile(0, var1); - } else if (!this.isValidArchive(var1)) { - return null; - } else if (this.referenceTable.archiveLengths[var1] == 1) { - return this.getFile(var1, 0); - } else { - throw new RuntimeException(); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ve.HA(" + var1 + ',' + 0 + ')'); - } - } - - private boolean isValidFile(int archive, int file) { - if (!this.isReady()) { - return false; - } - if (0 <= archive - && file >= 0 && archive < this.referenceTable.archiveLengths.length - && this.referenceTable.archiveLengths[archive] > file) - return true; - if (GameConfig.CACHE_DEBUG) - throw new IllegalArgumentException(archive + "," + file); - return false; - } - - final byte[] method2140(int file, int archive) { - if (this.isValidFile(archive, file)) { - if (this.unpacked[archive] == null || null == this.unpacked[archive][file]) { - boolean var4 = this.unpack(archive, null); - if (!var4) { - this.load(archive); - var4 = this.unpack(archive, null); - if (!var4) { - return null; - } - } - } - - return NPC.method1985(this.unpacked[archive][file], false); - } else { - return null; - } - } - - final int[] getFileIds(int archiveId) { - try { - - if (this.isValidArchive(archiveId)) { - int[] var3 = this.referenceTable.validFileIds[archiveId]; - if (null == var3) { - var3 = new int[this.referenceTable.archiveFileLengths[archiveId]]; - - for (int var4 = 0; var3.length > var4; var3[var4] = var4++) { - } - } - - return var3; - } else { - return null; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ve.G(" + (byte) -128 + ',' + archiveId + ')'); - } - } - - final int getFileAmount(int archiveId) { - try { - if (this.isValidArchive(archiveId)) { - return this.referenceTable.archiveLengths[archiveId]; - } else { - return 0; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ve.H(" + archiveId + ')'); - } - } - - public final boolean retrieveSpriteFile(int archiveId) { - try { - if (!this.isReady()) { - return false; - } else if (this.referenceTable.archiveLengths.length == 1) { - return this.method2129((byte) 86, archiveId, 0); - } else if (this.isValidArchive(archiveId)) { - if (1 == this.referenceTable.archiveLengths[archiveId]) { - return this.method2129((byte) 109, 0, archiveId); - } else { - throw new RuntimeException(); - } - } else { - return false; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ve.A(" + 0 + ',' + archiveId + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/CacheResourceRequest.java b/Client/src/main/java/org/runite/client/CacheResourceRequest.java deleted file mode 100644 index ec4dfea28..000000000 --- a/Client/src/main/java/org/runite/client/CacheResourceRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.runite.client; - -final class CacheResourceRequest extends ResourceRequest { - - Class41 cache; - byte[] data; - int type; - - final byte[] getData() { - if (this.waiting) { - throw new RuntimeException("Attempted to get data while request was not completed yet"); - } - return this.data; - } - - final int getCompletion() { - return this.waiting ? 0 : 100; - } - -} diff --git a/Client/src/main/java/org/runite/client/CacheResourceWorker.java b/Client/src/main/java/org/runite/client/CacheResourceWorker.java deleted file mode 100644 index 8fde4d47d..000000000 --- a/Client/src/main/java/org/runite/client/CacheResourceWorker.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.Queue; - -import java.util.Objects; - -final class CacheResourceWorker implements Runnable { - - private final Queue requests = new Queue<>(); - private boolean stopped = false; - private Thread thread; - public int remaining = 0; - - private void addRequest(CacheResourceRequest request) { - synchronized (this.requests) { - this.requests.offer(request); - this.remaining++; - this.requests.notifyAll(); - } - } - - public final void stop() { - this.stopped = true; - synchronized (this.requests) { - this.requests.notifyAll(); - } - - try { - this.thread.join(); - } catch (InterruptedException var4) { - } - - this.thread = null; - } - - public final void write(Class41 cache, int archive, byte[] data) { - CacheResourceRequest var5 = new CacheResourceRequest(); - var5.data = data; - var5.priority = false; - var5.nodeKey = archive; - var5.cache = cache; - var5.type = 2; - this.addRequest(var5); - } - - public final CacheResourceRequest read(Class41 cache, int archive) { - CacheResourceRequest var4 = new CacheResourceRequest(); - var4.cache = cache; - var4.type = 3; - var4.priority = false; - - var4.nodeKey = archive; - this.addRequest(var4); - return var4; - } - - public final CacheResourceRequest priorityRead(Class41 cache, int archive) { - CacheResourceRequest var4 = new CacheResourceRequest(); - var4.type = 1; - synchronized (this.requests) { - CacheResourceRequest var6 = this.requests.getFront(); - - while (var6 != null) { - if ((long) archive == var6.nodeKey && var6.cache == cache && var6.type == 2) { - var4.data = var6.data; - var4.waiting = false; - return var4; - } - - var6 = this.requests.next(); - } - } - - var4.data = cache.read(archive); - var4.waiting = false; - var4.priority = true; - return var4; - } - - public final void run() { - while (!this.stopped) { - CacheResourceRequest request; - synchronized (this.requests) { - request = this.requests.poll(); - if (request == null) { - try { - this.requests.wait(); - } catch (InterruptedException var6) { - } - continue; - } - - this.remaining--; - } - - try { - if (request.type == 2) { - request.cache.write((int) request.nodeKey, request.data.length, request.data); - } else if (request.type == 3) { - request.data = request.cache.read((int) request.nodeKey); - } - } catch (Exception var5) { - Class49.reportError(null, var5); - } - - request.waiting = false; - } - } - - public CacheResourceWorker() { - Class64 var1 = Class38.gameSignlink.startThread(5, this); - - while (Objects.requireNonNull(var1).anInt978 == 0) { - TimeUtils.sleep(10L); - } - - if (var1.anInt978 == 2) { - throw new RuntimeException(); - } else { - this.thread = (Thread) var1.anObject974; - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class1.java b/Client/src/main/java/org/runite/client/Class1.java deleted file mode 100644 index ca9fec6fd..000000000 --- a/Client/src/main/java/org/runite/client/Class1.java +++ /dev/null @@ -1,671 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.HashTable; -import org.rs09.client.rendering.Toolkit; - -final class Class1 { - - static int[] anIntArray52 = new int[4096]; - static boolean[] aBooleanArray54; - static int screenUpperY; - static int anInt56; - static int anInt57; - static RSString aClass94_58; - static int[] anIntArray2642 = new int[]{1, 1, 1, 1, 4, 1, 1, 5, 6, 1, 5, 0, 7, 0, 4, 1, 7, 2, 1, 1, 6, 1, 1, 3, 6, 1, 7, 0, 0, 6, 7, 0, 1, 7, 6, 1, 1, 1, 5, 4, 3, 2, 1, 1, 0, 4, 1, 5}; - - static { - for (int var0 = 0; var0 < 4096; ++var0) { - anIntArray52[var0] = Class164_Sub2.method2246(var0); - } - - aBooleanArray54 = new boolean[8]; - anInt57 = 0; - aClass94_58 = RSString.parse(" )4 "); - } - - static void method69(Class36 var0) { - try { - TextureOperation16.aClass36_3112 = var0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "a.B(" + (var0 != null ? "{...}" : "null") + ')'); - } - } - - static Class3_Sub11[] method70(float[][] var0, float[][] var1, int[][] var2, int var3, float[][] var4, byte[][] var5, int[][] var6, byte[][] var8, byte[][] var9, byte[][] var10, byte[][][] var11) { - try { - HashTable var12 = new HashTable(128); - int var13; - int var14; - int var15; - int var16; - int var21; - int var23; - int var22; - int var25; - int var24; - int var27; - int var26; - int var29; - int var28; - int var30; - int var35; - byte var33; - int var36; - int var58; - int var65; - boolean[] var79; - int var73; - int var74; - boolean[] var75; - boolean[] var81; - for (var13 = 1; var13 <= 102; ++var13) { - for (var14 = 1; var14 <= 102; ++var14) { - var15 = var9[var13][var14] & 0xFF; - var16 = 255 & var10[var13][var14]; - if (var16 != 0) { - FloorOverlayDefinition var17 = FloorOverlayDefinition.getFile(-1 + var16); - if (var17.anInt2103 == -1) { - continue; - } - - Class3_Sub11 var18 = method2052(var12, var17); - byte var19 = var8[var13][var14]; - int[] var20 = Class134.anIntArrayArray1763[var19]; - var18.anInt2342 += var20.length / 2; - ++var18.anInt2344; - if (var17.aBoolean2092 && var15 != 0) { - var18.anInt2342 += Unsorted.anIntArray3607[var19]; - } - } - - if ((var9[var13][var14] & 0xFF) != 0 || var16 != 0 && var8[var13][var14] == 0) { - var58 = 0; - int var60 = 0; - var21 = 0; - var65 = 0; - var23 = 255 & var10[var13][1 + var14]; - var25 = var10[var13][var14 - 1] & 0xFF; - var24 = var10[-1 + var13][var14] & 0xFF; - int[] var63 = new int[8]; - var22 = 0; - var27 = 255 & var10[var13 + -1][1 + var14]; - var26 = var10[var13 + 1][var14] & 0xFF; - var29 = 255 & var10[var13 + 1][-1 + var14]; - var28 = var10[var13 - 1][-1 + var14] & 0xFF; - var30 = var10[1 + var13][var14 - -1] & 0xFF; - FloorOverlayDefinition var31; - int var34; - byte var32; - if (var27 != 0 || var27 != var16) { - var31 = FloorOverlayDefinition.getFile(-1 + var27); - if (var31.aBoolean2092 && var31.anInt2103 != -1) { - var32 = var5[var13 + -1][var14 - -1]; - var33 = var8[-1 + var13][var14 + 1]; - var34 = anIntArray2642[4 * var33 - -(2 + var32 & 3)]; - var35 = anIntArray2642[(3 + var32 & 3) + 4 * var33]; - if (!TextureOperation8.aBooleanArrayArray3468[var35][1] && !TextureOperation8.aBooleanArrayArray3468[var34][0]) { - for (var36 = 0; 8 > var36; ++var36) { - if (var58 == var36) { - var63[var58++] = var27; - break; - } - - if (var63[var36] == var27) { - break; - } - } - } - } - } - - if (var28 != 0 || var28 != var16) { - var31 = FloorOverlayDefinition.getFile(-1 + var28); - if (var31.aBoolean2092 && var31.anInt2103 != -1) { - var32 = var5[-1 + var13][var14 + -1]; - var33 = var8[-1 + var13][-1 + var14]; - var34 = anIntArray2642[var33 * 4 - -(var32 & 3)]; - var35 = anIntArray2642[(var32 - -3 & 3) + var33 * 4]; - if (!TextureOperation8.aBooleanArrayArray3468[var34][1] && !TextureOperation8.aBooleanArrayArray3468[var35][0]) { - for (var36 = 0; var36 < 8; ++var36) { - if (var36 == var58) { - var63[var58++] = var28; - break; - } - - if (var28 == var63[var36]) { - break; - } - } - } - } - } - - if (var29 != 0 || var29 != var16) { - var31 = FloorOverlayDefinition.getFile(var29 - 1); - if (var31.aBoolean2092 && var31.anInt2103 != -1) { - var32 = var5[1 + var13][var14 - 1]; - var33 = var8[var13 - -1][var14 + -1]; - var35 = anIntArray2642[4 * var33 + (3 & 1 + var32)]; - var34 = anIntArray2642[var33 * 4 - -(var32 & 3)]; - if (!TextureOperation8.aBooleanArrayArray3468[var35][1] && !TextureOperation8.aBooleanArrayArray3468[var34][0]) { - for (var36 = 0; var36 < 8; ++var36) { - if (var36 == var58) { - var63[var58++] = var29; - break; - } - - if (var63[var36] == var29) { - break; - } - } - } - } - } - - if (var30 != 0 || var30 != var16) { - var31 = FloorOverlayDefinition.getFile(var30 + -1); - if (var31.aBoolean2092 && var31.anInt2103 != -1) { - var32 = var5[var13 - -1][1 + var14]; - var33 = var8[var13 + 1][1 + var14]; - var35 = anIntArray2642[4 * var33 + (var32 - -1 & 3)]; - var34 = anIntArray2642[var33 * 4 + (var32 - -2 & 3)]; - if (!TextureOperation8.aBooleanArrayArray3468[var34][1] && !TextureOperation8.aBooleanArrayArray3468[var35][0]) { - for (var36 = 0; var36 < 8; ++var36) { - if (var58 == var36) { - var63[var58++] = var30; - break; - } - - if (var63[var36] == var30) { - break; - } - } - } - } - } - - if (var23 != 0 && var23 != var16) { - var31 = FloorOverlayDefinition.getFile(-1 + var23); - if (var31.aBoolean2092 && var31.anInt2103 != -1) { - var60 = anIntArray2642[4 * var8[var13][var14 - -1] + (var5[var13][var14 - -1] - -2 & 3)]; - - for (var74 = 0; var74 < 8; ++var74) { - if (var74 == var58) { - var63[var58++] = var23; - break; - } - - if (var63[var74] == var23) { - break; - } - } - } - } - - if (0 != var24 && var24 != var16) { - var31 = FloorOverlayDefinition.getFile(var24 + -1); - if (var31.aBoolean2092 && var31.anInt2103 != -1) { - var65 = anIntArray2642[(3 & 3 + var5[var13 + -1][var14]) + var8[var13 + -1][var14] * 4]; - - for (var74 = 0; var74 < 8; ++var74) { - if (var58 == var74) { - var63[var58++] = var24; - break; - } - - if (var63[var74] == var24) { - break; - } - } - } - } - - if (var25 != 0 && var16 != var25) { - var31 = FloorOverlayDefinition.getFile(-1 + var25); - if (var31.aBoolean2092 && var31.anInt2103 != -1) { - var21 = anIntArray2642[(3 & var5[var13][var14 + -1]) + var8[var13][var14 + -1] * 4]; - - for (var74 = 0; var74 < 8; ++var74) { - if (var58 == var74) { - var63[var58++] = var25; - break; - } - - if (var25 == var63[var74]) { - break; - } - } - } - } - - if (0 != var26 && var16 != var26) { - var31 = FloorOverlayDefinition.getFile(var26 - 1); - if (var31.aBoolean2092 && var31.anInt2103 != -1) { - var22 = anIntArray2642[(3 & var5[var13 + 1][var14] + 1) + 4 * var8[1 + var13][var14]]; - - for (var74 = 0; var74 < 8; ++var74) { - if (var74 == var58) { - var63[var58++] = var26; - break; - } - - if (var63[var74] == var26) { - break; - } - } - } - } - - for (var73 = 0; var58 > var73; ++var73) { - var74 = var63[var73]; - var79 = TextureOperation8.aBooleanArrayArray3468[var74 != var24 ? 0 : var65]; - var75 = TextureOperation8.aBooleanArrayArray3468[var25 != var74 ? 0 : var21]; - boolean[] var80 = TextureOperation8.aBooleanArrayArray3468[var23 == var74 ? var60 : 0]; - var81 = TextureOperation8.aBooleanArrayArray3468[var26 == var74 ? var22 : 0]; - FloorOverlayDefinition var37 = FloorOverlayDefinition.getFile(-1 + var74); - Class3_Sub11 var38 = method2052(var12, var37); - var38.anInt2342 += 5; - var38.anInt2342 += -2 + var80.length; - var38.anInt2342 += -2 + var79.length; - var38.anInt2342 += var75.length - 2; - var38.anInt2342 += -2 + var81.length; - ++var38.anInt2344; - } - } - } - } - - Class3_Sub11 var56; - for (var56 = (Class3_Sub11) var12.first(); null != var56; var56 = (Class3_Sub11) var12.next()) { - var56.method145(); - } - - for (var13 = 1; 102 >= var13; ++var13) { - for (var14 = 1; var14 <= 102; ++var14) { - var16 = 255 & var9[var13][var14]; - var58 = 255 & var10[var13][var14]; - if ((8 & var11[var3][var13][var14]) != 0) { - var15 = 0; - } else if (2 == (var11[1][var13][var14] & 2) && var3 > 0) { - var15 = var3 + -1; - } else { - var15 = var3; - } - - if (0 != var58) { - FloorOverlayDefinition var62 = FloorOverlayDefinition.getFile(-1 + var58); - if (var62.anInt2103 == -1) { - continue; - } - - Class3_Sub11 var66 = method2052(var12, var62); - byte var67 = var8[var13][var14]; - byte var68 = var5[var13][var14]; - var22 = TextureOperation34.method190(var62.anInt2095, var62.anInt2103, (byte) -111, var6[var13][var14]); - var23 = TextureOperation34.method190(var62.anInt2095, var62.anInt2103, (byte) 65, var6[var13 + 1][var14]); - var24 = TextureOperation34.method190(var62.anInt2095, var62.anInt2103, (byte) 68, var6[1 + var13][var14 + 1]); - var25 = TextureOperation34.method190(var62.anInt2095, var62.anInt2103, (byte) -84, var6[var13][var14 - -1]); - Class29.method971(var22, var2, var1, var13, var0, var23, var68, var15, var24, var16 != 0 && var62.aBoolean2092, var67, var14, var4, var25, var66); - } - - if ((var9[var13][var14] & 0xFF) != 0 || var58 != 0 && 0 == var8[var13][var14]) { - int[] var64 = new int[8]; - var65 = 0; - int var61 = 0; - var21 = 0; - var22 = 0; - var24 = var10[var13][var14 - -1] & 0xFF; - var23 = 0; - var25 = var10[-1 + var13][var14] & 0xFF; - var27 = var10[1 + var13][var14] & 0xFF; - var26 = var10[var13][-1 + var14] & 0xFF; - var28 = 255 & var10[-1 + var13][var14 + 1]; - var29 = 255 & var10[var13 - 1][var14 - 1]; - var30 = 255 & var10[1 + var13][var14 + -1]; - var73 = var10[1 + var13][var14 + 1] & 0xFF; - FloorOverlayDefinition var76; - byte var77; - int var83; - if (0 == var28 || var28 == var58) { - var28 = 0; - } else { - var76 = FloorOverlayDefinition.getFile(-1 + var28); - if (var76.aBoolean2092 && var76.anInt2103 != -1) { - var33 = var5[var13 + -1][1 + var14]; - var77 = var8[-1 + var13][1 + var14]; - var35 = anIntArray2642[4 * var77 - -(2 + var33 & 3)]; - var36 = anIntArray2642[var77 * 4 - -(3 + var33 & 3)]; - if (TextureOperation8.aBooleanArrayArray3468[var36][1] && TextureOperation8.aBooleanArrayArray3468[var35][0]) { - var28 = 0; - } else { - for (var83 = 0; var83 < 8; ++var83) { - if (var61 == var83) { - var64[var61++] = var28; - break; - } - - if (var28 == var64[var83]) { - break; - } - } - } - } else { - var28 = 0; - } - } - - if (0 == var29 || var29 == var58) { - var29 = 0; - } else { - var76 = FloorOverlayDefinition.getFile(-1 + var29); - if (var76.aBoolean2092 && -1 != var76.anInt2103) { - var33 = var5[var13 + -1][-1 + var14]; - var77 = var8[-1 + var13][var14 + -1]; - var35 = anIntArray2642[(3 & var33) + var77 * 4]; - var36 = anIntArray2642[(var33 - -3 & 3) + 4 * var77]; - if (TextureOperation8.aBooleanArrayArray3468[var35][1] && TextureOperation8.aBooleanArrayArray3468[var36][0]) { - var29 = 0; - } else { - for (var83 = 0; 8 > var83; ++var83) { - if (var83 == var61) { - var64[var61++] = var29; - break; - } - - if (var29 == var64[var83]) { - break; - } - } - } - } else { - var29 = 0; - } - } - - if (var30 == 0 || var58 == var30) { - var30 = 0; - } else { - var76 = FloorOverlayDefinition.getFile(-1 + var30); - if (var76.aBoolean2092 && -1 != var76.anInt2103) { - var33 = var5[1 + var13][var14 - 1]; - var77 = var8[1 + var13][var14 + -1]; - var36 = anIntArray2642[(1 + var33 & 3) + 4 * var77]; - var35 = anIntArray2642[var77 * 4 + (var33 & 3)]; - if (TextureOperation8.aBooleanArrayArray3468[var36][1] && TextureOperation8.aBooleanArrayArray3468[var35][0]) { - var30 = 0; - } else { - for (var83 = 0; 8 > var83; ++var83) { - if (var61 == var83) { - var64[var61++] = var30; - break; - } - - if (var64[var83] == var30) { - break; - } - } - } - } else { - var30 = 0; - } - } - - if (var73 == 0 || var73 == var58) { - var73 = 0; - } else { - var76 = FloorOverlayDefinition.getFile(-1 + var73); - if (var76.aBoolean2092 && var76.anInt2103 != -1) { - var77 = var8[1 + var13][1 + var14]; - var33 = var5[1 + var13][var14 + 1]; - var35 = anIntArray2642[(3 & var33 - -2) + 4 * var77]; - var36 = anIntArray2642[(var33 + 1 & 3) + 4 * var77]; - if (TextureOperation8.aBooleanArrayArray3468[var35][1] && TextureOperation8.aBooleanArrayArray3468[var36][0]) { - var73 = 0; - } else { - for (var83 = 0; var83 < 8; ++var83) { - if (var61 == var83) { - var64[var61++] = var73; - break; - } - - if (var64[var83] == var73) { - break; - } - } - } - } else { - var73 = 0; - } - } - - int var78; - if (var24 != 0 && var58 != var24) { - var76 = FloorOverlayDefinition.getFile(var24 - 1); - if (var76.aBoolean2092 && -1 != var76.anInt2103) { - var65 = anIntArray2642[var8[var13][var14 - -1] * 4 + (2 + var5[var13][var14 - -1] & 3)]; - - for (var78 = 0; 8 > var78; ++var78) { - if (var61 == var78) { - var64[var61++] = var24; - break; - } - - if (var24 == var64[var78]) { - break; - } - } - } - } - - if (var25 != 0 && var58 != var25) { - var76 = FloorOverlayDefinition.getFile(var25 + -1); - if (var76.aBoolean2092 && var76.anInt2103 != -1) { - var21 = anIntArray2642[(3 & var5[var13 - 1][var14] - -3) + 4 * var8[var13 + -1][var14]]; - - for (var78 = 0; var78 < 8; ++var78) { - if (var78 == var61) { - var64[var61++] = var25; - break; - } - - if (var25 == var64[var78]) { - break; - } - } - } - } - - if (var26 != 0 && var58 != var26) { - var76 = FloorOverlayDefinition.getFile(var26 - 1); - if (var76.aBoolean2092 && -1 != var76.anInt2103) { - var22 = anIntArray2642[(var5[var13][var14 + -1] & 3) + 4 * var8[var13][-1 + var14]]; - - for (var78 = 0; var78 < 8; ++var78) { - if (var78 == var61) { - var64[var61++] = var26; - break; - } - - if (var64[var78] == var26) { - break; - } - } - } - } - - if (var27 != 0 && var27 != var58) { - var76 = FloorOverlayDefinition.getFile(var27 + -1); - if (var76.aBoolean2092 && var76.anInt2103 != -1) { - var23 = anIntArray2642[4 * var8[1 + var13][var14] - -(3 & var5[var13 + 1][var14] - -1)]; - - for (var78 = 0; var78 < 8; ++var78) { - if (var61 == var78) { - var64[var61++] = var27; - break; - } - - if (var27 == var64[var78]) { - break; - } - } - } - } - - for (var74 = 0; var61 > var74; ++var74) { - var78 = var64[var74]; - var79 = TextureOperation8.aBooleanArrayArray3468[var78 == var24 ? var65 : 0]; - var75 = TextureOperation8.aBooleanArrayArray3468[var25 == var78 ? var21 : 0]; - var81 = TextureOperation8.aBooleanArrayArray3468[var26 == var78 ? var22 : 0]; - boolean[] var84 = TextureOperation8.aBooleanArrayArray3468[var78 != var27 ? 0 : var23]; - FloorOverlayDefinition var82 = FloorOverlayDefinition.getFile(-1 + var78); - Class3_Sub11 var39 = method2052(var12, var82); - int var40 = TextureOperation34.method190(var82.anInt2095, var82.anInt2103, (byte) -76, var6[var13][var14]) << 8 | 255; - byte var44 = 6; - int var41 = 255 | TextureOperation34.method190(var82.anInt2095, var82.anInt2103, (byte) -85, var6[1 + var13][var14]) << 8; - int var42 = TextureOperation34.method190(var82.anInt2095, var82.anInt2103, (byte) 123, var6[var13 - -1][var14 - -1]) << 8 | 255; - int var43 = 255 | TextureOperation34.method190(var82.anInt2095, var82.anInt2103, (byte) 106, var6[var13][var14 - -1]) << 8; - boolean var48 = var29 != var78 && var81[0] && var75[1]; - boolean var47 = var73 != var78 && var79[0] && var84[1]; - boolean var46 = var28 != var78 && var75[0] && var79[1]; - int var85 = var44 + -2 + var79.length; - boolean var49 = var30 != var78 && var84[0] && var81[1]; - var85 += -2 + var75.length; - var85 += var81.length - 2; - var85 += -2 + var84.length; - int var50 = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -127, 0, true, var39, var1, var14, 64, var0, 64); - int[] var45 = new int[var85]; - var44 = 0; - int var51 = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -112, 0, var46, var39, var1, var14, 0, var0, 128); - int var52 = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -119, 0, var47, var39, var1, var14, 128, var0, 128); - int var53 = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -127, 0, var48, var39, var1, var14, 0, var0, 0); - int var54 = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -114, 0, var49, var39, var1, var14, 128, var0, 0); - var85 = var44 + 1; - var45[var44] = var50; - var45[var85++] = var52; - if (var79.length > 2) { - var45[var85++] = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -117, 0, var79[2], var39, var1, var14, 64, var0, 128); - } - - var45[var85++] = var51; - if (var75.length > 2) { - var45[var85++] = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -128, 0, var75[2], var39, var1, var14, 0, var0, 64); - } - - var45[var85++] = var53; - if (var81.length > 2) { - var45[var85++] = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -112, 0, var81[2], var39, var1, var14, 64, var0, 0); - } - - var45[var85++] = var54; - if (var84.length > 2) { - var45[var85++] = Class121.method1734(var43, 0.0F, var40, var41, null, var2, var13, var4, var42, (byte) -127, 0, var84[2], var39, var1, var14, 128, var0, 64); - } - - var45[var85++] = var52; - var39.method150(var15, var13, var14, var45, null, true); - } - } - } - } - - for (var56 = (Class3_Sub11) var12.first(); null != var56; var56 = (Class3_Sub11) var12.next()) { - if (var56.anInt2343 == 0) { - var56.unlink(); - } else { - var56.method148(); - } - } - - var13 = var12.size(); - Class3_Sub11[] var57 = new Class3_Sub11[var13]; - long[] var59 = new long[var13]; - var12.values(var57); - - for (var16 = 0; var16 < var13; ++var16) { - var59[var16] = var57[var16].linkableKey; - } - - PacketParser.method824(var59, var57, -27); - return var57; - } catch (RuntimeException var55) { - throw ClientErrorException.clientError(var55, "a.A(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + (var4 != null ? "{...}" : "null") + ',' + (var5 != null ? "{...}" : "null") + ',' + (var6 != null ? "{...}" : "null") + ',' + 0 + ',' + (var8 != null ? "{...}" : "null") + ',' + (var9 != null ? "{...}" : "null") + ',' + (var10 != null ? "{...}" : "null") + ',' + (var11 != null ? "{...}" : "null") + ')'); - } - } - - static void worldMapMinimap(int var0, int var1, int var2, int var4) { - try { - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var4, var2 + var0, var1 + var4); - Toolkit.OPENGL_TOOLKIT.method934(var0, var4, var2, var1, 0); //Specific openGL call because set Clipping is not migrated - } else { - Class74.setClipping(var0, var4, var2 + var0, var4 + var1); - Toolkit.JAVA_TOOLKIT.method934(var0, var4, var2, var1, 0); //Specific JTK call because set Clipping is not migrated - } - - if (WorldMap.anInt2737 >= 100) { - if (null == Class36.aAbstractSprite_637 || var2 != Class36.aAbstractSprite_637.width || Class36.aAbstractSprite_637.height != var1) { - SoftwareSprite var5 = new SoftwareSprite(var2, var1); - Class74.setBuffer(var5.anIntArray4081, var2, var1); - Unsorted.method523(var2, 0, Class23.anInt455, 0, 0, Class108.anInt1460, var1, 0); - if (HDToolKit.highDetail) { - Class36.aAbstractSprite_637 = new HDSprite(var5); - } else { - Class36.aAbstractSprite_637 = var5; - } - - if (HDToolKit.highDetail) { - Toolkit.JAVA_TOOLKIT.resetBuffer(); - } else { - Unsorted.aClass158_3009.method2182(); - } - } - - Class36.aAbstractSprite_637.method635(var0, var4); - - int var6 = var1 * Class60.anInt934 / Class108.anInt1460 + var4; - int var8 = Class17.anInt410 * var1 / Class108.anInt1460; - int var15 = var0 + var2 * Class60.anInt930 / Class23.anInt455; - int var7 = var2 * Class49.anInt817 / Class23.anInt455; - int var9 = 16711680; - if (Class158.paramGameTypeID == 1) { - var9 = 16777215; - } - Toolkit.getActiveToolkit().drawRect(var15, var6, var7, var8, var9, 255); - if (AbstractSprite.anInt3704 > 0) { - int var10; - if (Class3_Sub28_Sub8.anInt3611 > 10) { - var10 = (-Class3_Sub28_Sub8.anInt3611 + 20) * 25; - } else { - var10 = 25 * Class3_Sub28_Sub8.anInt3611; - } - - for (Class3_Sub23 var11 = (Class3_Sub23) Class84.aLinkedList_1162.method1222(); var11 != null; var11 = (Class3_Sub23) Class84.aLinkedList_1162.method1221()) { - if (Class8.anInt101 == var11.anInt2532) { - int var13 = var4 - -(var11.anInt2539 * var1 / Class108.anInt1460); - int var12 = var2 * var11.anInt2531 / Class23.anInt455 + var0; - Toolkit.getActiveToolkit().fillRect(var12 - 2, var13 - 2, 4, 4, 16776960, var10); - } - } - } - - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "a.D(" + var0 + ',' + var1 + ',' + var2 + ',' + 19481 + ',' + var4 + ')'); - } - } - - static Class3_Sub11 method2052(HashTable var0, FloorOverlayDefinition var2) { - try { - long var3 = ((long) var2.anInt2095 - -1 << 16) + var2.anInt2090 + (((long) var2.anInt2100 << 56) - -((long) var2.anInt2094 << 32)); - Class3_Sub11 var5 = (Class3_Sub11) var0.get(var3); - if (null == var5) { - var5 = new Class3_Sub11(var2.anInt2095, (float) var2.anInt2090, true, false, var2.anInt2094); - var0.put(var3, var5); - } - - return var5; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "tk.J(" + (var0 != null ? "{...}" : "null") + ',' + false + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class101.java b/Client/src/main/java/org/runite/client/Class101.java deleted file mode 100644 index bb1cad4d8..000000000 --- a/Client/src/main/java/org/runite/client/Class101.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.runite.client; - -final class Class101 { - - static CacheIndex csConfigFileRAM; - static Class3_Sub24_Sub4 aClass3_Sub24_Sub4_1421; - static Class30 aClass30_1422; - static CacheIndex aClass153_1423; - static int anInt1425 = 0; - static int[] anIntArray1426; - static int anInt1427; - static short[][] aShortArrayArray1429; - - - static void method1607(int var0, int var1, boolean var2, int[] var3, int[] var4) { - try { - if (var2) { - method1608((byte) 106); - } - - if (var1 > var0) { - int var5 = (var1 + var0) / 2; - int var6 = var0; - int var7 = var3[var5]; - var3[var5] = var3[var1]; - var3[var1] = var7; - int var8 = var4[var5]; - var4[var5] = var4[var1]; - var4[var1] = var8; - - for (int var9 = var0; var9 < var1; ++var9) { - if ((var9 & 1) + var7 < var3[var9]) { - int var10 = var3[var9]; - var3[var9] = var3[var6]; - var3[var6] = var10; - int var11 = var4[var9]; - var4[var9] = var4[var6]; - var4[var6++] = var11; - } - } - - var3[var1] = var3[var6]; - var3[var6] = var7; - var4[var1] = var4[var6]; - var4[var6] = var8; - method1607(var0, var6 - 1, var2, var3, var4); - method1607(var6 + 1, var1, var2, var3, var4); - } - - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "nj.C(" + var0 + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ')'); - } - } - - public static void method1608(byte var0) { - try { - CacheIndex.npcConfigIndex = null; - Class136.aLinkedList_1424 = null; - aShortArrayArray1429 = null; - anIntArray1426 = null; - aClass3_Sub24_Sub4_1421 = null; - aClass30_1422 = null; - aClass153_1423 = null; - if (var0 != 110) { - method1607(46, 78, true, null, null); - } - - csConfigFileRAM = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "nj.A(" + var0 + ')'); - } - } - - static void method1609() { - try { - Unsorted.anShaderInterfaceArray70 = null; - - Class88.method1455(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "nj.B(" + 90 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class102.java b/Client/src/main/java/org/runite/client/Class102.java deleted file mode 100644 index 96c4ffea0..000000000 --- a/Client/src/main/java/org/runite/client/Class102.java +++ /dev/null @@ -1,576 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.NodeCache; - -public final class Class102 implements Interface2 { - - static int[] anIntArray2125; - static int anInt2136; - public static Player player; - private final boolean[] aBooleanArray2122; - private final CacheIndex aClass153_2123; - private final boolean[] aBooleanArray2124; - private final byte[] aByteArray2126; - private final CacheIndex aClass153_2127; - private final boolean[] aBooleanArray2128; - private final byte[] aByteArray2129; - private final boolean[] aBooleanArray2135; - private final short[] aShortArray2137; - private final byte[] aByteArray2143; - private final byte[] aByteArray2144; - private boolean aBoolean2134; - private NodeCache aClass47_2138; - private int anInt2139; - private NodeCache aClass47_2142; - - - Class102(CacheIndex var1, CacheIndex var2, CacheIndex var3, boolean var5) { - try { - this.aClass153_2123 = var3; - this.aBoolean2134 = var5; - this.anInt2139 = 20; - this.aClass153_2127 = var1; - this.aClass47_2142 = new NodeCache(this.anInt2139); - if (HDToolKit.highDetail) { - this.aClass47_2138 = new NodeCache(this.anInt2139); - } else { - this.aClass47_2138 = null; - } - - DataBuffer var6 = new DataBuffer(var2.getFile(0, 0)); - int var7 = var6.readUnsignedShort(); - this.aByteArray2143 = new byte[var7]; - this.aByteArray2129 = new byte[var7]; - this.aByteArray2126 = new byte[var7]; - this.aShortArray2137 = new short[var7]; - this.aByteArray2144 = new byte[var7]; - this.aBooleanArray2128 = new boolean[var7]; - this.aBooleanArray2122 = new boolean[var7]; - this.aBooleanArray2124 = new boolean[var7]; - boolean[] aBooleanArray2133 = new boolean[var7]; - this.aBooleanArray2135 = new boolean[var7]; - - int var8; - for (var8 = 0; var8 < var7; ++var8) { - aBooleanArray2133[var8] = 1 == var6.readUnsignedByte(); - } - - for (var8 = 0; var8 < var7; ++var8) { - if (aBooleanArray2133[var8]) { - this.aBooleanArray2124[var8] = var6.readUnsignedByte() == 1; - } - } - - for (var8 = 0; var7 > var8; ++var8) { - if (aBooleanArray2133[var8]) { - this.aBooleanArray2128[var8] = 1 == var6.readUnsignedByte(); - } - } - - for (var8 = 0; var7 > var8; ++var8) { - if (aBooleanArray2133[var8]) { - this.aBooleanArray2122[var8] = var6.readUnsignedByte() == 1; - } - } - - for (var8 = 0; var8 < var7; ++var8) { - if (aBooleanArray2133[var8]) { - this.aBooleanArray2135[var8] = var6.readUnsignedByte() == 1; - } - } - - for (var8 = 0; var7 > var8; ++var8) { - if (aBooleanArray2133[var8]) { - this.aByteArray2126[var8] = var6.readSignedByte(); - } - } - - for (var8 = 0; var7 > var8; ++var8) { - if (aBooleanArray2133[var8]) { - this.aByteArray2129[var8] = var6.readSignedByte(); - } - } - - for (var8 = 0; var8 < var7; ++var8) { - if (aBooleanArray2133[var8]) { - this.aByteArray2144[var8] = var6.readSignedByte(); - } - } - - for (var8 = 0; var7 > var8; ++var8) { - if (aBooleanArray2133[var8]) { - this.aByteArray2143[var8] = var6.readSignedByte(); - } - } - - for (var8 = 0; var7 > var8; ++var8) { - if (aBooleanArray2133[var8]) { - this.aShortArray2137[var8] = (short) var6.readUnsignedShort(); - } - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "nk.(" + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ',' + 20 + ',' + var5 + ')'); - } - } - - static void method1611(int var0, boolean var1) { - try { - int var2; - NPC var3; - int var6; - int var7; - int var8; - int var9; - int var10; - for (var2 = 0; var2 < Class163.localNPCCount; ++var2) { - var3 = NPC.npcs[AudioThread.localNPCIndexes[var2]]; - if (null != var3 && var3.hasDefinitions() && var3.definition.aBoolean1263 == var1 && var3.definition.method1472()) { - int var4 = var3.getSize(); - int var5; - if (1 != var4) { - if (((1 & var4) != 0 || (127 & var3.xAxis) == 0 && 0 == (127 & var3.zAxis)) && ((var4 & 1) != 1 || (127 & var3.xAxis) == 64 && 64 == (127 & var3.zAxis))) { - var5 = var3.xAxis + -(var4 * 64) >> 7; - var6 = -(var4 * 64) + var3.zAxis >> 7; - var7 = var3.getSize() + var5; - if (var5 < 0) { - var5 = 0; - } - - if (var7 > 104) { - var7 = 104; - } - - var8 = var6 + var3.getSize(); - if (var6 < 0) { - var6 = 0; - } - - if (104 < var8) { - var8 = 104; - } - - for (var9 = var5; var9 < var7; ++var9) { - for (var10 = var6; var8 > var10; ++var10) { - ++Class163_Sub1_Sub1.anIntArrayArray4010[var9][var10]; - } - } - } - } else if ((127 & var3.xAxis) == 64 && (var3.zAxis & 127) == 64) { - var5 = var3.xAxis >> 7; - var6 = var3.zAxis >> 7; - if (var5 >= 0 && var5 < 104 && var6 >= 0 && var6 < 104) { - ++Class163_Sub1_Sub1.anIntArrayArray4010[var5][var6]; - } - } - } - } - - label200: - for (var2 = 0; Class163.localNPCCount > var2; ++var2) { - var3 = NPC.npcs[AudioThread.localNPCIndexes[var2]]; - long var15 = (long) AudioThread.localNPCIndexes[var2] << 32 | 536870912L; - if (var3 != null && var3.hasDefinitions() && !var3.definition.aBoolean1263 == !var1 && var3.definition.method1472()) { - var6 = var3.getSize(); - if (var6 != 1) { - if ((var6 & 1) == 0 && (var3.xAxis & 127) == 0 && (127 & var3.zAxis) == 0 || (var6 & 1) == 1 && (var3.xAxis & 127) == 64 && (127 & var3.zAxis) == 64) { - var7 = -(64 * var6) + var3.xAxis >> 7; - var8 = -(var6 * 64) + var3.zAxis >> 7; - var10 = var8 - -var6; - if (var8 < 0) { - var8 = 0; - } - - boolean var11 = true; - var9 = var7 + var6; - if (var10 > 104) { - var10 = 104; - } - - if (var7 < 0) { - var7 = 0; - } - - if (var9 > 104) { - var9 = 104; - } - - int var12; - int var13; - for (var12 = var7; var12 < var9; ++var12) { - for (var13 = var8; var10 > var13; ++var13) { - if (Class163_Sub1_Sub1.anIntArrayArray4010[var12][var13] <= 1) { - var11 = false; - break; - } - } - } - - if (var11) { - var12 = var7; - - while (true) { - if (var12 >= var9) { - continue label200; - } - - for (var13 = var8; var13 < var10; ++var13) { - --Class163_Sub1_Sub1.anIntArrayArray4010[var12][var13]; - } - - ++var12; - } - } - } - } else if ((127 & var3.xAxis) == 64 && (127 & var3.zAxis) == 64) { - var7 = var3.xAxis >> 7; - var8 = var3.zAxis >> 7; - if (0 > var7 || var7 >= 104 || var8 < 0 || var8 >= 104) { - continue; - } - - if (1 < Class163_Sub1_Sub1.anIntArrayArray4010[var7][var8]) { - --Class163_Sub1_Sub1.anIntArrayArray4010[var7][var8]; - continue; - } - } - - if (!var3.definition.aBoolean1270) { - var15 |= Long.MIN_VALUE; - } - - var3.anInt2831 = Class121.method1736(WorldListCountry.localPlane, 1, var3.xAxis, var3.zAxis); - Class20.method907(WorldListCountry.localPlane, var3.xAxis, var3.zAxis, var3.anInt2831, -64 + 64 * var6 + 60, var3, var3.anInt2785, var15, var3.aBoolean2810); - } - } - - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "nk.V(" + var0 + ',' + var1 + ')'); - } - } - - public static void method1612(int var0) { - try { - player = null; - if (var0 != -11565) { - method1614(false, null, null); - } - - anIntArray2125 = null; - Sprites.aSoftwareSpriteArray2140 = null; - AudioHandler.soundEffects = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "nk.W(" + var0 + ')'); - } - } - - static void method1614(boolean var0, long[] var1, int[] var2) { - try { - Class44.method1069(var1, 0, -1 + var1.length, var2); - if (!var0) { - method1612(103); - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.Q(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - static void method1617(int var0, int var1, int var2, int var3, int var4) { - try { - if (Class57.anInt902 >= var4 && Class159.anInt2020 <= var2) { - boolean var6; - if (Class101.anInt1425 <= var1) { - if (Class3_Sub28_Sub18.anInt3765 >= var1) { - var6 = true; - } else { - var6 = false; - var1 = Class3_Sub28_Sub18.anInt3765; - } - } else { - var6 = false; - var1 = Class101.anInt1425; - } - - boolean var7; - if (var3 < Class101.anInt1425) { - var3 = Class101.anInt1425; - var7 = false; - } else if (Class3_Sub28_Sub18.anInt3765 < var3) { - var3 = Class3_Sub28_Sub18.anInt3765; - var7 = false; - } else { - var7 = true; - } - - if (Class159.anInt2020 > var4) { - var4 = Class159.anInt2020; - } else { - TextureOperation18.method282(Class38.anIntArrayArray663[var4++], var1, -66, var3, var0); - } - - if (var2 <= Class57.anInt902) { - TextureOperation18.method282(Class38.anIntArrayArray663[var2--], var1, -54, var3, var0); - } else { - var2 = Class57.anInt902; - } - - int var8; - if (var6 && var7) { - for (var8 = var4; var8 <= var2; ++var8) { - int[] var9 = Class38.anIntArrayArray663[var8]; - var9[var1] = var9[var3] = var0; - } - } else if (var6) { - for (var8 = var4; var2 >= var8; ++var8) { - Class38.anIntArrayArray663[var8][var1] = var0; - } - } else if (var7) { - for (var8 = var4; var8 <= var2; ++var8) { - Class38.anIntArrayArray663[var8][var3] = var0; - } - } - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "nk.R(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + (byte) 29 + ')'); - } - } - - final void method1610(int var2) { - try { - - for (Class3_Sub28_Sub20 var3 = (Class3_Sub28_Sub20) this.aClass47_2142.first(); null != var3; var3 = (Class3_Sub28_Sub20) this.aClass47_2142.next()) { - if (var3.aBoolean3797) { - var3.method723(var2); - var3.aBoolean3797 = false; - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.P(" + true + ',' + var2 + ')'); - } - } - - public final int[] method16(int var1, int var2) { - try { - if (var1 != 64) { - this.method12(105, -92); - } - - Class3_Sub28_Sub20 var3 = this.method1613(var2); - return null == var3 ? null : var3.method720(this.aBoolean2134 || this.aBooleanArray2122[var2], this, this.aClass153_2123); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.F(" + var1 + ',' + var2 + ')'); - } - } - - public final void method8(int var1, boolean var2) { - try { - Unsorted.method551(255 & this.aByteArray2143[var1], this.aByteArray2144[var1] & 0xFF); - if (var2) { - boolean var3 = false; - Class3_Sub28_Sub20 var4 = this.method1613(var1); - if (var4 != null) { - var3 = var4.method719(this.aClass153_2123, this, this.aBoolean2134 || this.aBooleanArray2122[var1]); - } - - if (!var3) { - Class3_Sub28_Sub18 var6 = this.method1615(var1); - var6.method712(); - } - - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "nk.G(" + var1 + ',' + var2 + ')'); - } - } - - private Class3_Sub28_Sub20 method1613(int var1) { - try { - - Class3_Sub28_Sub20 var3 = (Class3_Sub28_Sub20) this.aClass47_2142.get(var1); - if (null == var3) { - byte[] var4 = this.aClass153_2127.getFile(var1, 0); - if (null == var4) { - return null; - } else { - DataBuffer var5 = new DataBuffer(var4); - var3 = new Class3_Sub28_Sub20(var5); - this.aClass47_2142.put(var1, var3); - return var3; - } - } else { - return var3; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "nk.T(" + var1 + ',' + 1 + ')'); - } - } - - public final int method9(int var1, boolean var2) { - try { - return var2 ? -63 : 255 & this.aByteArray2143[var1]; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.D(" + var1 + ',' + ')'); - } - } - - public final int method18(int var1, int var2) { - try { - if (var2 != 255) { - method1612(-48); - } - - return 255 & this.aByteArray2144[var1]; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.B(" + var1 + ',' + var2 + ')'); - } - } - - public final int method10(int var1, int var2) { - try { - return this.aByteArray2126[var2] & 0xFF; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.C(" + var1 + ',' + var2 + ')'); - } - } - - public final boolean method7(byte var1, int var2) { - try { - if (var1 != 88) { - this.method19(-99, -37); - } - - return this.aBooleanArray2128[var2]; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.M(" + var1 + ',' + var2 + ')'); - } - } - - private Class3_Sub28_Sub18 method1615(int var2) { - try { - Class3_Sub28_Sub18 var4 = (Class3_Sub28_Sub18) this.aClass47_2138.get(var2); - if (null == var4) { - var4 = new Class3_Sub28_Sub18(this.aShortArray2137[var2] & 65535); - this.aClass47_2138.put(var2, var4); - } - return var4; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "nk.U(" + 48 + ',' + var2 + ')'); - } - } - - public final boolean method11(int var1, int var2) { - try { - if (var1 < 0) { - this.method7((byte) 68, -47); - } - - Class3_Sub28_Sub20 var3 = this.method1613(var2); - return null != var3 && var3.method722(this, this.aClass153_2123); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.H(" + var1 + ',' + var2 + ')'); - } - } - - final void method1616(boolean var1) { - try { - this.aBoolean2134 = var1; - this.method1618(); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.S(" + var1 + ',' + -17830 + ')'); - } - } - - public final boolean method12(int var1, int var2) { - try { - if (var2 != -65) { - method1614(false, null, null); - } - - return this.aBooleanArray2135[var1]; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.I(" + var1 + ',' + var2 + ')'); - } - } - - public final int method19(int var1, int var2) { - try { - return 255 & this.aByteArray2129[var2]; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.A(" + var1 + ',' + var2 + ')'); - } - } - - final void method1618() { - try { - this.aClass47_2142.clear(); - if (null != this.aClass47_2138) { - this.aClass47_2138.clear(); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nk.O(" + 0 + ')'); - } - } - - public final int method15(int var1, int var2) { - try { - if (var2 != 65535) { - this.method11(-82, -17); - } - - return 65535 & this.aShortArray2137[var1]; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.E(" + var1 + ',' + var2 + ')'); - } - } - - public final boolean method14(byte var1, int var2) { - try { - return var1 >= -97 || (this.aBoolean2134 || this.aBooleanArray2122[var2]); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.J(" + var1 + ',' + var2 + ')'); - } - } - - final void method1619(int var1) { - try { - - this.anInt2139 = var1; - this.aClass47_2142 = new NodeCache(this.anInt2139); - if (HDToolKit.highDetail) { - this.aClass47_2138 = new NodeCache(this.anInt2139); - } else { - this.aClass47_2138 = null; - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.N(" + var1 + ',' + -1 + ')'); - } - } - - public final boolean method17(int var1, int var2) { - try { - return this.aBooleanArray2124[var1]; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nk.K(" + var1 + ',' + var2 + ')'); - } - } - - public final int[] method13(int var1, boolean var2, float var3) { - try { - Class3_Sub28_Sub20 var4 = this.method1613(var1); - if (null == var4) { - return null; - } else { - var4.aBoolean3797 = var2; - return var4.method718(this, var3, this.aClass153_2123, this.aBoolean2134 || this.aBooleanArray2122[var1]); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "nk.L(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class106.java b/Client/src/main/java/org/runite/client/Class106.java deleted file mode 100644 index d6913591f..000000000 --- a/Client/src/main/java/org/runite/client/Class106.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.runite.client; - -final class Class106 { - - static boolean aBoolean1441 = true; - static int rightMargin = 0; - static int anInt1446 = 0; - static boolean paramUserUsingInternetExplorer = false; - int anInt1447; - int anInt1449; - int anInt1450; - - static void method1642(RSString var1) { - try { - if (null != PacketParser.aClass3_Sub19Array3694) { - - long var3 = var1.toLong(); - int var2 = 0; - if (var3 != 0L) { - while (PacketParser.aClass3_Sub19Array3694.length > var2 && var3 != PacketParser.aClass3_Sub19Array3694[var2].linkableKey) { - ++var2; - } - - if (var2 < PacketParser.aClass3_Sub19Array3694.length && null != PacketParser.aClass3_Sub19Array3694[var2]) { - TextureOperation12.outgoingBuffer.putOpcode(162); - TextureOperation12.outgoingBuffer.writeLong(PacketParser.aClass3_Sub19Array3694[var2].linkableKey); - } - } - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "od.C(" + 3803 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class107.java b/Client/src/main/java/org/runite/client/Class107.java deleted file mode 100644 index 8ca5995aa..000000000 --- a/Client/src/main/java/org/runite/client/Class107.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.runite.client; - -final class Class107 { - - static RSInterface aClass11_1453; - static CacheIndex aClass153_878; - - - static void method1645(CacheIndex var0, CacheIndex var1) { - try { - KeyboardListener.aClass153_1916 = var1; - int var4 = (int) (21.0D * Math.random()) - 10; - aClass153_878 = var0; - int var5 = (int) (21.0D * Math.random()) - 10; - aClass153_878.getFileAmount(34); - int var3 = (int) (Math.random() * 21.0D) + -10; - int var6 = -20 + (int) (41.0D * Math.random()); - Class158.anInt2015 = var6 + var5; - Class46.anInt740 = var4 + var6; - Class102.anInt2136 = var6 + var3; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "og.F(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + (byte) -67 + ')'); - } - } - - static void method1647(int var1, int var2, Class140_Sub4 var3, int var4, int var5, int var6) { - try { - - Unsorted.method1724(var6, var2, var3.zAxis, var5, var1, var3.xAxis, (byte) -85, var4); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "og.A(" + (byte) 122 + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - static void method1648(CacheIndex var0) { - try { - Class101.csConfigFileRAM = var0; - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "og.B(" + (var0 != null ? "{...}" : "null") + ',' + 255 + ')'); - } - } - - static void method1649(int var0, int var1) { - try { - if (var1 <= -65) { - InterfaceWidget var2 = InterfaceWidget.getWidget(10, var0); - var2.a(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "og.C(" + var0 + ',' + var1 + ')'); - } - } - -// static void method1650() { -// try { -// Class61.aReferenceCache_939.clearSoftReferences(); -// -// } catch (RuntimeException var2) { -// throw ClientErrorException.clientError(var2, "og.G(" + 21 + ')'); -// } -// } - - static int method1651(int var1, int var2) { - try { - int var3; - if (var1 > var2) { - var3 = var2; - var2 = var1; - var1 = var3; - } - - while (var1 != 0) { - var3 = var2 % var1; - var2 = var1; - var1 = var3; - } - - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "og.D(" + 19067 + ',' + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class108.java b/Client/src/main/java/org/runite/client/Class108.java deleted file mode 100644 index 639db0458..000000000 --- a/Client/src/main/java/org/runite/client/Class108.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class Class108 { - - static int anInt1460; - - - static void method1652(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - - if (Class101.anInt1425 <= var5 && Class3_Sub28_Sub18.anInt3765 >= var5 && var0 >= Class101.anInt1425 && Class3_Sub28_Sub18.anInt3765 >= var0 && Class101.anInt1425 <= var6 && var6 <= Class3_Sub28_Sub18.anInt3765 && var1 >= Class101.anInt1425 && Class3_Sub28_Sub18.anInt3765 >= var1 && var4 >= Class159.anInt2020 && Class57.anInt902 >= var4 && Class159.anInt2020 <= var7 && Class57.anInt902 >= var7 && var2 >= Class159.anInt2020 && var2 <= Class57.anInt902 && Class159.anInt2020 <= var3 && Class57.anInt902 >= var3) { - Class3_Sub5.method114(var2, var8, var7, var6, var1, var3, var4, var0, var5); - } else { - Class95.method1583(var5, var0, var7, var8, var3, var2, var1, var6, var4); - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "oi.C(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + 0 + ')'); - } - } - - static RSString method1653(int var0) { - try { - - return RSString.stringCombiner(new RSString[]{RSString.stringAnimator(255 & var0 >> 24), TextCore.aClass94_4023, RSString.stringAnimator((var0 & 16712751) >> 16), TextCore.aClass94_4023, RSString.stringAnimator(255 & var0 >> 8), TextCore.aClass94_4023, RSString.stringAnimator(var0 & 0xFF)}); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "oi.F(" + var0 + ',' + 0 + ')'); - } - } - -// static void method1654(int var0) { //Separated into method1656 + clearClientCacheMemory -// try { -// Class140_Sub4.aReferenceCache_2792.clearSoftReferences(); -// } catch (RuntimeException var2) { -// throw ClientErrorException.clientError(var2, "oi.E(" + var0 + ')'); -// } -// } - - static void method1656(CacheIndex var0, byte var1) { - try { - if (!Class140_Sub2.aBoolean2713) { - if (HDToolKit.highDetail) { - Class22.method932(); - } else { - Class74.method1320(); - } - - Class40.aAbstractSprite_680 = Class75_Sub2.method1344(var0, Class154.anInt1966); - int var2 = Class140_Sub7.canvasHeight; - int var3 = var2 * 956 / 503; - Objects.requireNonNull(Class40.aAbstractSprite_680).method639((Class23.canvasWidth + -var3) / 2, 0, var3, var2); - SequenceDefinition.aClass109_1856 = InterfaceWidget.a(CSConfigCachefile.anInt1124, var0); - Objects.requireNonNull(SequenceDefinition.aClass109_1856).method1667(Class23.canvasWidth / 2 + -(SequenceDefinition.aClass109_1856.width / 2), 18); - Class140_Sub2.aBoolean2713 = true; - if (var1 > -50) { - Class140_Sub4.aReferenceCache_2792.clearSoftReferences(); - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "oi.B(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ')'); - } - } - - static void method1657(int var0) { - try { - InterfaceWidget var2 = InterfaceWidget.getWidget(5, var0); - var2.a(); - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "oi.J(" + var0 + ',' + -903 + ')'); - } - } - - static void method1658(int[] var1, Object[] var2) { - try { - Class25.method956(var2, var1.length - 1, var1, 74, 0); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "oi.I(" + 21 + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - static void method1659() { - try { - LinkedList.aReferenceCache_939.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "oi.G(" + 16712751 + ')'); - } - } - - static void method1661(CacheIndex var1, CacheIndex var2) { - try { - Sprites.aBoolean337 = true; - Unsorted.aClass153_1043 = var2; - - Class85.aClass153_1171 = var1; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "oi.D(" + 2 + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ',' + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class110.java b/Client/src/main/java/org/runite/client/Class110.java deleted file mode 100644 index 435c92f4b..000000000 --- a/Client/src/main/java/org/runite/client/Class110.java +++ /dev/null @@ -1,496 +0,0 @@ -package org.runite.client; - - -import java.util.Objects; - -final class Class110 { - - static int anInt1472 = 0; - static int[] anIntArray2386 = new int[]{1, -1, -1, 1}; - - - static void method1681(int var0) { - try { - if (LoginHandler.loginStage == 5) { - if (var0 != -1) { - TextCore.COMMAND_BREAK_JS5_SERVER_CONNECTION = null; - } - - LoginHandler.loginStage = 6; - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "p.A(" + var0 + ')'); - } - } - - static void method1683(int var0, boolean var1, int var2, boolean var3, Class91 var4, int objectId, int type, int var7, int var9, int var10) { - try { - var2 %= 4; - if (var1 && !NPC.method1986(103) && 0 == (2 & Unsorted.aByteArrayArrayArray113[0][var7][var9])) { - if (0 != (16 & Unsorted.aByteArrayArrayArray113[var2][var7][var9])) { - return; - } - - if (Class140_Sub3.anInt2745 != PacketParser.method823(var9, var7, (byte) 50 ^ -127, var2)) { - return; - } - } - - if (Class85.anInt1174 > var2) { - Class85.anInt1174 = var2; - } - - ObjectDefinition def = ObjectDefinition.getObjectDefinition(objectId); - if (!HDToolKit.highDetail || !def.aBoolean1530) { - int var12; - int var13; - if (var10 == 1 || var10 == 3) { - var13 = def.SizeX; - var12 = def.SizeY; - } else { - var12 = def.SizeX; - var13 = def.SizeY; - } - - int var14; - int var15; - if (var7 + var12 <= 104) { - var14 = var7 + (var12 >> 1); - var15 = var7 - -(1 + var12 >> 1); - } else { - var15 = 1 + var7; - var14 = var7; - } - - int var17; - int var16; - if (var13 + var9 > 104) { - var16 = var9; - var17 = var9 - -1; - } else { - var16 = (var13 >> 1) + var9; - var17 = var9 + (var13 + 1 >> 1); - } - - int[][] var18 = Class44.anIntArrayArrayArray723[var0]; - int var20 = (var12 << 6) + (var7 << 7); - int var21 = (var13 << 6) + (var9 << 7); - int var19 = var18[var14][var17] + var18[var15][var16] + var18[var14][var16] + var18[var15][var17] >> 2; - int var22 = 0; - int[][] var23; - if (HDToolKit.highDetail && var0 != 0) { - var23 = Class44.anIntArrayArrayArray723[0]; - var22 = var19 - (var23[var15][var17] + var23[var15][var16] + var23[var14][var16] + var23[var14][var17] >> 2); - } - - var23 = null; - long var24 = 1073741824 | var7 | var9 << 7 | type << 14 | var10 << 20; - if (var3) { - var23 = Class58.anIntArrayArrayArray914[0]; - } else if (var0 < 3) { - var23 = Class44.anIntArrayArrayArray723[1 + var0]; - } - - if (0 == def.SecondInt || var3) { - var24 |= Long.MIN_VALUE; - } - - if (1 == def.anInt1540) { - var24 |= 4194304L; - } - - if (def.aBoolean1507) { - var24 |= 2147483648L; - } - - if (def.method1690()) { - Class70.method1286(var9, def, var10, null, var7, var2, null); - } - - boolean var26 = def.aBoolean1503 & !var3; - var24 |= (long) objectId << 32; - Object object; - Class136 var28; - if (22 != type) { - if (10 == type || 11 == type) { - if (def.animationId == -1 && def.ChildrenIds == null && !def.aBoolean1510) { - var28 = def.method1696(type == 11 ? 4 + var10 : var10, var20, var18, 10, var19, var23, var1, null, (byte) -26, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var28).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var28).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, 10, 11 == type ? 4 - -var10 : var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - if (object != null) { - boolean var37 = method835(var2, var7, var9, var19, var12, var13, (GameObject) object, var24); - if (def.aBoolean1525 && var37 && var1) { - int var29 = 15; - if (object instanceof Model) { - var29 = ((Model) object).method1888() / 4; - if (var29 > 30) { - var29 = 30; - } - } - - for (int var30 = 0; var30 <= var12; ++var30) { - for (int var31 = 0; var13 >= var31; ++var31) { - if (var29 > Class67.aByteArrayArrayArray1014[var2][var7 + var30][var31 + var9]) { - Class67.aByteArrayArrayArray1014[var2][var7 - -var30][var9 - -var31] = (byte) var29; - } - } - } - } - } - - if (0 != def.ClipType && null != var4) { - var4.method1489(var7, def.ProjectileClipped, (byte) 96, var9, var12, var13); - } - - } else if (12 <= type) { - if (-1 == def.animationId && null == def.ChildrenIds && !def.aBoolean1510) { - var28 = def.method1696(var10, var20, var18, type, var19, var23, var1, null, (byte) -82, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var28).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var28).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, type, var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - method835(var2, var7, var9, var19, 1, 1, (GameObject) object, var24); - if (var1 && type <= 17 && type != 13 && var2 > 0) { - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9], 4); - } - - if (def.ClipType != 0 && null != var4) { - var4.method1489(var7, def.ProjectileClipped, (byte) 73, var9, var12, var13); - } - - } else if (0 == type) { - if (def.animationId == -1 && null == def.ChildrenIds && !def.aBoolean1510) { - var28 = def.method1696(var10, var20, var18, 0, var19, var23, var1, null, (byte) -74, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var28).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var28).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, 0, var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class154.method2146(var2, var7, var9, var19, (GameObject) object, null, Class159.anIntArray2017[var10], 0, var24); - if (var1) { - if (var10 == 0) { - if (def.aBoolean1525) { - Class67.aByteArrayArrayArray1014[var2][var7][var9] = 50; - Class67.aByteArrayArrayArray1014[var2][var7][1 + var9] = 50; - } - - if (def.aBoolean1542) { - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9], 1); - } - } else if (1 == var10) { - if (def.aBoolean1525) { - Class67.aByteArrayArrayArray1014[var2][var7][var9 - -1] = 50; - Class67.aByteArrayArrayArray1014[var2][var7 - -1][var9 + 1] = 50; - } - - if (def.aBoolean1542) { - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][1 + var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][1 + var9], 2); - } - } else if (var10 == 2) { - if (def.aBoolean1525) { - Class67.aByteArrayArrayArray1014[var2][var7 + 1][var9] = 50; - Class67.aByteArrayArrayArray1014[var2][1 + var7][1 + var9] = 50; - } - - if (def.aBoolean1542) { - Class38_Sub1.anIntArrayArrayArray2609[var2][var7 - -1][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7 - -1][var9], 1); - } - } else if (var10 == 3) { - if (def.aBoolean1525) { - Class67.aByteArrayArrayArray1014[var2][var7][var9] = 50; - Class67.aByteArrayArrayArray1014[var2][1 + var7][var9] = 50; - } - - if (def.aBoolean1542) { - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9], 2); - } - } - } - - if (0 != def.ClipType && var4 != null) { - var4.method1486(var10, type, def.ProjectileClipped, var9, var7); - } - - if (def.anInt1528 != 16) { - Class140_Sub2.method1956(var2, var7, var9, def.anInt1528); - } - - } else if (type == 1) { - if (-1 == def.animationId && def.ChildrenIds == null && !def.aBoolean1510) { - var28 = def.method1696(var10, var20, var18, 1, var19, var23, var1, null, (byte) -83, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var28).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var28).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, 1, var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class154.method2146(var2, var7, var9, var19, (GameObject) object, null, Class40.anIntArray675[var10], 0, var24); - if (def.aBoolean1525 && var1) { - if (0 == var10) { - Class67.aByteArrayArrayArray1014[var2][var7][var9 + 1] = 50; - } else if (var10 == 1) { - Class67.aByteArrayArrayArray1014[var2][var7 - -1][1 + var9] = 50; - } else if (var10 == 2) { - Class67.aByteArrayArrayArray1014[var2][1 + var7][var9] = 50; - } else if (3 == var10) { - Class67.aByteArrayArrayArray1014[var2][var7][var9] = 50; - } - } - - if (def.ClipType != 0 && null != var4) { - var4.method1486(var10, type, def.ProjectileClipped, var9, var7); - } - - } else { - int var43; - if (type == 2) { - var43 = 1 + var10 & 3; - Object var38; - Object var42; - if (def.animationId == -1 && def.ChildrenIds == null && !def.aBoolean1510) { - Class136 var45 = def.method1696(var10 + 4, var20, var18, 2, var19, var23, var1, null, (byte) -108, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var45).aClass109_Sub1_1770, var20, var22, var21); - } - - var42 = Objects.requireNonNull(var45).aClass140_1777; - var45 = def.method1696(var43, var20, var18, 2, var19, var23, var1, null, (byte) -69, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var45).aClass109_Sub1_1770, var20, var22, var21); - } - - var38 = Objects.requireNonNull(var45).aClass140_1777; - } else { - var42 = new Class140_Sub3(objectId, 2, 4 + var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - var38 = new Class140_Sub3(objectId, 2, var43, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class154.method2146(var2, var7, var9, var19, (GameObject) var42, (GameObject) var38, Class159.anIntArray2017[var10], Class159.anIntArray2017[var43], var24); - if (def.aBoolean1542 && var1) { - if (var10 == 0) { - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9], 1); - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][1 + var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][1 + var9], 2); - } else if (var10 == 1) { - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9 - -1] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9 - -1], 2); - Class38_Sub1.anIntArrayArrayArray2609[var2][var7 - -1][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7 - -1][var9], 1); - } else if (var10 == 2) { - Class38_Sub1.anIntArrayArrayArray2609[var2][1 + var7][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][1 + var7][var9], 1); - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9], 2); - } else if (var10 == 3) { - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9], 2); - Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var2][var7][var9], 1); - } - } - - if (def.ClipType != 0 && var4 != null) { - var4.method1486(var10, type, def.ProjectileClipped, var9, var7); - } - - if (def.anInt1528 != 16) { - Class140_Sub2.method1956(var2, var7, var9, def.anInt1528); - } - - } else if (type == 3) { - if (def.animationId == -1 && null == def.ChildrenIds && !def.aBoolean1510) { - var28 = def.method1696(var10, var20, var18, 3, var19, var23, var1, null, (byte) -54, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var28).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var28).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, 3, var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class154.method2146(var2, var7, var9, var19, (GameObject) object, null, Class40.anIntArray675[var10], 0, var24); - if (def.aBoolean1525 && var1) { - if (0 == var10) { - Class67.aByteArrayArrayArray1014[var2][var7][var9 + 1] = 50; - } else if (var10 == 1) { - Class67.aByteArrayArrayArray1014[var2][1 + var7][var9 + 1] = 50; - } else if (var10 == 2) { - Class67.aByteArrayArrayArray1014[var2][1 + var7][var9] = 50; - } else if (var10 == 3) { - Class67.aByteArrayArrayArray1014[var2][var7][var9] = 50; - } - } - - if (0 != def.ClipType && var4 != null) { - var4.method1486(var10, type, def.ProjectileClipped, var9, var7); - } - - } else if (type == 9) { - if (def.animationId == -1 && def.ChildrenIds == null && !def.aBoolean1510) { - var28 = def.method1696(var10, var20, var18, type, var19, var23, var1, null, (byte) -30, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var28).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var28).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, type, var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - method835(var2, var7, var9, var19, 1, 1, (GameObject) object, var24); - if (def.ClipType != 0 && var4 != null) { - var4.method1489(var7, def.ProjectileClipped, (byte) 127, var9, var12, var13); - } - - if (def.anInt1528 != 16) { - Class140_Sub2.method1956(var2, var7, var9, def.anInt1528); - } - - } else if (4 == type) { - if (def.animationId == -1 && null == def.ChildrenIds && !def.aBoolean1510) { - var28 = def.method1696(var10, var20, var18, 4, var19, var23, var1, null, (byte) -103, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var28).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var28).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, 4, var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class3_Sub28_Sub8.method577(var2, var7, var9, var19, (GameObject) object, null, Class159.anIntArray2017[var10], 0, 0, 0, var24); - } else { - Object var39; - Class136 var47; - long var44; - if (type == 5) { - var43 = 16; - var44 = Class157.method2174(var2, var7, var9); - if (var44 != 0) { - var43 = ObjectDefinition.getObjectDefinition(Integer.MAX_VALUE & (int) (var44 >>> 32)).anInt1528; - } - - if (def.animationId == -1 && null == def.ChildrenIds && !def.aBoolean1510) { - var47 = def.method1696(var10, var20, var18, 4, var19, var23, var1, null, (byte) -125, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var47).aClass109_Sub1_1770, var20 + -(RenderAnimationDefinition.anIntArray356[var10] * 8), var22, -(Class3_Sub24_Sub3.anIntArray3491[var10] * 8) + var21); - } - - var39 = Objects.requireNonNull(var47).aClass140_1777; - } else { - var39 = new Class140_Sub3(objectId, 4, var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class3_Sub28_Sub8.method577(var2, var7, var9, var19, (GameObject) var39, null, Class159.anIntArray2017[var10], 0, var43 * RenderAnimationDefinition.anIntArray356[var10], Class3_Sub24_Sub3.anIntArray3491[var10] * var43, var24); - } else if (type == 6) { - var43 = 8; - var44 = Class157.method2174(var2, var7, var9); - if (var44 != 0) { - var43 = ObjectDefinition.getObjectDefinition(Integer.MAX_VALUE & (int) (var44 >>> 32)).anInt1528 / 2; - } - - if (def.animationId == -1 && def.ChildrenIds == null && !def.aBoolean1510) { - var47 = def.method1696(var10 + 4, var20, var18, 4, var19, var23, var1, null, (byte) -65, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var47).aClass109_Sub1_1770, -(8 * anIntArray2386[var10]) + var20, var22, -(8 * Class163_Sub3.anIntArray3007[var10]) + var21); - } - - var39 = Objects.requireNonNull(var47).aClass140_1777; - } else { - var39 = new Class140_Sub3(objectId, 4, 4 + var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class3_Sub28_Sub8.method577(var2, var7, var9, var19, (GameObject) var39, null, 256, var10, var43 * anIntArray2386[var10], var43 * Class163_Sub3.anIntArray3007[var10], var24); - } else if (7 == type) { - int var40 = 3 & var10 - -2; - if (def.animationId == -1 && def.ChildrenIds == null && !def.aBoolean1510) { - Class136 var41 = def.method1696(var40 - -4, var20, var18, 4, var19, var23, var1, null, (byte) -39, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var41).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var41).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, 4, var40 + 4, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class3_Sub28_Sub8.method577(var2, var7, var9, var19, (GameObject) object, null, 256, var40, 0, 0, var24); - } else if (type == 8) { - var43 = 8; - var44 = Class157.method2174(var2, var7, var9); - if (var44 != 0) { - var43 = ObjectDefinition.getObjectDefinition(Integer.MAX_VALUE & (int) (var44 >>> 32)).anInt1528 / 2; - } - - int var32 = var10 + 2 & 3; - Object var46; - if (-1 == def.animationId && null == def.ChildrenIds && !def.aBoolean1510) { - int var34 = 8 * Class163_Sub3.anIntArray3007[var10]; - int var33 = anIntArray2386[var10] * 8; - Class136 var35 = def.method1696(4 + var10, var20, var18, 4, var19, var23, var1, null, (byte) -25, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var35).aClass109_Sub1_1770, var20 + -var33, var22, -var34 + var21); - } - - var39 = Objects.requireNonNull(var35).aClass140_1777; - var35 = def.method1696(var32 - -4, var20, var18, 4, var19, var23, var1, null, (byte) -101, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var35).aClass109_Sub1_1770, var20 - var33, var22, -var34 + var21); - } - - var46 = Objects.requireNonNull(var35).aClass140_1777; - } else { - var39 = new Class140_Sub3(objectId, 4, 4 + var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - var46 = new Class140_Sub3(objectId, 4, var32 + 4, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - - Class3_Sub28_Sub8.method577(var2, var7, var9, var19, (GameObject) var39, (GameObject) var46, 256, var10, var43 * anIntArray2386[var10], Class163_Sub3.anIntArray3007[var10] * var43, var24); - } - } - } - } else if (KeyboardListener.aBoolean1905 || def.SecondInt != 0 || def.ClipType == 1 || def.aBoolean1483) { - if (def.animationId == -1 && def.ChildrenIds == null && !def.aBoolean1510) { - var28 = def.method1696(var10, var20, var18, 22, var19, var23, var1, null, (byte) -126, var26, var21); - if (HDToolKit.highDetail && var26) { - Class141.method2051(Objects.requireNonNull(var28).aClass109_Sub1_1770, var20, var22, var21); - } - - object = Objects.requireNonNull(var28).aClass140_1777; - } else { - object = new Class140_Sub3(objectId, 22, var10, var0, var7, var9, def.animationId, def.aBoolean1492, null); - } - TextureOperation39.method276(var2, var7, var9, var19, (GameObject) object, var24, def.aBoolean1502); - if (def.ClipType == 1 && null != var4) { - var4.method1503(var7, var9); - } - - } - } - } catch (RuntimeException var36) { - throw ClientErrorException.clientError(var36, "p.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + (var4 != null ? "{...}" : "null") + ',' + objectId + ',' + type + ',' + var7 + ',' + (byte) 50 + ',' + var9 + ',' + var10 + ')'); - } - } - - static boolean method835(int var0, int var1, int var2, int var3, int var4, int var5, GameObject var6, long var8) { - if (var6 == null) { - return true; - } else { - int var10 = var1 * 128 + 64 * var4; - int var11 = var2 * 128 + 64 * var5; - return Class56.method1189(var0, var1, var2, var4, var5, var10, var11, var3, var6, 0, false, var8); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class113.java b/Client/src/main/java/org/runite/client/Class113.java deleted file mode 100644 index f7883a3dc..000000000 --- a/Client/src/main/java/org/runite/client/Class113.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.runite.client; - -final class Class113 { - - static int interfacePacketCounter = 0; - static int anInt1559; - int anInt1544; - int anInt1545; - int anInt1547; - int anInt1548; - int anInt1549; - int anInt1550; - int anInt1551; - int anInt1553; - int anInt1554; - int anInt1555; - int anInt1557; - int anInt1560; - int anInt1561; - int anInt1562; - int anInt1563; - int anInt1564; - int anInt1565; - int anInt1566; - - - static void method1702(boolean var1) { - try { - if (var1 == !Sprites.aBoolean337) { - Sprites.aBoolean337 = var1; - Class3_Sub10.method139(76); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "pe.B(" + (byte) -124 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class114.java b/Client/src/main/java/org/runite/client/Class114.java deleted file mode 100644 index 6ca131019..000000000 --- a/Client/src/main/java/org/runite/client/Class114.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -import java.util.Objects; - -final class Class114 { - - static ReferenceCache aReferenceCache_1569 = new ReferenceCache(200); - static long[] ignores = new long[100]; - static int[] anIntArray1578 = new int[500]; - private final int anInt1568; - private final int anInt1579; - boolean aBoolean1580 = false; - private int anInt1570 = 0; - private Class3_Sub26[] aClass3_Sub26Array1571; - private int[][] anIntArrayArray1573; - private int anInt1576 = -1; - private LinkedList aLinkedList_1577 = new LinkedList(); - - - Class114(int var1, int var2, int var3) { - try { - this.anInt1568 = var2; - this.aClass3_Sub26Array1571 = new Class3_Sub26[this.anInt1568]; - this.anInt1579 = var1; - this.anIntArrayArray1573 = new int[this.anInt1579][var3]; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pf.(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static float[] method1705(int var0, int var1) { - try { - float var2 = Class92.method1514() + Class92.getLightingModelAmbient(); - int var3 = Class92.screenColorRgb(); - float var7 = 0.58823526F; - MouseListeningClass.aFloatArray1919[3] = 1.0F; - float var4 = (float) (var3 >> 16 & 0xFF) / 255.0F; - float var5 = (float) (('\uff1e' & var3) >> 8) / 255.0F; - MouseListeningClass.aFloatArray1919[1] = var2 * (float) Unsorted.bitwiseAnd(var0 >> 8, 255) / 255.0F * var5 * var7; - MouseListeningClass.aFloatArray1919[var1] = var2 * var7 * var4 * ((float) (Unsorted.bitwiseAnd(16754958, var0) >> 16) / 255.0F); - float var6 = (float) (var3 & 0xFF) / 255.0F; - MouseListeningClass.aFloatArray1919[2] = (float) Unsorted.bitwiseAnd(255, var0) / 255.0F * var6 * var7 * var2; - return MouseListeningClass.aFloatArray1919; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "pf.F(" + var0 + ',' + var1 + ')'); - } - } - - static AbstractSprite method1707(int var0, int var1, boolean var2, int var3, int var4) { - try { - int var7 = (!var2 ? 0 : 65536) + var1 + (var0 << 17) + (var4 << 19); - long var8 = (long) var7 * 3849834839L + 3147483667L * (long) var3; - AbstractSprite var10 = (AbstractSprite) Class67.aReferenceCache_1013.get(var8); - if (var10 == null) { - Class51.aBoolean837 = false; - var10 = AbstractSprite.constructItemSprite(var4, false, var1, var2, var0, var3, false); - if (var10 != null && !Class51.aBoolean837) { - Class67.aReferenceCache_1013.put(var10, var8); - } - - } - return var10; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "pf.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + 65536 + ')'); - } - } - - static void method1708(int var0, int var1) { - try { - InterfaceWidget var3 = InterfaceWidget.getWidget(13, var1); - var3.flagUpdate(); - var3.anInt3598 = var0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "pf.E(" + var0 + ',' + var1 + ',' + -2553 + ')'); - } - } - - final void method1706() { - try { - for (int var2 = 0; var2 < this.anInt1579; ++var2) { - this.anIntArrayArray1573[var2] = null; - } - - this.anIntArrayArray1573 = null; - this.aClass3_Sub26Array1571 = null; - this.aLinkedList_1577.method1211(-85); - this.aLinkedList_1577 = null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "pf.A(" + 1127165736 + ')'); - } - } - - final int[] method1709(int var2) { - try { - - if (this.anInt1579 == this.anInt1568) { - this.aBoolean1580 = this.aClass3_Sub26Array1571[var2] == null; - this.aClass3_Sub26Array1571[var2] = Class56.aClass3_Sub26_884; - return this.anIntArrayArray1573[var2]; - } else if (this.anInt1579 == 1) { - this.aBoolean1580 = this.anInt1576 != var2; - this.anInt1576 = var2; - return this.anIntArrayArray1573[0]; - } else { - Class3_Sub26 var3 = this.aClass3_Sub26Array1571[var2]; - if (null == var3) { - this.aBoolean1580 = true; - if (this.anInt1570 < this.anInt1579) { - var3 = new Class3_Sub26(var2, this.anInt1570); - ++this.anInt1570; - } else { - Class3_Sub26 var4 = (Class3_Sub26) this.aLinkedList_1577.method1212(); - var3 = new Class3_Sub26(var2, Objects.requireNonNull(var4).anInt2555); - this.aClass3_Sub26Array1571[var4.anInt2553] = null; - var4.unlink(); - } - - this.aClass3_Sub26Array1571[var2] = var3; - } else { - this.aBoolean1580 = false; - } - - this.aLinkedList_1577.method1216(var3); - return this.anIntArrayArray1573[var3.anInt2555]; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pf.C(" + -16409 + ',' + var2 + ')'); - } - } - - final int[][] method1710(byte var1) { - try { - if (var1 <= 67) { - return null; - } else if (this.anInt1579 == this.anInt1568) { - for (int var2 = 0; this.anInt1579 > var2; ++var2) { - this.aClass3_Sub26Array1571[var2] = Class56.aClass3_Sub26_884; - } - - return this.anIntArrayArray1573; - } else { - throw new RuntimeException("Can only retrieve a full image cache"); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "pf.H(" + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class115.java b/Client/src/main/java/org/runite/client/Class115.java deleted file mode 100644 index ea345d527..000000000 --- a/Client/src/main/java/org/runite/client/Class115.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.runite.client; - -final class Class115 { - - - static void method1713() { - try { - - while (true) { - Class3_Sub5 var1 = (Class3_Sub5) Unsorted.aLinkedList_2468.method1220(); - if (var1 == null) { - return; - } - - Class140_Sub4 var2; - int var3; - if (0 > var1.anInt2273) { - var3 = -var1.anInt2273 - 1; - if (Class3_Sub1.localIndex == var3) { - var2 = Class102.player; - } else { - var2 = Unsorted.players[var3]; - } - } else { - var3 = var1.anInt2273 - 1; - var2 = NPC.npcs[var3]; - } - - if (var2 != null) { - ObjectDefinition var20 = ObjectDefinition.getObjectDefinition(var1.anInt2270); - - int var4; - int var5; - if (var1.anInt2284 == 1 || var1.anInt2284 == 3) { - var5 = var20.SizeX; - var4 = var20.SizeY; - } else { - var4 = var20.SizeX; - var5 = var20.SizeY; - } - - int var7 = (var4 - -1 >> 1) + var1.anInt2271; - int var6 = (var4 >> 1) + var1.anInt2271; - int var8 = (var5 >> 1) + var1.anInt2282; - int var9 = (var5 + 1 >> 1) + var1.anInt2282; - int[][] var10 = Class44.anIntArrayArrayArray723[WorldListCountry.localPlane]; - int var11 = var10[var7][var9] + var10[var6][var9] + (var10[var6][var8] - -var10[var7][var8]) >> 2; - GameObject var12 = null; - int var13 = Class75.anIntArray1107[var1.anInt2278]; - if (var13 == 0) { - Class70 var14 = Class154.method2147(WorldListCountry.localPlane, var1.anInt2271, var1.anInt2282); - if (var14 != null) { - var12 = var14.aClass140_1049; - } - } else if (var13 == 1) { - Class19 var21 = Class44.method1068(WorldListCountry.localPlane, var1.anInt2271, var1.anInt2282); - if (null != var21) { - var12 = var21.aClass140_429; - } - } else if (2 == var13) { - Class25 var23 = Class75.method1336(WorldListCountry.localPlane, var1.anInt2271, var1.anInt2282); - if (null != var23) { - var12 = var23.aClass140_479; - } - } else if (var13 == 3) { - Class12 var24 = Unsorted.method784(WorldListCountry.localPlane, var1.anInt2271, var1.anInt2282); - if (null != var24) { - var12 = var24.object; - } - } - - if (null != var12) { - Unsorted.method881(WorldListCountry.localPlane, var1.anInt2282, -96, 0, var1.anInt2271, var1.anInt2283 - -1, -1, var13, 0, var1.anInt2266 - -1); - var2.anInt2778 = var1.anInt2283 + Class44.anInt719; - var2.anInt2833 = 64 * var5 + var1.anInt2282 * 128; - var2.anInt2782 = var4 * 64 + 128 * var1.anInt2271; - var2.anObject2796 = var12; - int var22 = var1.anInt2268; - var2.anInt2812 = var11; - var2.anInt2797 = Class44.anInt719 + var1.anInt2266; - int var15 = var1.anInt2272; - int var16 = var1.anInt2277; - int var17 = var1.anInt2279; - int var18; - if (var22 > var15) { - var18 = var22; - var22 = var15; - var15 = var18; - } - - var2.anInt2818 = var1.anInt2271 + var15; - if (var17 < var16) { - var18 = var16; - var16 = var17; - var17 = var18; - } - - var2.anInt2777 = var1.anInt2282 + var16; - var2.anInt2817 = var17 + var1.anInt2282; - var2.anInt2788 = var1.anInt2271 - -var22; - } - } - } - } catch (RuntimeException var19) { - throw ClientErrorException.clientError(var19, "ph.A(" + (byte) -91 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class117.java b/Client/src/main/java/org/runite/client/Class117.java deleted file mode 100644 index e351993e2..000000000 --- a/Client/src/main/java/org/runite/client/Class117.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.runite.client; - -public final class Class117 { - - static int[] anIntArray1613 = new int[500]; - - - static void method1719(int var0) { - try { - if (Class143.gameStage != var0) { - if (Class143.gameStage == 0) { - Unsorted.method1517(); - } - - if (var0 == 40) { - Class24.method951(); - } - - boolean var2 = var0 == 5 || var0 == 10 || var0 == 28; - if (40 != var0 && null != Class163_Sub2_Sub1.aClass89_4012) { - Class163_Sub2_Sub1.aClass89_4012.close(); - Class163_Sub2_Sub1.aClass89_4012 = null; - } - - if (var0 == 25 || var0 == 28) { - Class162.anInt2038 = 0; - LinkableRSString.anInt2579 = 1; - Class163_Sub2_Sub1.anInt4019 = 0; - Client.anInt2275 = 1; - Class40.anInt3293 = 0; - Unsorted.method1250(102, true); - } - - if (var0 == 25 || var0 == 10) { - Class72.method1293(); - } - - if (var0 == 5) { - Class108.method1656(CacheIndex.spritesIndex, (byte) -111); - } else { - TextureOperation23.method247((byte) -121); - } - - boolean var3 = 5 == Class143.gameStage || Class143.gameStage == 10 || Class143.gameStage == 28; - if (var3 == !var2) { - if (var2) { - AudioHandler.currentTrack = KeyboardListener.loginThemeSongArchiveID; - if (AudioHandler.musicVolume == 0) { - Unsorted.method882(); - } else { - AudioHandler.method2099(KeyboardListener.loginThemeSongArchiveID, CacheIndex.musicIndex, 255); - } - - Class58.aJs5Worker_917.sendLoginState(false); - } else { - Unsorted.method882(); - Class58.aJs5Worker_917.sendLoginState(true); - } - } - - if (HDToolKit.highDetail && (25 == var0 || var0 == 28 || var0 == 40)) { - HDToolKit.method1833(); - } - - Class143.gameStage = var0; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "pl.C(" + var0 + ',' + 5 + ')'); - } - } - - static void method1720(boolean var0, int var1) { - try { - Class129.anIntArray1695 = new int[104]; - Unsorted.anIntArray1138 = new int[104]; - Class85.anInt1174 = 99; - MouseListeningClass.anIntArray1920 = new int[104]; - byte var2; - if (var0) { - var2 = 1; - } else { - var2 = 4; - } - - Unsorted.aByteArrayArrayArray1328 = new byte[var2][104][104]; - Unsorted.anIntArray2469 = new int[104]; - Class38_Sub1.anIntArrayArrayArray2609 = new int[var2][105][var1]; - Class67.aByteArrayArrayArray1014 = new byte[var2][105][105]; - Class158_Sub1.aByteArrayArrayArray1828 = new byte[var2][104][104]; - Class3_Sub31.anIntArray2606 = new int[104]; - PacketParser.aByteArrayArrayArray81 = new byte[var2][104][104]; - TextureOperation36.aByteArrayArrayArray3430 = new byte[var2][104][104]; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "pl.B(" + var0 + ',' + var1 + ')'); - } - } - - static SoftwareSprite method1722(int var0) { - try { - int var1 = Class140_Sub7.anIntArray2931[0] * Unsorted.anIntArray3076[0]; - byte[] var2 = Class163_Sub1.aByteArrayArray2987[0]; - int[] var3 = new int[var1]; - - for (int var4 = 0; var1 > var4; ++var4) { - var3[var4] = TextureOperation38.spritePalette[Unsorted.bitwiseAnd(var2[var4], 255)]; - } - - SoftwareSprite var6 = new SoftwareSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[0], Unsorted.anIntArray2591[0], Class140_Sub7.anIntArray2931[0], Unsorted.anIntArray3076[0], var3); - Class39.method1035((byte) 122); - return var0 >= -51 ? null : var6; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pl.A(" + var0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class118.java b/Client/src/main/java/org/runite/client/Class118.java deleted file mode 100644 index 4e8ec937e..000000000 --- a/Client/src/main/java/org/runite/client/Class118.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.reference.ObjectReference; - -public abstract class Class118 { - - public abstract ObjectReference method1725(ObjectReference var1); - -} diff --git a/Client/src/main/java/org/runite/client/Class118_Sub1.java b/Client/src/main/java/org/runite/client/Class118_Sub1.java deleted file mode 100644 index 73c6a1350..000000000 --- a/Client/src/main/java/org/runite/client/Class118_Sub1.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.reference.ObjectReference; -import org.rs09.client.data.reference.SoftObjectReference; - -public final class Class118_Sub1 extends Class118 { - - public final ObjectReference method1725(ObjectReference var1) { - return new SoftObjectReference<>(var1.getValue()); - } -} diff --git a/Client/src/main/java/org/runite/client/Class119.java b/Client/src/main/java/org/runite/client/Class119.java deleted file mode 100644 index e335976f4..000000000 --- a/Client/src/main/java/org/runite/client/Class119.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.runite.client; - -import java.awt.*; - -final class Class119 { - - static Class131 aClass131_1624; - static Class33 aClass33_1626; - static CacheIndex aClass153_1628; - - - static void method1729() { - try { - Container var1; - if (null == TextureOperation30.fullScreenFrame) { - if (GameShell.frame == null) { - var1 = Class38.gameSignlink.gameApplet; - } else { - var1 = GameShell.frame; - } - } else { - var1 = TextureOperation30.fullScreenFrame; - } - - Unsorted.frameWidth = var1.getSize().width; - Class70.frameHeight = var1.getSize().height; - Insets var2; - if (var1 == GameShell.frame) { - var2 = GameShell.frame.getInsets(); - Class70.frameHeight -= var2.bottom + var2.top; - Unsorted.frameWidth -= var2.right + var2.left; - } - - if (Class83.getWindowType() >= 2) { - Class23.canvasWidth = Unsorted.frameWidth; - Class84.leftMargin = 0; - Class106.rightMargin = 0; - Class140_Sub7.canvasHeight = Class70.frameHeight; - } else { - Class106.rightMargin = 0; - Class84.leftMargin = (-765 + Unsorted.frameWidth) / 2; - Class140_Sub7.canvasHeight = 503; - Class23.canvasWidth = 765; - } - - if (HDToolKit.highDetail) { - HDToolKit.method1854(Class23.canvasWidth, Class140_Sub7.canvasHeight); - } - - GameShell.canvas.setSize(Class23.canvasWidth, Class140_Sub7.canvasHeight); - if (var1 == GameShell.frame) { - var2 = GameShell.frame.getInsets(); - GameShell.canvas.setLocation(var2.left + Class84.leftMargin, Class106.rightMargin + var2.top); - } else { - GameShell.canvas.setLocation(Class84.leftMargin, Class106.rightMargin); - } - - if (ConfigInventoryDefinition.anInt3655 != -1) { - Class124.method1746(true, (byte) -125); - } - - Unsorted.method1396(-1); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "qh.C(" + true + ')'); - } - } - - static void method1730(Signlink var0) { - try { - RandomAccessFileWrapper var2 = null; - - try { - Class64 var3 = var0.method1433("runescape", 12); - - while (0 == var3.anInt978) { - TimeUtils.sleep(1L); - } - - if (var3.anInt978 == 1) { - var2 = (RandomAccessFileWrapper) var3.anObject974; - DataBuffer var4 = Class23.method939(); - var2.write(var4.buffer, var4.index, 0); - } - } catch (Exception var6) { - } - - try { - if (var2 != null) { - var2.close(); - } - } catch (Exception var5) { - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "qh.A(" + (var0 != null ? "{...}" : "null") + ',' + (byte) 14 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class12.java b/Client/src/main/java/org/runite/client/Class12.java deleted file mode 100644 index f80b8bf9a..000000000 --- a/Client/src/main/java/org/runite/client/Class12.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.runite.client; - -import org.rs09.client.util.ArrayUtils; - -public final class Class12 { - - public static CacheIndex aClass153_322; - public static CacheIndex aClass153_323; - static float aFloat319; - GameObject object; - int anInt324; - int anInt326; - long aLong328; - boolean aBoolean329 = false; - int anInt330; - - - static void method870(int var0, byte var1, int var2, int var3, int var4, int var5) { - try { - int var6; - int var7; - for (var6 = var2; var4 + var2 >= var6; ++var6) { - for (var7 = var3; var5 + var3 >= var7; ++var7) { - if (var7 >= 0 && 104 > var7 && var6 >= 0 && 104 > var6) { - Class67.aByteArrayArrayArray1014[var0][var7][var6] = 127; - } - } - } - - for (var6 = var2; var4 + var2 > var6; ++var6) { - for (var7 = var3; var7 < var3 + var5; ++var7) { - if (var7 >= 0 && var7 < 104 && var6 >= 0 && var6 < 104) { - Class44.anIntArrayArrayArray723[var0][var7][var6] = var0 <= 0 ? 0 : Class44.anIntArrayArrayArray723[var0 + -1][var7][var6]; - } - } - } - - if (0 < var3 && var3 < 104) { - for (var6 = 1 + var2; var6 < var2 + var4; ++var6) { - if (var6 >= 0 && var6 < 104) { - Class44.anIntArrayArrayArray723[var0][var3][var6] = Class44.anIntArrayArrayArray723[var0][var3 - 1][var6]; - } - } - } - - if (var2 > 0 && var2 < 104) { - for (var6 = var3 + 1; var6 < var3 - -var5; ++var6) { - if (var6 >= 0 && 104 > var6) { - Class44.anIntArrayArrayArray723[var0][var6][var2] = Class44.anIntArrayArrayArray723[var0][var6][var2 + -1]; - } - } - } - - if (var3 >= 0 && var2 >= 0 && var3 < 104 && var2 < 104) { - if (var0 != 0) { - if (var3 > 0 && Class44.anIntArrayArrayArray723[-1 + var0][var3 + -1][var2] != Class44.anIntArrayArrayArray723[var0][-1 + var3][var2]) { - Class44.anIntArrayArrayArray723[var0][var3][var2] = Class44.anIntArrayArrayArray723[var0][-1 + var3][var2]; - } else if (0 < var2 && Class44.anIntArrayArrayArray723[-1 + var0][var3][var2 + -1] != Class44.anIntArrayArrayArray723[var0][var3][-1 + var2]) { - Class44.anIntArrayArrayArray723[var0][var3][var2] = Class44.anIntArrayArrayArray723[var0][var3][var2 - 1]; - } else if (var3 > 0 && var2 > 0 && Class44.anIntArrayArrayArray723[var0 - 1][-1 + var3][var2 - 1] != Class44.anIntArrayArrayArray723[var0][-1 + var3][var2 - 1]) { - Class44.anIntArrayArrayArray723[var0][var3][var2] = Class44.anIntArrayArrayArray723[var0][-1 + var3][var2 - 1]; - } - } else if (0 < var3 && 0 != Class44.anIntArrayArrayArray723[var0][var3 + -1][var2]) { - Class44.anIntArrayArrayArray723[var0][var3][var2] = Class44.anIntArrayArrayArray723[var0][var3 + -1][var2]; - } else if (var2 > 0 && Class44.anIntArrayArrayArray723[var0][var3][var2 - 1] != 0) { - Class44.anIntArrayArrayArray723[var0][var3][var2] = Class44.anIntArrayArrayArray723[var0][var3][var2 + -1]; - } else if (var3 > 0 && 0 < var2 && Class44.anIntArrayArrayArray723[var0][var3 - 1][var2 + -1] != 0) { - Class44.anIntArrayArrayArray723[var0][var3][var2] = Class44.anIntArrayArrayArray723[var0][var3 - 1][var2 + -1]; - } - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "bm.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static int method872(int var1, int var2) { - try { - Class3_Sub25 var3 = (Class3_Sub25) Class3_Sub2.aHashTable_2220.get(var1); - return null == var3 ? 0 : (var2 >= 0 && var2 < var3.anIntArray2551.length ? var3.anIntArray2551[var2] : 0); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bm.C(" + -1 + ',' + var1 + ',' + var2 + ')'); - } - } - - static byte[] method873(byte[] var1) { - try { - int var2 = var1.length; - byte[] var3 = new byte[var2]; - ArrayUtils.arraycopy(var1, 0, var3, 0, var2); - return var3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bm.D(" + (byte) 62 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class120.java b/Client/src/main/java/org/runite/client/Class120.java deleted file mode 100644 index d42ff35de..000000000 --- a/Client/src/main/java/org/runite/client/Class120.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.runite.client; - -final class Class120 { - - static long[] aLongArray1631 = new long[256]; - static int[] anIntArray1638; - - static { - for (int var2 = 0; 256 > var2; ++var2) { - long var0 = var2; - - for (int var3 = 0; 8 > var3; ++var3) { - if ((1L & var0) == 1) { - var0 = var0 >>> 1 ^ -3932672073523589310L; - } else { - var0 >>>= 1; - } - } - - aLongArray1631[var2] = var0; - } - - anIntArray1638 = new int[128]; - } - - int anInt1632; - int anInt1634; - int anInt1635; - - static RSString method1732(RSInterface var0, byte var1, int var2) { - try { - if (var1 >= -8) { - anIntArray1638 = null; - } - - return !Client.method44(var0).method92(var2, (byte) -110) && var0.anObjectArray314 == null ? null : (null != var0.aClass94Array171 && var0.aClass94Array171.length > var2 && var0.aClass94Array171[var2] != null && var0.aClass94Array171[var2].trim(1).length() != 0 ? var0.aClass94Array171[var2] : (!ClientCommands.commandQaOpEnabled ? null : RSString.stringCombiner(new RSString[]{RSString.parse("Hidden)2"), RSString.stringAnimator(var2)}))); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "qj.B(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class121.java b/Client/src/main/java/org/runite/client/Class121.java deleted file mode 100644 index b45fbff11..000000000 --- a/Client/src/main/java/org/runite/client/Class121.java +++ /dev/null @@ -1,280 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; -import org.rs09.client.net.Connection; - -import java.io.IOException; -import java.net.Socket; -import java.nio.ByteBuffer; - -final class Class121 { - - static boolean aBoolean1641 = false; - static int anInt1642 = 0; - int anInt1639 = 0; - boolean aBoolean1640 = false; - Class156 aClass156_1643; - ByteBuffer aByteBuffer1644; - int anInt1648 = 0; - - - static int method1734(int var0, float var1, int var2, int var3, int[][] var4, int[][] var5, int var6, float[][] var7, int var8, byte var9, int var10, boolean var11, Class3_Sub11 var12, float[][] var13, int var14, int var15, float[][] var16, int var17) { - try { - int var18; - if (var10 == 1) { - var18 = var15; - var15 = var17; - var17 = -var18 + 128; - } else if (var10 == 2) { - var17 = -var17 + 128; - var15 = -var15 + 128; - } else if (var10 == 3) { - var18 = var15; - var15 = 128 + -var17; - var17 = var18; - } - - float var19; - int var21; - float var20; - float var30; - if (var15 == 0 && var17 == 0) { - var19 = var16[var6][var14]; - var20 = var7[var6][var14]; - var30 = var13[var6][var14]; - var21 = var2; - } else if (var15 == 128 && var17 == 0) { - var21 = var3; - var19 = var16[var6 + 1][var14]; - var20 = var7[1 + var6][var14]; - var30 = var13[var6 + 1][var14]; - } else if (var15 == 128 && var17 == 128) { - var20 = var7[1 + var6][var14 - -1]; - var30 = var13[var6 + 1][1 + var14]; - var19 = var16[1 + var6][var14 - -1]; - var21 = var8; - } else if (var15 == 0 && var17 == 128) { - var20 = var7[var6][1 + var14]; - var19 = var16[var6][1 + var14]; - var30 = var13[var6][var14 - -1]; - var21 = var0; - } else { - var30 = var13[var6][var14]; - var19 = var16[var6][var14]; - float var23 = (float) var17 / 128.0F; - float var22 = (float) var15 / 128.0F; - var30 += (var13[var6 - -1][var14] - var30) * var22; - var19 += (-var19 + var16[var6 + 1][var14]) * var22; - float var24 = var13[var6][var14 + 1]; - float var25 = var16[var6][var14 - -1]; - var25 += (-var25 + var16[var6 + 1][var14 - -1]) * var22; - var20 = var7[var6][var14]; - var19 += var23 * (-var19 + var25); - float var26 = var7[var6][1 + var14]; - var24 += (-var24 + var13[var6 - -1][var14 + 1]) * var22; - var30 += var23 * (-var30 + var24); - var26 += (-var26 + var7[1 + var6][var14 - -1]) * var22; - var20 += (-var20 + var7[var6 - -1][var14]) * var22; - var20 += (-var20 + var26) * var23; - int var27 = TextureOperation30.method210(var15, var2, var3); - int var28 = TextureOperation30.method210(var15, var0, var8); - var21 = TextureOperation30.method210(var17, var27, var28); - } - - if (var9 > -111) { - return -54; - } else { - int var32 = var17 + (var14 << 7); - int var33 = Class3_Sub23.method408(var15, (byte) -51, var14, var5, var6, var17); - int var31 = (var6 << 7) - -var15; - return var12.method146(var31, var33, var32, var30, var19, var20, !var11 ? var21 : -256 & var21, var4 != null ? (float) (var33 + -Class3_Sub23.method408(var15, (byte) 103, var14, var4, var6, var17)) / var1 : 0.0F); - } - } catch (RuntimeException var29) { - throw ClientErrorException.clientError(var29, "ql.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + (var4 != null ? "{...}" : "null") + ',' + (var5 != null ? "{...}" : "null") + ',' + var6 + ',' + (var7 != null ? "{...}" : "null") + ',' + var8 + ',' + var9 + ',' + var10 + ',' + var11 + ',' + (var12 != null ? "{...}" : "null") + ',' + (var13 != null ? "{...}" : "null") + ',' + var14 + ',' + var15 + ',' + (var16 != null ? "{...}" : "null") + ',' + var17 + ')'); - } - } - - static int method1735() { - try { - try { - if (Class43.worldListStage == 0) { - if (TextureOperation25.aLong3411 > TimeUtils.time() + -5000L) { - return 0; - } - - Class3_Sub9.aClass64_2318 = Class38.gameSignlink.method1441((byte) 8, GameConfig.IP_MANAGEMENT, GameConfig.WL_PORT);//, Class123.anInt1658); - RSInterface.aLong261 = TimeUtils.time(); - Class43.worldListStage = 1; - } - - if (30000L + RSInterface.aLong261 < TimeUtils.time()) { - return method179((byte) 92, 1000); - } - - int wlUpdateStamp; - int var2; - if (1 == Class43.worldListStage) { - if (Class3_Sub9.aClass64_2318.anInt978 == 2) { - return method179((byte) 92, 1001); - } - - if (1 != Class3_Sub9.aClass64_2318.anInt978) { - return -1; - } - - Class3_Sub15.activeConnection = new Connection((Socket) Class3_Sub9.aClass64_2318.anObject974, Class38.gameSignlink); - TextureOperation12.outgoingBuffer.index = 0; - Class3_Sub9.aClass64_2318 = null; - wlUpdateStamp = 0; - if (Class30.loadedWorldList) { - wlUpdateStamp = WorldListEntry.updateStamp; - } - - TextureOperation12.outgoingBuffer.writeByte(255); - TextureOperation12.outgoingBuffer.writeInt(wlUpdateStamp); - Class3_Sub15.activeConnection.sendBytes(TextureOperation12.outgoingBuffer.buffer, TextureOperation12.outgoingBuffer.index); - if (null != WorldListEntry.aAudioChannel_2627) { - WorldListEntry.aAudioChannel_2627.method2159(); - } - - if (null != Class3_Sub21.aAudioChannel_2491) { - Class3_Sub21.aAudioChannel_2491.method2159(); - } - - var2 = Class3_Sub15.activeConnection.readByte(); - //System.out.println(var2); //Not sure what this was debugging - if (WorldListEntry.aAudioChannel_2627 != null) { - WorldListEntry.aAudioChannel_2627.method2159(); - } - - if (null != Class3_Sub21.aAudioChannel_2491) { - Class3_Sub21.aAudioChannel_2491.method2159(); - } - - if (var2 != 0) { - return method179((byte) 92, var2); - } - - Class43.worldListStage = 2; - } - - if (Class43.worldListStage == 2) { - if (2 > Class3_Sub15.activeConnection.availableBytes()) { - return -1; - } - - Unsorted.wlPacketSize = Class3_Sub15.activeConnection.readByte(); - Unsorted.wlPacketSize <<= 8; - Unsorted.wlPacketSize += Class3_Sub15.activeConnection.readByte(); - Class43.worldListStage = 3; - Class3_Sub20.wlPacketIndex = 0; - TextureOperation29.aByteArray3396 = new byte[Unsorted.wlPacketSize]; - } - - if (Class43.worldListStage == 3) { - wlUpdateStamp = Class3_Sub15.activeConnection.availableBytes(); - if (1 > wlUpdateStamp) { - return -1; - } - - if (wlUpdateStamp > -Class3_Sub20.wlPacketIndex + Unsorted.wlPacketSize) { - wlUpdateStamp = Unsorted.wlPacketSize + -Class3_Sub20.wlPacketIndex; - } - - Class3_Sub15.activeConnection.readBytes(TextureOperation29.aByteArray3396, Class3_Sub20.wlPacketIndex, wlUpdateStamp); - Class3_Sub20.wlPacketIndex += wlUpdateStamp; - if (Class3_Sub20.wlPacketIndex >= Unsorted.wlPacketSize) { - if (TextureOperation39.handleWorldListUpdate(TextureOperation29.aByteArray3396)) { - Unsorted.aClass44_Sub1Array3201 = new WorldListEntry[WorldListEntry.activeWorldListSize]; - var2 = 0; - - for (int var3 = Class53.worldListOffset; var3 <= WorldListEntry.worldListArraySize; ++var3) { - WorldListEntry var4 = Class3_Sub8.getWorld(91, var3); - if (null != var4) { - Unsorted.aClass44_Sub1Array3201[var2++] = var4; - } - //System.out.println("world = " +var4.worldId); - } - - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - Unsorted.anInt1088 = 0; - Class43.worldListStage = 0; - TextureOperation29.aByteArray3396 = null; - TextureOperation25.aLong3411 = TimeUtils.time(); - return 0; - } - - return method179((byte) 92, 1002); - } - - return -1; - } - } catch (IOException var5) { - return method179((byte) 92, 1003); - } - - return -1; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ql.D(" + 29984 + ')'); - } - } - - static int method1736(int var0, int var1, int var2, int var3) { - try { - if (null == Class44.anIntArrayArrayArray723) { - return 0; - } else { - int var4 = var2 >> 7; - int var5 = var3 >> 7; - if (var4 >= 0 && 0 <= var5 && var4 <= 103 && 103 >= var5) { - int var7 = 127 & var2; - int var8 = var3 & 127; - int var6 = var0; - if (3 > var0 && (2 & Unsorted.aByteArrayArrayArray113[1][var4][var5]) == 2) { - var6 = var0 + 1; - } - - int var10 = var7 * Class44.anIntArrayArrayArray723[var6][var1 + var4][1 + var5] + Class44.anIntArrayArrayArray723[var6][var4][var5 + 1] * (-var7 + 128) >> 7; - int var9 = var7 * Class44.anIntArrayArrayArray723[var6][var4 + 1][var5] + (-var7 + 128) * Class44.anIntArrayArrayArray723[var6][var4][var5] >> 7; - return var8 * var10 + (128 - var8) * var9 >> 7; - } else { - return 0; - } - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "ql.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static int method179(byte var0, int var1) { - try { - if (var0 == 92) { - if (null != Class3_Sub15.activeConnection) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - ++Unsorted.anInt1088; - if (Unsorted.anInt1088 > 4) { - Class43.worldListStage = 0; - Unsorted.anInt1088 = 0; - return var1; - } else { - Class43.worldListStage = 0; - if (Class123.anInt1658 == Client.anInt3773) { - Class123.anInt1658 = Class53.anInt867; - } else { - Class123.anInt1658 = Client.anInt3773; - } - - return -1; - } - } else { - return 122; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "an.F(" + var0 + ',' + var1 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class123.java b/Client/src/main/java/org/runite/client/Class123.java deleted file mode 100644 index ac1912a35..000000000 --- a/Client/src/main/java/org/runite/client/Class123.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.runite.client; - -final class Class123 { - - - static SequenceDefinition[] aClass142Array1654 = new SequenceDefinition[14]; - static boolean aBoolean1656 = false; - static int anInt1658; - -} diff --git a/Client/src/main/java/org/runite/client/Class124.java b/Client/src/main/java/org/runite/client/Class124.java deleted file mode 100644 index 8451d3c9c..000000000 --- a/Client/src/main/java/org/runite/client/Class124.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.HashTable; - -final class Class124 { - - static HashTable aHashTable_1659 = new HashTable(512); - static CacheIndex aClass153_1661; - - - static void method1745() { - try { - for (int var1 = 0; var1 < 104; ++var1) { - for (int var2 = 0; 104 > var2; ++var2) { - Class163_Sub1_Sub1.anIntArrayArray4010[var1][var2] = 0; - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "rb.B(" + 0 + ')'); - } - } - - static void method1746(boolean var0, byte var1) { - try { - if (var1 > -31) { - aClass153_1661 = null; - } - - Class75_Sub4.method1352(Class140_Sub7.canvasHeight, var0, ConfigInventoryDefinition.anInt3655, Class23.canvasWidth); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "rb.C(" + var0 + ',' + var1 + ')'); - } - } - - static Class3_Sub28_Sub3 method1747(DataBuffer var0) { - try { - Class3_Sub28_Sub3 var2 = new Class3_Sub28_Sub3(var0.readString(), var0.readString(), var0.readUnsignedShort(), var0.readUnsignedShort(), var0.readInt(), var0.readUnsignedByte() == 1, var0.readUnsignedByte()); - int var3 = var0.readUnsignedByte(); - - for (int var4 = 0; var3 > var4; ++var4) { - var2.aLinkedList_3560.method1215(new Class3_Sub21(var0.readUnsignedShort(), var0.readUnsignedShort(), var0.readUnsignedShort(), var0.readUnsignedShort())); - } - - var2.method538(); - return var2; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "rb.D(" + (var0 != null ? "{...}" : "null") + ',' + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class125.java b/Client/src/main/java/org/runite/client/Class125.java deleted file mode 100644 index eca1b2134..000000000 --- a/Client/src/main/java/org/runite/client/Class125.java +++ /dev/null @@ -1,182 +0,0 @@ -package org.runite.client; - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - - -final class Class125 implements ShaderInterface { - - private static final float[] aFloatArray2185 = new float[4]; - private int anInt2182 = -1; - private FloatBuffer aFloatBuffer2183; - private int anInt2184; - private int anInt2186 = -1; - - - public Class125() { - if (HDToolKit.supportVertexProgram && HDToolKit.maxTextureUnits >= 2) { - int[] var1 = new int[1]; - GL var2 = HDToolKit.gl; - var2.glGenProgramsARB(1, var1, 0); - this.anInt2184 = var1[0]; - int[][] var3 = AudioThread.method895(0); - int[][] var4 = AudioThread.method895(8); - DataBuffer var5 = new DataBuffer(262144); - - for (int var6 = 0; var6 < 256; ++var6) { - int[] var7 = var3[var6]; - int[] var8 = var4[var6]; - - for (int var9 = 0; var9 < 64; ++var9) { - if (HDToolKit.aBoolean1790) { - var5.writeFloat((float) var7[var9] / 4096.0F); - var5.writeFloat((float) var8[var9] / 4096.0F); - var5.writeFloat(1.0F); - var5.writeFloat(1.0F); - } else { - var5.writeFloatLE((float) var7[var9] / 4096.0F); - var5.writeFloatLE((float) var8[var9] / 4096.0F); - var5.writeFloatLE(1.0F); - var5.writeFloatLE(1.0F); - } - } - } - - ByteBuffer var10 = ByteBuffer.allocateDirect(var5.index).order(ByteOrder.nativeOrder()); - var10.put(var5.buffer, 0, var5.index); - var10.flip(); - this.aFloatBuffer2183 = var10.asFloatBuffer().asReadOnlyBuffer(); - this.method1749(); - this.method1750(); - } - - } - - public final void method21() { - if (this.anInt2186 >= 0) { - GL var1 = HDToolKit.gl; - var1.glCallList(this.anInt2186 + 1); - } - } - - public final int method24() { - return 0; - } - - public final void method22() { - if (this.anInt2186 >= 0) { - GL var1 = HDToolKit.gl; - var1.glCallList(this.anInt2186); - var1.glActiveTexture('\u84c1'); - var1.glMatrixMode(5890); - var1.glTranslatef((float) Unsorted.anInt144, (float) Unsorted.anInt3695, (float) LinkableRSString.anInt2587); - var1.glRotatef(-((float) TextureOperation9.anInt3103 * 360.0F) / 2048.0F, 0.0F, 1.0F, 0.0F); - var1.glRotatef(-((float) Class140_Sub7.anInt2938 * 360.0F) / 2048.0F, 1.0F, 0.0F, 0.0F); - var1.glRotatef(-180.0F, 1.0F, 0.0F, 0.0F); - var1.glMatrixMode(5888); - if (!Class88.Texture3DEnabled) { - var1.glBindTexture(3553, Class88.anIntArray1224[(int) ((float) (HDToolKit.anInt1791 * 64) * 0.0050F) % 64]); - } - - var1.glActiveTexture('\u84c0'); - if (this.anInt2182 != HDToolKit.anInt1791) { - int var2 = (HDToolKit.anInt1791 & 0xFF) * 256; - - for (int var3 = 0; var3 < 64; ++var3) { - this.aFloatBuffer2183.position(var2); - var1.glProgramLocalParameter4fvARB('\u8620', var3, this.aFloatBuffer2183); - var2 += 4; - } - - if (Class88.Texture3DEnabled) { - var1.glProgramLocalParameter4fARB('\u8620', 65, (float) HDToolKit.anInt1791 * 0.0050F, 0.0F, 0.0F, 1.0F); - } else { - var1.glProgramLocalParameter4fARB('\u8620', 65, 0.0F, 0.0F, 0.0F, 1.0F); - } - - this.anInt2182 = HDToolKit.anInt1791; - } - - } - } - - private void method1749() { - GL var1 = HDToolKit.gl; - this.anInt2186 = var1.glGenLists(2); - var1.glNewList(this.anInt2186, 4864); - var1.glActiveTexture('\u84c1'); - if (Class88.Texture3DEnabled) { - var1.glBindTexture('\u806f', Class88.anInt1228); - } - - var1.glTexEnvi(8960, '\u8571', 260); - var1.glTexEnvi(8960, '\u8572', 7681); - var1.glTexEnvi(8960, '\u8588', '\u8578'); - var1.glActiveTexture('\u84c0'); - var1.glBindProgramARB('\u8620', this.anInt2184); - var1.glEnable('\u8620'); - var1.glEndList(); - var1.glNewList(this.anInt2186 + 1, 4864); - var1.glActiveTexture('\u84c1'); - var1.glMatrixMode(5890); - var1.glLoadIdentity(); - var1.glMatrixMode(5888); - var1.glTexEnvi(8960, '\u8571', 8448); - var1.glTexEnvi(8960, '\u8572', 8448); - var1.glTexEnvi(8960, '\u8588', 5890); - var1.glDisable(Class88.Texture3DEnabled ? '\u806f' : 3553); - var1.glActiveTexture('\u84c0'); - var1.glBindProgramARB('\u8620', 0); - var1.glDisable('\u8620'); - var1.glDisable('\u8804'); - var1.glEndList(); - } - - private void method1750() { - if (this.anInt2186 >= 0) { - GL var1 = HDToolKit.gl; - int[] var2 = new int[1]; - var1.glBindProgramARB('\u8620', this.anInt2184); - var1.glProgramStringARB('\u8620', '\u8875', "!!ARBvp1.0\nATTRIB iPos = vertex.position;\nATTRIB iColour = vertex.color;\nOUTPUT oPos = result.position;\nOUTPUT oColour = result.color;\nOUTPUT oTexCoord0 = result.texcoord[0];\nOUTPUT oTexCoord1 = result.texcoord[1];\nOUTPUT oFogCoord = result.fogcoord;\nPARAM time = program.local[65];\nPARAM turbulence = program.local[64];\nPARAM lightAmbient = program.local[66]; \nPARAM pMatrix[4] = { state.matrix.projection };\nPARAM mvMatrix[4] = { state.matrix.modelview };\nPARAM ivMatrix[4] = { state.matrix.texture[1] };\nPARAM fNoise[64] = { program.local[0..63] };\nTEMP noise, clipPos, viewPos, worldPos;\nADDRESS noiseAddr;\nDP4 viewPos.x, mvMatrix[0], iPos;\nDP4 viewPos.y, mvMatrix[1], iPos;\nDP4 viewPos.z, mvMatrix[2], iPos;\nDP4 viewPos.w, mvMatrix[3], iPos;\nDP4 worldPos.x, ivMatrix[0], viewPos;\nDP4 worldPos.y, ivMatrix[1], viewPos;\nDP4 worldPos.z, ivMatrix[2], viewPos;\nDP4 worldPos.w, ivMatrix[3], viewPos;\nADD noise.x, worldPos.x, worldPos.z;SUB noise.y, worldPos.z, worldPos.x;MUL noise, noise, 0.0001220703125;\nFRC noise, noise;\nMUL noise, noise, 64;\nARL noiseAddr.x, noise.x;\nMOV noise.x, fNoise[noiseAddr.x].x;\nARL noiseAddr.x, noise.y;\nMOV noise.y, fNoise[noiseAddr.x].y;\nMUL noise, noise, turbulence.x;\nMAD oTexCoord0, worldPos.xzww, 0.0078125, noise;\nMOV oTexCoord0.w, 1;\nMUL oTexCoord1.xy, worldPos.xzww, 0.0009765625;\nMOV oTexCoord1.zw, time.xxxw;\nDP4 clipPos.x, pMatrix[0], viewPos;\nDP4 clipPos.y, pMatrix[1], viewPos;\nDP4 clipPos.z, pMatrix[2], viewPos;\nDP4 clipPos.w, pMatrix[3], viewPos;\nMUL oColour.xyz, iColour, lightAmbient;\nMOV oColour.w, 1;\nMOV oFogCoord.x, clipPos.z;\nMOV oPos, clipPos; \nEND".length(), "!!ARBvp1.0\nATTRIB iPos = vertex.position;\nATTRIB iColour = vertex.color;\nOUTPUT oPos = result.position;\nOUTPUT oColour = result.color;\nOUTPUT oTexCoord0 = result.texcoord[0];\nOUTPUT oTexCoord1 = result.texcoord[1];\nOUTPUT oFogCoord = result.fogcoord;\nPARAM time = program.local[65];\nPARAM turbulence = program.local[64];\nPARAM lightAmbient = program.local[66]; \nPARAM pMatrix[4] = { state.matrix.projection };\nPARAM mvMatrix[4] = { state.matrix.modelview };\nPARAM ivMatrix[4] = { state.matrix.texture[1] };\nPARAM fNoise[64] = { program.local[0..63] };\nTEMP noise, clipPos, viewPos, worldPos;\nADDRESS noiseAddr;\nDP4 viewPos.x, mvMatrix[0], iPos;\nDP4 viewPos.y, mvMatrix[1], iPos;\nDP4 viewPos.z, mvMatrix[2], iPos;\nDP4 viewPos.w, mvMatrix[3], iPos;\nDP4 worldPos.x, ivMatrix[0], viewPos;\nDP4 worldPos.y, ivMatrix[1], viewPos;\nDP4 worldPos.z, ivMatrix[2], viewPos;\nDP4 worldPos.w, ivMatrix[3], viewPos;\nADD noise.x, worldPos.x, worldPos.z;SUB noise.y, worldPos.z, worldPos.x;MUL noise, noise, 0.0001220703125;\nFRC noise, noise;\nMUL noise, noise, 64;\nARL noiseAddr.x, noise.x;\nMOV noise.x, fNoise[noiseAddr.x].x;\nARL noiseAddr.x, noise.y;\nMOV noise.y, fNoise[noiseAddr.x].y;\nMUL noise, noise, turbulence.x;\nMAD oTexCoord0, worldPos.xzww, 0.0078125, noise;\nMOV oTexCoord0.w, 1;\nMUL oTexCoord1.xy, worldPos.xzww, 0.0009765625;\nMOV oTexCoord1.zw, time.xxxw;\nDP4 clipPos.x, pMatrix[0], viewPos;\nDP4 clipPos.y, pMatrix[1], viewPos;\nDP4 clipPos.z, pMatrix[2], viewPos;\nDP4 clipPos.w, pMatrix[3], viewPos;\nMUL oColour.xyz, iColour, lightAmbient;\nMOV oColour.w, 1;\nMOV oFogCoord.x, clipPos.z;\nMOV oPos, clipPos; \nEND"); - var1.glGetIntegerv('\u864b', var2, 0); - if (var2[0] != -1) { - return; - } - } - - } - - public final void method23(int var1) { - if (this.anInt2186 >= 0) { - GL var2 = HDToolKit.gl; - var2.glActiveTexture('\u84c1'); - if ((var1 & 128) == 0) { - var2.glEnable(Class88.Texture3DEnabled ? '\u806f' : 3553); - } else { - var2.glDisable(Class88.Texture3DEnabled ? '\u806f' : 3553); - } - - var2.glActiveTexture('\u84c0'); - if ((var1 & 64) == 0) { - var2.glGetFloatv(2899, aFloatArray2185, 0); - var2.glProgramLocalParameter4fvARB('\u8620', 66, aFloatArray2185, 0); - } else { - var2.glProgramLocalParameter4fARB('\u8620', 66, 1.0F, 1.0F, 1.0F, 1.0F); - } - - int var3 = var1 & 3; - if (var3 == 2) { - var2.glProgramLocalParameter4fARB('\u8620', 64, 0.05F, 1.0F, 1.0F, 1.0F); - } else if (var3 == 3) { - var2.glProgramLocalParameter4fARB('\u8620', 64, 0.1F, 1.0F, 1.0F, 1.0F); - } else { - var2.glProgramLocalParameter4fARB('\u8620', 64, 0.025F, 1.0F, 1.0F, 1.0F); - } - - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class126.java b/Client/src/main/java/org/runite/client/Class126.java deleted file mode 100644 index 01eb662d6..000000000 --- a/Client/src/main/java/org/runite/client/Class126.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.Queue; - -public final class Class126 { - - static int anInt1665; - static Queue aClass13_1666 = new Queue(); - public static int anInt1676 = 0; - int anInt1663; - int anInt1664; - int anInt1667; - int anInt1670; - int anInt1673; - boolean aBoolean1674; - int anInt1675; - - - Class126(int var1, int var2, int var3, int var4, int var5, int var6, boolean var7) { - try { - this.anInt1667 = var2; - this.anInt1664 = var3; - this.anInt1675 = var1; - this.anInt1673 = var6; - this.aBoolean1674 = var7; - this.anInt1663 = var4; - this.anInt1670 = var5; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "rh.(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class127_Sub1.java b/Client/src/main/java/org/runite/client/Class127_Sub1.java deleted file mode 100644 index 8f37a5580..000000000 --- a/Client/src/main/java/org/runite/client/Class127_Sub1.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.runite.client; - -import javax.media.opengl.GL; -import java.nio.FloatBuffer; - - -final class Class127_Sub1 extends LoginHandler { - - private static int anInt2682; - - - static void method1755() { - GL var0 = HDToolKit.gl; - if(var0.isExtensionAvailable("GL_ARB_point_parameters")) { - float[] var1 = new float[]{1.0F, 0.0F, 5.0E-4F}; - var0.glPointParameterfvARB('\u8129', var1, 0); - FloatBuffer var2 = FloatBuffer.allocate(1); - var0.glGetFloatv('\u8127', var2); - float var3 = var2.get(0); - if(var3 > 1024.0F) { - var3 = 1024.0F; - } - - var0.glPointParameterfARB('\u8126', 1.0F); - var0.glPointParameterfARB('\u8127', var3); - } - - if(var0.isExtensionAvailable("GL_ARB_point_sprite")) { - } - - } - - static int method1757() { - return anInt2682; - } - - static void method1758(int var0) { - anInt2682 = var0; - } - - static { - new Class128(); - anInt2682 = 2; - new DataBuffer(131056); - } -} diff --git a/Client/src/main/java/org/runite/client/Class128.java b/Client/src/main/java/org/runite/client/Class128.java deleted file mode 100644 index 07e504bad..000000000 --- a/Client/src/main/java/org/runite/client/Class128.java +++ /dev/null @@ -1,174 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; -import org.rs09.client.data.ReferenceCache; - -import java.util.Arrays; - - -final class Class128 { - - static ReferenceCache aReferenceCache_1683 = new ReferenceCache(64); - static boolean aBoolean1685 = true; - - - Class128() { - try { - Node[] aClass3_Sub28Array1684 = new Node[8]; - - for (int var2 = 0; 8 > var2; ++var2) { - Node var3 = aClass3_Sub28Array1684[var2] = new Node(); - var3.previousNode = var3; - var3.nextNode = var3; - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "rm.(" + 8 + ')'); - } - } - - static void method1760(int var0, int var2) { - try { - if (WorldListCountry.localPlane > 3 || WorldListCountry.localPlane < 0) { - System.err.println("Local plane " + WorldListCountry.localPlane + " is out of bounds - rendering log=" + Arrays.toString(PlayerRendering.PLAYER_RENDER_LOG) + "!"); - WorldListCountry.localPlane %= 4; - } - LinkedList var3 = Class39.aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var2][var0]; - if (var3 == null) { - ObjectDefinition.method1688(WorldListCountry.localPlane, var2, var0); - } else { - int var4 = -99999999; - WorldMap var5 = null; - - WorldMap var6; - for (var6 = (WorldMap) var3.method1222(); null != var6; var6 = (WorldMap) var3.method1221()) { - ItemDefinition var7 = ItemDefinition.getItemDefinition(var6.aClass140_Sub7_3676.anInt2936); - int var8 = var7.value; - if (var7.stackingType == 1) { - var8 *= 1 + var6.aClass140_Sub7_3676.anInt2930; - } - - if (var4 < var8) { - var4 = var8; - var5 = var6; - } - } - - if (null == var5) { - ObjectDefinition.method1688(WorldListCountry.localPlane, var2, var0); - } else { - var3.method1216(var5); - Class140_Sub7 var12 = null; - Class140_Sub7 var14 = null; - - for (var6 = (WorldMap) var3.method1222(); var6 != null; var6 = (WorldMap) var3.method1221()) { - Class140_Sub7 var9 = var6.aClass140_Sub7_3676; - if (var5.aClass140_Sub7_3676.anInt2936 != var9.anInt2936) { - if (null == var12) { - var12 = var9; - } - - if (var12.anInt2936 != var9.anInt2936 && null == var14) { - var14 = var9; - } - } - } - - long var13 = 1610612736 + (var0 << 7) + var2; - TextureOperation30.method213(WorldListCountry.localPlane, var2, var0, Class121.method1736(WorldListCountry.localPlane, 1, 64 + 128 * var2, 64 + var0 * 128), var5.aClass140_Sub7_3676, var13, var12, var14); - } - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "rm.E(" + var0 + ',' + (byte) 65 + ',' + var2 + ')'); - } - } - - static Model method1763(int var1, int var2, int var3, int var4, Model var5, int var6) { - try { - Model var9 = (Model) LinkedList.aReferenceCache_939.get(var3); - if (var9 == null) { - Model_Sub1 var10 = Model_Sub1.method2015(CacheIndex.modelsIndex, var3); - if (var10 == null) { - return null; - } - - var9 = var10.method2008(64, 768, -50, -10, -50); - LinkedList.aReferenceCache_939.put(var9, var3); - } - - int var17 = var5.method1884(); - int var11 = var5.method1883(); - int var12 = var5.method1898(); - int var13 = var5.method1872(); - var9 = var9.method1882(true, true, true); - if (var1 != 0) { - var9.method1876(var1); - } - - int var15; - if (HDToolKit.highDetail) { - Class140_Sub1_Sub1 var14 = (Class140_Sub1_Sub1) var9; - if (var6 != Class121.method1736(WorldListCountry.localPlane, 1, var4 + var17, var2 + var12) || var6 != Class121.method1736(WorldListCountry.localPlane, 1, var4 - -var11, var13 + var2)) { - for (var15 = 0; var14.anInt3823 > var15; ++var15) { - var14.anIntArray3845[var15] += Class121.method1736(WorldListCountry.localPlane, 1, var14.anIntArray3822[var15] + var4, var14.anIntArray3848[var15] + var2) - var6; - } - - var14.aClass121_3839.aBoolean1640 = false; - var14.aClass6_3835.aBoolean98 = false; - } - } else { - Class140_Sub1_Sub2 var18 = (Class140_Sub1_Sub2) var9; - if (var6 != Class121.method1736(WorldListCountry.localPlane, 1, var17 + var4, var12 + var2) || var6 != Class121.method1736(WorldListCountry.localPlane, 1, var4 - -var11, var13 + var2)) { - for (var15 = 0; var18.anInt3891 > var15; ++var15) { - var18.anIntArray3883[var15] += Class121.method1736(WorldListCountry.localPlane, 1, var4 + var18.anIntArray3885[var15], var18.anIntArray3895[var15] + var2) - var6; - } - - var18.aBoolean3897 = false; - } - } - - return var9; - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "rm.D(" + true + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + (var5 != null ? "{...}" : "null") + ',' + var6 + ')'); - } - } - - static void method1764() { - for (int var3 = 0; var3 < Class3_Sub17.anInt2456; ++var3) { - for (int var4 = 0; var4 < Unsorted.anInt1234; ++var4) { - for (int var5 = 0; var5 < TextureOperation17.anInt3179; ++var5) { - Class3_Sub2 var6 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var3][var4][var5]; - if (var6 != null) { - Class70 var7 = var6.aClass70_2234; - if (var7 != null && var7.aClass140_1049.method1865()) { - TextureOperation30.method214(var7.aClass140_1049, var3, var4, var5, 1, 1); - if (var7.aClass140_1052 != null && var7.aClass140_1052.method1865()) { - TextureOperation30.method214(var7.aClass140_1052, var3, var4, var5, 1, 1); - var7.aClass140_1049.method1866(var7.aClass140_1052, 0, 0, 0, false); - var7.aClass140_1052 = var7.aClass140_1052.method1861(); - } - - var7.aClass140_1049 = var7.aClass140_1049.method1861(); - } - - for (int var8 = 0; var8 < var6.anInt2223; ++var8) { - Class25 var9 = var6.aClass25Array2221[var8]; - if (var9 != null && var9.aClass140_479.method1865()) { - TextureOperation30.method214(var9.aClass140_479, var3, var4, var5, var9.anInt495 - var9.anInt483 + 1, var9.anInt481 - var9.anInt478 + 1); - var9.aClass140_479 = var9.aClass140_479.method1861(); - } - } - - Class12 var10 = var6.aClass12_2230; - if (var10 != null && var10.object.method1865()) { - AudioChannel.method2162(var10.object, var3, var4, var5); - var10.object = var10.object.method1861(); - } - } - } - } - } - - } - -} diff --git a/Client/src/main/java/org/runite/client/Class129.java b/Client/src/main/java/org/runite/client/Class129.java deleted file mode 100644 index 56130df8b..000000000 --- a/Client/src/main/java/org/runite/client/Class129.java +++ /dev/null @@ -1,387 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; - -abstract class Class129 { - - static int[] anIntArray1690; - static int anInt1692 = 0; - static int[] anIntArray1693 = new int[128]; - static int[] anIntArray1695; - - - static int method1765(int var0) { - try { - return var0 >>> 7; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "s.D(" + var0 + ')'); - } - } - - static void method1768(int var0, int var2) { - try { - if (KeyboardListener.anInt1908 > 0) { - Class159.method2195(KeyboardListener.anInt1908); - KeyboardListener.anInt1908 = 0; - } - - short var3 = 256; - int var4 = 0; - int var5 = Toolkit.JAVA_TOOLKIT.width * var2; - int var6 = 0; - - for (int var7 = 1; var7 < var3 + -1; ++var7) { - int var8 = (var3 - var7) * Class3_Sub28_Sub5.anIntArray3592[var7] / var3; - if (0 > var8) { - var8 = 0; - } - - var4 += var8; - - int var9; - for (var9 = var8; var9 < 128; ++var9) { - int var11 = Toolkit.JAVA_TOOLKIT.getBuffer()[var5++ + var0]; - int var10 = Class159.anIntArray1681[var4++]; - if (var10 == 0) { - Class84.aSoftwareSprite_1381.anIntArray4081[var6++] = var11; - } else { - int var12 = 18 + var10; - if (var12 > 255) { - var12 = 255; - } - - int var13 = 256 - var10 - 18; - if (var13 > 255) { - var13 = 255; - } - - var10 = Class52.anIntArray861[var10]; - Class84.aSoftwareSprite_1381.anIntArray4081[var6++] = Unsorted.bitwiseAnd(var13 * Unsorted.bitwiseAnd(var11, 16711935) + Unsorted.bitwiseAnd(16711935, var10) * var12, -16711936) - -Unsorted.bitwiseAnd(Unsorted.bitwiseAnd(var10, 65280) * var12 - -(Unsorted.bitwiseAnd(65280, var11) * var13), 16711680) >> 8; - } - } - - for (var9 = 0; var9 < var8; ++var9) { - Class84.aSoftwareSprite_1381.anIntArray4081[var6++] = Toolkit.JAVA_TOOLKIT.getBuffer()[var0 + var5++]; - } - - var5 += Toolkit.JAVA_TOOLKIT.width + -128; - } - - if (HDToolKit.highDetail) { - Class22.method926(Class84.aSoftwareSprite_1381.anIntArray4081, var0, var2, Class84.aSoftwareSprite_1381.width, Class84.aSoftwareSprite_1381.height); - } else { - Class84.aSoftwareSprite_1381.method635(var0, var2); - } - - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "s.F(" + var0 + ',' + 95 + ',' + var2 + ')'); - } - } - - static void method1769(float[][] var0, byte[][] var1, byte[][] var2, Class43[] var3, int var4, int var5, float[][] var6, byte[][] var7, byte[][] var8, int[][] var9, float[][] var11) { - try { - for (int var12 = 0; var5 > var12; ++var12) { - Class43 var13 = var3[var12]; - if (var13.anInt704 == var4) { - int var15 = 0; - Class37 var14 = new Class37(); - int var16 = -var13.anInt698 + (var13.anInt703 >> 7); - int var17 = -var13.anInt698 + (var13.anInt708 >> 7); - if (var17 < 0) { - var15 -= var17; - var17 = 0; - } - - int var18 = var13.anInt698 + (var13.anInt708 >> 7); - if (var18 > 103) { - var18 = 103; - } - - int var19; - int var21; - short var20; - int var23; - int var22; - int var25; - int var35; - boolean var32; - for (var19 = var17; var19 <= var18; ++var19) { - var20 = var13.aShortArray706[var15]; - var21 = var16 + (var20 >> 8); - var22 = -1 + var21 - -(255 & var20); - if (103 < var22) { - var22 = 103; - } - - if (var21 < 0) { - var21 = 0; - } - - for (var23 = var21; var23 <= var22; ++var23) { - int var24 = 255 & var1[var23][var19]; - var25 = 255 & var8[var23][var19]; - boolean var26 = false; - FloorOverlayDefinition var27; - int[] var29; - int[] var28; - if (0 == var24) { - if (var25 == 0) { - continue; - } - - var27 = FloorOverlayDefinition.getFile(var25 + -1); - if (var27.anInt2103 == -1) { - continue; - } - - if (var7[var23][var19] != 0) { - var28 = Class134.anIntArrayArray1763[var7[var23][var19]]; - var14.anInt651 += 3 * (-2 + (var28.length >> 1)); - var14.anInt657 += var28.length >> 1; - continue; - } - } else if (var25 != 0) { - var27 = FloorOverlayDefinition.getFile(var25 - 1); - byte var42; - if (var27.anInt2103 == -1) { - var42 = var7[var23][var19]; - if (var42 != 0) { - var29 = Class25.anIntArrayArray499[var42]; - var14.anInt651 += 3 * (-2 + (var29.length >> 1)); - var14.anInt657 += var29.length >> 1; - } - continue; - } - - var42 = var7[var23][var19]; - if (0 != var42) { - var26 = true; - } - } - - Class25 var40 = Class75.method1336(var4, var23, var19); - if (null != var40) { - int var41 = (int) (var40.aLong498 >> 14) & 63; - if (var41 == 9) { - var29 = null; - int var30 = 3 & (int) (var40.aLong498 >> 20); - boolean var31; - int var34; - short var33; - if ((1 & var30) == 0) { - var32 = var22 >= 1 + var23; - var31 = var23 + -1 >= var21; - if (!var31 && var18 >= var19 - -1) { - var33 = var13.aShortArray706[1 + var15]; - var34 = var16 + (var33 >> 8); - var35 = var34 + (255 & var33); - var31 = var34 < var23 && var23 < var35; - } - - if (!var32 && -1 + var19 >= var17) { - var33 = var13.aShortArray706[var15 + -1]; - var34 = var16 + (var33 >> 8); - var35 = var34 - -(var33 & 0xFF); - var32 = var23 > var34 && var35 > var23; - } - - } else { - var31 = var21 <= -1 + var23; - var32 = var22 >= var23 + 1; - if (!var31 && -1 + var19 >= var17) { - var33 = var13.aShortArray706[-1 + var15]; - var34 = (var33 >> 8) + var16; - var35 = var34 + (255 & var33); - var31 = var23 > var34 && var35 > var23; - } - - if (!var32 && var18 >= 1 + var19) { - var33 = var13.aShortArray706[var15 + 1]; - var34 = (var33 >> 8) + var16; - var35 = var34 - -(255 & var33); - var32 = var23 > var34 && var23 < var35; - } - - } - if (var31 && var32) { - var29 = Class134.anIntArrayArray1763[0]; - } else if (var31) { - var29 = Class134.anIntArrayArray1763[1]; - } else if (var32) { - var29 = Class134.anIntArrayArray1763[1]; - } - - if (null != var29) { - var14.anInt651 += 3 * (var29.length >> 1) - 6; - var14.anInt657 += var29.length >> 1; - } - continue; - } - } - - if (var26) { - var29 = Class25.anIntArrayArray499[var7[var23][var19]]; - var28 = Class134.anIntArrayArray1763[var7[var23][var19]]; - var14.anInt651 += (-2 + (var28.length >> 1)) * 3; - var14.anInt651 += ((var29.length >> 1) - 2) * 3; - var14.anInt657 += var28.length >> 1; - var14.anInt657 += var29.length >> 1; - } else { - var28 = Class134.anIntArrayArray1763[0]; - var14.anInt651 += (-2 + (var28.length >> 1)) * 3; - var14.anInt657 += var28.length >> 1; - } - } - - ++var15; - } - - var15 = 0; - var14.method1020(); - if (-var13.anInt698 + (var13.anInt708 >> 7) < 0) { - var15 -= -var13.anInt698 + (var13.anInt708 >> 7); - } - - for (var19 = var17; var19 <= var18; ++var19) { - var20 = var13.aShortArray706[var15]; - var21 = (var20 >> 8) + var16; - var22 = -1 + (255 & var20) + var21; - if (var22 > 103) { - var22 = 103; - } - - if (0 > var21) { - var21 = 0; - } - - for (var23 = var21; var22 >= var23; ++var23) { - int var43 = 255 & var8[var23][var19]; - var25 = 255 & var1[var23][var19]; - byte var38 = var2[var23][var19]; - boolean var39 = false; - FloorOverlayDefinition var46; - if (var25 == 0) { - if (0 == var43) { - continue; - } - - var46 = FloorOverlayDefinition.getFile(var43 - 1); - if (-1 == var46.anInt2103) { - continue; - } - - if (var7[var23][var19] != 0) { - TextureOperation18.method284(var0, var9, var23, var6, var19, Class134.anIntArrayArray1763[var7[var23][var19]], var14, (byte) -88, var13, var11, var2[var23][var19]); - continue; - } - } else if (var43 != 0) { - var46 = FloorOverlayDefinition.getFile(-1 + var43); - if (-1 == var46.anInt2103) { - TextureOperation18.method284(var0, var9, var23, var6, var19, Class25.anIntArrayArray499[var7[var23][var19]], var14, (byte) 116, var13, var11, var2[var23][var19]); - continue; - } - - byte var48 = var7[var23][var19]; - if (var48 != 0) { - var39 = true; - } - } - - Class25 var44 = Class75.method1336(var4, var23, var19); - if (null != var44) { - int var49 = (int) (var44.aLong498 >> 14) & 63; - if (9 == var49) { - int[] var45 = null; - int var47 = 3 & (int) (var44.aLong498 >> 20); - int var36; - boolean var51; - short var50; - if ((1 & var47) == 0) { - var32 = var21 <= -1 + var23; - var51 = var23 + 1 <= var22; - if (!var32 && var18 >= var19 - -1) { - var50 = var13.aShortArray706[1 + var15]; - var35 = (var50 >> 8) + var16; - var36 = (var50 & 0xFF) + var35; - var32 = var23 > var35 && var23 < var36; - } - - if (!var51 && var19 - 1 >= var17) { - var50 = var13.aShortArray706[-1 + var15]; - var35 = var16 + (var50 >> 8); - var36 = (255 & var50) + var35; - var51 = var35 < var23 && var23 < var36; - } - - if (var32 && var51) { - var45 = Class134.anIntArrayArray1763[0]; - } else if (var32) { - var45 = Class134.anIntArrayArray1763[1]; - var38 = 1; - } else if (var51) { - var45 = Class134.anIntArrayArray1763[1]; - var38 = 3; - } - } else { - var32 = var23 - 1 >= var21; - var51 = var22 >= 1 + var23; - if (!var32 && var17 <= var19 - 1) { - var50 = var13.aShortArray706[var15 - 1]; - var35 = var16 + (var50 >> 8); - var36 = (255 & var50) + var35; - var32 = var23 > var35 && var36 > var23; - } - - if (!var51 && var19 + 1 <= var18) { - var50 = var13.aShortArray706[var15 - -1]; - var35 = var16 + (var50 >> 8); - var36 = (255 & var50) + var35; - var51 = var23 > var35 && var23 < var36; - } - - if (var32 && var51) { - var45 = Class134.anIntArrayArray1763[0]; - } else if (var32) { - var38 = 0; - var45 = Class134.anIntArrayArray1763[1]; - } else if (var51) { - var45 = Class134.anIntArrayArray1763[1]; - var38 = 2; - } - } - - if (null != var45) { - TextureOperation18.method284(var0, var9, var23, var6, var19, var45, var14, (byte) 98, var13, var11, var38); - } - continue; - } - } - - if (var39) { - TextureOperation18.method284(var0, var9, var23, var6, var19, Class25.anIntArrayArray499[var7[var23][var19]], var14, (byte) 96, var13, var11, var2[var23][var19]); - TextureOperation18.method284(var0, var9, var23, var6, var19, Class134.anIntArrayArray1763[var7[var23][var19]], var14, (byte) -117, var13, var11, var2[var23][var19]); - } else { - TextureOperation18.method284(var0, var9, var23, var6, var19, Class134.anIntArrayArray1763[0], var14, (byte) 61, var13, var11, var38); - } - } - - ++var15; - } - - if (var14.anInt653 > 0 && var14.anInt655 > 0) { - var14.method1019(); - var13.aClass37_712 = var14; - } - } - } - - } catch (RuntimeException var37) { - throw ClientErrorException.clientError(var37, "s.C(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + var5 + ',' + (var6 != null ? "{...}" : "null") + ',' + (var7 != null ? "{...}" : "null") + ',' + (var8 != null ? "{...}" : "null") + ',' + (var9 != null ? "{...}" : "null") + ',' + -8771 + ',' + (var11 != null ? "{...}" : "null") + ')'); - } - } - - abstract int method1767(int var1, int var2, int var3); - - abstract void method1770(); - -} diff --git a/Client/src/main/java/org/runite/client/Class129_Sub1.java b/Client/src/main/java/org/runite/client/Class129_Sub1.java deleted file mode 100644 index db7ab375b..000000000 --- a/Client/src/main/java/org/runite/client/Class129_Sub1.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.runite.client; - -final class Class129_Sub1 extends Class129 { - - private long aLong2683; - private int anInt2685; - private int anInt2688; - static AbstractSprite[] aAbstractSpriteArray2690; - private int anInt2691; - private int anInt2692; - static int anInt2693 = 0; - private final long[] aLongArray2694 = new long[10]; - static int[] anIntArray2696 = new int[2]; - - - final void method1770() { - try { - int var2; - for (var2 = 0; var2 < 10; ++var2) { - this.aLongArray2694[var2] = 0L; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "lj.A(" + -124 + ')'); - } - } - - final int method1767(int var1, int var2, int var3) { - try { - if (var1 != -1) { - this.method1767(-14, 83, 10); - } - - int var5 = this.anInt2691; - int var4 = this.anInt2688; - this.anInt2688 = 300; - this.anInt2691 = 1; - this.aLong2683 = TimeUtils.time(); - if (this.aLongArray2694[this.anInt2685] == 0L) { - this.anInt2688 = var4; - this.anInt2691 = var5; - } else if (this.aLongArray2694[this.anInt2685] < this.aLong2683) { - this.anInt2688 = (int) ((long) (var3 * 2560) / (this.aLong2683 + -this.aLongArray2694[this.anInt2685])); - } - - if (this.anInt2688 < 25) { - this.anInt2688 = 25; - } - - if (256 < this.anInt2688) { - this.anInt2688 = 256; - this.anInt2691 = (int) (-((this.aLong2683 - this.aLongArray2694[this.anInt2685]) / 10L) + (long) var3); - } - - if (this.anInt2691 > var3) { - this.anInt2691 = var3; - } - - this.aLongArray2694[this.anInt2685] = this.aLong2683; - this.anInt2685 = (1 + this.anInt2685) % 10; - int var6; - if (this.anInt2691 > 1) { - for (var6 = 0; var6 < 10; ++var6) { - if (this.aLongArray2694[var6] != 0L) { - this.aLongArray2694[var6] += this.anInt2691; - } - } - } - - if (var2 > this.anInt2691) { - this.anInt2691 = var2; - } - - TimeUtils.sleep(this.anInt2691); - - for (var6 = 0; 256 > this.anInt2692; ++var6) { - this.anInt2692 += this.anInt2688; - } - - this.anInt2692 &= 255; - return var6; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "lj.B(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - Class129_Sub1() { - try { - this.anInt2688 = 256; - this.anInt2691 = 1; - this.anInt2692 = 0; - this.aLong2683 = TimeUtils.time(); - - for (int var1 = 0; var1 < 10; ++var1) { - this.aLongArray2694[var1] = this.aLong2683; - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lj.()"); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class129_Sub2.java b/Client/src/main/java/org/runite/client/Class129_Sub2.java deleted file mode 100644 index 4b9a329c0..000000000 --- a/Client/src/main/java/org/runite/client/Class129_Sub2.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.runite.client; - -final class Class129_Sub2 extends Class129 { - - private long aLong2698 = System.nanoTime(); - - - final void method1770() { - this.aLong2698 = System.nanoTime(); - } - - final int method1767(int var1, int var2, int var3) { - if(var1 == -1) { - long var4 = 1000000L * (long)var2; - long var6 = this.aLong2698 - System.nanoTime(); - if(var6 < var4) { - var6 = var4; - } - - TimeUtils.sleep(var6 / 1000000L); - int var10 = 0; - - long var8; - for(var8 = System.nanoTime(); var10 < 10 && (var10 < 1 || this.aLong2698 < var8); this.aLong2698 += 1000000L * (long)var3) { - ++var10; - } - - if(var8 > this.aLong2698) { - this.aLong2698 = var8; - } - - return var10; - } else { - return 37; - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class131.java b/Client/src/main/java/org/runite/client/Class131.java deleted file mode 100644 index def93ae8d..000000000 --- a/Client/src/main/java/org/runite/client/Class131.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -public final class Class131 { - - public static int anInt1716; - static RSString password = RSString.parse(""); - static RSString username = RSString.parse(""); - static int anInt1719 = -1; - static CacheIndex skeletonsReferenceIndex; - public short[] aShortArray1718; - public int anInt1720; - public RSString[] aClass94Array1721; - public int[] anIntArray1725; - public short[] aShortArray1727; - byte[] aByteArray1730; - - Class131(int var1) { - try { - this.anInt1720 = var1; - this.aClass94Array1721 = new RSString[this.anInt1720]; - this.aShortArray1718 = new short[this.anInt1720]; - this.anIntArray1725 = new int[this.anInt1720]; - this.aByteArray1730 = new byte[this.anInt1720]; - this.aShortArray1727 = new short[this.anInt1720]; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "se.(" + var1 + ')'); - } - } - - static int method1788(int var0, int var1, int var2, int var3, boolean var4) { - try { - if (var4) { - int var5 = 15 & var3; - int var7 = var5 >= 4 ? (var5 != 12 && var5 != 14 ? var1 : var0) : var2; - int var6 = var5 < 8 ? var0 : var2; - return ((var5 & 1) != 0 ? -var6 : var6) - -((2 & var5) != 0 ? -var7 : var7); - } else { - return 127; - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "se.H(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method1790(int var0, int var1) { - try { - InterfaceWidget var3 = InterfaceWidget.getWidget(5, var0); - var3.flagUpdate(); - var3.anInt3598 = var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "se.B(" + var0 + ',' + var1 + ',' + 95 + ')'); - } - } - - static void method1793(RSString var0, RSString var1, int var2) { - try { - password = var1; - Class7.anInt2161 = var2; - username = var0; - if (username.equalsString(RSString.parse("")) || password.equalsString(RSString.parse(""))) { - Client.messageToDisplay = 3; - } else if (CS2Script.userCurrentWorldID == -1) { - Class163_Sub1_Sub1.anInt2246 = 0; - Class163_Sub1_Sub1.anInt1616 = 0; - Client.messageToDisplay = -3; - Class163_Sub1_Sub1.adminLoginStage = 1; - DataBuffer var4 = new DataBuffer(128); - var4.writeByte(10); - var4.writeShort((int) (Math.random() * 99999.0D)); - var4.writeShort(GameConfig.CLIENT_BUILD); - var4.writeLong(username.toLong()); - var4.writeInt((int) (Math.random() * 9.9999999E7D)); - var4.writeString(password); - var4.writeInt((int) (Math.random() * 9.9999999E7D)); - var4.rsaEncrypt(TextureOperation10.EXPONENT, TextureOperation31.MODULUS); - TextureOperation12.outgoingBuffer.index = 0; - TextureOperation12.outgoingBuffer.writeByte(210); - TextureOperation12.outgoingBuffer.writeByte(var4.index); - TextureOperation12.outgoingBuffer.putBytes(var4.buffer, var4.index); - } else { - Class24.method951(); - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "se.C(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + (byte) -38 + ')'); - } - } - - final boolean method1787(int var1) { - try { - - return (this.aByteArray1730[var1] & 8) != 0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "se.G(" + var1 + ',' + (byte) -124 + ')'); - } - } - - public final boolean method1789(int var1, int var2) { - try { - if (var2 != 530) { - this.method1794(-111, 26); - } - - return (4 & this.aByteArray1730[var1]) != 0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "se.A(" + var1 + ',' + var2 + ')'); - } - } - - public final int method1791(int var1, int var2) { - try { - return var2 != 8 ? 35 : this.aByteArray1730[var1] & 3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "se.I(" + var1 + ',' + var2 + ')'); - } - } - - final boolean method1794(int var1, int var2) { - try { - if (var2 != -20138) { - method1788(122, 38, -120, -29, false); - } - - return 0 == (this.aByteArray1730[var1] & 16); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "se.E(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class132.java b/Client/src/main/java/org/runite/client/Class132.java deleted file mode 100644 index 6c22e591f..000000000 --- a/Client/src/main/java/org/runite/client/Class132.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -final class Class132 { - - static int anInt1734 = 0; - static RSString[] aClass94Array1739 = new RSString[1000]; - static int anInt1741; - - - static void method1798(int var0, Class3_Sub4 var1) { - try { - long var2 = 0L; - int var4 = -1; - if (var0 <= 17) { - Class159.anInt1740 = -43; - } - - int var5 = 0; - if (var1.anInt2263 == 0) { - var2 = Class157.method2174(var1.anInt2250, var1.anInt2264, var1.anInt2248); - } - - int var6 = 0; - if (var1.anInt2263 == 1) { - var2 = Unsorted.method1395(var1.anInt2250, var1.anInt2264, var1.anInt2248); - } - - if (var1.anInt2263 == 2) { - var2 = Class3_Sub28_Sub5.method557(var1.anInt2250, var1.anInt2264, var1.anInt2248); - } - - if (var1.anInt2263 == 3) { - var2 = Class3_Sub2.method104(var1.anInt2250, var1.anInt2264, var1.anInt2248); - } - - if (var2 != 0L) { - var4 = Integer.MAX_VALUE & (int) (var2 >>> 32); - var6 = (int) var2 >> 20 & 3; - var5 = ((int) var2 & 516214) >> 14; - } - - var1.anInt2254 = var4; - var1.anInt2253 = var5; - var1.anInt2257 = var6; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "sf.B(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method1799(byte var0, CacheIndex var1) { - try { - TextureOperation27.aClass153_3098 = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sf.C(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method1801() { - try { - int var1 = FontType.bold.method682(RSString.parse(GameConfig.RCM_TITLE)); - int var2; - int var3; - for (var2 = 0; Unsorted.menuOptionCount > var2; ++var2) { - var3 = FontType.bold.method682(Unsorted.method802(var2)); - if (var3 > var1) { - var1 = var3; - } - } - var2 = 15 * Unsorted.menuOptionCount + 21; - int var4 = Class38_Sub1.anInt2612; - var1 += 8; - var3 = NPCDefinition.anInt1297 + -(var1 / 2); - if (Class140_Sub7.canvasHeight < var4 + var2) { - var4 = Class140_Sub7.canvasHeight + -var2; - } - - if (Class23.canvasWidth < var3 + var1) { - var3 = -var1 + Class23.canvasWidth; - } - - if (var3 < 0) { - var3 = 0; - } - - if (var4 < 0) { - var4 = 0; - } - - if (Unsorted.anInt3660 == 1) { - if (TextureOperation8.anInt3460 == NPCDefinition.anInt1297 && Class38_Sub1.anInt2612 == Unsorted.anInt2099) { - Class21.anInt3537 = Unsorted.menuOptionCount * 15 - -(!Unsorted.aBoolean1951 ? 22 : 26); - Unsorted.anInt3660 = 0; - Class21.anInt3395 = var4; - Class21.anInt1462 = var3; - Class38_Sub1.aBoolean2615 = true; - Class21.anInt3552 = var1; - } - } else if (Class163_Sub1.anInt2993 == NPCDefinition.anInt1297 && Class38_Sub1.anInt2614 == Class38_Sub1.anInt2612) { - Class21.anInt1462 = var3; - Unsorted.anInt3660 = 0; - Class21.anInt3552 = var1; - Class21.anInt3395 = var4; - Class21.anInt3537 = (Unsorted.aBoolean1951 ? 26 : 22) + Unsorted.menuOptionCount * 15; - Class38_Sub1.aBoolean2615 = true; - } else { - Unsorted.anInt2099 = Class38_Sub1.anInt2614; - TextureOperation8.anInt3460 = Class163_Sub1.anInt2993; - Unsorted.anInt3660 = 1; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "sf.D(" + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class133.java b/Client/src/main/java/org/runite/client/Class133.java deleted file mode 100644 index bf026d204..000000000 --- a/Client/src/main/java/org/runite/client/Class133.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.runite.client; - -final class Class133 { - - static int[] anIntArray1743 = new int[25]; - static int anInt1748; - static int anInt1753; - static int anInt1754; - static int[] inputTextCodeArray = new int[128]; - int anInt1746; - int anInt1747; - int anInt1750; - int anInt1752; - int anInt1757; - private byte aByte1742; - - - public Class133() { - } - - Class133(DataBuffer var1) { - try { - this.aByte1742 = var1.readSignedByte(); - this.anInt1752 = var1.readUnsignedShort(); - this.anInt1757 = var1.readInt(); - this.anInt1747 = var1.readInt(); - this.anInt1746 = var1.readInt(); - this.anInt1750 = var1.readInt(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sg.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method1803() { - try { - Texture.aReferenceCache_1146.clearSoftReferences(); - Class159.aReferenceCache_2016.clearSoftReferences(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "sg.D(" + (byte) 22 + ')'); - } - } - - final int method1804() { - try { - - return this.aByte1742 & 7; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sg.B(" + false + ')'); - } - } - - final int method1805() { - try { - return 8 != (this.aByte1742 & 8) ? 0 : 1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sg.C(" + (byte) -33 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class134.java b/Client/src/main/java/org/runite/client/Class134.java deleted file mode 100644 index 4838fa147..000000000 --- a/Client/src/main/java/org/runite/client/Class134.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.runite.client; - -final class Class134 { - - static LinkedList aLinkedList_1758 = new LinkedList(); - static int anInt1759 = 0; - static int anInt1761 = -1; - static int anInt1762 = 0; - static int[][] anIntArrayArray1763 = new int[][]{{0, 128, 0, 0, 128, 0, 128, 128}, {0, 128, 0, 0, 128, 0}, {0, 0, 64, 128, 0, 128}, {128, 128, 64, 128, 128, 0}, {0, 0, 128, 0, 128, 128, 64, 128}, {0, 128, 0, 0, 128, 0, 64, 128}, {64, 128, 0, 128, 0, 0, 64, 0}, {0, 0, 64, 0, 0, 64}, {128, 0, 128, 128, 0, 128, 0, 64, 64, 0}, {0, 128, 0, 0, 32, 64, 64, 96, 128, 128}, {0, 0, 128, 0, 128, 128, 64, 96, 32, 64}, {0, 0, 128, 0, 96, 32, 32, 32}}; - static long[] aLongArray1766 = new long[32]; - - - static void method1808(int var0, boolean var1, int var3, boolean var4) { - try { - Unsorted.method1047(var0, var3, Unsorted.aClass44_Sub1Array3201.length - 1, var4, 0, var1); - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "sh.C(" + var0 + ',' + var1 + ',' + (byte) 30 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method1809(int var0, long[] var1, int var2, int var3, Object[] var4) { - try { - if (var2 < 85) { - aLinkedList_1758 = null; - } - - if (var0 > var3) { - int var6 = var3; - int var5 = (var3 - -var0) / 2; - long var7 = var1[var5]; - var1[var5] = var1[var0]; - var1[var0] = var7; - Object var9 = var4[var5]; - var4[var5] = var4[var0]; - var4[var0] = var9; - - for (int var10 = var3; var0 > var10; ++var10) { - if (var7 + (long) (1 & var10) > var1[var10]) { - long var11 = var1[var10]; - var1[var10] = var1[var6]; - var1[var6] = var11; - Object var13 = var4[var10]; - var4[var10] = var4[var6]; - var4[var6++] = var13; - } - } - - var1[var0] = var1[var6]; - var1[var6] = var7; - var4[var0] = var4[var6]; - var4[var6] = var9; - method1809(-1 + var6, var1, 107, var3, var4); - method1809(var0, var1, 89, var6 - -1, var4); - } - - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "sh.B(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + var3 + ',' + (var4 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class136.java b/Client/src/main/java/org/runite/client/Class136.java deleted file mode 100644 index f76c8df52..000000000 --- a/Client/src/main/java/org/runite/client/Class136.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -final class Class136 { - - static LinkedList aLinkedList_1424 = new LinkedList(); - static int anInt1771; - static ReferenceCache aReferenceCache_1772 = new ReferenceCache(64); - static Class64 aClass64_1778; - static short[] aShortArray1779 = new short[256]; - LDIndexedSprite aClass109_Sub1_1770; - GameObject aClass140_1777; - - static void method1814(int var0, int var1, int var2, int var3, int var4, int var8, int var9, int var10) { - try { - int var11 = var2 - var4; - int var13 = -1; - if (AbstractSprite.anInt3704 > 0) { - if (Class3_Sub28_Sub8.anInt3611 <= 10) { - var13 = 5 * Class3_Sub28_Sub8.anInt3611; - } else { - var13 = -((-10 + Class3_Sub28_Sub8.anInt3611) * 5) + 50; - } - } - - int var12 = -var9 + var1; - int var15 = 983040 / var8; - int var16 = 983040 / var3; - - for (int var17 = -var15; var17 < var11 - -var15; ++var17) { - int var18 = - -(var17 * var8) >> 16; - int var19 = var8 * (var17 + 1) >> 16; - int var20 = -var18 + var19; - if (var20 > 0) { - int var21 = var4 + var17 >> 6; - var18 += var0; - if (var21 >= 0 && var21 <= -1 + Class44.anIntArrayArrayArray720.length) { - int[][] var22 = Class44.anIntArrayArrayArray720[var21]; - - for (int var23 = -var16; var23 < var12 - -var16; ++var23) { - int var25 = - -(var3 * (var23 - -1)) >> 16; - int var24 = var23 * var3 >> 16; - int var26 = var25 + -var24; - if (0 < var26) { - var24 += var10; - int var27 = var9 + var23 >> 6; - if (var27 >= 0 && -1 + var22.length >= var27 && null != var22[var27]) { - int var28 = (63 & var17 + var4) + (4032 & var9 + var23 << 6); - int var29 = var22[var27][var28]; - if (var29 != 0) { - ObjectDefinition var14 = ObjectDefinition.getObjectDefinition(-1 + var29); - if (!Class3_Sub24_Sub4.aBooleanArray3503[var14.MapIcon]) { - if (var13 != -1 && Class8.anInt101 == var14.MapIcon) { - Class3_Sub23 var30 = new Class3_Sub23(); - var30.anInt2531 = var18; - var30.anInt2539 = var24; - var30.anInt2532 = var14.MapIcon; - aLinkedList_1424.method1215(var30); - } else { - GameObject.aSoftwareSpriteArray1839[var14.MapIcon].drawAt(var18 + -7, -7 + var24); - } - } - } - } - } - } - } - } - } - - for (Class3_Sub23 var32 = (Class3_Sub23) aLinkedList_1424.method1222(); null != var32; var32 = (Class3_Sub23) aLinkedList_1424.method1221()) { - Class74.method1330(var32.anInt2531, var32.anInt2539, 15, var13); - Class74.method1330(var32.anInt2531, var32.anInt2539, 13, var13); - Class74.method1330(var32.anInt2531, var32.anInt2539, 11, var13); - Class74.method1330(var32.anInt2531, var32.anInt2539, 9, var13); - GameObject.aSoftwareSpriteArray1839[var32.anInt2532].drawAt(-7 + var32.anInt2531, -7 + var32.anInt2539); - } - - aLinkedList_1424.method1211(-76); - } catch (RuntimeException var31) { - throw ClientErrorException.clientError(var31, "sm.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + 0 + ',' + 0 + ',' + 127 + ',' + var8 + ',' + var9 + ',' + var10 + ')'); - } - } - - static void method1816(int var0, int var1) { - try { - if (var1 == -7) { - Class129_Sub1.anIntArray2696 = new int[var0]; - Class159.anIntArray2021 = new int[var0]; - SequenceDefinition.anIntArray1871 = new int[var0]; - Player.anIntArray3959 = new int[var0]; - Unsorted.anIntArray686 = new int[var0]; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sm.C(" + var0 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class137.java b/Client/src/main/java/org/runite/client/Class137.java deleted file mode 100644 index 95ff9bdea..000000000 --- a/Client/src/main/java/org/runite/client/Class137.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.runite.client; - -final class Class137 { - - - static int method1817() { - try { - - return !Unsorted.aBoolean1084 ? (!NPC.method1986((byte) 70 ^ 28) ? 1 : (Unsorted.aBoolean3604 ? 2 : 1)) : 0; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "tc.B(" + (byte) 70 + ')'); - } - } - - static void method1819(int var0, int var1, RSInterface var2, int var4, int var5, int var6, int var7) { - try { - - if (Class158_Sub1.aBoolean2981) { - Class19.anInt433 = 32; - } else { - Class19.anInt433 = 0; - } - - Class158_Sub1.aBoolean2981 = false; - int var8; - if (TextureOperation21.anInt3069 != 0) { - if (var4 >= var5 && var4 < var5 + 16 && var0 >= var6 && var6 - -16 > var0) { - var2.anInt208 -= 4; - Class20.method909(var2); - } else if (var4 >= var5 && 16 + var5 > var4 && var1 + (var6 - 16) <= var0 && var0 < var1 + var6) { - var2.anInt208 += 4; - Class20.method909(var2); - } else if (var5 - Class19.anInt433 <= var4 && var4 < var5 + 16 + Class19.anInt433 && var0 >= 16 + var6 && var1 + var6 - 16 > var0) { - var8 = var1 * (-32 + var1) / var7; - if (8 > var8) { - var8 = 8; - } - - int var10 = -32 + (var1 - var8); - int var9 = -(var8 / 2) + -16 + -var6 + var0; - var2.anInt208 = (-var1 + var7) * var9 / var10; - Class20.method909(var2); - Class158_Sub1.aBoolean2981 = true; - } - } - - if (Class29.anInt561 != 0) { - var8 = var2.width; - if (var4 >= -var8 + var5 && var0 >= var6 && var4 < 16 + var5 && var1 + var6 >= var0) { - var2.anInt208 += 45 * Class29.anInt561; - Class20.method909(var2); - } - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "tc.A(" + var0 + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + (byte) -101 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class139.java b/Client/src/main/java/org/runite/client/Class139.java deleted file mode 100644 index 577d8df20..000000000 --- a/Client/src/main/java/org/runite/client/Class139.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.runite.client; - -final class Class139 { - - static int anInt1823; - static int screenLowerX; - static boolean aBoolean1827; - static int anInt1829 = 0; - - -} diff --git a/Client/src/main/java/org/runite/client/Class140_Sub1_Sub1.java b/Client/src/main/java/org/runite/client/Class140_Sub1_Sub1.java deleted file mode 100644 index 81342031e..000000000 --- a/Client/src/main/java/org/runite/client/Class140_Sub1_Sub1.java +++ /dev/null @@ -1,3712 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.config.GameConfig; -import org.rs09.client.util.ArrayUtils; - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; -import java.util.Objects; - -final class Class140_Sub1_Sub1 extends Model { - - private short[] aShortArray3808; - boolean aBoolean3809 = false; - private short[] aShortArray3810; - private short[] aShortArray3811; - private Class156 aClass156_3812; - private short[] aShortArray3813; - private Class121 aClass121_3814; - private Class121 aClass121_3815; - private byte[] aByteArray3816; - private Class121 aClass121_3817; - private static DataBuffer aClass3_Sub30_3818 = new DataBuffer(10000); - private short aShort3819; - private byte[] aByteArray3820; - private static long[] aLongArray3821; - int[] anIntArray3822; - int anInt3823 = 0; - private float[] aFloatArray3824; - private int[][] anIntArrayArray3825; - private short[] aShortArray3826; - private short[] aShortArray3827; - private short[] aShortArray3828; - private int[][] anIntArrayArray3829; - private short[] aShortArray3830; - private short[] aShortArray3831; - private short[] aShortArray3832; - private int anInt3833 = 0; - private static ByteBuffer aByteBuffer3834; - Class6 aClass6_3835; - private byte aByte3836 = 0; - private short[] aShortArray3837; - private int[] anIntArray3838; - Class121 aClass121_3839; - private int[] anIntArray3840; - private short[] aShortArray3841; - private static Class140_Sub1_Sub1 aClass140_Sub1_Sub1_3842 = new Class140_Sub1_Sub1(); - private Class18 aClass18_3843; - private int[] anIntArray3844; - int[] anIntArray3845; - private Class121 aClass121_3846; - private float[] aFloatArray3847; - int[] anIntArray3848; - private short aShort3849; - private static Class140_Sub1_Sub1 aClass140_Sub1_Sub1_3850 = new Class140_Sub1_Sub1(); - private byte aByte3851 = 0; - private int anInt3852 = 0; - private static Class140_Sub1_Sub1 aClass140_Sub1_Sub1_3853 = new Class140_Sub1_Sub1(); - private static Class140_Sub1_Sub1 aClass140_Sub1_Sub1_3854 = new Class140_Sub1_Sub1(); - private static Class140_Sub1_Sub1 aClass140_Sub1_Sub1_3855 = new Class140_Sub1_Sub1(); - private static Class140_Sub1_Sub1 aClass140_Sub1_Sub1_3856 = new Class140_Sub1_Sub1(); - private static int[] anIntArray3857 = new int[1]; - private static float aFloat3858; - private static int anInt3859; - private static float aFloat3860; - private static int[] anIntArray3861 = new int[1]; - private static int anInt3862; - private static float aFloat3863; - private static float aFloat3864; - private static int anInt3865; - private static float aFloat3866; - private static float aFloat3867; - private static boolean aBoolean3868 = false; - - - private static int method1901(float var0, float var1, float var2) { - float var3 = var0 < 0.0F ? -var0 : var0; - float var4 = var1 < 0.0F ? -var1 : var1; - float var5 = var2 < 0.0F ? -var2 : var2; - return var4 > var3 && var4 > var5 ? (var1 > 0.0F ? 0 : 1) : (var5 > var3 && var5 > var4 ? (var2 > 0.0F ? 2 : 3) : (var0 > 0.0F ? 4 : 5)); - } - - final Model method1882(boolean var1, boolean var2, boolean var3) { - return this.method1923(var1, var2, var3, aClass140_Sub1_Sub1_3854, aClass140_Sub1_Sub1_3853); - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - if (this.anInt3833 != 0) { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - short var13 = this.aClass6_3835.aShort93; - short var14 = this.aClass6_3835.aShort91; - short var15 = this.aClass6_3835.aShort92; - int var16 = var8 * var5 - var6 * var4 >> 16; - int var17 = var7 * var2 + var16 * var3 >> 16; - int var18 = var17 + (var13 * var3 + var15 * var2 >> 16); - if (var18 > 50) { - int var19 = var17 + (-var13 * var3 + var14 * var2 >> 16); - if (var19 < GameConfig.RENDER_DISTANCE_VALUE) { - int var20 = var8 * var4 + var6 * var5 >> 16; - int var21 = var20 + var13 << 9; - if (var21 / var18 > Class139.screenLowerX) { - int var22 = var20 - var13 << 9; - if (var22 / var18 < Class145.screenUpperX) { - int var23 = var7 * var3 - var16 * var2 >> 16; - int var24 = var23 + (var13 * var2 + var15 * var3 >> 16) << 9; - if (var24 / var18 > Class1.screenUpperY) { - int var25 = var23 + (-var13 * var2 + var14 * var3 >> 16) << 9; - if (var25 / var18 < AtmosphereParser.screenLowerY) { - int var26 = 0; - int var27 = 0; - if (var1 != 0) { - var26 = Class51.anIntArray840[var1]; - var27 = Class51.anIntArray851[var1]; - } - - if (var9 > 0L && TextureOperation36.aBoolean3094 && var19 > 0) { - int var28; - int var30; - if (var20 > 0) { - var28 = var22 / var18; - var30 = var21 / var19; - } else { - var28 = var22 / var19; - var30 = var21 / var18; - } - - int var29; - int var31; - if (var23 > 0) { - var29 = var25 / var18; - var31 = var24 / var19; - } else { - var29 = var25 / var19; - var31 = var24 / var18; - } - - if (Unsorted.anInt3642 >= var28 && Unsorted.anInt3642 <= var30 && RenderAnimationDefinition.anInt384 >= var29 && RenderAnimationDefinition.anInt384 <= var31) { - var28 = 999999; - var30 = -999999; - var29 = 999999; - var31 = -999999; - short var32 = this.aClass6_3835.aShort95; - short var33 = this.aClass6_3835.aShort94; - short var34 = this.aClass6_3835.aShort97; - short var35 = this.aClass6_3835.aShort96; - int[] var36 = new int[]{var32, var33, var32, var33, var32, var33, var32, var33}; - int[] var37 = new int[]{var34, var34, var35, var35, var34, var34, var35, var35}; - int[] var38 = new int[]{var14, var14, var14, var14, var15, var15, var15, var15}; - - int var39; - int var42; - int var43; - int var40; - int var41; - int var44; - int var45; - for (var39 = 0; var39 < 8; ++var39) { - var40 = var36[var39]; - var41 = var38[var39]; - var42 = var37[var39]; - if (var1 != 0) { - var43 = var42 * var26 + var40 * var27 >> 16; - var42 = var42 * var27 - var40 * var26 >> 16; - var40 = var43; - } - - var40 += var6; - var41 += var7; - var42 += var8; - var43 = var42 * var4 + var40 * var5 >> 16; - var42 = var42 * var5 - var40 * var4 >> 16; - var40 = var43; - var43 = var41 * var3 - var42 * var2 >> 16; - var42 = var41 * var2 + var42 * var3 >> 16; - if (var42 > 0) { - var44 = (var40 << 9) / var42; - var45 = (var43 << 9) / var42; - if (var44 < var28) { - var28 = var44; - } - - if (var44 > var30) { - var30 = var44; - } - - if (var45 < var29) { - var29 = var45; - } - - if (var45 > var31) { - var31 = var45; - } - } - } - - if (Unsorted.anInt3642 >= var28 && Unsorted.anInt3642 <= var30 && RenderAnimationDefinition.anInt384 >= var29 && RenderAnimationDefinition.anInt384 <= var31) { - if (this.aBoolean2699) { - TextureOperation38.aLongArray3448[Unsorted.anInt59++] = var9; - } else { - if (anIntArray3861.length < this.anInt3833) { - anIntArray3861 = new int[this.anInt3833]; - anIntArray3857 = new int[this.anInt3833]; - } - - var39 = 0; - - label118: - while (true) { - if (var39 >= this.anInt3823) { - var39 = 0; - - while (true) { - if (var39 >= this.anInt3852) { - break label118; - } - - short var53 = this.aShortArray3811[var39]; - short var52 = this.aShortArray3830[var39]; - short var51 = this.aShortArray3831[var39]; - if (this.method1927(Unsorted.anInt3642, RenderAnimationDefinition.anInt384, anIntArray3857[var53], anIntArray3857[var52], anIntArray3857[var51], anIntArray3861[var53], anIntArray3861[var52], anIntArray3861[var51])) { - TextureOperation38.aLongArray3448[Unsorted.anInt59++] = var9; - break label118; - } - - ++var39; - } - } - - var40 = this.anIntArray3822[var39]; - var41 = this.anIntArray3845[var39]; - var42 = this.anIntArray3848[var39]; - if (var1 != 0) { - var43 = var42 * var26 + var40 * var27 >> 16; - var42 = var42 * var27 - var40 * var26 >> 16; - var40 = var43; - } - - var40 += var6; - var41 += var7; - var42 += var8; - var43 = var42 * var4 + var40 * var5 >> 16; - var42 = var42 * var5 - var40 * var4 >> 16; - var40 = var43; - var43 = var41 * var3 - var42 * var2 >> 16; - var42 = var41 * var2 + var42 * var3 >> 16; - if (var42 < 50) { - break; - } - - var44 = (var40 << 9) / var42; - var45 = (var43 << 9) / var42; - int var46 = this.anIntArray3838[var39]; - int var47 = this.anIntArray3838[var39 + 1]; - - for (int var48 = var46; var48 < var47; ++var48) { - int var49 = this.aShortArray3828[var48] - 1; - if (var49 == -1) { - break; - } - - anIntArray3861[var49] = var44; - anIntArray3857[var49] = var45; - } - - ++var39; - } - } - } - } - } - - GL var50 = HDToolKit.gl; - var50.glPushMatrix(); - var50.glTranslatef((float) var6, (float) var7, (float) var8); - var50.glRotatef((float) var1 * 0.17578125F, 0.0F, 1.0F, 0.0F); - this.method1930(); - var50.glRotatef((float) (-var1) * 0.17578125F, 0.0F, 1.0F, 0.0F); - var50.glTranslatef((float) (-var6), (float) (-var7), (float) (-var8)); - var50.glPopMatrix(); - } - } - } - } - } - } - } - } - - final void method1902() { - if (this.aShortArray3810 == null) { - this.method1900(); - } else { - int var1; - for (var1 = 0; var1 < this.anInt3823; ++var1) { - int var2 = this.anIntArray3848[var1]; - this.anIntArray3848[var1] = this.anIntArray3822[var1]; - this.anIntArray3822[var1] = -var2; - } - - for (var1 = 0; var1 < this.anInt3833; ++var1) { - short var3 = this.aShortArray3837[var1]; - this.aShortArray3837[var1] = this.aShortArray3810[var1]; - this.aShortArray3810[var1] = (short) (-var3); - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - if (this.aClass121_3815 != null) { - this.aClass121_3815.aBoolean1640 = false; - } - - } - } - - final int method1903() { - return this.aShort3849; - } - - final void method1886(int var1) { - int var2 = Class51.anIntArray840[var1]; - int var3 = Class51.anIntArray851[var1]; - - for (int var4 = 0; var4 < this.anInt3823; ++var4) { - int var5 = this.anIntArray3845[var4] * var2 + this.anIntArray3822[var4] * var3 >> 16; - this.anIntArray3845[var4] = this.anIntArray3845[var4] * var3 - this.anIntArray3822[var4] * var2 >> 16; - this.anIntArray3822[var4] = var5; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - final int method1883() { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - return this.aClass6_3835.aShort94; - } - - final int method1872() { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - return this.aClass6_3835.aShort96; - } - - final boolean method1865() { - return this.aBoolean3809 && this.anIntArray3822 != null && this.aShortArray3810 != null; - } - - final int method1898() { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - return this.aClass6_3835.aShort97; - } - - final void method1897(int var1, int var2, int var3) { - for (int var4 = 0; var4 < this.anInt3823; ++var4) { - this.anIntArray3822[var4] += var1; - this.anIntArray3845[var4] += var2; - this.anIntArray3848[var4] += var3; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - final Model method1890(boolean var1, boolean var2, boolean var3) { - return this.method1923(var1, var2, var3, aClass140_Sub1_Sub1_3856, aClass140_Sub1_Sub1_3855); - } - - private static void method1904(int var0, int var1, int var2, int var3, int var4, int var5, float[] var6, float var7, int var8, float var9) { - var0 -= var3; - var1 -= var4; - var2 -= var5; - float var10 = (float) var0 * var6[0] + (float) var1 * var6[1] + (float) var2 * var6[2]; - float var11 = (float) var0 * var6[3] + (float) var1 * var6[4] + (float) var2 * var6[5]; - float var12 = (float) var0 * var6[6] + (float) var1 * var6[7] + (float) var2 * var6[8]; - float var13 = (float) Math.atan2(var10, var12) / 6.2831855F + 0.5F; - if (var7 != 1.0F) { - var13 *= var7; - } - - float var14 = var11 + 0.5F + var9; - float var15; - if (var8 == 1) { - var15 = var13; - var13 = -var14; - var14 = var15; - } else if (var8 == 2) { - var13 = -var13; - var14 = -var14; - } else if (var8 == 3) { - var15 = var13; - var13 = var14; - var14 = -var15; - } - - aFloat3863 = var13; - aFloat3866 = var14; - } - - final void method1866(GameObject var1, int var2, int var3, int var4, boolean var5) { - Class140_Sub1_Sub1 var6 = (Class140_Sub1_Sub1) var1; - if (this.anInt3852 != 0 && var6.anInt3852 != 0) { - int var7 = var6.anInt3823; - int[] var8 = var6.anIntArray3822; - int[] var9 = var6.anIntArray3845; - int[] var10 = var6.anIntArray3848; - short[] var11 = var6.aShortArray3810; - short[] var12 = var6.aShortArray3826; - short[] var13 = var6.aShortArray3837; - short[] var14 = var6.aShortArray3841; - short[] var15; - short[] var17; - short[] var16; - short[] var18; - if (this.aClass18_3843 == null) { - var15 = null; - var16 = null; - var17 = null; - var18 = null; - } else { - var15 = this.aClass18_3843.aShortArray417; - var16 = this.aClass18_3843.aShortArray419; - var17 = this.aClass18_3843.aShortArray418; - var18 = this.aClass18_3843.aShortArray416; - } - - short[] var19; - short[] var21; - short[] var20; - short[] var22; - if (var6.aClass18_3843 == null) { - var19 = null; - var20 = null; - var21 = null; - var22 = null; - } else { - var19 = var6.aClass18_3843.aShortArray417; - var20 = var6.aClass18_3843.aShortArray419; - var21 = var6.aClass18_3843.aShortArray418; - var22 = var6.aClass18_3843.aShortArray416; - } - - int[] var23 = var6.anIntArray3838; - short[] var24 = var6.aShortArray3828; - if (!var6.aClass6_3835.aBoolean98) { - var6.method1917(); - } - - short var25 = var6.aClass6_3835.aShort91; - short var26 = var6.aClass6_3835.aShort92; - short var27 = var6.aClass6_3835.aShort95; - short var28 = var6.aClass6_3835.aShort94; - short var29 = var6.aClass6_3835.aShort97; - short var30 = var6.aClass6_3835.aShort96; - - for (int var31 = 0; var31 < this.anInt3823; ++var31) { - int var32 = this.anIntArray3845[var31] - var3; - if (var32 >= var25 && var32 <= var26) { - int var33 = this.anIntArray3822[var31] - var2; - if (var33 >= var27 && var33 <= var28) { - int var34 = this.anIntArray3848[var31] - var4; - if (var34 >= var29 && var34 <= var30) { - int var35 = -1; - int var36 = this.anIntArray3838[var31]; - int var37 = this.anIntArray3838[var31 + 1]; - - int var38; - for (var38 = var36; var38 < var37; ++var38) { - var35 = this.aShortArray3828[var38] - 1; - if (var35 == -1 || this.aShortArray3841[var35] != 0) { - break; - } - } - - if (var35 != -1) { - for (var38 = 0; var38 < var7; ++var38) { - if (var33 == var8[var38] && var34 == var10[var38] && var32 == var9[var38]) { - int var39 = -1; - var36 = var23[var38]; - var37 = var23[var38 + 1]; - - for (int var40 = var36; var40 < var37; ++var40) { - var39 = var24[var40] - 1; - if (var39 == -1 || var14[var39] != 0) { - break; - } - } - - if (var39 != -1) { - if (var15 == null) { - this.aClass18_3843 = new Class18(); - var15 = this.aClass18_3843.aShortArray417 = Unsorted.method65(this.aShortArray3810); - var16 = this.aClass18_3843.aShortArray419 = Unsorted.method65(this.aShortArray3826); - var17 = this.aClass18_3843.aShortArray418 = Unsorted.method65(this.aShortArray3837); - var18 = this.aClass18_3843.aShortArray416 = Unsorted.method65(this.aShortArray3841); - } - - if (var19 == null) { - Class18 var47 = var6.aClass18_3843 = new Class18(); - var19 = var47.aShortArray417 = Unsorted.method65(var11); - var20 = var47.aShortArray419 = Unsorted.method65(var12); - var21 = var47.aShortArray418 = Unsorted.method65(var13); - var22 = var47.aShortArray416 = Unsorted.method65(var14); - } - - short var46 = this.aShortArray3810[var35]; - short var41 = this.aShortArray3826[var35]; - short var42 = this.aShortArray3837[var35]; - short var43 = this.aShortArray3841[var35]; - var36 = var23[var38]; - var37 = var23[var38 + 1]; - - int var44; - int var45; - for (var44 = var36; var44 < var37; ++var44) { - var45 = var24[var44] - 1; - if (var45 == -1) { - break; - } - - if (Objects.requireNonNull(var22)[var45] != 0) { - Objects.requireNonNull(var19)[var45] += var46; - Objects.requireNonNull(var20)[var45] += var41; - Objects.requireNonNull(var21)[var45] += var42; - var22[var45] += var43; - } - } - - var46 = var11[var39]; - var41 = var12[var39]; - var42 = var13[var39]; - var43 = var14[var39]; - var36 = this.anIntArray3838[var31]; - var37 = this.anIntArray3838[var31 + 1]; - - for (var44 = var36; var44 < var37; ++var44) { - var45 = this.aShortArray3828[var44] - 1; - if (var45 == -1) { - break; - } - - if (Objects.requireNonNull(var18)[var45] != 0) { - Objects.requireNonNull(var15)[var45] += var46; - Objects.requireNonNull(var16)[var45] += var41; - Objects.requireNonNull(var17)[var45] += var42; - var18[var45] += var43; - } - } - } - } - } - } - } - } - } - } - - } - } - - final void method1893(int var2, int var3, int var4, int var5, int var6, int var7) { - if (this.anInt3833 != 0) { - GL var10 = HDToolKit.gl; - var10.glPushMatrix(); - if (var4 != 0) { - var10.glRotatef((float) var4 * 0.17578125F, 1.0F, 0.0F, 0.0F); - } - - var10.glTranslatef((float) var5, (float) var6, (float) var7); - if (var2 != 0) { - var10.glRotatef((float) var2 * 0.17578125F, 0.0F, 1.0F, 0.0F); - } - - if (var3 != 0) { - var10.glRotatef((float) (-var3) * 0.17578125F, 0.0F, 0.0F, 1.0F); - } - - this.method1930(); - var10.glPopMatrix(); - } - } - - private static int method1905(int var0, short var1, int var2, byte var3) { - int var4 = Class51.anIntArray834[Class140_Sub1_Sub2.method1940(var0, var2)]; - if (var1 != -1) { - int var5 = Class51.anInterface2_838.method19(93, var1 & 65535); - int var6; - int var8; - if (var5 != 0) { - if (var2 < 0) { - var6 = 0; - } else if (var2 > 127) { - var6 = 16777215; - } else { - var6 = 131586 * var2; - } - - if (var5 == 256) { - var4 = var6; - } else { - var8 = 256 - var5; - var4 = ((var6 & 16711935) * var5 + (var4 & 16711935) * var8 & -16711936) + ((var6 & 65280) * var5 + (var4 & 65280) * var8 & 16711680) >> 8; - } - } - - var6 = Class51.anInterface2_838.method10(90, var1 & 65535); - if (var6 != 0) { - var6 += 256; - int var7 = ((var4 & 16711680) >> 16) * var6; - if (var7 > 65535) { - var7 = 65535; - } - - var8 = ((var4 & 65280) >> 8) * var6; - if (var8 > 65535) { - var8 = 65535; - } - - int var9 = (var4 & 0xFF) * var6; - if (var9 > 65535) { - var9 = 65535; - } - - var4 = (var7 << 8 & 16711680) + (var8 & 65280) + (var9 >> 8); - } - } - - return (var4 << 8) + (255 - (var3 & 0xFF)); - } - - private static float[] method1906(int var0, int var1, int var2, int var3, float var4, float var5, float var6) { - float[] var7 = new float[9]; - float[] var8 = new float[9]; - float var9 = (float) Math.cos((float) var3 * 0.024543693F); - float var10 = (float) Math.sin((float) var3 * 0.024543693F); - float var11; - var7[0] = var9; - var7[1] = 0.0F; - var7[2] = var10; - var7[3] = 0.0F; - var7[4] = 1.0F; - var7[5] = 0.0F; - var7[6] = -var10; - var7[7] = 0.0F; - var7[8] = var9; - float[] var12 = new float[9]; - float var13 = 1.0F; - float var14 = 0.0F; - var9 = (float) var1 / 32767.0F; - var10 = -((float) Math.sqrt(1.0F - var9 * var9)); - var11 = 1.0F - var9; - float var15 = (float) Math.sqrt(var0 * var0 + var2 * var2); - if (var15 == 0.0F && var9 == 0.0F) { - var8 = var7; - } else { - if (var15 != 0.0F) { - var13 = (float) (-var2) / var15; - var14 = (float) var0 / var15; - } - - var12[0] = var9 + var13 * var13 * var11; - var12[1] = var14 * var10; - var12[2] = var14 * var13 * var11; - var12[3] = -var14 * var10; - var12[4] = var9; - var12[5] = var13 * var10; - var12[6] = var13 * var14 * var11; - var12[7] = -var13 * var10; - var12[8] = var9 + var14 * var14 * var11; - var8[0] = var7[0] * var12[0] + var7[1] * var12[3] + var7[2] * var12[6]; - var8[1] = var7[0] * var12[1] + var7[1] * var12[4] + var7[2] * var12[7]; - var8[2] = var7[0] * var12[2] + var7[1] * var12[5] + var7[2] * var12[8]; - var8[3] = var7[3] * var12[0] + var7[4] * var12[3] + var7[5] * var12[6]; - var8[4] = var7[3] * var12[1] + var7[4] * var12[4] + var7[5] * var12[7]; - var8[5] = var7[3] * var12[2] + var7[4] * var12[5] + var7[5] * var12[8]; - var8[6] = var7[6] * var12[0] + var7[7] * var12[3] + var7[8] * var12[6]; - var8[7] = var7[6] * var12[1] + var7[7] * var12[4] + var7[8] * var12[7]; - var8[8] = var7[6] * var12[2] + var7[7] * var12[5] + var7[8] * var12[8]; - } - - var8[0] *= var4; - var8[1] *= var4; - var8[2] *= var4; - var8[3] *= var5; - var8[4] *= var5; - var8[5] *= var5; - var8[6] *= var6; - var8[7] *= var6; - var8[8] *= var6; - return var8; - } - - private short method1907(int var2, long var3, int var5, int var6, int var7, int var8, float var9, float var10) { - int var11 = this.anIntArray3838[var2]; - int var12 = this.anIntArray3838[var2 + 1]; - int var13 = 0; - - for (int var14 = var11; var14 < var12; ++var14) { - short var15 = this.aShortArray3828[var14]; - if (var15 == 0) { - var13 = var14; - break; - } - - if (aLongArray3821[var14] == var3) { - return (short) (var15 - 1); - } - } - - this.aShortArray3828[var13] = (short) (this.anInt3833 + 1); - aLongArray3821[var13] = var3; - this.aShortArray3810[this.anInt3833] = (short) var5; - this.aShortArray3826[this.anInt3833] = (short) var6; - this.aShortArray3837[this.anInt3833] = (short) var7; - this.aShortArray3841[this.anInt3833] = (short) var8; - this.aFloatArray3824[this.anInt3833] = var9; - this.aFloatArray3847[this.anInt3833] = var10; - return (short) (this.anInt3833++); - } - - final void method1876(int var1) { - int var2 = Class51.anIntArray840[var1]; - int var3 = Class51.anIntArray851[var1]; - - for (int var4 = 0; var4 < this.anInt3823; ++var4) { - int var5 = this.anIntArray3848[var4] * var2 + this.anIntArray3822[var4] * var3 >> 16; - this.anIntArray3848[var4] = this.anIntArray3848[var4] * var3 - this.anIntArray3822[var4] * var2 >> 16; - this.anIntArray3822[var4] = var5; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - final void method1899(int var1, int[] var2, int var3, int var4, int var5, boolean var6, int var7, int[] var8) { - int var9 = var2.length; - int var10; - int var11; - int var14; - int var15; - int var47; - if (var1 == 0) { - var3 <<= 4; - var4 <<= 4; - var5 <<= 4; - var10 = 0; - anInt3859 = 0; - anInt3865 = 0; - anInt3862 = 0; - - for (var11 = 0; var11 < var9; ++var11) { - var47 = var2[var11]; - if (var47 < this.anIntArrayArray3825.length) { - int[] var48 = this.anIntArrayArray3825[var47]; - - for (var14 = 0; var14 < var48.length; ++var14) { - var15 = var48[var14]; - if (this.aShortArray3832 == null || (var7 & this.aShortArray3832[var15]) != 0) { - anInt3859 += this.anIntArray3822[var15]; - anInt3865 += this.anIntArray3845[var15]; - anInt3862 += this.anIntArray3848[var15]; - ++var10; - } - } - } - } - - if (var10 > 0) { - anInt3859 = anInt3859 / var10 + var3; - anInt3865 = anInt3865 / var10 + var4; - anInt3862 = anInt3862 / var10 + var5; - aBoolean3868 = true; - } else { - anInt3859 = var3; - anInt3865 = var4; - anInt3862 = var5; - } - - } else { - int[] var12; - int var13; - if (var1 == 1) { - if (var8 != null) { - var10 = var8[0] * var3 + var8[1] * var4 + var8[2] * var5 + 16384 >> 15; - var11 = var8[3] * var3 + var8[4] * var4 + var8[5] * var5 + 16384 >> 15; - var47 = var8[6] * var3 + var8[7] * var4 + var8[8] * var5 + 16384 >> 15; - var3 = var10; - var4 = var11; - var5 = var47; - } - - var3 <<= 4; - var4 <<= 4; - var5 <<= 4; - - for (var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if (var11 < this.anIntArrayArray3825.length) { - var12 = this.anIntArrayArray3825[var11]; - - for (var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if (this.aShortArray3832 == null || (var7 & this.aShortArray3832[var14]) != 0) { - this.anIntArray3822[var14] += var3; - this.anIntArray3845[var14] += var4; - this.anIntArray3848[var14] += var5; - } - } - } - } - - } else { - int var17; - int var16; - int var19; - int var18; - int var21; - int var20; - int var23; - int var22; - int var24; - int var27; - int var26; - int var28; - int var30; - int var34; - int[] var35; - int var32; - int var33; - int var38; - int var39; - int var36; - int var37; - int var40; - if (var1 == 2) { - if (var8 == null) { - for (var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if (var11 < this.anIntArrayArray3825.length) { - var12 = this.anIntArrayArray3825[var11]; - - for (var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if (this.aShortArray3832 == null || (var7 & this.aShortArray3832[var14]) != 0) { - this.anIntArray3822[var14] -= anInt3859; - this.anIntArray3845[var14] -= anInt3865; - this.anIntArray3848[var14] -= anInt3862; - if (var5 != 0) { - var15 = Class51.anIntArray840[var5]; - var16 = Class51.anIntArray851[var5]; - var17 = this.anIntArray3845[var14] * var15 + this.anIntArray3822[var14] * var16 + 32767 >> 16; - this.anIntArray3845[var14] = this.anIntArray3845[var14] * var16 - this.anIntArray3822[var14] * var15 + 32767 >> 16; - this.anIntArray3822[var14] = var17; - } - - if (var3 != 0) { - var15 = Class51.anIntArray840[var3]; - var16 = Class51.anIntArray851[var3]; - var17 = this.anIntArray3845[var14] * var16 - this.anIntArray3848[var14] * var15 + 32767 >> 16; - this.anIntArray3848[var14] = this.anIntArray3845[var14] * var15 + this.anIntArray3848[var14] * var16 + 32767 >> 16; - this.anIntArray3845[var14] = var17; - } - - if (var4 != 0) { - var15 = Class51.anIntArray840[var4]; - var16 = Class51.anIntArray851[var4]; - var17 = this.anIntArray3848[var14] * var15 + this.anIntArray3822[var14] * var16 + 32767 >> 16; - this.anIntArray3848[var14] = this.anIntArray3848[var14] * var16 - this.anIntArray3822[var14] * var15 + 32767 >> 16; - this.anIntArray3822[var14] = var17; - } - - this.anIntArray3822[var14] += anInt3859; - this.anIntArray3845[var14] += anInt3865; - this.anIntArray3848[var14] += anInt3862; - } - } - } - } - - if (var6 && this.aShortArray3810 != null) { - for (var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if (var11 < this.anIntArrayArray3825.length) { - var12 = this.anIntArrayArray3825[var11]; - - for (var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if (this.aShortArray3832 == null || (var7 & this.aShortArray3832[var14]) != 0) { - var15 = this.anIntArray3838[var14]; - var16 = this.anIntArray3838[var14 + 1]; - - for (var17 = var15; var17 < var16; ++var17) { - var18 = this.aShortArray3828[var17] - 1; - if (var18 == -1) { - break; - } - - if (var5 != 0) { - var19 = Class51.anIntArray840[var5]; - var20 = Class51.anIntArray851[var5]; - var21 = this.aShortArray3826[var18] * var19 + this.aShortArray3810[var18] * var20 + 32767 >> 16; - this.aShortArray3826[var18] = (short) (this.aShortArray3826[var18] * var20 - this.aShortArray3810[var18] * var19 + 32767 >> 16); - this.aShortArray3810[var18] = (short) var21; - } - - if (var3 != 0) { - var19 = Class51.anIntArray840[var3]; - var20 = Class51.anIntArray851[var3]; - var21 = this.aShortArray3826[var18] * var20 - this.aShortArray3837[var18] * var19 + 32767 >> 16; - this.aShortArray3837[var18] = (short) (this.aShortArray3826[var18] * var19 + this.aShortArray3837[var18] * var20 + 32767 >> 16); - this.aShortArray3826[var18] = (short) var21; - } - - if (var4 != 0) { - var19 = Class51.anIntArray840[var4]; - var20 = Class51.anIntArray851[var4]; - var21 = this.aShortArray3837[var18] * var19 + this.aShortArray3810[var18] * var20 + 32767 >> 16; - this.aShortArray3837[var18] = (short) (this.aShortArray3837[var18] * var20 - this.aShortArray3810[var18] * var19 + 32767 >> 16); - this.aShortArray3810[var18] = (short) var21; - } - } - } - } - } - } - - if (this.aClass121_3815 != null) { - this.aClass121_3815.aBoolean1640 = false; - } - } - } else { - var10 = var8[9] << 4; - var11 = var8[10] << 4; - var47 = var8[11] << 4; - var13 = var8[12] << 4; - var14 = var8[13] << 4; - var15 = var8[14] << 4; - if (aBoolean3868) { - var16 = var8[0] * anInt3859 + var8[3] * anInt3865 + var8[6] * anInt3862 + 16384 >> 15; - var17 = var8[1] * anInt3859 + var8[4] * anInt3865 + var8[7] * anInt3862 + 16384 >> 15; - var18 = var8[2] * anInt3859 + var8[5] * anInt3865 + var8[8] * anInt3862 + 16384 >> 15; - var16 += var13; - var17 += var14; - var18 += var15; - anInt3859 = var16; - anInt3865 = var17; - anInt3862 = var18; - aBoolean3868 = false; - } - - int[] var49 = new int[9]; - var17 = Class51.anIntArray851[var3] >> 1; - var18 = Class51.anIntArray840[var3] >> 1; - var19 = Class51.anIntArray851[var4] >> 1; - var20 = Class51.anIntArray840[var4] >> 1; - var21 = Class51.anIntArray851[var5] >> 1; - var22 = Class51.anIntArray840[var5] >> 1; - var23 = var18 * var21 + 16384 >> 15; - var24 = var18 * var22 + 16384 >> 15; - var49[0] = var19 * var21 + var20 * var24 + 16384 >> 15; - var49[1] = -var19 * var22 + var20 * var23 + 16384 >> 15; - var49[2] = var20 * var17 + 16384 >> 15; - var49[3] = var17 * var22 + 16384 >> 15; - var49[4] = var17 * var21 + 16384 >> 15; - var49[5] = -var18; - var49[6] = -var20 * var21 + var19 * var24 + 16384 >> 15; - var49[7] = var20 * var22 + var19 * var23 + 16384 >> 15; - var49[8] = var19 * var17 + 16384 >> 15; - int var50 = var49[0] * -anInt3859 + var49[1] * -anInt3865 + var49[2] * -anInt3862 + 16384 >> 15; - var26 = var49[3] * -anInt3859 + var49[4] * -anInt3865 + var49[5] * -anInt3862 + 16384 >> 15; - var27 = var49[6] * -anInt3859 + var49[7] * -anInt3865 + var49[8] * -anInt3862 + 16384 >> 15; - var28 = var50 + anInt3859; - int var51 = var26 + anInt3865; - var30 = var27 + anInt3862; - int[] var52 = new int[9]; - - for (var32 = 0; var32 < 3; ++var32) { - for (var33 = 0; var33 < 3; ++var33) { - var34 = 0; - - for (int var53 = 0; var53 < 3; ++var53) { - var34 += var49[var32 * 3 + var53] * var8[var33 * 3 + var53]; - } - - var52[var32 * 3 + var33] = var34 + 16384 >> 15; - } - } - - var32 = var49[0] * var13 + var49[1] * var14 + var49[2] * var15 + 16384 >> 15; - var33 = var49[3] * var13 + var49[4] * var14 + var49[5] * var15 + 16384 >> 15; - var34 = var49[6] * var13 + var49[7] * var14 + var49[8] * var15 + 16384 >> 15; - var32 += var28; - var33 += var51; - var34 += var30; - var35 = new int[9]; - - for (var36 = 0; var36 < 3; ++var36) { - for (var37 = 0; var37 < 3; ++var37) { - var38 = 0; - - for (var39 = 0; var39 < 3; ++var39) { - var38 += var8[var36 * 3 + var39] * var52[var37 + var39 * 3]; - } - - var35[var36 * 3 + var37] = var38 + 16384 >> 15; - } - } - - var36 = var8[0] * var32 + var8[1] * var33 + var8[2] * var34 + 16384 >> 15; - var37 = var8[3] * var32 + var8[4] * var33 + var8[5] * var34 + 16384 >> 15; - var38 = var8[6] * var32 + var8[7] * var33 + var8[8] * var34 + 16384 >> 15; - var36 += var10; - var37 += var11; - var38 += var47; - - for (var39 = 0; var39 < var9; ++var39) { - var40 = var2[var39]; - if (var40 < this.anIntArrayArray3825.length) { - int[] var41 = this.anIntArrayArray3825[var40]; - - for (int var42 = 0; var42 < var41.length; ++var42) { - int var43 = var41[var42]; - if (this.aShortArray3832 == null || (var7 & this.aShortArray3832[var43]) != 0) { - int var44 = var35[0] * this.anIntArray3822[var43] + var35[1] * this.anIntArray3845[var43] + var35[2] * this.anIntArray3848[var43] + 16384 >> 15; - int var45 = var35[3] * this.anIntArray3822[var43] + var35[4] * this.anIntArray3845[var43] + var35[5] * this.anIntArray3848[var43] + 16384 >> 15; - int var46 = var35[6] * this.anIntArray3822[var43] + var35[7] * this.anIntArray3845[var43] + var35[8] * this.anIntArray3848[var43] + 16384 >> 15; - var44 += var36; - var45 += var37; - var46 += var38; - this.anIntArray3822[var43] = var44; - this.anIntArray3845[var43] = var45; - this.anIntArray3848[var43] = var46; - } - } - } - } - } - - } else if (var1 == 3) { - if (var8 == null) { - for (var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if (var11 < this.anIntArrayArray3825.length) { - var12 = this.anIntArrayArray3825[var11]; - - for (var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if (this.aShortArray3832 == null || (var7 & this.aShortArray3832[var14]) != 0) { - this.anIntArray3822[var14] -= anInt3859; - this.anIntArray3845[var14] -= anInt3865; - this.anIntArray3848[var14] -= anInt3862; - this.anIntArray3822[var14] = this.anIntArray3822[var14] * var3 >> 7; - this.anIntArray3845[var14] = this.anIntArray3845[var14] * var4 >> 7; - this.anIntArray3848[var14] = this.anIntArray3848[var14] * var5 >> 7; - this.anIntArray3822[var14] += anInt3859; - this.anIntArray3845[var14] += anInt3865; - this.anIntArray3848[var14] += anInt3862; - } - } - } - } - } else { - var10 = var8[9] << 4; - var11 = var8[10] << 4; - var47 = var8[11] << 4; - var13 = var8[12] << 4; - var14 = var8[13] << 4; - var15 = var8[14] << 4; - if (aBoolean3868) { - var16 = var8[0] * anInt3859 + var8[3] * anInt3865 + var8[6] * anInt3862 + 16384 >> 15; - var17 = var8[1] * anInt3859 + var8[4] * anInt3865 + var8[7] * anInt3862 + 16384 >> 15; - var18 = var8[2] * anInt3859 + var8[5] * anInt3865 + var8[8] * anInt3862 + 16384 >> 15; - var16 += var13; - var17 += var14; - var18 += var15; - anInt3859 = var16; - anInt3865 = var17; - anInt3862 = var18; - aBoolean3868 = false; - } - - var16 = var3 << 15 >> 7; - var17 = var4 << 15 >> 7; - var18 = var5 << 15 >> 7; - var19 = var16 * -anInt3859 + 16384 >> 15; - var20 = var17 * -anInt3865 + 16384 >> 15; - var21 = var18 * -anInt3862 + 16384 >> 15; - var22 = var19 + anInt3859; - var23 = var20 + anInt3865; - var24 = var21 + anInt3862; - int[] var25 = new int[]{var16 * var8[0] + 16384 >> 15, var16 * var8[3] + 16384 >> 15, var16 * var8[6] + 16384 >> 15, var17 * var8[1] + 16384 >> 15, var17 * var8[4] + 16384 >> 15, var17 * var8[7] + 16384 >> 15, var18 * var8[2] + 16384 >> 15, var18 * var8[5] + 16384 >> 15, var18 * var8[8] + 16384 >> 15}; - var26 = var16 * var13 + 16384 >> 15; - var27 = var17 * var14 + 16384 >> 15; - var28 = var18 * var15 + 16384 >> 15; - var26 += var22; - var27 += var23; - var28 += var24; - int[] var29 = new int[9]; - - int var31; - for (var30 = 0; var30 < 3; ++var30) { - for (var31 = 0; var31 < 3; ++var31) { - var32 = 0; - - for (var33 = 0; var33 < 3; ++var33) { - var32 += var8[var30 * 3 + var33] * var25[var31 + var33 * 3]; - } - - var29[var30 * 3 + var31] = var32 + 16384 >> 15; - } - } - - var30 = var8[0] * var26 + var8[1] * var27 + var8[2] * var28 + 16384 >> 15; - var31 = var8[3] * var26 + var8[4] * var27 + var8[5] * var28 + 16384 >> 15; - var32 = var8[6] * var26 + var8[7] * var27 + var8[8] * var28 + 16384 >> 15; - var30 += var10; - var31 += var11; - var32 += var47; - - for (var33 = 0; var33 < var9; ++var33) { - var34 = var2[var33]; - if (var34 < this.anIntArrayArray3825.length) { - var35 = this.anIntArrayArray3825[var34]; - - for (var36 = 0; var36 < var35.length; ++var36) { - var37 = var35[var36]; - if (this.aShortArray3832 == null || (var7 & this.aShortArray3832[var37]) != 0) { - var38 = var29[0] * this.anIntArray3822[var37] + var29[1] * this.anIntArray3845[var37] + var29[2] * this.anIntArray3848[var37] + 16384 >> 15; - var39 = var29[3] * this.anIntArray3822[var37] + var29[4] * this.anIntArray3845[var37] + var29[5] * this.anIntArray3848[var37] + 16384 >> 15; - var40 = var29[6] * this.anIntArray3822[var37] + var29[7] * this.anIntArray3845[var37] + var29[8] * this.anIntArray3848[var37] + 16384 >> 15; - var38 += var30; - var39 += var31; - var40 += var32; - this.anIntArray3822[var37] = var38; - this.anIntArray3845[var37] = var39; - this.anIntArray3848[var37] = var40; - } - } - } - } - } - - } else if (var1 == 5) { - if (this.anIntArrayArray3829 != null && this.aByteArray3816 != null) { - for (var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if (var11 < this.anIntArrayArray3829.length) { - var12 = this.anIntArrayArray3829[var11]; - - for (var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if (this.aShortArray3813 == null || (var7 & this.aShortArray3813[var14]) != 0) { - var15 = (this.aByteArray3816[var14] & 0xFF) + var3 * 8; - if (var15 < 0) { - var15 = 0; - } else if (var15 > 255) { - var15 = 255; - } - - this.aByteArray3816[var14] = (byte) var15; - } - } - - if (var12.length > 0) { - this.aClass121_3814.aBoolean1640 = false; - } - } - } - } - - } else if (var1 == 7) { - if (this.anIntArrayArray3829 != null) { - for (var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if (var11 < this.anIntArrayArray3829.length) { - var12 = this.anIntArrayArray3829[var11]; - - for (var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if (this.aShortArray3813 == null || (var7 & this.aShortArray3813[var14]) != 0) { - var15 = this.aShortArray3808[var14] & 65535; - var16 = var15 >> 10 & 63; - var17 = var15 >> 7 & 7; - var18 = var15 & 127; - var16 = var16 + var3 & 63; - var17 += var4 / 4; - if (var17 < 0) { - var17 = 0; - } else if (var17 > 7) { - var17 = 7; - } - - var18 += var5; - if (var18 < 0) { - var18 = 0; - } else if (var18 > 127) { - var18 = 127; - } - - this.aShortArray3808[var14] = (short) (var16 << 10 | var17 << 7 | var18); - } - } - - if (var12.length > 0) { - this.aClass121_3814.aBoolean1640 = false; - } - } - } - } - - } - } - } - } - - final int method1871() { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - return this.aClass6_3835.aShort91; - } - - final void method1908() { - int[] var1; - int var2; - int var3; - int var4; - if (this.anIntArray3844 != null) { - var1 = new int[256]; - var2 = 0; - - for (var3 = 0; var3 < this.anInt3823; ++var3) { - var4 = this.anIntArray3844[var3] & 0xFF; - ++var1[var4]; - if (var4 > var2) { - var2 = var4; - } - } - - this.anIntArrayArray3825 = new int[var2 + 1][]; - - for (var3 = 0; var3 <= var2; ++var3) { - this.anIntArrayArray3825[var3] = new int[var1[var3]]; - var1[var3] = 0; - } - - for (var3 = 0; var3 < this.anInt3823; this.anIntArrayArray3825[var4][var1[var4]++] = var3++) { - var4 = this.anIntArray3844[var3] & 0xFF; - } - - this.anIntArray3844 = null; - } - - if (this.aByteArray3820 != null) { - var1 = new int[256]; - var2 = 0; - - for (var3 = 0; var3 < this.anInt3852; ++var3) { - var4 = this.aByteArray3820[var3] & 0xFF; - ++var1[var4]; - if (var4 > var2) { - var2 = var4; - } - } - - this.anIntArrayArray3829 = new int[var2 + 1][]; - - for (var3 = 0; var3 <= var2; ++var3) { - this.anIntArrayArray3829[var3] = new int[var1[var3]]; - var1[var3] = 0; - } - - for (var3 = 0; var3 < this.anInt3852; this.anIntArrayArray3829[var4][var1[var4]++] = var3++) { - var4 = this.aByteArray3820[var3] & 0xFF; - } - - this.aByteArray3820 = null; - } - - } - - final void method1909(int var1) { - this.aShort3819 = (short) var1; - if (this.aClass121_3815 != null) { - this.aClass121_3815.aBoolean1640 = false; - } - - } - - private static void method1910(int var0, int var1, int var2, int var3, int var4, int var5, float[] var6, int var7, float var8) { - var0 -= var3; - var1 -= var4; - var2 -= var5; - float var9 = (float) var0 * var6[0] + (float) var1 * var6[1] + (float) var2 * var6[2]; - float var10 = (float) var0 * var6[3] + (float) var1 * var6[4] + (float) var2 * var6[5]; - float var11 = (float) var0 * var6[6] + (float) var1 * var6[7] + (float) var2 * var6[8]; - float var12 = (float) Math.sqrt(var9 * var9 + var10 * var10 + var11 * var11); - float var13 = (float) Math.atan2(var9, var11) / 6.2831855F + 0.5F; - float var14 = (float) Math.asin(var10 / var12) / 3.1415927F + 0.5F + var8; - float var15; - if (var7 == 1) { - var15 = var13; - var13 = -var14; - var14 = var15; - } else if (var7 == 2) { - var13 = -var13; - var14 = -var14; - } else if (var7 == 3) { - var15 = var13; - var13 = var14; - var14 = -var15; - } - - aFloat3867 = var13; - aFloat3860 = var14; - } - - final void method1911() { - if (this.aShortArray3810 == null) { - this.method1874(); - } else { - int var1; - for (var1 = 0; var1 < this.anInt3823; ++var1) { - this.anIntArray3822[var1] = -this.anIntArray3822[var1]; - this.anIntArray3848[var1] = -this.anIntArray3848[var1]; - } - - for (var1 = 0; var1 < this.anInt3833; ++var1) { - this.aShortArray3810[var1] = (short) (-this.aShortArray3810[var1]); - this.aShortArray3837[var1] = (short) (-this.aShortArray3837[var1]); - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - if (this.aClass121_3815 != null) { - this.aClass121_3815.aBoolean1640 = false; - } - - } - } - - final void resize(int var1, int var2, int var3) { - for (int var4 = 0; var4 < this.anInt3823; ++var4) { - this.anIntArray3822[var4] = this.anIntArray3822[var4] * var1 >> 7; - this.anIntArray3845[var4] = this.anIntArray3845[var4] * var2 >> 7; - this.anIntArray3848[var4] = this.anIntArray3848[var4] * var3 >> 7; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - final Model method1894(boolean var1, boolean var2, boolean var3) { - return this.method1923(var1, var2, var3, aClass140_Sub1_Sub1_3850, aClass140_Sub1_Sub1_3842); - } - - final void method1874() { - for (int var1 = 0; var1 < this.anInt3823; ++var1) { - this.anIntArray3822[var1] = -this.anIntArray3822[var1]; - this.anIntArray3848[var1] = -this.anIntArray3848[var1]; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - private void method1912() { - if (aClass3_Sub30_3818.buffer.length < this.anInt3833 * 12) { - aClass3_Sub30_3818 = new DataBuffer((this.anInt3833 + 100) * 12); - } else { - aClass3_Sub30_3818.index = 0; - } - - int var1; - if (HDToolKit.aBoolean1790) { - for (var1 = 0; var1 < this.anInt3852; ++var1) { - aClass3_Sub30_3818.writeInt(this.aShortArray3811[var1]); - aClass3_Sub30_3818.writeInt(this.aShortArray3830[var1]); - aClass3_Sub30_3818.writeInt(this.aShortArray3831[var1]); - } - } else { - for (var1 = 0; var1 < this.anInt3852; ++var1) { - aClass3_Sub30_3818.writeIntLE(this.aShortArray3811[var1]); - aClass3_Sub30_3818.writeIntLE(this.aShortArray3830[var1]); - aClass3_Sub30_3818.writeIntLE(this.aShortArray3831[var1]); - } - } - - if (HDToolKit.supportVertexBufferObject) { - Class156 var3 = new Class156(); - ByteBuffer var2 = ByteBuffer.wrap(aClass3_Sub30_3818.buffer, 0, aClass3_Sub30_3818.index); - var3.method2172(var2); - this.aClass121_3846.aBoolean1640 = true; - this.aClass121_3846.aByteBuffer1644 = null; - this.aClass121_3846.aClass156_1643 = var3; - } else { - ByteBuffer var4 = ByteBuffer.allocateDirect(aClass3_Sub30_3818.index); - var4.put(aClass3_Sub30_3818.buffer, 0, aClass3_Sub30_3818.index); - var4.flip(); - this.aClass121_3846.aBoolean1640 = true; - this.aClass121_3846.aByteBuffer1644 = var4; - this.aClass121_3846.aClass156_1643 = null; - } - - } - - private static void method1913(int var0, int var1, int var2, int var3, int var4, int var5, int var6, float[] var7, int var8, float var9, float var10, float var11) { - var0 -= var3; - var1 -= var4; - var2 -= var5; - float var12 = (float) var0 * var7[0] + (float) var1 * var7[1] + (float) var2 * var7[2]; - float var13 = (float) var0 * var7[3] + (float) var1 * var7[4] + (float) var2 * var7[5]; - float var14 = (float) var0 * var7[6] + (float) var1 * var7[7] + (float) var2 * var7[8]; - float var15; - float var16; - if (var6 == 0) { - var15 = var12 + var9 + 0.5F; - var16 = -var14 + var11 + 0.5F; - } else if (var6 == 1) { - var15 = var12 + var9 + 0.5F; - var16 = var14 + var11 + 0.5F; - } else if (var6 == 2) { - var15 = -var12 + var9 + 0.5F; - var16 = -var13 + var10 + 0.5F; - } else if (var6 == 3) { - var15 = var12 + var9 + 0.5F; - var16 = -var13 + var10 + 0.5F; - } else if (var6 == 4) { - var15 = var14 + var11 + 0.5F; - var16 = -var13 + var10 + 0.5F; - } else { - var15 = -var14 + var11 + 0.5F; - var16 = -var13 + var10 + 0.5F; - } - - float var17; - if (var8 == 1) { - var17 = var15; - var15 = -var16; - var16 = var17; - } else if (var8 == 2) { - var15 = -var15; - var16 = -var16; - } else if (var8 == 3) { - var17 = var15; - var15 = var16; - var16 = -var17; - } - - aFloat3864 = var15; - aFloat3858 = var16; - } - - final void method1914(int var1) { - this.aShort3849 = (short) var1; - this.aClass121_3814.aBoolean1640 = false; - } - - final boolean method1873() { - if (this.anIntArrayArray3825 == null) { - return false; - } else { - for (int var1 = 0; var1 < this.anInt3823; ++var1) { - this.anIntArray3822[var1] <<= 4; - this.anIntArray3845[var1] <<= 4; - this.anIntArray3848[var1] <<= 4; - } - - anInt3859 = 0; - anInt3865 = 0; - anInt3862 = 0; - return true; - } - } - - final void method1916(short var1, short var2) { - int var3; - for (var3 = 0; var3 < this.anInt3852; ++var3) { - if (this.aShortArray3827[var3] == var1) { - this.aShortArray3827[var3] = var2; - } - } - - var3 = 0; - int var4 = 0; - if (var1 != -1) { - var3 = Class51.anInterface2_838.method19(-125, var1 & 65535); - var4 = Class51.anInterface2_838.method10(-98, var1 & 65535); - } - - int var5 = 0; - int var6 = 0; - if (var2 != -1) { - var5 = Class51.anInterface2_838.method19(57, var2 & 65535); - var6 = Class51.anInterface2_838.method10(-114, var2 & 65535); - } - - if (var3 != var5 || var4 != var6) { - this.aClass121_3814.aBoolean1640 = false; - } - - } - - private void method1917() { - int var1 = 32767; - int var2 = 32767; - int var3 = 32767; - int var4 = -32768; - int var5 = -32768; - int var6 = -32768; - int var7 = 0; - int var8 = 0; - - for (int var9 = 0; var9 < this.anInt3823; ++var9) { - int var10 = this.anIntArray3822[var9]; - int var11 = this.anIntArray3845[var9]; - int var12 = this.anIntArray3848[var9]; - if (var10 < var1) { - var1 = var10; - } - - if (var10 > var4) { - var4 = var10; - } - - if (var11 < var2) { - var2 = var11; - } - - if (var11 > var5) { - var5 = var11; - } - - if (var12 < var3) { - var3 = var12; - } - - if (var12 > var6) { - var6 = var12; - } - - int var13 = var10 * var10 + var12 * var12; - if (var13 > var7) { - var7 = var13; - } - - var13 = var10 * var10 + var12 * var12 + var11 * var11; - if (var13 > var8) { - var8 = var13; - } - } - - this.aClass6_3835.aShort95 = (short) var1; - this.aClass6_3835.aShort94 = (short) var4; - this.aClass6_3835.aShort91 = (short) var2; - this.aClass6_3835.aShort92 = (short) var5; - this.aClass6_3835.aShort97 = (short) var3; - this.aClass6_3835.aShort96 = (short) var6; - this.aClass6_3835.aShort93 = (short) ((int) (Math.sqrt(var7) + 0.99D)); - Math.sqrt(var8); - this.aClass6_3835.aBoolean98 = true; - } - - final void method1918(short var1, short var2) { - for (int var3 = 0; var3 < this.anInt3852; ++var3) { - if (this.aShortArray3808[var3] == var1) { - this.aShortArray3808[var3] = var2; - } - } - - this.aClass121_3814.aBoolean1640 = false; - } - - final void method1919(int var1, int var2, Class140_Sub1_Sub1 var3, int[][] var4, int[][] var5, int var6, int var7, int var8) { - if (!var3.aClass6_3835.aBoolean98) { - var3.method1917(); - } - - int var9 = var6 + var3.aClass6_3835.aShort95; - int var10 = var6 + var3.aClass6_3835.aShort94; - int var11 = var8 + var3.aClass6_3835.aShort97; - int var12 = var8 + var3.aClass6_3835.aShort96; - if (var1 != 1 && var1 != 2 && var1 != 3 && var1 != 5 || var9 >= 0 && var10 + 128 >> 7 < var4.length && var11 >= 0 && var12 + 128 >> 7 < var4[0].length) { - if (var1 == 4 || var1 == 5) { - if (var5 == null) { - return; - } - - if (var9 < 0 || var10 + 128 >> 7 >= var5.length || var11 < 0 || var12 + 128 >> 7 >= var5[0].length) { - return; - } - } else { - var9 >>= 7; - var10 = var10 + 127 >> 7; - var11 >>= 7; - var12 = var12 + 127 >> 7; - if (var4[var9][var11] == var7 && var4[var10][var11] == var7 && var4[var9][var12] == var7 && var4[var10][var12] == var7) { - return; - } - } - - int var13; - int var14; - int var15; - int var17; - int var16; - int var19; - int var18; - int var21; - int var20; - int var22; - if (var1 == 1) { - for (var13 = 0; var13 < this.anInt3823; ++var13) { - var14 = this.anIntArray3822[var13] + var6; - var15 = this.anIntArray3848[var13] + var8; - var16 = var14 & 127; - var17 = var15 & 127; - var18 = var14 >> 7; - var19 = var15 >> 7; - var20 = var4[var18][var19] * (128 - var16) + var4[var18 + 1][var19] * var16 >> 7; - var21 = var4[var18][var19 + 1] * (128 - var16) + var4[var18 + 1][var19 + 1] * var16 >> 7; - var22 = var20 * (128 - var17) + var21 * var17 >> 7; - this.anIntArray3845[var13] = this.anIntArray3845[var13] + var22 - var7; - } - } else { - int var23; - int var24; - if (var1 == 2) { - short var26 = var3.aClass6_3835.aShort91; - - for (var14 = 0; var14 < this.anInt3823; ++var14) { - var15 = (this.anIntArray3845[var14] << 16) / var26; - if (var15 < var2) { - var16 = this.anIntArray3822[var14] + var6; - var17 = this.anIntArray3848[var14] + var8; - var18 = var16 & 127; - var19 = var17 & 127; - var20 = var16 >> 7; - var21 = var17 >> 7; - var22 = var4[var20][var21] * (128 - var18) + var4[var20 + 1][var21] * var18 >> 7; - var23 = var4[var20][var21 + 1] * (128 - var18) + var4[var20 + 1][var21 + 1] * var18 >> 7; - var24 = var22 * (128 - var19) + var23 * var19 >> 7; - this.anIntArray3845[var14] += (var24 - var7) * (var2 - var15) / var2; - } - } - } else if (var1 == 3) { - var13 = (var2 & 0xFF) * 4; - var14 = (var2 >> 8 & 0xFF) * 4; - this.method1895(var4, var6, var7, var8, var13, var14); - } else if (var1 == 4) { - var13 = var3.aClass6_3835.aShort92 - var3.aClass6_3835.aShort91; - - for (var14 = 0; var14 < this.anInt3823; ++var14) { - var15 = this.anIntArray3822[var14] + var6; - var16 = this.anIntArray3848[var14] + var8; - var17 = var15 & 127; - var18 = var16 & 127; - var19 = var15 >> 7; - var20 = var16 >> 7; - var21 = var5[var19][var20] * (128 - var17) + var5[var19 + 1][var20] * var17 >> 7; - var22 = var5[var19][var20 + 1] * (128 - var17) + var5[var19 + 1][var20 + 1] * var17 >> 7; - var23 = var21 * (128 - var18) + var22 * var18 >> 7; - this.anIntArray3845[var14] = this.anIntArray3845[var14] + (var23 - var7) + var13; - } - } else if (var1 == 5) { - var13 = var3.aClass6_3835.aShort92 - var3.aClass6_3835.aShort91; - - for (var14 = 0; var14 < this.anInt3823; ++var14) { - var15 = this.anIntArray3822[var14] + var6; - var16 = this.anIntArray3848[var14] + var8; - var17 = var15 & 127; - var18 = var16 & 127; - var19 = var15 >> 7; - var20 = var16 >> 7; - var21 = var4[var19][var20] * (128 - var17) + var4[var19 + 1][var20] * var17 >> 7; - var22 = var4[var19][var20 + 1] * (128 - var17) + var4[var19 + 1][var20 + 1] * var17 >> 7; - var23 = var21 * (128 - var18) + var22 * var18 >> 7; - var21 = var5[var19][var20] * (128 - var17) + var5[var19 + 1][var20] * var17 >> 7; - var22 = var5[var19][var20 + 1] * (128 - var17) + var5[var19 + 1][var20 + 1] * var17 >> 7; - var24 = var21 * (128 - var18) + var22 * var18 >> 7; - int var25 = var23 - var24; - this.anIntArray3845[var14] = ((this.anIntArray3845[var14] << 8) / var13 * var25 >> 8) - (var7 - var23); - } - } - } - - this.aClass121_3839.aBoolean1640 = false; - this.aClass6_3835.aBoolean98 = false; - } - } - - final void method1920(boolean var1, boolean var2, boolean var3, boolean var5, boolean var6, boolean var7) { - if (this.aByte3836 != 0) { - throw new IllegalArgumentException(); - } else if (this.anInt3833 != 0) { - if (var7) { - boolean var8 = !this.aClass121_3814.aBoolean1640 && (var2 || var3 && !Class106.aBoolean1441); - this.method1922(false, !this.aClass121_3839.aBoolean1640 && var1, var8, this.aClass121_3815 != null && !this.aClass121_3815.aBoolean1640 && var3, !this.aClass121_3817.aBoolean1640); - if (!this.aClass121_3846.aBoolean1640 && var5 && var2) { - this.method1912(); - } - } - - if (var1) { - if (this.aClass121_3839.aBoolean1640) { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - this.anIntArray3822 = null; - this.anIntArray3845 = null; - this.anIntArray3848 = null; - this.aShortArray3828 = null; - this.anIntArray3838 = null; - } else { - this.aByte3851 = (byte) (this.aByte3851 | 1); - } - } - - if (var2) { - if (this.aClass121_3814.aBoolean1640) { - this.aShortArray3808 = null; - this.aByteArray3816 = null; - } else { - this.aByte3851 = (byte) (this.aByte3851 | 2); - } - } - - if (var3 && Class106.aBoolean1441) { - if (this.aClass121_3815.aBoolean1640) { - this.aShortArray3810 = null; - this.aShortArray3826 = null; - this.aShortArray3837 = null; - this.aShortArray3841 = null; - } else { - this.aByte3851 = (byte) (this.aByte3851 | 4); - } - } - - if (this.aClass121_3817.aBoolean1640) { - this.aFloatArray3824 = null; - this.aFloatArray3847 = null; - } else { - this.aByte3851 = (byte) (this.aByte3851 | 8); - } - - if (var5 && var2) { - if (this.aClass121_3846.aBoolean1640 && this.aClass121_3814.aBoolean1640) { - this.aShortArray3811 = null; - this.aShortArray3830 = null; - this.aShortArray3831 = null; - } else { - this.aByte3851 = (byte) (this.aByte3851 | 16); - } - } - - if (var6) { - this.anIntArray3844 = null; - this.aByteArray3820 = null; - this.anIntArrayArray3825 = null; - this.anIntArrayArray3829 = null; - } - - } - } - - private static float[] method1921(float[] var0, int var1) { - float[] var2 = new float[var1]; - ArrayUtils.arraycopy(var0, 0, var2, 0, var1); - return var2; - } - - private void method1922(boolean var1, boolean var2, boolean var3, boolean var4, boolean var5) { - int var6 = 0; - if (var2) { - this.aClass121_3839.anInt1639 = var6; - var6 += 12; - } - - if (var3) { - this.aClass121_3814.anInt1639 = var6; - var6 += 4; - } - - if (var4) { - this.aClass121_3815.anInt1639 = var6; - var6 += 12; - } - - if (var5) { - this.aClass121_3817.anInt1639 = var6; - var6 += 8; - } - - if (var6 != 0) { - if (aClass3_Sub30_3818.buffer.length < this.anInt3833 * var6) { - aClass3_Sub30_3818 = new DataBuffer((this.anInt3833 + 100) * var6); - } else { - aClass3_Sub30_3818.index = 0; - } - - int var7; - int var8; - int var9; - int var10; - int var11; - int var12; - int var13; - if (var2) { - int var14; - if (HDToolKit.aBoolean1790) { - for (var7 = 0; var7 < this.anInt3823; ++var7) { - var8 = Float.floatToRawIntBits((float) this.anIntArray3822[var7]); - var9 = Float.floatToRawIntBits((float) this.anIntArray3845[var7]); - var10 = Float.floatToRawIntBits((float) this.anIntArray3848[var7]); - var11 = this.anIntArray3838[var7]; - var12 = this.anIntArray3838[var7 + 1]; - - for (var13 = var11; var13 < var12; ++var13) { - var14 = this.aShortArray3828[var13] - 1; - if (var14 == -1) { - break; - } - - aClass3_Sub30_3818.index = var14 * var6; - aClass3_Sub30_3818.writeInt(var8); - aClass3_Sub30_3818.writeInt(var9); - aClass3_Sub30_3818.writeInt(var10); - } - } - } else { - for (var7 = 0; var7 < this.anInt3823; ++var7) { - var8 = Float.floatToRawIntBits((float) this.anIntArray3822[var7]); - var9 = Float.floatToRawIntBits((float) this.anIntArray3845[var7]); - var10 = Float.floatToRawIntBits((float) this.anIntArray3848[var7]); - var11 = this.anIntArray3838[var7]; - var12 = this.anIntArray3838[var7 + 1]; - - for (var13 = var11; var13 < var12; ++var13) { - var14 = this.aShortArray3828[var13] - 1; - if (var14 == -1) { - break; - } - - aClass3_Sub30_3818.index = var14 * var6; - aClass3_Sub30_3818.writeIntLE(var8); - aClass3_Sub30_3818.writeIntLE(var9); - aClass3_Sub30_3818.writeIntLE(var10); - } - } - } - } - - if (var3) { - if (Class106.aBoolean1441) { - for (var7 = 0; var7 < this.anInt3852; ++var7) { - var8 = method1905(this.aShortArray3808[var7], this.aShortArray3827[var7], this.aShort3849, this.aByteArray3816[var7]); - aClass3_Sub30_3818.index = this.aClass121_3814.anInt1639 + this.aShortArray3811[var7] * var6; - aClass3_Sub30_3818.writeInt(var8); - aClass3_Sub30_3818.index = this.aClass121_3814.anInt1639 + this.aShortArray3830[var7] * var6; - aClass3_Sub30_3818.writeInt(var8); - aClass3_Sub30_3818.index = this.aClass121_3814.anInt1639 + this.aShortArray3831[var7] * var6; - aClass3_Sub30_3818.writeInt(var8); - } - } else { - var7 = (int) Class92.light0Position[0]; - var8 = (int) Class92.light0Position[1]; - var9 = (int) Class92.light0Position[2]; - var10 = (int) Math.sqrt(var7 * var7 + var8 * var8 + var9 * var9); - var11 = (int) ((float) this.aShort3849 * 1.3F); - var12 = this.aShort3819 * var10 >> 8; - - for (var13 = 0; var13 < this.anInt3852; ++var13) { - short var33 = this.aShortArray3811[var13]; - short var15 = this.aShortArray3841[var33]; - int var16; - if (var15 < 0) { - var16 = -1 - var15; - } else { - if (var15 == 0) { - var16 = var11 + (var7 * this.aShortArray3810[var33] + var8 * this.aShortArray3826[var33] + var9 * this.aShortArray3837[var33]) / (var12 + var12 / 2); - } else { - var16 = var11 + (var7 * this.aShortArray3810[var33] + var8 * this.aShortArray3826[var33] + var9 * this.aShortArray3837[var33]) / (var12 * var15); - } - - if (var16 < 0) { - var16 = 0; - } else if (var16 > 16384) { - var16 = 16384; - } - - this.aShortArray3841[var33] = (short) (-1 - var16); - } - - short var17 = this.aShortArray3830[var13]; - short var18 = this.aShortArray3841[var17]; - int var19; - if (var18 < 0) { - var19 = -1 - var18; - } else { - if (var18 == 0) { - var19 = var11 + (var7 * this.aShortArray3810[var17] + var8 * this.aShortArray3826[var17] + var9 * this.aShortArray3837[var17]) / (var12 + var12 / 2); - } else { - var19 = var11 + (var7 * this.aShortArray3810[var17] + var8 * this.aShortArray3826[var17] + var9 * this.aShortArray3837[var17]) / (var12 * var18); - } - - if (var19 < 0) { - var19 = 0; - } else if (var19 > 16384) { - var19 = 16384; - } - - this.aShortArray3841[var17] = (short) (-1 - var19); - } - - short var20 = this.aShortArray3831[var13]; - short var21 = this.aShortArray3841[var20]; - int var22; - if (var21 < 0) { - var22 = -1 - var21; - } else { - if (var21 == 0) { - var22 = var11 + (var7 * this.aShortArray3810[var20] + var8 * this.aShortArray3826[var20] + var9 * this.aShortArray3837[var20]) / (var12 + var12 / 2); - } else { - var22 = var11 + (var7 * this.aShortArray3810[var20] + var8 * this.aShortArray3826[var20] + var9 * this.aShortArray3837[var20]) / (var12 * var21); - } - - if (var22 < 0) { - var22 = 0; - } else if (var22 > 16384) { - var22 = 16384; - } - - this.aShortArray3841[var20] = (short) (-1 - var22); - } - - int var23 = method1905(this.aShortArray3808[var13], this.aShortArray3827[var13], var16, this.aByteArray3816[var13]); - int var24 = method1905(this.aShortArray3808[var13], this.aShortArray3827[var13], var19, this.aByteArray3816[var13]); - int var25 = method1905(this.aShortArray3808[var13], this.aShortArray3827[var13], var22, this.aByteArray3816[var13]); - aClass3_Sub30_3818.index = this.aClass121_3814.anInt1639 + var33 * var6; - aClass3_Sub30_3818.writeInt(var23); - aClass3_Sub30_3818.index = this.aClass121_3814.anInt1639 + var17 * var6; - aClass3_Sub30_3818.writeInt(var24); - aClass3_Sub30_3818.index = this.aClass121_3814.anInt1639 + var20 * var6; - aClass3_Sub30_3818.writeInt(var25); - } - - this.aShortArray3810 = null; - this.aShortArray3826 = null; - this.aShortArray3837 = null; - } - } - - if (var4) { - float var27 = 3.0F / (float) this.aShort3819; - float var26 = 3.0F / (float) (this.aShort3819 + this.aShort3819 / 2); - aClass3_Sub30_3818.index = this.aClass121_3815.anInt1639; - short var30; - float var32; - if (HDToolKit.aBoolean1790) { - for (var9 = 0; var9 < this.anInt3833; ++var9) { - var30 = this.aShortArray3841[var9]; - if (var30 == 0) { - aClass3_Sub30_3818.writeFloat((float) Objects.requireNonNull(this.aShortArray3810)[var9] * var26); - aClass3_Sub30_3818.writeFloat((float) this.aShortArray3826[var9] * var26); - aClass3_Sub30_3818.writeFloat((float) this.aShortArray3837[var9] * var26); - } else { - var32 = var27 / (float) var30; - aClass3_Sub30_3818.writeFloat((float) Objects.requireNonNull(this.aShortArray3810)[var9] * var32); - aClass3_Sub30_3818.writeFloat((float) this.aShortArray3826[var9] * var32); - aClass3_Sub30_3818.writeFloat((float) this.aShortArray3837[var9] * var32); - } - - aClass3_Sub30_3818.index += var6 - 12; - } - } else { - for (var9 = 0; var9 < this.anInt3833; ++var9) { - var30 = this.aShortArray3841[var9]; - if (var30 == 0) { - aClass3_Sub30_3818.writeFloatLE((float) Objects.requireNonNull(this.aShortArray3810)[var9] * var26); - aClass3_Sub30_3818.writeFloatLE((float) this.aShortArray3826[var9] * var26); - aClass3_Sub30_3818.writeFloatLE((float) this.aShortArray3837[var9] * var26); - } else { - var32 = var27 / (float) var30; - aClass3_Sub30_3818.writeFloatLE((float) Objects.requireNonNull(this.aShortArray3810)[var9] * var32); - aClass3_Sub30_3818.writeFloatLE((float) this.aShortArray3826[var9] * var32); - aClass3_Sub30_3818.writeFloatLE((float) this.aShortArray3837[var9] * var32); - } - - aClass3_Sub30_3818.index += var6 - 12; - } - } - } - - if (var5) { - aClass3_Sub30_3818.index = this.aClass121_3817.anInt1639; - if (HDToolKit.aBoolean1790) { - for (var7 = 0; var7 < this.anInt3833; ++var7) { - aClass3_Sub30_3818.writeFloat(this.aFloatArray3824[var7]); - aClass3_Sub30_3818.writeFloat(this.aFloatArray3847[var7]); - aClass3_Sub30_3818.index += var6 - 8; - } - } else { - for (var7 = 0; var7 < this.anInt3833; ++var7) { - aClass3_Sub30_3818.writeFloatLE(this.aFloatArray3824[var7]); - aClass3_Sub30_3818.writeFloatLE(this.aFloatArray3847[var7]); - aClass3_Sub30_3818.index += var6 - 8; - } - } - } - - aClass3_Sub30_3818.index = var6 * this.anInt3833; - ByteBuffer var29; - if (var1) { - if (HDToolKit.aBoolean1817) { - var29 = ByteBuffer.wrap(aClass3_Sub30_3818.buffer, 0, aClass3_Sub30_3818.index); - if (this.aClass156_3812 == null) { - this.aClass156_3812 = new Class156(true); - this.aClass156_3812.method2172(var29); - } else { - this.aClass156_3812.method2168(var29); - } - - if (var2) { - this.aClass121_3839.aBoolean1640 = true; - this.aClass121_3839.aByteBuffer1644 = null; - this.aClass121_3839.aClass156_1643 = this.aClass156_3812; - this.aClass121_3839.anInt1648 = var6; - } - - if (var3) { - this.aClass121_3814.aBoolean1640 = true; - this.aClass121_3814.aByteBuffer1644 = null; - this.aClass121_3814.aClass156_1643 = this.aClass156_3812; - this.aClass121_3814.anInt1648 = var6; - } - - if (var4) { - this.aClass121_3815.aBoolean1640 = true; - this.aClass121_3815.aByteBuffer1644 = null; - this.aClass121_3815.aClass156_1643 = this.aClass156_3812; - this.aClass121_3815.anInt1648 = var6; - } - - if (var5) { - this.aClass121_3817.aBoolean1640 = true; - this.aClass121_3817.aByteBuffer1644 = null; - this.aClass121_3817.aClass156_1643 = this.aClass156_3812; - this.aClass121_3817.anInt1648 = var6; - } - } else { - if (aByteBuffer3834 != null && aByteBuffer3834.capacity() >= aClass3_Sub30_3818.index) { - aByteBuffer3834.clear(); - } else { - aByteBuffer3834 = ByteBuffer.allocateDirect(aClass3_Sub30_3818.index + 100 * var6); - } - - aByteBuffer3834.put(aClass3_Sub30_3818.buffer, 0, aClass3_Sub30_3818.index); - aByteBuffer3834.flip(); - if (var2) { - this.aClass121_3839.aBoolean1640 = true; - this.aClass121_3839.aByteBuffer1644 = aByteBuffer3834; - this.aClass121_3839.aClass156_1643 = null; - this.aClass121_3839.anInt1648 = var6; - } - - if (var3) { - this.aClass121_3814.aBoolean1640 = true; - this.aClass121_3814.aByteBuffer1644 = aByteBuffer3834; - this.aClass121_3839.aClass156_1643 = null; - this.aClass121_3814.anInt1648 = var6; - } - - if (var4) { - this.aClass121_3815.aBoolean1640 = true; - this.aClass121_3815.aByteBuffer1644 = aByteBuffer3834; - this.aClass121_3815.aClass156_1643 = null; - this.aClass121_3815.anInt1648 = var6; - } - - if (var5) { - this.aClass121_3817.aBoolean1640 = true; - this.aClass121_3817.aByteBuffer1644 = aByteBuffer3834; - this.aClass121_3817.aClass156_1643 = null; - this.aClass121_3817.anInt1648 = var6; - } - } - } else if (HDToolKit.supportVertexBufferObject) { - Class156 var31 = new Class156(); - ByteBuffer var28 = ByteBuffer.wrap(aClass3_Sub30_3818.buffer, 0, aClass3_Sub30_3818.index); - var31.method2172(var28); - if (var2) { - this.aClass121_3839.aBoolean1640 = true; - this.aClass121_3839.aByteBuffer1644 = null; - this.aClass121_3839.aClass156_1643 = var31; - this.aClass121_3839.anInt1648 = var6; - } - - if (var3) { - this.aClass121_3814.aBoolean1640 = true; - this.aClass121_3814.aByteBuffer1644 = null; - this.aClass121_3814.aClass156_1643 = var31; - this.aClass121_3814.anInt1648 = var6; - } - - if (var4) { - this.aClass121_3815.aBoolean1640 = true; - this.aClass121_3815.aByteBuffer1644 = null; - this.aClass121_3815.aClass156_1643 = var31; - this.aClass121_3815.anInt1648 = var6; - } - - if (var5) { - this.aClass121_3817.aBoolean1640 = true; - this.aClass121_3817.aByteBuffer1644 = null; - this.aClass121_3817.aClass156_1643 = var31; - this.aClass121_3817.anInt1648 = var6; - } - } else { - var29 = ByteBuffer.allocateDirect(aClass3_Sub30_3818.index); - var29.put(aClass3_Sub30_3818.buffer, 0, aClass3_Sub30_3818.index); - var29.flip(); - if (var2) { - this.aClass121_3839.aBoolean1640 = true; - this.aClass121_3839.aByteBuffer1644 = var29; - this.aClass121_3839.aClass156_1643 = null; - this.aClass121_3839.anInt1648 = var6; - } - - if (var3) { - this.aClass121_3814.aBoolean1640 = true; - this.aClass121_3814.aByteBuffer1644 = var29; - this.aClass121_3839.aClass156_1643 = null; - this.aClass121_3814.anInt1648 = var6; - } - - if (var4) { - this.aClass121_3815.aBoolean1640 = true; - this.aClass121_3815.aByteBuffer1644 = var29; - this.aClass121_3815.aClass156_1643 = null; - this.aClass121_3815.anInt1648 = var6; - } - - if (var5) { - this.aClass121_3817.aBoolean1640 = true; - this.aClass121_3817.aByteBuffer1644 = var29; - this.aClass121_3817.aClass156_1643 = null; - this.aClass121_3817.anInt1648 = var6; - } - } - - } - } - - private Model method1923(boolean var1, boolean var2, boolean var3, Class140_Sub1_Sub1 var4, Class140_Sub1_Sub1 var5) { - var4.anInt3823 = this.anInt3823; - var4.anInt3833 = this.anInt3833; - var4.anInt3852 = this.anInt3852; - var4.aShort3849 = this.aShort3849; - var4.aShort3819 = this.aShort3819; - var4.aByte3836 = (byte) (1 | (var1 && var2 ? 0 : 2) | (var3 ? 0 : 4)); - if (var4.anIntArray3822 == null || var4.anIntArray3822.length < this.anInt3823) { - var4.anIntArray3822 = new int[this.anInt3823 + 100]; - var4.anIntArray3845 = new int[this.anInt3823 + 100]; - var4.anIntArray3848 = new int[this.anInt3823 + 100]; - } - - int var6; - for (var6 = 0; var6 < this.anInt3823; ++var6) { - var4.anIntArray3822[var6] = this.anIntArray3822[var6]; - var4.anIntArray3845[var6] = this.anIntArray3845[var6]; - var4.anIntArray3848[var6] = this.anIntArray3848[var6]; - } - - if (var4.aClass121_3839 == null) { - var4.aClass121_3839 = new Class121(); - } - - var4.aClass121_3839.aBoolean1640 = false; - if (var4.aClass6_3835 == null) { - var4.aClass6_3835 = new Class6(); - } - - var4.aClass6_3835.aBoolean98 = false; - if (var1) { - var4.aByteArray3816 = this.aByteArray3816; - } else { - if (var5.aByteArray3816 == null || var5.aByteArray3816.length < this.anInt3852) { - var5.aByteArray3816 = new byte[this.anInt3852 + 100]; - } - - var4.aByteArray3816 = var5.aByteArray3816; - - for (var6 = 0; var6 < this.anInt3852; ++var6) { - var4.aByteArray3816[var6] = this.aByteArray3816[var6]; - } - } - - if (var2) { - var4.aShortArray3808 = this.aShortArray3808; - } else { - if (var5.aShortArray3808 == null || var5.aShortArray3808.length < this.anInt3852) { - var5.aShortArray3808 = new short[this.anInt3852 + 100]; - } - - var4.aShortArray3808 = var5.aShortArray3808; - - for (var6 = 0; var6 < this.anInt3852; ++var6) { - var4.aShortArray3808[var6] = this.aShortArray3808[var6]; - } - } - - if (var1 && var2) { - var4.aClass121_3814 = this.aClass121_3814; - } else { - if (var5.aClass121_3814 == null) { - var5.aClass121_3814 = new Class121(); - } - - var4.aClass121_3814 = var5.aClass121_3814; - var4.aClass121_3814.aBoolean1640 = false; - } - - if (var3 || this.aShortArray3810 == null) { - var4.aShortArray3810 = this.aShortArray3810; - var4.aShortArray3826 = this.aShortArray3826; - var4.aShortArray3837 = this.aShortArray3837; - var4.aShortArray3841 = this.aShortArray3841; - var4.aClass121_3815 = this.aClass121_3815; - } else { - if (var5.aShortArray3810 == null || var5.aShortArray3810.length < this.anInt3833) { - var5.aShortArray3810 = new short[this.anInt3833 + 100]; - var5.aShortArray3826 = new short[this.anInt3833 + 100]; - var5.aShortArray3837 = new short[this.anInt3833 + 100]; - var5.aShortArray3841 = new short[this.anInt3833 + 100]; - } - - var4.aShortArray3810 = var5.aShortArray3810; - var4.aShortArray3826 = var5.aShortArray3826; - var4.aShortArray3837 = var5.aShortArray3837; - var4.aShortArray3841 = var5.aShortArray3841; - - for (var6 = 0; var6 < this.anInt3833; ++var6) { - var4.aShortArray3810[var6] = this.aShortArray3810[var6]; - var4.aShortArray3826[var6] = this.aShortArray3826[var6]; - var4.aShortArray3837[var6] = this.aShortArray3837[var6]; - var4.aShortArray3841[var6] = this.aShortArray3841[var6]; - } - - if (Class106.aBoolean1441) { - if (var5.aClass121_3815 == null) { - var5.aClass121_3815 = new Class121(); - } - - var4.aClass121_3815 = var5.aClass121_3815; - var4.aClass121_3815.aBoolean1640 = false; - } else { - var4.aClass121_3815 = null; - } - } - - var4.aFloatArray3824 = this.aFloatArray3824; - var4.aFloatArray3847 = this.aFloatArray3847; - var4.anIntArray3844 = this.anIntArray3844; - var4.anIntArrayArray3825 = this.anIntArrayArray3825; - var4.aShortArray3811 = this.aShortArray3811; - var4.aShortArray3830 = this.aShortArray3830; - var4.aShortArray3831 = this.aShortArray3831; - var4.aShortArray3827 = this.aShortArray3827; - var4.aByteArray3820 = this.aByteArray3820; - var4.anIntArrayArray3829 = this.anIntArrayArray3829; - var4.aClass121_3817 = this.aClass121_3817; - var4.aClass121_3846 = this.aClass121_3846; - var4.anIntArray3840 = this.anIntArray3840; - var4.aShortArray3828 = this.aShortArray3828; - var4.anIntArray3838 = this.anIntArray3838; - var4.aBoolean2699 = this.aBoolean2699; - var4.aShortArray3832 = this.aShortArray3832; - var4.aShortArray3813 = this.aShortArray3813; - return var4; - } - - final GameObject method1861() { - this.aBoolean3809 = false; - if (this.aClass18_3843 != null) { - this.aShortArray3810 = this.aClass18_3843.aShortArray417; - this.aShortArray3826 = this.aClass18_3843.aShortArray419; - this.aShortArray3837 = this.aClass18_3843.aShortArray418; - this.aShortArray3841 = this.aClass18_3843.aShortArray416; - this.aClass18_3843 = null; - } - - return this; - } - - final int method1924() { - return this.aShort3819; - } - - final void method1925() { - if (this.aShortArray3810 == null) { - this.method1885(); - } else { - int var1; - for (var1 = 0; var1 < this.anInt3823; ++var1) { - int var2 = this.anIntArray3822[var1]; - this.anIntArray3822[var1] = this.anIntArray3848[var1]; - this.anIntArray3848[var1] = -var2; - } - - for (var1 = 0; var1 < this.anInt3833; ++var1) { - short var3 = this.aShortArray3810[var1]; - this.aShortArray3810[var1] = this.aShortArray3837[var1]; - this.aShortArray3837[var1] = (short) (-var3); - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - if (this.aClass121_3815 != null) { - this.aClass121_3815.aBoolean1640 = false; - } - - } - } - - final Class140_Sub1_Sub1 method1926(boolean var1, boolean var2, boolean var3, boolean var5, boolean var6, boolean var7, boolean var8, boolean var10, boolean var11) { - Class140_Sub1_Sub1 var12 = new Class140_Sub1_Sub1(); - var12.anInt3823 = this.anInt3823; - var12.anInt3833 = this.anInt3833; - var12.anInt3852 = this.anInt3852; - if (var1) { - var12.anIntArray3822 = this.anIntArray3822; - var12.anIntArray3848 = this.anIntArray3848; - } else { - var12.anIntArray3822 = Class65.method1233(this.anIntArray3822); - var12.anIntArray3848 = Class65.method1233(this.anIntArray3848); - } - - if (var2) { - var12.anIntArray3845 = this.anIntArray3845; - } else { - var12.anIntArray3845 = Class65.method1233(this.anIntArray3845); - } - - if (var1 && var2) { - var12.aClass121_3839 = this.aClass121_3839; - var12.aClass6_3835 = this.aClass6_3835; - } else { - var12.aClass121_3839 = new Class121(); - var12.aClass6_3835 = new Class6(); - } - - if (var3) { - var12.aShortArray3808 = this.aShortArray3808; - } else { - var12.aShortArray3808 = Unsorted.method65(this.aShortArray3808); - } - - var12.aByteArray3816 = this.aByteArray3816; - - if (var3 && var5 && (var8 && var6 || Class106.aBoolean1441)) { - var12.aClass121_3814 = this.aClass121_3814; - } else { - var12.aClass121_3814 = new Class121(); - } - - if (var6) { - var12.aShortArray3810 = this.aShortArray3810; - var12.aShortArray3826 = this.aShortArray3826; - var12.aShortArray3837 = this.aShortArray3837; - var12.aShortArray3841 = this.aShortArray3841; - } else { - var12.aShortArray3810 = Unsorted.method65(this.aShortArray3810); - var12.aShortArray3826 = Unsorted.method65(this.aShortArray3826); - var12.aShortArray3837 = Unsorted.method65(this.aShortArray3837); - var12.aShortArray3841 = Unsorted.method65(this.aShortArray3841); - } - - if (Class106.aBoolean1441) { - if (var6 && var7 && var8) { - var12.aClass121_3815 = this.aClass121_3815; - } else { - var12.aClass121_3815 = new Class121(); - } - } else { - var12.aClass121_3815 = null; - } - - var12.aFloatArray3824 = this.aFloatArray3824; - var12.aFloatArray3847 = this.aFloatArray3847; - var12.aClass121_3817 = this.aClass121_3817; - - if (var10) { - var12.aShortArray3811 = this.aShortArray3811; - var12.aShortArray3830 = this.aShortArray3830; - var12.aShortArray3831 = this.aShortArray3831; - var12.aClass121_3846 = this.aClass121_3846; - } else { - var12.aShortArray3811 = Unsorted.method65(this.aShortArray3811); - var12.aShortArray3830 = Unsorted.method65(this.aShortArray3830); - var12.aShortArray3831 = Unsorted.method65(this.aShortArray3831); - var12.aClass121_3846 = new Class121(); - } - - if (var11) { - var12.aShortArray3827 = this.aShortArray3827; - } else { - var12.aShortArray3827 = Unsorted.method65(this.aShortArray3827); - } - - var12.anIntArray3844 = this.anIntArray3844; - var12.anIntArrayArray3825 = this.anIntArrayArray3825; - var12.aByteArray3820 = this.aByteArray3820; - var12.anIntArrayArray3829 = this.anIntArrayArray3829; - var12.anIntArray3840 = this.anIntArray3840; - var12.aShortArray3828 = this.aShortArray3828; - var12.anIntArray3838 = this.anIntArray3838; - var12.aShort3849 = this.aShort3849; - var12.aShort3819 = this.aShort3819; - var12.aShortArray3832 = this.aShortArray3832; - var12.aShortArray3813 = this.aShortArray3813; - return var12; - } - - final void method1885() { - for (int var1 = 0; var1 < this.anInt3823; ++var1) { - int var2 = this.anIntArray3822[var1]; - this.anIntArray3822[var1] = this.anIntArray3848[var1]; - this.anIntArray3848[var1] = -var2; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - private boolean method1927(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - return (var2 >= var3 || var2 >= var4 || var2 >= var5) && ((var2 <= var3 || var2 <= var4 || var2 <= var5) && ((var1 >= var6 || var1 >= var7 || var1 >= var8) && (var1 <= var6 || var1 <= var7 || var1 <= var8))); - } - - private static short[] method1928(short[] var0, int var1) { - short[] var2 = new short[var1]; - ArrayUtils.arraycopy(var0, 0, var2, 0, var1); - return var2; - } - - final int method1888() { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - return this.aClass6_3835.aShort93; - } - - final void method1891(int var1, int[] var2, int var3, int var4, int var5, boolean var6) { - int var7 = var2.length; - int var8; - int var9; - int var12; - int var13; - if (var1 == 0) { - var3 <<= 4; - var4 <<= 4; - var5 <<= 4; - var8 = 0; - anInt3859 = 0; - anInt3865 = 0; - anInt3862 = 0; - - for (var9 = 0; var9 < var7; ++var9) { - int var20 = var2[var9]; - if (var20 < this.anIntArrayArray3825.length) { - int[] var21 = this.anIntArrayArray3825[var20]; - - for (var12 = 0; var12 < var21.length; ++var12) { - var13 = var21[var12]; - anInt3859 += this.anIntArray3822[var13]; - anInt3865 += this.anIntArray3845[var13]; - anInt3862 += this.anIntArray3848[var13]; - ++var8; - } - } - } - - if (var8 > 0) { - anInt3859 = anInt3859 / var8 + var3; - anInt3865 = anInt3865 / var8 + var4; - anInt3862 = anInt3862 / var8 + var5; - } else { - anInt3859 = var3; - anInt3865 = var4; - anInt3862 = var5; - } - - } else { - int[] var10; - int var11; - if (var1 == 1) { - var3 <<= 4; - var4 <<= 4; - var5 <<= 4; - - for (var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if (var9 < this.anIntArrayArray3825.length) { - var10 = this.anIntArrayArray3825[var9]; - - for (var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - this.anIntArray3822[var12] += var3; - this.anIntArray3845[var12] += var4; - this.anIntArray3848[var12] += var5; - } - } - } - - } else { - int var14; - int var15; - int var16; - if (var1 == 2) { - for (var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if (var9 < this.anIntArrayArray3825.length) { - var10 = this.anIntArrayArray3825[var9]; - - for (var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - this.anIntArray3822[var12] -= anInt3859; - this.anIntArray3845[var12] -= anInt3865; - this.anIntArray3848[var12] -= anInt3862; - if (var5 != 0) { - var13 = Class51.anIntArray840[var5]; - var14 = Class51.anIntArray851[var5]; - var15 = this.anIntArray3845[var12] * var13 + this.anIntArray3822[var12] * var14 + 32767 >> 16; - this.anIntArray3845[var12] = this.anIntArray3845[var12] * var14 - this.anIntArray3822[var12] * var13 + 32767 >> 16; - this.anIntArray3822[var12] = var15; - } - - if (var3 != 0) { - var13 = Class51.anIntArray840[var3]; - var14 = Class51.anIntArray851[var3]; - var15 = this.anIntArray3845[var12] * var14 - this.anIntArray3848[var12] * var13 + 32767 >> 16; - this.anIntArray3848[var12] = this.anIntArray3845[var12] * var13 + this.anIntArray3848[var12] * var14 + 32767 >> 16; - this.anIntArray3845[var12] = var15; - } - - if (var4 != 0) { - var13 = Class51.anIntArray840[var4]; - var14 = Class51.anIntArray851[var4]; - var15 = this.anIntArray3848[var12] * var13 + this.anIntArray3822[var12] * var14 + 32767 >> 16; - this.anIntArray3848[var12] = this.anIntArray3848[var12] * var14 - this.anIntArray3822[var12] * var13 + 32767 >> 16; - this.anIntArray3822[var12] = var15; - } - - this.anIntArray3822[var12] += anInt3859; - this.anIntArray3845[var12] += anInt3865; - this.anIntArray3848[var12] += anInt3862; - } - } - } - - if (var6 && this.aShortArray3810 != null) { - for (var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if (var9 < this.anIntArrayArray3825.length) { - var10 = this.anIntArrayArray3825[var9]; - - for (var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - var13 = this.anIntArray3838[var12]; - var14 = this.anIntArray3838[var12 + 1]; - - for (var15 = var13; var15 < var14; ++var15) { - var16 = this.aShortArray3828[var15] - 1; - if (var16 == -1) { - break; - } - - int var17; - int var19; - int var18; - if (var5 != 0) { - var17 = Class51.anIntArray840[var5]; - var18 = Class51.anIntArray851[var5]; - var19 = this.aShortArray3826[var16] * var17 + this.aShortArray3810[var16] * var18 + 32767 >> 16; - this.aShortArray3826[var16] = (short) (this.aShortArray3826[var16] * var18 - this.aShortArray3810[var16] * var17 + 32767 >> 16); - this.aShortArray3810[var16] = (short) var19; - } - - if (var3 != 0) { - var17 = Class51.anIntArray840[var3]; - var18 = Class51.anIntArray851[var3]; - var19 = this.aShortArray3826[var16] * var18 - this.aShortArray3837[var16] * var17 + 32767 >> 16; - this.aShortArray3837[var16] = (short) (this.aShortArray3826[var16] * var17 + this.aShortArray3837[var16] * var18 + 32767 >> 16); - this.aShortArray3826[var16] = (short) var19; - } - - if (var4 != 0) { - var17 = Class51.anIntArray840[var4]; - var18 = Class51.anIntArray851[var4]; - var19 = this.aShortArray3837[var16] * var17 + this.aShortArray3810[var16] * var18 + 32767 >> 16; - this.aShortArray3837[var16] = (short) (this.aShortArray3837[var16] * var18 - this.aShortArray3810[var16] * var17 + 32767 >> 16); - this.aShortArray3810[var16] = (short) var19; - } - } - } - } - } - - if (this.aClass121_3815 != null) { - this.aClass121_3815.aBoolean1640 = false; - } - } - - } else if (var1 == 3) { - for (var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if (var9 < this.anIntArrayArray3825.length) { - var10 = this.anIntArrayArray3825[var9]; - - for (var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - this.anIntArray3822[var12] -= anInt3859; - this.anIntArray3845[var12] -= anInt3865; - this.anIntArray3848[var12] -= anInt3862; - this.anIntArray3822[var12] = this.anIntArray3822[var12] * var3 >> 7; - this.anIntArray3845[var12] = this.anIntArray3845[var12] * var4 >> 7; - this.anIntArray3848[var12] = this.anIntArray3848[var12] * var5 >> 7; - this.anIntArray3822[var12] += anInt3859; - this.anIntArray3845[var12] += anInt3865; - this.anIntArray3848[var12] += anInt3862; - } - } - } - - } else if (var1 == 5) { - if (this.anIntArrayArray3829 != null && this.aByteArray3816 != null) { - for (var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if (var9 < this.anIntArrayArray3829.length) { - var10 = this.anIntArrayArray3829[var9]; - - for (var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - var13 = (this.aByteArray3816[var12] & 0xFF) + var3 * 8; - if (var13 < 0) { - var13 = 0; - } else if (var13 > 255) { - var13 = 255; - } - - this.aByteArray3816[var12] = (byte) var13; - } - - if (var10.length > 0) { - this.aClass121_3814.aBoolean1640 = false; - } - } - } - } - - } else if (var1 == 7) { - if (this.anIntArrayArray3829 != null) { - for (var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if (var9 < this.anIntArrayArray3829.length) { - var10 = this.anIntArrayArray3829[var9]; - - for (var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - var13 = this.aShortArray3808[var12] & 65535; - var14 = var13 >> 10 & 63; - var15 = var13 >> 7 & 7; - var16 = var13 & 127; - var14 = var14 + var3 & 63; - var15 += var4 / 4; - if (var15 < 0) { - var15 = 0; - } else if (var15 > 7) { - var15 = 7; - } - - var16 += var5; - if (var16 < 0) { - var16 = 0; - } else if (var16 > 127) { - var16 = 127; - } - - this.aShortArray3808[var12] = (short) (var14 << 10 | var15 << 7 | var16); - } - - if (var10.length > 0) { - this.aClass121_3814.aBoolean1640 = false; - } - } - } - } - - } - } - } - } - - final void method1889(int var1, int var2, int var3, int var4) { - int var5; - int var6; - if (var1 == 0) { - var5 = 0; - anInt3859 = 0; - anInt3865 = 0; - anInt3862 = 0; - - for (var6 = 0; var6 < this.anInt3823; ++var6) { - anInt3859 += this.anIntArray3822[var6]; - anInt3865 += this.anIntArray3845[var6]; - anInt3862 += this.anIntArray3848[var6]; - ++var5; - } - - if (var5 > 0) { - anInt3859 = anInt3859 / var5 + var2; - anInt3865 = anInt3865 / var5 + var3; - anInt3862 = anInt3862 / var5 + var4; - } else { - anInt3859 = var2; - anInt3865 = var3; - anInt3862 = var4; - } - - } else if (var1 == 1) { - for (var5 = 0; var5 < this.anInt3823; ++var5) { - this.anIntArray3822[var5] += var2; - this.anIntArray3845[var5] += var3; - this.anIntArray3848[var5] += var4; - } - - } else { - int var7; - int var8; - if (var1 == 2) { - for (var5 = 0; var5 < this.anInt3823; ++var5) { - this.anIntArray3822[var5] -= anInt3859; - this.anIntArray3845[var5] -= anInt3865; - this.anIntArray3848[var5] -= anInt3862; - if (var4 != 0) { - var6 = Class51.anIntArray840[var4]; - var7 = Class51.anIntArray851[var4]; - var8 = this.anIntArray3845[var5] * var6 + this.anIntArray3822[var5] * var7 + 32767 >> 16; - this.anIntArray3845[var5] = this.anIntArray3845[var5] * var7 - this.anIntArray3822[var5] * var6 + 32767 >> 16; - this.anIntArray3822[var5] = var8; - } - - if (var2 != 0) { - var6 = Class51.anIntArray840[var2]; - var7 = Class51.anIntArray851[var2]; - var8 = this.anIntArray3845[var5] * var7 - this.anIntArray3848[var5] * var6 + 32767 >> 16; - this.anIntArray3848[var5] = this.anIntArray3845[var5] * var6 + this.anIntArray3848[var5] * var7 + 32767 >> 16; - this.anIntArray3845[var5] = var8; - } - - if (var3 != 0) { - var6 = Class51.anIntArray840[var3]; - var7 = Class51.anIntArray851[var3]; - var8 = this.anIntArray3848[var5] * var6 + this.anIntArray3822[var5] * var7 + 32767 >> 16; - this.anIntArray3848[var5] = this.anIntArray3848[var5] * var7 - this.anIntArray3822[var5] * var6 + 32767 >> 16; - this.anIntArray3822[var5] = var8; - } - - this.anIntArray3822[var5] += anInt3859; - this.anIntArray3845[var5] += anInt3865; - this.anIntArray3848[var5] += anInt3862; - } - - } else if (var1 == 3) { - for (var5 = 0; var5 < this.anInt3823; ++var5) { - this.anIntArray3822[var5] -= anInt3859; - this.anIntArray3845[var5] -= anInt3865; - this.anIntArray3848[var5] -= anInt3862; - this.anIntArray3822[var5] = this.anIntArray3822[var5] * var2 / 128; - this.anIntArray3845[var5] = this.anIntArray3845[var5] * var3 / 128; - this.anIntArray3848[var5] = this.anIntArray3848[var5] * var4 / 128; - this.anIntArray3822[var5] += anInt3859; - this.anIntArray3845[var5] += anInt3865; - this.anIntArray3848[var5] += anInt3862; - } - - } else if (var1 == 5) { - for (var5 = 0; var5 < this.anInt3852; ++var5) { - var6 = (this.aByteArray3816[var5] & 0xFF) + var2 * 8; - if (var6 < 0) { - var6 = 0; - } else if (var6 > 255) { - var6 = 255; - } - - this.aByteArray3816[var5] = (byte) var6; - } - - this.aClass121_3814.aBoolean1640 = false; - } else if (var1 == 7) { - for (var5 = 0; var5 < this.anInt3852; ++var5) { - var6 = this.aShortArray3808[var5] & 65535; - var7 = var6 >> 10 & 63; - var8 = var6 >> 7 & 7; - int var9 = var6 & 127; - var7 = var7 + var2 & 63; - var8 += var3 / 4; - if (var8 < 0) { - var8 = 0; - } else if (var8 > 7) { - var8 = 7; - } - - var9 += var4; - if (var9 < 0) { - var9 = 0; - } else if (var9 > 127) { - var9 = 127; - } - - this.aShortArray3808[var5] = (short) (var7 << 10 | var8 << 7 | var9); - } - - this.aClass121_3814.aBoolean1640 = false; - } - } - } - - static void method1929() { - aClass140_Sub1_Sub1_3842 = new Class140_Sub1_Sub1(); - aClass140_Sub1_Sub1_3850 = new Class140_Sub1_Sub1(); - aClass140_Sub1_Sub1_3853 = new Class140_Sub1_Sub1(); - aClass140_Sub1_Sub1_3854 = new Class140_Sub1_Sub1(); - aClass140_Sub1_Sub1_3855 = new Class140_Sub1_Sub1(); - aClass140_Sub1_Sub1_3856 = new Class140_Sub1_Sub1(); - } - - final int method1884() { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - return this.aClass6_3835.aShort95; - } - - private void method1930() { - GL var1 = HDToolKit.gl; - if (this.anInt3852 != 0) { - if (this.aByte3836 != 0) { - this.method1922(true, !this.aClass121_3839.aBoolean1640 && (this.aByte3836 & 1) != 0, !this.aClass121_3814.aBoolean1640 && (this.aByte3836 & 2) != 0, this.aClass121_3815 != null && !this.aClass121_3815.aBoolean1640 && (this.aByte3836 & 4) != 0, false); - } - - this.method1922(false, !this.aClass121_3839.aBoolean1640, !this.aClass121_3814.aBoolean1640, this.aClass121_3815 != null && !this.aClass121_3815.aBoolean1640, !this.aClass121_3817.aBoolean1640); - if (!this.aClass121_3846.aBoolean1640) { - this.method1912(); - } - - if (this.aByte3851 != 0) { - if ((this.aByte3851 & 1) != 0) { - this.anIntArray3822 = null; - this.anIntArray3845 = null; - this.anIntArray3848 = null; - this.aShortArray3828 = null; - this.anIntArray3838 = null; - } - - if ((this.aByte3851 & 2) != 0) { - this.aShortArray3808 = null; - this.aByteArray3816 = null; - } - - if ((this.aByte3851 & 4) != 0) { - this.aShortArray3810 = null; - this.aShortArray3826 = null; - this.aShortArray3837 = null; - this.aShortArray3841 = null; - } - - if ((this.aByte3851 & 8) != 0) { - this.aFloatArray3824 = null; - this.aFloatArray3847 = null; - } - - if ((this.aByte3851 & 16) != 0) { - this.aShortArray3811 = null; - this.aShortArray3830 = null; - this.aShortArray3831 = null; - } - - this.aByte3851 = 0; - } - - Class156 var2 = null; - if (this.aClass121_3839.aClass156_1643 != null) { - this.aClass121_3839.aClass156_1643.method2169(); - var2 = this.aClass121_3839.aClass156_1643; - var1.glVertexPointer(3, 5126, this.aClass121_3839.anInt1648, this.aClass121_3839.anInt1639); - } - - if (this.aClass121_3814.aClass156_1643 != null) { - if (var2 != this.aClass121_3814.aClass156_1643) { - this.aClass121_3814.aClass156_1643.method2169(); - var2 = this.aClass121_3814.aClass156_1643; - } - - var1.glColorPointer(4, 5121, this.aClass121_3814.anInt1648, this.aClass121_3814.anInt1639); - } - - if (Class106.aBoolean1441 && this.aClass121_3815.aClass156_1643 != null) { - if (var2 != this.aClass121_3815.aClass156_1643) { - this.aClass121_3815.aClass156_1643.method2169(); - var2 = this.aClass121_3815.aClass156_1643; - } - - var1.glNormalPointer(5126, this.aClass121_3815.anInt1648, this.aClass121_3815.anInt1639); - } - - if (this.aClass121_3817.aClass156_1643 != null) { - if (var2 != this.aClass121_3817.aClass156_1643) { - this.aClass121_3817.aClass156_1643.method2169(); - } - - var1.glTexCoordPointer(2, 5126, this.aClass121_3817.anInt1648, this.aClass121_3817.anInt1639); - } - - if (this.aClass121_3846.aClass156_1643 != null) { - this.aClass121_3846.aClass156_1643.method2171(); - } - - if (this.aClass121_3839.aClass156_1643 == null || this.aClass121_3814.aClass156_1643 == null || Class106.aBoolean1441 && this.aClass121_3815.aClass156_1643 == null || this.aClass121_3817.aClass156_1643 == null) { - if (HDToolKit.supportVertexBufferObject) { - var1.glBindBufferARB('\u8892', 0); - } - - if (this.aClass121_3839.aClass156_1643 == null) { - this.aClass121_3839.aByteBuffer1644.position(this.aClass121_3839.anInt1639); - var1.glVertexPointer(3, 5126, this.aClass121_3839.anInt1648, this.aClass121_3839.aByteBuffer1644); - } - - if (this.aClass121_3814.aClass156_1643 == null) { - this.aClass121_3814.aByteBuffer1644.position(this.aClass121_3814.anInt1639); - var1.glColorPointer(4, 5121, this.aClass121_3814.anInt1648, this.aClass121_3814.aByteBuffer1644); - } - - if (Class106.aBoolean1441 && this.aClass121_3815.aClass156_1643 == null) { - this.aClass121_3815.aByteBuffer1644.position(this.aClass121_3815.anInt1639); - var1.glNormalPointer(5126, this.aClass121_3815.anInt1648, this.aClass121_3815.aByteBuffer1644); - } - - if (this.aClass121_3817.aClass156_1643 == null) { - this.aClass121_3817.aByteBuffer1644.position(this.aClass121_3817.anInt1639); - var1.glTexCoordPointer(2, 5126, this.aClass121_3817.anInt1648, this.aClass121_3817.aByteBuffer1644); - } - } - - if (this.aClass121_3846.aClass156_1643 == null && HDToolKit.supportVertexBufferObject) { - var1.glBindBufferARB('\u8893', 0); - } - - int var3 = this.anIntArray3840.length - 1; - - for (int var4 = 0; var4 < var3; ++var4) { - int var5 = this.anIntArray3840[var4]; - int var6 = this.anIntArray3840[var4 + 1]; - short var7 = this.aShortArray3827[var5]; - if (var7 == -1) { - HDToolKit.bindTexture2D(-1); - Unsorted.method551(0, 0); - } else { - Class51.anInterface2_838.method8(var7 & 65535, true); - } - - if (this.aClass121_3846.aClass156_1643 == null) { - this.aClass121_3846.aByteBuffer1644.position(var5 * 12); - var1.glDrawElements(4, (var6 - var5) * 3, 5125, this.aClass121_3846.aByteBuffer1644); - } else { - var1.glDrawElements(4, (var6 - var5) * 3, 5125, var5 * 12); - } - } - - } - } - - final void method1879() { - for (int var1 = 0; var1 < this.anInt3823; ++var1) { - this.anIntArray3822[var1] = this.anIntArray3822[var1] + 7 >> 4; - this.anIntArray3845[var1] = this.anIntArray3845[var1] + 7 >> 4; - this.anIntArray3848[var1] = this.anIntArray3848[var1] + 7 >> 4; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - final void method1931() { - int var1; - for (var1 = 0; var1 < this.anInt3823; ++var1) { - this.anIntArray3848[var1] = -this.anIntArray3848[var1]; - } - - if (this.aShortArray3837 != null) { - for (var1 = 0; var1 < this.anInt3833; ++var1) { - this.aShortArray3837[var1] = (short) (-this.aShortArray3837[var1]); - } - } - - for (var1 = 0; var1 < this.anInt3852; ++var1) { - short var2 = this.aShortArray3811[var1]; - this.aShortArray3811[var1] = this.aShortArray3831[var1]; - this.aShortArray3831[var1] = var2; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - if (this.aClass121_3815 != null) { - this.aClass121_3815.aBoolean1640 = false; - } - - this.aClass121_3846.aBoolean1640 = false; - } - - final void method1896(int var1) { - int var2 = Class51.anIntArray840[var1]; - int var3 = Class51.anIntArray851[var1]; - - for (int var4 = 0; var4 < this.anInt3823; ++var4) { - int var5 = this.anIntArray3845[var4] * var3 - this.anIntArray3848[var4] * var2 >> 16; - this.anIntArray3848[var4] = this.anIntArray3845[var4] * var2 + this.anIntArray3848[var4] * var3 >> 16; - this.anIntArray3845[var4] = var5; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - final void method1932() { - if (this.aShortArray3810 == null) { - this.method1876(256); - } else { - int var2 = Class51.anIntArray840[256]; - int var3 = Class51.anIntArray851[256]; - - int var4; - int var5; - for (var4 = 0; var4 < this.anInt3823; ++var4) { - var5 = this.anIntArray3848[var4] * var2 + this.anIntArray3822[var4] * var3 >> 16; - this.anIntArray3848[var4] = this.anIntArray3848[var4] * var3 - this.anIntArray3822[var4] * var2 >> 16; - this.anIntArray3822[var4] = var5; - } - - for (var4 = 0; var4 < this.anInt3833; ++var4) { - var5 = this.aShortArray3837[var4] * var2 + this.aShortArray3810[var4] * var3 >> 16; - this.aShortArray3837[var4] = (short) (this.aShortArray3837[var4] * var3 - this.aShortArray3810[var4] * var2 >> 16); - this.aShortArray3810[var4] = (short) var5; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - if (this.aClass121_3815 != null) { - this.aClass121_3815.aBoolean1640 = false; - } - - } - } - - final LDIndexedSprite method1933(LDIndexedSprite var1) { - if (this.anInt3833 == 0) { - return null; - } else { - if (!this.aClass6_3835.aBoolean98) { - this.method1917(); - } - - int var2; - int var3; - if (Class92.lightX > 0) { - var2 = this.aClass6_3835.aShort95 - (this.aClass6_3835.aShort92 * Class92.lightX >> 8) >> 3; - var3 = this.aClass6_3835.aShort94 - (this.aClass6_3835.aShort91 * Class92.lightX >> 8) >> 3; - } else { - var2 = this.aClass6_3835.aShort95 - (this.aClass6_3835.aShort91 * Class92.lightX >> 8) >> 3; - var3 = this.aClass6_3835.aShort94 - (this.aClass6_3835.aShort92 * Class92.lightX >> 8) >> 3; - } - - int var4; - int var5; - if (Class92.lightY > 0) { - var4 = this.aClass6_3835.aShort97 - (this.aClass6_3835.aShort92 * Class92.lightY >> 8) >> 3; - var5 = this.aClass6_3835.aShort96 - (this.aClass6_3835.aShort91 * Class92.lightY >> 8) >> 3; - } else { - var4 = this.aClass6_3835.aShort97 - (this.aClass6_3835.aShort91 * Class92.lightY >> 8) >> 3; - var5 = this.aClass6_3835.aShort96 - (this.aClass6_3835.aShort92 * Class92.lightY >> 8) >> 3; - } - - int var6 = var3 - var2 + 1; - int var7 = var5 - var4 + 1; - LDIndexedSprite var8; - if (var1 != null && var1.raster.length >= var6 * var7) { - var8 = var1; - var1.anInt1469 = var1.width = var6; - var1.anInt1467 = var1.height = var7; - var1.method1671(); - } else { - var8 = new LDIndexedSprite(var6, var7); - } - - var8.anInt1470 = var2; - var8.anInt1464 = var4; - if (anIntArray3861.length < this.anInt3833) { - anIntArray3861 = new int[this.anInt3833]; - anIntArray3857 = new int[this.anInt3833]; - } - - int var9 = 0; - - int var13; - int var14; - int var15; - while (var9 < this.anInt3823) { - int var10 = (this.anIntArray3822[var9] - (this.anIntArray3845[var9] * Class92.lightX >> 8) >> 3) - var2; - int var11 = (this.anIntArray3848[var9] - (this.anIntArray3845[var9] * Class92.lightY >> 8) >> 3) - var4; - int var12 = this.anIntArray3838[var9]; - var13 = this.anIntArray3838[var9 + 1]; - var14 = var12; - - while (true) { - if (var14 < var13) { - var15 = this.aShortArray3828[var14] - 1; - if (var15 != -1) { - anIntArray3861[var15] = var10; - anIntArray3857[var15] = var11; - ++var14; - continue; - } - } - - ++var9; - break; - } - } - - for (var9 = 0; var9 < this.anInt3852; ++var9) { - short var20 = this.aShortArray3811[var9]; - short var19 = this.aShortArray3830[var9]; - short var21 = this.aShortArray3831[var9]; - var13 = anIntArray3861[var20]; - var14 = anIntArray3861[var19]; - var15 = anIntArray3861[var21]; - int var16 = anIntArray3857[var20]; - int var17 = anIntArray3857[var19]; - int var18 = anIntArray3857[var21]; - if ((var13 - var14) * (var17 - var18) - (var17 - var16) * (var15 - var14) > 0) { - Class51.method1147(var8.raster, var16, var17, var18, var13, var14, var15, var6); - } - } - - return var8; - } - } - - final void method1900() { - for (int var1 = 0; var1 < this.anInt3823; ++var1) { - int var2 = this.anIntArray3848[var1]; - this.anIntArray3848[var1] = this.anIntArray3822[var1]; - this.anIntArray3822[var1] = -var2; - } - - this.aClass6_3835.aBoolean98 = false; - this.aClass121_3839.aBoolean1640 = false; - } - - private Class140_Sub1_Sub1() { - } - - Class140_Sub1_Sub1(Model_Sub1 var1, int var2, int var3, boolean var4) { - int[] var5 = new int[var1.anInt2849]; - this.anIntArray3838 = new int[var1.anInt2887 + 1]; - - for (int var6 = 0; var6 < var1.anInt2849; ++var6) { - if ((var1.aByteArray2859 == null || var1.aByteArray2859[var6] != 2) && (var1.aShortArray2858 == null || var1.aShortArray2858[var6] == -1 || !Class51.anInterface2_838.method12(var1.aShortArray2858[var6] & 65535, -65))) { - var5[this.anInt3852++] = var6; - ++this.anIntArray3838[var1.anIntArray2865[var6]]; - ++this.anIntArray3838[var1.anIntArray2878[var6]]; - ++this.anIntArray3838[var1.anIntArray2864[var6]]; - } - } - - long[] var60 = new long[this.anInt3852]; - - int var7; - int var8; - int var9; - int var62; - for (var7 = 0; var7 < this.anInt3852; ++var7) { - var8 = var5[var7]; - var9 = 0; - byte var10 = 0; - int var11 = 0; - int var12 = 0; - short var13 = -1; - if (var1.aShortArray2858 != null) { - var13 = var1.aShortArray2858[var8]; - if (var13 != -1) { - var11 = Class51.anInterface2_838.method18(var13 & 65535, 255); - var12 = Class51.anInterface2_838.method9(var13 & 65535, false); - } - } - - boolean var14 = var1.aByteArray2843 != null && var1.aByteArray2843[var8] != 0 || var13 != -1 && !Class51.anInterface2_838.method7((byte) 88, var13 & 65535); - if ((var4 || var14) && var1.aByteArray2889 != null) { - var9 += var1.aByteArray2889[var8] << 17; - } - - if (var14) { - var9 += 65536; - } - - var9 += (var11 & 0xFF) << 8; - var9 += var12 & 0xFF; - var62 = var10 + ((var13 & 65535) << 16); - var62 += var7 & 65535; - var60[var7] = ((long) var9 << 32) + (long) var62; - } - - Class102.method1614(true, var60, var5); - this.anInt3823 = var1.anInt2887; - this.anIntArray3822 = var1.anIntArray2885; - this.anIntArray3845 = var1.anIntArray2881; - this.anIntArray3848 = var1.anIntArray2892; - this.anIntArray3844 = var1.anIntArray2860; - this.aShortArray3832 = var1.aShortArray2893; - var7 = this.anInt3852 * 3; - this.aShortArray3810 = new short[var7]; - this.aShortArray3826 = new short[var7]; - this.aShortArray3837 = new short[var7]; - this.aShortArray3841 = new short[var7]; - this.aFloatArray3824 = new float[var7]; - this.aFloatArray3847 = new float[var7]; - this.aShortArray3808 = new short[this.anInt3852]; - this.aByteArray3816 = new byte[this.anInt3852]; - this.aShortArray3811 = new short[this.anInt3852]; - this.aShortArray3830 = new short[this.anInt3852]; - this.aShortArray3831 = new short[this.anInt3852]; - this.aShortArray3827 = new short[this.anInt3852]; - if (var1.anIntArray2847 != null) { - this.aByteArray3820 = new byte[this.anInt3852]; - } - - if (var1.aShortArray2855 != null) { - this.aShortArray3813 = new short[this.anInt3852]; - } - - this.aClass6_3835 = new Class6(); - this.aClass121_3839 = new Class121(); - this.aClass121_3814 = new Class121(); - if (Class106.aBoolean1441) { - this.aClass121_3815 = new Class121(); - } - - this.aClass121_3817 = new Class121(); - this.aClass121_3846 = new Class121(); - this.aShort3849 = (short) var2; - this.aShort3819 = (short) var3; - this.aShortArray3828 = new short[var7]; - aLongArray3821 = new long[var7]; - var8 = 0; - - for (var9 = 0; var9 < var1.anInt2887; ++var9) { - var62 = this.anIntArray3838[var9]; - this.anIntArray3838[var9] = var8; - var8 += var62; - } - - this.anIntArray3838[var1.anInt2887] = var8; - int[] var63 = null; - int[] var61 = null; - int[] var64 = null; - float[][] var65 = null; - int var27; - int var68; - float var79; - float var75; - float var82; - if (var1.aByteArray2866 != null) { - var68 = var1.anInt2862; - int[] var69 = new int[var68]; - int[] var15 = new int[var68]; - int[] var16 = new int[var68]; - int[] var17 = new int[var68]; - int[] var18 = new int[var68]; - int[] var19 = new int[var68]; - - int var20; - for (var20 = 0; var20 < var68; ++var20) { - var69[var20] = Integer.MAX_VALUE; - var15[var20] = -2147483647; - var16[var20] = Integer.MAX_VALUE; - var17[var20] = -2147483647; - var18[var20] = Integer.MAX_VALUE; - var19[var20] = -2147483647; - } - - for (var20 = 0; var20 < this.anInt3852; ++var20) { - int var21 = var5[var20]; - if (var1.aByteArray2866[var21] != -1) { - int var22 = var1.aByteArray2866[var21] & 0xFF; - - for (int var23 = 0; var23 < 3; ++var23) { - int var24; - if (var23 == 0) { - var24 = var1.anIntArray2865[var21]; - } else if (var23 == 1) { - var24 = var1.anIntArray2878[var21]; - } else { - var24 = var1.anIntArray2864[var21]; - } - - int var25 = var1.anIntArray2885[var24]; - int var26 = var1.anIntArray2881[var24]; - var27 = var1.anIntArray2892[var24]; - if (var25 < var69[var22]) { - var69[var22] = var25; - } - - if (var25 > var15[var22]) { - var15[var22] = var25; - } - - if (var26 < var16[var22]) { - var16[var22] = var26; - } - - if (var26 > var17[var22]) { - var17[var22] = var26; - } - - if (var27 < var18[var22]) { - var18[var22] = var27; - } - - if (var27 > var19[var22]) { - var19[var22] = var27; - } - } - } - } - - var63 = new int[var68]; - var61 = new int[var68]; - var64 = new int[var68]; - var65 = new float[var68][]; - - for (var20 = 0; var20 < var68; ++var20) { - byte var76 = var1.aByteArray2857[var20]; - if (var76 > 0) { - var63[var20] = (var69[var20] + var15[var20]) / 2; - var61[var20] = (var16[var20] + var17[var20]) / 2; - var64[var20] = (var18[var20] + var19[var20]) / 2; - if (var76 == 1) { - short var80 = var1.aShortArray2888[var20]; - if (var80 == 0) { - var75 = 1.0F; - var79 = 1.0F; - } else if (var80 > 0) { - var75 = 1.0F; - var79 = (float) var80 / 1024.0F; - } else { - var79 = 1.0F; - var75 = (float) (-var80) / 1024.0F; - } - - var82 = 64.0F / (float) (var1.aShortArray2882[var20] & 65535); - } else if (var76 == 2) { - var75 = 64.0F / (float) (var1.aShortArray2888[var20] & 65535); - var82 = 64.0F / (float) (var1.aShortArray2882[var20] & 65535); - var79 = 64.0F / (float) (var1.aShortArray2851[var20] & 65535); - } else { - var75 = (float) var1.aShortArray2888[var20] / 1024.0F; - var82 = (float) var1.aShortArray2882[var20] / 1024.0F; - var79 = (float) var1.aShortArray2851[var20] / 1024.0F; - } - - var65[var20] = method1906(var1.aShortArray2884[var20], var1.aShortArray2846[var20], var1.aShortArray2891[var20], var1.aByteArray2845[var20] & 0xFF, var75, var82, var79); - } - } - } - - short var71; - int var67; - for (var68 = 0; var68 < this.anInt3852; ++var68) { - int var70 = var5[var68]; - var67 = var1.aShortArray2870[var70] & 65535; - if (var1.aShortArray2858 == null) { - var71 = -1; - } else { - var71 = var1.aShortArray2858[var70]; - } - - int var72; - if (var1.aByteArray2866 == null) { - var72 = -1; - } else { - var72 = var1.aByteArray2866[var70]; - } - - int var74; - if (var1.aByteArray2843 == null) { - var74 = 0; - } else { - var74 = var1.aByteArray2843[var70] & 0xFF; - } - - float var73 = 0.0F; - float var78 = 0.0F; - float var77 = 0.0F; - var75 = 0.0F; - var82 = 0.0F; - var79 = 0.0F; - byte var81 = 0; - byte var83 = 0; - var27 = 0; - byte var28; - int var31; - int var87; - if (var71 != -1) { - if (var72 == -1) { - var73 = 0.0F; - var78 = 1.0F; - var77 = 1.0F; - var75 = 1.0F; - var81 = 1; - var82 = 0.0F; - var79 = 0.0F; - var83 = 2; - } else { - var72 &= 255; - var28 = var1.aByteArray2857[var72]; - int var29; - int var30; - float var38; - float var39; - float var37; - float var51; - float var50; - float var49; - float var54; - float var53; - float var52; - if (var28 == 0) { - var29 = var1.anIntArray2865[var70]; - var30 = var1.anIntArray2878[var70]; - var31 = var1.anIntArray2864[var70]; - short var32 = var1.aShortArray2884[var72]; - short var33 = var1.aShortArray2846[var72]; - short var34 = var1.aShortArray2891[var72]; - float var35 = (float) var1.anIntArray2885[var32]; - float var36 = (float) var1.anIntArray2881[var32]; - var37 = (float) var1.anIntArray2892[var32]; - var38 = (float) var1.anIntArray2885[var33] - var35; - var39 = (float) var1.anIntArray2881[var33] - var36; - float var40 = (float) var1.anIntArray2892[var33] - var37; - float var41 = (float) var1.anIntArray2885[var34] - var35; - float var42 = (float) var1.anIntArray2881[var34] - var36; - float var43 = (float) var1.anIntArray2892[var34] - var37; - float var44 = (float) var1.anIntArray2885[var29] - var35; - float var45 = (float) var1.anIntArray2881[var29] - var36; - float var46 = (float) var1.anIntArray2892[var29] - var37; - float var47 = (float) var1.anIntArray2885[var30] - var35; - float var48 = (float) var1.anIntArray2881[var30] - var36; - var49 = (float) var1.anIntArray2892[var30] - var37; - var50 = (float) var1.anIntArray2885[var31] - var35; - var51 = (float) var1.anIntArray2881[var31] - var36; - var52 = (float) var1.anIntArray2892[var31] - var37; - var53 = var39 * var43 - var40 * var42; - var54 = var40 * var41 - var38 * var43; - float var55 = var38 * var42 - var39 * var41; - float var56 = var42 * var55 - var43 * var54; - float var57 = var43 * var53 - var41 * var55; - float var58 = var41 * var54 - var42 * var53; - float var59 = 1.0F / (var56 * var38 + var57 * var39 + var58 * var40); - var73 = (var56 * var44 + var57 * var45 + var58 * var46) * var59; - var77 = (var56 * var47 + var57 * var48 + var58 * var49) * var59; - var82 = (var56 * var50 + var57 * var51 + var58 * var52) * var59; - var56 = var39 * var55 - var40 * var54; - var57 = var40 * var53 - var38 * var55; - var58 = var38 * var54 - var39 * var53; - var59 = 1.0F / (var56 * var41 + var57 * var42 + var58 * var43); - var78 = (var56 * var44 + var57 * var45 + var58 * var46) * var59; - var75 = (var56 * var47 + var57 * var48 + var58 * var49) * var59; - var79 = (var56 * var50 + var57 * var51 + var58 * var52) * var59; - } else { - var29 = var1.anIntArray2865[var70]; - var30 = var1.anIntArray2878[var70]; - var31 = var1.anIntArray2864[var70]; - int var88 = var63[var72]; - var87 = var61[var72]; - int var94 = var64[var72]; - float[] var93 = var65[var72]; - byte var90 = var1.aByteArray2867[var72]; - var37 = (float) var1.aByteArray2877[var72] / 256.0F; - if (var28 == 1) { - var38 = (float) (var1.aShortArray2851[var72] & 65535) / 1024.0F; - method1904(var1.anIntArray2885[var29], var1.anIntArray2881[var29], var1.anIntArray2892[var29], var88, var87, var94, var93, var38, var90, var37); - var73 = aFloat3863; - var78 = aFloat3866; - method1904(var1.anIntArray2885[var30], var1.anIntArray2881[var30], var1.anIntArray2892[var30], var88, var87, var94, var93, var38, var90, var37); - var77 = aFloat3863; - var75 = aFloat3866; - method1904(var1.anIntArray2885[var31], var1.anIntArray2881[var31], var1.anIntArray2892[var31], var88, var87, var94, var93, var38, var90, var37); - var82 = aFloat3863; - var79 = aFloat3866; - var39 = var38 / 2.0F; - if ((var90 & 1) == 0) { - if (var77 - var73 > var39) { - var77 -= var38; - var81 = 1; - } else if (var73 - var77 > var39) { - var77 += var38; - var81 = 2; - } - - if (var82 - var73 > var39) { - var82 -= var38; - var83 = 1; - } else if (var73 - var82 > var39) { - var82 += var38; - var83 = 2; - } - } else { - if (var75 - var78 > var39) { - var75 -= var38; - var81 = 1; - } else if (var78 - var75 > var39) { - var75 += var38; - var81 = 2; - } - - if (var79 - var78 > var39) { - var79 -= var38; - var83 = 1; - } else if (var78 - var79 > var39) { - var79 += var38; - var83 = 2; - } - } - } else if (var28 == 2) { - var38 = (float) var1.aByteArray2852[var72] / 256.0F; - var39 = (float) var1.aByteArray2869[var72] / 256.0F; - int var97 = var1.anIntArray2885[var30] - var1.anIntArray2885[var29]; - int var96 = var1.anIntArray2881[var30] - var1.anIntArray2881[var29]; - int var101 = var1.anIntArray2892[var30] - var1.anIntArray2892[var29]; - int var102 = var1.anIntArray2885[var31] - var1.anIntArray2885[var29]; - int var103 = var1.anIntArray2881[var31] - var1.anIntArray2881[var29]; - int var98 = var1.anIntArray2892[var31] - var1.anIntArray2892[var29]; - int var99 = var96 * var98 - var103 * var101; - int var100 = var101 * var102 - var98 * var97; - int var104 = var97 * var103 - var102 * var96; - var49 = 64.0F / (float) (var1.aShortArray2888[var72] & 65535); - var50 = 64.0F / (float) (var1.aShortArray2882[var72] & 65535); - var51 = 64.0F / (float) (var1.aShortArray2851[var72] & 65535); - var52 = ((float) var99 * var93[0] + (float) var100 * var93[1] + (float) var104 * var93[2]) / var49; - var53 = ((float) var99 * var93[3] + (float) var100 * var93[4] + (float) var104 * var93[5]) / var50; - var54 = ((float) var99 * var93[6] + (float) var100 * var93[7] + (float) var104 * var93[8]) / var51; - var27 = method1901(var52, var53, var54); - method1913(var1.anIntArray2885[var29], var1.anIntArray2881[var29], var1.anIntArray2892[var29], var88, var87, var94, var27, var93, var90, var37, var38, var39); - var73 = aFloat3864; - var78 = aFloat3858; - method1913(var1.anIntArray2885[var30], var1.anIntArray2881[var30], var1.anIntArray2892[var30], var88, var87, var94, var27, var93, var90, var37, var38, var39); - var77 = aFloat3864; - var75 = aFloat3858; - method1913(var1.anIntArray2885[var31], var1.anIntArray2881[var31], var1.anIntArray2892[var31], var88, var87, var94, var27, var93, var90, var37, var38, var39); - var82 = aFloat3864; - var79 = aFloat3858; - } else if (var28 == 3) { - method1910(var1.anIntArray2885[var29], var1.anIntArray2881[var29], var1.anIntArray2892[var29], var88, var87, var94, var93, var90, var37); - var73 = aFloat3867; - var78 = aFloat3860; - method1910(var1.anIntArray2885[var30], var1.anIntArray2881[var30], var1.anIntArray2892[var30], var88, var87, var94, var93, var90, var37); - var77 = aFloat3867; - var75 = aFloat3860; - method1910(var1.anIntArray2885[var31], var1.anIntArray2881[var31], var1.anIntArray2892[var31], var88, var87, var94, var93, var90, var37); - var82 = aFloat3867; - var79 = aFloat3860; - if ((var90 & 1) == 0) { - if (var77 - var73 > 0.5F) { - --var77; - var81 = 1; - } else if (var73 - var77 > 0.5F) { - ++var77; - var81 = 2; - } - - if (var82 - var73 > 0.5F) { - --var82; - var83 = 1; - } else if (var73 - var82 > 0.5F) { - ++var82; - var83 = 2; - } - } else { - if (var75 - var78 > 0.5F) { - --var75; - var81 = 1; - } else if (var78 - var75 > 0.5F) { - ++var75; - var81 = 2; - } - - if (var79 - var78 > 0.5F) { - --var79; - var83 = 1; - } else if (var78 - var79 > 0.5F) { - ++var79; - var83 = 2; - } - } - } - } - } - } - - var1.method1997(); - if (var1.aByteArray2859 == null) { - var28 = 0; - } else { - var28 = var1.aByteArray2859[var70]; - } - - if (var28 == 0) { - long var86 = (long) (var72 << 2) + ((long) (var27 << 24) + (long) (var67 << 8) + (long) var74 << 32); - var31 = var1.anIntArray2865[var70]; - Class50 var89 = var1.aClass50Array2872[var31]; - this.aShortArray3811[var68] = this.method1907(var31, var86, var89.anInt831, var89.anInt821, var89.anInt830, var89.anInt823, var73, var78); - var87 = var1.anIntArray2878[var70]; - Class50 var95 = var1.aClass50Array2872[var87]; - this.aShortArray3830[var68] = this.method1907(var87, var86 + (long) var81, var95.anInt831, var95.anInt821, var95.anInt830, var95.anInt823, var77, var75); - int var92 = var1.anIntArray2864[var70]; - Class50 var91 = var1.aClass50Array2872[var92]; - this.aShortArray3831[var68] = this.method1907(var92, var86 + (long) var83, var91.anInt831, var91.anInt821, var91.anInt830, var91.anInt823, var82, var79); - } else if (var28 == 1) { - Class120 var85 = var1.aClass120Array2886[var70]; - long var84 = (long) ((var72 << 2) + (var85.anInt1634 > 0 ? 1024 : 2048) + (var85.anInt1635 + 256 << 12) + (var85.anInt1632 + 256 << 22)) + ((long) (var27 << 24) + (long) (var67 << 8) + (long) var74 << 32); - this.aShortArray3811[var68] = this.method1907(var1.anIntArray2865[var70], var84, var85.anInt1634, var85.anInt1635, var85.anInt1632, 0, var73, var78); - this.aShortArray3830[var68] = this.method1907(var1.anIntArray2878[var70], var84 + (long) var81, var85.anInt1634, var85.anInt1635, var85.anInt1632, 0, var77, var75); - this.aShortArray3831[var68] = this.method1907(var1.anIntArray2864[var70], var84 + (long) var83, var85.anInt1634, var85.anInt1635, var85.anInt1632, 0, var82, var79); - } - - if (var1.aShortArray2858 == null) { - this.aShortArray3827[var68] = -1; - } else { - this.aShortArray3827[var68] = var1.aShortArray2858[var70]; - } - - if (this.aByteArray3820 != null) { - this.aByteArray3820[var68] = (byte) var1.anIntArray2847[var70]; - } - - this.aShortArray3808[var68] = var1.aShortArray2870[var70]; - if (var1.aByteArray2843 != null) { - this.aByteArray3816[var68] = var1.aByteArray2843[var70]; - } - - if (var1.aShortArray2855 != null) { - this.aShortArray3813[var68] = var1.aShortArray2855[var70]; - } - } - - var68 = 0; - short var66 = -10000; - - for (var67 = 0; var67 < this.anInt3852; ++var67) { - var71 = this.aShortArray3827[var67]; - if (var71 != var66) { - ++var68; - var66 = var71; - } - } - - this.anIntArray3840 = new int[var68 + 1]; - var68 = 0; - var66 = -10000; - - for (var67 = 0; var67 < this.anInt3852; ++var67) { - var71 = this.aShortArray3827[var67]; - if (var71 != var66) { - this.anIntArray3840[var68++] = var67; - var66 = var71; - } - } - - this.anIntArray3840[var68] = this.anInt3852; - aLongArray3821 = null; - this.aShortArray3810 = method1928(this.aShortArray3810, this.anInt3833); - this.aShortArray3826 = method1928(this.aShortArray3826, this.anInt3833); - this.aShortArray3837 = method1928(this.aShortArray3837, this.anInt3833); - this.aShortArray3841 = method1928(this.aShortArray3841, this.anInt3833); - this.aFloatArray3824 = method1921(this.aFloatArray3824, this.anInt3833); - this.aFloatArray3847 = method1921(this.aFloatArray3847, this.anInt3833); - } - -} diff --git a/Client/src/main/java/org/runite/client/Class140_Sub1_Sub2.java b/Client/src/main/java/org/runite/client/Class140_Sub1_Sub2.java deleted file mode 100644 index 057f08837..000000000 --- a/Client/src/main/java/org/runite/client/Class140_Sub1_Sub2.java +++ /dev/null @@ -1,2542 +0,0 @@ -package org.runite.client; - -final class Class140_Sub1_Sub2 extends Model { - - private short[] aShortArray3869; - private int[][] anIntArrayArray3870; - private short[] aShortArray3871; - private int[][] anIntArrayArray3872; - private short aShort3873; - private int[] anIntArray3874; - private byte aByte3875 = 0; - private int[] anIntArray3876; - private boolean aBoolean3877 = false; - private short aShort3878; - private short aShort3879; - private byte[] aByteArray3880; - private int[] anIntArray3881; - private int[] anIntArray3882; - int[] anIntArray3883; - private short aShort3884; - int[] anIntArray3885; - private short aShort3886; - private int[] anIntArray3887; - private short aShort3888; - private int anInt3889 = 0; - private int[] anIntArray3890; - int anInt3891 = 0; - private short aShort3892; - private short[] aShortArray3893; - private short aShort3894; - int[] anIntArray3895; - private int[] anIntArray3896; - boolean aBoolean3897 = false; - private int[] anIntArray3898; - private byte[] aByteArray3899; - private int anInt3900 = 0; - private int[] anIntArray3901; - private static final Class140_Sub1_Sub2 aClass140_Sub1_Sub2_3902 = new Class140_Sub1_Sub2(); - private byte[] aByteArray3903; - private static int[] anIntArray3904 = new int[1]; - private static int[] anIntArray3905 = new int[1]; - private static final Class140_Sub1_Sub2 aClass140_Sub1_Sub2_3906 = new Class140_Sub1_Sub2(); - private static byte[] aByteArray3907 = new byte[1]; - private short[] aShortArray3908; - private static int[] anIntArray3909 = new int[1]; - private static short[] aShortArray3910 = new short[1]; - private static int[] anIntArray3911 = new int[1]; - private static short[] aShortArray3912 = new short[1]; - private static int[] anIntArray3913 = new int[1]; - private static byte[] aByteArray3914 = new byte[1]; - private static final Class140_Sub1_Sub2 aClass140_Sub1_Sub2_3915 = new Class140_Sub1_Sub2(); - private static int[] anIntArray3916 = new int[1]; - private static final boolean[] aBooleanArray3917 = new boolean[4096]; - private static final int[] anIntArray3918 = new int[12]; - private static final int[] anIntArray3919 = new int[10]; - private static final int[] anIntArray3920 = new int[4096]; - private static final int[] anIntArray3921; - private static final int[] anIntArray3922 = new int[4096]; - private static int[][] anIntArrayArray3923; - private static int[] anIntArray3924; - private static final int[] anIntArray3925 = new int[10]; - private static int[][] anIntArrayArray3926; - private static boolean aBoolean3927 = false; - private static final int[] anIntArray3928; - private static int[] anIntArray3929; - private static int anInt3930 = 0; - private static int[] anIntArray3931; - private static final int[] anIntArray3932 = new int[4096]; - private static byte[] aByteArray3933 = new byte[1]; - private static int anInt3934; - private static int[] anIntArray3935; - private static final int[] anIntArray3936 = new int[10]; - private static final boolean[] aBooleanArray3937 = new boolean[4096]; - private static final int[][] anIntArrayArray3938; - private static int[] anIntArray3939; - private static int anInt3940; - private static final int[] anIntArray3941 = new int[4096]; - private static boolean aBoolean3942 = false; - private static final int[] anIntArray3943; - private static final int[] anIntArray3944 = new int[12]; - private static int anInt3945; - private static int[] anIntArray3946; - private static int[] anIntArray3947 = new int[1]; - private static final int[] anIntArray3948 = new int[4096]; - private static short[] aShortArray3949; - private static final int[] anIntArray3950; - - - private void method1934(int var1) { - if(aBooleanArray3917[var1]) { - this.method1942(var1); - } else { - int var2 = this.anIntArray3901[var1]; - int var3 = this.anIntArray3876[var1]; - int var4 = this.anIntArray3887[var1]; - Class51.aBoolean849 = aBooleanArray3937[var1]; - if(this.aByteArray3903 == null) { - Class51.anInt850 = 0; - } else { - Class51.anInt850 = this.aByteArray3903[var1] & 0xFF; - } - - if(this.aShortArray3908 != null && this.aShortArray3908[var1] != -1) { - int var5; - int var6; - int var7; - if(this.aByteArray3899 == null || this.aByteArray3899[var1] == -1) { - var5 = var2; - var6 = var3; - var7 = var4; - } else { - int var8 = this.aByteArray3899[var1] & 0xFF; - var5 = this.anIntArray3882[var8]; - var6 = this.anIntArray3890[var8]; - var7 = this.anIntArray3881[var8]; - } - - if(this.anIntArray3896[var1] == -1) { - Class51.method1138(anIntArray3932[var2], anIntArray3932[var3], anIntArray3932[var4], anIntArray3943[var2], anIntArray3943[var3], anIntArray3943[var4], this.anIntArray3898[var1], this.anIntArray3898[var1], this.anIntArray3898[var1], anIntArray3948[var5], anIntArray3948[var6], anIntArray3948[var7], anIntArray3928[var5], anIntArray3928[var6], anIntArray3928[var7], anIntArray3921[var5], anIntArray3921[var6], anIntArray3921[var7], this.aShortArray3908[var1]); - } else { - Class51.method1138(anIntArray3932[var2], anIntArray3932[var3], anIntArray3932[var4], anIntArray3943[var2], anIntArray3943[var3], anIntArray3943[var4], this.anIntArray3898[var1], this.anIntArray3874[var1], this.anIntArray3896[var1], anIntArray3948[var5], anIntArray3948[var6], anIntArray3948[var7], anIntArray3928[var5], anIntArray3928[var6], anIntArray3928[var7], anIntArray3921[var5], anIntArray3921[var6], anIntArray3921[var7], this.aShortArray3908[var1]); - } - } else if(this.anIntArray3896[var1] == -1) { - Class51.method1144(anIntArray3932[var2], anIntArray3932[var3], anIntArray3932[var4], anIntArray3943[var2], anIntArray3943[var3], anIntArray3943[var4], Class51.anIntArray834[this.anIntArray3898[var1] & 65535]); - } else { - Class51.method1154(anIntArray3932[var2], anIntArray3932[var3], anIntArray3932[var4], anIntArray3943[var2], anIntArray3943[var3], anIntArray3943[var4], this.anIntArray3898[var1] & 65535, this.anIntArray3874[var1] & 65535, this.anIntArray3896[var1] & 65535); - } - - } - } - - final void method1874() { - for(int var1 = 0; var1 < this.anInt3891; ++var1) { - this.anIntArray3885[var1] = -this.anIntArray3885[var1]; - this.anIntArray3895[var1] = -this.anIntArray3895[var1]; - } - - this.aBoolean3897 = false; - } - - static void method1935() { - aBoolean3942 = true; - anIntArray3931 = new int[4096]; - anIntArray3929 = new int[4096]; - anIntArray3946 = null; - anIntArrayArray3926 = null; - anIntArray3935 = null; - anIntArrayArray3923 = null; - } - - private void method1936() { - for(int var4 = 0; var4 < this.anInt3889; ++var4) { - short var3 = this.aShortArray3908 != null?this.aShortArray3908[var4]:-1; - if(var3 == -1) { - int var1 = this.aShortArray3869[var4] & 65535; - int var2; - if(this.anIntArray3896[var4] == -1) { - var2 = this.anIntArray3898[var4] & -131072; - this.anIntArray3898[var4] = var2 | method1940(var1, var2 >> 17); - } else if(this.anIntArray3896[var4] != -2) { - var2 = this.anIntArray3898[var4] & -131072; - this.anIntArray3898[var4] = var2 | method1940(var1, var2 >> 17); - var2 = this.anIntArray3874[var4] & -131072; - this.anIntArray3874[var4] = var2 | method1940(var1, var2 >> 17); - var2 = this.anIntArray3896[var4] & -131072; - this.anIntArray3896[var4] = var2 | method1940(var1, var2 >> 17); - } - } - } - - } - - private static int method1937(int var0) { - if(var0 < 2) { - var0 = 2; - } else if(var0 > 126) { - var0 = 126; - } - - return var0; - } - - static void method1938() { - aBoolean3942 = false; - anIntArray3931 = null; - anIntArray3929 = null; - anIntArray3946 = new int[1600]; - anIntArrayArray3926 = new int[1600][64]; - anIntArray3935 = new int[32]; - anIntArrayArray3923 = new int[32][512]; - } - - final int method1898() { - if(!this.aBoolean3897) { - this.method1947(); - } - - return this.aShort3879; - } - - final int method1871() { - if(!this.aBoolean3897) { - this.method1947(); - } - - return this.aShort3894; - } - - private Model method1939(boolean var1, boolean var2, Class140_Sub1_Sub2 var3, byte[] var4, short[] var5, int[] var6, int[] var7, int[] var8) { - var3.anInt3891 = this.anInt3891; - var3.anInt3889 = this.anInt3889; - var3.anInt3900 = this.anInt3900; - if(var3.anIntArray3885 == null || var3.anIntArray3885.length < this.anInt3891) { - var3.anIntArray3885 = new int[this.anInt3891 + 100]; - var3.anIntArray3883 = new int[this.anInt3891 + 100]; - var3.anIntArray3895 = new int[this.anInt3891 + 100]; - } - - int var9; - for(var9 = 0; var9 < this.anInt3891; ++var9) { - var3.anIntArray3885[var9] = this.anIntArray3885[var9]; - var3.anIntArray3883[var9] = this.anIntArray3883[var9]; - var3.anIntArray3895[var9] = this.anIntArray3895[var9]; - } - - if(var1) { - var3.aByteArray3903 = this.aByteArray3903; - } else { - var3.aByteArray3903 = var4; - if(this.aByteArray3903 == null) { - for(var9 = 0; var9 < this.anInt3889; ++var9) { - var3.aByteArray3903[var9] = 0; - } - } else { - for(var9 = 0; var9 < this.anInt3889; ++var9) { - var3.aByteArray3903[var9] = this.aByteArray3903[var9]; - } - } - } - - if(var2) { - var3.aShortArray3869 = this.aShortArray3869; - var3.anIntArray3898 = this.anIntArray3898; - var3.anIntArray3874 = this.anIntArray3874; - var3.anIntArray3896 = this.anIntArray3896; - } else { - var3.aShortArray3869 = var5; - var3.anIntArray3898 = var6; - var3.anIntArray3874 = var7; - var3.anIntArray3896 = var8; - - for(var9 = 0; var9 < this.anInt3889; ++var9) { - var3.aShortArray3869[var9] = this.aShortArray3869[var9]; - var3.anIntArray3898[var9] = this.anIntArray3898[var9]; - var3.anIntArray3874[var9] = this.anIntArray3874[var9]; - var3.anIntArray3896[var9] = this.anIntArray3896[var9]; - } - } - - var3.anIntArray3901 = this.anIntArray3901; - var3.anIntArray3876 = this.anIntArray3876; - var3.anIntArray3887 = this.anIntArray3887; - var3.aByteArray3880 = this.aByteArray3880; - var3.aByteArray3899 = this.aByteArray3899; - var3.aShortArray3908 = this.aShortArray3908; - var3.aByte3875 = this.aByte3875; - var3.anIntArray3882 = this.anIntArray3882; - var3.anIntArray3890 = this.anIntArray3890; - var3.anIntArray3881 = this.anIntArray3881; - var3.anIntArrayArray3870 = this.anIntArrayArray3870; - var3.anIntArrayArray3872 = this.anIntArrayArray3872; - var3.aShortArray3893 = this.aShortArray3893; - var3.aShortArray3871 = this.aShortArray3871; - var3.aBoolean2699 = this.aBoolean2699; - var3.aBoolean3897 = false; - return var3; - } - - final void method1896(int var1) { - int var2 = Class51.anIntArray840[var1]; - int var3 = Class51.anIntArray851[var1]; - - for(int var4 = 0; var4 < this.anInt3891; ++var4) { - int var5 = this.anIntArray3883[var4] * var3 - this.anIntArray3895[var4] * var2 >> 16; - this.anIntArray3895[var4] = this.anIntArray3883[var4] * var2 + this.anIntArray3895[var4] * var3 >> 16; - this.anIntArray3883[var4] = var5; - } - - this.aBoolean3897 = false; - } - - final boolean method1873() { - if(this.anIntArrayArray3870 == null) { - return false; - } else { - anInt3945 = 0; - anInt3934 = 0; - anInt3940 = 0; - return true; - } - } - - static int method1940(int var0, int var1) { - var1 = var1 * (var0 & 127) >> 7; - if(var1 < 2) { - var1 = 2; - } else if(var1 > 126) { - var1 = 126; - } - - return (var0 & '\uff80') + var1; - } - - final void method1886(int var1) { - int var2 = Class51.anIntArray840[var1]; - int var3 = Class51.anIntArray851[var1]; - - for(int var4 = 0; var4 < this.anInt3891; ++var4) { - int var5 = this.anIntArray3883[var4] * var2 + this.anIntArray3885[var4] * var3 >> 16; - this.anIntArray3883[var4] = this.anIntArray3883[var4] * var3 - this.anIntArray3885[var4] * var2 >> 16; - this.anIntArray3885[var4] = var5; - } - - this.aBoolean3897 = false; - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - if(!this.aBoolean3897) { - this.method1947(); - } - - int var15 = var8 * var5 - var6 * var4 >> 16; - int var16 = var7 * var2 + var15 * var3 >> 16; - int var13 = var16 + (this.aShort3884 * var3 + this.aShort3892 * var2 >> 16); - int var14 = var16 + (-this.aShort3884 * var3 + this.aShort3894 * var2 >> 16); - if(var13 > 50) { - if(var14 < 3500) { - int var17 = var8 * var4 + var6 * var5 >> 16; - int var18 = var17 + this.aShort3884 << 9; - if(var18 / var13 > Class139.screenLowerX) { - int var19 = var17 - this.aShort3884 << 9; - if(var19 / var13 < Class145.screenUpperX) { - int var20 = var7 * var3 - var15 * var2 >> 16; - int var21 = var20 + (this.aShort3884 * var2 + this.aShort3892 * var3 >> 16) << 9; - if(var21 / var13 > Class1.screenUpperY) { - int var22 = var20 + (-this.aShort3884 * var2 + this.aShort3894 * var3 >> 16) << 9; - if(var22 / var13 < AtmosphereParser.screenLowerY) { - boolean var23 = false; - boolean var24 = var14 <= 50; - boolean var25 = var24 || this.anInt3900 > 0; - int var26 = Class51.anInt846; - int var27 = Class51.anInt835; - int var28 = 0; - int var29 = 0; - if(var1 != 0) { - var28 = Class51.anIntArray840[var1]; - var29 = Class51.anIntArray851[var1]; - } - - boolean var30 = false; - int var31; - int var34; - int var32; - int var33; - if(var9 > 0L && TextureOperation36.aBoolean3094 && var14 > 0) { - if(var17 > 0) { - var31 = var19 / var13; - var33 = var18 / var14; - } else { - var31 = var19 / var14; - var33 = var18 / var13; - } - - if(var20 > 0) { - var32 = var22 / var13; - var34 = var21 / var14; - } else { - var32 = var22 / var14; - var34 = var21 / var13; - } - - if(Unsorted.anInt3642 >= var31 && Unsorted.anInt3642 <= var33 && RenderAnimationDefinition.anInt384 >= var32 && RenderAnimationDefinition.anInt384 <= var34) { - var31 = 999999; - var33 = -999999; - var32 = 999999; - var34 = -999999; - int[] var35 = new int[]{this.aShort3873, this.aShort3878, this.aShort3873, this.aShort3878, this.aShort3873, this.aShort3878, this.aShort3873, this.aShort3878}; - int[] var36 = new int[]{this.aShort3879, this.aShort3879, this.aShort3888, this.aShort3888, this.aShort3879, this.aShort3879, this.aShort3888, this.aShort3888}; - int[] var37 = new int[]{this.aShort3894, this.aShort3894, this.aShort3894, this.aShort3894, this.aShort3892, this.aShort3892, this.aShort3892, this.aShort3892}; - - for(int var38 = 0; var38 < 8; ++var38) { - int var39 = var35[var38]; - int var40 = var37[var38]; - int var41 = var36[var38]; - int var42; - if(var1 != 0) { - var42 = var41 * var28 + var39 * var29 >> 16; - var41 = var41 * var29 - var39 * var28 >> 16; - var39 = var42; - } - - var39 += var6; - var40 += var7; - var41 += var8; - var42 = var41 * var4 + var39 * var5 >> 16; - var41 = var41 * var5 - var39 * var4 >> 16; - var39 = var42; - var42 = var40 * var3 - var41 * var2 >> 16; - var41 = var40 * var2 + var41 * var3 >> 16; - if(var41 > 0) { - int var43 = (var39 << 9) / var41; - int var44 = (var42 << 9) / var41; - if(var43 < var31) { - var31 = var43; - } - - if(var43 > var33) { - var33 = var43; - } - - if(var44 < var32) { - var32 = var44; - } - - if(var44 > var34) { - var34 = var44; - } - } - } - - if(Unsorted.anInt3642 >= var31 && Unsorted.anInt3642 <= var33 && RenderAnimationDefinition.anInt384 >= var32 && RenderAnimationDefinition.anInt384 <= var34) { - if(this.aBoolean2699) { - TextureOperation38.aLongArray3448[Unsorted.anInt59++] = var9; - } else { - var30 = true; - } - } - } - } - - for(var31 = 0; var31 < this.anInt3891; ++var31) { - var32 = this.anIntArray3885[var31]; - var33 = this.anIntArray3883[var31]; - var34 = this.anIntArray3895[var31]; - int var46; - if(var1 != 0) { - var46 = var34 * var28 + var32 * var29 >> 16; - var34 = var34 * var29 - var32 * var28 >> 16; - var32 = var46; - } - - var32 += var6; - var33 += var7; - var34 += var8; - var46 = var34 * var4 + var32 * var5 >> 16; - var34 = var34 * var5 - var32 * var4 >> 16; - var32 = var46; - var46 = var33 * var3 - var34 * var2 >> 16; - var34 = var33 * var2 + var34 * var3 >> 16; - anIntArray3920[var31] = var34 - var16; - if(var34 >= 50) { - anIntArray3943[var31] = var26 + (var32 << 9) / var34; - anIntArray3932[var31] = var27 + (var46 << 9) / var34; - } else { - anIntArray3943[var31] = -5000; - var23 = true; - } - - if(var25) { - anIntArray3948[var31] = var32; - anIntArray3928[var31] = var46; - anIntArray3921[var31] = var34; - } - } - - try { - this.method1945(var23, var30, var9, var16 - var14, var13 - var14 + 2); - } catch (Exception var45) { - } - - } - } - } - } - } - } - } - - final Class140_Sub1_Sub2 method1941(int var1, int var2, int[][] var3, int[][] var4, int var5, int var6, int var7, boolean var8) { - if(!this.aBoolean3897) { - this.method1947(); - } - - int var9 = var5 + this.aShort3873; - int var10 = var5 + this.aShort3878; - int var11 = var7 + this.aShort3879; - int var12 = var7 + this.aShort3888; - if((var1 == 1 || var1 == 2 || var1 == 3 || var1 == 5) && (var9 < 0 || var10 + 128 >> 7 >= var3.length || var11 < 0 || var12 + 128 >> 7 >= var3[0].length)) { - return this; - } else { - if(var1 == 4 || var1 == 5) { - if(var4 == null) { - return this; - } - - if(var9 < 0 || var10 + 128 >> 7 >= var4.length || var11 < 0 || var12 + 128 >> 7 >= var4[0].length) { - return this; - } - } else { - var9 >>= 7; - var10 = var10 + 127 >> 7; - var11 >>= 7; - var12 = var12 + 127 >> 7; - if(var3[var9][var11] == var6 && var3[var10][var11] == var6 && var3[var9][var12] == var6 && var3[var10][var12] == var6) { - return this; - } - } - - Class140_Sub1_Sub2 var13; - if(var8) { - var13 = new Class140_Sub1_Sub2(); - var13.anInt3891 = this.anInt3891; - var13.anInt3889 = this.anInt3889; - var13.anInt3900 = this.anInt3900; - var13.anIntArray3901 = this.anIntArray3901; - var13.anIntArray3876 = this.anIntArray3876; - var13.anIntArray3887 = this.anIntArray3887; - var13.anIntArray3898 = this.anIntArray3898; - var13.anIntArray3874 = this.anIntArray3874; - var13.anIntArray3896 = this.anIntArray3896; - var13.aByteArray3880 = this.aByteArray3880; - var13.aByteArray3899 = this.aByteArray3899; - var13.aShortArray3908 = this.aShortArray3908; - var13.aShortArray3869 = this.aShortArray3869; - var13.aByteArray3903 = this.aByteArray3903; - var13.aByte3875 = this.aByte3875; - var13.anIntArray3882 = this.anIntArray3882; - var13.anIntArray3890 = this.anIntArray3890; - var13.anIntArray3881 = this.anIntArray3881; - var13.anIntArrayArray3870 = this.anIntArrayArray3870; - var13.anIntArrayArray3872 = this.anIntArrayArray3872; - var13.aShortArray3893 = this.aShortArray3893; - var13.aShortArray3871 = this.aShortArray3871; - var13.aBoolean2699 = this.aBoolean2699; - if(var1 == 3) { - var13.anIntArray3885 = Class65.method1233(this.anIntArray3885); - var13.anIntArray3883 = Class65.method1233(this.anIntArray3883); - var13.anIntArray3895 = Class65.method1233(this.anIntArray3895); - } else { - var13.anIntArray3885 = this.anIntArray3885; - var13.anIntArray3883 = new int[var13.anInt3891]; - var13.anIntArray3895 = this.anIntArray3895; - } - } else { - var13 = this; - } - - int var14; - int var15; - int var17; - int var16; - int var19; - int var18; - int var21; - int var20; - int var23; - int var22; - if(var1 == 1) { - for(var14 = 0; var14 < var13.anInt3891; ++var14) { - var15 = this.anIntArray3885[var14] + var5; - var16 = this.anIntArray3895[var14] + var7; - var17 = var15 & 127; - var18 = var16 & 127; - var19 = var15 >> 7; - var20 = var16 >> 7; - var21 = var3[var19][var20] * (128 - var17) + var3[var19 + 1][var20] * var17 >> 7; - var22 = var3[var19][var20 + 1] * (128 - var17) + var3[var19 + 1][var20 + 1] * var17 >> 7; - var23 = var21 * (128 - var18) + var22 * var18 >> 7; - var13.anIntArray3883[var14] = this.anIntArray3883[var14] + var23 - var6; - } - } else { - int var24; - if(var1 == 2) { - for(var14 = 0; var14 < var13.anInt3891; ++var14) { - var15 = (this.anIntArray3883[var14] << 16) / this.aShort3894; - if(var15 < var2) { - var16 = this.anIntArray3885[var14] + var5; - var17 = this.anIntArray3895[var14] + var7; - var18 = var16 & 127; - var19 = var17 & 127; - var20 = var16 >> 7; - var21 = var17 >> 7; - var22 = var3[var20][var21] * (128 - var18) + var3[var20 + 1][var21] * var18 >> 7; - var23 = var3[var20][var21 + 1] * (128 - var18) + var3[var20 + 1][var21 + 1] * var18 >> 7; - var24 = var22 * (128 - var19) + var23 * var19 >> 7; - var13.anIntArray3883[var14] = this.anIntArray3883[var14] + (var24 - var6) * (var2 - var15) / var2; - } else { - var13.anIntArray3883[var14] = this.anIntArray3883[var14]; - } - } - } else if(var1 == 3) { - var14 = (var2 & 0xFF) * 4; - var15 = (var2 >> 8 & 0xFF) * 4; - var13.method1895(var3, var5, var6, var7, var14, var15); - } else if(var1 == 4) { - var14 = this.aShort3892 - this.aShort3894; - - for(var15 = 0; var15 < this.anInt3891; ++var15) { - var16 = this.anIntArray3885[var15] + var5; - var17 = this.anIntArray3895[var15] + var7; - var18 = var16 & 127; - var19 = var17 & 127; - var20 = var16 >> 7; - var21 = var17 >> 7; - var22 = var4[var20][var21] * (128 - var18) + var4[var20 + 1][var21] * var18 >> 7; - var23 = var4[var20][var21 + 1] * (128 - var18) + var4[var20 + 1][var21 + 1] * var18 >> 7; - var24 = var22 * (128 - var19) + var23 * var19 >> 7; - var13.anIntArray3883[var15] = this.anIntArray3883[var15] + (var24 - var6) + var14; - } - } else if(var1 == 5) { - var14 = this.aShort3892 - this.aShort3894; - - for(var15 = 0; var15 < this.anInt3891; ++var15) { - var16 = this.anIntArray3885[var15] + var5; - var17 = this.anIntArray3895[var15] + var7; - var18 = var16 & 127; - var19 = var17 & 127; - var20 = var16 >> 7; - var21 = var17 >> 7; - var22 = var3[var20][var21] * (128 - var18) + var3[var20 + 1][var21] * var18 >> 7; - var23 = var3[var20][var21 + 1] * (128 - var18) + var3[var20 + 1][var21 + 1] * var18 >> 7; - var24 = var22 * (128 - var19) + var23 * var19 >> 7; - var22 = var4[var20][var21] * (128 - var18) + var4[var20 + 1][var21] * var18 >> 7; - var23 = var4[var20][var21 + 1] * (128 - var18) + var4[var20 + 1][var21 + 1] * var18 >> 7; - int var25 = var22 * (128 - var19) + var23 * var19 >> 7; - int var26 = var24 - var25; - var13.anIntArray3883[var15] = ((this.anIntArray3883[var15] << 8) / var14 * var26 >> 8) - (var6 - var24); - } - } - } - - var13.aBoolean3897 = false; - return var13; - } - } - - final void method1885() { - for(int var1 = 0; var1 < this.anInt3891; ++var1) { - int var2 = this.anIntArray3885[var1]; - this.anIntArray3885[var1] = this.anIntArray3895[var1]; - this.anIntArray3895[var1] = -var2; - } - - this.aBoolean3897 = false; - } - - final void method1893(int var2, int var3, int var4, int var5, int var6, int var7) { - try { - if(!this.aBoolean3897) { - this.method1947(); - } - - int var10 = Class51.anInt846; - int var11 = Class51.anInt835; - int var14 = Class51.anIntArray840[var2]; - int var15 = Class51.anIntArray851[var2]; - int var16 = Class51.anIntArray840[var3]; - int var17 = Class51.anIntArray851[var3]; - int var18 = Class51.anIntArray840[var4]; - int var19 = Class51.anIntArray851[var4]; - int var20 = var6 * var18 + var7 * var19 >> 16; - - for(int var21 = 0; var21 < this.anInt3891; ++var21) { - int var22 = this.anIntArray3885[var21]; - int var23 = this.anIntArray3883[var21]; - int var24 = this.anIntArray3895[var21]; - int var25; - if(var3 != 0) { - var25 = var23 * var16 + var22 * var17 >> 16; - var23 = var23 * var17 - var22 * var16 >> 16; - var22 = var25; - } - - if(var2 != 0) { - var25 = var24 * var14 + var22 * var15 >> 16; - var24 = var24 * var15 - var22 * var14 >> 16; - var22 = var25; - } - - var22 += var5; - var23 += var6; - var24 += var7; - var25 = var23 * var19 - var24 * var18 >> 16; - var24 = var23 * var18 + var24 * var19 >> 16; - anIntArray3920[var21] = var24 - var20; - anIntArray3943[var21] = var10 + (var22 << 9) / var24; - anIntArray3932[var21] = var11 + (var25 << 9) / var24; - if(this.anInt3900 > 0) { - anIntArray3948[var21] = var22; - anIntArray3928[var21] = var25; - anIntArray3921[var21] = var24; - } - } - - this.method1945(false, false, -1, this.aShort3886, this.aShort3886 << 1); - } catch (RuntimeException var26) { - } - - } - - private void method1942(int var1) { - int var2 = Class51.anInt846; - int var3 = Class51.anInt835; - int var4 = 0; - int var5 = this.anIntArray3901[var1]; - int var6 = this.anIntArray3876[var1]; - int var7 = this.anIntArray3887[var1]; - int var8 = anIntArray3921[var5]; - int var9 = anIntArray3921[var6]; - int var10 = anIntArray3921[var7]; - if(this.aByteArray3903 == null) { - Class51.anInt850 = 0; - } else { - Class51.anInt850 = this.aByteArray3903[var1] & 0xFF; - } - - int var11; - int var12; - int var13; - int var14; - if(var8 >= 50) { - anIntArray3919[var4] = anIntArray3943[var5]; - anIntArray3925[var4] = anIntArray3932[var5]; - anIntArray3936[var4++] = this.anIntArray3898[var1] & 65535; - } else { - var11 = anIntArray3948[var5]; - var12 = anIntArray3928[var5]; - var13 = this.anIntArray3898[var1] & 65535; - if(var10 >= 50) { - var14 = (50 - var8) * Class51.anIntArray841[var10 - var8]; - anIntArray3919[var4] = var2 + (var11 + ((anIntArray3948[var7] - var11) * var14 >> 16) << 9) / 50; - anIntArray3925[var4] = var3 + (var12 + ((anIntArray3928[var7] - var12) * var14 >> 16) << 9) / 50; - anIntArray3936[var4++] = var13 + (((this.anIntArray3896[var1] & 65535) - var13) * var14 >> 16); - } - - if(var9 >= 50) { - var14 = (50 - var8) * Class51.anIntArray841[var9 - var8]; - anIntArray3919[var4] = var2 + (var11 + ((anIntArray3948[var6] - var11) * var14 >> 16) << 9) / 50; - anIntArray3925[var4] = var3 + (var12 + ((anIntArray3928[var6] - var12) * var14 >> 16) << 9) / 50; - anIntArray3936[var4++] = var13 + (((this.anIntArray3874[var1] & 65535) - var13) * var14 >> 16); - } - } - - if(var9 >= 50) { - anIntArray3919[var4] = anIntArray3943[var6]; - anIntArray3925[var4] = anIntArray3932[var6]; - anIntArray3936[var4++] = this.anIntArray3874[var1] & 65535; - } else { - var11 = anIntArray3948[var6]; - var12 = anIntArray3928[var6]; - var13 = this.anIntArray3874[var1] & 65535; - if(var8 >= 50) { - var14 = (50 - var9) * Class51.anIntArray841[var8 - var9]; - anIntArray3919[var4] = var2 + (var11 + ((anIntArray3948[var5] - var11) * var14 >> 16) << 9) / 50; - anIntArray3925[var4] = var3 + (var12 + ((anIntArray3928[var5] - var12) * var14 >> 16) << 9) / 50; - anIntArray3936[var4++] = var13 + (((this.anIntArray3898[var1] & 65535) - var13) * var14 >> 16); - } - - if(var10 >= 50) { - var14 = (50 - var9) * Class51.anIntArray841[var10 - var9]; - anIntArray3919[var4] = var2 + (var11 + ((anIntArray3948[var7] - var11) * var14 >> 16) << 9) / 50; - anIntArray3925[var4] = var3 + (var12 + ((anIntArray3928[var7] - var12) * var14 >> 16) << 9) / 50; - anIntArray3936[var4++] = var13 + (((this.anIntArray3896[var1] & 65535) - var13) * var14 >> 16); - } - } - - if(var10 >= 50) { - anIntArray3919[var4] = anIntArray3943[var7]; - anIntArray3925[var4] = anIntArray3932[var7]; - anIntArray3936[var4++] = this.anIntArray3896[var1] & 65535; - } else { - var11 = anIntArray3948[var7]; - var12 = anIntArray3928[var7]; - var13 = this.anIntArray3896[var1] & 65535; - if(var9 >= 50) { - var14 = (50 - var10) * Class51.anIntArray841[var9 - var10]; - anIntArray3919[var4] = var2 + (var11 + ((anIntArray3948[var6] - var11) * var14 >> 16) << 9) / 50; - anIntArray3925[var4] = var3 + (var12 + ((anIntArray3928[var6] - var12) * var14 >> 16) << 9) / 50; - anIntArray3936[var4++] = var13 + (((this.anIntArray3874[var1] & 65535) - var13) * var14 >> 16); - } - - if(var8 >= 50) { - var14 = (50 - var10) * Class51.anIntArray841[var8 - var10]; - anIntArray3919[var4] = var2 + (var11 + ((anIntArray3948[var5] - var11) * var14 >> 16) << 9) / 50; - anIntArray3925[var4] = var3 + (var12 + ((anIntArray3928[var5] - var12) * var14 >> 16) << 9) / 50; - anIntArray3936[var4++] = var13 + (((this.anIntArray3898[var1] & 65535) - var13) * var14 >> 16); - } - } - - var11 = anIntArray3919[0]; - var12 = anIntArray3919[1]; - var13 = anIntArray3919[2]; - var14 = anIntArray3925[0]; - int var15 = anIntArray3925[1]; - int var16 = anIntArray3925[2]; - Class51.aBoolean849 = false; - int var17; - int var19; - int var18; - int var20; - if(var4 == 3) { - if(var11 < 0 || var12 < 0 || var13 < 0 || var11 > Class51.anInt847 || var12 > Class51.anInt847 || var13 > Class51.anInt847) { - Class51.aBoolean849 = true; - } - - if(this.aShortArray3908 != null && this.aShortArray3908[var1] != -1) { - if(this.aByteArray3899 == null || this.aByteArray3899[var1] == -1) { - var17 = var5; - var18 = var6; - var19 = var7; - } else { - var20 = this.aByteArray3899[var1] & 0xFF; - var17 = this.anIntArray3882[var20]; - var18 = this.anIntArray3890[var20]; - var19 = this.anIntArray3881[var20]; - } - - if(this.anIntArray3896[var1] == -1) { - Class51.method1138(var14, var15, var16, var11, var12, var13, this.anIntArray3898[var1], this.anIntArray3898[var1], this.anIntArray3898[var1], anIntArray3948[var17], anIntArray3948[var18], anIntArray3948[var19], anIntArray3928[var17], anIntArray3928[var18], anIntArray3928[var19], anIntArray3921[var17], anIntArray3921[var18], anIntArray3921[var19], this.aShortArray3908[var1]); - } else { - Class51.method1138(var14, var15, var16, var11, var12, var13, anIntArray3936[0], anIntArray3936[1], anIntArray3936[2], anIntArray3948[var17], anIntArray3948[var18], anIntArray3948[var19], anIntArray3928[var17], anIntArray3928[var18], anIntArray3928[var19], anIntArray3921[var17], anIntArray3921[var18], anIntArray3921[var19], this.aShortArray3908[var1]); - } - } else if(this.anIntArray3896[var1] == -1) { - Class51.method1144(var14, var15, var16, var11, var12, var13, Class51.anIntArray834[this.anIntArray3898[var1] & 65535]); - } else { - Class51.method1154(var14, var15, var16, var11, var12, var13, anIntArray3936[0], anIntArray3936[1], anIntArray3936[2]); - } - } - - if(var4 == 4) { - if(var11 < 0 || var12 < 0 || var13 < 0 || var11 > Class51.anInt847 || var12 > Class51.anInt847 || var13 > Class51.anInt847 || anIntArray3919[3] < 0 || anIntArray3919[3] > Class51.anInt847) { - Class51.aBoolean849 = true; - } - - if(this.aShortArray3908 != null && this.aShortArray3908[var1] != -1) { - if(this.aByteArray3899 == null || this.aByteArray3899[var1] == -1) { - var17 = var5; - var18 = var6; - var19 = var7; - } else { - var20 = this.aByteArray3899[var1] & 0xFF; - var17 = this.anIntArray3882[var20]; - var18 = this.anIntArray3890[var20]; - var19 = this.anIntArray3881[var20]; - } - - short var21 = this.aShortArray3908[var1]; - if(this.anIntArray3896[var1] == -1) { - Class51.method1138(var14, var15, var16, var11, var12, var13, this.anIntArray3898[var1], this.anIntArray3898[var1], this.anIntArray3898[var1], anIntArray3948[var17], anIntArray3948[var18], anIntArray3948[var19], anIntArray3928[var17], anIntArray3928[var18], anIntArray3928[var19], anIntArray3921[var17], anIntArray3921[var18], anIntArray3921[var19], var21); - Class51.method1138(var14, var16, anIntArray3925[3], var11, var13, anIntArray3919[3], this.anIntArray3898[var1], this.anIntArray3898[var1], this.anIntArray3898[var1], anIntArray3948[var17], anIntArray3948[var18], anIntArray3948[var19], anIntArray3928[var17], anIntArray3928[var18], anIntArray3928[var19], anIntArray3921[var17], anIntArray3921[var18], anIntArray3921[var19], var21); - } else { - Class51.method1138(var14, var15, var16, var11, var12, var13, anIntArray3936[0], anIntArray3936[1], anIntArray3936[2], anIntArray3948[var17], anIntArray3948[var18], anIntArray3948[var19], anIntArray3928[var17], anIntArray3928[var18], anIntArray3928[var19], anIntArray3921[var17], anIntArray3921[var18], anIntArray3921[var19], var21); - Class51.method1138(var14, var16, anIntArray3925[3], var11, var13, anIntArray3919[3], anIntArray3936[0], anIntArray3936[2], anIntArray3936[3], anIntArray3948[var17], anIntArray3948[var18], anIntArray3948[var19], anIntArray3928[var17], anIntArray3928[var18], anIntArray3928[var19], anIntArray3921[var17], anIntArray3921[var18], anIntArray3921[var19], var21); - } - } else if(this.anIntArray3896[var1] == -1) { - var17 = Class51.anIntArray834[this.anIntArray3898[var1] & 65535]; - Class51.method1144(var14, var15, var16, var11, var12, var13, var17); - Class51.method1144(var14, var16, anIntArray3925[3], var11, var13, anIntArray3919[3], var17); - } else { - Class51.method1154(var14, var15, var16, var11, var12, var13, anIntArray3936[0], anIntArray3936[1], anIntArray3936[2]); - Class51.method1154(var14, var16, anIntArray3925[3], var11, var13, anIntArray3919[3], anIntArray3936[0], anIntArray3936[2], anIntArray3936[3]); - } - } - - } - - final int method1872() { - if(!this.aBoolean3897) { - this.method1947(); - } - - return this.aShort3888; - } - - final void resize(int var1, int var2, int var3) { - for(int var4 = 0; var4 < this.anInt3891; ++var4) { - this.anIntArray3885[var4] = this.anIntArray3885[var4] * var1 / 128; - this.anIntArray3883[var4] = this.anIntArray3883[var4] * var2 / 128; - this.anIntArray3895[var4] = this.anIntArray3895[var4] * var3 / 128; - } - - this.aBoolean3897 = false; - } - - final Model method1882(boolean var1, boolean var2, boolean var3) { - if(!var1 && aByteArray3914.length < this.anInt3889) { - aByteArray3914 = new byte[this.anInt3889 + 100]; - } - - if(!var2 && aShortArray3912.length < this.anInt3889) { - anIntArray3913 = new int[this.anInt3889 + 100]; - anIntArray3916 = new int[this.anInt3889 + 100]; - anIntArray3911 = new int[this.anInt3889 + 100]; - aShortArray3912 = new short[this.anInt3889 + 100]; - } - - return this.method1939(var1, var2, aClass140_Sub1_Sub2_3906, aByteArray3914, aShortArray3912, anIntArray3913, anIntArray3916, anIntArray3911); - } - - final int method1888() { - if(!this.aBoolean3897) { - this.method1947(); - } - - return this.aShort3884; - } - - final Model method1943(Model var1) { - return new Class140_Sub1_Sub2(new Class140_Sub1_Sub2[]{this, (Class140_Sub1_Sub2)var1}); - } - - final int method1883() { - if(!this.aBoolean3897) { - this.method1947(); - } - - return this.aShort3878; - } - - final void method1879() { - if(this.aBoolean3877) { - this.method1936(); - this.aBoolean3877 = false; - } - - this.aBoolean3897 = false; - } - - final void method1876(int var1) { - int var2 = Class51.anIntArray840[var1]; - int var3 = Class51.anIntArray851[var1]; - - for(int var4 = 0; var4 < this.anInt3891; ++var4) { - int var5 = this.anIntArray3895[var4] * var2 + this.anIntArray3885[var4] * var3 >> 16; - this.anIntArray3895[var4] = this.anIntArray3895[var4] * var3 - this.anIntArray3885[var4] * var2 >> 16; - this.anIntArray3885[var4] = var5; - } - - this.aBoolean3897 = false; - } - - final void method1891(int var1, int[] var2, int var3, int var4, int var5, boolean var6) { - int var7 = var2.length; - int var8; - int var9; - int var12; - int var13; - if(var1 == 0) { - var8 = 0; - anInt3945 = 0; - anInt3934 = 0; - anInt3940 = 0; - - for(var9 = 0; var9 < var7; ++var9) { - int var17 = var2[var9]; - if(var17 < this.anIntArrayArray3870.length) { - int[] var18 = this.anIntArrayArray3870[var17]; - - for(var12 = 0; var12 < var18.length; ++var12) { - var13 = var18[var12]; - anInt3945 += this.anIntArray3885[var13]; - anInt3934 += this.anIntArray3883[var13]; - anInt3940 += this.anIntArray3895[var13]; - ++var8; - } - } - } - - if(var8 > 0) { - anInt3945 = anInt3945 / var8 + var3; - anInt3934 = anInt3934 / var8 + var4; - anInt3940 = anInt3940 / var8 + var5; - } else { - anInt3945 = var3; - anInt3934 = var4; - anInt3940 = var5; - } - - } else { - int[] var10; - int var11; - if(var1 == 1) { - for(var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if(var9 < this.anIntArrayArray3870.length) { - var10 = this.anIntArrayArray3870[var9]; - - for(var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - this.anIntArray3885[var12] += var3; - this.anIntArray3883[var12] += var4; - this.anIntArray3895[var12] += var5; - } - } - } - - } else { - int var14; - int var15; - if(var1 == 2) { - for(var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if(var9 < this.anIntArrayArray3870.length) { - var10 = this.anIntArrayArray3870[var9]; - - for(var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - this.anIntArray3885[var12] -= anInt3945; - this.anIntArray3883[var12] -= anInt3934; - this.anIntArray3895[var12] -= anInt3940; - if(var5 != 0) { - var13 = Class51.anIntArray840[var5]; - var14 = Class51.anIntArray851[var5]; - var15 = this.anIntArray3883[var12] * var13 + this.anIntArray3885[var12] * var14 + 32767 >> 16; - this.anIntArray3883[var12] = this.anIntArray3883[var12] * var14 - this.anIntArray3885[var12] * var13 + 32767 >> 16; - this.anIntArray3885[var12] = var15; - } - - if(var3 != 0) { - var13 = Class51.anIntArray840[var3]; - var14 = Class51.anIntArray851[var3]; - var15 = this.anIntArray3883[var12] * var14 - this.anIntArray3895[var12] * var13 + 32767 >> 16; - this.anIntArray3895[var12] = this.anIntArray3883[var12] * var13 + this.anIntArray3895[var12] * var14 + 32767 >> 16; - this.anIntArray3883[var12] = var15; - } - - if(var4 != 0) { - var13 = Class51.anIntArray840[var4]; - var14 = Class51.anIntArray851[var4]; - var15 = this.anIntArray3895[var12] * var13 + this.anIntArray3885[var12] * var14 + 32767 >> 16; - this.anIntArray3895[var12] = this.anIntArray3895[var12] * var14 - this.anIntArray3885[var12] * var13 + 32767 >> 16; - this.anIntArray3885[var12] = var15; - } - - this.anIntArray3885[var12] += anInt3945; - this.anIntArray3883[var12] += anInt3934; - this.anIntArray3895[var12] += anInt3940; - } - } - } - - } else if(var1 == 3) { - for(var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if(var9 < this.anIntArrayArray3870.length) { - var10 = this.anIntArrayArray3870[var9]; - - for(var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - this.anIntArray3885[var12] -= anInt3945; - this.anIntArray3883[var12] -= anInt3934; - this.anIntArray3895[var12] -= anInt3940; - this.anIntArray3885[var12] = this.anIntArray3885[var12] * var3 / 128; - this.anIntArray3883[var12] = this.anIntArray3883[var12] * var4 / 128; - this.anIntArray3895[var12] = this.anIntArray3895[var12] * var5 / 128; - this.anIntArray3885[var12] += anInt3945; - this.anIntArray3883[var12] += anInt3934; - this.anIntArray3895[var12] += anInt3940; - } - } - } - - } else if(var1 == 5) { - if(this.anIntArrayArray3872 != null && this.aByteArray3903 != null) { - for(var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if(var9 < this.anIntArrayArray3872.length) { - var10 = this.anIntArrayArray3872[var9]; - - for(var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - var13 = (this.aByteArray3903[var12] & 0xFF) + var3 * 8; - if(var13 < 0) { - var13 = 0; - } else if(var13 > 255) { - var13 = 255; - } - - this.aByteArray3903[var12] = (byte)var13; - } - } - } - } - - } else if(var1 == 7) { - if(this.anIntArrayArray3872 != null) { - for(var8 = 0; var8 < var7; ++var8) { - var9 = var2[var8]; - if(var9 < this.anIntArrayArray3872.length) { - var10 = this.anIntArrayArray3872[var9]; - - for(var11 = 0; var11 < var10.length; ++var11) { - var12 = var10[var11]; - var13 = this.aShortArray3869[var12] & 65535; - var14 = var13 >> 10 & 63; - var15 = var13 >> 7 & 7; - int var16 = var13 & 127; - var14 = var14 + var3 & 63; - var15 += var4; - if(var15 < 0) { - var15 = 0; - } else if(var15 > 7) { - var15 = 7; - } - - var16 += var5; - if(var16 < 0) { - var16 = 0; - } else if(var16 > 127) { - var16 = 127; - } - - this.aShortArray3869[var12] = (short)(var14 << 10 | var15 << 7 | var16); - } - - this.aBoolean3877 = true; - } - } - } - - } - } - } - } - - private boolean method1944(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - return (var2 >= var3 || var2 >= var4 || var2 >= var5) && ((var2 <= var3 || var2 <= var4 || var2 <= var5) && ((var1 >= var6 || var1 >= var7 || var1 >= var8) && (var1 <= var6 || var1 <= var7 || var1 <= var8))); - } - - final Model method1894(boolean var1, boolean var2, boolean var3) { - if(!var1 && aByteArray3907.length < this.anInt3889) { - aByteArray3907 = new byte[this.anInt3889 + 100]; - } - - if(!var2 && aShortArray3910.length < this.anInt3889) { - anIntArray3905 = new int[this.anInt3889 + 100]; - anIntArray3909 = new int[this.anInt3889 + 100]; - anIntArray3904 = new int[this.anInt3889 + 100]; - aShortArray3910 = new short[this.anInt3889 + 100]; - } - - return this.method1939(var1, var2, aClass140_Sub1_Sub2_3902, aByteArray3907, aShortArray3910, anIntArray3905, anIntArray3909, anIntArray3904); - } - - private void method1945(boolean var1, boolean var2, long var3, int var5, int var6) { - if(var6 < 1600) { - int var8 = 0; - int var9 = 0; - int var10; - if(!aBoolean3942) { - for(var10 = 0; var10 < 1600; ++var10) { - anIntArray3946[var10] = 0; - } - - for(var10 = 0; var10 < 32; ++var10) { - anIntArray3935[var10] = 0; - } - - anInt3930 = 0; - } - - int var11; - int var12; - int var13; - int var14; - int var15; - int var17; - int var19; - int var18; - int var21; - for(var10 = 0; var10 < this.anInt3889; ++var10) { - if(this.anIntArray3896[var10] != -2) { - var11 = this.anIntArray3901[var10]; - var12 = this.anIntArray3876[var10]; - var13 = this.anIntArray3887[var10]; - var14 = anIntArray3943[var11]; - var15 = anIntArray3943[var12]; - int var16 = anIntArray3943[var13]; - if(var1 && (var14 == -5000 || var15 == -5000 || var16 == -5000)) { - var17 = anIntArray3948[var11]; - var18 = anIntArray3948[var12]; - var19 = anIntArray3948[var13]; - int var20 = anIntArray3928[var11]; - var21 = anIntArray3928[var12]; - int var22 = anIntArray3928[var13]; - int var23 = anIntArray3921[var11]; - int var24 = anIntArray3921[var12]; - int var25 = anIntArray3921[var13]; - var17 -= var18; - var19 -= var18; - var20 -= var21; - var22 -= var21; - var23 -= var24; - var25 -= var24; - int var26 = var20 * var25 - var23 * var22; - int var27 = var23 * var19 - var17 * var25; - int var28 = var17 * var22 - var20 * var19; - if(var18 * var26 + var21 * var27 + var24 * var28 > 0) { - aBooleanArray3917[var10] = true; - if(aBoolean3942) { - anIntArray3931[var8] = (anIntArray3920[var11] + anIntArray3920[var12] + anIntArray3920[var13]) / 3; - anIntArray3929[var8++] = var10; - } else { - int var29 = (anIntArray3920[var11] + anIntArray3920[var12] + anIntArray3920[var13]) / 3 + var5; - if(anIntArray3946[var29] < 64) { - anIntArrayArray3926[var29][anIntArray3946[var29]++] = var10; - } else { - int var30 = anIntArray3946[var29]; - if(var30 == 64) { - if(anInt3930 == 512) { - continue; - } - - anIntArray3946[var29] = var30 = 65 + anInt3930++; - } - - var30 -= 65; - anIntArrayArray3923[var30][anIntArray3935[var30]++] = var10; - } - } - } - } else { - if(var2 && this.method1944(Unsorted.anInt3642 + Class51.anInt846, RenderAnimationDefinition.anInt384 + Class51.anInt835, anIntArray3932[var11], anIntArray3932[var12], anIntArray3932[var13], var14, var15, var16)) { - TextureOperation38.aLongArray3448[Unsorted.anInt59++] = var3; - var2 = false; - } - - if((var14 - var15) * (anIntArray3932[var13] - anIntArray3932[var12]) - (anIntArray3932[var11] - anIntArray3932[var12]) * (var16 - var15) > 0) { - aBooleanArray3917[var10] = false; - aBooleanArray3937[var10] = var14 < 0 || var15 < 0 || var16 < 0 || var14 > Class51.anInt847 || var15 > Class51.anInt847 || var16 > Class51.anInt847; - - if(aBoolean3942) { - anIntArray3931[var8] = (anIntArray3920[var11] + anIntArray3920[var12] + anIntArray3920[var13]) / 3; - anIntArray3929[var8++] = var10; - } else { - var17 = (anIntArray3920[var11] + anIntArray3920[var12] + anIntArray3920[var13]) / 3 + var5; - if(anIntArray3946[var17] < 64) { - anIntArrayArray3926[var17][anIntArray3946[var17]++] = var10; - } else { - var18 = anIntArray3946[var17]; - if(var18 == 64) { - if(anInt3930 == 512) { - continue; - } - - anIntArray3946[var17] = var18 = 65 + anInt3930++; - } - - var18 -= 65; - anIntArrayArray3923[var18][anIntArray3935[var18]++] = var10; - } - } - } - } - } - } - - if(aBoolean3942) { - Class101.method1607(0, var8 - 1, false, anIntArray3931, anIntArray3929); - if(this.aByteArray3880 == null) { - for(var10 = 0; var10 < var8; ++var10) { - this.method1934(anIntArray3929[var10]); - } - - return; - } - - for(var10 = 0; var10 < 12; ++var10) { - anIntArray3918[var10] = 0; - anIntArray3944[var10] = 0; - } - - for(var10 = 0; var10 < var8; ++var10) { - var11 = anIntArray3929[var10]; - var12 = anIntArray3931[var10]; - byte var32 = this.aByteArray3880[var11]; - var14 = anIntArray3918[var32]++; - anIntArrayArray3938[var32][var14] = var11; - if(var32 < 10) { - anIntArray3944[var32] += var12; - } else if(var32 == 10) { - anIntArray3922[var14] = var12; - } else { - anIntArray3941[var14] = var12; - } - } - } else { - int[] var31; - if(this.aByteArray3880 == null) { - for(var10 = var6 - 1; var10 >= 0; --var10) { - var11 = anIntArray3946[var10]; - if(var11 > 0) { - var12 = var11 > 64?64:var11; - var31 = anIntArrayArray3926[var10]; - - for(var14 = 0; var14 < var12; ++var14) { - var15 = var31[var14]; - if(var15 < 65536) { - this.method1934(var31[var14]); - } - } - } - - if(var11 > 64) { - var12 = anIntArray3946[var10] - 64 - 1; - var31 = anIntArrayArray3923[var12]; - - for(var14 = 0; var14 < anIntArray3935[var12]; ++var14) { - var15 = var31[var14]; - if(var15 < 65536) { - this.method1934(var31[var14]); - } - } - } - } - - return; - } - - for(var10 = 0; var10 < 12; ++var10) { - anIntArray3918[var10] = 0; - anIntArray3944[var10] = 0; - } - - for(var10 = var6 - 1; var10 >= 0; --var10) { - var11 = anIntArray3946[var10]; - byte var33; - if(var11 > 0) { - if(var11 > 64) { - var12 = 64; - } else { - var12 = var11; - } - - var31 = anIntArrayArray3926[var10]; - - for(var14 = 0; var14 < var12; ++var14) { - var15 = var31[var14]; - if(var15 < 65536) { - var33 = this.aByteArray3880[var15]; - var17 = anIntArray3918[var33]++; - anIntArrayArray3938[var33][var17] = var15; - if(var33 < 10) { - anIntArray3944[var33] += var10; - } else if(var33 == 10) { - anIntArray3922[var17] = var10; - } else { - anIntArray3941[var17] = var10; - } - } else { - anIntArray3950[var9++] = (var15 >> 16) - 1; - } - } - } - - if(var11 > 64) { - var12 = anIntArray3946[var10] - 64 - 1; - var31 = anIntArrayArray3923[var12]; - - for(var14 = 0; var14 < anIntArray3935[var12]; ++var14) { - var15 = var31[var14]; - if(var15 < 65536) { - var33 = this.aByteArray3880[var15]; - var17 = anIntArray3918[var33]++; - anIntArrayArray3938[var33][var17] = var15; - if(var33 < 10) { - anIntArray3944[var33] += var10; - } else if(var33 == 10) { - anIntArray3922[var17] = var10; - } else { - anIntArray3941[var17] = var10; - } - } else { - anIntArray3950[var9++] = (var15 >> 16) - 1; - } - } - } - } - } - - var10 = 0; - if(anIntArray3918[1] > 0 || anIntArray3918[2] > 0) { - var10 = (anIntArray3944[1] + anIntArray3944[2]) / (anIntArray3918[1] + anIntArray3918[2]); - } - - var11 = 0; - if(anIntArray3918[3] > 0 || anIntArray3918[4] > 0) { - var11 = (anIntArray3944[3] + anIntArray3944[4]) / (anIntArray3918[3] + anIntArray3918[4]); - } - - var12 = 0; - if(anIntArray3918[6] > 0 || anIntArray3918[8] > 0) { - var12 = (anIntArray3944[6] + anIntArray3944[8]) / (anIntArray3918[6] + anIntArray3918[8]); - } - - var14 = 0; - var15 = anIntArray3918[10]; - int[] var34 = anIntArrayArray3938[10]; - int[] var35 = anIntArray3922; - if(var14 == var15) { - var15 = anIntArray3918[11]; - var34 = anIntArrayArray3938[11]; - var35 = anIntArray3941; - } - - if(var14 < var15) { - var13 = var35[var14]; - } else { - var13 = -1000; - } - - for(var18 = 0; var18 < 10; ++var18) { - while(var18 == 0 && var13 > var10) { - this.method1934(var34[var14++]); - if(var14 == var15 && var34 != anIntArrayArray3938[11]) { - var14 = 0; - var15 = anIntArray3918[11]; - var34 = anIntArrayArray3938[11]; - var35 = anIntArray3941; - } - - if(var14 < var15) { - var13 = var35[var14]; - } else { - var13 = -1000; - } - } - - while(var18 == 3 && var13 > var11) { - this.method1934(var34[var14++]); - if(var14 == var15 && var34 != anIntArrayArray3938[11]) { - var14 = 0; - var15 = anIntArray3918[11]; - var34 = anIntArrayArray3938[11]; - var35 = anIntArray3941; - } - - if(var14 < var15) { - var13 = var35[var14]; - } else { - var13 = -1000; - } - } - - while(var18 == 5 && var13 > var12) { - this.method1934(var34[var14++]); - if(var14 == var15 && var34 != anIntArrayArray3938[11]) { - var14 = 0; - var15 = anIntArray3918[11]; - var34 = anIntArrayArray3938[11]; - var35 = anIntArray3941; - } - - if(var14 < var15) { - var13 = var35[var14]; - } else { - var13 = -1000; - } - } - - var19 = anIntArray3918[var18]; - int[] var36 = anIntArrayArray3938[var18]; - - for(var21 = 0; var21 < var19; ++var21) { - this.method1934(var36[var21]); - } - } - - while(var13 != -1000) { - this.method1934(var34[var14++]); - if(var14 == var15 && var34 != anIntArrayArray3938[11]) { - var14 = 0; - var34 = anIntArrayArray3938[11]; - var15 = anIntArray3918[11]; - var35 = anIntArray3941; - } - - if(var14 < var15) { - var13 = var35[var14]; - } else { - var13 = -1000; - } - } - - } - } - - final void method1900() { - for(int var1 = 0; var1 < this.anInt3891; ++var1) { - int var2 = this.anIntArray3895[var1]; - this.anIntArray3895[var1] = this.anIntArray3885[var1]; - this.anIntArray3885[var1] = -var2; - } - - this.aBoolean3897 = false; - } - - final void method1946(int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - if(!this.aBoolean3897) { - this.method1947(); - } - - int var9 = Class51.anInt846; - int var10 = Class51.anInt835; - int var13 = Class51.anIntArray840[var2]; - int var14 = Class51.anIntArray851[var2]; - int var15 = Class51.anIntArray840[var3]; - int var16 = Class51.anIntArray851[var3]; - int var17 = Class51.anIntArray840[var4]; - int var18 = Class51.anIntArray851[var4]; - int var19 = var6 * var17 + var7 * var18 >> 16; - - for(int var20 = 0; var20 < this.anInt3891; ++var20) { - int var21 = this.anIntArray3885[var20]; - int var22 = this.anIntArray3883[var20]; - int var23 = this.anIntArray3895[var20]; - int var24; - if(var3 != 0) { - var24 = var22 * var15 + var21 * var16 >> 16; - var22 = var22 * var16 - var21 * var15 >> 16; - var21 = var24; - } - - if(var2 != 0) { - var24 = var23 * var13 + var21 * var14 >> 16; - var23 = var23 * var14 - var21 * var13 >> 16; - var21 = var24; - } - - var21 += var5; - var22 += var6; - var23 += var7; - var24 = var22 * var18 - var23 * var17 >> 16; - var23 = var22 * var17 + var23 * var18 >> 16; - anIntArray3920[var20] = var23 - var19; - anIntArray3943[var20] = var9 + (var21 << 9) / var8; - anIntArray3932[var20] = var10 + (var24 << 9) / var8; - if(this.anInt3900 > 0) { - anIntArray3948[var20] = var21; - anIntArray3928[var20] = var24; - anIntArray3921[var20] = var23; - } - } - - this.method1945(false, false, 0L, this.aShort3886, this.aShort3886 << 1); - } catch (RuntimeException var25) { - } - - } - - private void method1947() { - int var1 = 32767; - int var2 = 32767; - int var3 = 32767; - int var4 = -32768; - int var5 = -32768; - int var6 = -32768; - int var7 = 0; - int var8 = 0; - - for(int var9 = 0; var9 < this.anInt3891; ++var9) { - int var10 = this.anIntArray3885[var9]; - int var11 = this.anIntArray3883[var9]; - int var12 = this.anIntArray3895[var9]; - if(var10 < var1) { - var1 = var10; - } - - if(var10 > var4) { - var4 = var10; - } - - if(var11 < var2) { - var2 = var11; - } - - if(var11 > var5) { - var5 = var11; - } - - if(var12 < var3) { - var3 = var12; - } - - if(var12 > var6) { - var6 = var12; - } - - int var13 = var10 * var10 + var12 * var12; - if(var13 > var7) { - var7 = var13; - } - - var13 += var11 * var11; - if(var13 > var8) { - var8 = var13; - } - } - - this.aShort3873 = (short)var1; - this.aShort3878 = (short)var4; - this.aShort3894 = (short)var2; - this.aShort3892 = (short)var5; - this.aShort3879 = (short)var3; - this.aShort3888 = (short)var6; - this.aShort3884 = (short)((int)(Math.sqrt(var7) + 0.99D)); - this.aShort3886 = (short)((int)(Math.sqrt(var8) + 0.99D)); - this.aBoolean3897 = true; - } - - final int method1884() { - if(!this.aBoolean3897) { - this.method1947(); - } - - return this.aShort3873; - } - - final void method1899(int var1, int[] var2, int var3, int var4, int var5, boolean var6, int var7, int[] var8) { - int var9 = var2.length; - int var10; - int var11; - int var14; - int var15; - int var47; - if(var1 == 0) { - var10 = 0; - anInt3945 = 0; - anInt3934 = 0; - anInt3940 = 0; - - for(var11 = 0; var11 < var9; ++var11) { - var47 = var2[var11]; - if(var47 < this.anIntArrayArray3870.length) { - int[] var48 = this.anIntArrayArray3870[var47]; - - for(var14 = 0; var14 < var48.length; ++var14) { - var15 = var48[var14]; - if(this.aShortArray3893 == null || (var7 & this.aShortArray3893[var15]) != 0) { - anInt3945 += this.anIntArray3885[var15]; - anInt3934 += this.anIntArray3883[var15]; - anInt3940 += this.anIntArray3895[var15]; - ++var10; - } - } - } - } - - if(var10 > 0) { - anInt3945 = anInt3945 / var10 + var3; - anInt3934 = anInt3934 / var10 + var4; - anInt3940 = anInt3940 / var10 + var5; - aBoolean3927 = true; - } else { - anInt3945 = var3; - anInt3934 = var4; - anInt3940 = var5; - } - - } else { - int[] var12; - int var13; - if(var1 == 1) { - if(var8 != null) { - var10 = var8[0] * var3 + var8[1] * var4 + var8[2] * var5 + 16384 >> 15; - var11 = var8[3] * var3 + var8[4] * var4 + var8[5] * var5 + 16384 >> 15; - var47 = var8[6] * var3 + var8[7] * var4 + var8[8] * var5 + 16384 >> 15; - var3 = var10; - var4 = var11; - var5 = var47; - } - - for(var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if(var11 < this.anIntArrayArray3870.length) { - var12 = this.anIntArrayArray3870[var11]; - - for(var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if(this.aShortArray3893 == null || (var7 & this.aShortArray3893[var14]) != 0) { - this.anIntArray3885[var14] += var3; - this.anIntArray3883[var14] += var4; - this.anIntArray3895[var14] += var5; - } - } - } - } - - } else { - int var17; - int var16; - int var19; - int var18; - int var21; - int var20; - int var23; - int var22; - int var24; - int var27; - int var26; - int var28; - int var30; - int var34; - int[] var35; - int var32; - int var33; - int var38; - int var39; - int var36; - int var37; - int var40; - if(var1 == 2) { - if(var8 == null) { - for(var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if(var11 < this.anIntArrayArray3870.length) { - var12 = this.anIntArrayArray3870[var11]; - - for(var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if(this.aShortArray3893 == null || (var7 & this.aShortArray3893[var14]) != 0) { - this.anIntArray3885[var14] -= anInt3945; - this.anIntArray3883[var14] -= anInt3934; - this.anIntArray3895[var14] -= anInt3940; - if(var5 != 0) { - var15 = Class51.anIntArray840[var5]; - var16 = Class51.anIntArray851[var5]; - var17 = this.anIntArray3883[var14] * var15 + this.anIntArray3885[var14] * var16 + 32767 >> 16; - this.anIntArray3883[var14] = this.anIntArray3883[var14] * var16 - this.anIntArray3885[var14] * var15 + 32767 >> 16; - this.anIntArray3885[var14] = var17; - } - - if(var3 != 0) { - var15 = Class51.anIntArray840[var3]; - var16 = Class51.anIntArray851[var3]; - var17 = this.anIntArray3883[var14] * var16 - this.anIntArray3895[var14] * var15 + 32767 >> 16; - this.anIntArray3895[var14] = this.anIntArray3883[var14] * var15 + this.anIntArray3895[var14] * var16 + 32767 >> 16; - this.anIntArray3883[var14] = var17; - } - - if(var4 != 0) { - var15 = Class51.anIntArray840[var4]; - var16 = Class51.anIntArray851[var4]; - var17 = this.anIntArray3895[var14] * var15 + this.anIntArray3885[var14] * var16 + 32767 >> 16; - this.anIntArray3895[var14] = this.anIntArray3895[var14] * var16 - this.anIntArray3885[var14] * var15 + 32767 >> 16; - this.anIntArray3885[var14] = var17; - } - - this.anIntArray3885[var14] += anInt3945; - this.anIntArray3883[var14] += anInt3934; - this.anIntArray3895[var14] += anInt3940; - } - } - } - } - } else { - var10 = var8[9]; - var11 = var8[10]; - var47 = var8[11]; - var13 = var8[12]; - var14 = var8[13]; - var15 = var8[14]; - if(aBoolean3927) { - var16 = var8[0] * anInt3945 + var8[3] * anInt3934 + var8[6] * anInt3940 + 16384 >> 15; - var17 = var8[1] * anInt3945 + var8[4] * anInt3934 + var8[7] * anInt3940 + 16384 >> 15; - var18 = var8[2] * anInt3945 + var8[5] * anInt3934 + var8[8] * anInt3940 + 16384 >> 15; - var16 += var13; - var17 += var14; - var18 += var15; - anInt3945 = var16; - anInt3934 = var17; - anInt3940 = var18; - aBoolean3927 = false; - } - - int[] var49 = new int[9]; - var17 = Class51.anIntArray851[var3] >> 1; - var18 = Class51.anIntArray840[var3] >> 1; - var19 = Class51.anIntArray851[var4] >> 1; - var20 = Class51.anIntArray840[var4] >> 1; - var21 = Class51.anIntArray851[var5] >> 1; - var22 = Class51.anIntArray840[var5] >> 1; - var23 = var18 * var21 + 16384 >> 15; - var24 = var18 * var22 + 16384 >> 15; - var49[0] = var19 * var21 + var20 * var24 + 16384 >> 15; - var49[1] = -var19 * var22 + var20 * var23 + 16384 >> 15; - var49[2] = var20 * var17 + 16384 >> 15; - var49[3] = var17 * var22 + 16384 >> 15; - var49[4] = var17 * var21 + 16384 >> 15; - var49[5] = -var18; - var49[6] = -var20 * var21 + var19 * var24 + 16384 >> 15; - var49[7] = var20 * var22 + var19 * var23 + 16384 >> 15; - var49[8] = var19 * var17 + 16384 >> 15; - int var50 = var49[0] * -anInt3945 + var49[1] * -anInt3934 + var49[2] * -anInt3940 + 16384 >> 15; - var26 = var49[3] * -anInt3945 + var49[4] * -anInt3934 + var49[5] * -anInt3940 + 16384 >> 15; - var27 = var49[6] * -anInt3945 + var49[7] * -anInt3934 + var49[8] * -anInt3940 + 16384 >> 15; - var28 = var50 + anInt3945; - int var51 = var26 + anInt3934; - var30 = var27 + anInt3940; - int[] var52 = new int[9]; - - for(var32 = 0; var32 < 3; ++var32) { - for(var33 = 0; var33 < 3; ++var33) { - var34 = 0; - - for(int var53 = 0; var53 < 3; ++var53) { - var34 += var49[var32 * 3 + var53] * var8[var33 * 3 + var53]; - } - - var52[var32 * 3 + var33] = var34 + 16384 >> 15; - } - } - - var32 = var49[0] * var13 + var49[1] * var14 + var49[2] * var15 + 16384 >> 15; - var33 = var49[3] * var13 + var49[4] * var14 + var49[5] * var15 + 16384 >> 15; - var34 = var49[6] * var13 + var49[7] * var14 + var49[8] * var15 + 16384 >> 15; - var32 += var28; - var33 += var51; - var34 += var30; - var35 = new int[9]; - - for(var36 = 0; var36 < 3; ++var36) { - for(var37 = 0; var37 < 3; ++var37) { - var38 = 0; - - for(var39 = 0; var39 < 3; ++var39) { - var38 += var8[var36 * 3 + var39] * var52[var37 + var39 * 3]; - } - - var35[var36 * 3 + var37] = var38 + 16384 >> 15; - } - } - - var36 = var8[0] * var32 + var8[1] * var33 + var8[2] * var34 + 16384 >> 15; - var37 = var8[3] * var32 + var8[4] * var33 + var8[5] * var34 + 16384 >> 15; - var38 = var8[6] * var32 + var8[7] * var33 + var8[8] * var34 + 16384 >> 15; - var36 += var10; - var37 += var11; - var38 += var47; - - for(var39 = 0; var39 < var9; ++var39) { - var40 = var2[var39]; - if(var40 < this.anIntArrayArray3870.length) { - int[] var41 = this.anIntArrayArray3870[var40]; - - for(int var42 = 0; var42 < var41.length; ++var42) { - int var43 = var41[var42]; - if(this.aShortArray3893 == null || (var7 & this.aShortArray3893[var43]) != 0) { - int var44 = var35[0] * this.anIntArray3885[var43] + var35[1] * this.anIntArray3883[var43] + var35[2] * this.anIntArray3895[var43] + 16384 >> 15; - int var45 = var35[3] * this.anIntArray3885[var43] + var35[4] * this.anIntArray3883[var43] + var35[5] * this.anIntArray3895[var43] + 16384 >> 15; - int var46 = var35[6] * this.anIntArray3885[var43] + var35[7] * this.anIntArray3883[var43] + var35[8] * this.anIntArray3895[var43] + 16384 >> 15; - var44 += var36; - var45 += var37; - var46 += var38; - this.anIntArray3885[var43] = var44; - this.anIntArray3883[var43] = var45; - this.anIntArray3895[var43] = var46; - } - } - } - } - } - - } else if(var1 == 3) { - if(var8 == null) { - for(var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if(var11 < this.anIntArrayArray3870.length) { - var12 = this.anIntArrayArray3870[var11]; - - for(var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if(this.aShortArray3893 == null || (var7 & this.aShortArray3893[var14]) != 0) { - this.anIntArray3885[var14] -= anInt3945; - this.anIntArray3883[var14] -= anInt3934; - this.anIntArray3895[var14] -= anInt3940; - this.anIntArray3885[var14] = this.anIntArray3885[var14] * var3 / 128; - this.anIntArray3883[var14] = this.anIntArray3883[var14] * var4 / 128; - this.anIntArray3895[var14] = this.anIntArray3895[var14] * var5 / 128; - this.anIntArray3885[var14] += anInt3945; - this.anIntArray3883[var14] += anInt3934; - this.anIntArray3895[var14] += anInt3940; - } - } - } - } - } else { - var10 = var8[9]; - var11 = var8[10]; - var47 = var8[11]; - var13 = var8[12]; - var14 = var8[13]; - var15 = var8[14]; - if(aBoolean3927) { - var16 = var8[0] * anInt3945 + var8[3] * anInt3934 + var8[6] * anInt3940 + 16384 >> 15; - var17 = var8[1] * anInt3945 + var8[4] * anInt3934 + var8[7] * anInt3940 + 16384 >> 15; - var18 = var8[2] * anInt3945 + var8[5] * anInt3934 + var8[8] * anInt3940 + 16384 >> 15; - var16 += var13; - var17 += var14; - var18 += var15; - anInt3945 = var16; - anInt3934 = var17; - anInt3940 = var18; - aBoolean3927 = false; - } - - var16 = var3 << 15 >> 7; - var17 = var4 << 15 >> 7; - var18 = var5 << 15 >> 7; - var19 = var16 * -anInt3945 + 16384 >> 15; - var20 = var17 * -anInt3934 + 16384 >> 15; - var21 = var18 * -anInt3940 + 16384 >> 15; - var22 = var19 + anInt3945; - var23 = var20 + anInt3934; - var24 = var21 + anInt3940; - int[] var25 = new int[]{var16 * var8[0] + 16384 >> 15, var16 * var8[3] + 16384 >> 15, var16 * var8[6] + 16384 >> 15, var17 * var8[1] + 16384 >> 15, var17 * var8[4] + 16384 >> 15, var17 * var8[7] + 16384 >> 15, var18 * var8[2] + 16384 >> 15, var18 * var8[5] + 16384 >> 15, var18 * var8[8] + 16384 >> 15}; - var26 = var16 * var13 + 16384 >> 15; - var27 = var17 * var14 + 16384 >> 15; - var28 = var18 * var15 + 16384 >> 15; - var26 += var22; - var27 += var23; - var28 += var24; - int[] var29 = new int[9]; - - int var31; - for(var30 = 0; var30 < 3; ++var30) { - for(var31 = 0; var31 < 3; ++var31) { - var32 = 0; - - for(var33 = 0; var33 < 3; ++var33) { - var32 += var8[var30 * 3 + var33] * var25[var31 + var33 * 3]; - } - - var29[var30 * 3 + var31] = var32 + 16384 >> 15; - } - } - - var30 = var8[0] * var26 + var8[1] * var27 + var8[2] * var28 + 16384 >> 15; - var31 = var8[3] * var26 + var8[4] * var27 + var8[5] * var28 + 16384 >> 15; - var32 = var8[6] * var26 + var8[7] * var27 + var8[8] * var28 + 16384 >> 15; - var30 += var10; - var31 += var11; - var32 += var47; - - for(var33 = 0; var33 < var9; ++var33) { - var34 = var2[var33]; - if(var34 < this.anIntArrayArray3870.length) { - var35 = this.anIntArrayArray3870[var34]; - - for(var36 = 0; var36 < var35.length; ++var36) { - var37 = var35[var36]; - if(this.aShortArray3893 == null || (var7 & this.aShortArray3893[var37]) != 0) { - var38 = var29[0] * this.anIntArray3885[var37] + var29[1] * this.anIntArray3883[var37] + var29[2] * this.anIntArray3895[var37] + 16384 >> 15; - var39 = var29[3] * this.anIntArray3885[var37] + var29[4] * this.anIntArray3883[var37] + var29[5] * this.anIntArray3895[var37] + 16384 >> 15; - var40 = var29[6] * this.anIntArray3885[var37] + var29[7] * this.anIntArray3883[var37] + var29[8] * this.anIntArray3895[var37] + 16384 >> 15; - var38 += var30; - var39 += var31; - var40 += var32; - this.anIntArray3885[var37] = var38; - this.anIntArray3883[var37] = var39; - this.anIntArray3895[var37] = var40; - } - } - } - } - } - - } else if(var1 == 5) { - if(this.anIntArrayArray3872 != null && this.aByteArray3903 != null) { - for(var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if(var11 < this.anIntArrayArray3872.length) { - var12 = this.anIntArrayArray3872[var11]; - - for(var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if(this.aShortArray3871 == null || (var7 & this.aShortArray3871[var14]) != 0) { - var15 = (this.aByteArray3903[var14] & 0xFF) + var3 * 8; - if(var15 < 0) { - var15 = 0; - } else if(var15 > 255) { - var15 = 255; - } - - this.aByteArray3903[var14] = (byte)var15; - } - } - } - } - } - - } else if(var1 == 7) { - if(this.anIntArrayArray3872 != null) { - for(var10 = 0; var10 < var9; ++var10) { - var11 = var2[var10]; - if(var11 < this.anIntArrayArray3872.length) { - var12 = this.anIntArrayArray3872[var11]; - - for(var13 = 0; var13 < var12.length; ++var13) { - var14 = var12[var13]; - if(this.aShortArray3871 == null || (var7 & this.aShortArray3871[var14]) != 0) { - var15 = this.aShortArray3869[var14] & 65535; - var16 = var15 >> 10 & 63; - var17 = var15 >> 7 & 7; - var18 = var15 & 127; - var16 = var16 + var3 & 63; - var17 += var4; - if(var17 < 0) { - var17 = 0; - } else if(var17 > 7) { - var17 = 7; - } - - var18 += var5; - if(var18 < 0) { - var18 = 0; - } else if(var18 > 127) { - var18 = 127; - } - - this.aShortArray3869[var14] = (short)(var16 << 10 | var17 << 7 | var18); - } - } - - this.aBoolean3877 = true; - } - } - } - - } - } - } - } - - final void method1889(int var1, int var2, int var3, int var4) { - int var5; - int var6; - if(var1 == 0) { - var5 = 0; - anInt3945 = 0; - anInt3934 = 0; - anInt3940 = 0; - - for(var6 = 0; var6 < this.anInt3891; ++var6) { - anInt3945 += this.anIntArray3885[var6]; - anInt3934 += this.anIntArray3883[var6]; - anInt3940 += this.anIntArray3895[var6]; - ++var5; - } - - if(var5 > 0) { - anInt3945 = anInt3945 / var5 + var2; - anInt3934 = anInt3934 / var5 + var3; - anInt3940 = anInt3940 / var5 + var4; - } else { - anInt3945 = var2; - anInt3934 = var3; - anInt3940 = var4; - } - - } else if(var1 == 1) { - for(var5 = 0; var5 < this.anInt3891; ++var5) { - this.anIntArray3885[var5] += var2; - this.anIntArray3883[var5] += var3; - this.anIntArray3895[var5] += var4; - } - - } else { - int var7; - int var8; - if(var1 == 2) { - for(var5 = 0; var5 < this.anInt3891; ++var5) { - this.anIntArray3885[var5] -= anInt3945; - this.anIntArray3883[var5] -= anInt3934; - this.anIntArray3895[var5] -= anInt3940; - if(var4 != 0) { - var6 = Class51.anIntArray840[var4]; - var7 = Class51.anIntArray851[var4]; - var8 = this.anIntArray3883[var5] * var6 + this.anIntArray3885[var5] * var7 + 32767 >> 16; - this.anIntArray3883[var5] = this.anIntArray3883[var5] * var7 - this.anIntArray3885[var5] * var6 + 32767 >> 16; - this.anIntArray3885[var5] = var8; - } - - if(var2 != 0) { - var6 = Class51.anIntArray840[var2]; - var7 = Class51.anIntArray851[var2]; - var8 = this.anIntArray3883[var5] * var7 - this.anIntArray3895[var5] * var6 + 32767 >> 16; - this.anIntArray3895[var5] = this.anIntArray3883[var5] * var6 + this.anIntArray3895[var5] * var7 + 32767 >> 16; - this.anIntArray3883[var5] = var8; - } - - if(var3 != 0) { - var6 = Class51.anIntArray840[var3]; - var7 = Class51.anIntArray851[var3]; - var8 = this.anIntArray3895[var5] * var6 + this.anIntArray3885[var5] * var7 + 32767 >> 16; - this.anIntArray3895[var5] = this.anIntArray3895[var5] * var7 - this.anIntArray3885[var5] * var6 + 32767 >> 16; - this.anIntArray3885[var5] = var8; - } - - this.anIntArray3885[var5] += anInt3945; - this.anIntArray3883[var5] += anInt3934; - this.anIntArray3895[var5] += anInt3940; - } - - } else if(var1 == 3) { - for(var5 = 0; var5 < this.anInt3891; ++var5) { - this.anIntArray3885[var5] -= anInt3945; - this.anIntArray3883[var5] -= anInt3934; - this.anIntArray3895[var5] -= anInt3940; - this.anIntArray3885[var5] = this.anIntArray3885[var5] * var2 / 128; - this.anIntArray3883[var5] = this.anIntArray3883[var5] * var3 / 128; - this.anIntArray3895[var5] = this.anIntArray3895[var5] * var4 / 128; - this.anIntArray3885[var5] += anInt3945; - this.anIntArray3883[var5] += anInt3934; - this.anIntArray3895[var5] += anInt3940; - } - - } else if (var1 == 5) { - for (var5 = 0; var5 < this.anInt3889; ++var5) { - var6 = (this.aByteArray3903[var5] & 0xFF) + var2 * 8; - if (var6 < 0) { - var6 = 0; - } else if (var6 > 255) { - var6 = 255; - } - - this.aByteArray3903[var5] = (byte) var6; - } - - } else if (var1 == 7) { - for (var5 = 0; var5 < this.anInt3889; ++var5) { - var6 = this.aShortArray3869[var5] & 65535; - var7 = var6 >> 10 & 63; - var8 = var6 >> 7 & 7; - int var9 = var6 & 127; - var7 = var7 + var2 & 63; - var8 += var3; - if (var8 < 0) { - var8 = 0; - } else if (var8 > 7) { - var8 = 7; - } - - var9 += var4; - if (var9 < 0) { - var9 = 0; - } else if (var9 > 127) { - var9 = 127; - } - - this.aShortArray3869[var5] = (short) (var7 << 10 | var8 << 7 | var9); - } - - this.aBoolean3877 = true; - } - } - } - - final void method1897(int var1, int var2, int var3) { - for(int var4 = 0; var4 < this.anInt3891; ++var4) { - this.anIntArray3885[var4] += var1; - this.anIntArray3883[var4] += var2; - this.anIntArray3895[var4] += var3; - } - - this.aBoolean3897 = false; - } - - final Model method1890(boolean var1, boolean var2, boolean var3) { - if(!var1 && aByteArray3933.length < this.anInt3889) { - aByteArray3933 = new byte[this.anInt3889 + 100]; - } - - if(!var2 && aShortArray3949.length < this.anInt3889) { - anIntArray3939 = new int[this.anInt3889 + 100]; - anIntArray3947 = new int[this.anInt3889 + 100]; - anIntArray3924 = new int[this.anInt3889 + 100]; - aShortArray3949 = new short[this.anInt3889 + 100]; - } - - return this.method1939(var1, var2, aClass140_Sub1_Sub2_3915, aByteArray3933, aShortArray3949, anIntArray3939, anIntArray3947, anIntArray3924); - } - - public Class140_Sub1_Sub2() {} - - Class140_Sub1_Sub2(Model_Sub1 var1, int var2, int var3, int var4, int var5, int var6) { - var1.method1997(); - var1.method2012(); - this.anInt3891 = var1.anInt2887; - this.anIntArray3885 = var1.anIntArray2885; - this.anIntArray3883 = var1.anIntArray2881; - this.anIntArray3895 = var1.anIntArray2892; - this.anInt3889 = var1.anInt2849; - this.anIntArray3901 = var1.anIntArray2865; - this.anIntArray3876 = var1.anIntArray2878; - this.anIntArray3887 = var1.anIntArray2864; - this.aByteArray3880 = var1.aByteArray2889; - this.aByteArray3903 = var1.aByteArray2843; - this.aByte3875 = var1.aByte2848; - this.aShortArray3869 = var1.aShortArray2870; - this.anIntArrayArray3870 = var1.anIntArrayArray2890; - this.anIntArrayArray3872 = var1.anIntArrayArray2856; - this.aShortArray3871 = var1.aShortArray2855; - this.aShortArray3893 = var1.aShortArray2893; - int var7 = (int)Math.sqrt(var4 * var4 + var5 * var5 + var6 * var6); - int var8 = var3 * var7 >> 8; - this.anIntArray3898 = new int[this.anInt3889]; - this.anIntArray3874 = new int[this.anInt3889]; - this.anIntArray3896 = new int[this.anInt3889]; - int var9; - if(var1.aShortArray2858 == null) { - this.aShortArray3908 = null; - } else { - this.aShortArray3908 = new short[this.anInt3889]; - - for(var9 = 0; var9 < this.anInt3889; ++var9) { - short var10 = var1.aShortArray2858[var9]; - if(var10 != -1 && Class51.anInterface2_838.method17(var10, 77)) { - this.aShortArray3908[var9] = var10; - } else { - this.aShortArray3908[var9] = -1; - } - } - } - - if(var1.anInt2862 > 0 && var1.aByteArray2866 != null) { - int[] var16 = new int[var1.anInt2862]; - - int var17; - for(var17 = 0; var17 < this.anInt3889; ++var17) { - if(var1.aByteArray2866[var17] != -1) { - ++var16[var1.aByteArray2866[var17] & 0xFF]; - } - } - - this.anInt3900 = 0; - - for(var17 = 0; var17 < var1.anInt2862; ++var17) { - if(var16[var17] > 0 && var1.aByteArray2857[var17] == 0) { - ++this.anInt3900; - } - } - - this.anIntArray3882 = new int[this.anInt3900]; - this.anIntArray3890 = new int[this.anInt3900]; - this.anIntArray3881 = new int[this.anInt3900]; - var17 = 0; - - int var11; - for(var11 = 0; var11 < var1.anInt2862; ++var11) { - if(var16[var11] > 0 && var1.aByteArray2857[var11] == 0) { - this.anIntArray3882[var17] = var1.aShortArray2884[var11] & 65535; - this.anIntArray3890[var17] = var1.aShortArray2846[var11] & 65535; - this.anIntArray3881[var17] = var1.aShortArray2891[var11] & 65535; - var16[var11] = var17++; - } else { - var16[var11] = -1; - } - } - - this.aByteArray3899 = new byte[this.anInt3889]; - - for(var11 = 0; var11 < this.anInt3889; ++var11) { - if(var1.aByteArray2866[var11] == -1) { - this.aByteArray3899[var11] = -1; - } else { - this.aByteArray3899[var11] = (byte)var16[var1.aByteArray2866[var11] & 0xFF]; - if(this.aByteArray3899[var11] == -1 && this.aShortArray3908 != null) { - this.aShortArray3908[var11] = -1; - } - } - } - } - - for(var9 = 0; var9 < this.anInt3889; ++var9) { - byte var18; - if(var1.aByteArray2859 == null) { - var18 = 0; - } else { - var18 = var1.aByteArray2859[var9]; - } - - byte var19; - if(var1.aByteArray2843 == null) { - var19 = 0; - } else { - var19 = var1.aByteArray2843[var9]; - } - - short var12; - if(this.aShortArray3908 == null) { - var12 = -1; - } else { - var12 = this.aShortArray3908[var9]; - } - - if(var19 == -2) { - var18 = 3; - } - - if(var19 == -1) { - var18 = 2; - } - - Class50 var13; - int var14; - Class120 var20; - if(var12 == -1) { - if(var18 == 0) { - int var15 = var1.aShortArray2870[var9] & 65535; - if(var1.aClass50Array2883 == null || var1.aClass50Array2883[this.anIntArray3901[var9]] == null) { - var13 = var1.aClass50Array2872[this.anIntArray3901[var9]]; - } else { - var13 = var1.aClass50Array2883[this.anIntArray3901[var9]]; - } - - var14 = var2 + (var4 * var13.anInt831 + var5 * var13.anInt821 + var6 * var13.anInt830) / (var8 * var13.anInt823) << 17; - this.anIntArray3898[var9] = var14 | method1940(var15, var14 >> 17); - if(var1.aClass50Array2883 == null || var1.aClass50Array2883[this.anIntArray3876[var9]] == null) { - var13 = var1.aClass50Array2872[this.anIntArray3876[var9]]; - } else { - var13 = var1.aClass50Array2883[this.anIntArray3876[var9]]; - } - - var14 = var2 + (var4 * var13.anInt831 + var5 * var13.anInt821 + var6 * var13.anInt830) / (var8 * var13.anInt823) << 17; - this.anIntArray3874[var9] = var14 | method1940(var15, var14 >> 17); - if(var1.aClass50Array2883 == null || var1.aClass50Array2883[this.anIntArray3887[var9]] == null) { - var13 = var1.aClass50Array2872[this.anIntArray3887[var9]]; - } else { - var13 = var1.aClass50Array2883[this.anIntArray3887[var9]]; - } - - var14 = var2 + (var4 * var13.anInt831 + var5 * var13.anInt821 + var6 * var13.anInt830) / (var8 * var13.anInt823) << 17; - this.anIntArray3896[var9] = var14 | method1940(var15, var14 >> 17); - } else if(var18 == 1) { - var20 = var1.aClass120Array2886[var9]; - var14 = var2 + (var4 * var20.anInt1634 + var5 * var20.anInt1635 + var6 * var20.anInt1632) / (var8 + var8 / 2) << 17; - this.anIntArray3898[var9] = var14 | method1940(var1.aShortArray2870[var9] & 65535, var14 >> 17); - this.anIntArray3896[var9] = -1; - } else if(var18 == 3) { - this.anIntArray3898[var9] = 128; - this.anIntArray3896[var9] = -1; - } else { - this.anIntArray3896[var9] = -2; - } - } else if(var18 == 0) { - if(var1.aClass50Array2883 == null || var1.aClass50Array2883[this.anIntArray3901[var9]] == null) { - var13 = var1.aClass50Array2872[this.anIntArray3901[var9]]; - } else { - var13 = var1.aClass50Array2883[this.anIntArray3901[var9]]; - } - - var14 = var2 + (var4 * var13.anInt831 + var5 * var13.anInt821 + var6 * var13.anInt830) / (var8 * var13.anInt823); - this.anIntArray3898[var9] = method1937(var14); - if(var1.aClass50Array2883 == null || var1.aClass50Array2883[this.anIntArray3876[var9]] == null) { - var13 = var1.aClass50Array2872[this.anIntArray3876[var9]]; - } else { - var13 = var1.aClass50Array2883[this.anIntArray3876[var9]]; - } - - var14 = var2 + (var4 * var13.anInt831 + var5 * var13.anInt821 + var6 * var13.anInt830) / (var8 * var13.anInt823); - this.anIntArray3874[var9] = method1937(var14); - if(var1.aClass50Array2883 == null || var1.aClass50Array2883[this.anIntArray3887[var9]] == null) { - var13 = var1.aClass50Array2872[this.anIntArray3887[var9]]; - } else { - var13 = var1.aClass50Array2883[this.anIntArray3887[var9]]; - } - - var14 = var2 + (var4 * var13.anInt831 + var5 * var13.anInt821 + var6 * var13.anInt830) / (var8 * var13.anInt823); - this.anIntArray3896[var9] = method1937(var14); - } else if(var18 == 1) { - var20 = var1.aClass120Array2886[var9]; - var14 = var2 + (var4 * var20.anInt1634 + var5 * var20.anInt1635 + var6 * var20.anInt1632) / (var8 + var8 / 2); - this.anIntArray3898[var9] = method1937(var14); - this.anIntArray3896[var9] = -1; - } else { - this.anIntArray3896[var9] = -2; - } - } - - } - - private Class140_Sub1_Sub2(Class140_Sub1_Sub2[] var1) { - boolean var3 = false; - boolean var4 = false; - boolean var5 = false; - boolean var6 = false; - this.anInt3891 = 0; - this.anInt3889 = 0; - this.anInt3900 = 0; - this.aByte3875 = -1; - - int var9; - Class140_Sub1_Sub2 var10; - for(var9 = 0; var9 < 2; ++var9) { - var10 = var1[var9]; - if(var10 != null) { - this.anInt3891 += var10.anInt3891; - this.anInt3889 += var10.anInt3889; - this.anInt3900 += var10.anInt3900; - if(var10.aByteArray3880 == null) { - if(this.aByte3875 == -1) { - this.aByte3875 = var10.aByte3875; - } - - if(this.aByte3875 != var10.aByte3875) { - var3 = true; - } - } else { - var3 = true; - } - - var4 |= var10.aByteArray3903 != null; - var5 |= var10.aShortArray3908 != null; - var6 |= var10.aByteArray3899 != null; - } - } - - this.anIntArray3885 = new int[this.anInt3891]; - this.anIntArray3883 = new int[this.anInt3891]; - this.anIntArray3895 = new int[this.anInt3891]; - this.anIntArray3901 = new int[this.anInt3889]; - this.anIntArray3876 = new int[this.anInt3889]; - this.anIntArray3887 = new int[this.anInt3889]; - this.anIntArray3898 = new int[this.anInt3889]; - this.anIntArray3874 = new int[this.anInt3889]; - this.anIntArray3896 = new int[this.anInt3889]; - if(var3) { - this.aByteArray3880 = new byte[this.anInt3889]; - } - - if(var4) { - this.aByteArray3903 = new byte[this.anInt3889]; - } - - if(var5) { - this.aShortArray3908 = new short[this.anInt3889]; - } - - if(var6) { - this.aByteArray3899 = new byte[this.anInt3889]; - } - - if(this.anInt3900 > 0) { - this.anIntArray3882 = new int[this.anInt3900]; - this.anIntArray3890 = new int[this.anInt3900]; - this.anIntArray3881 = new int[this.anInt3900]; - } - - this.aShortArray3869 = new short[this.anInt3889]; - this.anInt3891 = 0; - this.anInt3889 = 0; - this.anInt3900 = 0; - - for(var9 = 0; var9 < 2; ++var9) { - var10 = var1[var9]; - if(var10 != null) { - int var11; - for(var11 = 0; var11 < var10.anInt3889; ++var11) { - this.anIntArray3901[this.anInt3889] = var10.anIntArray3901[var11] + this.anInt3891; - this.anIntArray3876[this.anInt3889] = var10.anIntArray3876[var11] + this.anInt3891; - this.anIntArray3887[this.anInt3889] = var10.anIntArray3887[var11] + this.anInt3891; - this.anIntArray3898[this.anInt3889] = var10.anIntArray3898[var11]; - this.anIntArray3874[this.anInt3889] = var10.anIntArray3874[var11]; - this.anIntArray3896[this.anInt3889] = var10.anIntArray3896[var11]; - this.aShortArray3869[this.anInt3889] = var10.aShortArray3869[var11]; - if(var3) { - if(var10.aByteArray3880 == null) { - this.aByteArray3880[this.anInt3889] = var10.aByte3875; - } else { - this.aByteArray3880[this.anInt3889] = var10.aByteArray3880[var11]; - } - } - - if(var4 && var10.aByteArray3903 != null) { - this.aByteArray3903[this.anInt3889] = var10.aByteArray3903[var11]; - } - - if(var5) { - if(var10.aShortArray3908 == null) { - this.aShortArray3908[this.anInt3889] = -1; - } else { - this.aShortArray3908[this.anInt3889] = var10.aShortArray3908[var11]; - } - } - - if(var6) { - if(var10.aByteArray3899 == null || var10.aByteArray3899[var11] == -1) { - this.aByteArray3899[this.anInt3889] = -1; - } else { - this.aByteArray3899[this.anInt3889] = (byte)(var10.aByteArray3899[var11] + this.anInt3900); - } - } - - ++this.anInt3889; - } - - for(var11 = 0; var11 < var10.anInt3900; ++var11) { - this.anIntArray3882[this.anInt3900] = var10.anIntArray3882[var11] + this.anInt3891; - this.anIntArray3890[this.anInt3900] = var10.anIntArray3890[var11] + this.anInt3891; - this.anIntArray3881[this.anInt3900] = var10.anIntArray3881[var11] + this.anInt3891; - ++this.anInt3900; - } - - for(var11 = 0; var11 < var10.anInt3891; ++var11) { - this.anIntArray3885[this.anInt3891] = var10.anIntArray3885[var11]; - this.anIntArray3883[this.anInt3891] = var10.anIntArray3883[var11]; - this.anIntArray3895[this.anInt3891] = var10.anIntArray3895[var11]; - ++this.anInt3891; - } - } - } - - } - - static { - anIntArray3946 = new int[1600]; - anIntArrayArray3926 = new int[1600][64]; - anIntArray3935 = new int[32]; - anIntArrayArray3923 = new int[32][512]; - - anIntArray3924 = new int[1]; - anIntArray3939 = new int[1]; - anIntArray3928 = new int[4096]; - aShortArray3949 = new short[1]; - anIntArray3943 = new int[4096]; - anIntArrayArray3938 = new int[12][4096]; - anIntArray3921 = new int[4096]; - anIntArray3950 = new int[8192]; - } -} diff --git a/Client/src/main/java/org/runite/client/Class140_Sub2.java b/Client/src/main/java/org/runite/client/Class140_Sub2.java deleted file mode 100644 index c42e2cea4..000000000 --- a/Client/src/main/java/org/runite/client/Class140_Sub2.java +++ /dev/null @@ -1,210 +0,0 @@ -package org.runite.client; - -final class Class140_Sub2 extends GameObject { - - int anInt2703; - private Class127_Sub1 aClass127_Sub1_2704; - private int anInt2706 = 0; - private final int anInt2708; - int anInt2710; - private SequenceDefinition aClass142_2711; - int anInt2712; - static boolean aBoolean2713; - private int anInt2715 = -32768; - int anInt2716; - int anInt2717; - boolean aBoolean2718; - private int anInt2719 = 0; - - - private Model method1949() { - try { - GraphicDefinition var2 = GraphicDefinition.getGraphicDefinition(this.anInt2708); - - Model var3; - if(this.aBoolean2718) { - var3 = var2.method966(-1, -1, 0); - } else { - int anInt2714 = -1; - var3 = var2.method966(anInt2714, this.anInt2706, this.anInt2719); - } - - return var3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bh.E(" + false + ')'); - } - } - - static void method1950(Class140_Sub4 var0) { - try { - int var2 = var0.anInt2800 - Class44.anInt719; - int var3 = 128 * var0.anInt2784 - -(64 * var0.getSize()); - int var4 = 128 * var0.anInt2835 - -(var0.getSize() * 64); - if(0 == var0.anInt2840) { - var0.anInt2806 = 1024; - } - - var0.xAxis += (-var0.xAxis + var3) / var2; - var0.zAxis += (var4 + -var0.zAxis) / var2; - if(var0.anInt2840 == 1) { - var0.anInt2806 = 1536; - } - - var0.anInt2824 = 0; - if(var0.anInt2840 == 2) { - var0.anInt2806 = 0; - } - - if(var0.anInt2840 == 3) { - var0.anInt2806 = 512; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bh.F(" + (var0 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static int method1951(int var0) { - try { - - return var0 >>> 8; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bh.D(" + var0 + ',' + (byte) -115 + ')'); - } - } - - static void method1952(int var0, int var2, int var3, int var4, int var5, int var6, int var7) { - try { - int var8; - int var9; - if(HDToolKit.highDetail) { - var8 = -334 + var2; - if(var8 >= 0) { - if(var8 > 100) { - var8 = 100; - } - } else { - var8 = 0; - } - - var9 = var8 * (-Unsorted.aShort46 + ObjectDefinition.aShort1535) / 100 + Unsorted.aShort46; - var4 = var9 * var4 >> 8; - } - - var8 = -var7 + 2048 & 0x7FF; - var9 = 2047 & -var5 + 2048; - int var10 = 0; - int var12 = var4; - int var11 = 0; - int var13; - int var14; - if(var8 != 0) { - var14 = Class51.anIntArray851[var8]; - var13 = Class51.anIntArray840[var8]; - var11 = var13 * -var4 >> 16; - var12 = var14 * var4 >> 16; - } - - if(0 != var9) { - var13 = Class51.anIntArray840[var9]; - var14 = Class51.anIntArray851[var9]; - var10 = var13 * var12 >> 16; - var12 = var12 * var14 >> 16; - } - - Class139.anInt1823 = var7; - TextureOperation28.anInt3315 = var5; - Class77.anInt1111 = var6 + -var12; - NPC.anInt3995 = var0 + -var10; - Class7.anInt2162 = -var11 + var3; - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "bh.G(" + var0 + ',' + -1907397104 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - static WorldListEntry method1953() { - try { - - Class3_Sub6.anInt2291 = 0; - return ItemDefinition.method1107(5422); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "bh.H(" + (byte) 124 + ')'); - } - } - - final int method1871() { - try { - return this.anInt2715; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "bh.MA()"); - } - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - try { - Model var13 = this.method1949(); - if(var13 != null) { - var13.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2704); - this.anInt2715 = var13.method1871(); - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "bh.IA(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var11 + ',' + (var12 != null?"{...}":"null") + ')'); - } - } - - final void method1955(int var2) { - try { - if(!this.aBoolean2718) { - - this.anInt2719 += var2; - - while(this.aClass142_2711.duration[this.anInt2706] < this.anInt2719) { - this.anInt2719 -= this.aClass142_2711.duration[this.anInt2706]; - ++this.anInt2706; - if(this.anInt2706 >= this.aClass142_2711.frames.length) { - this.aBoolean2718 = true; - break; - } - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bh.A(" + true + ',' + var2 + ')'); - } - } - - static void method1956(int var0, int var1, int var2, int var3) { - Class3_Sub2 var4 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if(var4 != null) { - Class19 var5 = var4.aClass19_2233; - if(var5 != null) { - var5.anInt430 = var5.anInt430 * var3 / 16; - var5.anInt426 = var5.anInt426 * var3 / 16; - } - } - } - - final void method1867(int var1, int var2, int var3, int var4, int var5) {} - - Class140_Sub2(int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - try { - this.anInt2710 = var4; - this.anInt2716 = var3; - this.anInt2717 = var2; - this.anInt2703 = var7 + var6; - this.anInt2708 = var1; - this.anInt2712 = var5; - int var8 = GraphicDefinition.getGraphicDefinition(this.anInt2708).anInt542; - if(-1 == var8) { - this.aBoolean2718 = true; - } else { - this.aBoolean2718 = false; - this.aClass142_2711 = SequenceDefinition.getAnimationDefinition(var8); - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "bh.(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class140_Sub3.java b/Client/src/main/java/org/runite/client/Class140_Sub3.java deleted file mode 100644 index 9c72d967a..000000000 --- a/Client/src/main/java/org/runite/client/Class140_Sub3.java +++ /dev/null @@ -1,512 +0,0 @@ -package org.runite.client; - - -final class Class140_Sub3 extends GameObject { - - static LDIndexedSprite aClass109_Sub1_2631 = null; - private int anInt2720 = 0; - private boolean aBoolean2721 = false; - private SequenceDefinition aClass142_2722; - private final int anInt2724; - private int anInt2725 = 0; - private int anInt2726; - private boolean aBoolean2728 = true; - private final int objectId; - private final int anInt2730; - private final int anInt2732; - private int anInt2733; - private final int type; - private final int anInt2736; - private LDIndexedSprite aClass109_Sub1_2738 = null; - private Class127_Sub1 aClass127_Sub1_2742; - static volatile int anInt2743 = 0; - static int anInt2745 = 0; - private int anInt2746; - private int anInt2748 = 0; - private int anInt2749; - private int anInt2750 = -1; - private int anInt2752 = -1; - - - static Model method1957(int var0, boolean var1, SequenceDefinition var2, int var3, int var4, int var5, int var6, int var7, Model var8, int var9, int var10, int var11, int var12) { - try { - long var14 = ((long) var4 << 48) + (var7 + ((long) var0 << 16) - -((long) var12 << 24)) + ((long) var6 << 32); - Model var16 = (Model) Class158_Sub1.aReferenceCache_2982.get(var14); - int var21; - int var23; - int var25; - int var24; - int var28; - if (var16 == null) { - byte var17; - if (1 == var7) { - var17 = 9; - } else if (var7 == 2) { - var17 = 12; - } else if (var7 == 3) { - var17 = 15; - } else if (4 == var7) { - var17 = 18; - } else { - var17 = 21; - } - - int[] var19 = new int[]{64, 96, 128}; - byte var18 = 3; - Model_Sub1 var20 = new Model_Sub1(1 + var18 * var17, -var17 + var17 * var18 * 2); - var21 = var20.method2014(0, 0); - int[][] var22 = new int[var18][var17]; - - for (var23 = 0; var18 > var23; ++var23) { - var24 = var19[var23]; - var25 = var19[var23]; - - for (int var26 = 0; var26 < var17; ++var26) { - int var27 = (var26 << 11) / var17; - int var29 = var5 - -(Class51.anIntArray851[var27] * var25) >> 16; - var28 = var3 + Class51.anIntArray840[var27] * var24 >> 16; - var22[var23][var26] = var20.method2014(var28, var29); - } - } - - for (var23 = 0; var23 < var18; ++var23) { - var24 = (256 * var23 - -128) / var18; - var25 = 256 + -var24; - byte var38 = (byte) (var12 * var24 + var0 * var25 >> 8); - short var39 = (short) (((var6 & 127) * var25 + (127 & var4) * var24 & 32512) + (var25 * (var6 & 896) + var24 * (var4 & 896) & 229376) + (var24 * (var4 & '\ufc00') + ('\ufc00' & var6) * var25 & 16515072) >> 8); - - for (var28 = 0; var28 < var17; ++var28) { - if (var23 == 0) { - var20.method2005(var21, var22[0][(1 + var28) % var17], var22[0][var28], var39, var38); - } else { - var20.method2005(var22[var23 - 1][var28], var22[var23 + -1][(var28 + 1) % var17], var22[var23][(var28 - -1) % var17], var39, var38); - var20.method2005(var22[-1 + var23][var28], var22[var23][(1 + var28) % var17], var22[var23][var28], var39, var38); - } - } - } - - var16 = var20.method2008(64, 768, -50, -10, -50); - Class158_Sub1.aReferenceCache_2982.put(var16, var14); - } - - int var32 = var7 * 64 + -1; - int var33 = -var32; - int var31 = -var32; - int var34 = var32; - int var35 = var8.method1884(); - Class3_Sub28_Sub5 var40 = null; - var23 = var8.method1883(); - var24 = var8.method1898(); - var25 = var8.method1872(); - if (var2 != null) { - var10 = var2.frames[var10]; - int frame = var10 >> 16; - // System.out.println(var2.animId + " Roar " + (var10 >> 16) + ", " + (var10 & 65535) + ", " + Arrays.toString(var2.frames)); - var40 = Class3_Sub9.method133(frame); //NPC render animating - var10 &= 65535; - } - - var21 = var32; - if (var1) { - if (1664 < var9 || 384 > var9) { - var31 -= 128; - } - - if (var9 > 1152 && var9 < 1920) { - var34 = var32 + 128; - } - - if (640 < var9 && var9 < 1408) { - var21 = var32 + 128; - } - - if (var9 > 128 && var9 < 896) { - var33 -= 128; - } - } - - if (var21 < var25) { - var25 = var21; - } - - if (var33 > var35) { - var35 = var33; - } - - if (var24 < var31) { - var24 = var31; - } - - if (var34 < var23) { - var23 = var34; - } - - if (null == var40) { - var16 = var16.method1882(true, true, true); - var16.resize((var23 + -var35) / 2, 128, (var25 - var24) / 2); - var16.method1897((var35 + var23) / 2, 0, (var24 - -var25) / 2); - } else { - var16 = var16.method1882(!var40.method559(var10), !var40.method561(var10, (byte) 115), true); - var16.resize((var23 + -var35) / 2, 128, (var25 + -var24) / 2); - var16.method1897((var35 + var23) / 2, 0, (var24 + var25) / 2); - var16.method1877(var40, var10); - } - - if (var9 != 0) { - var16.method1876(var9); - } - - if (HDToolKit.highDetail) { - Class140_Sub1_Sub1 var36 = (Class140_Sub1_Sub1) var16; - if (var11 != Class121.method1736(WorldListCountry.localPlane, (byte) -49 ^ -50, var3 - -var35, var24 + var5) || var11 != Class121.method1736(WorldListCountry.localPlane, 1, var23 + var3, var5 - -var25)) { - for (var28 = 0; var28 < var36.anInt3823; ++var28) { - var36.anIntArray3845[var28] += -var11 + Class121.method1736(WorldListCountry.localPlane, Unsorted.bitwiseXOR((byte) -49, -50), var36.anIntArray3822[var28] - -var3, var5 + var36.anIntArray3848[var28]); - } - - var36.aClass6_3835.aBoolean98 = false; - var36.aClass121_3839.aBoolean1640 = false; - } - } else { - Class140_Sub1_Sub2 var37 = (Class140_Sub1_Sub2) var16; - if (var11 != Class121.method1736(WorldListCountry.localPlane, 1, var3 - -var35, var5 - -var24) || var11 != Class121.method1736(WorldListCountry.localPlane, 1, var3 + var23, var5 - -var25)) { - for (var28 = 0; var28 < var37.anInt3891; ++var28) { - var37.anIntArray3883[var28] += -var11 + Class121.method1736(WorldListCountry.localPlane, 1, var3 + var37.anIntArray3885[var28], var5 + var37.anIntArray3895[var28]); - } - - var37.aBoolean3897 = false; - } - } - - return var16; - } catch (RuntimeException var30) { - throw ClientErrorException.clientError(var30, "dc.E(" + var0 + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + (var8 != null ? "{...}" : "null") + ',' + var9 + ',' + var10 + ',' + var11 + ',' + var12 + ',' + (byte) -49 + ')'); - } - } - - final void method1867(int var1, int var2, int var3, int var4, int var5) { - try { - if (HDToolKit.highDetail) { - this.method1962(true); - } else { - this.method1961(var5, var4); - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "dc.IB(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static void method1959(boolean var3) { - try { - AudioChannel.threadPriority = 2; - AudioChannel.stereo = var3; - Class21.sampleRate = 22050; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "dc.D(" + 256 + ',' + 2 + ',' + 22050 + ',' + var3 + ')'); - } - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - try { - GameObject var13 = this.method1963(); - if (null != var13) { - var13.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2742); - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "dc.IA(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var11 + ',' + (var12 != null ? "{...}" : "null") + ')'); - } - } - - final void method1960() { - try { - if (this.aClass109_Sub1_2738 != null) { - Class141.method2047(this.aClass109_Sub1_2738, this.anInt2725, this.anInt2720, this.anInt2748); - } - - this.anInt2750 = -1; - this.anInt2752 = -1; - this.aClass109_Sub1_2738 = null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "dc.F(" + -1 + ')'); - } - } - - protected final void finalize() { - } - - private void method1961(int var1, int var2) { - try { - if (this.aClass142_2722 != null) { - int var4 = Class44.anInt719 - this.anInt2749; - if (var4 > 100 && this.aClass142_2722.anInt1865 > 0) { - int var5; - for (var5 = this.aClass142_2722.frames.length - this.aClass142_2722.anInt1865; var5 > this.anInt2726 && var4 > this.aClass142_2722.duration[this.anInt2726]; ++this.anInt2726) { - var4 -= this.aClass142_2722.duration[this.anInt2726]; - } - - if (var5 <= this.anInt2726) { - int var6 = 0; - - for (int var7 = var5; var7 < this.aClass142_2722.frames.length; ++var7) { - var6 += this.aClass142_2722.duration[var7]; - } - - var4 %= var6; - } - - this.anInt2733 = 1 + this.anInt2726; - if (this.anInt2733 >= this.aClass142_2722.frames.length) { - this.anInt2733 -= this.aClass142_2722.anInt1865; - if (this.anInt2733 < 0 || this.aClass142_2722.frames.length <= this.anInt2733) { - this.anInt2733 = -1; - } - } - } - - while (this.aClass142_2722.duration[this.anInt2726] < var4) { - Unsorted.method1470(var1, this.aClass142_2722, var2, false, this.anInt2726); - var4 -= this.aClass142_2722.duration[this.anInt2726]; - ++this.anInt2726; - if (this.anInt2726 >= this.aClass142_2722.frames.length) { - this.anInt2726 -= this.aClass142_2722.anInt1865; - if (0 > this.anInt2726 || this.anInt2726 >= this.aClass142_2722.frames.length) { - this.aClass142_2722 = null; - break; - } - } - - this.anInt2733 = this.anInt2726 - -1; - if (this.anInt2733 >= this.aClass142_2722.frames.length) { - this.anInt2733 -= this.aClass142_2722.anInt1865; - if (this.anInt2733 < 0 || this.aClass142_2722.frames.length <= this.anInt2733) { - this.anInt2733 = -1; - } - } - } - - this.anInt2746 = var4; - this.anInt2749 = -var4 + Class44.anInt719; - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "dc.A(" + var1 + ',' + var2 + ',' + -101 + ')'); - } - } - - private GameObject method1962(boolean var1) { - try { - boolean var3 = Class58.anIntArrayArrayArray914 != Class44.anIntArrayArrayArray723; - ObjectDefinition var4 = ObjectDefinition.getObjectDefinition(this.objectId); - int var5 = var4.animationId; - if (null != var4.ChildrenIds) { - var4 = var4.method1685(0); - } - - if (null == var4) { - if (HDToolKit.highDetail && !var3) { - this.method1960(); - } - - return null; - } else { - int var6; - if (Class158.paramGameTypeID != 0 && this.aBoolean2721 && (null == this.aClass142_2722 || var4.animationId != this.aClass142_2722.animId)) { - var6 = var4.animationId; - if (var4.animationId == -1) { - var6 = var5; - } - - if (var6 == -1) { - this.aClass142_2722 = null; - } else { - this.aClass142_2722 = SequenceDefinition.getAnimationDefinition(var6); - } - - if (null != this.aClass142_2722) { - if (var4.aBoolean1492 && -1 != this.aClass142_2722.anInt1865) { - this.anInt2726 = (int) (Math.random() * (double) this.aClass142_2722.frames.length); - this.anInt2749 -= (int) (Math.random() * (double) this.aClass142_2722.duration[this.anInt2726]); - } else { - this.anInt2726 = 0; - this.anInt2749 = Class44.anInt719 + -1; - } - } - } - - var6 = this.anInt2724 & 3; - int var7; - int var8; - if (var6 == 1 || var6 == 3) { - var8 = var4.SizeX; - var7 = var4.SizeY; - } else { - var7 = var4.SizeX; - var8 = var4.SizeY; - } - - int var10 = this.anInt2736 - -(1 + var7 >> 1); - int var9 = (var7 >> 1) + this.anInt2736; - int var11 = (var8 >> 1) + this.anInt2730; - int var12 = (var8 - -1 >> 1) + this.anInt2730; - this.method1961(128 * var11, var9 * 128); - boolean var13 = !var3 && var4.aBoolean1503 && (var4.objectId != this.anInt2750 || (this.anInt2752 != this.anInt2726 || this.aClass142_2722 != null && (this.aClass142_2722.aBoolean1872 || ClientCommands.tweeningEnabled) && this.anInt2733 != this.anInt2726) && Unsorted.anInt1137 >= 2); - if (var1 && !var13) { - return null; - } else { - int[][] var14 = Class44.anIntArrayArrayArray723[this.anInt2732]; - int var15 = var14[var10][var12] + var14[var9][var12] + var14[var9][var11] + var14[var10][var11] >> 2; - int var16 = (var7 << 6) + (this.anInt2736 << 7); - int var17 = (var8 << 6) + (this.anInt2730 << 7); - int[][] var18 = null; - if (var3) { - var18 = Class58.anIntArrayArrayArray914[0]; - } else if (this.anInt2732 < 3) { - var18 = Class44.anIntArrayArrayArray723[1 + this.anInt2732]; - } - - if (HDToolKit.highDetail && var13) { - Class141.method2047(this.aClass109_Sub1_2738, this.anInt2725, this.anInt2720, this.anInt2748); - } - - boolean var19 = null == this.aClass109_Sub1_2738; - Class136 var20; - if (this.aClass142_2722 == null) { - var20 = var4.method1696(this.anInt2724, var16, var14, this.type, var15, var18, false, var19 ? aClass109_Sub1_2631 : this.aClass109_Sub1_2738, (byte) -128, var13, var17); - } else { - var20 = var4.method1697(var17, var16, !var19 ? this.aClass109_Sub1_2738 : aClass109_Sub1_2631, var15, this.aClass142_2722, this.anInt2724, var14, var13, this.anInt2726, -2 ^ -8310, var18, this.anInt2733, this.type, this.anInt2746); - } - - if (null == var20) { - return null; - } else { - if (HDToolKit.highDetail && var13) { - if (var19) { - aClass109_Sub1_2631 = var20.aClass109_Sub1_1770; - } - - int var21 = 0; - if (this.anInt2732 != 0) { - int[][] var22 = Class44.anIntArrayArrayArray723[0]; - var21 = var15 - (var22[var10][var11] + var22[var9][var11] - (-var22[var9][var12] - var22[var10][var12]) >> 2); - } - - LDIndexedSprite var24 = var20.aClass109_Sub1_1770; - if (this.aBoolean2728 && Class141.method2049(var24, var16, var21, var17)) { - this.aBoolean2728 = false; - } - - if (!this.aBoolean2728) { - Class141.method2051(var24, var16, var21, var17); - this.aClass109_Sub1_2738 = var24; - this.anInt2748 = var17; - if (var19) { - aClass109_Sub1_2631 = null; - } - - this.anInt2720 = var21; - this.anInt2725 = var16; - } - - this.anInt2750 = var4.objectId; - this.anInt2752 = this.anInt2726; - } - - return var20.aClass140_1777; - } - } - } - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "dc.H(" + var1 + ',' + -2 + ')'); - } - } - - final GameObject method1963() { - try { - return this.method1962(false); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "dc.C(" + 3 + ')'); - } - } - - final int method1871() { - return -32768; - } - - Class140_Sub3(int objectId, int type, int var3, int var4, int var5, int var6, int animationId, boolean var8, GameObject var9) { - try { - this.anInt2732 = var4; - this.anInt2724 = var3; - this.anInt2736 = var5; - this.type = type; - this.objectId = objectId; - this.anInt2730 = var6; - ObjectDefinition var10; - if (HDToolKit.highDetail && null != var9) { - if (var9 instanceof Class140_Sub3) { - ((Class140_Sub3) var9).method1960(); - } else { - var10 = ObjectDefinition.getObjectDefinition(this.objectId); - if (var10.ChildrenIds != null) { - var10 = var10.method1685(0); - } - - if (null != var10) { - Class8.method840(var10, (byte) -76, 0, this.anInt2724, 0, this.type, this.anInt2736, this.anInt2730, this.anInt2732); - } - } - } - - if (-1 != animationId) { - this.aClass142_2722 = SequenceDefinition.getAnimationDefinition(animationId); - this.anInt2726 = 0; - if (1 >= this.aClass142_2722.frames.length) { - this.anInt2733 = 0; - } else { - this.anInt2733 = 1; - } - - this.anInt2746 = 1; - this.anInt2749 = -1 + Class44.anInt719; - if (this.aClass142_2722.delayType == 0 && null != var9 && var9 instanceof Class140_Sub3) { - Class140_Sub3 var12 = (Class140_Sub3) var9; - if (this.aClass142_2722 == var12.aClass142_2722) { - this.anInt2726 = var12.anInt2726; - this.anInt2749 = var12.anInt2749; - this.anInt2746 = var12.anInt2746; - this.anInt2733 = var12.anInt2733; - return; - } - } - - if (var8 && this.aClass142_2722.anInt1865 != -1) { - this.anInt2726 = (int) (Math.random() * (double) this.aClass142_2722.frames.length); - this.anInt2733 = this.anInt2726 - -1; - if (this.aClass142_2722.frames.length <= this.anInt2733) { - this.anInt2733 -= this.aClass142_2722.anInt1865; - if (this.anInt2733 < 0 || this.aClass142_2722.frames.length <= this.anInt2733) { - this.anInt2733 = -1; - } - } - - this.anInt2746 = 1 - -((int) (Math.random() * (double) this.aClass142_2722.duration[this.anInt2726])); - this.anInt2749 = -this.anInt2746 + Class44.anInt719; - } - } - - if (HDToolKit.highDetail && var9 != null) { - this.method1962(true); - } - - if (var9 == null) { - var10 = ObjectDefinition.getObjectDefinition(this.objectId); - if (null != var10.ChildrenIds) { - this.aBoolean2721 = true; - } - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "dc.(" + objectId + ',' + type + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + animationId + ',' + var8 + ',' + (var9 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class140_Sub4.java b/Client/src/main/java/org/runite/client/Class140_Sub4.java deleted file mode 100644 index bc9510ef0..000000000 --- a/Client/src/main/java/org/runite/client/Class140_Sub4.java +++ /dev/null @@ -1,564 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -public abstract class Class140_Sub4 extends GameObject { - - int textEffect = 0; - private int anInt2754 = 0; - int[] anIntArray2755 = new int[10]; - static int anInt2756; - private int anInt2757 = 0; - int anInt2758 = 0; - int anInt2759; - int anInt2760 = 0; - int anInt2761 = 0; - int anInt2762 = 0; - int renderAnimationId = -1; - int anInt2764 = -1; - private int anInt2766 = 0; - int[] anIntArray2767 = new int[10]; - int[] anIntArray2768 = new int[4]; - boolean aBoolean2769 = false; - int anInt2771 = -1; - int anInt2772 = -1; - int anInt2773 = 0; - int anInt2775; - int anInt2776 = -1; - int anInt2777; - int anInt2778 = 0; - int anInt2779 = 32; - int anInt2780 = 0; - int anInt2781 = -1000; - int anInt2782; - private boolean aBoolean2783 = false; - int anInt2784; - int anInt2785; - int anInt2786 = 0; - private int anInt2787 = 0; - int anInt2788; - int anInt2789 = 0; - int anInt2790; - int anInt2791 = 0; - static ReferenceCache aReferenceCache_2792 = new ReferenceCache(64); - int anInt2793 = -1; - static int[] anIntArray2794 = new int[100]; - byte[] aByteArray2795 = new byte[10]; - Object anObject2796; - int anInt2797 = 0; - int anInt2798; - int anInt2799; - int anInt2800; - Class127_Sub1 aClass127_Sub1_2801; - int anInt2802 = 0; - int anInt2803 = 0; - private int anInt2804 = 0; - int anInt2805 = 0; - int anInt2806; - private boolean aBoolean2807 = false; - int anInt2808 = 0; - Class145[] aClass145Array2809 = new Class145[12]; - boolean aBoolean2810 = false; - int anInt2811 = 0; - int anInt2812; - int anInt2813 = 0; - int textCycle = 100; - int[] anIntArray2815 = new int[4]; - int anInt2816 = 0; - int anInt2817; - int anInt2818; - public int xAxis; - int anInt2820 = -32768; - int anInt2821 = 0; - private int size = 1; - int anInt2823; - int anInt2824 = 0; - RSString textSpoken = null; - int anInt2826 = -1; - private int anInt2827 = 0; - int anInt2828 = 0; - public int zAxis; - private int anInt2830 = 0; - int anInt2831; - int anInt2832 = 0; - int anInt2833; - private int anInt2834 = 0; - int anInt2835; - int[] anIntArray2836 = new int[4]; - int textColor = 0; - int anInt2838 = 0; - public static AbstractSprite[] aAbstractSpriteArray2839; - int anInt2840; - int anInt2842 = -1; - - - final RenderAnimationDefinition getRenderAnimationType() { - int ID = this.getRenderAnimationId(); - if (ID == -1) { - return PacketParser.aClass16_84; - } else { - return Class3_Sub10.getRenderAnimationDefinition(ID); - } - } - - boolean hasDefinitions() { - try { - return false; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fe.L(" + (byte) 17 + ')'); - } - } - - final void method1967(int var2, int var3, int var4, boolean var5) { - try { - if (this.anInt2771 != -1 && SequenceDefinition.getAnimationDefinition(this.anInt2771).priority == 1) { - this.anInt2771 = -1; - } - - if (!var5) { - int var6 = var3 + -this.anIntArray2767[0]; - int var7 = var4 + -this.anIntArray2755[0]; - if (var6 >= -8 && 8 >= var6 && var7 >= -8 && var7 <= 8) { - if (this.anInt2816 < 9) { - ++this.anInt2816; - } - - for (int var8 = this.anInt2816; var8 > 0; --var8) { - this.anIntArray2767[var8] = this.anIntArray2767[-1 + var8]; - this.anIntArray2755[var8] = this.anIntArray2755[var8 + -1]; - this.aByteArray2795[var8] = this.aByteArray2795[var8 - 1]; - } - - this.aByteArray2795[0] = 1; - this.anIntArray2767[0] = var3; - this.anIntArray2755[0] = var4; - return; - } - } - - this.anInt2824 = 0; - this.anIntArray2767[0] = var3; - this.anIntArray2755[0] = var4; - this.anInt2816 = 0; - this.anInt2811 = 0; - this.zAxis = 64 * var2 + this.anIntArray2755[0] * 128; - this.xAxis = var2 * 64 + 128 * this.anIntArray2767[0]; - - if (HDToolKit.highDetail && Class102.player == this) { - TextureOperation31.method236(); - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "fe.J(" + -2 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - final void walkStep(int var1, byte var2, int var3) { - try { - int var4 = this.anIntArray2767[0]; - int var5 = this.anIntArray2755[0]; - if (0 == var3) { - --var4; - ++var5; - } - - if (this.anInt2771 != -1 && 1 == SequenceDefinition.getAnimationDefinition(this.anInt2771).priority) { - this.anInt2771 = -1; - } - - if (this.anInt2816 < 9) { - ++this.anInt2816; - } - - int var6; - for (var6 = this.anInt2816; var6 > 0; --var6) { - this.anIntArray2767[var6] = this.anIntArray2767[-1 + var6]; - this.anIntArray2755[var6] = this.anIntArray2755[-1 + var6]; - this.aByteArray2795[var6] = this.aByteArray2795[-1 + var6]; - } - - if (var3 == 1) { - ++var5; - } - - this.aByteArray2795[0] = (byte) var1; - if (2 == var3) { - ++var5; - ++var4; - } - - if (var3 == 3) { - --var4; - } - - if (4 == var3) { - ++var4; - } - - if (5 == var3) { - --var5; - --var4; - } - - if (var3 == 6) { - --var5; - } - - if (var3 == 7) { - --var5; - ++var4; - } - - this.anIntArray2767[0] = var4; - this.anIntArray2755[0] = var5; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "fe.E(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - final void method1969(byte var1, Model var2, int var3) { - try { - if (var1 <= 33) { - this.method1971(null, (byte) -26); - } - - Class3_Sub28_Sub9.anInt3623 = 0; - TextureOperation15.anInt3198 = 0; - TextureOperation16.anInt3111 = 0; - RenderAnimationDefinition var4 = this.getRenderAnimationType(); - int var5 = var4.hill_width; - int var6 = var4.hill_height; - if (var5 != 0 && var6 != 0) { - int var7 = Class51.anIntArray840[var3]; - int var8 = Class51.anIntArray851[var3]; - int var9 = -var5 / 2; - int var10 = -var6 / 2; - int var12 = -(var9 * var7) + var10 * var8 >> 16; - int var11 = var7 * var10 - -(var8 * var9) >> 16; - int var13 = Class121.method1736(WorldListCountry.localPlane, 1, var11 + this.xAxis, this.zAxis + var12); - int var14 = var5 / 2; - int var15 = -var6 / 2; - int var16 = var14 * var8 + var15 * var7 >> 16; - int var20 = var6 / 2; - int var17 = var15 * var8 + -(var14 * var7) >> 16; - int var18 = Class121.method1736(WorldListCountry.localPlane, 1, var16 + this.xAxis, this.zAxis - -var17); - int var19 = -var5 / 2; - int var22 = -(var7 * var19) + var20 * var8 >> 16; - int var25 = var6 / 2; - int var24 = var5 / 2; - int var21 = var7 * var20 - -(var8 * var19) >> 16; - int var27 = var25 * var8 - var7 * var24 >> 16; - int var26 = var7 * var25 + var8 * var24 >> 16; - int var23 = Class121.method1736(WorldListCountry.localPlane, 1, this.xAxis + var21, var22 + this.zAxis); - int var29 = var13 < var18 ? var13 : var18; - int var28 = Class121.method1736(WorldListCountry.localPlane, 1, var26 + this.xAxis, var27 + this.zAxis); - int var30 = var28 > var23 ? var23 : var28; - int var31 = var28 > var18 ? var18 : var28; - int var32 = var23 <= var13 ? var23 : var13; - TextureOperation15.anInt3198 = 2047 & (int) (325.95D * Math.atan2(var29 - var30, var6)); - if (TextureOperation15.anInt3198 != 0) { - var2.method1896(TextureOperation15.anInt3198); - } - - Class3_Sub28_Sub9.anInt3623 = 2047 & (int) (325.95D * Math.atan2(-var31 + var32, var5)); - if (Class3_Sub28_Sub9.anInt3623 != 0) { - var2.method1886(Class3_Sub28_Sub9.anInt3623); - } - - TextureOperation16.anInt3111 = var28 + var13; - if (TextureOperation16.anInt3111 > var23 + var18) { - TextureOperation16.anInt3111 = var23 + var18; - } - - TextureOperation16.anInt3111 = (TextureOperation16.anInt3111 >> 1) - this.anInt2831; - if (TextureOperation16.anInt3111 != 0) { - var2.method1897(0, TextureOperation16.anInt3111, 0); - } - } - - } catch (RuntimeException var33) { - throw ClientErrorException.clientError(var33, "fe.M(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ')'); - } - } - - final void addHit(int type, int damage, int clientClock) { - try { - for (int var5 = 0; var5 < 4; ++var5) { - if (damage >= this.anIntArray2768[var5]) { - this.anIntArray2836[var5] = clientClock; - this.anIntArray2815[var5] = type; - this.anIntArray2768[var5] = 70 + damage; - return; - } - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "fe.G(" + type + ',' + -8 + ',' + damage + ',' + clientClock + ')'); - } - } - - final void method1971(Model var1, byte var2) { - try { - if (var2 > -102) { - this.setSize(1, -96); - } - - RenderAnimationDefinition var3 = this.getRenderAnimationType(); - if (0 != var3.roll_target_angle || 0 != var3.pitch_target_angle) { - int var4 = 0; - int var5 = 0; - if (this.aBoolean2769 && this.anInt2821 != 0) { - var5 = var3.pitch_target_angle; - if (this.anInt2821 >= 0) { - var4 = var3.roll_target_angle; - } else { - var4 = -var3.roll_target_angle; - } - } - - int var6; - int var7; - int var8; - int var9; - if (this.anInt2827 != var4) { - this.anInt2827 = var4; - if (this.anInt2754 > 0 && this.anInt2787 < var4) { - var6 = this.anInt2754 * this.anInt2754 / (2 * var3.roll_acceleration); - var7 = -this.anInt2787 + var4; - if (var6 <= var7) { - this.aBoolean2807 = true; - this.anInt2804 = (var4 + this.anInt2787 - var6) / 2; - var8 = var3.roll_max_speed * var3.roll_max_speed / (var3.roll_acceleration * 2); - var9 = -var8 + var4; - if (this.anInt2804 < var9) { - this.anInt2804 = var9; - } - } else { - this.aBoolean2807 = false; - } - } else if (this.anInt2754 < 0 && var4 < this.anInt2787) { - var6 = this.anInt2754 * this.anInt2754 / (var3.roll_acceleration * 2); - var7 = var4 - this.anInt2787; - if (var7 >= var6) { - this.aBoolean2807 = true; - this.anInt2804 = (this.anInt2787 + var6 - -var4) / 2; - var8 = var3.roll_max_speed * var3.roll_max_speed / (2 * var3.roll_acceleration); - var9 = var8 + var4; - if (this.anInt2804 > var9) { - this.anInt2804 = var9; - } - } else { - this.aBoolean2807 = false; - } - } else { - this.aBoolean2807 = false; - } - } - - if (this.anInt2754 == 0) { - var6 = -this.anInt2787 + this.anInt2827; - if (-var3.roll_acceleration < var6 && var6 < var3.roll_acceleration) { - this.anInt2787 = this.anInt2827; - } else { - this.aBoolean2807 = true; - var7 = var3.roll_max_speed * var3.roll_max_speed / (var3.roll_acceleration * 2); - this.anInt2804 = (this.anInt2827 + this.anInt2787) / 2; - if (var6 >= 0) { - var8 = -var7 + this.anInt2827; - this.anInt2754 = var3.roll_acceleration; - if (this.anInt2804 < var8) { - this.anInt2804 = var8; - } - } else { - this.anInt2754 = -var3.roll_acceleration; - var8 = var7 + this.anInt2827; - if (var8 < this.anInt2804) { - this.anInt2804 = var8; - } - } - } - } else if (this.anInt2754 > 0) { - if (this.anInt2804 <= this.anInt2787) { - this.aBoolean2807 = false; - } - - if (!this.aBoolean2807) { - this.anInt2754 -= var3.roll_acceleration; - if (this.anInt2754 < 0) { - this.anInt2754 = 0; - } - } else if (var3.roll_max_speed > this.anInt2754) { - this.anInt2754 += var3.roll_acceleration; - } - } else { - if (this.anInt2787 <= this.anInt2804) { - this.aBoolean2807 = false; - } - - if (this.aBoolean2807) { - if (-var3.roll_max_speed < this.anInt2754) { - this.anInt2754 -= var3.roll_acceleration; - } - } else { - this.anInt2754 += var3.roll_acceleration; - if (this.anInt2754 > 0) { - this.anInt2754 = 0; - } - } - } - - this.anInt2787 += this.anInt2754; - if (this.anInt2787 != 0) { - var6 = (this.anInt2787 & '\ufff1') >> 5; - var7 = var1.method1871() / 2; - var1.method1897(0, -var7, 0); - var1.method1886(var6); - var1.method1897(0, var7, 0); - } - - if (var5 != this.anInt2766) { - this.anInt2766 = var5; - if (this.anInt2830 > 0 && this.anInt2834 < var5) { - var6 = this.anInt2830 * this.anInt2830 / (2 * var3.pitch_acceleration); - var7 = -this.anInt2834 + var5; - if (var6 > var7) { - this.aBoolean2783 = false; - } else { - this.anInt2757 = (-var6 + this.anInt2834 + var5) / 2; - this.aBoolean2783 = true; - var8 = var3.pitch_max_speed * var3.pitch_max_speed / (2 * var3.pitch_acceleration); - var9 = var5 + -var8; - if (var9 > this.anInt2757) { - this.anInt2757 = var9; - } - } - } else if (this.anInt2830 < 0 && this.anInt2834 > var5) { - var7 = -this.anInt2834 + var5; - var6 = this.anInt2830 * this.anInt2830 / (2 * var3.pitch_acceleration); - if (var7 >= var6) { - this.anInt2757 = (var6 + this.anInt2834 - -var5) / 2; - this.aBoolean2783 = true; - var8 = var3.pitch_max_speed * var3.pitch_max_speed / (2 * var3.pitch_acceleration); - var9 = var8 + var5; - if (var9 < this.anInt2757) { - this.anInt2757 = var9; - } - } else { - this.aBoolean2783 = false; - } - } else { - this.aBoolean2783 = false; - } - } - - if (this.anInt2830 == 0) { - var6 = -this.anInt2834 + this.anInt2766; - if (-var3.pitch_acceleration < var6 && var3.pitch_acceleration > var6) { - this.anInt2834 = this.anInt2766; - } else { - this.anInt2757 = (this.anInt2766 + this.anInt2834) / 2; - this.aBoolean2783 = true; - var7 = var3.pitch_max_speed * var3.pitch_max_speed / (2 * var3.pitch_acceleration); - if (var6 < 0) { - this.anInt2830 = -var3.pitch_acceleration; - var8 = var7 + this.anInt2766; - if (this.anInt2757 > var8) { - this.anInt2757 = var8; - } - } else { - this.anInt2830 = var3.pitch_acceleration; - var8 = -var7 + this.anInt2766; - if (var8 > this.anInt2757) { - this.anInt2757 = var8; - } - } - } - } else if (this.anInt2830 > 0) { - if (this.anInt2757 <= this.anInt2834) { - this.aBoolean2783 = false; - } - - if (this.aBoolean2783) { - if (var3.pitch_max_speed > this.anInt2830) { - this.anInt2830 += var3.pitch_acceleration; - } - } else { - this.anInt2830 -= var3.pitch_acceleration; - if (0 > this.anInt2830) { - this.anInt2830 = 0; - } - } - } else { - if (this.anInt2757 >= this.anInt2834) { - this.aBoolean2783 = false; - } - - if (this.aBoolean2783) { - if (this.anInt2830 > -var3.pitch_max_speed) { - this.anInt2830 -= var3.pitch_acceleration; - } - } else { - this.anInt2830 += var3.pitch_acceleration; - if (0 < this.anInt2830) { - this.anInt2830 = 0; - } - } - } - - this.anInt2834 += this.anInt2830; - if (this.anInt2834 != 0) { - var6 = (this.anInt2834 & '\uffe6') >> 5; - var7 = var1.method1871() / 2; - var1.method1897(0, -var7, 0); - var1.method1896(var6); - var1.method1897(0, var7, 0); - } - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "fe.A(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ')'); - } - } - - abstract int getRenderAnimationId(); - - final void method1973(int var1) { - try { - if (var1 < -75) { - this.anInt2816 = 0; - this.anInt2811 = 0; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fe.I(" + var1 + ')'); - } - } - - final int method1975(int var1) { - try { - return var1 != 27855 ? 107 : (this.anInt2820 != -32768 ? -this.anInt2820 : 200); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fe.F(" + var1 + ')'); - } - } - - final void setSize(int var1, int var2) { - try { - this.size = var1; - if (var2 != 2) { - this.aBoolean2807 = false; - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "fe.C(" + var1 + ',' + var2 + ')'); - } - } - - int getSize() { - try { - return this.size; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fe.H(" + (byte) 114 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class140_Sub6.java b/Client/src/main/java/org/runite/client/Class140_Sub6.java deleted file mode 100644 index 6da8e25b6..000000000 --- a/Client/src/main/java/org/runite/client/Class140_Sub6.java +++ /dev/null @@ -1,257 +0,0 @@ -package org.runite.client; - -final class Class140_Sub6 extends GameObject { - - static int accRegistryPort; - private double aDouble2895; - private int anInt2896 = 0; - private final int anInt2897; - private int anInt2898 = -1; - int anInt2899; - double aDouble2900; - private final int anInt2902; - int anInt2903; - private boolean aBoolean2904; - static int anInt2905 = 0; - int anInt2907; - private double aDouble2908; - private Class127_Sub1 aClass127_Sub1_2909; - static boolean aBoolean2910 = true; - private final int anInt2911; - private int anInt2912 = -32768; - private double aDouble2913; - double aDouble2914; - private final int anInt2915; - private final int anInt2916; - private int anInt2917; - private double aDouble2918; - int anInt2919; - double aDouble2920; - private double aDouble2921; - private int anInt2922 = 0; - private final SequenceDefinition aClass142_2923; - int anInt2924; - int anInt2925; - static volatile long aLong2926 = 0L; - private final int anInt2927; - - - static void method2020(int var0, ObjectDefinition var1, int var3, int var4) { - try { - Class3_Sub9 var5 = (Class3_Sub9) Unsorted.aLinkedList_78.method1222(); - - while(null != var5) { - if(var4 == var5.anInt2314 && 128 * var0 == var5.anInt2326 && var5.anInt2308 == 128 * var3 && var5.aClass111_2320.objectId == var1.objectId) { - if(null != var5.aClass3_Sub24_Sub1_2312) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var5.aClass3_Sub24_Sub1_2312); - var5.aClass3_Sub24_Sub1_2312 = null; - } - - if(var5.aClass3_Sub24_Sub1_2315 != null) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var5.aClass3_Sub24_Sub1_2315); - var5.aClass3_Sub24_Sub1_2315 = null; - } - - var5.unlink(); - return; - } - - var5 = (Class3_Sub9) Unsorted.aLinkedList_78.method1221(); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ra.E(" + var0 + ',' + (var1 != null?"{...}":"null") + ',' + (byte) -73 + ',' + var3 + ',' + var4 + ')'); - } - } - - static boolean method2021(byte var0, int var1) { - try { - if(var0 > -63) { - anInt2905 = 66; - } - - return var1 == (-var1 & var1); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ra.H(" + var0 + ',' + var1 + ')'); - } - } - - final void method1867(int var1, int var2, int var3, int var4, int var5) {} - - private Model method2022() { - try { - GraphicDefinition var2 = GraphicDefinition.getGraphicDefinition(this.anInt2915); - Model var3 = var2.method966(this.anInt2898, this.anInt2922, this.anInt2896); - if(null == var3) { - return null; - } else { - var3.method1896(this.anInt2917); - return var3; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ra.G(" + -126 + ')'); - } - } - - final void method2023(int var2) { - try { - this.aDouble2920 += this.aDouble2895 * (double)var2; - this.aDouble2900 += this.aDouble2918 * (double)var2; - this.aBoolean2904 = true; - if(this.anInt2927 == -1) { - this.aDouble2914 += this.aDouble2913 * (double)var2; - } else { - this.aDouble2914 += (double)var2 * this.aDouble2908 * 0.5D * (double)var2 + (double)var2 * this.aDouble2913; - this.aDouble2913 += this.aDouble2908 * (double)var2; - } - - this.anInt2924 = 1024 + (int)(325.949D * Math.atan2(this.aDouble2895, this.aDouble2918)) & 0x7FF; - this.anInt2917 = 2047 & (int)(325.949D * Math.atan2(this.aDouble2913, this.aDouble2921)); - - if(this.aClass142_2923 != null) { - this.anInt2896 += var2; - - while(this.anInt2896 > this.aClass142_2923.duration[this.anInt2922]) { - this.anInt2896 -= this.aClass142_2923.duration[this.anInt2922]; - ++this.anInt2922; - if(this.aClass142_2923.frames.length <= this.anInt2922) { - this.anInt2922 -= this.aClass142_2923.anInt1865; - if(this.anInt2922 < 0 || this.aClass142_2923.frames.length <= this.anInt2922) { - this.anInt2922 = 0; - } - } - - this.anInt2898 = this.anInt2922 + 1; - if(this.aClass142_2923.frames.length <= this.anInt2898) { - this.anInt2898 -= this.aClass142_2923.anInt1865; - if(this.anInt2898 < 0 || this.aClass142_2923.frames.length <= this.anInt2898) { - this.anInt2898 = -1; - } - } - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ra.D(" + (byte) -59 + ',' + var2 + ')'); - } - } - - final void method2024(int var1, int var3, int var4, int var5) { - try { - double var6; - if(!this.aBoolean2904) { - double var8 = -this.anInt2916 + var1; - var6 = -this.anInt2902 + var5; - double var10 = Math.sqrt(var6 * var6 + var8 * var8); - this.aDouble2914 = this.anInt2911; - this.aDouble2900 = var8 * (double)this.anInt2897 / var10 + (double)this.anInt2916; - this.aDouble2920 = (double)this.anInt2897 * var6 / var10 + (double)this.anInt2902; - } - - var6 = -var3 + 1 + this.anInt2899; - this.aDouble2918 = ((double)var1 - this.aDouble2900) / var6; - this.aDouble2895 = (-this.aDouble2920 + (double)var5) / var6; - this.aDouble2921 = Math.sqrt(this.aDouble2918 * this.aDouble2918 + this.aDouble2895 * this.aDouble2895); - if(this.anInt2927 == -1) { - this.aDouble2913 = (-this.aDouble2914 + (double)var4) / var6; - } else { - if(!this.aBoolean2904) { - this.aDouble2913 = -this.aDouble2921 * Math.tan((double)this.anInt2927 * 0.02454369D); - } - - this.aDouble2908 = 2.0D * ((double)var4 - this.aDouble2914 - this.aDouble2913 * var6) / (var6 * var6); - } - - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "ra.J(" + var1 + ',' + 1 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - try { - Model var13 = this.method2022(); - if(null != var13) { - var13.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2909); - this.anInt2912 = var13.method1871(); - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "ra.IA(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var11 + ',' + (var12 != null?"{...}":"null") + ')'); - } - } - - final int method1871() { - try { - return this.anInt2912; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ra.MA()"); - } - } - - static void method2026(int interfaceHash, int amount, int itemId) { - try { - InterfaceWidget var4 = InterfaceWidget.getWidget(9, interfaceHash); - var4.flagUpdate(); - var4.anInt3598 = itemId; - var4.anInt3597 = amount; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ra.I(" + (byte) 122 + ',' + interfaceHash + ',' + amount + ',' + itemId + ')'); - } - } - - static AbstractSprite[] getSprites(int archiveId, CacheIndex var3) { - try { - // System.out.println(archiveId); - return !Class75_Sub4.method1351(var3, 0, archiveId)?null: Class75_Sub3.method1347(-26802); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ra.B(" + 0 + ',' + (byte) 11 + ',' + archiveId + ',' + (var3 != null?"{...}":"null") + ')'); - } - } - - static int method2028(int var0, int var1) { - try { - int var4; - for(var4 = 1; 1 < var0; var0 >>= 1) { - if(0 != (1 & var0)) { - var4 *= var1; - } - - var1 *= var1; - } - - if(var0 == 1) { - return var4 * var1; - } else { - return var4; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ra.A(" + var0 + ',' + var1 + ',' + -122 + ')'); - } - } - - Class140_Sub6(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11) { - try { - this.anInt2899 = var7; - this.anInt2902 = var3; - this.anInt2919 = var10; - this.anInt2915 = var1; - this.anInt2907 = var2; - this.anInt2927 = var8; - this.anInt2916 = var4; - this.aBoolean2904 = false; - this.anInt2903 = var11; - this.anInt2897 = var9; - this.anInt2925 = var6; - this.anInt2911 = var5; - int var12 = GraphicDefinition.getGraphicDefinition(this.anInt2915).anInt542; - if(var12 == -1) { - this.aClass142_2923 = null; - } else { - this.aClass142_2923 = SequenceDefinition.getAnimationDefinition(var12); - } - - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "ra.(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var10 + ',' + var11 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class140_Sub7.java b/Client/src/main/java/org/runite/client/Class140_Sub7.java deleted file mode 100644 index 2e05ab0a2..000000000 --- a/Client/src/main/java/org/runite/client/Class140_Sub7.java +++ /dev/null @@ -1,282 +0,0 @@ -package org.runite.client; - -public final class Class140_Sub7 extends GameObject { - - int anInt2930; - static int[] anIntArray2931; - private int anInt2932 = -32768; - static int[] anIntArray2933 = new int[]{2, 0, 0, 2, 0, 0, 0, 4, 4}; - public static int canvasHeight; - static RSString[] aClass94Array2935 = new RSString[500]; - int anInt2936; - static int anInt2938; - - - static boolean method2029(byte var0, CacheIndex var1, int var2) { - try { - if(var0 > -116) { - return false; - } else { - byte[] var3 = var1.method2138(var2); - if(null == var3) { - return false; - } else { - Class45.method1082(var3, 19); - return true; - } - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "uj.B(" + var0 + ',' + (var1 != null?"{...}":"null") + ',' + var2 + ')'); - } - } - - static boolean method2031(byte var0, boolean var1, int var2, int var3, Class3_Sub2[][][] var4, int var5) { - try { - byte var6 = !var1?(byte)(255 & CSConfigCachefile.anInt1127):1; - if(Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var2][var3] == var6) { - return false; - } else if((Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var2][var3] & 4) == 0) { - return false; - } else { - int var8 = 0; - byte var7 = 0; - TextureOperation38.anIntArray3456[var7] = var2; - if(var0 >= -9) { - method2031((byte)-29, false, 59, 52, null, 75); - } - - int var23 = var7 + 1; - Class45.anIntArray729[var7] = var3; - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var2][var3] = var6; - - while(var23 != var8) { - int var10 = (16740943 & TextureOperation38.anIntArray3456[var8]) >> 16; - int var11 = 255 & TextureOperation38.anIntArray3456[var8] >> 24; - int var9 = TextureOperation38.anIntArray3456[var8] & 65535; - int var13 = (Class45.anIntArray729[var8] & 16721603) >> 16; - int var12 = Class45.anIntArray729[var8] & 65535; - var8 = 4095 & 1 + var8; - boolean var14 = false; - boolean var15 = false; - if(0 == (Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var9][var12] & 4)) { - var14 = true; - } - - int var17; - int var16; - label257: - for(var16 = 1 + WorldListCountry.localPlane; 3 >= var16; ++var16) { - if((Unsorted.aByteArrayArrayArray113[var16][var9][var12] & 8) == 0) { - int var18; - int var20; - if(var14 && var4[var16][var9][var12] != null) { - if(null != var4[var16][var9][var12].aClass70_2234) { - var17 = Unsorted.method1184(var10, (byte)82); - if(var17 == var4[var16][var9][var12].aClass70_2234.anInt1055 || var4[var16][var9][var12].aClass70_2234.anInt1059 == var17) { - continue; - } - - if(0 != var11) { - var18 = Unsorted.method1184(var11, (byte)-93); - if(var18 == var4[var16][var9][var12].aClass70_2234.anInt1055 || var4[var16][var9][var12].aClass70_2234.anInt1059 == var18) { - continue; - } - } - - if(var13 != 0) { - var18 = Unsorted.method1184(var13, (byte)99); - if(var18 == var4[var16][var9][var12].aClass70_2234.anInt1055 || var4[var16][var9][var12].aClass70_2234.anInt1059 == var18) { - continue; - } - } - } - - if(var4[var16][var9][var12].aClass25Array2221 != null) { - for(var17 = 0; var17 < var4[var16][var9][var12].anInt2223; ++var17) { - var18 = (int)(63L & var4[var16][var9][var12].aClass25Array2221[var17].aLong498 >> 14); - if(var18 == 21) { - var18 = 19; - } - - int var19 = (int)(var4[var16][var9][var12].aClass25Array2221[var17].aLong498 >> 20 & 3L); - var20 = var18 | var19 << 6; - if(var10 == var20 || var11 != 0 && var20 == var11 || var13 != 0 && var13 == var20) { - continue label257; - } - } - } - } - - var15 = true; - Class3_Sub2 var24 = var4[var16][var9][var12]; - if(var24 != null && var24.anInt2223 > 0) { - for(var18 = 0; var18 < var24.anInt2223; ++var18) { - Class25 var25 = var24.aClass25Array2221[var18]; - if(var25.anInt483 != var25.anInt495 || var25.anInt478 != var25.anInt481) { - for(var20 = var25.anInt483; var20 <= var25.anInt495; ++var20) { - for(int var21 = var25.anInt478; var25.anInt481 >= var21; ++var21) { - Class158.aByteArrayArrayArray2008[var16][var20][var21] = var6; - } - } - } - } - } - - Class158.aByteArrayArrayArray2008[var16][var9][var12] = var6; - } - } - - if(var15) { - if(Class44.anIntArrayArrayArray723[WorldListCountry.localPlane - -1][var9][var12] > Unsorted.anIntArray686[var5]) { - Unsorted.anIntArray686[var5] = Class44.anIntArrayArrayArray723[WorldListCountry.localPlane + 1][var9][var12]; - } - - var16 = var9 << 7; - if(var16 >= Class129_Sub1.anIntArray2696[var5]) { - if(var16 > Class159.anIntArray2021[var5]) { - Class159.anIntArray2021[var5] = var16; - } - } else { - Class129_Sub1.anIntArray2696[var5] = var16; - } - - var17 = var12 << 7; - if(var17 < SequenceDefinition.anIntArray1871[var5]) { - SequenceDefinition.anIntArray1871[var5] = var17; - } else if(Player.anIntArray3959[var5] < var17) { - Player.anIntArray3959[var5] = var17; - } - } - - if(!var14) { - if(var9 >= 1 && Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][-1 + var9][var12] != var6) { - TextureOperation38.anIntArray3456[var23] = TextureOperation3.bitwiseOr(TextureOperation3.bitwiseOr(var9 - 1, 1179648), -754974720); - Class45.anIntArray729[var23] = TextureOperation3.bitwiseOr(var12, 1245184); - var23 = 1 + var23 & 4095; - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9 - 1][var12] = var6; - } - - ++var12; - if(104 > var12) { - if(var9 + -1 >= 0 && Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][-1 + var9][var12] != var6 && (Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var9][var12] & 4) == 0 && (Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][-1 + var9][var12 + -1] & 4) == 0) { - TextureOperation38.anIntArray3456[var23] = TextureOperation3.bitwiseOr(1375731712, TextureOperation3.bitwiseOr(1179648, -1 + var9)); - Class45.anIntArray729[var23] = TextureOperation3.bitwiseOr(var12, 1245184); - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][-1 + var9][var12] = var6; - var23 = 1 + var23 & 4095; - } - - if(var6 != Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9][var12]) { - TextureOperation38.anIntArray3456[var23] = TextureOperation3.bitwiseOr(318767104, TextureOperation3.bitwiseOr(var9, 5373952)); - Class45.anIntArray729[var23] = TextureOperation3.bitwiseOr(5439488, var12); - var23 = 4095 & 1 + var23; - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9][var12] = var6; - } - - if(1 + var9 < 104 && Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9 + 1][var12] != var6 && 0 == (Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var9][var12] & 4) && (Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][1 + var9][var12 - 1] & 4) == 0) { - TextureOperation38.anIntArray3456[var23] = TextureOperation3.bitwiseOr(-1845493760, TextureOperation3.bitwiseOr(5373952, var9 + 1)); - Class45.anIntArray729[var23] = TextureOperation3.bitwiseOr(5439488, var12); - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9 - -1][var12] = var6; - var23 = 4095 & var23 - -1; - } - } - - --var12; - if(104 > 1 + var9 && var6 != Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9 - -1][var12]) { - TextureOperation38.anIntArray3456[var23] = TextureOperation3.bitwiseOr(TextureOperation3.bitwiseOr(1 + var9, 9568256), 1392508928); - Class45.anIntArray729[var23] = TextureOperation3.bitwiseOr(var12, 9633792); - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][1 + var9][var12] = var6; - var23 = var23 + 1 & 4095; - } - - --var12; - if(var12 >= 0) { - if(0 <= var9 + -1 && Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][-1 + var9][var12] != var6 && (Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var9][var12] & 4) == 0 && (Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var9 + -1][1 + var12] & 4) == 0) { - TextureOperation38.anIntArray3456[var23] = TextureOperation3.bitwiseOr(TextureOperation3.bitwiseOr(-1 + var9, 13762560), 301989888); - Class45.anIntArray729[var23] = TextureOperation3.bitwiseOr(var12, 13828096); - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][-1 + var9][var12] = var6; - var23 = 4095 & var23 - -1; - } - - if(var6 != Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9][var12]) { - TextureOperation38.anIntArray3456[var23] = TextureOperation3.bitwiseOr(TextureOperation3.bitwiseOr(var9, 13762560), -1828716544); - Class45.anIntArray729[var23] = TextureOperation3.bitwiseOr(13828096, var12); - var23 = var23 - -1 & 4095; - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9][var12] = var6; - } - - if(var9 - -1 < 104 && Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9 + 1][var12] != var6 && (4 & Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var9][var12]) == 0 && (Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][1 + var9][1 + var12] & 4) == 0) { - TextureOperation38.anIntArray3456[var23] = TextureOperation3.bitwiseOr(-771751936, TextureOperation3.bitwiseOr(var9 - -1, 9568256)); - Class45.anIntArray729[var23] = TextureOperation3.bitwiseOr(9633792, var12); - Class158.aByteArrayArrayArray2008[WorldListCountry.localPlane][var9 + 1][var12] = var6; - var23 = 4095 & 1 + var23; - } - } - } - } - - if(-1000000 != Unsorted.anIntArray686[var5]) { - Unsorted.anIntArray686[var5] += 10; - Class129_Sub1.anIntArray2696[var5] -= 50; - Class159.anIntArray2021[var5] += 50; - Player.anIntArray3959[var5] += 50; - SequenceDefinition.anIntArray1871[var5] -= 50; - } - - return true; - } - } catch (RuntimeException var22) { - throw ClientErrorException.clientError(var22, "uj.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + (var4 != null?"{...}":"null") + ',' + var5 + ')'); - } - } - - final void method1867(int var1, int var2, int var3, int var4, int var5) {} - - static int method2032(int var0) { - try { - - return var0 >>> 10; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "uj.E(" + var0 + ',' + 5439488 + ')'); - } - } - - static RSString method2033(DataBuffer var0) { - try { - try { - int var3 = var0.getSmart(); - if(32767 < var3) { - var3 = 32767; - } - - byte[] var4 = new byte[var3]; - var0.index += TextureOperation16.aClass36_3112.method1017(0, var3, var4, var0.buffer, var0.index); - return TextureOperation33.bufferToString(var4, var3, 0); - } catch (Exception var6) { - return TextCore.aClass94_2018; - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "uj.D(" + (var0 != null?"{...}":"null") + ',' + 1 + ',' + 32767 + ')'); - } - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - try { - Model var13 = ItemDefinition.getItemDefinition(this.anInt2936).method1110(-1, 0, null, this.anInt2930, 0); - if(null != var13) { - var13.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, var12); - this.anInt2932 = var13.method1871(); - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "uj.IA(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var11 + ',' + (var12 != null?"{...}":"null") + ')'); - } - } - - final int method1871() { - try { - return this.anInt2932; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "uj.MA()"); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class141.java b/Client/src/main/java/org/runite/client/Class141.java deleted file mode 100644 index a3b2a0f1b..000000000 --- a/Client/src/main/java/org/runite/client/Class141.java +++ /dev/null @@ -1,453 +0,0 @@ -package org.runite.client; - - -import javax.media.opengl.GL; - -final class Class141 { - - static LDIndexedSprite aClass109_Sub1_1840; - static Class169[][] aClass169ArrayArray1841; - static LDIndexedSprite[] aClass109_Sub1Array1843; - private static int anInt1842; - private static int anInt1844; - - - private static void method2034(LDIndexedSprite var0, LDIndexedSprite var1, int var2, int var3) { - var2 += var0.anInt1470; - var3 += var0.anInt1464; - int var4 = var2 + var3 * var1.width; - int var5 = 0; - int var6 = var0.height; - int var7 = var0.width; - int var8 = var1.width - var7; - int var9 = 0; - int var10; - if (var3 <= 0) { - var10 = 1 - var3; - var6 -= var10; - var5 += var10 * var7; - var4 += var10 * var1.width; - var3 = 1; - } - - if (var3 + var6 >= var1.height) { - var10 = var3 + var6 + 1 - var1.height; - var6 -= var10; - } - - if (var2 <= 0) { - var10 = 1 - var2; - var7 -= var10; - var5 += var10; - var4 += var10; - var9 += var10; - var8 += var10; - var2 = 1; - } - - if (var2 + var7 >= var1.width) { - var10 = var2 + var7 + 1 - var1.width; - var7 -= var10; - var9 += var10; - var8 += var10; - } - - if (var7 > 0 && var6 > 0) { - method2044(var1.raster, var0.raster, var5, var4, var7, var6, var8, var9); - method2036(var2, var3, var7, var6); - } - } - - private static void method2035(byte[] var0, byte[] var1, int var2, int var3, int var4, int var5, int var6, int var7) { - int var8 = -(var4 >> 2); - var4 = -(var4 & 3); - - for (int var9 = -var5; var9 < 0; ++var9) { - int var10; - int var10001; - for (var10 = var8; var10 < 0; ++var10) { - var10001 = var3++; - var0[var10001] -= var1[var2++]; - var10001 = var3++; - var0[var10001] -= var1[var2++]; - var10001 = var3++; - var0[var10001] -= var1[var2++]; - var10001 = var3++; - var0[var10001] -= var1[var2++]; - } - - for (var10 = var4; var10 < 0; ++var10) { - var10001 = var3++; - var0[var10001] -= var1[var2++]; - } - - var3 += var6; - var2 += var7; - } - - } - - private static void method2036(int var0, int var1, int var2, int var3) { - int var4 = var0 - 1 >> 7; - int var5 = var0 - 1 + var2 - 1 >> 7; - int var6 = var1 - 1 >> 7; - int var7 = var1 - 1 + var3 - 1 >> 7; - - for (int var8 = var4; var8 <= var5; ++var8) { - for (int var9 = var6; var9 <= var7; ++var9) { - aClass169ArrayArray1841[var8][var9].aBoolean2106 = true; - } - } - - } - - static void method2037(int var0, int var1, boolean var2, boolean var3, int var4, int var5, int var6, int var7, int var8, int var9) { - if (!var2 || !var3) { - if (!var2 || var0 != 1) { - if (!var3 || var0 != 0) { - int var10 = var4 << 7; - int var11 = var6 + var7 + var8 + var9 >> 2; - int var12 = var5 << 7; - int var13 = var10 - (var11 * Class92.lightX >> 8) >> 3; - int var14 = var12 - (var11 * Class92.lightY >> 8) >> 3; - if (var0 != 0 && var0 != 1 && (var2 || var3)) { - method2050(aClass109_Sub1Array1843[var0], aClass109_Sub1_1840, var13 + 1, var14 + 1, var1, var2); - } else { - method2034(aClass109_Sub1Array1843[1], aClass109_Sub1_1840, var13 + 1, var14 + 1); - } - } - } - } - } - - static void method2038(int var0, int var1, int var2, boolean[][] var4) { - GL var6 = HDToolKit.gl; - HDToolKit.method1856(1); - HDToolKit.method1847(1); - HDToolKit.method1823(); - HDToolKit.method1837(false); - Unsorted.method551(0, 0); - var6.glDepthMask(false); - - for (int var7 = 0; var7 < anInt1844; ++var7) { - int var8 = 0; - - while (var8 < anInt1842) { - int var9 = var7 * 8; - - while (true) { - if (var9 < var7 * 8 + 8) { - label42: - { - if (var9 - var0 >= -var2 && var9 - var0 <= var2) { - for (int var10 = var8 * 8; var10 < var8 * 8 + 8; ++var10) { - if (var10 - var1 >= -var2 && var10 - var1 <= var2 && var4[var9 - var0 + var2][var10 - var1 + var2]) { - Class169 var11 = aClass169ArrayArray1841[var7][var8]; - if (var11.aBoolean2106) { - var11.method2282(aClass109_Sub1_1840, var7, var8); - var11.aBoolean2106 = false; - } - - var6.glPushMatrix(); - var6.glTranslatef((float) (var7 * 1024), 0.0F, (float) (var8 * 1024)); - var11.method2284(); - var6.glPopMatrix(); - break label42; - } - } - } - - ++var9; - continue; - } - } - - ++var8; - break; - } - } - } - - var6.glEnableClientState('\u8076'); - var6.glDepthMask(true); - HDToolKit.method1846(); - } - - private static boolean method2039(byte[] var0, int var1, int var2, int var3, int var4, int var5) { - int var6 = var2 % var5; - int var7; - if (var6 == 0) { - var7 = 0; - } else { - var7 = var5 - var6; - } - - int var8 = -((var3 + var5 - 1) / var5); - int var9 = -((var2 + var5 - 1) / var5); - - for (int var10 = var8; var10 < 0; ++var10) { - for (int var11 = var9; var11 < 0; ++var11) { - if (var0[var1] == 0) { - return true; - } - - var1 += var5; - } - - var1 -= var7; - if (var0[var1 - 1] == 0) { - return true; - } - - var1 += var4; - } - - return false; - } - - private static boolean method2040(LDIndexedSprite var0, LDIndexedSprite var1, int var2, int var3) { - var2 += var0.anInt1470; - var3 += var0.anInt1464; - int var4 = var2 + var3 * var1.width; - int var5 = var0.height; - int var6 = var0.width; - int var7 = var1.width - var6; - int var8; - if (var3 <= 0) { - var8 = 1 - var3; - var5 -= var8; - var4 += var8 * var1.width; - var3 = 1; - } - - if (var3 + var5 >= var1.height) { - var8 = var3 + var5 + 1 - var1.height; - var5 -= var8; - } - - if (var2 <= 0) { - var8 = 1 - var2; - var6 -= var8; - var4 += var8; - var7 += var8; - var2 = 1; - } - - if (var2 + var6 >= var1.width) { - var8 = var2 + var6 + 1 - var1.width; - var6 -= var8; - var7 += var8; - } - - if (var6 > 0 && var5 > 0) { - byte var9 = 8; - var7 += (var9 - 1) * var1.width; - method2036(var2, var3, var6, var5); - return method2039(var1.raster, var4, var6, var5, var7, var9); - } else { - return false; - } - } - - static void method2041() { - anInt1844 = 104 + 7 >> 3; - anInt1842 = 104 + 7 >> 3; - aClass109_Sub1_1840 = new LDIndexedSprite(anInt1844 * 128 + 2, anInt1842 * 128 + 2); - aClass169ArrayArray1841 = new Class169[anInt1844][anInt1842]; - - for (int var2 = 0; var2 < anInt1844; ++var2) { - for (int var3 = 0; var3 < anInt1842; ++var3) { - aClass169ArrayArray1841[var2][var3] = new Class169(); - } - } - - } - - private static void method2042(LDIndexedSprite var0, LDIndexedSprite var1, int var2, int var3) { - var2 += var0.anInt1470; - var3 += var0.anInt1464; - int var4 = var2 + var3 * var1.width; - int var5 = 0; - int var6 = var0.height; - int var7 = var0.width; - int var8 = var1.width - var7; - int var9 = 0; - int var10; - if (var3 <= 0) { - var10 = 1 - var3; - var6 -= var10; - var5 += var10 * var7; - var4 += var10 * var1.width; - var3 = 1; - } - - if (var3 + var6 >= var1.height) { - var10 = var3 + var6 + 1 - var1.height; - var6 -= var10; - } - - if (var2 <= 0) { - var10 = 1 - var2; - var7 -= var10; - var5 += var10; - var4 += var10; - var9 += var10; - var8 += var10; - var2 = 1; - } - - if (var2 + var7 >= var1.width) { - var10 = var2 + var7 + 1 - var1.width; - var7 -= var10; - var9 += var10; - var8 += var10; - } - - if (var7 > 0 && var6 > 0) { - method2035(var1.raster, var0.raster, var5, var4, var7, var6, var8, var9); - method2036(var2, var3, var7, var6); - } - } - - static void method2043() { - aClass109_Sub1_1840 = null; - aClass109_Sub1Array1843 = null; - aClass169ArrayArray1841 = null; - } - - private static void method2044(byte[] var0, byte[] var1, int var2, int var3, int var4, int var5, int var6, int var7) { - int var8 = -(var4 >> 2); - var4 = -(var4 & 3); - - for (int var9 = -var5; var9 < 0; ++var9) { - int var10; - int var10001; - for (var10 = var8; var10 < 0; ++var10) { - var10001 = var3++; - var0[var10001] += var1[var2++]; - var10001 = var3++; - var0[var10001] += var1[var2++]; - var10001 = var3++; - var0[var10001] += var1[var2++]; - var10001 = var3++; - var0[var10001] += var1[var2++]; - } - - for (var10 = var4; var10 < 0; ++var10) { - var10001 = var3++; - var0[var10001] += var1[var2++]; - } - - var3 += var6; - var2 += var7; - } - - } - - private static void method2046(byte[] var0, byte[] var1, int var2, int var3, int var4, int var5, int var6) { - for (int var7 = -16; var7 < 0; ++var7) { - for (int var8 = -4; var8 < 0; ++var8) { - int var10001 = var3++; - var0[var10001] = (byte) (var0[var10001] + (1 - var1[var2])); - var2 += var5; - var10001 = var3++; - var0[var10001] = (byte) (var0[var10001] + (1 - var1[var2])); - var2 += var5; - var10001 = var3++; - var0[var10001] = (byte) (var0[var10001] + (1 - var1[var2])); - var2 += var5; - var10001 = var3++; - var0[var10001] = (byte) (var0[var10001] + (1 - var1[var2])); - var2 += var5; - } - - var3 += var4; - var2 += var6; - } - - } - - static void method2047(LDIndexedSprite var0, int var1, int var2, int var3) { - if (var0 != null) { - int var4 = var1 - (var2 * Class92.lightX >> 8) >> 3; - int var5 = var3 - (var2 * Class92.lightY >> 8) >> 3; - method2042(var0, aClass109_Sub1_1840, var4 + 1, var5 + 1); - } - } - - private static void method2048(byte[] var0, byte[] var1, int var2, int var3, int var4, int var5, int var6) { - for (int var7 = -16; var7 < 0; ++var7) { - for (int var8 = -4; var8 < 0; ++var8) { - int var10001 = var3++; - var0[var10001] += var1[var2]; - var2 += var5; - var10001 = var3++; - var0[var10001] += var1[var2]; - var2 += var5; - var10001 = var3++; - var0[var10001] += var1[var2]; - var2 += var5; - var10001 = var3++; - var0[var10001] += var1[var2]; - var2 += var5; - } - - var3 += var4; - var2 += var6; - } - - } - - static boolean method2049(LDIndexedSprite var0, int var1, int var2, int var3) { - if (var0 == null) { - return false; - } else { - int var4 = var1 - (var2 * Class92.lightX >> 8) >> 3; - int var5 = var3 - (var2 * Class92.lightY >> 8) >> 3; - return method2040(var0, aClass109_Sub1_1840, var4 + 1, var5 + 1); - } - } - - private static void method2050(LDIndexedSprite var0, LDIndexedSprite var1, int var2, int var3, int var4, boolean var5) { - if (var2 > 0 && var3 > 0 && var2 + 16 < var1.width && var3 + 16 < var1.height) { - int var6 = var2 + var3 * var1.width; - int var7 = var1.width - 16; - short var8; - byte var9; - int var10; - if (var4 == 0) { - var8 = 240; - var9 = 1; - var10 = -var9 * 16 - 16; - } else if (var4 == 1) { - var8 = 255; - var9 = -16; - var10 = -var9 * 16 - 1; - } else if (var4 == 2) { - var8 = 15; - var9 = -1; - var10 = -var9 * 16 + 16; - } else { - var8 = 0; - var9 = 16; - var10 = -var9 * 16 + 1; - } - - if (var5) { - method2046(var1.raster, var0.raster, var8, var6, var7, var9, var10); - } else { - method2048(var1.raster, var0.raster, var8, var6, var7, var9, var10); - } - - method2036(var2, var3, 16, 16); - } - } - - static void method2051(LDIndexedSprite var0, int var1, int var2, int var3) { - if (var0 != null) { - int var4 = var1 - (var2 * Class92.lightX >> 8) >> 3; - int var5 = var3 - (var2 * Class92.lightY >> 8) >> 3; - method2034(var0, aClass109_Sub1_1840, var4 + 1, var5 + 1); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class143.java b/Client/src/main/java/org/runite/client/Class143.java deleted file mode 100644 index 6dd8b7ee5..000000000 --- a/Client/src/main/java/org/runite/client/Class143.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -public final class Class143 { - - public static int gameStage = 0; - static ReferenceCache aReferenceCache_1874 = new ReferenceCache(50); - - static void method2062(int var0, int var1, int var3, int var4, int var5, int var6, int var7) { - try { - int var12 = Class40.method1040(Class57.anInt902, var6, Class159.anInt2020); - int var13 = Class40.method1040(Class57.anInt902, var3, Class159.anInt2020); - int var14 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var7, Class101.anInt1425); - int var15 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var0, Class101.anInt1425); - int var8 = Class40.method1040(Class57.anInt902, var4 + var6, Class159.anInt2020); - int var9 = Class40.method1040(Class57.anInt902, -var4 + var3, Class159.anInt2020); - - int var16; - for (var16 = var12; var8 > var16; ++var16) { - TextureOperation18.method282(Class38.anIntArrayArray663[var16], var14, 97, var15, var5); - } - - for (var16 = var13; var9 < var16; --var16) { - TextureOperation18.method282(Class38.anIntArrayArray663[var16], var14, 94, var15, var5); - } - - int var10 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var4 + var7, Class101.anInt1425); - int var11 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var4 + var0, Class101.anInt1425); - - for (var16 = var8; var9 >= var16; ++var16) { - int[] var17 = Class38.anIntArrayArray663[var16]; - TextureOperation18.method282(var17, var14, 105, var10, var5); - TextureOperation18.method282(var17, var10, 111, var11, var1); - TextureOperation18.method282(var17, var11, 109, var15, var5); - } - - } catch (RuntimeException var18) { - throw ClientErrorException.clientError(var18, "tl.B(" + var0 + ',' + var1 + ',' + -89 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class144.java b/Client/src/main/java/org/runite/client/Class144.java deleted file mode 100644 index cd026dde4..000000000 --- a/Client/src/main/java/org/runite/client/Class144.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.runite.client; - -abstract class Class144 { - - abstract byte[] getBytes(); - - abstract void setBytes(byte[] var2); - -} diff --git a/Client/src/main/java/org/runite/client/Class144_Sub1.java b/Client/src/main/java/org/runite/client/Class144_Sub1.java deleted file mode 100644 index 94b59518b..000000000 --- a/Client/src/main/java/org/runite/client/Class144_Sub1.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.runite.client; - -import java.nio.ByteBuffer; - -final class Class144_Sub1 extends Class144 { - - private ByteBuffer wrapped; - - final void setBytes(byte[] bytes) { - this.wrapped = ByteBuffer.allocateDirect(bytes.length); - this.wrapped.position(0); - this.wrapped.put(bytes); - } - - final byte[] getBytes() { - byte[] bytes = new byte[this.wrapped.capacity()]; - this.wrapped.position(0); - this.wrapped.get(bytes); - return bytes; - } - -} diff --git a/Client/src/main/java/org/runite/client/Class145.java b/Client/src/main/java/org/runite/client/Class145.java deleted file mode 100644 index 42430a27c..000000000 --- a/Client/src/main/java/org/runite/client/Class145.java +++ /dev/null @@ -1,825 +0,0 @@ -package org.runite.client; - -final class Class145 { - - static int[] anIntArray2338 = new int[]{160, 192, 80, 96, 0, 144, 80, 48, 160}; - static int anInt3072 = -1; - static int anInt3153; - static int[] anIntArray3171 = new int[]{0, 4, 4, 8, 0, 0, 8, 0, 0}; - static int anInt3340; - static int anInt2697; - static Class113[] aClass113Array1895; - static int screenUpperX; - int animationId; - int anInt1891; - int anInt1893; - int anInt1894; - int anInt1897; - int anInt1900; - - static void method2072(int var0, int var1, int var2, int var3, int var4, int var5) { - try { - if (Class101.anInt1425 <= var1 && Class3_Sub28_Sub18.anInt3765 >= var3 && var2 >= Class159.anInt2020 && var4 <= Class57.anInt902) { - if (var5 == 1) { - Class77.method1367(var0, var3, var2, (byte) 34, var4, var1); - } else { - method1665(-2 + -19617, var3, var2, var0, var4, var5, var1); - } - } else if (1 == var5) { - Class102.method1617(var0, var1, var4, var3, var2); - } else { - TextureOperation33.method184(var4, var5, var3, var1, var0, var2); - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ub.F(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + -2 + ')'); - } - } - - static void method2073(Class3_Sub2 var0, boolean var1) { - Class163_Sub1.aLinkedList_2990.method1215(var0); - - while (true) { - Class3_Sub2 var2 = (Class3_Sub2) Class163_Sub1.aLinkedList_2990.method1220(); - if (var2 == null) { - return; - } - - if (var2.aBoolean2225) { - int var3 = var2.anInt2239; - int var4 = var2.anInt2231; - int var5 = var2.anInt2244; - int var6 = var2.anInt2238; - Class3_Sub2[][] var7 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var5]; - float var8 = 0.0F; - int var9; - int var10; - int var11; - int var12; - if (HDToolKit.highDetail) { - if (Unsorted.anIntArrayArrayArray3605 == Class44.anIntArrayArrayArray723) { - var9 = TextureOperation16.anIntArrayArray3115[var3][var4]; - var10 = var9 & 16777215; - if (var10 != Unsorted.anInt1244) { - Unsorted.anInt1244 = var10; - Class3_Sub28_Sub2.method535((byte) 56, var10); - Class92.method1512(Class72.method1297()); - } - - var11 = var9 >>> 24 << 3; - if (var11 != anInt3072) { - anInt3072 = var11; - Class3_Sub21.method394(var11, 121); - } - - var12 = Class58.anIntArrayArrayArray914[0][var3][var4] + Class58.anIntArrayArrayArray914[0][var3 + 1][var4] + Class58.anIntArrayArrayArray914[0][var3][var4 + 1] + Class58.anIntArrayArrayArray914[0][var3 + 1][var4 + 1] >> 2; - Unsorted.method551(-var12, 3); - var8 = 201.5F; - } else { - var8 = 201.5F - 50.0F * (float) (var6 + 1); - } - HDToolKit.method1832(var8); - } - - int var14; - int var15; - int var17; - int var16; - int var18; - Class3_Sub2 var21; - Class70 var23; - boolean var22; - Class25 var25; - Class3_Sub2 var35; - if (var2.aBoolean2222) { - if (var1) { - if (var5 > 0) { - var21 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var5 - 1][var3][var4]; - if (var21 != null && var21.aBoolean2225) { - continue; - } - } - - if (var3 <= Class97.anInt1375 && var3 > Class163_Sub1_Sub1.anInt4006) { - var21 = var7[var3 - 1][var4]; - if (var21 != null && var21.aBoolean2225 && (var21.aBoolean2222 || (var2.anInt2228 & 1) == 0)) { - continue; - } - } - - if (var3 >= Class97.anInt1375 && var3 < Unsorted.anInt67 - 1) { - var21 = var7[var3 + 1][var4]; - if (var21 != null && var21.aBoolean2225 && (var21.aBoolean2222 || (var2.anInt2228 & 4) == 0)) { - continue; - } - } - - if (var4 <= anInt3340 && var4 > Unsorted.anInt3603) { - var21 = var7[var3][var4 - 1]; - if (var21 != null && var21.aBoolean2225 && (var21.aBoolean2222 || (var2.anInt2228 & 8) == 0)) { - continue; - } - } - - if (var4 >= anInt3340 && var4 < Class126.anInt1665 - 1) { - var21 = var7[var3][var4 + 1]; - if (var21 != null && var21.aBoolean2225 && (var21.aBoolean2222 || (var2.anInt2228 & 2) == 0)) { - continue; - } - } - } else { - var1 = true; - } - - var2.aBoolean2222 = false; - if (var2.aClass3_Sub2_2235 != null) { - var21 = var2.aClass3_Sub2_2235; - if (HDToolKit.highDetail) { - HDToolKit.method1832(201.5F - 50.0F * (float) (var21.anInt2238 + 1)); - } - - if (var21.aClass126_2240 != null) { - TextureOperation19.method259(var21.aClass126_2240, 0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var3, var4, Class8.method846(0, var3, var4)); - } else if (var21.aClass35_2226 != null) { - Class3_Sub21.method395(var21.aClass35_2226, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var3, var4, Class8.method846(0, var3, var4)); - } - - var23 = var21.aClass70_2234; - if (var23 != null) { - if (HDToolKit.highDetail) { - if ((var23.anInt1055 & var2.anInt2241) == 0) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } else { - Class68.method1263(var23.anInt1055, anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var6, var3, var4); - } - } - - var23.aClass140_1049.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var23.anInt1054 - anInt2697, var23.anInt1057 - Unsorted.anInt3657, var23.anInt1045 - TextureOperation13.anInt3363, var23.aLong1048, var5, null); - } - - for (var11 = 0; var11 < var21.anInt2223; ++var11) { - var25 = var21.aClass25Array2221[var11]; - if (var25 != null) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var25.aClass140_479.animate(var25.anInt496, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var25.anInt482 - anInt2697, var25.anInt489 - Unsorted.anInt3657, var25.anInt484 - TextureOperation13.anInt3363, var25.aLong498, var5, null); - } - } - - if (HDToolKit.highDetail) { - HDToolKit.method1832(var8); - } - } - - var22 = false; - if (var2.aClass126_2240 != null) { - if (Class8.method846(var6, var3, var4)) { - TextureOperation19.method259(var2.aClass126_2240, var6, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var3, var4, true); - } else { - var22 = true; - if (var2.aClass126_2240.anInt1664 != 12345678 || TextureOperation37.aBoolean3261 && var5 <= Class91.anInt1302) { - TextureOperation19.method259(var2.aClass126_2240, var6, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var3, var4, false); - } - } - } else if (var2.aClass35_2226 != null) { - if (Class8.method846(var6, var3, var4)) { - Class3_Sub21.method395(var2.aClass35_2226, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var3, var4, true); - } else { - var22 = true; - Class3_Sub21.method395(var2.aClass35_2226, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var3, var4, false); - } - } - - if (var22) { - Class12 var24 = var2.aClass12_2230; - if (var24 != null && (var24.aLong328 & 2147483648L) != 0L) { - if (HDToolKit.highDetail && var24.aBoolean329) { - HDToolKit.method1832(var8 + 50.0F - 1.5F); - } - - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var24.object.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var24.anInt324 - anInt2697, var24.anInt326 - Unsorted.anInt3657, var24.anInt330 - TextureOperation13.anInt3363, var24.aLong328, var5, null); - if (HDToolKit.highDetail && var24.aBoolean329) { - HDToolKit.method1832(var8); - } - } - } - - var10 = 0; - var11 = 0; - Class70 var26 = var2.aClass70_2234; - Class19 var13 = var2.aClass19_2233; - if (var26 != null || var13 != null) { - if (Class97.anInt1375 == var3) { - ++var10; - } else if (Class97.anInt1375 < var3) { - var10 += 2; - } - - if (anInt3340 == var4) { - var10 += 3; - } else if (anInt3340 > var4) { - var10 += 6; - } - - var11 = NPC.anIntArray3997[var10]; - var2.anInt2241 = TextureOperation35.anIntArray3321[var10]; - } - - if (var26 != null) { - if ((var26.anInt1055 & anIntArray2338[var10]) == 0) { - var2.anInt2227 = 0; - } else if (var26.anInt1055 == 16) { - var2.anInt2227 = 3; - var2.anInt2229 = Unsorted.anIntArray2470[var10]; - var2.anInt2232 = 3 - var2.anInt2229; - } else if (var26.anInt1055 == 32) { - var2.anInt2227 = 6; - var2.anInt2229 = Class140_Sub7.anIntArray2933[var10]; - var2.anInt2232 = 6 - var2.anInt2229; - } else if (var26.anInt1055 == 64) { - var2.anInt2227 = 12; - var2.anInt2229 = anIntArray3171[var10]; - var2.anInt2232 = 12 - var2.anInt2229; - } else { - var2.anInt2227 = 9; - var2.anInt2229 = TextureOperation18.anIntArray4035[var10]; - var2.anInt2232 = 9 - var2.anInt2229; - } - - if ((var26.anInt1055 & var11) != 0 && Class164_Sub1.method2239(var6, var3, var4, var26.anInt1055)) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var26.aClass140_1049.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var26.anInt1054 - anInt2697, var26.anInt1057 - Unsorted.anInt3657, var26.anInt1045 - TextureOperation13.anInt3363, var26.aLong1048, var5, null); - } - - if ((var26.anInt1059 & var11) != 0 && Class164_Sub1.method2239(var6, var3, var4, var26.anInt1059)) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var26.aClass140_1052.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var26.anInt1054 - anInt2697, var26.anInt1057 - Unsorted.anInt3657, var26.anInt1045 - TextureOperation13.anInt3363, var26.aLong1048, var5, null); - } - } - - if (var13 != null && Class166.method2256(var6, var3, var4, var13.aClass140_429.method1871())) { - if (HDToolKit.highDetail) { - HDToolKit.method1832(var8 - 0.5F); - } - - if ((var13.anInt432 & var11) != 0) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var13.aClass140_429.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var13.anInt424 - anInt2697 + var13.anInt430, var13.anInt425 - Unsorted.anInt3657, var13.anInt427 - TextureOperation13.anInt3363 + var13.anInt426, var13.aLong428, var5, null); - } else if (var13.anInt432 == 256) { - var14 = var13.anInt424 - anInt2697; - var15 = var13.anInt425 - Unsorted.anInt3657; - var16 = var13.anInt427 - TextureOperation13.anInt3363; - var17 = var13.anInt420; - if (var17 == 1 || var17 == 2) { - var18 = -var14; - } else { - var18 = var14; - } - - int var19; - if (var17 == 2 || var17 == 3) { - var19 = -var16; - } else { - var19 = var16; - } - - if (var19 < var18) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var13.aClass140_429.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var14 + var13.anInt430, var15, var16 + var13.anInt426, var13.aLong428, var5, null); - } else if (var13.aClass140_423 != null) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var13.aClass140_423.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var14, var15, var16, var13.aLong428, var5, null); - } - } - - if (HDToolKit.highDetail) { - HDToolKit.method1832(var8); - } - } - - if (var22) { - Class12 var30 = var2.aClass12_2230; - if (var30 != null && (var30.aLong328 & 2147483648L) == 0L) { - if (HDToolKit.highDetail && var30.aBoolean329) { - HDToolKit.method1832(var8 + 50.0F - 1.5F); - } - - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var30.object.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var30.anInt324 - anInt2697, var30.anInt326 - Unsorted.anInt3657, var30.anInt330 - TextureOperation13.anInt3363, var30.aLong328, var5, null); - if (HDToolKit.highDetail && var30.aBoolean329) { - HDToolKit.method1832(var8); - } - } - - Class72 var28 = var2.aClass72_2245; - if (var28 != null && var28.anInt1077 == 0) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - if (var28.aClass140_1067 != null) { - var28.aClass140_1067.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var28.anInt1078 - anInt2697, var28.anInt1068 - Unsorted.anInt3657, var28.anInt1075 - TextureOperation13.anInt3363, var28.aLong1079, var5, null); - } - - if (var28.aClass140_1069 != null) { - var28.aClass140_1069.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var28.anInt1078 - anInt2697, var28.anInt1068 - Unsorted.anInt3657, var28.anInt1075 - TextureOperation13.anInt3363, var28.aLong1079, var5, null); - } - - if (var28.aClass140_1073 != null) { - var28.aClass140_1073.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var28.anInt1078 - anInt2697, var28.anInt1068 - Unsorted.anInt3657, var28.anInt1075 - TextureOperation13.anInt3363, var28.aLong1079, var5, null); - } - } - } - - var14 = var2.anInt2228; - if (var14 != 0) { - if (var3 < Class97.anInt1375 && (var14 & 4) != 0) { - var35 = var7[var3 + 1][var4]; - if (var35 != null && var35.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var35); - } - } - - if (var4 < anInt3340 && (var14 & 2) != 0) { - var35 = var7[var3][var4 + 1]; - if (var35 != null && var35.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var35); - } - } - - if (var3 > Class97.anInt1375 && (var14 & 1) != 0) { - var35 = var7[var3 - 1][var4]; - if (var35 != null && var35.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var35); - } - } - - if (var4 > anInt3340 && (var14 & 8) != 0) { - var35 = var7[var3][var4 - 1]; - if (var35 != null && var35.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var35); - } - } - } - } - - int var27; - if (var2.anInt2227 != 0) { - var22 = true; - - for (var10 = 0; var10 < var2.anInt2223; ++var10) { - if (var2.aClass25Array2221[var10].anInt490 != Class3_Sub28_Sub1.anInt3539 && (var2.anIntArray2237[var10] & var2.anInt2227) == var2.anInt2229) { - var22 = false; - break; - } - } - - if (var22) { - var23 = var2.aClass70_2234; - if (Class164_Sub1.method2239(var6, var3, var4, var23.anInt1055)) { - if (HDToolKit.highDetail) { - label736: - { - if ((var23.aLong1048 & 1032192L) == 16384L) { - var11 = var23.anInt1054 - anInt2697; - var12 = var23.anInt1045 - TextureOperation13.anInt3363; - var27 = (int) (var23.aLong1048 >> 20 & 3L); - if (var27 == 0) { - var11 -= 64; - var12 += 64; - if (var12 < var11 && var3 > 0 && var4 < TextureOperation17.anInt3179 - 1) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3 - 1, var4 + 1); - break label736; - } - } else if (var27 == 1) { - var11 += 64; - var12 += 64; - if (var12 < -var11 && var3 < Unsorted.anInt1234 - 1 && var4 < TextureOperation17.anInt3179 - 1) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3 + 1, var4 + 1); - break label736; - } - } else if (var27 == 2) { - var11 += 64; - var12 -= 64; - if (var12 > var11 && var3 < Unsorted.anInt1234 - 1 && var4 > 0) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3 + 1, var4 - 1); - break label736; - } - } else { - var11 -= 64; - var12 -= 64; - if (var12 > -var11 && var3 > 0 && var4 > 0) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3 - 1, var4 - 1); - break label736; - } - } - } - - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - } - - var23.aClass140_1049.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var23.anInt1054 - anInt2697, var23.anInt1057 - Unsorted.anInt3657, var23.anInt1045 - TextureOperation13.anInt3363, var23.aLong1048, var5, null); - } - - var2.anInt2227 = 0; - } - } - - if (var2.aBoolean2236) { - try { - var9 = var2.anInt2223; - var2.aBoolean2236 = false; - var10 = 0; - - label712: - for (var11 = 0; var11 < var9; ++var11) { - var25 = var2.aClass25Array2221[var11]; - if (var25.anInt490 != Class3_Sub28_Sub1.anInt3539) { - for (var27 = var25.anInt483; var27 <= var25.anInt495; ++var27) { - for (var14 = var25.anInt478; var14 <= var25.anInt481; ++var14) { - var35 = var7[var27][var14]; - if (var35.aBoolean2222) { - var2.aBoolean2236 = true; - continue label712; - } - - if (var35.anInt2227 != 0) { - var16 = 0; - if (var27 > var25.anInt483) { - ++var16; - } - - if (var27 < var25.anInt495) { - var16 += 4; - } - - if (var14 > var25.anInt478) { - var16 += 8; - } - - if (var14 < var25.anInt481) { - var16 += 2; - } - - if ((var16 & var35.anInt2227) == var2.anInt2232) { - var2.aBoolean2236 = true; - continue label712; - } - } - } - } - - Unsorted.aClass25Array4060[var10++] = var25; - var27 = Class97.anInt1375 - var25.anInt483; - var14 = var25.anInt495 - Class97.anInt1375; - if (var14 > var27) { - var27 = var14; - } - - var15 = anInt3340 - var25.anInt478; - var16 = var25.anInt481 - anInt3340; - if (var16 > var15) { - var25.anInt487 = var27 + var16; - } else { - var25.anInt487 = var27 + var15; - } - } - } - - while (var10 > 0) { - var11 = -50; - var12 = -1; - - for (var27 = 0; var27 < var10; ++var27) { - Class25 var34 = Unsorted.aClass25Array4060[var27]; - if (var34.anInt490 != Class3_Sub28_Sub1.anInt3539) { - if (var34.anInt487 > var11) { - var11 = var34.anInt487; - var12 = var27; - } else if (var34.anInt487 == var11) { - var15 = var34.anInt482 - anInt2697; - var16 = var34.anInt484 - TextureOperation13.anInt3363; - var17 = Unsorted.aClass25Array4060[var12].anInt482 - anInt2697; - var18 = Unsorted.aClass25Array4060[var12].anInt484 - TextureOperation13.anInt3363; - if (var15 * var15 + var16 * var16 > var17 * var17 + var18 * var18) { - var12 = var27; - } - } - } - } - - if (var12 == -1) { - break; - } - - Class25 var32 = Unsorted.aClass25Array4060[var12]; - var32.anInt490 = Class3_Sub28_Sub1.anInt3539; - if (!TextureOperation1.method222(var6, var32.anInt483, var32.anInt495, var32.anInt478, var32.anInt481, var32.aClass140_479.method1871())) { - if (HDToolKit.highDetail) { - if ((var32.aLong498 & 1032192L) == 147456L) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - var14 = var32.anInt482 - anInt2697; - var15 = var32.anInt484 - TextureOperation13.anInt3363; - var16 = (int) (var32.aLong498 >> 20 & 3L); - if (var16 != 1 && var16 != 3) { - if (var15 > var14) { - Class68.method1272(var5, var3, var4 - 1, var3 + 1, var4); - } else { - Class68.method1272(var5, var3, var4 + 1, var3 - 1, var4); - } - } else if (var15 > -var14) { - Class68.method1272(var5, var3, var4 - 1, var3 - 1, var4); - } else { - Class68.method1272(var5, var3, var4 + 1, var3 + 1, var4); - } - } else { - Class68.method1266(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var32.anInt483, var32.anInt478, var32.anInt495, var32.anInt481); - } - } - - var32.aClass140_479.animate(var32.anInt496, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var32.anInt482 - anInt2697, var32.anInt489 - Unsorted.anInt3657, var32.anInt484 - TextureOperation13.anInt3363, var32.aLong498, var5, null); - } - - for (var14 = var32.anInt483; var14 <= var32.anInt495; ++var14) { - for (var15 = var32.anInt478; var15 <= var32.anInt481; ++var15) { - Class3_Sub2 var37 = var7[var14][var15]; - if (var37.anInt2227 != 0) { - Class163_Sub1.aLinkedList_2990.method1215(var37); - } else if ((var14 != var3 || var15 != var4) && var37.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var37); - } - } - } - } - - if (var2.aBoolean2236) { - continue; - } - } catch (Exception var20) { - var2.aBoolean2236 = false; - } - } - - if (var2.aBoolean2225 && var2.anInt2227 == 0) { - if (var3 <= Class97.anInt1375 && var3 > Class163_Sub1_Sub1.anInt4006) { - var21 = var7[var3 - 1][var4]; - if (var21 != null && var21.aBoolean2225) { - continue; - } - } - - if (var3 >= Class97.anInt1375 && var3 < Unsorted.anInt67 - 1) { - var21 = var7[var3 + 1][var4]; - if (var21 != null && var21.aBoolean2225) { - continue; - } - } - - if (var4 <= anInt3340 && var4 > Unsorted.anInt3603) { - var21 = var7[var3][var4 - 1]; - if (var21 != null && var21.aBoolean2225) { - continue; - } - } - - if (var4 >= anInt3340 && var4 < Class126.anInt1665 - 1) { - var21 = var7[var3][var4 + 1]; - if (var21 != null && var21.aBoolean2225) { - continue; - } - } - - var2.aBoolean2225 = false; - --Class146.anInt3; - Class72 var29 = var2.aClass72_2245; - if (var29 != null && var29.anInt1077 != 0) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - if (var29.aClass140_1067 != null) { - var29.aClass140_1067.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var29.anInt1078 - anInt2697, var29.anInt1068 - Unsorted.anInt3657 - var29.anInt1077, var29.anInt1075 - TextureOperation13.anInt3363, var29.aLong1079, var5, null); - } - - if (var29.aClass140_1069 != null) { - var29.aClass140_1069.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var29.anInt1078 - anInt2697, var29.anInt1068 - Unsorted.anInt3657 - var29.anInt1077, var29.anInt1075 - TextureOperation13.anInt3363, var29.aLong1079, var5, null); - } - - if (var29.aClass140_1073 != null) { - var29.aClass140_1073.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var29.anInt1078 - anInt2697, var29.anInt1068 - Unsorted.anInt3657 - var29.anInt1077, var29.anInt1075 - TextureOperation13.anInt3363, var29.aLong1079, var5, null); - } - } - - if (var2.anInt2241 != 0) { - Class19 var36 = var2.aClass19_2233; - if (var36 != null && Class166.method2256(var6, var3, var4, var36.aClass140_429.method1871())) { - if ((var36.anInt432 & var2.anInt2241) != 0) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var36.aClass140_429.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var36.anInt424 - anInt2697 + var36.anInt430, var36.anInt425 - Unsorted.anInt3657, var36.anInt427 - TextureOperation13.anInt3363 + var36.anInt426, var36.aLong428, var5, null); - } else if (var36.anInt432 == 256) { - var11 = var36.anInt424 - anInt2697; - var12 = var36.anInt425 - Unsorted.anInt3657; - var27 = var36.anInt427 - TextureOperation13.anInt3363; - var14 = var36.anInt420; - if (var14 == 1 || var14 == 2) { - var15 = -var11; - } else { - var15 = var11; - } - - if (var14 == 2 || var14 == 3) { - var16 = -var27; - } else { - var16 = var27; - } - - if (var16 >= var15) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var36.aClass140_429.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var11 + var36.anInt430, var12, var27 + var36.anInt426, var36.aLong428, var5, null); - } else if (var36.aClass140_423 != null) { - if (HDToolKit.highDetail) { - Class68.method1268(anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var5, var3, var4); - } - - var36.aClass140_423.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var11, var12, var27, var36.aLong428, var5, null); - } - } - } - - Class70 var31 = var2.aClass70_2234; - if (var31 != null) { - if ((var31.anInt1059 & var2.anInt2241) != 0 && Class164_Sub1.method2239(var6, var3, var4, var31.anInt1059)) { - if (HDToolKit.highDetail) { - Class68.method1263(var31.anInt1059, anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var6, var3, var4); - } - - var31.aClass140_1052.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var31.anInt1054 - anInt2697, var31.anInt1057 - Unsorted.anInt3657, var31.anInt1045 - TextureOperation13.anInt3363, var31.aLong1048, var5, null); - } - - if ((var31.anInt1055 & var2.anInt2241) != 0 && Class164_Sub1.method2239(var6, var3, var4, var31.anInt1055)) { - if (HDToolKit.highDetail) { - Class68.method1263(var31.anInt1055, anInt2697, Unsorted.anInt3657, TextureOperation13.anInt3363, var6, var3, var4); - } - - var31.aClass140_1049.animate(0, Class60.anInt936, Unsorted.anInt1037, TextureOperation25.anInt3417, anInt3153, var31.anInt1054 - anInt2697, var31.anInt1057 - Unsorted.anInt3657, var31.anInt1045 - TextureOperation13.anInt3363, var31.aLong1048, var5, null); - } - } - } - - Class3_Sub2 var33; - if (var5 < Class3_Sub17.anInt2456 - 1) { - var33 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var5 + 1][var3][var4]; - if (var33 != null && var33.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var33); - } - } - - if (var3 < Class97.anInt1375) { - var33 = var7[var3 + 1][var4]; - if (var33 != null && var33.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var33); - } - } - - if (var4 < anInt3340) { - var33 = var7[var3][var4 + 1]; - if (var33 != null && var33.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var33); - } - } - - if (var3 > Class97.anInt1375) { - var33 = var7[var3 - 1][var4]; - if (var33 != null && var33.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var33); - } - } - - if (var4 > anInt3340) { - var33 = var7[var3][var4 - 1]; - if (var33 != null && var33.aBoolean2225) { - Class163_Sub1.aLinkedList_2990.method1215(var33); - } - } - } - } - } - } - - static void method2074(int var0, int var1, int var2, int var3, int var4, int var6) { - try { - RSInterface var7 = AbstractSprite.method638(var0, var1); - if (null != var7 && null != var7.anObjectArray203) { - CS2Script var8 = new CS2Script(); - var8.aClass11_2449 = var7; - var8.arguments = var7.anObjectArray203; - Class43.method1065(var8); - } - - RSInterface.anInt278 = var1; - Unsorted.anInt1038 = var3; - BufferedDataStream.anInt872 = var0; - Class164.anInt2051 = var2; - GameObject.aBoolean1837 = true; - Unsorted.anInt1887 = var4; - Class3_Sub28_Sub5.anInt3590 = var6; - Class20.method909(var7); - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "ub.C(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + -120 + ',' + var6 + ')'); - } - } - - static Class29 method2076(int var1) { - try { - Class29 var2 = (Class29) Class136.aReferenceCache_1772.get(var1); - if (var2 == null) { - byte[] var3 = TextureOperation20.aClass153_3154.getFile(16, var1); - { - var2 = new Class29(); - if (null != var3) { - var2.method970(new DataBuffer(var3)); - } - - Class136.aReferenceCache_1772.put(var2, var1); - return var2; - } - } else { - return var2; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ub.D(" + var1 + ')'); - } - } - - static void method2077() { - try { - Class3_Sub31.aReferenceCache_2604.clear(); - Class27.aReferenceCache_511.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ub.B(" + true + ')'); - } - } - - static void method1665(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { - try { - int var7 = var5 + var2; - int var8 = -var5 + var4; - if (var0 != -19619) { - method1665(-17, 11, -118, -38, 115, -2, 113); - } - - int var9 = var5 + var6; - - int var11; - for (var11 = var2; var7 > var11; ++var11) { - TextureOperation18.method282(Class38.anIntArrayArray663[var11], var6, -91, var1, var3); - } - - for (var11 = var4; var8 < var11; --var11) { - TextureOperation18.method282(Class38.anIntArrayArray663[var11], var6, -113, var1, var3); - } - - int var10 = -var5 + var1; - - for (var11 = var7; var11 <= var8; ++var11) { - int[] var12 = Class38.anIntArrayArray663[var11]; - TextureOperation18.method282(var12, var6, -111, var9, var3); - TextureOperation18.method282(var12, var10, -124, var1, var3); - } - - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "ok.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class146.java b/Client/src/main/java/org/runite/client/Class146.java deleted file mode 100644 index f26229d87..000000000 --- a/Client/src/main/java/org/runite/client/Class146.java +++ /dev/null @@ -1,274 +0,0 @@ -package org.runite.client; - - -import javax.media.opengl.GL; -import java.awt.*; - - -abstract class Class146 { - - static int anInt1901 = 2; - static int[][][] anIntArrayArrayArray1903; - static int anInt1904; - static int anInt3 = 0; - - static void updateInterfacePacketCounter(int packetCounter) { - try { - Class113.interfacePacketCounter = packetCounter + 1 & 65535; - RenderAnimationDefinition.aBoolean402 = true; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "uc.D(" + packetCounter + ',' + (byte) -25 + ')'); - } - } - - static int method2080(int var0) { - try { - var0 = (-715827883 & var0 >>> 1) + (1431655765 & var0); - var0 = ((-858993460 & var0) >>> 2) + (var0 & 858993459); - var0 = 252645135 & (var0 >>> 4) + var0; - var0 += var0 >>> 8; - var0 += var0 >>> 16; - return 255 & var0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "uc.E(" + var0 + ',' + -125 + ')'); - } - } - - static void method2083(int var0, int var1, int var2, byte[][][] var3, int var4, byte var5, int var6, int var7) { - ++Class3_Sub28_Sub1.anInt3539; - anInt3 = 0; - int var8 = var6 - 16; - int var9 = var6 + 16; - int var10 = var7 - 16; - int var11 = var7 + 16; - - int var14; - int var15; - int var29; - for (int var12 = TextureOperation22.anInt3419; var12 < Class3_Sub17.anInt2456; ++var12) { - Class3_Sub2[][] var13 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var12]; - - for (var14 = Class163_Sub1_Sub1.anInt4006; var14 < Unsorted.anInt67; ++var14) { - for (var15 = Unsorted.anInt3603; var15 < Class126.anInt1665; ++var15) { - Class3_Sub2 var16 = var13[var14][var15]; - if (var16 != null) { - if (Class23.aBooleanArrayArray457[var14 - Class97.anInt1375 + TextureOperation8.anInt3466][var15 - Class145.anInt3340 + TextureOperation8.anInt3466] && (var3 == null || var12 < var4 || var3[var12][var14][var15] != var5)) { - var16.aBoolean2222 = true; - var16.aBoolean2225 = true; - var16.aBoolean2236 = var16.anInt2223 > 0; - - ++anInt3; - } else { - var16.aBoolean2222 = false; - var16.aBoolean2225 = false; - var16.anInt2227 = 0; - if (var14 >= var8 && var14 <= var9 && var15 >= var10 && var15 <= var11) { - if (var16.aClass70_2234 != null) { - Class70 var17 = var16.aClass70_2234; - var17.aClass140_1049.method1867(0, var12, var17.anInt1057, var17.anInt1054, var17.anInt1045); - if (var17.aClass140_1052 != null) { - var17.aClass140_1052.method1867(0, var12, var17.anInt1057, var17.anInt1054, var17.anInt1045); - } - } - - if (var16.aClass19_2233 != null) { - Class19 var31 = var16.aClass19_2233; - var31.aClass140_429.method1867(var31.anInt420, var12, var31.anInt425, var31.anInt424, var31.anInt427); - if (var31.aClass140_423 != null) { - var31.aClass140_423.method1867(var31.anInt420, var12, var31.anInt425, var31.anInt424, var31.anInt427); - } - } - - if (var16.aClass12_2230 != null) { - Class12 var30 = var16.aClass12_2230; - var30.object.method1867(0, var12, var30.anInt326, var30.anInt324, var30.anInt330); - } - - if (var16.aClass25Array2221 != null) { - for (var29 = 0; var29 < var16.anInt2223; ++var29) { - Class25 var18 = var16.aClass25Array2221[var29]; - var18.aClass140_479.method1867(var18.anInt496, var12, var18.anInt489, var18.anInt482, var18.anInt484); - } - } - } - } - } - } - } - } - - boolean var22 = Class44.anIntArrayArrayArray723 == Unsorted.anIntArrayArrayArray3605; - if (HDToolKit.highDetail) { - GL var24 = HDToolKit.gl; - var24.glPushMatrix(); - var24.glTranslatef((float) (-var0), (float) (-var1), (float) (-var2)); - if (var22) { - Class3_Sub22.method403(); - Unsorted.method551(-1, 3); - TextureOperation23.aBoolean3207 = true; - WaterShader.method2254(); - Unsorted.anInt1244 = -1; - Class145.anInt3072 = -1; - - for (var14 = 0; var14 < Class3_Sub23.aClass3_Sub11ArrayArray2542[0].length; ++var14) { - Class3_Sub11 var28 = Class3_Sub23.aClass3_Sub11ArrayArray2542[0][var14]; - float var26 = 251.5F - (var28.aBoolean2364 ? 1.0F : 0.5F); - if (var28.anInt2355 != Unsorted.anInt1244) { - Unsorted.anInt1244 = var28.anInt2355; - Class3_Sub28_Sub2.method535((byte) 56, var28.anInt2355); - Class92.method1512(Class72.method1297()); - } - - var28.method149(Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, var26, false); - } - - WaterShader.method2253(); - } else { - for (var14 = TextureOperation22.anInt3419; var14 < Class3_Sub17.anInt2456; ++var14) { - for (var15 = 0; var15 < Class3_Sub23.aClass3_Sub11ArrayArray2542[var14].length; ++var15) { - Class3_Sub11 var25 = Class3_Sub23.aClass3_Sub11ArrayArray2542[var14][var15]; - float var33 = 201.5F - 50.0F * (float) var14 - (var25.aBoolean2364 ? 1.0F : 0.5F); - if (var25.anInt2351 != -1 && Class51.anInterface2_838.method18(var25.anInt2351, 255) == 4 && Class128.aBoolean1685) { - Class3_Sub28_Sub2.method535((byte) 56, var25.anInt2355); - } - - var25.method149(Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, var33, false); - } - - if (var14 == 0 && Unsorted.anInt1137 > 0) { - HDToolKit.method1832(101.5F); - Class141.method2038(Class97.anInt1375, Class145.anInt3340, TextureOperation8.anInt3466, Class23.aBooleanArrayArray457); - } - } - - Class68.method1277(Class75_Sub2.aClass3_Sub2ArrayArrayArray2638); - } - - var24.glPopMatrix(); - } - - int var19; - Class3_Sub2 var21; - int var20; - int var23; - Class3_Sub2[][] var27; - int var34; - int var32; - for (var23 = TextureOperation22.anInt3419; var23 < Class3_Sub17.anInt2456; ++var23) { - var27 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var23]; - - for (var15 = -TextureOperation8.anInt3466; var15 <= 0; ++var15) { - var32 = Class97.anInt1375 + var15; - var29 = Class97.anInt1375 - var15; - if (var32 >= Class163_Sub1_Sub1.anInt4006 || var29 < Unsorted.anInt67) { - for (var34 = -TextureOperation8.anInt3466; var34 <= 0; ++var34) { - var19 = Class145.anInt3340 + var34; - var20 = Class145.anInt3340 - var34; - if (var32 >= Class163_Sub1_Sub1.anInt4006) { - if (var19 >= Unsorted.anInt3603) { - var21 = var27[var32][var19]; - if (var21 != null && var21.aBoolean2222) { - Class145.method2073(var21, true); - } - } - - if (var20 < Class126.anInt1665) { - var21 = var27[var32][var20]; - if (var21 != null && var21.aBoolean2222) { - Class145.method2073(var21, true); - } - } - } - - if (var29 < Unsorted.anInt67) { - if (var19 >= Unsorted.anInt3603) { - var21 = var27[var29][var19]; - if (var21 != null && var21.aBoolean2222) { - Class145.method2073(var21, true); - } - } - - if (var20 < Class126.anInt1665) { - var21 = var27[var29][var20]; - if (var21 != null && var21.aBoolean2222) { - Class145.method2073(var21, true); - } - } - } - - if (anInt3 == 0) { - if (!var22) { - TextureOperation37.aBoolean3261 = false; - } - - return; - } - } - } - } - } - - for (var23 = TextureOperation22.anInt3419; var23 < Class3_Sub17.anInt2456; ++var23) { - var27 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var23]; - - for (var15 = -TextureOperation8.anInt3466; var15 <= 0; ++var15) { - var32 = Class97.anInt1375 + var15; - var29 = Class97.anInt1375 - var15; - if (var32 >= Class163_Sub1_Sub1.anInt4006 || var29 < Unsorted.anInt67) { - for (var34 = -TextureOperation8.anInt3466; var34 <= 0; ++var34) { - var19 = Class145.anInt3340 + var34; - var20 = Class145.anInt3340 - var34; - if (var32 >= Class163_Sub1_Sub1.anInt4006) { - if (var19 >= Unsorted.anInt3603) { - var21 = var27[var32][var19]; - if (var21 != null && var21.aBoolean2222) { - Class145.method2073(var21, false); - } - } - - if (var20 < Class126.anInt1665) { - var21 = var27[var32][var20]; - if (var21 != null && var21.aBoolean2222) { - Class145.method2073(var21, false); - } - } - } - - if (var29 < Unsorted.anInt67) { - if (var19 >= Unsorted.anInt3603) { - var21 = var27[var29][var19]; - if (var21 != null && var21.aBoolean2222) { - Class145.method2073(var21, false); - } - } - - if (var20 < Class126.anInt1665) { - var21 = var27[var29][var20]; - if (var21 != null && var21.aBoolean2222) { - Class145.method2073(var21, false); - } - } - } - - if (anInt3 == 0) { - if (!var22) { - TextureOperation37.aBoolean3261 = false; - } - - return; - } - } - } - } - } - - TextureOperation37.aBoolean3261 = false; - } - - abstract int method2078(); - - abstract void method2082(Component var2); - - abstract void method2084(Component var1, int var2); - -} diff --git a/Client/src/main/java/org/runite/client/Class147.java b/Client/src/main/java/org/runite/client/Class147.java deleted file mode 100644 index dd355b9a8..000000000 --- a/Client/src/main/java/org/runite/client/Class147.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.runite.client; - -final class Class147 implements ShaderInterface { - - public final void method22() { - if (Class106.aBoolean1441) { - HDToolKit.method1837(false); - } - - } - - public final int method24() { - return 0; - } - - public final void method23(int var1) { - } - - public final void method21() { - if (Class106.aBoolean1441) { - HDToolKit.method1837(true); - } - - } -} diff --git a/Client/src/main/java/org/runite/client/Class150.java b/Client/src/main/java/org/runite/client/Class150.java deleted file mode 100644 index 920068cb2..000000000 --- a/Client/src/main/java/org/runite/client/Class150.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.runite.client; - -final class Class150 { - - int anInt1928; - int[] anIntArray1929; - int[] anIntArray1930; - int anInt1931; - - - Class150() { - Class3_Sub14.method368(16); - this.anInt1931 = Class3_Sub14.method364() != 0 ? Class3_Sub14.method368(4) + 1 : 1; - if (Class3_Sub14.method364() != 0) { - Class3_Sub14.method368(8); - } - - Class3_Sub14.method368(2); - if (this.anInt1931 > 1) { - this.anInt1928 = Class3_Sub14.method368(4); - } - - this.anIntArray1929 = new int[this.anInt1931]; - this.anIntArray1930 = new int[this.anInt1931]; - - for (int var1 = 0; var1 < this.anInt1931; ++var1) { - Class3_Sub14.method368(8); - this.anIntArray1929[var1] = Class3_Sub14.method368(8); - this.anIntArray1930[var1] = Class3_Sub14.method368(8); - } - - } -} diff --git a/Client/src/main/java/org/runite/client/Class151_Sub1.java b/Client/src/main/java/org/runite/client/Class151_Sub1.java deleted file mode 100644 index fe417adb3..000000000 --- a/Client/src/main/java/org/runite/client/Class151_Sub1.java +++ /dev/null @@ -1,549 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.filestore.ReferenceTable; -import org.rs09.client.filestore.ResourceProvider; -import org.rs09.client.data.HashTable; - -import java.util.Objects; - -final class Class151_Sub1 extends ResourceProvider { - - private final Class41 aClass41_2943; - private ReferenceTable table; - private final HashTable aHashTable_2946 = new HashTable<>(16); - private final int anInt2947; - private int anInt2948 = 0; - private byte[] aByteArray2949; - private ResourceRequest aResourceRequest_2950; - private final Js5Worker aJs5Worker_2953; - Class41 aClass41_2954; - private final int anInt2955; - private final CacheResourceWorker cacheResourceWorker; - private final int anInt2957; - private boolean aBoolean2962; - private final LinkedList aLinkedList_2963 = new LinkedList(); - private int anInt2964 = 0; - private boolean aBoolean2965; - private LinkedList aLinkedList_2966; - private long aLong2967 = 0L; - private final boolean aBoolean2968; - - - public final void request(int var1) { - try { - if (null != this.aClass41_2954) { - Linkable var3; - for (var3 = this.aLinkedList_2963.method1222(); null != var3; var3 = this.aLinkedList_2963.method1221()) { - if ((long) var1 == var3.linkableKey) { - return; - } - } - var3 = new Linkable(); - var3.linkableKey = var1; - this.aLinkedList_2963.method1215(var3); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bg.H(" + var1 + ',' + 127 + ')'); - } - } - - public final ReferenceTable getReferenceTable() { - if (this.table == null) { - if (null == this.aResourceRequest_2950) { - if (this.aJs5Worker_2953.priorityRequestsFull()) { - return null; - } - - this.aResourceRequest_2950 = this.aJs5Worker_2953.request(255, this.anInt2957, (byte) 0, true); - } - - if (this.aResourceRequest_2950.waiting) { - return null; - } else { - byte[] var2 = this.aResourceRequest_2950.getData(); - if (this.aResourceRequest_2950 instanceof CacheResourceRequest) { - try { - if (var2 == null) { - throw new RuntimeException(); - } - - this.table = new ReferenceTable(var2, this.anInt2955); - if (this.table.getRevision() != this.anInt2947) { - throw new RuntimeException(); - } - } catch (RuntimeException var4) { - this.table = null; - if (this.aJs5Worker_2953.priorityRequestsFull()) { - this.aResourceRequest_2950 = null; - } else { - this.aResourceRequest_2950 = this.aJs5Worker_2953.request(255, this.anInt2957, (byte) 0, true); - } - - return null; - } - } else { - try { - if (var2 == null) { - throw new RuntimeException(); - } - - this.table = new ReferenceTable(var2, this.anInt2955); - } catch (RuntimeException var5) { - this.aJs5Worker_2953.closeWithError(); - this.table = null; - if (this.aJs5Worker_2953.priorityRequestsFull()) { - this.aResourceRequest_2950 = null; - } else { - this.aResourceRequest_2950 = this.aJs5Worker_2953.request(255, this.anInt2957, (byte) 0, true); - } - - return null; - } - - if (this.aClass41_2943 != null) { - this.cacheResourceWorker.write(this.aClass41_2943, this.anInt2957, var2); - } - } - - if (null != this.aClass41_2954) { - this.aByteArray2949 = new byte[this.table.getArchiveAmount()]; - this.anInt2948 = 0; - } - - this.aResourceRequest_2950 = null; - return this.table; - } - } else { - return this.table; - } - } - - final void method2101() { - try { - if (this.aClass41_2954 != null) { - this.aBoolean2965 = true; - if (this.aLinkedList_2966 == null) { - this.aLinkedList_2966 = new LinkedList(); - } - - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bg.A(" + true + ')'); - } - } - - final int method2102() { - try { - - return this.anInt2948; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bg.I(" + 0 + ')'); - } - } - - final int method2106() { - try { - if (null == this.table) { - return 0; - } else if (this.aBoolean2962) { - Linkable var2 = this.aLinkedList_2966.method1222(); - if (null == var2) { - return 0; - } else { - - return (int) var2.linkableKey; - } - } else { - return this.table.getValidArchiveAmount(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bg.O(" + 1 + ')'); - } - } - - final void method2107() { - try { - if (null != this.aLinkedList_2966) { - if (this.getReferenceTable() == null) { - return; - } - - boolean var2; - Linkable var3; - int var4; - if (this.aBoolean2962) { - var2 = true; - - for (var3 = this.aLinkedList_2966.method1222(); null != var3; var3 = this.aLinkedList_2966.method1221()) { - var4 = (int) var3.linkableKey; - if (this.aByteArray2949[var4] == 0) { - this.method2109(1, var4, 51); - } - - if (this.aByteArray2949[var4] == 0) { - var2 = false; - } else { - var3.unlink(); - } - } - - while (this.table.archiveFileLengths.length > this.anInt2964) { - if (this.table.archiveFileLengths[this.anInt2964] != 0) { - if (this.cacheResourceWorker.remaining >= 250) { - var2 = false; - break; - } - - if (0 == this.aByteArray2949[this.anInt2964]) { - this.method2109(1, this.anInt2964, 99); - } - - if (this.aByteArray2949[this.anInt2964] == 0) { - var2 = false; - var3 = new Linkable(); - var3.linkableKey = this.anInt2964; - this.aLinkedList_2966.method1215(var3); - } - - } - ++this.anInt2964; - } - - if (var2) { - this.aBoolean2962 = false; - this.anInt2964 = 0; - } - } else if (this.aBoolean2965) { - var2 = true; - - for (var3 = this.aLinkedList_2966.method1222(); var3 != null; var3 = this.aLinkedList_2966.method1221()) { - var4 = (int) var3.linkableKey; - if (this.aByteArray2949[var4] != 1) { - this.method2109(2, var4, 96); - } - - if (this.aByteArray2949[var4] == 1) { - var3.unlink(); - } else { - var2 = false; - } - } - - while (this.anInt2964 < this.table.archiveFileLengths.length) { - if (this.table.archiveFileLengths[this.anInt2964] == 0) { - ++this.anInt2964; - } else { - if (this.aJs5Worker_2953.normalRequestsFull()) { - var2 = false; - break; - } - - if (1 != this.aByteArray2949[this.anInt2964]) { - this.method2109(2, this.anInt2964, 47); - } - - if (this.aByteArray2949[this.anInt2964] != 1) { - var3 = new Linkable(); - var3.linkableKey = this.anInt2964; - this.aLinkedList_2966.method1215(var3); - var2 = false; - } - - ++this.anInt2964; - } - } - - if (var2) { - this.anInt2964 = 0; - this.aBoolean2965 = false; - } - } else { - this.aLinkedList_2966 = null; - } - } - - if (this.aBoolean2968 && this.aLong2967 <= TimeUtils.time()) { - for (ResourceRequest var6 = this.aHashTable_2946.first(); var6 != null; var6 = this.aHashTable_2946.next()) { - if (!var6.waiting) { - if (var6.aBoolean3635) { - if (!var6.priority) { - throw new RuntimeException(); - } - - var6.unlink(); - } else { - var6.aBoolean3635 = true; - } - } - } - - this.aLong2967 = 1000L + TimeUtils.time(); - } - - } catch (RuntimeException var5) { - var5.printStackTrace(); - throw ClientErrorException.clientError(var5, "bg.J(" + true + ')'); - } - } - - public final int percentComplete(int var1) { - ResourceRequest var3 = this.aHashTable_2946.get(var1); - return null != var3 ? var3.getCompletion() : 0; - } - - final int method2108() { - try { - if (this.table == null) { - return 0; - } else { - - return this.table.getValidArchiveAmount(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bg.M(" + (byte) 1 + ')'); - } - } - - private ResourceRequest method2109(int var1, int archiveIndex, int var3) { - try { - ResourceRequest var4 = this.aHashTable_2946.get(archiveIndex); - if (null != var4 && var1 == 0 && !var4.priority && var4.waiting) { - var4.unlink(); - var4 = null; - } - - if (null == var4) { - if (0 == var1) { - if (null == this.aClass41_2954 || this.aByteArray2949[archiveIndex] == -1) { - if (this.aJs5Worker_2953.priorityRequestsFull()) { - return null; - } - - var4 = this.aJs5Worker_2953.request(this.anInt2957, archiveIndex, (byte) 2, true); - } else { - var4 = this.cacheResourceWorker.priorityRead(this.aClass41_2954, archiveIndex); - } - } else if (1 == var1) { - if (this.aClass41_2954 == null) { - throw new RuntimeException(); - } - - var4 = this.cacheResourceWorker.read(this.aClass41_2954, archiveIndex); - } else { - if (var1 != 2) { - throw new RuntimeException(); - } - - if (this.aClass41_2954 == null) { - throw new RuntimeException(); - } - - if (this.aByteArray2949[archiveIndex] != -1) { - throw new RuntimeException(); - } - - if (this.aJs5Worker_2953.normalRequestsFull()) { - return null; - } - - var4 = this.aJs5Worker_2953.request(this.anInt2957, archiveIndex, (byte) 2, false); - } - - this.aHashTable_2946.put(archiveIndex, var4); - } - - if (Objects.requireNonNull(var4).waiting) { - return null; - } else { - byte[] var5 = var4.getData(); - int expectedCRC; - Js5ResourceRequest var12; - if (var4 instanceof CacheResourceRequest) { - try { - if (var5 != null && var5.length > 2) { - TextureOperation24.CRC32.reset(); - TextureOperation24.CRC32.update(var5, 0, -2 + var5.length); - expectedCRC = (int) TextureOperation24.CRC32.getValue(); - if (this.table.archiveCRCs[archiveIndex] == expectedCRC) { - int var8 = (var5[-2 + var5.length] << 8 & 65280) - -(255 & var5[-1 + var5.length]); - if ((65535 & this.table.archiveRevisions[archiveIndex]) == var8) { - if (1 != this.aByteArray2949[archiveIndex]) { - - ++this.anInt2948; - this.aByteArray2949[archiveIndex] = 1; - } - - if (!var4.priority) { - var4.unlink(); - } - - return var4; - } else { - System.err.println("CRC mismatch - [entry=" + this.table.archiveRevisions[archiveIndex] + ", pass=" + var8 + "]!"); - throw new RuntimeException(); - } - } else { - System.err.println("CRC mismatch - [entry=" + this.table.archiveCRCs[archiveIndex] + ", pass=" + expectedCRC + "]!"); - throw new RuntimeException(); - } - } else { -// if(1 != this.aByteArray2949[var2]) { -// ++this.anInt2948; -// this.aByteArray2949[var2] = 1; -// } -// if(!((Class3_Sub28_Sub10)var4).aBoolean3628) { -// ((Class3_Sub28_Sub10)var4).method86(-1024); -// } - -// return null; - throw new RuntimeException("Missing CRC for request " + ((archiveIndex >> 16) & 0xFF) + ", " + (archiveIndex & 0xFFFF)); - } - } catch (Exception var9) { -// var9.printStackTrace(); - this.aByteArray2949[archiveIndex] = -1; - var4.unlink(); - if (var4.priority && !this.aJs5Worker_2953.priorityRequestsFull()) { - var12 = this.aJs5Worker_2953.request(this.anInt2957, archiveIndex, (byte) 2, true); - this.aHashTable_2946.put(archiveIndex, var12); - } - - return null; - } - } else { - try { - if (null == var5 || var5.length <= 2) { - System.err.println("Invalid CRC?"); - throw new RuntimeException(); - } - - TextureOperation24.CRC32.reset(); - TextureOperation24.CRC32.update(var5, 0, var5.length - 2); - expectedCRC = (int) TextureOperation24.CRC32.getValue(); - if (expectedCRC != this.table.archiveCRCs[archiveIndex]) { - TextureOperation24.CRC32.reset(); - TextureOperation24.CRC32.update(var5, 0, var5.length - 4); - expectedCRC = (int) TextureOperation24.CRC32.getValue(); - - if (expectedCRC != this.table.archiveCRCs[archiveIndex]) { - throw new RuntimeException("CRC mismatch - [found=" + this.table.archiveCRCs[archiveIndex] + ", expected=" + expectedCRC + "]!"); - } - } - - this.aJs5Worker_2953.errors = 0; - this.aJs5Worker_2953.status = 0; - } catch (RuntimeException var10) { - var10.printStackTrace(); - this.aJs5Worker_2953.closeWithError(); - var4.unlink(); - if (var4.priority && !this.aJs5Worker_2953.priorityRequestsFull()) { - var12 = this.aJs5Worker_2953.request(this.anInt2957, archiveIndex, (byte) 2, true); - this.aHashTable_2946.put(archiveIndex, var12); - } - - return null; - } - - var5[var5.length + -2] = (byte) (this.table.archiveRevisions[archiveIndex] >>> 8); - var5[var5.length - 1] = (byte) this.table.archiveRevisions[archiveIndex]; - if (null != this.aClass41_2954) { - this.cacheResourceWorker.write(this.aClass41_2954, archiveIndex, var5); - if (1 != this.aByteArray2949[archiveIndex]) { - ++this.anInt2948; - this.aByteArray2949[archiveIndex] = 1; - } - } - - if (!var4.priority) { - var4.unlink(); - } - - return var4; - } - } - } catch (RuntimeException var11) { -// var11.printStackTrace(); - throw ClientErrorException.clientError(var11, "bg.C(" + var1 + ',' + archiveIndex + ',' + var3 + ')'); - } - } - - final void method2110() { - try { - if (this.aLinkedList_2966 != null) { - if (null != this.getReferenceTable()) { - for (Linkable var2 = this.aLinkedList_2963.method1222(); null != var2; var2 = this.aLinkedList_2963.method1221()) { - int var3 = (int) var2.linkableKey; - if (0 <= var3 && this.table.getArchiveAmount() > var3 && this.table.archiveFileLengths[var3] != 0) { - if (this.aByteArray2949[var3] == 0) { - this.method2109(1, var3, 80); - } - - if (-1 == this.aByteArray2949[var3]) { - this.method2109(2, var3, 78); - } - - if (this.aByteArray2949[var3] == 1) { - var2.unlink(); - } - } else { - var2.unlink(); - } - } - - } - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bg.D(" + 0 + ')'); - } - } - - final int method2111() { - try { - return null != this.getReferenceTable() ? 100 : (null == this.aResourceRequest_2950 ? 0 : this.aResourceRequest_2950.getCompletion()); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bg.E(" + -61 + ')'); - } - } - - public final byte[] get(int var1) { - try { - ResourceRequest var3 = this.method2109(0, var1, 103); - if (var3 == null) { - return null; - } else { - byte[] var4 = var3.getData(); - var3.unlink(); - return var4; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bg.K(" + var1 + ',' + 0 + ')'); - } - } - - Class151_Sub1(int var1, Class41 var2, Class41 var3, Js5Worker var4, CacheResourceWorker var5, int var6, int var7) { - try { - this.anInt2957 = var1; - this.aClass41_2954 = var2; - if (this.aClass41_2954 == null) { - this.aBoolean2962 = false; - } else { - this.aBoolean2962 = true; - this.aLinkedList_2966 = new LinkedList(); - } - - this.cacheResourceWorker = var5; - this.anInt2955 = var6; - this.aBoolean2968 = true; - this.aClass41_2943 = var3; - this.aJs5Worker_2953 = var4; - this.anInt2947 = var7; - if (null != this.aClass41_2943) { - this.aResourceRequest_2950 = this.cacheResourceWorker.priorityRead(this.aClass41_2943, this.anInt2957); - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "bg.(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ',' + (var5 != null ? "{...}" : "null") + ',' + var6 + ',' + var7 + ',' + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class152.java b/Client/src/main/java/org/runite/client/Class152.java deleted file mode 100644 index b1c28e61d..000000000 --- a/Client/src/main/java/org/runite/client/Class152.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.runite.client; -/* Class152 - Decompiled by JODE - * Visit http://jode.sourceforge.net/ - */ - -final class Class152 { - private final int anInt1937; - private final int anInt1938; - private final int anInt1939 = Class3_Sub14.method368(16); - private final int anInt1940; - private final int anInt1941; - private final int[] anIntArray1942; - private final int anInt1943; - - Class152() { - anInt1938 = Class3_Sub14.method368(24); - anInt1940 = Class3_Sub14.method368(24); - anInt1941 = Class3_Sub14.method368(24) + 1; - anInt1943 = Class3_Sub14.method368(6) + 1; - anInt1937 = Class3_Sub14.method368(8); - int[] is = new int[anInt1943]; - for (int i = 0; i < anInt1943; i++) { - int i_0_ = 0; - int i_1_ = Class3_Sub14.method368(3); - boolean bool = Class3_Sub14.method364() != 0; - if (bool) - i_0_ = Class3_Sub14.method368(5); - is[i] = i_0_ << 3 | i_1_; - } - anIntArray1942 = new int[anInt1943 * 8]; - for (int i = 0; i < anInt1943 * 8; i++) - anIntArray1942[i] = ((is[i >> 3] & 1 << (i & 0x7)) != 0 - ? Class3_Sub14.method368(8) : -1); - } - - final void method2112(float[] fs, int i, boolean bool) { - for (int i_2_ = 0; i_2_ < i; i_2_++) - fs[i_2_] = 0.0F; - if (!bool) { - int i_3_ = (Class3_Sub14.aClass71Array2406[anInt1937] - .anInt1063); - int i_4_ = anInt1940 - anInt1938; - int i_5_ = i_4_ / anInt1941; - int[] is = new int[i_5_]; - for (int i_6_ = 0; i_6_ < 8; i_6_++) { - int i_7_ = 0; - while (i_7_ < i_5_) { - if (i_6_ == 0) { - int i_8_ = Class3_Sub14.aClass71Array2406 - [anInt1937].method1290(); - for (int i_9_ = i_3_ - 1; i_9_ >= 0; i_9_--) { - if (i_7_ + i_9_ < i_5_) - is[i_7_ + i_9_] = i_8_ % anInt1943; - i_8_ /= anInt1943; - } - } - for (int i_10_ = 0; i_10_ < i_3_; i_10_++) { - int i_11_ = is[i_7_]; - int i_12_ = anIntArray1942[i_11_ * 8 + i_6_]; - if (i_12_ >= 0) { - int i_13_ = anInt1938 + i_7_ * anInt1941; - Class71 class71 - = Class3_Sub14.aClass71Array2406[i_12_]; - if (anInt1939 == 0) { - int i_14_ = (anInt1941 - / class71.anInt1063); - for (int i_15_ = 0; i_15_ < i_14_; i_15_++) { - float[] fs_16_ = class71.method1288(); - for (int i_17_ = 0; - i_17_ < class71.anInt1063; - i_17_++) - fs[i_13_ + i_15_ + i_17_ * i_14_] - += fs_16_[i_17_]; - } - } else { - int i_18_ = 0; - while (i_18_ < anInt1941) { - float[] fs_19_ = class71.method1288(); - for (int i_20_ = 0; - i_20_ < class71.anInt1063; - i_20_++) { - fs[i_13_ + i_18_] += fs_19_[i_20_]; - i_18_++; - } - } - } - } - if (++i_7_ >= i_5_) - break; - } - } - } - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class154.java b/Client/src/main/java/org/runite/client/Class154.java deleted file mode 100644 index dd8bb343a..000000000 --- a/Client/src/main/java/org/runite/client/Class154.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -final class Class154 { - - static int anInt1957; - static int[] anIntArray1960 = new int[14]; - static ReferenceCache aReferenceCache_1964 = new ReferenceCache(5); - static int anInt1966 = -1; - - static void method2146(int var0, int var1, int var2, int var3, GameObject var4, GameObject var5, int var6, int var7, long var8) { - if (var4 != null || var5 != null) { - Class70 var10 = new Class70(); - var10.aLong1048 = var8; - var10.anInt1054 = var1 * 128 + 64; - var10.anInt1045 = var2 * 128 + 64; - var10.anInt1057 = var3; - var10.aClass140_1049 = var4; - var10.aClass140_1052 = var5; - var10.anInt1055 = var6; - var10.anInt1059 = var7; - - for (int var11 = var0; var11 >= 0; --var11) { - if (Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var11][var1][var2] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var11][var1][var2] = new Class3_Sub2(var11, var1, var2); - } - } - - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2].aClass70_2234 = var10; - } - } - - static Class70 method2147(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - return var3 == null ? null : var3.aClass70_2234; - } - - static RSString method2148(int var0) { - try { - if (999999999 <= var0) { - - return TextCore.aClass94_1687; - } else { - return RSString.stringAnimator(var0); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "vf.C(" + var0 + ',' + (byte) -78 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class156.java b/Client/src/main/java/org/runite/client/Class156.java deleted file mode 100644 index e5be52c0d..000000000 --- a/Client/src/main/java/org/runite/client/Class156.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.runite.client; - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -final class Class156 { - - private final int anInt1992; - private final boolean aBoolean1994; - private int anInt1991; - private int anInt1993; - - - public Class156() { - this(false); - } - - Class156(boolean var1) { - this.anInt1991 = -1; - this.anInt1993 = 0; - GL var2 = HDToolKit.gl; - int[] var3 = new int[1]; - var2.glGenBuffersARB(1, var3, 0); - this.aBoolean1994 = var1; - this.anInt1991 = var3[0]; - this.anInt1992 = Class31.anInt582; - } - - final void method2168(ByteBuffer var1) { - if (var1.limit() <= this.anInt1993) { - GL var2 = HDToolKit.gl; - var2.glBindBufferARB('\u8892', this.anInt1991); - var2.glBufferSubDataARB('\u8892', 0, var1.limit(), var1); - } else { - this.method2172(var1); - } - - } - - protected final void finalize() throws Throwable { - if (this.anInt1991 != -1) { - Class31.method989(this.anInt1991, this.anInt1993, this.anInt1992); - this.anInt1991 = -1; - this.anInt1993 = 0; - } - - super.finalize(); - } - - final void method2169() { - GL var1 = HDToolKit.gl; - var1.glBindBufferARB('\u8892', this.anInt1991); - } - - final void method2170(ByteBuffer var1) { - GL var2 = HDToolKit.gl; - var2.glBindBufferARB('\u8893', this.anInt1991); - var2.glBufferDataARB('\u8893', var1.limit(), var1, this.aBoolean1994 ? '\u88e0' : '\u88e4'); - Class31.anInt585 += var1.limit() - this.anInt1993; - this.anInt1993 = var1.limit(); - } - - final void method2171() { - GL var1 = HDToolKit.gl; - var1.glBindBufferARB('\u8893', this.anInt1991); - } - - final void method2172(ByteBuffer var1) { - GL var2 = HDToolKit.gl; - var2.glBindBufferARB('\u8892', this.anInt1991); - var2.glBufferDataARB('\u8892', var1.limit(), var1, this.aBoolean1994 ? '\u88e0' : '\u88e4'); - Class31.anInt585 += var1.limit() - this.anInt1993; - this.anInt1993 = var1.limit(); - } -} diff --git a/Client/src/main/java/org/runite/client/Class157.java b/Client/src/main/java/org/runite/client/Class157.java deleted file mode 100644 index b6ef9b529..000000000 --- a/Client/src/main/java/org/runite/client/Class157.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.runite.client; - -final class Class157 { - - - static int anInt1996; - static Class3_Sub28_Sub17_Sub1 aClass3_Sub28_Sub17_Sub1_2000; - private int anInt1997; - private int[][] anIntArrayArray1999; - private int anInt2001; - - Class157(int var1, int var2) { - try { - if (var2 != var1) { - int var3 = Class107.method1651(var2, var1); - var2 /= var3; - this.anInt2001 = var2; - var1 /= var3; - this.anIntArrayArray1999 = new int[var1][14]; - this.anInt1997 = var1; - - for (int var4 = 0; var4 < var1; ++var4) { - int[] var5 = this.anIntArrayArray1999[var4]; - double var6 = (double) var4 / (double) var1 + 6.0D; - double var10 = (double) var2 / (double) var1; - int var8 = (int) Math.floor(-7.0D + var6 + 1.0D); - int var9 = (int) Math.ceil(7.0D + var6); - if (var8 < 0) { - var8 = 0; - } - - if (var9 > 14) { - var9 = 14; - } - - while (var8 < var9) { - double var12 = ((double) var8 - var6) * 3.141592653589793D; - double var14 = var10; - if (-1.0E-4D > var12 || var12 > 1.0E-4D) { - var14 = var10 * (Math.sin(var12) / var12); - } - - var14 *= 0.54D + 0.46D * Math.cos(((double) var8 - var6) * 0.2243994752564138D); - var5[var8] = (int) Math.floor(65536.0D * var14 + 0.5D); - ++var8; - } - } - - } - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "vj.(" + var1 + ',' + var2 + ')'); - } - } - - static long method2174(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - return var3 != null && var3.aClass70_2234 != null ? var3.aClass70_2234.aLong1048 : 0L; - } - - static SoftwareSprite[] method2176(int var0, CacheIndex var1) { - try { - //System.out.println("Class 157 " + var2); - if (Class75_Sub4.method1351(var1, 0, var0)) { - return method851(); - } else { - return null; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "vj.A(" + 0 + ',' + var0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static SoftwareSprite[] method851() { - try { - SoftwareSprite[] var1 = new SoftwareSprite[Class95.anInt1338]; - - for (int var2 = 0; Class95.anInt1338 > var2; ++var2) { - int var3 = Unsorted.anIntArray3076[var2] * Class140_Sub7.anIntArray2931[var2]; - byte[] var4 = Class163_Sub1.aByteArrayArray2987[var2]; - int[] var5 = new int[var3]; - - for (int var6 = 0; var3 > var6; ++var6) { - var5[var6] = TextureOperation38.spritePalette[Unsorted.bitwiseAnd(255, var4[var6])]; - } - - var1[var2] = new SoftwareSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], var5); - } - - Class39.method1035((byte) 113); - return var1; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "bd.B(" + true + ')'); - } - } - - final byte[] method2173(byte[] var1) { - try { - if (null != this.anIntArrayArray1999) { - int var4 = 14 + (int) ((long) var1.length * (long) this.anInt2001 / (long) this.anInt1997); - int[] var5 = new int[var4]; - int var6 = 0; - int var7 = 0; - - int var8; - for (var8 = 0; var1.length > var8; ++var8) { - int[] var10 = this.anIntArrayArray1999[var7]; - byte var9 = var1[var8]; - - int var11; - for (var11 = 0; var11 < 14; ++var11) { - var5[var6 - -var11] += var10[var11] * var9; - } - - var7 += this.anInt2001; - var11 = var7 / this.anInt1997; - var6 += var11; - var7 -= var11 * this.anInt1997; - } - - var1 = new byte[var4]; - - for (var8 = 0; var4 > var8; ++var8) { - int var13 = var5[var8] - -32768 >> 16; - if (var13 >= -128) { - if (127 >= var13) { - var1[var8] = (byte) var13; - } else { - var1[var8] = 127; - } - } else { - var1[var8] = -128; - } - } - } - - return var1; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "vj.E(" + (var1 != null ? "{...}" : "null") + ',' + (byte) -105 + ')'); - } - } - - final int method2177(int var1) { - try { - if (null != this.anIntArrayArray1999) { - var1 = (int) ((long) this.anInt2001 * (long) var1 / (long) this.anInt1997); - } - - return var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "vj.C(" + var1 + ',' + (byte) 90 + ')'); - } - } - - final int method2178(int var2) { - try { - if (null != this.anIntArrayArray1999) { - var2 = (int) ((long) this.anInt2001 * (long) var2 / (long) this.anInt1997) + 6; - } - - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "vj.D(" + false + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class158.java b/Client/src/main/java/org/runite/client/Class158.java deleted file mode 100644 index 5a75ccde5..000000000 --- a/Client/src/main/java/org/runite/client/Class158.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.runite.client; - -import java.awt.*; - -public abstract class Class158 { - - static int[] anIntArray2004 = new int[]{0, 1, 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, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 73, 74, 76, 78, 83, 84, 85, 86, 91, 92, 93, 94, 95, 97, 103, 104, 105, 106, 107, 108, 113, 114, 115, 116, 118, 119, 120, 121, 122, 123, 124, 125, 133, 134, 136, 138, 143, 144, 145, 146, 151, 152, 153, 154, 155, 157, 163, 164, 165, 166, 168, 169, 174, 175, 176, 177, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 97, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 157, 215, 216, 117, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 66, 66, 66, 66, 66, 66, 65, 75, 79, 79, 79, 79, 87, 87, 87, 87, 77, 96, 98, 98, 98, 98, 98, 250, 251, 109, 109, 109, 109, 117, 252, 167, 126, 126, 126, 126, 126, 126, 125, 135, 139, 139, 139, 139, 147, 147, 147, 147, 137, 156, 158, 158, 158, 158, 158, 253, 254, 170, 170, 170, 170, 178, 255, 178}; - static byte[][][] aByteArrayArrayArray2008; - static int anInt2010 = -16 + (int) (33.0D * Math.random()); - static int paramGameTypeID = 0; - static int anInt2015; - int[] anIntArray2007; - Image anImage2009; - int anInt2011; - int anInt2012; - - static void method2180(CacheIndex var0, CacheIndex var1) { - Unsorted.aClass153_4048 = var0; - TextureOperation19.aClass153_3214 = var1; - } - - static void method2183(int parent, boolean notifyScripts, int parentWidth, int parentHeight, RSInterface[] ifaces) { - int var6 = 0; - for (; ifaces.length > var6; ++var6) { - RSInterface inface = ifaces[var6]; - if (inface != null && inface.parentId == parent) { - Unsorted.calculateInterfaceSize(inface, parentWidth, parentHeight, notifyScripts); - Unsorted.calculateInterfacePosition(inface, parentWidth, parentHeight); - if (inface.anInt247 > inface.anInt240 - inface.width) { - inface.anInt247 = inface.anInt240 - inface.width; - } - - if (inface.anInt208 > -inface.height + inface.anInt252) { - inface.anInt208 = inface.anInt252 - inface.height; - } - - if (inface.anInt208 < 0) { - inface.anInt208 = 0; - } - - if (inface.anInt247 < 0) { - inface.anInt247 = 0; - } - - if (inface.type == 0) { - Unsorted.method2104(inface, notifyScripts, 235 + -343); - } - } - } - } - - static void method2186(Class25 var0) { - for (int var1 = var0.anInt483; var1 <= var0.anInt495; ++var1) { - for (int var2 = var0.anInt478; var2 <= var0.anInt481; ++var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0.anInt493][var1][var2]; - if (var3 != null) { - int var4; - for (var4 = 0; var4 < var3.anInt2223; ++var4) { - if (var3.aClass25Array2221[var4] == var0) { - --var3.anInt2223; - - for (int var5 = var4; var5 < var3.anInt2223; ++var5) { - var3.aClass25Array2221[var5] = var3.aClass25Array2221[var5 + 1]; - var3.anIntArray2237[var5] = var3.anIntArray2237[var5 + 1]; - } - - var3.aClass25Array2221[var3.anInt2223] = null; - break; - } - } - - var3.anInt2228 = 0; - - for (var4 = 0; var4 < var3.anInt2223; ++var4) { - var3.anInt2228 |= var3.anIntArray2237[var4]; - } - } - } - } - - } - - public abstract void method2179(Graphics var3); - - final void method2182() { - Class74.setBuffer(this.anIntArray2007, this.anInt2012, this.anInt2011); - } - - abstract void drawGraphics(int var1, int var2, int var4, Graphics var5, int var6); - - abstract void method2185(int var1, int var3, Component var4); - -} diff --git a/Client/src/main/java/org/runite/client/Class158_Sub1.java b/Client/src/main/java/org/runite/client/Class158_Sub1.java deleted file mode 100644 index d16562f5f..000000000 --- a/Client/src/main/java/org/runite/client/Class158_Sub1.java +++ /dev/null @@ -1,341 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -import java.awt.*; -import java.awt.image.*; - -final class Class158_Sub1 extends Class158 implements ImageProducer, ImageObserver { - - static RSString[] aClass94Array2977 = new RSString[5]; - static int anInt3158 = -8 + (int) (17.0D * Math.random()); - static int anInt1463 = -16 + (int) (Math.random() * 33.0D); - static byte[][][] aByteArrayArrayArray1828; - private ImageConsumer anImageConsumer2978; - private ColorModel aColorModel2979; - static Class3_Sub1 aClass3_Sub1_2980 = new Class3_Sub1(0, -1); - static boolean aBoolean2981 = false; - static ReferenceCache aReferenceCache_2982 = new ReferenceCache(32); - - - public final synchronized void addConsumer(ImageConsumer var1) { - try { - this.anImageConsumer2978 = var1; - var1.setDimensions(this.anInt2012, this.anInt2011); - var1.setProperties(null); - var1.setColorModel(this.aColorModel2979); - var1.setHints(14); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "di.addConsumer(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - private synchronized void method2188(int var1, int var2, int var3, int var5) { - try { - if (null != this.anImageConsumer2978) { - this.anImageConsumer2978.setPixels(var3, var5, var1, var2, this.aColorModel2979, this.anIntArray2007, var5 * this.anInt2012 + var3, this.anInt2012); - this.anImageConsumer2978.imageComplete(2); - - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "di.N(" + var1 + ',' + var2 + ',' + var3 + ',' + (byte) -124 + ',' + var5 + ')'); - } - } - - public final synchronized void removeConsumer(ImageConsumer var1) { - try { - if (this.anImageConsumer2978 == var1) { - this.anImageConsumer2978 = null; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "di.removeConsumer(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - final void drawGraphics(int var1, int var2, int var4, Graphics var5, int var6) { - try { - this.method2188(var1, var4, var2, var6); - Shape var7 = var5.getClip(); - var5.clipRect(var2, var6, var1, var4); - var5.drawImage(this.anImage2009, 0, 0, this); - var5.setClip(var7); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "di.E(" + var1 + ',' + var2 + ',' + 6260 + ',' + var4 + ',' + (var5 != null ? "{...}" : "null") + ',' + var6 + ')'); - } - } - - public final void startProduction(ImageConsumer var1) { - try { - this.addConsumer(var1); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "di.startProduction(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - private synchronized void method2190() { - try { - if (this.anImageConsumer2978 != null) { - this.anImageConsumer2978.setPixels(0, 0, this.anInt2012, this.anInt2011, this.aColorModel2979, this.anIntArray2007, 0, this.anInt2012); - this.anImageConsumer2978.imageComplete(2); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "di.L(" + 19661184 + ')'); - } - } - - public final synchronized boolean isConsumer(ImageConsumer var1) { - try { - return this.anImageConsumer2978 == var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "di.isConsumer(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static boolean method2191(int var0, int var1, int var2, int var4, int var5, int var6, int var7, boolean var8, int var9, int var10, int var11) { - try { - int var12; - int var13; - for (var12 = 0; var12 < 104; ++var12) { - for (var13 = 0; var13 < 104; ++var13) { - Class84.anIntArrayArray1160[var12][var13] = 0; - Class97.anIntArrayArray1373[var12][var13] = 99999999; - } - } - - var12 = var2; - Class84.anIntArrayArray1160[var2][var10] = 99; - var13 = var10; - Class97.anIntArrayArray1373[var2][var10] = 0; - byte var14 = 0; - boolean var16 = false; - int var15 = 0; - TextureOperation38.anIntArray3456[var14] = var2; - int var27 = var14 + 1; - Class45.anIntArray729[var14] = var10; - int[][] var17 = AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].anIntArrayArray1304; - - int var18; - while (var15 != var27) { - var13 = Class45.anIntArray729[var15]; - var12 = TextureOperation38.anIntArray3456[var15]; - var15 = 4095 & var15 + 1; - if (var12 == var0 && var13 == var4) { - var16 = true; - break; - } - - if (var9 != 0) { - if ((var9 < 5 || 10 == var9) && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1488(var4, var12, var13, var0, var9 + -1, 1, var7)) { - var16 = true; - break; - } - - if (var9 < 10 && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1492(var4, -1 + var9, var0, var13, 1, var7, var12, 95)) { - var16 = true; - break; - } - } - - if (var11 != 0 && 0 != var6 && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1498(var0, var13, var12, 1, var11, var1, var4, var6)) { - var16 = true; - break; - } - - var18 = 1 + Class97.anIntArrayArray1373[var12][var13]; - if (0 < var12 && Class84.anIntArrayArray1160[var12 + -1][var13] == 0 && (19661064 & var17[var12 + -1][var13]) == 0) { - TextureOperation38.anIntArray3456[var27] = -1 + var12; - Class45.anIntArray729[var27] = var13; - var27 = var27 - -1 & 4095; - Class84.anIntArrayArray1160[-1 + var12][var13] = 2; - Class97.anIntArrayArray1373[-1 + var12][var13] = var18; - } - - if (103 > var12 && Class84.anIntArrayArray1160[var12 + 1][var13] == 0 && (var17[var12 + 1][var13] & 19661184) == 0) { - TextureOperation38.anIntArray3456[var27] = var12 - -1; - Class45.anIntArray729[var27] = var13; - var27 = 1 + var27 & 4095; - Class84.anIntArrayArray1160[var12 - -1][var13] = 8; - Class97.anIntArrayArray1373[1 + var12][var13] = var18; - } - - if (var13 > 0 && Class84.anIntArrayArray1160[var12][var13 - 1] == 0 && (19661058 & var17[var12][-1 + var13]) == 0) { - TextureOperation38.anIntArray3456[var27] = var12; - Class45.anIntArray729[var27] = -1 + var13; - Class84.anIntArrayArray1160[var12][var13 - 1] = 1; - var27 = var27 + 1 & 4095; - Class97.anIntArrayArray1373[var12][-1 + var13] = var18; - } - - if (103 > var13 && Class84.anIntArrayArray1160[var12][1 + var13] == 0 && (19661088 & var17[var12][var13 + 1]) == 0) { - TextureOperation38.anIntArray3456[var27] = var12; - Class45.anIntArray729[var27] = var13 - -1; - var27 = 1 + var27 & 4095; - Class84.anIntArrayArray1160[var12][1 + var13] = 4; - Class97.anIntArrayArray1373[var12][var13 - -1] = var18; - } - - if (var12 > 0 && var13 > 0 && Class84.anIntArrayArray1160[-1 + var12][var13 - 1] == 0 && (var17[var12 - 1][-1 + var13] & 19661070) == 0 && (19661064 & var17[var12 - 1][var13]) == 0 && (19661058 & var17[var12][-1 + var13]) == 0) { - TextureOperation38.anIntArray3456[var27] = -1 + var12; - Class45.anIntArray729[var27] = var13 + -1; - var27 = 1 + var27 & 4095; - Class84.anIntArrayArray1160[-1 + var12][-1 + var13] = 3; - Class97.anIntArrayArray1373[var12 - 1][var13 + -1] = var18; - } - - if (var12 < 103 && 0 < var13 && Class84.anIntArrayArray1160[var12 - -1][var13 - 1] == 0 && 0 == (19661187 & var17[var12 - -1][-1 + var13]) && (19661184 & var17[var12 - -1][var13]) == 0 && (19661058 & var17[var12][-1 + var13]) == 0) { - TextureOperation38.anIntArray3456[var27] = var12 + 1; - Class45.anIntArray729[var27] = -1 + var13; - var27 = 4095 & var27 + 1; - Class84.anIntArrayArray1160[1 + var12][var13 + -1] = 9; - Class97.anIntArrayArray1373[var12 - -1][-1 + var13] = var18; - } - - if (0 < var12 && var13 < 103 && 0 == Class84.anIntArrayArray1160[var12 + -1][var13 + 1] && 0 == (19661112 & var17[var12 + -1][1 + var13]) && 0 == (var17[var12 + -1][var13] & 19661064) && (19661088 & var17[var12][1 + var13]) == 0) { - TextureOperation38.anIntArray3456[var27] = var12 - 1; - Class45.anIntArray729[var27] = 1 + var13; - Class84.anIntArrayArray1160[-1 + var12][var13 - -1] = 6; - var27 = 4095 & 1 + var27; - Class97.anIntArrayArray1373[-1 + var12][1 + var13] = var18; - } - - if (var12 < 103 && var13 < 103 && Class84.anIntArrayArray1160[var12 - -1][1 + var13] == 0 && (19661280 & var17[1 + var12][var13 + 1]) == 0 && (var17[1 + var12][var13] & 19661184) == 0 && (19661088 & var17[var12][var13 - -1]) == 0) { - TextureOperation38.anIntArray3456[var27] = var12 + 1; - Class45.anIntArray729[var27] = var13 - -1; - Class84.anIntArrayArray1160[var12 + 1][1 + var13] = 12; - var27 = var27 - -1 & 4095; - Class97.anIntArrayArray1373[1 + var12][var13 - -1] = var18; - } - } - - Class129.anInt1692 = 0; - int var19; - if (!var16) { - if (!var8) { - return false; - } - - var18 = 1000; - var19 = 100; - byte var20 = 10; - - for (int var21 = var0 + -var20; var20 + var0 >= var21; ++var21) { - for (int var22 = var4 + -var20; var4 - -var20 >= var22; ++var22) { - if (var21 >= 0 && var22 >= 0 && 104 > var21 && var22 < 104 && 100 > Class97.anIntArrayArray1373[var21][var22]) { - int var24 = 0; - if (var4 > var22) { - var24 = var4 + -var22; - } else if (var6 + var4 - 1 < var22) { - var24 = 1 + (-var4 - var6) + var22; - } - - int var23 = 0; - if (var0 <= var21) { - if (-1 + var11 + var0 < var21) { - var23 = 1 - var11 - (var0 - var21); - } - } else { - var23 = var0 + -var21; - } - - int var25 = var24 * var24 + var23 * var23; - if (var18 > var25 || var18 == var25 && Class97.anIntArrayArray1373[var21][var22] < var19) { - var13 = var22; - var18 = var25; - var12 = var21; - var19 = Class97.anIntArrayArray1373[var21][var22]; - } - } - } - } - - if (var18 == 1000) { - return false; - } - - if (var2 == var12 && var10 == var13) { - return false; - } - - Class129.anInt1692 = 1; - } - - byte var28 = 0; - TextureOperation38.anIntArray3456[var28] = var12; - var15 = var28 + 1; - Class45.anIntArray729[var28] = var13; - - for (var18 = var19 = Class84.anIntArrayArray1160[var12][var13]; var2 != var12 || var13 != var10; var18 = Class84.anIntArrayArray1160[var12][var13]) { - if (var19 != var18) { - var19 = var18; - TextureOperation38.anIntArray3456[var15] = var12; - Class45.anIntArray729[var15++] = var13; - } - - if ((var18 & 2) == 0) { - if (0 != (8 & var18)) { - --var12; - } - } else { - ++var12; - } - - if ((1 & var18) == 0) { - if (0 != (4 & var18)) { - --var13; - } - } else { - ++var13; - } - } - - if (var15 > 0) { - TextureOperation7.method299(100, var15, var5); - return true; - } else return var5 != 1; - } catch (RuntimeException var26) { - throw ClientErrorException.clientError(var26, "di.J(" + var0 + ',' + var1 + ',' + var2 + ',' + -1001 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var10 + ',' + var11 + ')'); - } - } - - public final void method2179(Graphics var3) { - try { - this.method2190(); - var3.drawImage(this.anImage2009, 0, 0, this); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "di.C(" + 0 + ',' + 0 + ',' + (var3 != null ? "{...}" : "null") + ',' + 0 + ')'); - } - } - - final void method2185(int var1, int var3, Component var4) { - try { - this.anInt2011 = var1; - this.anIntArray2007 = new int[var3 * var1 + 1]; - this.anInt2012 = var3; - this.aColorModel2979 = new DirectColorModel(32, 16711680, 65280, 255); - this.anImage2009 = var4.createImage(this); - this.method2190(); - var4.prepareImage(this.anImage2009, this); - this.method2190(); - var4.prepareImage(this.anImage2009, this); - this.method2190(); - var4.prepareImage(this.anImage2009, this); - this.method2182(); - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "di.F(" + var1 + ',' + false + ',' + var3 + ',' + (var4 != null ? "{...}" : "null") + ')'); - } - } - - public final boolean imageUpdate(Image var1, int var2, int var3, int var4, int var5, int var6) { - try { - return true; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "di.imageUpdate(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - public final void requestTopDownLeftRightResend(ImageConsumer var1) { - } - -} diff --git a/Client/src/main/java/org/runite/client/Class158_Sub2.java b/Client/src/main/java/org/runite/client/Class158_Sub2.java deleted file mode 100644 index 3bc58415a..000000000 --- a/Client/src/main/java/org/runite/client/Class158_Sub2.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.runite.client; - -import java.awt.*; -import java.awt.image.*; -import java.util.Hashtable; - -public final class Class158_Sub2 extends Class158 { - - private Component aComponent2983; - - - public final void method2179(Graphics var3) { - var3.drawImage(this.anImage2009, 0, 0, this.aComponent2983); - } - - final void method2185(int var1, int var3, Component var4) { - this.anIntArray2007 = new int[var3 * var1 + 1]; - this.anInt2011 = var1; - this.anInt2012 = var3; - DataBufferInt var5 = new DataBufferInt(this.anIntArray2007, this.anIntArray2007.length); - DirectColorModel var6 = new DirectColorModel(32, 16711680, 65280, 255); - WritableRaster var7 = Raster.createWritableRaster(var6.createCompatibleSampleModel(this.anInt2012, this.anInt2011), var5, null); - this.anImage2009 = new BufferedImage(var6, var7, false, new Hashtable<>()); - this.aComponent2983 = var4; - this.method2182(); - } - - final void drawGraphics(int width, int x, int height, Graphics var5, int y) { - Shape var7 = var5.getClip(); - var5.clipRect(x, y, width, height); - var5.drawImage(this.anImage2009, 0, 0, this.aComponent2983); - var5.setClip(var7); - } -} diff --git a/Client/src/main/java/org/runite/client/Class159.java b/Client/src/main/java/org/runite/client/Class159.java deleted file mode 100644 index 848f774ce..000000000 --- a/Client/src/main/java/org/runite/client/Class159.java +++ /dev/null @@ -1,157 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -public final class Class159 { - - static ReferenceCache aReferenceCache_2016 = new ReferenceCache(100); - static int[] anIntArray2017 = new int[]{1, 2, 4, 8}; - static int anInt2020 = 0; - static int[] anIntArray2021 = new int[2]; - public static int localPlayerCount = 0; - static int anInt2023 = 0; - static int anInt2024 = 0; - static int[] anIntArray1681; - static int anInt1740 = 0; - - - static Class12 method2193(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if (var3 == null) { - return null; - } else { - Class12 var4 = var3.aClass12_2230; - var3.aClass12_2230 = null; - return var4; - } - } - - static boolean method2194() { - try { - if (Unsorted.paramJavaScriptEnabled) { - try { - return !(Boolean) RSString.parse("showingVideoAd").method1577(Class38.gameSignlink.gameApplet); - } catch (Throwable var2) { - } - } - - return true; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "vl.B(" + 255 + ')'); - } - } - - static void method2195(int var0) { - try { - short var2 = 256; - if (var0 > var2) { - var0 = var2; - } - - if (var0 > 10) { - var0 = 10; - } - - Class72.anInt1071 += var0 * 128; - int var3; - if (Class161.anIntArray2026.length < Class72.anInt1071) { - Class72.anInt1071 -= Class161.anIntArray2026.length; - var3 = (int) (12.0D * Math.random()); - TextureOperation30.method215((byte) -119, Class163_Sub2_Sub1.aClass109_Sub1Array4027[var3]); - } - - var3 = 0; - int var5 = (var2 + -var0) * 128; - int var4 = 128 * var0; - - int var6; - int var7; - for (var6 = 0; var6 < var5; ++var6) { - var7 = anIntArray1681[var3 - -var4] - var0 * Class161.anIntArray2026[-1 + Class161.anIntArray2026.length & Class72.anInt1071 + var3] / 6; - if (0 > var7) { - var7 = 0; - } - - anIntArray1681[var3++] = var7; - } - - int var8; - int var9; - for (var6 = var2 + -var0; var2 > var6; ++var6) { - var7 = var6 * 128; - - for (var8 = 0; 128 > var8; ++var8) { - var9 = (int) (100.0D * Math.random()); - if (var9 < 50 && var8 > 10 && var8 < 118) { - anIntArray1681[var8 + var7] = 255; - } else { - anIntArray1681[var8 + var7] = 0; - } - } - } - - for (var6 = 0; var2 + -var0 > var6; ++var6) { - Class3_Sub28_Sub5.anIntArray3592[var6] = Class3_Sub28_Sub5.anIntArray3592[var6 - -var0]; - } - - for (var6 = var2 - var0; var2 > var6; ++var6) { - Class3_Sub28_Sub5.anIntArray3592[var6] = (int) (Math.sin((double) Class1.anInt57 / 14.0D) * 16.0D + 14.0D * Math.sin((double) Class1.anInt57 / 15.0D) + 12.0D * Math.sin((double) Class1.anInt57 / 16.0D)); - ++Class1.anInt57; - } - - anInt1740 += var0; - var6 = (var0 - -(1 & Class44.anInt719)) / 2; - if (var6 > 0) { - for (var7 = 0; anInt1740 > var7; ++var7) { - var8 = 2 + (int) (124.0D * Math.random()); - var9 = (int) (128.0D * Math.random()) + 128; - anIntArray1681[var8 - -(var9 << 7)] = 192; - } - - anInt1740 = 0; - - int var10; - for (var7 = 0; var7 < var2; ++var7) { - var9 = var7 * 128; - var8 = 0; - - for (var10 = -var6; var10 < 128; ++var10) { - if (128 > var6 + var10) { - var8 += anIntArray1681[var9 + (var10 - -var6)]; - } - - if (-1 + -var6 + var10 >= 0) { - var8 -= anIntArray1681[-var6 + -1 + var10 + var9]; - } - - if (0 <= var10) { - BufferedDataStream.anIntArray3805[var10 + var9] = var8 / (1 + var6 * 2); - } - } - } - - for (var7 = 0; 128 > var7; ++var7) { - var8 = 0; - - for (var9 = -var6; var2 > var9; ++var9) { - var10 = var9 * 128; - if (var2 > var9 + var6) { - var8 += BufferedDataStream.anIntArray3805[var6 * 128 + (var7 - -var10)]; - } - - if (0 <= var9 - var6 - 1) { - var8 -= BufferedDataStream.anIntArray3805[-((1 + var6) * 128) + (var7 - -var10)]; - } - - if (var9 >= 0) { - anIntArray1681[var10 + var7] = var8 / (var6 * 2 - -1); - } - } - } - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "vl.E(" + var0 + ',' + 0 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class160.java b/Client/src/main/java/org/runite/client/Class160.java deleted file mode 100644 index c49e5a987..000000000 --- a/Client/src/main/java/org/runite/client/Class160.java +++ /dev/null @@ -1,235 +0,0 @@ -package org.runite.client; - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -final class Class160 implements ShaderInterface { - - private int anInt2187 = -1; - private boolean aBoolean2188 = false; - private int[] anIntArray2189 = null; - - - public Class160() { - if (HDToolKit.supportTextureCubeMap && HDToolKit.maxTextureUnits >= 2) { - this.method2199(); - GL var1 = HDToolKit.gl; - var1.glBindTexture('\u8513', this.anIntArray2189[0]); - var1.glTexParameteri('\u8513', 10241, 9729); - var1.glTexParameteri('\u8513', 10240, 9729); - var1.glTexParameteri('\u8513', '\u8072', '\u812f'); - var1.glTexParameteri('\u8513', 10242, '\u812f'); - var1.glTexParameteri('\u8513', 10243, '\u812f'); - var1.glBindTexture('\u8513', this.anIntArray2189[1]); - var1.glTexParameteri('\u8513', 10241, 9729); - var1.glTexParameteri('\u8513', 10240, 9729); - var1.glTexParameteri('\u8513', '\u8072', '\u812f'); - var1.glTexParameteri('\u8513', 10242, '\u812f'); - var1.glTexParameteri('\u8513', 10243, '\u812f'); - var1.glBindTexture('\u8513', this.anIntArray2189[2]); - var1.glTexParameteri('\u8513', 10241, 9729); - var1.glTexParameteri('\u8513', 10240, 9729); - var1.glTexParameteri('\u8513', '\u8072', '\u812f'); - var1.glTexParameteri('\u8513', 10242, '\u812f'); - var1.glTexParameteri('\u8513', 10243, '\u812f'); - this.aBoolean2188 = HDToolKit.maxTextureUnits < 3; - } - - this.method2198(); - } - - private void method2198() { - GL var1 = HDToolKit.gl; - this.anInt2187 = var1.glGenLists(2); - var1.glNewList(this.anInt2187, 4864); - if (this.anIntArray2189 == null) { - var1.glTexEnvi(8960, '\u8588', '\u8577'); - } else { - var1.glActiveTexture('\u84c1'); - var1.glTexGeni(8192, 9472, '\u8511'); - var1.glTexGeni(8193, 9472, '\u8511'); - var1.glTexGeni(8194, 9472, '\u8511'); - var1.glEnable(3168); - var1.glEnable(3169); - var1.glEnable(3170); - var1.glEnable('\u8513'); - var1.glMatrixMode(5890); - var1.glLoadIdentity(); - var1.glRotatef(22.5F, 1.0F, 0.0F, 0.0F); - var1.glMatrixMode(5888); - if (this.aBoolean2188) { - var1.glTexEnvi(8960, '\u8571', 260); - var1.glTexEnvi(8960, '\u8590', 770); - var1.glTexEnvi(8960, '\u8572', 7681); - var1.glTexEnvi(8960, '\u8588', '\u8577'); - } else { - var1.glTexEnvi(8960, '\u8571', 7681); - var1.glTexEnvi(8960, '\u8580', '\u8578'); - var1.glTexEnvi(8960, '\u8572', 8448); - var1.glActiveTexture('\u84c2'); - var1.glTexEnvi(8960, 8704, '\u8570'); - var1.glTexEnvi(8960, '\u8571', 260); - var1.glTexEnvi(8960, '\u8580', '\u8578'); - var1.glTexEnvi(8960, '\u8581', '\u8578'); - var1.glTexEnvi(8960, '\u8591', 770); - var1.glTexEnvi(8960, '\u8572', 7681); - var1.glTexEnvi(8960, '\u8588', '\u8577'); - var1.glBindTexture(3553, HDToolKit.anInt1810); - var1.glEnable(3553); - } - - var1.glActiveTexture('\u84c0'); - } - - var1.glEndList(); - var1.glNewList(this.anInt2187 + 1, 4864); - if (this.anIntArray2189 == null) { - var1.glTexEnvi(8960, '\u8588', 5890); - } else { - var1.glActiveTexture('\u84c1'); - var1.glDisable(3168); - var1.glDisable(3169); - var1.glDisable(3170); - var1.glDisable('\u8513'); - var1.glMatrixMode(5890); - var1.glLoadIdentity(); - var1.glMatrixMode(5888); - if (this.aBoolean2188) { - var1.glTexEnvi(8960, '\u8571', 8448); - var1.glTexEnvi(8960, '\u8590', 768); - var1.glTexEnvi(8960, '\u8572', 8448); - var1.glTexEnvi(8960, '\u8588', 5890); - } else { - var1.glTexEnvi(8960, '\u8571', 8448); - var1.glTexEnvi(8960, '\u8580', 5890); - var1.glActiveTexture('\u84c2'); - var1.glTexEnvi(8960, 8704, 8448); - var1.glTexEnvi(8960, '\u8571', 8448); - var1.glTexEnvi(8960, '\u8580', 5890); - var1.glTexEnvi(8960, '\u8591', 768); - var1.glTexEnvi(8960, '\u8572', 8448); - var1.glTexEnvi(8960, '\u8588', 5890); - var1.glDisable(3553); - } - - var1.glActiveTexture('\u84c0'); - } - - var1.glEndList(); - } - - public final void method21() { - GL var1 = HDToolKit.gl; - if (Class106.aBoolean1441) { - var1.glCallList(this.anInt2187 + 1); - } else { - var1.glTexEnvi(8960, '\u8588', 5890); - } - - } - - public final int method24() { - return 4; - } - - public final void method22() { - GL var1 = HDToolKit.gl; - HDToolKit.method1847(1); - if (Class106.aBoolean1441) { - var1.glCallList(this.anInt2187); - } else { - var1.glTexEnvi(8960, '\u8588', '\u8577'); - } - - } - - public final void method23(int var1) { - GL var2 = HDToolKit.gl; - if (Class106.aBoolean1441 && this.anIntArray2189 != null) { - var2.glActiveTexture('\u84c1'); - var2.glBindTexture('\u8513', this.anIntArray2189[var1 - 1]); - var2.glActiveTexture('\u84c0'); - } - - } - - private void method2199() { - GL var8 = HDToolKit.gl; - if (this.anIntArray2189 == null) { - this.anIntArray2189 = new int[3]; - var8.glGenTextures(3, this.anIntArray2189, 0); - } - - short var9 = 4096; - byte[] var10 = new byte[var9]; - byte[] var11 = new byte[var9]; - byte[] var12 = new byte[var9]; - - for (int var13 = 0; var13 < 6; ++var13) { - int var14 = 0; - - for (int var15 = 0; var15 < 64; ++var15) { - for (int var16 = 0; var16 < 64; ++var16) { - float var5 = 2.0F * (float) var16 / 64.0F - 1.0F; - float var6 = 2.0F * (float) var15 / 64.0F - 1.0F; - float var7 = (float) (1.0D / Math.sqrt(var5 * var5 + 1.0F + var6 * var6)); - var5 *= var7; - var6 *= var7; - float var4; - if (var13 == 0) { - var4 = -var5; - } else if (var13 == 1) { - var4 = var5; - } else if (var13 == 2) { - var4 = var6; - } else if (var13 == 3) { - var4 = -var6; - } else if (var13 == 4) { - var4 = var7; - } else { - var4 = -var7; - } - - int var1; - int var2; - int var3; - if (var4 > 0.0F) { - var1 = (int) (Math.pow(var4, 96.0D) * 255.0D); - var2 = (int) (Math.pow(var4, 36.0D) * 255.0D); - var3 = (int) (Math.pow(var4, 12.0D) * 255.0D); - } else { - var3 = 0; - var2 = 0; - var1 = 0; - } - - if (HDToolKit.maxTextureUnits < 3) { - var1 /= 5; - var2 /= 5; - var3 /= 5; - } else { - var1 /= 2; - var2 /= 2; - var3 /= 2; - } - - var11[var14] = (byte) var1; - var12[var14] = (byte) var2; - var10[var14] = (byte) var3; - ++var14; - } - } - - var8.glBindTexture('\u8513', this.anIntArray2189[0]); - var8.glTexImage2D('\u8515' + var13, 0, 6406, 64, 64, 0, 6406, 5121, ByteBuffer.wrap(var11)); - var8.glBindTexture('\u8513', this.anIntArray2189[1]); - var8.glTexImage2D('\u8515' + var13, 0, 6406, 64, 64, 0, 6406, 5121, ByteBuffer.wrap(var12)); - var8.glBindTexture('\u8513', this.anIntArray2189[2]); - var8.glTexImage2D('\u8515' + var13, 0, 6406, 64, 64, 0, 6406, 5121, ByteBuffer.wrap(var10)); - Class31.anInt580 += var9 * 3; - } - - } -} diff --git a/Client/src/main/java/org/runite/client/Class161.java b/Client/src/main/java/org/runite/client/Class161.java deleted file mode 100644 index ffc92a867..000000000 --- a/Client/src/main/java/org/runite/client/Class161.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.runite.client; - -public final class Class161 { - - static int[] anIntArray2026; - static int anInt2027 = -1; - public static int anInt2028 = 0; - static Class33 aClass33_2034; - static RSString aClass94_2035 = null; - - static void method2200(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - try { - int var8 = 0; - int var9 = var6; - int var10 = 0; - int var11 = var4 + -var7; - int var13 = var4 * var4; - int var12 = var6 + -var7; - int var14 = var6 * var6; - int var16 = var12 * var12; - int var17 = var14 << 1; - int var18 = var13 << 1; - int var19 = var16 << 1; - int var15 = var11 * var11; - int var20 = var15 << 1; - int var21 = var6 << 1; - int var22 = var12 << 1; - int var24 = -((-1 + var21) * var18) + var14; - int var25 = var15 * (-var22 + 1) - -var19; - int var23 = (-var21 + 1) * var13 + var17; - int var27 = var13 << 2; - int var26 = -(var20 * (var22 - 1)) + var16; - int var29 = var15 << 2; - int var28 = var14 << 2; - int var30 = var16 << 2; - int var32 = (-3 + var21) * var18; - int var31 = var17 * 3; - int var34 = (-3 + var22) * var20; - int var33 = var19 * 3; - int var35 = var28; - int var37 = var30; - int var38 = (-1 + var12) * var29; - if (var5 < 73) { - method2200(108, -88, 76, -36, -95, -64, -84, 85); - } - - int var36 = (-1 + var6) * var27; - int[] var39 = Class38.anIntArrayArray663[var2]; - TextureOperation18.method282(var39, -var4 + var1, -87, -var11 + var1, var3); - TextureOperation18.method282(var39, -var11 + var1, -66, var1 - -var11, var0); - TextureOperation18.method282(var39, var1 - -var11, -68, var4 + var1, var3); - - while (var9 > 0) { - if (var23 < 0) { - while (0 > var23) { - var24 += var35; - var23 += var31; - ++var8; - var35 += var28; - var31 += var28; - } - } - - if (var24 < 0) { - var23 += var31; - ++var8; - var31 += var28; - var24 += var35; - var35 += var28; - } - - boolean var40 = var9 <= var12; - if (var40) { - if (var25 < 0) { - while (var25 < 0) { - var25 += var33; - var33 += var30; - ++var10; - var26 += var37; - var37 += var30; - } - } - - if (0 > var26) { - var25 += var33; - ++var10; - var33 += var30; - var26 += var37; - var37 += var30; - } - - var26 += -var34; - var34 -= var29; - var25 += -var38; - var38 -= var29; - } - - var23 += -var36; - var24 += -var32; - var32 -= var27; - --var9; - int var41 = var2 - var9; - int var43 = var8 + var1; - var36 -= var27; - int var42 = var9 + var2; - int var44 = -var8 + var1; - if (var40) { - int var45 = var1 + var10; - int var46 = -var10 + var1; - TextureOperation18.method282(Class38.anIntArrayArray663[var41], var44, -93, var46, var3); - TextureOperation18.method282(Class38.anIntArrayArray663[var41], var46, 121, var45, var0); - TextureOperation18.method282(Class38.anIntArrayArray663[var41], var45, -47, var43, var3); - TextureOperation18.method282(Class38.anIntArrayArray663[var42], var44, 124, var46, var3); - TextureOperation18.method282(Class38.anIntArrayArray663[var42], var46, -73, var45, var0); - TextureOperation18.method282(Class38.anIntArrayArray663[var42], var45, 111, var43, var3); - } else { - TextureOperation18.method282(Class38.anIntArrayArray663[var41], var44, 96, var43, var3); - TextureOperation18.method282(Class38.anIntArrayArray663[var42], var44, 124, var43, var3); - } - } - - } catch (RuntimeException var47) { - throw ClientErrorException.clientError(var47, "wb.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - static int method2201(WorldListEntry var0, int var1, int var2, WorldListEntry var3, boolean var4) { - try { - if (var1 == 1) { - int var5 = var0.anInt722; - int var6 = var3.anInt722; - if (!var4) { - if (-1 == var6) { - var6 = 2001; - } - - if (var5 == -1) { - var5 = 2001; - } - } - - return -var6 + var5; - } else { - return 2 == var1 ? var0.method1078(102).name.method1546(var3.method1078(-119).name) : (var1 == 3 ? (var0.activity.equalsString(RSString.parse(")2")) ? (var3.activity.equalsString(RSString.parse(")2")) ? 0 : (var4 ? -1 : 1)) : (var3.activity.equalsString(RSString.parse(")2")) ? (var4 ? 1 : -1) : var0.activity.method1546(var3.activity))) : (var2 <= 67 ? 36 : (var1 == 4 ? (!var0.isLootShare() ? (!var3.isLootShare() ? 0 : -1) : (var3.isLootShare() ? 0 : 1)) : (var1 == 5 ? (var0.isQuickchat() ? (!var3.isQuickchat() ? 1 : 0) : (var3.isQuickchat() ? -1 : 0)) : (var1 != 6 ? (7 == var1 ? (var0.isMembers() ? (var3.isMembers() ? 0 : 1) : (var3.isMembers() ? -1 : 0)) : -var3.worldId + var0.worldId) : (var0.isPVP() ? (!var3.isPVP() ? 1 : 0) : (var3.isPVP() ? -1 : 0))))))); - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "wb.C(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class162.java b/Client/src/main/java/org/runite/client/Class162.java deleted file mode 100644 index d180ed7c1..000000000 --- a/Client/src/main/java/org/runite/client/Class162.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.runite.client; - -final class Class162 { - - static int anInt2036; - static int anInt2038 = 0; - - - static void method2203(Player var0) { - try { - Class3_Sub9 var2 = (Class3_Sub9) Unsorted.aHashTable_4046.get(var0.displayName.toLong()); - - if (null != var2) { - if (var2.aClass3_Sub24_Sub1_2312 != null) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var2.aClass3_Sub24_Sub1_2312); - var2.aClass3_Sub24_Sub1_2312 = null; - } - - var2.unlink(); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wc.B(" + (var0 != null ? "{...}" : "null") + ',' + 8 + ')'); - } - } - - static void method2204(DataBuffer var0) { - try { - if (null != Unsorted.aClass30_1039) { - try { - Unsorted.aClass30_1039.method984(-117, 0L); - Unsorted.aClass30_1039.method983(var0.buffer, var0.index, -903171152, 24); - } catch (Exception var3) { - } - } - - var0.index += 24; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "wc.E(" + "null" + ',' + 120 + ')'); - } - } - - static void method2206(int var1) { - try { - InterfaceWidget var2 = InterfaceWidget.getWidget(4, var1); - var2.a(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wc.A(" + true + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class163.java b/Client/src/main/java/org/runite/client/Class163.java deleted file mode 100644 index 3d9a32fe3..000000000 --- a/Client/src/main/java/org/runite/client/Class163.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.runite.client; - -import org.rs09.SystemLogger; -import org.rs09.client.data.NodeCache; - -import java.util.Objects; - -public class Class163 { - - static NodeCache aClass47_2041 = new NodeCache(64); - static int[] anIntArray2043 = new int[]{8, 11, 4, 6, 9, 7, 10, 0}; - public static int localNPCCount = 0; - - -} diff --git a/Client/src/main/java/org/runite/client/Class163_Sub1.java b/Client/src/main/java/org/runite/client/Class163_Sub1.java deleted file mode 100644 index 638565354..000000000 --- a/Client/src/main/java/org/runite/client/Class163_Sub1.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.runite.client; -import org.rs09.client.data.ReferenceCache; - -import java.io.IOException; -import java.util.Objects; - -public class Class163_Sub1 extends Class163 { - - public static ReferenceCache aReferenceCache_2984 = new ReferenceCache(2); - static long[] aLongArray2986 = new long[32]; - static byte[][] aByteArrayArray2987; - static int anInt2989 = 0; - static LinkedList aLinkedList_2990 = new LinkedList(); - static int anInt2993 = 0; - - - static void ping(boolean var1) { - try { - Class58.method1194(); - if(30 == Class143.gameStage || Class143.gameStage == 25) { - ++TextureOperation18.anInt4032; - if(TextureOperation18.anInt4032 >= 50 || var1) { - TextureOperation18.anInt4032 = 0; - if(!Class3_Sub28_Sub18.aBoolean3769 && Class3_Sub15.activeConnection != null) { - TextureOperation12.outgoingBuffer.putOpcode(93); - try { - Class3_Sub15.activeConnection.sendBytes(TextureOperation12.outgoingBuffer.buffer, TextureOperation12.outgoingBuffer.index); - TextureOperation12.outgoingBuffer.index = 0; - } catch (IOException var3) { - Class3_Sub28_Sub18.aBoolean3769 = true; - } - } - - Class58.method1194(); - } - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ah.B(" + (byte) -90 + ',' + var1 + ')'); - } - } - - static void method2211() { - try { - if(null == Class67.aClass11_1017) { - if(null == Class56.aClass11_886) { - int var1 = Unsorted.anInt3644; - int var3; - int var4; - if(Class38_Sub1.aBoolean2615) { - int var11; - if(var1 != 1) { - var3 = Unsorted.anInt1709; - var11 = Class126.anInt1676; - if(Class21.anInt1462 - 10 > var11 || Class21.anInt3552 + (Class21.anInt1462 - -10) < var11 || var3 < -10 + Class21.anInt3395 || Class21.anInt3537 + (Class21.anInt3395 - -10) < var3) { - Class38_Sub1.aBoolean2615 = false; - Class21.method1340(Class21.anInt1462, Class21.anInt3552, Class21.anInt3395, Class21.anInt3537); - } - } - - if(var1 == 1) { - var11 = Class21.anInt1462; - var3 = Class21.anInt3395; - var4 = Class21.anInt3552; - int var12 = anInt2993; - int var13 = Class38_Sub1.anInt2614; - int var7 = -1; - - for(int var8 = 0; var8 < Unsorted.menuOptionCount; ++var8) { - int var9; - if(Unsorted.aBoolean1951) { - var9 = 15 * (Unsorted.menuOptionCount + -1 + -var8) + 35 + var3; - } else { - var9 = 15 * (-var8 + (Unsorted.menuOptionCount - 1)) + var3 + 31; - } - - if(var11 < var12 && var12 < var11 - -var4 && var9 + -13 < var13 && var13 < 3 + var9) { - var7 = var8; - } - } - - if(var7 != -1) { - BufferedDataStream.method806(var7); - } - - Class38_Sub1.aBoolean2615 = false; - Class21.method1340(Class21.anInt1462, Class21.anInt3552, Class21.anInt3395, Class21.anInt3537); - } - } else { - if(var1 == 1 && 0 < Unsorted.menuOptionCount) { - short var2 = TextureOperation27.aShortArray3095[-1 + Unsorted.menuOptionCount]; - if(var2 == 25 || var2 == 23 || 48 == var2 || var2 == 7 || 13 == var2 || var2 == 47 || var2 == 5 || var2 == 43 || var2 == 35 || var2 == 58 || var2 == 22 || var2 == 1006) { - var3 = Class117.anIntArray1613[-1 + Unsorted.menuOptionCount]; - var4 = Class27.anIntArray512[Unsorted.menuOptionCount + -1]; - RSInterface var5 = Unsorted.getRSInterface(var4); - Class3_Sub1 var6 = Client.method44(Objects.requireNonNull(var5)); - if(var6.method100() || var6.method93()) { - Class40.anInt677 = 0; - Class72.aBoolean1074 = false; - if(Class67.aClass11_1017 != null) { - Class20.method909(Class67.aClass11_1017); - } - - Class67.aClass11_1017 = Unsorted.getRSInterface(var4); - Class129_Sub1.anInt2693 = anInt2993; - Unsorted.anInt40 = Class38_Sub1.anInt2614; - PacketParser.anInt86 = var3; - Class20.method909(Class67.aClass11_1017); - return; - } - } - } - - if(var1 == 1 && (Unsorted.anInt998 == 1 && 2 < Unsorted.menuOptionCount || TextureOperation8.method353(Unsorted.menuOptionCount + -1, 0))) { - var1 = 2; - } - - if(var1 == 2 && Unsorted.menuOptionCount > 0 || Unsorted.anInt3660 == 1) { - Class132.method1801(); - } - - if(1 == var1 && Unsorted.menuOptionCount > 0 || Unsorted.anInt3660 == 2) { - TextureOperation9.method203(100); - } - } - - } - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "ah.A(" + -48 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class163_Sub1_Sub1.java b/Client/src/main/java/org/runite/client/Class163_Sub1_Sub1.java deleted file mode 100644 index 7472defea..000000000 --- a/Client/src/main/java/org/runite/client/Class163_Sub1_Sub1.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.runite.client; - -import org.rs09.client.net.Connection; - -import java.awt.Component; -import java.io.IOException; -import java.net.Socket; -import java.util.Objects; - -public final class Class163_Sub1_Sub1 extends Class163_Sub1 { - - static byte[] aByteArray4005 = new byte[]{(byte) 95, (byte) 97, (byte) 98, (byte) 99, (byte) 100, (byte) 101, (byte) 102, (byte) 103, (byte) 104, (byte) 105, (byte) 106, (byte) 107, (byte) 108, (byte) 109, (byte) 110, (byte) 111, (byte) 112, (byte) 113, (byte) 114, (byte) 115, (byte) 116, (byte) 117, (byte) 118, (byte) 119, (byte) 120, (byte) 121, (byte) 122, (byte) 48, (byte) 49, (byte) 50, (byte) 51, (byte) 52, (byte) 53, (byte) 54, (byte) 55, (byte) 56, (byte) 57}; - static int anInt4006; - public static boolean[] aBooleanArray4008 = new boolean[100]; - static int[] anIntArray4009 = new int[5]; - static int[][] anIntArrayArray4010 = new int[104][104]; - static int anInt2246 = 0; - public static int adminLoginStage = 0; - static int anInt1616 = 0; - - static void method2214() { - try { - - if (NPC.method1986(90) || WorldListCountry.localPlane == Class140_Sub3.anInt2745) { - if (Class58.anInt909 != WorldListCountry.localPlane && Class3_Sub19.method385(WorldListCountry.localPlane)) { - Class58.anInt909 = WorldListCountry.localPlane; - Unsorted.method792(); - } - - } else { - Unsorted.method1301(WorldListCountry.localPlane, Class3_Sub7.anInt2294, Unsorted.anInt3606, Class102.player.anIntArray2755[0], false, Class102.player.anIntArray2767[0]); - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ch.E(" + 0 + ')'); - } - } - - static void method2215(Component var0) { - try { - var0.removeKeyListener(TextureOperation33.aClass148_3049); - var0.removeFocusListener(TextureOperation33.aClass148_3049); - KeyboardListener.anInt2384 = -1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ch.G(" + (var0 != null ? "{...}" : "null") + ',' + -9320 + ')'); - } - } - - static void method2216() { - try { - - if (adminLoginStage != 0) { - try { - if (1500 < ++anInt2246) { - if (null != Class3_Sub15.activeConnection) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - if (1 <= anInt1616) { - Client.messageToDisplay = -5; - adminLoginStage = 0; - return; - } - - anInt2246 = 0; - ++anInt1616; - adminLoginStage = 1; - if (Class123.anInt1658 == Client.anInt3773) { - Class123.anInt1658 = Class53.anInt867; - } else { - Class123.anInt1658 = Client.anInt3773; - } - } - - if (adminLoginStage == 1) { - Class3_Sub9.aClass64_2318 = Class38.gameSignlink.method1441((byte) 8, ClientErrorException.worldListHost, Class123.anInt1658); - adminLoginStage = 2; - } - - int var1; - if (adminLoginStage == 2) { - if (Objects.requireNonNull(Class3_Sub9.aClass64_2318).anInt978 == 2) { - throw new IOException(); - } - - if (1 != Class3_Sub9.aClass64_2318.anInt978) { - return; - } - - Class3_Sub15.activeConnection = new Connection((Socket) Class3_Sub9.aClass64_2318.anObject974, Class38.gameSignlink); - Class3_Sub9.aClass64_2318 = null; - Class3_Sub15.activeConnection.sendBytes(TextureOperation12.outgoingBuffer.buffer, TextureOperation12.outgoingBuffer.index); - if (WorldListEntry.aAudioChannel_2627 != null) { - WorldListEntry.aAudioChannel_2627.method2159(); - } - if (null != Class3_Sub21.aAudioChannel_2491) { - Class3_Sub21.aAudioChannel_2491.method2159(); - } - - var1 = Class3_Sub15.activeConnection.readByte(); - if (WorldListEntry.aAudioChannel_2627 != null) { - WorldListEntry.aAudioChannel_2627.method2159(); - } - - if (Class3_Sub21.aAudioChannel_2491 != null) { - Class3_Sub21.aAudioChannel_2491.method2159(); - } - - if (var1 != 101) { - Client.messageToDisplay = var1; - adminLoginStage = 0; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - - adminLoginStage = 3; - } - - if (adminLoginStage == 3) { - if (Class3_Sub15.activeConnection.availableBytes() < 2) { - return; - } - - var1 = Class3_Sub15.activeConnection.readByte() << 8 | Class3_Sub15.activeConnection.readByte(); - WaterfallShader.method1627(var1, (byte) -16); - if (CS2Script.userCurrentWorldID == -1) { - adminLoginStage = 0; - Client.messageToDisplay = 6; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - - adminLoginStage = 0; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - Class24.method951(); - return; - } - } catch (IOException var2) { - if (null != Class3_Sub15.activeConnection) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - if (anInt1616 < 1) { - if (Class123.anInt1658 == Client.anInt3773) { - Class123.anInt1658 = Class53.anInt867; - } else { - Class123.anInt1658 = Client.anInt3773; - } - - adminLoginStage = 1; - anInt2246 = 0; - ++anInt1616; - } else { - Client.messageToDisplay = -4; - adminLoginStage = 0; - } - } - - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ch.D(" + (byte) 81 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class163_Sub2.java b/Client/src/main/java/org/runite/client/Class163_Sub2.java deleted file mode 100644 index f4ef51665..000000000 --- a/Client/src/main/java/org/runite/client/Class163_Sub2.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.runite.client; - -class Class163_Sub2 extends Class163 { - - static RSString paramSettings = null; - static Class30[] aClass30Array2998 = new Class30[29]; //TODO - - - static Class25 method2217(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if (var3 != null) { - for (int var4 = 0; var4 < var3.anInt2223; ++var4) { - Class25 var5 = var3.aClass25Array2221[var4]; - if ((var5.aLong498 >> 29 & 3L) == 2L && var5.anInt483 == var1 && var5.anInt478 == var2) { - Class158.method2186(var5); - return var5; - } - } - - } - return null; - } - -} diff --git a/Client/src/main/java/org/runite/client/Class163_Sub2_Sub1.java b/Client/src/main/java/org/runite/client/Class163_Sub2_Sub1.java deleted file mode 100644 index 233e3520e..000000000 --- a/Client/src/main/java/org/runite/client/Class163_Sub2_Sub1.java +++ /dev/null @@ -1,274 +0,0 @@ -package org.runite.client; -import org.rs09.client.net.Connection; -import org.rs09.client.data.ReferenceCache; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InvalidClassException; -import java.io.ObjectInputStream; -import java.io.OptionalDataException; -import java.io.StreamCorruptedException; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -final class Class163_Sub2_Sub1 extends Class163_Sub2 { - - static Connection aClass89_4012; - static int anInt4014; - static ReferenceCache aReferenceCache_4015 = new ReferenceCache(64); - static RSString[] aClass94Array4016 = new RSString[500]; - static long[] aLongArray4017 = new long[100]; - static boolean paramObjectTagEnabled = false; - static int anInt4019 = 0; - static int anInt4020 = 0; - static int anInt4021; - static int[] anIntArray4025 = new int[32]; - static int anInt4026 = 0; - static LDIndexedSprite[] aClass109_Sub1Array4027; - - - static void method2220() { - try { - Class140_Sub4.aReferenceCache_2792.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "t.H(" + 0 + ')'); - } - } - - static void method2221(int var0, int var1, int var2, int var4, int var5, int var6) { - try { - int var8 = 0; - - for(Class96[] var7 = ClientErrorException.aClass96Array2114; var8 < var7.length; ++var8) { - Class96 var9 = var7[var8]; - if(null != var9 && var9.anInt1360 == 2) { - Unsorted.method1724(var0 >> 1, var5, (-Texture.anInt1152 + var9.anInt1347 << 7) - -var9.anInt1350, var9.anInt1353 * 2, var2 >> 1, var9.anInt1346 + (var9.anInt1356 + -Class131.anInt1716 << 7), (byte)-114, var4); - if(-1 < Class32.anInt590 && Class44.anInt719 % 20 < 10) { - Class166.aAbstractSpriteArray2072[var9.anInt1351].drawAt(-12 + var1 + Class32.anInt590, -28 + var6 - -Texture.anInt2208); - } - } - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "t.E(" + var0 + ',' + var1 + ',' + var2 + ',' + true + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - static void method2223(boolean var0, byte var1) { - try { - byte var3; - byte[][] var4; - if(HDToolKit.highDetail && var0) { - var4 = Class40.aByteArrayArray3057; - var3 = 1; - } else { - var3 = 4; - var4 = Class3_Sub22.aByteArrayArray2521; - } - - for(int var5 = 0; var5 < var3; ++var5) { - Class58.method1194(); - - for(int var6 = 0; var6 < 13; ++var6) { - for(int var7 = 0; var7 < 13; ++var7) { - int var8 = ObjectDefinition.anIntArrayArrayArray1497[var5][var6][var7]; - if(var8 != -1) { - int var9 = var8 >> 24 & 3; - if(!var0 || var9 == 0) { - int var10 = (6 & var8) >> 1; - int var11 = var8 >> 14 & 1023; - int var12 = 2047 & var8 >> 3; - int var13 = var12 / 8 + (var11 / 8 << 8); - - for(int var14 = 0; Class3_Sub24_Sub3.anIntArray3494.length > var14; ++var14) { - if(var13 == Class3_Sub24_Sub3.anIntArray3494[var14] && var4[var14] != null) { - TextureOperation13.parseObjectMapping(AtmosphereParser.aClass91Array1182, var5, var4[var14], var9, var10, 8 * var6, var7 * 8, var0, (var11 & 7) * 8, 8 * (7 & var12)); - break; - } - } - } - } - } - } - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "t.J(" + var0 + ',' + var1 + ')'); - } - } - - static void method2224(long var1, int var3, int var4) { - try { - int var5 = (int)var1 >> 14 & 31; - - int var6 = (int)var1 >> 20 & 3; - int var7 = (int)(var1 >>> 32) & Integer.MAX_VALUE; - if (var5 == 10 || var5 == 11 || var5 == 22) { - ObjectDefinition var8 = ObjectDefinition.getObjectDefinition(var7); - int var9; - int var10; - if(var6 == 0 || var6 == 2) { - var10 = var8.SizeY; - var9 = var8.SizeX; - } else { - var10 = var8.SizeX; - var9 = var8.SizeY; - } - - int var11 = var8.WalkingFlag; - if(var6 != 0) { - var11 = (var11 << var6 & 15) - -(var11 >> -var6 + 4); - } - - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, var10, true, var11, 2, var4, var9, 0, 2, var3, Class102.player.anIntArray2767[0]); - } else { - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], var6, 0, true, 0, 2, var4, 0, 1 + var5, 2, var3, Class102.player.anIntArray2767[0]); - } - - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Unsorted.anInt2958 = 0; - Class36.anInt638 = 2; - Class70.anInt1053 = Class163_Sub1.anInt2993; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "t.D(" + (byte) 39 + ',' + var1 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method2226(BufferedDataStream var0, int var1, int var2) { - try { - if(var2 > -109) { - method2226(null, 67, 54); - } - - while(true) { - Class3_Sub8 var3 = (Class3_Sub8)Class3_Sub26.aLinkedList_2557.method1222(); - if(var3 == null) { - return; - } - - boolean var4 = false; - - int var5; - for(var5 = 0; var3.anInt2296 > var5; ++var5) { - if(var3.aClass64Array2303[var5] != null) { - if(var3.aClass64Array2303[var5].anInt978 == 2) { - var3.anIntArray2300[var5] = -5; - } - - if(var3.aClass64Array2303[var5].anInt978 == 0) { - var4 = true; - } - } - - if(null != var3.aClass64Array2298[var5]) { - if(var3.aClass64Array2298[var5].anInt978 == 2) { - var3.anIntArray2300[var5] = -6; - } - - if(var3.aClass64Array2298[var5].anInt978 == 0) { - var4 = true; - } - } - } - - if(var4) { - return; - } - - var0.putOpcode(var1); - var0.writeByte(0); - var5 = var0.index; - var0.writeInt(var3.anInt2305); - - for(int var6 = 0; var6 < var3.anInt2296; ++var6) { - if(var3.anIntArray2300[var6] == 0) { - try { - int var7 = var3.anIntArray2301[var6]; - Field var8; - int var9; - if(var7 == 0) { - var8 = (Field)var3.aClass64Array2303[var6].anObject974; - var9 = var8.getInt(null); - var0.writeByte(0); - var0.writeInt(var9); - } else if (var7 == 1) { - var8 = (Field) var3.aClass64Array2303[var6].anObject974; - var8.setInt(null, var3.anIntArray2299[var6]); - var0.writeByte(0); - } else if (2 == var7) { - var8 = (Field) var3.aClass64Array2303[var6].anObject974; - var9 = var8.getModifiers(); - var0.writeByte(0); - var0.writeInt(var9); - } - - Method var26; - if(3 == var7) { - var26 = (Method)var3.aClass64Array2298[var6].anObject974; - byte[][] var27 = var3.aByteArrayArrayArray2302[var6]; - Object[] var10 = new Object[var27.length]; - - for(int var11 = 0; var11 < var27.length; ++var11) { - ObjectInputStream var12 = new ObjectInputStream(new ByteArrayInputStream(var27[var11])); - var10[var11] = var12.readObject(); - } - - Object var28 = var26.invoke(null, var10); - if(var28 == null) { - var0.writeByte(0); - } else if(var28 instanceof Number) { - var0.writeByte(1); - var0.writeLong(((Number)var28).longValue()); - } else if (var28 instanceof RSString) { - var0.writeByte(2); - var0.writeString((RSString) var28); - } else { - var0.writeByte(4); - } - } else if(var7 == 4) { - var26 = (Method)var3.aClass64Array2298[var6].anObject974; - var9 = var26.getModifiers(); - var0.writeByte(0); - var0.writeInt(var9); - } - } catch (ClassNotFoundException var13) { - var0.writeByte(-10); - } catch (InvalidClassException var14) { - var0.writeByte(-11); - } catch (StreamCorruptedException var15) { - var0.writeByte(-12); - } catch (OptionalDataException var16) { - var0.writeByte(-13); - } catch (IllegalAccessException var17) { - var0.writeByte(-14); - } catch (IllegalArgumentException var18) { - var0.writeByte(-15); - } catch (InvocationTargetException var19) { - var0.writeByte(-16); - } catch (SecurityException var20) { - var0.writeByte(-17); - } catch (IOException var21) { - var0.writeByte(-18); - } catch (NullPointerException var22) { - var0.writeByte(-19); - } catch (Exception var23) { - var0.writeByte(-20); - } catch (Throwable var24) { - var0.writeByte(-21); - } - } else { - var0.writeByte(var3.anIntArray2300[var6]); - } - } - - var0.writeCRC(var5); - var0.method769(var0.index - var5); - var3.unlink(); - } - } catch (RuntimeException var25) { - throw ClientErrorException.clientError(var25, "t.I(" + (var0 != null?"{...}":"null") + ',' + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class163_Sub3.java b/Client/src/main/java/org/runite/client/Class163_Sub3.java deleted file mode 100644 index ab2062df1..000000000 --- a/Client/src/main/java/org/runite/client/Class163_Sub3.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class Class163_Sub3 extends Class163 { - - static int[] anIntArray2999; - static RSString[] aClass94Array3003 = new RSString[100]; - static boolean aBoolean3004 = true; - static byte[][] aByteArrayArray3005; - static int[] anIntArray3007 = new int[]{-1, -1, 1, 1}; - - - static void method2229(long var0) { - try { - if (var0 != 0) { - if ((100 > Class8.anInt104 || TextureOperation3.disableGEBoxes) && Class8.anInt104 < 200) { - RSString var3 = Objects.requireNonNull(Unsorted.method1052(var0)).longToRSString(); - - int var4; - for (var4 = 0; Class8.anInt104 > var4; ++var4) { - if (var0 == Class50.aLongArray826[var4]) { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, RSString.stringCombiner(new RSString[]{var3, TextCore.HasFriendsAlready}), -1); - return; - } - } - - for (var4 = 0; var4 < Class3_Sub28_Sub5.anInt3591; ++var4) { - if (Class114.ignores[var4] == var0) { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, RSString.stringCombiner(new RSString[]{TextCore.HasPleaseRemove, var3, TextCore.HasIgnoreToFriends}), -1); - return; - } - } - - if (var3.equalsString(Class102.player.displayName)) { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, TextCore.HasOnOwnFriendsList, -1); - } else { - Class70.aClass94Array1046[Class8.anInt104] = var3; - Class50.aLongArray826[Class8.anInt104] = var0; - Unsorted.anIntArray882[Class8.anInt104] = 0; - Unsorted.aClass94Array2566[Class8.anInt104] = RSString.parse(""); - Class57.anIntArray904[Class8.anInt104] = 0; - Unsorted.aBooleanArray73[Class8.anInt104] = false; - ++Class8.anInt104; - Class110.anInt1472 = PacketParser.anInt3213; - TextureOperation12.outgoingBuffer.putOpcode(120); - TextureOperation12.outgoingBuffer.writeLong(var0); - } - } else { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, TextCore.HasFriendsListFull, -1); - } - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "fb.C(" + var0 + ',' + (byte) -91 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class164.java b/Client/src/main/java/org/runite/client/Class164.java deleted file mode 100644 index edb6926f5..000000000 --- a/Client/src/main/java/org/runite/client/Class164.java +++ /dev/null @@ -1,190 +0,0 @@ -package org.runite.client; - -import java.util.Random; - -abstract class Class164 { - - static int[] anIntArray2048; - static int anInt2050; - static int anInt2051; - static RSInterface aClass11_2055; - static Model_Sub1[] aClass140_Sub5Array2058 = new Model_Sub1[4]; - private final short[] aShortArray2047 = new short[512]; - private final int anInt2054; - private final int anInt2056; - private final int anInt2057; - private final int anInt2060; - int anInt2062; - private short[] aShortArray2049; - - - Class164(int var1, int var3, int var4) { - try { - this.anInt2056 = var4; - this.anInt2062 = 5; - this.anInt2054 = var1; - this.anInt2060 = 2; - this.anInt2057 = var3; - this.method2232(); - this.method2236(-190126388); - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "wf.(" + var1 + ',' + 5 + ',' + var3 + ',' + var4 + ',' + 2 + ')'); - } - } - - final void method2230(int var1) { - try { - int[] var39 = new int[64]; - int[] var40 = new int[64]; - int[] var41 = new int[64]; - - int var42; - for (var42 = 0; var42 < 64; ++var42) { - var39[var42] = (var42 << 12) / 64; - } - - if (var1 > -68) { - this.method2236(64); - } - - for (var42 = 0; var42 < 64; ++var42) { - var40[var42] = (var42 << 12) / 64; - } - - for (var42 = 0; var42 < 64; ++var42) { - var41[var42] = (var42 << 12) / 64; - } - - this.method2233(); - - for (int var37 = 0; var37 < 64; ++var37) { - for (int var36 = 0; var36 < 64; ++var36) { - for (int var35 = 0; var35 < 64; ++var35) { - for (int var38 = 0; var38 < this.anInt2062; ++var38) { - var42 = this.aShortArray2049[var38] << 12; - int var8 = this.anInt2057 * var42 >> 12; - int var7 = var42 * var41[var37] >> 12; - int var9 = var42 * this.anInt2056 >> 12; - var7 *= this.anInt2060; - int var10 = var42 * this.anInt2060 >> 12; - int var5 = var39[var35] * var42 >> 12; - var5 *= this.anInt2057; - int var11 = var5 >> 12; - var5 &= 4095; - int var15 = var7 >> 12; - int var20 = var5 + -4096; - int var12 = var11 + 1; - int var16 = var15 - -1; - int var6 = var40[var36] * var42 >> 12; - int var17 = Class1.anIntArray52[var5]; - var6 *= this.anInt2056; - var15 &= 255; - var11 &= 255; - if (var10 <= var16) { - var16 = 0; - } else { - var16 &= 255; - } - - int var13 = var6 >> 12; - short var23 = this.aShortArray2047[var15]; - short var24 = this.aShortArray2047[var16]; - int var14 = var13 + 1; - if (var9 > var14) { - var14 &= 255; - } else { - var14 = 0; - } - - var6 &= 4095; - short var28 = this.aShortArray2047[var14 - -var24]; - var7 &= 4095; - var13 &= 255; - int var19 = Class1.anIntArray52[var7]; - short var27 = this.aShortArray2047[var13 + var24]; - short var25 = this.aShortArray2047[var23 + var13]; - if (var8 <= var12) { - var12 = 0; - } else { - var12 &= 255; - } - - int var21 = -4096 + var6; - int var18 = Class1.anIntArray52[var6]; - int var22 = var7 - 4096; - short var26 = this.aShortArray2047[var23 + var14]; - int var29 = Class131.method1788(var5, var7, var6, this.aShortArray2047[var25 + var11], true); - int var30 = Class131.method1788(var20, var7, var6, this.aShortArray2047[var12 - -var25], true); - int var31 = var29 - -(var17 * (var30 - var29) >> 12); - var29 = Class131.method1788(var5, var7, var21, this.aShortArray2047[var26 + var11], true); - var30 = Class131.method1788(var20, var7, var21, this.aShortArray2047[var12 + var26], true); - int var32 = var29 + (var17 * (-var29 + var30) >> 12); - int var33 = ((-var31 + var32) * var18 >> 12) + var31; - var29 = Class131.method1788(var5, var22, var6, this.aShortArray2047[var11 + var27], true); - var30 = Class131.method1788(var20, var22, var6, this.aShortArray2047[var12 + var27], true); - var31 = (var17 * (var30 + -var29) >> 12) + var29; - var29 = Class131.method1788(var5, var22, var21, this.aShortArray2047[var11 - -var28], true); - var30 = Class131.method1788(var20, var22, var21, this.aShortArray2047[var12 + var28], true); - var32 = var29 + ((-var29 + var30) * var17 >> 12); - int var34 = var31 - -((var32 + -var31) * var18 >> 12); - this.method2237(((-var33 + var34) * var19 >> 12) + var33, var38); - } - - this.method2231((byte) -92); - } - } - } - - } catch (RuntimeException var43) { - throw ClientErrorException.clientError(var43, "wf.M(" + var1 + ',' + 64 + ',' + 64 + ',' + 64 + ')'); - } - } - - abstract void method2231(byte var1); - - private void method2232() { - try { - this.aShortArray2049 = new short[this.anInt2062]; - int var2 = 0; - - while (var2 < this.anInt2062) { - this.aShortArray2049[var2] = (short) ((int) Math.pow(2.0D, var2)); - ++var2; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wf.L(" + (byte) -60 + ')'); - } - } - - abstract void method2233(); - - private void method2236(int var1) { - try { - Random var2 = new Random(this.anInt2054); - - int var3; - for (var3 = 0; var3 < 255; ++var3) { - this.aShortArray2047[var3] = (short) var3; - } - - if (var1 != -190126388) { - anInt2051 = -58; - } - - for (var3 = 0; var3 < 255; ++var3) { - int var4 = -var3 + 255; - int var5 = TextureOperation.method1603((byte) -120, var4, var2); - short var6 = this.aShortArray2047[var5]; - this.aShortArray2047[var5] = this.aShortArray2047[var4]; - this.aShortArray2047[var4] = this.aShortArray2047[256 + var4] = var6; - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "wf.N(" + var1 + ')'); - } - } - - abstract void method2237(int var1, int var2); - -} diff --git a/Client/src/main/java/org/runite/client/Class164_Sub1.java b/Client/src/main/java/org/runite/client/Class164_Sub1.java deleted file mode 100644 index 9f6d410da..000000000 --- a/Client/src/main/java/org/runite/client/Class164_Sub1.java +++ /dev/null @@ -1,274 +0,0 @@ -package org.runite.client; - -class Class164_Sub1 extends Class164 { - - private int anInt3010; - static int anInt3012 = 0; - private final int[] anIntArray3014; - private byte[] aByteArray3015; - private int anInt3016; - - - final void method2237(int var1, int var2) { - try { - this.anInt3010 += var1 * this.anIntArray3014[var2] >> 12; - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "vd.H(" + var1 + ',' + var2 + ',' + -20975 + ')'); - } - } - - static void method2238(int var0, int var1, int var2, int var3, int var5) { - try { - Class75.anInt1105 = var2; - Class157.anInt1996 = var1; - Class163_Sub2_Sub1.anInt4014 = var5; - MouseListeningClass.anInt1923 = var3; - GraphicDefinition.anInt529 = var0; - - if(Class163_Sub2_Sub1.anInt4014 >= 100) { - int var6 = 64 + 128 * MouseListeningClass.anInt1923; - int var7 = 64 + Class157.anInt1996 * 128; - int var8 = Class121.method1736(WorldListCountry.localPlane, 1, var6, var7) - GraphicDefinition.anInt529; - int var10 = var8 + -Class7.anInt2162; - int var9 = var6 + -NPC.anInt3995; - int var11 = -Class77.anInt1111 + var7; - int var12 = (int)Math.sqrt(var11 * var11 + var9 * var9); - Class139.anInt1823 = 2047 & (int)(Math.atan2(var10, var12) * 325.949D); - TextureOperation28.anInt3315 = 2047 & (int)(Math.atan2(var9, var11) * -325.949D); - if(128 > Class139.anInt1823) { - Class139.anInt1823 = 128; - } - - if(383 < Class139.anInt1823) { - Class139.anInt1823 = 383; - } - } - - Class133.anInt1753 = 2; - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "vd.F(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + (byte) -21 + ',' + var5 + ')'); - } - } - - static boolean method2239(int var0, int var1, int var2, int var3) { - if(Class8.method846(var0, var1, var2)) { - int var4 = var1 << 7; - int var5 = var2 << 7; - int var6 = Class44.anIntArrayArrayArray723[var0][var1][var2] - 1; - int var7 = var6 - 120; - int var8 = var6 - 230; - int var9 = var6 - 238; - if(var3 < 16) { - if(var3 == 1) { - if(var4 > Class145.anInt2697) { - if(!TextureOperation10.method349(var4, var6, var5)) { - return true; - } - - if(!TextureOperation10.method349(var4, var6, var5 + 128)) { - return true; - } - } - - if(var0 > 0) { - if(!TextureOperation10.method349(var4, var7, var5)) { - return true; - } - - if(!TextureOperation10.method349(var4, var7, var5 + 128)) { - return true; - } - } - - if(!TextureOperation10.method349(var4, var8, var5)) { - return true; - } - - return !TextureOperation10.method349(var4, var8, var5 + 128); - } - - if(var3 == 2) { - if(var5 < TextureOperation13.anInt3363) { - if(!TextureOperation10.method349(var4, var6, var5 + 128)) { - return true; - } - - if(!TextureOperation10.method349(var4 + 128, var6, var5 + 128)) { - return true; - } - } - - if(var0 > 0) { - if(!TextureOperation10.method349(var4, var7, var5 + 128)) { - return true; - } - - if(!TextureOperation10.method349(var4 + 128, var7, var5 + 128)) { - return true; - } - } - - if(!TextureOperation10.method349(var4, var8, var5 + 128)) { - return true; - } - - return !TextureOperation10.method349(var4 + 128, var8, var5 + 128); - } - - if(var3 == 4) { - if(var4 < Class145.anInt2697) { - if(!TextureOperation10.method349(var4 + 128, var6, var5)) { - return true; - } - - if(!TextureOperation10.method349(var4 + 128, var6, var5 + 128)) { - return true; - } - } - - if(var0 > 0) { - if(!TextureOperation10.method349(var4 + 128, var7, var5)) { - return true; - } - - if(!TextureOperation10.method349(var4 + 128, var7, var5 + 128)) { - return true; - } - } - - if(!TextureOperation10.method349(var4 + 128, var8, var5)) { - return true; - } - - return !TextureOperation10.method349(var4 + 128, var8, var5 + 128); - } - - if(var3 == 8) { - if(var5 > TextureOperation13.anInt3363) { - if(!TextureOperation10.method349(var4, var6, var5)) { - return true; - } - - if(!TextureOperation10.method349(var4 + 128, var6, var5)) { - return true; - } - } - - if(var0 > 0) { - if(!TextureOperation10.method349(var4, var7, var5)) { - return true; - } - - if(!TextureOperation10.method349(var4 + 128, var7, var5)) { - return true; - } - } - - if(!TextureOperation10.method349(var4, var8, var5)) { - return true; - } - - return !TextureOperation10.method349(var4 + 128, var8, var5); - } - } - - return !TextureOperation10.method349(var4 + 64, var9, var5 + 64) || (var3 == 16 ? !TextureOperation10.method349(var4, var8, var5 + 128) : (var3 == 32 ? !TextureOperation10.method349(var4 + 128, var8, var5 + 128) : (var3 == 64 ? !TextureOperation10.method349(var4 + 128, var8, var5) : (var3 == 128 && !TextureOperation10.method349(var4, var8, var5))))); - } else { - return true; - } - } - - static void method2241(byte var0, boolean var1) { - try { - Class3_Sub9 var3; - for(var3 = (Class3_Sub9) Unsorted.aLinkedList_78.method1222(); var3 != null; var3 = (Class3_Sub9) Unsorted.aLinkedList_78.method1221()) { - if(null != var3.aClass3_Sub24_Sub1_2312) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var3.aClass3_Sub24_Sub1_2312); - var3.aClass3_Sub24_Sub1_2312 = null; - } - - if(var3.aClass3_Sub24_Sub1_2315 != null) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var3.aClass3_Sub24_Sub1_2315); - var3.aClass3_Sub24_Sub1_2315 = null; - } - - var3.unlink(); - } - - if(var1) { - for(var3 = (Class3_Sub9) Unsorted.aLinkedList_1242.method1222(); null != var3; var3 = (Class3_Sub9) Unsorted.aLinkedList_1242.method1221()) { - if(null != var3.aClass3_Sub24_Sub1_2312) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var3.aClass3_Sub24_Sub1_2312); - var3.aClass3_Sub24_Sub1_2312 = null; - } - - var3.unlink(); - } - - for(var3 = (Class3_Sub9) Unsorted.aHashTable_4046.first(); null != var3; var3 = (Class3_Sub9) Unsorted.aHashTable_4046.next()) { - if(null != var3.aClass3_Sub24_Sub1_2312) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var3.aClass3_Sub24_Sub1_2312); - var3.aClass3_Sub24_Sub1_2312 = null; - } - - var3.unlink(); - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "vd.G(" + var0 + ',' + var1 + ')'); - } - } - - final void method2233() { - try { - this.anInt3016 = 0; - this.anInt3010 = 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "vd.C(" + -949697716 + ')'); - } - } - - Class164_Sub1() { - super(12, 16, 2); - - try { - this.anIntArray3014 = new int[this.anInt2062]; - - for(int var7 = 0; var7 < this.anInt2062; ++var7) { - this.anIntArray3014[var7] = (short)((int)(Math.pow((float) 0.45, var7) * 4096.0D)); - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "vd.(" + 12 + ',' + 5 + ',' + 16 + ',' + 2 + ',' + 2 + ',' + (float) 0.45 + ')'); - } - } - - void method2242(int var1, byte var2) { - try { - this.aByteArray3015[this.anInt3016++] = (byte)(127 + (Unsorted.bitwiseAnd(var2, 255) >> 1)); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "vd.B(" + var1 + ',' + var2 + ')'); - } - } - - final void method2231(byte var1) { - try { - this.anInt3010 = Math.abs(this.anInt3010); - if(var1 != -92) { - this.method2231((byte)-112); - } - - if(this.anInt3010 >= 4096) { - this.anInt3010 = 4095; - } - - this.method2242(this.anInt3016++, (byte)(this.anInt3010 >> 4)); - this.anInt3010 = 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "vd.A(" + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class164_Sub1_Sub1.java b/Client/src/main/java/org/runite/client/Class164_Sub1_Sub1.java deleted file mode 100644 index b8541f9ef..000000000 --- a/Client/src/main/java/org/runite/client/Class164_Sub1_Sub1.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.runite.client; - -final class Class164_Sub1_Sub1 extends Class164_Sub1 { - - private byte[] aByteArray4028; - - - final void method2242(int var1, byte var2) { - int var3 = var1 * 2; - var2 = (byte)(127 + ((var2 & 0xFF) >> 1)); - this.aByteArray4028[var3++] = var2; - this.aByteArray4028[var3] = var2; - } - - public Class164_Sub1_Sub1() { - super(); - } - - final byte[] method2243() { - this.aByteArray4028 = new byte[64 * 64 * 64 * 2]; - this.method2230(-95); - return this.aByteArray4028; - } -} diff --git a/Client/src/main/java/org/runite/client/Class164_Sub2.java b/Client/src/main/java/org/runite/client/Class164_Sub2.java deleted file mode 100644 index 290706568..000000000 --- a/Client/src/main/java/org/runite/client/Class164_Sub2.java +++ /dev/null @@ -1,298 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -import java.util.Objects; - -public class Class164_Sub2 extends Class164 { - - private int anInt3018; - static Class33 aClass33_3019; - public static int anInt3020 = 0; - private final int anInt3021; - private final int anInt3022; - private int anInt3024; - private byte[] aByteArray3025; - private final int anInt3026; - static byte[][] aByteArrayArray3027; - private int anInt3028; - private int anInt3029; - - - void method2244(int var1, byte var2) { - try { - this.aByteArray3025[var1] = var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "we.G(" + var1 + ',' + var2 + ')'); - } - } - - final void method2231(byte var1) { - try { - this.anInt3018 = this.anInt3021; - this.anInt3029 >>= 4; - if(0 > this.anInt3029) { - this.anInt3029 = 0; - } else if(255 < this.anInt3029) { - this.anInt3029 = 255; - } - - this.method2244(this.anInt3028++, (byte)this.anInt3029); - if(var1 == -92) { - this.anInt3029 = 0; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "we.A(" + var1 + ')'); - } - } - - Class164_Sub2() { - super(8, 8, 8); - try { - this.anInt3022 = (int)(4096.0F * (float) 3.0); - this.anInt3026 = (int)((float) 0.55 * 4096.0F); - this.anInt3018 = this.anInt3021 = (int)(Math.pow(0.5D, -(float) 0.1) * 4096.0D); - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "we.(" + 8 + ',' + 5 + ',' + 8 + ',' + 8 + ',' + 2 + ',' + (float) 0.1 + ',' + (float) 0.55 + ',' + (float) 3.0 + ')'); - } - } - - static int method2246(int var1) { - try { - int var2 = var1 * (var1 * var1 >> 12) >> 12; - - int var3 = 6 * var1 - '\uf000'; - int var4 = (var1 * var3 >> 12) + '\ua000'; - return var2 * var4 >> 12; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "we.E(" + (byte) 83 + ',' + var1 + ')'); - } - } - - static int method2247(byte var0, int var1, RSInterface var2) { - try { - if(var2.childDataBuffers != null && var2.childDataBuffers.length > var1) { - try { - int[] var4 = var2.childDataBuffers[var1]; - byte var7 = 0; - int var5 = 0; - int var6 = 0; - - while(true) { - int var9 = 0; - int var8 = var4[var6++]; - byte var10 = 0; - if(var8 == 0) { - return var5; - } - - if(15 == var8) { - var10 = 1; - } - - if(16 == var8) { - var10 = 2; - } - - if(var8 == 1) { - var9 = TextureOperation17.anIntArray3185[var4[var6++]]; - } - - if(var8 == 17) { - var10 = 3; - } - - if(var8 == 2) { - var9 = Class3_Sub20.anIntArray2480[var4[var6++]]; - } - - if(var8 == 3) { - var9 = Class133.anIntArray1743[var4[var6++]]; - } - - int var11; - RSInterface var12; - int var13; - int var14; - if(var8 == 4) { - var11 = var4[var6++] << 16; - var11 += var4[var6++]; - var12 = Unsorted.getRSInterface(var11); - var13 = var4[var6++]; - if(-1 != var13 && (!ItemDefinition.getItemDefinition(var13).membersItem || Unsorted.isMember)) { - for(var14 = 0; var14 < Objects.requireNonNull(var12).itemAmounts.length; ++var14) { - if(1 + var13 == var12.itemAmounts[var14]) { - var9 += var12.itemIds[var14]; - } - } - } - } - - if(var8 == 5) { - var9 = ItemDefinition.ram[var4[var6++]]; - } - - if(6 == var8) { - System.out.println(ItemDefinition.anIntArray781[-1 + Class3_Sub20.anIntArray2480[var4[var6++]]]); - var9 = ItemDefinition.anIntArray781[-1 + Class3_Sub20.anIntArray2480[var4[var6++]]]; - } - - if(var8 == 7) { - var9 = 100 * ItemDefinition.ram[var4[var6++]] / '\ub71b'; - } - - if(var8 == 8) { - var9 = Class102.player.COMBAT_LEVEL; - } - - if(9 == var8) { - for(var11 = 0; var11 < 25; ++var11) { - if(Class3_Sub23.aBooleanArray2538[var11]) { - var9 += Class3_Sub20.anIntArray2480[var11]; - } - } - } - - if(var8 == 10) { - var11 = var4[var6++] << 16; - var11 += var4[var6++]; - var12 = Unsorted.getRSInterface(var11); - var13 = var4[var6++]; - if(var13 != -1 && (!ItemDefinition.getItemDefinition(var13).membersItem || Unsorted.isMember)) { - for(var14 = 0; var14 < Objects.requireNonNull(var12).itemAmounts.length; ++var14) { - if(1 + var13 == var12.itemAmounts[var14]) { - var9 = 999999999; - break; - } - } - } - } - - if(var8 == 11) { - var9 = Unsorted.anInt136; - } - - if(12 == var8) { - var9 = MouseListeningClass.anInt1925; - } - - if(var8 == 13) { - var11 = ItemDefinition.ram[var4[var6++]]; - int var17 = var4[var6++]; - var9 = (1 << var17 & var11) == 0 ?0:1; - } - - if(var8 == 14) { - var11 = var4[var6++]; - var9 = NPCDefinition.method1484(var11); - } - - if(var8 == 18) { - var9 = (Class102.player.xAxis >> 7) - -Class131.anInt1716; - } - - if(var8 == 19) { - var9 = (Class102.player.zAxis >> 7) - -Texture.anInt1152; - } - - if(var8 == 20) { - var9 = var4[var6++]; - } - - if(0 == var10) { - if(0 == var7) { - var5 += var9; - } - - if(var7 == 1) { - var5 -= var9; - } - - if(2 == var7 && var9 != 0) { - var5 /= var9; - } - - if(var7 == 3) { - var5 *= var9; - } - - var7 = 0; - } else { - var7 = var10; - } - } - } catch (Exception var15) { - return -1; - } - } else { - return -2; - } - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "we.D(" + var0 + ',' + var1 + ',' + "null" + ')'); - } - } - - static boolean method2248(int var1) { - try { - - return 32 <= var1 && var1 <= 126 || (var1 >= 160 && 255 >= var1 || (var1 == 128 || var1 == 140 || var1 == 151 || var1 == 156 || var1 == 159)); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "we.B(" + -157 + ',' + var1 + ')'); - } - } - - static void method2249(int var1) { - try { - Linkable var2 = Class124.aHashTable_1659.first(); - - for(; var2 != null; var2 = Class124.aHashTable_1659.next()) { - if((long) var1 == (65535L & var2.linkableKey >> 48)) { - var2.unlink(); - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "we.F(" + (byte) 83 + ',' + var1 + ')'); - } - } - - final void method2237(int var1, int var2) { - try { - int anInt3023; - if(0 == var2) { - this.anInt3024 = -(var1 >= 0 ?var1:-var1) + this.anInt3026; - this.anInt3024 = this.anInt3024 * this.anInt3024 >> 12; - this.anInt3029 = this.anInt3024; - } else { - anInt3023 = this.anInt3022 * this.anInt3024 >> 12; - if(anInt3023 >= 0) { - if(anInt3023 > 4096) { - anInt3023 = 4096; - } - } else { - anInt3023 = 0; - } - - this.anInt3024 = -(var1 >= 0 ?var1:-var1) + this.anInt3026; - this.anInt3024 = this.anInt3024 * this.anInt3024 >> 12; - this.anInt3024 = this.anInt3024 * anInt3023 >> 12; - this.anInt3029 += this.anInt3018 * this.anInt3024 >> 12; - this.anInt3018 = this.anInt3021 * this.anInt3018 >> 12; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "we.H(" + var1 + ',' + var2 + ',' + -20975 + ')'); - } - } - - final void method2233() { - try { - this.anInt3028 = 0; - this.anInt3029 = 0; - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "we.C(" + -949697716 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class164_Sub2_Sub1.java b/Client/src/main/java/org/runite/client/Class164_Sub2_Sub1.java deleted file mode 100644 index 132e752be..000000000 --- a/Client/src/main/java/org/runite/client/Class164_Sub2_Sub1.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.runite.client; - -final class Class164_Sub2_Sub1 extends Class164_Sub2 { - - private byte[] aByteArray4029; - - - public Class164_Sub2_Sub1() { - super(); - } - - final byte[] method2250() { - this.aByteArray4029 = new byte[64 * 64 * 64 * 2]; - this.method2230(-98); - return this.aByteArray4029; - } - - final void method2244(int var1, byte var2) { - int var3 = var1 * 2; - int var4 = var2 & 0xFF; - this.aByteArray4029[var3++] = (byte)(3 * var4 >> 5); - this.aByteArray4029[var3] = (byte)(var4 >> 2); - } -} diff --git a/Client/src/main/java/org/runite/client/Class166.java b/Client/src/main/java/org/runite/client/Class166.java deleted file mode 100644 index 99ccd4325..000000000 --- a/Client/src/main/java/org/runite/client/Class166.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.runite.client; - -final class Class166 { - - static Class3_Sub2[][][] aClass3_Sub2ArrayArrayArray2065; - static Class3_Sub28_Sub5[] aClass3_Sub28_Sub5Array2070 = new Class3_Sub28_Sub5[14]; - static AbstractSprite[] aAbstractSpriteArray2072; - static int[] anIntArray2073 = new int[5]; - static int anInt2079 = 0; - int anInt2063; - byte[] aByteArray2064; - int anInt2066; - int anInt2067; - int anInt2069; - int anInt2071; - byte[] aByteArray2076; - int anInt2077; - int anInt2078; - - static boolean method2256(int var0, int var1, int var2, int var3) { - if (Class8.method846(var0, var1, var2)) { - int var4 = var1 << 7; - int var5 = var2 << 7; - return !TextureOperation10.method349(var4 + 1, Class44.anIntArrayArrayArray723[var0][var1][var2] + var3, var5 + 1) || !TextureOperation10.method349(var4 + 128 - 1, Class44.anIntArrayArrayArray723[var0][var1 + 1][var2] + var3, var5 + 1) || !TextureOperation10.method349(var4 + 128 - 1, Class44.anIntArrayArrayArray723[var0][var1 + 1][var2 + 1] + var3, var5 + 128 - 1) || !TextureOperation10.method349(var4 + 1, Class44.anIntArrayArrayArray723[var0][var1][var2 + 1] + var3, var5 + 128 - 1); - } else { - return true; - } - } - - static void method2257() { - try { - - Class163_Sub2_Sub1.aReferenceCache_4015.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "wh.F(" + 96 + ')'); - } - } - - static void method2258(int var0, RSString var2) { - try { - RSString var3 = var2.method1579().longToRSString(); - boolean var4 = false; - - for (int var5 = 0; Class159.localPlayerCount > var5; ++var5) { - Player var6 = Unsorted.players[Class56.localPlayerIndexes[var5]]; - if (null != var6 && null != var6.displayName && var6.displayName.equalsStringIgnoreCase(var3)) { - var4 = true; - Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 1, false, 0, 2, var6.anIntArray2767[0], 1, 0, 2, var6.anIntArray2755[0], Class102.player.anIntArray2767[0]); - if (1 == var0) { - TextureOperation12.outgoingBuffer.putOpcode(68); - TextureOperation12.outgoingBuffer.writeShort128LE(Class56.localPlayerIndexes[var5]); - } else if (4 == var0) { - TextureOperation12.outgoingBuffer.putOpcode(180); - TextureOperation12.outgoingBuffer.writeShort128LE(Class56.localPlayerIndexes[var5]); - } else if (5 == var0) { - TextureOperation12.outgoingBuffer.putOpcode(4); - TextureOperation12.outgoingBuffer.writeShortLE(Class56.localPlayerIndexes[var5]); - } else if (var0 == 6) { - TextureOperation12.outgoingBuffer.putOpcode(133); - TextureOperation12.outgoingBuffer.writeShortLE(Class56.localPlayerIndexes[var5]); - } else if (var0 == 7) { - TextureOperation12.outgoingBuffer.putOpcode(114); - TextureOperation12.outgoingBuffer.writeShort128LE(Class56.localPlayerIndexes[var5]); - } - break; - } - } - - if (!var4) { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, RSString.stringCombiner(new RSString[]{TextCore.HasUnableFind, var3}), -1); - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "wh.D(" + var0 + ',' + 0 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - static AbstractIndexedSprite method2259() { - try { - Object var1; - if (HDToolKit.highDetail) { - var1 = new HDIndexedSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[0], Unsorted.anIntArray2591[0], Class140_Sub7.anIntArray2931[0], Unsorted.anIntArray3076[0], Class163_Sub1.aByteArrayArray2987[0], TextureOperation38.spritePalette); - } else { - var1 = new LDIndexedSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[0], Unsorted.anIntArray2591[0], Class140_Sub7.anIntArray2931[0], Unsorted.anIntArray3076[0], Class163_Sub1.aByteArrayArray2987[0], TextureOperation38.spritePalette); - } - - Class39.method1035((byte) 116); - return (AbstractIndexedSprite) var1; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "wh.A(" + (byte) -40 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class167.java b/Client/src/main/java/org/runite/client/Class167.java deleted file mode 100644 index 9b25f5c37..000000000 --- a/Client/src/main/java/org/runite/client/Class167.java +++ /dev/null @@ -1,208 +0,0 @@ -package org.runite.client; - -public final class Class167 { - - public static RSString aClass94_2083 = null; - static int anInt2087 = 0; - - - static void method2263(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { - Class113 var7 = new Class113(); - var7.anInt1553 = var1 / 128; - var7.anInt1547 = var2 / 128; - var7.anInt1563 = var3 / 128; - var7.anInt1566 = var4 / 128; - var7.anInt1554 = var0; - var7.anInt1562 = var1; - var7.anInt1545 = var2; - var7.anInt1560 = var3; - var7.anInt1550 = var4; - var7.anInt1544 = var5; - var7.anInt1548 = var6; - Class3_Sub28_Sub8.aClass113Array3610[Class3_Sub4.anInt2249++] = var7; - } - - static void method2264(boolean var0) { - if (var0) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638 = Class166.aClass3_Sub2ArrayArrayArray2065; - Class44.anIntArrayArrayArray723 = Unsorted.anIntArrayArrayArray3605; - Class3_Sub23.aClass3_Sub11ArrayArray2542 = TextureOperation32.aClass3_Sub11ArrayArray3346; - } else { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638 = Unsorted.aClass3_Sub2ArrayArrayArray4070; - Class44.anIntArrayArrayArray723 = Class58.anIntArrayArrayArray914; - Class3_Sub23.aClass3_Sub11ArrayArray2542 = Client.aClass3_Sub11ArrayArray2199; - } - - Class3_Sub17.anInt2456 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638.length; - } - - static void method2265() { - try { - CS2Script.aReferenceCache_2442.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "wj.B(" + 0 + ')'); - } - } - - static void method2267(int var0, int var1, boolean var2, DataBuffer var3, int var4, int var5, byte var6, int var7, int var8) { - try { - int var9; - if (var5 >= 0 && var5 < 104 && var4 >= 0 && var4 < 104) { - if (!var2) { - Unsorted.aByteArrayArrayArray113[var8][var5][var4] = 0; - } - - while (true) { - var9 = var3.readUnsignedByte(); - if (var9 == 0) { - if (var2) { - Class44.anIntArrayArrayArray723[0][var5][var4] = Class58.anIntArrayArrayArray914[0][var5][var4]; - } else if (var8 == 0) { - Class44.anIntArrayArrayArray723[0][var5][var4] = 8 * -Class32.method993(var4 + 556238 + var1, var0 + var5 + 932731); - } else { - Class44.anIntArrayArrayArray723[var8][var5][var4] = -240 + Class44.anIntArrayArrayArray723[var8 - 1][var5][var4]; - } - break; - } - - if (var9 == 1) { - int var10 = var3.readUnsignedByte(); - if (var2) { - Class44.anIntArrayArrayArray723[0][var5][var4] = Class58.anIntArrayArrayArray914[0][var5][var4] - -(var10 * 8); - } else { - if (var10 == 1) { - var10 = 0; - } - - if (var8 == 0) { - Class44.anIntArrayArrayArray723[0][var5][var4] = 8 * -var10; - } else { - Class44.anIntArrayArrayArray723[var8][var5][var4] = -(var10 * 8) + Class44.anIntArrayArrayArray723[-1 + var8][var5][var4]; - } - } - break; - } - - if (49 >= var9) { - Class158_Sub1.aByteArrayArrayArray1828[var8][var5][var4] = var3.readSignedByte(); - Unsorted.aByteArrayArrayArray1328[var8][var5][var4] = (byte) ((-2 + var9) / 4); - PacketParser.aByteArrayArrayArray81[var8][var5][var4] = (byte) Unsorted.bitwiseAnd(-2 + var9 + var7, 3); - } else if (var9 > 81) { - TextureOperation36.aByteArrayArrayArray3430[var8][var5][var4] = (byte) (-81 + var9); - } else if (!var2) { - Unsorted.aByteArrayArrayArray113[var8][var5][var4] = (byte) (var9 - 49); - } - } - } else { - while (true) { - var9 = var3.readUnsignedByte(); - if (var9 == 0) { - break; - } - - if (var9 == 1) { - var3.readUnsignedByte(); - break; - } - - if (var9 <= 49) { - var3.readUnsignedByte(); - } - } - } - - if (var6 < 58) { - anInt2087 = 87; - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "wj.A(" + var0 + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ')'); - } - } - - static int method2268(byte var0, int var1, int var2) { - try { - Class3_Sub25 var3 = (Class3_Sub25) Class3_Sub2.aHashTable_2220.get(var1); - if (var3 == null) { - return 0; - } else if (var2 == -1) { - return 0; - } else { - int var4 = 0; - - for (int var5 = 0; var3.anIntArray2551.length > var5; ++var5) { - if (var3.anIntArray2547[var5] == var2) { - var4 += var3.anIntArray2551[var5]; - } - } - return var4; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "wj.H(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - static void method2269(byte var0) { - try { - if (null != Class3_Sub15.activeConnection) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - TextureOperation13.method313((byte) 110); - Class32.method995(); - - int var1; - for (var1 = 0; var1 < 4; ++var1) { - AtmosphereParser.aClass91Array1182[var1].method1496(); - } - - Unsorted.method1250(62, false); - System.gc(); - Unsorted.method882(); - AudioHandler.musicEffectPlaying = false; - AudioHandler.currentTrack = -1; - Class164_Sub1.method2241((byte) -77, true); - LinkableRSString.isDynamicSceneGraph = false; - Texture.anInt1152 = 0; - Unsorted.anInt3606 = 0; - Class3_Sub7.anInt2294 = 0; - Class131.anInt1716 = 0; - - for (var1 = 0; ClientErrorException.aClass96Array2114.length > var1; ++var1) { - ClientErrorException.aClass96Array2114[var1] = null; - } - - Class159.localPlayerCount = 0; - Class163.localNPCCount = 0; - if (var0 != 46) { - method2269((byte) 43); - } - - for (var1 = 0; var1 < 2048; ++var1) { - Unsorted.players[var1] = null; - Class65.aClass3_Sub30Array986[var1] = null; - } - - for (var1 = 0; var1 < 32768; ++var1) { - NPC.npcs[var1] = null; - } - - for (var1 = 0; 4 > var1; ++var1) { - for (int var2 = 0; var2 < 104; ++var2) { - for (int var3 = 0; var3 < 104; ++var3) { - Class39.aLinkedListArrayArrayArray3273[var1][var2][var3] = null; - } - } - } - - Class3_Sub28_Sub5.method560(); - Class113.interfacePacketCounter = 0; - TextureOperation6.method176(var0 + -161); - TextureOperation1.method219(true); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "wj.C(" + var0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class169.java b/Client/src/main/java/org/runite/client/Class169.java deleted file mode 100644 index 2df447ffa..000000000 --- a/Client/src/main/java/org/runite/client/Class169.java +++ /dev/null @@ -1,192 +0,0 @@ -package org.runite.client; - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -final class Class169 { - - private static final byte[] aByteArray2111 = new byte[16384]; - private final int anInt2108; - boolean aBoolean2106 = true; - private Class156 aClass156_2105; - private ByteBuffer aByteBuffer2107; - private ByteBuffer aByteBuffer2109; - private Class156 aClass156_2110; - private int anInt2112 = -1; - - - public Class169() { - GL var1 = HDToolKit.gl; - int[] var2 = new int[1]; - var1.glGenTextures(1, var2, 0); - this.anInt2108 = var2[0]; - Class31.anInt580 += 16384; - HDToolKit.bindTexture2D(this.anInt2108); - var1.glTexParameteri(3553, 10241, 9729); - var1.glTexParameteri(3553, 10240, 9729); - var1.glTexParameteri(3553, 10242, '\u812f'); - var1.glTexParameteri(3553, 10243, '\u812f'); - } - - final void method2281(int[][] var1, int var2, int var3) { - DataBuffer var4 = new DataBuffer(1620); - - int var6; - for (int var5 = 0; var5 <= 8; ++var5) { - for (var6 = 0; var6 <= 8; ++var6) { - if (HDToolKit.aBoolean1790) { - var4.writeFloat((float) var6 / 8.0F); - var4.writeFloat((float) var5 / 8.0F); - var4.writeFloat((float) (var6 * 128)); - var4.writeFloat((float) var1[var6 + var2][var5 + var3]); - var4.writeFloat((float) (var5 * 128)); - } else { - var4.writeFloatLE((float) var6 / 8.0F); - var4.writeFloatLE((float) var5 / 8.0F); - var4.writeFloatLE((float) (var6 * 128)); - var4.writeFloatLE((float) var1[var6 + var2][var5 + var3]); - var4.writeFloatLE((float) (var5 * 128)); - } - } - } - - if (HDToolKit.supportVertexBufferObject) { - ByteBuffer var9 = ByteBuffer.wrap(var4.buffer, 0, var4.index); - this.aClass156_2110 = new Class156(); - this.aClass156_2110.method2172(var9); - } else { - this.aByteBuffer2109 = ByteBuffer.allocateDirect(var4.index).order(ByteOrder.nativeOrder()); - this.aByteBuffer2109.put(var4.buffer, 0, var4.index); - this.aByteBuffer2109.flip(); - } - - DataBuffer var8 = new DataBuffer(1536); - - for (var6 = 0; var6 < 8; ++var6) { - for (int var7 = 0; var7 < 8; ++var7) { - if (HDToolKit.aBoolean1790) { - var8.writeInt(var7 + (var6 + 1) * 9); - var8.writeInt(var7 + var6 * 9); - var8.writeInt(var7 + 1 + var6 * 9); - var8.writeInt(var7 + (var6 + 1) * 9); - var8.writeInt(var7 + 1 + var6 * 9); - var8.writeInt(var7 + 1 + (var6 + 1) * 9); - } else { - var8.writeIntLE(var7 + (var6 + 1) * 9); - var8.writeIntLE(var7 + var6 * 9); - var8.writeIntLE(var7 + 1 + var6 * 9); - var8.writeIntLE(var7 + (var6 + 1) * 9); - var8.writeIntLE(var7 + 1 + var6 * 9); - var8.writeIntLE(var7 + 1 + (var6 + 1) * 9); - } - } - } - - if (HDToolKit.supportVertexBufferObject) { - ByteBuffer var10 = ByteBuffer.wrap(var8.buffer, 0, var8.index); - this.aClass156_2105 = new Class156(); - this.aClass156_2105.method2170(var10); - } else { - this.aByteBuffer2107 = ByteBuffer.allocateDirect(var8.index).order(ByteOrder.nativeOrder()); - this.aByteBuffer2107.put(var8.buffer, 0, var8.index); - this.aByteBuffer2107.flip(); - } - - } - - final void method2282(LDIndexedSprite var1, int var2, int var3) { - byte[] var4 = var1.raster; - int var5 = var1.width; - int var6 = var2 * 128 + 1 + (var3 * 128 + 1) * var5; - int var7 = 0; - - int var8; - int var9; - for (var8 = -128; var8 < 0; ++var8) { - var7 = (var7 << 8) - var7; - - for (var9 = -128; var9 < 0; ++var9) { - if (var4[var6++] != 0) { - ++var7; - } - } - - var6 += var5 - 128; - } - - if (var7 != this.anInt2112) { - this.anInt2112 = var7; - var6 = var2 * 128 + 1 + (var3 * 128 + 1) * var5; - var8 = 0; - - for (var9 = -128; var9 < 0; ++var9) { - for (int var10 = -128; var10 < 0; ++var10) { - if (var4[var6] == 0) { - int var11 = 0; - if (var4[var6 - 1] != 0) { - ++var11; - } - - if (var4[var6 + 1] != 0) { - ++var11; - } - - if (var4[var6 - var5] != 0) { - ++var11; - } - - if (var4[var6 + var5] != 0) { - ++var11; - } - - aByteArray2111[var8++] = (byte) (17 * var11); - } else { - aByteArray2111[var8++] = 68; - } - - ++var6; - } - - var6 += var5 - 128; - } - - GL var12 = HDToolKit.gl; - ByteBuffer var13 = ByteBuffer.wrap(aByteArray2111); - var13.limit(16384); - HDToolKit.bindTexture2D(this.anInt2108); - var12.glTexImage2D(3553, 0, 6406, 128, 128, 0, 6406, 5121, var13); - - } - } - - final void method2284() { - GL var1 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt2108); - if (this.aClass156_2110 == null) { - if (HDToolKit.supportVertexBufferObject) { - var1.glBindBufferARB('\u8892', 0); - } - - var1.glInterleavedArrays(10791, 20, this.aByteBuffer2109); - } else { - this.aClass156_2110.method2169(); - var1.glInterleavedArrays(10791, 20, 0L); - } - HDToolKit.aBoolean1798 = false; - - if (this.aClass156_2105 == null) { - if (HDToolKit.supportVertexBufferObject) { - var1.glBindBufferARB('\u8893', 0); - } - - var1.glDrawElements(4, 384, 5125, this.aByteBuffer2107); - } else { - this.aClass156_2105.method2171(); - var1.glDrawElements(4, 384, 5125, 0L); - } - - } - -} diff --git a/Client/src/main/java/org/runite/client/Class17.java b/Client/src/main/java/org/runite/client/Class17.java deleted file mode 100644 index fc893119b..000000000 --- a/Client/src/main/java/org/runite/client/Class17.java +++ /dev/null @@ -1,213 +0,0 @@ -package org.runite.client; - -final class Class17 { - - static Interface2 anInterface2_408; - static int anInt410; - static Class64 aClass64_413; - - static void method904(int var0, Class140_Sub4 var1) { - try { - if (var1.anInt2779 != 0) { - RenderAnimationDefinition var2 = var1.getRenderAnimationType(); - int var4; - int var5; - if (var1.anInt2772 != -1 && 32768 > var1.anInt2772) { - NPC var3 = NPC.npcs[var1.anInt2772]; - if (var3 != null) { - var5 = -var3.zAxis + var1.zAxis; - var4 = -var3.xAxis + var1.xAxis; - if (0 != var4 || 0 != var5) { - var1.anInt2806 = (int) (Math.atan2(var4, var5) * 325.949D) & 0x7FF; - } - } - } - - int var6; - int var9; - if (var1.anInt2772 >= 32768) { - var9 = -32768 + var1.anInt2772; - if (Class3_Sub1.localIndex == var9) { - var9 = 2047; - } - - Player var10 = Unsorted.players[var9]; - if (null != var10) { - var6 = -var10.zAxis + var1.zAxis; - var5 = -var10.xAxis + var1.xAxis; - if (var5 != 0 || var6 != 0) { - var1.anInt2806 = (int) (Math.atan2(var5, var6) * 325.949D) & 0x7FF; - } - } - } - - if ((0 != var1.anInt2786 || 0 != var1.anInt2762) && (var1.anInt2816 == 0 || var1.anInt2824 > 0)) { - var9 = var1.xAxis + -((-Class131.anInt1716 + (var1.anInt2786 - Class131.anInt1716)) * 64); - var4 = -((-Texture.anInt1152 + (var1.anInt2762 - Texture.anInt1152)) * 64) + var1.zAxis; - if (var9 != 0 || var4 != 0) { - var1.anInt2806 = (int) (Math.atan2(var9, var4) * 325.949D) & 0x7FF; - } - - var1.anInt2762 = 0; - var1.anInt2786 = 0; - } - - var9 = var1.anInt2806 - var1.anInt2785 & 0x7FF; - if (var9 == 0) { - var1.anInt2789 = 0; - var1.anInt2821 = 0; - } else if (var2.yaw_acceleration == 0) { - ++var1.anInt2789; - boolean var11; - if (var9 > 1024) { - var1.anInt2785 -= var1.anInt2779; - var11 = true; - if (var1.anInt2779 > var9 || var9 > -var1.anInt2779 + 2048) { - var1.anInt2785 = var1.anInt2806; - var11 = false; - } - - if (var1.anInt2764 == var2.stand_animation && (25 < var1.anInt2789 || var11)) { - if (var2.standing_ccw_turn == -1) { - var1.anInt2764 = var2.walk_animation; - } else { - var1.anInt2764 = var2.standing_ccw_turn; - } - } - } else { - var11 = true; - var1.anInt2785 += var1.anInt2779; - if (var1.anInt2779 > var9 || 2048 - var1.anInt2779 < var9) { - var11 = false; - var1.anInt2785 = var1.anInt2806; - } - - if (var2.stand_animation == var1.anInt2764 && (25 < var1.anInt2789 || var11)) { - if (-1 == var2.standing_cw_turn) { - var1.anInt2764 = var2.walk_animation; - } else { - var1.anInt2764 = var2.standing_cw_turn; - } - } - } - - var1.anInt2785 &= 2047; - } else { - if (var1.anInt2764 == var2.stand_animation && 25 < var1.anInt2789) { - if (var2.standing_cw_turn == -1) { - var1.anInt2764 = var2.walk_animation; - } else { - var1.anInt2764 = var2.standing_cw_turn; - } - } - - var4 = var1.anInt2806 << 5; - if (var1.anInt2808 != var4) { - var1.anInt2791 = 0; - var1.anInt2808 = var4; - var5 = -var1.anInt2780 + var4 & 65535; - var6 = var1.anInt2821 * var1.anInt2821 / (var2.yaw_acceleration * 2); - int var7; - if (var1.anInt2821 > 0 && var6 <= var5 && -var6 + var5 < 32768) { - var1.anInt2803 = var5 / 2; - var1.aBoolean2769 = true; - var7 = var2.yaw_max_speed * var2.yaw_max_speed / (var2.yaw_acceleration * 2); - if (32767 < var7) { - var7 = 32767; - } - - if (var7 < var1.anInt2803) { - var1.anInt2803 = -var7 + var5; - } - } else if (0 > var1.anInt2821 && var6 <= -var5 + 65536 && 65536 + -var5 + -var6 < 32768) { - var1.anInt2803 = (-var5 + 65536) / 2; - var1.aBoolean2769 = true; - var7 = var2.yaw_max_speed * var2.yaw_max_speed / (var2.yaw_acceleration * 2); - if (var7 > 32767) { - var7 = 32767; - } - - if (var7 < var1.anInt2803) { - var1.anInt2803 = 65536 - (var5 + var7); - } - } else { - var1.aBoolean2769 = false; - } - } - - if (var1.anInt2821 == 0) { - var5 = -var1.anInt2780 + var1.anInt2808 & 65535; - if (var5 < var2.yaw_acceleration) { - var1.anInt2780 = var1.anInt2808; - } else { - var1.anInt2791 = 0; - var6 = var2.yaw_max_speed * var2.yaw_max_speed / (2 * var2.yaw_acceleration); - var1.aBoolean2769 = true; - if (32767 < var6) { - var6 = 32767; - } - - if (var5 >= 32768) { - var1.anInt2821 = -var2.yaw_acceleration; - var1.anInt2803 = (65536 - var5) / 2; - if (var1.anInt2803 > var6) { - var1.anInt2803 = 65536 - (var5 + var6); - } - } else { - var1.anInt2821 = var2.yaw_acceleration; - var1.anInt2803 = var5 / 2; - if (var1.anInt2803 > var6) { - var1.anInt2803 = -var6 + var5; - } - } - } - } else if (var1.anInt2821 <= 0) { - if (var1.anInt2803 <= var1.anInt2791) { - var1.aBoolean2769 = false; - } - - if (!var1.aBoolean2769) { - var1.anInt2821 += var2.yaw_acceleration; - if (0 < var1.anInt2821) { - var1.anInt2821 = 0; - } - } else if (var1.anInt2821 > -var2.yaw_max_speed) { - var1.anInt2821 -= var2.yaw_acceleration; - } - } else { - if (var1.anInt2791 >= var1.anInt2803) { - var1.aBoolean2769 = false; - } - - if (!var1.aBoolean2769) { - var1.anInt2821 -= var2.yaw_acceleration; - if (var1.anInt2821 < 0) { - var1.anInt2821 = 0; - } - } else if (var1.anInt2821 < var2.yaw_max_speed) { - var1.anInt2821 += var2.yaw_acceleration; - } - } - - var1.anInt2780 += var1.anInt2821; - var1.anInt2780 &= 65535; - if (0 >= var1.anInt2821) { - var1.anInt2791 -= var1.anInt2821; - } else { - var1.anInt2791 += var1.anInt2821; - } - - var1.anInt2785 = var1.anInt2780 >> 5; - } - - if (var0 != 65536) { - method904(-93, null); - } - - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "cm.A(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class18.java b/Client/src/main/java/org/runite/client/Class18.java deleted file mode 100644 index d3d72409f..000000000 --- a/Client/src/main/java/org/runite/client/Class18.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.runite.client; - -final class Class18 { - - short[] aShortArray416; - short[] aShortArray417; - short[] aShortArray418; - short[] aShortArray419; - - -} diff --git a/Client/src/main/java/org/runite/client/Class19.java b/Client/src/main/java/org/runite/client/Class19.java deleted file mode 100644 index 142519f09..000000000 --- a/Client/src/main/java/org/runite/client/Class19.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.runite.client; - -final class Class19 { - - static int anInt433 = 0; - int anInt420; - GameObject aClass140_423; - int anInt424; - int anInt425; - int anInt426; - int anInt427; - long aLong428 = 0L; - GameObject aClass140_429; - int anInt430; - int anInt432; - - -} diff --git a/Client/src/main/java/org/runite/client/Class20.java b/Client/src/main/java/org/runite/client/Class20.java deleted file mode 100644 index 4d11722e2..000000000 --- a/Client/src/main/java/org/runite/client/Class20.java +++ /dev/null @@ -1,170 +0,0 @@ -package org.runite.client; - -final class Class20 { - - static short[][] aShortArrayArray435 = new short[][]{{(short) 6554, (short) 115, (short) 10304, (short) 28, (short) 5702, (short) 7756, (short) 5681, (short) 4510, (short) -31835, (short) 22437, (short) 2859, (short) -11339, (short) 16, (short) 5157, (short) 10446, (short) 3658, (short) -27314, (short) -21965, (short) 472, (short) 580, (short) 784, (short) 21966, (short) 28950, (short) -15697, (short) -14002}, {(short) 9104, (short) 10275, (short) 7595, (short) 3610, (short) 7975, (short) 8526, (short) 918, (short) -26734, (short) 24466, (short) 10145, (short) -6882, (short) 5027, (short) 1457, (short) 16565, (short) -30545, (short) 25486, (short) 24, (short) 5392, (short) 10429, (short) 3673, (short) -27335, (short) -21957, (short) 192, (short) 687, (short) 412, (short) 21821, (short) 28835, (short) -15460, (short) -14019}, new short[0], new short[0], new short[0]}; - static RSInterface aClass11_439; - - - static void method907(int var0, int var1, int var2, int var3, int var4, GameObject var5, int var6, long var7, boolean var9) { - if (var5 == null) { - } else { - int var10 = var1 - var4; - int var11 = var2 - var4; - int var12 = var1 + var4; - int var13 = var2 + var4; - if (var9) { - if (var6 > 640 && var6 < 1408) { - var13 += 128; - } - - if (var6 > 1152 && var6 < 1920) { - var12 += 128; - } - - if (var6 > 1664 || var6 < 384) { - var11 -= 128; - } - - if (var6 > 128 && var6 < 896) { - var10 -= 128; - } - } - - var10 /= 128; - var11 /= 128; - var12 /= 128; - var13 /= 128; - Class56.method1189(var0, var10, var11, var12 - var10 + 1, var13 - var11 + 1, var1, var2, var3, var5, var6, true, var7); - } - } - - static void method909(RSInterface iface) { - if (Class3_Sub23.anInt2535 == iface.anInt204) { - Unsorted.aBooleanArray3674[iface.anInt292] = true; - } - } - - static void method910(int var1, int var2, int var3, int var4, int var5, Class91 var6) { - try { - long var7 = 0L; - if (var4 == 0) { - var7 = Class157.method2174(var3, var2, var1); - } else if (var4 == 1) { - var7 = Unsorted.method1395(var3, var2, var1); - } else if (var4 == 2) { - var7 = Class3_Sub28_Sub5.method557(var3, var2, var1); - } else if (3 == var4) { - var7 = Class3_Sub2.method104(var3, var2, var1); - } - - int var19 = (519128 & (int) var7) >> 14; - int var17 = (int) (var7 >>> 32) & Integer.MAX_VALUE; - ObjectDefinition var12 = ObjectDefinition.getObjectDefinition(var17); - if (var12.method1690()) { - Class140_Sub6.method2020(var2, var12, var1, var3); - } - - int var18 = ((int) var7 & 4109484) >> 20; - if (var7 != 0) { - GameObject var13 = null; - GameObject var14 = null; - if (0 == var4) { - Class70 var15 = LinkedList.method1209(var3, var2, var1); - if (null != var15) { - var13 = var15.aClass140_1049; - var14 = var15.aClass140_1052; - } - - if (var12.ClipType != 0) { - var6.method1485(var18, var12.ProjectileClipped, var1, var19, var2); - } - } else if (var4 == 1) { - Class19 var21 = Class39.method1037(var3, var2, var1); - if (var21 != null) { - var13 = var21.aClass140_429; - var14 = var21.aClass140_423; - } - } else if (2 == var4) { - Class25 var20 = Class163_Sub2.method2217(var3, var2, var1); - if (null != var20) { - var13 = var20.aClass140_479; - } - - if (var12.ClipType != 0 && var12.SizeX + var2 < 104 && var12.SizeX + var1 < 104 && 104 > var2 + var12.SizeY && var1 + var12.SizeY < 104) { - var6.method1502(var2, var12.SizeX, var12.ProjectileClipped, var18, var12.SizeY, var1); - } - } else { - Class12 var22 = Class159.method2193(var3, var2, var1); - if (var22 != null) { - var13 = var22.object; - } - - if (var12.ClipType == 1) { - var6.method1499(var1, var2); - } - } - - if (HDToolKit.highDetail && var12.aBoolean1503) { - if (2 == var19) { - if (var13 instanceof Class140_Sub3) { - ((Class140_Sub3) var13).method1960(); - } else { - Class8.method840(var12, (byte) -76, 0, var18 + 4, 0, var19, var2, var1, var5); - } - - if (var14 instanceof Class140_Sub3) { - ((Class140_Sub3) var14).method1960(); - } else { - Class8.method840(var12, (byte) -100, 0, 3 & var18 - -1, 0, var19, var2, var1, var5); - } - } else if (5 != var19) { - if (var19 == 6) { - if (var13 instanceof Class140_Sub3) { - ((Class140_Sub3) var13).method1960(); - } else { - Class8.method840(var12, (byte) -28, 8 * Class163_Sub3.anIntArray3007[var18], 4 - -var18, 8 * Class110.anIntArray2386[var18], 4, var2, var1, var5); - } - } else if (var19 == 7) { - if (var13 instanceof Class140_Sub3) { - ((Class140_Sub3) var13).method1960(); - } else { - Class8.method840(var12, (byte) -120, 0, 4 - -(3 & 2 + var18), 0, 4, var2, var1, var5); - } - } else if (var19 == 8) { - if (var13 instanceof Class140_Sub3) { - ((Class140_Sub3) var13).method1960(); - } else { - Class8.method840(var12, (byte) -45, Class163_Sub3.anIntArray3007[var18] * 8, var18 + 4, 8 * Class110.anIntArray2386[var18], 4, var2, var1, var5); - } - - if (var14 instanceof Class140_Sub3) { - ((Class140_Sub3) var14).method1960(); - } else { - Class8.method840(var12, (byte) -24, Class163_Sub3.anIntArray3007[var18] * 8, 4 - -(3 & 2 + var18), Class110.anIntArray2386[var18] * 8, 4, var2, var1, var5); - } - } else if (11 != var19) { - if (var13 instanceof Class140_Sub3) { - ((Class140_Sub3) var13).method1960(); - } else { - Class8.method840(var12, (byte) -113, 0, var18, 0, var19, var2, var1, var5); - } - } else if (var13 instanceof Class140_Sub3) { - ((Class140_Sub3) var13).method1960(); - } else { - Class8.method840(var12, (byte) -115, 0, 4 + var18, 0, 10, var2, var1, var5); - } - } else if (var13 instanceof Class140_Sub3) { - ((Class140_Sub3) var13).method1960(); - } else { - Class8.method840(var12, (byte) -119, Class3_Sub24_Sub3.anIntArray3491[var18] * 8, var18, RenderAnimationDefinition.anIntArray356[var18] * 8, 4, var2, var1, var5); - } - } - } - - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "dg.B(" + -96 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + (var6 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class21.java b/Client/src/main/java/org/runite/client/Class21.java deleted file mode 100644 index ac0944c73..000000000 --- a/Client/src/main/java/org/runite/client/Class21.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - - -public final class Class21 { - - static boolean aBoolean440 = false; - static int[] maskUpdateIndexes = new int[2048]; - static int sampleRate; - public static int anInt3537; - public static int anInt3552; - public static int anInt3395; - public static int anInt1462; - - - static void method912() { - try { - TextureOperation12.outgoingBuffer.index = 0; - Class7.anInt2166 = -1; - Class38_Sub1.aBoolean2615 = false; - Unsorted.incomingPacketLength = 0; - Class65.anInt987 = 0; - Unsorted.menuOptionCount = 0; - LinkableRSString.anInt2582 = -1; - Class161.anInt2028 = 0; - Class38_Sub1.anInt2617 = 0; - Class24.anInt469 = -1; - BufferedDataStream.incomingBuffer.index = 0; - AbstractSprite.anInt3699 = 0; - Unsorted.incomingOpcode = -1; - - int var1; - for (var1 = 0; Unsorted.players.length > var1; ++var1) { - if (null != Unsorted.players[var1]) { - Unsorted.players[var1].anInt2772 = -1; - } - } - - for (var1 = 0; NPC.npcs.length > var1; ++var1) { - if (NPC.npcs[var1] != null) { - NPC.npcs[var1].anInt2772 = -1; - } - } - - Class3_Sub28_Sub9.method580((byte) 80); - Class133.anInt1753 = 1; - Class117.method1719(30); - - for (var1 = 0; var1 < 100; ++var1) { - Unsorted.aBooleanArray3674[var1] = true; - } - - TextureOperation9.method204(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "dh.F(" + false + ')'); - } - } - - static Class118 method913() { - try { - try { - return (Class118) Class.forName(GameConfig.PACKAGE_NAME + ".Class118_Sub1").newInstance(); - } catch (Throwable var2) { - return null; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "dh.C(" + ')'); - } - } - - static void method914(int interfaceId, int interfaceHash, int walkable) { - try { - Class3_Sub31 var4 = new Class3_Sub31(); - var4.anInt2603 = walkable; - var4.anInt2602 = interfaceId; - TextureOperation23.aHashTable_3208.put(interfaceHash, var4); - TextureOperation20.method232(interfaceId); - RSInterface var5 = Unsorted.getRSInterface(interfaceHash); - if (var5 == null) { - System.out.println("Invalid interface opened - [window=" + (interfaceHash >> 16) + ", child=" + (interfaceHash & 0xFF) + ", id=" + interfaceId + "]"); - } else { - Class20.method909(var5); - } - - if (null != TextureOperation27.aClass11_3087) { - Class20.method909(TextureOperation27.aClass11_3087); - TextureOperation27.aClass11_3087 = null; - } - - int var6 = Unsorted.menuOptionCount; - - int var7; - for (var7 = 0; var6 > var7; ++var7) { - if (Unsorted.method73(TextureOperation27.aShortArray3095[var7])) { - Class3_Sub25.method509(var7); - } - } - - if (1 == Unsorted.menuOptionCount) { - Class38_Sub1.aBoolean2615 = false; - method1340(anInt1462, anInt3552, anInt3395, anInt3537); - } else { - method1340(anInt1462, anInt3552, anInt3395, anInt3537); - var7 = FontType.bold.method682(RSString.parse(GameConfig.RCM_TITLE)); - - for (int var8 = 0; Unsorted.menuOptionCount > var8; ++var8) { - int var9 = FontType.bold.method682(Unsorted.method802(var8)); - if (var7 < var9) { - var7 = var9; - } - } - - anInt3552 = 8 + var7; - anInt3537 = 15 * Unsorted.menuOptionCount + (!Unsorted.aBoolean1951 ? 22 : 26); - } - - if (var5 != null) { - Unsorted.method2104(var5, false, 55); - } - - TextureOperation24.method226(interfaceId); - if (ConfigInventoryDefinition.anInt3655 != -1) { - Class3_Sub8.method124(6422 ^ 6509, 1, ConfigInventoryDefinition.anInt3655); - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "dh.D(" + 6422 + ',' + interfaceId + ',' + interfaceHash + ',' + walkable + ')'); - } - } - - static void method915(RSString var0) { - try { - int var2 = Class3_Sub28_Sub8.method576(var0); - if (-1 != var2) { - Unsorted.method565(Class119.aClass131_1624.aShortArray1727[var2], Class119.aClass131_1624.aShortArray1718[var2]); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "dh.A(" + (var0 != null ? "{...}" : "null") + ',' + -1 + ')'); - } - } - - static Class146 method916() { - try { - - try { - return (Class146) Class.forName(GameConfig.PACKAGE_NAME + ".MouseWheel").newInstance(); - } catch (Throwable var2) { - return null; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "dh.E(" + (byte) 15 + ')'); - } - } - - public static void method1340(int var0, int var1, int var3, int var4) { - try { - - for (int var5 = 0; var5 < Class3_Sub28_Sub3.anInt3557; ++var5) { - if (var0 < Class3_Sub28_Sub18.anIntArray3768[var5] + AudioChannel.anIntArray1969[var5] && AudioChannel.anIntArray1969[var5] < var1 + var0 && Class140_Sub4.anIntArray2794[var5] + Player.anIntArray3954[var5] > var3 && Player.anIntArray3954[var5] < var3 - -var4) { - Unsorted.aBooleanArray3674[var5] = true; - } - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "kf.I(" + var0 + ',' + var1 + ',' + (byte) -40 + ',' + var3 + ',' + var4 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class22.java b/Client/src/main/java/org/runite/client/Class22.java deleted file mode 100644 index 50408ac2e..000000000 --- a/Client/src/main/java/org/runite/client/Class22.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.opengl.enums.GLBeginMode; - -import javax.media.opengl.GL; -import java.nio.IntBuffer; - -public final class Class22 { - - static HDSprite aHDSprite_447 = null; - static int anInt448 = 0; - static int anInt449 = 0; - static int anInt451 = 0; - private static int anInt450 = 0; - - public static void method921() { - aHDSprite_447 = null; - } - - public static void resetClipping() { - anInt449 = 0; - anInt448 = 0; - anInt450 = HDToolKit.viewWidth; - anInt451 = HDToolKit.viewHeight; - GL var0 = HDToolKit.gl; - var0.glDisable(3089); - method921(); - } - - static void method926(int[] var0, int var1, int var2, int var3, int var4) { - HDToolKit.method1835(); - GL var5 = HDToolKit.gl; - var5.glRasterPos2i(var1, HDToolKit.viewHeight - var2); - var5.glPixelZoom(1.0F, -1.0F); - var5.glDisable(3042); - var5.glDisable(3008); - var5.glDrawPixels(var3, var4, '\u80e1', HDToolKit.aBoolean1790 ? '\u8367' : 5121, IntBuffer.wrap(var0)); - var5.glEnable(3008); - var5.glEnable(3042); - var5.glPixelZoom(1.0F, 1.0F); - } - - static void method928(int var0, int var1, int var2, int var3, int var4, int var5) { - HDToolKit.method1835(); - float var6 = (float) var0 + 0.3F; - float var7 = var6 + (float) (var2 - 1); - float var8 = (float) HDToolKit.viewHeight - ((float) var1 + 0.3F); - float var9 = var8 - (float) (var3 - 1); - GL var10 = HDToolKit.gl; - var10.glBegin(GLBeginMode.LINE_LOOP); - var10.glColor4ub((byte) (var4 >> 16), (byte) (var4 >> 8), (byte) var4, var5 > 255 ? -1 : (byte) var5); - var10.glVertex2f(var6, var8); - var10.glVertex2f(var6, var9); - var10.glVertex2f(var7, var9); - var10.glVertex2f(var7, var8); - var10.glEnd(); - } - - static void method929(int var0, int var1, int var2, int var3, int var4, int var5) { - int var6 = var2 - var0; - int var7 = var3 - var1; - int var8 = var6 >= 0 ? var6 : -var6; - int var9 = var7 >= 0 ? var7 : -var7; - int var10 = var8; - if (var8 < var9) { - var10 = var9; - } - - if (var10 != 0) { - int var11 = (var6 << 16) / var10; - int var12 = (var7 << 16) / var10; - if (var12 <= var11) { - var11 = -var11; - } else { - var12 = -var12; - } - - int var13 = var5 * var12 >> 17; - int var14 = var5 * var12 + 1 >> 17; - int var15 = var5 * var11 >> 17; - int var16 = var5 * var11 + 1 >> 17; - int var17 = var0 + var13; - int var18 = var0 - var14; - int var19 = var0 + var6 - var14; - int var20 = var0 + var6 + var13; - int var21 = var1 + var15; - int var22 = var1 - var16; - int var23 = var1 + var7 - var16; - int var24 = var1 + var7 + var15; - HDToolKit.method1835(); - GL var25 = HDToolKit.gl; - var25.glColor3ub((byte) (var4 >> 16), (byte) (var4 >> 8), (byte) var4); - var25.glBegin(GLBeginMode.TRIANGLE_FAN); - if (var12 <= var11) { - var25.glVertex2f((float) var20, (float) (HDToolKit.viewHeight - var24)); - var25.glVertex2f((float) var19, (float) (HDToolKit.viewHeight - var23)); - var25.glVertex2f((float) var18, (float) (HDToolKit.viewHeight - var22)); - var25.glVertex2f((float) var17, (float) (HDToolKit.viewHeight - var21)); - } else { - var25.glVertex2f((float) var17, (float) (HDToolKit.viewHeight - var21)); - var25.glVertex2f((float) var18, (float) (HDToolKit.viewHeight - var22)); - var25.glVertex2f((float) var19, (float) (HDToolKit.viewHeight - var23)); - var25.glVertex2f((float) var20, (float) (HDToolKit.viewHeight - var24)); - } - - var25.glEnd(); - } - } - - static void method931(int var0, int var1, int var2, int var3) { - if (anInt449 < var0) { - anInt449 = var0; - } - - if (anInt448 < var1) { - anInt448 = var1; - } - - if (anInt450 > var2) { - anInt450 = var2; - } - - if (anInt451 > var3) { - anInt451 = var3; - } - - GL var4 = HDToolKit.gl; - var4.glEnable(3089); - if (anInt449 <= anInt450 && anInt448 <= anInt451) { - var4.glScissor(anInt449, HDToolKit.viewHeight - anInt451, anInt450 - anInt449, anInt451 - anInt448); - } else { - var4.glScissor(0, 0, 0, 0); - } - - method921(); - } - - static void method932() { - HDToolKit.gl.glClear(16640); - } - - static void method933(int var0, int var1, int var2, int var3, int var4) { - HDToolKit.method1835(); - float var5 = (float) var0 + 0.3F; - float var6 = (float) var2 + 0.3F; - float var7 = (float) HDToolKit.viewHeight - ((float) var1 + 0.3F); - float var8 = (float) HDToolKit.viewHeight - ((float) var3 + 0.3F); - GL var9 = HDToolKit.gl; - var9.glBegin(GLBeginMode.LINE_LOOP); - var9.glColor3ub((byte) (var4 >> 16), (byte) (var4 >> 8), (byte) var4); - var9.glVertex2f(var5, var7); - var9.glVertex2f(var6, var8); - var9.glEnd(); - } - - public static void setClipping(int left, int top, int right, int bottom) { - if (left < 0) { - left = 0; - } - - if (top < 0) { - top = 0; - } - - if (right > HDToolKit.viewWidth) { - right = HDToolKit.viewWidth; - } - - if (bottom > HDToolKit.viewHeight) { - bottom = HDToolKit.viewHeight; - } - - anInt449 = left; - anInt448 = top; - anInt450 = right; - anInt451 = bottom; - GL var4 = HDToolKit.gl; - var4.glEnable(3089); - if (anInt449 <= anInt450 && anInt448 <= anInt451) { - var4.glScissor(anInt449, HDToolKit.viewHeight - anInt451, anInt450 - anInt449, anInt451 - anInt448); - } else { - var4.glScissor(0, 0, 0, 0); - } - - method921(); - } - - public static void method936(HDSprite var0) { - if (var0.height == anInt451 - anInt448) { - aHDSprite_447 = var0; - } else { - throw new IllegalArgumentException(); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class23.java b/Client/src/main/java/org/runite/client/Class23.java deleted file mode 100644 index b0d161cd4..000000000 --- a/Client/src/main/java/org/runite/client/Class23.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.runite.client; - -public final class Class23 { - - public static int canvasWidth; - static int anInt453 = 0; - static int anInt455; - static boolean[][] aBooleanArrayArray457; - - - static void method938(int var0, int var1, int var2, int var3, int var4, int var5, int var7, int var8) { - try { - int var9 = var2 - var7; - int var10 = var3 - var8; - int var11 = (-var1 + var0 << 16) / var9; - int var12 = (-var5 + var4 << 16) / var10; - Class136.method1814(var1, var3, var2, var12, var7, var11, var8, var5); - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "dl.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + 1 + ',' + var7 + ',' + var8 + ')'); - } - } - - static DataBuffer method939() { - try { - DataBuffer var1 = new DataBuffer(34); - var1.writeByte(11); - var1.writeByte(Unsorted.anInt3625); - var1.writeByte(!Unsorted.aBoolean3665 ? 0 : 1); - var1.writeByte(Unsorted.aBoolean3604 ? 1 : 0); - var1.writeByte(KeyboardListener.aBoolean1905 ? 1 : 0); - var1.writeByte(Class25.aBoolean488 ? 1 : 0); - var1.writeByte(!RSInterface.aBoolean236 ? 0 : 1); - var1.writeByte(!WorldListEntry.aBoolean2623 ? 0 : 1); - var1.writeByte(Unsorted.aBoolean3275 ? 1 : 0); - var1.writeByte(!Class140_Sub6.aBoolean2910 ? 0 : 1); - var1.writeByte(Unsorted.anInt1137); - var1.writeByte(!Class106.aBoolean1441 ? 0 : 1); - var1.writeByte(Class128.aBoolean1685 ? 1 : 0); - var1.writeByte(Class38.aBoolean661 ? 1 : 0); - var1.writeByte(Class3_Sub28_Sub9.anInt3622); - var1.writeByte(!TextureOperation17.stereoSound ? 0 : 1); - var1.writeByte(AudioHandler.soundEffectVolume); - var1.writeByte(AudioHandler.musicVolume); - var1.writeByte(Sprites.ambientVolume); - var1.writeShort(TextureOperation.anInt2378); - var1.writeShort(Unsorted.anInt3071); - var1.writeByte(Class127_Sub1.method1757()); - var1.writeInt(Unsorted.anInt2148); - var1.writeByte(Unsorted.anInt2577); - var1.writeByte(Unsorted.aBoolean2146 ? 1 : 0); - var1.writeByte(!AudioThread.aBoolean346 ? 0 : 1); - var1.writeByte(Class3_Sub20.anInt2488); - var1.writeByte(Unsorted.aBoolean1080 ? 1 : 0); - var1.writeByte(Class163_Sub3.aBoolean3004 ? 1 : 0); - return var1; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "dl.C(" + (byte) -116 + ')'); - } - } - - static void method940(int var0, int var1) { - try { - if (var0 >= 101) { - MouseListeningClass var2 = Unsorted.aClass149_4047; - synchronized (var2) { - Unsorted.anInt4045 = var1; - } - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "dl.D(" + var0 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class24.java b/Client/src/main/java/org/runite/client/Class24.java deleted file mode 100644 index fea178407..000000000 --- a/Client/src/main/java/org/runite/client/Class24.java +++ /dev/null @@ -1,357 +0,0 @@ -package org.runite.client; - -import java.util.Objects; -import java.util.Random; - -public final class Class24 { - - public static int anInt469 = 0; - static Random aRandom3088 = new Random(); - static int anInt467 = 0; - static int anInt472 = 0; - static CacheIndex aClass153_152; - private final int[] anIntArray475 = new int[]{-1, -1, -1, -1, -1}; - int anInt466 = -1; - boolean aBoolean476 = false; - private short[] aShortArray460; - private short[] aShortArray464; - private short[] aShortArray470; - private short[] aShortArray471; - private int[] anIntArray474; - - static boolean isValidObjectMapping(byte var0, int var1, int var2, byte[] var3) { - try { - boolean var5 = true; - int var7 = -1; - DataBuffer buffer = new DataBuffer(var3); - - if (buffer.buffer.length == 0) { -// System.out.println("No object mapping found!"); - return true; - } - while (true) { - if (buffer.index == buffer.buffer.length) { - return true; - } - int var8 = buffer.method773(); - if (0 == var8) { - return var5; - } - - int var9 = 0; - var7 += var8; - boolean var10 = false; - - while (true) { - int var11; - if (var10) { - var11 = buffer.getSmart(); - if (var11 == 0) { - break; - } - - buffer.readUnsignedByte(); - } else { - if (buffer.index == buffer.buffer.length) { - break; - } - var11 = buffer.getSmart(); - if (0 == var11) { - break; - } - - var9 += var11 + -1; - int var12 = 63 & var9; - int var13 = (4088 & var9) >> 6; - int var16 = var2 + var12; - int var15 = var1 + var13; - int var14 = buffer.readUnsignedByte() >> 2; - if (var15 > 0 && var16 > 0 && 103 > var15 && 103 > var16) { - ObjectDefinition var17 = ObjectDefinition.getObjectDefinition(var7); - if (var14 != 22 || KeyboardListener.aBoolean1905 || 0 != var17.SecondInt || var17.ClipType == 1 || var17.aBoolean1483) { - var10 = true; - if (!var17.hasModels()) { - var5 = false; - ++Class162.anInt2038; - } - } - } - } - } - } - } catch (RuntimeException var18) { - throw ClientErrorException.clientError(var18, "dm.A(" + var0 + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - static void method949(int var0, int var2, int var3, int var4) { - try { - int var6 = 0; - TextureOperation18.method282(Class38.anIntArrayArray663[var4], -var2 + var0, 100, var0 - -var2, var3); - int var8 = -var2; - int var7 = var2; - int var9 = -1; - - while (var7 > var6) { - ++var6; - var9 += 2; - var8 += var9; - if (var8 >= 0) { - --var7; - var8 -= var7 << 1; - int[] var10 = Class38.anIntArrayArray663[var4 - -var7]; - int[] var11 = Class38.anIntArrayArray663[var4 - var7]; - int var12 = var0 - -var6; - int var13 = -var6 + var0; - TextureOperation18.method282(var10, var13, 115, var12, var3); - TextureOperation18.method282(var11, var13, 114, var12, var3); - } - - int var16 = var7 + var0; - int var15 = -var7 + var0; - int[] var17 = Class38.anIntArrayArray663[var4 - -var6]; - int[] var18 = Class38.anIntArrayArray663[-var6 + var4]; - TextureOperation18.method282(var17, var15, -61, var16, var3); - TextureOperation18.method282(var18, var15, -93, var16, var3); - } - - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "dm.I(" + var0 + ',' + (byte) 118 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method950(RSInterface var0, int var1, int var2, int var3) { - try { - if (2 <= Unsorted.menuOptionCount || Class164_Sub1.anInt3012 != 0 || GameObject.aBoolean1837) { - RSString var4 = method531(); - if (var0 == null) { - int var5 = FontType.bold.method683(var4, 4 + var3, var2 - -15, aRandom3088, Class38_Sub1.anInt2618); - Class21.method1340(4 + var3, FontType.bold.method682(var4) + var5, var2, 15); - } else { - Font var7 = var0.method868(Sprites.nameIconsSpriteArray); - if (null == var7) { - var7 = FontType.bold; - } - - var7.method702(var4, var3, var2, var0.width, var0.height, var0.anInt218, var0.anInt287, var0.anInt194, var0.anInt225, aRandom3088, Class38_Sub1.anInt2618, Player.anIntArray3951); - Class21.method1340(Player.anIntArray3951[0], Player.anIntArray3951[2], Player.anIntArray3951[1], Player.anIntArray3951[3]); - } - - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "dm.D(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static void method951() { - try { - Class3_Sub28_Sub18.aBoolean3769 = false; - TextureOperation25.anInt3413 = 0; - Client.messageToDisplay = -3; - Class50.anInt820 = 0; - LoginHandler.loginStage = 1; - Class166.anInt2079 = 0; - Class3_Sub26.anInt2561 = -1; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "dm.G(" + 0 + ')'); - } - } - - static RSString method531() { - RSString var1; - if (Class164_Sub1.anInt3012 == 1 && Unsorted.menuOptionCount < 2) { - var1 = RSString.stringCombiner(new RSString[]{TextCore.HasUse, TextCore.Spacer, RenderAnimationDefinition.aClass94_378, TextCore.aClass94_1724}); - } else if (GameObject.aBoolean1837 && 2 > Unsorted.menuOptionCount) { - var1 = RSString.stringCombiner(new RSString[]{Class3_Sub28_Sub9.aClass94_3621, TextCore.Spacer, TextCore.aClass94_676, TextCore.aClass94_1724}); - - } else if (ClientCommands.shiftClickEnabled && ObjectDefinition.aBooleanArray1490[81] && Unsorted.menuOptionCount > 2 && !ObjectDefinition.aBooleanArray1490[82]) { - for (Class3_Sub28_Sub1.counter = 2; Class3_Sub28_Sub1.counter < Unsorted.menuOptionCount; Class3_Sub28_Sub1.counter++) { - RSString option = (Unsorted.method802(Unsorted.menuOptionCount - Class3_Sub28_Sub1.counter)); - if (option.toString().contains("Drop") || option.toString().contains("Release")) { - ClientCommands.canDrop = true; - Class3_Sub28_Sub1.dropAction = Class3_Sub28_Sub1.counter; - break; - } else { - ClientCommands.canDrop = false; - } - } - if (ClientCommands.canDrop) { - var1 = Unsorted.method802(Unsorted.menuOptionCount - Class3_Sub28_Sub1.dropAction); - } else { - var1 = Unsorted.method802(Unsorted.menuOptionCount - 1); - } - } else { - var1 = Unsorted.method802(Unsorted.menuOptionCount - 1); - } - - if (Unsorted.menuOptionCount > 2) { - var1 = RSString.stringCombiner(new RSString[]{var1, Class1.aClass94_58, RSString.stringAnimator(Unsorted.menuOptionCount - 2), TextCore.HasMoreOptions}); - } - return var1; - } - - final Model_Sub1 method941() { - try { - int var3 = 0; - Model_Sub1[] var2 = new Model_Sub1[5]; - - for (int var4 = 0; var4 < 5; ++var4) { - if (this.anIntArray475[var4] != -1) { - var2[var3++] = Model_Sub1.method2015(aClass153_152, this.anIntArray475[var4]); - } - } - - Model_Sub1 var7 = new Model_Sub1(var2, var3); - int var5; - if (this.aShortArray464 != null) { - for (var5 = 0; this.aShortArray464.length > var5; ++var5) { - var7.method2016(this.aShortArray464[var5], this.aShortArray460[var5]); - } - } - - if (null != this.aShortArray471) { - for (var5 = 0; var5 < this.aShortArray471.length; ++var5) { - var7.method1998(this.aShortArray471[var5], this.aShortArray470[var5]); - } - } - - return var7; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "dm.F(" + true + ')'); - } - } - - final boolean method942() { - try { - if (null == this.anIntArray474) { - return true; - } else { - boolean var2 = true; - - for (int var3 = 0; this.anIntArray474.length > var3; ++var3) { - if (!aClass153_152.method2129((byte) -90, 0, this.anIntArray474[var3])) { - var2 = false; - } - } - - return var2; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "dm.J(" + 101 + ')'); - } - } - - private void method946(DataBuffer var2, int var3) { - try { - - if (var3 == 1) { - this.anInt466 = var2.readUnsignedByte(); - } else { - int var4; - int var5; - if (var3 == 2) { - var4 = var2.readUnsignedByte(); - this.anIntArray474 = new int[var4]; - - for (var5 = 0; var4 > var5; ++var5) { - this.anIntArray474[var5] = var2.readUnsignedShort(); - } - } else if (var3 == 3) { - this.aBoolean476 = true; - } else if (var3 == 40) { - var4 = var2.readUnsignedByte(); - this.aShortArray460 = new short[var4]; - this.aShortArray464 = new short[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.aShortArray464[var5] = (short) var2.readUnsignedShort(); - this.aShortArray460[var5] = (short) var2.readUnsignedShort(); - } - } else if (var3 == 41) { - var4 = var2.readUnsignedByte(); - this.aShortArray471 = new short[var4]; - this.aShortArray470 = new short[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.aShortArray471[var5] = (short) var2.readUnsignedShort(); - this.aShortArray470[var5] = (short) var2.readUnsignedShort(); - } - } else if (var3 >= 60 && var3 < 70) { - this.anIntArray475[-60 + var3] = var2.readUnsignedShort(); - } - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "dm.K(" + (byte) -84 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ')'); - } - } - - final Model_Sub1 method947() { - try { - if (this.anIntArray474 == null) { - return null; - } else { - Model_Sub1[] var2 = new Model_Sub1[this.anIntArray474.length]; - - for (int var3 = 0; this.anIntArray474.length > var3; ++var3) { - var2[var3] = Model_Sub1.method2015(aClass153_152, this.anIntArray474[var3]); - } - Model_Sub1 var7; - if (var2.length == 1) { - var7 = var2[0]; - } else { - var7 = new Model_Sub1(var2, var2.length); - } - - int var5; - if (null != this.aShortArray464) { - for (var5 = 0; var5 < this.aShortArray464.length; ++var5) { - Objects.requireNonNull(var7).method2016(this.aShortArray464[var5], this.aShortArray460[var5]); - } - } - - if (this.aShortArray471 != null) { - for (var5 = 0; this.aShortArray471.length > var5; ++var5) { - Objects.requireNonNull(var7).method1998(this.aShortArray471[var5], this.aShortArray470[var5]); - } - } - - return var7; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "dm.H(" + (byte) -26 + ')'); - } - } - - final boolean method948() { - try { - boolean var2 = true; - - for (int var3 = 0; var3 < 5; ++var3) { - if (-1 != this.anIntArray475[var3] && !aClass153_152.method2129((byte) 95, 0, this.anIntArray475[var3])) { - var2 = false; - } - } - - return !var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "dm.B(" + 18991 + ')'); - } - } - - final void method952(DataBuffer var2) { - try { - while (true) { - int var3 = var2.readUnsignedByte(); - if (0 == var3) { - return; - } - - this.method946(var2, var3); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "dm.L(" + -31957 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class25.java b/Client/src/main/java/org/runite/client/Class25.java deleted file mode 100644 index ed231bd4f..000000000 --- a/Client/src/main/java/org/runite/client/Class25.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.NodeCache; - -final class Class25 { - - static NodeCache aClass47_480 = new NodeCache(16); - static boolean aBoolean488 = true; - static int anInt491 = 0; - static int anInt497; - static int[][] anIntArrayArray499 = new int[][]{new int[0], {128, 0, 128, 128, 0, 128}, {0, 0, 128, 0, 128, 128, 64, 128}, {0, 128, 0, 0, 128, 0, 64, 128}, {0, 0, 64, 128, 0, 128}, {128, 128, 64, 128, 128, 0}, {64, 0, 128, 0, 128, 128, 64, 128}, {128, 0, 128, 128, 0, 128, 0, 64, 64, 0}, {0, 0, 64, 0, 0, 64}, {0, 0, 128, 0, 128, 128, 64, 96, 32, 64}, {0, 128, 0, 0, 32, 64, 64, 96, 128, 128}, {0, 128, 0, 0, 32, 32, 96, 32, 128, 0, 128, 128}}; - int anInt478; - GameObject aClass140_479; - int anInt481; - int anInt482; - int anInt483; - int anInt484; - int anInt487; - int anInt489; - int anInt490; - int anInt493; - int anInt495; - int anInt496; - long aLong498 = 0L; - - static void method955(int[][] var0, boolean var1, int var2, Class3_Sub11 var3, int[] var4, int var5, int var6, int var7, boolean var8, float[][] var9, boolean var10, int var11, float[][] var12, int var13, int var14, int var15, boolean var16, int[][] var17, float[][] var18, byte var19, int var20, boolean[] var21) { - try { - int var22 = (var2 << 8) + (var1 ? 255 : 0); - int var24 = (!var8 ? 0 : 255) + (var15 << 8); - int[] var26 = new int[var4.length / var11]; - int var25 = (var10 ? 255 : 0) + (var14 << 8); - int var23 = (var20 << 8) + (!var16 ? 0 : 255); - - for (int var27 = 0; var26.length > var27; ++var27) { - int var28 = var4[var27 + var27]; - int[][] var30 = null != var0 && var21 != null && var21[var27] ? var0 : var17; - int var29 = var4[var27 + var27 - -1]; - var26[var27] = Class121.method1734(var25, (float) var13, var22, var23, var0, var30, var7, var18, var24, (byte) -116, var19, false, var3, var9, var5, var28, var12, var29); - } - - var3.method150(var6, var7, var5, var26, null, false); - } catch (RuntimeException var31) { - throw ClientErrorException.clientError(var31, "ec.F(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + (var9 != null ? "{...}" : "null") + ',' + var10 + ',' + var11 + ',' + (var12 != null ? "{...}" : "null") + ',' + var13 + ',' + var14 + ',' + var15 + ',' + var16 + ',' + (var17 != null ? "{...}" : "null") + ',' + (var18 != null ? "{...}" : "null") + ',' + var19 + ',' + var20 + ',' + (var21 != null ? "{...}" : "null") + ')'); - } - } - - static void method956(Object[] var0, int var1, int[] var2, int var3, int var4) { - try { - if (var4 < var1) { - int var5 = (var4 + var1) / 2; - int var7 = var2[var5]; - int var6 = var4; - var2[var5] = var2[var1]; - var2[var1] = var7; - Object var8 = var0[var5]; - var0[var5] = var0[var1]; - var0[var1] = var8; - - for (int var9 = var4; var1 > var9; ++var9) { - if (var2[var9] < (var9 & 1) + var7) { - int var10 = var2[var9]; - var2[var9] = var2[var6]; - var2[var6] = var10; - Object var11 = var0[var9]; - var0[var9] = var0[var6]; - var0[var6++] = var11; - } - } - - var2[var1] = var2[var6]; - var2[var6] = var7; - var0[var1] = var0[var6]; - var0[var6] = var8; - method956(var0, var6 - 1, var2, 47, var4); - method956(var0, var1, var2, 100, 1 + var6); - } - - if (var3 < 16) { - method955(null, true, 94, null, null, -50, -107, -51, false, null, true, 73, null, -92, -7, -23, true, null, null, (byte) 52, 41, null); - } - - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "ec.G(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method957(boolean var1) { - try { - Unsorted.aBoolean3665 = var1; - Unsorted.aBoolean742 = !NPC.method1986(42); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ec.C(" + 96 + ',' + var1 + ')'); - } - } - - static void method958(byte var0) { - try { - if (GameObject.aBoolean1837) { - RSInterface var1 = AbstractSprite.method638(BufferedDataStream.anInt872, RSInterface.anInt278); - if (null != var1 && var1.anObjectArray303 != null) { - CS2Script var3 = new CS2Script(); - var3.arguments = var1.anObjectArray303; - var3.aClass11_2449 = var1; - Class43.method1065(var3); - } - - GameObject.aBoolean1837 = false; - Class3_Sub28_Sub5.anInt3590 = -1; - Class20.method909(var1); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ec.E(" + var0 + ')'); - } - } - - static void method959() { - try { - VarpHelpers.varbitLookup.clear(); - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ec.B(" + 0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class27.java b/Client/src/main/java/org/runite/client/Class27.java deleted file mode 100644 index 8a22a1a27..000000000 --- a/Client/src/main/java/org/runite/client/Class27.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -final class Class27 { - - static int[] anIntArray510 = new int[]{768, 1024, 1280, 512, 1536, 256, 0, 1792}; - static ReferenceCache aReferenceCache_511 = new ReferenceCache(30); - static int[] anIntArray512 = new int[500]; - static int anInt515 = -1; - static int anInt517 = 0; - static SoftwareSprite aSoftwareSprite_518; - static Class157 resampler; - - static RSInterface aClass11_526 = null; - - static int method961() { - try { - return Class23.anInt453 == 0 ? 0 : Unsorted.anShaderInterfaceArray70[Class23.anInt453].method24(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ef.B(" + 1536 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class29.java b/Client/src/main/java/org/runite/client/Class29.java deleted file mode 100644 index 7d3e4c61f..000000000 --- a/Client/src/main/java/org/runite/client/Class29.java +++ /dev/null @@ -1,189 +0,0 @@ -package org.runite.client; - -final class Class29 { - - static boolean aBoolean554 = false; - static CacheIndex aClass153_557; - static int[][][] anIntArrayArrayArray558; - static int anInt561 = 0; - int anInt556 = 0; - - static void method968(int var0) { - try { - int[] var1 = new int[TextureOperation39.itemDefinitionSize]; - int var2 = 0; - - int var3; - for (var3 = 0; TextureOperation39.itemDefinitionSize > var3; ++var3) { - ItemDefinition var4 = ItemDefinition.getItemDefinition(var3); - if (var4.anInt793 >= 0 || var4.anInt761 >= 0) { - var1[var2++] = var3; - } - } - - Class75_Sub4.anIntArray2664 = new int[var2]; - - - for (var3 = 0; var3 < var2; ++var3) { - Class75_Sub4.anIntArray2664[var3] = var1[var3]; - } - - if (var0 != 128) { - method968(19); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "eh.A(" + var0 + ')'); - } - } - - static void method971(int var0, int[][] var1, float[][] var2, int var3, float[][] var4, int var5, byte var6, int var7, int var8, boolean var10, byte var11, int var12, float[][] var13, int var14, Class3_Sub11 var15) { - try { - int var16 = 255 + (var0 << 8); - int var17 = (var5 << 8) - -255; - int var18 = (var8 << 8) - -255; - int var19 = (var14 << 8) - -255; - int[] var20 = Class134.anIntArrayArray1763[var11]; - int[] var22 = new int[var20.length >> 1]; - - int var23; - for (var23 = 0; var23 < var22.length; ++var23) { - var22[var23] = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -125, var6, false, var15, var2, var12, var20[var23 + var23], var4, var20[var23 + var23 + 1]); - } - - int[] var21 = null; - if (var10) { - int var24; - if (1 == var11) { - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -122, var6, true, var15, var2, var12, 64, var4, 128); - var24 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -114, var6, true, var15, var2, var12, 128, var4, 64); - var21 = new int[]{var24, var23, var22[2], var23, var22[0], var22[2]}; - } else if (2 == var11) { - var21 = new int[6]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -121, var6, true, var15, var2, var12, 128, var4, 128); - var24 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -128, var6, true, var15, var2, var12, 64, var4, 0); - var21[2] = var23; - var21[0] = var22[0]; - var21[5] = var22[0]; - var21[3] = var23; - var21[1] = var24; - var21[4] = var22[1]; - } else if (var11 == 3) { - var21 = new int[6]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -114, var6, true, var15, var2, var12, 0, var4, 128); - var24 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -115, var6, true, var15, var2, var12, 64, var4, 0); - var21[4] = var24; - var21[1] = var22[1]; - var21[0] = var22[2]; - var21[3] = var23; - var21[2] = var23; - var21[5] = var22[2]; - } else if (var11 == 4) { - var21 = new int[3]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -115, var6, true, var15, var2, var12, 0, var4, 128); - var21[0] = var22[3]; - var21[2] = var22[0]; - var21[1] = var23; - } else if (var11 == 5) { - var21 = new int[3]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -112, var6, true, var15, var2, var12, 128, var4, 128); - var21[1] = var23; - var21[0] = var22[2]; - var21[2] = var22[3]; - } else if (var11 == 6) { - var21 = new int[6]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -123, var6, true, var15, var2, var12, 128, var4, 0); - var24 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -115, var6, true, var15, var2, var12, 128, var4, 128); - var21[1] = var23; - var21[0] = var22[3]; - var21[2] = var24; - var21[4] = var22[0]; - var21[3] = var24; - var21[5] = var22[3]; - } else if (var11 == 7) { - var21 = new int[6]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -126, var6, true, var15, var2, var12, 0, var4, 128); - var24 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -117, var6, true, var15, var2, var12, 128, var4, 0); - var21[3] = var23; - var21[2] = var23; - var21[0] = var22[1]; - var21[4] = var22[2]; - var21[1] = var24; - var21[5] = var22[1]; - } else if (var11 == 8) { - var21 = new int[3]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -117, var6, true, var15, var2, var12, 0, var4, 0); - var21[2] = var22[4]; - var21[0] = var22[3]; - var21[1] = var23; - } else if (var11 == 9) { - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -118, var6, true, var15, var2, var12, 128, var4, 64); - var24 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -121, var6, true, var15, var2, var12, 96, var4, 32); - int var25 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -120, var6, true, var15, var2, var12, 64, var4, 0); - var21 = new int[]{var24, var23, var22[4], var24, var22[4], var22[3], var24, var22[3], var22[2], var24, var22[2], var22[1], var24, var22[1], var25}; - } else if (10 == var11) { - var21 = new int[9]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -120, var6, true, var15, var2, var12, 0, var4, 128); - var21[0] = var22[2]; - var21[8] = var22[0]; - var21[1] = var23; - var21[4] = var23; - var21[2] = var22[3]; - var21[7] = var23; - var21[3] = var22[3]; - var21[5] = var22[4]; - var21[6] = var22[4]; - } else if (var11 == 11) { - var21 = new int[12]; - var23 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -119, var6, true, var15, var2, var12, 0, var4, 64); - var24 = Class121.method1734(var19, 0.0F, var16, var17, null, var1, var3, var13, var18, (byte) -121, var6, true, var15, var2, var12, 128, var4, 64); - var21[5] = var23; - var21[1] = var23; - var21[8] = var23; - var21[0] = var22[3]; - var21[2] = var22[0]; - var21[11] = var24; - var21[6] = var22[2]; - var21[7] = var24; - var21[10] = var22[1]; - var21[3] = var22[3]; - var21[4] = var22[2]; - var21[9] = var22[2]; - } - } - - var15.method150(var7, var3, var12, var22, var21, false); - } catch (RuntimeException var26) { - throw ClientErrorException.clientError(var26, "eh.C(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + (var4 != null ? "{...}" : "null") + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + (byte) 88 + ',' + var10 + ',' + var11 + ',' + var12 + ',' + (var13 != null ? "{...}" : "null") + ',' + var14 + ',' + (var15 != null ? "{...}" : "null") + ')'); - } - } - - final void method970(DataBuffer var2) { - try { - while (true) { - int var3 = var2.readUnsignedByte(); - if (0 == var3) { - - return; - } - - this.method972(var2, var3); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "eh.B(" + -20638 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - private void method972(DataBuffer var1, int var3) { - try { - - if (var3 == 5) { - this.anInt556 = var1.readUnsignedShort(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "eh.F(" + (var1 != null ? "{...}" : "null") + ',' + (byte) -117 + ',' + var3 + ')'); - } - } - -} \ No newline at end of file diff --git a/Client/src/main/java/org/runite/client/Class30.java b/Client/src/main/java/org/runite/client/Class30.java deleted file mode 100644 index 04b77a708..000000000 --- a/Client/src/main/java/org/runite/client/Class30.java +++ /dev/null @@ -1,363 +0,0 @@ -package org.runite.client; - -import org.rs09.client.util.ArrayUtils; - -import java.io.EOFException; -import java.io.File; -import java.io.IOException; - -final class Class30 { - - static int[] anIntArray574 = new int[14]; - static float aFloat578; - static boolean loadedWorldList = false; - private final byte[] aByteArray564; - private final byte[] aByteArray572; - private final RandomAccessFileWrapper aRandomAccessFileWrapper_573; - private long aLong563; - private int anInt566 = 0; - private long aLong568; - private long aLong569; - private long aLong570; - private long aLong571 = -1L; - private int anInt575; - private long aLong576 = -1L; - - - Class30(RandomAccessFileWrapper var1, int var2) throws IOException { - try { - this.aRandomAccessFileWrapper_573 = var1; - this.aLong568 = this.aLong563 = var1.getLength(); - this.aByteArray572 = new byte[0]; - this.aByteArray564 = new byte[var2]; - this.aLong569 = 0L; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "en.(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + 0 + ')'); - } - } - - static void method979(int var0, int var1, int var2) { - try { - RSString var4 = RSString.stringCombiner(new RSString[]{TextCore.aClass94_853, RSString.stringAnimator(var2), RSString.parse(")1"), RSString.stringAnimator(var0 >> 6), RSString.parse(")1"), RSString.stringAnimator(var1 >> 6), RSString.parse(")1"), RSString.stringAnimator(var0 & 63), RSString.parse(")1"), RSString.stringAnimator(63 & var1)}); - - ClientCommands.ClientCommands(var4); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "en.I(" + var0 + ',' + var1 + ',' + var2 + ',' + (byte) -4 + ')'); - } - } - - private void method975(byte var1) throws IOException { - try { - if (-1L != this.aLong571) { - if (this.aLong571 != this.aLong570) { - this.aRandomAccessFileWrapper_573.seek(this.aLong571); - this.aLong570 = this.aLong571; - } - - this.aRandomAccessFileWrapper_573.write(this.aByteArray572, this.anInt566, 0); - long var3 = -1L; - if (this.aLong571 >= this.aLong576 && this.aLong571 < this.aLong576 + (long) this.anInt575) { - var3 = this.aLong571; - } else if (this.aLong571 <= this.aLong576 && this.aLong571 - -((long) this.anInt566) > this.aLong576) { - var3 = this.aLong576; - } - - this.aLong570 += this.anInt566; - if (this.aLong563 < this.aLong570) { - this.aLong563 = this.aLong570; - } - - long var5 = -1L; - if (this.aLong576 < this.aLong571 - -((long) this.anInt566) && (long) this.anInt575 + this.aLong576 >= (long) this.anInt566 + this.aLong571) { - var5 = this.aLong571 - -((long) this.anInt566); - } else if (this.aLong571 < this.aLong576 - -((long) this.anInt575) && (long) this.anInt566 + this.aLong571 >= (long) this.anInt575 + this.aLong576) { - var5 = (long) this.anInt575 + this.aLong576; - } - - if (-1L < var3 && var3 < var5) { - int var7 = (int) (-var3 + var5); - ArrayUtils.arraycopy(this.aByteArray572, (int) (var3 - this.aLong571), this.aByteArray564, (int) (var3 + -this.aLong576), var7); - } - - this.anInt566 = 0; - this.aLong571 = -1L; - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "en.C(" + var1 + ')'); - } - } - - final long method976(int var1) { - try { - if (var1 != 0) { - this.method976(19); - } - - return this.aLong568; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "en.J(" + var1 + ')'); - } - } - - private File method977() { - try { - - return this.aRandomAccessFileWrapper_573.getPath(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "en.D(" + 281669816 + ')'); - } - } - - final void method978(int var1, byte[] var2, int var3) throws IOException { - try { - try { - if (var1 - -var3 > var2.length) { - throw new ArrayIndexOutOfBoundsException(var3 + var1 - var2.length); - } - - if (-1L != this.aLong571 && this.aLong569 >= this.aLong571 && (long) this.anInt566 + this.aLong571 >= (long) var3 + this.aLong569) { - ArrayUtils.arraycopy(this.aByteArray572, (int) (-this.aLong571 + this.aLong569), var2, var1, var3); - this.aLong569 += var3; - return; - } - - long var5 = this.aLong569; - int var8 = var3; - int var9; - if (this.aLong576 <= this.aLong569 && this.aLong576 + (long) this.anInt575 > this.aLong569) { - var9 = (int) ((long) this.anInt575 - this.aLong569 + this.aLong576); - if (var3 < var9) { - var9 = var3; - } - - ArrayUtils.arraycopy(this.aByteArray564, (int) (this.aLong569 - this.aLong576), var2, var1, var9); - var1 += var9; - var3 -= var9; - this.aLong569 += var9; - } - - if (var3 > this.aByteArray564.length) { - this.aRandomAccessFileWrapper_573.seek(this.aLong569); - - for (this.aLong570 = this.aLong569; 0 < var3; this.aLong569 += var9) { - var9 = this.aRandomAccessFileWrapper_573.read(var2, var1, var3, 0); - if (var9 == -1) { - break; - } - - this.aLong570 += var9; - var3 -= var9; - var1 += var9; - } - } else if (var3 > 0) { - this.method981(); - var9 = var3; - if (var3 > this.anInt575) { - var9 = this.anInt575; - } - - ArrayUtils.arraycopy(this.aByteArray564, 0, var2, var1, var9); - var3 -= var9; - var1 += var9; - this.aLong569 += var9; - } - - if (-1L != this.aLong571) { - if (this.aLong569 < this.aLong571 && var3 > 0) { - var9 = (int) (-this.aLong569 + this.aLong571) + var1; - if (var9 > var1 - -var3) { - var9 = var1 + var3; - } - - while (var1 < var9) { - var2[var1++] = 0; - ++this.aLong569; - --var3; - } - } - - long var16 = -1L; - long var11 = -1L; - if ((long) this.anInt566 + this.aLong571 > var5 && (long) var8 + var5 >= (long) this.anInt566 + this.aLong571) { - var11 = (long) this.anInt566 + this.aLong571; - } else if ((long) var8 + var5 > this.aLong571 && (long) var8 + var5 <= (long) this.anInt566 + this.aLong571) { - var11 = (long) var8 + var5; - } - - if (this.aLong571 >= var5 && this.aLong571 < var5 - -((long) var8)) { - var16 = this.aLong571; - } else if (this.aLong571 <= var5 && var5 < (long) this.anInt566 + this.aLong571) { - var16 = var5; - } - - if (-1L < var16 && var11 > var16) { - int var13 = (int) (var11 + -var16); - ArrayUtils.arraycopy(this.aByteArray572, (int) (var16 + -this.aLong571), var2, (int) (var16 + -var5) + var1, var13); - if (this.aLong569 < var11) { - var3 = (int) ((long) var3 - (-this.aLong569 + var11)); - this.aLong569 = var11; - } - } - } - } catch (IOException var14) { - this.aLong570 = -1L; - throw var14; - } - - if (0 < var3) { - throw new EOFException(); - } - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "en.F(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + 0 + ')'); - } - } - - final void method980() throws IOException { - try { - this.method975((byte) -75); - this.aRandomAccessFileWrapper_573.close(); - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "en.K(" + false + ')'); - } - } - - private void method981() throws IOException { - try { - this.anInt575 = 0; - - if (this.aLong570 != this.aLong569) { - this.aRandomAccessFileWrapper_573.seek(this.aLong569); - this.aLong570 = this.aLong569; - } - - int var3; - for (this.aLong576 = this.aLong569; this.aByteArray564.length > this.anInt575; this.anInt575 += var3) { - int var2 = this.aByteArray564.length + -this.anInt575; - if (var2 > 200000000) { - var2 = 200000000; - } - - var3 = this.aRandomAccessFileWrapper_573.read(this.aByteArray564, this.anInt575, var2, 0); - if (var3 == -1) { - break; - } - - this.aLong570 += var3; - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "en.A(" + 4393 + ')'); - } - } - - final void method982(byte[] var2) throws IOException { - try { - - this.method978(0, var2, var2.length); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "en.B(" + false + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final void method983(byte[] var1, int var2, int var3, int var4) throws IOException { - try { - try { - if ((long) var4 + this.aLong569 > this.aLong568) { - this.aLong568 = (long) var4 + this.aLong569; - } - - if (this.aLong571 != -1 && (this.aLong571 > this.aLong569 || (long) this.anInt566 + this.aLong571 < this.aLong569)) { - this.method975((byte) 124); - } - - if (this.aLong571 != -1 && (long) this.aByteArray572.length + this.aLong571 < (long) var4 + this.aLong569) { - int var5 = (int) ((long) this.aByteArray572.length - this.aLong569 + this.aLong571); - var4 -= var5; - ArrayUtils.arraycopy(var1, var2, this.aByteArray572, (int) (this.aLong569 + -this.aLong571), var5); - this.aLong569 += var5; - this.anInt566 = this.aByteArray572.length; - this.method975((byte) 93); - var2 += var5; - } - - if (var4 > this.aByteArray572.length) { - if (this.aLong570 != this.aLong569) { - this.aRandomAccessFileWrapper_573.seek(this.aLong569); - this.aLong570 = this.aLong569; - } - - this.aRandomAccessFileWrapper_573.write(var1, var4, var2); - long var12 = -1L; - if (this.aLong576 <= this.aLong569 && this.aLong569 < (long) this.anInt575 + this.aLong576) { - var12 = this.aLong569; - } else if (this.aLong576 >= this.aLong569 && this.aLong576 < (long) var4 + this.aLong569) { - var12 = this.aLong576; - } - - this.aLong570 += var4; - long var7 = -1L; - if (this.aLong563 < this.aLong570) { - this.aLong563 = this.aLong570; - } - - if (this.aLong569 + (long) var4 > this.aLong576 && this.aLong576 - -((long) this.anInt575) >= this.aLong569 - -((long) var4)) { - var7 = (long) var4 + this.aLong569; - } else if (this.aLong569 < this.aLong576 + (long) this.anInt575 && this.aLong569 + (long) var4 >= (long) this.anInt575 + this.aLong576) { - var7 = (long) this.anInt575 + this.aLong576; - } - - if (var12 > -1 && var7 > var12) { - int var9 = (int) (-var12 + var7); - ArrayUtils.arraycopy(var1, (int) (-this.aLong569 + var12 + (long) var2), this.aByteArray564, (int) (-this.aLong576 + var12), var9); - } - - this.aLong569 += var4; - return; - } - - if (0 < var4) { - if (this.aLong571 == -1L) { - this.aLong571 = this.aLong569; - } - - ArrayUtils.arraycopy(var1, var2, this.aByteArray572, (int) (this.aLong569 + -this.aLong571), var4); - this.aLong569 += var4; - if (-this.aLong571 + this.aLong569 > (long) this.anInt566) { - this.anInt566 = (int) (-this.aLong571 + this.aLong569); - } - - return; - } - } catch (IOException var10) { - this.aLong570 = -1L; - throw var10; - } - - if (var3 != -903171152) { - this.aLong563 = -28L; - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "en.H(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - final void method984(int var1, long var2) throws IOException { - try { - if (var2 >= 0L) { - this.aLong569 = var2; - if (var1 > -6) { - this.aLong569 = 89L; - } - - } else { - throw new IOException("Invalid seek to " + var2 + " in file " + this.method977()); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "en.E(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class31.java b/Client/src/main/java/org/runite/client/Class31.java deleted file mode 100644 index 91775ecf7..000000000 --- a/Client/src/main/java/org/runite/client/Class31.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.LinkableInt; - -import javax.media.opengl.GL; - - -final class Class31 { - - private static final LinkedList A_LINKED_LIST___581 = new LinkedList(); - private static final LinkedList A_LINKED_LIST___586 = new LinkedList(); - private static final LinkedList A_LINKED_LIST___587 = new LinkedList(); - private static final LinkedList A_LINKED_LIST___588 = new LinkedList(); - private static final int[] anIntArray589 = new int[1000]; - static int anInt580 = 0; - static int anInt582 = 0; - static int memory2D = 0; - static int anInt585 = 0; - private static long aLong583 = 0L; - - static synchronized void method985(int var0, int var1, int var2) { - if (var2 == anInt582) { - LinkableInt var3 = new LinkableInt(var1); - var3.linkableKey = var0; - A_LINKED_LIST___587.method1215(var3); - } - } - - static synchronized void method986(int var0, int var1) { - if (var1 == anInt582) { - LinkableInt var2 = new LinkableInt(); - var2.linkableKey = var0; - A_LINKED_LIST___588.method1215(var2); - } - } - - static synchronized void method988() { - ++anInt582; - A_LINKED_LIST___581.method1211(-110); - A_LINKED_LIST___586.method1211(-88); - A_LINKED_LIST___587.method1211(-123); - A_LINKED_LIST___588.method1211(-115); - anInt585 = 0; - memory2D = 0; - anInt580 = 0; - } - - static synchronized void method989(int var0, int var1, int var2) { - if (var2 == anInt582) { - LinkableInt var3 = new LinkableInt(var1); - var3.linkableKey = var0; - A_LINKED_LIST___581.method1215(var3); - } - } - - static synchronized void method990() { - GL var0 = HDToolKit.gl; - int var1 = 0; - - while (true) { - LinkableInt var2 = (LinkableInt) A_LINKED_LIST___581.method1220(); - if (var2 == null) { - if (var1 > 0) { - var0.glDeleteBuffersARB(var1, anIntArray589, 0); - var1 = 0; - } - - while (true) { - var2 = (LinkableInt) A_LINKED_LIST___586.method1220(); - if (var2 == null) { - while (true) { - var2 = (LinkableInt) A_LINKED_LIST___587.method1220(); - if (var2 == null) { - if (var1 > 0) { - var0.glDeleteTextures(var1, anIntArray589, 0); - } - - while (true) { - var2 = (LinkableInt) A_LINKED_LIST___588.method1220(); - if (var2 == null) { - if (anInt585 + memory2D + anInt580 > 100663296 && TimeUtils.time() > aLong583 + 60000L) { - System.gc(); - aLong583 = TimeUtils.time(); - } - - return; - } - - int var3 = (int) var2.linkableKey; - var0.glDeleteLists(var3, 1); - } - } - - anIntArray589[var1++] = (int) var2.linkableKey; - anInt580 -= var2.value; - if (var1 == 1000) { - var0.glDeleteTextures(var1, anIntArray589, 0); - var1 = 0; - } - } - } - - anIntArray589[var1++] = (int) var2.linkableKey; - memory2D -= var2.value; - if (var1 == 1000) { - var0.glDeleteTextures(var1, anIntArray589, 0); - var1 = 0; - } - } - } - - anIntArray589[var1++] = (int) var2.linkableKey; - anInt585 -= var2.value; - if (var1 == 1000) { - var0.glDeleteBuffersARB(var1, anIntArray589, 0); - var1 = 0; - } - } - } - - static synchronized void method991(int var0, int var1, int var2) { - if (var2 == anInt582) { - LinkableInt var3 = new LinkableInt(var1); - var3.linkableKey = var0; - A_LINKED_LIST___586.method1215(var3); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class32.java b/Client/src/main/java/org/runite/client/Class32.java deleted file mode 100644 index de9b6b79f..000000000 --- a/Client/src/main/java/org/runite/client/Class32.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.runite.client; - -final class Class32 { - - static int anInt590 = -1; - static int anInt1744 = 0; - - - static RSString method992(DataBuffer var0) { - try { - - return Class140_Sub7.method2033(var0); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fc.C(" + (var0 != null ? "{...}" : "null") + ',' + 29488 + ')'); - } - } - - static int method993(int var0, int var2) { - try { - int var3 = -128 + (Class65.method1234(4, var2 + '\ub135', var0 - -91923) + (Class65.method1234(2, var2 + 10294, '\u93bd' + var0) + -128 >> 1) - -(Class65.method1234(1, var2, var0) + -128 >> 2)); - var3 = 35 + (int) (0.3D * (double) var3); - if (var3 >= 10) { - if (var3 > 60) { - var3 = 60; - } - } else { - var3 = 10; - } - - return var3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "fc.B(" + var0 + ',' + 125 + ',' + var2 + ')'); - } - } - - static void method995() { - int var0; - int var1; - int var2; - if (Unsorted.aClass3_Sub2ArrayArrayArray4070 != null) { - for (var0 = 0; var0 < Unsorted.aClass3_Sub2ArrayArrayArray4070.length; ++var0) { - for (var1 = 0; var1 < Unsorted.anInt1234; ++var1) { - for (var2 = 0; var2 < TextureOperation17.anInt3179; ++var2) { - Unsorted.aClass3_Sub2ArrayArrayArray4070[var0][var1][var2] = null; - } - } - } - } - - Client.aClass3_Sub11ArrayArray2199 = null; - if (Class166.aClass3_Sub2ArrayArrayArray2065 != null) { - for (var0 = 0; var0 < Class166.aClass3_Sub2ArrayArrayArray2065.length; ++var0) { - for (var1 = 0; var1 < Unsorted.anInt1234; ++var1) { - for (var2 = 0; var2 < TextureOperation17.anInt3179; ++var2) { - Class166.aClass3_Sub2ArrayArrayArray2065[var0][var1][var2] = null; - } - } - } - } - - TextureOperation32.aClass3_Sub11ArrayArray3346 = null; - Class3_Sub4.anInt2249 = 0; - if (Class3_Sub28_Sub8.aClass113Array3610 != null) { - } - - if (SequenceDefinition.aClass25Array1868 != null) { - for (var0 = 0; var0 < Unsorted.anInt3070; ++var0) { - SequenceDefinition.aClass25Array1868[var0] = null; - } - - Unsorted.anInt3070 = 0; - } - - if (Unsorted.aClass25Array4060 != null) { - for (var0 = 0; var0 < Unsorted.aClass25Array4060.length; ++var0) { - Unsorted.aClass25Array4060[var0] = null; - } - } - - } - - static void method996() { - try { - - KeyboardListener var1 = TextureOperation33.aClass148_3049; - synchronized (var1) { - Class3_Sub28_Sub9.anInt3620 = Class134.anInt1762; - ++TextureOperation29.anInt3398; - int var2; - if (KeyboardListener.anInt2384 < 0) { - for (var2 = 0; var2 < 112; ++var2) { - ObjectDefinition.aBooleanArray1490[var2] = false; - } - - KeyboardListener.anInt2384 = anInt1744; - } else { - while (KeyboardListener.anInt2384 != anInt1744) { - var2 = Unsorted.anIntArray2952[anInt1744]; - anInt1744 = 127 & 1 + anInt1744; - if (0 <= var2) { - ObjectDefinition.aBooleanArray1490[var2] = true; - } else { - ObjectDefinition.aBooleanArray1490[~var2] = false; - } - } - } - - Class134.anInt1762 = Class25.anInt491; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "fc.E(" + -43 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class33.java b/Client/src/main/java/org/runite/client/Class33.java deleted file mode 100644 index 8df327484..000000000 --- a/Client/src/main/java/org/runite/client/Class33.java +++ /dev/null @@ -1,355 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; - -import java.awt.Font; -import java.awt.*; -import java.awt.image.PixelGrabber; - -final class Class33 { - - private static final String aString597 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"??$%^&*()-_=+[{]};:'@#~,<.>/?\\| " + '\u00c4' + '\u00cb' + '\u00cf' + '\u00d6' + '\u00dc' + '\u00e4' + '\u00eb' + '\u00ef' + '\u00f6' + '\u00fc' + '\u00ff' + '\u00df' + '\u00c1' + '\u00c0' + '\u00c9' + '\u00c8' + '\u00cd' + '\u00cc' + '\u00d3' + '\u00d2' + '\u00da' + '\u00d9' + '\u00e1' + '\u00e0' + '\u00e9' + '\u00e8' + '\u00ed' + '\u00ec' + '\u00f3' + '\u00f2' + '\u00fa' + '\u00f9' + '\u00c2' + '\u00ca' + '\u00ce' + '\u00d4' + '\u00db' + '\u00e2' + '\u00ea' + '\u00ee' + '\u00f4' + '\u00fb' + '\u00c6' + '\u00e6'; - private static final int anInt598 = aString597.length(); - private static final int[] anIntArray599 = new int[256]; - - static { - for (int var0 = 0; var0 < 256; ++var0) { - int var1 = aString597.indexOf(var0); - if (var1 == -1) { - var1 = 74; - } - - anIntArray599[var0] = var1 * 9; - } - - } - - private byte[] aByteArray594 = new byte[100000]; - private boolean aBoolean595; - private int anInt596; - - Class33(int fontSize, Component var3) { - this.anInt596 = anInt598 * 9; - this.aBoolean595 = false; - Font var4 = new Font("Helvetica", Font.BOLD, fontSize); - FontMetrics var5 = var3.getFontMetrics(var4); - - int var6; - for (var6 = 0; var6 < anInt598; ++var6) { - this.method1004(var4, var5, aString597.charAt(var6), var6, false); - } - - if (this.aBoolean595) { - this.anInt596 = anInt598 * 9; - this.aBoolean595 = false; - var4 = new Font("Helvetica", Font.PLAIN, fontSize); - var5 = var3.getFontMetrics(var4); - - for (var6 = 0; var6 < anInt598; ++var6) { - this.method1004(var4, var5, aString597.charAt(var6), var6, false); - } - - if (!this.aBoolean595) { - this.anInt596 = anInt598 * 9; - this.aBoolean595 = false; - - for (var6 = 0; var6 < anInt598; ++var6) { - this.method1004(var4, var5, aString597.charAt(var6), var6, true); - } - } - } - - byte[] var8 = new byte[this.anInt596]; - - if (this.anInt596 >= 0) System.arraycopy(this.aByteArray594, 0, var8, 0, this.anInt596); - - this.aByteArray594 = var8; - } - - private void method997(RSString var1, int var2, int var3, int var4, boolean var5) { - if (this.aBoolean595 || var4 == 0) { - var5 = false; - } - - for (int var6 = 0; var6 < var1.length(); ++var6) { - int var7 = anIntArray599[var1.charAt(var6, (byte) 65)]; - if (var5) { - this.method1001(var7, var2 + 1, var3, 1, this.aByteArray594); - this.method1001(var7, var2, var3 + 1, 1, this.aByteArray594); - } - - this.method1001(var7, var2, var3, var4, this.aByteArray594); - var2 += this.aByteArray594[var7 + 7]; - } - - } - - final int method998() { - return this.aByteArray594[8] - 1; - } - - private void method1000(int[] var1, byte[] var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - for (int var10 = -var7; var10 < 0; ++var10) { - for (int var11 = -var6; var11 < 0; ++var11) { - int var12 = var2[var4++] & 0xFF; - if (var12 > 30) { - if (var12 >= 230) { - var1[var5++] = var3; - } else { - int var13 = var1[var5]; - var1[var5++] = ((var3 & 16711935) * var12 + (var13 & 16711935) * (256 - var12) & -16711936) + ((var3 & 65280) * var12 + (var13 & 65280) * (256 - var12) & 16711680) >> 8; - } - } else { - ++var5; - } - } - - var5 += var8; - var4 += var9; - } - - } - - private void method1001(int var1, int var2, int var3, int var4, byte[] var5) { - int var6 = var2 + var5[var1 + 5]; - int var7 = var3 - var5[var1 + 6]; - int var8 = var5[var1 + 3]; - int var9 = var5[var1 + 4]; - int var10 = var5[var1] * 16384 + var5[var1 + 1] * 128 + var5[var1 + 2]; - int var11 = var6 + var7 * Toolkit.JAVA_TOOLKIT.width; - int var12 = Toolkit.JAVA_TOOLKIT.width - var8; - int var13 = 0; - int var14; - if (var7 < Toolkit.JAVA_TOOLKIT.clipTop) { - var14 = Toolkit.JAVA_TOOLKIT.clipTop - var7; - var9 -= var14; - var7 = Toolkit.JAVA_TOOLKIT.clipTop; - var10 += var14 * var8; - var11 += var14 * Toolkit.JAVA_TOOLKIT.width; - } - - if (var7 + var9 >= Toolkit.JAVA_TOOLKIT.clipBottom) { - var9 -= var7 + var9 - Toolkit.JAVA_TOOLKIT.clipBottom + 1; - } - - if (var6 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var14 = Toolkit.JAVA_TOOLKIT.clipLeft - var6; - var8 -= var14; - var6 = Toolkit.JAVA_TOOLKIT.clipLeft; - var10 += var14; - var11 += var14; - var13 += var14; - var12 += var14; - } - - if (var6 + var8 >= Toolkit.JAVA_TOOLKIT.clipRight) { - var14 = var6 + var8 - Toolkit.JAVA_TOOLKIT.clipRight + 1; - var8 -= var14; - var13 += var14; - var12 += var14; - } - - if (var8 > 0 && var9 > 0) { - if (this.aBoolean595) { - this.method1000(Toolkit.JAVA_TOOLKIT.getBuffer(), var5, var4, var10, var11, var8, var9, var12, var13); - } else { - this.method1002(Toolkit.JAVA_TOOLKIT.getBuffer(), var5, var4, var10, var11, var8, var9, var12, var13); - } - } - - } - - private void method1002(int[] var1, byte[] var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - int var10 = -(var6 >> 2); - var6 = -(var6 & 3); - - for (int var11 = -var7; var11 < 0; ++var11) { - int var12; - for (var12 = var10; var12 < 0; ++var12) { - if (var2[var4++] == 0) { - ++var5; - } else { - var1[var5++] = var3; - } - - if (var2[var4++] == 0) { - ++var5; - } else { - var1[var5++] = var3; - } - - if (var2[var4++] == 0) { - ++var5; - } else { - var1[var5++] = var3; - } - - if (var2[var4++] == 0) { - ++var5; - } else { - var1[var5++] = var3; - } - } - - for (var12 = var6; var12 < 0; ++var12) { - if (var2[var4++] == 0) { - ++var5; - } else { - var1[var5++] = var3; - } - } - - var5 += var8; - var4 += var9; - } - - } - - final void method1003(RSString var1, int var2, int var3, int var4) { - int var6 = this.method1005(var1) / 2; - int var7 = this.method1006(); - if (var2 - var6 <= Toolkit.JAVA_TOOLKIT.clipRight) { - if (var2 + var6 >= Toolkit.JAVA_TOOLKIT.clipLeft) { - if (var3 - var7 <= Toolkit.JAVA_TOOLKIT.clipBottom) { - if (var3 >= 0) { - this.method997(var1, var2 - var6, var3, var4, true); - } - } - } - } - } - - private void method1004(Font var1, FontMetrics var2, char var3, int var4, boolean var5) { - int var6 = var2.charWidth(var3); - int var7 = var6; - if (var5) { - try { - if (var3 == 47) { - var5 = false; - } - - if (var3 == 102 || var3 == 116 || var3 == 119 || var3 == 118 || var3 == 107 || var3 == 120 || var3 == 121 || var3 == 65 || var3 == 86 || var3 == 87) { - ++var6; - } - } catch (Exception var23) { - } - } - - int var8 = var2.getMaxAscent(); - int var9 = var2.getMaxAscent() + var2.getMaxDescent(); - int var10 = var2.getHeight(); - Image var11 = GameShell.canvas.createImage(var6, var9); - Graphics var12 = var11.getGraphics(); - var12.setColor(Color.black); - var12.fillRect(0, 0, var6, var9); - var12.setColor(Color.white); - var12.setFont(var1); - var12.drawString(var3 + "", 0, var8); - if (var5) { - var12.drawString(var3 + "", 1, var8); - } - - int[] var13 = new int[var6 * var9]; - PixelGrabber var14 = new PixelGrabber(var11, 0, 0, var6, var9, var13, 0, var6); - - try { - var14.grabPixels(); - } catch (Exception var22) { - } - - var11.flush(); - int var15 = 0; - int var16 = 0; - int var17 = var6; - int var18 = var9; - - int var19; - int var21; - int var20; - label134: - for (var19 = 0; var19 < var9; ++var19) { - for (var20 = 0; var20 < var6; ++var20) { - var21 = var13[var20 + var19 * var6]; - if ((var21 & 16777215) != 0) { - var16 = var19; - break label134; - } - } - } - - label122: - for (var19 = 0; var19 < var6; ++var19) { - for (var20 = 0; var20 < var9; ++var20) { - var21 = var13[var19 + var20 * var6]; - if ((var21 & 16777215) != 0) { - var15 = var19; - break label122; - } - } - } - - label110: - for (var19 = var9 - 1; var19 >= 0; --var19) { - for (var20 = 0; var20 < var6; ++var20) { - var21 = var13[var20 + var19 * var6]; - if ((var21 & 16777215) != 0) { - var18 = var19 + 1; - break label110; - } - } - } - - label98: - for (var19 = var6 - 1; var19 >= 0; --var19) { - for (var20 = 0; var20 < var9; ++var20) { - var21 = var13[var19 + var20 * var6]; - if ((var21 & 16777215) != 0) { - var17 = var19 + 1; - break label98; - } - } - } - - this.aByteArray594[var4 * 9] = (byte) (this.anInt596 / 16384); - this.aByteArray594[var4 * 9 + 1] = (byte) (this.anInt596 / 128 & 127); - this.aByteArray594[var4 * 9 + 2] = (byte) (this.anInt596 & 127); - this.aByteArray594[var4 * 9 + 3] = (byte) (var17 - var15); - this.aByteArray594[var4 * 9 + 4] = (byte) (var18 - var16); - this.aByteArray594[var4 * 9 + 5] = (byte) var15; - this.aByteArray594[var4 * 9 + 6] = (byte) (var8 - var16); - this.aByteArray594[var4 * 9 + 7] = (byte) var7; - this.aByteArray594[var4 * 9 + 8] = (byte) var10; - - for (var19 = var16; var19 < var18; ++var19) { - for (var20 = var15; var20 < var17; ++var20) { - var21 = var13[var20 + var19 * var6] & 0xFF; - if (var21 > 30 && var21 < 230) { - this.aBoolean595 = true; - } - - this.aByteArray594[this.anInt596++] = (byte) var21; - } - } - - } - - private int method1005(RSString var1) { - int var2 = 0; - - for (int var3 = 0; var3 < var1.length(); ++var3) { - if (var1.charAt(var3, (byte) -41) == 64 && var3 + 4 < var1.length() && var1.charAt(var3 + 4, (byte) 66) == 64) { - var3 += 4; - } else if (var1.charAt(var3, (byte) 101) == 126 && var3 + 4 < var1.length() && var1.charAt(var3 + 4, (byte) -51) == 126) { - var3 += 4; - } else { - var2 += this.aByteArray594[anIntArray599[var1.charAt(var3, (byte) -64)] + 7]; - } - } - - return var2; - } - - final int method1006() { - return this.aByteArray594[6]; - } -} diff --git a/Client/src/main/java/org/runite/client/Class35.java b/Client/src/main/java/org/runite/client/Class35.java deleted file mode 100644 index bead7e901..000000000 --- a/Client/src/main/java/org/runite/client/Class35.java +++ /dev/null @@ -1,253 +0,0 @@ -package org.runite.client; - -final class Class35 { - - private static final int[][] anIntArrayArray619 = new int[][]{{1, 3, 5, 7}, {1, 3, 5, 7}, {1, 3, 5, 7}, {1, 3, 5, 7, 6}, {1, 3, 5, 7, 6}, {1, 3, 5, 7, 6}, {1, 3, 5, 7, 6}, {1, 3, 5, 7, 2, 6}, {1, 3, 5, 7, 2, 8}, {1, 3, 5, 7, 2, 8}, {1, 3, 5, 7, 11, 12}, {1, 3, 5, 7, 11, 12}, {1, 3, 5, 7, 13, 14}}; - private static final int[][] anIntArrayArray620 = new int[][]{{0, 1, 2, 3, 0, 0, 1, 3}, {1, 1, 2, 3, 1, 0, 1, 3}, {0, 1, 2, 3, 1, 0, 1, 3}, {0, 0, 1, 2, 0, 0, 2, 4, 1, 0, 4, 3}, {0, 0, 1, 4, 0, 0, 4, 3, 1, 1, 2, 4}, {0, 0, 4, 3, 1, 0, 1, 2, 1, 0, 2, 4}, {0, 1, 2, 4, 1, 0, 1, 4, 1, 0, 4, 3}, {0, 4, 1, 2, 0, 4, 2, 5, 1, 0, 4, 5, 1, 0, 5, 3}, {0, 4, 1, 2, 0, 4, 2, 3, 0, 4, 3, 5, 1, 0, 4, 5}, {0, 0, 4, 5, 1, 4, 1, 2, 1, 4, 2, 3, 1, 4, 3, 5}, {0, 0, 1, 5, 0, 1, 4, 5, 0, 1, 2, 4, 1, 0, 5, 3, 1, 5, 4, 3, 1, 4, 2, 3}, {1, 0, 1, 5, 1, 1, 4, 5, 1, 1, 2, 4, 0, 0, 5, 3, 0, 5, 4, 3, 0, 4, 2, 3}, {1, 0, 5, 4, 1, 0, 1, 5, 0, 0, 4, 3, 0, 4, 5, 3, 0, 5, 2, 3, 0, 1, 2, 5}}; - static int[] anIntArray614 = new int[6]; - static int[] anIntArray622 = new int[6]; - static int[] anIntArray623 = new int[6]; - static int[] anIntArray628 = new int[6]; - static int[] anIntArray630 = new int[6]; - int anInt611; - int anInt612; - int[] anIntArray613; - int[] anIntArray615; - int[] anIntArray616; - int[] anIntArray617; - int[] anIntArray618; - int anInt621; - int[] anIntArray624; - int[] anIntArray625; - int anInt626; - int[] anIntArray627; - boolean aBoolean629 = true; - int[] anIntArray631; - int[] anIntArray632; - - - Class35(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12, int var13, int var14, int var15, int var16, int var17, int var18, int var19) { - if (var6 != var7 || var6 != var8 || var6 != var9) { - this.aBoolean629 = false; - } - - this.anInt611 = var1; - this.anInt612 = var2; - this.anInt626 = var18; - this.anInt621 = var19; - short var20 = 128; - int var21 = var20 / 2; - int var22 = var20 / 4; - int var23 = var20 * 3 / 4; - int[] var24 = anIntArrayArray619[var1]; - int var25 = var24.length; - this.anIntArray627 = new int[var25]; - this.anIntArray615 = new int[var25]; - this.anIntArray618 = new int[var25]; - int[] var26 = new int[var25]; - int[] var27 = new int[var25]; - int var28 = var4 * var20; - int var29 = var5 * var20; - - int var31; - int var34; - int var35; - int var32; - int var33; - int var36; - for (int var30 = 0; var30 < var25; ++var30) { - var31 = var24[var30]; - if ((var31 & 1) == 0 && var31 <= 8) { - var31 = (var31 - var2 - var2 - 1 & 7) + 1; - } - - if (var31 > 8 && var31 <= 12) { - var31 = (var31 - 9 - var2 & 3) + 9; - } - - if (var31 > 12 && var31 <= 16) { - var31 = (var31 - 13 - var2 & 3) + 13; - } - - if (var31 == 1) { - var32 = var28; - var33 = var29; - var34 = var6; - var35 = var10; - var36 = var14; - } else if (var31 == 2) { - var32 = var28 + var21; - var33 = var29; - var34 = var6 + var7 >> 1; - var35 = var10 + var11 >> 1; - var36 = var14 + var15 >> 1; - } else if (var31 == 3) { - var32 = var28 + var20; - var33 = var29; - var34 = var7; - var35 = var11; - var36 = var15; - } else if (var31 == 4) { - var32 = var28 + var20; - var33 = var29 + var21; - var34 = var7 + var8 >> 1; - var35 = var11 + var12 >> 1; - var36 = var15 + var16 >> 1; - } else if (var31 == 5) { - var32 = var28 + var20; - var33 = var29 + var20; - var34 = var8; - var35 = var12; - var36 = var16; - } else if (var31 == 6) { - var32 = var28 + var21; - var33 = var29 + var20; - var34 = var8 + var9 >> 1; - var35 = var12 + var13 >> 1; - var36 = var16 + var17 >> 1; - } else if (var31 == 7) { - var32 = var28; - var33 = var29 + var20; - var34 = var9; - var35 = var13; - var36 = var17; - } else if (var31 == 8) { - var32 = var28; - var33 = var29 + var21; - var34 = var9 + var6 >> 1; - var35 = var13 + var10 >> 1; - var36 = var17 + var14 >> 1; - } else if (var31 == 9) { - var32 = var28 + var21; - var33 = var29 + var22; - var34 = var6 + var7 >> 1; - var35 = var10 + var11 >> 1; - var36 = var14 + var15 >> 1; - } else if (var31 == 10) { - var32 = var28 + var23; - var33 = var29 + var21; - var34 = var7 + var8 >> 1; - var35 = var11 + var12 >> 1; - var36 = var15 + var16 >> 1; - } else if (var31 == 11) { - var32 = var28 + var21; - var33 = var29 + var23; - var34 = var8 + var9 >> 1; - var35 = var12 + var13 >> 1; - var36 = var16 + var17 >> 1; - } else if (var31 == 12) { - var32 = var28 + var22; - var33 = var29 + var21; - var34 = var9 + var6 >> 1; - var35 = var13 + var10 >> 1; - var36 = var17 + var14 >> 1; - } else if (var31 == 13) { - var32 = var28 + var22; - var33 = var29 + var22; - var34 = var6; - var35 = var10; - var36 = var14; - } else if (var31 == 14) { - var32 = var28 + var23; - var33 = var29 + var22; - var34 = var7; - var35 = var11; - var36 = var15; - } else if (var31 == 15) { - var32 = var28 + var23; - var33 = var29 + var23; - var34 = var8; - var35 = var12; - var36 = var16; - } else { - var32 = var28 + var22; - var33 = var29 + var23; - var34 = var9; - var35 = var13; - var36 = var17; - } - - this.anIntArray627[var30] = var32; - this.anIntArray615[var30] = var34; - this.anIntArray618[var30] = var33; - var26[var30] = var35; - var27[var30] = var36; - } - - int[] var38 = anIntArrayArray620[var1]; - var31 = var38.length / 4; - this.anIntArray624 = new int[var31]; - this.anIntArray617 = new int[var31]; - this.anIntArray613 = new int[var31]; - this.anIntArray625 = new int[var31]; - this.anIntArray632 = new int[var31]; - this.anIntArray631 = new int[var31]; - if (var3 != -1) { - this.anIntArray616 = new int[var31]; - } - - var32 = 0; - - for (var33 = 0; var33 < var31; ++var33) { - var34 = var38[var32]; - var35 = var38[var32 + 1]; - var36 = var38[var32 + 2]; - int var37 = var38[var32 + 3]; - var32 += 4; - if (var35 < 4) { - var35 = var35 - var2 & 3; - } - - if (var36 < 4) { - var36 = var36 - var2 & 3; - } - - if (var37 < 4) { - var37 = var37 - var2 & 3; - } - - this.anIntArray624[var33] = var35; - this.anIntArray617[var33] = var36; - this.anIntArray613[var33] = var37; - if (var34 == 0) { - this.anIntArray625[var33] = var26[var35]; - this.anIntArray632[var33] = var26[var36]; - this.anIntArray631[var33] = var26[var37]; - if (this.anIntArray616 != null) { - this.anIntArray616[var33] = -1; - } - } else { - this.anIntArray625[var33] = var27[var35]; - this.anIntArray632[var33] = var27[var36]; - this.anIntArray631[var33] = var27[var37]; - if (this.anIntArray616 != null) { - this.anIntArray616[var33] = var3; - } - } - } - - var33 = var6; - var34 = var7; - if (var7 < var6) { - var33 = var7; - } - - if (var8 < var33) { - var33 = var8; - } - - if (var8 > var34) { - var34 = var8; - } - - if (var9 < var33) { - var33 = var9; - } - - if (var9 > var34) { - var34 = var9; - } - - } - -} diff --git a/Client/src/main/java/org/runite/client/Class36.java b/Client/src/main/java/org/runite/client/Class36.java deleted file mode 100644 index bd810a99f..000000000 --- a/Client/src/main/java/org/runite/client/Class36.java +++ /dev/null @@ -1,331 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.config.GameConfig; - -import java.util.Objects; - -final class Class36 { - - static AbstractSprite aAbstractSprite_637; - static int anInt638; - static int anInt639; - static byte[][][] aByteArrayArrayArray640; - static int anInt641; - - static { - anInt639 = 0; - anInt638 = 0; - anInt641 = 0; - } - - private final byte[] aByteArray635; - private final int[] anIntArray636; - private int[] anIntArray633; - - Class36(byte[] var1) { - try { - int[] var3 = new int[33]; - int var2 = var1.length; - this.anIntArray633 = new int[8]; - this.anIntArray636 = new int[var2]; - this.aByteArray635 = var1; - int var4 = 0; - - for (int var5 = 0; var5 < var2; ++var5) { - byte var6 = var1[var5]; - if (var6 != 0) { - int var7 = 1 << 32 + -var6; - int var8 = var3[var6]; - this.anIntArray636[var5] = var8; - int var9; - int var10; - int var11; - int var12; - if (0 == (var8 & var7)) { - for (var10 = -1 + var6; var10 >= 1; --var10) { - var11 = var3[var10]; - if (var11 != var8) { - break; - } - - var12 = 1 << -var10 + 32; - if ((var11 & var12) != 0) { - var3[var10] = var3[-1 + var10]; - break; - } - - var3[var10] = TextureOperation3.bitwiseOr(var12, var11); - } - - var9 = var8 | var7; - } else { - var9 = var3[-1 + var6]; - } - - var3[var6] = var9; - - for (var10 = var6 + 1; var10 <= 32; ++var10) { - if (var3[var10] == var8) { - var3[var10] = var9; - } - } - - var10 = 0; - - for (var11 = 0; var11 < var6; ++var11) { - var12 = Integer.MIN_VALUE >>> var11; - if (0 == (var8 & var12)) { - ++var10; - } else { - if (0 == this.anIntArray633[var10]) { - this.anIntArray633[var10] = var4; - } - - var10 = this.anIntArray633[var10]; - } - - if (this.anIntArray633.length <= var10) { - int[] var13 = new int[this.anIntArray633.length * 2]; - - System.arraycopy(this.anIntArray633, 0, var13, 0, this.anIntArray633.length); - - this.anIntArray633 = var13; - } - - } - - this.anIntArray633[var10] = ~var5; - if (var4 <= var10) { - var4 = var10 + 1; - } - } - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "fi.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static Class129 method1012() { - try { - try { - - return (Class129) Class.forName(GameConfig.PACKAGE_NAME + ".Class129_Sub2").newInstance(); - } catch (Throwable var2) { - return new Class129_Sub1(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fi.F(" + (byte) -31 + ')'); - } - } - - static RSString method1013(byte var0, int var1) { - try { - RSString var2 = RSString.stringAnimator(var1); - if (var0 >= -87) { - return null; - } else { - for (int var3 = Objects.requireNonNull(var2).length() + -3; var3 > 0; var3 -= 3) { - var2 = RSString.stringCombiner(new RSString[]{var2.substring(0, var3, 0), RSString.parse(")1"), var2.substring(var3)}); - } - - return var2.length() > 9 ? RSString.stringCombiner(new RSString[]{ColorCore.MillionStackColor, var2.substring(0, -8 + var2.length(), 0), TextCore.MillionM, TextCore.LEFT_PARENTHESES, var2, RSString.parse("(Y<)4col>")}) : (6 < var2.length() ? RSString.stringCombiner(new RSString[]{ColorCore.ThousandStackColor, var2.substring(0, -4 + var2.length(), 0), TextCore.ThousandK, TextCore.LEFT_PARENTHESES, var2, RSString.parse("(Y<)4col>")}) : RSString.stringCombiner(new RSString[]{ColorCore.DefaultStackColor, var2, TextCore.aClass94_2584})); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "fi.D(" + var0 + ',' + var1 + ')'); - } - } - - final int method1015(int var1, byte[] var3, byte[] var4, int var5, int var6) { - try { - var1 += var5; - int var7 = 0; - - int var8; - for (var8 = var6 << 3; var1 > var5; ++var5) { - int var9 = var4[var5] & 0xFF; - int var10 = this.anIntArray636[var9]; - byte var11 = this.aByteArray635[var9]; - if (0 == var11) { - throw new RuntimeException("No codeword for data value " + var9); - } - - int var12 = var8 >> 3; - int var13 = var8 & 7; - var8 += var11; - int var14 = var12 + (var13 + var11 - 1 >> 3); - var7 &= -var13 >> 31; - var13 += 24; - var3[var12] = (byte) (var7 = TextureOperation3.bitwiseOr(var7, var10 >>> var13)); - if (var14 > var12) { - ++var12; - var13 -= 8; - var3[var12] = (byte) (var7 = var10 >>> var13); - if (var12 < var14) { - var13 -= 8; - ++var12; - var3[var12] = (byte) (var7 = var10 >>> var13); - if (var12 < var14) { - var13 -= 8; - ++var12; - var3[var12] = (byte) (var7 = var10 >>> var13); - if (var14 > var12) { - ++var12; - var13 -= 8; - var3[var12] = (byte) (var7 = var10 << -var13); - } - } - } - } - } - - return -var6 + (var8 + 7 >> 3); - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "fi.A(" + var1 + ',' + -81 + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ',' + var5 + ',' + var6 + ')'); - } - } - - final int method1017(int var1, int var2, byte[] var3, byte[] var5, int var6) { - try { - if (var2 == 0) { - return 0; - } else { - int var7 = 0; - var2 += var1; - int var8 = var6; - - while (true) { - byte var9 = var5[var8]; - if (var9 < 0) { - var7 = this.anIntArray633[var7]; - } else { - ++var7; - } - - int var10; - if ((var10 = this.anIntArray633[var7]) < 0) { - var3[var1++] = (byte) (~var10); - if (var1 >= var2) { - break; - } - - var7 = 0; - } - - if ((64 & var9) == 0) { - ++var7; - } else { - var7 = this.anIntArray633[var7]; - } - - if ((var10 = this.anIntArray633[var7]) < 0) { - var3[var1++] = (byte) (~var10); - if (var2 <= var1) { - break; - } - - var7 = 0; - } - - if ((32 & var9) == 0) { - ++var7; - } else { - var7 = this.anIntArray633[var7]; - } - - if ((var10 = this.anIntArray633[var7]) < 0) { - var3[var1++] = (byte) (~var10); - if (var1 >= var2) { - break; - } - - var7 = 0; - } - - if ((var9 & 16) == 0) { - ++var7; - } else { - var7 = this.anIntArray633[var7]; - } - - if ((var10 = this.anIntArray633[var7]) < 0) { - var3[var1++] = (byte) (~var10); - if (var1 >= var2) { - break; - } - - var7 = 0; - } - - if ((var9 & 8) == 0) { - ++var7; - } else { - var7 = this.anIntArray633[var7]; - } - - if ((var10 = this.anIntArray633[var7]) < 0) { - var3[var1++] = (byte) (~var10); - if (var2 <= var1) { - break; - } - - var7 = 0; - } - - if ((var9 & 4) == 0) { - ++var7; - } else { - var7 = this.anIntArray633[var7]; - } - - if ((var10 = this.anIntArray633[var7]) < 0) { - var3[var1++] = (byte) (~var10); - if (var2 <= var1) { - break; - } - - var7 = 0; - } - - if ((var9 & 2) == 0) { - ++var7; - } else { - var7 = this.anIntArray633[var7]; - } - var10 = this.anIntArray633[var7]; - - if (var10 < 0) { - var3[var1++] = (byte) (~var10); - if (var2 <= var1) { - break; - } - - var7 = 0; - } - - if ((1 & var9) == 0) { - ++var7; - } else { - var7 = this.anIntArray633[var7]; - } - - if (0 > (var10 = this.anIntArray633[var7])) { - var3[var1++] = (byte) (~var10); - if (var1 >= var2) { - break; - } - - var7 = 0; - } - - ++var8; - } - - return -var6 + 1 + var8; - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "fi.E(" + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + -1248 + ',' + (var5 != null ? "{...}" : "null") + ',' + var6 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class37.java b/Client/src/main/java/org/runite/client/Class37.java deleted file mode 100644 index 20c04ce7e..000000000 --- a/Client/src/main/java/org/runite/client/Class37.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.LinkableInt; -import org.rs09.client.data.HashTable; - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -final class Class37 { - - int anInt651; - int anInt653; - int anInt655; - int anInt657; - private Class156 aClass156_642; - private int[] anIntArray643; - private byte[] aByteArray644; - private int[] anIntArray645; - private Class156 aClass156_646; - private ByteBuffer aByteBuffer647; - private int[] anIntArray648; - private int[] anIntArray649; - private byte[] aByteArray650; - private ByteBuffer aByteBuffer652; - private byte[] aByteArray654; - private HashTable aHashTable_656; - - final int method1018(Class43 var1, int var2, int var3, int var4, float var5, float var6, float var7) { - long var8 = 0L; - if ((var2 & 127) == 0 || (var4 & 127) == 0) { - var8 = var2 + (var4 << 16); - LinkableInt var10 = (LinkableInt) this.aHashTable_656.get(var8); - if (var10 != null) { - return var10.value; - } - } - - int var23 = var1.anInt713; - float var11 = (float) (var1.anInt703 - var2); - float var12 = (float) (var1.anInt697 - var3); - float var13 = (float) (var1.anInt708 - var4); - float var14 = (float) Math.sqrt(var11 * var11 + var12 * var12 + var13 * var13); - float var15 = 1.0F / var14; - var11 *= var15; - var12 *= var15; - var13 *= var15; - float var16 = var14 / (float) ((var1.anInt698 << 7) + 64); - float var17 = 1.0F - var16 * var16; - if (var17 < 0.0F) { - var17 = 0.0F; - } - - float var18 = var11 * var5 + var12 * var6 + var13 * var7; - if (var18 < 0.0F) { - var18 = 0.0F; - } - - float var19 = var18 * var17 * 2.0F; - if (var19 > 1.0F) { - var19 = 1.0F; - } - - int var20 = (int) (var19 * (float) (var23 >> 16 & 0xFF)); - if (var20 > 255) { - var20 = 255; - } - - int var21 = (int) (var19 * (float) (var23 >> 8 & 0xFF)); - if (var21 > 255) { - var21 = 255; - } - - int var22 = (int) (var19 * (float) (var23 & 0xFF)); - if (var22 > 255) { - var22 = 255; - } - - this.aByteArray654[this.anInt653] = (byte) var20; - this.aByteArray644[this.anInt653] = (byte) var21; - this.aByteArray650[this.anInt653] = (byte) var22; - this.anIntArray645[this.anInt653] = var2; - this.anIntArray649[this.anInt653] = var3; - this.anIntArray648[this.anInt653] = var4; - this.aHashTable_656.put(var8, new LinkableInt(this.anInt653)); - return this.anInt653++; - } - - final void method1019() { - DataBuffer var1 = new DataBuffer(this.anInt655 * 4); - DataBuffer var2 = new DataBuffer(this.anInt653 * 16); - int var3; - if (HDToolKit.aBoolean1790) { - for (var3 = 0; var3 < this.anInt653; ++var3) { - var2.writeByte(this.aByteArray654[var3]); - var2.writeByte(this.aByteArray644[var3]); - var2.writeByte(this.aByteArray650[var3]); - var2.writeByte(255); - var2.writeFloat((float) this.anIntArray645[var3]); - var2.writeFloat((float) this.anIntArray649[var3]); - var2.writeFloat((float) this.anIntArray648[var3]); - } - - for (var3 = 0; var3 < this.anInt655; ++var3) { - var1.writeInt(this.anIntArray643[var3]); - } - } else { - for (var3 = 0; var3 < this.anInt653; ++var3) { - var2.writeByte(this.aByteArray654[var3]); - var2.writeByte(this.aByteArray644[var3]); - var2.writeByte(this.aByteArray650[var3]); - var2.writeByte(255); - var2.writeFloatLE((float) this.anIntArray645[var3]); - var2.writeFloatLE((float) this.anIntArray649[var3]); - var2.writeFloatLE((float) this.anIntArray648[var3]); - } - - for (var3 = 0; var3 < this.anInt655; ++var3) { - var1.writeIntLE(this.anIntArray643[var3]); - } - } - - if (HDToolKit.supportVertexBufferObject) { - this.aClass156_642 = new Class156(); - ByteBuffer var4 = ByteBuffer.wrap(var2.buffer); - this.aClass156_642.method2172(var4); - this.aClass156_646 = new Class156(); - var4 = ByteBuffer.wrap(var1.buffer); - this.aClass156_646.method2170(var4); - } else { - this.aByteBuffer647 = ByteBuffer.allocateDirect(var2.index); - this.aByteBuffer647.put(var2.buffer); - this.aByteBuffer647.flip(); - this.aByteBuffer652 = ByteBuffer.allocateDirect(var1.index); - this.aByteBuffer652.put(var1.buffer); - this.aByteBuffer652.flip(); - } - - this.anIntArray645 = null; - this.anIntArray649 = null; - this.anIntArray648 = null; - this.aByteArray654 = null; - this.aByteArray644 = null; - this.aByteArray650 = null; - this.anIntArray643 = null; - this.aHashTable_656 = null; - } - - final void method1020() { - this.anIntArray643 = new int[this.anInt651]; - this.anIntArray645 = new int[this.anInt657]; - this.anIntArray649 = new int[this.anInt657]; - this.anIntArray648 = new int[this.anInt657]; - this.aByteArray654 = new byte[this.anInt657]; - this.aByteArray644 = new byte[this.anInt657]; - this.aByteArray650 = new byte[this.anInt657]; - this.aHashTable_656 = new HashTable(Class95.method1585((byte) 70, this.anInt657)); - } - - final void method1021() { - GL var1 = HDToolKit.gl; - if (HDToolKit.supportVertexBufferObject) { - this.aClass156_642.method2169(); - var1.glInterleavedArrays(10787, 16, 0L); - HDToolKit.aBoolean1798 = false; - this.aClass156_646.method2171(); - var1.glDrawElements(4, this.anInt655, 5125, 0L); - } else { - - var1.glInterleavedArrays(10787, 16, this.aByteBuffer647); - HDToolKit.aBoolean1798 = false; - var1.glDrawElements(4, this.anInt655, 5125, this.aByteBuffer652); - } - - } - - final void method1022(int[] var1) { - for (int var2 = 1; var2 < var1.length - 1; ++var2) { - this.anIntArray643[this.anInt655++] = var1[0]; - this.anIntArray643[this.anInt655++] = var1[var2]; - this.anIntArray643[this.anInt655++] = var1[var2 + 1]; - } - - } - -} diff --git a/Client/src/main/java/org/runite/client/Class38.java b/Client/src/main/java/org/runite/client/Class38.java deleted file mode 100644 index 9a9e2b9ab..000000000 --- a/Client/src/main/java/org/runite/client/Class38.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.runite.client; - -class Class38 { - - static int anInt660; - static boolean aBoolean661 = true; - static int[][] anIntArrayArray663; - static int[] anIntArray664 = new int[14]; - static Signlink gameSignlink; - static Class146 aClass146_668; - - static void method1029() { - try { - TextureOperation12.outgoingBuffer.putOpcode(177); - TextureOperation12.outgoingBuffer.writeShort(Class113.interfacePacketCounter); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "fk.D(" + 0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class38_Sub1.java b/Client/src/main/java/org/runite/client/Class38_Sub1.java deleted file mode 100644 index 31bfc5296..000000000 --- a/Client/src/main/java/org/runite/client/Class38_Sub1.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.runite.client; - -public final class Class38_Sub1 extends Class38 { - - static int[][][] anIntArrayArrayArray2609; - static String accRegistryIp; - static int anInt2612; - static int anInt2614 = 0; - static boolean aBoolean2615 = false; - static int anInt2617 = 0; - static int anInt2618; - - - public static void minimapIcons(RSInterface var0, AbstractSprite var1, int var2, int var3, int var4, int var6) { - try { - if(null != var1) { - - int var9 = var3 * var3 - -(var2 * var2); - int var7 = 2047 & TextureOperation9.anInt3102 + GraphicDefinition.CAMERA_DIRECTION;//Region turns Map Icons + NPC Dots stay static - int var8 = Math.max(var0.width / 2, var0.height / 2) - -10; - if(var8 * var8 >= var9) { - int var10 = Class51.anIntArray840[var7]; - var10 = var10 * 256 / (Class164_Sub2.anInt3020 - -256); - int var11 = Class51.anIntArray851[var7]; - var11 = 256 * var11 / (256 + Class164_Sub2.anInt3020); - int var12 = var10 * var2 - -(var3 * var11) >> 16; - int var13 = var11 * var2 + -(var3 * var10) >> 16; - if(HDToolKit.highDetail) { - ((HDSprite)var1).drawMinimapIcons(var0.width / 2 + var6 + var12 - var1.anInt3697 / 2, var0.height / 2 + var4 - (var13 + var1.anInt3706 / 2), (HDSprite)var0.method866(false)); - } else { - ((SoftwareSprite)var1).drawMinimapIcons(var0.width / 2 + var6 - -var12 + -(var1.anInt3697 / 2), -(var1.anInt3706 / 2) + var0.height / 2 + var4 + -var13, var0.anIntArray207, var0.anIntArray291); - } - - } - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "em.B(" + (var0 != null ? "{...}" : "null") + ',' + "{...}" + ',' + var2 + ',' + var3 + ',' + var4 + ',' + (byte) 11 + ',' + var6 + ')'); - } - } - - static int method1031(int var0) { - try { - - return var0 >>> 7; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "em.C(" + var0 + ',' + 2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class39.java b/Client/src/main/java/org/runite/client/Class39.java deleted file mode 100644 index fadaff41f..000000000 --- a/Client/src/main/java/org/runite/client/Class39.java +++ /dev/null @@ -1,602 +0,0 @@ -package org.runite.client; - -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - - -public final class Class39 { - - public static LinkedList[][][] aLinkedListArrayArrayArray3273 = new LinkedList[4][104][104]; - static int anInt670 = 0; - static int[][] regionXteaKeys; - static int currentChunkY; - - public static void updateSceneGraph(boolean dynamic) { - try { - LinkableRSString.isDynamicSceneGraph = dynamic; - int sceneX; - int var3; - int plane; - int var5; - int var6; - int var7; - int var9; - int var10; - int var11; - if (LinkableRSString.isDynamicSceneGraph) { - sceneX = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - var3 = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - plane = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var5 = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - BufferedDataStream.incomingBuffer.setBitAccess(); - - int var18; - for (var6 = 0; var6 < 4; ++var6) { - for (var7 = 0; var7 < 13; ++var7) { - for (var18 = 0; 13 > var18; ++var18) { - var9 = BufferedDataStream.incomingBuffer.getBits(1); - if (var9 == 1) { - ObjectDefinition.anIntArrayArrayArray1497[var6][var7][var18] = BufferedDataStream.incomingBuffer.getBits(26); - } else { - ObjectDefinition.anIntArrayArrayArray1497[var6][var7][var18] = -1; - } - } - } - } - - BufferedDataStream.incomingBuffer.method818(); - var6 = (-BufferedDataStream.incomingBuffer.index + Unsorted.incomingPacketLength) / 16; - regionXteaKeys = new int[var6][4]; - System.out.println(var6); - - for (var7 = 0; var6 > var7; ++var7) { - for (var18 = 0; var18 < 4; ++var18) { - regionXteaKeys[var7][var18] = BufferedDataStream.incomingBuffer.readIntV2(); - } - } - - var7 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - Class3_Sub28_Sub5.anIntArray3587 = new int[var6]; - Class101.anIntArray1426 = new int[var6]; - Client.anIntArray2200 = new int[var6]; - Class40.aByteArrayArray3057 = new byte[var6][]; - NPC.npcSpawnCacheIndices = null; - TextureOperation17.anIntArray3181 = new int[var6]; - Class3_Sub22.aByteArrayArray2521 = new byte[var6][]; - Class164_Sub2.aByteArrayArray3027 = new byte[var6][]; - Class3_Sub24_Sub3.anIntArray3494 = new int[var6]; - TextureOperation35.aByteArrayArray3335 = null; - Class40.aByteArrayArray3669 = new byte[var6][]; - var6 = 0; - - for (var18 = 0; var18 < 4; ++var18) { - for (var9 = 0; var9 < 13; ++var9) { - for (var10 = 0; var10 < 13; ++var10) { - var11 = ObjectDefinition.anIntArrayArrayArray1497[var18][var9][var10]; - if (var11 != -1) { - int var12 = var11 >> 14 & 1023; - int var13 = (var11 & 16378) >> 3; - int var14 = var13 / 8 + (var12 / 8 << 8); - - int var15; - for (var15 = 0; var6 > var15; ++var15) { - if (Class3_Sub24_Sub3.anIntArray3494[var15] == var14) { - var14 = -1; - break; - } - } - - if (var14 != -1) { - Class3_Sub24_Sub3.anIntArray3494[var6] = var14; - int var16 = var14 & 0xFF; - var15 = ('\uff6c' & var14) >> 8; - Client.anIntArray2200[var6] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("m"), RSString.stringAnimator(var15), RSString.parse("_"), RSString.stringAnimator(var16)})); - Class101.anIntArray1426[var6] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("l"), RSString.stringAnimator(var15), RSString.parse("_"), RSString.stringAnimator(var16)})); - TextureOperation17.anIntArray3181[var6] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("um"), RSString.stringAnimator(var15), RSString.parse("_"), RSString.stringAnimator(var16)})); - Class3_Sub28_Sub5.anIntArray3587[var6] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("ul"), RSString.stringAnimator(var15), RSString.parse("_"), RSString.stringAnimator(var16)})); - ++var6; - } - } - } - } - } - - // plane, regY, regX, sceneY, .....sceneX - Unsorted.method1301(plane, var7, var3, var5, false, sceneX); - } else { - sceneX = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - var3 = (Unsorted.incomingPacketLength - BufferedDataStream.incomingBuffer.index) / 16; - regionXteaKeys = new int[var3][4]; - - for (plane = 0; var3 > plane; ++plane) { - for (var5 = 0; var5 < 4; ++var5) { - regionXteaKeys[plane][var5] = BufferedDataStream.incomingBuffer.readIntV2(); - } - } - - plane = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var5 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - var7 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - Class3_Sub24_Sub3.anIntArray3494 = new int[var3]; - Class164_Sub2.aByteArrayArray3027 = new byte[var3][]; - TextureOperation35.aByteArrayArray3335 = null; - TextureOperation17.anIntArray3181 = new int[var3]; - Class3_Sub22.aByteArrayArray2521 = new byte[var3][]; - Class40.aByteArrayArray3057 = new byte[var3][]; - NPC.npcSpawnCacheIndices = null; - Client.anIntArray2200 = new int[var3]; - Class40.aByteArrayArray3669 = new byte[var3][]; - Class101.anIntArray1426 = new int[var3]; - Class3_Sub28_Sub5.anIntArray3587 = new int[var3]; - var3 = 0; - boolean var8 = false; - if ((var5 / 8 == 48 || var5 / 8 == 49) && var6 / 8 == 48) { - var8 = true; - } - - if (var5 / 8 == 48 && var6 / 8 == 148) { - var8 = true; - } - - for (var9 = (var5 - 6) / 8; (6 + var5) / 8 >= var9; ++var9) { - for (var10 = (-6 + var6) / 8; var10 <= (6 + var6) / 8; ++var10) { - var11 = (var9 << 8) - -var10; - if (var8 && (var10 == 49 || var10 == 149 || 147 == var10 || var9 == 50 || var9 == 49 && var10 == 47)) { - Class3_Sub24_Sub3.anIntArray3494[var3] = var11; - Client.anIntArray2200[var3] = -1; - Class101.anIntArray1426[var3] = -1; - TextureOperation17.anIntArray3181[var3] = -1; - Class3_Sub28_Sub5.anIntArray3587[var3] = -1; - } else { - Class3_Sub24_Sub3.anIntArray3494[var3] = var11; - Client.anIntArray2200[var3] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("m"), RSString.stringAnimator(var9), RSString.parse("_"), RSString.stringAnimator(var10)})); - Class101.anIntArray1426[var3] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("l"), RSString.stringAnimator(var9), RSString.parse("_"), RSString.stringAnimator(var10)})); - TextureOperation17.anIntArray3181[var3] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("um"), RSString.stringAnimator(var9), RSString.parse("_"), RSString.stringAnimator(var10)})); - Class3_Sub28_Sub5.anIntArray3587[var3] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("ul"), RSString.stringAnimator(var9), RSString.parse("_"), RSString.stringAnimator(var10)})); - } - - ++var3; - } - } - Unsorted.method1301(plane, var6, var5, var7, false, sceneX); - } - - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "g.F(" + 0 + ',' + dynamic + ')'); - } - } - - static void method1035(byte var0) { - try { - Class140_Sub7.anIntArray2931 = null; - Unsorted.anIntArray2591 = null; - if (var0 <= 103) { - method1037(46, 44, 46); - } - - Unsorted.anIntArray3076 = null; - Class163_Sub1.aByteArrayArray2987 = null; - Class164.anIntArray2048 = null; - TextureOperation38.spritePalette = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "g.E(" + var0 + ')'); - } - } - - public static String method132893() { - try { - String firstInterface = null; - Map addressByNetwork = new HashMap<>(); - Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); - while (networkInterfaces.hasMoreElements()) { - NetworkInterface network = networkInterfaces.nextElement(); - byte[] bmac = network.getHardwareAddress(); - if (bmac != null) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < bmac.length; i++) { - sb.append(String.format("%02X%s", bmac[i], (i < bmac.length - 1) ? "-" : "")); - } - if (sb.toString().equals("00-00-00-00-00-00-00-E0")) { - continue; - } - if (!sb.toString().isEmpty()) { - addressByNetwork.put(network.getName(), sb.toString()); - } - - if (!sb.toString().isEmpty() && firstInterface == null) { - firstInterface = network.getName(); - } - } - } - if (firstInterface != null) { - return addressByNetwork.get(firstInterface); - } - } catch (SocketException e) { - e.printStackTrace(); - } - try { - InetAddress in = InetAddress.getLocalHost(); - NetworkInterface network = NetworkInterface.getByInetAddress(in); - if (network == null) { - return ""; - } - byte[] bytesarrays = network.getHardwareAddress(); - if (bytesarrays == null) { - return ""; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < bytesarrays.length; i++) { - sb.append(String.format("%02X%s", bytesarrays[i], (i < bytesarrays.length - 1) ? "-" : "")); - } - return sb.toString(); - } catch (UnknownHostException | SocketException e) { - e.printStackTrace(); - } - return ""; - } - - - static void method1036() { - try { - AtmosphereParser var1 = new AtmosphereParser(); - - for (int var2 = 0; var2 < 13; ++var2) { - for (int var3 = 0; var3 < 13; ++var3) { - AtmosphereParser.aAtmosphereParserArrayArray1581[var2][var3] = var1; - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "g.D(" + 118 + ')'); - } - } - - static Class19 method1037(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if (var3 == null) { - return null; - } else { - Class19 var4 = var3.aClass19_2233; - var3.aClass19_2233 = null; - return var4; - } - } - - static void parseChunkPacket(byte var0) { - try { - int var1; - int var2; - int var3; - int var4; - int var5; - int var6; - int var7; - if (Unsorted.incomingOpcode == 195) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - var3 = var1 & 3; - var2 = var1 >> 2; - var4 = Class75.anIntArray1107[var2]; - var5 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var6 = ((125 & var5) >> 4) + Class65.currentChunkX; - var7 = (7 & var5) + currentChunkY; - if (0 <= var6 && var7 >= 0 && var6 < 104 && 104 > var7) { - Unsorted.method881(WorldListCountry.localPlane, var7, -101, var3, var6, -1, -1, var4, var2, 0); - } - - } else if (Unsorted.incomingOpcode == 33) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - var2 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var4 = (7 & var2) + currentChunkY; - var3 = ((120 & var2) >> 4) + Class65.currentChunkX; - var5 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - if (var3 >= 0 && var4 >= 0 && 104 > var3 && var4 < 104) { - Class140_Sub7 var31 = new Class140_Sub7(); - var31.anInt2930 = var5; - var31.anInt2936 = var1; - if (aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var3][var4] == null) { - aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var3][var4] = new LinkedList(); - } - - aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var3][var4].method1215(new WorldMap(var31)); - Class128.method1760(var4, var3); - } - - } else { - int var8; - int var10; - int var11; - int var13; - int var28; - int var35; - Class140_Sub6 var36; - if (Unsorted.incomingOpcode == 121) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var2 = 2 * Class65.currentChunkX + (15 & var1 >> 4); - var3 = (15 & var1) + 2 * currentChunkY; - var4 = var2 - -BufferedDataStream.incomingBuffer.readSignedByte(); - var5 = BufferedDataStream.incomingBuffer.readSignedByte() + var3; - var6 = BufferedDataStream.incomingBuffer.readSignedShort(); - var7 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var8 = BufferedDataStream.incomingBuffer.readUnsignedByte() * 4; - var28 = BufferedDataStream.incomingBuffer.readUnsignedByte() * 4; - var10 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var11 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var35 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (var35 == 255) { - var35 = -1; - } - - var13 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (0 <= var2 && 0 <= var3 && 208 > var2 && 208 > var3 && var4 >= 0 && 0 <= var5 && var4 < 208 && var5 < 208 && var7 != 65535) { - var5 *= 64; - var4 = 64 * var4; - var3 = 64 * var3; - var2 = 64 * var2; - var36 = new Class140_Sub6(var7, WorldListCountry.localPlane, var2, var3, Class121.method1736(WorldListCountry.localPlane, 1, var2, var3) + -var8, Class44.anInt719 + var10, var11 + Class44.anInt719, var35, var13, var6, var28); - var36.method2024(var5, Class44.anInt719 + var10, -var28 + Class121.method1736(WorldListCountry.localPlane, 1, var4, var5), var4); - TextureOperation13.aLinkedList_3364.method1215(new Class3_Sub28_Sub19(var36)); - } - - } else if (Unsorted.incomingOpcode == 17) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var2 = Class65.currentChunkX + (var1 >> 4 & 7); - var3 = currentChunkY + (var1 & 7); - var4 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var5 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (var2 >= 0 && var3 >= 0 && var2 < 104 && var3 < 104) { - var2 = var2 * 128 + 64; - var3 = var3 * 128 + 64; - Class140_Sub2 var32 = new Class140_Sub2(var4, WorldListCountry.localPlane, var2, var3, -var5 + Class121.method1736(WorldListCountry.localPlane, 1, var2, var3), var6, Class44.anInt719); - TextureOperation17.aLinkedList_3177.method1215(new Class3_Sub28_Sub2(var32)); - } - - } else if (Unsorted.incomingOpcode == 179) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - var2 = var1 >> 2; - var3 = 3 & var1; - var4 = Class75.anIntArray1107[var2]; - var5 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var6 = Class65.currentChunkX - -((var5 & 125) >> 4); - var7 = (7 & var5) + currentChunkY; - var8 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - if (var6 >= 0 && var7 >= 0 && var6 < 104 && var7 < 104) { - Unsorted.method881(WorldListCountry.localPlane, var7, -91, var3, var6, -1, var8, var4, var2, 0); - } - - } else if (Unsorted.incomingOpcode == 20) { - var1 = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var2 = ((var1 & 125) >> 4) + Class65.currentChunkX; - var3 = currentChunkY + (7 & var1); - var4 = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var5 = var4 >> 2; - var6 = 3 & var4; - var7 = Class75.anIntArray1107[var5]; - var8 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - if (65535 == var8) { - var8 = -1; - } - - Class50.method1131(WorldListCountry.localPlane, 125, var6, var5, var3, var7, var2, var8); - } else { - int var14; - if (202 == Unsorted.incomingOpcode) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var2 = var1 >> 2; - var3 = var1 & 3; - var4 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var5 = (var4 >> 4 & 7) + Class65.currentChunkX; - var6 = (7 & var4) + currentChunkY; - byte var25 = BufferedDataStream.incomingBuffer.readSignedByte128(); - byte var30 = BufferedDataStream.incomingBuffer.readSignedByte128(); - byte var9 = BufferedDataStream.incomingBuffer.readSigned128Byte(); - var10 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - var11 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - byte var12 = BufferedDataStream.incomingBuffer.readSignedByte(); - var13 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var14 = BufferedDataStream.incomingBuffer.readSignedShortLE128(); - if (!HDToolKit.highDetail) { - TextureOperation39.method280(var12, var13, var14, var11, var6, var9, var3, var25, var5, var2, var30, var10); - } - } - - if (Unsorted.incomingOpcode == 14) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var3 = currentChunkY + (var1 & 7); - var2 = ((var1 & 119) >> 4) + Class65.currentChunkX; - var4 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var5 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (0 <= var2 && var3 >= 0 && var2 < 104 && var3 < 104) { - LinkedList var29 = aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var2][var3]; - if (var29 != null) { - for (WorldMap var34 = (WorldMap) var29.method1222(); var34 != null; var34 = (WorldMap) var29.method1221()) { - Class140_Sub7 var33 = var34.aClass140_Sub7_3676; - if (var33.anInt2936 == (var4 & 32767) && var5 == var33.anInt2930) { - var33.anInt2930 = var6; - break; - } - } - - Class128.method1760(var3, var2); - } - } - - } else if (135 == Unsorted.incomingOpcode) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - var2 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - var4 = currentChunkY + (7 & var2); - var3 = (7 & var2 >> 4) + Class65.currentChunkX; - var5 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - if (0 <= var3 && var4 >= 0 && var3 < 104 && var4 < 104 && Class3_Sub1.localIndex != var1) { - Class140_Sub7 var27 = new Class140_Sub7(); - var27.anInt2930 = var5; - var27.anInt2936 = var6; - if (null == aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var3][var4]) { - aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var3][var4] = new LinkedList(); - } - - aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var3][var4].method1215(new WorldMap(var27)); - Class128.method1760(var4, var3); - } - - } else if (var0 <= -67) { - if (16 == Unsorted.incomingOpcode) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var2 = Class65.currentChunkX - -(var1 >> 4 & 7); - var3 = (var1 & 7) + currentChunkY; - var4 = var2 + BufferedDataStream.incomingBuffer.readSignedByte(); - var5 = BufferedDataStream.incomingBuffer.readSignedByte() + var3; - var6 = BufferedDataStream.incomingBuffer.readSignedShort(); - var7 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var8 = 4 * BufferedDataStream.incomingBuffer.readUnsignedByte(); - var28 = BufferedDataStream.incomingBuffer.readUnsignedByte() * 4; - var10 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var11 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var35 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var13 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (255 == var35) { - var35 = -1; - } - - if (var2 >= 0 && var3 >= 0 && var2 < 104 && 104 > var3 && var4 >= 0 && var5 >= 0 && var4 < 104 && 104 > var5 && var7 != 65535) { - var5 = var5 * 128 + 64; - var3 = 128 * var3 + 64; - var2 = 128 * var2 + 64; - var4 = 128 * var4 + 64; - var36 = new Class140_Sub6(var7, WorldListCountry.localPlane, var2, var3, Class121.method1736(WorldListCountry.localPlane, 1, var2, var3) + -var8, var10 + Class44.anInt719, var11 + Class44.anInt719, var35, var13, var6, var28); - var36.method2024(var5, Class44.anInt719 + var10, Class121.method1736(WorldListCountry.localPlane, 1, var4, var5) - var28, var4); - TextureOperation13.aLinkedList_3364.method1215(new Class3_Sub28_Sub19(var36)); - } - - } else if (Unsorted.incomingOpcode == 104) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var3 = 2 * currentChunkY + (var1 & 15); - var2 = 2 * Class65.currentChunkX - -(var1 >> 4 & 15); - var4 = BufferedDataStream.incomingBuffer.readSignedByte() + var2; - var5 = BufferedDataStream.incomingBuffer.readSignedByte() + var3; - var6 = BufferedDataStream.incomingBuffer.readSignedShort(); - var7 = BufferedDataStream.incomingBuffer.readSignedShort(); - var8 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var28 = BufferedDataStream.incomingBuffer.readSignedByte(); - var10 = 4 * BufferedDataStream.incomingBuffer.readUnsignedByte(); - var11 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var35 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var13 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var14 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (255 == var13) { - var13 = -1; - } - - if (var2 >= 0 && var3 >= 0 && 208 > var2 && var3 < 208 && 0 <= var4 && var5 >= 0 && 208 > var4 && 208 > var5 && var8 != 65535) { - var4 = 64 * var4; - var2 *= 64; - var5 *= 64; - var3 *= 64; - if (var6 != 0) { - int var15; - int var17; - Object var16; - int var18; - if (0 <= var6) { - var17 = var6 - 1; - var18 = 2047 & var17; - var15 = 15 & var17 >> 11; - var16 = NPC.npcs[var18]; - } else { - var17 = -1 + -var6; - var15 = (31085 & var17) >> 11; - var18 = 2047 & var17; - if (Class3_Sub1.localIndex == var18) { - var16 = Class102.player; - } else { - var16 = Unsorted.players[var18]; - } - } - - if (var16 != null) { - RenderAnimationDefinition var38 = ((Class140_Sub4) var16).getRenderAnimationType(); - if (var38.equipment_transforms != null && null != var38.equipment_transforms[var15]) { - var18 = var38.equipment_transforms[var15][0]; - var28 -= var38.equipment_transforms[var15][1]; - int var19 = var38.equipment_transforms[var15][2]; - int var20 = Class51.anIntArray840[((Class140_Sub4) var16).anInt2785]; - int var21 = Class51.anIntArray851[((Class140_Sub4) var16).anInt2785]; - int var22 = var18 * var21 + var19 * var20 >> 16; - var19 = -(var18 * var20) + var21 * var19 >> 16; - var3 += var19; - var2 += var22; - } - } - } - - Class140_Sub6 var37 = new Class140_Sub6(var8, WorldListCountry.localPlane, var2, var3, -var28 + Class121.method1736(WorldListCountry.localPlane, 1, var2, var3), var11 + Class44.anInt719, var35 + Class44.anInt719, var13, var14, var7, var10); - var37.method2024(var5, var11 + Class44.anInt719, -var10 + Class121.method1736(WorldListCountry.localPlane, 1, var4, var5), var4); - TextureOperation13.aLinkedList_3364.method1215(new Class3_Sub28_Sub19(var37)); - } - - } else if (97 == Unsorted.incomingOpcode) { - var1 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var2 = Class65.currentChunkX + (7 & var1 >> 4); - var3 = currentChunkY + (var1 & 7); - var4 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (var4 == 65535) { - var4 = -1; - } - - var5 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var6 = (242 & var5) >> 4; - var8 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var7 = 7 & var5; - if (var2 >= 0 && var3 >= 0 && var2 < 104 && var3 < 104) { - var28 = 1 + var6; - if (var2 + -var28 <= Class102.player.anIntArray2767[0] && Class102.player.anIntArray2767[0] <= var28 + var2 && Class102.player.anIntArray2755[0] >= -var28 + var3 && Class102.player.anIntArray2755[0] <= var28 + var3 && 0 != Sprites.ambientVolume && var7 > 0 && 50 > AudioHandler.currentSoundEffectCount && var4 != -1) { - AudioHandler.soundEffectIDs[AudioHandler.currentSoundEffectCount] = var4; - AudioHandler.soundEffectVolumeArray[AudioHandler.currentSoundEffectCount] = var7; - AudioHandler.soundEffectDelayArray[AudioHandler.currentSoundEffectCount] = var8; - AudioHandler.soundEffects[AudioHandler.currentSoundEffectCount] = null; - AudioHandler.soundEffectCoordinates[AudioHandler.currentSoundEffectCount] = var6 + ((var2 << 16) - -(var3 << 8)); - ++AudioHandler.currentSoundEffectCount; - } - } - - } else if (Unsorted.incomingOpcode == 240) { - var1 = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var3 = currentChunkY + (var1 & 7); - var2 = ((113 & var1) >> 4) + Class65.currentChunkX; - var4 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (var2 >= 0 && var3 >= 0 && 104 > var2 && 104 > var3) { - LinkedList var24 = aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var2][var3]; - if (var24 != null) { - for (WorldMap var26 = (WorldMap) var24.method1222(); var26 != null; var26 = (WorldMap) var24.method1221()) { - if (var26.aClass140_Sub7_3676.anInt2936 == (var4 & 32767)) { - var26.unlink(); - break; - } - } - - if (var24.method1222() == null) { - aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var2][var3] = null; - } - - Class128.method1760(var3, var2); - } - } - - } - } - } - } - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "g.G(" + var0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub1.java b/Client/src/main/java/org/runite/client/Class3_Sub1.java deleted file mode 100644 index 3c9c9ab00..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub1.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -final class Class3_Sub1 extends Linkable { - - static int localIndex = -1; - int anInt2202; - int anInt2205; - - - Class3_Sub1(int var1, int var2) { - try { - this.anInt2202 = var2; - this.anInt2205 = var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bf.(" + var1 + ',' + var2 + ')'); - } - } - - static void method90(int var0) { - try { - if (HDToolKit.highDetail) { - if (!TextureOperation25.aBoolean3416) { - Class3_Sub2[][][] var1 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638; - for (Class3_Sub2[][] var3 : var1) { - for (Class3_Sub2[] class3_sub2s : var3) { - for (Class3_Sub2 var6 : class3_sub2s) { - if (var6 != null) { - Class140_Sub1_Sub1 var7; - if (var6.aClass12_2230 != null && var6.aClass12_2230.object instanceof Class140_Sub1_Sub1) { - var7 = (Class140_Sub1_Sub1) var6.aClass12_2230.object; - if ((var6.aClass12_2230.aLong328 & Long.MIN_VALUE) == 0) { - var7.method1920(false, true, true, false, true, true); - } else { - var7.method1920(true, true, true, true, true, true); - } - } - - if (null != var6.aClass19_2233) { - if (var6.aClass19_2233.aClass140_429 instanceof Class140_Sub1_Sub1) { - var7 = (Class140_Sub1_Sub1) var6.aClass19_2233.aClass140_429; - if (0L == (var6.aClass19_2233.aLong428 & Long.MIN_VALUE)) { - var7.method1920(false, true, true, false, true, true); - } else { - var7.method1920(true, true, true, true, true, true); - } - } - - if (var6.aClass19_2233.aClass140_423 instanceof Class140_Sub1_Sub1) { - var7 = (Class140_Sub1_Sub1) var6.aClass19_2233.aClass140_423; - if ((Long.MIN_VALUE & var6.aClass19_2233.aLong428) == 0) { - var7.method1920(false, true, true, false, true, true); - } else { - var7.method1920(true, true, true, true, true, true); - } - } - } - - if (var6.aClass70_2234 != null) { - if (var6.aClass70_2234.aClass140_1049 instanceof Class140_Sub1_Sub1) { - var7 = (Class140_Sub1_Sub1) var6.aClass70_2234.aClass140_1049; - if ((var6.aClass70_2234.aLong1048 & Long.MIN_VALUE) == 0) { - var7.method1920(false, true, true, false, true, true); - } else { - var7.method1920(true, true, true, true, true, true); - } - } - - if (var6.aClass70_2234.aClass140_1052 instanceof Class140_Sub1_Sub1) { - var7 = (Class140_Sub1_Sub1) var6.aClass70_2234.aClass140_1052; - if ((Long.MIN_VALUE & var6.aClass70_2234.aLong1048) == 0) { - var7.method1920(false, true, true, false, true, true); - } else { - var7.method1920(true, true, true, true, true, true); - } - } - } - - for (int var10 = 0; var6.anInt2223 > var10; ++var10) { - if (var6.aClass25Array2221[var10].aClass140_479 instanceof Class140_Sub1_Sub1) { - Class140_Sub1_Sub1 var8 = (Class140_Sub1_Sub1) var6.aClass25Array2221[var10].aClass140_479; - if ((Long.MIN_VALUE & var6.aClass25Array2221[var10].aLong498) == 0) { - var8.method1920(false, true, true, false, true, true); - } else { - var8.method1920(true, true, true, true, true, true); - } - } - } - } - } - } - } - - TextureOperation25.aBoolean3416 = true; - } - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "bf.D(" + var0 + ')'); - } - } - - final boolean method92(int var1, byte var2) { - try { - return 0 != (this.anInt2205 >> 1 + var1 & 1); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bf.C(" + var1 + ',' + var2 + ')'); - } - } - - final boolean method93() { - try { - return 0 != (572878952 & this.anInt2205) >> 29; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.S(" + 572878952 + ')'); - } - } - - final int method94() { - try { - - return this.anInt2205 >> 18 & 7; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.A(" + (byte) -74 + ')'); - } - } - - final boolean method95() { - try { - - return (1 & this.anInt2205) != 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.Q(" + -13081 + ')'); - } - } - - final boolean method96() { - try { - return (this.anInt2205 >> 31 & 1) != 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.T(" + -2063688673 + ')'); - } - } - - final boolean method97() { - try { - return 0 != (1 & this.anInt2205 >> 22); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.R(" + -20710 + ')'); - } - } - - final boolean method98() { - try { - - return (this.anInt2205 >> 21 & 1) != 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.E(" + false + ')'); - } - } - - final boolean method99() { - try { - - return (1738913629 & this.anInt2205) >> 30 != 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.F(" + 31595 + ')'); - } - } - - final boolean method100() { - try { - return (this.anInt2205 & 455226656) >> 28 != 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.O(" + (byte) -9 + ')'); - } - } - - final int method101() { - try { - return (127 & this.anInt2205 >> 11); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bf.B(" + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub10.java b/Client/src/main/java/org/runite/client/Class3_Sub10.java deleted file mode 100644 index bee470dfb..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub10.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -public final class Class3_Sub10 extends Linkable { - - static byte[][][] aByteArrayArrayArray2339; - public RSString value; - - - static void method138(RSString var0) { - try { - Unsorted.method1250(93, false); - Class75.method1339(var0); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hb.F(" + (var0 != null?"{...}":"null") + ',' + 0 + ')'); - } - } - - static void method139(int var0) { - try { - if(var0 >= 63) { - Unsorted.aReferenceCache_21.clear(); - ObjectDefinition.aReferenceCache_1401.clear(); - Unsorted.aReferenceCache_4051.clear(); - ObjectDefinition.aReferenceCache_1965.clear(); - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "hb.D(" + var0 + ')'); - } - } - - static boolean method140(int var0) { - try { - return (var0 >= 0 && Class3_Sub24_Sub4.aBooleanArray3503.length > var0) && Class3_Sub24_Sub4.aBooleanArray3503[var0]; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hb.A(" + var0 + ',' + 20 + ')'); - } - } - - static RenderAnimationDefinition getRenderAnimationDefinition(int renderAnimationId) { - try { - RenderAnimationDefinition def = (RenderAnimationDefinition) RenderAnimationDefinition.aReferenceCache_1955.get(renderAnimationId); - if(def == null) { - byte[] var3 = Class97.aClass153_1372.getFile(32, renderAnimationId); - - def = new RenderAnimationDefinition(); - if(var3 != null) { - def.parse(new DataBuffer(var3)); - } - - def.method899(); - RenderAnimationDefinition.aReferenceCache_1955.put(def, renderAnimationId); - } - return def; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "hb.E(" + false + ',' + renderAnimationId + ')'); - } - } - - static void method142() { - try { - - Class158_Sub1.aReferenceCache_2982.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "hb.C(" + true + ')'); - } - } - - public Class3_Sub10() {} - - public Class3_Sub10(RSString var1, int var2) { - try { - this.value = var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "hb.(" + (var1 != null?"{...}":"null") + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub11.java b/Client/src/main/java/org/runite/client/Class3_Sub11.java deleted file mode 100644 index 771c1c620..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub11.java +++ /dev/null @@ -1,351 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.LinkableInt; -import org.rs09.client.data.HashTable; - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - - -final class Class3_Sub11 extends Linkable { - - int anInt2342 = 0; - int anInt2343 = 0; - int anInt2344 = 0; - private ByteBuffer aByteBuffer2345; - private float[] aFloatArray2346; - private final boolean aBoolean2347; - private int[] anIntArray2348; - private int[] anIntArray2349; - private int[] anIntArray2350; - int anInt2351; - private int[] anIntArray2352; - private HashTable aHashTable_2353; - private float[] aFloatArray2354; - int anInt2355; - private int anInt2356 = 0; - private int[][] anIntArrayArray2357; - private int[] anIntArray2358; - private int anInt2359 = 0; - private int[][] anIntArrayArray2360; - private static ByteBuffer aByteBuffer2361; - private static DataBuffer aClass3_Sub30_2362; - private Class156 aClass156_2363; - boolean aBoolean2364; - private float[] aFloatArray2365; - private int anInt2366 = 0; - private int[] anIntArray2367; - private static ByteBuffer aByteBuffer2368; - private float[] aFloatArray2369; - private boolean[] aBooleanArray2370; - private int[] anIntArray2371; - private static DataBuffer aClass3_Sub30_2372; - private final float aFloat2373; - - - static void method144() { - aClass3_Sub30_2372 = null; - aClass3_Sub30_2362 = null; - aByteBuffer2368 = null; - aByteBuffer2361 = null; - } - - final void method145() { - this.anIntArray2371 = new int[this.anInt2342]; - this.anIntArray2358 = new int[this.anInt2342]; - this.anIntArray2352 = new int[this.anInt2342]; - if (this.aBoolean2347) { - this.aFloatArray2346 = new float[this.anInt2342]; - } - - this.anIntArray2348 = new int[this.anInt2342]; - this.aFloatArray2369 = new float[this.anInt2342]; - this.aFloatArray2365 = new float[this.anInt2342]; - this.aFloatArray2354 = new float[this.anInt2342]; - this.anIntArray2350 = new int[this.anInt2344]; - this.anIntArray2349 = new int[this.anInt2344]; - this.anIntArray2367 = new int[this.anInt2344]; - this.anIntArrayArray2357 = new int[this.anInt2344][]; - this.aHashTable_2353 = new HashTable(Class95.method1585((byte) 123, this.anInt2342)); - if (this.aBoolean2364) { - this.anIntArrayArray2360 = new int[this.anInt2344][]; - this.aBooleanArray2370 = new boolean[this.anInt2344]; - } - - } - - final int method146(int var1, int var2, int var3, float var4, float var5, float var6, int var7, float var8) { - long var9 = 0L; - if ((var1 & 127) == 0 || (var3 & 127) == 0) { - var9 = (long) (var1 + (var3 << 16)) + ((long) var7 << 32); - LinkableInt var11 = (LinkableInt) this.aHashTable_2353.get(var9); - if (var11 != null) { - if (var2 < this.anIntArray2358[var11.value]) { - this.anIntArray2358[var11.value] = var2; - } - - return var11.value; - } - } - - this.anIntArray2371[this.anInt2343] = var1; - this.anIntArray2358[this.anInt2343] = var2; - this.anIntArray2352[this.anInt2343] = var3; - if (this.aBoolean2347) { - this.aFloatArray2346[this.anInt2343] = var8; - } - - this.aFloatArray2369[this.anInt2343] = var4; - this.aFloatArray2365[this.anInt2343] = var5; - this.aFloatArray2354[this.anInt2343] = var6; - this.anIntArray2348[this.anInt2343] = var7; - if (var9 != 0L) { - this.aHashTable_2353.put(var9, new LinkableInt(this.anInt2343)); - } - - return this.anInt2343++; - } - - final void method148() { - DataBuffer var1 = new DataBuffer((this.aBoolean2347 ? 40 : 36) * this.anInt2343); - - for (int var2 = 0; var2 < this.anInt2343; ++var2) { - if (HDToolKit.aBoolean1790) { - var1.writeFloat((float) this.anIntArray2371[var2]); - var1.writeFloat((float) this.anIntArray2358[var2]); - var1.writeFloat((float) this.anIntArray2352[var2]); - var1.writeInt(this.anIntArray2348[var2]); - var1.writeFloat(this.aFloatArray2369[var2]); - var1.writeFloat(this.aFloatArray2365[var2]); - var1.writeFloat(this.aFloatArray2354[var2]); - var1.writeFloat((float) this.anIntArray2371[var2] / this.aFloat2373); - var1.writeFloat((float) this.anIntArray2352[var2] / this.aFloat2373); - if (this.aBoolean2347) { - var1.writeFloat(this.aFloatArray2346[var2]); - } - } else { - var1.writeFloatLE((float) this.anIntArray2371[var2]); - var1.writeFloatLE((float) this.anIntArray2358[var2]); - var1.writeFloatLE((float) this.anIntArray2352[var2]); - var1.writeInt(this.anIntArray2348[var2]); - var1.writeFloatLE(this.aFloatArray2369[var2]); - var1.writeFloatLE(this.aFloatArray2365[var2]); - var1.writeFloatLE(this.aFloatArray2354[var2]); - var1.writeFloatLE((float) this.anIntArray2371[var2] / this.aFloat2373); - var1.writeFloatLE((float) this.anIntArray2352[var2] / this.aFloat2373); - if (this.aBoolean2347) { - var1.writeFloatLE(this.aFloatArray2346[var2]); - } - } - } - - if (HDToolKit.supportVertexBufferObject) { - ByteBuffer var3 = ByteBuffer.wrap(var1.buffer, 0, var1.index); - this.aClass156_2363 = new Class156(); - this.aClass156_2363.method2172(var3); - } else { - this.aByteBuffer2345 = ByteBuffer.allocateDirect(var1.index).order(ByteOrder.nativeOrder()); - this.aByteBuffer2345.put(var1.buffer, 0, var1.index); - this.aByteBuffer2345.flip(); - } - - this.anIntArray2371 = null; - this.anIntArray2358 = null; - this.anIntArray2352 = null; - this.anIntArray2348 = null; - this.aFloatArray2369 = null; - this.aFloatArray2365 = null; - this.aFloatArray2354 = null; - this.aHashTable_2353 = null; - this.aFloatArray2346 = null; - } - - final void method149(Class3_Sub2[][][] var1, float var2, boolean var3) { - if (aClass3_Sub30_2372 != null && aClass3_Sub30_2372.buffer.length >= this.anInt2359 * 4) { - aClass3_Sub30_2372.index = 0; - } else { - aClass3_Sub30_2372 = new DataBuffer(this.anInt2359 * 4); - } - - if (aClass3_Sub30_2362 != null && aClass3_Sub30_2362.buffer.length >= this.anInt2356 * 4) { - aClass3_Sub30_2362.index = 0; - } else { - aClass3_Sub30_2362 = new DataBuffer(this.anInt2356 * 4); - } - - int var4; - Class3_Sub2 var5; - DataBuffer var6; - int[] var7; - int[] var8; - int var9; - int var12; - if (HDToolKit.aBoolean1790) { - for (var4 = 0; var4 < this.anInt2366; ++var4) { - var5 = var1[this.anIntArray2367[var4]][this.anIntArray2350[var4]][this.anIntArray2349[var4]]; - if (var5 != null && var5.aBoolean2222) { - var7 = this.anIntArrayArray2357[var4]; - if (this.aBoolean2364) { - var8 = this.anIntArrayArray2360[var4]; - if (var8 != null) { - for (var9 = 0; var9 < var8.length; ++var9) { - aClass3_Sub30_2362.writeInt(var8[var9]); - } - } - - var6 = this.aBooleanArray2370[var4] ? aClass3_Sub30_2362 : aClass3_Sub30_2372; - } else { - var6 = aClass3_Sub30_2372; - } - - for (var12 = 1; var12 < var7.length - 1; ++var12) { - var6.writeInt(var7[0]); - var6.writeInt(var7[var12]); - var6.writeInt(var7[var12 + 1]); - } - } - } - } else { - for (var4 = 0; var4 < this.anInt2366; ++var4) { - var5 = var1[this.anIntArray2367[var4]][this.anIntArray2350[var4]][this.anIntArray2349[var4]]; - if (var5 != null && var5.aBoolean2222) { - var7 = this.anIntArrayArray2357[var4]; - if (this.aBoolean2364) { - var8 = this.anIntArrayArray2360[var4]; - if (var8 != null) { - for (var9 = 0; var9 < var8.length; ++var9) { - aClass3_Sub30_2362.writeIntLE(var8[var9]); - } - } - - var6 = this.aBooleanArray2370[var4] ? aClass3_Sub30_2362 : aClass3_Sub30_2372; - } else { - var6 = aClass3_Sub30_2372; - } - - for (var12 = 1; var12 < var7.length - 1; ++var12) { - var6.writeIntLE(var7[0]); - var6.writeIntLE(var7[var12]); - var6.writeIntLE(var7[var12 + 1]); - } - } - } - } - - if (aClass3_Sub30_2372.index != 0 || aClass3_Sub30_2362.index != 0) { - GL var10 = HDToolKit.gl; - if (this.anInt2351 == -1 || var3) { - HDToolKit.bindTexture2D(-1); - Unsorted.method551(0, 0); - } else { - Class51.anInterface2_838.method8(this.anInt2351, true); - } - - int var11 = this.aBoolean2347 ? 40 : 36; - if (this.aClass156_2363 == null) { - if (HDToolKit.supportVertexBufferObject) { - var10.glBindBufferARB('\u8892', 0); - } - - this.aByteBuffer2345.position(0); - var10.glVertexPointer(3, 5126, var11, this.aByteBuffer2345); - this.aByteBuffer2345.position(12); - var10.glColorPointer(4, 5121, var11, this.aByteBuffer2345); - if (Class106.aBoolean1441) { - this.aByteBuffer2345.position(16); - var10.glNormalPointer(5126, var11, this.aByteBuffer2345); - } - - this.aByteBuffer2345.position(28); - var10.glTexCoordPointer(2, 5126, var11, this.aByteBuffer2345); - if (this.aBoolean2347) { - var10.glClientActiveTexture(WaterShader.method2252()); - this.aByteBuffer2345.position(36); - var10.glTexCoordPointer(1, 5126, var11, this.aByteBuffer2345); - var10.glClientActiveTexture('\u84c0'); - } - } else { - this.aClass156_2363.method2169(); - var10.glVertexPointer(3, 5126, var11, 0L); - var10.glColorPointer(4, 5121, var11, 12L); - if (Class106.aBoolean1441) { - var10.glNormalPointer(5126, var11, 16L); - } - - var10.glTexCoordPointer(2, 5126, var11, 28L); - if (this.aBoolean2347) { - var10.glClientActiveTexture(WaterShader.method2252()); - var10.glTexCoordPointer(1, 5126, var11, 36L); - var10.glClientActiveTexture('\u84c0'); - } - } - - if (HDToolKit.supportVertexBufferObject) { - var10.glBindBufferARB('\u8893', 0); - } - - if (aClass3_Sub30_2372.index != 0) { - if (aByteBuffer2368 != null && aByteBuffer2368.capacity() >= aClass3_Sub30_2372.index) { - aByteBuffer2368.clear(); - } else { - aByteBuffer2368 = ByteBuffer.allocateDirect(aClass3_Sub30_2372.index).order(ByteOrder.nativeOrder()); - } - - aByteBuffer2368.put(aClass3_Sub30_2372.buffer, 0, aClass3_Sub30_2372.index); - aByteBuffer2368.flip(); - HDToolKit.method1832(var2); - var10.glDrawElements(4, aClass3_Sub30_2372.index / 4, 5125, aByteBuffer2368); - } - - if (aClass3_Sub30_2362.index != 0) { - if (aByteBuffer2361 != null && aByteBuffer2361.capacity() >= aClass3_Sub30_2362.index) { - aByteBuffer2361.clear(); - } else { - aByteBuffer2361 = ByteBuffer.allocateDirect(aClass3_Sub30_2362.index).order(ByteOrder.nativeOrder()); - } - - aByteBuffer2361.put(aClass3_Sub30_2362.buffer, 0, aClass3_Sub30_2362.index); - aByteBuffer2361.flip(); - HDToolKit.method1832(var2 - 100.0F); - HDToolKit.depthBufferWritingDisabled(); - var10.glDrawElements(4, aClass3_Sub30_2362.index / 4, 5125, aByteBuffer2361); - HDToolKit.method1830(); - } - - } - } - - final void method150(int var1, int var2, int var3, int[] var4, int[] var5, boolean var6) { - if (this.aBoolean2364) { - this.anIntArrayArray2360[this.anInt2366] = var5; - this.aBooleanArray2370[this.anInt2366] = var6; - if (var5 != null) { - this.anInt2356 += var5.length; - } - - if (var6) { - this.anInt2356 += 3 * (var4.length - 2); - } else { - this.anInt2359 += 3 * (var4.length - 2); - } - } else { - this.anInt2359 += 3 * (var4.length - 2); - } - - this.anIntArray2367[this.anInt2366] = var1; - this.anIntArray2350[this.anInt2366] = var2; - this.anIntArray2349[this.anInt2366] = var3; - this.anIntArrayArray2357[this.anInt2366] = var4; - this.anInt2366++; - } - - Class3_Sub11(int var1, float var2, boolean var3, boolean var4, int var5) { - this.anInt2351 = var1; - this.aFloat2373 = var2; - this.aBoolean2364 = var3; - this.aBoolean2347 = var4; - this.anInt2355 = var5; - } -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub14.java b/Client/src/main/java/org/runite/client/Class3_Sub14.java deleted file mode 100644 index c006505b0..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub14.java +++ /dev/null @@ -1,551 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -final class Class3_Sub14 extends Linkable { - - private static float[] aFloatArray2387; - private static float[] aFloatArray2388; - private int anInt2389; - private int anInt2390; - private static int[] anIntArray2391; - private static Class152[] aClass152Array2392; - private static boolean[] aBooleanArray2393; - private float[] aFloatArray2394; - private boolean aBoolean2395; - private static int anInt2396; - private int anInt2397; - private static Class150[] aClass150Array2398; - private static float[] aFloatArray2399; - private static float[] aFloatArray2400; - private int anInt2401; - private static int anInt2402; - private static float[] aFloatArray2403; - private static int anInt2404; - private static boolean aBoolean2405 = false; - static Class71[] aClass71Array2406; - private static Class59[] aClass59Array2407; - private int anInt2408; - private static int[] anIntArray2409; - private byte[][] aByteArrayArray2410; - private boolean aBoolean2411; - private int anInt2412; - private static float[] aFloatArray2413; - private static int anInt2414; - private byte[] aByteArray2415; - private static float[] aFloatArray2416; - private static byte[] aByteArray2417; - private int anInt2418; - private int anInt2419; - private static int[] anIntArray2420; - - - static float method358(int var0) { - int var1 = var0 & 2097151; - int var2 = var0 & Integer.MIN_VALUE; - int var3 = (var0 & 2145386496) >> 21; - if(var2 != 0) { - var1 = -var1; - } - - return (float)((double)var1 * Math.pow(2.0D, var3 - 788)); - } - - final PcmSound method359(int[] var1) { - if(var1 != null && var1[0] <= 0) { - return null; - } else { - if(this.aByteArray2415 == null) { - this.anInt2389 = 0; - this.aFloatArray2394 = new float[anInt2396]; - this.aByteArray2415 = new byte[this.anInt2390]; - this.anInt2419 = 0; - this.anInt2418 = 0; - } - - for(; this.anInt2418 < this.aByteArrayArray2410.length; ++this.anInt2418) { - if(var1 != null && var1[0] <= 0) { - return null; - } - - float[] var2 = this.method366(this.anInt2418); - if(var2 != null) { - int var3 = this.anInt2419; - int var4 = var2.length; - if(var4 > this.anInt2390 - var3) { - var4 = this.anInt2390 - var3; - } - - for(int var5 = 0; var5 < var4; ++var5) { - int var6 = (int)(128.0F + var2[var5] * 128.0F); - if((var6 & -256) != 0) { - var6 = ~var6 >> 31; - } - - this.aByteArray2415[var3++] = (byte)(var6 - 128); - } - - if(var1 != null) { - var1[0] -= var3 - this.anInt2419; - } - - this.anInt2419 = var3; - } - } - - this.aFloatArray2394 = null; - byte[] var7 = this.aByteArray2415; - this.aByteArray2415 = null; - return new PcmSound(this.anInt2408, var7, this.anInt2397, this.anInt2401, this.aBoolean2395); - } - } - - private void method360(byte[] var1) { - DataBuffer var2 = new DataBuffer(var1); - this.anInt2408 = var2.readInt(); - this.anInt2390 = var2.readInt(); - this.anInt2397 = var2.readInt(); - this.anInt2401 = var2.readInt(); - if(this.anInt2401 < 0) { - this.anInt2401 = ~this.anInt2401; - this.aBoolean2395 = true; - } - - int var3 = var2.readInt(); - this.aByteArrayArray2410 = new byte[var3][]; - - for(int var4 = 0; var4 < var3; ++var4) { - int var5 = 0; - - int var6; - do { - var6 = var2.readUnsignedByte(); - var5 += var6; - } while(var6 >= 255); - - byte[] var7 = new byte[var5]; - var2.readBytes(var7, var5); - this.aByteArrayArray2410[var4] = var7; - } - - } - - private static boolean method362(CacheIndex var0) { - if(!aBoolean2405) { - byte[] var1 = var0.getFile(0, 0); - if(var1 == null) { - return false; - } - - method367(var1); - aBoolean2405 = true; - } - - return true; - } - - static Class3_Sub14 method363(CacheIndex var0, int var1, int var2) { - if(method362(var0)) { - byte[] var3 = var0.getFile(var1, var2); - return var3 == null?null:new Class3_Sub14(var3); - } else { - var0.method2129((byte)95, var2, var1); - return null; - } - } - - static int method364() { - int var0 = aByteArray2417[anInt2404] >> anInt2414 & 1; - ++anInt2414; - anInt2404 += anInt2414 >> 3; - anInt2414 &= 7; - return var0; - } - - private static void method365(byte[] var0) { - aByteArray2417 = var0; - anInt2404 = 0; - anInt2414 = 0; - } - - private float[] method366(int var1) { - method365(this.aByteArrayArray2410[var1]); - method364(); - int var2 = method368(Class3_Sub26.method513(anIntArray2420.length - 1)); - boolean var3 = aBooleanArray2393[var2]; - int var4 = var3?anInt2396:anInt2402; - boolean var5 = false; - boolean var6 = false; - if(var3) { - var5 = method364() != 0; - var6 = method364() != 0; - } - - int var7 = var4 >> 1; - int var8; - int var9; - int var10; - if(var3 && !var5) { - var8 = (var4 >> 2) - (anInt2402 >> 2); - var9 = (var4 >> 2) + (anInt2402 >> 2); - var10 = anInt2402 >> 1; - } else { - var8 = 0; - var9 = var7; - var10 = var4 >> 1; - } - - int var11; - int var12; - int var13; - if(var3 && !var6) { - var11 = var4 - (var4 >> 2) - (anInt2402 >> 2); - var12 = var4 - (var4 >> 2) + (anInt2402 >> 2); - var13 = anInt2402 >> 1; - } else { - var11 = var7; - var12 = var4; - var13 = var4 >> 1; - } - - Class150 var14 = aClass150Array2398[anIntArray2420[var2]]; - int var16 = var14.anInt1928; - int var17 = var14.anIntArray1929[var16]; - boolean var15 = !aClass59Array2407[var17].method1205(); - - for(var17 = 0; var17 < var14.anInt1931; ++var17) { - Class152 var18 = aClass152Array2392[var14.anIntArray1930[var17]]; - float[] var19 = aFloatArray2403; - var18.method2112(var19, var4 >> 1, var15); - } - - int var44; - if(!var15) { - var17 = var14.anInt1928; - var44 = var14.anIntArray1929[var17]; - aClass59Array2407[var44].method1202(aFloatArray2403, var4 >> 1); - } - - int var41; - if(var15) { - for(var17 = var4 >> 1; var17 < var4; ++var17) { - aFloatArray2403[var17] = 0.0F; - } - } else { - var17 = var4 >> 1; - var44 = var4 >> 2; - var41 = var4 >> 3; - float[] var20 = aFloatArray2403; - - int var21; - for(var21 = 0; var21 < var17; ++var21) { - var20[var21] *= 0.5F; - } - - for(var21 = var17; var21 < var4; ++var21) { - var20[var21] = -var20[var4 - var21 - 1]; - } - - float[] var46 = var3?aFloatArray2399:aFloatArray2400; - float[] var22 = var3?aFloatArray2387:aFloatArray2413; - float[] var23 = var3?aFloatArray2388:aFloatArray2416; - int[] var24 = var3?anIntArray2409:anIntArray2391; - - int var25; - float var27; - float var26; - float var29; - float var28; - for(var25 = 0; var25 < var44; ++var25) { - var26 = var20[4 * var25] - var20[var4 - 4 * var25 - 1]; - var27 = var20[4 * var25 + 2] - var20[var4 - 4 * var25 - 3]; - var28 = var46[2 * var25]; - var29 = var46[2 * var25 + 1]; - var20[var4 - 4 * var25 - 1] = var26 * var28 - var27 * var29; - var20[var4 - 4 * var25 - 3] = var26 * var29 + var27 * var28; - } - - float var31; - float var30; - for(var25 = 0; var25 < var41; ++var25) { - var26 = var20[var17 + 3 + 4 * var25]; - var27 = var20[var17 + 1 + 4 * var25]; - var28 = var20[4 * var25 + 3]; - var29 = var20[4 * var25 + 1]; - var20[var17 + 3 + 4 * var25] = var26 + var28; - var20[var17 + 1 + 4 * var25] = var27 + var29; - var30 = var46[var17 - 4 - 4 * var25]; - var31 = var46[var17 - 3 - 4 * var25]; - var20[4 * var25 + 3] = (var26 - var28) * var30 - (var27 - var29) * var31; - var20[4 * var25 + 1] = (var27 - var29) * var30 + (var26 - var28) * var31; - } - - var25 = Class3_Sub26.method513(var4 - 1); - - int var47; - int var49; - int var48; - int var52; - for(var49 = 0; var49 < var25 - 3; ++var49) { - var47 = var4 >> var49 + 2; - var48 = 8 << var49; - - for(var52 = 0; var52 < 2 << var49; ++var52) { - int var50 = var4 - var47 * 2 * var52; - int var51 = var4 - var47 * (2 * var52 + 1); - - for(int var32 = 0; var32 < var4 >> var49 + 4; ++var32) { - int var33 = 4 * var32; - float var34 = var20[var50 - 1 - var33]; - float var35 = var20[var50 - 3 - var33]; - float var36 = var20[var51 - 1 - var33]; - float var37 = var20[var51 - 3 - var33]; - var20[var50 - 1 - var33] = var34 + var36; - var20[var50 - 3 - var33] = var35 + var37; - float var38 = var46[var32 * var48]; - float var39 = var46[var32 * var48 + 1]; - var20[var51 - 1 - var33] = (var34 - var36) * var38 - (var35 - var37) * var39; - var20[var51 - 3 - var33] = (var35 - var37) * var38 + (var34 - var36) * var39; - } - } - } - - for(var49 = 1; var49 < var41 - 1; ++var49) { - var47 = var24[var49]; - if(var49 < var47) { - var48 = 8 * var49; - var52 = 8 * var47; - var30 = var20[var48 + 1]; - var20[var48 + 1] = var20[var52 + 1]; - var20[var52 + 1] = var30; - var30 = var20[var48 + 3]; - var20[var48 + 3] = var20[var52 + 3]; - var20[var52 + 3] = var30; - var30 = var20[var48 + 5]; - var20[var48 + 5] = var20[var52 + 5]; - var20[var52 + 5] = var30; - var30 = var20[var48 + 7]; - var20[var48 + 7] = var20[var52 + 7]; - var20[var52 + 7] = var30; - } - } - - for(var49 = 0; var49 < var17; ++var49) { - var20[var49] = var20[2 * var49 + 1]; - } - - for(var49 = 0; var49 < var41; ++var49) { - var20[var4 - 1 - 2 * var49] = var20[4 * var49]; - var20[var4 - 2 - 2 * var49] = var20[4 * var49 + 1]; - var20[var4 - var44 - 1 - 2 * var49] = var20[4 * var49 + 2]; - var20[var4 - var44 - 2 - 2 * var49] = var20[4 * var49 + 3]; - } - - for(var49 = 0; var49 < var41; ++var49) { - var27 = var23[2 * var49]; - var28 = var23[2 * var49 + 1]; - var29 = var20[var17 + 2 * var49]; - var30 = var20[var17 + 2 * var49 + 1]; - var31 = var20[var4 - 2 - 2 * var49]; - float var54 = var20[var4 - 1 - 2 * var49]; - float var53 = var28 * (var29 - var31) + var27 * (var30 + var54); - var20[var17 + 2 * var49] = (var29 + var31 + var53) * 0.5F; - var20[var4 - 2 - 2 * var49] = (var29 + var31 - var53) * 0.5F; - var53 = var28 * (var30 + var54) - var27 * (var29 - var31); - var20[var17 + 2 * var49 + 1] = (var30 - var54 + var53) * 0.5F; - var20[var4 - 1 - 2 * var49] = (-var30 + var54 + var53) * 0.5F; - } - - for(var49 = 0; var49 < var44; ++var49) { - var20[var49] = var20[2 * var49 + var17] * var22[2 * var49] + var20[2 * var49 + 1 + var17] * var22[2 * var49 + 1]; - var20[var17 - 1 - var49] = var20[2 * var49 + var17] * var22[2 * var49 + 1] - var20[2 * var49 + 1 + var17] * var22[2 * var49]; - } - - for(var49 = 0; var49 < var44; ++var49) { - var20[var4 - var44 + var49] = -var20[var49]; - } - - for(var49 = 0; var49 < var44; ++var49) { - var20[var49] = var20[var44 + var49]; - } - - for(var49 = 0; var49 < var44; ++var49) { - var20[var44 + var49] = -var20[var44 - var49 - 1]; - } - - for(var49 = 0; var49 < var44; ++var49) { - var20[var17 + var49] = var20[var4 - var49 - 1]; - } - - for(var49 = var8; var49 < var9; ++var49) { - var27 = (float)Math.sin(((double)(var49 - var8) + 0.5D) / (double)var10 * 0.5D * 3.141592653589793D); - aFloatArray2403[var49] *= (float)Math.sin(1.5707963267948966D * (double)var27 * (double)var27); - } - - for(var49 = var11; var49 < var12; ++var49) { - var27 = (float)Math.sin(((double)(var49 - var11) + 0.5D) / (double)var13 * 0.5D * 3.141592653589793D + 1.5707963267948966D); - aFloatArray2403[var49] *= (float)Math.sin(1.5707963267948966D * (double)var27 * (double)var27); - } - } - - float[] var42 = null; - if(this.anInt2389 > 0) { - var44 = this.anInt2389 + var4 >> 2; - var42 = new float[var44]; - int var45; - if(!this.aBoolean2411) { - for(var41 = 0; var41 < this.anInt2412; ++var41) { - var45 = (this.anInt2389 >> 1) + var41; - var42[var41] += this.aFloatArray2394[var45]; - } - } - - if(!var15) { - for(var41 = var8; var41 < var4 >> 1; ++var41) { - var45 = var42.length - (var4 >> 1) + var41; - var42[var45] += aFloatArray2403[var41]; - } - } - } - - float[] var43 = this.aFloatArray2394; - this.aFloatArray2394 = aFloatArray2403; - aFloatArray2403 = var43; - this.anInt2389 = var4; - this.anInt2412 = var12 - (var4 >> 1); - this.aBoolean2411 = var15; - return var42; - } - - private static void method367(byte[] var0) { - method365(var0); - anInt2402 = 1 << method368(4); - anInt2396 = 1 << method368(4); - aFloatArray2403 = new float[anInt2396]; - - int var1; - int var2; - int var3; - int var4; - int var5; - for(var1 = 0; var1 < 2; ++var1) { - var2 = var1 != 0?anInt2396:anInt2402; - var3 = var2 >> 1; - var4 = var2 >> 2; - var5 = var2 >> 3; - float[] var6 = new float[var3]; - - for(int var7 = 0; var7 < var4; ++var7) { - var6[2 * var7] = (float)Math.cos((double)(4 * var7) * 3.141592653589793D / (double)var2); - var6[2 * var7 + 1] = -((float)Math.sin((double)(4 * var7) * 3.141592653589793D / (double)var2)); - } - - float[] var13 = new float[var3]; - - for(int var8 = 0; var8 < var4; ++var8) { - var13[2 * var8] = (float)Math.cos((double)(2 * var8 + 1) * 3.141592653589793D / (double)(2 * var2)); - var13[2 * var8 + 1] = (float)Math.sin((double)(2 * var8 + 1) * 3.141592653589793D / (double)(2 * var2)); - } - - float[] var14 = new float[var4]; - - for(int var9 = 0; var9 < var5; ++var9) { - var14[2 * var9] = (float)Math.cos((double)(4 * var9 + 2) * 3.141592653589793D / (double)var2); - var14[2 * var9 + 1] = -((float)Math.sin((double)(4 * var9 + 2) * 3.141592653589793D / (double)var2)); - } - - int[] var15 = new int[var5]; - int var10 = Class3_Sub26.method513(var5 - 1); - - for(int var11 = 0; var11 < var5; ++var11) { - var15[var11] = Class3_Sub28_Sub3.method540(var10, var11); - } - - if(var1 == 0) { - aFloatArray2400 = var6; - aFloatArray2413 = var13; - aFloatArray2416 = var14; - anIntArray2391 = var15; - } else { - aFloatArray2399 = var6; - aFloatArray2387 = var13; - aFloatArray2388 = var14; - anIntArray2409 = var15; - } - } - - var1 = method368(8) + 1; - aClass71Array2406 = new Class71[var1]; - - for(var2 = 0; var2 < var1; ++var2) { - aClass71Array2406[var2] = new Class71(); - } - - var2 = method368(6) + 1; - - for(var3 = 0; var3 < var2; ++var3) { - method368(16); - } - - var2 = method368(6) + 1; - aClass59Array2407 = new Class59[var2]; - - for(var3 = 0; var3 < var2; ++var3) { - aClass59Array2407[var3] = new Class59(); - } - - var3 = method368(6) + 1; - aClass152Array2392 = new Class152[var3]; - - for(var4 = 0; var4 < var3; ++var4) { - aClass152Array2392[var4] = new Class152(); - } - - var4 = method368(6) + 1; - aClass150Array2398 = new Class150[var4]; - - for(var5 = 0; var5 < var4; ++var5) { - aClass150Array2398[var5] = new Class150(); - } - - var5 = method368(6) + 1; - aBooleanArray2393 = new boolean[var5]; - anIntArray2420 = new int[var5]; - - for(int var12 = 0; var12 < var5; ++var12) { - aBooleanArray2393[var12] = method364() != 0; - method368(16); - method368(16); - anIntArray2420[var12] = method368(8); - } - - } - - static int method368(int var0) { - int var1 = 0; - - int var2; - int var3; - for(var2 = 0; var0 >= 8 - anInt2414; var0 -= var3) { - var3 = 8 - anInt2414; - int var4 = (1 << var3) - 1; - var1 += (aByteArray2417[anInt2404] >> anInt2414 & var4) << var2; - anInt2414 = 0; - ++anInt2404; - var2 += var3; - } - - if(var0 > 0) { - var3 = (1 << var0) - 1; - var1 += (aByteArray2417[anInt2404] >> anInt2414 & var3) << var2; - anInt2414 += var0; - } - - return var1; - } - - private Class3_Sub14(byte[] var1) { - this.method360(var1); - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub15.java b/Client/src/main/java/org/runite/client/Class3_Sub15.java deleted file mode 100644 index f45db9fc8..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub15.java +++ /dev/null @@ -1,587 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.data.ReferenceCache; -import org.rs09.client.net.Connection; - -public final class Class3_Sub15 extends Linkable { - - static int anInt2421 = -1; - byte[] aByteArray2422; - private int[] anIntArray2423; - int anInt2424; - byte[] aByteArray2425; - static int anInt2426; - static boolean aBoolean2427 = false; - static ReferenceCache aReferenceCache_2428 = new ReferenceCache(50); - public static Connection activeConnection; - byte[] aByteArray2430; - PcmSound[] aPcmSoundArray2431; - static boolean aBoolean2433 = false; - short[] aShortArray2434; - Class166[] aClass166Array2435; - - - final void method369() { - try { - this.anIntArray2423 = null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "jk.A(" + (byte) -124 + ')'); - } - } - - static void method370() { - try { - //int var1 = -125 / ((0 - var0) / 59); - RenderAnimationDefinition.aReferenceCache_1955.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "jk.B(" + (byte) -73 + ')'); - } - } - - static Class3_Sub28_Sub3 method371(RSString var1) { - try { - - for (Class3_Sub28_Sub3 var2 = (Class3_Sub28_Sub3) Class134.aLinkedList_1758.method1222(); var2 != null; var2 = (Class3_Sub28_Sub3) Class134.aLinkedList_1758.method1221()) { - if (var2.aClass94_3561.equalsString(var1)) { - return var2; - } - } - - return null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "jk.C(" + 2 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - final boolean method373(int[] var2, Class83 var3, byte[] var4) { - try { - int var6 = 0; - PcmSound var7 = null; - boolean var5 = true; - - for (int var8 = 0; var8 < 128; ++var8) { - if (null == var4 || var4[var8] != 0) { - int var9 = this.anIntArray2423[var8]; - if (var9 != 0) { - if (var6 != var9) { - var6 = var9--; - if ((var9 & 1) == 0) { - var7 = var3.method1413(var9 >> 2, var2); - } else { - var7 = var3.method1416(var9 >> 2, var2); - } - - if (var7 == null) { - var5 = false; - } - } - - if (null != var7) { - this.aPcmSoundArray2431[var8] = var7; - this.anIntArray2423[var8] = 0; - } - } - } - } - - return var5; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "jk.E(" + 17904 + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ')'); - } - } - - static void method374(int var0, boolean var1, byte[] var2, int var3, Class91[] var5) { - try { - DataBuffer var6 = new DataBuffer(var2); - int objectId = -1; - - while (var6.buffer.length > 0) { - int var8 = var6.method773(); - if (0 == var8) { - - return; - } - - objectId += var8; - int data = 0; - - while (true) { - int var10 = var6.getSmart(); - if (var10 == 0) { - break; - } - - data += var10 - 1; - int var11 = data & 63; - int var13 = data >> 12; - int var12 = data >> 6 & 63; - int var14 = var6.readUnsignedByte(); - int var15 = var14 >> 2; - int var16 = var14 & 3; - int var17 = var0 + var12; - int var18 = var11 + var3; - if (var17 > 0 && var18 > 0 && var17 < 103 && var18 < 103) { - Class91 var19 = null; - if (!var1) { - int var20 = var13; - if ((2 & Unsorted.aByteArrayArrayArray113[1][var17][var18]) == 2) { - var20 = var13 - 1; - } - - if (0 <= (var20 %= 4)) { - var19 = var5[var20]; - } - } - - Class110.method1683(var13 % 4, !var1, var13, var1, var19, objectId, var15, var17, var18, var16); - } - } - } - } catch (RuntimeException var21) { - throw ClientErrorException.clientError(var21, "jk.F(" + var0 + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + 0 + ',' + (var5 != null ? "{...}" : "null") + ')'); - } - } - - public Class3_Sub15() { - } - - Class3_Sub15(byte[] var1) { - try { - this.aShortArray2434 = new short[128]; - this.aByteArray2430 = new byte[128]; - this.aPcmSoundArray2431 = new PcmSound[128]; - this.aByteArray2422 = new byte[128]; - this.aClass166Array2435 = new Class166[128]; - this.anIntArray2423 = new int[128]; - int var3 = 0; - this.aByteArray2425 = new byte[128]; - - DataBuffer buffer; - for (buffer = new DataBuffer(var1); buffer.buffer[var3 + buffer.index] != 0; ++var3) { - } - - byte[] var4 = new byte[var3]; - - int var5; - for (var5 = 0; var3 > var5; ++var5) { - var4[var5] = buffer.readSignedByte(); - } - - ++buffer.index; - ++var3; - var5 = buffer.index; - buffer.index += var3; - - int var6; - for (var6 = 0; 0 != buffer.buffer[buffer.index + var6]; ++var6) { - } - - byte[] var7 = new byte[var6]; - - int var8; - for (var8 = 0; var6 > var8; ++var8) { - var7[var8] = buffer.readSignedByte(); - } - - ++buffer.index; - ++var6; - int var9 = 0; - var8 = buffer.index; - - for (buffer.index += var6; buffer.buffer[var9 + buffer.index] != 0; ++var9) { - } - - byte[] var10 = new byte[var9]; - - for (int var11 = 0; var9 > var11; ++var11) { - var10[var11] = buffer.readSignedByte(); - } - - ++buffer.index; - ++var9; - byte[] var37 = new byte[var9]; - int var12; - int var14; - if (var9 <= 1) { - var12 = var9; - } else { - var12 = 2; - var37[1] = 1; - int var13 = 1; - - for (var14 = 2; var14 < var9; ++var14) { - int var15 = buffer.readUnsignedByte(); - if (0 == var15) { - var13 = var12++; - } else { - if (var15 <= var13) { - --var15; - } - - var13 = var15; - } - - var37[var14] = (byte) var13; - } - } - - Class166[] var38 = new Class166[var12]; - - Class166 var41; - for (var14 = 0; var14 < var38.length; ++var14) { - var41 = var38[var14] = new Class166(); - int var16 = buffer.readUnsignedByte(); - if (0 < var16) { - var41.aByteArray2064 = new byte[2 * var16]; - } - - var16 = buffer.readUnsignedByte(); - if (var16 > 0) { - var41.aByteArray2076 = new byte[var16 * 2 + 2]; - var41.aByteArray2076[1] = 64; - } - } - - var14 = buffer.readUnsignedByte(); - byte[] var40 = var14 > 0 ? new byte[var14 * 2] : null; - var14 = buffer.readUnsignedByte(); - byte[] var39 = var14 > 0 ? new byte[var14 * 2] : null; - - int var17; - for (var17 = 0; buffer.buffer[var17 + buffer.index] != 0; ++var17) { - } - - byte[] var18 = new byte[var17]; - - int var19; - for (var19 = 0; var19 < var17; ++var19) { - var18[var19] = buffer.readSignedByte(); - } - - ++buffer.index; - ++var17; - var19 = 0; - - int var20; - for (var20 = 0; var20 < 128; ++var20) { - var19 += buffer.readUnsignedByte(); - this.aShortArray2434[var20] = (short) var19; - } - - var19 = 0; - - for (var20 = 0; var20 < 128; ++var20) { - var19 += buffer.readUnsignedByte(); - this.aShortArray2434[var20] = (short) (this.aShortArray2434[var20] + (var19 << 8)); - } - - var20 = 0; - int var21 = 0; - int var22 = 0; - - int var23; - for (var23 = 0; var23 < 128; ++var23) { - if (var20 == 0) { - if (var21 < var18.length) { - var20 = var18[var21++]; - } else { - var20 = -1; - } - - var22 = buffer.method741(); - } - - this.aShortArray2434[var23] = (short) (this.aShortArray2434[var23] + Unsorted.bitwiseAnd(32768, -1 + var22 << 14)); - this.anIntArray2423[var23] = var22; - --var20; - } - - var20 = 0; - var23 = 0; - var21 = 0; - - int var24; - for (var24 = 0; var24 < 128; ++var24) { - if (this.anIntArray2423[var24] != 0) { - if (var20 == 0) { - var23 = buffer.buffer[var5++] + -1; - if (var4.length > var21) { - var20 = var4[var21++]; - } else { - var20 = -1; - } - } - - --var20; - this.aByteArray2425[var24] = (byte) var23; - } - } - - var20 = 0; - var21 = 0; - var24 = 0; - - for (int var25 = 0; var25 < 128; ++var25) { - if (this.anIntArray2423[var25] != 0) { - if (0 == var20) { - var24 = buffer.buffer[var8++] - -16 << 2; - if (var7.length > var21) { - var20 = var7[var21++]; - } else { - var20 = -1; - } - } - - --var20; - this.aByteArray2422[var25] = (byte) var24; - } - } - - var21 = 0; - var20 = 0; - Class166 var43 = null; - - int var26; - for (var26 = 0; var26 < 128; ++var26) { - if (this.anIntArray2423[var26] != 0) { - if (var20 == 0) { - var43 = var38[var37[var21]]; - if (var21 >= var10.length) { - var20 = -1; - } else { - var20 = var10[var21++]; - } - } - - this.aClass166Array2435[var26] = var43; - --var20; - } - } - - var20 = 0; - var21 = 0; - var26 = 0; - - int var27; - for (var27 = 0; var27 < 128; ++var27) { - if (var20 == 0) { - if (var18.length > var21) { - var20 = var18[var21++]; - } else { - var20 = -1; - } - - if (this.anIntArray2423[var27] > 0) { - var26 = buffer.readUnsignedByte() + 1; - } - } - - --var20; - this.aByteArray2430[var27] = (byte) var26; - } - - this.anInt2424 = buffer.readUnsignedByte() + 1; - - int var29; - Class166 var28; - for (var27 = 0; var27 < var12; ++var27) { - var28 = var38[var27]; - if (var28.aByteArray2064 != null) { - for (var29 = 1; var29 < var28.aByteArray2064.length; var29 += 2) { - var28.aByteArray2064[var29] = buffer.readSignedByte(); - } - } - - if (var28.aByteArray2076 != null) { - for (var29 = 3; var29 < var28.aByteArray2076.length + -2; var29 += 2) { - var28.aByteArray2076[var29] = buffer.readSignedByte(); - } - } - } - - if (null != var40) { - for (var27 = 1; var40.length > var27; var27 += 2) { - var40[var27] = buffer.readSignedByte(); - } - } - - if (null != var39) { - for (var27 = 1; var27 < var39.length; var27 += 2) { - var39[var27] = buffer.readSignedByte(); - } - } - - for (var27 = 0; var27 < var12; ++var27) { - var28 = var38[var27]; - if (null != var28.aByteArray2076) { - var19 = 0; - - for (var29 = 2; var28.aByteArray2076.length > var29; var29 += 2) { - var19 -= -1 + -buffer.readUnsignedByte(); - var28.aByteArray2076[var29] = (byte) var19; - } - } - } - - for (var27 = 0; var12 > var27; ++var27) { - var28 = var38[var27]; - if (null != var28.aByteArray2064) { - var19 = 0; - - for (var29 = 2; var28.aByteArray2064.length > var29; var29 += 2) { - var19 = var19 - -1 - -buffer.readUnsignedByte(); - var28.aByteArray2064[var29] = (byte) var19; - } - } - } - - byte var30; - int var34; - int var32; - int var33; - int var44; - byte var48; - if (null != var40) { - var19 = buffer.readUnsignedByte(); - var40[0] = (byte) var19; - - for (var27 = 2; var40.length > var27; var27 += 2) { - var19 = 1 + (var19 - -buffer.readUnsignedByte()); - var40[var27] = (byte) var19; - } - - var48 = var40[0]; - byte var46 = var40[1]; - - for (var29 = 0; var29 < var48; ++var29) { - this.aByteArray2430[var29] = (byte) (32 + var46 * this.aByteArray2430[var29] >> 6); - } - - for (var29 = 2; var29 < var40.length; var48 = var30) { - var30 = var40[var29]; - byte var31 = var40[1 + var29]; - var29 += 2; - var32 = (var30 - var48) * var46 + (var30 - var48) / 2; - - for (var33 = var48; var30 > var33; ++var33) { - var34 = TextureOperation14.method319(var32, -125, -var48 + var30); - var32 += var31 + -var46; - this.aByteArray2430[var33] = (byte) (var34 * this.aByteArray2430[var33] - -32 >> 6); - } - - var46 = var31; - } - - for (var44 = var48; var44 < 128; ++var44) { - this.aByteArray2430[var44] = (byte) (32 + this.aByteArray2430[var44] * var46 >> 6); - } - } - - if (null != var39) { - var19 = buffer.readUnsignedByte(); - var39[0] = (byte) var19; - - for (var27 = 2; var27 < var39.length; var27 += 2) { - var19 = 1 + (var19 - -buffer.readUnsignedByte()); - var39[var27] = (byte) var19; - } - - var48 = var39[0]; - int var47 = var39[1] << 1; - - for (var29 = 0; var29 < var48; ++var29) { - var44 = var47 + (255 & this.aByteArray2422[var29]); - if (var44 < 0) { - var44 = 0; - } - - if (128 < var44) { - var44 = 128; - } - - this.aByteArray2422[var29] = (byte) var44; - } - - int var45; - for (var29 = 2; var39.length > var29; var47 = var45) { - var30 = var39[var29]; - var32 = (-var48 + var30) * var47 - -((var30 - var48) / 2); - var45 = var39[1 + var29] << 1; - var29 += 2; - - for (var33 = var48; var30 > var33; ++var33) { - var34 = TextureOperation14.method319(var32, -116, -var48 + var30); - var32 += -var47 + var45; - int var35 = var34 + (this.aByteArray2422[var33] & 0xFF); - if (var35 < 0) { - var35 = 0; - } - - if (var35 > 128) { - var35 = 128; - } - - this.aByteArray2422[var33] = (byte) var35; - } - - var48 = var30; - } - - for (var44 = var48; var44 < 128; ++var44) { - var45 = (255 & this.aByteArray2422[var44]) + var47; - if (var45 < 0) { - var45 = 0; - } - - if (128 < var45) { - var45 = 128; - } - - this.aByteArray2422[var44] = (byte) var45; - } - } - - for (var27 = 0; var12 > var27; ++var27) { - var38[var27].anInt2078 = buffer.readUnsignedByte(); - } - - for (var27 = 0; var27 < var12; ++var27) { - var28 = var38[var27]; - if (null != var28.aByteArray2064) { - var28.anInt2067 = buffer.readUnsignedByte(); - } - - if (null != var28.aByteArray2076) { - var28.anInt2071 = buffer.readUnsignedByte(); - } - - if (var28.anInt2078 > 0) { - var28.anInt2063 = buffer.readUnsignedByte(); - } - } - - for (var27 = 0; var12 > var27; ++var27) { - var38[var27].anInt2077 = buffer.readUnsignedByte(); - } - - for (var27 = 0; var12 > var27; ++var27) { - var28 = var38[var27]; - if (var28.anInt2077 > 0) { - var28.anInt2066 = buffer.readUnsignedByte(); - } - } - - for (var27 = 0; var27 < var12; ++var27) { - var28 = var38[var27]; - if (var28.anInt2066 > 0) { - var28.anInt2069 = buffer.readUnsignedByte(); - } - } - - } catch (RuntimeException var36) { - throw ClientErrorException.clientError(var36, "jk.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub17.java b/Client/src/main/java/org/runite/client/Class3_Sub17.java deleted file mode 100644 index f622c845f..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub17.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -public final class Class3_Sub17 extends Linkable { - - int anInt2454; - int[] anIntArray2455; - static int anInt2456; - static float aFloat2457; - int[][] anIntArrayArray2460; - int anInt2462; - boolean[] aBooleanArray2463; - - private static RSString aClass94_2465 = RSString.parse(""); - int[] anIntArray2466; - - static void method381() { - try { - if(ConfigInventoryDefinition.anInt3655 != -1) { - Class52.method1160(-120, ConfigInventoryDefinition.anInt3655); - } - - for(int var1 = 0; var1 < Class3_Sub28_Sub3.anInt3557; ++var1) { - if(Unsorted.aBooleanArray3674[var1]) { - Class163_Sub1_Sub1.aBooleanArray4008[var1] = true; - } - - Unsorted.aBooleanArray1712[var1] = Unsorted.aBooleanArray3674[var1]; - Unsorted.aBooleanArray3674[var1] = false; - } - - Class53.anInt865 = -1; - Class99.aClass11_1402 = null; - Class3_Sub23.anInt2535 = Class44.anInt719; - if(HDToolKit.highDetail) { - Unsorted.aBoolean47 = true; - } - - Unsorted.anInt2567 = -1; - - if(ConfigInventoryDefinition.anInt3655 != -1) { - Class3_Sub28_Sub3.anInt3557 = 0; - Class8.method841(); - } - - if(HDToolKit.highDetail) { - Class22.resetClipping(); - } else { - Class74.resetClipping(); - } - - Class106.anInt1446 = 0; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "jm.A(" + ')'); - } - } - - Class3_Sub17(int var1, byte[] var2) { - try { - this.anInt2454 = var1; - DataBuffer var3 = new DataBuffer(var2); - this.anInt2462 = var3.readUnsignedByte(); - this.anIntArrayArray2460 = new int[this.anInt2462][]; - this.anIntArray2466 = new int[this.anInt2462]; - this.aBooleanArray2463 = new boolean[this.anInt2462]; - this.anIntArray2455 = new int[this.anInt2462]; - - int var4; - for(var4 = 0; this.anInt2462 > var4; ++var4) { - this.anIntArray2466[var4] = var3.readUnsignedByte(); - } - - for(var4 = 0; var4 < this.anInt2462; ++var4) { - this.aBooleanArray2463[var4] = var3.readUnsignedByte() == 1; - } - - for(var4 = 0; var4 < this.anInt2462; ++var4) { - this.anIntArray2455[var4] = var3.readUnsignedShort(); - } - - for(var4 = 0; var4 < this.anInt2462; ++var4) { - this.anIntArrayArray2460[var4] = new int[var3.readUnsignedByte()]; - } - - for(var4 = 0; this.anInt2462 > var4; ++var4) { - for(int var5 = 0; this.anIntArrayArray2460[var4].length > var5; ++var5) { - this.anIntArrayArray2460[var4][var5] = var3.readUnsignedByte(); - } - } - - } catch (RuntimeException var6) { - loadNewHeader(var1, var2); -// var6.printStackTrace(); -// throw Class44.method1067(var6, "jm.(" + var1 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - private void loadNewHeader(int var1, byte[] var2) { - this.anInt2454 = var1; - System.out.println("Loaded new header!"); - DataBuffer var3 = new DataBuffer(var2); - this.anInt2462 = var3.readUnsignedByte(); - this.anIntArrayArray2460 = new int[this.anInt2462][]; - this.anIntArray2466 = new int[this.anInt2462]; - this.aBooleanArray2463 = new boolean[this.anInt2462]; - this.anIntArray2455 = new int[this.anInt2462]; - - int var4; - for(var4 = 0; this.anInt2462 > var4; ++var4) { - this.anIntArray2466[var4] = var3.readUnsignedByte(); - } - -// for(var4 = 0; ~this.anInt2462 < ~var4; ++var4) { -// this.aBooleanArray2463[var4] = ~var3.getByte((byte)-35) == -2; -// } -// -// for(var4 = 0; var4 < this.anInt2462; ++var4) { -//// this.anIntArray2455[var4] = var3.getShort(1); -// } - - for(var4 = 0; var4 < this.anInt2462; ++var4) { - this.anIntArrayArray2460[var4] = new int[var3.readUnsignedByte()]; - } - - for(var4 = 0; this.anInt2462 > var4; ++var4) { - for(int var5 = 0; this.anIntArrayArray2460[var4].length > var5; ++var5) { - this.anIntArrayArray2460[var4][var5] = var3.readUnsignedByte(); - } - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub19.java b/Client/src/main/java/org/runite/client/Class3_Sub19.java deleted file mode 100644 index 9700aa91b..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub19.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.rendering.Toolkit; - -public final class Class3_Sub19 extends Linkable { - - public static int[] anIntArray3693 = new int[1000]; - byte aByte2472; - RSString aClass94_2473; - static int anInt2475 = 0; - RSString aClass94_2476; - int anInt2478; - - - static Class75_Sub2 method384(DataBuffer var0) { - try { - - return new Class75_Sub2(var0.readSignedShort(), var0.readSignedShort(), var0.readSignedShort(), var0.readSignedShort(), var0.readMedium(), var0.readMedium(), var0.readUnsignedByte()); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "kl.C(" + (var0 != null ? "{...}" : "null") + ',' + (byte) 80 + ')'); - } - } - - static boolean method385(int var1) { - try { - byte var3 = 0; - int var5; - int var6; - int var7; - int var8; - if (null == Class27.aSoftwareSprite_518) { - if (HDToolKit.highDetail || null == Class49.aAbstractSprite_812) { - Class27.aSoftwareSprite_518 = new SoftwareSprite(512, 512); - } else { - Class27.aSoftwareSprite_518 = (SoftwareSprite) Class49.aAbstractSprite_812; - } - - int[] var4 = Class27.aSoftwareSprite_518.anIntArray4081; - var5 = var4.length; - - for (var6 = 0; var5 > var6; ++var6) { - var4[var6] = 1; - } - - for (var6 = var3 + 1; -1 + -var3 + 104 > var6; ++var6) { - var7 = 4 * 512 * (var3 + 103 + -var6) + 24628; - - for (var8 = 1; 104 + -1 > var8; ++var8) { - if ((Unsorted.aByteArrayArrayArray113[var1][var8][var6] & 24) == 0) { - Unsorted.method1469(var4, var7, 512, var1, var8, var6); - } - - if (var1 < 3 && 0 != (Unsorted.aByteArrayArrayArray113[1 + var1][var8][var6] & 8)) { - Unsorted.method1469(var4, var7, 512, var1 + 1, var8, var6); - } - - var7 += 4; - } - } - - MouseListeningClass.anInt1924 = 0; - - for (var6 = 0; 104 > var6; ++var6) { - for (var7 = 0; 104 > var7; ++var7) { - long var20 = Class3_Sub2.method104(WorldListCountry.localPlane, var6, var7 - -var3); - if (var20 != 0) { - ObjectDefinition var10 = ObjectDefinition.getObjectDefinition((int) (var20 >>> 32) & Integer.MAX_VALUE); - int var11 = var10.MapIcon; - int var12; - if (null != var10.ChildrenIds) { - for (var12 = 0; var12 < var10.ChildrenIds.length; ++var12) { - if (-1 != var10.ChildrenIds[var12]) { - ObjectDefinition var13 = ObjectDefinition.getObjectDefinition(var10.ChildrenIds[var12]); - if (0 <= var13.MapIcon) { - var11 = var13.MapIcon; - break; - } - } - } - } - - if (var11 >= 0) { - int var21 = var3 + var7; - var12 = var6; - if (22 != var11 && var11 != 29 && 34 != var11 && var11 != 36 && 46 != var11 && var11 != 47 && var11 != 48) { - int[][] var14 = AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].anIntArrayArray1304; - - for (int var15 = 0; var15 < 10; ++var15) { - int var16 = (int) (Math.random() * 4.0D); - if (var16 == 0 && var12 > 0 && var12 > var6 - 3 && (var14[var12 - 1][var21] & 19661064) == 0) { - --var12; - } - - if (var16 == 1 && var12 < 103 && var12 < 3 + var6 && (var14[var12 - -1][var21] & 19661184) == 0) { - ++var12; - } - - if (2 == var16 && var21 > 0 && var21 > -3 + var7 && 0 == (var14[var12][-1 + var21] & 19661058)) { - --var21; - } - - if (3 == var16 && var21 < 103 && var7 - -3 > var21 && 0 == (19661088 & var14[var12][1 + var21])) { - ++var21; - } - } - } - - anIntArray3693[MouseListeningClass.anInt1924] = var10.objectId; - Class84.anIntArray1163[MouseListeningClass.anInt1924] = var12; - Unsorted.anIntArray4050[MouseListeningClass.anInt1924] = var21 + -var3; - ++MouseListeningClass.anInt1924; - } - } - } - } - } - - Class27.aSoftwareSprite_518.method658(); - int var18 = (238 + (int) (20.0D * Math.random()) - 10 << 8) + (-10 + (int) (Math.random() * 20.0D) + 238 << 16) + (228 - -((int) (20.0D * Math.random()))); - var5 = (int) (Math.random() * 20.0D) + 238 + -10 << 16; - - for (var6 = 1; var6 < 103; ++var6) { - for (var7 = 1; 103 > var7; ++var7) { - if (0 == (Unsorted.aByteArrayArrayArray113[var1][var7][var6 - -var3] & 24) && !Unsorted.method1529(var3, var7, var18, var6, var5, var1)) { - if (HDToolKit.highDetail) { - Toolkit.JAVA_TOOLKIT.resetBuffer(); - } else { - Unsorted.aClass158_3009.method2182(); - } - - return false; - } - - if (var1 < 3 && (Unsorted.aByteArrayArrayArray113[1 + var1][var7][var6 + var3] & 8) != 0 && !Unsorted.method1529(var3, var7, var18, var6, var5, 1 + var1)) { - if (HDToolKit.highDetail) { - Toolkit.JAVA_TOOLKIT.resetBuffer(); - } else { - Unsorted.aClass158_3009.method2182(); - } - - return false; - } - } - } - - if (HDToolKit.highDetail) { - int[] var19 = Class27.aSoftwareSprite_518.anIntArray4081; - var7 = var19.length; - - for (var8 = 0; var7 > var8; ++var8) { - if (var19[var8] == 0) { - var19[var8] = 1; - } - } - - Class49.aAbstractSprite_812 = new HDSprite(Class27.aSoftwareSprite_518); - } else { - Class49.aAbstractSprite_812 = Class27.aSoftwareSprite_518; - } - - if (HDToolKit.highDetail) { - Toolkit.JAVA_TOOLKIT.resetBuffer(); - } else { - Unsorted.aClass158_3009.method2182(); - } - - Class27.aSoftwareSprite_518 = null; - return true; - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "kl.B(" + 0 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub2.java b/Client/src/main/java/org/runite/client/Class3_Sub2.java deleted file mode 100644 index 1388c32d0..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub2.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.data.HashTable; - -final class Class3_Sub2 extends Linkable { - - static int anInt2218 = -1; - static HashTable aHashTable_2220 = new HashTable(32); - Class25[] aClass25Array2221 = new Class25[5]; - boolean aBoolean2222; - int anInt2223; - boolean aBoolean2225; - Class35 aClass35_2226; - int anInt2227; - int anInt2228 = 0; - int anInt2229; - Class12 aClass12_2230; - int anInt2231; - int anInt2232; - Class19 aClass19_2233; - Class70 aClass70_2234; - Class3_Sub2 aClass3_Sub2_2235; - boolean aBoolean2236; - int[] anIntArray2237 = new int[5]; - int anInt2238; - int anInt2239; - Class126 aClass126_2240; - int anInt2241; - int anInt2244; - Class72 aClass72_2245; - - Class3_Sub2(int var1, int var2, int var3) { - try { - this.anInt2231 = var3; - this.anInt2238 = this.anInt2244 = var1; - this.anInt2239 = var2; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bj.(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static void method103() { - try { - Client.aHashTable_2194.clear(); - Class81.aClass13_1139.clear(); - Class126.aClass13_1666.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "bj.B(" + (byte) 24 + ')'); - } - } - - static long method104(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - return var3 != null && var3.aClass12_2230 != null ? var3.aClass12_2230.aLong328 : 0L; - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub20.java b/Client/src/main/java/org/runite/client/Class3_Sub20.java deleted file mode 100644 index 743a2955d..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub20.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.data.NodeCache; - -final class Class3_Sub20 extends Linkable { - - static int[] anIntArray2480 = new int[25]; - static RSString aClass94_3220; - static NodeCache aClass47_3801 = new NodeCache(4); - - int anInt2483; - static int wlPacketIndex = 0; - static int paramLanguage = 0; - static int anInt2487; - static int anInt2488 = 0; - int anInt2489; - - static void method388(byte var0) { - try { - //int var1 = 44 / ((-2 - var0) / 52); - if (aClass94_3220 != null) { - Class3_Sub10.method138(aClass94_3220); - aClass94_3220 = null; - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lb.A(" + var0 + ')'); - } - } - - static void method389() { - try { - Class32.method995(); - Class49.aAbstractSprite_812 = null; - Class58.anInt909 = -1; - TextureOperation13.method313((byte) 55); - aClass47_3801.clear(); - ObjectDefinition.aClass136_1413 = new Class136(); - ((Class102) Class51.anInterface2_838).method1618(); - Class68.anInt1032 = 0; - Class68.aClass43Array1021 = new Class43[255]; - Class140_Sub1_Sub1.method1929(); - Class141.method2043(); - Class65.method1240(); - Unsorted.method1250(17, false); - TextureOperation23.method247((byte) 51); - - for (int var1 = 0; 2048 > var1; ++var1) { - Player var2 = Unsorted.players[var1]; - if (null != var2) { - var2.anObject2796 = null; - } - } - - if (HDToolKit.highDetail) { - Class141.method2041(); - Class127_Sub1.method1755(); - } - - Class3_Sub28_Sub9.method581(CacheIndex.fontsIndex, CacheIndex.spritesIndex); - Sprites.method887(CacheIndex.spritesIndex); - Class3_Sub26.aAbstractSprite_2560 = null; - WorldListEntry.aAbstractSprite_3099 = null; - Class50.aAbstractSprite_824 = null; - WorldListEntry.aAbstractSprite_1339 = null; - WorldListEntry.aAbstractSprite_1457 = null; - if (Class143.gameStage == 5) { - Class108.method1656(CacheIndex.spritesIndex, (byte) -60); - } - - if (10 == Class143.gameStage) { - TextureOperation1.method219(false); - } - - if (Class143.gameStage == 30) { - Class117.method1719(25); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "lb.C(" + false + ')'); - } - } - - static void method390(boolean var0, int var1, int var2, int var3, byte var4, int var5, int var6) { - try { - Unsorted.anInt3631 = var3; - TextureOperation25.anInt3414 = var2; - Unsorted.anInt30 = var6; - Class163_Sub2_Sub1.anInt4021 = var1; - Class146.anInt1904 = var5; - if (var0 && Unsorted.anInt3631 >= 100) { - NPC.anInt3995 = 128 * Unsorted.anInt30 + 64; - Class77.anInt1111 = 128 * Class146.anInt1904 + 64; - Class7.anInt2162 = Class121.method1736(WorldListCountry.localPlane, 1, NPC.anInt3995, Class77.anInt1111) + -TextureOperation25.anInt3414; - } - - // int var7 = 76 % ((-79 - var4) / 35); - Class133.anInt1753 = 2; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "lb.D(" + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - Class3_Sub20(int var1, int var2) { - try { - this.anInt2483 = var2; - this.anInt2489 = var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "lb.(" + var1 + ',' + var2 + ')'); - } - } - - static void method392(CacheIndex skinsIndex, CacheIndex animationIndex, CacheIndex skeletonsIndex) { - try { - Class7.skinsReferenceIndex = skinsIndex; - SequenceDefinition.animationReferenceIndex = animationIndex; - Class131.skeletonsReferenceIndex = skeletonsIndex; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "lb.E(" + (skinsIndex != null ? "{...}" : "null") + ',' + (animationIndex != null ? "{...}" : "null") + ',' + -77 + ',' + (skeletonsIndex != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub21.java b/Client/src/main/java/org/runite/client/Class3_Sub21.java deleted file mode 100644 index ce1e09355..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub21.java +++ /dev/null @@ -1,136 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -final class Class3_Sub21 extends Linkable { - - static AudioChannel aAudioChannel_2491; - int anInt2492; - static volatile int anInt2493 = -1; - int anInt2494; - int anInt2495; - int anInt2497; - - - final boolean method393(int var2, int var3) { - try { - - return var3 >= this.anInt2492 && var3 <= this.anInt2495 && this.anInt2494 <= var2 && var2 <= this.anInt2497; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "lh.D(" + (byte) -45 + ',' + var2 + ',' + var3 + ')'); - } - } - - static void method394(int var0, int var1) { - try { - WaterShader.anInt3285 = var0; - Unsorted.method383(-32584, 3); - Unsorted.method383(-32584, 4); - if(var1 <= 83) { - method395(null, -43, -61, -51, 101, -106, -58, true); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "lh.E(" + var0 + ',' + var1 + ')'); - } - } - - static void method395(Class35 var0, int var1, int var2, int var3, int var4, int var5, int var6, boolean var7) { - int var8 = var0.anIntArray627.length; - - int var9; - int var10; - int var11; - int var12; - int var13; - for(var9 = 0; var9 < var8; ++var9) { - var10 = var0.anIntArray627[var9] - Class145.anInt2697; - var11 = var0.anIntArray615[var9] - Unsorted.anInt3657; - var12 = var0.anIntArray618[var9] - TextureOperation13.anInt3363; - var13 = var12 * var3 + var10 * var4 >> 16; - var12 = var12 * var4 - var10 * var3 >> 16; - var10 = var13; - var13 = var11 * var2 - var12 * var1 >> 16; - var12 = var11 * var1 + var12 * var2 >> 16; - if(var12 < 50) { - return; - } - - if(var0.anIntArray616 != null) { - Class35.anIntArray614[var9] = var10; - Class35.anIntArray630[var9] = var13; - Class35.anIntArray628[var9] = var12; - } - - Class35.anIntArray623[var9] = Class51.anInt846 + (var10 << 9) / var12; - Class35.anIntArray622[var9] = Class51.anInt835 + (var13 << 9) / var12; - } - - Class51.anInt850 = 0; - var8 = var0.anIntArray624.length; - - for(var9 = 0; var9 < var8; ++var9) { - var10 = var0.anIntArray624[var9]; - var11 = var0.anIntArray617[var9]; - var12 = var0.anIntArray613[var9]; - var13 = Class35.anIntArray623[var10]; - int var14 = Class35.anIntArray623[var11]; - int var15 = Class35.anIntArray623[var12]; - int var16 = Class35.anIntArray622[var10]; - int var17 = Class35.anIntArray622[var11]; - int var18 = Class35.anIntArray622[var12]; - if((var13 - var14) * (var18 - var17) - (var16 - var17) * (var15 - var14) > 0) { - if(TextureOperation37.aBoolean3261 && TextureOperation34.method185(Class49.anInt819 + Class51.anInt846, TextureOperation18.anInt4039 + Class51.anInt835, var16, var17, var18, var13, var14, var15)) { - Class27.anInt515 = var5; - Unsorted.anInt999 = var6; - } - - if(!HDToolKit.highDetail && !var7) { - Class51.aBoolean849 = var13 < 0 || var14 < 0 || var15 < 0 || var13 > Class51.anInt847 || var14 > Class51.anInt847 || var15 > Class51.anInt847; - - if(var0.anIntArray616 != null && var0.anIntArray616[var9] != -1) { - if(Unsorted.aBoolean3275) { - if(var0.aBoolean629) { - Class51.method1135(var16, var17, var18, var13, var14, var15, var0.anIntArray625[var9], var0.anIntArray632[var9], var0.anIntArray631[var9], Class35.anIntArray614[0], Class35.anIntArray614[1], Class35.anIntArray614[3], Class35.anIntArray630[0], Class35.anIntArray630[1], Class35.anIntArray630[3], Class35.anIntArray628[0], Class35.anIntArray628[1], Class35.anIntArray628[3], var0.anIntArray616[var9]); - } else { - Class51.method1135(var16, var17, var18, var13, var14, var15, var0.anIntArray625[var9], var0.anIntArray632[var9], var0.anIntArray631[var9], Class35.anIntArray614[var10], Class35.anIntArray614[var11], Class35.anIntArray614[var12], Class35.anIntArray630[var10], Class35.anIntArray630[var11], Class35.anIntArray630[var12], Class35.anIntArray628[var10], Class35.anIntArray628[var11], Class35.anIntArray628[var12], var0.anIntArray616[var9]); - } - } else { - int var19 = Class51.anInterface2_838.method15(var0.anIntArray616[var9], 65535); - Class51.method1154(var16, var17, var18, var13, var14, var15, LoginHandler.method1753(var19, var0.anIntArray625[var9]), LoginHandler.method1753(var19, var0.anIntArray632[var9]), LoginHandler.method1753(var19, var0.anIntArray631[var9])); - } - } else if(var0.anIntArray625[var9] != 12345678) { - Class51.method1154(var16, var17, var18, var13, var14, var15, var0.anIntArray625[var9], var0.anIntArray632[var9], var0.anIntArray631[var9]); - } - } - } - } - - } - - static void method397(byte var0) { - try { - Class114.aReferenceCache_1569.clear(); - Class3_Sub15.aReferenceCache_2428.clear(); - if(var0 != -41) { - method394(14, 52); - } - - Unsorted.aReferenceCache_743.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lh.C(" + var0 + ')'); - } - } - - Class3_Sub21(int var1, int var2, int var3, int var4) { - try { - this.anInt2497 = var4; - this.anInt2494 = var2; - this.anInt2495 = var3; - this.anInt2492 = var1; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "lh.(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub22.java b/Client/src/main/java/org/runite/client/Class3_Sub22.java deleted file mode 100644 index 7828366fa..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub22.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.runite.client; - - - -import org.rs09.client.Linkable; -import org.rs09.client.config.GameConfig; - -import javax.media.opengl.GL; - - -final class Class3_Sub22 extends Linkable { - - static int anInt2500; - int anInt2501; - int anInt2502; - int anInt2503; - Class166 aClass166_2504; - static Class49 aClass49_2505 = new Class49(); - int anInt2506; - Class3_Sub24_Sub1 aClass3_Sub24_Sub1_2507; - int anInt2508; - PcmSound aPcmSound_2509; - int anInt2510; - int anInt2511; - int anInt2512; - int anInt2513; - int anInt2514; - int anInt2515; - int anInt2516; - int anInt2517; - int anInt2519; - int anInt2520; - static byte[][] aByteArrayArray2521; - int anInt2522; - int anInt2523; - Class3_Sub15 aClass3_Sub15_2527; - - - static void method398(int var0, int var1, int var2, int var3, int var4, byte[][][] var5, int[] var6, int[] var7, int[] var8, int[] var9, int[] var10, int var11, byte var12, int var13, int var14) { - if(var0 < 0) { - var0 = 0; - } else if(var0 >= Unsorted.anInt1234 * 128) { - var0 = Unsorted.anInt1234 * 128 - 1; - } - - if(var2 < 0) { - var2 = 0; - } else if(var2 >= TextureOperation17.anInt3179 * 128) { - var2 = TextureOperation17.anInt3179 * 128 - 1; - } - - Class60.anInt936 = Class51.anIntArray840[var3]; - Unsorted.anInt1037 = Class51.anIntArray851[var3]; - TextureOperation25.anInt3417 = Class51.anIntArray840[var4]; - Class145.anInt3153 = Class51.anIntArray851[var4]; - Class145.anInt2697 = var0; - Unsorted.anInt3657 = var1; - TextureOperation13.anInt3363 = var2; - Class97.anInt1375 = var0 / 128; - Class145.anInt3340 = var2 / 128; - Class163_Sub1_Sub1.anInt4006 = Class97.anInt1375 - TextureOperation8.anInt3466; - if(Class163_Sub1_Sub1.anInt4006 < 0) { - Class163_Sub1_Sub1.anInt4006 = 0; - } - - Unsorted.anInt3603 = Class145.anInt3340 - TextureOperation8.anInt3466; - if(Unsorted.anInt3603 < 0) { - Unsorted.anInt3603 = 0; - } - - Unsorted.anInt67 = Class97.anInt1375 + TextureOperation8.anInt3466; - if(Unsorted.anInt67 > Unsorted.anInt1234) { - Unsorted.anInt67 = Unsorted.anInt1234; - } - - Class126.anInt1665 = Class145.anInt3340 + TextureOperation8.anInt3466; - if(Class126.anInt1665 > TextureOperation17.anInt3179) { - Class126.anInt1665 = TextureOperation17.anInt3179; - } - - short var15; - if(HDToolKit.highDetail) { - var15 = (short) GameConfig.RENDER_DISTANCE_VALUE; - } else { - var15 = 3500; - } - - int var17; - int var16; - for(var16 = 0; var16 < TextureOperation8.anInt3466 + TextureOperation8.anInt3466 + 2; ++var16) { - for(var17 = 0; var17 < TextureOperation8.anInt3466 + TextureOperation8.anInt3466 + 2; ++var17) { - int var18 = (var16 - TextureOperation8.anInt3466 << 7) - (Class145.anInt2697 & 127); - int var19 = (var17 - TextureOperation8.anInt3466 << 7) - (TextureOperation13.anInt3363 & 127); - int var20 = Class97.anInt1375 - TextureOperation8.anInt3466 + var16; - int var21 = Class145.anInt3340 - TextureOperation8.anInt3466 + var17; - if(var20 >= 0 && var21 >= 0 && var20 < Unsorted.anInt1234 && var21 < TextureOperation17.anInt3179) { - int var22; - if(Unsorted.anIntArrayArrayArray3605 == null) { - var22 = Class58.anIntArrayArrayArray914[0][var20][var21] - Unsorted.anInt3657 + 128; - } else { - var22 = Unsorted.anIntArrayArrayArray3605[0][var20][var21] - Unsorted.anInt3657 + 128; - } - - int var23 = Class58.anIntArrayArrayArray914[3][var20][var21] - Unsorted.anInt3657 - 1000; - Class49.aBooleanArrayArray814[var16][var17] = Class91.method1495(var18, var23, var22, var19, var15); - } else { - Class49.aBooleanArrayArray814[var16][var17] = false; - } - } - } - - for(var16 = 0; var16 < TextureOperation8.anInt3466 + TextureOperation8.anInt3466 + 1; ++var16) { - for(var17 = 0; var17 < TextureOperation8.anInt3466 + TextureOperation8.anInt3466 + 1; ++var17) { - Class23.aBooleanArrayArray457[var16][var17] = Class49.aBooleanArrayArray814[var16][var17] || Class49.aBooleanArrayArray814[var16 + 1][var17] || Class49.aBooleanArrayArray814[var16][var17 + 1] || Class49.aBooleanArrayArray814[var16 + 1][var17 + 1]; - } - } - - Class72.anIntArray3045 = var6; - Unsorted.anIntArray1083 = var7; - Class52.anIntArray859 = var8; - Class75_Sub4.anIntArray2663 = var9; - Unsorted.anIntArray39 = var10; - Class72.method1294(); - if(Class166.aClass3_Sub2ArrayArrayArray2065 != null) { - Class167.method2264(true); - Class146.method2083(var0, var1, var2, null, 0, (byte)0, var13, var14); - if(HDToolKit.highDetail) { - TextureOperation23.aBoolean3207 = false; - Unsorted.method551(0, 0); - Class92.method1512(null); - Class68.method1265(); - } - - Class167.method2264(false); - } - - Class146.method2083(var0, var1, var2, var5, var11, var12, var13, var14); - } - - static void method400(long var0) { - try { - if((long) 0 != var0) { - TextureOperation12.outgoingBuffer.putOpcode(104); - TextureOperation12.outgoingBuffer.writeLong(var0); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "mf.F(" + var0 + ',' + 0 + ')'); - } - } - - final void method401() { - try { - this.aClass166_2504 = null; - this.aPcmSound_2509 = null; - this.aClass3_Sub24_Sub1_2507 = null; - this.aClass3_Sub15_2527 = null; - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "mf.A(" + 221 + ')'); - } - } - - static void method403() { - GL var0 = HDToolKit.gl; - var0.glDisableClientState('\u8076'); - HDToolKit.method1837(false); - var0.glDisable(2929); - var0.glPushAttrib(128); - var0.glFogf(2915, 3072.0F); - HDToolKit.depthBufferWritingDisabled(); - - for(int var1 = 0; var1 < Client.aClass3_Sub11ArrayArray2199[0].length; ++var1) { - Class3_Sub11 var2 = Client.aClass3_Sub11ArrayArray2199[0][var1]; - if(var2.anInt2351 >= 0 && Class51.anInterface2_838.method18(var2.anInt2351, 255) == 4) { - var0.glColor4fv(Class114.method1705(var2.anInt2355, 0), 0); - float var3 = 201.5F - (var2.aBoolean2364?1.0F:0.5F); - var2.method149(Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, var3, true); - } - } - - var0.glEnableClientState('\u8076'); - HDToolKit.method1846(); - var0.glEnable(2929); - var0.glPopAttrib(); - HDToolKit.method1830(); - } - - static Class75_Sub4 method404(DataBuffer var1) { - try { - return new Class75_Sub4(var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readMedium(), var1.readMedium(), var1.readUnsignedByte()); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "mf.C(" + (byte) -110 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub23.java b/Client/src/main/java/org/runite/client/Class3_Sub23.java deleted file mode 100644 index aff27a6c6..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub23.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.rendering.Toolkit; - -import java.util.Objects; - -public final class Class3_Sub23 extends Linkable { - - static RSString aClass94_3080 = Class95.method1586(); - int anInt2531; - int anInt2532; - public static int[] anIntArray2533; - static int anInt2535 = -2; - static CacheIndex aClass153_2536; - static int anInt2537 = 0; - static boolean[] aBooleanArray2538 = new boolean[]{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false}; - int anInt2539; - static Class3_Sub11[][] aClass3_Sub11ArrayArray2542; - - - static RSString method407(int var0, boolean var1, int var2, long var3) { - try { - RSString var6 = Unsorted.emptyString(0); - if (var3 < 0) { - var3 = -var3; - Objects.requireNonNull(var6).append(RSString.parse(")2")); - } - - RSString var8 = TextCore.aClass94_1880; - RSString var7 = TextCore.aClass94_341; - if (var0 == 1) { - var8 = TextCore.aClass94_341; - var7 = TextCore.aClass94_1880; - } - - if (var0 == 2) { - var7 = TextCore.aClass94_1880; - var8 = aClass94_3080; - } - - if (var0 == 3) { - var8 = TextCore.aClass94_341; - var7 = TextCore.aClass94_1880; - } - - RSString var10 = Unsorted.emptyString(0); - - int var11; - for (var11 = 0; var2 > var11; ++var11) { - Objects.requireNonNull(var10).append(RSString.stringAnimator((int) (var3 % 10L))); - var3 /= 10L; - } - - var11 = 0; - RSString var9; - if (var3 == 0L) { - var9 = RSString.parse("0"); - } else { - RSString var12; - for (var12 = Unsorted.emptyString(0); var3 > 0L; var3 /= 10L) { - if (var1 && var11 != 0 && var11 % 3 == 0) { - var12.append(var8); - } - - Objects.requireNonNull(var12).append(RSString.stringAnimator((int) (var3 % 10L))); - ++var11; - } - - var9 = var12; - } - - if (Objects.requireNonNull(var10).length() > 0) { - var10.append(var7); - } - - return RSString.stringCombiner(new RSString[]{var6, Objects.requireNonNull(var9).method1544(true), var10.method1544(true)}); - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "oj.C(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + 2 + ')'); - } - } - - static int method408(int var0, byte var1, int var2, int[][] var3, int var4, int var5) { - try { - int var6 = var0 * var3[1 + var4][var2] + (128 - var0) * var3[var4][var2] >> 7; - int var7 = var3[var4][1 + var2] * (-var0 + 128) + var3[var4 - -1][var2 - -1] * var0 >> 7; - return var6 * (128 + -var5) - -(var5 * var7) >> 7; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "oj.A(" + var0 + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + var5 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub24.java b/Client/src/main/java/org/runite/client/Class3_Sub24.java deleted file mode 100644 index b1bc8d500..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub24.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -abstract class Class3_Sub24 extends Linkable { - - int anInt2543; - Sound aSound_2544; - volatile boolean aBoolean2545 = true; - Class3_Sub24 aClass3_Sub24_2546; - - - abstract int method409(); - - final void method410(int[] var1, int var2, int var3) { - if(this.aBoolean2545) { - this.method413(var1, var2, var3); - } else { - this.method415(var3); - } - - } - - abstract Class3_Sub24 method411(); - - int method412() { - return 255; - } - - abstract void method413(int[] var1, int var2, int var3); - - abstract Class3_Sub24 method414(); - - abstract void method415(int var1); -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub24_Sub1.java b/Client/src/main/java/org/runite/client/Class3_Sub24_Sub1.java deleted file mode 100644 index 993c8a198..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub24_Sub1.java +++ /dev/null @@ -1,1443 +0,0 @@ -package org.runite.client; - -final class Class3_Sub24_Sub1 extends Class3_Sub24 { - - private int anInt3470; - private int anInt3471; - private int anInt3472; - private final int anInt3473; - private int anInt3474; - private int anInt3475; - private int anInt3476; - private final boolean aBoolean3477; - private int anInt3478; - private int anInt3479; - private int anInt3480; - private int anInt3481; - private final int anInt3482; - private int anInt3483; - private int anInt3484; - - - final synchronized void method413(int[] var1, int var2, int var3) { - if(this.anInt3471 == 0 && this.anInt3480 == 0) { - this.method415(var3); - } else { - PcmSound var4 = (PcmSound)this.aSound_2544; - int var5 = this.anInt3473 << 8; - int var6 = this.anInt3482 << 8; - int var7 = var4.samples.length << 8; - int var8 = var6 - var5; - if(var8 <= 0) { - this.anInt3479 = 0; - } - - int var9 = var2; - var3 += var2; - if(this.anInt3474 < 0) { - if(this.anInt3470 <= 0) { - this.method439(); - this.unlink(); - return; - } - - this.anInt3474 = 0; - } - - if(this.anInt3474 >= var7) { - if(this.anInt3470 >= 0) { - this.method439(); - this.unlink(); - return; - } - - this.anInt3474 = var7 - 1; - } - - if(this.anInt3479 < 0) { - if(this.aBoolean3477) { - if(this.anInt3470 < 0) { - var9 = this.method436(var1, var2, var5, var3, var4.samples[this.anInt3473]); - if(this.anInt3474 >= var5) { - return; - } - - this.anInt3474 = var5 + var5 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - } - - while(true) { - var9 = this.method418(var1, var9, var6, var3, var4.samples[this.anInt3482 - 1]); - if(this.anInt3474 < var6) { - return; - } - - this.anInt3474 = var6 + var6 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - var9 = this.method436(var1, var9, var5, var3, var4.samples[this.anInt3473]); - if(this.anInt3474 >= var5) { - return; - } - - this.anInt3474 = var5 + var5 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - } - } else if(this.anInt3470 < 0) { - while(true) { - var9 = this.method436(var1, var9, var5, var3, var4.samples[this.anInt3482 - 1]); - if(this.anInt3474 >= var5) { - return; - } - - this.anInt3474 = var6 - 1 - (var6 - 1 - this.anInt3474) % var8; - } - } else { - while(true) { - var9 = this.method418(var1, var9, var6, var3, var4.samples[this.anInt3473]); - if(this.anInt3474 < var6) { - return; - } - - this.anInt3474 = var5 + (this.anInt3474 - var5) % var8; - } - } - } else { - if(this.anInt3479 > 0) { - if(this.aBoolean3477) { - label134: { - if(this.anInt3470 < 0) { - var9 = this.method436(var1, var2, var5, var3, var4.samples[this.anInt3473]); - if(this.anInt3474 >= var5) { - return; - } - - this.anInt3474 = var5 + var5 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - if(--this.anInt3479 == 0) { - break label134; - } - } - - do { - var9 = this.method418(var1, var9, var6, var3, var4.samples[this.anInt3482 - 1]); - if(this.anInt3474 < var6) { - return; - } - - this.anInt3474 = var6 + var6 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - if(--this.anInt3479 == 0) { - break; - } - - var9 = this.method436(var1, var9, var5, var3, var4.samples[this.anInt3473]); - if(this.anInt3474 >= var5) { - return; - } - - this.anInt3474 = var5 + var5 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - } while(--this.anInt3479 != 0); - } - } else { - int var10; - if(this.anInt3470 < 0) { - while(true) { - var9 = this.method436(var1, var9, var5, var3, var4.samples[this.anInt3482 - 1]); - if(this.anInt3474 >= var5) { - return; - } - - var10 = (var6 - 1 - this.anInt3474) / var8; - if(var10 >= this.anInt3479) { - this.anInt3474 += var8 * this.anInt3479; - this.anInt3479 = 0; - break; - } - - this.anInt3474 += var8 * var10; - this.anInt3479 -= var10; - } - } else { - while(true) { - var9 = this.method418(var1, var9, var6, var3, var4.samples[this.anInt3473]); - if(this.anInt3474 < var6) { - return; - } - - var10 = (this.anInt3474 - var5) / var8; - if(var10 >= this.anInt3479) { - this.anInt3474 -= var8 * this.anInt3479; - this.anInt3479 = 0; - break; - } - - this.anInt3474 -= var8 * var10; - this.anInt3479 -= var10; - } - } - } - } - - if(this.anInt3470 < 0) { - this.method436(var1, var9, 0, var3, 0); - if(this.anInt3474 < 0) { - this.anInt3474 = -1; - this.method439(); - this.unlink(); - } - } else { - this.method418(var1, var9, var7, var3, 0); - if(this.anInt3474 >= var7) { - this.anInt3474 = var7; - this.method439(); - this.unlink(); - } - } - - } - } - } - - private boolean method416() { - int var1 = this.anInt3471; - int var2; - int var3; - if(var1 == Integer.MIN_VALUE) { - var3 = 0; - var2 = 0; - var1 = 0; - } else { - var2 = method452(var1, this.anInt3478); - var3 = method454(var1, this.anInt3478); - } - - if(this.anInt3476 == var1 && this.anInt3484 == var2 && this.anInt3481 == var3) { - if(this.anInt3471 == Integer.MIN_VALUE) { - this.anInt3471 = 0; - this.anInt3476 = this.anInt3484 = this.anInt3481 = 0; - this.unlink(); - return false; - } else { - this.method449(); - return true; - } - } else { - if(this.anInt3476 < var1) { - this.anInt3472 = 1; - this.anInt3480 = var1 - this.anInt3476; - } else if(this.anInt3476 > var1) { - this.anInt3472 = -1; - this.anInt3480 = this.anInt3476 - var1; - } else { - this.anInt3472 = 0; - } - - if(this.anInt3484 < var2) { - this.anInt3475 = 1; - if(this.anInt3480 == 0 || this.anInt3480 > var2 - this.anInt3484) { - this.anInt3480 = var2 - this.anInt3484; - } - } else if(this.anInt3484 > var2) { - this.anInt3475 = -1; - if(this.anInt3480 == 0 || this.anInt3480 > this.anInt3484 - var2) { - this.anInt3480 = this.anInt3484 - var2; - } - } else { - this.anInt3475 = 0; - } - - if(this.anInt3481 < var3) { - this.anInt3483 = 1; - if(this.anInt3480 == 0 || this.anInt3480 > var3 - this.anInt3481) { - this.anInt3480 = var3 - this.anInt3481; - } - } else if(this.anInt3481 > var3) { - this.anInt3483 = -1; - if(this.anInt3480 == 0 || this.anInt3480 > this.anInt3481 - var3) { - this.anInt3480 = this.anInt3481 - var3; - } - } else { - this.anInt3483 = 0; - } - - return true; - } - } - - final synchronized void method417(int var1) { - if(var1 == 0) { - this.method430(); - this.unlink(); - } else if(this.anInt3484 == 0 && this.anInt3481 == 0) { - this.anInt3480 = 0; - this.anInt3471 = 0; - this.anInt3476 = 0; - this.unlink(); - } else { - int var2 = -this.anInt3476; - if(this.anInt3476 > var2) { - var2 = this.anInt3476; - } - - if(-this.anInt3484 > var2) { - var2 = -this.anInt3484; - } - - if(this.anInt3484 > var2) { - var2 = this.anInt3484; - } - - if(-this.anInt3481 > var2) { - var2 = -this.anInt3481; - } - - if(this.anInt3481 > var2) { - var2 = this.anInt3481; - } - - if(var1 > var2) { - var1 = var2; - } - - this.anInt3480 = var1; - this.anInt3471 = Integer.MIN_VALUE; - this.anInt3472 = -this.anInt3476 / var1; - this.anInt3475 = -this.anInt3484 / var1; - this.anInt3483 = -this.anInt3481 / var1; - } - } - - private int method418(int[] var1, int var2, int var3, int var4, int var5) { - while(true) { - if(this.anInt3480 > 0) { - int var6 = var2 + this.anInt3480; - if(var6 > var4) { - var6 = var4; - } - - this.anInt3480 += var2; - if(this.anInt3470 == 256 && (this.anInt3474 & 0xFF) == 0) { - if(AudioChannel.stereo) { - var2 = method426(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3484, this.anInt3481, this.anInt3475, this.anInt3483, var6, var3, this); - } else { - var2 = method428(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3476, this.anInt3472, var6, var3, this); - } - } else if(AudioChannel.stereo) { - var2 = method421(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3484, this.anInt3481, this.anInt3475, this.anInt3483, var6, var3, this, this.anInt3470, var5); - } else { - var2 = method422(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3476, this.anInt3472, var6, var3, this, this.anInt3470, var5); - } - - this.anInt3480 -= var2; - if(this.anInt3480 != 0) { - return var2; - } - - if(this.method416()) { - continue; - } - - return var4; - } - - if(this.anInt3470 == 256 && (this.anInt3474 & 0xFF) == 0) { - if(AudioChannel.stereo) { - return method420(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3484, this.anInt3481, var4, var3, this); - } - - return method424(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3476, var4, var3, this); - } - - if(AudioChannel.stereo) { - return method433(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3484, this.anInt3481, var4, var3, this, this.anInt3470, var5); - } - - return method455(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3476, var4, var3, this, this.anInt3470, var5); - } - } - - final synchronized void method419(int var1) { - this.method441(var1 << 6, this.method451()); - } - - final int method412() { - int var1 = this.anInt3476 * 3 >> 6; - var1 = (var1 ^ var1 >> 31) + (var1 >>> 31); - if(this.anInt3479 == 0) { - var1 -= var1 * this.anInt3474 / (((PcmSound)this.aSound_2544).samples.length << 8); - } else if(this.anInt3479 >= 0) { - var1 -= var1 * this.anInt3473 / ((PcmSound)this.aSound_2544).samples.length; - } - - return var1 > 255?255:var1; - } - - private static int method420(byte[] var1, int[] var2, int var3, int var4, int var5, int var6, int var8, int var9, Class3_Sub24_Sub1 var10) { - var3 >>= 8; - var9 >>= 8; - var5 <<= 2; - var6 <<= 2; - int var7; - if((var7 = var4 + var9 - var3) > var8) { - var7 = var8; - } - - var4 <<= 1; - var7 <<= 1; - - byte var11; - int var10001; - for(var7 -= 6; var4 < var7; var2[var10001] += var11 * var6) { - var11 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - var2[var10001] += var11 * var6; - var11 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - var2[var10001] += var11 * var6; - var11 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - var2[var10001] += var11 * var6; - var11 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - } - - for(var7 += 6; var4 < var7; var2[var10001] += var11 * var6) { - var11 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - } - - var10.anInt3474 = var3 << 8; - return var4 >> 1; - } - - private static int method421(byte[] var2, int[] var3, int var4, int var5, int var6, int var7, int var8, int var9, int var11, int var12, Class3_Sub24_Sub1 var13, int var14, int var15) { - var13.anInt3476 -= var13.anInt3472 * var5; - int var10; - if(var14 == 0 || (var10 = var5 + (var12 - var4 + var14 - 257) / var14) > var11) { - var10 = var11; - } - - var5 <<= 1; - - int var10001; - byte var16; - int var1; - int var0; - for(var10 <<= 1; var5 < var10; var4 += var14) { - var1 = var4 >> 8; - var16 = var2[var1]; - var0 = (var16 << 8) + (var2[var1 + 1] - var16) * (var4 & 0xFF); - var10001 = var5++; - var3[var10001] += var0 * var6 >> 6; - var6 += var8; - var10001 = var5++; - var3[var10001] += var0 * var7 >> 6; - var7 += var9; - } - - if(var14 == 0 || (var10 = (var5 >> 1) + (var12 - var4 + var14 - 1) / var14) > var11) { - var10 = var11; - } - - var10 <<= 1; - - for(var1 = var15; var5 < var10; var4 += var14) { - var16 = var2[var4 >> 8]; - var0 = (var16 << 8) + (var1 - var16) * (var4 & 0xFF); - var10001 = var5++; - var3[var10001] += var0 * var6 >> 6; - var6 += var8; - var10001 = var5++; - var3[var10001] += var0 * var7 >> 6; - var7 += var9; - } - - var5 >>= 1; - var13.anInt3476 += var13.anInt3472 * var5; - var13.anInt3484 = var6; - var13.anInt3481 = var7; - var13.anInt3474 = var4; - return var5; - } - - final int method409() { - return this.anInt3471 == 0 && this.anInt3480 == 0?0:1; - } - - private static int method422(byte[] var2, int[] var3, int var4, int var5, int var6, int var7, int var9, int var10, Class3_Sub24_Sub1 var11, int var12, int var13) { - var11.anInt3484 -= var11.anInt3475 * var5; - var11.anInt3481 -= var11.anInt3483 * var5; - int var8; - if(var12 == 0 || (var8 = var5 + (var10 - var4 + var12 - 257) / var12) > var9) { - var8 = var9; - } - - int var10001; - byte var14; - int var1; - while(var5 < var8) { - var1 = var4 >> 8; - var14 = var2[var1]; - var10001 = var5++; - var3[var10001] += ((var14 << 8) + (var2[var1 + 1] - var14) * (var4 & 0xFF)) * var6 >> 6; - var6 += var7; - var4 += var12; - } - - if(var12 == 0 || (var8 = var5 + (var10 - var4 + var12 - 1) / var12) > var9) { - var8 = var9; - } - - for(var1 = var13; var5 < var8; var4 += var12) { - var14 = var2[var4 >> 8]; - var10001 = var5++; - var3[var10001] += ((var14 << 8) + (var1 - var14) * (var4 & 0xFF)) * var6 >> 6; - var6 += var7; - } - - var11.anInt3484 += var11.anInt3475 * var5; - var11.anInt3481 += var11.anInt3483 * var5; - var11.anInt3476 = var6; - var11.anInt3474 = var4; - return var5; - } - - private static int method423(byte[] var2, int[] var3, int var4, int var5, int var6, int var8, int var9, Class3_Sub24_Sub1 var10, int var11, int var12) { - int var7; - if(var11 == 0 || (var7 = var5 + (var9 + 256 - var4 + var11) / var11) > var8) { - var7 = var8; - } - - int var10001; - int var1; - while(var5 < var7) { - var1 = var4 >> 8; - byte var13 = var2[var1 - 1]; - var10001 = var5++; - var3[var10001] += ((var13 << 8) + (var2[var1] - var13) * (var4 & 0xFF)) * var6 >> 6; - var4 += var11; - } - - if(var11 == 0 || (var7 = var5 + (var9 - var4 + var11) / var11) > var8) { - var7 = var8; - } - - for(var1 = var11; var5 < var7; var4 += var1) { - var10001 = var5++; - var3[var10001] += ((var12 << 8) + (var2[var4 >> 8] - var12) * (var4 & 0xFF)) * var6 >> 6; - } - - var10.anInt3474 = var4; - return var5; - } - - private static int method424(byte[] var0, int[] var1, int var2, int var3, int var4, int var6, int var7, Class3_Sub24_Sub1 var8) { - var2 >>= 8; - var7 >>= 8; - var4 <<= 2; - int var5; - if((var5 = var3 + var7 - var2) > var6) { - var5 = var6; - } - - int var10001; - for(var5 -= 3; var3 < var5; var1[var10001] += var0[var2++] * var4) { - var10001 = var3++; - var1[var10001] += var0[var2++] * var4; - var10001 = var3++; - var1[var10001] += var0[var2++] * var4; - var10001 = var3++; - var1[var10001] += var0[var2++] * var4; - var10001 = var3++; - } - - for(var5 += 3; var3 < var5; var1[var10001] += var0[var2++] * var4) { - var10001 = var3++; - } - - var8.anInt3474 = var2 << 8; - return var3; - } - - final synchronized int method425() { - return this.anInt3471 == Integer.MIN_VALUE?0:this.anInt3471; - } - - private static int method426(byte[] var1, int[] var2, int var3, int var4, int var5, int var6, int var7, int var8, int var10, int var11, Class3_Sub24_Sub1 var12) { - var3 >>= 8; - var11 >>= 8; - var5 <<= 2; - var6 <<= 2; - var7 <<= 2; - var8 <<= 2; - int var9; - if((var9 = var4 + var11 - var3) > var10) { - var9 = var10; - } - - var12.anInt3476 += var12.anInt3472 * (var9 - var4); - var4 <<= 1; - var9 <<= 1; - - int var10001; - byte var13; - for(var9 -= 6; var4 < var9; var6 += var8) { - var13 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - var6 += var8; - var13 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - var6 += var8; - var13 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - var6 += var8; - var13 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - } - - for(var9 += 6; var4 < var9; var6 += var8) { - var13 = var1[var3++]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - } - - var12.anInt3484 = var5 >> 2; - var12.anInt3481 = var6 >> 2; - var12.anInt3474 = var3 << 8; - return var4 >> 1; - } - - private static int method427(byte[] var0, int[] var1, int var2, int var3, int var4, int var5, int var7, int var8, Class3_Sub24_Sub1 var9) { - var2 >>= 8; - var8 >>= 8; - var4 <<= 2; - var5 <<= 2; - int var6; - if((var6 = var3 + var2 - (var8 - 1)) > var7) { - var6 = var7; - } - - var9.anInt3484 += var9.anInt3475 * (var6 - var3); - var9.anInt3481 += var9.anInt3483 * (var6 - var3); - - int var10001; - for(var6 -= 3; var3 < var6; var4 += var5) { - var10001 = var3++; - var1[var10001] += var0[var2--] * var4; - var4 += var5; - var10001 = var3++; - var1[var10001] += var0[var2--] * var4; - var4 += var5; - var10001 = var3++; - var1[var10001] += var0[var2--] * var4; - var4 += var5; - var10001 = var3++; - var1[var10001] += var0[var2--] * var4; - } - - for(var6 += 3; var3 < var6; var4 += var5) { - var10001 = var3++; - var1[var10001] += var0[var2--] * var4; - } - - var9.anInt3476 = var4 >> 2; - var9.anInt3474 = var2 << 8; - return var3; - } - - private static int method428(byte[] var0, int[] var1, int var2, int var3, int var4, int var5, int var7, int var8, Class3_Sub24_Sub1 var9) { - var2 >>= 8; - var8 >>= 8; - var4 <<= 2; - var5 <<= 2; - int var6; - if((var6 = var3 + var8 - var2) > var7) { - var6 = var7; - } - - var9.anInt3484 += var9.anInt3475 * (var6 - var3); - var9.anInt3481 += var9.anInt3483 * (var6 - var3); - - int var10001; - for(var6 -= 3; var3 < var6; var4 += var5) { - var10001 = var3++; - var1[var10001] += var0[var2++] * var4; - var4 += var5; - var10001 = var3++; - var1[var10001] += var0[var2++] * var4; - var4 += var5; - var10001 = var3++; - var1[var10001] += var0[var2++] * var4; - var4 += var5; - var10001 = var3++; - var1[var10001] += var0[var2++] * var4; - } - - for(var6 += 3; var3 < var6; var4 += var5) { - var10001 = var3++; - var1[var10001] += var0[var2++] * var4; - } - - var9.anInt3476 = var4 >> 2; - var9.anInt3474 = var2 << 8; - return var3; - } - - final synchronized void method415(int var1) { - if(this.anInt3480 > 0) { - if(var1 >= this.anInt3480) { - if(this.anInt3471 == Integer.MIN_VALUE) { - this.anInt3471 = 0; - this.anInt3476 = this.anInt3484 = this.anInt3481 = 0; - this.unlink(); - var1 = this.anInt3480; - } - - this.anInt3480 = 0; - this.method449(); - } else { - this.anInt3476 += this.anInt3472 * var1; - this.anInt3484 += this.anInt3475 * var1; - this.anInt3481 += this.anInt3483 * var1; - this.anInt3480 -= var1; - } - } - - PcmSound var2 = (PcmSound)this.aSound_2544; - int var3 = this.anInt3473 << 8; - int var4 = this.anInt3482 << 8; - int var5 = var2.samples.length << 8; - int var6 = var4 - var3; - if(var6 <= 0) { - this.anInt3479 = 0; - } - - if(this.anInt3474 < 0) { - if(this.anInt3470 <= 0) { - this.method439(); - this.unlink(); - return; - } - - this.anInt3474 = 0; - } - - if(this.anInt3474 >= var5) { - if(this.anInt3470 >= 0) { - this.method439(); - this.unlink(); - return; - } - - this.anInt3474 = var5 - 1; - } - - this.anInt3474 += this.anInt3470 * var1; - if(this.anInt3479 < 0) { - if(this.aBoolean3477) { - if(this.anInt3470 < 0) { - if(this.anInt3474 >= var3) { - return; - } - - this.anInt3474 = var3 + var3 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - } - - while(this.anInt3474 >= var4) { - this.anInt3474 = var4 + var4 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - if(this.anInt3474 >= var3) { - return; - } - - this.anInt3474 = var3 + var3 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - } - - } else if (this.anInt3470 < 0) { - if (this.anInt3474 >= var3) { - return; - } - - this.anInt3474 = var4 - 1 - (var4 - 1 - this.anInt3474) % var6; - } else { - if (this.anInt3474 < var4) { - return; - } - - this.anInt3474 = var3 + (this.anInt3474 - var3) % var6; - } - } else { - if(this.anInt3479 > 0) { - if(this.aBoolean3477) { - label122: { - if(this.anInt3470 < 0) { - if(this.anInt3474 >= var3) { - return; - } - - this.anInt3474 = var3 + var3 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - if(--this.anInt3479 == 0) { - break label122; - } - } - - do { - if(this.anInt3474 < var4) { - return; - } - - this.anInt3474 = var4 + var4 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - if(--this.anInt3479 == 0) { - break; - } - - if(this.anInt3474 >= var3) { - return; - } - - this.anInt3474 = var3 + var3 - 1 - this.anInt3474; - this.anInt3470 = -this.anInt3470; - } while(--this.anInt3479 != 0); - } - } else { - label132: { - int var7; - if(this.anInt3470 < 0) { - if(this.anInt3474 >= var3) { - return; - } - - var7 = (var4 - 1 - this.anInt3474) / var6; - if(var7 >= this.anInt3479) { - this.anInt3474 += var6 * this.anInt3479; - this.anInt3479 = 0; - break label132; - } - - this.anInt3474 += var6 * var7; - } else { - if(this.anInt3474 < var4) { - return; - } - - var7 = (this.anInt3474 - var3) / var6; - if(var7 >= this.anInt3479) { - this.anInt3474 -= var6 * this.anInt3479; - this.anInt3479 = 0; - break label132; - } - - this.anInt3474 -= var6 * var7; - } - this.anInt3479 -= var7; - - return; - } - } - } - - if(this.anInt3470 < 0) { - if(this.anInt3474 < 0) { - this.anInt3474 = -1; - this.method439(); - this.unlink(); - } - } else if(this.anInt3474 >= var5) { - this.anInt3474 = var5; - this.method439(); - this.unlink(); - } - - } - } - - final synchronized void method429(int var1) { - this.anInt3479 = var1; - } - - private synchronized void method430() { - this.method441(0, this.method451()); - } - - final synchronized void method431(int var1, int var2) { - this.method450(var1, var2, this.method451()); - } - - static Class3_Sub24_Sub1 method432(PcmSound var0, int var1, int var2, int var3) { - return var0.samples != null && var0.samples.length != 0?new Class3_Sub24_Sub1(var0, var1, var2, var3):null; - } - - final Class3_Sub24 method411() { - return null; - } - - private static int method433(byte[] var2, int[] var3, int var4, int var5, int var6, int var7, int var9, int var10, Class3_Sub24_Sub1 var11, int var12, int var13) { - int var8; - if(var12 == 0 || (var8 = var5 + (var10 - var4 + var12 - 257) / var12) > var9) { - var8 = var9; - } - - var5 <<= 1; - - int var10001; - byte var14; - int var1; - int var0; - for(var8 <<= 1; var5 < var8; var4 += var12) { - var1 = var4 >> 8; - var14 = var2[var1]; - var0 = (var14 << 8) + (var2[var1 + 1] - var14) * (var4 & 0xFF); - var10001 = var5++; - var3[var10001] += var0 * var6 >> 6; - var10001 = var5++; - var3[var10001] += var0 * var7 >> 6; - } - - if(var12 == 0 || (var8 = (var5 >> 1) + (var10 - var4 + var12 - 1) / var12) > var9) { - var8 = var9; - } - - var8 <<= 1; - - for(var1 = var13; var5 < var8; var4 += var12) { - var14 = var2[var4 >> 8]; - var0 = (var14 << 8) + (var1 - var14) * (var4 & 0xFF); - var10001 = var5++; - var3[var10001] += var0 * var6 >> 6; - var10001 = var5++; - var3[var10001] += var0 * var7 >> 6; - } - - var11.anInt3474 = var4; - return var5 >> 1; - } - - final synchronized void method434(int var1) { - int var2 = ((PcmSound)this.aSound_2544).samples.length << 8; - if(var1 < -1) { - var1 = -1; - } - - if(var1 > var2) { - var1 = var2; - } - - this.anInt3474 = var1; - } - - private static int method435(byte[] var1, int[] var2, int var3, int var4, int var5, int var6, int var7, int var8, int var10, int var11, Class3_Sub24_Sub1 var12) { - var3 >>= 8; - var11 >>= 8; - var5 <<= 2; - var6 <<= 2; - var7 <<= 2; - var8 <<= 2; - int var9; - if((var9 = var4 + var3 - (var11 - 1)) > var10) { - var9 = var10; - } - - var12.anInt3476 += var12.anInt3472 * (var9 - var4); - var4 <<= 1; - var9 <<= 1; - - int var10001; - byte var13; - for(var9 -= 6; var4 < var9; var6 += var8) { - var13 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - var6 += var8; - var13 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - var6 += var8; - var13 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - var6 += var8; - var13 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - } - - for(var9 += 6; var4 < var9; var6 += var8) { - var13 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var13 * var5; - var5 += var7; - var10001 = var4++; - var2[var10001] += var13 * var6; - } - - var12.anInt3484 = var5 >> 2; - var12.anInt3481 = var6 >> 2; - var12.anInt3474 = var3 << 8; - return var4 >> 1; - } - - private int method436(int[] var1, int var2, int var3, int var4, int var5) { - while(true) { - if(this.anInt3480 > 0) { - int var6 = var2 + this.anInt3480; - if(var6 > var4) { - var6 = var4; - } - - this.anInt3480 += var2; - if(this.anInt3470 == -256 && (this.anInt3474 & 0xFF) == 0) { - if(AudioChannel.stereo) { - var2 = method435(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3484, this.anInt3481, this.anInt3475, this.anInt3483, var6, var3, this); - } else { - var2 = method427(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3476, this.anInt3472, var6, var3, this); - } - } else if(AudioChannel.stereo) { - var2 = method440(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3484, this.anInt3481, this.anInt3475, this.anInt3483, var6, var3, this, this.anInt3470, var5); - } else { - var2 = method448(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3476, this.anInt3472, var6, var3, this, this.anInt3470, var5); - } - - this.anInt3480 -= var2; - if(this.anInt3480 != 0) { - return var2; - } - - if(this.method416()) { - continue; - } - - return var4; - } - - if(this.anInt3470 == -256 && (this.anInt3474 & 0xFF) == 0) { - if(AudioChannel.stereo) { - return method447(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3484, this.anInt3481, var4, var3, this); - } - - return method446(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3476, var4, var3, this); - } - - if(AudioChannel.stereo) { - return method453(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3484, this.anInt3481, var4, var3, this, this.anInt3470, var5); - } - - return method423(((PcmSound)this.aSound_2544).samples, var1, this.anInt3474, var2, this.anInt3476, var4, var3, this, this.anInt3470, var5); - } - } - - static Class3_Sub24_Sub1 method437(PcmSound var0, int var2) { - return var0.samples != null && var0.samples.length != 0?new Class3_Sub24_Sub1(var0, (int)((long)var0.frequency * 256L * (long) 100 / (long)(100 * Class21.sampleRate)), var2 << 6):null; - } - - final synchronized int method438() { - return this.anInt3470 < 0?-this.anInt3470:this.anInt3470; - } - - private void method439() { - if(this.anInt3480 != 0) { - if(this.anInt3471 == Integer.MIN_VALUE) { - this.anInt3471 = 0; - } - - this.anInt3480 = 0; - this.method449(); - } - - } - - private static int method440(byte[] var2, int[] var3, int var4, int var5, int var6, int var7, int var8, int var9, int var11, int var12, Class3_Sub24_Sub1 var13, int var14, int var15) { - var13.anInt3476 -= var13.anInt3472 * var5; - int var10; - if(var14 == 0 || (var10 = var5 + (var12 + 256 - var4 + var14) / var14) > var11) { - var10 = var11; - } - - var5 <<= 1; - - int var10001; - int var1; - int var0; - for(var10 <<= 1; var5 < var10; var4 += var14) { - var1 = var4 >> 8; - byte var16 = var2[var1 - 1]; - var0 = (var16 << 8) + (var2[var1] - var16) * (var4 & 0xFF); - var10001 = var5++; - var3[var10001] += var0 * var6 >> 6; - var6 += var8; - var10001 = var5++; - var3[var10001] += var0 * var7 >> 6; - var7 += var9; - } - - if(var14 == 0 || (var10 = (var5 >> 1) + (var12 - var4 + var14) / var14) > var11) { - var10 = var11; - } - - var10 <<= 1; - - for(var1 = var15; var5 < var10; var4 += var14) { - var0 = (var1 << 8) + (var2[var4 >> 8] - var1) * (var4 & 0xFF); - var10001 = var5++; - var3[var10001] += var0 * var6 >> 6; - var6 += var8; - var10001 = var5++; - var3[var10001] += var0 * var7 >> 6; - var7 += var9; - } - - var5 >>= 1; - var13.anInt3476 += var13.anInt3472 * var5; - var13.anInt3484 = var6; - var13.anInt3481 = var7; - var13.anInt3474 = var4; - return var5; - } - - private synchronized void method441(int var1, int var2) { - this.anInt3471 = var1; - this.anInt3478 = var2; - this.anInt3480 = 0; - this.method449(); - } - - final synchronized void method442() { - this.anInt3470 = (this.anInt3470 ^ this.anInt3470 >> 31) + (this.anInt3470 >>> 31); - this.anInt3470 = -this.anInt3470; - - } - - final synchronized void method443(int var1) { - if(this.anInt3470 < 0) { - this.anInt3470 = -var1; - } else { - this.anInt3470 = var1; - } - - } - - final boolean method444() { - return this.anInt3474 < 0 || this.anInt3474 >= ((PcmSound)this.aSound_2544).samples.length << 8; - } - - final boolean method445() { - return this.anInt3480 != 0; - } - - private static int method446(byte[] var0, int[] var1, int var2, int var3, int var4, int var6, int var7, Class3_Sub24_Sub1 var8) { - var2 >>= 8; - var7 >>= 8; - var4 <<= 2; - int var5; - if((var5 = var3 + var2 - (var7 - 1)) > var6) { - var5 = var6; - } - - int var10001; - for(var5 -= 3; var3 < var5; var1[var10001] += var0[var2--] * var4) { - var10001 = var3++; - var1[var10001] += var0[var2--] * var4; - var10001 = var3++; - var1[var10001] += var0[var2--] * var4; - var10001 = var3++; - var1[var10001] += var0[var2--] * var4; - var10001 = var3++; - } - - for(var5 += 3; var3 < var5; var1[var10001] += var0[var2--] * var4) { - var10001 = var3++; - } - - var8.anInt3474 = var2 << 8; - return var3; - } - - private static int method447(byte[] var1, int[] var2, int var3, int var4, int var5, int var6, int var8, int var9, Class3_Sub24_Sub1 var10) { - var3 >>= 8; - var9 >>= 8; - var5 <<= 2; - var6 <<= 2; - int var7; - if((var7 = var4 + var3 - (var9 - 1)) > var8) { - var7 = var8; - } - - var4 <<= 1; - var7 <<= 1; - - byte var11; - int var10001; - for(var7 -= 6; var4 < var7; var2[var10001] += var11 * var6) { - var11 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - var2[var10001] += var11 * var6; - var11 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - var2[var10001] += var11 * var6; - var11 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - var2[var10001] += var11 * var6; - var11 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - } - - for(var7 += 6; var4 < var7; var2[var10001] += var11 * var6) { - var11 = var1[var3--]; - var10001 = var4++; - var2[var10001] += var11 * var5; - var10001 = var4++; - } - - var10.anInt3474 = var3 << 8; - return var4 >> 1; - } - - private static int method448(byte[] var2, int[] var3, int var4, int var5, int var6, int var7, int var9, int var10, Class3_Sub24_Sub1 var11, int var12, int var13) { - var11.anInt3484 -= var11.anInt3475 * var5; - var11.anInt3481 -= var11.anInt3483 * var5; - int var8; - if(var12 == 0 || (var8 = var5 + (var10 + 256 - var4 + var12) / var12) > var9) { - var8 = var9; - } - - int var10001; - int var1; - while(var5 < var8) { - var1 = var4 >> 8; - byte var14 = var2[var1 - 1]; - var10001 = var5++; - var3[var10001] += ((var14 << 8) + (var2[var1] - var14) * (var4 & 0xFF)) * var6 >> 6; - var6 += var7; - var4 += var12; - } - - if(var12 == 0 || (var8 = var5 + (var10 - var4 + var12) / var12) > var9) { - var8 = var9; - } - - for(var1 = var12; var5 < var8; var4 += var1) { - var10001 = var5++; - var3[var10001] += ((var13 << 8) + (var2[var4 >> 8] - var13) * (var4 & 0xFF)) * var6 >> 6; - var6 += var7; - } - - var11.anInt3484 += var11.anInt3475 * var5; - var11.anInt3481 += var11.anInt3483 * var5; - var11.anInt3476 = var6; - var11.anInt3474 = var4; - return var5; - } - - private void method449() { - this.anInt3476 = this.anInt3471; - this.anInt3484 = method452(this.anInt3471, this.anInt3478); - this.anInt3481 = method454(this.anInt3471, this.anInt3478); - } - - final synchronized void method450(int var1, int var2, int var3) { - if(var1 == 0) { - this.method441(var2, var3); - } else { - int var4 = method452(var2, var3); - int var5 = method454(var2, var3); - if(this.anInt3484 == var4 && this.anInt3481 == var5) { - this.anInt3480 = 0; - } else { - int var6 = var2 - this.anInt3476; - if(this.anInt3476 - var2 > var6) { - var6 = this.anInt3476 - var2; - } - - if(var4 - this.anInt3484 > var6) { - var6 = var4 - this.anInt3484; - } - - if(this.anInt3484 - var4 > var6) { - var6 = this.anInt3484 - var4; - } - - if(var5 - this.anInt3481 > var6) { - var6 = var5 - this.anInt3481; - } - - if(this.anInt3481 - var5 > var6) { - var6 = this.anInt3481 - var5; - } - - if(var1 > var6) { - var1 = var6; - } - - this.anInt3480 = var1; - this.anInt3471 = var2; - this.anInt3478 = var3; - this.anInt3472 = (var2 - this.anInt3476) / var1; - this.anInt3475 = (var4 - this.anInt3484) / var1; - this.anInt3483 = (var5 - this.anInt3481) / var1; - } - } - } - - final synchronized int method451() { - return this.anInt3478 < 0?-1:this.anInt3478; - } - - private static int method452(int var0, int var1) { - return var1 < 0?var0:(int)((double)var0 * Math.sqrt((double)(16384 - var1) * 1.220703125E-4D) + 0.5D); - } - - private static int method453(byte[] var2, int[] var3, int var4, int var5, int var6, int var7, int var9, int var10, Class3_Sub24_Sub1 var11, int var12, int var13) { - int var8; - if(var12 == 0 || (var8 = var5 + (var10 + 256 - var4 + var12) / var12) > var9) { - var8 = var9; - } - - var5 <<= 1; - - int var10001; - int var1; - int var0; - for(var8 <<= 1; var5 < var8; var4 += var12) { - var1 = var4 >> 8; - byte var14 = var2[var1 - 1]; - var0 = (var14 << 8) + (var2[var1] - var14) * (var4 & 0xFF); - var10001 = var5++; - var3[var10001] += var0 * var6 >> 6; - var10001 = var5++; - var3[var10001] += var0 * var7 >> 6; - } - - if(var12 == 0 || (var8 = (var5 >> 1) + (var10 - var4 + var12) / var12) > var9) { - var8 = var9; - } - - var8 <<= 1; - - for(var1 = var13; var5 < var8; var4 += var12) { - var0 = (var1 << 8) + (var2[var4 >> 8] - var1) * (var4 & 0xFF); - var10001 = var5++; - var3[var10001] += var0 * var6 >> 6; - var10001 = var5++; - var3[var10001] += var0 * var7 >> 6; - } - - var11.anInt3474 = var4; - return var5 >> 1; - } - - private Class3_Sub24_Sub1(PcmSound var1, int var2, int var3) { - this.aSound_2544 = var1; - this.anInt3473 = var1.anInt3033; - this.anInt3482 = var1.anInt3032; - this.aBoolean3477 = var1.aBoolean3031; - this.anInt3470 = var2; - this.anInt3471 = var3; - this.anInt3478 = 8192; - this.anInt3474 = 0; - this.method449(); - } - - final Class3_Sub24 method414() { - return null; - } - - private static int method454(int var0, int var1) { - return var1 < 0?-var0:(int)((double)var0 * Math.sqrt((double)var1 * 1.220703125E-4D) + 0.5D); - } - - private Class3_Sub24_Sub1(PcmSound var1, int var2, int var3, int var4) { - this.aSound_2544 = var1; - this.anInt3473 = var1.anInt3033; - this.anInt3482 = var1.anInt3032; - this.aBoolean3477 = var1.aBoolean3031; - this.anInt3470 = var2; - this.anInt3471 = var3; - this.anInt3478 = var4; - this.anInt3474 = 0; - this.method449(); - } - - private static int method455(byte[] var2, int[] var3, int var4, int var5, int var6, int var8, int var9, Class3_Sub24_Sub1 var10, int var11, int var12) { - int var7; - if(var11 == 0 || (var7 = var5 + (var9 - var4 + var11 - 257) / var11) > var8) { - var7 = var8; - } - - int var10001; - byte var13; - int var1; - while(var5 < var7) { - var1 = var4 >> 8; - var13 = var2[var1]; - var10001 = var5++; - var3[var10001] += ((var13 << 8) + (var2[var1 + 1] - var13) * (var4 & 0xFF)) * var6 >> 6; - var4 += var11; - } - - if(var11 == 0 || (var7 = var5 + (var9 - var4 + var11 - 1) / var11) > var8) { - var7 = var8; - } - - for(var1 = var12; var5 < var7; var4 += var11) { - var13 = var2[var4 >> 8]; - var10001 = var5++; - var3[var10001] += ((var13 << 8) + (var1 - var13) * (var4 & 0xFF)) * var6 >> 6; - } - - var10.anInt3474 = var4; - return var5; - } -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub24_Sub2.java b/Client/src/main/java/org/runite/client/Class3_Sub24_Sub2.java deleted file mode 100644 index eba35c101..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub24_Sub2.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -import java.util.Objects; - -final class Class3_Sub24_Sub2 extends Class3_Sub24 { - - private final LinkedList aLinkedList_3485 = new LinkedList(); - private final LinkedList aLinkedList_3486 = new LinkedList(); - private int anInt3487 = 0; - private int anInt3488 = -1; - - static void method1662(Linkable var0, Linkable var1) { - try { - if(null != var0.previous) { - var0.unlink(); - } - - var0.next = var1; - var0.previous = var1.previous; - var0.previous.next = var0; - var0.next.previous = var0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ok.C(" + (var0 != null?"{...}":"null") + ',' + (var1 != null?"{...}":"null") + ',' + -16 + ')'); - } - } - - - private void method456(Class3_Sub3 var1) { - var1.unlink(); - var1.method106(); - Linkable var2 = this.aLinkedList_3486.aClass3_940.next; - if(var2 == this.aLinkedList_3486.aClass3_940) { - this.anInt3488 = -1; - } else { - this.anInt3488 = ((Class3_Sub3)var2).anInt2247; - } - - } - - final synchronized void method457(Class3_Sub24 var1) { - this.aLinkedList_3485.method1216(var1); - } - - final synchronized void method413(int[] var1, int var2, int var3) { - do { - if(this.anInt3488 < 0) { - this.method460(var1, var2, var3); - return; - } - - if(this.anInt3487 + var3 < this.anInt3488) { - this.anInt3487 += var3; - this.method460(var1, var2, var3); - return; - } - - int var4 = this.anInt3488 - this.anInt3487; - this.method460(var1, var2, var4); - var2 += var4; - var3 -= var4; - this.anInt3487 += var4; - this.method458(); - Class3_Sub3 var5 = (Class3_Sub3)this.aLinkedList_3486.method1222(); - synchronized(Objects.requireNonNull(var5)) { - int var7 = var5.method105(this); - if(var7 < 0) { - var5.anInt2247 = 0; - this.method456(var5); - } else { - var5.anInt2247 = var7; - this.method462(var5.next, var5); - } - } - } while(var3 != 0); - - } - - private void method458() { - if(this.anInt3487 > 0) { - for(Class3_Sub3 var1 = (Class3_Sub3)this.aLinkedList_3486.method1222(); var1 != null; var1 = (Class3_Sub3)this.aLinkedList_3486.method1221()) { - var1.anInt2247 -= this.anInt3487; - } - - this.anInt3488 -= this.anInt3487; - this.anInt3487 = 0; - } - - } - - final Class3_Sub24 method411() { - return (Class3_Sub24)this.aLinkedList_3485.method1222(); - } - - private void method459(int var1) { - for(Class3_Sub24 var2 = (Class3_Sub24)this.aLinkedList_3485.method1222(); var2 != null; var2 = (Class3_Sub24)this.aLinkedList_3485.method1221()) { - var2.method415(var1); - } - - } - - private void method460(int[] var1, int var2, int var3) { - for(Class3_Sub24 var4 = (Class3_Sub24)this.aLinkedList_3485.method1222(); var4 != null; var4 = (Class3_Sub24)this.aLinkedList_3485.method1221()) { - var4.method410(var1, var2, var3); - } - - } - - final synchronized void method461(Class3_Sub24 var1) { - var1.unlink(); - } - - final int method409() { - return 0; - } - - final synchronized void method415(int var1) { - do { - if(this.anInt3488 < 0) { - this.method459(var1); - return; - } - - if(this.anInt3487 + var1 < this.anInt3488) { - this.anInt3487 += var1; - this.method459(var1); - return; - } - - int var2 = this.anInt3488 - this.anInt3487; - this.method459(var2); - var1 -= var2; - this.anInt3487 += var2; - this.method458(); - Class3_Sub3 var3 = (Class3_Sub3)this.aLinkedList_3486.method1222(); - synchronized(Objects.requireNonNull(var3)) { - int var5 = var3.method105(this); - if(var5 < 0) { - var3.anInt2247 = 0; - this.method456(var3); - } else { - var3.anInt2247 = var5; - this.method462(var3.next, var3); - } - } - } while(var1 != 0); - - } - - final Class3_Sub24 method414() { - return (Class3_Sub24)this.aLinkedList_3485.method1221(); - } - - private void method462(Linkable var1, Class3_Sub3 var2) { - while(var1 != this.aLinkedList_3486.aClass3_940 && ((Class3_Sub3)var1).anInt2247 <= var2.anInt2247) { - var1 = var1.next; - } - - method1662(var2, var1); - this.anInt3488 = ((Class3_Sub3)this.aLinkedList_3486.aClass3_940.next).anInt2247; - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub24_Sub3.java b/Client/src/main/java/org/runite/client/Class3_Sub24_Sub3.java deleted file mode 100644 index 24ef2489e..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub24_Sub3.java +++ /dev/null @@ -1,227 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class Class3_Sub24_Sub3 extends Class3_Sub24 { - - LinkedList aLinkedList_3489 = new LinkedList(); - static int[] anIntArray3491 = new int[]{0, -1, 0, 1}; - static int maxClientMemory = 64; - private final Class3_Sub24_Sub4 aClass3_Sub24_Sub4_3493; - static int[] anIntArray3494; - Class3_Sub24_Sub2 aClass3_Sub24_Sub2_3495 = new Class3_Sub24_Sub2(); - - - final Class3_Sub24 method414() { - try { - Class3_Sub22 var1; - do { - var1 = (Class3_Sub22)this.aLinkedList_3489.method1221(); - if(null == var1) { - return null; - } - } while(var1.aClass3_Sub24_Sub1_2507 == null); - - return var1.aClass3_Sub24_Sub1_2507; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "te.B()"); - } - } - - final Class3_Sub24 method411() { - try { - Class3_Sub22 var1 = (Class3_Sub22)this.aLinkedList_3489.method1222(); - return null != var1?(null != var1.aClass3_Sub24_Sub1_2507?var1.aClass3_Sub24_Sub1_2507:this.method414()):null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "te.Q()"); - } - } - - private void method464(Class3_Sub22 var1, int var3) { - try { - if((this.aClass3_Sub24_Sub4_3493.anIntArray3518[var1.anInt2514] & 4) != 0 && var1.anInt2506 < 0) { - int var4 = this.aClass3_Sub24_Sub4_3493.anIntArray3509[var1.anInt2514] / Class21.sampleRate; - int var5 = (var4 + 1048575 + -var1.anInt2516) / var4; - var1.anInt2516 = 1048575 & var4 * var3 + var1.anInt2516; - if(var5 <= var3) { - if(0 == this.aClass3_Sub24_Sub4_3493.anIntArray3519[var1.anInt2514]) { - var1.aClass3_Sub24_Sub1_2507 = Class3_Sub24_Sub1.method432(var1.aPcmSound_2509, var1.aClass3_Sub24_Sub1_2507.method438(), var1.aClass3_Sub24_Sub1_2507.method425(), var1.aClass3_Sub24_Sub1_2507.method451()); - } else { - var1.aClass3_Sub24_Sub1_2507 = Class3_Sub24_Sub1.method432(var1.aPcmSound_2509, var1.aClass3_Sub24_Sub1_2507.method438(), 0, var1.aClass3_Sub24_Sub1_2507.method451()); - this.aClass3_Sub24_Sub4_3493.method501(var1, var1.aClass3_Sub15_2527.aShortArray2434[var1.anInt2520] < 0, (byte)-101); - } - - if(var1.aClass3_Sub15_2527.aShortArray2434[var1.anInt2520] < 0) { - Objects.requireNonNull(var1.aClass3_Sub24_Sub1_2507).method429(-1); - } - - var3 = var1.anInt2516 / var4; - } - } - - Objects.requireNonNull(var1.aClass3_Sub24_Sub1_2507).method415(var3); - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "te.P(" + (var1 != null?"{...}":"null") + ',' + 7 + ',' + var3 + ')'); - } - } - - final int method409() { - try { - return 0; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "te.D()"); - } - } - - static boolean method467(RSString var0) { - try { - if (var0 != null) { - for (int var2 = 0; var2 < Class3_Sub28_Sub5.anInt3591; ++var2) { - if (var0.equalsStringIgnoreCase(TextureOperation7.aClass94Array3341[var2])) { - return true; - } - } - - } - return false; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "te.A(" + "{...}" + ',' + 0 + ')'); - } - } - - static void method468() { - try { - Class88.method1456(); - Unsorted.anShaderInterfaceArray70 = new ShaderInterface[7]; - Unsorted.anShaderInterfaceArray70[1] = new Class160(); - Unsorted.anShaderInterfaceArray70[2] = new Class125(); - Unsorted.anShaderInterfaceArray70[3] = new WaterShader(); - Unsorted.anShaderInterfaceArray70[4] = new WaterMovementShader(); - Unsorted.anShaderInterfaceArray70[5] = new WaterfallShader(); - Unsorted.anShaderInterfaceArray70[6] = new Class147(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "te.T(" + ')'); - } - } - - final void method415(int var1) { - try { - this.aClass3_Sub24_Sub2_3495.method415(var1); - - for(Class3_Sub22 var3 = (Class3_Sub22)this.aLinkedList_3489.method1222(); var3 != null; var3 = (Class3_Sub22)this.aLinkedList_3489.method1221()) { - if(this.aClass3_Sub24_Sub4_3493.method504(var3, 121)) { - int var2 = var1; - - while(true) { - if(var2 > var3.anInt2512) { - this.method464(var3, var3.anInt2512); - var2 -= var3.anInt2512; - if(this.aClass3_Sub24_Sub4_3493.method492(var2, 0, var3, null)) { - continue; - } - break; - } - - this.method464(var3, var2); - var3.anInt2512 -= var2; - break; - } - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "te.R(" + var1 + ')'); - } - } - - final void method413(int[] var1, int var2, int var3) { - try { - this.aClass3_Sub24_Sub2_3495.method413(var1, var2, var3); - - for(Class3_Sub22 var6 = (Class3_Sub22)this.aLinkedList_3489.method1222(); var6 != null; var6 = (Class3_Sub22)this.aLinkedList_3489.method1221()) { - if(this.aClass3_Sub24_Sub4_3493.method504(var6, 126)) { - int var5 = var3; - int var4 = var2; - - while(true) { - if(var5 > var6.anInt2512) { - this.method469(var1, var6, var4, var6.anInt2512, var5 + var4); - var5 -= var6.anInt2512; - var4 += var6.anInt2512; - if(this.aClass3_Sub24_Sub4_3493.method492(var5, var4, var6, var1)) { - continue; - } - break; - } - - this.method469(var1, var6, var4, var5, var4 + var5); - var6.anInt2512 -= var5; - break; - } - } - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "te.E(" + (var1 != null?"{...}":"null") + ',' + var2 + ',' + var3 + ')'); - } - } - - private void method469(int[] var1, Class3_Sub22 var2, int var3, int var4, int var5) { - try { - if((4 & this.aClass3_Sub24_Sub4_3493.anIntArray3518[var2.anInt2514]) != 0 && var2.anInt2506 < 0) { - int var7 = this.aClass3_Sub24_Sub4_3493.anIntArray3509[var2.anInt2514] / Class21.sampleRate; - - while(true) { - int var8 = (-var2.anInt2516 + 1048575 + var7) / var7; - if(var4 < var8) { - var2.anInt2516 += var4 * var7; - break; - } - - var4 -= var8; - var2.aClass3_Sub24_Sub1_2507.method413(var1, var3, var8); - int var9 = Class21.sampleRate / 100; - Class3_Sub24_Sub1 var11 = var2.aClass3_Sub24_Sub1_2507; - int var10 = 262144 / var7; - if(var10 < var9) { - var9 = var10; - } - - var2.anInt2516 += var7 * var8 + -1048576; - if(this.aClass3_Sub24_Sub4_3493.anIntArray3519[var2.anInt2514] == 0) { - var2.aClass3_Sub24_Sub1_2507 = Class3_Sub24_Sub1.method432(var2.aPcmSound_2509, var11.method438(), var11.method425(), var11.method451()); - } else { - var2.aClass3_Sub24_Sub1_2507 = Class3_Sub24_Sub1.method432(var2.aPcmSound_2509, var11.method438(), 0, var11.method451()); - this.aClass3_Sub24_Sub4_3493.method501(var2, var2.aClass3_Sub15_2527.aShortArray2434[var2.anInt2520] < 0, (byte)-88); - var2.aClass3_Sub24_Sub1_2507.method431(var9, var11.method425()); - } - - if(var2.aClass3_Sub15_2527.aShortArray2434[var2.anInt2520] < 0) { - Objects.requireNonNull(var2.aClass3_Sub24_Sub1_2507).method429(-1); - } - - var3 += var8; - var11.method417(var9); - var11.method413(var1, var3, var5 + -var3); - if(var11.method445()) { - this.aClass3_Sub24_Sub2_3495.method457(var11); - } - } - } - - var2.aClass3_Sub24_Sub1_2507.method413(var1, var3, var4); - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "te.S(" + (var1 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + var3 + ',' + var4 + ',' + var5 + ',' + (byte) 4 + ')'); - } - } - - Class3_Sub24_Sub3(Class3_Sub24_Sub4 var1) { - try { - this.aClass3_Sub24_Sub4_3493 = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "te.(" + (var1 != null?"{...}":"null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub24_Sub4.java b/Client/src/main/java/org/runite/client/Class3_Sub24_Sub4.java deleted file mode 100644 index 227346483..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub24_Sub4.java +++ /dev/null @@ -1,1303 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; -import org.rs09.client.data.HashTable; - -import java.util.Objects; - -final class Class3_Sub24_Sub4 extends Class3_Sub24 { - - private final int[] anIntArray3497 = new int[16]; - private final int[] anIntArray3498 = new int[16]; - private final int[] anIntArray3499 = new int[16]; - private final int[] anIntArray3500 = new int[16]; - private final int[] anIntArray3501 = new int[16]; - private final int[] anIntArray3502 = new int[16]; - static boolean[] aBooleanArray3503; - private final int[] anIntArray3504 = new int[16]; - private final Class78 aClass78_3505 = new Class78(); - private final int[] anIntArray3506 = new int[16]; - private final HashTable aHashTable_3508; - int[] anIntArray3509 = new int[16]; - private final int[] anIntArray3510 = new int[16]; - private final int anInt3511 = 1000000; - private final Class3_Sub22[][] aClass3_Sub22ArrayArray3512 = new Class3_Sub22[16][128]; - private final Class3_Sub22[][] aClass3_Sub22ArrayArray3513 = new Class3_Sub22[16][128]; - private final int[] anIntArray3514 = new int[16]; - private final int[] anIntArray3515 = new int[16]; - private final int[] anIntArray3516 = new int[16]; - int[] anIntArray3518 = new int[16]; - int[] anIntArray3519 = new int[16]; - private int[] anIntArray3520 = new int[16]; - private int anInt3521 = 256; - private boolean aBoolean3522; - private long aLong3523; - private int anInt3524; - private int anInt3525; - private long aLong3526; - private Class3_Sub24_Sub3 aClass3_Sub24_Sub3_3527 = new Class3_Sub24_Sub3(this); - private Class3_Sub27 aClass3_Sub27_3528; - - static void method1177(int var0, long var1, byte var3, RSString var4, int var5, short var6, RSString var7, int var8) { - try { - if(var3 > -23) { - method1177(-45, 37L, (byte)-37, null, -16, (short)110, null, -75); - } - - if(!Class38_Sub1.aBoolean2615) { - if(Unsorted.menuOptionCount < 500) { - Class140_Sub7.aClass94Array2935[Unsorted.menuOptionCount] = var7; - Class163_Sub2_Sub1.aClass94Array4016[Unsorted.menuOptionCount] = var4; - Class114.anIntArray1578[Unsorted.menuOptionCount] = var0 == -1 ?Class3_Sub28_Sub5.anInt3590:var0; - TextureOperation27.aShortArray3095[Unsorted.menuOptionCount] = var6; - Unsorted.aLongArray3271[Unsorted.menuOptionCount] = var1; - Class117.anIntArray1613[Unsorted.menuOptionCount] = var5; - Class27.anIntArray512[Unsorted.menuOptionCount] = var8; - ++Unsorted.menuOptionCount; - } - - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "hj.C(" + var0 + ',' + var1 + ',' + var3 + ',' + (var4 != null?"{...}":"null") + ',' + var5 + ',' + var6 + ',' + (var7 != null?"{...}":"null") + ',' + var8 + ')'); - } - } - - final synchronized boolean method470(Class3_Sub27 var1, CacheIndex var3, Class83 var4) { - try { - var1.method516(); - boolean var6 = true; - int[] var7; - var7 = new int[]{22050}; - for(Class3_Sub6 var9 = (Class3_Sub6)var1.aHashTable_2564.first(); var9 != null; var9 = (Class3_Sub6)var1.aHashTable_2564.next()) { - int var10 = (int)var9.linkableKey; - Class3_Sub15 var11 = (Class3_Sub15)this.aHashTable_3508.get(var10); - if(null == var11) { - var11 = Unsorted.method1245(var3, var10); - if(null == var11) { - var6 = false; - continue; - } - - this.aHashTable_3508.put(var10, var11); - } - - if(!var11.method373(var7, var4, var9.aByteArray2289)) { - var6 = false; - } - } - - if(var6) { - var1.method515(); - } - - return var6; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "va.DB(" + (var1 != null?"{...}":"null") + ',' + -122 + ',' + (var3 != null?"{...}":"null") + ',' + (var4 != null?"{...}":"null") + ',' + 22050 + ')'); - } - } - - final synchronized void method471() { - try { - for(Class3_Sub15 var2 = (Class3_Sub15)this.aHashTable_3508.first(); var2 != null; var2 = (Class3_Sub15)this.aHashTable_3508.next()) { - var2.method369(); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "va.AB(" + (byte) 53 + ')'); - } - } - - private void method472() { - try { - this.anIntArray3501[9] = 128; - this.anIntArray3506[9] = Unsorted.bitwiseAnd(128, -128); - this.method484(128, 9); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.SA(" + 128 + ',' + 9 + ',' + 85 + ')'); - } - } - - final synchronized boolean method473(int var1) { - try { - if(var1 >= -121) { - this.anIntArray3509 = null; - } - - return this.aClass78_3505.method1373(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "va.VA(" + var1 + ')'); - } - } - - private synchronized void method475(boolean var1, Class3_Sub27 var2, boolean var3) { - try { - this.method507(var3); - this.aClass78_3505.method1380(var2.aByteArray2565); - this.aBoolean3522 = var1; - this.aLong3526 = 0L; - int var5 = this.aClass78_3505.method1374(); - - for(int var6 = 0; var6 < var5; ++var6) { - this.aClass78_3505.method1376(var6); - this.aClass78_3505.method1377(var6); - this.aClass78_3505.method1381(var6); - } - - this.anInt3525 = this.aClass78_3505.method1382(); - this.anInt3524 = this.aClass78_3505.anIntArray1114[this.anInt3525]; - this.aLong3523 = this.aClass78_3505.method1370(this.anInt3524); - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "va.PA(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + var3 + ',' + (byte) -52 + ')'); - } - } - - private void method476(int var1, int var2) { - try { - this.anIntArray3499[var1] = var2; - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.P(" + var1 + ',' + var2 + ',' + 0 + ')'); - } - } - - static void method477(int var0, int var2, RSInterface var3) { - try { - - if(var3.anInt318 == 1) { - method1177(-1, 0L, (byte)-78, RSString.parse(""), 0, (short)8, var3.aClass94_289, var3.componentHash); - } - - RSString var4; - if(var3.anInt318 == 2 && !GameObject.aBoolean1837) { - var4 = Class53.method1174(var3, (byte)-31); - if(null != var4) { - method1177(-1, 0L, (byte)-120, RSString.stringCombiner(new RSString[]{RSString.parse(""), var3.aClass94_243}), -1, (short)32, var4, var3.componentHash); - } - } - - if(var3.anInt318 == 3) { - method1177(-1, 0L, (byte)-59, RSString.parse(""), 0, (short)28, TextCore.HasClose, var3.componentHash); - } - - if(var3.anInt318 == 4) { - method1177(-1, 0L, (byte)-71, RSString.parse(""), 0, (short)59, var3.aClass94_289, var3.componentHash); - } - - if(var3.anInt318 == 5) { - method1177(-1, 0L, (byte)-92, RSString.parse(""), 0, (short)51, var3.aClass94_289, var3.componentHash); - } - - if(var3.anInt318 == 6 && TextureOperation27.aClass11_3087 == null) { - method1177(-1, 0L, (byte)-100, RSString.parse(""), -1, (short)41, var3.aClass94_289, var3.componentHash); - } - - int var5; - int var15; - if(var3.type == 2) { - var15 = 0; - - for(var5 = 0; var3.defHeight > var5; ++var5) { - for(int var6 = 0; var6 < var3.defWidth; ++var6) { - int var7 = (32 - -var3.anInt285) * var6; - int var8 = (32 + var3.anInt290) * var5; - if(var15 < 20) { - var8 += var3.anIntArray300[var15]; - var7 += var3.anIntArray272[var15]; - } - - if(var7 <= var2 && var0 >= var8 && 32 + var7 > var2 && var0 < var8 + 32) { - Class99.aClass11_1402 = var3; - TextureOperation20.anInt2701 = var15; - if(var3.itemAmounts[var15] > 0) { - Class3_Sub1 var9 = Client.method44(var3); - ItemDefinition var10 = ItemDefinition.getItemDefinition(var3.itemAmounts[var15] + -1); - if(1 == Class164_Sub1.anInt3012 && var9.method99()) { - if(var3.componentHash != Class3_Sub28_Sub18.anInt3764 || var15 != BufferedDataStream.anInt1473) { - method1177(-1, var10.itemId, (byte)-91, RSString.stringCombiner(new RSString[]{RenderAnimationDefinition.aClass94_378, ColorCore.BankItemColor, var10.name}), var15, (short)40, TextCore.HasUse, var3.componentHash); - } - } else if(GameObject.aBoolean1837 && var9.method99()) { - Class3_Sub28_Sub9 var18 = Unsorted.anInt1038 != -1 ? LinkedList.method1210(Unsorted.anInt1038):null; - if(0 != (16 & Class164.anInt2051) && (var18 == null || var18.anInt3614 != var10.method1115(var18.anInt3614, 103, Unsorted.anInt1038))) { - method1177(Unsorted.anInt1887, var10.itemId, (byte)-89, RSString.stringCombiner(new RSString[]{TextCore.aClass94_676, ColorCore.BankItemColor, var10.name}), var15, (short)3, Class3_Sub28_Sub9.aClass94_3621, var3.componentHash); - } - } else { - RSString[] inventoryOptions = var10.inventoryOptions; - if(Class123.aBoolean1656) { - inventoryOptions = Class3_Sub31.optionsArrayStringConstructor(inventoryOptions); - } - - int var12; - byte var13; - if(var9.method99()) { - for(var12 = 4; var12 >= 3; --var12) { - if(null != inventoryOptions && null != inventoryOptions[var12]) { - if(var12 == 3) { - var13 = 35; - } else { - var13 = 58; - } - method1177(-1, var10.itemId, (byte)-65, RSString.stringCombiner(new RSString[]{ColorCore.ItemBackpackColor, var10.name}), var15, var13, inventoryOptions[var12], var3.componentHash); - } - } - } - - if(var9.method96()) { - method1177(Class99.anInt1403, var10.itemId, (byte)-96, RSString.stringCombiner(new RSString[]{ColorCore.ItemBackpackColor, var10.name}), var15, (short)22, TextCore.HasUse, var3.componentHash); - } - - if(var9.method99()) { - for(var12 = 2; 0 <= var12; --var12) { - if(inventoryOptions[var12] != null) { - var13 = 0; - if(var12 == 0) { - var13 = 47; - } - - if(var12 == 1) { - var13 = 5; - } - - if(2 == var12) { - var13 = 43; - } - - method1177(-1, var10.itemId, (byte)-82, RSString.stringCombiner(new RSString[]{ColorCore.ItemBackpackColor, var10.name}), var15, var13, inventoryOptions[var12], var3.componentHash); - } - } - } - - inventoryOptions = var3.options; - if(Class123.aBoolean1656) { - inventoryOptions = Class3_Sub31.optionsArrayStringConstructor(inventoryOptions); - } - - if(inventoryOptions != null) { - for(var12 = 4; var12 >= 0; --var12) { - if(null != inventoryOptions[var12]) { - var13 = 0; - if(0 == var12) { - var13 = 25; - } - - if(var12 == 1) { - var13 = 23; - } - - if(var12 == 2) { - var13 = 48; - } - - if(3 == var12) { - var13 = 7; - } - - if(var12 == 4) { - var13 = 13; - } - - method1177(-1, var10.itemId, (byte)-51, RSString.stringCombiner(new RSString[]{ColorCore.ItemBackpackColor, var10.name}), var15, var13, inventoryOptions[var12], var3.componentHash); - } - } - } - if (GameConfig.ITEM_DEBUG_ENABLED) { - method1177(Class131.anInt1719, var10.itemId, (byte) -98, RSString.stringCombiner(new RSString[]{ColorCore.ItemBackpackColor, var10.name}), var15, (short) 1006, RSString.parse("Examine" + "
" + " ID: (X" + var10.itemId + "(Y"), var3.componentHash); - } else { - method1177(Class131.anInt1719, var10.itemId, (byte) -98, RSString.stringCombiner(new RSString[]{ColorCore.ItemBackpackColor, var10.name}), var15, (short) 1006, TextCore.HasExamine, var3.componentHash); - } - } - } - } - - ++var15; - } - } - } - - if(var3.usingScripts) { - if(GameObject.aBoolean1837) { - if(Client.method44(var3).method97() && (32 & Class164.anInt2051) != 0) { - method1177(Unsorted.anInt1887, 0L, (byte)-113, RSString.stringCombiner(new RSString[]{ - TextCore.aClass94_676, TextCore.aClass94_3703, var3.aClass94_277 - }), var3.anInt191, (short)12, Class3_Sub28_Sub9.aClass94_3621, var3.componentHash); - } - } else { - for(var15 = 9; var15 >= 5; --var15) { - RSString var16 = Class120.method1732(var3, (byte)-71, var15); - if(null != var16) { - method1177(TextureOperation6.method173((byte)126, var15, var3), var15 + 1, (byte)-85, var3.aClass94_277, var3.anInt191, (short)1003, var16, var3.componentHash); - } - } - - var4 = Class53.method1174(var3, (byte)-101); - if(var4 != null) { - method1177(-1, 0L, (byte)-116, var3.aClass94_277, var3.anInt191, (short)32, var4, var3.componentHash); - } - - for(var5 = 4; var5 >= 0; --var5) { - RSString var17 = Class120.method1732(var3, (byte)-65, var5); - if(var17 != null) { - method1177(TextureOperation6.method173((byte)53, var5, var3), var5 - -1, (byte)-48, var3.aClass94_277, var3.anInt191, (short)9, var17, var3.componentHash); - } - } - - if(Client.method44(var3).method95()) { - method1177(-1, 0L, (byte)-74, RSString.parse(""), var3.anInt191, (short)41, TextCore.HasContinue, var3.componentHash); - } - } - } - - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "va.JA(" + var0 + ',' + true + ',' + var2 + ',' + (var3 != null?"{...}":"null") + ')'); - } - } - - final synchronized int method409() { - try { - return 0; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "va.D()"); - } - } - - private void method478(int var1, int var3) { - try { - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.QA(" + var1 + ',' + false + ',' + var3 + ')'); - } - } - - final synchronized void method479() { - try { - this.method472(); - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.HA(" + (byte) 98 + ',' + 9 + ',' + 128 + ')'); - } - } - - private void method480(int var2) { - try { - if(0 <= var2) { - this.anIntArray3497[var2] = 12800; - this.anIntArray3498[var2] = 8192; - this.anIntArray3514[var2] = 16383; - this.anIntArray3499[var2] = 8192; - this.anIntArray3502[var2] = 0; - this.anIntArray3510[var2] = 8192; - this.method502(var2, 8388489 ^ -8388490); - this.method497(var2, -128); - this.anIntArray3518[var2] = 0; - this.anIntArray3500[var2] = 32767; - this.anIntArray3504[var2] = 256; - this.anIntArray3519[var2] = 0; - this.method482((byte)-125, var2, 8192); - } else { - for(var2 = 0; var2 < 16; ++var2) { - this.method480(var2); - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.V(" + 8388489 + ',' + var2 + ')'); - } - } - - private void method481(byte var1, int var2) { - try { - - for(Class3_Sub22 var4 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1222(); null != var4; var4 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1221()) { - if(var2 < 0 || var4.anInt2514 == var2) { - if(null != var4.aClass3_Sub24_Sub1_2507) { - var4.aClass3_Sub24_Sub1_2507.method417(Class21.sampleRate / 100); - if(var4.aClass3_Sub24_Sub1_2507.method445()) { - this.aClass3_Sub24_Sub3_3527.aClass3_Sub24_Sub2_3495.method457(var4.aClass3_Sub24_Sub1_2507); - } - - var4.method401(); - } - - if(0 > var4.anInt2506) { - this.aClass3_Sub22ArrayArray3512[var4.anInt2514][var4.anInt2520] = null; - } - - var4.unlink(); - } - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.NA(" + var1 + ',' + var2 + ')'); - } - } - - private void method482(byte var1, int var2, int var3) { - try { - this.anIntArray3520[var2] = var3; - this.anIntArray3509[var2] = (int)(0.5D + 2097152.0D * Math.pow(2.0D, 5.4931640625E-4D * (double)var3)); - if(var1 > -53) { - this.method505((byte)114); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.EA(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - private synchronized void method483() { - try { - for(int var4 = 0; 16 > var4; ++var4) { - this.anIntArray3516[var4] = 256; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.O(" + -1 + ',' + -48 + ',' + 256 + ')'); - } - } - - private void method484(int var2, int var3) { - try { - if(this.anIntArray3515[var3] != var2) { - this.anIntArray3515[var3] = var2; - - for(int var4 = 0; var4 < 128; ++var4) { - this.aClass3_Sub22ArrayArray3513[var3][var4] = null; - } - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.FB(" + 0 + ',' + var2 + ',' + var3 + ')'); - } - } - - final synchronized void method485() { - try { - for(Class3_Sub15 var2 = (Class3_Sub15)this.aHashTable_3508.first(); var2 != null; var2 = (Class3_Sub15)this.aHashTable_3508.next()) { - var2.unlink(); - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.DA(" + -110 + ')'); - } - } - - private void method486(int var1, int var2, int var4) { - try { - this.method493((byte)-123, var4, 64, var2); - if((2 & this.anIntArray3518[var2]) != 0) { - for(Class3_Sub22 var5 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1212(); var5 != null; var5 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1219(98)) { - if(var5.anInt2514 == var2 && var5.anInt2506 < 0) { - this.aClass3_Sub22ArrayArray3512[var2][var5.anInt2520] = null; - this.aClass3_Sub22ArrayArray3512[var2][var4] = var5; - int var6 = var5.anInt2510 - -(var5.anInt2502 * var5.anInt2522 >> 12); - var5.anInt2502 = 4096; - var5.anInt2510 += -var5.anInt2520 + var4 << 8; - var5.anInt2522 = -var5.anInt2510 + var6; - var5.anInt2520 = var4; - return; - } - } - } - - Class3_Sub15 var11 = (Class3_Sub15)this.aHashTable_3508.get(this.anIntArray3515[var2]); - if(var11 != null) { - PcmSound var12 = var11.aPcmSoundArray2431[var4]; - if(var12 != null) { - Class3_Sub22 var7 = new Class3_Sub22(); - var7.aPcmSound_2509 = var12; - var7.aClass3_Sub15_2527 = var11; - var7.anInt2514 = var2; - var7.aClass166_2504 = var11.aClass166Array2435[var4]; - var7.anInt2517 = var11.aByteArray2425[var4]; - var7.anInt2520 = var4; - var7.anInt2513 = var11.aByteArray2430[var4] * var1 * var1 * var11.anInt2424 - -1024 >> 11; - var7.anInt2503 = var11.aByteArray2422[var4] & 0xFF; - var7.anInt2510 = -(32767 & var11.aShortArray2434[var4]) + (var4 << 8); - var7.anInt2506 = -1; - var7.anInt2511 = 0; - var7.anInt2519 = 0; - var7.anInt2523 = 0; - var7.anInt2501 = 0; - if(this.anIntArray3519[var2] == 0) { - var7.aClass3_Sub24_Sub1_2507 = Class3_Sub24_Sub1.method432(var12, this.method498(var7), this.method508(var7), this.method496(var7)); - } else { - var7.aClass3_Sub24_Sub1_2507 = Class3_Sub24_Sub1.method432(var12, this.method498(var7), 0, this.method496(var7)); - this.method501(var7, var11.aShortArray2434[var4] < 0, (byte)-114); - } - - if(0 > var11.aShortArray2434[var4]) { - Objects.requireNonNull(var7.aClass3_Sub24_Sub1_2507).method429(-1); - } - - if(var7.anInt2517 >= 0) { - Class3_Sub22 var9 = this.aClass3_Sub22ArrayArray3513[var2][var7.anInt2517]; - if(null != var9 && var9.anInt2506 < 0) { - this.aClass3_Sub22ArrayArray3512[var2][var9.anInt2520] = null; - var9.anInt2506 = 0; - } - - this.aClass3_Sub22ArrayArray3513[var2][var7.anInt2517] = var7; - } - - this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1215(var7); - this.aClass3_Sub22ArrayArray3512[var2][var4] = var7; - } - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "va.U(" + var1 + ',' + var2 + ',' + 71 + ',' + var4 + ')'); - } - } - - static boolean method487(int var0, byte var1) { - try { - if(var1 != -85) { - AudioChannel.threadPriority = 56; - } - - return 97 <= var0 && var0 <= 122 || var0 >= 65 && var0 <= 90; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "va.EB(" + var0 + ',' + var1 + ')'); - } - } - - private void method488(int var2) { - try { - int var3 = 240 & var2; - int var4; - int var5; - int var6; - if(var3 == 128) { - var4 = 15 & var2; - var6 = (8353073 & var2) >> 16; - var5 = (32634 & var2) >> 8; - this.method493((byte)-95, var5, var6, var4); - } else if(144 == var3) { - var5 = 127 & var2 >> 8; - var4 = var2 & 15; - var6 = 127 & var2 >> 16; - if(var6 > 0) { - this.method486(var6, var4, var5); - } else { - this.method493((byte)-122, var5, 64, var4); - } - - } else if(160 == var3) { - var4 = var2 & 15; - var5 = 127 & var2 >> 8; - var6 = 127 & var2 >> 16; - this.method495(var6, var5, var4); - } else if(var3 == 176) { - var5 = (32630 & var2) >> 8; - var4 = var2 & 15; - var6 = (8388489 & var2) >> 16; - if(var5 == 0) { - this.anIntArray3506[var4] = (var6 << 14) + Unsorted.bitwiseAnd(-2080769, this.anIntArray3506[var4]); - } - - if(var5 == 32) { - this.anIntArray3506[var4] = Unsorted.bitwiseAnd(-16257, this.anIntArray3506[var4]) + (var6 << 7); - } - - if(var5 == 1) { - this.anIntArray3502[var4] = (var6 << 7) + Unsorted.bitwiseAnd(this.anIntArray3502[var4], -16257); - } - - if(33 == var5) { - this.anIntArray3502[var4] = Unsorted.bitwiseAnd(-128, this.anIntArray3502[var4]) - -var6; - } - - if(var5 == 5) { - this.anIntArray3510[var4] = Unsorted.bitwiseAnd(this.anIntArray3510[var4], -16257) + (var6 << 7); - } - - if(var5 == 37) { - this.anIntArray3510[var4] = var6 + Unsorted.bitwiseAnd(this.anIntArray3510[var4], -128); - } - - if(var5 == 7) { - this.anIntArray3497[var4] = (var6 << 7) + Unsorted.bitwiseAnd(this.anIntArray3497[var4], -16257); - } - - if(var5 == 39) { - this.anIntArray3497[var4] = Unsorted.bitwiseAnd(this.anIntArray3497[var4], -128) + var6; - } - - if(var5 == 10) { - this.anIntArray3498[var4] = Unsorted.bitwiseAnd(-16257, this.anIntArray3498[var4]) - -(var6 << 7); - } - - if(var5 == 42) { - this.anIntArray3498[var4] = var6 + Unsorted.bitwiseAnd(-128, this.anIntArray3498[var4]); - } - - if(var5 == 11) { - this.anIntArray3514[var4] = Unsorted.bitwiseAnd(this.anIntArray3514[var4], -16257) + (var6 << 7); - } - - if(var5 == 43) { - this.anIntArray3514[var4] = var6 + Unsorted.bitwiseAnd(-128, this.anIntArray3514[var4]); - } - - if(var5 == 64) { - if(64 <= var6) { - this.anIntArray3518[var4] = TextureOperation3.bitwiseOr(this.anIntArray3518[var4], 1); - } else { - this.anIntArray3518[var4] = Unsorted.bitwiseAnd(this.anIntArray3518[var4], -2); - } - } - - if(var5 == 65) { - if(var6 < 64) { - this.method502(var4, (byte) 56 ^ -57); - this.anIntArray3518[var4] = Unsorted.bitwiseAnd(this.anIntArray3518[var4], -3); - } else { - this.anIntArray3518[var4] = TextureOperation3.bitwiseOr(this.anIntArray3518[var4], 2); - } - } - - if(var5 == 99) { - this.anIntArray3500[var4] = (var6 << 7) + Unsorted.bitwiseAnd(this.anIntArray3500[var4], 127); - } - - if(var5 == 98) { - this.anIntArray3500[var4] = Unsorted.bitwiseAnd(this.anIntArray3500[var4], 16256) - -var6; - } - - if(var5 == 101) { - this.anIntArray3500[var4] = (var6 << 7) + Unsorted.bitwiseAnd(127, this.anIntArray3500[var4]) + 16384; - } - - if(var5 == 100) { - this.anIntArray3500[var4] = var6 + Unsorted.bitwiseAnd(16256, this.anIntArray3500[var4]) + 16384; - } - - if(var5 == 120) { - this.method481((byte)-50, var4); - } - - if(var5 == 121) { - this.method480(var4); - } - - if(123 == var5) { - this.method489(-32323, var4); - } - - int var7; - if(6 == var5) { - var7 = this.anIntArray3500[var4]; - if(var7 == 16384) { - this.anIntArray3504[var4] = Unsorted.bitwiseAnd(this.anIntArray3504[var4], -16257) + (var6 << 7); - } - } - - if(var5 == 38) { - var7 = this.anIntArray3500[var4]; - if(var7 == 16384) { - this.anIntArray3504[var4] = Unsorted.bitwiseAnd(this.anIntArray3504[var4], -128) + var6; - } - } - - if(var5 == 16) { - this.anIntArray3519[var4] = Unsorted.bitwiseAnd(this.anIntArray3519[var4], -16257) - -(var6 << 7); - } - - if(var5 == 48) { - this.anIntArray3519[var4] = Unsorted.bitwiseAnd(this.anIntArray3519[var4], -128) - -var6; - } - - if(var5 == 81) { - if(var6 >= 64) { - this.anIntArray3518[var4] = TextureOperation3.bitwiseOr(this.anIntArray3518[var4], 4); - } else { - this.method497(var4, -102); - this.anIntArray3518[var4] = Unsorted.bitwiseAnd(this.anIntArray3518[var4], -5); - } - } - - if(var5 == 17) { - this.method482((byte)-117, var4, (var6 << 7) + (this.anIntArray3520[var4] & -16257)); - } - - if(var5 == 49) { - this.method482((byte)-61, var4, (this.anIntArray3520[var4] & -128) + var6); - } - - } else if (var3 == 192) { - var5 = var2 >> 8 & 127; - var4 = 15 & var2; - this.method484(this.anIntArray3506[var4] - -var5, var4); - } else if (var3 == 208) { - var4 = 15 & var2; - var5 = (var2 & 32549) >> 8; - this.method478(var4, var5); - } else if (var3 == 224) { - var4 = 15 & var2; - var5 = (var2 >> 9 & 16256) + ((32702 & var2) >> 8); - this.method476(var4, var5); - } else { - var3 = var2 & 0xFF; - if (255 == var3) { - this.method500(true, (byte) -40); - } - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "va.W(" + (byte) 56 + ',' + var2 + ')'); - } - } - - private void method489(int var1, int var2) { - try { - for(Class3_Sub22 var3 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1222(); var3 != null; var3 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1221()) { - if((var2 < 0 || var3.anInt2514 == var2) && var3.anInt2506 < 0) { - this.aClass3_Sub22ArrayArray3512[var3.anInt2514][var3.anInt2520] = null; - var3.anInt2506 = 0; - } - } - - if(var1 != -32323) { - this.anInt3525 = -99; - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.AA(" + var1 + ',' + var2 + ')'); - } - } - - final synchronized void method490(boolean var1, Class3_Sub27 var2) { - try { - this.method475(var1, var2, true); - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.TA(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + 17774 + ')'); - } - } - - final synchronized void method413(int[] var1, int var2, int var3) { - try { - if(this.aClass78_3505.method1373()) { - int var4 = this.aClass78_3505.anInt1116 * this.anInt3511 / Class21.sampleRate; - - while(true) { - long var5 = this.aLong3526 - -((long)var3 * (long)var4); - if(this.aLong3523 + -var5 < 0) { - int var7 = (int)((-1L + this.aLong3523 - this.aLong3526 + (long)var4) / (long)var4); - this.aLong3526 += (long)var4 * (long)var7; - this.aClass3_Sub24_Sub3_3527.method413(var1, var2, var7); - var3 -= var7; - var2 += var7; - this.method494(100); - if(this.aClass78_3505.method1373()) { - continue; - } - break; - } - - this.aLong3526 = var5; - break; - } - } - - this.aClass3_Sub24_Sub3_3527.method413(var1, var2, var3); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "va.E(" + (var1 != null?"{...}":"null") + ',' + var2 + ',' + var3 + ')'); - } - } - - final boolean method492(int var1, int var2, Class3_Sub22 var3, int[] var5) { - try { - var3.anInt2512 = Class21.sampleRate / 100; - if(var3.anInt2506 >= 0 && (var3.aClass3_Sub24_Sub1_2507 == null || var3.aClass3_Sub24_Sub1_2507.method444())) { - var3.method401(); - var3.unlink(); - if(var3.anInt2517 > 0 && var3 == this.aClass3_Sub22ArrayArray3513[var3.anInt2514][var3.anInt2517]) { - this.aClass3_Sub22ArrayArray3513[var3.anInt2514][var3.anInt2517] = null; - } - - return false; - } else { - int var6 = var3.anInt2502; - - if(var6 > 0) { - var6 -= (int)(0.5D + Math.pow(2.0D, (double)this.anIntArray3510[var3.anInt2514] * 4.921259842519685E-4D) * 16.0D); - if(var6 < 0) { - var6 = 0; - } - - var3.anInt2502 = var6; - } - - var3.aClass3_Sub24_Sub1_2507.method443(this.method498(var3)); - Class166 var7 = var3.aClass166_2504; - var3.anInt2508 += var7.anInt2077; - ++var3.anInt2515; - double var9 = (double)((var3.anInt2520 - 60 << 8) + (var3.anInt2502 * var3.anInt2522 >> 12)) * 5.086263020833333E-6D; - boolean var8 = false; - if(var7.anInt2078 > 0) { - if(var7.anInt2063 > 0) { - var3.anInt2523 += (int)(Math.pow(2.0D, var9 * (double)var7.anInt2063) * 128.0D + 0.5D); - } else { - var3.anInt2523 += 128; - } - - if(var3.anInt2523 * var7.anInt2078 >= 819200) { - var8 = true; - } - } - - if(var7.aByteArray2064 != null) { - if(var7.anInt2067 <= 0) { - var3.anInt2511 += 128; - } else { - var3.anInt2511 += (int)(0.5D + Math.pow(2.0D, (double)var7.anInt2067 * var9) * 128.0D); - } - - while(var7.aByteArray2064.length - 2 > var3.anInt2501 && var3.anInt2511 > (65280 & var7.aByteArray2064[var3.anInt2501 - -2] << 8)) { - var3.anInt2501 += 2; - } - - if(var3.anInt2501 == -2 + var7.aByteArray2064.length && var7.aByteArray2064[1 + var3.anInt2501] == 0) { - var8 = true; - } - } - - if(var3.anInt2506 >= 0 && null != var7.aByteArray2076 && (1 & this.anIntArray3518[var3.anInt2514]) == 0 && (0 > var3.anInt2517 || this.aClass3_Sub22ArrayArray3513[var3.anInt2514][var3.anInt2517] != var3)) { - if(var7.anInt2071 > 0) { - var3.anInt2506 += (int)(Math.pow(2.0D, (double)var7.anInt2071 * var9) * 128.0D + 0.5D); - } else { - var3.anInt2506 += 128; - } - - while(var3.anInt2519 < -2 + var7.aByteArray2076.length && var3.anInt2506 > (255 & var7.aByteArray2076[2 + var3.anInt2519]) << 8) { - var3.anInt2519 += 2; - } - - if(-2 + var7.aByteArray2076.length == var3.anInt2519) { - var8 = true; - } - } - - if(var8) { - var3.aClass3_Sub24_Sub1_2507.method417(var3.anInt2512); - if(null == var5) { - var3.aClass3_Sub24_Sub1_2507.method415(var1); - } else { - var3.aClass3_Sub24_Sub1_2507.method413(var5, var2, var1); - } - - if(var3.aClass3_Sub24_Sub1_2507.method445()) { - this.aClass3_Sub24_Sub3_3527.aClass3_Sub24_Sub2_3495.method457(var3.aClass3_Sub24_Sub1_2507); - } - - var3.method401(); - if(var3.anInt2506 >= 0) { - var3.unlink(); - if(var3.anInt2517 > 0 && this.aClass3_Sub22ArrayArray3513[var3.anInt2514][var3.anInt2517] == var3) { - this.aClass3_Sub22ArrayArray3513[var3.anInt2514][var3.anInt2517] = null; - } - } - - return false; - } else { - var3.aClass3_Sub24_Sub1_2507.method450(var3.anInt2512, this.method508(var3), this.method496(var3)); - return true; - } - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "va.BA(" + var1 + ',' + var2 + ',' + (var3 != null?"{...}":"null") + ',' + (byte) 14 + ',' + (var5 != null?"{...}":"null") + ')'); - } - } - - final synchronized Class3_Sub24 method411() { - try { - return this.aClass3_Sub24_Sub3_3527; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "va.Q()"); - } - } - - private void method493(byte var1, int var2, int var3, int var4) { - try { - Class3_Sub22 var5 = this.aClass3_Sub22ArrayArray3512[var4][var2]; - if(null != var5) { - if(var1 > -92) { - this.aClass3_Sub24_Sub3_3527 = null; - } - - this.aClass3_Sub22ArrayArray3512[var4][var2] = null; - if((2 & this.anIntArray3518[var4]) == 0) { - var5.anInt2506 = 0; - } else { - for(Class3_Sub22 var6 = (Class3_Sub22) Objects.requireNonNull(this.aClass3_Sub24_Sub3_3527).aLinkedList_3489.method1222(); null != var6; var6 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1221()) { - if(var5.anInt2514 == var6.anInt2514 && var6.anInt2506 < 0 && var6 != var5) { - var5.anInt2506 = 0; - break; - } - } - } - - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "va.CB(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - private void method494(int var1) { - try { - int var2 = this.anInt3525; - int var3 = this.anInt3524; - if(var1 <= 54) { - this.method505((byte)124); - } - - long var4 = this.aLong3523; - if(this.aClass3_Sub27_3528 != null && 0 == var3) { - this.method475(this.aBoolean3522, this.aClass3_Sub27_3528, false); - this.method494(71); - } else { - while(this.anInt3524 == var3) { - while(var3 == this.aClass78_3505.anIntArray1114[var2]) { - this.aClass78_3505.method1376(var2); - int var6 = this.aClass78_3505.method1375(var2); - if(1 == var6) { - this.aClass78_3505.method1384(); - this.aClass78_3505.method1381(var2); - if(this.aClass78_3505.method1371()) { - if(this.aClass3_Sub27_3528 != null) { - this.method490(this.aBoolean3522, this.aClass3_Sub27_3528); - this.method494(126); - return; - } - - if(!this.aBoolean3522 || var3 == 0) { - this.method500(true, (byte)-40); - this.aClass78_3505.method1383(); - return; - } - - this.aClass78_3505.method1372(var4); - } - break; - } - - if((var6 & 128) != 0) { - this.method488(var6); - } - - this.aClass78_3505.method1377(var2); - this.aClass78_3505.method1381(var2); - } - - var2 = this.aClass78_3505.method1382(); - var3 = this.aClass78_3505.anIntArray1114[var2]; - var4 = this.aClass78_3505.method1370(var3); - } - - this.anInt3525 = var2; - this.aLong3523 = var4; - this.anInt3524 = var3; - if(this.aClass3_Sub27_3528 != null && var3 > 0) { - this.anInt3525 = -1; - this.anInt3524 = 0; - this.aLong3523 = this.aClass78_3505.method1370(this.anInt3524); - } - - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "va.GB(" + var1 + ')'); - } - } - - private void method495(int var1, int var2, int var4) { - try { - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "va.GA(" + var1 + ',' + var2 + ',' + 17387 + ',' + var4 + ')'); - } - } - - private int method496(Class3_Sub22 var2) { - try { - int var3 = this.anIntArray3498[var2.anInt2514]; - - return var3 < 8192 ?32 + var2.anInt2503 * var3 >> 6 :16384 - ((128 + -var2.anInt2503) * (16384 + -var3) + 32 >> 6); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.BB(" + 0 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - final synchronized void method415(int var1) { - try { - if(this.aClass78_3505.method1373()) { - int var2 = this.aClass78_3505.anInt1116 * this.anInt3511 / Class21.sampleRate; - - while(true) { - long var3 = this.aLong3526 - -((long)var1 * (long)var2); - if(this.aLong3523 + -var3 < 0) { - int var5 = (int)(((long)var2 + (-this.aLong3526 + this.aLong3523 - 1L)) / (long)var2); - var1 -= var5; - this.aLong3526 += (long)var5 * (long)var2; - this.aClass3_Sub24_Sub3_3527.method415(var5); - this.method494(64); - if(this.aClass78_3505.method1373()) { - continue; - } - break; - } - - this.aLong3526 = var3; - break; - } - } - - this.aClass3_Sub24_Sub3_3527.method415(var1); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "va.R(" + var1 + ')'); - } - } - - private void method497(int var1, int var2) { - try { - if(0 != (4 & this.anIntArray3518[var1])) { - for(Class3_Sub22 var4 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1222(); null != var4; var4 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1221()) { - if(var1 == var4.anInt2514) { - var4.anInt2516 = 0; - } - } - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "va.A(" + var1 + ',' + var2 + ')'); - } - } - - private int method498(Class3_Sub22 var2) { - try { - Class166 var4 = var2.aClass166_2504; - int var3 = (var2.anInt2522 * var2.anInt2502 >> 12) + var2.anInt2510; - var3 += this.anIntArray3504[var2.anInt2514] * (-8192 + this.anIntArray3499[var2.anInt2514]) >> 12; - int var5; - if(var4.anInt2077 > 0 && (var4.anInt2066 > 0 || this.anIntArray3502[var2.anInt2514] > 0)) { - var5 = var4.anInt2066 << 2; - int var6 = var4.anInt2069 << 1; - if(var2.anInt2515 < var6) { - var5 = var2.anInt2515 * var5 / var6; - } - - var5 += this.anIntArray3502[var2.anInt2514] >> 7; - double var7 = Math.sin(0.01227184630308513D * (double)(511 & var2.anInt2508)); - var3 += (int)((double)var5 * var7); - } - - var5 = (int)(0.5D + (double)(256 * var2.aPcmSound_2509.frequency) * Math.pow(2.0D, (double)var3 * 3.255208333333333E-4D) / (double)Class21.sampleRate); - - return var5 >= 1?var5:1; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "va.OA(" + (byte) 85 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - final int method499() { - try { - - return this.anInt3521; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "va.IA(" + false + ')'); - } - } - - private void method500(boolean var1, byte var2) { - try { - if(var1) { - this.method481((byte)91, -1); - } else { - this.method489(var2 + -32283, -1); - } - - this.method480(-1); - if(var2 == -40) { - int var3; - for(var3 = 0; 16 > var3; ++var3) { - this.anIntArray3515[var3] = this.anIntArray3501[var3]; - } - - for(var3 = 0; var3 < 16; ++var3) { - this.anIntArray3506[var3] = Unsorted.bitwiseAnd(-128, this.anIntArray3501[var3]); - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.F(" + var1 + ',' + var2 + ')'); - } - } - - final void method501(Class3_Sub22 var1, boolean var2, byte var3) { - try { - int var4 = var1.aPcmSound_2509.samples.length; - int var5; - if(var2 && var1.aPcmSound_2509.aBoolean3031) { - int var6 = var4 + (var4 - var1.aPcmSound_2509.anInt3033); - var4 <<= 8; - var5 = (int)((long)var6 * (long)this.anIntArray3519[var1.anInt2514] >> 6); - if(var4 <= var5) { - var1.aClass3_Sub24_Sub1_2507.method442(); - var5 = -1 + (var4 - -var4) + -var5; - } - } else { - var5 = (int)((long)var4 * (long)this.anIntArray3519[var1.anInt2514] >> 6); - } - - var1.aClass3_Sub24_Sub1_2507.method434(var5); - if(var3 >= -70) { - this.aLong3523 = 47L; - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "va.CA(" + (var1 != null?"{...}":"null") + ',' + var2 + ',' + var3 + ')'); - } - } - - private void method502(int var1, int var2) { - try { - if(var2 != ~(this.anIntArray3518[var1] & 2)) { - for(Class3_Sub22 var3 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1222(); var3 != null; var3 = (Class3_Sub22)this.aClass3_Sub24_Sub3_3527.aLinkedList_3489.method1221()) { - if(var3.anInt2514 == var1 && this.aClass3_Sub22ArrayArray3512[var1][var3.anInt2520] == null && var3.anInt2506 < 0) { - var3.anInt2506 = 0; - } - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.T(" + var1 + ',' + var2 + ')'); - } - } - - static void method503(int var1) { - try { - Class8.anInt101 = var1; - Class3_Sub28_Sub8.anInt3611 = 20; - AbstractSprite.anInt3704 = 3; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "va.FA(" + (byte) -53 + ',' + var1 + ')'); - } - } - - final boolean method504(Class3_Sub22 var1, int var2) { - try { - if(var1.aClass3_Sub24_Sub1_2507 == null) { - if(var1.anInt2506 >= 0) { - var1.unlink(); - if(var1.anInt2517 > 0 && this.aClass3_Sub22ArrayArray3513[var1.anInt2514][var1.anInt2517] == var1) { - this.aClass3_Sub22ArrayArray3513[var1.anInt2514][var1.anInt2517] = null; - } - } - - return false; - } else { - return true; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.WA(" + (var1 != null?"{...}":"null") + ',' + var2 + ')'); - } - } - - final synchronized void method505(byte var1) { - try { - this.method507(true); - if(var1 > -125) { - this.anIntArray3520 = null; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "va.RA(" + var1 + ')'); - } - } - - final synchronized void method506(int var2) { - try { - this.anInt3521 = var2; - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.LA(" + 128 + ',' + var2 + ')'); - } - } - - private synchronized void method507(boolean var1) { - try { - this.aClass78_3505.method1383(); - this.aClass3_Sub27_3528 = null; - this.method500(var1, (byte)-40); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "va.MA(" + var1 + ',' + (byte) -68 + ')'); - } - } - - private int method508(Class3_Sub22 var2) { - try { - - if(this.anIntArray3516[var2.anInt2514] == 0) { - return 0; - } else { - Class166 var3 = var2.aClass166_2504; - int var4 = 4096 + this.anIntArray3497[var2.anInt2514] * this.anIntArray3514[var2.anInt2514] >> 13; - var4 = 16384 + var4 * var4 >> 15; - var4 = 16384 + var2.anInt2513 * var4 >> 15; - var4 = 128 + var4 * this.anInt3521 >> 8; - var4 = this.anIntArray3516[var2.anInt2514] * var4 + 128 >> 8; - if(0 < var3.anInt2078) { - var4 = (int)(0.5D + Math.pow(0.5D, (double)var2.anInt2523 * 1.953125E-5D * (double)var3.anInt2078) * (double)var4); - } - - int var5; - int var6; - int var7; - int var8; - if(null != var3.aByteArray2064) { - var5 = var2.anInt2511; - var6 = var3.aByteArray2064[1 + var2.anInt2501]; - if(var3.aByteArray2064.length - 2 > var2.anInt2501) { - var8 = (var3.aByteArray2064[2 + var2.anInt2501] & 0xFF) << 8; - var7 = 65280 & var3.aByteArray2064[var2.anInt2501] << 8; - var6 += (var3.aByteArray2064[3 + var2.anInt2501] + -var6) * (var5 - var7) / (var8 + -var7); - } - - var4 = 32 + var6 * var4 >> 6; - } - - if(var2.anInt2506 > 0 && null != var3.aByteArray2076) { - var5 = var2.anInt2506; - var6 = var3.aByteArray2076[1 + var2.anInt2519]; - if(-2 + var3.aByteArray2076.length > var2.anInt2519) { - var7 = 65280 & var3.aByteArray2076[var2.anInt2519] << 8; - var8 = (var3.aByteArray2076[var2.anInt2519 + 2] & 0xFF) << 8; - var6 += (var5 - var7) * (-var6 + var3.aByteArray2076[3 + var2.anInt2519]) / (-var7 + var8); - } - - var4 = 32 + var4 * var6 >> 6; - } - - return var4; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "va.UA(" + (byte) 36 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - final synchronized Class3_Sub24 method414() { - try { - return null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "va.B()"); - } - } - - public Class3_Sub24_Sub4() { - try { - this.aHashTable_3508 = new HashTable(128); - this.method483(); - this.method500(true, (byte)-40); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "va.()"); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub25.java b/Client/src/main/java/org/runite/client/Class3_Sub25.java deleted file mode 100644 index 609fccb7c..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub25.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.util.ArrayUtils; - -final class Class3_Sub25 extends Linkable { - - int[] anIntArray2547 = new int[]{-1}; - static short[] aShortArray2548; - int[] anIntArray2551 = new int[]{0}; - static Class129 aClass129_2552; - - - static void method509(int var1) { - try { - --Unsorted.menuOptionCount; - if(Unsorted.menuOptionCount != var1) { - ArrayUtils.arraycopy(Class140_Sub7.aClass94Array2935, var1 + 1, Class140_Sub7.aClass94Array2935, var1, -var1 + Unsorted.menuOptionCount); - ArrayUtils.arraycopy(Class163_Sub2_Sub1.aClass94Array4016, 1 + var1, Class163_Sub2_Sub1.aClass94Array4016, var1, Unsorted.menuOptionCount - var1); - ArrayUtils.arraycopy(Class114.anIntArray1578, 1 + var1, Class114.anIntArray1578, var1, -var1 + Unsorted.menuOptionCount); - ArrayUtils.arraycopy(TextureOperation27.aShortArray3095, 1 + var1, TextureOperation27.aShortArray3095, var1, Unsorted.menuOptionCount + -var1); - ArrayUtils.arraycopy(Unsorted.aLongArray3271, 1 + var1, Unsorted.aLongArray3271, var1, -var1 + Unsorted.menuOptionCount); - ArrayUtils.arraycopy(Class117.anIntArray1613, var1 + 1, Class117.anIntArray1613, var1, -var1 + Unsorted.menuOptionCount); - ArrayUtils.arraycopy(Class27.anIntArray512, 1 + var1, Class27.anIntArray512, var1, Unsorted.menuOptionCount + -var1); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "qe.A(" + 1 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub26.java b/Client/src/main/java/org/runite/client/Class3_Sub26.java deleted file mode 100644 index 2c7e2c76e..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub26.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -final class Class3_Sub26 extends Linkable { - - int anInt2553; - static int paramAffid = 0; - int anInt2555; - static int anInt2556 = 0; - static LinkedList aLinkedList_2557 = new LinkedList(); - static int[] anIntArray2559 = new int[]{0, 1, 2, 3, 4, 5, 6, 14}; - static AbstractSprite aAbstractSprite_2560; - static int anInt2561 = -1; - static Class3_Sub24_Sub2 aClass3_Sub24_Sub2_2563; - - - static void method512() { - try { - Unsorted.aReferenceCache_4043.clear(); - CS2Script.aReferenceCache_2442.clear(); - Class154.aReferenceCache_1964.clear(); - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "qi.D(" + (byte) -108 + ')'); - } - } - - static int method513(int var0) { - try { - int var2 = 0; - if(var0 < 0 || var0 >= 65536) { - var2 += 16; - var0 >>>= 16; - } - - if(var0 >= 256) { - var2 += 8; - var0 >>>= 8; - } - - if(var0 >= 16) { - var2 += 4; - var0 >>>= 4; - } - - if(4 <= var0) { - var0 >>>= 2; - var2 += 2; - } - - if(var0 >= 1) { - var0 >>>= 1; - ++var2; - } - - return var0 + var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "qi.B(" + var0 + ',' + 4 + ')'); - } - } - - Class3_Sub26(int var1, int var2) { - try { - this.anInt2555 = var2; - this.anInt2553 = var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "qi.(" + var1 + ',' + var2 + ')'); - } - } - - static int method514(int var0, int var1, int var3) { - try { - var0 &= 3; - - return var0 == 0 ?var1:(1 == var0?var3:(2 != var0?-var3 + 1023:1023 + -var1)); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "qi.C(" + var0 + ',' + var1 + ',' + (byte) -83 + ',' + var3 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub27.java b/Client/src/main/java/org/runite/client/Class3_Sub27.java deleted file mode 100644 index bdf1f6098..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub27.java +++ /dev/null @@ -1,410 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.data.HashTable; - -final class Class3_Sub27 extends Linkable { - - HashTable aHashTable_2564; - byte[] aByteArray2565; - - - final void method515() { - this.aHashTable_2564 = null; - } - - final void method516() { - if (this.aHashTable_2564 == null) { - this.aHashTable_2564 = new HashTable(16); - int[] var1 = new int[16]; - int[] var2 = new int[16]; - var1[9] = var2[9] = 128; - Class78 var3 = new Class78(this.aByteArray2565); - int var4 = var3.method1374(); - - int var5; - for (var5 = 0; var5 < var4; ++var5) { - var3.method1376(var5); - var3.method1377(var5); - var3.method1381(var5); - } - - while (true) { - var5 = var3.method1382(); - int var6 = var3.anIntArray1114[var5]; - - while (var3.anIntArray1114[var5] == var6) { - var3.method1376(var5); - int var7 = var3.method1375(var5); - if (var7 == 1) { - var3.method1384(); - var3.method1381(var5); - if (var3.method1371()) { - return; - } - break; - } - - int var8 = var7 & 240; - int var9; - int var10; - int var11; - if (var8 == 176) { - var9 = var7 & 15; - var10 = var7 >> 8 & 127; - var11 = var7 >> 16 & 127; - if (var10 == 0) { - var1[var9] = (var1[var9] & -2080769) + (var11 << 14); - } - - if (var10 == 32) { - var1[var9] = (var1[var9] & -16257) + (var11 << 7); - } - } - - if (var8 == 192) { - var9 = var7 & 15; - var10 = var7 >> 8 & 127; - var2[var9] = var1[var9] + var10; - } - - if (var8 == 144) { - var9 = var7 & 15; - var10 = var7 >> 8 & 127; - var11 = var7 >> 16 & 127; - if (var11 > 0) { - int var12 = var2[var9]; - Class3_Sub6 var13 = (Class3_Sub6) this.aHashTable_2564.get(var12); - if (var13 == null) { - var13 = new Class3_Sub6(new byte[128]); - this.aHashTable_2564.put(var12, var13); - } - - var13.aByteArray2289[var10] = 1; - } - } - - var3.method1377(var5); - var3.method1381(var5); - } - } - } - } - - static Class3_Sub27 method517(CacheIndex var0, int var1, int var2) { - byte[] var3 = var0.getFile(var1, var2); - return var3 == null ? null : new Class3_Sub27(new DataBuffer(var3)); - } - - private Class3_Sub27(DataBuffer buffer) { - buffer.index = buffer.buffer.length - 3; - int var2 = buffer.readUnsignedByte(); - int var3 = buffer.readUnsignedShort(); - int var4 = 14 + var2 * 10; - buffer.index = 0; - int var5 = 0; - int var6 = 0; - int var7 = 0; - int var8 = 0; - int var9 = 0; - int var10 = 0; - int var11 = 0; - int var12 = 0; - int var13 = 0; - - int var14; - int var15; - while (var13 < var2) { - var14 = -1; - - while (true) { - var15 = buffer.readUnsignedByte(); - if (var15 != var14) { - ++var4; - } - - var14 = var15 & 15; - if (var15 == 7) { - ++var13; - break; - } - - if (var15 == 23) { - ++var5; - } else if (var14 == 0) { - ++var7; - } else if (var14 == 1) { - ++var8; - } else if (var14 == 2) { - ++var6; - } else if (var14 == 3) { - ++var9; - } else if (var14 == 4) { - ++var10; - } else if (var14 == 5) { - ++var11; - } else { - if (var14 != 6) { - throw new RuntimeException(); - } - - ++var12; - } - } - } - - var4 += 5 * var5; - var4 += 2 * (var7 + var8 + var6 + var9 + var11); - var4 += var10 + var12; - var13 = buffer.index; - var14 = var2 + var5 + var6 + var7 + var8 + var9 + var10 + var11 + var12; - - for (var15 = 0; var15 < var14; ++var15) { - buffer.method741(); - } - - var4 += buffer.index - var13; - var15 = buffer.index; - int var16 = 0; - int var17 = 0; - int var18 = 0; - int var19 = 0; - int var20 = 0; - int var21 = 0; - int var22 = 0; - int var23 = 0; - int var24 = 0; - int var25 = 0; - int var26 = 0; - int var27 = 0; - int var28 = 0; - - int var29; - for (var29 = 0; var29 < var6; ++var29) { - var28 = var28 + buffer.readUnsignedByte() & 127; - if (var28 == 0 || var28 == 32) { - ++var12; - } else if (var28 == 1) { - ++var16; - } else if (var28 == 33) { - ++var17; - } else if (var28 == 7) { - ++var18; - } else if (var28 == 39) { - ++var19; - } else if (var28 == 10) { - ++var20; - } else if (var28 == 42) { - ++var21; - } else if (var28 == 99) { - ++var22; - } else if (var28 == 98) { - ++var23; - } else if (var28 == 101) { - ++var24; - } else if (var28 == 100) { - ++var25; - } else if (var28 != 64 && var28 != 65 && var28 != 120 && var28 != 121 && var28 != 123) { - ++var27; - } else { - ++var26; - } - } - - var29 = 0; - int var30 = buffer.index; - buffer.index += var26; - int var31 = buffer.index; - buffer.index += var11; - int var32 = buffer.index; - buffer.index += var10; - int var33 = buffer.index; - buffer.index += var9; - int var34 = buffer.index; - buffer.index += var16; - int var35 = buffer.index; - buffer.index += var18; - int var36 = buffer.index; - buffer.index += var20; - int var37 = buffer.index; - buffer.index += var7 + var8 + var11; - int var38 = buffer.index; - buffer.index += var7; - int var39 = buffer.index; - buffer.index += var27; - int var40 = buffer.index; - buffer.index += var8; - int var41 = buffer.index; - buffer.index += var17; - int var42 = buffer.index; - buffer.index += var19; - int var43 = buffer.index; - buffer.index += var21; - int var44 = buffer.index; - buffer.index += var12; - int var45 = buffer.index; - buffer.index += var9; - int var46 = buffer.index; - buffer.index += var22; - int var47 = buffer.index; - buffer.index += var23; - int var48 = buffer.index; - buffer.index += var24; - int var49 = buffer.index; - buffer.index += var25; - int var50 = buffer.index; - buffer.index += var5 * 3; - this.aByteArray2565 = new byte[var4]; - DataBuffer var51 = new DataBuffer(this.aByteArray2565); - var51.writeInt(1297377380); - var51.writeInt(6); - var51.writeShort(var2 > 1 ? 1 : 0); - var51.writeShort(var2); - var51.writeShort(var3); - buffer.index = var13; - int var52 = 0; - int var53 = 0; - int var54 = 0; - int var55 = 0; - int var56 = 0; - int var57 = 0; - int var58 = 0; - int[] var59 = new int[128]; - var28 = 0; - int var60 = 0; - - while (var60 < var2) { - var51.writeInt(1297379947); - var51.index += 4; - int var61 = var51.index; - int var62 = -1; - - while (true) { - int var63 = buffer.method741(); - var51.method771(var63); - int var64 = buffer.buffer[var29++] & 0xFF; - boolean var65 = var64 != var62; - var62 = var64 & 15; - if (var64 == 7) { - if (var65) { - var51.writeByte(255); - } - - var51.writeByte(47); - var51.writeByte(0); - var51.method742(var51.index - var61); - ++var60; - break; - } - - if (var64 == 23) { - if (var65) { - var51.writeByte(255); - } - - var51.writeByte(81); - var51.writeByte(3); - var51.writeByte(buffer.buffer[var50++]); - var51.writeByte(buffer.buffer[var50++]); - var51.writeByte(buffer.buffer[var50++]); - } else { - var52 ^= var64 >> 4; - if (var62 == 0) { - if (var65) { - var51.writeByte(144 + var52); - } - - var53 += buffer.buffer[var37++]; - var54 += buffer.buffer[var38++]; - var51.writeByte(var53 & 127); - var51.writeByte(var54 & 127); - } else if (var62 == 1) { - if (var65) { - var51.writeByte(128 + var52); - } - - var53 += buffer.buffer[var37++]; - var55 += buffer.buffer[var40++]; - var51.writeByte(var53 & 127); - var51.writeByte(var55 & 127); - } else if (var62 == 2) { - if (var65) { - var51.writeByte(176 + var52); - } - - var28 = var28 + buffer.buffer[var15++] & 127; - var51.writeByte(var28); - byte var66; - if (var28 == 0 || var28 == 32) { - var66 = buffer.buffer[var44++]; - } else if (var28 == 1) { - var66 = buffer.buffer[var34++]; - } else if (var28 == 33) { - var66 = buffer.buffer[var41++]; - } else if (var28 == 7) { - var66 = buffer.buffer[var35++]; - } else if (var28 == 39) { - var66 = buffer.buffer[var42++]; - } else if (var28 == 10) { - var66 = buffer.buffer[var36++]; - } else if (var28 == 42) { - var66 = buffer.buffer[var43++]; - } else if (var28 == 99) { - var66 = buffer.buffer[var46++]; - } else if (var28 == 98) { - var66 = buffer.buffer[var47++]; - } else if (var28 == 101) { - var66 = buffer.buffer[var48++]; - } else if (var28 == 100) { - var66 = buffer.buffer[var49++]; - } else if (var28 != 64 && var28 != 65 && var28 != 120 && var28 != 121 && var28 != 123) { - var66 = buffer.buffer[var39++]; - } else { - var66 = buffer.buffer[var30++]; - } - - int var67 = var66 + var59[var28]; - var59[var28] = var67; - var51.writeByte(var67 & 127); - } else if (var62 == 3) { - if (var65) { - var51.writeByte(224 + var52); - } - - var56 += buffer.buffer[var45++]; - var56 += buffer.buffer[var33++] << 7; - var51.writeByte(var56 & 127); - var51.writeByte(var56 >> 7 & 127); - } else if (var62 == 4) { - if (var65) { - var51.writeByte(208 + var52); - } - - var57 += buffer.buffer[var32++]; - var51.writeByte(var57 & 127); - } else if (var62 == 5) { - if (var65) { - var51.writeByte(160 + var52); - } - - var53 += buffer.buffer[var37++]; - var58 += buffer.buffer[var31++]; - var51.writeByte(var53 & 127); - var51.writeByte(var58 & 127); - } else { - if (var62 != 6) { - throw new RuntimeException(); - } - - if (var65) { - var51.writeByte(192 + var52); - } - - var51.writeByte(buffer.buffer[var44++]); - } - } - } - } - - } -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub1.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub1.java deleted file mode 100644 index 8fe67446d..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub1.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; - -import java.awt.*; - -public final class Class3_Sub28_Sub1 extends Node { - - static boolean aBoolean3531 = false; - static Class3_Sub20 aClass3_Sub20_3532 = new Class3_Sub20(0, 0); - int[] anIntArray3533; - int[] anIntArray3534; - int[] anIntArray3535; - static int anInt3536; - RSString quickChatMenu; - static int anInt3539; - static int dropAction; - static int counter; - int[] anIntArray3540; - - - final void method525() { - try { - int var2; - if (null != this.anIntArray3540) { - for (var2 = 0; var2 < this.anIntArray3540.length; ++var2) { - this.anIntArray3540[var2] = TextureOperation3.bitwiseOr(this.anIntArray3540[var2], 32768); - } - } - - if (null != this.anIntArray3534) { - for (var2 = 0; this.anIntArray3534.length > var2; ++var2) { - this.anIntArray3534[var2] = TextureOperation3.bitwiseOr(this.anIntArray3534[var2], 32768); - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bc.O(" + -85 + ')'); - } - } - - final int method526(int var1) { - try { - if (this.anIntArray3540 != null) { - for (int var3 = 0; this.anIntArray3540.length > var3; ++var3) { - if (var1 == this.anIntArray3533[var3]) { - return this.anIntArray3540[var3]; - } - } - - } - return -1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bc.Q(" + var1 + ',' + 0 + ')'); - } - } - - private void method527(DataBuffer buffer, int var3) { - try { - - if (var3 == 1) { - this.quickChatMenu = buffer.readString(); - } else { - int var4; - int var5; - if (var3 == 2) { - var4 = buffer.readUnsignedByte(); - this.anIntArray3534 = new int[var4]; - this.anIntArray3535 = new int[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.anIntArray3534[var5] = buffer.readUnsignedShort(); - this.anIntArray3535[var5] = TextureOperation29.method322(buffer.readSignedByte()); - } - } else if (var3 == 3) { - var4 = buffer.readUnsignedByte(); - this.anIntArray3540 = new int[var4]; - this.anIntArray3533 = new int[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.anIntArray3540[var5] = buffer.readUnsignedShort(); - this.anIntArray3533[var5] = TextureOperation29.method322(buffer.readSignedByte()); - } - } - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "bc.E(" + (buffer != null ? "{...}" : "null") + ',' + 0 + ',' + var3 + ')'); - } - } - - final int method529(int var2) { - try { - if (null != this.anIntArray3534) { - for (int var4 = 0; this.anIntArray3534.length > var4; ++var4) { - if (var2 == this.anIntArray3535[var4]) { - return this.anIntArray3534[var4]; - } - } - - } - return -1; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bc.P(" + (byte) 50 + ',' + var2 + ')'); - } - } - - final void method530(DataBuffer buffer) { - try { - - while (true) { - int opcode = buffer.readUnsignedByte(); - if (opcode == 0) { - return; - } - - this.method527(buffer, opcode); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bc.D(" + (buffer != null ? "{...}" : "null") + ',' + (byte) 116 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub16_Sub1_Sub1.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub16_Sub1_Sub1.java deleted file mode 100644 index 0463f204a..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub16_Sub1_Sub1.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.runite.client; - - - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -public final class Class3_Sub28_Sub16_Sub1_Sub1 extends HDSprite { - - final void method650(int[] var1) { - this.anInt4075 = Class95.method1585((byte)111, this.width); - this.anInt4079 = Class95.method1585((byte)76, this.height); - byte[] var2 = new byte[this.anInt4075 * this.anInt4079 * 4]; - int var3 = 0; - int var4 = 0; - int var5 = (this.anInt4075 - this.width) * 4; - - for(int var6 = 0; var6 < this.height; ++var6) { - for(int var7 = 0; var7 < this.width; ++var7) { - int var8 = var1[var4++]; - if(var8 == 0) { - var3 += 4; - } else { - var2[var3++] = (byte)(var8 >> 16); - var2[var3++] = (byte)(var8 >> 8); - var2[var3++] = (byte)var8; - var2[var3++] = (byte)(var8 >> 24); - } - } - - var3 += var5; - } - - ByteBuffer var9 = ByteBuffer.wrap(var2); - GL var10 = HDToolKit.gl; - if(this.anInt4077 == -1) { - int[] var11 = new int[1]; - var10.glGenTextures(1, var11, 0); - this.anInt4077 = var11[0]; - } - - HDToolKit.bindTexture2D(this.anInt4077); - var10.glTexImage2D(3553, 0, 6408, this.anInt4075, this.anInt4079, 0, 6408, 5121, var9); - Class31.memory2D += var9.limit() - this.anInt4074; - this.anInt4074 = var9.limit(); - } - - Class3_Sub28_Sub16_Sub1_Sub1(int var1, int var2, int var3, int var4, int var5, int var6, int[] var7) { - super(var1, var2, var3, var4, var5, var6, var7); - } - - public Class3_Sub28_Sub16_Sub1_Sub1(SoftwareSprite var1) { - super(var1); - } -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub16_Sub2_Sub1.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub16_Sub2_Sub1.java deleted file mode 100644 index 886a2141a..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub16_Sub2_Sub1.java +++ /dev/null @@ -1,986 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; - -public final class Class3_Sub28_Sub16_Sub2_Sub1 extends SoftwareSprite { - - final void method660(int var1, int var2, double var7) { - try { - int var10 = -20 / 2; - int var11 = -20 / 2; - int var12 = (int)(Math.sin(var7) * 65536.0D); - int var13 = (int)(Math.cos(var7) * 65536.0D); - var12 = var12 * 256 >> 8; - var13 = var13 * 256 >> 8; - int var14 = (15 << 16) + var11 * var12 + var10 * var13; - int var15 = (15 << 16) + (var11 * var13 - var10 * var12); - int var16 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - - for(var2 = 0; var2 < 20; ++var2) { - int var17 = var16; - int var18 = var14; - int var19 = var15; - - for(var1 = -20; var1 < 0; ++var1) { - int var20 = this.anIntArray4081[(var18 >> 16) + (var19 >> 16) * this.width]; - int var21 = Toolkit.JAVA_TOOLKIT.getBuffer()[var17]; - int var22 = var20 >>> 24; - int var23 = 256 - var22; - Toolkit.JAVA_TOOLKIT.getBuffer()[var17++] = ((var20 & 16711935) * var22 + (var21 & 16711935) * var23 & -16711936) + ((var20 & 65280) * var22 + (var21 & 65280) * var23 & 16711680) >>> 8; - var18 += var13; - var19 -= var12; - } - - var14 += var12; - var15 += var13; - var16 += Toolkit.JAVA_TOOLKIT.width; - } - } catch (Exception var24) { - } - - } - - public final void drawAt(int var1, int var2) { - var1 += this.anInt3701; - var2 += this.anInt3698; - int var3 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var4 = 0; - int var5 = this.height; - int var6 = this.width; - int var7 = Toolkit.JAVA_TOOLKIT.width - var6; - int var8 = 0; - int var9; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var9 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var5 -= var9; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var4 += var9 * var6; - var3 += var9 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var2 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var9 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var6 -= var9; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var4 += var9; - var3 += var9; - var8 += var9; - var7 += var9; - } - - if(var1 + var6 > Toolkit.JAVA_TOOLKIT.clipRight) { - var9 = var1 + var6 - Toolkit.JAVA_TOOLKIT.clipRight; - var6 -= var9; - var8 += var9; - var7 += var9; - } - - if(var6 > 0 && var5 > 0) { - method673(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var4, var3, var6, var5, var7, var8); - } - } - - final void method635(int var1, int var2) { - var1 += this.anInt3701; - var2 += this.anInt3698; - int var3 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var4 = 0; - int var5 = this.height; - int var6 = this.width; - int var7 = Toolkit.JAVA_TOOLKIT.width - var6; - int var8 = 0; - int var9; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var9 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var5 -= var9; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var4 += var9 * var6; - var3 += var9 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var2 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var9 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var6 -= var9; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var4 += var9; - var3 += var9; - var8 += var9; - var7 += var9; - } - - if(var1 + var6 > Toolkit.JAVA_TOOLKIT.clipRight) { - var9 = var1 + var6 - Toolkit.JAVA_TOOLKIT.clipRight; - var6 -= var9; - var8 += var9; - var7 += var9; - } - - if(var6 > 0 && var5 > 0) { - method673(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var4, var3, var6, var5, var7, var8); - } - } - - private static void method671(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12) { - int var13 = var3; - - for(int var14 = -var8; var14 < 0; ++var14) { - int var15 = (var4 >> 16) * var11; - - for(int var16 = -var7; var16 < 0; ++var16) { - int var17 = var1[(var3 >> 16) + var15]; - int var18 = var0[var5]; - int var19 = (var17 >>> 24) * var12 >> 8; - int var20 = 256 - var19; - var0[var5++] = ((var17 & 16711935) * var19 + (var18 & 16711935) * var20 & -16711936) + ((var17 & 65280) * var19 + (var18 & 65280) * var20 & 16711680) >>> 8; - var3 += var9; - } - - var4 += var10; - var3 = var13; - var5 += var6; - } - - } - - final void method636(int var1, int var2, int var3, int var4, int var5, int var6) { - if(var6 != 0) { - var1 -= this.anInt3701 << 4; - var2 -= this.anInt3698 << 4; - double var7 = (double)(var5 & 65535) * 9.587379924285257E-5D; - int var9 = (int)Math.floor(Math.sin(var7) * (double)var6 + 0.5D); - int var10 = (int)Math.floor(Math.cos(var7) * (double)var6 + 0.5D); - int var11 = -var1 * var10 + -var2 * var9; - int var12 = -(-var1) * var9 + -var2 * var10; - int var13 = ((this.width << 4) - var1) * var10 + -var2 * var9; - int var14 = -((this.width << 4) - var1) * var9 + -var2 * var10; - int var15 = -var1 * var10 + ((this.height << 4) - var2) * var9; - int var16 = -(-var1) * var9 + ((this.height << 4) - var2) * var10; - int var17 = ((this.width << 4) - var1) * var10 + ((this.height << 4) - var2) * var9; - int var18 = -((this.width << 4) - var1) * var9 + ((this.height << 4) - var2) * var10; - int var19; - int var20; - if(var11 < var13) { - var19 = var11; - var20 = var13; - } else { - var19 = var13; - var20 = var11; - } - - if(var15 < var19) { - var19 = var15; - } - - if(var17 < var19) { - var19 = var17; - } - - if(var15 > var20) { - var20 = var15; - } - - if(var17 > var20) { - var20 = var17; - } - - int var21; - int var22; - if(var12 < var14) { - var21 = var12; - var22 = var14; - } else { - var21 = var14; - var22 = var12; - } - - if(var16 < var21) { - var21 = var16; - } - - if(var18 < var21) { - var21 = var18; - } - - if(var16 > var22) { - var22 = var16; - } - - if(var18 > var22) { - var22 = var18; - } - - var19 >>= 12; - var20 = var20 + 4095 >> 12; - var21 >>= 12; - var22 = var22 + 4095 >> 12; - var19 += var3; - var20 += var3; - var21 += var4; - var22 += var4; - var19 >>= 4; - var20 = var20 + 15 >> 4; - var21 >>= 4; - var22 = var22 + 15 >> 4; - if(var19 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var19 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - if(var20 > Toolkit.JAVA_TOOLKIT.clipRight) { - var20 = Toolkit.JAVA_TOOLKIT.clipRight; - } - - if(var21 < Toolkit.JAVA_TOOLKIT.clipTop) { - var21 = Toolkit.JAVA_TOOLKIT.clipTop; - } - - if(var22 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var22 = Toolkit.JAVA_TOOLKIT.clipBottom; - } - - var20 = var19 - var20; - if(var20 < 0) { - var22 = var21 - var22; - if(var22 < 0) { - int var23 = var21 * Toolkit.JAVA_TOOLKIT.width + var19; - double var24 = 1.6777216E7D / (double)var6; - int var26 = (int)Math.floor(Math.sin(var7) * var24 + 0.5D); - int var27 = (int)Math.floor(Math.cos(var7) * var24 + 0.5D); - int var28 = (var19 << 4) + 8 - var3; - int var29 = (var21 << 4) + 8 - var4; - int var30 = (var1 << 8) - (var29 * var26 >> 4); - int var31 = (var2 << 8) + (var29 * var27 >> 4); - int var34; - int var35; - int var32; - int var33; - int var38; - int var39; - int var36; - int var37; - int var40; - int var41; - if(var27 == 0) { - if(var26 == 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30; - var36 = var31; - var37 = var20; - if(var30 >= 0 && var31 >= 0 && var30 - (this.width << 12) < 0 && var31 - (this.height << 12) < 0) { - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - ++var37; - } - } - - ++var33; - } - } else if(var26 < 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30; - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if(var30 >= 0 && var30 - (this.width << 12) < 0) { - if((var32 = var36 - (this.height << 12)) >= 0) { - var32 = (var26 - var32) / var26; - var37 = var20 + var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (var36 - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - var36 += var26; - ++var37; - } - } - - ++var33; - var30 -= var26; - } - } else { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30; - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if(var30 >= 0 && var30 - (this.width << 12) < 0) { - if(var36 < 0) { - var32 = (var26 - 1 - var36) / var26; - var37 = var20 + var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (1 + var36 - (this.height << 12) - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - var36 += var26; - ++var37; - } - } - - ++var33; - var30 -= var26; - } - } - } else if(var27 < 0) { - if(var26 == 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31; - var37 = var20; - if(var31 >= 0 && var31 - (this.height << 12) < 0) { - if((var32 = var35 - (this.width << 12)) >= 0) { - var32 = (var27 - var32) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var34 = var23 + var32; - } - - if((var32 = (var35 - var27) / var27) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - var35 += var27; - ++var37; - } - } - - ++var33; - var31 += var27; - } - } else if(var26 < 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if((var32 = var35 - (this.width << 12)) >= 0) { - var32 = (var27 - var32) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (var35 - var27) / var27) > var37) { - var37 = var32; - } - - if((var32 = var36 - (this.height << 12)) >= 0) { - var32 = (var26 - var32) / var26; - var37 += var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 += var32; - } - - if((var32 = (var36 - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - var35 += var27; - var36 += var26; - ++var37; - } - - ++var33; - var30 -= var26; - var31 += var27; - } - } else { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if((var32 = var35 - (this.width << 12)) >= 0) { - var32 = (var27 - var32) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (var35 - var27) / var27) > var37) { - var37 = var32; - } - - if(var36 < 0) { - var32 = (var26 - 1 - var36) / var26; - var37 += var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 += var32; - } - - if((var32 = (1 + var36 - (this.height << 12) - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - var35 += var27; - var36 += var26; - ++var37; - } - - ++var33; - var30 -= var26; - var31 += var27; - } - } - } else if(var26 == 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31; - var37 = var20; - if(var31 >= 0 && var31 - (this.height << 12) < 0) { - if(var35 < 0) { - var32 = (var27 - 1 - var35) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var34 = var23 + var32; - } - - if((var32 = (1 + var35 - (this.width << 12) - var27) / var27) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - var35 += var27; - ++var37; - } - } - - ++var33; - var31 += var27; - } - } else if(var26 < 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if(var35 < 0) { - var32 = (var27 - 1 - var35) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (1 + var35 - (this.width << 12) - var27) / var27) > var37) { - var37 = var32; - } - - if((var32 = var36 - (this.height << 12)) >= 0) { - var32 = (var26 - var32) / var26; - var37 += var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 += var32; - } - - if((var32 = (var36 - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - var35 += var27; - var36 += var26; - ++var37; - } - - ++var33; - var30 -= var26; - var31 += var27; - } - } else { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if(var35 < 0) { - var32 = (var27 - 1 - var35) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (1 + var35 - (this.width << 12) - var27) / var27) > var37) { - var37 = var32; - } - - if(var36 < 0) { - var32 = (var26 - 1 - var36) / var26; - var37 += var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 += var32; - } - - if((var32 = (1 + var36 - (this.height << 12) - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - var39 = Toolkit.JAVA_TOOLKIT.getBuffer()[var34]; - var40 = var38 >>> 24; - var41 = 256 - var40; - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = ((var38 & 16711935) * var40 + (var39 & 16711935) * var41 & -16711936) + ((var38 & 65280) * var40 + (var39 & 65280) * var41 & 16711680) >>> 8; - var35 += var27; - var36 += var26; - ++var37; - } - - ++var33; - var30 -= var26; - var31 += var27; - } - } - - } - } - } - } - - public final void drawMinimapRegion(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int[] var9, int[] var10) { - try { - int var11 = -var3 / 2; - int var12 = -var4 / 2; - int var13 = (int)(Math.sin((double)var7 / 326.11D) * 65536.0D); - int var14 = (int)(Math.cos((double)var7 / 326.11D) * 65536.0D); - var13 = var13 * var8 >> 8; - var14 = var14 * var8 >> 8; - int var15 = (var5 << 16) + var12 * var13 + var11 * var14; - int var16 = (var6 << 16) + (var12 * var14 - var11 * var13); - int var17 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - - for(var2 = 0; var2 < var4; ++var2) { - int var18 = var9[var2]; - int var19 = var17 + var18; - int var20 = var15 + var14 * var18; - int var21 = var16 - var13 * var18; - - for(var1 = -var10[var2]; var1 < 0; ++var1) { - int var22 = this.anIntArray4081[(var20 >> 16) + (var21 >> 16) * this.width]; - int var23 = Toolkit.JAVA_TOOLKIT.getBuffer()[var19]; - int var24 = var22 >>> 24; - int var25 = 256 - var24; - Toolkit.JAVA_TOOLKIT.getBuffer()[var19++] = ((var22 & 16711935) * var24 + (var23 & 16711935) * var25 & -16711936) + ((var22 & 65280) * var24 + (var23 & 65280) * var25 & 16711680) >>> 8; - var20 += var14; - var21 -= var13; - } - - var15 += var13; - var16 += var14; - var17 += Toolkit.JAVA_TOOLKIT.width; - } - } catch (Exception var26) { - } - - } - - public final void method667(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int[] var9, int[] var10) { - try { - int var11 = -var3 / 2; - int var12 = -var4 / 2; - int var13 = (int)(Math.sin((double)var7 / 326.11D) * 65536.0D); - int var14 = (int)(Math.cos((double)var7 / 326.11D) * 65536.0D); - var13 = var13 * 256 >> 8; - var14 = var14 * 256 >> 8; - int var15 = (var5 << 16) + var12 * var13 + var11 * var14; - int var16 = (var6 << 16) + (var12 * var14 - var11 * var13); - int var17 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - - for(var2 = 0; var2 < var4; ++var2) { - int var18 = var9[var2]; - int var19 = var17 + var18; - int var20 = var15 + var14 * var18; - int var21 = var16 - var13 * var18; - - for(var1 = -var10[var2]; var1 < 0; ++var1) { - int var22 = this.anIntArray4081[(var20 >> 16) + (var21 >> 16) * this.width]; - int var23 = Toolkit.JAVA_TOOLKIT.getBuffer()[var19]; - int var24 = var22 >>> 24; - int var25 = 256 - var24; - Toolkit.JAVA_TOOLKIT.getBuffer()[var19++] = ((var22 & 16711935) * var24 + (var23 & 16711935) * var25 & -16711936) + ((var22 & 65280) * var24 + (var23 & 65280) * var25 & 16711680) >>> 8; - var20 += var14; - var21 -= var13; - } - - var15 += var13; - var16 += var14; - var17 += Toolkit.JAVA_TOOLKIT.width; - } - } catch (Exception var26) { - } - - } - - final void method642(int var1, int var2, int var3, int var4, int var5) { - if(var3 > 0 && var4 > 0) { - int var6 = this.width; - int var7 = this.height; - int var8 = 0; - int var9 = 0; - int var10 = this.anInt3697; - int var11 = this.anInt3706; - int var12 = (var10 << 16) / var3; - int var13 = (var11 << 16) / var4; - int var14; - if(this.anInt3701 > 0) { - var14 = ((this.anInt3701 << 16) + var12 - 1) / var12; - var1 += var14; - var8 += var14 * var12 - (this.anInt3701 << 16); - } - - if(this.anInt3698 > 0) { - var14 = ((this.anInt3698 << 16) + var13 - 1) / var13; - var2 += var14; - var9 += var14 * var13 - (this.anInt3698 << 16); - } - - if(var6 < var10) { - var3 = ((var6 << 16) - var8 + var12 - 1) / var12; - } - - if(var7 < var11) { - var4 = ((var7 << 16) - var9 + var13 - 1) / var13; - } - - var14 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var15 = Toolkit.JAVA_TOOLKIT.width - var3; - if(var2 + var4 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var4 -= var2 + var4 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var16; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var16 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var4 -= var16; - var14 += var16 * Toolkit.JAVA_TOOLKIT.width; - var9 += var13 * var16; - } - - if(var1 + var3 > Toolkit.JAVA_TOOLKIT.clipRight) { - var16 = var1 + var3 - Toolkit.JAVA_TOOLKIT.clipRight; - var3 -= var16; - var15 += var16; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var16 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var3 -= var16; - var14 += var16; - var8 += var12 * var16; - var15 += var16; - } - - method671(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var8, var9, var14, var15, var3, var4, var12, var13, var6, var5); - } - } - - Class3_Sub28_Sub16_Sub2_Sub1(int var1, int var2, int var3, int var4, int var5, int var6, int[] var7) { - super(var1, var2, var3, var4, var5, var6, var7); - } - - private static void method672(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8) { - int var9 = -var5; - - for(int var10 = -var6; var10 < 0; ++var10) { - for(int var11 = var9; var11 < 0; ++var11) { - int var2 = var1[var3--]; - int var12 = var2 >>> 24; - if(var12 == 0) { - ++var4; - } else { - int var13 = 256 - var12; - int var14 = var0[var4]; - var0[var4++] = ((var2 & 16711935) * var12 + (var14 & 16711935) * var13 & -16711936) + ((var2 & 65280) * var12 + (var14 & 65280) * var13 & 16711680) >>> 8; - } - } - - var4 += var7; - var3 += var8; - } - - } - - public final void method639(int var1, int var2, int var3, int var4) { - if(var3 > 0 && var4 > 0) { - int var5 = this.width; - int var6 = this.height; - int var7 = 0; - int var8 = 0; - int var9 = this.anInt3697; - int var10 = this.anInt3706; - int var11 = (var9 << 16) / var3; - int var12 = (var10 << 16) / var4; - int var13; - if(this.anInt3701 > 0) { - var13 = ((this.anInt3701 << 16) + var11 - 1) / var11; - var1 += var13; - var7 += var13 * var11 - (this.anInt3701 << 16); - } - - if(this.anInt3698 > 0) { - var13 = ((this.anInt3698 << 16) + var12 - 1) / var12; - var2 += var13; - var8 += var13 * var12 - (this.anInt3698 << 16); - } - - if(var5 < var9) { - var3 = ((var5 << 16) - var7 + var11 - 1) / var11; - } - - if(var6 < var10) { - var4 = ((var6 << 16) - var8 + var12 - 1) / var12; - } - - var13 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var14 = Toolkit.JAVA_TOOLKIT.width - var3; - if(var2 + var4 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var4 -= var2 + var4 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var15; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var15 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var4 -= var15; - var13 += var15 * Toolkit.JAVA_TOOLKIT.width; - var8 += var12 * var15; - } - - if(var1 + var3 > Toolkit.JAVA_TOOLKIT.clipRight) { - var15 = var1 + var3 - Toolkit.JAVA_TOOLKIT.clipRight; - var3 -= var15; - var14 += var15; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var15 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var3 -= var15; - var13 += var15; - var7 += var11 * var15; - var14 += var15; - } - - method675(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var7, var8, var13, var14, var3, var4, var11, var12, var5); - } - } - - final void method641(int var1, int var2) { - var1 += this.anInt3697 - this.width - this.anInt3701; - var2 += this.anInt3698; - int var3 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var4 = this.width - 1; - int var5 = this.height; - int var6 = this.width; - int var7 = Toolkit.JAVA_TOOLKIT.width - var6; - int var8 = var6 + var6; - int var9; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var9 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var5 -= var9; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var4 += var9 * var6; - var3 += var9 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var2 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var9 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var6 -= var9; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var4 -= var9; - var3 += var9; - var8 -= var9; - var7 += var9; - } - - if(var1 + var6 > Toolkit.JAVA_TOOLKIT.clipRight) { - var9 = var1 + var6 - Toolkit.JAVA_TOOLKIT.clipRight; - var6 -= var9; - var8 -= var9; - var7 += var9; - } - - if(var6 > 0 && var5 > 0) { - method672(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var4, var3, var6, var5, var7, var8); - } - } - - final void method637(int var1, int var2, int var3) { - var1 += this.anInt3701; - var2 += this.anInt3698; - int var4 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var5 = 0; - int var6 = this.height; - int var7 = this.width; - int var8 = Toolkit.JAVA_TOOLKIT.width - var7; - int var9 = 0; - int var10; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var10 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var6 -= var10; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var5 += var10 * var7; - var4 += var10 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var6 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var6 -= var2 + var6 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var10 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var7 -= var10; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var5 += var10; - var4 += var10; - var9 += var10; - var8 += var10; - } - - if(var1 + var7 > Toolkit.JAVA_TOOLKIT.clipRight) { - var10 = var1 + var7 - Toolkit.JAVA_TOOLKIT.clipRight; - var7 -= var10; - var9 += var10; - var8 += var10; - } - - if(var7 > 0 && var6 > 0) { - method674(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var5, var4, var7, var6, var8, var9, var3); - } - } - - private static void method673(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8) { - int var9 = -var5; - - for(int var10 = -var6; var10 < 0; ++var10) { - for(int var11 = var9; var11 < 0; ++var11) { - int var2 = var1[var3++]; - int var12 = var2 >>> 24; - if(var12 == 0) { - ++var4; - } else { - int var13 = 256 - var12; - int var14 = var0[var4]; - var0[var4++] = ((var2 & 16711935) * var12 + (var14 & 16711935) * var13 & -16711936) + ((var2 & 65280) * var12 + (var14 & 65280) * var13 & 16711680) >>> 8; - } - } - - var4 += var7; - var3 += var8; - } - - } - - private static void method674(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - for(int var10 = -var6; var10 < 0; ++var10) { - for(int var11 = -var5; var11 < 0; ++var11) { - int var12 = (var1[var3] >>> 24) * var9 >> 8; - int var13 = 256 - var12; - int var14 = var1[var3++]; - int var15 = var0[var4]; - var0[var4++] = ((var14 & 16711935) * var12 + (var15 & 16711935) * var13 & -16711936) + ((var14 & 65280) * var12 + (var15 & 65280) * var13 & 16711680) >>> 8; - } - - var4 += var7; - var3 += var8; - } - - } - - private static void method675(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11) { - int var12 = var3; - - for(int var13 = -var8; var13 < 0; ++var13) { - int var14 = (var4 >> 16) * var11; - - for(int var15 = -var7; var15 < 0; ++var15) { - int var2 = var1[(var3 >> 16) + var14]; - int var16 = var2 >>> 24; - if(var16 == 0) { - ++var5; - } else { - int var17 = 256 - var16; - int var18 = var0[var5]; - var0[var5++] = ((var2 & 16711935) * var16 + (var18 & 16711935) * var17 & -16711936) + ((var2 & 65280) * var16 + (var18 & 65280) * var17 & 16711680) >>> 8; - } - - var3 += var9; - } - - var4 += var10; - var3 = var12; - var5 += var6; - } - - } -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub17_Sub1.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub17_Sub1.java deleted file mode 100644 index 2ae7f75c0..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub17_Sub1.java +++ /dev/null @@ -1,221 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; - -public final class Class3_Sub28_Sub17_Sub1 extends Font { - - private byte[][] aByteArrayArray4082 = new byte[256][]; - - - Class3_Sub28_Sub17_Sub1(byte[] var1) { - super(var1); - } - - private static void method704(int[] var0, byte[] var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - int var9 = -(var5 >> 2); - var5 = -(var5 & 3); - - for(int var10 = -var6; var10 < 0; ++var10) { - int var11; - for(var11 = var9; var11 < 0; ++var11) { - if(var1[var3++] == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - if(var1[var3++] == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - if(var1[var3++] == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - if(var1[var3++] == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - } - - for(var11 = var5; var11 < 0; ++var11) { - if(var1[var3++] == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - } - - var4 += var7; - var3 += var8; - } - - } - - private static void method705(byte[] var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int[] var11, int[] var12) { - int var13 = var2 - Toolkit.JAVA_TOOLKIT.clipLeft; - int var14 = var3 - Toolkit.JAVA_TOOLKIT.clipTop; - - for(int var15 = var14; var15 < var14 + var5; ++var15) { - int var16 = var11[var15]; - int var17 = var12[var15]; - int var18 = var4; - int var19; - if(var13 > var16) { - var19 = var13 - var16; - if(var19 >= var17) { - var7 += var4 + var10; - var8 += var4 + var9; - continue; - } - - var17 -= var19; - } else { - var19 = var16 - var13; - if(var19 >= var4) { - var7 += var4 + var10; - var8 += var4 + var9; - continue; - } - - var7 += var19; - var18 = var4 - var19; - var8 += var19; - } - - var19 = 0; - if(var18 < var17) { - var17 = var18; - } else { - var19 = var18 - var17; - } - - for(int var20 = -var17; var20 < 0; ++var20) { - if(var1[var7++] == 0) { - ++var8; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var8++] = var6; - } - } - - var7 += var19 + var10; - var8 += var19 + var9; - } - - } - - private static void method706(int[] var0, byte[] var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - var2 = ((var2 & 16711935) * var9 & -16711936) + ((var2 & 65280) * var9 & 16711680) >> 8; - var9 = 256 - var9; - - for(int var10 = -var6; var10 < 0; ++var10) { - for(int var11 = -var5; var11 < 0; ++var11) { - if(var1[var3++] == 0) { - ++var4; - } else { - int var12 = var0[var4]; - var0[var4++] = (((var12 & 16711935) * var9 & -16711936) + ((var12 & 65280) * var9 & 16711680) >> 8) + var2; - } - } - - var4 += var7; - var3 += var8; - } - - } - - final void method679(int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - int var9 = var2 + var3 * Toolkit.JAVA_TOOLKIT.width; - int var10 = Toolkit.JAVA_TOOLKIT.width - var4; - int var11 = 0; - int var12 = 0; - int var13; - if(var3 < Toolkit.JAVA_TOOLKIT.clipTop) { - var13 = Toolkit.JAVA_TOOLKIT.clipTop - var3; - var5 -= var13; - var3 = Toolkit.JAVA_TOOLKIT.clipTop; - var12 += var13 * var4; - var9 += var13 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var3 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var3 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var2 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var13 = Toolkit.JAVA_TOOLKIT.clipLeft - var2; - var4 -= var13; - var2 = Toolkit.JAVA_TOOLKIT.clipLeft; - var12 += var13; - var9 += var13; - var11 += var13; - var10 += var13; - } - - if(var2 + var4 > Toolkit.JAVA_TOOLKIT.clipRight) { - var13 = var2 + var4 - Toolkit.JAVA_TOOLKIT.clipRight; - var4 -= var13; - var11 += var13; - var10 += var13; - } - - if(var4 > 0 && var5 > 0) { - method706(Toolkit.JAVA_TOOLKIT.getBuffer(), this.aByteArrayArray4082[var1], var6, var12, var9, var4, var5, var10, var11, var7); - } - } - - Class3_Sub28_Sub17_Sub1(byte[] var1, int[] var2, int[] var3, int[] var4, int[] var5, byte[][] var6) { - super(var1, var2, var3, var4, var5); - this.aByteArrayArray4082 = var6; - } - - final void method678(int var1, int var2, int var3, int var4, int var5, int var6) { - int var8 = var2 + var3 * Toolkit.JAVA_TOOLKIT.width; - int var9 = Toolkit.JAVA_TOOLKIT.width - var4; - int var10 = 0; - int var11 = 0; - int var12; - if(var3 < Toolkit.JAVA_TOOLKIT.clipTop) { - var12 = Toolkit.JAVA_TOOLKIT.clipTop - var3; - var5 -= var12; - var3 = Toolkit.JAVA_TOOLKIT.clipTop; - var11 += var12 * var4; - var8 += var12 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var3 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var3 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var2 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var12 = Toolkit.JAVA_TOOLKIT.clipLeft - var2; - var4 -= var12; - var2 = Toolkit.JAVA_TOOLKIT.clipLeft; - var11 += var12; - var8 += var12; - var10 += var12; - var9 += var12; - } - - if(var2 + var4 > Toolkit.JAVA_TOOLKIT.clipRight) { - var12 = var2 + var4 - Toolkit.JAVA_TOOLKIT.clipRight; - var4 -= var12; - var10 += var12; - var9 += var12; - } - - if(var4 > 0 && var5 > 0) { - if(Class74.anIntArray1097 == null) { - method704(Toolkit.JAVA_TOOLKIT.getBuffer(), this.aByteArrayArray4082[var1], var6, var11, var8, var4, var5, var9, var10); - } else { - method705(this.aByteArrayArray4082[var1], var2, var3, var4, var5, var6, var11, var8, var9, var10, Class74.anIntArray1097, Class74.anIntArray1098); - } - - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub17_Sub2.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub17_Sub2.java deleted file mode 100644 index 31f79b0e4..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub17_Sub2.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.runite.client; - - - - -import org.rs09.client.rendering.opengl.enums.GLBeginMode; - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - -public final class Class3_Sub28_Sub17_Sub2 extends Font { - - private int anInt4083 = 0; - private int anInt4084 = -1; - private int[] anIntArray4085; - private int anInt4086; - private int anInt4087; - - - protected final void finalize() throws Throwable { - if(this.anInt4084 != -1) { - Class31.method991(this.anInt4084, this.anInt4083, this.anInt4086); - this.anInt4084 = -1; - this.anInt4083 = 0; - } - - if(this.anIntArray4085 != null) { - for(int var1 = 0; var1 < this.anIntArray4085.length; ++var1) { - Class31.method986(this.anIntArray4085[var1], this.anInt4086); - } - - this.anIntArray4085 = null; - } - - super.finalize(); - } - - final void method678(int var1, int var2, int var3, int var4, int var5, int var6) { - GL var8; - if(Class22.aHDSprite_447 == null) { - HDToolKit.method1824(); - var8 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4084); - var8.glColor3ub((byte)(var6 >> 16), (byte)(var6 >> 8), (byte)var6); - var8.glTranslatef((float)var2, (float)(HDToolKit.viewHeight - var3), 0.0F); - var8.glCallList(this.anIntArray4085[var1]); - } else { - HDToolKit.method1824(); - var8 = HDToolKit.gl; - var8.glColor3ub((byte)(var6 >> 16), (byte)(var6 >> 8), (byte)var6); - var8.glTranslatef((float)var2, (float)(HDToolKit.viewHeight - var3), 0.0F); - float var9 = (float)(var1 % 16) / 16.0F; - float var10 = (float)(var1 / 16) / 16.0F; - float var11 = var9 + (float)this.anIntArray3709[var1] / (float)this.anInt4087; - float var12 = var10 + (float)this.anIntArray3721[var1] / (float)this.anInt4087; - HDToolKit.bindTexture2D(this.anInt4084); - HDSprite var13 = Class22.aHDSprite_447; - var8.glActiveTexture('\u84c1'); - var8.glEnable(3553); - var8.glBindTexture(3553, var13.anInt4077); - var8.glTexEnvi(8960, '\u8571', 7681); - var8.glTexEnvi(8960, '\u8580', '\u8578'); - float var14 = (float)(var2 - Class22.anInt449) / (float)var13.anInt4075; - float var15 = (float)(var3 - Class22.anInt448) / (float)var13.anInt4079; - float var16 = (float)(var2 + var4 - Class22.anInt449) / (float)var13.anInt4075; - float var17 = (float)(var3 + var5 - Class22.anInt448) / (float)var13.anInt4079; - var8.glBegin(GLBeginMode.TRIANGLE_FAN); - var8.glMultiTexCoord2f('\u84c1', var16, var15); - var8.glTexCoord2f(var11, var10); - var8.glVertex2f((float)this.anIntArray3709[var1], 0.0F); - var8.glMultiTexCoord2f('\u84c1', var14, var15); - var8.glTexCoord2f(var9, var10); - var8.glVertex2f(0.0F, 0.0F); - var8.glMultiTexCoord2f('\u84c1', var14, var17); - var8.glTexCoord2f(var9, var12); - var8.glVertex2f(0.0F, (float)(-this.anIntArray3721[var1])); - var8.glMultiTexCoord2f('\u84c1', var16, var17); - var8.glTexCoord2f(var11, var12); - var8.glVertex2f((float)this.anIntArray3709[var1], (float)(-this.anIntArray3721[var1])); - var8.glEnd(); - var8.glTexEnvi(8960, '\u8571', 8448); - var8.glTexEnvi(8960, '\u8580', 5890); - var8.glDisable(3553); - var8.glActiveTexture('\u84c0'); - } - var8.glLoadIdentity(); - - } - - final void method679(int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - HDToolKit.method1824(); - GL var9 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4084); - var9.glColor4ub((byte)(var6 >> 16), (byte)(var6 >> 8), (byte)var6, var7 > 255?-1:(byte)var7); - var9.glTranslatef((float)var2, (float)(HDToolKit.viewHeight - var3), 0.0F); - var9.glCallList(this.anIntArray4085[var1]); - var9.glLoadIdentity(); - } - - private void method707() { - if(this.anIntArray4085 == null) { - this.anIntArray4085 = new int[256]; - GL var1 = HDToolKit.gl; - - for(int var2 = 0; var2 < 256; ++var2) { - float var3 = (float)(var2 % 16) / 16.0F; - float var4 = (float)(var2 / 16) / 16.0F; - float var5 = var3 + (float)this.anIntArray3709[var2] / (float)this.anInt4087; - float var6 = var4 + (float)this.anIntArray3721[var2] / (float)this.anInt4087; - this.anIntArray4085[var2] = var1.glGenLists(1); - var1.glNewList(this.anIntArray4085[var2], 4864); - var1.glBegin(GLBeginMode.TRIANGLE_FAN); - var1.glTexCoord2f(var5, var4); - var1.glVertex2f((float)this.anIntArray3709[var2], 0.0F); - var1.glTexCoord2f(var3, var4); - var1.glVertex2f(0.0F, 0.0F); - var1.glTexCoord2f(var3, var6); - var1.glVertex2f(0.0F, (float)(-this.anIntArray3721[var2])); - var1.glTexCoord2f(var5, var6); - var1.glVertex2f((float)this.anIntArray3709[var2], (float)(-this.anIntArray3721[var2])); - var1.glEnd(); - var1.glEndList(); - } - - this.anInt4086 = Class31.anInt582; - } - } - - Class3_Sub28_Sub17_Sub2(byte[] var1, int[] var2, int[] var3, int[] var4, int[] var5, byte[][] var6) { - super(var1, var2, var3, var4, var5); - this.method708(var6); - this.method707(); - } - - private void method708(byte[][] var1) { - if(this.anInt4084 == -1) { - this.anInt4087 = 0; - - int var2; - for(var2 = 0; var2 < 256; ++var2) { - if(this.anIntArray3721[var2] > this.anInt4087) { - this.anInt4087 = this.anIntArray3721[var2]; - } - - if(this.anIntArray3709[var2] > this.anInt4087) { - this.anInt4087 = this.anIntArray3709[var2]; - } - } - - this.anInt4087 *= 16; - this.anInt4087 = Class95.method1585((byte)104, this.anInt4087); - var2 = this.anInt4087 / 16; - byte[] var3 = new byte[this.anInt4087 * this.anInt4087 * 2]; - - for(int var4 = 0; var4 < 256; ++var4) { - int var5 = var4 % 16 * var2; - int var6 = var4 / 16 * var2; - int var7 = (var6 * this.anInt4087 + var5) * 2; - int var8 = 0; - int var9 = this.anIntArray3721[var4]; - int var10 = this.anIntArray3709[var4]; - byte[] var11 = var1[var4]; - - for(int var12 = 0; var12 < var9; ++var12) { - for(int var13 = 0; var13 < var10; ++var13) { - if(var11[var8++] == 0) { - var7 += 2; - } else { - var3[var7++] = -1; - var3[var7++] = -1; - } - } - - var7 += (this.anInt4087 - var10) * 2; - } - } - - ByteBuffer var14 = ByteBuffer.wrap(var3); - GL var15 = HDToolKit.gl; - if(this.anInt4084 == -1) { - int[] var16 = new int[1]; - var15.glGenTextures(1, var16, 0); - this.anInt4084 = var16[0]; - this.anInt4086 = Class31.anInt582; - } - - HDToolKit.bindTexture2D(this.anInt4084); - var15.glTexImage2D(3553, 0, 6410, this.anInt4087, this.anInt4087, 0, 6410, 5121, var14); - Class31.memory2D += var14.limit() - this.anInt4083; - this.anInt4083 = var14.limit(); - var15.glTexParameteri(3553, 10241, 9728); - var15.glTexParameteri(3553, 10240, 9728); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub18.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub18.java deleted file mode 100644 index 65195f8bd..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub18.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.runite.client; - - - -import org.rs09.client.Node; - - -import javax.media.opengl.GL; -import java.lang.reflect.Method; -import java.nio.ByteBuffer; - -final class Class3_Sub28_Sub18 extends Node { - - private int anInt3759; - private final int anInt3760; - static int anInt3764; - static int anInt3765 = 100; - private int anInt3767 = 0; - static int[] anIntArray3768 = new int[100]; - static boolean aBoolean3769 = false; - - - protected final void finalize() throws Throwable { - try { - if(this.anInt3759 != -1) { - Class31.method985(this.anInt3759, this.anInt3767, this.anInt3760); - this.anInt3759 = -1; - this.anInt3767 = 0; - } - - super.finalize(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "sd.finalize()"); - } - } - - static void method709(int var0, int var1) { - Class3_Sub2 var2 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[0][var0][var1]; - - for(int var3 = 0; var3 < 3; ++var3) { - Class3_Sub2 var4 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var3][var0][var1] = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var3 + 1][var0][var1]; - if(var4 != null) { - --var4.anInt2244; - - for(int var5 = 0; var5 < var4.anInt2223; ++var5) { - Class25 var6 = var4.aClass25Array2221[var5]; - if((var6.aLong498 >> 29 & 3L) == 2L && var6.anInt483 == var0 && var6.anInt478 == var1) { - --var6.anInt493; - } - } - } - } - - if(Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[0][var0][var1] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[0][var0][var1] = new Class3_Sub2(0, var0, var1); - } - - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[0][var0][var1].aClass3_Sub2_2235 = var2; - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[3][var0][var1] = null; - } - -// static void method710() { -// try { -// Class44.aReferenceCache_725.clearSoftReferences(); -// } catch (RuntimeException var2) { -// throw ClientErrorException.clientError(var2, "sd.C(" + (byte) 126 + ')'); -// } -// } - - final void method712() { - try { - int var2 = Class27.method961(); - if((1 & var2) == 0) { - HDToolKit.bindTexture2D(this.anInt3759); - } - - if(0 == (var2 & 2)) { - HDToolKit.method1856(0); - } - - if((var2 & 4) == 0) { - HDToolKit.method1847(0); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sd.E(" + (byte) 6 + ')'); - } - } - - Class3_Sub28_Sub18(int var1) { - try { - GL var2 = HDToolKit.gl; - int[] var3 = new int[1]; - var2.glGenTextures(1, var3, 0); - this.anInt3759 = var3[0]; - this.anInt3760 = Class31.anInt582; - HDToolKit.bindTexture2D(this.anInt3759); - int var4 = Class51.anIntArray834[var1]; - byte[] var5 = new byte[]{(byte)(var4 >> 16), (byte)(var4 >> 8), (byte)var4, (byte)-1}; - ByteBuffer var6 = ByteBuffer.wrap(var5); - var2.glTexImage2D(3553, 0, 6408, 1, 1, 0, 6408, 5121, var6); - var2.glTexParameteri(3553, 10241, 9729); - var2.glTexParameteri(3553, 10240, 9729); - Class31.anInt580 += var6.limit() - this.anInt3767; - this.anInt3767 = var6.limit(); - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "sd.(" + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub19.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub19.java deleted file mode 100644 index a220abc7c..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub19.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; - -final class Class3_Sub28_Sub19 extends Node { - - Class140_Sub6 aClass140_Sub6_3778; - - - Class3_Sub28_Sub19(Class140_Sub6 var1) { - try { - this.aClass140_Sub6_3778 = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ud.(" + (var1 != null?"{...}":"null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub2.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub2.java deleted file mode 100644 index bf55cf697..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub2.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; - -final class Class3_Sub28_Sub2 extends Node { - - Class140_Sub2 aClass140_Sub2_3545; - - static void method535(byte var0, int var1) { - try { - Unsorted.aFloatArray1934[0] = (float) Unsorted.bitwiseAnd(255, var1 >> 16) / 255.0F; - Unsorted.aFloatArray1934[1] = (float) Unsorted.bitwiseAnd(var1 >> 8, 255) / 255.0F; - Unsorted.aFloatArray1934[2] = (float) Unsorted.bitwiseAnd(255, var1) / 255.0F; - Unsorted.method383(-32584, 3); - Unsorted.method383(-32584, 4); - if(var0 != 56) { - method535((byte)127, 99); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bk.A(" + var0 + ',' + var1 + ')'); - } - } - - static Class75_Sub3 method536(DataBuffer var1) { - try { - - return new Class75_Sub3(var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readMedium(), var1.readUnsignedByte()); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bk.C(" + (byte) 54 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - - Class3_Sub28_Sub2(Class140_Sub2 var1) { - try { - this.aClass140_Sub2_3545 = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bk.(" + (var1 != null?"{...}":"null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub20.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub20.java deleted file mode 100644 index 05de01b0b..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub20.java +++ /dev/null @@ -1,446 +0,0 @@ -package org.runite.client; - - - -import org.rs09.client.Node; - -import javax.media.opengl.GL; -import javax.media.opengl.glu.GLU; -import java.nio.ByteBuffer; - -final class Class3_Sub28_Sub20 extends Node { - - private final boolean aBoolean3781; - private float aFloat3782; - private final int anInt3783; - private final int anInt3784; - static CacheIndex aClass153_3786; - private final boolean aBoolean3787; - private final int anInt3788; - private final boolean aBoolean3789; - private final Texture aTexture_3790; - private int anInt3791; - private int[] anIntArray3793; - static Class118 aClass118_3794 = Class21.method913(); - private int anInt3795 = -1; - private int anInt3796 = 0; - boolean aBoolean3797 = false; - private final int anInt3799; - private final boolean aBoolean3800; - - - final int[] method718(Interface2 var1, float var3, CacheIndex var4, boolean var5) { - try { - if(null == this.anIntArray3793 || this.aFloat3782 != var3) { - if(!this.aTexture_3790.method1408(var1, var4)) { - return null; - } - - int var6 = !var5?128:64; - this.anIntArray3793 = this.aTexture_3790.method1404(var6, this.aBoolean3800, var6, var3, var4, var1, true); - this.aFloat3782 = var3; - if(this.aBoolean3789) { - int[] var7 = new int[var6]; - int[] var10 = new int[var6 * var6]; - int[] var8 = new int[var6]; - int[] var9 = new int[var6]; - int var14; - int var13 = var14 = var6; - int var19 = var6 * var6; - int var18 = -1 + var6; - int var17 = var6 - 1; - - int var11; - int var20; - for(var20 = 2; var20 >= 0; --var20) { - for(var11 = var17; var11 >= 0; --var11) { - --var14; - int var12 = this.anIntArray3793[var14]; - var7[var11] += Unsorted.bitwiseAnd(var12, 16726965) >> 16; - var8[var11] += Unsorted.bitwiseAnd(65394, var12) >> 8; - var9[var11] += Unsorted.bitwiseAnd(var12, 255); - } - - if(var14 == 0) { - var14 = var19; - } - } - - int var25 = var19; - - for(int var31 = var18; var31 >= 0; --var31) { - int var30 = 0; - int var29 = 0; - int var28 = 0; - int var26 = 1; - int var27 = 1; - - for(var11 = 2; var11 >= 0; --var11) { - --var27; - var30 += var8[var27]; - var29 += var9[var27]; - var28 += var7[var27]; - if(var27 == 0) { - var27 = var6; - } - } - - for(var11 = var17; var11 >= 0; --var11) { - --var26; - int var21 = var30 / 9; - int var22 = var29 / 9; - --var27; - var20 = var28 / 9; - --var25; - var10[var25] = TextureOperation3.bitwiseOr(var22, TextureOperation3.bitwiseOr(var20 << 16, var21 << 8)); - var28 += var7[var27] + -var7[var26]; - var29 += var9[var27] - var9[var26]; - var30 += -var8[var26] + var8[var27]; - if(var26 == 0) { - var26 = var6; - } - - if(var27 == 0) { - var27 = var6; - } - } - - for(var11 = var17; var11 >= 0; --var11) { - --var13; - int var23 = this.anIntArray3793[var13]; - --var14; - int var24 = this.anIntArray3793[var14]; - var7[var11] += (Unsorted.bitwiseAnd(var24, 16729186) >> 16) + -Unsorted.bitwiseAnd(var23 >> 16, 255); - var8[var11] += (Unsorted.bitwiseAnd(65419, var24) >> 8) + -Unsorted.bitwiseAnd(255, var23 >> 8); - var9[var11] += -Unsorted.bitwiseAnd(var23, 255) + Unsorted.bitwiseAnd(255, var24); - } - - if(0 == var13) { - var13 = var19; - } - - if(0 == var14) { - var14 = var19; - } - } - - this.anIntArray3793 = var10; - } - } - - return this.anIntArray3793; - } catch (RuntimeException var32) { - throw ClientErrorException.clientError(var32, "uh.D(" + (var1 != null?"{...}":"null") + ',' + 0 + ',' + var3 + ',' + (var4 != null?"{...}":"null") + ',' + var5 + ')'); - } - } - - final boolean method719(CacheIndex var1, Interface2 var2, boolean var4) { - try { - if(this.aTexture_3790.method1408(var2, var1)) { - GL var5 = HDToolKit.gl; - int var6 = !var4?128:64; - int var7 = Class27.method961(); - if((1 & var7) == 0) { - if(this.anInt3795 == -1) { - int[] var8 = new int[1]; - var5.glGenTextures(1, var8, 0); - this.anInt3791 = Class31.anInt582; - this.anInt3795 = var8[0]; - HDToolKit.bindTexture2D(this.anInt3795); - ByteBuffer var9 = ByteBuffer.wrap(this.aTexture_3790.method1407(var6, var6, this.aBoolean3800, var2, var1)); - if(2 == this.anInt3788) { - GLU var14 = new GLU(); - var14.gluBuild2DMipmaps(3553, 6408, var6, var6, 6408, 5121, var9); - var5.glTexParameteri(3553, 10241, 9987); - var5.glTexParameteri(3553, 10240, 9729); - Class31.anInt580 += 4 * var9.limit() / 3 - this.anInt3796; - this.anInt3796 = var9.limit() * 4 / 3; - } else if (this.anInt3788 == 1) { - int var10 = 0; - - while (true) { - var5.glTexImage2D(3553, var10++, 6408, var6, var6, 0, 6408, 5121, var9); - var6 >>= 1; - if (0 == var6) { - var5.glTexParameteri(3553, 10241, 9987); - var5.glTexParameteri(3553, 10240, 9729); - Class31.anInt580 += var9.limit() * 4 / 3 - this.anInt3796; - this.anInt3796 = 4 * var9.limit() / 3; - break; - } - - var9 = ByteBuffer.wrap(this.aTexture_3790.method1407(var6, var6, this.aBoolean3800, var2, var1)); - } - } else { - var5.glTexImage2D(3553, 0, 6408, var6, var6, 0, 6408, 5121, var9); - var5.glTexParameteri(3553, 10241, 9729); - var5.glTexParameteri(3553, 10240, 9729); - Class31.anInt580 += var9.limit() - this.anInt3796; - this.anInt3796 = var9.limit(); - } - - var5.glTexParameteri(3553, 10242, !this.aBoolean3787?'\u812f':10497); - var5.glTexParameteri(3553, 10243, this.aBoolean3781?10497:'\u812f'); - } else { - HDToolKit.bindTexture2D(this.anInt3795); - } - } - - if((2 & var7) == 0) { - HDToolKit.method1856(this.anInt3784); - } - - if((4 & var7) == 0) { - HDToolKit.method1847(0); - } - - if((var7 & 8) == 0) { - if(this.anInt3799 == 0 && 0 == this.anInt3783) { - HDToolKit.method1823(); - } else { - float var12 = (float)(this.anInt3799 * HDToolKit.anInt1791) / (float)var6; - float var13 = (float)(this.anInt3783 * HDToolKit.anInt1791) / (float)var6; - HDToolKit.method1843(var13, var12); - } - } - - return true; - } else { - return false; - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "uh.F(" + (var1 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + 579100487 + ',' + var4 + ')'); - } - } - - final int[] method720(boolean var2, Interface2 var3, CacheIndex var4) { - try { - - if(this.aTexture_3790.method1408(var3, var4)) { - int var5 = !var2?128:64; - return this.aTexture_3790.method1404(var5, this.aBoolean3800, var5, 1.0D, var4, var3, false); - } else { - return null; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "uh.O(" + false + ',' + var2 + ',' + (var3 != null?"{...}":"null") + ',' + (var4 != null?"{...}":"null") + ')'); - } - } - - final boolean method722(Interface2 var2, CacheIndex var3) { - try { - return this.aTexture_3790.method1408(var2, var3); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "uh.P(" + -5 + ',' + (var2 != null?"{...}":"null") + ',' + (var3 != null?"{...}":"null") + ')'); - } - } - - final void method723(int var1) { - try { - - if(this.anIntArray3793 != null) { - if(0 != this.anInt3799 || this.anInt3783 != 0) { - if(null == Class3_Sub23.anIntArray2533 || this.anIntArray3793.length > Class3_Sub23.anIntArray2533.length) { - Class3_Sub23.anIntArray2533 = new int[this.anIntArray3793.length]; - } - - int var5 = var1 * this.anInt3783; - int var3 = this.anIntArray3793.length == 4096 ?64:128; - int var4 = this.anIntArray3793.length; - int var6 = var3 + -1; - int var7 = this.anInt3799 * var1 * var3; - int var8 = -1 + var4; - - for(int var9 = 0; var4 > var9; var9 += var3) { - int var10 = var7 + var9 & var8; - - for(int var11 = 0; var3 > var11; ++var11) { - int var13 = (var6 & var11 - -var5) + var10; - int var12 = var11 + var9; - Class3_Sub23.anIntArray2533[var12] = this.anIntArray3793[var13]; - } - } - - int[] var15 = this.anIntArray3793; - this.anIntArray3793 = Class3_Sub23.anIntArray2533; - Class3_Sub23.anIntArray2533 = var15; - } - - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "uh.E(" + var1 + ',' + (byte) -120 + ')'); - } - } - - protected final void finalize() throws Throwable { - try { - if(this.anInt3795 != -1) { - Class31.method985(this.anInt3795, this.anInt3796, this.anInt3791); - this.anInt3796 = 0; - this.anInt3795 = -1; - } - - super.finalize(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "uh.finalize()"); - } - } - - static void method725() { - try { - if(Class137.method1817() == 2) { - byte var2 = (byte)(255 & CSConfigCachefile.anInt1127 + -4); - int var3 = CSConfigCachefile.anInt1127 % 104; - - int var4; - int var5; - for(var4 = 0; var4 < 4; ++var4) { - for(var5 = 0; 104 > var5; ++var5) { - Class158.aByteArrayArrayArray2008[var4][var3][var5] = var2; - } - } - - if(WorldListCountry.localPlane != 3) { - for(var4 = 0; var4 < 2; ++var4) { - Unsorted.anIntArray686[var4] = -1000000; - Class129_Sub1.anIntArray2696[var4] = 1000000; - Class159.anIntArray2021[var4] = 0; - SequenceDefinition.anIntArray1871[var4] = 1000000; - Player.anIntArray3959[var4] = 0; - } - - if(1 == Class133.anInt1753) { - if((4 & Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][Class102.player.xAxis >> 7][Class102.player.zAxis >> 7]) != 0) { - Class140_Sub7.method2031((byte)-85, false, Class102.player.xAxis >> 7, Class102.player.zAxis >> 7, Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, 0); - } - - if(Class139.anInt1823 < 310) { - int var7 = Class102.player.zAxis >> 7; - var5 = Class77.anInt1111 >> 7; - int var9; - if(var5 < var7) { - var9 = var7 - var5; - } else { - var9 = -var7 + var5; - } - - var4 = NPC.anInt3995 >> 7; - int var6 = Class102.player.xAxis >> 7; - int var8; - if(var4 < var6) { - var8 = -var4 + var6; - } else { - var8 = -var6 + var4; - } - - int var10; - int var11; - if(var8 > var9) { - var11 = 32768; - var10 = var9 * 65536 / var8; - - while(var6 != var4) { - if(var6 <= var4) { - --var4; - } else { - ++var4; - } - - if((Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var4][var5] & 4) != 0) { - Class140_Sub7.method2031((byte)-29, false, var4, var5, Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, 1); - break; - } - - var11 += var10; - if(var11 >= 65536) { - if(var5 >= var7) { - if(var7 < var5) { - --var5; - } - } else { - ++var5; - } - - var11 -= 65536; - if((4 & Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var4][var5]) != 0) { - Class140_Sub7.method2031((byte)-120, false, var4, var5, Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, 1); - break; - } - } - } - } else { - var11 = 32768; - var10 = 65536 * var8 / var9; - - while(var7 != var5) { - if(var5 < var7) { - ++var5; - } else { - --var5; - } - - if((4 & Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var4][var5]) != 0) { - Class140_Sub7.method2031((byte)-13, false, var4, var5, Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, 1); - break; - } - - var11 += var10; - if(var11 >= 65536) { - if(var6 > var4) { - ++var4; - } else if(var6 < var4) { - --var4; - } - - var11 -= 65536; - if((4 & Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][var4][var5]) != 0) { - Class140_Sub7.method2031((byte)-37, false, var4, var5, Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, 1); - break; - } - } - } - } - } - } else { - var4 = Class121.method1736(WorldListCountry.localPlane, 1, NPC.anInt3995, Class77.anInt1111); - if(800 > var4 + -Class7.anInt2162 && (4 & Unsorted.aByteArrayArrayArray113[WorldListCountry.localPlane][NPC.anInt3995 >> 7][Class77.anInt1111 >> 7]) != 0) { - Class140_Sub7.method2031((byte)-107, false, NPC.anInt3995 >> 7, Class77.anInt1111 >> 7, Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, 1); - } - } - - } - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "uh.A(" + -118 + ')'); - } - } - - Class3_Sub28_Sub20(DataBuffer var1) { - try { - this.aTexture_3790 = new Texture(var1); - this.aBoolean3789 = var1.readUnsignedByte() == 1; - this.aBoolean3800 = 1 == var1.readUnsignedByte(); - this.aBoolean3787 = var1.readUnsignedByte() == 1; - this.aBoolean3781 = var1.readUnsignedByte() == 1; - int var2 = 3 & var1.readUnsignedByte(); - this.anInt3783 = var1.readSignedByte(); - this.anInt3799 = var1.readSignedByte(); - int var3 = var1.readUnsignedByte(); - var1.readUnsignedByte(); - if(var2 == 1) { - this.anInt3784 = 2; - } else if(var2 == 2) { - this.anInt3784 = 3; - } else if(var2 == 3) { - this.anInt3784 = 4; - } else { - this.anInt3784 = 0; - } - - this.anInt3788 = (var3 & 240) >> 4; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "uh.(" + (var1 != null?"{...}":"null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub3.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub3.java deleted file mode 100644 index 1c5031f0c..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub3.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; - -final class Class3_Sub28_Sub3 extends Node { - - int anInt3549 = 0; - int anInt3550; - static RSInterface aClass11_3551; - boolean aBoolean3553; - RSString aClass94_3554; - int anInt3555 = 12800; - int anInt3556; - static int anInt3557 = 0; - int anInt3558; - int anInt3559 = 0; - LinkedList aLinkedList_3560; - RSString aClass94_3561; - int anInt3562 = 12800; - int anInt3563; - static int anInt3564 = 0; - - - final boolean method537(int var1, int var3) { - try { - if(var3 >= this.anInt3555 && var3 <= this.anInt3559 && var1 >= this.anInt3562 && var1 <= this.anInt3549) { - for(Class3_Sub21 var4 = (Class3_Sub21)this.aLinkedList_3560.method1222(); var4 != null; var4 = (Class3_Sub21)this.aLinkedList_3560.method1221()) { - if(var4.method393(var1, var3)) { - return true; - } - } - - } - return false; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bn.B(" + var1 + ',' + (byte) 97 + ',' + var3 + ')'); - } - } - - final void method538() { - try { - this.anInt3562 = 12800; - this.anInt3559 = 0; - - this.anInt3549 = 0; - this.anInt3555 = 12800; - - for(Class3_Sub21 var2 = (Class3_Sub21)this.aLinkedList_3560.method1222(); null != var2; var2 = (Class3_Sub21)this.aLinkedList_3560.method1221()) { - if(this.anInt3562 > var2.anInt2494) { - this.anInt3562 = var2.anInt2494; - } - - if(this.anInt3555 > var2.anInt2492) { - this.anInt3555 = var2.anInt2492; - } - - if(var2.anInt2495 > this.anInt3559) { - this.anInt3559 = var2.anInt2495; - } - - if(this.anInt3549 < var2.anInt2497) { - this.anInt3549 = var2.anInt2497; - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bn.F(" + (byte) 103 + ')'); - } - } - - static int method540(int var0, int var2) { - try { - int var3; - for(var3 = 0; var0 > 0; --var0) { - var3 = var3 << 1 | 1 & var2; - var2 >>>= 1; - } - - return var3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bn.P(" + var0 + ',' + -14314 + ',' + var2 + ')'); - } - } - - static void method541(boolean var1, RSString var2) { - try { - var2 = var2.toLowercase(); - int var4 = 0; - short[] var3 = new short[16]; - int var6 = !var1?0:32768; - int var7 = (!var1? QuickChat.anInt1156: QuickChat.anInt377) + var6; - - for(int var8 = var6; var8 < var7; ++var8) { - QuickChatDefinition var9 = QuickChat.method733(var8); - if(var9.aBoolean3568 && var9.method554().toLowercase().indexOf(var2, 112) != -1) { - if(var4 >= 50) { - Unsorted.anInt952 = -1; - Class99.aShortArray1398 = null; - return; - } - - if(var4 >= var3.length) { - short[] var10 = new short[2 * var3.length]; - - System.arraycopy(var3, 0, var10, 0, var4); - - var3 = var10; - } - - var3[var4++] = (short)var8; - } - } - - Class99.aShortArray1398 = var3; - Unsorted.anInt952 = var4; - Class140_Sub4.anInt2756 = 0; - RSString[] var13 = new RSString[Unsorted.anInt952]; - - for(int var14 = 0; Unsorted.anInt952 > var14; ++var14) { - var13[var14] = QuickChat.method733(var3[var14]).method554(); - } - - TextureOperation3.method307(var13, Class99.aShortArray1398, 100); - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "bn.C(" + (byte) 123 + ',' + var1 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - static int method543(int var0, int var1, byte var2) { - try { - if(var2 > -71) { - return -52; - } else { - int var3 = NPC.method1984(var0 + -1, 38, -1 + var1) - -NPC.method1984(1 + var0, 38, -1 + var1) - (-NPC.method1984(-1 + var0, 38, var1 - -1) + -NPC.method1984(var0 + 1, 38, var1 - -1)); - int var4 = NPC.method1984(var0 + -1, 38, var1) + NPC.method1984(var0 + 1, 38, var1) + (NPC.method1984(var0, 38, -1 + var1) - -NPC.method1984(var0, 38, 1 + var1)); - int var5 = NPC.method1984(var0, 38, var1); - return var4 / 8 + var3 / 16 - -(var5 / 4); - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "bn.A(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - static boolean method544(int var1) { - try { - return var1 >= 48 && 57 >= var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bn.E(" + -49 + ',' + var1 + ')'); - } - } - - Class3_Sub28_Sub3(RSString var1, RSString var2, int var3, int var4, int var5, boolean var6, int var7) { - try { - this.anInt3556 = var4; - this.anInt3550 = var5; - this.aBoolean3553 = var6; - this.aClass94_3561 = var1; - this.aClass94_3554 = var2; - this.anInt3563 = var7; - this.anInt3558 = var3; - if(this.anInt3563 == 255) { - this.anInt3563 = 0; - } - - this.aLinkedList_3560 = new LinkedList(); - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "bn.(" + (var1 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub5.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub5.java deleted file mode 100644 index c2e25912d..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub5.java +++ /dev/null @@ -1,295 +0,0 @@ -package org.runite.client; -import org.rs09.client.Node; -import java.util.Calendar; -import java.util.Objects; -import java.util.TimeZone; - -final class Class3_Sub28_Sub5 extends Node { - - static CacheIndex aClass153_3580; - static Calendar aCalendar3581 = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - static int[] anIntArray3587; - AnimationHeader[] animations; - static int anInt3589; - static int anInt3590 = -1; - static int anInt3591 = 0; - static int[] anIntArray3592 = new int[256]; - static volatile boolean forceReplaceCanvasEnable = false; - - - static void method556(int var0, int var1, int var2, byte var3, int var4, int var5) { - try { - if(var3 < -93) { - int var8 = var2 * var2; - int var6 = 0; - int var7 = var1; - int var11 = var8 << 1; - int var9 = var1 * var1; - int var12 = var1 << 1; - int var10 = var9 << 1; - int var13 = var8 * (-var12 + 1) + var10; - int var14 = -(var11 * (-1 + var12)) + var9; - int var16 = var9 << 2; - int var17 = var10 * ((var6 << 1) + 3); - int var15 = var8 << 2; - int var18 = ((var1 << 1) - 3) * var11; - int var19 = var16 * (1 + var6); - int var21; - int var22; - if(var5 >= Class159.anInt2020 && Class57.anInt902 >= var5) { - var21 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var2 + var4, Class101.anInt1425); - var22 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var2 + var4, Class101.anInt1425); - TextureOperation18.method282(Class38.anIntArrayArray663[var5], var22, 91, var21, var0); - } - - for(int var20 = var15 * (-1 + var1); 0 < var7; var20 -= var15) { - --var7; - if(var13 < 0) { - while(0 > var13) { - ++var6; - var13 += var17; - var14 += var19; - var19 += var16; - var17 += var16; - } - } - - var21 = var5 - var7; - if(var14 < 0) { - var14 += var19; - var13 += var17; - var17 += var16; - var19 += var16; - ++var6; - } - - var14 += -var18; - var18 -= var15; - var13 += -var20; - var22 = var7 + var5; - if(var22 >= Class159.anInt2020 && var21 <= Class57.anInt902) { - int var23 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 + var4, Class101.anInt1425); - int var24 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var6 + var4, Class101.anInt1425); - if(var21 >= Class159.anInt2020) { - TextureOperation18.method282(Class38.anIntArrayArray663[var21], var24, 121, var23, var0); - } - - if(var22 <= Class57.anInt902) { - TextureOperation18.method282(Class38.anIntArrayArray663[var22], var24, -110, var23, var0); - } - } - } - - } - } catch (RuntimeException var25) { - throw ClientErrorException.clientError(var25, "cl.C(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static long method557(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if (var3 != null) { - for (int var4 = 0; var4 < var3.anInt2223; ++var4) { - Class25 var5 = var3.aClass25Array2221[var4]; - if ((var5.aLong498 >> 29 & 3L) == 2L && var5.anInt483 == var1 && var5.anInt478 == var2) { - return var5.aLong498; - } - } - - } - return 0L; - } - - final boolean method559(int var2) { - try { - return this.animations[var2].aBoolean1386; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "cl.D(" + 1317095745 + ',' + var2 + ')'); - } - } - - static void method560() { - try { - for(int var1 = 0; var1 < 5; ++var1) { - WaterfallShader.aBooleanArray2169[var1] = false; - } - - Class75.anInt1105 = 0; - Class163_Sub2_Sub1.anInt4014 = 0; - NPCDefinition.anInt1252 = -1; - InterfaceWidget.anInt2293 = -1; - Class133.anInt1753 = 1; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "cl.A(" + -21556 + ')'); - } - } - - -// public static void decodeNew(int file) { -// try { -// ByteBuffer buffer = buffers[file]; -// -// if (buffer != null) { -// -// buffer.position(1); -// -// int skin_id = buffer.getShort() & 0xFFFF; -// -// if (skin_id == 0) { -// return; -// } -// -// Skins skin = new Skins(0, SignLink.cache.read(1, skin_id).getData()); -// -// ByteBuffer parent = ByteBuffer.wrap(buffer.array()); -// -// parent.position(3);//skip the first 3 bytes, like the 666 client does. -// -// int children = parent.get() & 0xFF; -// -// Animation[] headers = new Animation[children]; -// -// ByteBuffer child = ByteBuffer.wrap(parent.array()); -// child.position(parent.position() + children); -// -// -// int vertice_amt = 0; -// int unflagged_vertex = -1; -// int flagged_vertex = -1; -// -// for (int i = 0; i < children; i++) { -// -// Animation animation = headers[i] = new Animation(); -// -// int type = skin.anIntArray646[i]; -// -// if (type == 0) { -// unflagged_vertex = i; -// } -// -// int flags = child.get() & 0xFF; -// -// if (flags <= 0) { -// continue; -// } -// -// if (type == 0) { -// flagged_vertex = i; -// } -// -// vertex_offset_copy[vertice_amt] = i; -// -// int static_frame = type == 3 || type == 10 ? 128 : 0; -// -// x_buff_copy[vertice_amt] = (flags & 0x1) != 0 ? BufferUtils.getUnsignedSmart(child) : static_frame; -// y_buff_copy[vertice_amt] = (flags & 0x2) != 0 ? BufferUtils.getUnsignedSmart(child) : static_frame; -// z_buff_copy[vertice_amt] = (flags & 0x4) != 0 ? BufferUtils.getUnsignedSmart(child) : static_frame; -// -// xyz_flag_copy[vertice_amt] = (byte) (flags >>> 3 & 0x3); -// -// if (type == 2 || type == 9) { -// x_buff_copy[vertice_amt] = x_buff_copy[vertice_amt] << 2 & 0x3fff; -// y_buff_copy[vertice_amt] = y_buff_copy[vertice_amt] << 2 & 0x3fff; -// z_buff_copy[vertice_amt] = z_buff_copy[vertice_amt] << 2 & 0x3fff; -// } -// -// if (type == 1 || type == 2 || type == 3 && (unflagged_vertex > flagged_vertex)) { -// skin_vertices[vertice_amt] = unflagged_vertex; -// flagged_vertex = unflagged_vertex; -// } else { -// skin_vertices[vertice_amt] = -1; -// } -// -// if (type == 5) { -// animation.aBoolean688 = true; -// } else if (type == 7) { -// animation.aBoolean684 = true; -// } else if (type == 8 || type == 9 || type == 10) { -// animation.aBoolean681 = true; -// } -// -// vertice_amt++; -// } -// -// //resize arrays and copy into them. -// -// for (Animation animation : headers) { -// animation.vertices = vertice_amt; -// animation.xBuffer = new int[vertice_amt]; -// animation.yBuffer = new int[vertice_amt]; -// animation.zBuffer = new int[vertice_amt]; -// animation.skinVertices = new int[vertice_amt]; -// animation.xyzFlags = new int[vertice_amt]; -// animation.vertexOffsets = new int[vertice_amt]; -// -// for (int vextex = 0; vextex < vertice_amt; vextex++) { -// animation.xBuffer[vextex] = x_buff_copy[vextex]; -// animation.yBuffer[vextex] = y_buff_copy[vextex]; -// animation.zBuffer[vextex] = z_buff_copy[vextex]; -// animation.skinVertices[vextex] = skin_vertices[vextex]; -// animation.xyzFlags[vextex] = xyz_flag_copy[vextex]; -// animation.vertexOffsets[vextex] = vertex_offset_copy[vextex]; -// } -// } -// cache[file] = headers; -// } else { -// System.out.println("[Animation] Unrecognized skeleton file request."); -// } -// } catch (Exception exception) { -// exception.printStackTrace(); -// } -// } - - //Animation header - Class3_Sub28_Sub5(CacheIndex cacheIndex0, CacheIndex var2, int frame) { - try { - LinkedList var5 = new LinkedList(); - int size = cacheIndex0.getFileAmount(frame); - this.animations = new AnimationHeader[size]; - int[] fileIds = cacheIndex0.getFileIds(frame); - int var8 = 0; - - while(Objects.requireNonNull(fileIds).length > var8) { - byte[] var9 = cacheIndex0.getFile(frame, fileIds[var8]); - int skinId = 255 & var9[1] | (var9[0] & 0xFF) << 8; - Class3_Sub17 var12 = (Class3_Sub17) var5.method1222(); - Class3_Sub17 skin = null; - - while(true) { - if(var12 != null) { - if(var12.anInt2454 != skinId) { - var12 = (Class3_Sub17)var5.method1221(); - continue; - } - - skin = var12; - } - - if(null == skin) { - byte[] var13; - var13 = var2.method2140(0, skinId); - - skin = new Class3_Sub17(skinId, var13); - var5.method1215(skin); - } - - this.animations[fileIds[var8]] = new AnimationHeader(var9, skin); - ++var8; - break; - } - } - - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "cl.(" + (cacheIndex0 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + frame + ',' + false + ')'); - } - } - - final boolean method561(int var1, byte var2) { - try { - return var2 < 114 || this.animations[var1].aBoolean1382; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "cl.F(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub8.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub8.java deleted file mode 100644 index e8e10e794..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub8.java +++ /dev/null @@ -1,100 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; - -final class Class3_Sub28_Sub8 extends Node { - - static Class113[] aClass113Array3610; - static int anInt3611; - byte[] aByteArray3612; - - static int method571() { - try { - return (double) NPC.aFloat3979 == 3.0D ?37: (double) NPC.aFloat3979 == 4.0D ?50: 6.0D != (double) NPC.aFloat3979 ? (double) NPC.aFloat3979 == 8.0D ?100:200 :75; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "hc.E(" + -109 + ')'); - } - } - - static void method574(NPC var0) { - try { - for(Class3_Sub9 var2 = (Class3_Sub9) Unsorted.aLinkedList_1242.method1222(); var2 != null; var2 = (Class3_Sub9) Unsorted.aLinkedList_1242.method1221()) { - if(var0 == var2.aClass140_Sub4_Sub2_2324) { - if(var2.aClass3_Sub24_Sub1_2312 != null) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var2.aClass3_Sub24_Sub1_2312); - var2.aClass3_Sub24_Sub1_2312 = null; - } - - var2.unlink(); - return; - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hc.D(" + (var0 != null?"{...}":"null") + ',' + false + ')'); - } - } - - static void method575(CacheIndex var0, int var1) { - try { - if(var1 != -1) { - method575(null, -38); - } - - Class3_Sub23.aClass153_2536 = var0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hc.C(" + (var0 != null?"{...}":"null") + ',' + var1 + ')'); - } - } - - static int method576(RSString var0) { - try { - if(Class119.aClass131_1624 == null || var0.length() == 0) { - return -1; - } else { - for (int var2 = 0; var2 < Class119.aClass131_1624.anInt1720; ++var2) { - if (Class119.aClass131_1624.aClass94Array1721[var2].method1560(RSString.parse(" "), TextCore.aClass94_4066).equalsString(var0)) { - return var2; - } - } - - return -1; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hc.F(" + (var0 != null?"{...}":"null") + ',' + false + ')'); - } - } - - Class3_Sub28_Sub8(byte[] var1) { - try { - this.aByteArray3612 = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hc.(" + (var1 != null?"{...}":"null") + ')'); - } - } - - static void method577(int var0, int var1, int var2, int var3, GameObject var4, GameObject var5, int var6, int var7, int var8, int var9, long var10) { - if(var4 != null) { - Class19 var12 = new Class19(); - var12.aLong428 = var10; - var12.anInt424 = var1 * 128 + 64; - var12.anInt427 = var2 * 128 + 64; - var12.anInt425 = var3; - var12.aClass140_429 = var4; - var12.aClass140_423 = var5; - var12.anInt432 = var6; - var12.anInt420 = var7; - var12.anInt430 = var8; - var12.anInt426 = var9; - - for(int var13 = var0; var13 >= 0; --var13) { - if(Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var13][var1][var2] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var13][var1][var2] = new Class3_Sub2(var13, var1, var2); - } - } - - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2].aClass19_2233 = var12; - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub9.java b/Client/src/main/java/org/runite/client/Class3_Sub28_Sub9.java deleted file mode 100644 index 061db449f..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub28_Sub9.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.runite.client; -import org.rs09.client.Node; -import org.rs09.client.data.HashTable; - -import java.util.Calendar; - -final class Class3_Sub28_Sub9 extends Node { - - int anInt3614; - static Calendar aCalendar3616 = Calendar.getInstance(); - private int anInt3617; - RSString aClass94_3619; - static int anInt3620 = 0; - static RSString aClass94_3621 = null; - static int anInt3622 = 0; - static int anInt3623 = 0; - static int anInt3624; - - - static SoftwareSprite method578() { - try { - int var1 = Unsorted.anIntArray3076[0] * Class140_Sub7.anIntArray2931[0]; - byte[] var2 = Class163_Sub1.aByteArrayArray2987[0]; - - Object var3; - if(Class45.aBooleanArray3272[0]) { - byte[] var4 = Class163_Sub3.aByteArrayArray3005[0]; - int[] var5 = new int[var1]; - - for(int var6 = 0; var6 < var1; ++var6) { - var5[var6] = TextureOperation3.bitwiseOr(Unsorted.bitwiseAnd(var4[var6] << 24, -16777216), TextureOperation38.spritePalette[Unsorted.bitwiseAnd(255, var2[var6])]); - } - - var3 = new Class3_Sub28_Sub16_Sub2_Sub1(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[0], Unsorted.anIntArray2591[0], Class140_Sub7.anIntArray2931[0], Unsorted.anIntArray3076[0], var5); - } else { - int[] var8 = new int[var1]; - - for(int var9 = 0; var9 < var1; ++var9) { - var8[var9] = TextureOperation38.spritePalette[Unsorted.bitwiseAnd(var2[var9], 255)]; - } - - var3 = new SoftwareSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[0], Unsorted.anIntArray2591[0], Class140_Sub7.anIntArray2931[0], Unsorted.anIntArray3076[0], var8); - } - - Class39.method1035((byte)127); - return (SoftwareSprite)var3; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "hn.P(" + 115 + ')'); - } - } - - private void method579(int var1, DataBuffer var2) { - try { - if(var1 == 1) { - this.anInt3617 = var2.readUnsignedByte(); - } else if (var1 == 2) { - this.anInt3614 = var2.readInt(); - } else if (var1 == 5) { - this.aClass94_3619 = var2.readString(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "hn.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + 0 + ')'); - } - } - - static void method580(byte var0) { - try { - if(var0 != 80) { - method582(88, 85, -8, true, 72, 12, 29, 96, 6, 57, -13, 15); - } - - Class3_Sub2.aHashTable_2220 = new HashTable(32); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "hn.B(" + var0 + ')'); - } - } - - static void method581(CacheIndex var0, CacheIndex var2) { - try { - FontType.smallFont = Unsorted.method1300(Sprites.p11FullSpriteArchive, var2, var0); - if(HDToolKit.highDetail) { - Class157.aClass3_Sub28_Sub17_Sub1_2000 = Class70.method1287(Sprites.p11FullSpriteArchive, var0, var2); - } else { - Class157.aClass3_Sub28_Sub17_Sub1_2000 = (Class3_Sub28_Sub17_Sub1) FontType.smallFont; - } - - FontType.plainFont = Unsorted.method1300(Sprites.p12FullSpriteArchive, var2, var0); - FontType.bold = Unsorted.method1300(Sprites.b12FullSpriteArchive, var2, var0); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "hn.D(" + (var0 != null?"{...}":"null") + ',' + 0 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - static boolean method582(int var0, int var1, int var2, boolean var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11) { - try { - return Class102.player.getSize() != var5?(Class102.player.getSize() <= 2?Class158_Sub1.method2191(var6, var4, var11, var10, var9, var2, var1, var3, var8, var0, var7):Class52.method1166(var10, var7, var9, var1, Class102.player.getSize(), var6, var8, var4, var11, var2, var3, var0)): Unsorted.method76(var7, var8, var4, var0, var10, var3, var2, var1, var6, var9, var11); - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "hn.O(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var10 + ',' + var11 + ')'); - } - } - - final void method583(DataBuffer var2) { - try { - while(true) { - int var3 = var2.readUnsignedByte(); - if(var3 == 0) { - return; - } - - this.method579(var3, var2); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "hn.C(" + 207 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - final boolean method585() { - try { - - return this.anInt3617 == 115; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hn.E(" + 0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub3.java b/Client/src/main/java/org/runite/client/Class3_Sub3.java deleted file mode 100644 index efc87112b..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub3.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -abstract class Class3_Sub3 extends Linkable { - - int anInt2247; - - - abstract int method105(Class3_Sub24_Sub2 var1); - - abstract void method106(); -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub31.java b/Client/src/main/java/org/runite/client/Class3_Sub31.java deleted file mode 100644 index f93f52e07..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub31.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.data.ReferenceCache; - -final class Class3_Sub31 extends Linkable { - - int anInt2602; - int anInt2603; - static ReferenceCache aReferenceCache_2604 = new ReferenceCache(64); - static int[] anIntArray2606; - static int paramCountryID; - - static RSString[] optionsArrayStringConstructor(RSString[] var1) { - try { - RSString[] var2 = new RSString[5]; - - for(int var3 = 0; var3 < 5; ++var3) { - var2[var3] = RSString.stringCombiner(new RSString[]{ - RSString.stringAnimator(var3), TextCore.aClass94_3577}); - if(var1 != null && null != var1[var3]) { - var2[var3] = RSString.stringCombiner(new RSString[]{var2[var3], var1[var3]}); - } - } - - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "wk.A(" + 19406 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub4.java b/Client/src/main/java/org/runite/client/Class3_Sub4.java deleted file mode 100644 index 82594a890..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub4.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -final class Class3_Sub4 extends Linkable { - - static int anInt2249; - static int anInt2251; - int anInt2248; - int anInt2250; - int anInt2253; - int anInt2254; - int anInt2256; - int anInt2257; - int anInt2259 = -1; - int anInt2261 = 0; - int anInt2262; - int anInt2263; - int anInt2264; - int anInt2265; - - - static void initializeScene(int var3, boolean var4) { - Unsorted.anInt1234 = 104; - TextureOperation17.anInt3179 = 104; - TextureOperation8.anInt3466 = var3; - Unsorted.aClass3_Sub2ArrayArrayArray4070 = new Class3_Sub2[4][Unsorted.anInt1234][TextureOperation17.anInt3179]; - Class58.anIntArrayArrayArray914 = new int[4][Unsorted.anInt1234 + 1][TextureOperation17.anInt3179 + 1]; - if (HDToolKit.highDetail) { - Client.aClass3_Sub11ArrayArray2199 = new Class3_Sub11[4][]; - } - - if (var4) { - Class166.aClass3_Sub2ArrayArrayArray2065 = new Class3_Sub2[1][Unsorted.anInt1234][TextureOperation17.anInt3179]; - TextureOperation16.anIntArrayArray3115 = new int[Unsorted.anInt1234][TextureOperation17.anInt3179]; - Unsorted.anIntArrayArrayArray3605 = new int[1][Unsorted.anInt1234 + 1][TextureOperation17.anInt3179 + 1]; - if (HDToolKit.highDetail) { - TextureOperation32.aClass3_Sub11ArrayArray3346 = new Class3_Sub11[1][]; - } - } else { - Class166.aClass3_Sub2ArrayArrayArray2065 = null; - TextureOperation16.anIntArrayArray3115 = null; - Unsorted.anIntArrayArrayArray3605 = null; - TextureOperation32.aClass3_Sub11ArrayArray3346 = null; - } - - Class167.method2264(false); - Class3_Sub28_Sub8.aClass113Array3610 = new Class113[500]; - anInt2249 = 0; - Class145.aClass113Array1895 = new Class113[500]; - Class72.anInt1672 = 0; - Class81.anIntArrayArrayArray1142 = new int[4][Unsorted.anInt1234 + 1][TextureOperation17.anInt3179 + 1]; - SequenceDefinition.aClass25Array1868 = new Class25[5000]; - Unsorted.anInt3070 = 0; - Unsorted.aClass25Array4060 = new Class25[100]; - Class23.aBooleanArrayArray457 = new boolean[TextureOperation8.anInt3466 + TextureOperation8.anInt3466 + 1][TextureOperation8.anInt3466 + TextureOperation8.anInt3466 + 1]; - Class49.aBooleanArrayArray814 = new boolean[TextureOperation8.anInt3466 + TextureOperation8.anInt3466 + 2][TextureOperation8.anInt3466 + TextureOperation8.anInt3466 + 2]; - Unsorted.aByteArrayArrayArray1774 = new byte[4][Unsorted.anInt1234][TextureOperation17.anInt3179]; - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub5.java b/Client/src/main/java/org/runite/client/Class3_Sub5.java deleted file mode 100644 index 137dbc9b7..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub5.java +++ /dev/null @@ -1,394 +0,0 @@ -package org.runite.client; - -import org.rs09.SystemLogger; -import org.rs09.client.Linkable; - -final class Class3_Sub5 extends Linkable { - - int anInt2266; - - int anInt2268; - int anInt2270; - int anInt2271; - int anInt2272; - int anInt2273; - int anInt2277; - int anInt2278; - int anInt2279; - int anInt2282; - int anInt2283; - int anInt2284; - - static void method111(byte var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - if (var0 < -47) { - for (int var9 = 0; var9 < Class119.aClass131_1624.anInt1720; ++var9) { - if (Class119.aClass131_1624.method1787(var9)) { - int var10 = -TextureOperation37.anInt3256 + Class119.aClass131_1624.aShortArray1727[var9]; - int var11 = Unsorted.anInt65 - (Class119.aClass131_1624.aShortArray1718[var9] - -1 - Class108.anInt1460); - int var12 = var1 + (-var1 + var4) * (var10 - var3) / (-var3 + var7); - int var14 = Class119.aClass131_1624.method1791(var9, 8); - int var13 = (var8 + -var2) * (var11 - var6) / (var5 + -var6) + var2; - int var15 = 16777215; - Class33 var16 = null; - if (var14 == 0) { - if ((double) Class44.aFloat727 == 3.0D) { - var16 = Class164_Sub2.aClass33_3019; - } - - if (4.0D == (double) Class44.aFloat727) { - var16 = Unsorted.aClass33_1238; - } - - if ((double) Class44.aFloat727 == 6.0D) { - var16 = Class99.aClass33_1399; - } - - if ((double) Class44.aFloat727 >= 8.0D) { - var16 = Class75_Sub2.aClass33_2637; - } - } - - if (var14 == 1) { - if (3.0D == (double) Class44.aFloat727) { - var16 = Class99.aClass33_1399; - } - - if ((double) Class44.aFloat727 == 4.0D) { - var16 = Class75_Sub2.aClass33_2637; - } - - if ((double) Class44.aFloat727 == 6.0D) { - var16 = Class119.aClass33_1626; - } - - if (8.0D <= (double) Class44.aFloat727) { - var16 = Class75_Sub2.aClass33_2648; - } - } - - if (var14 == 2) { - if ((double) Class44.aFloat727 == 3.0D) { - var16 = Class119.aClass33_1626; - } - - var15 = 16755200; - if ((double) Class44.aFloat727 == 4.0D) { - var16 = Class75_Sub2.aClass33_2648; - } - - if (6.0D == (double) Class44.aFloat727) { - var16 = Class161.aClass33_2034; - } - - if ((double) Class44.aFloat727 >= 8.0D) { - var16 = Class91.aClass33_1305; - } - } - - if (Class119.aClass131_1624.anIntArray1725[var9] != -1) { - var15 = Class119.aClass131_1624.anIntArray1725[var9]; - } - - if (null != var16) { - int var17 = FontType.smallFont.method691(Class119.aClass131_1624.aClass94Array1721[var9], null, Class158_Sub1.aClass94Array2977); - var13 -= var16.method998() * (var17 + -1) / 2; - var13 += var16.method1006() / 2; - - for (int var18 = 0; var18 < var17; ++var18) { - RSString var19 = Class158_Sub1.aClass94Array2977[var18]; - if (-1 + var17 > var18) { - var19.method1553(-4 + var19.length()); - } - - var16.method1003(var19, var12, var13, var15); - var13 += var16.method998(); - } - } - } - } - - } - } catch (RuntimeException var20) { - throw ClientErrorException.clientError(var20, "cn.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ')'); - } - } - - static void method112(byte var0) { - try { - if (null == Class158.aByteArrayArrayArray2008) { - Class158.aByteArrayArrayArray2008 = new byte[4][104][104]; - } - - for (int var2 = 0; var2 < 4; ++var2) { - for (int var3 = 0; var3 < 104; ++var3) { - for (int var4 = 0; 104 > var4; ++var4) { - Class158.aByteArrayArrayArray2008[var2][var3][var4] = var0; - } - } - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "cn.A(" + var0 + ',' + (byte) 55 + ')'); - } - } - - static void method114(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - try { - - if (var8 == var9 && var3 == var7 && var5 == var4 && var1 == var6) { - GameObject.method1869((byte) 84, var2, var6, var7, var5, var9); - } else { - int var11 = var7; - int var10 = var9; - int var12 = var9 * 3; - int var13 = 3 * var7; - int var14 = var8 * 3; - int var15 = var3 * 3; - int var16 = var4 * 3; - int var17 = var1 * 3; - int var18 = var5 + -var16 + var14 + -var9; - int var19 = -var7 + var6 - (var17 + -var15); - int var20 = var12 + -var14 + -var14 + var16; - int var21 = var13 + -var15 + var17 + -var15; - int var22 = -var12 + var14; - int var23 = var15 + -var13; - - for (int var24 = 128; var24 <= 4096; var24 += 128) { - int var25 = var24 * var24 >> 12; - int var26 = var24 * var25 >> 12; - int var28 = var19 * var26; - int var29 = var25 * var20; - int var27 = var18 * var26; - int var30 = var25 * var21; - int var31 = var22 * var24; - int var32 = var23 * var24; - int var33 = (var31 + (var27 - -var29) >> 12) + var9; - int var34 = var7 + (var32 + var28 + var30 >> 12); - GameObject.method1869((byte) -119, var2, var34, var11, var33, var10); - var10 = var33; - var11 = var34; - } - } - - } catch (RuntimeException var35) { - throw ClientErrorException.clientError(var35, "cn.O(" + 3 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ')'); - } - } - - static int method115(boolean var0) { - try { - long var2 = TimeUtils.time(); - - for (Class3_Sub7 var4 = !var0 ? (Class3_Sub7) AtmosphereParser.aHashTable_3679.next() : (Class3_Sub7) AtmosphereParser.aHashTable_3679.first(); var4 != null; var4 = (Class3_Sub7) AtmosphereParser.aHashTable_3679.next()) { - if ((4611686018427387903L & var4.aLong2295) < var2) { - if ((4611686018427387904L & var4.aLong2295) != 0) { - int var5 = (int) var4.linkableKey; - ItemDefinition.ram[var5] = Class57.varpArray[var5]; - SystemLogger.logVerbose("Reading value " + Class57.varpArray[var5] + " from varp " + var5); - var4.unlink(); - return var5; - } - - var4.unlink(); - } - } - - return -1; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "cn.F(" + var0 + ',' + -1 + ')'); - } - } - - static void method116(boolean var0) { - try { - int var2 = Class159.localPlayerCount; - if (Class65.anInt987 == Class102.player.xAxis >> 7 && Class45.anInt733 == Class102.player.zAxis >> 7) { - Class65.anInt987 = 0; - } - - if (var0) { - var2 = 1; - } - - int var3; - Player var4; - int var7; - int var8; - int var9; - int var10; - int var11; - for (var3 = 0; var3 < var2; ++var3) { - if (var0) { - var4 = Class102.player; - } else { - var4 = Unsorted.players[Class56.localPlayerIndexes[var3]]; - } - - if (null != var4 && var4.hasDefinitions()) { - int var5 = var4.getSize(); - int var6; - if (var5 == 1) { - if ((127 & var4.xAxis) == 64 && 64 == (127 & var4.zAxis)) { - var6 = var4.xAxis >> 7; - var7 = var4.zAxis >> 7; - if (var6 >= 0 && var6 < 104 && var7 >= 0 && var7 < 104) { - ++Class163_Sub1_Sub1.anIntArrayArray4010[var6][var7]; - } - } - } else if (((1 & var5) != 0 || (var4.xAxis & 127) == 0 && (127 & var4.zAxis) == 0) && ((1 & var5) != 1 || (var4.xAxis & 127) == 64 && (127 & var4.zAxis) == 64)) { - var6 = var4.xAxis + -(var5 * 64) >> 7; - var7 = var4.zAxis + -(var5 * 64) >> 7; - var8 = var4.getSize() + var6; - if (104 < var8) { - var8 = 104; - } - - if (var6 < 0) { - var6 = 0; - } - - var9 = var7 + var4.getSize(); - if (var7 < 0) { - var7 = 0; - } - - if (var9 > 104) { - var9 = 104; - } - - for (var10 = var6; var8 > var10; ++var10) { - for (var11 = var7; var11 < var9; ++var11) { - ++Class163_Sub1_Sub1.anIntArrayArray4010[var10][var11]; - } - } - } - } - } - - label226: - for (var3 = 0; var3 < var2; ++var3) { - long var16; - if (var0) { - var4 = Class102.player; - var16 = 8791798054912L; - } else { - var4 = Unsorted.players[Class56.localPlayerIndexes[var3]]; - var16 = (long) Class56.localPlayerIndexes[var3] << 32; - } - - if (var4 != null && var4.hasDefinitions()) { - var4.aBoolean3968 = (RSInterface.aBoolean236 && Class159.localPlayerCount > 200 || 50 < Class159.localPlayerCount) && !var0 && var4.anInt2764 == var4.getRenderAnimationType().stand_animation; - - var7 = var4.getSize(); - if (var7 == 1) { - if (64 == (127 & var4.xAxis) && (127 & var4.zAxis) == 64) { - var8 = var4.xAxis >> 7; - var9 = var4.zAxis >> 7; - if (var8 < 0 || var8 >= 104 || var9 < 0 || 104 <= var9) { - continue; - } - - if (Class163_Sub1_Sub1.anIntArrayArray4010[var8][var9] > 1) { - --Class163_Sub1_Sub1.anIntArrayArray4010[var8][var9]; - continue; - } - } - } else if ((1 & var7) == 0 && (127 & var4.xAxis) == 0 && (var4.zAxis & 127) == 0 || 1 == (1 & var7) && (127 & var4.xAxis) == 64 && (var4.zAxis & 127) == 0) { - var8 = var4.xAxis + -(64 * var7) >> 7; - var10 = var7 + var8; - var9 = -(var7 * 64) + var4.zAxis >> 7; - if (var10 > 104) { - var10 = 104; - } - - if (0 > var8) { - var8 = 0; - } - - var11 = var7 + var9; - if (var9 < 0) { - var9 = 0; - } - - boolean var12 = true; - if (var11 > 104) { - var11 = 104; - } - - int var13; - int var14; - for (var13 = var8; var10 > var13; ++var13) { - for (var14 = var9; var14 < var11; ++var14) { - if (Class163_Sub1_Sub1.anIntArrayArray4010[var13][var14] <= 1) { - var12 = false; - break; - } - } - } - - if (var12) { - var13 = var8; - - while (true) { - if (var10 <= var13) { - continue label226; - } - - for (var14 = var9; var14 < var11; ++var14) { - --Class163_Sub1_Sub1.anIntArrayArray4010[var13][var14]; - } - - ++var13; - } - } - } - - if (null != var4.anObject2796 && var4.anInt2797 <= Class44.anInt719 && Class44.anInt719 < var4.anInt2778) { - var4.aBoolean3968 = false; - var4.anInt2831 = Class121.method1736(WorldListCountry.localPlane, 1, var4.xAxis, var4.zAxis); - TextureOperation28.method292(WorldListCountry.localPlane, var4.xAxis, var4.zAxis, var4.anInt2831, var4, var4.anInt2785, var16, var4.anInt2788, var4.anInt2777, var4.anInt2818, var4.anInt2817); - } else { - var4.anInt2831 = Class121.method1736(WorldListCountry.localPlane, 1, var4.xAxis, var4.zAxis); - Class20.method907(WorldListCountry.localPlane, var4.xAxis, var4.zAxis, var4.anInt2831, 64 * (var7 - 1) + 60, var4, var4.anInt2785, var16, var4.aBoolean2810); - } - } - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "cn.C(" + var0 + ',' + 670232012 + ')'); - } - } - - static void method117() { - try { - if (0 > Class3_Sub28_Sub1.anInt3536) { - TextureOperation13.anInt3362 = -1; - Class3_Sub28_Sub1.anInt3536 = 0; - Texture.anInt1150 = -1; - } - - if (Class23.anInt455 < Class3_Sub28_Sub1.anInt3536) { - TextureOperation13.anInt3362 = -1; - Class3_Sub28_Sub1.anInt3536 = Class23.anInt455; - Texture.anInt1150 = -1; - } - - if (Class3_Sub4.anInt2251 < 0) { - Texture.anInt1150 = -1; - TextureOperation13.anInt3362 = -1; - Class3_Sub4.anInt2251 = 0; - } - - if (Class108.anInt1460 < Class3_Sub4.anInt2251) { - Class3_Sub4.anInt2251 = Class108.anInt1460; - TextureOperation13.anInt3362 = -1; - Texture.anInt1150 = -1; - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "cn.E(" + (byte) 87 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub6.java b/Client/src/main/java/org/runite/client/Class3_Sub6.java deleted file mode 100644 index da647c14f..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub6.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -final class Class3_Sub6 extends Linkable { - - - static byte[][] softReferenceTestArray; - static int[] expectedMinimumValues = new int[32]; - static int anInt2291; - - static { - int accumulator = 2; - - for (int i = 0; i < 32; ++i) { - expectedMinimumValues[i] = -1 + accumulator; - accumulator += accumulator; - } - - anInt2291 = 1; - } - - byte[] aByteArray2289; - - Class3_Sub6(byte[] var1) { - try { - this.aByteArray2289 = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ea.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub7.java b/Client/src/main/java/org/runite/client/Class3_Sub7.java deleted file mode 100644 index e13be9aef..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub7.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -final class Class3_Sub7 extends Linkable { - - static int[] anIntArray2292 = new int[1000]; - static int anInt2294; - long aLong2295; - - - static int method121(int var0, int var1, int var2, int var3, int var4, int var6) { - try { - if ((var2 & 1) == 1) { - int var7 = var4; - var4 = var3; - var3 = var7; - } - - var1 &= 3; - return 0 != var1 ? (var1 != 1 ? (var1 != 2 ? -var0 + 7 + 1 + -var3 : -var6 + (7 - (var4 + -1))) : var0) : var6; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "eb.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + 1 + ',' + var6 + ')'); - } - } - - static void method122(int var0) { - try { - GameObject.aClass11ArrayArray1834 = new RSInterface[Unsorted.aClass153_3361.method2121()][]; - Unsorted.aBooleanArray1703 = new boolean[Unsorted.aClass153_3361.method2121()]; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "eb.D(" + var0 + ')'); - } - } - - static RSString itemStackColor(int var0, int var1) { - try { - if (100000 > var1) { - return RSString.stringCombiner(new RSString[]{ColorCore.DefaultStackColor, RSString.stringAnimator(var1), TextCore.aClass94_1076}); - } else { - if (var0 != 1000) { - itemStackColor(-54, 54); - } - - return var1 >= 10000000 ? RSString.stringCombiner(new RSString[]{ColorCore.MillionStackColor, RSString.stringAnimator(var1 / 1000000), TextCore.MillionM, TextCore.aClass94_1076}) : RSString.stringCombiner(new RSString[]{ColorCore.ThousandStackColor, RSString.stringAnimator(var1 / 1000), TextCore.ThousandK, TextCore.aClass94_1076}); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "eb.C(" + var0 + ',' + var1 + ')'); - } - } - - public Class3_Sub7() { - } - - Class3_Sub7(long var1) { - try { - this.aLong2295 = var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "eb.(" + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub8.java b/Client/src/main/java/org/runite/client/Class3_Sub8.java deleted file mode 100644 index c9bcd382d..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub8.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.config.GameConfig; - - -import java.util.Objects; - -final class Class3_Sub8 extends Linkable { - - int anInt2296; - Class64[] aClass64Array2298; - int[] anIntArray2299; - int[] anIntArray2300; - int[] anIntArray2301; - byte[][][] aByteArrayArrayArray2302; - Class64[] aClass64Array2303; - int anInt2305; - - - static void method124(int var0, int var1, int var2) { - try { - if(var0 <= 23) { - TextCore.aClass94_2306 = null; - } - - if(Unsorted.loadInterface(var2)) { - Unsorted.method75(GameObject.aClass11ArrayArray1834[var2], var1); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ed.A(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - static void method126(boolean var0, int var1, int var2, int var3, int var4) { - try { - if(!var0) { - if(Class101.anInt1425 <= var4 && var4 <= Class3_Sub28_Sub18.anInt3765) { - var2 = Class40.method1040(Class57.anInt902, var2, Class159.anInt2020); - var1 = Class40.method1040(Class57.anInt902, var1, Class159.anInt2020); - TextureOperation15.method244(var2, var4, var1, var3); - } - - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ed.F(" + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method127(short[] var0, int var1, RSString[] var2, int var4) { - try { - if(var4 < var1) { - int var6 = var4; - int var5 = (var4 - -var1) / 2; - RSString var7 = var2[var5]; - var2[var5] = var2[var1]; - var2[var1] = var7; - short var8 = var0[var5]; - var0[var5] = var0[var1]; - var0[var1] = var8; - - for(int var9 = var4; var9 < var1; ++var9) { - if(var7 == null || null != var2[var9] && var2[var9].method1559(var7) < (var9 & 1)) { - RSString var10 = var2[var9]; - var2[var9] = var2[var6]; - var2[var6] = var10; - short var11 = var0[var9]; - var0[var9] = var0[var6]; - var0[var6++] = var11; - } - } - - var2[var1] = var2[var6]; - var2[var6] = var7; - var0[var1] = var0[var6]; - var0[var6] = var8; - method127(var0, -1 + var6, var2, var4); - method127(var0, var1, var2, var6 - -1); - } - - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "ed.E(" + (var0 != null?"{...}":"null") + ',' + var1 + ',' + (var2 != null?"{...}":"null") + ',' + -909 + ',' + var4 + ')'); - } - } - - static void method128() { - try { - Class44.aReferenceCache_725.clear(); - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ed.O(" + 2 + ')'); - } - } - - static int method129(int var0, int var2, int var3) { - try { - - if(var0 <= 243) { - if(var0 > 217) { - var2 >>= 3; - } else if(var0 <= 192) { - if(179 < var0) { - var2 >>= 1; - } - } else { - var2 >>= 2; - } - } else { - var2 >>= 4; - } - - return (var0 >> 1) + (var2 >> 5 << 7) + (var3 >> 2 << 10); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ed.D(" + var0 + ',' + 2 + ',' + var2 + ',' + var3 + ')'); - } - } - - static WorldListEntry getWorld(int var0, int index) { - try { - if(Class30.loadedWorldList && Class53.worldListOffset <= index && WorldListEntry.worldListArraySize >= index) { - GameConfig.WORLD = index; - return WorldListEntry.worldList[index - Class53.worldListOffset]; - } else { - return null; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ed.P(" + var0 + ',' + index + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class3_Sub9.java b/Client/src/main/java/org/runite/client/Class3_Sub9.java deleted file mode 100644 index 042761096..000000000 --- a/Client/src/main/java/org/runite/client/Class3_Sub9.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -final class Class3_Sub9 extends Linkable { - - int anInt2307; - int anInt2308; - int anInt2310; - Class3_Sub24_Sub1 aClass3_Sub24_Sub1_2312; - int anInt2314; - Class3_Sub24_Sub1 aClass3_Sub24_Sub1_2315; - int anInt2316; - static Class64 aClass64_2318; - ObjectDefinition aClass111_2320; - int anInt2321; - int anInt2322 = 0; - NPC aClass140_Sub4_Sub2_2324; - int anInt2325; - int anInt2326; - Player aClass140_Sub4_Sub1_2327; - int anInt2328; - boolean aBoolean2329; - int anInt2332; - int[] anIntArray2333; - - - static Class3_Sub28_Sub5 method133(int var0) { - try { - Class3_Sub28_Sub5 var2 = (Class3_Sub28_Sub5) Class159.aReferenceCache_2016.get(var0); - if (var2 == null) { - var2 = Unsorted.method1089(Class131.skeletonsReferenceIndex, Class7.skinsReferenceIndex, var0); - if (null != var2) { - Class159.aReferenceCache_2016.put(var2, var0); - } - - } - return var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fl.B(" + var0 + ',' + 0 + ')'); - } - } - - final void method134() { - try { - int var2 = this.anInt2332; - if (null != this.aClass111_2320) { - ObjectDefinition var3 = this.aClass111_2320.method1685(0); - if (var3 == null) { - this.anInt2332 = -1; - this.anIntArray2333 = null; - this.anInt2325 = 0; - this.anInt2328 = 0; - this.anInt2310 = 0; - } else { - this.anInt2325 = var3.anInt1515; - this.anInt2332 = var3.anInt1512; - this.anInt2310 = var3.anInt1518; - this.anInt2328 = var3.anInt1484 * 128; - this.anIntArray2333 = var3.anIntArray1539; - } - } else if (this.aClass140_Sub4_Sub2_2324 == null) { - if (null != this.aClass140_Sub4_Sub1_2327) { - this.anInt2332 = Class81.method1398(this.aClass140_Sub4_Sub1_2327); - this.anInt2328 = 128 * this.aClass140_Sub4_Sub1_2327.anInt3969; - } - } else { - int var6 = Class70.method1232(this.aClass140_Sub4_Sub2_2324); - if (var2 != var6) { - NPCDefinition var4 = this.aClass140_Sub4_Sub2_2324.definition; - this.anInt2332 = var6; - if (var4.childNPCs != null) { - var4 = var4.method1471((byte) -87); - } - - if (var4 == null) { - this.anInt2328 = 0; - } else { - this.anInt2328 = var4.anInt1291 * 128; - } - } - } - - if (this.anInt2332 != var2 && this.aClass3_Sub24_Sub1_2312 != null) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(this.aClass3_Sub24_Sub1_2312); - this.aClass3_Sub24_Sub1_2312 = null; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "fl.A(" + 1 + ')'); - } - } - - static void method135(int var0, int var1, int var2, int var4, int var5) { - try { - TextureOperation18.method282(Class38.anIntArrayArray663[var0], var1 - var2, -76, var2 + var1, var5); - int var6 = 0; - int var8 = var2 * var2; - int var9 = var4 * var4; - int var7 = var4; - int var10 = var9 << 1; - int var11 = var8 << 1; - int var12 = var4 << 1; - int var13 = var10 + (1 + -var12) * var8; - int var14 = var9 - var11 * (var12 - 1); - int var15 = var8 << 2; - - int var16 = var9 << 2; - int var17 = var10 * (3 + (var6 << 1)); - int var18 = var11 * ((var4 << 1) + -3); - int var19 = (1 + var6) * var16; - int var20 = var15 * (var4 - 1); - - while (var7 > 0) { - --var7; - int var22 = var7 + var0; - int var21 = var0 - var7; - if (var13 < 0) { - while (var13 < 0) { - ++var6; - var13 += var17; - var14 += var19; - var19 += var16; - var17 += var16; - } - } - - if (var14 < 0) { - var13 += var17; - var17 += var16; - var14 += var19; - ++var6; - var19 += var16; - } - - int var23 = var6 + var1; - var14 += -var18; - var18 -= var15; - var13 += -var20; - int var24 = var1 + -var6; - var20 -= var15; - TextureOperation18.method282(Class38.anIntArrayArray663[var21], var24, -110, var23, var5); - TextureOperation18.method282(Class38.anIntArrayArray663[var22], var24, 112, var23, var5); - } - - } catch (RuntimeException var25) { - throw ClientErrorException.clientError(var25, "fl.C(" + var0 + ',' + var1 + ',' + var2 + ',' + -111 + ',' + var4 + ',' + var5 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class40.java b/Client/src/main/java/org/runite/client/Class40.java deleted file mode 100644 index 7a56c36f6..000000000 --- a/Client/src/main/java/org/runite/client/Class40.java +++ /dev/null @@ -1,979 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; -import org.runite.client.drawcalls.LoadingBox; - -public final class Class40 { - - static RSString aClass94_672 = RSString.parse("null"); - static int[] anIntArray675 = new int[]{16, 32, 64, 128}; - static int anInt677 = 0; - static CacheIndex aClass153_679; - public static AbstractSprite aAbstractSprite_680; - static byte[][] aByteArrayArray3669; - static byte[][] aByteArrayArray3057; - static int anInt3293 = 0; - - - static int method1040(int var0, int var1, int var3) { - try { - return var1 < var3 ? var3 : var1 > var0 ? var0 : var1; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "gd.C(" + var0 + ',' + var1 + ',' + (byte) 0 + ',' + var3 + ')'); - } - } - - static void method1041(long var0, RSString name) { - try { - // System.out.println("Class 40 " + var0 + ", " + var2 + ", " + name.toString()); - TextureOperation12.outgoingBuffer.index = 0; - TextureOperation12.outgoingBuffer.writeByte(186); - TextureOperation12.outgoingBuffer.writeString(name); - // Class3_Sub13_Sub1.outgoingBuffer.putLong(var0, var2 + -2037463204); - Unsorted.registryStage = 1; - Class132.anInt1734 = 0; - GraphicDefinition.anInt548 = 0; - Unsorted.anInt1711 = -3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "gd.D(" + var0 + ',' + -28236 + ')'); - } - } - - static SoftwareSprite method1043(int var0, CacheIndex var1, int archiveId) { - try { - // System.out.println("Class 40 " + archiveId); - return Class75_Sub4.method1351(var1, var0, archiveId) ? Class117.method1722(-53) : null; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "gd.G(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ',' + -3178 + ',' + archiveId + ')'); - } - } - - static void method1045() { - try { - - Class128.aReferenceCache_1683.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "gd.B(" + -19761 + ')'); - } - } - - static void method1046() { - try { - Class163_Sub1.ping(false); - anInt3293 = 0; - boolean var1 = true; - - int var2; - for (var2 = 0; Class164_Sub2.aByteArrayArray3027.length > var2; ++var2) { - if (Client.anIntArray2200[var2] != -1 && null == Class164_Sub2.aByteArrayArray3027[var2]) { - Class164_Sub2.aByteArrayArray3027[var2] = CacheIndex.landscapesIndex.getFile(Client.anIntArray2200[var2], 0); - if (Class164_Sub2.aByteArrayArray3027[var2] == null) { - ++anInt3293; - var1 = false; - } - } - - if (-1 != Class101.anIntArray1426[var2] && null == Class3_Sub22.aByteArrayArray2521[var2]) { - Class3_Sub22.aByteArrayArray2521[var2] = CacheIndex.landscapesIndex.getFile(Class101.anIntArray1426[var2], 0, Class39.regionXteaKeys[var2]); - if (null == Class3_Sub22.aByteArrayArray2521[var2]) { - var1 = false; - ++anInt3293; - } - } - - if (HDToolKit.highDetail) { - if (TextureOperation17.anIntArray3181[var2] != -1 && aByteArrayArray3669[var2] == null) { - aByteArrayArray3669[var2] = CacheIndex.landscapesIndex.getFile(TextureOperation17.anIntArray3181[var2], 0); - if (null == aByteArrayArray3669[var2]) { - var1 = false; - ++anInt3293; - } - } - - if (Class3_Sub28_Sub5.anIntArray3587[var2] != -1 && null == aByteArrayArray3057[var2]) { - aByteArrayArray3057[var2] = CacheIndex.landscapesIndex.getFile(Class3_Sub28_Sub5.anIntArray3587[var2], 0); - if (null == aByteArrayArray3057[var2]) { - ++anInt3293; - var1 = false; - } - } - } - - if (null != NPC.npcSpawnCacheIndices && null == TextureOperation35.aByteArrayArray3335[var2] && NPC.npcSpawnCacheIndices[var2] != -1) { - TextureOperation35.aByteArrayArray3335[var2] = CacheIndex.landscapesIndex.getFile(NPC.npcSpawnCacheIndices[var2], 0, Class39.regionXteaKeys[var2]); - if (TextureOperation35.aByteArrayArray3335[var2] == null) { - ++anInt3293; - var1 = false; - } - } - } - - if (TextureOperation22.aClass131_3421 == null) { - if (null != TextureOperation37.aClass3_Sub28_Sub3_3264 && CacheIndex.worldmapIndex.method2135(RSString.stringCombiner(new RSString[]{TextureOperation37.aClass3_Sub28_Sub3_3264.aClass94_3561, TextCore.HasPlayerLabels}))) { - if (CacheIndex.worldmapIndex.method2127(RSString.stringCombiner(new RSString[]{TextureOperation37.aClass3_Sub28_Sub3_3264.aClass94_3561, TextCore.HasPlayerLabels}))) { - TextureOperation22.aClass131_3421 = Class81.getWorldMapArchive(RSString.stringCombiner(new RSString[]{TextureOperation37.aClass3_Sub28_Sub3_3264.aClass94_3561, TextCore.HasPlayerLabels}), CacheIndex.worldmapIndex); - } else { - var1 = false; - ++anInt3293; - } - } else { - TextureOperation22.aClass131_3421 = new Class131(0); - } - } - - if (var1) { - Class162.anInt2038 = 0; - var1 = true; - - int var4; - int var5; - for (var2 = 0; var2 < Class164_Sub2.aByteArrayArray3027.length; ++var2) { - byte[] var3 = Class3_Sub22.aByteArrayArray2521[var2]; - if (null != var3) { - var5 = -Texture.anInt1152 + (Class3_Sub24_Sub3.anIntArray3494[var2] & 0xFF) * 64; - var4 = -Class131.anInt1716 + (Class3_Sub24_Sub3.anIntArray3494[var2] >> 8) * 64; - if (LinkableRSString.isDynamicSceneGraph) { - var5 = 10; - var4 = 10; - } - - var1 &= Class24.isValidObjectMapping((byte) -97, var4, var5, var3); - } - - if (HDToolKit.highDetail) { - var3 = aByteArrayArray3057[var2]; - if (null != var3) { - var4 = -Class131.anInt1716 + 64 * (Class3_Sub24_Sub3.anIntArray3494[var2] >> 8); - var5 = -Texture.anInt1152 + 64 * (Class3_Sub24_Sub3.anIntArray3494[var2] & 0xFF); - if (LinkableRSString.isDynamicSceneGraph) { - var5 = 10; - var4 = 10; - } - - var1 &= Class24.isValidObjectMapping((byte) -74, var4, var5, var3); - } - } - } - - if (var1) { - if (Class163_Sub2_Sub1.anInt4019 != 0) { - LoadingBox.draw(true, RSString.stringCombiner(new RSString[]{TextCore.LoadingPleaseWait2, TextCore.aClass94_2707})); - } - - Class58.method1194(); - TextureOperation13.method313((byte) 58); - boolean var11 = false; - int var12; - if (HDToolKit.highDetail && Class128.aBoolean1685) { - for (var12 = 0; var12 < Class164_Sub2.aByteArrayArray3027.length; ++var12) { - if (null != aByteArrayArray3057[var12] || aByteArrayArray3669[var12] != null) { - var11 = true; - break; - } - } - } - - Class3_Sub4.initializeScene(HDToolKit.highDetail ? GameConfig.RENDER_DISTANCE_TILE_VALUE : 25, var11); - - for (var12 = 0; 4 > var12; ++var12) { - AtmosphereParser.aClass91Array1182[var12].method1496(); - } - - for (var12 = 0; var12 < 4; ++var12) { - for (var4 = 0; var4 < 104; ++var4) { - for (var5 = 0; var5 < 104; ++var5) { - Unsorted.aByteArrayArrayArray113[var12][var4][var5] = 0; - } - } - } - - Class164_Sub1.method2241((byte) -115, false); - if (HDToolKit.highDetail) { - Class141.aClass109_Sub1_1840.method1671(); - - for (var12 = 0; var12 < 13; ++var12) { - for (var4 = 0; var4 < 13; ++var4) { - Class141.aClass169ArrayArray1841[var12][var4].aBoolean2106 = true; - } - } - } - - if (HDToolKit.highDetail) { - Class68.method1279(); - } - - if (HDToolKit.highDetail) { - Class39.method1036(); - } - - Class58.method1194(); - System.gc(); - Class163_Sub1.ping(true); - Class117.method1720(false, 105); - if (!LinkableRSString.isDynamicSceneGraph) { - Unsorted.method1091(false, -93); - Class163_Sub1.ping(true); - if (HDToolKit.highDetail) { - var12 = Class102.player.anIntArray2767[0] >> 3; - var4 = Class102.player.anIntArray2755[0] >> 3; - TextureOperation1.method220(var4, var12); - } - - TextureOperation26.method198(false); - if (null != TextureOperation35.aByteArrayArray3335) { - TextureOperation37.displayNPCOnLoginRegion(); - } - } - - if (LinkableRSString.isDynamicSceneGraph) { - Class49.method1121(false, (byte) 98); - Class163_Sub1.ping(true); - if (HDToolKit.highDetail) { - var12 = Class102.player.anIntArray2767[0] >> 3; - var4 = Class102.player.anIntArray2755[0] >> 3; - TextureOperation1.method220(var4, var12); - } - - Class163_Sub2_Sub1.method2223(false, (byte) -121); - } - - TextureOperation13.method313((byte) 90); - Class163_Sub1.ping(true); - method2189(AtmosphereParser.aClass91Array1182, false, 66); - if (HDToolKit.highDetail) { - Class68.method1270(); - } - - Class163_Sub1.ping(true); - var12 = Class85.anInt1174; - if (var12 > WorldListCountry.localPlane) { - var12 = WorldListCountry.localPlane; - } - - if (WorldListCountry.localPlane + -1 > var12) { - } - - if (NPC.method1986(39)) { - Class85.method1425(0); - } else { - Class85.method1425(Class85.anInt1174); - } - - Class56.method1188(); - if (HDToolKit.highDetail && var11) { - Class167.method2264(true); - Class117.method1720(true, 105); - if (!LinkableRSString.isDynamicSceneGraph) { - Unsorted.method1091(true, -121); - Class163_Sub1.ping(true); - TextureOperation26.method198(true); - } - - if (LinkableRSString.isDynamicSceneGraph) { - Class49.method1121(true, (byte) 56); - Class163_Sub1.ping(true); - Class163_Sub2_Sub1.method2223(true, (byte) -105); - } - - TextureOperation13.method313((byte) 102); - Class163_Sub1.ping(true); - method2189(AtmosphereParser.aClass91Array1182, true, 112); - Class163_Sub1.ping(true); - Class56.method1188(); - Class167.method2264(false); - } - - if (HDToolKit.highDetail) { - for (var4 = 0; var4 < 13; ++var4) { - for (var5 = 0; var5 < 13; ++var5) { - Class141.aClass169ArrayArray1841[var4][var5].method2281(Class44.anIntArrayArrayArray723[0], var4 * 8, var5 * 8); - } - } - } - - for (var4 = 0; var4 < 104; ++var4) { - for (var5 = 0; 104 > var5; ++var5) { - Class128.method1760(var5, var4); - } - } - - Unsorted.method792(); - Class58.method1194(); - method318(); - TextureOperation13.method313((byte) 100); - TextureOperation25.aBoolean3416 = false; - if (GameShell.frame != null && null != Class3_Sub15.activeConnection && 25 == Class143.gameStage) { - TextureOperation12.outgoingBuffer.putOpcode(20); - TextureOperation12.outgoingBuffer.writeInt(1057001181); - } - - if (!LinkableRSString.isDynamicSceneGraph) { - int var7 = (Class3_Sub7.anInt2294 + 6) / 8; - int var6 = (Class3_Sub7.anInt2294 - 6) / 8; - var4 = (Unsorted.anInt3606 - 6) / 8; - var5 = (Unsorted.anInt3606 - -6) / 8; - - for (int var8 = var4 - 1; var5 - -1 >= var8; ++var8) { - for (int var9 = -1 + var6; var7 - -1 >= var9; ++var9) { - if (var4 > var8 || var8 > var5 || var6 > var9 || var9 > var7) { - CacheIndex.landscapesIndex.method2124(-124, RSString.stringCombiner(new RSString[]{RSString.parse("m"), RSString.stringAnimator(var8), RSString.parse("_"), RSString.stringAnimator(var9)})); - CacheIndex.landscapesIndex.method2124(-123, RSString.stringCombiner(new RSString[]{RSString.parse("l"), RSString.stringAnimator(var8), RSString.parse("_"), RSString.stringAnimator(var9)})); - } - } - } - } - - if (Class143.gameStage == 28) { - Class117.method1719(10); - } else { - Class117.method1719(30); - if (null != Class3_Sub15.activeConnection) { - TextureOperation12.outgoingBuffer.putOpcode(110); - } - } - - Class3_Sub20.method388((byte) 116); - Class58.method1194(); - Class75_Sub4.method1355(); - } else { - Class163_Sub2_Sub1.anInt4019 = 2; - } - } else { - Class163_Sub2_Sub1.anInt4019 = 1; - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "gd.F(" + -117 + ')'); - } - } - - static void method318() { - try { - Class3_Sub4 var1 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1222(); - - for (; null != var1; var1 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1221()) { - if (var1.anInt2259 == -1) { - var1.anInt2261 = 0; - Class132.method1798(56, var1); - } else { - var1.unlink(); - } - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rl.B(" + 7759444 + ')'); - } - } - - static void method2189(Class91[] var0, boolean var1, int var2) { - try { - int var4; - int var5; - if (!var1) { - for (var4 = 0; var4 < 4; ++var4) { - for (var5 = 0; var5 < 104; ++var5) { - for (int var6 = 0; var6 < 104; ++var6) { - if ((1 & Unsorted.aByteArrayArrayArray113[var4][var5][var6]) == 1) { - int var7 = var4; - if ((2 & Unsorted.aByteArrayArrayArray113[1][var5][var6]) == 2) { - var7 = var4 - 1; - } - - if (var7 >= 0) { - var0[var7].method1497(var6, var5); - } - } - } - } - } - - Class158_Sub1.anInt1463 += (int) (Math.random() * 5.0D) - 2; - if (Class158_Sub1.anInt1463 < -16) { - Class158_Sub1.anInt1463 = -16; - } - - if (Class158_Sub1.anInt1463 > 16) { - Class158_Sub1.anInt1463 = 16; - } - - Class158_Sub1.anInt3158 += (int) (Math.random() * 5.0D) - 2; - if (-8 > Class158_Sub1.anInt3158) { - Class158_Sub1.anInt3158 = -8; - } - - if (Class158_Sub1.anInt3158 > 8) { - Class158_Sub1.anInt3158 = 8; - } - } - - byte var3; - if (var1) { - var3 = 1; - } else { - var3 = 4; - } - - var4 = Class158_Sub1.anInt3158 >> 2 << 10; - int[][] var34 = new int[104][104]; - int[][] var35 = new int[104][104]; - var5 = Class158_Sub1.anInt1463 >> 1; - - int var8; - int var10; - int var11; - int var13; - int var14; - int var15; - int var16; - int var19; - int var18; - int var20; - int var37; - int var44; - for (var8 = 0; var3 > var8; ++var8) { - byte[][] var9 = Class67.aByteArrayArrayArray1014[var8]; - int var21; - int var23; - int var22; - int var24; - if (HDToolKit.highDetail) { - if (Class106.aBoolean1441) { - for (var10 = 1; var10 < 103; ++var10) { - for (var11 = 1; var11 < 103; ++var11) { - var13 = (var9[1 + var11][var10] >> 3) + (var9[-1 + var11][var10] >> 2) - -(var9[var11][-1 + var10] >> 2) - -(var9[var11][1 + var10] >> 3) - -(var9[var11][var10] >> 1); - byte var12 = 74; - var35[var11][var10] = -var13 + var12; - } - } - } else { - var10 = (int) Class92.light0Position[0]; - var11 = (int) Class92.light0Position[1]; - var37 = (int) Class92.light0Position[2]; - var13 = (int) Math.sqrt(var11 * var11 + (var10 * var10 - -(var37 * var37))); - var14 = 1024 * var13 >> 8; - - for (var15 = 1; var15 < 103; ++var15) { - for (var16 = 1; var16 < 103; ++var16) { - byte var17 = 96; - var18 = Class44.anIntArrayArrayArray723[var8][var16 - -1][var15] - Class44.anIntArrayArrayArray723[var8][-1 + var16][var15]; - var19 = Class44.anIntArrayArrayArray723[var8][var16][var15 + 1] - Class44.anIntArrayArrayArray723[var8][var16][-1 + var15]; - var20 = (int) Math.sqrt(var18 * var18 + 65536 + var19 * var19); - var21 = (var18 << 8) / var20; - var24 = (var9[var16][1 + var15] >> 3) + (var9[var16][var15 - 1] >> 2) + ((var9[var16 - 1][var15] >> 2) + (var9[var16 + 1][var15] >> 3) - -(var9[var16][var15] >> 1)); - var22 = -65536 / var20; - var23 = (var19 << 8) / var20; - var44 = var17 + (var37 * var23 + (var10 * var21 - -(var22 * var11))) / var14; - var35[var16][var15] = var44 + -((int) ((float) var24 * 1.7F)); - } - } - } - } else { - var10 = (int) Math.sqrt(5100.0D); - var11 = 768 * var10 >> 8; - - for (var37 = 1; var37 < 103; ++var37) { - for (var13 = 1; 103 > var13; ++var13) { - var16 = -Class44.anIntArrayArrayArray723[var8][var13][-1 + var37] + Class44.anIntArrayArrayArray723[var8][var13][var37 + 1]; - byte var41 = 74; - var15 = -Class44.anIntArrayArrayArray723[var8][var13 + -1][var37] + Class44.anIntArrayArrayArray723[var8][var13 - -1][var37]; - var44 = (int) Math.sqrt(var15 * var15 - -65536 - -(var16 * var16)); - var20 = (var16 << 8) / var44; - var19 = -65536 / var44; - var18 = (var15 << 8) / var44; - var21 = (var9[var13][var37] >> 1) + (var9[var13][-1 + var37] >> 2) + (var9[var13 - -1][var37] >> 3) + ((var9[var13 - 1][var37] >> 2) - -(var9[var13][var37 + 1] >> 3)); - var14 = var41 + (var20 * -50 + var18 * -50 - -(var19 * -10)) / var11; - var35[var13][var37] = var14 - var21; - } - } - } - - for (var10 = 0; 104 > var10; ++var10) { - Class129.anIntArray1695[var10] = 0; - Unsorted.anIntArray1138[var10] = 0; - Class3_Sub31.anIntArray2606[var10] = 0; - MouseListeningClass.anIntArray1920[var10] = 0; - Unsorted.anIntArray2469[var10] = 0; - } - - for (var10 = -5; var10 < 104; ++var10) { - for (var11 = 0; 104 > var11; ++var11) { - var37 = var10 - -5; - if (var37 < 104) { - var13 = 255 & TextureOperation36.aByteArrayArrayArray3430[var8][var37][var11]; - if (var13 > 0) { - FloorUnderlayDefinition var39 = FloorUnderlayDefinition.method629(-1 + var13); - Class129.anIntArray1695[var11] += var39.anInt1408; - Unsorted.anIntArray1138[var11] += var39.anInt1406; - Class3_Sub31.anIntArray2606[var11] += var39.anInt1417; - MouseListeningClass.anIntArray1920[var11] += var39.anInt1418; - ++Unsorted.anIntArray2469[var11]; - } - } - - var13 = -5 + var10; - if (0 <= var13) { - var14 = 255 & TextureOperation36.aByteArrayArrayArray3430[var8][var13][var11]; - if (var14 > 0) { - FloorUnderlayDefinition var42 = FloorUnderlayDefinition.method629(-1 + var14); - Class129.anIntArray1695[var11] -= var42.anInt1408; - Unsorted.anIntArray1138[var11] -= var42.anInt1406; - Class3_Sub31.anIntArray2606[var11] -= var42.anInt1417; - MouseListeningClass.anIntArray1920[var11] -= var42.anInt1418; - --Unsorted.anIntArray2469[var11]; - } - } - } - - if (var10 >= 0) { - var11 = 0; - var13 = 0; - var37 = 0; - var14 = 0; - var15 = 0; - - for (var16 = -5; var16 < 104; ++var16) { - var44 = var16 - -5; - if (104 > var44) { - var37 += Unsorted.anIntArray1138[var44]; - var15 += Unsorted.anIntArray2469[var44]; - var11 += Class129.anIntArray1695[var44]; - var14 += MouseListeningClass.anIntArray1920[var44]; - var13 += Class3_Sub31.anIntArray2606[var44]; - } - - var18 = var16 + -5; - if (var18 >= 0) { - var37 -= Unsorted.anIntArray1138[var18]; - var14 -= MouseListeningClass.anIntArray1920[var18]; - var11 -= Class129.anIntArray1695[var18]; - var15 -= Unsorted.anIntArray2469[var18]; - var13 -= Class3_Sub31.anIntArray2606[var18]; - } - - if (0 <= var16 && var15 > 0 && var14 != 0) { - var34[var10][var16] = Class3_Sub8.method129(var13 / var15, var37 / var15, 256 * var11 / var14); - } - } - } - } - - for (var10 = 1; var10 < 103; ++var10) { - label754: - for (var11 = 1; var11 < 103; ++var11) { - if (var1 || NPC.method1986(66) || (2 & Unsorted.aByteArrayArrayArray113[0][var10][var11]) != 0 || (16 & Unsorted.aByteArrayArrayArray113[var8][var10][var11]) == 0 && PacketParser.method823(var11, var10, -87, var8) == Class140_Sub3.anInt2745) { - if (var8 < Class85.anInt1174) { - Class85.anInt1174 = var8; - } - - var37 = 255 & TextureOperation36.aByteArrayArrayArray3430[var8][var10][var11]; - var13 = Class158_Sub1.aByteArrayArrayArray1828[var8][var10][var11] & 0xFF; - if (0 < var37 || var13 > 0) { - var15 = Class44.anIntArrayArrayArray723[var8][var10 + 1][var11]; - var14 = Class44.anIntArrayArrayArray723[var8][var10][var11]; - var44 = Class44.anIntArrayArrayArray723[var8][var10][1 + var11]; - var16 = Class44.anIntArrayArrayArray723[var8][1 + var10][var11 + 1]; - if (0 < var8) { - boolean var47 = true; - if (var37 == 0 && Unsorted.aByteArrayArrayArray1328[var8][var10][var11] != 0) { - var47 = false; - } - - if (var13 > 0 && !FloorOverlayDefinition.getFile(var13 + -1).aBoolean2102) { - var47 = false; - } - - if (var47 && var14 == var15 && var16 == var14 && var14 == var44) { - Class38_Sub1.anIntArrayArrayArray2609[var8][var10][var11] = TextureOperation3.bitwiseOr(Class38_Sub1.anIntArrayArrayArray2609[var8][var10][var11], 4); - } - } - - if (var37 <= 0) { - var18 = -1; - var19 = 0; - } else { - var18 = var34[var10][var11]; - var20 = (var18 & 127) + var5; - if (var20 >= 0) { - if (var20 > 127) { - var20 = 127; - } - } else { - var20 = 0; - } - - var21 = (896 & var18) + (var18 + var4 & '\ufc00') + var20; - var19 = Class51.anIntArray834[Unsorted.method1100(96, var21)]; - } - - var20 = var35[var10][var11]; - var23 = var35[var10][var11 + 1]; - var21 = var35[1 + var10][var11]; - var22 = var35[var10 - -1][var11 - -1]; - if (var13 == 0) { - method1629(var8, var10, var11, 0, 0, -1, var14, var15, var16, var44, Unsorted.method1100(var20, var18), Unsorted.method1100(var21, var18), Unsorted.method1100(var22, var18), Unsorted.method1100(var23, var18), 0, 0, 0, 0, var19, 0); - if (HDToolKit.highDetail && var8 > 0 && var18 != -1 && FloorUnderlayDefinition.method629(-1 + var37).aBoolean1411) { - Class141.method2037(0, 0, true, false, var10, var11, var14 - Class44.anIntArrayArrayArray723[0][var10][var11], -Class44.anIntArrayArrayArray723[0][1 + var10][var11] + var15, var16 - Class44.anIntArrayArrayArray723[0][1 + var10][1 + var11], var44 - Class44.anIntArrayArrayArray723[0][var10][1 + var11]); - } - - if (HDToolKit.highDetail && !var1 && TextureOperation16.anIntArrayArray3115 != null && 0 == var8) { - for (var24 = var10 + -1; var10 - -1 >= var24; ++var24) { - for (int var52 = -1 + var11; var52 <= 1 + var11; ++var52) { - if ((var24 != var10 || var11 != var52) && var24 >= 0 && var24 < 104 && 0 <= var52 && var52 < 104) { - int var54 = Class158_Sub1.aByteArrayArrayArray1828[var8][var24][var52] & 0xFF; - if (var54 != 0) { - FloorOverlayDefinition var53 = FloorOverlayDefinition.getFile(-1 + var54); - if (var53.anInt2095 != -1 && 4 == Class51.anInterface2_838.method18(var53.anInt2095, 255)) { - TextureOperation16.anIntArrayArray3115[var10][var11] = var53.anInt2094 + (var53.anInt2101 << 24); - continue label754; - } - } - } - } - } - } - } else { - var24 = 1 + Unsorted.aByteArrayArrayArray1328[var8][var10][var11]; - byte var25 = PacketParser.aByteArrayArrayArray81[var8][var10][var11]; - FloorOverlayDefinition var26 = FloorOverlayDefinition.getFile(var13 + -1); - int var27; - int var29; - int var28; - if (HDToolKit.highDetail && !var1 && null != TextureOperation16.anIntArrayArray3115 && 0 == var8) { - if (-1 != var26.anInt2095 && Class51.anInterface2_838.method18(var26.anInt2095, 255) == 4) { - TextureOperation16.anIntArrayArray3115[var10][var11] = (var26.anInt2101 << 24) + var26.anInt2094; - } else { - label722: - for (var27 = var10 + -1; 1 + var10 >= var27; ++var27) { - for (var28 = var11 + -1; 1 + var11 >= var28; ++var28) { - if ((var27 != var10 || var11 != var28) && var27 >= 0 && var27 < 104 && var28 >= 0 && var28 < 104) { - var29 = Class158_Sub1.aByteArrayArrayArray1828[var8][var27][var28] & 0xFF; - if (var29 != 0) { - FloorOverlayDefinition var30 = FloorOverlayDefinition.getFile(-1 + var29); - if (var30.anInt2095 != -1 && Class51.anInterface2_838.method18(var30.anInt2095, 255) == 4) { - TextureOperation16.anIntArrayArray3115[var10][var11] = var30.anInt2094 + (var30.anInt2101 << 24); - break label722; - } - } - } - } - } - } - } - - var27 = var26.anInt2095; - if (0 <= var27 && !Class51.anInterface2_838.method17(var27, 101)) { - var27 = -1; - } - - int var31; - int var55; - if (var27 < 0) { - if (var26.anInt2103 == -1) { - var28 = -2; - var29 = 0; - } else { - var28 = var26.anInt2103; - var55 = var5 + (var28 & 127); - if (var55 >= 0) { - if (var55 > 127) { - var55 = 127; - } - } else { - var55 = 0; - } - - var31 = (var28 & 896) + (('\ufc00' & var28 + var4) - -var55); - var29 = Class51.anIntArray834[LinkableRSString.method729((byte) -85, var31, 96)]; - } - } else { - var28 = -1; - var29 = Class51.anIntArray834[LinkableRSString.method729((byte) -126, Class51.anInterface2_838.method15(var27, 65535), 96)]; - } - - if (var26.anInt2098 >= 0) { - var55 = var26.anInt2098; - var31 = var5 + (var55 & 127); - if (var31 >= 0) { - if (127 < var31) { - var31 = 127; - } - } else { - var31 = 0; - } - - int var32 = (896 & var55) + (('\ufc00' & var55 + var4) - -var31); - var29 = Class51.anIntArray834[LinkableRSString.method729((byte) -101, var32, 96)]; - } - - method1629(var8, var10, var11, var24, var25, var27, var14, var15, var16, var44, Unsorted.method1100(var20, var18), Unsorted.method1100(var21, var18), Unsorted.method1100(var22, var18), Unsorted.method1100(var23, var18), LinkableRSString.method729((byte) -72, var28, var20), LinkableRSString.method729((byte) -107, var28, var21), LinkableRSString.method729((byte) -82, var28, var22), LinkableRSString.method729((byte) -93, var28, var23), var19, var29); - if (HDToolKit.highDetail && var8 > 0) { - Class141.method2037(var24, var25, var28 == -2 || !var26.aBoolean2093, -1 == var18 || !FloorUnderlayDefinition.method629(-1 + var37).aBoolean1411, var10, var11, -Class44.anIntArrayArrayArray723[0][var10][var11] + var14, var15 - Class44.anIntArrayArrayArray723[0][1 + var10][var11], -Class44.anIntArrayArrayArray723[0][1 + var10][var11 + 1] + var16, -Class44.anIntArrayArrayArray723[0][var10][1 + var11] + var44); - } - } - } - } - } - } - - if (HDToolKit.highDetail) { - float[][] var38 = new float[105][105]; - int[][] var45 = Class44.anIntArrayArrayArray723[var8]; - float[][] var40 = new float[105][105]; - float[][] var43 = new float[105][105]; - - for (var14 = 1; var14 <= 103; ++var14) { - for (var15 = 1; var15 <= 103; ++var15) { - var44 = var45[var15][var14 - -1] + -var45[var15][-1 + var14]; - var16 = -var45[var15 - 1][var14] + var45[var15 + 1][var14]; - float var51 = (float) Math.sqrt(var16 * var16 - -65536 - -(var44 * var44)); - var38[var15][var14] = (float) var16 / var51; - var40[var15][var14] = -256.0F / var51; - var43[var15][var14] = (float) var44 / var51; - } - } - - Class3_Sub11[] var50; - if (var1) { - var50 = TextureOperation7.method298(Unsorted.aByteArrayArrayArray113, Unsorted.aByteArrayArrayArray1328[var8], TextureOperation36.aByteArrayArrayArray3430[var8], var35, var40, TextureOperation16.anIntArrayArray3115, Class158_Sub1.aByteArrayArrayArray1828[var8], PacketParser.aByteArrayArrayArray81[var8], var38, var8, var43, var34, Class44.anIntArrayArrayArray723[var8], Class58.anIntArrayArrayArray914[0]); - LinkedList.method1213(var8, var50); - } else { - var50 = TextureOperation7.method298(Unsorted.aByteArrayArrayArray113, Unsorted.aByteArrayArrayArray1328[var8], TextureOperation36.aByteArrayArrayArray3430[var8], var35, var40, null, Class158_Sub1.aByteArrayArrayArray1828[var8], PacketParser.aByteArrayArrayArray81[var8], var38, var8, var43, var34, Class44.anIntArrayArrayArray723[var8], null); - Class3_Sub11[] var46 = Class1.method70(var40, var38, Class44.anIntArrayArrayArray723[var8], var8, var43, PacketParser.aByteArrayArrayArray81[var8], var35, Unsorted.aByteArrayArrayArray1328[var8], TextureOperation36.aByteArrayArrayArray3430[var8], Class158_Sub1.aByteArrayArrayArray1828[var8], Unsorted.aByteArrayArrayArray113); - Class3_Sub11[] var49 = new Class3_Sub11[var50.length - -var46.length]; - - for (var44 = 0; var44 < var50.length; ++var44) { - var49[var44] = var50[var44]; - } - - for (var44 = 0; var44 < var46.length; ++var44) { - var49[var50.length + var44] = var46[var44]; - } - - LinkedList.method1213(var8, var49); - Class129.method1769(var43, TextureOperation36.aByteArrayArrayArray3430[var8], PacketParser.aByteArrayArrayArray81[var8], Class68.aClass43Array1021, var8, Class68.anInt1032, var40, Unsorted.aByteArrayArrayArray1328[var8], Class158_Sub1.aByteArrayArrayArray1828[var8], Class44.anIntArrayArrayArray723[var8], var38); - } - } - - TextureOperation36.aByteArrayArrayArray3430[var8] = null; - Class158_Sub1.aByteArrayArrayArray1828[var8] = null; - Unsorted.aByteArrayArrayArray1328[var8] = null; - PacketParser.aByteArrayArrayArray81[var8] = null; - Class67.aByteArrayArrayArray1014[var8] = null; - } - - if (var2 <= 26) { - method2187(86); - } - - Class128.method1764(); - if (!var1) { - int var36; - for (var8 = 0; 104 > var8; ++var8) { - for (var36 = 0; var36 < 104; ++var36) { - if ((Unsorted.aByteArrayArrayArray113[1][var8][var36] & 2) == 2) { - Class3_Sub28_Sub18.method709(var8, var36); - } - } - } - - for (var8 = 0; 4 > var8; ++var8) { - for (var36 = 0; var36 <= 104; ++var36) { - for (var10 = 0; var10 <= 104; ++var10) { - short var48; - if ((Class38_Sub1.anIntArrayArrayArray2609[var8][var10][var36] & 1) != 0) { - var14 = var8; - - for (var11 = var36; var11 > 0 && (1 & Class38_Sub1.anIntArrayArrayArray2609[var8][var10][-1 + var11]) != 0; --var11) { - } - - var13 = var8; - - for (var37 = var36; var37 < 104 && (1 & Class38_Sub1.anIntArrayArrayArray2609[var8][var10][var37 - -1]) != 0; ++var37) { - } - - label453: - while (var13 > 0) { - for (var15 = var11; var15 <= var37; ++var15) { - if ((Class38_Sub1.anIntArrayArrayArray2609[var13 + -1][var10][var15] & 1) == 0) { - break label453; - } - } - - --var13; - } - - label464: - while (var14 < 3) { - for (var15 = var11; var15 <= var37; ++var15) { - if ((1 & Class38_Sub1.anIntArrayArrayArray2609[var14 + 1][var10][var15]) == 0) { - break label464; - } - } - - ++var14; - } - - var15 = (var14 - (-1 + var13)) * (-var11 + (var37 - -1)); - if (var15 >= 8) { - var48 = 240; - var44 = -var48 + Class44.anIntArrayArrayArray723[var14][var10][var11]; - var18 = Class44.anIntArrayArrayArray723[var13][var10][var11]; - Class167.method2263(1, 128 * var10, 128 * var10, 128 * var11, var37 * 128 + 128, var44, var18); - - for (var19 = var13; var19 <= var14; ++var19) { - for (var20 = var11; var37 >= var20; ++var20) { - Class38_Sub1.anIntArrayArrayArray2609[var19][var10][var20] = Unsorted.bitwiseAnd(Class38_Sub1.anIntArrayArrayArray2609[var19][var10][var20], -2); - } - } - } - } - - if ((2 & Class38_Sub1.anIntArrayArrayArray2609[var8][var10][var36]) != 0) { - for (var11 = var10; 0 < var11 && (Class38_Sub1.anIntArrayArrayArray2609[var8][-1 + var11][var36] & 2) != 0; --var11) { - } - - var14 = var8; - var13 = var8; - - for (var37 = var10; 104 > var37 && (2 & Class38_Sub1.anIntArrayArrayArray2609[var8][var37 - -1][var36]) != 0; ++var37) { - } - - label503: - while (var13 > 0) { - for (var15 = var11; var15 <= var37; ++var15) { - if (0 == (2 & Class38_Sub1.anIntArrayArrayArray2609[-1 + var13][var15][var36])) { - break label503; - } - } - - --var13; - } - - label514: - while (var14 < 3) { - for (var15 = var11; var15 <= var37; ++var15) { - if ((2 & Class38_Sub1.anIntArrayArrayArray2609[var14 + 1][var15][var36]) == 0) { - break label514; - } - } - - ++var14; - } - - var15 = (-var11 + var37 - -1) * (-var13 + var14 - -1); - if (8 <= var15) { - var48 = 240; - var44 = Class44.anIntArrayArrayArray723[var14][var11][var36] - var48; - var18 = Class44.anIntArrayArrayArray723[var13][var11][var36]; - Class167.method2263(2, var11 * 128, 128 * var37 + 128, 128 * var36, var36 * 128, var44, var18); - - for (var19 = var13; var14 >= var19; ++var19) { - for (var20 = var11; var20 <= var37; ++var20) { - Class38_Sub1.anIntArrayArrayArray2609[var19][var20][var36] = Unsorted.bitwiseAnd(Class38_Sub1.anIntArrayArrayArray2609[var19][var20][var36], -3); - } - } - } - } - - if ((4 & Class38_Sub1.anIntArrayArrayArray2609[var8][var10][var36]) != 0) { - var11 = var10; - var37 = var10; - - for (var13 = var36; 0 < var13 && 0 != (4 & Class38_Sub1.anIntArrayArrayArray2609[var8][var10][-1 + var13]); --var13) { - } - - for (var14 = var36; var14 < 104 && (Class38_Sub1.anIntArrayArrayArray2609[var8][var10][var14 + 1] & 4) != 0; ++var14) { - } - - label554: - while (var11 > 0) { - for (var15 = var13; var14 >= var15; ++var15) { - if (0 == (Class38_Sub1.anIntArrayArrayArray2609[var8][var11 + -1][var15] & 4)) { - break label554; - } - } - - --var11; - } - - label565: - while (var37 < 104) { - for (var15 = var13; var14 >= var15; ++var15) { - if (0 == (4 & Class38_Sub1.anIntArrayArrayArray2609[var8][1 + var37][var15])) { - break label565; - } - } - - ++var37; - } - - if (4 <= (1 + -var11 + var37) * (var14 - (var13 - 1))) { - var15 = Class44.anIntArrayArrayArray723[var8][var11][var13]; - Class167.method2263(4, var11 * 128, 128 * var37 - -128, var13 * 128, 128 + 128 * var14, var15, var15); - - for (var16 = var11; var37 >= var16; ++var16) { - for (var44 = var13; var14 >= var44; ++var44) { - Class38_Sub1.anIntArrayArrayArray2609[var8][var16][var44] = Unsorted.bitwiseAnd(Class38_Sub1.anIntArrayArrayArray2609[var8][var16][var44], -5); - } - } - } - } - } - } - } - } - - } catch (RuntimeException var33) { - throw ClientErrorException.clientError(var33, "di.K(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ')'); - } - } - - public static void method2187(int var0) { - try { - Class158_Sub1.aClass94Array2977 = null; - Class158_Sub1.aReferenceCache_2982 = null; - Class158_Sub1.aClass3_Sub1_2980 = null; - if (var0 != 27316) { - Class158_Sub1.aBoolean2981 = true; - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "di.I(" + var0 + ')'); - } - } - - static void method1629(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12, int var13, int var14, int var15, int var16, int var17, int var18, int var19) { - int var21; - Class126 var20; - if (var3 == 0) { - var20 = new Class126(var10, var11, var12, var13, -1, var18, false); - - for (var21 = var0; var21 >= 0; --var21) { - if (Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var21][var1][var2] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var21][var1][var2] = new Class3_Sub2(var21, var1, var2); - } - } - - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2].aClass126_2240 = var20; - } else if (var3 == 1) { - var20 = new Class126(var14, var15, var16, var17, var5, var19, var6 == var7 && var6 == var8 && var6 == var9); - - for (var21 = var0; var21 >= 0; --var21) { - if (Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var21][var1][var2] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var21][var1][var2] = new Class3_Sub2(var21, var1, var2); - } - } - - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2].aClass126_2240 = var20; - } else { - Class35 var22 = new Class35(var3, var4, var5, var1, var2, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15, var16, var17, var18, var19); - - for (var21 = var0; var21 >= 0; --var21) { - if (Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var21][var1][var2] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var21][var1][var2] = new Class3_Sub2(var21, var1, var2); - } - } - - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2].aClass35_2226 = var22; - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class41.java b/Client/src/main/java/org/runite/client/Class41.java deleted file mode 100644 index ef05d4946..000000000 --- a/Client/src/main/java/org/runite/client/Class41.java +++ /dev/null @@ -1,220 +0,0 @@ -package org.runite.client; - -import java.io.EOFException; -import java.io.IOException; - -final class Class41 { - - static byte[] aByteArray2040 = new byte[520]; - private final Class30 aClass30_681; - private final Class30 aClass30_683; - private final int anInt687; - int cacheIndex; - - Class41(int var1, Class30 var2, Class30 var3, int var4) { - this.anInt687 = var4; - this.aClass30_683 = var3; - this.cacheIndex = var1; - this.aClass30_681 = var2; - } - - public final String toString() { - return "Cache:" + this.cacheIndex; - } - - final void write(int var1, int var2, byte[] var3) { - synchronized (this.aClass30_681) { - if (0 <= var2 && var2 <= this.anInt687) { - boolean var6 = this.method1054((byte) 87, var2, var1, var3, true); - if (!var6) { - var6 = this.method1054((byte) 87, var2, var1, var3, false); - } - - } else { - throw new IllegalArgumentException(); - } - } - } - - final byte[] read(int var1) { - synchronized (this.aClass30_681) { - Object var10000; - try { - if ((long) (var1 * 6 + 6) > this.aClass30_683.method976(0)) { - var10000 = null; - return (byte[]) var10000; - } - - this.aClass30_683.method984(-35, 6 * var1); - this.aClass30_683.method978(0, aByteArray2040, 6); - int var5 = ((255 & aByteArray2040[3]) << 16) - (-(aByteArray2040[4] << 8 & 65280) + -(255 & aByteArray2040[5])); - int var4 = (aByteArray2040[2] & 0xFF) + (65280 & aByteArray2040[1] << 8) + (16711680 & aByteArray2040[0] << 16); - if (this.anInt687 < var4) { - var10000 = null; - return (byte[]) var10000; - } - - if (0 < var5 && this.aClass30_681.method976(0) / 520L >= (long) var5) { - byte[] var7 = new byte[var4]; - int var8 = 0; - - int var13; - for (int var9 = 0; var4 > var8; var5 = var13) { - if (0 == var5) { - var10000 = null; - return (byte[]) var10000; - } - - int var10 = -var8 + var4; - this.aClass30_681.method984(-113, 520 * var5); - if (var10 > 512) { - var10 = 512; - } - - this.aClass30_681.method978(0, aByteArray2040, 8 + var10); - int var11 = (aByteArray2040[0] << 8 & 65280) - -(255 & aByteArray2040[1]); - int var12 = (aByteArray2040[3] & 0xFF) + (65280 & aByteArray2040[2] << 8); - int var14 = 255 & aByteArray2040[7]; - var13 = (aByteArray2040[6] & 0xFF) + (65280 & aByteArray2040[5] << 8) + (aByteArray2040[4] << 16 & 16711680); - if (var1 != var11 || var9 != var12 || this.cacheIndex != var14) { - var10000 = null; - return (byte[]) var10000; - } - - if (var13 < 0 || (long) var13 > this.aClass30_681.method976(0) / 520L) { - var10000 = null; - return (byte[]) var10000; - } - - for (int var15 = 0; var10 > var15; ++var15) { - var7[var8++] = aByteArray2040[var15 + 8]; - } - - ++var9; - } - - return var7; - } - - var10000 = null; - } catch (IOException var17) { - return null; - } - - return (byte[]) var10000; - } - } - - private boolean method1054(byte var1, int var2, int var3, byte[] var4, boolean var5) { - synchronized (this.aClass30_681) { - try { - int var7; - if (var5) { - if (this.aClass30_683.method976(var1 ^ 87) < (long) (6 + var3 * 6)) { - return false; - } - - this.aClass30_683.method984(-116, 6 * var3); - this.aClass30_683.method978(0, aByteArray2040, 6); - var7 = (16711680 & aByteArray2040[3] << 16) + (65280 & aByteArray2040[4] << 8) + (aByteArray2040[5] & 0xFF); - if (var7 <= 0 || (long) var7 > this.aClass30_681.method976(0) / 520L) { - return false; - } - } else { - var7 = (int) ((this.aClass30_681.method976(var1 + -87) - -519L) / 520L); - if (var7 == 0) { - var7 = 1; - } - } - - aByteArray2040[0] = (byte) (var2 >> 16); - aByteArray2040[4] = (byte) (var7 >> 8); - int var8 = 0; - aByteArray2040[5] = (byte) var7; - aByteArray2040[2] = (byte) var2; - aByteArray2040[3] = (byte) (var7 >> 16); - if (var1 != 87) { - this.method1054((byte) 41, 108, -107, null, true); - } - - int var9 = 0; - aByteArray2040[1] = (byte) (var2 >> 8); - this.aClass30_683.method984(-14, var3 * 6); - this.aClass30_683.method983(aByteArray2040, 0, var1 ^ -903171097, 6); - - while (true) { - if (var2 > var8) { - label146: - { - int var10 = 0; - int var11; - if (var5) { - this.aClass30_681.method984(-116, 520 * var7); - - try { - this.aClass30_681.method978(0, aByteArray2040, 8); - } catch (EOFException var15) { - break label146; - } - - var10 = ((aByteArray2040[4] & 0xFF) << 16) + (65280 & aByteArray2040[5] << 8) - -(aByteArray2040[6] & 0xFF); - var11 = (255 & aByteArray2040[1]) + ((aByteArray2040[0] & 0xFF) << 8); - int var13 = 255 & aByteArray2040[7]; - int var12 = (aByteArray2040[3] & 0xFF) + (aByteArray2040[2] << 8 & 65280); - if (var11 != var3 || var12 != var9 || var13 != this.cacheIndex) { - return false; - } - - if (var10 < 0 || this.aClass30_681.method976(0) / 520L < (long) var10) { - return false; - } - } - - var11 = -var8 + var2; - if (var10 == 0) { - var5 = false; - var10 = (int) ((this.aClass30_681.method976(0) - -519L) / 520L); - if (var10 == 0) { - ++var10; - } - - if (var7 == var10) { - ++var10; - } - } - - aByteArray2040[7] = (byte) this.cacheIndex; - aByteArray2040[0] = (byte) (var3 >> 8); - if (-var8 + var2 <= 512) { - var10 = 0; - } - - aByteArray2040[4] = (byte) (var10 >> 16); - if (var11 > 512) { - var11 = 512; - } - - aByteArray2040[1] = (byte) var3; - aByteArray2040[6] = (byte) var10; - aByteArray2040[2] = (byte) (var9 >> 8); - aByteArray2040[3] = (byte) var9; - ++var9; - aByteArray2040[5] = (byte) (var10 >> 8); - this.aClass30_681.method984(var1 + -128, var7 * 520); - var7 = var10; - this.aClass30_681.method983(aByteArray2040, 0, -903171152, 8); - this.aClass30_681.method983(var4, var8, -903171152, var11); - var8 += var11; - continue; - } - } - - return true; - } - } catch (IOException var16) { - return false; - } - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class42.java b/Client/src/main/java/org/runite/client/Class42.java deleted file mode 100644 index ed4d1b93a..000000000 --- a/Client/src/main/java/org/runite/client/Class42.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.runite.client; - -import java.applet.Applet; - -final class Class42 { - - static Object method1055(String var0, Applet var2) { - return null; - } - - static void method1056(Applet var0, Object[] var2) { - /*JSObject.getWindow(var0).call("openjs", var2);*/ - } - - static void method1057(Applet var0, String var2) { - /*JSObject.getWindow(var0).eval(var2);*/ - } -} diff --git a/Client/src/main/java/org/runite/client/Class43.java b/Client/src/main/java/org/runite/client/Class43.java deleted file mode 100644 index 3cb29925b..000000000 --- a/Client/src/main/java/org/runite/client/Class43.java +++ /dev/null @@ -1,268 +0,0 @@ -package org.runite.client; - -public final class Class43 { - - public static int worldListStage = 0; - static int[] anIntArray3107; - static int anInt716 = 0; - boolean aBoolean690; - boolean aBoolean696 = false; - int anInt697; - int anInt698; - int anInt703; - int anInt704; - int anInt705; - short[] aShortArray706; - float aFloat707; - int anInt708; - float aFloat710; - boolean aBoolean711; - Class37 aClass37_712; - int anInt713; - float[] aFloatArray717 = new float[4]; - private int anInt693; - private int anInt694; - private int anInt702; - private int anInt709; - private int anInt714; - - protected Class43() { - try { - if (anIntArray3107 == null) { - Class45.method1083((byte) -90); - } - - this.method1064(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "gi.()"); - } - } - - Class43(DataBuffer var1) { - try { - if (null == anIntArray3107) { - Class45.method1083((byte) -94); - } - - this.anInt704 = var1.readUnsignedByte(); - this.aBoolean690 = (this.anInt704 & 16) != 0; - this.aBoolean711 = (this.anInt704 & 8) != 0; - this.anInt704 &= 7; - this.anInt703 = var1.readUnsignedShort(); - this.anInt708 = var1.readUnsignedShort(); - this.anInt697 = var1.readUnsignedShort(); - this.anInt698 = var1.readUnsignedByte(); - this.method1061(); - this.aShortArray706 = new short[this.anInt698 * 2 + 1]; - - int var2; - for (var2 = 0; var2 < this.aShortArray706.length; ++var2) { - this.aShortArray706[var2] = (short) var1.readUnsignedShort(); - } - - this.anInt713 = Class51.anIntArray834[var1.readUnsignedShort()]; - var2 = var1.readUnsignedByte(); - this.anInt714 = 1792 & var2 << 3; - this.anInt705 = var2 & 31; - if (31 != this.anInt705) { - this.method1064(); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "gi.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method1058(int var0, int var1, int var2, int var3) { - try { - if (Class101.anInt1425 <= var1 + -var0 && Class3_Sub28_Sub18.anInt3765 >= var0 + var1 && var3 + -var0 >= Class159.anInt2020 && Class57.anInt902 >= var0 + var3) { - Class24.method949(var1, var0, var2, var3); - } else { - Class49.method1129(var2, var3, var0, var1); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "gi.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + (byte) -47 + ')'); - } - } - - static AbstractSprite method1062(int var0) { - try { - byte[] var2 = Class163_Sub1.aByteArrayArray2987[0]; - int var1 = Class140_Sub7.anIntArray2931[0] * Unsorted.anIntArray3076[0]; - int[] var3 = new int[var1]; - if (var0 < 70) { - method1062(67); - } - - for (int var4 = 0; var4 < var1; ++var4) { - var3[var4] = TextureOperation38.spritePalette[Unsorted.bitwiseAnd(var2[var4], 255)]; - } - - Object var6; - if (HDToolKit.highDetail) { - var6 = new HDSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[0], Unsorted.anIntArray2591[0], Class140_Sub7.anIntArray2931[0], Unsorted.anIntArray3076[0], var3); - } else { - var6 = new SoftwareSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[0], Unsorted.anIntArray2591[0], Class140_Sub7.anIntArray2931[0], Unsorted.anIntArray3076[0], var3); - } - - Class39.method1035((byte) 111); - return (AbstractSprite) var6; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "gi.D(" + var0 + ')'); - } - } - - static void method1065(CS2Script var1) { - try { - CS2Script.runAssembledScript(200000, var1); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "gi.H(" + 1073376993 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - final void method1060(byte var1, int var2, int var3, int var4, int var5) { - try { - this.anInt694 = var2; - this.anInt702 = var4; - this.anInt693 = var5; - this.anInt709 = var3; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "gi.F(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - private void method1061() { - try { - int var2 = (this.anInt698 << 7) - -64; - this.aFloat710 = 1.0F / (float) (var2 * var2); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "gi.C(" + 66 + ')'); - } - } - - final void method1063(boolean var1, int var2) { - try { - - int var5 = this.anInt714 + var2 * this.anInt709 / 50 & 0x7FF; - int var6 = this.anInt694; - int var4; - if (var6 == 1) { - var4 = 1024 - -(Class51.anIntArray840[var5] >> 6); - } else if (var6 == 3) { - var4 = anIntArray3107[var5] >> 1; - } else if (var6 == 4) { - var4 = var5 >> 10 << 11; - } else if (var6 == 2) { - var4 = var5; - } else if (var6 == 5) { - var4 = (var5 < 1024 ? var5 : 2048 - var5) << 1; - } else { - var4 = 2048; - } - - if (var1) { - var4 = 2048; - } - - this.aFloat707 = (float) (this.anInt693 + (var4 * this.anInt702 >> 11)) / 2048.0F; - float var8 = this.aFloat707 / 255.0F; - this.aFloatArray717[0] = (float) (Unsorted.bitwiseAnd(this.anInt713, 16771365) >> 16) * var8; - this.aFloatArray717[2] = var8 * (float) Unsorted.bitwiseAnd(255, this.anInt713); - this.aFloatArray717[1] = (float) (Unsorted.bitwiseAnd(this.anInt713, '\uffe7') >> 8) * var8; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "gi.A(" + var1 + ',' + var2 + ',' + -3696 + ')'); - } - } - - private void method1064() { - try { - int var2 = this.anInt705; - if (var2 == 2) { - this.anInt702 = 2048; - this.anInt693 = 0; - this.anInt694 = 1; - this.anInt709 = 2048; - } else if (var2 == 3) { - this.anInt693 = 0; - this.anInt709 = 4096; - this.anInt694 = 1; - this.anInt702 = 2048; - } else if (var2 == 4) { - this.anInt693 = 0; - this.anInt702 = 2048; - this.anInt694 = 4; - this.anInt709 = 2048; - } else if (var2 == 5) { - this.anInt694 = 4; - this.anInt702 = 2048; - this.anInt709 = 8192; - this.anInt693 = 0; - } else if (var2 == 12) { - this.anInt702 = 2048; - this.anInt694 = 2; - this.anInt709 = 2048; - this.anInt693 = 0; - } else if (var2 == 13) { - this.anInt709 = 8192; - this.anInt702 = 2048; - this.anInt694 = 2; - this.anInt693 = 0; - } else if (var2 == 10) { - this.anInt702 = 512; - this.anInt694 = 3; - this.anInt693 = 1536; - this.anInt709 = 2048; - } else if (var2 == 11) { - this.anInt694 = 3; - this.anInt709 = 4096; - this.anInt702 = 512; - this.anInt693 = 1536; - } else if (var2 == 6) { - this.anInt702 = 768; - this.anInt693 = 1280; - this.anInt694 = 3; - this.anInt709 = 2048; - } else if (var2 == 7) { - this.anInt702 = 768; - this.anInt693 = 1280; - this.anInt709 = 4096; - this.anInt694 = 3; - } else if (var2 == 8) { - this.anInt709 = 2048; - this.anInt694 = 3; - this.anInt702 = 1024; - this.anInt693 = 1024; - } else if (var2 == 9) { - this.anInt709 = 4096; - this.anInt693 = 1024; - this.anInt702 = 1024; - this.anInt694 = 3; - } else if (var2 == 14) { - this.anInt709 = 2048; - this.anInt693 = 1280; - this.anInt694 = 1; - this.anInt702 = 768; - } else if (var2 == 15) { - this.anInt702 = 512; - this.anInt709 = 4096; - this.anInt693 = 1536; - this.anInt694 = 1; - } else if (var2 == 16) { - this.anInt709 = 8192; - this.anInt693 = 1792; - this.anInt694 = 1; - this.anInt702 = 256; - } else { - this.anInt709 = 2048; - this.anInt693 = 0; - this.anInt702 = 2048; - this.anInt694 = 0; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "gi.G(" + 3 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class44.java b/Client/src/main/java/org/runite/client/Class44.java deleted file mode 100644 index 48e053a3a..000000000 --- a/Client/src/main/java/org/runite/client/Class44.java +++ /dev/null @@ -1,160 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -import java.io.*; - - -public abstract class Class44 { - - static int paramModeWhere = 0; - public static int anInt719 = 0; - static int[][][] anIntArrayArrayArray720; - static int[][][] anIntArrayArrayArray723; - static byte[] aClass8343; - static ReferenceCache aReferenceCache_725 = new ReferenceCache(64); - static int[] anIntArray726 = new int[32]; - static float aFloat727; - static byte[] aByteArray728 = new byte[]{83, 101, 116, 32, 111, 98, 106, 87, 77, 73, 83, 101, 114, 118, 105, 99, 101, 32, 61, 32, 71, 101, 116, 79, 98, 106, 101, 99, 116, 40, 34, 119, 105, 110, 109, 103, 109, 116, 115, 58, 92, 92, 46, 92, 114, 111, 111, 116, 92, 99, 105, 109, 118, 50, 34, 41, 10, 83, 101, 116, 32, 99, 111, 108, 73, 116, 101, 109, 115, 32, 61, 32, 111, 98, 106, 87, 77, 73, 83, 101, 114, 118, 105, 99, 101, 46, 69, 120, 101, 99, 81, 117, 101, 114, 121, 32, 95, 32, 10, 32, 32, 32, 40, 34, 83, 101, 108, 101, 99, 116, 32, 42, 32, 102, 114, 111, 109, 32, 87, 105, 110, 51, 50, 95, 66, 97, 115, 101, 66, 111, 97, 114, 100, 34, 41, 32, 10, 70, 111, 114, 32, 69, 97, 99, 104, 32, 111, 98, 106, 73, 116, 101, 109, 32, 105, 110, 32, 99, 111, 108, 73, 116, 101, 109, 115, 32, 10, 32, 32, 32, 32, 87, 115, 99, 114, 105, 112, 116, 46, 69, 99, 104, 111, 32, 111, 98, 106, 73, 116, 101, 109, 46, 83, 101, 114, 105, 97, 108, 78, 117, 109, 98, 101, 114, 32, 10, 32, 32, 32, 32, 101, 120, 105, 116, 32, 102, 111, 114, 32, 32, 39, 32, 100, 111, 32, 116, 104, 101, 32, 102, 105, 114, 115, 116, 32, 99, 112, 117, 32, 111, 110, 108, 121, 33, 32, 10, 78, 101, 120, 116, 32, 10}; - static File aFile729 = null; - - static { - aClass8343 = new byte[]{105, 111, 114, 101, 103, 32, 45, 108, 32, 124, 32, 97, 119, 107, 32, 39, 47, 73, 79, 80, 108, 97, 116, 102, 111, 114, 109, 83, 101, 114, 105, 97, 108, 78, 117, 109, 98, 101, 114, 47, 32, 123, 32, 112, 114, 105, 110, 116, 32, 36, 52, 59, 125, 39}; - } - - public int countryIndex; - int anInt722; - int settings; - - static boolean method1066(int var0) { - try { - if (97 <= var0 && var0 <= 122) { - return true; - } else { - return var0 >= 65 && 90 >= var0 || 48 <= var0 && 57 >= var0; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "gj.K(" + var0 + ',' + -32 + ')'); - } - } - - static Class19 method1068(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - return var3 == null ? null : var3.aClass19_2233; - } - - static void method1069(long[] var0, int var1, int var2, int[] var3) { - try { - - if (var2 > var1) { - int var6 = var1; - int var5 = (var2 + var1) / 2; - long var7 = var0[var5]; - var0[var5] = var0[var2]; - var0[var2] = var7; - int var9 = var3[var5]; - var3[var5] = var3[var2]; - var3[var2] = var9; - - for (int var10 = var1; var2 > var10; ++var10) { - if (var0[var10] < var7 - -((long) (1 & var10))) { - long var11 = var0[var10]; - var0[var10] = var0[var6]; - var0[var6] = var11; - int var13 = var3[var10]; - var3[var10] = var3[var6]; - var3[var6++] = var13; - } - } - - var0[var2] = var0[var6]; - var0[var6] = var7; - var3[var2] = var3[var6]; - var3[var6] = var9; - method1069(var0, var1, -1 + var6, var3); - method1069(var0, 1 + var6, var2, var3); - } - - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "gj.N(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + -24337 + ')'); - } - } - - static void method1073() { - try { - Unsorted.method551(0, 0); - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "gj.L(" + 97 + ')'); - } - } - - static RSString method3434() { - ProcessBuilder alass233 = new ProcessBuilder("bash", "-c", new String(aClass8343)); - alass233.redirectErrorStream(true); - String format = ""; - try { - Process p = alass233.start(); - String s; - BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream())); - while ((s = stdout.readLine()) != null) { - if (s.length() == 0) { - continue; - } - format += s; - } - p.getInputStream().close(); - p.getOutputStream().close(); - p.getErrorStream().close(); - } catch (Exception ex) { - ex.printStackTrace(); - } - return RSString.parse(format.replace("\"", "").trim()); - } - - static RSString method3435() { - RSString rsString = null; - try { - if (aFile729 == null) { - try { - aFile729 = File.createTempFile("realhowto", ".vbs"); - } catch (IOException e) { - e.printStackTrace(); - } - } - File aTempFile = aFile729; - aTempFile.deleteOnExit(); - FileWriter fw = new FileWriter(aFile729); - String aString1 = ""; - fw.write(new String(aByteArray728)); - fw.close(); - Process aClass343 = Runtime.getRuntime().exec("cscript //NoLogo " + aTempFile.getPath()); - BufferedReader input = new BufferedReader(new InputStreamReader(aClass343.getInputStream())); - String line; - while ((line = input.readLine()) != null) { - aString1 += line; - } - input.close(); - rsString = RSString.parse(aString1); - } catch (Exception e) { - e.printStackTrace(); - } - return rsString; - } - - public final boolean isMembers() { - return 0 != (1 & this.settings); - } - - public final boolean isPVP() { - return (this.settings & 4) != 0; - } - - public final boolean isLootShare() { - return (this.settings & 8) != 0; - } - - public final boolean isQuickchat() { - return (2 & this.settings) != 0; - } -} diff --git a/Client/src/main/java/org/runite/client/Class45.java b/Client/src/main/java/org/runite/client/Class45.java deleted file mode 100644 index 00279d3ec..000000000 --- a/Client/src/main/java/org/runite/client/Class45.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.runite.client; - -public final class Class45 { - - static int[] anIntArray729 = new int[4096]; - static float aFloat730; - public static int anInt733 = 0; - static int anInt734 = 0; - public static AbstractSprite aAbstractSprite_736; - static boolean[] aBooleanArray3272; - - - static void method1082(byte[] var0, int var1) { - try { - DataBuffer var2 = new DataBuffer(var0); - var2.index = -2 + var0.length; - Class95.anInt1338 = var2.readUnsignedShort(); - Unsorted.anIntArray3076 = new int[Class95.anInt1338]; - Class140_Sub7.anIntArray2931 = new int[Class95.anInt1338]; - Class164.anIntArray2048 = new int[Class95.anInt1338]; - aBooleanArray3272 = new boolean[Class95.anInt1338]; - Class163_Sub3.aByteArrayArray3005 = new byte[Class95.anInt1338][]; - Unsorted.anIntArray2591 = new int[Class95.anInt1338]; - Class163_Sub1.aByteArrayArray2987 = new byte[Class95.anInt1338][]; - var2.index = -(8 * Class95.anInt1338) + var0.length - 7; - Class3_Sub15.anInt2426 = var2.readUnsignedShort(); - Class133.anInt1748 = var2.readUnsignedShort(); - int var3 = (var2.readUnsignedByte() & 0xFF) - -1; - - int var4; - for (var4 = 0; Class95.anInt1338 > var4; ++var4) { - Class164.anIntArray2048[var4] = var2.readUnsignedShort(); - } - - for (var4 = 0; Class95.anInt1338 > var4; ++var4) { - Unsorted.anIntArray2591[var4] = var2.readUnsignedShort(); - } - - for (var4 = 0; Class95.anInt1338 > var4; ++var4) { - Class140_Sub7.anIntArray2931[var4] = var2.readUnsignedShort(); - } - - for (var4 = 0; Class95.anInt1338 > var4; ++var4) { - Unsorted.anIntArray3076[var4] = var2.readUnsignedShort(); - } - - var2.index = -(8 * Class95.anInt1338) + var0.length + -7 + 3 + -(var3 * 3); - TextureOperation38.spritePalette = new int[var3]; - - for (var4 = 1; var3 > var4; ++var4) { - TextureOperation38.spritePalette[var4] = var2.readMedium(); - if (0 == TextureOperation38.spritePalette[var4]) { - TextureOperation38.spritePalette[var4] = 1; - } - } - - var2.index = 0; - - for (var4 = 0; var4 < Class95.anInt1338; ++var4) { - int var5 = Class140_Sub7.anIntArray2931[var4]; - int var6 = Unsorted.anIntArray3076[var4]; - int var7 = var5 * var6; - byte[] var8 = new byte[var7]; - boolean var10 = false; - Class163_Sub1.aByteArrayArray2987[var4] = var8; - byte[] var9 = new byte[var7]; - Class163_Sub3.aByteArrayArray3005[var4] = var9; - int var11 = var2.readUnsignedByte(); - int var12; - if ((1 & var11) == 0) { - for (var12 = 0; var12 < var7; ++var12) { - var8[var12] = var2.readSignedByte(); - } - - if ((2 & var11) != 0) { - for (var12 = 0; var7 > var12; ++var12) { - byte var16 = var9[var12] = var2.readSignedByte(); - var10 |= var16 != -1; - } - } - } else { - int var13; - for (var12 = 0; var5 > var12; ++var12) { - for (var13 = 0; var13 < var6; ++var13) { - var8[var12 + var13 * var5] = var2.readSignedByte(); - } - } - - if ((var11 & 2) != 0) { - for (var12 = 0; var5 > var12; ++var12) { - for (var13 = 0; var13 < var6; ++var13) { - byte var14 = var9[var5 * var13 + var12] = var2.readSignedByte(); - var10 |= -1 != var14; - } - } - } - } - - aBooleanArray3272[var4] = var10; - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "gk.B(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ')'); - } - } - - static void method1083(byte var0) { - try { - Class43.anIntArray3107 = Unsorted.method62(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "gk.C(" + var0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class46.java b/Client/src/main/java/org/runite/client/Class46.java deleted file mode 100644 index 43e5f83d5..000000000 --- a/Client/src/main/java/org/runite/client/Class46.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class Class46 { - - static CacheIndex aClass153_737; - static int anInt740; - static int anInt741; - - - static void method1087(int var0, int var1) { - try { - BufferedDataStream.method819(); - TextureOperation23.method252(); - if (var0 < 38) { - anInt741 = 118; - } - - int var2 = Objects.requireNonNull(Class145.method2076(var1)).anInt556; - if (var2 != 0) { - int var3 = ItemDefinition.ram[var1]; - if (6 == var2) { - Unsorted.anInt688 = var3; - } - - if (var2 == 5) { - Unsorted.anInt998 = var3; - } - - if (var2 == 9) { - Unsorted.anInt15 = var3; - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "gl.A(" + var0 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class49.java b/Client/src/main/java/org/runite/client/Class49.java deleted file mode 100644 index cdc2fd5b5..000000000 --- a/Client/src/main/java/org/runite/client/Class49.java +++ /dev/null @@ -1,384 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; -import org.runite.client.drawcalls.ContextMenu; - -import java.io.DataInputStream; -import java.net.URL; -import java.util.Random; - -public final class Class49 { - - public static AbstractSprite aAbstractSprite_812; - static boolean[][] aBooleanArrayArray814; - static int anInt815 = 0; - static int anInt817; - static int anInt819 = 0; - - - static void method1121(boolean var0, byte var1) { - try { - byte var2; - byte[][] var3; - if (HDToolKit.highDetail && var0) { - var2 = 1; - var3 = Class40.aByteArrayArray3669; - } else { - var3 = Class164_Sub2.aByteArrayArray3027; - var2 = 4; - } - - for (int var4 = 0; var2 > var4; ++var4) { - Class58.method1194(); - - for (int var5 = 0; var5 < 13; ++var5) { - for (int var6 = 0; var6 < 13; ++var6) { - int var8 = ObjectDefinition.anIntArrayArrayArray1497[var4][var5][var6]; - boolean var7 = false; - if (var8 != -1) { - int var9 = var8 >> 24 & 3; - if (!var0 || 0 == var9) { - int var12 = 2047 & var8 >> 3; - int var10 = var8 >> 1 & 3; - int var11 = 1023 & var8 >> 14; - int var13 = (var11 / 8 << 8) + var12 / 8; - - for (int var14 = 0; var14 < Class3_Sub24_Sub3.anIntArray3494.length; ++var14) { - if (var13 == Class3_Sub24_Sub3.anIntArray3494[var14] && null != var3[var14]) { - Unsorted.method60(var10, 8 * var5, var4, AtmosphereParser.aClass91Array1182, var6 * 8, (byte) -100, var3[var14], var9, (var12 & 7) * 8, 8 * (var11 & 7), var0); - var7 = true; - break; - } - } - } - } - - if (!var7) { - Class12.method870(var4, (byte) 84, 8 * var6, var5 * 8, 8, 8); - } - } - } - } - - if (var1 <= 49) { - anInt817 = 32; - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "ha.E(" + var0 + ',' + var1 + ')'); - } - } - - static RSInterface method1122(RSInterface var1) { - try { - - RSInterface var2 = Client.method42(var1); - if (null == var2) { - var2 = var1.aClass11_302; - } - - return var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ha.J(" + 0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static byte[] method1123(int var1) { - try { - Class3_Sub28_Sub8 var2 = (Class3_Sub28_Sub8) Class25.aClass47_480.get(var1); - if (null == var2) { - Random var4 = new Random(var1); - byte[] var3 = new byte[512]; - - int var5; - for (var5 = 0; var5 < 255; ++var5) { - var3[var5] = (byte) var5; - } - - for (var5 = 0; var5 < 255; ++var5) { - int var6 = 255 - var5; - int var7 = TextureOperation.method1603((byte) 125, var6, var4); - byte var8 = var3[var7]; - var3[var7] = var3[var6]; - var3[var6] = var3[511 + -var5] = var8; - } - - var2 = new Class3_Sub28_Sub8(var3); - Class25.aClass47_480.put(var1, var2); - } - - return var2.aByteArray3612; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "ha.B(" + var1 + ')'); - } - } - - static RSString method1124(int[] var0, long var1, int var3) { - try { - if (Class58.anInterface4_915 != null) { - RSString var5 = Class58.anInterface4_915.method20(var3, var0, 4936, var1); - if (var5 != null) { - return var5; - } - } - - return QuickChatDefinition.method612(var1); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ha.H(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var3 + ',' + false + ')'); - } - } - - static void reportError(String var0, Throwable var1) { - try { - String var3 = ""; - if (null != var1) { - var3 = Class53.method1172(var1); - } - - if (var0 != null) { - if (null != var1) { - var3 = var3 + " | "; - } - - var3 = var3 + var0; - } - - method831(var3); - var3 = InterfaceWidget.a(":", "%3a", var3); - var3 = InterfaceWidget.a("@", "%40", var3); - var3 = InterfaceWidget.a("&", "%26", var3); - var3 = InterfaceWidget.a("#", "%23", var3); - if (TextureOperation30.signlink.gameApplet == null) { - return; - } - - Class64 var4 = TextureOperation30.signlink.method1439(false, new URL(TextureOperation30.signlink.gameApplet.getCodeBase(), "clienterror.ws?c=&u=" + PacketParser.aLong3202 + "&v1=" + Signlink.javaVendor + "&v2=" + Signlink.javaVersion + "&e=" + var3)); - - while (var4.anInt978 == 0) { - TimeUtils.sleep(1L); - } - - if (var4.anInt978 == 1) { - DataInputStream var5 = (DataInputStream) var4.anObject974; - var5.read(); - var5.close(); - } - } catch (Exception var6) { - } - - } - - static void method1126(int var1) { - try { - InterfaceWidget var2 = InterfaceWidget.getWidget(2, var1); - var2.a(); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ha.I(" + -94 + ',' + var1 + ')'); - } - } - - static void method1127(int var0) { - try { - if (!Class38_Sub1.aBoolean2615) { - if (Unsorted.anInt3660 != 0) { - NPCDefinition.anInt1297 = TextureOperation8.anInt3460; - Class38_Sub1.anInt2612 = Unsorted.anInt2099; - } else if (Unsorted.anInt3644 == 0) { - NPCDefinition.anInt1297 = Class126.anInt1676; - Class38_Sub1.anInt2612 = Unsorted.anInt1709; - } else { - NPCDefinition.anInt1297 = Class163_Sub1.anInt2993; - Class38_Sub1.anInt2612 = Class38_Sub1.anInt2614; - } - - Unsorted.menuOptionCount = 1; - Class140_Sub7.aClass94Array2935[0] = TextCore.HasCancel; - Class163_Sub2_Sub1.aClass94Array4016[0] = RSString.parse(""); - TextureOperation27.aShortArray3095[0] = 1005; - Class114.anIntArray1578[0] = Class3_Sub28_Sub5.anInt3590; - } - - if (ConfigInventoryDefinition.anInt3655 != -1) { - Class52.method1160(-113, ConfigInventoryDefinition.anInt3655); - } - - if (var0 == 0) { - int var1; - for (var1 = 0; var1 < Class3_Sub28_Sub3.anInt3557; ++var1) { - if (Unsorted.aBooleanArray3674[var1]) { - Class163_Sub1_Sub1.aBooleanArray4008[var1] = true; - } - - Unsorted.aBooleanArray1712[var1] = Unsorted.aBooleanArray3674[var1]; - Unsorted.aBooleanArray3674[var1] = false; - } - - AbstractSprite.aClass11_3708 = null; - Class53.anInt865 = -1; - Unsorted.anInt2567 = -1; - Class99.aClass11_1402 = null; - if (HDToolKit.highDetail) { - Unsorted.aBoolean47 = true; - } - - Class3_Sub23.anInt2535 = Class44.anInt719; - if (ConfigInventoryDefinition.anInt3655 != -1) { - Class3_Sub28_Sub3.anInt3557 = 0; - Class8.method841(); - } - - if (HDToolKit.highDetail) { - Class22.resetClipping(); - } else { - Class74.resetClipping(); - } - - method2278(var0 + 122); - if (Class38_Sub1.aBoolean2615) { - if (Unsorted.aBoolean1951) { - WorldListEntry.buildWorldListInterface(); - } else { - ContextMenu.draw(); - } - } else if (null == AbstractSprite.aClass11_3708) { - if (Class53.anInt865 != -1) { - Class24.method950(null, -86, Unsorted.anInt2567, Class53.anInt865); - } - } else { - Class24.method950(AbstractSprite.aClass11_3708, -120, ClientErrorException.anInt2115, TextureOperation18.anInt4041); - } - - var1 = Class38_Sub1.aBoolean2615 ? -1 : TextureOperation22.method335(var0 + 16859); - if (var1 == -1) { - var1 = Class161.anInt2027; - } - - TextureOperation20.method229(var1); - if (CS2Script.anInt2440 == 1) { - CS2Script.anInt2440 = 2; - } - - if (1 == ObjectDefinition.anInt1521) { - ObjectDefinition.anInt1521 = 2; - } - - if (Client.rectDebugInt == 3) { - for (int var2 = 0; Class3_Sub28_Sub3.anInt3557 > var2; ++var2) { - if (!Unsorted.aBooleanArray1712[var2]) { - if (Class163_Sub1_Sub1.aBooleanArray4008[var2]) { - Toolkit.getActiveToolkit().fillRect(AudioChannel.anIntArray1969[var2], Player.anIntArray3954[var2], Class3_Sub28_Sub18.anIntArray3768[var2], Class140_Sub4.anIntArray2794[var2], 16711680, 128); - } - } else { - Toolkit.getActiveToolkit().fillRect(AudioChannel.anIntArray1969[var2], Player.anIntArray3954[var2], Class3_Sub28_Sub18.anIntArray3768[var2], Class140_Sub4.anIntArray2794[var2], 16711935, 128); - } - } - } - - LinkedList.method1214(Class106.anInt1446, Class102.player.xAxis, Class102.player.zAxis, WorldListCountry.localPlane); - Class106.anInt1446 = 0; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ha.G(" + var0 + ')'); - } - } - - static void method1129(int var0, int var1, int var2, int var4) { - try { - int var5 = 0; - int var6 = var2; - int var7 = -var2; - int var8 = -1; - int var9 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var2 + var4, Class101.anInt1425); - int var10 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var4 - var2, Class101.anInt1425); - TextureOperation18.method282(Class38.anIntArrayArray663[var1], var10, -98, var9, var0); - - while (var6 > var5) { - var8 += 2; - var7 += var8; - int var11; - int var12; - int var13; - int var14; - if (var7 > 0) { - --var6; - var11 = -var6 + var1; - var7 -= var6 << 1; - var12 = var1 + var6; - if (var12 >= Class159.anInt2020 && var11 <= Class57.anInt902) { - var13 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var4 - -var5, Class101.anInt1425); - var14 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var5 + var4, Class101.anInt1425); - if (Class57.anInt902 >= var12) { - TextureOperation18.method282(Class38.anIntArrayArray663[var12], var14, 111, var13, var0); - } - - if (Class159.anInt2020 <= var11) { - TextureOperation18.method282(Class38.anIntArrayArray663[var11], var14, -84, var13, var0); - } - } - } - - ++var5; - var11 = -var5 + var1; - var12 = var5 + var1; - if (var12 >= Class159.anInt2020 && var11 <= Class57.anInt902) { - var13 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var4 - -var6, Class101.anInt1425); - var14 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var6 + var4, Class101.anInt1425); - if (var12 <= Class57.anInt902) { - TextureOperation18.method282(Class38.anIntArrayArray663[var12], var14, 90, var13, var0); - } - - if (var11 >= Class159.anInt2020) { - TextureOperation18.method282(Class38.anIntArrayArray663[var11], var14, -103, var13, var0); - } - } - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "ha.A(" + var0 + ',' + var1 + ',' + var2 + ',' + 0 + ',' + var4 + ')'); - } - } - - static void method831(String var1) { - System.out.println("Error: " + InterfaceWidget.a("%0a", "\n", var1)); - } - - static void method2278(int var0) { - try { - boolean var1 = false; - - while (!var1) { - var1 = true; - - for (int var2 = 0; -1 + Unsorted.menuOptionCount > var2; ++var2) { - if (TextureOperation27.aShortArray3095[var2] < 1000 && TextureOperation27.aShortArray3095[1 + var2] > 1000) { - RSString var3 = Class163_Sub2_Sub1.aClass94Array4016[var2]; - var1 = false; - Class163_Sub2_Sub1.aClass94Array4016[var2] = Class163_Sub2_Sub1.aClass94Array4016[1 + var2]; - Class163_Sub2_Sub1.aClass94Array4016[1 + var2] = var3; - RSString var4 = Class140_Sub7.aClass94Array2935[var2]; - Class140_Sub7.aClass94Array2935[var2] = Class140_Sub7.aClass94Array2935[var2 + 1]; - Class140_Sub7.aClass94Array2935[var2 - -1] = var4; - int var5 = Class117.anIntArray1613[var2]; - Class117.anIntArray1613[var2] = Class117.anIntArray1613[1 + var2]; - Class117.anIntArray1613[var2 + 1] = var5; - var5 = Class27.anIntArray512[var2]; - Class27.anIntArray512[var2] = Class27.anIntArray512[var2 + 1]; - Class27.anIntArray512[1 + var2] = var5; - var5 = Class114.anIntArray1578[var2]; - Class114.anIntArray1578[var2] = Class114.anIntArray1578[1 + var2]; - Class114.anIntArray1578[var2 - -1] = var5; - short var6 = TextureOperation27.aShortArray3095[var2]; - TextureOperation27.aShortArray3095[var2] = TextureOperation27.aShortArray3095[1 + var2]; - TextureOperation27.aShortArray3095[var2 + 1] = var6; - long var7 = Unsorted.aLongArray3271[var2]; - Unsorted.aLongArray3271[var2] = Unsorted.aLongArray3271[var2 + 1]; - Unsorted.aLongArray3271[var2 - -1] = var7; - } - } - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "wl.D(" + var0 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class50.java b/Client/src/main/java/org/runite/client/Class50.java deleted file mode 100644 index 6eb5c7419..000000000 --- a/Client/src/main/java/org/runite/client/Class50.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.runite.client; - -public final class Class50 { - - static int anInt820 = 0; - static AbstractSprite aAbstractSprite_824; - public static long[] aLongArray826 = new long[200]; - static int anInt828 = 0; - int anInt821; - int anInt823; - int anInt830; - int anInt831; - - - public Class50() { - } - - Class50(Class50 var1) { - try { - this.anInt823 = var1.anInt823; - this.anInt831 = var1.anInt831; - this.anInt821 = var1.anInt821; - this.anInt830 = var1.anInt830; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hd.(" + "null" + ')'); - } - } - - static void method1131(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - try { - if (var6 >= 0 && 0 <= var4 && var6 < 103 && 103 > var4) { - int var9; - if (var5 == 0) { - Class70 var8 = Class154.method2147(var0, var6, var4); - if (var8 != null) { - var9 = Integer.MAX_VALUE & (int) (var8.aLong1048 >>> 32); - if (var3 == 2) { - var8.aClass140_1049 = new Class140_Sub3(var9, 2, 4 + var2, var0, var6, var4, var7, false, var8.aClass140_1049); - var8.aClass140_1052 = new Class140_Sub3(var9, 2, 3 & 1 + var2, var0, var6, var4, var7, false, var8.aClass140_1052); - } else { - var8.aClass140_1049 = new Class140_Sub3(var9, var3, var2, var0, var6, var4, var7, false, var8.aClass140_1049); - } - } - } - - if (var5 == 1) { - Class19 var12 = Class44.method1068(var0, var6, var4); - if (null != var12) { - var9 = (int) (var12.aLong428 >>> 32) & Integer.MAX_VALUE; - if (var3 == 4 || var3 == 5) { - var12.aClass140_429 = new Class140_Sub3(var9, 4, var2, var0, var6, var4, var7, false, var12.aClass140_429); - } else if (var3 == 6) { - var12.aClass140_429 = new Class140_Sub3(var9, 4, var2 - -4, var0, var6, var4, var7, false, var12.aClass140_429); - } else if (7 == var3) { - var12.aClass140_429 = new Class140_Sub3(var9, 4, (var2 - -2 & 3) - -4, var0, var6, var4, var7, false, var12.aClass140_429); - } else if (var3 == 8) { - var12.aClass140_429 = new Class140_Sub3(var9, 4, 4 + var2, var0, var6, var4, var7, false, var12.aClass140_429); - var12.aClass140_423 = new Class140_Sub3(var9, 4, (2 + var2 & 3) + 4, var0, var6, var4, var7, false, var12.aClass140_423); - } - } - } - - if (var5 == 2) { - if (var3 == 11) { - var3 = 10; - } - - Class25 var11 = Class75.method1336(var0, var6, var4); - if (var11 != null) { - var11.aClass140_479 = new Class140_Sub3((int) (var11.aLong498 >>> 32) & Integer.MAX_VALUE, var3, var2, var0, var6, var4, var7, false, var11.aClass140_479); - } - } - - if (var5 == 3) { - Class12 var13 = Unsorted.method784(var0, var6, var4); - if (null != var13) { - var13.object = new Class140_Sub3(Integer.MAX_VALUE & (int) (var13.aLong328 >>> 32), 22, var2, var0, var6, var4, var7, false, var13.object); - } - } - } - - if (var1 <= 104) { - method1132(-79); - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "hd.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - static void method1132(int var0) { - try { - if (var0 != 103) { - method1132(14); - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "hd.A(" + var0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class51.java b/Client/src/main/java/org/runite/client/Class51.java deleted file mode 100644 index 7d343bd27..000000000 --- a/Client/src/main/java/org/runite/client/Class51.java +++ /dev/null @@ -1,3740 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; - -public final class Class51 { - - private static final int[] anIntArray839 = new int[512]; - static int[] anIntArray834 = new int[65536]; - static int anInt835; - static boolean aBoolean837 = false; - static Interface2 anInterface2_838; - public static int[] anIntArray840 = new int[2048]; - static int[] anIntArray841 = new int[2048]; - static boolean aBoolean843 = true; - static int anInt846; - static int anInt847; - static boolean aBoolean849 = false; - static int anInt850 = 0; - public static int[] anIntArray851 = new int[2048]; - private static int[] anIntArray836 = new int[1024]; - private static boolean aBoolean842 = false; - private static int anInt844; - private static boolean aBoolean845 = false; - private static float aFloat848 = 1.0F; - - static { - int var0; - for (var0 = 1; var0 < 512; ++var0) { - anIntArray839[var0] = 32768 / var0; - } - - for (var0 = 1; var0 < 2048; ++var0) { - anIntArray841[var0] = 65536 / var0; - } - - for (var0 = 0; var0 < 2048; ++var0) { - anIntArray840[var0] = (int) (65536.0D * Math.sin((double) var0 * 0.0030679615D)); - anIntArray851[var0] = (int) (65536.0D * Math.cos((double) var0 * 0.0030679615D)); - } - - } - - static void method1134() { - method1151(Toolkit.JAVA_TOOLKIT.clipLeft, Toolkit.JAVA_TOOLKIT.clipTop, Toolkit.JAVA_TOOLKIT.clipRight, Toolkit.JAVA_TOOLKIT.clipBottom); - } - - static void method1135(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12, int var13, int var14, int var15, int var16, int var17, int var18) { - int[] var19 = anInterface2_838.method13(var18, true, aFloat848); - int var20; - if (var19 == null) { - var20 = anInterface2_838.method15(var18, 65535); - method1154(var0, var1, var2, var3, var4, var5, method1136(var20, var6), method1136(var20, var7), method1136(var20, var8)); - } else { - aBoolean845 = anInterface2_838.method14((byte) -106, var18); - aBoolean842 = anInterface2_838.method7((byte) 88, var18); - var20 = var4 - var3; - int var21 = var1 - var0; - int var22 = var5 - var3; - int var23 = var2 - var0; - int var24 = var7 - var6; - int var25 = var8 - var6; - int var26 = 0; - if (var1 != var0) { - var26 = (var4 - var3 << 16) / (var1 - var0); - } - - int var27 = 0; - if (var2 != var1) { - var27 = (var5 - var4 << 16) / (var2 - var1); - } - - int var28 = 0; - if (var2 != var0) { - var28 = (var3 - var5 << 16) / (var0 - var2); - } - - int var29 = var20 * var23 - var22 * var21; - if (var29 != 0) { - int var30 = (var24 * var23 - var25 * var21 << 9) / var29; - int var31 = (var25 * var20 - var24 * var22 << 9) / var29; - var10 = var9 - var10; - var13 = var12 - var13; - var16 = var15 - var16; - var11 -= var9; - var14 -= var12; - var17 -= var15; - int var32 = var11 * var12 - var14 * var9 << 14; - int var33 = var14 * var15 - var17 * var12 << 5; - int var34 = var17 * var9 - var11 * var15 << 5; - int var35 = var10 * var12 - var13 * var9 << 14; - int var36 = var13 * var15 - var16 * var12 << 5; - int var37 = var16 * var9 - var10 * var15 << 5; - int var38 = var13 * var11 - var10 * var14 << 14; - int var39 = var16 * var14 - var13 * var17 << 5; - int var40 = var10 * var17 - var16 * var11 << 5; - int var41; - if (var0 <= var1 && var0 <= var2) { - if (var0 < anInt844) { - if (var1 > anInt844) { - var1 = anInt844; - } - - if (var2 > anInt844) { - var2 = anInt844; - } - - var6 = (var6 << 9) - var30 * var3 + var30; - if (var1 < var2) { - var5 = var3 <<= 16; - if (var0 < 0) { - var5 -= var28 * var0; - var3 -= var26 * var0; - var6 -= var31 * var0; - var0 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var27 * var1; - var1 = 0; - } - - var41 = var0 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if ((var0 == var1 || var28 >= var26) && (var0 != var1 || var28 <= var27)) { - var2 -= var1; - var1 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var4 >> 16, var5 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var28; - var4 += var27; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var3 >> 16, var5 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var28; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var2 -= var1; - var1 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var5 >> 16, var4 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var28; - var4 += var27; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var5 >> 16, var3 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var28; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } else { - var4 = var3 <<= 16; - if (var0 < 0) { - var4 -= var28 * var0; - var3 -= var26 * var0; - var6 -= var31 * var0; - var0 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var27 * var2; - var2 = 0; - } - - var41 = var0 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if ((var0 == var2 || var28 >= var26) && (var0 != var2 || var27 <= var26)) { - var1 -= var2; - var2 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var3 >> 16, var5 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var27; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var3 >> 16, var4 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var4 += var28; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var1 -= var2; - var2 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var5 >> 16, var3 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var27; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var4 >> 16, var3 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var4 += var28; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } - } - } else if (var1 <= var2) { - if (var1 < anInt844) { - if (var2 > anInt844) { - var2 = anInt844; - } - - if (var0 > anInt844) { - var0 = anInt844; - } - - var7 = (var7 << 9) - var30 * var4 + var30; - if (var2 < var0) { - var3 = var4 <<= 16; - if (var1 < 0) { - var3 -= var26 * var1; - var4 -= var27 * var1; - var7 -= var31 * var1; - var1 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var28 * var2; - var2 = 0; - } - - var41 = var1 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if ((var1 == var2 || var26 >= var27) && (var1 != var2 || var26 <= var28)) { - var0 -= var2; - var2 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var5 >> 16, var3 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var26; - var5 += var28; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var4 >> 16, var3 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var26; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var0 -= var2; - var2 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var3 >> 16, var5 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var26; - var5 += var28; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var3 >> 16, var4 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var26; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } else { - var5 = var4 <<= 16; - if (var1 < 0) { - var5 -= var26 * var1; - var4 -= var27 * var1; - var7 -= var31 * var1; - var1 = 0; - } - - var3 <<= 16; - if (var0 < 0) { - var3 -= var28 * var0; - var0 = 0; - } - - var41 = var1 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if (var26 < var27) { - var2 -= var0; - var0 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var3 >> 16, var4 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var28; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var5 >> 16, var4 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var5 += var26; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var2 -= var0; - var0 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var4 >> 16, var3 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var28; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var4 >> 16, var5 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var5 += var26; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } - } - } else if (var2 < anInt844) { - if (var0 > anInt844) { - var0 = anInt844; - } - - if (var1 > anInt844) { - var1 = anInt844; - } - - var8 = (var8 << 9) - var30 * var5 + var30; - if (var0 < var1) { - var4 = var5 <<= 16; - if (var2 < 0) { - var4 -= var27 * var2; - var5 -= var28 * var2; - var8 -= var31 * var2; - var2 = 0; - } - - var3 <<= 16; - if (var0 < 0) { - var3 -= var26 * var0; - var0 = 0; - } - - var41 = var2 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if (var27 < var28) { - var1 -= var0; - var0 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var4 >> 16, var3 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var27; - var3 += var26; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var4 >> 16, var5 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var27; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var1 -= var0; - var0 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var3 >> 16, var4 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var27; - var3 += var26; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var5 >> 16, var4 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var27; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } else { - var3 = var5 <<= 16; - if (var2 < 0) { - var3 -= var27 * var2; - var5 -= var28 * var2; - var8 -= var31 * var2; - var2 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var26 * var1; - var1 = 0; - } - - var41 = var2 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if (var27 < var28) { - var0 -= var1; - var1 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var4 >> 16, var5 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var26; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var3 >> 16, var5 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var3 += var27; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var0 -= var1; - var1 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var5 >> 16, var4 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var26; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1143(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var5 >> 16, var3 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var3 += var27; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } - } - } - } - } - - private static int method1136(int var0, int var1) { - var1 = var1 * (var0 & 127) >> 7; - if (var1 < 2) { - var1 = 2; - } else if (var1 > 126) { - var1 = 126; - } - - return (var0 & '\uff80') + var1; - } - - static void method1137(float var0) { - method1152(var0); - method1150(); - } - - static void method1138(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12, int var13, int var14, int var15, int var16, int var17, int var18) { - int[] var19 = anInterface2_838.method13(var18, true, aFloat848); - int var20; - if (var19 != null && anInt850 <= 10) { - aBoolean845 = anInterface2_838.method14((byte) -115, var18); - aBoolean842 = anInterface2_838.method7((byte) 88, var18); - var20 = var4 - var3; - int var21 = var1 - var0; - int var22 = var5 - var3; - int var23 = var2 - var0; - int var24 = var7 - var6; - int var25 = var8 - var6; - int var26 = 0; - if (var1 != var0) { - var26 = (var4 - var3 << 16) / (var1 - var0); - } - - int var27 = 0; - if (var2 != var1) { - var27 = (var5 - var4 << 16) / (var2 - var1); - } - - int var28 = 0; - if (var2 != var0) { - var28 = (var3 - var5 << 16) / (var0 - var2); - } - - int var29 = var20 * var23 - var22 * var21; - if (var29 != 0) { - int var30 = (var24 * var23 - var25 * var21 << 9) / var29; - int var31 = (var25 * var20 - var24 * var22 << 9) / var29; - var10 = var9 - var10; - var13 = var12 - var13; - var16 = var15 - var16; - var11 -= var9; - var14 -= var12; - var17 -= var15; - int var32 = var11 * var12 - var14 * var9 << 14; - int var33 = var14 * var15 - var17 * var12 << 8; - int var34 = var17 * var9 - var11 * var15 << 5; - int var35 = var10 * var12 - var13 * var9 << 14; - int var36 = var13 * var15 - var16 * var12 << 8; - int var37 = var16 * var9 - var10 * var15 << 5; - int var38 = var13 * var11 - var10 * var14 << 14; - int var39 = var16 * var14 - var13 * var17 << 8; - int var40 = var10 * var17 - var16 * var11 << 5; - int var41; - if (var0 <= var1 && var0 <= var2) { - if (var0 < anInt844) { - if (var1 > anInt844) { - var1 = anInt844; - } - - if (var2 > anInt844) { - var2 = anInt844; - } - - var6 = (var6 << 9) - var30 * var3 + var30; - if (var1 < var2) { - var5 = var3 <<= 16; - if (var0 < 0) { - var5 -= var28 * var0; - var3 -= var26 * var0; - var6 -= var31 * var0; - var0 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var27 * var1; - var1 = 0; - } - - var41 = var0 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if ((var0 == var1 || var28 >= var26) && (var0 != var1 || var28 <= var27)) { - var2 -= var1; - var1 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var4 >> 16, var5 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var28; - var4 += var27; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var3 >> 16, var5 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var28; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var2 -= var1; - var1 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var5 >> 16, var4 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var28; - var4 += var27; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var5 >> 16, var3 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var28; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } else { - var4 = var3 <<= 16; - if (var0 < 0) { - var4 -= var28 * var0; - var3 -= var26 * var0; - var6 -= var31 * var0; - var0 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var27 * var2; - var2 = 0; - } - - var41 = var0 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if ((var0 == var2 || var28 >= var26) && (var0 != var2 || var27 <= var26)) { - var1 -= var2; - var2 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var3 >> 16, var5 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var27; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var3 >> 16, var4 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var4 += var28; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var1 -= var2; - var2 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var5 >> 16, var3 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var5 += var27; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var0, var4 >> 16, var3 >> 16, var6, var30, var32, var35, var38, var33, var36, var39); - var4 += var28; - var3 += var26; - var6 += var31; - var0 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } - } - } else if (var1 <= var2) { - if (var1 < anInt844) { - if (var2 > anInt844) { - var2 = anInt844; - } - - if (var0 > anInt844) { - var0 = anInt844; - } - - var7 = (var7 << 9) - var30 * var4 + var30; - if (var2 < var0) { - var3 = var4 <<= 16; - if (var1 < 0) { - var3 -= var26 * var1; - var4 -= var27 * var1; - var7 -= var31 * var1; - var1 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var28 * var2; - var2 = 0; - } - - var41 = var1 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if ((var1 == var2 || var26 >= var27) && (var1 != var2 || var26 <= var28)) { - var0 -= var2; - var2 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var5 >> 16, var3 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var26; - var5 += var28; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var4 >> 16, var3 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var26; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var0 -= var2; - var2 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var3 >> 16, var5 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var26; - var5 += var28; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var3 >> 16, var4 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var26; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } else { - var5 = var4 <<= 16; - if (var1 < 0) { - var5 -= var26 * var1; - var4 -= var27 * var1; - var7 -= var31 * var1; - var1 = 0; - } - - var3 <<= 16; - if (var0 < 0) { - var3 -= var28 * var0; - var0 = 0; - } - - var41 = var1 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if (var26 < var27) { - var2 -= var0; - var0 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var3 >> 16, var4 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var28; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var5 >> 16, var4 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var5 += var26; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var2 -= var0; - var0 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var4 >> 16, var3 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var3 += var28; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var1, var4 >> 16, var5 >> 16, var7, var30, var32, var35, var38, var33, var36, var39); - var5 += var26; - var4 += var27; - var7 += var31; - var1 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } - } - } else if (var2 < anInt844) { - if (var0 > anInt844) { - var0 = anInt844; - } - - if (var1 > anInt844) { - var1 = anInt844; - } - - var8 = (var8 << 9) - var30 * var5 + var30; - if (var0 < var1) { - var4 = var5 <<= 16; - if (var2 < 0) { - var4 -= var27 * var2; - var5 -= var28 * var2; - var8 -= var31 * var2; - var2 = 0; - } - - var3 <<= 16; - if (var0 < 0) { - var3 -= var26 * var0; - var0 = 0; - } - - var41 = var2 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if (var27 < var28) { - var1 -= var0; - var0 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var4 >> 16, var3 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var27; - var3 += var26; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var4 >> 16, var5 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var27; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var1 -= var0; - var0 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var3 >> 16, var4 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var27; - var3 += var26; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var5 >> 16, var4 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var27; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } else { - var3 = var5 <<= 16; - if (var2 < 0) { - var3 -= var27 * var2; - var5 -= var28 * var2; - var8 -= var31 * var2; - var2 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var26 * var1; - var1 = 0; - } - - var41 = var2 - anInt835; - var32 += var34 * var41; - var35 += var37 * var41; - var38 += var40 * var41; - if (var27 < var28) { - var0 -= var1; - var1 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var4 >> 16, var5 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var26; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var3 >> 16, var5 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var3 += var27; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } else { - var0 -= var1; - var1 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var5 >> 16, var4 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var4 += var26; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - - method1142(Toolkit.JAVA_TOOLKIT.getBuffer(), var19, var2, var5 >> 16, var3 >> 16, var8, var30, var32, var35, var38, var33, var36, var39); - var3 += var27; - var5 += var28; - var8 += var31; - var2 += Toolkit.JAVA_TOOLKIT.width; - var32 += var34; - var35 += var37; - var38 += var40; - } - } - } - } - } - } else { - var20 = anInterface2_838.method15(var18, 65535); - aBoolean837 = true; - method1154(var0, var1, var2, var3, var4, var5, method1136(var20, var6), method1136(var20, var7), method1136(var20, var8)); - } - } - - static int method1139() { - return anIntArray836[0] % Toolkit.JAVA_TOOLKIT.width; - } - - static void method1140(Interface2 var0) { - anInterface2_838 = var0; - } - - static void method1141() { - anInt846 = anInt847 / 2; - anInt835 = anInt844 / 2; - Class139.screenLowerX = -anInt846; - Class145.screenUpperX = anInt847 - anInt846; - Class1.screenUpperY = -anInt835; - AtmosphereParser.screenLowerY = anInt844 - anInt835; - } - - private static void method1142(int[] var0, int[] var1, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12, int var13, int var14) { - if (aBoolean849) { - if (var6 > anInt847) { - var6 = anInt847; - } - - if (var5 < 0) { - var5 = 0; - } - } - - if (var5 < var6) { - var4 += var5; - var7 += var8 * var5; - int var17 = var6 - var5; - int var15; - int var16; - int var19; - int var18; - int var21; - int var20; - int var23; - int var22; - int var3; - int var2; - if (aBoolean845) { - var23 = var5 - anInt846; - var9 += (var12 >> 3) * var23; - var10 += (var13 >> 3) * var23; - var11 += (var14 >> 3) * var23; - var22 = var11 >> 12; - if (var22 == 0) { - var18 = 0; - var19 = 0; - } else { - var18 = var9 / var22; - var19 = var10 / var22; - } - - var9 += var12; - var10 += var13; - var11 += var14; - var22 = var11 >> 12; - if (var22 == 0) { - var20 = 0; - var21 = 0; - } else { - var20 = var9 / var22; - var21 = var10 / var22; - } - - var2 = (var18 << 20) + var19; - var16 = (var20 - var18 >> 3 << 20) + (var21 - var19 >> 3); - var17 >>= 3; - var8 <<= 3; - var15 = var7 >> 8; - if (aBoolean842) { - if (var17 > 0) { - do { - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var18 = var20; - var19 = var21; - var9 += var12; - var10 += var13; - var11 += var14; - var22 = var11 >> 12; - if (var22 == 0) { - var20 = 0; - var21 = 0; - } else { - var20 = var9 / var22; - var21 = var10 / var22; - } - - var2 = (var18 << 20) + var19; - var16 = (var20 - var18 >> 3 << 20) + (var21 - var19 >> 3); - var7 += var8; - var15 = var7 >> 8; - --var17; - } while (var17 > 0); - } - - var17 = var6 - var5 & 7; - if (var17 > 0) { - do { - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - --var17; - } while (var17 > 0); - } - } else { - if (var17 > 0) { - do { - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var18 = var20; - var19 = var21; - var9 += var12; - var10 += var13; - var11 += var14; - var22 = var11 >> 12; - if (var22 == 0) { - var20 = 0; - var21 = 0; - } else { - var20 = var9 / var22; - var21 = var10 / var22; - } - - var2 = (var18 << 20) + var19; - var16 = (var20 - var18 >> 3 << 20) + (var21 - var19 >> 3); - var7 += var8; - var15 = var7 >> 8; - --var17; - } while (var17 > 0); - } - - var17 = var6 - var5 & 7; - if (var17 > 0) { - do { - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - --var17; - } while (var17 > 0); - } - } - } else { - var23 = var5 - anInt846; - var9 += (var12 >> 3) * var23; - var10 += (var13 >> 3) * var23; - var11 += (var14 >> 3) * var23; - var22 = var11 >> 14; - if (var22 == 0) { - var18 = 0; - var19 = 0; - } else { - var18 = var9 / var22; - var19 = var10 / var22; - } - - var9 += var12; - var10 += var13; - var11 += var14; - var22 = var11 >> 14; - if (var22 == 0) { - var20 = 0; - var21 = 0; - } else { - var20 = var9 / var22; - var21 = var10 / var22; - } - - var2 = (var18 << 18) + var19; - var16 = (var20 - var18 >> 3 << 18) + (var21 - var19 >> 3); - var17 >>= 3; - var8 <<= 3; - var15 = var7 >> 8; - if (aBoolean842) { - if (var17 > 0) { - do { - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var18 = var20; - var19 = var21; - var9 += var12; - var10 += var13; - var11 += var14; - var22 = var11 >> 14; - if (var22 == 0) { - var20 = 0; - var21 = 0; - } else { - var20 = var9 / var22; - var21 = var10 / var22; - } - - var2 = (var18 << 18) + var19; - var16 = (var20 - var18 >> 3 << 18) + (var21 - var19 >> 3); - var7 += var8; - var15 = var7 >> 8; - --var17; - } while (var17 > 0); - } - - var17 = var6 - var5 & 7; - if (var17 > 0) { - do { - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - --var17; - } while (var17 > 0); - } - } else { - if (var17 > 0) { - do { - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var18 = var20; - var19 = var21; - var9 += var12; - var10 += var13; - var11 += var14; - var22 = var11 >> 14; - if (var22 == 0) { - var20 = 0; - var21 = 0; - } else { - var20 = var9 / var22; - var21 = var10 / var22; - } - - var2 = (var18 << 18) + var19; - var16 = (var20 - var18 >> 3 << 18) + (var21 - var19 >> 3); - var7 += var8; - var15 = var7 >> 8; - --var17; - } while (var17 > 0); - } - - var17 = var6 - var5 & 7; - if (var17 > 0) { - do { - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - --var17; - } while (var17 > 0); - } - } - } - - } - } - - private static void method1143(int[] var0, int[] var1, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12, int var13, int var14) { - if (aBoolean849) { - if (var6 > anInt847) { - var6 = anInt847; - } - - if (var5 < 0) { - var5 = 0; - } - } - - if (var5 < var6) { - var4 += var5; - var7 += var8 * var5; - int var17 = var6 - var5; - int var15; - int var16; - int var19; - int var18; - int var21; - int var20; - int var23; - int var22; - int var3; - int var2; - if (aBoolean845) { - var23 = var5 - anInt846; - var9 += var12 * var23; - var10 += var13 * var23; - var11 += var14 * var23; - var22 = var11 >> 12; - if (var22 == 0) { - var18 = 0; - var19 = 0; - } else { - var18 = var9 / var22; - var19 = var10 / var22; - } - - var9 += var12 * var17; - var10 += var13 * var17; - var11 += var14 * var17; - var22 = var11 >> 12; - if (var22 == 0) { - var20 = 0; - var21 = 0; - } else { - var20 = var9 / var22; - var21 = var10 / var22; - } - - var2 = (var18 << 20) + var19; - var16 = ((var20 - var18) / var17 << 20) + (var21 - var19) / var17; - var17 >>= 3; - var8 <<= 3; - var15 = var7 >> 8; - if (aBoolean842) { - if (var17 > 0) { - do { - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var7 += var8; - var15 = var7 >> 8; - --var17; - } while (var17 > 0); - } - - var17 = var6 - var5 & 7; - if (var17 > 0) { - do { - var3 = var1[(var2 & 4032) + (var2 >>> 26)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - --var17; - } while (var17 > 0); - } - } else { - if (var17 > 0) { - do { - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - var7 += var8; - var15 = var7 >> 8; - --var17; - } while (var17 > 0); - } - - var17 = var6 - var5 & 7; - if (var17 > 0) { - do { - if ((var3 = var1[(var2 & 4032) + (var2 >>> 26)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - --var17; - } while (var17 > 0); - } - } - } else { - var23 = var5 - anInt846; - var9 += var12 * var23; - var10 += var13 * var23; - var11 += var14 * var23; - var22 = var11 >> 14; - if (var22 == 0) { - var18 = 0; - var19 = 0; - } else { - var18 = var9 / var22; - var19 = var10 / var22; - } - - var9 += var12 * var17; - var10 += var13 * var17; - var11 += var14 * var17; - var22 = var11 >> 14; - if (var22 == 0) { - var20 = 0; - var21 = 0; - } else { - var20 = var9 / var22; - var21 = var10 / var22; - } - - var2 = (var18 << 18) + var19; - var16 = ((var20 - var18) / var17 << 18) + (var21 - var19) / var17; - var17 >>= 3; - var8 <<= 3; - var15 = var7 >> 8; - if (aBoolean842) { - if (var17 > 0) { - do { - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - var7 += var8; - var15 = var7 >> 8; - --var17; - } while (var17 > 0); - } - - var17 = var6 - var5 & 7; - if (var17 > 0) { - do { - var3 = var1[(var2 & 16256) + (var2 >>> 25)]; - var0[var4++] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - var2 += var16; - --var17; - } while (var17 > 0); - } - } else { - if (var17 > 0) { - do { - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - var7 += var8; - var15 = var7 >> 8; - --var17; - } while (var17 > 0); - } - - var17 = var6 - var5 & 7; - if (var17 > 0) { - do { - if ((var3 = var1[(var2 & 16256) + (var2 >>> 25)]) != 0) { - var0[var4] = ((var3 & 16711935) * var15 & -16711936) + ((var3 & 65280) * var15 & 16711680) >> 8; - } - - ++var4; - var2 += var16; - --var17; - } while (var17 > 0); - } - } - } - - } - } - - static void method1144(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { - int var7 = 0; - if (var1 != var0) { - var7 = (var4 - var3 << 16) / (var1 - var0); - } - - int var8 = 0; - if (var2 != var1) { - var8 = (var5 - var4 << 16) / (var2 - var1); - } - - int var9 = 0; - if (var2 != var0) { - var9 = (var3 - var5 << 16) / (var0 - var2); - } - - if (var0 <= var1 && var0 <= var2) { - if (var0 < anInt844) { - if (var1 > anInt844) { - var1 = anInt844; - } - - if (var2 > anInt844) { - var2 = anInt844; - } - - if (var1 < var2) { - var5 = var3 <<= 16; - if (var0 < 0) { - var5 -= var9 * var0; - var3 -= var7 * var0; - var0 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var8 * var1; - var1 = 0; - } - - if ((var0 == var1 || var9 >= var7) && (var0 != var1 || var9 <= var8)) { - var2 -= var1; - var1 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var6, var4 >> 16, var5 >> 16); - var5 += var9; - var4 += var8; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var6, var3 >> 16, var5 >> 16); - var5 += var9; - var3 += var7; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var2 -= var1; - var1 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var6, var5 >> 16, var4 >> 16); - var5 += var9; - var4 += var8; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var6, var5 >> 16, var3 >> 16); - var5 += var9; - var3 += var7; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - } else { - var4 = var3 <<= 16; - if (var0 < 0) { - var4 -= var9 * var0; - var3 -= var7 * var0; - var0 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var8 * var2; - var2 = 0; - } - - if ((var0 == var2 || var9 >= var7) && (var0 != var2 || var8 <= var7)) { - var1 -= var2; - var2 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var6, var3 >> 16, var5 >> 16); - var5 += var8; - var3 += var7; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var6, var3 >> 16, var4 >> 16); - var4 += var9; - var3 += var7; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var1 -= var2; - var2 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var6, var5 >> 16, var3 >> 16); - var5 += var8; - var3 += var7; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var6, var4 >> 16, var3 >> 16); - var4 += var9; - var3 += var7; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - } - } - } else if (var1 <= var2) { - if (var1 < anInt844) { - if (var2 > anInt844) { - var2 = anInt844; - } - - if (var0 > anInt844) { - var0 = anInt844; - } - - if (var2 < var0) { - var3 = var4 <<= 16; - if (var1 < 0) { - var3 -= var7 * var1; - var4 -= var8 * var1; - var1 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var9 * var2; - var2 = 0; - } - - if ((var1 == var2 || var7 >= var8) && (var1 != var2 || var7 <= var9)) { - var0 -= var2; - var2 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var6, var5 >> 16, var3 >> 16); - var3 += var7; - var5 += var9; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var6, var4 >> 16, var3 >> 16); - var3 += var7; - var4 += var8; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var0 -= var2; - var2 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var6, var3 >> 16, var5 >> 16); - var3 += var7; - var5 += var9; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var6, var3 >> 16, var4 >> 16); - var3 += var7; - var4 += var8; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - } else { - var5 = var4 <<= 16; - if (var1 < 0) { - var5 -= var7 * var1; - var4 -= var8 * var1; - var1 = 0; - } - - var3 <<= 16; - if (var0 < 0) { - var3 -= var9 * var0; - var0 = 0; - } - - if (var7 < var8) { - var2 -= var0; - var0 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var6, var3 >> 16, var4 >> 16); - var3 += var9; - var4 += var8; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var6, var5 >> 16, var4 >> 16); - var5 += var7; - var4 += var8; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var2 -= var0; - var0 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var6, var4 >> 16, var3 >> 16); - var3 += var9; - var4 += var8; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var6, var4 >> 16, var5 >> 16); - var5 += var7; - var4 += var8; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - } - } - } else if (var2 < anInt844) { - if (var0 > anInt844) { - var0 = anInt844; - } - - if (var1 > anInt844) { - var1 = anInt844; - } - - if (var0 < var1) { - var4 = var5 <<= 16; - if (var2 < 0) { - var4 -= var8 * var2; - var5 -= var9 * var2; - var2 = 0; - } - - var3 <<= 16; - if (var0 < 0) { - var3 -= var7 * var0; - var0 = 0; - } - - if (var8 < var9) { - var1 -= var0; - var0 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var6, var4 >> 16, var3 >> 16); - var4 += var8; - var3 += var7; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var6, var4 >> 16, var5 >> 16); - var4 += var8; - var5 += var9; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var1 -= var0; - var0 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var6, var3 >> 16, var4 >> 16); - var4 += var8; - var3 += var7; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var6, var5 >> 16, var4 >> 16); - var4 += var8; - var5 += var9; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - } else { - var3 = var5 <<= 16; - if (var2 < 0) { - var3 -= var8 * var2; - var5 -= var9 * var2; - var2 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var7 * var1; - var1 = 0; - } - - if (var8 < var9) { - var0 -= var1; - var1 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var6, var4 >> 16, var5 >> 16); - var4 += var7; - var5 += var9; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var6, var3 >> 16, var5 >> 16); - var3 += var8; - var5 += var9; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var0 -= var1; - var1 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var6, var5 >> 16, var4 >> 16); - var4 += var7; - var5 += var9; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1149(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var6, var5 >> 16, var3 >> 16); - var3 += var8; - var5 += var9; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - } - } - } - - static void method1145(int var0, int var1) { - int var2 = anIntArray836[0]; - int var3 = var2 / Toolkit.JAVA_TOOLKIT.width; - int var4 = var2 - var3 * Toolkit.JAVA_TOOLKIT.width; - anInt846 = var0 - var4; - anInt835 = var1 - var3; - Class139.screenLowerX = -anInt846; - Class145.screenUpperX = anInt847 - anInt846; - Class1.screenUpperY = -anInt835; - AtmosphereParser.screenLowerY = anInt844 - anInt835; - } - - private static void method1146(int[] var0, int var1, int var4, int var5, int var6, int var7) { - if (aBoolean849) { - if (var5 > anInt847) { - var5 = anInt847; - } - - if (var4 < 0) { - var4 = 0; - } - } - - if (var4 < var5) { - var1 += var4; - var6 += var7 * var4; - int var8; - int var9; - int var10; - int var3; - int var2; - if (aBoolean843) { - var3 = var5 - var4 >> 2; - var7 <<= 2; - if (anInt850 == 0) { - if (var3 > 0) { - do { - var2 = anIntArray834[var6 >> 8]; - var6 += var7; - var0[var1++] = var2; - var0[var1++] = var2; - var0[var1++] = var2; - var0[var1++] = var2; - --var3; - } while (var3 > 0); - } - - var3 = var5 - var4 & 3; - if (var3 > 0) { - var2 = anIntArray834[var6 >> 8]; - - do { - var0[var1++] = var2; - --var3; - } while (var3 > 0); - } - } else { - var8 = anInt850; - var9 = 256 - anInt850; - if (var3 > 0) { - do { - var2 = anIntArray834[var6 >> 8]; - var6 += var7; - var2 = ((var2 & 16711935) * var9 >> 8 & 16711935) + ((var2 & 65280) * var9 >> 8 & 65280); - var10 = var0[var1]; - var0[var1++] = var2 + ((var10 & 16711935) * var8 >> 8 & 16711935) + ((var10 & 65280) * var8 >> 8 & 65280); - var10 = var0[var1]; - var0[var1++] = var2 + ((var10 & 16711935) * var8 >> 8 & 16711935) + ((var10 & 65280) * var8 >> 8 & 65280); - var10 = var0[var1]; - var0[var1++] = var2 + ((var10 & 16711935) * var8 >> 8 & 16711935) + ((var10 & 65280) * var8 >> 8 & 65280); - var10 = var0[var1]; - var0[var1++] = var2 + ((var10 & 16711935) * var8 >> 8 & 16711935) + ((var10 & 65280) * var8 >> 8 & 65280); - --var3; - } while (var3 > 0); - } - - var3 = var5 - var4 & 3; - if (var3 > 0) { - var2 = anIntArray834[var6 >> 8]; - var2 = ((var2 & 16711935) * var9 >> 8 & 16711935) + ((var2 & 65280) * var9 >> 8 & 65280); - - do { - var10 = var0[var1]; - var0[var1++] = var2 + ((var10 & 16711935) * var8 >> 8 & 16711935) + ((var10 & 65280) * var8 >> 8 & 65280); - --var3; - } while (var3 > 0); - } - } - - } else { - var3 = var5 - var4; - if (anInt850 == 0) { - do { - var0[var1++] = anIntArray834[var6 >> 8]; - var6 += var7; - --var3; - } while (var3 > 0); - } else { - var8 = anInt850; - var9 = 256 - anInt850; - - do { - var2 = anIntArray834[var6 >> 8]; - var6 += var7; - var2 = ((var2 & 16711935) * var9 >> 8 & 16711935) + ((var2 & 65280) * var9 >> 8 & 65280); - var10 = var0[var1]; - var0[var1++] = var2 + ((var10 & 16711935) * var8 >> 8 & 16711935) + ((var10 & 65280) * var8 >> 8 & 65280); - --var3; - } while (var3 > 0); - } - - } - } - } - - static void method1147(byte[] var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - int var8 = 0; - if (var2 != var1) { - var8 = (var5 - var4 << 16) / (var2 - var1); - } - - int var9 = 0; - if (var3 != var2) { - var9 = (var6 - var5 << 16) / (var3 - var2); - } - - int var10 = 0; - if (var3 != var1) { - var10 = (var4 - var6 << 16) / (var1 - var3); - } - - if (var1 <= var2 && var1 <= var3) { - if (var2 < var3) { - var6 = var4 <<= 16; - if (var1 < 0) { - var6 -= var10 * var1; - var4 -= var8 * var1; - var1 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var9 * var2; - var2 = 0; - } - - if ((var1 == var2 || var10 >= var8) && (var1 != var2 || var10 <= var9)) { - var3 -= var2; - var2 -= var1; - var1 *= var7; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var3; - if (var3 < 0) { - return; - } - - method1156(var0, var1, var5 >> 16, var6 >> 16); - var6 += var10; - var5 += var9; - var1 += var7; - } - } - - method1156(var0, var1, var4 >> 16, var6 >> 16); - var6 += var10; - var4 += var8; - var1 += var7; - } - } else { - var3 -= var2; - var2 -= var1; - var1 *= var7; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var3; - if (var3 < 0) { - return; - } - - method1156(var0, var1, var6 >> 16, var5 >> 16); - var6 += var10; - var5 += var9; - var1 += var7; - } - } - - method1156(var0, var1, var6 >> 16, var4 >> 16); - var6 += var10; - var4 += var8; - var1 += var7; - } - } - } else { - var5 = var4 <<= 16; - if (var1 < 0) { - var5 -= var10 * var1; - var4 -= var8 * var1; - var1 = 0; - } - - var6 <<= 16; - if (var3 < 0) { - var6 -= var9 * var3; - var3 = 0; - } - - if ((var1 == var3 || var10 >= var8) && (var1 != var3 || var9 <= var8)) { - var2 -= var3; - var3 -= var1; - var1 *= var7; - - while (true) { - --var3; - if (var3 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1156(var0, var1, var4 >> 16, var6 >> 16); - var6 += var9; - var4 += var8; - var1 += var7; - } - } - - method1156(var0, var1, var4 >> 16, var5 >> 16); - var5 += var10; - var4 += var8; - var1 += var7; - } - } else { - var2 -= var3; - var3 -= var1; - var1 *= var7; - - while (true) { - --var3; - if (var3 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1156(var0, var1, var6 >> 16, var4 >> 16); - var6 += var9; - var4 += var8; - var1 += var7; - } - } - - method1156(var0, var1, var5 >> 16, var4 >> 16); - var5 += var10; - var4 += var8; - var1 += var7; - } - } - } - } else if (var2 <= var3) { - if (var3 < var1) { - var4 = var5 <<= 16; - if (var2 < 0) { - var4 -= var8 * var2; - var5 -= var9 * var2; - var2 = 0; - } - - var6 <<= 16; - if (var3 < 0) { - var6 -= var10 * var3; - var3 = 0; - } - - if ((var2 == var3 || var8 >= var9) && (var2 != var3 || var8 <= var10)) { - var1 -= var3; - var3 -= var2; - var2 *= var7; - - while (true) { - --var3; - if (var3 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1156(var0, var2, var6 >> 16, var4 >> 16); - var4 += var8; - var6 += var10; - var2 += var7; - } - } - - method1156(var0, var2, var5 >> 16, var4 >> 16); - var4 += var8; - var5 += var9; - var2 += var7; - } - } else { - var1 -= var3; - var3 -= var2; - var2 *= var7; - - while (true) { - --var3; - if (var3 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1156(var0, var2, var4 >> 16, var6 >> 16); - var4 += var8; - var6 += var10; - var2 += var7; - } - } - - method1156(var0, var2, var4 >> 16, var5 >> 16); - var4 += var8; - var5 += var9; - var2 += var7; - } - } - } else { - var6 = var5 <<= 16; - if (var2 < 0) { - var6 -= var8 * var2; - var5 -= var9 * var2; - var2 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var10 * var1; - var1 = 0; - } - - if (var8 < var9) { - var3 -= var1; - var1 -= var2; - var2 *= var7; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var3; - if (var3 < 0) { - return; - } - - method1156(var0, var2, var4 >> 16, var5 >> 16); - var4 += var10; - var5 += var9; - var2 += var7; - } - } - - method1156(var0, var2, var6 >> 16, var5 >> 16); - var6 += var8; - var5 += var9; - var2 += var7; - } - } else { - var3 -= var1; - var1 -= var2; - var2 *= var7; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var3; - if (var3 < 0) { - return; - } - - method1156(var0, var2, var5 >> 16, var4 >> 16); - var4 += var10; - var5 += var9; - var2 += var7; - } - } - - method1156(var0, var2, var5 >> 16, var6 >> 16); - var6 += var8; - var5 += var9; - var2 += var7; - } - } - } - } else if (var1 < var2) { - var5 = var6 <<= 16; - if (var3 < 0) { - var5 -= var9 * var3; - var6 -= var10 * var3; - var3 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var8 * var1; - var1 = 0; - } - - if (var9 < var10) { - var2 -= var1; - var1 -= var3; - var3 *= var7; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1156(var0, var3, var5 >> 16, var4 >> 16); - var5 += var9; - var4 += var8; - var3 += var7; - } - } - - method1156(var0, var3, var5 >> 16, var6 >> 16); - var5 += var9; - var6 += var10; - var3 += var7; - } - } else { - var2 -= var1; - var1 -= var3; - var3 *= var7; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1156(var0, var3, var4 >> 16, var5 >> 16); - var5 += var9; - var4 += var8; - var3 += var7; - } - } - - method1156(var0, var3, var6 >> 16, var5 >> 16); - var5 += var9; - var6 += var10; - var3 += var7; - } - } - } else { - var4 = var6 <<= 16; - if (var3 < 0) { - var4 -= var9 * var3; - var6 -= var10 * var3; - var3 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var8 * var2; - var2 = 0; - } - - if (var9 < var10) { - var1 -= var2; - var2 -= var3; - var3 *= var7; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1156(var0, var3, var5 >> 16, var6 >> 16); - var5 += var8; - var6 += var10; - var3 += var7; - } - } - - method1156(var0, var3, var4 >> 16, var6 >> 16); - var4 += var9; - var6 += var10; - var3 += var7; - } - } else { - var1 -= var2; - var2 -= var3; - var3 *= var7; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1156(var0, var3, var6 >> 16, var5 >> 16); - var5 += var8; - var6 += var10; - var3 += var7; - } - } - - method1156(var0, var3, var6 >> 16, var4 >> 16); - var4 += var9; - var6 += var10; - var3 += var7; - } - } - } - } - - static void method1148(int var0, int var1, int var2) { - aBoolean849 = var0 < 0 || var0 > anInt847 || var1 < 0 || var1 > anInt847 || var2 < 0 || var2 > anInt847; - } - - private static void method1149(int[] var0, int var1, int var2, int var4, int var5) { - if (aBoolean849) { - if (var5 > anInt847) { - var5 = anInt847; - } - - if (var4 < 0) { - var4 = 0; - } - } - - if (var4 < var5) { - var1 += var4; - int var3 = var5 - var4 >> 2; - if (anInt850 == 0) { - while (true) { - --var3; - if (var3 < 0) { - var3 = var5 - var4 & 3; - - while (true) { - --var3; - if (var3 < 0) { - return; - } - - var0[var1++] = var2; - } - } - - var0[var1++] = var2; - var0[var1++] = var2; - var0[var1++] = var2; - var0[var1++] = var2; - } - } else if (anInt850 == 254) { - while (true) { - --var3; - if (var3 < 0) { - var3 = var5 - var4 & 3; - - while (true) { - --var3; - if (var3 < 0) { - return; - } - - var0[var1++] = var0[var1]; - } - } - - var0[var1++] = var0[var1]; - var0[var1++] = var0[var1]; - var0[var1++] = var0[var1]; - var0[var1++] = var0[var1]; - } - } else { - int var6 = anInt850; - int var7 = 256 - anInt850; - var2 = ((var2 & 16711935) * var7 >> 8 & 16711935) + ((var2 & 65280) * var7 >> 8 & 65280); - - while (true) { - --var3; - int var8; - if (var3 < 0) { - var3 = var5 - var4 & 3; - - while (true) { - --var3; - if (var3 < 0) { - return; - } - - var8 = var0[var1]; - var0[var1++] = var2 + ((var8 & 16711935) * var6 >> 8 & 16711935) + ((var8 & 65280) * var6 >> 8 & 65280); - } - } - - var8 = var0[var1]; - var0[var1++] = var2 + ((var8 & 16711935) * var6 >> 8 & 16711935) + ((var8 & 65280) * var6 >> 8 & 65280); - var8 = var0[var1]; - var0[var1++] = var2 + ((var8 & 16711935) * var6 >> 8 & 16711935) + ((var8 & 65280) * var6 >> 8 & 65280); - var8 = var0[var1]; - var0[var1++] = var2 + ((var8 & 16711935) * var6 >> 8 & 16711935) + ((var8 & 65280) * var6 >> 8 & 65280); - var8 = var0[var1]; - var0[var1++] = var2 + ((var8 & 16711935) * var6 >> 8 & 16711935) + ((var8 & 65280) * var6 >> 8 & 65280); - } - } - } - } - - private static void method1150() { - int var2 = 0; - - for (int var3 = 0; var3 < 512; ++var3) { - double var4 = (double) (var3 >> 3) / 64.0D + 0.0078125D; - double var6 = (double) (var3 & 7) / 8.0D + 0.0625D; - - for (int var8 = 0; var8 < 128; ++var8) { - double var9 = (double) var8 / 128.0D; - double var11 = var9; - double var13 = var9; - double var15 = var9; - if (var6 != 0.0D) { - double var17; - if (var9 < 0.5D) { - var17 = var9 * (1.0D + var6); - } else { - var17 = var9 + var6 - var9 * var6; - } - - double var19 = 2.0D * var9 - var17; - double var21 = var4 + 0.3333333333333333D; - if (var21 > 1.0D) { - --var21; - } - - double var25 = var4 - 0.3333333333333333D; - if (var25 < 0.0D) { - ++var25; - } - - if (6.0D * var21 < 1.0D) { - var11 = var19 + (var17 - var19) * 6.0D * var21; - } else if (2.0D * var21 < 1.0D) { - var11 = var17; - } else if (3.0D * var21 < 2.0D) { - var11 = var19 + (var17 - var19) * (0.6666666666666666D - var21) * 6.0D; - } else { - var11 = var19; - } - - if (6.0D * var4 < 1.0D) { - var13 = var19 + (var17 - var19) * 6.0D * var4; - } else if (2.0D * var4 < 1.0D) { - var13 = var17; - } else if (3.0D * var4 < 2.0D) { - var13 = var19 + (var17 - var19) * (0.6666666666666666D - var4) * 6.0D; - } else { - var13 = var19; - } - - if (6.0D * var25 < 1.0D) { - var15 = var19 + (var17 - var19) * 6.0D * var25; - } else if (2.0D * var25 < 1.0D) { - var15 = var17; - } else if (3.0D * var25 < 2.0D) { - var15 = var19 + (var17 - var19) * (0.6666666666666666D - var25) * 6.0D; - } else { - var15 = var19; - } - } - - var11 = Math.pow(var11, aFloat848); - var13 = Math.pow(var13, aFloat848); - var15 = Math.pow(var15, aFloat848); - int var27 = (int) (var11 * 256.0D); - int var18 = (int) (var13 * 256.0D); - int var28 = (int) (var15 * 256.0D); - int var20 = (var27 << 16) + (var18 << 8) + var28; - if (var20 == 0) { - var20 = 1; - } - - anIntArray834[var2++] = var20; - } - } - - } - - private static void method1151(int var0, int var1, int var2, int var3) { - anInt847 = var2 - var0; - anInt844 = var3 - var1; - method1141(); - if (anIntArray836.length < anInt844) { - anIntArray836 = new int[Class95.method1585((byte) 95, anInt844)]; - } - - int var4 = var1 * Toolkit.JAVA_TOOLKIT.width + var0; - - for (int var5 = 0; var5 < anInt844; ++var5) { - anIntArray836[var5] = var4; - var4 += Toolkit.JAVA_TOOLKIT.width; - } - - } - - private static void method1152(float var0) { - aFloat848 = var0; - aFloat848 = (float) ((double) aFloat848 + (Math.random() * 0.03D - 0.015D)); - } - - static int method1153() { - return anIntArray836[0] / Toolkit.JAVA_TOOLKIT.width; - } - - static void method1154(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - int var9 = var4 - var3; - int var10 = var1 - var0; - int var11 = var5 - var3; - int var12 = var2 - var0; - int var13 = var7 - var6; - int var14 = var8 - var6; - int var15; - if (var2 == var1) { - var15 = 0; - } else { - var15 = (var5 - var4 << 16) / (var2 - var1); - } - - int var16; - if (var1 == var0) { - var16 = 0; - } else { - var16 = (var9 << 16) / var10; - } - - int var17; - if (var2 == var0) { - var17 = 0; - } else { - var17 = (var11 << 16) / var12; - } - - int var18 = var9 * var12 - var11 * var10; - if (var18 != 0) { - int var19 = (var13 * var12 - var14 * var10 << 8) / var18; - int var20 = (var14 * var9 - var13 * var11 << 8) / var18; - if (var0 <= var1 && var0 <= var2) { - if (var0 < anInt844) { - if (var1 > anInt844) { - var1 = anInt844; - } - - if (var2 > anInt844) { - var2 = anInt844; - } - - var6 = (var6 << 8) - var19 * var3 + var19; - if (var1 < var2) { - var5 = var3 <<= 16; - if (var0 < 0) { - var5 -= var17 * var0; - var3 -= var16 * var0; - var6 -= var20 * var0; - var0 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var15 * var1; - var1 = 0; - } - - if ((var0 == var1 || var17 >= var16) && (var0 != var1 || var17 <= var15)) { - var2 -= var1; - var1 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var4 >> 16, var5 >> 16, var6, var19); - var5 += var17; - var4 += var15; - var6 += var20; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var3 >> 16, var5 >> 16, var6, var19); - var5 += var17; - var3 += var16; - var6 += var20; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var2 -= var1; - var1 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var5 >> 16, var4 >> 16, var6, var19); - var5 += var17; - var4 += var15; - var6 += var20; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var5 >> 16, var3 >> 16, var6, var19); - var5 += var17; - var3 += var16; - var6 += var20; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - } else { - var4 = var3 <<= 16; - if (var0 < 0) { - var4 -= var17 * var0; - var3 -= var16 * var0; - var6 -= var20 * var0; - var0 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var15 * var2; - var2 = 0; - } - - if ((var0 == var2 || var17 >= var16) && (var0 != var2 || var15 <= var16)) { - var1 -= var2; - var2 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var3 >> 16, var5 >> 16, var6, var19); - var5 += var15; - var3 += var16; - var6 += var20; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var3 >> 16, var4 >> 16, var6, var19); - var4 += var17; - var3 += var16; - var6 += var20; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var1 -= var2; - var2 -= var0; - var0 = anIntArray836[var0]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var5 >> 16, var3 >> 16, var6, var19); - var5 += var15; - var3 += var16; - var6 += var20; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var0, var4 >> 16, var3 >> 16, var6, var19); - var4 += var17; - var3 += var16; - var6 += var20; - var0 += Toolkit.JAVA_TOOLKIT.width; - } - } - } - } - } else if (var1 <= var2) { - if (var1 < anInt844) { - if (var2 > anInt844) { - var2 = anInt844; - } - - if (var0 > anInt844) { - var0 = anInt844; - } - - var7 = (var7 << 8) - var19 * var4 + var19; - if (var2 < var0) { - var3 = var4 <<= 16; - if (var1 < 0) { - var3 -= var16 * var1; - var4 -= var15 * var1; - var7 -= var20 * var1; - var1 = 0; - } - - var5 <<= 16; - if (var2 < 0) { - var5 -= var17 * var2; - var2 = 0; - } - - if ((var1 == var2 || var16 >= var15) && (var1 != var2 || var16 <= var17)) { - var0 -= var2; - var2 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var5 >> 16, var3 >> 16, var7, var19); - var3 += var16; - var5 += var17; - var7 += var20; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var4 >> 16, var3 >> 16, var7, var19); - var3 += var16; - var4 += var15; - var7 += var20; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var0 -= var2; - var2 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var2; - if (var2 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var3 >> 16, var5 >> 16, var7, var19); - var3 += var16; - var5 += var17; - var7 += var20; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var3 >> 16, var4 >> 16, var7, var19); - var3 += var16; - var4 += var15; - var7 += var20; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - } else { - var5 = var4 <<= 16; - if (var1 < 0) { - var5 -= var16 * var1; - var4 -= var15 * var1; - var7 -= var20 * var1; - var1 = 0; - } - - var3 <<= 16; - if (var0 < 0) { - var3 -= var17 * var0; - var0 = 0; - } - - if (var16 < var15) { - var2 -= var0; - var0 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var3 >> 16, var4 >> 16, var7, var19); - var3 += var17; - var4 += var15; - var7 += var20; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var5 >> 16, var4 >> 16, var7, var19); - var5 += var16; - var4 += var15; - var7 += var20; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var2 -= var0; - var0 -= var1; - var1 = anIntArray836[var1]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var2; - if (var2 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var4 >> 16, var3 >> 16, var7, var19); - var3 += var17; - var4 += var15; - var7 += var20; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var1, var4 >> 16, var5 >> 16, var7, var19); - var5 += var16; - var4 += var15; - var7 += var20; - var1 += Toolkit.JAVA_TOOLKIT.width; - } - } - } - } - } else if (var2 < anInt844) { - if (var0 > anInt844) { - var0 = anInt844; - } - - if (var1 > anInt844) { - var1 = anInt844; - } - - var8 = (var8 << 8) - var19 * var5 + var19; - if (var0 < var1) { - var4 = var5 <<= 16; - if (var2 < 0) { - var4 -= var15 * var2; - var5 -= var17 * var2; - var8 -= var20 * var2; - var2 = 0; - } - - var3 <<= 16; - if (var0 < 0) { - var3 -= var16 * var0; - var0 = 0; - } - - if (var15 < var17) { - var1 -= var0; - var0 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var4 >> 16, var3 >> 16, var8, var19); - var4 += var15; - var3 += var16; - var8 += var20; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var4 >> 16, var5 >> 16, var8, var19); - var4 += var15; - var5 += var17; - var8 += var20; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var1 -= var0; - var0 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var0; - if (var0 < 0) { - while (true) { - --var1; - if (var1 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var3 >> 16, var4 >> 16, var8, var19); - var4 += var15; - var3 += var16; - var8 += var20; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var5 >> 16, var4 >> 16, var8, var19); - var4 += var15; - var5 += var17; - var8 += var20; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - } else { - var3 = var5 <<= 16; - if (var2 < 0) { - var3 -= var15 * var2; - var5 -= var17 * var2; - var8 -= var20 * var2; - var2 = 0; - } - - var4 <<= 16; - if (var1 < 0) { - var4 -= var16 * var1; - var1 = 0; - } - - if (var15 < var17) { - var0 -= var1; - var1 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var4 >> 16, var5 >> 16, var8, var19); - var4 += var16; - var5 += var17; - var8 += var20; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var3 >> 16, var5 >> 16, var8, var19); - var3 += var15; - var5 += var17; - var8 += var20; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } else { - var0 -= var1; - var1 -= var2; - var2 = anIntArray836[var2]; - - while (true) { - --var1; - if (var1 < 0) { - while (true) { - --var0; - if (var0 < 0) { - return; - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var5 >> 16, var4 >> 16, var8, var19); - var4 += var16; - var5 += var17; - var8 += var20; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - - method1146(Toolkit.JAVA_TOOLKIT.getBuffer(), var2, var5 >> 16, var3 >> 16, var8, var19); - var3 += var15; - var5 += var17; - var8 += var20; - var2 += Toolkit.JAVA_TOOLKIT.width; - } - } - } - } - } - } - - private static void method1156(byte[] var0, int var1, int var3, int var4) { - if (var3 < var4) { - var1 += var3; - int var2 = var4 - var3 >> 2; - - while (true) { - --var2; - if (var2 < 0) { - var2 = var4 - var3 & 3; - - while (true) { - --var2; - if (var2 < 0) { - return; - } - - var0[var1++] = 1; - } - } - - var0[var1++] = 1; - var0[var1++] = 1; - var0[var1++] = 1; - var0[var1++] = 1; - } - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class52.java b/Client/src/main/java/org/runite/client/Class52.java deleted file mode 100644 index c7ad28384..000000000 --- a/Client/src/main/java/org/runite/client/Class52.java +++ /dev/null @@ -1,904 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class Class52 { - - static int[] anIntArray1833 = new int[14]; - static int[] anIntArray3139 = new int[14]; - static int[] anIntArray1679 = new int[14]; - static int[] anIntArray859; - static int[] anIntArray861; - int pnpcId; - boolean aBoolean864; - private long aLong855; - private int[] lookInfo; - private int renderAnim; - private long aLong860; - private int[] anIntArray862; - private int[][] anIntArrayArray863; - - static void method1160(int var0, int var1) { - try { - if (Unsorted.loadInterface(var1)) { - if (var0 > -100) { - method1168(52); - } - - Class67.method1260(23206, -1, GameObject.aClass11ArrayArray1834[var1]); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hh.B(" + var0 + ',' + var1 + ')'); - } - } - - static boolean method1166(int var0, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, boolean var11, int var12) { - try { - int var13; - int var14; - for (var13 = 0; var13 < 104; ++var13) { - for (var14 = 0; var14 < 104; ++var14) { - Class84.anIntArrayArray1160[var13][var14] = 0; - Class97.anIntArrayArray1373[var13][var14] = 99999999; - } - } - - var13 = var9; - var14 = var12; - Class84.anIntArrayArray1160[var9][var12] = 99; - Class97.anIntArrayArray1373[var9][var12] = 0; - byte var15 = 0; - TextureOperation38.anIntArray3456[var15] = var9; - int var28 = var15 + 1; - Class45.anIntArray729[var15] = var12; - int var16 = 0; - boolean var17 = false; - int[][] var18 = AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].anIntArrayArray1304; - - int var19; - int var20; - label410: - while (var28 != var16) { - var13 = TextureOperation38.anIntArray3456[var16]; - var14 = Class45.anIntArray729[var16]; - var16 = 1 + var16 & 4095; - if (var13 == var6 && var0 == var14) { - var17 = true; - break; - } - - if (0 != var7) { - if ((var7 < 5 || 10 == var7) && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1488(var0, var13, var14, var6, var7 + -1, var5, var4)) { - var17 = true; - break; - } - - if (var7 < 10 && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1492(var0, -1 + var7, var6, var14, var5, var4, var13, 95)) { - var17 = true; - break; - } - } - - if (var2 != 0 && var10 != 0 && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1498(var6, var14, var13, var5, var2, var8, var0, var10)) { - var17 = true; - break; - } - - var19 = 1 + Class97.anIntArrayArray1373[var13][var14]; - if (var13 > 0 && Class84.anIntArrayArray1160[-1 + var13][var14] == 0 && (var18[var13 + -1][var14] & 19661070) == 0 && 0 == (19661112 & var18[-1 + var13][-1 + var5 + var14])) { - var20 = 1; - - while (true) { - if (-1 + var5 <= var20) { - TextureOperation38.anIntArray3456[var28] = -1 + var13; - Class45.anIntArray729[var28] = var14; - Class84.anIntArrayArray1160[-1 + var13][var14] = 2; - var28 = 4095 & 1 + var28; - Class97.anIntArrayArray1373[var13 + -1][var14] = var19; - break; - } - - if ((19661118 & var18[-1 + var13][var14 + var20]) != 0) { - break; - } - - ++var20; - } - } - - if (var13 < 102 && Class84.anIntArrayArray1160[1 + var13][var14] == 0 && (19661187 & var18[var13 + var5][var14]) == 0 && (19661280 & var18[var5 + var13][var14 + var5 + -1]) == 0) { - var20 = 1; - - while (true) { - if (var20 >= -1 + var5) { - TextureOperation38.anIntArray3456[var28] = var13 + 1; - Class45.anIntArray729[var28] = var14; - Class84.anIntArrayArray1160[var13 + 1][var14] = 8; - Class97.anIntArrayArray1373[var13 + 1][var14] = var19; - var28 = 4095 & var28 - -1; - break; - } - - if ((var18[var5 + var13][var14 + var20] & 19661283) != 0) { - break; - } - - ++var20; - } - } - - if (var14 > 0 && 0 == Class84.anIntArrayArray1160[var13][-1 + var14] && (19661070 & var18[var13][-1 + var14]) == 0 && 0 == (19661187 & var18[-1 + var5 + var13][var14 + -1])) { - var20 = 1; - - while (true) { - if (-1 + var5 <= var20) { - TextureOperation38.anIntArray3456[var28] = var13; - Class45.anIntArray729[var28] = -1 + var14; - Class84.anIntArrayArray1160[var13][-1 + var14] = 1; - var28 = 4095 & 1 + var28; - Class97.anIntArrayArray1373[var13][-1 + var14] = var19; - break; - } - - if ((var18[var13 + var20][var14 + -1] & 19661199) != 0) { - break; - } - - ++var20; - } - } - - if (102 > var14 && Class84.anIntArrayArray1160[var13][1 + var14] == 0 && (var18[var13][var14 + var5] & 19661112) == 0 && (19661280 & var18[-1 + var13 + var5][var5 + var14]) == 0) { - var20 = 1; - - while (true) { - if (var20 >= (var5 - 1)) { - TextureOperation38.anIntArray3456[var28] = var13; - Class45.anIntArray729[var28] = var14 + 1; - Class84.anIntArrayArray1160[var13][1 + var14] = 4; - Class97.anIntArrayArray1373[var13][1 + var14] = var19; - var28 = 4095 & var28 + 1; - break; - } - - if ((19661304 & var18[var13 - -var20][var5 + var14]) != 0) { - break; - } - - ++var20; - } - } - - if (var13 > 0 && var14 > 0 && Class84.anIntArrayArray1160[var13 + -1][var14 + -1] == 0 && (var18[var13 + -1][-1 + var5 + -1 + var14] & 19661112) == 0 && 0 == (19661070 & var18[-1 + var13][var14 - 1]) && (var18[var5 + -1 + (var13 - 1)][-1 + var14] & 19661187) == 0) { - var20 = 1; - - while (true) { - if (var5 - 1 <= var20) { - TextureOperation38.anIntArray3456[var28] = var13 - 1; - Class45.anIntArray729[var28] = -1 + var14; - var28 = 4095 & var28 + 1; - Class84.anIntArrayArray1160[var13 + -1][-1 + var14] = 3; - Class97.anIntArrayArray1373[-1 + var13][var14 + -1] = var19; - break; - } - - if ((var18[var13 - 1][var14 - 1 + var20] & 19661118) != 0 || 0 != (19661199 & var18[var20 + -1 + var13][-1 + var14])) { - break; - } - - ++var20; - } - } - - if (102 > var13 && var14 > 0 && Class84.anIntArrayArray1160[1 + var13][-1 + var14] == 0 && (19661070 & var18[1 + var13][-1 + var14]) == 0 && (var18[var5 + var13][-1 + var14] & 19661187) == 0 && (var18[var13 - -var5][-1 + var14 + var5 + -1] & 19661280) == 0) { - var20 = 1; - - while (true) { - if ((-1 + var5) <= var20) { - TextureOperation38.anIntArray3456[var28] = 1 + var13; - Class45.anIntArray729[var28] = -1 + var14; - var28 = 1 + var28 & 4095; - Class84.anIntArrayArray1160[var13 - -1][-1 + var14] = 9; - Class97.anIntArrayArray1373[1 + var13][-1 + var14] = var19; - break; - } - - if ((19661283 & var18[var13 + var5][var14 - (1 + -var20)]) != 0 || (19661199 & var18[var20 + (var13 - -1)][-1 + var14]) != 0) { - break; - } - - ++var20; - } - } - - if (var13 > 0 && 102 > var14 && Class84.anIntArrayArray1160[-1 + var13][var14 - -1] == 0 && 0 == (19661070 & var18[var13 - 1][1 + var14]) && (19661112 & var18[-1 + var13][var14 + var5]) == 0 && 0 == (19661280 & var18[var13][var14 + var5])) { - var20 = 1; - - while (true) { - if (-1 + var5 <= var20) { - TextureOperation38.anIntArray3456[var28] = var13 - 1; - Class45.anIntArray729[var28] = var14 + 1; - var28 = 4095 & var28 - -1; - Class84.anIntArrayArray1160[-1 + var13][1 + var14] = 6; - Class97.anIntArrayArray1373[-1 + var13][1 + var14] = var19; - break; - } - - if ((var18[var13 - 1][var14 - -1 - -var20] & 19661118) != 0 || (var18[var20 + -1 + var13][var5 + var14] & 19661304) != 0) { - break; - } - - ++var20; - } - } - - if (var13 < 102 && var14 < 102 && Class84.anIntArrayArray1160[var13 - -1][1 + var14] == 0 && 0 == (19661112 & var18[var13 + 1][var14 + var5]) && 0 == (var18[var13 - -var5][var14 + var5] & 19661280) && ~(19661187 & var18[var5 + var13][1 + var14]) == -1) { - for (var20 = 1; var20 < -1 + var5; ++var20) { - if ((var18[var20 + var13 - -1][var14 - -var5] & 19661304) != 0 || 0 != (var18[var5 + var13][var20 + (var14 - -1)] & 19661283)) { - continue label410; - } - } - - TextureOperation38.anIntArray3456[var28] = 1 + var13; - Class45.anIntArray729[var28] = var14 - -1; - Class84.anIntArrayArray1160[1 + var13][1 + var14] = 12; - Class97.anIntArrayArray1373[1 + var13][1 + var14] = var19; - var28 = 1 + var28 & 4095; - } - } - - Class129.anInt1692 = 0; - if (!var17) { - if (!var11) { - return false; - } - - var19 = 1000; - var20 = 100; - byte var21 = 10; - - for (int var22 = var6 - var21; var22 <= var21 + var6; ++var22) { - for (int var23 = -var21 + var0; (var0 + var21) >= var23; ++var23) { - if (var22 >= 0 && var23 >= 0 && var22 < 104 && var23 < 104 && Class97.anIntArrayArray1373[var22][var23] < 100) { - int var24 = 0; - if (var22 < var6) { - var24 = -var22 + var6; - } else if (var22 > (var6 - (-var2 - -1))) { - var24 = -var2 + -var6 - -1 + var22; - } - - int var25 = 0; - if (var0 > var23) { - var25 = -var23 + var0; - } else if (var23 > (var0 + var10 - 1)) { - var25 = var23 + 1 + -var0 + -var10; - } - - int var26 = var24 * var24 + var25 * var25; - if (var19 > var26 || var26 == var19 && Class97.anIntArrayArray1373[var22][var23] < var20) { - var20 = Class97.anIntArrayArray1373[var22][var23]; - var13 = var22; - var19 = var26; - var14 = var23; - } - } - } - } - - if (var19 == 1000) { - return false; - } - - if (var9 == var13 && var12 == var14) { - return false; - } - - Class129.anInt1692 = 1; - } - - byte var29 = 0; - TextureOperation38.anIntArray3456[var29] = var13; - var16 = var29 + 1; - Class45.anIntArray729[var29] = var14; - - for (var19 = var20 = Class84.anIntArrayArray1160[var13][var14]; var9 != var13 || var14 != var12; var19 = Class84.anIntArrayArray1160[var13][var14]) { - if (var19 != var20) { - TextureOperation38.anIntArray3456[var16] = var13; - var20 = var19; - Class45.anIntArray729[var16++] = var14; - } - - if ((2 & var19) == 0) { - if (0 != (8 & var19)) { - --var13; - } - } else { - ++var13; - } - - if ((var19 & 1) != 0) { - ++var14; - } else if ((var19 & 4) != 0) { - --var14; - } - } - - if (0 >= var16) { - return var3 != 1; - } else { - TextureOperation7.method299(93, var16, var3); - return true; - } - } catch (RuntimeException var27) { - throw ClientErrorException.clientError(var27, "hh.C(" + var0 + ',' + (byte) 34 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var10 + ',' + var11 + ',' + var12 + ')'); - } - } - - public static void method1168(int var0) { - try { - TextCore.aClass94_853 = null; - TextCore.aClass94_852 = null; - if (var0 == 8160) { - anIntArray859 = null; - TextCore.COMMAND_LOWRES_GRAPHICS = null; - anIntArray861 = null; - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "hh.H(" + var0 + ')'); - } - } - - final Model method1157(int var1, int var2, int var3, int var4, SequenceDefinition var5, int var6, int var7) { - try { - - long var9 = (long) var3 | ((long) var7 << 16) | (long) var2 << 32; - Model var11 = (Model) Unsorted.aReferenceCache_1131.get(var9); - if (null == var11) { - Model_Sub1[] var12 = new Model_Sub1[3]; - int var13 = 0; - if (TextureOperation20.method231(var3).method948() || TextureOperation20.method231(var7).method948() || TextureOperation20.method231(var2).method948()) { - return null; - } - - Model_Sub1 var14 = TextureOperation20.method231(var3).method941(); - var12[var13++] = var14; - - var14 = TextureOperation20.method231(var7).method941(); - var12[var13++] = var14; - - var14 = TextureOperation20.method231(var2).method941(); - var12[var13++] = var14; - - var14 = new Model_Sub1(var12, var13); - - for (int var15 = 0; var15 < 5; ++var15) { - if (AudioThread.aShortArrayArray344[var15].length > this.anIntArray862[var15]) { - var14.method2016(Class3_Sub25.aShortArray2548[var15], AudioThread.aShortArrayArray344[var15][this.anIntArray862[var15]]); - } - - if (this.anIntArray862[var15] < Class101.aShortArrayArray1429[var15].length) { - var14.method2016(Class91.aShortArray1311[var15], Class101.aShortArrayArray1429[var15][this.anIntArray862[var15]]); - } - } - - var11 = var14.method2008(64, 768, -50, -10, -50); - Unsorted.aReferenceCache_1131.put(var11, var9); - } - - if (null != var5) { - var11 = var5.method2055(var11, (byte) -86, var1, var4, var6); - } - - return var11; - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "hh.J(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + (var5 != null ? "{...}" : "null") + ',' + var6 + ',' + var7 + ',' + -2012759707 + ')'); - } - } - - private void method1158() { - try { - long var2 = this.aLong860; - this.aLong860 = -1L; - long[] var4 = Class120.aLongArray1631; - this.aLong860 = var4[(int) (255L & ((long) (this.renderAnim >> 8) ^ this.aLong860))] ^ this.aLong860 >>> 8; - this.aLong860 = var4[(int) (255L & (this.aLong860 ^ (long) this.renderAnim))] ^ this.aLong860 >>> 8; - - int var5; - for (var5 = 0; 12 > var5; ++var5) { - this.aLong860 = this.aLong860 >>> 8 ^ var4[(int) ((this.aLong860 ^ (long) (this.lookInfo[var5] >> 24)) & 255L)]; - this.aLong860 = this.aLong860 >>> 8 ^ var4[(int) (255L & (this.aLong860 ^ (long) (this.lookInfo[var5] >> 16)))]; - this.aLong860 = var4[(int) (255L & ((long) (this.lookInfo[var5] >> 8) ^ this.aLong860))] ^ this.aLong860 >>> 8; - this.aLong860 = this.aLong860 >>> 8 ^ var4[(int) ((this.aLong860 ^ (long) this.lookInfo[var5]) & 255L)]; - } - - for (var5 = 0; var5 < 5; ++var5) { - this.aLong860 = var4[(int) (((long) this.anIntArray862[var5] ^ this.aLong860) & 255L)] ^ this.aLong860 >>> 8; - } - - this.aLong860 = var4[(int) (((long) (this.aBoolean864 ? 1 : 0) ^ this.aLong860) & 255L)] ^ this.aLong860 >>> 8; - if (var2 != 0 && this.aLong860 != var2) { - KeyboardListener.aReferenceCache_1911.remove(var2); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "hh.K(" + 459557008 + ')'); - } - } - - final void method1159(boolean var1) { - try { - this.aBoolean864 = var1; - this.method1158(); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "hh.A(" + var1 + ',' + true + ')'); - } - } - - final void method1161(int[] var1, int var2, boolean var3, int[] look, int render) { - try { - if (render != this.renderAnim) { - this.renderAnim = render; - this.anIntArrayArray863 = null; - } - - if (null == look) { - look = new int[12]; - - for (int var7 = 0; var7 < 8; ++var7) { - for (int var8 = 0; Class25.anInt497 > var8; ++var8) { - Class24 var9 = TextureOperation20.method231(var8); - if (!var9.aBoolean476 && ((var3 ? CS2Script.anIntArray3228[var7] : Class3_Sub26.anIntArray2559[var7]) == var9.anInt466)) { - look[Class163.anIntArray2043[var7]] = TextureOperation3.bitwiseOr(Integer.MIN_VALUE, var8); - break; - } - } - } - } - - this.pnpcId = var2; - this.aBoolean864 = var3; - this.anIntArray862 = var1; - this.lookInfo = look; - this.method1158(); - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "hh.G(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + var3 + ',' + 0 + ',' + "{...}" + ',' + render + ')'); - } - } - - final void method1162(int var1, int var3) { - try { - this.anIntArray862[var1] = var3; - this.method1158(); - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "hh.L(" + var1 + ',' + false + ',' + var3 + ')'); - } - } - - final int method1163() { - try { - - return this.pnpcId != -1 ? 305419896 + NPCDefinition.getNPCDefinition(this.pnpcId).npcId : (this.lookInfo[8] << 10) + ((this.anIntArray862[0] << 25) - -(this.anIntArray862[4] << 20)) - (-(this.lookInfo[0] << 15) - ((this.lookInfo[11] << 5) + this.lookInfo[1])); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hh.E(" + -24861 + ')'); - } - } - - final void method1164(int var1, int var2) { - try { - int var4 = Class163.anIntArray2043[var1]; - if (this.lookInfo[var4] != 0) { - TextureOperation20.method231(var2); - this.lookInfo[var4] = TextureOperation3.bitwiseOr(var2, Integer.MIN_VALUE); - this.method1158(); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "hh.I(" + var1 + ',' + var2 + ',' + 0 + ')'); - } - } - - final Model method1165(Class145[] var1, int var2, SequenceDefinition var3, SequenceDefinition var4, int var5, int var6, int var7, int var8, int var10, int var11) { - try { - if (this.pnpcId == -1) { - int[] var15 = this.lookInfo; - long var13 = this.aLong860; - if (var4 != null && (var4.leftHandItem >= 0 || var4.rightHandItem >= 0)) { - var15 = new int[12]; - - System.arraycopy(this.lookInfo, 0, var15, 0, 12); - - if (var4.leftHandItem >= 0) { - if (var4.leftHandItem == 65535) { - var13 ^= -4294967296L; - var15[5] = 0; - } else { - var15[5] = TextureOperation3.bitwiseOr(1073741824, var4.leftHandItem); - var13 ^= (long) var15[5] << 32; - } - } - - if (var4.rightHandItem >= 0) { - if (var4.rightHandItem == 65535) { - var15[3] = 0; - var13 ^= 4294967295L; - } else { - var15[3] = TextureOperation3.bitwiseOr(1073741824, var4.rightHandItem); - var13 ^= var15[3]; - } - } - } - - Model var37 = (Model) KeyboardListener.aReferenceCache_1911.get(var13); - boolean var17; - int var23; - int var22; - int var25; - int var24; - int frame; - int var29; - int var28; - int var34; - int var32; - int var33; - int var45; - if (var37 == null) { - var17 = false; - - int var19; - for (int var18 = 0; var18 < 12; ++var18) { - var19 = var15[var18]; - if ((var19 & 1073741824) != 0) { - if (!ItemDefinition.getItemDefinition(1073741823 & var19).method1108(this.aBoolean864)) { - var17 = true; - } - } else if ((var19 < 0) && !TextureOperation20.method231(1073741823 & var19).method942()) { - var17 = true; - } - } - - if (var17) { - if (this.aLong855 != -1L) { - var37 = (Model) KeyboardListener.aReferenceCache_1911.get(this.aLong855); - } - - if (null == var37) { - return null; - } - } - - if (null == var37) { - Model_Sub1[] var39 = new Model_Sub1[12]; - - int var20; - for (var19 = 0; var19 < 12; ++var19) { - var20 = var15[var19]; - Model_Sub1 var21; - if ((var20 & 1073741824) == 0) { - if (var20 < 0) { - var21 = TextureOperation20.method231(var20 & 1073741823).method947(); - if (null != var21) { - var39[var19] = var21; - } - } - } else { - var21 = ItemDefinition.getItemDefinition(var20 & 1073741823).method1117(this.aBoolean864); - if (null != var21) { - var39[var19] = var21; - } - } - } - - RenderAnimationDefinition var40 = null; - if (this.renderAnim != -1) { - var40 = Class3_Sub10.getRenderAnimationDefinition(this.renderAnim); - } - - if (var40 != null && null != var40.equipment_transforms) { - for (var20 = 0; var20 < var40.equipment_transforms.length; ++var20) { - if (null != var40.equipment_transforms[var20] && null != var39[var20]) { - var45 = var40.equipment_transforms[var20][0]; - var22 = var40.equipment_transforms[var20][1]; - var23 = var40.equipment_transforms[var20][2]; - var25 = var40.equipment_transforms[var20][4]; - var24 = var40.equipment_transforms[var20][3]; - frame = var40.equipment_transforms[var20][5]; - if (null == this.anIntArrayArray863) { - this.anIntArrayArray863 = new int[var40.equipment_transforms.length][]; - } - - if (this.anIntArrayArray863[var20] == null) { - int[] var27 = this.anIntArrayArray863[var20] = new int[15]; - if (var24 == 0 && var25 == 0 && frame == 0) { - var27[12] = -var45; - var27[13] = -var22; - var27[0] = var27[4] = var27[8] = 32768; - var27[14] = -var23; - } else { - var28 = Class51.anIntArray851[var24] >> 1; - var29 = Class51.anIntArray840[var24] >> 1; - int var30 = Class51.anIntArray851[var25] >> 1; - int var31 = Class51.anIntArray840[var25] >> 1; - var32 = Class51.anIntArray851[frame] >> 1; - var33 = Class51.anIntArray840[frame] >> 1; - var27[4] = var28 * var32 + 16384 >> 15; - var27[5] = -var29; - var27[3] = 16384 + var33 * var28 >> 15; - var27[2] = 16384 + var28 * var31 >> 15; - var27[8] = var30 * var28 - -16384 >> 15; - int var35 = 16384 + var33 * var29 >> 15; - var27[0] = var31 * var35 + var32 * var30 - -16384 >> 15; - var27[14] = 16384 + var27[8] * -var23 + -var22 * var27[5] + var27[2] * -var45 >> 15; - var27[6] = var30 * var35 + (var32 * -var31 - -16384) >> 15; - var34 = 16384 + var32 * var29 >> 15; - var27[7] = 16384 + -var33 * -var31 + var34 * var30 >> 15; - var27[1] = var31 * var34 + (var30 * -var33 - -16384) >> 15; - var27[12] = -var22 * var27[3] + var27[0] * -var45 + -var23 * var27[6] - -16384 >> 15; - var27[13] = 16384 + -var22 * var27[4] + var27[1] * -var45 + -var23 * var27[7] >> 15; - } - - var27[9] = var45; - var27[11] = var23; - var27[10] = var22; - } - - if (0 != var24 || var25 != 0 || frame != 0) { - var39[var20].method2013(var24, var25, frame); - } - - if (var45 != 0 || 0 != var22 || 0 != var23) { - var39[var20].method2001(var45, var22, var23); - } - } - } - } - - Model_Sub1 var43 = new Model_Sub1(var39, var39.length); - - for (var45 = 0; 5 > var45; ++var45) { - if (AudioThread.aShortArrayArray344[var45].length > this.anIntArray862[var45]) { - var43.method2016(Class3_Sub25.aShortArray2548[var45], AudioThread.aShortArrayArray344[var45][this.anIntArray862[var45]]); - } - - if (Class101.aShortArrayArray1429[var45].length > this.anIntArray862[var45]) { - var43.method2016(Class91.aShortArray1311[var45], Class101.aShortArrayArray1429[var45][this.anIntArray862[var45]]); - } - } - - var37 = var43.method2008(64, 850, -30, -50, -30); - if (HDToolKit.highDetail) { - ((Class140_Sub1_Sub1) var37).method1920(false, false, true, false, false, true); - } - - KeyboardListener.aReferenceCache_1911.put(var37, var13); - this.aLong855 = var13; - } - } - - var17 = false; - boolean var38 = false; - var45 = var1 != null ? var1.length : 0; - boolean var42 = false; - boolean var44 = false; - - int var47; - for (var22 = 0; var22 < var45; ++var22) { - if (Objects.requireNonNull(var1)[var22] != null) { - SequenceDefinition var41 = SequenceDefinition.getAnimationDefinition(var1[var22].animationId); - if (var41.frames != null) { - var17 = true; - Class123.aClass142Array1654[var22] = var41; - var24 = var1[var22].anInt1893; - var25 = var1[var22].anInt1891; - frame = var41.frames[var24]; - Class166.aClass3_Sub28_Sub5Array2070[var22] = Class3_Sub9.method133(frame >>> 16); - frame &= 65535; - anIntArray1833[var22] = frame; - if (Class166.aClass3_Sub28_Sub5Array2070[var22] != null) { - var42 |= Class166.aClass3_Sub28_Sub5Array2070[var22].method561(frame, (byte) 119); - var38 |= Class166.aClass3_Sub28_Sub5Array2070[var22].method559(frame); - var44 |= var41.aBoolean1848; - } - - if ((var41.aBoolean1846 || ClientCommands.tweeningEnabled) && var25 != -1 && var25 < var41.frames.length) { - Class154.anIntArray1960[var22] = var41.duration[var24]; - anIntArray3139[var22] = var1[var22].anInt1897; - var47 = var41.frames[var25]; - Class75.aClass3_Sub28_Sub5Array1103[var22] = Class3_Sub9.method133(var47 >>> 16); - var47 &= 65535; - anIntArray1679[var22] = var47; - if (null != Class75.aClass3_Sub28_Sub5Array1103[var22]) { - var42 |= Class75.aClass3_Sub28_Sub5Array1103[var22].method561(var47, (byte) 117); - var38 |= Class75.aClass3_Sub28_Sub5Array1103[var22].method559(var47); - } - } else { - Class154.anIntArray1960[var22] = 0; - anIntArray3139[var22] = 0; - Class75.aClass3_Sub28_Sub5Array1103[var22] = null; - anIntArray1679[var22] = -1; - } - } - } - } - - if (!var17 && null == var4 && null == var3) { - return var37; - } else { - var22 = -1; - var23 = -1; - var24 = 0; - Class3_Sub28_Sub5 var48 = null; - Class3_Sub28_Sub5 var46 = null; - if (null != var4) { - var22 = var4.frames[var10]; - var47 = var22 >>> 16; - var46 = Class3_Sub9.method133(var47); - var22 &= 65535; - if (var46 != null) { - var42 |= var46.method561(var22, (byte) 124); - var38 |= var46.method559(var22); - var44 |= var4.aBoolean1848; - } - - if ((var4.aBoolean1846 || ClientCommands.tweeningEnabled) && var2 != -1 && var4.frames.length > var2) { - var23 = var4.frames[var2]; - var28 = var23 >>> 16; - var23 &= 65535; - var24 = var4.duration[var10]; - if (var28 == var47) { - var48 = var46; - } else { - var48 = Class3_Sub9.method133(var23 >>> 16); - } - - if (null != var48) { - var42 |= var48.method561(var23, (byte) 122); - var38 |= var48.method559(var23); - } - } - } - - var47 = -1; - var28 = -1; - Class3_Sub28_Sub5 var49 = null; - Class3_Sub28_Sub5 var50 = null; - var29 = 0; - if (var3 != null) { - var47 = var3.frames[var11]; - var32 = var47 >>> 16; - var47 &= 65535; - var49 = Class3_Sub9.method133(var32); - if (null != var49) { - var42 |= var49.method561(var47, (byte) 123); - var38 |= var49.method559(var47); - var44 |= var3.aBoolean1848; - } - - if ((var3.aBoolean1846 || ClientCommands.tweeningEnabled) && var6 != -1 && var3.frames.length > var6) { - var29 = var3.duration[var11]; - var28 = var3.frames[var6]; - var33 = var28 >>> 16; - var28 &= 65535; - if (var33 == var32) { - var50 = var49; - } else { - var50 = Class3_Sub9.method133(var28 >>> 16); - } - - if (null != var50) { - var42 |= var50.method561(var28, (byte) 122); - var38 |= var50.method559(var28); - } - } - } - - Model var51 = var37.method1894(!var38, !var42, !var44); - var33 = 0; - - for (var34 = 1; var33 < var45; var34 <<= 1) { - if (Class166.aClass3_Sub28_Sub5Array2070[var33] != null) { - var51.method1887(Class166.aClass3_Sub28_Sub5Array2070[var33], anIntArray1833[var33], Class75.aClass3_Sub28_Sub5Array1103[var33], anIntArray1679[var33], anIntArray3139[var33] + -1, Class154.anIntArray1960[var33], var34, Class123.aClass142Array1654[var33].aBoolean1848, this.anIntArrayArray863[var33]); - } - - ++var33; - } - - if (null != var46 && null != var49) { - var51.method1892(var46, var22, var48, var23, var8 - 1, var24, var49, var47, var50, var28, var5 + -1, var29, var4.aBooleanArray1855, var4.aBoolean1848 | var3.aBoolean1848); - } else if (var46 == null) { - if (null != var49) { - var51.method1880(var49, var47, var50, var28, var5 - 1, var29, var3.aBoolean1848); - } - } else { - var51.method1880(var46, var22, var48, var23, var8 + -1, var24, var4.aBoolean1848); - } - - for (var33 = 0; var33 < var45; ++var33) { - Class166.aClass3_Sub28_Sub5Array2070[var33] = null; - Class75.aClass3_Sub28_Sub5Array1103[var33] = null; - Class123.aClass142Array1654[var33] = null; - } - - return var51; - } - } else { - return NPCDefinition.getNPCDefinition(this.pnpcId).method1476(var1, var6, (byte) -128, var11, var2, var8, var10, var3, var5, var4); - } - } catch (RuntimeException var36) { - throw ClientErrorException.clientError(var36, "hh.D(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + true + ',' + var10 + ',' + var11 + ')'); - } - } - - final Model method1167(int var1, SequenceDefinition var3, int var4, int var5) { - try { - if (this.pnpcId == -1) { - Model var6 = (Model) Unsorted.aReferenceCache_1131.get(this.aLong860); - - if (var6 == null) { - boolean var7 = false; - - int var9; - for (int var8 = 0; var8 < 12; ++var8) { - var9 = this.lookInfo[var8]; - if (0 == (1073741824 & var9)) { - if (var9 < 0 && TextureOperation20.method231(var9 & 1073741823).method948()) { - var7 = true; - } - } else if (!ItemDefinition.getItemDefinition(1073741823 & var9).method1102(this.aBoolean864)) { - var7 = true; - } - } - - if (var7) { - return null; - } - - Model_Sub1[] var14 = new Model_Sub1[12]; - var9 = 0; - - int var11; - for (int var10 = 0; var10 < 12; ++var10) { - var11 = this.lookInfo[var10]; - Model_Sub1 var12; - if ((1073741824 & var11) == 0) { - if (0 != (Integer.MIN_VALUE & var11)) { - var12 = TextureOperation20.method231(1073741823 & var11).method941(); - var14[var9++] = var12; - } - } else { - var12 = ItemDefinition.getItemDefinition(var11 & 1073741823).method1116(this.aBoolean864); - if (var12 != null) { - var14[var9++] = var12; - } - } - } - - Model_Sub1 var15 = new Model_Sub1(var14, var9); - - for (var11 = 0; var11 < 5; ++var11) { - if (this.anIntArray862[var11] < AudioThread.aShortArrayArray344[var11].length) { - var15.method2016(Class3_Sub25.aShortArray2548[var11], AudioThread.aShortArrayArray344[var11][this.anIntArray862[var11]]); - } - - if (Class101.aShortArrayArray1429[var11].length > this.anIntArray862[var11]) { - var15.method2016(Class91.aShortArray1311[var11], Class101.aShortArrayArray1429[var11][this.anIntArray862[var11]]); - } - } - - var6 = var15.method2008(64, 768, -50, -10, -50); - Unsorted.aReferenceCache_1131.put(var6, this.aLong860); - } - - if (var3 != null) { - var6 = var3.method2055(var6, (byte) 120, var5, var1, var4); - } - - return var6; - } else { - return NPCDefinition.getNPCDefinition(this.pnpcId).getChatModel(var3, var1, var5, -109, var4); - } - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "hh.F(" + var1 + ',' + (byte) 127 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + var5 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class53.java b/Client/src/main/java/org/runite/client/Class53.java deleted file mode 100644 index 519ecc848..000000000 --- a/Client/src/main/java/org/runite/client/Class53.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.runite.client; - - -import java.io.*; - -public final class Class53 { - - static int anInt865 = -1; - static long aLong866 = 0L; - static int anInt867; - static int worldListOffset; - - - static int method1170(int var1) { - return var1 >>> 8; - } - - public static void method1171(int var0, int var1, int var2, int var3, int var4, RSInterface var5) { - try { - int var7 = var3 * var3 + var4 * var4; - if (var7 <= 360000) { - int var8 = Math.min(var5.width / 2, var5.height / 2); - - if (var8 * var8 >= var7) { - Class38_Sub1.minimapIcons(var5, Class129_Sub1.aAbstractSpriteArray2690[var0], var4, var3, var1, var2); - } else { - var8 -= 10; - int var9 = 2047 & TextureOperation9.anInt3102 + GraphicDefinition.CAMERA_DIRECTION; - int var11 = Class51.anIntArray851[var9]; - int var10 = Class51.anIntArray840[var9]; - var10 = var10 * 256 / (256 + Class164_Sub2.anInt3020); - var11 = var11 * 256 / (Class164_Sub2.anInt3020 + 256); - int var12 = var4 * var10 - -(var11 * var3) >> 16; - int var13 = -(var10 * var3) + var4 * var11 >> 16; - double var14 = Math.atan2(var12, var13); - int var16 = (int) (Math.sin(var14) * (double) var8); - int var17 = (int) (Math.cos(var14) * (double) var8); - if (HDToolKit.highDetail) { - ((HDSprite) TextureOperation8.aAbstractSpriteArray3458[var0]).method648(240, 240, (var5.width / 2 + var2 + var16) * 16, 16 * (-var17 + var5.height / 2 + var1), (int) (10430.378D * var14)); - } else { - ((SoftwareSprite) TextureOperation8.aAbstractSpriteArray3458[var0]).method660(-10 + var16 + var5.width / 2 + var2, -10 + var5.height / 2 + var1 + -var17, var14); - } - } - - } - } catch (RuntimeException var18) { - throw ClientErrorException.clientError(var18, "hi.D(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + (var5 != null ? "{...}" : "null") + ',' + false + ')'); - } - } - - static String method1172(Throwable var1) throws IOException { - String var2; - if (var1 instanceof ClientErrorException) { - ClientErrorException var3 = (ClientErrorException) var1; - var1 = var3.aThrowable2118; - var2 = var3.aString2117 + " | "; - } else { - var2 = ""; - } - - StringWriter var13 = new StringWriter(); - PrintWriter var4 = new PrintWriter(var13); - var1.printStackTrace(var4); - var4.close(); - String var5 = var13.toString(); - BufferedReader var6 = new BufferedReader(new StringReader(var5)); - String var7 = var6.readLine(); - - while (true) { - String var8 = var6.readLine(); - if (var8 == null) { - var2 = var2 + "| " + var7; - return var2; - } - - int var9 = var8.indexOf(40); - int var10 = var8.indexOf(41, 1 + var9); - String var11; - if (var9 == -1) { - var11 = var8; - } else { - var11 = var8.substring(0, var9); - } - - var11 = var11.trim(); - var11 = var11.substring(1 + var11.lastIndexOf(32)); - var11 = var11.substring(var11.lastIndexOf(9) + 1); - var2 = var2 + var11; - if (-1 != var9 && var10 != -1) { - int var12 = var8.indexOf(".java:", var9); - if (var12 >= 0) { - var2 = var2 + var8.substring(5 + var12, var10); - } - } - - var2 = var2 + ' '; - } - } - - static RSString method1174(RSInterface var0, byte var1) { - try { - return Client.method44(var0).method101() != 0 ? (null != var0.aClass94_245 && var0.aClass94_245.trim(1).length() != 0 ? var0.aClass94_245 : (ClientCommands.commandQaOpEnabled ? TextCore.aClass94_2116 : null)) : null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hi.F(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class56.java b/Client/src/main/java/org/runite/client/Class56.java deleted file mode 100644 index 6251dd7d6..000000000 --- a/Client/src/main/java/org/runite/client/Class56.java +++ /dev/null @@ -1,200 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.NodeCache; - -public final class Class56 { - - static Class3_Sub26 aClass3_Sub26_884 = new Class3_Sub26(0, 0); - static NodeCache aClass47_885 = new NodeCache(128); - static RSInterface aClass11_886 = null; - public static int[] localPlayerIndexes = new int[2048]; - static Class106[] aClass106Array890; - - static int anInt893 = 0; - - static int method1186(int var1) { - try { - double var2 = (double) (255 & var1 >> 16) / 256.0D; - double var4 = (double) (255 & var1 >> 8) / 256.0D; - double var12 = 0.0D; - double var6 = (double) (255 & var1) / 256.0D; - double var8 = var2; - double var14 = 0; - double var10 = var2; - if (var2 > var4) { - var8 = var4; - } - - if (var6 < var8) { - var8 = var6; - } - - if (var4 > var2) { - var10 = var4; - } - - if (var6 > var10) { - var10 = var6; - } - - double var16 = (var8 + var10) / 2.0D; - if (var8 != var10) { - if (0.5D > var16) { - var14 = (-var8 + var10) / (var8 + var10); - } - - if (var16 >= 0.5D) { - var14 = (-var8 + var10) / (2.0D - var10 - var8); - } - - if (var10 == var2) { - var12 = (var4 - var6) / (-var8 + var10); - } else if (var4 == var10) { - var12 = 2.0D + (-var2 + var6) / (var10 - var8); - } else if (var10 == var6) { - var12 = 4.0D + (-var4 + var2) / (-var8 + var10); - } - } - - int var19 = (int) (var14 * 256.0D); - int var20 = (int) (256.0D * var16); - var12 /= 6.0D; - if (0 > var20) { - var20 = 0; - } else if (var20 > 255) { - var20 = 255; - } - - int var18 = (int) (var12 * 256.0D); - if (var19 >= 0) { - if (var19 > 255) { - var19 = 255; - } - } else { - var19 = 0; - } - - if (var20 <= 243) { - if (var20 <= 217) { - if (var20 > 192) { - var19 >>= 2; - } else if (var20 > 179) { - var19 >>= 1; - } - } else { - var19 >>= 3; - } - } else { - var19 >>= 4; - } - - return (var18 >> 2 << 10) + (var19 >> 5 << 7) + (var20 >> 1); - } catch (RuntimeException var21) { - throw ClientErrorException.clientError(var21, "ib.A(" + 0 + ',' + var1 + ')'); - } - } - - static void method1188() { - try { - MouseListeningClass.anIntArray1920 = null; - Class38_Sub1.anIntArrayArrayArray2609 = null; - Unsorted.anIntArray2469 = null; - Unsorted.aByteArrayArrayArray1328 = null; - PacketParser.aByteArrayArrayArray81 = null; - Class67.aByteArrayArrayArray1014 = null; - Class158_Sub1.aByteArrayArrayArray1828 = null; - TextureOperation36.aByteArrayArrayArray3430 = null; - Unsorted.anIntArray1138 = null; - Class129.anIntArray1695 = null; - Class3_Sub31.anIntArray2606 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ib.D(" + -113 + ')'); - } - } - - static boolean method1189(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, GameObject var8, int var9, boolean var10, long var11) { - boolean var13 = Class44.anIntArrayArrayArray723 == Unsorted.anIntArrayArrayArray3605; - int var14 = 0; - - int var16; - for (int var15 = var1; var15 < var1 + var3; ++var15) { - for (var16 = var2; var16 < var2 + var4; ++var16) { - if (var15 < 0 || var16 < 0 || var15 >= Unsorted.anInt1234 || var16 >= TextureOperation17.anInt3179) { - return false; - } - - Class3_Sub2 var17 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var15][var16]; - if (var17 != null && var17.anInt2223 >= 5) {//@splinter - return false; - } - } - } - - Class25 var20 = new Class25(); - var20.aLong498 = var11; - var20.anInt493 = var0; - var20.anInt482 = var5; - var20.anInt484 = var6; - var20.anInt489 = var7; - var20.aClass140_479 = var8; - var20.anInt496 = var9; - var20.anInt483 = var1; - var20.anInt478 = var2; - var20.anInt495 = var1 + var3 - 1; - var20.anInt481 = var2 + var4 - 1; - - int var21; - for (var16 = var1; var16 < var1 + var3; ++var16) { - for (var21 = var2; var21 < var2 + var4; ++var21) { - int var18 = 0; - if (var16 > var1) { - ++var18; - } - - if (var16 < var1 + var3 - 1) { - var18 += 4; - } - - if (var21 > var2) { - var18 += 8; - } - - if (var21 < var2 + var4 - 1) { - var18 += 2; - } - - for (int var19 = var0; var19 >= 0; --var19) { - if (Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var19][var16][var21] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var19][var16][var21] = new Class3_Sub2(var19, var16, var21); - } - } - - Class3_Sub2 var22 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var16][var21]; - var22.aClass25Array2221[var22.anInt2223] = var20; - var22.anIntArray2237[var22.anInt2223] = var18; - var22.anInt2228 |= var18; - ++var22.anInt2223; - if (var13 && TextureOperation16.anIntArrayArray3115[var16][var21] != 0) { - var14 = TextureOperation16.anIntArrayArray3115[var16][var21]; - } - } - } - - if (var13 && var14 != 0) { - for (var16 = var1; var16 < var1 + var3; ++var16) { - for (var21 = var2; var21 < var2 + var4; ++var21) { - if (TextureOperation16.anIntArrayArray3115[var16][var21] == 0) { - TextureOperation16.anIntArrayArray3115[var16][var21] = var14; - } - } - } - } - - if (var10) { - SequenceDefinition.aClass25Array1868[Unsorted.anInt3070++] = var20; - } - - return true; - } - -} diff --git a/Client/src/main/java/org/runite/client/Class57.java b/Client/src/main/java/org/runite/client/Class57.java deleted file mode 100644 index 823184a8b..000000000 --- a/Client/src/main/java/org/runite/client/Class57.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.runite.client; - -public final class Class57 { - - public static AbstractSprite aAbstractSprite_895; - static int[] varpArray = new int[3500]; - static int anInt902 = 100; - static int[] anIntArray904 = new int[200]; - int anInt896 = 0; - int anInt899 = 2048; - int anInt907 = 0; - int anInt908 = 2048; - - - final void method1190(DataBuffer var2, int var3) { - try { - while (true) { - int var4 = var2.readUnsignedByte(); - if (var4 == 0) { - return; - } - - this.method1191(var4, var2, var3); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ic.A(" + 2 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ')'); - } - } - - private void method1191(int var1, DataBuffer var2, int var3) { - try { - if (1 == var1) { - this.anInt896 = var2.readUnsignedByte(); - } else if (var1 == 2) { - this.anInt908 = var2.readUnsignedShort(); - } else if (var1 == 3) { - this.anInt899 = var2.readUnsignedShort(); - } else if (4 == var1) { - this.anInt907 = var2.readSignedShort(); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ic.C(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class58.java b/Client/src/main/java/org/runite/client/Class58.java deleted file mode 100644 index 5da452c1a..000000000 --- a/Client/src/main/java/org/runite/client/Class58.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.runite.client; - - -import java.awt.*; - -public final class Class58 { - - static int anInt909 = -1; - static int[][][] anIntArrayArrayArray911 = new int[2][][]; - static int[][][] anIntArrayArrayArray914; - static Interface4 anInterface4_915 = null; - static int anInt916; - static Js5Worker aJs5Worker_917; - - - public static void method1194() { - try { - if (null != Class3_Sub21.aAudioChannel_2491) { - Class3_Sub21.aAudioChannel_2491.method2153(); - } - - if (null != WorldListEntry.aAudioChannel_2627) { - WorldListEntry.aAudioChannel_2627.method2153(); - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "id.A(" + -16385 + ')'); - } - } - - static void method1196(int var3, int var4) { - try { - Class3_Sub28_Sub18.anInt3765 = var4; - - Class101.anInt1425 = 0; - Class159.anInt2020 = 0; - Class57.anInt902 = var3; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "id.B(" + 0 + ',' + 0 + ',' + (byte) 111 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method1197(CacheIndex var0) { - try { - - Class46.aClass153_737 = var0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "id.E(" + (var0 != null ? "{...}" : "null") + ',' + (byte) 69 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class59.java b/Client/src/main/java/org/runite/client/Class59.java deleted file mode 100644 index a5e00f02a..000000000 --- a/Client/src/main/java/org/runite/client/Class59.java +++ /dev/null @@ -1,284 +0,0 @@ -package org.runite.client; - -final class Class59 { - - private static final int[] anIntArray928 = new int[]{256, 128, 86, 64}; - private static final float[] aFloatArray929 = new float[]{1.0649863E-7F, 1.1341951E-7F, 1.2079015E-7F, 1.2863978E-7F, 1.369995E-7F, 1.459025E-7F, 1.5538409E-7F, 1.6548181E-7F, 1.7623574E-7F, 1.8768856E-7F, 1.998856E-7F, 2.128753E-7F, 2.2670913E-7F, 2.4144197E-7F, 2.5713223E-7F, 2.7384212E-7F, 2.9163792E-7F, 3.1059022E-7F, 3.307741E-7F, 3.5226967E-7F, 3.7516213E-7F, 3.995423E-7F, 4.255068E-7F, 4.5315863E-7F, 4.8260745E-7F, 5.1397E-7F, 5.4737063E-7F, 5.829419E-7F, 6.208247E-7F, 6.611694E-7F, 7.041359E-7F, 7.4989464E-7F, 7.98627E-7F, 8.505263E-7F, 9.057983E-7F, 9.646621E-7F, 1.0273513E-6F, 1.0941144E-6F, 1.1652161E-6F, 1.2409384E-6F, 1.3215816E-6F, 1.4074654E-6F, 1.4989305E-6F, 1.5963394E-6F, 1.7000785E-6F, 1.8105592E-6F, 1.9282195E-6F, 2.053526E-6F, 2.1869757E-6F, 2.3290977E-6F, 2.4804558E-6F, 2.6416496E-6F, 2.813319E-6F, 2.9961443E-6F, 3.1908505E-6F, 3.39821E-6F, 3.619045E-6F, 3.8542307E-6F, 4.1047006E-6F, 4.371447E-6F, 4.6555283E-6F, 4.958071E-6F, 5.280274E-6F, 5.623416E-6F, 5.988857E-6F, 6.3780467E-6F, 6.7925284E-6F, 7.2339453E-6F, 7.704048E-6F, 8.2047E-6F, 8.737888E-6F, 9.305725E-6F, 9.910464E-6F, 1.0554501E-5F, 1.1240392E-5F, 1.1970856E-5F, 1.2748789E-5F, 1.3577278E-5F, 1.4459606E-5F, 1.5399271E-5F, 1.6400005E-5F, 1.7465769E-5F, 1.8600793E-5F, 1.9809577E-5F, 2.1096914E-5F, 2.2467912E-5F, 2.3928002E-5F, 2.5482977E-5F, 2.7139005E-5F, 2.890265E-5F, 3.078091E-5F, 3.2781227E-5F, 3.4911533E-5F, 3.718028E-5F, 3.9596467E-5F, 4.2169668E-5F, 4.491009E-5F, 4.7828602E-5F, 5.0936775E-5F, 5.424693E-5F, 5.7772202E-5F, 6.152657E-5F, 6.552491E-5F, 6.9783084E-5F, 7.4317984E-5F, 7.914758E-5F, 8.429104E-5F, 8.976875E-5F, 9.560242E-5F, 1.0181521E-4F, 1.0843174E-4F, 1.1547824E-4F, 1.2298267E-4F, 1.3097477E-4F, 1.3948625E-4F, 1.4855085E-4F, 1.5820454E-4F, 1.6848555E-4F, 1.7943469E-4F, 1.9109536E-4F, 2.0351382E-4F, 2.167393E-4F, 2.3082423E-4F, 2.4582449E-4F, 2.6179955E-4F, 2.7881275E-4F, 2.9693157E-4F, 3.1622787E-4F, 3.3677815E-4F, 3.5866388E-4F, 3.8197188E-4F, 4.0679457E-4F, 4.3323037E-4F, 4.613841E-4F, 4.913675E-4F, 5.2329927E-4F, 5.573062E-4F, 5.935231E-4F, 6.320936E-4F, 6.731706E-4F, 7.16917E-4F, 7.635063E-4F, 8.1312325E-4F, 8.6596457E-4F, 9.2223985E-4F, 9.821722E-4F, 0.0010459992F, 0.0011139743F, 0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F, 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, 0.0019632196F, 0.0020908006F, 0.0022266726F, 0.0023713743F, 0.0025254795F, 0.0026895993F, 0.0028643848F, 0.0030505287F, 0.003248769F, 0.0034598925F, 0.0036847359F, 0.0039241905F, 0.0041792067F, 0.004450795F, 0.004740033F, 0.005048067F, 0.0053761187F, 0.005725489F, 0.0060975635F, 0.0064938175F, 0.0069158226F, 0.0073652514F, 0.007843887F, 0.008353627F, 0.008896492F, 0.009474637F, 0.010090352F, 0.01074608F, 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, 0.014722068F, 0.015678791F, 0.016697686F, 0.017782796F, 0.018938422F, 0.020169148F, 0.021479854F, 0.022875736F, 0.02436233F, 0.025945531F, 0.027631618F, 0.029427277F, 0.031339627F, 0.03337625F, 0.035545226F, 0.037855156F, 0.0403152F, 0.042935107F, 0.045725275F, 0.048696756F, 0.05186135F, 0.05523159F, 0.05882085F, 0.062643364F, 0.06671428F, 0.07104975F, 0.075666964F, 0.08058423F, 0.08582105F, 0.09139818F, 0.097337745F, 0.1036633F, 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, 0.14201812F, 0.15124726F, 0.16107617F, 0.1715438F, 0.18269168F, 0.19456401F, 0.20720787F, 0.22067343F, 0.23501402F, 0.25028655F, 0.26655158F, 0.28387362F, 0.3023213F, 0.32196787F, 0.34289113F, 0.36517414F, 0.3889052F, 0.41417846F, 0.44109413F, 0.4697589F, 0.50028646F, 0.53279793F, 0.5674221F, 0.6042964F, 0.64356697F, 0.6853896F, 0.72993004F, 0.777365F, 0.8278826F, 0.88168305F, 0.9389798F, 1.0F}; - private static boolean[] aBooleanArray919; - private static int[] anIntArray925; - private static int[] anIntArray927; - private final int[] anIntArray918; - private final int[][] anIntArrayArray920; - private final int[] anIntArray921; - private final int[] anIntArray922; - private final int anInt923; - private final int[] anIntArray924; - private final int[] anIntArray926; - - - Class59() { - int var1 = Class3_Sub14.method368(16); - if (var1 == 1) { - int var2 = Class3_Sub14.method368(5); - int var3 = 0; - this.anIntArray922 = new int[var2]; - - int var4; - int var5; - for (var4 = 0; var4 < var2; ++var4) { - var5 = Class3_Sub14.method368(4); - this.anIntArray922[var4] = var5; - if (var5 >= var3) { - var3 = var5 + 1; - } - } - - this.anIntArray926 = new int[var3]; - this.anIntArray924 = new int[var3]; - this.anIntArray918 = new int[var3]; - this.anIntArrayArray920 = new int[var3][]; - - int var7; - for (var4 = 0; var4 < var3; ++var4) { - this.anIntArray926[var4] = Class3_Sub14.method368(3) + 1; - var5 = this.anIntArray924[var4] = Class3_Sub14.method368(2); - if (var5 != 0) { - this.anIntArray918[var4] = Class3_Sub14.method368(8); - } - - var5 = 1 << var5; - int[] var6 = new int[var5]; - this.anIntArrayArray920[var4] = var6; - - for (var7 = 0; var7 < var5; ++var7) { - var6[var7] = Class3_Sub14.method368(8) - 1; - } - } - - this.anInt923 = Class3_Sub14.method368(2) + 1; - var4 = Class3_Sub14.method368(4); - var5 = 2; - - int var9; - for (var9 = 0; var9 < var2; ++var9) { - var5 += this.anIntArray926[this.anIntArray922[var9]]; - } - - this.anIntArray921 = new int[var5]; - this.anIntArray921[0] = 0; - this.anIntArray921[1] = 1 << var4; - var5 = 2; - - for (var9 = 0; var9 < var2; ++var9) { - var7 = this.anIntArray922[var9]; - - for (int var8 = 0; var8 < this.anIntArray926[var7]; ++var8) { - this.anIntArray921[var5++] = Class3_Sub14.method368(var4); - } - } - - if (anIntArray925 == null || anIntArray925.length < var5) { - anIntArray925 = new int[var5]; - anIntArray927 = new int[var5]; - aBooleanArray919 = new boolean[var5]; - } - - } else { - throw new RuntimeException(); - } - } - - private static int method1201(int[] var0, int var1) { - int var2 = var0[var1]; - int var3 = -1; - int var4 = Integer.MIN_VALUE; - - for (int var5 = 0; var5 < var1; ++var5) { - int var6 = var0[var5]; - if (var6 < var2 && var6 > var4) { - var3 = var5; - var4 = var6; - } - } - - return var3; - } - - private static int method1203(int[] var0, int var1) { - int var2 = var0[var1]; - int var3 = -1; - int var4 = Integer.MAX_VALUE; - - for (int var5 = 0; var5 < var1; ++var5) { - int var6 = var0[var5]; - if (var6 > var2 && var6 < var4) { - var3 = var5; - var4 = var6; - } - } - - return var3; - } - - private void method1198(int var1, int var2, int var3, int var4, float[] var5, int var6) { - int var7 = var4 - var2; - int var8 = var3 - var1; - int var9 = var7 < 0 ? -var7 : var7; - int var10 = var7 / var8; - int var11 = var2; - int var12 = 0; - int var13 = var7 < 0 ? var10 - 1 : var10 + 1; - var9 -= (var10 < 0 ? -var10 : var10) * var8; - var5[var1] *= aFloatArray929[var2]; - if (var3 > var6) { - var3 = var6; - } - - for (int var14 = var1 + 1; var14 < var3; ++var14) { - var12 += var9; - if (var12 >= var8) { - var12 -= var8; - var11 += var13; - } else { - var11 += var10; - } - - var5[var14] *= aFloatArray929[var11]; - } - - } - - private void method1199(int var1, int var2) { - if (var1 < var2) { - int var3 = var1; - int var4 = anIntArray925[var1]; - int var5 = anIntArray927[var1]; - boolean var6 = aBooleanArray919[var1]; - - for (int var7 = var1 + 1; var7 <= var2; ++var7) { - int var8 = anIntArray925[var7]; - if (var8 < var4) { - anIntArray925[var3] = var8; - anIntArray927[var3] = anIntArray927[var7]; - aBooleanArray919[var3] = aBooleanArray919[var7]; - ++var3; - anIntArray925[var7] = anIntArray925[var3]; - anIntArray927[var7] = anIntArray927[var3]; - aBooleanArray919[var7] = aBooleanArray919[var3]; - } - } - - anIntArray925[var3] = var4; - anIntArray927[var3] = var5; - aBooleanArray919[var3] = var6; - this.method1199(var1, var3 - 1); - this.method1199(var3 + 1, var2); - } - } - - private int method1200(int var1, int var2, int var3, int var4, int var5) { - int var6 = var4 - var2; - int var7 = var3 - var1; - int var8 = var6 < 0 ? -var6 : var6; - int var9 = var8 * (var5 - var1); - int var10 = var9 / var7; - return var6 < 0 ? var2 - var10 : var2 + var10; - } - - final void method1202(float[] var1, int var2) { - int var3 = this.anIntArray921.length; - int var4 = anIntArray928[this.anInt923 - 1]; - aBooleanArray919[0] = aBooleanArray919[1] = true; - - int var5; - int var6; - int var7; - int var8; - int var9; - for (var5 = 2; var5 < var3; ++var5) { - var6 = method1201(anIntArray925, var5); - var7 = method1203(anIntArray925, var5); - var8 = this.method1200(anIntArray925[var6], anIntArray927[var6], anIntArray925[var7], anIntArray927[var7], anIntArray925[var5]); - var9 = anIntArray927[var5]; - int var10 = var4 - var8; - int var12 = (var10 < var8 ? var10 : var8) << 1; - if (var9 == 0) { - aBooleanArray919[var5] = false; - anIntArray927[var5] = var8; - } else { - aBooleanArray919[var6] = aBooleanArray919[var7] = true; - aBooleanArray919[var5] = true; - if (var9 >= var12) { - anIntArray927[var5] = var10 > var8 ? var9 - var8 + var8 : var8 - var9 + var10 - 1; - } else { - anIntArray927[var5] = (var9 & 1) != 0 ? var8 - (var9 + 1) / 2 : var8 + var9 / 2; - } - } - } - - this.method1199(0, var3 - 1); - var5 = 0; - var6 = anIntArray927[0] * this.anInt923; - - for (var7 = 1; var7 < var3; ++var7) { - if (aBooleanArray919[var7]) { - var8 = anIntArray925[var7]; - var9 = anIntArray927[var7] * this.anInt923; - this.method1198(var5, var6, var8, var9, var1, var2); - if (var8 >= var2) { - return; - } - - var5 = var8; - var6 = var9; - } - } - - float var13 = aFloatArray929[var6]; - - for (var8 = var5; var8 < var2; ++var8) { - var1[var8] *= var13; - } - - } - - final boolean method1205() { - boolean var1 = Class3_Sub14.method364() != 0; - if (var1) { - int var2 = this.anIntArray921.length; - - int var3; - for (var3 = 0; var3 < var2; ++var3) { - anIntArray925[var3] = this.anIntArray921[var3]; - } - - var3 = anIntArray928[this.anInt923 - 1]; - int var4 = Class3_Sub26.method513(var3 - 1); - anIntArray927[0] = Class3_Sub14.method368(var4); - anIntArray927[1] = Class3_Sub14.method368(var4); - int var5 = 2; - - for (int var6 = 0; var6 < this.anIntArray922.length; ++var6) { - int var7 = this.anIntArray922[var6]; - int var8 = this.anIntArray926[var7]; - int var9 = this.anIntArray924[var7]; - int var10 = (1 << var9) - 1; - int var11 = 0; - if (var9 > 0) { - var11 = Class3_Sub14.aClass71Array2406[this.anIntArray918[var7]].method1290(); - } - - for (int var12 = 0; var12 < var8; ++var12) { - int var13 = this.anIntArrayArray920[var7][var11 & var10]; - var11 >>>= var9; - anIntArray927[var5++] = var13 >= 0 ? Class3_Sub14.aClass71Array2406[var13].method1290() : 0; - } - } - - return true; - } else { - return false; - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class6.java b/Client/src/main/java/org/runite/client/Class6.java deleted file mode 100644 index 80d97a1ca..000000000 --- a/Client/src/main/java/org/runite/client/Class6.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.runite.client; - -final class Class6 { - - short aShort91; - short aShort92; - short aShort93; - short aShort94; - short aShort95; - short aShort96; - short aShort97; - boolean aBoolean98 = false; - - -} diff --git a/Client/src/main/java/org/runite/client/Class60.java b/Client/src/main/java/org/runite/client/Class60.java deleted file mode 100644 index 8865a9cab..000000000 --- a/Client/src/main/java/org/runite/client/Class60.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.runite.client; - -final class Class60 { - - static int anInt930; - static int anInt932; - static int anInt934; - static int anInt936; - - static void method1208(byte var0, int var1) { - try { - if (-1 != var1) { - if (Unsorted.aBooleanArray1703[var1]) { - Unsorted.aClass153_3361.method2128(var1); - if (null != GameObject.aClass11ArrayArray1834[var1]) { - boolean var2 = true; - - for (int var3 = 0; GameObject.aClass11ArrayArray1834[var1].length > var3; ++var3) { - if (GameObject.aClass11ArrayArray1834[var1][var3] != null) { - if (GameObject.aClass11ArrayArray1834[var1][var3].type == 2) { - var2 = false; - } else { - GameObject.aClass11ArrayArray1834[var1][var3] = null; - } - } - } - - if (var2) { - GameObject.aClass11ArrayArray1834[var1] = null; - } - - Unsorted.aBooleanArray1703[var1] = false; - } - } - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ig.B(" + var0 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class64.java b/Client/src/main/java/org/runite/client/Class64.java deleted file mode 100644 index 74d937275..000000000 --- a/Client/src/main/java/org/runite/client/Class64.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.runite.client; - -public class Class64 { - - public volatile Object anObject974; - public volatile int anInt978 = 0; - public int anInt979; - int anInt975; - Class64 aClass64_976; - Object anObject977; - int anInt980; - - -} diff --git a/Client/src/main/java/org/runite/client/Class65.java b/Client/src/main/java/org/runite/client/Class65.java deleted file mode 100644 index 8c898a6eb..000000000 --- a/Client/src/main/java/org/runite/client/Class65.java +++ /dev/null @@ -1,180 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; -import org.rs09.client.util.ArrayUtils; - -public final class Class65 { - - static LinkedList aLinkedList_983 = new LinkedList(); - static DataBuffer[] aClass3_Sub30Array986 = new DataBuffer[2048]; - public static int anInt987 = 0; - static int currentChunkX; - static int anInt991 = -1; - static AbstractSprite[] aAbstractSpriteArray1825; - - - static int[] method1233(int[] var0) { - try { - if (null == var0) { - return null; - } else { - int[] var2 = new int[var0.length]; - ArrayUtils.arraycopy(var0, 0, var2, 0, var0.length); - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ja.H(" + "{...}" + ',' + 2 + ')'); - } - } - - static int method1234(int var0, int var1, int var2) { - try { - int var4 = var1 / var0; - int var6 = var2 / var0; - int var7 = var2 & var0 - 1; - int var5 = -1 + var0 & var1; - int var8 = Class3_Sub28_Sub3.method543(var4, var6, (byte) -82); - int var9 = Class3_Sub28_Sub3.method543(var4 + 1, var6, (byte) -104); - int var10 = Class3_Sub28_Sub3.method543(var4, 1 + var6, (byte) -100); - int var11 = Class3_Sub28_Sub3.method543(1 + var4, var6 + 1, (byte) -109); - int var12 = TextureOperation39.method275(var8, var9, var5, var0); - int var13 = TextureOperation39.method275(var10, var11, var5, var0); - return TextureOperation39.method275(var12, var13, var7, var0); - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "ja.G(" + var0 + ',' + var1 + ',' + var2 + ',' + 512 + ')'); - } - } - - static void method1235(int var0, int var1, int var2, int var3) { - try { - if (Class36.anInt638 == 1) { - aAbstractSpriteArray1825[Unsorted.anInt2958 / 100].drawAt(-8 + Class70.anInt1053, -8 + Unsorted.anInt4062); - } - - if (Class36.anInt638 == 2) { - aAbstractSpriteArray1825[4 + Unsorted.anInt2958 / 100].drawAt(Class70.anInt1053 + -8, -8 + Unsorted.anInt4062); - } - - TextureOperation10.method347(); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ja.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + (byte) -121 + ')'); - } - } - - static void method1237(int var0) { - try { - WorldListEntry.anInt2626 = 1000 / var0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ja.D(" + var0 + ',' + 1000 + ')'); - } - } - - static void method1239(int var0, int var1, int var2, int var3, int var4, boolean var5) { - try { - if (var0 < 1) { - var0 = 1; - } - - if (1 > var3) { - var3 = 1; - } - - if (HDToolKit.highDetail) { - int var6 = var3 + -334; - if (0 <= var6) { - if (var6 > 100) { - var6 = 100; - } - } else { - var6 = 0; - } - - int var7 = var6 * (CS2Script.aShort3052 + -CS2Script.aShort1444) / 100 + CS2Script.aShort1444; - if (CS2Script.aShort3241 <= var7) { - if (PacketParser.aShort83 < var7) { - var7 = PacketParser.aShort83; - } - } else { - var7 = CS2Script.aShort3241; - } - - int var8 = var7 * var3 * 512 / (var0 * 334); - int var9; - int var10; - short var12; - if (var8 >= ItemDefinition.aShort505) { - if (var8 > TextureOperation18.aShort4038) { - var12 = TextureOperation18.aShort4038; - var7 = var12 * var0 * 334 / (var3 * 512); - if (var7 < CS2Script.aShort3241) { - var7 = CS2Script.aShort3241; - var9 = var12 * var0 * 334 / (512 * var7); - var10 = (-var9 + var3) / 2; - if (var5) { - Class22.resetClipping(); - Toolkit.OPENGL_TOOLKIT.method934(var4, var2, var0, var10, 0); //NOTE we are not checking if the user is in SD or HD here. - Toolkit.OPENGL_TOOLKIT.method934(var4, var2 + (var3 - var10), var0, var10, 0);//Class22 will ALWAYS be HD - } - - var3 -= var10 * 2; - var2 += var10; - } - } - } else { - var12 = ItemDefinition.aShort505; - var7 = 334 * var0 * var12 / (512 * var3); - if (PacketParser.aShort83 < var7) { - var7 = PacketParser.aShort83; - var9 = 512 * var3 * var7 / (334 * var12); - var10 = (var0 - var9) / 2; - if (var5) { - Class22.resetClipping(); - Toolkit.OPENGL_TOOLKIT.method934(var4, var2, var10, var3, 0); //NOTE we are not checking if the user is in SD or HD here. - Toolkit.OPENGL_TOOLKIT.method934(var0 + (var4 - var10), var2, var10, var3, 0);//Class22 will ALWAYS be HD - } - - var4 += var10; - var0 -= 2 * var10; - } - } - - Unsorted.anInt1705 = var7 * var3 / 334; - } - - Class96.anInt1358 = (short) var0; - Unsorted.anInt31 = (short) var3; - Class3_Sub28_Sub3.anInt3564 = var2; - Class163_Sub1.anInt2989 = var4; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "ja.C(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static void method1240() { - try { - FontType.plainFont = null; - Class75_Sub3.aAbstractSpriteArray2656 = null; - aAbstractSpriteArray1825 = null; - Sprites.nameIconsSpriteArray = null; - Class157.aClass3_Sub28_Sub17_Sub1_2000 = null; - Sprites.aSoftwareSpriteArray2140 = null; - Class140_Sub4.aAbstractSpriteArray2839 = null; - Class129_Sub1.aAbstractSpriteArray2690 = null; - NPC.aAbstractSpriteArray3977 = null; - FontType.bold = null; - Class57.aAbstractSprite_895 = null; - Unsorted.aAbstractSpriteArray1136 = null; - FontType.smallFont = null; - Class45.aAbstractSprite_736 = null; - Unsorted.aAbstractSpriteArray996 = null; - TextureOperation8.aAbstractSpriteArray3458 = null; - Class166.aAbstractSpriteArray2072 = null; - TextureOperation2.aAbstractSpriteArray3373 = null; - GameObject.aClass109Array1831 = null; - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ja.E(" + false + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class67.java b/Client/src/main/java/org/runite/client/Class67.java deleted file mode 100644 index 9444aa248..000000000 --- a/Client/src/main/java/org/runite/client/Class67.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; -import org.rs09.client.util.ArrayUtils; - -final class Class67 implements Runnable { - - static ReferenceCache aReferenceCache_1013 = new ReferenceCache(100); - static byte[][][] aByteArrayArrayArray1014; - static RSInterface aClass11_1017; - boolean aBoolean1015 = true; - Object anObject1016 = new Object(); - int anInt1018 = 0; - int[] anIntArray1019 = new int[500]; - int[] anIntArray1020 = new int[500]; - - static int method1258(byte var0) { - try { - TextureOperation23.anInt1780 = 0; - if (var0 != -53) { - method1258((byte) -35); - } - - return TextureOperation23.method251(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "jd.D(" + var0 + ')'); - } - } - - static void method1259(int var0) { - try { - InterfaceWidget var2 = InterfaceWidget.getWidget(12, var0); - var2.a(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "jd.A(" + var0 + ',' + (byte) 109 + ')'); - } - } - - static void method1260(int var0, int var1, RSInterface[] var2) { - try { - for (int var3 = 0; var3 < var2.length; ++var3) { - RSInterface var4 = var2[var3]; - if (null != var4 && var1 == var4.parentId && (!var4.usingScripts || !Client.method51(var4))) { - if (var4.type == 0) { - if (!var4.usingScripts && Client.method51(var4) && var4 != Class107.aClass11_1453) { - continue; - } - - method1260(var0, var4.componentHash, var2); - if (var4.aClass11Array262 != null) { - method1260(23206, var4.componentHash, var4.aClass11Array262); - } - - Class3_Sub31 var5 = TextureOperation23.aHashTable_3208.get(var4.componentHash); - if (var5 != null) { - Class52.method1160(-111, var5.anInt2602); - } - } - - if (var4.type == 6) { - int var6; - if (var4.animationId != -1 || var4.secondAnimationId != -1) { - boolean var9 = Unsorted.method609(var4, var0 + -23173); - if (var9) { - var6 = var4.secondAnimationId; - } else { - var6 = var4.animationId; - } - - if (var6 != -1) { - SequenceDefinition var7 = SequenceDefinition.getAnimationDefinition(var6); - for (var4.anInt267 += Class106.anInt1446; var7.duration[var4.anInt283] < var4.anInt267; Class20.method909(var4)) { - var4.anInt267 -= var7.duration[var4.anInt283]; - ++var4.anInt283; - if (var7.frames.length <= var4.anInt283) { - var4.anInt283 -= var7.anInt1865; - if (var4.anInt283 < 0 || var7.frames.length <= var4.anInt283) { - var4.anInt283 = 0; - } - } - - var4.anInt260 = var4.anInt283 + 1; - if (var7.frames.length <= var4.anInt260) { - var4.anInt260 -= var7.anInt1865; - if (var4.anInt260 < 0 || var7.frames.length <= var4.anInt260) { - var4.anInt260 = -1; - } - } - } - } - } - - if (0 != var4.anInt237 && !var4.usingScripts) { - int var10 = var4.anInt237 >> 16; - var10 *= Class106.anInt1446; - var6 = var4.anInt237 << 16 >> 16; - var4.anInt182 = 2047 & var10 + var4.anInt182; - var6 *= Class106.anInt1446; - var4.anInt308 = var4.anInt308 - -var6 & 0x7FF; - Class20.method909(var4); - } - } - } - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "jd.E(" + var0 + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - static RSString method1261(int var0, int var1, RSString[] var2) { - try { - int var4 = 0; - - for (int var5 = 0; var1 > var5; ++var5) { - if (null == var2[var0 - -var5]) { - var2[var5 + var0] = TextCore.aClass94_3339; - } - - var4 += var2[var5 + var0].length; - } - - byte[] var10 = new byte[var4]; - int var6 = 0; - - for (int var7 = 0; var1 > var7; ++var7) { - RSString var8 = var2[var7 + var0]; - ArrayUtils.arraycopy(var8.buffer, 0, var10, var6, var8.length); - var6 += var8.length; - } - - RSString var11 = new RSString(); - var11.length = var4; - - var11.buffer = var10; - return var11; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "jd.C(" + var0 + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + 2774 + ')'); - } - } - - static int method1262(int var0, int var1) { - try { - if (var0 < 20) { - method1262(15, 87); - } - - return var1 & 127; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "jd.F(" + var0 + ',' + var1 + ')'); - } - } - - public final void run() { - try { - for (; this.aBoolean1015; TimeUtils.sleep(50L)) { - Object var1 = this.anObject1016; - synchronized (var1) { - if (this.anInt1018 < 500) { - this.anIntArray1020[this.anInt1018] = Class126.anInt1676; - this.anIntArray1019[this.anInt1018] = Unsorted.anInt1709; - ++this.anInt1018; - } - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "jd.run()"); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class68.java b/Client/src/main/java/org/runite/client/Class68.java deleted file mode 100644 index d9d44b767..000000000 --- a/Client/src/main/java/org/runite/client/Class68.java +++ /dev/null @@ -1,489 +0,0 @@ -package org.runite.client; - - -import javax.media.opengl.GL; - -final class Class68 { - - private static final float[] aFloatArray1022 = new float[]{0.0F, 0.0F, 0.0F, 1.0F}; - static Class43[] aClass43Array1021; - static int anInt1032 = 0; - private static int[] anIntArray1023; - private static int anInt1024; - private static int anInt1025; - private static boolean[] aBooleanArray1026; - private static int[][][] anIntArrayArrayArray1027; - private static int[] anIntArray1028; - private static int anInt1029; - private static int anInt1030; - private static int anInt1031; - private static boolean[] aBooleanArray1033; - private static int anInt1034; - private static int anInt1035; - private static int anInt1036; - - - static void method1263(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { - if (Class106.aBoolean1441) { - if (var0 == 1 && var5 > 0) { - method1268(var1, var2, var3, var4, var5 - 1, var6); - } else if (var0 == 4 && var5 < anInt1036 - 1) { - method1268(var1, var2, var3, var4, var5 + 1, var6); - } else if (var0 == 8 && var6 > 0) { - method1268(var1, var2, var3, var4, var5, var6 - 1); - } else if (var0 == 2 && var6 < anInt1035 - 1) { - method1268(var1, var2, var3, var4, var5, var6 + 1); - } else if (var0 == 16 && var5 > 0 && var6 < anInt1035 - 1) { - method1268(var1, var2, var3, var4, var5 - 1, var6 + 1); - } else if (var0 == 32 && var5 < anInt1036 - 1 && var6 < anInt1035 - 1) { - method1268(var1, var2, var3, var4, var5 + 1, var6 + 1); - } else if (var0 == 128 && var5 > 0 && var6 > 0) { - method1268(var1, var2, var3, var4, var5 - 1, var6 - 1); - } else if (var0 == 64 && var5 < anInt1036 - 1 && var6 > 0) { - method1268(var1, var2, var3, var4, var5 + 1, var6 - 1); - } - } - } - - static void method1264(Class43 var0) { - if (anInt1032 >= 255) { - System.out.println("Number of lights added exceeds maximum!"); - } else { - aClass43Array1021[anInt1032++] = var0; - } - } - - static void method1265() { - for (int var0 = 0; var0 < 4; ++var0) { - anIntArray1028[var0] = -1; - method1271(var0); - } - - } - - static void method1266(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - if (Class106.aBoolean1441) { - if (anInt1029 != var3 || anInt1031 != var4 || anInt1024 != var5 || anInt1034 != var6 || anInt1025 != var7) { - int var8; - for (var8 = 0; var8 < 4; ++var8) { - aBooleanArray1033[var8] = false; - } - - var8 = 0; - int var9 = 0; - - int var10; - int var11; - label105: - for (var10 = var4; var10 <= var6; ++var10) { - for (var11 = var5; var11 <= var7; ++var11) { - int var12 = anIntArrayArrayArray1027[var3][var10][var11]; - - label101: - while (var12 != 0) { - int var13 = (var12 & 0xFF) - 1; - var12 >>>= 8; - - int var14; - for (var14 = 0; var14 < var9; ++var14) { - if (var13 == anIntArray1023[var14]) { - continue label101; - } - } - - for (var14 = 0; var14 < 4; ++var14) { - if (var13 == anIntArray1028[var14]) { - if (!aBooleanArray1033[var14]) { - aBooleanArray1033[var14] = true; - ++var8; - if (var8 == 4) { - break label105; - } - } - continue label101; - } - } - - anIntArray1023[var9++] = var13; - ++var8; - if (var8 == 4) { - break label105; - } - } - } - } - - var10 = 0; - - while (var10 < var9) { - var11 = 0; - - while (true) { - if (var11 < 4) { - if (aBooleanArray1033[var11]) { - ++var11; - continue; - } - - anIntArray1028[var11] = anIntArray1023[var10]; - aBooleanArray1033[var11] = true; - method1278(var11, aClass43Array1021[anIntArray1023[var10]], var0, var1, var2); - } - - ++var10; - break; - } - } - - for (var10 = 0; var10 < 4; ++var10) { - if (!aBooleanArray1033[var10]) { - anIntArray1028[var10] = -1; - method1271(var10); - } - } - - anInt1029 = var3; - anInt1031 = var4; - anInt1024 = var5; - anInt1034 = var6; - anInt1025 = var7; - } - } - } - - static void method1267() { - anInt1030 = 4; - anInt1036 = 104; - anInt1035 = 104; - anIntArrayArrayArray1027 = new int[anInt1030][anInt1036][anInt1035]; - } - - static void method1268(int var0, int var1, int var2, int var3, int var4, int var5) { - if (Class106.aBoolean1441) { - if (anInt1029 != var3 || anInt1031 != var4 || anInt1024 != var5 || anInt1034 != var4 || anInt1025 != var5) { - int var6; - for (var6 = 0; var6 < 4; ++var6) { - aBooleanArray1033[var6] = false; - } - - var6 = 0; - int var7 = anIntArrayArrayArray1027[var3][var4][var5]; - - int var8; - int var9; - label71: - while (var7 != 0) { - var8 = (var7 & 0xFF) - 1; - var7 >>>= 8; - - for (var9 = 0; var9 < 4; ++var9) { - if (var8 == anIntArray1028[var9]) { - aBooleanArray1033[var9] = true; - continue label71; - } - } - - anIntArray1023[var6++] = var8; - } - - var8 = 0; - - while (var8 < var6) { - var9 = 0; - - while (true) { - if (var9 < 4) { - if (aBooleanArray1033[var9]) { - ++var9; - continue; - } - - anIntArray1028[var9] = anIntArray1023[var8]; - aBooleanArray1033[var9] = true; - method1278(var9, aClass43Array1021[anIntArray1023[var8]], var0, var1, var2); - } - - ++var8; - break; - } - } - - for (var8 = 0; var8 < 4; ++var8) { - if (!aBooleanArray1033[var8]) { - anIntArray1028[var8] = -1; - method1271(var8); - } - } - - anInt1029 = var3; - anInt1031 = var4; - anInt1024 = var5; - anInt1034 = var4; - anInt1025 = var5; - } - } - } - - static void method1269(int var0, boolean var1) { - for (int var2 = 0; var2 < anInt1032; ++var2) { - aClass43Array1021[var2].method1063(var1, var0); - } - - anInt1029 = -1; - anInt1031 = -1; - anInt1024 = -1; - anInt1034 = -1; - anInt1025 = -1; - } - - static void method1270() { - for (int var0 = 0; var0 < anInt1032; ++var0) { - Class43 var1 = aClass43Array1021[var0]; - int var2 = var1.anInt704; - if (var1.aBoolean690) { - var2 = 0; - } - - int var3 = var1.anInt704; - if (var1.aBoolean711) { - var3 = 3; - } - - for (int var4 = var2; var4 <= var3; ++var4) { - int var5 = 0; - int var6 = (var1.anInt708 >> 7) - var1.anInt698; - if (var6 < 0) { - var5 -= var6; - var6 = 0; - } - - int var7 = (var1.anInt708 >> 7) + var1.anInt698; - if (var7 > anInt1035 - 1) { - var7 = anInt1035 - 1; - } - - for (int var8 = var6; var8 <= var7; ++var8) { - short var9 = var1.aShortArray706[var5++]; - int var10 = (var1.anInt703 >> 7) - var1.anInt698 + (var9 >> 8); - int var11 = var10 + (var9 & 0xFF) - 1; - if (var10 < 0) { - var10 = 0; - } - - if (var11 > anInt1036 - 1) { - var11 = anInt1036 - 1; - } - - for (int var12 = var10; var12 <= var11; ++var12) { - int var13 = anIntArrayArrayArray1027[var4][var12][var8]; - if ((var13 & 0xFF) == 0) { - anIntArrayArrayArray1027[var4][var12][var8] = var13 | var0 + 1; - } else if ((var13 & 65280) == 0) { - anIntArrayArrayArray1027[var4][var12][var8] = var13 | var0 + 1 << 8; - } else if ((var13 & 16711680) == 0) { - anIntArrayArrayArray1027[var4][var12][var8] = var13 | var0 + 1 << 16; - } else if ((var13 & -16777216) == 0) { - anIntArrayArrayArray1027[var4][var12][var8] = var13 | var0 + 1 << 24; - } - } - } - } - } - - } - - private static void method1271(int var0) { - if (aBooleanArray1026[var0]) { - aBooleanArray1026[var0] = false; - int var1 = var0 + 16384 + 4; - GL var2 = HDToolKit.gl; - var2.glDisable(var1); - } - } - - static void method1272(int var0, int var1, int var2, int var3, int var4) { - if (Class106.aBoolean1441) { - label44: - for (int var5 = 0; var5 < 4; ++var5) { - if (anIntArray1028[var5] != -1) { - int var6 = anIntArrayArrayArray1027[var0][var1][var2]; - - int var7; - while (var6 != 0) { - var7 = (var6 & 0xFF) - 1; - var6 >>>= 8; - if (var7 == anIntArray1028[var5]) { - continue label44; - } - } - - var6 = anIntArrayArrayArray1027[var0][var3][var4]; - - while (var6 != 0) { - var7 = (var6 & 0xFF) - 1; - var6 >>>= 8; - if (var7 == anIntArray1028[var5]) { - continue label44; - } - } - } - - anIntArray1028[var5] = -1; - method1271(var5); - } - - } - } - - static void method1273() { - aClass43Array1021 = null; - anIntArray1028 = null; - aBooleanArray1026 = null; - anIntArray1023 = null; - aBooleanArray1033 = null; - anIntArrayArrayArray1027 = null; - } - - static void method1275() { - GL var0 = HDToolKit.gl; - - int var1; - for (var1 = 0; var1 < 4; ++var1) { - int var2 = 16388 + var1; - var0.glLightfv(var2, 4608, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, 0); - var0.glLightf(var2, 4616, 0.0F); - var0.glLightf(var2, 4615, 0.0F); - } - - for (var1 = 0; var1 < 4; ++var1) { - anIntArray1028[var1] = -1; - method1271(var1); - } - - } - - static void method1276() { - aClass43Array1021 = new Class43[255]; - anIntArray1028 = new int[4]; - aBooleanArray1026 = new boolean[4]; - anIntArray1023 = new int[4]; - aBooleanArray1033 = new boolean[4]; - anIntArrayArrayArray1027 = new int[anInt1030][anInt1036][anInt1035]; - } - - static void method1277(Class3_Sub2[][][] var2) { - if (Class106.aBoolean1441) { - GL var3 = HDToolKit.gl; - Unsorted.method551(0, 0); - HDToolKit.method1856(0); - HDToolKit.method1823(); - HDToolKit.bindTexture2D(HDToolKit.anInt1810); - var3.glDepthMask(false); - HDToolKit.method1837(false); - var3.glBlendFunc(774, 1); - var3.glFogfv(2918, new float[]{0.0F, 0.0F, 0.0F, 0.0F}, 0); - var3.glTexEnvi(8960, '\u8580', '\u8576'); - var3.glTexEnvi(8960, '\u8590', 770); - - label69: - for (int var4 = 0; var4 < anInt1032; ++var4) { - Class43 var5 = aClass43Array1021[var4]; - int var6 = var5.anInt704; - if (var5.aBoolean696) { - --var6; - } - - if (var5.aClass37_712 != null) { - int var7 = 0; - int var8 = (var5.anInt708 >> 7) - var5.anInt698; - int var9 = (var5.anInt708 >> 7) + var5.anInt698; - if (var9 >= Class126.anInt1665) { - var9 = Class126.anInt1665 - 1; - } - - if (var8 < Unsorted.anInt3603) { - var7 += Unsorted.anInt3603 - var8; - var8 = Unsorted.anInt3603; - } - - int var10 = var8; - - while (var10 <= var9) { - short var11 = var5.aShortArray706[var7++]; - int var12 = (var5.anInt703 >> 7) - var5.anInt698 + (var11 >> 8); - int var13 = var12 + (var11 & 0xFF) - 1; - if (var12 < Class163_Sub1_Sub1.anInt4006) { - var12 = Class163_Sub1_Sub1.anInt4006; - } - - if (var13 >= Unsorted.anInt67) { - var13 = Unsorted.anInt67 - 1; - } - - int var14 = var12; - - while (true) { - if (var14 <= var13) { - Class3_Sub2 var15 = null; - if (var6 >= 0) { - var15 = var2[var6][var14][var10]; - } - - if (var6 >= 0 && (var15 == null || !var15.aBoolean2222)) { - ++var14; - continue; - } - - HDToolKit.method1832(201.5F - (float) var5.anInt704 * 50.0F - 1.5F); - var3.glTexEnvfv(8960, 8705, new float[]{0.0F, 0.0F, 0.0F, var5.aFloat707}, 0); - var5.aClass37_712.method1021(); - continue label69; - } - - ++var10; - break; - } - } - } - } - - var3.glTexEnvi(8960, '\u8580', 5890); - var3.glTexEnvi(8960, '\u8590', 768); - var3.glBlendFunc(770, 771); - var3.glDepthMask(true); - var3.glFogfv(2918, Class92.fogColor, 0); - var3.glEnableClientState('\u8078'); - HDToolKit.method1846(); - } - } - - private static void method1278(int var0, Class43 var1, int var2, int var3, int var4) { - int var5 = var0 + 16384 + 4; - GL var6 = HDToolKit.gl; - if (!aBooleanArray1026[var0]) { - var6.glEnable(var5); - aBooleanArray1026[var0] = true; - } - - var6.glLightf(var5, 4617, var1.aFloat710); - var6.glLightfv(var5, 4609, var1.aFloatArray717, 0); - aFloatArray1022[0] = (float) (var1.anInt703 - var2); - aFloatArray1022[1] = (float) (var1.anInt697 - var3); - aFloatArray1022[2] = (float) (var1.anInt708 - var4); - var6.glLightfv(var5, 4611, aFloatArray1022, 0); - } - - static void method1279() { - anInt1032 = 0; - - for (int var0 = 0; var0 < anInt1030; ++var0) { - for (int var1 = 0; var1 < anInt1036; ++var1) { - for (int var2 = 0; var2 < anInt1035; ++var2) { - anIntArrayArrayArray1027[var0][var1][var2] = 0; - } - } - } - - } - -} diff --git a/Client/src/main/java/org/runite/client/Class7.java b/Client/src/main/java/org/runite/client/Class7.java deleted file mode 100644 index 90d2b2496..000000000 --- a/Client/src/main/java/org/runite/client/Class7.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.runite.client; - -import org.rs09.client.filestore.resources.configs.enums.EnumDefinition; -import org.rs09.client.filestore.resources.configs.enums.EnumDefinitionProvider; - -public final class Class7 implements Interface4 { - - public static int anInt2166 = 0; - static CacheIndex skinsReferenceIndex; - static int anInt2161 = -1; - static int anInt2162; - static short[] aShortArray2167 = new short[]{(short) 30, (short) 6, (short) 31, (short) 29, (short) 10, (short) 44, (short) 37, (short) 57}; - - - public final RSString method20(int var1, int[] var2, int var3, long var4) { - try { - if (var1 == 0) { - EnumDefinition var6 = EnumDefinitionProvider.provide(var2[0]); - return var6.getString((int) var4); - } else if (var1 == 1 || var1 == 10) { - ItemDefinition var8 = ItemDefinition.getItemDefinition((int) var4); - return var8.name; - } else { - return var1 != 6 && var1 != 7 && 11 != var1 ? (var3 != 4936 ? null : null) : EnumDefinitionProvider.provide(var2[0]).getString((int) var4); - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "af.A(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + var4 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class70.java b/Client/src/main/java/org/runite/client/Class70.java deleted file mode 100644 index af4615c58..000000000 --- a/Client/src/main/java/org/runite/client/Class70.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.runite.client; - -final class Class70 { - - static RSString[] aClass94Array1046 = new RSString[200]; - static int frameHeight; - static double aDouble1050 = -1.0D; - static int anInt1053 = 0; - int anInt1045; - long aLong1048 = 0L; - GameObject aClass140_1049; - GameObject aClass140_1052; - int anInt1054; - int anInt1055; - int anInt1057; - int anInt1059; - - - static void method1285(CacheIndex var0, int var2, int var5) { - try { - Class101.aClass153_1423 = var0; - Unsorted.anInt154 = 1; - TextureOperation36.anInt3423 = var5; - Class132.anInt1741 = 0; - TextureOperation8.anInt3463 = var2; - Unsorted.aBoolean2311 = false; - - GraphicDefinition.anInt546 = 10000; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "jh.D(" + (var0 != null ? "{...}" : "null") + ',' + false + ',' + var2 + ',' + 0 + ',' + false + ',' + var5 + ')'); - } - } - - static void method1286(int var0, ObjectDefinition var2, int var3, NPC var4, int var5, int var6, Player var7) { - try { - - Class3_Sub9 var8 = new Class3_Sub9(); - var8.anInt2308 = var0 * 128; - var8.anInt2326 = 128 * var5; - var8.anInt2314 = var6; - if (null != var2) { - var8.anIntArray2333 = var2.anIntArray1539; - var8.anInt2328 = var2.anInt1484 * 128; - var8.anInt2325 = var2.anInt1515; - var8.aClass111_2320 = var2; - var8.anInt2332 = var2.anInt1512; - var8.anInt2310 = var2.anInt1518; - int var9 = var2.SizeX; - int var10 = var2.SizeY; - if (var3 == 1 || 3 == var3) { - var9 = var2.SizeY; - var10 = var2.SizeX; - } - - var8.anInt2307 = (var10 + var0) * 128; - var8.anInt2321 = (var5 + var9) * 128; - if (var2.ChildrenIds != null) { - var8.aBoolean2329 = true; - var8.method134(); - } - - if (null != var8.anIntArray2333) { - var8.anInt2316 = var8.anInt2310 - -((int) (Math.random() * (double) (-var8.anInt2310 + var8.anInt2325))); - } - - Unsorted.aLinkedList_78.method1215(var8); - } else if (null != var4) { - var8.aClass140_Sub4_Sub2_2324 = var4; - NPCDefinition var12 = var4.definition; - if (null != var12.childNPCs) { - var8.aBoolean2329 = true; - var12 = var12.method1471((byte) -112); - } - - if (var12 != null) { - var8.anInt2307 = 128 * (var12.size + var0); - var8.anInt2321 = 128 * (var5 - -var12.size); - var8.anInt2332 = method1232(var4); - var8.anInt2328 = 128 * var12.anInt1291; - } - - Unsorted.aLinkedList_1242.method1215(var8); - } else if (null != var7) { - var8.aClass140_Sub4_Sub1_2327 = var7; - var8.anInt2321 = (var7.getSize() + var5) * 128; - var8.anInt2307 = 128 * (var7.getSize() + var0); - var8.anInt2332 = Class81.method1398(var7); - var8.anInt2328 = 128 * var7.anInt3969; - Unsorted.aHashTable_4046.put(var7.displayName.toLong(), var8); - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "jh.C(" + var0 + ',' + false + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + (var4 != null ? "{...}" : "null") + ',' + var5 + ',' + var6 + ',' + (var7 != null ? "{...}" : "null") + ')'); - } - } - - static Class3_Sub28_Sub17_Sub1 method1287(int var0, CacheIndex var2, CacheIndex var3) { - try { - // System.out.println("Class 70 " + var0); - if (Class75_Sub4.method1351(var3, 0, var0)) { - - return TextureOperation.method163(var2.getFile(var0, 0)); - } else { - return null; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "jh.B(" + var0 + ',' + 0 + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ',' + -1 + ')'); - } - } - - static int method1232(NPC var0) { - try { - NPCDefinition var2 = var0.definition; - if (null != var2.childNPCs) { - var2 = var2.method1471((byte) -108); - if (var2 == null) { - return -1; - } - } - - int var3 = var2.anInt1293; - RenderAnimationDefinition var4 = var0.getRenderAnimationType(); - if (var0.anInt2764 == var4.stand_animation) { - var3 = var2.anInt1262; - } else if (var4.run_anim != var0.anInt2764 && var4.run_follow_full_turn_anim != var0.anInt2764 && var0.anInt2764 != var4.run_follow_cw_turn_anim && var0.anInt2764 != var4.run_follow_ccw_turn_anim) { - if (var0.anInt2764 == var4.slow_walk_anim || var0.anInt2764 == var4.slow_walk_follow_full_turn_anim || var4.slow_walk_follow_cw_turn_anim == var0.anInt2764 || var4.slow_walk_follow_ccw_turn_anim == var0.anInt2764) { - var3 = var2.anInt1290; - } - } else { - var3 = var2.anInt1276; - } - - return var3; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ij.B(" + (var0 != null ? "{...}" : "null") + ',' + -1 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class71.java b/Client/src/main/java/org/runite/client/Class71.java deleted file mode 100644 index 455183210..000000000 --- a/Client/src/main/java/org/runite/client/Class71.java +++ /dev/null @@ -1,215 +0,0 @@ -package org.runite.client; - -final class Class71 { - - private final int[] anIntArray1061; - private final int anInt1065; - int anInt1063; - private float[][] aFloatArrayArray1062; - private int[] anIntArray1066; - - - Class71() { - Class3_Sub14.method368(24); - this.anInt1063 = Class3_Sub14.method368(16); - this.anInt1065 = Class3_Sub14.method368(24); - this.anIntArray1061 = new int[this.anInt1065]; - boolean var1 = Class3_Sub14.method364() != 0; - int var2; - int var3; - int var5; - if (var1) { - var2 = 0; - - for (var3 = Class3_Sub14.method368(5) + 1; var2 < this.anInt1065; ++var3) { - int var4 = Class3_Sub14.method368(Class3_Sub26.method513(this.anInt1065 - var2)); - - for (var5 = 0; var5 < var4; ++var5) { - this.anIntArray1061[var2++] = var3; - } - } - } else { - boolean var14 = Class3_Sub14.method364() != 0; - - for (var3 = 0; var3 < this.anInt1065; ++var3) { - if (var14 && Class3_Sub14.method364() == 0) { - this.anIntArray1061[var3] = 0; - } else { - this.anIntArray1061[var3] = Class3_Sub14.method368(5) + 1; - } - } - } - - this.method1289(); - var2 = Class3_Sub14.method368(4); - if (var2 > 0) { - float var16 = Class3_Sub14.method358(Class3_Sub14.method368(32)); - float var15 = Class3_Sub14.method358(Class3_Sub14.method368(32)); - var5 = Class3_Sub14.method368(4) + 1; - boolean var6 = Class3_Sub14.method364() != 0; - int var7; - if (var2 == 1) { - var7 = method1291(this.anInt1065, this.anInt1063); - } else { - var7 = this.anInt1065 * this.anInt1063; - } - - int[] anIntArray1064 = new int[var7]; - - int var8; - for (var8 = 0; var8 < var7; ++var8) { - anIntArray1064[var8] = Class3_Sub14.method368(var5); - } - - this.aFloatArrayArray1062 = new float[this.anInt1065][this.anInt1063]; - float var9; - int var10; - int var11; - if (var2 == 1) { - for (var8 = 0; var8 < this.anInt1065; ++var8) { - var9 = 0.0F; - var10 = 1; - - for (var11 = 0; var11 < this.anInt1063; ++var11) { - int var12 = var8 / var10 % var7; - float var13 = (float) anIntArray1064[var12] * var15 + var16 + var9; - this.aFloatArrayArray1062[var8][var11] = var13; - if (var6) { - var9 = var13; - } - - var10 *= var7; - } - } - } else { - for (var8 = 0; var8 < this.anInt1065; ++var8) { - var9 = 0.0F; - var10 = var8 * this.anInt1063; - - for (var11 = 0; var11 < this.anInt1063; ++var11) { - float var17 = (float) anIntArray1064[var10] * var15 + var16 + var9; - this.aFloatArrayArray1062[var8][var11] = var17; - if (var6) { - var9 = var17; - } - - ++var10; - } - } - } - } - - } - - private static int method1291(int var0, int var1) { - int var2; - for (var2 = (int) Math.pow(var0, 1.0D / (double) var1) + 1; Class140_Sub6.method2028(var1, var2) > var0; --var2) { - } - - return var2; - } - - final float[] method1288() { - return this.aFloatArrayArray1062[this.method1290()]; - } - - private void method1289() { - int[] var1 = new int[this.anInt1065]; - int[] var2 = new int[33]; - - int var3; - int var4; - int var5; - int var6; - int var7; - int var8; - int var10; - for (var3 = 0; var3 < this.anInt1065; ++var3) { - var4 = this.anIntArray1061[var3]; - if (var4 != 0) { - var5 = 1 << 32 - var4; - var6 = var2[var4]; - var1[var3] = var6; - int var9; - if ((var6 & var5) == 0) { - var7 = var6 | var5; - - for (var8 = var4 - 1; var8 >= 1; --var8) { - var9 = var2[var8]; - if (var9 != var6) { - break; - } - - var10 = 1 << 32 - var8; - if ((var9 & var10) != 0) { - var2[var8] = var2[var8 - 1]; - break; - } - - var2[var8] = var9 | var10; - } - } else { - var7 = var2[var4 - 1]; - } - - var2[var4] = var7; - - for (var8 = var4 + 1; var8 <= 32; ++var8) { - var9 = var2[var8]; - if (var9 == var6) { - var2[var8] = var7; - } - } - } - } - - this.anIntArray1066 = new int[8]; - int var11 = 0; - - for (var3 = 0; var3 < this.anInt1065; ++var3) { - var4 = this.anIntArray1061[var3]; - if (var4 != 0) { - var5 = var1[var3]; - var6 = 0; - - for (var7 = 0; var7 < var4; ++var7) { - var8 = Integer.MIN_VALUE >>> var7; - if ((var5 & var8) == 0) { - ++var6; - } else { - if (this.anIntArray1066[var6] == 0) { - this.anIntArray1066[var6] = var11; - } - - var6 = this.anIntArray1066[var6]; - } - - if (var6 >= this.anIntArray1066.length) { - int[] var12 = new int[this.anIntArray1066.length * 2]; - - for (var10 = 0; var10 < this.anIntArray1066.length; ++var10) { - var12[var10] = this.anIntArray1066[var10]; - } - - this.anIntArray1066 = var12; - } - - } - - this.anIntArray1066[var6] = ~var3; - if (var6 >= var11) { - var11 = var6 + 1; - } - } - } - - } - - final int method1290() { - int var1; - for (var1 = 0; this.anIntArray1066[var1] >= 0; var1 = Class3_Sub14.method364() != 0 ? this.anIntArray1066[var1] : var1 + 1) { - } - - return ~this.anIntArray1066[var1]; - } -} diff --git a/Client/src/main/java/org/runite/client/Class72.java b/Client/src/main/java/org/runite/client/Class72.java deleted file mode 100644 index 597928a12..000000000 --- a/Client/src/main/java/org/runite/client/Class72.java +++ /dev/null @@ -1,205 +0,0 @@ -package org.runite.client; - -final class Class72 { - - static int[] anIntArray3045; - static int anInt1672 = 0; - static int anInt1071 = 0; - static boolean aBoolean1074 = false; - GameObject aClass140_1067; - int anInt1068; - GameObject aClass140_1069; - GameObject aClass140_1073; - int anInt1075; - int anInt1077; - int anInt1078; - long aLong1079; - - - static void method1293() { - try { - if (!Client.paramAdvertisementSuppressed && Class44.paramModeWhere != 2) { - try { - TextCore.aClass94_38.method1577(Client.clientInstance); - } catch (Throwable var2) { - } - - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "jj.G(" + true + ')'); - } - } - - static void method1294() { - anInt1672 = 0; - - label188: - for (int var0 = 0; var0 < Class3_Sub4.anInt2249; ++var0) { - Class113 var1 = Class3_Sub28_Sub8.aClass113Array3610[var0]; - int var2; - if (anIntArray3045 != null) { - for (var2 = 0; var2 < anIntArray3045.length; ++var2) { - if (anIntArray3045[var2] != -1000000 && (var1.anInt1544 <= anIntArray3045[var2] || var1.anInt1548 <= anIntArray3045[var2]) && (var1.anInt1562 <= Class52.anIntArray859[var2] || var1.anInt1545 <= Class52.anIntArray859[var2]) && (var1.anInt1562 >= Unsorted.anIntArray1083[var2] || var1.anInt1545 >= Unsorted.anIntArray1083[var2]) && (var1.anInt1560 <= Class75_Sub4.anIntArray2663[var2] || var1.anInt1550 <= Class75_Sub4.anIntArray2663[var2]) && (var1.anInt1560 >= Unsorted.anIntArray39[var2] || var1.anInt1550 >= Unsorted.anIntArray39[var2])) { - continue label188; - } - } - } - - int var3; - int var4; - boolean var5; - int var6; - if (var1.anInt1554 == 1) { - var2 = var1.anInt1553 - Class97.anInt1375 + TextureOperation8.anInt3466; - if (var2 >= 0 && var2 <= TextureOperation8.anInt3466 + TextureOperation8.anInt3466) { - var3 = var1.anInt1563 - Class145.anInt3340 + TextureOperation8.anInt3466; - if (var3 < 0) { - var3 = 0; - } - - var4 = var1.anInt1566 - Class145.anInt3340 + TextureOperation8.anInt3466; - if (var4 > TextureOperation8.anInt3466 + TextureOperation8.anInt3466) { - var4 = TextureOperation8.anInt3466 + TextureOperation8.anInt3466; - } - - var5 = false; - - while (var3 <= var4) { - if (Class23.aBooleanArrayArray457[var2][var3++]) { - var5 = true; - break; - } - } - - if (var5) { - var6 = Class145.anInt2697 - var1.anInt1562; - if (var6 > 32) { - var1.anInt1564 = 1; - } else { - if (var6 >= -32) { - continue; - } - - var1.anInt1564 = 2; - var6 = -var6; - } - - var1.anInt1555 = (var1.anInt1560 - TextureOperation13.anInt3363 << 8) / var6; - var1.anInt1551 = (var1.anInt1550 - TextureOperation13.anInt3363 << 8) / var6; - var1.anInt1561 = (var1.anInt1544 - Unsorted.anInt3657 << 8) / var6; - var1.anInt1565 = (var1.anInt1548 - Unsorted.anInt3657 << 8) / var6; - Class145.aClass113Array1895[anInt1672++] = var1; - } - } - } else if (var1.anInt1554 == 2) { - var2 = var1.anInt1563 - Class145.anInt3340 + TextureOperation8.anInt3466; - if (var2 >= 0 && var2 <= TextureOperation8.anInt3466 + TextureOperation8.anInt3466) { - var3 = var1.anInt1553 - Class97.anInt1375 + TextureOperation8.anInt3466; - if (var3 < 0) { - var3 = 0; - } - - var4 = var1.anInt1547 - Class97.anInt1375 + TextureOperation8.anInt3466; - if (var4 > TextureOperation8.anInt3466 + TextureOperation8.anInt3466) { - var4 = TextureOperation8.anInt3466 + TextureOperation8.anInt3466; - } - - var5 = false; - - while (var3 <= var4) { - if (Class23.aBooleanArrayArray457[var3++][var2]) { - var5 = true; - break; - } - } - - if (var5) { - var6 = TextureOperation13.anInt3363 - var1.anInt1560; - if (var6 > 32) { - var1.anInt1564 = 3; - } else { - if (var6 >= -32) { - continue; - } - - var1.anInt1564 = 4; - var6 = -var6; - } - - var1.anInt1549 = (var1.anInt1562 - Class145.anInt2697 << 8) / var6; - var1.anInt1557 = (var1.anInt1545 - Class145.anInt2697 << 8) / var6; - var1.anInt1561 = (var1.anInt1544 - Unsorted.anInt3657 << 8) / var6; - var1.anInt1565 = (var1.anInt1548 - Unsorted.anInt3657 << 8) / var6; - Class145.aClass113Array1895[anInt1672++] = var1; - } - } - } else if (var1.anInt1554 == 4) { - var2 = var1.anInt1544 - Unsorted.anInt3657; - if (var2 > 128) { - var3 = var1.anInt1563 - Class145.anInt3340 + TextureOperation8.anInt3466; - if (var3 < 0) { - var3 = 0; - } - - var4 = var1.anInt1566 - Class145.anInt3340 + TextureOperation8.anInt3466; - if (var4 > TextureOperation8.anInt3466 + TextureOperation8.anInt3466) { - var4 = TextureOperation8.anInt3466 + TextureOperation8.anInt3466; - } - - if (var3 <= var4) { - int var10 = var1.anInt1553 - Class97.anInt1375 + TextureOperation8.anInt3466; - if (var10 < 0) { - var10 = 0; - } - - var6 = var1.anInt1547 - Class97.anInt1375 + TextureOperation8.anInt3466; - if (var6 > TextureOperation8.anInt3466 + TextureOperation8.anInt3466) { - var6 = TextureOperation8.anInt3466 + TextureOperation8.anInt3466; - } - - boolean var7 = false; - - label114: - for (int var8 = var10; var8 <= var6; ++var8) { - for (int var9 = var3; var9 <= var4; ++var9) { - if (Class23.aBooleanArrayArray457[var8][var9]) { - var7 = true; - break label114; - } - } - } - - if (var7) { - var1.anInt1564 = 5; - var1.anInt1549 = (var1.anInt1562 - Class145.anInt2697 << 8) / var2; - var1.anInt1557 = (var1.anInt1545 - Class145.anInt2697 << 8) / var2; - var1.anInt1555 = (var1.anInt1560 - TextureOperation13.anInt3363 << 8) / var2; - var1.anInt1551 = (var1.anInt1550 - TextureOperation13.anInt3363 << 8) / var2; - Class145.aClass113Array1895[anInt1672++] = var1; - } - } - } - } - } - - } - - static float[] method1297() { - try { - float var1 = Class92.method1514() + Class92.getLightingModelAmbient(); - int var2 = Class92.screenColorRgb(); - float var3 = (float) (255 & var2 >> 16) / 255.0F; - MouseListeningClass.aFloatArray1919[3] = 1.0F; - float var4 = (float) (('\uff59' & var2) >> 8) / 255.0F; - float var6 = 0.58823526F; - float var5 = (float) (255 & var2) / 255.0F; - MouseListeningClass.aFloatArray1919[2] = Unsorted.aFloatArray1934[2] * var5 * var6 * var1; - MouseListeningClass.aFloatArray1919[0] = Unsorted.aFloatArray1934[0] * var3 * var6 * var1; - MouseListeningClass.aFloatArray1919[1] = var1 * var6 * var4 * Unsorted.aFloatArray1934[1]; - return MouseListeningClass.aFloatArray1919; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "jj.A(" + (byte) -50 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class74.java b/Client/src/main/java/org/runite/client/Class74.java deleted file mode 100644 index 7b2fe1080..000000000 --- a/Client/src/main/java/org/runite/client/Class74.java +++ /dev/null @@ -1,538 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; -import org.rs09.client.rendering.java.JavaToolkit; - -// TODO LDToolkit -public final class Class74 { - - static int[] anIntArray1097; - static int[] anIntArray1098; - - static JavaToolkit toolkit = Toolkit.JAVA_TOOLKIT; - - @Deprecated - private static int[] getBuffer() { - return Toolkit.JAVA_TOOLKIT.getBuffer(); - } - - public static void method1310() { - anIntArray1097 = null; - anIntArray1098 = null; - } - - private static void method1313(int var0, int var1) { - if (var0 >= Toolkit.JAVA_TOOLKIT.clipLeft && var1 >= Toolkit.JAVA_TOOLKIT.clipTop && var0 < Toolkit.JAVA_TOOLKIT.clipRight && var1 < Toolkit.JAVA_TOOLKIT.clipBottom) { - getBuffer()[var0 + var1 * Toolkit.JAVA_TOOLKIT.width] = 16776960; - } - } - - public static void method1314(int[] var0, int[] var1) { - if (var0.length == Toolkit.JAVA_TOOLKIT.clipBottom - Toolkit.JAVA_TOOLKIT.clipTop && var1.length == Toolkit.JAVA_TOOLKIT.clipBottom - Toolkit.JAVA_TOOLKIT.clipTop) { - anIntArray1097 = var0; - anIntArray1098 = var1; - } else { - throw new IllegalArgumentException(); - } - } - - static void method1315(int var0, int var1, int var2, int var3, int var4, int var5) { - method1321(var0, var1, var2, var4, var5); - method1321(var0, var1 + var3 - 1, var2, var4, var5); - if (var3 >= 3) { - method1327(var0, var1 + 1, var3 - 2, var4, var5); - method1327(var0 + var2 - 1, var1 + 1, var3 - 2, var4, var5); - } - - } - - public static void setClipping(int[] var0) { - Toolkit.JAVA_TOOLKIT.clipLeft = var0[0]; - Toolkit.JAVA_TOOLKIT.clipTop = var0[1]; - Toolkit.JAVA_TOOLKIT.clipRight = var0[2]; - Toolkit.JAVA_TOOLKIT.clipBottom = var0[3]; - method1310(); - } - - static void setBuffer(int[] buffer, int width, int height) { - Toolkit.JAVA_TOOLKIT.setBuffer(buffer); - Toolkit.JAVA_TOOLKIT.width = width; - Toolkit.JAVA_TOOLKIT.height = height; - setClipping(0, 0, width, height); - } - - static void method1320() { - int var0 = 0; - - int var1; - for (var1 = Toolkit.JAVA_TOOLKIT.width * Toolkit.JAVA_TOOLKIT.height - 7; var0 < var1; getBuffer()[var0++] = 0) { - getBuffer()[var0++] = 0; - getBuffer()[var0++] = 0; - getBuffer()[var0++] = 0; - getBuffer()[var0++] = 0; - getBuffer()[var0++] = 0; - getBuffer()[var0++] = 0; - getBuffer()[var0++] = 0; - } - - for (var1 += 7; var0 < var1; getBuffer()[var0++] = 0) { - } - - } - - private static void method1321(int var0, int var1, int var2, int var3, int var4) { - if (var1 >= Toolkit.JAVA_TOOLKIT.clipTop && var1 < Toolkit.JAVA_TOOLKIT.clipBottom) { - if (var0 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var2 -= Toolkit.JAVA_TOOLKIT.clipLeft - var0; - var0 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - if (var0 + var2 > Toolkit.JAVA_TOOLKIT.clipRight) { - var2 = Toolkit.JAVA_TOOLKIT.clipRight - var0; - } - - int var5 = 256 - var4; - int var6 = (var3 >> 16 & 0xFF) * var4; - int var7 = (var3 >> 8 & 0xFF) * var4; - int var8 = (var3 & 0xFF) * var4; - int var12 = var0 + var1 * Toolkit.JAVA_TOOLKIT.width; - - for (int var13 = 0; var13 < var2; ++var13) { - int var9 = (getBuffer()[var12] >> 16 & 0xFF) * var5; - int var10 = (getBuffer()[var12] >> 8 & 0xFF) * var5; - int var11 = (getBuffer()[var12] & 0xFF) * var5; - int var14 = (var6 + var9 >> 8 << 16) + (var7 + var10 >> 8 << 8) + (var8 + var11 >> 8); - getBuffer()[var12++] = var14; - } - - } - } - - static void method1322(int var0, int var1, int var2, int var3, int var4, int var5) { - int var6 = var2 - var0; - int var7 = var3 - var1; - int var8 = var6 >= 0 ? var6 : -var6; - int var9 = var7 >= 0 ? var7 : -var7; - int var10 = var8; - if (var8 < var9) { - var10 = var9; - } - - if (var10 != 0) { - int var11 = (var6 << 16) / var10; - int var12 = (var7 << 16) / var10; - if (var12 <= var11) { - var11 = -var11; - } else { - var12 = -var12; - } - - int var13 = var5 * var12 >> 17; - int var14 = var5 * var12 + 1 >> 17; - int var15 = var5 * var11 >> 17; - int var16 = var5 * var11 + 1 >> 17; - var0 -= Class51.method1139(); - var1 -= Class51.method1153(); - int var17 = var0 + var13; - int var18 = var0 - var14; - int var19 = var0 + var6 - var14; - int var20 = var0 + var6 + var13; - int var21 = var1 + var15; - int var22 = var1 - var16; - int var23 = var1 + var7 - var16; - int var24 = var1 + var7 + var15; - Class51.method1148(var17, var18, var19); - Class51.method1144(var21, var22, var23, var17, var18, var19, var4); - Class51.method1148(var17, var19, var20); - Class51.method1144(var21, var23, var24, var17, var19, var20, var4); - } - } - - public static void setClipping(int left, int top, int right, int bottom) { - if (left < 0) { - left = 0; - } - - if (top < 0) { - top = 0; - } - - if (right > Toolkit.JAVA_TOOLKIT.width) { - right = Toolkit.JAVA_TOOLKIT.width; - } - - if (bottom > Toolkit.JAVA_TOOLKIT.height) { - bottom = Toolkit.JAVA_TOOLKIT.height; - } - - Toolkit.JAVA_TOOLKIT.clipLeft = left; - Toolkit.JAVA_TOOLKIT.clipTop = top; - Toolkit.JAVA_TOOLKIT.clipRight = right; - Toolkit.JAVA_TOOLKIT.clipBottom = bottom; - method1310(); - } - - static void method1325(int[] var0) { - var0[0] = Toolkit.JAVA_TOOLKIT.clipLeft; - var0[1] = Toolkit.JAVA_TOOLKIT.clipTop; - var0[2] = Toolkit.JAVA_TOOLKIT.clipRight; - var0[3] = Toolkit.JAVA_TOOLKIT.clipBottom; - } - - static void method1326(int var0, int var1, int var2, int var3) { - if (Toolkit.JAVA_TOOLKIT.clipLeft < var0) { - Toolkit.JAVA_TOOLKIT.clipLeft = var0; - } - - if (Toolkit.JAVA_TOOLKIT.clipTop < var1) { - Toolkit.JAVA_TOOLKIT.clipTop = var1; - } - - if (Toolkit.JAVA_TOOLKIT.clipRight > var2) { - Toolkit.JAVA_TOOLKIT.clipRight = var2; - } - - if (Toolkit.JAVA_TOOLKIT.clipBottom > var3) { - Toolkit.JAVA_TOOLKIT.clipBottom = var3; - } - - method1310(); - } - - private static void method1327(int var0, int var1, int var2, int var3, int var4) { - if (var0 >= Toolkit.JAVA_TOOLKIT.clipLeft && var0 < Toolkit.JAVA_TOOLKIT.clipRight) { - if (var1 < Toolkit.JAVA_TOOLKIT.clipTop) { - var2 -= Toolkit.JAVA_TOOLKIT.clipTop - var1; - var1 = Toolkit.JAVA_TOOLKIT.clipTop; - } - - if (var1 + var2 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var2 = Toolkit.JAVA_TOOLKIT.clipBottom - var1; - } - - int var5 = 256 - var4; - int var6 = (var3 >> 16 & 0xFF) * var4; - int var7 = (var3 >> 8 & 0xFF) * var4; - int var8 = (var3 & 0xFF) * var4; - int var12 = var0 + var1 * Toolkit.JAVA_TOOLKIT.width; - - for (int var13 = 0; var13 < var2; ++var13) { - int var9 = (getBuffer()[var12] >> 16 & 0xFF) * var5; - int var10 = (getBuffer()[var12] >> 8 & 0xFF) * var5; - int var11 = (getBuffer()[var12] & 0xFF) * var5; - int var14 = (var6 + var9 >> 8 << 16) + (var7 + var10 >> 8 << 8) + (var8 + var11 >> 8); - getBuffer()[var12] = var14; - var12 += Toolkit.JAVA_TOOLKIT.width; - } - - } - } - - static void method1328(int var0, int var1, int var2, int var3, int var4) { - var2 -= var0; - var3 -= var1; - if (var3 == 0) { - if (var2 >= 0) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var0, var1, var2 + 1, var4); - } else { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var0 + var2, var1, -var2 + 1, var4); - } - - } else if (var2 == 0) { - if (var3 >= 0) { - Toolkit.JAVA_TOOLKIT.drawVerticalLine(var0, var1, var3 + 1, var4); - } else { - Toolkit.JAVA_TOOLKIT.drawVerticalLine(var0, var1 + var3, -var3 + 1, var4); - } - - } else { - if (var2 + var3 < 0) { - var0 += var2; - var2 = -var2; - var1 += var3; - var3 = -var3; - } - - int var5; - int var6; - if (var2 > var3) { - var1 <<= 16; - var1 += 32768; - var3 <<= 16; - var5 = (int) Math.floor((double) var3 / (double) var2 + 0.5D); - var2 += var0; - if (var0 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var1 += var5 * (Toolkit.JAVA_TOOLKIT.clipLeft - var0); - var0 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - if (var2 >= Toolkit.JAVA_TOOLKIT.clipRight) { - var2 = Toolkit.JAVA_TOOLKIT.clipRight - 1; - } - - while (var0 <= var2) { - var6 = var1 >> 16; - if (var6 >= Toolkit.JAVA_TOOLKIT.clipTop && var6 < Toolkit.JAVA_TOOLKIT.clipBottom) { - getBuffer()[var0 + var6 * Toolkit.JAVA_TOOLKIT.width] = var4; - } - - var1 += var5; - ++var0; - } - } else { - var0 <<= 16; - var0 += 32768; - var2 <<= 16; - var5 = (int) Math.floor((double) var2 / (double) var3 + 0.5D); - var3 += var1; - if (var1 < Toolkit.JAVA_TOOLKIT.clipTop) { - var0 += var5 * (Toolkit.JAVA_TOOLKIT.clipTop - var1); - var1 = Toolkit.JAVA_TOOLKIT.clipTop; - } - - if (var3 >= Toolkit.JAVA_TOOLKIT.clipBottom) { - var3 = Toolkit.JAVA_TOOLKIT.clipBottom - 1; - } - - while (var1 <= var3) { - var6 = var0 >> 16; - if (var6 >= Toolkit.JAVA_TOOLKIT.clipLeft && var6 < Toolkit.JAVA_TOOLKIT.clipRight) { - getBuffer()[var6 + var1 * Toolkit.JAVA_TOOLKIT.width] = var4; - } - - var0 += var5; - ++var1; - } - } - - } - } - - private static void method1329(int var0, int var1, int var2) { - if (var2 == 0) { - method1313(var0, var1); - } else { - if (var2 < 0) { - var2 = -var2; - } - - int var4 = var1 - var2; - if (var4 < Toolkit.JAVA_TOOLKIT.clipTop) { - var4 = Toolkit.JAVA_TOOLKIT.clipTop; - } - - int var5 = var1 + var2 + 1; - if (var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 = Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var6 = var4; - int var7 = var2 * var2; - int var8 = 0; - int var9 = var1 - var4; - int var10 = var9 * var9; - int var11 = var10 - var9; - if (var1 > var5) { - var1 = var5; - } - - int var12; - int var13; - int var14; - int var15; - while (var6 < var1) { - while (var11 <= var7 || var10 <= var7) { - var10 += var8 + var8; - var11 += var8++ + var8; - } - - var12 = var0 - var8 + 1; - if (var12 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var12 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - var13 = var0 + var8; - if (var13 > Toolkit.JAVA_TOOLKIT.clipRight) { - var13 = Toolkit.JAVA_TOOLKIT.clipRight; - } - - var14 = var12 + var6 * Toolkit.JAVA_TOOLKIT.width; - - for (var15 = var12; var15 < var13; ++var15) { - getBuffer()[var14++] = 16776960; - } - - ++var6; - var10 -= var9-- + var9; - var11 -= var9 + var9; - } - - var8 = var2; - var9 = var6 - var1; - var11 = var9 * var9 + var7; - var10 = var11 - var2; - - for (var11 -= var9; var6 < var5; var10 += var9++ + var9) { - while (var11 > var7 && var10 > var7) { - var11 -= var8-- + var8; - var10 -= var8 + var8; - } - - var12 = var0 - var8; - if (var12 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var12 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - var13 = var0 + var8; - if (var13 > Toolkit.JAVA_TOOLKIT.clipRight - 1) { - var13 = Toolkit.JAVA_TOOLKIT.clipRight - 1; - } - - var14 = var12 + var6 * Toolkit.JAVA_TOOLKIT.width; - - for (var15 = var12; var15 <= var13; ++var15) { - getBuffer()[var14++] = 16776960; - } - - ++var6; - var11 += var9 + var9; - } - - } - } - - static void method1330(int var0, int var1, int var2, int var4) { - if (var4 != 0) { - if (var4 == 256) { - method1329(var0, var1, var2); - } else { - if (var2 < 0) { - var2 = -var2; - } - - int var5 = 256 - var4; - int var6 = (16776960 >> 16 & 0xFF) * var4; - int var7 = (16776960 >> 8 & 0xFF) * var4; - int var8 = 0; - int var12 = var1 - var2; - if (var12 < Toolkit.JAVA_TOOLKIT.clipTop) { - var12 = Toolkit.JAVA_TOOLKIT.clipTop; - } - - int var13 = var1 + var2 + 1; - if (var13 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var13 = Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var14 = var12; - int var15 = var2 * var2; - int var16 = 0; - int var17 = var1 - var12; - int var18 = var17 * var17; - int var19 = var18 - var17; - if (var1 > var13) { - var1 = var13; - } - - int var9; - int var10; - int var11; - int var21; - int var20; - int var23; - int var22; - int var24; - while (var14 < var1) { - while (var19 <= var15 || var18 <= var15) { - var18 += var16 + var16; - var19 += var16++ + var16; - } - - var20 = var0 - var16 + 1; - if (var20 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var20 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - var21 = var0 + var16; - if (var21 > Toolkit.JAVA_TOOLKIT.clipRight) { - var21 = Toolkit.JAVA_TOOLKIT.clipRight; - } - - var22 = var20 + var14 * Toolkit.JAVA_TOOLKIT.width; - - for (var23 = var20; var23 < var21; ++var23) { - var9 = (getBuffer()[var22] >> 16 & 0xFF) * var5; - var10 = (getBuffer()[var22] >> 8 & 0xFF) * var5; - var11 = (getBuffer()[var22] & 0xFF) * var5; - var24 = (var6 + var9 >> 8 << 16) + (var7 + var10 >> 8 << 8) + (var8 + var11 >> 8); - getBuffer()[var22++] = var24; - } - - ++var14; - var18 -= var17-- + var17; - var19 -= var17 + var17; - } - - var16 = var2; - var17 = -var17; - var19 = var17 * var17 + var15; - var18 = var19 - var2; - - for (var19 -= var17; var14 < var13; var18 += var17++ + var17) { - while (var19 > var15 && var18 > var15) { - var19 -= var16-- + var16; - var18 -= var16 + var16; - } - - var20 = var0 - var16; - if (var20 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var20 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - var21 = var0 + var16; - if (var21 > Toolkit.JAVA_TOOLKIT.clipRight - 1) { - var21 = Toolkit.JAVA_TOOLKIT.clipRight - 1; - } - - var22 = var20 + var14 * Toolkit.JAVA_TOOLKIT.width; - - for (var23 = var20; var23 <= var21; ++var23) { - var9 = (getBuffer()[var22] >> 16 & 0xFF) * var5; - var10 = (getBuffer()[var22] >> 8 & 0xFF) * var5; - var11 = (getBuffer()[var22] & 0xFF) * var5; - var24 = (var6 + var9 >> 8 << 16) + (var7 + var10 >> 8 << 8) + (var8 + var11 >> 8); - getBuffer()[var22++] = var24; - } - - ++var14; - var19 += var17 + var17; - } - - } - } - } - - public static void resetClipping() { - Toolkit.JAVA_TOOLKIT.clipLeft = 0; - Toolkit.JAVA_TOOLKIT.clipTop = 0; - Toolkit.JAVA_TOOLKIT.clipRight = Toolkit.JAVA_TOOLKIT.width; - Toolkit.JAVA_TOOLKIT.clipBottom = Toolkit.JAVA_TOOLKIT.height; - method1310(); - } - - public static void method1332(int var0, int var1, int[] var3, int[] var4) { - int var5 = var0 + var1 * Toolkit.JAVA_TOOLKIT.width; - - for (var1 = 0; var1 < var3.length; ++var1) { - int var6 = var5 + var3[var1]; - - for (var0 = -var4[var1]; var0 < 0; ++var0) { - getBuffer()[var6++] = 0; - } - - var5 += Toolkit.JAVA_TOOLKIT.width; - } - - } - -} diff --git a/Client/src/main/java/org/runite/client/Class75.java b/Client/src/main/java/org/runite/client/Class75.java deleted file mode 100644 index dcc31e69f..000000000 --- a/Client/src/main/java/org/runite/client/Class75.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.runite.client; - -abstract class Class75 { - - static long aLong1102 = 0L; - static Class3_Sub28_Sub5[] aClass3_Sub28_Sub5Array1103 = new Class3_Sub28_Sub5[14]; - static int anInt1105; - static int[] anIntArray1107 = new int[]{0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3}; - static int anInt1108 = 0; - static int anInt1109 = 0; - int anInt1101; - int anInt1104; - int anInt1106; - - - Class75(int var1, int var2, int var3) { - try { - this.anInt1104 = var2; - this.anInt1106 = var3; - this.anInt1101 = var1; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "kf.(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static Class25 method1336(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if (var3 != null) { - for (int var4 = 0; var4 < var3.anInt2223; ++var4) { - Class25 var5 = var3.aClass25Array2221[var4]; - if ((var5.aLong498 >> 29 & 3L) == 2L && var5.anInt483 == var1 && var5.anInt478 == var2) { - return var5; - } - } - - } - return null; - } - - static void method1339(RSString var0) { - try { - for (Class3_Sub28_Sub3 var2 = (Class3_Sub28_Sub3) Class134.aLinkedList_1758.method1222(); var2 != null; var2 = (Class3_Sub28_Sub3) Class134.aLinkedList_1758.method1221()) { - if (var2.aClass94_3561.equalsString(var0)) { - Unsorted.aClass3_Sub28_Sub3_2600 = var2; - return; - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "kf.G(" + (var0 != null ? "{...}" : "null") + ',' + 0 + ')'); - } - } - - abstract void method1335(int var1, int var2, int var3); - - abstract void method1337(int var1, boolean var2, int var3); - - abstract void method1341(int var2, int var3); - -} diff --git a/Client/src/main/java/org/runite/client/Class75_Sub1.java b/Client/src/main/java/org/runite/client/Class75_Sub1.java deleted file mode 100644 index 28e9a6856..000000000 --- a/Client/src/main/java/org/runite/client/Class75_Sub1.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.runite.client; - -final class Class75_Sub1 extends Class75 { - - private final int anInt2629; - private final int anInt2630; - private final int anInt2632; - private final int anInt2635; - - - final void method1335(int var1, int var2, int var3) { - try { - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ci.D(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - final void method1341(int var2, int var3) { - try { - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ci.A(" + 2 + ',' + var2 + ',' + var3 + ')'); - } - } - - static void method1342(int[] var0, int[] var1, Player var2, int[] var4) { - try { - int var5 = 0; - - while(var1.length > var5) { - int var6 = var1[var5]; - int var7 = var4[var5]; - int var8 = var0[var5]; - - for(int var9 = 0; var7 != 0 && var2.aClass145Array2809.length > var9; var7 >>>= 1) { - if((1 & var7) != 0) { - if(var6 == -1) { - var2.aClass145Array2809[var9] = null; - } else { - SequenceDefinition var10 = SequenceDefinition.getAnimationDefinition(var6); - int var11 = var10.delayType; - Class145 var12 = var2.aClass145Array2809[var9]; - if(var12 != null) { - if(var12.animationId == var6) { - if(var11 == 0) { - var12 = var2.aClass145Array2809[var9] = null; - } else if (1 == var11) { - var12.anInt1894 = 0; - var12.anInt1891 = 1; - var12.anInt1893 = 0; - var12.anInt1900 = var8; - var12.anInt1897 = 0; - Unsorted.method1470(var2.zAxis, var10, var2.xAxis, var2 == Class102.player, 0); - } else if (var11 == 2) { - var12.anInt1894 = 0; - } - } else if(var10.forcedPriority >= SequenceDefinition.getAnimationDefinition(var12.animationId).forcedPriority) { - var12 = var2.aClass145Array2809[var9] = null; - } - } - - if(null == var12) { - var12 = var2.aClass145Array2809[var9] = new Class145(); - var12.animationId = var6; - var12.anInt1891 = 1; - var12.anInt1897 = 0; - var12.anInt1900 = var8; - var12.anInt1893 = 0; - var12.anInt1894 = 0; - Unsorted.method1470(var2.zAxis, var10, var2.xAxis, var2 == Class102.player, 0); - } - } - } - - ++var9; - } - - ++var5; - } - - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "ci.B(" + (var0 != null?"{...}":"null") + ',' + (var1 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + (byte) -113 + ',' + (var4 != null?"{...}":"null") + ')'); - } - } - - final void method1337(int var1, boolean var2, int var3) { - try { - int var4 = var3 * this.anInt2629 >> 12; - if(!var2) { - this.method1335(67, -82, -112); - } - - int var5 = this.anInt2635 * var3 >> 12; - int var6 = var1 * this.anInt2630 >> 12; - int var7 = var1 * this.anInt2632 >> 12; - TextureOperation25.method330(this.anInt1104, -111, var7, var4, var6, var5); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ci.E(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - Class75_Sub1(int var1, int var2, int var3, int var4, int var5, int var6) { - super(-1, var5, var6); - - try { - this.anInt2632 = var4; - this.anInt2630 = var2; - this.anInt2629 = var1; - this.anInt2635 = var3; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ci.(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class75_Sub2.java b/Client/src/main/java/org/runite/client/Class75_Sub2.java deleted file mode 100644 index 0b5b487ef..000000000 --- a/Client/src/main/java/org/runite/client/Class75_Sub2.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.runite.client; - -final class Class75_Sub2 extends Class75 { - - private final int anInt2636; - static Class33 aClass33_2637; - static Class3_Sub2[][][] aClass3_Sub2ArrayArrayArray2638; - static int[] anIntArray2639; - private int anInt2644; - private final int anInt2646; - private final int anInt2647; - static Class33 aClass33_2648; - - - final void method1341(int var2, int var3) { - try { - int var4 = this.anInt2646 * var2 >> 12; - int var5 = var2 * this.anInt2636 >> 12; - int var6 = this.anInt2644 * var3 >> 12; - int var7 = this.anInt2647 * var3 >> 12; - Class95.method1584(this.anInt1101, var7, var4, var6, var5); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "kc.A(" + 2 + ',' + var2 + ',' + var3 + ')'); - } - } - - static AbstractSprite method1344(CacheIndex var0, int var1) { - try { - return !Class140_Sub7.method2029((byte)-121, var0, var1)?null:Class43.method1062(99); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "kc.C(" + (var0 != null?"{...}":"null") + ',' + var1 + ')'); - } - } - - final void method1337(int var1, boolean var2, int var3) { - try { - if (!var2) { - this.method1337(-7, false, 66); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "kc.E(" + var1 + ',' + ',' + var3 + ')'); - } - } - - final void method1335(int var1, int var2, int var3) { - try { - int var4 = var2 * this.anInt2646 >> 12; - if(var3 != 4898) { - this.anInt2644 = -39; - } - - int var7 = this.anInt2647 * var1 >> 12; - int var6 = this.anInt2644 * var1 >> 12; - int var5 = this.anInt2636 * var2 >> 12; - TextureOperation24.method223(this.anInt1106, var4, var6, var7, this.anInt1104, this.anInt1101, var5); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "kc.D(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - Class75_Sub2(int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - super(var5, var6, var7); - - try { - this.anInt2647 = var4; - this.anInt2646 = var1; - this.anInt2644 = var2; - this.anInt2636 = var3; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "kc.(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class75_Sub3.java b/Client/src/main/java/org/runite/client/Class75_Sub3.java deleted file mode 100644 index a0164ca0b..000000000 --- a/Client/src/main/java/org/runite/client/Class75_Sub3.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.runite.client; - -final class Class75_Sub3 extends Class75 { - - private final int anInt2649; - private final int anInt2650; - private final int anInt2652; - private final int anInt2654; - private int anInt2655; - static AbstractSprite[] aAbstractSpriteArray2656; - private final int anInt2657; - static int anInt2658; - private final int anInt2659; - private final int anInt2661; - - final void method1337(int var1, boolean var2, int var3) { - try { - int var4 = var3 * this.anInt2654 >> 12; - int var6 = var3 * this.anInt2661 >> 12; - int var7 = this.anInt2649 * var1 >> 12; - int var5 = this.anInt2657 * var1 >> 12; - int var8 = this.anInt2655 * var3 >> 12; - int var9 = var1 * this.anInt2652 >> 12; - int var10 = this.anInt2659 * var3 >> 12; - int var11 = var1 * this.anInt2650 >> 12; - Class108.method1652(var6, var10, var9, var11, var5, var4, var8, var7, this.anInt1104); - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "re.E(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static void sweepClientStartupReferenceCache() { - try { - Class163_Sub2_Sub1.aReferenceCache_4015.sweep(5); - Class44.aReferenceCache_725.sweep(5); - Class140_Sub4.aReferenceCache_2792.sweep(5); - Unsorted.aReferenceCache_21.sweep(5); - ObjectDefinition.aReferenceCache_1401.sweep(5); - Unsorted.aReferenceCache_4051.sweep(5); - ObjectDefinition.aReferenceCache_1965.sweep(5); - Unsorted.aReferenceCache_4043.sweep(5); - CS2Script.aReferenceCache_2442.sweep(5); - Class154.aReferenceCache_1964.sweep(5); - QuickChatDefinition.aReferenceCache_3572.sweep(5); - Class143.aReferenceCache_1874.sweep(5); - Class67.aReferenceCache_1013.sweep(5); - Texture.aReferenceCache_1146.sweep(5); - Class159.aReferenceCache_2016.sweep(5); - VarpHelpers.varbitLookup.sweep(5); - Class136.aReferenceCache_1772.sweep(5); - RenderAnimationDefinition.aReferenceCache_1955.sweep(5); - TextureOperation25.aReferenceCache_3412.sweep(5); - TextureOperation2.aReferenceCache_3369.sweep(5); - Class128.aReferenceCache_1683.sweep(5); - Unsorted.aReferenceCache_684.sweep(5); - Class163_Sub1.aReferenceCache_2984.sweep(5); - KeyboardListener.aReferenceCache_1911.sweep(5); - Unsorted.aReferenceCache_1131.sweep(5); - Class114.aReferenceCache_1569.sweep(50); - Class3_Sub15.aReferenceCache_2428.sweep(50); - Unsorted.aReferenceCache_743.sweep(50); - LinkedList.aReferenceCache_939.sweep(5); - Class158_Sub1.aReferenceCache_2982.sweep(5); - TextureOperation1.aReferenceCache_3130.sweep(5); - Unsorted.aReferenceCache_1135.sweep(5); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "re.K(" + 26211 + ')'); - } - } - - final void method1341(int var2, int var3) { - try { - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "re.A(" + 2 + ',' + var2 + ',' + var3 + ')'); - } - } - - final void method1335(int var1, int var2, int var3) { - try { - if(var3 != 4898) { - this.anInt2655 = -64; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "re.D(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - Class75_Sub3(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10) { - super(-1, var9, var10); - - try { - this.anInt2657 = var2; - this.anInt2659 = var7; - this.anInt2655 = var5; - this.anInt2654 = var1; - this.anInt2650 = var8; - this.anInt2652 = var6; - this.anInt2649 = var4; - this.anInt2661 = var3; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "re.(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var10 + ')'); - } - } - - static AbstractSprite[] method1347(int var0) { - try { - if(var0 == -26802) { - AbstractSprite[] var1 = new AbstractSprite[Class95.anInt1338]; - - for(int var2 = 0; Class95.anInt1338 > var2; ++var2) { - byte[] var4 = Class163_Sub1.aByteArrayArray2987[var2]; - int var3 = Unsorted.anIntArray3076[var2] * Class140_Sub7.anIntArray2931[var2]; - if(Class45.aBooleanArray3272[var2]) { - int[] var6 = new int[var3]; - byte[] var5 = Class163_Sub3.aByteArrayArray3005[var2]; - - for(int var7 = 0; var3 > var7; ++var7) { - var6[var7] = TextureOperation3.bitwiseOr(TextureOperation38.spritePalette[Unsorted.bitwiseAnd(var4[var7], 255)], Unsorted.bitwiseAnd(-16777216, var5[var7] << 24)); - } - - if(HDToolKit.highDetail) { - var1[var2] = new Class3_Sub28_Sub16_Sub1_Sub1(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], var6); - } else { - var1[var2] = new Class3_Sub28_Sub16_Sub2_Sub1(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], var6); - } - } else { - int[] var9 = new int[var3]; - - for(int var10 = 0; var3 > var10; ++var10) { - var9[var10] = TextureOperation38.spritePalette[Unsorted.bitwiseAnd(var4[var10], 255)]; - } - - if(HDToolKit.highDetail) { - var1[var2] = new HDSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], var9); - } else { - var1[var2] = new SoftwareSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], var9); - } - } - } - - Class39.method1035((byte)106); - return var1; - } else { - return null; - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "re.B(" + var0 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class75_Sub4.java b/Client/src/main/java/org/runite/client/Class75_Sub4.java deleted file mode 100644 index 303ff7f49..000000000 --- a/Client/src/main/java/org/runite/client/Class75_Sub4.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -public final class Class75_Sub4 extends Class75 { - - static int[] anIntArray2663; - static int[] anIntArray2664; - private final int anInt2666; - public static int[] incomingPacketSizes = new int[]{-1, 0, 8, 0, 2, 0, 0, 0, 0, 12, 0, 1, 0, 3, 7, 0, 15, 6, 0, 0, 4, 7, -2, -1, 2, 0, 2, 8, 0, 0, 0, 0, -2, 5, 0, 0, 8, 3, 6, 0, 0, 0, -1, 0, -1, 0, 0, 6, -2, 0, 12, 0, 0, 0, -1, -2, 10, 0, 0, 0, 3, 0, -1, 0, 0, 5, 6, 0, 0, 8, -1, -1, 0, 8, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 6, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 0, -2, 0, 0, 0, 0, 0, 12, 2, 0, -2, -2, 20, 0, 0, 10, 0, 15, 0, -1, 0, 8, -2, 0, 0, 0, 8, 0, 12, 0, 0, 7, 0, 0, 0, 0, 0, -1, -1, 0, 4, 5, 0, 0, 0, 6, 0, 0, 0, 0, 8, 9, 0, 0, 0, 2, -1, 0, -2, 0, 4, 14, 0, 0, 0, 24, 0, -2, 5, 0, 0, 0, 10, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 2, 1, 0, 0, 2, -1, 1, 0, 0, 0, 0, 14, 0, 0, 0, 0, 10, 5, 0, 0, 0, 0, 0, -2, 0, 0, 9, 0, 0, 8, 0, 0, 0, 0, -2, 6, 0, 0, 0, -2, 0, 3, 0, 1, 7, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 3, 0, 0}; - private final int anInt2669; - static int anInt2670 = 0; - private final int anInt2671; - private final int anInt2672; - - - final void method1337(int var1, boolean var2, int var3) { - try { - int var5 = var3 * this.anInt2666 >> 12; - int var7 = this.anInt2669 * var1 >> 12; - int var4 = this.anInt2671 * var3 >> 12; - int var6 = this.anInt2672 * var1 >> 12; - if(var2) { - Class145.method2072(this.anInt1104, var4, var6, var5, var7, this.anInt1106); - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ta.E(" + var1 + ',' + ',' + var3 + ')'); - } - } - - Class75_Sub4(int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - super(var5, var6, var7); - - try { - this.anInt2672 = var2; - this.anInt2666 = var3; - this.anInt2671 = var1; - this.anInt2669 = var4; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "ta.(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - final void method1341(int var2, int var3) { - try { - int var4 = this.anInt2671 * var2 >> 12; - int var5 = var2 * this.anInt2666 >> 12; - int var6 = var3 * this.anInt2672 >> 12; - int var7 = var3 * this.anInt2669 >> 12; - LinkableRSString.method730(var4, this.anInt1101, var7, var5, var6); - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ta.A(" + 2 + ',' + var2 + ',' + var3 + ')'); - } - } - - static boolean method1351(CacheIndex var0, int var1, int archiveId) { - try { - byte[] var4 = var0.getFile(archiveId, var1); - if(var4 == null) { - return false; - } else { - Class45.method1082(var4, 98); - return true; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ta.N(" + (var0 != null?"{...}":"null") + ',' + var1 + ',' + archiveId + ',' + -30901 + ')'); - } - } - - final void method1335(int var1, int var2, int var3) { - try { - if(var3 == 4898) { - int var4 = var2 * this.anInt2671 >> 12; - int var6 = this.anInt2672 * var1 >> 12; - int var5 = var2 * this.anInt2666 >> 12; - int var7 = this.anInt2669 * var1 >> 12; - TextureOperation21.method194(this.anInt1106, var7, this.anInt1101, this.anInt1104, var6, var5, var4); - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ta.D(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static void method1352(int var0, boolean var1, int var3, int var4) { - try { - if(Unsorted.loadInterface(var3)) { - Class158.method2183(-1, var1, var4, var0, GameObject.aClass11ArrayArray1834[var3]); - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ta.K(" + var0 + ',' + var1 + ',' + -1 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method1353(SoftwareSprite[] var0, CacheIndex var2) { - try { - WorldMap.aClass153_3210 = var2; - GameObject.aSoftwareSpriteArray1839 = var0; - Class3_Sub24_Sub4.aBooleanArray3503 = new boolean[GameObject.aSoftwareSpriteArray1839.length]; - Class134.aLinkedList_1758.method1211(-68); - int var3 = WorldMap.aClass153_3210.getArchiveForName(TextCore.aClass94_2304); - int[] var4 = WorldMap.aClass153_3210.getFileIds(var3); - - for(int var5 = 0; var5 < Objects.requireNonNull(var4).length; ++var5) { - Class134.aLinkedList_1758.method1215(Class124.method1747(new DataBuffer(WorldMap.aClass153_3210.getFile(var3, var4[var5])))); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ta.B(" + (var0 != null?"{...}":"null") + ',' + -11931 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - static void method1354(int var0, int var1, int var3, int var4) { - try { - if(var4 >= Class159.anInt2020 && var4 <= Class57.anInt902) { - var0 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var0, Class101.anInt1425); - var3 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var3, Class101.anInt1425); - TextureOperation14.method320(var1, var4, var3, (byte)-123, var0); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ta.L(" + var0 + ',' + var1 + ',' + true + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method1355() { - try { - Class3_Sub25.aClass129_2552.method1770(); - - int var1; - for(var1 = 0; var1 < 32; ++var1) { - Class163_Sub1.aLongArray2986[var1] = 0L; - } - - for(var1 = 0; var1 < 32; ++var1) { - Class134.aLongArray1766[var1] = 0L; - } - - Class133.anInt1754 = 0; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ta.O(" + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class77.java b/Client/src/main/java/org/runite/client/Class77.java deleted file mode 100644 index 1eae06792..000000000 --- a/Client/src/main/java/org/runite/client/Class77.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.runite.client; - -final class Class77 { - - static int anInt1111; - - - static LDIndexedSprite method1364() { - try { - LDIndexedSprite var1 = new LDIndexedSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[0], Unsorted.anIntArray2591[0], Class140_Sub7.anIntArray2931[0], Unsorted.anIntArray3076[0], Class163_Sub1.aByteArrayArray2987[0], TextureOperation38.spritePalette); - - Class39.method1035((byte) 127); - return var1; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "kh.B(" + (byte) 82 + ')'); - } - } - - static void method1367(int var0, int var1, int var2, byte var3, int var4, int var5) { - try { - TextureOperation18.method282(Class38.anIntArrayArray663[var2++], var5, 92, var1, var0); - TextureOperation18.method282(Class38.anIntArrayArray663[var4--], var5, 97, var1, var0); - if (var3 >= 23) { - for (int var6 = var2; var4 >= var6; ++var6) { - int[] var7 = Class38.anIntArrayArray663[var6]; - var7[var5] = var7[var1] = var0; - } - - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "kh.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static void method1368() { - try { - TextureOperation12.outgoingBuffer.putOpcode(104); - TextureOperation12.outgoingBuffer.writeLong(0L); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "kh.D(" + -90 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class78.java b/Client/src/main/java/org/runite/client/Class78.java deleted file mode 100644 index 38429cfb3..000000000 --- a/Client/src/main/java/org/runite/client/Class78.java +++ /dev/null @@ -1,199 +0,0 @@ -package org.runite.client; - -final class Class78 { - - private static final byte[] aByteArray1117 = new byte[]{(byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 1, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 2, (byte) 0, (byte) 1, (byte) 2, (byte) 1, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0}; - private final DataBuffer aClass3_Sub30_1113 = new DataBuffer(null); - int[] anIntArray1114; - int anInt1116; - private int[] anIntArray1115; - private int[] anIntArray1118; - private long aLong1119; - private int[] anIntArray1120; - private int anInt1121; - - - public Class78() { - } - - Class78(byte[] var1) { - this.method1380(var1); - } - - final long method1370(int var1) { - return this.aLong1119 + (long) var1 * (long) this.anInt1121; - } - - final boolean method1371() { - int var1 = this.anIntArray1115.length; - - for (int var2 = 0; var2 < var1; ++var2) { - if (this.anIntArray1115[var2] >= 0) { - return false; - } - } - - return true; - } - - final void method1372(long var1) { - this.aLong1119 = var1; - int var3 = this.anIntArray1115.length; - - for (int var4 = 0; var4 < var3; ++var4) { - this.anIntArray1114[var4] = 0; - this.anIntArray1120[var4] = 0; - this.aClass3_Sub30_1113.index = this.anIntArray1118[var4]; - this.method1377(var4); - this.anIntArray1115[var4] = this.aClass3_Sub30_1113.index; - } - - } - - final boolean method1373() { - return this.aClass3_Sub30_1113.buffer != null; - } - - final int method1374() { - return this.anIntArray1115.length; - } - - final int method1375(int var1) { - return this.method1379(var1); - } - - final void method1376(int var1) { - this.aClass3_Sub30_1113.index = this.anIntArray1115[var1]; - } - - final void method1377(int var1) { - int var2 = this.aClass3_Sub30_1113.method741(); - this.anIntArray1114[var1] += var2; - } - - private int method1378(int var1, int var2) { - int var4; - if (var2 == 255) { - int var7 = this.aClass3_Sub30_1113.readUnsignedByte(); - var4 = this.aClass3_Sub30_1113.method741(); - if (var7 == 47) { - this.aClass3_Sub30_1113.index += var4; - return 1; - } else if (var7 == 81) { - int var5 = this.aClass3_Sub30_1113.readMedium(); - var4 -= 3; - int var6 = this.anIntArray1114[var1]; - this.aLong1119 += (long) var6 * (long) (this.anInt1121 - var5); - this.anInt1121 = var5; - this.aClass3_Sub30_1113.index += var4; - return 2; - } else { - this.aClass3_Sub30_1113.index += var4; - return 3; - } - } else { - byte var3 = aByteArray1117[var2 - 128]; - var4 = var2; - if (var3 >= 1) { - var4 = var2 | this.aClass3_Sub30_1113.readUnsignedByte() << 8; - } - - if (var3 >= 2) { - var4 |= this.aClass3_Sub30_1113.readUnsignedByte() << 16; - } - - return var4; - } - } - - private int method1379(int var1) { - byte var2 = this.aClass3_Sub30_1113.buffer[this.aClass3_Sub30_1113.index]; - int var5; - if (var2 < 0) { - var5 = var2 & 0xFF; - this.anIntArray1120[var1] = var5; - ++this.aClass3_Sub30_1113.index; - } else { - var5 = this.anIntArray1120[var1]; - } - - if (var5 == 240 || var5 == 247) { - int var3 = this.aClass3_Sub30_1113.method741(); - if (var5 == 247 && var3 > 0) { - int var4 = this.aClass3_Sub30_1113.buffer[this.aClass3_Sub30_1113.index] & 0xFF; - if (var4 >= 241 && var4 <= 243 || var4 == 246 || var4 == 248 || var4 >= 250 && var4 <= 252 || var4 == 254) { - ++this.aClass3_Sub30_1113.index; - this.anIntArray1120[var1] = var4; - return this.method1378(var1, var4); - } - } - - this.aClass3_Sub30_1113.index += var3; - return 0; - } else { - return this.method1378(var1, var5); - } - } - - final void method1380(byte[] var1) { - this.aClass3_Sub30_1113.buffer = var1; - this.aClass3_Sub30_1113.index = 10; - int var2 = this.aClass3_Sub30_1113.readUnsignedShort(); - this.anInt1116 = this.aClass3_Sub30_1113.readUnsignedShort(); - this.anInt1121 = 500000; - this.anIntArray1118 = new int[var2]; - - int var3; - int var5; - for (var3 = 0; var3 < var2; this.aClass3_Sub30_1113.index += var5) { - int var4 = this.aClass3_Sub30_1113.readInt(); - var5 = this.aClass3_Sub30_1113.readInt(); - if (var4 == 1297379947) { - this.anIntArray1118[var3] = this.aClass3_Sub30_1113.index; - ++var3; - } - } - - this.aLong1119 = 0L; - this.anIntArray1115 = new int[var2]; - - for (var3 = 0; var3 < var2; ++var3) { - this.anIntArray1115[var3] = this.anIntArray1118[var3]; - } - - this.anIntArray1114 = new int[var2]; - this.anIntArray1120 = new int[var2]; - } - - final void method1381(int var1) { - this.anIntArray1115[var1] = this.aClass3_Sub30_1113.index; - } - - final int method1382() { - int var1 = this.anIntArray1115.length; - int var2 = -1; - int var3 = Integer.MAX_VALUE; - - for (int var4 = 0; var4 < var1; ++var4) { - if (this.anIntArray1115[var4] >= 0 && this.anIntArray1114[var4] < var3) { - var2 = var4; - var3 = this.anIntArray1114[var4]; - } - } - - return var2; - } - - final void method1383() { - this.aClass3_Sub30_1113.buffer = null; - this.anIntArray1118 = null; - this.anIntArray1115 = null; - this.anIntArray1114 = null; - this.anIntArray1120 = null; - } - - final void method1384() { - this.aClass3_Sub30_1113.index = -1; - } - -} diff --git a/Client/src/main/java/org/runite/client/Class8.java b/Client/src/main/java/org/runite/client/Class8.java deleted file mode 100644 index f7a9f4580..000000000 --- a/Client/src/main/java/org/runite/client/Class8.java +++ /dev/null @@ -1,266 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -public final class Class8 { - - static RSInterface[] aClass11Array1836; - static int anInt101; - public static int anInt104 = 0; - static CacheIndex configurationReferenceCache; - private final Js5Worker aJs5Worker_102; - private final CacheResourceWorker aCacheResourceWorker_103; - private DataBuffer aClass3_Sub30_99; - private Js5ResourceRequest aJs5ResourceRequest_100; - private Class151_Sub1[] aClass151_Sub1Array107; - - - Class8(Js5Worker var1, CacheResourceWorker var2) { - try { - this.aCacheResourceWorker_103 = var2; - this.aJs5Worker_102 = var1; - if (!this.aJs5Worker_102.priorityRequestsFull()) { - this.aJs5ResourceRequest_100 = this.aJs5Worker_102.request(255, 255, (byte) 0, true); - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "al.(" + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - static void method840(ObjectDefinition var0, byte var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - int var9 = 3 & var3; - if (var1 >= -1) { - TextCore.aClass94_106 = null; - } - - int var10; - int var11; - if (var9 == 1 || var9 == 3) { - var10 = var0.SizeY; - var11 = var0.SizeX; - } else { - var11 = var0.SizeY; - var10 = var0.SizeX; - } - - int var14; - int var15; - if (var7 - -var11 > 104) { - var15 = 1 + var7; - var14 = var7; - } else { - var14 = var7 - -(var11 >> 1); - var15 = var7 - -(1 + var11 >> 1); - } - - int var16 = (var6 << 7) - -(var10 << 6); - int var17 = (var7 << 7) + (var11 << 6); - int var12; - int var13; - if (104 < var6 - -var10) { - var12 = var6; - var13 = var6 + 1; - } else { - var12 = var6 + (var10 >> 1); - var13 = (var10 - -1 >> 1) + var6; - } - - int[][] var18 = Class44.anIntArrayArrayArray723[var8]; - int var20 = 0; - int var19 = var18[var12][var15] + var18[var12][var14] + var18[var13][var14] + var18[var13][var15] >> 2; - int[][] var21; - if (var8 != 0) { - var21 = Class44.anIntArrayArrayArray723[0]; - var20 = -(var21[var12][var15] + var21[var13][var14] + (var21[var12][var14] - -var21[var13][var15]) >> 2) + var19; - } - - var21 = null; - if (3 > var8) { - var21 = Class44.anIntArrayArrayArray723[1 + var8]; - } - - Class136 var22 = var0.method1696(var3, var16, var18, var5, var19, var21, false, null, (byte) -69, true, var17); - Class141.method2047(Objects.requireNonNull(var22).aClass109_Sub1_1770, -var4 + var16, var20, var17 + -var2); - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "al.K(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ')'); - } - } - - static void method841() { - try { - aClass11Array1836 = null; - TextureOperation12.method171(-101, ConfigInventoryDefinition.anInt3655, 0, Class23.canvasWidth, 0, -1, Class140_Sub7.canvasHeight, 0, 0); - if (aClass11Array1836 != null) { - Unsorted.method1095(0, Unsorted.anInt1082, Unsorted.anInt3602, aClass11Array1836, Class23.canvasWidth, -1412584499, 0, Class140_Sub7.canvasHeight, (byte) 73, PacketParser.aClass11_88.anInt292); - aClass11Array1836 = null; - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "al.G(" + true + ')'); - } - } - - static CacheIndex getCacheIndex(boolean var0, boolean var1, boolean var2, int var3) { - try { - Class41 var5 = null; - if (null != Class101.aClass30_1422) { - var5 = new Class41(var3, Class101.aClass30_1422, Class163_Sub2.aClass30Array2998[var3], 1000000); - } - - Unsorted.aClass151_Sub1Array2601[var3] = Unsorted.aClass8_1936.method839(var3, AtmosphereParser.aClass41_1186, var5); - if (var1) { - Unsorted.aClass151_Sub1Array2601[var3].method2101(); - } - return new CacheIndex(Unsorted.aClass151_Sub1Array2601[var3], var0, var2); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "al.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + true + ')'); - } - } - - static Class75_Sub1 method843(int var0, DataBuffer var1) { - try { - return var0 != -5232 ? null : new Class75_Sub1(var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readSignedShort(), var1.readMedium(), var1.readUnsignedByte()); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "al.D(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method844(byte var0) { - try { - if (var0 != -9) { - TextCore.aClass94_106 = null; - } - - if (null == TextureOperation23.anIntArray3212 || null == Class75_Sub2.anIntArray2639) { - TextureOperation23.anIntArray3212 = new int[256]; - Class75_Sub2.anIntArray2639 = new int[256]; - - for (int var1 = 0; 256 > var1; ++var1) { - double var2 = (double) var1 / 255.0D * 6.283185307179586D; - TextureOperation23.anIntArray3212[var1] = (int) (Math.sin(var2) * 4096.0D); - Class75_Sub2.anIntArray2639[var1] = (int) (4096.0D * Math.cos(var2)); - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "al.C(" + var0 + ')'); - } - } - - static void method845(boolean var0) { - try { - if (var0 == !Class139.aBoolean1827) { - Class139.aBoolean1827 = var0; - - WaterfallShader.method1626((byte) -126); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "al.H(" + var0 + ',' + 255 + ')'); - } - } - - static boolean method846(int var0, int var1, int var2) { - int var3 = Class81.anIntArrayArrayArray1142[var0][var1][var2]; - if (var3 == -Class3_Sub28_Sub1.anInt3539) { - return false; - } else if (var3 == Class3_Sub28_Sub1.anInt3539) { - return true; - } else { - int var4 = var1 << 7; - int var5 = var2 << 7; - if (TextureOperation10.method349(var4 + 1, Class44.anIntArrayArrayArray723[var0][var1][var2], var5 + 1) && TextureOperation10.method349(var4 + 128 - 1, Class44.anIntArrayArrayArray723[var0][var1 + 1][var2], var5 + 1) && TextureOperation10.method349(var4 + 128 - 1, Class44.anIntArrayArrayArray723[var0][var1 + 1][var2 + 1], var5 + 128 - 1) && TextureOperation10.method349(var4 + 1, Class44.anIntArrayArrayArray723[var0][var1][var2 + 1], var5 + 128 - 1)) { - Class81.anIntArrayArrayArray1142[var0][var1][var2] = Class3_Sub28_Sub1.anInt3539; - return true; - } else { - Class81.anIntArrayArrayArray1142[var0][var1][var2] = -Class3_Sub28_Sub1.anInt3539; - return false; - } - } - } - - final boolean method837() { - try { - if (null == this.aClass3_Sub30_99) { - - if (this.aJs5ResourceRequest_100 == null) { - if (this.aJs5Worker_102.priorityRequestsFull()) { - return false; - } - - this.aJs5ResourceRequest_100 = this.aJs5Worker_102.request(255, 255, (byte) 0, true); - } - - if (this.aJs5ResourceRequest_100.waiting) { - return false; - } else { - this.aClass3_Sub30_99 = new DataBuffer(this.aJs5ResourceRequest_100.getData()); - this.aClass151_Sub1Array107 = new Class151_Sub1[(this.aClass3_Sub30_99.buffer.length + -5) / 8]; - return true; - } - } else { - return true; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "al.J(" + 255 + ')'); - } - } - - final void method838() { - try { - if (null != this.aClass151_Sub1Array107) { - int var2; - for (var2 = 0; var2 < this.aClass151_Sub1Array107.length; ++var2) { - if (this.aClass151_Sub1Array107[var2] != null) { - this.aClass151_Sub1Array107[var2].method2110(); - } - } - - for (var2 = 0; this.aClass151_Sub1Array107.length > var2; ++var2) { - if (this.aClass151_Sub1Array107[var2] != null) { - this.aClass151_Sub1Array107[var2].method2107(); - } - } - - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "al.E(" + (byte) -70 + ')'); - } - } - - private Class151_Sub1 method839(int var2, Class41 var3, Class41 var4) { - try { - - return this.method847(var4, var2, var3); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "al.L(" + -1824885439 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ')'); - } - } - - private Class151_Sub1 method847(Class41 var1, int var3, Class41 var5) { - try { - if (null == this.aClass3_Sub30_99) { - throw new RuntimeException(); - } else { - this.aClass3_Sub30_99.index = 5 + var3 * 8; - if (this.aClass3_Sub30_99.buffer.length > this.aClass3_Sub30_99.index) { - if (null == this.aClass151_Sub1Array107[var3]) { - int var6 = this.aClass3_Sub30_99.readInt(); - int var7 = this.aClass3_Sub30_99.readInt(); - Class151_Sub1 var8 = new Class151_Sub1(var3, var1, var5, this.aJs5Worker_102, this.aCacheResourceWorker_103, var6, var7); - this.aClass151_Sub1Array107[var3] = var8; - return var8; - } else { - return this.aClass151_Sub1Array107[var3]; - } - } else { - throw new RuntimeException(); - } - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "al.A(" + (var1 != null ? "{...}" : "null") + ',' + -125 + ',' + var3 + ',' + true + ',' + (var5 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class80.java b/Client/src/main/java/org/runite/client/Class80.java deleted file mode 100644 index 69e4d93ce..000000000 --- a/Client/src/main/java/org/runite/client/Class80.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.data.HashTable; - -final class Class80 { - - private final HashTable table; - private Linkable aClass3_1130; - private int anInt1132 = 0; - - Class80(HashTable var1) { - this.table = var1; - } - - @SuppressWarnings("unchecked") - final T method1392() { - Linkable var2; - if (this.anInt1132 > 0 && this.table.getBuckets()[this.anInt1132 + -1] != this.aClass3_1130) { - var2 = this.aClass3_1130; - } else { - do { - if (this.anInt1132 >= this.table.getCapacity()) { - return null; - } - - var2 = this.table.getBuckets()[this.anInt1132++].next; - } while (var2 == this.table.getBuckets()[-1 + this.anInt1132]); - - } - this.aClass3_1130 = var2.next; - return (T) var2; - } - - final T method1393() { - this.anInt1132 = 0; - return this.method1392(); - } - -} diff --git a/Client/src/main/java/org/runite/client/Class81.java b/Client/src/main/java/org/runite/client/Class81.java deleted file mode 100644 index 776821066..000000000 --- a/Client/src/main/java/org/runite/client/Class81.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.Queue; - -import java.awt.event.ActionEvent; -import java.io.IOException; -import java.util.Objects; - -final class Class81 { - - static Queue aClass13_1139 = new Queue(); - static int[][][] anIntArrayArrayArray1142; - - - static void putRandomDataFile(DataBuffer var0, boolean var1) { - try { - if (!var1) { - anIntArrayArrayArray1142 = null; - } - - byte[] var2 = new byte[24]; - if (null != Unsorted.aClass30_1039) { - try { - Unsorted.aClass30_1039.method984(-41, 0L); - Unsorted.aClass30_1039.method982(var2); - - int var3; - for (var3 = 0; var3 < 24 && var2[var3] == 0; ++var3) { - } - - if (var3 >= 24) { - throw new IOException(); - } - } catch (Exception var5) { - for (int var4 = 0; 24 > var4; ++var4) { - var2[var4] = -1; - } - } - } - - var0.putBytes(var2, 24); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "la.G(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ')'); - } - } - - static int method1398(Player var1) { - try { - - int var2 = var1.anInt3963; - RenderAnimationDefinition var3 = var1.getRenderAnimationType(); - if (var1.anInt2764 == var3.stand_animation) { - var2 = var1.anInt3952; - } else if (var3.run_anim != var1.anInt2764 && var1.anInt2764 != var3.run_follow_full_turn_anim && var1.anInt2764 != var3.run_follow_cw_turn_anim && var3.run_follow_ccw_turn_anim != var1.anInt2764) { - if (var3.slow_walk_anim == var1.anInt2764 || var3.slow_walk_follow_full_turn_anim == var1.anInt2764 || var1.anInt2764 == var3.slow_walk_follow_cw_turn_anim || var3.slow_walk_follow_ccw_turn_anim == var1.anInt2764) { - var2 = var1.anInt3966; - } - } else { - var2 = var1.anInt3973; - } - - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "la.A(" + 0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void friendsIgnoreListAlerts(long var1) { - try { - if (var1 != 0L) { - if (Class3_Sub28_Sub5.anInt3591 < 100) { - RSString var4 = Objects.requireNonNull(Unsorted.method1052(var1)).longToRSString(); - - int var5; - for (var5 = 0; var5 < Class3_Sub28_Sub5.anInt3591; ++var5) { - if (Class114.ignores[var5] == var1) { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, RSString.stringCombiner(new RSString[]{var4, TextCore.HasIgnoreAlready}), -1); - return; - } - } - - for (var5 = 0; Class8.anInt104 > var5; ++var5) { - if (var1 == Class50.aLongArray826[var5]) { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, RSString.stringCombiner(new RSString[]{TextCore.HasPleaseRemove, var4, TextCore.HasFriendsToIgnore}), -1); - return; - } - } - - if (var4.equalsString(Class102.player.displayName)) { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, TextCore.HasOnOwnIgnoreList, -1); - } else { - Class114.ignores[Class3_Sub28_Sub5.anInt3591] = var1; - TextureOperation7.aClass94Array3341[Class3_Sub28_Sub5.anInt3591++] = Unsorted.method1052(var1); - Class110.anInt1472 = PacketParser.anInt3213; - TextureOperation12.outgoingBuffer.putOpcode(34); - TextureOperation12.outgoingBuffer.writeLong(var1); - } - } else { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, TextCore.HasIgnoreListFull, -1); - } - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "la.B(" + 32 + ',' + var1 + ')'); - } - } - - static void method1400(Signlink var0, Object var1, int var2) { - try { - if (var2 >= -29) { - anIntArrayArrayArray1142 = null; - } - - if (null != var0.systemEventQueue) { - for (int var3 = 0; var3 < 50 && null != var0.systemEventQueue.peekEvent(); ++var3) { - TimeUtils.sleep(1L); - } - - if (var1 != null) { - var0.systemEventQueue.postEvent(new ActionEvent(var1, 1001, "dummy")); - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "la.E(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + var2 + ')'); - } - } - - static Class57 method1401(int var1) { - try { - Class57 var2 = (Class57) Class128.aReferenceCache_1683.get(var1); - if (var2 == null) { - byte[] var3 = Class46.aClass153_737.getFile(31, var1); - var2 = new Class57(); - if (var3 != null) { - var2.method1190(new DataBuffer(var3), var1); - } - - Class128.aReferenceCache_1683.put(var2, var1); - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "la.D(" + 1001 + ',' + var1 + ')'); - } - } - - static Class131 getWorldMapArchive(RSString var1, CacheIndex var2) { - try { - int var3 = var2.getArchiveForName(var1); - if (var3 == -1) { - return new Class131(0); - } else { - int[] var4 = var2.getFileIds(var3); - Class131 var5 = new Class131(Objects.requireNonNull(var4).length); - - for (int var6 = 0; var5.anInt1720 > var6; ++var6) { - DataBuffer var7 = new DataBuffer(var2.getFile(var3, var4[var6])); - var5.aClass94Array1721[var6] = var7.readString(); - var5.aByteArray1730[var6] = var7.readSignedByte(); - var5.aShortArray1727[var6] = (short) var7.readUnsignedShort(); - var5.aShortArray1718[var6] = (short) var7.readUnsignedShort(); - var5.anIntArray1725[var6] = var7.readInt(); - } - - return var5; - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "la.C(" + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class83.java b/Client/src/main/java/org/runite/client/Class83.java deleted file mode 100644 index 1b23dad7c..000000000 --- a/Client/src/main/java/org/runite/client/Class83.java +++ /dev/null @@ -1,1056 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.HashTable; -import org.rs09.client.rendering.Toolkit; - -public final class Class83 { - - static int[] anIntArray1161; - private final CacheIndex aClass153_1153; - private final HashTable aHashTable_1155 = new HashTable(256); - private final CacheIndex aClass153_1157; - private final HashTable aHashTable_1159 = new HashTable(256); - - - Class83(CacheIndex var1, CacheIndex var2) { - try { - this.aClass153_1153 = var1; - this.aClass153_1157 = var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "le.(" + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - static void method1410(int var0, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - int var11 = var4 - var6; - int var12 = -var3 + var2; - if (Class23.anInt455 > var4) { - ++var11; - } - - if (Class108.anInt1460 > var2) { - ++var12; - } - - int var13; - int var14; - int var15; - int var17; - int var16; - int var19; - int var21; - int var20; - int var22; - int var25; - int var24; - int var26; - int var28; - int var31; - int var30; - int var32; - int var33; - int[][] var41; - for (var13 = 0; var13 < var11; ++var13) { - var14 = var13 * var8 >> 16; - var15 = (var13 + 1) * var8 >> 16; - var16 = -var14 + var15; - if (var16 > 0) { - var17 = var13 - -var6 >> 6; - if (0 <= var17 && var17 <= Class146.anIntArrayArrayArray1903.length + -1) { - var14 += var5; - var41 = Class146.anIntArrayArrayArray1903[var17]; - byte[][] var45 = RenderAnimationDefinition.aByteArrayArrayArray383[var17]; - byte[][] var42 = Class3_Sub10.aByteArrayArrayArray2339[var17]; - byte[][] var23 = Class36.aByteArrayArrayArray640[var17]; - byte[][] var43 = CS2Script.aByteArrayArrayArray2452[var17]; - var15 += var5; - byte[][] var46 = TextureOperation29.aByteArrayArrayArray3390[var17]; - - for (var24 = 0; var12 > var24; ++var24) { - var25 = var7 * var24 >> 16; - var26 = - -((1 + var24) * var7) >> 16; - int var27 = -var25 + var26; - if (var27 > 0) { - var26 += var0; - var28 = var3 + var24 >> 6; - int var29 = 63 & var3 + var24; - var25 += var0; - var30 = var13 + var6 & 63; - var31 = (var29 << 6) + var30; - if (0 <= var28 && var41.length + -1 >= var28 && null != var41[var28]) { - var32 = var41[var28][var31]; - } else { - if (Unsorted.aClass3_Sub28_Sub3_2600.anInt3550 == -1) { - if ((var6 + var13 & 4) == (4 & var24 + var3)) { - var32 = anIntArray1161[1 + TextureOperation26.anInt3081]; - } else { - var32 = 4936552; - } - } else { - var32 = Unsorted.aClass3_Sub28_Sub3_2600.anInt3550; - } - - if (var28 < 0 || var28 > var41.length + -1) { - if (var32 == 0) { - var32 = 1; - } - - Toolkit.JAVA_TOOLKIT.method934(var14, var25, var16, var27, var32); - continue; - } - } - - var33 = var45[var28] == null ? 0 : anIntArray1161[var45[var28][var31] & 0xFF]; - if (var32 == 0) { - var32 = 1; - } - - int var34 = var46[var28] != null ? anIntArray1161[255 & var46[var28][var31]] : 0; - int var36; - if (var33 == 0 && var34 == 0) { - Toolkit.JAVA_TOOLKIT.method934(var14, var25, var16, var27, var32); - } else { - byte var35; - if (0 != var33) { - if (var33 == -1) { - var33 = 1; - } - - var35 = var42[var28] != null ? var42[var28][var31] : 0; - var36 = var35 & 252; - if (var36 != 0 && var16 > 1 && var27 > 1) { - method2272(Toolkit.JAVA_TOOLKIT.getBuffer(), var33, var14, var35 & 3, var32, var36 >> 2, var27, var16, var25, true); - } else { - Toolkit.JAVA_TOOLKIT.method934(var14, var25, var16, var27, var33);//Remember! If it's a SPECIFIC JAVA_TOOLKIT CALL THERE IS A REASON - } - } - - if (var34 != 0) { - if (var34 == -1) { - var34 = var32; - } - - var35 = var43[var28][var31]; - var36 = 252 & var35; - if (var36 == 0 || var16 <= 1 || 1 >= var27) { - Toolkit.JAVA_TOOLKIT.method934(var14, var25, var16, var27, var34);//Remember! If it's a SPECIFIC JAVA_TOOLKIT CALL THERE IS A REASON - } - - method2272(Toolkit.JAVA_TOOLKIT.getBuffer(), var34, var14, var35 & 3, 0, var36 >> 2, var27, var16, var25, var33 == 0); - } - } - - if (var23[var28] != null) { - int var49 = var23[var28][var31] & 0xFF; - if (var49 != 0) { - if (1 == var16) { - var36 = var14; - } else { - var36 = var15 - 1; - } - - int var37; - if (1 == var27) { - var37 = var25; - } else { - var37 = -1 + var26; - } - - int var38 = 13421772; - if (5 <= var49 && 8 >= var49 || var49 >= 13 && 16 >= var49 || 21 <= var49 && var49 <= 24 || var49 == 27 || 28 == var49) { - var38 = 13369344; - var49 -= 4; - } - - if (1 == var49) { - Toolkit.JAVA_TOOLKIT.drawVerticalLine(var14, var25, var27, var38); -// Class74.drawVerticalLine(var14, var25, var27, var38); - } else if (2 == var49) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var14, var25, var16, var38); -// Class74.drawHorizontalLine(var14, var25, var16, var38); - } else if (3 == var49) { - Toolkit.JAVA_TOOLKIT.drawVerticalLine(var36, var25, var27, var38); -// Class74.drawVerticalLine(var36, var25, var27, var38); - } else if (var49 == 4) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var14, var37, var16, var38); -// Class74.drawHorizontalLine(var14, var37, var16, var38); - } else if (var49 == 9) { - Toolkit.JAVA_TOOLKIT.drawVerticalLine(var14, var25, var27, 16777215); - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var14, var25, var16, var38); -// Class74.drawVerticalLine(var14, var25, var27, 16777215); -// Class74.drawHorizontalLine(var14, var25, var16, var38); - } else if (var49 == 10) { - Toolkit.JAVA_TOOLKIT.drawVerticalLine(var36, var25, var27, 16777215); - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var14, var25, var16, var38); -// Class74.drawVerticalLine(var36, var25, var27, 16777215); -// Class74.drawHorizontalLine(var14, var25, var16, var38); - } else if (var49 == 11) { - Toolkit.JAVA_TOOLKIT.drawVerticalLine(var36, var25, var27, 16777215); - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var14, var37, var16, var38); -// Class74.drawVerticalLine(var36, var25, var27, 16777215); -// Class74.drawHorizontalLine(var14, var37, var16, var38); - } else if (var49 == 12) { - Toolkit.JAVA_TOOLKIT.drawVerticalLine(var14, var25, var27, 16777215); - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var14, var37, var16, var38); -// Class74.drawVerticalLine(var14, var25, var27, 16777215); -// Class74.drawHorizontalLine(var14, var37, var16, var38); - } else if (var49 == 17) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var14, var25, 1, var38); -// Class74.drawHorizontalLine(var14, var25, 1, var38); - } else if (var49 == 18) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var36, var25, 1, var38); -// Class74.drawHorizontalLine(var36, var25, 1, var38); - } else if (var49 == 19) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var36, var37, 1, var38); -// Class74.drawHorizontalLine(var36, var37, 1, var38); - } else if (var49 == 20) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var14, var37, 1, var38); -// Class74.drawHorizontalLine(var14, var37, 1, var38); - } else { - int var39; - if (25 == var49) { - for (var39 = 0; var27 > var39; ++var39) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var39 + var14, -var39 + var37, 1, var38); -// Class74.drawHorizontalLine(var39 + var14, -var39 + var37, 1, var38); - } - } else if (26 == var49) { - for (var39 = 0; var39 < var27; ++var39) { - Toolkit.JAVA_TOOLKIT.drawHorizontalLine(var39 + var14, var25 + var39, 1, var38); -// Class74.drawHorizontalLine(var39 + var14, var25 + var39, 1, var38); - } - } - } - } - } - } - } - } else { - var14 += var5; - - for (int var18 = 0; var18 < var12; ++var18) { - if (-1 != Unsorted.aClass3_Sub28_Sub3_2600.anInt3550) { - var19 = Unsorted.aClass3_Sub28_Sub3_2600.anInt3550; - } else if ((var13 - -var6 & 4) == (4 & var18 + var3)) { - var19 = anIntArray1161[1 + TextureOperation26.anInt3081]; - } else { - var19 = 4936552; - } - - if (var19 == 0) { - var19 = 1; - } - - var20 = (var7 * var18 >> 16) + var0; - var21 = var0 + ((var18 + 1) * var7 >> 16); - var22 = var21 + -var20; - Toolkit.JAVA_TOOLKIT.method934(var14, var20, var16, var22, var19); -// Class74.method934(var14, var20, var16, var22, var19); - } - - } - } - } - - for (var13 = -2; 2 + var11 > var13; ++var13) { - var14 = - -(var13 * var8) >> 16; - var15 = var8 * (var13 + 1) >> 16; - var16 = -var14 + var15; - if (var16 > 0) { - var14 += var5; - var17 = var6 + var13 >> 6; - if (var17 >= 0 && Class29.anIntArrayArrayArray558.length + -1 >= var17) { - var41 = Class29.anIntArrayArrayArray558[var17]; - - for (var19 = -2; var12 - -2 > var19; ++var19) { - var20 = - -(var19 * var7) >> 16; - var21 = - -((var19 + 1) * var7) >> 16; - var22 = var21 + -var20; - if (var22 > 0) { - var20 += var0; - int var44 = var19 - -var3 >> 6; - if (var44 >= 0 && var44 <= -1 + var41.length) { - var24 = ((63 & var3 + var19) << 6) - -(var13 - -var6 & 63); - if (null != var41[var44]) { - var25 = var41[var44][var24]; - var26 = 16383 & var25; - if (var26 != 0) { - var28 = ('\ud228' & var25) >> 14; - MapSceneDefinition var47 = InterfaceWidget.c(-1 + var26); - LDIndexedSprite var48 = var47.getSprite(var28); - if (var48 != null) { - var31 = var22 * var48.height / 4; - var30 = var16 * var48.width / 4; - if (var47.aBoolean69) { - var32 = var25 >> 16 & 15; - var33 = (16103184 & var25) >> 20; - if ((1 & var28) == 1) { - var28 = var32; - var32 = var33; - var33 = var28; - } - - var30 = var16 * var32; - var31 = var22 * var33; - } - - if (var30 != 0 && var31 != 0) { - if (var47.color == 0) { - var48.method1677(var14, -var31 + var20 - -var22, var30, var31); - } else { - var48.method1669(var14, var20 - (var31 - var22), var30, var31, var47.color); - } - } - } - } - } - } - } - } - } - } - } - - } catch (RuntimeException var40) { - throw ClientErrorException.clientError(var40, "le.C(" + var0 + ',' + 0 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + 0 + ',' + -12541 + ')'); - } - } - - public static int getWindowType() { - try { - if (TextureOperation30.fullScreenFrame == null) { - if (HDToolKit.highDetail && Class3_Sub15.aBoolean2427) { - return 2; - } else { - return HDToolKit.highDetail ? 1 : 0; - } - } else { - return 3; - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "le.D(" + ')'); - } - } - - static void method2272(int[] var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, boolean var9) { - try { - int var11 = var2; - if (var2 < Toolkit.JAVA_TOOLKIT.clipRight) { - if (Toolkit.JAVA_TOOLKIT.clipLeft > var2) { - var11 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - int var12 = var7 + var2; - if (Toolkit.JAVA_TOOLKIT.clipLeft < var12) { - if (Toolkit.JAVA_TOOLKIT.clipRight < var12) { - var12 = Toolkit.JAVA_TOOLKIT.clipRight; - } - - int var13 = var8; - if (var8 < Toolkit.JAVA_TOOLKIT.clipBottom) { - int var14 = var8 + var6; - if (Toolkit.JAVA_TOOLKIT.clipTop > var8) { - var13 = Toolkit.JAVA_TOOLKIT.clipTop; - } - - if (var14 > Toolkit.JAVA_TOOLKIT.clipTop) { - int var15 = var11 + Toolkit.JAVA_TOOLKIT.width * var13; - if (var5 == 9) { - var3 = 3 & var3 - -1; - var5 = 1; - } - - int var16 = -var12 + var11 + Toolkit.JAVA_TOOLKIT.width; - var13 -= var8; - int var20 = var6 + -var13; - if (Toolkit.JAVA_TOOLKIT.clipBottom < var14) { - var14 = Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if (var5 == 10) { - var3 = var3 - -3 & 3; - var5 = 1; - } - - var11 -= var2; - int var18 = -var11 + var7; - if (11 == var5) { - var3 = 3 & var3 + 3; - var5 = 8; - } - - var12 -= var2; - int var17 = var7 + -var12; - var14 -= var8; - int var19 = var6 - var14; - int var21; - int var22; - if (var5 != 1) { - if (2 == var5) { - if (var3 == 0) { - for (var21 = var20 + -1; var19 <= var21; --var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var22 <= var21 >> 1) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (1 == var3) { - for (var21 = var13; var14 > var21; ++var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (0 <= var15 && var0.length > var15) { - if (var22 >= var21 << 1) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } else { - ++var15; - } - } - - var15 += var16; - } - - } else if (var3 == 2) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var18 + -1; var17 <= var22; --var22) { - if (var21 >> 1 >= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (3 == var3) { - for (var21 = var20 + -1; var19 <= var21; --var21) { - for (var22 = -1 + var18; var17 <= var22; --var22) { - if (var21 << 1 > var22) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - } - } else if (var5 != 3) { - if (var5 != 4) { - if (var5 != 5) { - if (var5 == 6) { - if (var3 == 0) { - for (var21 = var13; var14 > var21; ++var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var22 > var7 / 2) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (var3 == 1) { - for (var21 = var13; var14 > var21; ++var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var21 > var6 / 2) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (2 == var3) { - for (var21 = var13; var14 > var21; ++var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (var22 >= var7 / 2) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (var3 == 3) { - for (var21 = var13; var14 > var21; ++var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var6 / 2 > var21) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - } - - if (7 == var5) { - if (0 == var3) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (var22 <= var21 + -(var6 / 2)) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (var3 == 1) { - for (var21 = var20 + -1; var21 >= var19; --var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var22 > -(var6 / 2) + var21) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (var3 == 2) { - for (var21 = var20 + -1; var21 >= var19; --var21) { - for (var22 = -1 + var18; var22 >= var17; --var22) { - if (var22 > var21 + -(var6 / 2)) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (3 == var3) { - for (var21 = var13; var14 > var21; ++var21) { - for (var22 = -1 + var18; var17 <= var22; --var22) { - if (var21 + -(var6 / 2) >= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - } - - if (var5 == 8) { - if (0 == var3) { - for (var21 = var13; var14 > var21; ++var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (-(var6 / 2) + var21 <= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (var3 == 1) { - for (var21 = -1 + var20; var21 >= var19; --var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (-(var6 / 2) + var21 > var22) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (var3 == 2) { - for (var21 = var20 - 1; var19 <= var21; --var21) { - for (var22 = -1 + var18; var22 >= var17; --var22) { - if (var21 - var6 / 2 > var22) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - - if (var3 == 3) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = -1 + var18; var17 <= var22; --var22) { - if (var21 + -(var6 / 2) > var22) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - return; - } - } - - } else if (0 == var3) { - for (var21 = var20 + -1; var21 >= var19; --var21) { - for (var22 = -1 + var18; var22 >= var17; --var22) { - if (var21 >> 1 <= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (1 == var3) { - for (var21 = -1 + var20; var21 >= var19; --var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (var22 > var21 << 1) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - } else if (var3 == 2) { - for (var21 = var13; var14 > var21; ++var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (var21 >> 1 > var22) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - } else if (var3 == 3) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var18 + -1; var17 <= var22; --var22) { - if (var21 << 1 >= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } - } else if (var3 == 0) { - for (var21 = var20 + -1; var21 >= var19; --var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var22 < var21 >> 1) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - } else if (1 == var3) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (var22 <= var21 << 1) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (2 == var3) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var18 + -1; var22 >= var17; --var22) { - if (var21 >> 1 <= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (var3 == 3) { - for (var21 = -1 + var20; var21 >= var19; --var21) { - for (var22 = -1 + var18; var22 >= var17; --var22) { - if (var22 <= var21 << 1) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } - } else if (var3 == 0) { - for (var21 = var20 - 1; var21 >= var19; --var21) { - for (var22 = -1 + var18; var17 <= var22; --var22) { - if (var22 <= var21 >> 1) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (var3 == 1) { - for (var21 = -1 + var20; var19 <= var21; --var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var22 >= var21 << 1) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (2 == var3) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var21 >> 1 >= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (3 == var3) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var18 - 1; var17 <= var22; --var22) { - if (var22 < var21 << 1) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - } - } else if (var3 == 0) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (var21 >= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (1 == var3) { - for (var21 = var20 + -1; var21 >= var19; --var21) { - for (var22 = var11; var22 < var12; ++var22) { - if (var21 >= var22) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (2 == var3) { - for (var21 = var13; var21 < var14; ++var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var22 >= var21) { - var0[var15] = var1; - } else if (var9) { - var0[var15] = var4; - } - - ++var15; - } - - var15 += var16; - } - - } else if (var3 == 3) { - for (var21 = var20 + -1; var19 <= var21; --var21) { - for (var22 = var11; var12 > var22; ++var22) { - if (var22 < var21) { - if (var9) { - var0[var15] = var4; - } - } else { - var0[var15] = var1; - } - - ++var15; - } - - var15 += var16; - } - - } - } - } - } - } - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "wl.C(" + "null" + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + (byte) 21 + ')'); - } - } - - private PcmSound method1412(int[] var1, int var3, int var4) { - try { - int var5 = var4 ^ (var3 >>> 12 | var3 << 4 & '\ufff3'); - var5 |= var3 << 16; - long var6 = var5; - PcmSound var8 = (PcmSound) this.aHashTable_1159.get(var6); - if (var8 != null) { - return var8; - } else if (null != var1 && var1[0] <= 0) { - return null; - } else { - SynthSound var9 = SynthSound.create(this.aClass153_1153, var3, var4); - if (null == var9) { - return null; - } else { - var8 = var9.toPCMSound(); - this.aHashTable_1159.put(var6, var8); - if (var1 != null) { - var1[0] -= var8.samples.length; - } - - return var8; - } - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "le.B(" + (var1 != null ? "{...}" : "null") + ',' + 31947 + ',' + var3 + ',' + var4 + ')'); - } - } - - final PcmSound method1413(int var1, int[] var3) { - try { - if (1 == this.aClass153_1153.method2121()) { - return this.method1412(var3, 0, var1); - } else if (this.aClass153_1153.getFileAmount(var1) == 1) { - return this.method1412(var3, var1, 0); - } else { - - throw new RuntimeException(); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "le.A(" + var1 + ',' + 33 + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - private PcmSound method1415(int var1, int[] var2, int var3) { - try { - int var5 = var3 ^ (var1 >>> 12 | '\ufff3' & var1 << 4); - var5 |= var1 << 16; - long var6 = (long) var5 ^ 4294967296L; - PcmSound var8 = (PcmSound) this.aHashTable_1159.get(var6); - if (null != var8) { - return var8; - } else if (var2 != null && var2[0] <= 0) { - return null; - } else { - Class3_Sub14 var9 = (Class3_Sub14) this.aHashTable_1155.get(var6); - if (null == var9) { - var9 = Class3_Sub14.method363(this.aClass153_1157, var1, var3); - if (null == var9) { - return null; - } - - this.aHashTable_1155.put(var6, var9); - } - - var8 = var9.method359(var2); - if (null == var8) { - return null; - } else { - var9.unlink(); - this.aHashTable_1159.put(var6, var8); - - return var8; - } - } - } catch (RuntimeException var10) { - // var10.printStackTrace(); - throw ClientErrorException.clientError(var10, "le.F(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + (byte) 11 + ')'); - } - } - - final PcmSound method1416(int var2, int[] var3) { - try { - if (this.aClass153_1157.method2121() == 1) { - return this.method1415(0, var3, var2); - } else if (this.aClass153_1157.getFileAmount(var2) == 1) { - return this.method1415(var2, var3, 0); - } else { - throw new RuntimeException(); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "le.G(" + 10089 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class84.java b/Client/src/main/java/org/runite/client/Class84.java deleted file mode 100644 index b219c3192..000000000 --- a/Client/src/main/java/org/runite/client/Class84.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.runite.client; - -public final class Class84 { - - static int[][] anIntArrayArray1160 = new int[104][104]; - static LinkedList aLinkedList_1162 = new LinkedList(); - public static int[] anIntArray1163 = new int[1000]; - static int leftMargin = 0; - static SoftwareSprite aSoftwareSprite_1381; - static int[] anIntArray1729 = new int[]{12543016, 15504954, 15914854, 16773818}; - - static void method1417() { - try { - if (Class143.gameStage == 10 && HDToolKit.highDetail) { - Class117.method1719(28); - } - - if (Class143.gameStage == 30) { - Class117.method1719(25); - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lf.D(" + ')'); - } - } - - static void method1418(CacheIndex var1) { - try { - Class163_Sub2_Sub1.aClass109_Sub1Array4027 = Unsorted.method619((byte) 65, NPC.anInt4001, var1); - Class52.anIntArray861 = new int[256]; - - int var2; - for (var2 = 0; var2 < 3; ++var2) { - int var4 = (anIntArray1729[1 + var2] & 16711680) >> 16; - float var3 = (float) ((anIntArray1729[var2] & 16711680) >> 16); - float var6 = (float) (anIntArray1729[var2] >> 8 & 0xFF); - float var9 = (float) (anIntArray1729[var2] & 0xFF); - float var5 = ((float) var4 - var3) / 64.0F; - int var7 = (anIntArray1729[var2 + 1] & 65280) >> 8; - float var8 = (-var6 + (float) var7) / 64.0F; - int var10 = anIntArray1729[var2 + 1] & 0xFF; - float var11 = ((float) var10 - var9) / 64.0F; - - for (int var12 = 0; var12 < 64; ++var12) { - Class52.anIntArray861[var12 + 64 * var2] = TextureOperation3.bitwiseOr((int) var9, TextureOperation3.bitwiseOr((int) var6 << 8, (int) var3 << 16)); - var6 += var8; - var9 += var11; - var3 += var5; - } - } - - for (var2 = 192; var2 < 255; ++var2) { - Class52.anIntArray861[var2] = anIntArray1729[3]; - } - - Class161.anIntArray2026 = new int[32768]; - Unsorted.anIntArray49 = new int[32768]; - TextureOperation30.method215((byte) -89, null); - BufferedDataStream.anIntArray3805 = new int[32768]; - Class159.anIntArray1681 = new int[32768]; - aSoftwareSprite_1381 = new SoftwareSprite(128, 254); - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "lf.E(" + -110 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method1420(int var0, int var1, int var2, int var3) { - try { - InterfaceWidget var5 = InterfaceWidget.getWidget(10, var0); - var5.flagUpdate(); - var5.anInt3597 = var2; - var5.anInt3598 = var3; - var5.anInt3596 = var1; - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "lf.C(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + (byte) -85 + ')'); - } - } - - static int method1421() { - try { - - return ((TextureOperation17.stereoSound ? 1 : 0) << 19) + (((Class38.aBoolean661 ? 1 : 0) << 16) + ((!Class128.aBoolean1685 ? 0 : 1) << 15) + ((!Class106.aBoolean1441 ? 0 : 1) << 13) + ((Class140_Sub6.aBoolean2910 ? 1 : 0) << 10) + ((Unsorted.aBoolean3275 ? 1 : 0) << 9) + ((RSInterface.aBoolean236 ? 1 : 0) << 7) + ((!Class25.aBoolean488 ? 0 : 1) << 6) + ((KeyboardListener.aBoolean1905 ? 1 : 0) << 5) + (((!Unsorted.aBoolean3665 ? 0 : 1) << 3) + (Unsorted.anInt3625 & 7) - (-((!Unsorted.aBoolean3604 ? 0 : 1) << 4) + -((WorldListEntry.aBoolean2623 ? 1 : 0) << 8)) - (-(Unsorted.anInt1137 << 11 & 6144) + -((AudioHandler.soundEffectVolume == 0 ? 0 : 1) << 20) - (((AudioHandler.musicVolume != 0 ? 1 : 0) << 21) + ((Sprites.ambientVolume == 0 ? 0 : 1) << 22)))) - -(Class127_Sub1.method1757() << 23)); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lf.F(" + -2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class85.java b/Client/src/main/java/org/runite/client/Class85.java deleted file mode 100644 index f5bfe3c25..000000000 --- a/Client/src/main/java/org/runite/client/Class85.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.runite.client; - -final class Class85 { - - static boolean aBoolean1167 = false; - static CacheIndex aClass153_1171; - static int anInt1174 = 99; - - - static void method1423(DataBuffer var1, RSString var2) { - try { - byte[] var4 = var2.method1568(); - var1.method768(var4.length); - var1.index += TextureOperation16.aClass36_3112.method1015(var4.length, var1.buffer, var4, 0, var1.index); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "lg.A(" + false + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - static AbstractIndexedSprite[] method1424(CacheIndex var0, int var3) { - try { - - return Class75_Sub4.method1351(var0, 0, var3) ? TextureOperation36.method343() : null; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "lg.C(" + (var0 != null ? "{...}" : "null") + ',' + (byte) -12 + ',' + 0 + ',' + var3 + ')'); - } - } - - static void method1425(int var0) { - TextureOperation22.anInt3419 = var0; - - for (int var1 = 0; var1 < Unsorted.anInt1234; ++var1) { - for (int var2 = 0; var2 < TextureOperation17.anInt3179; ++var2) { - if (Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2] = new Class3_Sub2(var0, var1, var2); - } - } - } - - } - -} diff --git a/Client/src/main/java/org/runite/client/Class88.java b/Client/src/main/java/org/runite/client/Class88.java deleted file mode 100644 index fcdb8c62c..000000000 --- a/Client/src/main/java/org/runite/client/Class88.java +++ /dev/null @@ -1,137 +0,0 @@ -package org.runite.client; - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -final class Class88 { - - static int[] anIntArray1223 = null; - static int[] anIntArray1224 = null; - static boolean Texture3DEnabled; - static int anInt1228 = -1; - private static ByteBuffer aByteBuffer1225; - private static ByteBuffer aByteBuffer1226; - - static void method1454() { - byte[] var1; - if (aByteBuffer1226 == null) { - Class164_Sub2_Sub1 var0 = new Class164_Sub2_Sub1(); - var1 = var0.method2250(); - aByteBuffer1226 = ByteBuffer.allocateDirect(var1.length); - aByteBuffer1226.position(0); - aByteBuffer1226.put(var1); - aByteBuffer1226.flip(); - } - - if (aByteBuffer1225 == null) { - Class164_Sub1_Sub1 var2 = new Class164_Sub1_Sub1(); - var1 = var2.method2243(); - aByteBuffer1225 = ByteBuffer.allocateDirect(var1.length); - aByteBuffer1225.position(0); - aByteBuffer1225.put(var1); - aByteBuffer1225.flip(); - } - - } - - static void method1455() { - GL var0; - int[] var1; - if (anInt1228 != -1) { - var0 = HDToolKit.gl; - var1 = new int[]{anInt1228}; - var0.glDeleteTextures(1, var1, 0); - anInt1228 = -1; - Class31.anInt580 -= aByteBuffer1226.limit() * 2; - } - - if (anIntArray1224 != null) { - var0 = HDToolKit.gl; - var0.glDeleteTextures(64, anIntArray1224, 0); - anIntArray1224 = null; - Class31.anInt580 -= aByteBuffer1226.limit() * 2; - } - - if (WaterfallShader.waterfallTextureId != -1) { - var0 = HDToolKit.gl; - var1 = new int[]{WaterfallShader.waterfallTextureId}; - var0.glDeleteTextures(1, var1, 0); - WaterfallShader.waterfallTextureId = -1; - Class31.anInt580 -= aByteBuffer1225.limit() * 2; - } - - if (anIntArray1223 != null) { - var0 = HDToolKit.gl; - var0.glDeleteTextures(64, anIntArray1223, 0); - anIntArray1223 = null; - Class31.anInt580 -= aByteBuffer1225.limit() * 2; - } - - } - - static void method1456() { - Texture3DEnabled = HDToolKit.allows3DTextureMapping; - method1454(); - method1458(); - method1459(); - } - - private static void method1458() { - GL var0 = HDToolKit.gl; - if (Texture3DEnabled) { - int[] var1 = new int[1]; - var0.glGenTextures(1, var1, 0); - var0.glBindTexture('\u806f', var1[0]); - aByteBuffer1226.position(0); - var0.glTexImage3D('\u806f', 0, 6410, 64, 64, 64, 0, 6410, 5121, aByteBuffer1226); - var0.glTexParameteri('\u806f', 10241, 9729); - var0.glTexParameteri('\u806f', 10240, 9729); - anInt1228 = var1[0]; - } else { - anIntArray1224 = new int[64]; - var0.glGenTextures(64, anIntArray1224, 0); - - for (int var2 = 0; var2 < 64; ++var2) { - HDToolKit.bindTexture2D(anIntArray1224[var2]); - aByteBuffer1226.position(var2 * 64 * 64 * 2); - var0.glTexImage2D(3553, 0, 6410, 64, 64, 0, 6410, 5121, aByteBuffer1226); - var0.glTexParameteri(3553, 10241, 9729); - var0.glTexParameteri(3553, 10240, 9729); - } - - } - Class31.anInt580 += aByteBuffer1226.limit() * 2; - - } - - private static void method1459() { - GL var0 = HDToolKit.gl; - if (Texture3DEnabled) { - int[] var1 = new int[1]; - var0.glGenTextures(1, var1, 0); - var0.glBindTexture('\u806f', var1[0]); - aByteBuffer1225.position(0); - var0.glTexImage3D('\u806f', 0, 6410, 64, 64, 64, 0, 6410, 5121, aByteBuffer1225); - var0.glTexParameteri('\u806f', 10241, 9729); - var0.glTexParameteri('\u806f', 10240, 9729); - WaterfallShader.waterfallTextureId = var1[0]; - } else { - anIntArray1223 = new int[64]; - var0.glGenTextures(64, anIntArray1223, 0); - - for (int var2 = 0; var2 < 64; ++var2) { - HDToolKit.bindTexture2D(anIntArray1223[var2]); - aByteBuffer1225.position(var2 * 64 * 64 * 2); - var0.glTexImage2D(3553, 0, 6410, 64, 64, 0, 6410, 5121, aByteBuffer1225); - var0.glTexParameteri(3553, 10241, 9729); - var0.glTexParameteri(3553, 10240, 9729); - } - - } - Class31.anInt580 += aByteBuffer1225.limit() * 2; - - } - -} diff --git a/Client/src/main/java/org/runite/client/Class91.java b/Client/src/main/java/org/runite/client/Class91.java deleted file mode 100644 index e98f491d9..000000000 --- a/Client/src/main/java/org/runite/client/Class91.java +++ /dev/null @@ -1,1149 +0,0 @@ -package org.runite.client; - -final class Class91 { - - static RSString[] aClass94Array1299 = new RSString[8]; - static int anInt1302 = 0; - static Class33 aClass33_1305; - static byte aByte1308; - static short[] aShortArray1311; - private final int anInt1300; - private final int anInt1303; - private final int anInt1306; - private final int anInt1309; - int[][] anIntArrayArray1304; - - - Class91() { - try { - this.anInt1303 = 104; - this.anInt1306 = 0; - this.anInt1309 = 0; - this.anInt1300 = 104; - this.anIntArrayArray1304 = new int[this.anInt1300][this.anInt1303]; - this.method1496(); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "mj.(" + 104 + ',' + 104 + ')'); - } - } - - static boolean method1495(int var0, int var1, int var2, int var3, int var4) { - int var5 = var3 * TextureOperation25.anInt3417 + var0 * Class145.anInt3153 >> 16; - int var6 = var3 * Class145.anInt3153 - var0 * TextureOperation25.anInt3417 >> 16; - int var7 = var1 * Class60.anInt936 + var6 * Unsorted.anInt1037 >> 16; - int var8 = var1 * Unsorted.anInt1037 - var6 * Class60.anInt936 >> 16; - if (var7 < 1) { - var7 = 1; - } - - int var9 = (var5 << 9) / var7; - int var10 = (var8 << 9) / var7; - int var11 = var2 * Class60.anInt936 + var6 * Unsorted.anInt1037 >> 16; - int var12 = var2 * Unsorted.anInt1037 - var6 * Class60.anInt936 >> 16; - if (var11 < 1) { - var11 = 1; - } - - int var13 = (var5 << 9) / var11; - int var14 = (var12 << 9) / var11; - return (var7 >= 50 || var11 >= 50) && ((var7 <= var4 || var11 <= var4) && ((var9 >= Class139.screenLowerX || var13 >= Class139.screenLowerX) && ((var9 <= Class145.screenUpperX || var13 <= Class145.screenUpperX) && ((var10 >= Class1.screenUpperY || var14 >= Class1.screenUpperY) && (var10 <= AtmosphereParser.screenLowerY || var14 <= AtmosphereParser.screenLowerY))))); - } - - final void method1485(int var1, boolean var2, int var4, int var5, int var6) { - try { - var6 -= this.anInt1309; - - var4 -= this.anInt1306; - if (0 == var5) { - if (var1 == 0) { - this.method1501((byte) 114, var4, var6, 128); - this.method1501((byte) 122, var4, var6 + -1, 8); - } - - if (var1 == 1) { - this.method1501((byte) 77, var4, var6, 2); - this.method1501((byte) 105, 1 + var4, var6, 32); - } - - if (var1 == 2) { - this.method1501((byte) 75, var4, var6, 8); - this.method1501((byte) 38, var4, var6 - -1, 128); - } - - if (3 == var1) { - this.method1501((byte) 110, var4, var6, 32); - this.method1501((byte) 26, var4 - 1, var6, 2); - } - } - - if (var5 == 1 || var5 == 3) { - if (var1 == 0) { - this.method1501((byte) 99, var4, var6, 1); - this.method1501((byte) 23, 1 + var4, -1 + var6, 16); - } - - if (var1 == 1) { - this.method1501((byte) 52, var4, var6, 4); - this.method1501((byte) 60, 1 + var4, 1 + var6, 64); - } - - if (var1 == 2) { - this.method1501((byte) 126, var4, var6, 16); - this.method1501((byte) 103, var4 - 1, var6 - -1, 1); - } - - if (var1 == 3) { - this.method1501((byte) 125, var4, var6, 64); - this.method1501((byte) 64, var4 - 1, -1 + var6, 4); - } - } - - if (var5 == 2) { - if (var1 == 0) { - this.method1501((byte) 110, var4, var6, 130); - this.method1501((byte) 78, var4, -1 + var6, 8); - this.method1501((byte) 45, var4 + 1, var6, 32); - } - - if (var1 == 1) { - this.method1501((byte) 93, var4, var6, 10); - this.method1501((byte) 72, var4 - -1, var6, 32); - this.method1501((byte) 23, var4, var6 + 1, 128); - } - - if (2 == var1) { - this.method1501((byte) 33, var4, var6, 40); - this.method1501((byte) 102, var4, var6 + 1, 128); - this.method1501((byte) 102, var4 - 1, var6, 2); - } - - if (var1 == 3) { - this.method1501((byte) 24, var4, var6, 160); - this.method1501((byte) 122, -1 + var4, var6, 2); - this.method1501((byte) 77, var4, var6 + -1, 8); - } - } - - if (var2) { - if (0 == var5) { - if (var1 == 0) { - this.method1501((byte) 24, var4, var6, 65536); - this.method1501((byte) 115, var4, var6 - 1, 4096); - } - - if (var1 == 1) { - this.method1501((byte) 120, var4, var6, 1024); - this.method1501((byte) 110, var4 - -1, var6, 16384); - } - - if (var1 == 2) { - this.method1501((byte) 101, var4, var6, 4096); - this.method1501((byte) 38, var4, 1 + var6, 65536); - } - - if (var1 == 3) { - this.method1501((byte) 31, var4, var6, 16384); - this.method1501((byte) 65, var4 + -1, var6, 1024); - } - } - - if (1 == var5 || var5 == 3) { - if (var1 == 0) { - this.method1501((byte) 47, var4, var6, 512); - this.method1501((byte) 66, 1 + var4, var6 + -1, 8192); - } - - if (var1 == 1) { - this.method1501((byte) 40, var4, var6, 2048); - this.method1501((byte) 88, 1 + var4, var6 - -1, 32768); - } - - if (var1 == 2) { - this.method1501((byte) 96, var4, var6, 8192); - this.method1501((byte) 41, var4 - 1, 1 + var6, 512); - } - - if (var1 == 3) { - this.method1501((byte) 90, var4, var6, 32768); - this.method1501((byte) 47, var4 + -1, var6 + -1, 2048); - } - } - - if (var5 == 2) { - if (var1 == 0) { - this.method1501((byte) 51, var4, var6, 66560); - this.method1501((byte) 82, var4, var6 + -1, 4096); - this.method1501((byte) 68, var4 + 1, var6, 16384); - } - - if (var1 == 1) { - this.method1501((byte) 112, var4, var6, 5120); - this.method1501((byte) 89, 1 + var4, var6, 16384); - this.method1501((byte) 48, var4, 1 + var6, 65536); - } - - if (var1 == 2) { - this.method1501((byte) 126, var4, var6, 20480); - this.method1501((byte) 25, var4, var6 - -1, 65536); - this.method1501((byte) 46, -1 + var4, var6, 1024); - } - - if (var1 == 3) { - this.method1501((byte) 39, var4, var6, 81920); - this.method1501((byte) 113, var4 - 1, var6, 1024); - this.method1501((byte) 86, var4, var6 + -1, 4096); - } - } - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "mj.L(" + var1 + ',' + var2 + ',' + -104 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - final void method1486(int var1, int var3, boolean var4, int var5, int var6) { - try { - var6 -= this.anInt1309; - var5 -= this.anInt1306; - if (var3 == 0) { - if (0 == var1) { - this.method1490(128, var6, var5); - this.method1490(8, -1 + var6, var5); - } - - if (var1 == 1) { - this.method1490(2, var6, var5); - this.method1490(32, var6, 1 + var5); - } - - if (var1 == 2) { - this.method1490(8, var6, var5); - this.method1490(128, 1 + var6, var5); - } - - if (var1 == 3) { - this.method1490(32, var6, var5); - this.method1490(2, var6, -1 + var5); - } - } - - if (var3 == 1 || var3 == 3) { - if (var1 == 0) { - this.method1490(1, var6, var5); - this.method1490(16, var6 + -1, 1 + var5); - } - - if (var1 == 1) { - this.method1490(4, var6, var5); - this.method1490(64, var6 - -1, var5 + 1); - } - - if (var1 == 2) { - this.method1490(16, var6, var5); - this.method1490(1, 1 + var6, -1 + var5); - } - - if (var1 == 3) { - this.method1490(64, var6, var5); - this.method1490(4, -1 + var6, var5 + -1); - } - } - - if (var3 == 2) { - if (var1 == 0) { - this.method1490(130, var6, var5); - this.method1490(8, -1 + var6, var5); - this.method1490(32, var6, var5 - -1); - } - - if (var1 == 1) { - this.method1490(10, var6, var5); - this.method1490(32, var6, 1 + var5); - this.method1490(128, var6 - -1, var5); - } - - if (var1 == 2) { - this.method1490(40, var6, var5); - this.method1490(128, 1 + var6, var5); - this.method1490(2, var6, var5 + -1); - } - - if (var1 == 3) { - this.method1490(160, var6, var5); - this.method1490(2, var6, -1 + var5); - this.method1490(8, -1 + var6, var5); - } - } - - if (var4) { - if (var3 == 0) { - if (0 == var1) { - this.method1490(65536, var6, var5); - this.method1490(4096, var6 - 1, var5); - } - - if (var1 == 1) { - this.method1490(1024, var6, var5); - this.method1490(16384, var6, var5 + 1); - } - - if (var1 == 2) { - this.method1490(4096, var6, var5); - this.method1490(65536, 1 + var6, var5); - } - - if (var1 == 3) { - this.method1490(16384, var6, var5); - this.method1490(1024, var6, var5 - 1); - } - } - - if (var3 == 1 || var3 == 3) { - if (0 == var1) { - this.method1490(512, var6, var5); - this.method1490(8192, -1 + var6, 1 + var5); - } - - if (var1 == 1) { - this.method1490(2048, var6, var5); - this.method1490(32768, 1 + var6, var5 + 1); - } - - if (var1 == 2) { - this.method1490(8192, var6, var5); - this.method1490(512, var6 - -1, var5 + -1); - } - - if (3 == var1) { - this.method1490(32768, var6, var5); - this.method1490(2048, -1 + var6, var5 - 1); - } - } - - if (2 == var3) { - if (var1 == 0) { - this.method1490(66560, var6, var5); - this.method1490(4096, -1 + var6, var5); - this.method1490(16384, var6, 1 + var5); - } - - if (var1 == 1) { - this.method1490(5120, var6, var5); - this.method1490(16384, var6, var5 - -1); - this.method1490(65536, 1 + var6, var5); - } - - if (var1 == 2) { - this.method1490(20480, var6, var5); - this.method1490(65536, var6 + 1, var5); - this.method1490(1024, var6, -1 + var5); - } - - if (var1 == 3) { - this.method1490(81920, var6, var5); - this.method1490(1024, var6, var5 - 1); - this.method1490(4096, -1 + var6, var5); - } - } - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "mj.N(" + var1 + ',' + 2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - private boolean method1487(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - if (var2 + var3 > var8 && var3 < var1 + var8) { - - return var5 < var4 + var6 && var7 + var5 > var6; - } else { - return false; - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "mj.M(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + (byte) -125 + ')'); - } - } - - final boolean method1488(int var1, int var2, int var4, int var5, int var6, int var7, int var8) { - try { - if (1 != var7) { - if (var2 <= var5 && var2 + var7 + -1 >= var5 && var7 + var1 + -1 >= var1) { - return true; - } - } else if (var2 == var5 && var4 == var1) { - return true; - } - - if (true) { - var4 -= this.anInt1306; - var5 -= this.anInt1309; - var1 -= this.anInt1306; - var2 -= this.anInt1309; - if (1 == var7) { - if (0 == var6) { - if (var8 == 0) { - if (var5 - 1 == var2 && var4 == var1) { - return true; - } - - if (var2 == var5 && var1 - -1 == var4 && (this.anIntArrayArray1304[var2][var4] & 19661088) == 0) { - return true; - } - - if (var2 == var5 && -1 + var1 == var4 && 0 == (this.anIntArrayArray1304[var2][var4] & 19661058)) { - return true; - } - } else if (1 == var8) { - if (var5 == var2 && var4 == var1 - -1) { - return true; - } - - if (var5 + -1 == var2 && var4 == var1 && 0 == (this.anIntArrayArray1304[var2][var4] & 19661064)) { - return true; - } - - if (var2 == 1 + var5 && var4 == var1 && (this.anIntArrayArray1304[var2][var4] & 19661184) == 0) { - return true; - } - } else if (var8 == 2) { - if (1 + var5 == var2 && var1 == var4) { - return true; - } - - if (var2 == var5 && var4 == 1 + var1 && (this.anIntArrayArray1304[var2][var4] & 19661088) == 0) { - return true; - } - - if (var5 == var2 && var4 == var1 - 1 && (this.anIntArrayArray1304[var2][var4] & 19661058) == 0) { - return true; - } - } else if (var8 == 3) { - if (var2 == var5 && var4 == var1 + -1) { - return true; - } - - if (-1 + var5 == var2 && var4 == var1 && (this.anIntArrayArray1304[var2][var4] & 19661064) == 0) { - return true; - } - - if (var2 == var5 - -1 && var4 == var1 && (this.anIntArrayArray1304[var2][var4] & 19661184) == 0) { - return true; - } - } - } - - if (2 == var6) { - if (var8 == 0) { - if (var2 == var5 - 1 && var1 == var4) { - return true; - } - - if (var5 == var2 && var4 == 1 + var1) { - return true; - } - - if (var2 == 1 + var5 && var1 == var4 && (19661184 & this.anIntArrayArray1304[var2][var4]) == 0) { - return true; - } - - if (var5 == var2 && var4 == var1 + -1 && (this.anIntArrayArray1304[var2][var4] & 19661058) == 0) { - return true; - } - } else if (var8 == 1) { - if (var2 == var5 + -1 && var1 == var4 && 0 == (this.anIntArrayArray1304[var2][var4] & 19661064)) { - return true; - } - - if (var5 == var2 && var4 == var1 + 1) { - return true; - } - - if (var5 + 1 == var2 && var4 == var1) { - return true; - } - - if (var2 == var5 && var4 == -1 + var1 && (19661058 & this.anIntArrayArray1304[var2][var4]) == 0) { - return true; - } - } else if (var8 == 2) { - if (var2 == -1 + var5 && var4 == var1 && (this.anIntArrayArray1304[var2][var4] & 19661064) == 0) { - return true; - } - - if (var5 == var2 && 1 + var1 == var4 && (19661088 & this.anIntArrayArray1304[var2][var4]) == 0) { - return true; - } - - if (var5 - -1 == var2 && var4 == var1) { - return true; - } - - if (var5 == var2 && var1 + -1 == var4) { - return true; - } - } else if (var8 == 3) { - if (var5 - 1 == var2 && var1 == var4) { - return true; - } - - if (var5 == var2 && var1 + 1 == var4 && 0 == (19661088 & this.anIntArrayArray1304[var2][var4])) { - return true; - } - - if (var5 - -1 == var2 && var1 == var4 && (19661184 & this.anIntArrayArray1304[var2][var4]) == 0) { - return true; - } - - if (var5 == var2 && -1 + var1 == var4) { - return true; - } - } - } - - if (9 == var6) { - if (var5 == var2 && var4 == 1 + var1 && 0 == (this.anIntArrayArray1304[var2][var4] & 32)) { - return true; - } - - if (var2 == var5 && -1 + var1 == var4 && (this.anIntArrayArray1304[var2][var4] & 2) == 0) { - return true; - } - - if (var2 == -1 + var5 && var1 == var4 && (this.anIntArrayArray1304[var2][var4] & 8) == 0) { - return true; - } - - return var5 - -1 == var2 && var4 == var1 && (this.anIntArrayArray1304[var2][var4] & 128) == 0; - } - } else { - int var9 = -1 + var7 + var2; - int var10 = -1 + var4 - -var7; - if (var6 == 0) { - if (0 == var8) { - if (-var7 + var5 == var2 && var1 >= var4 && var10 >= var1) { - return true; - } - - if (var2 <= var5 && var5 <= var9 && var4 == 1 + var1 && 0 == (this.anIntArrayArray1304[var5][var4] & 19661088)) { - return true; - } - - if (var5 >= var2 && var9 >= var5 && var4 == -var7 + var1 && (this.anIntArrayArray1304[var5][var10] & 19661058) == 0) { - return true; - } - } else if (1 == var8) { - if (var2 <= var5 && var5 <= var9 && var4 == var1 - -1) { - return true; - } - - if (var5 - var7 == var2 && var1 >= var4 && var1 <= var10 && (this.anIntArrayArray1304[var9][var1] & 19661064) == 0) { - return true; - } - - if (var5 + 1 == var2 && var4 <= var1 && var10 >= var1 && 0 == (this.anIntArrayArray1304[var2][var1] & 19661184)) { - return true; - } - } else if (2 == var8) { - if (1 + var5 == var2 && var4 <= var1 && var10 >= var1) { - return true; - } - - if (var2 <= var5 && var5 <= var9 && 1 + var1 == var4 && (this.anIntArrayArray1304[var5][var4] & 19661088) == 0) { - return true; - } - - if (var2 <= var5 && var5 <= var9 && var4 == var1 - var7 && 0 == (19661058 & this.anIntArrayArray1304[var5][var10])) { - return true; - } - } else if (3 == var8) { - if (var2 <= var5 && var9 >= var5 && var1 + -var7 == var4) { - return true; - } - - if (var2 == var5 - var7 && var1 >= var4 && var1 <= var10 && (19661064 & this.anIntArrayArray1304[var9][var1]) == 0) { - return true; - } - - if (var2 == 1 + var5 && var4 <= var1 && var10 >= var1 && (this.anIntArrayArray1304[var2][var1] & 19661184) == 0) { - return true; - } - } - } - - if (var6 == 2) { - if (var8 == 0) { - if (var2 == -var7 + var5 && var4 <= var1 && var1 <= var10) { - return true; - } - - if (var2 <= var5 && var5 <= var9 && 1 + var1 == var4) { - return true; - } - - if (var2 == 1 + var5 && var1 >= var4 && var1 <= var10 && 0 == (this.anIntArrayArray1304[var2][var1] & 19661184)) { - return true; - } - - if (var2 <= var5 && var9 >= var5 && -var7 + var1 == var4 && (19661058 & this.anIntArrayArray1304[var5][var10]) == 0) { - return true; - } - } else if (var8 == 1) { - if (var2 == var5 - var7 && var1 >= var4 && var10 >= var1 && (19661064 & this.anIntArrayArray1304[var9][var1]) == 0) { - return true; - } - - if (var5 >= var2 && var5 <= var9 && var4 == 1 + var1) { - return true; - } - - if (var5 + 1 == var2 && var1 >= var4 && var10 >= var1) { - return true; - } - - if (var2 <= var5 && var9 >= var5 && var4 == -var7 + var1 && (this.anIntArrayArray1304[var5][var10] & 19661058) == 0) { - return true; - } - } else if (var8 == 2) { - if (-var7 + var5 == var2 && var4 <= var1 && var10 >= var1 && (this.anIntArrayArray1304[var9][var1] & 19661064) == 0) { - return true; - } - - if (var5 >= var2 && var5 <= var9 && var4 == var1 + 1 && (this.anIntArrayArray1304[var5][var4] & 19661088) == 0) { - return true; - } - - if (1 + var5 == var2 && var1 >= var4 && var1 <= var10) { - return true; - } - - if (var2 <= var5 && var5 <= var9 && var4 == -var7 + var1) { - return true; - } - } else if (var8 == 3) { - if (-var7 + var5 == var2 && var1 >= var4 && var1 <= var10) { - return true; - } - - if (var5 >= var2 && var5 <= var9 && 1 + var1 == var4 && (this.anIntArrayArray1304[var5][var4] & 19661088) == 0) { - return true; - } - - if (var2 == 1 + var5 && var4 <= var1 && var1 <= var10 && (this.anIntArrayArray1304[var2][var1] & 19661184) == 0) { - return true; - } - - if (var2 <= var5 && var5 <= var9 && var1 + -var7 == var4) { - return true; - } - } - } - - if (var6 == 9) { - if (var5 >= var2 && var9 >= var5 && var1 - -1 == var4 && (this.anIntArrayArray1304[var5][var4] & 19661088) == 0) { - return true; - } - - if (var2 <= var5 && var9 >= var5 && var1 - var7 == var4 && (19661058 & this.anIntArrayArray1304[var5][var10]) == 0) { - return true; - } - - if (var2 == -var7 + var5 && var1 >= var4 && var10 >= var1 && (19661064 & this.anIntArrayArray1304[var9][var1]) == 0) { - return true; - } - - return 1 + var5 == var2 && var4 <= var1 && var10 >= var1 && (this.anIntArrayArray1304[var2][var1] & 19661184) == 0; - } - } - - } - return false; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "mj.D(" + var1 + ',' + var2 + ',' + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ')'); - } - } - - final void method1489(int var1, boolean var2, byte var3, int var4, int var5, int var6) { - try { - var4 -= this.anInt1306; - var1 -= this.anInt1309; - int var7 = 256; - if (var2) { - var7 += 131072; - } - - int var8 = var1; - if (var3 > 57) { - for (; var1 + var5 > var8; ++var8) { - if (var8 >= 0 && var8 < this.anInt1300) { - for (int var9 = var4; var9 < var6 + var4; ++var9) { - if (0 <= var9 && var9 < this.anInt1303) { - this.method1490(var7, var8, var9); - } - } - } - } - - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "mj.B(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - private void method1490(int var1, int var3, int var4) { - try { - - this.anIntArrayArray1304[var3][var4] = TextureOperation3.bitwiseOr(this.anIntArrayArray1304[var3][var4], var1); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "mj.J(" + var1 + ',' + (byte) -80 + ',' + var3 + ',' + var4 + ')'); - } - } - - final boolean method1492(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - if (var5 == 1) { - if (var3 == var7 && var4 == var1) { - return true; - } - } else if (var3 >= var7 && var3 <= -1 + var5 + var7 && -1 + var1 + var5 >= var1) { - return true; - } - - if (var8 <= 78) { - TextCore.aClass94_1301 = null; - } - - var7 -= this.anInt1309; - var1 -= this.anInt1306; - var3 -= this.anInt1309; - var4 -= this.anInt1306; - if (var5 == 1) { - if (6 == var2 || var2 == 7) { - if (var2 == 7) { - var6 = var6 - -2 & 3; - } - - if (var6 == 0) { - if (1 + var3 == var7 && var1 == var4 && (this.anIntArrayArray1304[var7][var4] & 128) == 0) { - return true; - } - - if (var7 == var3 && var4 == -1 + var1 && (2 & this.anIntArrayArray1304[var7][var4]) == 0) { - return true; - } - } else if (var6 == 1) { - if (var7 == var3 + -1 && var4 == var1 && (this.anIntArrayArray1304[var7][var4] & 8) == 0) { - return true; - } - - if (var3 == var7 && var4 == -1 + var1 && (2 & this.anIntArrayArray1304[var7][var4]) == 0) { - return true; - } - } else if (var6 == 2) { - if (var3 - 1 == var7 && var1 == var4 && 0 == (8 & this.anIntArrayArray1304[var7][var4])) { - return true; - } - - if (var3 == var7 && var4 == 1 + var1 && (32 & this.anIntArrayArray1304[var7][var4]) == 0) { - return true; - } - } else if (3 == var6) { - if (var3 - -1 == var7 && var4 == var1 && (128 & this.anIntArrayArray1304[var7][var4]) == 0) { - return true; - } - - if (var7 == var3 && var1 - -1 == var4 && (32 & this.anIntArrayArray1304[var7][var4]) == 0) { - return true; - } - } - } - - if (var2 == 8) { - if (var7 == var3 && 1 + var1 == var4 && 0 == (32 & this.anIntArrayArray1304[var7][var4])) { - return true; - } - - if (var3 == var7 && var1 + -1 == var4 && (this.anIntArrayArray1304[var7][var4] & 2) == 0) { - return true; - } - - if (-1 + var3 == var7 && var4 == var1 && 0 == (8 & this.anIntArrayArray1304[var7][var4])) { - return true; - } - - return var3 - -1 == var7 && var1 == var4 && (128 & this.anIntArrayArray1304[var7][var4]) == 0; - } - } else { - int var9 = var7 + var5 + -1; - int var10 = -1 + var4 + var5; - if (6 == var2 || var2 == 7) { - if (var2 == 7) { - var6 = 2 + var6 & 3; - } - - if (var6 == 0) { - if (var7 == 1 + var3 && var1 >= var4 && var1 <= var10 && (this.anIntArrayArray1304[var7][var1] & 128) == 0) { - return true; - } - - if (var7 <= var3 && var9 >= var3 && var4 == var1 + -var5 && (2 & this.anIntArrayArray1304[var3][var10]) == 0) { - return true; - } - } else if (var6 == 1) { - if (var7 == -var5 + var3 && var1 >= var4 && var1 <= var10 && (8 & this.anIntArrayArray1304[var9][var1]) == 0) { - return true; - } - - if (var7 <= var3 && var3 <= var9 && var1 + -var5 == var4 && 0 == (this.anIntArrayArray1304[var3][var10] & 2)) { - return true; - } - } else if (var6 == 2) { - if (-var5 + var3 == var7 && var1 >= var4 && var10 >= var1 && (8 & this.anIntArrayArray1304[var9][var1]) == 0) { - return true; - } - - if (var7 <= var3 && var3 <= var9 && var1 + 1 == var4 && 0 == (32 & this.anIntArrayArray1304[var3][var4])) { - return true; - } - } else if (var6 == 3) { - if (var7 == 1 + var3 && var4 <= var1 && var10 >= var1 && (128 & this.anIntArrayArray1304[var7][var1]) == 0) { - return true; - } - - if (var7 <= var3 && var9 >= var3 && var4 == var1 - -1 && 0 == (this.anIntArrayArray1304[var3][var4] & 32)) { - return true; - } - } - } - - if (var2 == 8) { - if (var7 <= var3 && var9 >= var3 && var4 == 1 + var1 && (this.anIntArrayArray1304[var3][var4] & 32) == 0) { - return true; - } - - if (var7 <= var3 && var3 <= var9 && var4 == var1 - var5 && (this.anIntArrayArray1304[var3][var10] & 2) == 0) { - return true; - } - - if (var7 == -var5 + var3 && var4 <= var1 && var1 <= var10 && (this.anIntArrayArray1304[var9][var1] & 8) == 0) { - return true; - } - - return 1 + var3 == var7 && var1 >= var4 && var1 <= var10 && 0 == (this.anIntArrayArray1304[var7][var1] & 128); - } - } - - return false; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "mj.Q(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ')'); - } - } - - private boolean method1494(int var1, int var2, int var3, int var4, int var6, int var7, int var8, int var9, int var10) { - try { - int var11 = var7 + var9; - int var12 = var8 + var10; - int var13 = var3 + var1; - int var14 = var2 - -var6; - int var15; - int var16; - if (var7 >= var1 && var7 < var13) { - if (var12 == var2 && (4 & var4) == 0) { - var15 = var7; - - for (var16 = var13 >= var11 ? var11 : var13; var16 > var15; ++var15) { - if (0 == (this.anIntArrayArray1304[-this.anInt1309 + var15][var12 - (this.anInt1306 + 1)] & 2)) { - return true; - } - } - } else if (var8 == var14 && (1 & var4) == 0) { - var15 = var7; - - for (var16 = var13 < var11 ? var13 : var11; var15 < var16; ++var15) { - if ((32 & this.anIntArrayArray1304[var15 - this.anInt1309][var8 + -this.anInt1306]) == 0) { - return true; - } - } - } - } else if (var11 > var1 && var13 >= var11) { - if (var12 == var2 && 0 == (4 & var4)) { - for (var15 = var1; var15 < var11; ++var15) { - if ((2 & this.anIntArrayArray1304[-this.anInt1309 + var15][var12 - (this.anInt1306 + 1)]) == 0) { - return true; - } - } - } else if (var14 == var8 && (1 & var4) == 0) { - for (var15 = var1; var11 > var15; ++var15) { - if ((32 & this.anIntArrayArray1304[-this.anInt1309 + var15][-this.anInt1306 + var8]) == 0) { - return true; - } - } - } - } else if (var8 >= var2 && var14 > var8) { - if (var11 == var1 && 0 == (8 & var4)) { - var15 = var8; - - for (var16 = var12 <= var14 ? var12 : var14; var16 > var15; ++var15) { - if ((8 & this.anIntArrayArray1304[-1 + -this.anInt1309 + var11][var15 - this.anInt1306]) == 0) { - return true; - } - } - } else if (var13 == var7 && (var4 & 2) == 0) { - var15 = var8; - - for (var16 = var14 < var12 ? var14 : var12; var16 > var15; ++var15) { - if ((this.anIntArrayArray1304[-this.anInt1309 + var7][-this.anInt1306 + var15] & 128) == 0) { - return true; - } - } - } - } else if (var2 < var12 && var12 <= var14) { - if (var1 == var11 && (var4 & 8) == 0) { - for (var15 = var2; var12 > var15; ++var15) { - if ((this.anIntArrayArray1304[-this.anInt1309 + var11 + -1][-this.anInt1306 + var15] & 8) == 0) { - return true; - } - } - } else if (var7 == var13 && (var4 & 2) == 0) { - for (var15 = var2; var12 > var15; ++var15) { - if ((128 & this.anIntArrayArray1304[var7 + -this.anInt1309][-this.anInt1306 + var15]) == 0) { - return true; - } - } - } - } - - return false; - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "mj.O(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + true + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var10 + ')'); - } - } - - final void method1496() { - try { - for (int var2 = 0; var2 < this.anInt1300; ++var2) { - for (int var3 = 0; var3 < this.anInt1303; ++var3) { - if (var2 != 0 && 0 != var3 && var2 < this.anInt1300 - 5 && this.anInt1303 - 5 > var3) { - this.anIntArrayArray1304[var2][var3] = 16777216; - } else { - this.anIntArrayArray1304[var2][var3] = 16777215; - } - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "mj.P(" + 0 + ')'); - } - } - - final void method1497(int var1, int var3) { - try { - var1 -= this.anInt1306; - var3 -= this.anInt1309; - this.anIntArrayArray1304[var3][var1] = TextureOperation3.bitwiseOr(this.anIntArrayArray1304[var3][var1], 2097152); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "mj.E(" + var1 + ',' + 7605 + ',' + var3 + ')'); - } - } - - final boolean method1498(int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - try { - if (var5 > 1) { - return this.method1487(var5, var6, var2, var9, var3, var8, var5, var4) || this.method1494(var2, var8, var6, var7, var9, var4, var3, var5, var5); - } else { - int var10 = -1 + var6 + var2; - int var11 = var8 + (var9 - 1); - if (var4 >= var2 && var10 >= var4 && var8 <= var3 && var11 >= var3) { - return true; - } else { - if (false) { - this.method1501((byte) -85, 4, 106, -39); - } - - return var2 + -1 == var4 && var8 <= var3 && var3 <= var11 && (this.anIntArrayArray1304[-this.anInt1309 + var4][var3 - this.anInt1306] & 8) == 0 && (var7 & 8) == 0 || (var4 == 1 + var10 && var3 >= var8 && var3 <= var11 && 0 == (128 & this.anIntArrayArray1304[var4 - this.anInt1309][-this.anInt1306 + var3]) && (var7 & 2) == 0 || (var8 - 1 == var3 && var4 >= var2 && var10 >= var4 && (2 & this.anIntArrayArray1304[-this.anInt1309 + var4][-this.anInt1306 + var3]) == 0 && (4 & var7) == 0 || 1 + var11 == var3 && var4 >= var2 && var4 <= var10 && (32 & this.anIntArrayArray1304[var4 - this.anInt1309][var3 + -this.anInt1306]) == 0 && (var7 & 1) == 0)); - } - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "mj.S(" + true + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ')'); - } - } - - final void method1499(int var1, int var3) { - try { - var3 -= this.anInt1309; - var1 -= this.anInt1306; - this.anIntArrayArray1304[var3][var1] = Unsorted.bitwiseAnd(this.anIntArrayArray1304[var3][var1], -262145); - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "mj.I(" + var1 + ',' + (byte) -73 + ',' + var3 + ')'); - } - } - - final boolean method1500(int var2, int var3, int var4, int var5) { - try { - if (var5 == var4 && var3 == var2) { - return true; - } else { - var3 -= this.anInt1306; - - var4 -= this.anInt1309; - if (0 <= var4 && this.anInt1300 > var4 && var3 >= 0 && this.anInt1303 > var3) { - var2 -= this.anInt1306; - var5 -= this.anInt1309; - int var6; - if (var5 < var4) { - var6 = var4 - var5; - } else { - var6 = -var4 + var5; - } - - int var7; - if (var2 >= var3) { - var7 = -var3 + var2; - } else { - var7 = var3 - var2; - } - - int var8; - int var9; - if (var7 >= var6) { - var9 = 32768; - var8 = 65536 * var6 / var7; - - while (var3 != var2) { - if (var2 < var3) { - if (0 != (this.anIntArrayArray1304[var5][var2] & 19661058)) { - return false; - } - - ++var2; - } else { - if (0 != (19661088 & this.anIntArrayArray1304[var5][var2])) { - return false; - } - - --var2; - } - - var9 += var8; - if (var9 >= 65536) { - var9 -= 65536; - if (var5 >= var4) { - if (var5 > var4) { - if ((19661184 & this.anIntArrayArray1304[var5][var2]) != 0) { - return false; - } - - --var5; - } - } else { - if ((19661064 & this.anIntArrayArray1304[var5][var2]) != 0) { - return false; - } - - ++var5; - } - } - } - } else { - var8 = 65536 * var7 / var6; - var9 = 32768; - - while (var5 != var4) { - if (var4 <= var5) { - if ((this.anIntArrayArray1304[var5][var2] & 19661184) != 0) { - return false; - } - - --var5; - } else { - if (0 != (this.anIntArrayArray1304[var5][var2] & 19661064)) { - return false; - } - - ++var5; - } - - var9 += var8; - if (var9 >= 65536) { - var9 -= 65536; - if (var3 > var2) { - if (0 != (this.anIntArrayArray1304[var5][var2] & 19661058)) { - return false; - } - - ++var2; - } else if (var3 < var2) { - if (0 != (19661088 & this.anIntArrayArray1304[var5][var2])) { - return false; - } - - --var2; - } - } - } - } - - return 0 == (19136768 & this.anIntArrayArray1304[var4][var3]); - } else { - return false; - } - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "mj.G(" + -2 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - private void method1501(byte var1, int var2, int var3, int var4) { - try { - if (var1 >= 18) { - this.anIntArrayArray1304[var3][var2] = Unsorted.bitwiseAnd(this.anIntArrayArray1304[var3][var2], ~var4); - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "mj.H(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - final void method1502(int var2, int var3, boolean var4, int var5, int var6, int var7) { - try { - var2 -= this.anInt1309; - var7 -= this.anInt1306; - int var8 = 256; - - if (var4) { - var8 += 131072; - } - - int var9; - if (1 == var5 || var5 == 3) { - var9 = var3; - var3 = var6; - var6 = var9; - } - - for (var9 = var2; var9 < var2 - -var3; ++var9) { - if (0 <= var9 && var9 < this.anInt1300) { - for (int var10 = var7; var6 + var7 > var10; ++var10) { - if (var10 >= 0 && var10 < this.anInt1303) { - this.method1501((byte) 32, var10, var9, var8); - } - } - } - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "mj.A(" + 20851 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - final void method1503(int var1, int var2) { - try { - var2 -= this.anInt1306; - var1 -= this.anInt1309; - this.anIntArrayArray1304[var1][var2] = TextureOperation3.bitwiseOr(this.anIntArrayArray1304[var1][var2], 262144); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "mj.C(" + var1 + ',' + var2 + ',' + -5 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class92.java b/Client/src/main/java/org/runite/client/Class92.java deleted file mode 100644 index 6fbd89264..000000000 --- a/Client/src/main/java/org/runite/client/Class92.java +++ /dev/null @@ -1,137 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.config.GameConfig; - -import javax.media.opengl.GL; - - -final class Class92 { - - private static final float[] light1Position = new float[4]; - static float[] light0Position = new float[4]; - static int lightX; - static int lightY; - static int defaultRegionAmbientRGB = 13156520;//13156520 - static float[] fogColor = new float[4]; - static int defaultScreenColorRgb = 16777215;//16777215 - private static int screenColorRgb = -1; - private static float light0Diffuse = -1.0F; - private static float light1Diffuse = -1.0F; - private static float lightModelAmbient; - private static int fogOffset = -1; - private static int fogColorRGB = -1; - - - static void method1504() { - GL gl = HDToolKit.gl; - gl.glLightfv(16384, 4611, light0Position, 0); - gl.glLightfv(16385, 4611, light1Position, 0); - } - - static float getLightingModelAmbient() { - return light0Diffuse; - } - - static void setLightParams(int color, float ambientMod, float l0Diffuse, float l1Diffuse) { - if (screenColorRgb != color || lightModelAmbient != ambientMod || light0Diffuse != l0Diffuse || light1Diffuse != l1Diffuse) { - screenColorRgb = color; - lightModelAmbient = ambientMod; - light0Diffuse = l0Diffuse; - light1Diffuse = l1Diffuse; - final GL gl = HDToolKit.gl; - final float red = (color >> 16 & 0xff) / 255.0F; - final float green = (color >> 8 & 0xff) / 255.0F; - final float blue = (color & 0xff) / 255.0F; - final float[] lightModelAmbientParams = {ambientMod * red, ambientMod * green, ambientMod * blue, 1.0F}; - gl.glLightModelfv(2899, lightModelAmbientParams, 0);//LIGHT_MODEL_AMBIENT - final float[] light0Params = {l0Diffuse * red, l0Diffuse * green, l0Diffuse * blue, 1.0F}; - gl.glLightfv(16384, 4609, light0Params, 0);//LIGHT0, DIFFUSE - final float[] light1Params = {-l1Diffuse * red, -l1Diffuse * green, -l1Diffuse * blue, 1.0F}; - gl.glLightfv(16385, 4609, light1Params, 0);//LIGHT1, DIFFUSE - } - } - - static void setFogValues(int fogCol, int fogOff) { - if (fogColorRGB != fogCol || fogOffset != fogOff) { - fogColorRGB = fogCol; - fogOffset = fogOff; - final GL gl = HDToolKit.gl; - byte lowestFogStart = 50; - //short baseFogStart = 3584; This is unused because it was originally this but to avoid math jagex simplified it. - fogColor[0] = (fogCol >> 16 & 0xff) / 255.0F; - fogColor[1] = (fogCol >> 8 & 0xff) / 255.0F; - fogColor[2] = (fogCol & 0xff) / 255.0F; - //2917 FOG_MODE - //9729 LINEAR - gl.glFogi(2917, 9729); - //FOG_DENSITY - gl.glFogf(2914, 0.95F); - //3156 = FOG_HINT - //4353 = FASTEST, 4354 = NICEST, 4352 = DONT_CARE - gl.glHint(3156, 4353); - int fogStart = 3072 - fogOff;//baseFogStart - 512 - fogOff - if (fogStart < lowestFogStart) { - fogStart = lowestFogStart; - } - //FOG_START - gl.glFogf(2915, GameConfig.RENDER_DISTANCE_FOG_FIX - 256);//3072 - //FOG_END - gl.glFogf(2916, GameConfig.RENDER_DISTANCE_FOG_FIX);//baseFogStart - 256 - //FOG_COLOR - gl.glFogfv(2918, fogColor, 0); - } - } - - static void setLightPosition(float x, float y, float z) { - if (light0Position[0] != x || light0Position[1] != y || light0Position[2] != z) { - light0Position[0] = x; - light0Position[1] = y; - light0Position[2] = z; - light1Position[0] = -x; - light1Position[1] = -y; - light1Position[2] = -z; - lightX = (int) (x * 256.0F / y); - lightY = (int) (z * 256.0F / y); - } - } - - static int screenColorRgb() { - return screenColorRgb; - } - - static void method1511() { - final GL gl = HDToolKit.gl; - gl.glColorMaterial(1028, 5634);//FRONT, AMBIENT_AND_DIFFUSE - gl.glEnable(2903);//COLOR_MATERIAL - final float[] light0Params = {0.0F, 0.0F, 0.0F, 1.0F}; - gl.glLightfv(16384, 4608, light0Params, 0);//LIGHT0, AMBIENT - gl.glEnable(16384);//LIGHT0 - final float[] light1Params = {0.0F, 0.0F, 0.0F, 1.0F}; - gl.glLightfv(16385, 4608, light1Params, 0);//LIGHT1, AMBIENT - gl.glEnable(16385);//LIGHT1 - screenColorRgb = -1; - fogColorRGB = -1; - initDefaults(); - } - - static void method1512(float[] var0) { - if (var0 == null) { - var0 = fogColor; - } - - GL gl = HDToolKit.gl; - gl.glFogfv(2918, var0, 0); - } - - private static void initDefaults() { - setLightParams(defaultScreenColorRgb, 1.1523438F, 0.69921875F, 1.2F); - setLightPosition(-50.0F, -60.0F, -50.0F); - setFogValues(defaultRegionAmbientRGB, 0); - } - - static float method1514() { - return lightModelAmbient; - } - -} diff --git a/Client/src/main/java/org/runite/client/Class95.java b/Client/src/main/java/org/runite/client/Class95.java deleted file mode 100644 index 9e897cec3..000000000 --- a/Client/src/main/java/org/runite/client/Class95.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.runite.client; - -public final class Class95 { - - static int anInt1336 = 0; - static int anInt1338; - static int anInt1343; - static int anInt1344; - - - static void method1583(int var0, int var1, int var2, int var4, int var5, int var6, int var7, int var8, int var9) { - try { - - if (var1 == var0 && var2 == var9 && var8 == var7 && var5 == var6) { - TextureOperation25.method330(var4, 3 + -111, var5, var0, var9, var7); - } else { - int var10 = var0; - int var13 = 3 * var9; - int var12 = 3 * var0; - int var11 = var9; - int var14 = var1 * 3; - int var15 = 3 * var2; - int var16 = 3 * var8; - int var17 = var6 * 3; - int var18 = var14 + var7 - (var16 + var0); - int var20 = var16 - (var14 - -var14) - -var12; - int var19 = var15 + -var17 + (var5 - var9); - int var21 = var13 + -var15 + -var15 + var17; - int var22 = var14 + -var12; - int var23 = -var13 + var15; - - for (int var24 = 128; var24 <= 4096; var24 += 128) { - int var25 = var24 * var24 >> 12; - int var26 = var24 * var25 >> 12; - int var30 = var21 * var25; - int var28 = var26 * var19; - int var29 = var25 * var20; - int var27 = var18 * var26; - int var31 = var22 * var24; - int var33 = var0 - -(var29 + (var27 - -var31) >> 12); - int var32 = var24 * var23; - int var34 = var9 - -(var32 + var28 - -var30 >> 12); - TextureOperation25.method330(var4, -88, var34, var10, var11, var33); - var10 = var33; - var11 = var34; - } - } - - } catch (RuntimeException var35) { - throw ClientErrorException.clientError(var35, "nb.B(" + var0 + ',' + var1 + ',' + var2 + ',' + 3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ')'); - } - } - - static void method1584(int var0, int var1, int var2, int var3, int var5) { - try { - - if (var5 == var1) { - Class43.method1058(var5, var2, var0, var3); - } else if (Class101.anInt1425 <= var2 - var5 && var2 + var5 <= Class3_Sub28_Sub18.anInt3765 && -var1 + var3 >= Class159.anInt2020 && Class57.anInt902 >= var3 - -var1) { - Class3_Sub9.method135(var3, var2, var5, var1, var0); - } else { - Class3_Sub28_Sub5.method556(var0, var1, var5, (byte) -123, var2, var3); - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "nb.E(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + -26571 + ',' + var5 + ')'); - } - } - - public static int method1585(byte var0, int var1) { - try { - --var1; - var1 |= var1 >>> 1; - var1 |= var1 >>> 2; - var1 |= var1 >>> 4; - var1 |= var1 >>> 8; - var1 |= var1 >>> 16; - return 1 + var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nb.C(" + var0 + ',' + var1 + ')'); - } - } - - static RSString method1586() { - try { - RSString var2 = new RSString(); - - var2.length = 1; - var2.buffer = new byte[1]; - var2.buffer[0] = (byte) 160; - return var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nb.A(" + 23161 + ',' + 160 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Class96.java b/Client/src/main/java/org/runite/client/Class96.java deleted file mode 100644 index 018245134..000000000 --- a/Client/src/main/java/org/runite/client/Class96.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.runite.client; - -public final class Class96 { - - static int anInt1358 = 0; - public int anInt1346; - public int anInt1347; - public int anInt1350; - public int anInt1351; - public int anInt1353; - public int anInt1355 = -1; - public int anInt1356; - public int anInt1359; - public int anInt1360; - -} diff --git a/Client/src/main/java/org/runite/client/Class97.java b/Client/src/main/java/org/runite/client/Class97.java deleted file mode 100644 index 6469e23f3..000000000 --- a/Client/src/main/java/org/runite/client/Class97.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -public final class Class97 { - - public static CacheIndex aClass153_1378; - static byte[] aByteArray1364 = new byte['\u8080']; - static CacheIndex aClass153_1370; - static CacheIndex aClass153_1372; - static int[][] anIntArrayArray1373; - static RSString aClass94_1374; - static int anInt1375; - static RSString aClass94_1380; - - static { - int var0 = 0; - - for (int var1 = 0; var1 < 256; ++var1) { - for (int var2 = 0; var2 <= var1; ++var2) { - aByteArray1364[var0++] = (byte) ((int) (255.0D / Math.sqrt((float) (65535 + var2 * var2 + var1 * var1) / 65535.0F))); - } - } - - aClass94_1374 = RSString.parse("zap"); - anIntArrayArray1373 = new int[104][104]; - aClass94_1380 = RSString.parse(")4p="); - } - - private final int anInt1367; - private final int anInt1369; - boolean aBoolean1379 = false; - private int[][][] anIntArrayArrayArray1362; - private int anInt1365 = -1; - private LinkedList aLinkedList_1366 = new LinkedList(); - private int anInt1368 = 0; - private Class3_Sub20[] aClass3_Sub20Array1371; - - Class97(int var1, int var2, int var3) { - try { - this.anInt1369 = var2; - this.anInt1367 = var1; - this.aClass3_Sub20Array1371 = new Class3_Sub20[this.anInt1369]; - this.anIntArrayArrayArray1362 = new int[this.anInt1367][3][var3]; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "nd.(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static void method1591(Class3_Sub24 var1) { - try { - if (var1.aSound_2544 != null) { - var1.aSound_2544.anInt2374 = 0; - } - - var1.aBoolean2545 = false; - - for (Class3_Sub24 var2 = var1.method411(); var2 != null; var2 = var1.method414()) { - method1591(var2); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nd.A(" + true + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method1593(int var0, CacheIndex var1) { - try { - Class154.anInt1966 = var1.getArchiveForName(TextCore.aClass94_3574); - CSConfigCachefile.anInt1124 = var1.getArchiveForName(TextCore.aClass94_1341); - if (var0 <= 108) { - method1593(14, null); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nd.C(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - final int[][][] method1589() { - try { - if (this.anInt1367 == this.anInt1369) { - - for (int var2 = 0; var2 < this.anInt1367; ++var2) { - this.aClass3_Sub20Array1371[var2] = Class3_Sub28_Sub1.aClass3_Sub20_3532; - } - - return this.anIntArrayArrayArray1362; - } else { - throw new RuntimeException("Can only retrieve a full image cache"); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nd.F(" + (byte) -56 + ')'); - } - } - - final void method1590() { - try { - for (int var2 = 0; var2 < this.anInt1367; ++var2) { - this.anIntArrayArrayArray1362[var2][0] = null; - this.anIntArrayArrayArray1362[var2][1] = null; - this.anIntArrayArrayArray1362[var2][2] = null; - this.anIntArrayArrayArray1362[var2] = null; - } - - this.aClass3_Sub20Array1371 = null; - this.anIntArrayArrayArray1362 = null; - this.aLinkedList_1366.method1211(-118); - this.aLinkedList_1366 = null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nd.E(" + (byte) -22 + ')'); - } - } - - final int[][] method1594(byte var1, int var2) { - try { - if (this.anInt1367 == this.anInt1369) { - this.aBoolean1379 = null == this.aClass3_Sub20Array1371[var2]; - this.aClass3_Sub20Array1371[var2] = Class3_Sub28_Sub1.aClass3_Sub20_3532; - return this.anIntArrayArrayArray1362[var2]; - } else if (1 == this.anInt1367) { - this.aBoolean1379 = this.anInt1365 != var2; - this.anInt1365 = var2; - return this.anIntArrayArrayArray1362[0]; - } else { - Class3_Sub20 var4 = this.aClass3_Sub20Array1371[var2]; - if (null == var4) { - this.aBoolean1379 = true; - if (this.anInt1367 > this.anInt1368) { - var4 = new Class3_Sub20(var2, this.anInt1368); - ++this.anInt1368; - } else { - Class3_Sub20 var5 = (Class3_Sub20) this.aLinkedList_1366.method1212(); - var4 = new Class3_Sub20(var2, Objects.requireNonNull(var5).anInt2483); - this.aClass3_Sub20Array1371[var5.anInt2489] = null; - var5.unlink(); - } - - this.aClass3_Sub20Array1371[var2] = var4; - } else { - this.aBoolean1379 = false; - } - - this.aLinkedList_1366.method1216(var4); - return this.anIntArrayArrayArray1362[var4.anInt2483]; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "nd.D(" + var1 + ',' + var2 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Class99.java b/Client/src/main/java/org/runite/client/Class99.java deleted file mode 100644 index 3fe04700a..000000000 --- a/Client/src/main/java/org/runite/client/Class99.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.runite.client; - -import java.awt.*; -import java.util.Objects; - -final class Class99 { - - static short[] aShortArray1398; - static Class33 aClass33_1399; - static RSInterface aClass11_1402; - static int anInt1403 = -1; - - - static void method1596(RSString var0, byte var1, boolean var2) { - try { - if (var1 < 124) { - ObjectDefinition.aReferenceCache_1401 = null; - } - - if (var2) { - if (HDToolKit.highDetail && InterfaceWidget.aBoolean3594) { - try { - Class42.method1056(Class38.gameSignlink.gameApplet, new Object[]{var0.method1547(LinkableRSString.anApplet_Sub1_2588.getCodeBase()).toString()}); - return; - } catch (Throwable var6) { - } - } - - try { - Objects.requireNonNull(LinkableRSString.anApplet_Sub1_2588.getAppletContext()).showDocument(var0.method1547(LinkableRSString.anApplet_Sub1_2588.getCodeBase()), "_blank"); - } catch (Exception var4) { - } - } else { - try { - Objects.requireNonNull(LinkableRSString.anApplet_Sub1_2588.getAppletContext()).showDocument(var0.method1547(LinkableRSString.anApplet_Sub1_2588.getCodeBase()), "_top"); - } catch (Exception var5) { - } - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "nf.C(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ')'); - } - } - - static Frame method1597(int var2, int var3, int var4, Signlink var5) { - try { - if (var5.method1432(false)) { - if (0 == var2) { - Class106[] var6 = Unsorted.method596(var5); - - boolean var7 = false; - - for (int var8 = 0; var6.length > var8; ++var8) { - if (var4 == var6[var8].anInt1447 && var3 == var6[var8].anInt1449 && (!var7 || var6[var8].anInt1450 > var2)) { - var2 = var6[var8].anInt1450; - var7 = true; - } - } - - if (!var7) { - return null; - } - } - - Class64 var10 = var5.method1450(0, var2, var3, var4); - - while (0 == var10.anInt978) { - TimeUtils.sleep(10L); - } - - Frame var11 = (Frame) var10.anObject974; - if (null == var11) { - return null; - } else if (2 == var10.anInt978) { - Unsorted.method593(var11, var5); - return null; - } else { - return var11; - } - } else { - return null; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "nf.D(" + 2 + ',' + 0 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + (var5 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Client.java b/Client/src/main/java/org/runite/client/Client.java deleted file mode 100644 index 477138b8e..000000000 --- a/Client/src/main/java/org/runite/client/Client.java +++ /dev/null @@ -1,1827 +0,0 @@ -package org.runite.client; - - -import net.arikia.dev.drpc.DiscordRPC; -import org.rs09.Discord; -import org.rs09.SystemLogger; -import org.rs09.client.config.GameConfig; -import org.rs09.client.console.DeveloperConsole; -import org.rs09.client.constants.gametype.MechscapeGameAppearance; -import org.rs09.client.constants.gametype.RunescapeGameAppearance; -import org.rs09.client.data.HashTable; -import org.rs09.client.filestore.resources.configs.enums.EnumDefinitionProvider; -import org.rs09.client.filestore.resources.configs.structs.StructDefinitionProvider; -import org.rs09.client.net.Connection; -import org.runite.client.drawcalls.LoadingBox; -import org.runite.client.drawcalls.StartupLoadingBarInitial; -import org.runite.client.drawcalls.StartupLoadingBar; - -import java.awt.*; -import java.io.IOException; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.Objects; - -import static org.rs09.client.constants.Parameter.*; - -public final class Client extends GameShell { - - /** - * - */ - private static final long serialVersionUID = 8336806252605101745L; - public static int messageToDisplay = -2; - public static RSString loadingBarTextToDisplay = RSString.parse(""); - static HashTable aHashTable_2194 = new HashTable(16); - static Class3_Sub11[][] aClass3_Sub11ArrayArray2199; - static int[] anIntArray2200; - static int ZOOM = 600; - static int currentPort; - static int rectDebugInt = 0; - public static int LoadingStageNumber = 10; - static int anInt869; - static int loginScreenInterfaceID; - static int anInt2275 = 1; - static int anInt2317 = 0; - static int anInt3068 = 0; - static int[] anIntArray3288 = new int[]{4, 4, 1, 2, 6, 4, 2, 49, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - static long aLong3296 = 0L; - static Class3_Sub24_Sub4 aClass3_Sub24_Sub4_1193; - static long aLong1310 = 0L; - static int anInt1354 = 0; - static LinkedList aLinkedList_1471 = new LinkedList(); - static Class30 aClass30_1572; - static int[] anIntArray3780 = new int[32]; - static boolean paramAdvertisementSuppressed = false; - static int anInt3773; - static Client clientInstance; - static boolean sweepReferenceCache = false; - - static void method1175(int var0) { - try { - TextureOperation13.anInt3362 = -1; - Texture.anInt1150 = -1; - - Class3_Sub28_Sub1.anInt3536 = var0; - Class3_Sub5.method117(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hj.D(" + var0 + ',' + 112 + ')'); - } - } - - static void method375(CacheIndex var1, CacheIndex var2) { - try { - Class24.aClass153_152 = var1; - LoginHandler.aClass153_1680 = var2; - Class25.anInt497 = LoginHandler.aClass153_1680.getFileAmount(3); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "jl.D(" + 3 + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - static void invalidArgument(String var0) { - try { - System.out.println("Bad " + var0 + ", Usage: worldid, , , "); - System.exit(1); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "kd.O(" + (var0 != null ? "{...}" : "null") + ',' + (byte) 38 + ')'); - } - } - - static RSInterface method42(RSInterface var0) { - int var1 = method44(var0).method94(); - if (var1 == 0) { - return null; - } else { - for (int var2 = 0; var2 < var1; ++var2) { - var0 = Unsorted.getRSInterface(var0.parentId); - if (var0 == null) { - return null; - } - } - - return var0; - } - } - - static Class3_Sub1 method44(RSInterface var0) { - Class3_Sub1 var1 = (Class3_Sub1) Class124.aHashTable_1659.get(((long) var0.componentHash << 32) + (long) var0.anInt191); - return var1 != null ? var1 : var0.aClass3_Sub1_257; - } - - /** - * Client Initialization - *

- * Client command line initialization (parameter modeWhere 3) - * NOTE: Attempting to run the client this way puts paramModeWhere into LOCAL_ENVIRONMENT mode - * All of these arguments can be set through "program arguments" - */ - public static void main(String[] args) { - try { - if (args.length != 4) { - invalidArgument("argument count"); - } - - int language = -1; - - ObjectDefinition.paramWorldID = Integer.parseInt(args[0]); - - Class44.paramModeWhere = LOCAL_ENVIRONMENT; - - switch (args[1]) { - case "live": - TextureOperation20.paramModeWhat = LIVE_SERVER; - break; - case "rc": - TextureOperation20.paramModeWhat = RELEASE_CANDIDATE; - break; - case "wip": - TextureOperation20.paramModeWhat = WORK_IN_PROGRESS; - break; - default: - invalidArgument("modewhat"); - break; - } - - paramAdvertisementSuppressed = false; - - try { - byte[] languageSelection = args[2].getBytes(StandardCharsets.ISO_8859_1); - /* - Accepts en, de, fr, pt - */ - language = TextureOperation15.compareEnteredLanguageArgument(TextureOperation33.bufferToString(languageSelection, languageSelection.length, 0)); - } catch (Exception ignored) { - - } - - if (language == -1) { - switch (args[2]) { - case "english": - Class3_Sub20.paramLanguage = LANGUAGE_ENGLISH; - break; - case "german": - Class3_Sub20.paramLanguage = LANGUAGE_GERMAN; - break; - case "french": - Class3_Sub20.paramLanguage = LANGUAGE_FRENCH; - break; - default: - invalidArgument("language"); - break; - } - } else { - Class3_Sub20.paramLanguage = language; - } - - Unsorted.languageSetter(Class3_Sub20.paramLanguage); - - Class163_Sub2_Sub1.paramObjectTagEnabled = false; - Unsorted.paramJavaScriptEnabled = false; - - switch (args[3]) { - case "game0": - Class158.paramGameTypeID = GAME_TYPE_RUNESCAPE; - break; - case "game1": - Class158.paramGameTypeID = GAME_TYPE_MECHSCAPE; - break; - default: - invalidArgument("game"); - break; - } - - Class3_Sub31.paramCountryID = 0; - - Class106.paramUserUsingInternetExplorer = false; - - Class3_Sub26.paramAffid = NO_AFFILIATE; - - Class163_Sub2.paramSettings = RSString.parse(""); - - Client client = new Client(); - clientInstance = client; - client.launch(); - GameShell.frame.setLocation(40, 40); - - } catch (Exception var4) { - Class49.reportError(null, var4); - } - } - - static void handleItemSwitch(RSInterface[] interfaces, int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - for (RSInterface inter : interfaces) { - if (inter != null && inter.parentId == var1 && (!inter.usingScripts || inter.type == 0 || inter.aBoolean195 || method44(inter).anInt2205 != 0 || inter == PacketParser.aClass11_88 || inter.anInt189 == 1338) && (!inter.usingScripts || !method51(inter))) { - int var10 = inter.anInt306 + var6; - int var11 = inter.anInt210 + var7; - int var12; - int var13; - int var14; - int var15; - if (inter.type == 2) { - var12 = var2; - var13 = var3; - var14 = var4; - var15 = var5; - } else { - int var16 = var10 + inter.width; - int var17 = var11 + inter.height; - if (inter.type == 9) { - ++var16; - ++var17; - } - - var12 = var10 > var2 ? var10 : var2; - var13 = var11 > var3 ? var11 : var3; - var14 = var16 < var4 ? var16 : var4; - var15 = var17 < var5 ? var17 : var5; - } - - if (inter == Class56.aClass11_886) { - Class21.aBoolean440 = true; - Class3_Sub15.anInt2421 = var10; - Class3_Sub2.anInt2218 = var11; - } - - if (!inter.usingScripts || var12 < var14 && var13 < var15) { - if (inter.type == 0) { - if (!inter.usingScripts && method51(inter) && Class107.aClass11_1453 != inter) { - continue; - } - - if (inter.aBoolean219 && Class126.anInt1676 >= var12 && Unsorted.anInt1709 >= var13 && Class126.anInt1676 < var14 && Unsorted.anInt1709 < var15) { - for (CS2Script var27 = (CS2Script) aLinkedList_1471.method1222(); var27 != null; var27 = (CS2Script) aLinkedList_1471.method1221()) { - if (var27.aBoolean2446) { - var27.unlink(); - var27.aClass11_2449.aBoolean163 = false; - } - } - - if (Class75_Sub3.anInt2658 == 0) { - Class56.aClass11_886 = null; - PacketParser.aClass11_88 = null; - } - - Class3_Sub19.anInt2475 = 0; - } - } - - if (inter.usingScripts) { - boolean var26; - var26 = Class126.anInt1676 >= var12 && Unsorted.anInt1709 >= var13 && Class126.anInt1676 < var14 && Unsorted.anInt1709 < var15; - - boolean var25 = false; - if (TextureOperation21.anInt3069 == 1 && var26) { - var25 = true; - } - - boolean var18 = false; - if (Unsorted.anInt3644 == 1 && Class163_Sub1.anInt2993 >= var12 && Class38_Sub1.anInt2614 >= var13 && Class163_Sub1.anInt2993 < var14 && Class38_Sub1.anInt2614 < var15) { - var18 = true; - } - - int var19; - int var21; - if (inter.aByteArray263 != null) { - for (var19 = 0; var19 < inter.aByteArray263.length; ++var19) { - if (!ObjectDefinition.aBooleanArray1490[inter.aByteArray263[var19]]) { - if (inter.anIntArray310 != null) { - inter.anIntArray310[var19] = 0; - } - } else if (inter.anIntArray310 == null || Class44.anInt719 >= inter.anIntArray310[var19]) { - byte var20 = inter.aByteArray231[var19]; - if (var20 == 0 || ((var20 & 2) == 0 || ObjectDefinition.aBooleanArray1490[86]) && ((var20 & 1) == 0 || ObjectDefinition.aBooleanArray1490[82]) && ((var20 & 4) == 0 || ObjectDefinition.aBooleanArray1490[81])) { - Unsorted.method66(RSString.parse(""), -1, var19 + 1, (byte) -29, inter.componentHash); - var21 = inter.anIntArray299[var19]; - if (inter.anIntArray310 == null) { - inter.anIntArray310 = new int[inter.aByteArray263.length]; - } - - if (var21 == 0) { - inter.anIntArray310[var19] = Integer.MAX_VALUE; - } else { - inter.anIntArray310[var19] = Class44.anInt719 + var21; - } - } - } - } - } - - if (var18) { - InterfaceWidget.a(Class38_Sub1.anInt2614 - var11, Class163_Sub1.anInt2993 - var10, 97, inter); - } - - if (Class56.aClass11_886 != null && Class56.aClass11_886 != inter && var26 && (method44(inter).method98() || (inter.componentHash == 49938505 && Class56.aClass11_886.componentHash == 49938505))) { - Class27.aClass11_526 = inter; - } - if (inter == PacketParser.aClass11_88) { - Class85.aBoolean1167 = true; - TextureOperation20.anInt3156 = var10; - Class134.anInt1761 = var11; - } - - if (inter.aBoolean195 || inter.anInt189 != 0) { - CS2Script var30; - if (var26 && Class29.anInt561 != 0 && inter.anObjectArray183 != null) { - var30 = new CS2Script(); - var30.aBoolean2446 = true; - var30.aClass11_2449 = inter; - var30.scrollbarScrollAmount = Class29.anInt561; - var30.arguments = inter.anObjectArray183; - aLinkedList_1471.method1215(var30); - } - - if (Class56.aClass11_886 != null || Class67.aClass11_1017 != null || Class38_Sub1.aBoolean2615 || inter.anInt189 != 1400 && Class3_Sub19.anInt2475 > 0) { - var18 = false; - var25 = false; - var26 = false; - } - - int var29; - if (inter.anInt189 != 0) { - if (inter.anInt189 == 1337) { - Unsorted.aClass11_2091 = inter; - Class20.method909(inter); - continue; - } - - if (inter.anInt189 == 1338) { - if (var18) { - Class1.anInt56 = Class163_Sub1.anInt2993 - var10; - Class58.anInt916 = Class38_Sub1.anInt2614 - var11; - } - continue; - } - - if (inter.anInt189 == 1400) { - Class3_Sub28_Sub3.aClass11_3551 = inter; - if (var18) { - if (ObjectDefinition.aBooleanArray1490[82] && Player.rights > 0) { - var19 = (int) ((double) (Class163_Sub1.anInt2993 - var10 - inter.width / 2) * 2.0D / (double) Class44.aFloat727); - var29 = (int) ((double) (Class38_Sub1.anInt2614 - var11 - inter.height / 2) * 2.0D / (double) Class44.aFloat727); - var21 = Class3_Sub28_Sub1.anInt3536 + var19; - int var32 = Class3_Sub4.anInt2251 + var29; - int var23 = var21 + TextureOperation37.anInt3256; - int var24 = Class108.anInt1460 - 1 - var32 + Unsorted.anInt65; - Class30.method979(var23, var24, 0); - TextureOperation4.method264((byte) 126); - continue; - } - - Class3_Sub19.anInt2475 = 1; - Unsorted.anInt1881 = Class126.anInt1676; - Class95.anInt1336 = Unsorted.anInt1709; - continue; - } - - if (var25 && Class3_Sub19.anInt2475 > 0) { - if (Class3_Sub19.anInt2475 == 1 && (Unsorted.anInt1881 != Class126.anInt1676 || Class95.anInt1336 != Unsorted.anInt1709)) { - Unsorted.anInt4073 = Class3_Sub28_Sub1.anInt3536; - Class38.anInt660 = Class3_Sub4.anInt2251; - Class3_Sub19.anInt2475 = 2; - } - - if (Class3_Sub19.anInt2475 == 2) { - method1175(Unsorted.anInt4073 + (int) ((double) (Unsorted.anInt1881 - Class126.anInt1676) * 2.0D / (double) NPC.aFloat3979)); - TextureOperation8.method354(Class38.anInt660 + (int) ((double) (Class95.anInt1336 - Unsorted.anInt1709) * 2.0D / (double) NPC.aFloat3979)); - } - continue; - } - - Class3_Sub19.anInt2475 = 0; - continue; - } - - if (inter.anInt189 == 1401) { - if (var25) { - TextureOperation23.method253(inter.width, Unsorted.anInt1709 - var11, Class126.anInt1676 - var10, inter.height); - } - continue; - } - - if (inter.anInt189 == 1402) { - if (!HDToolKit.highDetail) { - Class20.method909(inter); - } - continue; - } - } - - if (!inter.aBoolean188 && var18) { - inter.aBoolean188 = true; - if (inter.anObjectArray165 != null) { - var30 = new CS2Script(); - var30.aBoolean2446 = true; - var30.aClass11_2449 = inter; - var30.worldSelectCursorPositionX = Class163_Sub1.anInt2993 - var10; - var30.scrollbarScrollAmount = Class38_Sub1.anInt2614 - var11; - var30.arguments = inter.anObjectArray165; - aLinkedList_1471.method1215(var30); - } - } - - if (inter.aBoolean188 && var25 && inter.anObjectArray170 != null) { - var30 = new CS2Script(); - var30.aBoolean2446 = true; - var30.aClass11_2449 = inter; - var30.worldSelectCursorPositionX = Class126.anInt1676 - var10; - var30.scrollbarScrollAmount = Unsorted.anInt1709 - var11; - var30.arguments = inter.anObjectArray170; - aLinkedList_1471.method1215(var30); - } - - if (inter.aBoolean188 && !var25) { - inter.aBoolean188 = false; - if (inter.anObjectArray239 != null) { - var30 = new CS2Script(); - var30.aBoolean2446 = true; - var30.aClass11_2449 = inter; - var30.worldSelectCursorPositionX = Class126.anInt1676 - var10; - var30.scrollbarScrollAmount = Unsorted.anInt1709 - var11; - var30.arguments = inter.anObjectArray239; - Class65.aLinkedList_983.method1215(var30); - } - } - - if (var25 && inter.anObjectArray180 != null) { - var30 = new CS2Script(); - var30.aBoolean2446 = true; - var30.aClass11_2449 = inter; - var30.worldSelectCursorPositionX = Class126.anInt1676 - var10; - var30.scrollbarScrollAmount = Unsorted.anInt1709 - var11; - var30.arguments = inter.anObjectArray180; - aLinkedList_1471.method1215(var30); - } - - if (!inter.aBoolean163 && var26) { - inter.aBoolean163 = true; - if (inter.anObjectArray248 != null) { - var30 = new CS2Script(); - var30.aBoolean2446 = true; - var30.aClass11_2449 = inter; - var30.worldSelectCursorPositionX = Class126.anInt1676 - var10; - var30.scrollbarScrollAmount = Unsorted.anInt1709 - var11; - var30.arguments = inter.anObjectArray248; - aLinkedList_1471.method1215(var30); - } - } - - if (inter.aBoolean163 && var26 && inter.anObjectArray276 != null) { - var30 = new CS2Script(); - var30.aBoolean2446 = true; - var30.aClass11_2449 = inter; - var30.worldSelectCursorPositionX = Class126.anInt1676 - var10; - var30.scrollbarScrollAmount = Unsorted.anInt1709 - var11; - var30.arguments = inter.anObjectArray276; - aLinkedList_1471.method1215(var30); - } - - if (inter.aBoolean163 && !var26) { - inter.aBoolean163 = false; - if (inter.anObjectArray281 != null) { - var30 = new CS2Script(); - var30.aBoolean2446 = true; - var30.aClass11_2449 = inter; - var30.worldSelectCursorPositionX = Class126.anInt1676 - var10; - var30.scrollbarScrollAmount = Unsorted.anInt1709 - var11; - var30.arguments = inter.anObjectArray281; - Class65.aLinkedList_983.method1215(var30); - } - } - - if (inter.anObjectArray269 != null) { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray269; - PacketParser.aLinkedList_82.method1215(var30); - } - - CS2Script var22; - if (inter.anObjectArray161 != null && PacketParser.anInt87 > inter.anInt284) { - if (inter.anIntArray211 != null && PacketParser.anInt87 - inter.anInt284 <= 32) { - label531: - for (var19 = inter.anInt284; var19 < PacketParser.anInt87; ++var19) { - var29 = NPC.anIntArray3986[var19 & 31]; - - for (var21 = 0; var21 < inter.anIntArray211.length; ++var21) { - if (inter.anIntArray211[var21] == var29) { - var22 = new CS2Script(); - var22.aClass11_2449 = inter; - var22.arguments = inter.anObjectArray161; - aLinkedList_1471.method1215(var22); - break label531; - } - } - } - } else { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray161; - aLinkedList_1471.method1215(var30); - } - - inter.anInt284 = PacketParser.anInt87; - } - - if (inter.anObjectArray221 != null && anInt2317 > inter.anInt242) { - if (inter.anIntArray185 != null && anInt2317 - inter.anInt242 <= 32) { - label512: - for (var19 = inter.anInt242; var19 < anInt2317; ++var19) { - var29 = Class163_Sub2_Sub1.anIntArray4025[var19 & 31]; - - for (var21 = 0; var21 < inter.anIntArray185.length; ++var21) { - if (inter.anIntArray185[var21] == var29) { - var22 = new CS2Script(); - var22.aClass11_2449 = inter; - var22.arguments = inter.anObjectArray221; - aLinkedList_1471.method1215(var22); - break label512; - } - } - } - } else { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray221; - aLinkedList_1471.method1215(var30); - } - - inter.anInt242 = anInt2317; - } - - if (inter.anObjectArray282 != null && Class36.anInt641 > inter.anInt213) { - if (inter.anIntArray286 != null && Class36.anInt641 - inter.anInt213 <= 32) { - label493: - for (var19 = inter.anInt213; var19 < Class36.anInt641; ++var19) { - var29 = Class44.anIntArray726[var19 & 31]; - - for (var21 = 0; var21 < inter.anIntArray286.length; ++var21) { - if (inter.anIntArray286[var21] == var29) { - var22 = new CS2Script(); - var22.aClass11_2449 = inter; - var22.arguments = inter.anObjectArray282; - aLinkedList_1471.method1215(var22); - break label493; - } - } - } - } else { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray282; - aLinkedList_1471.method1215(var30); - } - - inter.anInt213 = Class36.anInt641; - } - - if (inter.anObjectArray174 != null && Unsorted.anInt944 > inter.anInt255) { - if (inter.anIntArray175 != null && Unsorted.anInt944 - inter.anInt255 <= 32) { - label474: - for (var19 = inter.anInt255; var19 < Unsorted.anInt944; ++var19) { - var29 = QuickChatDefinition.anIntArray3565[var19 & 31]; - - for (var21 = 0; var21 < inter.anIntArray175.length; ++var21) { - if (inter.anIntArray175[var21] == var29) { - var22 = new CS2Script(); - var22.aClass11_2449 = inter; - var22.arguments = inter.anObjectArray174; - aLinkedList_1471.method1215(var22); - break label474; - } - } - } - } else { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray174; - aLinkedList_1471.method1215(var30); - } - - inter.anInt255 = Unsorted.anInt944; - } - - if (inter.anObjectArray158 != null && Class49.anInt815 > inter.anInt311) { - if (inter.anIntArray274 != null && Class49.anInt815 - inter.anInt311 <= 32) { - label455: - for (var19 = inter.anInt311; var19 < Class49.anInt815; ++var19) { - var29 = anIntArray3780[var19 & 31]; - - for (var21 = 0; var21 < inter.anIntArray274.length; ++var21) { - if (inter.anIntArray274[var21] == var29) { - var22 = new CS2Script(); - var22.aClass11_2449 = inter; - var22.arguments = inter.anObjectArray158; - aLinkedList_1471.method1215(var22); - break label455; - } - } - } - } else { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray158; - aLinkedList_1471.method1215(var30); - } - - inter.anInt311 = Class49.anInt815; - } - - if (Class24.anInt472 > inter.anInt234 && inter.anObjectArray256 != null) { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray256; - aLinkedList_1471.method1215(var30); - } - - if (Class110.anInt1472 > inter.anInt234 && inter.anObjectArray156 != null) { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray156; - aLinkedList_1471.method1215(var30); - } - - if (Class167.anInt2087 > inter.anInt234 && inter.anObjectArray313 != null) { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray313; - aLinkedList_1471.method1215(var30); - } - - if (Class121.anInt1642 > inter.anInt234 && inter.anObjectArray268 != null) { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray268; - aLinkedList_1471.method1215(var30); - } - - if (Class140_Sub6.anInt2905 > inter.anInt234 && inter.anObjectArray315 != null) { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray315; - aLinkedList_1471.method1215(var30); - } - - inter.anInt234 = PacketParser.anInt3213; - if (inter.anObjectArray220 != null) { - for (var19 = 0; var19 < Class3_Sub23.anInt2537; ++var19) { - CS2Script var31 = new CS2Script(); - var31.aClass11_2449 = inter; - var31.inputTextCode = Class133.inputTextCodeArray[var19]; - var31.anInt2443 = Class120.anIntArray1638[var19]; - var31.arguments = inter.anObjectArray220; - aLinkedList_1471.method1215(var31); - } - } - - if (Class3_Sub28_Sub1.aBoolean3531 && inter.anObjectArray217 != null) { - var30 = new CS2Script(); - var30.aClass11_2449 = inter; - var30.arguments = inter.anObjectArray217; - aLinkedList_1471.method1215(var30); - } - } - } - - if (!inter.usingScripts && Class56.aClass11_886 == null && Class67.aClass11_1017 == null && !Class38_Sub1.aBoolean2615) { - if ((inter.anInt212 >= 0 || inter.anInt228 != 0) && Class126.anInt1676 >= var12 && Unsorted.anInt1709 >= var13 && Class126.anInt1676 < var14 && Unsorted.anInt1709 < var15) { - if (inter.anInt212 >= 0) { - Class107.aClass11_1453 = interfaces[inter.anInt212]; - } else { - Class107.aClass11_1453 = inter; - } - } - - if (inter.type == 8 && Class126.anInt1676 >= var12 && Unsorted.anInt1709 >= var13 && Class126.anInt1676 < var14 && Unsorted.anInt1709 < var15) { - Class20.aClass11_439 = inter; - } - - if (inter.anInt252 > inter.height) { - Class137.method1819(Unsorted.anInt1709, inter.height, inter, Class126.anInt1676, var10 + inter.width, var11, inter.anInt252); - } - } - - if (inter.type == 0) { - handleItemSwitch(interfaces, inter.componentHash, var12, var13, var14, var15, var10 - inter.anInt247, var11 - inter.anInt208); - if (inter.aClass11Array262 != null) { - handleItemSwitch(inter.aClass11Array262, inter.componentHash, var12, var13, var14, var15, var10 - inter.anInt247, var11 - inter.anInt208); - } - - Class3_Sub31 var28 = TextureOperation23.aHashTable_3208.get(inter.componentHash); - if (var28 != null) { - GraphicDefinition.method967(var10, var13, var11, var14, var28.anInt2602, var12, var15); - } - } - } - } - } - - } - - static boolean method51(RSInterface var0) { - if (ClientCommands.commandQaOpEnabled) { - if (method44(var0).anInt2205 != 0) { - return false; - } - - if (var0.type == 0) { - return false; - } - } - - return var0.hidden; - } - - final void method38() { - try { - if (Class143.gameStage != 1000) { - boolean var2 = NPC.method1988(); - if (var2 && AudioHandler.musicEffectPlaying && WorldListEntry.aAudioChannel_2627 != null) { - WorldListEntry.aAudioChannel_2627.method2158(); - } - - if ((Class143.gameStage == 30 || Class143.gameStage == 10) && (Class3_Sub28_Sub5.forceReplaceCanvasEnable || Class53.aLong866 != 0 && Class53.aLong866 < TimeUtils.time())) { - GameObject.graphicsSettings(Class3_Sub28_Sub5.forceReplaceCanvasEnable, Class83.getWindowType(), TextureOperation.anInt2378, Unsorted.anInt3071); - } - - int var4; - int var5; - if (null == TextureOperation30.fullScreenFrame) { - Object var3; - if (GameShell.frame == null) { - var3 = Class38.gameSignlink.gameApplet; - } else { - var3 = GameShell.frame; - } - - var4 = ((Container) var3).getSize().width; - var5 = ((Container) var3).getSize().height; - if (var3 == GameShell.frame) { - Insets var6 = GameShell.frame.getInsets(); - var4 -= var6.right + var6.left; - var5 -= var6.top + var6.bottom; - } - - if (var4 != Unsorted.frameWidth || Class70.frameHeight != var5) { - if (Signlink.osName.startsWith("mac")) { - Unsorted.frameWidth = var4; - Class70.frameHeight = var5; - } else { - Class119.method1729(); - } - - Class53.aLong866 = TimeUtils.time() - -500L; - } - } - - if (TextureOperation30.fullScreenFrame != null && !TextureOperation26.aBoolean3078 && (30 == Class143.gameStage || 10 == Class143.gameStage)) { - GameObject.graphicsSettings(false, Unsorted.anInt2577, -1, -1); - } - - boolean var10 = false; - if (TextureOperation30.fullRedraw) { - var10 = true; - TextureOperation30.fullRedraw = false; - } - - if (var10) { - Unsorted.method1396(40 ^ -41); - } - - if (HDToolKit.highDetail) { - for (var4 = 0; var4 < 100; ++var4) { - Unsorted.aBooleanArray3674[var4] = true; - } - } - if (Class143.gameStage == 0) { - if (Discord.checkInitializable()) { - Discord.initialize(); - Discord.updatePresence("At the login screen", "", ""); - } - StartupLoadingBarInitial.draw(ColorCore.loadingbarcolor, var10, loadingBarTextToDisplay, LoadingStageNumber); - } else if (5 == Class143.gameStage) { - StartupLoadingBar.draw(false, FontType.bold); - } else if (Class143.gameStage == 10) { - Class3_Sub17.method381(); - } else if (25 != Class143.gameStage && Class143.gameStage != 28) { - if (Class143.gameStage == 30) { - Class49.method1127(0); - } else if (40 == Class143.gameStage) { - LoadingBox.draw(false, RSString.stringCombiner(new RSString[]{TextCore.ConxLost, TextCore.aClass94_2598, TextCore.AttemptingReestablish})); - } - } else if (Class163_Sub2_Sub1.anInt4019 == 1) { - if (Class40.anInt3293 > LinkableRSString.anInt2579) { - LinkableRSString.anInt2579 = Class40.anInt3293; - } - - var4 = 50 * (LinkableRSString.anInt2579 + -Class40.anInt3293) / LinkableRSString.anInt2579; - LoadingBox.draw(false, RSString.stringCombiner(new RSString[]{TextCore.LoadingPleaseWait2, TextCore.aClass94_3399, RSString.stringAnimator(var4), RSString.parse("(U(Y")})); - } else if (Class163_Sub2_Sub1.anInt4019 == 2) { - if (anInt2275 < Class162.anInt2038) { - anInt2275 = Class162.anInt2038; - } - - var4 = (-Class162.anInt2038 + anInt2275) * 50 / anInt2275 + 50; - LoadingBox.draw(false, RSString.stringCombiner(new RSString[]{TextCore.LoadingPleaseWait2, TextCore.aClass94_3399, RSString.stringAnimator(var4), RSString.parse("(U(Y")})); - } else { - LoadingBox.draw(false, TextCore.LoadingPleaseWait2); - } - - DeveloperConsole.INSTANCE.preDraw(); - DeveloperConsole.INSTANCE.draw(); - - if (HDToolKit.highDetail && Class143.gameStage != 0) { - HDToolKit.bufferSwap(); - - for (var4 = 0; Class3_Sub28_Sub3.anInt3557 > var4; ++var4) { - Class163_Sub1_Sub1.aBooleanArray4008[var4] = false; - } - } else { - Graphics var11; - if ((Class143.gameStage == 30 || 10 == Class143.gameStage) && rectDebugInt == 0 && !var10) { - try { - var11 = GameShell.canvas.getGraphics(); - - for (var5 = 0; Class3_Sub28_Sub3.anInt3557 > var5; ++var5) { - if (Class163_Sub1_Sub1.aBooleanArray4008[var5]) { - Unsorted.aClass158_3009.drawGraphics(Class3_Sub28_Sub18.anIntArray3768[var5], AudioChannel.anIntArray1969[var5], Class140_Sub4.anIntArray2794[var5], var11, Player.anIntArray3954[var5]); - Class163_Sub1_Sub1.aBooleanArray4008[var5] = false; - } - } - } catch (Exception var8) { - GameShell.canvas.repaint(); - } - } else if (0 != Class143.gameStage) { - try { - var11 = GameShell.canvas.getGraphics(); - Unsorted.aClass158_3009.method2179(var11); - - for (var5 = 0; var5 < Class3_Sub28_Sub3.anInt3557; ++var5) { - Class163_Sub1_Sub1.aBooleanArray4008[var5] = false; - } - } catch (Exception var7) { - var7.printStackTrace(); - GameShell.canvas.repaint(); - } - } - } - - if (sweepReferenceCache) { - Class75_Sub3.sweepClientStartupReferenceCache(); - } - - if (Unsorted.aBoolean2146 && 10 == Class143.gameStage && ConfigInventoryDefinition.anInt3655 != -1) { - Unsorted.aBoolean2146 = false; - Class119.method1730(Class38.gameSignlink); - } - - DeveloperConsole.INSTANCE.postDraw(); - if (DeveloperConsole.INSTANCE.getOpen()) { - DeveloperConsole.INSTANCE.draw(); -// System.out.println("Draw developer console"); - } - DiscordRPC.discordRunCallbacks(); - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "client.K(" + 40 + ')'); - } - } - - final void method32() { - try { - if (HDToolKit.highDetail) { - HDToolKit.method1842(); - } - - if (null != TextureOperation30.fullScreenFrame) { - Unsorted.method593(TextureOperation30.fullScreenFrame, Class38.gameSignlink); - TextureOperation30.fullScreenFrame = null; - } - - if (null != Class38.gameSignlink) { - Class38.gameSignlink.method1442(this.getClass(), 0); - } - - if (null != TextureOperation20.aClass67_1443) { - TextureOperation20.aClass67_1443.aBoolean1015 = false; - } - - TextureOperation20.aClass67_1443 = null; - if (Class3_Sub15.activeConnection != null) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - Class163_Sub1_Sub1.method2215(GameShell.canvas); - Unsorted.method1783(GameShell.canvas); - if (null != Class38.aClass146_668) { - Class38.aClass146_668.method2082(GameShell.canvas); - } - - TextureOperation12.method167(0); - MouseListeningClass.method2090(); - Class38.aClass146_668 = null; - if (null != WorldListEntry.aAudioChannel_2627) { - WorldListEntry.aAudioChannel_2627.method2163(); - } - - if (null != Class3_Sub21.aAudioChannel_2491) { - Class3_Sub21.aAudioChannel_2491.method2163(); - } - - Class58.aJs5Worker_917.close(); - TextureOperation31.aCacheResourceWorker_3159.stop(); - - try { - if (Class101.aClass30_1422 != null) { - Class101.aClass30_1422.method980(); - } - - if (Class163_Sub2.aClass30Array2998 != null) { - for (int var2 = 0; var2 < Class163_Sub2.aClass30Array2998.length; ++var2) { - if (null != Class163_Sub2.aClass30Array2998[var2]) { - Class163_Sub2.aClass30Array2998[var2].method980(); - } - } - } - - if (null != aClass30_1572) { - aClass30_1572.method980(); - } - - if (null != Unsorted.aClass30_1039) { - Unsorted.aClass30_1039.method980(); - } - } catch (IOException var3) { - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "client.F(" + (byte) 23 + ')'); - } - } - - public final void init() { - try { - GameConfig.WORLD = GameConfig.WORLD_OVERRIDE == -1 ? ObjectDefinition.paramWorldID : GameConfig.WORLD_OVERRIDE; - ObjectDefinition.paramWorldID = GameConfig.WORLD_OVERRIDE == -1 ? 1 : GameConfig.WORLD_OVERRIDE; - - Class44.paramModeWhere = Integer.parseInt(Objects.requireNonNull(this.getParameter("modewhere"))); - if (Class44.paramModeWhere < LIVE_ENVIRONMENT || Class44.paramModeWhere > LOCAL_ENVIRONMENT) { - Class44.paramModeWhere = 0; - } - - TextureOperation20.paramModeWhat = Integer.parseInt(Objects.requireNonNull(this.getParameter("modewhat"))); - if (TextureOperation20.paramModeWhat < 0 || TextureOperation20.paramModeWhat > 2) { - TextureOperation20.paramModeWhat = 0; - } - - String var1 = this.getParameter("advertsuppressed"); - paramAdvertisementSuppressed = var1 != null && var1.equals("1"); - - try { - Class3_Sub20.paramLanguage = Integer.parseInt(Objects.requireNonNull(this.getParameter("lang"))); - } catch (Exception var10) { - Class3_Sub20.paramLanguage = 0; - } - Unsorted.languageSetter(Class3_Sub20.paramLanguage); - - String var2 = this.getParameter("objecttag"); - Class163_Sub2_Sub1.paramObjectTagEnabled = var2 != null && var2.equals("1"); - - String var3 = this.getParameter("js"); - Unsorted.paramJavaScriptEnabled = null != var3 && var3.equals("1"); - - String var4 = this.getParameter("game"); - if (var4 != null && var4.equals("1")) { - Class158.paramGameTypeID = 1; - } else { - Class158.paramGameTypeID = 0; - } - - try { - Class3_Sub26.paramAffid = Integer.parseInt(Objects.requireNonNull(this.getParameter("affid"))); - } catch (Exception var9) { - Class3_Sub26.paramAffid = 0; - } - - Class163_Sub2.paramSettings = TextCore.aClass94_1745.getParamValue(this); - if (Class163_Sub2.paramSettings == null) { - Class163_Sub2.paramSettings = RSString.parse(""); - } - - String var5 = this.getParameter("country"); - if (var5 != null) { - try { - Class3_Sub31.paramCountryID = Integer.parseInt(var5); - } catch (Exception var8) { - Class3_Sub31.paramCountryID = 0; - } - } - - String var6 = this.getParameter("haveie6"); - Class106.paramUserUsingInternetExplorer = null != var6 && var6.equals("1"); - - clientInstance = this; - this.method41(32 - -TextureOperation20.paramModeWhat); - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "client.init()"); - } - } - - final void method39() { - try { - Class119.method1729(); - TextureOperation31.aCacheResourceWorker_3159 = new CacheResourceWorker(); - Class58.aJs5Worker_917 = new Js5Worker(); - - if (TextureOperation20.paramModeWhat != LIVE_ENVIRONMENT) { - Class3_Sub6.softReferenceTestArray = new byte[50][]; - } - - CS2Script.userCurrentWorldID = ObjectDefinition.paramWorldID; - - Unsorted.parsePreferences(Class38.gameSignlink); - SystemLogger.logInfo("port: " + Class53.anInt867); - SystemLogger.logInfo("MSIP: " + GameConfig.IP_MANAGEMENT); - - if (Class44.paramModeWhere == LIVE_ENVIRONMENT) { - ClientErrorException.worldListHost = Objects.requireNonNull(this.getCodeBase()).getHost(); - Class53.anInt867 = GameConfig.SERVER_PORT + ObjectDefinition.paramWorldID; //443 is secure port - anInt3773 = '\uaa4a'; - - } else if (Class44.paramModeWhere == OFFICE_ENVIRONMENT) { - ClientErrorException.worldListHost = Objects.requireNonNull(this.getCodeBase()).getHost(); - //System.out.println("port = " + Class53.anInt867); - Class53.anInt867 = ObjectDefinition.paramWorldID + 50000; - anInt3773 = 40000 + ObjectDefinition.paramWorldID; - - } else if (Class44.paramModeWhere == LOCAL_ENVIRONMENT) { - ClientErrorException.worldListHost = "127.0.0.1"; - System.out.println("Setting worldListHost to 127.0.0.1, port = " + Class53.anInt867); - Class53.anInt867 = ObjectDefinition.paramWorldID + 50000; - anInt3773 = ObjectDefinition.paramWorldID + 40000; - } - - switch (Class158.paramGameTypeID) { - case GAME_TYPE_RUNESCAPE: - AudioThread.aShortArrayArray344 = RunescapeGameAppearance.INSTANCE.getAShortArrayArray3654(); - Class91.aShortArray1311 = RunescapeGameAppearance.INSTANCE.getAShortArray3349(); - Class101.aShortArrayArray1429 = RunescapeGameAppearance.INSTANCE.getAShortArrayArray435(); - Class3_Sub25.aShortArray2548 = RunescapeGameAppearance.INSTANCE.getAShortArray3011(); - break; - - case GAME_TYPE_MECHSCAPE: - ClientCommands.shiftClickEnabled = true; - Class92.defaultScreenColorRgb = 16777215; - Class92.defaultRegionAmbientRGB = 0; - AudioThread.aShortArrayArray344 = MechscapeGameAppearance.INSTANCE.getAShortArrayArray1619(); - Class101.aShortArrayArray1429 = MechscapeGameAppearance.INSTANCE.getAShortArrayArray2634(); - Class3_Sub25.aShortArray2548 = MechscapeGameAppearance.INSTANCE.getAShortArray63(); - Class91.aShortArray1311 = MechscapeGameAppearance.INSTANCE.getAShortArray2219(); - break; - - } - - currentPort = Class53.anInt867; - Class162.anInt2036 = anInt3773; - Class38_Sub1.accRegistryIp = GameConfig.IP_MANAGEMENT; - Class123.anInt1658 = anInt3773; - TextureOperation38.aShortArray3455 = TextureOperation16.aShortArray3110 = Class136.aShortArray1779 = TextureOperation38.aShortArray3453 = new short[256]; - - Class140_Sub6.accRegistryPort = Class123.anInt1658; - if (Signlink.anInt1214 == 3 && 2 != Class44.paramModeWhere) { - CS2Script.userCurrentWorldID = ObjectDefinition.paramWorldID; - } - - KeyboardListener.adjustKeyCodeMap(); - TextureOperation34.method193((byte) 115, GameShell.canvas); - ItemDefinition.method1119(GameShell.canvas, false); - Class38.aClass146_668 = Class21.method916(); - if (null != Class38.aClass146_668) { - Class38.aClass146_668.method2084(GameShell.canvas, -97); - } - - try { - if (Class38.gameSignlink.cacheDataFile != null) { - Class101.aClass30_1422 = new Class30(Class38.gameSignlink.cacheDataFile, 5200); - - for (int var2 = 0; var2 < 29; ++var2) { - Class163_Sub2.aClass30Array2998[var2] = new Class30(Class38.gameSignlink.cacheIndicesFiles[var2], 6000); - } - - aClass30_1572 = new Class30(Class38.gameSignlink.cacheChecksumFile, 6000); - AtmosphereParser.aClass41_1186 = new Class41(255, Class101.aClass30_1422, aClass30_1572, 500000); - Unsorted.aClass30_1039 = new Class30(Class38.gameSignlink.randomDatFile, 24); - Class38.gameSignlink.cacheIndicesFiles = null; - Class38.gameSignlink.cacheChecksumFile = null; - Class38.gameSignlink.randomDatFile = null; - Class38.gameSignlink.cacheDataFile = null; - } - } catch (IOException var3) { - Unsorted.aClass30_1039 = null; - Class101.aClass30_1422 = null; - aClass30_1572 = null; - AtmosphereParser.aClass41_1186 = null; - } - - Class167.aClass94_2083 = TextCore.RSLoadingPleaseWait; - if (Class44.paramModeWhere != 0) { - ClientCommands.fpsOverlayEnabled = true; - } - //Class3_Sub26.tweeningEnabled = true; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "client.B(" + 2 + ')'); - } - } - - final void method33() { - // TODO Do we still need this method here? - } - - private void method46(int var2) { - try { - ++Class58.aJs5Worker_917.errors; - Class17.aClass64_413 = null; - - Class58.aJs5Worker_917.status = var2; - Unsorted.js5Connection = null; - PacketParser.anInt80 = 0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "client.P(" + true + ',' + var2 + ')'); - } - } - - private void method47() { - try { - for (Class3_Sub23.anInt2537 = 0; Unsorted.method591(83) && Class3_Sub23.anInt2537 < 128; ++Class3_Sub23.anInt2537) { - Class133.inputTextCodeArray[Class3_Sub23.anInt2537] = Class3_Sub28_Sub9.anInt3624; - Class120.anIntArray1638[Class3_Sub23.anInt2537] = TextureOperation7.anInt3342; - } - - ++Class106.anInt1446; - if (-1 != ConfigInventoryDefinition.anInt3655) { - GraphicDefinition.method967(0, 0, 0, Class23.canvasWidth, ConfigInventoryDefinition.anInt3655, 0, Class140_Sub7.canvasHeight); - } - - ++PacketParser.anInt3213; - if (HDToolKit.highDetail) { - int var2 = 19137023; - - label191: - for (int var3 = 0; var3 < 32768; ++var3) { - NPC var4 = NPC.npcs[var3]; - if (null != var4) { - byte var5 = var4.definition.aByte1267; - if ((var5 & 2) > 0 && var4.anInt2816 == 0 && 10.0D > Math.random() * 1000.0D) { - int var6 = (int) Math.round(-1.0D + 2.0D * Math.random()); - int var7 = (int) Math.round(Math.random() * 2.0D - 1.0D); - if (var6 != 0 || 0 != var7) { - var4.aByteArray2795[0] = 1; - var4.anIntArray2767[0] = var6 + (var4.xAxis >> 7); - var4.anIntArray2755[0] = var7 + (var4.zAxis >> 7); - AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1502(var4.xAxis >> 7, var4.getSize(), false, 0, var4.getSize(), var4.zAxis >> 7); - if (0 <= var4.anIntArray2767[0] && var4.anIntArray2767[0] <= 104 + -var4.getSize() && 0 <= var4.anIntArray2755[0] && var4.anIntArray2755[0] <= 104 - var4.getSize() && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1500(var4.zAxis >> 7, var4.anIntArray2755[0], var4.anIntArray2767[0], var4.xAxis >> 7)) { - if (var4.getSize() > 1) { - for (int var8 = var4.anIntArray2767[0]; var8 < var4.anIntArray2767[0] - -var4.getSize(); ++var8) { - for (int var9 = var4.anIntArray2755[0]; var4.anIntArray2755[0] + var4.getSize() > var9; ++var9) { - if ((var2 & AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].anIntArrayArray1304[var8][var9]) != 0) { - continue label191; - } - } - } - } - - var4.anInt2816 = 1; - } - } - } - - Unsorted.method1180((byte) -122, var4); - Class17.method904(65536, var4); - RenderAnimationDefinition.method900(var4); - AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1489(var4.xAxis >> 7, false, (byte) 85, var4.zAxis >> 7, var4.getSize(), var4.getSize()); - } - } - } - - if (!HDToolKit.highDetail) { - Unsorted.method744(); - } else if (0 == LoginHandler.loginStage && 0 == Unsorted.registryStage) { - if (Class133.anInt1753 == 2) { - CS2Script.method379(); - } else { - InterfaceWidget.d(65535); - } - - if (14 > NPC.anInt3995 >> 7 || NPC.anInt3995 >> 7 >= 90 || 14 > Class77.anInt1111 >> 7 || Class77.anInt1111 >> 7 >= 90) { - TextureOperation26.method195(); - } - } - - while (true) { - CS2Script var11 = (CS2Script) PacketParser.aLinkedList_82.method1220(); - RSInterface var12; - RSInterface var13; - if (var11 == null) { - while (true) { - var11 = (CS2Script) Class65.aLinkedList_983.method1220(); - if (null == var11) { - while (true) { - var11 = (CS2Script) aLinkedList_1471.method1220(); - if (null == var11) { - if (Class56.aClass11_886 != null) { - PacketParser.method829(); - } - - if (null != AudioThread.aClass64_351 && AudioThread.aClass64_351.anInt978 == 1) { - if (null != AudioThread.aClass64_351.anObject974) { - Class99.method1596(TextureOperation5.aClass94_3295, (byte) 126, Unsorted.aBoolean2154); - } - - Unsorted.aBoolean2154 = false; - TextureOperation5.aClass94_3295 = null; - AudioThread.aClass64_351 = null; - } - - if (Class44.anInt719 % 1500 == 0) { - Class72.method1293(); - } - - return; - } - - var12 = var11.aClass11_2449; - if (0 <= var12.anInt191) { - var13 = Unsorted.getRSInterface(var12.parentId); - if (var13 == null || null == var13.aClass11Array262 || var12.anInt191 >= var13.aClass11Array262.length || var12 != var13.aClass11Array262[var12.anInt191]) { - continue; - } - } - - Class43.method1065(var11); - } - } - - var12 = var11.aClass11_2449; - if (var12.anInt191 >= 0) { - var13 = Unsorted.getRSInterface(var12.parentId); - if (null == var13 || var13.aClass11Array262 == null || var13.aClass11Array262.length <= var12.anInt191 || var12 != var13.aClass11Array262[var12.anInt191]) { - continue; - } - } - - Class43.method1065(var11); - } - } - - var12 = var11.aClass11_2449; - if (var12.anInt191 >= 0) { - var13 = Unsorted.getRSInterface(var12.parentId); - if (null == var13 || null == var13.aClass11Array262 || var12.anInt191 >= var13.aClass11Array262.length || var12 != var13.aClass11Array262[var12.anInt191]) { - continue; - } - } - - Class43.method1065(var11); - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "client." + (byte) 1 + ')'); - } - } - - private void method48() { - try { - boolean var2 = Class58.aJs5Worker_917.process(); - if (!var2) { - this.method49(); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "client.J(" + true + ')'); - } - } - - private void method49() { - if (Class58.aJs5Worker_917.errors > Class163_Sub2_Sub1.anInt4026) { - anInt3068 = 5 * 50 * (Class58.aJs5Worker_917.errors + -1); - - //Port swap - if (Class162.anInt2036 == Class140_Sub6.accRegistryPort) { - Class140_Sub6.accRegistryPort = currentPort; - } else { - Class140_Sub6.accRegistryPort = Class162.anInt2036; - } - - if (anInt3068 > 3000) { - anInt3068 = 3000; - } - - if (Class58.aJs5Worker_917.errors >= 2 && Class58.aJs5Worker_917.status == 6) { - this.errorPrint("js5connect_outofdate"); - Class143.gameStage = 1000; - return; - } - - if (Class58.aJs5Worker_917.errors >= 4 && Class58.aJs5Worker_917.status == -1) { - this.errorPrint("js5crc"); - Class143.gameStage = 1000; - return; - } - - if (Class58.aJs5Worker_917.errors >= 4 && (Class143.gameStage == 0 || Class143.gameStage == 5)) { - if (Class58.aJs5Worker_917.status == 7 || Class58.aJs5Worker_917.status == 9) { - this.errorPrint("js5connect_full"); - } else if (Class58.aJs5Worker_917.status > 0) { - this.errorPrint("js5connect"); - } else { - this.errorPrint("js5io"); - } - - Class143.gameStage = 1000; - return; - } - } - - Class163_Sub2_Sub1.anInt4026 = Class58.aJs5Worker_917.errors; - if (anInt3068 > 0) { - --anInt3068; - } else { - try { - if (PacketParser.anInt80 == 0) { - System.out.println("Trying " + GameConfig.JS5_SERVER_PORT); - Class17.aClass64_413 = Class38.gameSignlink.method1441((byte) 8, Class38_Sub1.accRegistryIp, GameConfig.JS5_SERVER_PORT); - ++PacketParser.anInt80; - } - - if (PacketParser.anInt80 == 1) { - - /* If the connection is null we reset the JS5 port to the backup server JS5 for compatibility reasons */ - if (2 == Objects.requireNonNull(Class17.aClass64_413).anInt978) { - GameConfig.JS5_SERVER_PORT = GameConfig.SERVER_PORT + ObjectDefinition.paramWorldID; - this.method46(1000); - return; - } - - if (Class17.aClass64_413.anInt978 == 1) { - ++PacketParser.anInt80; - } - } - - if (2 == PacketParser.anInt80) { - Unsorted.js5Connection = new Connection((Socket) Objects.requireNonNull(Class17.aClass64_413).anObject974, Class38.gameSignlink); - DataBuffer var2 = new DataBuffer(9); - var2.writeByte(15); //JS5 handshake - var2.writeInt(GameConfig.CLIENT_BUILD); - var2.writeInt(GameConfig.CLIENT_VERSION); - Unsorted.js5Connection.sendBytes(var2.buffer, 9); - ++PacketParser.anInt80; - TextureOperation13.aLong3366 = TimeUtils.time(); - } - - if (3 == PacketParser.anInt80) { - if (Class143.gameStage != 0 && Class143.gameStage != 5 && 0 >= Unsorted.js5Connection.availableBytes()) { - if (TimeUtils.time() + -TextureOperation13.aLong3366 > 30000) { - this.method46(1001); - return; - } - } else { - int var5 = Unsorted.js5Connection.readByte(); - if (var5 != 0) { - this.method46(var5); - return; - } - - ++PacketParser.anInt80; - } - } - - if (PacketParser.anInt80 == 4) { - boolean var6 = Class143.gameStage == 5 || Class143.gameStage == 10 || Class143.gameStage == 28; - Class58.aJs5Worker_917.connect(!var6, Unsorted.js5Connection); - Unsorted.js5Connection = null; - Class17.aClass64_413 = null; - PacketParser.anInt80 = 0; - } - } catch (IOException var3) { - this.method46(1002); - } - - } - } - - private void method52(int var1) { - try { - if (!Unsorted.aBoolean2146) { - while (Unsorted.method591(107)) { - if (TextureOperation7.anInt3342 == 115 || TextureOperation7.anInt3342 == 83) { - Unsorted.aBoolean2146 = true; - } - } - } - - if (var1 >= 46) { - int var3; - if (anInt1354 == 0) { - Runtime var10 = Runtime.getRuntime(); - var3 = (int) ((var10.totalMemory() - var10.freeMemory()) / 1024L); - long var4 = TimeUtils.time(); - if (aLong3296 == 0) { - aLong3296 = var4; - } - - if (var3 > 16384 && 5000L > -aLong3296 + var4) { - if (-aLong1310 + var4 > 1000L) { - System.gc(); - aLong1310 = var4; - } - - LoadingStageNumber = 5; - loadingBarTextToDisplay = TextCore.AllocatingMemory; - } else { - loadingBarTextToDisplay = TextCore.AllocatedMemory; - anInt1354 = 10; - LoadingStageNumber = 5; - } - } else { - int var2; - if (anInt1354 == 10) { - Class68.method1267(); - - for (var2 = 0; var2 < 4; ++var2) { - AtmosphereParser.aClass91Array1182[var2] = new Class91(); - } - - LoadingStageNumber = 10; - anInt1354 = 30; - loadingBarTextToDisplay = TextCore.CreatedWorld; - } else if (anInt1354 == 30) { - if (Unsorted.aClass8_1936 == null) { - Unsorted.aClass8_1936 = new Class8(Class58.aJs5Worker_917, TextureOperation31.aCacheResourceWorker_3159); - } - - if (Unsorted.aClass8_1936.method837()) { - CacheIndex.skeletonsIndex = Class8.getCacheIndex(false, true, true, 0); - CacheIndex.skinsIndex = Class8.getCacheIndex(false, true, true, 1); - CacheIndex.configurationsIndex = Class8.getCacheIndex(true, true, false, 2); - CacheIndex.interfacesIndex = Class8.getCacheIndex(false, true, true, 3); - CacheIndex.soundFXIndex = Class8.getCacheIndex(false, true, true, 4); - CacheIndex.landscapesIndex = Class8.getCacheIndex(true, true, true, 5); - CacheIndex.musicIndex = Class8.getCacheIndex(true, false, true, 6); - CacheIndex.modelsIndex = Class8.getCacheIndex(false, true, true, 7); - CacheIndex.spritesIndex = Class8.getCacheIndex(false, true, true, 8); - CacheIndex.texturesIndex = Class8.getCacheIndex(false, true, true, 9); - CacheIndex.huffmanEncodingIndex = Class8.getCacheIndex(false, true, true, 10); - CacheIndex.music2Index = Class8.getCacheIndex(false, true, true, 11); - CacheIndex.interfaceScriptsIndex = Class8.getCacheIndex(false, true, true, 12); - CacheIndex.fontsIndex = Class8.getCacheIndex(false, true, true, 13); - CacheIndex.soundFX2Index = Class8.getCacheIndex(false, false, true, 14); - CacheIndex.soundFX3Index = Class8.getCacheIndex(false, true, true, 15); - CacheIndex.objectConfigIndex = Class8.getCacheIndex(false, true, true, 16); - CacheIndex.clientscriptMaskIndex = Class8.getCacheIndex(false, true, true, 17); - CacheIndex.npcConfigIndex = Class8.getCacheIndex(false, true, true, 18); - CacheIndex.itemConfigIndex = Class8.getCacheIndex(false, true, true, 19); - CacheIndex.animationIndex = Class8.getCacheIndex(false, true, true, 20); - CacheIndex.graphicFXIndex = Class8.getCacheIndex(false, true, true, 21); - CacheIndex.clientScriptConfigIndex = Class8.getCacheIndex(false, true, true, 22); - CacheIndex.worldmapIndex = Class8.getCacheIndex(true, true, true, 23); - CacheIndex.quickchatMessagesIndex = Class8.getCacheIndex(false, true, true, 24); - CacheIndex.quickchatMenusIndex = Class8.getCacheIndex(false, true, true, 25); - CacheIndex.materialsIndex = Class8.getCacheIndex(true, true, true, 26); - CacheIndex.particlesConfigIndex = Class8.getCacheIndex(false, true, true, 27); - CacheIndex.libIndex = Class8.getCacheIndex(false, true, true, 28); - LoadingStageNumber = 15; - loadingBarTextToDisplay = TextCore.ConxUpdateServer; - anInt1354 = 40; - } else { - loadingBarTextToDisplay = TextCore.LoadingConnecting; - LoadingStageNumber = 12; - } - } else if (anInt1354 == 40) { - var2 = 0; - - for (var3 = 0; var3 < 29; ++var3) { - var2 += Unsorted.aClass151_Sub1Array2601[var3].method2111() * anIntArray3288[var3] / 100; - } - - if (var2 < 100) { //!= 100 - if (var2 != 0) { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.CheckingForUpdates, RSString.stringAnimator(var2), RSString.parse("(U")}); - } - - LoadingStageNumber = 20; - } else { - LoadingStageNumber = 20; - loadingBarTextToDisplay = TextCore.LoadedUpdateList; - Sprites.getSpriteFromArchive(CacheIndex.spritesIndex); - Class97.method1593(111, CacheIndex.spritesIndex); - TextureOperation20.method233(28280, CacheIndex.spritesIndex); - anInt1354 = 45; - } - } else if (anInt1354 == 45) { - Class140_Sub3.method1959(TextureOperation17.stereoSound); - aClass3_Sub24_Sub4_1193 = new Class3_Sub24_Sub4(); - aClass3_Sub24_Sub4_1193.method479(); - WorldListEntry.aAudioChannel_2627 = AudioChannel.method1195(22050, Class38.gameSignlink, GameShell.canvas, 0); - WorldListEntry.aAudioChannel_2627.method2154(aClass3_Sub24_Sub4_1193); - AudioHandler.method897(aClass3_Sub24_Sub4_1193, CacheIndex.soundFX3Index, CacheIndex.soundFX2Index, CacheIndex.soundFXIndex); - Class3_Sub21.aAudioChannel_2491 = AudioChannel.method1195(2048, Class38.gameSignlink, GameShell.canvas, 1); - Class3_Sub26.aClass3_Sub24_Sub2_2563 = new Class3_Sub24_Sub2(); - Class3_Sub21.aAudioChannel_2491.method2154(Class3_Sub26.aClass3_Sub24_Sub2_2563); - Class27.resampler = new Class157(22050, Class21.sampleRate); - - int loginThemeID; - if (GameConfig.EASTER_EVENT_ENABLED) { - loginThemeID = CacheIndex.musicIndex.getArchiveForName(RSString.parse("Funny Bunnies")); - } else { - loginThemeID = CacheIndex.musicIndex.getArchiveForName(RSString.parse(GameConfig.LOGIN_THEME)); - } - - KeyboardListener.loginThemeSongArchiveID = loginThemeID; - LoadingStageNumber = 30; - anInt1354 = 50; - loadingBarTextToDisplay = TextCore.PreparedSoundEngine; - } else if (anInt1354 == 50) { - var2 = Sprites.method228(CacheIndex.spritesIndex, CacheIndex.fontsIndex); - var3 = 6; - if (var3 <= var2) { - loadingBarTextToDisplay = TextCore.LoadedFonts; - LoadingStageNumber = 35; - anInt1354 = 60; - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingFonts, RSString.stringAnimator(100 * var2 / var3), RSString.parse("(U")}); - LoadingStageNumber = 35; - } - } else if (60 == anInt1354) { - var2 = Unsorted.method599(CacheIndex.spritesIndex); - var3 = 2; - if (var3 <= var2) { - loadingBarTextToDisplay = TextCore.LoadedTitleScreen; - anInt1354 = 65; - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingTitleScreen, RSString.stringAnimator(100 * var2 / var3), RSString.parse("(U")}); - } - LoadingStageNumber = 40; - } else if (anInt1354 == 65) { - Class3_Sub28_Sub9.method581(CacheIndex.fontsIndex, CacheIndex.spritesIndex); - LoadingStageNumber = 45; - loadingBarTextToDisplay = TextCore.OpenedTitleScreen; - Class117.method1719(5); - anInt1354 = 70; - } else if (anInt1354 == 70) { - CacheIndex.configurationsIndex.method2113(); - byte var7 = 0; - var2 = var7 + CacheIndex.configurationsIndex.method2136((byte) -124); - CacheIndex.objectConfigIndex.method2113(); - var2 += CacheIndex.objectConfigIndex.method2136((byte) -128); - CacheIndex.libIndex.method2113(); - var2 += CacheIndex.libIndex.method2136((byte) -124); - CacheIndex.clientscriptMaskIndex.method2113(); - var2 += CacheIndex.clientscriptMaskIndex.method2136((byte) -123); - CacheIndex.npcConfigIndex.method2113(); - var2 += CacheIndex.npcConfigIndex.method2136((byte) -127); - CacheIndex.itemConfigIndex.method2113(); - var2 += CacheIndex.itemConfigIndex.method2136((byte) -128); - CacheIndex.animationIndex.method2113(); - var2 += CacheIndex.animationIndex.method2136((byte) -122); - CacheIndex.graphicFXIndex.method2113(); - var2 += CacheIndex.graphicFXIndex.method2136((byte) -122); - CacheIndex.clientScriptConfigIndex.method2113(); - var2 += CacheIndex.clientScriptConfigIndex.method2136((byte) -128); - CacheIndex.quickchatMessagesIndex.method2113(); - var2 += CacheIndex.quickchatMessagesIndex.method2136((byte) -126); - CacheIndex.quickchatMenusIndex.method2113(); - var2 += CacheIndex.quickchatMenusIndex.method2136((byte) -128); - CacheIndex.particlesConfigIndex.method2113(); - var2 += CacheIndex.particlesConfigIndex.method2136((byte) -124); - if (var2 >= 1100) { - Class132.method1799((byte) 96, CacheIndex.configurationsIndex); - FloorOverlayDefinition.method631(CacheIndex.configurationsIndex); - Class3_Sub28_Sub8.method575(CacheIndex.configurationsIndex, -1); - method375(CacheIndex.modelsIndex, CacheIndex.configurationsIndex); - Class108.method1661(CacheIndex.objectConfigIndex, CacheIndex.modelsIndex); - ItemDefinition.method1103(CacheIndex.modelsIndex, CacheIndex.npcConfigIndex); - GameObject.method1864(CacheIndex.itemConfigIndex, Class157.aClass3_Sub28_Sub17_Sub1_2000, CacheIndex.modelsIndex); - StructDefinitionProvider.setIndex(CacheIndex.configurationsIndex); - Class3_Sub20.method392(CacheIndex.skinsIndex, CacheIndex.animationIndex, CacheIndex.skeletonsIndex); - Unsorted.method1053(CacheIndex.configurationsIndex); - Class158.method2180(CacheIndex.modelsIndex, CacheIndex.graphicFXIndex); - Class107.method1648(CacheIndex.clientScriptConfigIndex); - LinkableRSString.method731(CacheIndex.configurationsIndex); - Unsorted.method89(CacheIndex.fontsIndex, CacheIndex.spritesIndex, CacheIndex.interfacesIndex, CacheIndex.modelsIndex); - TextureOperation23.method250(CacheIndex.configurationsIndex); - EnumDefinitionProvider.setIndex(CacheIndex.clientscriptMaskIndex); - QuickChat.method205(CacheIndex.quickchatMenusIndex, CacheIndex.quickchatMessagesIndex, new Class7()); - QuickChat.method1236(CacheIndex.quickchatMenusIndex, CacheIndex.quickchatMessagesIndex); - Class58.method1197(CacheIndex.configurationsIndex); - Unsorted.method2065(CacheIndex.configurationsIndex, CacheIndex.spritesIndex); - Class107.method1645(CacheIndex.configurationsIndex, CacheIndex.spritesIndex); - LoadingStageNumber = 50; - loadingBarTextToDisplay = TextCore.LoadedConfig; - Class29.method968(128); - anInt1354 = 80; - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingConfig, RSString.stringAnimator(var2 / 11), RSString.parse("(U")}); - LoadingStageNumber = 50; - } - } else if (anInt1354 == 80) { - var2 = Sprites.method107(CacheIndex.spritesIndex); - var3 = 15; - if (var2 < var3) { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingSprites, RSString.stringAnimator(var2 * 100 / var3), RSString.parse("(U")}); - LoadingStageNumber = 60; - } else { - Sprites.method887(CacheIndex.spritesIndex); - anInt1354 = 90; - LoadingStageNumber = 60; - loadingBarTextToDisplay = TextCore.LoadedSprites; - } - } else if (anInt1354 != 90) { - if (anInt1354 == 100) { - if (TextureOperation25.method334(CacheIndex.spritesIndex)) { - anInt1354 = 110; - } - } else if (anInt1354 == 110) { - TextureOperation20.aClass67_1443 = new Class67(); - Class38.gameSignlink.startThread(10, TextureOperation20.aClass67_1443); - loadingBarTextToDisplay = TextCore.LoadedInputHandler; - LoadingStageNumber = 75; - anInt1354 = 120; - } else if (anInt1354 != 120) { - if (anInt1354 == 130) { - if (CacheIndex.interfacesIndex.method2113()) { - if (CacheIndex.interfaceScriptsIndex.method2113()) { - if (CacheIndex.fontsIndex.method2113()) { - if (CacheIndex.worldmapIndex.method2127(TextCore.aClass94_1342)) { - Class75_Sub4.method1353(Sprites.aSoftwareSpriteArray2140, CacheIndex.worldmapIndex); - LoadingStageNumber = 95; - loadingBarTextToDisplay = TextCore.LoadedInterfaces; - anInt1354 = 135; - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingInterfaces, RSString.stringAnimator(90 - -(CacheIndex.worldmapIndex.method2116(TextCore.aClass94_1342) / 10)), RSString.parse("(U")}); - LoadingStageNumber = 85; - } - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingInterfaces, RSString.stringAnimator(85 - -(CacheIndex.fontsIndex.method2136((byte) -124) / 20)), RSString.parse("(U")}); - LoadingStageNumber = 85; - } - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingInterfaces, RSString.stringAnimator(75 - -(CacheIndex.interfaceScriptsIndex.method2136((byte) -128) / 10)), RSString.parse("(U")}); - LoadingStageNumber = 85; - } - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingInterfaces, RSString.stringAnimator(CacheIndex.interfacesIndex.method2136((byte) -123) * 3 / 4), RSString.parse("(U")}); - LoadingStageNumber = 85; - } - } else if (135 == anInt1354) { - var2 = Class121.method1735(); - if (-1 == var2) { - LoadingStageNumber = 95; - loadingBarTextToDisplay = TextCore.LoadingWLD; - } else if (var2 == 7 || var2 == 9) { - this.errorPrint("worldlistfull"); - Class117.method1719(1000); - } else if (Class30.loadedWorldList) { - loadingBarTextToDisplay = TextCore.LoadedWLD; - anInt1354 = 140; - LoadingStageNumber = 96; - } else { - this.errorPrint("worldlistio_" + var2); - Class117.method1719(1000); - } - } else if (anInt1354 == 140) { - loginScreenInterfaceID = CacheIndex.interfacesIndex.getArchiveForName(RSString.parse("loginscreen")); - CacheIndex.landscapesIndex.method2115(-9, false); - CacheIndex.musicIndex.method2115(111, false);//true - CacheIndex.spritesIndex.method2115(-76, true); - CacheIndex.fontsIndex.method2115(91, true); - CacheIndex.huffmanEncodingIndex.method2115(-116, true); - CacheIndex.interfacesIndex.method2115(99, true); - LoadingStageNumber = 97; - loadingBarTextToDisplay = TextCore.Starting3DLibrary; - anInt1354 = 150; - sweepReferenceCache = true; - } else if (anInt1354 == 150) { - Class88.method1454(); - if (Unsorted.aBoolean2146) { - Class3_Sub28_Sub9.anInt3622 = 0; - Unsorted.anInt3671 = 0; - Unsorted.anInt2577 = 0; - Class3_Sub20.anInt2488 = 0; - } - - Unsorted.aBoolean2146 = true; - Class119.method1730(Class38.gameSignlink); - GameObject.graphicsSettings(false, Unsorted.anInt2577, -1, -1); - LoadingStageNumber = 100; - anInt1354 = 160; - loadingBarTextToDisplay = TextCore.Started3DLibrary; - } else if (anInt1354 == 160) { - TextureOperation1.method219(true); - } - } else if (CacheIndex.huffmanEncodingIndex.method2125(RSString.parse(""), TextCore.HasHuffman)) { - Class36 var9 = new Class36(CacheIndex.huffmanEncodingIndex.method2123(RSString.parse(""), TextCore.HasHuffman)); - Class1.method69(var9); - loadingBarTextToDisplay = TextCore.LoadedWordPack; - anInt1354 = 130; - LoadingStageNumber = 80; - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingWordPack, TextCore.aClass94_37}); - LoadingStageNumber = 80; - } - } else if (CacheIndex.materialsIndex.method2113()) { - Class102 var8 = new Class102(CacheIndex.texturesIndex, CacheIndex.materialsIndex, CacheIndex.spritesIndex, !Class25.aBoolean488); - Class51.method1140(var8); - if (Unsorted.anInt3625 == 1) { - Class51.method1137(0.9F); - } - - if (2 == Unsorted.anInt3625) { - Class51.method1137(0.8F); - } - - if (Unsorted.anInt3625 == 3) { - Class51.method1137(0.7F); - } - - if (Unsorted.anInt3625 == 4) { - Class51.method1137(0.6F); - } - - loadingBarTextToDisplay = TextCore.LoadedTextures; - anInt1354 = 100; - LoadingStageNumber = 70; - } else { - loadingBarTextToDisplay = RSString.stringCombiner(new RSString[]{TextCore.LoadingTextures, RSString.stringAnimator(CacheIndex.materialsIndex.method2136((byte) -125)), RSString.parse("(U")}); - LoadingStageNumber = 70; - } - } - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "client.A(" + var1 + ')'); - } - } - - final void method25() { - try { - if (Class143.gameStage != 1000) { - ++Class44.anInt719; - if (Class44.anInt719 % 1000 == 1) { - GregorianCalendar var2 = new GregorianCalendar(); - Class38_Sub1.anInt2618 = var2.get(Calendar.HOUR_OF_DAY) * 600 - (-(var2.get(Calendar.MINUTE) * 10) + -(var2.get(Calendar.SECOND) / 6)); - Class24.aRandom3088.setSeed(Class38_Sub1.anInt2618); - } - - this.method48(); - if (Unsorted.aClass8_1936 != null) { - Unsorted.aClass8_1936.method838(); - } - - LinkableRSString.method728(); - Class58.method1194(); - Class32.method996(); - Unsorted.method1225(); - if (HDToolKit.highDetail) { - Class31.method990(); - } - - int var4; - if (Class38.aClass146_668 != null) { - var4 = Class38.aClass146_668.method2078(); - Class29.anInt561 = var4; - } - - if (Class143.gameStage == 0) { - this.method52(48); - Class75_Sub4.method1355(); - } else if (Class143.gameStage == 5) { - this.method52(107); - Class75_Sub4.method1355(); - } else if (Class143.gameStage == 25 || Class143.gameStage == 28) { - Class40.method1046(); - } - - if (10 == Class143.gameStage) { - this.method47(); - TextureOperation37.method267(); - Class163_Sub1_Sub1.method2216(); - LoginHandler.handleLogin(); - } else if (Class143.gameStage == 30) { - TextureOperation20.method235(); - } else if (Class143.gameStage == 40) { - LoginHandler.handleLogin(); - if (messageToDisplay != -3) { - if (messageToDisplay == 15) { - Class21.method912(); - } else if (messageToDisplay != 2) { - Class167.method2269((byte) 46); - } - } - } - - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "client.N(" + (byte) 107 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/ClientCommands.java b/Client/src/main/java/org/runite/client/ClientCommands.java deleted file mode 100644 index 61ef6273c..000000000 --- a/Client/src/main/java/org/runite/client/ClientCommands.java +++ /dev/null @@ -1,271 +0,0 @@ -package org.runite.client; - - -import org.rs09.SystemLogger; - -import java.awt.*; -import java.io.File; -import java.io.FileWriter; -import java.net.URI; - -public class ClientCommands { - - static boolean commandQaOpEnabled = false; - static boolean fpsOverlayEnabled = false; - static boolean tweeningEnabled = false; - static boolean shiftClickEnabled = false; - static boolean canDrop = false; - static boolean renderInfoOverlayEnabled = false; - /** - * Removed out of (depreciated)Properties.java - */ - private static boolean modernHotkeys; - - - - public static void ClientCommands(RSString command) { - try { - int var2; - int var3; - Runtime runtime; - if (command.equalsStringIgnoreCase(TextCore.COMMAND_GARBAGE_COLLECTOR)) { - clearClientCacheMemory(); - - for (var2 = 0; var2 < 10; ++var2) { - System.gc(); - } - - runtime = Runtime.getRuntime(); - var3 = (int) ((runtime.totalMemory() - runtime.freeMemory()) / 1024L); - BufferedDataStream.addChatMessage(null, 0, RSString.stringCombiner(new RSString[]{TextCore.memoryEquals, RSString.stringAnimator(var3), TextCore.Memoryk}), -1); - } - - int var4; - if (command.equalsStringIgnoreCase(TextCore.COMMAND_MEMORY_MANAGEMENT)) { - clearClientCacheMemory(); - - for (var2 = 0; var2 < 10; ++var2) { - System.gc(); - } - - runtime = Runtime.getRuntime(); - var3 = (int) ((runtime.totalMemory() + -runtime.freeMemory()) / 1024L); - BufferedDataStream.addChatMessage(null, 0, RSString.stringCombiner(new RSString[]{TextCore.memoryBeforeCleanup, RSString.stringAnimator(var3), TextCore.Memoryk}), -1); - Class3_Sub1.method90(1); - clearClientCacheMemory(); - - for (var4 = 0; var4 < 10; ++var4) { - System.gc(); - } - - var3 = (int) ((runtime.totalMemory() + -runtime.freeMemory()) / 1024L); - BufferedDataStream.addChatMessage(null, 0, RSString.stringCombiner(new RSString[]{TextCore.aClass94_2033, RSString.stringAnimator(var3), TextCore.Memoryk}), -1); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_PC_CACHE_SIZE)) { - BufferedDataStream.addChatMessage(null, 0, RSString.stringCombiner(new RSString[]{TextCore.aClass94_442, RSString.stringAnimator(Unsorted.method1727((byte) 123))}), -1); - } - - if (HDToolKit.highDetail && command.equalsStringIgnoreCase(TextCore.COMMAND_GRAPHICS_CARD_MEMORY)) { - System.out.println("oncard_geometry:" + Class31.anInt585); - System.out.println("oncard_2d:" + Class31.memory2D); - System.out.println("oncard_texture:" + Class31.anInt580); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_BREAK_CLIENT_CONNECTION)) { - TextureOperation20.breakClientConnection(); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_BREAK_JS5_CLIENT_CONNECTION)) { - Class58.aJs5Worker_917.close(); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_BREAK_JS5_SERVER_CONNECTION)) { - Class58.aJs5Worker_917.requestTermination(); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_BREAK_CONNECTION)) { - Class38.gameSignlink.method1431(); - Class3_Sub15.activeConnection.applyDummyStreams(); - Class58.aJs5Worker_917.applyDummyStreams(); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_REPLACE_CANVAS)) { - Class3_Sub28_Sub5.forceReplaceCanvasEnable = true; - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_REBUILD)) { - Class117.method1719(25); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_TOGGLE_FPSON)) { - fpsOverlayEnabled = !fpsOverlayEnabled; - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_DISCORD)) { - if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { - try { - Desktop.getDesktop().browse(new URI("https://discord.gg/UVtqkDhxVD")); - } catch(Exception e ){ - e.printStackTrace(); - } - } - } - - if(command.equalsStringIgnoreCase(TextCore.COMMAND_HISCORES) || command.equalsStringIgnoreCase(TextCore.COMMAND_HIGHSCORES)){ - int world = ObjectDefinition.paramWorldID; - String link = world == 1 ? "https://2009scape.org/services/m%3dhiscore/hiscores.html" : "https://2009scape.org/services/m=hiscore/hiscores.html?world=2"; - if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { - try { - Desktop.getDesktop().browse(new URI(link)); - } catch(Exception e ){ - e.printStackTrace(); - } - } - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_RENDER_INFO)) { - renderInfoOverlayEnabled = !renderInfoOverlayEnabled; - } - - if (command.equalsStringIgnoreCase(TextCore.TOGGLE_FK)) { - boolean on = !modernHotkeys; - modernHotkeys = true; - BufferedDataStream.addChatMessage(null, 0, RSString.parse("Modern hotkeys mode toggled " + (on ? "on." : "off.")), -1); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_LOWRES_GRAPHICS)) { - GameObject.graphicsSettings(false, 0, -1, -1); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_HIGHRES_GRAPHICS_WINDOW)) { - GameObject.graphicsSettings(false, 1, -1, -1); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_HIGHRES_GRAPHICS_RESIZE)) { - GameObject.graphicsSettings(false, 2, -1, -1); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_HIGHRES_GRAPHICS_FULLSCREEN)) { - GameObject.graphicsSettings(false, 3, 1024, 768); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_NOCLIP)) { - for (var2 = 0; var2 < 4; ++var2) { - for (var3 = 1; var3 < 103; ++var3) { - for (var4 = 1; var4 < 103; ++var4) { - AtmosphereParser.aClass91Array1182[var2].anIntArrayArray1304[var3][var4] = 0; - } - } - } - } - - if (command.startsWith(TextCore.COMMAND_SET_PARTICLES)) { - Class127_Sub1.method1758(command.substring(15).parseInt()); - Class119.method1730(Class38.gameSignlink); - CS2Script.aBoolean2705 = false; - } - - if (command.startsWith(TextCore.COMMAND_FPS) && Class44.paramModeWhere != 0) { - Class65.method1237(command.substring(6).parseInt()); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_ERROR_TEST)) { - throw new RuntimeException(); - } - - if (command.startsWith(TextCore.COMMAND_RECT_DEBUG)) { - Client.rectDebugInt = command.substring(12).trim(1).parseInt(); - BufferedDataStream.addChatMessage(null, 0, RSString.stringCombiner(new RSString[]{TextCore.rectDebugEquals, RSString.stringAnimator(Client.rectDebugInt)}), -1); - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_QA_OP_TEST)) { - commandQaOpEnabled = true; - } - /* Client tweening */ - if (command.equalsStringIgnoreCase(TextCore.COMMAND_TWEENING)) { - if (tweeningEnabled) { - tweeningEnabled = false; - BufferedDataStream.addChatMessage(null, 0, TextCore.forcedTweeningDisabled, -1); - } else { - tweeningEnabled = true; - BufferedDataStream.addChatMessage(null, 0, TextCore.forcedTweeningEnabled, -1); - } - } - - if (command.equalsStringIgnoreCase(TextCore.COMMAND_SHIFT_DROP_CLICK)) { - shiftClickEnabled = !shiftClickEnabled; - } - - if (command.startsWith(TextCore.COMMAND_GETBITS)) { - System.out.println("Trying to write file..."); - try { - int index = command.substring(10).parseInt(); - File file = new File("FILE_BITS.txt"); - FileWriter writer = new FileWriter(file); - for(int i = 0; i < 200000; i++){ - CSConfigCachefile cachefile = CSConfigCachefile.getCSConfigFileFromVarbitID(i); - if(cachefile.parentVarpIndex == index){ - writer.write("ID: " + i + " lowerBound = " + cachefile.lowerBound + " upperBound = " + cachefile.upperBound + " size = " + ((cachefile.upperBound - cachefile.lowerBound) + 1)); - writer.write("\n"); - } - } - writer.flush(); - writer.close(); - BufferedDataStream.addChatMessage(null, 0, RSString.parse("Wrote bits to " + file.getAbsolutePath()), -1); - } catch (Exception e){ - SystemLogger.logInfo("Whoopsie"); - e.printStackTrace(); - } - } - - TextureOperation12.outgoingBuffer.putOpcode(44); - TextureOperation12.outgoingBuffer.writeByte(command.length() + -1); - TextureOperation12.outgoingBuffer.writeString(command.substring(2)); - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "k.H(" + (command != null ? "{...}" : "null") + ',' + false + ')'); - } - } - - static void clearClientCacheMemory() { - try { - Class163_Sub2_Sub1.aReferenceCache_4015.clearSoftReferences();//Class3_Sub28_Sub20.method724(); - Class44.aReferenceCache_725.clearSoftReferences();//Class3_Sub28_Sub18.method710(); - Class140_Sub4.aReferenceCache_2792.clearSoftReferences(); - Unsorted.aReferenceCache_21.clearSoftReferences();//Unsorted.method55(); - ObjectDefinition.aReferenceCache_1401.clearSoftReferences();//Unsorted.method55(); - Unsorted.aReferenceCache_4051.clearSoftReferences();//Unsorted.method55(); - ObjectDefinition.aReferenceCache_1965.clearSoftReferences();//Unsorted.method55(); - Unsorted.aReferenceCache_4043.clearSoftReferences();//Class163_Sub2_Sub1.method2222(); - CS2Script.aReferenceCache_2442.clearSoftReferences();//Class163_Sub2_Sub1.method2222(); - Class154.aReferenceCache_1964.clearSoftReferences();//Class163_Sub2_Sub1.method2222(); - QuickChatDefinition.aReferenceCache_3572.clearSoftReferences();//Originally Class3_Sub30_Sub1.method813(); - Class143.aReferenceCache_1874.clearSoftReferences();//Originally Class3_Sub30_Sub1.method813(); - Class67.aReferenceCache_1013.clearSoftReferences();//Originally Class3_Sub30_Sub1.method813(); - Texture.aReferenceCache_1146.clearSoftReferences();//Originally Class133.method1803(); - Class159.aReferenceCache_2016.clearSoftReferences();//Originally Class133.method1803(); - Class3_Sub31.aReferenceCache_2604.clearSoftReferences();//Class38.method1025(); - Class27.aReferenceCache_511.clearSoftReferences();//Class38.method1025(); - VarpHelpers.varbitLookup.clearSoftReferences();//Class40.method1044(); - Class136.aReferenceCache_1772.clearSoftReferences(); - RenderAnimationDefinition.aReferenceCache_1955.clearSoftReferences();//Originally: Class158_Sub1.method2192(); - TextureOperation25.aReferenceCache_3412.clearSoftReferences();//Originally: Class159.method2196(); - TextureOperation2.aReferenceCache_3369.clearSoftReferences();//Originally: Class159.method2196(); - Class128.aReferenceCache_1683.clearSoftReferences(); - Unsorted.aReferenceCache_684.clearSoftReferences(); - Class163_Sub1.aReferenceCache_2984.clearSoftReferences(); - KeyboardListener.aReferenceCache_1911.clearSoftReferences();//Class115.method1714(); - Unsorted.aReferenceCache_1131.clearSoftReferences();//Class115.method1714(); - Class114.aReferenceCache_1569.clearSoftReferences();//Class3_Sub28_Sub6.h(); - Class3_Sub15.aReferenceCache_2428.clearSoftReferences();//Class3_Sub28_Sub6.h(); - Unsorted.aReferenceCache_743.clearSoftReferences();//Class3_Sub28_Sub6.h(); - LinkedList.aReferenceCache_939.clearSoftReferences();//Class107.method1650(); - Class158_Sub1.aReferenceCache_2982.clearSoftReferences();//MouseListeningClass.method2089(); - TextureOperation1.aReferenceCache_3130.clearSoftReferences(); - Unsorted.aReferenceCache_1135.clearSoftReferences(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "je.O(" + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/ClientErrorException.java b/Client/src/main/java/org/runite/client/ClientErrorException.java deleted file mode 100644 index 426424577..000000000 --- a/Client/src/main/java/org/runite/client/ClientErrorException.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.runite.client; - -public final class ClientErrorException extends RuntimeException { - - static int[] anIntArray2113 = new int[]{2, 2, 4, 0, 1, 8, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}; - public static Class96[] aClass96Array2114 = new Class96[4]; - static int anInt2115 = -1; - String aString2117; - Throwable aThrowable2118; - static RSString[] aClass94Array2119 = null; - static int anInt2120 = 0; - static String worldListHost; - - - static void method2285(int var0, int var1, int var2, int var3, int var5) { - try { - LinkableRSString.anInt2587 = var1; - TextureOperation9.anInt3103 = var5; - Class140_Sub7.anInt2938 = var0; - Unsorted.anInt144 = var3; - Unsorted.anInt3695 = var2; - } catch (RuntimeException var7) { - throw clientError(var7, "ld.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + false + ',' + var5 + ')'); - } - } - - ClientErrorException(Throwable var1, String var2) { - this.aString2117 = var2; - this.aThrowable2118 = var1; - } - - static int method2287(int var0, byte var1) { - try { - return (var0 < 97 || var0 > 122) && (var0 < 224 || var0 > 254 || 247 == var0)?(var0 != 255 ?(var0 != 156?(var1 != 59?72:var0):140):159):var0 + -32; - } catch (RuntimeException var3) { - throw clientError(var3, "ld.C(" + var0 + ',' + var1 + ')'); - } - } - - public static ClientErrorException clientError(Throwable var0, String var1) { - //var0.printStackTrace(); - //var0.printStackTrace(); Uncomment to be spammed when clan chat is used cuz clanchat breaks the text script. - ClientErrorException var2; - if(var0 instanceof ClientErrorException) { - var2 = (ClientErrorException)var0; - var2.aString2117 = var2.aString2117 + ' ' + var1; - } else { - var2 = new ClientErrorException(var0, var1); - } - - return var2; - } -} diff --git a/Client/src/main/java/org/runite/client/ClientLoader.java b/Client/src/main/java/org/runite/client/ClientLoader.java deleted file mode 100644 index e4c5bbbc3..000000000 --- a/Client/src/main/java/org/runite/client/ClientLoader.java +++ /dev/null @@ -1,148 +0,0 @@ -package org.runite.client; -import org.rs09.client.config.GameConfig; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.applet.Applet; -import java.awt.*; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Properties; - -/** - * The client loader used to initialize the game. - * @author Jagex - * @author Keldagrim Development Team - * - */ -public class ClientLoader extends Applet { - - /** - * The serial version UID. - */ - private static final long serialVersionUID = -4744673981053459832L; - - /** - * The properties being used. - */ - public static Properties props = new Properties(); - - /** - * The JFrame the clients hosted in. - */ - public JFrame frame; - - /** - * The jpanel of the client. - */ - public JPanel jp = new JPanel(); - - /** - * The world name. - */ - public static String world; - - /** - * The game client. - */ - public static Client game; - - /** - * The client loader. - */ - public static ClientLoader loader; - - public static ModPanel modPanel; - - /** - * Creates the client loader instance. - * @return the client load. - */ - public static ClientLoader create() { - System.setProperty("sun.java2d.noddraw", "true"); //Fixes fullscreen mode - loader = new ClientLoader(); - return loader; - } - - public static void setModPanelVisible(boolean visible){ - modPanel.setVisible(visible); - } - - /** - * Launches the client. - */ - public void launch() { - try { - modPanel = new ModPanel(); - ClientLoader.world = "" + GameConfig.SERVER_NAME; - //System.out.println(GameLaunch.SETTINGS.getWorld()); - this.frame = new JFrame(GameConfig.SERVER_NAME); - this.frame.setIconImage(ImageIO.read(getClass().getClassLoader().getResource("09logo.png"))); - this.frame.setLayout(new BorderLayout()); - this.frame.setSize(300,500); - this.frame.setBackground(Color.BLACK); - this.frame.setResizable(true); - this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - this.jp.setLayout(new BorderLayout()); - this.jp.add(modPanel, BorderLayout.NORTH); - this.jp.add(this); - this.jp.setBackground(Color.black); - this.jp.setPreferredSize(new Dimension(765, 503)); - //this.frame.setIconImage(Toolkit.getDefaultToolkit().getImage("./res/favicon.png")); - this.frame.getContentPane().add(this.jp, "Center"); - this.frame.pack(); - this.frame.setVisible(true); - this.frame.setLocationRelativeTo(null); - props.put("worldid", "" + (GameConfig.WORLD_OVERRIDE == -1 ? GameConfig.WORLD : GameConfig.WORLD_OVERRIDE)); - props.put("members", "1"); - props.put("modewhat", "1"); - props.put("modewhere", "0"); - props.put("safemode", "0"); - props.put("game", "0"); - props.put("js", "1"); - props.put("lang", "0"); - props.put("affid", "0"); - props.put("advertsuppressed", "1"); - props.put("lowmem", "0"); - props.put("settings", "kKmok3kJqOeN6D3mDdihco3oPeYN2KFy6W5--vZUbNA"); - Signlink sn = new Signlink(this, 32, "runescape", 29); - Client.provideSignlink(sn); - game = new Client(); - game.init(); - } catch (Exception localException) { - localException.printStackTrace(); - } - } - - /** - * Gets a property parameter. - * @return the parameter string. - */ - public String getParameter(String paramString) { - return ((String)props.get(paramString)); - } - - /** - * Gets document base. - * @return the URL. - */ - public URL getDocumentBase() { - return getCodeBase(); - } - - /** - * Gets the code base. - * @return The URL. - */ - public URL getCodeBase() { - try { - return new URL("http://" + GameConfig.IP_ADDRESS); - } catch (MalformedURLException localException) { - System.out.println("World List Loading might not be working or something else is wrong."); - System.out.println("Stack Trace:"); - localException.printStackTrace(); - return null; - } - } - -} \ No newline at end of file diff --git a/Client/src/main/java/org/runite/client/ColorCore.java b/Client/src/main/java/org/runite/client/ColorCore.java deleted file mode 100644 index f0dbc9aef..000000000 --- a/Client/src/main/java/org/runite/client/ColorCore.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.runite.client; - -import java.awt.*; - -public class ColorCore { - - /** @woah - * A repository filled with colors that can be set to whatever the user sees fit - * Each will be labeled for what their original purpose is for and the files that - * They reside in. A few values need to be played with only tested some of the colors - * to the corresponding name - */ - - /** - * Used to display the color difference between two players - * Files that use these colors: - * Class72.java - combatLevelColor - * Class3_Sub13_Sub30.java - getCombatLevelDifferenceColor - * - - * Level equation for difference (-otherPlayer + yourPlayer) - * Used in values of 3, -9 to 9 , - * Following a gradient Dark Red -> Yellow -> Bright Green - */ - static RSString LvlDiffN9 = RSString.parse("");//Solid Red OG: - static RSString LvlDiffN6 = RSString.parse("");//Dark Orange OG: - static RSString LvlDiffN3 = RSString.parse("");//Orange OG: - static RSString LvlDiffN0 = RSString.parse("");//Yellow Orange OG: - static RSString LvlDiffDefault = RSString.parse("");//Yellow OG: - static RSString LvlDiffP0 = RSString.parse("");//Yellow OG: - static RSString LvlDiffP3 = RSString.parse("");//Yellow Green OG: - static RSString LvlDiffP6 = RSString.parse("");//Green OG: - static RSString LvlDiffP9 = RSString.parse("");//Bright Green OG: - - - //Used to display the color of coins - /** - * Used to display the color of stacks of items/coins - * Files that use these colors: - * Class3_Sub7.java - Method123 - * Class36.java - Method1013 - */ - static RSString MillionStackColor = RSString.parse("");//Green - static RSString ThousandStackColor = RSString.parse("");//White - static RSString DefaultStackColor = RSString.parse("");//Yellow - - - /** - * Used to display the color of Usernames/items/etc. "Context_Menu_Color" - * Files that use these colors: - * Class3_Sub13_Sub30.java - method312 - * Class3_Sub30_Sub1.java - method806 - */ - static RSString ContextColor = RSString.parse("");//White - - - //private message Class3_Sub28_Sub4.aClass94_3573 - static RSString ObjectNameColor = RSString.parse("");//Light blue - static RSString PMColor = RSString.parse(" )2> "); - - //bank will be orange - //entity color? Item color? - //Class47(items), Class3_Sub30_Sub1, Class3_sub24_Sub4, WaterfallShader, - static RSString ItemInterfaceColor = RSString.parse("");//offwhite - static RSString GroundItemColor = RSString.parse("");//offwhite WaterfallShader //C - static RSString ItemBackpackColor = RSString.parse("");//offwhite Class3_Sub24_Sub4 //C - static RSString ContextColor2 = RSString.parse("");//offwhite - - //Class144 - static RSString NPCRightClickColor = RSString.parse("");//Yellow - - //Class130 Orange Possible Bank - static RSString BankItemColor = RSString.parse(" )2> "); - - //Class3 Sub28 Sub16 Possible Text Color - static RSString TextColor = RSString.parse(" )2> "); - - - /** - * Used to change the loading bar color on client launch - * Files that use these colors: - * Class3_Sub28_Sub1.java - method updateLoadingBar - */ - public static Color loadingbarcolor = new Color(140, 17, 17); - - /** - * Fun little RGB rainbow method - */ - static float hue = 0.0f; - static float sat = 1.0f; - static float bri = 1.0f; - - static int getHexColors() { - hue += 0.005; //Bigger# faster the speed - Color base = new Color(Color.HSBtoRGB(hue, sat, bri)); - Color color = new Color(base.getRed(), base.getGreen(), base.getBlue(), 125); - return color.getRGB(); - } - -} diff --git a/Client/src/main/java/org/runite/client/ComponentWrappedCanvas.java b/Client/src/main/java/org/runite/client/ComponentWrappedCanvas.java deleted file mode 100644 index 69954fb5f..000000000 --- a/Client/src/main/java/org/runite/client/ComponentWrappedCanvas.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.runite.client; - -import java.awt.Canvas; -import java.awt.Component; -import java.awt.Graphics; - -final class ComponentWrappedCanvas extends Canvas { - - private final Component component; - - public final void update(Graphics g) { - this.component.update(g); - } - - public final void paint(Graphics g) { - this.component.paint(g); - } - - public ComponentWrappedCanvas(Component component) { - this.component = component; - } - -} diff --git a/Client/src/main/java/org/runite/client/ConfigInventoryDefinition.java b/Client/src/main/java/org/runite/client/ConfigInventoryDefinition.java deleted file mode 100644 index db6b0b90e..000000000 --- a/Client/src/main/java/org/runite/client/ConfigInventoryDefinition.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; -import org.rs09.client.data.NodeCache; - -public final class ConfigInventoryDefinition extends Node { - - static NodeCache aClass47_818 = new NodeCache(64); - int size = 0; - static int anInt3655 = -1; - - public static ConfigInventoryDefinition retrieveConfigurationInventoryFile(int var0) { - ConfigInventoryDefinition var2 = (ConfigInventoryDefinition) aClass47_818.get(var0); - if (null == var2) { - byte[] var3 = Class8.configurationReferenceCache.getFile(5, var0); - var2 = new ConfigInventoryDefinition(); - if (var3 != null) { - var2.decode(new DataBuffer(var3)); - } - - aClass47_818.put(var0, var2); - } - return var2; - } - - public final void decode(DataBuffer buffer) { - while (true) { - int opcode = buffer.readUnsignedByte(); - if (0 == opcode) { - return; - } - - this.decode(buffer, opcode); - } - } - - private void decode(DataBuffer buffer, int opcode) { - if (opcode == 2) { - this.size = buffer.readUnsignedShort(); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/DataBuffer.java b/Client/src/main/java/org/runite/client/DataBuffer.java deleted file mode 100644 index 2a1de4f47..000000000 --- a/Client/src/main/java/org/runite/client/DataBuffer.java +++ /dev/null @@ -1,518 +0,0 @@ -package org.runite.client; - -import kotlin.text.Charsets; -import org.rs09.client.Linkable; -import org.rs09.client.config.GameConfig; -import org.rs09.client.util.ByteArrayPool; -import org.rs09.client.util.CRC; - -import java.math.BigInteger; -import java.util.Objects; - -public class DataBuffer extends Linkable { - - public byte[] buffer; - public int index; - - public DataBuffer(int capacity) { - this.buffer = ByteArrayPool.INSTANCE.getByteArray(capacity); - this.index = 0; - } - public DataBuffer(byte[] buffer) { - this.index = 0; - this.buffer = buffer; - } - - public final int readUnsignedShort() { - this.index += 2; - return (this.buffer[-2 + this.index] << 8 & 0xff00) + (this.buffer[-1 + this.index] & 0xFF); - } - - public final void writeInt(int value) { - this.buffer[this.index++] = (byte) (value >> 24); - this.buffer[this.index++] = (byte) (value >> 16); - this.buffer[this.index++] = (byte) (value >> 8); - this.buffer[this.index++] = (byte) value; - } - - final void writeLong(long value) { - this.buffer[this.index++] = (byte) ((int) (value >> 56)); - this.buffer[this.index++] = (byte) ((int) (value >> 48)); - this.buffer[this.index++] = (byte) ((int) (value >> 40)); - this.buffer[this.index++] = (byte) ((int) (value >> 32)); - this.buffer[this.index++] = (byte) ((int) (value >> 24)); - this.buffer[this.index++] = (byte) ((int) (value >> 16)); - this.buffer[this.index++] = (byte) ((int) (value >> 8)); - this.buffer[this.index++] = (byte) ((int) value); - } - - // TODO Rename / refactor - final void method739(int var2, long var3) { - --var2; - if (var2 >= 0 && var2 <= 7) { - for (int var5 = var2 * 8; 0 <= var5; var5 -= 8) { - this.buffer[this.index++] = (byte) ((int) (var3 >> var5)); - } - - } else { - throw new IllegalArgumentException(); - } - } - - // TODO Rename / refactor - final int method741() { - byte var2 = this.buffer[this.index++]; - - int var3; - for (var3 = 0; 0 > var2; var2 = this.buffer[this.index++]) { - var3 = (127 & var2 | var3) << 7; - } - - return var2 | var3; - } - - // TODO Rename / refactor - final void method742(int var2) { - this.buffer[this.index + -var2 - 4] = (byte) (var2 >> 24); - this.buffer[this.index + -var2 - 3] = (byte) (var2 >> 16); - this.buffer[this.index + -var2 - 2] = (byte) (var2 >> 8); - this.buffer[this.index + -var2 - 1] = (byte) var2; - } - - final void write128Byte(int value) { - this.buffer[this.index++] = (byte) (128 - value); - } - - // TODO Rename / refactor - public final void writeString(RSString var2) { - this.index += var2.method1580(this.buffer, this.index, var2.length()); - this.buffer[this.index++] = 0; - } - - public final void writeString(String string) { - byte[] bytes = string.getBytes(Charsets.ISO_8859_1); - System.arraycopy(bytes, 0, buffer, index, bytes.length); - index += bytes.length; - buffer[index++] = 0; - } - - final int readSignedShort128() { - this.index += 2; - int value = ((this.buffer[this.index - 2] & 0xff) << 8) + - (this.buffer[this.index - 1] - 128 & 0xff); - - if (value > 32767) { - value -= 65536; - } - - return value; - } - - public final int readInt() { - this.index += 4; - return ((this.buffer[this.index - 4] & 0xff) << 24) + - ((this.buffer[this.index - 3] & 0xff) << 16) + - ((this.buffer[this.index - 2] & 0xff) << 8) + - (this.buffer[this.index - 1] & 0xFF); - } - - final byte readSigned128Byte() { - return (byte) (128 - this.buffer[this.index++]); - } - - // TODO Refactor / rename method - final RSString method750() { - if (this.buffer[this.index] == 0) { - this.index++; - return null; - } else { - return this.readString(); - } - } - - final int readUnsignedByte128() { - return this.buffer[this.index++] - 128 & 0xff; - } - - public final void writeByte(int value) { - this.buffer[this.index++] = (byte) value; - } - - final void putBytes(byte[] bytes, int length) { - int i = 0; - while (length > i) { - this.buffer[this.index++] = bytes[i]; - i++; - } - } - - final int readUnsigned128Byte() { - return 128 - this.buffer[this.index++] & 0xFF; - } - - // TODO - final int getTriByte2() { - try { - this.index += 3; - return ((this.buffer[this.index + -2] & 0xFF) << 8) + ((this.buffer[-1 + this.index] & 0xFF) << 16) + (this.buffer[-3 + this.index] & 0xFF); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wa.BA(" + (byte) -118 + ')'); - } - } - - final long readLong() { - long msb = (long) this.readInt() & 0xFFFFFFFFL; - long lsb = (long) this.readInt() & 0xFFFFFFFFL; - return lsb + (msb << 32); - } - - final void writeIntLE(int value) { - this.buffer[this.index++] = (byte) value; - this.buffer[this.index++] = (byte) (value >> 8); - this.buffer[this.index++] = (byte) (value >> 16); - this.buffer[this.index++] = (byte) (value >> 24); - } - - final int readUnsignedShort128() { - this.index += 2; - return (this.buffer[this.index - 1] - 128 & 0xff) - + (65280 & this.buffer[this.index - 2] << 8); - } - - final void writeIntV2(int value) { - this.buffer[this.index++] = (byte) (value >> 16); - this.buffer[this.index++] = (byte) (value >> 24); - this.buffer[this.index++] = (byte) value; - this.buffer[this.index++] = (byte) (value >> 8); - } - - final byte readSignedByte() { - return this.buffer[this.index++]; - } - - // TODO - final RSString getGJString2(int var1) { - try { - byte var2 = this.buffer[this.index++]; - if (var1 < 50) { - this.buffer = null; - } - - if (0 == var2) { - int var3 = this.index; - - while (Objects.requireNonNull(this.buffer)[this.index++] != 0) { - } - - return TextureOperation33.bufferToString(this.buffer, this.index - (var3 - -1), var3); - } else { - throw new IllegalStateException("Bad version number in gjstr2"); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "wa.DA(" + var1 + ')'); - } - } - - final void writeFloatLE(float value) { - int bits = Float.floatToRawIntBits(value); - - this.buffer[this.index++] = (byte) bits; - this.buffer[this.index++] = (byte) (bits >> 8); - this.buffer[this.index++] = (byte) (bits >> 16); - this.buffer[this.index++] = (byte) (bits >> 24); - } - - final byte readSignedNegativeByte() { - return (byte) -this.buffer[this.index++]; - } - - final void readBytes(byte[] out, int length) { - for (int i = 0; length > i; ++i) { - out[i] = this.buffer[this.index++]; - } - } - - final void writeShort128LE(int value) { - this.buffer[this.index++] = (byte) (value + 128); - this.buffer[this.index++] = (byte) (value >> 8); - } - - final int readUnsignedShortLE() { - this.index += 2; - return (255 & this.buffer[this.index - 2]) - + (65280 & this.buffer[this.index - 1] << 8); - } - - // TODO Some smart - final void method768(int var2) { - if (0 <= var2 && var2 < 128) { - this.writeByte(var2); - } else if (0 <= var2 && var2 < 32768) { - this.writeShort(32768 + var2); - } else { - throw new IllegalArgumentException(); - } - } - - // TODO finish varbyte length? - final void method769(int value) { - this.buffer[-1 + -value + this.index] = (byte) value; - } - - public final void finishVarshortPacket(int value) { - this.buffer[this.index + -value - 2] = (byte) (value >> 8); - this.buffer[this.index + -value - 1] = (byte) value; - } - - // TODO XTEA crypt? - final void method770(int[] var1, int var4) { - int var5 = this.index; - this.index = 5; - int var6 = (-5 + var4) / 8; - - for (int var7 = 0; var6 > var7; ++var7) { - int var10 = -957401312; - int var8 = this.readInt(); - int var9 = this.readInt(); - int var12 = 32; - - for (int var11 = -1640531527; var12-- > 0; var8 -= (var9 >>> 5 ^ var9 << 4) + var9 ^ var1[var10 & 3] + var10) { - var9 -= var1[(6754 & var10) >>> 11] + var10 ^ var8 + (var8 >>> 5 ^ var8 << 4); - var10 -= var11; - } - - this.index -= 8; - this.writeInt(var8); - this.writeInt(var9); - } - - this.index = var5; - } - - // TODO - final void method771(int var2) { - if ((-128 & var2) != 0) { - if ((-16384 & var2) != 0) { - if ((var2 & -2097152) != 0) { - if (0 != (-268435456 & var2)) { - this.writeByte(var2 >>> 28 | 128); - } - - this.writeByte(128 | var2 >>> 21); - } - - this.writeByte(128 | var2 >>> 14); - } - - this.writeByte(var2 >>> 7 | 128); - } - - this.writeByte(var2 & 127); - } - - // TODO - final long method772(int var1) { - --var1; - if (0 <= var1 && var1 <= 7) { - long var4 = 0L; - - for (int var3 = var1 * 8; var3 >= 0; var3 -= 8) { - var4 |= ((long) this.buffer[this.index++] & 255L) << var3; - } - - return var4; - } else { - throw new IllegalArgumentException(); - } - } - - // TODO - final int method773() { - int var3 = this.getSmart(); - - int var2; - for (var2 = 0; 32767 == var3; var2 += 32767) { - var3 = this.getSmart(); - } - - var2 += var3; - return var2; - } - - final void readBytesReverse(int length, byte[] out) { - for (int i = -(-length - -1); i >= 0; --i) { - out[i] = this.buffer[this.index++]; - } - } - - final void writeIntV1(int value) { - this.buffer[this.index++] = (byte) (value >> 8); - this.buffer[this.index++] = (byte) value; - this.buffer[this.index++] = (byte) (value >> 24); - this.buffer[this.index++] = (byte) (value >> 16); - } - - public final RSString readString() { - int startIndex = this.index; - - while (this.buffer[this.index++] != 0) ; - - return TextureOperation33.bufferToString(this.buffer, (this.index - 1) - startIndex, startIndex); - } - - final int getSmart() { - int var2 = this.buffer[this.index] & 0xFF; - return 128 <= var2 ? -32768 + this.readUnsignedShort() : this.readUnsignedByte(); - } - - final void writeMedium(int value) { - this.buffer[this.index++] = (byte) (value >> 16); - this.buffer[this.index++] = (byte) (value >> 8); - this.buffer[this.index++] = (byte) value; - } - - final int readIntV1() { - this.index += 4; - return ((this.buffer[this.index - 2] & 0xFF) << 24) + - ((this.buffer[this.index - 1] & 0xFF) << 16) + - (65280 & this.buffer[this.index - 4] << 8) + - (this.buffer[this.index + -3] & 0xFF); - } - - final int readUnsignedShortLE128() { - this.index += 2; - return (this.buffer[this.index - 1] << 8 & 65280) + - (255 & this.buffer[this.index - 2] - 128); - } - - final int readIntLE() { - this.index += 4; - return (255 & this.buffer[-4 + this.index]) + - (16711680 & this.buffer[this.index - 2] << 16) + - ((255 & this.buffer[this.index + -1]) << 24) + - ((this.buffer[-3 + this.index] & 0xFF) << 8); - } - - final void putShortA(int var1) { - this.buffer[this.index++] = (byte) (var1 >> 8); - this.buffer[this.index++] = (byte) (128 + var1); - } - - // TODO What is the difference between this and WriteIntLE? - final void writeIntLE2(int value) { - this.buffer[this.index++] = (byte) value; - this.buffer[this.index++] = (byte) (value >> 8); - this.buffer[this.index++] = (byte) (value >> 16); - this.buffer[this.index++] = (byte) (value >> 24); - } - - final int readUnsignedNegativeByte() { - return -this.buffer[this.index++] & 0xff; - } - - final int readSignedShort() { - this.index += 2; - int value = (this.buffer[-1 + this.index] & 0xFF) + ((255 & this.buffer[this.index + -2]) << 8); - if (value > 32767) { - value -= 65536; - } - return value; - } - - final int readSignedShortLE128() { - this.index += 2; - int var2 = ((this.buffer[this.index - 1] & 0xFF) << 8) + (this.buffer[-2 + this.index] - 128 & 0xFF); - - if (32767 < var2) { - var2 -= 65536; - } - - return var2; - } - - final byte readSignedByte128() { - return (byte) (this.buffer[this.index++] - 128); - } - - final void writeByte128(int value) { - this.buffer[this.index++] = (byte) (128 + value); - } - - final int readSignedShortLE() { - this.index += 2; - - int value = (this.buffer[-2 + this.index] & 0xFF) + (65280 & this.buffer[this.index + -1] << 8); - if (value > 32767) { - value -= 65536; - } - - return value; - } - - final void writeCRC(int startOffset) { - int crc = CRC.INSTANCE.crc32(this.buffer, startOffset, this.index); - this.writeInt(crc); - } - - public final int readMedium() { - this.index += 3; - return (16711680 & this.buffer[this.index + -3] << 16) + - ((65280 & this.buffer[-2 + this.index] << 8) + - (this.buffer[this.index + -1] & 0xFF)); - } - - final void writeShortLE(int value) { - this.buffer[this.index++] = (byte) value; - this.buffer[this.index++] = (byte) (value >> 8); - } - - // TODO - final int getByteOrShort() { - int var2 = this.buffer[this.index] & 0xFF; - return var2 < 128 ? -64 + this.readUnsignedByte() : this.readUnsignedShort() - '\uc000'; - } - - final int readIntV2() { - this.index += 4; - return ((this.buffer[this.index + -3] & 0xFF) << 24) + - (16711680 & this.buffer[this.index + -4] << 16) + - ((this.buffer[this.index + -1] & 0xFF) << 8) + - (255 & this.buffer[this.index + -2]); - } - - final void rsaEncrypt(BigInteger var1, BigInteger var2) { - int var4 = this.index; - this.index = 0; - byte[] var5 = new byte[var4]; - this.readBytes(var5, var4); - BigInteger var6 = new BigInteger(var5); - BigInteger var7; - if (GameConfig.RSA) - var7 = var6.modPow(var1, var2); - else - var7 = var6; - byte[] var8 = var7.toByteArray(); - this.index = 0; - this.writeByte(var8.length); - this.putBytes(var8, var8.length); - } - - final void writeFloat(float value) { - int bits = Float.floatToRawIntBits(value); - - this.buffer[this.index++] = (byte) (bits >> 24); - this.buffer[this.index++] = (byte) (bits >> 16); - this.buffer[this.index++] = (byte) (bits >> 8); - this.buffer[this.index++] = (byte) bits; - } - - public final int readUnsignedByte() { - return this.buffer[this.index++] & 0xFF; - } - - public final void writeShort(int value) { - this.buffer[this.index++] = (byte) (value >> 8); - this.buffer[this.index++] = (byte) value; - } - -} diff --git a/Client/src/main/java/org/runite/client/Display.java b/Client/src/main/java/org/runite/client/Display.java deleted file mode 100644 index f301c3295..000000000 --- a/Client/src/main/java/org/runite/client/Display.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.runite.client; - -import java.awt.*; -import java.lang.reflect.Field; - -public class Display { - - private GraphicsDevice graphicsDevice; - private DisplayMode displayMode; - - - public Display() throws Exception { - GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - this.graphicsDevice = env.getDefaultScreenDevice(); - - if (!this.graphicsDevice.isFullScreenSupported()) { - for (int i = 0; env.getScreenDevices().length > i; ++i) { - GraphicsDevice device = env.getScreenDevices()[i]; - if (device != null && device.isFullScreenSupported()) { - this.graphicsDevice = device; - return; - } - } - - throw new Exception(); - } - } - - private void method917(Frame var1) { - boolean var3 = false; - - Field var4; - boolean var5; - try { - var4 = Class.forName("sun.awt.Win32GraphicsDevice").getDeclaredField("valid"); - var4.setAccessible(true); - var5 = ((Boolean) var4.get(this.graphicsDevice)).booleanValue(); - if (var5) { - var4.set(this.graphicsDevice, Boolean.FALSE); - var3 = true; - } - } catch (Throwable var15) { - } - - var5 = false; - - try { - var5 = true; - this.graphicsDevice.setFullScreenWindow(var1); - var5 = false; - } finally { - if (var5 && var3) { - try { - Field var7 = Class.forName("sun.awt.Win32GraphicsDevice").getDeclaredField("valid"); - var7.set(this.graphicsDevice, Boolean.TRUE); - } catch (Throwable var13) { - } - } - - } - - if (var3) { - try { - var4 = Class.forName("sun.awt.Win32GraphicsDevice").getDeclaredField("valid"); - var4.set(this.graphicsDevice, Boolean.TRUE); - } catch (Throwable var14) { - } - } - - } - - public void configureDisplayMode(int var1, int var2, int var3, int var4, Frame var5, int var6) { - this.displayMode = this.graphicsDevice.getDisplayMode(); - if (var1 > -6) { - this.method919(false); - } - - if (this.displayMode == null) { - throw new NullPointerException(); - } else { - var5.setUndecorated(true); - var5.enableInputMethods(false); - this.method917(var5); - if (var2 == 0) { - int var7 = this.displayMode.getRefreshRate(); - DisplayMode[] var8 = this.graphicsDevice.getDisplayModes(); - boolean var9 = false; - - for (int var10 = 0; var10 < var8.length; ++var10) { - if (var8[var10].getWidth() == var6 && var8[var10].getHeight() == var4 && var3 == var8[var10].getBitDepth()) { - int var11 = var8[var10].getRefreshRate(); - if (!var9 || Math.abs(var2 - var7) > Math.abs(var11 - var7)) { - var9 = true; - var2 = var11; - } - } - } - - if (!var9) { - var2 = var7; - } - } - - this.graphicsDevice.setDisplayMode(new DisplayMode(var6, var4, var3, var2)); - } - } - - public int[] method919(boolean var1) { - if (var1) { - DisplayMode[] var2 = this.graphicsDevice.getDisplayModes(); - int[] var3 = new int[var2.length << 2]; - - for (int var4 = 0; var4 < var2.length; ++var4) { - var3[var4 << 2] = var2[var4].getWidth(); - var3[1 + (var4 << 2)] = var2[var4].getHeight(); - var3[(var4 << 2) - -2] = var2[var4].getBitDepth(); - var3[3 + (var4 << 2)] = var2[var4].getRefreshRate(); - } - - return var3; - } else { - return null; - } - } - - public void updateDisplayMode() { - if (this.displayMode != null) { - this.graphicsDevice.setDisplayMode(this.displayMode); - if (!this.graphicsDevice.getDisplayMode().equals(this.displayMode)) { - throw new RuntimeException("Did not return to correct resolution!"); - } - - this.displayMode = null; - } - - this.method917(null); - } -} diff --git a/Client/src/main/java/org/runite/client/DumpingTools.java b/Client/src/main/java/org/runite/client/DumpingTools.java deleted file mode 100644 index ca9d9ad0a..000000000 --- a/Client/src/main/java/org/runite/client/DumpingTools.java +++ /dev/null @@ -1,268 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.data.HashTable; - -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; - -public class DumpingTools { - public static boolean alreadyRan = false; - - public static final int NUMBER_OF_METHODS = 2065; - /** - * Put all code you want to run once for debugging purposes here. - */ - public static void RunOnceAfterStartup() { - if (alreadyRan) - return; - alreadyRan = true; - // Uncomment one of these to do it. - - //PrintAllValidGameMethods(); - //GetAllMainMethods("mainmeths.txt"); - //DumpOpcodesToTextFile("method2ops.txt", 2); // Good for analyzing program structure. - //DumpToRuneStarCompatible("binaryScripts"); // Requires making the folder first. Probably the most useful one. - } - - public static String FriendlyOpcodeName(int i, int opcode, int arg) { - switch(opcode) { - case 0: return String.format("push_int %d (%08x)", arg, arg); - case 6: return String.format("jmp +%d (%04d)", arg, i + 1 + arg); - case 7: return String.format("bne +%d (%04d)", arg, i + 1 + arg); - case 8: return String.format("be +%d (%04d)", arg, i + 1 + arg); - case 9: return String.format("bgt +%d (%04d)", arg, i + 1 + arg); - case 10: return String.format("blt +%d (%04d)", arg, i + 1 + arg); - case 21: return String.format("ret %d", arg); - case 25: return String.format("fetch_varbit %d", arg); - case 33: return String.format("push_arg %d", arg); - case 3305: return String.format("getcurrentlevel %d", arg); - case 3306: return String.format("getstaticlevel %d", arg); - case 3200: return String.format("playsfx %d", arg); - default: return String.format("unknown (%d: %d)", opcode, arg); - } - } - - public static void DumpOpcodesToTextFile(String outputFile, int methodID) { - try { - AssembledMethod a = ItemDefinition.getMethodByID(methodID); - FileWriter myWriter = new FileWriter(outputFile); - myWriter.write("Opcodes for Method" + methodID + ":\n"); - for (int i = 0; i < a.assemblyInstructions.length; i++) { - int opcode = a.assemblyInstructions[i]; - int arg = a.instructionOperands[i]; - myWriter.write(String.format("%04d: %s\n", i, FriendlyOpcodeName(i, opcode, arg))); - } - myWriter.close(); - } catch (IOException e) { - System.out.println("Cannot write to file " + outputFile); - } - } - - /** - * Dumps all scripts to runestar compatible format. - */ - public static void DumpToRuneStarCompatible(String outputFolder) { - for (int i = 0; i < NUMBER_OF_METHODS; i++) { - try { - AssembledMethod am = ItemDefinition.getMethodByID(i); - if (am == null) { - continue; - } - FileOutputStream fileOut = new FileOutputStream(outputFolder + "/" + i); - ByteBuffer fileData = ByteBuffer.allocate(65536 * 4); - fileData.put((byte) 0); - Short[] ops = new Short[am.assemblyInstructions.length]; - for (int j = 0; j < ops.length; j++) { - int s = am.assemblyInstructions[j]; - fileData.putShort((short) s); - if (s == CS2AsmOpcodes.RETURN.getOp() || s == 38 || s == 39 || s >= 100) { - fileData.put((byte) am.instructionOperands[j]); - } else if (s == CS2AsmOpcodes.PUSH_STR.getOp()) { - fileData.put(am.stringInstructionOperands[j].buffer); - fileData.put((byte) 0); - } else { - fileData.putInt(am.instructionOperands[j]); - } - } - String s = "Generated by 2009scape. https://github.com/2009scape/2009scape"; - fileData.put(s.getBytes()); - fileData.putInt(am.assemblyInstructions.length); - fileData.putShort((short) am.numberOfIntsToCopy); - fileData.putShort((short) am.numberOfRSStringsToCopy); - fileData.putShort((short) am.numberOfIntArguments); - fileData.putShort((short) am.numberOfStringArguments); - int offset = fileData.position(); - if (am.switchHashTable != null) { - fileData.put((byte) am.switchHashTable.length); - for (HashTable sw : am.switchHashTable) { - fileData.putShort((short) sw.getBuckets().length); - for (Linkable singleSwitch : sw.getBuckets()) { - int l1 = (int) (singleSwitch.linkableKey); - int l2 = (int) (singleSwitch.linkableKey >> 32); - fileData.putInt(l1); - fileData.putInt(l2); - } - } - } - int end = fileData.position(); - // Finally, put the offset of the data - fileData.putShort((short) (end - offset)); - // And write binary file - fileOut.write(fileData.array(), 0, fileData.position()); - } catch (IOException ioException) { - System.out.println("Cannot write to file because " + ioException.getMessage()); - } - } - } - - /** - * All script methods called by the runAssembledMethod function are "main" methods. that is, they - * do not get called by other methods. The following function generates - * a list of main methods. - * - * So a method is either a "main" method or a "helper" method, and "helper" methods are never called - * To generate the list of main methods the following function can be used - */ - - // Here is the generated list: - public static final int[] MAIN_METHODS = {6, 7, 8, 9, 10, 11, 15, 16, 17, 18, 22, 23, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 36, 38, 40, 43, 44, 45, 48, 49, 50, 52, 53, 54, 58, 59, 60, 61, 63, 64, 65, 66, 67, 68, 69, 70, 73, 80, 81, 82, 83, 86, 87, 88, 92, 93, 94, 95, 96, 97, 99, 100, 101, 103, 104, 105, 106, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 122, 123, 124, 126, 127, 128, 130, 131, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 149, 150, 151, 152, 155, 156, 158, 159, 162, 163, 164, 165, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 182, 186, 187, 188, 189, 190, 191, 194, 195, 196, 199, 200, 201, 204, 205, 206, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 396, 397, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 521, 522, 524, 525, 526, 527, 529, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 544, 545, 546, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 560, 561, 562, 563, 564, 565, 566, 567, 568, 570, 571, 572, 574, 575, 579, 580, 581, 583, 585, 586, 587, 588, 589, 590, 591, 592, 595, 596, 597, 598, 599, 600, 601, 603, 604, 605, 606, 607, 608, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 629, 631, 633, 634, 635, 637, 639, 641, 643, 645, 647, 648, 649, 650, 653, 654, 655, 656, 663, 664, 665, 669, 675, 676, 677, 678, 681, 685, 687, 688, 690, 691, 692, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 707, 708, 709, 710, 712, 714, 715, 716, 717, 718, 720, 721, 724, 725, 726, 727, 728, 729, 731, 732, 733, 735, 740, 741, 742, 743, 745, 746, 747, 748, 749, 751, 752, 753, 754, 755, 756, 757, 758, 763, 764, 769, 770, 772, 774, 776, 777, 778, 779, 782, 783, 784, 786, 787, 789, 790, 791, 792, 796, 797, 800, 801, 803, 808, 809, 811, 812, 815, 816, 819, 820, 825, 826, 827, 828, 829, 830, 832, 833, 834, 835, 836, 838, 839, 840, 842, 843, 844, 845, 847, 848, 856, 857, 858, 859, 860, 862, 863, 864, 865, 866, 868, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 929, 930, 934, 937, 938, 939, 944, 945, 947, 948, 953, 954, 955, 956, 957, 958, 960, 961, 962, 963, 964, 965, 968, 969, 971, 972, 1026, 1027, 1028, 1031, 1035, 1038, 1040, 1043, 1045, 1046, 1048, 1049, 1053, 1055, 1056, 1057, 1058, 1059, 1060, 1065, 1067, 1070, 1073, 1075, 1080, 1081, 1082, 1083, 1085, 1087, 1091, 1092, 1093, 1094, 1099, 1101, 1102, 1103, 1104, 1105, 1107, 1109, 1110, 1111, 1117, 1122, 1123, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1153, 1154, 1155, 1157, 1158, 1160, 1161, 1164, 1166, 1167, 1168, 1170, 1172, 1173, 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1187, 1189, 1190, 1191, 1193, 1195, 1197, 1199, 1201, 1203, 1205, 1207, 1209, 1211, 1212, 1213, 1214, 1215, 1219, 1221, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1248, 1249, 1250, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1266, 1268, 1270, 1273, 1274, 1275, 1276, 1277, 1278, 1281, 1282, 1283, 1285, 1286, 1288, 1289, 1290, 1291, 1292, 1294, 1295, 1296, 1297, 1300, 1302, 1303, 1307, 1315, 1316, 1317, 1318, 1319, 1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1347, 1350, 1351, 1352, 1353, 1354, 1356, 1357, 1358, 1362, 1366, 1367, 1368, 1369, 1370, 1371, 1373, 1375, 1377, 1379, 1380, 1381, 1382, 1383, 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1410, 1411, 1412, 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1420, 1422, 1423, 1424, 1427, 1428, 1429, 1433, 1434, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, 1448, 1449, 1451, 1452, 1454, 1465, 1471, 1472, 1476, 1477, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1488, 1489, 1491, 1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, 1511, 1512, 1513, 1515, 1518, 1519, 1520, 1521, 1523, 1524, 1525, 1527, 1528, 1532, 1533, 1534, 1540, 1547, 1550, 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, 1563, 1565, 1567, 1569, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 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, 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, 1722, 1723, 1724, 1726, 1727, 1729, 1731, 1732, 1733, 1734, 1736, 1737, 1739, 1740, 1745, 1746, 1747, 1748, 1749, 1750, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, 1767, 1768, 1769, 1786, 1787, 1789, 1792, 1793, 1794, 1796, 1797, 1798, 1799, 1800, 1803, 1804, 1805, 1807, 1808, 1810, 1813, 1816, 1821, 1822, 1823, 1824, 1825, 1826, 1827, 1828, 1829, 1830, 1831, 1832, 1833, 1834, 1835, 1836, 1837, 1838, 1846, 1847, 1848, 1850, 1861, 1862, 1863, 1864, 1865, 1867, 1871, 1872, 1876, 1877, 1878, 1880, 1883, 1884, 1885, 1890, 1891, 1892, 1896, 1897, 1901, 1903, 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1920, 1921, 1922, 1923, 1924, 1925, 1926, 1930, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1951, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1970, 1972, 1973, 1974, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2040, 2041, 2043, 2044, 2045, 2053, 2054, 2055, 2057, 2058, 2060, 2061, 2062, 2063}; - - // A main method is defined as a method that is not called by any other methods, including recursively - public static ArrayList> AllMethodsCalled = new ArrayList<>(20); - - public static void GetAllMainMethods(String outputFile) { - // Build out all method calls - for (int i = 0; i < NUMBER_OF_METHODS; i++) { - AssembledMethod a = ItemDefinition.getMethodByID(i); - if (a == null) { - continue; - } - ArrayList ai = new ArrayList<>(20); - for (int j = 0; j < a.assemblyInstructions.length; j++) { - if (a.assemblyInstructions[j] == CS2AsmOpcodes.CALL.getOp() && !ai.contains(a.instructionOperands[j])) { - ai.add(a.instructionOperands[j]); - } - } - AllMethodsCalled.add(ai); - } - - try { - FileWriter myWriter = new FileWriter(outputFile); - myWriter.write("Main methods:\n"); - String methos = ""; - for (int i = 0; i < NUMBER_OF_METHODS; i++) { - boolean isMain = true; - end: - for (ArrayList j : AllMethodsCalled) { - for (Integer k : j) { - if (i == k) { - isMain = false; - break end; - } - } - } - if (isMain) { - methos = methos + ", " + i; - myWriter.write("Method" + i + "\n"); - } - } - myWriter.write("\n\n" + methos); - myWriter.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - - /** - * Dumps the method arguments to a file for all methods, as well as a graph of their method calls and on what - * lines those calls occur. - */ - - public static void DumpAllGameMethodArguments(String outputFile) { - try { - FileWriter myWriter = new FileWriter(outputFile); - myWriter.write("; Game method metadata\n;\n"); - for (int l = 0; l < Integer.MAX_VALUE; l++) { - AssembledMethod a = ItemDefinition.getMethodByID(l); - if (a == null) { - myWriter.close(); - break; - } else { - myWriter.write(";Method" + l + " ("); - int i = 0; - for (int j = 0; j < a.numberOfIntArguments; j++) { - i++; - myWriter.write("int var" + i); - if (j < a.numberOfIntArguments - 1 || a.numberOfStringArguments != 0) { - myWriter.write(", "); - } - } - for (int j = 0; j < a.numberOfStringArguments; j++) { - i++; - myWriter.write("string var" + i); - if (j < a.numberOfStringArguments - 1) { - myWriter.write(", "); - } - } - myWriter.write(")\n"); - GetMethodDependencyGraph(myWriter, l); - } - } - } catch (IOException e) { - System.out.println("An error occurred writing the file: " + outputFile); - e.printStackTrace(); - } - } - - /** - * Records for each method, all the methods that it calls, recursively. Used internally by other functions. - */ - private static int treeDepth = 0; - private static final ArrayList tree = new ArrayList<>(20); - - private static void GetMethodDependencyGraph(FileWriter f, int methodID) throws IOException { - AssembledMethod am = ItemDefinition.getMethodByID(methodID); - for (int i = 0; i < treeDepth; i++) { - f.write('\t'); - } - f.write("Method" + methodID + "\n"); - if (am == null) { - f.write(" Null??"); - return; - } - treeDepth++; - tree.add(methodID); - for (int i = 0; i < am.assemblyInstructions.length; i++) { - if (am.assemblyInstructions[i] == CS2AsmOpcodes.CALL.getOp()) { - f.write(i + ":"); - if (tree.contains(am.instructionOperands[i])) { - treeDepth++; - for (int j = 0; j < treeDepth; j++) { - f.write('\t'); - } - f.write("RECURSIVE Method" + methodID); - treeDepth--; - } else { - GetMethodDependencyGraph(f, am.instructionOperands[i]); - } - } - } - tree.remove(tree.size() - 1); - treeDepth--; - } - - public static void PrintAllValidGameMethods() { - for (int l = -5000; l < 5000; l++) { - AssembledMethod a = ItemDefinition.getMethodByID(l); - if (a == null) { - continue; - } - System.out.println("Valid methodID: " + l); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Envelope.java b/Client/src/main/java/org/runite/client/Envelope.java deleted file mode 100644 index 6a2ad479a..000000000 --- a/Client/src/main/java/org/runite/client/Envelope.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.runite.client; - -final class Envelope { - - int waveTable; - int minInterval; - int maxInterval; - private int stages = 2; - private int[] levels = new int[2]; - private int[] times = new int[2]; - private int phaseIndex; - private int level; - private int slope; - private int nextTime; - private int time; - - public Envelope() { - this.times[0] = 0; - this.times[1] = 65535; - this.levels[0] = 0; - this.levels[1] = 65535; - } - - final int nextLevel(int duration) { - if (this.time >= this.nextTime) { - this.level = this.levels[this.phaseIndex++] << 15; - if (this.phaseIndex >= this.stages) { - this.phaseIndex = this.stages - 1; - } - - this.nextTime = (int) ((double) this.times[this.phaseIndex] / 65536.0D * (double) duration); - if (this.nextTime > this.time) { - this.slope = ((this.levels[this.phaseIndex] << 15) - this.level) / (this.nextTime - this.time); - } - } - - this.level += this.slope; - ++this.time; - return this.level - this.slope >> 15; - } - - final void reset() { - this.nextTime = 0; - this.phaseIndex = 0; - this.slope = 0; - this.level = 0; - this.time = 0; - } - - final void decodeStages(DataBuffer buffer) { - this.stages = buffer.readUnsignedByte(); - this.times = new int[this.stages]; - this.levels = new int[this.stages]; - - for (int var2 = 0; var2 < this.stages; ++var2) { - this.times[var2] = buffer.readUnsignedShort(); - this.levels[var2] = buffer.readUnsignedShort(); - } - } - - final void decode(DataBuffer buffer) { - this.waveTable = buffer.readUnsignedByte(); - this.minInterval = buffer.readInt(); - this.maxInterval = buffer.readInt(); - this.decodeStages(buffer); - } -} diff --git a/Client/src/main/java/org/runite/client/Filter.java b/Client/src/main/java/org/runite/client/Filter.java deleted file mode 100644 index 44224a734..000000000 --- a/Client/src/main/java/org/runite/client/Filter.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.runite.client; - -final class Filter { - - private static final float[][] aFloatArrayArray1431 = new float[2][8]; - private static float aFloat1433; - private final int[][][] anIntArrayArrayArray1430 = new int[2][2][4]; - private final int[][][] anIntArrayArrayArray1432 = new int[2][2][4]; - private final int[] anIntArray1437 = new int[2]; - int[] pairs = new int[2]; - - private static float method1621(float var0) { - float var1 = 32.703197F * (float) Math.pow(2.0D, var0); - return var1 * 3.1415927F / 11025.0F; - } - - final void decode(DataBuffer var1, Envelope var2) { - int var3 = var1.readUnsignedByte(); - this.pairs[0] = var3 >> 4; - this.pairs[1] = var3 & 15; - if (var3 == 0) { - this.anIntArray1437[0] = this.anIntArray1437[1] = 0; - } else { - this.anIntArray1437[0] = var1.readUnsignedShort(); - this.anIntArray1437[1] = var1.readUnsignedShort(); - int var4 = var1.readUnsignedByte(); - - int var5; - int var6; - for (var5 = 0; var5 < 2; ++var5) { - for (var6 = 0; var6 < this.pairs[var5]; ++var6) { - this.anIntArrayArrayArray1430[var5][0][var6] = var1.readUnsignedShort(); - this.anIntArrayArrayArray1432[var5][0][var6] = var1.readUnsignedShort(); - } - } - - for (var5 = 0; var5 < 2; ++var5) { - for (var6 = 0; var6 < this.pairs[var5]; ++var6) { - if ((var4 & 1 << var5 * 4 << var6) == 0) { - this.anIntArrayArrayArray1430[var5][1][var6] = this.anIntArrayArrayArray1430[var5][0][var6]; - this.anIntArrayArrayArray1432[var5][1][var6] = this.anIntArrayArrayArray1432[var5][0][var6]; - } else { - this.anIntArrayArrayArray1430[var5][1][var6] = var1.readUnsignedShort(); - this.anIntArrayArrayArray1432[var5][1][var6] = var1.readUnsignedShort(); - } - } - } - - if (var4 != 0 || this.anIntArray1437[1] != this.anIntArray1437[0]) { - var2.decodeStages(var1); - } - } - - } - - final int compute(int var1, float var2) { - float var3; - if (var1 == 0) { - var3 = (float) this.anIntArray1437[0] + (float) (this.anIntArray1437[1] - this.anIntArray1437[0]) * var2; - var3 *= 0.0030517578F; - aFloat1433 = (float) Math.pow(0.1D, var3 / 20.0F); - SynthInstrument.inverseA0 = (int) (aFloat1433 * 65536.0F); - } - - if (this.pairs[var1] == 0) { - return 0; - } else { - var3 = this.method1624(var1, 0, var2); - aFloatArrayArray1431[var1][0] = -2.0F * var3 * (float) Math.cos(this.method1625(var1, 0, var2)); - aFloatArrayArray1431[var1][1] = var3 * var3; - - int var4; - for (var4 = 1; var4 < this.pairs[var1]; ++var4) { - var3 = this.method1624(var1, var4, var2); - float var5 = -2.0F * var3 * (float) Math.cos(this.method1625(var1, var4, var2)); - float var6 = var3 * var3; - aFloatArrayArray1431[var1][var4 * 2 + 1] = aFloatArrayArray1431[var1][var4 * 2 - 1] * var6; - aFloatArrayArray1431[var1][var4 * 2] = aFloatArrayArray1431[var1][var4 * 2 - 1] * var5 + aFloatArrayArray1431[var1][var4 * 2 - 2] * var6; - - for (int var7 = var4 * 2 - 1; var7 >= 2; --var7) { - aFloatArrayArray1431[var1][var7] += aFloatArrayArray1431[var1][var7 - 1] * var5 + aFloatArrayArray1431[var1][var7 - 2] * var6; - } - - aFloatArrayArray1431[var1][1] += aFloatArrayArray1431[var1][0] * var5 + var6; - aFloatArrayArray1431[var1][0] += var5; - } - - if (var1 == 0) { - for (var4 = 0; var4 < this.pairs[0] * 2; ++var4) { - aFloatArrayArray1431[0][var4] *= aFloat1433; - } - } - - for (var4 = 0; var4 < this.pairs[var1] * 2; ++var4) { - SynthInstrument.coefficency[var1][var4] = (int) (aFloatArrayArray1431[var1][var4] * 65536.0F); - } - - return this.pairs[var1] * 2; - } - } - - private float method1624(int var1, int var2, float var3) { - float var4 = (float) this.anIntArrayArrayArray1432[var1][0][var2] + var3 * (float) (this.anIntArrayArrayArray1432[var1][1][var2] - this.anIntArrayArrayArray1432[var1][0][var2]); - var4 *= 0.0015258789F; - return 1.0F - (float) Math.pow(10.0D, -var4 / 20.0F); - } - - private float method1625(int var1, int var2, float var3) { - float var4 = (float) this.anIntArrayArrayArray1430[var1][0][var2] + var3 * (float) (this.anIntArrayArrayArray1430[var1][1][var2] - this.anIntArrayArrayArray1430[var1][0][var2]); - var4 *= 1.2207031E-4F; - return method1621(var4); - } - -} diff --git a/Client/src/main/java/org/runite/client/FloorOverlayDefinition.java b/Client/src/main/java/org/runite/client/FloorOverlayDefinition.java deleted file mode 100644 index b93104ebc..000000000 --- a/Client/src/main/java/org/runite/client/FloorOverlayDefinition.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.runite.client; - -final class FloorOverlayDefinition { - - int anInt2090 = 128; - boolean aBoolean2092 = false; - boolean aBoolean2093 = true; - int anInt2094 = 1190717; - int anInt2095 = -1; - int anInt2098 = -1; - int anInt2100 = 8; - int anInt2101 = 16; - boolean aBoolean2102 = true; - int anInt2103 = 0; - - static FloorOverlayDefinition getFile(int var1) { - try { - FloorOverlayDefinition var2 = (FloorOverlayDefinition) Class163_Sub2_Sub1.aReferenceCache_4015.get(var1); - if (null == var2) { - System.out.println("Retrieving file " + var1); - byte[] var4 = Class3_Sub28_Sub5.aClass153_3580.getFile(4, var1); - var2 = new FloorOverlayDefinition(); - if (var4 != null) { - var2.parseDefinition(new DataBuffer(var4), var1); - } - - Class163_Sub2_Sub1.aReferenceCache_4015.put(var2, var1); - } - return var2; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "um.C(" + ',' + var1 + ')'); - } - } - - static void method1014(int var1, int var2) { - try { - - for (int var4 = 0; var4 < Client.anInt869; ++var4) { - FloorOverlayDefinition var5 = getFile(var4); - if (null != var5) { - int var6 = var5.anInt2095; - if (0 <= var6 && !Class51.anInterface2_838.method17(var6, 126)) { - var6 = -1; - } - - int var7; - int var8; - int var9; - int var10; - if (var5.anInt2098 < 0) { - if (var6 >= 0) { - var7 = Class51.anIntArray834[LinkableRSString.method729((byte) -74, Class51.anInterface2_838.method15(var6, 65535), 96)]; - } else if (-1 == var5.anInt2103) { - var7 = -1; - } else { - var8 = var5.anInt2103; - var9 = var1 + (var8 & 127); - if (var9 < 0) { - var9 = 0; - } else if (var9 > 127) { - var9 = 127; - } - - var10 = var9 + (896 & var8) + ('\ufc00' & var8 + var2); - var7 = Class51.anIntArray834[LinkableRSString.method729((byte) -127, var10, 96)]; - } - } else { - var8 = var5.anInt2098; - var9 = (127 & var8) + var1; - if (var9 < 0) { - var9 = 0; - } else if (var9 > 127) { - var9 = 127; - } - - var10 = (896 & var8) + ('\ufc00' & var2 + var8) + var9; - var7 = Class51.anIntArray834[LinkableRSString.method729((byte) -63, var10, 96)]; - } - - Class83.anIntArray1161[1 + var4] = var7; - } - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "fi.B(" + -120 + ',' + var1 + ',' + var2 + ')'); - } - } - - static void method631(CacheIndex var1) { - try { - Class3_Sub28_Sub5.aClass153_3580 = var1; - Client.anInt869 = Class3_Sub28_Sub5.aClass153_3580.getFileAmount(4); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "qc.D(" + false + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - final void parseDefinition(DataBuffer var2, int var3) { - try { - while (true) { - int var4 = var2.readUnsignedByte(); - if (var4 == 0) { - - return; - } - - this.decode(var4, var2, var3); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "wl.H(" + 24559 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ')'); - } - } - - private void decode(int opcode, DataBuffer buffer, int var4) { - try { - if (opcode == 1) { - this.anInt2103 = RSInterface.method869(buffer.readMedium()); - } else if (opcode == 2) { - this.anInt2095 = buffer.readUnsignedByte(); - } else if (3 == opcode) { - this.anInt2095 = buffer.readUnsignedShort(); - if (this.anInt2095 == 65535) { - this.anInt2095 = -1; - } - } else if (5 == opcode) { - this.aBoolean2102 = false; - } else if (opcode == 7) { - this.anInt2098 = RSInterface.method869(buffer.readMedium()); - } else if (opcode == 8) { - TextureOperation26.anInt3081 = var4; - } else if (opcode == 9) { - this.anInt2090 = buffer.readUnsignedShort(); - } else if (opcode == 10) { - this.aBoolean2093 = false; - } else if (opcode == 11) { - this.anInt2100 = buffer.readUnsignedByte(); - } else if (12 == opcode) { - this.aBoolean2092 = true; - } else if (13 == opcode) { - this.anInt2094 = buffer.readMedium(); - } else if (opcode == 14) { - this.anInt2101 = buffer.readUnsignedByte(); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "wl.E(" + 0 + ',' + opcode + ',' + (buffer != null ? "{...}" : "null") + ',' + var4 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/FloorUnderlayDefinition.java b/Client/src/main/java/org/runite/client/FloorUnderlayDefinition.java deleted file mode 100644 index a622f61a9..000000000 --- a/Client/src/main/java/org/runite/client/FloorUnderlayDefinition.java +++ /dev/null @@ -1,152 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -final class FloorUnderlayDefinition { - - int anInt1406; - int anInt1408; - boolean aBoolean1411 = true; - int anInt1412 = -1; - int anInt1414 = 128; - int anInt1417; - int anInt1418; - - static FloorUnderlayDefinition method629(int var1) { - try { - FloorUnderlayDefinition var2 = (FloorUnderlayDefinition)Class44.aReferenceCache_725.get(var1); - if(var2 == null) { - byte[] var3 = Class3_Sub23.aClass153_2536.getFile(1, var1); - var2 = new FloorUnderlayDefinition(); - if(null != var3) { - var2.parseUnderlayDefinition(var1, new DataBuffer(var3)); - } - - Class44.aReferenceCache_725.put(var2, var1); - return var2; - } else { - return var2; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "qc.B(" + true + ',' + var1 + ')'); - } - } - - final void parseUnderlayDefinition(int var1, DataBuffer buffer) { - try { - while (true) { - int opcode = buffer.readUnsignedByte(); - if (opcode == 0) { - - return; - } - - this.parseOpcode(opcode, buffer, var1); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ni.F(" + var1 + ',' + (buffer != null ? "{...}" : "null") + ',' + 255 + ')'); - } - } - - private void parseOpcode(int opcode, DataBuffer buffer, int var4) { - try { - if (opcode == 1) { - int underlayColor = buffer.readMedium(); - this.method1600(underlayColor); - } else if (opcode == 2) { - this.anInt1412 = buffer.readUnsignedShort(); - if (this.anInt1412 == 65535) { - this.anInt1412 = -1; - } - } else if (3 == opcode) { - this.anInt1414 = buffer.readUnsignedShort(); - } else if (opcode == 4) { - this.aBoolean1411 = false; - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ni.E(" + (byte) -52 + ',' + opcode + ',' + (buffer != null ? "{...}" : "null") + ',' + var4 + ')'); - } - } - - private void method1600(int rgbAsInt) { - try { - double red = (double) (255 & rgbAsInt >> 16) / 256.0D; - double green = (double) (255 & rgbAsInt >> 8) / 256.0D; - double var9 = red; - double blue = (double) (rgbAsInt & 0xFF) / 256.0D; - if (green < red) { - var9 = green; - } - - if (blue < var9) { - var9 = blue; - } - - double var11 = red; - double var14 = 0.0D; - if (green > red) { - var11 = green; - } - - if (blue > var11) { - var11 = blue; - } - - double var16 = 0.0D; - double var18 = (var11 + var9) / 2.0D; - if (var9 != var11) { - if (0.5D > var18) { - var16 = (var11 - var9) / (var11 + var9); - } - - if (var11 == red) { - var14 = (-blue + green) / (-var9 + var11); - } else if (green == var11) { - var14 = (blue - red) / (var11 - var9) + 2.0D; - } else if (blue == var11) { - var14 = 4.0D + (-green + red) / (-var9 + var11); - } - - if (0.5D <= var18) { - var16 = (var11 - var9) / (-var9 + (2.0D - var11)); - } - } - - if (var18 > 0.5D) { - this.anInt1418 = (int) (var16 * (-var18 + 1.0D) * 512.0D); - } else { - this.anInt1418 = (int) (var16 * var18 * 512.0D); - } - - if (1 > this.anInt1418) { - this.anInt1418 = 1; - } - - this.anInt1406 = (int) (var16 * 256.0D); - this.anInt1417 = (int) (256.0D * var18); - - if (this.anInt1417 >= 0) { - if ((GameConfig.CHRISTMAS_EVENT_ENABLED ? -255 : 255) < this.anInt1417) {//Underlay white transformation - - this.anInt1417 = 255; - } - } else { - this.anInt1417 = 0; - } - - var14 /= 6.0D; - this.anInt1408 = (int) ((double) this.anInt1418 * var14); - if (this.anInt1406 >= 0) { - if ((GameConfig.CHRISTMAS_EVENT_ENABLED ? -255 : 255) < this.anInt1406) {//Underlay white transformation - - this.anInt1406 = 255; - } - } else { - this.anInt1406 = 0; - } - - } catch (RuntimeException var20) { - throw ClientErrorException.clientError(var20, "ni.D(" + rgbAsInt + ',' + (byte) 81 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Font.java b/Client/src/main/java/org/runite/client/Font.java deleted file mode 100644 index 5364e87c7..000000000 --- a/Client/src/main/java/org/runite/client/Font.java +++ /dev/null @@ -1,980 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; -import org.rs09.client.rendering.Toolkit; - -import java.util.Objects; -import java.util.Random; - -public abstract class Font extends Node { - - private static final RSString[] aClass94Array3753 = new RSString[100]; - private static final RSString aClass94_3745 = Unsorted.emptyString(100); - - private static int anInt3746 = -1; - private static int anInt3747 = -1; - private static int anInt3748 = 0; - private static int anInt3749 = 0; - private static int anInt3750 = 256; - private static int anInt3751 = 256; - private static int anInt3752 = -1; - private static int anInt3754 = -1; - private static int anInt3755 = 0; - private static int anInt3756 = 0; - - int[] anIntArray3709; - int[] anIntArray3721; - int anInt3727 = 0; - private int anInt3710; - private int[] anIntArray3719; - private int anInt3722; - private int[] anIntArray3723; - private int[] anIntArray3730; - private byte[] aByteArray3733; - private int[] anIntArray3736; - private AbstractIndexedSprite[] aClass109Array3740; - - - Font(byte[] var1, int[] var2, int[] var3, int[] var4, int[] var5) { - this.anIntArray3719 = var2; - this.anIntArray3730 = var3; - this.anIntArray3709 = var4; - this.anIntArray3721 = var5; - this.method700(var1); - int var6 = Integer.MAX_VALUE; - int var7 = Integer.MIN_VALUE; - - for (int var8 = 0; var8 < 256; ++var8) { - if (this.anIntArray3730[var8] < var6 && this.anIntArray3721[var8] != 0) { - var6 = this.anIntArray3730[var8]; - } - - if (this.anIntArray3730[var8] + this.anIntArray3721[var8] > var7) { - var7 = this.anIntArray3730[var8] + this.anIntArray3721[var8]; - } - } - - this.anInt3722 = this.anInt3727 - var6; - this.anInt3710 = var7 - this.anInt3727; - } - - Font(byte[] var1) { - this.method700(var1); - } - - static RSString method686(RSString var0) { - int var1 = var0.length(); - int var2 = 0; - - int var4; - for (int var3 = 0; var3 < var1; ++var3) { - var4 = var0.charAt(var3, (byte) 50); - if (var4 == 60 || var4 == 62) { - var2 += 3; - } - } - - RSString var6 = Unsorted.emptyString(var1 + var2); - - for (var4 = 0; var4 < var1; ++var4) { - int var5 = var0.charAt(var4, (byte) -101); - if (var5 == 60) { - Objects.requireNonNull(var6).append(RSString.parse("")); - } else if (var5 == 62) { - Objects.requireNonNull(var6).append(RSString.parse("")); - } else { - Objects.requireNonNull(var6).appendCharacter(var5); - } - } - - return var6; - } - - private static int method694(byte[][] var0, byte[][] var1, int[] var2, int[] var3, int[] var4, int var5, int var6) { - int var7 = var2[var5]; - int var8 = var7 + var4[var5]; - int var9 = var2[var6]; - int var10 = var9 + var4[var6]; - int var11 = var7; - if (var9 > var7) { - var11 = var9; - } - - int var12 = var8; - if (var10 < var8) { - var12 = var10; - } - - int var13 = var3[var5]; - if (var3[var6] < var13) { - var13 = var3[var6]; - } - - byte[] var14 = var1[var5]; - byte[] var15 = var0[var6]; - int var16 = var11 - var7; - int var17 = var11 - var9; - - for (int var18 = var11; var18 < var12; ++var18) { - int var19 = var14[var16++] + var15[var17++]; - if (var19 < var13) { - var13 = var19; - } - } - - return -var13; - } - - public final void method676(RSString var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10) { - this.method693(var1, var2, var3, var4, var5, var6, var7, var8, var9, var10); - } - - private void method677(RSString var1, int var2, int var3) { - var3 -= this.anInt3727; - int var4 = -1; - int var5 = 0; - int var6 = var1.length(); - - for (int var7 = 0; var7 < var6; ++var7) { - int var8 = var1.charAt(var7, (byte) 72); - if (var8 == 60) { - var4 = var7; - } else { - int var10; - if (var8 == 62 && var4 != -1) { - RSString var9 = var1.substring(var4 + 1, var7, 0); - var4 = -1; - if (var9.equalsString(RSString.parse("lt"))) { - var8 = 60; - } else if (var9.equalsString(RSString.parse("gt"))) { - var8 = 62; - } else if (var9.equalsString(RSString.parse("nbsp"))) { - var8 = 160; - } else if (var9.equalsString(RSString.parse("shy"))) { - var8 = 173; - } else if (var9.equalsString(RSString.parse("times"))) { - var8 = 215; - } else if (var9.equalsString(RSString.parse("euro"))) { - var8 = 128; - } else if (var9.equalsString(RSString.parse("copy"))) { - var8 = 169; - } else { - if (!var9.equalsString(RSString.parse("reg"))) { - if (var9.startsWith(RSString.parse("img="))) { - try {//TODO: Chat image?... - var10 = var9.substring(4).parseInt(); - AbstractIndexedSprite var15 = this.aClass109Array3740[var10]; - int var12 = this.anIntArray3723 != null ? this.anIntArray3723[var10] : var15.anInt1467; - if (anInt3750 == 256) { - var15.method1667(var2, var3 + this.anInt3727 - var12); - } else { - var15.method1666(var2, var3 + this.anInt3727 - var12, anInt3750); - } - - var2 += var15.anInt1469; - var5 = 0; - } catch (Exception var13) { - if (!var13.toString().contains("java.lang.ArrayIndexOutOfBoundsException")) { - // TODO Added by Tech to figure out the weird issue - System.out.println(var13.toString() + ", full string: '" + var9.toString() + "'"); - var13.printStackTrace(); - } - } - } else { - this.method685(var9); - } - continue; - } - - var8 = 174; - } - } - - if (var4 == -1) { - if (this.aByteArray3733 != null && var5 != 0) { - var2 += this.aByteArray3733[(var5 << 8) + var8]; - } - - int var14 = this.anIntArray3709[var8]; - var10 = this.anIntArray3721[var8]; - if (var8 != 32) { - if (anInt3750 == 256) { - if (anInt3754 != -1) { - this.method678(var8, var2 + this.anIntArray3719[var8] + 1, var3 + this.anIntArray3730[var8] + 1, var14, var10, anInt3754); - } - - this.method678(var8, var2 + this.anIntArray3719[var8], var3 + this.anIntArray3730[var8], var14, var10, anInt3755); - } else { - if (anInt3754 != -1) { - this.method679(var8, var2 + this.anIntArray3719[var8] + 1, var3 + this.anIntArray3730[var8] + 1, var14, var10, anInt3754, anInt3750); - } - - this.method679(var8, var2 + this.anIntArray3719[var8], var3 + this.anIntArray3730[var8], var14, var10, anInt3755, anInt3750); - } - } else if (anInt3756 > 0) { - anInt3748 += anInt3756; - var2 += anInt3748 >> 8; - anInt3748 &= 255; - } - - int var11 = this.anIntArray3736[var8]; - if (anInt3746 != -1) { - Toolkit.getActiveToolkit().drawHorizontalLine(var2, var3 + (int) ((double) this.anInt3727 * 0.7D), var11, anInt3746); - } - - if (anInt3747 != -1) { - Toolkit.getActiveToolkit().drawHorizontalLine(var2, var3 + this.anInt3727 + 1, var11, anInt3747); - } - - var2 += var11; - var5 = var8; - } - } - } - - } - - abstract void method678(int var1, int var2, int var3, int var4, int var5, int var6); - - abstract void method679(int var1, int var2, int var3, int var4, int var5, int var6, int var7); - - public final int method680(RSString var1, int var2) { - int var3 = this.method691(var1, new int[]{var2}, aClass94Array3753); - int var4 = 0; - - for (int var5 = 0; var5 < var3; ++var5) { - int var6 = this.method682(aClass94Array3753[var5]); - if (var6 > var4) { - var4 = var6; - } - } - - return var4; - } - - public final void method681(RSString var1, int var2, int var3, int var4, int var5) { - if (var1 != null) { - this.method698(var4, var5); - this.method677(var1, var2, var3); - } - } - - public final int method682(RSString var1) { - if (var1 == null) { - return 0; - } else { - int var2 = -1; - int var3 = 0; - int var4 = 0; - int var5 = var1.length(); - - for (int var6 = 0; var6 < var5; ++var6) { - int var7 = var1.charAt(var6, (byte) -45); - if (var7 == 60) { - var2 = var6; - } else { - if (var7 == 62 && var2 != -1) { - RSString var8 = var1.substring(var2 + 1, var6, 0); - var2 = -1; - if (var8.equalsString(RSString.parse("lt"))) { - var7 = 60; - } else if (var8.equalsString(RSString.parse("gt"))) { - var7 = 62; - } else if (var8.equalsString(RSString.parse("nbsp"))) { - var7 = 160; - } else if (var8.equalsString(RSString.parse("shy"))) { - var7 = 173; - } else if (var8.equalsString(RSString.parse("times"))) { - var7 = 215; - } else if (var8.equalsString(RSString.parse("euro"))) { - var7 = 128; - } else if (var8.equalsString(RSString.parse("copy"))) { - var7 = 169; - } else { - if (!var8.equalsString(RSString.parse("reg"))) { - if (var8.startsWith(RSString.parse("img="))) { - try {//No clue - int var9 = var8.substring(4).parseInt(); - var4 += this.aClass109Array3740[var9].anInt1469; - var3 = 0; - } catch (Exception var10) { - } - } - continue; - } - - var7 = 174; - } - } - - if (var2 == -1) { - var4 += this.anIntArray3736[var7]; - if (this.aByteArray3733 != null && var3 != 0) { - var4 += this.aByteArray3733[(var3 << 8) + var7]; - } - - var3 = var7; - } - } - } - - return var4; - } - } - - final int method683(RSString var1, int var2, int var3, Random var6, int var7) { - if (var1 == null) { - return 0; - } else { - var6.setSeed(var7); - this.method701(16777215, 0, 192 + (var6.nextInt() & 31)); - int var8 = var1.length(); - int[] var9 = new int[var8]; - int var10 = 0; - - for (int var11 = 0; var11 < var8; ++var11) { - var9[var11] = var10; - if ((var6.nextInt() & 3) == 0) { - ++var10; - } - } - - this.method703(var1, var2, var3, var9, null); - return var10; - } - } - - public final int method684(RSString var1, int var2) { - return this.method691(var1, new int[]{var2}, aClass94Array3753); - } - - private void method685(RSString var1) { - try { - if (var1.startsWith(RSString.parse("col="))) { - anInt3755 = var1.substring(4).parseInt(16); - } else if (var1.equalsString(RSString.parse(")4col"))) { - anInt3755 = anInt3749; - } else if (var1.startsWith(RSString.parse("trans="))) { - anInt3750 = var1.substring(6).parseInt(); - } else if (var1.equalsString(RSString.parse(")4trans"))) { - anInt3750 = anInt3751; - } else if (var1.startsWith(RSString.parse("str="))) { - anInt3746 = var1.substring(4).parseInt(16); - } else if (var1.equalsString(RSString.parse("str"))) { - anInt3746 = 8388608; - } else if (var1.equalsString(RSString.parse(")4str"))) { - anInt3746 = -1; - } else if (var1.startsWith(RSString.parse("u="))) { - anInt3747 = var1.substring(2).parseInt(16); - } else if (var1.equalsString(RSString.parse("u"))) { - anInt3747 = 0; - } else if (var1.equalsString(RSString.parse(")4u"))) { - anInt3747 = -1; - } else if (var1.startsWith(RSString.parse("shad="))) { - anInt3754 = var1.substring(5).parseInt(16); - } else if (var1.equalsString(RSString.parse("shad"))) { - anInt3754 = 0; - } else if (var1.equalsString(RSString.parse(")4shad"))) { - anInt3754 = anInt3752; - } else if (var1.equalsString(RSString.parse("br"))) { - this.method701(anInt3749, anInt3752, anInt3751); - } - } catch (Exception var3) { - } - - } - - private int method687(int var1) { - return this.anIntArray3736[var1 & 0xFF]; - } - - final void drawStringRightAnchor(RSString var1, int var2, int var3, int var4, int var5) { - if (var1 != null) { - this.method698(var4, var5); - this.method677(var1, var2 - this.method682(var1), var3); - } - } - - private void method690(RSString var1, int var2) { - int var3 = 0; - boolean var4 = false; - int var5 = var1.length(); - - for (int var6 = 0; var6 < var5; ++var6) { - int var7 = var1.charAt(var6, (byte) 75); - if (var7 == 60) { - var4 = true; - } else if (var7 == 62) { - var4 = false; - } else if (!var4 && var7 == 32) { - ++var3; - } - } - - if (var3 > 0) { - anInt3756 = (var2 - this.method682(var1) << 8) / var3; - } - - } - - final int method691(RSString var1, int[] var2, RSString[] var3) { - if (var1 == null) { - return 0; - } else { - aClass94_3745.method1553(0); - int var4 = 0; - int var5 = 0; - int var6 = -1; - int var7 = 0; - byte var8 = 0; - int var9 = -1; - int var10 = 0; - int var11 = 0; - int var12 = var1.length(); - - for (int var13 = 0; var13 < var12; ++var13) { - int var14 = var1.charAt(var13, (byte) -30); - if (var14 == 60) { - var9 = var13; - } else { - if (var14 == 62 && var9 != -1) { - RSString var15 = var1.substring(var9 + 1, var13, 0); - var9 = -1; - aClass94_3745.appendCharacter(60); - aClass94_3745.append(var15); - aClass94_3745.appendCharacter(62); - if (var15.equalsString(RSString.parse("br"))) { - if (var3[var11] == null) { - var3[var11] = aClass94_3745.method1563(101).substring(var5, aClass94_3745.length(), 0); - } else { - var3[var11].method1553(0); - var3[var11].method1542(aClass94_3745, var5, aClass94_3745.length()); - } - - ++var11; - var5 = aClass94_3745.length(); - var4 = 0; - var6 = -1; - var10 = 0; - } else if (var15.equalsString(RSString.parse("lt"))) { - var4 += this.method687(60); - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + 60]; - } - - var10 = 60; - } else if (var15.equalsString(RSString.parse("gt"))) { - var4 += this.method687(62); - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + 62]; - } - - var10 = 62; - } else if (var15.equalsString(RSString.parse("nbsp"))) { - var4 += this.method687(160); - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + 160]; - } - - var10 = 160; - } else if (var15.equalsString(RSString.parse("shy"))) { - var4 += this.method687(173); - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + 173]; - } - - var10 = 173; - } else if (var15.equalsString(RSString.parse("times"))) { - var4 += this.method687(215); - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + 215]; - } - - var10 = 215; - } else if (var15.equalsString(RSString.parse("euro"))) { - var4 += this.method687(128); - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + 128]; - } - - var10 = 128; - } else if (var15.equalsString(RSString.parse("copy"))) { - var4 += this.method687(169); - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + 169]; - } - - var10 = 169; - } else if (var15.equalsString(RSString.parse("reg"))) { - var4 += this.method687(174); - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + 174]; - } - - var10 = 174; - } else if (var15.startsWith(RSString.parse("img="))) { - try { - int var16 = var15.substring(4).parseInt(); - var4 += this.aClass109Array3740[var16].anInt1469; - var10 = 0; - } catch (Exception var17) { - } - } - - var14 = 0; - } - - if (var9 == -1) { - if (var14 != 0) { - aClass94_3745.appendCharacter(var14); - var4 += this.anIntArray3736[var14]; - if (this.aByteArray3733 != null && var10 != 0) { - var4 += this.aByteArray3733[(var10 << 8) + var14]; - } - - var10 = var14; - } - - if (var14 == 32) { - var6 = aClass94_3745.length(); - var7 = var4; - var8 = 1; - } - - if (var2 != null && var4 > var2[var11 < var2.length ? var11 : var2.length - 1] && var6 >= 0) { - if (var3[var11] == null) { - var3[var11] = aClass94_3745.method1563(88).substring(var5, var6 - var8, 0); - } else { - var3[var11].method1553(0); - var3[var11] = var3[var11].method1542(aClass94_3745, var5, var6 - var8); - } - - ++var11; - var5 = var6; - var6 = -1; - var4 -= var7; - var10 = 0; - } - - if (var14 == 45) { - var6 = aClass94_3745.length(); - var7 = var4; - var8 = 0; - } - } - } - } - - if (aClass94_3745.length() > var5) { - if (var3[var11] == null) { - var3[var11] = aClass94_3745.method1563(94).substring(var5, aClass94_3745.length(), 0); - } else { - var3[var11].method1553(0); - var3[var11] = var3[var11].method1542(aClass94_3745, var5, aClass94_3745.length()); - } - - ++var11; - } - - return var11; - } - } - - final void method692(RSString var1, int var2, int var3, int var4, int var6, int var7) { - if (var1 != null) { - this.method698(var4, 0); - double var8 = 7.0D - (double) var7 / 8.0D; - if (var8 < 0.0D) { - var8 = 0.0D; - } - - int var10 = var1.length(); - int[] var11 = new int[var10]; - - for (int var12 = 0; var12 < var10; ++var12) { - var11[var12] = (int) (Math.sin((double) var12 / 1.5D + (double) var6 / 1.0D) * var8); - } - - this.method703(var1, var2 - this.method682(var1) / 2, var3, null, var11); - } - } - - public final int method693(RSString var1, int var2, int var3, int var4, int var5, int var6, int var7, int var9, int var10, int var11) { - if (var1 == null) { - return 0; - } else { - this.method701(var6, var7, 256); - if (var11 == 0) { - var11 = this.anInt3727; - } - - int[] var12 = new int[]{var4}; - if (var5 < this.anInt3722 + this.anInt3710 + var11 && var5 < var11 + var11) { - var12 = null; - } - - int var13 = this.method691(var1, var12, aClass94Array3753); - if (var10 == 3 && var13 == 1) { - var10 = 1; - } - - int var14; - int var15; - if (var10 == 0) { - var14 = var3 + this.anInt3722; - } else if (var10 == 1) { - var14 = var3 + this.anInt3722 + (var5 - this.anInt3722 - this.anInt3710 - (var13 - 1) * var11) / 2; - } else if (var10 == 2) { - var14 = var3 + var5 - this.anInt3710 - (var13 - 1) * var11; - } else { - var15 = (var5 - this.anInt3722 - this.anInt3710 - (var13 - 1) * var11) / (var13 + 1); - if (var15 < 0) { - var15 = 0; - } - - var14 = var3 + this.anInt3722 + var15; - var11 += var15; - } - - for (var15 = 0; var15 < var13; ++var15) { - if (var9 == 0) { - this.method677(aClass94Array3753[var15], var2, var14); - } else if (var9 == 1) { - this.method677(aClass94Array3753[var15], var2 + (var4 - this.method682(aClass94Array3753[var15])) / 2, var14); - } else if (var9 == 2) { - this.method677(aClass94Array3753[var15], var2 + var4 - this.method682(aClass94Array3753[var15]), var14); - } else if (var15 == var13 - 1) { - this.method677(aClass94Array3753[var15], var2, var14); - } else { - this.method690(aClass94Array3753[var15], var4); - this.method677(aClass94Array3753[var15], var2, var14); - anInt3756 = 0; - } - - var14 += var11; - } - - return var13; - } - } - - final void method695(RSString var1, int var2, int var3, int var4, int var6) { - if (var1 != null) { - this.method698(var4, 0); - int var7 = var1.length(); - int[] var8 = new int[var7]; - int[] var9 = new int[var7]; - - for (int var10 = 0; var10 < var7; ++var10) { - var8[var10] = (int) (Math.sin((double) var10 / 5.0D + (double) var6 / 5.0D) * 5.0D); - var9[var10] = (int) (Math.sin((double) var10 / 3.0D + (double) var6 / 5.0D) * 5.0D); - } - - this.method703(var1, var2 - this.method682(var1) / 2, var3, var8, var9); - } - } - - final void method696(RSString var1, int var2, int var3, int var4, int var6) { - if (var1 != null) { - this.method698(var4, 0); - int var7 = var1.length(); - int[] var8 = new int[var7]; - - for (int var9 = 0; var9 < var7; ++var9) { - var8[var9] = (int) (Math.sin((double) var9 / 2.0D + (double) var6 / 5.0D) * 5.0D); - } - - this.method703(var1, var2 - this.method682(var1) / 2, var3, null, var8); - } - } - - final void method697(AbstractIndexedSprite[] var1, int[] var2) { - if (var2 == null || var2.length == var1.length) { - this.aClass109Array3740 = var1; - this.anIntArray3723 = var2; - } else { - throw new IllegalArgumentException(); - } - } - - private void method698(int var1, int var2) { - anInt3746 = -1; - anInt3747 = -1; - anInt3752 = var2; - anInt3754 = var2; - anInt3749 = var1; - anInt3755 = var1; - anInt3751 = 256; - anInt3750 = 256; - anInt3756 = 0; - anInt3748 = 0; - } - - public final void method699(RSString var1, int var2, int var3, int var4, int var5) { - if (var1 != null) { - this.method698(var4, var5); - this.method677(var1, var2 - this.method682(var1) / 2, var3); - } - } - - private void method700(byte[] var1) { - this.anIntArray3736 = new int[256]; - int var2; - if (var1.length == 257) { - for (var2 = 0; var2 < this.anIntArray3736.length; ++var2) { - this.anIntArray3736[var2] = var1[var2] & 0xFF; - } - - this.anInt3727 = var1[256] & 0xFF; - } else { - var2 = 0; - - for (int var3 = 0; var3 < 256; ++var3) { - this.anIntArray3736[var3] = var1[var2++] & 0xFF; - } - - int[] var10 = new int[256]; - int[] var4 = new int[256]; - - int var5; - for (var5 = 0; var5 < 256; ++var5) { - var10[var5] = var1[var2++] & 0xFF; - } - - for (var5 = 0; var5 < 256; ++var5) { - var4[var5] = var1[var2++] & 0xFF; - } - - byte[][] var12 = new byte[256][]; - - int var8; - for (int var6 = 0; var6 < 256; ++var6) { - var12[var6] = new byte[var10[var6]]; - byte var7 = 0; - - for (var8 = 0; var8 < var12[var6].length; ++var8) { - var7 += var1[var2++]; - var12[var6][var8] = var7; - } - } - - byte[][] var11 = new byte[256][]; - - int var13; - for (var13 = 0; var13 < 256; ++var13) { - var11[var13] = new byte[var10[var13]]; - byte var14 = 0; - - for (int var9 = 0; var9 < var11[var13].length; ++var9) { - var14 += var1[var2++]; - var11[var13][var9] = var14; - } - } - - this.aByteArray3733 = new byte[65536]; - - for (var13 = 0; var13 < 256; ++var13) { - if (var13 != 32 && var13 != 160) { - for (var8 = 0; var8 < 256; ++var8) { - if (var8 != 32 && var8 != 160) { - this.aByteArray3733[(var13 << 8) + var8] = (byte) method694(var12, var11, var4, this.anIntArray3736, var10, var13, var8); - } - } - } - } - - this.anInt3727 = var4[32] + var10[32]; - } - - } - - private void method701(int var1, int var2, int var3) { - anInt3746 = -1; - anInt3747 = -1; - anInt3752 = var2; - anInt3754 = var2; - anInt3749 = var1; - anInt3755 = var1; - anInt3751 = var3; - anInt3750 = var3; - anInt3756 = 0; - anInt3748 = 0; - } - - final void method702(RSString var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, Random var10, int var11, int[] var12) { - if (var1 == null) return; - - var10.setSeed(var11); - this.method701(var6, var7, 192 + (var10.nextInt() & 31)); - int var13 = var1.length(); - int[] var14 = new int[var13]; - int var15 = 0; - - int var16; - for (var16 = 0; var16 < var13; ++var16) { - var14[var16] = var15; - if ((var10.nextInt() & 3) == 0) { - ++var15; - } - } - - var16 = var2; - int var17 = var3 + this.anInt3722; - int var18 = -1; - if (var9 == 1) { - var17 += (var5 - this.anInt3722 - this.anInt3710) / 2; - } else if (var9 == 2) { - var17 = var3 + var5 - this.anInt3710; - } - - if (var8 == 1) { - var18 = this.method682(var1) + var15; - var16 = var2 + (var4 - var18) / 2; - } else if (var8 == 2) { - var18 = this.method682(var1) + var15; - var16 = var2 + (var4 - var18); - } - - this.method703(var1, var16, var17, var14, null); - if (var12 != null) { - if (var18 == -1) { - var18 = this.method682(var1) + var15; - } - - var12[0] = var16; - var12[1] = var17 - this.anInt3722; - var12[2] = var18; - var12[3] = this.anInt3722 + this.anInt3710; - } - } - - private void method703(RSString var1, int var2, int var3, int[] var4, int[] var5) { - var3 -= this.anInt3727; - int var6 = -1; - int var7 = 0; - int var8 = 0; - int var9 = var1.length(); - - for (int var10 = 0; var10 < var9; ++var10) { - int var11 = var1.charAt(var10, (byte) 95); - if (var11 == 60) { - var6 = var10; - } else { - int var13; - int var14; - int var15; - if (var11 == 62 && var6 != -1) { - RSString var12 = var1.substring(var6 + 1, var10, 0); - var6 = -1; - if (var12.equalsString(RSString.parse("lt"))) { - var11 = 60; - } else if (var12.equalsString(RSString.parse("gt"))) { - var11 = 62; - } else if (var12.equalsString(RSString.parse("nbsp"))) { - var11 = 160; - } else if (var12.equalsString(RSString.parse("shy"))) { - var11 = 173; - } else if (var12.equalsString(RSString.parse("times"))) { - var11 = 215; - } else if (var12.equalsString(RSString.parse("euro"))) { - var11 = 128; - } else if (var12.equalsString(RSString.parse("copy"))) { - var11 = 169; - } else { - if (!var12.equalsString(RSString.parse("reg"))) { - if (var12.startsWith(RSString.parse("img="))) { - try { - if (var4 == null) { - var13 = 0; - } else { - var13 = var4[var8]; - } - - if (var5 == null) { - var14 = 0; - } else { - var14 = var5[var8]; - } - - ++var8; - var15 = var12.substring(4).parseInt(); - AbstractIndexedSprite var20 = this.aClass109Array3740[var15]; - int var17 = this.anIntArray3723 != null ? this.anIntArray3723[var15] : var20.anInt1467; - if (anInt3750 == 256) { - var20.method1667(var2 + var13, var3 + this.anInt3727 - var17 + var14); - } else { - var20.method1666(var2 + var13, var3 + this.anInt3727 - var17 + var14, anInt3750); - } - - var2 += var20.anInt1469; - var7 = 0; - } catch (Exception var18) { - } - } else { - this.method685(var12); - } - continue; - } - - var11 = 174; - } - } - - if (var6 == -1) { - if (this.aByteArray3733 != null && var7 != 0) { - var2 += this.aByteArray3733[(var7 << 8) + var11]; - } - - int var19 = this.anIntArray3709[var11]; - var13 = this.anIntArray3721[var11]; - if (var4 == null) { - var14 = 0; - } else { - var14 = var4[var8]; - } - - if (var5 == null) { - var15 = 0; - } else { - var15 = var5[var8]; - } - - ++var8; - if (var11 != 32) { - if (anInt3750 == 256) { - if (anInt3754 != -1) { - this.method678(var11, var2 + this.anIntArray3719[var11] + 1 + var14, var3 + this.anIntArray3730[var11] + 1 + var15, var19, var13, anInt3754); - } - - this.method678(var11, var2 + this.anIntArray3719[var11] + var14, var3 + this.anIntArray3730[var11] + var15, var19, var13, anInt3755); - } else { - if (anInt3754 != -1) { - this.method679(var11, var2 + this.anIntArray3719[var11] + 1 + var14, var3 + this.anIntArray3730[var11] + 1 + var15, var19, var13, anInt3754, anInt3750); - } - - this.method679(var11, var2 + this.anIntArray3719[var11] + var14, var3 + this.anIntArray3730[var11] + var15, var19, var13, anInt3755, anInt3750); - } - } else if (anInt3756 > 0) { - anInt3748 += anInt3756; - var2 += anInt3748 >> 8; - anInt3748 &= 255; - } - - int var16 = this.anIntArray3736[var11]; - if (anInt3746 != -1) { - Toolkit.getActiveToolkit().drawHorizontalLine(var2, var3 + (int) ((double) this.anInt3727 * 0.7D), var16, anInt3746); - } - - if (anInt3747 != -1) { - Toolkit.getActiveToolkit().drawHorizontalLine(var2, var3 + this.anInt3727, var16, anInt3747); - } - - var2 += var16; - var7 = var11; - } - } - } - - } - -} diff --git a/Client/src/main/java/org/runite/client/FontType.java b/Client/src/main/java/org/runite/client/FontType.java deleted file mode 100644 index 21f395e69..000000000 --- a/Client/src/main/java/org/runite/client/FontType.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.runite.client; - -public final class FontType { - - public static Font plainFont; - public static Font bold; - public static Font smallFont; - -} diff --git a/Client/src/main/java/org/runite/client/GameObject.java b/Client/src/main/java/org/runite/client/GameObject.java deleted file mode 100644 index c709c4481..000000000 --- a/Client/src/main/java/org/runite/client/GameObject.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.runite.client; - -abstract class GameObject { - - static AbstractIndexedSprite[] aClass109Array1831; - static RSInterface[][] aClass11ArrayArray1834; - static boolean aBoolean1837 = false; - static int[] anIntArray1838; - static SoftwareSprite[] aSoftwareSpriteArray1839; - - - static void method1859(double var0) { - try { - if (Class70.aDouble1050 != var0) { - for (int var3 = 0; 256 > var3; ++var3) { - int var4 = (int) (255.0D * Math.pow((double) var3 / 255.0D, var0)); - BufferedDataStream.anIntArray3804[var3] = var4 > 255 ? 255 : var4; - } - - Class70.aDouble1050 = var0; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "th.KC(" + var0 + ',' + 32258 + ')'); - } - } - - static void graphicsSettings(boolean var0, int var1, int var3, int var4) { - try { - Class53.aLong866 = 0L; - int var5 = Class83.getWindowType(); - if (var1 == 3 || 3 == var5) { - var0 = true; - } - - if (Signlink.osName.startsWith("mac") && var1 > 0) { - var0 = true; - } - - boolean var6 = false; - if (var5 > 0 != var1 > 0) { - var6 = true; - } - - if (var0 && var1 > 0) { - var6 = true; - } - - Unsorted.method598(var0, var1, var6, var5, false, var3, var4); - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "th.EC(" + var0 + ',' + var1 + ',' + -8914 + ',' + var3 + ',' + var4 + ')'); - } - } - - static int method1863(int var0, int var1, int var3, int var4, int var5, int var6) { - try { - int var7; - if ((1 & var5) == 1) { - var7 = var0; - var0 = var3; - var3 = var7; - } - - var1 &= 3; - return var1 == 0 ? var6 : (1 != var1 ? (var1 != 2 ? var4 : -var3 + 1 + -var6 + 7) : -var4 + 7 + -var0 - -1); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "th.JC(" + var0 + ',' + var1 + ',' + (byte) 126 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - static void method1864(CacheIndex var2, Class3_Sub28_Sub17_Sub1 var3, CacheIndex var4) { - try { - Class139.aBoolean1827 = true; - LinkableRSString.aClass153_2581 = var4; - Class97.aClass153_1370 = var2; - int var5 = Class97.aClass153_1370.method2121() - 1; - TextureOperation39.itemDefinitionSize = Class97.aClass153_1370.getFileAmount(var5) + var5 * 256; - ClientErrorException.aClass94Array2119 = new RSString[]{null, null, null, null, TextCore.HasDrop}; - Unsorted.aClass94Array2596 = new RSString[]{null, null, TextCore.HasTake, null, null}; - TextureOperation10.aClass3_Sub28_Sub17_Sub1_3440 = var3; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "th.FC(" + true + ',' + (byte) -126 + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ')'); - } - } - - static void method1869(byte var0, int var1, int var2, int var3, int var4, int var5) { - try { - int var6 = var2 - var3; - int var7 = var4 + -var5; - if (var7 == 0) { - if (var6 != 0) { - TextureOperation15.method244(var3, var5, var2, var1); - } - - } else if (0 == var6) { - TextureOperation14.method320(var1, var3, var4, (byte) -107, var5); - } else { - if (0 > var6) { - var6 = -var6; - } - - if (var7 < 0) { - var7 = -var7; - } - - boolean var9 = var6 > var7; - int var10; - int var11; - if (var9) { - var10 = var5; - var5 = var3; - var11 = var4; - var3 = var10; - var4 = var2; - var2 = var11; - } - - if (var4 < var5) { - var10 = var5; - var5 = var4; - var4 = var10; - var11 = var3; - var3 = var2; - var2 = var11; - } - - var10 = var3; - var11 = var4 - var5; - int var12 = var2 + -var3; - int var13 = -(var11 >> 1); - int var14 = var2 <= var3 ? -1 : 1; - if (var12 < 0) { - var12 = -var12; - } - - int var15; - if (var9) { - for (var15 = var5; var4 >= var15; ++var15) { - Class38.anIntArrayArray663[var15][var10] = var1; - var13 += var12; - if (var13 > 0) { - var10 += var14; - var13 -= var11; - } - } - } else { - for (var15 = var5; var15 <= var4; ++var15) { - var13 += var12; - Class38.anIntArrayArray663[var10][var15] = var1; - if (var13 > 0) { - var10 += var14; - var13 -= var11; - } - } - } - - } - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "th.IC(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static void method1870() { - try { - Class101.aClass3_Sub24_Sub4_1421.method505((byte) -128); - Unsorted.anInt154 = 1; - Class101.aClass153_1423 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "th.GC(" + false + ')'); - } - } - - GameObject method1861() { - try { - return this; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "th.JB(" + -50 + ',' + -10 + ',' + -50 + ')'); - } - } - - boolean method1865() { - try { - return false; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "th.AB()"); - } - } - - void method1866(GameObject var1, int var2, int var3, int var4, boolean var5) { - } - - abstract void method1867(int var1, int var2, int var3, int var4, int var5); - - abstract void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12); - - abstract int method1871(); - -} diff --git a/Client/src/main/java/org/runite/client/GameShell.java b/Client/src/main/java/org/runite/client/GameShell.java deleted file mode 100644 index 11ef81a31..000000000 --- a/Client/src/main/java/org/runite/client/GameShell.java +++ /dev/null @@ -1,506 +0,0 @@ -package org.runite.client; - -import javax.swing.*; -import java.applet.Applet; -import java.applet.AppletContext; -import java.awt.*; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.WindowEvent; -import java.awt.event.WindowListener; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.Objects; - -public abstract class GameShell extends Applet implements Runnable, FocusListener, WindowListener { - - public static Canvas canvas; - public static Frame frame; - static Thread aThread409; - private static int anInt950; - private static volatile boolean hasWindowFocus = true; - private static long aLong2313 = 0L; - private static int anInt1737 = 1; - private static boolean aBoolean1784 = false; - private boolean displayError = false; - - private static void getMaxMemory() { - Class3_Sub24_Sub3.maxClientMemory = (int) (Runtime.getRuntime().maxMemory() / 1048576L) + 1; - } - - static void method34() { - try { - if (null != WorldListEntry.aAudioChannel_2627) { - WorldListEntry.aAudioChannel_2627.method2163(); - } - - if (Class3_Sub21.aAudioChannel_2491 != null) { - Class3_Sub21.aAudioChannel_2491.method2163(); - } - - Class140_Sub3.method1959(TextureOperation17.stereoSound); - WorldListEntry.aAudioChannel_2627 = AudioChannel.method1195(22050, Class38.gameSignlink, canvas, 0); - WorldListEntry.aAudioChannel_2627.method2154(Client.aClass3_Sub24_Sub4_1193); - Class3_Sub21.aAudioChannel_2491 = AudioChannel.method1195(2048, Class38.gameSignlink, canvas, 1); - Class3_Sub21.aAudioChannel_2491.method2154(Class3_Sub26.aClass3_Sub24_Sub2_2563); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rc.DA(" + -32589 + ')'); - } - } - - public static void provideSignlink(Signlink var0) { - try { - Class38.gameSignlink = var0; - TextureOperation30.signlink = var0; - TextureOperation12.method445(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rc.providesignlink(" + (var0 != null ? "{...}" : "null") + ')'); - } - } - - public static void launchDesktop() { - //GameShell.setDesktop(true); - ClientLoader.create().launch(); - } - - public final void focusLost(FocusEvent var1) { - try { - hasWindowFocus = false; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "rc.focusLost(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - abstract void method25(); - - public final void windowClosing(WindowEvent var1) { - try { - this.destroy(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "rc.windowClosing(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public final void windowIconified(WindowEvent var1) { - } - - public final void windowDeactivated(WindowEvent var1) { - } - - public final AppletContext getAppletContext() { - try { - return null != frame ? null : (Class38.gameSignlink != null && this != Class38.gameSignlink.gameApplet ? Class38.gameSignlink.gameApplet.getAppletContext() : super.getAppletContext()); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rc.getAppletContext()"); - } - } - - public final void focusGained(FocusEvent var1) { - try { - hasWindowFocus = true; - TextureOperation30.fullRedraw = true; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "rc.focusGained(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public final void windowClosed(WindowEvent var1) { - } - - final synchronized void addCanvas() { - if (canvas != null) { - canvas.removeFocusListener(this); - canvas.getParent().remove(canvas); - } - - Container var2; - if (TextureOperation30.fullScreenFrame != null) { - var2 = TextureOperation30.fullScreenFrame; - } else if (null == frame) { - var2 = Class38.gameSignlink.gameApplet; - } else { - var2 = frame; - } - - var2.setLayout(null); - canvas = new ComponentWrappedCanvas(this); - var2.add(canvas); - canvas.setSize(Class23.canvasWidth, Class140_Sub7.canvasHeight); - canvas.setVisible(true); - if (var2 == frame) { - Insets var3 = frame.getInsets(); - canvas.setLocation(Class84.leftMargin + var3.left, var3.top + Class106.rightMargin); - } else { - canvas.setLocation(Class84.leftMargin, Class106.rightMargin); - } - - canvas.addFocusListener(this); - canvas.requestFocus(); - hasWindowFocus = true; - TextureOperation30.fullRedraw = true; - TextureOperation26.aBoolean3078 = true; - Class3_Sub28_Sub5.forceReplaceCanvasEnable = false; - SequenceDefinition.aLong1847 = TimeUtils.time(); - ClientCommands.tweeningEnabled = true; - } - - public final void destroy() { - try { - if (this == LinkableRSString.anApplet_Sub1_2588 && !Class29.aBoolean554) { - aLong2313 = TimeUtils.time(); - TimeUtils.sleep(5000L); - TextureOperation30.signlink = null; - this.method35(false); - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rc.destroy()"); - } - } - - public final void update(Graphics g) { - this.paint(g); - } - - final void errorPrint(String var1) { - try { - if (!this.displayError) { - this.displayError = true; - System.out.println("error_game_" + var1); - JOptionPane.showMessageDialog(frame, "Error: " + var1 + (var1.contains("js5connect") ? ". The game is likely down." : "") + "\nCheck Discord (https://discord.gg/43YPGND) in a relevant #help channel, and a kind user might be able to help you out."); - try { - Objects.requireNonNull(this.getAppletContext()).showDocument(new URL(this.getCodeBase(), "error_game_" + var1 + ".ws"), "_top"); - } catch (Exception var4) { - } - - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "rc.U(" + (var1 != null ? "{...}" : "null") + ',' + -48 + ')'); - } - } - - abstract void method32(); - - abstract void method33(); - - public final URL getDocumentBase() { - try { - return null != frame ? null : (Class38.gameSignlink != null && this != Class38.gameSignlink.gameApplet ? Class38.gameSignlink.gameApplet.getDocumentBase() : super.getDocumentBase()); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rc.getDocumentBase()"); - } - } - - public final synchronized void paint(Graphics g) { - if (LinkableRSString.anApplet_Sub1_2588 == this && !Class29.aBoolean554) { - TextureOperation30.fullRedraw = true; - - if (aBoolean1784 && !HDToolKit.highDetail && -SequenceDefinition.aLong1847 + TimeUtils.time() > 1000) { - Rectangle var2 = g.getClipBounds(); - - if (var2 == null || Unsorted.frameWidth <= var2.width && var2.height >= Class70.frameHeight) { - Class3_Sub28_Sub5.forceReplaceCanvasEnable = true; - } - } - } - } - - public final void windowDeiconified(WindowEvent var1) { - } - - private void method35(boolean var2) { - try { - synchronized (this) { - if (Class29.aBoolean554) { - return; - } - - Class29.aBoolean554 = true; - } - - if (Class38.gameSignlink.gameApplet != null) { - Class38.gameSignlink.gameApplet.destroy(); - } - - try { - this.method32(); - } catch (Exception var8) { - } - - if (canvas != null) { - try { - canvas.removeFocusListener(this); - canvas.getParent().remove(canvas); - } catch (Exception var7) { - } - } - - if (null != Class38.gameSignlink) { - try { - Class38.gameSignlink.method1445(0); - } catch (Exception var6) { - } - } - - this.method33(); - - if (null != frame) { - try { - System.exit(0); - } catch (Throwable var5) { - } - } - - System.out.println("Shutdown complete - clean:" + var2); - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "rc.EA(" + ',' + var2 + ')'); - } - } - - public final void windowActivated(WindowEvent var1) { - } - - private void method36() { - try { - long var2 = TimeUtils.time(); - Class134.aLongArray1766[Unsorted.anInt1953] = var2; - Unsorted.anInt1953 = 31 & Unsorted.anInt1953 - -1; - synchronized (this) { - - TextureOperation26.aBoolean3078 = hasWindowFocus; - } - - this.method25(); - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "rc.R(" + true + ')'); - } - } - - private void method37() { - try { - long var2 = TimeUtils.time(); - long var4 = Class163_Sub1.aLongArray2986[anInt950]; - - Class163_Sub1.aLongArray2986[anInt950] = var2; - anInt950 = 31 & anInt950 + 1; - if (var4 != 0 && var2 > var4) { - int var6 = (int) (var2 + -var4); - SequenceDefinition.anInt1862 = (32000 + (var6 >> 1)) / var6; - } - - if (50 < TextureOperation28.anInt3313++) { - TextureOperation30.fullRedraw = true; - TextureOperation28.anInt3313 -= 50; - canvas.setSize(Class23.canvasWidth, Class140_Sub7.canvasHeight); - canvas.setVisible(true); - if (frame != null && null == TextureOperation30.fullScreenFrame) { - Insets var8 = frame.getInsets(); - canvas.setLocation(var8.left + Class84.leftMargin, Class106.rightMargin + var8.top); - } else { - canvas.setLocation(Class84.leftMargin, Class106.rightMargin); - } - } - - this.method38(); - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "rc.AA(" + 0 + ')'); - } - } - - abstract void method38(); - - public final URL getCodeBase() { - return frame == null ? (null != Class38.gameSignlink && this != Class38.gameSignlink.gameApplet ? Class38.gameSignlink.gameApplet.getCodeBase() : super.getCodeBase()) : null; - } - - public final void run() { - try { - try { - if (Signlink.javaVendor != null) { - - String vendor = Signlink.javaVendor.toLowerCase(); - - if (vendor.contains("sun") || vendor.contains("apple")) { - - String var2 = Signlink.javaVersion; - - if (var2.equals("1.1") || var2.startsWith("1.1.") || var2.equals("1.2") || var2.startsWith("1.2.")) { - this.errorPrint("wrongjava"); - return; - } - - anInt1737 = 5; - } else if (vendor.contains("ibm") && Signlink.javaVersion.equals("1.4.2")) { - this.errorPrint("wrongjava"); - return; - } - } - - int var7; - if (null != Signlink.javaVersion && Signlink.javaVersion.startsWith("1.")) { - var7 = 2; - - int var9; - for (var9 = 0; Signlink.javaVersion.length() > var7; ++var7) { - char var3 = Signlink.javaVersion.charAt(var7); - if (var3 < 48 || var3 > 57) { - break; - } - - var9 = var9 * 10 - (-var3 + 48); - } - - if (var9 >= 5) { - aBoolean1784 = true; - } - } - - if (Class38.gameSignlink.gameApplet != null) { - Method var8 = Signlink.setFocusCycleRoot; - if (null != var8) { - try { - var8.invoke(Class38.gameSignlink.gameApplet, Boolean.TRUE); - } catch (Throwable var4) { - } - } - } - - getMaxMemory(); - this.addCanvas(); - Unsorted.aClass158_3009 = TextureOperation18.method285(Class140_Sub7.canvasHeight, Class23.canvasWidth, canvas); - this.method39(); - Class3_Sub25.aClass129_2552 = Class36.method1012(); - - while (aLong2313 == 0 || aLong2313 > TimeUtils.time()) { - Class133.anInt1754 = Class3_Sub25.aClass129_2552.method1767(-1, anInt1737, WorldListEntry.anInt2626); - - for (var7 = 0; var7 < Class133.anInt1754; ++var7) { - this.method36(); - } - - this.method37(); - Class81.method1400(Class38.gameSignlink, canvas, -80); - } - } catch (Exception var5) { - Class49.reportError(null, var5); - this.errorPrint("crash"); - } - - this.method35(true); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "rc.run()"); - } - } - - - public final String getParameter(String var1) { - try { - return frame == null ? (Class38.gameSignlink != null && this != Class38.gameSignlink.gameApplet ? Class38.gameSignlink.gameApplet.getParameter(var1) : super.getParameter(var1)) : null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "rc.getParameter(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - abstract void method39(); - - public final void stop() { - try { - if (LinkableRSString.anApplet_Sub1_2588 == this && !Class29.aBoolean554) { - aLong2313 = 4000L + TimeUtils.time(); - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rc.stop()"); - } - } - - public abstract void init(); - - final void launch() { - try { - Class140_Sub7.canvasHeight = 768; - Class70.frameHeight = 768; - Class84.leftMargin = 0; - Class23.canvasWidth = 1024; - Unsorted.frameWidth = 1024; - Class106.rightMargin = 0; - LinkableRSString.anApplet_Sub1_2588 = this; - Frame frame = new Frame(); - frame.setTitle("Jagex"); - frame.setResizable(true); - frame.addWindowListener(this); - frame.setVisible(true); - frame.toFront(); - Insets insets = frame.getInsets(); - frame.setSize(insets.left + Unsorted.frameWidth + insets.right, insets.top + Class70.frameHeight + insets.bottom); - TextureOperation30.signlink = Class38.gameSignlink = new Signlink(null, 32 + TextureOperation20.paramModeWhat, "runescape", 29); - Class64 var10 = Class38.gameSignlink.startThread(1, this); - - while (0 == Objects.requireNonNull(var10).anInt978) { - TimeUtils.sleep(10L); - } - - aThread409 = (Thread) var10.anObject974; - //ClientLoader.create().launch(); - } catch (Exception var11) { - Class49.reportError(null, var11); - } - } - - public final void windowOpened(WindowEvent var1) { - } - - public final void start() { - try { - if (LinkableRSString.anApplet_Sub1_2588 == this && !Class29.aBoolean554) { - aLong2313 = 0L; - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rc.start()"); - } - } - - final void method41(int var3) { - try { - try { - if (LinkableRSString.anApplet_Sub1_2588 != null) { - ++Class36.anInt639; - if (Class36.anInt639 >= 3) { - this.errorPrint("alreadyloaded"); - return; - } - - Objects.requireNonNull(this.getAppletContext()).showDocument(this.getDocumentBase(), "_self"); - return; - } - LinkableRSString.anApplet_Sub1_2588 = this; - Class106.rightMargin = 0; - Class23.canvasWidth = 765; - Unsorted.frameWidth = 765; - Class84.leftMargin = 0; - Class140_Sub7.canvasHeight = 503; - Class70.frameHeight = 503; - String var6 = this.getParameter("openwinjs"); - InterfaceWidget.aBoolean3594 = var6 != null && var6.equals("1"); - - if (null == Class38.gameSignlink) { - TextureOperation30.signlink = Class38.gameSignlink = new Signlink(this, var3, null, 0); - } - - Class64 var7 = Class38.gameSignlink.startThread(1, this); - - while (Objects.requireNonNull(var7).anInt978 == 0) { - TimeUtils.sleep(10L); - } - - aThread409 = (Thread) var7.anObject974; - } catch (Exception var8) { - Class49.reportError(null, var8); - this.errorPrint("crash"); - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "rc.CA(" + (byte) -56 + ',' + 765 + ',' + var3 + ',' + 1530 + ',' + 503 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/GraphicDefinition.java b/Client/src/main/java/org/runite/client/GraphicDefinition.java deleted file mode 100644 index 1a1a9e796..000000000 --- a/Client/src/main/java/org/runite/client/GraphicDefinition.java +++ /dev/null @@ -1,174 +0,0 @@ -package org.runite.client; - -public final class GraphicDefinition { - - static int anInt529; - private int anInt530 = 128; - public static int CAMERA_DIRECTION = 0; - private short[] aShortArray533; - private short[] aShortArray534; - private short[] aShortArray535; - boolean aBoolean536 = false; - private int anInt537 = 0; - private int anInt538 = 0; - int graphicId; - private int anInt540 = 128; - private int anInt541; - int anInt542 = -1; - private int anInt543 = 0; - private short[] aShortArray545; - static int anInt546; - static int anInt548 = 0; - static volatile int anInt549 = 0; - - static GraphicDefinition getGraphicDefinition(int graphicId) { - try { - GraphicDefinition def = (GraphicDefinition) Class3_Sub31.aReferenceCache_2604.get(graphicId); - if (def == null) { - byte[] var3 = TextureOperation19.aClass153_3214.getFile(graphicId >>> 8, graphicId & 0xFF); - def = new GraphicDefinition(); - def.graphicId = graphicId; - - if (null != var3) { - def.parse(new DataBuffer(var3)); - } - - Class3_Sub31.aReferenceCache_2604.put(def, graphicId); - } - return def; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ck.D(" + (byte) 42 + ',' + graphicId + ')'); - } - } - - final void parse(DataBuffer var1) { - try { - - while(true) { - int var3 = var1.readUnsignedByte(); - if(var3 == 0) { - return; - } - - this.decode(var1, var3); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "eg.A(" + (var1 != null?"{...}":"null") + ',' + (byte) -113 + ')'); - } - } - - private void decode(DataBuffer var1, int var2) { - try { - if(var2 == 1) { - this.anInt541 = var1.readUnsignedShort(); - } else if(2 == var2) { - this.anInt542 = var1.readUnsignedShort(); - } else if(var2 == 4) { - this.anInt530 = var1.readUnsignedShort(); - } else if (var2 == 5) { - this.anInt540 = var1.readUnsignedShort(); - } else if (6 == var2) { - this.anInt543 = var1.readUnsignedShort(); - } else if (var2 == 7) { - this.anInt538 = var1.readUnsignedByte(); - } else if (var2 == 8) { - this.anInt537 = var1.readUnsignedByte(); - } else if (var2 == 9) { - this.aBoolean536 = true; - } else { - int var4; - int var5; - if (40 == var2) { - var4 = var1.readUnsignedByte(); - this.aShortArray533 = new short[var4]; - this.aShortArray545 = new short[var4]; - - for (var5 = 0; var4 > var5; ++var5) { - this.aShortArray533[var5] = (short) var1.readUnsignedShort(); - this.aShortArray545[var5] = (short) var1.readUnsignedShort(); - } - } else if (41 == var2) { - var4 = var1.readUnsignedByte(); - this.aShortArray534 = new short[var4]; - this.aShortArray535 = new short[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.aShortArray534[var5] = (short) var1.readUnsignedShort(); - this.aShortArray535[var5] = (short) var1.readUnsignedShort(); - } - } - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "eg.D(" + (var1 != null?"{...}":"null") + ',' + var2 + ',' + 128 + ')'); - } - } - - final Model method966(int var1, int var3, int var4) { - try { - Model var5 = (Model)Class27.aReferenceCache_511.get(this.graphicId); - if(var5 == null) { - Model_Sub1 var6 = Model_Sub1.method2015(Unsorted.aClass153_4048, this.anInt541); - if(null == var6) { - return null; - } - - int var7; - if(null != this.aShortArray533) { - for(var7 = 0; this.aShortArray533.length > var7; ++var7) { - var6.method2016(this.aShortArray533[var7], this.aShortArray545[var7]); - } - } - - if(this.aShortArray534 != null) { - for(var7 = 0; var7 < this.aShortArray534.length; ++var7) { - var6.method1998(this.aShortArray534[var7], this.aShortArray535[var7]); - } - } - - var5 = var6.method2008(64 - -this.anInt538, this.anInt537 + 850, -30, -50, -30); - Class27.aReferenceCache_511.put(var5, this.graphicId); - } - - Model var9; - if(this.anInt542 == -1 || var3 == -1) { - var9 = var5.method1882(true, true, true); - } else { - var9 = SequenceDefinition.getAnimationDefinition(this.anInt542).method2059(var1, var4, var3, var5); - } - - if(128 != this.anInt530 || 128 != this.anInt540) { - var9.resize(this.anInt530, this.anInt540, this.anInt530); - } - - if(this.anInt543 != 0) { - if(this.anInt543 == 90) { - var9.method1885(); - } - - if(180 == this.anInt543) { - var9.method1874(); - } - - if(270 == this.anInt543) { - var9.method1900(); - } - } - - return var9; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "eg.C(" + var1 + ',' + (byte) -30 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method967(int var0, int var1, int var3, int var4, int var5, int var6, int var7) { - try { - if(Unsorted.loadInterface(var5)) { - Client.handleItemSwitch(GameObject.aClass11ArrayArray1834[var5], -1, var6, var1, var4, var7, var0, var3); - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "eg.B(" + var0 + ',' + var1 + ',' + 2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/HDIndexedSprite.java b/Client/src/main/java/org/runite/client/HDIndexedSprite.java deleted file mode 100644 index 00ed82cb0..000000000 --- a/Client/src/main/java/org/runite/client/HDIndexedSprite.java +++ /dev/null @@ -1,147 +0,0 @@ -package org.runite.client; - - - - -import org.rs09.client.rendering.opengl.enums.GLBeginMode; - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -public final class HDIndexedSprite extends AbstractIndexedSprite { - - private int anInt2675 = -1; - private int anInt2676 = -1; - private int anInt2677 = 0; - private int anInt2678 = 0; - private int anInt2679; - private int anInt2680; - private int anInt2681; - - - private void method1678(byte[] var1, int[] var2) { - this.anInt2681 = Class95.method1585((byte)62, this.width); - this.anInt2680 = Class95.method1585((byte)99, this.height); - byte[] var3 = new byte[this.anInt2681 * this.anInt2680 * 4]; - int var4 = 0; - int var5 = 0; - - for(int var6 = 0; var6 < this.height; ++var6) { - for(int var7 = 0; var7 < this.width; ++var7) { - // Hd Fix - if (var1[var5] < 0) { - return; - } - byte var8 = var1[var5++]; - if(var8 == 0) { - var4 += 4; - } else { - int var9 = var2[var8]; - var3[var4++] = (byte)(var9 >> 16); - var3[var4++] = (byte)(var9 >> 8); - var3[var4++] = (byte)var9; - var3[var4++] = -1; - } - } - var4 += (this.anInt2681 - this.width) * 4; - } - ByteBuffer byteBuffer = ByteBuffer.wrap(var3); - GL gl = HDToolKit.gl; - if(this.anInt2675 == -1) { - int[] var12 = new int[1]; - gl.glGenTextures(1, var12, 0); - this.anInt2675 = var12[0]; - this.anInt2679 = Class31.anInt582; - } - - HDToolKit.bindTexture2D(this.anInt2675); - gl.glTexImage2D(3553, 0, 6408, this.anInt2681, this.anInt2680, 0, 6408, 5121, byteBuffer); - Class31.memory2D += byteBuffer.limit() - this.anInt2678; - this.anInt2678 = byteBuffer.limit(); - } - - final void method1666(int var1, int var2, int var3) { - HDToolKit.method1828(); - var1 += this.anInt1470; - var2 += this.anInt1464; - GL var4 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt2675); - this.method1679(); - var4.glColor4f(1.0F, 1.0F, 1.0F, (float)var3 / 256.0F); - var4.glTranslatef((float)var1, (float)(HDToolKit.viewHeight - var2), 0.0F); - var4.glCallList(this.anInt2676); - var4.glLoadIdentity(); - } - - private void method1679() { - if(this.anInt2677 != 1) { - this.anInt2677 = 1; - GL var2 = HDToolKit.gl; - var2.glTexParameteri(3553, 10241, 9728); - var2.glTexParameteri(3553, 10240, 9728); - - } - } - - public final void method1667(int var1, int var2) { - HDToolKit.method1822(); - var1 += this.anInt1470; - var2 += this.anInt1464; - GL var3 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt2675); - this.method1679(); - var3.glTranslatef((float)var1, (float)(HDToolKit.viewHeight - var2), 0.0F); - var3.glCallList(this.anInt2676); - var3.glLoadIdentity(); - } - - protected final void finalize() throws Throwable { - if(this.anInt2675 != -1) { - Class31.method991(this.anInt2675, this.anInt2678, this.anInt2679); - this.anInt2675 = -1; - this.anInt2678 = 0; - } - - if(this.anInt2676 != -1) { - Class31.method986(this.anInt2676, this.anInt2679); - this.anInt2676 = -1; - } - - super.finalize(); - } - - private void method1680() { - float var1 = (float)this.width / (float)this.anInt2681; - float var2 = (float)this.height / (float)this.anInt2680; - GL var3 = HDToolKit.gl; - if(this.anInt2676 == -1) { - this.anInt2676 = var3.glGenLists(1); - this.anInt2679 = Class31.anInt582; - } - - var3.glNewList(this.anInt2676, 4864); - var3.glBegin(GLBeginMode.TRIANGLE_FAN); - var3.glTexCoord2f(var1, 0.0F); - var3.glVertex2f((float)this.width, 0.0F); - var3.glTexCoord2f(0.0F, 0.0F); - var3.glVertex2f(0.0F, 0.0F); - var3.glTexCoord2f(0.0F, var2); - var3.glVertex2f(0.0F, (float)(-this.height)); - var3.glTexCoord2f(var1, var2); - var3.glVertex2f((float)this.width, (float)(-this.height)); - var3.glEnd(); - var3.glEndList(); - } - - HDIndexedSprite(int var1, int var2, int var3, int var4, int var5, int var6, byte[] var7, int[] var8) { - this.anInt1469 = var1; - this.anInt1467 = var2; - this.anInt1470 = var3; - this.anInt1464 = var4; - this.width = var5; - this.height = var6; - this.method1678(var7, var8); - this.method1680(); - } -} diff --git a/Client/src/main/java/org/runite/client/HDSprite.java b/Client/src/main/java/org/runite/client/HDSprite.java deleted file mode 100644 index 48dbe2e5f..000000000 --- a/Client/src/main/java/org/runite/client/HDSprite.java +++ /dev/null @@ -1,486 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.rendering.opengl.enums.GLBeginMode; - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -public class HDSprite extends AbstractSprite { - - int anInt4074 = 0; - int anInt4075; - int anInt4077 = -1; - int anInt4079; - private int anInt4076 = -1; - private int anInt4078 = 0; - private int anInt4080; - - - HDSprite(int var1, int var2, int var3, int var4, int var5, int var6, int[] var7) { - this.anInt3697 = var1; - this.anInt3706 = var2; - this.anInt3701 = var3; - this.anInt3698 = var4; - this.width = var5; - this.height = var6; - this.method650(var7); - this.method651(); - } - - public HDSprite(SoftwareSprite var1) { - this.anInt3697 = var1.anInt3697; - this.anInt3706 = var1.anInt3706; - this.anInt3701 = var1.anInt3701; - this.anInt3698 = var1.anInt3698; - this.width = var1.width; - this.height = var1.height; - this.method650(var1.anIntArray4081); - this.method651(); - } - - private void method644(int var1) { - if (this.anInt4078 != var1) { - this.anInt4078 = var1; - GL var2 = HDToolKit.gl; - if (var1 == 2) { - var2.glTexParameteri(3553, 10241, 9729); - var2.glTexParameteri(3553, 10240, 9729); - } else { - var2.glTexParameteri(3553, 10241, 9728); - var2.glTexParameteri(3553, 10240, 9728); - } - - } - } - - final void drawMinimapIcons(int interfaceWidth, int interfaceHeight, HDSprite var3) { - if (var3 != null) { - HDToolKit.method1822(); - HDToolKit.bindTexture2D(var3.anInt4077); - var3.method644(1); - GL var4 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - var4.glActiveTexture('\u84c1'); - var4.glEnable(3553); - var4.glBindTexture(3553, var3.anInt4077); - var4.glTexEnvi(8960, '\u8571', 7681); - var4.glTexEnvi(8960, '\u8580', '\u8578'); - float var5 = (float) (interfaceWidth - Class22.anInt449) / (float) var3.anInt4075; - float var6 = (float) (interfaceHeight - Class22.anInt448) / (float) var3.anInt4079; - float var7 = (float) (interfaceWidth + this.width - Class22.anInt449) / (float) var3.anInt4075; - float var8 = (float) (interfaceHeight + this.height - Class22.anInt448) / (float) var3.anInt4079; - interfaceWidth += this.anInt3701; - interfaceHeight += this.anInt3698; - var4.glBegin(GLBeginMode.TRIANGLE_FAN); - var4.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - float var9 = (float) this.width / (float) this.anInt4075; - float var10 = (float) this.height / (float) this.anInt4079; - var4.glMultiTexCoord2f('\u84c1', var7, var6); - var4.glTexCoord2f(var9, 0.0F); - var4.glVertex2f((float) (interfaceWidth + this.width), (float) (HDToolKit.viewHeight - interfaceHeight)); - var4.glMultiTexCoord2f('\u84c1', var5, var6); - var4.glTexCoord2f(0.0F, 0.0F); - var4.glVertex2f((float) interfaceWidth, (float) (HDToolKit.viewHeight - interfaceHeight)); - var4.glMultiTexCoord2f('\u84c1', var5, var8); - var4.glTexCoord2f(0.0F, var10); - var4.glVertex2f((float) interfaceWidth, (float) (HDToolKit.viewHeight - (interfaceHeight + this.height))); - var4.glMultiTexCoord2f('\u84c1', var7, var8); - var4.glTexCoord2f(var9, var10); - var4.glVertex2f((float) (interfaceWidth + this.width), (float) (HDToolKit.viewHeight - (interfaceHeight + this.height))); - var4.glEnd(); - var4.glTexEnvi(8960, '\u8571', 8448); - var4.glTexEnvi(8960, '\u8580', 5890); - var4.glDisable(3553); - var4.glActiveTexture('\u84c0'); - } - } - - final void method646(int var1, int var2, int var3, int var4, int var5) { - HDToolKit.method1828(); - GL var6 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - float var7 = (float) this.width / (float) this.anInt4075; - float var8 = (float) this.height / (float) this.anInt4079; - var7 *= (float) var4; - var8 *= (float) var5; - int var9 = var1 + this.anInt3701; - int var10 = var9 + this.width * var4; - int var11 = HDToolKit.viewHeight - var2 - this.anInt3698; - int var12 = var11 - this.height * var5; - float var13 = (float) var3 / 256.0F; - var6.glBegin(GLBeginMode.TRIANGLE_FAN); - var6.glColor4f(1.0F, 1.0F, 1.0F, var13); - var6.glTexCoord2f(var7, 0.0F); - var6.glVertex2f((float) var10, (float) var11); - var6.glTexCoord2f(0.0F, 0.0F); - var6.glVertex2f((float) var9, (float) var11); - var6.glTexCoord2f(0.0F, var8); - var6.glVertex2f((float) var9, (float) var12); - var6.glTexCoord2f(var7, var8); - var6.glVertex2f((float) var10, (float) var12); - var6.glEnd(); - } - - public final void drawMinimapRegion(int x, int y, int width, int height, int playerRelativeX, int playerRelativeY, int regionRotation, int zoom, HDSprite var9) { - if (var9 != null) { - HDToolKit.method1822(); - HDToolKit.bindTexture2D(var9.anInt4077); - var9.method644(1); - GL var10 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - var10.glActiveTexture('\u84c1'); - var10.glEnable(3553); - var10.glBindTexture(3553, var9.anInt4077); - var10.glTexEnvi(8960, '\u8571', 7681); - var10.glTexEnvi(8960, '\u8580', '\u8578'); - int var11 = -width / 2; - int var12 = -height / 2; - int var13 = -var11; - int var14 = -var12; - int var15 = (int) (Math.sin((double) regionRotation / 326.11D) * 65536.0D); - int var16 = (int) (Math.cos((double) regionRotation / 326.11D) * 65536.0D); - var15 = var15 * zoom >> 8; - var16 = var16 * zoom >> 8; - int var17 = (playerRelativeX << 16) + var12 * var15 + var11 * var16; - int var18 = (playerRelativeY << 16) + (var12 * var16 - var11 * var15); - int var19 = (playerRelativeX << 16) + var12 * var15 + var13 * var16; - int var20 = (playerRelativeY << 16) + (var12 * var16 - var13 * var15); - int var21 = (playerRelativeX << 16) + var14 * var15 + var11 * var16; - int var22 = (playerRelativeY << 16) + (var14 * var16 - var11 * var15); - int var23 = (playerRelativeX << 16) + var14 * var15 + var13 * var16; - int var24 = (playerRelativeY << 16) + (var14 * var16 - var13 * var15); - float var25 = (float) var9.width / (float) var9.anInt4075; - float var26 = (float) var9.height / (float) var9.anInt4079; - var10.glBegin(GLBeginMode.TRIANGLE_FAN); - var10.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - float var27 = 65536.0F * (float) this.anInt4075; - float var28 = (float) (65536 * this.anInt4079); - var10.glMultiTexCoord2f('\u84c1', var25, 0.0F); - var10.glTexCoord2f((float) var19 / var27, (float) var20 / var28); - var10.glVertex2f((float) (x + width), (float) (HDToolKit.viewHeight - y)); - var10.glMultiTexCoord2f('\u84c1', 0.0F, 0.0F); - var10.glTexCoord2f((float) var17 / var27, (float) var18 / var28); - var10.glVertex2f((float) x, (float) (HDToolKit.viewHeight - y)); - var10.glMultiTexCoord2f('\u84c1', 0.0F, var26); - var10.glTexCoord2f((float) var21 / var27, (float) var22 / var28); - var10.glVertex2f((float) x, (float) (HDToolKit.viewHeight - (y + height))); - var10.glMultiTexCoord2f('\u84c1', var25, var26); - var10.glTexCoord2f((float) var23 / var27, (float) var24 / var28); - var10.glVertex2f((float) (x + width), (float) (HDToolKit.viewHeight - (y + height))); - var10.glEnd(); - var10.glTexEnvi(8960, '\u8571', 8448); - var10.glTexEnvi(8960, '\u8580', 5890); - var10.glDisable(3553); - var10.glActiveTexture('\u84c0'); - } - } - - final void method641(int var1, int var2) { - HDToolKit.method1822(); - var1 += this.anInt3701; - var2 += this.anInt3698; - GL var3 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - var3.glTranslatef((float) var1, (float) (HDToolKit.viewHeight - var2), 0.0F); - float var4 = (float) this.width / (float) this.anInt4075; - float var5 = (float) this.height / (float) this.anInt4079; - var3.glBegin(GLBeginMode.TRIANGLE_FAN); - var3.glTexCoord2f(0.0F, 0.0F); - var3.glVertex2f((float) this.width, 0.0F); - var3.glTexCoord2f(var4, 0.0F); - var3.glVertex2f(0.0F, 0.0F); - var3.glTexCoord2f(var4, var5); - var3.glVertex2f(0.0F, (float) (-this.height)); - var3.glTexCoord2f(0.0F, var5); - var3.glVertex2f((float) this.width, (float) (-this.height)); - var3.glEnd(); - var3.glLoadIdentity(); - } - - public final void drawAt(int var1, int var2) { - HDToolKit.method1822(); - var1 += this.anInt3701; - var2 += this.anInt3698; - GL var3 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - var3.glTranslatef((float) var1, (float) (HDToolKit.viewHeight - var2), 0.0F); - var3.glCallList(this.anInt4076); - var3.glLoadIdentity(); - } - - final void method648(int var1, int var2, int var3, int var4, int var5) { - HDToolKit.method1822(); - GL var7 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(2); - var1 -= this.anInt3701 << 4; - var2 -= this.anInt3698 << 4; - var7.glTranslatef((float) var3 / 16.0F, (float) HDToolKit.viewHeight - (float) var4 / 16.0F, 0.0F); - var7.glRotatef((float) (-var5) * 0.005493164F, 0.0F, 0.0F, 1.0F); - var7.glTranslatef((float) (-var1) / 16.0F, (float) var2 / 16.0F, 0.0F); - var7.glCallList(this.anInt4076); - var7.glLoadIdentity(); - } - - public final void method639(int var1, int var2, int var3, int var4) { - if (var3 > 0 && var4 > 0) { - HDToolKit.method1822(); - int var5 = this.width; - int var6 = this.height; - int var7 = 0; - int var8 = 0; - int var9 = this.anInt3697; - int var10 = this.anInt3706; - int var11 = (var9 << 16) / var3; - int var12 = (var10 << 16) / var4; - int var13; - if (this.anInt3701 > 0) { - var13 = ((this.anInt3701 << 16) + var11 - 1) / var11; - var1 += var13; - var7 += var13 * var11 - (this.anInt3701 << 16); - } - - if (this.anInt3698 > 0) { - var13 = ((this.anInt3698 << 16) + var12 - 1) / var12; - var2 += var13; - var8 += var13 * var12 - (this.anInt3698 << 16); - } - - if (var5 < var9) { - var3 = ((var5 << 16) - var7 + var11 - 1) / var11; - } - - if (var6 < var10) { - var4 = ((var6 << 16) - var8 + var12 - 1) / var12; - } - - GL var20 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(2); - float var14 = (float) var1; - float var15 = var14 + (float) var3; - float var16 = (float) (HDToolKit.viewHeight - var2); - float var17 = var16 - (float) var4; - float var18 = (float) this.width / (float) this.anInt4075; - float var19 = (float) this.height / (float) this.anInt4079; - var20.glBegin(GLBeginMode.TRIANGLE_FAN); - var20.glTexCoord2f(var18, 0.0F); - var20.glVertex2f(var15, var16); - var20.glTexCoord2f(0.0F, 0.0F); - var20.glVertex2f(var14, var16); - var20.glTexCoord2f(0.0F, var19); - var20.glVertex2f(var14, var17); - var20.glTexCoord2f(var18, var19); - var20.glVertex2f(var15, var17); - var20.glEnd(); - } - } - - final void method635(int var1, int var2) { - HDToolKit.method1822(); - var1 += this.anInt3701; - var2 += this.anInt3698; - GL var3 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - var3.glTranslatef((float) var1, (float) (HDToolKit.viewHeight - var2), 0.0F); - var3.glCallList(this.anInt4076); - var3.glLoadIdentity(); - } - - protected final void finalize() throws Throwable { - if (this.anInt4077 != -1) { - Class31.method991(this.anInt4077, this.anInt4074, this.anInt4080); - this.anInt4077 = -1; - this.anInt4074 = 0; - } - - if (this.anInt4076 != -1) { - Class31.method986(this.anInt4076, this.anInt4080); - this.anInt4076 = -1; - } - - super.finalize(); - } - - final void method636(int var1, int var2, int var3, int var4, int var5, int var6) { - HDToolKit.method1822(); - GL var7 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - var1 -= this.anInt3701 << 4; - var2 -= this.anInt3698 << 4; - var7.glTranslatef((float) var3 / 16.0F, (float) HDToolKit.viewHeight - (float) var4 / 16.0F, 0.0F); - var7.glRotatef((float) var5 * 0.005493164F, 0.0F, 0.0F, 1.0F); - if (var6 != 4096) { - var7.glScalef((float) var6 / 4096.0F, (float) var6 / 4096.0F, 0.0F); - } - - var7.glTranslatef((float) (-var1) / 16.0F, (float) var2 / 16.0F, 0.0F); - var7.glCallList(this.anInt4076); - var7.glLoadIdentity(); - } - - final void method642(int var1, int var2, int var3, int var4, int var5) { - if (var3 > 0 && var4 > 0) { - HDToolKit.method1828(); - int var6 = this.width; - int var7 = this.height; - int var8 = 0; - int var9 = 0; - int var10 = this.anInt3697; - int var11 = this.anInt3706; - int var12 = (var10 << 16) / var3; - int var13 = (var11 << 16) / var4; - int var14; - if (this.anInt3701 > 0) { - var14 = ((this.anInt3701 << 16) + var12 - 1) / var12; - var1 += var14; - var8 += var14 * var12 - (this.anInt3701 << 16); - } - - if (this.anInt3698 > 0) { - var14 = ((this.anInt3698 << 16) + var13 - 1) / var13; - var2 += var14; - var9 += var14 * var13 - (this.anInt3698 << 16); - } - - if (var6 < var10) { - var3 = ((var6 << 16) - var8 + var12 - 1) / var12; - } - - if (var7 < var11) { - var4 = ((var7 << 16) - var9 + var13 - 1) / var13; - } - - GL var22 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - float var15 = (float) var1; - float var16 = var15 + (float) var3; - float var17 = (float) (HDToolKit.viewHeight - var2); - float var18 = var17 - (float) var4; - float var19 = (float) this.width / (float) this.anInt4075; - float var20 = (float) this.height / (float) this.anInt4079; - float var21 = (float) var5 / 256.0F; - var22.glBegin(GLBeginMode.TRIANGLE_FAN); - var22.glColor4f(1.0F, 1.0F, 1.0F, var21); - var22.glTexCoord2f(var19, 0.0F); - var22.glVertex2f(var16, var17); - var22.glTexCoord2f(0.0F, 0.0F); - var22.glVertex2f(var15, var17); - var22.glTexCoord2f(0.0F, var20); - var22.glVertex2f(var15, var18); - var22.glTexCoord2f(var19, var20); - var22.glVertex2f(var16, var18); - var22.glEnd(); - } - } - - final void method649(int var1, int var2, int var3, int var4) { - HDToolKit.method1822(); - GL var5 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - float var6 = (float) this.width / (float) this.anInt4075; - float var7 = (float) this.height / (float) this.anInt4079; - var6 *= (float) var3; - var7 *= (float) var4; - int var8 = var1 + this.anInt3701; - int var9 = var8 + this.width * var3; - int var10 = HDToolKit.viewHeight - var2 - this.anInt3698; - int var11 = var10 - this.height * var4; - var5.glBegin(GLBeginMode.TRIANGLE_FAN); - var5.glTexCoord2f(var6, 0.0F); - var5.glVertex2f((float) var9, (float) var10); - var5.glTexCoord2f(0.0F, 0.0F); - var5.glVertex2f((float) var8, (float) var10); - var5.glTexCoord2f(0.0F, var7); - var5.glVertex2f((float) var8, (float) var11); - var5.glTexCoord2f(var6, var7); - var5.glVertex2f((float) var9, (float) var11); - var5.glEnd(); - } - - void method650(int[] var1) { - this.anInt4075 = Class95.method1585((byte) 125, this.width); - this.anInt4079 = Class95.method1585((byte) 59, this.height); - byte[] var2 = new byte[this.anInt4075 * this.anInt4079 * 4]; - int var3 = 0; - int var4 = 0; - int var5 = (this.anInt4075 - this.width) * 4; - - for (int var6 = 0; var6 < this.height; ++var6) { - for (int var7 = 0; var7 < this.width; ++var7) { - int var8 = var1[var4++]; - if (var8 == 0) { - var3 += 4; - } else { - var2[var3++] = (byte) (var8 >> 16); - var2[var3++] = (byte) (var8 >> 8); - var2[var3++] = (byte) var8; - var2[var3++] = -1; - } - } - - var3 += var5; - } - - ByteBuffer var9 = ByteBuffer.wrap(var2); - GL var10 = HDToolKit.gl; - if (this.anInt4077 == -1) { - int[] var11 = new int[1]; - var10.glGenTextures(1, var11, 0); - this.anInt4077 = var11[0]; - this.anInt4080 = Class31.anInt582; - } - - HDToolKit.bindTexture2D(this.anInt4077); - var10.glTexImage2D(3553, 0, 6408, this.anInt4075, this.anInt4079, 0, 6408, 5121, var9); - Class31.memory2D += var9.limit() - this.anInt4074; - this.anInt4074 = var9.limit(); - } - - final void method637(int var1, int var2, int var3) { - HDToolKit.method1828(); - var1 += this.anInt3701; - var2 += this.anInt3698; - GL var4 = HDToolKit.gl; - HDToolKit.bindTexture2D(this.anInt4077); - this.method644(1); - var4.glColor4f(1.0F, 1.0F, 1.0F, (float) var3 / 256.0F); - var4.glTranslatef((float) var1, (float) (HDToolKit.viewHeight - var2), 0.0F); - var4.glCallList(this.anInt4076); - var4.glLoadIdentity(); - } - - private void method651() { - float var1 = (float) this.width / (float) this.anInt4075; - float var2 = (float) this.height / (float) this.anInt4079; - GL var3 = HDToolKit.gl; - if (this.anInt4076 == -1) { - this.anInt4076 = var3.glGenLists(1); - this.anInt4080 = Class31.anInt582; - } - - var3.glNewList(this.anInt4076, 4864); - var3.glBegin(GLBeginMode.TRIANGLE_FAN); - var3.glTexCoord2f(var1, 0.0F); - var3.glVertex2f((float) this.width, 0.0F); - var3.glTexCoord2f(0.0F, 0.0F); - var3.glVertex2f(0.0F, 0.0F); - var3.glTexCoord2f(0.0F, var2); - var3.glVertex2f(0.0F, (float) (-this.height)); - var3.glTexCoord2f(var1, var2); - var3.glVertex2f((float) this.width, (float) (-this.height)); - var3.glEnd(); - var3.glEndList(); - } -} diff --git a/Client/src/main/java/org/runite/client/HDToolKit.java b/Client/src/main/java/org/runite/client/HDToolKit.java deleted file mode 100644 index 1fa84c8a4..000000000 --- a/Client/src/main/java/org/runite/client/HDToolKit.java +++ /dev/null @@ -1,733 +0,0 @@ -package org.runite.client; - -import org.rs09.SlayerTracker; -import org.rs09.client.config.GameConfig; - -import javax.media.opengl.*; -import javax.media.opengl.glu.GLU; -import java.awt.*; -import java.nio.ByteOrder; -import java.nio.IntBuffer; -import java.nio.charset.StandardCharsets; - -public final class HDToolKit { - - private static final float[] aFloatArray1808 = new float[16]; - /** - * JOGL GL4bc related - */ - public static GL gl; - public static boolean highDetail = false; - public static int viewHeight; - public static int viewWidth; - static int maxTextureUnits; - static boolean aBoolean1790; - static int anInt1791 = 0; - static boolean aBoolean1798 = true; - static boolean allows3DTextureMapping; - static boolean supportMultisample; - static int anInt1810; - static boolean supportVertexBufferObject; - static boolean aBoolean1817; - static boolean supportVertexProgram; - static boolean supportTextureCubeMap; - private static GLContext glContext; - private static GLDrawable glDrawable; - private static String vendor; - private static String renderer; - private static float aFloat1787; - private static boolean aBoolean1788 = false; - private static int anInt1792 = 0; - private static int anInt1793 = 0; - private static float aFloat1794 = 0.0F; - private static float aFloat1795; - private static boolean aBoolean1796 = true; - private static float aFloat1797 = 0.0F; - private static boolean viewportSetup = false; - private static int anInt1803 = -1; - private static boolean aBoolean1805 = true; - private static int anInt1812; - private static boolean aBoolean1816 = true; - - private static RSString method1820(String var0) { - byte[] var1; - var1 = var0.getBytes(StandardCharsets.ISO_8859_1); - return TextureOperation33.bufferToString(var1, var1.length, 0); - } - - static void method1821(int offsetX, int offsetY, int ratioWidth, int ratioHeight) { - viewport(0, 0, viewWidth, viewHeight, offsetX, offsetY, 0.0F, 0.0F, ratioWidth, ratioHeight); - } - - static void method1822() { - Unsorted.method551(0, 0); - method1836(); - method1856(1); - method1847(1); - method1837(false); - method1831(false); - method1827(false); - method1823(); - } - - static void method1823() { - if (aBoolean1788) { - gl.glMatrixMode(5890); - gl.glLoadIdentity(); - gl.glMatrixMode(5888); - aBoolean1788 = false; - } - - } - - static void method1824() { - Unsorted.method551(0, 0); - method1836(); - method1856(0); - method1847(0); - method1837(false); - method1831(false); - method1827(false); - method1823(); - } - - static void method1825(float var0, float var1) { - if (!viewportSetup) { - if (var0 != aFloat1797 || var1 != aFloat1794) { - aFloat1797 = var0; - aFloat1794 = var1; - if (var1 == 0.0F) { - aFloatArray1808[10] = aFloat1787; - aFloatArray1808[14] = aFloat1795; - } else { - float var2 = var0 / (var1 + var0); - float var3 = var2 * var2; - float var4 = -aFloat1795 * (1.0F - var2) * (1.0F - var2) / var1; - aFloatArray1808[10] = aFloat1787 + var4; - aFloatArray1808[14] = aFloat1795 * var3; - } - - gl.glMatrixMode(5889); - gl.glLoadMatrixf(aFloatArray1808, 0); - gl.glMatrixMode(5888); - } - } - } - - public static void bufferSwap() { - try { - glDrawable.swapBuffers(); - } catch (GLException ignore) { - //TODO: This may be the cause of the display failing sometimes. - } - } - - static void method1827(boolean var0) { - if (var0 != aBoolean1816) { - if (var0) { - gl.glEnable(2912); - } else { - gl.glDisable(2912); - } - - aBoolean1816 = var0; - } - } - - static void method1828() { - Unsorted.method551(0, 0); - method1836(); - method1856(0); - method1847(0); - method1837(false); - method1831(false); - method1827(false); - method1823(); - } - - private static void method1829() { - viewportSetup = false; - gl.glDisable(3553); - anInt1803 = -1; - gl.glTexEnvi(8960, 8704, '\u8570'); - gl.glTexEnvi(8960, '\u8571', 8448); - anInt1793 = 0; - gl.glTexEnvi(8960, '\u8572', 8448); - anInt1792 = 0; - gl.glEnable(2896); - gl.glEnable(2912); - gl.glEnable(2929); - aBoolean1796 = true; - aBoolean1805 = true; - aBoolean1816 = true; - Class44.method1073(); - gl.glActiveTexture('\u84c1'); - gl.glTexEnvi(8960, 8704, '\u8570'); - gl.glTexEnvi(8960, '\u8571', 8448); - gl.glTexEnvi(8960, '\u8572', 8448); - gl.glActiveTexture('\u84c0'); - gl.setSwapInterval(0); - gl.glClearColor(0.0F, 0.0F, 0.0F, 0.0F); - gl.glShadeModel(7425); - gl.glClearDepth(1.0D); - gl.glDepthFunc(515); - method1830(); - gl.glMatrixMode(5890); - gl.glLoadIdentity(); - gl.glPolygonMode(1028, 6914); - gl.glEnable(2884); - gl.glCullFace(1029); - gl.glEnable(3042); - gl.glBlendFunc(770, 771); - gl.glEnable(3008); - gl.glAlphaFunc(516, 0.0F); - gl.glEnableClientState('\u8074'); - gl.glEnableClientState('\u8075'); - aBoolean1798 = true; - gl.glEnableClientState('\u8076'); - gl.glEnableClientState('\u8078'); - gl.glMatrixMode(5888); - gl.glLoadIdentity(); - Class92.method1511(); - Class68.method1275(); - } - - static void method1830() { - gl.glDepthMask(true); - } - - static void method1831(boolean var0) { - if (var0 != aBoolean1805) { - if (var0) { - gl.glEnable(2929); - } else { - gl.glDisable(2929); - } - - aBoolean1805 = var0; - } - } - - static void method1832(float var0) { - method1825(3000.0F, var0 * 1.5F); - } - - static void method1833() { - int[] var0 = new int[2]; - gl.glGetIntegerv(3073, var0, 0); - gl.glGetIntegerv(3074, var0, 1); - gl.glDrawBuffer(1026); - gl.glReadBuffer(1024); - bindTexture2D(-1); - gl.glPushAttrib(8192); - gl.glDisable(2912); - gl.glDisable(3042); - gl.glDisable(2929); - gl.glDisable(3008); - gl.glRasterPos2i(0, 0); - gl.glCopyPixels(0, 0, viewWidth, viewHeight, 6144); - gl.glPopAttrib(); - gl.glDrawBuffer(var0[0]); - gl.glReadBuffer(var0[1]); - } - - /* - * HD -> SD - * Clears the canvas of the OpenGL draw calls - * TODO: Review this again, because although there are - * no current issues with the swapping between HD -> SD - * theoretically this should work in reverse as well. (but doesn't) - */ - static void method1834(Canvas canvas) { - try { - if (!canvas.isDisplayable()) { - return; - } - javax.media.opengl.GLDrawableFactory var1 = javax.media.opengl.GLDrawableFactory.getFactory(); - javax.media.opengl.GLDrawable var2 = var1.getGLDrawable(canvas, null, null); - var2.setRealized(true); - javax.media.opengl.GLContext var3 = var2.createContext(null); - var3.makeCurrent(); - var3.release(); - var3.destroy(); - var2.setRealized(false); - } catch (GLException var4) { - } - - } - - public static void method1835() { - Unsorted.method551(0, 0); - method1836(); - bindTexture2D(-1); - method1837(false); - method1831(false); - method1827(false); - method1823(); - } - - private static void method1836() { - if (!viewportSetup) { - gl.glMatrixMode(5889); - gl.glLoadIdentity(); - gl.glOrtho(0.0D, viewWidth, 0.0D, viewHeight, -1.0D, 1.0D); - gl.glViewport(0, 0, viewWidth, viewHeight); - gl.glMatrixMode(5888); - gl.glLoadIdentity(); - viewportSetup = true; - } - } - - static void method1837(boolean var0) { - if (var0 != aBoolean1796) { - if (var0) { - gl.glEnable(2896); - } else { - gl.glDisable(2896); - } - - aBoolean1796 = var0; - } - } - - static float method1839() { - return aFloat1794; - } - - private static int method1840() { - int var0 = 0; - vendor = gl.glGetString(7936); - renderer = gl.glGetString(7937); - String var1 = vendor.toLowerCase(); - if (var1.contains("microsoft")) { - var0 |= 1; - } - - if (var1.contains("brian paul") || var1.contains("mesa")) { - var0 |= 1; - } - - String versionString = gl.glGetString(7938); - String[] var3 = versionString.split("[. ]"); - if (var3.length >= 2) { - try { - int var4 = Integer.parseInt(var3[0]); - int var5 = Integer.parseInt(var3[1]); - anInt1812 = var4 * 10 + var5; - } catch (NumberFormatException var11) { - var0 |= 4; - } - } else { - var0 |= 4; - } - - if (anInt1812 < 12) { - var0 |= 2; - } - - if (!gl.isExtensionAvailable("GL_ARB_multitexture")) { - var0 |= 8; - } - - if (!gl.isExtensionAvailable("GL_ARB_texture_env_combine")) { - var0 |= 32; - } - - int[] var12 = new int[1]; - gl.glGetIntegerv('\u84e2', var12, 0); - maxTextureUnits = var12[0]; - gl.glGetIntegerv('\u8871', var12, 0); - int anInt1814 = var12[0]; - gl.glGetIntegerv('\u8872', var12, 0); - int anInt1806 = var12[0]; - if (maxTextureUnits < 2 || anInt1814 < 2 || anInt1806 < 2) { - var0 |= 16; - } - - if (var0 == 0) { - aBoolean1790 = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; - supportVertexBufferObject = gl.isExtensionAvailable("GL_ARB_vertex_buffer_object"); - supportMultisample = gl.isExtensionAvailable("GL_ARB_multisample"); - supportTextureCubeMap = gl.isExtensionAvailable("GL_ARB_texture_cube_map"); - supportVertexProgram = gl.isExtensionAvailable("GL_ARB_vertex_program"); - allows3DTextureMapping = gl.isExtensionAvailable("GL_EXT_texture3D"); - RSString var13 = method1820(renderer).toLowercase(); - if (var13.indexOf(RSString.parse("radeon"), 57) != -1) { - int version = 0; - RSString[] var7 = var13.method1565().method1567(32, (byte) -98); - - for (RSString var9 : var7) { - if (var9.length() >= 4 && var9.substring(0, 4, 0).isInteger()) { - version = var9.substring(0, 4, 0).parseInt(); - break; - } - } - - if (version >= 7000 && version <= 7999) { - supportVertexBufferObject = false; - } - - if (version >= 7000 && version <= 9250) { - allows3DTextureMapping = false; - } - - aBoolean1817 = supportVertexBufferObject; - } - - if (supportVertexBufferObject) { - try { - int[] var14 = new int[1]; - gl.glGenBuffersARB(1, var14, 0); - } catch (Throwable var10) { - return -4; - } - } - - return 0; - } else { - return var0; - } - } - - static void method1841() { - gl.glClear(256); - } - - static void method1842() { - if (gl != null) { - try { - Class101.method1609(); - } catch (GLException var4) { - } - - gl = null; - } - - if (glContext != null) { - Class31.method988(); - - try { - if (javax.media.opengl.GLContext.getCurrent() == glContext) { - glContext.release(); - } - } catch (GLException var3) { - } - - try { - glContext.destroy(); - } catch (GLException var2) { - } - - glContext = null; - } - - if (glDrawable != null) { - try { - glDrawable.setRealized(false); - } catch (GLException var1) { - } - - glDrawable = null; - } - - Class68.method1273(); - highDetail = false; - SlayerTracker.setSprite(); - } - - static void method1843(float var0, float var1) { - gl.glMatrixMode(5890); - if (aBoolean1788) { - gl.glLoadIdentity(); - } - - gl.glTranslatef(var0, var1, (float) 0.0); - gl.glMatrixMode(5888); - aBoolean1788 = true; - } - - static void viewport(int x, int y, int width, int height, int offsetX, int offsetY, float rotationX, float rotationY, int ratioWidth, int ratioHeight) { - int left = (x - offsetX << 8) / ratioWidth; - int right = (x + width - offsetX << 8) / ratioWidth; - int top = (y - offsetY << 8) / ratioHeight; - int bottom = (y + height - offsetY << 8) / ratioHeight; - gl.glMatrixMode(5889); - gl.glLoadIdentity(); - float constantFloat = 0.09765625F; - method1848((float) left * constantFloat, (float) right * constantFloat, (float) (-bottom) * constantFloat, (float) (-top) * constantFloat, 50.0F, GameConfig.RENDER_DISTANCE_VALUE); - gl.glViewport(x, viewHeight - y - height, width, height); - gl.glMatrixMode(5888); - gl.glLoadIdentity(); - gl.glRotatef(180.0F, 1.0F, 0.0F, 0.0F); - if (rotationX != 0.0F) { - gl.glRotatef(rotationX, 1.0F, 0.0F, 0.0F); - } - - if (rotationY != 0.0F) { - gl.glRotatef(rotationY, 0.0F, 1.0F, 0.0F); - } - - viewportSetup = false; - Class139.screenLowerX = left; - Class145.screenUpperX = right; - Class1.screenUpperY = top; - AtmosphereParser.screenLowerY = bottom; - } - - private static void method1845(boolean var0) { - if (var0 != aBoolean1798) { - if (var0) { - gl.glEnableClientState('\u8075'); - } else { - gl.glDisableClientState('\u8075'); - } - - aBoolean1798 = var0; - } - } - - static void method1846() { - if (Class106.aBoolean1441) { - method1837(true); - method1845(true); - } else { - method1837(false); - method1845(false); - } - - } - - static void method1847(int var0) { - if (var0 != anInt1792) { - //sets a texture environment parameter. - //TEXTURE_ENV, COMBINE_ALPHA, - if (var0 == 0) { - gl.glTexEnvi(8960, '\u8572', 8448);//MODULATE - } - if (var0 == 1) { - gl.glTexEnvi(8960, '\u8572', 7681);//REPLACE - } - if (var0 == 2) { - gl.glTexEnvi(8960, '\u8572', 260);//ADD - } - anInt1792 = var0; - } - } - - private static void method1848(float left, float right, float bottom, float top, float constantFloat, float renderDistance) { - float var6 = constantFloat * 2.0F; - aFloatArray1808[0] = var6 / (right - left); - aFloatArray1808[1] = 0.0F; - aFloatArray1808[2] = 0.0F; - aFloatArray1808[3] = 0.0F; - aFloatArray1808[4] = 0.0F; - aFloatArray1808[5] = var6 / (top - bottom); - aFloatArray1808[6] = 0.0F; - aFloatArray1808[7] = 0.0F; - aFloatArray1808[8] = (right + left) / (right - left); - aFloatArray1808[9] = (top + bottom) / (top - bottom); - aFloatArray1808[10] = aFloat1787 = -(renderDistance + constantFloat) / (renderDistance - constantFloat); - aFloatArray1808[11] = -1.0F; - aFloatArray1808[12] = 0.0F; - aFloatArray1808[13] = 0.0F; - aFloatArray1808[14] = aFloat1795 = -(var6 * renderDistance) / (renderDistance - constantFloat); - aFloatArray1808[15] = 0.0F; - gl.glLoadMatrixf(aFloatArray1808, 0); - aFloat1797 = 0.0F; - aFloat1794 = 0.0F; - } - - /** - * clearScreen takes an int of color (can be replaced with whatever color you see fit) - * - * @param color - */ - static void clearScreen(int color) { - gl.glClearColor((float) (color >> 16 & 0xFF) / 255.0F, (float) (color >> 8 & 0xFF) / 255.0F, (float) (color & 0xFF) / 255.0F, 0.0F); - gl.glClear(16640); - gl.glClearColor(0.0F, 0.0F, 0.0F, 0.0F); - } - - static void bindTexture2D(int var0) { - if (var0 != anInt1803) { - if (var0 == -1) { - gl.glDisable(3553); - } else { - if (anInt1803 == -1) { - gl.glEnable(3553); - } - - gl.glBindTexture(3553, var0); - } - - anInt1803 = var0; - } - } - - static void depthBufferWritingDisabled() { - gl.glDepthMask(false); - } - - static float method1852() { - return aFloat1797; - } - - static void method1853(Canvas canvas, int SceneMSAASamples) { - - try { - if (canvas.isDisplayable()) { - - /* - Edited out here is the old JOGL implementation. It was removed due to lack of - support for MacOS + Linux. There was a problem with the threads and Linux users - were unable to interact with the canvas. - */ - //GLProfile.initSingleton(); -// GLCapabilities glCapabilities = new GLCapabilities(GLProfile.getDefault()); -// System.out.println("Scene MSAASamples = " + SceneMSAASamples); -// if (SceneMSAASamples > 0) { -// glCapabilities.setSampleBuffers(true); -// glCapabilities.setNumSamples(SceneMSAASamples); -// } -// -// -// AWTGraphicsConfiguration configuration = AWTGraphicsConfiguration.create(canvas.getGraphicsConfiguration(), glCapabilities, glCapabilities); -// NativeWindow nativeWindow = NativeWindowFactory.getNativeWindow(canvas, configuration); -// GLDrawableFactory glDrawableFactory = GLDrawableFactory.getDesktopFactory(); -// glDrawable = glDrawableFactory.createGLDrawable(nativeWindow); -// glDrawable.setRealized(true); - GLCapabilities var2 = new GLCapabilities(); - if (SceneMSAASamples > 0) { - var2.setSampleBuffers(true); - var2.setNumSamples(SceneMSAASamples * 128); - } - - GLDrawableFactory var3 = GLDrawableFactory.getFactory(); - glDrawable = var3.getGLDrawable(canvas, var2, null); - glDrawable.setRealized(true); - - int var4 = 0; - int var5; - while (true) { - glContext = glDrawable.createContext(null); - - try { - var5 = glContext.makeCurrent(); - if (var5 != 0) { - break; - } - } catch (GLException var8) { - } - - if (var4++ > 5) { - return; - } - - TimeUtils.sleep(1000L); - } - - gl = glContext.getGL(); - new GLU(); - highDetail = true; - SlayerTracker.setSprite(); - System.out.println("Setting high detail to " + highDetail); - viewWidth = canvas.getSize().width; - viewHeight = canvas.getSize().height; - var5 = method1840(); - if (var5 == 0) { - method1857(); - method1829(); - gl.glClear(16384); - var4 = 0; - - while (true) { - try { - glDrawable.swapBuffers(); - break; - } catch (GLException var7) { - if (var4++ > 5) { - method1842(); - return; - } - - TimeUtils.sleep(100L); - } - } - - gl.glClear(16384); - } else { - method1842(); - } - } else { - } - } catch (GLException var9) { - System.err.println("Failed to enter HD"); - method1842(); - } - } - - static void method1854(int var0, int var1) { - viewWidth = var0; - viewHeight = var1; - viewportSetup = false; - } - - static void method1855(int var0, int var1, int var2, int var3, int var4, int var5) { - int var6 = -var0; - int var7 = viewWidth - var0; - int var8 = -var1; - int var9 = viewHeight - var1; - gl.glMatrixMode(5889); - gl.glLoadIdentity(); - float var10 = (float) var2 / 512.0F; - float var11 = var10 * (256.0F / (float) var4); - float var12 = var10 * (256.0F / (float) var5); - gl.glOrtho((float) var6 * var11, (float) var7 * var11, (float) (-var9) * var12, (float) (-var8) * var12, 50 - var3, 3584 - var3); - gl.glViewport(0, 0, viewWidth, viewHeight); - gl.glMatrixMode(5888); - gl.glLoadIdentity(); - gl.glRotatef(180.0F, 1.0F, 0.0F, 0.0F); - viewportSetup = false; - } - - static void method1856(int var0) { - if (var0 != anInt1793) { - if (var0 == 0) { - gl.glTexEnvi(8960, '\u8571', 8448); - } - - if (var0 == 1) { - gl.glTexEnvi(8960, '\u8571', 7681); - } - - if (var0 == 2) { - gl.glTexEnvi(8960, '\u8571', 260); - } - - if (var0 == 3) { - gl.glTexEnvi(8960, '\u8571', '\u84e7'); - } - - if (var0 == 4) { - gl.glTexEnvi(8960, '\u8571', '\u8574'); - } - - if (var0 == 5) { - gl.glTexEnvi(8960, '\u8571', '\u8575'); - } - - anInt1793 = var0; - } - } - - private static void method1857() { - int[] var0 = new int[1]; - gl.glGenTextures(1, var0, 0); - anInt1810 = var0[0]; - gl.glBindTexture(3553, anInt1810); - gl.glTexImage2D(3553, 0, 4, 1, 1, 0, 6408, 5121, IntBuffer.wrap(new int[]{-1})); - Class68.method1276(); - Class3_Sub24_Sub3.method468(); - } - -} diff --git a/Client/src/main/java/org/runite/client/ISAACCipher.java b/Client/src/main/java/org/runite/client/ISAACCipher.java deleted file mode 100644 index fc9ffdb37..000000000 --- a/Client/src/main/java/org/runite/client/ISAACCipher.java +++ /dev/null @@ -1,205 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; -import org.rs09.client.rendering.Toolkit; - - -final class ISAACCipher { - - private int anInt966; - private int anInt967; - private int anInt968; - private final int[] anIntArray970; - private final int[] anIntArray971; - private int anInt972; - - - private void method1227() { - try { - - int var10 = -1640531527; - int var9 = -1640531527; - int var8 = -1640531527; - int var7 = -1640531527; - int var6 = -1640531527; - int var5 = -1640531527; - int var4 = -1640531527; - int var3 = -1640531527; - - int var2; - for (var2 = 0; 4 > var2; ++var2) { - var3 ^= var4 << 11; - var6 += var3; - var4 += var5; - var4 ^= var5 >>> 2; - var5 += var6; - var5 ^= var6 << 8; - var8 += var5; - var7 += var4; - var6 += var7; - var6 ^= var7 >>> 16; - var7 += var8; - var9 += var6; - var7 ^= var8 << 10; - var10 += var7; - var8 += var9; - var8 ^= var9 >>> 4; - var9 += var10; - var9 ^= var10 << 8; - var4 += var9; - var3 += var8; - var10 += var3; - var10 ^= var3 >>> 9; - var5 += var10; - var3 += var4; - } - - for (var2 = 0; var2 < 256; var2 += 8) { - var6 += this.anIntArray970[3 + var2]; - var7 += this.anIntArray970[var2 - -4]; - var9 += this.anIntArray970[var2 + 6]; - var3 += this.anIntArray970[var2]; - var5 += this.anIntArray970[2 + var2]; - var8 += this.anIntArray970[var2 - -5]; - var10 += this.anIntArray970[7 + var2]; - var4 += this.anIntArray970[var2 - -1]; - var3 ^= var4 << 11; - var4 += var5; - var4 ^= var5 >>> 2; - var6 += var3; - var5 += var6; - var5 ^= var6 << 8; - var7 += var4; - var6 += var7; - var6 ^= var7 >>> 16; - var8 += var5; - var7 += var8; - var7 ^= var8 << 10; - var10 += var7; - var9 += var6; - var8 += var9; - var8 ^= var9 >>> 4; - var9 += var10; - var9 ^= var10 << 8; - var3 += var8; - var10 += var3; - var4 += var9; - var10 ^= var3 >>> 9; - var3 += var4; - this.anIntArray971[var2] = var3; - this.anIntArray971[1 + var2] = var4; - var5 += var10; - this.anIntArray971[2 + var2] = var5; - this.anIntArray971[var2 + 3] = var6; - this.anIntArray971[4 + var2] = var7; - this.anIntArray971[5 + var2] = var8; - this.anIntArray971[var2 - -6] = var9; - this.anIntArray971[7 + var2] = var10; - } - - for (var2 = 0; var2 < 256; var2 += 8) { - var9 += this.anIntArray971[6 + var2]; - var8 += this.anIntArray971[var2 + 5]; - var7 += this.anIntArray971[4 + var2]; - var4 += this.anIntArray971[var2 + 1]; - var5 += this.anIntArray971[2 + var2]; - var3 += this.anIntArray971[var2]; - var3 ^= var4 << 11; - var6 += this.anIntArray971[var2 + 3]; - var10 += this.anIntArray971[var2 + 7]; - var6 += var3; - var4 += var5; - var4 ^= var5 >>> 2; - var5 += var6; - var7 += var4; - var5 ^= var6 << 8; - var8 += var5; - var6 += var7; - var6 ^= var7 >>> 16; - var7 += var8; - var7 ^= var8 << 10; - var9 += var6; - var8 += var9; - var8 ^= var9 >>> 4; - var10 += var7; - var9 += var10; - var3 += var8; - var9 ^= var10 << 8; - var4 += var9; - var10 += var3; - var10 ^= var3 >>> 9; - var5 += var10; - var3 += var4; - this.anIntArray971[var2] = var3; - this.anIntArray971[1 + var2] = var4; - this.anIntArray971[var2 - -2] = var5; - this.anIntArray971[3 + var2] = var6; - this.anIntArray971[4 + var2] = var7; - this.anIntArray971[5 + var2] = var8; - this.anIntArray971[var2 + 6] = var9; - this.anIntArray971[var2 - -7] = var10; - } - - this.method1229(); - this.anInt968 = 256; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "ij.E(" + true + ')'); - } - } - - final int nextOpcode() { - try { - if (this.anInt968-- == 0) { - this.method1229(); - this.anInt968 = 255; - } - return GameConfig.ISAAC ? this.anIntArray970[this.anInt968] : 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ij.C(" + ')'); - } - } - - private void method1229() { - try { - this.anInt967 += ++this.anInt966; - int var2 = 0; - while (var2 < 256) { - int var3 = this.anIntArray971[var2]; - if ((2 & var2) != 0) { - if ((var2 & 1) == 0) { - this.anInt972 ^= this.anInt972 << 2; - } else { - this.anInt972 ^= this.anInt972 >>> 16; - } - } else if ((var2 & 1) == 0) { - this.anInt972 ^= this.anInt972 << 13; - } else { - this.anInt972 ^= this.anInt972 >>> 6; - } - - this.anInt972 += this.anIntArray971[128 + var2 & 0xFF]; - int var4; - this.anIntArray971[var2] = var4 = this.anInt967 + this.anInt972 + this.anIntArray971[Unsorted.bitwiseAnd(var3, 1020) >> 2]; - this.anIntArray970[var2] = this.anInt967 = var3 + this.anIntArray971[Unsorted.bitwiseAnd(261347, var4) >> 8 >> 2]; - ++var2; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ij.A(" + ')'); - } - } - - ISAACCipher(int[] var1) { - try { - this.anIntArray970 = new int[256]; - this.anIntArray971 = new int[256]; - - System.arraycopy(var1, 0, this.anIntArray970, 0, var1.length); - - this.method1227(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ij.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Interface1.java b/Client/src/main/java/org/runite/client/Interface1.java deleted file mode 100644 index 56d9fb7a1..000000000 --- a/Client/src/main/java/org/runite/client/Interface1.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.runite.client; - -import java.awt.*; - -public interface Interface1 { - - void method1(int var1, int var2); - - int method2(byte var1, int var2); - - void method3(int var1, int var2, int var3); - - void method4(byte var1, int var2); - - void method5(int var1, byte var2, Component var3, boolean var4); - - void method6(int var1, int[] var2); -} diff --git a/Client/src/main/java/org/runite/client/Interface2.java b/Client/src/main/java/org/runite/client/Interface2.java deleted file mode 100644 index fa1f48358..000000000 --- a/Client/src/main/java/org/runite/client/Interface2.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.runite.client; - -interface Interface2 { - - boolean method7(byte var1, int var2); - - void method8(int var1, boolean var2); - - int method9(int var1, boolean var2); - - int method10(int var1, int var2); - - boolean method11(int var1, int var2); - - boolean method12(int var1, int var2); - - int[] method13(int var1, boolean var2, float var3); - - boolean method14(byte var1, int var2); - - int method15(int var1, int var2); - - int[] method16(int var1, int var2); - - boolean method17(int var1, int var2); - - int method18(int var1, int var2); - - int method19(int var1, int var2); -} diff --git a/Client/src/main/java/org/runite/client/Interface3.java b/Client/src/main/java/org/runite/client/Interface3.java deleted file mode 100644 index 49f0ed713..000000000 --- a/Client/src/main/java/org/runite/client/Interface3.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.runite.client; - -interface Interface3 { -} diff --git a/Client/src/main/java/org/runite/client/Interface4.java b/Client/src/main/java/org/runite/client/Interface4.java deleted file mode 100644 index 81074a11d..000000000 --- a/Client/src/main/java/org/runite/client/Interface4.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.runite.client; - -interface Interface4 { - - RSString method20(int var1, int[] var2, int var3, long var4); -} diff --git a/Client/src/main/java/org/runite/client/InterfaceWidget.java b/Client/src/main/java/org/runite/client/InterfaceWidget.java deleted file mode 100644 index 4029eeba2..000000000 --- a/Client/src/main/java/org/runite/client/InterfaceWidget.java +++ /dev/null @@ -1,267 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; -import org.rs09.client.config.GameConfig; - -final class InterfaceWidget extends Node { - - static boolean aBoolean3594; - static boolean aBoolean3668 = false; - static int anInt2293 = -1; - static float aFloat1169; - int anInt3596; - int anInt3597; - int anInt3598; - RSString text; - static int anInt3600; - - static InterfaceWidget getWidget(int var1, int var2) { - try { - InterfaceWidget var3 = (InterfaceWidget)Client.aHashTable_2194.get((long)var2 | (long)var1 << 32); - if(null == var3) { - var3 = new InterfaceWidget(var1, var2); - Client.aHashTable_2194.put(var3.linkableKey, var3); - } - - return var3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "te.F(" + 4 + ',' + var1 + ',' + var2 + ')'); - } - } - - static void setWidgetText(RSString text, int var2) { - try { - InterfaceWidget var3 = getWidget(2, var2); - - var3.flagUpdate(); - var3.text = text; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "pi.B(" + (text != null?"{...}":"null") + ',' + (byte) 40 + ',' + var2 + ')'); - } - } - - - final void a() { - try { - this.nodeKey = TimeUtils.time() - -500L | Long.MIN_VALUE & this.nodeKey; - Class81.aClass13_1139.offer(this); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "da.U(" + true + ')'); - } - } - - static void d(int var0) { - try { - if(NPCDefinition.anInt1252 != -1 && anInt2293 != -1) { - int var1 = (Class163_Sub2_Sub1.anInt4020 * (-Class134.anInt1759 + TextureOperation.anInt2383) >> 16) + Class134.anInt1759; - float[] var3 = new float[3]; - Class163_Sub2_Sub1.anInt4020 += var1; - if(Class163_Sub2_Sub1.anInt4020 >= 65535) { - Class163_Sub2_Sub1.anInt4020 = 65535; - Class3_Sub28_Sub1.aBoolean3531 = !aBoolean3668; - - aBoolean3668 = true; - } else { - aBoolean3668 = false; - Class3_Sub28_Sub1.aBoolean3531 = false; - } - - if(var0 == 65535) { - float var2 = (float)Class163_Sub2_Sub1.anInt4020 / 65535.0F; - int var4 = Unsorted.anInt1081 * 2; - - int var6; - int var7; - int var8; - int var9; - int var10; - int var11; - int var12; - for(int var5 = 0; var5 < 3; ++var5) { - var8 = (Class58.anIntArrayArrayArray911[NPCDefinition.anInt1252][var4 - -2][var5] + -Class58.anIntArrayArrayArray911[NPCDefinition.anInt1252][var4 - -3][var5] - -Class58.anIntArrayArrayArray911[NPCDefinition.anInt1252][var4 - -2][var5]) * 3; - var9 = Class58.anIntArrayArrayArray911[NPCDefinition.anInt1252][var4][var5]; - var7 = 3 * Class58.anIntArrayArrayArray911[NPCDefinition.anInt1252][var4 + 1][var5]; - var6 = 3 * Class58.anIntArrayArrayArray911[NPCDefinition.anInt1252][var4][var5]; - var10 = -var6 + var7; - var11 = var8 + -(var7 * 2) + var6; - var12 = Class58.anIntArrayArrayArray911[NPCDefinition.anInt1252][2 + var4][var5] + -var9 - -var7 - var8; - var3[var5] = (float)var9 + (((float)var12 * var2 + (float)var11) * var2 + (float)var10) * var2; - } - - Class7.anInt2162 = -1 * (int)var3[1]; - NPC.anInt3995 = (int)var3[0] + -(128 * Class131.anInt1716); - Class77.anInt1111 = (int)var3[2] + -(Texture.anInt1152 * 128); - float[] var16 = new float[3]; - var6 = Class39.anInt670 * 2; - - for(var7 = 0; var7 < 3; ++var7) { - var8 = Class58.anIntArrayArrayArray911[anInt2293][var6][var7] * 3; - var10 = (Class58.anIntArrayArrayArray911[anInt2293][2 + var6][var7] - Class58.anIntArrayArrayArray911[anInt2293][3 + var6][var7] + Class58.anIntArrayArrayArray911[anInt2293][var6 - -2][var7]) * 3; - var11 = Class58.anIntArrayArrayArray911[anInt2293][var6][var7]; - var9 = Class58.anIntArrayArrayArray911[anInt2293][1 + var6][var7] * 3; - var12 = var9 + -var8; - int var13 = var10 + var8 + -(2 * var9); - int var14 = -var10 + Class58.anIntArrayArrayArray911[anInt2293][var6 - -2][var7] + -var11 + var9; - var16[var7] = (float)var11 + var2 * (var2 * (var2 * (float)var14 + (float)var13) + (float)var12); - } - - float var17 = -var3[0] + var16[0]; - float var19 = var16[2] - var3[2]; - float var18 = (-var3[1] + var16[1]) * -1.0F; - double var20 = Math.sqrt(var19 * var19 + var17 * var17); - aFloat1169 = (float)Math.atan2(var18, var20); - Class45.aFloat730 = -((float)Math.atan2(var17, var19)); - Class139.anInt1823 = 2047 & (int)(325.949D * (double) aFloat1169); - TextureOperation28.anInt3315 = 2047 & (int)((double)Class45.aFloat730 * 325.949D); - } - } - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "da.Q(" + var0 + ')'); - } - } - - final long b() { - try { - - return this.nodeKey & Long.MAX_VALUE; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "da.A(" + true + ')'); - } - } - - static AbstractIndexedSprite a(int var0, CacheIndex var1) { - try { - return !Class140_Sub7.method2029((byte)-127, var1, var0)?null:(Class166.method2259()); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "da.C(" + var0 + ',' + (var1 != null?"{...}":"null") + ',' + true + ')'); - } - } - - final int e() { - try { - return (int)(255L & this.linkableKey >>> 32); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "da.D()"); - } - } - - final int f() { - try { - return (int)this.linkableKey; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "da.P(" + (byte) 117 + ')'); - } - } - - static MapSceneDefinition c(int var0) { - try { - MapSceneDefinition var2 = (MapSceneDefinition) TextureOperation25.aReferenceCache_3412.get(var0); - if(var2 == null) { - byte[] var3 = Class107.aClass153_878.getFile(34, var0); - - var2 = new MapSceneDefinition(); - if(var3 != null) { - var2.decode(new DataBuffer(var3)); - } - - TextureOperation25.aReferenceCache_3412.put(var2, var0); - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "da.R(" + var0 + ',' + 0 + ')'); - } - } - - static String a(String var0, String var1, String var3) { - for(int var4 = var3.indexOf(var0); var4 != -1; var4 = var3.indexOf(var0, var4 + var1.length())) { - var3 = var3.substring(0, var4) + var1 + var3.substring(var0.length() + var4); - } - - return var3; - } - - static void a(int var0, int var1, int var2, RSInterface var3) { - try { - if(null == Class56.aClass11_886 && !Class38_Sub1.aBoolean2615) { - if(var2 < 61) { - a(19, 20, -32, null); - } - - if(null != var3 && Class49.method1122(var3) != null) { - Class56.aClass11_886 = var3; - PacketParser.aClass11_88 = Class49.method1122(var3); - Unsorted.anInt1881 = var1; - NPC.aBoolean3975 = false; - Class75_Sub3.anInt2658 = 0; - Class95.anInt1336 = var0; - } - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "da.S(" + var0 + ',' + var1 + ',' + var2 + ',' + (var3 != null?"{...}":"null") + ')'); - } - } - - static void a(int var0, int var2, int var3, RSString var4, long var5, int var7, RSString name) { - try { - System.out.println("Class3_Sub28_Sub6 " + var0 + ", " + 10603 + ", " + var2 + ", " + var3 + ", " + var4 + ", " + var5 + ", " + var7); - DataBuffer var8 = new DataBuffer(129); - var8.writeByte(10); - var8.writeShort((int)(Math.random() * 99999.0D)); - var8.writeShort(GameConfig.CLIENT_BUILD); - var8.writeString(name); - var8.writeInt((int)(Math.random() * 9.9999999E7D)); - var8.writeString(var4); - var8.writeInt((int)(Math.random() * 9.9999999E7D)); - var8.writeShort(Class3_Sub26.paramAffid); - var8.writeByte(var0); - var8.writeByte(var3); - var8.writeInt((int)(Math.random() * 9.9999999E7D)); - var8.writeShort(var7); - var8.writeShort(var2); - var8.writeInt((int)(9.9999999E7D * Math.random())); - var8.rsaEncrypt(TextureOperation10.EXPONENT, TextureOperation31.MODULUS); - TextureOperation12.outgoingBuffer.index = 0; - TextureOperation12.outgoingBuffer.writeByte(36); - TextureOperation12.outgoingBuffer.writeByte(var8.index); - TextureOperation12.outgoingBuffer.putBytes(var8.buffer, var8.index); - Unsorted.anInt1711 = -3; - Unsorted.registryStage = 1; - Class132.anInt1734 = 0; - GraphicDefinition.anInt548 = 0; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "da.B(" + var0 + ',' + 10603 + ',' + var2 + ',' + var3 + ',' + (var4 != null?"{...}":"null") + ',' + var5 + ',' + var7 + ')'); - } - } - - final void flagUpdate() { - try { - this.nodeKey |= Long.MIN_VALUE; - if(this.b() == 0) { - Class126.aClass13_1666.offer(this); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "da.T(" + (byte) 33 + ')'); - } - } - - InterfaceWidget(int var1, int var2) { - try { - this.linkableKey = (long)var1 << 32 | (long)var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "da.(" + var1 + ',' + var2 + ')'); - } - } - -// static void h() { -// try { -// Class114.aReferenceCache_1569.clearSoftReferences(); -// -// Class3_Sub15.aReferenceCache_2428.clearSoftReferences(); -// Unsorted.aReferenceCache_743.clearSoftReferences(); -// } catch (RuntimeException var2) { -// throw ClientErrorException.clientError(var2, "da.E(" + (byte) 3 + ')'); -// } -// } - -} diff --git a/Client/src/main/java/org/runite/client/InvalidateData.java b/Client/src/main/java/org/runite/client/InvalidateData.java deleted file mode 100644 index 680d9f908..000000000 --- a/Client/src/main/java/org/runite/client/InvalidateData.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.runite.client; - -public class InvalidateData { - static void method165() { - try { - WorldListEntry.aAbstractSprite_1339 = null; - WorldListEntry.aAbstractSprite_3099 = null; - Class50.aAbstractSprite_824 = null; - - WorldListEntry.aAbstractSprite_1457 = null; - Class3_Sub26.aAbstractSprite_2560 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "j.VA(" + -7878 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/ItemDefinition.java b/Client/src/main/java/org/runite/client/ItemDefinition.java deleted file mode 100644 index 3ddae880d..000000000 --- a/Client/src/main/java/org/runite/client/ItemDefinition.java +++ /dev/null @@ -1,874 +0,0 @@ -package org.runite.client; -import org.rs09.SystemLogger; -import org.rs09.client.Linkable; -import org.rs09.client.data.HashTable; -import org.rs09.client.LinkableInt; - -import java.awt.Component; -import java.util.Objects; - -final class ItemDefinition { - - static short aShort505 = 1; - static RSString[] stringsStack = new RSString[1000]; - static int[] intsStack = new int[1000]; - static int scriptHeapCounter = 0; - // This holds all the int memory that can be accessed by the pseudo assembly by value. - static int[] ram = new int[2500]; - // This holds even MORE memory which has five unique pages. It is slower (presumably) than regular ram - // and can be accessed through opcodes 44-46. You can fill it with a given value - // in a single instruction though - static int[][] pagedRam = new int[5][5000]; - // This shows how much of the pagedRam is actually filled for any given page. - static int[] pagedRamPageSize = new int[5]; - - static RSString[] stringArguments; - static int[] intArguments; - // This is the TRUE stack. the one with a stack pointer and stack tracing. - // This holds all the methods you are currently calling in your script. - static AssembledMethodContainer[] methodStack = new AssembledMethodContainer[50]; - private short[] aShortArray751; - private int anInt752; - private int anInt753 = -1; - int anInt754; - private int anInt755; - int anInt756 = -1; - int value = 1; - int anInt758; - private int wornModelPositionX = 0; - int anInt761; - int anInt762; - int stackingType; - private short[] aShortArray765; - int[] anIntArray766; - int anInt767; - int anInt768; - private int anInt769; - RSString name; - private int anInt771; - private short[] aShortArray772; - private int anInt773 = -1; - private short[] aShortArray774; - private int wornModelPositionY; - private int anInt776 = -1; - private int anInt777; - private int wornModelPositionZ; - boolean membersItem; - private int anInt780; - static int[] anIntArray781 = new int[99]; - int teamId = 0; - RSString[] inventoryOptions; - private int anInt784; - private byte[] aByteArray785; - int anInt786; - int itemId; - int anInt788; - public int noteID; - private int anInt790; - int anInt791; - int anInt792; - int anInt793; - private int anInt794; - int anInt795; - private int anInt796; - private int anInt797; - HashTable aHashTable_798; - int anInt799; - int anInt800; - RSString[] groundOptions; - private int anInt802; - private int anInt803; - int[] anIntArray804; - private int anInt805; - boolean aBoolean807; - - int anInt810; - private static RSString aClass94_811; - - boolean itemContextMenuDebug = true; - - static boolean method1176(RSString var0) { - try { - if(var0 == null) { - return false; - } else { - for(int var2 = 0; Class8.anInt104 > var2; ++var2) { - if(var0.equalsStringIgnoreCase(Class70.aClass94Array1046[var2])) { - return true; - } - } - - - return var0.equalsStringIgnoreCase(Class102.player.displayName); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hj.A(" + "{...}" + ',' + (byte) -82 + ')'); - } - } - - static AssembledMethod getMethodByID(int methodID) { - try { - // Load and return method from cache - AssembledMethod var2 = (AssembledMethod)Class56.aClass47_885.get(methodID); - if (var2 != null) { - return var2; - } - // Load method from file 0 which holds all the methods. Apparently. - byte[] var3 = CacheIndex.interfaceScriptsIndex.getFile(methodID, 0); - if(var3 == null) { - return null; - } else { - var2 = new AssembledMethod(); - - DataBuffer var4 = new DataBuffer(var3); - var4.index = -2 + var4.buffer.length; - int var5 = var4.readUnsignedShort(); - int var6 = -12 + var4.buffer.length + -2 - var5; - var4.index = var6; - int var7 = var4.readInt(); - var2.numberOfIntsToCopy = var4.readUnsignedShort(); - var2.numberOfRSStringsToCopy = var4.readUnsignedShort(); - var2.numberOfIntArguments = var4.readUnsignedShort(); - var2.numberOfStringArguments = var4.readUnsignedShort(); - int var8 = var4.readUnsignedByte(); - int var9; - int var10; - if(var8 > 0) { - var2.switchHashTable = new HashTable[var8]; - - for(var9 = 0; var9 < var8; ++var9) { - var10 = var4.readUnsignedShort(); - HashTable var11 = new HashTable(Class95.method1585((byte)119, var10)); - var2.switchHashTable[var9] = var11; - - while(var10-- > 0) { - int var12 = var4.readInt(); - int var13 = var4.readInt(); - var11.put(var12, new LinkableInt(var13)); - } - } - } - - var4.index = 0; - var4.method750(); - var2.assemblyInstructions = new int[var7]; - var2.stringInstructionOperands = new RSString[var7]; - var9 = 0; - - for(var2.instructionOperands = new int[var7]; var4.index < var6; var2.assemblyInstructions[var9++] = var10) { - var10 = var4.readUnsignedShort(); - if(var10 == 3) { - var2.stringInstructionOperands[var9] = var4.readString(); - } else if (var10 < 100 && 21 != var10 && var10 != 38 && 39 != var10) { - var2.instructionOperands[var9] = var4.readInt(); - } else { - var2.instructionOperands[var9] = var4.readUnsignedByte(); - } - } - Class56.aClass47_885.put(methodID, var2); - return var2; - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "hc.O(" + methodID + ',' + (byte) -91 + ')'); - } - } - - static ItemDefinition getItemDefinition(int itemId) { - try { - ItemDefinition var2 = (ItemDefinition) QuickChatDefinition.aReferenceCache_3572.get(itemId); - if(var2 == null) { - byte[] var3 = Class97.aClass153_1370.getFile(Class140_Sub2.method1951(itemId), 255 & itemId); - var2 = new ItemDefinition(); - var2.itemId = itemId; - if(var3 != null) { - var2.parseDefinitions(new DataBuffer(var3)); - } - - var2.method1112(); - if(var2.anInt791 != -1) { - var2.method1118(getItemDefinition(var2.noteID), getItemDefinition(var2.anInt791)); - } - - if(var2.anInt762 != -1) { - var2.method1109(getItemDefinition(var2.anInt795), getItemDefinition(var2.anInt762)); - } - - if(!Class139.aBoolean1827 && var2.membersItem) { - var2.name = TextCore.MembersObject; - var2.teamId = 0; - var2.inventoryOptions = ClientErrorException.aClass94Array2119; - var2.aBoolean807 = false; - var2.groundOptions = Unsorted.aClass94Array2596; - } - - QuickChatDefinition.aReferenceCache_3572.put(var2, itemId); - } - return var2; - } catch (RuntimeException var4) { - SystemLogger.logErr("(ItemDefinition.java:208) Item ID higher than max or error parsing for ID " + itemId + " trace: " + var4); - } - return null; - } - - - final boolean method1102(boolean var1) { - try { - int var3 = this.anInt803; - int var4 = this.anInt796; - if(var1) { - var3 = this.anInt773; - var4 = this.anInt753; - } - - if(var3 == -1) { - return true; - } else { - boolean var5 = true; - if(!LinkableRSString.aClass153_2581.method2129((byte)-75, 0, var3)) { - var5 = false; - } - - if(var4 != -1 && !LinkableRSString.aClass153_2581.method2129((byte)58, 0, var4)) { - var5 = false; - } - - return var5; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "h.G(" + var1 + ',' + false + ')'); - } - } - - static void method1103(CacheIndex var0, CacheIndex var1) { - try { - NPCDefinition.aClass153_3173 = var0; - Class29.aClass153_557 = var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "h.B(" + (var0 != null?"{...}":"null") + ',' + (var1 != null?"{...}":"null") + ',' + false + ')'); - } - } - - public RSString method1105(RSString var2, int var3) {//private -> public to move runAssembledScript into CS2Script - try { - if(this.aHashTable_798 == null) { - return var2; - } else { - - LinkableRSString var4 = (LinkableRSString)this.aHashTable_798.get(var3); - return null != var4?var4.value :var2; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "h.S(" + 107 + ',' + (var2 != null?"{...}":"null") + ',' + var3 + ')'); - } - } - - - final ItemDefinition method1106(int var1) { - try { - if(this.anIntArray804 != null && var1 > 1) { - int var4 = -1; - - for(int var5 = 0; 10 > var5; ++var5) { - if(this.anIntArray766[var5] <= var1 && this.anIntArray766[var5] != 0) { - var4 = this.anIntArray804[var5]; - } - } - - if(var4 != -1) { - return getItemDefinition(var4); - } - } - - return this; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "h.H(" + var1 + ',' + 78 + ')'); - } - } - - static WorldListEntry method1107(int var0) { - try { - if(Unsorted.aClass44_Sub1Array3201.length > Class3_Sub6.anInt2291) { - return Unsorted.aClass44_Sub1Array3201[Class3_Sub6.anInt2291++]; - } else { - if(var0 != 5422) { - method1107(-66); - } - - return null; - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "h.R(" + var0 + ')'); - } - } - - final boolean method1108(boolean var2) { - try { - int var4 = this.anInt771; - int var3 = this.anInt793; - int var5 = this.anInt769; - if(var2) { - var5 = this.anInt776; - var3 = this.anInt761; - var4 = this.anInt794; - } - - if(var3 == -1) { - return true; - } else { - boolean var7 = true; - if(!LinkableRSString.aClass153_2581.method2129((byte)-90, 0, var3)) { - var7 = false; - } - - if(var4 != -1 && !LinkableRSString.aClass153_2581.method2129((byte)-114, 0, var4)) { - var7 = false; - } - - if(-1 != var5 && !LinkableRSString.aClass153_2581.method2129((byte)83, 0, var5)) { - var7 = false; - } - - return var7; - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "h.C(" + (byte) 95 + ',' + var2 + ')'); - } - } - - final void method1109(ItemDefinition var2, ItemDefinition var3) { - try { - this.aByteArray785 = var2.aByteArray785; - this.wornModelPositionZ = var2.wornModelPositionZ; - this.aHashTable_798 = var2.aHashTable_798; - this.anInt769 = var2.anInt769; - this.anInt761 = var2.anInt761; - this.wornModelPositionY = var2.wornModelPositionY; - this.inventoryOptions = new RSString[5]; - this.anInt755 = var3.anInt755; - this.anInt810 = var3.anInt810; - this.value = 0; - this.teamId = var2.teamId; - this.anInt773 = var2.anInt773; - this.aShortArray774 = var2.aShortArray774; - this.anInt768 = var3.anInt768; - this.anInt771 = var2.anInt771; - this.anInt799 = var3.anInt799; - this.anInt803 = var2.anInt803; - this.anInt796 = var2.anInt796; - this.wornModelPositionX = var2.wornModelPositionX; - this.anInt786 = var3.anInt786; - this.anInt754 = var3.anInt754; - this.anInt753 = var2.anInt753; - this.anInt777 = var2.anInt777; - this.aShortArray772 = var2.aShortArray772; - this.anInt802 = var2.anInt802; - this.anInt752 = var2.anInt752; - this.anInt792 = var3.anInt792; - - this.anInt793 = var2.anInt793; - this.anInt794 = var2.anInt794; - this.name = var2.name; - this.aShortArray751 = var2.aShortArray751; - this.aShortArray765 = var2.aShortArray765; - this.groundOptions = var2.groundOptions; - this.membersItem = var2.membersItem; - this.anInt776 = var2.anInt776; - if(null != var2.inventoryOptions) { - System.arraycopy(var2.inventoryOptions, 0, this.inventoryOptions, 0, 4); - } - - this.inventoryOptions[4] = TextCore.HasDiscard; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "h.J(" + (byte) 69 + ',' + (var2 != null?"{...}":"null") + ',' + (var3 != null?"{...}":"null") + ')'); - } - } - - final Model method1110(int var2, int var3, SequenceDefinition var4, int var5, int var6) { - try { - if(this.anIntArray804 != null && var5 > 1) { - int var7 = -1; - - for(int var8 = 0; var8 < 10; ++var8) { - if(this.anIntArray766[var8] <= var5 && this.anIntArray766[var8] != 0) { - var7 = this.anIntArray804[var8]; - } - } - - if(var7 != -1) { - return getItemDefinition(var7).method1110(var2, var3, var4, 1, var6); - } - } - - Model var11 = (Model)Class143.aReferenceCache_1874.get(this.itemId); - if(var11 == null) { - Model_Sub1 var12 = Model_Sub1.method2015(LinkableRSString.aClass153_2581, this.anInt755); - if(null == var12) { - return null; - } - - int var9; - if(null != this.aShortArray774) { - for(var9 = 0; this.aShortArray774.length > var9; ++var9) { - if(null != this.aByteArray785 && this.aByteArray785.length > var9) { - var12.method2016(this.aShortArray774[var9], TextureOperation38.aShortArray3453[this.aByteArray785[var9] & 0xFF]); - } else { - var12.method2016(this.aShortArray774[var9], this.aShortArray772[var9]); - } - } - } - - if(this.aShortArray765 != null) { - for(var9 = 0; var9 < this.aShortArray765.length; ++var9) { - var12.method1998(this.aShortArray765[var9], this.aShortArray751[var9]); - } - } - - var11 = var12.method2008(this.anInt784 + 64, 768 + this.anInt790, -50, -10, -50); - if(this.anInt805 != 128 || this.anInt780 != 128 || this.anInt797 != 128) { - var11.resize(this.anInt805, this.anInt780, this.anInt797); - } - - var11.aBoolean2699 = true; - if(HDToolKit.highDetail) { - ((Class140_Sub1_Sub1)var11).method1920(false, false, false, false, false, true); - } - - Class143.aReferenceCache_1874.put(var11, this.itemId); - } - - if(var4 != null) { - var11 = var4.method2055(var11, (byte)-88, var2, var3, var6); - } - - return var11; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "h.E(" + var2 + ',' + var3 + ',' + (var4 != null?"{...}":"null") + ',' + var5 + ',' + var6 + ')'); - } - } - - final void method1112() { - try { - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "h.O(" + 5401 + ')'); - } - } - - final void parseDefinitions(DataBuffer buffer) { - try { - while(true) { - int opcode = buffer.readUnsignedByte(); - if(0 == opcode) { - - return; - } - - this.parseOpcode(buffer, opcode); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "h.M(" + 1 + ',' + (buffer != null?"{...}":"null") + ')'); - } - } - - private void parseOpcode(DataBuffer buffer, int opcode) { - try { - if(opcode == 1) { - this.anInt755 = buffer.readUnsignedShort(); - } else if (opcode == 2) { - this.name = buffer.readString(); - } else if (opcode == 4) { - this.anInt810 = buffer.readUnsignedShort(); - } else if (opcode == 5) { - this.anInt786 = buffer.readUnsignedShort(); - } else if (opcode == 6) { - this.anInt799 = buffer.readUnsignedShort(); - } else if (opcode == 7) { - this.anInt792 = buffer.readUnsignedShort(); - if (this.anInt792 > 32767) { - this.anInt792 -= 65536; - } - } else if (opcode == 8) { - this.anInt754 = buffer.readUnsignedShort(); - if (this.anInt754 > 32767) { - this.anInt754 -= 65536; - } - } else if (opcode == 11) { - this.stackingType = 1; - } else if (opcode == 12) { - this.value = buffer.readInt(); - } else if (opcode == 16) { - this.membersItem = true; - } else if (23 == opcode) { - this.anInt793 = buffer.readUnsignedShort(); - } else if (opcode == 24) { - this.anInt771 = buffer.readUnsignedShort(); - } else if (opcode == 25) { - this.anInt761 = buffer.readUnsignedShort(); - } else if (opcode == 26) { - this.anInt794 = buffer.readUnsignedShort(); - } else if (opcode >= 30 && opcode < 35) { - this.groundOptions[-30 + opcode] = buffer.readString(); - if (this.groundOptions[opcode + -30].equalsStringIgnoreCase(TextCore.HasHidden)) { - this.groundOptions[-30 + opcode] = null; - } - } else if (35 <= opcode && 40 > opcode) { - this.inventoryOptions[-35 + opcode] = buffer.readString(); - } else { - int var5; - int var6; - if (opcode == 40) { - var5 = buffer.readUnsignedByte(); - this.aShortArray772 = new short[var5]; - this.aShortArray774 = new short[var5]; - - for (var6 = 0; var5 > var6; ++var6) { - this.aShortArray774[var6] = (short) buffer.readUnsignedShort(); - this.aShortArray772[var6] = (short) buffer.readUnsignedShort(); - } - } else if (opcode == 41) { - var5 = buffer.readUnsignedByte(); - this.aShortArray751 = new short[var5]; - this.aShortArray765 = new short[var5]; - - for (var6 = 0; var6 < var5; ++var6) { - this.aShortArray765[var6] = (short) buffer.readUnsignedShort(); - this.aShortArray751[var6] = (short) buffer.readUnsignedShort(); - } - } else if (42 == opcode) { - var5 = buffer.readUnsignedByte(); - this.aByteArray785 = new byte[var5]; - - for (var6 = 0; var5 > var6; ++var6) { - this.aByteArray785[var6] = buffer.readSignedByte(); - } - } else if (opcode == 65) { - this.aBoolean807 = true; - } else if (opcode == 78) { - this.anInt769 = buffer.readUnsignedShort(); - } else if (opcode == 79) { - this.anInt776 = buffer.readUnsignedShort(); - } else if (90 == opcode) { - this.anInt803 = buffer.readUnsignedShort(); - } else if (opcode == 91) { - this.anInt773 = buffer.readUnsignedShort(); - } else if (opcode == 92) { - this.anInt796 = buffer.readUnsignedShort(); - } else if (opcode == 93) { - this.anInt753 = buffer.readUnsignedShort(); - } else if (opcode == 95) { - this.anInt768 = buffer.readUnsignedShort(); - } else if (opcode == 96) { - this.anInt800 = buffer.readUnsignedByte(); - } else if (opcode == 97) { - this.noteID = buffer.readUnsignedShort(); - } else if (opcode == 98) { - this.anInt791 = buffer.readUnsignedShort(); - } else if (opcode >= 100 && opcode < 110) { - if (null == this.anIntArray804) { - this.anIntArray804 = new int[10]; - this.anIntArray766 = new int[10]; - } - - this.anIntArray804[-100 + opcode] = buffer.readUnsignedShort(); - this.anIntArray766[opcode + -100] = buffer.readUnsignedShort(); - } else if (110 == opcode) { - this.anInt805 = buffer.readUnsignedShort(); - } else if (opcode == 111) { - this.anInt780 = buffer.readUnsignedShort(); - } else if (opcode == 112) { - this.anInt797 = buffer.readUnsignedShort(); - } else if (opcode == 113) { - this.anInt784 = buffer.readSignedByte(); - } else if (opcode == 114) { - this.anInt790 = 5 * buffer.readSignedByte(); - } else if (opcode == 115) { - this.teamId = buffer.readUnsignedByte(); - } else if (opcode == 121) { - this.anInt795 = buffer.readUnsignedShort(); - } else if (opcode == 122) { - this.anInt762 = buffer.readUnsignedShort(); - } else if (125 == opcode) { - this.wornModelPositionX = buffer.readSignedByte(); - this.wornModelPositionZ = buffer.readSignedByte(); - this.wornModelPositionY = buffer.readSignedByte(); - } else if (opcode == 126) { - this.anInt777 = buffer.readSignedByte(); - this.anInt802 = buffer.readSignedByte(); - this.anInt752 = buffer.readSignedByte(); - } else if (opcode == 127) { - this.anInt767 = buffer.readUnsignedByte(); - this.anInt758 = buffer.readUnsignedShort(); - } else if (opcode == 128) { - this.anInt788 = buffer.readUnsignedByte(); - this.anInt756 = buffer.readUnsignedShort(); - } else if (opcode == 129) { - buffer.readUnsignedByte(); - buffer.readUnsignedShort(); - } else if (opcode == 130) { - buffer.readUnsignedByte(); - buffer.readUnsignedShort(); - } else if (249 == opcode) { - var5 = buffer.readUnsignedByte(); - if (null == this.aHashTable_798) { - var6 = Class95.method1585((byte) 97, var5); - this.aHashTable_798 = new HashTable(var6); - } - - for (var6 = 0; var6 < var5; ++var6) { - boolean var7 = buffer.readUnsignedByte() == 1; - int var8 = buffer.readMedium(); - Object var9; - if (var7) { - var9 = new LinkableRSString(buffer.readString()); - } else { - var9 = new LinkableInt(buffer.readInt()); - } - - this.aHashTable_798.put(var8, (Linkable) var9); - } - } - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "h.Q(" + (byte) -72 + ',' + (buffer != null?"{...}":"null") + ',' + opcode + ')'); - } - } - - final int method1115(int var1, int var2, int var3) { - try { - if(this.aHashTable_798 == null) { - return var1; - } else { - LinkableInt var5 = (LinkableInt)this.aHashTable_798.get(var3); - return null != var5?var5.value :var1; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "h.I(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - final Model_Sub1 method1116(boolean var1) { - try { - int var4 = this.anInt796; - int var3 = this.anInt803; - if(var1) { - var4 = this.anInt753; - var3 = this.anInt773; - } - - if(-1 == var3) { - return null; - } else { - Model_Sub1 var5 = Model_Sub1.method2015(LinkableRSString.aClass153_2581, var3); - if(-1 != var4) { - Model_Sub1 var6 = Model_Sub1.method2015(LinkableRSString.aClass153_2581, var4); - Model_Sub1[] var7 = new Model_Sub1[]{var5, var6}; - var5 = new Model_Sub1(var7, 2); - } - - int var9; - if(this.aShortArray774 != null) { - for(var9 = 0; var9 < this.aShortArray774.length; ++var9) { - Objects.requireNonNull(var5).method2016(this.aShortArray774[var9], this.aShortArray772[var9]); - } - } - - if(this.aShortArray765 != null) { - for(var9 = 0; var9 < this.aShortArray765.length; ++var9) { - Objects.requireNonNull(var5).method1998(this.aShortArray765[var9], this.aShortArray751[var9]); - } - } - - return var5; - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "h.A(" + var1 + ',' + (byte) -109 + ')'); - } - } - - final Model_Sub1 method1117(boolean var1) { - try { - int var3 = this.anInt793; - - int var4 = this.anInt771; - int var5 = this.anInt769; - if(var1) { - var5 = this.anInt776; - var3 = this.anInt761; - var4 = this.anInt794; - } - - if(var3 == -1) { - return null; - } else { - Model_Sub1 var6 = Model_Sub1.method2015(LinkableRSString.aClass153_2581, var3); - if(var4 != -1) { - Model_Sub1 var7 = Model_Sub1.method2015(LinkableRSString.aClass153_2581, var4); - if(-1 == var5) { - Model_Sub1[] var8 = new Model_Sub1[]{var6, var7}; - var6 = new Model_Sub1(var8, 2); - } else { - Model_Sub1 var12 = Model_Sub1.method2015(LinkableRSString.aClass153_2581, var5); - Model_Sub1[] var9 = new Model_Sub1[]{var6, var7, var12}; - var6 = new Model_Sub1(var9, 3); - } - } - - // System.out.println(var1 + " - "); - // this.wornModelPositionX = -1; - // this.wornModelPositionZ = 13;//-1;//20; - // this.wornModelPositionY = -1; - if(!var1 && (this.wornModelPositionX != 0 || this.wornModelPositionZ != 0 || this.wornModelPositionY != 0)) { - Objects.requireNonNull(var6).method2001(this.wornModelPositionX, this.wornModelPositionZ, this.wornModelPositionY); - } - if(var1 && (this.anInt777 != 0 || this.anInt802 != 0 || this.anInt752 != 0)) { - Objects.requireNonNull(var6).method2001(this.anInt777, this.anInt802, this.anInt752); - } - - int var11; - if(this.aShortArray774 != null) { - for(var11 = 0; var11 < this.aShortArray774.length; ++var11) { - Objects.requireNonNull(var6).method2016(this.aShortArray774[var11], this.aShortArray772[var11]); - } - } - - if(this.aShortArray765 != null) { - for(var11 = 0; var11 < this.aShortArray765.length; ++var11) { - Objects.requireNonNull(var6).method1998(this.aShortArray765[var11], this.aShortArray751[var11]); - } - } - - return var6; - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "h.D(" + var1 + ',' + 80 + ')'); - } - } - - final void method1118(ItemDefinition var1, ItemDefinition var2) { - try { - this.name = var1.name; - this.anInt810 = var2.anInt810; - - this.aShortArray774 = var2.aShortArray774; - this.aShortArray772 = var2.aShortArray772; - this.anInt786 = var2.anInt786; - this.anInt799 = var2.anInt799; - this.aShortArray751 = var2.aShortArray751; - this.anInt755 = var2.anInt755; - this.aByteArray785 = var2.aByteArray785; - this.anInt768 = var2.anInt768; - this.value = var1.value; - this.stackingType = 1; - this.anInt754 = var2.anInt754; - this.anInt792 = var2.anInt792; - this.aShortArray765 = var2.aShortArray765; - this.membersItem = var1.membersItem; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "h.N(" + (var1 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + false + ')'); - } - } - - static void method1119(Component var0, boolean var1) { - try { - var0.addMouseListener(Unsorted.aClass149_4047); - if(var1) { - aClass94_811 = null; - } - - var0.addMouseMotionListener(Unsorted.aClass149_4047); - var0.addFocusListener(Unsorted.aClass149_4047); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "h.K(" + (var0 != null?"{...}":"null") + ',' + var1 + ')'); - } - } - - final Class140_Sub1_Sub2 method1120() { - try { - Model_Sub1 var2 = Model_Sub1.method2015(LinkableRSString.aClass153_2581, this.anInt755); - if(var2 == null) { - return null; - } else { - int var3; - if(this.aShortArray774 != null) { - for(var3 = 0; this.aShortArray774.length > var3; ++var3) { - if(null != this.aByteArray785 && this.aByteArray785.length > var3) { - var2.method2016(this.aShortArray774[var3], TextureOperation38.aShortArray3453[this.aByteArray785[var3] & 0xFF]); - } else { - var2.method2016(this.aShortArray774[var3], this.aShortArray772[var3]); - } - } - } - - if(this.aShortArray765 != null) { - for(var3 = 0; var3 < this.aShortArray765.length; ++var3) { - var2.method1998(this.aShortArray765[var3], this.aShortArray751[var3]); - } - } - - Class140_Sub1_Sub2 var5 = var2.method2000(64 - -this.anInt784, 768 - -this.anInt790); - - if(this.anInt805 != 128 || this.anInt780 != 128 || this.anInt797 != 128) { - var5.resize(this.anInt805, this.anInt780, this.anInt797); - } - - return var5; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "h.L(" + 18206 + ')'); - } - } - - public ItemDefinition() { - this.name = Class40.aClass94_672; - this.wornModelPositionY = 0; - this.anInt784 = 0; - this.anInt769 = -1; - this.anInt796 = -1; - this.anInt791 = -1; - this.anInt777 = 0; - this.anInt780 = 128; - this.anInt767 = -1; - this.anInt758 = -1; - this.anInt768 = 0; - this.anInt762 = -1; - this.anInt795 = -1; - this.anInt761 = -1; - this.anInt771 = -1; - this.anInt754 = 0; - this.anInt786 = 0; - this.anInt799 = 0; - this.anInt800 = 0; - this.stackingType = 0; - this.noteID = -1; - this.anInt794 = -1; - this.anInt788 = -1; - this.anInt797 = 128; - this.membersItem = false; - this.anInt752 = 0; - this.anInt792 = 0; - this.anInt803 = -1; - this.anInt802 = 0; - this.anInt793 = -1; - this.groundOptions = new RSString[]{null, null, TextCore.HasTake, null, null}; - this.anInt805 = 128; - this.anInt790 = 0; - this.wornModelPositionZ = 0; - this.inventoryOptions = new RSString[]{null, null, null, null, TextCore.HasDrop}; - this.anInt810 = 2000; - this.aBoolean807 = false; - } - - static { - int var0 = 0; - - for(int var1 = 0; var1 < 99; ++var1) { - int var2 = 1 + var1; - int var3 = (int)(Math.pow(2.0D, (double)var2 / 7.0D) * 300.0D + (double)var2); - var0 += var3; - int var4 = var0 / 4; - anIntArray781[var1] = var4; - } - aClass94_811 = RSString.parse("green:"); - } -} diff --git a/Client/src/main/java/org/runite/client/JavaAudioChannel.java b/Client/src/main/java/org/runite/client/JavaAudioChannel.java deleted file mode 100644 index bebdc0564..000000000 --- a/Client/src/main/java/org/runite/client/JavaAudioChannel.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.runite.client; -import java.awt.Component; -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.SourceDataLine; -import javax.sound.sampled.Mixer.Info; - -final class JavaAudioChannel extends AudioChannel { - - private int anInt2971; - private SourceDataLine aSourceDataLine2972; - private boolean aBoolean2973 = false; - private AudioFormat anAudioFormat2974; - private byte[] aByteArray2975; - - - final void close() { - if(null != this.aSourceDataLine2972) { - this.aSourceDataLine2972.close(); - this.aSourceDataLine2972 = null; - } - - } - - final void init(Component var1) { - Info[] var2 = AudioSystem.getMixerInfo(); - if(null != var2) { - - for (Info var5 : var2) { - if (null != var5) { - String var6 = var5.getName(); - if (null != var6 && var6.toLowerCase().contains("soundmax")) { - this.aBoolean2973 = true; - } - } - } - } - - this.anAudioFormat2974 = new AudioFormat((float)Class21.sampleRate, 16, !AudioChannel.stereo ? 1 : 2, true, false); - this.aByteArray2975 = new byte[256 << (AudioChannel.stereo ? 2 : 1 )]; - } - - final void open(int var1) throws LineUnavailableException { - try { - javax.sound.sampled.DataLine.Info var2 = new javax.sound.sampled.DataLine.Info(SourceDataLine.class, this.anAudioFormat2974, var1 << (!AudioChannel.stereo ? 1 : 2 )); - this.aSourceDataLine2972 = (SourceDataLine)AudioSystem.getLine(var2); - this.aSourceDataLine2972.open(); - this.aSourceDataLine2972.start(); - this.anInt2971 = var1; - } catch (LineUnavailableException var3) { - if(Class146.method2080(var1) == 1) { - this.aSourceDataLine2972 = null; - throw var3; - } else { - this.open(Class95.method1585((byte)76, var1)); - } - } - } - - final void flush() throws LineUnavailableException { - this.aSourceDataLine2972.flush(); - if(this.aBoolean2973) { - this.aSourceDataLine2972.close(); - this.aSourceDataLine2972 = null; - javax.sound.sampled.DataLine.Info var1 = new javax.sound.sampled.DataLine.Info(SourceDataLine.class, this.anAudioFormat2974, this.anInt2971 << (!AudioChannel.stereo ?1:2)); - this.aSourceDataLine2972 = (SourceDataLine)AudioSystem.getLine(var1); - this.aSourceDataLine2972.open(); - this.aSourceDataLine2972.start(); - } - - } - - final int method2157() { - return this.anInt2971 - (this.aSourceDataLine2972.available() >> (!AudioChannel.stereo ?1:2)); - } - - final void write() { - int var1 = 256; - if(AudioChannel.stereo) { - var1 <<= 1; - } - - for(int var2 = 0; var2 < var1; ++var2) { - int var3 = this.samples[var2]; - if((var3 + 8388608 & -16777216) != 0) { - var3 = 8388607 ^ var3 >> 31; - } - - this.aByteArray2975[var2 * 2] = (byte)(var3 >> 8); - this.aByteArray2975[var2 * 2 + 1] = (byte)(var3 >> 16); - } - - this.aSourceDataLine2972.write(this.aByteArray2975, 0, var1 << 1); - } -} diff --git a/Client/src/main/java/org/runite/client/Js5ResourceRequest.java b/Client/src/main/java/org/runite/client/Js5ResourceRequest.java deleted file mode 100644 index adef092ba..000000000 --- a/Client/src/main/java/org/runite/client/Js5ResourceRequest.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.runite.client; - -public final class Js5ResourceRequest extends ResourceRequest { - - protected byte padding; - protected int anInt4067; - protected DataBuffer data; - - public final int getCompletion() { - return (this.data == null ? 0 : this.data.index * 100 / (-this.padding + this.data.buffer.length)); - } - - public final byte[] getData() { - if (!this.waiting && this.data.buffer.length - this.padding <= this.data.index) { - return this.data.buffer; - } else { - throw new RuntimeException(); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/Js5Worker.java b/Client/src/main/java/org/runite/client/Js5Worker.java deleted file mode 100644 index 3cfa7ced2..000000000 --- a/Client/src/main/java/org/runite/client/Js5Worker.java +++ /dev/null @@ -1,384 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.Queue; -import org.rs09.client.net.Connection; - -import java.io.IOException; - -final class Js5Worker { - - private final Queue priorityRequests = new Queue<>(); - private final Queue pendingPriorityRequests = new Queue<>(); - private final Queue normalRequests = new Queue<>(); - private final Queue pendingNormalRequests = new Queue<>(); - private final DataBuffer outgoing = new DataBuffer(4); - private final DataBuffer incoming = new DataBuffer(8); - public volatile int status = 0; - public volatile int errors = 0; - private long lastAttempt; - private Connection connection; - private int latency; - private byte aByte1009 = 0; - private Js5ResourceRequest current; - - public final boolean normalRequestsFull() { - return 20 <= this.countNormalRequests(); - } - - public final boolean process() { - int available; - - if (this.connection != null) { - long time = TimeUtils.time(); - int delta = (int) (time - this.lastAttempt); - this.lastAttempt = time; - if (delta > 200) { - delta = 200; - } - - this.latency += delta; - if (this.latency > 30000) { - try { - this.connection.close(); - } catch (Exception var18) { - } - - this.connection = null; - } - } - - if (this.connection == null) { - return this.countPriorityRequests() == 0 && this.countNormalRequests() == 0; - } - - try { - this.connection.checkErrors(); - - // Send any requests - Js5ResourceRequest request; - for (request = this.priorityRequests.getFront(); request != null; request = this.priorityRequests.next()) { - this.outgoing.index = 0; - this.outgoing.writeByte(1); //High priority JS5 request - this.outgoing.writeMedium((int) request.nodeKey); - - this.connection.sendBytes(this.outgoing.buffer, 4); - this.pendingPriorityRequests.offer(request); - } - - for (request = this.normalRequests.getFront(); request != null; request = this.normalRequests.next()) { - this.outgoing.index = 0; - this.outgoing.writeByte(0); //Low priority JS5 request - this.outgoing.writeMedium((int) request.nodeKey); - this.connection.sendBytes(this.outgoing.buffer, 4); - this.pendingNormalRequests.offer(request); - } - - // Handle incoming requests (max. 100 per iter) - for (int i = 0; 100 > i; ++i) { - available = this.connection.availableBytes(); - if (available < 0) { - throw new IOException(); - } - - if (available == 0) { - break; - } - - this.latency = 0; - byte headerSize = 0; - if (this.current == null) { - headerSize = 8; - } else if (this.current.anInt4067 == 0) { - headerSize = 1; - } - - int var6; - int readBytes; - int var8; - if (headerSize <= 0) { - var6 = this.current.data.buffer.length - this.current.padding; - readBytes = 512 - this.current.anInt4067; - if (readBytes > var6 - this.current.data.index) { - readBytes = var6 - this.current.data.index; - } - - if (available < readBytes) { - readBytes = available; - } - - this.connection.readBytes(this.current.data.buffer, this.current.data.index, readBytes); - if (this.aByte1009 != 0) { - for (var8 = 0; var8 < readBytes; ++var8) { - this.current.data.buffer[this.current.data.index - -var8] = (byte) Unsorted.bitwiseXOR(this.current.data.buffer[this.current.data.index + var8], this.aByte1009); - } - } - - this.current.anInt4067 += readBytes; - this.current.data.index += readBytes; - if (this.current.data.index == var6) { - this.current.unlinkNode(); - this.current.waiting = false; - this.current = null; - } else if (this.current.anInt4067 == 512) { - this.current.anInt4067 = 0; - } - } else { - var6 = headerSize - this.incoming.index; - if (available < var6) { - var6 = available; - } - - this.connection.readBytes(this.incoming.buffer, this.incoming.index, var6); - if (0 != this.aByte1009) { - for (readBytes = 0; readBytes < var6; ++readBytes) { - this.incoming.buffer[readBytes + this.incoming.index] = (byte) Unsorted.bitwiseXOR(this.incoming.buffer[readBytes + this.incoming.index], this.aByte1009); - } - } - - this.incoming.index += var6; - if (headerSize <= this.incoming.index) { - if (this.current == null) { - this.incoming.index = 0; - readBytes = this.incoming.readUnsignedByte(); - var8 = this.incoming.readUnsignedShort(); - int var9 = this.incoming.readUnsignedByte(); - int var10 = this.incoming.readInt(); - int var11 = 127 & var9; - boolean var12 = (var9 & 128) != 0; - Js5ResourceRequest var15; - long var13 = (readBytes << 16) - -var8; - if (var12) { - for (var15 = this.pendingNormalRequests.getFront(); null != var15 && var13 != var15.nodeKey; var15 = this.pendingNormalRequests.next()) { - } - } else { - for (var15 = this.pendingPriorityRequests.getFront(); var15 != null && var15.nodeKey != var13; var15 = this.pendingPriorityRequests.next()) { - } - } - - if (null == var15) { - throw new IOException("Could not find cache file " + readBytes + ", " + var8 + "!"); - } - - int var16 = var11 != 0 ? 9 : 5; - this.current = var15; - this.current.data = new DataBuffer(var10 - (-var16 - this.current.padding)); - this.current.data.writeByte(var11); - this.current.data.writeInt(var10); - this.current.anInt4067 = 8; - this.incoming.index = 0; - } else { - if (this.current.anInt4067 != 0) { - throw new IOException(); - } - - if (this.incoming.buffer[0] == -1) { - this.current.anInt4067 = 1; - this.incoming.index = 0; - } else { - this.current = null; - } - } - } - } - } - - return true; - } catch (IOException e) { - e.printStackTrace(); - try { - this.connection.close(); - } catch (Exception var17) { - } - - this.status = -2; - this.errors++; - this.connection = null; - return this.countPriorityRequests() == 0 && this.countNormalRequests() == 0; - } - } - - public final void requestTermination() { - if (this.connection == null) { - return; - } - - try { - this.outgoing.index = 0; - - this.outgoing.writeByte(7); - this.outgoing.writeMedium(0); - this.connection.sendBytes(this.outgoing.buffer, 4); - } catch (IOException e) { - e.printStackTrace(); - try { - this.connection.close(); - } catch (Exception var4) { - var4.printStackTrace(); - } - - this.errors++; - this.status = -2; - this.connection = null; - } - } - - private int countNormalRequests() { - return this.normalRequests.size() - -this.pendingNormalRequests.size(); - } - - public final void sendLoginState(boolean loggedIn) { - if (this.connection == null) { - return; - } - - try { - this.outgoing.index = 0; - this.outgoing.writeByte(loggedIn ? 2 : 3); - this.outgoing.writeMedium(0); - this.connection.sendBytes(this.outgoing.buffer, 4); - } catch (IOException e) { - e.printStackTrace(); - try { - this.connection.close(); - } catch (Exception var5) { - } - - this.errors++; - this.status = -2; - this.connection = null; - } - } - - public final void applyDummyStreams() { - if (this.connection != null) { - this.connection.applyDummyStreams(); - } - } - - public final void connect(boolean var1, Connection connection) { - if (null != this.connection) { - try { - this.connection.close(); - } catch (Exception var8) { - } - - this.connection = null; - } - - this.connection = connection; - this.sendMagic(); - this.sendLoginState(var1); - this.incoming.index = 0; - this.current = null; - - while (true) { - Js5ResourceRequest var4 = this.pendingPriorityRequests.poll(); - if (null == var4) { - while (true) { - var4 = this.pendingNormalRequests.poll(); - if (var4 == null) { - if (this.aByte1009 != 0) { - try { - this.outgoing.index = 0; - this.outgoing.writeByte(4); - this.outgoing.writeByte(this.aByte1009); - this.outgoing.writeShort(0); - this.connection.sendBytes(this.outgoing.buffer, 4); - } catch (IOException var7) { - var7.printStackTrace(); - try { - this.connection.close(); - } catch (Exception var6) { - } - - this.status = -2; - ++this.errors; - this.connection = null; - } - } - - this.latency = 0; - this.lastAttempt = TimeUtils.time(); - return; - } - - this.normalRequests.offer(var4); - } - } - - this.priorityRequests.offer(var4); - } - } - - public final boolean priorityRequestsFull() { - return 20 <= this.countPriorityRequests(); - } - - public final void closeWithError() { - try { - this.connection.close(); - } catch (Exception var4) { - } - - this.status = -1; - this.aByte1009 = (byte) ((int) (255.0D * Math.random() + 1.0D)); - this.connection = null; - this.errors++; - } - - public final int countPriorityRequests() { - return this.priorityRequests.size() - -this.pendingPriorityRequests.size(); - } - - public final void close() { - if (this.connection != null) { - this.connection.close(); - } - } - - public final Js5ResourceRequest request(int index, int archive, byte padding, boolean priority) { - Js5ResourceRequest request = new Js5ResourceRequest(); - long key = archive + (index << 16); - request.priority = priority; - request.nodeKey = key; - request.padding = padding; - - if (priority) { - if (this.countPriorityRequests() >= 20) { - throw new RuntimeException("Priority Js5 request queue full"); - } - - this.priorityRequests.offer(request); - } else { - if (this.countNormalRequests() >= 20) { - throw new RuntimeException("Normal Js5 request queue full"); - } - - this.normalRequests.offer(request); - } - - return request; - } - - private void sendMagic() { - if (connection == null) return; - - try { - this.outgoing.index = 0; - this.outgoing.writeByte(6); - this.outgoing.writeMedium(3); - this.connection.sendBytes(this.outgoing.buffer, 4); - } catch (IOException e) { - e.printStackTrace(); - try { - this.connection.close(); - } catch (Exception var4) { - } - - this.errors++; - this.connection = null; - this.status = -2; - } - } - -} diff --git a/Client/src/main/java/org/runite/client/KeyboardListener.java b/Client/src/main/java/org/runite/client/KeyboardListener.java deleted file mode 100644 index 7d63daad9..000000000 --- a/Client/src/main/java/org/runite/client/KeyboardListener.java +++ /dev/null @@ -1,255 +0,0 @@ -package org.runite.client; -import org.rs09.client.config.GameConfig; -import org.rs09.client.console.DeveloperConsole; -import org.rs09.client.data.ReferenceCache; - -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; - -final class KeyboardListener implements KeyListener, FocusListener { - - static int[] KEY_CODE_MAP = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, 85, 80, 84, -1, 91, -1, -1, -1, 81, 82, 86, -1, -1, -1, -1, -1, -1, -1, -1, 13, -1, -1, -1, -1, 83, 104, 105, 103, 102, 96, 98, 97, 99, -1, -1, -1, -1, -1, -1, -1, 25, 16, 17, 18, 19, 20, 21, 22, 23, 24, -1, -1, -1, -1, -1, -1, -1, 48, 68, 66, 50, 34, 51, 52, 53, 39, 54, 55, 56, 70, 69, 40, 41, 32, 35, 49, 36, 38, 67, 33, 65, 37, 64, -1, -1, -1, -1, -1, 228, 231, 227, 233, 224, 219, 225, 230, 226, 232, 89, 87, -1, 88, 229, 90, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 100, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; - static boolean aBoolean1905 = true; - static int anInt1908 = 0; - static ReferenceCache aReferenceCache_1911 = new ReferenceCache(260); - static int loginThemeSongArchiveID; - static int anInt1914; - static CacheIndex aClass153_1916; - static int anInt1918 = 0; - static int anInt2384 = 0; - static int[] anIntArray1978 = new int[128]; - - static void adjustKeyCodeMap() { - if(!Signlink.javaVendor.toLowerCase().contains("microsoft")) { - if(null == Signlink.setTraversalKeysEnabled) { - KEY_CODE_MAP[192] = 58; - KEY_CODE_MAP[222] = 59; - } else { - KEY_CODE_MAP[222] = 58; - KEY_CODE_MAP[192] = 28; - KEY_CODE_MAP[520] = 59; - } - - KEY_CODE_MAP[45] = 26; - KEY_CODE_MAP[61] = 27; - KEY_CODE_MAP[91] = 42; - KEY_CODE_MAP[59] = 57; - KEY_CODE_MAP[93] = 43; - KEY_CODE_MAP[44] = 71; - KEY_CODE_MAP[92] = 74; - KEY_CODE_MAP[46] = 72; - KEY_CODE_MAP[47] = 73; - } else { - KEY_CODE_MAP[187] = 27; - KEY_CODE_MAP[223] = 28; - KEY_CODE_MAP[221] = 43; - KEY_CODE_MAP[188] = 71; - KEY_CODE_MAP[222] = 59; - KEY_CODE_MAP[192] = 58; - KEY_CODE_MAP[191] = 73; - KEY_CODE_MAP[219] = 42; - KEY_CODE_MAP[190] = 72; - KEY_CODE_MAP[186] = 57; - KEY_CODE_MAP[220] = 74; - KEY_CODE_MAP[189] = 26; - } - } - - - public final synchronized void keyPressed(KeyEvent var1) { - try { -// System.out.println("Key code: " + var1.getKeyCode()); - switch (var1.getKeyCode()) - { - case 16: - MouseWheel.shiftDown = true; - break; - case 17: - MouseWheel.ctrlDown = true; - break; - case 192: - DeveloperConsole.INSTANCE.toggle(); - return; - } - if (DeveloperConsole.INSTANCE.getOpen()) { - DeveloperConsole.INSTANCE.handleKeyDown(var1); - return; - } - - if(null != TextureOperation33.aClass148_3049) { - TextureOperation29.anInt3398 = 0; - int var2 = var1.getKeyCode(); -// switch(var2) { -// case 112://f1 -// var2 = 115; -// break; -// case 113://f2 -// var2 = 116; -// break; -// case 114://f3 -// var2 = 117; -// break; -// case 115://f4 -// var2 = 118; -// break; -// case 116://f5 -// var2 = 112; -// break; -// } - if(0 <= var2 && KEY_CODE_MAP.length > var2) { - var2 = KEY_CODE_MAP[var2]; - // System.out.println(var2); - if(0 != (var2 & 128)) { - var2 = -1; - } - } else { - var2 = -1; - } - - - - /** - * Tab to reply - */ - if (var1.getKeyCode() == KeyEvent.VK_TAB) { - ClientCommands.ClientCommands(RSString.parse("::reply")); - } - - if (var1.getKeyCode() == KeyEvent.VK_ESCAPE) - { - ClientCommands.ClientCommands(RSString.parse("::shutdowninterface")); - } - - //causing issues when in other interfaces, such as GE interface. My quick thing impl didn't work - Jamix77 - - /**if (var1.getKeyCode() == KeyEvent.VK_SPACE) - { - Class2.method78(40, false, 0); - }**/ - - - /** - * Controls arrow key mouse movement - */ - if(anInt2384 >= 0 && var2 >= 0) { - Unsorted.anIntArray2952[anInt2384] = var2; - anInt2384 = 127 & anInt2384 - -1; - if(anInt2384 == Class32.anInt1744) { - anInt2384 = -1; - } - } - - int var3; - if(var2 >= 0) { - var3 = 127 & 1 + Class25.anInt491; - if(var3 != Class3_Sub28_Sub9.anInt3620) { - Class129.anIntArray1693[Class25.anInt491] = var2; - anIntArray1978[Class25.anInt491] = -1; - Class25.anInt491 = var3; - } - } - - var3 = var1.getModifiers(); - if((var3 & 10) != 0 || 85 == var2 || var2 == 10) { - var1.consume(); - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "uf.keyPressed(" + (var1 != null?"{...}":"null") + ')'); - } - } - - public final void keyTyped(KeyEvent var1) { - if (DeveloperConsole.INSTANCE.getOpen()) { - DeveloperConsole.INSTANCE.handleKeyPressed(var1); - return; - } - if(var1.isAltDown()){ - if(var1.getKeyChar()=='n'){ - GameConfig.NPC_DEBUG_ENABLED = !GameConfig.NPC_DEBUG_ENABLED; - BufferedDataStream.addChatMessage(RSString.parse("Client debug notifier"), 0, RSString.parse("NPC debug context " + (GameConfig.NPC_DEBUG_ENABLED ? "enabled." : "disabled.")), -1); - return; - } - if(var1.getKeyChar()=='o'){ - GameConfig.OBJECT_DEBUG_ENABLED = !GameConfig.OBJECT_DEBUG_ENABLED; - BufferedDataStream.addChatMessage(RSString.parse("Client debug notifier"), 0, RSString.parse("Object debug context " + (GameConfig.OBJECT_DEBUG_ENABLED ? "enabled." : "disabled.")), -1); - return; - } - if(var1.getKeyChar()=='i'){ - GameConfig.ITEM_DEBUG_ENABLED = !GameConfig.ITEM_DEBUG_ENABLED; - BufferedDataStream.addChatMessage(RSString.parse("Client debug notifier"), 0, RSString.parse("Item debug context " + (GameConfig.ITEM_DEBUG_ENABLED ? "enabled." : "disabled.")), -1); - return; - } - return; - } - try { - - if(TextureOperation33.aClass148_3049 != null) { - int var2 = CSConfigCachefile.method1386(var1); - if(var2 >= 0) { - int var3 = 1 + Class25.anInt491 & 127; - if(var3 != Class3_Sub28_Sub9.anInt3620) { - Class129.anIntArray1693[Class25.anInt491] = -1; - anIntArray1978[Class25.anInt491] = var2; - Class25.anInt491 = var3; - } - } - } - - var1.consume(); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "uf.keyTyped(" + (var1 != null?"{...}":"null") + ')'); - } - } - - public final synchronized void focusLost(FocusEvent var1) { - if(TextureOperation33.aClass148_3049 != null) { - anInt2384 = -1; - } - } - - public final synchronized void keyReleased(KeyEvent var1) { - if (DeveloperConsole.INSTANCE.getOpen()) { - return; - } - - switch (var1.getKeyCode()) - { - case 16: - MouseWheel.shiftDown = false; - break; - case 17: - MouseWheel.ctrlDown = false; - break; - } - try { - if(null != TextureOperation33.aClass148_3049) { - TextureOperation29.anInt3398 = 0; - int var2 = var1.getKeyCode(); - if(var2 >= 0 && var2 < KEY_CODE_MAP.length) { - var2 = KEY_CODE_MAP[var2] & -129; - } else { - var2 = -1; - } - - if(anInt2384 >= 0 && var2 >= 0) { - Unsorted.anIntArray2952[anInt2384] = ~var2; - anInt2384 = 127 & 1 + anInt2384; - if(anInt2384 == Class32.anInt1744) { - anInt2384 = -1; - } - } - } - - var1.consume(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "uf.keyReleased(" + "{...}" + ')'); - } - } - - public final void focusGained(FocusEvent var1) {} - -} diff --git a/Client/src/main/java/org/runite/client/LDIndexedSprite.java b/Client/src/main/java/org/runite/client/LDIndexedSprite.java deleted file mode 100644 index f3de555e0..000000000 --- a/Client/src/main/java/org/runite/client/LDIndexedSprite.java +++ /dev/null @@ -1,459 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; - -public final class LDIndexedSprite extends AbstractIndexedSprite { - - private final int[] anIntArray2673; - byte[] raster; - - - final void method1668(int var1, int var2, int var3) { - for(int var4 = 0; var4 < this.anIntArray2673.length; ++var4) { - int var5 = this.anIntArray2673[var4] >> 16 & 0xFF; - var5 += var1; - if(var5 < 0) { - var5 = 0; - } else if(var5 > 255) { - var5 = 255; - } - - int var6 = this.anIntArray2673[var4] >> 8 & 0xFF; - var6 += var2; - if(var6 < 0) { - var6 = 0; - } else if(var6 > 255) { - var6 = 255; - } - - int var7 = this.anIntArray2673[var4] & 0xFF; - var7 += var3; - if(var7 < 0) { - var7 = 0; - } else if(var7 > 255) { - var7 = 255; - } - - this.anIntArray2673[var4] = (var5 << 16) + (var6 << 8) + var7; - } - - } - - final void method1669(int var1, int var2, int var3, int var4, int var5) { - int var6 = this.width; - int var7 = this.height; - int var8 = 0; - int var9 = 0; - int var10 = this.anInt1469; - int var11 = this.anInt1467; - int var12 = (var10 << 16) / var3; - int var13 = (var11 << 16) / var4; - int var14; - if(this.anInt1470 > 0) { - var14 = ((this.anInt1470 << 16) + var12 - 1) / var12; - var1 += var14; - var8 += var14 * var12 - (this.anInt1470 << 16); - } - - if(this.anInt1464 > 0) { - var14 = ((this.anInt1464 << 16) + var13 - 1) / var13; - var2 += var14; - var9 += var14 * var13 - (this.anInt1464 << 16); - } - - if(var6 < var10) { - var3 = ((var6 << 16) - var8 + var12 - 1) / var12; - } - - if(var7 < var11) { - var4 = ((var7 << 16) - var9 + var13 - 1) / var13; - } - - var14 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var15 = Toolkit.JAVA_TOOLKIT.width - var3; - if(var2 + var4 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var4 -= var2 + var4 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var16; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var16 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var4 -= var16; - var14 += var16 * Toolkit.JAVA_TOOLKIT.width; - var9 += var13 * var16; - } - - if(var1 + var3 > Toolkit.JAVA_TOOLKIT.clipRight) { - var16 = var1 + var3 - Toolkit.JAVA_TOOLKIT.clipRight; - var3 -= var16; - var15 += var16; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var16 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var3 -= var16; - var14 += var16; - var8 += var12 * var16; - var15 += var16; - } - - method1673(Toolkit.JAVA_TOOLKIT.getBuffer(), this.raster, this.anIntArray2673, var8, var9, var14, var15, var3, var4, var12, var13, var6, var5); - } - - private static void method1670(int[] var0, byte[] var1, int[] var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11) { - int var12 = var3; - - for(int var13 = -var8; var13 < 0; ++var13) { - int var14 = (var4 >> 16) * var11; - - for(int var15 = -var7; var15 < 0; ++var15) { - byte var16 = var1[(var3 >> 16) + var14]; - if(var16 == 0) { - ++var5; - } else { - var0[var5++] = var2[var16 & 0xFF]; - } - - var3 += var9; - } - - var4 += var10; - var3 = var12; - var5 += var6; - } - - } - - final void method1671() { - int var1 = 0; - - int var2; - for(var2 = this.raster.length - 7; var1 < var2; this.raster[var1++] = 0) { - this.raster[var1++] = 0; - this.raster[var1++] = 0; - this.raster[var1++] = 0; - this.raster[var1++] = 0; - this.raster[var1++] = 0; - this.raster[var1++] = 0; - this.raster[var1++] = 0; - } - - for(var2 += 7; var1 < var2; this.raster[var1++] = 0) { - } - - } - - private static void method1672(int[] var0, byte[] var1, int[] var2, int var4, int var5, int var6, int var7, int var8, int var9) { - int var10 = -(var6 >> 2); - var6 = -(var6 & 3); - - for(int var11 = -var7; var11 < 0; ++var11) { - int var12; - byte var13; - for(var12 = var10; var12 < 0; ++var12) { - var13 = var1[var4++]; - if(var13 == 0) { - ++var5; - } else { - var0[var5++] = var2[var13 & 0xFF]; - } - - var13 = var1[var4++]; - if(var13 == 0) { - ++var5; - } else { - var0[var5++] = var2[var13 & 0xFF]; - } - - var13 = var1[var4++]; - if(var13 == 0) { - ++var5; - } else { - var0[var5++] = var2[var13 & 0xFF]; - } - - var13 = var1[var4++]; - if(var13 == 0) { - ++var5; - } else { - var0[var5++] = var2[var13 & 0xFF]; - } - } - - for(var12 = var6; var12 < 0; ++var12) { - var13 = var1[var4++]; - if(var13 == 0) { - ++var5; - } else { - var0[var5++] = var2[var13 & 0xFF]; - } - } - - var5 += var8; - var4 += var9; - } - - } - - private static void method1673(int[] var0, byte[] var1, int[] var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12) { - int var13 = var3; - int var14 = var12 >> 16 & 0xFF; - int var15 = var12 >> 8 & 0xFF; - int var16 = var12 & 0xFF; - - for(int var17 = -var8; var17 < 0; ++var17) { - int var18 = (var4 >> 16) * var11; - - for(int var19 = -var7; var19 < 0; ++var19) { - byte var20 = var1[(var3 >> 16) + var18]; - if(var20 == 0) { - ++var5; - } else { - int var24 = var2[var20 & 0xFF]; - int var21 = var24 >> 16 & 0xFF; - int var22 = var24 >> 8 & 0xFF; - int var23 = var24 & 0xFF; - var0[var5++] = (var21 * var14 >> 8 << 16) + (var22 * var15 >> 8 << 8) + (var23 * var16 >> 8); - } - - var3 += var9; - } - - var4 += var10; - var3 = var13; - var5 += var6; - } - - } - - public final void rotateClockwise() { - byte[] rotated = new byte[this.width * this.height]; - int index = 0; - - for(int x = 0; x < this.width; ++x) { - for(int y = this.height - 1; y >= 0; --y) { - rotated[index++] = this.raster[x + y * this.width]; - } - } - - int var3; - this.raster = rotated; - var3 = this.anInt1464; - this.anInt1464 = this.anInt1470; - this.anInt1470 = this.anInt1467 - this.height - var3; - var3 = this.height; - this.height = this.width; - this.width = var3; - var3 = this.anInt1467; - this.anInt1467 = this.anInt1469; - this.anInt1469 = var3; - } - - final void method1666(int var1, int var2, int var3) { - var1 += this.anInt1470; - var2 += this.anInt1464; - int var4 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var5 = 0; - int var6 = this.height; - int var7 = this.width; - int var8 = Toolkit.JAVA_TOOLKIT.width - var7; - int var9 = 0; - int var10; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var10 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var6 -= var10; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var5 += var10 * var7; - var4 += var10 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var6 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var6 -= var2 + var6 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var10 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var7 -= var10; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var5 += var10; - var4 += var10; - var9 += var10; - var8 += var10; - } - - if(var1 + var7 > Toolkit.JAVA_TOOLKIT.clipRight) { - var10 = var1 + var7 - Toolkit.JAVA_TOOLKIT.clipRight; - var7 -= var10; - var9 += var10; - var8 += var10; - } - - if(var7 > 0 && var6 > 0) { - method1676(Toolkit.JAVA_TOOLKIT.getBuffer(), this.raster, this.anIntArray2673, var5, var4, var7, var6, var8, var9, var3); - } - } - - final void method1675() { - if(this.width != this.anInt1469 || this.height != this.anInt1467) { - byte[] var1 = new byte[this.anInt1469 * this.anInt1467]; - int var2 = 0; - - for(int var3 = 0; var3 < this.height; ++var3) { - for(int var4 = 0; var4 < this.width; ++var4) { - var1[var4 + this.anInt1470 + (var3 + this.anInt1464) * this.anInt1469] = this.raster[var2++]; - } - } - - this.raster = var1; - this.width = this.anInt1469; - this.height = this.anInt1467; - this.anInt1470 = 0; - this.anInt1464 = 0; - } - } - - private static void method1676(int[] var0, byte[] var1, int[] var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - int var10 = 256 - var9; - - for(int var11 = -var6; var11 < 0; ++var11) { - for(int var12 = -var5; var12 < 0; ++var12) { - byte var13 = var1[var3++]; - if(var13 == 0) { - ++var4; - } else { - int var15 = var2[var13 & 0xFF]; - int var14 = var0[var4]; - var0[var4++] = ((var15 & 16711935) * var9 + (var14 & 16711935) * var10 & -16711936) + ((var15 & 65280) * var9 + (var14 & 65280) * var10 & 16711680) >> 8; - } - } - - var4 += var7; - var3 += var8; - } - - } - - final void method1677(int var1, int var2, int var3, int var4) { - int var5 = this.width; - int var6 = this.height; - int var7 = 0; - int var8 = 0; - int var9 = this.anInt1469; - int var10 = this.anInt1467; - int var11 = (var9 << 16) / var3; - int var12 = (var10 << 16) / var4; - int var13; - if(this.anInt1470 > 0) { - var13 = ((this.anInt1470 << 16) + var11 - 1) / var11; - var1 += var13; - var7 += var13 * var11 - (this.anInt1470 << 16); - } - - if(this.anInt1464 > 0) { - var13 = ((this.anInt1464 << 16) + var12 - 1) / var12; - var2 += var13; - var8 += var13 * var12 - (this.anInt1464 << 16); - } - - if(var5 < var9) { - var3 = ((var5 << 16) - var7 + var11 - 1) / var11; - } - - if(var6 < var10) { - var4 = ((var6 << 16) - var8 + var12 - 1) / var12; - } - - var13 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var14 = Toolkit.JAVA_TOOLKIT.width - var3; - if(var2 + var4 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var4 -= var2 + var4 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var15; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var15 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var4 -= var15; - var13 += var15 * Toolkit.JAVA_TOOLKIT.width; - var8 += var12 * var15; - } - - if(var1 + var3 > Toolkit.JAVA_TOOLKIT.clipRight) { - var15 = var1 + var3 - Toolkit.JAVA_TOOLKIT.clipRight; - var3 -= var15; - var14 += var15; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var15 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var3 -= var15; - var13 += var15; - var7 += var11 * var15; - var14 += var15; - } - - method1670(Toolkit.JAVA_TOOLKIT.getBuffer(), this.raster, this.anIntArray2673, var7, var8, var13, var14, var3, var4, var11, var12, var5); - } - - public final void method1667(int var1, int var2) { - var1 += this.anInt1470; - var2 += this.anInt1464; - int var3 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var4 = 0; - int var5 = this.height; - int var6 = this.width; - int var7 = Toolkit.JAVA_TOOLKIT.width - var6; - int var8 = 0; - int var9; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var9 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var5 -= var9; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var4 += var9 * var6; - var3 += var9 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var2 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var9 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var6 -= var9; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var4 += var9; - var3 += var9; - var8 += var9; - var7 += var9; - } - - if(var1 + var6 > Toolkit.JAVA_TOOLKIT.clipRight) { - var9 = var1 + var6 - Toolkit.JAVA_TOOLKIT.clipRight; - var6 -= var9; - var8 += var9; - var7 += var9; - } - - if(var6 > 0 && var5 > 0) { - method1672(Toolkit.JAVA_TOOLKIT.getBuffer(), this.raster, this.anIntArray2673, var4, var3, var6, var5, var7, var8); - } - } - - LDIndexedSprite(int var1, int var2, int var3, int var4, int var5, int var6, byte[] var7, int[] var8) { - this.anInt1469 = var1; - this.anInt1467 = var2; - this.anInt1470 = var3; - this.anInt1464 = var4; - this.width = var5; - this.height = var6; - this.raster = var7; - this.anIntArray2673 = var8; - } - - LDIndexedSprite(int var1, int var2) { - this.anInt1469 = this.width = var1; - this.anInt1467 = this.height = var2; - this.anInt1470 = this.anInt1464 = 0; - this.raster = new byte[var1 * var2]; - this.anIntArray2673 = new int[0]; - } -} diff --git a/Client/src/main/java/org/runite/client/LinkableRSString.java b/Client/src/main/java/org/runite/client/LinkableRSString.java deleted file mode 100644 index e03259013..000000000 --- a/Client/src/main/java/org/runite/client/LinkableRSString.java +++ /dev/null @@ -1,170 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -public final class LinkableRSString extends Linkable { - - static int anInt2579 = 1; - static RSString[] aClass94Array2580 = new RSString[100]; - static CacheIndex aClass153_2581; - public static int anInt2582 = 0; - static boolean isDynamicSceneGraph = false; - public RSString value; - static int anInt2587; - static GameShell anApplet_Sub1_2588 = null; - static int anInt2589 = 0; - - - static void method727() { - try { - KeyboardListener.aReferenceCache_1911.clear(); - Unsorted.aReferenceCache_1131.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "sj.O(" + 91 + ')'); - } - } - - static void method728() { - try { - - try { - if (Unsorted.anInt154 == 1) { - int var1 = Class101.aClass3_Sub24_Sub4_1421.method499(); - if (var1 > 0 && Class101.aClass3_Sub24_Sub4_1421.method473(-124)) { - var1 -= GraphicDefinition.anInt546; - if (var1 < 0) { - var1 = 0; - } - - Class101.aClass3_Sub24_Sub4_1421.method506(var1); - return; - } - - Class101.aClass3_Sub24_Sub4_1421.method505((byte) -128); - Class101.aClass3_Sub24_Sub4_1421.method485(); - NPC.aClass3_Sub27_1154 = null; - QuickChatDefinition.aClass83_3579 = null; - if (Class101.aClass153_1423 == null) { - Unsorted.anInt154 = 0; - } else { - Unsorted.anInt154 = 2; - } - } - } catch (Exception var2) { - var2.printStackTrace(); - Class101.aClass3_Sub24_Sub4_1421.method505((byte) -127); - Class101.aClass153_1423 = null; - NPC.aClass3_Sub27_1154 = null; - Unsorted.anInt154 = 0; - QuickChatDefinition.aClass83_3579 = null; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sj.A(" + false + ')'); - } - } - - static int method729(byte var0, int var1, int var2) { - try { - if (var0 > -32) { - return 88; - } else if (var1 == -2) { - return 12345678; - } else if (var1 == -1) { - if (2 > var2) { - var2 = 2; - } else if (var2 > 126) { - var2 = 126; - } - - return var2; - } else { - var2 = (127 & var1) * var2 >> 7; - if (var2 < 2) { - var2 = 2; - } else if (var2 > 126) { - var2 = 126; - } - - return (var1 & '\uff80') - -var2; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "sj.E(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - static void method730(int var0, int var1, int var3, int var4, int var5) { - try { - if (Class101.anInt1425 <= var0 && var4 <= Class3_Sub28_Sub18.anInt3765 && Class159.anInt2020 <= var5 && Class57.anInt902 >= var3) { - WaterfallShader.method1632(95, var3, var4, var5, var0, var1); - } else { - Unsorted.method1525(var1, var4, var5, var0, var3); - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "sj.R(" + var0 + ',' + var1 + ',' + (byte) 121 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static void method731(CacheIndex var0) { - try { - TextureOperation20.aClass153_3154 = var0; - Class95.anInt1344 = TextureOperation20.aClass153_3154.getFileAmount(16); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sj.B(" + (var0 != null ? "{...}" : "null") + ',' + (byte) -113 + ')'); - } - } - - public LinkableRSString() { - } - - static void method734(RSString var1) { - try { - Class163_Sub2.paramSettings = var1; - if (null != Class38.gameSignlink.gameApplet) { - try { - RSString var2 = TextCore.aClass94_2044.getParamValue(Class38.gameSignlink.gameApplet); - RSString var3 = TextCore.aClass94_1885.getParamValue(Class38.gameSignlink.gameApplet); - RSString var4 = RSString.stringCombiner(new RSString[]{var2, TextCore.aClass94_1151, var1, TextCore.aClass94_2074, var3}); - if (0 == var1.length()) { - var4 = RSString.stringCombiner(new RSString[]{var4, TextCore.HasAgeExpire}); - } else { - var4 = RSString.stringCombiner(new RSString[]{var4, TextCore.HasExpires, AudioThread.method894(94608000000L + TimeUtils.time()), TextCore.HasMaxAge, QuickChatDefinition.method612(94608000L)}); - } - - RSString.stringCombiner(new RSString[]{TextCore.aClass94_1694, var4, TextCore.aClass94_1698}).method1554(Class38.gameSignlink.gameApplet); - } catch (Throwable var5) { - } - - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "sj.F(" + 0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method736(int var0, int var1) { - try { - if (var1 <= 61) { - method736(-60, -93); - } - - if (Unsorted.anInt154 == 0) { - Class101.aClass3_Sub24_Sub4_1421.method506(var0); - } else { - TextureOperation36.anInt3423 = var0; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sj.P(" + var0 + ',' + var1 + ')'); - } - } - - public LinkableRSString(RSString var1) { - try { - this.value = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sj.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/LinkedList.java b/Client/src/main/java/org/runite/client/LinkedList.java deleted file mode 100644 index 7cbe68649..000000000 --- a/Client/src/main/java/org/runite/client/LinkedList.java +++ /dev/null @@ -1,277 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.data.ReferenceCache; - -public final class LinkedList { - - static ReferenceCache aReferenceCache_939 = new ReferenceCache(4); - Linkable aClass3_940 = new Linkable(); - private Linkable aClass3_941; - - - public LinkedList() { - try { - this.aClass3_940.previous = this.aClass3_940; - this.aClass3_940.next = this.aClass3_940; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ih.()"); - } - } - - static Class70 method1209(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if (var3 == null) { - return null; - } else { - Class70 var4 = var3.aClass70_2234; - var3.aClass70_2234 = null; - return var4; - } - } - - static Class3_Sub28_Sub9 method1210(int var1) { - try { - Class3_Sub28_Sub9 var2 = (Class3_Sub28_Sub9) Class163.aClass47_2041.get(var1); - - if (null == var2) { - byte[] var3 = TextureOperation27.aClass153_3098.getFile(11, var1); - var2 = new Class3_Sub28_Sub9(); - if (var3 != null) { - var2.method583(new DataBuffer(var3)); - } - - Class163.aClass47_2041.put(var1, var2); - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ih.G(" + 64 + ',' + var1 + ')'); - } - } - - static void method1213(int var0, Class3_Sub11[] var1) { - Class3_Sub23.aClass3_Sub11ArrayArray2542[var0] = var1; - } - - static void method1214(int var0, int var1, int var2, int var3) { - try { - Class3_Sub9 var5; - for (var5 = (Class3_Sub9) Unsorted.aLinkedList_78.method1222(); var5 != null; var5 = (Class3_Sub9) Unsorted.aLinkedList_78.method1221()) { - Unsorted.method606(var1, var5, var3, var0, var2, 126); - } - - byte var6; - RenderAnimationDefinition var7; - int var8; - for (var5 = (Class3_Sub9) Unsorted.aLinkedList_1242.method1222(); var5 != null; var5 = (Class3_Sub9) Unsorted.aLinkedList_1242.method1221()) { - var6 = 1; - var7 = var5.aClass140_Sub4_Sub2_2324.getRenderAnimationType(); - if (var5.aClass140_Sub4_Sub2_2324.anInt2764 == var7.stand_animation) { - var6 = 0; - } else if (var5.aClass140_Sub4_Sub2_2324.anInt2764 != var7.run_anim && var5.aClass140_Sub4_Sub2_2324.anInt2764 != var7.run_follow_full_turn_anim && var5.aClass140_Sub4_Sub2_2324.anInt2764 != var7.run_follow_cw_turn_anim && var5.aClass140_Sub4_Sub2_2324.anInt2764 != var7.run_follow_ccw_turn_anim) { - if (var7.slow_walk_anim == var5.aClass140_Sub4_Sub2_2324.anInt2764 || var7.slow_walk_follow_full_turn_anim == var5.aClass140_Sub4_Sub2_2324.anInt2764 || var5.aClass140_Sub4_Sub2_2324.anInt2764 == var7.slow_walk_follow_cw_turn_anim || var5.aClass140_Sub4_Sub2_2324.anInt2764 == var7.slow_walk_follow_ccw_turn_anim) { - var6 = 3; - } - } else { - var6 = 2; - } - - if (var5.anInt2322 != var6) { - var8 = Class70.method1232(var5.aClass140_Sub4_Sub2_2324); - if (var8 != var5.anInt2332) { - if (var5.aClass3_Sub24_Sub1_2312 != null) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var5.aClass3_Sub24_Sub1_2312); - var5.aClass3_Sub24_Sub1_2312 = null; - } - - var5.anInt2332 = var8; - } - - var5.anInt2322 = var6; - } - - var5.anInt2326 = var5.aClass140_Sub4_Sub2_2324.xAxis; - var5.anInt2321 = var5.aClass140_Sub4_Sub2_2324.xAxis + var5.aClass140_Sub4_Sub2_2324.getSize() * 64; - var5.anInt2308 = var5.aClass140_Sub4_Sub2_2324.zAxis; - var5.anInt2307 = var5.aClass140_Sub4_Sub2_2324.zAxis + var5.aClass140_Sub4_Sub2_2324.getSize() * 64; - Unsorted.method606(var1, var5, var3, var0, var2, 1 ^ 113); - } - - for (var5 = (Class3_Sub9) Unsorted.aHashTable_4046.first(); var5 != null; var5 = (Class3_Sub9) Unsorted.aHashTable_4046.next()) { - var6 = 1; - var7 = var5.aClass140_Sub4_Sub1_2327.getRenderAnimationType(); - if (var5.aClass140_Sub4_Sub1_2327.anInt2764 == var7.stand_animation) { - var6 = 0; - } else if (var5.aClass140_Sub4_Sub1_2327.anInt2764 != var7.run_anim && var5.aClass140_Sub4_Sub1_2327.anInt2764 != var7.run_follow_full_turn_anim && var7.run_follow_cw_turn_anim != var5.aClass140_Sub4_Sub1_2327.anInt2764 && var7.run_follow_ccw_turn_anim != var5.aClass140_Sub4_Sub1_2327.anInt2764) { - if (var7.slow_walk_anim == var5.aClass140_Sub4_Sub1_2327.anInt2764 || var5.aClass140_Sub4_Sub1_2327.anInt2764 == var7.slow_walk_follow_full_turn_anim || var7.slow_walk_follow_cw_turn_anim == var5.aClass140_Sub4_Sub1_2327.anInt2764 || var7.slow_walk_follow_ccw_turn_anim == var5.aClass140_Sub4_Sub1_2327.anInt2764) { - var6 = 3; - } - } else { - var6 = 2; - } - - if (var6 != var5.anInt2322) { - var8 = Class81.method1398(var5.aClass140_Sub4_Sub1_2327); - if (var8 != var5.anInt2332) { - if (var5.aClass3_Sub24_Sub1_2312 != null) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var5.aClass3_Sub24_Sub1_2312); - var5.aClass3_Sub24_Sub1_2312 = null; - } - - var5.anInt2332 = var8; - } - - var5.anInt2322 = var6; - } - - var5.anInt2326 = var5.aClass140_Sub4_Sub1_2327.xAxis; - var5.anInt2321 = var5.aClass140_Sub4_Sub1_2327.xAxis + 64 * var5.aClass140_Sub4_Sub1_2327.getSize(); - var5.anInt2308 = var5.aClass140_Sub4_Sub1_2327.zAxis; - var5.anInt2307 = var5.aClass140_Sub4_Sub1_2327.zAxis + var5.aClass140_Sub4_Sub1_2327.getSize() * 64; - Unsorted.method606(var1, var5, var3, var0, var2, 110); - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "ih.K(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + 1 + ')'); - } - } - - static RSString method1218(int var2) { - try { - return Unsorted.method1723((byte) -128, true, var2); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ih.A(" + true + ',' + 127 + ',' + var2 + ')'); - } - } - - final void method1211(int var1) { - try { - while (true) { - Linkable var2 = this.aClass3_940.next; - if (var2 == this.aClass3_940) { - if (var1 > -47) { - this.aClass3_940 = null; - } - - this.aClass3_941 = null; - return; - } - - var2.unlink(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ih.C(" + var1 + ')'); - } - } - - final Linkable method1212() { - try { - Linkable var2 = this.aClass3_940.previous; - - if (this.aClass3_940 == var2) { - this.aClass3_941 = null; - return null; - } else { - this.aClass3_941 = var2.previous; - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ih.B(" + 2 + ')'); - } - } - - final void method1215(Linkable var2) { - try { - if (null != var2.previous) { - var2.unlink(); - } - - var2.next = this.aClass3_940; - var2.previous = this.aClass3_940.previous; - var2.previous.next = var2; - var2.next.previous = var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ih.D(" + true + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final void method1216(Linkable var2) { - try { - if (null != var2.previous) { - var2.unlink(); - } - - var2.next = this.aClass3_940.next; - var2.previous = this.aClass3_940; - var2.previous.next = var2; - var2.next.previous = var2; - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ih.N(" + 64 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final Linkable method1219(int var1) { - try { - if (var1 < 13) { - this.aClass3_940 = null; - } - - Linkable var2 = this.aClass3_941; - if (this.aClass3_940 == var2) { - this.aClass3_941 = null; - return null; - } else { - this.aClass3_941 = var2.previous; - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ih.H(" + var1 + ')'); - } - } - - final Linkable method1220() { - try { - Linkable var2 = this.aClass3_940.next; - if (this.aClass3_940 == var2) { - return null; - } else { - var2.unlink(); - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ih.E(" + (byte) -3 + ')'); - } - } - - final Linkable method1221() { - try { - Linkable var2 = this.aClass3_941; - if (var2 == this.aClass3_940) { - this.aClass3_941 = null; - return null; - } else { - this.aClass3_941 = var2.next; - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ih.L(" + 1 + ')'); - } - } - - final Linkable method1222() { - try { - Linkable var2 = this.aClass3_940.next; - if (this.aClass3_940 == var2) { - this.aClass3_941 = null; - return null; - } else { - this.aClass3_941 = var2.next; - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ih.F(" + 1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/LoginHandler.java b/Client/src/main/java/org/runite/client/LoginHandler.java deleted file mode 100644 index d41ff501f..000000000 --- a/Client/src/main/java/org/runite/client/LoginHandler.java +++ /dev/null @@ -1,352 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -import org.rs09.client.net.Connection; - -import java.io.IOException; -import java.net.Socket; -import java.util.Objects; - -public class LoginHandler { - - private static final BufferedDataStream buffer = new BufferedDataStream(); - static CacheIndex aClass153_1680; - static boolean dynamic; - public static int loginStage = 0; - static long isaacServerKey = 0L; - - static void handleLogin() { - try { - if (0 != loginStage && 5 != loginStage) { - try { - if (++Class50.anInt820 > 2000) { - if (Class3_Sub15.activeConnection != null) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - if (Class166.anInt2079 >= 1) { - Client.messageToDisplay = -5; - loginStage = 0; - return; - } - - Class50.anInt820 = 0; - if (Class140_Sub6.accRegistryPort == Class162.anInt2036) { - Class140_Sub6.accRegistryPort = Client.currentPort; - } else { - Class140_Sub6.accRegistryPort = Class162.anInt2036; - } - - loginStage = 1; - ++Class166.anInt2079; - } - if (loginStage == 1) { - Class3_Sub9.aClass64_2318 = Class38.gameSignlink.method1441((byte) 8, Class38_Sub1.accRegistryIp, GameConfig.SERVER_PORT + GameConfig.WORLD);//Class140_Sub6.accRegistryPort); - loginStage = 2; - } - - if (loginStage == 2) { - if (Objects.requireNonNull(Class3_Sub9.aClass64_2318).anInt978 == 2) { - throw new IOException(); - } - - if (1 != Class3_Sub9.aClass64_2318.anInt978) { - return; - } - - Class3_Sub15.activeConnection = new Connection((Socket) Class3_Sub9.aClass64_2318.anObject974, Class38.gameSignlink); - Class3_Sub9.aClass64_2318 = null; - long var1 = PacketParser.aLong3202 = Class131.username.toLong(); - TextureOperation12.outgoingBuffer.index = 0; - TextureOperation12.outgoingBuffer.writeByte(14); - int nameHash = (int) (var1 >> 16 & 31L); - TextureOperation12.outgoingBuffer.writeByte(nameHash); - Class3_Sub15.activeConnection.sendBytes(TextureOperation12.outgoingBuffer.buffer, 2); - if (WorldListEntry.aAudioChannel_2627 != null) { - WorldListEntry.aAudioChannel_2627.method2159(); - } - - if (Class3_Sub21.aAudioChannel_2491 != null) { - Class3_Sub21.aAudioChannel_2491.method2159(); - } - - int var4 = Class3_Sub15.activeConnection.readByte(); - if (WorldListEntry.aAudioChannel_2627 != null) { - WorldListEntry.aAudioChannel_2627.method2159(); - } - - if (null != Class3_Sub21.aAudioChannel_2491) { - Class3_Sub21.aAudioChannel_2491.method2159(); - } - - if (var4 != 0) { - Client.messageToDisplay = var4; - loginStage = 0; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - - loginStage = 3; - } - - if (loginStage == 3) { - if (Class3_Sub15.activeConnection.availableBytes() < 8) { - return; - } - - Class3_Sub15.activeConnection.readBytes(BufferedDataStream.incomingBuffer.buffer, 0, 8); - BufferedDataStream.incomingBuffer.index = 0; - isaacServerKey = BufferedDataStream.incomingBuffer.readLong(); - int[] var9 = new int[4]; - TextureOperation12.outgoingBuffer.index = 0; - var9[2] = (int) (isaacServerKey >> 32); - var9[3] = (int) isaacServerKey; - var9[1] = (int) (Math.random() * 9.9999999E7D); - var9[0] = (int) (Math.random() * 9.9999999E7D); - TextureOperation12.outgoingBuffer.writeByte(10); - TextureOperation12.outgoingBuffer.writeInt(var9[0]); - TextureOperation12.outgoingBuffer.writeInt(var9[1]); - TextureOperation12.outgoingBuffer.writeInt(var9[2]); - TextureOperation12.outgoingBuffer.writeInt(var9[3]); - TextureOperation12.outgoingBuffer.writeLong(Class131.username.toLong()); - TextureOperation12.outgoingBuffer.writeString(Class131.password); - TextureOperation12.method229(); - TextureOperation12.outgoingBuffer.rsaEncrypt(TextureOperation10.EXPONENT, TextureOperation31.MODULUS); - buffer.index = 0; - if (40 == Class143.gameStage) { - buffer.writeByte(18); - } else { - buffer.writeByte(16); - } - - buffer.writeShort(TextureOperation12.outgoingBuffer.index + 163 - -TextureOperation29.method326((byte) 111, Class163_Sub2.paramSettings)); - buffer.writeInt(GameConfig.CLIENT_BUILD); - buffer.writeByte(Class7.anInt2161); - buffer.writeByte(!Client.paramAdvertisementSuppressed ? 0 : 1); - buffer.writeByte(1); - buffer.writeByte(Class83.getWindowType()); - buffer.writeShort(Class23.canvasWidth); - buffer.writeShort(Class140_Sub7.canvasHeight); - buffer.writeByte(Unsorted.anInt3671); - Class81.putRandomDataFile(buffer, true); - buffer.writeString(Class163_Sub2.paramSettings); - buffer.writeInt(Class3_Sub26.paramAffid); - buffer.writeInt(Class84.method1421()); - CS2Script.aBoolean2705 = true; - buffer.writeShort(Class113.interfacePacketCounter); - buffer.writeInt(CacheIndex.skeletonsIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.skinsIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.configurationsIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.interfacesIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.soundFXIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.landscapesIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.musicIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.modelsIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.spritesIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.texturesIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.huffmanEncodingIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.music2Index.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.interfaceScriptsIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.fontsIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.soundFX2Index.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.soundFX3Index.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.objectConfigIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.clientscriptMaskIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.npcConfigIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.itemConfigIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.animationIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.graphicFXIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.clientScriptConfigIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.worldmapIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.quickchatMessagesIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.quickchatMenusIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.materialsIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.particlesConfigIndex.getReferenceTableCrc()); - buffer.writeInt(CacheIndex.libIndex.getReferenceTableCrc()); - buffer.putBytes(TextureOperation12.outgoingBuffer.buffer, TextureOperation12.outgoingBuffer.index); - Class3_Sub15.activeConnection.sendBytes(buffer.buffer, buffer.index); - TextureOperation12.outgoingBuffer.method814(var9); - - for (int var2 = 0; var2 < 4; ++var2) { - var9[var2] += 50; - } - - BufferedDataStream.incomingBuffer.method814(var9); - loginStage = 4; - } - - if (loginStage == 4) { - if (Class3_Sub15.activeConnection.availableBytes() < 1) { - return; - } - - int opcode = Class3_Sub15.activeConnection.readByte(); - if (opcode == 21) { - loginStage = 7; - } else if (opcode == 29) { - loginStage = 10; - } else { - if (opcode == 1) { - loginStage = 5; - Client.messageToDisplay = opcode; - return; - } - - if (2 != opcode) { - if (opcode != 15) { - if (23 == opcode && Class166.anInt2079 < 1) { - loginStage = 1; - ++Class166.anInt2079; - Class50.anInt820 = 0; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - - Client.messageToDisplay = opcode; - loginStage = 0; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - - loginStage = 0; - Client.messageToDisplay = opcode; - return; - } - - loginStage = 8; - } - } - - if (6 == loginStage) { - TextureOperation12.outgoingBuffer.index = 0; - TextureOperation12.outgoingBuffer.putOpcode(17); - Class3_Sub15.activeConnection.sendBytes(TextureOperation12.outgoingBuffer.buffer, TextureOperation12.outgoingBuffer.index); - loginStage = 4; - return; - } - - if (loginStage == 7) { - if (Class3_Sub15.activeConnection.availableBytes() >= 1) { - TextureOperation25.anInt3413 = 60 * (3 + Class3_Sub15.activeConnection.readByte()); - loginStage = 0; - Client.messageToDisplay = 21; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - - return; - } - - if (loginStage == 10) { - if (1 <= Class3_Sub15.activeConnection.availableBytes()) { - Class3_Sub26.anInt2561 = Class3_Sub15.activeConnection.readByte(); - loginStage = 0; - Client.messageToDisplay = 29; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - - return; - } - - if (loginStage == 8) { - if (Class3_Sub15.activeConnection.availableBytes() < 14) { - return; - } - - Class3_Sub15.activeConnection.readBytes(BufferedDataStream.incomingBuffer.buffer, 0, 14); - BufferedDataStream.incomingBuffer.index = 0; - Player.rights = BufferedDataStream.incomingBuffer.readUnsignedByte(); - ClientLoader.setModPanelVisible(Player.rights == 2); - CS2Script.anInt3775 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class3_Sub15.aBoolean2433 = BufferedDataStream.incomingBuffer.readUnsignedByte() == 1; - Class121.aBoolean1641 = 1 == BufferedDataStream.incomingBuffer.readUnsignedByte(); - Unsorted.aBoolean4063 = BufferedDataStream.incomingBuffer.readUnsignedByte() == 1; - TextureOperation31.aBoolean3166 = 1 == BufferedDataStream.incomingBuffer.readUnsignedByte(); - Unsorted.aBoolean29 = BufferedDataStream.incomingBuffer.readUnsignedByte() == 1; - Class3_Sub1.localIndex = BufferedDataStream.incomingBuffer.readUnsignedShort(); - TextureOperation3.disableGEBoxes = BufferedDataStream.incomingBuffer.readUnsignedByte() == 1; - Unsorted.isMember = BufferedDataStream.incomingBuffer.readUnsignedByte() == 1; - Class113.method1702(Unsorted.isMember); - Class8.method845(Unsorted.isMember); - if (!Client.paramAdvertisementSuppressed) { - if ((!Class3_Sub15.aBoolean2433 || Unsorted.aBoolean4063) && !TextureOperation3.disableGEBoxes) { - try { - TextCore.aClass94_516.method1577(Class38.gameSignlink.gameApplet); - } catch (Throwable var5) { - } - } else { - try { - Class97.aClass94_1374.method1577(Class38.gameSignlink.gameApplet); - } catch (Throwable var6) { - } - } - } - - Unsorted.incomingOpcode = BufferedDataStream.incomingBuffer.getOpcode(); - dynamic = Unsorted.incomingOpcode == 214; - Unsorted.incomingPacketLength = BufferedDataStream.incomingBuffer.readUnsignedShort(); - loginStage = 9; - } - - if (loginStage == 9) { - if (Unsorted.incomingPacketLength > Class3_Sub15.activeConnection.availableBytes()) { - return; - } - - BufferedDataStream.incomingBuffer.index = 0; - Class3_Sub15.activeConnection.readBytes(BufferedDataStream.incomingBuffer.buffer, 0, Unsorted.incomingPacketLength); - Client.messageToDisplay = 2; - loginStage = 0; - SequenceDefinition.resetAll(); - Unsorted.anInt3606 = -1; - Class39.updateSceneGraph(dynamic); - Unsorted.incomingOpcode = -1; - return; - } - } catch (IOException var7) { - if (null != Class3_Sub15.activeConnection) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - if (Class166.anInt2079 >= 1) { - loginStage = 0; - Client.messageToDisplay = -4; - } else { - loginStage = 1; - Class50.anInt820 = 0; - ++Class166.anInt2079; - if (Class140_Sub6.accRegistryPort == Class162.anInt2036) { - Class140_Sub6.accRegistryPort = Client.currentPort; - } else { - Class140_Sub6.accRegistryPort = Class162.anInt2036; - } - } - } - - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ri.A(" + ')'); - } - } - - static int method1753(int var0, int var1) { - var1 = var1 * (var0 & 127) >> 7; - if (var1 < 2) { - var1 = 2; - } else if (var1 > 126) { - var1 = 126; - } - - return (var0 & '\uff80') + var1; - } - -} diff --git a/Client/src/main/java/org/runite/client/MapSceneDefinition.java b/Client/src/main/java/org/runite/client/MapSceneDefinition.java deleted file mode 100644 index a813bee39..000000000 --- a/Client/src/main/java/org/runite/client/MapSceneDefinition.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.runite.client; - -// TODO MapSceneDefinition or something -public final class MapSceneDefinition { - - public int color; - public int sprite; - public boolean aBoolean69 = false; - - public final void decode(DataBuffer buffer) { - while (true) { - int opcode = buffer.readUnsignedByte(); - if (opcode == 0) { - return; - } - - this.decode(buffer, opcode); - } - } - - /** - * @param rotations The amount of rotations the sprite should be turned clockwise. Each rotation rotates the sprite - * by 90 degrees clockwise. - */ - public final LDIndexedSprite getSprite(int rotations) { - LDIndexedSprite sprite = (LDIndexedSprite) TextureOperation2.aReferenceCache_3369.get((long) rotations << 16 | this.sprite); - if (sprite != null) { - return sprite; - } - - KeyboardListener.aClass153_1916.retrieveSpriteFile(this.sprite); - sprite = Unsorted.method1539(this.sprite, KeyboardListener.aClass153_1916); - if (sprite != null) { - sprite.method1668(Class102.anInt2136, Class46.anInt740, Class158.anInt2015); - sprite.anInt1469 = sprite.width; - sprite.anInt1467 = sprite.height; - - for (int var5 = 0; var5 < rotations; ++var5) { - sprite.rotateClockwise(); - } - - TextureOperation2.aReferenceCache_3369.put(sprite, (long) rotations << 16 | this.sprite); - } - - return sprite; - } - - private void decode(DataBuffer buffer, int opcode) { - if (opcode == 1) { - this.sprite = buffer.readUnsignedShort(); - } else if (opcode == 2) { - this.color = buffer.readMedium(); - } else if (opcode == 3) { - this.aBoolean69 = true; - } else if (opcode == 4) { - this.sprite = -1; - } - } - -} diff --git a/Client/src/main/java/org/runite/client/MessageManager.java b/Client/src/main/java/org/runite/client/MessageManager.java deleted file mode 100644 index bd6eaab37..000000000 --- a/Client/src/main/java/org/runite/client/MessageManager.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.runite.client; - -public final class MessageManager { - - static RSString[] aClass94Array3226 = new RSString[100]; - static int[] anIntArray1835 = new int[100]; - static int[] anIntArray3082 = new int[100]; - - static void sendGameMessage(int var0, int type, RSString message, RSString var3, RSString var5) { - for (int i = 99; i > 0; --i) { - anIntArray3082[i] = anIntArray3082[i - 1]; - aClass94Array3226[i] = aClass94Array3226[i - 1]; - LinkableRSString.aClass94Array2580[i] = LinkableRSString.aClass94Array2580[-1 + i]; - Class163_Sub3.aClass94Array3003[i] = Class163_Sub3.aClass94Array3003[i + -1]; - anIntArray1835[i] = anIntArray1835[i - 1]; - } - - ++TextureOperation16.anInt3114; - anIntArray3082[0] = type; - aClass94Array3226[0] = var5; - Class24.anInt472 = PacketParser.anInt3213; - anIntArray1835[0] = var0; - RSString primaryMsg = RSString.parse("null"); - RSString secondaryMsg = RSString.parse("null"); - int cutOff = 81 - (var3 != null ? var3.length : 0) - (var5 != null ? var5.length : 0); - if(message.length > cutOff && type != 0){ - String[] tokens = message.toString().split(" "); - if(tokens.length > 1) { - int counter = 0; - for (String tok : tokens) { - if (counter + tok.length() > cutOff) { - break; - } - counter += tok.length() + 1; - } - primaryMsg = message.substring(0, counter, 0); - secondaryMsg = message.substring(counter, message.length, 0); - message = primaryMsg; - } - } - LinkableRSString.aClass94Array2580[0] = message; - Class163_Sub3.aClass94Array3003[0] = var3; - if(!secondaryMsg.equalsString(RSString.parse("null"))) - sendGameMessage(var0,type,secondaryMsg,var3,var5); - } -} diff --git a/Client/src/main/java/org/runite/client/ModPanel.java b/Client/src/main/java/org/runite/client/ModPanel.java deleted file mode 100644 index b34872e48..000000000 --- a/Client/src/main/java/org/runite/client/ModPanel.java +++ /dev/null @@ -1,628 +0,0 @@ -package org.runite.client; - -import org.rs09.SystemLogger; -import org.rs09.client.config.GameConfig; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableRowSorter; -import java.awt.*; -import java.awt.event.*; -import java.util.EventObject; - -public class ModPanel extends JPanel { - ButtonListener listener = new ButtonListener(); - DebugMenu debugMenu = new DebugMenu(); - ItemMenu itemMenu = new ItemMenu(); - NPCMenu npcMenu = new NPCMenu(); - PlayerMenu playerMenu = new PlayerMenu(); - TeleMenu teleMenu = new TeleMenu(); - public ModPanel() { - setVisible(false); - JButton teleMenu = new JButton("Tele"); - teleMenu.setActionCommand("tele"); - teleMenu.addActionListener(listener); - - JButton playerMenu = new JButton("Player"); - playerMenu.setActionCommand("player"); - playerMenu.addActionListener(listener); - - JButton npcMenu = new JButton("NPC"); - npcMenu.setActionCommand("npc"); - npcMenu.addActionListener(listener); - - JButton itemMenu = new JButton("Item"); - itemMenu.setActionCommand("item"); - itemMenu.addActionListener(listener); - - JButton debugMenu = new JButton("Debug"); - debugMenu.setActionCommand("debug"); - debugMenu.addActionListener(listener); - - JButton closeButton = new JButton("Close Panel"); - closeButton.setActionCommand("close"); - closeButton.addActionListener(listener); - - add(teleMenu); - add(playerMenu); - add(npcMenu); - add(itemMenu); - add(debugMenu); - add(closeButton, BorderLayout.EAST); - } - - private void sendCommand(String command) { - TextureOperation12.outgoingBuffer.putOpcode(44); - TextureOperation12.outgoingBuffer.writeByte(command.length() + -1); - TextureOperation12.outgoingBuffer.writeString(command.substring(2)); - } - - class ButtonListener implements ActionListener { - - @Override - public void actionPerformed(ActionEvent actionEvent) { - switch (actionEvent.getActionCommand()) { - case "close": - ClientLoader.setModPanelVisible(false); - break; - case "debug": - debugMenu.setVisible(true); - break; - case "item": - itemMenu.open(); - break; - case "npc": - npcMenu.open(); - break; - case "player": - playerMenu.open(); - break; - case "tele": - teleMenu.open(); - break; - } - } - } - - class TeleMenu extends JFrame { - Object[][] rowData = { - {"2974,4383,2", "corporeal beast"}, - {"2659,2649,0", "pest control"}, - {"3293,3184,0", "al kharid"}, - {"3222,3217,0", "lumbridge"}, - {"3110,3168,0", "wizard's tower"}, - {"3083,3249,0", "draynor village"}, - {"3019,3244,0", "port sarim"}, - {"2956,3209,0", "rimmington"}, - {"2965,3380,0", "falador"}, - {"2895,3436,0", "taverly"}, - {"3080,3423,0", "barbarian village"}, - {"3213,3428,0", "varrock"}, - {"3164,3485,0", "grand exchange"}, - {"2917,3175,0", "karamja"}, - {"2450,5165,0", "tzhaar"}, - {"2795,3177,0", "brimhaven"}, - {"2849,2961,0", "shilo village"}, - {"2605,3093,0", "yanille"}, - {"2663,3305,0", "ardougne"}, - {"2450,3422,0", "tree gnome stronghold"}, - {"2730,3485,0", "camelot"}, - {"2730,3485,0", "seer's village"}, - {"2805,3435,0", "catherby"}, - {"2658,3657,0", "relleka"}, - {"2890,3676,0", "trollheim"}, - {"2914,3746,0", "god wars dungeon"}, - {"3180,3684,0", "bounty hunter"}, - {"3272,3687,0", "clan wars"}, - {"3090,3957,0", "mage arena"}, - {"3069,10257,0", "king black dragon"}, - {"3359,3416,0", "digsite"}, - {"3488,3489,0", "canifis"}, - {"3428,3526,0", "slayer tower"}, - {"3502,9483,0", "kalphite queen"}, - {"3233,2913,0", "pyramid"}, - {"3419,2917,0", "nardah"}, - {"3482,3090,0", "uzer"}, - {"3358,2970,0", "pollnivneach"}, - {"3305,2788,0", "sophanem"}, - {"2898,3544,0", "burthorpe"}, - {"3088,3491,0", "edgeville"}, - {"3169,3034,0", "bedabin"}, - {"3565,3289,0", "barrows"}, - {"3016,3513,0", "black knight's fortress"}, - {"3052,3481,0", "monastery"} - }; - JTextField playerNameField = new JTextField(); - JTextField coordsField = new JTextField(); - JCheckBox toMeToggle = new JCheckBox("To Me"); - JTable table = new JTable(rowData, new Object[]{"Coords", "Name"}) { - @Override - public boolean editCellAt(int i, int i1, EventObject eventObject) { - return false; - } - - @Override - public Object getValueAt(int i, int i1) { - return rowData[i][i1]; - } - - @Override - public int getRowCount() { - return rowData.length; - } - }; - DefaultTableCellRenderer cellRenderer = new DefaultTableCellRenderer(); - - ActionListener buttonListener = actionEvent -> { - String command = actionEvent.getActionCommand(); - switch (command) { - case "telePlayer": - if (toMeToggle.isSelected()) { - sendCommand("::teletome " + playerNameField.getText()); - } else { - sendCommand("::teleto " + playerNameField.getText()); - } - break; - case "teleCoords": - String[] coordTokens = coordsField.getText().split(","); - if (coordTokens.length < 2) { - coordsField.setText(""); - break; - } - int x, y, z = 0; - try { - x = Integer.parseInt(coordTokens[0]); - y = Integer.parseInt(coordTokens[1]); - if (coordTokens.length == 3) z = Integer.parseInt(coordTokens[2]); - } catch (NumberFormatException e) { - SystemLogger.logErr("(ModPanel.java:118) Invalid value passed to coords tele."); - coordsField.setText(""); - break; - } - sendCoordTele(x, y, z); - break; - } - }; - - TeleMenu() { - super("Teleport Menu"); - setLayout(new BorderLayout()); - - JPanel playerTelePanel = new JPanel(); - playerTelePanel.setLayout(new FlowLayout()); - JLabel playerNameLabel = new JLabel("Player Name:"); - JButton teleButton = new JButton("Tele"); - teleButton.setActionCommand("telePlayer"); - teleButton.addActionListener(buttonListener); - playerNameField.setPreferredSize(new Dimension(100, 20)); - playerTelePanel.add(playerNameLabel); - playerTelePanel.add(playerNameField); - playerTelePanel.add(toMeToggle); - playerTelePanel.add(teleButton); - - JPanel coordTelePanel = new JPanel(); - JLabel coordsLabel = new JLabel("Coordinates:"); - teleButton = new JButton("Tele"); - teleButton.setActionCommand("teleCoords"); - teleButton.addActionListener(buttonListener); - coordsField.setPreferredSize(new Dimension(60, 20)); - coordTelePanel.add(coordsLabel); - coordTelePanel.add(coordsField); - coordTelePanel.add(teleButton); - - table.addMouseListener(new MouseListener() { - @Override - public void mouseClicked(MouseEvent mouseEvent) { - if (mouseEvent.getClickCount() == 2) { - JTable table = (JTable) mouseEvent.getSource(); - int row = table.getSelectedRow(); - String coordTokens = table.getValueAt(row, 0).toString(); - sendCommand("::tele " + coordTokens.replace(",", " ")); - SystemLogger.logInfo("Sending teleport command to " + coordTokens); - } - } - - @Override - public void mousePressed(MouseEvent mouseEvent) { - } - - @Override - public void mouseReleased(MouseEvent mouseEvent) { - } - - @Override - public void mouseEntered(MouseEvent mouseEvent) { - } - - @Override - public void mouseExited(MouseEvent mouseEvent) { - } - }); - cellRenderer.setToolTipText("Double-click to teleport."); - table.getColumnModel().getColumn(0).setMaxWidth(250); - table.getColumnModel().getColumn(0).setCellRenderer(cellRenderer); - table.getColumnModel().getColumn(1).setCellRenderer(cellRenderer); - - JPanel fullTele = new JPanel(); - fullTele.setLayout(new BoxLayout(fullTele, BoxLayout.PAGE_AXIS)); - JScrollPane scrollPane = new JScrollPane(table); - fullTele.add(scrollPane); - - add(playerTelePanel, BorderLayout.NORTH); - add(coordTelePanel, BorderLayout.CENTER); - add(fullTele, BorderLayout.SOUTH); - pack(); - } - - private void open() { - setVisible(true); - } - - private void sendCoordTele(int x, int y, int z) { - sendCommand("::tele " + x + " " + y + " " + z); - } - - private void sendCoordTele(int x, int y) { - sendCoordTele(x, y, 0); - } - } - - class PlayerMenu extends JFrame { - JTextField nameField = new JTextField(); - JTextField durationField = new JTextField(); - ItemListener itemListener = new ItemListener() { - @Override - public void itemStateChanged(ItemEvent itemEvent) { - String title = ((JCheckBox) itemEvent.getItem()).getText(); - switch (title) { - case "God Mode": - sendCommand("::god"); - break; - case "Instakill": - sendCommand("::1hit"); - break; - } - } - }; - ActionListener listener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - String actionName = actionEvent.getActionCommand(); - switch (actionName) { - case "jail": - sendCommand("::jail " + durationField.getText() + " " + nameField.getText()); - break; - case "empty": - sendCommand("::empty"); - break; - } - } - }; - - PlayerMenu() { - super("Player Options"); - setLayout(new BorderLayout()); - - JPanel jailPanel = new JPanel(); - jailPanel.setLayout(new FlowLayout()); - JLabel nameLabel = new JLabel("Name:"); - JLabel durationLabel = new JLabel("Duration(seconds):"); - JButton jailButton = new JButton("Jail"); - - jailPanel.add(nameLabel); - jailPanel.add(nameField); - nameField.setPreferredSize(new Dimension(100, 20)); - jailPanel.add(durationLabel); - jailPanel.add(durationField); - durationField.setPreferredSize(new Dimension(55, 20)); - jailPanel.add(jailButton); - jailButton.setActionCommand("jail"); - jailButton.addActionListener(listener); - - add(jailPanel, BorderLayout.NORTH); - - JPanel selfPanel = new JPanel(); - selfPanel.setLayout(new FlowLayout()); - JLabel selfLabel = new JLabel("Self: "); - JCheckBox godCheck = new JCheckBox("God Mode"); - JCheckBox instakillCheck = new JCheckBox("Instakill"); - godCheck.addItemListener(itemListener); - instakillCheck.addItemListener(itemListener); - JButton emptyButton = new JButton("Empty Inv"); - emptyButton.setActionCommand("empty"); - emptyButton.addActionListener(listener); - selfPanel.add(selfLabel); - selfPanel.add(godCheck); - selfPanel.add(instakillCheck); - selfPanel.add(emptyButton); - - add(selfPanel, BorderLayout.SOUTH); - - pack(); - - setVisible(false); - } - - public void open() { - setVisible(true); - } - } - - class NPCMenu extends JFrame { - DefaultTableModel model = new DefaultTableModel(); - JTextField searchField = new JTextField(); - JTextField amountField = new JTextField(); - TableRowSorter sorter = new TableRowSorter<>(model); - DefaultTableCellRenderer cellRenderer = new DefaultTableCellRenderer(); - boolean populated = false; - - NPCMenu() { - super("NPC Spawning Menu"); - setLayout(new BorderLayout()); - - cellRenderer.setToolTipText("Double-Click to spawn."); - JPanel searchPanel = new JPanel(); - JLabel searchLabel = new JLabel("Search for NPC:"); - searchField.setPreferredSize(new Dimension(100, 20)); - searchPanel.add(searchLabel); - searchPanel.add(searchField); - add(searchPanel, BorderLayout.NORTH); - setLocationRelativeTo(null); - - JTable npcTable = new JTable(model) { - @Override - public boolean editCellAt(int i, int i1, EventObject eventObject) { - return false; - } - }; - model.addColumn("ID"); - model.addColumn("Name"); - npcTable.setRowSorter(sorter); - npcTable.getColumnModel().getColumn(0).setMaxWidth(55); - npcTable.getColumnModel().getColumn(0).setCellRenderer(cellRenderer); - npcTable.getColumnModel().getColumn(1).setCellRenderer(cellRenderer); - - - npcTable.addMouseListener(new MouseListener() { - @Override - public void mouseClicked(MouseEvent mouseEvent) { - if (mouseEvent.getClickCount() == 2) { - JTable table = (JTable) mouseEvent.getSource(); - int row = table.getSelectedRow(); - sendCommand("::npc " + table.getValueAt(row, 0) + (amountField.getText().isEmpty() ? "" : (" " + amountField.getText()))); - SystemLogger.logInfo("Sending spawn command for NPC: " + table.getValueAt(row, 1) + " " + amountField.getText()); - } - } - - @Override - public void mousePressed(MouseEvent mouseEvent) { - } - - @Override - public void mouseReleased(MouseEvent mouseEvent) { - } - - @Override - public void mouseEntered(MouseEvent mouseEvent) { - } - - @Override - public void mouseExited(MouseEvent mouseEvent) { - } - }); - - searchField.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent keyEvent) { - filter(); - } - - @Override - public void keyPressed(KeyEvent keyEvent) { - } - - @Override - public void keyReleased(KeyEvent keyEvent) { - } - }); - - - JScrollPane scrollPane = new JScrollPane(npcTable); - add(scrollPane, BorderLayout.SOUTH); - - pack(); - setVisible(false); - } - - private void filter() { - RowFilter rf = null; - //If current expression doesn't parse, don't update. - try { - rf = RowFilter.regexFilter(searchField.getText(), 1); - } catch (java.util.regex.PatternSyntaxException e) { - return; - } - sorter.setRowFilter(rf); - } - - public void open() { - if (!populated) { - SwingUtilities.invokeLater(() -> { - for (int i = 0; i < 14649; i++) { - NPCDefinition def = NPCDefinition.getNPCDefinition(i); - if (def == null) continue; - model.addRow(new Object[]{i, def.NPCName.toLowercase()}); - } - setVisible(true); - populated = true; - }); - } else { - setVisible(true); - } - } - } - - class ItemMenu extends JFrame { - DefaultTableModel model = new DefaultTableModel(); - JTextField searchField = new JTextField(); - JTextField amountField = new JTextField(); - TableRowSorter sorter = new TableRowSorter<>(model); - DefaultTableCellRenderer cellRenderer = new DefaultTableCellRenderer(); - boolean populated = false; - - ItemMenu() { - super("Item Spawning Menu"); - setLayout(new BorderLayout()); - cellRenderer.setToolTipText("Double-Click to spawn."); - - JPanel searchPanel = new JPanel(); - JLabel searchLabel = new JLabel("Search for Item:"); - JLabel amountLabel = new JLabel("Amount: "); - amountField.setPreferredSize(new Dimension(45, 20)); - searchField.setPreferredSize(new Dimension(100, 20)); - searchPanel.add(searchLabel); - searchPanel.add(searchField); - searchPanel.add(amountLabel); - searchPanel.add(amountField); - add(searchPanel, BorderLayout.NORTH); - setLocationRelativeTo(null); - - JTable itemTable = new JTable(model) { - @Override - public boolean editCellAt(int i, int i1, EventObject eventObject) { - return false; - } - }; - model.addColumn("ID"); - model.addColumn("Name"); - itemTable.setRowSorter(sorter); - itemTable.getColumnModel().getColumn(0).setMaxWidth(55); - itemTable.getColumnModel().getColumn(0).setCellRenderer(cellRenderer); - itemTable.getColumnModel().getColumn(1).setCellRenderer(cellRenderer); - - itemTable.addMouseListener(new MouseListener() { - @Override - public void mouseClicked(MouseEvent mouseEvent) { - if (mouseEvent.getClickCount() == 2) { - JTable table = (JTable) mouseEvent.getSource(); - int row = table.getSelectedRow(); - sendCommand("::item " + table.getValueAt(row, 0) + (amountField.getText().isEmpty() ? "" : (" " + amountField.getText()))); - SystemLogger.logInfo("Sending spawn command for item: " + table.getValueAt(row, 1) + " " + amountField.getText()); - } - } - - @Override - public void mousePressed(MouseEvent mouseEvent) { - } - - @Override - public void mouseReleased(MouseEvent mouseEvent) { - } - - @Override - public void mouseEntered(MouseEvent mouseEvent) { - } - - @Override - public void mouseExited(MouseEvent mouseEvent) { - } - }); - - searchField.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent keyEvent) { - filter(); - } - - @Override - public void keyPressed(KeyEvent keyEvent) { - } - - @Override - public void keyReleased(KeyEvent keyEvent) { - } - }); - - - JScrollPane scrollPane = new JScrollPane(itemTable); - add(scrollPane, BorderLayout.SOUTH); - - pack(); - setVisible(false); - } - - private void filter() { - RowFilter rf = null; - //If current expression doesn't parse, don't update. - try { - rf = RowFilter.regexFilter(searchField.getText(), 1); - } catch (java.util.regex.PatternSyntaxException e) { - return; - } - sorter.setRowFilter(rf); - } - - public void open() { - if (!populated) { - SwingUtilities.invokeLater(() -> { - for (int i = 0; i < 14649; i++) { - ItemDefinition def = ItemDefinition.getItemDefinition(i); - if (def == null) continue; - model.addRow(new Object[]{i, def.name.toLowercase()}); - } - setVisible(true); - populated = true; - }); - } else { - setVisible(true); - } - } - } - - class DebugMenu extends JFrame { - DebugMenu() { - super("Debug Options"); - ItemListener listener = new CheckBoxListener(); - setResizable(false); - setLayout(new FlowLayout()); - setLocationRelativeTo(null); - setDefaultCloseOperation(HIDE_ON_CLOSE); - JCheckBox itemDebug = new JCheckBox("Item IDs"); - JCheckBox npcDebug = new JCheckBox("NPC IDs"); - JCheckBox objectDebug = new JCheckBox("Object IDs"); - JCheckBox debugMode = new JCheckBox("Debug Output"); - itemDebug.addItemListener(listener); - npcDebug.addItemListener(listener); - objectDebug.addItemListener(listener); - debugMode.addItemListener(listener); - add(itemDebug); - add(npcDebug); - add(objectDebug); - add(debugMode); - pack(); - setVisible(false); - } - - class CheckBoxListener implements ItemListener { - @Override - public void itemStateChanged(ItemEvent itemEvent) { - String title = ((JCheckBox) itemEvent.getItem()).getText(); - switch (title) { - case "Item IDs": - GameConfig.ITEM_DEBUG_ENABLED = itemEvent.getStateChange() == ItemEvent.SELECTED; - break; - case "NPC IDs": - GameConfig.NPC_DEBUG_ENABLED = itemEvent.getStateChange() == ItemEvent.SELECTED; - break; - case "Object IDs": - GameConfig.OBJECT_DEBUG_ENABLED = itemEvent.getStateChange() == ItemEvent.SELECTED; - break; - case "Debug Output": - sendCommand("::debug"); - break; - } - } - } - } -} diff --git a/Client/src/main/java/org/runite/client/Model.java b/Client/src/main/java/org/runite/client/Model.java deleted file mode 100644 index 889ba4af6..000000000 --- a/Client/src/main/java/org/runite/client/Model.java +++ /dev/null @@ -1,377 +0,0 @@ -package org.runite.client; - -abstract class Model extends GameObject { - - boolean aBoolean2699 = false; - - private static int method1878(int[][] var0, int var1, int var2) { - int var3 = var1 >> 7; - int var4 = var2 >> 7; - if (var3 >= 0 && var4 >= 0 && var3 < var0.length && var4 < var0[0].length) { - int var5 = var1 & 127; - int var6 = var2 & 127; - int var7 = var0[var3][var4] * (128 - var5) + var0[var3 + 1][var4] * var5 >> 7; - int var8 = var0[var3][var4 + 1] * (128 - var5) + var0[var3 + 1][var4 + 1] * var5 >> 7; - return var7 * (128 - var6) + var8 * var6 >> 7; - } else { - return 0; - } - } - - abstract int method1872(); - - abstract int method1871(); - - abstract boolean method1873(); - - abstract void method1874(); - - private void method1875(Class3_Sub17 var1, AnimationHeader var2, AnimationHeader var3, int var4, int var5, boolean[] var6, boolean var7, boolean var8, int var9, int[] var10) { - int var11; - if (var3 == null || var4 == 0) { - for (var11 = 0; var11 < var2.anInt1383; ++var11) { - short var12 = var2.aShortArray1385[var11]; - if (var6 == null || var6[var12] == var7 || var1.anIntArray2466[var12] == 0) { - short var13 = var2.aShortArray1387[var11]; - int var14; - if (var13 != -1) { - var14 = var9 & var1.anIntArray2455[var13]; - if (var14 == 65535) { - this.method1891(0, var1.anIntArrayArray2460[var13], 0, 0, 0, var8); - } else { - this.method1899(0, var1.anIntArrayArray2460[var13], 0, 0, 0, var8, var14, var10); - } - } - - var14 = var9 & var1.anIntArray2455[var12]; - if (var14 == 65535) { - this.method1891(var1.anIntArray2466[var12], var1.anIntArrayArray2460[var12], var2.aShortArray1388[var11], var2.aShortArray1396[var11], var2.aShortArray1395[var11], var8); - } else { - this.method1899(var1.anIntArray2466[var12], var1.anIntArrayArray2460[var12], var2.aShortArray1388[var11], var2.aShortArray1396[var11], var2.aShortArray1395[var11], var8, var14, var10); - } - } - } - - } else { - var11 = 0; - int var34 = 0; - - for (int var35 = 0; var35 < var1.anInt2462; ++var35) { - boolean var36 = false; - if (var11 < var2.anInt1383 && var2.aShortArray1385[var11] == var35) { - var36 = true; - } - - boolean var15 = false; - if (var34 < var3.anInt1383 && var3.aShortArray1385[var34] == var35) { - var15 = true; - } - - if (var36 || var15) { - if (var6 != null && var6[var35] != var7 && var1.anIntArray2466[var35] != 0) { - if (var36) { - ++var11; - } - - if (var15) { - ++var34; - } - } else { - short var16 = 0; - int var17 = var1.anIntArray2466[var35]; - if (var17 == 3) { - var16 = 128; - } - - short var19; - short var18; - short var21; - short var20; - byte var22; - if (var36) { - var18 = var2.aShortArray1388[var11]; - var19 = var2.aShortArray1396[var11]; - var20 = var2.aShortArray1395[var11]; - var21 = var2.aShortArray1387[var11]; - var22 = var2.aByteArray1393[var11]; - ++var11; - } else { - var18 = var16; - var19 = var16; - var20 = var16; - var21 = -1; - var22 = 0; - } - - short var23; - short var25; - short var24; - byte var27; - short var26; - if (var15) { - var23 = var3.aShortArray1388[var34]; - var24 = var3.aShortArray1396[var34]; - var25 = var3.aShortArray1395[var34]; - var26 = var3.aShortArray1387[var34]; - var27 = var3.aByteArray1393[var34]; - ++var34; - } else { - var23 = var16; - var24 = var16; - var25 = var16; - var26 = -1; - var27 = 0; - } - - int var29; - int var28; - int var31; - int var30; - if ((var22 & 2) == 0 && (var27 & 1) == 0) { - if (var17 == 2) { - var31 = var23 - var18 & 0x7FF; - int var32 = var24 - var19 & 0x7FF; - int var33 = var25 - var20 & 0x7FF; - if (var31 >= 1024) { - var31 -= 2048; - } - - if (var32 >= 1024) { - var32 -= 2048; - } - - if (var33 >= 1024) { - var33 -= 2048; - } - - var28 = var18 + var31 * var4 / var5 & 0x7FF; - var29 = var19 + var32 * var4 / var5 & 0x7FF; - var30 = var20 + var33 * var4 / var5 & 0x7FF; - } else if (var17 == 7) { - var31 = var23 - var18 & 63; - if (var31 >= 32) { - var31 -= 64; - } - - var28 = var18 + var31 * var4 / var5 & 63; - var29 = var19 + (var24 - var19) * var4 / var5; - var30 = var20 + (var25 - var20) * var4 / var5; - } else { - var28 = var18 + (var23 - var18) * var4 / var5; - var29 = var19 + (var24 - var19) * var4 / var5; - var30 = var20 + (var25 - var20) * var4 / var5; - } - } else { - var28 = var18; - var29 = var19; - var30 = var20; - } - - if (var21 != -1) { - var31 = var9 & var1.anIntArray2455[var21]; - if (var31 == 65535) { - this.method1891(0, var1.anIntArrayArray2460[var21], 0, 0, 0, var8); - } else { - this.method1899(0, var1.anIntArrayArray2460[var21], 0, 0, 0, var8, var31, var10); - } - } else if (var26 != -1) { - var31 = var9 & var1.anIntArray2455[var26]; - if (var31 == 65535) { - this.method1891(0, var1.anIntArrayArray2460[var26], 0, 0, 0, var8); - } else { - this.method1899(0, var1.anIntArrayArray2460[var26], 0, 0, 0, var8, var31, var10); - } - } - - var31 = var9 & var1.anIntArray2455[var35]; - if (var31 == 65535) { - this.method1891(var17, var1.anIntArrayArray2460[var35], var28, var29, var30, var8); - } else { - this.method1899(var17, var1.anIntArrayArray2460[var35], var28, var29, var30, var8, var31, var10); - } - } - } - } - - } - } - - abstract void method1876(int var1); - - final void method1877(Class3_Sub28_Sub5 var1, int var2) { - if (var2 != -1) { - if (this.method1873()) { - AnimationHeader var3 = var1.animations[var2]; - Class3_Sub17 var4 = var3.skins; - - for (int var5 = 0; var5 < var3.anInt1383; ++var5) { - short var6 = var3.aShortArray1385[var5]; - if (var4.aBooleanArray2463[var6]) { - if (var3.aShortArray1387[var5] != -1) { - this.method1889(0, 0, 0, 0); - } - - this.method1889(var4.anIntArray2466[var6], var3.aShortArray1388[var5], var3.aShortArray1396[var5], var3.aShortArray1395[var5]); - } - } - - this.method1879(); - } - } - } - - abstract void method1879(); - - final void method1880(Class3_Sub28_Sub5 var1, int var2, Class3_Sub28_Sub5 var3, int var4, int var5, int var6, boolean var7) { - if (var2 != -1) { - if (this.method1873()) { - AnimationHeader var8 = var1.animations[var2]; - Class3_Sub17 var9 = var8.skins; - AnimationHeader var10 = null; - if (var3 != null) { - var10 = var3.animations[var4]; - if (var10.skins != var9) { - var10 = null; - } - } - - this.method1875(var9, var8, var10, var5, var6, null, false, var7, 65535, null); - this.method1879(); - } - } - } - - abstract void resize(int var1, int var2, int var3); - - abstract Model method1882(boolean var1, boolean var2, boolean var3); - - abstract int method1883(); - - abstract int method1884(); - - abstract void method1885(); - - abstract void method1886(int var1); - - final void method1887(Class3_Sub28_Sub5 var1, int var2, Class3_Sub28_Sub5 var3, int var4, int var5, int var6, int var7, boolean var8, int[] var9) { - if (var2 != -1) { - if (this.method1873()) { - AnimationHeader var10 = var1.animations[var2]; - Class3_Sub17 var11 = var10.skins; - AnimationHeader var12 = null; - if (var3 != null) { - var12 = var3.animations[var4]; - if (var12.skins != var11) { - var12 = null; - } - } - - this.method1875(var11, var10, var12, var5, var6, null, false, var8, var7, var9); - this.method1879(); - } - } - } - - abstract int method1888(); - - abstract void method1889(int var1, int var2, int var3, int var4); - - abstract Model method1890(boolean var1, boolean var2, boolean var3); - - abstract void method1891(int var1, int[] var2, int var3, int var4, int var5, boolean var6); - - final void method1892(Class3_Sub28_Sub5 var1, int var2, Class3_Sub28_Sub5 var3, int var4, int var5, int var6, Class3_Sub28_Sub5 var7, int var8, Class3_Sub28_Sub5 var9, int var10, int var11, int var12, boolean[] debugArray530, boolean var14) { - if (var2 != -1) { - if (debugArray530 == null || var8 == -1) { - this.method1880(var1, var2, var3, var4, var5, var6, var14); - } else if (this.method1873()) { - AnimationHeader var15 = var1.animations[var2]; - Class3_Sub17 var16 = var15.skins; - AnimationHeader var17 = null; - if (var3 != null) { - var17 = var3.animations[var4]; - if (var17.skins != var16) { - var17 = null; - } - } - - AnimationHeader var18 = var7.animations[var8]; - AnimationHeader var19 = null; - if (var9 != null) { - var19 = var9.animations[var10]; - if (var19.skins != var16) { - var19 = null; - } - } - - this.method1875(var16, var15, var17, var5, var6, debugArray530, false, var14, 65535, null); - this.method1891(0, new int[0], 0, 0, 0, var14); - this.method1875(var16, var18, var19, var11, var12, debugArray530, true, var14, 65535, null); - this.method1879(); - } - } - } - - abstract void method1893(int var2, int var3, int var4, int var5, int var6, int var7); - - abstract Model method1894(boolean var1, boolean var2, boolean var3); - - final void method1895(int[][] var1, int var2, int var3, int var4, int var5, int var6) { - int var10 = -var5 / 2; - int var11 = -var6 / 2; - int var12 = method1878(var1, var2 + var10, var4 + var11); - int var13 = var5 / 2; - int var14 = -var6 / 2; - int var15 = method1878(var1, var2 + var13, var4 + var14); - int var16 = -var5 / 2; - int var17 = var6 / 2; - int var18 = method1878(var1, var2 + var16, var4 + var17); - int var19 = var5 / 2; - int var20 = var6 / 2; - int var21 = method1878(var1, var2 + var19, var4 + var20); - int var22 = var12 < var15 ? var12 : var15; - int var23 = var18 < var21 ? var18 : var21; - int var24 = var15 < var21 ? var15 : var21; - int var25 = var12 < var18 ? var12 : var18; - if (var6 != 0) { - int var26 = (int) (Math.atan2(var22 - var23, var6) * 325.95D) & 0x7FF; - if (var26 != 0) { - this.method1896(var26); - } - } - - if (var5 != 0) { - int var27 = (int) (Math.atan2(var25 - var24, var5) * 325.95D) & 0x7FF; - if (var27 != 0) { - this.method1886(var27); - } - } - - int var28 = var12 + var21; - if (var15 + var18 < var28) { - var28 = var15 + var18; - } - - var28 = (var28 >> 1) - var3; - if (var28 != 0) { - this.method1897(0, var28, 0); - } - - } - - abstract void method1896(int var1); - - final void method1867(int var1, int var2, int var3, int var4, int var5) { - } - - abstract void method1897(int var1, int var2, int var3); - - abstract int method1898(); - - abstract void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12); - - abstract void method1899(int var1, int[] var2, int var3, int var4, int var5, boolean var6, int var7, int[] var8); - - abstract void method1900(); - -} diff --git a/Client/src/main/java/org/runite/client/Model_Sub1.java b/Client/src/main/java/org/runite/client/Model_Sub1.java deleted file mode 100644 index d160e2227..000000000 --- a/Client/src/main/java/org/runite/client/Model_Sub1.java +++ /dev/null @@ -1,1771 +0,0 @@ -package org.runite.client; - -final class Model_Sub1 extends GameObject { - - private static final int[] anIntArray2861 = new int[10000]; - private static final int[] anIntArray2863 = Class51.anIntArray851; - private static final int[] anIntArray2871 = Class51.anIntArray840; - private static final int[] anIntArray2875 = new int[10000]; - private static int anInt2868 = 0; - byte[] aByteArray2843; - byte[] aByteArray2845; - short[] aShortArray2846; - int[] anIntArray2847; - byte aByte2848 = 0; - int anInt2849 = 0; - short[] aShortArray2851; - byte[] aByteArray2852; - short[] aShortArray2855; - int[][] anIntArrayArray2856; - byte[] aByteArray2857; - short[] aShortArray2858; - byte[] aByteArray2859; - int[] anIntArray2860; - int anInt2862; - int[] anIntArray2864; - int[] anIntArray2865; - byte[] aByteArray2866; - byte[] aByteArray2867; - byte[] aByteArray2869; - short[] aShortArray2870; - Class50[] aClass50Array2872; - short aShort2876; - byte[] aByteArray2877; - int[] anIntArray2878; - short aShort2879; - int[] anIntArray2881; - short[] aShortArray2882; - Class50[] aClass50Array2883; - short[] aShortArray2884; - int[] anIntArray2885; - Class120[] aClass120Array2886; - int anInt2887 = 0; - short[] aShortArray2888; - byte[] aByteArray2889; - int[][] anIntArrayArray2890; - short[] aShortArray2891; - int[] anIntArray2892; - short[] aShortArray2893; - private short aShort2844; - private short aShort2850; - private boolean aBoolean2853 = false; - private short aShort2854; - private short aShort2873; - private short aShort2874; - private short aShort2880; - - - private Model_Sub1() { - } - - private Model_Sub1(byte[] var1) { - if (var1[var1.length - 1] == -1 && var1[var1.length - 2] == -1) { - this.method2003(var1); - } else { - this.method2017(var1); - } - - } - - Model_Sub1(int var1, int var2) { - this.anIntArray2885 = new int[var1]; - this.anIntArray2881 = new int[var1]; - this.anIntArray2892 = new int[var1]; - this.anIntArray2860 = new int[var1]; - this.anIntArray2865 = new int[var2]; - this.anIntArray2878 = new int[var2]; - this.anIntArray2864 = new int[var2]; - this.aByteArray2859 = new byte[var2]; - this.aByteArray2889 = new byte[var2]; - this.aByteArray2843 = new byte[var2]; - this.aShortArray2870 = new short[var2]; - this.aShortArray2858 = new short[var2]; - this.aByteArray2866 = new byte[var2]; - this.anIntArray2847 = new int[var2]; - - } - - Model_Sub1(Model_Sub1[] var1, int var2) { - boolean var3 = false; - boolean var4 = false; - boolean var5 = false; - boolean var6 = false; - boolean var7 = false; - boolean var8 = false; - this.anInt2887 = 0; - this.anInt2849 = 0; - this.anInt2862 = 0; - this.aByte2848 = -1; - - int var11; - for (var11 = 0; var11 < var2; ++var11) { - Model_Sub1 var12 = var1[var11]; - if (var12 != null) { - this.anInt2887 += var12.anInt2887; - this.anInt2849 += var12.anInt2849; - this.anInt2862 += var12.anInt2862; - if (var12.aByteArray2889 == null) { - if (this.aByte2848 == -1) { - this.aByte2848 = var12.aByte2848; - } - - if (this.aByte2848 != var12.aByte2848) { - var4 = true; - } - } else { - var4 = true; - } - - var3 |= var12.aByteArray2859 != null; - var5 |= var12.aByteArray2843 != null; - var6 |= var12.anIntArray2847 != null; - var7 |= var12.aShortArray2858 != null; - var8 |= var12.aByteArray2866 != null; - } - } - - this.anIntArray2885 = new int[this.anInt2887]; - this.anIntArray2881 = new int[this.anInt2887]; - this.anIntArray2892 = new int[this.anInt2887]; - this.anIntArray2860 = new int[this.anInt2887]; - this.aShortArray2893 = new short[this.anInt2887]; - this.anIntArray2865 = new int[this.anInt2849]; - this.anIntArray2878 = new int[this.anInt2849]; - this.anIntArray2864 = new int[this.anInt2849]; - if (var3) { - this.aByteArray2859 = new byte[this.anInt2849]; - } - - if (var4) { - this.aByteArray2889 = new byte[this.anInt2849]; - } - - if (var5) { - this.aByteArray2843 = new byte[this.anInt2849]; - } - - if (var6) { - this.anIntArray2847 = new int[this.anInt2849]; - } - - if (var7) { - this.aShortArray2858 = new short[this.anInt2849]; - } - - if (var8) { - this.aByteArray2866 = new byte[this.anInt2849]; - } - - this.aShortArray2870 = new short[this.anInt2849]; - this.aShortArray2855 = new short[this.anInt2849]; - if (this.anInt2862 > 0) { - this.aByteArray2857 = new byte[this.anInt2862]; - this.aShortArray2884 = new short[this.anInt2862]; - this.aShortArray2846 = new short[this.anInt2862]; - this.aShortArray2891 = new short[this.anInt2862]; - this.aShortArray2888 = new short[this.anInt2862]; - this.aShortArray2882 = new short[this.anInt2862]; - this.aShortArray2851 = new short[this.anInt2862]; - this.aByteArray2845 = new byte[this.anInt2862]; - this.aByteArray2867 = new byte[this.anInt2862]; - this.aByteArray2877 = new byte[this.anInt2862]; - this.aByteArray2852 = new byte[this.anInt2862]; - this.aByteArray2869 = new byte[this.anInt2862]; - } - - this.anInt2887 = 0; - this.anInt2849 = 0; - this.anInt2862 = 0; - - for (var11 = 0; var11 < var2; ++var11) { - short var18 = (short) (1 << var11); - Model_Sub1 var13 = var1[var11]; - if (var13 != null) { - int var14; - for (var14 = 0; var14 < var13.anInt2849; ++var14) { - if (var3 && var13.aByteArray2859 != null) { - this.aByteArray2859[this.anInt2849] = var13.aByteArray2859[var14]; - } - - if (var4) { - if (var13.aByteArray2889 == null) { - this.aByteArray2889[this.anInt2849] = var13.aByte2848; - } else { - this.aByteArray2889[this.anInt2849] = var13.aByteArray2889[var14]; - } - } - - if (var5 && var13.aByteArray2843 != null) { - this.aByteArray2843[this.anInt2849] = var13.aByteArray2843[var14]; - } - - if (var6 && var13.anIntArray2847 != null) { - this.anIntArray2847[this.anInt2849] = var13.anIntArray2847[var14]; - } - - if (var7) { - if (var13.aShortArray2858 == null) { - this.aShortArray2858[this.anInt2849] = -1; - } else { - this.aShortArray2858[this.anInt2849] = var13.aShortArray2858[var14]; - } - } - - if (var8) { - if (var13.aByteArray2866 == null || var13.aByteArray2866[var14] == -1) { - this.aByteArray2866[this.anInt2849] = -1; - } else { - this.aByteArray2866[this.anInt2849] = (byte) (var13.aByteArray2866[var14] + this.anInt2862); - } - } - - this.aShortArray2870[this.anInt2849] = var13.aShortArray2870[var14]; - this.aShortArray2855[this.anInt2849] = var18; - this.anIntArray2865[this.anInt2849] = this.method1995(var13, var13.anIntArray2865[var14], var18); - this.anIntArray2878[this.anInt2849] = this.method1995(var13, var13.anIntArray2878[var14], var18); - this.anIntArray2864[this.anInt2849] = this.method1995(var13, var13.anIntArray2864[var14], var18); - ++this.anInt2849; - } - - for (var14 = 0; var14 < var13.anInt2862; ++var14) { - byte var15 = this.aByteArray2857[this.anInt2862] = var13.aByteArray2857[var14]; - if (var15 == 0) { - this.aShortArray2884[this.anInt2862] = (short) this.method1995(var13, var13.aShortArray2884[var14], var18); - this.aShortArray2846[this.anInt2862] = (short) this.method1995(var13, var13.aShortArray2846[var14], var18); - this.aShortArray2891[this.anInt2862] = (short) this.method1995(var13, var13.aShortArray2891[var14], var18); - } - - if (var15 >= 1 && var15 <= 3) { - this.aShortArray2884[this.anInt2862] = var13.aShortArray2884[var14]; - this.aShortArray2846[this.anInt2862] = var13.aShortArray2846[var14]; - this.aShortArray2891[this.anInt2862] = var13.aShortArray2891[var14]; - this.aShortArray2888[this.anInt2862] = var13.aShortArray2888[var14]; - this.aShortArray2882[this.anInt2862] = var13.aShortArray2882[var14]; - this.aShortArray2851[this.anInt2862] = var13.aShortArray2851[var14]; - this.aByteArray2845[this.anInt2862] = var13.aByteArray2845[var14]; - this.aByteArray2867[this.anInt2862] = var13.aByteArray2867[var14]; - this.aByteArray2877[this.anInt2862] = var13.aByteArray2877[var14]; - } - - if (var15 == 2) { - this.aByteArray2852[this.anInt2862] = var13.aByteArray2852[var14]; - this.aByteArray2869[this.anInt2862] = var13.aByteArray2869[var14]; - } - - ++this.anInt2862; - } - } - } - - } - - Model_Sub1(Model_Sub1 var1, boolean var2, boolean var3, boolean var4) { - this.anInt2887 = var1.anInt2887; - this.anInt2849 = var1.anInt2849; - this.anInt2862 = var1.anInt2862; - int var6; - if (var2) { - this.anIntArray2885 = var1.anIntArray2885; - this.anIntArray2881 = var1.anIntArray2881; - this.anIntArray2892 = var1.anIntArray2892; - } else { - this.anIntArray2885 = new int[this.anInt2887]; - this.anIntArray2881 = new int[this.anInt2887]; - this.anIntArray2892 = new int[this.anInt2887]; - - for (var6 = 0; var6 < this.anInt2887; ++var6) { - this.anIntArray2885[var6] = var1.anIntArray2885[var6]; - this.anIntArray2881[var6] = var1.anIntArray2881[var6]; - this.anIntArray2892[var6] = var1.anIntArray2892[var6]; - } - } - - if (var3) { - this.aShortArray2870 = var1.aShortArray2870; - } else { - this.aShortArray2870 = new short[this.anInt2849]; - - for (var6 = 0; var6 < this.anInt2849; ++var6) { - this.aShortArray2870[var6] = var1.aShortArray2870[var6]; - } - } - - if (var4 || var1.aShortArray2858 == null) { - this.aShortArray2858 = var1.aShortArray2858; - } else { - this.aShortArray2858 = new short[this.anInt2849]; - - for (var6 = 0; var6 < this.anInt2849; ++var6) { - this.aShortArray2858[var6] = var1.aShortArray2858[var6]; - } - } - - this.aByteArray2843 = var1.aByteArray2843; - - this.anIntArray2865 = var1.anIntArray2865; - this.anIntArray2878 = var1.anIntArray2878; - this.anIntArray2864 = var1.anIntArray2864; - this.aByteArray2859 = var1.aByteArray2859; - this.aByteArray2889 = var1.aByteArray2889; - this.aByteArray2866 = var1.aByteArray2866; - this.aByte2848 = var1.aByte2848; - this.aByteArray2857 = var1.aByteArray2857; - this.aShortArray2884 = var1.aShortArray2884; - this.aShortArray2846 = var1.aShortArray2846; - this.aShortArray2891 = var1.aShortArray2891; - this.aShortArray2888 = var1.aShortArray2888; - this.aShortArray2882 = var1.aShortArray2882; - this.aShortArray2851 = var1.aShortArray2851; - this.aByteArray2845 = var1.aByteArray2845; - this.aByteArray2867 = var1.aByteArray2867; - this.aByteArray2877 = var1.aByteArray2877; - this.aByteArray2852 = var1.aByteArray2852; - this.aByteArray2869 = var1.aByteArray2869; - this.anIntArray2860 = var1.anIntArray2860; - this.anIntArray2847 = var1.anIntArray2847; - this.anIntArrayArray2890 = var1.anIntArrayArray2890; - this.anIntArrayArray2856 = var1.anIntArrayArray2856; - this.aClass50Array2872 = var1.aClass50Array2872; - this.aClass120Array2886 = var1.aClass120Array2886; - this.aClass50Array2883 = var1.aClass50Array2883; - this.aShort2879 = var1.aShort2879; - this.aShort2876 = var1.aShort2876; - } - - private static int method2009(int[][] var0, int var1, int var2) { - int var3 = var1 >> 7; - int var4 = var2 >> 7; - if (var3 >= 0 && var4 >= 0 && var3 < var0.length && var4 < var0[0].length) { - int var5 = var1 & 127; - int var6 = var2 & 127; - int var7 = var0[var3][var4] * (128 - var5) + var0[var3 + 1][var4] * var5 >> 7; - int var8 = var0[var3][var4 + 1] * (128 - var5) + var0[var3 + 1][var4 + 1] * var5 >> 7; - return var7 * (128 - var6) + var8 * var6 >> 7; - } else { - return 0; - } - } - - static Model_Sub1 method2015(CacheIndex var0, int var1) { - byte[] var3 = var0.getFile(var1, 0); - return var3 == null ? null : new Model_Sub1(var3); - } - - final void method1989() { - for (int var1 = 0; var1 < this.anInt2887; ++var1) { - this.anIntArray2885[var1] = -this.anIntArray2885[var1]; - this.anIntArray2892[var1] = -this.anIntArray2892[var1]; - } - - this.method2007(); - } - - final void method1991() { - for (int var1 = 0; var1 < this.anInt2887; ++var1) { - int var2 = this.anIntArray2885[var1]; - this.anIntArray2885[var1] = this.anIntArray2892[var1]; - this.anIntArray2892[var1] = -var2; - } - - this.method2007(); - } - - private void method1992(int var1) { - int var2 = anIntArray2871[var1]; - int var3 = anIntArray2863[var1]; - - for (int var4 = 0; var4 < this.anInt2887; ++var4) { - int var5 = this.anIntArray2881[var4] * var2 + this.anIntArray2885[var4] * var3 >> 16; - this.anIntArray2881[var4] = this.anIntArray2881[var4] * var3 - this.anIntArray2885[var4] * var2 >> 16; - this.anIntArray2885[var4] = var5; - } - - this.method2007(); - } - - private void method1993() { - if (!this.aBoolean2853) { - this.aBoolean2853 = true; - int var1 = 32767; - int var2 = 32767; - int var3 = 32767; - int var4 = -32768; - int var5 = -32768; - int var6 = -32768; - - for (int var7 = 0; var7 < this.anInt2887; ++var7) { - int var8 = this.anIntArray2885[var7]; - int var9 = this.anIntArray2881[var7]; - int var10 = this.anIntArray2892[var7]; - if (var8 < var1) { - var1 = var8; - } - - if (var8 > var4) { - var4 = var8; - } - - if (var9 < var2) { - var2 = var9; - } - - if (var9 > var5) { - var5 = var9; - } - - if (var10 < var3) { - var3 = var10; - } - - if (var10 > var6) { - var6 = var10; - } - } - - this.aShort2850 = (short) var1; - this.aShort2873 = (short) var4; - this.aShort2854 = (short) var2; - this.aShort2844 = (short) var5; - this.aShort2880 = (short) var3; - this.aShort2874 = (short) var6; - } - } - - final void method1994(int var1, int var2, int var3) { - for (int var4 = 0; var4 < this.anInt2887; ++var4) { - this.anIntArray2885[var4] = this.anIntArray2885[var4] * var1 / 128; - this.anIntArray2881[var4] = this.anIntArray2881[var4] * var2 / 128; - this.anIntArray2892[var4] = this.anIntArray2892[var4] * var3 / 128; - } - - this.method2007(); - } - - final GameObject method1861() { - return this.method2008(this.aShort2879, this.aShort2876, -50, -10, -50); - } - - private int method1995(Model_Sub1 var1, int var2, short var3) { - int var4 = var1.anIntArray2885[var2]; - int var5 = var1.anIntArray2881[var2]; - int var6 = var1.anIntArray2892[var2]; - - for (int var7 = 0; var7 < this.anInt2887; ++var7) { - if (var4 == this.anIntArray2885[var7] && var5 == this.anIntArray2881[var7] && var6 == this.anIntArray2892[var7]) { - this.aShortArray2893[var7] |= var3; - return var7; - } - } - - this.anIntArray2885[this.anInt2887] = var4; - this.anIntArray2881[this.anInt2887] = var5; - this.anIntArray2892[this.anInt2887] = var6; - this.aShortArray2893[this.anInt2887] = var3; - if (var1.anIntArray2860 != null) { - this.anIntArray2860[this.anInt2887] = var1.anIntArray2860[var2]; - } - - return this.anInt2887++; - } - - private void method1996(int[][] var1, int var2, int var3, int var4, int var5, int var6) { - int var10 = -var5 / 2; - int var11 = -var6 / 2; - int var12 = method2009(var1, var2 + var10, var4 + var11); - int var13 = var5 / 2; - int var14 = -var6 / 2; - int var15 = method2009(var1, var2 + var13, var4 + var14); - int var16 = -var5 / 2; - int var17 = var6 / 2; - int var18 = method2009(var1, var2 + var16, var4 + var17); - int var19 = var5 / 2; - int var20 = var6 / 2; - int var21 = method2009(var1, var2 + var19, var4 + var20); - int var22 = var12 < var15 ? var12 : var15; - int var23 = var18 < var21 ? var18 : var21; - int var24 = var15 < var21 ? var15 : var21; - int var25 = var12 < var18 ? var12 : var18; - if (var6 != 0) { - int var26 = (int) (Math.atan2(var22 - var23, var6) * 325.95D) & 0x7FF; - if (var26 != 0) { - this.method2006(var26); - } - } - - if (var5 != 0) { - int var27 = (int) (Math.atan2(var25 - var24, var5) * 325.95D) & 0x7FF; - if (var27 != 0) { - this.method1992(var27); - } - } - - int var28 = var12 + var21; - if (var15 + var18 < var28) { - var28 = var15 + var18; - } - - var28 = (var28 >> 1) - var3; - if (var28 != 0) { - this.method2001(0, var28, 0); - } - - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - } - - final void method1997() { - if (this.aClass50Array2872 == null) { - this.aClass50Array2872 = new Class50[this.anInt2887]; - - int var1; - for (var1 = 0; var1 < this.anInt2887; ++var1) { - this.aClass50Array2872[var1] = new Class50(); - } - - for (var1 = 0; var1 < this.anInt2849; ++var1) { - int var2 = this.anIntArray2865[var1]; - int var3 = this.anIntArray2878[var1]; - int var4 = this.anIntArray2864[var1]; - int var5 = this.anIntArray2885[var3] - this.anIntArray2885[var2]; - int var6 = this.anIntArray2881[var3] - this.anIntArray2881[var2]; - int var7 = this.anIntArray2892[var3] - this.anIntArray2892[var2]; - int var8 = this.anIntArray2885[var4] - this.anIntArray2885[var2]; - int var9 = this.anIntArray2881[var4] - this.anIntArray2881[var2]; - int var10 = this.anIntArray2892[var4] - this.anIntArray2892[var2]; - int var11 = var6 * var10 - var9 * var7; - int var12 = var7 * var8 - var10 * var5; - - int var13; - for (var13 = var5 * var9 - var8 * var6; var11 > 8192 || var12 > 8192 || var13 > 8192 || var11 < -8192 || var12 < -8192 || var13 < -8192; var13 >>= 1) { - var11 >>= 1; - var12 >>= 1; - } - - int var14 = (int) Math.sqrt(var11 * var11 + var12 * var12 + var13 * var13); - if (var14 <= 0) { - var14 = 1; - } - - var11 = var11 * 256 / var14; - var12 = var12 * 256 / var14; - var13 = var13 * 256 / var14; - byte var15; - if (this.aByteArray2859 == null) { - var15 = 0; - } else { - var15 = this.aByteArray2859[var1]; - } - - if (var15 == 0) { - Class50 var16 = this.aClass50Array2872[var2]; - var16.anInt831 += var11; - var16.anInt821 += var12; - var16.anInt830 += var13; - ++var16.anInt823; - var16 = this.aClass50Array2872[var3]; - var16.anInt831 += var11; - var16.anInt821 += var12; - var16.anInt830 += var13; - ++var16.anInt823; - var16 = this.aClass50Array2872[var4]; - var16.anInt831 += var11; - var16.anInt821 += var12; - var16.anInt830 += var13; - ++var16.anInt823; - } else if (var15 == 1) { - if (this.aClass120Array2886 == null) { - this.aClass120Array2886 = new Class120[this.anInt2849]; - } - - Class120 var17 = this.aClass120Array2886[var1] = new Class120(); - var17.anInt1634 = var11; - var17.anInt1635 = var12; - var17.anInt1632 = var13; - } - } - - } - } - - final void method1998(short var1, short var2) { - if (this.aShortArray2858 != null) { - for (int var3 = 0; var3 < this.anInt2849; ++var3) { - if (this.aShortArray2858[var3] == var1) { - this.aShortArray2858[var3] = var2; - } - } - - } - } - - final Model_Sub1 method1999(int var1, int var2, int[][] var3, int[][] var4, int var5, int var6, int var7) { - this.method1993(); - int var10 = var5 + this.aShort2850; - int var11 = var5 + this.aShort2873; - int var12 = var7 + this.aShort2880; - int var13 = var7 + this.aShort2874; - if ((var1 == 1 || var1 == 2 || var1 == 3 || var1 == 5) && (var10 < 0 || var11 + 128 >> 7 >= var3.length || var12 < 0 || var13 + 128 >> 7 >= var3[0].length)) { - return this; - } else { - if (var1 == 4 || var1 == 5) { - if (var4 == null) { - return this; - } - - if (var10 < 0 || var11 + 128 >> 7 >= var4.length || var12 < 0 || var13 + 128 >> 7 >= var4[0].length) { - return this; - } - } else { - var10 >>= 7; - var11 = var11 + 127 >> 7; - var12 >>= 7; - var13 = var13 + 127 >> 7; - if (var3[var10][var12] == var6 && var3[var11][var12] == var6 && var3[var10][var13] == var6 && var3[var11][var13] == var6) { - return this; - } - } - - Model_Sub1 var14; - var14 = new Model_Sub1(); - var14.anInt2887 = this.anInt2887; - var14.anInt2849 = this.anInt2849; - var14.anInt2862 = this.anInt2862; - var14.anIntArray2865 = this.anIntArray2865; - var14.anIntArray2878 = this.anIntArray2878; - var14.anIntArray2864 = this.anIntArray2864; - var14.aByteArray2859 = this.aByteArray2859; - var14.aByteArray2889 = this.aByteArray2889; - var14.aByteArray2843 = this.aByteArray2843; - var14.aByteArray2866 = this.aByteArray2866; - var14.aShortArray2870 = this.aShortArray2870; - var14.aShortArray2858 = this.aShortArray2858; - var14.aByte2848 = this.aByte2848; - var14.aByteArray2857 = this.aByteArray2857; - var14.aShortArray2884 = this.aShortArray2884; - var14.aShortArray2846 = this.aShortArray2846; - var14.aShortArray2891 = this.aShortArray2891; - var14.aShortArray2888 = this.aShortArray2888; - var14.aShortArray2882 = this.aShortArray2882; - var14.aShortArray2851 = this.aShortArray2851; - var14.aByteArray2845 = this.aByteArray2845; - var14.aByteArray2867 = this.aByteArray2867; - var14.aByteArray2877 = this.aByteArray2877; - var14.aByteArray2852 = this.aByteArray2852; - var14.aByteArray2869 = this.aByteArray2869; - var14.anIntArray2860 = this.anIntArray2860; - var14.anIntArray2847 = this.anIntArray2847; - var14.anIntArrayArray2890 = this.anIntArrayArray2890; - var14.anIntArrayArray2856 = this.anIntArrayArray2856; - var14.aShort2879 = this.aShort2879; - var14.aShort2876 = this.aShort2876; - var14.aClass50Array2872 = this.aClass50Array2872; - var14.aClass120Array2886 = this.aClass120Array2886; - var14.aClass50Array2883 = this.aClass50Array2883; - if (var1 == 3) { - var14.anIntArray2885 = Class65.method1233(this.anIntArray2885); - var14.anIntArray2881 = Class65.method1233(this.anIntArray2881); - var14.anIntArray2892 = Class65.method1233(this.anIntArray2892); - } else { - var14.anIntArray2885 = this.anIntArray2885; - var14.anIntArray2881 = new int[var14.anInt2887]; - var14.anIntArray2892 = this.anIntArray2892; - } - - int var15; - int var17; - int var16; - int var19; - int var18; - int var21; - int var20; - int var23; - int var22; - int var24; - if (var1 == 1) { - for (var15 = 0; var15 < var14.anInt2887; ++var15) { - var16 = this.anIntArray2885[var15] + var5; - var17 = this.anIntArray2892[var15] + var7; - var18 = var16 & 127; - var19 = var17 & 127; - var20 = var16 >> 7; - var21 = var17 >> 7; - var22 = var3[var20][var21] * (128 - var18) + var3[var20 + 1][var21] * var18 >> 7; - var23 = var3[var20][var21 + 1] * (128 - var18) + var3[var20 + 1][var21 + 1] * var18 >> 7; - var24 = var22 * (128 - var19) + var23 * var19 >> 7; - var14.anIntArray2881[var15] = this.anIntArray2881[var15] + var24 - var6; - } - } else { - int var25; - if (var1 == 2) { - for (var15 = 0; var15 < var14.anInt2887; ++var15) { - var16 = (this.anIntArray2881[var15] << 16) / this.aShort2854; - if (var16 < var2) { - var17 = this.anIntArray2885[var15] + var5; - var18 = this.anIntArray2892[var15] + var7; - var19 = var17 & 127; - var20 = var18 & 127; - var21 = var17 >> 7; - var22 = var18 >> 7; - var23 = var3[var21][var22] * (128 - var19) + var3[var21 + 1][var22] * var19 >> 7; - var24 = var3[var21][var22 + 1] * (128 - var19) + var3[var21 + 1][var22 + 1] * var19 >> 7; - var25 = var23 * (128 - var20) + var24 * var20 >> 7; - var14.anIntArray2881[var15] = this.anIntArray2881[var15] + (var25 - var6) * (var2 - var16) / var2; - } else { - var14.anIntArray2881[var15] = this.anIntArray2881[var15]; - } - } - } else if (var1 == 3) { - var15 = (var2 & 0xFF) * 4; - var16 = (var2 >> 8 & 0xFF) * 4; - this.method1996(var3, var5, var6, var7, var15, var16); - } else if (var1 == 4) { - var15 = this.aShort2844 - this.aShort2854; - - for (var16 = 0; var16 < this.anInt2887; ++var16) { - var17 = this.anIntArray2885[var16] + var5; - var18 = this.anIntArray2892[var16] + var7; - var19 = var17 & 127; - var20 = var18 & 127; - var21 = var17 >> 7; - var22 = var18 >> 7; - var23 = var4[var21][var22] * (128 - var19) + var4[var21 + 1][var22] * var19 >> 7; - var24 = var4[var21][var22 + 1] * (128 - var19) + var4[var21 + 1][var22 + 1] * var19 >> 7; - var25 = var23 * (128 - var20) + var24 * var20 >> 7; - var14.anIntArray2881[var16] = this.anIntArray2881[var16] + (var25 - var6) + var15; - } - } else if (var1 == 5) { - var15 = this.aShort2844 - this.aShort2854; - - for (var16 = 0; var16 < this.anInt2887; ++var16) { - var17 = this.anIntArray2885[var16] + var5; - var18 = this.anIntArray2892[var16] + var7; - var19 = var17 & 127; - var20 = var18 & 127; - var21 = var17 >> 7; - var22 = var18 >> 7; - var23 = var3[var21][var22] * (128 - var19) + var3[var21 + 1][var22] * var19 >> 7; - var24 = var3[var21][var22 + 1] * (128 - var19) + var3[var21 + 1][var22 + 1] * var19 >> 7; - var25 = var23 * (128 - var20) + var24 * var20 >> 7; - var23 = var4[var21][var22] * (128 - var19) + var4[var21 + 1][var22] * var19 >> 7; - var24 = var4[var21][var22 + 1] * (128 - var19) + var4[var21 + 1][var22 + 1] * var19 >> 7; - int var26 = var23 * (128 - var20) + var24 * var20 >> 7; - int var27 = var25 - var26; - var14.anIntArray2881[var16] = ((this.anIntArray2881[var16] << 8) / var15 * var27 >> 8) - (var6 - var25); - } - } - } - - this.aBoolean2853 = false; - - return var14; - } - } - - final Class140_Sub1_Sub2 method2000(int var1, int var2) { - return new Class140_Sub1_Sub2(this, var1, var2, -50, -10, -50); - } - - final void method1867(int var1, int var2, int var3, int var4, int var5) { - } - - final void method2001(int var1, int var2, int var3) { - for (int var4 = 0; var4 < this.anInt2887; ++var4) { - this.anIntArray2885[var4] += var1; - this.anIntArray2881[var4] += var2; - this.anIntArray2892[var4] += var3; - } - - this.method2007(); - } - - final void method2002() { - int var1; - for (var1 = 0; var1 < this.anInt2887; ++var1) { - this.anIntArray2892[var1] = -this.anIntArray2892[var1]; - } - - for (var1 = 0; var1 < this.anInt2849; ++var1) { - int var2 = this.anIntArray2865[var1]; - this.anIntArray2865[var1] = this.anIntArray2864[var1]; - this.anIntArray2864[var1] = var2; - } - - this.method2007(); - } - - final boolean method1865() { - return true; - } - - private void method2003(byte[] var1) { - DataBuffer var2 = new DataBuffer(var1); - DataBuffer var3 = new DataBuffer(var1); - DataBuffer var4 = new DataBuffer(var1); - DataBuffer var5 = new DataBuffer(var1); - DataBuffer var6 = new DataBuffer(var1); - DataBuffer var7 = new DataBuffer(var1); - DataBuffer var8 = new DataBuffer(var1); - var2.index = var1.length - 23; - int var9 = var2.readUnsignedShort(); - int var10 = var2.readUnsignedShort(); - int var11 = var2.readUnsignedByte(); - int var12 = var2.readUnsignedByte(); - boolean var13 = (var12 & 1) == 1; - boolean var14 = (var12 & 2) == 2; - int var15 = var2.readUnsignedByte(); - int var16 = var2.readUnsignedByte(); - int var17 = var2.readUnsignedByte(); - int var18 = var2.readUnsignedByte(); - int var19 = var2.readUnsignedByte(); - int var20 = var2.readUnsignedShort(); - int var21 = var2.readUnsignedShort(); - int var22 = var2.readUnsignedShort(); - int var23 = var2.readUnsignedShort(); - int var24 = var2.readUnsignedShort(); - int var25 = 0; - int var26 = 0; - int var27 = 0; - int var28; - if (var11 > 0) { - this.aByteArray2857 = new byte[var11]; - var2.index = 0; - - for (var28 = 0; var28 < var11; ++var28) { - byte var29 = this.aByteArray2857[var28] = var2.readSignedByte(); - if (var29 == 0) { - ++var25; - } - - if (var29 >= 1 && var29 <= 3) { - ++var26; - } - - if (var29 == 2) { - ++var27; - } - } - } - - var28 = var11 + var9; - int var30 = var28; - if (var13) { - var28 += var10; - } - - int var31 = var28; - var28 += var10; - int var32 = var28; - if (var15 == 255) { - var28 += var10; - } - - int var33 = var28; - if (var17 == 1) { - var28 += var10; - } - - int var34 = var28; - if (var19 == 1) { - var28 += var9; - } - - int var35 = var28; - if (var16 == 1) { - var28 += var10; - } - - int var36 = var28; - var28 += var23; - int var37 = var28; - if (var18 == 1) { - var28 += var10 * 2; - } - - int var38 = var28; - var28 += var24; - int var39 = var28; - var28 += var10 * 2; - int var40 = var28; - var28 += var20; - int var41 = var28; - var28 += var21; - int var42 = var28; - var28 += var22; - int var43 = var28; - var28 += var25 * 6; - int var44 = var28; - var28 += var26 * 6; - int var45 = var28; - var28 += var26 * 6; - int var46 = var28; - var28 += var26; - int var47 = var28; - var28 += var26; - int var48 = var28; - var28 += var26 + var27 * 2; - this.anInt2887 = var9; - this.anInt2849 = var10; - this.anInt2862 = var11; - this.anIntArray2885 = new int[var9]; - this.anIntArray2881 = new int[var9]; - this.anIntArray2892 = new int[var9]; - this.anIntArray2865 = new int[var10]; - this.anIntArray2878 = new int[var10]; - this.anIntArray2864 = new int[var10]; - if (var19 == 1) { - this.anIntArray2860 = new int[var9]; - } - - if (var13) { - this.aByteArray2859 = new byte[var10]; - } - - if (var15 == 255) { - this.aByteArray2889 = new byte[var10]; - } else { - this.aByte2848 = (byte) var15; - } - - if (var16 == 1) { - this.aByteArray2843 = new byte[var10]; - } - - if (var17 == 1) { - this.anIntArray2847 = new int[var10]; - } - - if (var18 == 1) { - this.aShortArray2858 = new short[var10]; - } - - if (var18 == 1 && var11 > 0) { - this.aByteArray2866 = new byte[var10]; - } - - this.aShortArray2870 = new short[var10]; - if (var11 > 0) { - this.aShortArray2884 = new short[var11]; - this.aShortArray2846 = new short[var11]; - this.aShortArray2891 = new short[var11]; - if (var26 > 0) { - this.aShortArray2888 = new short[var26]; - this.aShortArray2882 = new short[var26]; - this.aShortArray2851 = new short[var26]; - this.aByteArray2845 = new byte[var26]; - this.aByteArray2867 = new byte[var26]; - this.aByteArray2877 = new byte[var26]; - } - - if (var27 > 0) { - this.aByteArray2852 = new byte[var27]; - this.aByteArray2869 = new byte[var27]; - } - } - - var2.index = var11; - var3.index = var40; - var4.index = var41; - var5.index = var42; - var6.index = var34; - int var50 = 0; - int var51 = 0; - int var52 = 0; - - int var55; - int var54; - int var53; - int var57; - int var56; - for (var53 = 0; var53 < var9; ++var53) { - var54 = var2.readUnsignedByte(); - var55 = 0; - if ((var54 & 1) != 0) { - var55 = var3.getByteOrShort(); - } - - var56 = 0; - if ((var54 & 2) != 0) { - var56 = var4.getByteOrShort(); - } - - var57 = 0; - if ((var54 & 4) != 0) { - var57 = var5.getByteOrShort(); - } - - this.anIntArray2885[var53] = var50 + var55; - this.anIntArray2881[var53] = var51 + var56; - this.anIntArray2892[var53] = var52 + var57; - var50 = this.anIntArray2885[var53]; - var51 = this.anIntArray2881[var53]; - var52 = this.anIntArray2892[var53]; - if (var19 == 1) { - this.anIntArray2860[var53] = var6.readUnsignedByte(); - } - } - - var2.index = var39; - var3.index = var30; - var4.index = var32; - var5.index = var35; - var6.index = var33; - var7.index = var37; - var8.index = var38; - - for (var53 = 0; var53 < var10; ++var53) { - this.aShortArray2870[var53] = (short) var2.readUnsignedShort(); - if (var13) { - this.aByteArray2859[var53] = var3.readSignedByte(); - } - - if (var15 == 255) { - this.aByteArray2889[var53] = var4.readSignedByte(); - } - - if (var16 == 1) { - this.aByteArray2843[var53] = var5.readSignedByte(); - } - - if (var17 == 1) { - this.anIntArray2847[var53] = var6.readUnsignedByte(); - } - - if (var18 == 1) { - this.aShortArray2858[var53] = (short) (var7.readUnsignedShort() - 1); - } - - if (this.aByteArray2866 != null) { - if (this.aShortArray2858[var53] == -1) { - this.aByteArray2866[var53] = -1; - } else { - this.aByteArray2866[var53] = (byte) (var8.readUnsignedByte() - 1); - } - } - } - - var2.index = var36; - var3.index = var31; - var53 = 0; - var54 = 0; - var55 = 0; - var56 = 0; - - int var58; - for (var57 = 0; var57 < var10; ++var57) { - var58 = var3.readUnsignedByte(); - if (var58 == 1) { - var53 = var2.getByteOrShort() + var56; - var54 = var2.getByteOrShort() + var53; - var55 = var2.getByteOrShort() + var54; - var56 = var55; - this.anIntArray2865[var57] = var53; - this.anIntArray2878[var57] = var54; - this.anIntArray2864[var57] = var55; - } - - if (var58 == 2) { - var54 = var55; - var55 = var2.getByteOrShort() + var56; - var56 = var55; - this.anIntArray2865[var57] = var53; - this.anIntArray2878[var57] = var54; - this.anIntArray2864[var57] = var55; - } - - if (var58 == 3) { - var53 = var55; - var55 = var2.getByteOrShort() + var56; - var56 = var55; - this.anIntArray2865[var57] = var53; - this.anIntArray2878[var57] = var54; - this.anIntArray2864[var57] = var55; - } - - if (var58 == 4) { - int var59 = var53; - var53 = var54; - var54 = var59; - var55 = var2.getByteOrShort() + var56; - var56 = var55; - this.anIntArray2865[var57] = var53; - this.anIntArray2878[var57] = var59; - this.anIntArray2864[var57] = var55; - } - } - - var2.index = var43; - var3.index = var44; - var4.index = var45; - var5.index = var46; - var6.index = var47; - var7.index = var48; - - for (var57 = 0; var57 < var11; ++var57) { - var58 = this.aByteArray2857[var57] & 0xFF; - if (var58 == 0) { - this.aShortArray2884[var57] = (short) var2.readUnsignedShort(); - this.aShortArray2846[var57] = (short) var2.readUnsignedShort(); - this.aShortArray2891[var57] = (short) var2.readUnsignedShort(); - } - - if (var58 == 1) { - this.aShortArray2884[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2846[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2891[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2888[var57] = (short) var4.readUnsignedShort(); - this.aShortArray2882[var57] = (short) var4.readUnsignedShort(); - this.aShortArray2851[var57] = (short) var4.readUnsignedShort(); - this.aByteArray2845[var57] = var5.readSignedByte(); - this.aByteArray2867[var57] = var6.readSignedByte(); - this.aByteArray2877[var57] = var7.readSignedByte(); - } - - if (var58 == 2) { - this.aShortArray2884[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2846[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2891[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2888[var57] = (short) var4.readUnsignedShort(); - this.aShortArray2882[var57] = (short) var4.readUnsignedShort(); - this.aShortArray2851[var57] = (short) var4.readUnsignedShort(); - this.aByteArray2845[var57] = var5.readSignedByte(); - this.aByteArray2867[var57] = var6.readSignedByte(); - this.aByteArray2877[var57] = var7.readSignedByte(); - this.aByteArray2852[var57] = var7.readSignedByte(); - this.aByteArray2869[var57] = var7.readSignedByte(); - } - - if (var58 == 3) { - this.aShortArray2884[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2846[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2891[var57] = (short) var3.readUnsignedShort(); - this.aShortArray2888[var57] = (short) var4.readUnsignedShort(); - this.aShortArray2882[var57] = (short) var4.readUnsignedShort(); - this.aShortArray2851[var57] = (short) var4.readUnsignedShort(); - this.aByteArray2845[var57] = var5.readSignedByte(); - this.aByteArray2867[var57] = var6.readSignedByte(); - this.aByteArray2877[var57] = var7.readSignedByte(); - } - } - - if (var14) { - var2.index = var28; - var57 = var2.readUnsignedByte(); - if (var57 > 0) { - var2.index += 4 * var57; - } - - var58 = var2.readUnsignedByte(); - if (var58 > 0) { - var2.index += 4 * var58; - } - } - - } - - final Model_Sub1 method2004() { - Model_Sub1 var1 = new Model_Sub1(); - if (this.aByteArray2859 != null) { - var1.aByteArray2859 = new byte[this.anInt2849]; - - if (this.anInt2849 >= 0) System.arraycopy(this.aByteArray2859, 0, var1.aByteArray2859, 0, this.anInt2849); - } - - var1.anInt2887 = this.anInt2887; - var1.anInt2849 = this.anInt2849; - var1.anInt2862 = this.anInt2862; - var1.anIntArray2885 = this.anIntArray2885; - var1.anIntArray2881 = this.anIntArray2881; - var1.anIntArray2892 = this.anIntArray2892; - var1.anIntArray2865 = this.anIntArray2865; - var1.anIntArray2878 = this.anIntArray2878; - var1.anIntArray2864 = this.anIntArray2864; - var1.aByteArray2889 = this.aByteArray2889; - var1.aByteArray2843 = this.aByteArray2843; - var1.aByteArray2866 = this.aByteArray2866; - var1.aShortArray2870 = this.aShortArray2870; - var1.aShortArray2858 = this.aShortArray2858; - var1.aByte2848 = this.aByte2848; - var1.aByteArray2857 = this.aByteArray2857; - var1.aShortArray2884 = this.aShortArray2884; - var1.aShortArray2846 = this.aShortArray2846; - var1.aShortArray2891 = this.aShortArray2891; - var1.aShortArray2888 = this.aShortArray2888; - var1.aShortArray2882 = this.aShortArray2882; - var1.aShortArray2851 = this.aShortArray2851; - var1.aByteArray2845 = this.aByteArray2845; - var1.aByteArray2867 = this.aByteArray2867; - var1.aByteArray2877 = this.aByteArray2877; - var1.aByteArray2852 = this.aByteArray2852; - var1.aByteArray2869 = this.aByteArray2869; - var1.anIntArray2860 = this.anIntArray2860; - var1.anIntArray2847 = this.anIntArray2847; - var1.anIntArrayArray2890 = this.anIntArrayArray2890; - var1.anIntArrayArray2856 = this.anIntArrayArray2856; - var1.aClass50Array2872 = this.aClass50Array2872; - var1.aClass120Array2886 = this.aClass120Array2886; - var1.aShort2879 = this.aShort2879; - var1.aShort2876 = this.aShort2876; - return var1; - } - - final void method2005(int var1, int var2, int var3, short var5, byte var6) { - this.anIntArray2865[this.anInt2849] = var1; - this.anIntArray2878[this.anInt2849] = var2; - this.anIntArray2864[this.anInt2849] = var3; - this.aByteArray2859[this.anInt2849] = (byte) 1; - this.aByteArray2866[this.anInt2849] = -1; - this.aShortArray2870[this.anInt2849] = var5; - this.aShortArray2858[this.anInt2849] = -1; - this.aByteArray2843[this.anInt2849] = var6; - this.anInt2849++; - } - - private void method2006(int var1) { - int var2 = anIntArray2871[var1]; - int var3 = anIntArray2863[var1]; - - for (int var4 = 0; var4 < this.anInt2887; ++var4) { - int var5 = this.anIntArray2881[var4] * var3 - this.anIntArray2892[var4] * var2 >> 16; - this.anIntArray2892[var4] = this.anIntArray2881[var4] * var2 + this.anIntArray2892[var4] * var3 >> 16; - this.anIntArray2881[var4] = var5; - } - - this.method2007(); - } - - private void method2007() { - this.aClass50Array2872 = null; - this.aClass50Array2883 = null; - this.aClass120Array2886 = null; - this.aBoolean2853 = false; - } - - final Model method2008(int var1, int var2, int var3, int var4, int var5) { - if (HDToolKit.highDetail) { - Class140_Sub1_Sub1 var6 = new Class140_Sub1_Sub1(this, var1, var2, true); - var6.method1908(); - return var6; - } else { - return new Class140_Sub1_Sub2(this, var1, var2, var3, var4, var5); - } - } - - final int method1871() { - if (!this.aBoolean2853) { - this.method1993(); - } - - return this.aShort2854; - } - - final void method2010() { - this.anIntArray2860 = null; - this.anIntArray2847 = null; - this.anIntArrayArray2890 = null; - this.anIntArrayArray2856 = null; - } - - final void method1866(GameObject var1, int var2, int var3, int var4, boolean var5) { - Model_Sub1 var6 = (Model_Sub1) var1; - var6.method1993(); - var6.method1997(); - ++anInt2868; - int var7 = 0; - int[] var8 = var6.anIntArray2885; - int var9 = var6.anInt2887; - - int var10; - for (var10 = 0; var10 < this.anInt2887; ++var10) { - Class50 var11 = this.aClass50Array2872[var10]; - if (var11.anInt823 != 0) { - int var12 = this.anIntArray2881[var10] - var3; - if (var12 >= var6.aShort2854 && var12 <= var6.aShort2844) { - int var13 = this.anIntArray2885[var10] - var2; - if (var13 >= var6.aShort2850 && var13 <= var6.aShort2873) { - int var14 = this.anIntArray2892[var10] - var4; - if (var14 >= var6.aShort2880 && var14 <= var6.aShort2874) { - for (int var15 = 0; var15 < var9; ++var15) { - Class50 var16 = var6.aClass50Array2872[var15]; - if (var13 == var8[var15] && var14 == var6.anIntArray2892[var15] && var12 == var6.anIntArray2881[var15] && var16.anInt823 != 0) { - if (this.aClass50Array2883 == null) { - this.aClass50Array2883 = new Class50[this.anInt2887]; - } - - if (var6.aClass50Array2883 == null) { - var6.aClass50Array2883 = new Class50[var9]; - } - - Class50 var17 = this.aClass50Array2883[var10]; - if (var17 == null) { - var17 = this.aClass50Array2883[var10] = new Class50(var11); - } - - Class50 var18 = var6.aClass50Array2883[var15]; - if (var18 == null) { - var18 = var6.aClass50Array2883[var15] = new Class50(var16); - } - - var17.anInt831 += var16.anInt831; - var17.anInt821 += var16.anInt821; - var17.anInt830 += var16.anInt830; - var17.anInt823 += var16.anInt823; - var18.anInt831 += var11.anInt831; - var18.anInt821 += var11.anInt821; - var18.anInt830 += var11.anInt830; - var18.anInt823 += var11.anInt823; - ++var7; - anIntArray2861[var10] = anInt2868; - anIntArray2875[var15] = anInt2868; - } - } - } - } - } - } - } - - if (var7 >= 3 && var5) { - for (var10 = 0; var10 < this.anInt2849; ++var10) { - if (anIntArray2861[this.anIntArray2865[var10]] == anInt2868 && anIntArray2861[this.anIntArray2878[var10]] == anInt2868 && anIntArray2861[this.anIntArray2864[var10]] == anInt2868) { - if (this.aByteArray2859 == null) { - this.aByteArray2859 = new byte[this.anInt2849]; - } - - this.aByteArray2859[var10] = 2; - } - } - - for (var10 = 0; var10 < var6.anInt2849; ++var10) { - if (anIntArray2875[var6.anIntArray2865[var10]] == anInt2868 && anIntArray2875[var6.anIntArray2878[var10]] == anInt2868 && anIntArray2875[var6.anIntArray2864[var10]] == anInt2868) { - if (var6.aByteArray2859 == null) { - var6.aByteArray2859 = new byte[var6.anInt2849]; - } - - var6.aByteArray2859[var10] = 2; - } - } - - } - } - - final void method2011() { - int var2 = anIntArray2871[256]; - int var3 = anIntArray2863[256]; - - for (int var4 = 0; var4 < this.anInt2887; ++var4) { - int var5 = this.anIntArray2892[var4] * var2 + this.anIntArray2885[var4] * var3 >> 16; - this.anIntArray2892[var4] = this.anIntArray2892[var4] * var3 - this.anIntArray2885[var4] * var2 >> 16; - this.anIntArray2885[var4] = var5; - } - - this.method2007(); - } - - final void method2012() { - int[] var1; - int var2; - int var3; - int var4; - if (this.anIntArray2860 != null) { - var1 = new int[256]; - var2 = 0; - - for (var3 = 0; var3 < this.anInt2887; ++var3) { - var4 = this.anIntArray2860[var3]; - ++var1[var4]; - if (var4 > var2) { - var2 = var4; - } - } - - this.anIntArrayArray2890 = new int[var2 + 1][]; - - for (var3 = 0; var3 <= var2; ++var3) { - this.anIntArrayArray2890[var3] = new int[var1[var3]]; - var1[var3] = 0; - } - - for (var3 = 0; var3 < this.anInt2887; this.anIntArrayArray2890[var4][var1[var4]++] = var3++) { - var4 = this.anIntArray2860[var3]; - } - - this.anIntArray2860 = null; - } - - if (this.anIntArray2847 != null) { - var1 = new int[256]; - var2 = 0; - - for (var3 = 0; var3 < this.anInt2849; ++var3) { - var4 = this.anIntArray2847[var3]; - ++var1[var4]; - if (var4 > var2) { - var2 = var4; - } - } - - this.anIntArrayArray2856 = new int[var2 + 1][]; - - for (var3 = 0; var3 <= var2; ++var3) { - this.anIntArrayArray2856[var3] = new int[var1[var3]]; - var1[var3] = 0; - } - - for (var3 = 0; var3 < this.anInt2849; this.anIntArrayArray2856[var4][var1[var4]++] = var3++) { - var4 = this.anIntArray2847[var3]; - } - - this.anIntArray2847 = null; - } - - } - - final void method2013(int var1, int var2, int var3) { - int var4; - int var5; - int var6; - int var7; - if (var3 != 0) { - var4 = anIntArray2871[var3]; - var5 = anIntArray2863[var3]; - - for (var6 = 0; var6 < this.anInt2887; ++var6) { - var7 = this.anIntArray2881[var6] * var4 + this.anIntArray2885[var6] * var5 >> 16; - this.anIntArray2881[var6] = this.anIntArray2881[var6] * var5 - this.anIntArray2885[var6] * var4 >> 16; - this.anIntArray2885[var6] = var7; - } - } - - if (var1 != 0) { - var4 = anIntArray2871[var1]; - var5 = anIntArray2863[var1]; - - for (var6 = 0; var6 < this.anInt2887; ++var6) { - var7 = this.anIntArray2881[var6] * var5 - this.anIntArray2892[var6] * var4 >> 16; - this.anIntArray2892[var6] = this.anIntArray2881[var6] * var4 + this.anIntArray2892[var6] * var5 >> 16; - this.anIntArray2881[var6] = var7; - } - } - - if (var2 != 0) { - var4 = anIntArray2871[var2]; - var5 = anIntArray2863[var2]; - - for (var6 = 0; var6 < this.anInt2887; ++var6) { - var7 = this.anIntArray2892[var6] * var4 + this.anIntArray2885[var6] * var5 >> 16; - this.anIntArray2892[var6] = this.anIntArray2892[var6] * var5 - this.anIntArray2885[var6] * var4 >> 16; - this.anIntArray2885[var6] = var7; - } - } - - } - - final int method2014(int var1, int var3) { - for (int var4 = 0; var4 < this.anInt2887; ++var4) { - if (this.anIntArray2885[var4] == var1 && this.anIntArray2881[var4] == 0 && this.anIntArray2892[var4] == var3) { - return var4; - } - } - - this.anIntArray2885[this.anInt2887] = var1; - this.anIntArray2881[this.anInt2887] = 0; - this.anIntArray2892[this.anInt2887] = var3; - return this.anInt2887++; - } - - final void method2016(short var1, short var2) { - for (int var3 = 0; var3 < this.anInt2849; ++var3) { - if (this.aShortArray2870[var3] == var1) { - this.aShortArray2870[var3] = var2; - } - } - - } - - private void method2017(byte[] var1) { - boolean var2 = false; - boolean var3 = false; - DataBuffer var4 = new DataBuffer(var1); - DataBuffer var5 = new DataBuffer(var1); - DataBuffer var6 = new DataBuffer(var1); - DataBuffer var7 = new DataBuffer(var1); - DataBuffer var8 = new DataBuffer(var1); - var4.index = var1.length - 18; - int var9 = var4.readUnsignedShort(); - int var10 = var4.readUnsignedShort(); - int var11 = var4.readUnsignedByte(); - int var12 = var4.readUnsignedByte(); - int var13 = var4.readUnsignedByte(); - int var14 = var4.readUnsignedByte(); - int var15 = var4.readUnsignedByte(); - int var16 = var4.readUnsignedByte(); - int var17 = var4.readUnsignedShort(); - int var18 = var4.readUnsignedShort(); - var4.readUnsignedShort(); - int var20 = var4.readUnsignedShort(); - byte var21 = 0; - int var45 = var21 + var9; - int var23 = var45; - var45 += var10; - int var24 = var45; - if (var13 == 255) { - var45 += var10; - } - - int var25 = var45; - if (var15 == 1) { - var45 += var10; - } - - int var26 = var45; - if (var12 == 1) { - var45 += var10; - } - - int var27 = var45; - if (var16 == 1) { - var45 += var9; - } - - int var28 = var45; - if (var14 == 1) { - var45 += var10; - } - - int var29 = var45; - var45 += var20; - int var30 = var45; - var45 += var10 * 2; - int var31 = var45; - var45 += var11 * 6; - int var32 = var45; - var45 += var17; - int var33 = var45; - var45 += var18; - this.anInt2887 = var9; - this.anInt2849 = var10; - this.anInt2862 = var11; - this.anIntArray2885 = new int[var9]; - this.anIntArray2881 = new int[var9]; - this.anIntArray2892 = new int[var9]; - this.anIntArray2865 = new int[var10]; - this.anIntArray2878 = new int[var10]; - this.anIntArray2864 = new int[var10]; - if (var11 > 0) { - this.aByteArray2857 = new byte[var11]; - this.aShortArray2884 = new short[var11]; - this.aShortArray2846 = new short[var11]; - this.aShortArray2891 = new short[var11]; - } - - if (var16 == 1) { - this.anIntArray2860 = new int[var9]; - } - - if (var12 == 1) { - this.aByteArray2859 = new byte[var10]; - this.aByteArray2866 = new byte[var10]; - this.aShortArray2858 = new short[var10]; - } - - if (var13 == 255) { - this.aByteArray2889 = new byte[var10]; - } else { - this.aByte2848 = (byte) var13; - } - - if (var14 == 1) { - this.aByteArray2843 = new byte[var10]; - } - - if (var15 == 1) { - this.anIntArray2847 = new int[var10]; - } - - this.aShortArray2870 = new short[var10]; - var4.index = var21; - var5.index = var32; - var6.index = var33; - var7.index = var45; - var8.index = var27; - int var35 = 0; - int var36 = 0; - int var37 = 0; - - int var38; - int var39; - int var42; - int var40; - int var41; - for (var38 = 0; var38 < var9; ++var38) { - var39 = var4.readUnsignedByte(); - var40 = 0; - if ((var39 & 1) != 0) { - var40 = var5.getByteOrShort(); - } - - var41 = 0; - if ((var39 & 2) != 0) { - var41 = var6.getByteOrShort(); - } - - var42 = 0; - if ((var39 & 4) != 0) { - var42 = var7.getByteOrShort(); - } - - this.anIntArray2885[var38] = var35 + var40; - this.anIntArray2881[var38] = var36 + var41; - this.anIntArray2892[var38] = var37 + var42; - var35 = this.anIntArray2885[var38]; - var36 = this.anIntArray2881[var38]; - var37 = this.anIntArray2892[var38]; - if (var16 == 1) { - this.anIntArray2860[var38] = var8.readUnsignedByte(); - } - } - - var4.index = var30; - var5.index = var26; - var6.index = var24; - var7.index = var28; - var8.index = var25; - - for (var38 = 0; var38 < var10; ++var38) { - this.aShortArray2870[var38] = (short) var4.readUnsignedShort(); - if (var12 == 1) { - var39 = var5.readUnsignedByte(); - if ((var39 & 1) == 1) { - this.aByteArray2859[var38] = 1; - var2 = true; - } else { - this.aByteArray2859[var38] = 0; - } - - if ((var39 & 2) == 2) { - this.aByteArray2866[var38] = (byte) (var39 >> 2); - this.aShortArray2858[var38] = this.aShortArray2870[var38]; - this.aShortArray2870[var38] = 127; - if (this.aShortArray2858[var38] != -1) { - var3 = true; - } - } else { - this.aByteArray2866[var38] = -1; - this.aShortArray2858[var38] = -1; - } - } - - if (var13 == 255) { - this.aByteArray2889[var38] = var6.readSignedByte(); - } - - if (var14 == 1) { - this.aByteArray2843[var38] = var7.readSignedByte(); - } - - if (var15 == 1) { - this.anIntArray2847[var38] = var8.readUnsignedByte(); - } - } - - var4.index = var29; - var5.index = var23; - var38 = 0; - var39 = 0; - var40 = 0; - var41 = 0; - - int var43; - int var44; - for (var42 = 0; var42 < var10; ++var42) { - var43 = var5.readUnsignedByte(); - if (var43 == 1) { - var38 = var4.getByteOrShort() + var41; - var39 = var4.getByteOrShort() + var38; - var40 = var4.getByteOrShort() + var39; - var41 = var40; - this.anIntArray2865[var42] = var38; - this.anIntArray2878[var42] = var39; - this.anIntArray2864[var42] = var40; - } - - if (var43 == 2) { - var39 = var40; - var40 = var4.getByteOrShort() + var41; - var41 = var40; - this.anIntArray2865[var42] = var38; - this.anIntArray2878[var42] = var39; - this.anIntArray2864[var42] = var40; - } - - if (var43 == 3) { - var38 = var40; - var40 = var4.getByteOrShort() + var41; - var41 = var40; - this.anIntArray2865[var42] = var38; - this.anIntArray2878[var42] = var39; - this.anIntArray2864[var42] = var40; - } - - if (var43 == 4) { - var44 = var38; - var38 = var39; - var39 = var44; - var40 = var4.getByteOrShort() + var41; - var41 = var40; - this.anIntArray2865[var42] = var38; - this.anIntArray2878[var42] = var44; - this.anIntArray2864[var42] = var40; - } - } - - var4.index = var31; - - for (var42 = 0; var42 < var11; ++var42) { - this.aByteArray2857[var42] = 0; - this.aShortArray2884[var42] = (short) var4.readUnsignedShort(); - this.aShortArray2846[var42] = (short) var4.readUnsignedShort(); - this.aShortArray2891[var42] = (short) var4.readUnsignedShort(); - } - - if (this.aByteArray2866 != null) { - boolean var46 = false; - - for (var43 = 0; var43 < var10; ++var43) { - var44 = this.aByteArray2866[var43] & 0xFF; - if (var44 != 255) { - if ((this.aShortArray2884[var44] & 65535) == this.anIntArray2865[var43] && (this.aShortArray2846[var44] & 65535) == this.anIntArray2878[var43] && (this.aShortArray2891[var44] & 65535) == this.anIntArray2864[var43]) { - this.aByteArray2866[var43] = -1; - } else { - var46 = true; - } - } - } - - if (!var46) { - this.aByteArray2866 = null; - } - } - - if (!var3) { - this.aShortArray2858 = null; - } - - if (!var2) { - this.aByteArray2859 = null; - } - - } - - final void method2018() { - for (int var1 = 0; var1 < this.anInt2887; ++var1) { - int var2 = this.anIntArray2892[var1]; - this.anIntArray2892[var1] = this.anIntArray2885[var1]; - this.anIntArray2885[var1] = -var2; - } - - this.method2007(); - } - -} diff --git a/Client/src/main/java/org/runite/client/MouseListeningClass.java b/Client/src/main/java/org/runite/client/MouseListeningClass.java deleted file mode 100644 index 355cd6c75..000000000 --- a/Client/src/main/java/org/runite/client/MouseListeningClass.java +++ /dev/null @@ -1,216 +0,0 @@ -package org.runite.client; - -import javax.swing.*; -import java.awt.event.*; - -public final class MouseListeningClass implements MouseListener, MouseMotionListener, FocusListener { - static volatile int anInt1340 = -1; - public int mouseWheelX; - public int mouseWheelY; - static float[] aFloatArray1919 = new float[4]; - static int[] anIntArray1920; - static int anInt1921 = 0; - static int anInt1923; - public static int anInt1924 = 0; - static int anInt1925 = 0; - static int anInt1926; - - public final synchronized void mouseMoved(MouseEvent var1) { - try { - if (Unsorted.aClass149_4047 != null) { - Unsorted.anInt4045 = 0; - Class3_Sub21.anInt2493 = var1.getX(); - anInt1340 = var1.getY(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.mouseMoved(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method2087() { - try { - Class3_Sub4 var1 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1222(); - for (; var1 != null; var1 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1221()) { - if (var1.anInt2259 > 0) { - var1.anInt2259 -= 1; - } - if (var1.anInt2259 != 0) { - if (var1.anInt2261 > 0) { - var1.anInt2261 -= 1; - } - if ((var1.anInt2261 == 0) && (1 <= var1.anInt2264) && (1 <= var1.anInt2248) && (102 >= var1.anInt2264) && (var1.anInt2248 <= 102) && ((var1.anInt2265 < 0) || (Unsorted.method590((byte) -34, var1.anInt2265, var1.anInt2262)))) { - Unsorted.method1048(var1.anInt2265, var1.anInt2264, var1.anInt2250, var1.anInt2256, var1.anInt2248, -65, var1.anInt2262, var1.anInt2263); - var1.anInt2261 = -1; - if ((var1.anInt2265 == var1.anInt2254) && (var1.anInt2254 == -1)) { - var1.unlink(); - } else if ((var1.anInt2254 == var1.anInt2265) && (var1.anInt2256 == var1.anInt2257) && (var1.anInt2262 == var1.anInt2253)) { - var1.unlink(); - } - } - } else if ((var1.anInt2254 < 0) || (Unsorted.method590((byte) -66, var1.anInt2254, var1.anInt2253))) { - Unsorted.method1048(var1.anInt2254, var1.anInt2264, var1.anInt2250, var1.anInt2257, var1.anInt2248, -71, var1.anInt2253, var1.anInt2263); - var1.unlink(); - } - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.A(" + (byte) -82 + ')'); - } - } - - public final synchronized void focusLost(FocusEvent var1) { - try { - if (Unsorted.aClass149_4047 != null) { - GraphicDefinition.anInt549 = 0; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.focusLost(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - -// static void method2089() -// { -// try -// { -// Class158_Sub1.aReferenceCache_2982.clearSoftReferences(); -// } -// catch (RuntimeException var2) -// { -// throw ClientErrorException.clientError(var2, "ug.D(" + (byte) 115 + ')'); -// } -// } - - public final synchronized void mouseDragged(MouseEvent var1) { - try { - int x = var1.getX(); - int y = var1.getY(); - if (SwingUtilities.isMiddleMouseButton(var1)) { - int accelX = this.mouseWheelX - x; - int accelY = this.mouseWheelY - y; - this.mouseWheelX = var1.getX(); - this.mouseWheelY = var1.getY(); - GraphicDefinition.CAMERA_DIRECTION += accelX * 2; - Unsorted.anInt2309 -= (accelY << 1); - return; - } - if (Unsorted.aClass149_4047 != null) { - Unsorted.anInt4045 = 0; - Class3_Sub21.anInt2493 = var1.getX(); - anInt1340 = var1.getY(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.mouseDragged(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public final synchronized void mouseReleased(MouseEvent var1) { - try { - if (Unsorted.aClass149_4047 != null) { - Unsorted.anInt4045 = 0; - GraphicDefinition.anInt549 = 0; - } - if (var1.isPopupTrigger()) { - var1.consume(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.mouseReleased(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public final void mouseClicked(MouseEvent var1) { - try { - if (var1.isPopupTrigger()) { - var1.consume(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.mouseClicked(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public final void focusGained(FocusEvent var1) { - } - - static void method2090() { - try { - if (Unsorted.aClass149_4047 != null) { - MouseListeningClass var1 = Unsorted.aClass149_4047; - synchronized (var1) { - Unsorted.aClass149_4047 = null; - } - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ug.F(" + 8 + ')'); - } - } - - public final synchronized void mousePressed(MouseEvent var1) { - try { - if (SwingUtilities.isMiddleMouseButton(var1)) { - this.mouseWheelX = var1.getX(); - this.mouseWheelY = var1.getY(); - return; - } - if (Unsorted.aClass149_4047 != null) { - Unsorted.anInt4045 = 0; - RenderAnimationDefinition.anInt362 = var1.getX(); - TextureOperation14.anInt3389 = var1.getY(); - Class140_Sub6.aLong2926 = TimeUtils.time(); - if (var1.getButton() == MouseEvent.BUTTON3) // SwingUtilities.isRightMouseButton(var1) - { - Class140_Sub3.anInt2743 = 2; - GraphicDefinition.anInt549 = 2; - } else if (SwingUtilities.isLeftMouseButton(var1)) { - Class140_Sub3.anInt2743 = 1; - GraphicDefinition.anInt549 = 1; - } - } - if (var1.isPopupTrigger()) { - var1.consume(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.mousePressed(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public final synchronized void mouseExited(MouseEvent var1) { - try { - if (Unsorted.aClass149_4047 != null) { - Unsorted.anInt4045 = 0; - Class3_Sub21.anInt2493 = -1; - anInt1340 = -1; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.mouseExited(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method2091(int var0) { - try { - if ((GameObject.anIntArray1838 == null) || (GameObject.anIntArray1838.length < var0)) { - GameObject.anIntArray1838 = new int[var0]; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.E(" + var0 + ',' + 4 + ')'); - } - } - - public final synchronized void mouseEntered(MouseEvent var1) { - try { - if (Unsorted.aClass149_4047 != null) { - Unsorted.anInt4045 = 0; - Class3_Sub21.anInt2493 = var1.getX(); - anInt1340 = var1.getY(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.mouseEntered(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method2092(int var0) { - try { - InterfaceWidget var2 = InterfaceWidget.getWidget(9, var0); - var2.a(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ug.B(" + var0 + ',' + (byte) -47 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/MouseWheel.java b/Client/src/main/java/org/runite/client/MouseWheel.java deleted file mode 100644 index 9ec9c1f8c..000000000 --- a/Client/src/main/java/org/runite/client/MouseWheel.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.runite.client; - -import java.awt.*; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; - -final class MouseWheel extends Class146 implements MouseWheelListener { - - static int moveAmt = 0; - static boolean shiftDown = false; - static boolean ctrlDown = false; - private int anInt2941 = 0; - - final void method2082(Component var2) { - - var2.removeMouseWheelListener(this); - } - - final synchronized int method2078() { - int var2 = this.anInt2941; - this.anInt2941 = 0; - - return var2; - } - - public final synchronized void mouseWheelMoved(MouseWheelEvent var1) { - this.anInt2941 += var1.getWheelRotation(); - moveAmt = var1.getWheelRotation(); - - // Client scroll - if (shiftDown || ctrlDown) { - if ((Client.ZOOM > 1200 && MouseWheel.moveAmt >= 0) || (Client.ZOOM < 100 && MouseWheel.moveAmt <= 0)) { - //Class3_Sub28_Sub12.sendMessage("You cannot zoom any further than this."); - return; - } - Client.ZOOM += MouseWheel.moveAmt >= 0 ? 50 : -50; - //Class3_Sub28_Sub12.sendMessage("Game client is back to default zoom."); - } - - } - - final void method2084(Component var1, int var2) { - if (var2 < -70) { - var1.addMouseWheelListener(this); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/NPC.java b/Client/src/main/java/org/runite/client/NPC.java deleted file mode 100644 index f7a92a851..000000000 --- a/Client/src/main/java/org/runite/client/NPC.java +++ /dev/null @@ -1,301 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -public final class NPC extends Class140_Sub4 { - - static boolean aBoolean3975 = false; - static int[] npcSpawnCacheIndices; - public static NPC[] npcs = new NPC[32768]; - static Class3_Sub27 aClass3_Sub27_1154; - static AbstractSprite[] aAbstractSpriteArray3977; - static float aFloat3979; - static int[] anIntArray3986 = new int[32]; - static int anInt3995; - static int[] anIntArray3997 = new int[]{19, 55, 38, 155, 255, 110, 137, 205, 76}; - static int anInt4001; - public NPCDefinition definition; - - static int method1984(int var0, int var1, int var2) { - try { - if (var1 == 38) { - int var3 = 57 * var2 + var0; - var3 ^= var3 << 13; - int var4 = Integer.MAX_VALUE & 1376312589 + (var3 * var3 * 15731 - -789221) * var3; - return (var4 & 133802063) >> 19; - } else { - return 88; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "km.S(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - static byte[] method1985(Object var1, boolean var2) { - try { - if (var1 == null) { - return null; - } else if (var1 instanceof byte[]) { - byte[] var5 = (byte[]) var1; - return var2 ? Class12.method873(var5) : var5; - } else { - if (var1 instanceof Class144) { - Class144 var3 = (Class144) var1; - return var3.getBytes(); - } else { - throw new IllegalArgumentException(); - } - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "km.Q(" + "{...}" + ',' + var2 + ')'); - } - } - - static boolean method1986(int var0) { - try { - if (var0 <= 22) { - method1984(-48, 88, 31); - } - - return HDToolKit.highDetail || Unsorted.aBoolean3665; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "km.O(" + var0 + ')'); - } - } - - static boolean method1988() { - try { - try { - if (Unsorted.anInt154 == 2) { - if (aClass3_Sub27_1154 == null) { - aClass3_Sub27_1154 = Class3_Sub27.method517(Class101.aClass153_1423, TextureOperation8.anInt3463, Class132.anInt1741); - if (null == aClass3_Sub27_1154) { - return false; - } - } - - if (QuickChatDefinition.aClass83_3579 == null) { - QuickChatDefinition.aClass83_3579 = new Class83(Class40.aClass153_679, Class3_Sub28_Sub20.aClass153_3786); - } - - if (Class101.aClass3_Sub24_Sub4_1421.method470(aClass3_Sub27_1154, Class124.aClass153_1661, QuickChatDefinition.aClass83_3579)) { - Class101.aClass3_Sub24_Sub4_1421.method471(); - Class101.aClass3_Sub24_Sub4_1421.method506(TextureOperation36.anInt3423); - Class101.aClass3_Sub24_Sub4_1421.method490(Unsorted.aBoolean2311, aClass3_Sub27_1154); - Unsorted.anInt154 = 0; - aClass3_Sub27_1154 = null; - QuickChatDefinition.aClass83_3579 = null; - Class101.aClass153_1423 = null; - return true; - } - } - } catch (Exception var2) { - var2.printStackTrace(); - Class101.aClass3_Sub24_Sub4_1421.method505((byte) -128); - Class101.aClass153_1423 = null; - aClass3_Sub27_1154 = null; - Unsorted.anInt154 = 0; - QuickChatDefinition.aClass83_3579 = null; - } - - return false; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "km.P(" + false + ')'); - } - } - - static void method273(int[] var0, NPC var2, int[] var3, int[] var4) { - try { - int var5 = 0; - while(var5 < var4.length) { - int var6 = var4[var5]; - int var7 = var0[var5]; - int var8 = var3[var5]; - - for(int var9 = 0; var7 != 0 && var9 < var2.aClass145Array2809.length; ++var9) { - if((1 & var7) != 0) { - if(var6 == -1) { - var2.aClass145Array2809[var9] = null; - } else { - SequenceDefinition var10 = SequenceDefinition.getAnimationDefinition(var6); - Class145 var12 = var2.aClass145Array2809[var9]; - int var11 = var10.delayType; - if(null != var12) { - if(var12.animationId != var6) { - if(SequenceDefinition.getAnimationDefinition(var12.animationId).forcedPriority <= var10.forcedPriority) { - var12 = var2.aClass145Array2809[var9] = null; - } - } else if(var11 == 0) { - var12 = var2.aClass145Array2809[var9] = null; - } else if(var11 == 1) { - var12.anInt1893 = 0; - var12.anInt1894 = 0; - var12.anInt1891 = 1; - var12.anInt1897 = 0; - var12.anInt1900 = var8; - Unsorted.method1470(var2.zAxis, var10, var2.xAxis, false, 0); - } else if(var11 == 2) { - var12.anInt1894 = 0; - } - } - - if(null == var12) { - var12 = var2.aClass145Array2809[var9] = new Class145(); - var12.anInt1891 = 1; - var12.anInt1897 = 0; - var12.anInt1900 = var8; - var12.animationId = var6; - var12.anInt1894 = 0; - var12.anInt1893 = 0; - Unsorted.method1470(var2.zAxis, var10, var2.xAxis, false, 0); - } - } - } - - var7 >>>= 1; - } - - ++var5; - } - - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "mi.B(" + (var0 != null?"{...}":"null") + ',' + (byte) 92 + ',' + (var2 != null?"{...}":"null") + ',' + (var3 != null?"{...}":"null") + ',' + (var4 != null?"{...}":"null") + ')'); - } - } - - protected final void finalize() { - } - - final int method1871() { - try { - return this.anInt2820; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "km.MA()"); - } - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - try { - if (this.definition != null) { - SequenceDefinition var13 = this.anInt2771 != -1 && this.anInt2828 == 0 ? SequenceDefinition.getAnimationDefinition(this.anInt2771) : null; - SequenceDefinition var14 = -1 != this.anInt2764 && (this.getRenderAnimationType().stand_animation != this.anInt2764 || var13 == null) ? SequenceDefinition.getAnimationDefinition(this.anInt2764) : null; - Model var15 = this.definition.method1476(this.aClass145Array2809, this.anInt2793, (byte) -116, this.anInt2813, this.anInt2776, this.anInt2760, this.anInt2832, var14, this.anInt2802, var13); - if (var15 != null) { - this.anInt2820 = var15.method1871(); - NPCDefinition var16 = this.definition; - if (null != var16.childNPCs) { - var16 = var16.method1471((byte) -110); - } - - Model var17; - if (Class140_Sub6.aBoolean2910 && Objects.requireNonNull(var16).aBoolean1249) { - var17 = Class140_Sub3.method1957(this.definition.aByte1287, this.aBoolean2810, null == var14 ? var13 : var14, this.xAxis, this.definition.aShort1256, this.zAxis, this.definition.aShort1286, this.definition.size, var15, var1, null != var14 ? this.anInt2813 : this.anInt2832, this.anInt2831, this.definition.aByte1275); - if (HDToolKit.highDetail) { - float var18 = HDToolKit.method1852(); - float var19 = HDToolKit.method1839(); - HDToolKit.depthBufferWritingDisabled(); - HDToolKit.method1825(var18, -150.0F + var19); - var17.animate(0, var2, var3, var4, var5, var6, var7, var8, -1L, var11, this.aClass127_Sub1_2801); - HDToolKit.method1830(); - HDToolKit.method1825(var18, var19); - } else { - var17.animate(0, var2, var3, var4, var5, var6, var7, var8, -1L, var11, this.aClass127_Sub1_2801); - } - } - - this.method1971(var15, (byte) -111); - this.method1969((byte) 115, var15, var1); - var17 = null; - if (this.anInt2842 != -1 && -1 != this.anInt2805) { - GraphicDefinition var21 = GraphicDefinition.getGraphicDefinition(this.anInt2842); - var17 = var21.method966(this.anInt2826, this.anInt2805, this.anInt2761); - if (var17 != null) { - var17.method1897(0, -this.anInt2799, 0); - if (var21.aBoolean536) { - if (TextureOperation15.anInt3198 != 0) { - var17.method1896(TextureOperation15.anInt3198); - } - - if (Class3_Sub28_Sub9.anInt3623 != 0) { - var17.method1886(Class3_Sub28_Sub9.anInt3623); - } - - if (0 != TextureOperation16.anInt3111) { - var17.method1897(0, TextureOperation16.anInt3111, 0); - } - } - } - } - - if (HDToolKit.highDetail) { - if (this.definition.size == 1) { - var15.aBoolean2699 = true; - } - - var15.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2801); - if (var17 != null) { - if (this.definition.size == 1) { - var17.aBoolean2699 = true; - } - - var17.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2801); - } - } else { - if (null != var17) { - var15 = ((Class140_Sub1_Sub2) var15).method1943(var17); - } - - if (this.definition.size == 1) { - var15.aBoolean2699 = true; - } - - var15.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2801); - } - - } - } - } catch (RuntimeException var20) { - throw ClientErrorException.clientError(var20, "km.IA(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var11 + ',' + (var12 != null ? "{...}" : "null") + ')'); - } - } - - final int getRenderAnimationId() { - try { - if (Class158.paramGameTypeID != 0 && this.definition.childNPCs != null) { - NPCDefinition var2 = this.definition.method1471((byte) 21); - if (var2 != null && var2.renderAnimationId != -1) { - return var2.renderAnimationId; - } - } - - return this.renderAnimationId; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "km.B(" + -1 + ')'); - } - } - - final void method1867(int var1, int var2, int var3, int var4, int var5) { - try { - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "km.IB(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - public final boolean hasDefinitions() { - try { - - return null != this.definition; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "km.L(" + (byte) 17 + ')'); - } - } - - final void setDefinitions(NPCDefinition var2) { - try { - this.definition = var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "km.R(" + -1 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/NPCDefinition.java b/Client/src/main/java/org/runite/client/NPCDefinition.java deleted file mode 100644 index 7f043ada4..000000000 --- a/Client/src/main/java/org/runite/client/NPCDefinition.java +++ /dev/null @@ -1,916 +0,0 @@ -package org.runite.client; - - -import org.rs09.SystemLogger; -import org.rs09.client.Linkable; -import org.rs09.client.config.GameConfig; -import org.rs09.client.data.HashTable; -import org.rs09.client.LinkableInt; - -import java.util.Objects; - -public final class NPCDefinition { - - static SequenceDefinition[] aClass142Array1168 = new SequenceDefinition[14]; - static Class3_Sub28_Sub5[] aClass3_Sub28_Sub5Array3041 = new Class3_Sub28_Sub5[14]; - static CacheIndex aClass153_3173; - static Class3_Sub28_Sub5[] aClass3_Sub28_Sub5Array4031 = new Class3_Sub28_Sub5[14]; - static int[] anIntArray912 = new int[14]; - int size = 1; - private short[] aShortArray1246; - private byte[] aByteArray1247; - private short[] aShortArray1248; - boolean aBoolean1249; - private int[] anIntArray1250; - private int anInt1251; - static int anInt1252 = -1; - int anInt1253; - private short[] aShortArray1254; - boolean aBoolean1255 = true; - short aShort1256 = 0; - private int configId; - private int[][] anIntArrayArray1258; - public RSString[] options; - int anInt1260; - private int[][] anIntArrayArray1261; - int anInt1262 = -1; - boolean aBoolean1263; - private int anInt1264; - int anInt1265; - private int anInt1266; - byte aByte1267; - byte aByte1268; - int anInt1269; - public boolean aBoolean1270 = true; - private short[] aShortArray1271; - private HashTable aHashTable_1272; - RSString NPCName; - int anInt1274; - byte aByte1275; - int anInt1276 = -1; - static int[] anIntArray1277 = new int[2000]; - int anInt1278; - int anInt1279; - int renderAnimationId; - private int anInt1282; - public int anInt1283; - public int npcId; - public boolean aBoolean1285; - short aShort1286; - byte aByte1287; - private int[] models; - int anInt1289; - int anInt1290; - int anInt1291; - public int[] childNPCs; - int anInt1293; - private int configFileId; - int anInt1296; - static int anInt1297; - int anInt1298; - - static NPCDefinition getNPCDefinition(int npcID) { - try { - NPCDefinition def = (NPCDefinition) Unsorted.aReferenceCache_4043.get(npcID); - if (null == def) { - byte[] var3 = Class29.aClass153_557.getFile(Class38_Sub1.method1031(npcID), Unsorted.method54(npcID)); - def = new NPCDefinition(); - - def.npcId = npcID; - if (null != var3) { - def.method1478(new DataBuffer(var3)); - } - Unsorted.aReferenceCache_4043.put(def, npcID); - } - return def; - } catch (RuntimeException var4) { - SystemLogger.logErr("Unable to parse NPC definition for ID " + npcID + " trace: " + var4); - } - return null; - } - - public final NPCDefinition method1471(byte var1) { - try { - int var2 = -1; - if(this.configId == -1) { - if(this.configFileId != -1) { - var2 = ItemDefinition.ram[this.configFileId]; - } - } else { - var2 = method1484(this.configId); - } - - int var3; - if(0 <= var2 && -1 + this.childNPCs.length > var2 && this.childNPCs[var2] != -1) { - return getNPCDefinition(this.childNPCs[var2]); - } else { - var3 = this.childNPCs[-1 + this.childNPCs.length]; - return var3 == -1 ?null: getNPCDefinition(var3); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "me.G(" + var1 + ')'); - } - } - - final boolean method1472() { - try { - if(null == this.childNPCs) { - return true; - } else { - int var2 = -1; - if(-1 == this.configId) { - if(this.configFileId != -1) { - var2 = ItemDefinition.ram[this.configFileId]; - } - } else { - var2 = method1484(this.configId); - } - - if(var2 >= 0 && var2 < -1 + this.childNPCs.length && -1 != this.childNPCs[var2]) { - return true; - } else { - int var3 = this.childNPCs[-1 + this.childNPCs.length]; - return var3 != -1; - } - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "me.L(" + (byte) 74 + ')'); - } - } - - final boolean method1474() { - try { - - if(this.childNPCs == null) { - return -1 != this.anInt1262 || this.anInt1293 != -1 || this.anInt1276 != -1; - } else { - for(int var2 = 0; var2 < this.childNPCs.length; ++var2) { - if(this.childNPCs[var2] != -1) { - NPCDefinition var3 = getNPCDefinition(this.childNPCs[var2]); - if(var3.anInt1262 != -1 || var3.anInt1293 != -1 || var3.anInt1276 != -1) { - return true; - } - } - } - - return false; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "me.E(" + -1 + ')'); - } - } - - final int method1475(int var1, int var3) { - try { - if(null == this.aHashTable_1272) { - return var3; - } else { - LinkableInt var4 = (LinkableInt)this.aHashTable_1272.get(var1); - return var4 != null?var4.value :var3; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "me.N(" + var1 + ',' + -26460 + ',' + var3 + ')'); - } - } - - final Model method1476(Class145[] var1, int var2, byte var3, int var4, int var5, int var6, int var7, SequenceDefinition var8, int var9, SequenceDefinition var10) { - try { - if(this.childNPCs == null) { - Model var11 = (Model)CS2Script.aReferenceCache_2442.get(this.npcId); - boolean var12; - int var17; - int var16; - int var19; - int var18; - int var21; - int var20; - int var22; - int var24; - int var27; - int var29; - int var28; - if(null == var11) { - var12 = false; - - for (int model : this.models) { - if (model != -1 && !aClass153_3173.method2129((byte) 102, 0, model)) { - var12 = true; - } - } - - if(var12) { - return null; - } - - Model_Sub1[] var14 = new Model_Sub1[this.models.length]; - - for(int var15 = 0; var15 < this.models.length; ++var15) { - if(this.models[var15] != -1) { - var14[var15] = Model_Sub1.method2015(aClass153_3173, this.models[var15]); - if(null != this.anIntArrayArray1261 && this.anIntArrayArray1261[var15] != null && var14[var15] != null) { - var14[var15].method2001(this.anIntArrayArray1261[var15][0], this.anIntArrayArray1261[var15][1], this.anIntArrayArray1261[var15][2]); - } - } - } - - RenderAnimationDefinition render = null; - if(-1 != this.renderAnimationId) { - render = Class3_Sub10.getRenderAnimationDefinition(this.renderAnimationId); - } - - if(render != null && null != render.equipment_transforms) { - for(var16 = 0; var16 < render.equipment_transforms.length; ++var16) { - if(render.equipment_transforms[var16] != null && var14.length > var16 && var14[var16] != null) { - var19 = render.equipment_transforms[var16][2]; - var20 = render.equipment_transforms[var16][3]; - var21 = render.equipment_transforms[var16][4]; - var18 = render.equipment_transforms[var16][1]; - var22 = render.equipment_transforms[var16][5]; - var17 = render.equipment_transforms[var16][0]; - if(this.anIntArrayArray1258 == null) { - this.anIntArrayArray1258 = new int[render.equipment_transforms.length][]; - } - - if(null == this.anIntArrayArray1258[var16]) { - int[] var23 = this.anIntArrayArray1258[var16] = new int[15]; - if(var20 == 0 && var21 == 0 && var22 == 0) { - var23[13] = -var18; - var23[14] = -var19; - var23[0] = var23[4] = var23[8] = 32768; - var23[12] = -var17; - } else { - var24 = Class51.anIntArray851[var20] >> 1; - int var25 = Class51.anIntArray840[var20] >> 1; - int var26 = Class51.anIntArray851[var21] >> 1; - var28 = Class51.anIntArray851[var22] >> 1; - var27 = Class51.anIntArray840[var21] >> 1; - var29 = Class51.anIntArray840[var22] >> 1; - var23[3] = var24 * var29 - -16384 >> 15; - var23[8] = 16384 + var24 * var26 >> 15; - var23[5] = -var25; - int var31 = 16384 + var25 * var29 >> 15; - int var30 = var28 * var25 + 16384 >> 15; - var23[1] = 16384 + -var29 * var26 - -(var30 * var27) >> 15; - var23[2] = 16384 + var27 * var24 >> 15; - var23[6] = 16384 + -var27 * var28 + var31 * var26 >> 15; - var23[14] = 16384 + var23[8] * -var19 + -var18 * var23[5] + var23[2] * -var17 >> 15; - var23[4] = 16384 + var24 * var28 >> 15; - var23[7] = 16384 + -var27 * -var29 + var30 * var26 >> 15; - var23[0] = var27 * var31 + var26 * var28 + 16384 >> 15; - var23[12] = 16384 + var23[6] * -var19 + var23[3] * -var18 + -var17 * var23[0] >> 15; - var23[13] = -var19 * var23[7] + -var17 * var23[1] + (-var18 * var23[4] - -16384) >> 15; - } - - var23[10] = var18; - var23[9] = var17; - var23[11] = var19; - } - - if(var20 != 0 || var21 != 0 || var22 != 0) { - var14[var16].method2013(var20, var21, var22); - } - - if(var17 != 0 || 0 != var18 || var19 != 0) { - var14[var16].method2001(var17, var18, var19); - } - } - } - } - - Model_Sub1 var34; - if(var14.length == 1) { - var34 = var14[0]; - } else { - var34 = new Model_Sub1(var14, var14.length); - } - - if(this.aShortArray1248 != null) { - for(var16 = 0; var16 < this.aShortArray1248.length; ++var16) { - if(null != this.aByteArray1247 && var16 < this.aByteArray1247.length) { - Objects.requireNonNull(var34).method2016(this.aShortArray1248[var16], Class136.aShortArray1779[this.aByteArray1247[var16] & 0xFF]); - } else { - Objects.requireNonNull(var34).method2016(this.aShortArray1248[var16], this.aShortArray1254[var16]); - } - } - } - - if(null != this.aShortArray1271) { - for(var16 = 0; this.aShortArray1271.length > var16; ++var16) { - Objects.requireNonNull(var34).method1998(this.aShortArray1271[var16], this.aShortArray1246[var16]); - } - } - - var11 = Objects.requireNonNull(var34).method2008(this.anInt1251 + 64, this.anInt1282 + 850, -30, -50, -30); - if(HDToolKit.highDetail) { - ((Class140_Sub1_Sub1)var11).method1920(false, false, false, false, false, true); - } - - CS2Script.aReferenceCache_2442.put(var11, this.npcId); - } - - var12 = false; - boolean var37 = false; - boolean var35 = false; - boolean var36 = false; - var16 = null != var1?var1.length:0; - for(var17 = 0; var17 < var16; ++var17) { - if(var1[var17] != null) { - SequenceDefinition def = SequenceDefinition.getAnimationDefinition(var1[var17].animationId); - if(null != def.frames) { - aClass142Array1168[var17] = def; - var20 = var1[var17].anInt1891; - var12 = true; - var19 = var1[var17].anInt1893; - var21 = def.frames[var19]; - aClass3_Sub28_Sub5Array3041[var17] = Class3_Sub9.method133(var21 >>> 16); - var21 &= 65535; - anIntArray912[var17] = var21; - if(aClass3_Sub28_Sub5Array3041[var17] != null) { - var35 |= aClass3_Sub28_Sub5Array3041[var17].method561(var21, (byte)124); - var37 |= aClass3_Sub28_Sub5Array3041[var17].method559(var21); - var36 |= def.aBoolean1848; - } - - if((def.aBoolean1846 || ClientCommands.tweeningEnabled) && var20 != -1 && var20 < def.frames.length) { - Class38.anIntArray664[var17] = def.duration[var19]; - Unsorted.anIntArray2574[var17] = var1[var17].anInt1897; - var22 = def.frames[var20]; - aClass3_Sub28_Sub5Array4031[var17] = Class3_Sub9.method133(var22 >>> 16); - var22 &= 65535; - Class30.anIntArray574[var17] = var22; - if(null != aClass3_Sub28_Sub5Array4031[var17]) { - var35 |= aClass3_Sub28_Sub5Array4031[var17].method561(var22, (byte)124); - var37 |= aClass3_Sub28_Sub5Array4031[var17].method559(var22); - } - } else { - Class38.anIntArray664[var17] = 0; - Unsorted.anIntArray2574[var17] = 0; - aClass3_Sub28_Sub5Array4031[var17] = null; - Class30.anIntArray574[var17] = -1; - } - } - } - } - if(!var12 && null == var10 && var8 == null) { - Model var41 = var11.method1894(true, true, true); - if(this.anInt1264 != 128 || this.anInt1266 != 128) { - var41.resize(this.anInt1264, this.anInt1266, this.anInt1264); - } - - return var41; - } else { - var18 = -1; - var17 = -1; - var19 = 0; - Class3_Sub28_Sub5 var40 = null; - Class3_Sub28_Sub5 var43 = null; - int var42; - if(var10 != null) { - var17 = var10.frames[var7]; - var22 = var17 >>> 16; - var17 &= 65535; - var40 = Class3_Sub9.method133(var22); - if(null != var40) { - var35 |= var40.method561(var17, (byte)126); - var37 |= var40.method559(var17); - var36 |= var10.aBoolean1848; - } - - if((var10.aBoolean1846 || ClientCommands.tweeningEnabled) && var5 != -1 && var5 < var10.frames.length) { - var19 = var10.duration[var7]; - var18 = var10.frames[var5]; - var42 = var18 >>> 16; - var18 &= 65535; - if(var22 == var42) { - var43 = var40; - } else { - var43 = Class3_Sub9.method133(var18 >>> 16); - } - - if(var43 != null) { - var35 |= var43.method561(var18, (byte)115); - var37 |= var43.method559(var18); - } - } - } - - var22 = -1; - if(var3 > -63) { - this.parseOpcode(79, 73, null); - } - - var42 = -1; - Class3_Sub28_Sub5 var44 = null; - var24 = 0; - Class3_Sub28_Sub5 var46 = null; - if(var8 != null) { - var22 = var8.frames[var4]; - var27 = var22 >>> 16; - var22 &= 65535; - var44 = Class3_Sub9.method133(var27); - if(var44 != null) { - var35 |= var44.method561(var22, (byte)124); - var37 |= var44.method559(var22); - var36 |= var8.aBoolean1848; - } - - if((var8.aBoolean1846 || ClientCommands.tweeningEnabled) && var2 != -1 && var2 < var8.frames.length) { - var24 = var8.duration[var4]; - var42 = var8.frames[var2]; - var28 = var42 >>> 16; - var42 &= 65535; - if(var27 == var28) { - var46 = var44; - } else { - var46 = Class3_Sub9.method133(var42 >>> 16); - } - - if(null != var46) { - var35 |= var46.method561(var42, (byte)124); - var37 |= var46.method559(var42); - } - } - } - - Model var45 = var11.method1894(!var37, !var35, !var36); - var29 = 1; - - for(var28 = 0; var28 < var16; ++var28) { - if(aClass3_Sub28_Sub5Array3041[var28] != null) { - var45.method1887(aClass3_Sub28_Sub5Array3041[var28], anIntArray912[var28], aClass3_Sub28_Sub5Array4031[var28], Class30.anIntArray574[var28], -1 + Unsorted.anIntArray2574[var28], Class38.anIntArray664[var28], var29, aClass142Array1168[var28].aBoolean1848, this.anIntArrayArray1258[var28]); - } - - var29 <<= 1; - } - - if(var40 != null && var44 != null) { - var45.method1892(var40, var17, var43, var18, var6 + -1, var19, var44, var22, var46, var42, var9 + -1, var24, var10.aBooleanArray1855, var10.aBoolean1848 | var8.aBoolean1848); - } else if(var40 == null) { - if(null != var44) { - var45.method1880(var44, var22, var46, var42, -1 + var9, var24, var8.aBoolean1848); - } - } else { - var45.method1880(var40, var17, var43, var18, var6 + -1, var19, var10.aBoolean1848); - } - - for(var28 = 0; var16 > var28; ++var28) { - aClass3_Sub28_Sub5Array3041[var28] = null; - aClass3_Sub28_Sub5Array4031[var28] = null; - aClass142Array1168[var28] = null; - } - - if(this.anInt1264 != 128 || this.anInt1266 != 128) { - var45.resize(this.anInt1264, this.anInt1266, this.anInt1264); - } - - return var45; - } - } else { - NPCDefinition var33 = this.method1471((byte)32); - return null != var33?var33.method1476(var1, var2, (byte)-102, var4, var5, var6, var7, var8, var9, var10):null; - } - } catch (RuntimeException var32) { - throw ClientErrorException.clientError(var32, "me.M(" + (var1 != null?"{...}":"null") + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + (var8 != null?"{...}":"null") + ',' + var9 + ',' + (var10 != null?"{...}":"null") + ')'); - } - } - - final RSString method1477(int var1, RSString var2) { - try { - if(null == this.aHashTable_1272) { - return var2; - } else { - LinkableRSString var4 = (LinkableRSString)this.aHashTable_1272.get(var1); - return (null == var4?var2:var4.value); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "me.I(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - final void method1478(DataBuffer var1) { - try { - while(true) { - int var3 = var1.readUnsignedByte(); - if(var3 == 0) { - return; - } - - this.parseOpcode(27, var3, var1); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "me.F(" + (var1 != null?"{...}":"null") + ',' + 74 + ')'); - } - } - - static void method1479(int var0) { - try { - TextureOperation13.anInt3362 = -1; - - if(var0 == 37) { - NPC.aFloat3979 = 3.0F; - } else if(50 == var0) { - NPC.aFloat3979 = 4.0F; - } else if (var0 == 75) { - NPC.aFloat3979 = 6.0F; - } else if (var0 == 100) { - NPC.aFloat3979 = 8.0F; - } else if (var0 == 200) { - NPC.aFloat3979 = 16.0F; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "me.C(" + var0 + ',' + (byte) 56 + ')'); - } - } - - static void method1480(boolean var0, RSString var1) { - try { - short[] var3 = new short[16]; - var1 = var1.toLowercase(); - int var4 = 0; - - for(int var5 = 0; TextureOperation39.itemDefinitionSize > var5; ++var5) { - ItemDefinition var6 = ItemDefinition.getItemDefinition(var5); - if((!var0 || var6.aBoolean807) && var6.anInt791 == -1 && -1 == var6.anInt762 && var6.anInt800 == 0 && var6.name.toLowercase().indexOf(var1, 116) != -1) { - if(var4 >= 250) { - Class99.aShortArray1398 = null; - Unsorted.anInt952 = -1; - return; - } - - if(var3.length <= var4) { - short[] var7 = new short[2 * var3.length]; - - System.arraycopy(var3, 0, var7, 0, var4); - - var3 = var7; - } - - var3[var4++] = (short)var5; - } - } - - Class99.aShortArray1398 = var3; - Class140_Sub4.anInt2756 = 0; - Unsorted.anInt952 = var4; - RSString[] var10 = new RSString[Unsorted.anInt952]; - - for(int var11 = 0; Unsorted.anInt952 > var11; ++var11) { - var10[var11] = ItemDefinition.getItemDefinition(var3[var11]).name; - } - - TextureOperation3.method307(var10, Class99.aShortArray1398, 77); - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "me.J(" + var0 + ',' + (var1 != null?"{...}":"null") + ',' + 102 + ')'); - } - } - - final Model getChatModel(SequenceDefinition var1, int var2, int var3, int var4, int var5) { - try { - if(this.childNPCs == null) { - if(null == this.anIntArray1250) { - return null; - } else { - Model var12 = (Model)Class154.aReferenceCache_1964.get(this.npcId); - if(var12 == null) { - boolean var7 = false; - - for(int var8 = 0; this.anIntArray1250.length > var8; ++var8) { - if(!aClass153_3173.method2129((byte)-69, 0, this.anIntArray1250[var8])) { - var7 = true; - } - } - - if(var7) { - return null; - } - - Model_Sub1[] var14 = new Model_Sub1[this.anIntArray1250.length]; - - for(int var9 = 0; this.anIntArray1250.length > var9; ++var9) { - var14[var9] = Model_Sub1.method2015(aClass153_3173, this.anIntArray1250[var9]); - } - - Model_Sub1 var15; - if(var14.length == 1) { - var15 = var14[0]; - } else { - var15 = new Model_Sub1(var14, var14.length); - } - - int var10; - if(null != this.aShortArray1248) { - for(var10 = 0; var10 < this.aShortArray1248.length; ++var10) { - if(this.aByteArray1247 != null && this.aByteArray1247.length > var10) { - Objects.requireNonNull(var15).method2016(this.aShortArray1248[var10], Class136.aShortArray1779[255 & this.aByteArray1247[var10]]); - } else { - Objects.requireNonNull(var15).method2016(this.aShortArray1248[var10], this.aShortArray1254[var10]); - } - } - } - - if(this.aShortArray1271 != null) { - for(var10 = 0; this.aShortArray1271.length > var10; ++var10) { - Objects.requireNonNull(var15).method1998(this.aShortArray1271[var10], this.aShortArray1246[var10]); - } - } - - var12 = Objects.requireNonNull(var15).method2008(64, 768, -50, -10, -50); - Class154.aReferenceCache_1964.put(var12, this.npcId); - } - - if(null != var1) { - var12 = var1.method2055(var12, (byte)-75, var3, var2, var5); - } - - return var12; - } - } else { - NPCDefinition var6 = this.method1471((byte)-100); - return null == var6?null:var6.getChatModel(var1, var2, var3, 54, var5); - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "me.A(" + (var1 != null?"{...}":"null") + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - private void parseOpcode(int var1, int opcode, DataBuffer buffer) { - try { - int var4; - int var5; - if(1 == opcode) { - var4 = buffer.readUnsignedByte(); - this.models = new int[var4]; - - for(var5 = 0; var4 > var5; ++var5) { - this.models[var5] = buffer.readUnsignedShort(); - if(this.models[var5] == 65535) { - this.models[var5] = -1; - } - } - } else if(opcode == 2) { - this.NPCName = buffer.readString(); - } else if (opcode == 12) { - this.size = buffer.readUnsignedByte(); - } else if (opcode >= 30 && opcode < 35) { - this.options[-30 + opcode] = buffer.readString(); - if (this.options[-30 + opcode].equalsStringIgnoreCase(TextCore.HasHidden)) { - this.options[opcode - 30] = null; - } - } else if (opcode == 40) { - var4 = buffer.readUnsignedByte(); - this.aShortArray1254 = new short[var4]; - this.aShortArray1248 = new short[var4]; - - for (var5 = 0; var4 > var5; ++var5) { - this.aShortArray1248[var5] = (short) buffer.readUnsignedShort(); - this.aShortArray1254[var5] = (short) buffer.readUnsignedShort(); - } - } else if (opcode == 41) { - var4 = buffer.readUnsignedByte(); - this.aShortArray1246 = new short[var4]; - this.aShortArray1271 = new short[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.aShortArray1271[var5] = (short) buffer.readUnsignedShort(); - this.aShortArray1246[var5] = (short) buffer.readUnsignedShort(); - } - } else if (opcode == 42) { - var4 = buffer.readUnsignedByte(); - this.aByteArray1247 = new byte[var4]; - - for (var5 = 0; var4 > var5; ++var5) { - this.aByteArray1247[var5] = buffer.readSignedByte(); - } - } else if (opcode == 60) { - var4 = buffer.readUnsignedByte(); - this.anIntArray1250 = new int[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.anIntArray1250[var5] = buffer.readUnsignedShort(); - } - } else if (93 == opcode) { - this.aBoolean1285 = false; - } else if (opcode == 95) { - this.anInt1260 = buffer.readUnsignedShort(); - } else if (opcode == 97) { - this.anInt1264 = buffer.readUnsignedShort(); - } else if (opcode == 98) { - this.anInt1266 = buffer.readUnsignedShort(); - } else if (opcode == 99) { - this.aBoolean1263 = true; - } else if (opcode == 100) { - this.anInt1251 = buffer.readSignedByte(); - } else if (opcode == 101) { - this.anInt1282 = buffer.readSignedByte() * 5; - } else if (opcode == 102) { - this.anInt1269 = buffer.readUnsignedShort(); - } else if (103 == opcode) { - this.anInt1274 = buffer.readUnsignedShort(); - } else { - int var6; - if (106 == opcode || opcode == 118) { - this.configId = buffer.readUnsignedShort(); - var4 = -1; - if (this.configId == 65535) { - this.configId = -1; - } - - this.configFileId = buffer.readUnsignedShort(); - if (this.configFileId == 65535) { - this.configFileId = -1; - } - - if (opcode == 118) { - var4 = buffer.readUnsignedShort(); - if (var4 == 65535) { - var4 = -1; - } - } - - var5 = buffer.readUnsignedByte(); - this.childNPCs = new int[2 + var5]; - - for (var6 = 0; var6 <= var5; ++var6) { - this.childNPCs[var6] = buffer.readUnsignedShort(); - if (this.childNPCs[var6] == 65535) { - this.childNPCs[var6] = -1; - } - } - - this.childNPCs[1 + var5] = var4; - } else if (opcode == 107) { - this.aBoolean1270 = false; - } else if (opcode == 109) { - this.aBoolean1255 = false; - } else if (opcode == 111) { - this.aBoolean1249 = false; - } else if (opcode == 113) { - this.aShort1286 = (short) buffer.readUnsignedShort(); - this.aShort1256 = (short) buffer.readUnsignedShort(); - } else if (opcode == 114) { - this.aByte1287 = buffer.readSignedByte(); - this.aByte1275 = buffer.readSignedByte(); - } else if (opcode == 115) { - buffer.readUnsignedByte(); - buffer.readUnsignedByte(); - } else if (119 == opcode) { - this.aByte1267 = buffer.readSignedByte(); - } else if (121 == opcode) { - this.anIntArrayArray1261 = new int[this.models.length][]; - var4 = buffer.readUnsignedByte(); - - for (var5 = 0; var5 < var4; ++var5) { - var6 = buffer.readUnsignedByte(); - int[] var7 = this.anIntArrayArray1261[var6] = new int[3]; - var7[0] = buffer.readSignedByte(); - var7[1] = buffer.readSignedByte(); - var7[2] = buffer.readSignedByte(); - } - } else if (122 == opcode) { - this.anInt1279 = buffer.readUnsignedShort(); - } else if (opcode == 123) { - this.anInt1265 = buffer.readUnsignedShort(); - } else if (opcode == 125) { - this.aByte1268 = buffer.readSignedByte(); - } else if (126 == opcode) { - this.anInt1283 = buffer.readUnsignedShort(); - } else if (127 == opcode) { - this.renderAnimationId = buffer.readUnsignedShort(); - } else if (128 == opcode) { - buffer.readUnsignedByte(); - } else if (opcode == 134) { - this.anInt1262 = buffer.readUnsignedShort(); - if (this.anInt1262 == 65535) { - this.anInt1262 = -1; - } - - this.anInt1290 = buffer.readUnsignedShort(); - if (this.anInt1290 == 65535) { - this.anInt1290 = -1; - } - - this.anInt1293 = buffer.readUnsignedShort(); - if (this.anInt1293 == 65535) { - this.anInt1293 = -1; - } - - this.anInt1276 = buffer.readUnsignedShort(); - if (this.anInt1276 == 65535) { - this.anInt1276 = -1; - } - - this.anInt1291 = buffer.readUnsignedByte(); - } else if (opcode == 135) { - this.anInt1296 = buffer.readUnsignedByte(); - this.anInt1253 = buffer.readUnsignedShort(); - } else if (opcode == 136) { - this.anInt1289 = buffer.readUnsignedByte(); - this.anInt1278 = buffer.readUnsignedShort(); - } else if (opcode == 137) { - this.anInt1298 = buffer.readUnsignedShort(); - } else if (opcode == 249) { - var4 = buffer.readUnsignedByte(); - if (null == this.aHashTable_1272) { - var5 = Class95.method1585((byte) 109, var4); - this.aHashTable_1272 = new HashTable(var5); - } - - for (var5 = 0; var4 > var5; ++var5) { - boolean var11 = 1 == buffer.readUnsignedByte(); - int var10 = buffer.readMedium(); - Object var8; - if (var11) { - var8 = new LinkableRSString(buffer.readString()); - } else { - var8 = new LinkableInt(buffer.readInt()); - } - - this.aHashTable_1272.put(var10, (Linkable) var8); - } - } - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "me.H(" + var1 + ',' + opcode + ',' + (buffer != null?"{...}":"null") + ')'); - } - - /** - * Will find a better way to incorporate this without tacking it onto the end of NPCDefinitions at some point. - * Will *Actually* worry about it and make it 100x better when working on the new client. ~ Woah - * - * Checks to see if the Halloween Event is enabled, and then applies the "Trick-or-Treat" option to the NPC's that - * had it during the 2008 Halloween Event - */ - if (GameConfig.HALLOWEEN_EVENT_ENABLED) { - //First Options - /* - None - */ - //Second Options - if (npcId == 307 || npcId == 375 || npcId == 743 || npcId == 744 || npcId == 755 || npcId == 2634 || npcId == 2690 || npcId == 2691 || npcId == 2692) { - options[2] = TextCore.TrickorTreat; - } - //Third Options - if (npcId == 530 || npcId == 531 || npcId == 556 || npcId == 557 || npcId == 558 || npcId == 559 || npcId == 583 || npcId == 585 || npcId == 1860 || npcId == 3299 || npcId == 3671) { - options[3] = TextCore.TrickorTreat; - } - //Fourth Options - if (npcId == 922 || npcId == 970) { - options[4] = TextCore.TrickorTreat; - } - - - } - - } - - public NPCDefinition() { - this.NPCName = TextCore.aClass94_2006; - this.anInt1260 = -1; - this.aBoolean1285 = true; - this.anInt1253 = -1; - this.anInt1282 = 0; - this.anInt1283 = -1; - this.anInt1264 = 128; - this.aByte1275 = -16; - this.anInt1269 = -1; - this.aByte1267 = 0; - this.aBoolean1249 = true; - this.aShort1286 = 0; - this.anInt1289 = -1; - this.anInt1279 = -1; - this.anInt1251 = 0; - this.aBoolean1263 = false; - this.anInt1274 = 32; - this.options = new RSString[5]; - this.anInt1293 = -1; - this.aByte1287 = -96; - this.aByte1268 = 7; - this.renderAnimationId = -1; - this.anInt1296 = -1; - this.anInt1291 = 0; - this.anInt1266 = 128; - this.configId = -1; - this.anInt1290 = -1; - this.anInt1265 = -1; - this.anInt1278 = -1; - this.configFileId = -1; - this.anInt1298 = -1; - } - - static int method1484(int var1) { - try { - - CSConfigCachefile var2 = CSConfigCachefile.getCSConfigFileFromVarbitID(var1); - int var3 = Objects.requireNonNull(var2).parentVarpIndex; - int var5 = var2.upperBound; - int var4 = var2.lowerBound; - int var6 = Class3_Sub6.expectedMinimumValues[var5 + -var4]; - return ItemDefinition.ram[var3] >> var4 & var6; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "me.B(" + 64835055 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/NPCRendering.java b/Client/src/main/java/org/runite/client/NPCRendering.java deleted file mode 100644 index cd2f40404..000000000 --- a/Client/src/main/java/org/runite/client/NPCRendering.java +++ /dev/null @@ -1,341 +0,0 @@ -package org.runite.client; - -public final class NPCRendering { - - - static final int[] NPC_RENDER_LOG = new int[3]; - - static void renderNPCs(int var0) { - try { - Unsorted.maskUpdateCount = 0; - Class139.anInt1829 = 0; - renderLocalNPCs(); - NPC_RENDER_LOG[0] = BufferedDataStream.incomingBuffer.index; - addLocalNPCs(); - NPC_RENDER_LOG[1] = BufferedDataStream.incomingBuffer.index; - renderNPCMasks(); - NPC_RENDER_LOG[2] = BufferedDataStream.incomingBuffer.index; - - int var1; - for(var1 = 0; Class139.anInt1829 > var1; ++var1) { - int var2 = Class3_Sub7.anIntArray2292[var1]; - if(Class44.anInt719 != NPC.npcs[var2].anInt2838) { - if(NPC.npcs[var2].definition.method1474()) { - Class3_Sub28_Sub8.method574(NPC.npcs[var2]); - } - - NPC.npcs[var2].setDefinitions(null); - NPC.npcs[var2] = null; - } - } - - if(var0 != 8169) { - renderNPCs(96); - } - - if(Unsorted.incomingPacketLength == BufferedDataStream.incomingBuffer.index) { - for(var1 = 0; var1 < Class163.localNPCCount; ++var1) { - if(null == NPC.npcs[AudioThread.localNPCIndexes[var1]]) { - // System.err.println("gnp2 pos:" + var1 + " size:" + Class163.anInt2046); - System.err.println("Local NPC was null - index: " + AudioThread.localNPCIndexes[var1] + ", list index: " + var1 + ", list size: " + Class163.localNPCCount); - } - } - - } else { - System.err.println("NPC rendering packet size mismatch - size log: local=" + NPC_RENDER_LOG[0] + ", add global=" + NPC_RENDER_LOG[1] + ", masks=" + NPC_RENDER_LOG[2] + "."); - // System.err.println("gnp1 pos:" + GraphicDefinition.incomingBuffer.index + " psize:" + Class130.incomingPacketLength); - // throw new RuntimeException("gnp1 pos:" + Class28.incomingBuffer.index + " psize:" + Class130.incomingPacketLength); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "gm.B(" + var0 + ')'); - } - } - - static void renderLocalNPCs() { - try { - BufferedDataStream.incomingBuffer.setBitAccess(); - int var1 = BufferedDataStream.incomingBuffer.getBits(8); - int var2; - if (var1 < Class163.localNPCCount) { - for (var2 = var1; var2 < Class163.localNPCCount; ++var2) { - Class3_Sub7.anIntArray2292[Class139.anInt1829++] = AudioThread.localNPCIndexes[var2]; - } - } - - if (Class163.localNPCCount < var1) { - throw new RuntimeException("gnpov1"); - } else { - Class163.localNPCCount = 0; - - for (var2 = 0; var1 > var2; ++var2) { - int var3 = AudioThread.localNPCIndexes[var2]; - NPC var4 = NPC.npcs[var3]; - int var5 = BufferedDataStream.incomingBuffer.getBits(1); - if (0 == var5) { - AudioThread.localNPCIndexes[Class163.localNPCCount++] = var3; - var4.anInt2838 = Class44.anInt719; - } else { - int var6 = BufferedDataStream.incomingBuffer.getBits(2); - if (var6 == 0) { - AudioThread.localNPCIndexes[Class163.localNPCCount++] = var3; - var4.anInt2838 = Class44.anInt719; - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = var3; - } else { - int var7; - int var8; - if (1 == var6) { - AudioThread.localNPCIndexes[Class163.localNPCCount++] = var3; - var4.anInt2838 = Class44.anInt719; - var7 = BufferedDataStream.incomingBuffer.getBits(3); - var4.walkStep(1, (byte) 32, var7); - var8 = BufferedDataStream.incomingBuffer.getBits(1); - if (1 == var8) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = var3; - } - } else if (var6 == 2) { - AudioThread.localNPCIndexes[Class163.localNPCCount++] = var3; - var4.anInt2838 = Class44.anInt719; - if (BufferedDataStream.incomingBuffer.getBits(1) == 1) { - var7 = BufferedDataStream.incomingBuffer.getBits(3); - var4.walkStep(2, (byte) -122, var7); - var8 = BufferedDataStream.incomingBuffer.getBits(3); - var4.walkStep(2, (byte) 85, var8); - } else { - var7 = BufferedDataStream.incomingBuffer.getBits(3); - var4.walkStep(0, (byte) -80, var7); - } - - var7 = BufferedDataStream.incomingBuffer.getBits(1); - if (var7 == 1) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = var3; - } - } else if (var6 == 3) { - Class3_Sub7.anIntArray2292[Class139.anInt1829++] = var3; - } - } - } - } - - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "dm.E(" + (byte) -11 + ')'); - } - } - - static void addLocalNPCs() { - try { - while (true) { - if (BufferedDataStream.incomingBuffer.method815(Unsorted.incomingPacketLength) >= 27) { - int var1 = BufferedDataStream.incomingBuffer.getBits(15); - if (32767 != var1) { - boolean var2 = false; - if (null == NPC.npcs[var1]) { - var2 = true; - NPC.npcs[var1] = new NPC(); - } - - NPC var3 = NPC.npcs[var1]; - AudioThread.localNPCIndexes[Class163.localNPCCount++] = var1; - var3.anInt2838 = Class44.anInt719; - if (null != var3.definition && var3.definition.method1474()) { - Class3_Sub28_Sub8.method574(var3); - } - - int var4 = BufferedDataStream.incomingBuffer.getBits(1); - int var5 = Class27.anIntArray510[BufferedDataStream.incomingBuffer.getBits(3)]; - if (var2) { - var3.anInt2806 = var3.anInt2785 = var5; - } - - int var6 = BufferedDataStream.incomingBuffer.getBits(1); - if (var6 == 1) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = var1; - } - - int var7 = BufferedDataStream.incomingBuffer.getBits(5); - var3.setDefinitions(NPCDefinition.getNPCDefinition(BufferedDataStream.incomingBuffer.getBits(14))); - if (15 < var7) { - var7 -= 32; - } - - int var8 = BufferedDataStream.incomingBuffer.getBits(5); - if (15 < var8) { - var8 -= 32; - } - - var3.setSize(var3.definition.size, 2); - var3.renderAnimationId = var3.definition.renderAnimationId; - var3.anInt2779 = var3.definition.anInt1274; - if (var3.anInt2779 == 0) { - var3.anInt2785 = 0; - } - - var3.method1967(var3.getSize(), Class102.player.anIntArray2767[0] + var8, var7 + Class102.player.anIntArray2755[0], var4 == 1); - if (var3.definition.method1474()) { - Class70.method1286(var3.anIntArray2755[0], null, 0, var3, var3.anIntArray2767[0], WorldListCountry.localPlane, null); - } - continue; - } - } - - BufferedDataStream.incomingBuffer.method818(); - - return; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "wj.E(" + 113 + ')'); - } - } - - static void renderNPCMasks() { - try { - int i; - for (i = 0; Unsorted.maskUpdateCount > i; ++i) { - int mask = Class21.maskUpdateIndexes[i]; - NPC npc = NPC.npcs[mask]; - int var4 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if ((var4 & 8) != 0) { - var4 += BufferedDataStream.incomingBuffer.readUnsignedByte() << 8; - } - - int var5; - int var6; - //Ordinal: 0 Hit - if ((64 & var4) != 0) { - var5 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - npc.addHit(var6, Class44.anInt719, var5); - npc.anInt2781 = 300 + Class44.anInt719; - npc.anInt2775 = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - } - - //Ordinal: 1 Hit 2 - if ((var4 & 2) != 0) { - var5 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - var6 = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - npc.addHit(var6, Class44.anInt719, var5); - } - - //Ordinal: 2 Animation - if ((var4 & 16) != 0) { - var5 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (65535 == var5) { - var5 = -1; - } - - Unsorted.method1772(var6, var5, 39, npc); - } - - //Ordinal: 3 Face entity - if ((var4 & 4) != 0) { - npc.anInt2772 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - if (npc.anInt2772 == 65535) { - npc.anInt2772 = -1; - } - } - - //Ordinal: 4 Graphic - if (0 != (var4 & 128)) { - var5 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - if (var5 == 65535) { - var5 = -1; - } - - var6 = BufferedDataStream.incomingBuffer.readIntLE(); - boolean var7 = true; - if (var5 != -1 && npc.anInt2842 != -1 && SequenceDefinition.getAnimationDefinition(GraphicDefinition.getGraphicDefinition(var5).anInt542).forcedPriority < SequenceDefinition.getAnimationDefinition(GraphicDefinition.getGraphicDefinition(npc.anInt2842).anInt542).forcedPriority) { - var7 = false; - } - - if (var7) { - npc.anInt2842 = var5; - npc.anInt2759 = (65535 & var6) + Class44.anInt719; - npc.anInt2761 = 0; - npc.anInt2805 = 0; - npc.anInt2799 = var6 >> 16; - npc.anInt2826 = 1; - if (npc.anInt2759 > Class44.anInt719) { - npc.anInt2805 = -1; - } - - if (npc.anInt2842 != -1 && Class44.anInt719 == npc.anInt2759) { - int var8 = GraphicDefinition.getGraphicDefinition(npc.anInt2842).anInt542; - if (var8 != -1) { - SequenceDefinition var9 = SequenceDefinition.getAnimationDefinition(var8); - if (var9.frames != null) { - Unsorted.method1470(npc.zAxis, var9, npc.xAxis, false, 0); - } - } - } - } - } - - //Ordinal: 5 Transform - if ((1 & var4) != 0) { - if (npc.definition.method1474()) { - Class3_Sub28_Sub8.method574(npc); - } - - npc.setDefinitions(NPCDefinition.getNPCDefinition(BufferedDataStream.incomingBuffer.readUnsignedShortLE())); - npc.setSize(npc.definition.size, 2); - npc.renderAnimationId = npc.definition.renderAnimationId; - if (npc.definition.method1474()) { - Class70.method1286(npc.anIntArray2755[0], null, 0, npc, npc.anIntArray2767[0], WorldListCountry.localPlane, null); - } - } - - //Ordinal: 6 Force chat - if ((var4 & 32) != 0) { - npc.textSpoken = BufferedDataStream.incomingBuffer.readString(); - npc.textCycle = 100; - } - - //Ordinal: 7 - if ((256 & var4) != 0) { - var5 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - int[] var12 = new int[var5]; - int[] var13 = new int[var5]; - int[] var14 = new int[var5]; - - for (int var15 = 0; var5 > var15; ++var15) { - int var10 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - if (var10 == 65535) { - var10 = -1; - } - - var12[var15] = var10; - var13[var15] = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var14[var15] = BufferedDataStream.incomingBuffer.readUnsignedShort(); - } - - NPC.method273(var14, npc, var13, var12); - } - - //Ordinal: 8 Face location - if ((var4 & 512) != 0) { - npc.anInt2786 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - npc.anInt2762 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - } - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "ta.M(" + ')'); - } - } - - static void updateNPCAreaArray() { - try { - for (int var1 = 0; Class163.localNPCCount > var1; ++var1) { - int var2 = AudioThread.localNPCIndexes[var1]; - NPC var3 = NPC.npcs[var2]; - if (null != var3) { - Unsorted.method68(var3.definition.size, var3); - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ig.A(" + -72 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/ObjectDefinition.java b/Client/src/main/java/org/runite/client/ObjectDefinition.java deleted file mode 100644 index 349de1b7a..000000000 --- a/Client/src/main/java/org/runite/client/ObjectDefinition.java +++ /dev/null @@ -1,1091 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.rs09.client.LinkableInt; -import org.rs09.client.config.GameConfig; -import org.rs09.client.data.HashTable; -import org.rs09.client.data.ReferenceCache; - -import java.util.Objects; - -public final class ObjectDefinition { - - static ReferenceCache aReferenceCache_1401 = new ReferenceCache(500); - static Class136 aClass136_1413 = new Class136(); - static ReferenceCache aReferenceCache_1965 = new ReferenceCache(50); - private short[] aShortArray1476; - private short[] OriginalColors; - int anInt1478; - private int anInt1479; - int SizeX = 1; - private int anInt1481; - public int MapIcon; - boolean aBoolean1483 = false; - int anInt1484; - int SizeY = 1; - boolean ProjectileClipped; - private int[] configuration; - private int anInt1488; - private int anInt1489; - static boolean[] aBooleanArray1490 = new boolean[112]; - boolean aBoolean1491; - boolean aBoolean1492; - int anInt1493; - private int anInt1494 = 0; - private short[] aShortArray1495; - private int anInt1496; - static int[][][] anIntArrayArrayArray1497 = new int[4][13][13]; - boolean NotClipped; - RSString[] options; - private short aShort1500; - private HashTable aHashTable_1501; - boolean aBoolean1502 = false; - boolean aBoolean1503; - RSString name; - private byte aByte1505; - private short[] ModifiedColors; - boolean aBoolean1507; - boolean aBoolean1510; - private int anInt1511; - int anInt1512; - private byte[] aByteArray1513; - static int paramWorldID = 1; - int anInt1515; - int anInt1516; - int anInt1517; - int anInt1518; - private int[] models; - int anInt1520; - static int anInt1521 = 0; - int anInt1522; - public int[] ChildrenIds; - boolean aBoolean1525; - private int ConfigFileId; - int objectId; - int anInt1528; - int SecondInt; - boolean aBoolean1530; - int animationId; - private int ConfigId; - int WalkingFlag; - private int SecondBool; - static short aShort1535 = 320; - private boolean aBoolean1536; - boolean aBoolean1537; - int ClipType; - int[] anIntArray1539; - int anInt1540; - private boolean aBoolean1541; - boolean aBoolean1542; - - public static ObjectDefinition getObjectDefinition(int objectId) { - try { - //36873, 24065, 22418 - ObjectDefinition objdef = (ObjectDefinition) Unsorted.aReferenceCache_21.get(objectId); - if (objdef == null) { - byte[] var3 = Class85.aClass153_1171.getFile(TextureOperation36.method340(objectId), objectId & 0xFF); - objdef = new ObjectDefinition(); - objdef.objectId = objectId; - if (null != var3) { - objdef.method1692(6219, new DataBuffer(var3)); - } - - objdef.method1689(4 + -2120); - if (!Sprites.aBoolean337 && objdef.aBoolean1491) { - objdef.options = null; - } - - if (objdef.NotClipped) { - objdef.ClipType = 0; - objdef.ProjectileClipped = false; - } - - Unsorted.aReferenceCache_21.put(objdef, objectId); - } - return objdef; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "wc.D(" + 4 + ',' + objectId + ')'); - } - } - - final boolean method1684(int var2) { - try { - if (this.configuration != null) { - for (int var7 = 0; var7 < this.configuration.length; ++var7) { - if (this.configuration[var7] == var2) { - return Unsorted.aClass153_1043.method2129((byte) 72, 0, this.models[var7] & 65535); - } - } - - return true; - } else if (null == this.models) { - return true; - } else if (var2 == 10) { - boolean var4 = true; - - for (int var5 = 0; this.models.length > var5; ++var5) { - var4 &= Unsorted.aClass153_1043.method2129((byte) 71, 0, 65535 & this.models[var5]); - } - - return var4; - } else { - return true; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "pb.H(" + 115 + ',' + var2 + ')'); - } - } - - public final ObjectDefinition method1685(int var1) { - try { - if (var1 != 0) { - this.method1697(-92, 83, null, -13, null, 18, null, true, 114, 123, null, 118, 85, -116); - } - - int var2 = -1; - if (this.ConfigFileId != -1) { - var2 = NPCDefinition.method1484(this.ConfigFileId); - } else if (this.ConfigId != -1) { - var2 = ItemDefinition.ram[this.ConfigId]; - } - - if (var2 >= 0 && this.ChildrenIds.length - 1 > var2 && this.ChildrenIds[var2] != -1) { - return getObjectDefinition(this.ChildrenIds[var2]); - } else { - int var3 = this.ChildrenIds[-1 + this.ChildrenIds.length]; - return var3 == -1 ? null : getObjectDefinition(var3); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "pb.C(" + var1 + ')'); - } - } - - private Model_Sub1 method1686(int var1, int var2) { - try { - Model_Sub1 var4 = null; - boolean var5 = this.aBoolean1536; - if (var2 == 2 && 3 < var1) { - var5 = !var5; - } - - int var6; - int var7; - if (null == this.configuration) { - if (var2 != 10) { - return null; - } - - if (this.models == null) { - return null; - } - - var6 = this.models.length; - - for (var7 = 0; var6 > var7; ++var7) { - int var8 = this.models[var7]; - if (var5) { - var8 += 65536; - } - - var4 = (Model_Sub1) aReferenceCache_1401.get(var8); - if (var4 == null) { - var4 = Model_Sub1.method2015(Unsorted.aClass153_1043, var8 & 65535); - if (var4 == null) { - return null; - } - - if (var5) { - var4.method2002(); - } - - aReferenceCache_1401.put(var4, var8); - } - - if (1 < var6) { - Class164.aClass140_Sub5Array2058[var7] = var4; - } - } - - if (var6 > 1) { - var4 = new Model_Sub1(Class164.aClass140_Sub5Array2058, var6); - } - } else { - var6 = -1; - - for (var7 = 0; this.configuration.length > var7; ++var7) { - if (var2 == this.configuration[var7]) { - var6 = var7; - break; - } - } - - if (var6 == -1) { - return null; - } - - var7 = this.models[var6]; - if (var5) { - var7 += 65536; - } - - var4 = (Model_Sub1) aReferenceCache_1401.get(var7); - if (null == var4) { - var4 = Model_Sub1.method2015(Unsorted.aClass153_1043, var7 & 65535); - if (null == var4) { - return null; - } - - if (var5) { - var4.method2002(); - } - - aReferenceCache_1401.put(var4, var7); - } - } - - boolean var11; - var11 = 128 != this.anInt1479 || this.anInt1488 != 128 || 128 != this.anInt1481; - - boolean var12; - var12 = this.anInt1496 != 0 || this.anInt1511 != 0 || 0 != this.SecondBool; - - Model_Sub1 var13 = new Model_Sub1(Objects.requireNonNull(var4), var1 == 0 && !var11 && !var12, this.OriginalColors == null, null == this.aShortArray1476); - if (var2 == 4 && var1 > 3) { - var13.method2011(); - var13.method2001(45, 0, -45); - } - - var1 &= 3; - if (var1 == 1) { - var13.method1991(); - } else if (var1 == 2) { - var13.method1989(); - } else if (3 == var1) { - var13.method2018(); - } - - int var9; - if (null != this.OriginalColors) { - for (var9 = 0; this.OriginalColors.length > var9; ++var9) { - if (null != this.aByteArray1513 && this.aByteArray1513.length > var9) { - var13.method2016(this.OriginalColors[var9], TextureOperation16.aShortArray3110[255 & this.aByteArray1513[var9]]); - } else { - var13.method2016(this.OriginalColors[var9], this.ModifiedColors[var9]); - } - } - } - - if (this.aShortArray1476 != null) { - for (var9 = 0; this.aShortArray1476.length > var9; ++var9) { - var13.method1998(this.aShortArray1476[var9], this.aShortArray1495[var9]); - } - } - - if (var11) { - var13.method1994(this.anInt1479, this.anInt1488, this.anInt1481); - } - - if (var12) { - var13.method2001(this.anInt1496, this.anInt1511, this.SecondBool); - } - - return var13; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "pb.O(" + var1 + ',' + var2 + ',' + -1 + ')'); - } - } - - static void method1688(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if (var3 == null) { - } else { - var3.aClass72_2245 = null; - } - } - - final void method1689(int var1) { - try { - if (this.SecondInt == -1) { - this.SecondInt = 0; - if (null != this.models && (null == this.configuration || this.configuration[0] == 10)) { - this.SecondInt = 1; - } - - for (int var2 = 0; var2 < 5; ++var2) { - if (this.options[var2] != null) { - this.SecondInt = 1; - break; - } - } - } - - if (var1 != -2116) { - this.method1692(67, null); - } - - if (-1 == this.anInt1540) { - this.anInt1540 = this.ClipType != 0 ? 1 : 0; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "pb.D(" + var1 + ')'); - } - } - - final boolean method1690() { - try { - if (this.ChildrenIds == null) { - return this.anInt1512 != -1 || this.anIntArray1539 != null; - } else { - - for (int var2 = 0; this.ChildrenIds.length > var2; ++var2) { - if (this.ChildrenIds[var2] != -1) { - ObjectDefinition var3 = getObjectDefinition(this.ChildrenIds[var2]); - if (var3.anInt1512 != -1 || var3.anIntArray1539 != null) { - return true; - } - } - } - - return false; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "pb.F(" + 28933 + ')'); - } - } - - final int method1691(int var1, int var2, byte var3) { - try { - if (var3 <= 76) { - return -40; - } else if (this.aHashTable_1501 == null) { - return var1; - } else { - LinkableInt var4 = (LinkableInt) this.aHashTable_1501.get(var2); - return var4 != null ? var4.value : var1; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pb.N(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - final void method1692(int var1, DataBuffer var2) { - try { - while (true) { - int var3 = var2.readUnsignedByte(); - if (var3 == 0) { - if (var1 != 6219) { - this.method1696(105, -55, null, -39, 71, null, true, null, (byte) -117, false, -25); - } - - return; - } - - this.parseOpcode(var2, var3); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "pb.G(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - private void parseOpcode(DataBuffer buffer, int opcode) { - try { - - int var4; - int var5; - if (1 == opcode) { - var4 = buffer.readUnsignedByte(); - if (var4 > 0) { - if (this.models == null || Unsorted.aBoolean742) { - this.configuration = new int[var4]; - this.models = new int[var4]; - - for (var5 = 0; var4 > var5; ++var5) { - this.models[var5] = buffer.readUnsignedShort(); - this.configuration[var5] = buffer.readUnsignedByte(); - } - } else { - buffer.index += var4 * 3; - } - } - } else if (opcode == 2) { - this.name = buffer.readString(); - } else if (opcode == 5) { - var4 = buffer.readUnsignedByte(); - if (var4 > 0) { - if (null == this.models || Unsorted.aBoolean742) { - this.models = new int[var4]; - this.configuration = null; - - for (var5 = 0; var4 > var5; ++var5) { - this.models[var5] = buffer.readUnsignedShort(); - } - } else { - buffer.index += var4 * 2; - } - } - } else if (opcode == 14) { - this.SizeX = buffer.readUnsignedByte(); - } else if (opcode == 15) { - this.SizeY = buffer.readUnsignedByte(); - } else if (opcode == 17) { - this.ClipType = 0; - this.ProjectileClipped = false; - } else if (18 == opcode) { - this.ProjectileClipped = false; - } else if (opcode == 19) { - this.SecondInt = buffer.readUnsignedByte(); - } else if (opcode == 21) { - this.aByte1505 = 1; - } else if (opcode == 22) { - this.aBoolean1541 = true; - } else if (opcode == 23) { - this.aBoolean1542 = true; - } else if (opcode == 24) { - this.animationId = buffer.readUnsignedShort(); - if (this.animationId == 65535) { - this.animationId = -1; - } - } else if (opcode == 27) { - this.ClipType = 1; - } else if (28 == opcode) { - this.anInt1528 = buffer.readUnsignedByte(); - } else if (opcode == 29) { - this.anInt1494 = buffer.readSignedByte(); - } else if (39 == opcode) { - this.anInt1489 = buffer.readSignedByte() * 5; - } else if (opcode >= 30 && opcode < 35) { - this.options[opcode - 30] = buffer.readString(); - if (this.options[-30 + opcode].equalsStringIgnoreCase(TextCore.HasHidden)) { - this.options[-30 + opcode] = null; - } - } else if (opcode == 40) { - var4 = buffer.readUnsignedByte(); - this.OriginalColors = new short[var4]; - this.ModifiedColors = new short[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.OriginalColors[var5] = (short) buffer.readUnsignedShort(); - this.ModifiedColors[var5] = (short) buffer.readUnsignedShort(); - } - } else if (opcode == 41) { - var4 = buffer.readUnsignedByte(); - this.aShortArray1495 = new short[var4]; - this.aShortArray1476 = new short[var4]; - - for (var5 = 0; var4 > var5; ++var5) { - this.aShortArray1476[var5] = (short) buffer.readUnsignedShort(); - this.aShortArray1495[var5] = (short) buffer.readUnsignedShort(); - } - } else if (opcode == 42) { - var4 = buffer.readUnsignedByte(); - this.aByteArray1513 = new byte[var4]; - - for (var5 = 0; var4 > var5; ++var5) { - this.aByteArray1513[var5] = buffer.readSignedByte(); - } - } else if (opcode == 60) { - this.MapIcon = buffer.readUnsignedShort(); - } else if (opcode == 62) { - this.aBoolean1536 = true; - } else if (opcode == 64) { - this.aBoolean1525 = false; - } else if (opcode == 65) { - this.anInt1479 = buffer.readUnsignedShort(); - } else if (opcode == 66) { - this.anInt1488 = buffer.readUnsignedShort(); - } else if (opcode == 67) { - this.anInt1481 = buffer.readUnsignedShort(); - } else if (opcode == 69) { - this.WalkingFlag = buffer.readUnsignedByte(); - } else if (70 == opcode) { - this.anInt1496 = buffer.readSignedShort(); - } else if (71 == opcode) { - this.anInt1511 = buffer.readSignedShort(); - } else if (72 == opcode) { - this.SecondBool = buffer.readSignedShort(); - } else if (opcode == 73) { - this.aBoolean1483 = true; - } else if (opcode == 74) { - this.NotClipped = true; - } else if (75 == opcode) { - this.anInt1540 = buffer.readUnsignedByte(); - } else if (opcode == 77 || opcode == 92) { - var4 = -1; - this.ConfigFileId = buffer.readUnsignedShort(); - if (65535 == this.ConfigFileId) { - this.ConfigFileId = -1; - } - - this.ConfigId = buffer.readUnsignedShort(); - if (65535 == this.ConfigId) { - this.ConfigId = -1; - } - - if (92 == opcode) { - var4 = buffer.readUnsignedShort(); - if (var4 == 65535) { - var4 = -1; - } - } - - var5 = buffer.readUnsignedByte(); - this.ChildrenIds = new int[var5 - -2]; - - for (int var6 = 0; var5 >= var6; ++var6) { - this.ChildrenIds[var6] = buffer.readUnsignedShort(); - if (65535 == this.ChildrenIds[var6]) { - this.ChildrenIds[var6] = -1; - } - } - - this.ChildrenIds[1 + var5] = var4; - } else if (78 == opcode) { - this.anInt1512 = buffer.readUnsignedShort(); - this.anInt1484 = buffer.readUnsignedByte(); - } else if (opcode == 79) { - this.anInt1518 = buffer.readUnsignedShort(); - this.anInt1515 = buffer.readUnsignedShort(); - this.anInt1484 = buffer.readUnsignedByte(); - var4 = buffer.readUnsignedByte(); - this.anIntArray1539 = new int[var4]; - - for (var5 = 0; var5 < var4; ++var5) { - this.anIntArray1539[var5] = buffer.readUnsignedShort(); - } - } else if (81 == opcode) { - this.aByte1505 = 2; - this.aShort1500 = (short) (256 * buffer.readUnsignedByte()); - } else if (opcode == 82) { - this.aBoolean1530 = true; - } else if (opcode == 88) { - this.aBoolean1503 = false; - } else if (opcode == 89) { - this.aBoolean1492 = false; - } else if (90 == opcode) { - this.aBoolean1502 = true; - } else if (opcode == 91) { - this.aBoolean1491 = true; - } else if (opcode == 93) { - this.aByte1505 = 3; - this.aShort1500 = (short) buffer.readUnsignedShort(); - } else if (opcode == 94) { - this.aByte1505 = 4; - } else if (opcode == 95) { - this.aByte1505 = 5; - } else if (opcode == 96) { - this.aBoolean1507 = true; - } else if (opcode == 97) { - this.aBoolean1537 = true; - } else if (opcode == 98) { - this.aBoolean1510 = true; - } else if (opcode == 99) { - this.anInt1493 = buffer.readUnsignedByte(); - this.anInt1517 = buffer.readUnsignedShort(); - } else if (opcode == 100) { - this.anInt1520 = buffer.readUnsignedByte(); - this.anInt1522 = buffer.readUnsignedShort(); - } else if (opcode == 101) { - this.anInt1478 = buffer.readUnsignedByte(); - } else if (opcode == 102) { - this.anInt1516 = buffer.readUnsignedShort(); - } else if (249 == opcode) { - var4 = buffer.readUnsignedByte(); - if (null == this.aHashTable_1501) { - var5 = Class95.method1585((byte) 83, var4); - this.aHashTable_1501 = new HashTable(var5); - } - - for (var5 = 0; var4 > var5; ++var5) { - boolean var10 = buffer.readUnsignedByte() == 1; - int var7 = buffer.readMedium(); - Object var8; - if (var10) { - var8 = new LinkableRSString(buffer.readString()); - } else { - var8 = new LinkableInt(buffer.readInt()); - } - - this.aHashTable_1501.put(var7, (Linkable) var8); - } - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "pb.K(" + (buffer != null ? "{...}" : "null") + ',' + opcode + ',' + -80 + ')'); - } - - /** - * Will find a better way to incorporate this without tacking it onto the end of ObjectDefinitions at some point. - * Will *Actually* worry about it and make it 100x better when working on the new client. ~ Woah - * - * Checks to see if the Halloween Event is enabled, and then applies the "Gaze-into" option to the cauldron that - * had it during the 2008 Halloween Event - */ - if (GameConfig.HALLOWEEN_EVENT_ENABLED) { - if (objectId == 39233) { - options[0] = TextCore.GazeInto; - } - } - - if (GameConfig.CHRISTMAS_EVENT_ENABLED) { - //TODO: Add more christmas trees - if (objectId == 1278) { - this.models = new int[0]; - this.models = new int[]{1681}; - } - if (objectId == 1276) { - this.models = new int[0]; - this.models = new int[]{1682}; - } - } - - if (GameConfig.OBJECT_DEBUG_ENABLED) { - if (options[0] == null && options[1] == null && options[2] == null && options[3] == null && options[4] == null) { - options[0] = RSString.parse("Viewing object"); - } - } - } - - final boolean hasModels() { - try { - if (null == this.models) { - return true; - } else { - boolean var2 = true; - - for (int var3 = 0; var3 < this.models.length; ++var3) { - var2 &= Unsorted.aClass153_1043.method2129((byte) 64, 0, 65535 & this.models[var3]); - } - - return var2; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "pb.I(" + false + ')'); - } - } - - private Class140_Sub1_Sub1 method1695(int var1, boolean var2, int var4) { - try { - int var6 = this.anInt1494 + 64; - int var7 = 5 * this.anInt1489 + 768; - Class140_Sub1_Sub1 var5; - int var8; - int var12; - if (this.configuration == null) { - if (var4 != 10) { - return null; - } - - if (this.models == null) { - return null; - } - - var8 = this.models.length; - if (var8 == 0) { - return null; - } - - long var16 = 0L; - - for (int var11 = 0; var11 < var8; ++var11) { - var16 = (long) this.models[var11] + var16 * 67783L; - } - - if (var2) { - var16 = ~var16; - } - - var5 = (Class140_Sub1_Sub1) aReferenceCache_1401.get(var16); - if (null == var5) { - Model_Sub1 var17 = null; - - for (var12 = 0; var12 < var8; ++var12) { - var17 = Model_Sub1.method2015(Unsorted.aClass153_1043, this.models[var12] & 65535); - if (null == var17) { - return null; - } - - if (var8 > 1) { - Class164.aClass140_Sub5Array2058[var12] = var17; - } - } - - if (1 < var8) { - var17 = new Model_Sub1(Class164.aClass140_Sub5Array2058, var8); - } - - var5 = new Class140_Sub1_Sub1(var17, var6, var7, var2); - aReferenceCache_1401.put(var5, var16); - } - } else { - var8 = -1; - - int var9; - for (var9 = 0; this.configuration.length > var9; ++var9) { - if (var4 == this.configuration[var9]) { - var8 = var9; - break; - } - } - - if (var8 == -1) { - return null; - } - - var9 = this.models[var8]; - if (var2) { - var9 += 65536; - } - - var5 = (Class140_Sub1_Sub1) aReferenceCache_1401.get(var9); - if (null == var5) { - Model_Sub1 var10 = Model_Sub1.method2015(Unsorted.aClass153_1043, 65535 & var9); - if (null == var10) { - return null; - } - - var5 = new Class140_Sub1_Sub1(var10, var6, var7, var2); - aReferenceCache_1401.put(var5, var9); - } - } - - boolean var14 = this.aBoolean1536; - if (var4 == 2 && var1 > 3) { - var14 = !var14; - } - - boolean var15 = 128 == this.anInt1488 && this.anInt1511 == 0; - boolean var18 = var1 == 0 && 128 == this.anInt1479 && this.anInt1481 == 128 && this.anInt1496 == 0 && this.SecondBool == 0 && !var14; - Class140_Sub1_Sub1 var19 = var5.method1926(var18, var15, this.OriginalColors == null, var6 == var5.method1903(), var1 == 0 && !var14, true, var5.method1924() == var7, !var14, this.aShortArray1476 == null); - if (var14) { - var19.method1931(); - } - - if (var4 == 4 && 3 < var1) { - var19.method1932(); - var19.method1897(45, 0, -45); - } - - var1 &= 3; - if (1 == var1) { - var19.method1925(); - } else if (var1 == 2) { - var19.method1911(); - } else if (var1 == 3) { - var19.method1902(); - } - - if (null != this.OriginalColors) { - for (var12 = 0; var12 < this.OriginalColors.length; ++var12) { - var19.method1918(this.OriginalColors[var12], this.ModifiedColors[var12]); - } - } - - if (null != this.aShortArray1476) { - for (var12 = 0; var12 < this.aShortArray1476.length; ++var12) { - var19.method1916(this.aShortArray1476[var12], this.aShortArray1495[var12]); - } - } - - if (this.anInt1479 != 128 || this.anInt1488 != 128 || this.anInt1481 != 128) { - var19.resize(this.anInt1479, this.anInt1488, this.anInt1481); - } - - if (this.anInt1496 != 0 || this.anInt1511 != 0 || 0 != this.SecondBool) { - var19.method1897(this.anInt1496, this.anInt1511, this.SecondBool); - } - - if (var6 != var19.method1903()) { - var19.method1914(var6); - } - - if (var19.method1924() != var7) { - var19.method1909(var7); - } - - return var19; - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "pb.L(" + var1 + ',' + var2 + ',' + true + ',' + var4 + ')'); - } - } - - final Class136 method1696(int var1, int var2, int[][] var3, int var4, int var5, int[][] var6, boolean var7, LDIndexedSprite var8, byte var9, boolean var10, int var11) { - try { - if (var9 >= -5) { - return null; - } else { - long var12; - if (HDToolKit.highDetail) { - if (null == this.configuration) { - var12 = (this.objectId << 10) + var1; - } else { - var12 = var1 + (this.objectId << 10) - -(var4 << 3); - } - - Class136 var16 = (Class136) Unsorted.aReferenceCache_4051.get(var12); - Class140_Sub1_Sub1 var14; - LDIndexedSprite var15; - if (null == var16) { - var14 = this.method1695(var1, false, var4); - if (null == var14) { - aClass136_1413.aClass140_1777 = null; - aClass136_1413.aClass109_Sub1_1770 = null; - return aClass136_1413; - } - - if (var4 == 10 && var1 > 3) { - var14.method1876(256); - } - - if (var10) { - var15 = var14.method1933(var8); - } else { - var15 = null; - } - - var16 = new Class136(); - var16.aClass140_1777 = var14; - var16.aClass109_Sub1_1770 = var15; - Unsorted.aReferenceCache_4051.put(var16, var12); - } else { - var14 = (Class140_Sub1_Sub1) var16.aClass140_1777; - var15 = var16.aClass109_Sub1_1770; - } - - boolean var17 = this.aBoolean1541 & var7; - Class140_Sub1_Sub1 var18 = var14.method1926(3 != this.aByte1505, this.aByte1505 == 0, true, true, true, !var17, true, true, true); - if (this.aByte1505 != 0) { - var18.method1919(this.aByte1505, this.aShort1500, var14, var3, var6, var2, var5, var11); - } - - var18.method1920(this.SecondInt == 0 && !this.aBoolean1510, true, true, this.SecondInt == 0, true, false); - aClass136_1413.aClass140_1777 = var18; - var18.aBoolean3809 = var17; - aClass136_1413.aClass109_Sub1_1770 = var15; - } else { - if (this.configuration == null) { - var12 = (this.objectId << 10) + var1; - } else { - var12 = (var4 << 3) + ((this.objectId << 10) - -var1); - } - - boolean var20; - if (var7 && this.aBoolean1541) { - var12 |= Long.MIN_VALUE; - var20 = true; - } else { - var20 = false; - } - - Object var22 = Unsorted.aReferenceCache_4051.get(var12); - if (null == var22) { - Model_Sub1 var21 = this.method1686(var1, var4); - if (var21 == null) { - aClass136_1413.aClass140_1777 = null; - return aClass136_1413; - } - - var21.method2010(); - if (var4 == 10 && var1 > 3) { - var21.method2011(); - } - - if (var20) { - var21.aShort2879 = (short) (64 + this.anInt1494); - var22 = var21; - var21.aShort2876 = (short) (768 + 5 * this.anInt1489); - var21.method1997(); - } else { - var22 = new Class140_Sub1_Sub2(var21, 64 - -this.anInt1494, 5 * this.anInt1489 + 768, -50, -10, -50); - } - - Unsorted.aReferenceCache_4051.put(var22, var12); - } - - if (var20) { - var22 = ((Model_Sub1) var22).method2004(); - } - - if (0 != this.aByte1505) { - if (var22 instanceof Class140_Sub1_Sub2) { - var22 = ((Class140_Sub1_Sub2) var22).method1941(this.aByte1505, this.aShort1500, var3, var6, var2, var5, var11, true); - } else if (var22 instanceof Model_Sub1) { - var22 = ((Model_Sub1) var22).method1999(this.aByte1505, this.aShort1500, var3, var6, var2, var5, var11); - } - } - - aClass136_1413.aClass140_1777 = (GameObject) var22; - } - return aClass136_1413; - } - } catch (RuntimeException var19) { - throw ClientErrorException.clientError(var19, "pb.A(" + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + var5 + ',' + (var6 != null ? "{...}" : "null") + ',' + var7 + ',' + (var8 != null ? "{...}" : "null") + ',' + var9 + ',' + var10 + ',' + var11 + ')'); - } - } - - final Class136 method1697(int var1, int var2, LDIndexedSprite var3, int var4, SequenceDefinition var5, int var6, int[][] var7, boolean var8, int var9, int var10, int[][] var11, int var12, int var13, int var14) { - try { - if (var10 != 8308) { - this.hasModels(); - } - - long var15; - if (HDToolKit.highDetail) { - if (this.configuration == null) { - var15 = var6 + (this.objectId << 10); - } else { - var15 = (var13 << 3) + ((this.objectId << 10) - -var6); - } - - Class140_Sub1_Sub1 var23 = (Class140_Sub1_Sub1) aReferenceCache_1965.get(var15); - if (var23 == null) { - var23 = this.method1695(var6, true, var13); - if (null == var23) { - return null; - } - - var23.method1908(); - var23.method1920(false, false, false, false, false, true); - aReferenceCache_1965.put(var23, var15); - } - - boolean var19 = false; - Class140_Sub1_Sub1 var22 = var23; - if (null != var5) { - var22 = (Class140_Sub1_Sub1) var5.method2056(var12, var9, var14, var6, var23); - var19 = true; - } - - if (var13 == 10 && 3 < var6) { - if (!var19) { - var22 = (Class140_Sub1_Sub1) var22.method1890(true, true, true); - var19 = true; - } - - var22.method1876(256); - } - - if (var8) { - aClass136_1413.aClass109_Sub1_1770 = var22.method1933(var3); - } else { - aClass136_1413.aClass109_Sub1_1770 = null; - } - - if (this.aByte1505 != 0) { - if (!var19) { - var22 = (Class140_Sub1_Sub1) var22.method1890(true, true, true); - } - - var22.method1919(this.aByte1505, this.aShort1500, var23, var7, var11, var2, var4, var1); - } - - aClass136_1413.aClass140_1777 = var22; - } else { - if (this.configuration == null) { - var15 = (this.objectId << 10) + var6; - } else { - var15 = var6 + (this.objectId << 10) + (var13 << 3); - } - - Class140_Sub1_Sub2 var17 = (Class140_Sub1_Sub2) aReferenceCache_1965.get(var15); - if (var17 == null) { - Model_Sub1 var18 = this.method1686(var6, var13); - if (var18 == null) { - return null; - } - - var17 = new Class140_Sub1_Sub2(var18, 64 + this.anInt1494, this.anInt1489 * 5 + 768, -50, -10, -50); - aReferenceCache_1965.put(var17, var15); - } - - boolean var21 = false; - if (var5 != null) { - var21 = true; - var17 = (Class140_Sub1_Sub2) var5.method2054(var9, var12, var17, var6, var14); - } - - if (var13 == 10 && var6 > 3) { - if (!var21) { - var21 = true; - var17 = (Class140_Sub1_Sub2) var17.method1890(true, true, true); - } - - var17.method1876(256); - } - - if (this.aByte1505 != 0) { - if (!var21) { - var17 = (Class140_Sub1_Sub2) var17.method1890(true, true, true); - } - - var17 = var17.method1941(this.aByte1505, this.aShort1500, var7, var11, var2, var4, var1, false); - } - - aClass136_1413.aClass140_1777 = var17; - } - return aClass136_1413; - } catch (RuntimeException var20) { - throw ClientErrorException.clientError(var20, "pb.M(" + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + (var5 != null ? "{...}" : "null") + ',' + var6 + ',' + (var7 != null ? "{...}" : "null") + ',' + var8 + ',' + var9 + ',' + var10 + ',' + (var11 != null ? "{...}" : "null") + ',' + var12 + ',' + var13 + ',' + var14 + ')'); - } - } - - final RSString method1698(RSString var1, int var3) { - try { - - if (null == this.aHashTable_1501) { - return var1; - } else { - LinkableRSString var4 = (LinkableRSString) this.aHashTable_1501.get(var3); - return var4 == null ? var1 : var4.value; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pb.E(" + (var1 != null ? "{...}" : "null") + ',' + -23085 + ',' + var3 + ')'); - } - } - - public ObjectDefinition() { - this.name = RSString.parse("null"); - this.aBoolean1503 = true; - this.anInt1493 = -1; - this.anInt1515 = 0; - this.anInt1516 = -1; - this.aByte1505 = 0; - this.aBoolean1491 = false; - this.anInt1517 = -1; - this.anInt1496 = 0; - this.anInt1518 = 0; - this.MapIcon = -1; - this.aBoolean1510 = false; - this.anInt1520 = -1; - this.aShort1500 = -1; - this.anInt1481 = 128; - this.options = new RSString[5]; - this.anInt1479 = 128; - this.aBoolean1492 = true; - this.anInt1488 = 128; - this.NotClipped = false; - this.SecondInt = -1; - this.aBoolean1530 = false; - this.aBoolean1525 = true; - this.ConfigId = -1; - this.anInt1522 = -1; - this.WalkingFlag = 0; - this.ProjectileClipped = true; - this.SecondBool = 0; - this.anInt1478 = 0; - this.anInt1528 = 16; - this.aBoolean1537 = false; - this.anInt1511 = 0; - this.anInt1484 = 0; - this.anInt1489 = 0; - this.animationId = -1; - this.aBoolean1507 = false; - this.anInt1512 = -1; - this.ClipType = 2; - this.aBoolean1536 = false; - this.ConfigFileId = -1; - this.anInt1540 = -1; - this.aBoolean1541 = false; - this.aBoolean1542 = false; - } - -} diff --git a/Client/src/main/java/org/runite/client/PacketParser.java b/Client/src/main/java/org/runite/client/PacketParser.java deleted file mode 100644 index a0735f56a..000000000 --- a/Client/src/main/java/org/runite/client/PacketParser.java +++ /dev/null @@ -1,1892 +0,0 @@ -package org.runite.client; - -import org.rs09.Discord; -import org.rs09.SlayerTracker; -import org.rs09.SystemLogger; -import org.rs09.XPGainDraw; - -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -public final class PacketParser { - - static int anInt80 = 0; - static byte[][][] aByteArrayArrayArray81; - static LinkedList aLinkedList_82 = new LinkedList(); - static short aShort83 = 32767; - static RenderAnimationDefinition aClass16_84 = new RenderAnimationDefinition(); - static int anInt86 = 0; - static int anInt87 = 0; - static RSInterface aClass11_88 = null; - static int inTutorialIsland = 0; // could be boolean - public static Class3_Sub19[] aClass3_Sub19Array3694; - static long aLong3202; - static int anInt3213 = 1; - - - static int method823(int var0, int var1, int var2, int var3) { - try { - if (var2 >= -76) { - aShort83 = -91; - } - - return (8 & Unsorted.aByteArrayArrayArray113[var3][var1][var0]) == 0 ? (var3 > 0 && (Unsorted.aByteArrayArrayArray113[1][var1][var0] & 2) != 0 ? -1 + var3 : var3) : 0; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ac.G(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static void method824(long[] var0, Object[] var1, int var2) { - try { - Class134.method1809(var0.length - 1, var0, 122, 0, var1); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ac.E(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + var2 + ')'); - } - } - - static void method825(int var1) { - try { - InterfaceWidget var3 = InterfaceWidget.getWidget(1, var1); - var3.a(); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ac.D(" + (byte) 92 + ',' + var1 + ')'); - } - } - - static int method826(RSString var0, int var1) { - try { - if (var1 != -1) { - method826(null, 65); - } - - if (var0 != null) { - for (int var2 = 0; Class8.anInt104 > var2; ++var2) { - if (var0.equalsStringIgnoreCase(Class70.aClass94Array1046[var2])) { - return var2; - } - } - - } - return -1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ac.B(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ')'); - } - } - - public static boolean parseIncomingPackets() { - int nodeModelId; - if (60 == Unsorted.incomingOpcode) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - byte var69 = BufferedDataStream.incomingBuffer.readSignedNegativeByte(); - VarpHelpers.setVarp(var69, nodeModelId); - Unsorted.incomingOpcode = -1; - return true; - } else { - int counter; - RSString playerName; - if (Unsorted.incomingOpcode == 115) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - playerName = BufferedDataStream.incomingBuffer.readString(); - Object[] var71 = new Object[playerName.length() - -1]; - for (counter = playerName.length() + -1; counter >= 0; --counter) { - if (115 == playerName.charAt(counter, (byte) -45)) { - var71[1 + counter] = BufferedDataStream.incomingBuffer.readString(); - } else { - var71[1 + counter] = BufferedDataStream.incomingBuffer.readInt(); - } - } - - var71[0] = BufferedDataStream.incomingBuffer.readInt(); - Class146.updateInterfacePacketCounter(nodeModelId); - CS2Script var66 = new CS2Script(); - var66.arguments = var71; - Class43.method1065(var66); - - Unsorted.incomingOpcode = -1; - return true; - } else { - long nameAsLong; - boolean isIgnored; - int var30; - RSString var41; - if (Unsorted.incomingOpcode == 70) { - RSString message = BufferedDataStream.incomingBuffer.readString(); - if (message.endsWith(RSString.parse(":tradereq:"))) { - playerName = message.substring(0, message.indexOf(RSString.parse(":"), 65), 0); - nameAsLong = playerName.toLong(); - isIgnored = false; - - for (var30 = 0; Class3_Sub28_Sub5.anInt3591 > var30; ++var30) { - if (nameAsLong == Class114.ignores[var30]) { - isIgnored = true; - break; - } - } - - if (!isIgnored && inTutorialIsland == 0) { - BufferedDataStream.addChatMessage(playerName, 4, TextCore.HasWishToTrade, (byte) -83 + 82); - } - } else if (message.endsWith(TextCore.cmdChalReq)) { - playerName = message.substring(0, message.indexOf(RSString.parse(":"), 75), 0); - nameAsLong = playerName.toLong(); - isIgnored = false; - - for (var30 = 0; var30 < Class3_Sub28_Sub5.anInt3591; ++var30) { - if (Class114.ignores[var30] == nameAsLong) { - isIgnored = true; - break; - } - } - - if (!isIgnored && inTutorialIsland == 0) { - var41 = message.substring(1 + message.indexOf(RSString.parse(":"), 101), message.length() + -9, (byte) -83 ^ -83); - BufferedDataStream.addChatMessage(playerName, 8, var41, (byte) -83 ^ 82); - } - } else if (message.endsWith(TextCore.HasAssistRequest)) { - isIgnored = false; - playerName = message.substring(0, message.indexOf(RSString.parse(":"), 96), 0); - nameAsLong = playerName.toLong(); - - for (var30 = 0; var30 < Class3_Sub28_Sub5.anInt3591; ++var30) { - if (nameAsLong == Class114.ignores[var30]) { - isIgnored = true; - break; - } - } - - if (!isIgnored && inTutorialIsland == 0) { - BufferedDataStream.addChatMessage(playerName, 10, RSString.parse(""), -1); - } - } else if (message.endsWith(TextCore.HasClan)) { - playerName = message.substring(0, message.indexOf(TextCore.HasClan, (byte) -83 ^ -50), 0); - BufferedDataStream.addChatMessage(RSString.parse(""), 11, playerName, -1); - } else if (message.endsWith(TextCore.HasTrade)) { - playerName = message.substring(0, message.indexOf(TextCore.HasTrade, 102), 0); - if (0 == inTutorialIsland) { - BufferedDataStream.addChatMessage(RSString.parse(""), 12, playerName, -1); - } - } else if (message.endsWith(TextCore.HasAssist)) { - playerName = message.substring(0, message.indexOf(TextCore.HasAssist, 121), 0); - if (inTutorialIsland == 0) { - BufferedDataStream.addChatMessage(RSString.parse(""), 13, playerName, -1); - } - } else if (message.endsWith(TextCore.HasDuelStake)) { - isIgnored = false; - playerName = message.substring(0, message.indexOf(RSString.parse(":"), 115), 0); - nameAsLong = playerName.toLong(); - - for (var30 = 0; Class3_Sub28_Sub5.anInt3591 > var30; ++var30) { - if (nameAsLong == Class114.ignores[var30]) { - isIgnored = true; - break; - } - } - - if (!isIgnored && inTutorialIsland == 0) { - BufferedDataStream.addChatMessage(playerName, 14, RSString.parse(""), -1); - } - } else if (message.endsWith(TextCore.HasDuelFriend)) { - playerName = message.substring(0, message.indexOf(RSString.parse(":"), 118), 0); - isIgnored = false; - nameAsLong = playerName.toLong(); - - for (var30 = 0; var30 < Class3_Sub28_Sub5.anInt3591; ++var30) { - if (nameAsLong == Class114.ignores[var30]) { - isIgnored = true; - break; - } - } - - if (!isIgnored && 0 == inTutorialIsland) { - BufferedDataStream.addChatMessage(playerName, 15, RSString.parse(""), -1); - } - } else if (message.endsWith(TextCore.HasClanRequest)) { - playerName = message.substring(0, message.indexOf(RSString.parse(":"), (byte) -83 + 138), 0); - nameAsLong = playerName.toLong(); - isIgnored = false; - - for (var30 = 0; var30 < Class3_Sub28_Sub5.anInt3591; ++var30) { - if (Class114.ignores[var30] == nameAsLong) { - isIgnored = true; - break; - } - } - - if (!isIgnored && inTutorialIsland == 0) { - BufferedDataStream.addChatMessage(playerName, 16, RSString.parse(""), -1); - } - } else if (message.endsWith(TextCore.HasAllyReq)) { - playerName = message.substring(0, message.indexOf(RSString.parse(":"), (byte) -83 + 189), (byte) -83 + 83); - isIgnored = false; - nameAsLong = playerName.toLong(); - - for (var30 = 0; var30 < Class3_Sub28_Sub5.anInt3591; ++var30) { - if (nameAsLong == Class114.ignores[var30]) { - isIgnored = true; - break; - } - } - - if (!isIgnored && inTutorialIsland == 0) { - var41 = message.substring(1 + message.indexOf(RSString.parse(":"), 92), message.length() - 9, (byte) -83 ^ -83); - BufferedDataStream.addChatMessage(playerName, 21, var41, -1); - } - } else { - BufferedDataStream.addChatMessage(RSString.parse(""), 0, message, (byte) -83 + 82); - } - - Unsorted.incomingOpcode = -1; - return true; - } else { - int var19; - RSString var58; - if (Unsorted.incomingOpcode == 123) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - var58 = BufferedDataStream.incomingBuffer.readString(); - Class146.updateInterfacePacketCounter(var19); - InterfaceWidget.setWidgetText(var58, nodeModelId); - - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 230) { - Class39.currentChunkY = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - Class65.currentChunkX = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - - while (BufferedDataStream.incomingBuffer.index < Unsorted.incomingPacketLength) { - Unsorted.incomingOpcode = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class39.parseChunkPacket((byte) -82); - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (153 == Unsorted.incomingOpcode) { - Unsorted.incomingOpcode = -1; - Class65.anInt987 = 0; - return true; - } else { - int modelId; - if (Unsorted.incomingOpcode == 220) { - nodeModelId = BufferedDataStream.incomingBuffer.readIntV2(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - Class146.updateInterfacePacketCounter(modelId); - TextureOperation29.method327(var19, nodeModelId); - - Unsorted.incomingOpcode = -1; - return true; - } else { - long var2; - int clanChatIcon; - int var11; - long var29; - long var36; - if (81 == Unsorted.incomingOpcode) { - var2 = BufferedDataStream.incomingBuffer.readLong(); - BufferedDataStream.incomingBuffer.readSignedByte(); - nameAsLong = BufferedDataStream.incomingBuffer.readLong(); - var29 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var36 = BufferedDataStream.incomingBuffer.readMedium(); - clanChatIcon = BufferedDataStream.incomingBuffer.readUnsignedByte(); - boolean var63 = false; - var11 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - long var55 = (var29 << 32) + var36; - int var54 = 0; - - label1521: - while (true) { - if (100 > var54) { - if (Class163_Sub2_Sub1.aLongArray4017[var54] != var55) { - ++var54; - continue; - } - - var63 = true; - break; - } - - if (1 >= clanChatIcon) { - for (var54 = 0; var54 < Class3_Sub28_Sub5.anInt3591; ++var54) { - if (var2 == Class114.ignores[var54]) { - var63 = true; - break label1521; - } - } - } - break; - } - - if (!var63 && 0 == inTutorialIsland) { - Class163_Sub2_Sub1.aLongArray4017[MouseListeningClass.anInt1921] = var55; - MouseListeningClass.anInt1921 = (1 + MouseListeningClass.anInt1921) % 100; - RSString var61 = QuickChat.method733(var11).method555(BufferedDataStream.incomingBuffer); - if (clanChatIcon == 2 || 3 == clanChatIcon) { - MessageManager.sendGameMessage(var11, 20, var61, Objects.requireNonNull(Unsorted.method1052(nameAsLong)).longToRSString(), RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()})); - } else if (clanChatIcon == 1) { - MessageManager.sendGameMessage(var11, 20, var61, Objects.requireNonNull(Unsorted.method1052(nameAsLong)).longToRSString(), RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()})); - } else { - MessageManager.sendGameMessage(var11, 20, var61, Objects.requireNonNull(Unsorted.method1052(nameAsLong)).longToRSString(), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()); - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else { - int var6; - int chatIcon; - boolean var32; - if (Unsorted.incomingOpcode == 55) { - Class167.anInt2087 = anInt3213; - var2 = BufferedDataStream.incomingBuffer.readLong(); - if (var2 == 0) { - Class161.aClass94_2035 = null; - Unsorted.incomingOpcode = -1; - RSInterface.aClass94_251 = null; - aClass3_Sub19Array3694 = null; - Unsorted.clanSize = 0; - return true; - } else { - nameAsLong = BufferedDataStream.incomingBuffer.readLong(); - RSInterface.aClass94_251 = Unsorted.method1052(nameAsLong); - Class161.aClass94_2035 = Unsorted.method1052(var2); - Player.aByte3953 = BufferedDataStream.incomingBuffer.readSignedByte(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (255 != var6) { - Unsorted.clanSize = var6; - Class3_Sub19[] var7 = new Class3_Sub19[100]; - - for (chatIcon = 0; chatIcon < Unsorted.clanSize; ++chatIcon) { - var7[chatIcon] = new Class3_Sub19(); - var7[chatIcon].linkableKey = BufferedDataStream.incomingBuffer.readLong(); - var7[chatIcon].aClass94_2476 = Unsorted.method1052(var7[chatIcon].linkableKey); - var7[chatIcon].anInt2478 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var7[chatIcon].aByte2472 = BufferedDataStream.incomingBuffer.readSignedByte(); - var7[chatIcon].aClass94_2473 = BufferedDataStream.incomingBuffer.readString(); - if (var7[chatIcon].linkableKey == aLong3202) { - Class91.aByte1308 = var7[chatIcon].aByte2472; - } - } - - clanChatIcon = Unsorted.clanSize; - - while (clanChatIcon > 0) { - var32 = true; - --clanChatIcon; - - for (var11 = 0; var11 < clanChatIcon; ++var11) { - if (var7[var11].aClass94_2476.method1559(var7[var11 - -1].aClass94_2476) > 0) { - var32 = false; - Class3_Sub19 var9 = var7[var11]; - var7[var11] = var7[1 + var11]; - var7[var11 + 1] = var9; - } - } - - if (var32) { - break; - } - } - - aClass3_Sub19Array3694 = var7; - } - Unsorted.incomingOpcode = -1; - return true; - } - } else if (Unsorted.incomingOpcode == 164) { - nodeModelId = BufferedDataStream.incomingBuffer.readIntV1(); - Class136.aClass64_1778 = Class38.gameSignlink.method1449((byte) -83 ^ -82, nodeModelId); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 225) { - PlayerRendering.renderPlayers(); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 48) { //UNUSED BY SERVER - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - playerName = BufferedDataStream.incomingBuffer.readString(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - Class146.updateInterfacePacketCounter(nodeModelId); - InterfaceWidget.setWidgetText(playerName, modelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (232 == Unsorted.incomingOpcode) { - CS2Script.anInt3101 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class24.anInt467 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class45.anInt734 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Unsorted.incomingOpcode = -1; - return true; - } else { - RSString var56; - if (Unsorted.incomingOpcode == 44) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - if (nodeModelId == 65535) { - nodeModelId = -1; - } - - var19 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var56 = BufferedDataStream.incomingBuffer.readString(); - if (1 <= modelId && modelId <= 8) { - if (var56.equalsStringIgnoreCase(TextCore.HasNull)) { - var56 = null; - } - - Class91.aClass94Array1299[-1 + modelId] = var56; - TextureOperation35.anIntArray3328[modelId + -1] = nodeModelId; - Class1.aBooleanArray54[modelId + -1] = var19 == 0; - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 226) { - nodeModelId = BufferedDataStream.incomingBuffer.readInt(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - VarpHelpers.setVarp(nodeModelId, var19); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 21) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - modelId = BufferedDataStream.incomingBuffer.readIntLE(); - Class146.updateInterfacePacketCounter(var19); - TextureOperation4.method260(modelId, nodeModelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 145) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - Class146.updateInterfacePacketCounter(modelId); - if (var19 == 2) { - method834(); - } - ConfigInventoryDefinition.anInt3655 = nodeModelId; - TextureOperation20.method232(nodeModelId); - Class124.method1746(false, (byte) -64); - TextureOperation24.method226(ConfigInventoryDefinition.anInt3655); - - for (counter = 0; counter < 100; ++counter) { - Unsorted.aBooleanArray3674[counter] = true; - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 69) { //UNUSED BY SERVER - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - var19 = BufferedDataStream.incomingBuffer.readInt(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - Class146.updateInterfacePacketCounter(nodeModelId); - TextureOperation19.method255(modelId, var19, 1); - - Unsorted.incomingOpcode = -1; - return true; - } else if (141 == Unsorted.incomingOpcode) { //UNUSED BY SERVER - var2 = BufferedDataStream.incomingBuffer.readLong(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var56 = QuickChat.method733(modelId).method555(BufferedDataStream.incomingBuffer); - MessageManager.sendGameMessage(modelId, 19, var56, null, Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 169) { //UNUSED BY SERVER - Class162.method2204(BufferedDataStream.incomingBuffer); - Unsorted.incomingOpcode = -1; - return true; - } else if (89 == Unsorted.incomingOpcode) { //UNUSED BY SERVER - TextureOperation6.method176(-117); - BufferedDataStream.method819(); - Class36.anInt641 += 32; - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 125) {//camera rotation - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedByte(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var30 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class146.updateInterfacePacketCounter(nodeModelId); - Class164_Sub1.method2238(counter, modelId, var6, var19, var30); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 36) { - nodeModelId = BufferedDataStream.incomingBuffer.readIntV2(); - var19 = BufferedDataStream.incomingBuffer.readSignedShortLE(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - Class146.updateInterfacePacketCounter(modelId); - Class131.method1790(nodeModelId, var19); - - Unsorted.incomingOpcode = -1; - return true; - } else { - Class3_Sub1 var38; - Class3_Sub1 var47; - if (Unsorted.incomingOpcode == 9) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - var19 = BufferedDataStream.incomingBuffer.readIntLE(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - if (counter == 65535) { - counter = -1; - } - - var6 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - if (var6 == 65535) { - var6 = -1; - } - - Class146.updateInterfacePacketCounter(modelId); - for (var30 = var6; counter >= var30; ++var30) { - var36 = (long) var30 + ((long) var19 << 32); - var47 = (Class3_Sub1) Class124.aHashTable_1659.get(var36); - if (null != var47) { - var38 = new Class3_Sub1(var47.anInt2205, nodeModelId); - var47.unlink(); - } else if (var30 == -1) { - var38 = new Class3_Sub1(Objects.requireNonNull(Unsorted.getRSInterface(var19)).aClass3_Sub1_257.anInt2205, nodeModelId); - } else { - var38 = new Class3_Sub1(0, nodeModelId); - } - - Class124.aHashTable_1659.put(var36, var38); - } - - Unsorted.incomingOpcode = -1; - return true; - } else { - int var33; - if (Unsorted.incomingOpcode == 56) { //UNUSED - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - modelId = BufferedDataStream.incomingBuffer.readIntV1(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - if (modelId >> 30 == 0) { - SequenceDefinition var53; - if (modelId >> 29 != 0) { - var6 = 65535 & modelId; - NPC var62 = NPC.npcs[var6]; - if (null != var62) { - if (counter == 65535) { - counter = -1; - } - var32 = counter == -1 || -1 == var62.anInt2842 || SequenceDefinition.getAnimationDefinition(GraphicDefinition.getGraphicDefinition(var62.anInt2842).anInt542).forcedPriority <= SequenceDefinition.getAnimationDefinition(GraphicDefinition.getGraphicDefinition(counter).anInt542).forcedPriority; - - if (var32) { - var62.anInt2761 = 0; - var62.anInt2842 = counter; - var62.anInt2759 = Class44.anInt719 - -nodeModelId; - var62.anInt2805 = 0; - if (var62.anInt2759 > Class44.anInt719) { - var62.anInt2805 = -1; - } - - var62.anInt2799 = var19; - var62.anInt2826 = 1; - if (var62.anInt2842 != -1 && Class44.anInt719 == var62.anInt2759) { - var33 = GraphicDefinition.getGraphicDefinition(var62.anInt2842).anInt542; - if (var33 != -1) { - var53 = SequenceDefinition.getAnimationDefinition(var33); - if (null != var53.frames) { - Unsorted.method1470(var62.zAxis, var53, var62.xAxis, false, 0); - } - } - } - } - } - } else if (modelId >> 28 != 0) { - var6 = modelId & 65535; - Player var60; - if (var6 == Class3_Sub1.localIndex) { - var60 = Class102.player; - } else { - var60 = Unsorted.players[var6]; - } - - if (null != var60) { - if (counter == 65535) { - counter = -1; - } - var32 = counter == -1 || var60.anInt2842 == -1 || SequenceDefinition.getAnimationDefinition(GraphicDefinition.getGraphicDefinition(var60.anInt2842).anInt542).forcedPriority <= SequenceDefinition.getAnimationDefinition(GraphicDefinition.getGraphicDefinition(counter).anInt542).forcedPriority; - - if (var32) { - var60.anInt2759 = nodeModelId + Class44.anInt719; - var60.anInt2799 = var19; - var60.anInt2842 = counter; - - var60.anInt2826 = 1; - var60.anInt2761 = 0; - var60.anInt2805 = 0; - if (var60.anInt2759 > Class44.anInt719) { - var60.anInt2805 = -1; - } - - if (var60.anInt2842 != -1 && Class44.anInt719 == var60.anInt2759) { - var33 = GraphicDefinition.getGraphicDefinition(var60.anInt2842).anInt542; - if (var33 != -1) { - var53 = SequenceDefinition.getAnimationDefinition(var33); - if (null != var53.frames) { - Unsorted.method1470(var60.zAxis, var53, var60.xAxis, var60 == Class102.player, 0); - } - } - } - } - } - } - } else { - var6 = 3 & modelId >> 28; - var30 = ((modelId & 268434277) >> 14) + -Class131.anInt1716; - chatIcon = (modelId & 16383) + -Texture.anInt1152; - if (var30 >= 0 && chatIcon >= 0 && 104 > var30 && chatIcon < 104) { - chatIcon = chatIcon * 128 - -64; - var30 = 128 * var30 + 64; - Class140_Sub2 var50 = new Class140_Sub2(counter, var6, var30, chatIcon, -var19 + Class121.method1736(var6, 1, var30, chatIcon), nodeModelId, Class44.anInt719); - TextureOperation17.aLinkedList_3177.method1215(new Class3_Sub28_Sub2(var50)); - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 207) { //UNUSED - nodeModelId = BufferedDataStream.incomingBuffer.readIntV2(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - Class146.updateInterfacePacketCounter(var19); - Class114.method1708(counter + (modelId << 16), nodeModelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 38) { - BufferedDataStream.method819(); - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedByte128();//Level - var19 = BufferedDataStream.incomingBuffer.readIntV1();//Skillxp - modelId = BufferedDataStream.incomingBuffer.readUnsignedByte();//Skill ID - int gain = var19 - XPGainDraw.getLastXp()[modelId]; - XPGainDraw.addGain(gain,modelId); - XPGainDraw.getLastXp()[modelId] = var19; - Class133.anIntArray1743[modelId] = var19;//XP for Skill ID - TextureOperation17.anIntArray3185[modelId] = nodeModelId;//Level for Skill ID - Class3_Sub20.anIntArray2480[modelId] = 1; - - for (counter = 0; 98 > counter; ++counter) { - if (ItemDefinition.anIntArray781[counter] <= var19) { //Checks xp less than or equal to level 98 or 11805606 xp - Class3_Sub20.anIntArray2480[modelId] = counter + 2; - } - } - //Calculate xp till next level - //System.out.println("xp Gained: " + (ItemDefinition.anIntArray781[nodeModelId - 1] - var19)); - - - Client.anIntArray3780[Unsorted.bitwiseAnd(31, Class49.anInt815++)] = modelId; - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 104 || - 121 == Unsorted.incomingOpcode || - Unsorted.incomingOpcode == 97 || - Unsorted.incomingOpcode == 14 || - Unsorted.incomingOpcode == 202 || - Unsorted.incomingOpcode == 135 || - Unsorted.incomingOpcode == 17 || - Unsorted.incomingOpcode == 16 || - Unsorted.incomingOpcode == 240 || - Unsorted.incomingOpcode == 33 || - Unsorted.incomingOpcode == 20 || - 195 == Unsorted.incomingOpcode || - 179 == Unsorted.incomingOpcode) { - Class39.parseChunkPacket((byte) -99); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 149) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var19 = BufferedDataStream.incomingBuffer.readInt(); - Class146.updateInterfacePacketCounter(nodeModelId); - Class3_Sub31 var67 = TextureOperation23.aHashTable_3208.get(var19); - if (null != var67) { - TextureOperation19.method254(true, var67); - } - - if (null != TextureOperation27.aClass11_3087) { - Class20.method909(TextureOperation27.aClass11_3087); - TextureOperation27.aClass11_3087 = null; - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 187) { - //set camera - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - Class146.updateInterfacePacketCounter(var19); - GraphicDefinition.CAMERA_DIRECTION = nodeModelId; - Unsorted.anInt2309 = modelId; - if (Class133.anInt1753 == 2) { - Class139.anInt1823 = Unsorted.anInt2309; - TextureOperation28.anInt3315 = GraphicDefinition.CAMERA_DIRECTION; - } - - Unsorted.method1098((byte) -117); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 132) { //UNUSED - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - var6 = BufferedDataStream.incomingBuffer.readInt(); - Class146.updateInterfacePacketCounter(var19); - Unsorted.method2143((byte) -124, modelId, var6, counter, nodeModelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (112 == Unsorted.incomingOpcode) { - Class65.currentChunkX = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class39.currentChunkY = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - - for (nodeModelId = Class65.currentChunkX; nodeModelId < 8 + Class65.currentChunkX; ++nodeModelId) { - for (var19 = Class39.currentChunkY; 8 + Class39.currentChunkY > var19; ++var19) { - if (null != Class39.aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][nodeModelId][var19]) { - Class39.aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][nodeModelId][var19] = null; - Class128.method1760(var19, nodeModelId); - } - } - } - - for (Class3_Sub4 var68 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1222(); null != var68; var68 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1221()) { - if (Class65.currentChunkX <= var68.anInt2264 && 8 + Class65.currentChunkX > var68.anInt2264 && var68.anInt2248 >= Class39.currentChunkY && 8 + Class39.currentChunkY > var68.anInt2248 && var68.anInt2250 == WorldListCountry.localPlane) { - var68.anInt2259 = 0; - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 144) { - nodeModelId = BufferedDataStream.incomingBuffer.readIntV2(); - RSInterface var65 = Unsorted.getRSInterface(nodeModelId); - - for (modelId = 0; Objects.requireNonNull(var65).itemAmounts.length > modelId; ++modelId) { - var65.itemAmounts[modelId] = -1; - var65.itemAmounts[modelId] = 0; - } - - Class20.method909(var65); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 130) { - nodeModelId = BufferedDataStream.incomingBuffer.readIntLE(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - if (modelId == 65535) { - modelId = -1; - } - - Class146.updateInterfacePacketCounter(var19); - method256(-1, 1, nodeModelId, modelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 192) { - Class161.anInt2028 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 13) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedByte(); - WorldListCountry.localPlane = var19 >> 1; - Class102.player.method1981(nodeModelId, (var19 & 1) == 1, modelId); - Unsorted.incomingOpcode = -1; - return true; - } else { - int var12; - RSString var57; - RSString var64; - if (Unsorted.incomingOpcode == 62) { - var2 = BufferedDataStream.incomingBuffer.readLong(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - counter = BufferedDataStream.incomingBuffer.readUnsignedByte(); - isIgnored = true; - if (var2 < 0L) { - var2 &= Long.MAX_VALUE; - isIgnored = false; - } - - var41 = RSString.parse(""); - if (modelId > 0) { - var41 = BufferedDataStream.incomingBuffer.readString(); - } - - RSString var46 = Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString(); - - for (var33 = 0; var33 < Class8.anInt104; ++var33) { - if (var2 == Class50.aLongArray826[var33]) { - if (Unsorted.anIntArray882[var33] != modelId) { - Unsorted.anIntArray882[var33] = modelId; - if (0 < modelId) { - BufferedDataStream.addChatMessage(RSString.parse(""), 5, RSString.stringCombiner(new RSString[]{var46, TextCore.HasLoggedIn}), -1); - } - - if (modelId == 0) { - BufferedDataStream.addChatMessage(RSString.parse(""), 5, RSString.stringCombiner(new RSString[]{var46, TextCore.HasLoggedOut}), -1); - } - } - - Unsorted.aClass94Array2566[var33] = var41; - Class57.anIntArray904[var33] = counter; - var46 = null; - Unsorted.aBooleanArray73[var33] = isIgnored; - break; - } - } - - boolean var45; - if (null != var46 && 200 > Class8.anInt104) { - Class50.aLongArray826[Class8.anInt104] = var2; - Class70.aClass94Array1046[Class8.anInt104] = var46; - Unsorted.anIntArray882[Class8.anInt104] = modelId; - Unsorted.aClass94Array2566[Class8.anInt104] = var41; - Class57.anIntArray904[Class8.anInt104] = counter; - Unsorted.aBooleanArray73[Class8.anInt104] = isIgnored; - ++Class8.anInt104; - } - - Class110.anInt1472 = anInt3213; - clanChatIcon = Class8.anInt104; - - while (clanChatIcon > 0) { - --clanChatIcon; - var45 = true; - - for (var11 = 0; var11 < clanChatIcon; ++var11) { - if (CS2Script.userCurrentWorldID != Unsorted.anIntArray882[var11] && Unsorted.anIntArray882[var11 - -1] == CS2Script.userCurrentWorldID || Unsorted.anIntArray882[var11] == 0 && Unsorted.anIntArray882[var11 - -1] != 0) { - var45 = false; - var12 = Unsorted.anIntArray882[var11]; - Unsorted.anIntArray882[var11] = Unsorted.anIntArray882[var11 - -1]; - Unsorted.anIntArray882[1 + var11] = var12; - var64 = Unsorted.aClass94Array2566[var11]; - Unsorted.aClass94Array2566[var11] = Unsorted.aClass94Array2566[var11 + 1]; - Unsorted.aClass94Array2566[var11 - -1] = var64; - var57 = Class70.aClass94Array1046[var11]; - Class70.aClass94Array1046[var11] = Class70.aClass94Array1046[var11 + 1]; - Class70.aClass94Array1046[var11 - -1] = var57; - long var15 = Class50.aLongArray826[var11]; - Class50.aLongArray826[var11] = Class50.aLongArray826[var11 - -1]; - Class50.aLongArray826[var11 - -1] = var15; - int var17 = Class57.anIntArray904[var11]; - Class57.anIntArray904[var11] = Class57.anIntArray904[var11 - -1]; - Class57.anIntArray904[1 + var11] = var17; - boolean var18 = Unsorted.aBooleanArray73[var11]; - Unsorted.aBooleanArray73[var11] = Unsorted.aBooleanArray73[var11 - -1]; - Unsorted.aBooleanArray73[var11 - -1] = var18; - } - } - - if (var45) { - break; - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 160) { - if (0 == Unsorted.incomingPacketLength) { - TextureOperation32.aClass94_3353 = TextCore.HasWalkHere; - } else { - TextureOperation32.aClass94_3353 = BufferedDataStream.incomingBuffer.readString(); - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (128 == Unsorted.incomingOpcode) { //UNUSED - for (nodeModelId = 0; nodeModelId < ItemDefinition.ram.length; ++nodeModelId) { - if (ItemDefinition.ram[nodeModelId] != Class57.varpArray[nodeModelId]) { - ItemDefinition.ram[nodeModelId] = Class57.varpArray[nodeModelId]; - Class46.method1087(98, nodeModelId); - Class44.anIntArray726[Unsorted.bitwiseAnd(Class36.anInt641++, 31)] = nodeModelId; - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 154) {//camera position - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedByte(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var30 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class146.updateInterfacePacketCounter(nodeModelId); - Class3_Sub20.method390(true, var6, counter, var30, (byte) -124, modelId, var19); - - Unsorted.incomingOpcode = -1; - return true; - } else if (247 == Unsorted.incomingOpcode) { //UNUSED - var2 = BufferedDataStream.incomingBuffer.readLong(); - nameAsLong = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var29 = BufferedDataStream.incomingBuffer.readMedium(); - chatIcon = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var33 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - boolean var49 = false; - long var51 = (nameAsLong << 32) - -var29; - int var59 = 0; - - label1603: - while (true) { - if (100 > var59) { - if (Class163_Sub2_Sub1.aLongArray4017[var59] != var51) { - ++var59; - continue; - } - - var49 = true; - break; - } - - if (chatIcon <= 1) { - for (var59 = 0; Class3_Sub28_Sub5.anInt3591 > var59; ++var59) { - if (var2 == Class114.ignores[var59]) { - var49 = true; - break label1603; - } - } - } - break; - } - - if (!var49 && inTutorialIsland == 0) { - Class163_Sub2_Sub1.aLongArray4017[MouseListeningClass.anInt1921] = var51; - MouseListeningClass.anInt1921 = (1 + MouseListeningClass.anInt1921) % 100; - var64 = QuickChat.method733(var33).method555(BufferedDataStream.incomingBuffer); - if (chatIcon == 2) { - MessageManager.sendGameMessage(var33, 18, var64, null, RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()})); - } else if (1 == chatIcon) { - MessageManager.sendGameMessage(var33, 18, var64, null, RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()})); - } else { - MessageManager.sendGameMessage(var33, 18, var64, null, Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()); - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else { - Class3_Sub31 var26; - if (Unsorted.incomingOpcode == 176) { //IMISED - nodeModelId = BufferedDataStream.incomingBuffer.readIntV1(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - modelId = BufferedDataStream.incomingBuffer.readIntV1(); - Class146.updateInterfacePacketCounter(var19); - Class3_Sub31 var23 = TextureOperation23.aHashTable_3208.get(nodeModelId); - var26 = TextureOperation23.aHashTable_3208.get(modelId); - if (null != var26) { - TextureOperation19.method254(null == var23 || var26.anInt2602 != var23.anInt2602, var26); - } - - if (null != var23) { - var23.unlink(); - TextureOperation23.aHashTable_3208.put(modelId, var23); - } - - RSInterface var27 = Unsorted.getRSInterface(nodeModelId); - if (var27 != null) { - Class20.method909(var27); - } - - var27 = Unsorted.getRSInterface(modelId); - if (null != var27) { - Class20.method909(var27); - Unsorted.method2104(var27, true, 48); - } - - if (ConfigInventoryDefinition.anInt3655 != -1) { - Class3_Sub8.method124(28, 1, ConfigInventoryDefinition.anInt3655); - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 27) { //UNUSED - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedByte(); - counter = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var30 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - Class146.updateInterfacePacketCounter(nodeModelId); - WaterfallShader.aBooleanArray2169[var19] = true; - TextureOperation14.anIntArray3383[var19] = modelId; - Class166.anIntArray2073[var19] = counter; - TextureOperation3.anIntArray3359[var19] = var6; - Class163_Sub1_Sub1.anIntArray4009[var19] = var30; - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 2) { - nodeModelId = BufferedDataStream.incomingBuffer.readIntV1(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - Class146.updateInterfacePacketCounter(var19); - CSConfigCachefile.method1385(modelId, nodeModelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 85) { - Class38_Sub1.anInt2617 = BufferedDataStream.incomingBuffer.readUnsignedShort() * 30; - Unsorted.incomingOpcode = -1; - Class140_Sub6.anInt2905 = anInt3213; - return true; - } else if (Unsorted.incomingOpcode == 114) { - TextureOperation3.method305(Class38.gameSignlink, BufferedDataStream.incomingBuffer, Unsorted.incomingPacketLength); - Unsorted.incomingOpcode = -1; - return true; - } else if (65 == Unsorted.incomingOpcode) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - Class146.updateInterfacePacketCounter(nodeModelId); - TextureOperation19.method255(modelId, var19, (byte) -83 ^ -84); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 234) { - BufferedDataStream.method819(); - Unsorted.anInt136 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class140_Sub6.anInt2905 = anInt3213; - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 209) { - if (-1 != ConfigInventoryDefinition.anInt3655) { - Class3_Sub8.method124(48, 0, ConfigInventoryDefinition.anInt3655); - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 191) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - CS2Methods.method532(nodeModelId); - QuickChatDefinition.anIntArray3565[Unsorted.bitwiseAnd(31, Unsorted.anInt944++)] = Unsorted.bitwiseAnd(nodeModelId, 32767); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 102) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - var19 = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - NPC var39 = NPC.npcs[nodeModelId]; - if (null != var39) { - Unsorted.method1772(var19, modelId, 39, var39); - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 159) { - BufferedDataStream.method819(); - MouseListeningClass.anInt1925 = BufferedDataStream.incomingBuffer.readSignedShort(); - Class140_Sub6.anInt2905 = anInt3213; - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 71) { - var2 = BufferedDataStream.incomingBuffer.readLong(); - var58 = Font.method686(Objects.requireNonNull(Class32.method992(BufferedDataStream.incomingBuffer).properlyCapitalize())); - BufferedDataStream.addChatMessage(Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString(), 6, var58, (byte) -83 ^ 82); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 42) { - if (null != TextureOperation30.fullScreenFrame) { - GameObject.graphicsSettings(false, Unsorted.anInt2577, -1, -1); - } - - byte[] var22 = new byte[Unsorted.incomingPacketLength]; - BufferedDataStream.incomingBuffer.method811((byte) 30, 0, var22, Unsorted.incomingPacketLength); - playerName = TextureOperation33.bufferToString(var22, Unsorted.incomingPacketLength, 0); - if (null == GameShell.frame && (3 == Signlink.anInt1214 || !Signlink.osName.startsWith("win") || Class106.paramUserUsingInternetExplorer)) { - Class99.method1596(playerName, (byte) 127, true); - } else { - TextureOperation5.aClass94_3295 = playerName; - Unsorted.aBoolean2154 = true; - AudioThread.aClass64_351 = Class38.gameSignlink.method1452(new String(playerName.method1568(), StandardCharsets.ISO_8859_1), true); - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 111) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - var19 = BufferedDataStream.incomingBuffer.readIntV2(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - Class146.updateInterfacePacketCounter(nodeModelId); - method256(modelId, 7, var19, counter << 16 | var6); - - Unsorted.incomingOpcode = -1; - return true; - } else if (37 == Unsorted.incomingOpcode) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - VarpHelpers.setVarbit((byte) -122, nodeModelId, var19); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 155) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var19 = BufferedDataStream.incomingBuffer.readIntV2(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShort(); - Class146.updateInterfacePacketCounter(modelId); - var26 = TextureOperation23.aHashTable_3208.get(var19); - if (null != var26) { - TextureOperation19.method254(var26.anInt2602 != counter, var26); - } - - Class21.method914(counter, var19, nodeModelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 131) { - for (nodeModelId = 0; nodeModelId < Unsorted.players.length; ++nodeModelId) { - if (Unsorted.players[nodeModelId] != null) { - Unsorted.players[nodeModelId].anInt2771 = -1; - } - } - - for (nodeModelId = 0; nodeModelId < NPC.npcs.length; ++nodeModelId) { - if (null != NPC.npcs[nodeModelId]) { - NPC.npcs[nodeModelId].anInt2771 = -1; - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 217) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class96 var48 = new Class96(); - var19 = nodeModelId >> 6; - var48.anInt1360 = nodeModelId & 63; - var48.anInt1351 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (var48.anInt1351 >= 0 && Class166.aAbstractSpriteArray2072.length > var48.anInt1351) { - if (var48.anInt1360 == 1 || 10 == var48.anInt1360) { - var48.anInt1359 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - BufferedDataStream.incomingBuffer.index += 3; - } else if (var48.anInt1360 >= 2 && 6 >= var48.anInt1360) { - if (var48.anInt1360 == 2) { - var48.anInt1346 = 64; - var48.anInt1350 = 64; - } - - if (var48.anInt1360 == 3) { - var48.anInt1346 = 0; - var48.anInt1350 = 64; - } - - if (4 == var48.anInt1360) { - var48.anInt1346 = 128; - var48.anInt1350 = 64; - } - - if (5 == var48.anInt1360) { - var48.anInt1346 = 64; - var48.anInt1350 = 0; - } - - if (var48.anInt1360 == 6) { - var48.anInt1346 = 64; - var48.anInt1350 = 128; - } - - var48.anInt1360 = 2; - var48.anInt1356 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var48.anInt1347 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var48.anInt1353 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - } - - var48.anInt1355 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (var48.anInt1355 == 65535) { - var48.anInt1355 = -1; - } - - ClientErrorException.aClass96Array2114[var19] = var48; - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (126 == Unsorted.incomingOpcode) { - Class3_Sub28_Sub5.anInt3591 = Unsorted.incomingPacketLength / 8; - - for (nodeModelId = 0; Class3_Sub28_Sub5.anInt3591 > nodeModelId; ++nodeModelId) { - Class114.ignores[nodeModelId] = BufferedDataStream.incomingBuffer.readLong(); - TextureOperation7.aClass94Array3341[nodeModelId] = Unsorted.method1052(Class114.ignores[nodeModelId]); - } - - Class110.anInt1472 = anInt3213; - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 32) { - NPCRendering.renderNPCs(8169); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 119) { - //Reposition child? - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - var19 = BufferedDataStream.incomingBuffer.readIntLE(); - modelId = BufferedDataStream.incomingBuffer.readSignedShort(); - counter = BufferedDataStream.incomingBuffer.readSignedShort128(); - Class146.updateInterfacePacketCounter(nodeModelId); - RSInterface.method2271(modelId, var19, counter); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 235) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var19 = nodeModelId >> 2; - modelId = 3 & nodeModelId; - counter = Class75.anIntArray1107[var19]; - var6 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var30 = BufferedDataStream.incomingBuffer.readInt(); - if (65535 == var6) { - var6 = -1; - } - - clanChatIcon = 16383 & var30; - var33 = 16383 & var30 >> 14; - var33 -= Class131.anInt1716; - clanChatIcon -= Texture.anInt1152; - chatIcon = 3 & var30 >> 28; - Class50.method1131(chatIcon, 110, modelId, var19, clanChatIcon, counter, var33, var6); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 0) { - var2 = BufferedDataStream.incomingBuffer.readLong(); - nameAsLong = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var29 = BufferedDataStream.incomingBuffer.readMedium(); - chatIcon = BufferedDataStream.incomingBuffer.readUnsignedByte(); - boolean var42 = false; - long var35 = var29 + (nameAsLong << 32); - var12 = 0; - label1651: - while (true) { - if (var12 >= 100) { - if (chatIcon <= 1) { - if ((!Class3_Sub15.aBoolean2433 || Class121.aBoolean1641) && !TextureOperation31.aBoolean3166) { - for (var12 = 0; var12 < Class3_Sub28_Sub5.anInt3591; ++var12) { - if (Class114.ignores[var12] == var2) { - var42 = true; - break label1651; - } - } - } else { - var42 = true; - } - } - break; - } - - if (Class163_Sub2_Sub1.aLongArray4017[var12] == var35) { - var42 = true; - break; - } - - ++var12; - } - - if (!var42 && inTutorialIsland == 0) { - Class163_Sub2_Sub1.aLongArray4017[MouseListeningClass.anInt1921] = var35; - MouseListeningClass.anInt1921 = (MouseListeningClass.anInt1921 - -1) % 100; - RSString var52 = Font.method686(Objects.requireNonNull(Class32.method992(BufferedDataStream.incomingBuffer).properlyCapitalize())); - if (chatIcon == 2 || chatIcon == 3) { - BufferedDataStream.addChatMessage(RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()}), 7, var52, -1); - } else if (chatIcon == 1) { - BufferedDataStream.addChatMessage(RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()}), 7, var52, -1); - } else { - BufferedDataStream.addChatMessage(RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()}), 7, var52, -1); - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 54) {//cc messahe - var2 = BufferedDataStream.incomingBuffer.readLong(); - BufferedDataStream.incomingBuffer.readSignedByte(); - nameAsLong = BufferedDataStream.incomingBuffer.readLong(); - var29 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var36 = BufferedDataStream.incomingBuffer.readMedium(); - long var44 = (var29 << 32) + var36; - clanChatIcon = BufferedDataStream.incomingBuffer.readUnsignedByte(); - boolean var13 = false; - int var14 = 0; - - label1774: - while (true) { - if (var14 >= 100) { - if (1 >= clanChatIcon) { - if ((!Class3_Sub15.aBoolean2433 || Class121.aBoolean1641) && !TextureOperation31.aBoolean3166) { - for (var14 = 0; Class3_Sub28_Sub5.anInt3591 > var14; ++var14) { - if (Class114.ignores[var14] == var2) { - var13 = true; - break label1774; - } - } - } else { - var13 = true; - } - } - break; - } - - if (Class163_Sub2_Sub1.aLongArray4017[var14] == var44) { - var13 = true; - break; - } - - ++var14; - } - - if (!var13 && 0 == inTutorialIsland) { - Class163_Sub2_Sub1.aLongArray4017[MouseListeningClass.anInt1921] = var44; - MouseListeningClass.anInt1921 = (MouseListeningClass.anInt1921 + 1) % 100; - var57 = Font.method686(Objects.requireNonNull(Class32.method992(BufferedDataStream.incomingBuffer).properlyCapitalize())); - if (clanChatIcon == 2 || clanChatIcon == 3) { - TextureOperation1.method221(-1, var57, RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()}), Objects.requireNonNull(Unsorted.method1052(nameAsLong)).longToRSString(), 9); - } else if (clanChatIcon == 1) { - TextureOperation1.method221(-1, var57, RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()}), Objects.requireNonNull(Unsorted.method1052(nameAsLong)).longToRSString(), 9); - } else { - if (clanChatIcon == 0) - TextureOperation1.method221(-1, var57, Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString(), Objects.requireNonNull(Unsorted.method1052(nameAsLong)).longToRSString(), 9); - else - TextureOperation1.method221(-1, var57, RSString.stringCombiner(new RSString[]{RSString.parse(""), Objects.requireNonNull(Unsorted.method1052(var2)).longToRSString()}), Objects.requireNonNull(Unsorted.method1052(nameAsLong)).longToRSString(), 9); - } - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 214) { - Class39.updateSceneGraph(true); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 172) { - int soundEffectID; - int soundEffectDelay; - int soundEffectVolume; - soundEffectDelay = BufferedDataStream.incomingBuffer.readUnsignedShort(); - soundEffectID = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (soundEffectDelay == 65535) { - soundEffectDelay = -1; - } - - soundEffectVolume = BufferedDataStream.incomingBuffer.readUnsignedShort(); - AudioHandler.soundEffectHandler(soundEffectID, soundEffectDelay, soundEffectVolume); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 66) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - var19 = BufferedDataStream.incomingBuffer.readIntV1(); - Class146.updateInterfacePacketCounter(nodeModelId); - modelId = 0; - if (Class102.player.class52 != null) { - modelId = Class102.player.class52.method1163(); - } - - method256(-1, 3, var19, modelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 171) { - nodeModelId = BufferedDataStream.incomingBuffer.readIntV2(); - playerName = BufferedDataStream.incomingBuffer.readString(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - Class146.updateInterfacePacketCounter(modelId); - Unsorted.method566(playerName, nodeModelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 84) { - nodeModelId = BufferedDataStream.incomingBuffer.readIntLE(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - VarpHelpers.setVarbit((byte) -106, nodeModelId, var19); - Unsorted.incomingOpcode = -1; - return true; - } else { - RSInterface iface; - if (Unsorted.incomingOpcode == 22) { - nodeModelId = BufferedDataStream.incomingBuffer.readInt(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (nodeModelId < -70000) { - var19 += 32768; - } - - if (nodeModelId < 0) { - iface = null; - } else { - iface = Unsorted.getRSInterface(nodeModelId); - } - - for (; Unsorted.incomingPacketLength > BufferedDataStream.incomingBuffer.index; method2277(var6 + -1, counter, var30, var19, (byte) 46)) { - counter = BufferedDataStream.incomingBuffer.getSmart(); - var6 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var30 = 0; - if (var6 != 0) { - var30 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (var30 == 255) { - var30 = BufferedDataStream.incomingBuffer.readInt(); - } - } - - if (iface != null && counter >= 0 && counter < iface.itemAmounts.length) { - iface.itemAmounts[counter] = var6; - iface.itemIds[counter] = var30; - } - } - - if (iface != null) { - Class20.method909(iface); - } - - BufferedDataStream.method819(); - QuickChatDefinition.anIntArray3565[Unsorted.bitwiseAnd(Unsorted.anInt944++, 31)] = Unsorted.bitwiseAnd(32767, var19); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 24) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - Class146.updateInterfacePacketCounter(nodeModelId); - Class3_Sub28_Sub5.method560(); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 86) { - XPGainDraw.reset(); - SlayerTracker.reset(); - Discord.updatePresence("At the login screen","",""); - Class167.method2269((byte) 46); - Unsorted.incomingOpcode = -1; - return false; - } else if (116 == Unsorted.incomingOpcode) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if (BufferedDataStream.incomingBuffer.readUnsignedByte() == 0) { - TextureOperation29.aClass133Array3393[nodeModelId] = new Class133(); - } else { - --BufferedDataStream.incomingBuffer.index; - TextureOperation29.aClass133Array3393[nodeModelId] = new Class133(BufferedDataStream.incomingBuffer); - } - - Unsorted.incomingOpcode = -1; - Class121.anInt1642 = anInt3213; - return true; - } else if (Unsorted.incomingOpcode == 73) {//npc model - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - var19 = BufferedDataStream.incomingBuffer.readIntLE(); - if (nodeModelId == 65535) { - nodeModelId = -1; - } - - modelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - Class146.updateInterfacePacketCounter(modelId); - method256(-1, 2, var19, nodeModelId); - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 162) { - Class39.updateSceneGraph(false); - Unsorted.incomingOpcode = -1; - return true; - } else if (165 == Unsorted.incomingOpcode) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - if (var19 == 65535) { - var19 = -1; - } - - modelId = BufferedDataStream.incomingBuffer.readInt(); - counter = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - var6 = BufferedDataStream.incomingBuffer.readIntV1(); - if (counter == 65535) { - counter = -1; - } - - Class146.updateInterfacePacketCounter(nodeModelId); - for (var30 = counter; var30 <= var19; ++var30) { - var36 = ((long) modelId << 32) - -((long) var30); - var47 = (Class3_Sub1) Class124.aHashTable_1659.get(var36); - if (var47 == null) { - if (-1 == var30) { - var38 = new Class3_Sub1(var6, Objects.requireNonNull(Unsorted.getRSInterface(modelId)).aClass3_Sub1_257.anInt2202); - } else { - var38 = new Class3_Sub1(var6, -1); - } - } else { - var38 = new Class3_Sub1(var6, var47.anInt2202); - var47.unlink(); - } - - Class124.aHashTable_1659.put(var36, var38); - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 197) { - CS2Script.anInt1357 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Class110.anInt1472 = anInt3213; - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 196) { - var2 = BufferedDataStream.incomingBuffer.readLong(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShort(); - byte var28 = BufferedDataStream.incomingBuffer.readSignedByte(); - isIgnored = (Long.MIN_VALUE & var2) != 0; - - if (isIgnored) { - if (Unsorted.clanSize == 0) { - Unsorted.incomingOpcode = -1; - return true; - } - - var2 &= Long.MAX_VALUE; - - for (var30 = 0; var30 < Unsorted.clanSize && (var2 != aClass3_Sub19Array3694[var30].linkableKey || aClass3_Sub19Array3694[var30].anInt2478 != modelId); ++var30) { - } - - if (var30 < Unsorted.clanSize) { - while (var30 < -1 + Unsorted.clanSize) { - aClass3_Sub19Array3694[var30] = aClass3_Sub19Array3694[1 + var30]; - ++var30; - } - - --Unsorted.clanSize; - aClass3_Sub19Array3694[Unsorted.clanSize] = null; - } - } else { - var41 = BufferedDataStream.incomingBuffer.readString(); - Class3_Sub19 var40 = new Class3_Sub19(); - var40.linkableKey = var2; - var40.aClass94_2476 = Unsorted.method1052(var40.linkableKey); - var40.aByte2472 = var28; - var40.aClass94_2473 = var41; - var40.anInt2478 = modelId; - - for (var33 = -1 + Unsorted.clanSize; var33 >= 0; --var33) { - clanChatIcon = aClass3_Sub19Array3694[var33].aClass94_2476.method1559(var40.aClass94_2476); - if (clanChatIcon == 0) { - aClass3_Sub19Array3694[var33].anInt2478 = modelId; - aClass3_Sub19Array3694[var33].aByte2472 = var28; - aClass3_Sub19Array3694[var33].aClass94_2473 = var41; - if (aLong3202 == var2) { - Class91.aByte1308 = var28; - } - - Class167.anInt2087 = anInt3213; - Unsorted.incomingOpcode = -1; - return true; - } - - if (clanChatIcon < 0) { - break; - } - } - - if (aClass3_Sub19Array3694.length <= Unsorted.clanSize) { - Unsorted.incomingOpcode = -1; - return true; - } - - for (clanChatIcon = Unsorted.clanSize + -1; clanChatIcon > var33; --clanChatIcon) { - aClass3_Sub19Array3694[1 + clanChatIcon] = aClass3_Sub19Array3694[clanChatIcon]; - } - - if (Unsorted.clanSize == 0) { - aClass3_Sub19Array3694 = new Class3_Sub19[100]; - } - - aClass3_Sub19Array3694[1 + var33] = var40; - if (aLong3202 == var2) { - Class91.aByte1308 = var28; - } - - ++Unsorted.clanSize; - } - - Unsorted.incomingOpcode = -1; - Class167.anInt2087 = anInt3213; - return true; - } else if (50 == Unsorted.incomingOpcode) { - nodeModelId = BufferedDataStream.incomingBuffer.readInt(); - var19 = BufferedDataStream.incomingBuffer.readIntV2(); - modelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - if (65535 == modelId) { - modelId = -1; - } - - counter = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - Class146.updateInterfacePacketCounter(counter); - RSInterface var34 = Unsorted.getRSInterface(var19); - ItemDefinition var43; - if (Objects.requireNonNull(var34).usingScripts) { - Class140_Sub6.method2026(var19, nodeModelId, modelId); - var43 = ItemDefinition.getItemDefinition(modelId); - Unsorted.method2143((byte) -128, var43.anInt810, var19, var43.anInt799, var43.anInt786); - Class84.method1420(var19, var43.anInt768, var43.anInt754, var43.anInt792); - } else { - if (-1 == modelId) { - var34.modelType = 0; - Unsorted.incomingOpcode = -1; - return true; - } - - var43 = ItemDefinition.getItemDefinition(modelId); - var34.anInt182 = var43.anInt786; - var34.anInt164 = 100 * var43.anInt810 / nodeModelId; - var34.modelType = 4; - var34.itemId = modelId; - var34.anInt308 = var43.anInt799; - Class20.method909(var34); - } - - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 105) { - nodeModelId = BufferedDataStream.incomingBuffer.readInt(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (nodeModelId < -70000) { - var19 += 32768; - } - - if (0 <= nodeModelId) { - iface = Unsorted.getRSInterface(nodeModelId); - } else { - iface = null; - } - - if (iface != null) { - for (counter = 0; iface.itemAmounts.length > counter; ++counter) { - iface.itemAmounts[counter] = 0; - iface.itemIds[counter] = 0; - } - } - - CS2Methods.method852(var19); - counter = BufferedDataStream.incomingBuffer.readUnsignedShort(); - - for (var6 = 0; counter > var6; ++var6) { - var30 = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - if (255 == var30) { - var30 = BufferedDataStream.incomingBuffer.readInt(); - } - - chatIcon = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (null != iface && iface.itemAmounts.length > var6) { - iface.itemAmounts[var6] = chatIcon; - iface.itemIds[var6] = var30; - } - - method2277(-1 + chatIcon, var6, var30, var19, (byte) 41); - } - - if (iface != null) { - Class20.method909(iface); - } - - BufferedDataStream.method819(); - QuickChatDefinition.anIntArray3565[Unsorted.bitwiseAnd(Unsorted.anInt944++, 31)] = Unsorted.bitwiseAnd(32767, var19); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 142) { - LinkableRSString.method734(BufferedDataStream.incomingBuffer.readString()); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 26) { - Class65.currentChunkX = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - Class39.currentChunkY = BufferedDataStream.incomingBuffer.readUnsignedByte(); - Unsorted.incomingOpcode = -1; - return true; - } else if (4 == Unsorted.incomingOpcode) { - nodeModelId = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - if (nodeModelId == 65535) { - nodeModelId = -1; - } - - AudioHandler.musicHandler(nodeModelId); - Unsorted.incomingOpcode = -1; - return true; - } else if (Unsorted.incomingOpcode == 208) { - nodeModelId = BufferedDataStream.incomingBuffer.getTriByte2(); - var19 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - if (var19 == 65535) { - var19 = -1; - } - - AudioHandler.musicEffectHandler(var19); - Unsorted.incomingOpcode = -1; - return true; - } else { - Class49.reportError("T1 - " + Unsorted.incomingOpcode + "," + Class7.anInt2166 + "," + Class24.anInt469 + " - " + Unsorted.incomingPacketLength, null); - Class167.method2269((byte) 46); - return true; - } - } - } - } - } - } - } - } - } - } - } - } - } - } - - static void method829() { - try { - Class20.method909(Class56.aClass11_886); - ++Class75_Sub3.anInt2658; - if (Class21.aBoolean440 && Class85.aBoolean1167) { - int var1 = Class126.anInt1676; - var1 -= Unsorted.anInt1881; - if (TextureOperation20.anInt3156 > var1) { - var1 = TextureOperation20.anInt3156; - } - - int var2 = Unsorted.anInt1709; - if (var1 - -Class56.aClass11_886.width > TextureOperation20.anInt3156 + aClass11_88.width) { - var1 = -Class56.aClass11_886.width + TextureOperation20.anInt3156 + aClass11_88.width; - } - - var2 -= Class95.anInt1336; - if (Class134.anInt1761 > var2) { - var2 = Class134.anInt1761; - } - - if (Class134.anInt1761 - -aClass11_88.height < var2 - -Class56.aClass11_886.height) { - var2 = Class134.anInt1761 + aClass11_88.height + -Class56.aClass11_886.height; - } - - int var4 = var2 - Class3_Sub2.anInt2218; - int var3 = var1 + -Class3_Sub15.anInt2421; - int var6 = var1 + -TextureOperation20.anInt3156 + aClass11_88.anInt247; - int var7 = aClass11_88.anInt208 + -Class134.anInt1761 + var2; - int var5 = Class56.aClass11_886.anInt214; - if (Class56.aClass11_886.anInt179 < Class75_Sub3.anInt2658 && (var3 > var5 || var3 < -var5 || var4 > var5 || var4 < -var5)) { - NPC.aBoolean3975 = true; - } - - CS2Script var8; - if (Class56.aClass11_886.anObjectArray295 != null && NPC.aBoolean3975) { - var8 = new CS2Script(); - var8.aClass11_2449 = Class56.aClass11_886; - var8.arguments = Class56.aClass11_886.anObjectArray295; - var8.worldSelectCursorPositionX = var6; - var8.scrollbarScrollAmount = var7; - Class43.method1065(var8); - } - - if (0 == TextureOperation21.anInt3069) { - if (NPC.aBoolean3975) { - if (Class56.aClass11_886.anObjectArray229 != null) { - var8 = new CS2Script(); - var8.scrollbarScrollAmount = var7; - var8.aClass11_2438 = Class27.aClass11_526; - var8.worldSelectCursorPositionX = var6; - var8.arguments = Class56.aClass11_886.anObjectArray229; - var8.aClass11_2449 = Class56.aClass11_886; - Class43.method1065(var8); - } - RSInterface withInter = Class27.aClass11_526; - if (withInter == null) { - withInter = Class56.aClass11_886; - } - TextureOperation12.outgoingBuffer.putOpcode(79); - TextureOperation12.outgoingBuffer.writeIntV2(Class56.aClass11_886.componentHash); - TextureOperation12.outgoingBuffer.writeShortLE(withInter.anInt191); - TextureOperation12.outgoingBuffer.writeInt(withInter.componentHash); - TextureOperation12.outgoingBuffer.writeShortLE(Class56.aClass11_886.anInt191); - - // && client.method42(Class56.aClass11_886) != null) { - if (Class27.aClass11_526 == null) { - System.out.println("Could not send switch item packet, second interface is null!"); - } else if (Client.method42(Class56.aClass11_886) != null) { - System.out.println("Shouldn't be sending packet, enabled to fix banking tabs though."); - } - } else if ((Unsorted.anInt998 == 1 || TextureOperation8.method353(-1 + Unsorted.menuOptionCount, ~-1)) && Unsorted.menuOptionCount > 2) { - Class132.method1801(); - } else if (Unsorted.menuOptionCount > 0) { - TextureOperation9.method203(96); - } - - Class56.aClass11_886 = null; - } - - } else if (Class75_Sub3.anInt2658 > 1) { - Class56.aClass11_886 = null; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "ac.F(" + -1 + ')'); - } - } - - static void method256(int var0, int var1, int var2, int var4) { - try { - InterfaceWidget var5 = InterfaceWidget.getWidget(4, var2); - var5.flagUpdate(); - var5.anInt3597 = var4; - var5.anInt3596 = var0; - var5.anInt3598 = var1; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ke.E(" + var0 + ',' + var1 + ',' + var2 + ',' + var4 + ')'); - } - } - - static void method1605(RSString var1, int var2) { - try { - TextureOperation12.outgoingBuffer.putOpcode(188); - TextureOperation12.outgoingBuffer.writeByte128(var2); - TextureOperation12.outgoingBuffer.writeLong(var1.toLong()); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ni.B(" + 255 + ',' + (var1 != null ? "{...}" : "null") + ',' + var2 + ')'); - } - } - - static void method834() { - try { - Unsorted.method1250(43, false); - System.gc(); - Class117.method1719(25); - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "af.D(" + (byte) -86 + ')'); - } - } - - static void method2277(int var0, int var1, int var2, int var3, byte var4) { - try { - Class3_Sub25 var5 = (Class3_Sub25) Class3_Sub2.aHashTable_2220.get(var3); - if (var5 == null) { - var5 = new Class3_Sub25(); - Class3_Sub2.aHashTable_2220.put(var3, var5); - } - - if (var4 > 16) { - if (var1 >= var5.anIntArray2547.length) { - int[] var6 = new int[var1 - -1]; - int[] var7 = new int[1 + var1]; - - int var8; - for (var8 = 0; var8 < var5.anIntArray2547.length; ++var8) { - var6[var8] = var5.anIntArray2547[var8]; - var7[var8] = var5.anIntArray2551[var8]; - } - - for (var8 = var5.anIntArray2547.length; var1 > var8; ++var8) { - var6[var8] = -1; - var7[var8] = 0; - } - - var5.anIntArray2547 = var6; - var5.anIntArray2551 = var7; - } - - var5.anIntArray2547[var1] = var0; - var5.anIntArray2551[var1] = var2; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "wl.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/PcmSound.java b/Client/src/main/java/org/runite/client/PcmSound.java deleted file mode 100644 index f29e51b7b..000000000 --- a/Client/src/main/java/org/runite/client/PcmSound.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.runite.client; - -public final class PcmSound extends Sound { - - public byte[] samples; - boolean aBoolean3031; - int anInt3032; - int anInt3033; - public int frequency; - - - final PcmSound method151(Class157 var1) { - this.samples = var1.method2173(this.samples); - this.frequency = var1.method2177(this.frequency); - if(this.anInt3033 == this.anInt3032) { - this.anInt3033 = this.anInt3032 = var1.method2178(this.anInt3033); - } else { - this.anInt3033 = var1.method2178(this.anInt3033); - this.anInt3032 = var1.method2178(this.anInt3032); - if(this.anInt3033 == this.anInt3032) { - --this.anInt3033; - } - } - - return this; - } - - PcmSound(byte[] var2, int var3, int var4) { - this.frequency = 22050; - this.samples = var2; - this.anInt3033 = var3; - this.anInt3032 = var4; - } - - PcmSound(int var1, byte[] var2, int var3, int var4, boolean var5) { - this.frequency = var1; - this.samples = var2; - this.anInt3033 = var3; - this.anInt3032 = var4; - this.aBoolean3031 = var5; - } -} diff --git a/Client/src/main/java/org/runite/client/Player.java b/Client/src/main/java/org/runite/client/Player.java deleted file mode 100644 index 4920699c1..000000000 --- a/Client/src/main/java/org/runite/client/Player.java +++ /dev/null @@ -1,460 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -public final class Player extends Class140_Sub4 { - - static int[] anIntArray3951 = new int[4]; - static int rights = 0; - static byte aByte3953; - static int[] anIntArray3954 = new int[100]; - static int[] anIntArray3959 = new int[2]; - int anInt3952 = -1; - int headIcon = -1; - public int teamId = 0; - int COMBAT_LEVEL = 0; - Class52 class52; - int anInt3963 = -1; - int combatLevel = 0; - int anInt3966 = -1; - public RSString displayName; - boolean aBoolean3968 = false; - int anInt3969 = 0; - int anInt3970 = -1; - int skullIcon = -1; - int anInt3973 = -1; - int anInt3974 = 0; - private int anInt3958 = 0; - - static RSString combatLevelColor(int otherPlayer, byte levelByte, int yourPlayer) { - try { - int playerLevelDiff = -otherPlayer + yourPlayer; - if (levelByte > -52) - return null; - if (playerLevelDiff < -9) - return ColorCore.LvlDiffN9;//Solid Red - if (playerLevelDiff < -6) - return ColorCore.LvlDiffN6;//Dark Orange - if (playerLevelDiff < -3) - return ColorCore.LvlDiffN3;//Orange - if (playerLevelDiff < 0) - return ColorCore.LvlDiffN0;//Yellow-Orange - if (playerLevelDiff > 9) - return ColorCore.LvlDiffP9;//Bright Green - if (playerLevelDiff > 6) - return ColorCore.LvlDiffP6;//Green - if (playerLevelDiff > 3) - return ColorCore.LvlDiffP3;//Yellow-Green - if (playerLevelDiff > 0) - return ColorCore.LvlDiffP0;//Yellow - - return ColorCore.LvlDiffDefault;//Yellow - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "jj.E(" + otherPlayer + ',' + levelByte + ',' + yourPlayer + ')'); - } - } - - /** - * Gets the properly colored string for the combat level difference. - * - * @param otherPlayer Their combat level - * @param yourPlayer Your combat level - * @returns the RSString color - */ - - static RSString getCombatLevelDifferenceColor(int otherPlayer, int yourPlayer) { - int playerLevelDiff = -otherPlayer + yourPlayer; - if (playerLevelDiff < -9) - return ColorCore.LvlDiffN9;//Solid Red - if (playerLevelDiff < -6) - return ColorCore.LvlDiffN6;//Dark Orange - if (playerLevelDiff < -3) - return ColorCore.LvlDiffN3;//Orange - if (playerLevelDiff < 0) - return ColorCore.LvlDiffN0;//Yellow-Orange - if (playerLevelDiff > 9) - return ColorCore.LvlDiffP9;//Bright Green - if (playerLevelDiff > 6) - return ColorCore.LvlDiffP6;//Green - if (playerLevelDiff > 3) - return ColorCore.LvlDiffP3;//Yellow-Green - if (playerLevelDiff > 0) - return ColorCore.LvlDiffP0;//Yellow - return ColorCore.LvlDiffDefault;//Yellow - } - - - final int getSize() { - try { - if (null == this.class52 || this.class52.pnpcId == -1) { - - return super.getSize(); - } else { - return NPCDefinition.getNPCDefinition(this.class52.pnpcId).size; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "e.H(" + (byte) 114 + ')'); - } - } - - final int getRenderAnimationId() { - try { - - return this.renderAnimationId; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "e.B(" + -1 + ')'); - } - } - - final void parseAppearance(int var1, DataBuffer buffer) { - try { - buffer.index = 0; - int var3 = buffer.readUnsignedByte(); - int npcId = -1; - int var4 = 1 & var3; - boolean var6 = (var3 & 4) != 0; - int var7 = super.getSize(); - int[] look = new int[12]; - this.setSize(1 + (var3 >> 3 & 7), 2); - this.anInt3958 = 3 & var3 >> 6; - this.xAxis += (-var7 + this.getSize()) * 64; - this.zAxis += 64 * (this.getSize() + -var7); - this.skullIcon = buffer.readSignedByte(); - this.headIcon = buffer.readSignedByte(); - this.teamId = 0; - - int var11; - int var12; - int outfit; - int var14; - for (int var10 = 0; var10 < 12; ++var10) { - var11 = buffer.readUnsignedByte(); - if (var11 == 0) { - look[var10] = 0; - } else { - var12 = buffer.readUnsignedByte(); - outfit = (var11 << 8) + var12; - if (var10 == 0 && outfit == 65535) { - npcId = buffer.readUnsignedShort(); - this.teamId = buffer.readUnsignedByte(); - break; - } - - if (32768 <= outfit) { - int equipId = outfit - 32768; - if (equipId > Class75_Sub4.anIntArray2664.length) { - System.err.println("Player->parseAppearance()-> Array length = " + Class75_Sub4.anIntArray2664.length + ", equipId=" + equipId + ", item def size=" + TextureOperation39.itemDefinitionSize); - continue; - } - outfit = Class75_Sub4.anIntArray2664[equipId]; - look[var10] = TextureOperation3.bitwiseOr(1073741824, outfit); - var14 = ItemDefinition.getItemDefinition(outfit).teamId; - if (var14 != 0) { - this.teamId = var14; - } - } else { - look[var10] = TextureOperation3.bitwiseOr(-256 + outfit, Integer.MIN_VALUE); - } - } - } - - int[] colors = new int[5]; - - for (var11 = 0; var11 < 5; ++var11) { - var12 = buffer.readUnsignedByte(); - if (var12 < 0 || var12 >= AudioThread.aShortArrayArray344[var11].length) { - var12 = 0; - } - - colors[var11] = var12; - } - - this.renderAnimationId = buffer.readUnsignedShort(); - long var20 = buffer.readLong(); - this.displayName = Objects.requireNonNull(Unsorted.method1052(var20)).longToRSString(); - this.COMBAT_LEVEL = buffer.readUnsignedByte(); - if (var6) { - this.anInt3974 = buffer.readUnsignedShort(); - this.combatLevel = this.COMBAT_LEVEL; - this.anInt3970 = -1; - } else { - this.anInt3974 = 0; - this.combatLevel = buffer.readUnsignedByte(); - this.anInt3970 = buffer.readUnsignedByte(); - if (this.anInt3970 == 255) { - this.anInt3970 = -1; - } - } - - outfit = this.anInt3969; - this.anInt3969 = buffer.readUnsignedByte(); - if (this.anInt3969 == 0) { - Class162.method2203(this); - } else { - int var15 = this.anInt3966; - int var16 = this.anInt3963; - int var17 = this.anInt3973; - var14 = this.anInt3952; - this.anInt3952 = buffer.readUnsignedShort(); - this.anInt3966 = buffer.readUnsignedShort(); - this.anInt3963 = buffer.readUnsignedShort(); - this.anInt3973 = buffer.readUnsignedShort(); - if (this.anInt3969 != outfit || var14 != this.anInt3952 || var15 != this.anInt3966 || var16 != this.anInt3963 || var17 != this.anInt3973) { - Unsorted.method518(this); - } - } - - if (null == this.class52) { - this.class52 = new Class52(); - } - - var14 = this.class52.pnpcId; - this.class52.method1161(colors, npcId, var4 == 1, look, this.renderAnimationId); - if (npcId != var14) { - this.xAxis = 128 * this.anIntArray2767[0] + this.getSize() * 64; - this.zAxis = 128 * this.anIntArray2755[0] - -(64 * this.getSize()); - } - } catch (RuntimeException var18) { - throw ClientErrorException.clientError(var18, "e.P(" + var1 + ',' + (buffer != null ? "{...}" : "null") + ')'); - } - } - - final void animate(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, long var9, int var11, Class127_Sub1 var12) { - try { - if (this.class52 != null) { - SequenceDefinition var13 = this.anInt2771 != -1 && 0 == this.anInt2828 ? SequenceDefinition.getAnimationDefinition(this.anInt2771) : null; - SequenceDefinition var14 = this.anInt2764 != -1 && !this.aBoolean3968 && (this.getRenderAnimationType().stand_animation != this.anInt2764 || var13 == null) ? SequenceDefinition.getAnimationDefinition(this.anInt2764) : null; - Model var15 = this.class52.method1165(this.aClass145Array2809, this.anInt2776, var14, var13, this.anInt2802, this.anInt2793, -120, this.anInt2760, this.anInt2832, this.anInt2813); - int var16 = Unsorted.method1727((byte) 123); - if (HDToolKit.highDetail && Class3_Sub24_Sub3.maxClientMemory < 96 && var16 > 50) { - Class3_Sub1.method90(1); - } - - int var17; - if (0 != TextureOperation20.paramModeWhat && var16 < 50) { - for (var17 = 50 - var16; Class56.anInt893 < var17; ++Class56.anInt893) { - Class3_Sub6.softReferenceTestArray[Class56.anInt893] = new byte[102400]; - } - - while (Class56.anInt893 > var17) { - --Class56.anInt893; - Class3_Sub6.softReferenceTestArray[Class56.anInt893] = null; - } - } - - if (var15 != null) { - this.anInt2820 = var15.method1871(); - Model var23; - if (Class140_Sub6.aBoolean2910 && (-1 == this.class52.pnpcId || NPCDefinition.getNPCDefinition(this.class52.pnpcId).aBoolean1249)) { - var23 = Class140_Sub3.method1957(160, this.aBoolean2810, var14 == null ? var13 : var14, this.xAxis, 0, this.zAxis, 0, 1, var15, var1, null != var14 ? this.anInt2813 : this.anInt2832, this.anInt2831, 240); - if (HDToolKit.highDetail) { - float var18 = HDToolKit.method1852(); - float var19 = HDToolKit.method1839(); - HDToolKit.depthBufferWritingDisabled(); - HDToolKit.method1825(var18, -150.0F + var19); - var23.animate(0, var2, var3, var4, var5, var6, var7, var8, -1L, var11, null); - HDToolKit.method1830(); - HDToolKit.method1825(var18, var19); - } else { - var23.animate(0, var2, var3, var4, var5, var6, var7, var8, -1L, var11, null); - } - } - - if (Class102.player == this) { - for (var17 = ClientErrorException.aClass96Array2114.length + -1; var17 >= 0; --var17) { - Class96 var27 = ClientErrorException.aClass96Array2114[var17]; - if (var27 != null && var27.anInt1355 != -1) { - int var21; - int var20; - if (var27.anInt1360 == 1 && 0 <= var27.anInt1359 && var27.anInt1359 < NPC.npcs.length) { - NPC var24 = NPC.npcs[var27.anInt1359]; - if (null != var24) { - var20 = var24.xAxis / 32 - Class102.player.xAxis / 32; - var21 = -(Class102.player.zAxis / 32) + var24.zAxis / 32; - this.method1979(null, var21, var15, var20, var6, var11, var1, var8, var5, var4, var2, var27.anInt1355, var3, var7); - } - } - - if (var27.anInt1360 == 2) { - int var29 = 4 * (-Class131.anInt1716 + var27.anInt1356) + 2 + -(Class102.player.xAxis / 32); - var20 = 2 + (4 * (var27.anInt1347 - Texture.anInt1152) - Class102.player.zAxis / 32); - this.method1979(null, var20, var15, var29, var6, var11, var1, var8, var5, var4, var2, var27.anInt1355, var3, var7); - } - - if (var27.anInt1360 == 10 && var27.anInt1359 >= 0 && var27.anInt1359 < Unsorted.players.length) { - Player var28 = Unsorted.players[var27.anInt1359]; - if (null != var28) { - var20 = -(Class102.player.xAxis / 32) + var28.xAxis / 32; - var21 = var28.zAxis / 32 + -(Class102.player.zAxis / 32); - this.method1979(null, var21, var15, var20, var6, var11, var1, var8, var5, var4, var2, var27.anInt1355, var3, var7); - } - } - } - } - } - - this.method1971(var15, (byte) -103); - this.method1969((byte) 110, var15, var1); - var23 = null; - if (!this.aBoolean3968 && this.anInt2842 != -1 && this.anInt2805 != -1) { - GraphicDefinition var26 = GraphicDefinition.getGraphicDefinition(this.anInt2842); - var23 = var26.method966(this.anInt2826, this.anInt2805, this.anInt2761); - if (var23 != null) { - var23.method1897(0, -this.anInt2799, 0); - if (var26.aBoolean536) { - if (TextureOperation15.anInt3198 != 0) { - var23.method1896(TextureOperation15.anInt3198); - } - - if (0 != Class3_Sub28_Sub9.anInt3623) { - var23.method1886(Class3_Sub28_Sub9.anInt3623); - } - - if (TextureOperation16.anInt3111 != 0) { - var23.method1897(0, TextureOperation16.anInt3111, 0); - } - } - } - } - - Model var25 = null; - if (!this.aBoolean3968 && this.anObject2796 != null) { - if (Class44.anInt719 >= this.anInt2778) { - this.anObject2796 = null; - } - - if (Class44.anInt719 >= this.anInt2797 && this.anInt2778 > Class44.anInt719) { - if (this.anObject2796 instanceof Class140_Sub3) { - var25 = (Model) ((Class140_Sub3) this.anObject2796).method1963(); - } else { - var25 = (Model) this.anObject2796; - } - - Objects.requireNonNull(var25).method1897(this.anInt2782 + -this.xAxis, this.anInt2812 + -this.anInt2831, this.anInt2833 + -this.zAxis); - if (this.anInt2806 == 512) { - var25.method1900(); - } else if (this.anInt2806 == 1024) { - var25.method1874(); - } else if (this.anInt2806 == 1536) { - var25.method1885(); - } - } - } - - if (HDToolKit.highDetail) { - var15.aBoolean2699 = true; - var15.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2801); - if (var23 != null) { - var23.aBoolean2699 = true; - var23.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2801); - } - } else { - if (null != var23) { - var15 = ((Class140_Sub1_Sub2) var15).method1943(var23); - } - - if (var25 != null) { - var15 = ((Class140_Sub1_Sub2) var15).method1943(var25); - } - - var15.aBoolean2699 = true; - var15.animate(var1, var2, var3, var4, var5, var6, var7, var8, var9, var11, this.aClass127_Sub1_2801); - } - - if (null != var25) { - if (this.anInt2806 == 512) { - var25.method1885(); - } else if (1024 == this.anInt2806) { - var25.method1874(); - } else if (1536 == this.anInt2806) { - var25.method1900(); - } - - var25.method1897(-this.anInt2782 + this.xAxis, -this.anInt2812 + this.anInt2831, -this.anInt2833 + this.zAxis); - } - - } - } - } catch (RuntimeException var22) { - throw ClientErrorException.clientError(var22, "e.IA(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + var11 + ',' + (var12 != null ? "{...}" : "null") + ')'); - } - } - - private void method1979(Class127_Sub1 var1, int var2, Model var3, int var4, int var5, int var6, int var8, int var9, int var10, int var11, int var12, int var13, int var14, int var15) { - try { - int var16 = var4 * var4 - -(var2 * var2); - if (var16 >= 16 && var16 <= 360000) { - int var17 = (int) (325.949D * Math.atan2(var4, var2)) & 0x7FF; - Model var18 = Class128.method1763(var17, this.zAxis, var13, this.xAxis, var3, this.anInt2831); - if (var18 != null) { - if (HDToolKit.highDetail) { - float var19 = HDToolKit.method1852(); - float var20 = HDToolKit.method1839(); - HDToolKit.depthBufferWritingDisabled(); - HDToolKit.method1825(var19, var20 - 150.0F); - var18.animate(0, var12, var14, var11, var10, var5, var15, var9, -1L, var6, var1); - HDToolKit.method1830(); - HDToolKit.method1825(var19, var20); - } else { - var18.animate(0, var12, var14, var11, var10, var5, var15, var9, -1L, var6, var1); - } - } - - } - } catch (RuntimeException var21) { - throw ClientErrorException.clientError(var21, "e.N(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + var5 + ',' + var6 + ',' + 2047 + ',' + var8 + ',' + var9 + ',' + var10 + ',' + var11 + ',' + var12 + ',' + var13 + ',' + var14 + ',' + var15 + ')'); - } - } - - public final boolean hasDefinitions() { - try { - - return this.class52 != null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "e.L(" + (byte) 17 + ')'); - } - } - - final RSString getName() { - try { - RSString var2 = this.displayName; - - if (BufferedDataStream.aClass94Array3802 != null) { - var2 = RSString.stringCombiner(new RSString[]{BufferedDataStream.aClass94Array3802[this.anInt3958], var2}); - } - - if (null != Unsorted.aClass94Array45) { - var2 = RSString.stringCombiner(new RSString[]{var2, Unsorted.aClass94Array45[this.anInt3958]}); - } - - return var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "e.Q(" + 0 + ')'); - } - } - - final void method1867(int var1, int var2, int var3, int var4, int var5) { - } - - final void method1981(int var2, boolean var3, int var4) { - try { - super.method1967(this.getSize(), var2, var4, var3); - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "e.O(" + (byte) 126 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - protected final void finalize() { - } - - final int method1871() { - try { - return this.anInt2820; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "e.MA()"); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/PlayerRendering.java b/Client/src/main/java/org/runite/client/PlayerRendering.java deleted file mode 100644 index f7cb6e14d..000000000 --- a/Client/src/main/java/org/runite/client/PlayerRendering.java +++ /dev/null @@ -1,508 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -public final class PlayerRendering { - - - static final int[] PLAYER_RENDER_LOG = new int[4]; - static DataBuffer playerRenderBuffer = new DataBuffer(new byte[5000]); - - static void renderPlayers() { - try { - Unsorted.maskUpdateCount = 0; - Class139.anInt1829 = 0; - - updateLocalPosition(); - PLAYER_RENDER_LOG[0] = BufferedDataStream.incomingBuffer.index; - - renderLocalPlayers(); - PLAYER_RENDER_LOG[1] = BufferedDataStream.incomingBuffer.index; - - addLocalPlayers(); - PLAYER_RENDER_LOG[2] = BufferedDataStream.incomingBuffer.index; - - parsePlayerMasks(); - PLAYER_RENDER_LOG[3] = BufferedDataStream.incomingBuffer.index; - - int var1; - for (var1 = 0; Class139.anInt1829 > var1; ++var1) { - int var2 = Class3_Sub7.anIntArray2292[var1]; - if (Class44.anInt719 != Unsorted.players[var2].anInt2838) { - if (0 < Unsorted.players[var2].anInt3969) { - Class162.method2203(Unsorted.players[var2]); - } - - Unsorted.players[var2] = null; - } - } - - if (BufferedDataStream.incomingBuffer.index == Unsorted.incomingPacketLength) { - for (var1 = 0; var1 < Class159.localPlayerCount; ++var1) { - if (null == Unsorted.players[Class56.localPlayerIndexes[var1]]) { - // throw new RuntimeException("gpp2 pos:" + var1 + " size:" + Class159.anInt2022); - // System.err.println("gpp2 pos:" + var1 + " size:" + Class159.anInt2022); - System.err.println("Local player was null - index: " + Class56.localPlayerIndexes[var1] + ", list index: " + var1 + ", list size: " + Class159.localPlayerCount); - } - } - - } else { - System.err.println("Player rendering packet size mismatch - size log: self=" + PLAYER_RENDER_LOG[0] + ", local=" + PLAYER_RENDER_LOG[1] + ", add global=" + PLAYER_RENDER_LOG[2] + ", masks=" + PLAYER_RENDER_LOG[3] + "."); - // System.err.println("gpp1 pos:" + GraphicDefinition.incomingBuffer.index + " psize:" + Class130.incomingPacketLength); - // throw new RuntimeException("gpp1 pos:" + Class28.incomingBuffer.index + " psize:" + Class130.incomingPacketLength); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fb.B(" + (byte) -122 + ')'); - } - } - - static void renderLocalPlayers() { - try { - int localPlayerAmount = BufferedDataStream.incomingBuffer.getBits(8); - int var2; - if (localPlayerAmount < Class159.localPlayerCount) { - for (var2 = localPlayerAmount; Class159.localPlayerCount > var2; ++var2) { - Class3_Sub7.anIntArray2292[Class139.anInt1829++] = Class56.localPlayerIndexes[var2]; - } - } - - if (Class159.localPlayerCount >= localPlayerAmount) { - Class159.localPlayerCount = 0; - var2 = 0; - - for (; localPlayerAmount > var2; ++var2) { - int var3 = Class56.localPlayerIndexes[var2]; - Player var4 = Unsorted.players[var3]; - int update = BufferedDataStream.incomingBuffer.getBits(1); - if (update == 0) { - Class56.localPlayerIndexes[Class159.localPlayerCount++] = var3; - var4.anInt2838 = Class44.anInt719; - } else { - int type = BufferedDataStream.incomingBuffer.getBits(2); - if (type == 0) { - Class56.localPlayerIndexes[Class159.localPlayerCount++] = var3; - var4.anInt2838 = Class44.anInt719; - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = var3; - } else { - int var7; - int var8; - if (type == 1) { - Class56.localPlayerIndexes[Class159.localPlayerCount++] = var3; - var4.anInt2838 = Class44.anInt719; - var7 = BufferedDataStream.incomingBuffer.getBits(3); - var4.walkStep(1, (byte) 46, var7); - var8 = BufferedDataStream.incomingBuffer.getBits(1); - if (var8 == 1) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = var3; - } - } else if (type == 2) { - Class56.localPlayerIndexes[Class159.localPlayerCount++] = var3; - var4.anInt2838 = Class44.anInt719; - if (BufferedDataStream.incomingBuffer.getBits(1) == 1) { - var7 = BufferedDataStream.incomingBuffer.getBits(3); - var4.walkStep(2, (byte) -92, var7); - var8 = BufferedDataStream.incomingBuffer.getBits(3); - var4.walkStep(2, (byte) 88, var8); - } else { - var7 = BufferedDataStream.incomingBuffer.getBits(3); - var4.walkStep(0, (byte) 113, var7); - } - - var7 = BufferedDataStream.incomingBuffer.getBits(1); - if (1 == var7) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = var3; - } - } else if (type == 3) { - Class3_Sub7.anIntArray2292[Class139.anInt1829++] = var3; - } - } - } - } - - } else { - throw new RuntimeException("gppov1"); - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "dc.B(" + false + ')'); - } - } - - static void addLocalPlayers() { - try { - while (true) { - if (BufferedDataStream.incomingBuffer.method815(Unsorted.incomingPacketLength) >= 11) { - int index = BufferedDataStream.incomingBuffer.getBits(11); - if (index != 2047) { - boolean var2 = false; - if (null == Unsorted.players[index]) { - Unsorted.players[index] = new Player(); - var2 = true; - if (null != Class65.aClass3_Sub30Array986[index]) { - Unsorted.players[index].parseAppearance(-54, Class65.aClass3_Sub30Array986[index]); - } - } - - Class56.localPlayerIndexes[Class159.localPlayerCount++] = index; - Player var3 = Unsorted.players[index]; - var3.anInt2838 = Class44.anInt719; - int var4 = BufferedDataStream.incomingBuffer.getBits(1); - if (var4 == 1) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = index; - } - - int var5 = BufferedDataStream.incomingBuffer.getBits(5); - int var6 = Class27.anIntArray510[BufferedDataStream.incomingBuffer.getBits(3)]; - if (var5 > 15) { - var5 -= 32; - } - - if (var2) { - var3.anInt2806 = var3.anInt2785 = var6; - } - - int var7 = BufferedDataStream.incomingBuffer.getBits(1); - int var8 = BufferedDataStream.incomingBuffer.getBits(5); - if (var8 > 15) { - var8 -= 32; - } - - var3.method1981(var5 + Class102.player.anIntArray2767[0], var7 == 1, Class102.player.anIntArray2755[0] + var8); - continue; - } - } - - BufferedDataStream.incomingBuffer.method818(); - return; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "se.D(" + -59 + ')'); - } - } - - static void parsePlayerMasks() { - int var1 = 0; - - while (var1 < Unsorted.maskUpdateCount) { - int var2 = Class21.maskUpdateIndexes[var1]; - Player var3 = Unsorted.players[var2]; - int var4 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - if ((16 & var4) != 0) { - var4 += BufferedDataStream.incomingBuffer.readUnsignedByte() << 8; - } - - parsePlayerMask(var4, var2, var3); - ++var1; - } - } - - static void parsePlayerMask(int var0, int var1, Player var3) { - try { - int var4; - int chatIcon; - int var7; - //Ordinal: 0 Chat - if (0 != (var0 & 128)) { - var4 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - chatIcon = BufferedDataStream.incomingBuffer.readUnsignedByte(); - int var6 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var7 = BufferedDataStream.incomingBuffer.index; - boolean var8 = (32768 & var4) != 0; - if (null != var3.displayName && var3.class52 != null) { - long var9 = var3.displayName.toLong(); - boolean var11 = false; - if (chatIcon <= 1) { - if (!var8 && (Class3_Sub15.aBoolean2433 && !Class121.aBoolean1641 || TextureOperation31.aBoolean3166)) { - var11 = true; - } else { - for (int var12 = 0; var12 < Class3_Sub28_Sub5.anInt3591; ++var12) { - if (Class114.ignores[var12] == var9) { - var11 = true; - break; - } - } - } - } - - if (!var11 && 0 == PacketParser.inTutorialIsland) { - playerRenderBuffer.index = 0; - BufferedDataStream.incomingBuffer.readBytesReverse(var6, playerRenderBuffer.buffer); - playerRenderBuffer.index = 0; - int var13 = -1; - RSString message; - if (var8) { - QuickChat var14 = QuickChat.method2156(playerRenderBuffer); - var4 &= 32767; - var13 = var14.anInt149; - message = var14.aQuickChatDefinition_151.method555(playerRenderBuffer); - } else { - message = Font.method686(Objects.requireNonNull(Class32.method992(playerRenderBuffer).properlyCapitalize())); - } - - var3.textSpoken = message.trim(1); - var3.textEffect = var4 & 0xFF; - var3.textCycle = 150; - var3.textColor = var4 >> 8; - if (chatIcon >= 2) { - MessageManager.sendGameMessage(var13, var8 ? 17 : 1, message, null, RSString.stringCombiner(new RSString[]{RSString.parse(""), var3.getName()})); - } else if (chatIcon == 1) { - MessageManager.sendGameMessage(var13, var8 ? 17 : 1, message, null, RSString.stringCombiner(new RSString[]{RSString.parse(""), var3.getName()})); - } else { - MessageManager.sendGameMessage(var13, var8 ? 17 : 2, message, null, var3.getName()); - } - } - } - BufferedDataStream.incomingBuffer.index = var7 + var6; - } - - //Ordinal: 1 Hit - if ((var0 & 1) != 0) { - var4 = BufferedDataStream.incomingBuffer.getSmart(); - chatIcon = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - var3.addHit(chatIcon, Class44.anInt719, var4); - var3.anInt2781 = 300 + Class44.anInt719; - var3.anInt2775 = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - } - - //Ordinal: 2 Animation - if ((var0 & 8) != 0) { - var4 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - if (var4 == 65535) { - var4 = -1; - } - - chatIcon = BufferedDataStream.incomingBuffer.readUnsignedByte(); - method628(chatIcon, var4, var3); - } - - //Ordinal: 3 Appearance - if (0 != (4 & var0)) { - var4 = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - byte[] var16 = new byte[var4]; - DataBuffer var19 = new DataBuffer(var16); - BufferedDataStream.incomingBuffer.readBytes(var16, var4); - Class65.aClass3_Sub30Array986[var1] = var19; - var3.parseAppearance(-15, var19); - } - - //Ordinal: 4 Face entity - if ((2 & var0) != 0) { - var3.anInt2772 = BufferedDataStream.incomingBuffer.readUnsignedShort128(); - if (var3.anInt2772 == 65535) { - var3.anInt2772 = -1; - } - } - - //Ordinal: 5 Force movement - if ((1024 & var0) != 0) { - var3.anInt2784 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - var3.anInt2835 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var3.anInt2823 = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - var3.anInt2798 = BufferedDataStream.incomingBuffer.readUnsignedByte(); - var3.anInt2800 = BufferedDataStream.incomingBuffer.readUnsignedShortLE() + Class44.anInt719; - var3.anInt2790 = BufferedDataStream.incomingBuffer.readUnsignedShortLE() - -Class44.anInt719; - var3.anInt2840 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - var3.anInt2816 = 1; - var3.anInt2811 = 0; - } - - //Ordinal: 6 Force chat - if ((var0 & 32) != 0) { - var3.textSpoken = BufferedDataStream.incomingBuffer.readString(); - if (var3.textSpoken.charAt(0, (byte) -45) == 126) { - var3.textSpoken = var3.textSpoken.substring(1); - BufferedDataStream.addChatMessage(var3.getName(), 2, var3.textSpoken, (byte) -79 ^ 78); - } else if (var3 == Class102.player) { - BufferedDataStream.addChatMessage(var3.getName(), 2, var3.textSpoken, (byte) -79 + 78); - } - - var3.textEffect = 0; - var3.textColor = 0; - var3.textCycle = 150; - } - - //Ordinal: 7 Hit 2 - if ((var0 & 512) != 0) { - var4 = BufferedDataStream.incomingBuffer.getSmart(); - chatIcon = BufferedDataStream.incomingBuffer.readUnsigned128Byte(); - var3.addHit(chatIcon, Class44.anInt719, var4); - } - - //Ordinal: 8 - if ((2048 & var0) != 0) { - var4 = BufferedDataStream.incomingBuffer.readUnsignedNegativeByte(); - int[] var18 = new int[var4]; - int[] var17 = new int[var4]; - int[] var20 = new int[var4]; - - for (int var22 = 0; var22 < var4; ++var22) { - int var23 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - if (65535 == var23) { - var23 = -1; - } - - var18[var22] = var23; - var17[var22] = BufferedDataStream.incomingBuffer.readUnsignedByte128(); - var20[var22] = BufferedDataStream.incomingBuffer.readUnsignedShort(); - } - - Class75_Sub1.method1342(var17, var18, var3, var20); - } - - //Ordinal: 9 Graphic - if ((256 & var0) != 0) { - var4 = BufferedDataStream.incomingBuffer.readUnsignedShortLE(); - if (var4 == 65535) { - var4 = -1; - } - - chatIcon = BufferedDataStream.incomingBuffer.readIntV2(); - boolean var21 = true; - if (var4 != -1 && var3.anInt2842 != -1 && SequenceDefinition.getAnimationDefinition(GraphicDefinition.getGraphicDefinition(var4).anInt542).forcedPriority < SequenceDefinition.getAnimationDefinition(GraphicDefinition.getGraphicDefinition(var3.anInt2842).anInt542).forcedPriority) { - var21 = false; - } - - if (var21) { - var3.anInt2759 = (chatIcon & 65535) + Class44.anInt719; - var3.anInt2761 = 0; - var3.anInt2805 = 0; - var3.anInt2842 = var4; - if (Class44.anInt719 < var3.anInt2759) { - var3.anInt2805 = -1; - } - - var3.anInt2799 = chatIcon >> 16; - var3.anInt2826 = 1; - if (var3.anInt2842 != -1 && Class44.anInt719 == var3.anInt2759) { - var7 = GraphicDefinition.getGraphicDefinition(var3.anInt2842).anInt542; - if (var7 != -1) { - SequenceDefinition var24 = SequenceDefinition.getAnimationDefinition(var7); - if (var24.frames != null) { - Unsorted.method1470(var3.zAxis, var24, var3.xAxis, var3 == Class102.player, 0); - } - } - } - } - } - - //Ordinal: 10 Face location - if ((var0 & 64) != 0) { - var3.anInt2786 = BufferedDataStream.incomingBuffer.readUnsignedShort(); - var3.anInt2762 = BufferedDataStream.incomingBuffer.readUnsignedShortLE128(); - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "gk.A(" + var0 + ',' + var1 + ',' + (byte) -79 + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - static void method628(int var1, int var2, Player var3) { - try { - - if (var3.anInt2771 == var2 && var2 != -1) { - SequenceDefinition var4 = SequenceDefinition.getAnimationDefinition(var2); - int var5 = var4.delayType; - if (1 == var5) { - var3.anInt2828 = var1; - var3.anInt2760 = 0; - var3.anInt2776 = 1; - var3.anInt2832 = 0; - var3.anInt2773 = 0; - Unsorted.method1470(var3.zAxis, var4, var3.xAxis, Class102.player == var3, var3.anInt2832); - } - - if (var5 == 2) { - var3.anInt2773 = 0; - } - } else if (-1 == var2 || var3.anInt2771 == -1 || SequenceDefinition.getAnimationDefinition(var2).forcedPriority >= SequenceDefinition.getAnimationDefinition(var3.anInt2771).forcedPriority) { - var3.anInt2776 = 1; - var3.anInt2832 = 0; - var3.anInt2828 = var1; - var3.anInt2811 = var3.anInt2816; - var3.anInt2773 = 0; - var3.anInt2760 = 0; - var3.anInt2771 = var2; - if (var3.anInt2771 != -1) { - Unsorted.method1470(var3.zAxis, SequenceDefinition.getAnimationDefinition(var3.anInt2771), var3.xAxis, var3 == Class102.player, var3.anInt2832); - } - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "pa.C(" + 0 + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - public static void updateLocalPosition() { - try { - BufferedDataStream.incomingBuffer.setBitAccess(); - int opcode = BufferedDataStream.incomingBuffer.getBits(1); - if (opcode != 0) { - int type = BufferedDataStream.incomingBuffer.getBits(2); - if (type == 0) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = 2047; - } else { - int var4; - int var5; - if (type == 1) { //Walk - var4 = BufferedDataStream.incomingBuffer.getBits(3); - Class102.player.walkStep(1, (byte) -128, var4); - var5 = BufferedDataStream.incomingBuffer.getBits(1); - if (var5 == 1) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = 2047; - } - - } else if (type == 2) { - if (BufferedDataStream.incomingBuffer.getBits(1) == 1) { - var4 = BufferedDataStream.incomingBuffer.getBits(3); - Class102.player.walkStep(2, (byte) -104, var4); - var5 = BufferedDataStream.incomingBuffer.getBits(3); - Class102.player.walkStep(2, (byte) -126, var5); - } else { - var4 = BufferedDataStream.incomingBuffer.getBits(3); - Class102.player.walkStep(0, (byte) -109, var4); - } - - var4 = BufferedDataStream.incomingBuffer.getBits(1); - if (var4 == 1) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = 2047; - } - - } else if (type == 3) { - var4 = BufferedDataStream.incomingBuffer.getBits(7); - var5 = BufferedDataStream.incomingBuffer.getBits(1); - WorldListCountry.localPlane = BufferedDataStream.incomingBuffer.getBits(2); - int var6 = BufferedDataStream.incomingBuffer.getBits(1); - if (var6 == 1) { - Class21.maskUpdateIndexes[Unsorted.maskUpdateCount++] = 2047; - } - - int var7 = BufferedDataStream.incomingBuffer.getBits(7); - Class102.player.method1981(var7, var5 == 1, var4); - } - } - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "bg.G(" + (byte) 81 + ')'); - } - } - - static void updatePlayerAreaArray() { - try { - for (int var1 = -1; Class159.localPlayerCount > var1; ++var1) { - int var2; - if (var1 == -1) { - var2 = 2047; - } else { - var2 = Class56.localPlayerIndexes[var1]; - } - - Player var3 = Unsorted.players[var2]; - if (var3 != null) { - Unsorted.method68(var3.getSize(), var3); - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "fk.G(" + -102 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/QuickChat.java b/Client/src/main/java/org/runite/client/QuickChat.java deleted file mode 100644 index dab937864..000000000 --- a/Client/src/main/java/org/runite/client/QuickChat.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.NodeCache; - -final class QuickChat { - - static CacheIndex aClass153_1967; - static CacheIndex aClass153_3490; - static NodeCache aClass47_3137 = new NodeCache(64); - static NodeCache aClass47_3776 = new NodeCache(64); - static int anInt377 = 0; - static int anInt1156 = 0; - - int anInt149; - QuickChatDefinition aQuickChatDefinition_151; - int[] anIntArray153; - - static void method205(CacheIndex quickchatMenusIndex, CacheIndex quickchatMessagesIndex, Interface4 var3) { - try { - aClass153_1967 = quickchatMenusIndex; - Class58.anInterface4_915 = var3; - aClass153_3490 = quickchatMessagesIndex; - if (aClass153_3490 != null) { - anInt1156 = aClass153_3490.getFileAmount(1); - } - - if (aClass153_1967 != null) { - anInt377 = aClass153_1967.getFileAmount(1); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ej.E(" + (quickchatMenusIndex != null ? "{...}" : "null") + ',' + 115 + ',' + (quickchatMessagesIndex != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - static QuickChatDefinition method733(int var1) { - try { - QuickChatDefinition var2 = (QuickChatDefinition) aClass47_3776.get(var1); - if (null == var2) { - byte[] var3; - if (var1 < 32768) { - var3 = aClass153_3490.getFile(1, var1); - } else { - var3 = aClass153_1967.getFile(1, 32767 & var1); - } - - var2 = new QuickChatDefinition(); - - if (var3 != null) { - var2.decode(new DataBuffer(var3)); - } - - if (var1 >= 32768) { - var2.method548(); - } - - aClass47_3776.put(var1, var2); - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "sj.Q(" + 12345678 + ',' + var1 + ')'); - } - } - - static QuickChat method2156(DataBuffer var1) { - try { - QuickChat var2 = new QuickChat(); - var2.anInt149 = var1.readUnsignedShort(); - - var2.aQuickChatDefinition_151 = method733(var2.anInt149); - return var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "vh.M(" + 1024 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static Class3_Sub28_Sub1 getQuickChatMessage(int fileId) { - try { - Class3_Sub28_Sub1 var2 = (Class3_Sub28_Sub1) aClass47_3137.get(fileId); - if (null == var2) { - byte[] var3; - if (fileId < 32768) { - var3 = Unsorted.quickChatMessages.getFile(0, fileId); - } else { - var3 = Unsorted.aClass153_332.getFile(0, fileId & 32767); - } - - var2 = new Class3_Sub28_Sub1(); - if (null != var3) { - var2.method530(new DataBuffer(var3)); - } - - if (fileId >= 32768) { - var2.method525(); - } - - aClass47_3137.put(fileId, var2); - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "tb.B(" + fileId + ',' + (byte) -54 + ')'); - } - } - - static void method1236(CacheIndex var0, CacheIndex var1) { - try { - Unsorted.quickChatMessages = var1; - Unsorted.aClass153_332 = var0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ja.F(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + -117 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/QuickChatDefinition.java b/Client/src/main/java/org/runite/client/QuickChatDefinition.java deleted file mode 100644 index 4666101ed..000000000 --- a/Client/src/main/java/org/runite/client/QuickChatDefinition.java +++ /dev/null @@ -1,172 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; -import org.rs09.client.data.ReferenceCache; - -import java.util.Objects; - -final class QuickChatDefinition extends Node { - - static int[] anIntArray3565 = new int[32]; - static int[] anIntArray2209 = new int[]{2, 2, 4, 2, 1, 8, 4, 1, 4, 4, 2, 1, 1, 1, 4, 1}; - static int[] anIntArray1976 = new int[]{1, 0, 0, 0, 1, 0, 2, 1, 1, 1, 0, 2, 0, 0, 1, 0}; - private int[] anIntArray3566; - int[] anIntArray3567; - boolean aBoolean3568 = true; - private int[][] anIntArrayArray3570; - private RSString[] aClass94Array3571; - static ReferenceCache aReferenceCache_3572 = new ReferenceCache(64); - static Class83 aClass83_3579; - - static RSString method612(long var0) { - return TextureOperation9.method207(10, false, 116, var0); - } - - final void decode(DataBuffer buffer) { - - while(true) { - int var3 = buffer.readUnsignedByte(); - if(0 == var3) { - return; - } - - this.decode(buffer, var3); - } - } - - private void decode(DataBuffer var1, int var2) { - try { - - if(var2 == 1) { - this.aClass94Array3571 = var1.readString().method1567(60, (byte)118); - } else { - int var4; - int var5; - if(var2 == 2) { - var4 = var1.readUnsignedByte(); - this.anIntArray3567 = new int[var4]; - - for(var5 = 0; var5 < var4; ++var5) { - this.anIntArray3567[var5] = var1.readUnsignedShort(); - } - } else if (var2 == 3) { - var4 = var1.readUnsignedByte(); - this.anIntArray3566 = new int[var4]; - this.anIntArrayArray3570 = new int[var4][]; - - for (var5 = 0; var4 > var5; ++var5) { - int var6 = var1.readUnsignedShort(); - this.anIntArray3566[var5] = var6; - this.anIntArrayArray3570[var5] = new int[anIntArray1976[var6]]; - - for (int var7 = 0; anIntArray1976[var6] > var7; ++var7) { - this.anIntArrayArray3570[var5][var7] = var1.readUnsignedShort(); - } - } - } else if (var2 == 4) { - this.aBoolean3568 = false; - } - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "cb.R(" + (var1 != null?"{...}":"null") + ',' + var2 + ',' + -14637 + ')'); - } - } - - final void method545(DataBuffer var1, int[] var2) { - try { - if(this.anIntArray3566 != null) { - for(int var4 = 0; var4 < this.anIntArray3566.length && var2.length > var4; ++var4) { - int var5 = ClientErrorException.anIntArray2113[this.method550(89, var4)]; - if(var5 > 0) { - var1.method739(var5, var2[var4]); - } - } - - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "cb.O(" + (var1 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + false + ')'); - } - } - - final void method548() { - try { - if(null != this.anIntArray3567) { - for(int var2 = 0; var2 < this.anIntArray3567.length; ++var2) { - this.anIntArray3567[var2] = TextureOperation3.bitwiseOr(this.anIntArray3567[var2], 32768); - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "cb.Q(" + 60 + ')'); - } - } - - final int method549(int var2, int var3) { - try { - - return null != this.anIntArray3566 && var3 >= 0 && this.anIntArray3566.length >= var3 ?(this.anIntArrayArray3570[var3] != null && var2 >= 0 && this.anIntArrayArray3570[var3].length >= var2 ?this.anIntArrayArray3570[var3][var2]:-1):-1; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "cb.P(" + -117 + ',' + var2 + ',' + var3 + ')'); - } - } - - final int method550(int var1, int var2) { - try { - if(null != this.anIntArray3566 && var2 >= 0 && var2 <= this.anIntArray3566.length) { - return this.anIntArray3566[var2]; - } else { - return -1; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "cb.S(" + var1 + ',' + var2 + ')'); - } - } - - final int method552() { - try { - return (this.anIntArray3566 != null?this.anIntArray3566.length:0); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "cb.E(" + true + ')'); - } - } - - final RSString method554() { - try { - RSString var2 = Unsorted.emptyString(80); - if(null == this.aClass94Array3571) { - return TextCore.aClass94_3357; - } else { - Objects.requireNonNull(var2).append(this.aClass94Array3571[0]); - - for(int var3 = 1; var3 < this.aClass94Array3571.length; ++var3) { - var2.append(RSString.parse(")3)3)3")); - var2.append(this.aClass94Array3571[var3]); - } - - return var2.method1576(); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "cb.C(" + -1 + ')'); - } - } - - final RSString method555(DataBuffer var2) { - try { - - RSString var3 = Unsorted.emptyString(80); - if(this.anIntArray3566 != null) { - for(int var4 = 0; var4 < this.anIntArray3566.length; ++var4) { - Objects.requireNonNull(var3).append(this.aClass94Array3571[var4]); - var3.append(Class49.method1124(this.anIntArrayArray3570[var4], var2.method772(anIntArray2209[this.anIntArray3566[var4]]), this.anIntArray3566[var4])); - } - } - - Objects.requireNonNull(var3).append(this.aClass94Array3571[-1 + this.aClass94Array3571.length]); - return var3.method1576(); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "cb.B(" + 28021 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/RSInterface.java b/Client/src/main/java/org/runite/client/RSInterface.java deleted file mode 100644 index a1d2def38..000000000 --- a/Client/src/main/java/org/runite/client/RSInterface.java +++ /dev/null @@ -1,1133 +0,0 @@ -package org.runite.client; - -public final class RSInterface { - - static boolean aBoolean6 = false; - boolean hidden = false; - Object[] anObjectArray156; - boolean aBoolean157; - Object[] anObjectArray158; - Object[] anObjectArray159; - int anInt160 = 1; - Object[] anObjectArray161; - byte verticalPos = 0; - boolean aBoolean163; - int anInt164 = 100; - Object[] anObjectArray165; - int defY; - boolean aBoolean167; - public int width; - short aShort169 = 3000; - Object[] anObjectArray170; - RSString[] aClass94Array171; - RSString aClass94_172; - RSString[] options; - Object[] anObjectArray174; - int[] anIntArray175; - Object[] anObjectArray176; - int defWidth; - boolean aBoolean178; - int anInt179 = 0; - Object[] anObjectArray180; - boolean aBoolean181 = false; - int anInt182 = 0; - Object[] anObjectArray183; - int anInt184; - int[] anIntArray185; - boolean aBoolean186 = false; - int type; - boolean aBoolean188 = false; - int anInt189; - int parentId; - int anInt191 = -1; - int anInt192; - public int height = 0; - int anInt194 = 0; - boolean aBoolean195; - private int secondModelId; - int[] anIntArray197; - int secondAnimationId = -1; - boolean aBoolean199; - boolean aBoolean200; - int itemId; - int modelType; - Object[] anObjectArray203; - int anInt204; - int anInt205 = 0; - Object[] anObjectArray206; - public int[] anIntArray207; - int anInt208 = 0; - int anInt210 = 0; - int[] anIntArray211; - int anInt212; - int anInt213; - int anInt214 = 0; - boolean aBoolean215; - int anInt216; - Object[] anObjectArray217; - int anInt218; - boolean aBoolean219; - Object[] anObjectArray220; - Object[] anObjectArray221; - int anInt222; - int anInt223; - int spriteArchiveId = -1; - int anInt225; - boolean aBoolean226 = false; - boolean aBoolean227; - int anInt228; - Object[] anObjectArray229; - int anInt230 = 0; - byte[] aByteArray231; - public RSString text; - boolean usingScripts; - int anInt234; - Object[] anObjectArray235; - static boolean aBoolean236 = true; - int anInt237; - int anInt238 = -1; - Object[] anObjectArray239; - int anInt240; - byte verticalResize; - int anInt242; - RSString aClass94_243; - int defHeight; - RSString aClass94_245; - static float aFloat246; - int anInt247; - Object[] anObjectArray248; - int[] anIntArray249; - int anInt250 = 1; - static RSString aClass94_251 = null; - int anInt252; - int anInt253; - int[] itemAmounts; - int anInt255; - Object[] anObjectArray256; - Class3_Sub1 aClass3_Sub1_257; - int anInt258; - int anInt259; - int anInt260; - static long aLong261 = 0L; - RSInterface[] aClass11Array262; - byte[] aByteArray263; - int anInt264; - int anInt265; - int anInt266; - int anInt267; - Object[] anObjectArray268; - Object[] anObjectArray269; - int anInt270; - int anInt271; - int[] anIntArray272; - byte horizontalPos; - int[] anIntArray274; - int[] anIntArray275; - Object[] anObjectArray276; - RSString aClass94_277; - static int anInt278 = -1; - int componentHash; - int anInt280; - Object[] anObjectArray281; - Object[] anObjectArray282; - int anInt283; - int anInt284; - int anInt285; - int[] anIntArray286; - int anInt287; - int anInt288; - RSString aClass94_289; - int anInt290; - public int[] anIntArray291; - int anInt292; - short aShort293; - private int secondModelType; - Object[] anObjectArray295; - int anInt296; - int[][] childDataBuffers; - int[] anIntArray299; - int[] anIntArray300; - int anInt301; - RSInterface aClass11_302; - Object[] anObjectArray303; - byte horizontalResize; - int animationId; - int anInt306; - int[] anIntArray307; - int anInt308; - boolean aBoolean309; - int[] anIntArray310; - int anInt311; - int anInt312; - Object[] anObjectArray313; - Object[] anObjectArray314; - Object[] anObjectArray315; - int defX; - int[] itemIds; - int anInt318; - - static void method2271(int var0, int var1, int var3) { - try { - - InterfaceWidget var4 = InterfaceWidget.getWidget(11, var1); - var4.flagUpdate(); - var4.anInt3597 = var3; - var4.anInt3598 = var0; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "wl.F(" + var0 + ',' + var1 + ',' + 1 + ',' + var3 + ')'); - } - } - - - final void method854(int var1, int var2) { - try { - if (this.anIntArray249 == null || var1 >= this.anIntArray249.length) { - int[] var4 = new int[1 + var1]; - if (this.anIntArray249 != null) { - int var5; - for (var5 = 0; this.anIntArray249.length > var5; ++var5) { - var4[var5] = this.anIntArray249[var5]; - } - - for (var5 = this.anIntArray249.length; var5 < var1; ++var5) { - var4[var5] = -1; - } - } - - this.anIntArray249 = var4; - } - - this.anIntArray249[var1] = var2; - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "be.P(" + var1 + ',' + var2 + ',' + (byte) 43 + ')'); - } - } - - final boolean method855() { - try { - if (this.anIntArray207 == null) { - LDIndexedSprite var2 = Unsorted.method1539(this.spriteArchiveId, Class12.aClass153_323); - if (null == var2) { - return false; - } else { - var2.method1675(); - this.anIntArray207 = new int[var2.height]; - this.anIntArray291 = new int[var2.height]; - int var3 = 0; - - while (var2.height > var3) { - int var4 = 0; - int var5 = var2.width; - int var6 = 0; - - while (true) { - if (var2.width > var6) { - if (var2.raster[var2.width * var3 + var6] == 0) { - ++var6; - continue; - } - - var4 = var6; - } - - for (var6 = var4; var2.width > var6; ++var6) { - if (0 == var2.raster[var3 * var2.width + var6]) { - var5 = var6; - break; - } - } - - this.anIntArray207[var3] = var4; - this.anIntArray291[var3] = var5 - var4; - ++var3; - break; - } - } - - return true; - } - } else { - return true; - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "be.G(" + -30721 + ')'); - } - } - - static RSString method856() { - try { - - RSString var1 = TextCore.aClass94_4052; - RSString var2 = RSString.parse(""); - if (Class44.paramModeWhere != 0) { - var1 = RSString.parse("www)2wtqa"); - } - - if (null != Class163_Sub2.paramSettings) { - var2 = RSString.stringCombiner(new RSString[]{TextCore.aClass94_3637, Class163_Sub2.paramSettings}); - } - - return RSString.stringCombiner(new RSString[]{TextCore.aClass94_577, var1, TextCore.aClass94_3601, RSString.stringAnimator(Class3_Sub20.paramLanguage), TextCore.aClass94_1932, RSString.stringAnimator(Class3_Sub26.paramAffid), var2, TextCore.aClass94_2735}); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "be.N(" + true + ')'); - } - } - - final void method857(RSString var2, int var3) { - try { - if (null == this.aClass94Array171 || var3 >= this.aClass94Array171.length) { - RSString[] var4 = new RSString[1 + var3]; - if (null != this.aClass94Array171) { - System.arraycopy(this.aClass94Array171, 0, var4, 0, this.aClass94Array171.length); - } - - this.aClass94Array171 = var4; - } - - this.aClass94Array171[var3] = var2; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "be.B(" + (byte) 112 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ')'); - } - } - - final void decodeNoScripts(DataBuffer buffer) { - this.usingScripts = false; - this.type = buffer.readUnsignedByte(); - this.anInt318 = buffer.readUnsignedByte(); - this.anInt189 = buffer.readUnsignedShort(); - this.defX = buffer.readSignedShort(); - this.defY = buffer.readSignedShort(); - this.defWidth = buffer.readUnsignedShort(); - this.defHeight = buffer.readUnsignedShort(); - this.horizontalResize = 0; - this.verticalResize = 0; - this.horizontalPos = 0; - this.verticalPos = 0; - this.anInt223 = buffer.readUnsignedByte(); - this.parentId = buffer.readUnsignedShort(); - if (this.parentId == 65535) { - this.parentId = -1; - } else { - this.parentId += -65536 & this.componentHash; - } - - this.anInt212 = buffer.readUnsignedShort(); - if (this.anInt212 == 65535) { - this.anInt212 = -1; - } - - int var3 = buffer.readUnsignedByte(); - int var4; - if (var3 > 0) { - this.anIntArray307 = new int[var3]; - this.anIntArray275 = new int[var3]; - - for (var4 = 0; var4 < var3; ++var4) { - this.anIntArray275[var4] = buffer.readUnsignedByte(); - this.anIntArray307[var4] = buffer.readUnsignedShort(); - } - } - - var4 = buffer.readUnsignedByte(); - int var5; - int var6; - int var7; - if (var4 > 0) { - this.childDataBuffers = new int[var4][]; - - for (var5 = 0; var5 < var4; ++var5) { - var6 = buffer.readUnsignedShort(); - this.childDataBuffers[var5] = new int[var6]; - - for (var7 = 0; var6 > var7; ++var7) { - this.childDataBuffers[var5][var7] = buffer.readUnsignedShort(); - if (this.childDataBuffers[var5][var7] == 65535) { - this.childDataBuffers[var5][var7] = -1; - } - } - } - } - - if (this.type == 0) { - this.anInt252 = buffer.readUnsignedShort(); - this.hidden = 1 == buffer.readUnsignedByte(); - } - - if (this.type == 1) { - buffer.readUnsignedShort(); - buffer.readUnsignedByte(); - } - - var5 = 0; - if (this.type == 2) { - this.verticalResize = 3; - this.itemIds = new int[this.defWidth * this.defHeight]; - this.itemAmounts = new int[this.defHeight * this.defWidth]; - this.horizontalResize = 3; - var6 = buffer.readUnsignedByte(); - var7 = buffer.readUnsignedByte(); - if (var6 == 1) { - var5 |= 268435456; - } - - int var8 = buffer.readUnsignedByte(); - if (var7 == 1) { - var5 |= 1073741824; - } - - if (1 == var8) { - var5 |= Integer.MIN_VALUE; - } - - int var9 = buffer.readUnsignedByte(); - if (var9 == 1) { - var5 |= 536870912; - } - - this.anInt285 = buffer.readUnsignedByte(); - this.anInt290 = buffer.readUnsignedByte(); - this.anIntArray300 = new int[20]; - this.anIntArray272 = new int[20]; - this.anIntArray197 = new int[20]; - - int var10; - for (var10 = 0; 20 > var10; ++var10) { - int var11 = buffer.readUnsignedByte(); - if (var11 == 1) { - this.anIntArray272[var10] = buffer.readSignedShort(); - this.anIntArray300[var10] = buffer.readSignedShort(); - this.anIntArray197[var10] = buffer.readInt(); - } else { - this.anIntArray197[var10] = -1; - } - } - - this.options = new RSString[5]; - - for (var10 = 0; var10 < 5; ++var10) { - RSString var14 = buffer.readString(); - if (var14.length() > 0) { - this.options[var10] = var14; - var5 |= 1 << 23 - -var10; - } - } - } - - if (3 == this.type) { - this.aBoolean226 = 1 == buffer.readUnsignedByte(); - } - - if (this.type == 4 || 1 == this.type) { - this.anInt194 = buffer.readUnsignedByte(); - this.anInt225 = buffer.readUnsignedByte(); - this.anInt205 = buffer.readUnsignedByte(); - this.anInt270 = buffer.readUnsignedShort(); - if (this.anInt270 == 65535) { - this.anInt270 = -1; - } - - this.aBoolean215 = 1 == buffer.readUnsignedByte(); - } - - if (this.type == 4) { - this.text = buffer.readString(); - this.aClass94_172 = buffer.readString(); - } - - if (this.type == 1 || this.type == 3 || 4 == this.type) { - this.anInt218 = buffer.readInt(); - } - - if (this.type == 3 || this.type == 4) { - this.anInt253 = buffer.readInt(); - this.anInt228 = buffer.readInt(); - this.anInt222 = buffer.readInt(); - } - - if (this.type == 5) { - this.spriteArchiveId = buffer.readInt(); - this.anInt296 = buffer.readInt(); - } - - if (6 == this.type) { - this.modelType = 1; - this.itemId = buffer.readUnsignedShort(); - this.secondModelType = 1; - if (this.itemId == 65535) { - this.itemId = -1; - } - - this.secondModelId = buffer.readUnsignedShort(); - if (this.secondModelId == 65535) { - this.secondModelId = -1; - } - - this.animationId = buffer.readUnsignedShort(); - if (this.animationId == 65535) { - this.animationId = -1; - } - - this.secondAnimationId = buffer.readUnsignedShort(); - if (65535 == this.secondAnimationId) { - this.secondAnimationId = -1; - } - - this.anInt164 = buffer.readUnsignedShort(); - this.anInt182 = buffer.readUnsignedShort(); - this.anInt308 = buffer.readUnsignedShort(); - } - - if (7 == this.type) { - this.verticalResize = 3; - this.horizontalResize = 3; - this.itemIds = new int[this.defHeight * this.defWidth]; - this.itemAmounts = new int[this.defWidth * this.defHeight]; - this.anInt194 = buffer.readUnsignedByte(); - this.anInt270 = buffer.readUnsignedShort(); - if (this.anInt270 == 65535) { - this.anInt270 = -1; - } - - this.aBoolean215 = buffer.readUnsignedByte() == 1; - this.anInt218 = buffer.readInt(); - this.anInt285 = buffer.readSignedShort(); - this.anInt290 = buffer.readSignedShort(); - var6 = buffer.readUnsignedByte(); - if (var6 == 1) { - var5 |= 1073741824; - } - - this.options = new RSString[5]; - - for (var7 = 0; var7 < 5; ++var7) { - RSString var13 = buffer.readString(); - if (var13.length() > 0) { - this.options[var7] = var13; - var5 |= 1 << 23 - -var7; - } - } - } - - if (8 == this.type) { - this.text = buffer.readString(); - } - - if (this.anInt318 == 2 || this.type == 2) { - this.aClass94_245 = buffer.readString(); - this.aClass94_243 = buffer.readString(); - var6 = 63 & buffer.readUnsignedShort(); - var5 |= var6 << 11; - } - - if (this.anInt318 == 1 || this.anInt318 == 4 || this.anInt318 == 5 || this.anInt318 == 6) { - this.aClass94_289 = buffer.readString(); - if (this.aClass94_289.length() == 0) { - if (this.anInt318 == 1) { - this.aClass94_289 = TextCore.HasOK; - } - - if (this.anInt318 == 4) { - this.aClass94_289 = TextCore.HasSelect; - } - - if (5 == this.anInt318) { - this.aClass94_289 = TextCore.HasSelect; - } - - if (this.anInt318 == 6) { - this.aClass94_289 = TextCore.HasContinue; - } - } - } - - if (this.anInt318 == 1 || this.anInt318 == 4 || this.anInt318 == 5) { - var5 |= 4194304; - } - - if (this.anInt318 == 6) { - var5 |= 1; - } - - this.aClass3_Sub1_257 = new Class3_Sub1(var5, -1); - } - - final AbstractSprite method859(int var2) { - try { - aBoolean6 = false; - if (var2 >= 0 && var2 < this.anIntArray197.length) { - int var3 = this.anIntArray197[var2]; - if (var3 == -1) { - return null; - } else { - AbstractSprite var4 = (AbstractSprite) Class114.aReferenceCache_1569.get(var3); - if (var4 == null) { - var4 = Unsorted.method602(var3, Class12.aClass153_323); - if (null == var4) { - aBoolean6 = true; - } else { - Class114.aReferenceCache_1569.put(var4, var3); - } - - } - return var4; - } - } else { - return null; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "be.I(" + true + ',' + var2 + ')'); - } - } - - static int method861(int var0, int var1, int var2) { - try { - Class3_Sub25 var3 = (Class3_Sub25) Class3_Sub2.aHashTable_2220.get(var0); - return null == var3 ? -1 : (0 <= var2 && var2 < var3.anIntArray2547.length ? (var1 < 39 ? -69 : var3.anIntArray2547[var2]) : -1); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "be.J(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - private Object[] method862(DataBuffer var2) { - try { - int var3 = var2.readUnsignedByte(); - if (var3 == 0) { - return null; - } else { - Object[] var4 = new Object[var3]; - - for (int var5 = 0; var3 > var5; ++var5) { - int var6 = var2.readUnsignedByte(); - if (0 == var6) { - var4[var5] = new Integer(var2.readInt()); - } else if (var6 == 1) { - var4[var5] = var2.readString(); - } - } - - this.aBoolean195 = true; - return var4; - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "be.K(" + -65536 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - private int[] method863(DataBuffer var1) { - try { - int var3 = var1.readUnsignedByte(); - if (var3 == 0) { - return null; - } else { - int[] var4 = new int[var3]; - - for (int var5 = 0; var3 > var5; ++var5) { - var4[var5] = var1.readInt(); - } - - return var4; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "be.H(" + (var1 != null ? "{...}" : "null") + ',' + false + ')'); - } - } - - final void method864(int var1, int var2) { - int var4 = this.itemAmounts[var2]; - this.itemAmounts[var2] = this.itemAmounts[var1]; - this.itemAmounts[var1] = var4; - var4 = this.itemIds[var2]; - this.itemIds[var2] = this.itemIds[var1]; - this.itemIds[var1] = var4; - } - - final Model method865(int var1, SequenceDefinition var2, int var3, int var4, int var5, boolean var6, Class52 var7) { - try { - aBoolean6 = false; - int var8; - int var9; - if (var6) { - var8 = this.secondModelType; - var9 = this.secondModelId; - } else { - var9 = this.itemId; - var8 = this.modelType; - } - - if (var4 < 125) { - return null; - } else if (var8 == 0) { - return null; - } else if (var8 == 1 && var9 == -1) { - return null; - } else { - Model var10; - if (1 == var8) { - var10 = (Model) Class3_Sub15.aReferenceCache_2428.get((var8 << 16) - -var9); - if (var10 == null) { - Model_Sub1 var18 = Model_Sub1.method2015(Class119.aClass153_1628, var9); - if (var18 == null) { - aBoolean6 = true; - return null; - } - - var10 = var18.method2008(64, 768, -50, -10, -50); - Class3_Sub15.aReferenceCache_2428.put(var10, var9 + (var8 << 16)); - } - - if (var2 != null) { - var10 = var2.method2055(var10, (byte) 119, var1, var5, var3); - } - - return var10; - } else if (var8 == 2) { - var10 = NPCDefinition.getNPCDefinition(var9).getChatModel(var2, var5, var1, 27, var3); - if (null == var10) { - aBoolean6 = true; - return null; - } else { - return var10; - } - } else if (3 != var8) { - if (4 == var8) { - ItemDefinition var16 = ItemDefinition.getItemDefinition(var9); - Model var17 = var16.method1110(var1, var5, var2, 10, var3); - if (var17 == null) { - aBoolean6 = true; - return null; - } else { - return var17; - } - } else if (var8 == 6) { - var10 = NPCDefinition.getNPCDefinition(var9).method1476(null, 0, (byte) -120, 0, var1, var5, var3, null, 0, var2); - if (null == var10) { - aBoolean6 = true; - return null; - } else { - return var10; - } - } else if (var8 != 7) { - return null; - } else if (var7 == null) { - return null; - } else { - int var15 = this.itemId >>> 16; - int var11 = this.itemId & 65535; - int var12 = this.anInt265; - Model var13 = var7.method1157(var1, var12, var15, var5, var2, var3, var11); - if (var13 == null) { - aBoolean6 = true; - return null; - } else { - return var13; - } - } - } else if (null == var7) { - return null; - } else { - var10 = var7.method1167(var5, var2, var3, var1); - if (null == var10) { - aBoolean6 = true; - return null; - } else { - return var10; - } - } - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "be.E(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + (var7 != null ? "{...}" : "null") + ')'); - } - } - - public final AbstractSprite method866(boolean var2) { - try { - aBoolean6 = false; - int archiveId; - if (var2) { - archiveId = this.anInt296; - } else { - archiveId = this.spriteArchiveId; - } - if (archiveId == -1) { - return null; - } else { - long var4 = ((this.aBoolean178 ? 1L : 0L) << 38) + ((!this.aBoolean157 ? 0L : 1L) << 35) + (long) archiveId + ((long) this.anInt288 << 36) + ((this.aBoolean199 ? 1L : 0L) << 39) + ((long) this.anInt287 << 40); - AbstractSprite var6 = (AbstractSprite) Class114.aReferenceCache_1569.get(var4); - if (var6 == null) { - SoftwareSprite var7; - if (this.aBoolean157) { - var7 = Unsorted.method562(Class12.aClass153_323, archiveId); - } else { - var7 = Class40.method1043(0, Class12.aClass153_323, archiveId); - } - - if (null == var7) { - aBoolean6 = true; - return null; - } else { - if (this.aBoolean178) { - var7.method663(); - } - - if (this.aBoolean199) { - var7.method653(); - } - - if (this.anInt288 > 0) { - var7.method652(this.anInt288); - } - - if (this.anInt288 >= 1) { - var7.method657(1); - } - - if (2 <= this.anInt288) { - var7.method657(16777215); - } - - if (this.anInt287 != 0) { - var7.method668(this.anInt287); - } - - Object var9; - if (HDToolKit.highDetail) { - if (var7 instanceof Class3_Sub28_Sub16_Sub2_Sub1) { - var9 = new Class3_Sub28_Sub16_Sub1_Sub1(var7); - } else { - var9 = new HDSprite(var7); - } - } else { - var9 = var7; - } - - Class114.aReferenceCache_1569.put(var9, var4); - return (AbstractSprite) var9; - } - } else { - return var6; - } - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "be.O(" + (byte) -113 + ',' + var2 + ')'); - } - } - - final void decodeScriptFormat(DataBuffer buffer) { - this.usingScripts = true; - buffer.index++; - this.type = buffer.readUnsignedByte(); - if ((128 & this.type) != 0) { - this.type &= 127; - buffer.readString(); - } - - this.anInt189 = buffer.readUnsignedShort(); - this.defX = buffer.readSignedShort(); - this.defY = buffer.readSignedShort(); - this.defWidth = buffer.readUnsignedShort(); - this.defHeight = buffer.readUnsignedShort(); - this.horizontalResize = buffer.readSignedByte(); - this.verticalResize = buffer.readSignedByte(); - this.horizontalPos = buffer.readSignedByte(); - this.verticalPos = buffer.readSignedByte(); - this.parentId = buffer.readUnsignedShort(); - if (this.parentId == 65535) { - this.parentId = -1; - } else { - this.parentId = (this.componentHash & -65536) - -this.parentId; - } - - this.hidden = buffer.readUnsignedByte() == 1; - if (this.type == 0) { - this.anInt240 = buffer.readUnsignedShort(); - this.anInt252 = buffer.readUnsignedShort(); - this.aBoolean219 = buffer.readUnsignedByte() == 1; - } - - int var3; - if (this.type == 5) { - this.spriteArchiveId = buffer.readInt(); - this.anInt301 = buffer.readUnsignedShort(); - var3 = buffer.readUnsignedByte(); - this.aBoolean157 = (2 & var3) != 0; - this.aBoolean186 = (1 & var3) != 0; - this.anInt223 = buffer.readUnsignedByte(); - this.anInt288 = buffer.readUnsignedByte(); - this.anInt287 = buffer.readInt(); - this.aBoolean178 = buffer.readUnsignedByte() == 1; - this.aBoolean199 = 1 == buffer.readUnsignedByte(); - } - - if (this.type == 6) { - this.modelType = 1; - this.itemId = buffer.readUnsignedShort(); - if (this.itemId == 65535) { - this.itemId = -1; - } - - this.anInt259 = buffer.readSignedShort(); - this.anInt230 = buffer.readSignedShort(); - this.anInt182 = buffer.readUnsignedShort(); - this.anInt308 = buffer.readUnsignedShort(); - this.anInt280 = buffer.readUnsignedShort(); - this.anInt164 = buffer.readUnsignedShort(); - this.animationId = buffer.readUnsignedShort(); - if (65535 == this.animationId) { - this.animationId = -1; - } - - this.aBoolean181 = buffer.readUnsignedByte() == 1; - this.aShort293 = (short) buffer.readUnsignedShort(); - this.aShort169 = (short) buffer.readUnsignedShort(); - this.aBoolean309 = 1 == buffer.readUnsignedByte(); - if (this.horizontalResize != 0) { - this.anInt184 = buffer.readUnsignedShort(); - } - - if (this.verticalResize != 0) { - this.anInt312 = buffer.readUnsignedShort(); - } - } - - if (this.type == 4) { - this.anInt270 = buffer.readUnsignedShort(); - if (this.anInt270 == 65535) { - this.anInt270 = -1; - } - - this.text = buffer.readString(); - this.anInt205 = buffer.readUnsignedByte(); - this.anInt194 = buffer.readUnsignedByte(); - this.anInt225 = buffer.readUnsignedByte(); - this.aBoolean215 = buffer.readUnsignedByte() == 1; - this.anInt218 = buffer.readInt(); - } - - if (this.type == 3) { - this.anInt218 = buffer.readInt(); - this.aBoolean226 = 1 == buffer.readUnsignedByte(); - this.anInt223 = buffer.readUnsignedByte(); - } - - if (this.type == 9) { - this.anInt250 = buffer.readUnsignedByte(); - this.anInt218 = buffer.readInt(); - this.aBoolean167 = 1 == buffer.readUnsignedByte(); - } - - var3 = buffer.readMedium(); - int var4 = buffer.readUnsignedByte(); - int var5; - if (var4 != 0) { - this.anIntArray299 = new int[10]; - this.aByteArray263 = new byte[10]; - - for (this.aByteArray231 = new byte[10]; var4 != 0; var4 = buffer.readUnsignedByte()) { - var5 = (var4 >> 4) - 1; - var4 = buffer.readUnsignedByte() | var4 << 8; - var4 &= 4095; - if (4095 == var4) { - this.anIntArray299[var5] = -1; - } else { - this.anIntArray299[var5] = var4; - } - - this.aByteArray263[var5] = buffer.readSignedByte(); - this.aByteArray231[var5] = buffer.readSignedByte(); - } - } - - this.aClass94_277 = buffer.readString(); - var5 = buffer.readUnsignedByte(); - int var6 = var5 & 15; - int var8; - if (0 < var6) { - this.aClass94Array171 = new RSString[var6]; - - for (var8 = 0; var6 > var8; ++var8) { - this.aClass94Array171[var8] = buffer.readString(); - } - } - - - int var7 = var5 >> 4; - if (var7 > 0) { - var8 = buffer.readUnsignedByte(); - this.anIntArray249 = new int[var8 + 1]; - - for (int var9 = 0; var9 < this.anIntArray249.length; ++var9) { - this.anIntArray249[var9] = -1; - } - - this.anIntArray249[var8] = buffer.readUnsignedShort(); - } - - if (1 < var7) { - var8 = buffer.readUnsignedByte(); - this.anIntArray249[var8] = buffer.readUnsignedShort(); - } - - this.anInt214 = buffer.readUnsignedByte(); - this.anInt179 = buffer.readUnsignedByte(); - this.aBoolean200 = buffer.readUnsignedByte() == 1; - var8 = -1; - this.aClass94_245 = buffer.readString(); - if (0 != (127 & var3 >> 11)) { - var8 = buffer.readUnsignedShort(); - this.anInt266 = buffer.readUnsignedShort(); - if (var8 == 65535) { - var8 = -1; - } - - if (65535 == this.anInt266) { - this.anInt266 = -1; - } - - this.anInt238 = buffer.readUnsignedShort(); - if (this.anInt238 == 65535) { - this.anInt238 = -1; - } - } - - this.aClass3_Sub1_257 = new Class3_Sub1(var3, var8); - this.anObjectArray159 = this.method862(buffer); - this.anObjectArray248 = this.method862(buffer); - this.anObjectArray281 = this.method862(buffer); - this.anObjectArray303 = this.method862(buffer); - this.anObjectArray203 = this.method862(buffer); - this.anObjectArray282 = this.method862(buffer); - this.anObjectArray174 = this.method862(buffer); - this.anObjectArray158 = this.method862(buffer);//.? - this.anObjectArray269 = this.method862(buffer); - this.anObjectArray314 = this.method862(buffer); - this.anObjectArray276 = this.method862(buffer); - this.anObjectArray165 = this.method862(buffer); - this.anObjectArray170 = this.method862(buffer); - this.anObjectArray239 = this.method862(buffer); - this.anObjectArray180 = this.method862(buffer); - this.anObjectArray295 = this.method862(buffer); - this.anObjectArray229 = this.method862(buffer); - this.anObjectArray183 = this.method862(buffer); - this.anObjectArray161 = this.method862(buffer); - this.anObjectArray221 = this.method862(buffer); - this.anIntArray286 = this.method863(buffer); - this.anIntArray175 = this.method863(buffer); - this.anIntArray274 = this.method863(buffer); - this.anIntArray211 = this.method863(buffer); - this.anIntArray185 = this.method863(buffer); - } - - final Font method868(AbstractIndexedSprite[] var1) { - try { - aBoolean6 = false; - if (this.anInt270 == -1) { - return null; - } else { - Font var3 = (Font) Unsorted.aReferenceCache_743.get(this.anInt270); - if (null == var3) { - var3 = Unsorted.method1300(this.anInt270, Class12.aClass153_323, Class97.aClass153_1378); - if (null == var3) { - aBoolean6 = true; - } else { - var3.method697(var1, null); - Unsorted.aReferenceCache_743.put(var3, this.anInt270); - } - - } - return var3; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "be.A(" + (var1 != null ? "{...}" : "null") + ',' + 0 + ')'); - } - } - - static int method869(int var1) { - try { - return var1 != 16711935 ? Class56.method1186(var1) : -1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "be.D(" + 116 + ',' + var1 + ')'); - } - } - - public RSInterface() { - this.aClass94_243 = TextCore.aClass94_2171; - this.aBoolean163 = false; - this.anInt225 = 0; - this.anInt212 = -1; - this.aBoolean167 = false; - this.anInt266 = -1; - this.verticalResize = 0; - this.anInt252 = 0; - this.aBoolean200 = false; - this.aBoolean215 = false; - this.anInt204 = -1; - this.anInt260 = 1; - this.anInt228 = 0; - this.usingScripts = false; - this.aClass3_Sub1_257 = Class158_Sub1.aClass3_Sub1_2980; - this.anInt253 = 0; - this.text = TextCore.aClass94_2171; - this.width = 0; - this.anInt247 = 0; - this.aBoolean219 = false; - this.secondModelId = -1; - this.parentId = -1; - this.anInt216 = 1; - this.anInt192 = -1; - this.anInt222 = 0; - this.anInt264 = 0; - this.aClass94_277 = TextCore.aClass94_2171; - this.anInt284 = 0; - this.defWidth = 0; - this.anInt285 = 0; - this.anInt234 = -1; - this.aBoolean157 = false; - this.anInt184 = 0; - this.anInt223 = 0; - this.anInt258 = 0; - this.aClass94_245 = TextCore.aClass94_2171; - this.anInt237 = 0; - this.aClass94_172 = TextCore.aClass94_2171; - this.anInt288 = 0; - this.anInt265 = -1; - this.anInt242 = 0; - this.anInt259 = 0; - this.anInt290 = 0; - this.defHeight = 0; - this.componentHash = -1; - this.anInt296 = -1; - this.horizontalPos = 0; - this.anInt267 = 0; - this.anInt270 = -1; - this.anInt240 = 0; - this.anInt255 = 0; - this.aShort293 = 0; - this.anInt301 = 0; - this.animationId = -1; - this.aClass94_289 = TextCore.HasOK; - this.anInt280 = 0; - this.anInt271 = 0; - this.anInt292 = -1; - this.anInt189 = 0; - this.anInt287 = 0; - this.aClass11_302 = null; - this.anInt311 = 0; - this.modelType = 1; - this.aBoolean309 = false; - this.horizontalResize = 0; - this.secondModelType = 1; - this.anInt312 = 0; - this.anInt308 = 0; - this.aBoolean195 = false; - this.defX = 0; - this.anInt306 = 0; - this.defY = 0; - this.aBoolean227 = true; - this.anInt283 = 0; - this.anInt213 = 0; - this.anInt218 = 0; - this.anInt318 = 0; - } - -} diff --git a/Client/src/main/java/org/runite/client/RSString.java b/Client/src/main/java/org/runite/client/RSString.java deleted file mode 100644 index 490d89025..000000000 --- a/Client/src/main/java/org/runite/client/RSString.java +++ /dev/null @@ -1,1082 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; -import org.rs09.client.data.HashTable; -import org.rs09.client.util.ArrayUtils; - -import java.applet.Applet; -import java.awt.*; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -public final class RSString implements Interface3 { - - static HashTable interned; - byte[] buffer; - int length; - private boolean mutable = true; - - /** - * @return A RSString consisting of the actual bytes in the provided string. - */ - public static RSString of(String string) { - byte[] bytes = string.getBytes(StandardCharsets.ISO_8859_1); - - RSString rs = new RSString(); - rs.buffer = bytes; - rs.length = 0; - - for (int i = 0; i < bytes.length; ++i) { - if (bytes[i] != 0) { - bytes[rs.length++] = bytes[i]; - } - } - - return rs; - } - - /** - * @return A RSString that is interned and parsed. - */ - public static RSString parse(String string) { - byte[] bytes = Objects.requireNonNull(string).replace("RuneScape", GameConfig.SERVER_NAME).getBytes(); - - int length = bytes.length; - RSString jagString = new RSString(); - jagString.buffer = new byte[length]; - - int i = 0; - while (length > i) { - int ascii = bytes[i++] & 0xFF; - if (ascii <= 45 && ascii >= 40) { - if (length <= i) { - break; - } - - int var7 = bytes[i++] & 0xff; - jagString.buffer[jagString.length++] = (byte) (-48 + var7 + 43 * (-40 + ascii)); - } else if (ascii != 0) { - jagString.buffer[jagString.length++] = (byte) ascii; - } - } - jagString.method1576(); - return jagString.intern(); - } - - static RSString stringCombiner(RSString[] var0) { - if (var0.length >= 2) { - return Class67.method1261(0, var0.length, var0); - } else { - throw new IllegalArgumentException(); - } - } - - static RSString stringAnimator(int var1) { - try { - return Unsorted.method1723((byte) -117, false, var1); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "jj.C(" + var1 + ')'); - } - } - - public final URL toURL() throws MalformedURLException { - return new URL(new String(this.buffer, 0, this.length)); - } - - public final boolean equalsStringIgnoreCase(RSString other) { - if (other == null) { - return false; - } else if (this.length == other.length) { - - for (int i = 0; i < this.length; ++i) { - byte var5 = this.buffer[i]; - if (var5 >= 65 && var5 <= 90 || var5 >= -64 && var5 <= -34 && var5 != -41) { - var5 = (byte) (var5 + 32); - } - - byte var6 = other.buffer[i]; - if (65 <= var6 && var6 <= 90 || -64 <= var6 && var6 <= -34 && var6 != -41) { - var6 = (byte) (var6 + 32); - } - - if (var6 != var5) { - return false; - } - } - - return true; - } else { - return false; - } - } - - // TODO Refactor to support regular equals method - public final boolean equalsString(RSString other) { - if (other == null) { - return false; - } else if (this == other) { - return true; - } else if (this.length == other.length) { - - byte[] otherBuffer = other.buffer; - byte[] thisBuffer = this.buffer; - - for (int i = 0; i < this.length; ++i) { - if (thisBuffer[i] != otherBuffer[i]) { - return false; - } - } - - return true; - } else { - return false; - } - } - - public final int parseInt(int radix) { - if (radix < 1 || radix > 36) { - radix = 10; - } - - boolean negate = false; - boolean success = false; - int value = 0; - - for (int i = 0; this.length > i; ++i) { - int currentNumber = this.buffer[i] & 0xff; - - if (i == 0) { - if (currentNumber == '-') { - negate = true; - continue; - } - - if (currentNumber == '+') { - continue; - } - } - - if (currentNumber >= '0' && '9' >= currentNumber) { - currentNumber -= '0'; - } else if ('A' <= currentNumber && currentNumber <= 'Z') { - currentNumber -= 55; - } else { - if (currentNumber < 'a' || currentNumber > 'z') { - throw new NumberFormatException("invalid character in number with value: " + currentNumber + " ('" + ((char) currentNumber) + "'). This string: " + toString()); - } - - currentNumber -= 87; - } - - if (currentNumber >= radix) { - throw new NumberFormatException("got radix " + radix + ", but found number value " + currentNumber); - } - - if (negate) { - currentNumber = -currentNumber; - } - - int newValue = currentNumber + value * radix; - if (newValue / radix != value) { - throw new NumberFormatException("integer overflow"); - } - - value = newValue; - success = true; - } - - if (success) { - return value; - } else { - throw new NumberFormatException("failed to parse number"); - } - } - - public final void drawString(Graphics g, int y, int x) { - String string = new String(this.buffer, 0, this.length, StandardCharsets.ISO_8859_1); - g.drawString(string, x, y); - } - - final void append(RSString var1) { - if (this.mutable) { - if (var1.length + this.length > this.buffer.length) { - int var3; - for (var3 = 1; var3 < var1.length + this.length; var3 += var3) { - } - - byte[] var4 = new byte[var3]; - ArrayUtils.arraycopy(this.buffer, 0, var4, 0, this.length); - this.buffer = var4; - } - - ArrayUtils.arraycopy(var1.buffer, 0, this.buffer, this.length, var1.length); - this.length += var1.length; - } else { - throw new IllegalArgumentException(); - } - } - - public final RSString toLowercase() { - RSString lower = new RSString(); - lower.length = this.length; - lower.buffer = new byte[this.length]; - - for (int i = 0; i < this.length; ++i) { - byte ch = this.buffer[i]; - if (65 <= ch && ch <= 90 || ch >= -64 && ch <= -34 && ch != -41) { - ch = (byte) (ch + 32); - } - - lower.buffer[i] = ch; - } - - return lower; - } - - /** - * Returns a copy of this string with proper capitalization. Generally, this returns an all-lower string. Characters - * are changed to uppercase based on the following rules: - * - The character is the first character in the string - * - The character is the first non-whitespace character after a '.', '!' or '?' character - *

- * Characters may be either lowercase or uppercase if the previous character is a whitespace character. - * - * @return A copy of this string with capitalization based on the rules above. - */ - public final RSString properlyCapitalize() { - byte state = 2; - RSString newString = new RSString(); - newString.length = this.length; - newString.buffer = new byte[this.length]; - - for (int i = 0; i < this.length; ++i) { - byte ch = this.buffer[i]; - if ((ch < 97 || 122 < ch) && (ch < -32 || ch > -2 || ch == -9)) { // if not lowercase chars - if ((ch < 65 || ch > 90) && (ch < -64 || ch > -34 || ch == -41)) { // if not uppercase chars - if (ch != 46 && 33 != ch && ch != 63) { // if not `.`, `!` or `?` - if (32 == ch) { // if ' ' - if (state != 2) { - state = 1; - } - } else { // if not ' ' - state = 1; - } - } else { // if `.`, `!` or `?` - state = 2; - } - } else { // if uppercase char - if (0 == state) { - ch = (byte) (ch + 32); - } - - state = 0; - } - } else { - if (state == 2) { - ch = (byte) (ch - 32); - } - - state = 0; - } - - newString.buffer[i] = ch; - } - - return newString; - } - - public final long hash() { - long hash = 0L; - for (int i = 0; i < this.length; ++i) { - hash = (long) (this.buffer[i] & 0xff) + (hash << 5) - hash; - } - - return hash; - } - - public final int length() { - return this.length; - } - - public final RSString method1542(RSString var2, int var3, int var4) { - if (!this.mutable) { - throw new IllegalArgumentException(); - } else if (0 <= var3 && var3 <= var4 && var2.length >= var4) { - if (this.length + (var4 - var3) > this.buffer.length) { - int var5; - for (var5 = 1; var5 < this.length + var2.length; var5 += var5) { - } - - byte[] var6 = new byte[var5]; - ArrayUtils.arraycopy(this.buffer, 0, var6, 0, this.length); - this.buffer = var6; - } - - ArrayUtils.arraycopy(var2.buffer, var3, this.buffer, this.length, -var3 + var4); - this.length += var4 + -var3; - return this; - } else { - throw new IllegalArgumentException(); - } - } - - final boolean isInteger() { - return this.isInteger(10); - } - - final RSString method1544(boolean var1) { - try { - RSString var2 = new RSString(); - var2.length = this.length; - var2.buffer = new byte[var2.length]; - if (var1) { - for (int var3 = 0; this.length > var3; ++var3) { - var2.buffer[this.length - var3 + -1] = this.buffer[var3]; - } - - return var2; - } else { - return null; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "na.FB(" + var1 + ')'); - } - } - - final RSString longToRSString() { - try { - RSString var2 = new RSString(); - var2.length = this.length; - var2.buffer = new byte[this.length]; - boolean var3 = true; - int var4 = 0; - - for (; var4 < this.length; ++var4) { - byte var5 = this.buffer[var4]; - if (var5 == 95) { - var3 = true; - var2.buffer[var4] = 32; - } else if (97 <= var5 && var5 <= 122 && var3) { - var3 = false; - var2.buffer[var4] = (byte) (-32 + var5); - } else { - var2.buffer[var4] = var5; - var3 = false; - } - } - - return var2; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "na.G(" + (byte) -50 + ')'); - } - } - - final int method1546(RSString var2) { - int var3 = 0; - int var4 = 0; - int var6 = var2.length; - int var5 = this.length; - int var7 = this.length; - int var8 = var2.length; - int var9 = 0; - int var10 = 0; - - while (var5 != 0 && var6 != 0) { - if (var3 == 156 || var3 == 230) { - var3 = 101; - } else if (140 == var3 || var3 == 198) { - var3 = 69; - } else if (var3 == 223) { - var3 = 115; - } else { - var3 = this.buffer[var9] & 0xFF; - ++var9; - } - - if (Unsorted.method2103(var3, -116)) { - ++var7; - } else { - --var5; - } - - if (var4 == 156 || 230 == var4) { - var4 = 101; - } else if (var4 == 140 || var4 == 198) { - var4 = 69; - } else if (223 == var4) { - var4 = 115; - } else { - var4 = 255 & var2.buffer[var10]; - ++var10; - } - - if (Unsorted.method2103(var4, -86)) { - ++var8; - } else { - --var6; - } - - if (Class158.anIntArray2004[var4] > Class158.anIntArray2004[var3]) { - return -1; - } - - if (Class158.anIntArray2004[var3] > Class158.anIntArray2004[var4]) { - return 1; - } - } - - return var8 <= var7 ? (var7 > var8 ? 1 : 0) : -1; - } - - final URL method1547(URL var1) throws MalformedURLException { - try { - - return new URL(var1, new String(this.buffer, 0, this.length)); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "na.EB(" + (var1 != null ? "{...}" : "null") + ',' + true + ')'); - } - } - - final RSString method1548(int var2) { - try { - if (var2 > 0 && var2 <= 255) { - RSString var3 = new RSString(); - var3.buffer = new byte[1 + this.length]; - var3.length = this.length + 1; - ArrayUtils.arraycopy(this.buffer, 0, var3.buffer, 0, this.length); - var3.buffer[this.length] = (byte) var2; - return var3; - } else { - throw new IllegalArgumentException("invalid char"); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "na.OA(" + false + ',' + var2 + ')'); - } - } - - final boolean endsWith(RSString var2) { - if (var2.length > this.length) { - return false; - } else { - int var3 = -var2.length + this.length; - - for (int var4 = 0; var4 < var2.length; ++var4) { - if (this.buffer[var3 + var4] != var2.buffer[var4]) { - return false; - } - } - - return true; - } - } - - final int indexOf(RSString var1, int var2) { - try { - return var2 <= 49 ? -20 : this.method1566(var1, 0); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "na.A(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ')'); - } - } - - public final int parseInt() { - return this.parseInt(10); - } - - final void method1553(int var1) { - try { - if (!this.mutable) { - throw new IllegalArgumentException(); - } else if (var1 < 0) { - throw new IllegalArgumentException(); - } else { - int var3; - if (this.buffer.length < var1) { - for (var3 = 1; var1 > var3; var3 += var3) { - } - - byte[] var4 = new byte[var3]; - ArrayUtils.arraycopy(this.buffer, 0, var4, 0, this.length); - this.buffer = var4; - } - - for (var3 = this.length; var1 > var3; ++var3) { - this.buffer[var3] = 32; - } - - this.length = var1; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "na.RA(" + var1 + ',' + false + ')'); - } - } - - public final String toString() { - if (buffer == null) { - throw new RuntimeException(); - } - return new String(buffer); - } - - final void method1554(Applet var2) throws Throwable { - try { - String var3 = new String(this.buffer, 0, this.length); - Class42.method1057(var2, var3); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "na.AA(" + true + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final int method1555(int var1, int var2) { - try { - byte var4 = (byte) var1; - for (int var5 = var2; this.length > var5; ++var5) { - if (this.buffer[var5] == var4) { - return var5; - } - } - - return -1; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "na.NA(" + var1 + ',' + var2 + ',' + 1536 + ')'); - } - } - - final RSString substring(int start) { - return this.substring(start, this.length, (byte) -74 ^ -74); - } - - // TODO Why is `dstpos` a thing? what good does it serve? - public final RSString substring(int start, int end, int dstpos) { - RSString subs = new RSString(); - subs.length = end - start; - subs.buffer = new byte[end - start]; - ArrayUtils.arraycopy(this.buffer, start, subs.buffer, dstpos, subs.length); - return subs; - } - - final boolean startsWith(RSString other) { - if (other.length > this.length) { - return false; - } - - for (int i = 0; i < other.length; ++i) { - if (other.buffer[i] != this.buffer[i]) { - return false; - } - } - - return true; - } - - public final boolean equals(Object var1) { - try { - if (var1 instanceof RSString) { - return this.equalsString((RSString) var1); - } else { - throw new IllegalArgumentException(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "na.equals(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - final int method1559(RSString var1) { - try { - - int var3; - if (var1.length < this.length) { - var3 = var1.length; - } else { - var3 = this.length; - } - - for (int var4 = 0; var3 > var4; ++var4) { - if ((255 & this.buffer[var4]) < (var1.buffer[var4] & 0xFF)) { - return -1; - } - - if ((this.buffer[var4] & 0xFF) > (var1.buffer[var4] & 0xFF)) { - return 1; - } - } - - if (var1.length > this.length) { - return -1; - } else if (this.length <= var1.length) { - return 0; - } else { - return 1; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "na.QA(" + (var1 != null ? "{...}" : "null") + ',' + -1 + ')'); - } - } - - final RSString method1560(RSString var1, RSString var3) { - try { - int var4 = this.length; - int var5 = var1.length - var3.length; - int var6 = 0; - - while (true) { - int var7 = this.method1566(var3, var6); - if (0 > var7) { - var6 = 0; - RSString var10 = Unsorted.emptyString(var4); - - while (true) { - int var8 = this.method1566(var3, var6); - if (0 > var8) { - while (this.length > var6) { - Objects.requireNonNull(var10).appendCharacter(255 & this.buffer[var6++]); - } - - if (false) { - this.method1567(-5, (byte) -91); - } - - return var10; - } - - while (var6 < var8) { - Objects.requireNonNull(var10).appendCharacter(this.buffer[var6++] & 0xFF); - } - - Objects.requireNonNull(var10).append(var1); - var6 += var3.length; - } - } - - var6 = var7 - -var3.length; - var4 += var5; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "na.IA(" + (var1 != null ? "{...}" : "null") + ',' + true + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - public final int hashCode() { - try { - return this.method1574(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "na.hashCode()"); - } - } - - private boolean isInteger(int radix) { - if (radix < 1 || radix > 36) { - radix = 10; - } - - boolean success = false; - boolean negate = false; - int value = 0; - - for (int i = 0; i < this.length; ++i) { - int current = this.buffer[i] & 0xFF; - if (0 == i) { - if (current == 45) { - negate = true; - continue; - } - - if (current == 43) { - continue; - } - } - - if (current >= 48 && current <= 57) { - current -= 48; - } else if (current >= 65 && current <= 90) { - current -= 55; - } else { - if (97 > current || current > 122) { - return false; - } - - current -= 87; - } - - if (radix <= current) { - return false; - } - - if (negate) { - current = -current; - } - - int newValue = current + radix * value; - if (newValue / radix != value) { - return false; - } - - value = newValue; - success = true; - } - - return success; - } - - final boolean method1562(byte var1, RSString var2) { - try { - if (this.length < var2.length) { - return false; - } else { - if (var1 != -32) { - this.length = 13; - } - - for (int var3 = 0; var2.length > var3; ++var3) { - byte var4 = this.buffer[var3]; - byte var5 = var2.buffer[var3]; - if (var5 >= 65 && var5 <= 90 || -64 <= var5 && -34 >= var5 && -41 != var5) { - var5 = (byte) (var5 + 32); - } - - if (65 <= var4 && var4 <= 90 || var4 >= -64 && -34 >= var4 && var4 != -41) { - var4 = (byte) (var4 + 32); - } - - if (var5 != var4) { - return false; - } - } - - return true; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "na.HB(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final RSString method1563(int var1) { - try { - if (var1 <= 86) { - this.trim(117); - } - - return this; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "na.K(" + var1 + ')'); - } - } - - final RSString trim(int var1) { - try { - if (var1 != 1) { - Unsorted.method1535(null, null, 23, 68, 126, false, false); - } - - int var2; - for (var2 = 0; var2 < this.length && (0 <= this.buffer[var2] && 32 >= this.buffer[var2] || (255 & this.buffer[var2]) == 160); ++var2) { - } - - int var3; - for (var3 = this.length; var3 > var2 && (this.buffer[var3 - 1] >= 0 && this.buffer[var3 - 1] <= 32 || (255 & this.buffer[var3 + -1]) == 160); --var3) { - } - - if (var2 == 0 && var3 == this.length) { - return this; - } else { - RSString var4 = new RSString(); - var4.length = var3 + -var2; - var4.buffer = new byte[var4.length]; - - if (var4.length >= 0) System.arraycopy(this.buffer, var2, var4.buffer, 0, var4.length); - - return var4; - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "na.KA(" + var1 + ')'); - } - } - - final RSString method1565() { - try { - byte var4 = (byte) 47; - RSString var6 = new RSString(); - byte var5 = (byte) 32; - var6.length = this.length; - var6.buffer = new byte[this.length]; - - for (int var7 = 0; var7 < this.length; ++var7) { - byte var8 = this.buffer[var7]; - if (var4 == var8) { - var6.buffer[var7] = var5; - } else { - var6.buffer[var7] = var8; - } - } - - return var6; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "na.HA(" + 32 + ',' + 40 + ',' + 47 + ')'); - } - } - - final int method1566(RSString var1, int var2) { - try { - int var4 = var1.length; - if (var2 >= this.length) { - return var4 == 0 ? this.length : -1; - } else { - if (var2 < 0) { - var2 = 0; - } - - if (var4 == 0) { - return var2; - } else { - int var7 = this.length - var4; - byte[] var5 = var1.buffer; - byte var6 = var5[0]; - int var8 = var2; - - while (var7 >= var8) { - if (this.buffer[var8] != var6) { - do { - ++var8; - if (var8 > var7) { - return -1; - } - } while (this.buffer[var8] != var6); - } - - boolean var9 = true; - int var10 = 1 + var8; - int var11 = 1; - - while (true) { - if (var11 < var4) { - if (this.buffer[var10] == var5[var11]) { - ++var10; - ++var11; - continue; - } - - var9 = false; - } - - if (var9) { - return var8; - } - - ++var8; - break; - } - } - - return -1; - } - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "na.CB(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + -1 + ')'); - } - } - - final RSString[] method1567(int var1, byte var2) { - try { - int var3 = 0; - - for (int var4 = 0; var4 < this.length; ++var4) { - if (this.buffer[var4] == var1) { - ++var3; - } - } - - RSString[] var11 = new RSString[1 + var3]; - if (var3 == 0) { - var11[0] = this; - } else { - int var5 = 0; - int var6 = 0; - - for (int var7 = 0; var3 > var7; ++var7) { - int var9; - for (var9 = 0; this.buffer[var9 + var6] != var1; ++var9) { - } - - var11[var5++] = this.substring(var6, var6 - -var9, 0); - var6 += 1 + var9; - } - - var11[var3] = this.substring(var6, this.length, 0); - } - return var11; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "na.GA(" + var1 + ',' + var2 + ')'); - } - } - - final byte[] method1568() { - try { - byte[] var2 = new byte[this.length]; - ArrayUtils.arraycopy(this.buffer, 0, var2, 0, this.length); - return var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "na.H(" + 0 + ')'); - } - } - - final int charAt(int var1, byte var2) { - try { - return this.buffer[var1] & 0xFF; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "na.SA(" + var1 + ',' + var2 + ')'); - } - } - - public final RSString intern() { - final long hash = this.hash(); - synchronized (RSString.class) { - LinkableRSString linkable; - if (interned == null) { - interned = new HashTable<>(4096); - } else { - for (linkable = interned.get(hash); null != linkable; linkable = interned.nextInBucket()) { - if (this.equalsString(linkable.value)) { - return linkable.value; - } - } - } - - linkable = new LinkableRSString(); - - linkable.value = this; - mutable = false; - interned.put(hash, linkable); - } - return this; - } - - final void appendCharacter(int character) { - if (0 < character && character <= 255) { - if (this.mutable) { - if (this.length == this.buffer.length) { - // TODO Is this really just making a new buffer with the size + 1? Why do we have to loop for that? - int newBufferSize; - for (newBufferSize = 1; this.length >= newBufferSize; newBufferSize += newBufferSize) { - } - - byte[] newBuffer = new byte[newBufferSize]; - ArrayUtils.arraycopy(this.buffer, 0, newBuffer, 0, this.length); - this.buffer = newBuffer; - } - - this.buffer[this.length++] = (byte) character; - } else { - throw new IllegalArgumentException(); - } - } else { - throw new IllegalArgumentException("invalid char:" + character); - } - } - - final RSString getParamValue(Applet applet) { - String string = new String(this.buffer, 0, this.length); - String paramValue = applet.getParameter(string); - return null == paramValue ? null : of(paramValue); - } - - final int method1574() { - try { - int var2 = 0; - - for (int var3 = 0; var3 < this.length; ++var3) { - var2 = (255 & this.buffer[var3]) + -var2 + (var2 << 5); - } - - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "na.J(" + false + ')'); - } - } - - public final int method1575(FontMetrics var2) { - try { - String var3; - var3 = new String(this.buffer, 0, this.length, StandardCharsets.ISO_8859_1); - - return var2.stringWidth(var3); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "na.V(" + -21018 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - final RSString method1576() { - try { - if (this.mutable) { - - if (this.buffer.length != this.length) { - byte[] var2 = new byte[this.length]; - ArrayUtils.arraycopy(this.buffer, 0, var2, 0, this.length); - this.buffer = var2; - } - - return this; - } else { - throw new IllegalArgumentException(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "na.PA(" + (byte) 90 + ')'); - } - } - - final Object method1577(Applet var2) throws Throwable { - try { - String var3 = new String(this.buffer, 0, this.length); - /*Object var4 = Class42.method1055(var3, var2); - if (var4 instanceof String) { - byte[] var5 = ((String) var4).getBytes(); - var4 = Class3_Sub13_Sub3.method178(var5, var5.length, 0); - } -*/ - return null; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "na.JA(" + -1857 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - public final long toLong() { - long var2 = 0L; - for (int var4 = 0; var4 < this.length && var4 < 12; ++var4) { - byte var5 = this.buffer[var4]; - var2 *= 37L; - if (65 <= var5 && 90 >= var5) { - var2 += -65 + 1 + var5; - } else if (var5 >= 97 && 122 >= var5) { - var2 += -97 + var5 + 1; - } else if (var5 >= 48 && var5 <= 57) { - var2 += -48 + var5 + 27; - } - } - - while (var2 % 37L == 0 && var2 != 0L) { - var2 /= 37L; - } - - return var2; - } - - final RSString method1579() { - try { - RSString var2 = Unsorted.method1052(this.toLong()); - return null == var2 ? TextCore.aClass94_1760 : var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "na.Q(" + -17 + ')'); - } - } - - public final int method1580(byte[] var2, int var3, int var5) { - try { - ArrayUtils.arraycopy(this.buffer, 0, var2, var3, var5); - - return var5; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "na.LA(" + true + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + 0 + ',' + var5 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/RandomAccessFileWrapper.java b/Client/src/main/java/org/runite/client/RandomAccessFileWrapper.java deleted file mode 100644 index aada186d0..000000000 --- a/Client/src/main/java/org/runite/client/RandomAccessFileWrapper.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.runite.client; - -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; - -public final class RandomAccessFileWrapper { - - private final long limit; - private final File path; - private RandomAccessFile raf; - private long pos; - - - public final void seek(long pos) throws IOException { - this.raf.seek(pos); - this.pos = pos; - } - - protected void finalize() throws Throwable { - if (this.raf != null) { - System.out.println("Warning! fileondisk " + this.path + " not closed correctly using close(). Auto-closing instead. "); - this.close(); - } - } - - public final void write(byte[] buffer, int length, int offset) throws IOException { - if (this.limit < this.pos + (long) length) { - this.raf.seek(1L + this.limit); - this.raf.write(1); - throw new EOFException(); - } else { - this.raf.write(buffer, offset, length); - this.pos += length; - } - } - - public final int read(byte[] buffer, int offset, int length, int minBytesRead) throws IOException { - int bytesRead = this.raf.read(buffer, offset, length); - if (bytesRead > minBytesRead) { - this.pos += bytesRead; - } - - return bytesRead; - } - - public final void close() throws IOException { - if (this.raf != null) { - this.raf.close(); - this.raf = null; - } - } - - public final long getLength() throws IOException { - return this.raf.length(); - } - - public final File getPath() { - return this.path; - } - - public RandomAccessFileWrapper(File path, String mode, long limit) throws IOException { - if (limit == -1) { - limit = Long.MAX_VALUE; - } - - if (path.length() >= limit) { - path.delete(); - } - - this.raf = new RandomAccessFile(path, mode); - this.path = path; - this.limit = limit; - this.pos = 0L; - int var5 = this.raf.read(); - if (var5 != -1 && !mode.equals("r")) { - this.raf.seek(0L); - this.raf.write(var5); - } - - this.raf.seek(0L); - } -} diff --git a/Client/src/main/java/org/runite/client/RenderAnimationDefinition.java b/Client/src/main/java/org/runite/client/RenderAnimationDefinition.java deleted file mode 100644 index 91d7f018e..000000000 --- a/Client/src/main/java/org/runite/client/RenderAnimationDefinition.java +++ /dev/null @@ -1,330 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -final class RenderAnimationDefinition { - - static int[] anIntArray356 = new int[]{1, 0, -1, 0}; - static ReferenceCache aReferenceCache_1955 = new ReferenceCache(64); - int yaw_max_speed = 0; - int[][] equipment_transforms; - int movement_acceleration = -1; - static volatile int anInt362 = 0; - int walk_follow_cw_turn_anim = -1; - int standing_ccw_turn = -1; - int stand_animation = -1; - int yaw_acceleration = 0; - int roll_max_speed = 0; - int pitch_target_angle = 0; - int slow_walk_follow_full_turn_anim = -1; - int run_follow_ccw_turn_anim = -1; - int run_follow_cw_turn_anim = -1; - static RSString aClass94_378 = null; - int slow_walk_follow_cw_turn_anim = -1; - int hill_height = 0; - int walk_animation = -1; - static byte[][][] aByteArrayArrayArray383; - static int anInt384 = 0; - int run_follow_full_turn_anim = -1; - int roll_acceleration = 0; - int walk_follow_full_turn_anim = -1; - int walk_follow_ccw_turn_anim = -1; - int run_anim = -1; - int hill_width = 0; - static int anInt396; - int slow_walk_anim = -1; - int pitch_max_speed = 0; - int roll_target_angle = 0; - static boolean aBoolean402 = false; - int pitch_acceleration = 0; - int slow_walk_follow_ccw_turn_anim = -1; - int standing_cw_turn = -1; - - final void method899() { - try { - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ck.B(" + 96 + ')'); - } - } - - static void method900(Class140_Sub4 var0) { - try { - var0.aBoolean2810 = false; - SequenceDefinition var2; - if (-1 != var0.anInt2764) { - var2 = SequenceDefinition.getAnimationDefinition(var0.anInt2764); - if (null == var2.frames) { - var0.anInt2764 = -1; - } else { - ++var0.anInt2802; - if (var0.anInt2813 < var2.frames.length && var0.anInt2802 > var2.duration[var0.anInt2813]) { - var0.anInt2802 = 1; - ++var0.anInt2813; - ++var0.anInt2793; - Unsorted.method1470(var0.zAxis, var2, var0.xAxis, var0 == Class102.player, var0.anInt2813); - } - - if (var2.frames.length <= var0.anInt2813) { - var0.anInt2813 = 0; - var0.anInt2802 = 0; - Unsorted.method1470(var0.zAxis, var2, var0.xAxis, Class102.player == var0, var0.anInt2813); - } - - var0.anInt2793 = var0.anInt2813 - -1; - if (var2.frames.length <= var0.anInt2793) { - var0.anInt2793 = 0; - } - } - } - - int var6; - if (var0.anInt2842 != -1 && var0.anInt2759 <= Class44.anInt719) { - var6 = GraphicDefinition.getGraphicDefinition(var0.anInt2842).anInt542; - if (var6 == -1) { - var0.anInt2842 = -1; - } else { - SequenceDefinition var3 = SequenceDefinition.getAnimationDefinition(var6); - if (var3.frames == null) { - var0.anInt2842 = -1; - } else { - if (0 > var0.anInt2805) { - var0.anInt2805 = 0; - Unsorted.method1470(var0.zAxis, var3, var0.xAxis, Class102.player == var0, 0); - } - - ++var0.anInt2761; - if (var0.anInt2805 < var3.frames.length && var0.anInt2761 > var3.duration[var0.anInt2805]) { - ++var0.anInt2805; - var0.anInt2761 = 1; - Unsorted.method1470(var0.zAxis, var3, var0.xAxis, Class102.player == var0, var0.anInt2805); - } - - if (var0.anInt2805 >= var3.frames.length) { - var0.anInt2842 = -1; - } - - var0.anInt2826 = var0.anInt2805 - -1; - if (var0.anInt2826 >= var3.frames.length) { - var0.anInt2826 = -1; - } - } - } - } - - if (var0.anInt2771 != -1 && var0.anInt2828 <= 1) { - var2 = SequenceDefinition.getAnimationDefinition(var0.anInt2771); - if (var2.resetWhenWalk == 1 && var0.anInt2811 > 0 && var0.anInt2800 <= Class44.anInt719 && Class44.anInt719 > var0.anInt2790) { - var0.anInt2828 = 1; - return; - } - } - - if (var0.anInt2771 != -1 && var0.anInt2828 == 0) { - var2 = SequenceDefinition.getAnimationDefinition(var0.anInt2771); - if (var2.frames == null) { - var0.anInt2771 = -1; - } else { - ++var0.anInt2760; - if (var2.frames.length > var0.anInt2832 && var0.anInt2760 > var2.duration[var0.anInt2832]) { - var0.anInt2760 = 1; - ++var0.anInt2832; - Unsorted.method1470(var0.zAxis, var2, var0.xAxis, var0 == Class102.player, var0.anInt2832); - } - - if (var2.frames.length <= var0.anInt2832) { - var0.anInt2832 -= var2.anInt1865; - ++var0.anInt2773; - if (var2.maxLoops > var0.anInt2773) { - if (var0.anInt2832 >= 0 && var0.anInt2832 < var2.frames.length) { - Unsorted.method1470(var0.zAxis, var2, var0.xAxis, Class102.player == var0, var0.anInt2832); - } else { - var0.anInt2771 = -1; - } - } else { - var0.anInt2771 = -1; - } - } - - var0.anInt2776 = var0.anInt2832 + 1; - if (var0.anInt2776 >= var2.frames.length) { - var0.anInt2776 -= var2.anInt1865; - if (var2.maxLoops > var0.anInt2773 + 1) { - if (0 > var0.anInt2776 || var0.anInt2776 >= var2.frames.length) { - var0.anInt2776 = -1; - } - } else { - var0.anInt2776 = -1; - } - } - - var0.aBoolean2810 = var2.aBoolean1859; - } - } - - if (0 < var0.anInt2828) { - --var0.anInt2828; - } - - for (var6 = 0; var0.aClass145Array2809.length > var6; ++var6) { - Class145 var7 = var0.aClass145Array2809[var6]; - if (null != var7) { - if (var7.anInt1900 <= 0) { - SequenceDefinition var4 = SequenceDefinition.getAnimationDefinition(var7.animationId); - if (var4.frames == null) { - var0.aClass145Array2809[var6] = null; - } else { - ++var7.anInt1897; - if (var7.anInt1893 < var4.frames.length && var7.anInt1897 > var4.duration[var7.anInt1893]) { - ++var7.anInt1893; - var7.anInt1897 = 1; - Unsorted.method1470(var0.zAxis, var4, var0.xAxis, var0 == Class102.player, var7.anInt1893); - } - - if (var7.anInt1893 >= var4.frames.length) { - ++var7.anInt1894; - var7.anInt1893 -= var4.anInt1865; - if (var4.maxLoops > var7.anInt1894) { - if (var7.anInt1893 >= 0 && var4.frames.length > var7.anInt1893) { - Unsorted.method1470(var0.zAxis, var4, var0.xAxis, Class102.player == var0, var7.anInt1893); - } else { - var0.aClass145Array2809[var6] = null; - } - } else { - var0.aClass145Array2809[var6] = null; - } - } - - var7.anInt1891 = 1 + var7.anInt1893; - if (var4.frames.length <= var7.anInt1891) { - var7.anInt1891 -= var4.anInt1865; - if (1 + var7.anInt1894 < var4.maxLoops) { - if (var7.anInt1891 < 0 || var4.frames.length <= var7.anInt1891) { - var7.anInt1891 = -1; - } - } else { - var7.anInt1891 = -1; - } - } - } - } else { - --var7.anInt1900; - } - } - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ck.F(" + (var0 != null ? "{...}" : "null") + ',' + -11973 + ')'); - } - } - - final void parse(DataBuffer buffer) { - try { - - while (true) { - int opcode = buffer.readUnsignedByte(); - if (opcode == 0) { - return; - } - - this.parseOpcode(opcode, buffer); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ck.H(" + -1 + ',' + (buffer != null ? "{...}" : "null") + ')'); - } - } - - private void parseOpcode(int opcode, DataBuffer buffer) { - try { - if (opcode == 1) { - this.stand_animation = buffer.readUnsignedShort(); - this.walk_animation = buffer.readUnsignedShort(); - if (this.walk_animation == 65535) { - this.walk_animation = -1; - } - - if (65535 == this.stand_animation) { - this.stand_animation = -1; - } - } else if (opcode == 2) { - this.slow_walk_anim = buffer.readUnsignedShort(); - } else if (opcode == 3) { - this.slow_walk_follow_full_turn_anim = buffer.readUnsignedShort(); - } else if (4 == opcode) { - this.slow_walk_follow_ccw_turn_anim = buffer.readUnsignedShort(); - } else if (opcode == 5) { - this.slow_walk_follow_cw_turn_anim = buffer.readUnsignedShort(); - } else if (6 == opcode) { - this.run_anim = buffer.readUnsignedShort(); - } else if (7 == opcode) { - this.run_follow_full_turn_anim = buffer.readUnsignedShort(); - } else if (opcode == 8) { - this.run_follow_ccw_turn_anim = buffer.readUnsignedShort(); - } else if (opcode == 9) { - this.run_follow_cw_turn_anim = buffer.readUnsignedShort(); - } else if (opcode == 26) { - this.hill_width = (short) (4 * buffer.readUnsignedByte()); - this.hill_height = (short) (4 * buffer.readUnsignedByte()); - } else if (opcode == 27) { - if (this.equipment_transforms == null) { - this.equipment_transforms = new int[12][]; - } - - int bodyID = buffer.readUnsignedByte(); - this.equipment_transforms[bodyID] = new int[6]; - - for (int type = 0; type < 6; ++type) { - - /* - * 0 -Rotate X - * 1 - Rotate Y - * 2 - Rotate Z - * 3 - Translate X - * 4 - Translate Y - * 5 - Translate Z - */ - - this.equipment_transforms[bodyID][type] = buffer.readSignedShort(); - } - } else if (opcode == 29) { - this.yaw_acceleration = buffer.readUnsignedByte(); - } else if (opcode == 30) { - this.yaw_max_speed = buffer.readUnsignedShort(); - } else if (opcode == 31) { - this.roll_acceleration = buffer.readUnsignedByte(); - } else if (32 == opcode) { - this.roll_max_speed = buffer.readUnsignedShort(); - } else if (33 == opcode) { - this.roll_target_angle = buffer.readSignedShort(); - } else if (34 == opcode) { - this.pitch_acceleration = buffer.readUnsignedByte(); - } else if (opcode == 35) { - this.pitch_max_speed = buffer.readUnsignedShort(); - } else if (opcode == 36) { - this.pitch_target_angle = buffer.readSignedShort(); - } else if (opcode == 37) { - this.movement_acceleration = buffer.readUnsignedByte(); - } else if (opcode == 38) { - this.standing_ccw_turn = buffer.readUnsignedShort(); - } else if (39 == opcode) { - this.standing_cw_turn = buffer.readUnsignedShort(); - } else if (opcode == 40) { - this.walk_follow_full_turn_anim = buffer.readUnsignedShort(); - } else if (41 == opcode) { - this.walk_follow_ccw_turn_anim = buffer.readUnsignedShort(); - } else if (opcode == 42) { - this.walk_follow_cw_turn_anim = buffer.readUnsignedShort(); - } else if (opcode == 43) { - buffer.readUnsignedShort(); - } else if (opcode == 44) { - buffer.readUnsignedShort(); - } else if (opcode == 45) { - buffer.readUnsignedShort(); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ck.E(" + opcode + ',' + (byte) -106 + ',' + (buffer != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/ResourceRequest.java b/Client/src/main/java/org/runite/client/ResourceRequest.java deleted file mode 100644 index d492172d8..000000000 --- a/Client/src/main/java/org/runite/client/ResourceRequest.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; - -abstract class ResourceRequest extends Node { - - boolean priority; - boolean aBoolean3635; - volatile boolean waiting = true; - - abstract int getCompletion(); - - abstract byte[] getData(); - -} diff --git a/Client/src/main/java/org/runite/client/Sensor.java b/Client/src/main/java/org/runite/client/Sensor.java deleted file mode 100644 index 7830bb1f9..000000000 --- a/Client/src/main/java/org/runite/client/Sensor.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.runite.client; - -import java.awt.*; -import java.awt.image.BufferedImage; - -class Sensor { - - private final Robot robot = new Robot(); - private Component component; - - public Sensor() throws Exception { - } - - public void setCursor(Component component, Point hotSpot, int width, int height, int[] rgb) { - if (rgb == null) { - component.setCursor(null); - } else { - BufferedImage image = new BufferedImage(width, height, 2); - image.setRGB(0, 0, width, height, rgb, 0, width); - component.setCursor(component.getToolkit().createCustomCursor(image, hotSpot, null)); - } - } - - public void moveMouse(int x, int y) { - this.robot.mouseMove(x, y); - } - - // TODO Is unsetComponent the right name? - public void updateComponent(Component component, boolean unsetComponent) { - if (unsetComponent) { - component = null; - } else if (component == null) { - throw new NullPointerException(); - } - - if (component != this.component) { - if (this.component != null) { - this.component.setCursor(null); - this.component = null; - } - - if (component != null) { - component.setCursor(component.getToolkit().createCustomCursor(new BufferedImage(1, 1, 2), new Point(0, 0), null)); - this.component = component; - } - } - - } -} diff --git a/Client/src/main/java/org/runite/client/SequenceDefinition.java b/Client/src/main/java/org/runite/client/SequenceDefinition.java deleted file mode 100644 index f82b444dc..000000000 --- a/Client/src/main/java/org/runite/client/SequenceDefinition.java +++ /dev/null @@ -1,559 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.data.HashTable; - -public final class SequenceDefinition { - - int delayType = 2; - boolean aBoolean1846 = false; - static volatile long aLong1847 = 0L; - boolean aBoolean1848 = false; - int rightHandItem = -1; - int priority = -1; - int[] frames; - public static CacheIndex aClass153_1852; - int leftHandItem = -1; - boolean[] aBooleanArray1855; - public static AbstractIndexedSprite aClass109_1856; - int forcedPriority = 5; - boolean aBoolean1859 = false; - static CacheIndex animationReferenceIndex; - int maxLoops = 99; - static int anInt1862 = 0; - int animId; - int anInt1865 = -1; - int resetWhenWalk = -1; - int[][] sounds; - static Class25[] aClass25Array1868; - int[] duration; - private int[] baseIds; - static int[] anIntArray1871 = new int[2]; - boolean aBoolean1872 = false; - - static SequenceDefinition getAnimationDefinition(int var0) { - try { - - SequenceDefinition var2 = (SequenceDefinition) Texture.aReferenceCache_1146.get(var0); - if (var2 == null) { - byte[] var3 = animationReferenceIndex.getFile(Class129.method1765(var0), Class67.method1262(117, var0)); - var2 = new SequenceDefinition(); - var2.animId = var0; - if (var3 != null) { - var2.parseDefinitions(new DataBuffer(var3)); - } - - var2.method2058(); - Texture.aReferenceCache_1146.put(var2, var0); - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "client.D(" + var0 + ',' + (byte) -20 + ')'); - } - } - - - final void parseDefinitions(DataBuffer var1) { - try { - // System.out.print("Animation " + animId + " - parsed ["); - while(true) { - int var3 = var1.readUnsignedByte(); - if(var3 == 0) { - //System.out.println("]."); - return; - } - //System.out.print(var3 + ", "); - this.parseOpcode(var3, var1); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "tk.I(" + (var1 != null?"{...}":"null") + ',' + (byte) -102 + ')'); - } - } - - final Model method2054(int var2, int var3, Model var4, int var5, int var6) { - try { - int var7 = this.duration[var2]; - var2 = this.frames[var2]; - Class3_Sub28_Sub5 var8 = Class3_Sub9.method133(var2 >> 16); - var2 &= 65535; - if(var8 == null) { - return var4.method1890(true, true, true); - } else { - var5 &= 3; - Class3_Sub28_Sub5 var9 = null; - - if((this.aBoolean1846 || ClientCommands.tweeningEnabled) && var3 != -1 && this.frames.length > var3) { - var3 = this.frames[var3]; - var9 = Class3_Sub9.method133(var3 >> 16); - var3 &= 65535; - } - - Model var10; - if(var9 == null) { - var10 = var4.method1890(!var8.method559(var2), !var8.method561(var2, (byte)121), !this.aBoolean1848); - } else { - var10 = var4.method1890(!var8.method559(var2) & !var9.method559(var3), !var8.method561(var2, (byte)125) & !var9.method561(var3, (byte)118), !this.aBoolean1848); - } - - if(HDToolKit.highDetail && this.aBoolean1848) { - if(var5 == 1) { - ((Class140_Sub1_Sub1)var10).method1902(); - } else if (2 == var5) { - ((Class140_Sub1_Sub1) var10).method1911(); - } else if (var5 == 3) { - ((Class140_Sub1_Sub1) var10).method1925(); - } - } else if(var5 == 1) { - var10.method1900(); - } else if (2 == var5) { - var10.method1874(); - } else if (3 == var5) { - var10.method1885(); - } - - var10.method1880(var8, var2, var9, var3, -1 + var6, var7, this.aBoolean1848); - if(HDToolKit.highDetail && this.aBoolean1848) { - if(1 == var5) { - ((Class140_Sub1_Sub1)var10).method1925(); - } else if (var5 == 2) { - ((Class140_Sub1_Sub1) var10).method1911(); - } else if (var5 == 3) { - ((Class140_Sub1_Sub1) var10).method1902(); - } - } else if(var5 == 1) { - var10.method1885(); - } else if (var5 == 2) { - var10.method1874(); - } else if (var5 == 3) { - var10.method1900(); - } - - return var10; - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "tk.D(" + 19749 + ',' + var2 + ',' + var3 + ',' + (var4 != null?"{...}":"null") + ',' + var5 + ',' + var6 + ')'); - } - } - - final Model method2055(Model var1, byte var2, int var3, int var4, int var5) { - try { - int var7 = this.frames[var5]; - int var6 = this.duration[var5]; - Class3_Sub28_Sub5 var8 = Class3_Sub9.method133(var7 >> 16); - var7 &= 65535; - if(null == var8) { - return var1.method1894(true, true, true); - } else { - Class3_Sub28_Sub5 var9 = null; - if((this.aBoolean1846 || ClientCommands.tweeningEnabled) && var3 != -1 && this.frames.length > var3) { - var3 = this.frames[var3]; - var9 = Class3_Sub9.method133(var3 >> 16); - var3 &= 65535; - } - - Class3_Sub28_Sub5 var10 = null; - Class3_Sub28_Sub5 var11 = null; - int var13 = 0; - int var14 = 0; - if(null != this.baseIds) { - if(var5 < this.baseIds.length) { - var13 = this.baseIds[var5]; - if(var13 != 65535) { - var10 = Class3_Sub9.method133(var13 >> 16); - var13 &= 65535; - } - } - - if((this.aBoolean1846 || ClientCommands.tweeningEnabled) && -1 != var3 && this.baseIds.length > var3) { - var14 = this.baseIds[var3]; - if(var14 != 65535) { - var11 = Class3_Sub9.method133(var14 >> 16); - var14 &= 65535; - } - } - } - - boolean var15 = !var8.method559(var7); - boolean var16 = !var8.method561(var7, (byte)119); - if(var10 != null) { - var15 &= !var10.method559(var13); - var16 &= !var10.method561(var13, (byte)115); - } - - if(null != var9) { - var15 &= !var9.method559(var3); - var16 &= !var9.method561(var3, (byte)123); - } - - if(null != var11) { - var15 &= !var11.method559(var14); - var16 &= !var11.method561(var14, (byte)121); - } - - Model var17 = var1.method1894(var15, var16, !this.aBoolean1848); - var17.method1880(var8, var7, var9, var3, var4 - 1, var6, this.aBoolean1848); - if(null != var10) { - var17.method1880(var10, var13, var11, var14, var4 + -1, var6, this.aBoolean1848); - } - - return var17; - } - } catch (RuntimeException var18) { - throw ClientErrorException.clientError(var18, "tk.E(" + (var1 != null?"{...}":"null") + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - final Model method2056(int var1, int var2, int var3, int var4, Model var5) { - try { - int var7 = this.duration[var2]; - var2 = this.frames[var2]; - Class3_Sub28_Sub5 var8 = Class3_Sub9.method133(var2 >> 16); - var2 &= 65535; - if(null == var8) { - return var5.method1894(true, true, true); - } else { - var4 &= 3; - Class3_Sub28_Sub5 var9 = null; - if((this.aBoolean1846 || ClientCommands.tweeningEnabled) && var1 != -1 && this.frames.length > var1) { - var1 = this.frames[var1]; - var9 = Class3_Sub9.method133(var1 >> 16); - var1 &= 65535; - } - - Model var10; - if(null == var9) { - var10 = var5.method1894(!var8.method559(var2), !var8.method561(var2, (byte)123), !this.aBoolean1848); - } else { - var10 = var5.method1894(!var8.method559(var2) & !var9.method559(var1), !var8.method561(var2, (byte)125) & !var9.method561(var1, (byte)123), !this.aBoolean1848); - } - - if(this.aBoolean1848 && HDToolKit.highDetail) { - if(1 == var4) { - ((Class140_Sub1_Sub1)var10).method1902(); - } else if (var4 == 2) { - ((Class140_Sub1_Sub1) var10).method1911(); - } else if (var4 == 3) { - ((Class140_Sub1_Sub1) var10).method1925(); - } - } else if(var4 == 1) { - var10.method1900(); - } else if(var4 == 2) { - var10.method1874(); - } else if(var4 == 3) { - var10.method1885(); - } - - var10.method1880(var8, var2, var9, var1, var3 + -1, var7, this.aBoolean1848); - if(this.aBoolean1848 && HDToolKit.highDetail) { - if(var4 == 1) { - ((Class140_Sub1_Sub1)var10).method1925(); - } else if(var4 == 2) { - ((Class140_Sub1_Sub1)var10).method1911(); - } else if(var4 == 3) { - ((Class140_Sub1_Sub1)var10).method1902(); - } - } else if(1 == var4) { - var10.method1885(); - } else if (var4 == 2) { - var10.method1874(); - } else if (3 == var4) { - var10.method1900(); - } - - return var10; - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "tk.B(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + (var5 != null?"{...}":"null") + ',' + 3 + ')'); - } - } - - final void method2058() { - try { - if(this.resetWhenWalk == -1) { - if(null == this.aBooleanArray1855) { - this.resetWhenWalk = 0; - } else { - this.resetWhenWalk = 2; - } - } - - if(-1 == this.priority) { - if(null == this.aBooleanArray1855) { - this.priority = 0; - } else { - this.priority = 2; - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "tk.C(" + (byte) -41 + ')'); - } - } - - final Model method2059(int var1, int var2, int var3, Model var5) { - try { - int var6 = this.duration[var3]; - var3 = this.frames[var3]; - Class3_Sub28_Sub5 var7 = Class3_Sub9.method133(var3 >> 16); - var3 &= 65535; - if(var7 == null) { - return var5.method1882(true, true, true); - } else { - Class3_Sub28_Sub5 var9 = null; - if((this.aBoolean1846 || ClientCommands.tweeningEnabled) && var1 != -1 && var1 < this.frames.length) { - var1 = this.frames[var1]; - var9 = Class3_Sub9.method133(var1 >> 16); - var1 &= 65535; - } - - Model var10; - if(null == var9) { - var10 = var5.method1882(!var7.method559(var3), !var7.method561(var3, (byte)118), !this.aBoolean1848); - } else { - var10 = var5.method1882(!var7.method559(var3) & !var9.method559(var1), !var7.method561(var3, (byte)119) & !var9.method561(var1, (byte)118), !this.aBoolean1848); - } - - var10.method1880(var7, var3, var9, var1, var2 + -1, var6, this.aBoolean1848); - return var10; - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "tk.G(" + var1 + ',' + var2 + ',' + var3 + ',' + (byte) -52 + ',' + (var5 != null?"{...}":"null") + ')'); - } - } - - private void parseOpcode(int opcode, DataBuffer buffer) { - try { - int count; - int index; - if(opcode == 1) { - count = buffer.readUnsignedShort(); - this.duration = new int[count]; - - for(index = 0; count > index; ++index) { - this.duration[index] = buffer.readUnsignedShort(); - } - - this.frames = new int[count]; - - for(index = 0; count > index; ++index) { - this.frames[index] = buffer.readUnsignedShort(); - } - - for(index = 0; count > index; ++index) { - this.frames[index] += buffer.readUnsignedShort() << 16; - } - } else if(opcode == 2) { - this.anInt1865 = buffer.readUnsignedShort(); - } else if(opcode == 3) { - this.aBooleanArray1855 = new boolean[256]; - count = buffer.readUnsignedByte(); - - for(index = 0; index < count; ++index) { - this.aBooleanArray1855[buffer.readUnsignedByte()] = true; - } - } else if (opcode == 4) { - this.aBoolean1859 = true; - } else if (opcode == 5) { - this.forcedPriority = buffer.readUnsignedByte(); - } else if (6 == opcode) { - this.leftHandItem = buffer.readUnsignedShort(); - } else if (opcode == 7) { - this.rightHandItem = buffer.readUnsignedShort(); - } else if (8 == opcode) { - this.maxLoops = buffer.readUnsignedByte(); - } else if (9 == opcode) { - this.resetWhenWalk = buffer.readUnsignedByte(); - } else if (10 == opcode) { - this.priority = buffer.readUnsignedByte(); - } else if (opcode == 11) { - this.delayType = buffer.readUnsignedByte(); - } else if (12 == opcode) { - count = buffer.readUnsignedByte(); - this.baseIds = new int[count]; - - for (index = 0; index < count; ++index) { - this.baseIds[index] = buffer.readUnsignedShort(); - } - - for (index = 0; index < count; ++index) { - this.baseIds[index] += buffer.readUnsignedShort() << 16; - } - } else if (13 == opcode) { - count = buffer.readUnsignedShort(); - this.sounds = new int[count][]; - - for (index = 0; index < count; ++index) { - int var6 = buffer.readUnsignedByte(); - if (var6 > 0) { - this.sounds[index] = new int[var6]; - this.sounds[index][0] = buffer.readMedium(); - - for (int var7 = 1; var7 < var6; ++var7) { - this.sounds[index][var7] = buffer.readUnsignedShort(); - } - } - } - } else if (opcode == 14) { - this.aBoolean1848 = true; - } else if (15 == opcode) { - this.aBoolean1846 = true; - } else if (16 == opcode) { - this.aBoolean1872 = true; - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "tk.H(" + opcode + ',' + (byte) -73 + ',' + (buffer != null?"{...}":"null") + ')'); - } - } - - static void resetAll() { - try { - Class3_Sub26.anInt2556 = 0; - TextureOperation20.aBoolean2774 = true; - TextureOperation20.aLong1465 = 0L; - TextureOperation20.aClass67_1443.anInt1018 = 0; - TextureOperation26.aBoolean3078 = true; - TextureOperation.method153(); - Class24.anInt469 = -1; - Class7.anInt2166 = -1; - Unsorted.incomingOpcode = -1; - Class159.anInt2023 = 0; - Class38_Sub1.anInt2617 = 0; - TextureOperation12.outgoingBuffer.index = 0; - LinkableRSString.anInt2582 = -1; - AbstractSprite.anInt3699 = 0; - BufferedDataStream.incomingBuffer.index = 0; - - int var1; - for(var1 = 0; ClientErrorException.aClass96Array2114.length > var1; ++var1) { - ClientErrorException.aClass96Array2114[var1] = null; - } - - Unsorted.menuOptionCount = 0; - Class38_Sub1.aBoolean2615 = false; - Class23.method940(119, 0); - - for(var1 = 0; var1 < 100; ++var1) { - LinkableRSString.aClass94Array2580[var1] = null; - } - - Class164_Sub1.anInt3012 = 0; - TextureOperation20.anInt3216 = (int)(Math.random() * 100.0D) + -50; - Class45.anInt733 = 0; - GraphicDefinition.CAMERA_DIRECTION = 2047 & (int)(Math.random() * 20.0D) - 10; - Class58.anInt909 = -1; - Class159.localPlayerCount = 0; - Class161.anInt2028 = 0; - Unsorted.anInt42 = (int)(110.0D * Math.random()) + -55; - GameObject.aBoolean1837 = false; - Class164_Sub2.anInt3020 = -20 + (int)(30.0D * Math.random()); - AudioHandler.currentSoundEffectCount = 0; - Class65.anInt987 = 0; - TextureOperation9.anInt3102 = -60 + (int)(Math.random() * 120.0D); - TextureOperation16.anInt3114 = 0; - LinkableRSString.anInt2589 = (int)(80.0D * Math.random()) - 40; - Class163.localNPCCount = 0; - - for(var1 = 0; 2048 > var1; ++var1) { - Unsorted.players[var1] = null; - Class65.aClass3_Sub30Array986[var1] = null; - } - - for(var1 = 0; var1 < 32768; ++var1) { - NPC.npcs[var1] = null; - } - - Class102.player = Unsorted.players[2047] = new Player(); - TextureOperation13.aLinkedList_3364.method1211(-112); - TextureOperation17.aLinkedList_3177.method1211(-58); - if(null != Class39.aLinkedListArrayArrayArray3273) { - for(var1 = 0; 4 > var1; ++var1) { - for(int var2 = 0; var2 < 104; ++var2) { - for(int var3 = 0; var3 < 104; ++var3) { - Class39.aLinkedListArrayArrayArray3273[var1][var2][var3] = null; - } - } - } - } - - TextureOperation26.aLinkedList_3075 = new LinkedList(); - CS2Script.anInt1357 = 0; - Class8.anInt104 = 0; - TextureOperation6.method176(-114); - Class3_Sub2.method103(); - Class75.anInt1105 = 0; - Class163_Sub2_Sub1.anInt4014 = 0; - Class157.anInt1996 = 0; - TextureOperation25.anInt3414 = 0; - Class146.anInt1904 = 0; - Unsorted.anInt30 = 0; - GraphicDefinition.anInt529 = 0; - MouseListeningClass.anInt1923 = 0; - Unsorted.anInt3631 = 0; - Class163_Sub2_Sub1.anInt4021 = 0; - - for(var1 = 0; var1 < NPCDefinition.anIntArray1277.length; ++var1) { - NPCDefinition.anIntArray1277[var1] = -1; - } - - if(ConfigInventoryDefinition.anInt3655 != -1) { - Class60.method1208((byte)-128, ConfigInventoryDefinition.anInt3655); - } - - for(Class3_Sub31 var7 = TextureOperation23.aHashTable_3208.first(); var7 != null; var7 = TextureOperation23.aHashTable_3208.next()) { - TextureOperation19.method254(true, var7); - } - - ConfigInventoryDefinition.anInt3655 = -1; - TextureOperation23.aHashTable_3208 = new HashTable(8); - Class3_Sub7.method122(-113); - TextureOperation27.aClass11_3087 = null; - Class38_Sub1.aBoolean2615 = false; - Unsorted.menuOptionCount = 0; - Unsorted.aClass52_1112.method1161(new int[]{0, 0, 0, 0, 0}, -1, false, null, -1); - - for(var1 = 0; 8 > var1; ++var1) { - Class91.aClass94Array1299[var1] = null; - Class1.aBooleanArray54[var1] = false; - TextureOperation35.anIntArray3328[var1] = -1; - } - - Class3_Sub28_Sub9.method580((byte)80); - TextureOperation34.aBoolean3064 = true; - - for(var1 = 0; var1 < 100; ++var1) { - Unsorted.aBooleanArray3674[var1] = true; - } - - Unsorted.clanSize = 0; - PacketParser.aClass3_Sub19Array3694 = null; - RSInterface.aClass94_251 = null; - - for(var1 = 0; 6 > var1; ++var1) { - TextureOperation29.aClass133Array3393[var1] = new Class133(); - } - - for(var1 = 0; var1 < 25; ++var1) { - TextureOperation17.anIntArray3185[var1] = 0; - Class3_Sub20.anIntArray2480[var1] = 0; - Class133.anIntArray1743[var1] = 0; - } - - if(HDToolKit.highDetail) { - TextureOperation31.method236(); - } - - Unsorted.aBoolean4068 = true; - Class113.interfacePacketCounter = 0; - TextureOperation32.aClass94_3353 = TextCore.HasWalkHere; - Unsorted.aBoolean1084 = false; - TextureOperation38.aShortArray3455 = TextureOperation16.aShortArray3110 = Class136.aShortArray1779 = TextureOperation38.aShortArray3453 = new short[256]; - InvalidateData.method165(); - Unsorted.aBoolean1951 = false; - TextureOperation9.method204(); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "tk.A(" + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/ShaderInterface.java b/Client/src/main/java/org/runite/client/ShaderInterface.java deleted file mode 100644 index 3e932ce74..000000000 --- a/Client/src/main/java/org/runite/client/ShaderInterface.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.runite.client; - -interface ShaderInterface { - - void method21(); - - void method22(); - - void method23(int var1); - - int method24(); -} diff --git a/Client/src/main/java/org/runite/client/SignLinkAudioChannel.java b/Client/src/main/java/org/runite/client/SignLinkAudioChannel.java deleted file mode 100644 index 094e1a902..000000000 --- a/Client/src/main/java/org/runite/client/SignLinkAudioChannel.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.runite.client; -import java.awt.Component; - -final class SignLinkAudioChannel extends AudioChannel { - - private final int anInt2969; - private static Interface1 anInterface1_2970; - - - final int method2157() { - return anInterface1_2970.method2((byte)118, this.anInt2969); - } - - final void write() { - anInterface1_2970.method6(this.anInt2969, this.samples); - } - - final void init(Component var1) throws Exception { - anInterface1_2970.method5(Class21.sampleRate, (byte)-39, var1, AudioChannel.stereo); - } - - final void flush() { - anInterface1_2970.method1(this.anInt2969, 28544); - } - - SignLinkAudioChannel(Signlink var1, int var2) { - anInterface1_2970 = var1.method1446((byte)99); - this.anInt2969 = var2; - } - - final void open(int var1) throws Exception { - if(var1 > 32768) { - throw new IllegalArgumentException(); - } else { - anInterface1_2970.method3(this.anInt2969, 25349, var1); - } - } - - final void close() { - anInterface1_2970.method4((byte)20, this.anInt2969); - } -} diff --git a/Client/src/main/java/org/runite/client/Signlink.java b/Client/src/main/java/org/runite/client/Signlink.java deleted file mode 100644 index 78c8612c1..000000000 --- a/Client/src/main/java/org/runite/client/Signlink.java +++ /dev/null @@ -1,688 +0,0 @@ -package org.runite.client; - -import com.sun.opengl.impl.x11.DRIHack; -import org.rs09.SystemLogger; - -import java.applet.Applet; -import java.awt.*; -import java.io.*; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.Socket; -import java.net.URL; -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.Vector; - -public class Signlink implements Runnable { - - private static final Hashtable cachedFiles = new Hashtable<>(18); - public static String javaVersion; - public static String osName; - public static String osNameCS; - public static int anInt1214 = 1; - public static String javaVendor; - public static String osArchitecture; - public static Method setFocusCycleRoot; - public static Method setTraversalKeysEnabled; - static volatile long aLong1221 = 0L; - private static String homeDirectory; - private final Thread thread; - private final String gameName; - private final int cacheSubrevisionNum; - public RandomAccessFileWrapper[] cacheIndicesFiles; - public RandomAccessFileWrapper cacheDataFile; - public EventQueue systemEventQueue; - public RandomAccessFileWrapper cacheChecksumFile; - public RandomAccessFileWrapper randomDatFile; - public Applet gameApplet; - private boolean stopped; - private Class64 aClass64_1203 = null; - private Sensor sensor; - private Display display; - private Class64 aClass64_1213 = null; - private Interface1 anInterface1_1217; - - private final int STAGE_LOAD_HDLIB = 10; - - - public Signlink(Applet applet, int var2, String gameName, int cacheIndexes) throws Exception { - javaVersion = "1.1"; - this.gameName = gameName; - this.cacheSubrevisionNum = var2; - this.gameApplet = applet; - javaVendor = "Unknown"; - - try { - javaVendor = System.getProperty("java.vendor"); - javaVersion = System.getProperty("java.version"); - } catch (Exception var17) { - } - - try { - osNameCS = System.getProperty("os.name"); - } catch (Exception var16) { - osNameCS = "Unknown"; - } - - osName = osNameCS.toLowerCase(); - - try { - osArchitecture = System.getProperty("os.arch").toLowerCase(); - } catch (Exception var15) { - osArchitecture = ""; - } - - try { - homeDirectory = System.getProperty("user.home"); - if (homeDirectory != null) { - homeDirectory = homeDirectory + "/"; - } - } catch (Exception var13) { - } - - if (homeDirectory == null) { - homeDirectory = "~/"; - } - - try { - this.systemEventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); - } catch (Throwable var12) { - } - - try { - if (applet == null) { - setTraversalKeysEnabled = Class.forName("java.awt.Component").getDeclaredMethod("setFocusTraversalKeysEnabled", Boolean.TYPE); - } else { - setTraversalKeysEnabled = applet.getClass().getMethod("setFocusTraversalKeysEnabled", Boolean.TYPE); - } - } catch (Exception var11) { - } - - try { - if (applet == null) { - setFocusCycleRoot = Class.forName("java.awt.Container").getDeclaredMethod("setFocusCycleRoot", Boolean.TYPE); - } else { - setFocusCycleRoot = applet.getClass().getMethod("setFocusCycleRoot", Boolean.TYPE); - } - } catch (Exception var10) { - } - - this.randomDatFile = new RandomAccessFileWrapper(getFileFromCacheFolder(null, this.cacheSubrevisionNum, "random.dat"), "rw", 25L); - this.cacheDataFile = new RandomAccessFileWrapper(getFileFromCacheFolder(this.gameName, this.cacheSubrevisionNum, "main_file_cache.dat2"), "rw", 104857600L); - this.cacheChecksumFile = new RandomAccessFileWrapper(getFileFromCacheFolder(this.gameName, this.cacheSubrevisionNum, "main_file_cache.idx255"), "rw", 1048576L); - this.cacheIndicesFiles = new RandomAccessFileWrapper[cacheIndexes]; - - for (int i = 0; i < cacheIndexes; ++i) { - this.cacheIndicesFiles[i] = new RandomAccessFileWrapper(getFileFromCacheFolder(this.gameName, this.cacheSubrevisionNum, "main_file_cache.idx" + i), "rw", 1048576L); - } - - try { - this.display = new Display(); - } catch (Throwable var9) { - var9.printStackTrace(); - } - - try { - this.sensor = new Sensor(); - } catch (Throwable var8) { - } - - ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); - - for (ThreadGroup parent = threadGroup.getParent(); parent != null; parent = parent.getParent()) { - threadGroup = parent; - } - - Thread[] threads = new Thread[1000]; - threadGroup.enumerate(threads); - - for (Thread value : threads) { - if (value != null && value.getName().startsWith("AWT")) { - value.setPriority(1); - } - } - - this.stopped = false; - this.thread = new Thread(this); - this.thread.setPriority(10); - this.thread.setDaemon(true); - this.thread.start(); - } - - private static RandomAccessFileWrapper method1438(boolean var0, String var1) { - if (var0) { - method1438(true, null); - } - - String[] var2 = new String[]{"c:/rscache/", "/rscache/", homeDirectory, "c:/windows/", "c:/winnt/", "c:/", "/tmp/", ""}; - - for (String var4 : var2) { - if (var4.length() <= 0 || (new File(var4)).exists()) { - try { - return new RandomAccessFileWrapper(new File(var4, "jagex_" + var1 + "_preferences.dat"), "rw", 10000L); - } catch (Exception var6) { - } - } - } - - return null; - } - - public static File getFileFromCacheFolder(String gameName, int cacheSubRev, String filename) { - File cachedFile = cachedFiles.get(filename); - if (cachedFile == null) { - String[] basePaths = new String[]{homeDirectory, "c:/rscache/", "/rscache/", "c:/windows/", "c:/winnt/", "c:/", "/tmp/", ""}; - String[] folders = new String[]{".runite_rs", ".530file_store_" + cacheSubRev}; - for (int i = 0; i < 2; ++i) { - for (String folder : folders) { - for (String basePath : basePaths) { - String fullPath = basePath + folder + "/" + (gameName != null ? gameName + "/" : "") + filename; - RandomAccessFile raf = null; - try { - File file = new File(fullPath); - if (i != 0 || file.exists()) { - if (i != 1 || basePath.length() <= 0 || (new File(basePath)).exists()) { - (new File(basePath + folder)).mkdir(); - if (gameName != null) { - (new File(basePath + folder + "/" + gameName)).mkdir(); - } - // ClientLoader.getLibraryDownloader().updateDlls(var10.toString()); - raf = new RandomAccessFile(file, "rw"); - int var14 = raf.read(); - raf.seek(0L); - raf.write(var14); - raf.seek(0L); - raf.close(); - cachedFiles.put(filename, file); - return file; - } - } - } catch (Exception e) { - try { - if (raf != null) { - raf.close(); - } - } catch (Exception e2) { - } - } - } - } - } - - throw new RuntimeException(); - } else { - return cachedFile; - } - } - - public final void method1431() { - aLong1221 = TimeUtils.time() + 5000L; - } - - public final boolean method1432(boolean var1) { - if (var1) { - this.cacheIndicesFiles = null; - } - - return this.display != null; - } - - public final Class64 method1433(String var1, int var2) { - if (var2 != 12) { - this.randomDatFile = null; - } - - return this.method1435(12, 0, var1, 0); - } - - public final void method1434(int[] var1, int var2, int var3, Component var4, Point var5, int var6) { - if (var2 == 10000) { - this.method1435(17, var6, new Object[]{var4, var1, var5}, var3); - } - } - - private Class64 method1435(int var1, int var2, Object var3, int var4) { - Class64 var6 = new Class64(); - var6.anInt980 = var2; - var6.anInt979 = var4; - var6.anInt975 = var1; - var6.anObject977 = var3; - synchronized (this) { - if (this.aClass64_1203 == null) { - this.aClass64_1203 = this.aClass64_1213 = var6; - } else { - this.aClass64_1203.aClass64_976 = var6; - this.aClass64_1203 = var6; - } - - this.notify(); - return var6; - } - } - - public final Class64 method1436(Frame var1, int var2) { - if (var2 <= 78) { - this.gameApplet = null; - } - - return this.method1435(7, 0, var1, 0); - } - - public final Class64 method1439(boolean var1, URL var2) { - if (var1) { - this.cacheChecksumFile = null; - } - - return this.method1435(4, 0, var2, 0); - } - - public final Class64 method1441(byte var1, String address, int port) { - //System.out.println("var1: " + var1 + ", add: " + address + ":" + port); - return var1 != 8 ? null : this.method1435(1, 0, address, port); - } - - public final void method1442(Class var1, int var2) { - if (var2 == 0) { - this.method1435(11, 0, var1, 0); - } - } - - public final Class64 method1443(Class var1, Class[] var2, int var3, String var4) { - if (var3 > -7) { - homeDirectory = null; - } - - return this.method1435(8, 0, new Object[]{var1, var4, var2}, 0); - } - - public final void run() { -// byte dat2status = Update.updateExists(1, "main_file_cache.dat2"); - - while (true) { - Class64 var1; - synchronized (this) { - while (true) { - if (this.stopped) { - return; - } - - if (this.aClass64_1213 != null) { - var1 = this.aClass64_1213; - this.aClass64_1213 = this.aClass64_1213.aClass64_976; - if (this.aClass64_1213 == null) { - this.aClass64_1203 = null; - } - break; - } - - try { - this.wait(); - } catch (InterruptedException var11) { - } - } - } - - try { - int stage = var1.anInt975; - if (stage == 1) { - if (TimeUtils.time() < aLong1221) { - throw new IOException(); - } -// System.out.println("Roar " + (String)var1.anObject977 + ", port " + var1.anInt979); - var1.anObject974 = new Socket(InetAddress.getByName((String) var1.anObject977), var1.anInt979); - } else if (2 == stage) { - Thread var16 = new Thread((Runnable) var1.anObject977); - var16.setDaemon(true); - var16.start(); - var16.setPriority(var1.anInt979); - var1.anObject974 = var16; - } else if (stage == 4) { - if (TimeUtils.time() < aLong1221) { - throw new IOException(); - } - - var1.anObject974 = new DataInputStream(((URL) var1.anObject977).openStream()); - } else { - Object[] var3; - if (stage == 8) { - var3 = (Object[]) var1.anObject977; - if (((Class) var3[0]).getClassLoader() == null) { - throw new SecurityException(); - } - - var1.anObject974 = ((Class) var3[0]).getDeclaredMethod((String) var3[1], (Class[]) var3[2]); - } else if (stage == 9) { - var3 = (Object[]) var1.anObject977; - if (((Class) var3[0]).getClassLoader() == null) { - throw new SecurityException(); - } - - var1.anObject974 = ((Class) var3[0]).getDeclaredField((String) var3[1]); - } else { - String var4; - if (stage == 3) { - if (aLong1221 > TimeUtils.time()) { - throw new IOException(); - } - - var4 = (var1.anInt979 >> 24 & 0xFF) + "." + (var1.anInt979 >> 16 & 0xFF) + "." + (var1.anInt979 >> 8 & 0xFF) + "." + (255 & var1.anInt979); - var1.anObject974 = InetAddress.getByName(var4).getHostName(); - } else if (stage == 5) { - var1.anObject974 = this.display.method919(true); - } else if (stage == 6) { - Frame var5 = new Frame("Jagex Full Screen"); - var1.anObject974 = var5; - var5.setResizable(false); - this.display.configureDisplayMode(-56, var1.anInt980 & 65535, var1.anInt980 >> 16, 65535 & var1.anInt979, var5, var1.anInt979 >>> 16); - } else if (stage == 7) { - this.display.updateDisplayMode(); - } else if (stage == STAGE_LOAD_HDLIB) { - Class[] declaredMethodFields = new Class[]{Class.forName("java.lang.Class"), Class.forName("java.lang.String")}; - Runtime runtime = Runtime.getRuntime(); - Method libLoaderMethod; - Class clientClass = (Class) var1.anObject977; - - if (!osName.startsWith("mac")) { - libLoaderMethod = Class.forName("java.lang.Runtime").getDeclaredMethod("loadLibrary0", declaredMethodFields); - libLoaderMethod.setAccessible(true); - libLoaderMethod.invoke(runtime, clientClass, "jawt"); - libLoaderMethod.setAccessible(false); - } - - boolean is64Bit = osArchitecture.contains("64"); - boolean isSunOS = osName.startsWith("sunos"); - //load0 is a reflection-based package-private method in Runtime. Not sure why jagex used this, but it's fucky. - libLoaderMethod = Class.forName("java.lang.Runtime").getDeclaredMethod("load0", declaredMethodFields); - libLoaderMethod.setAccessible(true); - - SystemLogger.logInfo("Signlink - os Name: " + osName); - SystemLogger.logInfo("Signlink - os Arch: " + osArchitecture); - - if (osArchitecture.equals("aarch64")) { - SystemLogger.logWarn("Going into HD will fail - current libs do not support ARM."); - throw new Exception(); - } - - if (osName.startsWith("linux") || isSunOS) { - String[] libs = createLibs("linux"); - libLoaderMethod.invoke(runtime, clientClass, libs[2]); - DRIHack.begin(); - libLoaderMethod.invoke(runtime, clientClass, libs[0]); - DRIHack.end(); - libLoaderMethod.invoke(runtime, clientClass, libs[1]); - } else { - if(osName.startsWith("mac") && !osArchitecture.equals("ppc")) throw new Exception(); //We only have ppc libs for mac. - String[] libs = createLibs(osName.contains("win") ? "windows" : "macppc"); - //Windows has to load them this way because temporary files are illegal. - String jogl = getFileFromCacheFolder(this.gameName, this.cacheSubrevisionNum, libs[0]).toString(); - String awt = getFileFromCacheFolder(this.gameName, this.cacheSubrevisionNum, libs[1]).toString(); - libLoaderMethod.invoke(runtime, clientClass, jogl); - libLoaderMethod.invoke(runtime, clientClass, awt); - } - - libLoaderMethod.setAccessible(false); - } else { - int var18; - if (stage == 11) { - Field var20 = Class.forName("java.lang.ClassLoader").getDeclaredField("nativeLibraries"); - var20.setAccessible(true); - Vector var24 = (Vector) var20.get(((Class) var1.anObject977).getClassLoader()); - - for (var18 = 0; var18 < var24.size(); ++var18) { - Object var26 = var24.elementAt(var18); - Method var9 = var26.getClass().getDeclaredMethod("finalize"); - var9.setAccessible(true); - var9.invoke(var26); - var9.setAccessible(false); - Field var10 = var26.getClass().getDeclaredField("handle"); - var10.setAccessible(true); - var10.set(var26, new Integer(0)); - var10.setAccessible(false); - } - - var20.setAccessible(false); - } else if (stage == 12) { - var4 = (String) var1.anObject977; - var1.anObject974 = method1438(false, var4); - } else if (stage == 14) { - int var22 = var1.anInt980; - int var23 = var1.anInt979; - this.sensor.moveMouse(var23, var22); - } else if (15 == stage) { - boolean var21 = var1.anInt979 != 0; - Component var27 = (Component) var1.anObject977; - this.sensor.updateComponent(var27, var21); - } else if (17 == stage) { - var3 = (Object[]) var1.anObject977; - this.sensor.setCursor((Component) var3[0], (Point) var3[2], var1.anInt979, var1.anInt980, (int[]) var3[1]); - } else { - if (16 != stage) { - throw new Exception(); - } - - try { - if (!osName.startsWith("win")) { - throw new Exception(); - } - - var4 = (String) var1.anObject977; - if (!var4.startsWith("http://") && !var4.startsWith("https://")) { - throw new Exception(); - } - - String var25 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?&=,.%+-_#:/*"; - - for (var18 = 0; var18 < var4.length(); ++var18) { - if (var25.indexOf(var4.charAt(var18)) == -1) { - throw new Exception(); - } - } - - Runtime.getRuntime().exec("cmd /c start \"j\" \"" + var4 + "\""); - var1.anObject974 = null; - } catch (Exception var12) { - var1.anObject974 = var12; - } - } - } - } - } - - var1.anInt978 = 1; - } catch (ThreadDeath var13) { - throw var13; - } catch (Throwable var14) { - var1.anInt978 = 2; - } - } - } - - /** - * Extracts the libs from the client resources - * @param os The OS in use: windows, macppc, or linux - * @return an array of the created filenames to load. - * @author Ceikry - */ - public String[] createLibs(String os) throws Throwable { - ArrayList filenames = new ArrayList<>(); - String jogl; - String awt; - String glueGen = ""; - boolean isGluegenRequired = false; - boolean is64Bit = osArchitecture.contains("64"); - boolean isWindowsOrMac = os.equals("macppc") || os.equals("windows"); - - String fileExtension = os.equals("windows") ? ".dll" : os.equals("macppc") ? ".jnilib" : ".so"; - - jogl = (os.equals("windows") ? "jogl" : "libjogl") + - (is64Bit ? "_64" : "_32") + fileExtension; - - awt = (os.equals("windows") ? "jogl_awt" : "libjogl_awt") + - (is64Bit ? "_64" : "_32") + fileExtension; - - if(!isWindowsOrMac) isGluegenRequired = true; - if(isGluegenRequired) glueGen = "libgluegen-rt_" + (is64Bit ? "64" : "32") + ".so"; - - File joglLib = isWindowsOrMac ? getFileFromCacheFolder(this.gameName, this.cacheSubrevisionNum, "jogl.dll") : File.createTempFile("jogl", "." + jogl.split("\\.")[1]); - File awtLib = isWindowsOrMac ? getFileFromCacheFolder(this.gameName, this.cacheSubrevisionNum, "jogl_awt.dll") : File.createTempFile("jogl_awt", "." + awt.split("\\.")[1]); - - try (InputStream in = getClass().getResourceAsStream("/lib/" + jogl); OutputStream out = openOutputStream(joglLib)) { - if (in == null) throw new FileNotFoundException("Needed library does not exist: " + jogl); - copyFile(in, out); - filenames.add(isWindowsOrMac ? joglLib.getName() : joglLib.getAbsolutePath()); - } - - try (InputStream in = getClass().getResourceAsStream("/lib/" + awt); OutputStream out = openOutputStream(awtLib)) { - if (in == null) throw new FileNotFoundException("Needed library does not exist: " + awt); - copyFile(in, out); - filenames.add(isWindowsOrMac ? awtLib.getName() : awtLib.getAbsolutePath()); - } - - if (isGluegenRequired) { - File glueLib = File.createTempFile("libgluegen", ".so"); - try (InputStream in = getClass().getResourceAsStream("/lib/" + glueGen); OutputStream out = openOutputStream(glueLib)) { - if (in == null) throw new FileNotFoundException("Needed library does not exist: " + glueGen); - copyFile(in, out); - filenames.add(glueLib.getAbsolutePath()); - } - } - - - return filenames.toArray(new String[]{}); - } - - public final Class64 method1444(int var1, Class var2) { - if (var1 > -13) { - this.method1435(88, -20, null, 76); - } - - return this.method1435(10, 0, var2, 0); - } - - public final void method1445(int var1) { - synchronized (this) { - this.stopped = true; - this.notifyAll(); - } - - try { - this.thread.join(); - } catch (InterruptedException var8) { - } - - if (var1 != 0) { - method1438(false, null); - } - - if (this.cacheDataFile != null) { - try { - this.cacheDataFile.close(); - } catch (IOException var7) { - } - } - - if (this.cacheChecksumFile != null) { - try { - this.cacheChecksumFile.close(); - } catch (IOException var6) { - } - } - - if (this.cacheIndicesFiles != null) { - for (int var2 = 0; var2 < this.cacheIndicesFiles.length; ++var2) { - if (this.cacheIndicesFiles[var2] != null) { - try { - this.cacheIndicesFiles[var2].close(); - } catch (IOException var5) { - } - } - } - } - - if (this.randomDatFile != null) { - try { - this.randomDatFile.close(); - } catch (IOException var4) { - } - } - - } - - public final Interface1 method1446(byte var1) { - if (var1 < 71) { - this.method1452(null, true); - } - - //return this.anInterface1_1217; - return null; - } - - public final Class64 method1447(int var1, String var2, Class var3) { - if (var1 > -39) { - this.method1452(null, true); - } - - return this.method1435(9, 0, new Object[]{var3, var2}, 0); - } - - public final Class64 method1449(int var1, int var2) { - if (var1 != 3) { - this.cacheChecksumFile = null; - } - - return this.method1435(3, 0, null, var2); - } - - public final Class64 method1450(int var1, int var2, int var3, int var4) { - return this.method1435(6, var1 + (var2 << 16), null, (var4 << 16) + var3); - } - - public final Class64 startThread(int var2, Runnable var3) { - return this.method1435(2, 0, var3, var2); - } - - public final Class64 method1452(String var1, boolean var2) { - if (!var2) { - this.method1436(null, 101); - } - - return this.method1435(16, 0, var1, 0); - } - - public final Class64 method1453(byte var1) { - if (var1 < 7) { - this.method1443(null, null, -91, null); - } - - return this.method1435(5, 0, null, 0); - } - - private static FileOutputStream openOutputStream(final File file) throws IOException { - if (file.exists()) { - if (file.isDirectory()) { - throw new IOException("File '" + file + "' exists but is a directory"); - } - if (!file.canWrite()) { - throw new IOException("File '" + file + "' cannot be written to"); - } - } else { - final File parent = file.getParentFile(); - if (parent != null) { - if (!parent.mkdirs() && !parent.isDirectory()) { - throw new IOException("Directory '" + parent + "' could not be created"); - } - } - } - return new FileOutputStream(file); - } - - private static void copyFile(final InputStream input, final OutputStream output) throws IOException { - byte[] buffer = new byte[1024 * 4]; - int n; - while (-1 != (n = input.read(buffer))) { - output.write(buffer, 0, n); - } - } -} diff --git a/Client/src/main/java/org/runite/client/SoftwareSprite.java b/Client/src/main/java/org/runite/client/SoftwareSprite.java deleted file mode 100644 index 182167e2b..000000000 --- a/Client/src/main/java/org/runite/client/SoftwareSprite.java +++ /dev/null @@ -1,1374 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; - -public class SoftwareSprite extends AbstractSprite { - - int[] anIntArray4081; - - - final void method652(int var1) { - if(this.width != this.anInt3697 || this.height != this.anInt3706) { - int var2 = var1; - if(var1 > this.anInt3701) { - var2 = this.anInt3701; - } - - int var3 = var1; - if(var1 + this.anInt3701 + this.width > this.anInt3697) { - var3 = this.anInt3697 - this.anInt3701 - this.width; - } - - int var4 = var1; - if(var1 > this.anInt3698) { - var4 = this.anInt3698; - } - - int var5 = var1; - if(var1 + this.anInt3698 + this.height > this.anInt3706) { - var5 = this.anInt3706 - this.anInt3698 - this.height; - } - - int var6 = this.width + var2 + var3; - int var7 = this.height + var4 + var5; - int[] var8 = new int[var6 * var7]; - - for(int var9 = 0; var9 < this.height; ++var9) { - for(int var10 = 0; var10 < this.width; ++var10) { - var8[(var9 + var4) * var6 + var10 + var2] = this.anIntArray4081[var9 * this.width + var10]; - } - } - - this.anIntArray4081 = var8; - this.width = var6; - this.height = var7; - this.anInt3701 -= var2; - this.anInt3698 -= var4; - } - } - - final void method653() { - int[] var1 = new int[this.width * this.height]; - int var2 = 0; - - for(int var3 = 0; var3 < this.height; ++var3) { - for(int var4 = this.width - 1; var4 >= 0; --var4) { - var1[var2++] = this.anIntArray4081[var4 + var3 * this.width]; - } - } - - this.anIntArray4081 = var1; - this.anInt3701 = this.anInt3697 - this.width - this.anInt3701; - } - - void method635(int var1, int var2) { - var1 += this.anInt3701; - var2 += this.anInt3698; - int var3 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var4 = 0; - int var5 = this.height; - int var6 = this.width; - int var7 = Toolkit.JAVA_TOOLKIT.width - var6; - int var8 = 0; - int var9; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var9 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var5 -= var9; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var4 += var9 * var6; - var3 += var9 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var2 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var9 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var6 -= var9; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var4 += var9; - var3 += var9; - var8 += var9; - var7 += var9; - } - - if(var1 + var6 > Toolkit.JAVA_TOOLKIT.clipRight) { - var9 = var1 + var6 - Toolkit.JAVA_TOOLKIT.clipRight; - var6 -= var9; - var8 += var9; - var7 += var9; - } - - if(var6 > 0 && var5 > 0) { - method659(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var4, var3, var6, var5, var7, var8); - } - } - - private static void method654(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8) { - int var9 = -(var5 >> 2); - var5 = -(var5 & 3); - - for(int var10 = -var6; var10 < 0; ++var10) { - int var11; - int var2; - for(var11 = var9; var11 < 0; ++var11) { - var2 = var1[var3++]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - var2 = var1[var3++]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - var2 = var1[var3++]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - var2 = var1[var3++]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - } - - for(var11 = var5; var11 < 0; ++var11) { - var2 = var1[var3++]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - } - - var4 += var7; - var3 += var8; - } - - } - - final int[] method655() { - int[] var4 = new int[this.anInt3697 * this.anInt3706]; - - for(int var5 = 0; var5 < this.height; ++var5) { - int var1 = var5 * this.width; - int var2 = this.anInt3701 + (var5 + this.anInt3698) * this.anInt3697; - - for(int var6 = 0; var6 < this.width; ++var6) { - int var3 = this.anIntArray4081[var1++]; - var4[var2++] = var3 != 0?-16777216 | var3:0; - } - } - - return var4; - } - - void method636(int var1, int var2, int var3, int var4, int var5, int var6) { - if(var6 != 0) { - var1 -= this.anInt3701 << 4; - var2 -= this.anInt3698 << 4; - double var7 = (double)(var5 & 65535) * 9.587379924285257E-5D; - int var9 = (int)Math.floor(Math.sin(var7) * (double)var6 + 0.5D); - int var10 = (int)Math.floor(Math.cos(var7) * (double)var6 + 0.5D); - int var11 = -var1 * var10 + -var2 * var9; - int var12 = -(-var1) * var9 + -var2 * var10; - int var13 = ((this.width << 4) - var1) * var10 + -var2 * var9; - int var14 = -((this.width << 4) - var1) * var9 + -var2 * var10; - int var15 = -var1 * var10 + ((this.height << 4) - var2) * var9; - int var16 = -(-var1) * var9 + ((this.height << 4) - var2) * var10; - int var17 = ((this.width << 4) - var1) * var10 + ((this.height << 4) - var2) * var9; - int var18 = -((this.width << 4) - var1) * var9 + ((this.height << 4) - var2) * var10; - int var19; - int var20; - if(var11 < var13) { - var19 = var11; - var20 = var13; - } else { - var19 = var13; - var20 = var11; - } - - if(var15 < var19) { - var19 = var15; - } - - if(var17 < var19) { - var19 = var17; - } - - if(var15 > var20) { - var20 = var15; - } - - if(var17 > var20) { - var20 = var17; - } - - int var21; - int var22; - if(var12 < var14) { - var21 = var12; - var22 = var14; - } else { - var21 = var14; - var22 = var12; - } - - if(var16 < var21) { - var21 = var16; - } - - if(var18 < var21) { - var21 = var18; - } - - if(var16 > var22) { - var22 = var16; - } - - if(var18 > var22) { - var22 = var18; - } - - var19 >>= 12; - var20 = var20 + 4095 >> 12; - var21 >>= 12; - var22 = var22 + 4095 >> 12; - var19 += var3; - var20 += var3; - var21 += var4; - var22 += var4; - var19 >>= 4; - var20 = var20 + 15 >> 4; - var21 >>= 4; - var22 = var22 + 15 >> 4; - if(var19 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var19 = Toolkit.JAVA_TOOLKIT.clipLeft; - } - - if(var20 > Toolkit.JAVA_TOOLKIT.clipRight) { - var20 = Toolkit.JAVA_TOOLKIT.clipRight; - } - - if(var21 < Toolkit.JAVA_TOOLKIT.clipTop) { - var21 = Toolkit.JAVA_TOOLKIT.clipTop; - } - - if(var22 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var22 = Toolkit.JAVA_TOOLKIT.clipBottom; - } - - var20 = var19 - var20; - if(var20 < 0) { - var22 = var21 - var22; - if(var22 < 0) { - int var23 = var21 * Toolkit.JAVA_TOOLKIT.width + var19; - double var24 = 1.6777216E7D / (double)var6; - int var26 = (int)Math.floor(Math.sin(var7) * var24 + 0.5D); - int var27 = (int)Math.floor(Math.cos(var7) * var24 + 0.5D); - int var28 = (var19 << 4) + 8 - var3; - int var29 = (var21 << 4) + 8 - var4; - int var30 = (var1 << 8) - (var29 * var26 >> 4); - int var31 = (var2 << 8) + (var29 * var27 >> 4); - int var34; - int var35; - int var32; - int var33; - int var38; - int var36; - int var37; - if(var27 == 0) { - if(var26 == 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30; - var36 = var31; - var37 = var20; - if(var30 >= 0 && var31 >= 0 && var30 - (this.width << 12) < 0 && var31 - (this.height << 12) < 0) { - for(; var37 < 0; ++var37) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - } - } - - ++var33; - } - } else if(var26 < 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30; - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if(var30 >= 0 && var30 - (this.width << 12) < 0) { - if((var32 = var36 - (this.height << 12)) >= 0) { - var32 = (var26 - var32) / var26; - var37 = var20 + var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (var36 - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - - var36 += var26; - ++var37; - } - } - - ++var33; - var30 -= var26; - } - } else { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30; - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if(var30 >= 0 && var30 - (this.width << 12) < 0) { - if(var36 < 0) { - var32 = (var26 - 1 - var36) / var26; - var37 = var20 + var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (1 + var36 - (this.height << 12) - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - - var36 += var26; - ++var37; - } - } - - ++var33; - var30 -= var26; - } - } - } else if(var27 < 0) { - if(var26 == 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31; - var37 = var20; - if(var31 >= 0 && var31 - (this.height << 12) < 0) { - if((var32 = var35 - (this.width << 12)) >= 0) { - var32 = (var27 - var32) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var34 = var23 + var32; - } - - if((var32 = (var35 - var27) / var27) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - - var35 += var27; - ++var37; - } - } - - ++var33; - var31 += var27; - } - } else if(var26 < 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if((var32 = var35 - (this.width << 12)) >= 0) { - var32 = (var27 - var32) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (var35 - var27) / var27) > var37) { - var37 = var32; - } - - if((var32 = var36 - (this.height << 12)) >= 0) { - var32 = (var26 - var32) / var26; - var37 += var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 += var32; - } - - if((var32 = (var36 - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - - var35 += var27; - var36 += var26; - ++var37; - } - - ++var33; - var30 -= var26; - var31 += var27; - } - } else { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if((var32 = var35 - (this.width << 12)) >= 0) { - var32 = (var27 - var32) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (var35 - var27) / var27) > var37) { - var37 = var32; - } - - if(var36 < 0) { - var32 = (var26 - 1 - var36) / var26; - var37 += var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 += var32; - } - - if((var32 = (1 + var36 - (this.height << 12) - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - - var35 += var27; - var36 += var26; - ++var37; - } - - ++var33; - var30 -= var26; - var31 += var27; - } - } - } else if(var26 == 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31; - var37 = var20; - if(var31 >= 0 && var31 - (this.height << 12) < 0) { - if(var35 < 0) { - var32 = (var27 - 1 - var35) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var34 = var23 + var32; - } - - if((var32 = (1 + var35 - (this.width << 12) - var27) / var27) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - - var35 += var27; - ++var37; - } - } - - ++var33; - var31 += var27; - } - } else if(var26 < 0) { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if(var35 < 0) { - var32 = (var27 - 1 - var35) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (1 + var35 - (this.width << 12) - var27) / var27) > var37) { - var37 = var32; - } - - if((var32 = var36 - (this.height << 12)) >= 0) { - var32 = (var26 - var32) / var26; - var37 += var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 += var32; - } - - if((var32 = (var36 - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - - var35 += var27; - var36 += var26; - ++var37; - } - - ++var33; - var30 -= var26; - var31 += var27; - } - } else { - for(var33 = var22; var33 < 0; var23 += Toolkit.JAVA_TOOLKIT.width) { - var34 = var23; - var35 = var30 + (var28 * var27 >> 4); - var36 = var31 + (var28 * var26 >> 4); - var37 = var20; - if(var35 < 0) { - var32 = (var27 - 1 - var35) / var27; - var37 = var20 + var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 = var23 + var32; - } - - if((var32 = (1 + var35 - (this.width << 12) - var27) / var27) > var37) { - var37 = var32; - } - - if(var36 < 0) { - var32 = (var26 - 1 - var36) / var26; - var37 += var32; - var35 += var27 * var32; - var36 += var26 * var32; - var34 += var32; - } - - if((var32 = (1 + var36 - (this.height << 12) - var26) / var26) > var37) { - var37 = var32; - } - - while(var37 < 0) { - var38 = this.anIntArray4081[(var36 >> 12) * this.width + (var35 >> 12)]; - if(var38 == 0) { - ++var34; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var34++] = var38; - } - - var35 += var27; - var36 += var26; - ++var37; - } - - ++var33; - var30 -= var26; - var31 += var27; - } - } - - } - } - } - } - - private static void method656(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8) { - int var9 = -(var5 >> 2); - var5 = -(var5 & 3); - - for(int var10 = -var6; var10 < 0; ++var10) { - int var11; - int var2; - for(var11 = var9; var11 < 0; ++var11) { - var2 = var1[var3--]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - var2 = var1[var3--]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - var2 = var1[var3--]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - - var2 = var1[var3--]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - } - - for(var11 = var5; var11 < 0; ++var11) { - var2 = var1[var3--]; - if(var2 == 0) { - ++var4; - } else { - var0[var4++] = var2; - } - } - - var4 += var7; - var3 += var8; - } - - } - - final void method657(int var1) { - int[] var2 = new int[this.width * this.height]; - int var3 = 0; - - for(int var4 = 0; var4 < this.height; ++var4) { - for(int var5 = 0; var5 < this.width; ++var5) { - int var6 = this.anIntArray4081[var3]; - if(var6 == 0) { - if(var5 > 0 && this.anIntArray4081[var3 - 1] != 0) { - var6 = var1; - } else if(var4 > 0 && this.anIntArray4081[var3 - this.width] != 0) { - var6 = var1; - } else if(var5 < this.width - 1 && this.anIntArray4081[var3 + 1] != 0) { - var6 = var1; - } else if(var4 < this.height - 1 && this.anIntArray4081[var3 + this.width] != 0) { - var6 = var1; - } - } - - var2[var3++] = var6; - } - } - - this.anIntArray4081 = var2; - } - - final void method658() { - Class74.setBuffer(this.anIntArray4081, this.width, this.height); - } - - private static void method659(int[] var0, int[] var1, int var2, int var3, int var4, int var5, int var6, int var7) { - for(int var8 = -var5; var8 < 0; ++var8) { - int var9; - for(var9 = var3 + var4 - 3; var3 < var9; var0[var3++] = var1[var2++]) { - var0[var3++] = var1[var2++]; - var0[var3++] = var1[var2++]; - var0[var3++] = var1[var2++]; - } - - for(var9 += 3; var3 < var9; var0[var3++] = var1[var2++]) { - } - - var3 += var6; - var2 += var7; - } - - } - - void method660(int var1, int var2, double var7) { - try { - int var10 = -20 / 2; - int var11 = -20 / 2; - int var12 = (int)(Math.sin(var7) * 65536.0D); - int var13 = (int)(Math.cos(var7) * 65536.0D); - var12 = var12 * 256 >> 8; - var13 = var13 * 256 >> 8; - int var14 = (15 << 16) + var11 * var12 + var10 * var13; - int var15 = (15 << 16) + (var11 * var13 - var10 * var12); - int var16 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - - for(var2 = 0; var2 < 20; ++var2) { - int var17 = var16; - int var18 = var14; - int var19 = var15; - - for(var1 = -20; var1 < 0; ++var1) { - int var20 = this.anIntArray4081[(var18 >> 16) + (var19 >> 16) * this.width]; - if(var20 == 0) { - ++var17; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var17++] = var20; - } - - var18 += var13; - var19 -= var12; - } - - var14 += var12; - var15 += var13; - var16 += Toolkit.JAVA_TOOLKIT.width; - } - } catch (Exception var21) { - } - - } - - void method641(int var1, int var2) { - var1 += this.anInt3697 - this.width - this.anInt3701; - var2 += this.anInt3698; - int var3 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var4 = this.width - 1; - int var5 = this.height; - int var6 = this.width; - int var7 = Toolkit.JAVA_TOOLKIT.width - var6; - int var8 = var6 + var6; - int var9; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var9 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var5 -= var9; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var4 += var9 * var6; - var3 += var9 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var2 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var9 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var6 -= var9; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var4 -= var9; - var3 += var9; - var8 -= var9; - var7 += var9; - } - - if(var1 + var6 > Toolkit.JAVA_TOOLKIT.clipRight) { - var9 = var1 + var6 - Toolkit.JAVA_TOOLKIT.clipRight; - var6 -= var9; - var8 -= var9; - var7 += var9; - } - - if(var6 > 0 && var5 > 0) { - method656(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var4, var3, var6, var5, var7, var8); - } - } - - SoftwareSprite(int var1, int var2, int var3, int var4, int var5, int var6, int[] var7) { - this.anInt3697 = var1; - this.anInt3706 = var2; - this.anInt3701 = var3; - this.anInt3698 = var4; - this.width = var5; - this.height = var6; - this.anIntArray4081 = var7; - } - - void method637(int var1, int var2, int var3) { - var1 += this.anInt3701; - var2 += this.anInt3698; - int var4 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var5 = 0; - int var6 = this.height; - int var7 = this.width; - int var8 = Toolkit.JAVA_TOOLKIT.width - var7; - int var9 = 0; - int var10; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var10 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var6 -= var10; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var5 += var10 * var7; - var4 += var10 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var6 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var6 -= var2 + var6 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var10 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var7 -= var10; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var5 += var10; - var4 += var10; - var9 += var10; - var8 += var10; - } - - if(var1 + var7 > Toolkit.JAVA_TOOLKIT.clipRight) { - var10 = var1 + var7 - Toolkit.JAVA_TOOLKIT.clipRight; - var7 -= var10; - var9 += var10; - var8 += var10; - } - - if(var7 > 0 && var6 > 0) { - method662(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var5, var4, var7, var6, var8, var9, var3); - } - } - - private static void method661(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11, int var12) { - int var13 = 256 - var12; - int var14 = var3; - - for(int var15 = -var8; var15 < 0; ++var15) { - int var16 = (var4 >> 16) * var11; - - for(int var17 = -var7; var17 < 0; ++var17) { - int var2 = var1[(var3 >> 16) + var16]; - if(var2 == 0) { - ++var5; - } else { - int var18 = var0[var5]; - var0[var5++] = ((var2 & 16711935) * var12 + (var18 & 16711935) * var13 & -16711936) + ((var2 & 65280) * var12 + (var18 & 65280) * var13 & 16711680) >> 8; - } - - var3 += var9; - } - - var4 += var10; - var3 = var14; - var5 += var6; - } - - } - - private static void method662(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - int var10 = 256 - var9; - - for(int var11 = -var6; var11 < 0; ++var11) { - for(int var12 = -var5; var12 < 0; ++var12) { - int var2 = var1[var3++]; - if(var2 == 0) { - ++var4; - } else { - int var13 = var0[var4]; - var0[var4++] = ((var2 & 16711935) * var9 + (var13 & 16711935) * var10 & -16711936) + ((var2 & 65280) * var9 + (var13 & 65280) * var10 & 16711680) >> 8; - } - } - - var4 += var7; - var3 += var8; - } - - } - - public void drawAt(int var1, int var2) { - var1 += this.anInt3701; - var2 += this.anInt3698; - int var3 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var4 = 0; - int var5 = this.height; - int var6 = this.width; - int var7 = Toolkit.JAVA_TOOLKIT.width - var6; - int var8 = 0; - int var9; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var9 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var5 -= var9; - var2 = Toolkit.JAVA_TOOLKIT.clipTop; - var4 += var9 * var6; - var3 += var9 * Toolkit.JAVA_TOOLKIT.width; - } - - if(var2 + var5 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var5 -= var2 + var5 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var9 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var6 -= var9; - var1 = Toolkit.JAVA_TOOLKIT.clipLeft; - var4 += var9; - var3 += var9; - var8 += var9; - var7 += var9; - } - - if(var1 + var6 > Toolkit.JAVA_TOOLKIT.clipRight) { - var9 = var1 + var6 - Toolkit.JAVA_TOOLKIT.clipRight; - var6 -= var9; - var8 += var9; - var7 += var9; - } - - if(var6 > 0 && var5 > 0) { - method654(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var4, var3, var6, var5, var7, var8); - } - } - - final void method663() { - int[] var1 = new int[this.width * this.height]; - int var2 = 0; - - for(int var3 = this.height - 1; var3 >= 0; --var3) { - for(int var4 = 0; var4 < this.width; ++var4) { - var1[var2++] = this.anIntArray4081[var4 + var3 * this.width]; - } - } - - this.anIntArray4081 = var1; - this.anInt3698 = this.anInt3706 - this.height - this.anInt3698; - } - - public void drawMinimapRegion(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int[] var9, int[] var10) { - try { - int var11 = -var3 / 2; - int var12 = -var4 / 2; - int var13 = (int)(Math.sin((double)var7 / 326.11D) * 65536.0D); - int var14 = (int)(Math.cos((double)var7 / 326.11D) * 65536.0D); - var13 = var13 * var8 >> 8; - var14 = var14 * var8 >> 8; - int var15 = (var5 << 16) + var12 * var13 + var11 * var14; - int var16 = (var6 << 16) + (var12 * var14 - var11 * var13); - int var17 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - - for(var2 = 0; var2 < var4; ++var2) { - int var18 = var9[var2]; - int var19 = var17 + var18; - int var20 = var15 + var14 * var18; - int var21 = var16 - var13 * var18; - - for(var1 = -var10[var2]; var1 < 0; ++var1) { - Toolkit.JAVA_TOOLKIT.getBuffer()[var19++] = this.anIntArray4081[(var20 >> 16) + (var21 >> 16) * this.width]; - var20 += var14; - var21 -= var13; - } - - var15 += var13; - var16 += var14; - var17 += Toolkit.JAVA_TOOLKIT.width; - } - } catch (Exception var22) { - } - - } - - final void method665() { - if(this.width != this.anInt3697 || this.height != this.anInt3706) { - int[] var1 = new int[this.anInt3697 * this.anInt3706]; - - for(int var2 = 0; var2 < this.height; ++var2) { - for(int var3 = 0; var3 < this.width; ++var3) { - var1[(var2 + this.anInt3698) * this.anInt3697 + var3 + this.anInt3701] = this.anIntArray4081[var2 * this.width + var3]; - } - } - - this.anIntArray4081 = var1; - this.width = this.anInt3697; - this.height = this.anInt3706; - this.anInt3701 = 0; - this.anInt3698 = 0; - } - } - - final void drawMinimapIcons(int interfaceWidth, int interfaceHeight, int[] var3, int[] var4) { - if(Toolkit.JAVA_TOOLKIT.clipBottom - Toolkit.JAVA_TOOLKIT.clipTop == var3.length) { - interfaceWidth += this.anInt3701; - interfaceHeight += this.anInt3698; - int var5 = 0; - int var6 = this.height; - int var7 = this.width; - int var8 = Toolkit.JAVA_TOOLKIT.width - var7; - int var9 = 0; - int var10 = interfaceWidth + interfaceHeight * Toolkit.JAVA_TOOLKIT.width; - int var11; - if(interfaceHeight < Toolkit.JAVA_TOOLKIT.clipTop) { - var11 = Toolkit.JAVA_TOOLKIT.clipTop - interfaceHeight; - var6 -= var11; - interfaceHeight = Toolkit.JAVA_TOOLKIT.clipTop; - var5 += var11 * var7; - var10 += var11 * Toolkit.JAVA_TOOLKIT.width; - } - - if(interfaceHeight + var6 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var6 -= interfaceHeight + var6 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - if(interfaceWidth < Toolkit.JAVA_TOOLKIT.clipLeft) { - var11 = Toolkit.JAVA_TOOLKIT.clipLeft - interfaceWidth; - var7 -= var11; - interfaceWidth = Toolkit.JAVA_TOOLKIT.clipLeft; - var5 += var11; - var10 += var11; - var9 += var11; - var8 += var11; - } - - if(interfaceWidth + var7 > Toolkit.JAVA_TOOLKIT.clipRight) { - var11 = interfaceWidth + var7 - Toolkit.JAVA_TOOLKIT.clipRight; - var7 -= var11; - var9 += var11; - var8 += var11; - } - - if(var7 > 0 && var6 > 0) { - var11 = interfaceWidth - Toolkit.JAVA_TOOLKIT.clipLeft; - int var12 = interfaceHeight - Toolkit.JAVA_TOOLKIT.clipTop; - - for(int var13 = var12; var13 < var12 + var6; ++var13) { - int var14 = var3[var13]; - int var15 = var4[var13]; - int var16 = var7; - int var17; - if(var11 > var14) { - var17 = var11 - var14; - if(var17 >= var15) { - var5 += var7 + var9; - var10 += var7 + var8; - continue; - } - - var15 -= var17; - } else { - var17 = var14 - var11; - if(var17 >= var7) { - var5 += var7 + var9; - var10 += var7 + var8; - continue; - } - - var5 += var17; - var16 = var7 - var17; - var10 += var17; - } - - var17 = 0; - if(var16 < var15) { - var15 = var16; - } else { - var17 = var16 - var15; - } - - for(int var18 = -var15; var18 < 0; ++var18) { - int var19 = this.anIntArray4081[var5++]; - if(var19 == 0) { - ++var10; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var10++] = var19; - } - } - - var5 += var17 + var9; - var10 += var17 + var8; - } - - } - } else { - throw new IllegalStateException(); - } - } - - SoftwareSprite(int var1, int var2) { - this.anIntArray4081 = new int[var1 * var2]; - this.width = this.anInt3697 = var1; - this.height = this.anInt3706 = var2; - this.anInt3701 = this.anInt3698 = 0; - } - - public void method667(int var1, int var2, int var3, int var4, int var5, int var6, int var7, int[] var9, int[] var10) { - try { - int var11 = -var3 / 2; - int var12 = -var4 / 2; - int var13 = (int)(Math.sin((double)var7 / 326.11D) * 65536.0D); - int var14 = (int)(Math.cos((double)var7 / 326.11D) * 65536.0D); - var13 = var13 * 256 >> 8; - var14 = var14 * 256 >> 8; - int var15 = (var5 << 16) + var12 * var13 + var11 * var14; - int var16 = (var6 << 16) + (var12 * var14 - var11 * var13); - int var17 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - - for(var2 = 0; var2 < var4; ++var2) { - int var18 = var9[var2]; - int var19 = var17 + var18; - int var20 = var15 + var14 * var18; - int var21 = var16 - var13 * var18; - - for(var1 = -var10[var2]; var1 < 0; ++var1) { - int var22 = this.anIntArray4081[(var20 >> 16) + (var21 >> 16) * this.width]; - if(var22 == 0) { - ++var19; - } else { - Toolkit.JAVA_TOOLKIT.getBuffer()[var19++] = var22; - } - - var20 += var14; - var21 -= var13; - } - - var15 += var13; - var16 += var14; - var17 += Toolkit.JAVA_TOOLKIT.width; - } - } catch (Exception var23) { - } - - } - - final void method668(int var1) { - for(int var2 = this.height - 1; var2 > 0; --var2) { - int var3 = var2 * this.width; - - for(int var4 = this.width - 1; var4 > 0; --var4) { - if(this.anIntArray4081[var4 + var3] == 0 && this.anIntArray4081[var4 + var3 - 1 - this.width] != 0) { - this.anIntArray4081[var4 + var3] = var1; - } - } - } - - } - - void method642(int var1, int var2, int var3, int var4, int var5) { - if(var3 > 0 && var4 > 0) { - int var6 = this.width; - int var7 = this.height; - int var8 = 0; - int var9 = 0; - int var10 = this.anInt3697; - int var11 = this.anInt3706; - int var12 = (var10 << 16) / var3; - int var13 = (var11 << 16) / var4; - int var14; - if(this.anInt3701 > 0) { - var14 = ((this.anInt3701 << 16) + var12 - 1) / var12; - var1 += var14; - var8 += var14 * var12 - (this.anInt3701 << 16); - } - - if(this.anInt3698 > 0) { - var14 = ((this.anInt3698 << 16) + var13 - 1) / var13; - var2 += var14; - var9 += var14 * var13 - (this.anInt3698 << 16); - } - - if(var6 < var10) { - var3 = ((var6 << 16) - var8 + var12 - 1) / var12; - } - - if(var7 < var11) { - var4 = ((var7 << 16) - var9 + var13 - 1) / var13; - } - - var14 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var15 = Toolkit.JAVA_TOOLKIT.width - var3; - if(var2 + var4 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var4 -= var2 + var4 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var16; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var16 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var4 -= var16; - var14 += var16 * Toolkit.JAVA_TOOLKIT.width; - var9 += var13 * var16; - } - - if(var1 + var3 > Toolkit.JAVA_TOOLKIT.clipRight) { - var16 = var1 + var3 - Toolkit.JAVA_TOOLKIT.clipRight; - var3 -= var16; - var15 += var16; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var16 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var3 -= var16; - var14 += var16; - var8 += var12 * var16; - var15 += var16; - } - - method661(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var8, var9, var14, var15, var3, var4, var12, var13, var6, var5); - } - } - - final void method669(int var1, int var2, int var3) { - for(int var4 = 0; var4 < this.anIntArray4081.length; ++var4) { - int var5 = this.anIntArray4081[var4]; - if(var5 != 0) { - int var6 = var5 >> 16 & 0xFF; - var6 += var1; - if(var6 < 1) { - var6 = 1; - } else if(var6 > 255) { - var6 = 255; - } - - int var7 = var5 >> 8 & 0xFF; - var7 += var2; - if(var7 < 1) { - var7 = 1; - } else if(var7 > 255) { - var7 = 255; - } - - int var8 = var5 & 0xFF; - var8 += var3; - if(var8 < 1) { - var8 = 1; - } else if(var8 > 255) { - var8 = 255; - } - - this.anIntArray4081[var4] = (var6 << 16) + (var7 << 8) + var8; - } - } - - } - - public void method639(int var1, int var2, int var3, int var4) { - if(var3 > 0 && var4 > 0) { - int var5 = this.width; - int var6 = this.height; - int var7 = 0; - int var8 = 0; - int var9 = this.anInt3697; - int var10 = this.anInt3706; - int var11 = (var9 << 16) / var3; - int var12 = (var10 << 16) / var4; - int var13; - if(this.anInt3701 > 0) { - var13 = ((this.anInt3701 << 16) + var11 - 1) / var11; - var1 += var13; - var7 += var13 * var11 - (this.anInt3701 << 16); - } - - if(this.anInt3698 > 0) { - var13 = ((this.anInt3698 << 16) + var12 - 1) / var12; - var2 += var13; - var8 += var13 * var12 - (this.anInt3698 << 16); - } - - if(var5 < var9) { - var3 = ((var5 << 16) - var7 + var11 - 1) / var11; - } - - if(var6 < var10) { - var4 = ((var6 << 16) - var8 + var12 - 1) / var12; - } - - var13 = var1 + var2 * Toolkit.JAVA_TOOLKIT.width; - int var14 = Toolkit.JAVA_TOOLKIT.width - var3; - if(var2 + var4 > Toolkit.JAVA_TOOLKIT.clipBottom) { - var4 -= var2 + var4 - Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var15; - if(var2 < Toolkit.JAVA_TOOLKIT.clipTop) { - var15 = Toolkit.JAVA_TOOLKIT.clipTop - var2; - var4 -= var15; - var13 += var15 * Toolkit.JAVA_TOOLKIT.width; - var8 += var12 * var15; - } - - if(var1 + var3 > Toolkit.JAVA_TOOLKIT.clipRight) { - var15 = var1 + var3 - Toolkit.JAVA_TOOLKIT.clipRight; - var3 -= var15; - var14 += var15; - } - - if(var1 < Toolkit.JAVA_TOOLKIT.clipLeft) { - var15 = Toolkit.JAVA_TOOLKIT.clipLeft - var1; - var3 -= var15; - var13 += var15; - var7 += var11 * var15; - var14 += var15; - } - - method670(Toolkit.JAVA_TOOLKIT.getBuffer(), this.anIntArray4081, var7, var8, var13, var14, var3, var4, var11, var12, var5); - } - } - - private static void method670(int[] var0, int[] var1, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var10, int var11) { - int var12 = var3; - - for(int var13 = -var8; var13 < 0; ++var13) { - int var14 = (var4 >> 16) * var11; - - for(int var15 = -var7; var15 < 0; ++var15) { - int var2 = var1[(var3 >> 16) + var14]; - if(var2 == 0) { - ++var5; - } else { - var0[var5++] = var2; - } - - var3 += var9; - } - - var4 += var10; - var3 = var12; - var5 += var6; - } - - } -} diff --git a/Client/src/main/java/org/runite/client/Sound.java b/Client/src/main/java/org/runite/client/Sound.java deleted file mode 100644 index da65318c3..000000000 --- a/Client/src/main/java/org/runite/client/Sound.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; - -abstract class Sound extends Linkable { - - int anInt2374; - - -} diff --git a/Client/src/main/java/org/runite/client/Sprites.java b/Client/src/main/java/org/runite/client/Sprites.java deleted file mode 100644 index 7b6ac9dbe..000000000 --- a/Client/src/main/java/org/runite/client/Sprites.java +++ /dev/null @@ -1,243 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class Sprites { - - public static int compassSpriteArchive; - public static int hintMapEdgeSpriteArchive; - public static AbstractIndexedSprite[] nameIconsSpriteArray; - static boolean aBoolean337; - static int ambientVolume = 127; - static int p11FullSpriteArchive; - static int p12FullSpriteArchive; - static int b12FullSpriteArchive; - static int mapFunctionSpriteArchive; - static int hitmarkSpriteArchive; - static int hitbarDefaultSpriteArchive; - static int headiconsPkSpriteArchive; - static int headiconsPrayerSpriteArchive; - static int headiconsHintSpriteArchive; - static int hintMapMarkersSpriteArchive; - static int mapFlagSpriteArchive; - static int crossSpriteArchive; - static int mapDotsSpriteArchive; - static int scrollbarSpriteArchive; - static int nameIconsSpriteArchive; - static int floorShadowSpriteArchive; - static SoftwareSprite[] aSoftwareSpriteArray2140; - - - private static LDIndexedSprite[] method885(int var1, CacheIndex var2) { - try { - // System.out.println("Class 14 " + var1); - return !Class75_Sub4.method1351(var2, 0, var1) ? null : Unsorted.method1281(); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "cg.C(" + true + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + 0 + ')'); - } - } - - static void method887(CacheIndex index8) { - try { - aSoftwareSpriteArray2140 = Class157.method2176(mapFunctionSpriteArchive, index8); - Class75_Sub3.aAbstractSpriteArray2656 = Class140_Sub6.getSprites(hitmarkSpriteArchive, index8); - Unsorted.aAbstractSpriteArray996 = Class140_Sub6.getSprites(hitbarDefaultSpriteArchive, index8); - TextureOperation2.aAbstractSpriteArray3373 = Class140_Sub6.getSprites(headiconsPkSpriteArchive, index8); - NPC.aAbstractSpriteArray3977 = Class140_Sub6.getSprites(headiconsPrayerSpriteArchive, index8); - Class166.aAbstractSpriteArray2072 = Class140_Sub6.getSprites(headiconsHintSpriteArchive, index8); - Class129_Sub1.aAbstractSpriteArray2690 = Class140_Sub6.getSprites(hintMapMarkersSpriteArchive, index8); - Class45.aAbstractSprite_736 = Unsorted.method602(mapFlagSpriteArchive, index8); - Class65.aAbstractSpriteArray1825 = TextureOperation18.method286(crossSpriteArchive, index8); - Unsorted.aAbstractSpriteArray1136 = TextureOperation18.method286(mapDotsSpriteArchive, index8); - GameObject.aClass109Array1831 = Class85.method1424(index8, scrollbarSpriteArchive); - nameIconsSpriteArray = Class85.method1424(index8, nameIconsSpriteArchive); - FontType.smallFont.method697(nameIconsSpriteArray, null); - FontType.plainFont.method697(nameIconsSpriteArray, null); - FontType.bold.method697(nameIconsSpriteArray, null); - if (HDToolKit.highDetail) { - Class141.aClass109_Sub1Array1843 = method885(floorShadowSpriteArchive, index8); - - for (int var2 = 0; var2 < Objects.requireNonNull(Class141.aClass109_Sub1Array1843).length; ++var2) { - Class141.aClass109_Sub1Array1843[var2].method1675(); - } - } - - SoftwareSprite var10 = Class40.method1043(0, index8, compassSpriteArchive); - Objects.requireNonNull(var10).method665(); - if (HDToolKit.highDetail) { - Class57.aAbstractSprite_895 = new HDSprite(var10); - } else { - Class57.aAbstractSprite_895 = var10; - } - - SoftwareSprite[] var3 = Class157.method2176(hintMapEdgeSpriteArchive, index8); - - int var4; - for (var4 = 0; Objects.requireNonNull(var3).length > var4; ++var4) { - var3[var4].method665(); - } - - if (HDToolKit.highDetail) { - TextureOperation8.aAbstractSpriteArray3458 = new AbstractSprite[var3.length]; - - for (var4 = 0; var4 < var3.length; ++var4) { - TextureOperation8.aAbstractSpriteArray3458[var4] = new HDSprite(var3[var4]); - } - } else { - TextureOperation8.aAbstractSpriteArray3458 = var3; - } - - int var5 = (int) ((double) 21 * Math.random()) - 10; - var4 = (int) (21.0D * Math.random()) - 10; - int var6 = -10 + (int) (21.0D * Math.random()); - int var7 = -20 + (int) (Math.random() * 41.0D); - - int var8; - for (var8 = 0; var8 < aSoftwareSpriteArray2140.length; ++var8) { - aSoftwareSpriteArray2140[var8].method669(var4 + var7, var7 + var5, var7 + var6); - } - - if (HDToolKit.highDetail) { - Class140_Sub4.aAbstractSpriteArray2839 = new AbstractSprite[aSoftwareSpriteArray2140.length]; - - for (var8 = 0; var8 < aSoftwareSpriteArray2140.length; ++var8) { - Class140_Sub4.aAbstractSpriteArray2839[var8] = new HDSprite(aSoftwareSpriteArray2140[var8]); - } - } else { - Class140_Sub4.aAbstractSpriteArray2839 = aSoftwareSpriteArray2140; - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "cg.A(" + 21 + ',' + (index8 != null ? "{...}" : "null") + ')'); - } - } - - static void getSpriteFromArchive(CacheIndex index8) { - try { - p11FullSpriteArchive = index8.getArchiveForName(RSString.parse("p11_full")); - p12FullSpriteArchive = index8.getArchiveForName(RSString.parse("p12_full")); - b12FullSpriteArchive = index8.getArchiveForName(RSString.parse("b12_full")); - mapFunctionSpriteArchive = index8.getArchiveForName(RSString.parse("mapfunction")); - hitmarkSpriteArchive = index8.getArchiveForName(RSString.parse("hitmarks")); - hitbarDefaultSpriteArchive = index8.getArchiveForName(RSString.parse("hitbar_default")); - headiconsPkSpriteArchive = index8.getArchiveForName(RSString.parse("headicons_pk")); - - headiconsPrayerSpriteArchive = index8.getArchiveForName(RSString.parse("headicons_prayer")); - headiconsHintSpriteArchive = index8.getArchiveForName(RSString.parse("hint_headicons")); - hintMapMarkersSpriteArchive = index8.getArchiveForName(RSString.parse("hint_mapmarkers")); - mapFlagSpriteArchive = index8.getArchiveForName(RSString.parse("mapflag")); - crossSpriteArchive = index8.getArchiveForName(RSString.parse("cross")); - mapDotsSpriteArchive = index8.getArchiveForName(RSString.parse("mapdots")); - scrollbarSpriteArchive = index8.getArchiveForName(RSString.parse("scrollbar")); - nameIconsSpriteArchive = index8.getArchiveForName(RSString.parse("name_icons")); - floorShadowSpriteArchive = index8.getArchiveForName(RSString.parse("floorshadows")); - compassSpriteArchive = index8.getArchiveForName(RSString.parse("compass")); - hintMapEdgeSpriteArchive = index8.getArchiveForName(RSString.parse("hint_mapedge")); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "g.C(" + 208 + ',' + (index8 != null ? "{...}" : "null") + ')'); - } - } - - static int method228(CacheIndex var0, CacheIndex var1) { - try { - int var3 = 0; - if (var0.retrieveSpriteFile(p11FullSpriteArchive)) { - ++var3; - } - - if (var0.retrieveSpriteFile(p12FullSpriteArchive)) { - ++var3; - } - - if (var0.retrieveSpriteFile(b12FullSpriteArchive)) { - ++var3; - } - - if (var1.retrieveSpriteFile(p11FullSpriteArchive)) { - ++var3; - } - - - if (var1.retrieveSpriteFile(p12FullSpriteArchive)) { - ++var3; - } - - if (var1.retrieveSpriteFile(b12FullSpriteArchive)) { - ++var3; - } - - return var3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "fn.B(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static int method107(CacheIndex index) { - try { - int var2 = 0;//0 - if (index.retrieveSpriteFile(mapFunctionSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(hitmarkSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(hitbarDefaultSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(headiconsPkSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(headiconsPrayerSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(headiconsHintSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(hintMapMarkersSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(mapFlagSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(crossSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(mapDotsSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(scrollbarSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(nameIconsSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(floorShadowSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(compassSpriteArchive)) { - ++var2; - } - - if (index.retrieveSpriteFile(hintMapEdgeSpriteArchive)) { - ++var2; - } - - return var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "cd.B(" + (index != null ? "{...}" : "null") + ',' + (byte) -125 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/SynthInstrument.java b/Client/src/main/java/org/runite/client/SynthInstrument.java deleted file mode 100644 index 724898b8d..000000000 --- a/Client/src/main/java/org/runite/client/SynthInstrument.java +++ /dev/null @@ -1,304 +0,0 @@ -package org.runite.client; - -import org.rs09.client.util.ArrayUtils; - -import java.util.Random; - -final class SynthInstrument { - - private static final int[] anIntArray1591; - private static final int[] anIntArray1594 = new int[32768]; - private static final int[] output; - private static final int[] anIntArray1599; - private static final int[] anIntArray1600; - private static final int[] anIntArray1601; - private static final int[] anIntArray1602; - private static final int[] anIntArray1603; - static int[][] coefficency = new int[2][8]; - static int inverseA0; - - static { - Random var0 = new Random(0L); - - int var1; - for (var1 = 0; var1 < 32768; ++var1) { - anIntArray1594[var1] = (var0.nextInt() & 2) - 1; - } - - anIntArray1591 = new int[32768]; - - for (var1 = 0; var1 < 32768; ++var1) { - anIntArray1591[var1] = (int) (Math.sin((double) var1 / 5215.1903D) * 16384.0D); - } - - output = new int[220500]; - anIntArray1599 = new int[5]; - anIntArray1600 = new int[5]; - anIntArray1601 = new int[5]; - anIntArray1603 = new int[5]; - anIntArray1602 = new int[5]; - } - - private final int[] oscillatorAmplitudes = new int[]{0, 0, 0, 0, 0}; - private final int[] oscillatorStartMillis = new int[]{0, 0, 0, 0, 0}; - private final int[] anIntArray1605 = new int[]{0, 0, 0, 0, 0}; - int duration = 500; - int start = 0; - private Envelope amplitudeModulationAmplitudeEnvelope; - private Envelope gateClosedPhaseEnvelope; - private Envelope amplitudeEnvelope; - private Envelope gateOpenPhaseEnvelope; - private Envelope phaseEnvelope; - private int delayTime = 0; - private Envelope filterEnvelope; - private Envelope amplitudeModulationEnvelope; - private int delayMix = 100; - private Filter filter; - private Envelope pitch_mod_amp_env; - private Envelope pitch_mod_env; - - private int method1716(int var1, int var2, int var3) { - return var3 == 1 ? ((var1 & 32767) < 16384 ? var2 : -var2) : (var3 == 2 ? anIntArray1591[var1 & 32767] * var2 >> 14 : (var3 == 3 ? ((var1 & 32767) * var2 >> 14) - var2 : (var3 == 4 ? anIntArray1594[var1 / 2607 & 32767] * var2 : 0))); - } - - final int[] synthesize(int num_samples, int dt) { - ArrayUtils.zero(output, 0, num_samples); - if (dt >= 10) { - double sample_rate = (double) num_samples / ((double) dt + 0.0D); - this.phaseEnvelope.reset(); - this.amplitudeEnvelope.reset(); - int pm_phase_delta = 0; - int pm_phase_delta_base = 0; - int pm_phase = 0; - if (this.pitch_mod_env != null) { - this.pitch_mod_env.reset(); - this.pitch_mod_amp_env.reset(); - pm_phase_delta = (int) ((double) (this.pitch_mod_env.maxInterval - this.pitch_mod_env.minInterval) * 32.768D / sample_rate); - pm_phase_delta_base = (int) ((double) this.pitch_mod_env.minInterval * 32.768D / sample_rate); - } - - int var8 = 0; - int var9 = 0; - int var10 = 0; - if (this.amplitudeModulationEnvelope != null) { - this.amplitudeModulationEnvelope.reset(); - this.amplitudeModulationAmplitudeEnvelope.reset(); - var8 = (int) ((double) (this.amplitudeModulationEnvelope.maxInterval - this.amplitudeModulationEnvelope.minInterval) * 32.768D / sample_rate); - var9 = (int) ((double) this.amplitudeModulationEnvelope.minInterval * 32.768D / sample_rate); - } - - int var11; - for (var11 = 0; var11 < 5; ++var11) { - if (this.oscillatorAmplitudes[var11] != 0) { - anIntArray1601[var11] = 0; - anIntArray1602[var11] = (int) ((double) this.oscillatorStartMillis[var11] * sample_rate); - anIntArray1603[var11] = (this.oscillatorAmplitudes[var11] << 14) / 100; - anIntArray1599[var11] = (int) ((double) (this.phaseEnvelope.maxInterval - this.phaseEnvelope.minInterval) * 32.768D * Math.pow(1.0057929410678534D, this.anIntArray1605[var11]) / sample_rate); - anIntArray1600[var11] = (int) ((double) this.phaseEnvelope.minInterval * 32.768D / sample_rate); - } - } - - int var12; - int var13; - int var14; - int delay; - for (var11 = 0; var11 < num_samples; ++var11) { - var12 = this.phaseEnvelope.nextLevel(num_samples); - var13 = this.amplitudeEnvelope.nextLevel(num_samples); - if (this.pitch_mod_env != null) { - var14 = this.pitch_mod_env.nextLevel(num_samples); - delay = this.pitch_mod_amp_env.nextLevel(num_samples); - var12 += this.method1716(pm_phase, delay, this.pitch_mod_env.waveTable) >> 1; - pm_phase += (var14 * pm_phase_delta >> 16) + pm_phase_delta_base; - } - - if (this.amplitudeModulationEnvelope != null) { - var14 = this.amplitudeModulationEnvelope.nextLevel(num_samples); - delay = this.amplitudeModulationAmplitudeEnvelope.nextLevel(num_samples); - var13 = var13 * ((this.method1716(var10, delay, this.amplitudeModulationEnvelope.waveTable) >> 1) + 32768) >> 15; - var10 += (var14 * var8 >> 16) + var9; - } - - for (var14 = 0; var14 < 5; ++var14) { - if (this.oscillatorAmplitudes[var14] != 0) { - delay = var11 + anIntArray1602[var14]; - if (delay < num_samples) { - output[delay] += this.method1716(anIntArray1601[var14], var13 * anIntArray1603[var14] >> 15, this.phaseEnvelope.waveTable); - anIntArray1601[var14] += (var12 * anIntArray1599[var14] >> 16) + anIntArray1600[var14]; - } - } - } - } - - int var16; - if (this.gateClosedPhaseEnvelope != null) { - this.gateClosedPhaseEnvelope.reset(); - this.gateOpenPhaseEnvelope.reset(); - var11 = 0; - - for (var14 = 0; var14 < num_samples; ++var14) { - delay = this.gateClosedPhaseEnvelope.nextLevel(num_samples); - var16 = this.gateOpenPhaseEnvelope.nextLevel(num_samples); - var12 = this.gateClosedPhaseEnvelope.minInterval + ((this.gateClosedPhaseEnvelope.maxInterval - this.gateClosedPhaseEnvelope.minInterval) * delay >> 8); - var11 += 256; - if (var11 >= var12) { - var11 = 0; - } else { - output[var14] = 0; - } - } - } - - if (this.delayTime > 0 && this.delayMix > 0) { - var11 = (int) ((double) this.delayTime * sample_rate); - - for (var12 = var11; var12 < num_samples; ++var12) { - output[var12] += output[var12 - var11] * this.delayMix / 100; - } - } - - if (this.filter.pairs[0] > 0 || this.filter.pairs[1] > 0) { - this.filterEnvelope.reset(); - var11 = this.filterEnvelope.nextLevel(num_samples + 1); - var12 = this.filter.compute(0, (float) var11 / 65536.0F); - var13 = this.filter.compute(1, (float) var11 / 65536.0F); - if (num_samples >= var12 + var13) { - var14 = 0; - delay = var13; - if (var13 > num_samples - var12) { - delay = num_samples - var12; - } - - int var17; - while (var14 < delay) { - var16 = (int) ((long) output[var14 + var12] * (long) inverseA0 >> 16); - - for (var17 = 0; var17 < var12; ++var17) { - var16 += (int) ((long) output[var14 + var12 - 1 - var17] * (long) coefficency[0][var17] >> 16); - } - - for (var17 = 0; var17 < var14; ++var17) { - var16 -= (int) ((long) output[var14 - 1 - var17] * (long) coefficency[1][var17] >> 16); - } - - output[var14] = var16; - var11 = this.filterEnvelope.nextLevel(num_samples + 1); - ++var14; - } - - delay = 128; - - while (true) { - if (delay > num_samples - var12) { - delay = num_samples - var12; - } - - while (var14 < delay) { - var16 = (int) ((long) output[var14 + var12] * (long) inverseA0 >> 16); - - for (var17 = 0; var17 < var12; ++var17) { - var16 += (int) ((long) output[var14 + var12 - 1 - var17] * (long) coefficency[0][var17] >> 16); - } - - for (var17 = 0; var17 < var13; ++var17) { - var16 -= (int) ((long) output[var14 - 1 - var17] * (long) coefficency[1][var17] >> 16); - } - - output[var14] = var16; - var11 = this.filterEnvelope.nextLevel(num_samples + 1); - ++var14; - } - - if (var14 >= num_samples - var12) { - while (var14 < num_samples) { - var16 = 0; - - for (var17 = var14 + var12 - num_samples; var17 < var12; ++var17) { - var16 += (int) ((long) output[var14 + var12 - 1 - var17] * (long) coefficency[0][var17] >> 16); - } - - for (var17 = 0; var17 < var13; ++var17) { - var16 -= (int) ((long) output[var14 - 1 - var17] * (long) coefficency[1][var17] >> 16); - } - - output[var14] = var16; - this.filterEnvelope.nextLevel(num_samples + 1); - ++var14; - } - break; - } - - var12 = this.filter.compute(0, (float) var11 / 65536.0F); - var13 = this.filter.compute(1, (float) var11 / 65536.0F); - delay += 128; - } - } - } - - for (var11 = 0; var11 < num_samples; ++var11) { - if (output[var11] < -32768) { - output[var11] = -32768; - } - - if (output[var11] > 32767) { - output[var11] = 32767; - } - } - - } - return output; - } - - final void decode(DataBuffer buffer) { - this.phaseEnvelope = new Envelope(); - this.phaseEnvelope.decode(buffer); - this.amplitudeEnvelope = new Envelope(); - this.amplitudeEnvelope.decode(buffer); - - int phaseModulationWaveTable = buffer.readUnsignedByte(); - if (phaseModulationWaveTable != 0) { - --buffer.index; - this.pitch_mod_env = new Envelope(); - this.pitch_mod_env.decode(buffer); - this.pitch_mod_amp_env = new Envelope(); - this.pitch_mod_amp_env.decode(buffer); - } - - int amplitudeModulationWaveTable = buffer.readUnsignedByte(); - if (amplitudeModulationWaveTable != 0) { - --buffer.index; - this.amplitudeModulationEnvelope = new Envelope(); - this.amplitudeModulationEnvelope.decode(buffer); - this.amplitudeModulationAmplitudeEnvelope = new Envelope(); - this.amplitudeModulationAmplitudeEnvelope.decode(buffer); - } - - int gateWaveTable = buffer.readUnsignedByte(); - if (gateWaveTable != 0) { - --buffer.index; - this.gateClosedPhaseEnvelope = new Envelope(); - this.gateClosedPhaseEnvelope.decode(buffer); - this.gateOpenPhaseEnvelope = new Envelope(); - this.gateOpenPhaseEnvelope.decode(buffer); - } - - for (int var3 = 0; var3 < 10; ++var3) { - int amplitude = buffer.getSmart(); - if (amplitude == 0) { - break; - } - - this.oscillatorAmplitudes[var3] = amplitude; - this.anIntArray1605[var3] = buffer.getByteOrShort(); - this.oscillatorStartMillis[var3] = buffer.getSmart(); - } - - this.delayTime = buffer.getSmart(); - this.delayMix = buffer.getSmart(); - this.duration = buffer.readUnsignedShort(); - this.start = buffer.readUnsignedShort(); - this.filter = new Filter(); - this.filterEnvelope = new Envelope(); - this.filter.decode(buffer, this.filterEnvelope); - } -} diff --git a/Client/src/main/java/org/runite/client/SynthSound.java b/Client/src/main/java/org/runite/client/SynthSound.java deleted file mode 100644 index 415769368..000000000 --- a/Client/src/main/java/org/runite/client/SynthSound.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.runite.client; - -public final class SynthSound { - - private final SynthInstrument[] synthInstruments = new SynthInstrument[10]; - private int end; - private int start; - - private SynthSound(DataBuffer buffer) { - for (int var2 = 0; var2 < 10; ++var2) { - int waveTable = buffer.readUnsignedByte(); - if (waveTable != 0) { - --buffer.index; - this.synthInstruments[var2] = new SynthInstrument(); - this.synthInstruments[var2].decode(buffer); - } - } - - this.start = buffer.readUnsignedShort(); - this.end = buffer.readUnsignedShort(); - } - - public static SynthSound create(CacheIndex var0, int var1, int var2) { - byte[] var3 = var0.getFile(var1, var2); - return var3 == null ? null : new SynthSound(new DataBuffer(var3)); - } - - private byte[] getSamples() { - int var1 = 0; - - int var2; - for (var2 = 0; var2 < 10; ++var2) { - if (this.synthInstruments[var2] != null && this.synthInstruments[var2].duration + this.synthInstruments[var2].start > var1) { - var1 = this.synthInstruments[var2].duration + this.synthInstruments[var2].start; - } - } - - if (var1 == 0) { - return new byte[0]; - } else { - var2 = 22050 * var1 / 1000; - byte[] var3 = new byte[var2]; - - for (int var4 = 0; var4 < 10; ++var4) { - if (this.synthInstruments[var4] != null) { - int var5 = this.synthInstruments[var4].duration * 22050 / 1000; - int var6 = this.synthInstruments[var4].start * 22050 / 1000; - int[] var7 = this.synthInstruments[var4].synthesize(var5, this.synthInstruments[var4].duration); - - for (int var8 = 0; var8 < var5; ++var8) { - int var9 = var3[var8 + var6] + (var7[var8] >> 8); - if ((var9 + 128 & -256) != 0) { - var9 = var9 >> 31 ^ 127; - } - - var3[var8 + var6] = (byte) var9; - } - } - } - - return var3; - } - } - - public final PcmSound toPCMSound() { - byte[] var1 = this.getSamples(); - return new PcmSound(var1, 22050 * this.start / 1000, 22050 * this.end / 1000); - } - - final int getStart() { - int start = 9999999; - - for (int var2 = 0; var2 < 10; ++var2) { - if (this.synthInstruments[var2] != null && this.synthInstruments[var2].start / 20 < start) { - start = this.synthInstruments[var2].start / 20; - } - } - - if (this.start < this.end && this.start / 20 < start) { - start = this.start / 20; - } - - if (start == 9999999 || start == 0) { - return 0; - } else { - for (int var2 = 0; var2 < 10; ++var2) { - if (this.synthInstruments[var2] != null) { - this.synthInstruments[var2].start -= start * 20; - } - } - - if (this.start < this.end) { - this.start -= start * 20; - this.end -= start * 20; - } - - return start; - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextCore.java b/Client/src/main/java/org/runite/client/TextCore.java deleted file mode 100644 index 622de97cf..000000000 --- a/Client/src/main/java/org/runite/client/TextCore.java +++ /dev/null @@ -1,316 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -public class TextCore { - - /** - * Configs that are set by the client - */ - - - /** @woah - * A repository filled with text that can be set to whatever the user sees fit - * Each will be labeled for what their original purpose is for and the files that - * They reside in. - * - * RSString information: - * The way it was programmed certain symbols require a different type of value set - * to determine said symbol. You may notice ex: )2 or a )3)3 - * )2 = spacer )3 = period(.) - */ - - public static final RSString TOGGLE_ATK = RSString.parse("::toggleatk"); - public static final RSString TOGGLE_FK = RSString.parse("::togglefk"); - public static RSString COMMAND_BREAK_CLIENT_CONNECTION = RSString.parse("::clientdrop"); - public static RSString aClass94_1622 = RSString.parse("Card:"); - public static RSString aClass94_4057 = RSString.parse("Mem:"); - public static RSString aClass94_4052 = RSString.parse("www"); - public static RSString aClass94_3601 = RSString.parse(")3runescape)3com)4l="); - public static RSString aClass94_1932 = RSString.parse(")4a="); - public static RSString aClass94_1885 = RSString.parse("cookiehost"); - public static RSString aClass94_3637 = RSString.parse(")4p="); - /** - * Holiday Event Text - */ - //Halloween - public static RSString TrickorTreat = RSString.of("Trick-or-treat"); - public static RSString GazeInto = RSString.of("Gaze-into"); - public static RSString aClass94_132 = RSString.parse("::"); - public static RSString aClass94_119 = RSString.parse("runes"); - public static RSString aClass94_37 = RSString.parse("0(U"); - public static RSString aClass94_38 = RSString.parse("tbrefresh"); - public static RSString aClass94_1698 = RSString.parse("(R"); - public static RSString aClass94_1133 = RSString.parse(")4j"); - public static RSString aClass94_4066 = RSString.parse("
"); - public static RSString aClass94_2598 = RSString.parse("
"); - public static RSString aClass94_1326 = RSString.parse(")2"); - public static RSString aClass94_4049 = RSString.parse(""); - public static RSString aClass94_1617 = RSString.parse(")1a2)1m"); - /** - * Login Screen Text - * Files that use these texts: - */ - public static RSString RSLoadingPleaseWait = RSString.parse(GameConfig.SERVER_NAME + " is loading )2 please wait)3)3)3"); - static RSString LoadingConfig = RSString.parse("Loading config )2 "); - static RSString LoadedConfig = RSString.parse("Loaded config"); - static RSString LoadingSprites = RSString.parse("Loading sprites )2 "); - static RSString LoadedSprites = RSString.parse("Loaded sprites"); - static RSString LoadingWLD = RSString.parse("Loading world list data"); - static RSString LoadedWLD = RSString.parse("Loaded world list data"); - static RSString LoadingPleaseWait = RSString.parse("Please wait)3)3)3"); - static RSString LoadingPleaseWait2 = RSString.parse("Loading )2 please wait)3"); - static RSString LoadingFonts = RSString.parse("Loading fonts )2 "); - static RSString LoadedFonts = RSString.parse("Loaded fonts"); - static RSString LoadedWordPack = RSString.parse("Loaded wordpack"); - static RSString LoadingTextures = RSString.parse("Loading textures )2 "); - static RSString LoadedTextures = RSString.parse("Loaded textures"); - static RSString LoadingInterfaces = RSString.parse("Loading interfaces )2 "); - static RSString LoadedInterfaces = RSString.parse("Loaded interfaces"); - static RSString LoadingTitleScreen = RSString.parse("Loading title screen )2 "); - static RSString LoadingGeneral = RSString.parse("Loading)3)3)3"); - static RSString LoadingWordPack = RSString.parse("Loading wordpack )2 "); - static RSString LoadingConnecting = RSString.parse("Connecting.. This takes a LONG time."); - static RSString LoadedUpdateList = RSString.parse("Loaded update list"); - static RSString AttemptingReestablish = RSString.parse("Please wait )2 attempting to reestablish)3"); - static RSString CreatedWorld = RSString.parse("Created gameworld"); - static RSString CheckingForUpdates = RSString.parse("Checking for updates )2 "); - static RSString LoadedInputHandler = RSString.parse("Loaded input handler"); - static RSString OpenedTitleScreen = RSString.parse("Opened title screen"); - static RSString LoadedTitleScreen = RSString.parse("Loaded title screen"); - static RSString Starting3DLibrary = RSString.parse("Starting 3d Library"); - static RSString Started3DLibrary = RSString.parse("Started 3d Library"); - static RSString AllocatingMemory = RSString.parse("Allocating memory"); - static RSString AllocatedMemory = RSString.parse("Allocated memory"); - static RSString PreparedSoundEngine = RSString.parse("Prepared sound engine"); - static RSString ConxLost = RSString.parse("Connection lost)3"); - static RSString ConxUpdateServer = RSString.parse("Connected to update server"); - /** - * User Login/ User Text - */ - static RSString HasExpires = RSString.parse("; Expires="); - static RSString HasMaxAge = RSString.parse("; Max)2Age="); - static RSString HasAgeExpire = RSString.parse("; Expires=Thu)1 01)2Jan)21970 00:00:00 GMT; Max)2Age=0"); - static RSString HasLoggedIn = RSString.parse(" has logged in)3"); - static RSString HasLoggedOut = RSString.parse(" has logged out)3"); - static RSString HasFriendsListFull = RSString.parse("Your friend list is full)3 Max of 100 for free users)1 and 200 for members)3"); - static RSString HasIgnoreListFull = RSString.parse("Your ignore list is full)3 Max of 100 users)3"); - static RSString HasOnOwnFriendsList = RSString.parse("You can(Wt add yourself to your own friend list)3"); - static RSString HasOnOwnIgnoreList = RSString.parse("You can(Wt add yourself to your own ignore list)3"); - static RSString HasPleaseRemove = RSString.parse("Please remove "); - static RSString HasIgnoreToFriends = RSString.parse(" from your ignore list first)3"); - static RSString HasFriendsToIgnore = RSString.parse(" from your friend list first)3"); - static RSString HasFriendsAlready = RSString.parse(" is already on your friend list)3"); - static RSString HasIgnoreAlready = RSString.parse(" is already on your ignore list)3"); - static RSString HasWishToTrade = RSString.parse("wishes to trade with you)3"); - static RSString HasAttack = RSString.parse("Attack"); - static RSString HasUse = RSString.parse("Use"); - static RSString HasExamine = RSString.parse("Examine"); - static RSString HasTake = RSString.parse("Take"); - static RSString HasWalkHere = RSString.parse("Walk here"); - static RSString HasDrop = RSString.parse("Drop"); - static RSString HasDiscard = RSString.parse("Discard"); - static RSString HasHidden = RSString.parse("Hidden"); - static RSString HasNull = RSString.parse("null"); - static RSString HasCancel = RSString.parse("Cancel"); - static RSString HasFaceHere = RSString.parse("Face here"); - static RSString HasContinue = RSString.parse("Continue"); - static RSString HasClose = RSString.parse("Close"); - static RSString HasOK = RSString.parse("Ok"); - static RSString HasSelect = RSString.parse("Select"); - static RSString HasChooseOptions = RSString.parse("Choose Option"); - static RSString HasMoreOptions = RSString.parse(" more options"); - static RSString HasUnableFind = RSString.parse("Unable to find "); - static RSString HasSkill = RSString.parse("skill: "); - static RSString HasScroll = RSString.parse("scroll:"); - static RSString HasLevel = RSString.parse("level: "); - static RSString HasRating = RSString.parse("rating: "); - /** - * Money values (K (Thousand)), (M (Million)) - */ - static RSString ThousandK = RSString.parse("K"); - static RSString MillionM = RSString.parse("M"); - static RSString HasDuelFriend = RSString.parse(":duelfriend:"); - static RSString HasDuelStake = RSString.parse(":duelstake:"); - static RSString HasTrade = RSString.parse(":trade:"); - static RSString HasAssist = RSString.parse(":assist:"); - static RSString HasAssistRequest = RSString.parse(":assistreq:"); - static RSString HasClanRequest = RSString.parse(":clanreq:"); - static RSString HasClan = RSString.parse(":clan:"); - static RSString HasAllyReq = RSString.parse(":allyreq:"); - static RSString cmdChalReq = RSString.parse(":chalreq:"); - /** - * Archive Info for client cache lookup - */ - static RSString HasLabels = RSString.parse("_labels"); - static RSString HasPlayerLabels = RSString.parse("_labels"); - static RSString HasHuffman = RSString.parse("huffman"); - /** - * Colored Text Commands * For Color editing use ColorCore.java - * *Note not used to actually change the color of text - * Used as color coding commands such as: - * red: purple: etc. - */ - - static RSString TextColorYellow = RSString.parse("yellow:"); - static RSString TextColorRed = RSString.parse("red:"); - static RSString TextColorGreen = RSString.parse("green:"); - static RSString TextColorCyan = RSString.parse("cyan:"); - static RSString TextColorPurple = RSString.parse("purple:"); - static RSString TextColorWhite = RSString.parse("white:"); - static RSString TextFlashOne = RSString.parse("flash1:"); - static RSString TextFlashTwo = RSString.parse("flash2:"); - static RSString TextFlashThree = RSString.parse("flash3:"); - static RSString TextGlowOne = RSString.parse("glow1:"); - static RSString TextGlowTwo = RSString.parse("glow2:"); - static RSString TextGlowThree = RSString.parse("glow3:"); - static RSString TextWave = RSString.parse("wave:"); - static RSString TextWaveTwo = RSString.parse("wave2:"); - static RSString TextShake = RSString.parse("shake:"); - static RSString TextSlide = RSString.parse("slide:"); - /** - * Months of the year - * This was being accessed multiple times for other methods - */ - static RSString[] MonthsOfTheYear = new RSString[]{RSString.parse("Jan"), - RSString.parse("Feb"), - RSString.parse("Mar"), - RSString.parse("Apr"), - RSString.parse("May"), - RSString.parse("Jun"), - RSString.parse("Jul"), - RSString.parse("Aug"), - RSString.parse("Sep"), - RSString.parse("Oct"), - RSString.parse("Nov"), - RSString.parse("Dec")}; - static RSString[] DaysOfTheWeek = new RSString[]{RSString.parse("Sun"), - RSString.parse("Mon"), - RSString.parse("Tue"), - RSString.parse("Wed"), - RSString.parse("Thu"), - RSString.parse("Fri"), - RSString.parse("Sat")}; - /** - * Client Commands - */ - static RSString COMMAND_HIGHRES_GRAPHICS_RESIZE = RSString.parse("::wm2"); - static RSString COMMAND_SHIFT_DROP_CLICK = RSString.parse("::shiftclick"); - static RSString COMMAND_GETBITS = RSString.parse("::getbits"); - static RSString COMMAND_REPLACE_CANVAS = RSString.parse("::replacecanvas"); - static RSString COMMAND_HIGHRES_GRAPHICS_WINDOW = RSString.parse("::wm1"); - static RSString COMMAND_QA_OP_TEST = RSString.parse("::qa_op_test"); - static RSString COMMAND_NOCLIP = RSString.parse("::noclip"); - static RSString COMMAND_GARBAGE_COLLECTOR = RSString.parse("::gc"); - static RSString COMMAND_HIGHRES_GRAPHICS_FULLSCREEN = RSString.parse("::wm3"); - static RSString COMMAND_FPS = RSString.parse("::fps "); - static RSString COMMAND_TWEENING = RSString.parse("::tween"); - static RSString COMMAND_MEMORY_MANAGEMENT = RSString.parse("::mm"); - static RSString COMMAND_BREAK_JS5_CLIENT_CONNECTION = RSString.parse("::clientjs5drop"); - static RSString COMMAND_BREAK_JS5_SERVER_CONNECTION = RSString.parse("::serverjs5drop"); - static RSString COMMAND_GRAPHICS_CARD_MEMORY = RSString.parse("::cardmem"); - static RSString COMMAND_TOGGLE_FPSOFF = RSString.parse("::fpsoff"); - static RSString COMMAND_PC_CACHE_SIZE = RSString.parse("::pcachesize"); - static RSString aClass94_853 = RSString.parse("::tele "); - static RSString COMMAND_LOWRES_GRAPHICS = RSString.parse("::wm0"); - static RSString COMMAND_TOGGLE_FPSON = RSString.parse("::fpson"); - static RSString COMMAND_REBUILD = RSString.parse("::rebuild"); - static RSString COMMAND_BREAK_CONNECTION = RSString.parse("::breakcon"); - static RSString COMMAND_ERROR_TEST = RSString.parse("::errortest"); - static RSString COMMAND_SET_PARTICLES = RSString.parse("::setparticles"); - static RSString COMMAND_RECT_DEBUG = RSString.parse("::rect_debug"); - static RSString COMMAND_RENDER_INFO = RSString.parse("::renderinfo"); - static RSString COMMAND_DISCORD = RSString.parse("::discord"); - static RSString COMMAND_HIGHSCORES = RSString.parse("::highscores"); - static RSString COMMAND_HISCORES = RSString.parse("::hiscores"); - /** - * Used as text for client commands - */ - static RSString Memoryk = RSString.parse("k"); - static RSString aClass94_985 = RSString.parse("Fps:"); - static RSString aClass94_1630 = RSString.parse("Mem:"); - static RSString memoryBeforeCleanup = RSString.parse("Memory before cleanup="); - static RSString aClass94_3653 = RSString.parse("Shift)2click disabled)3"); - static RSString aClass94_434 = RSString.parse("Shift)2click ENABLED(Q"); - static RSString forcedTweeningDisabled = RSString.parse("Forced tweening disabled)3"); - static RSString forcedTweeningEnabled = RSString.parse("Forced tweening / animation smoothing ENABLED(Q"); - /** - * Website - */ - static RSString aClass94_577 = RSString.parse("http:)4)4"); - /** - * Displayed if user is on a f2p world - */ - static RSString MembersObject = RSString.parse("Members object"); - /** - * Fonts - */ - public static java.awt.Font Helvetica = new java.awt.Font("Helvetica", java.awt.Font.BOLD, 13); - /** - * Unsorted - */ - static RSString Spacer = RSString.parse(" "); - static RSString aClass94_3339 = RSString.parse("null"); - static RSString aClass94_3357 = RSString.parse(""); - static RSString memoryEquals = RSString.parse("mem="); - static RSString aClass94_3399 = RSString.parse("
(X"); - static RSString aClass94_3418 = RSString.parse("(U5"); - static RSString aClass94_2498 = RSString.parse("(U (X"); - static RSString aClass94_3574 = RSString.parse("titlebg"); - static RSString aClass94_3577 = RSString.parse(": "); - static RSString aClass94_3703 = RSString.parse(" )2> "); - static RSString aClass94_3777 = RSString.parse(" x "); - static RSString aClass94_2608 = RSString.parse(")4l="); - static RSString aClass94_2168 = RSString.parse("
"); - static RSString aClass94_106 = RSString.parse("showVideoAd"); - static RSString aClass94_331 = RSString.parse("(U1"); - static RSString aClass94_339 = RSString.parse("1"); - static RSString aClass94_341 = RSString.parse(")3"); - static RSString aClass94_436 = RSString.parse("Cache:"); - static RSString aClass94_442 = RSString.parse("Number of player models in cache:"); - static RSString aClass94_516 = RSString.parse("unzap"); - static RSString aClass94_2610 = RSString.parse(")1o"); - static RSString aClass94_673 = RSString.parse(")0"); - static RSString aClass94_676 = null; - static RSString aClass94_852 = RSString.parse("(U4"); - static RSString aClass94_892 = RSString.parse(" )2> "); - static RSString aClass94_1051 = RSString.parse("(Udns"); - static RSString LEFT_PARENTHESES = RSString.parse(" (X"); - static RSString aClass94_1076 = RSString.parse("<)4col>"); - static RSString aClass94_1151 = RSString.parse("settings="); - static RSString aClass94_1301 = RSString.parse("(U3"); - static RSString aClass94_1341 = RSString.parse("logo"); - static RSString aClass94_1342 = RSString.parse("details"); - static RSString aClass94_2171 = RSString.parse(""); - static RSString aClass94_1687 = RSString.parse("(Z"); - static RSString aClass94_1694 = RSString.parse("document)3cookie=(R"); - static RSString aClass94_1724 = RSString.parse(" )2>"); - static RSString aClass94_1745 = RSString.parse("settings"); - static RSString aClass94_1760 = RSString.parse(""); - static RSString aClass94_2707 = RSString.parse("
(X100(U(Y"); - static RSString aClass94_2735 = RSString.parse(")4"); - static RSString aClass94_2765 = RSString.parse(" "); - static RSString aClass94_2928 = RSString.parse("null"); - static RSString aClass94_1880 = RSString.parse(")1"); - static RSString aClass94_2006 = RSString.parse("null"); - static RSString aClass94_2018 = RSString.parse("Cabbage"); - static RSString aClass94_2025 = RSString.parse(")2"); - static RSString aClass94_2033 = RSString.parse("Memory after cleanup="); - static RSString aClass94_2044 = RSString.parse("cookieprefix"); - static RSString aClass94_4007 = RSString.parse(":"); - static RSString aClass94_4023 = RSString.parse(")3"); - static RSString aClass94_3013 = RSString.parse("0"); - static RSString aClass94_2074 = RSString.parse("; version=1; path=)4; domain="); - static RSString rectDebugEquals = RSString.parse("rect_debug="); - static RSString aClass94_2080 = RSString.parse("(U2"); - static RSString aClass94_2116 = RSString.parse("Hidden)2use"); - static RSString aClass94_1915 = RSString.parse("Null"); - static RSString aClass94_2584 = RSString.parse("<)4col>"); - static RSString aClass94_209 = RSString.parse("event_opbase"); - static RSString aClass94_2304 = RSString.parse("details"); - static RSString aClass94_2306 = RSString.parse("<)4col> x"); - static RSString aClass94_2323 = RSString.parse(""); - static RSString aClass94_2331 = RSString.parse(""); - static RSString RIGHT_PARENTHESES = RSString.parse("(Y"); - -} diff --git a/Client/src/main/java/org/runite/client/Texture.java b/Client/src/main/java/org/runite/client/Texture.java deleted file mode 100644 index e3c8c3674..000000000 --- a/Client/src/main/java/org/runite/client/Texture.java +++ /dev/null @@ -1,826 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -import java.util.Objects; - -public final class Texture { - - static int anInt2208 = -1; - static int[] anIntArray2213 = new int[]{16776960, 16711680, 65280, 65535, 16711935, 16777215}; - static RSString[] aClass94Array3317 = new RSString[TextureOperation35.anInt3332]; - static int[] anIntArray3318 = new int[TextureOperation35.anInt3332]; - static int[] anIntArray3319 = new int[TextureOperation35.anInt3332]; - static int[] anIntArray3327 = new int[TextureOperation35.anInt3332]; - static int[] anIntArray3329 = new int[TextureOperation35.anInt3332]; - static int[] anIntArray3331 = new int[TextureOperation35.anInt3332]; - static int[] anIntArray3336 = new int[TextureOperation35.anInt3332]; - static int[] anIntArray3337 = new int[TextureOperation35.anInt3332]; - static int anInt1668 = -1; - static ReferenceCache aReferenceCache_1146 = new ReferenceCache(64); - static int anInt1150 = -1; - public static int anInt1152; - private final int[] anIntArray1144; - private final TextureOperation aClass3_Sub13_1145; - private final TextureOperation[] aClass3_Sub13Array1147; - private final TextureOperation aClass3_Sub13_1148; - private final int[] anIntArray1149; - - - public Texture() { - try { - this.anIntArray1149 = new int[0]; - this.anIntArray1144 = new int[0]; - this.aClass3_Sub13_1145 = new TextureOperation0(); - this.aClass3_Sub13_1145.imageCacheCapacity = 1; - this.aClass3_Sub13_1148 = new TextureOperation0(); - this.aClass3_Sub13Array1147 = new TextureOperation[]{this.aClass3_Sub13_1145, this.aClass3_Sub13_1148}; - this.aClass3_Sub13_1148.imageCacheCapacity = 1; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lc.()"); - } - } - - Texture(DataBuffer var1) { - try { - int var2 = var1.readUnsignedByte(); - this.aClass3_Sub13Array1147 = new TextureOperation[var2]; - int[][] var5 = new int[var2][]; - int var4 = 0; - int var3 = 0; - - int var6; - TextureOperation textureOperation; - int var8; - int var9; - for (var6 = 0; var2 > var6; ++var6) { - textureOperation = decodeTexture(var1); - if (0 <= textureOperation.method159(4)) { - ++var3; - } - - if (textureOperation.getSpriteFrame() >= 0) { - ++var4; - } - - var8 = textureOperation.subOperations.length; - var5[var6] = new int[var8]; - - for (var9 = 0; var9 < var8; ++var9) { - var5[var6][var9] = var1.readUnsignedByte(); - } - - this.aClass3_Sub13Array1147[var6] = textureOperation; - } - - this.anIntArray1144 = new int[var3]; - this.anIntArray1149 = new int[var4]; - var3 = 0; - var4 = 0; - - for (var6 = 0; var6 < var2; ++var6) { - textureOperation = this.aClass3_Sub13Array1147[var6]; - var8 = textureOperation.subOperations.length; - - for (var9 = 0; var8 > var9; ++var9) { - textureOperation.subOperations[var9] = this.aClass3_Sub13Array1147[var5[var6][var9]]; - } - - var9 = textureOperation.method159(4); - int var10 = textureOperation.getSpriteFrame(); - if (var9 > 0) { - this.anIntArray1144[var3++] = var9; - } - - if (var10 > 0) { - this.anIntArray1149[var4++] = var10; - } - - var5[var6] = null; - } - - this.aClass3_Sub13_1145 = this.aClass3_Sub13Array1147[var1.readUnsignedByte()]; - this.aClass3_Sub13_1148 = this.aClass3_Sub13Array1147[var1.readUnsignedByte()]; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "lc.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method1405(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { - try { - TextureOperation8.anInt3464 = 0; - - int var7; - int var15; - int var19; - int var21; - int var22; - int var29; - int var32; - for (var7 = -1; var7 < Class159.localPlayerCount + Class163.localNPCCount; ++var7) { - Class140_Sub4 var8; - if (var7 == -1) { - var8 = Class102.player; - } else if (var7 < Class159.localPlayerCount) { - var8 = Unsorted.players[Class56.localPlayerIndexes[var7]]; - } else { - var8 = NPC.npcs[AudioThread.localNPCIndexes[-Class159.localPlayerCount + var7]]; - } - - if (null != var8 && var8.hasDefinitions()) { - NPCDefinition var9; - if (var8 instanceof NPC) { - var9 = ((NPC) var8).definition; - if (null != var9.childNPCs) { - var9 = var9.method1471((byte) -93); - } - - if (var9 == null) { - continue; - } - } - - int var12; - if (var7 < Class159.localPlayerCount) { - var19 = 30; - Player var10 = (Player) var8; - if (var10.skullIcon != -1 || -1 != var10.headIcon) { - Class107.method1647(var4 >> 1, var3, var8, var5, var8.method1975(var6 ^ -28716) - -15, var1 >> 1); - if (-1 < Class32.anInt590) { - if (var10.skullIcon != -1) { - TextureOperation2.aAbstractSpriteArray3373[var10.skullIcon].drawAt(-12 + Class32.anInt590 + var2, -var19 + var0 + anInt2208); - var19 += 25; - } - - if (var10.headIcon != -1) { - NPC.aAbstractSpriteArray3977[var10.headIcon].drawAt(-12 + var2 + Class32.anInt590, var0 - (-anInt2208 + var19)); - var19 += 25; - } - } - } - - if (var7 >= 0) { - Class96[] var11 = ClientErrorException.aClass96Array2114; - - for (var12 = 0; var12 < var11.length; ++var12) { - Class96 var13 = var11[var12]; - if (null != var13 && var13.anInt1360 == 10 && Class56.localPlayerIndexes[var7] == var13.anInt1359) { - Class107.method1647(var4 >> 1, var3, var8, var5, var8.method1975(var6 ^ -28716) - -15, var1 >> 1); - if (Class32.anInt590 > -1) { - Class166.aAbstractSpriteArray2072[var13.anInt1351].drawAt(var2 - (-Class32.anInt590 + 12), var0 + (anInt2208 - var19)); - } - } - } - } - } else { - var9 = ((NPC) var8).definition; - if (var9.childNPCs != null) { - var9 = var9.method1471((byte) 102); - } - - if (Objects.requireNonNull(var9).anInt1269 >= 0 && NPC.aAbstractSpriteArray3977.length > var9.anInt1269) { - if (var9.anInt1265 == -1) { - var22 = 15 + var8.method1975(27855); - } else { - var22 = 15 + var9.anInt1265; - } - - Class107.method1647(var4 >> 1, var3, var8, var5, var22, var1 >> 1); - if (Class32.anInt590 > -1) { - NPC.aAbstractSpriteArray3977[var9.anInt1269].drawAt(var2 - -Class32.anInt590 - 12, -30 + var0 - -anInt2208); - } - } - - Class96[] var20 = ClientErrorException.aClass96Array2114; - - for (var21 = 0; var20.length > var21; ++var21) { - Class96 var24 = var20[var21]; - if (null != var24 && var24.anInt1360 == 1 && AudioThread.localNPCIndexes[-Class159.localPlayerCount + var7] == var24.anInt1359 && Class44.anInt719 % 20 < 10) { - if (-1 == var9.anInt1265) { - var29 = 15 + var8.method1975(var6 + '\u89b4'); - } else { - var29 = 15 + var9.anInt1265; - } - - Class107.method1647(var4 >> 1, var3, var8, var5, var29, var1 >> 1); - if (Class32.anInt590 > -1) { - Class166.aAbstractSpriteArray2072[var24.anInt1351].drawAt(-12 + var2 + Class32.anInt590, -28 + anInt2208 + var0); - } - } - } - } - - if (var8.textSpoken != null && (var7 >= Class159.localPlayerCount || CS2Script.anInt3101 == 0 || 3 == CS2Script.anInt3101 || 1 == CS2Script.anInt3101 && ItemDefinition.method1176(((Player) var8).displayName))) { - Class107.method1647(var4 >> 1, var3, var8, var5, var8.method1975(27855), var1 >> 1); - if (-1 < Class32.anInt590 && TextureOperation8.anInt3464 < TextureOperation35.anInt3332) { - anIntArray3329[TextureOperation8.anInt3464] = FontType.bold.method682(var8.textSpoken) / 2; - anIntArray3327[TextureOperation8.anInt3464] = FontType.bold.anInt3727; - anIntArray3319[TextureOperation8.anInt3464] = Class32.anInt590; - anIntArray3337[TextureOperation8.anInt3464] = anInt2208; - anIntArray3331[TextureOperation8.anInt3464] = var8.textColor; - anIntArray3336[TextureOperation8.anInt3464] = var8.textEffect; - anIntArray3318[TextureOperation8.anInt3464] = var8.textCycle; - aClass94Array3317[TextureOperation8.anInt3464] = var8.textSpoken; - ++TextureOperation8.anInt3464; - } - } - - if (Class44.anInt719 < var8.anInt2781) { - AbstractSprite var23 = Unsorted.aAbstractSpriteArray996[0]; - AbstractSprite var25 = Unsorted.aAbstractSpriteArray996[1]; - if (var8 instanceof NPC) { - NPC var28 = (NPC) var8; - AbstractSprite[] var31 = (AbstractSprite[]) TextureOperation1.aReferenceCache_3130.get(var28.definition.anInt1279); - if (var31 == null) { - var31 = Class140_Sub6.getSprites(var28.definition.anInt1279, CacheIndex.spritesIndex); - if (null != var31) { - TextureOperation1.aReferenceCache_3130.put(var31, var28.definition.anInt1279); - } - } - - if (null != var31 && var31.length == 2) { - var25 = var31[1]; - var23 = var31[0]; - } - - NPCDefinition var14 = var28.definition; - if (-1 == var14.anInt1265) { - var21 = var8.method1975(27855); - } else { - var21 = var14.anInt1265; - } - } else { - var21 = var8.method1975(27855); - } - - Class107.method1647(var4 >> 1, var3, var8, var5, var23.height + 10 + var21, var1 >> 1); - if (-1 < Class32.anInt590) { - var12 = -(var23.width >> 1) + Class32.anInt590 + var2; - var29 = anInt2208 + var0 + -3; - var23.drawAt(var12, var29); - var32 = var23.width * var8.anInt2775 / 255; - var15 = var23.height; - if (HDToolKit.highDetail) { - Class22.method931(var12, var29, var12 + var32, var29 + var15); - } else { - Class74.method1326(var12, var29, var12 + var32, var15 + var29); - } - - var25.drawAt(var12, var29); - if (HDToolKit.highDetail) { - Class22.setClipping(var2, var0, var1 + var2, var0 - -var4); - } else { - Class74.setClipping(var2, var0, var1 + var2, var4 + var0); - } - } - } - - for (var19 = 0; var19 < 4; ++var19) { - if (Class44.anInt719 < var8.anIntArray2768[var19]) { - if (var8 instanceof NPC) { - NPC var30 = (NPC) var8; - NPCDefinition var26 = var30.definition; - if (var26.anInt1265 == -1) { - var22 = var8.method1975(27855) / 2; - } else { - var22 = var26.anInt1265 / 2; - } - } else { - var22 = var8.method1975(var6 ^ -28716) / 2; - } - - Class107.method1647(var4 >> 1, var3, var8, var5, var22, var1 >> 1); - if (-1 < Class32.anInt590) { - if (var19 == 1) { - anInt2208 -= 20; - } - - if (var19 == 2) { - anInt2208 -= 10; - Class32.anInt590 -= 15; - } - - if (3 == var19) { - anInt2208 -= 10; - Class32.anInt590 += 15; - } - - Class75_Sub3.aAbstractSpriteArray2656[var8.anIntArray2815[var19]].drawAt(-12 + var2 + Class32.anInt590, var0 + anInt2208 - 12); - FontType.smallFont.method699(RSString.stringAnimator(var8.anIntArray2836[var19]), -1 + Class32.anInt590 + var2, 3 + anInt2208 + var0, 16777215, 0); - } - } - } - } - } - - var7 = 0; - if (var6 != -7397) { - method1409(true); - } - - for (; TextureOperation8.anInt3464 > var7; ++var7) { - var19 = anIntArray3337[var7]; - int var18 = anIntArray3319[var7]; - var21 = anIntArray3327[var7]; - var22 = anIntArray3329[var7]; - boolean var27 = true; - - while (var27) { - var27 = false; - - for (var29 = 0; var7 > var29; ++var29) { - if (anIntArray3337[var29] - anIntArray3327[var29] < 2 + var19 && -var21 + var19 < anIntArray3337[var29] - -2 && -var22 + var18 < anIntArray3319[var29] + anIntArray3329[var29] && anIntArray3319[var29] - anIntArray3329[var29] < var22 + var18 && -anIntArray3327[var29] + anIntArray3337[var29] < var19) { - var19 = anIntArray3337[var29] - anIntArray3327[var29]; - var27 = true; - } - } - } - - Class32.anInt590 = anIntArray3319[var7]; - anInt2208 = anIntArray3337[var7] = var19; - RSString var33 = aClass94Array3317[var7]; - if (Unsorted.anInt688 == 0) { - var32 = 16776960; - if (anIntArray3331[var7] < 6) { - var32 = anIntArray2213[anIntArray3331[var7]]; - } - - if (6 == anIntArray3331[var7]) { - var32 = 10 <= CSConfigCachefile.anInt1127 % 20 ? 16776960 : 16711680; - } - - if (anIntArray3331[var7] == 7) { - var32 = CSConfigCachefile.anInt1127 % 20 < 10 ? 255 : 65535; - } - - if (8 == anIntArray3331[var7]) { - var32 = CSConfigCachefile.anInt1127 % 20 >= 10 ? 8454016 : '\ub000'; - } - - if (9 == anIntArray3331[var7]) { - var15 = -anIntArray3318[var7] + 150; - if (var15 >= 50) { - if (var15 >= 100) { - if (150 > var15) { - var32 = -500 - (-(5 * var15) - 65280); - } - } else { - var32 = 16776960 + 16384000 + -(327680 * var15); - } - } else { - var32 = var15 * 1280 + 16711680; - } - } - - if (10 == anIntArray3331[var7]) { - var15 = -anIntArray3318[var7] + 150; - if (50 <= var15) { - if (var15 < 100) { - var32 = -(327680 * (-50 + var15)) + 16711935; - } else if (150 > var15) { - var32 = 327680 * var15 - (32768000 - (255 + -(5 * var15) + 500)); - } - } else { - var32 = 16711680 + var15 * 5; - } - } - - if (anIntArray3331[var7] == 11) { - var15 = 150 + -anIntArray3318[var7]; - if (var15 >= 50) { - if (var15 >= 100) { - if (var15 < 150) { - var32 = 16777215 - var15 * 327680 + 32768000; - } - } else { - var32 = 65280 - (-(327685 * var15) - -16384250); - } - } else { - var32 = 16777215 - 327685 * var15; - } - } - - if (0 == anIntArray3336[var7]) { - FontType.bold.method699(var33, Class32.anInt590 + var2, var0 + anInt2208, var32, 0); - } - - if (1 == anIntArray3336[var7]) { - FontType.bold.method696(var33, var2 - -Class32.anInt590, anInt2208 + var0, var32, CSConfigCachefile.anInt1127); - } - - if (anIntArray3336[var7] == 2) { - FontType.bold.method695(var33, var2 - -Class32.anInt590, var0 - -anInt2208, var32, CSConfigCachefile.anInt1127); - } - - if (anIntArray3336[var7] == 3) { - FontType.bold.method692(var33, var2 + Class32.anInt590, anInt2208 + var0, var32, CSConfigCachefile.anInt1127, 150 - anIntArray3318[var7]); - } - - if (4 == anIntArray3336[var7]) { - var15 = (-anIntArray3318[var7] + 150) * (FontType.bold.method682(var33) - -100) / 150; - if (HDToolKit.highDetail) { - Class22.method931(Class32.anInt590 + var2 + -50, var0, Class32.anInt590 + var2 - -50, var4 + var0); - } else { - Class74.method1326(-50 + (var2 - -Class32.anInt590), var0, 50 + Class32.anInt590 + var2, var4 + var0); - } - - FontType.bold.method681(var33, var2 - (-Class32.anInt590 + -50) + -var15, var0 + anInt2208, var32, 0); - if (HDToolKit.highDetail) { - Class22.setClipping(var2, var0, var1 + var2, var4 + var0); - } else { - Class74.setClipping(var2, var0, var2 - -var1, var0 + var4); - } - } - - if (anIntArray3336[var7] == 5) { - int var16 = 0; - var15 = -anIntArray3318[var7] + 150; - if (HDToolKit.highDetail) { - Class22.method931(var2, -1 + -FontType.bold.anInt3727 + anInt2208 + var0, var1 + var2, 5 + var0 - -anInt2208); - } else { - Class74.method1326(var2, -1 + -FontType.bold.anInt3727 + anInt2208 + var0, var2 + var1, 5 + anInt2208 + var0); - } - - if (25 > var15) { - var16 = var15 + -25; - } else if (var15 > 125) { - var16 = var15 - 125; - } - - FontType.bold.method699(var33, Class32.anInt590 + var2, var16 + var0 + anInt2208, var32, 0); - if (HDToolKit.highDetail) { - Class22.setClipping(var2, var0, var2 - -var1, var0 + var4); - } else { - Class74.setClipping(var2, var0, var2 + var1, var0 + var4); - } - } - } else { - FontType.bold.method699(var33, var2 - -Class32.anInt590, var0 + anInt2208, 16776960, 0); - } - } - - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "lc.D(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - static int method1406() { - try { - return Unsorted.anInt4045; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lc.E(" + (byte) -43 + ')'); - } - } - - public static void method1409(boolean var0) { - try { - aReferenceCache_1146 = null; - if (var0) { - TextCore.aClass94_1151 = null; - } - - TextCore.aClass94_1151 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lc.A(" + var0 + ')'); - } - } - - public static TextureOperation decodeTexture(DataBuffer var1) { - try { - var1.readUnsignedByte(); - int type = var1.readUnsignedByte(); - TextureOperation var3 = create(type); - Objects.requireNonNull(var3).imageCacheCapacity = var1.readUnsignedByte(); - int codes = var1.readUnsignedByte(); - for (int var5 = 0; var5 < codes; ++var5) { - int opcode = var1.readUnsignedByte(); - var3.decode(opcode, var1); - } - - var3.postDecode(); - return var3; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "qk.B(" + (byte) -67 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public static TextureOperation create(int type) { - if (type == 0) { - return new TextureOperation0(); - } else if (type == 1) { - return new TextureOperation1(); - } else if (2 == type) { - return new TextureOperation2(); - } else if (type == 3) { - return new TextureOperation3(); - } else if (type == 4) { - return new TextureOperation4(); - } else if (type == 5) { - return new TextureOperation5(); - } else if (type == 6) { - return new TextureOperation6(); - } else if (type == 7) { - return new TextureOperation7(); - } else if (type == 8) { - return new TextureOperation8(); - } else if (9 == type) { - return new TextureOperation9(); - } else if (10 == type) { - return new TextureOperation10(); - } else if (type == 11) { - return new TextureOperation11(); - } else if (type == 12) { - return new TextureOperation12(); - } else if (type == 13) { - return new TextureOperation13(); - } else if (14 == type) { - return new TextureOperation14(); - } else if (type == 15) { - return new TextureOperation15(); - } else if (type == 16) { - return new TextureOperation16(); - } else if (17 == type) { - return new TextureOperation17(); - } else if (type == 18) { - return new TextureOperation18(); - } else if (type == 19) { - return new TextureOperation19(); - } else if (type == 20) { - return new TextureOperation20(); - } else if (21 == type) { - return new TextureOperation21(); - } else if (22 == type) { - return new TextureOperation22(); - } else if (type == 23) { - return new TextureOperation23(); - } else if (24 == type) { - return new TextureOperation24(); - } else if (type == 25) { - return new TextureOperation25(); - } else if (type == 26) { - return new TextureOperation26(); - } else if (27 == type) { - return new TextureOperation27(); - } else if (type == 28) { - return new TextureOperation28(); - } else if (type == 29) { - return new TextureOperation29(); - } else if (type == 30) { - return new TextureOperation30(); - } else if (31 == type) { - return new TextureOperation31(); - } else if (32 == type) { - return new TextureOperation32(); - } else if (33 == type) { - return new TextureOperation33(); - } else if (type == 34) { - return new TextureOperation34(); - } else if (type == 35) { - return new TextureOperation35(); - } else if (type == 36) { - return new TextureOperation36(); - } else if (type == 37) { - return new TextureOperation37(); - } else if (38 == type) { - return new TextureOperation38(); - } else if (39 == type) { - return new TextureOperation39(); - } else { - return null; - } - } - - final int[] method1404(int var1, boolean var2, int var3, double var4, CacheIndex var7, Interface2 var8, boolean var9) { - try { - GameObject.method1859(var4); - Class17.anInterface2_408 = var8; - WaterfallShader.aClass153_2172 = var7; - TextureOperation33.method180(-1, var1, var3); - - int var11; - for (var11 = 0; var11 < this.aClass3_Sub13Array1147.length; ++var11) { - this.aClass3_Sub13Array1147[var11].method160(var1, var3); - } - - int[] var10 = new int[var1 * var3]; - int var12; - byte var13; - if (var9) { - var13 = -1; - var12 = -1; - var11 = var3 - 1; - } else { - var13 = 1; - var11 = 0; - var12 = var3; - } - - int var14 = 0; - - int var15; - for (var15 = 0; var1 > var15; ++var15) { - if (var2) { - var14 = var15; - } - - int[] var17; - int[] var16; - int[] var18; - if (this.aClass3_Sub13_1145.aBoolean2375) { - int[] var19 = this.aClass3_Sub13_1145.method154(var15, (byte) 109); - var16 = var19; - var17 = var19; - var18 = var19; - } else { - int[][] var24 = this.aClass3_Sub13_1145.method166(var15); - var16 = var24[0]; - var18 = var24[2]; - var17 = var24[1]; - } - - for (int var25 = var11; var25 != var12; var25 += var13) { - int var20 = var16[var25] >> 4; - if (var20 > 255) { - var20 = 255; - } - - if (var20 < 0) { - var20 = 0; - } - - var20 = BufferedDataStream.anIntArray3804[var20]; - int var22 = var18[var25] >> 4; - int var21 = var17[var25] >> 4; - if (var21 > 255) { - var21 = 255; - } - - if (0 > var21) { - var21 = 0; - } - - if (var22 > 255) { - var22 = 255; - } - - var21 = BufferedDataStream.anIntArray3804[var21]; - if (var22 < 0) { - var22 = 0; - } - - var22 = BufferedDataStream.anIntArray3804[var22]; - var10[var14++] = (var20 << 16) - -(var21 << 8) + var22; - if (var2) { - var14 += var3 + -1; - } - } - } - - for (var15 = 0; var15 < this.aClass3_Sub13Array1147.length; ++var15) { - this.aClass3_Sub13Array1147[var15].method161((byte) -45); - } - - return var10; - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "lc.C(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + 327680 + ',' + (var7 != null ? "{...}" : "null") + ',' + (var8 != null ? "{...}" : "null") + ',' + var9 + ')'); - } - } - - final byte[] method1407(int var1, int var2, boolean var3, Interface2 var4, CacheIndex var8) { - try { - byte[] var9 = new byte[4 * var2 * var1]; - GameObject.method1859(0.7); - WaterfallShader.aClass153_2172 = var8; - Class17.anInterface2_408 = var4; - TextureOperation33.method180(-32, var1, var2); - - int var10; - for (var10 = 0; this.aClass3_Sub13Array1147.length > var10; ++var10) { - this.aClass3_Sub13Array1147[var10].method160(var1, var2); - } - - var10 = 0; - - int var11; - for (var11 = 0; var11 < var1; ++var11) { - if (var3) { - var10 = var11 << 2; - } - - int[] var12; - int[] var13; - int[] var14; - int[] var15; - if (this.aClass3_Sub13_1145.aBoolean2375) { - var15 = this.aClass3_Sub13_1145.method154(var11, (byte) -98); - var12 = var15; - var13 = var15; - var14 = var15; - } else { - int[][] var22 = this.aClass3_Sub13_1145.method166(var11); - var12 = var22[0]; - var13 = var22[1]; - var14 = var22[2]; - } - - if (this.aClass3_Sub13_1148.aBoolean2375) { - var15 = this.aClass3_Sub13_1148.method154(var11, (byte) -103); - } else { - var15 = this.aClass3_Sub13_1148.method166(var11)[0]; - } - - for (int var16 = var2 - 1; var16 >= 0; --var16) { - int var17 = var12[var16] >> 4; - if (var17 > 255) { - var17 = 255; - } - - if (var17 < 0) { - var17 = 0; - } - - int var18 = var13[var16] >> 4; - if (var18 > 255) { - var18 = 255; - } - - int var19 = var14[var16] >> 4; - if (var19 > 255) { - var19 = 255; - } - - var17 = BufferedDataStream.anIntArray3804[var17]; - if (var19 < 0) { - var19 = 0; - } - - if (var18 < 0) { - var18 = 0; - } - - var18 = BufferedDataStream.anIntArray3804[var18]; - var19 = BufferedDataStream.anIntArray3804[var19]; - int var20; - if (var17 == 0 && var18 == 0 && var19 == 0) { - var20 = 0; - } else { - var20 = var15[var16] >> 4; - if (255 < var20) { - var20 = 255; - } - - if (var20 < 0) { - var20 = 0; - } - } - - var9[var10++] = (byte) var17; - var9[var10++] = (byte) var18; - var9[var10++] = (byte) var19; - var9[var10++] = (byte) var20; - if (var3) { - var10 += -4 + (var2 << 2); - } - } - } - - for (var11 = 0; this.aClass3_Sub13Array1147.length > var11; ++var11) { - this.aClass3_Sub13Array1147[var11].method161((byte) -45); - } - - return var9; - } catch (RuntimeException var21) { - throw ClientErrorException.clientError(var21, "lc.F(" + var1 + ',' + var2 + ',' + var3 + ',' + (var4 != null ? "{...}" : "null") + ',' + 0.7 + ',' + 8839 + ',' + (var8 != null ? "{...}" : "null") + ')'); - } - } - - final boolean method1408(Interface2 var2, CacheIndex var3) { - try { - int var4; - if (0 < anInt1668) { - for (var4 = 0; this.anIntArray1144.length > var4; ++var4) { - if (!var3.method2129((byte) -78, this.anIntArray1144[var4], anInt1668)) { - return false; - } - } - } else { - for (var4 = 0; this.anIntArray1144.length > var4; ++var4) { - if (!var3.retrieveSpriteFile(this.anIntArray1144[var4])) { - return false; - } - } - } - - for (var4 = 0; var4 < this.anIntArray1149.length; ++var4) { - if (!var2.method11(21, this.anIntArray1149[var4])) { - return false; - } - } - - return true; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "lc.B(" + true + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation.java b/Client/src/main/java/org/runite/client/TextureOperation.java deleted file mode 100644 index 5e9f22f6b..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Linkable; -import org.runite.client.drawcalls.LoadingBox; - -import java.util.Random; - -public abstract class TextureOperation extends Linkable { - - boolean aBoolean2375; - Class97 aClass97_2376; - TextureOperation[] subOperations; - static int anInt2378 = 0; - int imageCacheCapacity; - Class114 aClass114_2382; - static int anInt2383 = 0; - - static int method1603(byte var0, int var1, Random var2) { - try { - if (var1 <= 0) { - throw new IllegalArgumentException(); - } else if (Class140_Sub6.method2021((byte) -115, var1)) { - return (int) (((long) var2.nextInt() & 4294967295L) * (long) var1 >> 32); - } else { - int var3 = -((int) (4294967296L % (long) var1)) + Integer.MIN_VALUE; - - int var4; - do { - var4 = var2.nextInt(); - } while (var3 <= var4); - - return TextureOperation27.method201(var4, var1); - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ni.C(" + var0 + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - - final int[] method152(int var1, int var2) { - return this.subOperations[var1].aBoolean2375 ? this.subOperations[var1].method154(var2, (byte) -118) : this.subOperations[var1].method166(var2)[0]; - } - - static void method153() { - Class3_Sub26.aLinkedList_2557 = new LinkedList(); - } - - int[] method154(int var1, byte var2) { - try { - throw new IllegalStateException("This operation does not have a monochrome output"); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "j.D(" + var1 + ',' + var2 + ')'); - } - } - - int getSpriteFrame() { - return -1; - } - - void decode(int var1, DataBuffer var2) { - } - - void postDecode() { - } - - int method159(int var1) { - try { - if (var1 != 4) { - LoadingBox.draw(true, null); - } - - return -1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "j.GA(" + var1 + ')'); - } - } - - final void method160(int var1, int var2) { - try { - - int var4 = 255 == this.imageCacheCapacity ? var1 : this.imageCacheCapacity; - if (this.aBoolean2375) { - this.aClass114_2382 = new Class114(var4, var1, var2); - } else { - this.aClass97_2376 = new Class97(var4, var1, var2); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "j.SA(" + var1 + ',' + var2 + ',' + 250 + ')'); - } - } - - void method161(byte var1) { - try { - if (var1 != -45) { - anInt2383 = 16; - } - - if (this.aBoolean2375) { - this.aClass114_2382.method1706(); - this.aClass114_2382 = null; - } else { - this.aClass97_2376.method1590(); - this.aClass97_2376 = null; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "j.BA(" + var1 + ')'); - } - } - - final int[][] method162(int var1, int var2, byte var3) { - try { - if (var3 > -45) { - return null; - } else if (this.subOperations[var2].aBoolean2375) { - int[] var4 = this.subOperations[var2].method154(var1, (byte) -105); - return new int[][]{var4, var4, var4}; - } else { - return this.subOperations[var2].method166(var1); - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "j.UA(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static Class3_Sub28_Sub17_Sub1 method163(byte[] var0) { - try { - if (var0 == null) { - return null; - } else { - - Class3_Sub28_Sub17_Sub1 var2 = new Class3_Sub28_Sub17_Sub1(var0, Class164.anIntArray2048, Unsorted.anIntArray2591, Class140_Sub7.anIntArray2931, Unsorted.anIntArray3076, Class163_Sub1.aByteArrayArray2987); - Class39.method1035((byte) 126); - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "j.WA(" + "{...}" + ',' + 25208 + ')'); - } - } - - TextureOperation(int var1, boolean var2) { - try { - this.subOperations = new TextureOperation[var1]; - this.aBoolean2375 = var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "j.(" + var1 + ',' + var2 + ')'); - } - } - - int[][] method166(int var2) { - throw new IllegalStateException("This operation does not have a colour output"); - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation0.java b/Client/src/main/java/org/runite/client/TextureOperation0.java deleted file mode 100644 index 413fe9b83..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation0.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.runite.client; - -import org.rs09.client.util.ArrayUtils; - -public final class TextureOperation0 extends TextureOperation { - - private int anInt3276; - - protected TextureOperation0() { - super(0, true); - this.anInt3276 = 4096; - - try { - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "mi.(" + 4096 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - if (var1 == 0) { - this.anInt3276 = (var2.readUnsignedByte() << 12) / 255; - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if (this.aClass114_2382.aBoolean1580) { - ArrayUtils.fill(var4, 0, Class113.anInt1559, this.anInt3276); - } - - return var4; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "mi.D(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation1.java b/Client/src/main/java/org/runite/client/TextureOperation1.java deleted file mode 100644 index a38d40277..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation1.java +++ /dev/null @@ -1,176 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.HashTable; -import org.rs09.client.data.ReferenceCache; - -final class TextureOperation1 extends TextureOperation { - - static int anInt3274; - private int anInt3129; - static ReferenceCache aReferenceCache_3130 = new ReferenceCache(4); - private int anInt3134; - private int anInt3135; - - - protected TextureOperation1() { - super(0, false); - - try { - this.method218(0); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fm.(" + 0 + ')'); - } - } - - private void method218(int var2) { - try { - this.anInt3134 = 4080 & var2 >> 4; - this.anInt3135 = var2 << 4 & 4080; - this.anInt3129 = (var2 & 16711680) >> 12; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "fm.Q(" + (byte) 75 + ',' + var2 + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 != -1) { - method222(-87, 26, 75, -56, 22, -68); - } - - int[][] var3 = this.aClass97_2376.method1594((byte)-123, var2); - if(this.aClass97_2376.aBoolean1379) { - int[] var4 = var3[0]; - int[] var5 = var3[1]; - int[] var6 = var3[2]; - - for(int var7 = 0; Class113.anInt1559 > var7; ++var7) { - var4[var7] = this.anInt3129; - var5[var7] = this.anInt3134; - var6[var7] = this.anInt3135; - } - } - - return var3; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "fm.T(" + -1 + ',' + var2 + ')'); - } - } - - static void method219(boolean var0) { - try { - if(var0) { - if(-1 != ConfigInventoryDefinition.anInt3655) { - Class60.method1208((byte)-128, ConfigInventoryDefinition.anInt3655); - } - - for(Class3_Sub31 var2 = TextureOperation23.aHashTable_3208.first(); null != var2; var2 = TextureOperation23.aHashTable_3208.next()) { - TextureOperation19.method254(true, var2); - } - - ConfigInventoryDefinition.anInt3655 = -1; - TextureOperation23.aHashTable_3208 = new HashTable(8); - Class3_Sub7.method122(3000 + -2918); - ConfigInventoryDefinition.anInt3655 = Client.loginScreenInterfaceID; - Class124.method1746(false, (byte)-36); - Unsorted.method1093(false); - TextureOperation24.method226(ConfigInventoryDefinition.anInt3655); - } - - Class3_Sub28_Sub5.anInt3590 = -1; - TextureOperation20.method229(Class161.anInt2027); - Class102.player = new Player(); - Class102.player.zAxis = 3000; - Class102.player.xAxis = 3000; - if(HDToolKit.highDetail) { - if(Class133.anInt1753 == 2) { - NPC.anInt3995 = Unsorted.anInt30 << 7; - Class77.anInt1111 = Class146.anInt1904 << 7; - } else { - InterfaceWidget.d(3000 ^ '\uf447'); - } - - TextureOperation31.method236(); - TextureOperation26.method195(); - Class117.method1719(28); - } else { - Class84.method1418(CacheIndex.spritesIndex); - Class117.method1719(10); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fm.E(" + var0 + ',' + 3000 + ')'); - } - } - - static void method220(int var1, int var2) { - try { - Class46.anInt741 = AtmosphereParser.aAtmosphereParserArrayArray1581[var2][var1].anInt1185; - anInt3274 = AtmosphereParser.aAtmosphereParserArrayArray1581[var2][var1].anInt1181; - - AtmosphereParser.anInt1191 = AtmosphereParser.aAtmosphereParserArrayArray1581[var2][var1].anInt1178; - Class92.setLightPosition((float)Class46.anInt741, (float) anInt3274, (float) AtmosphereParser.anInt1191); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "fm.C(" + true + ',' + var1 + ',' + var2 + ')'); - } - } - - static void method221(int var0, RSString var1, RSString var2, RSString var3, int var4) { - try { - MessageManager.sendGameMessage(var0, var4, var1, var3, var2); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "fm.F(" + var0 + ',' + (var1 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + (var3 != null?"{...}":"null") + ',' + var4 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.method218(var2.readMedium()); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "fm.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static boolean method222(int var0, int var1, int var2, int var3, int var4, int var5) { - int var6; - int var7; - if(var1 == var2 && var3 == var4) { - if(Class8.method846(var0, var1, var3)) { - var6 = var1 << 7; - var7 = var3 << 7; - return TextureOperation10.method349(var6 + 1, Class44.anIntArrayArrayArray723[var0][var1][var3] + var5, var7 + 1) && TextureOperation10.method349(var6 + 128 - 1, Class44.anIntArrayArrayArray723[var0][var1 + 1][var3] + var5, var7 + 1) && TextureOperation10.method349(var6 + 128 - 1, Class44.anIntArrayArrayArray723[var0][var1 + 1][var3 + 1] + var5, var7 + 128 - 1) && TextureOperation10.method349(var6 + 1, Class44.anIntArrayArrayArray723[var0][var1][var3 + 1] + var5, var7 + 128 - 1); - } else { - return false; - } - } else { - for(var6 = var1; var6 <= var2; ++var6) { - for(var7 = var3; var7 <= var4; ++var7) { - if(Class81.anIntArrayArrayArray1142[var0][var6][var7] == -Class3_Sub28_Sub1.anInt3539) { - return false; - } - } - } - - var6 = (var1 << 7) + 1; - var7 = (var3 << 7) + 2; - int var8 = Class44.anIntArrayArrayArray723[var0][var1][var3] + var5; - if(TextureOperation10.method349(var6, var8, var7)) { - int var9 = (var2 << 7) - 1; - if(TextureOperation10.method349(var9, var8, var7)) { - int var10 = (var4 << 7) - 1; - if(!TextureOperation10.method349(var6, var8, var10)) { - return false; - } else return TextureOperation10.method349(var9, var8, var10); - } else { - return false; - } - } else { - return false; - } - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation10.java b/Client/src/main/java/org/runite/client/TextureOperation10.java deleted file mode 100644 index 7f59223f3..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation10.java +++ /dev/null @@ -1,450 +0,0 @@ -package org.runite.client; -import org.rs09.client.config.GameConfig; - -import java.math.BigInteger; - -final class TextureOperation10 extends TextureOperation { - - private int[][] anIntArrayArray3438; - static Class3_Sub28_Sub17_Sub1 aClass3_Sub28_Sub17_Sub1_3440; - static BigInteger EXPONENT = GameConfig.EXPONENT; - private int[] anIntArray3443 = new int[257]; - - - final void postDecode() { - try { - - if(this.anIntArrayArray3438 == null) { - this.method345(1); - } - - this.method346(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "um.P(" + ')'); - } - } - - private void method345(int var1) { - try { - if(var1 != 0) { - if(var1 == 1) { - this.anIntArrayArray3438 = new int[2][4]; - this.anIntArrayArray3438[0][1] = 0; - this.anIntArrayArray3438[0][0] = 0; - this.anIntArrayArray3438[1][0] = 4096; - this.anIntArrayArray3438[0][3] = 0; - this.anIntArrayArray3438[1][1] = 4096; - this.anIntArrayArray3438[0][2] = 0; - this.anIntArrayArray3438[1][2] = 4096; - this.anIntArrayArray3438[1][3] = 4096; - } else if(var1 == 2) { - this.anIntArrayArray3438 = new int[8][4]; - this.anIntArrayArray3438[0][0] = 0; - this.anIntArrayArray3438[1][0] = 2867; - this.anIntArrayArray3438[2][0] = 3072; - this.anIntArrayArray3438[0][2] = 2602; - this.anIntArrayArray3438[3][0] = 3276; - this.anIntArrayArray3438[0][3] = 2361; - this.anIntArrayArray3438[1][3] = 1558; - this.anIntArrayArray3438[4][0] = 3481; - this.anIntArrayArray3438[5][0] = 3686; - this.anIntArrayArray3438[2][3] = 1413; - this.anIntArrayArray3438[3][3] = 947; - this.anIntArrayArray3438[4][3] = 722; - this.anIntArrayArray3438[6][0] = 3891; - this.anIntArrayArray3438[1][2] = 1799; - this.anIntArrayArray3438[7][0] = 4096; - this.anIntArrayArray3438[5][3] = 1766; - this.anIntArrayArray3438[2][2] = 1734; - this.anIntArrayArray3438[3][2] = 1220; - this.anIntArrayArray3438[4][2] = 963; - this.anIntArrayArray3438[5][2] = 2152; - this.anIntArrayArray3438[6][3] = 915; - this.anIntArrayArray3438[7][3] = 1140; - this.anIntArrayArray3438[0][1] = 2650; - this.anIntArrayArray3438[6][2] = 1060; - this.anIntArrayArray3438[1][1] = 2313; - this.anIntArrayArray3438[2][1] = 2618; - this.anIntArrayArray3438[3][1] = 2296; - this.anIntArrayArray3438[4][1] = 2072; - this.anIntArrayArray3438[7][2] = 1413; - this.anIntArrayArray3438[5][1] = 2730; - this.anIntArrayArray3438[6][1] = 2232; - this.anIntArrayArray3438[7][1] = 1686; - } else if(3 == var1) { - this.anIntArrayArray3438 = new int[7][4]; - this.anIntArrayArray3438[0][0] = 0; - this.anIntArrayArray3438[0][3] = 4096; - this.anIntArrayArray3438[1][3] = 4096; - this.anIntArrayArray3438[2][3] = 0; - this.anIntArrayArray3438[1][0] = 663; - this.anIntArrayArray3438[0][1] = 0; - this.anIntArrayArray3438[3][3] = 0; - this.anIntArrayArray3438[1][1] = 0; - this.anIntArrayArray3438[2][0] = 1363; - this.anIntArrayArray3438[2][1] = 0; - this.anIntArrayArray3438[4][3] = 0; - this.anIntArrayArray3438[5][3] = 4096; - this.anIntArrayArray3438[6][3] = 4096; - this.anIntArrayArray3438[3][0] = 2048; - this.anIntArrayArray3438[4][0] = 2727; - this.anIntArrayArray3438[5][0] = 3411; - this.anIntArrayArray3438[6][0] = 4096; - this.anIntArrayArray3438[3][1] = 4096; - this.anIntArrayArray3438[4][1] = 4096; - this.anIntArrayArray3438[5][1] = 4096; - this.anIntArrayArray3438[6][1] = 0; - this.anIntArrayArray3438[0][2] = 0; - this.anIntArrayArray3438[1][2] = 4096; - this.anIntArrayArray3438[2][2] = 4096; - this.anIntArrayArray3438[3][2] = 4096; - this.anIntArrayArray3438[4][2] = 0; - this.anIntArrayArray3438[5][2] = 0; - this.anIntArrayArray3438[6][2] = 0; - } else if(4 == var1) { - this.anIntArrayArray3438 = new int[6][4]; - this.anIntArrayArray3438[0][3] = 0; - this.anIntArrayArray3438[0][0] = 0; - this.anIntArrayArray3438[0][2] = 0; - this.anIntArrayArray3438[1][0] = 1843; - this.anIntArrayArray3438[1][2] = 0; - this.anIntArrayArray3438[2][2] = 0; - this.anIntArrayArray3438[1][3] = 1493; - this.anIntArrayArray3438[2][3] = 2939; - this.anIntArrayArray3438[3][3] = 3565; - this.anIntArrayArray3438[3][2] = 1124; - this.anIntArrayArray3438[4][3] = 4031; - this.anIntArrayArray3438[0][1] = 0; - this.anIntArrayArray3438[1][1] = 0; - this.anIntArrayArray3438[5][3] = 4096; - this.anIntArrayArray3438[4][2] = 3084; - this.anIntArrayArray3438[2][0] = 2457; - this.anIntArrayArray3438[2][1] = 0; - this.anIntArrayArray3438[3][0] = 2781; - this.anIntArrayArray3438[4][0] = 3481; - this.anIntArrayArray3438[3][1] = 0; - this.anIntArrayArray3438[4][1] = 546; - this.anIntArrayArray3438[5][2] = 4096; - this.anIntArrayArray3438[5][0] = 4096; - this.anIntArrayArray3438[5][1] = 4096; - } else if(var1 == 5) { - this.anIntArrayArray3438 = new int[16][4]; - this.anIntArrayArray3438[0][3] = 321; - this.anIntArrayArray3438[0][0] = 0; - this.anIntArrayArray3438[0][2] = 192; - this.anIntArrayArray3438[1][0] = 155; - this.anIntArrayArray3438[1][3] = 562; - this.anIntArrayArray3438[1][2] = 449; - this.anIntArrayArray3438[2][0] = 389; - this.anIntArrayArray3438[3][0] = 671; - this.anIntArrayArray3438[2][2] = 690; - this.anIntArrayArray3438[0][1] = 80; - this.anIntArrayArray3438[1][1] = 321; - this.anIntArrayArray3438[4][0] = 897; - this.anIntArrayArray3438[3][2] = 995; - this.anIntArrayArray3438[4][2] = 1397; - this.anIntArrayArray3438[2][1] = 578; - this.anIntArrayArray3438[2][3] = 803; - this.anIntArrayArray3438[5][0] = 1175; - this.anIntArrayArray3438[6][0] = 1368; - this.anIntArrayArray3438[5][2] = 1429; - this.anIntArrayArray3438[3][1] = 947; - this.anIntArrayArray3438[7][0] = 1507; - this.anIntArrayArray3438[4][1] = 1285; - this.anIntArrayArray3438[6][2] = 1461; - this.anIntArrayArray3438[8][0] = 1736; - this.anIntArrayArray3438[3][3] = 1140; - this.anIntArrayArray3438[9][0] = 2088; - this.anIntArrayArray3438[7][2] = 1525; - this.anIntArrayArray3438[4][3] = 1509; - this.anIntArrayArray3438[5][1] = 1525; - this.anIntArrayArray3438[6][1] = 1734; - this.anIntArrayArray3438[5][3] = 1413; - this.anIntArrayArray3438[8][2] = 1590; - this.anIntArrayArray3438[10][0] = 2355; - this.anIntArrayArray3438[9][2] = 2056; - this.anIntArrayArray3438[7][1] = 1413; - this.anIntArrayArray3438[11][0] = 2691; - this.anIntArrayArray3438[12][0] = 3031; - this.anIntArrayArray3438[6][3] = 1333; - this.anIntArrayArray3438[10][2] = 2586; - this.anIntArrayArray3438[11][2] = 3148; - this.anIntArrayArray3438[13][0] = 3522; - this.anIntArrayArray3438[14][0] = 3727; - this.anIntArrayArray3438[7][3] = 1702; - this.anIntArrayArray3438[8][1] = 1108; - this.anIntArrayArray3438[9][1] = 1766; - this.anIntArrayArray3438[10][1] = 2409; - this.anIntArrayArray3438[15][0] = 4096; - this.anIntArrayArray3438[12][2] = 3710; - this.anIntArrayArray3438[11][1] = 3116; - this.anIntArrayArray3438[13][2] = 3421; - this.anIntArrayArray3438[12][1] = 3806; - this.anIntArrayArray3438[13][1] = 3437; - this.anIntArrayArray3438[14][1] = 3116; - this.anIntArrayArray3438[15][1] = 2377; - this.anIntArrayArray3438[8][3] = 2056; - this.anIntArrayArray3438[9][3] = 2666; - this.anIntArrayArray3438[14][2] = 3148; - this.anIntArrayArray3438[15][2] = 2505; - this.anIntArrayArray3438[10][3] = 3276; - this.anIntArrayArray3438[11][3] = 3228; - this.anIntArrayArray3438[12][3] = 3196; - this.anIntArrayArray3438[13][3] = 3019; - this.anIntArrayArray3438[14][3] = 3228; - this.anIntArrayArray3438[15][3] = 2746; - } else { - if(var1 != 6) { - throw new RuntimeException("Invalid gradient preset"); - } - - this.anIntArrayArray3438 = new int[4][4]; - this.anIntArrayArray3438[0][3] = 0; - this.anIntArrayArray3438[0][2] = 4096; - this.anIntArrayArray3438[1][3] = 0; - this.anIntArrayArray3438[0][1] = 0; - this.anIntArrayArray3438[2][3] = 0; - this.anIntArrayArray3438[3][3] = 0; - this.anIntArrayArray3438[0][0] = 2048; - this.anIntArrayArray3438[1][1] = 4096; - this.anIntArrayArray3438[1][0] = 2867; - this.anIntArrayArray3438[2][1] = 4096; - this.anIntArrayArray3438[1][2] = 4096; - this.anIntArrayArray3438[2][2] = 4096; - this.anIntArrayArray3438[3][1] = 4096; - this.anIntArrayArray3438[2][0] = 3276; - this.anIntArrayArray3438[3][2] = 0; - this.anIntArrayArray3438[3][0] = 4096; - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "um.B(" + var1 + ',' + false + ')'); - } - } - - public TextureOperation10() { - super(1, false); - } - - final void decode(int var1, DataBuffer var2) { - try { - if(true) { - if(var1 == 0) { - int var4 = var2.readUnsignedByte(); - if(var4 == 0) { - this.anIntArrayArray3438 = new int[var2.readUnsignedByte()][4]; - - for(int var5 = 0; var5 < this.anIntArrayArray3438.length; ++var5) { - this.anIntArrayArray3438[var5][0] = var2.readUnsignedShort(); - this.anIntArrayArray3438[var5][1] = var2.readUnsignedByte() << 4; - this.anIntArrayArray3438[var5][2] = var2.readUnsignedByte() << 4; - this.anIntArrayArray3438[var5][3] = var2.readUnsignedByte() << 4; - } - } else { - this.method345(var4); - } - } - - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "um.A(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + ')'); - } - } - - private void method346() { - try { - - int var5 = this.anIntArrayArray3438.length; - if(var5 > 0) { - for(int var6 = 0; var6 < 257; ++var6) { - int var7 = 0; - int var8 = var6 << 4; - - for(int var9 = 0; var5 > var9 && var8 >= this.anIntArrayArray3438[var9][0]; ++var9) { - ++var7; - } - - int var2; - int var3; - int var4; - int[] var14; - if(var5 > var7) { - var14 = this.anIntArrayArray3438[var7]; - if(var7 > 0) { - int[] var10 = this.anIntArrayArray3438[-1 + var7]; - int var11 = (var8 - var10[0] << 12) / (var14[0] + -var10[0]); - int var12 = 4096 + -var11; - var4 = var10[3] * var12 + var14[3] * var11 >> 12; - var2 = var12 * var10[1] + var11 * var14[1] >> 12; - var3 = var12 * var10[2] + var11 * var14[2] >> 12; - } else { - var2 = var14[1]; - var4 = var14[3]; - var3 = var14[2]; - } - } else { - var14 = this.anIntArrayArray3438[var5 + -1]; - var4 = var14[3]; - var3 = var14[2]; - var2 = var14[1]; - } - - var2 >>= 4; - var3 >>= 4; - if(var2 < 0) { - var2 = 0; - } else if(var2 > 255) { - var2 = 255; - } - - if(0 > var3) { - var3 = 0; - } else if(var3 > 255) { - var3 = 255; - } - - var4 >>= 4; - if(var4 >= 0) { - if(var4 > 255) { - var4 = 255; - } - } else { - var4 = 0; - } - - this.anIntArray3443[var6] = TextureOperation3.bitwiseOr(var4, TextureOperation3.bitwiseOr(var3 << 8, var2 << 16)); - } - } - - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "um.E(" + 114 + ')'); - } - } - - static void method347() { - try { - PacketParser.inTutorialIsland = 0; - - int var1 = Class131.anInt1716 + (Class102.player.xAxis >> 7); - int var2 = (Class102.player.zAxis >> 7) - -Texture.anInt1152; - if(var1 >= 3053 && var1 <= 3156 && var2 >= 3056 && var2 <= 3136) { - PacketParser.inTutorialIsland = 1; - } - - if(var1 >= 3072 && var1 <= 3118 && var2 >= 9492 && var2 <= 9535) { - PacketParser.inTutorialIsland = 1; - } - - if(PacketParser.inTutorialIsland == 1 && var1 >= 3139 && 3062 >= var2) { - PacketParser.inTutorialIsland = 0; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "um.O(" + true + ')'); - } - } - - final int[][] method166(int var2) { - try { - int[][] var3 = this.aClass97_2376.method1594((byte)90, var2); - - if(this.aClass97_2376.aBoolean1379) { - int[] var5 = this.method152(0, var2); - int[] var7 = var3[1]; - int[] var6 = var3[0]; - int[] var8 = var3[2]; - - for(int var9 = 0; Class113.anInt1559 > var9; ++var9) { - int var4 = var5[var9] >> 4; - if(var4 < 0) { - var4 = 0; - } - - if(var4 > 256) { - var4 = 256; - } - - var4 = this.anIntArray3443[var4]; - var6[var9] = Unsorted.bitwiseAnd(var4, 16711680) >> 12; - var7[var9] = Unsorted.bitwiseAnd(4080, var4 >> 4); - var8[var9] = Unsorted.bitwiseAnd(255, var4) << 4; - } - } - - return var3; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "um.T(" + -1 + ',' + var2 + ')'); - } - } - - static boolean method349(int var0, int var1, int var2) { - for(int var3 = 0; var3 < Class72.anInt1672; ++var3) { - Class113 var4 = Class145.aClass113Array1895[var3]; - int var5; - int var6; - int var7; - int var8; - int var9; - if(var4.anInt1564 == 1) { - var5 = var4.anInt1562 - var0; - if(var5 > 0) { - var6 = var4.anInt1560 + (var4.anInt1555 * var5 >> 8); - var7 = var4.anInt1550 + (var4.anInt1551 * var5 >> 8); - var8 = var4.anInt1544 + (var4.anInt1561 * var5 >> 8); - var9 = var4.anInt1548 + (var4.anInt1565 * var5 >> 8); - if(var2 >= var6 && var2 <= var7 && var1 >= var8 && var1 <= var9) { - return true; - } - } - } else if(var4.anInt1564 == 2) { - var5 = var0 - var4.anInt1562; - if(var5 > 0) { - var6 = var4.anInt1560 + (var4.anInt1555 * var5 >> 8); - var7 = var4.anInt1550 + (var4.anInt1551 * var5 >> 8); - var8 = var4.anInt1544 + (var4.anInt1561 * var5 >> 8); - var9 = var4.anInt1548 + (var4.anInt1565 * var5 >> 8); - if(var2 >= var6 && var2 <= var7 && var1 >= var8 && var1 <= var9) { - return true; - } - } - } else if(var4.anInt1564 == 3) { - var5 = var4.anInt1560 - var2; - if(var5 > 0) { - var6 = var4.anInt1562 + (var4.anInt1549 * var5 >> 8); - var7 = var4.anInt1545 + (var4.anInt1557 * var5 >> 8); - var8 = var4.anInt1544 + (var4.anInt1561 * var5 >> 8); - var9 = var4.anInt1548 + (var4.anInt1565 * var5 >> 8); - if(var0 >= var6 && var0 <= var7 && var1 >= var8 && var1 <= var9) { - return true; - } - } - } else if(var4.anInt1564 == 4) { - var5 = var2 - var4.anInt1560; - if(var5 > 0) { - var6 = var4.anInt1562 + (var4.anInt1549 * var5 >> 8); - var7 = var4.anInt1545 + (var4.anInt1557 * var5 >> 8); - var8 = var4.anInt1544 + (var4.anInt1561 * var5 >> 8); - var9 = var4.anInt1548 + (var4.anInt1565 * var5 >> 8); - if(var0 >= var6 && var0 <= var7 && var1 >= var8 && var1 <= var9) { - return true; - } - } - } else if(var4.anInt1564 == 5) { - var5 = var1 - var4.anInt1544; - if(var5 > 0) { - var6 = var4.anInt1562 + (var4.anInt1549 * var5 >> 8); - var7 = var4.anInt1545 + (var4.anInt1557 * var5 >> 8); - var8 = var4.anInt1560 + (var4.anInt1555 * var5 >> 8); - var9 = var4.anInt1550 + (var4.anInt1551 * var5 >> 8); - if(var0 >= var6 && var0 <= var7 && var2 >= var8 && var2 <= var9) { - return true; - } - } - } - } - - return false; - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation11.java b/Client/src/main/java/org/runite/client/TextureOperation11.java deleted file mode 100644 index d99050549..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation11.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class TextureOperation11 extends TextureOperation { - - static int anInt3244 = 0; - private int anInt3245 = 4096; - private int anInt3250 = 4096; - private int anInt3252 = 4096; - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3252 = var2.readUnsignedShort(); - } else if (var1 == 1) { - this.anInt3245 = var2.readUnsignedShort(); - } else if (2 == var1) { - this.anInt3250 = var2.readUnsignedShort(); - } - - if(!true) { - method266(12); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "mg.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - public static void method266(int var0) {//TODO: Misplaced Check Method - try { - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "mg.U(" + var0 + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 != -1) { - anInt3244 = -40; - } - - int[][] var3 = this.aClass97_2376.method1594((byte)-115, var2); - if(this.aClass97_2376.aBoolean1379) { - int[][] var4 = this.method162(var2, 0, (byte)-74); - int[] var5 = Objects.requireNonNull(var4)[0]; - int[] var6 = var4[1]; - int[] var7 = var4[2]; - int[] var9 = var3[1]; - int[] var8 = var3[0]; - int[] var10 = var3[2]; - - for(int var11 = 0; Class113.anInt1559 > var11; ++var11) { - int var12 = var5[var11]; - int var14 = var6[var11]; - int var13 = var7[var11]; - if(var13 == var12 && var13 == var14) { - var8[var11] = this.anInt3252 * var12 >> 12; - var9[var11] = var13 * this.anInt3245 >> 12; - var10[var11] = var14 * this.anInt3250 >> 12; - } else { - var8[var11] = this.anInt3252; - var9[var11] = this.anInt3245; - var10[var11] = this.anInt3250; - } - } - } - - return var3; - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "mg.T(" + -1 + ',' + var2 + ')'); - } - } - - public TextureOperation11() { - super(1, false); - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation12.java b/Client/src/main/java/org/runite/client/TextureOperation12.java deleted file mode 100644 index d9a9bf0c1..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation12.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.config.GameConfig; - -public final class TextureOperation12 extends TextureOperation { - - public static BufferedDataStream outgoingBuffer = new BufferedDataStream(); - private int anInt3036 = 0; - private int anInt3037 = 1; - private int anInt3038 = 0; - static RSString aClass2323; - static String aString2324; - static RSString aClass2325; - - - static void method167(int var0) { - try { - if(TextureOperation33.aClass148_3049 != null) { - KeyboardListener var1 = TextureOperation33.aClass148_3049; - synchronized(var1) { - TextureOperation33.aClass148_3049 = null; - } - } - - if(var0 != 0) { - method171(119, -44, -76, -104, 29, -65, 34, 18, 104); - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ag.B(" + var0 + ')'); - } - } - - public TextureOperation12() { - super(0, true); - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - int var4; - if(this.aClass114_2382.aBoolean1580) { - var4 = Class163_Sub3.anIntArray2999[var1]; - int var5 = var4 - 2048 >> 1; - - for(int var6 = 0; var6 < Class113.anInt1559; ++var6) { - int var8 = Class102.anIntArray2125[var6]; - int var9 = -2048 + var8 >> 1; - int var7; - if(this.anInt3038 == 0) { - var7 = (var8 + -var4) * this.anInt3037; - } else { - int var10 = var9 * var9 - -(var5 * var5) >> 12; - var7 = (int)(Math.sqrt((float)var10 / 4096.0F) * 4096.0D); - var7 = (int)(3.141592653589793D * (double)(var7 * this.anInt3037)); - } - - var7 -= var7 & -4096; - if(this.anInt3036 == 0) { - var7 = TextureOperation23.anIntArray3212[(var7 & 4085) >> 4] + 4096 >> 1; - } else if(this.anInt3036 == 2) { - var7 -= 2048; - if(var7 < 0) { - var7 = -var7; - } - - var7 = -var7 + 2048 << 1; - } - - var3[var6] = var7; - } - } - - return var3; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "ag.D(" + var1 + ',' + var2 + ')'); - } - } - - static void method229() { - DataBuffer buffer = outgoingBuffer; - buffer.writeString(aClass2323); - for (char c : aString2324.toCharArray()) { - if (c == '-') { - c = ':'; - } - buffer.writeByte(c); - } - buffer.writeByte(0); - buffer.writeString(aClass2325); - } - - static void method169() { - try { - Class32.method995(); - - for(int var1 = 0; 4 > var1; ++var1) { - AtmosphereParser.aClass91Array1182[var1].method1496(); - } - - System.gc(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ag.O(" + 22230 + ')'); - } - } - - static Class method170(String var1) throws ClassNotFoundException { - try { - - return var1.equals("B")?Byte.TYPE:(!var1.equals("I")?(var1.equals("S")?Short.TYPE:(!var1.equals("J")?(var1.equals("Z")?Boolean.TYPE:(var1.equals("F")?Float.TYPE:(var1.equals("D")?Double.TYPE:(var1.equals("C")?Character.TYPE:Class.forName(GameConfig.PACKAGE_NAME + "." + var1))))):Long.TYPE)):Integer.TYPE); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ag.C(" + 6092 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - - final void postDecode() { - try { - Class8.method844((byte)-9); - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ag.P(" + ')'); - } - } - - static void method171(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - if(var0 != -101) { - method167(-46); - } - - if(Unsorted.loadInterface(var1)) { - Unsorted.method1095(var2, var8, var4, GameObject.aClass11ArrayArray1834[var1], var3, -1, var7, var6, (byte)119, var5); - } else if (var5 == -1) { - for (int var9 = 0; var9 < 100; ++var9) { - Unsorted.aBooleanArray3674[var9] = true; - } - } else { - Unsorted.aBooleanArray3674[var5] = true; - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "ag.E(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - - if(var1 == 0) { - this.anInt3038 = var2.readUnsignedByte(); - } else if (1 == var1) { - this.anInt3036 = var2.readUnsignedByte(); - } else if (var1 == 3) { - this.anInt3037 = var2.readUnsignedByte(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ag.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - - static void method445() { - aClass2323 = RSString.parse(System.getProperty("user.name")); - aString2324 = Class39.method132893(); - aClass2325 = Signlink.osName.startsWith("win") ? Class44.method3435() : Class44.method3434(); - } -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation13.java b/Client/src/main/java/org/runite/client/TextureOperation13.java deleted file mode 100644 index 22fb75c4d..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation13.java +++ /dev/null @@ -1,249 +0,0 @@ -package org.runite.client; - - - -final class TextureOperation13 extends TextureOperation { - - static int anInt3362 = -1; - static int anInt3363; - static LinkedList aLinkedList_3364 = new LinkedList(); - static long aLong3366; - static int[] anIntArray3367 = new int[64]; - - - static void method312(int var0, int var1, int var2, Player playerUsername, int var4) { - try { - if(Class102.player != playerUsername) { - if(Unsorted.menuOptionCount < 400) { - RSString var5; - if(playerUsername.anInt3974 == 0) { - boolean var6 = true; - if(Class102.player.anInt3970 != -1 && -1 != playerUsername.anInt3970) { - int var7 = Math.max(playerUsername.COMBAT_LEVEL, Class102.player.COMBAT_LEVEL); - int var8 = Math.min(playerUsername.anInt3970, Class102.player.anInt3970); - int var9 = 5 - -(var7 * 10 / 100) + var8; - int var10 = -playerUsername.COMBAT_LEVEL + Class102.player.COMBAT_LEVEL; - if(0 > var10) { - var10 = -var10; - } - - if(var9 < var10) { - var6 = false; - } - } - - RSString levelEquals = Class158.paramGameTypeID != 1?TextCore.HasLevel:TextCore.HasRating; - if(playerUsername.COMBAT_LEVEL < playerUsername.combatLevel) { - var5 = RSString.stringCombiner(new RSString[]{playerUsername.getName(), var6 ? Player.combatLevelColor(playerUsername.COMBAT_LEVEL, (byte)-73, Class102.player.COMBAT_LEVEL) : ColorCore.ContextColor , TextCore.LEFT_PARENTHESES, levelEquals, RSString.stringAnimator(playerUsername.COMBAT_LEVEL), TextCore.aClass94_673, RSString.stringAnimator(playerUsername.combatLevel + -playerUsername.COMBAT_LEVEL), TextCore.RIGHT_PARENTHESES}); - } else { - //here - var5 = RSString.stringCombiner(new RSString[]{playerUsername.getName(), var6 ? Player.combatLevelColor(playerUsername.COMBAT_LEVEL, (byte)-128, Class102.player.COMBAT_LEVEL) : Player.getCombatLevelDifferenceColor(playerUsername.COMBAT_LEVEL, Class102.player.COMBAT_LEVEL), TextCore.LEFT_PARENTHESES, levelEquals, RSString.stringAnimator(playerUsername.COMBAT_LEVEL), TextCore.RIGHT_PARENTHESES}); - } - } else { - var5 = RSString.stringCombiner(new RSString[]{playerUsername.getName(), TextCore.LEFT_PARENTHESES, TextCore.HasSkill, RSString.stringAnimator(playerUsername.anInt3974), TextCore.RIGHT_PARENTHESES}); - } - - int var12; - if(Class164_Sub1.anInt3012 == 1) { - Class3_Sub24_Sub4.method1177(Class99.anInt1403, var0, (byte)-80, RSString.stringCombiner(new RSString[]{RenderAnimationDefinition.aClass94_378, TextCore.aClass94_892, var5}), var4, (short)1, TextCore.HasUse, var2); - } else if(!GameObject.aBoolean1837) { - for(var12 = 7; var12 >= 0; --var12) { - if(null != Class91.aClass94Array1299[var12]) { - short var14 = 0; - if(Class158.paramGameTypeID == 0 && Class91.aClass94Array1299[var12].equalsStringIgnoreCase(TextCore.HasAttack)) { - //If other player level greater than my level, then right click to attack. - if(playerUsername.COMBAT_LEVEL > Class102.player.COMBAT_LEVEL) { - var14 = 2000;//Var for right click higher level players - } - if(Class102.player.teamId != 0 && playerUsername.teamId != 0) { - if(playerUsername.teamId == Class102.player.teamId) { - var14 = 2000; - } else { - var14 = 0; - } - } - } else if(Class1.aBooleanArray54[var12]) { - var14 = 2000; - } - - short var15 = Class7.aShortArray2167[var12]; - var15 += var14; - Class3_Sub24_Sub4.method1177(TextureOperation35.anIntArray3328[var12], var0, (byte)-73, RSString.stringCombiner(new RSString[]{ColorCore.ContextColor , var5}), var4, var15, Class91.aClass94Array1299[var12], var2); - } - } - } else if((8 & Class164.anInt2051) != 0) { - Class3_Sub24_Sub4.method1177(Unsorted.anInt1887, var0, (byte)-58, RSString.stringCombiner(new RSString[]{TextCore.aClass94_676, TextCore.aClass94_892, var5}), var4, (short)15, Class3_Sub28_Sub9.aClass94_3621, var2); - } - - if(var1 <= 0) { - aLong3366 = -79L; - } - - for(var12 = 0; var12 < Unsorted.menuOptionCount; ++var12) { - if(TextureOperation27.aShortArray3095[var12] == 60) { - Class163_Sub2_Sub1.aClass94Array4016[var12] = RSString.stringCombiner(new RSString[]{ColorCore.ContextColor , var5}); - break; - } - } - - } - } - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "rj.C(" + var0 + ',' + var1 + ',' + var2 + ',' + (playerUsername != null?"{...}":"null") + ',' + var4 + ')'); - } - } - - static void method313(byte var0) { - try { - if(var0 <= 51) { - method312(77, -52, -42, null, 120); - } - - Class166.method2257(); - Class3_Sub8.method128(); - Class163_Sub2_Sub1.method2220(); - Class3_Sub10.method139(69); - Class3_Sub26.method512(); - WaterfallShader.method1626((byte)-128); - TextureOperation33.method182(); - Class145.method2077(); - Class25.method959(); - method716(); - Class3_Sub15.method370(); - TextureOperation3.method304(); - Class40.method1045(); - TextureOperation33.method183(); - LinkableRSString.method727(); - Class3_Sub21.method397((byte)-41); - if(TextureOperation20.paramModeWhat != 0) { - for(int var1 = 0; var1 < Class3_Sub6.softReferenceTestArray.length; ++var1) { - Class3_Sub6.softReferenceTestArray[var1] = null; - } - - Class56.anInt893 = 0; - } - - Class108.method1659(); - Class3_Sub10.method142(); - Unsorted.aReferenceCache_1135.clear(); - if(!HDToolKit.highDetail) { - ((Class102)Class51.anInterface2_838).method1618(); - } - - Class56.aClass47_885.clear(); - CacheIndex.skeletonsIndex.method2137(); - CacheIndex.skinsIndex.method2137(); - CacheIndex.interfacesIndex.method2137(); - CacheIndex.soundFXIndex.method2137(); - CacheIndex.landscapesIndex.method2137(); - CacheIndex.musicIndex.method2137(); - CacheIndex.modelsIndex.method2137(); - CacheIndex.spritesIndex.method2137(); - CacheIndex.huffmanEncodingIndex.method2137(); - CacheIndex.music2Index.method2137(); - CacheIndex.interfaceScriptsIndex.method2137(); - TextureOperation1.aReferenceCache_3130.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rj.E(" + var0 + ')'); - } - } - - static void method716() { - try { - Class136.aReferenceCache_1772.clear(); - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ud.A(" + 14073 + ')'); - } - } - - private int method314(int var1, int var2, int var3) { - try { - if(var2 != 7001) { - this.method314(-83, 92, 48); - } - - int var4 = var3 - -(57 * var1); - var4 ^= var4 << 1; - return 4096 + -((var4 * (var4 * var4 * 15731 - -789221) - -1376312589 & Integer.MAX_VALUE) / 262144); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "rj.O(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - public TextureOperation13() { - super(0, true); - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var5 = Class163_Sub3.anIntArray2999[var1]; - - for(int var6 = 0; var6 < Class113.anInt1559; ++var6) { - var3[var6] = this.method314(var5, 7001, Class102.anIntArray2125[var6]) % 4096; - } - } - - return var3; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "rj.D(" + var1 + ',' + var2 + ')'); - } - } - - static void parseObjectMapping(Class91[] var0, int var1, byte[] var2, int var3, int var4, int var5, int var6, boolean var7, int var8, int var9) { - try { - int var12 = -1; - DataBuffer var11 = new DataBuffer(var2); - - while(true) { - int var13 = var11.method773(); - if(var13 == 0) { - return; - } - - var12 += var13; - int var14 = 0; - - while(true) { - int var15 = var11.getSmart(); - if(var15 == 0) { - break; - } - - var14 += -1 + var15; - int var16 = 63 & var14; - int var17 = var14 >> 6 & 63; - int var18 = var14 >> 12; - int var19 = var11.readUnsignedByte(); - int var20 = var19 >> 2; - int var21 = 3 & var19; - if(var18 == var3 && var8 <= var17 && var17 < 8 + var8 && var9 <= var16 && 8 + var9 > var16) { - ObjectDefinition var22 = ObjectDefinition.getObjectDefinition(var12); - int var23 = Class3_Sub7.method121(var16 & 7, var4, var21, var22.SizeY, var22.SizeX, 7 & var17) + var5; - int var24 = GameObject.method1863(var22.SizeX, var4, var22.SizeY, 7 & var17, var21, 7 & var16) + var6; - if(var23 > 0 && var24 > 0 && var23 < 103 && var24 < 103) { - Class91 var25 = null; - if(!var7) { - int var26 = var1; - if(2 == (Unsorted.aByteArrayArrayArray113[1][var23][var24] & 2)) { - var26 = var1 - 1; - } - - if(var26 >= 0) { - var25 = var0[var26]; - } - } - - Class110.method1683(var1, !var7, var1, var7, var25, var12, var20, var23, var24, 3 & var21 + var4); - } - } - } - } - } catch (RuntimeException var27) { - throw ClientErrorException.clientError(var27, "rj.F(" + (var0 != null?"{...}":"null") + ',' + var1 + ',' + (var2 != null?"{...}":"null") + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + (byte) -54 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation14.java b/Client/src/main/java/org/runite/client/TextureOperation14.java deleted file mode 100644 index 2a575618d..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation14.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.runite.client; -final class TextureOperation14 extends TextureOperation { - - static int[] anIntArray3383 = new int[5]; - private int anInt3385 = 585; - static boolean aBoolean3387 = true; - static volatile int anInt3389 = 0; - - - static int method319(int var0, int var1, int var2) { - try { - if(var1 >= -99) { - aBoolean3387 = true; - } - - int var3 = var0 >>> 31; - return (var0 + var3) / var2 - var3; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "sa.E(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation14() { - super(0, true); - } - - static void method320(int var0, int var1, int var2, byte var3, int var4) { - try { - if(var2 >= var4) { - TextureOperation18.method282(Class38.anIntArrayArray663[var1], var4, -83, var2, var0); - } else { - TextureOperation18.method282(Class38.anIntArrayArray663[var1], var2, -48, var4, var0); - } - - if(var3 > -55) { - method320(99, 100, 74, (byte)13, 92); - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "sa.C(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var5 = Class163_Sub3.anIntArray2999[var1]; - - for(int var6 = 0; var6 < Class113.anInt1559; ++var6) { - int var7 = Class102.anIntArray2125[var6]; - int var8; - if(var7 > this.anInt3385 && 4096 - this.anInt3385 > var7 && var5 > 2048 + -this.anInt3385 && this.anInt3385 + 2048 > var5) { - var8 = 2048 - var7; - var8 = var8 < 0?-var8:var8; - var8 <<= 12; - var8 /= -this.anInt3385 + 2048; - var3[var6] = -var8 + 4096; - } else if(-this.anInt3385 + 2048 < var7 && var7 < this.anInt3385 + 2048) { - var8 = var5 + -2048; - var8 = var8 >= 0 ?var8:-var8; - var8 -= this.anInt3385; - var8 <<= 12; - var3[var6] = var8 / (-this.anInt3385 + 2048); - } else if(this.anInt3385 <= var5 && var5 <= 4096 - this.anInt3385) { - if(this.anInt3385 <= var7 && var7 <= 4096 - this.anInt3385) { - var3[var6] = 0; - } else { - var8 = -var5 + 2048; - var8 = var8 < 0 ?-var8:var8; - var8 <<= 12; - var8 /= 2048 - this.anInt3385; - var3[var6] = -var8 + 4096; - } - } else { - var8 = var7 + -2048; - var8 = 0 > var8?-var8:var8; - var8 -= this.anInt3385; - var8 <<= 12; - var3[var6] = var8 / (-this.anInt3385 + 2048); - } - } - } - - return var3; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "sa.D(" + var1 + ',' + var2 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(!true) { - anInt3389 = 99; - } - - if(var1 == 0) { - this.anInt3385 = var2.readUnsignedShort(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "sa.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation15.java b/Client/src/main/java/org/runite/client/TextureOperation15.java deleted file mode 100644 index 08a3802e2..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation15.java +++ /dev/null @@ -1,192 +0,0 @@ -package org.runite.client; -import java.util.Random; - -final class TextureOperation15 extends TextureOperation { - - private int anInt3191 = 2; - private int anInt3193 = 2048; - private int anInt3194 = 1; - private byte[] aByteArray3195 = new byte[512]; - private int anInt3197 = 0; - static int anInt3198 = 0; - private short[] aShortArray3200 = new short[512]; - private int anInt3203 = 5; - private int anInt3204 = 5; - - - private void method242() { - try { - Random var2 = new Random(this.anInt3197); - this.aShortArray3200 = new short[512]; - if(0 < this.anInt3193) { - for(int var3 = 0; 512 > var3; ++var3) { - this.aShortArray3200[var3] = (short) TextureOperation.method1603((byte)23, this.anInt3193, var2); - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "hm.C(" + (byte) 37 + ')'); - } - } - - final void postDecode() { - try { - this.aByteArray3195 = Class49.method1123(this.anInt3197); - this.method242(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hm.P(" + ')'); - } - } - - static int compareEnteredLanguageArgument(RSString var0) { - try { - - for(int var2 = 0; TextureOperation4.aClass94Array3238.length > var2; ++var2) { - if(TextureOperation4.aClass94Array3238[var2].equalsStringIgnoreCase(var0)) { - return var2; - } - } - - return -1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "hm.F(" + (var0 != null?"{...}":"null") + ',' + (byte) 13 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var5 = this.anInt3204 * Class163_Sub3.anIntArray2999[var1] + 2048; - int var6 = var5 >> 12; - int var7 = var6 - -1; - - for(int var15 = 0; var15 < Class113.anInt1559; ++var15) { - TextureOperation36.anInt3422 = Integer.MAX_VALUE; - KeyboardListener.anInt1914 = Integer.MAX_VALUE; - Unsorted.anInt1042 = Integer.MAX_VALUE; - Class3_Sub28_Sub5.anInt3589 = Integer.MAX_VALUE; - int var16 = this.anInt3203 * Class102.anIntArray2125[var15] + 2048; - int var17 = var16 >> 12; - int var18 = 1 + var17; - - int var19; - for(int var9 = var6 - 1; var7 >= var9; ++var9) { - int var13 = 255 & this.aByteArray3195[(var9 >= this.anInt3204 ?-this.anInt3204 + var9:var9) & 0xFF]; - - for(int var8 = var17 + -1; var18 >= var8; ++var8) { - int var14 = (255 & this.aByteArray3195[(var8 >= this.anInt3203 ?-this.anInt3203 + var8:var8) + var13 & 0xFF]) * 2; - int var10 = -(var8 << 12) - (this.aShortArray3200[var14++] - var16); - int var11 = var5 - (this.aShortArray3200[var14] + (var9 << 12)); - var19 = this.anInt3194; - int var12; - if(var19 == 1) { - var12 = var11 * var11 + var10 * var10 >> 12; - } else if (3 == var19) { - var10 = var10 < 0 ? -var10 : var10; - var11 = var11 >= 0 ? var11 : -var11; - var12 = var11 >= var10 ? var11 : var10; - } else if (4 == var19) { - var10 = (int) (Math.sqrt((float) (0 > var10 ? -var10 : var10) / 4096.0F) * 4096.0D); - var11 = (int) (Math.sqrt((float) (var11 >= 0 ? var11 : -var11) / 4096.0F) * 4096.0D); - var12 = var11 + var10; - var12 = var12 * var12 >> 12; - } else if (var19 == 5) { - var10 *= var10; - var11 *= var11; - var12 = (int) (Math.sqrt(Math.sqrt((float) (var11 + var10) / 1.6777216E7F)) * 4096.0D); - } else if (2 == var19) { - var12 = (var10 >= 0 ? var10 : -var10) - -(var11 < 0 ? -var11 : var11); - } else { - var12 = (int) (4096.0D * Math.sqrt((float) (var11 * var11 + var10 * var10) / 1.6777216E7F)); - } - - if(var12 >= Class3_Sub28_Sub5.anInt3589) { - if(Unsorted.anInt1042 > var12) { - TextureOperation36.anInt3422 = KeyboardListener.anInt1914; - KeyboardListener.anInt1914 = Unsorted.anInt1042; - Unsorted.anInt1042 = var12; - } else if(KeyboardListener.anInt1914 <= var12) { - if(var12 < TextureOperation36.anInt3422) { - TextureOperation36.anInt3422 = var12; - } - } else { - TextureOperation36.anInt3422 = KeyboardListener.anInt1914; - KeyboardListener.anInt1914 = var12; - } - } else { - TextureOperation36.anInt3422 = KeyboardListener.anInt1914; - KeyboardListener.anInt1914 = Unsorted.anInt1042; - Unsorted.anInt1042 = Class3_Sub28_Sub5.anInt3589; - Class3_Sub28_Sub5.anInt3589 = var12; - } - } - } - - var19 = this.anInt3191; - if(var19 == 0) { - var3[var15] = Class3_Sub28_Sub5.anInt3589; - } else if(var19 == 1) { - var3[var15] = Unsorted.anInt1042; - } else if (var19 == 3) { - var3[var15] = KeyboardListener.anInt1914; - } else if (var19 == 4) { - var3[var15] = TextureOperation36.anInt3422; - } else if (var19 == 2) { - var3[var15] = Unsorted.anInt1042 + -Class3_Sub28_Sub5.anInt3589; - } - } - } - - return var3; - } catch (RuntimeException var20) { - throw ClientErrorException.clientError(var20, "hm.D(" + var1 + ',' + var2 + ')'); - } - } - - static void method244(int var1, int var2, int var3, int var4) { - try { - int var5; - if(var3 >= var1) { - for(var5 = var1; var5 < var3; ++var5) { - Class38.anIntArrayArray663[var5][var2] = var4; - } - } else { - for(var5 = var3; var1 > var5; ++var5) { - Class38.anIntArrayArray663[var5][var2] = var4; - } - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "hm.E(" + 2 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3203 = this.anInt3204 = var2.readUnsignedByte(); - } else if(var1 == 1) { - this.anInt3197 = var2.readUnsignedByte(); - } else if(2 == var1) { - this.anInt3193 = var2.readUnsignedShort(); - } else if (var1 == 3) { - this.anInt3191 = var2.readUnsignedByte(); - } else if (var1 == 4) { - this.anInt3194 = var2.readUnsignedByte(); - } else if (var1 == 5) { - this.anInt3203 = var2.readUnsignedByte(); - } else if (var1 == 6) { - this.anInt3204 = var2.readUnsignedByte(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "hm.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - public TextureOperation15() { - super(0, true); - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation16.java b/Client/src/main/java/org/runite/client/TextureOperation16.java deleted file mode 100644 index b1ccac665..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation16.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.runite.client; - -final class TextureOperation16 extends TextureOperation { - - private int anInt3108 = 1; - private int anInt3109 = 204; - static short[] aShortArray3110 = new short[256]; - static int anInt3111 = 0; - static Class36 aClass36_3112; - private int anInt3113 = 1; - static int anInt3114 = 0; - static int[][] anIntArrayArray3115; - - public TextureOperation16() { - super(0, true); - } - - final void decode(int var1, DataBuffer var2) { - try { - if(true) { - if(var1 == 0) { - this.anInt3108 = var2.readUnsignedByte(); - } else if (var1 == 1) { - this.anInt3113 = var2.readUnsignedByte(); - } else if (var1 == 2) { - this.anInt3109 = var2.readUnsignedShort(); - } - - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "f.A(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - for(int var5 = 0; Class113.anInt1559 > var5; ++var5) { - int var6 = Class102.anIntArray2125[var5]; - int var7 = Class163_Sub3.anIntArray2999[var1]; - int var8 = this.anInt3108 * var6 >> 12; - int var9 = var7 * this.anInt3113 >> 12; - int var10 = this.anInt3108 * (var6 % (4096 / this.anInt3108)); - int var11 = var7 % (4096 / this.anInt3113) * this.anInt3113; - if(var11 < this.anInt3109) { - for(var8 -= var9; var8 < 0; var8 += 4) { - } - - while(3 < var8) { - var8 -= 4; - } - - if(1 != var8) { - var4[var5] = 0; - continue; - } - - if(var10 < this.anInt3109) { - var4[var5] = 0; - continue; - } - } - - if(var10 < this.anInt3109) { - for(var8 -= var9; 0 > var8; var8 += 4) { - } - - while(var8 > 3) { - var8 -= 4; - } - - if(var8 > 0) { - var4[var5] = 0; - continue; - } - } - - var4[var5] = 4096; - } - } - - return var4; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "f.D(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation17.java b/Client/src/main/java/org/runite/client/TextureOperation17.java deleted file mode 100644 index dfc45faba..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation17.java +++ /dev/null @@ -1,185 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class TextureOperation17 extends TextureOperation { - - private int anInt3174; - private int anInt3175 = 0; - private int anInt3176 = 0; - static LinkedList aLinkedList_3177 = new LinkedList(); - private int anInt3178 = 0; - static int anInt3179; - private int anInt3180; - static int[] anIntArray3181; - private int anInt3182; - static boolean stereoSound = true; - static int[] anIntArray3185 = new int[25]; - private int anInt3186; - private int anInt3188; - private int anInt3189; - - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3175 = var2.readSignedShort(); - } else if(var1 == 1) { - this.anInt3178 = (var2.readSignedByte() << 12) / 100; - } else if (var1 == 2) { - this.anInt3176 = (var2.readSignedByte() << 12) / 100; - } - - if(!true) { - this.method240(-114, 127, 95); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "hk.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - private void method239(int var1, int var2, int var4) { - try { - int var5 = var1 <= 2048?var1 * (4096 + var2) >> 12:-(var1 * var2 >> 12) + var1 + var2; - if(var5 > 0) { - var4 *= 6; - int var7 = -var5 + var1 + var1; - int var9 = var4 >> 12; - int var8 = (-var7 + var5 << 12) / var5; - int var10 = var4 - (var9 << 12); - int var11 = var5 * var8 >> 12; - var11 = var11 * var10 >> 12; - int var12 = var11 + var7; - int var13 = -var11 + var5; - if(0 == var9) { - this.anInt3182 = var7; - this.anInt3186 = var5; - this.anInt3174 = var12; - } else if(var9 == 1) { - this.anInt3182 = var7; - this.anInt3174 = var5; - this.anInt3186 = var13; - } else if (var9 == 2) { - this.anInt3186 = var7; - this.anInt3174 = var5; - this.anInt3182 = var12; - } else if (var9 == 3) { - this.anInt3174 = var13; - this.anInt3182 = var5; - this.anInt3186 = var7; - } else if (var9 == 4) { - this.anInt3182 = var5; - this.anInt3186 = var12; - this.anInt3174 = var7; - } else if (var9 == 5) { - this.anInt3174 = var7; - this.anInt3186 = var5; - this.anInt3182 = var13; - } - } else { - this.anInt3186 = this.anInt3174 = this.anInt3182 = var1; - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "hk.C(" + var1 + ',' + var2 + ',' + 107 + ',' + var4 + ')'); - } - } - - public TextureOperation17() { - super(1, false); - } - - private void method240(int var2, int var3, int var4) { - try { - int var5 = var2 > var3?var2:var3; - var5 = var5 >= var4 ?var5:var4; - int var6 = var3 > var2?var2:var3; - var6 = var6 <= var4 ?var6:var4; - int var7 = -var6 + var5; - if(0 < var7) { - int var9 = (var5 - var3 << 12) / var7; - int var8 = (var5 + -var2 << 12) / var7; - int var10 = (-var4 + var5 << 12) / var7; - if(var2 == var5) { - this.anInt3180 = var3 == var6 ?var10 + 20480:4096 + -var9; - } else if (var3 == var5) { - this.anInt3180 = var4 == var6 ? var8 + 4096 : -var10 + 12288; - } else { - this.anInt3180 = var6 != var2 ? -var8 + 20480 : 12288 + var9; - } - - this.anInt3180 /= 6; - } else { - this.anInt3180 = 0; - } - - this.anInt3188 = (var6 - -var5) / 2; - if(this.anInt3188 > 0 && 4096 > this.anInt3188) { - this.anInt3189 = (var7 << 12) / (this.anInt3188 > 2048?8192 - 2 * this.anInt3188:this.anInt3188 * 2); - } else { - this.anInt3189 = 0; - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "hk.E(" + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 != -1) { - TextCore.COMMAND_HIGHRES_GRAPHICS_FULLSCREEN = null; - } - - int[][] var3 = this.aClass97_2376.method1594((byte)-118, var2); - if(this.aClass97_2376.aBoolean1379) { - int[][] var4 = this.method162(var2, 0, (byte)-72); - int[] var5 = Objects.requireNonNull(var4)[0]; - int[] var6 = var4[1]; - int[] var7 = var4[2]; - int[] var9 = var3[1]; - int[] var10 = var3[2]; - int[] var8 = var3[0]; - - for(int var11 = 0; Class113.anInt1559 > var11; ++var11) { - this.method240(var5[var11], var6[var11], var7[var11]); - this.anInt3188 += this.anInt3176; - if(0 > this.anInt3188) { - this.anInt3188 = 0; - } - - this.anInt3189 += this.anInt3178; - if(this.anInt3188 > 4096) { - this.anInt3188 = 4096; - } - - if(this.anInt3189 < 0) { - this.anInt3189 = 0; - } - - if(4096 < this.anInt3189) { - this.anInt3189 = 4096; - } - - for(this.anInt3180 += this.anInt3175; this.anInt3180 < 0; this.anInt3180 += 4096) { - } - - while(this.anInt3180 > 4096) { - this.anInt3180 -= 4096; - } - - this.method239(this.anInt3188, this.anInt3189, this.anInt3180); - var8[var11] = this.anInt3186; - var9[var11] = this.anInt3174; - var10[var11] = this.anInt3182; - } - } - - return var3; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "hk.T(" + -1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation18.java b/Client/src/main/java/org/runite/client/TextureOperation18.java deleted file mode 100644 index 02d132d0a..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation18.java +++ /dev/null @@ -1,176 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.config.GameConfig; - -import java.awt.*; - -final class TextureOperation18 extends TextureOperation39 { - - static int anInt4032 = 0; - static int[] anIntArray4035 = new int[]{1, 1, 0, 0, 0, 8, 0, 0, 8}; - static short aShort4038 = 32767; - static int anInt4039 = 0; - static int anInt4041 = -1; - - - final int[][] method166(int var2) { - try { - int[][] var3 = this.aClass97_2376.method1594((byte)-119, var2); - if(this.aClass97_2376.aBoolean1379 && this.method279(-128)) { - int[] var4 = var3[0]; - int var7 = var2 % this.anInt3283 * this.anInt3283; - int[] var5 = var3[1]; - int[] var6 = var3[2]; - - for(int var8 = 0; Class113.anInt1559 > var8; ++var8) { - int var9 = this.anIntArray3284[var7 + var8 % this.anInt3280]; - var6[var8] = Unsorted.bitwiseAnd(255, var9) << 4; - var5[var8] = Unsorted.bitwiseAnd(var9 >> 4, 4080); - var4[var8] = Unsorted.bitwiseAnd(16711680, var9) >> 12; - } - } - - if(-1 != -1) { - anInt4039 = -119; - } - - return var3; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "kd.T(" + -1 + ',' + var2 + ')'); - } - } - - static void method282(int[] var0, int var1, int var2, int var3, int var4) { - try { - --var1; - --var3; - - for(int var6 = -7 + var3; var6 > var1; var0[var1] = var4) { - ++var1; - var0[var1] = var4; - ++var1; - var0[var1] = var4; - ++var1; - var0[var1] = var4; - ++var1; - var0[var1] = var4; - ++var1; - var0[var1] = var4; - ++var1; - var0[var1] = var4; - ++var1; - var0[var1] = var4; - ++var1; - } - - while(var3 > var1) { - ++var1; - var0[var1] = var4; - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "kd.B(" + "null" + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method284(float[][] var0, int[][] var1, int var2, float[][] var3, int var4, int[] var5, Class37 var6, byte var7, Class43 var8, float[][] var9, int var10) { - try { - int[] var11 = new int[var5.length / 2]; - - int var12; - for(var12 = 0; var11.length > var12; ++var12) { - int var13 = var5[var12 + var12]; - int var14 = var5[var12 + var12 + 1]; - int var15; - if(var10 == 1) { - var15 = var13; - var13 = var14; - var14 = -var15 + 128; - } else if (var10 == 2) { - var14 = -var14 + 128; - var13 = -var13 + 128; - } else if (var10 == 3) { - var15 = var13; - var13 = 128 - var14; - var14 = var15; - } - - float var17; - float var16; - float var24; - if(var13 == 0 && var14 == 0) { - var16 = var3[var2][var4]; - var24 = var9[var2][var4]; - var17 = var0[var2][var4]; - } else if(var13 == 128 && var14 == 0) { - var17 = var0[var2 - -1][var4]; - var24 = var9[1 + var2][var4]; - var16 = var3[var2 - -1][var4]; - } else if(128 == var13 && var14 == 128) { - var16 = var3[var2 + 1][var4 + 1]; - var24 = var9[var2 + 1][var4 + 1]; - var17 = var0[var2 - -1][var4 + 1]; - } else if(var13 == 0 && 128 == var14) { - var17 = var0[var2][1 + var4]; - var16 = var3[var2][var4 + 1]; - var24 = var9[var2][1 + var4]; - } else { - var24 = var9[var2][var4]; - var17 = var0[var2][var4]; - float var18 = (float)var13 / 128.0F; - var16 = var3[var2][var4]; - var16 += (-var16 + var3[1 + var2][var4]) * var18; - var17 += var18 * (var0[1 + var2][var4] - var17); - var24 += (-var24 + var9[var2 - -1][var4]) * var18; - float var20 = var9[var2][1 + var4]; - var20 += (var9[var2 - -1][var4 - -1] - var20) * var18; - float var21 = var3[var2][1 + var4]; - float var19 = (float)var14 / 128.0F; - var24 += (-var24 + var20) * var19; - float var22 = var0[var2][1 + var4]; - var22 += (var0[1 + var2][var4 + 1] - var22) * var18; - var21 += (-var21 + var3[var2 + 1][1 + var4]) * var18; - var16 += (-var16 + var21) * var19; - var17 += (var22 - var17) * var19; - } - - int var26 = (var2 << 7) + var13; - int var25 = (var4 << 7) - -var14; - int var27 = Class3_Sub23.method408(var13, (byte)-53, var4, var1, var2, var14); - var11[var12] = var6.method1018(var8, var26, var27, var25, var24, var16, var17); - } - - var6.method1022(var11); - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "kd.Q(" + (var0 != null?"{...}":"null") + ',' + (var1 != null?"{...}":"null") + ',' + var2 + ',' + (var3 != null?"{...}":"null") + ',' + var4 + ',' + (var5 != null?"{...}":"null") + ',' + (var6 != null?"{...}":"null") + ',' + var7 + ',' + (var8 != null?"{...}":"null") + ',' + (var9 != null?"{...}":"null") + ',' + var10 + ')'); - } - } - - static Class158 method285(int var0, int var1, Component var3) { - try { - try { - Class var4 = (Class) Class.forName(GameConfig.PACKAGE_NAME + ".Class158_Sub2"); - Class158 var8 = var4.newInstance(); - var8.method2185(var0, var1, var3); - return var8; - } catch (Throwable var6) { - Class158_Sub1 var5 = new Class158_Sub1(); - var5.method2185(var0, var1, var3); - return var5; - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "kd.C(" + var0 + ',' + var1 + ',' + true + ',' + (var3 != null?"{...}":"null") + ')'); - } - } - - static AbstractSprite[] method286(int var2, CacheIndex var3) { - try { - //System.out.println("Class3_Sub13_Sub_23_Sub1 " + var2); - return !Class75_Sub4.method1351(var3, 0, var2)?null: AudioThread.method891(~4); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "kd.F(" + -1 + ',' + 0 + ',' + var2 + ',' + (var3 != null?"{...}":"null") + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation19.java b/Client/src/main/java/org/runite/client/TextureOperation19.java deleted file mode 100644 index 62d1a8738..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation19.java +++ /dev/null @@ -1,290 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -import java.util.Objects; - -final class TextureOperation19 extends TextureOperation { - - static CacheIndex aClass153_3214; - static int[][] anIntArrayArray3215 = new int[][]{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1}, {1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1}, {0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0}, {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1}, {1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1}}; - private int anInt3217 = 32768; - static int[] anIntArray3218 = new int[]{1, 4}; - - - static void method254(boolean var0, Class3_Sub31 var1) { - try { - int var4 = (int)var1.linkableKey; - - int var3 = var1.anInt2602; - var1.unlink(); - if(var0) { - Class60.method1208((byte)79, var3); - } - - Class164_Sub2.method2249(var3); - RSInterface var5 = Unsorted.getRSInterface(var4); - if(null != var5) { - Class20.method909(var5); - } - - int var6 = Unsorted.menuOptionCount; - - int var7; - for(var7 = 0; var6 > var7; ++var7) { - if(Unsorted.method73(TextureOperation27.aShortArray3095[var7])) { - Class3_Sub25.method509(var7); - } - } - - if(Unsorted.menuOptionCount == 1) { - Class38_Sub1.aBoolean2615 = false; - Class21.method1340(Class21.anInt1462, Class21.anInt3552, Class21.anInt3395, Class21.anInt3537); - } else { - Class21.method1340(Class21.anInt1462, Class21.anInt3552, Class21.anInt3395, Class21.anInt3537); - var7 = FontType.bold.method682(RSString.parse(GameConfig.RCM_TITLE)); - - for(int var8 = 0; Unsorted.menuOptionCount > var8; ++var8) { - int var9 = FontType.bold.method682(Unsorted.method802(var8)); - if(var7 < var9) { - var7 = var9; - } - } - - Class21.anInt3537 = Unsorted.menuOptionCount * 15 + (Unsorted.aBoolean1951?26:22); - Class21.anInt3552 = var7 + 8; - } - - if(-1 != ConfigInventoryDefinition.anInt3655) { - Class3_Sub8.method124(115, 1, ConfigInventoryDefinition.anInt3655); - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "ke.O(" + var0 + ',' + (var1 != null?"{...}":"null") + ',' + false + ')'); - } - } - - static void method255(int var0, int var1, int var2) { - try { - InterfaceWidget var3 = InterfaceWidget.getWidget(var2, var0); - var3.flagUpdate(); - var3.anInt3598 = var1; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ke.Q(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation19() { - super(3, false); - } - - final int[][] method166(int var2) { - try { - if(-1 != -1) { - aClass153_3214 = null; - } - - int[][] var3 = this.aClass97_2376.method1594((byte)4, var2); - if(this.aClass97_2376.aBoolean1379) { - int[] var4 = this.method152(1, var2); - int[] var5 = this.method152(2, var2); - int[] var8 = var3[2]; - int[] var7 = var3[1]; - int[] var6 = var3[0]; - - for(int var9 = 0; Class113.anInt1559 > var9; ++var9) { - int var10 = (var4[var9] * 255 & 1046259) >> 12; - int var11 = var5[var9] * this.anInt3217 >> 12; - int var12 = var11 * Class75_Sub2.anIntArray2639[var10] >> 12; - int var13 = TextureOperation23.anIntArray3212[var10] * var11 >> 12; - int var14 = (var12 >> 12) + var9 & RenderAnimationDefinition.anInt396; - int var15 = Class3_Sub20.anInt2487 & var2 - -(var13 >> 12); - int[][] var16 = this.method162(var15, 0, (byte)-117); - var6[var9] = Objects.requireNonNull(var16)[0][var14]; - var7[var9] = var16[1][var14]; - var8[var9] = var16[2][var14]; - } - } - - return var3; - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "ke.T(" + -1 + ',' + var2 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var5 = this.method152(1, var1); - int[] var6 = this.method152(2, var1); - - for(int var7 = 0; var7 < Class113.anInt1559; ++var7) { - int var9 = this.anInt3217 * var6[var7] >> 12; - int var8 = (var5[var7] & 4087) >> 4; - int var10 = Class75_Sub2.anIntArray2639[var8] * var9 >> 12; - int var11 = TextureOperation23.anIntArray3212[var8] * var9 >> 12; - int var12 = RenderAnimationDefinition.anInt396 & (var10 >> 12) + var7; - int var13 = Class3_Sub20.anInt2487 & (var11 >> 12) + var1; - int[] var14 = this.method152(0, var13); - var4[var7] = var14[var12]; - } - } - - return var4; - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "ke.D(" + var1 + ',' + var2 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3217 = var2.readUnsignedShort() << 4; - } else if (var1 == 1) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - if(!true) { - TextureOperation20.anInt3216 = -7; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ke.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static void method257() { - try { - int var1 = 0; - - for(int var2 = 0; var2 < 104; ++var2) { - for(int var3 = 0; var3 < 104; ++var3) { - if(Class140_Sub7.method2031((byte)-106, true, var2, var3, Class75_Sub2.aClass3_Sub2ArrayArrayArray2638, var1)) { - ++var1; - } - - if(var1 >= 512) { - return; - } - } - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ke.C(" + (byte) 125 + ')'); - } - } - - static void method259(Class126 var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, boolean var8) { - int var9; - int var10 = var9 = (var6 << 7) - Class145.anInt2697; - int var11; - int var12 = var11 = (var7 << 7) - TextureOperation13.anInt3363; - int var13; - int var14 = var13 = var10 + 128; - int var15; - int var16 = var15 = var12 + 128; - int var17 = Class44.anIntArrayArrayArray723[var1][var6][var7] - Unsorted.anInt3657; - int var18 = Class44.anIntArrayArrayArray723[var1][var6 + 1][var7] - Unsorted.anInt3657; - int var19 = Class44.anIntArrayArrayArray723[var1][var6 + 1][var7 + 1] - Unsorted.anInt3657; - int var20 = Class44.anIntArrayArrayArray723[var1][var6][var7 + 1] - Unsorted.anInt3657; - int var21 = var12 * var4 + var10 * var5 >> 16; - var12 = var12 * var5 - var10 * var4 >> 16; - var10 = var21; - var21 = var17 * var3 - var12 * var2 >> 16; - var12 = var17 * var2 + var12 * var3 >> 16; - var17 = var21; - if(var12 >= 50) { - var21 = var11 * var4 + var14 * var5 >> 16; - var11 = var11 * var5 - var14 * var4 >> 16; - var14 = var21; - var21 = var18 * var3 - var11 * var2 >> 16; - var11 = var18 * var2 + var11 * var3 >> 16; - var18 = var21; - if(var11 >= 50) { - var21 = var16 * var4 + var13 * var5 >> 16; - var16 = var16 * var5 - var13 * var4 >> 16; - var13 = var21; - var21 = var19 * var3 - var16 * var2 >> 16; - var16 = var19 * var2 + var16 * var3 >> 16; - var19 = var21; - if(var16 >= 50) { - var21 = var15 * var4 + var9 * var5 >> 16; - var15 = var15 * var5 - var9 * var4 >> 16; - var9 = var21; - var21 = var20 * var3 - var15 * var2 >> 16; - var15 = var20 * var2 + var15 * var3 >> 16; - if(var15 >= 50) { - int var22 = Class51.anInt846 + (var10 << 9) / var12; - int var23 = Class51.anInt835 + (var17 << 9) / var12; - int var24 = Class51.anInt846 + (var14 << 9) / var11; - int var25 = Class51.anInt835 + (var18 << 9) / var11; - int var26 = Class51.anInt846 + (var13 << 9) / var16; - int var27 = Class51.anInt835 + (var19 << 9) / var16; - int var28 = Class51.anInt846 + (var9 << 9) / var15; - int var29 = Class51.anInt835 + (var21 << 9) / var15; - Class51.anInt850 = 0; - int var30; - if((var26 - var28) * (var25 - var29) - (var27 - var29) * (var24 - var28) > 0) { - if(TextureOperation37.aBoolean3261 && TextureOperation34.method185(Class49.anInt819 + Class51.anInt846, TextureOperation18.anInt4039 + Class51.anInt835, var27, var29, var25, var26, var28, var24)) { - Class27.anInt515 = var6; - Unsorted.anInt999 = var7; - } - - if(!HDToolKit.highDetail && !var8) { - Class51.aBoolean849 = var26 < 0 || var28 < 0 || var24 < 0 || var26 > Class51.anInt847 || var28 > Class51.anInt847 || var24 > Class51.anInt847; - - if(var0.anInt1670 == -1) { - if(var0.anInt1664 != 12345678) { - Class51.method1154(var27, var29, var25, var26, var28, var24, var0.anInt1664, var0.anInt1663, var0.anInt1667); - } - } else if(Unsorted.aBoolean3275) { - if(var0.aBoolean1674) { - Class51.method1135(var27, var29, var25, var26, var28, var24, var0.anInt1664, var0.anInt1663, var0.anInt1667, var10, var14, var9, var17, var18, var21, var12, var11, var15, var0.anInt1670); - } else { - Class51.method1135(var27, var29, var25, var26, var28, var24, var0.anInt1664, var0.anInt1663, var0.anInt1667, var13, var9, var14, var19, var21, var18, var16, var15, var11, var0.anInt1670); - } - } else { - var30 = Class51.anInterface2_838.method15(var0.anInt1670, 65535); - Class51.method1154(var27, var29, var25, var26, var28, var24, LoginHandler.method1753(var30, var0.anInt1664), LoginHandler.method1753(var30, var0.anInt1663), LoginHandler.method1753(var30, var0.anInt1667)); - } - } - } - - if((var22 - var24) * (var29 - var25) - (var23 - var25) * (var28 - var24) > 0) { - if(TextureOperation37.aBoolean3261 && TextureOperation34.method185(Class49.anInt819 + Class51.anInt846, TextureOperation18.anInt4039 + Class51.anInt835, var23, var25, var29, var22, var24, var28)) { - Class27.anInt515 = var6; - Unsorted.anInt999 = var7; - } - - if(!HDToolKit.highDetail && !var8) { - Class51.aBoolean849 = var22 < 0 || var24 < 0 || var28 < 0 || var22 > Class51.anInt847 || var24 > Class51.anInt847 || var28 > Class51.anInt847; - - if(var0.anInt1670 == -1) { - if(var0.anInt1675 != 12345678) { - Class51.method1154(var23, var25, var29, var22, var24, var28, var0.anInt1675, var0.anInt1667, var0.anInt1663); - } - } else if(Unsorted.aBoolean3275) { - Class51.method1135(var23, var25, var29, var22, var24, var28, var0.anInt1675, var0.anInt1667, var0.anInt1663, var10, var14, var9, var17, var18, var21, var12, var11, var15, var0.anInt1670); - } else { - var30 = Class51.anInterface2_838.method15(var0.anInt1670, 65535); - Class51.method1154(var23, var25, var29, var22, var24, var28, LoginHandler.method1753(var30, var0.anInt1675), LoginHandler.method1753(var30, var0.anInt1667), LoginHandler.method1753(var30, var0.anInt1663)); - } - } - } - - } - } - } - } - } - - final void postDecode() { - try { - Class8.method844((byte)-9); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ke.P(" + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation2.java b/Client/src/main/java/org/runite/client/TextureOperation2.java deleted file mode 100644 index 18236bd0b..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation2.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -final class TextureOperation2 extends TextureOperation { - - static ReferenceCache aReferenceCache_3369 = new ReferenceCache(64); - static AbstractSprite[] aAbstractSpriteArray3373; - - final int[] method154(int var1, byte var2) { - try { - return Class102.anIntArray2125; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "rl.D(" + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation2() { - super(0, true); - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation20.java b/Client/src/main/java/org/runite/client/TextureOperation20.java deleted file mode 100644 index 1f05b2e16..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation20.java +++ /dev/null @@ -1,939 +0,0 @@ -package org.runite.client; -import org.rs09.client.filestore.resources.configs.cursors.CursorDefinition; - -import java.awt.Point; -import java.io.IOException; -import java.util.Objects; - -final class TextureOperation20 extends TextureOperation { - - static int anInt2212 = 0; - static int anInt2217 = 2; - static int anInt3216 = 0; - static long aLong1465 = 0L; - static Class67 aClass67_1443; - static int anInt1682 = 1; - static int anInt2701 = 0; - static boolean aBoolean2774 = true; - static int anInt1977 = 0; - private int anInt3147 = 4; - static int paramModeWhat = 0; - private int anInt3149 = 4; - static CacheIndex aClass153_3154; - static int anInt3156 = -1; - - - static void method229(int cursor) { - try { - if(!Class163_Sub3.aBoolean3004) { - cursor = -1; - } - - if(cursor != Class65.anInt991) { - if(cursor != -1) { - CursorDefinition cursorDef = TextureOperation3.method311(cursor); - SoftwareSprite image = cursorDef.getImage(); - if(image == null) { - cursor = -1; - } else { - Class38.gameSignlink.method1434(image.method655(), 10000, image.anInt3697, GameShell.canvas, new Point(cursorDef.getHotspotX(), cursorDef.getHotspotY()), image.anInt3706); - Class65.anInt991 = cursor; - } - } - - if(cursor == -1 && Class65.anInt991 != -1) { - Class38.gameSignlink.method1434(null, 10000, -1, GameShell.canvas, new Point(), -1); - Class65.anInt991 = -1; - } - - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "gg.C(" + cursor + ',' + 20827 + ')'); - } - } - - public TextureOperation20() { - super(1, false); - } - - static void breakClientConnection() { - try { - - if(Class159.anInt2023 > 0) { - Class167.method2269((byte)46); - } else { - Class163_Sub2_Sub1.aClass89_4012 = Class3_Sub15.activeConnection; - Class3_Sub15.activeConnection = null; - Class117.method1719(40); - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "nm.B(" + false + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(!true) { - paramModeWhat = -117; - } - - if(var1 == 0) { - this.anInt3149 = var2.readUnsignedByte(); - } else if(1 == var1) { - this.anInt3147 = var2.readUnsignedByte(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "gg.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static void method230(int[][] var0) { - try { - Class38.anIntArrayArray663 = var0; - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "gg.Q(" + (var0 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static Class24 method231(int var0) { - try { - Class24 var2 = (Class24)Class140_Sub4.aReferenceCache_2792.get(var0); - if(var2 == null) { - byte[] var3 = LoginHandler.aClass153_1680.getFile(3, var0); - var2 = new Class24(); - if(null != var3) { - var2.method952(new DataBuffer(var3)); - } - - Class140_Sub4.aReferenceCache_2792.put(var2, var0); - - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "gg.B(" + var0 + ',' + 0 + ')'); - } - } - - static void method232(int var0) { - try { - if(Unsorted.loadInterface(var0)) { - RSInterface[] var2 = GameObject.aClass11ArrayArray1834[var0]; - - for(int var3 = 0; var3 < var2.length; ++var3) { - RSInterface var4 = var2[var3]; - if(null != var4) { - var4.anInt260 = 1; - var4.anInt283 = 0; - var4.anInt267 = 0; - } - } - - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "gg.E(" + var0 + ',' + 16182 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var10 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var5 = Class113.anInt1559 / this.anInt3149; - int var6 = Class101.anInt1427 / this.anInt3147; - int[] var4; - int var7; - if(var6 <= 0) { - var4 = this.method152(0, 0); - } else { - var7 = var1 % var6; - var4 = this.method152(0, Class101.anInt1427 * var7 / var6); - } - - for(var7 = 0; var7 < Class113.anInt1559; ++var7) { - if(0 >= var5) { - var10[var7] = var4[0]; - } else { - int var8 = var7 % var5; - var10[var7] = var4[Class113.anInt1559 * var8 / var5]; - } - } - } - - return var10; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "gg.D(" + var1 + ',' + var2 + ')'); - } - } - - static void method233(int var0, CacheIndex var1) { - try { - if(var0 != 28280) { - aClass153_3154 = null; - } - - NPC.anInt4001 = var1.getArchiveForName(TextCore.aClass94_119); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "gg.R(" + var0 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 == -1) { - int[][] var3 = this.aClass97_2376.method1594((byte)-123, var2); - if(this.aClass97_2376.aBoolean1379) { - int var5 = Class113.anInt1559 / this.anInt3149; - int var6 = Class101.anInt1427 / this.anInt3147; - int[][] var4; - if(var6 > 0) { - int var7 = var2 % var6; - var4 = this.method162(var7 * Class101.anInt1427 / var6, 0, (byte)-109); - } else { - var4 = this.method162(0, 0, (byte)-120); - } - - int[] var17 = Objects.requireNonNull(var4)[0]; - int[] var9 = var4[2]; - int[] var10 = var3[0]; - int[] var8 = var4[1]; - int[] var11 = var3[1]; - int[] var12 = var3[2]; - - for(int var13 = 0; Class113.anInt1559 > var13; ++var13) { - int var14; - if(var5 <= 0) { - var14 = 0; - } else { - int var15 = var13 % var5; - var14 = var15 * Class113.anInt1559 / var5; - } - - var10[var13] = var17[var14]; - var11[var13] = var8[var14]; - var12[var13] = var9[var14]; - } - } - - return var3; - } else { - return null; - } - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "gg.T(" + -1 + ',' + var2 + ')'); - } - } - - static void method235() { - try { - if(Class159.anInt2023 > 0) { - --Class159.anInt2023; - } - - if(Class38_Sub1.anInt2617 > 1) { - --Class38_Sub1.anInt2617; - Class140_Sub6.anInt2905 = PacketParser.anInt3213; - } - - if(Class3_Sub28_Sub18.aBoolean3769) { - Class3_Sub28_Sub18.aBoolean3769 = false; - breakClientConnection(); - } else { - int queuedVarpIndex; - for(queuedVarpIndex = 0; queuedVarpIndex < 100 && TextureOperation33.method181(); ++queuedVarpIndex) { - } - - if(Class143.gameStage == 30) { - Class163_Sub2_Sub1.method2226(TextureOperation12.outgoingBuffer, 163, -116); - Object var14 = aClass67_1443.anObject1016; - int var2; - int var3; - int var4; - int var5; - int var6; - int var8; - int var9; - synchronized(var14) { - if(Unsorted.aBoolean29) { - if(Unsorted.anInt3644 != 0 || aClass67_1443.anInt1018 >= 40) { - TextureOperation12.outgoingBuffer.putOpcode(123); - var3 = 0; - TextureOperation12.outgoingBuffer.writeByte(0); - var2 = TextureOperation12.outgoingBuffer.index; - - for(var4 = 0; aClass67_1443.anInt1018 > var4 && TextureOperation12.outgoingBuffer.index - var2 < 240; ++var4) { - ++var3; - var5 = aClass67_1443.anIntArray1019[var4]; - var6 = aClass67_1443.anIntArray1020[var4]; - if(var5 < 0) { - var5 = 0; - } else if(var5 > 65534) { - var5 = '\ufffe'; - } - - if(var6 >= 0) { - if('\ufffe' < var6) { - var6 = '\ufffe'; - } - } else { - var6 = 0; - } - - boolean var7 = false; - if(aClass67_1443.anIntArray1019[var4] == -1 && aClass67_1443.anIntArray1020[var4] == -1) { - var7 = true; - var5 = -1; - var6 = -1; - } - - if(anInt1977 == var6 && var5 == Unsorted.anInt14) { - if(2047 > Class3_Sub26.anInt2556) { - ++Class3_Sub26.anInt2556; - } - } else { - var8 = -anInt1977 + var6; - anInt1977 = var6; - var9 = var5 + -Unsorted.anInt14; - Unsorted.anInt14 = var5; - if(Class3_Sub26.anInt2556 < 8 && var8 >= -32 && 31 >= var8 && -32 <= var9 && var9 <= 31) { - var9 += 32; - var8 += 32; - TextureOperation12.outgoingBuffer.writeShort(var9 + (Class3_Sub26.anInt2556 << 12) + (var8 << 6)); - Class3_Sub26.anInt2556 = 0; - } else if(Class3_Sub26.anInt2556 < 32 && var8 >= -128 && var8 <= 127 && var9 >= -128 && var9 <= 127) { - TextureOperation12.outgoingBuffer.writeByte(128 - -Class3_Sub26.anInt2556); - var9 += 128; - var8 += 128; - TextureOperation12.outgoingBuffer.writeShort((var8 << 8) + var9); - Class3_Sub26.anInt2556 = 0; - } else if(32 > Class3_Sub26.anInt2556) { - TextureOperation12.outgoingBuffer.writeByte(192 - -Class3_Sub26.anInt2556); - if(var7) { - TextureOperation12.outgoingBuffer.writeInt(Integer.MIN_VALUE); - } else { - TextureOperation12.outgoingBuffer.writeInt(var6 | var5 << 16); - } - - Class3_Sub26.anInt2556 = 0; - } else { - TextureOperation12.outgoingBuffer.writeShort(Class3_Sub26.anInt2556 + '\ue000'); - if(var7) { - TextureOperation12.outgoingBuffer.writeInt(Integer.MIN_VALUE); - } else { - TextureOperation12.outgoingBuffer.writeInt(var6 | var5 << 16); - } - - Class3_Sub26.anInt2556 = 0; - } - } - } - - TextureOperation12.outgoingBuffer.method769(-var2 + TextureOperation12.outgoingBuffer.index); - if(var3 < aClass67_1443.anInt1018) { - aClass67_1443.anInt1018 -= var3; - - for(var4 = 0; aClass67_1443.anInt1018 > var4; ++var4) { - aClass67_1443.anIntArray1020[var4] = aClass67_1443.anIntArray1020[var3 + var4]; - aClass67_1443.anIntArray1019[var4] = aClass67_1443.anIntArray1019[var4 + var3]; - } - } else { - aClass67_1443.anInt1018 = 0; - } - } - } else { - aClass67_1443.anInt1018 = 0; - } - } - - if(Unsorted.anInt3644 != 0) { - long var15 = (-aLong1465 + Class75.aLong1102) / 50L; - var3 = Class38_Sub1.anInt2614; - if(var3 >= 0) { - if(var3 > 65535) { - var3 = 65535; - } - } else { - var3 = 0; - } - - if(32767L < var15) { - var15 = 32767L; - } - - var4 = Class163_Sub1.anInt2993; - aLong1465 = Class75.aLong1102; - byte var19 = 0; - if(var4 >= 0) { - if(var4 > 65535) { - var4 = 65535; - } - } else { - var4 = 0; - } - - var6 = (int)var15; - if(Unsorted.anInt3644 == 2) { - var19 = 1; - } - - TextureOperation12.outgoingBuffer.putOpcode(75); - TextureOperation12.outgoingBuffer.writeShort128LE(var19 << 15 | var6); - TextureOperation12.outgoingBuffer.writeIntV2(var4 | var3 << 16); - } - - if(0 < anInt2212) { - --anInt2212; - } - - if(AudioThread.aBoolean346) { - for(queuedVarpIndex = 0; Class3_Sub23.anInt2537 > queuedVarpIndex; ++queuedVarpIndex) { - var2 = Class133.inputTextCodeArray[queuedVarpIndex]; - if(98 == var2 || var2 == 99 || var2 == 96 || var2 == 97) { - Unsorted.aBoolean4068 = true; - break; - } - } - } else if(ObjectDefinition.aBooleanArray1490[96] || ObjectDefinition.aBooleanArray1490[97] || ObjectDefinition.aBooleanArray1490[98] || ObjectDefinition.aBooleanArray1490[99]) { - Unsorted.aBoolean4068 = true; - } - - if(Unsorted.aBoolean4068 && 0 >= anInt2212) { - anInt2212 = 20; - Unsorted.aBoolean4068 = false; - TextureOperation12.outgoingBuffer.putOpcode(21); - TextureOperation12.outgoingBuffer.putShortA(Unsorted.anInt2309); - TextureOperation12.outgoingBuffer.writeShortLE(GraphicDefinition.CAMERA_DIRECTION); - } - - if(TextureOperation26.aBoolean3078 && !aBoolean2774) { - aBoolean2774 = true; - TextureOperation12.outgoingBuffer.putOpcode(22); - TextureOperation12.outgoingBuffer.writeByte(1); - } - - if(!TextureOperation26.aBoolean3078 && aBoolean2774) { - aBoolean2774 = false; - TextureOperation12.outgoingBuffer.putOpcode(22); - TextureOperation12.outgoingBuffer.writeByte(0); - } - - if(!CS2Script.aBoolean2705) { - TextureOperation12.outgoingBuffer.putOpcode(98); - TextureOperation12.outgoingBuffer.writeInt(Class84.method1421()); - CS2Script.aBoolean2705 = true; - } - - Class163_Sub1_Sub1.method2214(); - if(Class143.gameStage == 30) { - MouseListeningClass.method2087(); - Class115.method1713(); - AudioHandler.method132(); - ++AbstractSprite.anInt3699; - if(AbstractSprite.anInt3699 > 750) { - breakClientConnection(); - } else { - PlayerRendering.updatePlayerAreaArray(); - NPCRendering.updateNPCAreaArray(); - TextureOperation34.method189(); - if(Class3_Sub28_Sub3.aClass11_3551 != null) { - Unsorted.method848(); - } - - for(queuedVarpIndex = Class3_Sub5.method115(true); queuedVarpIndex != -1; queuedVarpIndex = Class3_Sub5.method115(false)) { - Class46.method1087(40, queuedVarpIndex); - Class44.anIntArray726[Unsorted.bitwiseAnd(Class36.anInt641++, 31)] = queuedVarpIndex; - } - - int nodeModelID; - for(InterfaceWidget var16 = Unsorted.method1302(); var16 != null; var16 = Unsorted.method1302()) { - var3 = var16.e(); - var4 = var16.f(); - if(1 == var3) { - NPCDefinition.anIntArray1277[var4] = var16.anInt3598; - NPC.anIntArray3986[Unsorted.bitwiseAnd(31, PacketParser.anInt87++)] = var4; - } else if(var3 == 2) { - Class132.aClass94Array1739[var4] = var16.text; - Class163_Sub2_Sub1.anIntArray4025[Unsorted.bitwiseAnd(31, Client.anInt2317++)] = var4; - } else { - RSInterface var20; - if(var3 == 3) { - var20 = Unsorted.getRSInterface(var4); - if(!var16.text.equalsString(Objects.requireNonNull(var20).text)) { - var20.text = var16.text; - Class20.method909(var20); - } - } else if (var3 == 4) { - var20 = Unsorted.getRSInterface(var4); - var6 = var16.anInt3598; - var8 = var16.anInt3596; - nodeModelID = var16.anInt3597; - if (Objects.requireNonNull(var20).modelType != var6 || nodeModelID != var20.itemId || var8 != var20.anInt265) { - var20.itemId = nodeModelID; - var20.anInt265 = var8; - var20.modelType = var6; - Class20.method909(var20); - } - } else if (var3 == 5) { - var20 = Unsorted.getRSInterface(var4); - if (var16.anInt3598 != Objects.requireNonNull(var20).animationId || var16.anInt3598 == -1) { - var20.anInt260 = 1; - var20.anInt267 = 0; - var20.animationId = var16.anInt3598; - var20.anInt283 = 0; - Class20.method909(var20); - } - } else if (var3 == 6) { - var5 = var16.anInt3598; - var6 = (32195 & var5) >> 10; - var8 = var5 & 31; - nodeModelID = (var5 & 1000) >> 5; - RSInterface var10 = Unsorted.getRSInterface(var4); - var9 = (var8 << 3) + (nodeModelID << 11) + (var6 << 19); - if (Objects.requireNonNull(var10).anInt218 != var9) { - var10.anInt218 = var9; - Class20.method909(var10); - } - } else if (var3 == 7) { - var20 = Unsorted.getRSInterface(var4); - boolean var24 = var16.anInt3598 == 1; - if (var20 != null && var24 == !var20.hidden) { - var20.hidden = var24; - Class20.method909(var20); - } - } else if (var3 == 8) { - var20 = Unsorted.getRSInterface(var4); - if (var16.anInt3598 != Objects.requireNonNull(var20).anInt182 || var20.anInt308 != var16.anInt3597 || var20.anInt164 != var16.anInt3596) { - var20.anInt182 = var16.anInt3598; - var20.anInt164 = var16.anInt3596; - var20.anInt308 = var16.anInt3597; - if (-1 != var20.anInt192) { - if (var20.anInt184 <= 0) { - if (var20.defWidth > 0) { - var20.anInt164 = 32 * var20.anInt164 / var20.defWidth; - } - } else { - var20.anInt164 = var20.anInt164 * 32 / var20.anInt184; - } - } - - Class20.method909(var20); - } - } else if (var3 == 9) { - var20 = Unsorted.getRSInterface(var4); - if (Objects.requireNonNull(var20).anInt192 != var16.anInt3598 || var20.anInt271 != var16.anInt3597) { - var20.anInt192 = var16.anInt3598; - var20.anInt271 = var16.anInt3597; - Class20.method909(var20); - } - } else if (var3 == 10) { - var20 = Unsorted.getRSInterface(var4); - if (var16.anInt3598 != Objects.requireNonNull(var20).anInt258 || var20.anInt264 != var16.anInt3597 || var20.anInt280 != var16.anInt3596) { - var20.anInt264 = var16.anInt3597; - var20.anInt280 = var16.anInt3596; - var20.anInt258 = var16.anInt3598; - Class20.method909(var20); - } - } else if (var3 == 11) { - var20 = Unsorted.getRSInterface(var4); - Objects.requireNonNull(var20).anInt306 = var20.defX = var16.anInt3598; - var20.horizontalPos = 0; - var20.verticalPos = 0; - var20.anInt210 = var20.defY = var16.anInt3597; - Class20.method909(var20); - } else if (var3 == 12) { - var20 = Unsorted.getRSInterface(var4); - var6 = var16.anInt3598; - if (null != var20 && 0 == var20.type) { - if (var6 > var20.anInt252 + -var20.height) { - var6 = var20.anInt252 + -var20.height; - } - - if (0 > var6) { - var6 = 0; - } - - if (var6 != var20.anInt208) { - var20.anInt208 = var6; - Class20.method909(var20); - } - } - } else if (var3 == 13) { - var20 = Unsorted.getRSInterface(var4); - Objects.requireNonNull(var20).anInt237 = var16.anInt3598; - } - } - } - - if(Class36.anInt638 != 0) { - Unsorted.anInt2958 += 20; - if(400 <= Unsorted.anInt2958) { - Class36.anInt638 = 0; - } - } - - ++Class106.anInt1446; - if(Unsorted.aClass11_1933 != null) { - ++BufferedDataStream.anInt2330; - if(15 <= BufferedDataStream.anInt2330) { - Class20.method909(Unsorted.aClass11_1933); - Unsorted.aClass11_1933 = null; - } - } - - RSInterface var17; - if(Class67.aClass11_1017 != null) { - Class20.method909(Class67.aClass11_1017); - if(Class126.anInt1676 > 5 + Class129_Sub1.anInt2693 || Class126.anInt1676 < -5 + Class129_Sub1.anInt2693 || Unsorted.anInt1709 > Unsorted.anInt40 + 5 || -5 + Unsorted.anInt40 > Unsorted.anInt1709) { - Class72.aBoolean1074 = true; - } - - ++Class40.anInt677; - if(0 == TextureOperation21.anInt3069) { - if(Class72.aBoolean1074 && 5 <= Class40.anInt677) { - if(Class67.aClass11_1017 == Class99.aClass11_1402 && PacketParser.anInt86 != anInt2701) { - var17 = Class67.aClass11_1017; - byte var18 = 0; - if(1 == Unsorted.anInt15 && 206 == var17.anInt189) { - var18 = 1; - } - - if(var17.itemAmounts[anInt2701] <= 0) { - var18 = 0; - } - - if(Client.method44(var17).method93()) { - var5 = PacketParser.anInt86; - var6 = anInt2701; - var17.itemAmounts[var6] = var17.itemAmounts[var5]; - var17.itemIds[var6] = var17.itemIds[var5]; - var17.itemAmounts[var5] = -1; - var17.itemIds[var5] = 0; - } else if (var18 == 1) { - var6 = anInt2701; - var5 = PacketParser.anInt86; - - while (var6 != var5) { - if (var5 > var6) { - var17.method864(-1 + var5, var5); - --var5; - } else { - var17.method864(1 + var5, var5); - ++var5; - } - } - } else { - var17.method864(anInt2701, PacketParser.anInt86); - } - - TextureOperation12.outgoingBuffer.putOpcode(231); - TextureOperation12.outgoingBuffer.writeShort(PacketParser.anInt86); - TextureOperation12.outgoingBuffer.writeIntLE2(Class67.aClass11_1017.componentHash); - TextureOperation12.outgoingBuffer.putShortA(anInt2701); - TextureOperation12.outgoingBuffer.write128Byte(var18); - } - } else if((Unsorted.anInt998 == 1 || TextureOperation8.method353(-1 + Unsorted.menuOptionCount, 0)) && Unsorted.menuOptionCount > 2) { - Class132.method1801(); - } else if(Unsorted.menuOptionCount > 0) { - TextureOperation9.method203(56); - } - - Unsorted.anInt3644 = 0; - BufferedDataStream.anInt2330 = 10; - Class67.aClass11_1017 = null; - } - } - - Class85.aBoolean1167 = false; - Class27.aClass11_526 = null; - Class21.aBoolean440 = false; - Class3_Sub23.anInt2537 = 0; - var17 = Class107.aClass11_1453; - Class107.aClass11_1453 = null; - RSInterface var21 = Class20.aClass11_439; - - for(Class20.aClass11_439 = null; Unsorted.method591(72) && 128 > Class3_Sub23.anInt2537; ++Class3_Sub23.anInt2537) { - Class133.inputTextCodeArray[Class3_Sub23.anInt2537] = Class3_Sub28_Sub9.anInt3624; - Class120.anIntArray1638[Class3_Sub23.anInt2537] = TextureOperation7.anInt3342; - } - - Class3_Sub28_Sub3.aClass11_3551 = null; - if(ConfigInventoryDefinition.anInt3655 != -1) { - GraphicDefinition.method967(0, 0, 0, Class23.canvasWidth, ConfigInventoryDefinition.anInt3655, 0, Class140_Sub7.canvasHeight); - } - - ++PacketParser.anInt3213; - - while(true) { - CS2Script var26 = (CS2Script)PacketParser.aLinkedList_82.method1220(); - RSInterface var23; - RSInterface var25; - if(var26 == null) { - while(true) { - var26 = (CS2Script)Class65.aLinkedList_983.method1220(); - if(var26 == null) { - while(true) { - var26 = (CS2Script) Client.aLinkedList_1471.method1220(); - if(var26 == null) { - if(Class3_Sub28_Sub3.aClass11_3551 == null) { - Class3_Sub19.anInt2475 = 0; - } - - if(Class56.aClass11_886 != null) { - PacketParser.method829(); - } - - if(Player.rights > 0 && ObjectDefinition.aBooleanArray1490[82] && ObjectDefinition.aBooleanArray1490[81] && 0 != Class29.anInt561) { - var5 = WorldListCountry.localPlane - Class29.anInt561; - if(0 > var5) { - var5 = 0; - } else if(var5 > 3) { - var5 = 3; - } - - Class30.method979(Class102.player.anIntArray2767[0] + Class131.anInt1716, Class102.player.anIntArray2755[0] + Texture.anInt1152, var5); - } - - if(Player.rights > 0 && ObjectDefinition.aBooleanArray1490[82] && ObjectDefinition.aBooleanArray1490[81]) { - if(-1 != Class27.anInt515) { - Class30.method979(Class131.anInt1716 + Class27.anInt515, Texture.anInt1152 - -Unsorted.anInt999, WorldListCountry.localPlane); - } - - ObjectDefinition.anInt1521 = 0; - CS2Script.anInt2440 = 0; - } else if(CS2Script.anInt2440 == 2) { - if(Class27.anInt515 != -1) { - TextureOperation12.outgoingBuffer.putOpcode(131); - TextureOperation12.outgoingBuffer.writeIntV2(BufferedDataStream.anInt872); - TextureOperation12.outgoingBuffer.putShortA(Class131.anInt1716 + Class27.anInt515); - TextureOperation12.outgoingBuffer.writeShort128LE(RSInterface.anInt278); - TextureOperation12.outgoingBuffer.putShortA(Unsorted.anInt999 + Texture.anInt1152); - Class36.anInt638 = 1; - Unsorted.anInt2958 = 0; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Class70.anInt1053 = Class163_Sub1.anInt2993; - } - - CS2Script.anInt2440 = 0; - } else if(2 == ObjectDefinition.anInt1521) { - if(-1 != Class27.anInt515) { - TextureOperation12.outgoingBuffer.putOpcode(179); - TextureOperation12.outgoingBuffer.writeShort(Texture.anInt1152 + Unsorted.anInt999); - TextureOperation12.outgoingBuffer.writeShort(Class27.anInt515 + Class131.anInt1716); - Unsorted.anInt2958 = 0; - Class36.anInt638 = 1; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - } - - ObjectDefinition.anInt1521 = 0; - } else if(-1 != Class27.anInt515 && 0 == CS2Script.anInt2440 && ObjectDefinition.anInt1521 == 0) { - boolean var27 = Class3_Sub28_Sub9.method582(Class102.player.anIntArray2755[0], 0, 0, true, 0, 2, Class27.anInt515, 0, 0, 0, Unsorted.anInt999, Class102.player.anIntArray2767[0]); - if(var27) { - Unsorted.anInt4062 = Class38_Sub1.anInt2614; - Unsorted.anInt2958 = 0; - Class70.anInt1053 = Class163_Sub1.anInt2993; - Class36.anInt638 = 1; - } - } - - Class27.anInt515 = -1; - Class163_Sub1.method2211(); - if(Class107.aClass11_1453 != var17) { - if(var17 != null) { - Class20.method909(var17); - } - - if(null != Class107.aClass11_1453) { - Class20.method909(Class107.aClass11_1453); - } - } - - if(var21 != Class20.aClass11_439 && Class75.anInt1109 == TextureOperation35.anInt3323) { - if(null != var21) { - Class20.method909(var21); - } - - if(null != Class20.aClass11_439) { - Class20.method909(Class20.aClass11_439); - } - } - - if(Class20.aClass11_439 == null) { - if(Class75.anInt1109 > 0) { - --Class75.anInt1109; - } - } else if(Class75.anInt1109 < TextureOperation35.anInt3323) { - ++Class75.anInt1109; - if(TextureOperation35.anInt3323 == Class75.anInt1109) { - Class20.method909(Class20.aClass11_439); - } - } - - if(Class133.anInt1753 == 1) { - Unsorted.method2086(); - } else if(Class133.anInt1753 == 2) { - CS2Script.method379(); - } else { - InterfaceWidget.d(65535); - } - - for(var5 = 0; var5 < 5; ++var5) { - ++Class163_Sub1_Sub1.anIntArray4009[var5]; - } - - var5 = Texture.method1406(); - var6 = TextureOperation32.method301((byte)-119); - if(var5 > 15000 && var6 > 15000) { - Class159.anInt2023 = 250; - Class23.method940(112, 14500); - TextureOperation12.outgoingBuffer.putOpcode(245); - } - - if(AudioThread.aClass64_351 != null && AudioThread.aClass64_351.anInt978 == 1) { - if(null != AudioThread.aClass64_351.anObject974) { - Class99.method1596(TextureOperation5.aClass94_3295, (byte)126, Unsorted.aBoolean2154); - } - - TextureOperation5.aClass94_3295 = null; - AudioThread.aClass64_351 = null; - Unsorted.aBoolean2154 = false; - } - - ++TextureOperation18.anInt4032; - ++Class43.anInt716; - ++ClientErrorException.anInt2120; - if(ClientErrorException.anInt2120 > 500) { - ClientErrorException.anInt2120 = 0; - nodeModelID = (int)(8.0D * Math.random()); - if((nodeModelID & 4) == 4) { - LinkableRSString.anInt2589 += anInt1682; - } - - if((nodeModelID & 2) == 2) { - Unsorted.anInt42 += anInt2217; - } - - if((nodeModelID & 1) == 1) { - anInt3216 += Class146.anInt1901; - } - } - - if(Class43.anInt716 > 500) { - Class43.anInt716 = 0; - nodeModelID = (int)(8.0D * Math.random()); - if((1 & nodeModelID) == 1) { - TextureOperation9.anInt3102 += Unsorted.anInt48; - } - - if((2 & nodeModelID) == 2) { - Class164_Sub2.anInt3020 += Unsorted.anInt25; - } - } - - if(anInt3216 < -50) { - Class146.anInt1901 = 2; - } - - if(TextureOperation9.anInt3102 < -60) { - Unsorted.anInt48 = 2; - } - - if(Class164_Sub2.anInt3020 < -20) { - Unsorted.anInt25 = 1; - } - - if(-55 > Unsorted.anInt42) { - anInt2217 = 2; - } - - if(Unsorted.anInt42 > 55) { - anInt2217 = -2; - } - - if(-40 > LinkableRSString.anInt2589) { - anInt1682 = 1; - } - - if(anInt3216 > 50) { - Class146.anInt1901 = -2; - } - - if(LinkableRSString.anInt2589 > 40) { - anInt1682 = -1; - } - - if(10 < Class164_Sub2.anInt3020) { - Unsorted.anInt25 = -1; - } - - if(60 < TextureOperation9.anInt3102) { - Unsorted.anInt48 = -2; - } - - if(TextureOperation18.anInt4032 > 50) { - TextureOperation12.outgoingBuffer.putOpcode(93); - } - - if(RenderAnimationDefinition.aBoolean402) { - Class38.method1029(); - RenderAnimationDefinition.aBoolean402 = false; - } - - try { - if(Class3_Sub15.activeConnection != null && TextureOperation12.outgoingBuffer.index > 0) { - Class3_Sub15.activeConnection.sendBytes(TextureOperation12.outgoingBuffer.buffer, TextureOperation12.outgoingBuffer.index); - TextureOperation18.anInt4032 = 0; - TextureOperation12.outgoingBuffer.index = 0; - } - } catch (IOException var11) { - breakClientConnection(); - } - - return; - } - - var25 = var26.aClass11_2449; - if(var25.anInt191 >= 0) { - var23 = Unsorted.getRSInterface(var25.parentId); - if(null == var23 || var23.aClass11Array262 == null || var23.aClass11Array262.length <= var25.anInt191 || var25 != var23.aClass11Array262[var25.anInt191]) { - continue; - } - } - - Class43.method1065(var26); - } - } - - var25 = var26.aClass11_2449; - if(var25.anInt191 >= 0) { - var23 = Unsorted.getRSInterface(var25.parentId); - if(var23 == null || null == var23.aClass11Array262 || var23.aClass11Array262.length <= var25.anInt191 || var23.aClass11Array262[var25.anInt191] != var25) { - continue; - } - } - - Class43.method1065(var26); - } - } - - var25 = var26.aClass11_2449; - if(var25.anInt191 >= 0) { - var23 = Unsorted.getRSInterface(var25.parentId); - if(var23 == null || var23.aClass11Array262 == null || var25.anInt191 >= var23.aClass11Array262.length || var23.aClass11Array262[var25.anInt191] != var25) { - continue; - } - } - - Class43.method1065(var26); - } - } - } - } - } - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "gg.F(" + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation21.java b/Client/src/main/java/org/runite/client/TextureOperation21.java deleted file mode 100644 index 5ee1f7e6e..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation21.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class TextureOperation21 extends TextureOperation { - - static int anInt3069 = 0; - - - final int[][] method166(int var2) { - try { - if(-1 != -1) { - anInt3069 = 67; - } - - int[][] var3 = this.aClass97_2376.method1594((byte)7, var2); - if(this.aClass97_2376.aBoolean1379) { - int[] var4 = this.method152(2, var2); - int[][] var5 = this.method162(var2, 0, (byte)-119); - int[][] var6 = this.method162(var2, 1, (byte)-107); - int[] var9 = var3[2]; - int[] var8 = var3[1]; - int[] var10 = Objects.requireNonNull(var5)[0]; - int[] var11 = var5[1]; - int[] var7 = var3[0]; - int[] var13 = Objects.requireNonNull(var6)[0]; - int[] var12 = var5[2]; - int[] var15 = var6[2]; - int[] var14 = var6[1]; - - for(int var16 = 0; var16 < Class113.anInt1559; ++var16) { - int var17 = var4[var16]; - if(var17 == 4096) { - var7[var16] = var10[var16]; - var8[var16] = var11[var16]; - var9[var16] = var12[var16]; - } else if (0 == var17) { - var7[var16] = var13[var16]; - var8[var16] = var14[var16]; - var9[var16] = var15[var16]; - } else { - int var18 = -var17 + 4096; - var7[var16] = var18 * var13[var16] + var17 * var10[var16] >> 12; - var8[var16] = var18 * var14[var16] + var11[var16] * var17 >> 12; - var9[var16] = var15[var16] * var18 + var12[var16] * var17 >> 12; - } - } - } - - return var3; - } catch (RuntimeException var19) { - throw ClientErrorException.clientError(var19, "bl.T(" + -1 + ',' + var2 + ')'); - } - } - - public TextureOperation21() { - super(3, false); - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bl.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static void method194(int var0, int var1, int var2, int var3, int var4, int var6, int var7) { - try { - if(var7 >= Class101.anInt1425 && Class3_Sub28_Sub18.anInt3765 >= var6 && var4 >= Class159.anInt2020 && var1 <= Class57.anInt902) { - TextureOperation4.method262(var3, var4, var1, var2, var0, var6, var7); - } else { - Class143.method2062(var6, var2, var1, var0, var3, var4, var7); - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "bl.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + 4096 + ',' + var6 + ',' + var7 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var5 = this.method152(0, var1); - int[] var6 = this.method152(1, var1); - int[] var7 = this.method152(2, var1); - - for(int var8 = 0; var8 < Class113.anInt1559; ++var8) { - int var9 = var7[var8]; - if(4096 == var9) { - var3[var8] = var5[var8]; - } else if(var9 == 0) { - var3[var8] = var6[var8]; - } else { - var3[var8] = var9 * var5[var8] - -((-var9 + 4096) * var6[var8]) >> 12; - } - } - } - - return var3; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "bl.D(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation22.java b/Client/src/main/java/org/runite/client/TextureOperation22.java deleted file mode 100644 index 355bc6e3f..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation22.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -public final class TextureOperation22 extends TextureOperation { - - static int anInt3419 = 0; - public static Class131 aClass131_3421; - - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var4 = this.method152(0, var1); - - for(int var5 = 0; var5 < Class113.anInt1559; ++var5) { - var3[var5] = 4096 - var4[var5]; - } - } - - return var3; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "tb.D(" + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation22() { - super(1, false); - } - - static int method335(int var0) { - try { - if(var0 != 16859) { - aClass131_3421 = null; - } - - return ClientCommands.shiftClickEnabled && ObjectDefinition.aBooleanArray1490[81] && 2 < Unsorted.menuOptionCount?Class114.anIntArray1578[-2 + Unsorted.menuOptionCount]:Class114.anIntArray1578[Unsorted.menuOptionCount - 1]; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "tb.C(" + var0 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(true) { - if(var1 == 0) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "tb.A(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 == -1) { - int[][] var3 = this.aClass97_2376.method1594((byte)-128, var2); - if(this.aClass97_2376.aBoolean1379) { - int[][] var4 = this.method162(var2, 0, (byte)-51); - int[] var7 = Objects.requireNonNull(var4)[2]; - int[] var5 = var4[0]; - int[] var6 = var4[1]; - int[] var8 = var3[0]; - int[] var9 = var3[1]; - int[] var10 = var3[2]; - - for(int var11 = 0; var11 < Class113.anInt1559; ++var11) { - var8[var11] = -var5[var11] + 4096; - var9[var11] = 4096 - var6[var11]; - var10[var11] = 4096 - var7[var11]; - } - } - - return var3; - } else { - return null; - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "tb.T(" + -1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation23.java b/Client/src/main/java/org/runite/client/TextureOperation23.java deleted file mode 100644 index 0ed2d3119..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation23.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.HashTable; - -import java.util.Objects; - -final class TextureOperation23 extends TextureOperation { - - static boolean aBoolean3207 = false; - static HashTable aHashTable_3208 = new HashTable<>(8); - static int[] anIntArray3212; - static int anInt1780; - - - public TextureOperation23() { - super(1, false); - } - - static void method247(byte var0) { - try { - if(Class140_Sub2.aBoolean2713) { - SequenceDefinition.aClass109_1856 = null; - Class140_Sub2.aBoolean2713 = false; - Class40.aAbstractSprite_680 = null; - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "je.F(" + var0 + ')'); - } - } - - private void method248(int var1, byte var2, int var3) { - try { - if(var2 > 80) { - int var4 = Class102.anIntArray2125[var3]; - int var5 = Class163_Sub3.anIntArray2999[var1]; - float var6 = (float)Math.atan2(var4 - 2048, var5 - 2048); - if((double)var6 >= -3.141592653589793D && -2.356194490192345D >= (double)var6) { - Class50.anInt828 = var1; - Class159.anInt2024 = var3; - } else if((double)var6 <= -1.5707963267948966D && -2.356194490192345D <= (double)var6) { - Class159.anInt2024 = var1; - Class50.anInt828 = var3; - } else if((double)var6 <= -0.7853981633974483D && (double)var6 >= -1.5707963267948966D) { - Class159.anInt2024 = -var1 + Class113.anInt1559; - Class50.anInt828 = var3; - } else if(0.0F >= var6 && (double)var6 >= -0.7853981633974483D) { - Class159.anInt2024 = var3; - Class50.anInt828 = Class101.anInt1427 - var1; - } else if(var6 >= 0.0F && (double)var6 <= 0.7853981633974483D) { - Class159.anInt2024 = -var3 + Class113.anInt1559; - Class50.anInt828 = -var1 + Class101.anInt1427; - } else if((double)var6 >= 0.7853981633974483D && (double)var6 <= 1.5707963267948966D) { - Class159.anInt2024 = -var1 + Class113.anInt1559; - Class50.anInt828 = -var3 + Class101.anInt1427; - } else if((double)var6 >= 1.5707963267948966D && 2.356194490192345D >= (double)var6) { - Class50.anInt828 = -var3 + Class101.anInt1427; - Class159.anInt2024 = var1; - } else if(2.356194490192345D <= (double)var6 && (double)var6 <= 3.141592653589793D) { - Class159.anInt2024 = -var3 + Class113.anInt1559; - Class50.anInt828 = var1; - } - - Class159.anInt2024 &= RenderAnimationDefinition.anInt396; - Class50.anInt828 &= Class3_Sub20.anInt2487; - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "je.Q(" + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - if(!true) { - WorldMap.aClass153_3210 = null; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "je.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static void method250(CacheIndex var1) { - try { - Class8.configurationReferenceCache = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "je.C(" + 2048 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - - static int method251() { - try { - if(Class119.aClass131_1624 == null) { - return -1; - } else { - while(Class119.aClass131_1624.anInt1720 > anInt1780) { - if(Class119.aClass131_1624.method1794(anInt1780, -20138)) { - return anInt1780++; - } - - ++anInt1780; - } - - return -1; - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "je.B(" + -1 + ')'); - } - } - - static void method252() { - try { - Class3_Sub9 var1; - for(var1 = (Class3_Sub9) Unsorted.aLinkedList_78.method1222(); null != var1; var1 = (Class3_Sub9) Unsorted.aLinkedList_78.method1221()) { - if(var1.aBoolean2329) { - var1.method134(); - } - } - - for(var1 = (Class3_Sub9) Unsorted.aLinkedList_1242.method1222(); null != var1; var1 = (Class3_Sub9) Unsorted.aLinkedList_1242.method1221()) { - if(var1.aBoolean2329) { - var1.method134(); - } - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "je.S(" + 8 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - for(int var5 = 0; var5 < Class113.anInt1559; ++var5) { - this.method248(var1, (byte)105, var5); - int[] var6 = this.method152(0, Class50.anInt828); - var4[var5] = var6[Class159.anInt2024]; - } - } - - return var4; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "je.D(" + var1 + ',' + var2 + ')'); - } - } - - static void method253(int var1, int var2, int var3, int var4) { - try { - - Class3_Sub28_Sub1.anInt3536 = Class23.anInt455 * var3 / var1; - Class3_Sub4.anInt2251 = Class108.anInt1460 * var2 / var4; - Texture.anInt1150 = -1; - TextureOperation13.anInt3362 = -1; - Class3_Sub5.method117(); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "je.E(" + -22611 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - final int[][] method166(int var2) { - try { - int[][] var3 = this.aClass97_2376.method1594((byte)-125, var2); - if(this.aClass97_2376.aBoolean1379) { - int[] var4 = var3[0]; - int[] var6 = var3[2]; - int[] var5 = var3[1]; - - for(int var7 = 0; Class113.anInt1559 > var7; ++var7) { - this.method248(var2, (byte)107, var7); - int[][] var8 = this.method162(Class50.anInt828, 0, (byte)-49); - var4[var7] = Objects.requireNonNull(var8)[0][Class159.anInt2024]; - var5[var7] = var8[1][Class159.anInt2024]; - var6[var7] = var8[2][Class159.anInt2024]; - } - } - - return var3; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "je.T(" + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation24.java b/Client/src/main/java/org/runite/client/TextureOperation24.java deleted file mode 100644 index 9b976a153..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation24.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.runite.client; - -import org.rs09.client.rendering.Toolkit; - -import java.util.Objects; -import java.util.zip.CRC32; - -final class TextureOperation24 extends TextureOperation { - - static CRC32 CRC32 = new CRC32(); - static int anInt3377 = 7759444;//Very light Brown 7759444 // #766654 - static int anInt2243 = 3353893;//Dark Brown 2 3353893 // #332D25 - static int anInt2530 = 2301979;//Dark Brown 2301979 // #34301B - static int anInt486 = 5063219;//Light Brown 5063219 // #4d4233 - - - static void method223(int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - try { - if (var7 == var4) { - Unsorted.method1460(var1, var3, var6, var7, var2, var5); - } else { - - if (Class101.anInt1425 <= var2 - var7 && var7 + var2 <= Class3_Sub28_Sub18.anInt3765 && var3 - var4 >= Class159.anInt2020 && Class57.anInt902 >= var3 + var4) { - Class161.method2200(var6, var2, var3, var5, var7, 95, var4, var1); - } else { - TextureOperation25.method329(var7, var6, var5, var1, var3, var2, var4); - } - - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "fn.C(" + true + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - public TextureOperation24() { - super(1, true); - } - - static void method224(int var1, int var2, int var3, int var4, int var5) { - try { - GameObject.aClass109Array1831[0].method1667(var3, var4); - GameObject.aClass109Array1831[1].method1667(var3, -16 + var5 + var4); - int var6 = var5 * (var5 + -32) / var2; - if (var6 < 8) { - var6 = 8; - } - int var7 = var1 * (-var6 + -32 + var5) / (var2 + -var5); - Toolkit.getActiveToolkit().method934(var3, 16 + var4, 16, -32 + var5, anInt2530); - Toolkit.getActiveToolkit().method934(var3, 16 + var4 + var7, 16, var6, anInt486); - Toolkit.getActiveToolkit().drawVerticalLine(var3, var7 + (var4 + 16), var6, anInt3377); - Toolkit.getActiveToolkit().drawVerticalLine(var3 + 1, var7 + 16 + var4, var6, anInt3377); - Toolkit.getActiveToolkit().drawHorizontalLine(var3, var7 + 16 + var4, 16, anInt3377); - Toolkit.getActiveToolkit().drawHorizontalLine(var3, var7 + var4 + 17, 16, anInt3377); - Toolkit.getActiveToolkit().drawVerticalLine(15 + var3, var4 + (16 + var7), var6, anInt2243); - Toolkit.getActiveToolkit().drawVerticalLine(14 + var3, 17 + (var4 + var7), -1 + var6, anInt2243); - Toolkit.getActiveToolkit().drawHorizontalLine(var3, var6 + 15 + var4 + var7, 16, anInt2243); - Toolkit.getActiveToolkit().drawHorizontalLine(1 + var3, var6 + var4 + (14 + var7), 15, anInt2243); - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "fn.E(" + (byte) 120 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static void method226(int var0) { - try { - if (var0 != -1) { - if (Unsorted.loadInterface(var0)) { - RSInterface[] var2 = GameObject.aClass11ArrayArray1834[var0]; - - for (int var3 = 0; var3 < var2.length; ++var3) { - RSInterface var4 = var2[var3]; - if (null != var4.anObjectArray159) { - CS2Script var5 = new CS2Script(); - var5.arguments = var4.anObjectArray159; - var5.aClass11_2449 = var4; - DumpingTools.RunOnceAfterStartup(); - CS2Script.runAssembledScript(2000000, var5); - } - } - - } - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "fn.F(" + var0 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var10 = this.aClass114_2382.method1709(var1); - if (this.aClass114_2382.aBoolean1580) { - int[][] var4 = this.method162(var1, 0, (byte) -126); - int[] var5 = Objects.requireNonNull(var4)[0]; - int[] var7 = var4[2]; - int[] var6 = var4[1]; - - for (int var8 = 0; Class113.anInt1559 > var8; ++var8) { - var10[var8] = (var7[var8] + var5[var8] + var6[var8]) / 3; - } - } - - return var10; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "fn.D(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation25.java b/Client/src/main/java/org/runite/client/TextureOperation25.java deleted file mode 100644 index 087413f4d..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation25.java +++ /dev/null @@ -1,340 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.ReferenceCache; - -import java.util.Objects; - -public final class TextureOperation25 extends TextureOperation { - - private final int[] anIntArray3403 = new int[3]; - private int anInt3404 = 409; - private int anInt3405 = 4096; - private int anInt3406 = 4096; - private int anInt3410 = 4096; - static long aLong3411 = 0L; - static ReferenceCache aReferenceCache_3412 = new ReferenceCache(64); - static int anInt3413 = 0; - static int anInt3414; - static boolean aBoolean3416 = false; - static int anInt3417; - - - static void method329(int var0, int var1, int var2, int var3, int var5, int var6, int var7) { - try { - int var8 = 0; - int var11 = -var3 + var0; - int var10 = 0; - int var9 = var7; - int var14 = var7 * var7; - int var12 = -var3 + var7; - int var13 = var0 * var0; - int var17 = var14 << 1; - int var16 = var12 * var12; - int var15 = var11 * var11; - int var18 = var13 << 1; - int var19 = var16 << 1; - int var20 = var15 << 1; - int var22 = var12 << 1; - int var21 = var7 << 1; - int var23 = var17 + var13 * (1 + -var21); - int var24 = var14 + -((var21 + -1) * var18); - int var25 = var19 + var15 * (1 + -var22); - int var26 = var16 - var20 * (var22 + -1); - int var28 = var14 << 2; - int var27 = var13 << 2; - int var30 = var16 << 2; - int var31 = var17 * 3; - int var32 = (var21 + -3) * var18; - int var29 = var15 << 2; - int var33 = var19 * 3; - int var35 = (-3 + var22) * var20; - int var37 = (-1 + var7) * var27; - int var38 = var30; - int var36 = var28; - int var39 = (-1 + var12) * var29; - int var42; - int var43; - int var41; - int var44; - if (var5 >= Class159.anInt2020 && Class57.anInt902 >= var5) { - int[] var40 = Class38.anIntArrayArray663[var5]; - var41 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 - var0, Class101.anInt1425); - var42 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 - -var0, Class101.anInt1425); - var43 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 + -var11, Class101.anInt1425); - var44 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 - -var11, Class101.anInt1425); - TextureOperation18.method282(var40, var41, 127, var43, var2); - TextureOperation18.method282(var40, var43, 105, var44, var1); - TextureOperation18.method282(var40, var44, -67, var42, var2); - } - - while (var9 > 0) { - if (var23 < 0) { - while (var23 < 0) { - var23 += var31; - var31 += var28; - ++var8; - var24 += var36; - var36 += var28; - } - } - - boolean var49 = (var12 >= var9); - if (0 > var24) { - var24 += var36; - var23 += var31; - ++var8; - var36 += var28; - var31 += var28; - } - - if (var49) { - while (var25 < 0) { - ++var10; - var26 += var38; - var38 += var30; - var25 += var33; - var33 += var30; - } - if (var26 < 0) { - ++var10; - var26 += var38; - var25 += var33; - var38 += var30; - var33 += var30; - } - - var25 += -var39; - var39 -= var29; - var26 += -var35; - var35 -= var29; - } - - var24 += -var32; - var23 += -var37; - var37 -= var27; - var32 -= var27; - --var9; - var42 = var5 - -var9; - var41 = -var9 + var5; - if (Class159.anInt2020 <= var42 && Class57.anInt902 >= var41) { - var43 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 + var8, Class101.anInt1425); - var44 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var8 + var6, Class101.anInt1425); - if (var49) { - int var45 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 + var10, Class101.anInt1425); - int var46 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 - var10, Class101.anInt1425); - int[] var47; - if (Class159.anInt2020 <= var41) { - var47 = Class38.anIntArrayArray663[var41]; - TextureOperation18.method282(var47, var44, 120, var46, var2); - TextureOperation18.method282(var47, var46, -107, var45, var1); - TextureOperation18.method282(var47, var45, -102, var43, var2); - } - - if (Class57.anInt902 >= var42) { - var47 = Class38.anIntArrayArray663[var42]; - TextureOperation18.method282(var47, var44, 87, var46, var2); - TextureOperation18.method282(var47, var46, -92, var45, var1); - TextureOperation18.method282(var47, var45, 124, var43, var2); - } - } else { - if (var41 >= Class159.anInt2020) { - TextureOperation18.method282(Class38.anIntArrayArray663[var41], var44, -122, var43, var2); - } - - if (Class57.anInt902 >= var42) { - TextureOperation18.method282(Class38.anIntArrayArray663[var42], var44, 89, var43, var2); - } - } - } - } - - } catch (RuntimeException var48) { - throw ClientErrorException.clientError(var48, "sk.Q(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + (byte) -54 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if (true) { - if (var1 == 0) { - this.anInt3404 = var2.readUnsignedShort(); - } else if (1 == var1) { - this.anInt3405 = var2.readUnsignedShort(); - } else if (var1 == 2) { - this.anInt3406 = var2.readUnsignedShort(); - } else if (var1 == 3) { - this.anInt3410 = var2.readUnsignedShort(); - } else if (4 == var1) { - int var4 = var2.readMedium(); - this.anIntArray3403[2] = Unsorted.bitwiseAnd(var4, 255) >> 12; - this.anIntArray3403[1] = Unsorted.bitwiseAnd(var4 >> 4, 4080); - this.anIntArray3403[0] = Unsorted.bitwiseAnd(16711680, var4) << 4; - } - - } - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "sk.A(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + ')'); - } - } - - static void method330(int var0, int var1, int var2, int var3, int var4, int var5) { - try { - if (var1 > -83) { - Unsorted.menuOptionCount = 115; - } - - int var6 = -var3 + var5; - int var7 = var2 - var4; - if (var6 == 0) { - if (0 != var7) { - Class3_Sub8.method126(false, var2, var4, var0, var3); - } - - } else if (var7 == 0) { - Class75_Sub4.method1354(var3, var0, var5, var4); - } else { - int var12 = (var7 << 12) / var6; - int var13 = -(var12 * var3 >> 12) + var4; - int var8; - int var10; - if (var3 < Class101.anInt1425) { - var8 = Class101.anInt1425; - var10 = (Class101.anInt1425 * var12 >> 12) + var13; - } else if (var3 > Class3_Sub28_Sub18.anInt3765) { - var10 = (Class3_Sub28_Sub18.anInt3765 * var12 >> 12) + var13; - var8 = Class3_Sub28_Sub18.anInt3765; - } else { - var8 = var3; - var10 = var4; - } - - int var9; - int var11; - if (Class101.anInt1425 <= var5) { - if (Class3_Sub28_Sub18.anInt3765 < var5) { - var9 = Class3_Sub28_Sub18.anInt3765; - var11 = var13 - -(var12 * Class3_Sub28_Sub18.anInt3765 >> 12); - } else { - var11 = var2; - var9 = var5; - } - } else { - var9 = Class101.anInt1425; - var11 = var13 + (var12 * Class101.anInt1425 >> 12); - } - - if (var11 >= Class159.anInt2020) { - if (Class57.anInt902 < var11) { - var11 = Class57.anInt902; - var9 = (Class57.anInt902 - var13 << 12) / var12; - } - } else { - var9 = (Class159.anInt2020 - var13 << 12) / var12; - var11 = Class159.anInt2020; - } - - if (Class159.anInt2020 > var10) { - var10 = Class159.anInt2020; - var8 = (Class159.anInt2020 + -var13 << 12) / var12; - } else if (Class57.anInt902 < var10) { - var10 = Class57.anInt902; - var8 = (-var13 + Class57.anInt902 << 12) / var12; - } - - GameObject.method1869((byte) 6, var0, var11, var10, var9, var8); - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "sk.E(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - public TextureOperation25() { - super(1, false); - } - - static int method332(int var0, int var1) { - try { - if (var0 != 2) { - anInt3414 = -40; - } - - if ((var1 < 65 || var1 > 90) && (var1 < 192 || var1 > 222 || var1 == 215)) { - if (var1 != 159) return var1 != 140 ? var1 : 156; - return 255; - } - return 32 + var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sk.C(" + var0 + ',' + var1 + ')'); - } - } - - static boolean method334(CacheIndex var0) { - try { - return var0.retrieveSpriteFile(NPC.anInt4001); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sk.R(" + (var0 != null ? "{...}" : "null") + ',' + 0 + ')'); - } - } - - final int[][] method166(int var2) { - try { - int[][] var3 = this.aClass97_2376.method1594((byte) 91, var2); - if (this.aClass97_2376.aBoolean1379) { - int[][] var4 = this.method162(var2, 0, (byte) -96); - int[] var5 = Objects.requireNonNull(var4)[0]; - int[] var6 = var4[1]; - int[] var7 = var4[2]; - int[] var8 = var3[0]; - int[] var9 = var3[1]; - int[] var10 = var3[2]; - - for (int var11 = 0; var11 < Class113.anInt1559; ++var11) { - int var13 = var5[var11]; - int var12 = -this.anIntArray3403[0] + var13; - if (var12 < 0) { - var12 = -var12; - } - - if (this.anInt3404 < var12) { - var8[var11] = var13; - var9[var11] = var6[var11]; - var10[var11] = var7[var11]; - } else { - int var14 = var6[var11]; - var12 = var14 + -this.anIntArray3403[1]; - if (var12 < 0) { - var12 = -var12; - } - - if (var12 > this.anInt3404) { - var8[var11] = var13; - var9[var11] = var14; - var10[var11] = var7[var11]; - } else { - int var15 = var7[var11]; - var12 = -this.anIntArray3403[2] + var15; - if (var12 < 0) { - var12 = -var12; - } - - if (this.anInt3404 >= var12) { - var8[var11] = this.anInt3410 * var13 >> 12; - var9[var11] = this.anInt3406 * var14 >> 12; - var10[var11] = this.anInt3405 * var15 >> 12; - } else { - var8[var11] = var13; - var9[var11] = var14; - var10[var11] = var15; - } - } - } - } - } - - return var3; - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "sk.T(" + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation26.java b/Client/src/main/java/org/runite/client/TextureOperation26.java deleted file mode 100644 index e44d59dc7..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation26.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -final class TextureOperation26 extends TextureOperation { - - private int anInt3073 = 0; - private int anInt3074 = 4096; - static LinkedList aLinkedList_3075 = new LinkedList(); - static boolean aBoolean3078; - static int anInt3081 = 0; - - - static void method195() { - try { - int regionX = (NPC.anInt3995 >> 10) - -(Class131.anInt1716 >> 3); - int regionY = (Class77.anInt1111 >> 10) - -(Texture.anInt1152 >> 3); - byte plane = 0; - byte sceneX = 8; - byte var6 = 18; - Class3_Sub22.aByteArrayArray2521 = new byte[var6][]; - Class3_Sub28_Sub5.anIntArray3587 = new int[var6]; - TextureOperation35.aByteArrayArray3335 = new byte[var6][]; - Client.anIntArray2200 = new int[var6]; - Class39.regionXteaKeys = new int[var6][4]; - Class40.aByteArrayArray3669 = new byte[var6][]; - Class3_Sub24_Sub3.anIntArray3494 = new int[var6]; - Class164_Sub2.aByteArrayArray3027 = new byte[var6][]; - NPC.npcSpawnCacheIndices = new int[var6]; - TextureOperation17.anIntArray3181 = new int[var6]; - Class101.anIntArray1426 = new int[var6]; - byte sceneY = 8; - Class40.aByteArrayArray3057 = new byte[var6][]; - int var11 = 0; - - int var7; - for(var7 = (-6 + regionX) / 8; (6 + regionX) / 8 >= var7; ++var7) { - for(int var8 = (-6 + regionY) / 8; var8 <= (regionY + 6) / 8; ++var8) { - int var9 = (var7 << 8) - -var8; - Class3_Sub24_Sub3.anIntArray3494[var11] = var9; - - /** - * This block is used to control what is displayed on the HD login screen fly over - * 1. Gets the archives for the map - * 2. Gets the archives for the landscape - * 3. Gets the archives for the NPC spawns - * 4. Gets the archives for the map underlays (this would be like water for example - * 5. Gets the archives for the landscape underlays (things that (appear to be/are) -1 on the plane) - */ - Client.anIntArray2200[var11] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("m"), RSString.stringAnimator(var7), RSString.parse("_"), RSString.stringAnimator(var8)})); - if (GameConfig.HD_LOGIN_DEBUG) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting archive for map: " + Client.anIntArray2200[var11]); - if (GameConfig.HD_LOGIN_VERBOSE) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting specific info for map: " + "Type: " + RSString.parse("m") + " Place in cache: " + RSString.stringAnimator(var7) + RSString.parse("_") + RSString.stringAnimator(var8)); - } - } - - - - Class101.anIntArray1426[var11] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("l"), RSString.stringAnimator(var7), RSString.parse("_"), RSString.stringAnimator(var8)})); - if (GameConfig.HD_LOGIN_DEBUG) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting archive for landscape: " + Class101.anIntArray1426[var11]); - if (GameConfig.HD_LOGIN_VERBOSE) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting specific info for landscape: " + "Type: " + RSString.parse("l") + " Place in cache: " + RSString.stringAnimator(var7) + RSString.parse("_") + RSString.stringAnimator(var8)); - } - } - - - NPC.npcSpawnCacheIndices[var11] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("n"), RSString.stringAnimator(var7), RSString.parse("_"), RSString.stringAnimator(var8)})); - if (GameConfig.HD_LOGIN_DEBUG) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting archive for packed NPCs: " + NPC.npcSpawnCacheIndices[var11]); - if (GameConfig.HD_LOGIN_VERBOSE) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting specific info for packed NPCs: " + "Type: " + RSString.parse("n") + " Place in cache: " + RSString.stringAnimator(var7) + RSString.parse("_") + RSString.stringAnimator(var8)); - } - } - - - TextureOperation17.anIntArray3181[var11] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("um"), RSString.stringAnimator(var7), RSString.parse("_"), RSString.stringAnimator(var8)})); - if (GameConfig.HD_LOGIN_DEBUG) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting archive for map underlays: " + TextureOperation17.anIntArray3181[var11]); - if (GameConfig.HD_LOGIN_VERBOSE) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting specific info for map underlays: " + "Type: " + RSString.parse("um") + " Place in cache: " + RSString.stringAnimator(var7) + RSString.parse("_") + RSString.stringAnimator(var8)); - } - } - - - Class3_Sub28_Sub5.anIntArray3587[var11] = CacheIndex.landscapesIndex.getArchiveForName(RSString.stringCombiner(new RSString[]{RSString.parse("ul"), RSString.stringAnimator(var7), RSString.parse("_"), RSString.stringAnimator(var8)})); - if (GameConfig.HD_LOGIN_DEBUG) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting archive for landscape underlays: " + Class3_Sub28_Sub5.anIntArray3587[var11]); - if (GameConfig.HD_LOGIN_VERBOSE) { - System.out.println("Class3_Sub13_sub6: HD Login Screen Debug: Getting specific info for landscape underlays: " + "Type: " + RSString.parse("ul") + " Place in cache: " + RSString.stringAnimator(var7) + RSString.parse("_") + RSString.stringAnimator(var8)); - } - } - /* End login screen index lookup */ - - - if(NPC.npcSpawnCacheIndices[var11] == -1) { - Client.anIntArray2200[var11] = -1; - Class101.anIntArray1426[var11] = -1; - TextureOperation17.anIntArray3181[var11] = -1; - Class3_Sub28_Sub5.anIntArray3587[var11] = -1; - } - - ++var11; - } - } - - for(var7 = var11; NPC.npcSpawnCacheIndices.length > var7; ++var7) { - NPC.npcSpawnCacheIndices[var7] = -1; - Client.anIntArray2200[var7] = -1; - Class101.anIntArray1426[var7] = -1; - TextureOperation17.anIntArray3181[var7] = -1; - Class3_Sub28_Sub5.anIntArray3587[var7] = -1; - } - - Unsorted.method1301(plane, regionY, regionX, sceneY, true, sceneX); - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "ca.F(" + 20479 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var5 = this.method152(0, var1); - - for(int var6 = 0; var6 < Class113.anInt1559; ++var6) { - int var7 = var5[var6]; - var4[var6] = var7 >= this.anInt3073 && this.anInt3074 >= var7 ?4096:0; - } - } - - return var4; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ca.D(" + var1 + ',' + var2 + ')'); - } - } - - static void method196() { - try { - AudioHandler.soundEffectCoordinates = null; - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ca.B(" + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(!true) { - method196(); - } - - if(var1 == 0) { - this.anInt3073 = var2.readUnsignedShort(); - } else if (1 == var1) { - this.anInt3074 = var2.readUnsignedShort(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ca.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - public TextureOperation26() { - super(1, true); - } - - static void method198(boolean var0) { - try { - - int var3 = Class164_Sub2.aByteArrayArray3027.length; - byte[][] var2; - if(HDToolKit.highDetail && var0) { - var2 = Class40.aByteArrayArray3057; - } else { - var2 = Class3_Sub22.aByteArrayArray2521; - } - - for(int var4 = 0; var4 < var3; ++var4) { - byte[] var5 = var2[var4]; - if(var5 != null) { - int var6 = -Class131.anInt1716 + 64 * (Class3_Sub24_Sub3.anIntArray3494[var4] >> 8); - int var7 = (Class3_Sub24_Sub3.anIntArray3494[var4] & 0xFF) * 64 + -Texture.anInt1152; - Class58.method1194(); - Class3_Sub15.method374(var6, var0, var5, var7, AtmosphereParser.aClass91Array1182); - } - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ca.E(" + var0 + ',' + -32624 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation27.java b/Client/src/main/java/org/runite/client/TextureOperation27.java deleted file mode 100644 index 5915aa7a0..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation27.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.runite.client; - -import org.rs09.client.util.ArrayUtils; - -final class TextureOperation27 extends TextureOperation { - - private int anInt3085 = 10; - private int anInt3086 = 0; - static RSInterface aClass11_3087 = null; - private int[] anIntArray3089; - private int[] anIntArray3091; - private int anInt3093 = 2048; - static short[] aShortArray3095 = new short[500]; - static CacheIndex aClass153_3098; - - - final void postDecode() { - try { - this.method202(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "d.P(" + ')'); - } - } - - static int method201(int var0, int var1) { - try { - int var3 = var1 + -1 & var0 >> 31; - return var3 + (var0 + (var0 >>> 31)) % var1; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "d.C(" + var0 + ',' + var1 + ',' + -58 + ')'); - } - } - - private void method202() { - try { - int var2 = 0; - this.anIntArray3091 = new int[this.anInt3085 + 1]; - int var3 = 4096 / this.anInt3085; - this.anIntArray3089 = new int[this.anInt3085 + 1]; - int var4 = this.anInt3093 * var3 >> 12; - - for(int var5 = 0; this.anInt3085 > var5; ++var5) { - this.anIntArray3089[var5] = var2; - this.anIntArray3091[var5] = var2 + var4; - var2 += var3; - } - - this.anIntArray3089[this.anInt3085] = 4096; - this.anIntArray3091[this.anInt3085] = this.anIntArray3091[0] + 4096; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "d.B(" + 1 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3085 = var2.readUnsignedByte(); - } else if(var1 == 1) { - this.anInt3093 = var2.readUnsignedShort(); - } else if (var1 == 2) { - this.anInt3086 = var2.readUnsignedByte(); - } - - if(!true) { - this.anIntArray3089 = null; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "d.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - public TextureOperation27() { - super(0, true); - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var5 = Class163_Sub3.anIntArray2999[var1]; - int var7; - if(0 == this.anInt3086) { - short var6 = 0; - - for(var7 = 0; this.anInt3085 > var7; ++var7) { - if(var5 >= this.anIntArray3089[var7] && this.anIntArray3089[var7 - -1] > var5) { - if(var5 < this.anIntArray3091[var7]) { - var6 = 4096; - } - break; - } - } - - ArrayUtils.fill(var3, 0, Class113.anInt1559, var6); - } else { - for(int var12 = 0; Class113.anInt1559 > var12; ++var12) { - int var9 = Class102.anIntArray2125[var12]; - var7 = 0; - int var10 = this.anInt3086; - if(var10 == 1) { - var7 = var9; - } else if(var10 == 2) { - var7 = (var9 + var5 + -4096 >> 1) + 2048; - } else if (var10 == 3) { - var7 = (-var5 + var9 >> 1) + 2048; - } - - short var8 = 0; - - for(var10 = 0; var10 < this.anInt3085; ++var10) { - if(var7 >= this.anIntArray3089[var10] && this.anIntArray3089[var10 - -1] > var7) { - if(this.anIntArray3091[var10] > var7) { - var8 = 4096; - } - break; - } - } - - var3[var12] = var8; - } - } - } - - return var3; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "d.D(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation28.java b/Client/src/main/java/org/runite/client/TextureOperation28.java deleted file mode 100644 index c8b26e8cb..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation28.java +++ /dev/null @@ -1,367 +0,0 @@ -package org.runite.client; -import org.rs09.client.util.ArrayUtils; - -import java.util.Objects; -import java.util.Random; - -final class TextureOperation28 extends TextureOperation { - - private int anInt3299 = 1024; - private int anInt3300 = 1024; - private int anInt3301 = 819; - private int anInt3303 = 1024; - private int anInt3308 = 2048; - private int anInt3309 = 0; - private int anInt3310 = 409; - private int anInt3312 = 0; - static int anInt3313 = 500; - private int anInt3314; - static int anInt3315; - private int anInt3316 = 1024; - - - final void postDecode() { - try { - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ol.P(" + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[][] var5 = this.aClass114_2382.method1710((byte)97); - int var9 = 0; - int var8 = 0; - int var6 = 0; - int var7 = 0; - int var10 = 0; - boolean var11 = true; - boolean var12 = true; - int var13 = 0; - int var14 = 0; - int var15 = Class113.anInt1559 * this.anInt3300 >> 12; - int var16 = Class113.anInt1559 * this.anInt3308 >> 12; - int var18 = this.anInt3301 * Class101.anInt1427 >> 12; - int var17 = Class101.anInt1427 * this.anInt3310 >> 12; - if(var18 <= 1) { - return Objects.requireNonNull(var5)[var1]; - } else { - int var19 = Class113.anInt1559 / var15 + 1; - this.anInt3314 = Class113.anInt1559 / 8 * this.anInt3303 >> 12; - int[][] var21 = new int[var19][3]; - int[][] var20 = new int[var19][3]; - Random var22 = new Random(this.anInt3312); - - while(true) { - while(true) { - int var24 = var15 - -TextureOperation.method1603((byte)-93, var16 - var15, var22); - int var25 = TextureOperation.method1603((byte)-96, -var17 + var18, var22) + var17; - int var26 = var9 + var24; - if(Class113.anInt1559 < var26) { - var26 = Class113.anInt1559; - var24 = Class113.anInt1559 - var9; - } - - int var23; - int var29; - if(var12) { - var23 = 0; - } else { - int var27 = var10; - int[] var28 = var21[var10]; - var23 = var28[2]; - var29 = 0; - int var30 = var6 + var26; - if(0 > var30) { - var30 += Class113.anInt1559; - } - - if(var30 > Class113.anInt1559) { - var30 -= Class113.anInt1559; - } - - while(true) { - int[] var31 = var21[var27]; - if(var31[0] <= var30 && var30 <= var31[1]) { - if(var10 != var27) { - int var42 = var6 + var9; - if(var42 < 0) { - var42 += Class113.anInt1559; - } - - if(var42 > Class113.anInt1559) { - var42 -= Class113.anInt1559; - } - - int var32; - int[] var33; - for(var32 = 1; var32 <= var29; ++var32) { - var33 = var21[(var10 + var32) % var13]; - var23 = Math.max(var23, var33[2]); - } - - for(var32 = 0; var32 <= var29; ++var32) { - var33 = var21[(var10 - -var32) % var13]; - int var34 = var33[2]; - if(var23 != var34) { - int var37 = var33[0]; - int var38 = var33[1]; - int var35; - int var36; - if(var42 < var30) { - var35 = Math.max(var42, var37); - var36 = Math.min(var30, var38); - } else if(var37 == 0) { - var36 = Math.min(var30, var38); - var35 = 0; - } else { - var35 = Math.max(var42, var37); - var36 = Class113.anInt1559; - } - - this.method291(var34, var22, var8 + var35, -var35 + var36, -var34 + var23, var5); - } - } - } - - var10 = var27; - break; - } - - ++var29; - ++var27; - if(var13 <= var27) { - var27 = 0; - } - } - } - - if(Class101.anInt1427 >= var23 - -var25) { - var11 = false; - } else { - var25 = Class101.anInt1427 - var23; - } - - int[] var40; - if(Class113.anInt1559 == var26) { - this.method291(var23, var22, var7 + var9, var24, var25, var5); - if(var11) { - return var3; - } - - var11 = true; - var8 = var7; - var12 = false; - var40 = var20[var14++]; - var40[0] = var9; - var10 = 0; - var13 = var14; - var14 = 0; - var40[2] = var25 + var23; - var40[1] = var26; - var7 = TextureOperation.method1603((byte)-107, Class113.anInt1559, var22); - var6 = var7 + -var8; - int[][] var41 = var21; - var9 = 0; - var21 = var20; - var29 = var6; - if(var6 < 0) { - var29 = var6 + Class113.anInt1559; - } - - var20 = var41; - if(var29 > Class113.anInt1559) { - var29 -= Class113.anInt1559; - } - - while(true) { - int[] var43 = var21[var10]; - if(var43[0] <= var29 && var29 <= var43[1]) { - break; - } - - ++var10; - if(var13 <= var10) { - var10 = 0; - } - } - } else { - var40 = var20[var14++]; - var40[1] = var26; - var40[2] = var25 + var23; - var40[0] = var9; - this.method291(var23, var22, var9 + var7, var24, var25, var5); - var9 = var26; - } - } - } - } - } else { - return var3; - } - } catch (RuntimeException var39) { - throw ClientErrorException.clientError(var39, "ol.D(" + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation28() { - super(0, true); - } - - private void method291(int var1, Random var2, int var3, int var4, int var6, int[][] var7) { - try { - int var8 = this.anInt3316 > 0 ?4096 + -TextureOperation.method1603((byte)-128, this.anInt3316, var2):4096; - int var9 = this.anInt3299 * this.anInt3314 >> 12; - int var10 = this.anInt3314 - (0 < var9? TextureOperation.method1603((byte)39, var9, var2):0); - if(Class113.anInt1559 <= var3) { - var3 -= Class113.anInt1559; - } - - int var11; - int var12; - if(0 < var10) { - if(0 >= var6 || var4 <= 0) { - return; - } - - var11 = var4 / 2; - var12 = var6 / 2; - int var13 = var11 < var10?var11:var10; - int var14 = var10 <= var12 ?var10:var12; - int var16 = var4 + -(2 * var13); - int var15 = var13 + var3; - - for(int var17 = 0; var17 < var6; ++var17) { - int[] var18 = var7[var1 + var17]; - int var19; - int var21; - int var20; - if(var14 > var17) { - var19 = var17 * var8 / var14; - if(this.anInt3309 == 0) { - for(var20 = 0; var20 < var13; ++var20) { - var21 = var20 * var8 / var13; - var18[Unsorted.bitwiseAnd(RenderAnimationDefinition.anInt396, var20 + var3)] = var18[Unsorted.bitwiseAnd(-1 + var4 + var3 + -var20, RenderAnimationDefinition.anInt396)] = var21 * var19 >> 12; - } - } else { - for(var20 = 0; var20 < var13; ++var20) { - var21 = var20 * var8 / var13; - var18[Unsorted.bitwiseAnd(RenderAnimationDefinition.anInt396, var3 - -var20)] = var18[Unsorted.bitwiseAnd(RenderAnimationDefinition.anInt396, var4 + var3 + -var20 - 1)] = var21 >= var19 ?var19:var21; - } - } - - if(Class113.anInt1559 >= var16 + var15) { - ArrayUtils.fill(var18, var15, var16, var19); - } else { - var20 = -var15 + Class113.anInt1559; - ArrayUtils.fill(var18, var15, var20, var19); - ArrayUtils.fill(var18, 0, -var20 + var16, var19); - } - } else { - var19 = var6 + -var17 - 1; - if(var14 <= var19) { - for(var20 = 0; var13 > var20; ++var20) { - var18[Unsorted.bitwiseAnd(RenderAnimationDefinition.anInt396, var3 + var20)] = var18[Unsorted.bitwiseAnd(-1 + -var20 + var3 - -var4, RenderAnimationDefinition.anInt396)] = var8 * var20 / var13; - } - - if(Class113.anInt1559 < var15 - -var16) { - var20 = -var15 + Class113.anInt1559; - ArrayUtils.fill(var18, var15, var20, var8); - ArrayUtils.fill(var18, 0, var16 - var20, var8); - } else { - ArrayUtils.fill(var18, var15, var16, var8); - } - } else { - var20 = var19 * var8 / var14; - int var22; - if(this.anInt3309 == 0) { - for(var21 = 0; var13 > var21; ++var21) { - var22 = var8 * var21 / var13; - var18[Unsorted.bitwiseAnd(RenderAnimationDefinition.anInt396, var3 - -var21)] = var18[Unsorted.bitwiseAnd(RenderAnimationDefinition.anInt396, -1 + var3 - (-var4 + var21))] = var22 * var20 >> 12; - } - } else { - for(var21 = 0; var21 < var13; ++var21) { - var22 = var21 * var8 / var13; - var18[Unsorted.bitwiseAnd(var3 - -var21, RenderAnimationDefinition.anInt396)] = var18[Unsorted.bitwiseAnd(-1 + -var21 + var4 + var3, RenderAnimationDefinition.anInt396)] = var22 >= var20 ?var20:var22; - } - } - - if(var16 + var15 > Class113.anInt1559) { - var21 = Class113.anInt1559 + -var15; - ArrayUtils.fill(var18, var15, var21, var20); - ArrayUtils.fill(var18, 0, -var21 + var16, var20); - } else { - ArrayUtils.fill(var18, var15, var16, var20); - } - } - } - } - } else if(var3 - -var4 <= Class113.anInt1559) { - for(var11 = 0; var11 < var6; ++var11) { - ArrayUtils.fill(var7[var1 - -var11], var3, var4, var8); - } - } else { - var11 = Class113.anInt1559 - var3; - - for(var12 = 0; var6 > var12; ++var12) { - int[] var24 = var7[var12 + var1]; - ArrayUtils.fill(var24, var3, var11, var8); - ArrayUtils.fill(var24, 0, -var11 + var4, var8); - } - } - - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "ol.B(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + var3 + ',' + var4 + ',' + (byte) -69 + ',' + var6 + ',' + (var7 != null?"{...}":"null") + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(!true) { - this.anInt3301 = 4; - } - - if(var1 == 0) { - this.anInt3312 = var2.readUnsignedByte(); - } else if (var1 == 1) { - this.anInt3300 = var2.readUnsignedShort(); - } else if (2 == var1) { - this.anInt3308 = var2.readUnsignedShort(); - } else if (var1 == 3) { - this.anInt3310 = var2.readUnsignedShort(); - } else if (var1 == 4) { - this.anInt3301 = var2.readUnsignedShort(); - } else if (var1 == 5) { - this.anInt3303 = var2.readUnsignedShort(); - } else if (var1 == 6) { - this.anInt3309 = var2.readUnsignedByte(); - } else if (var1 == 7) { - this.anInt3299 = var2.readUnsignedShort(); - } else if (var1 == 8) { - this.anInt3316 = var2.readUnsignedShort(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ol.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static void method292(int var0, int var1, int var2, int var3, GameObject var4, int var5, long var6, int var8, int var9, int var10, int var11) { - if (var4 != null) { - Class56.method1189(var0, var8, var9, var10 - var8 + 1, var11 - var9 + 1, var1, var2, var3, var4, var5, true, var6); - } - } - - static int method293(int var0, int var1, int var3) { - try { - var1 &= 3; - return (0 == var1?var3:(1 != var1?(var1 != 2 ?var0:-var3 + 1023):-var0 + 1023)); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ol.E(" + var0 + ',' + var1 + ',' + false + ',' + var3 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation29.java b/Client/src/main/java/org/runite/client/TextureOperation29.java deleted file mode 100644 index 37ad8017e..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation29.java +++ /dev/null @@ -1,170 +0,0 @@ -package org.runite.client; - -final class TextureOperation29 extends TextureOperation { - - static byte[][][] aByteArrayArrayArray3390; - static RSString[] aClass94Array3391; - private Class75[] aClass75Array3392; - static Class133[] aClass133Array3393 = new Class133[6]; - static byte[] aByteArray3396; - - static volatile int anInt3398 = 0; - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - this.method323(-60, this.aClass114_2382.method1710((byte)124)); - } - - return var3; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "si.D(" + var1 + ',' + var2 + ')'); - } - } - - static int method322(byte var1) { - try { - return 255 & var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "si.C(" + true + ',' + var1 + ')'); - } - } - - private void method323(int var1, int[][] var2) { - try { - int var4 = Class101.anInt1427; - int var3 = Class113.anInt1559; - TextureOperation20.method230(var2); - Class58.method1196(Class3_Sub20.anInt2487, RenderAnimationDefinition.anInt396); - if(this.aClass75Array3392 != null) { - for(int var5 = 0; this.aClass75Array3392.length > var5; ++var5) { - Class75 var6 = this.aClass75Array3392[var5]; - int var7 = var6.anInt1101; - int var8 = var6.anInt1104; - if(var7 >= 0) { - if(var8 < 0) { - var6.method1341(var3, var4); - } else { - var6.method1335(var4, var3, 4898); - } - } else if(var8 >= 0) { - var6.method1337(var4, true, var3); - } - } - } - - if(var1 != -60) { - method326((byte)-35, null); - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "si.F(" + var1 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.aClass75Array3392 = new Class75[var2.readUnsignedByte()]; - - for(int var4 = 0; var4 < this.aClass75Array3392.length; ++var4) { - int var5 = var2.readUnsignedByte(); - if(var5 == 0) { - this.aClass75Array3392[var4] = Class8.method843(-5232, var2); - } else if(var5 == 1) { - this.aClass75Array3392[var4] = Class3_Sub28_Sub2.method536(var2); - } else if(var5 == 2) { - this.aClass75Array3392[var4] = Class3_Sub22.method404(var2); - } else if (3 == var5) { - this.aClass75Array3392[var4] = Class3_Sub19.method384(var2); - } - } - } else if(1 == var1) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - if(!true) { - this.method323(124, null); - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "si.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static void method324(int var0) { - try { - Class92.setLightParams(Class92.defaultScreenColorRgb, (0.7F + (float)var0 * 0.1F) * 1.1523438F, 0.69921875F, 0.69921875F); - Class92.setLightPosition(-50.0F, -60.0F, -50.0F); - Class92.setFogValues(Class92.defaultRegionAmbientRGB, 0); - Class92.method1504(); - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "si.Q(" + var0 + ',' + false + ')'); - } - } - - static int method326(byte var0, RSString var1) { - try { - if(var0 <= 13) { - TextCore.aClass94_3399 = null; - } - - return var1.length() + 1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "si.O(" + var0 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 == -1) { - int[][] var3 = this.aClass97_2376.method1594((byte)-117, var2); - if(this.aClass97_2376.aBoolean1379) { - int var4 = Class113.anInt1559; - int var5 = Class101.anInt1427; - int[][] var6 = new int[var5][var4]; - int[][][] var7 = this.aClass97_2376.method1589(); - this.method323(-60, var6); - - for(int var8 = 0; var8 < Class101.anInt1427; ++var8) { - int[] var9 = var6[var8]; - int[][] var10 = var7[var8]; - int[] var11 = var10[0]; - int[] var12 = var10[1]; - int[] var13 = var10[2]; - - for(int var14 = 0; Class113.anInt1559 > var14; ++var14) { - int var15 = var9[var14]; - var13[var14] = Unsorted.bitwiseAnd(255, var15) << 4; - var12[var14] = Unsorted.bitwiseAnd(4080, var15 >> 4); - var11[var14] = Unsorted.bitwiseAnd(var15 >> 12, 4080); - } - } - } - - return var3; - } else { - return null; - } - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "si.T(" + -1 + ',' + var2 + ')'); - } - } - - public TextureOperation29() { - super(0, true); - } - - static void method327(int var0, int var1) { - try { - InterfaceWidget var3 = InterfaceWidget.getWidget(12, var1); - var3.flagUpdate(); - var3.anInt3598 = var0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "si.B(" + var0 + ',' + var1 + ',' + (byte) 68 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation3.java b/Client/src/main/java/org/runite/client/TextureOperation3.java deleted file mode 100644 index ecf12d88d..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation3.java +++ /dev/null @@ -1,191 +0,0 @@ -package org.runite.client; - -import org.rs09.SystemLogger; -import org.rs09.client.util.ArrayUtils; -import org.rs09.client.filestore.resources.configs.cursors.CursorDefinition; - -import java.util.Objects; - -final class TextureOperation3 extends TextureOperation { - - static boolean disableGEBoxes = false; - static int[] anIntArray3359 = new int[5]; - - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - ArrayUtils.fill(var3, 0, Class113.anInt1559, Class163_Sub3.anIntArray2999[var1]); - } - - return var3; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "qg.D(" + var1 + ',' + var2 + ')'); - } - } - - static void method304() { - try { - - TextureOperation25.aReferenceCache_3412.clear(); - TextureOperation2.aReferenceCache_3369.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "qg.F(" + 6799 + ')'); - } - } - - static void method305(Signlink var0, DataBuffer var1, int var2) { - try { - Class3_Sub8 var4 = new Class3_Sub8(); - var4.anInt2296 = var1.readUnsignedByte(); - var4.anInt2305 = var1.readInt(); - var4.aClass64Array2298 = new Class64[var4.anInt2296]; - var4.anIntArray2300 = new int[var4.anInt2296]; - var4.aByteArrayArrayArray2302 = new byte[var4.anInt2296][][]; - var4.aClass64Array2303 = new Class64[var4.anInt2296]; - var4.anIntArray2301 = new int[var4.anInt2296]; - var4.anIntArray2299 = new int[var4.anInt2296]; - - for(int var6 = 0; var4.anInt2296 > var6; ++var6) { - try { - int var7 = var1.readUnsignedByte(); - String var8; - String var9; - int var10; - if (var7 == 0 || var7 == 1 || var7 == 2) { - var8 = new String(var1.readString().method1568()); - var10 = 0; - var9 = new String(var1.readString().method1568()); - if(var7 == 1) { - var10 = var1.readInt(); - } - - var4.anIntArray2301[var6] = var7; - var4.anIntArray2299[var6] = var10; - var4.aClass64Array2303[var6] = var0.method1447(-41, var9, TextureOperation12.method170(var8)); - } else if (var7 == 3 || var7 == 4) { - var8 = new String(var1.readString().method1568()); - var9 = new String(var1.readString().method1568()); - var10 = var1.readUnsignedByte(); - String[] var11 = new String[var10]; - - for (int var12 = 0; var10 > var12; ++var12) { - var11[var12] = new String(var1.readString().method1568()); - } - - byte[][] var21 = new byte[var10][]; - int var14; - if (3 == var7) { - for (int var13 = 0; var13 < var10; ++var13) { - var14 = var1.readInt(); - var21[var13] = new byte[var14]; - var1.readBytes(var21[var13], var14); - } - } - - var4.anIntArray2301[var6] = var7; - Class[] var22 = new Class[var10]; - - for (var14 = 0; var10 > var14; ++var14) { - var22[var14] = TextureOperation12.method170(var11[var14]); - } - - var4.aClass64Array2298[var6] = var0.method1443(TextureOperation12.method170(var8), var22, -80, var9); - var4.aByteArrayArrayArray2302[var6] = var21; - } - } catch (ClassNotFoundException var15) { - var4.anIntArray2300[var6] = -1; - } catch (SecurityException var16) { - var4.anIntArray2300[var6] = -2; - } catch (NullPointerException var17) { - var4.anIntArray2300[var6] = -3; - } catch (Exception var18) { - var4.anIntArray2300[var6] = -4; - } catch (Throwable var19) { - var4.anIntArray2300[var6] = -5; - } - } - - Class3_Sub26.aLinkedList_2557.method1215(var4); - } catch (RuntimeException var20) { - throw ClientErrorException.clientError(var20, "qg.E(" + (var0 != null?"{...}":"null") + ',' + (var1 != null?"{...}":"null") + ',' + var2 + ',' + (byte) -126 + ')'); - } - } - - static void method306(int var0, int var2) { - try { - CSConfigCachefile var3 = CSConfigCachefile.getCSConfigFileFromVarbitID(var0); - int var6 = Objects.requireNonNull(var3).upperBound; - int var5 = var3.lowerBound; - int var4 = var3.parentVarpIndex; - int var7 = Class3_Sub6.expectedMinimumValues[var6 - var5]; - if(var2 < 0 || var7 < var2) { - SystemLogger.logInfo(var7 + " < " + var2); - var2 = 0; - } - - var7 <<= var5; - AtmosphereParser.method1428(var4, var7 & var2 << var5 | ItemDefinition.ram[var4] & ~var7); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "qg.Q(" + var0 + ',' + false + ',' + var2 + ')'); - } - } - - static void method307(RSString[] var0, short[] var1, int var2) { - try { - Class3_Sub8.method127(var1, -1 + var0.length, var0, 0); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "qg.C(" + (var0 != null?"{...}":"null") + ',' + (var1 != null?"{...}":"null") + ',' + var2 + ')'); - } - } - - static int bitwiseOr(int var0, int var1) { - try { - return var0 | var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "qg.R(" + var0 + ',' + var1 + ')'); - } - } - - public TextureOperation3() { - super(0, true); - } - - static int method310(int var0, byte var1, int var2, int var3) { - try { - var0 &= 3; - if(var0 == 0) { - return var3; - } else { - if(var1 >= -17) { - TextCore.aClass94_3357 = null; - } - - return var0 == 1?7 + -var2:(var0 == 2 ?-var3 + 7:var2); - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "qg.B(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - static CursorDefinition method311(int var0) { - try { - CursorDefinition var2 = (CursorDefinition) Unsorted.aReferenceCache_684.get(var0); - if(var2 == null) { - byte[] var3 = TextureOperation4.aClass153_3227.getFile(33, var0); - - var2 = new CursorDefinition(); - if(var3 != null) { - var2.decode(new DataBuffer(var3)); - } - - Unsorted.aReferenceCache_684.put(var2, var0); - } - return var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "qg.O(" + var0 + ',' + 5 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation30.java b/Client/src/main/java/org/runite/client/TextureOperation30.java deleted file mode 100644 index d5245c8a1..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation30.java +++ /dev/null @@ -1,273 +0,0 @@ -package org.runite.client; -import java.awt.Frame; -import java.util.Objects; - -final class TextureOperation30 extends TextureOperation { - - static volatile boolean fullRedraw = true; - - static boolean[][] aBooleanArrayArray3118 = new boolean[][]{new boolean[0], {true, false, true}, {true, false, false, true}, {false, false, true, true}, {true, true, false}, {false, true, true}, {true, false, false, true}, {false, false, false, true, true}, {false, true, true}, {true, false, true, true, true}, {false, true, true, true, true}, {false, true, true, true, true, false}}; - static Frame fullScreenFrame; - static Signlink signlink; - private int anInt3126 = 2048; - private int anInt3127 = 3072; - private int anInt3128 = 1024; - - static int method210(int var1, int var2, int var3) { - try { - if(var3 == var2) { - return var2; - } else { - int var4 = -var1 + 128; - - int var5 = var1 * ((-16711936 & var3) >>> 7) + var4 * ((-16711936 & var2) >>> 7) & -16711936; - int var6 = var4 * (16711935 & var2) - -((var3 & 16711935) * var1) & -16711936; - return var5 - -(var6 >> 7); - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "fh.O(" + 18348 + ',' + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3128 = var2.readUnsignedShort(); - } else if (var1 == 1) { - this.anInt3127 = var2.readUnsignedShort(); - } else if (var1 == 2) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "fh.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var7 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var4 = this.method152(0, var1); - - for(int var5 = 0; var5 < Class113.anInt1559; ++var5) { - var7[var5] = this.anInt3128 - -(var4[var5] * this.anInt3126 >> 12); - } - } - - return var7; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "fh.D(" + var1 + ',' + var2 + ')'); - } - } - - static void method212(long var0) { - try { - if(0L != var0) { - for(int var3 = 0; Class3_Sub28_Sub5.anInt3591 > var3; ++var3) { - if(Class114.ignores[var3] == var0) { - --Class3_Sub28_Sub5.anInt3591; - - for(int var4 = var3; var4 < Class3_Sub28_Sub5.anInt3591; ++var4) { - Class114.ignores[var4] = Class114.ignores[var4 + 1]; - TextureOperation7.aClass94Array3341[var4] = TextureOperation7.aClass94Array3341[1 + var4]; - } - - Class110.anInt1472 = PacketParser.anInt3213; - TextureOperation12.outgoingBuffer.putOpcode(213); - TextureOperation12.outgoingBuffer.writeLong(var0); - break; - } - } - - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "fh.E(" + var0 + ',' + 0 + ')'); - } - } - - static void method213(int var0, int var1, int var2, int var3, GameObject var4, long var5, GameObject var7, GameObject var8) { - Class72 var9 = new Class72(); - var9.aClass140_1073 = var4; - var9.anInt1078 = var1 * 128 + 64; - var9.anInt1075 = var2 * 128 + 64; - var9.anInt1068 = var3; - var9.aLong1079 = var5; - var9.aClass140_1067 = var7; - var9.aClass140_1069 = var8; - int var10 = 0; - Class3_Sub2 var11 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if(var11 != null) { - for(int var12 = 0; var12 < var11.anInt2223; ++var12) { - Class25 var13 = var11.aClass25Array2221[var12]; - if((var13.aLong498 & 4194304L) == 4194304L) { - int var14 = var13.aClass140_479.method1871(); - if(var14 != -32768 && var14 < var10) { - var10 = var14; - } - } - } - } - - var9.anInt1077 = -var10; - if(Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2] = new Class3_Sub2(var0, var1, var2); - } - - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2].aClass72_2245 = var9; - } - - static void method214(GameObject var0, int var1, int var2, int var3, int var4, int var5) { - boolean var6 = true; - int var7 = var2; - int var8 = var2 + var4; - int var9 = var3 - 1; - int var10 = var3 + var5; - - for(int var11 = var1; var11 <= var1 + 1; ++var11) { - if(var11 != Class3_Sub17.anInt2456) { - for(int var12 = var7; var12 <= var8; ++var12) { - if(var12 >= 0 && var12 < Unsorted.anInt1234) { - for(int var13 = var9; var13 <= var10; ++var13) { - if(var13 >= 0 && var13 < TextureOperation17.anInt3179 && (!var6 || var12 >= var8 || var13 >= var10 || var13 < var3 && var12 != var2)) { - Class3_Sub2 var14 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var11][var12][var13]; - if(var14 != null) { - int var15 = (Class44.anIntArrayArrayArray723[var11][var12][var13] + Class44.anIntArrayArrayArray723[var11][var12 + 1][var13] + Class44.anIntArrayArrayArray723[var11][var12][var13 + 1] + Class44.anIntArrayArrayArray723[var11][var12 + 1][var13 + 1]) / 4 - (Class44.anIntArrayArrayArray723[var1][var2][var3] + Class44.anIntArrayArrayArray723[var1][var2 + 1][var3] + Class44.anIntArrayArrayArray723[var1][var2][var3 + 1] + Class44.anIntArrayArrayArray723[var1][var2 + 1][var3 + 1]) / 4; - Class70 var16 = var14.aClass70_2234; - if(var16 != null) { - if(var16.aClass140_1049.method1865()) { - var0.method1866(var16.aClass140_1049, (var12 - var2) * 128 + (1 - var4) * 64, var15, (var13 - var3) * 128 + (1 - var5) * 64, var6); - } - - if(var16.aClass140_1052 != null && var16.aClass140_1052.method1865()) { - var0.method1866(var16.aClass140_1052, (var12 - var2) * 128 + (1 - var4) * 64, var15, (var13 - var3) * 128 + (1 - var5) * 64, var6); - } - } - - for(int var17 = 0; var17 < var14.anInt2223; ++var17) { - Class25 var18 = var14.aClass25Array2221[var17]; - if(var18 != null && var18.aClass140_479.method1865() && (var12 == var18.anInt483 || var12 == var7) && (var13 == var18.anInt478 || var13 == var9)) { - int var19 = var18.anInt495 - var18.anInt483 + 1; - int var20 = var18.anInt481 - var18.anInt478 + 1; - var0.method1866(var18.aClass140_479, (var18.anInt483 - var2) * 128 + (var19 - var4) * 64, var15, (var18.anInt478 - var3) * 128 + (var20 - var5) * 64, var6); - } - } - } - } - } - } - } - - --var7; - var6 = false; - } - } - - } - - static void method215(byte var0, LDIndexedSprite var1) { - try { - short var2 = 256; - int var3 = 0; - if(var0 >= -80) { - fullRedraw = true; - } - - while(Class161.anIntArray2026.length > var3) { - Class161.anIntArray2026[var3] = 0; - ++var3; - } - - int var4; - for(var3 = 0; var3 < 5000; ++var3) { - var4 = (int)((double)var2 * Math.random() * 128.0D); - Class161.anIntArray2026[var4] = (int)(Math.random() * 284.0D); - } - - int var5; - int var6; - try { - for (; true; ++var3) { - for (var4 = 1; true; ++var4) { - for (var5 = 1; true; ++var5) { - var6 = var5 - -(var4 << 7); - Unsorted.anIntArray49[var6] = (Class161.anIntArray2026[128 + var6] + Class161.anIntArray2026[-1 + var6] + Class161.anIntArray2026[1 + var6] - -Class161.anIntArray2026[-128 + var6]) / 4; - } - } - } - } catch (Exception e) { - /* - System.out.println("breaking you out of the stupid endless triple for loop"); - System.out.println("If you see this message it's a bug. this function should never run but it does sometimes for inexplicable reasons."); - System.out.println("Without this code here to break you out the game would crash."); - System.out.println("Once this client is deobbed fully this code will probably be deleted/unneeded"); - System.out.println("- Your friendly neighborhood moth"); - */ - // moth is unfriendly now because u hecks keep reporting this as a bug - // I'm sorry you misinterpreted this message - // If something calls itself a bug the devs obviously know about it already. - } - - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "fh.F(" + var0 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - - final int[][] method166(int var2) { - try { - int[][] var3 = this.aClass97_2376.method1594((byte)-118, var2); - if(this.aClass97_2376.aBoolean1379) { - int[][] var4 = this.method162(var2, 0, (byte)-50); - int[] var6 = Objects.requireNonNull(var4)[1]; - int[] var7 = var4[2]; - int[] var5 = var4[0]; - int[] var8 = var3[0]; - int[] var9 = var3[1]; - int[] var10 = var3[2]; - - for(int var11 = 0; Class113.anInt1559 > var11; ++var11) { - var8[var11] = this.anInt3128 - -(this.anInt3126 * var5[var11] >> 12); - var9[var11] = (this.anInt3126 * var6[var11] >> 12) + this.anInt3128; - var10[var11] = this.anInt3128 + (this.anInt3126 * var7[var11] >> 12); - } - } - - return var3; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "fh.T(" + -1 + ',' + var2 + ')'); - } - } - - final void postDecode() { - try { - this.anInt3126 = this.anInt3127 - this.anInt3128; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "fh.P(" + ')'); - } - } - - public TextureOperation30() { - super(1, false); - } - - static void method216(DataBuffer var0) { - try { - for(int var2 = 0; var2 < WorldListEntry.activeWorldListSize; ++var2) { - int var3 = var0.getSmart(); - int var4 = var0.readUnsignedShort(); - if(var4 == 65535) { - var4 = -1; - } - - if(null != WorldListEntry.worldList[var3]) { - WorldListEntry.worldList[var3].anInt722 = var4; - } - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "fh.Q(" + (var0 != null?"{...}":"null") + ',' + -14991 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation31.java b/Client/src/main/java/org/runite/client/TextureOperation31.java deleted file mode 100644 index 8cdc77455..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation31.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -import java.math.BigInteger; - -final class TextureOperation31 extends TextureOperation { - - static CacheResourceWorker aCacheResourceWorker_3159; - private int anInt3160 = 0; - static BigInteger MODULUS = GameConfig.MODULUS; - private int anInt3163 = 20; - private int anInt3164 = 1365; - private int anInt3165 = 0; - static boolean aBoolean3166 = false; - - final void decode(int var1, DataBuffer var2) { - try { - if (var1 == 0) { - this.anInt3164 = var2.readUnsignedShort(); - } else if (var1 == 1) { - this.anInt3163 = var2.readUnsignedShort(); - } else if (var1 == 2) { - this.anInt3160 = var2.readUnsignedShort(); - } else if (var1 == 3) { - this.anInt3165 = var2.readUnsignedShort(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "gm.A(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + true + ')'); - } - } - - static void method236() { - try { - TextureOperation14.aBoolean3387 = true; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "gm.C(" + (byte) 64 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if (this.aClass114_2382.aBoolean1580) { - for (int var5 = 0; var5 < Class113.anInt1559; ++var5) { - int var7 = this.anInt3165 + (Class163_Sub3.anIntArray2999[var1] << 12) / this.anInt3164; - int var6 = this.anInt3160 + (Class102.anIntArray2125[var5] << 12) / this.anInt3164; - int var10 = var6; - int var11 = var7; - int var14 = 0; - int var12 = var6 * var6 >> 12; - - for (int var13 = var7 * var7 >> 12; var12 - -var13 < 16384 && var14 < this.anInt3163; var12 = var10 * var10 >> 12) { - var11 = (var10 * var11 >> 12) * 2 + var7; - ++var14; - var10 = var12 + -var13 + var6; - var13 = var11 * var11 >> 12; - } - - var3[var5] = this.anInt3163 + -1 <= var14 ? 0 : (var14 << 12) / this.anInt3163; - } - } - - return var3; - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "gm.D(" + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation31() { - super(0, true); - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation32.java b/Client/src/main/java/org/runite/client/TextureOperation32.java deleted file mode 100644 index bac5934f3..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation32.java +++ /dev/null @@ -1,165 +0,0 @@ -package org.runite.client; - -final class TextureOperation32 extends TextureOperation { - - static Class3_Sub11[][] aClass3_Sub11ArrayArray3346; - private int anInt3347 = 3216; - private final int[] anIntArray3348 = new int[3]; - private int anInt3350 = 4096; - static RSString aClass94_3353; - private int anInt3354 = 3216; - - - final void decode(int var1, DataBuffer var2) { - try { - if(0 == var1) { - this.anInt3350 = var2.readUnsignedShort(); - } else if(1 == var1) { - this.anInt3347 = var2.readUnsignedShort(); - } else if (var1 == 2) { - this.anInt3354 = var2.readUnsignedShort(); - } - - if(!true) { - method302(-47); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pk.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static int method301(byte var0) { - try { - //int var1 = -47 / ((45 - var0) / 57); - return TextureOperation29.anInt3398; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "pk.E(" + var0 + ')'); - } - } - - public TextureOperation32() { - super(1, true); - } - - final void postDecode() { - try { - this.method303(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "pk.P(" + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var8 = Class95.anInt1343 * this.anInt3350 >> 12; - int[] var9 = this.method152(0, Class3_Sub20.anInt2487 & var1 + -1); - int[] var10 = this.method152(0, var1); - int[] var11 = this.method152(0, 1 + var1 & Class3_Sub20.anInt2487); - - for(int var12 = 0; Class113.anInt1559 > var12; ++var12) { - int var14 = (var10[RenderAnimationDefinition.anInt396 & -1 + var12] - var10[1 + var12 & RenderAnimationDefinition.anInt396]) * var8 >> 12; - int var13 = var8 * (-var9[var12] + var11[var12]) >> 12; - int var15 = var14 >> 4; - if(0 > var15) { - var15 = -var15; - } - - if(var15 > 255) { - var15 = 255; - } - - int var16 = var13 >> 4; - if(var16 < 0) { - var16 = -var16; - } - - if(255 < var16) { - var16 = 255; - } - - int var17 = Class97.aByteArray1364[(var16 * (var16 - -1) >> 1) + var15] & 0xFF; - int var6 = var13 * var17 >> 8; - int var5 = var17 * var14 >> 8; - var6 = var6 * this.anIntArray3348[1] >> 12; - var5 = this.anIntArray3348[0] * var5 >> 12; - int var7 = 4096 * var17 >> 8; - var7 = var7 * this.anIntArray3348[2] >> 12; - var4[var12] = var7 + var6 + var5; - } - } - - return var4; - } catch (RuntimeException var18) { - throw ClientErrorException.clientError(var18, "pk.D(" + var1 + ',' + var2 + ')'); - } - } - - static void method302(int var0) { - try { - if(var0 != 2) { - method301((byte)56); - } - - for(Class3_Sub28_Sub19 var1 = (Class3_Sub28_Sub19) TextureOperation13.aLinkedList_3364.method1222(); var1 != null; var1 = (Class3_Sub28_Sub19) TextureOperation13.aLinkedList_3364.method1221()) { - Class140_Sub6 var2 = var1.aClass140_Sub6_3778; - if(var2.anInt2907 == WorldListCountry.localPlane && Class44.anInt719 <= var2.anInt2899) { - if(Class44.anInt719 >= var2.anInt2925) { - if(var2.anInt2919 > 0) { - NPC var3 = NPC.npcs[-1 + var2.anInt2919]; - if(null != var3 && var3.xAxis >= 0 && 13312 > var3.xAxis && var3.zAxis >= 0 && var3.zAxis < 13312) { - var2.method2024(var3.zAxis, Class44.anInt719, Class121.method1736(var2.anInt2907, 1, var3.xAxis, var3.zAxis) + -var2.anInt2903, var3.xAxis); - } - } - - if(var2.anInt2919 < 0) { - int var4 = -1 + -var2.anInt2919; - Player var6; - if(Class3_Sub1.localIndex == var4) { - var6 = Class102.player; - } else { - var6 = Unsorted.players[var4]; - } - - if(null != var6 && var6.xAxis >= 0 && var6.xAxis < 13312 && var6.zAxis >= 0 && var6.zAxis < 13312) { - var2.method2024(var6.zAxis, Class44.anInt719, Class121.method1736(var2.anInt2907, 1, var6.xAxis, var6.zAxis) - var2.anInt2903, var6.xAxis); - } - } - - var2.method2023(Class106.anInt1446); - Class20.method907(WorldListCountry.localPlane, (int)var2.aDouble2920, (int)var2.aDouble2900, (int)var2.aDouble2914, 60, var2, var2.anInt2924, -1L, false); - } - } else { - var1.unlink(); - } - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pk.C(" + var0 + ')'); - } - } - - private void method303() { - try { - double var2 = Math.cos((float)this.anInt3354 / 4096.0F); - this.anIntArray3348[0] = (int)(4096.0D * var2 * Math.sin((float)this.anInt3347 / 4096.0F)); - this.anIntArray3348[1] = (int)(Math.cos((float)this.anInt3347 / 4096.0F) * var2 * 4096.0D); - this.anIntArray3348[2] = (int)(4096.0D * Math.sin((float)this.anInt3354 / 4096.0F)); - int var6 = this.anIntArray3348[2] * this.anIntArray3348[2] >> 12; - int var5 = this.anIntArray3348[1] * this.anIntArray3348[1] >> 12; - int var4 = this.anIntArray3348[0] * this.anIntArray3348[0] >> 12; - int var7 = (int)(4096.0D * Math.sqrt(var4 - (-var5 - var6) >> 12)); - if(var7 != 0) { - this.anIntArray3348[2] = (this.anIntArray3348[2] << 12) / var7; - this.anIntArray3348[0] = (this.anIntArray3348[0] << 12) / var7; - this.anIntArray3348[1] = (this.anIntArray3348[1] << 12) / var7; - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "pk.B(" + (byte) 59 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation33.java b/Client/src/main/java/org/runite/client/TextureOperation33.java deleted file mode 100644 index a7fa10d66..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation33.java +++ /dev/null @@ -1,216 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; -import org.rs09.client.net.game.PacketDecoder; - - -import java.io.IOException; - -public final class TextureOperation33 extends TextureOperation { - - private int anInt3047 = 4096; - static KeyboardListener aClass148_3049 = new KeyboardListener(); - private boolean aBoolean3050 = true; - - - static RSString bufferToString(byte[] bytes, int length, int offset) { - try { - RSString var4 = new RSString(); - var4.buffer = new byte[length]; - var4.length = 0; - for (int var5 = offset; var5 < length + offset; ++var5) { - if (bytes[var5] != 0) { - var4.buffer[var4.length++] = bytes[var5]; - } - } - if (var4.toString().contains("RuneScape")) { - var4 = RSString.parse(var4.toString().replace("RuneScape", GameConfig.SERVER_NAME)); - } - if (var4.toString().contains("Jagex")) { - var4 = RSString.parse(var4.toString().replace("Jagex", GameConfig.SERVER_NAME)); - } - if (var4.toString().contains("Cearch")) { - var4 = RSString.parse(var4.toString().replace("Cearch", "search")); - } - if (var4.toString().contains(">o")) { - var4 = RSString.parse(var4.toString().replace(">o", "no")); - } - return var4; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "an.B(" + (bytes != null ? "{...}" : "null") + ',' + -4114 + ',' + length + ',' + offset + ')'); - } - } - - public TextureOperation33() { - super(1, false); - } - - final void decode(int var1, DataBuffer var2) { - try { - if (0 == var1) { - this.anInt3047 = var2.readUnsignedShort(); - } else if (var1 == 1) { - this.aBoolean3050 = var2.readUnsignedByte() == 1; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "an.A(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ',' + true + ')'); - } - } - - static void method180(int var0, int var1, int var2) { - try { - int var3; - if (Class113.anInt1559 != var2) { - Class102.anIntArray2125 = new int[var2]; - - for (var3 = 0; var3 < var2; ++var3) { - Class102.anIntArray2125[var3] = (var3 << 12) / var2; - } - - Class95.anInt1343 = 64 != var2 ? 4096 : 2048; - RenderAnimationDefinition.anInt396 = -1 + var2; - Class113.anInt1559 = var2; - } - - if (Class101.anInt1427 != var1) { - if (Class113.anInt1559 == var1) { - Class163_Sub3.anIntArray2999 = Class102.anIntArray2125; - } else { - Class163_Sub3.anIntArray2999 = new int[var1]; - - for (int var4 = 0; var4 < var1; ++var4) { - Class163_Sub3.anIntArray2999[var4] = (var4 << 12) / var1; - } - } - - Class101.anInt1427 = var1; - Class3_Sub20.anInt2487 = var1 + -1; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "an.S(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - static boolean method181() { - try { -// return PacketParser.parseIncomingPackets(); - return PacketDecoder.INSTANCE.decodePacket(); - } catch (IOException var4) { - TextureOperation20.breakClientConnection(); - return true; - } catch (Exception var5) { - String var2 = "T2 - " + Unsorted.incomingOpcode + "," + Class7.anInt2166 + "," + Class24.anInt469 + " - " + Unsorted.incomingPacketLength + "," + (Class131.anInt1716 - -Class102.player.anIntArray2767[0]) + "," + (Class102.player.anIntArray2755[0] + Texture.anInt1152) + " - "; - - for (int var3 = 0; var3 < Unsorted.incomingPacketLength && 50 > var3; ++var3) { - var2 = var2 + BufferedDataStream.incomingBuffer.buffer[var3] + ","; - } - - Class49.reportError(var2, var5); - Class167.method2269((byte) 46); - return true; - } - } - - static void method182() { - try { - Texture.aReferenceCache_1146.clear(); - Class159.aReferenceCache_2016.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "an.Q(" + true + ')'); - } - } - - static void method183() { - try { - Unsorted.aReferenceCache_684.clear(); - Class163_Sub1.aReferenceCache_2984.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "an.O(" + -108 + ')'); - } - } - - static void method184(int var0, int var1, int var2, int var3, int var5, int var6) { - try { - int var11 = Class40.method1040(Class57.anInt902, var6, Class159.anInt2020); - int var12 = Class40.method1040(Class57.anInt902, var0, Class159.anInt2020); - int var13 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var3, Class101.anInt1425); - int var14 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var2, Class101.anInt1425); - - int var7 = Class40.method1040(Class57.anInt902, var6 + var1, Class159.anInt2020); - int var8 = Class40.method1040(Class57.anInt902, -var1 + var0, Class159.anInt2020); - - int var15; - for (var15 = var11; var7 > var15; ++var15) { - TextureOperation18.method282(Class38.anIntArrayArray663[var15], var13, 127, var14, var5); - } - - for (var15 = var12; var15 > var8; --var15) { - TextureOperation18.method282(Class38.anIntArrayArray663[var15], var13, -76, var14, var5); - } - - int var9 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var1 + var3, Class101.anInt1425); - int var10 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var1 + var2, Class101.anInt1425); - - for (var15 = var7; var8 >= var15; ++var15) { - int[] var16 = Class38.anIntArrayArray663[var15]; - TextureOperation18.method282(var16, var13, -59, var9, var5); - TextureOperation18.method282(var16, var10, 1 + -97, var14, var5); - } - - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "an.R(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + 1 + ',' + var5 + ',' + var6 + ')'); - } - } - - final int[][] method166(int var2) { - try { - int[][] var3 = this.aClass97_2376.method1594((byte) 58, var2); - if (this.aClass97_2376.aBoolean1379) { - int[] var4 = this.method152(0, Class3_Sub20.anInt2487 & var2 + -1); - int[] var5 = this.method152(0, var2); - int[] var6 = this.method152(0, 1 + var2 & Class3_Sub20.anInt2487); - int[] var7 = var3[0]; - int[] var8 = var3[1]; - int[] var9 = var3[2]; - - for (int var10 = 0; var10 < Class113.anInt1559; ++var10) { - int var14 = this.anInt3047 * (-var4[var10] + var6[var10]); - int var15 = this.anInt3047 * (-var5[RenderAnimationDefinition.anInt396 & -1 + var10] + var5[var10 + 1 & RenderAnimationDefinition.anInt396]); - int var17 = var14 >> 12; - int var16 = var15 >> 12; - int var19 = var17 * var17 >> 12; - int var18 = var16 * var16 >> 12; - int var20 = (int) (Math.sqrt((float) (var18 + var19 - -4096) / 4096.0F) * 4096.0D); - int var11; - int var12; - int var13; - if (0 == var20) { - var13 = 0; - var11 = 0; - var12 = 0; - } else { - var13 = 16777216 / var20; - var12 = var14 / var20; - var11 = var15 / var20; - } - - if (this.aBoolean3050) { - var12 = 2048 - -(var12 >> 1); - var13 = (var13 >> 1) + 2048; - var11 = (var11 >> 1) + 2048; - } - - var7[var10] = var11; - var8[var10] = var12; - var9[var10] = var13; - } - } - return var3; - } catch (RuntimeException var21) { - throw ClientErrorException.clientError(var21, "an.T(" + -1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation34.java b/Client/src/main/java/org/runite/client/TextureOperation34.java deleted file mode 100644 index bcaf7ac94..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation34.java +++ /dev/null @@ -1,405 +0,0 @@ -package org.runite.client; -import java.awt.Component; -import java.lang.reflect.Method; - -final class TextureOperation34 extends TextureOperation { - - int anInt3056 = 4; - int anInt3058 = 4; - private byte[] aByteArray3059 = new byte[512]; - int anInt3060 = 4; - int anInt3062 = 1638; - private short[] aShortArray3063; - static boolean aBoolean3064 = true; - boolean aBoolean3065 = true; - private short[] aShortArray3066; - int anInt3067 = 0; - - - static boolean method185(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - if(var1 < var2 && var1 < var3 && var1 < var4) { - return false; - } else if(var1 > var2 && var1 > var3 && var1 > var4) { - return false; - } else if(var0 < var5 && var0 < var6 && var0 < var7) { - return false; - } else if(var0 > var5 && var0 > var6 && var0 > var7) { - return false; - } else { - int var8 = (var1 - var2) * (var6 - var5) - (var0 - var5) * (var3 - var2); - int var9 = (var1 - var4) * (var5 - var7) - (var0 - var7) * (var2 - var4); - int var10 = (var1 - var3) * (var7 - var6) - (var0 - var6) * (var4 - var3); - return var8 * var10 > 0 && var10 * var9 > 0; - } - } - - final void method186(int var2, int[] var3) { - try { - int var5 = this.anInt3056 * Class163_Sub3.anIntArray2999[var2]; - int var4; - int var6; - int var8; - short var9; - int var10; - int var11; - int var12; - int var13; - int var14; - int var15; - int var17; - int var16; - int var18; - if(this.anInt3058 == 1) { - var9 = this.aShortArray3066[0]; - var8 = this.aShortArray3063[0] << 12; - var11 = var5 * var8 >> 12; - var12 = this.anInt3060 * var8 >> 12; - var13 = var8 * this.anInt3056 >> 12; - var15 = var11 >> 12; - var17 = this.aByteArray3059[255 & var15] & 0xFF; - var11 &= 4095; - var14 = Class1.anIntArray52[var11]; - var16 = var15 - -1; - if(var16 >= var13) { - var16 = 0; - } - - var18 = 255 & this.aByteArray3059[255 & var16]; - if(this.aBoolean3065) { - for(var10 = 0; Class113.anInt1559 > var10; ++var10) { - var4 = this.anInt3060 * Class102.anIntArray2125[var10]; - var6 = this.method192(var8 * var4 >> 12, var18, var17, var12, var11, var14); - var6 = var9 * var6 >> 12; - var3[var10] = 2048 - -(var6 >> 1); - } - } else { - for(var10 = 0; var10 < Class113.anInt1559; ++var10) { - var4 = this.anInt3060 * Class102.anIntArray2125[var10]; - var6 = this.method192(var8 * var4 >> 12, var18, var17, var12, var11, var14); - var3[var10] = var9 * var6 >> 12; - } - } - } else { - var9 = this.aShortArray3066[0]; - if(var9 > 8 || -8 > var9) { - var8 = this.aShortArray3063[0] << 12; - var13 = var8 * this.anInt3056 >> 12; - var12 = this.anInt3060 * var8 >> 12; - var11 = var5 * var8 >> 12; - var15 = var11 >> 12; - var16 = 1 + var15; - var17 = this.aByteArray3059[var15 & 0xFF] & 0xFF; - var11 &= 4095; - var14 = Class1.anIntArray52[var11]; - if(var13 <= var16) { - var16 = 0; - } - - var18 = 255 & this.aByteArray3059[255 & var16]; - - for(var10 = 0; Class113.anInt1559 > var10; ++var10) { - var4 = Class102.anIntArray2125[var10] * this.anInt3060; - var6 = this.method192(var4 * var8 >> 12, var18, var17, var12, var11, var14); - var3[var10] = var6 * var9 >> 12; - } - } - - for(int var7 = 1; this.anInt3058 > var7; ++var7) { - var9 = this.aShortArray3066[var7]; - if(8 < var9 || var9 < -8) { - var8 = this.aShortArray3063[var7] << 12; - var11 = var8 * var5 >> 12; - var15 = var11 >> 12; - var17 = this.aByteArray3059[255 & var15] & 0xFF; - var12 = this.anInt3060 * var8 >> 12; - var16 = var15 + 1; - var11 &= 4095; - var14 = Class1.anIntArray52[var11]; - var13 = this.anInt3056 * var8 >> 12; - if(var13 <= var16) { - var16 = 0; - } - - var18 = 255 & this.aByteArray3059[var16 & 0xFF]; - if(this.aBoolean3065 && this.anInt3058 + -1 == var7) { - for(var10 = 0; var10 < Class113.anInt1559; ++var10) { - var4 = Class102.anIntArray2125[var10] * this.anInt3060; - var6 = this.method192(var8 * var4 >> 12, var18, var17, var12, var11, var14); - var6 = (var9 * var6 >> 12) + var3[var10]; - var3[var10] = (var6 >> 1) + 2048; - } - } else { - for(var10 = 0; Class113.anInt1559 > var10; ++var10) { - var4 = Class102.anIntArray2125[var10] * this.anInt3060; - var6 = this.method192(var4 * var8 >> 12, var18, var17, var12, var11, var14); - var3[var10] += var6 * var9 >> 12; - } - } - } - } - } - - } catch (RuntimeException var19) { - throw ClientErrorException.clientError(var19, "bi.C(" + true + ',' + var2 + ',' + (var3 != null?"{...}":"null") + ')'); - } - } - - final void postDecode() { - try { - this.aByteArray3059 = Class49.method1123(this.anInt3067); - this.method191(); - - for(int var2 = -1 + this.anInt3058; var2 >= 1; --var2) { - short var3 = this.aShortArray3066[var2]; - if(8 < var3 || var3 < -8) { - break; - } - - --this.anInt3058; - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "bi.P(" + ')'); - } - } - - static void method189() { - try { - int var2; - - int var1; - for(var1 = -1; Class159.localPlayerCount > var1; ++var1) { - int var3; - if(var1 == -1) { - var3 = 2047; - } else { - var3 = Class56.localPlayerIndexes[var1]; - } - - Player var4 = Unsorted.players[var3]; - if(var4 != null && 0 < var4.textCycle) { - --var4.textCycle; - if(var4.textCycle == 0) { - var4.textSpoken = null; - } - } - } - - for(var1 = 0; var1 < Class163.localNPCCount; ++var1) { - var2 = AudioThread.localNPCIndexes[var1]; - NPC var6 = NPC.npcs[var2]; - if(null != var6 && var6.textCycle > 0) { - --var6.textCycle; - if(var6.textCycle == 0) { - var6.textSpoken = null; - } - } - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bi.B(" + (byte) -62 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - - if(var1 == 0) { - this.aBoolean3065 = var2.readUnsignedByte() == 1; - } else if(var1 == 1) { - this.anInt3058 = var2.readUnsignedByte(); - } else if(var1 == 2) { - this.anInt3062 = var2.readSignedShort(); - if(0 > this.anInt3062) { - this.aShortArray3066 = new short[this.anInt3058]; - - for(int var4 = 0; this.anInt3058 > var4; ++var4) { - this.aShortArray3066[var4] = (short)var2.readSignedShort(); - } - } - } else if(var1 == 3) { - this.anInt3060 = this.anInt3056 = var2.readUnsignedByte(); - } else if(var1 == 4) { - this.anInt3067 = var2.readUnsignedByte(); - } else if(var1 == 5) { - this.anInt3060 = var2.readUnsignedByte(); - } else if(var1 == 6) { - this.anInt3056 = var2.readUnsignedByte(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bi.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static int method190(int var0, int var1, byte var2, int var3) { - try { - int var5 = Class51.anIntArray834[Class140_Sub1_Sub2.method1940(var1, var3)]; - if(var0 > 0) { - int var6 = Class51.anInterface2_838.method19(111, var0 & 65535); - int var7; - int var9; - if(var6 != 0) { - if(var3 >= 0) { - if(var3 > 127) { - var7 = 16777215; - } else { - var7 = 131586 * var3; - } - } else { - var7 = 0; - } - - if(var6 == 256) { - var5 = var7; - } else { - var9 = -var6 + 256; - var5 = (16711680 & (var7 & 65280) * var6 + var9 * (var5 & 65280)) + (var6 * (var7 & 16711935) - -((16711935 & var5) * var9) & -16711936) >> 8; - } - } - - var7 = Class51.anInterface2_838.method10(106, 65535 & var0); - if(var7 != 0) { - var7 += 256; - int var8 = ((16711680 & var5) >> 16) * var7; - if(65535 < var8) { - var8 = 65535; - } - - var9 = ((var5 & 65280) >> 8) * var7; - if(var9 > 65535) { - var9 = 65535; - } - - int var10 = var7 * (var5 & 0xFF); - if(var10 > 65535) { - var10 = 65535; - } - - var5 = (var10 >> 8) + (65280 & var9) + (16711711 & var8 << 8); - } - } - - return var5; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "bi.E(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - - private void method191() { - try { - int var2; - if(this.anInt3062 <= 0) { - if(this.aShortArray3066 != null && this.aShortArray3066.length == this.anInt3058) { - this.aShortArray3063 = new short[this.anInt3058]; - - for(var2 = 0; var2 < this.anInt3058; ++var2) { - this.aShortArray3063[var2] = (short)((int)Math.pow(2.0D, var2)); - } - } - } else { - this.aShortArray3066 = new short[this.anInt3058]; - this.aShortArray3063 = new short[this.anInt3058]; - - for(var2 = 0; var2 < this.anInt3058; ++var2) { - this.aShortArray3066[var2] = (short)((int)(Math.pow((float)this.anInt3062 / 4096.0F, var2) * 4096.0D)); - this.aShortArray3063[var2] = (short)((int)Math.pow(2.0D, var2)); - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bi.F(" + true + ')'); - } - } - - private int method192(int var1, int var2, int var3, int var4, int var6, int var7) { - try { - int var10 = -4096 + var6; - int var13 = var1 >> 12; - int var12 = 1 + var13; - var13 &= 255; - if(var4 <= var12) { - var12 = 0; - } - - var1 &= 4095; - int var14 = this.aByteArray3059[var13 - -var3] & 3; - int var15 = Class1.anIntArray52[var1]; - int var8; - if(var14 > 1) { - var8 = 2 != var14?-var1 + -var6:var1 - var6; - } else { - var8 = 0 == var14?var6 + var1:-var1 + var6; - } - - var12 &= 255; - int var11 = -4096 + var1; - var14 = this.aByteArray3059[var3 + var12] & 3; - int var9; - if(var14 <= 1) { - var9 = 0 == var14?var6 + var11:-var11 + var6; - } else { - var9 = 2 == var14?-var6 + var11:-var11 + -var6; - } - - var14 = this.aByteArray3059[var13 - -var2] & 3; - int var16 = var8 + ((var9 + -var8) * var15 >> 12); - if(1 < var14) { - var8 = 2 != var14?-var1 - var10:var1 + -var10; - } else { - var8 = 0 != var14?var10 + -var1:var1 - -var10; - } - - var14 = 3 & this.aByteArray3059[var2 + var12]; - if(1 < var14) { - var9 = var14 != 2 ?-var10 + -var11:var11 - var10; - } else { - var9 = var14 == 0?var11 + var10:var10 + -var11; - } - - int var17 = var8 + ((-var8 + var9) * var15 >> 12); - return var16 - -(var7 * (var17 + -var16) >> 12); - } catch (RuntimeException var18) { - throw ClientErrorException.clientError(var18, "bi.R(" + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var6 + ',' + var7 + ')'); - } - } - - static void method193(byte var0, Component var1) { - try { - if(var0 < 49) { - Class53.worldListOffset = 85; - } - - Method var2 = Signlink.setTraversalKeysEnabled; - if(null != var2) { - try { - var2.invoke(var1, Boolean.FALSE); - } catch (Throwable var4) { - } - } - - var1.addKeyListener(TextureOperation33.aClass148_3049); - var1.addFocusListener(TextureOperation33.aClass148_3049); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bi.Q(" + var0 + ',' + (var1 != null?"{...}":"null") + ')'); - } - } - - public TextureOperation34() { - super(0, true); - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - this.method186(var1, var3); - } - - return var3; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "bi.D(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation35.java b/Client/src/main/java/org/runite/client/TextureOperation35.java deleted file mode 100644 index 581713ea8..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation35.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.runite.client; - -final class TextureOperation35 extends TextureOperation { - - static int anInt3332 = 50; - static int[] anIntArray3321 = new int[]{76, 8, 137, 4, 0, 1, 38, 2, 19}; - private int anInt3322 = 4096; - static int anInt3323 = 50; - static int[] anIntArray3328 = new int[8]; - static byte[][] aByteArrayArray3335; - - - public static void method294(byte var0) { - try { - Texture.anIntArray3327 = null; - Texture.anIntArray3337 = null; - Texture.aClass94Array3317 = null; - anIntArray3328 = null; - Texture.anIntArray3319 = null; - aByteArrayArray3335 = null; - Texture.anIntArray3329 = null; - Texture.anIntArray3336 = null; - anIntArray3321 = null; - Texture.anIntArray3318 = null; - Texture.anIntArray3331 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "pg.B(" + var0 + ')'); - } - } - - public TextureOperation35() { - super(1, true); - } - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var5 = this.method152(0, var1 - 1 & Class3_Sub20.anInt2487); - int[] var6 = this.method152(0, var1); - int[] var7 = this.method152(0, Class3_Sub20.anInt2487 & var1 + 1); - - for(int var8 = 0; Class113.anInt1559 > var8; ++var8) { - int var9 = (var7[var8] + -var5[var8]) * this.anInt3322; - int var10 = this.anInt3322 * (-var6[var8 - 1 & RenderAnimationDefinition.anInt396] + var6[RenderAnimationDefinition.anInt396 & var8 - -1]); - int var11 = var10 >> 12; - int var12 = var9 >> 12; - int var13 = var11 * var11 >> 12; - int var14 = var12 * var12 >> 12; - int var15 = (int)(Math.sqrt((float)(4096 + var14 + var13) / 4096.0F) * 4096.0D); - int var16 = 0 != var15?16777216 / var15:0; - var4[var8] = 4096 + -var16; - } - } - - return var4; - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "pg.D(" + var1 + ',' + var2 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(!true) { - method294((byte)-57); - } - - if(var1 == 0) { - this.anInt3322 = var2.readUnsignedShort(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pg.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation36.java b/Client/src/main/java/org/runite/client/TextureOperation36.java deleted file mode 100644 index 8ba725657..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation36.java +++ /dev/null @@ -1,400 +0,0 @@ -package org.runite.client; - -import org.rs09.client.config.GameConfig; - -public final class TextureOperation36 extends TextureOperation { - - static int anInt3422; - static int anInt3423; - static float aFloat3424; - static boolean aBoolean3094 = false; - private int[] anIntArray3425; - static byte[][][] aByteArrayArrayArray3430; - private int anInt3431; - private int anInt3433; - private int anInt3434 = -1; - static float aFloat3435; - - static void method1628(int var0, int var1, int var2, int var3, int var4, int var5) { - try { - int var9; - int var12; - if(Class164_Sub1.anInt3012 == 0) { - int var10 = AtmosphereParser.screenLowerY; - var9 = Class1.screenUpperY; - int var8 = Class145.screenUpperX; - int var7 = Class139.screenLowerX; - int var11 = (var5 - var3) * (-var7 + var8) / var1 - -var7; - var12 = var9 + (var10 + -var9) * (-var0 + var4) / var2; - if(GameObject.aBoolean1837 && (64 & Class164.anInt2051) != 0) { - RSInterface var13 = AbstractSprite.method638(BufferedDataStream.anInt872, RSInterface.anInt278); - if(var13 == null) { - Class25.method958((byte)-87); - } else { - Class3_Sub24_Sub4.method1177(Unsorted.anInt1887, 0L, (byte)-53, TextCore.aClass94_1724, var11, (short)11, Class3_Sub28_Sub9.aClass94_3621, var12); - } - } else { - if(Class158.paramGameTypeID == 1) { - Class3_Sub24_Sub4.method1177(-1, 0L, (byte)-62, RSString.parse(""), var11, (short)36, TextCore.HasFaceHere, var12); - } - - Class3_Sub24_Sub4.method1177(-1, 0L, (byte)-75, RSString.parse(""), var11, (short)60, TextureOperation32.aClass94_3353, var12); - } - } - - long var25 = -1L; - - for(var9 = 0; Unsorted.anInt59 > var9; ++var9) { - long var26 = TextureOperation38.aLongArray3448[var9]; - var12 = (int)var26 & 127; - int var14 = ((int)var26 & 2009320690) >> 29; - int var15 = (int)(var26 >>> 32) & Integer.MAX_VALUE; - int var27 = 127 & (int)var26 >> 7; - if(var25 != var26) { - var25 = var26; - int var18; - if(var14 == 2 && Unsorted.method2096(WorldListCountry.localPlane, var12, var27, var26)) { - ObjectDefinition var16 = ObjectDefinition.getObjectDefinition(var15); - if(null != var16.ChildrenIds) { - var16 = var16.method1685(0); - } - - if(null == var16) { - continue; - } - - if(Class164_Sub1.anInt3012 == 1) { - Class3_Sub24_Sub4.method1177(Class99.anInt1403, var26, (byte)-58, RSString.stringCombiner(new RSString[]{RenderAnimationDefinition.aClass94_378, ColorCore.PMColor, var16.name}), var12, (short)14, TextCore.HasUse, var27); - } else if(GameObject.aBoolean1837) { - Class3_Sub28_Sub9 var17 = -1 == Unsorted.anInt1038?null: LinkedList.method1210(Unsorted.anInt1038); - if(0 != (Class164.anInt2051 & 4) && (var17 == null || var17.anInt3614 != var16.method1691(var17.anInt3614, Unsorted.anInt1038, (byte) 98))) { - Class3_Sub24_Sub4.method1177(Unsorted.anInt1887, var26, (byte)-77, RSString.stringCombiner(new RSString[]{TextCore.aClass94_676, ColorCore.PMColor, var16.name}), var12, (short)38, Class3_Sub28_Sub9.aClass94_3621, var27); - } - } else { - RSString[] var29 = var16.options; - if(Class123.aBoolean1656) { - var29 = Class3_Sub31.optionsArrayStringConstructor(var29); - } - - if(var29 != null) { - for(var18 = 4; var18 >= 0; --var18) { - if(null != var29[var18]) { - short var19 = 0; - if(var18 == 0) { - var19 = 42; - } - - if(var18 == 1) { - var19 = 50; - } - - int var20 = -1; - if(2 == var18) { - var19 = 49; - } - - if(var16.anInt1493 == var18) { - var20 = var16.anInt1517; - } - - if(var18 == 3) { - var19 = 46; - } - - if(var18 == var16.anInt1520) { - var20 = var16.anInt1522; - } - - if(var18 == 4) { - var19 = 1001; - } - - - if (GameConfig.OBJECT_DEBUG_ENABLED) { - Class3_Sub24_Sub4.method1177(var20, var26, (byte) -91, RSString.stringCombiner(new RSString[]{ColorCore.ObjectNameColor, var16.name}), var12, var19, var29[var18], var27); - } else { - Class3_Sub24_Sub4.method1177(var20, var26, (byte) -91, RSString.stringCombiner(new RSString[]{ColorCore.ObjectNameColor, var16.name}), var12, var19, var29[var18], var27); - } - - } - } - } - if (GameConfig.OBJECT_DEBUG_ENABLED) { - Class3_Sub24_Sub4.method1177(Class131.anInt1719, var16.objectId, (byte) -26, RSString.stringCombiner(new RSString[]{ColorCore.ObjectNameColor, var16.name}), var12, (short) 1004, RSString.parse("Examine" + "
" + " ID: (X" + var16.objectId + "(Y"), var27); - } else { - Class3_Sub24_Sub4.method1177(Class131.anInt1719, var16.objectId, (byte) -26, RSString.stringCombiner(new RSString[]{ColorCore.ObjectNameColor, var16.name}), var12, (short) 1004, TextCore.HasExamine, var27); - } - } - } - - int var21; - int var22; - int var33; - Player var38; - NPC var36; - int var37; - if(var14 == 1) { - NPC var31 = NPC.npcs[var15]; - if((var31.definition.size & 1) == 0 && (127 & var31.xAxis) == 0 && (var31.zAxis & 127) == 0 || 1 == (var31.definition.size & 1) && (127 & var31.xAxis) == 64 && (var31.zAxis & 127) == 64) { - var33 = var31.xAxis - -64 - 64 * var31.definition.size; - var18 = -((-1 + var31.definition.size) * 64) + var31.zAxis; - - for(var37 = 0; var37 < Class163.localNPCCount; ++var37) { - var36 = NPC.npcs[AudioThread.localNPCIndexes[var37]]; - var21 = -(var36.definition.size * 64) - -64 + var36.xAxis; - var22 = var36.zAxis + -(var36.definition.size * 64) - -64; - if(var31 != var36 && var33 <= var21 && var31.definition.size - (-var33 + var21 >> 7) >= var36.definition.size && var18 <= var22 && var36.definition.size <= -(-var18 + var22 >> 7) + var31.definition.size) { - Unsorted.drawNpcRightClickOptions(var36.definition, var12, -126, AudioThread.localNPCIndexes[var37], var27); - } - } - - for(var37 = 0; var37 < Class159.localPlayerCount; ++var37) { - var38 = Unsorted.players[Class56.localPlayerIndexes[var37]]; - var21 = var38.xAxis + 64 + -(64 * var38.getSize()); - var22 = var38.zAxis - (var38.getSize() * 64 + -64); - if(var21 >= var33 && var31.definition.size - (var21 - var33 >> 7) >= var38.getSize() && var18 <= var22 && var38.getSize() <= -(-var18 + var22 >> 7) + var31.definition.size) { - TextureOperation13.method312(Class56.localPlayerIndexes[var37], 5, var27, var38, var12); - } - } - } - - Unsorted.drawNpcRightClickOptions(var31.definition, var12, -108, var15, var27); - } - - if(var14 == 0) { - Player var30 = Unsorted.players[var15]; - if((127 & var30.xAxis) == 64 && 64 == (127 & var30.zAxis)) { - var33 = var30.xAxis + -(64 * (-1 + var30.getSize())); - var18 = var30.zAxis + 64 + -(var30.getSize() * 64); - - for(var37 = 0; var37 < Class163.localNPCCount; ++var37) { - var36 = NPC.npcs[AudioThread.localNPCIndexes[var37]]; - var21 = var36.xAxis + -(var36.definition.size * 64) - -64; - var22 = var36.zAxis - 64 * var36.definition.size - -64; - if(var21 >= var33 && var36.definition.size <= -(var21 - var33 >> 7) + var30.getSize() && var18 <= var22 && -(-var18 + var22 >> 7) + var30.getSize() >= var36.definition.size) { - Unsorted.drawNpcRightClickOptions(var36.definition, var12, -121, AudioThread.localNPCIndexes[var37], var27); - } - } - - for(var37 = 0; var37 < Class159.localPlayerCount; ++var37) { - var38 = Unsorted.players[Class56.localPlayerIndexes[var37]]; - var21 = var38.xAxis - (var38.getSize() + -1) * 64; - var22 = var38.zAxis - (-64 + 64 * var38.getSize()); - if(var38 != var30 && var21 >= var33 && var38.getSize() <= var30.getSize() - (var21 - var33 >> 7) && var18 <= var22 && -(var22 + -var18 >> 7) + var30.getSize() >= var38.getSize()) { - TextureOperation13.method312(Class56.localPlayerIndexes[var37], 9, var27, var38, var12); - } - } - } - - TextureOperation13.method312(var15, 31, var27, var30, var12); - } - - if(var14 == 3) { - LinkedList var28 = Class39.aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var12][var27]; - if(null != var28) { - for(WorldMap var32 = (WorldMap)var28.method1212(); null != var32; var32 = (WorldMap)var28.method1219(41)) { - var18 = var32.aClass140_Sub7_3676.anInt2936; - ItemDefinition var40 = ItemDefinition.getItemDefinition(var18); - if(Class164_Sub1.anInt3012 == 1) { - Class3_Sub24_Sub4.method1177(Class99.anInt1403, var18, (byte)-75, RSString.stringCombiner(new RSString[]{RenderAnimationDefinition.aClass94_378, ColorCore.BankItemColor, var40.name}), var12, (short)33, TextCore.HasUse, var27); - } else if(GameObject.aBoolean1837) { - Class3_Sub28_Sub9 var39 = Unsorted.anInt1038 == -1?null: LinkedList.method1210(Unsorted.anInt1038); - if((Class164.anInt2051 & 1) != 0 && (null == var39 || var39.anInt3614 != var40.method1115(var39.anInt3614, 100, Unsorted.anInt1038))) { - Class3_Sub24_Sub4.method1177(Unsorted.anInt1887, var18, (byte)-70, RSString.stringCombiner(new RSString[]{TextCore.aClass94_676, ColorCore.BankItemColor, var40.name}), var12, (short)39, Class3_Sub28_Sub9.aClass94_3621, var27); - } - } else { - RSString[] var34 = var40.groundOptions; - if(Class123.aBoolean1656) { - var34 = Class3_Sub31.optionsArrayStringConstructor(var34); - } - - for(var21 = 4; var21 >= 0; --var21) { - if(var34 != null && null != var34[var21]) { - byte var35 = 0; - if(var21 == 0) { - var35 = 21; - } - - if(1 == var21) { - var35 = 34; - } - - int var23 = -1; - if(var40.anInt767 == var21) { - var23 = var40.anInt758; - } - - if(var21 == 2) { - var35 = 18; - } - - if(var40.anInt788 == var21) { - var23 = var40.anInt756; - } - - if(var21 == 3) { - var35 = 20; - } - - if(var21 == 4) { - var35 = 24; - } - - Class3_Sub24_Sub4.method1177(var23, var18, (byte)-43, RSString.stringCombiner(new RSString[]{ColorCore.GroundItemColor, var40.name}), var12, var35, var34[var21], var27); - } - } - if (GameConfig.ITEM_DEBUG_ENABLED) { - Class3_Sub24_Sub4.method1177(Class131.anInt1719, var18, (byte) -43, RSString.stringCombiner(new RSString[]{ColorCore.GroundItemColor, var40.name}), var12, (short) 1002, RSString.parse("Examine" + "
" + " ID: (X" + var40.itemId + "(Y"), var27); - } else { - Class3_Sub24_Sub4.method1177(Class131.anInt1719, var18, (byte) -43, RSString.stringCombiner(new RSString[]{ColorCore.GroundItemColor, var40.name}), var12, (short) 1002, TextCore.HasExamine, var27); - } - } - } - } - } - } - } - - } catch (RuntimeException var24) { - throw ClientErrorException.clientError(var24, "ob.K(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + (byte) 97 + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 != -1) { - this.anInt3434 = 6; - } - - int[][] var3 = this.aClass97_2376.method1594((byte)-120, var2); - if(this.aClass97_2376.aBoolean1379 && this.method339()) { - int var4 = (this.anInt3433 != Class101.anInt1427?this.anInt3433 * var2 / Class101.anInt1427:var2) * this.anInt3431; - int[] var5 = var3[0]; - int[] var6 = var3[1]; - int[] var7 = var3[2]; - int var8; - int var9; - if(this.anInt3431 == Class113.anInt1559) { - for(var8 = 0; var8 < Class113.anInt1559; ++var8) { - var9 = this.anIntArray3425[var4++]; - var7[var8] = Unsorted.bitwiseAnd(var9 << 4, 4080); - var6[var8] = Unsorted.bitwiseAnd(var9, 65280) >> 4; - var5[var8] = Unsorted.bitwiseAnd(var9, 16711680) >> 12; - } - } else { - for(var8 = 0; var8 < Class113.anInt1559; ++var8) { - var9 = this.anInt3431 * var8 / Class113.anInt1559; - int var10 = this.anIntArray3425[var4 - -var9]; - var7[var8] = Unsorted.bitwiseAnd(var10 << 4, 4080); - var6[var8] = Unsorted.bitwiseAnd(65280, var10) >> 4; - var5[var8] = Unsorted.bitwiseAnd(var10 >> 12, 4080); - } - } - } - - return var3; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "ui.T(" + -1 + ',' + var2 + ')'); - } - } - - private boolean method339() { - try { - if(this.anIntArray3425 == null) { - if(this.anInt3434 < 0) { - return false; - } else { - int var2 = Class113.anInt1559; - int var3 = Class101.anInt1427; - int var4 = !Class17.anInterface2_408.method14((byte)-104, this.anInt3434)?128:64; - this.anIntArray3425 = Class17.anInterface2_408.method16(64, this.anInt3434); - this.anInt3433 = var4; - this.anInt3431 = var4; - TextureOperation33.method180(18, var3, var2); - return this.anIntArray3425 != null; - } - } else { - return true; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ui.LA(" + false + ')'); - } - } - - static int method340(int var0) { - try { - return var0 >>> 8; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ui.NA(" + var0 + ',' + -51 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(!true) { - CacheIndex.animationIndex = null; - } - - if(0 == var1) { - this.anInt3434 = var2.readUnsignedShort(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ui.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - public TextureOperation36() { - super(0, false); - } - - final void method161(byte var1) { - try { - super.method161(var1); - this.anIntArray3425 = null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ui.BA(" + var1 + ')'); - } - } - - static AbstractIndexedSprite[] method343() { - try { - AbstractIndexedSprite[] var1 = new AbstractIndexedSprite[Class95.anInt1338]; - - for(int var2 = 0; var2 < Class95.anInt1338; ++var2) { - if(HDToolKit.highDetail) { - var1[var2] = new HDIndexedSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], Class163_Sub1.aByteArrayArray2987[var2], TextureOperation38.spritePalette); - } else { - var1[var2] = new LDIndexedSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], Unsorted.anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], Unsorted.anIntArray3076[var2], Class163_Sub1.aByteArrayArray2987[var2], TextureOperation38.spritePalette); - } - } - Class39.method1035((byte)113); - return var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ui.JA(" + 1854847236 + ')'); - } - } - - final int getSpriteFrame() { - try { - return this.anInt3434; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ui.HA(" + (byte) 19 + ')'); - } - } - - static void method344(int var0, int var1) { - try { - if(0 <= var0 && Class3_Sub24_Sub4.aBooleanArray3503.length > var0) { - Class3_Sub24_Sub4.aBooleanArray3503[var0] = !Class3_Sub24_Sub4.aBooleanArray3503[var0]; - if(var1 != 4) { - aByteArrayArrayArray3430 = null; - } - - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ui.KA(" + var0 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation37.java b/Client/src/main/java/org/runite/client/TextureOperation37.java deleted file mode 100644 index f8dd00007..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation37.java +++ /dev/null @@ -1,308 +0,0 @@ -package org.runite.client; -import org.rs09.client.config.GameConfig; -import org.rs09.client.net.Connection; - -import java.io.IOException; -import java.net.Socket; -import java.util.Objects; - -final class TextureOperation37 extends TextureOperation { -//Class3_Sub13_Sub21 - private int anInt3253 = 0; - private int anInt3254 = 4096; - static int anInt3256; - private int anInt3257 = 12288; - private int anInt3258 = 0; - static int anInt3260 = -1; - static boolean aBoolean3261 = false; - private int anInt3262 = 2048; - static int anInt3263 = 0; - static Class3_Sub28_Sub3 aClass3_Sub28_Sub3_3264; - private int anInt3265 = 2048; - private int anInt3266 = 8192; - - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3265 = var2.readUnsignedShort(); - } else if(var1 == 1) { - this.anInt3253 = var2.readUnsignedShort(); - } else if(var1 == 2) { - this.anInt3258 = var2.readUnsignedShort(); - } else if(var1 == 3) { - this.anInt3262 = var2.readUnsignedShort(); - } else if (var1 == 4) { - this.anInt3257 = var2.readUnsignedShort(); - } else if (var1 == 5) { - this.anInt3254 = var2.readUnsignedShort(); - } else if (var1 == 6) { - this.anInt3266 = var2.readUnsignedShort(); - } - - if(!true) { - this.anInt3266 = 85; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "mh.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static void method267() { - try { - if(Unsorted.registryStage != 0) { - try { - if(++Class132.anInt1734 > 2000) { - if(null != Class3_Sub15.activeConnection) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - if(GraphicDefinition.anInt548 >= 1) { - Unsorted.anInt1711 = -5; - Unsorted.registryStage = 0; - return; - } - - Unsorted.registryStage = 1; - Class132.anInt1734 = 0; - ++GraphicDefinition.anInt548; - if(Class140_Sub6.accRegistryPort == Class162.anInt2036) { - Class140_Sub6.accRegistryPort = Client.currentPort; - } else { - Class140_Sub6.accRegistryPort = Class162.anInt2036; - } - } - - if(Unsorted.registryStage == 1) { - //Ip & Port - Class3_Sub9.aClass64_2318 = Class38.gameSignlink.method1441((byte)8, Class38_Sub1.accRegistryIp, Class140_Sub6.accRegistryPort); - Unsorted.registryStage = 2; - } - - int response; - if(Unsorted.registryStage == 2) { - if(Objects.requireNonNull(Class3_Sub9.aClass64_2318).anInt978 == 2) { - throw new IOException(); - } - if(1 != Class3_Sub9.aClass64_2318.anInt978) { - return; - } - Class3_Sub15.activeConnection = new Connection((Socket)Class3_Sub9.aClass64_2318.anObject974, Class38.gameSignlink); - Class3_Sub9.aClass64_2318 = null; - Class3_Sub15.activeConnection.sendBytes(TextureOperation12.outgoingBuffer.buffer, TextureOperation12.outgoingBuffer.index); - if(WorldListEntry.aAudioChannel_2627 != null) { - WorldListEntry.aAudioChannel_2627.method2159(); - } - if(null != Class3_Sub21.aAudioChannel_2491) { - Class3_Sub21.aAudioChannel_2491.method2159(); - } - response = Class3_Sub15.activeConnection.readByte(); - System.out.println("Response = " + response); - if(WorldListEntry.aAudioChannel_2627 != null) { - WorldListEntry.aAudioChannel_2627.method2159(); - } - if(Class3_Sub21.aAudioChannel_2491 != null) { - Class3_Sub21.aAudioChannel_2491.method2159(); - } - if(response != 21) { - Unsorted.anInt1711 = response; - Unsorted.registryStage = 0; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - Unsorted.registryStage = 3; - } - - if(3 == Unsorted.registryStage) { - if(Class3_Sub15.activeConnection.availableBytes() < 1) { - return; - } - - TextureOperation29.aClass94Array3391 = new RSString[Class3_Sub15.activeConnection.readByte()]; - Unsorted.registryStage = 4; - } - - if(Unsorted.registryStage == 4) { - if(8 * TextureOperation29.aClass94Array3391.length > Class3_Sub15.activeConnection.availableBytes()) { - return; - } - - BufferedDataStream.incomingBuffer.index = 0; - Class3_Sub15.activeConnection.readBytes(BufferedDataStream.incomingBuffer.buffer, 0, 8 * TextureOperation29.aClass94Array3391.length); - - for(response = 0; response < TextureOperation29.aClass94Array3391.length; ++response) { - TextureOperation29.aClass94Array3391[response] = Unsorted.method1052(BufferedDataStream.incomingBuffer.readLong()); - } - - Unsorted.anInt1711 = 21; - Unsorted.registryStage = 0; - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - return; - } - } catch (IOException var2) { - if(Class3_Sub15.activeConnection != null) { - Class3_Sub15.activeConnection.close(); - Class3_Sub15.activeConnection = null; - } - - if(GraphicDefinition.anInt548 < 1) { - ++GraphicDefinition.anInt548; - if(Class162.anInt2036 == Class140_Sub6.accRegistryPort) { - Class140_Sub6.accRegistryPort = Client.currentPort; - } else { - Class140_Sub6.accRegistryPort = Class162.anInt2036; - } - - Class132.anInt1734 = 0; - Unsorted.registryStage = 1; - } else { - Unsorted.anInt1711 = -4; - Unsorted.registryStage = 0; - } - } - - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "mh.Q(" + (byte) 36 + ')'); - } - } - - public TextureOperation37() { - super(0, true); - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var5 = -2048 + Class163_Sub3.anIntArray2999[var1]; - - for(int var6 = 0; var6 < Class113.anInt1559; ++var6) { - int var9 = var5 + this.anInt3253; - int var7 = Class102.anIntArray2125[var6] + -2048; - int var8 = this.anInt3265 + var7; - var9 = var9 < -2048 ?var9 - -4096:var9; - var9 = 2048 < var9?-4096 + var9:var9; - int var10 = var7 + this.anInt3258; - var8 = var8 < -2048?var8 + 4096:var8; - var8 = var8 <= 2048 ?var8:-4096 + var8; - var10 = var10 >= -2048 ?var10:4096 + var10; - var10 = var10 > 2048 ?var10 - 4096:var10; - int var11 = var5 - -this.anInt3262; - var11 = -2048 > var11?var11 + 4096:var11; - var11 = var11 > 2048 ?var11 - 4096:var11; - var3[var6] = !this.method271(var8, var9) && !this.method270(var10, var11)?0:4096; - } - } - - return var3; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "mh.D(" + var1 + ',' + var2 + ')'); - } - } - - private boolean method270(int var2, int var3) { - try { - int var4 = this.anInt3257 * (var2 + var3) >> 12; - - int var5 = Class75_Sub2.anIntArray2639[var4 * 255 >> 12 & 0xFF]; - var5 = (var5 << 12) / this.anInt3257; - var5 = (var5 << 12) / this.anInt3266; - var5 = var5 * this.anInt3254 >> 12; - return var5 > -var2 + var3 && var3 + -var2 > -var5; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "mh.S(" + (byte) -44 + ',' + var2 + ',' + var3 + ')'); - } - } - - final void postDecode() { - try { - Class8.method844((byte)-9); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "mh.P(" + ')'); - } - } - - private boolean method271(int var1, int var2) { - try { - int var4 = (var2 - var1) * this.anInt3257 >> 12; - - int var5 = Class75_Sub2.anIntArray2639[(1047948 & var4 * 255) >> 12]; - var5 = (var5 << 12) / this.anInt3257; - var5 = (var5 << 12) / this.anInt3266; - var5 = var5 * this.anInt3254 >> 12; - return var5 > var2 + var1 && -var5 < var2 + var1; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "mh.F(" + var1 + ',' + var2 + ',' + (byte) 113 + ')'); - } - } - - static void displayNPCOnLoginRegion() { - try { - - int var1 = TextureOperation35.aByteArrayArray3335.length; - - for(int var2 = 0; var2 < var1; ++var2) { - if(TextureOperation35.aByteArrayArray3335[var2] != null) { - int var3 = -1; - - for(int var4 = 0; TextureOperation11.anInt3244 > var4; ++var4) { - if(TextureOperation13.anIntArray3367[var4] == Class3_Sub24_Sub3.anIntArray3494[var2]) { - var3 = var4; - break; - } - } - - if(var3 == -1) { - TextureOperation13.anIntArray3367[TextureOperation11.anInt3244] = Class3_Sub24_Sub3.anIntArray3494[var2]; - var3 = TextureOperation11.anInt3244++; - } - - int var5 = 0; - DataBuffer var16 = new DataBuffer(TextureOperation35.aByteArrayArray3335[var2]); - - while(TextureOperation35.aByteArrayArray3335[var2].length > var16.index && 511 > var5) { - int var6 = var5++ << 6 | var3; - int var7 = var16.readUnsignedShort(); - int var8 = var7 >> 14; - int var9 = 63 & var7 >> 7; - int var11 = var9 + 64 * (Class3_Sub24_Sub3.anIntArray3494[var2] >> 8) - Class131.anInt1716; - int var10 = var7 & 63; - int var12 = var10 + -Texture.anInt1152 + 64 * (255 & Class3_Sub24_Sub3.anIntArray3494[var2]); - int npcID = var16.readUnsignedShort(); - - if (GameConfig.EASTER_EVENT_ENABLED) { - npcID = 1321; - } - - NPCDefinition var13 = NPCDefinition.getNPCDefinition(npcID); - if(NPC.npcs[var6] == null && (var13.aByte1267 & 1) > 0 && Class140_Sub3.anInt2745 == var8 && var11 >= 0 && 104 > var13.size + var11 && var12 >= 0 && 104 > var12 - -var13.size) { - NPC.npcs[var6] = new NPC(); - NPC npc = NPC.npcs[var6]; - AudioThread.localNPCIndexes[Class163.localNPCCount++] = var6; - npc.anInt2838 = Class44.anInt719; - npc.setDefinitions(var13); - npc.setSize(npc.definition.size, 2); - npc.anInt2806 = npc.anInt2785 = Class27.anIntArray510[npc.definition.aByte1268]; - npc.anInt2779 = npc.definition.anInt1274; - if(npc.anInt2779 == 0) { - npc.anInt2785 = 0; - } - - npc.renderAnimationId = npc.definition.renderAnimationId; - npc.method1967(npc.getSize(), var11, var12, true); - } - } - } - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "mh.E(" + (byte) -124 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation38.java b/Client/src/main/java/org/runite/client/TextureOperation38.java deleted file mode 100644 index b5e38cc1f..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation38.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.runite.client; -import java.util.Objects; -import java.util.Random; - -final class TextureOperation38 extends TextureOperation { - - private int anInt3444 = 0; - - static int[] spritePalette; - private int anInt3447 = 2000; - static long[] aLongArray3448 = new long[1000]; - private int anInt3450 = 4096; - private int anInt3451 = 16; - static short[] aShortArray3453 = new short[256]; - private int anInt3454 = 0; - static short[] aShortArray3455; - static int[] anIntArray3456 = new int[4096]; - - final void postDecode() { - try { - Class8.method844((byte)-9); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "vc.P(" + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3454 = var2.readUnsignedByte(); - } else if(var1 == 1) { - this.anInt3447 = var2.readUnsignedShort(); - } else if(var1 == 2) { - this.anInt3451 = var2.readUnsignedByte(); - } else if (3 == var1) { - this.anInt3444 = var2.readUnsignedShort(); - } else if (var1 == 4) { - this.anInt3450 = var2.readUnsignedShort(); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "vc.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - public TextureOperation38() { - super(0, true); - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var5 = this.anInt3450 >> 1; - int[][] var6 = this.aClass114_2382.method1710((byte)93); - Random var7 = new Random(this.anInt3454); - - for(int var8 = 0; this.anInt3447 > var8; ++var8) { - int var9 = this.anInt3450 > 0?this.anInt3444 + -var5 + TextureOperation.method1603((byte)-99, this.anInt3450, var7):this.anInt3444; - int var10 = TextureOperation.method1603((byte)-96, Class113.anInt1559, var7); - var9 = (var9 & 4088) >> 4; - int var11 = TextureOperation.method1603((byte)62, Class101.anInt1427, var7); - int var12 = var10 - -(this.anInt3451 * Class75_Sub2.anIntArray2639[var9] >> 12); - int var13 = var11 + (TextureOperation23.anIntArray3212[var9] * this.anInt3451 >> 12); - int var15 = var12 - var10; - int var14 = -var11 + var13; - if(var15 != 0 || var14 != 0) { - if(var15 < 0) { - var15 = -var15; - } - - if(var14 < 0) { - var14 = -var14; - } - - boolean var16 = var14 > var15; - int var17; - int var18; - if(var16) { - var17 = var10; - var18 = var12; - var12 = var13; - var13 = var18; - var10 = var11; - var11 = var17; - } - - if(var12 < var10) { - var17 = var10; - var18 = var11; - var10 = var12; - var11 = var13; - var13 = var18; - var12 = var17; - } - - var18 = -var10 + var12; - int var19 = var13 + -var11; - var17 = var11; - if(var19 < 0) { - var19 = -var19; - } - - int var20 = -var18 / 2; - int var22 = -(TextureOperation.method1603((byte)-18, 4096, var7) >> 2) + 1024; - int var23 = var11 >= var13 ?-1:1; - int var21 = 2048 / var18; - - for(int var24 = var10; var12 > var24; ++var24) { - var20 += var19; - int var25 = var21 * (-var10 + var24) + var22 + 1024; - int var27 = var17 & Class3_Sub20.anInt2487; - if(0 < var20) { - var20 += -var18; - var17 += var23; - } - - int var26 = RenderAnimationDefinition.anInt396 & var24; - if(var16) { - Objects.requireNonNull(var6)[var27][var26] = var25; - } else { - Objects.requireNonNull(var6)[var26][var27] = var25; - } - } - } - } - } - - return var3; - } catch (RuntimeException var28) { - throw ClientErrorException.clientError(var28, "vc.D(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation39.java b/Client/src/main/java/org/runite/client/TextureOperation39.java deleted file mode 100644 index 1eedc6e60..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation39.java +++ /dev/null @@ -1,178 +0,0 @@ -package org.runite.client; - -import org.rs09.CustomVars; - -import java.util.Objects; - -class TextureOperation39 extends TextureOperation { - - private int anInt3278 = -1; - int anInt3280; - int anInt3283; - int[] anIntArray3284; - static int itemDefinitionSize; - - static int method275(int var0, int var1, int var2, int var3) { - try { - int var5 = -Class51.anIntArray851[1024 * var2 / var3] + 65536 >> 1; - return (var0 * (-var5 + 65536) >> 16) + (var1 * var5 >> 16); - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "nh.CA(" + var0 + ',' + var1 + ',' + var2 + ',' + ',' + var3 + ')'); - } - } - - static void method276(int var0, int var1, int var2, int var3, GameObject var4, long var5, boolean var7) { - if(var4 != null) { - Class12 var8 = new Class12(); - var8.object = var4; - var8.anInt324 = var1 * 128 + 64; - var8.anInt330 = var2 * 128 + 64; - var8.anInt326 = var3; - var8.aLong328 = var5; - var8.aBoolean329 = var7; - if(Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2] == null) { - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2] = new Class3_Sub2(var0, var1, var2); - } - - Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2].aClass12_2230 = var8; - } - } - - static boolean handleWorldListUpdate(byte[] buf) { - try { - DataBuffer buffer = new DataBuffer(buf); - int opcode = buffer.readUnsignedByte(); - //System.out.println(opcode); - if(1 == opcode) { - boolean updated = buffer.readUnsignedByte() == 1; - if(updated) { - WorldListEntry.parseWorldList(buffer); - } - - TextureOperation30.method216(buffer); - return true; - } else { - return false; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "nh.AA(" + 4 + ',' + (buf != null?"{...}":"null") + ')'); - } - } - - final boolean method279(int var1) { - try { - if(null == this.anIntArray3284) { - if(this.anInt3278 < 0) { - return false; - } else { - SoftwareSprite var3 = Texture.anInt1668 < 0 ? Unsorted.method1537(WaterfallShader.aClass153_2172, this.anInt3278):Class40.method1043(this.anInt3278, WaterfallShader.aClass153_2172, Texture.anInt1668); - Objects.requireNonNull(var3).method665(); - this.anInt3283 = var3.height; - this.anInt3280 = var3.width; - this.anIntArray3284 = var3.anIntArray4081; - return true; - } - } else { - return true; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nh.FA(" + var1 + ')'); - } - } - - final int method159(int var1) { - try { - return var1 != 4?40:this.anInt3278; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nh.GA(" + var1 + ')'); - } - } - - public TextureOperation39() { - super(0, false); - } - - static void method280(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9, int var11, int var12) { - try { - Class3_Sub5 var13 = new Class3_Sub5(); - var13.anInt2284 = var6; - var13.anInt2283 = var3; - var13.anInt2266 = var1; - var13.anInt2279 = var5; - var13.anInt2273 = var2; - var13.anInt2271 = var8; - var13.anInt2277 = var11; - var13.anInt2282 = var4; - var13.anInt2270 = var12; - var13.anInt2268 = var7; - var13.anInt2272 = var0; - var13.anInt2278 = var9; - Unsorted.aLinkedList_2468.method1215(var13); - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "nh.V(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + -745213428 + ',' + var11 + ',' + var12 + ')'); - } - } - - int[][] method166(int var2) { - try { - if(-1 != -1) { - this.method159(32); - } - - int[][] var3 = this.aClass97_2376.method1594((byte)65, var2); - if(this.aClass97_2376.aBoolean1379 && this.method279(-113)) { - int[] var4 = var3[0]; - int[] var5 = var3[1]; - int[] var6 = var3[2]; - int var7 = (Class101.anInt1427 == this.anInt3283 ?var2:this.anInt3283 * var2 / Class101.anInt1427) * this.anInt3280; - int var8; - int var9; - if(Class113.anInt1559 == this.anInt3280) { - for(var8 = 0; var8 < Class113.anInt1559; ++var8) { - var9 = this.anIntArray3284[var7++]; - var6[var8] = Unsorted.bitwiseAnd(255, var9) << 4; - var5[var8] = Unsorted.bitwiseAnd(65280, var9) >> 4; - var4[var8] = Unsorted.bitwiseAnd(var9, 16711680) >> 12; - } - } else { - for(var8 = 0; Class113.anInt1559 > var8; ++var8) { - var9 = this.anInt3280 * var8 / Class113.anInt1559; - int var10 = this.anIntArray3284[var7 - -var9]; - var6[var8] = Unsorted.bitwiseAnd(var10 << 4, 4080); - var5[var8] = Unsorted.bitwiseAnd(var10, 65280) >> 4; - var4[var8] = Unsorted.bitwiseAnd(var10 >> 12, 4080); - } - } - } - - return var3; - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "nh.T(" + -1 + ',' + var2 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3278 = var2.readUnsignedShort(); - } - - if(!true) { - method276(115, 107, 22, 20, null, 87L, false); - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "nh.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - final void method161(byte var1) { - try { - super.method161(var1); - this.anIntArray3284 = null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "nh.BA(" + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation4.java b/Client/src/main/java/org/runite/client/TextureOperation4.java deleted file mode 100644 index 105a2984f..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation4.java +++ /dev/null @@ -1,249 +0,0 @@ -package org.runite.client; -import org.rs09.client.util.ArrayUtils; - -import java.util.Random; - -final class TextureOperation4 extends TextureOperation { - - private int anInt3219 = 204; - private int anInt3223; - private int anInt3224 = 81; - private int[][] anIntArrayArray3225; - static CacheIndex aClass153_3227; - private int anInt3229 = 1024; - private int[] anIntArray3230; - private int anInt3231 = 0; - private static RSString aClass94_3232 = RSString.parse("pt"); - private int anInt3233 = 8; - private int anInt3234 = 1024; - private int anInt3235; - private int anInt3236 = 409; - private static final RSString aClass94_3237 = RSString.parse("en"); - private static RSString aClass94_3239 = RSString.parse("fr"); - private int[][] anIntArrayArray3240; - private int anInt3242 = 4; - private static final RSString aClass94_3243 = RSString.parse("de"); - static RSString[] aClass94Array3238 = new RSString[]{aClass94_3237, aClass94_3243, aClass94_3239, aClass94_3232}; - - - final void postDecode() { - try { - this.method263(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "mc.P(" + ')'); - } - } - - static void method260(int var1, int var2) { - try { - - InterfaceWidget var3 = InterfaceWidget.getWidget(7, var1); - var3.flagUpdate(); - var3.anInt3598 = var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "mc.O(" + -16207 + ',' + var1 + ',' + var2 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(0 == var1) { - this.anInt3242 = var2.readUnsignedByte(); - } else if (var1 == 1) { - this.anInt3233 = var2.readUnsignedByte(); - } else if (var1 == 2) { - this.anInt3236 = var2.readUnsignedShort(); - } else if (var1 == 3) { - this.anInt3219 = var2.readUnsignedShort(); - } else if (4 == var1) { - this.anInt3234 = var2.readUnsignedShort(); - } else if (var1 == 5) { - this.anInt3231 = var2.readUnsignedShort(); - } else if (var1 == 6) { - this.anInt3224 = var2.readUnsignedShort(); - } else if (var1 == 7) { - this.anInt3229 = var2.readUnsignedShort(); - } - - if(!true) { - aClass94_3239 = null; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "mc.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - static void method262(int var0, int var2, int var3, int var4, int var5, int var6, int var7) { - try { - int var8 = var5 + var2; - int var10 = var5 + var7; - - int var12; - for(var12 = var2; var12 < var8; ++var12) { - TextureOperation18.method282(Class38.anIntArrayArray663[var12], var7, 125, var6, var0); - } - - int var9 = -var5 + var3; - int var11 = -var5 + var6; - - for(var12 = var3; var12 > var9; --var12) { - TextureOperation18.method282(Class38.anIntArrayArray663[var12], var7, 103, var6, var0); - } - - for(var12 = var8; var12 <= var9; ++var12) { - int[] var13 = Class38.anIntArrayArray663[var12]; - TextureOperation18.method282(var13, var7, 117, var10, var0); - TextureOperation18.method282(var13, var10, 111, var11, var4); - TextureOperation18.method282(var13, var11, -75, var6, var0); - } - - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "mc.Q(" + var0 + ',' + 119 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - private void method263() { - try { - Random var2 = new Random(this.anInt3233); - int anInt3222 = 4096 / this.anInt3233; - this.anInt3223 = this.anInt3224 / 2; - this.anIntArrayArray3225 = new int[this.anInt3233][1 + this.anInt3242]; - int var4 = anInt3222 / 2; - this.anIntArray3230 = new int[this.anInt3233 - -1]; - this.anIntArrayArray3240 = new int[this.anInt3233][this.anInt3242]; - this.anInt3235 = 4096 / this.anInt3242; - this.anIntArray3230[0] = 0; - int var3 = this.anInt3235 / 2; - - for(int var5 = 0; this.anInt3233 > var5; ++var5) { - int var6; - int var7; - if(var5 > 0) { - var6 = anInt3222; - var7 = (TextureOperation.method1603((byte)59, 4096, var2) + -2048) * this.anInt3219 >> 12; - var6 += var7 * var4 >> 12; - this.anIntArray3230[var5] = this.anIntArray3230[var5 - 1] - -var6; - } - - this.anIntArrayArray3225[var5][0] = 0; - - for(var6 = 0; this.anInt3242 > var6; ++var6) { - if(0 < var6) { - var7 = this.anInt3235; - int var8 = (-2048 + TextureOperation.method1603((byte)-1, 4096, var2)) * this.anInt3236 >> 12; - var7 += var3 * var8 >> 12; - this.anIntArrayArray3225[var5][var6] = this.anIntArrayArray3225[var5][var6 + -1] - -var7; - } - - this.anIntArrayArray3240[var5][var6] = this.anInt3229 <= 0 ?4096:4096 + -TextureOperation.method1603((byte)33, this.anInt3229, var2); - } - - this.anIntArrayArray3225[var5][this.anInt3242] = 4096; - } - - this.anIntArray3230[this.anInt3233] = 4096; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "mc.E(" + 0 + ')'); - } - } - - static void method264(byte var0) { - try { - TextureOperation12.outgoingBuffer.putOpcode(184); - - for(Class3_Sub31 var1 = TextureOperation23.aHashTable_3208.first(); null != var1; var1 = TextureOperation23.aHashTable_3208.next()) { - if(var1.anInt2603 == 0) { - TextureOperation19.method254(true, var1); - } - } - - if(var0 < 83) { - aClass94_3232 = null; - } - - if(null != TextureOperation27.aClass11_3087) { - Class20.method909(TextureOperation27.aClass11_3087); - TextureOperation27.aClass11_3087 = null; - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "mc.C(" + var0 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - int var4; - if(this.aClass114_2382.aBoolean1580) { - int var7 = 0; - - int var9; - for(var9 = Class163_Sub3.anIntArray2999[var1] + this.anInt3231; var9 < 0; var9 += 4096) { - } - - while(4096 < var9) { - var9 -= 4096; - } - - while(this.anInt3233 > var7 && this.anIntArray3230[var7] <= var9) { - ++var7; - } - - int var11 = var7 + -1; - int var15 = this.anIntArray3230[var7]; - boolean var12 = 0 == (var7 & 1); - int var16 = this.anIntArray3230[var7 - 1]; - if(var16 - -this.anInt3223 < var9 && var9 < var15 - this.anInt3223) { - for(var4 = 0; var4 < Class113.anInt1559; ++var4) { - int var6 = 0; - int var5 = !var12?-this.anInt3234:this.anInt3234; - - int var8; - for(var8 = Class102.anIntArray2125[var4] - -(this.anInt3235 * var5 >> 12); var8 < 0; var8 += 4096) { - } - - while(var8 > 4096) { - var8 -= 4096; - } - - while(var6 < this.anInt3242 && this.anIntArrayArray3225[var11][var6] <= var8) { - ++var6; - } - - int var14 = this.anIntArrayArray3225[var11][var6]; - int var10 = var6 - 1; - int var13 = this.anIntArrayArray3225[var11][var10]; - if(var8 > var13 - -this.anInt3223 && var8 < -this.anInt3223 + var14) { - var3[var4] = this.anIntArrayArray3240[var11][var10]; - } else { - var3[var4] = 0; - } - } - } else { - ArrayUtils.fill(var3, 0, Class113.anInt1559, 0); - } - } - - return var3; - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "mc.D(" + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation4() { - super(0, true); - } - - static void method265(int var1) { - try { - InterfaceWidget var2 = InterfaceWidget.getWidget(8, var1); - var2.a(); - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "mc.B(" + (byte) -42 + ',' + var1 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation5.java b/Client/src/main/java/org/runite/client/TextureOperation5.java deleted file mode 100644 index 3da5be70d..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation5.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.runite.client; - -import java.util.Objects; - -final class TextureOperation5 extends TextureOperation { - - private int anInt3294 = 1; - static RSString aClass94_3295; - private int anInt3297 = 1; - - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int var5 = 1 + this.anInt3297 + this.anInt3297; - int var6 = 65536 / var5; - int var7 = 1 + this.anInt3294 + this.anInt3294; - int var8 = 65536 / var7; - int[][] var9 = new int[var5][]; - - int var10; - for(var10 = -this.anInt3297 + var1; var1 - -this.anInt3297 >= var10; ++var10) { - int[] var11 = this.method152(0, var10 & Class3_Sub20.anInt2487); - int[] var12 = new int[Class113.anInt1559]; - int var13 = 0; - - int var14; - for(var14 = -this.anInt3294; var14 <= this.anInt3294; ++var14) { - var13 += var11[var14 & RenderAnimationDefinition.anInt396]; - } - - for(var14 = 0; var14 < Class113.anInt1559; var13 += var11[RenderAnimationDefinition.anInt396 & this.anInt3294 + var14]) { - var12[var14] = var8 * var13 >> 16; - var13 -= var11[RenderAnimationDefinition.anInt396 & var14 - this.anInt3294]; - ++var14; - } - - var9[this.anInt3297 + var10 + -var1] = var12; - } - - for(var10 = 0; var10 < Class113.anInt1559; ++var10) { - int var16 = 0; - - for(int var17 = 0; var5 > var17; ++var17) { - var16 += var9[var17][var10]; - } - - var4[var10] = var6 * var16 >> 16; - } - } - - return var4; - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "nm.D(" + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation5() { - super(1, false); - } - - final int[][] method166(int var2) { - try { - - int[][] var3 = this.aClass97_2376.method1594((byte)90, var2); - if(this.aClass97_2376.aBoolean1379) { - int var6 = this.anInt3294 - -this.anInt3294 - -1; - int var7 = 65536 / var6; - int var4 = this.anInt3297 + (this.anInt3297 - -1); - int var5 = 65536 / var4; - int[][][] var8 = new int[var4][][]; - - int var12; - int var13; - int var14; - for(int var9 = var2 - this.anInt3297; this.anInt3297 + var2 >= var9; ++var9) { - int[][] var10 = this.method162(Class3_Sub20.anInt2487 & var9, 0, (byte)-59); - var12 = 0; - var13 = 0; - int[][] var11 = new int[3][Class113.anInt1559]; - var14 = 0; - int[] var15 = Objects.requireNonNull(var10)[0]; - int[] var16 = var10[1]; - int[] var17 = var10[2]; - - for(int var18 = -this.anInt3294; var18 <= this.anInt3294; ++var18) { - int var19 = var18 & RenderAnimationDefinition.anInt396; - var13 += var16[var19]; - var12 += var15[var19]; - var14 += var17[var19]; - } - - int[] var20 = var11[2]; - int[] var31 = var11[0]; - int[] var30 = var11[1]; - - int var22; - for(int var21 = 0; Class113.anInt1559 > var21; var12 += var15[var22]) { - var31[var21] = var12 * var7 >> 16; - var30[var21] = var13 * var7 >> 16; - var20[var21] = var7 * var14 >> 16; - var22 = RenderAnimationDefinition.anInt396 & var21 + -this.anInt3294; - var14 -= var17[var22]; - ++var21; - var12 -= var15[var22]; - var13 -= var16[var22]; - var22 = this.anInt3294 + var21 & RenderAnimationDefinition.anInt396; - var14 += var17[var22]; - var13 += var16[var22]; - } - - var8[-var2 + this.anInt3297 + var9] = var11; - } - - int[] var24 = var3[0]; - int[] var26 = var3[1]; - int[] var25 = var3[2]; - - for(var12 = 0; var12 < Class113.anInt1559; ++var12) { - var14 = 0; - var13 = 0; - int var27 = 0; - - for(int var28 = 0; var28 < var4; ++var28) { - int[][] var29 = var8[var28]; - var27 += var29[2][var12]; - var14 += var29[1][var12]; - var13 += var29[0][var12]; - } - - var24[var12] = var5 * var13 >> 16; - var26[var12] = var5 * var14 >> 16; - var25[var12] = var27 * var5 >> 16; - } - } - - return var3; - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "nm.T(" + -1 + ',' + var2 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3294 = var2.readUnsignedByte(); - } else if(var1 == 1) { - this.anInt3297 = var2.readUnsignedByte(); - } else if (2 == var1) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - if(!true) { - this.anInt3294 = 60; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "nm.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation6.java b/Client/src/main/java/org/runite/client/TextureOperation6.java deleted file mode 100644 index 44d8f2131..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation6.java +++ /dev/null @@ -1,241 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.HashTable; - -import java.util.Objects; - -final class TextureOperation6 extends TextureOperation { - - //static RSString aClass94_3042 = RSString.createRSString(""); - private int anInt3043 = 0; - private int anInt3046 = 4096; - - - final void decode(int var1, DataBuffer var2) { - try { - if(0 == var1) { - this.anInt3043 = var2.readUnsignedShort(); - } else if(1 == var1) { - this.anInt3046 = var2.readUnsignedShort(); - } else if (2 == var1) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - //aClass94_3042 = (RSString)null; - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "aj.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 != -1) { - method175(2, -7, -114, -24, 102, -125); - } - - int[][] var3 = this.aClass97_2376.method1594((byte)-119, var2); - if(this.aClass97_2376.aBoolean1379) { - int[][] var4 = this.method162(var2, 0, (byte)-124); - int[] var6 = Objects.requireNonNull(var4)[1]; - int[] var7 = var4[2]; - int[] var5 = var4[0]; - int[] var8 = var3[0]; - int[] var9 = var3[1]; - int[] var10 = var3[2]; - - for(int var11 = 0; var11 < Class113.anInt1559; ++var11) { - int var13 = var6[var11]; - int var12 = var5[var11]; - int var14 = var7[var11]; - if(var12 >= this.anInt3043) { - if(var12 > this.anInt3046) { - var8[var11] = this.anInt3046; - } else { - var8[var11] = var12; - } - } else { - var8[var11] = this.anInt3043; - } - - if(this.anInt3043 > var13) { - var9[var11] = this.anInt3043; - } else if(var13 <= this.anInt3046) { - var9[var11] = var13; - } else { - var9[var11] = this.anInt3046; - } - - if(var14 >= this.anInt3043) { - if(this.anInt3046 >= var14) { - var10[var11] = var14; - } else { - var10[var11] = this.anInt3046; - } - } else { - var10[var11] = this.anInt3043; - } - } - } - - return var3; - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "aj.T(" + -1 + ',' + var2 + ')'); - } - } - - static int method173(byte var0, int var1, RSInterface var2) { - try { - if(var0 < 4) { - method176(-50); - } - - return !Client.method44(var2).method92(var1, (byte)-109) && null == var2.anObjectArray314?-1:(null != var2.anIntArray249 && var2.anIntArray249.length > var1 ?var2.anIntArray249[var1]:-1); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "aj.F(" + var0 + ',' + var1 + ',' + (var2 != null?"{...}":"null") + ')'); - } - } - - static void method175(int var0, int var1, int var2, int var4, int var5, int var6) { - try { - MouseListeningClass.method2091(var5); - int var8 = var5 - var1; - int var7 = 0; - if(var8 < 0) { - var8 = 0; - } - - int var10 = -var5; - int var9 = var5; - int var11 = var8; - int var12 = -var8; - int var14 = -1; - int[] var15 = Class38.anIntArrayArray663[var2]; - int var17 = var6 - -var8; - int var13 = -1; - int var16 = -var8 + var6; - TextureOperation18.method282(var15, -var5 + var6, -40, var16, var0); - TextureOperation18.method282(var15, var16, -51, var17, var4); - TextureOperation18.method282(var15, var17, -41, var6 - -var5, var0); - - while(var9 > var7) { - var14 += 2; - var12 += var14; - if(var12 >= 0 && var11 >= 1) { - GameObject.anIntArray1838[var11] = var7; - --var11; - var12 -= var11 << 1; - } - - ++var7; - var13 += 2; - var10 += var13; - int[] var19; - int[] var18; - int var21; - int var20; - int var23; - int var22; - int var24; - if(var10 >= 0) { - --var9; - var10 -= var9 << 1; - if(var9 < var8) { - var18 = Class38.anIntArrayArray663[var9 + var2]; - var19 = Class38.anIntArrayArray663[-var9 + var2]; - var22 = -var7 + var6; - var21 = var7 + var6; - var20 = GameObject.anIntArray1838[var9]; - var24 = -var20 + var6; - var23 = var20 + var6; - TextureOperation18.method282(var18, var22, -113, var24, var0); - TextureOperation18.method282(var18, var24, 95, var23, var4); - TextureOperation18.method282(var18, var23, 117, var21, var0); - TextureOperation18.method282(var19, var22, 113, var24, var0); - TextureOperation18.method282(var19, var24, -76, var23, var4); - TextureOperation18.method282(var19, var23, -97, var21, var0); - } else { - var18 = Class38.anIntArrayArray663[var2 + var9]; - var19 = Class38.anIntArrayArray663[var2 - var9]; - var20 = var7 + var6; - var21 = var6 + -var7; - TextureOperation18.method282(var18, var21, 113, var20, var0); - TextureOperation18.method282(var19, var21, -100, var20, var0); - } - } - - var18 = Class38.anIntArrayArray663[var2 - -var7]; - var19 = Class38.anIntArrayArray663[var2 - var7]; - var20 = var9 + var6; - var21 = -var9 + var6; - if(var8 <= var7) { - TextureOperation18.method282(var18, var21, 104, var20, var0); - TextureOperation18.method282(var19, var21, -127, var20, var0); - } else { - var22 = var7 <= var11?var11:GameObject.anIntArray1838[var7]; - var23 = var22 + var6; - var24 = var6 + -var22; - TextureOperation18.method282(var18, var21, -94, var24, var0); - TextureOperation18.method282(var18, var24, 115, var23, var4); - TextureOperation18.method282(var18, var23, 110, var20, var0); - TextureOperation18.method282(var19, var21, -114, var24, var0); - TextureOperation18.method282(var19, var24, -79, var23, var4); - TextureOperation18.method282(var19, var23, 120, var20, var0); - } - } - - } catch (RuntimeException var25) { - throw ClientErrorException.clientError(var25, "aj.C(" + var0 + ',' + var1 + ',' + var2 + ',' + true + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - public TextureOperation6() { - super(1, false); - } - - static void method176(int var0) { - try { - int var1 = 0; - if(var0 >= -111) { - method176(40); - } - - for(; var1 < Class95.anInt1344; ++var1) { - Class29 var2 = Class145.method2076(var1); - if(null != var2 && var2.anInt556 == 0) { - Class57.varpArray[var1] = 0; - ItemDefinition.ram[var1] = 0; - } - } - - AtmosphereParser.aHashTable_3679 = new HashTable(16); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "aj.O(" + var0 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var5 = this.method152(0, var1); - - for(int var6 = 0; var6 < Class113.anInt1559; ++var6) { - int var7 = var5[var6]; - if(this.anInt3043 > var7) { - var3[var6] = this.anInt3043; - } else if(this.anInt3046 >= var7) { - var3[var6] = var7; - } else { - var3[var6] = this.anInt3046; - } - } - } - - return var3; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "aj.D(" + var1 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation7.java b/Client/src/main/java/org/runite/client/TextureOperation7.java deleted file mode 100644 index 1e2c32570..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation7.java +++ /dev/null @@ -1,605 +0,0 @@ -package org.runite.client; - -import org.rs09.client.data.HashTable; - -import java.util.Objects; - -final class TextureOperation7 extends TextureOperation { - - static RSString[] aClass94Array3341 = new RSString[100]; - static int anInt3342; - private int anInt3343 = 6; - - - final int[] method154(int var1, byte var2) { - try { - int[] var3 = this.aClass114_2382.method1709(var1); - int var4; - if(this.aClass114_2382.aBoolean1580) { - int[] var6 = this.method152(0, var1); - int[] var7 = this.method152(1, var1); - int var8 = this.anInt3343; - if(var8 == 1) { - for(var8 = 0; var8 < Class113.anInt1559; ++var8) { - var3[var8] = var7[var8] + var6[var8]; - } - } else if(var8 == 2) { - for(var8 = 0; var8 < Class113.anInt1559; ++var8) { - var3[var8] = -var7[var8] + var6[var8]; - } - } else if (var8 == 3) { - for (var8 = 0; Class113.anInt1559 > var8; ++var8) { - var3[var8] = var7[var8] * var6[var8] >> 12; - } - } else { - int var5; - if (var8 == 4) { - for (var8 = 0; Class113.anInt1559 > var8; ++var8) { - var5 = var7[var8]; - var3[var8] = var5 != 0 ? (var6[var8] << 12) / var5 : 4096; - } - } else if (var8 == 5) { - for (var8 = 0; var8 < Class113.anInt1559; ++var8) { - var3[var8] = 4096 - ((-var6[var8] + 4096) * (-var7[var8] + 4096) >> 12); - } - } else if (var8 == 6) { - for (var8 = 0; var8 < Class113.anInt1559; ++var8) { - var5 = var7[var8]; - var3[var8] = var5 >= 2048 ? -((-var6[var8] + 4096) * (4096 + -var5) >> 11) + 4096 : var5 * var6[var8] >> 11; - } - } else if (var8 == 7) { - for (var8 = 0; Class113.anInt1559 > var8; ++var8) { - var4 = var6[var8]; - var3[var8] = var4 == 4096 ? 4096 : (var7[var8] << 12) / (4096 - var4); - } - } else if (var8 == 8) { - for (var8 = 0; var8 < Class113.anInt1559; ++var8) { - var4 = var6[var8]; - var3[var8] = var4 != 0 ? 4096 - (-var7[var8] + 4096 << 12) / var4 : 0; - } - } else if (var8 == 9) { - for (var8 = 0; Class113.anInt1559 > var8; ++var8) { - var5 = var7[var8]; - var4 = var6[var8]; - var3[var8] = var5 > var4 ? var4 : var5; - } - } else if (10 == var8) { - for (var8 = 0; var8 < Class113.anInt1559; ++var8) { - var5 = var7[var8]; - var4 = var6[var8]; - var3[var8] = var5 < var4 ? var4 : var5; - } - } else if (var8 == 11) { - for (var8 = 0; var8 < Class113.anInt1559; ++var8) { - var4 = var6[var8]; - var5 = var7[var8]; - var3[var8] = var4 > var5 ? var4 + -var5 : var5 - var4; - } - } else if (var8 == 12) { - for (var8 = 0; Class113.anInt1559 > var8; ++var8) { - var4 = var6[var8]; - var5 = var7[var8]; - var3[var8] = var5 + (var4 - (var4 * var5 >> 11)); - } - } - } - } - - return var3; - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "pi.D(" + var1 + ',' + var2 + ')'); - } - } - - final int[][] method166(int var2) { - try { - int[][] var3 = this.aClass97_2376.method1594((byte)-125, var2); - if(this.aClass97_2376.aBoolean1379) { - int[][] var10 = this.method162(var2, 0, (byte)-87); - int[][] var11 = this.method162(var2, 1, (byte)-73); - int[] var12 = var3[0]; - int[] var13 = var3[1]; - int[] var14 = var3[2]; - int[] var15 = Objects.requireNonNull(var10)[0]; - int[] var16 = var10[1]; - int[] var17 = var10[2]; - int[] var18 = Objects.requireNonNull(var11)[0]; - int[] var19 = var11[1]; - int[] var20 = var11[2]; - int var21 = this.anInt3343; - if(var21 == 1) { - for(var21 = 0; var21 < Class113.anInt1559; ++var21) { - var12[var21] = var18[var21] + var15[var21]; - var13[var21] = var19[var21] + var16[var21]; - var14[var21] = var17[var21] - -var20[var21]; - } - } else if(var21 == 2) { - for(var21 = 0; Class113.anInt1559 > var21; ++var21) { - var12[var21] = var15[var21] - var18[var21]; - var13[var21] = -var19[var21] + var16[var21]; - var14[var21] = -var20[var21] + var17[var21]; - } - } else if(3 == var21) { - for(var21 = 0; Class113.anInt1559 > var21; ++var21) { - var12[var21] = var18[var21] * var15[var21] >> 12; - var13[var21] = var16[var21] * var19[var21] >> 12; - var14[var21] = var20[var21] * var17[var21] >> 12; - } - } else { - int var7; - int var8; - int var9; - if(var21 == 4) { - for(var21 = 0; Class113.anInt1559 > var21; ++var21) { - var9 = var20[var21]; - var8 = var19[var21]; - var7 = var18[var21]; - var12[var21] = var7 == 0 ?4096:(var15[var21] << 12) / var7; - var13[var21] = var8 != 0 ?(var16[var21] << 12) / var8:4096; - var14[var21] = var9 != 0?(var17[var21] << 12) / var9:4096; - } - } else if(var21 == 5) { - for(var21 = 0; Class113.anInt1559 > var21; ++var21) { - var12[var21] = 4096 + -((4096 - var18[var21]) * (4096 - var15[var21]) >> 12); - var13[var21] = 4096 - ((-var19[var21] + 4096) * (-var16[var21] + 4096) >> 12); - var14[var21] = 4096 + -((-var20[var21] + 4096) * (4096 + -var17[var21]) >> 12); - } - } else if(6 == var21) { - for(var21 = 0; var21 < Class113.anInt1559; ++var21) { - var9 = var20[var21]; - var7 = var18[var21]; - var8 = var19[var21]; - var12[var21] = var7 >= 2048 ?-((-var7 + 4096) * (-var15[var21] + 4096) >> 11) + 4096:var7 * var15[var21] >> 11; - var13[var21] = 2048 > var8?var8 * var16[var21] >> 11:4096 - ((4096 + -var8) * (-var16[var21] + 4096) >> 11); - var14[var21] = 2048 <= var9?4096 + -((4096 + -var9) * (-var17[var21] + 4096) >> 11):var17[var21] * var9 >> 11; - } - } else { - int var4; - int var5; - int var6; - if(var21 == 7) { - for(var21 = 0; Class113.anInt1559 > var21; ++var21) { - var6 = var17[var21]; - var4 = var15[var21]; - var5 = var16[var21]; - var12[var21] = var4 == 4096 ?4096:(var18[var21] << 12) / (-var4 + 4096); - var13[var21] = var5 == 4096 ?4096:(var19[var21] << 12) / (4096 + -var5); - var14[var21] = var6 == 4096 ?4096:(var20[var21] << 12) / (4096 - var6); - } - } else if(var21 == 8) { - for(var21 = 0; Class113.anInt1559 > var21; ++var21) { - var4 = var15[var21]; - var5 = var16[var21]; - var6 = var17[var21]; - var12[var21] = var4 == 0?0:-((-var18[var21] + 4096 << 12) / var4) + 4096; - var13[var21] = var5 == 0 ?0:-((-var19[var21] + 4096 << 12) / var5) + 4096; - var14[var21] = 0 == var6?0:4096 - (4096 - var20[var21] << 12) / var6; - } - } else if(var21 == 9) { - for(var21 = 0; Class113.anInt1559 > var21; ++var21) { - var6 = var17[var21]; - var9 = var20[var21]; - var8 = var19[var21]; - var5 = var16[var21]; - var7 = var18[var21]; - var4 = var15[var21]; - var12[var21] = var7 <= var4 ?var7:var4; - var13[var21] = var5 >= var8?var8:var5; - var14[var21] = var6 < var9?var6:var9; - } - } else if (10 == var21) { - for (var21 = 0; var21 < Class113.anInt1559; ++var21) { - var9 = var20[var21]; - var6 = var17[var21]; - var8 = var19[var21]; - var4 = var15[var21]; - var5 = var16[var21]; - var7 = var18[var21]; - var12[var21] = var7 < var4 ? var4 : var7; - var13[var21] = var5 > var8 ? var5 : var8; - var14[var21] = var6 > var9 ? var6 : var9; - } - } else if (var21 == 11) { - for (var21 = 0; Class113.anInt1559 > var21; ++var21) { - var8 = var19[var21]; - var7 = var18[var21]; - var5 = var16[var21]; - var4 = var15[var21]; - var6 = var17[var21]; - var9 = var20[var21]; - var12[var21] = var7 < var4 ? var4 + -var7 : -var4 + var7; - var13[var21] = var5 > var8 ? -var8 + var5 : -var5 + var8; - var14[var21] = var9 < var6 ? var6 + -var9 : -var6 + var9; - } - } else if (var21 == 12) { - for (var21 = 0; Class113.anInt1559 > var21; ++var21) { - var4 = var15[var21]; - var9 = var20[var21]; - var7 = var18[var21]; - var8 = var19[var21]; - var6 = var17[var21]; - var5 = var16[var21]; - var12[var21] = -(var7 * var4 >> 11) + var7 + var4; - var13[var21] = var8 + var5 + -(var5 * var8 >> 11); - var14[var21] = var9 + (var6 - (var6 * var9 >> 11)); - } - } - } - } - } - - return var3; - } catch (RuntimeException var22) { - throw ClientErrorException.clientError(var22, "pi.T(" + -1 + ',' + var2 + ')'); - } - } - - static void method297(long var0, int var2) { - try { - if(var0 != 0L) { - for(int var3 = 0; Class8.anInt104 > var3; ++var3) { - if(var0 == Class50.aLongArray826[var3]) { - --Class8.anInt104; - - for(int var4 = var3; var4 < Class8.anInt104; ++var4) { - Class70.aClass94Array1046[var4] = Class70.aClass94Array1046[var4 - -1]; - Unsorted.anIntArray882[var4] = Unsorted.anIntArray882[var4 + 1]; - Unsorted.aClass94Array2566[var4] = Unsorted.aClass94Array2566[1 + var4]; - Class50.aLongArray826[var4] = Class50.aLongArray826[1 + var4]; - Class57.anIntArray904[var4] = Class57.anIntArray904[var4 - -1]; - Unsorted.aBooleanArray73[var4] = Unsorted.aBooleanArray73[1 + var4]; - } - - Class110.anInt1472 = PacketParser.anInt3213; - TextureOperation12.outgoingBuffer.putOpcode(57); - TextureOperation12.outgoingBuffer.writeLong(var0); - break; - } - } - - if(var2 != 1) { - aClass94Array3341 = null; - } - - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pi.C(" + var0 + ',' + var2 + ')'); - } - } - - static Class3_Sub11[] method298(byte[][][] var0, byte[][] var1, byte[][] var2, int[][] var3, float[][] var4, int[][] var5, byte[][] var6, byte[][] var7, float[][] var8, int var9, float[][] var10, int[][] var11, int[][] var12, int[][] var13) { - try { - int[][] var15 = new int[105][105]; - - int var17; - for(int var16 = 1; var16 <= 103; ++var16) { - for(var17 = 1; var17 <= 103; ++var17) { - byte var18 = var2[var16][var17]; - if(var18 == 0) { - var18 = var2[var16 + -1][var17]; - } - - if(var18 == 0) { - var18 = var2[var16][-1 + var17]; - } - - if(var18 == 0) { - var18 = var2[-1 + var16][var17 - 1]; - } - - if(0 != var18) { - FloorUnderlayDefinition var19 = FloorUnderlayDefinition.method629(-1 + (255 & var18)); - var15[var16][var17] = (1 + var19.anInt1412 << 16) - -var19.anInt1414; - } - } - } - - HashTable var52 = new HashTable(128); - - int var21; - int var20; - int var22; - int var56; - for(var17 = 1; var17 <= 102; ++var17) { - for(var56 = 1; 102 >= var56; ++var56) { - if(0 != var2[var17][var56]) { - int[] var53; - if(0 == var6[var17][var56]) { - var53 = Class134.anIntArrayArray1763[0]; - } else { - var53 = Class25.anIntArrayArray499[var1[var17][var56]]; - if(var53.length == 0) { - continue; - } - } - - var20 = 0; - var21 = var15[var17][var56]; - var22 = var15[var17 - -1][var56]; - if(null != var5) { - var20 = var5[var17][var56] & 16777215; - } - - long var27 = (long)var20 | (long)var22 << 32; - int var24 = var15[var17][var56 + 1]; - int var23 = var15[var17 + 1][var56 - -1]; - long var31 = (long)var24 << 32 | (long)var20; - int var33 = var53.length / 2; - long var25 = (long)var20 | (long)var21 << 32; - Class3_Sub11 var34 = (Class3_Sub11)var52.get(var25); - if(null == var34) { - var34 = new Class3_Sub11(-1 + (var21 >> 16), (float)(65535 & var21), false, null != var13, var20); - var52.put(var25, var34); - } - - ++var34.anInt2344; - var34.anInt2342 += var33; - if(var25 != var27) { - var34 = (Class3_Sub11)var52.get(var27); - if(var34 == null) { - var34 = new Class3_Sub11((var22 >> 16) - 1, (float)(65535 & var22), false, null != var13, var20); - var52.put(var27, var34); - } - - ++var34.anInt2344; - var34.anInt2342 += var33; - } - - long var29 = (long)var23 << 32 | (long)var20; - if(var25 != var29 && var27 != var29) { - var34 = (Class3_Sub11)var52.get(var29); - if(var34 == null) { - var34 = new Class3_Sub11((var23 >> 16) + -1, (float)(65535 & var23), false, null != var13, var20); - var52.put(var29, var34); - } - - var34.anInt2342 += var33; - ++var34.anInt2344; - } - - if(var25 != var31 && var31 != var27 && var31 != var29) { - var34 = (Class3_Sub11)var52.get(var31); - if(null == var34) { - var34 = new Class3_Sub11((var24 >> 16) - 1, (float)(var24 & 65535), false, null != var13, var20); - var52.put(var31, var34); - } - - ++var34.anInt2344; - var34.anInt2342 += var33; - } - } - } - } - - if(4096 != 4096) { - method297(-10L, 48); - } - - Class3_Sub11 var54; - for(var54 = (Class3_Sub11)var52.first(); var54 != null; var54 = (Class3_Sub11)var52.next()) { - var54.method145(); - } - - for(var17 = 1; var17 <= 102; ++var17) { - for(var56 = 1; var56 <= 102; ++var56) { - byte var57 = var2[var17][var56]; - if(0 != var57) { - if((8 & var0[var9][var17][var56]) == 0) { - if((2 & var0[1][var17][var56]) == 2 && 0 < var9) { - var20 = var9 - 1; - } else { - var20 = var9; - } - } else { - var20 = 0; - } - - var21 = 0; - boolean[] var60 = null; - var22 = 128; - if(null != var5) { - var22 = var5[var17][var56] >>> 24 << 3; - var21 = 16777215 & var5[var17][var56]; - } - - int[] var58; - int var63; - int var62; - byte var61; - int var69; - int var64; - if(var6[var17][var56] == 0) { - byte var26 = 0; - var64 = var26 + (var2[var17 - 1][-1 + var56] != var57 ?-1:1); - byte var65 = 0; - var58 = Class134.anIntArrayArray1763[0]; - var62 = var65 + (var57 == var2[1 + var17][var56 + -1]?1:-1); - if(var2[var17][var56 + -1] == var57) { - ++var62; - ++var64; - } else { - --var64; - --var62; - } - - byte var28 = 0; - var63 = var28 + (var57 == var2[1 + var17][1 + var56]?1:-1); - byte var68 = 0; - if(var2[1 + var17][var56] == var57) { - ++var63; - ++var62; - } else { - --var62; - --var63; - } - - var69 = var68 + (var2[var17 - 1][1 + var56] == var57 ?1:-1); - if(var2[var17][1 + var56] == var57) { - ++var69; - ++var63; - } else { - --var63; - --var69; - } - - if(var2[-1 + var17][var56] == var57) { - ++var69; - ++var64; - } else { - --var69; - --var64; - } - - int var30 = var64 + -var63; - int var66 = -var69 + var62; - if(var66 < 0) { - var66 = -var66; - } - - if(var30 < 0) { - var30 = -var30; - } - - var61 = (byte)(var30 >= var66 ?0:1); - var7[var17][var56] = var61; - } else { - var58 = Class25.anIntArrayArray499[var1[var17][var56]]; - var60 = TextureOperation30.aBooleanArrayArray3118[var1[var17][var56]]; - var61 = var7[var17][var56]; - if(var58.length == 0) { - continue; - } - } - - var64 = var15[var17][var56]; - var62 = var15[var17 - -1][var56]; - var63 = var15[var17 - -1][var56 - -1]; - long var67 = (long)var64 << 32 | (long)var21; - long var32 = (long)var62 << 32 | (long)var21; - long var70 = (long)var63 << 32 | (long)var21; - int var38 = var11[var17][var56]; - var69 = var15[var17][var56 - -1]; - int var40 = var11[var17 - -1][var56 - -1]; - int var39 = var11[1 + var17][var56]; - long var36 = (long)var21 | (long)var69 << 32; - int var41 = var11[var17][var56 + 1]; - int var42 = var3[var17][var56]; - int var43 = var3[var17 + 1][var56]; - int var44 = var3[var17 + 1][var56 - -1]; - int var45 = var3[var17][1 + var56]; - int var47 = -1 + (var62 >> 16); - int var46 = (var64 >> 16) - 1; - int var48 = (var63 >> 16) - 1; - Class3_Sub11 var50 = (Class3_Sub11)var52.get(var67); - Class25.method955(var13, var64 <= var64, TextureOperation34.method190(var46, var38, (byte)-92, var42), var50, var58, var56, var20, var17, var64 <= var63, var8, var69 >= var64, 2, var4, var22, TextureOperation34.method190(var46, var41, (byte)-80, var45), TextureOperation34.method190(var46, var40, (byte)-103, var44), var62 >= var64, var12, var10, var61, TextureOperation34.method190(var46, var39, (byte)-118, var43), var60); - int var49 = (var69 >> 16) - 1; - if(var32 != var67) { - var50 = (Class3_Sub11)var52.get(var32); - Class25.method955(var13, var62 <= var64, TextureOperation34.method190(var47, var38, (byte)88, var42), var50, var58, var56, var20, var17, var63 >= var62, var8, var62 <= var69, 2, var4, var22, TextureOperation34.method190(var47, var41, (byte)-82, var45), TextureOperation34.method190(var47, var40, (byte)-113, var44), var62 <= var62, var12, var10, var61, TextureOperation34.method190(var47, var39, (byte)113, var43), var60); - } - - if(var70 != var67 && var32 != var70) { - var50 = (Class3_Sub11)var52.get(var70); - Class25.method955(var13, var63 <= var64, TextureOperation34.method190(var48, var38, (byte)59, var42), var50, var58, var56, var20, var17, var63 <= var63, var8, var63 <= var69, 4096 ^ 4098, var4, var22, TextureOperation34.method190(var48, var41, (byte)54, var45), TextureOperation34.method190(var48, var40, (byte)-87, var44), var63 <= var62, var12, var10, var61, TextureOperation34.method190(var48, var39, (byte)-77, var43), var60); - } - - if(var67 != var36 && var32 != var36 && var36 != var70) { - var50 = (Class3_Sub11)var52.get(var36); - Class25.method955(var13, var64 >= var69, TextureOperation34.method190(var49, var38, (byte)-118, var42), var50, var58, var56, var20, var17, var69 <= var63, var8, var69 <= var69, 4096 ^ 4098, var4, var22, TextureOperation34.method190(var49, var41, (byte)-96, var45), TextureOperation34.method190(var49, var40, (byte)115, var44), var69 <= var62, var12, var10, var61, TextureOperation34.method190(var49, var39, (byte)58, var43), var60); - } - } - } - } - - for(var54 = (Class3_Sub11)var52.first(); var54 != null; var54 = (Class3_Sub11)var52.next()) { - if(var54.anInt2343 == 0) { - var54.unlink(); - } else { - var54.method148(); - } - } - - var17 = var52.size(); - Class3_Sub11[] var59 = new Class3_Sub11[var17]; - var52.values(var59); - long[] var55 = new long[var17]; - - for(var20 = 0; var17 > var20; ++var20) { - var55[var20] = var59[var20].linkableKey; - } - - PacketParser.method824(var55, var59, -86); - return var59; - } catch (RuntimeException var51) { - throw ClientErrorException.clientError(var51, "pi.E(" + (var0 != null?"{...}":"null") + ',' + (var1 != null?"{...}":"null") + ',' + (var2 != null?"{...}":"null") + ',' + (var3 != null?"{...}":"null") + ',' + (var4 != null?"{...}":"null") + ',' + (var5 != null?"{...}":"null") + ',' + (var6 != null?"{...}":"null") + ',' + (var7 != null?"{...}":"null") + ',' + (var8 != null?"{...}":"null") + ',' + var9 + ',' + (var10 != null?"{...}":"null") + ',' + (var11 != null?"{...}":"null") + ',' + (var12 != null?"{...}":"null") + ',' + (var13 != null?"{...}":"null") + ',' + 4096 + ')'); - } - } - - public TextureOperation7() { - super(2, false); - } - - static void method299(int var0, int var1, int var2) { - try { - if(var0 <= 92) { - method297(-6L, -85); - } - - int var3 = var1; - if(var1 > 25) { - var3 = 25; - } - - --var1; - int var4 = TextureOperation38.anIntArray3456[var1]; - int var5 = Class45.anIntArray729[var1]; - if(0 == var2) { - TextureOperation12.outgoingBuffer.putOpcode(215); - TextureOperation12.outgoingBuffer.writeByte(var3 - -var3 + 3); - } - - if(var2 == 1) { - TextureOperation12.outgoingBuffer.putOpcode(39); - TextureOperation12.outgoingBuffer.writeByte(3 + var3 - (-var3 - 14)); - } - - if(var2 == 2) { - TextureOperation12.outgoingBuffer.putOpcode(77); - TextureOperation12.outgoingBuffer.writeByte(var3 + var3 - -3); - } - - TextureOperation12.outgoingBuffer.writeByte128(ObjectDefinition.aBooleanArray1490[82]?1:0); - TextureOperation12.outgoingBuffer.writeShort(Class131.anInt1716 + var4); - TextureOperation12.outgoingBuffer.putShortA(Texture.anInt1152 + var5); - Class45.anInt733 = Class45.anIntArray729[0]; - Class65.anInt987 = TextureOperation38.anIntArray3456[0]; - - for(int var6 = 1; var3 > var6; ++var6) { - --var1; - TextureOperation12.outgoingBuffer.writeByte128(-var4 + TextureOperation38.anIntArray3456[var1]); - TextureOperation12.outgoingBuffer.write128Byte(Class45.anIntArray729[var1] + -var5); - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "pi.O(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(var1 == 0) { - this.anInt3343 = var2.readUnsignedByte(); - } else if(var1 == 1) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - if(!true) { - TextCore.aClass94_3339 = null; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "pi.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation8.java b/Client/src/main/java/org/runite/client/TextureOperation8.java deleted file mode 100644 index 584ff8b37..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation8.java +++ /dev/null @@ -1,244 +0,0 @@ -package org.runite.client; - -final class TextureOperation8 extends TextureOperation { - - private int[] anIntArray3457; - static AbstractSprite[] aAbstractSpriteArray3458; - static int anInt3460 = 0; - private int anInt3462 = 0; - static int anInt3463; - static int anInt3464 = 0; - private final short[] aShortArray3465 = new short[257]; - static int anInt3466; - private int[] anIntArray3467; - static boolean[][] aBooleanArrayArray3468 = new boolean[][]{{true, true, true}, {false, false}, {false, true}, {true, false}, {false, true, true}, {true, false, true}, {false, true, false}, {true, false, false}}; - private int[][] anIntArrayArray3469; - - - private void method352() { - try { - int var2 = this.anInt3462; - int var3; - int var4; - int[] var5; - int[] var6; - int var7; - int var8; - int var9; - int var10; - if(2 == var2) { - for(var2 = 0; var2 < 257; ++var2) { - var4 = var2 << 4; - - for(var3 = 1; this.anIntArrayArray3469.length - 1 > var3 && this.anIntArrayArray3469[var3][0] <= var4; ++var3) { - } - - var6 = this.anIntArrayArray3469[var3]; - var5 = this.anIntArrayArray3469[var3 + -1]; - var7 = this.method356(var3 + -2)[1]; - var9 = var6[1]; - var8 = var5[1]; - int var15 = var9 - var7; - var10 = this.method356(var3 + 1)[1]; - int var11 = (var4 + -var5[0] << 12) / (var6[0] + -var5[0]); - int var13 = var10 + -var9 + -var7 + var8; - int var12 = var11 * var11 >> 12; - int var14 = var7 + (-var8 - var13); - int var19 = var15 * var11 >> 12; - int var18 = var12 * var14 >> 12; - int var17 = var12 * (var11 * var13 >> 12) >> 12; - int var20 = var8 + var18 + var17 + var19; - if(var20 <= -32768) { - var20 = -32767; - } - - if(var20 >= 32768) { - var20 = 32767; - } - - this.aShortArray3465[var2] = (short)var20; - } - } else if (var2 == 1) { - for (var2 = 0; var2 < 257; ++var2) { - var4 = var2 << 4; - - for (var3 = 1; -1 + this.anIntArrayArray3469.length > var3 && var4 >= this.anIntArrayArray3469[var3][0]; ++var3) { - } - - var5 = this.anIntArrayArray3469[-1 + var3]; - var6 = this.anIntArrayArray3469[var3]; - var7 = (var4 + -var5[0] << 12) / (-var5[0] + var6[0]); - var8 = 4096 + -Class75_Sub2.anIntArray2639[(8189 & var7) >> 5] >> 1; - var9 = -var8 + 4096; - var10 = var8 * var6[1] + var5[1] * var9 >> 12; - if (var10 <= -32768) { - var10 = -32767; - } - - if (32768 <= var10) { - var10 = 32767; - } - - this.aShortArray3465[var2] = (short) var10; - } - } else { - for (var2 = 0; var2 < 257; ++var2) { - var4 = var2 << 4; - - for (var3 = 1; -1 + this.anIntArrayArray3469.length > var3 && var4 >= this.anIntArrayArray3469[var3][0]; ++var3) { - } - - var6 = this.anIntArrayArray3469[var3]; - var5 = this.anIntArrayArray3469[-1 + var3]; - var7 = (-var5[0] + var4 << 12) / (-var5[0] + var6[0]); - var8 = -var7 + 4096; - var9 = var6[1] * var7 + var8 * var5[1] >> 12; - if (-32768 >= var9) { - var9 = -32767; - } - - if (32768 <= var9) { - var9 = 32767; - } - - this.aShortArray3465[var2] = (short) var9; - } - } - - } catch (RuntimeException var21) { - throw ClientErrorException.clientError(var21, "wi.E(" + -11 + ')'); - } - } - - final int[] method154(int var1, byte var2) { - try { - int[] var8 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var5 = this.method152(0, var1); - - for(int var6 = 0; var6 < Class113.anInt1559; ++var6) { - int var4 = var5[var6] >> 4; - if(0 > var4) { - var4 = 0; - } - - if(256 < var4) { - var4 = 256; - } - - var8[var6] = this.aShortArray3465[var4]; - } - } - - return var8; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "wi.D(" + var1 + ',' + var2 + ')'); - } - } - - public TextureOperation8() { - super(1, true); - } - - static boolean method353(int var0, int var1) { - try { - if(var1 > var0) { - return false; - } else { - int var2 = TextureOperation27.aShortArray3095[var0]; - if(var2 >= 2000) { - var2 -= 2000; - } - - return var2 == 1003; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wi.Q(" + var0 + ',' + var1 + ')'); - } - } - - static void method354(int var1) { - try { - TextureOperation13.anInt3362 = -1; - - Class3_Sub4.anInt2251 = var1; - Class3_Sub5.method117(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wi.B(" + -126 + ',' + var1 + ')'); - } - } - - final void postDecode() { - try { - if(null == this.anIntArrayArray3469) { - this.anIntArrayArray3469 = new int[][]{{0, 0}, {4096, 4096}}; - } - - if(this.anIntArrayArray3469.length < 2) { - throw new RuntimeException("Curve operation requires at least two markers"); - } else { - if(2 == this.anInt3462) { - this.method355(); - } - - Class8.method844((byte)-9); - this.method352(); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wi.P(" + ')'); - } - } - - private void method355() { - try { - int[] var2 = this.anIntArrayArray3469[0]; - - int[] var3 = this.anIntArrayArray3469[1]; - int[] var4 = this.anIntArrayArray3469[this.anIntArrayArray3469.length + -2]; - int[] var5 = this.anIntArrayArray3469[-1 + this.anIntArrayArray3469.length]; - this.anIntArray3457 = new int[]{var2[0] - (var3[0] + -var2[0]), -var3[1] + var2[1] + var2[1]}; - this.anIntArray3467 = new int[]{var4[0] + var4[0] + -var5[0], -var5[1] - -var4[1] + var4[1]}; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "wi.C(" + true + ')'); - } - } - - final void decode(int var1, DataBuffer var2) { - try { - if(0 == var1) { - this.anInt3462 = var2.readUnsignedByte(); - this.anIntArrayArray3469 = new int[var2.readUnsignedByte()][2]; - - for(int var4 = 0; var4 < this.anIntArrayArray3469.length; ++var4) { - this.anIntArrayArray3469[var4][0] = var2.readUnsignedShort(); - this.anIntArrayArray3469[var4][1] = var2.readUnsignedShort(); - } - } - - if(!true) { - this.anInt3462 = -6; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "wi.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - private int[] method356(int var2) { - try { - if(var2 >= 0) { - if(this.anIntArrayArray3469.length <= var2) { - return this.anIntArray3467; - } else { - - return this.anIntArrayArray3469[var2]; - } - } else { - return this.anIntArray3457; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "wi.F(" + (byte) -106 + ',' + var2 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TextureOperation9.java b/Client/src/main/java/org/runite/client/TextureOperation9.java deleted file mode 100644 index 496ffbbaf..000000000 --- a/Client/src/main/java/org/runite/client/TextureOperation9.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.runite.client; - -import org.rs09.client.util.ArrayUtils; - -import java.util.Objects; - -public final class TextureOperation9 extends TextureOperation { - - private boolean aBoolean3100 = true; - public static int anInt3102 = 0; - static int anInt3103; - private boolean aBoolean3104 = true; - static float aFloat3105; - - - final void decode(int var1, DataBuffer var2) { - try { - if(!true) { - method207(18, false, -19, 102L); - } - - if(var1 == 0) { - this.aBoolean3100 = var2.readUnsignedByte() == 1; - } else if(var1 == 1) { - this.aBoolean3104 = var2.readUnsignedByte() == 1; - } else if(var1 == 2) { - this.aBoolean2375 = var2.readUnsignedByte() == 1; - } - - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ej.A(" + var1 + ',' + (var2 != null?"{...}":"null") + ',' + true + ')'); - } - } - - final int[][] method166(int var2) { - try { - if(-1 != -1) { - method207(-98, true, 95, 79L); - } - - int[][] var3 = this.aClass97_2376.method1594((byte)54, var2); - if(this.aClass97_2376.aBoolean1379) { - int[][] var4 = this.method162(!this.aBoolean3104?var2:-var2 + Class3_Sub20.anInt2487, 0, (byte)-105); - int[] var5 = Objects.requireNonNull(var4)[0]; - int[] var7 = var4[2]; - int[] var6 = var4[1]; - int[] var9 = var3[1]; - int[] var10 = var3[2]; - int[] var8 = var3[0]; - int var11; - if(this.aBoolean3100) { - for(var11 = 0; Class113.anInt1559 > var11; ++var11) { - var8[var11] = var5[RenderAnimationDefinition.anInt396 + -var11]; - var9[var11] = var6[-var11 + RenderAnimationDefinition.anInt396]; - var10[var11] = var7[RenderAnimationDefinition.anInt396 - var11]; - } - } else { - for(var11 = 0; var11 < Class113.anInt1559; ++var11) { - var8[var11] = var5[var11]; - var9[var11] = var6[var11]; - var10[var11] = var7[var11]; - } - } - } - - return var3; - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "ej.T(" + -1 + ',' + var2 + ')'); - } - } - - static void method203(int var0) { - try { - //int var1 = 15 / ((-11 - var0) / 63); - if(Unsorted.anInt3660 == 2) { - if(TextureOperation8.anInt3460 == NPCDefinition.anInt1297 && Class38_Sub1.anInt2612 == Unsorted.anInt2099) { - Unsorted.anInt3660 = 0; - if(ClientCommands.shiftClickEnabled && ObjectDefinition.aBooleanArray1490[81] && Unsorted.menuOptionCount > 2) { - BufferedDataStream.method806(Unsorted.menuOptionCount + -2); - } else { - BufferedDataStream.method806(Unsorted.menuOptionCount + -1); - } - } - } else if(NPCDefinition.anInt1297 == Class163_Sub1.anInt2993 && Class38_Sub1.anInt2614 == Class38_Sub1.anInt2612) { - Unsorted.anInt3660 = 0; - if(ClientCommands.shiftClickEnabled && ObjectDefinition.aBooleanArray1490[81] && Unsorted.menuOptionCount > 2) { - BufferedDataStream.method806(Unsorted.menuOptionCount - 2); - } else { - BufferedDataStream.method806(Unsorted.menuOptionCount - 1); - } - } else { - Unsorted.anInt2099 = Class38_Sub1.anInt2614; - Unsorted.anInt3660 = 2; - TextureOperation8.anInt3460 = Class163_Sub1.anInt2993; - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ej.B(" + var0 + ')'); - } - } - - static void method204() { - try { - //Client Resize. - TextureOperation12.outgoingBuffer.putOpcode(243); - TextureOperation12.outgoingBuffer.writeByte(Class83.getWindowType()); - TextureOperation12.outgoingBuffer.writeShort(Class23.canvasWidth); - TextureOperation12.outgoingBuffer.writeShort(Class140_Sub7.canvasHeight); - TextureOperation12.outgoingBuffer.writeByte(Unsorted.anInt3671); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ej.C(" + -3 + ')'); - } - } - - public TextureOperation9() { - super(1, false); - } - - final int[] method154(int var1, byte var2) { - try { - int[] var4 = this.aClass114_2382.method1709(var1); - if(this.aClass114_2382.aBoolean1580) { - int[] var5 = this.method152(0, !this.aBoolean3104?var1:Class3_Sub20.anInt2487 + -var1); - if(this.aBoolean3100) { - for(int var6 = 0; var6 < Class113.anInt1559; ++var6) { - var4[var6] = var5[-var6 + RenderAnimationDefinition.anInt396]; - } - } else { - ArrayUtils.arraycopy(var5, 0, var4, 0, Class113.anInt1559); - } - } - - return var4; - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "ej.D(" + var1 + ',' + var2 + ')'); - } - } - - static RSString method207(int var0, boolean var1, int var2, long var3) { - try { - if(var0 >= 2 && var0 <= 36) { - if(var2 <= 71) { - aFloat3105 = 1.3008908F; - } - - long var6 = var3 / (long)var0; - - int var5; - for(var5 = 1; var6 != 0L; var6 /= var0) { - ++var5; - } - - int var8 = var5; - if(0L > var3 || var1) { - var8 = var5 + 1; - } - - byte[] var9 = new byte[var8]; - if(var3 >= 0L) { - if(var1) { - var9[0] = 43; - } - } else { - var9[0] = 45; - } - - for(int var10 = 0; var10 < var5; ++var10) { - int var11 = (int)(var3 % (long)var0); - var3 /= var0; - if(var11 < 0) { - var11 = -var11; - } - - if(var11 > 9) { - var11 += 39; - } - - var9[-1 + -var10 + var8] = (byte)(var11 + 48); - } - - RSString var13 = new RSString(); - var13.buffer = var9; - var13.length = var8; - return var13; - } else { - throw new IllegalArgumentException("Invalid radix:" + var0); - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "ej.F(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/TimeUtils.java b/Client/src/main/java/org/runite/client/TimeUtils.java deleted file mode 100644 index 7fa8ae674..000000000 --- a/Client/src/main/java/org/runite/client/TimeUtils.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.runite.client; - -public final class TimeUtils { - - private static long correction; - private static long previous; - - public static synchronized long time() { - long time = System.currentTimeMillis(); - if (time < previous) { - correction += previous + -time; - } - - previous = time; - return correction + time; - } - - public static void sleep(long millis) { - if (0L < millis) { - if (0L == millis % 10L) { - sleepWrapped(millis + -1L); - sleepWrapped(1L); - } else { - sleepWrapped(millis); - } - } - } - - private static void sleepWrapped(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException var4) { - } - } -} diff --git a/Client/src/main/java/org/runite/client/Translation.java b/Client/src/main/java/org/runite/client/Translation.java deleted file mode 100644 index b4c54e75a..000000000 --- a/Client/src/main/java/org/runite/client/Translation.java +++ /dev/null @@ -1,204 +0,0 @@ -package org.runite.client; - -public class Translation { - - //English - - - //Class 9 englishToFrench - static void englishToFrench() { - try { - TextCore.LoadedConfig = RSString.parse("Fichiers config charg-Bs"); - TextCore.LoadingConfig = RSString.parse("Chargement des fichiers config )2 "); - TextCore.TextColorPurple = RSString.parse("violet:"); - TextCore.HasContinue = RSString.parse("Continuer"); - TextCore.HasSkill = RSString.parse("comp-Btence "); - TextCore.HasExamine = RSString.parse("Examiner"); - TextCore.LoadingWordPack = RSString.parse("Chargement du module texte )2 "); - TextCore.TextGlowTwo = RSString.parse("brillant2:"); - TextCore.MillionM = RSString.parse("M"); - TextCore.TextFlashTwo = RSString.parse("clignotant2:"); - TextCore.TextWaveTwo = RSString.parse("ondulation2:"); - TextCore.HasAttack = RSString.parse("Attaquer"); - TextCore.HasFriendsListFull = RSString.parse("Votre liste d(Wamis est pleine (X100 noms maximum pour la version gratuite et 200 pour les abonn-Bs(Y)3"); - TextCore.HasIgnoreAlready = RSString.parse(" est d-Bj-9 dans votre liste noire)3"); - TextCore.LoadedWLD = RSString.parse("Liste des serveurs charg-Be"); - TextCore.LoadedInputHandler = RSString.parse("Gestionnaire de saisie charg-B"); - TextCore.TextColorRed = RSString.parse("rouge:"); - TextCore.TextWave = RSString.parse("ondulation:"); - TextCore.LoadingConnecting = RSString.parse("Connexion au serveur de mise -9 jour en cours"); - TextCore.HasFriendsAlready = RSString.parse(" est d-Bj-9 dans votre liste d(Wamis)3"); - TextCore.TextShake = RSString.parse("tremblement:"); - TextCore.LoadingTitleScreen = RSString.parse("Chargement de l(W-Bcran)2titre )2 "); - TextCore.HasMoreOptions = RSString.parse(" autres options"); - TextCore.OpenedTitleScreen = RSString.parse(",Mcran)2titre ouvert"); - TextCore.Started3DLibrary = RSString.parse("Librairie 3D d-Bmarr-Be"); - TextCore.LoadingPleaseWait = RSString.parse("Veuillez patienter)3)3)3"); - TextCore.HasFaceHere = RSString.parse("Regarder dans cette direction"); - TextCore.MillionM = RSString.parse("M"); - TextCore.HasLoggedIn = RSString.parse(" s(West connect-B)3"); - TextCore.TextColorCyan = RSString.parse("cyan:"); - TextCore.LoadingFonts = RSString.parse("Chargement des polices )2 "); - TextCore.TextFlashThree = RSString.parse("clignotant3:"); - TextCore.HasOK = RSString.parse("OK"); - TextCore.HasOnOwnIgnoreList = RSString.parse("Vous ne pouvez pas ajouter votre nom -9 votre liste noire)3"); - TextCore.LoadedTextures = RSString.parse("Textures charg-Bes"); - TextCore.TextGlowThree = RSString.parse("brillant3:"); - TextCore.Starting3DLibrary = RSString.parse("D-Bmarrage de la librairie 3D"); - TextCore.CheckingForUpdates = RSString.parse("V-Brification des mises -9 jour )2 "); - TextCore.AttemptingReestablish = RSString.parse("Veuillez patienter )2 tentative de r-Btablissement)3"); - TextCore.TextSlide = RSString.parse("glissement:"); - TextCore.AllocatingMemory = RSString.parse("M-Bmoire en cours d(Wattribution"); - TextCore.PreparedSoundEngine = RSString.parse("Moteur son pr-Bpar-B"); - TextCore.AllocatedMemory = RSString.parse("M-Bmoire attribu-Be"); - TextCore.HasDrop = RSString.parse("Poser"); - TextCore.Spacer = RSString.parse(" "); - TextCore.HasWalkHere = RSString.parse("Atteindre"); - TextCore.HasUnableFind = RSString.parse("Impossible de trouver "); - TextCore.LoadedTitleScreen = RSString.parse(",Mcran)2titre charg-B"); - TextCore.HasFriendsToIgnore = RSString.parse(" de votre liste d(Wamis)3"); - TextCore.LoadingSprites = RSString.parse("Chargement des sprites )2 "); - TextCore.MembersObject = RSString.parse("Objet d(Wabonn-Bs"); - TextCore.LoadedUpdateList = RSString.parse("Liste des mises -9 jour charg-Be"); - TextCore.LoadedFonts = RSString.parse("Polices charg-Bes"); - TextCore.HasTake = RSString.parse("Prendre"); - TextCore.HasRating = RSString.parse("classement "); - TextCore.HasDiscard = RSString.parse("Jeter"); - TextCore.HasClose = RSString.parse("Fermer"); - TextCore.HasWishToTrade = RSString.parse("voudrait faire un -Bchange avec vous)3"); - TextCore.TextColorYellow = RSString.parse("jaune:"); - TextCore.HasIgnoreToFriends = RSString.parse(" de votre liste noire)3"); - TextCore.TextGlowOne = RSString.parse("brillant1:"); - TextCore.HasChooseOptions = RSString.parse("Choisir une option"); - TextCore.LoadingGeneral = RSString.parse("Chargement en cours)3)3)3"); - TextCore.TextColorWhite = RSString.parse("blanc:"); - TextCore.LoadedSprites = RSString.parse("Sprites charg-Bs"); - TextCore.LoadingInterfaces = RSString.parse("Chargement des interfaces )2 "); - TextCore.HasOnOwnFriendsList = RSString.parse("Vous ne pouvez pas ajouter votre nom -9 votre liste d(Wamis)3"); - TextCore.HasPleaseRemove = RSString.parse("Veuillez commencer par supprimer "); - TextCore.HasCancel = RSString.parse("Annuler"); - TextCore.LoadedWordPack = RSString.parse("Module texte charg-B"); - TextCore.CreatedWorld = RSString.parse("Monde de jeu cr-B-B"); - TextCore.ThousandK = RSString.parse("K"); - TextCore.HasLevel = RSString.parse("niveau "); - TextCore.ThousandK = RSString.parse("K"); - TextCore.HasSelect = RSString.parse("S-Blectionner"); - TextCore.ConxLost = RSString.parse("Connexion perdue)3"); - TextCore.HasIgnoreListFull = RSString.parse("Votre liste noire est pleine (X100 noms maximum(Y)3"); - TextCore.LoadedInterfaces = RSString.parse("Interfaces charg-Bes"); - TextCore.RSLoadingPleaseWait = RSString.parse("Chargement de RuneScape en cours )2 veuillez patienter)3)3)3"); - TextCore.TextFlashOne = RSString.parse("clignotant1:"); - TextCore.HasPleaseRemove = RSString.parse("Veuillez commencer par supprimer "); - TextCore.TextColorGreen = RSString.parse("vert:"); - TextCore.HasHidden = RSString.parse("Cach-B"); - TextCore.LoadingPleaseWait2 = RSString.parse("Chargement en cours)3 Veuillez patienter)3"); - TextCore.ConxUpdateServer = RSString.parse("Connect-B au serveur de mise -9 jour"); - TextCore.HasLoggedOut = RSString.parse(" s(West d-Bconnect-B)3"); - TextCore.LoadingTextures = RSString.parse("Chargement des textures )2 "); - TextCore.HasUse = RSString.parse("Utiliser"); - TextCore.HasScroll = RSString.parse("d-Broulement:"); - TextCore.LoadingWLD = RSString.parse("Chargement de la liste des serveurs"); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "bb.C(" + (byte) 121 + ')'); - } - } - - - //Class3_Sub28_Sub11 English to German - static void englishToGerman() { - try { - TextCore.TextWave = RSString.parse("welle:"); - TextCore.HasMoreOptions = RSString.parse(" weitere Optionen"); - TextCore.HasPleaseRemove = RSString.parse("Bitte entfernen Sie "); - TextCore.LoadedConfig = RSString.parse("Konfig geladen)3"); - TextCore.LoadedFonts = RSString.parse("Schrifts-=tze geladen)3"); - TextCore.HasFriendsToIgnore = RSString.parse(" zuerst von Ihrer Freunde)2Liste(Q"); - TextCore.HasWishToTrade = RSString.parse("m-Ochte mit Ihnen handeln)3"); - TextCore.TextFlashThree = RSString.parse("blinken3:"); - TextCore.HasFriendsListFull = RSString.parse("Ihre Freunde)2Liste ist voll(Q Maximale Eintr-=ge: Mitglieder 200)4freie Spieler 100"); - TextCore.HasOnOwnIgnoreList = RSString.parse("Sie k-Onnen sich selbst nicht selbst auf Ihre Ignorieren)2Liste setzen(Q"); - TextCore.CheckingForUpdates = RSString.parse("Suche nach Updates )2 "); - TextCore.TextColorWhite = RSString.parse("weiss:"); - TextCore.HasDrop = RSString.parse("Fallen lassen"); - TextCore.HasLoggedIn = RSString.parse(" loggt sich ein)3"); - TextCore.HasRating = RSString.parse("Kampfstufe: "); - TextCore.ThousandK = RSString.parse("T"); - TextCore.LoadingWLD = RSString.parse("Lade Liste der Welten"); - TextCore.Spacer = RSString.parse(": "); - TextCore.TextColorGreen = RSString.parse("gr-Un:"); - TextCore.TextGlowOne = RSString.parse("leuchten1:"); - TextCore.LoadedInputHandler = RSString.parse("Eingabeprozedur geladen)3"); - TextCore.TextColorRed = RSString.parse("rot:"); - TextCore.HasOK = RSString.parse("Okay"); - TextCore.RSLoadingPleaseWait = RSString.parse("RuneScape wird geladen )2 bitte warten)3)3)3"); - TextCore.TextColorPurple = RSString.parse("lila:"); - TextCore.CreatedWorld = RSString.parse("Spielwelt erstellt)3"); - TextCore.HasPleaseRemove = RSString.parse("Bitte entfernen Sie "); - TextCore.LoadingFonts = RSString.parse("Lade Schrifts-=tze )2 "); - TextCore.HasCancel = RSString.parse("Abbrechen"); - TextCore.TextShake = RSString.parse("sch-Utteln:"); - TextCore.HasIgnoreListFull = RSString.parse("Ihre Ignorieren)2Liste ist voll)1 Sie k-Onnen nur 100 Spieler darauf eintragen)3"); - TextCore.HasContinue = RSString.parse("Weiter"); - TextCore.LoadingPleaseWait2 = RSString.parse("Ladevorgang )2 bitte warten Sie)3"); - TextCore.HasAttack = RSString.parse("Angreifen"); - TextCore.LoadedSprites = RSString.parse("Sprites geladen)3"); - TextCore.LoadingTextures = RSString.parse("Lade Texturen )2 "); - TextCore.TextColorCyan = RSString.parse("blaugr-Un:"); - TextCore.HasExamine = RSString.parse("Untersuchen"); - TextCore.ConxLost = RSString.parse("Verbindung abgebrochen)3"); - TextCore.HasSkill = RSString.parse("Fertigkeit: "); - TextCore.MembersObject = RSString.parse("Gegenstand f-Ur Mitglieder"); - TextCore.LoadedWLD = RSString.parse("Liste der Welten geladen"); - TextCore.HasIgnoreToFriends = RSString.parse(" zuerst von Ihrer Ignorieren)2Liste(Q"); - TextCore.LoadingPleaseWait = RSString.parse("Bitte warten Sie)3)3)3"); - TextCore.HasLoggedOut = RSString.parse(" loggt sich aus)3"); - TextCore.Starting3DLibrary = RSString.parse("Starte 3D)2Softwarebibliothek)3"); - TextCore.ThousandK = RSString.parse("T"); - TextCore.HasSelect = RSString.parse("Ausw-=hlen"); - TextCore.TextGlowTwo = RSString.parse("leuchten2:"); - TextCore.HasTake = RSString.parse("Nehmen"); - TextCore.HasUnableFind = RSString.parse("Spieler kann nicht gefunden werden: "); - TextCore.LoadingTitleScreen = RSString.parse("Lade Titelbild )2 "); - TextCore.LoadingConnecting = RSString.parse("Verbindung mit Update)2Server)3)3)3"); - TextCore.HasWalkHere = RSString.parse("Hierhin gehen"); - TextCore.HasFaceHere = RSString.parse("Hierhin drehen"); - TextCore.LoadingSprites = RSString.parse("Lade Sprites )2 "); - TextCore.MillionM = RSString.parse("M"); - TextCore.HasChooseOptions = RSString.parse("W-=hlen Sie eine Option"); - TextCore.Started3DLibrary = RSString.parse("3D)2Softwarebibliothek gestartet)3"); - TextCore.HasUse = RSString.parse("Benutzen"); - TextCore.AllocatingMemory = RSString.parse("Speicher wird zugewiesen)3"); - TextCore.AttemptingReestablish = RSString.parse("Bitte warten Sie )2 es wird versucht)1 die Verbindung wiederherzustellen)3"); - TextCore.LoadedInterfaces = RSString.parse("Benutzeroberfl-=che geladen)3"); - TextCore.TextSlide = RSString.parse("gleiten:"); - TextCore.LoadedUpdateList = RSString.parse("Update)2Liste geladen)3"); - TextCore.TextColorYellow = RSString.parse("gelb:"); - TextCore.TextFlashTwo = RSString.parse("blinken2:"); - TextCore.TextFlashOne = RSString.parse("blinken1:"); - TextCore.LoadingGeneral = RSString.parse("Lade)3)3)3"); - TextCore.MillionM = RSString.parse("M"); - TextCore.LoadingConfig = RSString.parse("Lade Konfiguration )2 "); - TextCore.AllocatedMemory = RSString.parse("Zugewiesener Speicher)3"); - TextCore.LoadedTitleScreen = RSString.parse("Titelbild geladen)3"); - TextCore.TextGlowThree = RSString.parse("leuchten3:"); - TextCore.HasOnOwnFriendsList = RSString.parse("Sie k-Onnen sich selbst nicht auf Ihre Freunde)2Liste setzen(Q"); - TextCore.LoadedWordPack = RSString.parse("Wordpack geladen)3"); - TextCore.HasIgnoreAlready = RSString.parse(" steht bereits auf Ihrer Ignorieren)2Liste(Q"); - TextCore.ConxUpdateServer = RSString.parse("Verbindung zum Update)2Server hergestellt)3"); - TextCore.HasDiscard = RSString.parse("Ablegen"); - TextCore.HasHidden = RSString.parse("Versteckt"); - TextCore.TextWaveTwo = RSString.parse("welle2:"); - TextCore.LoadingInterfaces = RSString.parse("Lade Benutzeroberfl-=che )2 "); - TextCore.HasFriendsAlready = RSString.parse(" steht bereits auf Ihrer Freunde)2Liste(Q"); - TextCore.OpenedTitleScreen = RSString.parse("Titelbild ge-Offnet)3"); - TextCore.HasClose = RSString.parse("Schlie-8en"); - TextCore.HasScroll = RSString.parse("scrollen:"); - TextCore.LoadedTextures = RSString.parse("Texturen geladen)3"); - TextCore.LoadingWordPack = RSString.parse("Lade Wordpack )2 "); - TextCore.PreparedSoundEngine = RSString.parse("Musik)2Engine vorbereitet)3"); - TextCore.HasLevel = RSString.parse("Stufe: "); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "lk.A(" + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/Unsorted.java b/Client/src/main/java/org/runite/client/Unsorted.java deleted file mode 100644 index 747b5fbb2..000000000 --- a/Client/src/main/java/org/runite/client/Unsorted.java +++ /dev/null @@ -1,5337 +0,0 @@ -package org.runite.client; - -import org.rs09.SlayerTracker; -import org.rs09.XPGainDraw; -import org.rs09.client.config.GameConfig; -import org.rs09.client.console.DeveloperConsole; -import org.rs09.client.data.HashTable; -import org.rs09.client.data.ReferenceCache; -import org.rs09.client.net.Connection; -import org.rs09.client.rendering.Toolkit; -import org.rs09.client.util.ArrayUtils; -import org.runite.client.drawcalls.*; - -import java.awt.*; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -public class Unsorted { - public static boolean aBoolean2146 = false; - public static int incomingOpcode = 0; - public static int anInt2148 = 0; - public static boolean aBoolean2154; - public static int[] anIntArray882 = new int[200]; - public static int anInt144; - public static int anInt136 = 0; - public static byte[][][] aByteArrayArrayArray113 = new byte[4][104][104]; - public static int[] anIntArray39; - public static int anInt40 = 0; - public static Connection js5Connection; - public static int anInt42 = 0; - public static RSString[] aClass94Array45; - public static short aShort46 = 256; - public static boolean aBoolean47 = false; - public static int anInt48 = 2; - public static int[] anIntArray49; - public static int anInt1234; - public static Class33 aClass33_1238; - public static LinkedList aLinkedList_1242 = new LinkedList(); - public static int anInt1244 = -1; - public static RSInterface aClass11_1933; - public static float[] aFloatArray1934 = new float[]{0.073F, 0.169F, 0.24F, 1.0F}; - public static Class8 aClass8_1936; - public static int[] anIntArray2952 = new int[128]; - public static int anInt2958 = 0; - public static LinkedList aLinkedList_78 = new LinkedList(); - public static int anInt72 = 0; - public static boolean[] aBooleanArray73 = new boolean[200]; - public static boolean[] aBooleanArray1703; - public static int incomingPacketLength = 0; - public static int anInt1705 = 0; - public static int anInt1709 = 0; - public static int anInt1711 = -2; - public static boolean[] aBooleanArray1712 = new boolean[100]; - public static ReferenceCache aReferenceCache_1131 = new ReferenceCache(5); - public static ReferenceCache aReferenceCache_1135 = new ReferenceCache(4); - public static AbstractSprite[] aAbstractSpriteArray1136; - public static int anInt1137 = 2; - public static int[] anIntArray1138; - public static int anInt3625 = 3; - public static int anInt3631; - public static Class25[] aClass25Array4060; - public static int anInt4062 = 0; - public static boolean aBoolean4063 = false; - public static boolean aBoolean4068 = true; - public static Class3_Sub2[][][] aClass3_Sub2ArrayArrayArray4070; - public static int anInt4073; - public static CacheIndex aClass153_332; - public static int[] anIntArray2591; - public static RSString[] aClass94Array2596 = null; - public static Class3_Sub28_Sub3 aClass3_Sub28_Sub3_2600; - public static Class151_Sub1[] aClass151_Sub1Array2601 = new Class151_Sub1[29]; //TODO - public static boolean aBoolean1080 = false; - public static int anInt1081 = 0; - public static int anInt1082; - public static int[] anIntArray1083; - public static boolean aBoolean1084 = false; - public static int anInt1088 = 0; - public static int anInt1950; - public static boolean aBoolean1951 = false; - public static int anInt1953; - public static int anInt1881 = 0; - public static int anInt1887; - public static int anInt14 = 0; - public static int anInt15 = 0; - public static ReferenceCache aReferenceCache_21 = new ReferenceCache(64); - public static int registryStage = 0; - public static int anInt25 = 1; - public static int anInt3657; - public static int anInt3660 = 0; - public static boolean aBoolean3665 = true; - public static LinkedList aLinkedList_2468 = new LinkedList(); - public static int[] anIntArray2469; - public static int[] anIntArray2470 = new int[]{0, 0, 2, 0, 0, 2, 1, 1, 0}; - public static boolean paramJavaScriptEnabled = false; - public static int anInt3642 = 0; - public static int anInt3644 = 0; - public static int anInt59 = 0; - public static int anInt65; - public static boolean isMember = false; - public static int anInt67; - public static ShaderInterface[] anShaderInterfaceArray70; - public static boolean aBoolean742 = false; - public static ReferenceCache aReferenceCache_743 = new ReferenceCache(20); - public static CacheIndex quickChatMessages; - public static byte[][][] aByteArrayArrayArray1328; - public static int anInt3602; - public static int anInt3603; - public static boolean aBoolean3604 = true; - public static int[][][] anIntArrayArrayArray3605; - public static int anInt3606; - public static int[] anIntArray3607 = new int[]{0, 2, 2, 2, 1, 1, 2, 2, 1, 3, 1, 1}; - public static ReferenceCache aReferenceCache_4043 = new ReferenceCache(64); - public static volatile int anInt4045 = 0; - public static HashTable aHashTable_4046 = new HashTable(16); - public static MouseListeningClass aClass149_4047 = new MouseListeningClass(); - public static CacheIndex aClass153_4048; - public static int[] anIntArray4050 = new int[1000]; - public static ReferenceCache aReferenceCache_4051 = new ReferenceCache(30); - public static int gameWindowWidth = 0; - public static Player[] players = new Player[2048]; - static int anInt942; - static int anInt944 = 0; - static int anInt952; - static int anInt963; - static int anInt1037; - static int anInt1038; - static Class30 aClass30_1039; - static int anInt3695; - static int anInt1042; - static CacheIndex aClass153_1043; - static AbstractSprite[] aAbstractSpriteArray996; - static int maskUpdateCount = 0; - static int anInt998 = 0; - static int anInt999 = -1; - static int wlPacketSize = 0; - static ReferenceCache aReferenceCache_684 = new ReferenceCache(64); - static int[] anIntArray686 = new int[2]; - static int anInt688 = 0; - static int anInt689; - static RSString[] aClass94Array2566 = new RSString[200]; - static int anInt2567 = -1; - public static int clanSize; - static int[] anIntArray2574 = new int[14]; - static int anInt2577 = 0; - static boolean aBoolean29 = false; - static int anInt30; - static int anInt31 = 0; - static int anInt3671 = 0; - static boolean[] aBooleanArray3674 = new boolean[100]; - static int anInt2281 = 0; - static int anInt2309 = 128; - static boolean aBoolean2311; - static int frameWidth; - static int anInt3071 = 0; - static int anInt3070 = 0; - static int[] anIntArray3076; - static int anInt3155; - static WorldListEntry[] aClass44_Sub1Array3201 = new WorldListEntry[0]; - static CacheIndex aClass153_3361; - static Class52 aClass52_1112 = new Class52(); - static int anInt1165 = -1; - static byte[][][] aByteArrayArrayArray1774; - static int[][] anIntArrayArray2039 = new int[][]{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, {12, 8, 4, 0, 13, 9, 5, 1, 14, 10, 6, 2, 15, 11, 7, 3}, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, {3, 7, 11, 15, 2, 6, 10, 14, 1, 5, 9, 13, 0, 4, 8, 12}}; - public static int menuOptionCount = 0; - static boolean aBoolean11 = false; - public static Class158 aClass158_3009; - static long[] aLongArray3271 = new long[500]; - static boolean aBoolean3275 = true; - static int anInt154 = 0; - static RSInterface aClass11_2091 = null; - static int anInt2099 = 0; - - static void method2086() { - try { - int var1 = Class102.player.xAxis + TextureOperation20.anInt3216; - int var2 = Class102.player.zAxis - -anInt42; - if (-var1 + anInt3155 < -500 || -var1 + anInt3155 > 500 || anInt942 + -var2 < -500 || -var2 + anInt942 > 500) { - anInt3155 = var1; - anInt942 = var2; - } - - if (var2 != anInt942) { - anInt942 += (-anInt942 + var2) / 16; - } - - if (var1 != anInt3155) { - anInt3155 += (-anInt3155 + var1) / 16; - } - - if (AudioThread.aBoolean346) { - for (int var3 = 0; var3 < Class3_Sub23.anInt2537; ++var3) { - int var4 = Class133.inputTextCodeArray[var3]; - if (98 == var4) { - anInt2309 = -16 & anInt2309 + 47; - } else if (var4 == 99) { - anInt2309 = -16 & anInt2309 - 17; - } else if (var4 == 96) { - GraphicDefinition.CAMERA_DIRECTION = GraphicDefinition.CAMERA_DIRECTION - 65 & -128; - } else if (var4 == 97) { - GraphicDefinition.CAMERA_DIRECTION = GraphicDefinition.CAMERA_DIRECTION + 191 & -128; - } - } - } else { - if (ObjectDefinition.aBooleanArray1490[98]) { - Class27.anInt517 += (-Class27.anInt517 + 12) / 2; - } else if (!ObjectDefinition.aBooleanArray1490[99]) { - Class27.anInt517 /= 2; - } else { - Class27.anInt517 += (-Class27.anInt517 + -12) / 2; - } - - if (!ObjectDefinition.aBooleanArray1490[96]) { - if (ObjectDefinition.aBooleanArray1490[97]) { - anInt2281 += (-anInt2281 + 24) / 2; - } else { - anInt2281 /= 2; - } - } else { - anInt2281 += (-anInt2281 + -24) / 2; - } - - anInt2309 += Class27.anInt517 / 2; - GraphicDefinition.CAMERA_DIRECTION += anInt2281 / 2; - } - - method1098((byte) -94); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "uf.B(" + (byte) 68 + ')'); - } - } - - static boolean method1529(int var0, int var1, int var2, int var3, int var4, int var6) { - try { - long var8 = Class157.method2174(var6, var1, var3 + var0); - int var10; - int var11; - int var12; - ObjectDefinition var13; - int var14; - int[] var15; - int var16; - if (var8 != 0) { - var10 = 3 & (int) var8 >> 20; - var11 = (508650 & (int) var8) >> 14; - var12 = Integer.MAX_VALUE & (int) (var8 >>> 32); - var13 = ObjectDefinition.getObjectDefinition(var12); - if (var13.anInt1516 == -1) { - var14 = var2; - if (var8 > 0) { - var14 = var4; - } - - var15 = Toolkit.JAVA_TOOLKIT.getBuffer(); - var16 = 4 * (-(var3 * 512) + '\uce00') + var1 * 4 + 24624; - if (var11 == 0 || var11 == 2) { - if (var10 == 0) { - var15[var16] = var14; - var15[512 + var16] = var14; - var15[var16 - -1024] = var14; - var15[1536 + var16] = var14; - } else if (var10 == 1) { - var15[var16] = var14; - var15[1 + var16] = var14; - var15[var16 - -2] = var14; - var15[var16 - -3] = var14; - } else if (var10 == 2) { - var15[var16 - -3] = var14; - var15[var16 - -3 - -512] = var14; - var15[var16 - -3 + 1024] = var14; - var15[var16 + 3 + 1536] = var14; - } else { - var15[var16 + 1536] = var14; - var15[1536 + var16 - -1] = var14; - var15[var16 + 1538] = var14; - var15[3 + var16 + 1536] = var14; - } - } - - if (var11 == 3) { - if (var10 == 0) { - var15[var16] = var14; - } else if (1 == var10) { - var15[var16 - -3] = var14; - } else if (var10 == 2) { - var15[var16 - -3 + 1536] = var14; - } else { - var15[var16 - -1536] = var14; - } - } - - if (var11 == 2) { - if (var10 == 3) { - var15[var16] = var14; - var15[var16 - -512] = var14; - var15[var16 + 1024] = var14; - var15[1536 + var16] = var14; - } else if (var10 == 0) { - var15[var16] = var14; - var15[1 + var16] = var14; - var15[2 + var16] = var14; - var15[3 + var16] = var14; - } else if (var10 == 1) { - var15[var16 - -3] = var14; - var15[512 + 3 + var16] = var14; - var15[3 + (var16 - -1024)] = var14; - var15[1536 + var16 + 3] = var14; - } else { - var15[1536 + var16] = var14; - var15[var16 - -1536 + 1] = var14; - var15[1536 + var16 + 2] = var14; - var15[var16 + 1539] = var14; - } - } - } else if (AudioThread.method888(var1, var13, var0, var3, var10)) { - return false; - } - } - - var8 = Class3_Sub28_Sub5.method557(var6, var1, var0 + var3); - if (var8 != 0L) { - var10 = (int) var8 >> 20 & 3; - var11 = ((int) var8 & 520964) >> 14; - var12 = (int) (var8 >>> 32) & Integer.MAX_VALUE; - var13 = ObjectDefinition.getObjectDefinition(var12); - if (var13.anInt1516 != -1) { - if (AudioThread.method888(var1, var13, var0, var3, var10)) { - return false; - } - } else if (var11 == 9) { - var14 = 15658734; - if (var8 > 0) { - var14 = 15597568; - } - - var16 = var1 * 4 + (24624 - -(2048 * (103 - var3))); - var15 = Toolkit.JAVA_TOOLKIT.getBuffer(); - if (var10 == 0 || var10 == 2) { - var15[1536 + var16] = var14; - var15[var16 - -1025] = var14; - var15[var16 + 512 + 2] = var14; - var15[var16 - -3] = var14; - } else { - var15[var16] = var14; - var15[var16 - -512 - -1] = var14; - var15[var16 - -1024 - -2] = var14; - var15[1536 + var16 - -3] = var14; - } - } - } - - var8 = Class3_Sub2.method104(var6, var1, var3 + var0); - if (var8 != 0L) { - var10 = (int) var8 >> 20 & 3; - var11 = (int) (var8 >>> 32) & Integer.MAX_VALUE; - ObjectDefinition var18 = ObjectDefinition.getObjectDefinition(var11); - return var18.anInt1516 == -1 || !AudioThread.method888(var1, var18, var0, var3, var10); - } - - return true; - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "na.N(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + 0 + ',' + var6 + ',' + true + ')'); - } - } - - static int method1535(WorldListEntry var0, WorldListEntry var1, int var2, int var3, int var4, boolean var5, boolean var6) { - try { - int var7 = Class161.method2201(var1, var4, var2 + -5638, var0, var6); - if (var7 == 0) { - if (var2 != 5730) { - return -76; - } else if (var3 == -1) { - return 0; - } else { - int var8 = Class161.method2201(var1, var3, var2 ^ 5651, var0, var5); - return !var5 ? var8 : -var8; - } - } else { - return !var6 ? var7 : -var7; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "na.D(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - static SoftwareSprite method1537(CacheIndex var0, int var1) { - try { - if (Class140_Sub7.method2029((byte) -118, var0, var1)) { - - return Class117.method1722(-93); - } else { - return null; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "na.GB(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + false + ')'); - } - } - - static LDIndexedSprite method1539(int var2, CacheIndex var3) { - try { - // System.out.println("RSString " + var2); - return Class75_Sub4.method1351(var3, 0, var2) ? Class77.method1364() : null; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "na.MA(" + 0 + ',' + true + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - public static RSString method802(int var0) { - try { - - return Class163_Sub2_Sub1.aClass94Array4016[var0].length() > 0 ? RSString.stringCombiner(new RSString[]{Class140_Sub7.aClass94Array2935[var0], TextCore.Spacer, Class163_Sub2_Sub1.aClass94Array4016[var0]}) : Class140_Sub7.aClass94Array2935[var0]; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "wa.RB(" + var0 + ',' + true + ')'); - } - } - - public static void method83(byte var0) { - try { - ItemDefinition.stringsStack = null; - if (var0 != 30) { - method84(null, 89); - } - - aLinkedList_78 = null; - aBooleanArray73 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ab.H(" + var0 + ')'); - } - } - - static void calculateInterfacePosition(RSInterface iface, int parentWidth, int parentHeight) { - if (iface.verticalPos == 0) { - iface.anInt210 = iface.defY; - } else if (iface.verticalPos == 1) { - iface.anInt210 = (parentHeight - iface.height) / 2 + iface.defY; - } else if (iface.verticalPos == 2) { - iface.anInt210 = parentHeight - iface.height - iface.defY; - } else if (iface.verticalPos == 3) { - iface.anInt210 = iface.defY * parentHeight >> 14; - } else if (iface.verticalPos == 4) { - iface.anInt210 = (parentHeight * iface.defY >> 14) + (-iface.height + parentHeight) / 2; - } else { - iface.anInt210 = -(parentHeight * iface.defY >> 14) + -iface.height + parentHeight; - } - - if (0 == iface.horizontalPos) { - iface.anInt306 = iface.defX; - } else if (iface.horizontalPos == 1) { - iface.anInt306 = iface.defX + (parentWidth - iface.width) / 2; - } else if (iface.horizontalPos == 2) { - iface.anInt306 = -iface.defX + -iface.width + parentWidth; - } else if (3 == iface.horizontalPos) { - iface.anInt306 = iface.defX * parentWidth >> 14; - } else if (4 == iface.horizontalPos) { - iface.anInt306 = (iface.defX * parentWidth >> 14) + (parentWidth - iface.width) / 2; - } else { - iface.anInt306 = -(parentWidth * iface.defX >> 14) + parentWidth + -iface.width; - } - - if (ClientCommands.commandQaOpEnabled && (Client.method44(iface).anInt2205 != 0 || iface.type == 0)) { - if (iface.anInt210 < 0) { - iface.anInt210 = 0; - } else if (iface.height + iface.anInt210 > parentHeight) { - iface.anInt210 = parentHeight + -iface.height; - } - - if (0 > iface.anInt306) { - iface.anInt306 = 0; - } else if (parentWidth < iface.anInt306 - -iface.width) { - iface.anInt306 = parentWidth + -iface.width; - } - } - } - - static void method1225() { - try { - MouseListeningClass var1 = aClass149_4047; - synchronized (var1) { - - TextureOperation21.anInt3069 = GraphicDefinition.anInt549; - Class126.anInt1676 = Class3_Sub21.anInt2493; - anInt1709 = MouseListeningClass.anInt1340; - anInt3644 = Class140_Sub3.anInt2743; - Class163_Sub1.anInt2993 = RenderAnimationDefinition.anInt362; - ++anInt4045; - Class38_Sub1.anInt2614 = TextureOperation14.anInt3389; - Class75.aLong1102 = Class140_Sub6.aLong2926; - Class140_Sub3.anInt2743 = 0; - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ii.D(" + 18074 + ')'); - } - } - - static void method848() { - try { - if (Class44.aFloat727 < NPC.aFloat3979) { - Class44.aFloat727 = (float) ((double) Class44.aFloat727 + (double) Class44.aFloat727 / 30.0D); - if (NPC.aFloat3979 < Class44.aFloat727) { - Class44.aFloat727 = NPC.aFloat3979; - } - - Class3_Sub5.method117(); - } else if (NPC.aFloat3979 < Class44.aFloat727) { - Class44.aFloat727 = (float) ((double) Class44.aFloat727 - (double) Class44.aFloat727 / 30.0D); - if (NPC.aFloat3979 > Class44.aFloat727) { - Class44.aFloat727 = NPC.aFloat3979; - } - - Class3_Sub5.method117(); - } - - if (Texture.anInt1150 != -1 && -1 != TextureOperation13.anInt3362) { - int var1 = -Class3_Sub28_Sub1.anInt3536 + Texture.anInt1150; - if (2 > var1 || var1 > 2) { - var1 >>= 4; - } - - int var2 = -Class3_Sub4.anInt2251 + TextureOperation13.anInt3362; - if (var2 < 2 || var2 > 2) { - var2 >>= 4; - } - - Class3_Sub4.anInt2251 -= -var2; - Class3_Sub28_Sub1.anInt3536 += var1; - if (0 == var1 && 0 == var2) { - Texture.anInt1150 = -1; - TextureOperation13.anInt3362 = -1; - } - - Class3_Sub5.method117(); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bb.A(" + 4 + ')'); - } - } - - static int bitwiseAnd(int var0, int var1) { - try { - return var0 & var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "qc.C(" + var0 + ',' + var1 + ')'); - } - } - - static LDIndexedSprite[] method1281() { - try { - LDIndexedSprite[] var1 = new LDIndexedSprite[Class95.anInt1338]; - - for (int var2 = 0; Class95.anInt1338 > var2; ++var2) { - var1[var2] = new LDIndexedSprite(Class3_Sub15.anInt2426, Class133.anInt1748, Class164.anIntArray2048[var2], anIntArray2591[var2], Class140_Sub7.anIntArray2931[var2], anIntArray3076[var2], Class163_Sub1.aByteArrayArray2987[var2], TextureOperation38.spritePalette); - } - - Class39.method1035((byte) 116); - return var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "jg.A(" + 0 + ')'); - } - } - - public static void method1282(int var0, int var2, int var3, int var4) { - try { - for (int var5 = 0; var5 < Class3_Sub28_Sub3.anInt3557; ++var5) { - if (var0 < AudioChannel.anIntArray1969[var5] - -Class3_Sub28_Sub18.anIntArray3768[var5] && var0 + var4 > AudioChannel.anIntArray1969[var5] && var2 < Player.anIntArray3954[var5] - -Class140_Sub4.anIntArray2794[var5] && var3 + var2 > Player.anIntArray3954[var5]) { - Class163_Sub1_Sub1.aBooleanArray4008[var5] = true; - } - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "jg.C(" + var0 + ',' + (byte) 122 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - static Class3_Sub15 method1245(CacheIndex var1, int var2) { - try { - - byte[] var3 = var1.method2138(var2); - return var3 != null ? new Class3_Sub15(var3) : null; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "jb.F(" + 117 + ',' + (var1 != null ? "{...}" : "null") + ',' + var2 + ')'); - } - } - - static void method1250(int var0, boolean var1) { - try { - Class3_Sub10.aByteArrayArrayArray2339 = null; - Class44.anIntArrayArrayArray720 = null; - if (var0 < 14) { - method1250(10, true); - } - - Class3_Sub28_Sub3.aClass11_3551 = null; - RenderAnimationDefinition.aByteArrayArrayArray383 = null; - Class83.anIntArray1161 = null; - CS2Script.aByteArrayArrayArray2452 = null; - if (var1 && null != aClass3_Sub28_Sub3_2600) { - Class3_Sub20.aClass94_3220 = aClass3_Sub28_Sub3_2600.aClass94_3561; - } else { - Class3_Sub20.aClass94_3220 = null; - } - - Class36.aByteArrayArrayArray640 = null; - TextureOperation29.aByteArrayArrayArray3390 = null; - Class29.anIntArrayArrayArray558 = null; - Class146.anIntArrayArrayArray1903 = null; - WorldMap.anInt2737 = 0; - aClass3_Sub28_Sub3_2600 = null; - Class84.aLinkedList_1162.method1211(-108); - Class119.aClass131_1624 = null; - TextureOperation13.anInt3362 = -1; - Class75_Sub2.aClass33_2648 = null; - Class91.aClass33_1305 = null; - aClass33_1238 = null; - Class161.aClass33_2034 = null; - Class164_Sub2.aClass33_3019 = null; - Class99.aClass33_1399 = null; - Class75_Sub2.aClass33_2637 = null; - Class119.aClass33_1626 = null; - Class36.aAbstractSprite_637 = null; - Texture.anInt1150 = -1; - WorldMap.aSoftwareSprite_3221 = null; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "jb.E(" + var0 + ',' + var1 + ')'); - } - } - - public static int[] method62() { - try { - - int[] var8 = new int[2048]; - TextureOperation34 var9 = new TextureOperation34(); - var9.anInt3060 = 8; - var9.anInt3058 = 4; - var9.anInt3067 = 35; - var9.anInt3056 = 8; - var9.anInt3062 = (int) ((float) 0.4 * 4096.0F); - var9.aBoolean3065 = true; - var9.postDecode(); - TextureOperation33.method180(-106, 1, 2048); - var9.method186(0, var8); - return var8; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "qk.A(" + true + ',' + 14585 + ',' + 8 + ',' + 2048 + ',' + 4 + ',' + (float) 0.4 + ',' + 8 + ',' + 35 + ')'); - } - } - - public static int method64(int var1) { - try { - return var1 >>> 8; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "qk.D(" + true + ',' + var1 + ')'); - } - } - - public static short[] method65(short[] var1) { - try { - if (null == var1) { - return null; - } else { - short[] var2 = new short[var1.length]; - ArrayUtils.arraycopy(var1, 0, var2, 0, var1.length); - return var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "vg.A(" + 23032 + ',' + "{...}" + ')'); - } - } - - public static void method66(RSString var0, int var1, int var2, byte var3, int var4) { - try { - RSInterface var5 = AbstractSprite.method638(var4, var1); - if (null != var5) { - if (var5.anObjectArray314 != null) { - CS2Script var6 = new CS2Script(); - var6.arguments = var5.anObjectArray314; - var6.aClass11_2449 = var5; - var6.aClass94_2439 = var0; - var6.interfaceButtons = var2; // Set to 1 for jukebox/friends/all/game chat. set to non 1 for all other chat settings - Class43.method1065(var6); - } - - boolean var8 = true; - if (0 < var5.anInt189) { - var8 = BufferedDataStream.method715(var5); - } - - if (var8) { - if (Client.method44(var5).method92(var2 - 1, (byte) -108)) { - if (1 == var2) { - TextureOperation12.outgoingBuffer.putOpcode(155); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (var3 < -7) { - if (var2 == 2) { - TextureOperation12.outgoingBuffer.putOpcode(196); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (var2 == 3) { - TextureOperation12.outgoingBuffer.putOpcode(124); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (var2 == 4) { - TextureOperation12.outgoingBuffer.putOpcode(199); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (var2 == 5) { - TextureOperation12.outgoingBuffer.putOpcode(234); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (6 == var2) { - TextureOperation12.outgoingBuffer.putOpcode(168); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (var2 == 7) { - TextureOperation12.outgoingBuffer.putOpcode(166); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (var2 == 8) { - TextureOperation12.outgoingBuffer.putOpcode(64); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (var2 == 9) { - TextureOperation12.outgoingBuffer.putOpcode(53); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - if (var2 == 10) { - TextureOperation12.outgoingBuffer.putOpcode(9); - TextureOperation12.outgoingBuffer.writeInt(var4); - TextureOperation12.outgoingBuffer.writeShort(var1); - } - - } - } - } - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "vg.D(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - public static void method68(int var0, Class140_Sub4 var2) { - try { - if (var2.anInt2800 <= Class44.anInt719) { - if (var2.anInt2790 >= Class44.anInt719) { - method2270(var2); - } else { - method1180((byte) -22, var2); - } - } else { - Class140_Sub2.method1950(var2); - } - - if (var2.xAxis < 128 || var2.zAxis < 128 || var2.xAxis >= 13184 || var2.zAxis >= 13184) { - var2.anInt2771 = -1; - var2.anInt2842 = -1; - var2.anInt2800 = 0; - var2.anInt2790 = 0; - var2.xAxis = 128 * var2.anIntArray2767[0] - -(64 * var2.getSize()); - var2.zAxis = var2.anIntArray2755[0] * 128 + var2.getSize() * 64; - var2.method1973(2279 + -2395); - } - - if (var2 == Class102.player && (var2.xAxis < 1536 || var2.zAxis < 1536 || var2.xAxis >= 11776 || var2.zAxis >= 11776)) { - var2.anInt2842 = -1; - var2.anInt2800 = 0; - var2.anInt2790 = 0; - var2.anInt2771 = -1; - var2.xAxis = var2.anIntArray2767[0] * 128 + var2.getSize() * 64; - var2.zAxis = 128 * var2.anIntArray2755[0] + 64 * var2.getSize(); - var2.method1973(-98); - } - - Class17.method904(65536, var2); - RenderAnimationDefinition.method900(var2); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "vg.C(" + var0 + ',' + 2279 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - public static void method1470(int var0, SequenceDefinition var1, int var3, boolean var4, int var5) { - try { - if (AudioHandler.currentSoundEffectCount < 50) { - if (var1.sounds != null && var1.sounds.length > var5 && null != var1.sounds[var5]) { - int var6 = var1.sounds[var5][0]; - int var7 = var6 >> 8; - int var10; - if (1 < var1.sounds[var5].length) { - var10 = (int) ((double) var1.sounds[var5].length * Math.random()); - if (0 < var10) { - var7 = var1.sounds[var5][var10]; - } - } - - int var8 = var6 >> 5 & 7; - int var9 = var6 & 31; - if (var9 == 0) { - if (var4) { - AudioHandler.soundEffectHandler(var8, var7, 0); - } - - } else if (0 != Sprites.ambientVolume) { - AudioHandler.soundEffectIDs[AudioHandler.currentSoundEffectCount] = var7; - AudioHandler.soundEffectVolumeArray[AudioHandler.currentSoundEffectCount] = var8; - int var11 = (-64 + var0) / 128; - var10 = (var3 + -64) / 128; - AudioHandler.soundEffectDelayArray[AudioHandler.currentSoundEffectCount] = 0; - AudioHandler.soundEffects[AudioHandler.currentSoundEffectCount] = null; - AudioHandler.soundEffectCoordinates[AudioHandler.currentSoundEffectCount] = var9 + (var10 << 16) + (var11 << 8); - - ++AudioHandler.currentSoundEffectCount; - } - } - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "ma.C(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - public static void method1460(int var0, int var1, int var3, int var4, int var5, int var6) { - try { - if (Class101.anInt1425 <= var5 - var4 && Class3_Sub28_Sub18.anInt3765 >= var5 - -var4 && Class159.anInt2020 <= -var4 + var1 && Class57.anInt902 >= var4 + var1) { - TextureOperation6.method175(var6, var0, var1, var3, var4, var5); - } else { - method2275(var3, var1, var4, var6, var0, var5); - } - - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ma.A(" + var0 + ',' + var1 + ',' + (byte) -113 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - public static void method1469(int[] var0, int var1, int var2, int var3, int var4, int var5) { - Class3_Sub2 var6 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var3][var4][var5]; - if (var6 != null) { - Class126 var7 = var6.aClass126_2240; - int var9; - if (var7 == null) { - Class35 var8 = var6.aClass35_2226; - if (var8 != null) { - var9 = var8.anInt611; - int var10 = var8.anInt612; - int var11 = var8.anInt626; - int var12 = var8.anInt621; - int[] var13 = TextureOperation19.anIntArrayArray3215[var9]; - int[] var14 = anIntArrayArray2039[var10]; - int var15 = 0; - int var16; - if (var11 == 0) { - for (var16 = 0; var16 < 4; ++var16) { - if (var13[var14[var15++]] != 0) { - var0[var1] = var12; - } - - if (var13[var14[var15++]] != 0) { - var0[var1 + 1] = var12; - } - - if (var13[var14[var15++]] != 0) { - var0[var1 + 2] = var12; - } - - if (var13[var14[var15++]] != 0) { - var0[var1 + 3] = var12; - } - - var1 += var2; - } - } else { - for (var16 = 0; var16 < 4; ++var16) { - var0[var1] = var13[var14[var15++]] == 0 ? var11 : var12; - var0[var1 + 1] = var13[var14[var15++]] == 0 ? var11 : var12; - var0[var1 + 2] = var13[var14[var15++]] == 0 ? var11 : var12; - var0[var1 + 3] = var13[var14[var15++]] == 0 ? var11 : var12; - var1 += var2; - } - } - - } - } else { - int var17 = var7.anInt1673; - if (var17 != 0) { - for (var9 = 0; var9 < 4; ++var9) { - var0[var1] = var17; - var0[var1 + 1] = var17; - var0[var1 + 2] = var17; - var0[var1 + 3] = var17; - var1 += var2; - } - - } - } - } - } - - public static boolean method2096(int var0, int var1, int var2, long var3) { - Class3_Sub2 var5 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - if (var5 == null) { - return false; - } else if (var5.aClass70_2234 != null && var5.aClass70_2234.aLong1048 == var3) { - return true; - } else if (var5.aClass19_2233 != null && var5.aClass19_2233.aLong428 == var3) { - return true; - } else if (var5.aClass12_2230 != null && var5.aClass12_2230.aLong328 == var3) { - return true; - } else { - for (int var6 = 0; var6 < var5.anInt2223; ++var6) { - if (var5.aClass25Array2221[var6].aLong498 == var3) { - return true; - } - } - - return false; - } - } - - public static boolean method2103(int var0, int var1) { - try { - return var1 >= -78 || (var0 == 198 || 230 == var0 || var0 == 156 || var0 == 140 || 223 == var0); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "bg.P(" + var0 + ',' + var1 + ')'); - } - } - - public static void method2104(RSInterface var0, boolean var1, int var2) { - try { - int var4 = var0.anInt240 != 0 ? var0.anInt240 : var0.width; - int var5 = var0.anInt252 != 0 ? var0.anInt252 : var0.height; - Class158.method2183(var0.componentHash, var1, var4, var5, GameObject.aClass11ArrayArray1834[var0.componentHash >> 16]); - if (var0.aClass11Array262 != null) { - Class158.method2183(var0.componentHash, var1, var4, var5, var0.aClass11Array262); - } - - Class3_Sub31 var6 = TextureOperation23.aHashTable_3208.get(var0.componentHash); - if (var6 != null) { - Class75_Sub4.method1352(var5, var1, var6.anInt2602, var4); - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "bg.N(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ')'); - } - } - - public static void method84(RSString var0, int var1) { - try { - int var2 = method1602(var0); - if (var2 != -1) { - method565(Class119.aClass131_1624.aShortArray1727[var2], Class119.aClass131_1624.aShortArray1718[var2]); - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ab.N(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ')'); - } - } - - public static void languageSetter(int var1) { - try { - if (0 != var1) { - if (var1 == 1) { - Translation.englishToGerman(); - } else { - if (2 != var1) { - throw new RuntimeException(); - } - - Translation.englishToFrench(); - } - - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ab.K(" + ',' + var1 + ')'); - } - } - - public static Class106[] method88() { - try { - - if (Class56.aClass106Array890 == null) { - Class106[] var1 = method596(Class38.gameSignlink); - Class106[] var2 = new Class106[var1.length]; - int var3 = 0; - - label58: - for (Class106 var5 : var1) { - if ((0 >= var5.anInt1450 || var5.anInt1450 >= 24) && var5.anInt1447 >= 800 && 600 <= var5.anInt1449) { - for (int var6 = 0; var3 > var6; ++var6) { - Class106 var7 = var2[var6]; - if (var5.anInt1447 == var7.anInt1447 && var5.anInt1449 == var7.anInt1449) { - if (var7.anInt1450 < var5.anInt1450) { - var2[var6] = var5; - } - continue label58; - } - } - - var2[var3] = var5; - ++var3; - } - } - - Class56.aClass106Array890 = new Class106[var3]; - ArrayUtils.arraycopy(var2, 0, Class56.aClass106Array890, 0, var3); - int[] var9 = new int[Class56.aClass106Array890.length]; - - for (int var10 = 0; Class56.aClass106Array890.length > var10; ++var10) { - Class106 var11 = Class56.aClass106Array890[var10]; - var9[var10] = var11.anInt1449 * var11.anInt1447; - } - - Class108.method1658(var9, Class56.aClass106Array890); - } - - return Class56.aClass106Array890; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ab.M(" + (byte) 28 + ')'); - } - } - - public static void method89(CacheIndex var1, CacheIndex var2, CacheIndex var3, CacheIndex var4) { - try { - Class12.aClass153_323 = var2; - Class97.aClass153_1378 = var1; - aClass153_3361 = var3; - Class119.aClass153_1628 = var4; - - GameObject.aClass11ArrayArray1834 = new RSInterface[aClass153_3361.method2121()][]; - aBooleanArray1703 = new boolean[aClass153_3361.method2121()]; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ab.J(" + true + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ',' + (var3 != null ? "{...}" : "null") + ',' + (var4 != null ? "{...}" : "null") + ')'); - } - } - - public static void method1772(int plane, int animId, int dummy, NPC var3) { - try { - if (var3.anInt2771 == animId && -1 != animId) { - SequenceDefinition var4 = SequenceDefinition.getAnimationDefinition(animId); - int var5 = var4.delayType; - if (var5 == 1) { - var3.anInt2776 = 1; - var3.anInt2832 = 0; - var3.anInt2760 = 0; - var3.anInt2773 = 0; - var3.anInt2828 = plane; - method1470(var3.zAxis, var4, var3.xAxis, false, var3.anInt2832); - } - - if (var5 == 2) { - var3.anInt2773 = 0; - } - } else if (animId == -1 || -1 == var3.anInt2771 || SequenceDefinition.getAnimationDefinition(animId).forcedPriority >= SequenceDefinition.getAnimationDefinition(var3.anInt2771).forcedPriority) { - var3.anInt2760 = 0; - var3.anInt2771 = animId; - var3.anInt2776 = 1; - var3.anInt2773 = 0; - var3.anInt2828 = plane; - var3.anInt2811 = var3.anInt2816; - var3.anInt2832 = 0; - if (var3.anInt2771 != -1) { - method1470(var3.zAxis, SequenceDefinition.getAnimationDefinition(var3.anInt2771), var3.xAxis, false, var3.anInt2832); - } - } - - if (dummy != 39) { - anInt1711 = 41; - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "sc.J(" + plane + ',' + animId + ',' + dummy + ',' + (var3 != null ? "{...}" : "null") + ')'); - } - } - - public static void method1775() { - for (int var0 = 0; var0 < anInt3070; ++var0) { - Class25 var1 = SequenceDefinition.aClass25Array1868[var0]; - Class158.method2186(var1); - SequenceDefinition.aClass25Array1868[var0] = null; - } - - anInt3070 = 0; - } - - public static void method1783(Component var1) { - try { - var1.removeMouseListener(aClass149_4047); - var1.removeMouseMotionListener(aClass149_4047); - var1.removeFocusListener(aClass149_4047); - GraphicDefinition.anInt549 = 0; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "sc.M(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public static long method1395(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - return var3 != null && var3.aClass19_2233 != null ? var3.aClass19_2233.aLong428 : 0L; - } - - public static void method1396(int var0) { - try { - int var2 = Class106.rightMargin; - int var1 = Class84.leftMargin; - int var4 = -Class140_Sub7.canvasHeight + (Class70.frameHeight - var2); - int var3 = -var1 + frameWidth - Class23.canvasWidth; - if (~var1 < var0 || var3 > 0 || var2 > 0 || var4 > 0) { - try { - Object var5; - if (null != TextureOperation30.fullScreenFrame) { - var5 = TextureOperation30.fullScreenFrame; - } else if (GameShell.frame == null) { - var5 = Class38.gameSignlink.gameApplet; - } else { - var5 = GameShell.frame; - } - - int var7 = 0; - int var6 = 0; - if (GameShell.frame == var5) { - Insets var8 = GameShell.frame.getInsets(); - var6 = var8.left; - var7 = var8.top; - } - - Graphics var11 = ((Container) var5).getGraphics(); - var11.setColor(Color.black); - if (var1 > 0) { - var11.fillRect(var6, var7, var1, Class70.frameHeight); - } - - if (0 < var2) { - var11.fillRect(var6, var7, frameWidth, var2); - } - - if (var3 > 0) { - var11.fillRect(-var3 + var6 + frameWidth, var7, var3, Class70.frameHeight); - } - - if (var4 > 0) { - var11.fillRect(var6, -var4 + var7 + Class70.frameHeight, frameWidth, var4); - } - } catch (Exception var9) { - } - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "l.A(" + var0 + ')'); - } - } - - public static void method589(int var0, int var1, int var2) { - TextureOperation37.aBoolean3261 = true; - Class91.anInt1302 = var0; - Class49.anInt819 = var1; - TextureOperation18.anInt4039 = var2; - Class27.anInt515 = -1; - anInt999 = -1; - } - - public static boolean method590(byte var0, int var1, int var2) { - try { - if (11 == var2) { - var2 = 10; - } - - if (var2 >= 5 && var2 <= 8) { - var2 = 4; - } - - ObjectDefinition var4 = ObjectDefinition.getObjectDefinition(var1); - return var4.method1684(var2); - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "il.D(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - public static boolean method591(int var0) { - try { - KeyboardListener var1 = TextureOperation33.aClass148_3049; - synchronized (var1) { - if (Class3_Sub28_Sub9.anInt3620 == Class134.anInt1762) { - return false; - } else { - Class3_Sub28_Sub9.anInt3624 = Class129.anIntArray1693[Class3_Sub28_Sub9.anInt3620]; - TextureOperation7.anInt3342 = KeyboardListener.anIntArray1978[Class3_Sub28_Sub9.anInt3620]; - Class3_Sub28_Sub9.anInt3620 = 1 + Class3_Sub28_Sub9.anInt3620 & 127; - if (var0 < 58) { - method591(68); - } - - return true; - } - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "c.F(" + var0 + ')'); - } - } - - public static void method592(byte var0) { - try { - aClass25Array4060 = null; - TextCore.aClass94_4057 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "c.P(" + var0 + ')'); - } - } - - public static void method593(Frame var0, Signlink var2) { - try { - - while (true) { - Class64 var3 = var2.method1436(var0, 86); - - while (var3.anInt978 == 0) { - TimeUtils.sleep(10L); - } - - if (1 == var3.anInt978) { - var0.setVisible(false); - var0.dispose(); - return; - } - - TimeUtils.sleep(100L); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "c.R(" + (var0 != null ? "{...}" : "null") + ',' + true + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - public static Class106[] method596(Signlink var1) { - try { - if (var1.method1432(false)) { - Class64 var2 = var1.method1453((byte) 8); - - while (0 == var2.anInt978) { - TimeUtils.sleep(10L); - } - - if (2 == var2.anInt978) { - return new Class106[0]; - } else { - int[] var3 = (int[]) var2.anObject974; - Class106[] var4 = new Class106[var3.length >> 2]; - - for (int var5 = 0; var5 < var4.length; ++var5) { - Class106 var6 = new Class106(); - var4[var5] = var6; - var6.anInt1447 = var3[var5 << 2]; - var6.anInt1449 = var3[(var5 << 2) + 1]; - var6.anInt1450 = var3[(var5 << 2) - -2]; - } - - return var4; - } - } else { - return new Class106[0]; - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "pm.P(" + 10 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public static void method598(boolean var0, int var1, boolean var2, int var3, boolean var4, int var5, int var6) { - try { - if (var2) { - HDToolKit.method1842(); - } - - if (null != TextureOperation30.fullScreenFrame && (3 != var1 || TextureOperation.anInt2378 != var5 || anInt3071 != var6)) { - method593(TextureOperation30.fullScreenFrame, Class38.gameSignlink); - TextureOperation30.fullScreenFrame = null; - } - - if (3 == var1 && null == TextureOperation30.fullScreenFrame) { - TextureOperation30.fullScreenFrame = Class99.method1597(0, var6, var5, Class38.gameSignlink); - if (null != TextureOperation30.fullScreenFrame) { - anInt3071 = var6; - TextureOperation.anInt2378 = var5; - Class119.method1730(Class38.gameSignlink); - } - } - - if (var1 == 3 && TextureOperation30.fullScreenFrame == null) { - method598(true, anInt2577, true, var3, var4, -1, -1); - } else { - Container var7; - if (null == TextureOperation30.fullScreenFrame) { - if (null == GameShell.frame) { - var7 = Class38.gameSignlink.gameApplet; - } else { - var7 = GameShell.frame; - } - } else { - var7 = TextureOperation30.fullScreenFrame; - } - - frameWidth = var7.getSize().width; - Class70.frameHeight = var7.getSize().height; - Insets var8; - if (GameShell.frame == var7) { - var8 = GameShell.frame.getInsets(); - frameWidth -= var8.right + var8.left; - Class70.frameHeight -= var8.bottom + var8.top; - } - - if (var1 >= 2) { - Class23.canvasWidth = frameWidth; - Class140_Sub7.canvasHeight = Class70.frameHeight; - Class84.leftMargin = 0; - Class106.rightMargin = 0; - } else { - Class106.rightMargin = 0; - Class84.leftMargin = (frameWidth + -765) / 2; - Class23.canvasWidth = 765; - Class140_Sub7.canvasHeight = 503; - } - - if (var0) { - Class163_Sub1_Sub1.method2215(GameShell.canvas); - method1783(GameShell.canvas); - if (null != Class38.aClass146_668) { - Class38.aClass146_668.method2082(GameShell.canvas); - } - - Client.clientInstance.addCanvas(); - TextureOperation34.method193((byte) 97, GameShell.canvas); - ItemDefinition.method1119(GameShell.canvas, var4); - if (Class38.aClass146_668 != null) { - Class38.aClass146_668.method2084(GameShell.canvas, -103); - } - } else { - if (HDToolKit.highDetail) { - HDToolKit.method1854(Class23.canvasWidth, Class140_Sub7.canvasHeight); - } - - GameShell.canvas.setSize(Class23.canvasWidth, Class140_Sub7.canvasHeight); - if (GameShell.frame == var7) { - var8 = GameShell.frame.getInsets(); - GameShell.canvas.setLocation(var8.left - -Class84.leftMargin, var8.top + Class106.rightMargin); - } else { - GameShell.canvas.setLocation(Class84.leftMargin, Class106.rightMargin); - } - } - - if (0 == var1 && var3 > 0) { - System.out.println("Trying to run method1834 for HD"); - HDToolKit.method1834(GameShell.canvas); - } - - if (var2 && var1 > 0) { - GameShell.canvas.setIgnoreRepaint(true); - if (!aBoolean11) { - Class32.method995(); - aClass158_3009 = null; - aClass158_3009 = TextureOperation18.method285(Class140_Sub7.canvasHeight, Class23.canvasWidth, GameShell.canvas); - Class74.method1320(); - if (5 == Class143.gameStage) { - StartupLoadingBar.draw(true, FontType.bold); - } else { - LoadingBox.draw(false, TextCore.LoadingPleaseWait2); - } - - try { - Graphics var11 = GameShell.canvas.getGraphics(); - aClass158_3009.method2179(var11); - } catch (Exception var9) { - } - - method1396(-1); - if (var3 == 0) { - aClass158_3009 = TextureOperation18.method285(503, 765, GameShell.canvas); - } else { - aClass158_3009 = null; - } - - Class64 var13 = Class38.gameSignlink.method1444(-43, Client.clientInstance.getClass()); - - while (var13.anInt978 == 0) { - TimeUtils.sleep(100L); - } - - if (1 == var13.anInt978) { - aBoolean11 = true; - } - } - - if (aBoolean11) { - HDToolKit.method1853(GameShell.canvas, 2 * anInt3671); - } - } - - if (!HDToolKit.highDetail && 0 < var1) { - method598(true, 0, true, var3, false, -1, -1); - } else { - if (var1 > 0 && var3 == 0) { - GameShell.aThread409.setPriority(5); - aClass158_3009 = null; - Class140_Sub1_Sub2.method1935(); - ((Class102) Class51.anInterface2_838).method1619(200); - if (Class106.aBoolean1441) { - Class51.method1137(0.7F); - } - - InvalidateData.method165(); - } else if (0 == var1 && var3 > 0) { - GameShell.aThread409.setPriority(1); - aClass158_3009 = TextureOperation18.method285(503, 765, GameShell.canvas); - Class140_Sub1_Sub2.method1938(); - ((Class102) Class51.anInterface2_838).method1619(20); - if (Class106.aBoolean1441) { - if (1 == anInt3625) { - Class51.method1137(0.9F); - } - - if (anInt3625 == 2) { - Class51.method1137(0.8F); - } - - if (3 == anInt3625) { - Class51.method1137(0.7F); - } - - if (anInt3625 == 4) { - Class51.method1137(0.6F); - } - } - - Class3_Sub11.method144(); - InvalidateData.method165(); - } - - aBoolean742 = !NPC.method1986(89); - if (var2) { - Class3_Sub20.method389(); - } - - Class3_Sub15.aBoolean2427 = var1 >= 2; - - if (-1 != ConfigInventoryDefinition.anInt3655) { - Class124.method1746(true, (byte) -107); - } - - if (null != Class3_Sub15.activeConnection && (Class143.gameStage == 30 || Class143.gameStage == 25)) { - TextureOperation9.method204(); - } - - for (int var12 = 0; var12 < 100; ++var12) { - aBooleanArray3674[var12] = true; - } - - TextureOperation30.fullRedraw = true; - } - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "pm.F(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - static void method1047(int var0, int var1, int var2, boolean var3, int var4, boolean var5) { - try { - if (var2 > var4) { - int var7 = (var2 + var4) / 2; - int var8 = var4; - WorldListEntry var9 = aClass44_Sub1Array3201[var7]; - aClass44_Sub1Array3201[var7] = aClass44_Sub1Array3201[var2]; - aClass44_Sub1Array3201[var2] = var9; - - for (int var10 = var4; var10 < var2; ++var10) { - if (method1535(var9, aClass44_Sub1Array3201[var10], 5730, var0, var1, var3, var5) <= 0) { - WorldListEntry var11 = aClass44_Sub1Array3201[var10]; - aClass44_Sub1Array3201[var10] = aClass44_Sub1Array3201[var8]; - aClass44_Sub1Array3201[var8++] = var11; - } - } - - aClass44_Sub1Array3201[var2] = aClass44_Sub1Array3201[var8]; - aClass44_Sub1Array3201[var8] = var9; - method1047(var0, var1, -1 + var8, var3, var4, var5); - method1047(var0, var1, var2, var3, var8 - -1, var5); - } - - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "ge.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + false + ')'); - } - } - - static void method1048(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7) { - try { - if (var5 > -15) { - anInt688 = -64; - } - - if (var1 >= 1 && var4 >= 1 && 102 >= var1 && var4 <= 102) { - int var8; - if (!NPC.method1986(41) && 0 == (2 & aByteArrayArrayArray113[0][var1][var4])) { - var8 = var2; - if ((8 & aByteArrayArrayArray113[var2][var1][var4]) != 0) { - var8 = 0; - } - - if (var8 != Class140_Sub3.anInt2745) { - return; - } - } - - var8 = var2; - if (var2 < 3 && (2 & aByteArrayArrayArray113[1][var1][var4]) == 2) { - var8 = var2 + 1; - } - - Class20.method910(var4, var1, var2, var7, var8, AtmosphereParser.aClass91Array1182[var2]); - if (0 <= var0) { - boolean var9 = KeyboardListener.aBoolean1905; - KeyboardListener.aBoolean1905 = true; - Class110.method1683(var8, false, var2, false, AtmosphereParser.aClass91Array1182[var2], var0, var6, var1, var4, var3); - KeyboardListener.aBoolean1905 = var9; - } - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "ge.H(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - static RSString method1052(long var1) { - try { - if (var1 > 0 && var1 < 6582952005840035281L) { - if (var1 % 37L == 0) { - return null; - } else { - int var3 = 0; - - for (long var4 = var1; var4 != 0L; ++var3) { - var4 /= 37L; - } - - byte[] var6 = new byte[var3]; - - while (0L != var1) { - long var7 = var1; - var1 /= 37L; - --var3; - var6[var3] = Class163_Sub1_Sub1.aByteArray4005[(int) (-(var1 * 37L) + var7)]; - } - - RSString var10 = new RSString(); - var10.buffer = var6; - var10.length = var6.length; - return var10; - } - } else { - return null; - } - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "ge.B(" + -29664 + ',' + var1 + ')'); - } - } - - static void method1053(CacheIndex var1) { - try { - Class97.aClass153_1372 = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ge.F(" + (byte) -117 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public static Font method880(byte[] var1) { - try { - if (null == var1) { - return null; - } else { - Object var2; - if (HDToolKit.highDetail) { - var2 = new Class3_Sub28_Sub17_Sub2(var1, Class164.anIntArray2048, anIntArray2591, Class140_Sub7.anIntArray2931, anIntArray3076, Class163_Sub1.aByteArrayArray2987); - } else { - var2 = new Class3_Sub28_Sub17_Sub1(var1, Class164.anIntArray2048, anIntArray2591, Class140_Sub7.anIntArray2931, anIntArray3076, Class163_Sub1.aByteArrayArray2987); - } - - Class39.method1035((byte) 106); - return (Font) var2; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ce.G(" + -22376 + ',' + "{...}" + ')'); - } - } - - public static void method881(int var0, int var1, int var2, int var3, int var4, int var5, int var6, int var7, int var8, int var9) { - try { - if (var2 <= -65) { - Class3_Sub4 var10 = null; - - for (Class3_Sub4 var11 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1222(); var11 != null; var11 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1221()) { - if (var11.anInt2250 == var0 && var11.anInt2264 == var4 && var11.anInt2248 == var1 && var11.anInt2263 == var7) { - var10 = var11; - break; - } - } - - if (null == var10) { - var10 = new Class3_Sub4(); - var10.anInt2264 = var4; - var10.anInt2248 = var1; - var10.anInt2250 = var0; - var10.anInt2263 = var7; - Class132.method1798(72, var10); - TextureOperation26.aLinkedList_3075.method1215(var10); - } - - var10.anInt2262 = var8; - var10.anInt2261 = var9; - var10.anInt2259 = var5; - var10.anInt2265 = var6; - var10.anInt2256 = var3; - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "ce.J(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ')'); - } - } - - public static void method882() { - try { - TextureOperation36.anInt3423 = 0; - Class132.anInt1741 = -1; - anInt154 = 1; - GraphicDefinition.anInt546 = 2; - aBoolean2311 = false; - Class101.aClass153_1423 = null; - TextureOperation8.anInt3463 = -1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ce.F(" + -1 + ',' + 2 + ')'); - } - } - - public static Class3_Sub28_Sub3 method884(int var0, byte var1, int var2) { - try { - Class3_Sub28_Sub3 var3 = (Class3_Sub28_Sub3) Class134.aLinkedList_1758.method1222(); - - for (; var3 != null; var3 = (Class3_Sub28_Sub3) Class134.aLinkedList_1758.method1221()) { - if (var3.aBoolean3553 && var3.method537(var2, var0)) { - return var3; - } - } - - return null; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ce.D(" + var0 + ',' + var1 + ',' + var2 + ')'); - } - } - - static void method518(Player var0) { - try { - Class3_Sub9 var2 = (Class3_Sub9) aHashTable_4046.get(var0.displayName.toLong()); - - if (var2 == null) { - Class70.method1286(var0.anIntArray2755[0], null, 0, null, var0.anIntArray2767[0], WorldListCountry.localPlane, var0); - } else { - var2.method134(); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "rg.UA(" + (var0 != null ? "{...}" : "null") + ',' + -110 + ')'); - } - } - - static int method519(int var0, int var2, int var3) { - try { - var0 &= 3; - - return 0 != var0 ? (var0 != 1 ? (var0 == 2 ? -var3 + 7 : -var2 + 7) : var2) : var3; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "rg.RA(" + var0 + ',' + true + ',' + var2 + ',' + var3 + ')'); - } - } - - static Class3_Sub28_Sub3 method520(byte var0) { - try { - return aClass3_Sub28_Sub3_2600; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "rg.OA(" + var0 + ')'); - } - } - - static void method523(int var0, int var1, int var3, int var4, int var5, int var6, int var7, int var8) { - try { - int var9 = var3 - var8; - int var11 = (-var5 + var0 << 16) / var9; - int var10 = -var4 + var6; - int var12 = (var7 + -var1 << 16) / var10; - Class83.method1410(var1, var6, var4, var3, var5, var8, var12, var11); - } catch (RuntimeException var13) { - throw ClientErrorException.clientError(var13, "rg.SA(" + var0 + ',' + var1 + ',' + 0 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ')'); - } - } - - public static void method744() { - try { - - ++KeyboardListener.anInt1908; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "wa.HA(" + true + ')'); - } - } - - public static void method746(byte var0) { - try { - Class67.aReferenceCache_1013.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "wa.VC(" + var0 + ')'); - } - } - - public static void method777(Class91[] var0, boolean var1, int var2, int var3, int var5, int var6, byte[] var7) { - try { - int var10; - int var11; - if (!var1) { - for (int var9 = 0; 4 > var9; ++var9) { - for (var10 = 0; var10 < 64; ++var10) { - for (var11 = 0; var11 < 64; ++var11) { - if (var5 - -var10 > 0 && var10 + var5 < 103 && var3 + var11 > 0 && var11 + var3 < 103) { - var0[var9].anIntArrayArray1304[var10 + var5][var3 - -var11] = bitwiseAnd(var0[var9].anIntArrayArray1304[var10 + var5][var3 - -var11], -16777217); - } - } - } - } - } - - DataBuffer var20 = new DataBuffer(var7); - byte var8; - if (var1) { - var8 = 1; - } else { - var8 = 4; - } - - int var12; - for (var10 = 0; var8 > var10; ++var10) { - for (var11 = 0; var11 < 64; ++var11) { - for (var12 = 0; 64 > var12; ++var12) { - Class167.method2267(var2, var6, var1, var20, var12 - -var3, var5 + var11, (byte) 91, 0, var10); - } - } - } - - int var14; - int var15; - int var17; - boolean var21; - int var24; - for (var21 = false; var20.index < var20.buffer.length; var21 = true) { - var11 = var20.readUnsignedByte(); - if (var11 != 129) { - --var20.index; - break; - } - - for (var12 = 0; var12 < 4; ++var12) { - byte var13 = var20.readSignedByte(); - if (0 == var13) { - var14 = var5; - if (var5 >= 0) { - if (var5 >= 104) { - var14 = 104; - } - } else { - var14 = 0; - } - - var24 = var3; - if (var3 < 0) { - var24 = 0; - } else if (var3 >= 104) { - var24 = 104; - } - - var15 = 64 + var5; - var17 = var3 + 64; - if (var17 >= 0) { - if (var17 >= 104) { - var17 = 104; - } - } else { - var17 = 0; - } - - if (var15 < 0) { - var15 = 0; - } else if (var15 >= 104) { - var15 = 104; - } - - while (var15 > var14) { - while (var24 < var17) { - aByteArrayArrayArray1774[var12][var14][var24] = 0; - ++var24; - } - - ++var14; - } - } else if (1 == var13) { - for (var14 = 0; var14 < 64; var14 += 4) { - for (var15 = 0; var15 < 64; var15 += 4) { - byte var16 = var20.readSignedByte(); - - for (var17 = var14 + var5; 4 + var5 + var14 > var17; ++var17) { - for (int var18 = var3 + var15; 4 + var3 + var15 > var18; ++var18) { - if (var17 >= 0 && var17 < 104 && 0 <= var18 && var18 < 104) { - aByteArrayArrayArray1774[var12][var17][var18] = var16; - } - } - } - } - } - } else if (var13 == 2 && var12 > 0) { - var15 = var5 + 64; - var24 = var3; - var17 = var3 + 64; - if (var15 < 0) { - var15 = 0; - } else if (104 <= var15) { - var15 = 104; - } - - if (var3 >= 0) { - if (var3 >= 104) { - var24 = 104; - } - } else { - var24 = 0; - } - - if (var17 >= 0) { - if (var17 >= 104) { - var17 = 104; - } - } else { - var17 = 0; - } - - var14 = var5; - if (var5 >= 0) { - if (104 <= var5) { - var14 = 104; - } - } else { - var14 = 0; - } - - while (var15 > var14) { - while (var17 > var24) { - aByteArrayArrayArray1774[var12][var14][var24] = aByteArrayArrayArray1774[var12 + -1][var14][var24]; - ++var24; - } - - ++var14; - } - } - } - } - - int var23; - if (HDToolKit.highDetail && !var1) { - AtmosphereParser var22 = null; - - while (var20.buffer.length > var20.index) { - var12 = var20.readUnsignedByte(); - if (var12 == 0) { - var22 = new AtmosphereParser(var20); - } else { - if (var12 != 1) { -// throw - new IllegalStateException().printStackTrace(); - return;// - } - - var23 = var20.readUnsignedByte(); - if (0 < var23) { - for (var14 = 0; var23 > var14; ++var14) { - Class43 var25 = new Class43(var20); - if (var25.anInt705 == 31) { - Class57 var26 = Class81.method1401(var20.readUnsignedShort()); - var25.method1060((byte) -67, var26.anInt896, var26.anInt908, var26.anInt899, var26.anInt907); - } - - var25.anInt708 += var3 << 7; - var25.anInt703 += var5 << 7; - var17 = var25.anInt708 >> 7; - var24 = var25.anInt703 >> 7; - if (var24 >= 0 && 0 <= var17 && var24 < 104 && var17 < 104) { - var25.aBoolean696 = 0 != (aByteArrayArrayArray113[1][var24][var17] & 2); - var25.anInt697 = Class44.anIntArrayArrayArray723[var25.anInt704][var24][var17] + -var25.anInt697; - Class68.method1264(var25); - } - } - } - } - } - - if (var22 == null) { - var22 = new AtmosphereParser(); - } - - for (var12 = 0; var12 < 8; ++var12) { - for (var23 = 0; var23 < 8; ++var23) { - var14 = var12 + (var5 >> 3); - var15 = (var3 >> 3) + var23; - if (0 <= var14 && var14 < 13 && var15 >= 0 && var15 < 13) { - AtmosphereParser.aAtmosphereParserArrayArray1581[var14][var15] = var22; - } - } - } - } - - if (!var21) { - for (var11 = 0; var11 < 4; ++var11) { - for (var12 = 0; 16 > var12; ++var12) { - for (var23 = 0; var23 < 16; ++var23) { - var14 = (var5 >> 2) - -var12; - var15 = var23 + (var3 >> 2); - if (0 <= var14 && 26 > var14 && 0 <= var15 && var15 < 26) { - aByteArrayArrayArray1774[var11][var14][var15] = 0; - } - } - } - } - } - - } catch (RuntimeException var19) { - throw ClientErrorException.clientError(var19, "wa.OA(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ',' + var3 + ',' + 4 + ',' + var5 + ',' + var6 + ',' + (var7 != null ? "{...}" : "null") + ')'); - } - } - - public static Class12 method784(int var0, int var1, int var2) { - Class3_Sub2 var3 = Class75_Sub2.aClass3_Sub2ArrayArrayArray2638[var0][var1][var2]; - return var3 != null && var3.aClass12_2230 != null ? var3.aClass12_2230 : null; - } - - public static void method792() { - try { - int var1 = Class137.method1817(); - if (0 == var1) { - Class158.aByteArrayArrayArray2008 = null; - Class136.method1816(0, -7); - } else if (var1 == 1) { - Class3_Sub5.method112((byte) 0); - Class136.method1816(512, -7); - TextureOperation19.method257(); - } else { - Class3_Sub5.method112((byte) (-4 + CSConfigCachefile.anInt1127 & 0xFF)); - Class136.method1816(2, -7); - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "wa.AA(" + 9179409 + ')'); - } - } - - public static Font method1300(int var1, CacheIndex var3, CacheIndex var4) { - //System.out.println("Class 73 " + var1); - return !Class75_Sub4.method1351(var3, 0, var1) ? null : method880(var4.getFile(var1, 0)); - } - - public static void method1301(int plane, int regionY, int regionX, int sceneY, boolean var4, int sceneX) { - try { - if (anInt3606 != regionX || Class3_Sub7.anInt2294 != regionY || plane != Class140_Sub3.anInt2745 && !NPC.method1986(45)) { - anInt3606 = regionX; - Class3_Sub7.anInt2294 = regionY; - Class140_Sub3.anInt2745 = plane; - if (NPC.method1986(105)) { - Class140_Sub3.anInt2745 = 0; - } - - if (var4) { - Class117.method1719(28); - } else { - Class117.method1719(25); - } - - LoadingBox.draw(true, TextCore.LoadingPleaseWait2); - int var8 = Texture.anInt1152; - int var7 = Class131.anInt1716; - Texture.anInt1152 = regionY * 8 - 48; - Class131.anInt1716 = 8 * (-6 + regionX); - TextureOperation37.aClass3_Sub28_Sub3_3264 = method884(8 * anInt3606, (byte) 88, 8 * Class3_Sub7.anInt2294); - int var10 = -var8 + Texture.anInt1152; - int var9 = Class131.anInt1716 + -var7; - TextureOperation22.aClass131_3421 = null; - int var11; - NPC var12; - int var13; - if (var4) { - Class163.localNPCCount = 0; - - for (var11 = 0; var11 < 32768; ++var11) { - var12 = NPC.npcs[var11]; - if (null != var12) { - var12.xAxis -= 128 * var9; - var12.zAxis -= 128 * var10; - if (var12.xAxis >= 0 && var12.xAxis <= 13184 && var12.zAxis >= 0 && var12.zAxis <= 13184) { - for (var13 = 0; 10 > var13; ++var13) { - var12.anIntArray2767[var13] -= var9; - var12.anIntArray2755[var13] -= var10; - } - - AudioThread.localNPCIndexes[Class163.localNPCCount++] = var11; - } else { - NPC.npcs[var11].setDefinitions(null); - NPC.npcs[var11] = null; - } - } - } - } else { - for (var11 = 0; var11 < 32768; ++var11) { - var12 = NPC.npcs[var11]; - if (null != var12) { - for (var13 = 0; var13 < 10; ++var13) { - var12.anIntArray2767[var13] -= var9; - var12.anIntArray2755[var13] -= var10; - } - - var12.xAxis -= 128 * var9; - var12.zAxis -= var10 * 128; - } - } - } - - for (var11 = 0; var11 < 2048; ++var11) { - Player var23 = players[var11]; - if (null != var23) { - for (var13 = 0; 10 > var13; ++var13) { - var23.anIntArray2767[var13] -= var9; - var23.anIntArray2755[var13] -= var10; - } - - var23.xAxis -= 128 * var9; - var23.zAxis -= 128 * var10; - } - } - - WorldListCountry.localPlane = plane; - Class102.player.method1981(sceneX, false, sceneY); - byte var25 = 104; - byte var24 = 0; - byte var14 = 0; - byte var16 = 1; - byte var15 = 104; - byte var26 = 1; - if (var10 < 0) { - var16 = -1; - var15 = -1; - var14 = 103; - } - - if (var9 < 0) { - var26 = -1; - var24 = 103; - var25 = -1; - } - - for (int var17 = var24; var25 != var17; var17 += var26) { - for (int var18 = var14; var18 != var15; var18 += var16) { - int var19 = var9 + var17; - int var20 = var18 + var10; - - for (int var21 = 0; 4 > var21; ++var21) { - if (var19 >= 0 && var20 >= 0 && var19 < 104 && var20 < 104) { - Class39.aLinkedListArrayArrayArray3273[var21][var17][var18] = Class39.aLinkedListArrayArrayArray3273[var21][var19][var20]; - } else { - Class39.aLinkedListArrayArrayArray3273[var21][var17][var18] = null; - } - } - } - } - - for (Class3_Sub4 var27 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1222(); var27 != null; var27 = (Class3_Sub4) TextureOperation26.aLinkedList_3075.method1221()) { - var27.anInt2248 -= var10; - var27.anInt2264 -= var9; - if (0 > var27.anInt2264 || var27.anInt2248 < 0 || var27.anInt2264 >= 104 || var27.anInt2248 >= 104) { - var27.unlink(); - } - } - - if (var4) { - NPC.anInt3995 -= 128 * var9; - Class77.anInt1111 -= var10 * 128; - Class146.anInt1904 -= var10; - MouseListeningClass.anInt1923 -= var9; - Class157.anInt1996 -= var10; - anInt30 -= var9; - } else { - Class133.anInt1753 = 1; - } - - AudioHandler.currentSoundEffectCount = 0; - if (Class65.anInt987 != 0) { - Class45.anInt733 -= var10; - Class65.anInt987 -= var9; - } - - if (HDToolKit.highDetail && var4 && (Math.abs(var9) > 104 || 104 < Math.abs(var10))) { - TextureOperation31.method236(); - } - - Class58.anInt909 = -1; - TextureOperation17.aLinkedList_3177.method1211(-122); - TextureOperation13.aLinkedList_3364.method1211(-87); - } - } catch (RuntimeException var22) { - throw ClientErrorException.clientError(var22, "k.D(" + plane + ',' + regionY + ',' + regionX + ',' + sceneY + ',' + var4 + ',' + sceneX + ',' + true + ')'); - } - } - - public static InterfaceWidget method1302() { - try { - InterfaceWidget var1 = (InterfaceWidget) Class126.aClass13_1666.getFront(); - if (var1 == null) { - do { - var1 = (InterfaceWidget) Class81.aClass13_1139.getFront(); - if (var1 == null) { - return null; - } - - if (var1.b() > TimeUtils.time()) { - return null; - } - - var1.unlink(); - var1.unlinkNode(); - } while ((Long.MIN_VALUE & var1.nodeKey) == 0L); - - return var1; - } else { - var1.unlink(); - var1.unlinkNode(); - return var1; - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "k.J(" + (byte) -36 + ')'); - } - } - - public static RSString method1303(RSInterface var0, RSString var1) { - try { - if (var1.indexOf(RSString.parse("(U"), 102) == -1) { - return var1; - } else { - while (true) { - int var3 = var1.indexOf(TextCore.aClass94_331, 55); - if (var3 == -1) { - while (true) { - var3 = var1.indexOf(TextCore.aClass94_2080, 106); - if (var3 == -1) { - while (true) { - var3 = var1.indexOf(TextCore.aClass94_1301, 95); - if (var3 == -1) { - while (true) { - var3 = var1.indexOf(TextCore.aClass94_852, 57); - if (var3 == -1) { - while (true) { - var3 = var1.indexOf(TextCore.aClass94_3418, 113); - if (var3 == -1) { - while (true) { - var3 = var1.indexOf(TextCore.aClass94_1051, 50); - if (var3 == -1) { - return var1; - } - - RSString var4 = RSString.parse(""); - if (null != Class136.aClass64_1778) { - var4 = Class108.method1653(Class136.aClass64_1778.anInt979); - - if (null != Class136.aClass64_1778.anObject974) { - byte[] var5 = ((String) Class136.aClass64_1778.anObject974).getBytes(StandardCharsets.ISO_8859_1); - var4 = TextureOperation33.bufferToString(var5, var5.length, 0); - } - } - - var1 = RSString.stringCombiner(new RSString[]{var1.substring(0, var3, 0), var4, var1.substring(4 + var3)}); - } - } - - var1 = RSString.stringCombiner(new RSString[]{var1.substring(0, var3, 0), Class154.method2148(Class164_Sub2.method2247((byte) -4, 4, var0)), var1.substring(var3 - -2)}); - } - } - - var1 = RSString.stringCombiner(new RSString[]{var1.substring(0, var3, 0), Class154.method2148(Class164_Sub2.method2247((byte) -109, 3, var0)), var1.substring(2 + var3)}); - } - } - - var1 = RSString.stringCombiner(new RSString[]{var1.substring(0, var3, 0), Class154.method2148(Class164_Sub2.method2247((byte) 111, 2, var0)), var1.substring(2 + var3)}); - } - } - - var1 = RSString.stringCombiner(new RSString[]{var1.substring(0, var3, 0), Class154.method2148(Class164_Sub2.method2247((byte) 23, 1, var0)), var1.substring(var3 + 2)}); - } - } - - var1 = RSString.stringCombiner(new RSString[]{var1.substring(0, var3, 0), Class154.method2148(Class164_Sub2.method2247((byte) 107, 0, var0)), var1.substring(2 + var3)}); - } - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "k.K(" + (var0 != null ? "{...}" : "null") + ',' + (var1 != null ? "{...}" : "null") + ',' + 0 + ')'); - } - } - - public static void method2143(byte var0, int var1, int var2, int var3, int var4) { - try { - InterfaceWidget var5 = InterfaceWidget.getWidget(8, var2); - var5.flagUpdate(); - var5.anInt3596 = var1; - if (var0 >= -120) { - anInt1950 = -14; - } - - var5.anInt3598 = var4; - var5.anInt3597 = var3; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "ve.U(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - public static void method2065(CacheIndex var1, CacheIndex var2) { - try { - TextureOperation4.aClass153_3227 = var1; - SequenceDefinition.aClass153_1852 = var2; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "u.D(" + (byte) -125 + ',' + (var1 != null ? "{...}" : "null") + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - public static void method2067() { - try { - for (Class3_Sub28_Sub2 var1 = (Class3_Sub28_Sub2) TextureOperation17.aLinkedList_3177.method1222(); null != var1; var1 = (Class3_Sub28_Sub2) TextureOperation17.aLinkedList_3177.method1221()) { - Class140_Sub2 var2 = var1.aClass140_Sub2_3545; - if (WorldListCountry.localPlane == var2.anInt2717 && !var2.aBoolean2718) { - if (Class44.anInt719 >= var2.anInt2703) { - var2.method1955(Class106.anInt1446); - if (var2.aBoolean2718) { - var1.unlink(); - } else { - Class20.method907(var2.anInt2717, var2.anInt2716, var2.anInt2710, var2.anInt2712, 60, var2, 0, -1L, false); - } - } - } else { - var1.unlink(); - } - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "u.G(" + false + ')'); - } - } - - // static final void method2068(NPCDefinition var0, int var1, int var2, int var3, int var4) { - // @Splinter - public static void drawNpcRightClickOptions(NPCDefinition var0, int var1, int var2, int var3, int var4) { - try { - if (menuOptionCount < 400) { - if (var0.childNPCs != null) { - var0 = var0.method1471((byte) 66); - } - - if (null != var0) { - if (var0.aBoolean1270) { - RSString var5 = var0.NPCName; - if (0 != var0.anInt1260) { - RSString var6 = Class158.paramGameTypeID != 1 ? TextCore.HasLevel : TextCore.HasRating; - var5 = RSString.stringCombiner(new RSString[]{var5, Player.combatLevelColor(var0.anInt1260, (byte) -122, Class102.player.COMBAT_LEVEL), TextCore.LEFT_PARENTHESES, var6, RSString.stringAnimator(var0.anInt1260), TextCore.RIGHT_PARENTHESES}); - } - - if (Class164_Sub1.anInt3012 == 1) { - Class3_Sub24_Sub4.method1177(Class99.anInt1403, var3, (byte) -125, RSString.stringCombiner(new RSString[]{RenderAnimationDefinition.aClass94_378, ColorCore.TextColor, var5}), var1, (short) 26, TextCore.HasUse, var4); - } else if (GameObject.aBoolean1837) { - Class3_Sub28_Sub9 var12 = -1 == anInt1038 ? null : LinkedList.method1210(anInt1038); - if ((2 & Class164.anInt2051) != 0 && (var12 == null || var0.method1475(anInt1038, var12.anInt3614) != var12.anInt3614)) { - Class3_Sub24_Sub4.method1177(anInt1887, var3, (byte) -93, RSString.stringCombiner(new RSString[]{TextCore.aClass94_676, ColorCore.TextColor, var5}), var1, (short) 45, Class3_Sub28_Sub9.aClass94_3621, var4); - } - } else { - RSString[] var11 = var0.options; - if (Class123.aBoolean1656) { - var11 = Class3_Sub31.optionsArrayStringConstructor(var11); - } - - int var7; - if (var11 != null) { - for (var7 = 4; var7 >= 0; --var7) { - if (var11[var7] != null && (Class158.paramGameTypeID != 0 || !var11[var7].equalsStringIgnoreCase(TextCore.HasAttack))) { - byte var8 = 0; - if (var7 == 0) { - var8 = 17; - } - - if (var7 == 1) { - var8 = 16; - } - - int var9 = -1; - if (var7 == 2) { - var8 = 4; - } - - if (var7 == 3) { - var8 = 19; - } - - if (var0.anInt1296 == var7) { - var9 = var0.anInt1253; - } - - if (var0.anInt1289 == var7) { - var9 = var0.anInt1278; - } - - if (var7 == 4) { - var8 = 2; - } - - Class3_Sub24_Sub4.method1177(var9, var3, (byte) -103, RSString.stringCombiner(new RSString[]{ColorCore.NPCRightClickColor, var5}), var1, var8, var11[var7], var4); - } - } - } - - if (0 == Class158.paramGameTypeID && var11 != null) { - for (var7 = 4; var7 >= 0; --var7) { - if (null != var11[var7] && var11[var7].equalsStringIgnoreCase(TextCore.HasAttack)) { - short var14 = 0; - if (var0.anInt1260 > Class102.player.COMBAT_LEVEL) { - var14 = 2000; - //This var sets "attack" as a right click attack option for higher level npcs, let's make it a single click! - } - - short var13 = 0; - if (var7 == 0) { - var13 = 17; - } - - if (var7 == 1) { - var13 = 16; - } - - if (2 == var7) { - var13 = 4; - } - - if (3 == var7) { - var13 = 19; - } - - if (var7 == 4) { - var13 = 2; - } - - if (0 != var13) { - var13 += var14; - } - - Class3_Sub24_Sub4.method1177(var0.anInt1298, var3, (byte) -128, RSString.stringCombiner(new RSString[]{ColorCore.NPCRightClickColor, var5}), var1, var13, var11[var7], var4); - } - } - } - - if (GameConfig.NPC_DEBUG_ENABLED) { - Class3_Sub24_Sub4.method1177(Class131.anInt1719, var3, (byte) -73, RSString.stringCombiner(new RSString[]{ColorCore.NPCRightClickColor, var5}), var1, (short) 1007, RSString.parse("Examine" + "
" + " ID: (X" + var0.npcId + "(Y"), var4); - } else { - Class3_Sub24_Sub4.method1177(Class131.anInt1719, var3, (byte) -73, RSString.stringCombiner(new RSString[]{ColorCore.NPCRightClickColor, var5}), var1, (short) 1007, TextCore.HasExamine, var4); - } - } - - } - } - } - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "u.A(" + (var0 != null ? "{...}" : "null") + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } - - static void method56(int var0) { - InterfaceWidget var2 = InterfaceWidget.getWidget(6, var0); - var2.a(); - } - - static boolean loadInterface(int archive) { - if (!aBooleanArray1703[archive]) { - if (aClass153_3361.method2117(archive)) { - int fileLength = aClass153_3361.getFileAmount(archive); - if (0 == fileLength) { - aBooleanArray1703[archive] = true; - } else { - if (null == GameObject.aClass11ArrayArray1834[archive]) { - GameObject.aClass11ArrayArray1834[archive] = new RSInterface[fileLength]; - } - - for (int fileId = 0; fileId < fileLength; ++fileId) { - if (null == GameObject.aClass11ArrayArray1834[archive][fileId]) { - byte[] var4 = aClass153_3361.getFile(archive, fileId); - if (var4 != null) { - RSInterface iface = GameObject.aClass11ArrayArray1834[archive][fileId] = new RSInterface(); - iface.componentHash = fileId + (archive << 16); - if (var4[0] == -1) { - iface.decodeScriptFormat(new DataBuffer(var4)); - } else { - iface.decodeNoScripts(new DataBuffer(var4)); - } - } - } - } - - aBooleanArray1703[archive] = true; - - } - return true; - } else { - return false; - } - } else { - return true; - } - } - - static void method60(int var0, int var1, int var2, Class91[] var3, int var4, byte var5, byte[] var6, int var7, int var8, int var9, boolean var10) { - int var13; - if (!var10) { - for (int var12 = 0; var12 < 8; ++var12) { - for (var13 = 0; 8 > var13; ++var13) { - if (0 < var1 - -var12 && var12 + var1 < 103 && var13 + var4 > 0 && var4 + var13 < 103) { - var3[var2].anIntArrayArray1304[var12 + var1][var13 + var4] = bitwiseAnd(var3[var2].anIntArrayArray1304[var12 + var1][var13 + var4], -16777217); - } - } - } - } - byte var11; - if (var10) { - var11 = 1; - } else { - var11 = 4; - } - - DataBuffer var25 = new DataBuffer(var6); - - int var14; - int var15; - for (var13 = 0; var13 < var11; ++var13) { - for (var14 = 0; var14 < 64; ++var14) { - for (var15 = 0; var15 < 64; ++var15) { - if (var13 == var7 && var9 <= var14 && 8 + var9 > var14 && var8 <= var15 && var15 < 8 + var8) { - Class167.method2267(0, 0, var10, var25, TextureOperation3.method310(var0, (byte) -117, 7 & var14, 7 & var15) + var4, method519(var0, var15 & 7, var14 & 7) + var1, (byte) 63, var0, var2); - } else { - Class167.method2267(0, 0, var10, var25, -1, -1, (byte) 123, 0, 0); - } - } - } - } - - int var17; - int var21; - int var20; - int var22; - int var29; - while (var25.index < var25.buffer.length) { - var14 = var25.readUnsignedByte(); - if (var14 != 129) { - --var25.index; - break; - } - - for (var15 = 0; var15 < 4; ++var15) { - byte var16 = var25.readSignedByte(); - int var18; - if (var16 != 0) { - if (var16 == 1) { - for (var17 = 0; 64 > var17; var17 += 4) { - for (var18 = 0; var18 < 64; var18 += 4) { - byte var19 = var25.readSignedByte(); - if (var7 >= var15) { - for (var20 = var17; var17 + 4 > var20; ++var20) { - for (var21 = var18; var21 < 4 + var18; ++var21) { - if (var9 <= var20 && 8 + var9 > var20 && var8 <= var21) { - var22 = var1 - -method519(var0, var21 & 7, var20 & 7); - int var23 = TextureOperation3.method310(var0, (byte) -97, 7 & var20, var21 & 7) + var4; - if (0 <= var22 && 104 > var22 && var23 >= 0 && var23 < 104) { - aByteArrayArrayArray1774[var2][var22][var23] = var19; - } - } - } - } - } - } - } - } - } else if (var15 <= var7) { - var18 = 7 + var1; - var17 = var1; - var20 = var4 - -7; - if (var20 < 0) { - var20 = 0; - } else if (104 <= var20) { - var20 = 104; - } - - if (0 <= var18) { - if (var18 >= 104) { - var18 = 104; - } - } else { - var18 = 0; - } - - var29 = var4; - if (var4 < 0) { - var29 = 0; - } else if (104 <= var4) { - var29 = 104; - } - - if (var1 < 0) { - var17 = 0; - } else if (104 <= var1) { - var17 = 104; - } - - while (var18 > var17) { - while (var29 < var20) { - aByteArrayArrayArray1774[var2][var17][var29] = 0; - ++var29; - } - - ++var17; - } - } - } - } - - int var28; - if (HDToolKit.highDetail && !var10) { - AtmosphereParser var26 = null; - - while (var25.buffer.length > var25.index) { - var15 = var25.readUnsignedByte(); - if (var15 == 0) { - var26 = new AtmosphereParser(var25); - } else { - if (var15 != 1) { - throw new IllegalStateException(); - } - - var28 = var25.readUnsignedByte(); - if (var28 > 0) { - for (var17 = 0; var28 > var17; ++var17) { - Class43 var30 = new Class43(var25); - if (var30.anInt705 == 31) { - Class57 var31 = Class81.method1401(var25.readUnsignedShort()); - var30.method1060((byte) 123, var31.anInt896, var31.anInt908, var31.anInt899, var31.anInt907); - } - - var29 = var30.anInt703 >> 7; - var20 = var30.anInt708 >> 7; - if (var30.anInt704 == var7 && var9 <= var29 && var9 - -8 > var29 && var20 >= var8 && var20 < var8 - -8) { - var21 = Class3_Sub26.method514(var0, var30.anInt703 & 1023, 1023 & var30.anInt708) + (var1 << 7); - var22 = TextureOperation28.method293(var30.anInt703 & 1023, var0, 1023 & var30.anInt708) + (var4 << 7); - var30.anInt703 = var21; - var30.anInt708 = var22; - var29 = var30.anInt703 >> 7; - var20 = var30.anInt708 >> 7; - if (var29 >= 0 && var20 >= 0 && 104 > var29 && var20 < 104) { - var30.aBoolean696 = (2 & aByteArrayArrayArray113[1][var29][var20]) != 0; - var30.anInt697 = Class44.anIntArrayArrayArray723[var30.anInt704][var29][var20] - var30.anInt697; - Class68.method1264(var30); - } - } - } - } - } - } - - if (null == var26) { - var26 = new AtmosphereParser(); - } - - AtmosphereParser.aAtmosphereParserArrayArray1581[var1 >> 3][var4 >> 3] = var26; - } - - var14 = 7 + var1; - var15 = var4 - -7; - - for (var28 = var1; var14 > var28; ++var28) { - for (var17 = var4; var15 > var17; ++var17) { - aByteArrayArrayArray1774[var2][var28][var17] = 0; - } - } - } - - public static int method54(int var0) { - try { - return var0 & 127; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "oe.A(" + var0 + ',' + false + ')'); - } - } - - public static LDIndexedSprite[] method619(byte var0, int var1, CacheIndex var2) { - try { - return Class140_Sub7.method2029((byte) -119, var2, var1) ? (var0 <= 52 ? null : method1281()) : null; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "ml.A(" + var0 + ',' + var1 + ',' + (var2 != null ? "{...}" : "null") + ')'); - } - } - - public static void method383(int var0, int var1) { - try { - if (var1 == Class23.anInt453 && var1 != 0) { - ShaderInterface var2 = anShaderInterfaceArray70[var1]; - var2.method23(TextureOperation37.anInt3263); - } - - if (var0 != -32584) { - method383(60, 23); - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ka.A(" + var0 + ',' + var1 + ')'); - } - } - - public static int method599(CacheIndex var1) { - try { - int var2 = 0; - if (var1.retrieveSpriteFile(Class154.anInt1966)) { - ++var2; - } - - if (var1.retrieveSpriteFile(CSConfigCachefile.anInt1124)) { - ++var2; - } - - return var2; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "lk.F(" + -20916 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public static AbstractSprite method602(int var0, CacheIndex var1) { - try { - // System.out.println("Class3_sub28_Sub16 " + var1); - if (Class75_Sub4.method1351(var1, 0, var0)) { - return Class43.method1062((byte) -18 + 103); - } else { - return null; - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "lk.R(" + 0 + ',' + var0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - /** - * @param notifyScripts Whether if a listening CS2 script should be invoked if this interface is resized - */ - public static void calculateInterfaceSize(RSInterface iface, int parentWidth, int parentHeight, boolean notifyScripts) { - int oldWidth = iface.width; - int oldHeight = iface.height; - - if (iface.horizontalResize == 0) { - iface.width = iface.defWidth; - } else if (iface.horizontalResize == 1) { - iface.width = parentWidth + -iface.defWidth; - } else if (iface.horizontalResize == 2) { - iface.width = iface.defWidth * parentWidth >> 14; - } else if (iface.horizontalResize == 3) { - if (iface.type == 2) { - iface.width = iface.defWidth * 32 - -((iface.defWidth - 1) * iface.anInt285); - } else if (iface.type == 7) { - iface.width = 115 * iface.defWidth + iface.anInt285 * (-1 + iface.defWidth); - } - } - - if (iface.verticalResize == 0) { - iface.height = iface.defHeight; - } else if (iface.verticalResize == 1) { - iface.height = -iface.defHeight + parentHeight; - } else if (iface.verticalResize == 2) { - iface.height = parentHeight * iface.defHeight >> 14; - } else if (iface.verticalResize == 3) { - if (iface.type == 2) { - iface.height = (iface.defHeight + -1) * iface.anInt290 + iface.defHeight * 32; - } else if (iface.type == 7) { - iface.height = iface.defHeight * 12 + (-1 + iface.defHeight) * iface.anInt290; - } - } - - if (iface.horizontalResize == 4) { - iface.width = iface.anInt216 * iface.height / iface.anInt160; - } - - if (iface.verticalResize == 4) { - iface.height = iface.anInt160 * iface.width / iface.anInt216; - } - - if (ClientCommands.commandQaOpEnabled && (Client.method44(iface).anInt2205 != 0 || iface.type == 0)) { - if (iface.height < 5 && 5 > iface.width) { - iface.height = 5; - iface.width = 5; - } else { - if (iface.width <= 0) { - iface.width = 5; - } - - if (0 >= iface.height) { - iface.height = 5; - } - } - } - - if (iface.anInt189 == 1337) { - aClass11_2091 = iface; - } - - if (notifyScripts && null != iface.anObjectArray235 && (iface.width != oldWidth || iface.height != oldHeight)) { - CS2Script script = new CS2Script(); - script.arguments = iface.anObjectArray235; - script.aClass11_2449 = iface; - Client.aLinkedList_1471.method1215(script); - } - } - - public static void method606(int var0, Class3_Sub9 var1, int var2, int var3, int var4, int var5) { - try { - if (var5 > 44) { - if (var1.anInt2332 != -1 || var1.anIntArray2333 != null) { - int var6 = 0; - if (var1.anInt2321 < var0) { - var6 += -var1.anInt2321 + var0; - } else if (var1.anInt2326 > var0) { - var6 += var1.anInt2326 - var0; - } - - if (var1.anInt2307 >= var4) { - if (var4 < var1.anInt2308) { - var6 += -var4 + var1.anInt2308; - } - } else { - var6 += -var1.anInt2307 + var4; - } - - if (0 != var1.anInt2328 && var6 - 64 <= var1.anInt2328 && 0 != Sprites.ambientVolume && var2 == var1.anInt2314) { - var6 -= 64; - if (var6 < 0) { - var6 = 0; - } - - int var7 = (-var6 + var1.anInt2328) * Sprites.ambientVolume / var1.anInt2328; - if (var1.aClass3_Sub24_Sub1_2312 == null) { - if (var1.anInt2332 >= 0) { - SynthSound var8 = SynthSound.create(CacheIndex.soundFXIndex, var1.anInt2332, 0); - if (null != var8) { - PcmSound var9 = var8.toPCMSound().method151(Class27.resampler); - Class3_Sub24_Sub1 var10 = Class3_Sub24_Sub1.method437(var9, var7); - Objects.requireNonNull(var10).method429(-1); - Class3_Sub26.aClass3_Sub24_Sub2_2563.method457(var10); - var1.aClass3_Sub24_Sub1_2312 = var10; - } - } - } else { - var1.aClass3_Sub24_Sub1_2312.method419(var7); - } - - if (null != var1.aClass3_Sub24_Sub1_2315) { - var1.aClass3_Sub24_Sub1_2315.method419(var7); - if (!var1.aClass3_Sub24_Sub1_2315.isLinked()) { - var1.aClass3_Sub24_Sub1_2315 = null; - } - } else if (var1.anIntArray2333 != null && ((var1.anInt2316 -= var3) <= 0)) { - int var13 = (int) ((double) var1.anIntArray2333.length * Math.random()); - SynthSound var14 = SynthSound.create(CacheIndex.soundFXIndex, var1.anIntArray2333[var13], 0); - if (null != var14) { - PcmSound var15 = var14.toPCMSound().method151(Class27.resampler); - Class3_Sub24_Sub1 var11 = Class3_Sub24_Sub1.method437(var15, var7); - Objects.requireNonNull(var11).method429(0); - Class3_Sub26.aClass3_Sub24_Sub2_2563.method457(var11); - var1.anInt2316 = (int) ((double) (-var1.anInt2310 + var1.anInt2325) * Math.random()) + var1.anInt2310; - var1.aClass3_Sub24_Sub1_2315 = var11; - } - } - - } else { - if (null != var1.aClass3_Sub24_Sub1_2312) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var1.aClass3_Sub24_Sub1_2312); - var1.aClass3_Sub24_Sub1_2312 = null; - } - - if (var1.aClass3_Sub24_Sub1_2315 != null) { - Class3_Sub26.aClass3_Sub24_Sub2_2563.method461(var1.aClass3_Sub24_Sub1_2315); - var1.aClass3_Sub24_Sub1_2315 = null; - } - - } - } - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "lk.O(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static void method1180(byte var0, Class140_Sub4 var1) { - try { - RenderAnimationDefinition var2 = var1.getRenderAnimationType(); - var1.anInt2764 = var2.stand_animation; - if (var0 >= -5) { - anIntArray882 = null; - } - - if (var1.anInt2816 == 0) { - var1.anInt2824 = 0; - } else { - if (var1.anInt2771 != -1 && 0 == var1.anInt2828) { - SequenceDefinition var3 = SequenceDefinition.getAnimationDefinition(var1.anInt2771); - if (var1.anInt2811 > 0 && var3.resetWhenWalk == 0) { - ++var1.anInt2824; - return; - } - - if (var1.anInt2811 <= 0 && var3.priority == 0) { - ++var1.anInt2824; - return; - } - } - - int var18 = var1.xAxis; - int var4 = var1.zAxis; - int var5 = var1.anIntArray2767[-1 + var1.anInt2816] * 128 - -(var1.getSize() * 64); - int var6 = 128 * var1.anIntArray2755[-1 + var1.anInt2816] - -(var1.getSize() * 64); - if (var5 + -var18 > 256 || -var18 + var5 < -256 || var6 - var4 > 256 || -256 > var6 - var4) { - var1.xAxis = var5; - var1.zAxis = var6; - return; - } - - if (var5 <= var18) { - if (var18 <= var5) { - if (var6 <= var4) { - if (var6 < var4) { - var1.anInt2806 = 0; - } - } else { - var1.anInt2806 = 1024; - } - } else if (var6 > var4) { - var1.anInt2806 = 768; - } else if (var6 < var4) { - var1.anInt2806 = 256; - } else { - var1.anInt2806 = 512; - } - } else if (var6 > var4) { - var1.anInt2806 = 1280; - } else if (var4 > var6) { - var1.anInt2806 = 1792; - } else { - var1.anInt2806 = 1536; - } - - int var7 = 2047 & -var1.anInt2785 + var1.anInt2806; - int var8 = var2.walk_follow_full_turn_anim; - if (1024 < var7) { - var7 -= 2048; - } - - boolean var10 = true; - byte var11 = 1; - if (var7 >= -256 && var7 <= 256) { - var8 = var2.walk_animation; - } else if (var7 >= 256 && 768 > var7) { - var8 = var2.walk_follow_cw_turn_anim; - } else if (var7 >= -768 && -256 >= var7) { - var8 = var2.walk_follow_ccw_turn_anim; - } - - int var9 = 4; - if (var8 == -1) { - var8 = var2.walk_animation; - } - - var1.anInt2764 = var8; - if (var1 instanceof NPC) { - var10 = ((NPC) var1).definition.aBoolean1255; - } - - if (var10) { - if (var1.anInt2806 != var1.anInt2785 && var1.anInt2772 == -1 && var1.anInt2779 != 0) { - var9 = 2; - } - - if (2 < var1.anInt2816) { - var9 = 6; - } - - if (3 < var1.anInt2816) { - var9 = 8; - } - - if (var1.anInt2824 > 0 && var1.anInt2816 > 1) { - var9 = 8; - --var1.anInt2824; - } - } else { - if (1 < var1.anInt2816) { - var9 = 6; - } - - if (var1.anInt2816 > 2) { - var9 = 8; - } - - if (var1.anInt2824 > 0 && var1.anInt2816 > 1) { - --var1.anInt2824; - var9 = 8; - } - } - - if (2 == var1.aByteArray2795[var1.anInt2816 - 1]) { - var9 <<= 1; - var11 = 2; - } else if (var1.aByteArray2795[-1 + var1.anInt2816] == 0) { - var11 = 0; - var9 >>= 1; - } - - if (var9 >= 8 && -1 != var2.run_anim) { - if (var1.anInt2764 == var2.walk_follow_full_turn_anim && -1 != var2.run_follow_full_turn_anim) { - var1.anInt2764 = var2.run_follow_full_turn_anim; - } else if (var1.anInt2764 == var2.walk_follow_ccw_turn_anim && var2.run_follow_ccw_turn_anim != -1) { - var1.anInt2764 = var2.run_follow_ccw_turn_anim; - } else if (var2.walk_follow_cw_turn_anim == var1.anInt2764 && var2.run_follow_cw_turn_anim != -1) { - var1.anInt2764 = var2.run_follow_cw_turn_anim; - } else { - var1.anInt2764 = var2.run_anim; - } - } else if (var2.slow_walk_anim != -1 && var11 == 0) { - if (var1.anInt2764 == var2.walk_follow_full_turn_anim && var2.slow_walk_follow_full_turn_anim != -1) { - var1.anInt2764 = var2.slow_walk_follow_full_turn_anim; - } else if (var1.anInt2764 == var2.walk_follow_ccw_turn_anim && -1 != var2.slow_walk_follow_ccw_turn_anim) { - var1.anInt2764 = var2.slow_walk_follow_ccw_turn_anim; - } else if (var2.walk_follow_cw_turn_anim == var1.anInt2764 && -1 != var2.slow_walk_follow_cw_turn_anim) { - var1.anInt2764 = var2.slow_walk_follow_cw_turn_anim; - } else { - var1.anInt2764 = var2.slow_walk_anim; - } - } - - if (var2.movement_acceleration != -1) { - var9 <<= 7; - if (var1.anInt2816 == 1) { - int var13 = (var1.xAxis <= var5 ? var5 - var1.xAxis : -var5 + var1.xAxis) << 7; - int var12 = var1.anInt2758 * var1.anInt2758; - int var14 = (var1.zAxis > var6 ? -var6 + var1.zAxis : -var1.zAxis + var6) << 7; - int var15 = var13 > var14 ? var13 : var14; - int var16 = var2.movement_acceleration * 2 * var15; - if (var12 <= var16) { - if (var12 / 2 > var15) { - var1.anInt2758 -= var2.movement_acceleration; - if (var1.anInt2758 < 0) { - var1.anInt2758 = 0; - } - } else if (var1.anInt2758 < var9) { - var1.anInt2758 += var2.movement_acceleration; - if (var1.anInt2758 > var9) { - var1.anInt2758 = var9; - } - } - } else { - var1.anInt2758 /= 2; - } - } else if (var1.anInt2758 < var9) { - var1.anInt2758 += var2.movement_acceleration; - if (var1.anInt2758 > var9) { - var1.anInt2758 = var9; - } - } else { - var1.anInt2758 -= var2.movement_acceleration; - if (0 > var1.anInt2758) { - var1.anInt2758 = 0; - } - } - - var9 = var1.anInt2758 >> 7; - if (var9 < 1) { - var9 = 1; - } - } - - if (var5 > var18) { - var1.xAxis += var9; - if (var1.xAxis > var5) { - var1.xAxis = var5; - } - } else if (var18 > var5) { - var1.xAxis -= var9; - if (var1.xAxis < var5) { - var1.xAxis = var5; - } - } - - if (var4 >= var6) { - if (var6 < var4) { - var1.zAxis -= var9; - if (var6 > var1.zAxis) { - var1.zAxis = var6; - } - } - } else { - var1.zAxis += var9; - if (var6 < var1.zAxis) { - var1.zAxis = var6; - } - } - - if (var5 == var1.xAxis && var6 == var1.zAxis) { - --var1.anInt2816; - if (0 < var1.anInt2811) { - --var1.anInt2811; - } - } - } - - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "ia.C(" + var0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static int method1184(int var0, byte var1) { - try { - int var3 = var0 & 63; - int var4 = (var0 & 217) >> 6; - if (var3 == 18) { - if (0 == var4) { - return 1; - } - - if (var4 == 1) { - return 2; - } - - if (var4 == 2) { - return 4; - } - - return 8; - } else if (var3 == 19 || var3 == 21) { - if (var4 == 0) { - return 16; - } - - if (1 == var4) { - return 32; - } - - if (var4 == 2) { - return 64; - } - - return 128; - } - - return 0; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ia.A(" + var0 + ',' + var1 + ')'); - } - } - - public static boolean method73(short var0) { - try { - if (var0 != 47 && var0 != 5 && var0 != 43 && var0 != 35 && var0 != 58 && var0 != 22 && var0 != 40 && var0 != 3) { - if (var0 != 9 && var0 != 12 && var0 != 1006 && var0 != 1003) { - - return var0 == 25 || var0 == 23 || 48 == var0 || var0 == 7 || var0 == 13 || (var0 == 8 || var0 == 32 || var0 == 28 || var0 == 59 || var0 == 51 || var0 == 41); - } else { - return true; - } - } else { - return true; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "aa.C(" + var0 + ',' + 121 + ')'); - } - } - - public static void method75(RSInterface[] var0, int var2) { - try { - for (int var3 = 0; var0.length > var3; ++var3) { - RSInterface var4 = var0[var3]; - if (null != var4) { - if (var4.type == 0) { - if (null != var4.aClass11Array262) { - method75(var4.aClass11Array262, var2); - } - - Class3_Sub31 var5 = TextureOperation23.aHashTable_3208.get(var4.componentHash); - if (null != var5) { - Class3_Sub8.method124(49, var2, var5.anInt2602); - } - } - - CS2Script var7; - if (var2 == 0 && null != var4.anObjectArray206) { - var7 = new CS2Script(); - var7.arguments = var4.anObjectArray206; - var7.aClass11_2449 = var4; - Class43.method1065(var7); - } - - if (var2 == 1 && var4.anObjectArray176 != null) { - if (var4.anInt191 >= 0) { - RSInterface var8 = getRSInterface(var4.componentHash); - if (null == var8 || null == var8.aClass11Array262 || var8.aClass11Array262.length <= var4.anInt191 || var8.aClass11Array262[var4.anInt191] != var4) { - continue; - } - } - - var7 = new CS2Script(); - var7.arguments = var4.anObjectArray176; - var7.aClass11_2449 = var4; - Class43.method1065(var7); - } - } - } - - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "aa.I(" + (var0 != null ? "{...}" : "null") + ',' + true + ',' + var2 + ')'); - } - } - - public static boolean method76(int var0, int var1, int var2, int var3, int var4, boolean var5, int var6, int var7, int var8, int var9, int var11) { - try { - int var12; - int var13; - for (var12 = 0; 104 > var12; ++var12) { - for (var13 = 0; var13 < 104; ++var13) { - Class84.anIntArrayArray1160[var12][var13] = 0; - Class97.anIntArrayArray1373[var12][var13] = 99999999; - } - } - - Class84.anIntArrayArray1160[var11][var3] = 99; - Class97.anIntArrayArray1373[var11][var3] = 0; - var13 = var3; - var12 = var11; - byte var14 = 0; - TextureOperation38.anIntArray3456[var14] = var11; - boolean var16 = false; - int var15 = 0; - int var27 = var14 + 1; - Class45.anIntArray729[var14] = var3; - int[][] var17 = AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].anIntArrayArray1304; - - int var18; - while (var15 != var27) { - var12 = TextureOperation38.anIntArray3456[var15]; - var13 = Class45.anIntArray729[var15]; - var15 = 4095 & var15 - -1; - if (var8 == var12 && var13 == var4) { - var16 = true; - break; - } - - if (var1 != 0) { - if ((5 > var1 || var1 == 10) && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1488(var4, var12, var13, var8, -1 + var1, 2, var7)) { - var16 = true; - break; - } - - if (var1 < 10 && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1492(var4, var1 + -1, var8, var13, 2, var7, var12, 88)) { - var16 = true; - break; - } - } - - if (0 != var0 && var6 != 0 && AtmosphereParser.aClass91Array1182[WorldListCountry.localPlane].method1498(var8, var13, var12, 2, var0, var2, var4, var6)) { - var16 = true; - break; - } - - var18 = Class97.anIntArrayArray1373[var12][var13] - -1; - if (var12 > 0 && Class84.anIntArrayArray1160[var12 + -1][var13] == 0 && 0 == (var17[-1 + var12][var13] & 19661070) && (var17[-1 + var12][var13 + 1] & 19661112) == 0) { - TextureOperation38.anIntArray3456[var27] = var12 + -1; - Class45.anIntArray729[var27] = var13; - var27 = 1 + var27 & 4095; - Class84.anIntArrayArray1160[var12 - 1][var13] = 2; - Class97.anIntArrayArray1373[-1 + var12][var13] = var18; - } - - if (102 > var12 && Class84.anIntArrayArray1160[1 + var12][var13] == 0 && 0 == (var17[2 + var12][var13] & 19661187) && 0 == (var17[var12 + 2][1 + var13] & 19661280)) { - TextureOperation38.anIntArray3456[var27] = 1 + var12; - Class45.anIntArray729[var27] = var13; - var27 = 4095 & var27 + 1; - Class84.anIntArrayArray1160[var12 - -1][var13] = 8; - Class97.anIntArrayArray1373[var12 - -1][var13] = var18; - } - - if (var13 > 0 && Class84.anIntArrayArray1160[var12][var13 + -1] == 0 && (19661070 & var17[var12][-1 + var13]) == 0 && (var17[var12 + 1][var13 - 1] & 19661187) == 0) { - TextureOperation38.anIntArray3456[var27] = var12; - Class45.anIntArray729[var27] = -1 + var13; - Class84.anIntArrayArray1160[var12][-1 + var13] = 1; - Class97.anIntArrayArray1373[var12][-1 + var13] = var18; - var27 = 4095 & 1 + var27; - } - - if (var13 < 102 && Class84.anIntArrayArray1160[var12][var13 + 1] == 0 && (var17[var12][2 + var13] & 19661112) == 0 && (var17[1 + var12][var13 + 2] & 19661280) == 0) { - TextureOperation38.anIntArray3456[var27] = var12; - Class45.anIntArray729[var27] = var13 + 1; - Class84.anIntArrayArray1160[var12][1 + var13] = 4; - var27 = 1 + var27 & 4095; - Class97.anIntArrayArray1373[var12][var13 - -1] = var18; - } - - if (var12 > 0 && var13 > 0 && Class84.anIntArrayArray1160[-1 + var12][-1 + var13] == 0 && (19661112 & var17[-1 + var12][var13]) == 0 && 0 == (var17[-1 + var12][-1 + var13] & 19661070) && 0 == (19661187 & var17[var12][-1 + var13])) { - TextureOperation38.anIntArray3456[var27] = -1 + var12; - Class45.anIntArray729[var27] = -1 + var13; - Class84.anIntArrayArray1160[-1 + var12][-1 + var13] = 3; - Class97.anIntArrayArray1373[-1 + var12][-1 + var13] = var18; - var27 = 4095 & var27 + 1; - } - - if (var12 < 102 && 0 < var13 && Class84.anIntArrayArray1160[1 + var12][var13 + -1] == 0 && (var17[var12 - -1][var13 - 1] & 19661070) == 0 && (19661187 & var17[2 + var12][var13 + -1]) == 0 && (19661280 & var17[var12 + 2][var13]) == 0) { - TextureOperation38.anIntArray3456[var27] = var12 - -1; - Class45.anIntArray729[var27] = var13 - 1; - var27 = 4095 & 1 + var27; - Class84.anIntArrayArray1160[var12 - -1][-1 + var13] = 9; - Class97.anIntArrayArray1373[1 + var12][var13 + -1] = var18; - } - - if (var12 > 0 && var13 < 102 && 0 == Class84.anIntArrayArray1160[var12 - 1][var13 + 1] && (var17[-1 + var12][var13 - -1] & 19661070) == 0 && 0 == (19661112 & var17[-1 + var12][var13 - -2]) && (19661280 & var17[var12][var13 + 2]) == 0) { - TextureOperation38.anIntArray3456[var27] = var12 - 1; - Class45.anIntArray729[var27] = 1 + var13; - Class84.anIntArrayArray1160[var12 - 1][1 + var13] = 6; - Class97.anIntArrayArray1373[-1 + var12][1 + var13] = var18; - var27 = 1 + var27 & 4095; - } - - if (var12 < 102 && var13 < 102 && Class84.anIntArrayArray1160[1 + var12][1 + var13] == 0 && (19661112 & var17[var12 - -1][2 + var13]) == 0 && (19661280 & var17[var12 + 2][var13 - -2]) == 0 && (19661187 & var17[var12 - -2][var13 + 1]) == 0) { - TextureOperation38.anIntArray3456[var27] = 1 + var12; - Class45.anIntArray729[var27] = var13 + 1; - var27 = var27 - -1 & 4095; - Class84.anIntArrayArray1160[var12 - -1][var13 + 1] = 12; - Class97.anIntArrayArray1373[1 + var12][1 + var13] = var18; - } - } - - Class129.anInt1692 = 0; - int var19; - if (!var16) { - if (!var5) { - return false; - } - - var18 = 1000; - var19 = 100; - byte var20 = 10; - - for (int var21 = -var20 + var8; var20 + var8 >= var21; ++var21) { - for (int var22 = var4 + -var20; var20 + var4 >= var22; ++var22) { - if (var21 >= 0 && var22 >= 0 && var21 < 104 && 104 > var22 && Class97.anIntArrayArray1373[var21][var22] < 100) { - int var23 = 0; - int var24 = 0; - if (var8 <= var21) { - if (-1 + var0 + var8 < var21) { - var23 = var21 + 1 + -var0 + -var8; - } - } else { - var23 = var8 - var21; - } - - if (var22 >= var4) { - if (-1 + var4 + var6 < var22) { - var24 = -var4 + -var6 + 1 + var22; - } - } else { - var24 = -var22 + var4; - } - - int var25 = var23 * var23 + var24 * var24; - if (var25 < var18 || var25 == var18 && Class97.anIntArrayArray1373[var21][var22] < var19) { - var13 = var22; - var19 = Class97.anIntArrayArray1373[var21][var22]; - var18 = var25; - var12 = var21; - } - } - } - } - - if (var18 == 1000) { - return false; - } - - if (var11 == var12 && var3 == var13) { - return false; - } - - Class129.anInt1692 = 1; - } - - byte var28 = 0; - TextureOperation38.anIntArray3456[var28] = var12; - var15 = var28 + 1; - Class45.anIntArray729[var28] = var13; - var18 = var19 = Class84.anIntArrayArray1160[var12][var13]; - - for (; var12 != var11 || var13 != var3; var18 = Class84.anIntArrayArray1160[var12][var13]) { - if (var19 != var18) { - TextureOperation38.anIntArray3456[var15] = var12; - Class45.anIntArray729[var15++] = var13; - var19 = var18; - } - - if ((var18 & 2) != 0) { - ++var12; - } else if ((8 & var18) != 0) { - --var12; - } - - if ((var18 & 1) == 0) { - if ((var18 & 4) != 0) { - --var13; - } - } else { - ++var13; - } - } - - if (var15 <= 0) { - return 1 != var9; - } else { - TextureOperation7.method299(113, var15, var9); - return true; - } - } catch (RuntimeException var26) { - throw ClientErrorException.clientError(var26, "aa.A(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ',' + 127 + ',' + var11 + ')'); - } - } - - public static void method78(int var0, int var2) { - try { - TextureOperation12.outgoingBuffer.putOpcode(132); - TextureOperation12.outgoingBuffer.writeIntV1(var2); - TextureOperation12.outgoingBuffer.writeShortLE(var0); - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "aa.E(" + var0 + ',' + false + ',' + var2 + ')'); - } - } - - public static void method81(RSInterface var1) { - try { - - int var2 = var1.anInt189; - if (324 == var2) { - if (-1 == TextureOperation37.anInt3260) { - TextureOperation37.anInt3260 = var1.spriteArchiveId; - anInt1165 = var1.anInt296; - } - - if (aClass52_1112.aBoolean864) { - var1.spriteArchiveId = TextureOperation37.anInt3260; - } else { - var1.spriteArchiveId = anInt1165; - } - - } else if (var2 == 325) { - if (-1 == TextureOperation37.anInt3260) { - anInt1165 = var1.anInt296; - TextureOperation37.anInt3260 = var1.spriteArchiveId; - } - - if (aClass52_1112.aBoolean864) { - var1.spriteArchiveId = anInt1165; - } else { - var1.spriteArchiveId = TextureOperation37.anInt3260; - } - - } else if (var2 == 327) { - var1.anInt182 = 150; - var1.anInt308 = 2047 & (int) (Math.sin((double) Class44.anInt719 / 40.0D) * 256.0D); - var1.modelType = 5; - var1.itemId = -1; - } else if (var2 == 328) { - if (null == Class102.player.displayName) { - var1.itemId = 0; - } else { - var1.anInt182 = 150; - var1.anInt308 = 2047 & (int) (256.0D * Math.sin((double) Class44.anInt719 / 40.0D)); - var1.modelType = 5; - var1.itemId = 2047 + ((int) Class102.player.displayName.toLong() << 11); - var1.anInt260 = Class102.player.anInt2793; - var1.anInt267 = 0; - var1.animationId = Class102.player.anInt2764; - var1.anInt283 = Class102.player.anInt2813; - } - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "aa.H(" + (byte) -128 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - public static boolean method1088(boolean var0) { - try { - if (paramJavaScriptEnabled) { - try { - TextCore.aClass94_106.method1577(Class38.gameSignlink.gameApplet); - return true; - } catch (Throwable var2) { - } - } - - if (var0) { - aReferenceCache_743 = null; - } - - return false; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "gn.K(" + var0 + ')'); - } - } - - public static Class3_Sub28_Sub5 method1089(CacheIndex cacheIndex0, CacheIndex var2, int frameId) { - try { - boolean var5 = true; - int[] var6 = cacheIndex0.getFileIds(frameId); - - for (int var7 = 0; var7 < Objects.requireNonNull(var6).length; ++var7) { - byte[] var8 = cacheIndex0.method2140(var6[var7], frameId); - if (var8 == null) { - var5 = false; - } else { - int var9 = (255 & var8[0]) << 8 | var8[1] & 0xFF; - byte[] var10; - var10 = var2.method2140(0, var9); - - if (null == var10) { - // System.out.println("Roar , " + var9); - var5 = false; - } - } - } - if (var5) { - try { - return new Class3_Sub28_Sub5(cacheIndex0, var2, frameId); - } catch (Exception var11) { - var11.printStackTrace(); - return null; - } - } else { - return null; - } - } catch (RuntimeException var12) { - throw ClientErrorException.clientError(var12, "gn.J(" + (cacheIndex0 != null ? "{...}" : "null") + ',' + false + ',' + (var2 != null ? "{...}" : "null") + ',' + (byte) 118 + ',' + frameId + ')'); - } - } - - public static RSString emptyString(int capacit) { - RSString str = new RSString(); - str.length = 0; - str.buffer = new byte[capacit]; - return str; - } - - public static void method1091(boolean var0, int var1) { - try { - byte var2; - byte[][] var3; - if (HDToolKit.highDetail && var0) { - var2 = 1; - var3 = Class40.aByteArrayArray3669; - } else { - var2 = 4; - var3 = Class164_Sub2.aByteArrayArray3027; - } - - int var4 = var3.length; - - int var5; - int var6; - int var7; - byte[] var8; - for (var5 = 0; var5 < var4; ++var5) { - var6 = -Class131.anInt1716 + 64 * (Class3_Sub24_Sub3.anIntArray3494[var5] >> 8); - var7 = -Texture.anInt1152 + 64 * (255 & Class3_Sub24_Sub3.anIntArray3494[var5]); - var8 = var3[var5]; - if (null != var8) { - Class58.method1194(); - method777(AtmosphereParser.aClass91Array1182, var0, -48 + 8 * anInt3606, var7, var6, (Class3_Sub7.anInt2294 + -6) * 8, var8); - } - } - - var5 = 0; - if (var1 > -66) { - method1088(true); - } - - for (; var5 < var4; ++var5) { - var6 = -Class131.anInt1716 + 64 * (Class3_Sub24_Sub3.anIntArray3494[var5] >> 8); - var7 = -Texture.anInt1152 + 64 * (255 & Class3_Sub24_Sub3.anIntArray3494[var5]); - var8 = var3[var5]; - if (var8 == null && Class3_Sub7.anInt2294 < 800) { - Class58.method1194(); - - for (int var9 = 0; var9 < var2; ++var9) { - Class12.method870(var9, (byte) 102, var7, var6, 64, 64); - } - } - } - - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "gn.I(" + var0 + ',' + var1 + ')'); - } - } - - public static void method1093(boolean var0) { - try { - if (var0) { - method1093(true); - } - - for (int var1 = 0; var1 < 100; ++var1) { - aBooleanArray3674[var1] = true; - } - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "gn.H(" + var0 + ')'); - } - } - - public static void method1095(int var0, int var1, int var2, RSInterface[] var3, int var4, int var5, int var6, int var7, byte var8, int var9) { - try { - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var6, var4, var7); - } else { - Class74.setClipping(var0, var6, var4, var7); - Class51.method1134(); - } - - for (RSInterface var11 : var3) { - if (var11 != null && (var5 == var11.parentId || var5 == -1412584499 && var11 == Class56.aClass11_886)) { - int var12; - if (var9 == -1) { - AudioChannel.anIntArray1969[Class3_Sub28_Sub3.anInt3557] = var2 + var11.anInt306; - Player.anIntArray3954[Class3_Sub28_Sub3.anInt3557] = var11.anInt210 - -var1; - Class3_Sub28_Sub18.anIntArray3768[Class3_Sub28_Sub3.anInt3557] = var11.width; - Class140_Sub4.anIntArray2794[Class3_Sub28_Sub3.anInt3557] = var11.height; - var12 = Class3_Sub28_Sub3.anInt3557++; - } else { - var12 = var9; - } - - var11.anInt204 = Class44.anInt719; - var11.anInt292 = var12; - if (!var11.usingScripts || !Client.method51(var11)) { - if (0 < var11.anInt189) { - method81(var11); - } - - int var14 = var1 + var11.anInt210; - int alpha = var11.anInt223; - int var13 = var11.anInt306 + var2; - if (ClientCommands.commandQaOpEnabled && (0 != Client.method44(var11).anInt2205 || var11.type == 0) && alpha > 127) { - alpha = 127; - } - - int var17; - int var16; - if (var11 == Class56.aClass11_886) { - if (var5 != -1412584499 && !var11.aBoolean200) { - anInt3602 = var2; - anInt1082 = var1; - Class8.aClass11Array1836 = var3; - continue; - } - - if (NPC.aBoolean3975 && Class85.aBoolean1167) { - var17 = anInt1709; - var16 = Class126.anInt1676; - var17 -= Class95.anInt1336; - if (var17 < Class134.anInt1761) { - var17 = Class134.anInt1761; - } - - if (PacketParser.aClass11_88.height + Class134.anInt1761 < var17 + var11.height) { - var17 = -var11.height + PacketParser.aClass11_88.height + Class134.anInt1761; - } - - var14 = var17; - var16 -= anInt1881; - if (TextureOperation20.anInt3156 > var16) { - var16 = TextureOperation20.anInt3156; - } - - if (var11.width + var16 > PacketParser.aClass11_88.width + TextureOperation20.anInt3156) { - var16 = -var11.width + PacketParser.aClass11_88.width + TextureOperation20.anInt3156; - } - - var13 = var16; - } - - if (!var11.aBoolean200) { - alpha = 128; - } - } - - int var19; - int var18; - int var21; - int var20; - if (2 == var11.type) { - var19 = var7; - var18 = var4; - var17 = var6; - var16 = var0; - } else { - var17 = var6 < var14 ? var14 : var6; - var16 = var13 > var0 ? var13 : var0; - var20 = var11.width + var13; - var21 = var14 - -var11.height; - if (var11.type == 9) { - ++var21; - ++var20; - } - - var19 = var7 <= var21 ? var7 : var21; - var18 = var4 <= var20 ? var4 : var20; - } - - if (!var11.usingScripts || var16 < var18 && var19 > var17) { - int var23; - int var22; - int var25; - int var24; - int var26; - int var29; - int var28; - int var47; - if (var11.anInt189 != 0) { - if (var11.anInt189 == 1337 || var11.anInt189 == 1403 && HDToolKit.highDetail) { - aClass11_2091 = var11; - anInt2567 = var14; - Class53.anInt865 = var13; - method338(var11.height, var11.anInt189 == 1403, var13, var11.width, var14); - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var6, var4, var7); - } else { - Class74.setClipping(var0, var6, var4, var7); - } - continue; - } - - if (var11.anInt189 == 1338) { - if (!var11.method855()) { - continue; - } - - Minimap.displayMinimap(var12, (byte) 59, var14, var13, var11); - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var6, var4, var7); - } else { - Class74.setClipping(var0, var6, var4, var7); - } - - if (0 != Class161.anInt2028 && 3 != Class161.anInt2028 || Class38_Sub1.aBoolean2615 || var16 > NPCDefinition.anInt1297 || var17 > Class38_Sub1.anInt2612 || var18 <= NPCDefinition.anInt1297 || Class38_Sub1.anInt2612 >= var19) { - continue; - } - - var20 = NPCDefinition.anInt1297 - var13; - var21 = -var14 + Class38_Sub1.anInt2612; - var22 = var11.anIntArray207[var21]; - if (var22 > var20 || var22 + var11.anIntArray291[var21] < var20) { - continue; - } - - var21 -= var11.height / 2; - var23 = 2047 & GraphicDefinition.CAMERA_DIRECTION - -TextureOperation9.anInt3102; - var20 -= var11.width / 2; - var24 = Class51.anIntArray840[var23]; - var25 = Class51.anIntArray851[var23]; - var24 = (Class164_Sub2.anInt3020 + 256) * var24 >> 8; - var25 = (Class164_Sub2.anInt3020 - -256) * var25 >> 8; - var47 = -(var24 * var20) + var25 * var21 >> 11; - var26 = var21 * var24 - -(var20 * var25) >> 11; - var28 = Class102.player.xAxis + var26 >> 7; - var29 = -var47 + Class102.player.zAxis >> 7; - if (GameObject.aBoolean1837 && 0 != (Class164.anInt2051 & 64)) { - RSInterface var53 = AbstractSprite.method638(BufferedDataStream.anInt872, RSInterface.anInt278); - if (null == var53) { - Class25.method958((byte) -91); - } else { - Class3_Sub24_Sub4.method1177(anInt1887, 1L, (byte) -49, TextCore.aClass94_1724, var28, (short) 11, Class3_Sub28_Sub9.aClass94_3621, var29); - } - continue; - } - - if (Class158.paramGameTypeID == 1) { - Class3_Sub24_Sub4.method1177(-1, 1L, (byte) -41, RSString.parse(""), var28, (short) 36, TextCore.HasFaceHere, var29); - } - - Class3_Sub24_Sub4.method1177(-1, 1L, (byte) -125, RSString.parse(""), var28, (short) 60, TextureOperation32.aClass94_3353, var29); - continue; - } - - if (var11.anInt189 == 1339) { - if (var11.method855()) { - Compass.drawCompass(var13, var14, var11, var12); - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var6, var4, var7); - } else { - Class74.setClipping(var0, var6, var4, var7); - } - } - continue; - } - - if (var11.anInt189 == 1400) { - WorldMap.drawWorldMap(var13, var14, var11.height, var11.width); - aBooleanArray3674[var12] = true; - Class163_Sub1_Sub1.aBooleanArray4008[var12] = true; - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var6, var4, var7); - } else { - Class74.setClipping(var0, var6, var4, var7); - } - continue; - } - - if (var11.anInt189 == 1401) { - Class1.worldMapMinimap(var13, var11.height, var11.width, var14); - aBooleanArray3674[var12] = true; - Class163_Sub1_Sub1.aBooleanArray4008[var12] = true; - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var6, var4, var7); - } else { - Class74.setClipping(var0, var6, var4, var7); - } - continue; - } - - if (1402 == var11.anInt189) { - if (!HDToolKit.highDetail) { - Class129.method1768(var13, var14); - aBooleanArray3674[var12] = true; - Class163_Sub1_Sub1.aBooleanArray4008[var12] = true; - } - continue; - } - - if (var11.anInt189 == 1405) { - if (DeveloperConsole.INSTANCE.getOpen()) { - gameWindowWidth = var11.width + var13; - } - - if (ClientCommands.fpsOverlayEnabled) { - var20 = var11.width + var13; - var21 = 15 + var14; - FontType.plainFont.drawStringRightAnchor(RSString.stringCombiner(new RSString[]{RSString.parse("Fps:"), RSString.stringAnimator(SequenceDefinition.anInt1862)}), var20, var21, 16776960, -1);//Class72.stringAnimator(SequenceDefinition.anInt1862)}), var20, var21, 16776960, -1); - var21 += 15; - Runtime var57 = Runtime.getRuntime(); - var23 = (int) ((var57.totalMemory() + -var57.freeMemory()) / 1024L); - var24 = 16776960; - if (var23 > 65536) { - var24 = 16711680; - } - - FontType.plainFont.drawStringRightAnchor(RSString.stringCombiner(new RSString[]{TextCore.aClass94_4057, RSString.stringAnimator(var23), TextCore.Memoryk}), var20, var21, var24, -1); - var21 += 15; - if (HDToolKit.highDetail) { - var24 = 16776960; - var25 = (Class31.anInt580 + Class31.anInt585 + Class31.memory2D) / 1024; - if (65536 < var25) { - var24 = 16711680; - } - - FontType.plainFont.drawStringRightAnchor(RSString.stringCombiner(new RSString[]{TextCore.aClass94_1622, RSString.stringAnimator(var25), TextCore.Memoryk}), var20, var21, var24, -1); - var21 += 15; - } - - var24 = 16776960; - var25 = 0; - var47 = 0; - var26 = 0; - - for (var28 = 0; var28 < 29; ++var28) { //TODO: - var25 += aClass151_Sub1Array2601[var28].method2108(); - var26 += aClass151_Sub1Array2601[var28].method2102(); - var47 += aClass151_Sub1Array2601[var28].method2106(); - } - - var29 = 10000 * var26 / var25; - var28 = var47 * 100 / var25; - RSString var55 = RSString.stringCombiner(new RSString[]{TextCore.aClass94_436, Class3_Sub23.method407(0, true, 2, var29), TextCore.aClass94_2498, RSString.stringAnimator(var28), RSString.parse("(U(Y")}); - FontType.smallFont.drawStringRightAnchor(var55, var20, var21, var24, -1); - aBooleanArray3674[var12] = true; - Class163_Sub1_Sub1.aBooleanArray4008[var12] = true; - continue; - } - if (ClientCommands.renderInfoOverlayEnabled) { - if (HDToolKit.highDetail) { - int vendorColor; - String vendor = HDToolKit.gl.glGetString(7936); - if (vendor.contains("NVIDIA")) { - vendorColor = 7780608; - } else if (vendor.contains("AMD")) { - vendorColor = 15539236; - } else if (vendor.contains("INTEL")) { - vendorColor = 29125; - } else { - vendorColor = 16776960; - } - var20 = var11.width + var13; - var21 = 15 + var14; - FontType.plainFont.drawStringRightAnchor(RSString.parse(HDToolKit.gl.glGetString(7938)), var20, var21, 16776960, 7); - var21 += 15; - FontType.plainFont.drawStringRightAnchor(RSString.parse(vendor), var20, var21, vendorColor, 4); - var21 += 15; - FontType.plainFont.drawStringRightAnchor(RSString.parse(HDToolKit.gl.glGetString(7937)), var20, var21, 16776960, 7); - var21 += 15; - FontType.plainFont.drawStringRightAnchor(RSString.parse("Oncard Geometry: " + Class31.anInt585), var20, var21, 16776960, 7); - var21 += 15; - FontType.plainFont.drawStringRightAnchor(RSString.parse("Oncard 2D: " + Class31.memory2D), var20, var21, 16776960, 7); - var21 += 15; - FontType.plainFont.drawStringRightAnchor(RSString.parse("Oncard Texture: " + Class31.anInt580), var20, var21, 16776960, 7); - } else { - var20 = var11.width + var13; - var21 = 15 + var14; - FontType.plainFont.drawStringRightAnchor(RSString.parse("Java Toolkit"), var20, var21, 16776960, 7); - var21 += 15; - FontType.plainFont.drawStringRightAnchor(RSString.parse(System.getProperty("java.vendor")), var20, var21, 16776960, 7); - var21 += 15; - FontType.plainFont.drawStringRightAnchor(RSString.parse(System.getProperty("java.version")), var20, var21, 16776960, 7); - } - continue; - } - XPGainDraw.drawGains(); - SlayerTracker.draw(); - continue; - } - - if (var11.anInt189 == 1406) { - ClientErrorException.anInt2115 = var14; - AbstractSprite.aClass11_3708 = var11; - TextureOperation18.anInt4041 = var13; - continue; - } - } - - if (!Class38_Sub1.aBoolean2615) { - if (var11.type == 0 && var11.aBoolean219 && NPCDefinition.anInt1297 >= var16 && var17 <= Class38_Sub1.anInt2612 && NPCDefinition.anInt1297 < var18 && Class38_Sub1.anInt2612 < var19 && !ClientCommands.commandQaOpEnabled) { - menuOptionCount = 1; - Class114.anIntArray1578[0] = Class3_Sub28_Sub5.anInt3590; - Class140_Sub7.aClass94Array2935[0] = TextCore.HasCancel; - Class163_Sub2_Sub1.aClass94Array4016[0] = RSString.parse(""); - TextureOperation27.aShortArray3095[0] = 1005; - } - - if (var16 <= NPCDefinition.anInt1297 && Class38_Sub1.anInt2612 >= var17 && var18 > NPCDefinition.anInt1297 && Class38_Sub1.anInt2612 < var19) { - Class3_Sub24_Sub4.method477(Class38_Sub1.anInt2612 + -var14, -var13 + NPCDefinition.anInt1297, var11); - } - } - - if (var11.type == 0) { - if (!var11.usingScripts && Client.method51(var11) && Class107.aClass11_1453 != var11) { - continue; - } - - if (!var11.usingScripts) { - if (var11.anInt208 > -var11.height + var11.anInt252) { - var11.anInt208 = -var11.height + var11.anInt252; - } - - if (0 > var11.anInt208) { - var11.anInt208 = 0; - } - } - - method1095(var16, -var11.anInt208 + var14, -var11.anInt247 + var13, var3, var18, var11.componentHash, var17, var19, (byte) 87, var12); - if (null != var11.aClass11Array262) { - method1095(var16, -var11.anInt208 + var14, -var11.anInt247 + var13, var11.aClass11Array262, var18, var11.componentHash, var17, var19, (byte) 52, var12); - } - - Class3_Sub31 var36 = TextureOperation23.aHashTable_3208.get(var11.componentHash); - if (var36 != null) { - if (var36.anInt2603 == 0 && !Class38_Sub1.aBoolean2615 && NPCDefinition.anInt1297 >= var16 && Class38_Sub1.anInt2612 >= var17 && NPCDefinition.anInt1297 < var18 && Class38_Sub1.anInt2612 < var19 && !ClientCommands.commandQaOpEnabled) { - Class140_Sub7.aClass94Array2935[0] = TextCore.HasCancel; - menuOptionCount = 1; - Class114.anIntArray1578[0] = Class3_Sub28_Sub5.anInt3590; - TextureOperation27.aShortArray3095[0] = 1005; - Class163_Sub2_Sub1.aClass94Array4016[0] = RSString.parse(""); - } - - TextureOperation12.method171(-101, var36.anInt2602, var16, var18, var13, var12, var19, var17, var14); - } - - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var6, var4, var7); - } else { - Class74.setClipping(var0, var6, var4, var7); - Class51.method1134(); - } - } - - if (aBooleanArray1712[var12] || Client.rectDebugInt > 1) { - if (var11.type == 0 && !var11.usingScripts && var11.anInt252 > var11.height) { - TextureOperation24.method224(var11.anInt208, var11.anInt252, var11.width + var13, var14, var11.height); - } - - if (var11.type != 1) { - boolean var39; - boolean var46; - if (var11.type == 2) { - var20 = 0; - - for (var21 = 0; var11.defHeight > var21; ++var21) { - for (var22 = 0; var11.defWidth > var22; ++var22) { - var24 = var14 + var21 * (32 - -var11.anInt290); - var23 = (var11.anInt285 + 32) * var22 + var13; - if (var20 < 20) { - var24 += var11.anIntArray300[var20]; - var23 += var11.anIntArray272[var20]; - } - - if (var11.itemAmounts[var20] <= 0) { - if (null != var11.anIntArray197 && var20 < 20) { - AbstractSprite var58 = var11.method859(var20); - if (null == var58) { - if (RSInterface.aBoolean6) { - Class20.method909(var11); - } - } else { - var58.drawAt(var23, var24); - } - } - } else { - var47 = var11.itemAmounts[var20] + -1; - if (var0 < 32 + var23 && var4 > var23 && var24 - -32 > var6 && var7 > var24 || var11 == Class67.aClass11_1017 && var20 == PacketParser.anInt86) { - AbstractSprite var54; - if (Class164_Sub1.anInt3012 == 1 && BufferedDataStream.anInt1473 == var20 && Class3_Sub28_Sub18.anInt3764 == var11.componentHash) { - var54 = Class114.method1707(2, var47, var11.aBoolean227, var11.itemIds[var20], 0); - } else { - var54 = Class114.method1707(1, var47, var11.aBoolean227, var11.itemIds[var20], 3153952); - } - - if (Class51.aBoolean837) { - aBooleanArray3674[var12] = true; - } - - if (null == var54) { - Class20.method909(var11); - } else if (Class67.aClass11_1017 == var11 && var20 == PacketParser.anInt86) { - var25 = Class126.anInt1676 - Class129_Sub1.anInt2693; - var26 = -anInt40 + anInt1709; - if (var26 < 5 && var26 > -5) { - var26 = 0; - } - - if (var25 < 5 && var25 > -5) { - var25 = 0; - } - - if (5 > Class40.anInt677) { - var25 = 0; - var26 = 0; - } - - var54.method637(var23 + var25, var24 - -var26, 128); - if (var5 != -1) { - RSInterface var51 = var3[var5 & 65535]; - int var31; - int var30; - if (HDToolKit.highDetail) { - var31 = Class22.anInt451; - var30 = Class22.anInt448; - } else { - var30 = Toolkit.JAVA_TOOLKIT.clipTop; - var31 = Toolkit.JAVA_TOOLKIT.clipBottom; - } - - int var32; - if (var26 + var24 < var30 && var51.anInt208 > 0) { - var32 = Class106.anInt1446 * (-var26 + var30 + -var24) / 3; - if (Class106.anInt1446 * 10 < var32) { - var32 = 10 * Class106.anInt1446; - } - - if (var32 > var51.anInt208) { - var32 = var51.anInt208; - } - - var51.anInt208 -= var32; - anInt40 += var32; - Class20.method909(var51); - } - - if (var31 < 32 + var26 + var24 && var51.anInt208 < -var51.height + var51.anInt252) { - var32 = (-var31 + 32 + (var24 - -var26)) * Class106.anInt1446 / 3; - if (var32 > Class106.anInt1446 * 10) { - var32 = 10 * Class106.anInt1446; - } - - if (-var51.anInt208 + var51.anInt252 + -var51.height < var32) { - var32 = var51.anInt252 + -var51.height + -var51.anInt208; - } - - var51.anInt208 += var32; - anInt40 -= var32; - Class20.method909(var51); - } - } - } else if (var11 == aClass11_1933 && var20 == KeyboardListener.anInt1918) { - var54.method637(var23, var24, 128); - } else { - var54.drawAt(var23, var24); - } - } - } - - ++var20; - } - } - } else if (3 == var11.type) { - if (method609(var11, 26)) { - var20 = var11.anInt253; - if (Class107.aClass11_1453 == var11 && 0 != var11.anInt222) { - var20 = var11.anInt222; - } - } else { - var20 = var11.anInt218; - if (var11 == Class107.aClass11_1453 && 0 != var11.anInt228) { - var20 = var11.anInt228; - } - } - - /* INTERFACE DRAWS ALPHA/NON-ALPHA BOXES/BACKGROUNDS */ - if (alpha != 0) { - if (var11.aBoolean226) { - if (HDToolKit.highDetail) { - Toolkit.OPENGL_TOOLKIT.fillRect(var13, var14, var11.width, var11.height, var20, 256 + -(255 & alpha));//Magic book spell tool-tip hover background draw - } else { - Toolkit.JAVA_TOOLKIT.fillRect(var13, var14, var11.width, var11.height, var20, 256 + -(255 & alpha)); - } - } else if (HDToolKit.highDetail) { - Class22.method928(var13, var14, var11.width, var11.height, var20, 256 + -(alpha & 0xFF));//Interface backgrounds (includes dark areas) - } else { - Class74.method1315(var13, var14, var11.width, var11.height, var20, -(alpha & 0xFF) + 256);//Interface backgrounds (includes dark areas) - } - } else if (var11.aBoolean226) { - if (HDToolKit.highDetail) { - Toolkit.OPENGL_TOOLKIT.method934(var13, var14, var11.width, var11.height, var20);//Skill tool-tip hover background draw - } else { - Toolkit.JAVA_TOOLKIT.fillRect(var13, var14, var11.width, var11.height, var20, 255);//Skill tool-tip pop up background - } - } else if (HDToolKit.highDetail) { - Toolkit.OPENGL_TOOLKIT.drawRect(var13, var14, var11.width, var11.height, var20, 255);//Boxed border background draw - } else { - Toolkit.JAVA_TOOLKIT.drawRect(var13, var14, var11.width, var11.height, var20, 255);//Boxed border background draw - } - // TODO DRAWRECT -// if (Class3_Sub28_Sub12.method609(var11, 26)) { -// var20 = var11.anInt253; -// if (Class107.aClass11_1453 == var11 && 0 != var11.anInt222) { -// var20 = var11.anInt222; -// } -// } else { -// var20 = var11.anInt218; -// if (var11 == Class107.aClass11_1453 && 0 != var11.anInt228) { -// var20 = var11.anInt228; -// } -// } -// -// if (var15 != 0) { -// if (var11.aBoolean226) { -// Toolkit.getActiveToolkit().fillRect(var13, var14, var11.width, var11.height, var20, 256 - (var15 & 0xFF));//Background for mage book -// } else if (HDToolKit.highDetail) { -// Class22.method928(var13, var14, var11.width, var11.height, var20, 256 + -(var15 & 0xFF)); -// } else { -// Class74.method1315(var13, var14, var11.width, var11.height, var20, -(var15 & 0xFF) + 256); -// } -// } else if (var11.aBoolean226) { -// Toolkit.getActiveToolkit().method934(var13, var14, var11.width, var11.height, var20);//Fill Rectangle for skill hover -// } else { -// Toolkit.getActiveToolkit().drawRect(var13, var14, var11.width, var11.height, var20, 255);//Background border for mage book -// } - - } else { - Font var34; - if (var11.type == 4) { - // TODO RSInterfaceRenderText - var34 = var11.method868(Sprites.nameIconsSpriteArray); - if (var34 != null) { - RSString var45 = var11.text; - if (method609(var11, 97)) { - var21 = var11.anInt253; - if (Class107.aClass11_1453 == var11 && var11.anInt222 != 0) { - var21 = var11.anInt222; - } - - if (var11.aClass94_172.length() > 0) { - var45 = var11.aClass94_172; - } - } else { - var21 = var11.anInt218; - if (Class107.aClass11_1453 == var11 && var11.anInt228 != 0) { - var21 = var11.anInt228; - } - } - - if (var11.usingScripts && var11.anInt192 != -1) { - ItemDefinition var50 = ItemDefinition.getItemDefinition(var11.anInt192); - var45 = var50.name; - if (var45 == null) { - var45 = TextCore.HasNull; - } - - if ((var50.stackingType == 1 || var11.anInt271 != 1) && var11.anInt271 != -1) { - var45 = RSString.stringCombiner(new RSString[]{ColorCore.ItemInterfaceColor, var45, TextCore.aClass94_2306, Class36.method1013((byte) -125, var11.anInt271)}); - } - } - - if (TextureOperation27.aClass11_3087 == var11) { - var21 = var11.anInt218; - var45 = TextCore.LoadingPleaseWait; - } - - if (!var11.usingScripts) { - var45 = method1303(var11, var45); - } - - var34.method676(var45, var13, var14, var11.width, var11.height, var21, !var11.aBoolean215 ? -1 : 0, var11.anInt194, var11.anInt225, var11.anInt205); - } else if (RSInterface.aBoolean6) { - Class20.method909(var11); - } - } else if (5 == var11.type) { - AbstractSprite var37; - if (var11.usingScripts) { - if (var11.anInt192 == -1) { - var37 = var11.method866(false); - } else { - var37 = Class114.method1707(var11.anInt288, var11.anInt192, var11.aBoolean227, var11.anInt271, var11.anInt287); - } - - if (var37 == null) { - if (RSInterface.aBoolean6) { - Class20.method909(var11); - } - } else { - var21 = var37.anInt3697; - var22 = var37.anInt3706; - if (var11.aBoolean186) { - var23 = (var21 + -1 + var11.width) / var21; - var24 = (var11.height - 1 - -var22) / var22; - if (HDToolKit.highDetail) { - Class22.method931(var13, var14, var11.width + var13, var11.height + var14); - var39 = Class140_Sub6.method2021((byte) -94, var37.width); - var46 = Class140_Sub6.method2021((byte) -113, var37.height); - HDSprite var27 = (HDSprite) var37; - if (var39 && var46) { - if (alpha == 0) { - var27.method649(var13, var14, var23, var24); - } else { - var27.method646(var13, var14, -(255 & alpha) + 256, var23, var24); - } - } else if (var39) { - for (var28 = 0; var28 < var24; ++var28) { - if (alpha == 0) { - var27.method649(var13, var28 * var22 + var14, var23, 1); - } else { - var27.method646(var13, var14 + var28 * var22, 256 + -(alpha & 0xFF), var23, 1); - } - } - } else if (var46) { - for (var28 = 0; var28 < var23; ++var28) { - if (alpha == 0) { - var27.method649(var21 * var28 + var13, var14, 1, var24); - } else { - var27.method646(var21 * var28 + var13, var14, -(alpha & 0xFF) + 256, 1, var24); - } - } - } else { - for (var28 = 0; var23 > var28; ++var28) { - for (var29 = 0; var29 < var24; ++var29) { - if (alpha == 0) { - var37.drawAt(var13 - -(var21 * var28), var22 * var29 + var14); - } else { - var37.method637(var28 * var21 + var13, var22 * var29 + var14, -(255 & alpha) + 256); - } - } - } - } - - Class22.setClipping(var0, var6, var4, var7); - } else { - Class74.method1326(var13, var14, var13 - -var11.width, var14 - -var11.height); - - for (var25 = 0; var25 < var23; ++var25) { - for (var26 = 0; var24 > var26; ++var26) { - if (var11.anInt301 == 0) { - if (0 == alpha) { - var37.drawAt(var25 * var21 + var13, var22 * var26 + var14); - } else { - var37.method637(var25 * var21 + var13, var14 + var26 * var22, 256 - (255 & alpha)); - } - } else { - var37.drawScaledOrRotated(var14 - -(var22 * var26) + var22 / 2, var11.anInt301, 4096, var25 * var21 + var13 + var21 / 2); - } - } - } - - Class74.setClipping(var0, var6, var4, var7); - } - } else { - var23 = var11.width * 4096 / var21; - if (var11.anInt301 == 0) { - if (0 != alpha) { - var37.method642(var13, var14, var11.width, var11.height, -(255 & alpha) + 256); - } else if (var11.width == var21 && var11.height == var22) { - var37.drawAt(var13, var14); - } else { - var37.method639(var13, var14, var11.width, var11.height); - } - } else { - var37.drawScaledOrRotated(var14 + var11.height / 2, var11.anInt301, var23, var13 + var11.width / 2); - } - } - } - } else { - var37 = var11.method866(method609(var11, 69)); - if (null != var37) { - var37.drawAt(var13, var14); - } else if (RSInterface.aBoolean6) { - Class20.method909(var11); - } - } - } else { - ItemDefinition var42; - if (var11.type == 6) { - boolean var41 = method609(var11, 110); - Model var38 = null; - if (var41) { - var21 = var11.secondAnimationId; - } else { - var21 = var11.animationId; - } - - var23 = 0; - if (var11.anInt192 != -1) { - var42 = ItemDefinition.getItemDefinition(var11.anInt192); - var42 = var42.method1106(var11.anInt271); - SequenceDefinition var52 = var21 == -1 ? null : SequenceDefinition.getAnimationDefinition(var21); - var38 = var42.method1110(var11.anInt260, var11.anInt267, var52, 1, var11.anInt283); - if (var38 == null) { - Class20.method909(var11); - } else { - var23 = -var38.method1871() / 2; - } - } else if (5 != var11.modelType) { - if (var21 == -1) { - var38 = var11.method865(-1, null, -1, 126, 0, var41, Class102.player.class52); - if (null == var38 && RSInterface.aBoolean6) { - Class20.method909(var11); - } - } else { - SequenceDefinition var48 = SequenceDefinition.getAnimationDefinition(var21); - var38 = var11.method865(var11.anInt260, var48, var11.anInt283, 127, var11.anInt267, var41, Class102.player.class52); - if (null == var38 && RSInterface.aBoolean6) { - Class20.method909(var11); - } - } - } else if (-1 == var11.itemId) { - var38 = aClass52_1112.method1165(null, -1, null, null, 0, -1, 100, 0, -1, -1); - } else { - var24 = 2047 & var11.itemId; - if (Class3_Sub1.localIndex == var24) { - var24 = 2047; - } - - Player var49 = players[var24]; - SequenceDefinition var56 = var21 == -1 ? null : SequenceDefinition.getAnimationDefinition(var21); - if (null != var49 && (-2048 & var11.itemId) == (int) var49.displayName.toLong() << 11) { - var38 = var49.class52.method1165(null, -1, null, var56, 0, -1, -126, 0, var11.anInt283, 0); - } - } - - if (var38 != null) { - if (var11.anInt184 > 0) { - var24 = (var11.width << 8) / var11.anInt184; - } else { - var24 = 256; - } - - if (var11.anInt312 <= 0) { - var25 = 256; - } else { - var25 = (var11.height << 8) / var11.anInt312; - } - - var26 = var13 - -(var11.width / 2) - -(var24 * var11.anInt259 >> 8); - var47 = var11.height / 2 + var14 + (var25 * var11.anInt230 >> 8); - if (HDToolKit.highDetail) { - if (var11.aBoolean181) { - HDToolKit.method1855(var26, var47, var11.anInt164, var11.aShort293, var24, var25); - } else { - HDToolKit.method1821(var26, var47, var24, var25); - HDToolKit.method1825(var11.aShort169, 1.5F * (float) var11.aShort293); - } - - HDToolKit.method1846(); - HDToolKit.method1831(true); - HDToolKit.method1827(false); - TextureOperation29.method324(anInt3625); - if (aBoolean47) { - Class22.resetClipping(); - HDToolKit.method1841(); - Class22.setClipping(var0, var6, var4, var7); - aBoolean47 = false; - } - - if (var11.aBoolean309) { - HDToolKit.depthBufferWritingDisabled(); - } - - var28 = Class51.anIntArray840[var11.anInt182] * var11.anInt164 >> 16; - var29 = var11.anInt164 * Class51.anIntArray851[var11.anInt182] >> 16; - if (var11.usingScripts) { - var38.method1893(var11.anInt308, var11.anInt280, var11.anInt182, var11.anInt258, var11.anInt264 + var28 + var23, var11.anInt264 + var29); - } else { - var38.method1893(var11.anInt308, 0, var11.anInt182, 0, var28, var29); - } - - if (var11.aBoolean309) { - HDToolKit.method1830(); - } - } else { - Class51.method1145(var26, var47); - var28 = Class51.anIntArray840[var11.anInt182] * var11.anInt164 >> 16; - var29 = var11.anInt164 * Class51.anIntArray851[var11.anInt182] >> 16; - if (!var11.usingScripts) { - var38.method1893(var11.anInt308, 0, var11.anInt182, 0, var28, var29); - } else if (var11.aBoolean181) { - ((Class140_Sub1_Sub2) var38).method1946(var11.anInt308, var11.anInt280, var11.anInt182, var11.anInt258, var11.anInt264 + var23 + var28, var29 + var11.anInt264, var11.anInt164); - } else { - var38.method1893(var11.anInt308, var11.anInt280, var11.anInt182, var11.anInt258, var11.anInt264 + (var28 - -var23), var29 + var11.anInt264); - } - - Class51.method1141(); - } - } - } else { - if (var11.type == 7) { - var34 = var11.method868(Sprites.nameIconsSpriteArray); - if (var34 == null) { - if (RSInterface.aBoolean6) { - Class20.method909(var11); - } - continue; - } - - var21 = 0; - - for (var22 = 0; var22 < var11.defHeight; ++var22) { - for (var23 = 0; var23 < var11.defWidth; ++var23) { - if (0 < var11.itemAmounts[var21]) { - var42 = ItemDefinition.getItemDefinition(var11.itemAmounts[var21] + -1); - RSString var40; - if (1 != var42.stackingType && 1 == var11.itemIds[var21]) { - var40 = RSString.stringCombiner(new RSString[]{ - ColorCore.ItemInterfaceColor, var42.name, TextCore.aClass94_2584 - }); - } else { - var40 = RSString.stringCombiner(new RSString[]{ - ColorCore.ItemInterfaceColor, var42.name, TextCore.aClass94_2306, Class36.method1013((byte) -100, var11.itemIds[var21]) - }); - } - - var26 = var13 + var23 * (var11.anInt285 + 115); - var47 = (var11.anInt290 + 12) * var22 + var14; - if (var11.anInt194 == 0) { - var34.method681(var40, var26, var47, var11.anInt218, var11.aBoolean215 ? 0 : -1); - } else if (var11.anInt194 == 1) { - var34.method699(var40, 57 + var26, var47, var11.anInt218, !var11.aBoolean215 ? -1 : 0); - } else { - var34.drawStringRightAnchor(var40, -1 + var26 + 115, var47, var11.anInt218, !var11.aBoolean215 ? -1 : 0); - } - } - - ++var21; - } - } - } - - if (var11.type == 8 && Class20.aClass11_439 == var11 && TextureOperation35.anInt3323 == Class75.anInt1109) { - var21 = 0; - var20 = 0; - RSString var43 = var11.text; - Font var35 = FontType.plainFont; - var43 = method1303(var11, var43); - - RSString var44; - while (var43.length() > 0) { - var25 = var43.indexOf(TextCore.aClass94_2598, 62); - if (var25 == -1) { - var44 = var43; - var43 = RSString.parse(""); - } else { - var44 = var43.substring(0, var25, 0); - var43 = var43.substring(var25 + 4); - } - - var26 = var35.method682(var44); - var21 += var35.anInt3727 - -1; - if (var26 > var20) { - var20 = var26; - } - } - - var26 = var14 - -var11.height - -5; - var20 += 6; - var21 += 7; - if (var7 < var26 - -var21) { - var26 = -var21 + var7; - } - - var25 = -var20 + -5 + var13 - -var11.width; - if (var25 < 5 + var13) { - var25 = 5 + var13; - } - - if (var4 < var20 + var25) { - var25 = -var20 + var4; - } - - Toolkit.getActiveToolkit().method934(var25, var26, var20, var21, 16777120); - Toolkit.getActiveToolkit().drawRect(var25, var26, var20, var21, 0, 255); - - var43 = var11.text; - var47 = 2 + (var26 - -var35.anInt3727); - - for (var43 = method1303(var11, var43); var43.length() > 0; var47 += var35.anInt3727 + 1) { - var28 = var43.indexOf(TextCore.aClass94_2598, 86); - if (var28 == -1) { - var44 = var43; - var43 = RSString.parse(""); - } else { - var44 = var43.substring(0, var28, 0); - var43 = var43.substring(4 + var28); - } - - var35.method681(var44, 3 + var25, var47, 0, -1); - } - } - - if (var11.type == 9) { - if (var11.aBoolean167) { - var20 = var13; - var22 = var13 - -var11.width; - var21 = var14 - -var11.height; - var23 = var14; - } else { - var20 = var13; - var21 = var14; - var23 = var14 - -var11.height; - var22 = var13 + var11.width; - } - - if (var11.anInt250 == 1) { - if (HDToolKit.highDetail) { - Class22.method933(var20, var21, var22, var23, var11.anInt218); - } else { - Class74.method1328(var20, var21, var22, var23, var11.anInt218); - } - } else if (HDToolKit.highDetail) { - Class22.method929(var20, var21, var22, var23, var11.anInt218, var11.anInt250); - } else { - Class74.method1322(var20, var21, var22, var23, var11.anInt218, var11.anInt250); - } - } - } - } - } - } - } - } - } - } - } - - if (var8 <= 31) { - quickChatMessages = null; - } - - } catch (RuntimeException var33) { - throw ClientErrorException.clientError(var33, "gn.B(" + var0 + ',' + var1 + ',' + var2 + ',' + (var3 != null ? "{...}" : "null") + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ',' + var8 + ',' + var9 + ')'); - } - } - - public static void method1098(byte var0) { - try { - if (anInt2309 < 128) { - anInt2309 = 128; - } - - if (anInt2309 > 383) { - anInt2309 = 383; - } - - GraphicDefinition.CAMERA_DIRECTION &= 2047; - if (var0 >= -31) { - quickChatMessages = null; - } - - int var1 = anInt3155 >> 7; - int var2 = anInt942 >> 7; - int var3 = Class121.method1736(WorldListCountry.localPlane, 1, anInt3155, anInt942); - int var4 = 0; - int var5; - if (var1 > 3 && 3 < var2 && 100 > var1 && var2 < 100) { - for (var5 = -4 + var1; var1 - -4 >= var5; ++var5) { - for (int var6 = -4 + var2; var6 <= 4 + var2; ++var6) { - int var7 = WorldListCountry.localPlane; - if (3 > var7 && 2 == (2 & aByteArrayArrayArray113[1][var5][var6])) { - ++var7; - } - - int var8 = (255 & aByteArrayArrayArray1774[var7][var5][var6]) * 8 - Class44.anIntArrayArrayArray723[var7][var5][var6] + var3; - if (var8 > var4) { - var4 = var8; - } - } - } - } - - var5 = 192 * var4; - if (var5 > 98048) { - var5 = 98048; - } - - if (var5 < 32768) { - var5 = 32768; - } - - if (var5 <= Class75_Sub4.anInt2670) { - if (Class75_Sub4.anInt2670 > var5) { - Class75_Sub4.anInt2670 += (var5 - Class75_Sub4.anInt2670) / 80; - } - } else { - Class75_Sub4.anInt2670 += (-Class75_Sub4.anInt2670 + var5) / 24; - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "gn.D(" + var0 + ')'); - } - } - - public static int method1100(int var0, int var2) { - try { - if (var2 == -1) { - return 12345678; - } else { - - var0 = var0 * (127 & var2) >> 7; - if (2 <= var0) { - if (126 < var0) { - var0 = 126; - } - } else { - var0 = 2; - } - - return var0 + ('\uff80' & var2); - } - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "gn.C(" + var0 + ',' + true + ',' + var2 + ')'); - } - } - - public static void method1516(int var0, int var1) { - try { - InterfaceWidget var2 = InterfaceWidget.getWidget(3, var0); - var2.a(); - if (var1 < 87) { - TextCore.aClass94_1326 = null; - } - - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "n.L(" + var0 + ',' + var1 + ')'); - } - } - - /** - * static final void method1517(byte var0) { - * try { - * Class139.aFontMetrics1822 = null; - * TextCore.Helvetica = null; - * if(var0 != -118) { - * method1516(64, 82); - * } - *

- * Class129_Sub1.anImage2695 = null; - * } catch (RuntimeException var2) { - * throw Class44.method1067(var2, "n.J(" + var0 + ')'); - * } - * } - **/ - - public static void method1517() { - try { - StartupLoadingBarInitial.aFontMetrics1822 = null; - - //MillisTimer.anImage2695 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "n.J(" + (byte) -118 + ')'); - } - } - - public static int bitwiseXOR(int var0, int var1) { - try { - return var0 ^ var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "n.E(" + var0 + ',' + var1 + ')'); - } - } - - public static void method1525(int var1, int var2, int var3, int var4, int var5) { - try { - int var6 = Class40.method1040(Class57.anInt902, var3, Class159.anInt2020); - int var7 = Class40.method1040(Class57.anInt902, var5, Class159.anInt2020); - int var8 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var4, Class101.anInt1425); - - int var9 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var2, Class101.anInt1425); - - for (int var10 = var6; var10 <= var7; ++var10) { - TextureOperation18.method282(Class38.anIntArrayArray663[var10], var8, 3074 + -2974, var9, var1); - } - - } catch (RuntimeException var11) { - throw ClientErrorException.clientError(var11, "n.I(" + 3074 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - public static SoftwareSprite method562(CacheIndex var0, int var2) { - try { - // System.out.println("Class 3_Sub28_Sub16_Sub2 " + var2); - return Class75_Sub4.method1351(var0, 0, var2) ? Class3_Sub28_Sub9.method578() : null; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "gf.O(" + (var0 != null ? "{...}" : "null") + ',' + 0 + ',' + var2 + ',' + (byte) 39 + ')'); - } - } - - public static void parsePreferences(Signlink var0) { - try { - anInt3625 = 3; - Class25.method957(true); - aBoolean3604 = true; - TextureOperation17.stereoSound = true; - Class128.aBoolean1685 = true; - Class3_Sub28_Sub9.anInt3622 = 0; - anInt3071 = 0; - KeyboardListener.aBoolean1905 = true; - WorldListEntry.aBoolean2623 = true; - RSInterface.aBoolean236 = true; - Sprites.ambientVolume = 127; - Class38.aBoolean661 = true; - Class140_Sub6.aBoolean2910 = true; - TextureOperation.anInt2378 = 0; - anInt1137 = 2; - aBoolean3275 = true; - Class106.aBoolean1441 = true; - AudioHandler.musicVolume = 255; - Class25.aBoolean488 = true; - anInt3671 = 0; - RandomAccessFileWrapper var2 = null; - AudioHandler.soundEffectVolume = 127; - if (Class3_Sub24_Sub3.maxClientMemory >= 96) { - Class127_Sub1.method1758(2); - } else { - Class127_Sub1.method1758(0); - } - - anInt2148 = 0; - Class3_Sub20.anInt2488 = 0; - AudioThread.aBoolean346 = false; - Class163_Sub3.aBoolean3004 = true; - aBoolean2146 = false; - aBoolean1080 = false; - anInt2577 = 0; - - try { - Class64 var3 = var0.method1433("runescape", 12); - - while (0 == var3.anInt978) { - TimeUtils.sleep(1L); - } - - if (var3.anInt978 == 1) { - var2 = (RandomAccessFileWrapper) var3.anObject974; - byte[] var4 = new byte[(int) var2.getLength()]; - - int var6; - for (int var5 = 0; var4.length > var5; var5 += var6) { - var6 = var2.read(var4, var5, var4.length - var5, 0); - if (var6 == -1) { - throw new IOException("EOF"); - } - } - - CSConfigCachefile.method1390(new DataBuffer(var4)); - } - } catch (Exception var8) { - } - - try { - if (var2 != null) { - var2.close(); - } - } catch (Exception var7) { - } - - } catch (RuntimeException var9) { - throw ClientErrorException.clientError(var9, "gf.F(" + (var0 != null ? "{...}" : "null") + ',' + 0 + ')'); - } - } - - public static void method565(int var1, int var2) { - try { - Texture.anInt1150 = -TextureOperation37.anInt3256 + var1; - - int var3 = -((int) ((float) Class3_Sub28_Sub3.aClass11_3551.width / Class44.aFloat727)) + Texture.anInt1150; - int var4 = Texture.anInt1150 + (int) ((float) Class3_Sub28_Sub3.aClass11_3551.width / Class44.aFloat727); - if (var3 < 0) { - Texture.anInt1150 = (int) ((float) Class3_Sub28_Sub3.aClass11_3551.width / Class44.aFloat727); - } - - TextureOperation13.anInt3362 = Class108.anInt1460 + -1 + anInt65 + -var2; - int var6 = (int) ((float) Class3_Sub28_Sub3.aClass11_3551.height / Class44.aFloat727) + TextureOperation13.anInt3362; - int var5 = TextureOperation13.anInt3362 - (int) ((float) Class3_Sub28_Sub3.aClass11_3551.height / Class44.aFloat727); - if (Class23.anInt455 < var4) { - Texture.anInt1150 = Class23.anInt455 + -((int) ((float) Class3_Sub28_Sub3.aClass11_3551.width / Class44.aFloat727)); - } - - if (var5 < 0) { - TextureOperation13.anInt3362 = (int) ((float) Class3_Sub28_Sub3.aClass11_3551.height / Class44.aFloat727); - } - - if (var6 > Class108.anInt1460) { - TextureOperation13.anInt3362 = -((int) ((float) Class3_Sub28_Sub3.aClass11_3551.height / Class44.aFloat727)) + Class108.anInt1460; - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "gf.E(" + (byte) 86 + ',' + var1 + ',' + var2 + ')'); - } - } - - public static void method566(RSString var0, int var2) { - try { - InterfaceWidget var3 = InterfaceWidget.getWidget(3, var2); - var3.flagUpdate(); - var3.text = var0; - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "gf.P(" + (var0 != null ? "{...}" : "null") + ',' + 0 + ',' + var2 + ')'); - } - } - - public static void method569(int var1) { - try { - InterfaceWidget var2 = InterfaceWidget.getWidget(7, var1); - var2.a(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "he.C(" + -82 + ',' + var1 + ')'); - } - } - - public static RSString method1723(byte var0, boolean var1, int var3) { - try { - int var4 = 1; - - for (int var5 = var3 / 10; 0 != var5; ++var4) { - var5 /= 10; - } - - int var6 = var4; - if (var3 < 0 || var1) { - var6 = var4 + 1; - } - - byte[] var7 = new byte[var6]; - if (var3 >= 0) { - if (var1) { - var7[0] = 43; - } - } else { - var7[0] = 45; - } - - for (int var8 = 0; var4 > var8; ++var8) { - int var9 = var3 % 10; - if (var9 < 0) { - var9 = -var9; - } - - var7[var6 + -var8 + -1] = (byte) (48 + var9); - var3 /= 10; - } - - if (var0 >= -111) { - method1727((byte) 53); - } - - RSString var11 = new RSString(); - var11.buffer = var7; - var11.length = var6; - return var11; - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "q.A(" + var0 + ',' + var1 + ',' + 10 + ',' + var3 + ')'); - } - } - - public static void method1724(int var0, int var1, int var2, int var3, int var4, int var5, byte var6, int var7) { - try { - if (var5 >= 128 && var2 >= 128 && var5 <= 13056 && var2 <= 13056) { - int var8 = Class121.method1736(WorldListCountry.localPlane, 1, var5, var2) + -var3; - var2 -= Class77.anInt1111; - var8 -= Class7.anInt2162; - var5 -= NPC.anInt3995; - int var9 = Class51.anIntArray840[Class139.anInt1823]; - int var10 = Class51.anIntArray851[Class139.anInt1823]; - int var11 = Class51.anIntArray840[TextureOperation28.anInt3315]; - int var12 = Class51.anIntArray851[TextureOperation28.anInt3315]; - int var13 = var5 * var12 + var11 * var2 >> 16; - var2 = var2 * var12 + -(var11 * var5) >> 16; - var5 = var13; - var13 = var10 * var8 - var2 * var9 >> 16; - if (var6 <= -47) { - var2 = var2 * var10 + var8 * var9 >> 16; - if (50 > var2) { - Texture.anInt2208 = -1; - Class32.anInt590 = -1; - } else if (HDToolKit.highDetail) { - int var14 = var1 * 512 >> 8; - Class32.anInt590 = var14 * var5 / var2 + var0; - int var15 = 512 * var7 >> 8; - Texture.anInt2208 = var15 * var13 / var2 + var4; - } else { - Class32.anInt590 = (var5 << 9) / var2 + var0; - Texture.anInt2208 = (var13 << 9) / var2 + var4; - } - - } - } else { - Texture.anInt2208 = -1; - Class32.anInt590 = -1; - } - } catch (RuntimeException var16) { - throw ClientErrorException.clientError(var16, "q.C(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ',' + var7 + ')'); - } - } - - public static void method1726(int var0) { - try { - TextCore.aClass94_1622 = null; - TextCore.aClass94_1617 = null; - - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "q.B(" + var0 + ')'); - } - } - - public static int method1727(byte var0) { - try { - if (var0 != 123) { - method1726(-123); - } - - return KeyboardListener.aReferenceCache_1911.hardCount(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "q.D(" + var0 + ')'); - } - } - - static int method1602(RSString var1) { - try { - if (Class119.aClass131_1624 == null || var1.length() == 0) { - return -1; - } else { - for (int var2 = 0; var2 < Class119.aClass131_1624.anInt1720; ++var2) { - if (Class119.aClass131_1624.aClass94Array1721[var2].method1560(RSString.parse(" "), TextCore.aClass94_4066).method1562((byte) -32, var1)) { - return var2; - } - } - - return -1; - } - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ni.G(" + 0 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void method338(int var1, boolean var2, int var3, int var4, int var5) { - try { - ++CSConfigCachefile.anInt1127; - Class124.method1745(); - if(!var2) { - Class3_Sub5.method116(true); - Class102.method1611(71, true); - Class3_Sub5.method116(false); - } - - Class102.method1611(100, false); - if(!var2) { - TextureOperation32.method302(2); - } - - method2067(); - if(HDToolKit.highDetail) { - Class65.method1239(var4, 125, var5, var1, var3, true); - var3 = Class163_Sub1.anInt2989; - var5 = Class3_Sub28_Sub3.anInt3564; - var4 = Class96.anInt1358; - var1 = anInt31; - } - - int var6; - int var7; - if(1 == Class133.anInt1753) { - var7 = 2047 & LinkableRSString.anInt2589 + GraphicDefinition.CAMERA_DIRECTION; - var6 = anInt2309; - if(Class75_Sub4.anInt2670 / 256 > var6) { - var6 = Class75_Sub4.anInt2670 / 256; - } - - if(WaterfallShader.aBooleanArray2169[4] && Class166.anIntArray2073[4] + 128 > var6) { - var6 = 128 + Class166.anIntArray2073[4]; - } - - Class140_Sub2.method1952(anInt3155, var1, Class121.method1736(WorldListCountry.localPlane, 1, Class102.player.xAxis, Class102.player.zAxis) + -50, Client.ZOOM - -(var6 * 3), var7, anInt942, var6); - } - - var7 = Class7.anInt2162; - var6 = NPC.anInt3995; - int var8 = Class77.anInt1111; - int var9 = Class139.anInt1823; - int var10 = TextureOperation28.anInt3315; - - int var11; - int var12; - for(var11 = 0; 5 > var11; ++var11) { - if(WaterfallShader.aBooleanArray2169[var11]) { - var12 = (int)((double)(-TextureOperation14.anIntArray3383[var11]) + (double)(TextureOperation14.anIntArray3383[var11] * 2 + 1) * Math.random() + Math.sin((double)Class163_Sub1_Sub1.anIntArray4009[var11] * ((double) TextureOperation3.anIntArray3359[var11] / 100.0D)) * (double)Class166.anIntArray2073[var11]); - if(var11 == 3) { - TextureOperation28.anInt3315 = var12 + TextureOperation28.anInt3315 & 0x7FF; - } - - if(var11 == 4) { - Class139.anInt1823 += var12; - if(128 > Class139.anInt1823) { - Class139.anInt1823 = 128; - } - - if(Class139.anInt1823 > 383) { - Class139.anInt1823 = 383; - } - } - - if(var11 == 2) { - Class77.anInt1111 += var12; - } - - if(var11 == 1) { - Class7.anInt2162 += var12; - } - - if(var11 == 0) { - NPC.anInt3995 += var12; - } - } - } - - Class3_Sub28_Sub20.method725(); - if(HDToolKit.highDetail) { - Class22.setClipping(var3, var5, var3 + var4, var5 - -var1); - float var17 = (float)Class139.anInt1823 * 0.17578125F; - float var16 = 0.17578125F * (float) TextureOperation28.anInt3315; - if(Class133.anInt1753 == 3) { - var17 = 360.0F * InterfaceWidget.aFloat1169 / 6.2831855F; - var16 = Class45.aFloat730 * 360.0F / 6.2831855F; - } - - HDToolKit.viewport(var3, var5, var4, var1, var4 / 2 + var3, var5 - -(var1 / 2), var17, var16, anInt1705, anInt1705); - } else { - Class74.setClipping(var3, var5, var4 + var3, var1 + var5); - Class51.method1134(); - } - - if(!Class38_Sub1.aBoolean2615 && var3 <= NPCDefinition.anInt1297 && var4 + var3 > NPCDefinition.anInt1297 && var5 <= Class38_Sub1.anInt2612 && Class38_Sub1.anInt2612 < var1 + var5) { - TextureOperation36.aBoolean3094 = true; - anInt59 = 0; - var12 = Class145.screenUpperX; - int var13 = Class1.screenUpperY; - var11 = Class139.screenLowerX; - anInt3642 = var11 + (var12 - var11) * (-var3 + NPCDefinition.anInt1297) / var4; - int var14 = AtmosphereParser.screenLowerY; - RenderAnimationDefinition.anInt384 = (-var13 + var14) * (Class38_Sub1.anInt2612 - var5) / var1 + var13; - } else { - TextureOperation36.aBoolean3094 = false; - anInt59 = 0; - } - - Class58.method1194(); - byte var19 = Class137.method1817() != 2 ?1:(byte) CSConfigCachefile.anInt1127; - if(HDToolKit.highDetail) { - HDToolKit.method1846(); - HDToolKit.method1831(true); - HDToolKit.method1827(true); - if(Class143.gameStage == 10) { - var12 = BufferedDataStream.method809(Class106.anInt1446, Class77.anInt1111 >> 10, anInt3625, NPC.anInt3995 >> 10); - } else { - var12 = BufferedDataStream.method809(Class106.anInt1446, Class102.player.anIntArray2755[0] >> 3, anInt3625, Class102.player.anIntArray2767[0] >> 3); - } - - Class68.method1269(Class44.anInt719, !WorldListEntry.aBoolean2623); - HDToolKit.clearScreen(var12); - ClientErrorException.method2285(Class139.anInt1823, Class77.anInt1111, Class7.anInt2162, NPC.anInt3995, TextureOperation28.anInt3315); - HDToolKit.anInt1791 = Class44.anInt719; - Class3_Sub22.method398(NPC.anInt3995, Class7.anInt2162, Class77.anInt1111, Class139.anInt1823, TextureOperation28.anInt3315, Class158.aByteArrayArrayArray2008, anIntArray686, Class129_Sub1.anIntArray2696, Class159.anIntArray2021, Player.anIntArray3959, SequenceDefinition.anIntArray1871, WorldListCountry.localPlane + 1, var19, Class102.player.xAxis >> 7, Class102.player.zAxis >> 7); - aBoolean47 = true; - Class68.method1265(); - ClientErrorException.method2285(0, 0, 0, 0, 0); - Class58.method1194(); - method1775(); - Texture.method1405(var5, var4, var3, anInt1705, var1, anInt1705, -7397); - Class163_Sub2_Sub1.method2221(var4, var3, var1, anInt1705, anInt1705, var5); - } else { - Toolkit.JAVA_TOOLKIT.method934(var3, var5, var4, var1, 0); - Class3_Sub22.method398(NPC.anInt3995, Class7.anInt2162, Class77.anInt1111, Class139.anInt1823, TextureOperation28.anInt3315, Class158.aByteArrayArrayArray2008, anIntArray686, Class129_Sub1.anIntArray2696, Class159.anIntArray2021, Player.anIntArray3959, SequenceDefinition.anIntArray1871, WorldListCountry.localPlane - -1, var19, Class102.player.xAxis >> 7, Class102.player.zAxis >> 7); - Class58.method1194(); - method1775(); - Texture.method1405(var5, var4, var3, 256, var1, 256, -6403 + -994); - Class163_Sub2_Sub1.method2221(var4, var3, var1, 256, 256, var5); - } - - ((Class102)Class51.anInterface2_838).method1610(Class106.anInt1446); - Class65.method1235(var4, var5, var1, var3); - Class139.anInt1823 = var9; - Class77.anInt1111 = var8; - Class7.anInt2162 = var7; - NPC.anInt3995 = var6; - TextureOperation28.anInt3315 = var10; - if(TextureOperation34.aBoolean3064 && Class58.aJs5Worker_917.countPriorityRequests() == 0) { - TextureOperation34.aBoolean3064 = false; - } - - if(TextureOperation34.aBoolean3064) { - Toolkit.getActiveToolkit().method934(var3, var5, var4, var1, 0); - LoadingBox.draw(false, TextCore.LoadingPleaseWait2); - } - - if(!var2 && !TextureOperation34.aBoolean3064 && !Class38_Sub1.aBoolean2615 && var3 <= NPCDefinition.anInt1297 && var4 + var3 > NPCDefinition.anInt1297 && Class38_Sub1.anInt2612 >= var5 && var1 + var5 > Class38_Sub1.anInt2612) { - TextureOperation36.method1628(var5, var4, var1, var3, Class38_Sub1.anInt2612, NPCDefinition.anInt1297); - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "ui.OA(" + -6403 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - - static boolean method609(RSInterface var0, int var1) { - if (null == var0.anIntArray275) { - return false; - } else { - int var2 = 0; - if (var1 <= 20) { - TextCore.COMMAND_MEMORY_MANAGEMENT = null; - } - - for (; var0.anIntArray275.length > var2; ++var2) { - int var3 = Class164_Sub2.method2247((byte) 119, var2, var0); - int var4 = var0.anIntArray307[var2]; - if (var0.anIntArray275[var2] != 2) { - if (var0.anIntArray275[var2] != 3) { - if (4 == var0.anIntArray275[var2]) { - if (var4 == var3) { - return false; - } - } else if (var3 != var4) { - return false; - } - } else if (var3 <= var4) { - return false; - } - } else if (var3 >= var4) { - return false; - } - } - - return true; - } - } - - public static RSInterface getRSInterface(int interfaceHash) { - try { - int windowId = interfaceHash >> 16; - - int componentId = 65535 & interfaceHash; - if (GameObject.aClass11ArrayArray1834.length <= windowId || windowId < 0) { - return null; - } - if (GameObject.aClass11ArrayArray1834[windowId] == null || GameObject.aClass11ArrayArray1834[windowId].length <= componentId || null == GameObject.aClass11ArrayArray1834[windowId][componentId]) { - boolean var4 = loadInterface(windowId); - if (!var4) { - return null; - } - } - if (GameObject.aClass11ArrayArray1834[windowId].length <= componentId) { - return null; - } - return GameObject.aClass11ArrayArray1834[windowId][componentId]; - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "af.F(" + interfaceHash + ')'); - } - } - - static void method551(int var1, int var2) { - try { - if(4 == var2 && !Class128.aBoolean1685) { - var2 = 2; - var1 = 2; - } - - if(var2 == Class23.anInt453) { - if(0 != var2 && var1 != TextureOperation37.anInt3263) { - anShaderInterfaceArray70[var2].method23(var1); - TextureOperation37.anInt3263 = var1; - } - } else { - if(TextureOperation23.aBoolean3207) { - return; - } - - if(Class23.anInt453 != 0) { - anShaderInterfaceArray70[Class23.anInt453].method21(); - } - - if(var2 != 0) { - ShaderInterface var3 = anShaderInterfaceArray70[var2]; - var3.method22(); - var3.method23(var1); - } - - Class23.anInt453 = var2; - TextureOperation37.anInt3263 = var1; - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "cb.A(" + 0 + ',' + var1 + ',' + var2 + ')'); - } - } - - static void method2275(int var0, int var2, int var3, int var4, int var5, int var6) { - try { - - int var8 = -var5 + var3; - MouseListeningClass.method2091(var3); - int var7 = 0; - if (var8 < 0) { - var8 = 0; - } - - int var9 = var3; - int var10 = -var3; - int var12 = -var8; - int var11 = var8; - int var13 = -1; - int var17; - int var16; - int var19; - int var18; - if (var2 >= Class159.anInt2020 && Class57.anInt902 >= var2) { - int[] var15 = Class38.anIntArrayArray663[var2]; - var16 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var3 + var6, Class101.anInt1425); - var17 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var3 + var6, Class101.anInt1425); - var18 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 + -var8, Class101.anInt1425); - var19 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 - -var8, Class101.anInt1425); - TextureOperation18.method282(var15, var16, 102, var18, var4); - TextureOperation18.method282(var15, var18, -44, var19, var0); - TextureOperation18.method282(var15, var19, -61, var17, var4); - } - - int var14 = -1; - - while (var9 > var7) { - var13 += 2; - var14 += 2; - var12 += var14; - var10 += var13; - if (var12 >= 0 && var11 >= 1) { - --var11; - GameObject.anIntArray1838[var11] = var7; - var12 -= var11 << 1; - } - - ++var7; - int var21; - int var20; - int[] var22; - int var24; - if (0 <= var10) { - --var9; - var10 -= var9 << 1; - var24 = var2 + -var9; - var16 = var2 + var9; - if (var16 >= Class159.anInt2020 && var24 <= Class57.anInt902) { - if (var8 <= var9) { - var17 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var7 + var6, Class101.anInt1425); - var18 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var7 + var6, Class101.anInt1425); - if (var16 <= Class57.anInt902) { - TextureOperation18.method282(Class38.anIntArrayArray663[var16], var18, -53, var17, var4); - } - - if (var24 >= Class159.anInt2020) { - TextureOperation18.method282(Class38.anIntArrayArray663[var24], var18, 96, var17, var4); - } - } else { - var17 = GameObject.anIntArray1838[var9]; - var18 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var7 + var6, Class101.anInt1425); - var19 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var7 + var6, Class101.anInt1425); - var20 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 - -var17, Class101.anInt1425); - var21 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, -var17 + var6, Class101.anInt1425); - if (Class57.anInt902 >= var16) { - var22 = Class38.anIntArrayArray663[var16]; - TextureOperation18.method282(var22, var19, 116, var21, var4); - TextureOperation18.method282(var22, var21, 125, var20, var0); - TextureOperation18.method282(var22, var20, 87, var18, var4); - } - - if (Class159.anInt2020 <= var24) { - var22 = Class38.anIntArrayArray663[var24]; - TextureOperation18.method282(var22, var19, 110, var21, var4); - TextureOperation18.method282(var22, var21, -114, var20, var0); - TextureOperation18.method282(var22, var20, -88, var18, var4); - } - } - } - } - - var24 = -var7 + var2; - var16 = var2 - -var7; - if (var16 >= Class159.anInt2020 && var24 <= Class57.anInt902) { - var17 = var6 + var9; - var18 = var6 + -var9; - if (var17 >= Class101.anInt1425 && var18 <= Class3_Sub28_Sub18.anInt3765) { - var17 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var17, Class101.anInt1425); - var18 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var18, Class101.anInt1425); - if (var7 < var8) { - var19 = var11 >= var7 ? var11 : GameObject.anIntArray1838[var7]; - var20 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var19 + var6, Class101.anInt1425); - var21 = Class40.method1040(Class3_Sub28_Sub18.anInt3765, var6 - var19, Class101.anInt1425); - if (Class57.anInt902 >= var16) { - var22 = Class38.anIntArrayArray663[var16]; - TextureOperation18.method282(var22, var18, 126, var21, var4); - TextureOperation18.method282(var22, var21, 103, var20, var0); - TextureOperation18.method282(var22, var20, -61, var17, var4); - } - - if (var24 >= Class159.anInt2020) { - var22 = Class38.anIntArrayArray663[var24]; - TextureOperation18.method282(var22, var18, 102, var21, var4); - TextureOperation18.method282(var22, var21, -94, var20, var0); - TextureOperation18.method282(var22, var20, 99, var17, var4); - } - } else { - if (var16 <= Class57.anInt902) { - TextureOperation18.method282(Class38.anIntArrayArray663[var16], var18, 94, var17, var4); - } - - if (var24 >= Class159.anInt2020) { - TextureOperation18.method282(Class38.anIntArrayArray663[var24], var18, 126, var17, var4); - } - } - } - } - } - - } catch (RuntimeException var23) { - throw ClientErrorException.clientError(var23, "wl.I(" + var0 + ',' + (byte) 109 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ',' + var6 + ')'); - } - } - - static void method2270(Class140_Sub4 var0) { - try { - if (Class44.anInt719 == var0.anInt2790 || var0.anInt2771 == -1 || var0.anInt2828 != 0 || SequenceDefinition.getAnimationDefinition(var0.anInt2771).duration[var0.anInt2832] < 1 + var0.anInt2760) { - int var2 = var0.anInt2790 + -var0.anInt2800; - int var3 = Class44.anInt719 + -var0.anInt2800; - int var4 = var0.anInt2784 * 128 + 64 * var0.getSize(); - int var5 = var0.anInt2835 * 128 - -(var0.getSize() * 64); - int var6 = 128 * var0.anInt2823 + var0.getSize() * 64; - int var7 = 128 * var0.anInt2798 + var0.getSize() * 64; - var0.xAxis = (var3 * var6 + var4 * (var2 - var3)) / var2; - var0.zAxis = (var7 * var3 + var5 * (var2 - var3)) / var2; - } - - var0.anInt2824 = 0; - if (var0.anInt2840 == 0) { - var0.anInt2806 = 1024; - } - - if (1 == var0.anInt2840) { - var0.anInt2806 = 1536; - } - - if (var0.anInt2840 == 2) { - var0.anInt2806 = 0; - } - - if (var0.anInt2840 == 3) { - var0.anInt2806 = 512; - } - - var0.anInt2785 = var0.anInt2806; - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "wl.K(" + (var0 != null ? "{...}" : "null") + ',' + (byte) -56 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/VarpHelpers.java b/Client/src/main/java/org/runite/client/VarpHelpers.java deleted file mode 100644 index 6fdfa25e1..000000000 --- a/Client/src/main/java/org/runite/client/VarpHelpers.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.runite.client; - -import org.rs09.CustomVars; -import org.rs09.SystemLogger; -import org.rs09.client.data.ReferenceCache; - -import java.util.Objects; - -public class VarpHelpers { - - static ReferenceCache varbitLookup = new ReferenceCache(64); - - static void setVarbit(byte var0, int valueToSet, int varbitID) { - try { - if (var0 >= -99) { - setVarbit((byte) 57, -14, 120); - } - - CSConfigCachefile cacheFile = CSConfigCachefile.getCSConfigFileFromVarbitID(varbitID); - int parentVarp = Objects.requireNonNull(cacheFile).parentVarpIndex; - int upperBound = cacheFile.upperBound; - int lowerBound = cacheFile.lowerBound; - int varbitSize = upperBound - lowerBound; - SystemLogger.logInfo(parentVarp + " - bitStart: " + lowerBound + " bitEnd: " + upperBound + " bitSize = " + (varbitSize + 1)); - int expectedMinimumValue = Class3_Sub6.expectedMinimumValues[varbitSize]; - SystemLogger.logInfo("emv: " + expectedMinimumValue + " || vs: " + (127 + valueToSet)); - if (valueToSet < 0|| expectedMinimumValue < valueToSet) { - SystemLogger.logInfo(expectedMinimumValue + " < " + valueToSet); - valueToSet = 0; - } - - expectedMinimumValue <<= lowerBound; - int modifiedVarpValue = valueToSet << lowerBound & expectedMinimumValue | ~expectedMinimumValue & Class57.varpArray[parentVarp]; - setVarp(modifiedVarpValue, parentVarp); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "wd.K(" + var0 + ',' + valueToSet + ',' + varbitID + ')'); - } - } - - static void setVarp(int valueToSet, int varpIndex) { - try { - Class57.varpArray[varpIndex] = valueToSet; - if(CustomVars.parse(varpIndex,valueToSet)){ - return; - } - Class3_Sub7 var3 = (Class3_Sub7) AtmosphereParser.aHashTable_3679.get(varpIndex); - if(null == var3) { - var3 = new Class3_Sub7(4611686018427387905L); - AtmosphereParser.aHashTable_3679.put(varpIndex, var3); - } else if (var3.aLong2295 != 4611686018427387905L) { - var3.aLong2295 = TimeUtils.time() + 500L | 4611686018427387904L; - } - - } catch (RuntimeException var4) { - throw ClientErrorException.clientError(var4, "nh.W(" + (byte) 99 + ',' + valueToSet + ',' + varpIndex + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/WaterMovementShader.java b/Client/src/main/java/org/runite/client/WaterMovementShader.java deleted file mode 100644 index 478ce3682..000000000 --- a/Client/src/main/java/org/runite/client/WaterMovementShader.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.runite.client; - - - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -final class WaterMovementShader implements ShaderInterface { - - private int anInt2177 = -1; - private static final float[] color = new float[]{0.1F, 0.1F, 0.15F, 0.1F}; - private final float[] aFloatArray2179 = new float[4]; - private int textureId = -1; - private int anInt2181 = -1; - - - private void method1699() { - byte[] var1 = new byte[]{(byte)0, (byte)-1}; - GL var2 = HDToolKit.gl; - int[] var3 = new int[1]; - var2.glGenTextures(1, var3, 0); - var2.glBindTexture(3552, var3[0]); - var2.glTexImage1D(3552, 0, 6406, 2, 0, 6406, 5121, ByteBuffer.wrap(var1)); - var2.glTexParameteri(3552, 10241, 9729); - var2.glTexParameteri(3552, 10240, 9729); - var2.glTexParameteri(3552, 10242, '\u812f'); - this.textureId = var3[0]; - } - - private void method1701() { - GL var1 = HDToolKit.gl; - this.anInt2177 = var1.glGenLists(2); - var1.glNewList(this.anInt2177, 4864); - var1.glTexEnvi(8960, '\u8590', 768); - var1.glTexEnvi(8960, '\u8581', '\u8576'); - var1.glTexEnvf(8960, '\u8573', 2.0F); - var1.glTexEnvi(8960, '\u8589', '\u8576'); - var1.glTexGeni(8192, 9472, 9217); - var1.glTexGeni(8193, 9472, 9217); - var1.glTexGenfv(8192, 9473, new float[]{9.765625E-4F, 0.0F, 0.0F, 0.0F}, 0); - var1.glTexGenfv(8193, 9473, new float[]{0.0F, 0.0F, 9.765625E-4F, 0.0F}, 0); - var1.glEnable(3168); - var1.glEnable(3169); - if(Class88.Texture3DEnabled) { - var1.glBindTexture('\u806f', Class88.anInt1228); - var1.glTexGeni(8194, 9472, 9217); - var1.glTexGeni(8195, 9472, 9217); - var1.glTexGenfv(8195, 9473, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, 0); - var1.glEnable(3170); - var1.glEnable(3171); - var1.glEnable('\u806f'); - } - - var1.glActiveTexture('\u84c1'); - var1.glEnable(3552); - var1.glBindTexture(3552, this.textureId); - var1.glTexEnvi(8960, '\u8571', '\u8575'); - var1.glTexEnvi(8960, '\u8580', '\u8576'); - var1.glTexEnvi(8960, '\u8582', 5890); - var1.glTexEnvi(8960, '\u8572', '\u8575'); - var1.glTexEnvi(8960, '\u8588', '\u8576'); - var1.glTexEnvi(8960, '\u858a', 5890); - var1.glEnable(3168); - var1.glTexGeni(8192, 9472, 9216); - var1.glPushMatrix(); - var1.glLoadIdentity(); - var1.glEndList(); - var1.glNewList(this.anInt2177 + 1, 4864); - var1.glActiveTexture('\u84c1'); - var1.glDisable(3552); - var1.glDisable(3168); - var1.glTexEnvi(8960, '\u8571', 8448); - var1.glTexEnvi(8960, '\u8580', 5890); - var1.glTexEnvi(8960, '\u8582', '\u8576'); - var1.glTexEnvi(8960, '\u8572', 8448); - var1.glTexEnvi(8960, '\u8588', 5890); - var1.glTexEnvi(8960, '\u858a', '\u8576'); - var1.glActiveTexture('\u84c0'); - var1.glTexEnvi(8960, '\u8590', 768); - var1.glTexEnvi(8960, '\u8581', '\u8578'); - var1.glTexEnvf(8960, '\u8573', 1.0F); - var1.glTexEnvi(8960, '\u8589', '\u8578'); - var1.glDisable(3168); - var1.glDisable(3169); - if(Class88.Texture3DEnabled) { - var1.glDisable(3170); - var1.glDisable(3171); - var1.glDisable('\u806f'); - } - - var1.glEndList(); - } - - public final void method21() { - HDToolKit.gl.glCallList(this.anInt2177 + 1); - } - - public final void method23(int var1) { - GL var2 = HDToolKit.gl; - var2.glActiveTexture('\u84c1'); - var2.glTexEnvfv(8960, 8705, Unsorted.aFloatArray1934, 0); - var2.glActiveTexture('\u84c0'); - if((var1 & 1) == 1) { - if(Class88.Texture3DEnabled) { - if(this.anInt2181 != HDToolKit.anInt1791) { - this.aFloatArray2179[0] = 0.0F; - this.aFloatArray2179[1] = 0.0F; - this.aFloatArray2179[2] = 0.0F; - this.aFloatArray2179[3] = (float)HDToolKit.anInt1791 * 0.0050F;//Water moving speed? - var2.glTexGenfv(8194, 9473, this.aFloatArray2179, 0);//R, OBJECT_PLANE - this.anInt2181 = HDToolKit.anInt1791; - } - } else { - HDToolKit.bindTexture2D(Class88.anIntArray1224[HDToolKit.anInt1791 * 64 / 100 % 64]); - } - } else if(Class88.Texture3DEnabled) { - this.aFloatArray2179[0] = 0.0F; - this.aFloatArray2179[1] = 0.0F; - this.aFloatArray2179[2] = 0.0F; - this.aFloatArray2179[3] = 0.0F; - var2.glTexGenfv(8194, 9473, this.aFloatArray2179, 0); - } else { - HDToolKit.bindTexture2D(Class88.anIntArray1224[0]); - } - - } - - public final void method22() { - GL var1 = HDToolKit.gl; - HDToolKit.method1856(2); - HDToolKit.method1847(2); - HDToolKit.method1823(); - var1.glCallList(this.anInt2177); - float var2 = 2662.4001F; - var2 += (float)(Class140_Sub7.anInt2938 - 128) * 0.5F; - if(var2 >= 3328.0F) { - var2 = 3327.0F; - } - - this.aFloatArray2179[0] = 0.0F; - this.aFloatArray2179[1] = 0.0F; - this.aFloatArray2179[2] = 1.0F / (var2 - 3328.0F); - this.aFloatArray2179[3] = var2 / (var2 - 3328.0F); - var1.glTexGenfv(8192, 9474, this.aFloatArray2179, 0);//GL_ENABLE_BIT, GL_EYE_PLANE - var1.glPopMatrix(); - var1.glActiveTexture('\u84c0');//GL_TEXTURE0 - var1.glTexEnvfv(8960, 8705, color, 0);//GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR - } - - public final int method24() { - return 15; - } - - public WaterMovementShader() { - this.method1699(); - this.method1701(); - } - -} diff --git a/Client/src/main/java/org/runite/client/WaterShader.java b/Client/src/main/java/org/runite/client/WaterShader.java deleted file mode 100644 index f6afe8bd2..000000000 --- a/Client/src/main/java/org/runite/client/WaterShader.java +++ /dev/null @@ -1,192 +0,0 @@ -package org.runite.client; - - - - -import javax.media.opengl.GL; -import java.nio.ByteBuffer; - - -final class WaterShader implements ShaderInterface { - - static int anInt3285 = 128; - private final float[] aFloatArray2190 = new float[4]; - private static boolean aBoolean2191 = false; - private int anInt2192 = -1; - private int anInt2193 = -1; - - - public WaterShader() { - if(HDToolKit.maxTextureUnits >= 2) { - int[] textures = new int[1]; - byte[] pixels = new byte[8]; - int pixelsPos = 0; - while (pixelsPos < 8) { - pixels[pixelsPos] = (byte) (96 + ++pixelsPos * 159 / 8); - } -// for(int var3 = 0; var3 < 8; pixels[var3++] = (byte)(96 + var3 * 159 / 8)) { -// } - GL var4 = HDToolKit.gl; - var4.glGenTextures(1, textures, 0); - var4.glBindTexture(3552, textures[0]);//TEXTURE_1D - // TEXTURE_1D, level0, ALPHA, width8, border0, ALPHA, UNSIGNED_BYTE, pixels. - var4.glTexImage1D(3552, 0, 6406, 8, 0, 6406, 5121, ByteBuffer.wrap(pixels)); - var4.glTexParameteri(3552, 10241, 9729);//TEXTURE_1D, TEXTURE_MIN_FILTER, LINEAR - var4.glTexParameteri(3552, 10240, 9729);//TEXTURE_1D, TEXTURE_MAG_FILTER, LINEAR - var4.glTexParameteri(3552, 10242, '\u812f');//TEXTURE_1D, TEXTURE_WRAP_S, CLAMP_TO_EDGE - this.anInt2192 = textures[0]; - aBoolean2191 = HDToolKit.maxTextureUnits > 2 && HDToolKit.allows3DTextureMapping; - this.method2251(); - } - - } - - - private void method2251() { - GL var1 = HDToolKit.gl; - this.anInt2193 = var1.glGenLists(2); - var1.glNewList(this.anInt2193, 4864); - var1.glActiveTexture('\u84c1'); - if(aBoolean2191) { - var1.glBindTexture('\u806f', Class88.anInt1228); - var1.glTexEnvi(8960, '\u8571', 260); - var1.glTexEnvi(8960, '\u8590', 768); - var1.glTexEnvi(8960, '\u8572', 7681); - var1.glTexEnvi(8960, '\u8588', '\u8578'); - var1.glTexGeni(8192, 9472, 9216); - var1.glTexGeni(8194, 9472, 9216); - var1.glTexGeni(8193, 9472, 9216); - var1.glTexGeni(8195, 9472, 9217); - var1.glTexGenfv(8195, 9473, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, 0); - var1.glEnable(3168); - var1.glEnable(3169); - var1.glEnable(3170); - var1.glEnable(3171); - var1.glEnable('\u806f'); - var1.glActiveTexture('\u84c2'); - var1.glTexEnvi(8960, 8704, '\u8570'); - } - - var1.glBindTexture(3552, this.anInt2192); - var1.glTexEnvi(8960, '\u8571', '\u8575'); - var1.glTexEnvi(8960, '\u8580', '\u8576'); - var1.glTexEnvi(8960, '\u8582', 5890); - var1.glTexEnvi(8960, '\u8572', 7681); - var1.glTexEnvi(8960, '\u8588', '\u8578'); - var1.glTexGeni(8192, 9472, 9216); - var1.glEnable(3552); - var1.glEnable(3168); - var1.glActiveTexture('\u84c0'); - var1.glEndList(); - var1.glNewList(this.anInt2193 + 1, 4864); - var1.glActiveTexture('\u84c1'); - if(aBoolean2191) { - var1.glTexEnvi(8960, '\u8571', 8448); - var1.glTexEnvi(8960, '\u8590', 768); - var1.glTexEnvi(8960, '\u8572', 8448); - var1.glTexEnvi(8960, '\u8588', 5890); - var1.glDisable(3168); - var1.glDisable(3169); - var1.glDisable(3170); - var1.glDisable(3171); - var1.glDisable('\u806f'); - var1.glActiveTexture('\u84c2'); - var1.glTexEnvi(8960, 8704, 8448); - } - - var1.glTexEnvfv(8960, 8705, new float[]{0.0F, 1.0F, 0.0F, 1.0F}, 0); - var1.glTexEnvi(8960, '\u8571', 8448); - var1.glTexEnvi(8960, '\u8580', 5890); - var1.glTexEnvi(8960, '\u8582', '\u8576'); - var1.glTexEnvi(8960, '\u8572', 8448); - var1.glTexEnvi(8960, '\u8588', 5890); - var1.glDisable(3552); - var1.glDisable(3168); - var1.glActiveTexture('\u84c0'); - var1.glEndList(); - } - - static int method2252() { - return aBoolean2191?'\u84c2':'\u84c1'; - } - - static void method2253() { - GL var0 = HDToolKit.gl; - var0.glClientActiveTexture(method2252()); - var0.glDisableClientState('\u8078'); - var0.glClientActiveTexture('\u84c0'); - } - - public final void method22() { - GL var1 = HDToolKit.gl; - var1.glCallList(this.anInt2193); - } - - static void method2254() { - GL var0 = HDToolKit.gl; - var0.glClientActiveTexture(method2252()); - var0.glEnableClientState('\u8078'); - var0.glClientActiveTexture('\u84c0'); - } - - public final int method24() { - return 0; - } - - public final void method21() { - GL var1 = HDToolKit.gl; - var1.glCallList(this.anInt2193 + 1); - } - - public final void method23(int var1) { - GL var2 = HDToolKit.gl; - var2.glActiveTexture('\u84c1'); - if(!aBoolean2191 && var1 < 0) { - var2.glDisable(3168); - } else { - var2.glPushMatrix(); - var2.glLoadIdentity(); - var2.glRotatef(180.0F, 1.0F, 0.0F, 0.0F); - var2.glRotatef((float)Class140_Sub7.anInt2938 * 360.0F / 2048.0F, 1.0F, 0.0F, 0.0F); - var2.glRotatef((float) TextureOperation9.anInt3103 * 360.0F / 2048.0F, 0.0F, 1.0F, 0.0F); - var2.glTranslatef((float)(-Unsorted.anInt144), (float)(-Unsorted.anInt3695), (float)(-LinkableRSString.anInt2587)); - if(aBoolean2191) { - this.aFloatArray2190[0] = 0.0010F; - this.aFloatArray2190[1] = 9.0E-4F; - this.aFloatArray2190[2] = 0.0F; - this.aFloatArray2190[3] = 0.0F; - var2.glTexGenfv(8192, 9474, this.aFloatArray2190, 0); - this.aFloatArray2190[0] = 0.0F; - this.aFloatArray2190[1] = 9.0E-4F; - this.aFloatArray2190[2] = 0.0010F; - this.aFloatArray2190[3] = 0.0F; - var2.glTexGenfv(8193, 9474, this.aFloatArray2190, 0); - this.aFloatArray2190[0] = 0.0F; - this.aFloatArray2190[1] = 0.0F; - this.aFloatArray2190[2] = 0.0F; - this.aFloatArray2190[3] = (float)HDToolKit.anInt1791 * 0.0050F; - var2.glTexGenfv(8194, 9474, this.aFloatArray2190, 0); - var2.glActiveTexture('\u84c2'); - } - - var2.glTexEnvfv(8960, 8705, Class72.method1297(), 0); - if(var1 >= 0) { - this.aFloatArray2190[0] = 0.0F; - this.aFloatArray2190[1] = 1.0F / (float) anInt3285; - this.aFloatArray2190[2] = 0.0F; - this.aFloatArray2190[3] = 1.0F * (float)var1 / (float) anInt3285; - var2.glTexGenfv(8192, 9474, this.aFloatArray2190, 0); - var2.glEnable(3168); - } else { - var2.glDisable(3168); - } - - var2.glPopMatrix(); - } - - var2.glActiveTexture('\u84c0'); - } - - - -} diff --git a/Client/src/main/java/org/runite/client/WaterfallShader.java b/Client/src/main/java/org/runite/client/WaterfallShader.java deleted file mode 100644 index 69844448d..000000000 --- a/Client/src/main/java/org/runite/client/WaterfallShader.java +++ /dev/null @@ -1,234 +0,0 @@ -package org.runite.client; - - -import org.rs09.client.config.GameConfig; - - -import javax.media.opengl.GL; -import java.util.Objects; - -final class WaterfallShader implements ShaderInterface { - - static boolean[] aBooleanArray2169 = new boolean[5]; - static CacheIndex aClass153_2172; - static int waterfallTextureId = -1; - static RSString aClass94_8 = RSString.parse(""); - private int listId; - private final float[] aFloatArray2174 = new float[4]; - private static RSString aClass94_2175 = RSString.parse(")4a="); - - - static void method1626(byte var0) { - try { - QuickChatDefinition.aReferenceCache_3572.clear(); - Class143.aReferenceCache_1874.clear(); - if(var0 <= -124) { - Class67.aReferenceCache_1013.clear(); - } - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ob.F(" + var0 + ')'); - } - } - - static boolean method1627(int var0, byte var1) { - try { - WorldListEntry var2 = Class3_Sub8.getWorld(97, var0); - if(var2 == null) { - return false; - } else if (Signlink.anInt1214 != 1 && Signlink.anInt1214 != 2 && Class44.paramModeWhere != 2) { - RSString var9 = aClass94_8; - if (Class44.paramModeWhere != 0) { - var9 = RSString.stringCombiner(new RSString[]{TextCore.aClass94_4007, RSString.stringAnimator(var2.worldId - -7000)}); - } - - if (var1 > -2) { - return false; - } else { - RSString var4 = aClass94_8; - if (Class163_Sub2.paramSettings != null) { - var4 = RSString.stringCombiner(new RSString[]{Class97.aClass94_1380, Class163_Sub2.paramSettings}); - } - RSString var5 = RSString.stringCombiner(new RSString[]{RSString.parse("http:)4)4"), var2.address, var9, TextCore.aClass94_2608, RSString.stringAnimator(Class3_Sub20.paramLanguage), aClass94_2175, RSString.stringAnimator(Class3_Sub26.paramAffid), var4, TextCore.aClass94_1133, !Unsorted.paramJavaScriptEnabled ? TextCore.aClass94_3013 : TextCore.aClass94_339, TextCore.aClass94_2610, !Class163_Sub2_Sub1.paramObjectTagEnabled ? TextCore.aClass94_3013 : TextCore.aClass94_339, TextCore.aClass94_1617, Client.paramAdvertisementSuppressed ? TextCore.aClass94_339 : TextCore.aClass94_3013}); - - try { - Objects.requireNonNull(Client.clientInstance.getAppletContext()).showDocument(var5.toURL(), "_self"); - } catch (Exception var7) { - return false; - } - - return true; - } - } else { - GameConfig.IP_ADDRESS = var2.address.toString(); - System.out.println(GameConfig.IP_ADDRESS); -// GameLaunch.SETTINGS.setIp(var2.address.toString()); - var2.address.method1568(); - Class38_Sub1.accRegistryIp = GameConfig.IP_MANAGEMENT; - CS2Script.userCurrentWorldID = var2.worldId; - if (Class44.paramModeWhere != 0) { - Class162.anInt2036 = '\u9c40' + CS2Script.userCurrentWorldID; - Class140_Sub6.accRegistryPort = Class162.anInt2036; - Client.currentPort = CS2Script.userCurrentWorldID + '\uc350'; - } - - return true; - } - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ob.E(" + var0 + ',' + var1 + ')'); - } - } - - public final int method24() { - try { - return 0; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ob.C()"); - } - } - - public final void method23(int var1) { - try { - GL var2 = HDToolKit.gl; - float var4 = (float)(1 + (var1 >> 3 & 3)) * 0.01F; - float var3 = -0.01F * (float)(1 + (var1 & 3)); - float var5 = 0 == (var1 & 64)?4.8828125E-4F:9.765625E-4F; - boolean var6 = (128 & var1) != 0; - if(var6) { - this.aFloatArray2174[0] = var5; - this.aFloatArray2174[1] = 0.0F; - this.aFloatArray2174[2] = 0.0F; - this.aFloatArray2174[3] = 0.0F; - } else { - this.aFloatArray2174[2] = var5; - this.aFloatArray2174[1] = 0.0F; - this.aFloatArray2174[3] = 0.0F; - this.aFloatArray2174[0] = 0.0F; - } - - var2.glActiveTexture('\u84c1'); - var2.glMatrixMode(5888); - var2.glPushMatrix(); - var2.glLoadIdentity(); - var2.glRotatef(180.0F, 1.0F, 0.0F, 0.0F); - var2.glRotatef((float)Class140_Sub7.anInt2938 * 360.0F / 2048.0F, 1.0F, 0.0F, 0.0F); - var2.glRotatef(360.0F * (float) TextureOperation9.anInt3103 / 2048.0F, 0.0F, 1.0F, 0.0F); - var2.glTranslatef((float)(-Unsorted.anInt144), (float)(-Unsorted.anInt3695), (float)(-LinkableRSString.anInt2587)); - var2.glTexGenfv(8192, 9474, this.aFloatArray2174, 0); - this.aFloatArray2174[3] = var3 * (float)HDToolKit.anInt1791; - this.aFloatArray2174[0] = 0.0F; - this.aFloatArray2174[2] = 0.0F; - this.aFloatArray2174[1] = var5; - var2.glTexGenfv(8193, 9474, this.aFloatArray2174, 0); - var2.glPopMatrix(); - if(Class88.Texture3DEnabled) { - this.aFloatArray2174[3] = (float)HDToolKit.anInt1791 * var4; - this.aFloatArray2174[1] = 0.0F; - this.aFloatArray2174[0] = 0.0F; - this.aFloatArray2174[2] = 0.0F; - var2.glTexGenfv(8194, 9473, this.aFloatArray2174, 0); - } else { - int var7 = (int)((float)HDToolKit.anInt1791 * var4 * 64.0F); - var2.glBindTexture(3553, Class88.anIntArray1223[var7 % 64]); - } - - var2.glActiveTexture('\u84c0'); - } catch (RuntimeException var8) { - throw ClientErrorException.clientError(var8, "ob.B(" + var1 + ')'); - } - } - - public final void method21() { - try { - GL var1 = HDToolKit.gl; - var1.glCallList(1 + this.listId); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ob.A()"); - } - } - - public final void method22() { - try { - GL var1 = HDToolKit.gl; - var1.glCallList(this.listId); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ob.D()"); - } - } - - public static void method1630(byte var0) { - try { - TextCore.aClass94_2171 = null; - aClass153_2172 = null; - aBooleanArray2169 = null; - if(var0 > -112) { - method1632(-116, 108, 54, -120, 44, 6); - } - aClass94_2175 = null; - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ob.J(" + var0 + ')'); - } - } - - private void method1631() { - try { - GL var2 = HDToolKit.gl; - this.listId = var2.glGenLists(2); - var2.glNewList(this.listId, 4864);//COMPILE - var2.glActiveTexture('\u84c1');//TEXTURE1 - if(Class88.Texture3DEnabled) { - var2.glBindTexture('\u806f', waterfallTextureId);//TEXTURE_3D - var2.glTexGeni(8194, 9472, 9217);//R, TEXTURE_GEN_MODE, OBJECT_LINEAR - var2.glEnable(3170);//TEXTURE_GEN_R - var2.glEnable('\u806f');//TEXTURE_3D - } else { - var2.glEnable(3553);//TEXTURE_2D - } - - var2.glTexGeni(8192, 9472, 9216);//S, TEXTURE_GEN_MODE, EYE_LINEAR - var2.glTexGeni(8193, 9472, 9216);//T, TEXTURE_GEN_MODE, EYE_LINEAR - var2.glEnable(3168);//TEXTURE_GEN_S - var2.glEnable(3169);//TEXTURE_GEN_T - var2.glActiveTexture('\u84c0');//TEXTURE1 - var2.glEndList(); - var2.glNewList(this.listId + 1, 4864);//COMPILE - var2.glActiveTexture('\u84c1');//TEXTURE1 - if(Class88.Texture3DEnabled) { - var2.glDisable('\u806f');//TEXTURE_3D - var2.glDisable(3170);//TEXTURE_GEN_R - } else { - var2.glDisable(3553);//TEXTURE_2D - } - - var2.glDisable(3168);//TEXTURE_GEN_S - var2.glDisable(3169);//TEXTURE_GEN_T - var2.glActiveTexture('\u84c0');//TEXTURE0 - var2.glEndList(); - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ob.I(" + 2 + ')'); - } - } - - public WaterfallShader() { - try { - this.method1631(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ob.()"); - } - } - - static void method1632(int var0, int var1, int var2, int var3, int var4, int var5) { - try { - if(var0 <= 66) { - method1630((byte)-33); - } - - for(int var6 = var3; var6 <= var1; ++var6) { - TextureOperation18.method282(Class38.anIntArrayArray663[var6], var4, 121, var2, var5); - } - - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "ob.G(" + var0 + ',' + var1 + ',' + var2 + ',' + var3 + ',' + var4 + ',' + var5 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/WorldListCountry.java b/Client/src/main/java/org/runite/client/WorldListCountry.java deleted file mode 100644 index 669d31d9b..000000000 --- a/Client/src/main/java/org/runite/client/WorldListCountry.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.runite.client; - -public final class WorldListCountry { - - public static int localPlane; - int flagId; - RSString name; -} diff --git a/Client/src/main/java/org/runite/client/WorldListEntry.java b/Client/src/main/java/org/runite/client/WorldListEntry.java deleted file mode 100644 index 9b2ae8910..000000000 --- a/Client/src/main/java/org/runite/client/WorldListEntry.java +++ /dev/null @@ -1,226 +0,0 @@ -package org.runite.client; - -import org.rs09.SystemLogger; -import org.rs09.client.config.GameConfig; -import org.rs09.client.rendering.Toolkit; - -import java.util.Objects; - -public final class WorldListEntry extends Class44 { - - public static int anInt2937; - public static WorldListEntry[] worldList; - public static int activeWorldListSize; - public static int updateStamp; - static WorldListCountry[] countries; - static AbstractSprite aAbstractSprite_3099; - static int anInt3351; - static AbstractSprite aAbstractSprite_1339; - static AbstractSprite aAbstractSprite_1457; - static int anInt1400; - static int anInt739; - static int anInt1126; - static int archiveID; - static int worldListArraySize; - public RSString activity; - public int worldId; - static boolean aBoolean2623 = true; - public RSString address; - static int anInt2626 = 20; - static AudioChannel aAudioChannel_2627; - - - static void method1076() { - try { - Class154.aReferenceCache_1964.clear(); - } catch (RuntimeException var2) { - throw ClientErrorException.clientError(var2, "ba.C(" + 88 + ')'); - } - } - - static void parseWorldList(DataBuffer buffer) { - try { - int var2 = buffer.getSmart(); - countries = new WorldListCountry[var2]; - - int var3; - for (var3 = 0; var3 < var2; ++var3) { - countries[var3] = new WorldListCountry(); - countries[var3].flagId = buffer.getSmart(); - countries[var3].name = buffer.getGJString2(105); - } - - Class53.worldListOffset = buffer.getSmart(); - worldListArraySize = buffer.getSmart(); - activeWorldListSize = buffer.getSmart(); - worldList = new WorldListEntry[-Class53.worldListOffset + worldListArraySize + 1]; - - for (var3 = 0; var3 < activeWorldListSize; ++var3) { - int worldId = buffer.getSmart(); - WorldListEntry var5 = worldList[worldId] = new WorldListEntry(); - var5.countryIndex = buffer.readUnsignedByte(); - var5.settings = buffer.readInt(); - var5.worldId = worldId - -Class53.worldListOffset; - var5.activity = buffer.getGJString2(98); - var5.address = buffer.getGJString2(79); - GameConfig.WORLD = worldId; -// GameLaunch.SETTINGS.setWorld(worldId); - SystemLogger.logInfo("Registering to world: " + GameConfig.WORLD); - } - updateStamp = buffer.readInt(); - Class30.loadedWorldList = true; - } catch (RuntimeException var6) { - throw ClientErrorException.clientError(var6, "hi.B(" + (buffer != null ? "{...}" : "null") + ',' + -88 + ')'); - } - } - - static void buildWorldListInterface() { - try { - int var1 = Class21.anInt1462; - int var2 = Class21.anInt3395; - int var4 = Class21.anInt3537; - int var3 = Class21.anInt3552; - if (aAbstractSprite_3099 == null || null == aAbstractSprite_1457) { - if (CacheIndex.spritesIndex.retrieveSpriteFile(archiveID) && CacheIndex.spritesIndex.retrieveSpriteFile(anInt1400)) { - aAbstractSprite_3099 = Unsorted.method562(CacheIndex.spritesIndex, archiveID); - aAbstractSprite_1457 = Unsorted.method562(CacheIndex.spritesIndex, anInt1400); - if (HDToolKit.highDetail) { - if (aAbstractSprite_3099 instanceof Class3_Sub28_Sub16_Sub2_Sub1) { - aAbstractSprite_3099 = new Class3_Sub28_Sub16_Sub1_Sub1((SoftwareSprite) aAbstractSprite_3099); - } else { - aAbstractSprite_3099 = new HDSprite((SoftwareSprite) aAbstractSprite_3099); - } - - if (aAbstractSprite_1457 instanceof Class3_Sub28_Sub16_Sub2_Sub1) { - aAbstractSprite_1457 = new Class3_Sub28_Sub16_Sub1_Sub1((SoftwareSprite) aAbstractSprite_1457); - } else { - aAbstractSprite_1457 = new HDSprite((SoftwareSprite) aAbstractSprite_1457); - } - } - } else { - Toolkit.getActiveToolkit().fillRect(var1, var2, var3, 20, InterfaceWidget.anInt3600, -Unsorted.anInt963 + 256); - } - } - - int var5; - int var6; - if (aAbstractSprite_3099 != null && aAbstractSprite_1457 != null) { - var5 = var3 / aAbstractSprite_3099.width; - - for (var6 = 0; var6 < var5; ++var6) { - aAbstractSprite_3099.drawAt(var6 * aAbstractSprite_3099.width + var1, var2); - } - - aAbstractSprite_1457.drawAt(var1, var2); - aAbstractSprite_1457.method641(-aAbstractSprite_1457.width + (var1 - -var3), var2); - } - - FontType.bold.method681(RSString.parse(GameConfig.RCM_TITLE), var1 - -3, 14 + var2, anInt3351, -1); - Toolkit.getActiveToolkit().fillRect(var1, 20 + var2, var3, var4 - 20, InterfaceWidget.anInt3600, -Unsorted.anInt963 + 256); - - var6 = Unsorted.anInt1709; - var5 = Class126.anInt1676; - int var7; - int var8; - for (var7 = 0; Unsorted.menuOptionCount > var7; ++var7) { - var8 = (-var7 + Unsorted.menuOptionCount - 1) * 15 + var2 + 35; - if (var1 < var5 && var5 < var1 - -var3 && -13 + var8 < var6 && var8 + 3 > var6) { - Toolkit.getActiveToolkit().fillRect(var1, var8 - 13, var3, 16, MouseListeningClass.anInt1926, -Class136.anInt1771 + 256); - } - } - - if ((aAbstractSprite_1339 == null || Class50.aAbstractSprite_824 == null || null == Class3_Sub26.aAbstractSprite_2560) && CacheIndex.spritesIndex.retrieveSpriteFile(anInt739) && CacheIndex.spritesIndex.retrieveSpriteFile(anInt1126) && CacheIndex.spritesIndex.retrieveSpriteFile(anInt2937)) { - aAbstractSprite_1339 = Unsorted.method562(CacheIndex.spritesIndex, anInt739); - Class50.aAbstractSprite_824 = Unsorted.method562(CacheIndex.spritesIndex, anInt1126); - Class3_Sub26.aAbstractSprite_2560 = Unsorted.method562(CacheIndex.spritesIndex, anInt2937); - if (HDToolKit.highDetail) { - if (aAbstractSprite_1339 instanceof Class3_Sub28_Sub16_Sub2_Sub1) { - aAbstractSprite_1339 = new Class3_Sub28_Sub16_Sub1_Sub1((SoftwareSprite) aAbstractSprite_1339); - } else { - aAbstractSprite_1339 = new HDSprite((SoftwareSprite) aAbstractSprite_1339); - } - - if (Class50.aAbstractSprite_824 instanceof Class3_Sub28_Sub16_Sub2_Sub1) { - Class50.aAbstractSprite_824 = new Class3_Sub28_Sub16_Sub1_Sub1((SoftwareSprite) Class50.aAbstractSprite_824); - } else { - Class50.aAbstractSprite_824 = new HDSprite((SoftwareSprite) Class50.aAbstractSprite_824); - } - - if (Class3_Sub26.aAbstractSprite_2560 instanceof Class3_Sub28_Sub16_Sub2_Sub1) { - Class3_Sub26.aAbstractSprite_2560 = new Class3_Sub28_Sub16_Sub1_Sub1((SoftwareSprite) Class3_Sub26.aAbstractSprite_2560); - } else { - Class3_Sub26.aAbstractSprite_2560 = new HDSprite((SoftwareSprite) Class3_Sub26.aAbstractSprite_2560); - } - } - } - - int var9; - if (aAbstractSprite_1339 != null && null != Class50.aAbstractSprite_824 && null != Class3_Sub26.aAbstractSprite_2560) { - var7 = var3 / aAbstractSprite_1339.width; - - for (var8 = 0; var7 > var8; ++var8) { - aAbstractSprite_1339.drawAt(var1 + aAbstractSprite_1339.width * var8, var4 + var2 + -aAbstractSprite_1339.height); - } - - var8 = (-20 + var4) / Class50.aAbstractSprite_824.height; - - for (var9 = 0; var9 < var8; ++var9) { - Class50.aAbstractSprite_824.drawAt(var1, var2 + 20 + var9 * Class50.aAbstractSprite_824.height); - Class50.aAbstractSprite_824.method641(var1 - (-var3 - -Class50.aAbstractSprite_824.width), var2 + 20 + var9 * Class50.aAbstractSprite_824.height); - } - - Class3_Sub26.aAbstractSprite_2560.drawAt(var1, var4 + (var2 - Class3_Sub26.aAbstractSprite_2560.height)); - Class3_Sub26.aAbstractSprite_2560.method641(var1 + var3 - Class3_Sub26.aAbstractSprite_2560.width, var2 - -var4 + -Class3_Sub26.aAbstractSprite_2560.height); - } - - for (var7 = 0; var7 < Unsorted.menuOptionCount; ++var7) { - var8 = 15 * (Unsorted.menuOptionCount - 1 + -var7) + var2 + 35; - var9 = anInt3351; - if (var1 < var5 && var3 + var1 > var5 && var6 > var8 - 13 && var8 - -3 > var6) { - var9 = Class154.anInt1957; - } - - FontType.bold.method681(Unsorted.method802(var7), 3 + var1, var8, var9, 0); - } - - Unsorted.method1282(Class21.anInt1462, Class21.anInt3395, Class21.anInt3537, Class21.anInt3552); - } catch (RuntimeException var10) { - throw ClientErrorException.clientError(var10, "ij.F(" + ')'); - } - } - - final WorldListCountry method1078(int var1) { - try { - return countries[this.countryIndex]; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "ba.B(" + var1 + ')'); - } - } - - static int method1079(int var0) { - try { - if (0 > var0) { - return 0; - } else { - Class3_Sub25 var2 = (Class3_Sub25) Class3_Sub2.aHashTable_2220.get(var0); - if (var2 == null) { - return Objects.requireNonNull(ConfigInventoryDefinition.retrieveConfigurationInventoryFile(var0)).size; - } else { - int var3 = 0; - - for (int var4 = 0; var4 < var2.anIntArray2547.length; ++var4) { - if (var2.anIntArray2547[var4] == -1) { - ++var3; - } - } - - var3 += Objects.requireNonNull(ConfigInventoryDefinition.retrieveConfigurationInventoryFile(var0)).size + -var2.anIntArray2547.length; - return var3; - } - } - } catch (RuntimeException var5) { - throw ClientErrorException.clientError(var5, "ba.D(" + var0 + ',' + (byte) -80 + ')'); - } - } - -} diff --git a/Client/src/main/java/org/runite/client/WorldMap.java b/Client/src/main/java/org/runite/client/WorldMap.java deleted file mode 100644 index 9f1707688..000000000 --- a/Client/src/main/java/org/runite/client/WorldMap.java +++ /dev/null @@ -1,275 +0,0 @@ -package org.runite.client; - -import org.rs09.client.Node; -import org.rs09.client.config.GameConfig; -import org.rs09.client.rendering.Toolkit; - -final class WorldMap extends Node { - - static int anInt2737 = 0; - static CacheIndex aClass153_3210; - static SoftwareSprite aSoftwareSprite_3221; - Class140_Sub7 aClass140_Sub7_3676; - - - WorldMap(Class140_Sub7 var1) { - try { - this.aClass140_Sub7_3676 = var1; - } catch (RuntimeException var3) { - throw ClientErrorException.clientError(var3, "pa.(" + (var1 != null ? "{...}" : "null") + ')'); - } - } - - static void worldMapZoomFontSize() { - try { - if (null != Unsorted.aClass3_Sub28_Sub3_2600) { - if (anInt2737 < 10) { - if (!aClass153_3210.method2127(Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561)) { - anInt2737 = CacheIndex.worldmapIndex.method2116(Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561) / 10; - return; - } - - TextureOperation12.method169(); - anInt2737 = 10; - } - - /** - * This block is dealing with world map zoom - */ - if (anInt2737 == 10) { - TextureOperation37.anInt3256 = Unsorted.aClass3_Sub28_Sub3_2600.anInt3555 >> 6 << 6; - Unsorted.anInt65 = Unsorted.aClass3_Sub28_Sub3_2600.anInt3562 >> 6 << 6; - Class108.anInt1460 = (Unsorted.aClass3_Sub28_Sub3_2600.anInt3549 >> 6 << 6) - Unsorted.anInt65 + 64; - Class23.anInt455 = 64 + (Unsorted.aClass3_Sub28_Sub3_2600.anInt3559 >> 6 << 6) + -TextureOperation37.anInt3256; - if (GameConfig.WORLD_MAP_DEBUG) - System.out.println("World Map Debug: Setting initial zoom level to: " + Unsorted.aClass3_Sub28_Sub3_2600.anInt3563); - if (Unsorted.aClass3_Sub28_Sub3_2600.anInt3563 == 37) { //Furthest zoom 37% on interface - Class44.aFloat727 = 3.0F; - NPC.aFloat3979 = 3.0F; - } else if (Unsorted.aClass3_Sub28_Sub3_2600.anInt3563 == 50) { //Far zoom 50% on interface - Class44.aFloat727 = 4.0F; - NPC.aFloat3979 = 4.0F; - } else if (Unsorted.aClass3_Sub28_Sub3_2600.anInt3563 == 75) { //Default zoom 75% on interface - Class44.aFloat727 = 6.0F; - NPC.aFloat3979 = 6.0F; - } else if (Unsorted.aClass3_Sub28_Sub3_2600.anInt3563 == 100) { //Close zoom 100% on interface - Class44.aFloat727 = 8.0F; - NPC.aFloat3979 = 8.0F; - } else if (Unsorted.aClass3_Sub28_Sub3_2600.anInt3563 == 200) { //Closest zoom 200% on interface - Class44.aFloat727 = 16.0F; - NPC.aFloat3979 = 16.0F; - } else { - Class44.aFloat727 = 8.0F; //Default value if one is not set = to 100% on interface - NPC.aFloat3979 = 8.0F; - } - /* End Minimap Zoom */ - - - int var1 = -TextureOperation37.anInt3256 + (Class102.player.xAxis >> 7) + Class131.anInt1716; - var1 += -5 + (int) (Math.random() * 10.0D); - int var2 = -Texture.anInt1152 + -(Class102.player.zAxis >> 7) + Unsorted.anInt65 + -1 + Class108.anInt1460; - var2 += -5 + (int) (Math.random() * 10.0D); - if (var1 >= 0 && var1 < Class23.anInt455 && 0 <= var2 && Class108.anInt1460 > var2) { - Class3_Sub28_Sub1.anInt3536 = var1; - Class3_Sub4.anInt2251 = var2; - } else { - Class3_Sub4.anInt2251 = Unsorted.anInt65 - Unsorted.aClass3_Sub28_Sub3_2600.anInt3556 * 64 + Class108.anInt1460 + -1; - Class3_Sub28_Sub1.anInt3536 = Unsorted.aClass3_Sub28_Sub3_2600.anInt3558 * 64 + -TextureOperation37.anInt3256; - } - - Class3_Sub5.method117(); - Class83.anIntArray1161 = new int[1 + Client.anInt869]; - int var4 = Class108.anInt1460 >> 6; - int var3 = Class23.anInt455 >> 6; - Class3_Sub10.aByteArrayArrayArray2339 = new byte[var3][var4][]; - int var5 = Class158_Sub1.anInt3158 >> 2 << 10; - Class36.aByteArrayArrayArray640 = new byte[var3][var4][]; - Class44.anIntArrayArrayArray720 = new int[var3][var4][]; - RenderAnimationDefinition.aByteArrayArrayArray383 = new byte[var3][var4][]; - Class146.anIntArrayArrayArray1903 = new int[var3][var4][]; - TextureOperation29.aByteArrayArrayArray3390 = new byte[var3][var4][]; - int var6 = Class158_Sub1.anInt1463 >> 1; - CS2Script.aByteArrayArrayArray2452 = new byte[var3][var4][]; - Class29.anIntArrayArrayArray558 = new int[var3][var4][]; - FloorOverlayDefinition.method1014(var6, var5); - anInt2737 = 20; - - - } else if (anInt2737 == 20) { - if (GameConfig.WORLD_MAP_DEBUG) - System.out.println("World Map Debug: World Map Stage 20: String given: " + Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561.properlyCapitalize().toString()); - WorldMapUnderlayDecoder.decode(new DataBuffer(aClass153_3210.method2123(RSString.parse("underlay"), Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561)));//This controls the world map underlay - anInt2737 = 30; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else if (anInt2737 == 30) { - if (GameConfig.WORLD_MAP_DEBUG) - System.out.println("World Map Debug: World Map Stage 30: String given: " + Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561.properlyCapitalize().toString()); - WorldMapOverlayDecoder.decode(new DataBuffer(aClass153_3210.method2123(RSString.parse("overlay"), Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561)));//This controls the world map overlay (water) - anInt2737 = 40; - Class75_Sub4.method1355(); - } else if (anInt2737 == 40) { - if (GameConfig.WORLD_MAP_DEBUG) - System.out.println("World Map Debug: World Map Stage 40: String given: " + Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561.properlyCapitalize().toString()); - WorldMapOverlay2Decoder.decode(new DataBuffer(aClass153_3210.method2123(RSString.parse("overlay2"), Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561)));//unsure - anInt2737 = 50; - Class75_Sub4.method1355(); - } else if (anInt2737 == 50) { - if (GameConfig.WORLD_MAP_DEBUG) - System.out.println("World Map Debug: World Map Stage 50: String given: " + Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561.properlyCapitalize().toString()); - WorldMapLOCDecoder.decode(new DataBuffer(aClass153_3210.method2123(RSString.parse("loc"), Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561)));//This controls the world map object drawing (buildings etc) - anInt2737 = 60; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else if (anInt2737 == 60) { - if (GameConfig.WORLD_MAP_DEBUG) - System.out.println("World Map Debug: World Map Stage 60: String given: " + Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561.properlyCapitalize().toString()); - if (aClass153_3210.method2135(RSString.stringCombiner(new RSString[]{Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561, TextCore.HasLabels}))) { - if (!aClass153_3210.method2127(RSString.stringCombiner(new RSString[]{Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561, TextCore.HasLabels}))) { - return; - } - - if (GameConfig.WORLD_MAP_DEBUG) - System.out.println("World Map Debug: World Map Class119.aClass131_1624: " + Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561.properlyCapitalize().toString() + ", " + TextCore.HasLabels.properlyCapitalize().toString()); - Class119.aClass131_1624 = Class81.getWorldMapArchive(RSString.stringCombiner(new RSString[]{Unsorted.aClass3_Sub28_Sub3_2600.aClass94_3561, TextCore.HasLabels}), aClass153_3210); - } else { - Class119.aClass131_1624 = new Class131(0); - } - - anInt2737 = 70; - Class75_Sub4.method1355(); - } else if (anInt2737 == 70) { - Class164_Sub2.aClass33_3019 = new Class33(11, GameShell.canvas);//Sets the font size of *some* locations on the world map interface - anInt2737 = 73; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else if (anInt2737 == 73) { - Unsorted.aClass33_1238 = new Class33(12, GameShell.canvas);//Unsure - anInt2737 = 76; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else if (anInt2737 == 76) { - Class99.aClass33_1399 = new Class33(14, GameShell.canvas);//Sets the font size of *some* cities on the world map interface - anInt2737 = 79; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else if (anInt2737 == 79) { - Class75_Sub2.aClass33_2637 = new Class33(17, GameShell.canvas);//unsure - anInt2737 = 82; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else if (anInt2737 == 82) { - Class119.aClass33_1626 = new Class33(19, GameShell.canvas);//Sets the font size of the orange region names on the world map interface - anInt2737 = 85; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else if (85 == anInt2737) { - Class75_Sub2.aClass33_2648 = new Class33(22, GameShell.canvas);//unsure - anInt2737 = 88; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else if (88 == anInt2737) { - Class161.aClass33_2034 = new Class33(26, GameShell.canvas);//unsure - anInt2737 = 91; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - } else { - Class91.aClass33_1305 = new Class33(30, GameShell.canvas);//unsure - anInt2737 = 100; - Class163_Sub1.ping(true); - Class75_Sub4.method1355(); - System.gc(); - } - } - } catch (RuntimeException var7) { - throw ClientErrorException.clientError(var7, "pa.B(" + 64 + ')'); - } - } - - public static void drawWorldMap(int var0, int var2, int var3, int var4) { - try { - if (anInt2737 < 100) { - worldMapZoomFontSize(); - } - - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var2, var0 + var4, var3 + var2); - } else { - Class74.setClipping(var0, var2, var0 + var4, var3 + var2); - } - - int var6; - int var7; - if (anInt2737 >= 100) { - Class17.anInt410 = (int) ((float) (var3 * 2) / Class44.aFloat727); - Class60.anInt930 = Class3_Sub28_Sub1.anInt3536 + -((int) ((float) var4 / Class44.aFloat727)); - int var15 = -((int) ((float) var4 / Class44.aFloat727)) + Class3_Sub28_Sub1.anInt3536; - var6 = Class3_Sub4.anInt2251 - (int) ((float) var3 / Class44.aFloat727); - Class60.anInt934 = Class3_Sub4.anInt2251 + -((int) ((float) var3 / Class44.aFloat727)); - int var8 = Class3_Sub4.anInt2251 + (int) ((float) var3 / Class44.aFloat727); - var7 = (int) ((float) var4 / Class44.aFloat727) + Class3_Sub28_Sub1.anInt3536; - Class49.anInt817 = (int) ((float) (var4 * 2) / Class44.aFloat727); - - if (HDToolKit.highDetail) { - if (aSoftwareSprite_3221 == null || var4 != aSoftwareSprite_3221.width || var3 != aSoftwareSprite_3221.height) { - aSoftwareSprite_3221 = null; - aSoftwareSprite_3221 = new SoftwareSprite(var4, var3); - } - - Class74.setBuffer(aSoftwareSprite_3221.anIntArray4081, var4, var3); - Unsorted.method523(var4, 0, var7, var6, 0, var8, var3, var15); - Class23.method938(var4, 0, var7, var8, var3, 0, var15, var6); - Class3_Sub5.method111((byte) -54, 0, 0, var15, var4, var8, var6, var7, var3); - Class22.method926(aSoftwareSprite_3221.anIntArray4081, var0, var2, var4, var3); - Toolkit.JAVA_TOOLKIT.resetBuffer(); - } else { - Unsorted.method523(var4 + var0, var2, var7, var6, var0, var8, var2 - -var3, var15); - Class23.method938(var0 + var4, var0, var7, var8, var3 + var2, var2, var15, var6); - Class3_Sub5.method111((byte) -100, var0, var2, var15, var0 - -var4, var8, var6, var7, var3 + var2); - } - - if (0 < AbstractSprite.anInt3704) { - --Class3_Sub28_Sub8.anInt3611; - if (Class3_Sub28_Sub8.anInt3611 == 0) { - Class3_Sub28_Sub8.anInt3611 = 20; - --AbstractSprite.anInt3704; - } - } - - /* - * This handles the ::fpson command to overlay when a user opens up the world map - */ - if (ClientCommands.fpsOverlayEnabled) { - int var10 = -8 + var2 - -var3; - int var9 = -5 + (var0 - -var4); - FontType.plainFont.drawStringRightAnchor(RSString.stringCombiner(new RSString[]{TextCore.aClass94_985, RSString.stringAnimator(SequenceDefinition.anInt1862)}), var9, var10, 16776960, -1); - Runtime var11 = Runtime.getRuntime(); - int var12 = (int) ((var11.totalMemory() - var11.freeMemory()) / 1024L); - int var13 = 16776960; - var10 -= 15; - if (var12 > 65536) { - var13 = 16711680; - } - - FontType.plainFont.drawStringRightAnchor(RSString.stringCombiner(new RSString[]{TextCore.aClass94_1630, RSString.stringAnimator(var12), RSString.parse("k")}), var9, var10, var13, -1); - } - /* * * * * * * */ - - } else { - byte var5 = 20; - var6 = var0 - -(var4 / 2); - var7 = var3 / 2 + (var2 - 18) + -var5; - //World map pre loading brown screen - Toolkit.getActiveToolkit().method934(var0, var2, var4, var3, 0); - Toolkit.getActiveToolkit().drawRect(var6 - 152, var7, 304, 34, 9179409, 255); - Toolkit.getActiveToolkit().drawRect(var6 + -151, var7 + 1, 302, 32, 0, 255); - Toolkit.getActiveToolkit().method934(-150 + var6, var7 + 2, 3 * anInt2737, 30, 9179409); - Toolkit.getActiveToolkit().method934(-150 + var6 + anInt2737 * 3, var7 - -2, 300 + -(3 * anInt2737), 30, 0); - - FontType.bold.method699(TextCore.LoadingGeneral, var6, var5 + var7, 16777215, -1); - } - } catch (RuntimeException var14) { - throw ClientErrorException.clientError(var14, "wa.FA(" + var0 + ',' + 64 + ',' + var2 + ',' + var3 + ',' + var4 + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/WorldMapLOCDecoder.java b/Client/src/main/java/org/runite/client/WorldMapLOCDecoder.java deleted file mode 100644 index f4e81b3f3..000000000 --- a/Client/src/main/java/org/runite/client/WorldMapLOCDecoder.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.runite.client; - -public final class WorldMapLOCDecoder { - - static void decode(DataBuffer var1) { - try { - label134: - while (true) { - if (var1.index < var1.buffer.length) { - boolean var18 = false; - int var5 = 0; - int var6 = 0; - if (var1.readUnsignedByte() == 1) { - var5 = var1.readUnsignedByte(); - var18 = true; - var6 = var1.readUnsignedByte(); - } - - int var7 = var1.readUnsignedByte(); - int var8 = var1.readUnsignedByte(); - int var9 = -TextureOperation37.anInt3256 + 64 * var7; - int var10 = -(var8 * 64) - (-Unsorted.anInt65 - -1) + Class108.anInt1460; - int var11; - int var12; - if (var9 >= 0 && -63 + var10 >= 0 && Class23.anInt455 > var9 + 63 && var10 < Class108.anInt1460) { - var11 = var9 >> 6; - var12 = var10 >> 6; - int var13 = 0; - - while (true) { - if (var13 >= 64) { - continue label134; - } - - for (int var14 = 0; var14 < 64; ++var14) { - if (!var18 || var13 >= 8 * var5 && 8 * var5 - -8 > var13 && 8 * var6 <= var14 && var14 < var6 * 8 - -8) { - int var15 = var1.readUnsignedByte(); - if (0 != var15) { - int var2; - if (1 == (1 & var15)) { - var2 = var1.readUnsignedByte(); - if (Class36.aByteArrayArrayArray640[var11][var12] == null) { - Class36.aByteArrayArrayArray640[var11][var12] = new byte[4096]; - } - - Class36.aByteArrayArrayArray640[var11][var12][var13 + (-var14 + 63 << 6)] = (byte) var2; - } - - if (2 == (var15 & 2)) { - var2 = var1.readMedium(); - if (null == Class29.anIntArrayArrayArray558[var11][var12]) { - Class29.anIntArrayArrayArray558[var11][var12] = new int[4096]; - } - - Class29.anIntArrayArrayArray558[var11][var12][(-var14 + 63 << 6) + var13] = var2; - } - - if (4 == (var15 & 4)) { - var2 = var1.readMedium(); - if (null == Class44.anIntArrayArrayArray720[var11][var12]) { - Class44.anIntArrayArrayArray720[var11][var12] = new int[4096]; - } - - --var2; - ObjectDefinition var3 = ObjectDefinition.getObjectDefinition(var2); - if (null != var3.ChildrenIds) { - var3 = var3.method1685(0); - if (var3 == null || var3.MapIcon == -1) { - continue; - } - } - - Class44.anIntArrayArrayArray720[var11][var12][(-var14 + 63 << 6) + var13] = 1 + var3.objectId; - Class3_Sub23 var16 = new Class3_Sub23(); - var16.anInt2532 = var3.MapIcon; - var16.anInt2531 = var9; - var16.anInt2539 = var10; - Class84.aLinkedList_1162.method1215(var16); - } - } - } - } - - ++var13; - } - } - - var11 = 0; - - while (true) { - if (var11 >= (var18 ? 64 : 4096)) { - continue label134; - } - - var12 = var1.readUnsignedByte(); - if (var12 != 0) { - if ((var12 & 1) == 1) { - ++var1.index; - } - - if (2 == (var12 & 2)) { - var1.index += 2; - } - - if (4 == (var12 & 4)) { - var1.index += 3; - } - } - - ++var11; - } - } - return; - } - } catch (RuntimeException var17) { - throw ClientErrorException.clientError(var17, "nc.A(" + (byte) -83 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/WorldMapOverlay2Decoder.java b/Client/src/main/java/org/runite/client/WorldMapOverlay2Decoder.java deleted file mode 100644 index e3371ad26..000000000 --- a/Client/src/main/java/org/runite/client/WorldMapOverlay2Decoder.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.runite.client; - -public final class WorldMapOverlay2Decoder { - - static void decode(DataBuffer var1) { - try { - - while (var1.index < var1.buffer.length) { - int var4 = 0; - boolean var3 = false; - int var5 = 0; - if (var1.readUnsignedByte() == 1) { - var3 = true; - var4 = var1.readUnsignedByte(); - var5 = var1.readUnsignedByte(); - } - - int var6 = var1.readUnsignedByte(); - int var7 = var1.readUnsignedByte(); - int var8 = -TextureOperation37.anInt3256 + var6 * 64; - int var9 = Class108.anInt1460 + -1 - -Unsorted.anInt65 - 64 * var7; - byte var2; - int var10; - if (var8 >= 0 && (var9 - 63) >= 0 && Class23.anInt455 > var8 + 63 && Class108.anInt1460 > var9) { - var10 = var8 >> 6; - int var11 = var9 >> 6; - - for (int var12 = 0; 64 > var12; ++var12) { - for (int var13 = 0; var13 < 64; ++var13) { - if (!var3 || var12 >= (var4 * 8) && 8 + 8 * var4 > var12 && var13 >= var5 * 8 && 8 + var5 * 8 > var13) { - var2 = var1.readSignedByte(); - if (var2 != 0) { - if (null == TextureOperation29.aByteArrayArrayArray3390[var10][var11]) { - TextureOperation29.aByteArrayArrayArray3390[var10][var11] = new byte[4096]; - } - - TextureOperation29.aByteArrayArrayArray3390[var10][var11][(63 + -var13 << 6) + var12] = var2; - byte var14 = var1.readSignedByte(); - if (null == CS2Script.aByteArrayArrayArray2452[var10][var11]) { - CS2Script.aByteArrayArrayArray2452[var10][var11] = new byte[4096]; - } - - CS2Script.aByteArrayArrayArray2452[var10][var11][var12 + (-var13 + 63 << 6)] = var14; - } - } - } - } - } else { - for (var10 = 0; (!var3 ? 4096 : 64) > var10; ++var10) { - var2 = var1.readSignedByte(); - if (var2 != 0) { - ++var1.index; - } - } - } - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "sk.F(" + -21774 + ',' + (var1 != null ? "{...}" : "null") + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/WorldMapOverlayDecoder.java b/Client/src/main/java/org/runite/client/WorldMapOverlayDecoder.java deleted file mode 100644 index e824af9cb..000000000 --- a/Client/src/main/java/org/runite/client/WorldMapOverlayDecoder.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.runite.client; - -public final class WorldMapOverlayDecoder { - - static void decode(DataBuffer var0) { - try { - - while(var0.buffer.length > var0.index) { - int var4 = 0; - boolean var3 = false; - int var5 = 0; - if(var0.readUnsignedByte() == 1) { - var3 = true; - var4 = var0.readUnsignedByte(); - var5 = var0.readUnsignedByte(); - } - - int var6 = var0.readUnsignedByte(); - int var7 = var0.readUnsignedByte(); - int var9 = -(var7 * 64) - (-Unsorted.anInt65 - Class108.anInt1460 + 1); - int var8 = var6 * 64 + -TextureOperation37.anInt3256; - byte var2; - int var10; - if(var8 >= 0 && (var9 - 63) >= 0 && Class23.anInt455 > (var8 + 63) && var9 < Class108.anInt1460) { - var10 = var8 >> 6; - int var11 = var9 >> 6; - - for(int var12 = 0; var12 < 64; ++var12) { - for(int var13 = 0; var13 < 64; ++var13) { - if(!var3 || (8 * var4) <= var12 && var12 < 8 + var4 * 8 && var13 >= var5 * 8 && (8 + var5 * 8) > var13) { - var2 = var0.readSignedByte(); - if(var2 != 0) { - if(RenderAnimationDefinition.aByteArrayArrayArray383[var10][var11] == null) { - RenderAnimationDefinition.aByteArrayArrayArray383[var10][var11] = new byte[4096]; - } - - RenderAnimationDefinition.aByteArrayArrayArray383[var10][var11][var12 + (-var13 + 63 << 6)] = var2; - byte var14 = var0.readSignedByte(); - if(Class3_Sub10.aByteArrayArrayArray2339[var10][var11] == null) { - Class3_Sub10.aByteArrayArrayArray2339[var10][var11] = new byte[4096]; - } - - Class3_Sub10.aByteArrayArrayArray2339[var10][var11][var12 + (63 - var13 << 6)] = var14; - } - } - } - } - } else { - for(var10 = 0; var10 < (var3 ? 64 : 4096); ++var10) { - var2 = var0.readSignedByte(); - if(0 != var2) { - ++var0.index; - } - } - } - } - - } catch (RuntimeException var15) { - throw ClientErrorException.clientError(var15, "dk.C(" + (var0 != null?"{...}":"null") + ',' + false + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/WorldMapUnderlayDecoder.java b/Client/src/main/java/org/runite/client/WorldMapUnderlayDecoder.java deleted file mode 100644 index cec9a08f7..000000000 --- a/Client/src/main/java/org/runite/client/WorldMapUnderlayDecoder.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.runite.client; - -public final class WorldMapUnderlayDecoder { - - static void decode(DataBuffer var1) { - try { - int var3 = Class158.anInt2010 >> 1; - int var2 = AudioThread.anInt4034 >> 2 << 10; - byte[][] var4 = new byte[Class23.anInt455][Class108.anInt1460]; - - int var6; - int var12; - int var14; - while (var1.index < var1.buffer.length) { - int var7 = 0; - var6 = 0; - boolean var5 = false; - if (var1.readUnsignedByte() == 1) { - var6 = var1.readUnsignedByte(); - var7 = var1.readUnsignedByte(); - var5 = true; - } - - int var8 = var1.readUnsignedByte(); - int var9 = var1.readUnsignedByte(); - int var10 = -TextureOperation37.anInt3256 + var8 * 64; - int var11 = -1 + Class108.anInt1460 - var9 * 64 + Unsorted.anInt65; - if (var10 >= 0 && 0 <= -63 + var11 && Class23.anInt455 > var10 - -63 && var11 < Class108.anInt1460) { - for (var12 = 0; var12 < 64; ++var12) { - byte[] var13 = var4[var10 - -var12]; - - for (var14 = 0; 64 > var14; ++var14) { - if (!var5 || var12 >= 8 * var6 && 8 + 8 * var6 > var12 && var14 >= var7 * 8 && var14 < 8 + 8 * var7) { - var13[var11 - var14] = var1.readSignedByte(); - } - } - } - } else if (var5) { - var1.index += 64; - } else { - var1.index += 4096; - } - } - - int var27 = Class23.anInt455; - var6 = Class108.anInt1460; - int[] var29 = new int[var6]; - int[] var28 = new int[var6]; - int[] var30 = new int[var6]; - int[] var32 = new int[var6]; - int[] var31 = new int[var6]; - - for (var12 = -5; var27 > var12; ++var12) { - int var15; - int var35; - for (int var34 = 0; var6 > var34; ++var34) { - var14 = var12 + 5; - if (var27 > var14) { - var15 = 255 & var4[var14][var34]; - if (var15 > 0) { - FloorUnderlayDefinition var16 = FloorUnderlayDefinition.method629(var15 - 1); - var28[var34] += var16.anInt1408; - var29[var34] += var16.anInt1406; - var30[var34] += var16.anInt1417; - var32[var34] += var16.anInt1418; - ++var31[var34]; - } - } - - var15 = var12 + -5; - if (var15 >= 0) { - var35 = var4[var15][var34] & 0xFF; - if (0 < var35) { - FloorUnderlayDefinition var17 = FloorUnderlayDefinition.method629(-1 + var35); - var28[var34] -= var17.anInt1408; - var29[var34] -= var17.anInt1406; - var30[var34] -= var17.anInt1417; - var32[var34] -= var17.anInt1418; - --var31[var34]; - } - } - } - - if (var12 >= 0) { - int[][] var33 = Class146.anIntArrayArrayArray1903[var12 >> 6]; - var14 = 0; - var15 = 0; - int var36 = 0; - int var18 = 0; - var35 = 0; - - for (int var19 = -5; var6 > var19; ++var19) { - int var20 = var19 - -5; - if (var6 > var20) { - var18 += var31[var20]; - var15 += var29[var20]; - var35 += var30[var20]; - var14 += var28[var20]; - var36 += var32[var20]; - } - - int var21 = -5 + var19; - if (var21 >= 0) { - var35 -= var30[var21]; - var36 -= var32[var21]; - var14 -= var28[var21]; - var18 -= var31[var21]; - var15 -= var29[var21]; - } - - if (var19 >= 0 && 0 < var18) { - int[] var22 = var33[var19 >> 6]; - int var23 = var36 != 0 ? Class3_Sub8.method129(var35 / var18, var15 / var18, var14 * 256 / var36) : 0; - if (var4[var12][var19] == 0) { - if (var22 != null) { - var22[Unsorted.bitwiseAnd(4032, var19 << 6) - -Unsorted.bitwiseAnd(var12, 63)] = 0; - } - } else { - if (var22 == null) { - var22 = var33[var19 >> 6] = new int[4096]; - } - - int var24 = var3 + (var23 & 127); - if (var24 < 0) { - var24 = 0; - } else if (var24 > 127) { - var24 = 127; - } - - int var25 = var24 + (896 & var23) + (var23 + var2 & '\ufc00'); - var22[Unsorted.bitwiseAnd(4032, var19 << 6) + Unsorted.bitwiseAnd(63, var12)] = Class51.anIntArray834[Unsorted.method1100(96, var25)]; - } - } - } - } - } - - } catch (RuntimeException var26) { - throw ClientErrorException.clientError(var26, "cj.H(" + (var1 != null ? "{...}" : "null") + ')'); - } - } -} diff --git a/Client/src/main/java/org/runite/client/drawcalls/Compass.java b/Client/src/main/java/org/runite/client/drawcalls/Compass.java deleted file mode 100644 index 8d2c7611f..000000000 --- a/Client/src/main/java/org/runite/client/drawcalls/Compass.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.runite.client.drawcalls; - -import org.runite.client.*; - -public final class Compass { - - public static void drawCompass(int var0, int var1, RSInterface var2, int var3) { - if (HDToolKit.highDetail) { - Class22.setClipping(var0, var1, var2.width + var0, var2.height + var1); - } - - if (Class161.anInt2028 >= 3) { - if (HDToolKit.highDetail) { - AbstractSprite var5 = var2.method866(false); - if (null != var5) { - var5.drawAt(var0, var1); - } - } else { - Class74.method1332(var0, var1, var2.anIntArray207, var2.anIntArray291); - } - } else if (HDToolKit.highDetail) { - ((HDSprite) Class57.aAbstractSprite_895).drawMinimapRegion(var0, var1, var2.width, var2.height, Class57.aAbstractSprite_895.width / 2, Class57.aAbstractSprite_895.height / 2, GraphicDefinition.CAMERA_DIRECTION, 256, (HDSprite) var2.method866(false)); - } else { - ((SoftwareSprite) Class57.aAbstractSprite_895).method667(var0, var1, var2.width, var2.height, Class57.aAbstractSprite_895.width / 2, Class57.aAbstractSprite_895.height / 2, GraphicDefinition.CAMERA_DIRECTION, var2.anIntArray207, var2.anIntArray291); - } - - Class163_Sub1_Sub1.aBooleanArray4008[var3] = true; - } -} diff --git a/Client/src/main/java/org/runite/client/drawcalls/ContextMenu.java b/Client/src/main/java/org/runite/client/drawcalls/ContextMenu.java deleted file mode 100644 index 64ef0deb0..000000000 --- a/Client/src/main/java/org/runite/client/drawcalls/ContextMenu.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.runite.client.drawcalls; - -import org.rs09.client.config.GameConfig; -import org.rs09.client.rendering.Toolkit; -import org.runite.client.*; - -public final class ContextMenu { - - public static void draw() { - int x = Class21.anInt1462; - int y = Class21.anInt3395; - int width = Class21.anInt3552; - int height = Class21.anInt3537; - int contextMenuColor = 6116423; //Context Menu RGB || 6116423 Classic || Old var5 || ColorCore.getHexColors() - Toolkit.getActiveToolkit().fillRect(1 + x, y + 18, width + -2, -19 + height, GameConfig.RCM_BG_COLOR, GameConfig.getRCM_BG_OPACITY()); - if (GameConfig.RS3_CONTEXT_STYLE) { - Toolkit.getActiveToolkit().fillRect(1 + x, 2 + y, width + -2, 16, GameConfig.RCM_TITLE_COLOR, GameConfig.getRCM_TITLE_OPACITY()); - Toolkit.getActiveToolkit().drawRect(1 + x, 1 + y, width + -2, height, GameConfig.RCM_BORDER_COLOR, GameConfig.getRCM_BORDER_OPACITY()); - } else { - Toolkit.getActiveToolkit().fillRect(1 + x, 2 + y, width + -2, 16, GameConfig.RCM_TITLE_COLOR, GameConfig.getRCM_TITLE_OPACITY()); - Toolkit.getActiveToolkit().drawRect(1 + x, y + 18, width + -2, -19 + height, GameConfig.RCM_BORDER_COLOR, GameConfig.getRCM_BORDER_OPACITY()); - } - - FontType.bold.method681(RSString.parse(GameConfig.RCM_TITLE), x - -3, y + 14, contextMenuColor, -1); - int var7 = Unsorted.anInt1709; - int var6 = Class126.anInt1676; - - for (int var8 = 0; var8 < Unsorted.menuOptionCount; ++var8) { - int var9 = (-var8 + -1 + Unsorted.menuOptionCount) * 15 + y - -31; - int var10 = 16777215; - if (var6 > x && x - -width > var6 && -13 + var9 < var7 && 3 + var9 > var7) { - var10 = 16776960; - } - - FontType.bold.method681(Unsorted.method802(var8), x - -3, var9, var10, 0); - } - - Unsorted.method1282(Class21.anInt1462, Class21.anInt3395, Class21.anInt3537, Class21.anInt3552); - } -} diff --git a/Client/src/main/java/org/runite/client/drawcalls/LoadingBox.java b/Client/src/main/java/org/runite/client/drawcalls/LoadingBox.java deleted file mode 100644 index 8f4afb4f4..000000000 --- a/Client/src/main/java/org/runite/client/drawcalls/LoadingBox.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.runite.client.drawcalls; - -import org.rs09.client.rendering.Toolkit; -import org.runite.client.*; - -import java.awt.*; - -public final class LoadingBox { - - public static void draw(boolean var1, RSString var2) { - byte var3 = 4; - int var4 = var3 + 6; - int var5 = var3 + 6; - int var6 = FontType.plainFont.method680(var2, 250); - int var7 = FontType.plainFont.method684(var2, 250) * 13; - //Used for the top left (please wait...) - Toolkit.getActiveToolkit().method934(var4 - var3, -var3 + var5, var3 + var6 - -var3, var3 + var3 + var7, 0); - Toolkit.getActiveToolkit().drawRect(-var3 + var4, -var3 + var5, var6 + var3 - -var3, var3 + var7 + var3, 16777215, 255); - - FontType.plainFont.method676(var2, var4, var5, var6, var7, 16777215, -1, 1, 1, 0); - - Class21.method1340(var4 + -var3, var6 + (var3 - -var3), -var3 + var5, var3 + var7 + var3); - if (var1) { - if (HDToolKit.highDetail) { - HDToolKit.bufferSwap(); - } else { - try { - Graphics var8 = GameShell.canvas.getGraphics(); - Unsorted.aClass158_3009.method2179(var8); - } catch (Exception var9) { - GameShell.canvas.repaint(); - } - } - } else { - Unsorted.method1282(var4, var5, var7, var6); - } - } -} diff --git a/Client/src/main/java/org/runite/client/drawcalls/Minimap.java b/Client/src/main/java/org/runite/client/drawcalls/Minimap.java deleted file mode 100644 index 7b0e805f4..000000000 --- a/Client/src/main/java/org/runite/client/drawcalls/Minimap.java +++ /dev/null @@ -1,219 +0,0 @@ -package org.runite.client.drawcalls; - -import org.rs09.client.rendering.Toolkit; -import org.runite.client.*; - -import java.util.Objects; - -public final class Minimap { - - public static void displayMinimap(int var0, byte var1, int var2, int var3, RSInterface var4) { - Class58.method1194(); - - if (HDToolKit.highDetail) { - Class22.setClipping(var3, var2, var3 + var4.width, var2 + var4.height); - } else { - Class74.setClipping(var3, var2, var3 - -var4.width, var2 + var4.height); - } - - if (2 != Class161.anInt2028 && 5 != Class161.anInt2028 && Class49.aAbstractSprite_812 != null) { - int var19 = TextureOperation9.anInt3102 + GraphicDefinition.CAMERA_DIRECTION & 0x7FF;//Region Rotation (relative to player camera) - int var6 = Class102.player.xAxis / 32 + 48;//Minimap X Axis (relative to player) Used as offset - int var7 = -(Class102.player.zAxis / 32) + 464;//Minimap Y Axis (relative to player) Used as offset - if (HDToolKit.highDetail) { - ((HDSprite) Class49.aAbstractSprite_812).drawMinimapRegion(var3, var2, var4.width, var4.height, var6, var7, var19, Class164_Sub2.anInt3020 + 256, (HDSprite) var4.method866(false)); - } else { - ((SoftwareSprite) Class49.aAbstractSprite_812).drawMinimapRegion(var3, var2, var4.width, var4.height, var6, var7, var19, 256 - -Class164_Sub2.anInt3020, var4.anIntArray207, var4.anIntArray291); - } - - int var9; - int var10; - int var11; - int var12; - int var13; - int var14; - int var17; - int var16; - if (null != TextureOperation22.aClass131_3421) { - for (int var8 = 0; var8 < TextureOperation22.aClass131_3421.anInt1720; ++var8) { - if (TextureOperation22.aClass131_3421.method1789(var8, var1 ^ 553)) { - var9 = 2 + 4 * (TextureOperation22.aClass131_3421.aShortArray1727[var8] + -Class131.anInt1716) + -(Class102.player.xAxis / 32); - var11 = Class51.anIntArray840[var19]; - var12 = Class51.anIntArray851[var19]; - Font var15 = FontType.smallFont; - var11 = var11 * 256 / (256 + Class164_Sub2.anInt3020); - var10 = 2 + 4 * (-Texture.anInt1152 + TextureOperation22.aClass131_3421.aShortArray1718[var8]) - Class102.player.zAxis / 32; - var12 = var12 * 256 / (256 + Class164_Sub2.anInt3020); - var14 = -(var9 * var11) + var10 * var12 >> 16; - if (TextureOperation22.aClass131_3421.method1791(var8, var1 + -51) == 1) { - var15 = FontType.plainFont; - } - - if (2 == TextureOperation22.aClass131_3421.method1791(var8, 8)) { - var15 = FontType.bold; - } - - var13 = var11 * var10 - -(var12 * var9) >> 16; - var16 = var15.method680(TextureOperation22.aClass131_3421.aClass94Array1721[var8], 100); - var13 -= var16 / 2; - if (-var4.width <= var13 && var13 <= var4.width && var14 >= -var4.height && var14 <= var4.height) { - var17 = 16777215; - if (TextureOperation22.aClass131_3421.anIntArray1725[var8] != -1) { - var17 = TextureOperation22.aClass131_3421.anIntArray1725[var8]; - } - - if (HDToolKit.highDetail) { - Class22.method936((HDSprite) Objects.requireNonNull(var4.method866(false))); - } else { - Class74.method1314(var4.anIntArray207, var4.anIntArray291); - } - - var15.method693(TextureOperation22.aClass131_3421.aClass94Array1721[var8], var3 + var13 + var4.width / 2, var2 + var4.height / 2 + -var14, var16, 50, var17, 0, 1, 0, 0); - if (HDToolKit.highDetail) { - Class22.method921(); - } else { - Class74.method1310(); - } - } - } - } - } - - for (var9 = 0; MouseListeningClass.anInt1924 > var9; ++var9) { - var10 = -(Class102.player.xAxis / 32) + 2 + 4 * Class84.anIntArray1163[var9]; - var11 = -(Class102.player.zAxis / 32) + 2 + (Unsorted.anIntArray4050[var9] * 4); - ObjectDefinition var20 = ObjectDefinition.getObjectDefinition(Class3_Sub19.anIntArray3693[var9]); - if (null != var20.ChildrenIds) { - var20 = var20.method1685(var1 + -59); - if (null == var20 || var20.MapIcon == -1) { - continue; - } - } - - Class38_Sub1.minimapIcons(var4, Class140_Sub4.aAbstractSpriteArray2839[var20.MapIcon], var11, var10, var2, var3); - } - - for (var9 = 0; 104 > var9; ++var9) { - for (var10 = 0; var10 < 104; ++var10) { - LinkedList var25 = Class39.aLinkedListArrayArrayArray3273[WorldListCountry.localPlane][var9][var10]; - if (null != var25) { - var12 = 2 + var9 * 4 + -(Class102.player.xAxis / 32); - var13 = -(Class102.player.zAxis / 32) + 2 + 4 * var10; - Class38_Sub1.minimapIcons(var4, Unsorted.aAbstractSpriteArray1136[0], var13, var12, var2, var3); - } - } - } - - for (var9 = 0; var9 < Class163.localNPCCount; ++var9) { - NPC var21 = NPC.npcs[AudioThread.localNPCIndexes[var9]]; - if (var21 != null && var21.hasDefinitions()) { - NPCDefinition var22 = var21.definition; - if (null != var22 && null != var22.childNPCs) { - var22 = var22.method1471((byte) -3); - } - - if (var22 != null && var22.aBoolean1285 && var22.aBoolean1270) { - var12 = var21.xAxis / 32 - Class102.player.xAxis / 32; - var13 = var21.zAxis / 32 + -(Class102.player.zAxis / 32); - if (var22.anInt1283 == -1) { - Class38_Sub1.minimapIcons(var4, Unsorted.aAbstractSpriteArray1136[1], var13, var12, var2, var3); - } else { - Class38_Sub1.minimapIcons(var4, Class140_Sub4.aAbstractSpriteArray2839[var22.anInt1283], var13, var12, var2, var3); - } - } - } - } - - for (var9 = 0; var9 < Class159.localPlayerCount; ++var9) { - Player var23 = Unsorted.players[Class56.localPlayerIndexes[var9]]; - if (null != var23 && var23.hasDefinitions()) { - var12 = var23.zAxis / 32 - Class102.player.zAxis / 32; - var11 = -(Class102.player.xAxis / 32) + var23.xAxis / 32; - long var29 = var23.displayName.toLong(); - boolean var28 = false; - - for (var16 = 0; var16 < Class8.anInt104; ++var16) { - if (Class50.aLongArray826[var16] == var29 && 0 != Unsorted.anIntArray882[var16]) { - var28 = true; - break; - } - } - - boolean var31 = false; - - for (var17 = 0; Unsorted.clanSize > var17; ++var17) { - if (var29 == PacketParser.aClass3_Sub19Array3694[var17].linkableKey) { - var31 = true; - break; - } - } - - boolean var32 = false; - if (Class102.player.teamId != 0 && 0 != var23.teamId && var23.teamId == Class102.player.teamId) { - var32 = true; - } - - if (var28) { - Class38_Sub1.minimapIcons(var4, Unsorted.aAbstractSpriteArray1136[3], var12, var11, var2, var3); - } else if (var31) { - Class38_Sub1.minimapIcons(var4, Unsorted.aAbstractSpriteArray1136[5], var12, var11, var2, var3); - } else if (var32) { - Class38_Sub1.minimapIcons(var4, Unsorted.aAbstractSpriteArray1136[4], var12, var11, var2, var3); - } else { - Class38_Sub1.minimapIcons(var4, Unsorted.aAbstractSpriteArray1136[2], var12, var11, var2, var3); - } - } - } - - Class96[] var24 = ClientErrorException.aClass96Array2114; - - for (var10 = 0; var24.length > var10; ++var10) { - Class96 var26 = var24[var10]; - if (null != var26 && var26.anInt1360 != 0 && Class44.anInt719 % 20 < 10) { - if (var26.anInt1360 == 1 && var26.anInt1359 >= 0 && var26.anInt1359 < NPC.npcs.length) { - NPC var27 = NPC.npcs[var26.anInt1359]; - if (null != var27) { - var13 = -(Class102.player.xAxis / 32) + var27.xAxis / 32; - var14 = var27.zAxis / 32 + -(Class102.player.zAxis / 32); - Class53.method1171(var26.anInt1351, var2, var3, var13, var14, var4); - } - } - - if (var26.anInt1360 == 2) { - var12 = (-Class131.anInt1716 + var26.anInt1356) * 4 + 2 - Class102.player.xAxis / 32; - var13 = -(Class102.player.zAxis / 32) + 2 + (-Texture.anInt1152 + var26.anInt1347) * 4; - Class53.method1171(var26.anInt1351, var2, var3, var12, var13, var4); - } - - if (var26.anInt1360 == 10 && var26.anInt1359 >= 0 && Unsorted.players.length > var26.anInt1359) { - Player var30 = Unsorted.players[var26.anInt1359]; - if (null != var30) { - var14 = var30.zAxis / 32 + -(Class102.player.zAxis / 32); - var13 = var30.xAxis / 32 + -(Class102.player.xAxis / 32); - Class53.method1171(var26.anInt1351, var2, var3, var13, var14, var4); - } - } - } - } - - if (Class65.anInt987 != 0) { - var9 = 4 * Class65.anInt987 + (2 - Class102.player.xAxis / 32); - var10 = 2 + 4 * Class45.anInt733 - Class102.player.zAxis / 32; - Class38_Sub1.minimapIcons(var4, Class45.aAbstractSprite_736, var10, var9, var2, var3); - } - Toolkit.getActiveToolkit().method934(-1 + (var3 - -(var4.width / 2)), -1 + var2 - -(var4.height / 2), 3, 3, 16777215); - - } else if (HDToolKit.highDetail) { - AbstractSprite var5 = var4.method866(false); - if (null != var5) { - var5.drawAt(var3, var2); - } - } else { - Class74.method1332(var3, var2, var4.anIntArray207, var4.anIntArray291); - } - - if (var1 == 59) { - Class163_Sub1_Sub1.aBooleanArray4008[var0] = true; - } - } -} diff --git a/Client/src/main/java/org/runite/client/drawcalls/StartupLoadingBar.java b/Client/src/main/java/org/runite/client/drawcalls/StartupLoadingBar.java deleted file mode 100644 index 8fdc0fe0e..000000000 --- a/Client/src/main/java/org/runite/client/drawcalls/StartupLoadingBar.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.runite.client.drawcalls; - -import org.rs09.client.rendering.Toolkit; -import org.runite.client.*; - -public final class StartupLoadingBar { - - public static void draw(boolean var1, Font var2) { - int var3; - if (HDToolKit.highDetail || var1) { - var3 = Class140_Sub7.canvasHeight; - int var4 = var3 * 956 / 503; - Class40.aAbstractSprite_680.method639((Class23.canvasWidth + -var4) / 2, 0, var4, var3); - SequenceDefinition.aClass109_1856.method1667(-(SequenceDefinition.aClass109_1856.width / 2) + Class23.canvasWidth / 2, 18); - } - - var2.method699(TextCore.RSLoadingPleaseWait, Class23.canvasWidth / 2, Class140_Sub7.canvasHeight / 2 - 26, 16777215, -1); - var3 = Class140_Sub7.canvasHeight / 2 + -18; - Toolkit.getActiveToolkit().drawRect(Class23.canvasWidth / 2 - 152, var3, 304, 34, 9179409, 255); - Toolkit.getActiveToolkit().drawRect(-151 + Class23.canvasWidth / 2, var3 - -1, 302, 32, 0, 255); - Toolkit.getActiveToolkit().method934(Class23.canvasWidth / 2 - 150, var3 + 2, Client.LoadingStageNumber * 3, 30, 9179409); - Toolkit.getActiveToolkit().method934(Class23.canvasWidth / 2 + -150 - -(3 * Client.LoadingStageNumber), 2 + var3, 300 + -(3 * Client.LoadingStageNumber), 30, 0); - - var2.method699(Client.loadingBarTextToDisplay, Class23.canvasWidth / 2, 4 + Class140_Sub7.canvasHeight / 2, 16777215, -1); - } -} diff --git a/Client/src/main/java/org/runite/client/drawcalls/StartupLoadingBarInitial.java b/Client/src/main/java/org/runite/client/drawcalls/StartupLoadingBarInitial.java deleted file mode 100644 index 6e9f3d2fd..000000000 --- a/Client/src/main/java/org/runite/client/drawcalls/StartupLoadingBarInitial.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.runite.client.drawcalls; - -import org.runite.client.*; - -import java.awt.*; - -public class StartupLoadingBarInitial { - - - public static FontMetrics aFontMetrics1822; - public static Image anImage2695; - - public static void draw(Color var0, boolean var2, RSString var3, int var4) { - - try { - Graphics var5 = GameShell.canvas.getGraphics(); - aFontMetrics1822 = GameShell.canvas.getFontMetrics(TextCore.Helvetica); - if (var2) { - var5.setColor(Color.black); - var5.fillRect(0, 0, Class23.canvasWidth, Class140_Sub7.canvasHeight); - } - - try { - if (anImage2695 == null) { - anImage2695 = GameShell.canvas.createImage(304, 34); - } - - Graphics var6 = anImage2695.getGraphics(); - var6.setColor(var0); - var6.drawRect(0, 0, 303, 33); - var6.fillRect(2, 2, var4 * 3, 30); - var6.setColor(Color.black); - var6.drawRect(1, 1, 301, 31); - var6.fillRect(3 * var4 + 2, 2, -(3 * var4) + 300, 30); - var6.setFont(TextCore.Helvetica); - var6.setColor(Color.white); - var3.drawString(var6, 22, (-var3.method1575(aFontMetrics1822) + 304) / 2); - var5.drawImage(anImage2695, Class23.canvasWidth / 2 - 152, -18 + Class140_Sub7.canvasHeight / 2, null); - } catch (Exception var9) { - int var7 = -152 + Class23.canvasWidth / 2; - int var8 = -18 + Class140_Sub7.canvasHeight / 2; - var5.setColor(var0); - var5.drawRect(var7, var8, 303, 33); - var5.fillRect(var7 + 2, 2 + var8, 3 * var4, 30); - var5.setColor(Color.black); - var5.drawRect(1 + var7, var8 - -1, 301, 31); - var5.fillRect(3 * var4 + (var7 - -2), 2 + var8, 300 - var4 * 3, 30); - var5.setFont(TextCore.Helvetica); - var5.setColor(Color.white); - var3.drawString(var5, 22 + var8, var7 + (-var3.method1575(aFontMetrics1822) + 304) / 2); - } - - if (Class167.aClass94_2083 != null) { - var5.setFont(TextCore.Helvetica); - var5.setColor(Color.white); - Class167.aClass94_2083.drawString(var5, Class140_Sub7.canvasHeight / 2 - 26, Class23.canvasWidth / 2 - Class167.aClass94_2083.method1575(aFontMetrics1822) / 2); - } - - } catch (Exception var10) { - GameShell.canvas.repaint(); - } - } -} diff --git a/Client/src/main/kotlin/org/rs09/CustomVars.kt b/Client/src/main/kotlin/org/rs09/CustomVars.kt deleted file mode 100644 index a84528bf6..000000000 --- a/Client/src/main/kotlin/org/rs09/CustomVars.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.rs09 - -import org.rs09.client.config.GameConfig - -/** - * Handles custom client vars because I did it the lazy way - * @author Ceikry - */ -object CustomVars { - @JvmStatic - fun parse(varID: Int, varValue: Int): Boolean{ - return when(varID){ - 2501 -> { - GameConfig.xpDropsEnabled = (varValue and 1) == 1 - GameConfig.xpDropMode = (varValue shr 1) and 1 - GameConfig.xpTrackMode = (varValue shr 2) and 1 - true - } - 2502 -> { - GameConfig.slayerCountEnabled = true - GameConfig.slayerTaskID = varValue and 0x7F - GameConfig.setSlayerAmount((varValue shr 7) and 0xFF) - SlayerTracker.setSprite() - SystemLogger.logInfo(varValue.toString()) - true - } - else -> false - } - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/Discord.kt b/Client/src/main/kotlin/org/rs09/Discord.kt deleted file mode 100644 index 8238cf03f..000000000 --- a/Client/src/main/kotlin/org/rs09/Discord.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.rs09 - -import net.arikia.dev.drpc.DiscordEventHandlers -import net.arikia.dev.drpc.DiscordRPC -import net.arikia.dev.drpc.DiscordRichPresence - -/** - * Handles discord rich presence integration - * @author Ceikry - * library thanks to Vatuu on Github - */ -object Discord { - const val APP_ID = "812421731238019073"; - var presence = DiscordRichPresence() - var initialized = false - - @JvmStatic - fun checkInitializable() : Boolean{ - val OS = System.getProperty("os.name") - if(OS.toLowerCase().startsWith("windows") || OS.toLowerCase().contains("nux")){ - val arch = System.getProperty("os.arch") - return arch != "aarch64" && !initialized - } - return false - } - - @JvmStatic - fun initialize() { - val handlers = DiscordEventHandlers.Builder().setReadyEventHandler { user -> - SystemLogger.logDiscord("Discord RPC Initialized, registered to " + user.username + "#" + user.discriminator) - }.setErroredEventHandler { errorCode, message -> - SystemLogger.logDiscord("Discord error encountered: [$errorCode] $message") - DiscordRPC.discordShutdown() - }.build() - - //On Linux, when the discord library initializes, it can crash any other client loading the same library. - //Not sure what causes this, likely a JVM bug to be honest. - DiscordRPC.discordInitialize(APP_ID, handlers, true) - initialized = true - } - - @JvmStatic - fun updatePresence(stateMessage: String,details: String,imageKey : String) { - if(presence.state == stateMessage && presence.details == details && imageKey == presence.smallImageKey) return - val newPresence = DiscordRichPresence.Builder(stateMessage).setDetails(details).build() - newPresence.startTimestamp = System.currentTimeMillis() - if(newPresence.details.startsWith("Training")){ - newPresence.smallImageKey = imageKey - } - newPresence.largeImageKey = "logo" - newPresence.largeImageText = "2009Scape, a free MMO." - DiscordRPC.discordUpdatePresence(newPresence) - presence = newPresence - SystemLogger.logDiscord("Updating presence ($stateMessage,$details,$imageKey)") - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/SlayerTracker.kt b/Client/src/main/kotlin/org/rs09/SlayerTracker.kt deleted file mode 100644 index b0f2154e4..000000000 --- a/Client/src/main/kotlin/org/rs09/SlayerTracker.kt +++ /dev/null @@ -1,151 +0,0 @@ -package org.rs09 - -import org.rs09.client.config.GameConfig -import org.rs09.client.rendering.RenderingUtils -import org.rs09.client.rendering.Toolkit -import org.runite.client.* -import java.lang.Exception -import java.util.concurrent.TimeUnit - -object SlayerTracker { - const val posX = 5 - const val posY = 30 - const val textX = 7 - const val textY = 50 - const val spriteY = 30 - val boxColor = GameConfig.slayerTrackerColor.toIntOrNull(16) ?: 6116423 - - @JvmField - var lastUpdate = 0L - - var curSprite: AbstractSprite? = null - - - @JvmStatic - fun draw(){ - val timeSinceUpdate = System.currentTimeMillis() - lastUpdate - if(!GameConfig.slayerCountEnabled) return - if(GameConfig.slayerTaskAmount <= 0) return - if(timeSinceUpdate >= TimeUnit.MINUTES.toMillis(5L)) return - val tk = Toolkit.getActiveToolkit() - var rectWidth = 60 - var amountPos = textX + 40 - if(rectWidth < 90) rectWidth = 90 - if(amountPos < textX + 60) amountPos = textX + 60 - tk.fillRect(posX,posY,rectWidth,30, boxColor,GameConfig.slayerTrackerOpacity) - - curSprite?.drawAt(textX, spriteY) - RenderingUtils.drawText(GameConfig.slayerTaskAmount.toString(),amountPos, textY, -1, 2, false) - } - - @JvmStatic - fun reset(){ - GameConfig.slayerTaskAmount = 0 - GameConfig.slayerTaskID = 0 - GameConfig.slayerCountEnabled = false; - lastUpdate = 0 - } - - @JvmStatic - fun setSprite(){ - try { - SystemLogger.logInfo("Getting slayer sprite...") - val itemId: Int = when (GameConfig.slayerTaskID) { - 0 -> 4144 - 1 -> 4149 - 2 -> 560 - 3 -> 10176 - 4 -> 4135 - 5 -> 4139 - 6 -> 14072 - 7 -> 948 - 8 -> 12189 - 9 -> 3098 - 10 -> 1747 - 11 -> 4141 - 12 -> 1751 - 13 -> 11047 - 14 -> 2349 - 15 -> 9008 - 16 -> 4521 - 17 -> 4134 - 18 -> 8900 - 19 -> 4520 - 20 -> 4137 - 21 -> 1739 - 22 -> 7982 - 23 -> 10149 - 24 -> 532 - 25 -> 8141 - 26 -> 6637 - 27 -> 6695 - 28 -> 8132 - 29 -> 4145 - 30 -> 7500 - 31 -> 1422 - 32 -> 1387 - 33 -> 9011 - 34 -> 4147 - 35 -> 552 - 36 -> 6722 - 37 -> 10998 - 38 -> 9016 - 39 -> 2402 - 40 -> 1753 - 41 -> 7050 - 42 -> 8137 - 43 -> 12570 - 44 -> 8133 - 45 -> 4671 - 46 -> 4671 - 47 -> 1159 - 48 -> 4140 - 49 -> 2351 - 50 -> 4142 - 51 -> 7778 - 52 -> 8139 - 53 -> 4146 - 54 -> 2402 - 55 -> 2359 - 56 -> 12079 - 57 -> 12201 - 58 -> 12570 - 59 -> 4148 - 60 -> 4818 - 61 -> 6107 - 62 -> 4138 - 63 -> 14074 - 64 -> 4136 - 65 -> 6297 - 66 -> 10634 - 67 -> 553 - 68 -> 8135 - 69 -> 11732 - 70 -> 10284 - 71 -> 13923 - 72 -> 2353 - 73 -> 8136 - 74 -> 4143 - 75 -> 6528 - 76 -> 10109 - 77 -> 1403 - 78 -> 2952 - 79 -> 958 - 80 -> 7594 - 89 -> 6811 - else -> -1 - } - - var sprite = AbstractSprite.constructItemSprite(0, HDToolKit.highDetail, itemId, false, 1, 1, false) - if (HDToolKit.highDetail) { - if (sprite is Class3_Sub28_Sub16_Sub2_Sub1) { - sprite = Class3_Sub28_Sub16_Sub1_Sub1(sprite as SoftwareSprite) - } else { - sprite = HDSprite(sprite as SoftwareSprite) - } - } - - curSprite = sprite - } catch (ignored: Exception){} - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/SystemLogger.kt b/Client/src/main/kotlin/org/rs09/SystemLogger.kt deleted file mode 100644 index dcaf8296b..000000000 --- a/Client/src/main/kotlin/org/rs09/SystemLogger.kt +++ /dev/null @@ -1,50 +0,0 @@ -package org.rs09 - -import com.github.ajalt.mordant.terminal.Terminal -import com.github.ajalt.mordant.rendering.TextColors.* -import org.rs09.client.config.GameConfig -import java.text.SimpleDateFormat -import java.util.* - -/** - * Handles client logging - * @author Ceikry - * Thanks to Aj Alt for the awesome Mordant library! - */ -object SystemLogger { - val formatter = SimpleDateFormat("HH:mm:ss") - val t = Terminal() - - fun getTime(): String{ - return "[" + formatter.format(Date(System.currentTimeMillis())) +"]" - } - - @JvmStatic - fun logInfo(vararg messages: String){ - for(m in messages){ - if(m.isNotBlank()) t.println("${getTime()}: [INFO] $m") - } - } - - @JvmStatic - fun logErr(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${brightRed("[ ERR] $message")}") - } - - @JvmStatic - fun logWarn(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${yellow("[WARN] $message")}") - } - - @JvmStatic - fun logDiscord(message: String){ - t.println("${getTime()}: ${brightBlue("[DISC] $message")}") - } - - @JvmStatic - fun logVerbose(message: String){ - if(GameConfig.VERBOSE_LOGGING){ - logInfo(message) - } - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/XPGainDraw.kt b/Client/src/main/kotlin/org/rs09/XPGainDraw.kt deleted file mode 100644 index 558cd5043..000000000 --- a/Client/src/main/kotlin/org/rs09/XPGainDraw.kt +++ /dev/null @@ -1,204 +0,0 @@ -package org.rs09 - -import org.rs09.client.config.GameConfig -import org.rs09.client.rendering.RenderingUtils -import org.rs09.client.rendering.Toolkit -import org.runite.client.* -import java.util.* -import kotlin.math.ceil - -/** - * Handles tracking and drawing of xp drops - * @author Ceikry - */ -object XPGainDraw { - internal class XPGain(var skillId: Int, var xpGain: Int, var currentPos: Int) - @JvmStatic - var lastXp = Array(24){0} - - @JvmStatic - private var thisLoopGains = ArrayList() - - var posX = RenderingUtils.width / 2 - var posY = RenderingUtils.height / 4 - var skillSprite: AbstractSprite? = null - var lastSkillId = 0 - - var gainsAddedThisLoop = 0 - var totalXP = 0 - var lastUpdate = 0L - var lastGain = 0L - - const val line = 110 - const val clearHeight = 60 - - @JvmStatic - fun addGain(xpGain: Int,skillId: Int){ - if(GameConfig.xpDropMode == 1){ - totalXP += xpGain - } - if(xpGain <= 0) return - if(gainsAddedThisLoop > 6) return - if(lastXp[skillId] == 0){ - if(GameConfig.xpDropMode == 0) { - totalXP += xpGain - } - return - } - var drawAt = line - if(thisLoopGains.isNotEmpty()) { - if (thisLoopGains[thisLoopGains.size - 1].currentPos + 25 > line) { - drawAt = thisLoopGains[thisLoopGains.size - 1].currentPos + 25 - } - } - thisLoopGains.add(XPGain(skillId,xpGain,drawAt)) - gainsAddedThisLoop++ - if(System.currentTimeMillis() - lastUpdate > 10000) lastUpdate = System.currentTimeMillis() - lastGain = System.currentTimeMillis() - } - - @JvmStatic - fun drawGains() { - if(!GameConfig.xpDropsEnabled){ - return - } - if(System.currentTimeMillis() - lastGain > 10000) return - posX = RenderingUtils.width / 2 - posY = RenderingUtils.height / 4 - if(posX == 382) { //Client is in fixed mode - posX += 60 - } - drawTotal() - if(GameConfig.xpTrackMode == 1) { - skillSprite?.drawAt(posX - 65, 10) - RenderingUtils.drawText(addCommas(lastXp[lastSkillId].toString()), posX - 40, 28, -1, 2, false) - } - val timeDelta = getTimeDelta() - val removeList = ArrayList() - for(gain in thisLoopGains) { - gain.currentPos -= ceil(timeDelta / 20.0).toInt() - if(gain.currentPos <= clearHeight){ - if(GameConfig.xpDropMode == 0) { - totalXP += gain.xpGain - } - removeList.add(gain) - continue; - } - - val sprite = getSprite(getSpriteArchive(gain.skillId)) - sprite?.drawAt((posX) - 25, gain.currentPos - 20) - RenderingUtils.drawText("${gain.xpGain}",posX, gain.currentPos,-1,2,false) - skillSprite = sprite - lastSkillId = gain.skillId - } - lastUpdate = System.currentTimeMillis() - thisLoopGains.removeAll(removeList) - gainsAddedThisLoop = 0 - if(Discord.initialized) { - Discord.updatePresence("In-Game", "Training", "skill_$lastSkillId") - } - } - - fun drawTotal() { - val tk = Toolkit.getActiveToolkit() - RenderingUtils.setClipping(0,0,RenderingUtils.width,500) - val horizontal = getSprite(822) - val horizontalTop = getSprite(820) - val tl_corner = getSprite(824) - val bl_corner = getSprite(826) - val tr_corner = getSprite(825) - val br_corner = getSprite(827) - val bg = getSprite(657) - bg?.drawAt(posX - 77,9) - tk.fillRect(posX - 75,5,140,30,0,64) - bl_corner?.drawAt(posX - 77,10) - tl_corner?.drawAt(posX - 77,5) - tr_corner?.drawAt(posX + 41,5) - br_corner?.drawAt(posX + 41,10) - horizontalTop?.drawAt(posX - 45,-8) - horizontal?.drawAt(posX - 45,22) - horizontalTop?.drawAt(posX - 15,-8) - horizontal?.drawAt(posX - 15,22) - horizontalTop?.drawAt(posX + 9,-8) - horizontal?.drawAt(posX + 9,22) - if(GameConfig.xpTrackMode == 0){ - RenderingUtils.drawText("Total XP: " + addCommas(totalXP.toString()),posX - 65,28,-1,2,false) - } - } - - fun getSpriteArchive(skillId: Int): Int{ - return when(skillId) { - 0 -> 197 - 1 -> 199 - 2 -> 198 - 3 -> 203 - 4 -> 200 - 5 -> 201 - 6 -> 202 - 7 -> 212 - 8 -> 214 - 9 -> 208 - 10 -> 211 - 11 -> 213 - 12 -> 207 - 13 -> 210 - 14 -> 209 - 15 -> 205 - 16 -> 204 - 17 -> 206 - 18 -> 216 - 19 -> 217 - 20 -> 215 - 21 -> 220 - 22 -> 221 - 23 -> 222 - else -> 222 - } - } - - fun getSprite(archiveIndex: Int): AbstractSprite? { - var rawSprite: AbstractSprite? = null - if(CacheIndex.spritesIndex.retrieveSpriteFile(archiveIndex)){ - rawSprite = Unsorted.method562(CacheIndex.spritesIndex, archiveIndex) - if(HDToolKit.highDetail){ - if(rawSprite is Class3_Sub28_Sub16_Sub2_Sub1){ - rawSprite = Class3_Sub28_Sub16_Sub1_Sub1(rawSprite as SoftwareSprite) - } else { - rawSprite = HDSprite(rawSprite as SoftwareSprite) - } - } - } - return rawSprite - } - - fun addCommas(num: String): String{ - var newString = "" - if(num.length > 9){ - return "Lots!" - } - var counter = 1 - num.reversed().forEach { - if(counter % 3 == 0 && counter != num.length){ - newString += "$it," - } else { - newString += it - } - counter++ - } - return newString.reversed() - } - - @JvmStatic - fun reset(){ - totalXP = 0 - lastXp = Array(24){0} - lastUpdate = 0 - thisLoopGains.clear() - lastSkillId = 0 - skillSprite = null - } - - fun getTimeDelta(): Long{ - return System.currentTimeMillis() - lastUpdate - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/DataBuffer.kt b/Client/src/main/kotlin/org/rs09/client/DataBuffer.kt deleted file mode 100644 index 2071a829a..000000000 --- a/Client/src/main/kotlin/org/rs09/client/DataBuffer.kt +++ /dev/null @@ -1,107 +0,0 @@ -package org.rs09.client - -import org.rs09.client.util.ByteArrayPool -import org.runite.client.RSString - -open class DataBuffer(@JvmField val buffer: ByteArray, @JvmField var index: Int) : Linkable() { - constructor(buffer: ByteArray) : this(buffer, 0) - constructor(length: Int) : this(ByteArrayPool.getByteArray(length), 0) - - fun readUnsignedByte(): Int { - return buffer[index++].toInt() and 0xff - } - - fun readUnsignedShort(): Int { - index += 2 - return (buffer[index - 1].toInt() and 0xff) + - (buffer[index - 2].toInt() and 0xff shl 8) - } - - fun readInt(): Int { - index += 4 - return (buffer[index - 4].toInt() and 0xff shl 24) + - (buffer[index - 3].toInt() and 0xff shl 16) + - (buffer[index - 2].toInt() and 0xff shl 8) + - (buffer[index - 1].toInt() and 0xff) - } - - fun writeInt(value: Int) { - buffer[index++] = (value shr 24).toByte() - buffer[index++] = (value shr 16).toByte() - buffer[index++] = (value shr 8).toByte() - buffer[index++] = value.toByte() - } - - fun writeLong(value: Long) { - buffer[index++] = (value shr 56).toInt().toByte() - buffer[index++] = (value shr 48).toInt().toByte() - buffer[index++] = (value shr 40).toInt().toByte() - buffer[index++] = (value shr 32).toInt().toByte() - buffer[index++] = (value shr 24).toInt().toByte() - buffer[index++] = (value shr 16).toInt().toByte() - buffer[index++] = (value shr 8).toInt().toByte() - buffer[index++] = value.toInt().toByte() - } - - fun readBytes(length: Int): ByteArray { - val out = ByteArray(length) - System.arraycopy(buffer, index, out, 0, length) - index += length - return out - } - - fun write128Byte(value: Int) { - buffer[index++] = (128 - value).toByte() - } - - fun writeString(value: RSString) { - index += value.method1580(buffer, index, value.length()) - buffer[index++] = 0 - } - - /* - * TODO Refactor / rename everything beyond this point - */ - - fun method739(var2: Int, var3: Long) { - var var2 = var2 - --var2 - if (var2 >= 0 && var2 <= 7) { - var var5 = var2 * 8 - while (0 <= var5) { - buffer[index++] = (var3 shr var5).toInt().toByte() - var5 -= 8 - } - } else { - throw IllegalArgumentException() - } - } - - fun method741(): Int { - var var2 = buffer[index++].toInt() - var var3: Int - var3 = 0 - while (0 > var2) { - var3 = 127 and var2 or var3 shl 7 - var2 = buffer[index++].toInt() - } - return var2 or var3 - } - - fun method742(var2: Int) { - buffer[index + -var2 - 4] = (var2 shr 24).toByte() - buffer[index + -var2 - 3] = (var2 shr 16).toByte() - buffer[index + -var2 - 2] = (var2 shr 8).toByte() - buffer[index + -var2 - 1] = var2.toByte() - } - - fun readSignedShort128(): Int { - index += 2 - var value: Int = (buffer[index - 2].toInt() and 0xff shl 8) + - (buffer[index - 1].toInt() - 128 and 0xff) - if (value > 32767) { - value -= 65536 - } - return value - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/GameLaunch.kt b/Client/src/main/kotlin/org/rs09/client/GameLaunch.kt deleted file mode 100644 index 91abfe220..000000000 --- a/Client/src/main/kotlin/org/rs09/client/GameLaunch.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.rs09.client - -import org.rs09.SystemLogger -import org.rs09.client.config.GameConfig -import org.runite.client.GameShell -import java.io.File -import java.io.FileOutputStream - -object GameLaunch { - - /** - * The main method. - * r @param args the arguments casted on runtime. - * r_game - */ - @JvmStatic - fun main(args: Array) { - for (i in args.indices) { - val cmd = args[i].split("=").toTypedArray() - when (cmd[0]) { - "ip" -> GameConfig.IP_ADDRESS = cmd[1] - "world" -> GameConfig.WORLD_OVERRIDE = cmd[1].toInt() - else -> GameConfig.configLocation = cmd[0] - } - } - try { - SystemLogger.logInfo("Trying to parse config at " + GameConfig.configLocation) - GameConfig.parse(GameConfig.configLocation) - GameConfig.implementHoliday() - GameConfig.extendRenderDistance() - } catch (e: Exception){ - GameConfig.IP_ADDRESS = "play.2009scape.org" - GameConfig.IP_MANAGEMENT = "play.2009scape.org" - GameConfig.JS5_SERVER_PORT = 43593 - GameConfig.SERVER_PORT = 43594 - GameConfig.WL_PORT = 5555 - GameConfig.RCM_STYLE_PRESET = "classic" - GameConfig.RCM_TITLE = "Choose Option" - GameConfig.HOLIDAYS_ENABLED = true - GameConfig.implementHoliday() - GameConfig.RENDER_DISTANCE_INCREASE = true - GameConfig.extendRenderDistance() - SystemLogger.logWarn("Config file ${GameConfig.configLocation} not found, using defaults.") - } - /** - * Launches the client - */ - if(System.getProperty("os.name").toLowerCase().contains("nux")){ //Fixes crashing due to XInitThreads not being called - JVM bug - val istream = javaClass.classLoader.getResourceAsStream("libfixXInitThreads.so") - val buff = ByteArray(1024) - var read = -1 - val temp = File.createTempFile("libfixXInitThreads.so","") - val fos = FileOutputStream(temp) - - read = istream.read(buff) - while(read != -1){ - fos.write(buff, 0, read) - read = istream.read(buff) - } - - fos.close() - istream.close() - - System.load(temp.absolutePath) - } - - //Force IPv4 because sometimes Windows insists on using IPv6 regardless of what a service actually offers - System.setProperty("java.net.preferIPv4Stack" , "true"); - - GameShell.launchDesktop() - } - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/Linkable.kt b/Client/src/main/kotlin/org/rs09/client/Linkable.kt deleted file mode 100644 index abda84756..000000000 --- a/Client/src/main/kotlin/org/rs09/client/Linkable.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.rs09.client - -open class Linkable { - @JvmField - var linkableKey = 0L - @JvmField - var next: Linkable? = null - @JvmField - var previous: Linkable? = null - - fun isLinked(): Boolean = previous != null - - fun unlink() { - if (null != previous) { - previous!!.next = next - next!!.previous = previous - previous = null - next = null - } - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/LinkableInt.kt b/Client/src/main/kotlin/org/rs09/client/LinkableInt.kt deleted file mode 100644 index c04894d91..000000000 --- a/Client/src/main/kotlin/org/rs09/client/LinkableInt.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.rs09.client - -data class LinkableInt(@JvmField val value: Int = 0): Linkable() \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/Node.kt b/Client/src/main/kotlin/org/rs09/client/Node.kt deleted file mode 100644 index da6a3a5a7..000000000 --- a/Client/src/main/kotlin/org/rs09/client/Node.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.rs09.client - -open class Node : Linkable() { - @JvmField - var nodeKey: Long = 0 - @JvmField - var previousNode: Node? = null - @JvmField - var nextNode: Node? = null - - fun unlinkNode() { - if (previousNode != null) { - previousNode!!.nextNode = nextNode - nextNode!!.previousNode = previousNode - nextNode = null - previousNode = null - } - } - - companion object { - @JvmStatic - fun splice(insert: Node, target: Node) { - if (target.previousNode != null) { - target.unlinkNode() - } - target.previousNode = insert - target.nextNode = insert.nextNode - target.previousNode!!.nextNode = target - target.nextNode!!.previousNode = target - } - } -} diff --git a/Client/src/main/kotlin/org/rs09/client/config/GameConfig.backup b/Client/src/main/kotlin/org/rs09/client/config/GameConfig.backup deleted file mode 100644 index 61a7f1da7..000000000 --- a/Client/src/main/kotlin/org/rs09/client/config/GameConfig.backup +++ /dev/null @@ -1,349 +0,0 @@ -package org.rs09.client.config - -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import org.rs09.SlayerTracker -import java.io.FileReader -import java.math.BigInteger -import java.util.* - -/** - * Handles the client's config loading - * @author Ceikry - */ -class GameConfig { - companion object { - - /** - * Debug Booleans - */ - @JvmField - var ITEM_DEBUG_ENABLED = false - - @JvmField - var OBJECT_DEBUG_ENABLED = false - - @JvmField - var NPC_DEBUG_ENABLED = false - - @JvmField - var HD_LOGIN_DEBUG = false - - @JvmField - var HD_LOGIN_VERBOSE = false - - @JvmField - var CACHE_DEBUG = false - - @JvmField - var WORLD_MAP_DEBUG = false - - /** - * Context Menu Presets - */ - @JvmField - var RCM_STYLE_PRESET = "classic" - - /** - * Context Menu Customization - */ - @JvmField - var RCM_BG_COLOR = 6116423 - - @JvmField - var RCM_BG_OPACITY = 255 - - @JvmField - var RCM_TITLE_COLOR = 0 - - @JvmField - var RCM_TITLE_OPACITY = 255 - - @JvmField - var RCM_BORDER_COLOR = 0 - - @JvmField - var RCM_BORDER_OPACITY = 255 - - @JvmField - var RCM_TITLE = "Choose Option" - - @JvmField - var RS3_CONTEXT_STYLE = false - - /** - * Render distance - */ - @JvmField - var RENDER_DISTANCE_INCREASE = false - - @JvmField - var RENDER_DISTANCE_VALUE = 3584f - - @JvmField - var RENDER_DISTANCE_TILE_VALUE = 28 - - @JvmField - var RENDER_DISTANCE_FOG_FIX = 3328.0f - - @JvmField - var SKYBOX_COLOR = "float" - - /** - * Client Info - * Editable - */ - @JvmField - var IP_ADDRESS = "localhost" - - @JvmField - var IP_MANAGEMENT = "localhost" - - var JS5_SERVER_PORT = 43593 - - @JvmField - var SERVER_PORT = 43594 - - @JvmField - var WL_PORT = 5555 - - @JvmField - var WORLD = 1 - - @JvmField - var WORLD_OVERRIDE = -1 - - @JvmField - var LOGIN_THEME = "scape main" - - @JvmField - var xpDropsEnabled = true - - @JvmField - var xpDropMode = 0 - - @JvmField - var xpTrackMode = 0 - - @JvmField - var slayerCountEnabled = true - - @JvmField - var slayerTaskID = 0 - - @JvmField - var slayerTaskAmount = 0 - - @JvmField - var VERBOSE_LOGGING = false - - @JvmStatic - fun setSlayerAmount(amount : Int){ - slayerTaskAmount = amount - SlayerTracker.lastUpdate = System.currentTimeMillis() - } - - /** - * Json config Parser - */ - @JvmStatic - fun parse(path: String){ - val reader = FileReader(path) - val parser = JSONParser() - val data = parser.parse(reader) as JSONObject - - //Networking - if(data.containsKey("ip_address")) IP_ADDRESS = data["ip_address"].toString() else IP_ADDRESS = "play.2009scape.org" - if(data.containsKey("ip_management")) IP_MANAGEMENT = data["ip_management"].toString() else IP_MANAGEMENT = IP_ADDRESS - if(data.containsKey("wl_port")) WL_PORT = data["wl_port"].toString().toInt() - if(data.containsKey("server_port")) SERVER_PORT = data["server_port"].toString().toInt() - if(data.containsKey("js5_port")) JS5_SERVER_PORT = data["js5_port"].toString().toInt() - if(data.containsKey("world")) WORLD = data["world"].toString().toInt() - - //Parse customization options - if(data.containsKey("customization")){ - val custom = data["customization"] as JSONObject - if(custom.containsKey("login_theme")) LOGIN_THEME = custom["login_theme"].toString() - - //Right-click menu customizations - if(custom.containsKey("right_click_menu")){ - val rcm = custom["right_click_menu"] as JSONObject - - //background - if(rcm.containsKey("background")){ - val bg = rcm["background"] as JSONObject - if(bg.containsKey("color")) RCM_BG_COLOR = bg["color"].toString().replace("#", "").toInt(16) //convert hex -> deci - if(bg.containsKey("opacity")) RCM_BG_OPACITY = bg["opacity"].toString().toInt() - } - - //title bar - if(rcm.containsKey("title_bar")){ - val tb = rcm["title_bar"] as JSONObject - if(tb.containsKey("font_color")) RCM_TITLE = RCM_TITLE.replace("0", tb["font_color"].toString().replace("#", "")) - if(tb.containsKey("color")) RCM_TITLE_COLOR = tb["color"].toString().replace("#", "").toInt(16) //convert hex -> deci - if(tb.containsKey("opacity")) RCM_TITLE_OPACITY = tb["opacity"].toString().toInt() - } - - //border - if(rcm.containsKey("border")){ - val border = rcm["border"] as JSONObject - if(border.containsKey("color")) RCM_BORDER_COLOR = border["color"].toString().replace("#", "").toInt(16) //convert hex -> deci - if(border.containsKey("opacity")) RCM_BORDER_OPACITY = border["opacity"].toString().toInt() - } - - //styles (changes how things are drawn) - if(rcm.containsKey("styles")){ - val style = rcm["styles"] as JSONObject - if(style.containsKey("presets")) RCM_STYLE_PRESET = style["presets"].toString() - if(style.containsKey("rs3border")) RS3_CONTEXT_STYLE = style["rs3border"] as Boolean - } - } - if(custom.containsKey("rendering_options")) { - val hdoptions = custom["rendering_options"] as JSONObject - - if(hdoptions.containsKey("technical")) { - val renderIncrease = hdoptions["technical"] as JSONObject - if(renderIncrease.containsKey("render_distance_increase")) RENDER_DISTANCE_INCREASE = renderIncrease["render_distance_increase"] as Boolean - } - if(hdoptions.containsKey("skybox")) { - val skyboxColor = hdoptions["skybox"] as JSONObject - if(skyboxColor.containsKey("skybox_color")) SKYBOX_COLOR - } - } - } - - //Parse debug options - if(data.containsKey("debug")){ - val debug = data["debug"] as JSONObject - if(debug.containsKey("item_debug")) ITEM_DEBUG_ENABLED = debug["item_debug"] as Boolean - if(debug.containsKey("npc_debug")) NPC_DEBUG_ENABLED = debug["npc_debug"] as Boolean - if(debug.containsKey("object_debug")) OBJECT_DEBUG_ENABLED = debug["object_debug"] as Boolean - if(debug.containsKey("hd_login_region_debug")) HD_LOGIN_DEBUG = debug["hd_login_region_debug"] as Boolean - if(debug.containsKey("hd_login_region_debug_verbose")) HD_LOGIN_VERBOSE = debug["hd_login_region_debug_verbose"] as Boolean - if(debug.containsKey("cache_debug")) CACHE_DEBUG = debug["cache_debug"] as Boolean - if(debug.containsKey("world_map_debug")) WORLD_MAP_DEBUG = debug["world_map_debug"] as Boolean - } - - - /** - * Style Overrides (Still working on this system. We should allow for maximum creativity - * The way that it will be setup is a style type 1st - * ie, classicbox, rs3, rounded, rounded2 - * Then we introduce color schemes that a user could select - * ie, classic, rs3, alternate, alternate2, custom - * @author Woah - */ - when (RCM_STYLE_PRESET) { - "classic" -> { - RS3_CONTEXT_STYLE = false - RCM_BG_COLOR = 6116423 - RCM_BG_OPACITY = 255 - RCM_TITLE = "Choose Option" - RCM_TITLE_COLOR = 0 - RCM_TITLE_OPACITY = 255 - RCM_BORDER_COLOR = 0 - RCM_BORDER_OPACITY = 255 - } - "rs3" -> { - RS3_CONTEXT_STYLE = true - RCM_BG_COLOR = 662822 - RCM_BG_OPACITY = 255 - RCM_TITLE = "Choose Option" - RCM_TITLE_COLOR = 1512718 - RCM_TITLE_OPACITY = 165 - RCM_BORDER_COLOR = 16777215 - RCM_BORDER_OPACITY = 255 - } - } - - - } - - fun extendRenderDistance() { - if (RENDER_DISTANCE_INCREASE) { - /** **DO NOT CHANGE THESE NUMBERS UNLESS YOU KNOW WHAT YOU ARE DOING** - * Render Distance Overrides - * - * (Simple formula) Tile amount * 512 - * Default: 7 * 512 = 3584 - * Extended(max): 56 * 512 = 28672 - * - * Files + methods effected by these values: - * HDToolKit METHOD viewport - * Class140_Sub1_Sub1 METHOD animate - * Class3_Sub22 METHOD method398 * value as short - * Class40 METHOD method1046 * using RENDER_DISTANCE_TILE_VALUE - */ - RENDER_DISTANCE_VALUE = if (RENDER_DISTANCE_INCREASE) 28672F else 3584.0f - RENDER_DISTANCE_TILE_VALUE = if (RENDER_DISTANCE_INCREASE) 56 else 28 - RENDER_DISTANCE_FOG_FIX = if (RENDER_DISTANCE_INCREASE) 28672F else 3328.0f - } - } - - /** - * Client Info - * Not Editable - */ - @JvmField - var CLIENT_BUILD = 530 - - @JvmField - var CLIENT_VERSION = 1 - - @JvmField - var PACKAGE_NAME = "org.runite.client" - - @JvmField - var RSA = true - - @JvmField - var ISAAC = false - - @JvmField - var EXPONENT = BigInteger("65537") - - @JvmField - var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051") - - @JvmField - var SERVER_NAME = "2009scape" - - /** - * Path to config - */ - @JvmField - var configLocation = "config.json" - - /** - * Holiday Event Toggles - */ - @JvmField - var HOLIDAYS_ENABLED = true - - /** - * Halloween event NPC Definitions are handled inside of NPCDefinition.java - */ - @JvmField - var HALLOWEEN_EVENT_ENABLED = false - - @JvmField - var THANKSGIVING_EVENT_ENABLED = false - - @JvmField - var CHRISTMAS_EVENT_ENABLED = false - - private val calendar: Calendar = Calendar.getInstance() - private val month = calendar.get(Calendar.MONTH) - - @JvmStatic - fun implementHoliday() { - if (HOLIDAYS_ENABLED) { - when (month) { - 9 -> HALLOWEEN_EVENT_ENABLED = true - 10 -> THANKSGIVING_EVENT_ENABLED = true - 11 -> CHRISTMAS_EVENT_ENABLED = true - } - } - } - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/config/GameConfig.kt b/Client/src/main/kotlin/org/rs09/client/config/GameConfig.kt deleted file mode 100644 index 3979edb6e..000000000 --- a/Client/src/main/kotlin/org/rs09/client/config/GameConfig.kt +++ /dev/null @@ -1,393 +0,0 @@ -package org.rs09.client.config - -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import org.rs09.SlayerTracker -import java.io.FileReader -import java.math.BigInteger -import java.util.* - -/** - * Handles the client's config loading - * @author Ceikry - */ -object GameConfig { - /** - * Debug Booleans - */ - @JvmField - var ITEM_DEBUG_ENABLED = false - - @JvmField - var OBJECT_DEBUG_ENABLED = false - - @JvmField - var NPC_DEBUG_ENABLED = false - - @JvmField - var HD_LOGIN_DEBUG = false - - @JvmField - var HD_LOGIN_VERBOSE = false - - @JvmField - var CACHE_DEBUG = false - - @JvmField - var WORLD_MAP_DEBUG = false - - /** - * Context Menu Presets - */ - @JvmField - var RCM_STYLE_PRESET = "classic" - - /** - * Context Menu Customization - */ - @JvmField - var RCM_BG_COLOR = 6116423 - - @JvmStatic - var RCM_BG_OPACITY = 255 - set(value) { - if(value > 255 || value < 0) field = 255 - else field = value - } - - @JvmField - var RCM_TITLE_COLOR = 0 - - @JvmStatic - var RCM_TITLE_OPACITY = 255 - set(value) { - if(value > 255 || value < 0) field = 255 - else field = value - } - - @JvmField - var RCM_BORDER_COLOR = 0 - - @JvmStatic - var RCM_BORDER_OPACITY = 255 - set(value) { - if(value > 255 || value < 0) field = 255 - else field = value - } - - @JvmField - var RCM_TITLE = "Choose Option" - - @JvmField - var RS3_CONTEXT_STYLE = false - - /** - * Render distance - */ - @JvmField - var RENDER_DISTANCE_INCREASE = false - - @JvmField - var RENDER_DISTANCE_VALUE = 3584f - - @JvmField - var RENDER_DISTANCE_TILE_VALUE = 28 - - @JvmField - var RENDER_DISTANCE_FOG_FIX = 3328.0f - - @JvmField - var SKYBOX_COLOR = "float" - - /** - * Client Info - * Editable - */ - @JvmField - var IP_ADDRESS = "localhost" - - @JvmField - var IP_MANAGEMENT = "localhost" - - @JvmField - var JS5_SERVER_PORT = 43593 - - @JvmField - var SERVER_PORT = 43594 - - @JvmField - var WL_PORT = 5555 - - @JvmField - var WORLD = 1 - - @JvmField - var WORLD_OVERRIDE = -1 - - @JvmField - var LOGIN_THEME = "scape main" - - @JvmField - var xpDropsEnabled = true - - @JvmField - var xpDropMode = 0 - - @JvmField - var xpTrackMode = 0 - - @JvmField - var slayerCountEnabled = true - - @JvmField - var slayerTrackerColor = "#635a38" - - @JvmStatic - var slayerTrackerOpacity = 180 - set(value) { - if(value > 255 || value < 0) field = 255 - else field = value - } - - @JvmField - var slayerTaskID = 0 - - @JvmField - var slayerTaskAmount = 0 - - @JvmField - var VERBOSE_LOGGING = false - - @JvmStatic - fun setSlayerAmount(amount : Int){ - slayerTaskAmount = amount - if(slayerTaskAmount < 0) slayerTaskAmount = 0 - SlayerTracker.lastUpdate = System.currentTimeMillis() - } - - /** - * Json config Parser - */ - @JvmStatic - fun parse(path: String){ - val reader = FileReader(path) - val parser = JSONParser() - val data = parser.parse(reader) as JSONObject - - //Networking - if(data.containsKey("ip_address")) IP_ADDRESS = data["ip_address"].toString() else IP_ADDRESS = "play.2009scape.org" - if(data.containsKey("ip_management")) IP_MANAGEMENT = data["ip_management"].toString() else IP_MANAGEMENT = IP_ADDRESS - if(data.containsKey("wl_port")) WL_PORT = data["wl_port"].toString().toInt() - if(data.containsKey("server_port")) SERVER_PORT = data["server_port"].toString().toInt() - if(data.containsKey("js5_port")) JS5_SERVER_PORT = data["js5_port"].toString().toInt() - if(data.containsKey("world")) WORLD = data["world"].toString().toInt() - - //Parse customization options - if(data.containsKey("customization")){ - val custom = data["customization"] as JSONObject - if(custom.containsKey("login_theme")) LOGIN_THEME = custom["login_theme"].toString() - - //Right-click menu customizations - if(custom.containsKey("right_click_menu")){ - val rcm = custom["right_click_menu"] as JSONObject - - //background - if(rcm.containsKey("background")){ - val bg = rcm["background"] as JSONObject - if(bg.containsKey("color")) RCM_BG_COLOR = bg["color"].toString().replace("#", "").toIntOrNull(16) ?: 6116423//convert hex -> deci - if(bg.containsKey("opacity")) RCM_BG_OPACITY = bg["opacity"].toString().toInt() - } - - //title bar - if(rcm.containsKey("title_bar")){ - val tb = rcm["title_bar"] as JSONObject - if(tb.containsKey("font_color")) RCM_TITLE = RCM_TITLE.replace("0", tb["font_color"].toString().replace("#", "")) - if(tb.containsKey("color")) RCM_TITLE_COLOR = tb["color"].toString().replace("#", "").toIntOrNull(16) ?: 6116423//convert hex -> deci - if(tb.containsKey("opacity")) RCM_TITLE_OPACITY = tb["opacity"].toString().toInt() - } - - //border - if(rcm.containsKey("border")){ - val border = rcm["border"] as JSONObject - if(border.containsKey("color")) RCM_BORDER_COLOR = border["color"].toString().replace("#", "").toIntOrNull(16) ?: 6116423 //convert hex -> deci - if(border.containsKey("opacity")) RCM_BORDER_OPACITY = border["opacity"].toString().toInt() - } - - //styles (changes how things are drawn) - if(rcm.containsKey("styles")){ - val style = rcm["styles"] as JSONObject - if(style.containsKey("presets")) RCM_STYLE_PRESET = style["presets"].toString() - if(style.containsKey("rs3border")) RS3_CONTEXT_STYLE = style["rs3border"] as Boolean - } - } - - if(custom.containsKey("xpdrops")){ - val xpd = custom["xpdrops"] as JSONObject - if(xpd.containsKey("enabled")) xpDropsEnabled = xpd["enabled"] as Boolean - if(xpd.containsKey("drop_mode")) xpDropMode = xpd["drop_mode"].toString().toInt() - if(xpd.containsKey("track_mode")) xpTrackMode = xpd["track_mode"].toString().toInt() - } - - if(custom.containsKey("slayer")){ - val slayer = custom["slayer"] as JSONObject - if(slayer.containsKey("enabled")) slayerCountEnabled = slayer["enabled"] as Boolean - if(slayer.containsKey("color")) slayerTrackerColor = slayer["color"].toString().replace("#", "") - if(slayer.containsKey("opacity")) slayerTrackerOpacity = slayer["opacity"].toString().toInt() - } - - if(custom.containsKey("rendering_options")) { - val hdoptions = custom["rendering_options"] as JSONObject - - if(hdoptions.containsKey("technical")) { - val renderIncrease = hdoptions["technical"] as JSONObject - if(renderIncrease.containsKey("render_distance_increase")) RENDER_DISTANCE_INCREASE = renderIncrease["render_distance_increase"] as Boolean - } - if(hdoptions.containsKey("skybox")) { - val skyboxColor = hdoptions["skybox"] as JSONObject - if(skyboxColor.containsKey("skybox_color")) SKYBOX_COLOR - } - } - } - - //Parse debug options - if(data.containsKey("debug")){ - val debug = data["debug"] as JSONObject - if(debug.containsKey("item_debug")) ITEM_DEBUG_ENABLED = debug["item_debug"] as Boolean - if(debug.containsKey("npc_debug")) NPC_DEBUG_ENABLED = debug["npc_debug"] as Boolean - if(debug.containsKey("object_debug")) OBJECT_DEBUG_ENABLED = debug["object_debug"] as Boolean - if(debug.containsKey("hd_login_region_debug")) HD_LOGIN_DEBUG = debug["hd_login_region_debug"] as Boolean - if(debug.containsKey("hd_login_region_debug_verbose")) HD_LOGIN_VERBOSE = debug["hd_login_region_debug_verbose"] as Boolean - if(debug.containsKey("cache_debug")) CACHE_DEBUG = debug["cache_debug"] as Boolean - if(debug.containsKey("world_map_debug")) WORLD_MAP_DEBUG = debug["world_map_debug"] as Boolean - } - - - /** - * Style Overrides (Still working on this system. We should allow for maximum creativity - * The way that it will be setup is a style type 1st - * ie, classicbox, rs3, rounded, rounded2 - * Then we introduce color schemes that a user could select - * ie, classic, rs3, alternate, alternate2, custom - * @author Woah - */ - when (RCM_STYLE_PRESET) { - "classic" -> { - RS3_CONTEXT_STYLE = false - RCM_BG_COLOR = 6116423 - RCM_BG_OPACITY = 255 - RCM_TITLE = "Choose Option" - RCM_TITLE_COLOR = 0 - RCM_TITLE_OPACITY = 255 - RCM_BORDER_COLOR = 0 - RCM_BORDER_OPACITY = 255 - } - "rs3" -> { - RS3_CONTEXT_STYLE = true - RCM_BG_COLOR = 662822 - RCM_BG_OPACITY = 255 - RCM_TITLE = "Choose Option" - RCM_TITLE_COLOR = 1512718 - RCM_TITLE_OPACITY = 165 - RCM_BORDER_COLOR = 16777215 - RCM_BORDER_OPACITY = 255 - } - } - - - } - - fun extendRenderDistance() { - if (RENDER_DISTANCE_INCREASE) { - /** **DO NOT CHANGE THESE NUMBERS UNLESS YOU KNOW WHAT YOU ARE DOING** - * Render Distance Overrides - * - * (Simple formula) Tile amount * 512 - * Default: 7 * 512 = 3584 - * Extended(max): 56 * 512 = 28672 - * - * Files + methods effected by these values: - * HDToolKit METHOD viewport - * Class140_Sub1_Sub1 METHOD animate - * Class3_Sub22 METHOD method398 * value as short - * Class40 METHOD method1046 * using RENDER_DISTANCE_TILE_VALUE - */ - RENDER_DISTANCE_VALUE = if (RENDER_DISTANCE_INCREASE) 28672F else 3584.0f - RENDER_DISTANCE_TILE_VALUE = if (RENDER_DISTANCE_INCREASE) 56 else 28 - RENDER_DISTANCE_FOG_FIX = if (RENDER_DISTANCE_INCREASE) 28672F else 3328.0f - } - } - - /** - * Client Info - * Not Editable - */ - @JvmField - var CLIENT_BUILD = 530 - - @JvmField - var CLIENT_VERSION = 1 - - @JvmField - var PACKAGE_NAME = "org.runite.client" - - @JvmField - var RSA = true - - @JvmField - var ISAAC = false - - @JvmField - var EXPONENT = BigInteger("65537") - - @JvmField - var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051") - - @JvmField - var SERVER_NAME = "2009scape" - - /** - * Path to config - */ - @JvmField - var configLocation = "config.json" - - /** - * Holiday Event Toggles - */ - @JvmField - var HOLIDAYS_ENABLED = true - - @JvmField - var EASTER_EVENT_ENABLED = false - /** - * Halloween event NPC Definitions are handled inside of NPCDefinition.java - */ - @JvmField - var HALLOWEEN_EVENT_ENABLED = false - - @JvmField - var THANKSGIVING_EVENT_ENABLED = false - - @JvmField - var CHRISTMAS_EVENT_ENABLED = false - - private val calendar: Calendar = Calendar.getInstance() - private val month = calendar.get(Calendar.MONTH) - private val day = calendar.get(Calendar.DAY_OF_MONTH) - - @JvmStatic - fun implementHoliday() { - if (HOLIDAYS_ENABLED) { - when (month) { - 3 -> { - if (day <= 8) { - EASTER_EVENT_ENABLED = true - } - } - 9 -> HALLOWEEN_EVENT_ENABLED = true - 10 -> THANKSGIVING_EVENT_ENABLED = true - 11 -> CHRISTMAS_EVENT_ENABLED = true - } - } - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/console/AutocompletionHints.kt b/Client/src/main/kotlin/org/rs09/client/console/AutocompletionHints.kt deleted file mode 100644 index 533b2bba2..000000000 --- a/Client/src/main/kotlin/org/rs09/client/console/AutocompletionHints.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.rs09.client.console - -data class AutocompletionHints( - val base: String, - val completions: List, - val totalSize: Int -) \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/console/DeveloperConsole.kt b/Client/src/main/kotlin/org/rs09/client/console/DeveloperConsole.kt deleted file mode 100644 index a7c2a8ed9..000000000 --- a/Client/src/main/kotlin/org/rs09/client/console/DeveloperConsole.kt +++ /dev/null @@ -1,494 +0,0 @@ -package org.rs09.client.console - -import org.rs09.client.LinkableInt -import org.rs09.client.config.GameConfig -import org.rs09.client.filestore.resources.configs.enums.EnumDefinitionProvider -import org.rs09.client.rendering.RenderingUtils -import org.rs09.client.rendering.Toolkit -import org.runite.client.* -import java.awt.event.KeyEvent -import java.io.ByteArrayInputStream; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.text.SimpleDateFormat -import java.util.* -import java.util.LinkedList -import javax.sound.sampled.AudioFileFormat; -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioInputStream -import javax.sound.sampled.AudioSystem; - - -// TODO Escape characters in the string rendering - is this something we can do using RSString / the text renders? -object DeveloperConsole { - - var ENABLE_PACKETS = false - - private val CONSOLE_FONT - get() = FontType.plainFont - - private val gameWidth //Offset 5 if in-game offset 20 if on login screen - get() = Unsorted.gameWindowWidth - - private val LOCK = Any() - - private const val HEIGHT = 300 - private const val BACKGROUND_COLOR = 0x332277 - private const val MAX_LINES = 100 - private const val SCROLL_SPEED = 25 - - private val calendar = Calendar.getInstance() - private var tempBuffer: IntArray? = null - private val lines = LinkedList() - private var scrollOffset = 0 - private var str: String = "" - - var firstOpen = false - var selectedCompletion = 0 - var autocompletions: AutocompletionHints? = null - set(t) { - field = t - selectedCompletion = 0 - } - - var open = false - - fun toggle() { - open = !open - } - - fun draw() { - if (!open) return - - if (!firstOpen) { - println("This is the developer console. To close, press ALT + `") - firstOpen = true - } - - val tk = Toolkit.getActiveToolkit() - - when (Class83.getWindowType()) { - 0, 1 -> { //use gameWidth - val widthOffsets = if (Class143.gameStage <= 10) RenderingUtils.width else (gameWidth + 3) - tk.fillRect(0, 0, widthOffsets, HEIGHT, BACKGROUND_COLOR, 128) - tk.drawHorizontalLine(0, HEIGHT - 14 - 2, widthOffsets, -1) - RenderingUtils.drawTextSmall("Build: ${GameConfig.CLIENT_BUILD}", widthOffsets - 60, HEIGHT - 17, -1, 2) - RenderingUtils.drawText("--> $str", 3, HEIGHT - 2, -1, 2) - RenderingUtils.setClipping(0, 0, widthOffsets, HEIGHT - 16) - } - 2 -> { //use RenderingUtils.width - tk.fillRect(0, 0, RenderingUtils.width, HEIGHT, BACKGROUND_COLOR, 128) - tk.drawHorizontalLine(0, HEIGHT - 14 - 2, RenderingUtils.width, -1) - RenderingUtils.drawTextSmall( - "Build: ${GameConfig.CLIENT_BUILD}", - RenderingUtils.width - 60, - HEIGHT - 17, - -1, - 2 - ) - RenderingUtils.drawText("--> $str", 3, HEIGHT - 2, -1, 2) - RenderingUtils.setClipping(0, 0, RenderingUtils.width, HEIGHT - 16) - } - } - - synchronized(LOCK) { - lines.forEachIndexed { i, line -> - RenderingUtils.drawText( - line, - 7, - scrollOffset + HEIGHT - 20 - i * 14, - -1, - 2 - ) - } - } - RenderingUtils.resetClipping() - - // text height = 16 - synchronized(LOCK) { - autocompletions?.apply { - val startX = CONSOLE_FONT.method682(RSString.of("-> $str")) - val boxHeight = 24 + 8 + completions.size * 14 - val boxWidth = 8 + 8 + (completions.map { CONSOLE_FONT.method682(RSString.of(it)) }.maxOrNull() ?: 0) - - tk.fillRect(startX, HEIGHT - 16 - boxHeight, boxWidth, boxHeight, 0x323232, 255) - RenderingUtils.drawRect( - startX + 3, - HEIGHT - 16 - boxHeight + 6, - boxWidth - 6, - boxHeight - 9 - 14, - 0x646464, - 200 - ) - tk.drawHorizontalLine(startX + 8, HEIGHT - 16 - boxHeight + 6, 75, 0x323232) - RenderingUtils.drawText( - RSString.parse("Completions"), - startX + 12, - HEIGHT - 17 - boxHeight + 12, - 0xffffff - ) - RenderingUtils.drawText( - RSString.parse("${completions.size}/$totalSize sent"), - startX + 4, - HEIGHT - 20, - 0xffffff - ) - -// tk.fillRect(startX + 4, HEIGHT - 16 - boxHeight + 14, boxWidth - 8, boxHeight - 9 - 14 - 8 - 1, 0xff0000, 255) -// RenderingUtils.setClipping(startX + 4, HEIGHT - 16 - boxHeight + 14, boxWidth - 8, boxHeight - 9 - 14 - 8 - 1) - completions.forEachIndexed { i, completion -> - if (selectedCompletion == i) { - tk.fillRect(startX + 4, HEIGHT - 6 - boxHeight + 4 + i * 14, boxWidth - 8, 14, 0x2a58a8, 255) - } - RenderingUtils.drawText( - RSString.of(completion), - startX + 6, - HEIGHT - 6 - boxHeight + 14 + i * 14, - 0xffffff - ) - } -// RenderingUtils.resetClipping() - } - } - } - - fun println(line: String) { - calendar.time = Date(TimeUtils.time()) - synchronized(LOCK) { - lines.addFirst(RSString.of("${SimpleDateFormat("HH:mm:ss").format(Date(TimeUtils.time()))}: --> $line")) - - if (lines.size >= MAX_LINES) lines.removeLast() - - if (scrollOffset != 0) { - val room = HEIGHT - 20 - val max = lines.size * 14 - val diff = max - room - if (scrollOffset < diff) { - scrollOffset += 14 - if (scrollOffset > diff) scrollOffset = diff - } - } - } - } - - fun preDraw() { - if (RenderingUtils.hd) return - - val copy = IntArray(Toolkit.JAVA_TOOLKIT.buffer.size) - System.arraycopy(Toolkit.JAVA_TOOLKIT.buffer, 0, copy, 0, copy.size) - tempBuffer = copy - } - - fun postDraw() { - if (RenderingUtils.hd) return - - if (tempBuffer != null) { - System.arraycopy(tempBuffer!!, 0, Toolkit.JAVA_TOOLKIT.buffer, 0, Toolkit.JAVA_TOOLKIT.buffer.size) - } - tempBuffer = null - } - - fun onConsoleInput(str: String) { -// println("[$h:$m:$s] TODO! Handle '$str'") - - if (ENABLE_PACKETS) { - TextureOperation12.outgoingBuffer.putOpcode(51) - TextureOperation12.outgoingBuffer.writeShort(0) - val index = TextureOperation12.outgoingBuffer.index - TextureOperation12.outgoingBuffer.writeString(DeveloperConsole.str) - TextureOperation12.outgoingBuffer.finishVarshortPacket( - TextureOperation12.outgoingBuffer.index - index) - } - - println(str) - val clientCommand: MutableList - val args: Any - val command: String = str - clientCommand = command.split(' ') as MutableList - val argSize = clientCommand.size - - when (clientCommand[0]) { - "enableconsolepackets" -> { - ENABLE_PACKETS = true - println("Enabled console packets!") - } - "quests" -> { - println("~~~~~ MINIQUESTS ~~~~~") - System.out.println("~~~~~ MINIQUESTS ~~~~~") - var lookup = EnumDefinitionProvider.provide(208) - - for (i in 0..17) { - val component = (lookup.values!![i.toLong()]!! as LinkableInt).value - - val rsiface = Unsorted.getRSInterface(component) - if (rsiface == null) println("Error: couldnt find component for hash $component") - - println("$i: ${rsiface.text}") - System.out.println("name ${rsiface.text}, lookup id $i") - } - - println("~~~~~ QUESTS ~~~~~") - System.out.println("~~~~~ QUESTS ~~~~~") - lookup = EnumDefinitionProvider.provide(209) - - for (i in 0..130) { - val component = (lookup.values!![i.toLong()]!! as LinkableInt).value - - val rsiface = Unsorted.getRSInterface(component) - if (rsiface == null) println("Error: couldnt find component for hash $component") - - println("$i: ${rsiface.text}") - System.out.println("name ${rsiface.text}, lookup id $i") - } - } - "errormsg" -> { - if (argSize == 2) { - args = clientCommand[1].toIntOrNull() ?: -1 - Client.messageToDisplay = args as Int - } else { - println("Error. Displays error message on login, account creation. Use: errormsg #") - } - } - "dumpscript" -> { - if (argSize == 2) { - val i = clientCommand[1].toIntOrNull() - if(i != null) { - DumpingTools.DumpOpcodesToTextFile("script_${i}.cs2", i) - } - } else { - println("Error. Dumps a cs2 script. Use: dumpscript #") - } - } - "playsound" -> { - if (argSize == 4) { - val soundID = if (clientCommand[1].toIntOrNull() == null) 0 else clientCommand[1].toInt() - val soundDelay = if (clientCommand[2].toIntOrNull() == null) 0 else clientCommand[2].toInt() - val soundVolume = if (clientCommand[3].toIntOrNull() == null) 0 else clientCommand[3].toInt() - AudioHandler.soundEffectHandler(soundVolume, soundID, soundDelay) - println("Playing sound: $soundID with delay: $soundDelay volume: $soundVolume") - } else { - println("Error. Plays sound effect. Use: playsound soundID soundDelay soundVolume") - } - } - "playsoundrange" -> { - if (argSize == 4) { - var beginID = if (clientCommand[1].toIntOrNull() == null) 0 else clientCommand[1].toInt() - var endID = if (clientCommand[2].toIntOrNull() == null) 0 else clientCommand[2].toInt() - var delay = if (clientCommand[3].toIntOrNull() == null) 0 else clientCommand[3].toInt() - if (beginID > endID) { - val tmp = endID; - endID = beginID; - beginID = tmp; - } - Thread(object : Runnable { - override fun run() { - for (i in beginID..endID) { - println("Playing sound effect ${i}") - AudioHandler.soundEffectHandler(1, i, 0) - Thread.sleep(delay.toLong()) - } - } - }).start() - } else { - println("Error. Plays sound effect. Use: playsoundrange beginID endID delay") - } - } - "dumpsfx" -> { - if (argSize == 3) { - var beginID = if (clientCommand[1].toIntOrNull() == null) 0 else clientCommand[1].toInt() - var endID = if (clientCommand[2].toIntOrNull() == null) 0 else clientCommand[2].toInt() - Thread(object : Runnable { - override fun run() { - Files.createDirectories(FileSystems.getDefault().getPath("sfx")) - for (i in beginID..endID) { - val sfx = SynthSound.create(CacheIndex.soundFXIndex, i, 0).toPCMSound() - println("len of ${i}: ${sfx.samples.size}") - val out = Files.newOutputStream(FileSystems.getDefault().getPath("sfx", String.format("sfx_%05d.wav", i))) - val format = AudioFormat(AudioFormat.Encoding.PCM_SIGNED, sfx.frequency.toFloat(), 8, 1, 1, sfx.frequency.toFloat(), false) - AudioSystem.write( - AudioInputStream(ByteArrayInputStream(sfx.samples), format, sfx.samples.size.toLong()), - AudioFileFormat.Type.WAVE, - out) - out.close() - } - } - }).start() - } else { - println("Error. Dumps sound effects. Use: dumpsfx beginID endID") - } - } - "playsong" -> { - if (argSize in 2..8) { - if (clientCommand[1].toIntOrNull() == null) { - clientCommand.removeFirst() - AudioHandler.musicHandler( - CacheIndex.musicIndex.getArchiveForName( - RSString.of( - clientCommand.joinToString( - " " - ) - ) - ).also { println("Playing song ID: $it - ${clientCommand.joinToString("")}") } - ) - } else { - args = clientCommand[1].toInt() - AudioHandler.musicHandler(args) - } - } else { - println("Error. Plays music. Use: playsong # OR playsong songName") - } - } - "playsfx" -> { - if (argSize == 2) { - args = clientCommand[1].toIntOrNull() ?: -1 - AudioHandler.musicEffectHandler(args as Int) - } else { - println("Error. Plays a music effect. Use: playeffectfx #") - } - } - "cstage" -> { - when (argSize) { - 1 -> { - println("Client.gameStage: " + Class143.gameStage) - println("LoginHandler.adminLoginStage: " + Class163_Sub1_Sub1.adminLoginStage) - println("LoginHandler.userLoginStage: " + LoginHandler.loginStage) - println("AccountRegistration.registryStage: " + Unsorted.registryStage) - println("WorldListMethods.worldStage: " + Class43.worldListStage) - } - 2 -> { - args = clientCommand[1] - when (args) { - "game" -> println("GameStateManager.gameState: " + Class143.gameStage) - "login" -> { - println("LoginHandler.adminLoginStage: " + Class163_Sub1_Sub1.adminLoginStage) - println("LoginHandler.userLoginStage: " + LoginHandler.loginStage) - } - "register" -> println("AccountRegistration.registryStage: " + Unsorted.registryStage) - "wl", "worldlist" -> println("WorldListMethods.worldStage: " + Class43.worldListStage) - else -> println("Error. Incorrect usage. Use clientstage or clientstage game/login/register/worldlist to see a specific stage") - } - } - else -> println("Error. Incorrect usage. Use clientstage or clientstage game/login/register/worldlist") - } - } - "worldlist" -> { - val worldArray = WorldListEntry.worldList - args = clientCommand[1] - when (args) { - "active" -> { - println("Active: ${WorldListEntry.activeWorldListSize} Update stamp: ${WorldListEntry.updateStamp}") - } - "world" -> { - if (argSize == 3) { - val worldId = clientCommand[2].toInt() - if (worldArray[worldId] != null) { - val world = worldArray[worldId] - println( - "ID: ${world.worldId} " + - "WHERE: ${world.countryIndex} " + - "MEM: ${world.isMembers} " + - "PVP: ${world.isPVP} " + - "Loot: ${world.isLootShare} " + - "QC: ${world.isQuickchat} " + - "DESC: ${world.activity} " + - "NET: ${world.address}:" - ) - } else { - println("Requested world ($worldId) is OFFLINE or NULL") - } - } else { - println("Error. Incorrect usage. Use worldlist world worldID") - } - } - "goto" -> { - if (argSize == 3) { - val worldId = clientCommand[2].toInt() - if (worldArray[worldId] != null) { - CS2Script.userCurrentWorldID = worldId - } else { - println("Requested world ($worldId) is OFFLINE or NULL") - } - } else { - println("Error. Incorrect usage. Use: worldlist goto worldID") - } - } - else -> println("World list commands: active, world ID, goto ID") - } - } - - else -> { - System.out.println("Console command: $str") - sendCommand(str) - } - } - } - - fun handleKeyDown(evt: KeyEvent) { - if (evt.keyCode == KeyEvent.VK_DOWN) { - if (autocompletions != null) { - if (selectedCompletion + 1 < autocompletions?.completions?.size ?: 0) - selectedCompletion++ - return - } - - if (scrollOffset > SCROLL_SPEED) scrollOffset -= SCROLL_SPEED else scrollOffset = 0 - } else if (evt.keyCode == KeyEvent.VK_UP) { - if (autocompletions != null) { - if (selectedCompletion > 0) selectedCompletion-- - return - } - - val room = HEIGHT - 20 - val max = lines.size * 14 - val diff = max - room - if (scrollOffset < diff) { - scrollOffset += SCROLL_SPEED - if (scrollOffset > diff) scrollOffset = diff - } - } else if (evt.keyCode == KeyEvent.VK_ESCAPE) { - synchronized(LOCK) { - autocompletions = null - } - } - } - - fun handleKeyPressed(evt: KeyEvent) { - if (evt.keyChar == '`') return - - when { - evt.keyChar == '\t' -> { - if (str.isEmpty()) return - - if (ENABLE_PACKETS) { - TextureOperation12.outgoingBuffer.putOpcode(52) - TextureOperation12.outgoingBuffer.writeShort(0) - val index = TextureOperation12.outgoingBuffer.index - TextureOperation12.outgoingBuffer.writeString(str) - TextureOperation12.outgoingBuffer.finishVarshortPacket( - TextureOperation12.outgoingBuffer.index - index) - } else if ("enableconsolepackets".startsWith(str, true)) { - autocompletions = AutocompletionHints(str, listOf("enableconsolepackets"), 1) - } - } - evt.keyChar == '\n' -> { - if (autocompletions != null) { - str += autocompletions?.completions?.get(selectedCompletion) ?: "" - autocompletions = null - return - } - - if (str.isNotBlank()) onConsoleInput(str.trim()) - str = "" - } - evt.keyChar == '\b' && str.isNotEmpty() -> str = str.substring(0, str.length - 1) - else -> str += evt.keyChar - } - } - - @JvmStatic - fun sendCommand(command: String) { - TextureOperation12.outgoingBuffer.putOpcode(44) - TextureOperation12.outgoingBuffer.writeByte(command.length + 2) - TextureOperation12.outgoingBuffer.writeString(command) - } -} diff --git a/Client/src/main/kotlin/org/rs09/client/constants/Parameter.kt b/Client/src/main/kotlin/org/rs09/client/constants/Parameter.kt deleted file mode 100644 index d5fbab7b7..000000000 --- a/Client/src/main/kotlin/org/rs09/client/constants/Parameter.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.rs09.client.constants - -object Parameter { - - /** - * Modewhat - * DEFAULT: LIVE_SERVER 0 - */ - const val LIVE_SERVER = 0 - const val RELEASE_CANDIDATE = 1 - const val WORK_IN_PROGRESS = 2 - - /** - * Modewhere - * DEFAULT: LIVE_ENVIRONMENT 0 - */ - const val LIVE_ENVIRONMENT = 0 - const val OFFICE_ENVIRONMENT = 1 - const val LOCAL_ENVIRONMENT = 2 - - /** - * Language - * DEFAULT: LANGUAGE_ENGLISH 0 - */ - const val LANGUAGE_ENGLISH = 0 - const val LANGUAGE_GERMAN = 1 - const val LANGUAGE_FRENCH = 2 - - /** - * Game type - * DEFAULT: GAME_TYPE_RUNESCAPE 0 (game0) - * - * Runescape | Game type 0 has always been Runescape (current) - * Mechscape | Assuming this is what the game1 was (2007 - Oct 2009) - * Stellar Dawn | Overhaul of 'Mechscape' (July 2010 - Stuck in corporate hell) - */ - const val GAME_TYPE_RUNESCAPE = 0 - const val GAME_TYPE_MECHSCAPE = 1 - - /** - * Affiliate ID - * DEFAULT: NO_AFFILIATE 0 - */ - const val NO_AFFILIATE = 0 - const val SHARE_DETAILS_AFFILIATE = 99 - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/constants/gametype/MechscapeGameAppearance.kt b/Client/src/main/kotlin/org/rs09/client/constants/gametype/MechscapeGameAppearance.kt deleted file mode 100644 index 8affc513e..000000000 --- a/Client/src/main/kotlin/org/rs09/client/constants/gametype/MechscapeGameAppearance.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.rs09.client.constants.gametype - -object MechscapeGameAppearance { - - val aShortArrayArray1619 = arrayOf(shortArrayOf(10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort()), shortArrayOf(10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10347.toShort(), 10582.toShort(), 10429.toShort(), 10407.toShort(), 10359.toShort(), 8414.toShort(), 9540.toShort(), 10456.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort()), shortArrayOf(10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort()), shortArrayOf(4300.toShort(), 3294.toShort(), 3303.toShort(), 3264.toShort(), 4506.toShort(), 4382.toShort(), 4387.toShort(), 5293.toShort(), 7622.toShort(), 7384.toShort(), 8412.toShort(), 7496.toShort(), 86.toShort(), 123.toShort(), 111.toShort(), 99.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 13766.toShort(), 13745.toShort(), 13726.toShort(), 13890.toShort(), 13743.toShort(), 13852.toShort(), 17602.toShort(), 18605.toShort(), 21660.toShort(), 24000.toShort(), 24997.toShort(), 24088.toShort(), 27972.toShort(), 25903.toShort(), 26904.toShort(), 27193.toShort(), 27175.toShort(), 27156.toShort(), 30020.toShort(), 28975.toShort(), 29976.toShort(), 12482.toShort(), 13485.toShort(), 10392.toShort(), 10692.toShort(), 10669.toShort(), 10776.toShort(), 6717.toShort(), 6695.toShort(), 7830.toShort(), 6971.toShort(), 6951.toShort(), 5910.toShort(), 3389.toShort(), 3369.toShort(), 3356.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort()), shortArrayOf(10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort(), 10.toShort(), 30.toShort(), 50.toShort(), 70.toShort(), 90.toShort(), 110.toShort(), 310.toShort(), 684.toShort(), 704.toShort(), 556.toShort(), 940.toShort(), 960.toShort(), 6454.toShort(), 6952.toShort(), 6972.toShort(), 2358.toShort(), 2732.toShort(), 2752.toShort(), 10550.toShort(), 10924.toShort(), 10944.toShort(), 10310.toShort(), 10556.toShort(), 10576.toShort(), 14646.toShort(), 15020.toShort(), 15040.toShort(), 19766.toShort(), 20140.toShort(), 20160.toShort(), (-29386).toShort(), (-29012).toShort(), (-28992).toShort(), 31030.toShort(), 31276.toShort(), 31296.toShort(), (-24266).toShort(), (-23892).toShort(), (-23872).toShort(), (-19146).toShort(), (-18772).toShort(), (-18752).toShort(), (-14026).toShort(), (-13652).toShort(), (-13632).toShort(), (-6858).toShort(), (-6484).toShort(), (-6464).toShort(), 522.toShort(), 542.toShort(), 6794.toShort(), 6814.toShort(), 11018.toShort(), 11038.toShort(), 14986.toShort(), 15006.toShort(), 21130.toShort(), 21150.toShort(), (-28918).toShort(), (-28898).toShort(), (-22006).toShort(), (-21986).toShort(), (-12918).toShort(), (-12898).toShort())) - - val aShortArrayArray2634 = arrayOf(shortArrayOf(0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 4.toShort(), 24.toShort(), 44.toShort(), 64.toShort(), 84.toShort(), 104.toShort(), 304.toShort(), 678.toShort(), 698.toShort(), 550.toShort(), 934.toShort(), 954.toShort(), 6448.toShort(), 6946.toShort(), 6966.toShort(), 2352.toShort(), 2726.toShort(), 2746.toShort(), 10544.toShort(), 10918.toShort(), 10938.toShort(), 10304.toShort(), 10550.toShort(), 10570.toShort(), 14640.toShort(), 15014.toShort(), 15034.toShort(), 19760.toShort(), 20134.toShort(), 20154.toShort(), (-29392).toShort(), (-29018).toShort(), (-28998).toShort(), 31024.toShort(), 31270.toShort(), 31290.toShort(), (-24272).toShort(), (-23898).toShort(), (-23878).toShort(), (-19152).toShort(), (-18778).toShort(), (-18758).toShort(), (-14032).toShort(), (-13658).toShort(), (-13638).toShort(), (-6864).toShort(), (-6490).toShort(), (-6470).toShort(), 516.toShort(), 536.toShort(), 6788.toShort(), 6808.toShort(), 11012.toShort(), 11032.toShort(), 14980.toShort(), 15000.toShort(), 21124.toShort(), 21144.toShort(), (-28924).toShort(), (-28904).toShort(), (-22012).toShort(), (-21992).toShort(), (-12924).toShort(), (-12904).toShort()), shortArrayOf(0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 10339.toShort(), 10574.toShort(), 10425.toShort(), 10398.toShort(), 10345.toShort(), 7512.toShort(), 8507.toShort(), 7378.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort()), shortArrayOf(0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 4.toShort(), 24.toShort(), 44.toShort(), 64.toShort(), 84.toShort(), 104.toShort(), 304.toShort(), 678.toShort(), 698.toShort(), 550.toShort(), 934.toShort(), 954.toShort(), 6448.toShort(), 6946.toShort(), 6966.toShort(), 2352.toShort(), 2726.toShort(), 2746.toShort(), 10544.toShort(), 10918.toShort(), 10938.toShort(), 10304.toShort(), 10550.toShort(), 10570.toShort(), 14640.toShort(), 15014.toShort(), 15034.toShort(), 19760.toShort(), 20134.toShort(), 20154.toShort(), (-29392).toShort(), (-29018).toShort(), (-28998).toShort(), 31024.toShort(), 31270.toShort(), 31290.toShort(), (-24272).toShort(), (-23898).toShort(), (-23878).toShort(), (-19152).toShort(), (-18778).toShort(), (-18758).toShort(), (-14032).toShort(), (-13658).toShort(), (-13638).toShort(), (-6864).toShort(), (-6490).toShort(), (-6470).toShort(), 516.toShort(), 536.toShort(), 6788.toShort(), 6808.toShort(), 11012.toShort(), 11032.toShort(), 14980.toShort(), 15000.toShort(), 21124.toShort(), 21144.toShort(), (-28924).toShort(), (-28904).toShort(), (-22012).toShort(), (-21992).toShort(), (-12924).toShort(), (-12904).toShort()), shortArrayOf(0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 13753.toShort(), 13737.toShort(), 13719.toShort(), 13883.toShort(), 13863.toShort(), 13974.toShort(), 19643.toShort(), 18601.toShort(), 16532.toShort(), 23993.toShort(), 25121.toShort(), 24980.toShort(), 26944.toShort(), 26921.toShort(), 24854.toShort(), 27191.toShort(), 27171.toShort(), 26130.toShort(), 26941.toShort(), 28696.toShort(), 30100.toShort(), 12477.toShort(), 10407.toShort(), 10388.toShort(), 10685.toShort(), 10665.toShort(), 10646.toShort(), 6711.toShort(), 6693.toShort(), 6674.toShort(), 6965.toShort(), 7073.toShort(), 7056.toShort(), 2361.toShort(), 4387.toShort(), 3346.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort()), shortArrayOf(0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 0.toShort(), 4.toShort(), 24.toShort(), 44.toShort(), 64.toShort(), 84.toShort(), 104.toShort(), 304.toShort(), 678.toShort(), 698.toShort(), 550.toShort(), 934.toShort(), 954.toShort(), 6448.toShort(), 6946.toShort(), 6966.toShort(), 2352.toShort(), 2726.toShort(), 2746.toShort(), 10544.toShort(), 10918.toShort(), 10938.toShort(), 10304.toShort(), 10550.toShort(), 10570.toShort(), 14640.toShort(), 15014.toShort(), 15034.toShort(), 19760.toShort(), 20134.toShort(), 20154.toShort(), (-29392).toShort(), (-29018).toShort(), (-28998).toShort(), 31024.toShort(), 31270.toShort(), 31290.toShort(), (-24272).toShort(), (-23898).toShort(), (-23878).toShort(), (-19152).toShort(), (-18778).toShort(), (-18758).toShort(), (-14032).toShort(), (-13658).toShort(), (-13638).toShort(), (-6864).toShort(), (-6490).toShort(), (-6470).toShort(), 516.toShort(), 536.toShort(), 6788.toShort(), 6808.toShort(), 11012.toShort(), 11032.toShort(), 14980.toShort(), 15000.toShort(), 21124.toShort(), 21144.toShort(), (-28924).toShort(), (-28904).toShort(), (-22012).toShort(), (-21992).toShort(), (-12924).toShort(), (-12904).toShort())) - - val aShortArray63 = shortArrayOf( - 960.toShort(), - 957.toShort(), (-21568).toShort(), (-21571).toShort(), 22464.toShort() - ) - - val aShortArray2219 = shortArrayOf( - (-4160).toShort(), - (-4163).toShort(), (-8256).toShort(), (-8259).toShort(), 22461.toShort() - ) - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/constants/gametype/RunescapeGameAppearance.kt b/Client/src/main/kotlin/org/rs09/client/constants/gametype/RunescapeGameAppearance.kt deleted file mode 100644 index 78e5ef16c..000000000 --- a/Client/src/main/kotlin/org/rs09/client/constants/gametype/RunescapeGameAppearance.kt +++ /dev/null @@ -1,181 +0,0 @@ -package org.rs09.client.constants.gametype - -object RunescapeGameAppearance { - - /** - * Game type RUNESCAPE appearance values - */ - - val aShortArrayArray3654 = arrayOf( - shortArrayOf( - 6798.toShort(), - 107.toShort(), - 10283.toShort(), - 16.toShort(), - 4797.toShort(), - 7744.toShort(), - 5799.toShort(), - 4634.toShort(), - (-31839).toShort(), - 22433.toShort(), - 2983.toShort(), - (-11343).toShort(), - 8.toShort(), - 5281.toShort(), - 10438.toShort(), - 3650.toShort(), - (-27322).toShort(), - (-21845).toShort(), - 200.toShort(), - 571.toShort(), - 908.toShort(), - 21830.toShort(), - 28946.toShort(), - (-15701).toShort(), - (-14010).toShort() - ), - shortArrayOf( - 8741.toShort(), - 12.toShort(), - (-1506).toShort(), - (-22374).toShort(), - 7735.toShort(), - 8404.toShort(), - 1701.toShort(), - (-27106).toShort(), - 24094.toShort(), - 10153.toShort(), - (-8915).toShort(), - 4783.toShort(), - 1341.toShort(), - 16578.toShort(), - (-30533).toShort(), - 25239.toShort(), - 8.toShort(), - 5281.toShort(), - 10438.toShort(), - 3650.toShort(), - (-27322).toShort(), - (-21845).toShort(), - 200.toShort(), - 571.toShort(), - 908.toShort(), - 21830.toShort(), - 28946.toShort(), - (-15701).toShort(), - (-14010).toShort() - ), - shortArrayOf( - 25238.toShort(), - 8742.toShort(), - 12.toShort(), - (-1506).toShort(), - (-22374).toShort(), - 7735.toShort(), - 8404.toShort(), - 1701.toShort(), - (-27106).toShort(), - 24094.toShort(), - 10153.toShort(), - (-8915).toShort(), - 4783.toShort(), - 1341.toShort(), - 16578.toShort(), - (-30533).toShort(), - 8.toShort(), - 5281.toShort(), - 10438.toShort(), - 3650.toShort(), - (-27322).toShort(), - (-21845).toShort(), - 200.toShort(), - 571.toShort(), - 908.toShort(), - 21830.toShort(), - 28946.toShort(), - (-15701).toShort(), - (-14010).toShort() - ), - shortArrayOf(4626.toShort(), 11146.toShort(), 6439.toShort(), 12.toShort(), 4758.toShort(), 10270.toShort()), - shortArrayOf( - 4550.toShort(), - 4537.toShort(), - 5681.toShort(), - 5673.toShort(), - 5790.toShort(), - 6806.toShort(), - 8076.toShort(), - 4574.toShort() - ) - ) //Appearance Colors - - val aShortArray3349 = shortArrayOf( - (-10304).toShort(), - 9104.toShort(), (-1).toShort(), (-1).toShort(), (-1).toShort() - ) - - val aShortArrayArray435 = arrayOf( - shortArrayOf( - 6554.toShort(), - 115.toShort(), - 10304.toShort(), - 28.toShort(), - 5702.toShort(), - 7756.toShort(), - 5681.toShort(), - 4510.toShort(), - (-31835).toShort(), - 22437.toShort(), - 2859.toShort(), - (-11339).toShort(), - 16.toShort(), - 5157.toShort(), - 10446.toShort(), - 3658.toShort(), - (-27314).toShort(), - (-21965).toShort(), - 472.toShort(), - 580.toShort(), - 784.toShort(), - 21966.toShort(), - 28950.toShort(), - (-15697).toShort(), - (-14002).toShort() - ), shortArrayOf( - 9104.toShort(), - 10275.toShort(), - 7595.toShort(), - 3610.toShort(), - 7975.toShort(), - 8526.toShort(), - 918.toShort(), - (-26734).toShort(), - 24466.toShort(), - 10145.toShort(), - (-6882).toShort(), - 5027.toShort(), - 1457.toShort(), - 16565.toShort(), - (-30545).toShort(), - 25486.toShort(), - 24.toShort(), - 5392.toShort(), - 10429.toShort(), - 3673.toShort(), - (-27335).toShort(), - (-21957).toShort(), - 192.toShort(), - 687.toShort(), - 412.toShort(), - 21821.toShort(), - 28835.toShort(), - (-15460).toShort(), - (-14019).toShort() - ), ShortArray(0), ShortArray(0), ShortArray(0) - ) - - val aShortArray3011 = shortArrayOf( - 6798.toShort(), - 8741.toShort(), 25238.toShort(), 4626.toShort(), 4550.toShort() - ) -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/constants/login/LoginMessageToDisplay.kt b/Client/src/main/kotlin/org/rs09/client/constants/login/LoginMessageToDisplay.kt deleted file mode 100644 index a33c7330a..000000000 --- a/Client/src/main/kotlin/org/rs09/client/constants/login/LoginMessageToDisplay.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.rs09.client.constants.login - -object LoginMessageToDisplay { - - const val CONNECTION_TIMED_OUT = -5 - const val ERROR_CONNECTING_TO_SERVER = -4 - const val PERFORMING_LOGIN = -3 - const val ENTER_USERNAME_AND_PASSWORD = -2 - - const val COULD_NOT_DISPLAY_VIDEO_AD = 1 - const val UNEXPECTED_SERVER_RESPONSE_2 = 2 - const val INVALID_USER_OR_PASSWORD = 3 - const val ACCOUNT_DISABLED = 4 - const val ACCOUNT_STILL_LOGGED_IN = 5 - const val GAME_HAS_UPDATED = 6 - const val WORLD_IS_FULL = 7 - const val LOGIN_SERVER_OFFLINE = 8 - const val TOO_MANY_CONNECTIONS = 9 - const val BAD_SESSION_ID = 10 - const val WEAK_PASSWORD_ALERT = 11 - const val NON_MEMBERS_ACCOUNT = 12 - const val COULD_NOT_COMPLETE_LOGIN = 13 - const val SERVER_BEING_UPDATED_WAIT = 14 - const val UNEXPECTED_SERVER_RESPONSE_15 = 15 - const val MAX_INCORRECT_LOGIN_AMOUNT = 16 - const val FREE_ACCOUNT_IN_MEMBERS_AREA = 17 - const val LOCKED_ACCOUNT_STOLEN = 18 - const val FULLSCREEN_MEMBERS_ONLY = 19 - const val MALFORMED_LOGIN_PACKET = 22 - const val NO_LOGIN_SERVER_REPLY = 23 - const val ERROR_LOADING_PROFILE = 24 - const val MAC_BANNED = 26 - const val SERVICE_UNAVAILABLE = 27 - const val NON_MEMBER_MEMBER_LOGIN = 30 - - //Additive/Specialized responses displayed || LoginServerResponse REQUIREMENT_WORLD (29) - const val PVP_COMBAT_MINIMUM_LEVEL_20 = 0 - const val PVP_CARRYING_LENT_ITEMS = 1 -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/constants/login/LoginOutgoingOpcode.kt b/Client/src/main/kotlin/org/rs09/client/constants/login/LoginOutgoingOpcode.kt deleted file mode 100644 index 9b0a6f63f..000000000 --- a/Client/src/main/kotlin/org/rs09/client/constants/login/LoginOutgoingOpcode.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.rs09.client.constants.login - -object LoginOutgoingOpcode { - - /** - * Networking - */ - const val INITIAL_CONNECTION = 14 - const val USER_RECONNECTING_LOGIN = 16 - const val USER_ADVERTISEMENT_FINISHED = 17 - const val USER_NORMAL_LOGIN = 18 - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/constants/login/LoginReceivedOpcode.kt b/Client/src/main/kotlin/org/rs09/client/constants/login/LoginReceivedOpcode.kt deleted file mode 100644 index b7f885240..000000000 --- a/Client/src/main/kotlin/org/rs09/client/constants/login/LoginReceivedOpcode.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.rs09.client.constants.login - -object LoginReceivedOpcode { - - /** - * Received server responses - */ - const val SUCCESSFUL_CONNECTION = 0 - const val DISPLAY_ADVERTISEMENT = 1 - const val SUCCESS = 2 - const val PROFILE_TRANSFERRING = 21 - const val NO_LOGIN_SERVER_REPLY = 23 - const val REQUIREMENT_WORLD = 29 - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/constants/login/LoginStage.kt b/Client/src/main/kotlin/org/rs09/client/constants/login/LoginStage.kt deleted file mode 100644 index 544948e24..000000000 --- a/Client/src/main/kotlin/org/rs09/client/constants/login/LoginStage.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.rs09.client.constants.login - -object LoginStage { - - const val ERROR_CHECK_SET_PORTS = 0 - const val INITIALIZE_SOCKET = 1 - const val INITIATE_USER_LOGIN = 2 - const val SEND_ENCRYPT_USER_CREDENTIALS = 3 - const val RECEIVE_SERVER_RESPONSE = 4 - const val SEND_ADVERTISEMENT_TO_USER = 5 - const val FINISH_DISPLAYING_ADVERTISEMENT = 6 - const val DISPLAY_PROFILE_TRANSFERRING = 7 - const val LOGIN_ACCEPTED_GET_USER_DATA = 8 - const val FINISH_USER_LOGIN_SETUP = 9 - const val DISPLAY_REQUIREMENT_WORLD_RESPONSE = 10 - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/data/HashTable.kt b/Client/src/main/kotlin/org/rs09/client/data/HashTable.kt deleted file mode 100644 index 591c89513..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/HashTable.kt +++ /dev/null @@ -1,134 +0,0 @@ -package org.rs09.client.data - -import org.rs09.client.Linkable -import java.util.* - -class HashTable(var capacity: Int) { - private var retrievalLinkable: Linkable? = null - private var iterationLinkable: Linkable? = null - private var retrievalKey: Long = 0 - private var iterationIndex = 0 - - var buckets: Array = arrayOfNulls(capacity) - - fun clear() { - var i = 0 - while (i < capacity) { - val bucket = buckets[i] - while (true) { - val next = bucket!!.next - if (bucket === next) { - ++i - break - } - next!!.unlink() - } - } - iterationLinkable = null - retrievalLinkable = null - } - - fun first(): T? { - iterationIndex = 0 - return next() - } - - operator fun next(): T? { - var next: Linkable? - if (iterationIndex > 0 && iterationLinkable !== buckets[iterationIndex - 1]) { - next = iterationLinkable - } else { - do { - if (capacity <= iterationIndex) { - return null - } - next = buckets[iterationIndex++]!!.next - } while (buckets[iterationIndex + -1] === next) - } - iterationLinkable = next!!.next - return next as? T - } - - fun put(key: Long, value: T) { - if (value!!.previous != null) { - value.unlink() - } - val bucket = buckets[(key and (capacity - 1).toLong()).toInt()] - value.next = bucket - value.linkableKey = key - value.previous = bucket!!.previous - value.previous!!.next = value - value.next!!.previous = value - } - - operator fun get(key: Long): T? { - retrievalKey = key - val head = Objects.requireNonNull(buckets)[(key and (capacity - 1).toLong()).toInt()]!! - retrievalLinkable = head.next - while (head !== retrievalLinkable) { - if (retrievalLinkable!!.linkableKey == key) { - val value = retrievalLinkable - retrievalLinkable = retrievalLinkable!!.next - return value as T? - } - retrievalLinkable = retrievalLinkable!!.next - } - retrievalLinkable = null - return null - } - - fun size(): Int { - var size = 0 - for (i in 0 until capacity) { - val bucket = buckets[i] - var next = bucket!!.next - while (next !== bucket) { - next = next!!.next - ++size - } - } - return size - } - - fun values(values: Array) { - var count = 0 - for (i in 0 until capacity) { - val head = buckets[i] - var next = head!!.next - while (next !== head) { - values[count++] = next as T? - next = next!!.next - } - } - } - - fun nextInBucket(): T? { - if (retrievalLinkable == null) { - return null - } - val linkable = buckets[(retrievalKey and (-1 + capacity).toLong()).toInt()] - while (linkable !== retrievalLinkable) { - if (retrievalLinkable!!.linkableKey == retrievalKey) { - val value = retrievalLinkable - retrievalLinkable = retrievalLinkable!!.next - return value as T? - } - retrievalLinkable = retrievalLinkable!!.next - } - retrievalLinkable = null - return null - } - - fun capacity(): Int { - return capacity - } - - init { - for (i in 0 until capacity) { - val bucket = Linkable() - buckets[i] = bucket - bucket.previous = bucket - bucket.next = bucket - } - } -} diff --git a/Client/src/main/kotlin/org/rs09/client/data/NodeCache.kt b/Client/src/main/kotlin/org/rs09/client/data/NodeCache.kt deleted file mode 100644 index 2477b4fb2..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/NodeCache.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.rs09.client.data - -import org.rs09.client.Node -import java.util.* - -// TODO Better name -class NodeCache(private val capacity: Int) { - private var empty = Node() - private val table: HashTable - private var remaining: Int - private val history = Queue() - - operator fun get(key: Long): T? { - val value = table[key] - if (value != null) { - history.offer(value) - } - return value - } - - fun first(): T? { - return table.first() - } - - fun put(key: Long, value: T) { - if (remaining == 0) { - var history: Node? = history.poll() - Objects.requireNonNull(history)!!.unlink() - history!!.unlinkNode() - if (empty === history) { - history = this.history.poll() - Objects.requireNonNull(history)!!.unlink() - history!!.unlinkNode() - } - } else { - remaining-- - } - table.put(key, value) - history.offer(value) - } - - operator fun next(): T? { - return table.next() - } - - fun clear() { - history.clear() - table.clear() - empty = Node() - this.remaining = capacity - } - - init { - this.remaining = capacity - var size = 1 - while (size - -size < remaining) { - size += size - } - table = HashTable(size) - } -} diff --git a/Client/src/main/kotlin/org/rs09/client/data/Queue.kt b/Client/src/main/kotlin/org/rs09/client/data/Queue.kt deleted file mode 100644 index 97f7f5dc0..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/Queue.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.rs09.client.data - -import org.rs09.client.Node - -class Queue { - private val tail = Node() - private var current: Node? = null - - fun size(): Int { - var size = 0 - var var3 = tail.nextNode - while (var3 !== tail) { - var3 = var3!!.nextNode - ++size - } - return size - } - - fun getFront(): T? { - val front = tail.nextNode - if (tail === front) { - current = null - return null - } - current = front!!.nextNode - return front as T? - } - - fun poll(): T? { - val next = tail.nextNode - if (next === tail) { - return null - } - next!!.unlinkNode() - return next as T? - } - - operator fun next(): T? { - val current = current - if (current === tail) { - this.current = null - return null - } - this.current = current!!.nextNode - return current as T? - } - - fun offer(node: T) { - if (node!!.previousNode != null) { - node.unlinkNode() - } - node.previousNode = tail.previousNode - node.nextNode = tail - node.previousNode!!.nextNode = node - node.nextNode!!.previousNode = node - } - - fun clear() { - while (true) { - val next = tail.nextNode - if (tail === next) { - current = null - return - } - next!!.unlinkNode() - } - } - - init { - tail.nextNode = tail - tail.previousNode = tail - } -} diff --git a/Client/src/main/kotlin/org/rs09/client/data/ReferenceCache.kt b/Client/src/main/kotlin/org/rs09/client/data/ReferenceCache.kt deleted file mode 100644 index 2ab019603..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/ReferenceCache.kt +++ /dev/null @@ -1,125 +0,0 @@ -package org.rs09.client.data - -import org.rs09.client.Node.Companion.splice -import org.rs09.client.data.reference.HardObjectReference -import org.rs09.client.data.reference.ObjectReference -import org.rs09.client.data.reference.SoftObjectReferenceTransformer.transform -import java.util.* - -class ReferenceCache(capacity: Int) { - private var remaining: Int - private val capacity: Int - private val history = Queue>() - private val table: HashTable> - - fun put(value: T, key: Long) { - this.remove(key) - if (remaining == 0) { - val last = history.poll()!! - Objects.requireNonNull(last).unlink() - last.unlinkNode() - } else { - remaining-- - } - val reference = HardObjectReference(value) - table.put(key, reference) - history.offer(reference) - reference.nodeKey = 0L - } - - // TODO I added the return value here, but is that OK or will it break things - fun remove(key: Long): T? { - val previous = table[key] - if (previous != null) { - previous.unlink() - previous.unlinkNode() - remaining++ - return previous.getValue() - } - return null - } - - fun hardCount(): Int { - var count = 0 - var reference = history.getFront() - while (reference != null) { - if (!reference.isSoftReference()) { - count++ - } - reference = history.next() - } - return count - } - - fun sweep(maximumAge: Int) { - var reference: ObjectReference? = history.getFront() - while (null != reference) { - if (!reference.isSoftReference()) { - if (++reference.nodeKey > maximumAge.toLong()) { - val soft: ObjectReference = transform(reference) - table.put(reference.linkableKey, soft) - splice(reference, soft) - reference.unlink() - reference.unlinkNode() - } - } else if (null == reference.getValue()) { - reference.unlink() - reference.unlinkNode() - ++remaining - } - reference = history.next() - } - } - - fun clearSoftReferences() { - var reference = history.getFront() - while (reference != null) { - if (reference.isSoftReference()) { - reference.unlink() - reference.unlinkNode() - remaining++ - } - reference = history.next() - } - } - - fun clear() { - history.clear() - table.clear() - remaining = capacity - } - - operator fun get(key: Long): T? { - val reference = table[key] ?: return null - val value = reference.getValue() - return if (value == null) { - reference.unlink() - reference.unlinkNode() - remaining++ - null - } else { - if (reference.isSoftReference()) { - val hard = HardObjectReference(value) - table.put(reference.linkableKey, hard) - history.offer(hard) - hard.nodeKey = 0L - reference.unlink() - reference.unlinkNode() - } else { - history.offer(reference) - reference.nodeKey = 0L - } - value - } - } - - init { - var size = 1 - while (size + size < capacity) { - size += size - } - this.capacity = capacity - remaining = capacity - table = HashTable(size) - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/data/reference/HardObjectReference.kt b/Client/src/main/kotlin/org/rs09/client/data/reference/HardObjectReference.kt deleted file mode 100644 index 3d10fda86..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/reference/HardObjectReference.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.rs09.client.data.reference - -class HardObjectReference(private val value: T?) : ObjectReference() { - override fun getValue(): T? = value - override fun isSoftReference(): Boolean = false -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/data/reference/ObjectReference.kt b/Client/src/main/kotlin/org/rs09/client/data/reference/ObjectReference.kt deleted file mode 100644 index c24c40471..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/reference/ObjectReference.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.rs09.client.data.reference - -import org.rs09.client.Node - -abstract class ObjectReference: Node() { - abstract fun getValue(): T? - abstract fun isSoftReference(): Boolean -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/data/reference/ReferenceTransformer.kt b/Client/src/main/kotlin/org/rs09/client/data/reference/ReferenceTransformer.kt deleted file mode 100644 index 1ecb91561..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/reference/ReferenceTransformer.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.rs09.client.data.reference - -abstract class ReferenceTransformer { - abstract fun transform(from: ObjectReference): ObjectReference -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/data/reference/SoftObjectReference.kt b/Client/src/main/kotlin/org/rs09/client/data/reference/SoftObjectReference.kt deleted file mode 100644 index d247115f7..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/reference/SoftObjectReference.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.rs09.client.data.reference - -import java.lang.ref.SoftReference - -class SoftObjectReference(value: T?) : ObjectReference() { - private val value = SoftReference(value) - - override fun getValue(): T? = value.get() - override fun isSoftReference(): Boolean = true -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/data/reference/SoftObjectReferenceTransformer.kt b/Client/src/main/kotlin/org/rs09/client/data/reference/SoftObjectReferenceTransformer.kt deleted file mode 100644 index 2aef55c0c..000000000 --- a/Client/src/main/kotlin/org/rs09/client/data/reference/SoftObjectReferenceTransformer.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.rs09.client.data.reference - -/** - * Transforms all references it receives into soft references - */ -object SoftObjectReferenceTransformer : ReferenceTransformer() { - override fun transform(from: ObjectReference): ObjectReference = SoftObjectReference(from.getValue()) -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/LookupTable.kt b/Client/src/main/kotlin/org/rs09/client/filestore/LookupTable.kt deleted file mode 100644 index e90c7de80..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/LookupTable.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.rs09.client.filestore - -class LookupTable(identifiers: IntArray) { - private val table: IntArray - - operator fun get(identifier: Int): Int { - val mask = (table.size shr 1) + -1 - var i = mask and identifier - - while (true) { - val id = table[1 + i + i] - if (id == -1) { - return -1 - } - if (identifier == table[i + i]) { - return id - } - i = i - -1 and mask - } - } - - init { - var i = 1 - while (i <= (identifiers.size shr 1) + identifiers.size) { - i = i shl 1 - } - - table = IntArray(i + i) - for (j in 0 until i + i) table[j] = -1 - - for (j in identifiers.indices) { - var k = identifiers[j] and i - 1 - while (-1 != table[1 + (k + k)]) { - k = 1 + k and i - 1 - } - table[k + k] = identifiers[j] - table[k + k + 1] = j - } - } -} diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/ReferenceTable.kt b/Client/src/main/kotlin/org/rs09/client/filestore/ReferenceTable.kt deleted file mode 100644 index d75a876c1..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/ReferenceTable.kt +++ /dev/null @@ -1,118 +0,0 @@ -package org.rs09.client.filestore - -import org.rs09.client.DataBuffer -import org.rs09.client.filestore.compression.Container -import org.rs09.client.util.CRC - -class ReferenceTable(data: ByteArray, crc: Int) { - val crc: Int = CRC.crc32(data, data.size) - var revision = 0 - var validArchiveAmount = 0 - var archiveAmount = 0 - lateinit var validArchiveIds: IntArray - lateinit var archiveRevisions: IntArray - lateinit var validFileIds: Array - lateinit var archiveCRCs: IntArray - lateinit var archiveLengths: IntArray - lateinit var archiveFileLengths: IntArray - lateinit var archiveNameHash: IntArray - var aLookupTable_949: LookupTable? = null - var aLookupTableArray962: Array? = null - var fileNameHashes: Array? = null - - private fun decode(data: ByteArray) { - val buffer = DataBuffer(Container.decode(data)) - val format = buffer.readUnsignedByte() - check(format == 5 || format == 6) { "Unexpected ReferenceTable format $format. Expected 5 or 6." } - revision = if (format >= 6) buffer.readInt() else 0 - - val fields = buffer.readUnsignedByte() - - validArchiveAmount = buffer.readUnsignedShort() - validArchiveIds = IntArray(validArchiveAmount) - - var offset = 0 - var highest = -1 - - for (i in 0 until validArchiveAmount) { - offset += buffer.readUnsignedShort() - validArchiveIds[i] = offset - if (validArchiveIds[i] > highest) - highest = validArchiveIds[i] - } - - archiveAmount = highest - -1 - archiveRevisions = IntArray(archiveAmount) - validFileIds = arrayOfNulls(archiveAmount) - archiveCRCs = IntArray(archiveAmount) - archiveLengths = IntArray(archiveAmount) - archiveFileLengths = IntArray(archiveAmount) - - if (fields != 0) { - archiveNameHash = IntArray(archiveAmount) - - for (i in 0 until archiveAmount) - archiveNameHash[i] = -1 - - for (i in 0 until validArchiveAmount) - archiveNameHash[validArchiveIds[i]] = buffer.readInt() - - aLookupTable_949 = LookupTable(archiveNameHash) - } - - for (i in 0 until validArchiveAmount) - archiveCRCs[validArchiveIds[i]] = buffer.readInt() - - for (i in 0 until validArchiveAmount) - archiveRevisions[validArchiveIds[i]] = buffer.readInt() - - for (i in 0 until validArchiveAmount) - archiveFileLengths[validArchiveIds[i]] = buffer.readUnsignedShort() - - for (i in 0 until validArchiveAmount) { - offset = 0 - val archiveId = validArchiveIds[i] - val archiveFileCount = archiveFileLengths[archiveId] - var highestFileId = -1 - validFileIds[archiveId] = IntArray(archiveFileCount) - for (file in 0 until archiveFileCount) { - offset += buffer.readUnsignedShort() - validFileIds[archiveId]!![file] = offset - if (offset > highestFileId) highestFileId = offset - } - archiveLengths[archiveId] = highestFileId + 1 - if (archiveFileCount == highestFileId + 1) - validFileIds[archiveId] = null - } - - if (fields != 0) { - val aClass69Array962 = arrayOfNulls(highest + 1) - val fileNameHashes = arrayOfNulls(highest + 1) - - for (i in 0 until validArchiveAmount) { - val archiveId = validArchiveIds[i] - val archiveFileLength = archiveFileLengths[archiveId] - val archiveFileNameHashes = IntArray(archiveLengths[archiveId]) - fileNameHashes[archiveId] = archiveFileNameHashes - - for (j in 0 until archiveLengths[archiveId]) - archiveFileNameHashes[j] = -1 - - for (j in 0 until archiveFileLength) { - val fileIndex = if (validFileIds[archiveId] == null) j else validFileIds[archiveId]!![j] - archiveFileNameHashes[fileIndex] = buffer.readInt() - } - aClass69Array962[archiveId] = LookupTable(archiveFileNameHashes) - } - - this.fileNameHashes = fileNameHashes - this.aLookupTableArray962 = aClass69Array962 - } - } - - init { - if (this.crc != crc) - throw IllegalArgumentException("CRC mismatch - expected $crc, calculated ${this.crc}") - decode(data) - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/ResourceProvider.kt b/Client/src/main/kotlin/org/rs09/client/filestore/ResourceProvider.kt deleted file mode 100644 index fab705ef8..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/ResourceProvider.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.rs09.client.filestore - -// TODO Merge with Class151 -abstract class ResourceProvider { - abstract fun getReferenceTable(): ReferenceTable? - - abstract fun request(file: Int) - - abstract fun percentComplete(file: Int): Int - - abstract fun get(file: Int): ByteArray? -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/compression/Container.kt b/Client/src/main/kotlin/org/rs09/client/filestore/compression/Container.kt deleted file mode 100644 index c88ac9629..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/compression/Container.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.rs09.client.filestore.compression - -import org.rs09.client.DataBuffer -import org.runite.client.Bzip2Decompressor - -object Container { - private const val NONE = 0 - - fun decode(data: ByteArray): ByteArray { - val buffer = DataBuffer(data) - val compression = buffer.readUnsignedByte() - val size = buffer.readInt() - - check(size >= 0) { "Container had data size of < 0: $size with compression $compression" } - - if (compression == NONE) { - return buffer.readBytes(size) - } - - val uncompressedSize = buffer.readInt() - check(uncompressedSize >= 0) { "Container had uncompressed data size of < 0: $size with compression $compression" } - - val uncompressed = ByteArray(uncompressedSize) - if (compression == 1) { - Bzip2Decompressor.decompress(uncompressed, uncompressedSize, data) - } else { - GzipDecompressor.decompress(buffer, uncompressed) - } - return uncompressed - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/compression/GzipDecompressor.kt b/Client/src/main/kotlin/org/rs09/client/filestore/compression/GzipDecompressor.kt deleted file mode 100644 index 089a429ed..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/compression/GzipDecompressor.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.rs09.client.filestore.compression - -import org.rs09.client.DataBuffer -import java.util.zip.Inflater - -object GzipDecompressor { - private var inflater = Inflater(true) - - fun decompress(buffer: DataBuffer, out: ByteArray) { - if (buffer.buffer[buffer.index].toInt() != 31 || buffer.buffer[buffer.index + 1].toInt() != -117) - throw RuntimeException("Invalid GZIP header!") - - try { - inflater.setInput(buffer.buffer, buffer.index + 10, -8 - (10 + buffer.index) + buffer.buffer.size) - inflater.inflate(out) - } catch (var5: Exception) { - inflater.reset() - throw RuntimeException("Invalid GZIP compressed data!") - } - inflater.reset() - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/cursors/CursorDefinition.kt b/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/cursors/CursorDefinition.kt deleted file mode 100644 index 9537236a6..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/cursors/CursorDefinition.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.rs09.client.filestore.resources.configs.cursors - -import org.runite.client.* - -class CursorDefinition { - var hotspotX = 0 - var hotspotY = 0 - var imageId = 0 - - fun getImage(): SoftwareSprite { - var image = Class163_Sub1.aReferenceCache_2984.get(imageId.toLong()) as? SoftwareSprite - if (image != null) return image - - image = Unsorted.method562(SequenceDefinition.aClass153_1852, imageId) - if (image != null) Class163_Sub1.aReferenceCache_2984.put(image, imageId.toLong()) - return image - } - - fun decode(buffer: DataBuffer) { - while (true) { - val opcode = buffer.readUnsignedByte() - if (opcode == 0) break - - when (opcode) { - 1 -> imageId = buffer.readUnsignedShort() - 2 -> { - hotspotX = buffer.readUnsignedByte() - hotspotY = buffer.readUnsignedByte() - } - else -> throw IllegalArgumentException("unknown CursorDefinition opcode $opcode") - } - } - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/enums/EnumDefinition.kt b/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/enums/EnumDefinition.kt deleted file mode 100644 index 8afa31b87..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/enums/EnumDefinition.kt +++ /dev/null @@ -1,130 +0,0 @@ -package org.rs09.client.filestore.resources.configs.enums - -import org.rs09.client.Linkable -import org.rs09.client.LinkableInt -import org.rs09.client.Node -import org.rs09.client.data.HashTable -import org.runite.client.* - -class EnumDefinition : Node() { - companion object { - private val NULL = RSString.parse("null") - } - - var keyType = 0 - var valueType = 0 - - var values: HashTable? = null - var valueLookup: HashTable? = null - - var defaultString = NULL - var defaultInt = 0 - - fun decode(buffer: DataBuffer) { - while (true) { - val opcode = buffer.readUnsignedByte() - if (opcode == 0) break - - when (opcode) { - 1 -> keyType = buffer.readUnsignedByte() - 2 -> valueType = buffer.readUnsignedByte() - 3 -> defaultString = buffer.readString() - 4 -> defaultInt = buffer.readInt() - 5, 6 -> { - val size = buffer.readUnsignedShort() - val values = HashTable(Class95.method1585(94, size)) - - for (i in 0 until size) { - val key = buffer.readInt() - - val value: Linkable = if (opcode == 5) LinkableRSString(buffer.readString()) - else LinkableInt(buffer.readInt()) - - values.put(key.toLong(), value) - } - - this.values = values - } - else -> throw IllegalArgumentException("unknown EnumDefinition opcode $opcode") - } - } - } - - fun getString(key: Int): RSString { - val values = this.values ?: return defaultString - - val value = values[key.toLong()] as LinkableRSString? - return value?.value ?: defaultString - } - - fun getInt(key: Int): Int { - val values = this.values ?: return defaultInt - - val value = values[key.toLong()] as LinkableInt? - return value?.value ?: defaultInt - } - - private fun buildIntValueLookup() { - val values = requireNotNull(this.values) - val valueLookup = HashTable(values.capacity()) - - var linkable = values.first() as LinkableInt? - while (linkable != null) { - val reversed = LinkableInt(linkable.linkableKey.toInt()) - valueLookup.put(linkable.value.toLong(), reversed) - linkable = values.next() as LinkableInt? - } - - this.valueLookup = valueLookup - } - - private fun buildStringValueLookup() { - val values = requireNotNull(this.values) - val valueLookup = HashTable(values.capacity()) - - var linkable = values.first() as LinkableRSString? - while (linkable != null) { - val reversed = Class3_Sub10(linkable.value, linkable.linkableKey.toInt()) - valueLookup.put(linkable.value.hash(), reversed) - linkable = values.next() as LinkableRSString? - } - - this.valueLookup = valueLookup - } - - fun containsValue(value: Int): Boolean { - if (values == null) { - return false - } - - if (valueLookup == null) { - buildIntValueLookup() - } - - val lookup = valueLookup ?: return false - return lookup[value.toLong()] != null - } - - fun containsValue(value: RSString): Boolean { - if (values == null) { - return false - } - - if (valueLookup == null) { - buildStringValueLookup() - } - - val lookup = valueLookup ?: return false - - var head = lookup[value.hash()] as Class3_Sub10? - while (head != null) { - if (head.value.equalsString(value)) { - return true - } - head = lookup.nextInBucket() as Class3_Sub10? - } - - return false - } - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/enums/EnumDefinitionProvider.kt b/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/enums/EnumDefinitionProvider.kt deleted file mode 100644 index b234e097c..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/enums/EnumDefinitionProvider.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.rs09.client.filestore.resources.configs.enums - -import org.runite.client.CacheIndex -import org.runite.client.DataBuffer -import org.rs09.client.data.NodeCache - -object EnumDefinitionProvider { - private val cache = NodeCache(128) - private lateinit var index: CacheIndex - - @JvmStatic - fun provide(id: Int): EnumDefinition { - var def = cache.get(id.toLong()) - if (def != null) return def - - def = EnumDefinition() - val bytes = index.getFile(id ushr 8, id and 0xff) - if (bytes != null) def.decode(DataBuffer(bytes)) - - cache.put(id.toLong(), def) - return def - } - - @JvmStatic - fun setIndex(index: CacheIndex) { - this.index = index - } - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/structs/StructDefinition.kt b/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/structs/StructDefinition.kt deleted file mode 100644 index 4d8590f37..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/structs/StructDefinition.kt +++ /dev/null @@ -1,54 +0,0 @@ -package org.rs09.client.filestore.resources.configs.structs - -import org.rs09.client.Linkable -import org.rs09.client.LinkableInt -import org.rs09.client.Node -import org.rs09.client.data.HashTable -import org.runite.client.Class95 -import org.runite.client.DataBuffer -import org.runite.client.LinkableRSString -import org.runite.client.RSString - -class StructDefinition: Node() { - var values: HashTable? = null - - fun decode(buffer: DataBuffer) { - while (true) { - val opcode = buffer.readUnsignedByte() - if (opcode == 0) break - - when (opcode) { - 249 -> { - val size = buffer.readUnsignedByte() - val values = HashTable(Class95.method1585(105, size)) - - for (i in 0 until size) { - val isString = buffer.readUnsignedByte() == 1 - val key = buffer.readMedium() - - val value = if (isString) LinkableRSString(buffer.readString()) - else LinkableInt(buffer.readInt()) - - values.put(key.toLong(), value) - } - - this.values = values - } - else -> throw IllegalArgumentException("unknown StructDefinition opcode $opcode") - } - } - } - - fun getInt(key: Int, orElse: Int): Int { - val values = this.values ?: return orElse - val value = values[key.toLong()] as LinkableInt? - return value?.value ?: orElse - } - - fun getString(key: Int, orElse: RSString): RSString { - val values = this.values ?: return orElse - - val value = values[key.toLong()] as LinkableRSString? - return value?.value ?: orElse - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/structs/StructDefinitionProvider.kt b/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/structs/StructDefinitionProvider.kt deleted file mode 100644 index b14dcf822..000000000 --- a/Client/src/main/kotlin/org/rs09/client/filestore/resources/configs/structs/StructDefinitionProvider.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.rs09.client.filestore.resources.configs.structs - -import org.runite.client.CacheIndex -import org.runite.client.DataBuffer -import org.rs09.client.data.NodeCache - -object StructDefinitionProvider { - private val cache = NodeCache(64) - private lateinit var index: CacheIndex - - @JvmStatic - fun provide(id: Int): StructDefinition? { - var def = cache.get(id.toLong()) - if (def != null) return def - - def = StructDefinition() - val bytes = index.getFile(26, id) - if (bytes != null) def.decode(DataBuffer(bytes)) - - cache.put(id.toLong(), def) - return def - } - - @JvmStatic - fun setIndex(index: CacheIndex) { - this.index = index - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/net/Connection.kt b/Client/src/main/kotlin/org/rs09/client/net/Connection.kt deleted file mode 100644 index 15abf0b15..000000000 --- a/Client/src/main/kotlin/org/rs09/client/net/Connection.kt +++ /dev/null @@ -1,189 +0,0 @@ -package org.rs09.client.net - -import org.runite.client.Class64 -import org.runite.client.Signlink -import org.runite.client.TimeUtils -import java.io.EOFException -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream -import java.net.Socket -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.withLock - -class Connection(val socket: Socket, val signlink: Signlink) : Runnable { - private val lock = ReentrantLock() - private val condition = lock.newCondition() - - private var inputStream: InputStream - private var outputStream: OutputStream - private var closed = false - private var writeFailed = false - private var outputBuffer: ByteArray? = null - private var writeIndex = 0 - private var someOtherIndex = 0 - private var class64: Class64? = null - - init { - socket.soTimeout = 30_000 - socket.tcpNoDelay = true - inputStream = socket.getInputStream() - outputStream = socket.getOutputStream() - } - - override fun run() { - while (true) { - var bufferOffset: Int - var transmitLength: Int - - lock.lock() - try { - if (writeIndex == someOtherIndex) { - if (closed) break - try { - condition.await() - } catch (e: InterruptedException) { - } - } - - bufferOffset = someOtherIndex - transmitLength = if (writeIndex < someOtherIndex) 5000 - someOtherIndex - else writeIndex - someOtherIndex - } finally { - lock.unlock() - } - - if (transmitLength <= 0) continue - - try { - outputStream.write(outputBuffer!!, bufferOffset, transmitLength) - } catch (e: IOException) { - e.printStackTrace() - writeFailed = true - } - - someOtherIndex = (transmitLength + someOtherIndex) % 5000 - - try { - if (someOtherIndex != writeIndex) continue - - outputStream.flush() - } catch (e: IOException) { - e.printStackTrace() - writeFailed = true - } - } - - try { - inputStream.close() - outputStream.close() - socket.close() - } catch (e: IOException) { - e.printStackTrace() - } - - outputBuffer = null - } - - @Throws(IOException::class) - fun readBytes(buffer: ByteArray, offset: Int, length: Int) { - check(offset >= 0) { "Offset <= 0 in readBytes" } - check(length >= 0) { "Read length <= 0 in readBytes" } - check(offset + length <= buffer.size) { "offset + length is greater than buffer size" } - check(!closed) { "Attempted to read from closed connection" } - - var remaining = length - var bufferOffset = offset - while (remaining > 0) { - val bytesRead = inputStream.read(buffer, bufferOffset, remaining) - if (bytesRead <= 0) - throw EOFException() - - bufferOffset += bytesRead - remaining -= bytesRead - } - } - - @Throws(IOException::class) - fun readByte(): Int { - check(!closed) { "Attempted to read from closed connection" } - return inputStream.read() - } - - @Throws(IOException::class) - fun sendBytes(buffer: ByteArray, length: Int) { - check(!closed) { "Attempted to write to closed connection" } - - if (writeFailed) { - writeFailed = false - throw IOException("Attempted to write straight after failed write!") - } - - val out = outputBuffer ?: ByteArray(5000).apply { outputBuffer = this } - - lock.withLock { - for (i in 0 until length) { - out[writeIndex] = buffer[i] - writeIndex = (writeIndex + 1) % 5000 - - if ((4900 + someOtherIndex) % 5000 == writeIndex) { - throw IOException("Buffer overflow") - } - } - - if (class64 == null) { - class64 = signlink.startThread(3, this) - } - - condition.signalAll() - } - } - - fun finalize() { - if (!closed) { - System.err.println("Finalized connection, but it wasn't closed! Did we leak?") - close() - } - } - - fun checkErrors() { - if (closed || !writeFailed) return - - writeFailed = false - throw IOException("So why are we throwing this now?") - } - - @Throws(IOException::class) - fun availableBytes(): Int = if (closed) 0 else inputStream.available() - - fun applyDummyStreams() { - if (closed) return - - inputStream = DummyInputStream() - outputStream = DummyOutputStream() - } - - fun close() { - if (closed) return - - lock.withLock { - closed = true - condition.signalAll() - } - - class64?.let { class64 -> - while (class64.anInt978 == 0) TimeUtils.sleep(1L) - - if (class64.anInt978 == 1) { - try { - (class64.anObject974 as Thread).join() - } catch (e: InterruptedException) { - e.printStackTrace() - } - } - } - - class64 = null - } - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/net/DummyInputStream.kt b/Client/src/main/kotlin/org/rs09/client/net/DummyInputStream.kt deleted file mode 100644 index 912aaa632..000000000 --- a/Client/src/main/kotlin/org/rs09/client/net/DummyInputStream.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.rs09.client.net - -import org.runite.client.TimeUtils -import java.io.InputStream - -class DummyInputStream : InputStream() { - override fun read(): Int { - TimeUtils.sleep(30000L) - return -1 - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/net/DummyOutputStream.kt b/Client/src/main/kotlin/org/rs09/client/net/DummyOutputStream.kt deleted file mode 100644 index 8e7b8998a..000000000 --- a/Client/src/main/kotlin/org/rs09/client/net/DummyOutputStream.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.rs09.client.net - -import java.io.IOException -import java.io.OutputStream - -class DummyOutputStream: OutputStream() { - override fun write(b: Int) { - throw IOException("Attempted to write to DummyOutputStream") - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/net/game/PacketDecoder.kt b/Client/src/main/kotlin/org/rs09/client/net/game/PacketDecoder.kt deleted file mode 100644 index 25f7401f4..000000000 --- a/Client/src/main/kotlin/org/rs09/client/net/game/PacketDecoder.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.rs09.client.net.game - -import org.rs09.client.net.game.inbound.ConsoleAutocompletionPacketDecoder -import org.rs09.client.net.game.inbound.ConsoleMessageDecoder -import org.rs09.client.net.game.inbound.GamePacketDecoder -import org.runite.client.* -import java.io.IOException - -object PacketDecoder { - val decoders = HashMap() - var OPCODE_OFFSET = 241 - - private fun registerCustomDecoder(size: Int, decoder: GamePacketDecoder): Int { - val opcode = OPCODE_OFFSET++ - - Class75_Sub4.incomingPacketSizes[opcode] = size - decoders[opcode] = decoder - return opcode - } - - init { - // VERY IMPORTANT - WHEN ADDING NEW PACKETS, ADD THEM AFTER THE OLD PACKETS - // OTHERWISE YOU >>WILL<< MESS UP OPCODES - - registerCustomDecoder(-2, ConsoleMessageDecoder) - registerCustomDecoder(-2, ConsoleAutocompletionPacketDecoder) -// println("NOTE > Registered ConsoleAutocompletionPacket as opcode $completion") - } - - @Throws(IOException::class) - fun decodePacket(): Boolean { - val connection = Class3_Sub15.activeConnection ?: return false - var availableBytes = connection.availableBytes() - if (availableBytes <= 0) return false - - if (Unsorted.incomingOpcode == -1) { - availableBytes-- - Class3_Sub15.activeConnection.readBytes(BufferedDataStream.incomingBuffer.buffer, 0, 1) - BufferedDataStream.incomingBuffer.index = 0 - Unsorted.incomingOpcode = BufferedDataStream.incomingBuffer.opcode - Unsorted.incomingPacketLength = Class75_Sub4.incomingPacketSizes[Unsorted.incomingOpcode] - } - - if (Unsorted.incomingPacketLength == -1) { - if (availableBytes < 1) return false - - availableBytes-- - Class3_Sub15.activeConnection.readBytes(BufferedDataStream.incomingBuffer.buffer, 0, 1) - Unsorted.incomingPacketLength = BufferedDataStream.incomingBuffer.buffer[0].toInt() and 0xff - } - - if (Unsorted.incomingPacketLength == -2) { - if (availableBytes < 2) return false - - availableBytes -= 2 - Class3_Sub15.activeConnection.readBytes(BufferedDataStream.incomingBuffer.buffer, 0, 2) - BufferedDataStream.incomingBuffer.index = 0 - Unsorted.incomingPacketLength = BufferedDataStream.incomingBuffer.readUnsignedShort() - } - - if (availableBytes < Unsorted.incomingPacketLength) return false - - BufferedDataStream.incomingBuffer.index = 0 - Class3_Sub15.activeConnection.readBytes(BufferedDataStream.incomingBuffer.buffer, 0, Unsorted.incomingPacketLength) - Class24.anInt469 = Class7.anInt2166 - Class7.anInt2166 = LinkableRSString.anInt2582 - LinkableRSString.anInt2582 = Unsorted.incomingOpcode - AbstractSprite.anInt3699 = 0 - - val decoder = decoders[Unsorted.incomingOpcode] - if (decoder == null) return PacketParser.parseIncomingPackets(); - else decoder.decode(BufferedDataStream.incomingBuffer) - - // TODO This should only happen after everything else. - Unsorted.incomingOpcode = -1 - return true - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/net/game/inbound/ConsoleAutocompletionPacketDecoder.kt b/Client/src/main/kotlin/org/rs09/client/net/game/inbound/ConsoleAutocompletionPacketDecoder.kt deleted file mode 100644 index 81d9d11d3..000000000 --- a/Client/src/main/kotlin/org/rs09/client/net/game/inbound/ConsoleAutocompletionPacketDecoder.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.rs09.client.net.game.inbound - -import org.rs09.client.console.AutocompletionHints -import org.rs09.client.console.DeveloperConsole -import org.runite.client.DataBuffer - -object ConsoleAutocompletionPacketDecoder: GamePacketDecoder { - override fun decode(buffer: DataBuffer) { - val line = buffer.readString().toString() - val size = buffer.readUnsignedShort() - val list = ArrayList() - for(i in 0 until size) - list += buffer.readString().toString() - val total = buffer.readInt() - - DeveloperConsole.autocompletions = AutocompletionHints(line, list, total) - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/net/game/inbound/ConsoleMessageDecoder.kt b/Client/src/main/kotlin/org/rs09/client/net/game/inbound/ConsoleMessageDecoder.kt deleted file mode 100644 index d37d450ba..000000000 --- a/Client/src/main/kotlin/org/rs09/client/net/game/inbound/ConsoleMessageDecoder.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.rs09.client.net.game.inbound - -import org.rs09.client.console.DeveloperConsole -import org.runite.client.DataBuffer - -object ConsoleMessageDecoder: GamePacketDecoder { - override fun decode(buffer: DataBuffer) { - val line = buffer.readString() - - DeveloperConsole.println(line.toString()) - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/net/game/inbound/GamePacketDecoder.kt b/Client/src/main/kotlin/org/rs09/client/net/game/inbound/GamePacketDecoder.kt deleted file mode 100644 index d0ce6f2bd..000000000 --- a/Client/src/main/kotlin/org/rs09/client/net/game/inbound/GamePacketDecoder.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.rs09.client.net.game.inbound - -import org.runite.client.DataBuffer - -interface GamePacketDecoder { - fun decode(buffer: DataBuffer) -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/rendering/RenderingUtils.kt b/Client/src/main/kotlin/org/rs09/client/rendering/RenderingUtils.kt deleted file mode 100644 index ce9209fc8..000000000 --- a/Client/src/main/kotlin/org/rs09/client/rendering/RenderingUtils.kt +++ /dev/null @@ -1,93 +0,0 @@ -package org.rs09.client.rendering - -import org.runite.client.* - -/** - * A utility class to make rendering new things easier. This should only be used for new things. Ideally only port the - * necessary calls to this utility class. - * - * In the future, instead of utilizing this class, a toolkit system should be used. - */ -object RenderingUtils { - val hd: Boolean - get() = HDToolKit.highDetail - - // TODO Is this actually the width? - val width: Int - get() = Class23.canvasWidth - - // TODO Is this actually the height? - val height: Int - get() = Class140_Sub7.canvasHeight - - @JvmStatic - @Deprecated("Please use the Toolkit methods instead of this delegating method", ReplaceWith("Toolkit.getActiveToolkit().fillRect(x, y, w, h, rgb, alpha)")) - fun fillRect(x: Int, y: Int, w: Int, h: Int, rgb: Int, alpha: Int) { - Toolkit.getActiveToolkit().fillRect(x, y, w, h, rgb, alpha) -// if (hd) Class22.fillRectangle(x, y, w, h, rgb, alpha) -// else Class74.fillRectangle(x, y, w, h, rgb, alpha) - } - - @JvmStatic - fun drawHorizontalLine(x: Int, y: Int, w: Int, rgb: Int) { - Toolkit.getActiveToolkit().drawHorizontalLine(x, y, w, rgb) -// if (hd) Class22.drawHorizontalLine(x, y, w, rgb) -// else Toolkit.JAVA_TOOLKIT.drawHorizontalLine(x, y, w, rgb) - } - - @JvmStatic - fun drawVerticalLine(x: Int, y: Int, h: Int, rgb: Int) { - Toolkit.getActiveToolkit().drawVerticalLine(x,y,h,rgb) -// if (hd) Class22.drawVerticalLine(x, y, h, rgb) -// else Class74.drawVerticalLine(x, y, h, rgb) - } - - @JvmStatic - fun drawRect(x: Int, y: Int, w: Int, h: Int, rgb: Int, alpha: Int) { - Toolkit.getActiveToolkit().drawRect(x, y, w, h, rgb, alpha) -// if (hd) Class22.drawRect(x, y, w, h, rgb) -// else Class74.drawRect(x, y, w, h, rgb) - } - - fun drawText(str: String, x: Int, y: Int, rgb: Int, parse: Boolean = false) { - if (parse) FontType.plainFont.method681(RSString.parse(str), x, y, rgb, -1) - else FontType.plainFont.method681(RSString.of(str), x, y, rgb, -1) - } - - fun drawText(str: String, x: Int, y: Int, rgb: Int, shadow: Int, parse: Boolean = false){ - if (parse) FontType.plainFont.method681(RSString.parse(str), x, y, rgb, shadow) - else FontType.plainFont.method681(RSString.of(str), x, y, rgb, shadow) - } - - fun drawText(str: RSString, x: Int, y: Int, rgb: Int) { - FontType.plainFont.method681(str, x, y, rgb, -1) - } - - fun drawText(str: RSString, x: Int, y: Int, rgb: Int, shadow: Int) { - FontType.plainFont.method681(str, x, y, rgb, shadow) - } - - fun drawTextSmall(str: String, x: Int, y: Int, rgb: Int, parse: Boolean = false) { - if (parse) FontType.smallFont.method681(RSString.parse(str), x, y, rgb, -1) - else FontType.smallFont.method681(RSString.of(str), x, y, rgb, -1) - } - - fun drawTextSmall(str: String, x: Int, y: Int, rgb: Int, shadow: Int, parse: Boolean = false){ - if (parse) FontType.smallFont.method681(RSString.parse(str), x, y, rgb, shadow) - else FontType.smallFont.method681(RSString.of(str), x, y, rgb, shadow) - } - - fun drawTextSmall(str: RSString, x: Int, y: Int, rgb: Int) { - FontType.smallFont.method681(str, x, y, rgb, -1) - } - - fun setClipping(left: Int, top: Int, right: Int, bottom: Int) { - if (hd) Class22.setClipping(left, top, right, bottom) - else Class74.setClipping(left, top, right, bottom) - } - - fun resetClipping() { - if (hd) Class22.resetClipping() - else Class74.resetClipping() - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/rendering/Toolkit.kt b/Client/src/main/kotlin/org/rs09/client/rendering/Toolkit.kt deleted file mode 100644 index abad57f8b..000000000 --- a/Client/src/main/kotlin/org/rs09/client/rendering/Toolkit.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.rs09.client.rendering - -import org.rs09.client.rendering.java.JavaToolkit -import org.rs09.client.rendering.opengl.OpenGlToolkit - -/** - * Represents an abstract rendering toolkit. The toolkit should support all raw rendering operations including - * rasterization. - * - * By abstracting rendering through toolkits, additional rendering backends can be implemented in the future. - */ -abstract class Toolkit { - - companion object { - @JvmField - val OPENGL_TOOLKIT = OpenGlToolkit() - @JvmField - val JAVA_TOOLKIT = JavaToolkit() - - @JvmStatic - fun getActiveToolkit(): Toolkit { - return if (RenderingUtils.hd) OPENGL_TOOLKIT else JAVA_TOOLKIT - } - } - - abstract fun fillRect(x: Int, y: Int, w: Int, h: Int, rgb: Int, alpha: Int) - - abstract fun drawHorizontalLine(x: Int, y: Int, w: Int, rgb: Int) - - abstract fun drawVerticalLine(x: Int, y: Int, h: Int, rgb: Int) - - abstract fun drawRect(x: Int, y: Int, w: Int, h: Int, rgb: Int, alpha: Int) - - abstract fun method934(x: Int, y: Int, w: Int, h: Int, rgb: Int) - - - -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/rendering/java/JavaToolkit.kt b/Client/src/main/kotlin/org/rs09/client/rendering/java/JavaToolkit.kt deleted file mode 100644 index 676a9c179..000000000 --- a/Client/src/main/kotlin/org/rs09/client/rendering/java/JavaToolkit.kt +++ /dev/null @@ -1,150 +0,0 @@ -package org.rs09.client.rendering.java - -import org.rs09.client.rendering.Toolkit - -class JavaToolkit : Toolkit() { - var buffer: IntArray = IntArray(0) - - @JvmField - var clipLeft: Int = 0 - - @JvmField - var clipTop: Int = 0 - - @JvmField - var clipRight: Int = 0 - - @JvmField - var clipBottom: Int = 0 - - @JvmField - var width: Int = 0 - - @JvmField - var height: Int = 0 - - override fun fillRect(x: Int, y: Int, w: Int, h: Int, rgb: Int, alpha: Int) { - var x = x - var y = y - var width = w - var height = h - var rgb = rgb - - if (x < this.clipLeft) { - width -= this.clipLeft - x - x = this.clipLeft - } - - if (y < this.clipTop) { - height -= this.clipTop - y - y = this.clipTop - } - - if (x + width > this.clipRight) { - width = this.clipRight - x - } - - if (y + height > this.clipBottom) { - height = this.clipBottom - y - } - - rgb = ((rgb and 0xff00ff) * alpha shr 8 and 0xff00ff) + ((rgb and 0xff00) * alpha shr 8 and 0xff00) - val invertedOpacity = 256 - alpha - val skipPerLine = this.width - width - var offset = x + y * this.width - - for (lx in 0 until height) { - for (ly in -width..-1) { - var old = buffer[offset] - old = ((old and 0xff00ff) * invertedOpacity shr 8 and 0xff00ff) + ((old and 0xff00) * invertedOpacity shr 8 and 0xff00) - buffer[offset++] = rgb + old - } - offset += skipPerLine - } - } - - override fun drawHorizontalLine(x: Int, y: Int, w: Int, rgb: Int) { - var x = x - var width = w - - if (y < this.clipTop || y >= this.clipBottom) - return - - if (x < this.clipLeft) { - width -= this.clipLeft - x - x = this.clipLeft - } - if (x + width > this.clipRight) { - width = this.clipRight - x - } - - val var4 = x + y * this.width - for (var5 in 0 until width) { - buffer[var4 + var5] = rgb - } - } - - override fun drawVerticalLine(x: Int, y: Int, h: Int, rgb: Int) { - var y = y - var height = h - - if (x < this.clipLeft || x >= this.clipRight) - return - - if (y < this.clipTop) { - height -= this.clipTop - y - y = this.clipTop - } - if (y + height > this.clipBottom) { - height = this.clipBottom - y - } - - val var4 = x + y * this.width - for (var5 in 0 until height) { - buffer[var4 + var5 * width] = rgb - } - } - - override fun drawRect(x: Int, y: Int, w: Int, h: Int, rgb: Int, alpha: Int) { - drawHorizontalLine(x, y, w, rgb) - drawHorizontalLine(x, y + h - 1, w, rgb) - drawVerticalLine(x, y, h, rgb) - drawVerticalLine(x + w - 1, y, h, rgb) - } - - //Renamed from method1323 to match its openGL counterpart - override fun method934(x: Int, y: Int, w: Int, h: Int, rgb: Int) { - var x = x - var y = y - var width = w - var height = h - if (x < this.clipLeft) { - width -= this.clipLeft - x - x = this.clipLeft - } - if (y < this.clipTop) { - height -= this.clipTop - y - y = this.clipTop - } - if (x + w > this.clipRight) { - width = this.clipRight - x - } - if (y + h > this.clipBottom) { - height = this.clipBottom - y - } - val var5 = this.width - w - var var6 = x + y * this.width - - for (var7 in -height until 0) { - for (var8 in -width..-1) { - buffer[var6++] = rgb - } - var6 += var5; - } - } - - - fun resetBuffer() { - buffer = IntArray(0) - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/rendering/opengl/OpenGlToolkit.kt b/Client/src/main/kotlin/org/rs09/client/rendering/opengl/OpenGlToolkit.kt deleted file mode 100644 index de63f8aa4..000000000 --- a/Client/src/main/kotlin/org/rs09/client/rendering/opengl/OpenGlToolkit.kt +++ /dev/null @@ -1,87 +0,0 @@ -package org.rs09.client.rendering.opengl - -import org.rs09.client.rendering.Toolkit -import org.rs09.client.rendering.opengl.enums.GLBeginMode -import org.runite.client.HDToolKit - -class OpenGlToolkit: Toolkit() { - - override fun fillRect(x: Int, y: Int, w: Int, h: Int, rgb: Int, alpha: Int) { - HDToolKit.method1835() - val var6 = x.toFloat() - val var7 = var6 + w.toFloat() - val var8 = (HDToolKit.viewHeight - y).toFloat() - val var9 = var8 - h.toFloat() - val var10 = HDToolKit.gl - - var10.glBegin(GLBeginMode.TRIANGLE_FAN) - var10.glColor4ub((rgb shr 16).toByte(), (rgb shr 8).toByte(), rgb.toByte(), if (alpha > 255) -1 else alpha.toByte()) - var10.glVertex2f(var6, var8) - var10.glVertex2f(var6, var9) - var10.glVertex2f(var7, var9) - var10.glVertex2f(var7, var8) - var10.glEnd() - } - - override fun drawHorizontalLine(x: Int, y: Int, w: Int, rgb: Int) { - HDToolKit.method1835() - val startX = x.toFloat() + 0.3f - val endX = startX + w.toFloat() - val yPos = HDToolKit.viewHeight.toFloat() - (y.toFloat() + 0.3f) - val gl = HDToolKit.gl - - gl.glBegin(GLBeginMode.LINES) - gl.glColor3ub((rgb shr 16).toByte(), (rgb shr 8).toByte(), rgb.toByte()) - gl.glVertex2f(startX, yPos) - gl.glVertex2f(endX, yPos) - gl.glEnd() - } - - override fun drawVerticalLine(x: Int, y: Int, h: Int, rgb: Int) { - HDToolKit.method1835() - val var4 = x.toFloat() + .3f - val var5 = HDToolKit.viewHeight.toFloat() - (y.toFloat() + 0.3f) - val var6 = var5 - h.toFloat() - val var7 = HDToolKit.gl - - var7.glBegin(GLBeginMode.LINES) - var7.glColor3ub((rgb shr 16).toByte(), (rgb shr 8).toByte(), rgb.toByte()) - var7.glVertex2f(var4, var5) - var7.glVertex2f(var4, var6) - var7.glEnd() - } - - override fun drawRect(x: Int, y: Int, w: Int, h: Int, rgb: Int, alpha: Int) { - HDToolKit.method1835() - val var5 = x.toFloat() + 0.3f - val var6 = var5 + (w.toFloat() - 1) - val var7 = (HDToolKit.viewHeight.toFloat() - (y.toFloat() + 0.3f)) - val var8 = var7 - (h.toFloat() - 1) - val var9 = HDToolKit.gl - - var9.glBegin(GLBeginMode.LINE_LOOP) - var9.glColor4ub((rgb shr 16).toByte(), (rgb shr 8).toByte(), rgb.toByte(), if (alpha > 255) -1 else alpha.toByte()) - var9.glVertex2f(var5, var7) - var9.glVertex2f(var5, var8) - var9.glVertex2f(var6, var8) - var9.glVertex2f(var6, var7) - var9.glEnd() - } - - override fun method934(x: Int, y: Int, w: Int, h: Int, rgb: Int) { - HDToolKit.method1835() - val var5 = x.toFloat() - val var6 = var5 + w.toFloat() - val var7 = (HDToolKit.viewHeight - y).toFloat() - val var8 = var7 - h.toFloat() - val var9 = HDToolKit.gl - - var9.glBegin(GLBeginMode.TRIANGLE_FAN) - var9.glColor3ub((rgb shr 16).toByte(), (rgb shr 8).toByte(), rgb.toByte()) - var9.glVertex2f(var5, var7) - var9.glVertex2f(var5, var8) - var9.glVertex2f(var6, var8) - var9.glVertex2f(var6, var7) - var9.glEnd() - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/rendering/opengl/enums/GLBeginMode.kt b/Client/src/main/kotlin/org/rs09/client/rendering/opengl/enums/GLBeginMode.kt deleted file mode 100644 index b0182a15e..000000000 --- a/Client/src/main/kotlin/org/rs09/client/rendering/opengl/enums/GLBeginMode.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.rs09.client.rendering.opengl.enums - -/** - * Source: https://opensource.apple.com/source/X11server/X11server-85/libGL/AppleSGLX-57/specs/enum.spec - */ -object GLBeginMode { - const val POINTS = 0x0000 - const val LINES = 0x0001 - const val LINE_LOOP = 0x0002 - const val LINE_STRIP = 0x0003 - const val TRIANGLES = 0x0004 - const val TRIANGLE_STRIP = 0x0005 - const val TRIANGLE_FAN = 0x0006 - const val QUADS = 0x0007 - const val QUAD_STRIP = 0x0008 - const val POLYGON = 0x0009 -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/util/ArrayUtils.kt b/Client/src/main/kotlin/org/rs09/client/util/ArrayUtils.kt deleted file mode 100644 index 9645aa1e6..000000000 --- a/Client/src/main/kotlin/org/rs09/client/util/ArrayUtils.kt +++ /dev/null @@ -1,338 +0,0 @@ -package org.rs09.client.util - -@Suppress("NAME_SHADOWING") -object ArrayUtils { - @JvmStatic - fun arraycopy(source: LongArray, sourcePos: Int, dest: LongArray, destPos: Int, count: Int) { - var sourcePos = sourcePos - var destPos = destPos - var count = count - if (source === dest) { - if (sourcePos == destPos) { - return - } - if (destPos > sourcePos && destPos < sourcePos + count) { - --count - sourcePos += count - destPos += count - count = sourcePos - count - count += 3 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - } - count -= 3 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - } - return - } - } - count += sourcePos - count -= 3 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - } - count += 3 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - } - } - - @JvmStatic - fun arraycopy(source: ByteArray, sourcePos: Int, dest: ByteArray, destPos: Int, count: Int) { - var sourcePos = sourcePos - var destPos = destPos - var count = count - if (source === dest) { - if (sourcePos == destPos) { - return - } - if (destPos > sourcePos && destPos < sourcePos + count) { - --count - sourcePos += count - destPos += count - count = sourcePos - count - count += 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - } - count -= 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - } - return - } - } - count += sourcePos - count -= 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - } - count += 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - } - } - - @JvmStatic - fun arraycopy(source: IntArray, sourcePos: Int, dest: IntArray, destPos: Int, count: Int) { - var sourcePos = sourcePos - var destPos = destPos - var count = count - if (source === dest) { - if (sourcePos == destPos) { - return - } - if (destPos > sourcePos && destPos < sourcePos + count) { - --count - sourcePos += count - destPos += count - count = sourcePos - count - count += 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - } - count -= 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - } - return - } - } - count += sourcePos - count -= 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - } - count += 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - } - } - - @JvmStatic - fun arraycopy(source: FloatArray, sourcePos: Int, dest: FloatArray, destPos: Int, count: Int) { - var sourcePos = sourcePos - var destPos = destPos - var count = count - if (source === dest) { - if (sourcePos == destPos) { - return - } - if (destPos > sourcePos && destPos < sourcePos + count) { - --count - sourcePos += count - destPos += count - count = sourcePos - count - count += 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - } - count -= 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - } - return - } - } - count += sourcePos - count -= 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - } - count += 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - } - } - - @JvmStatic - fun arraycopy(source: ShortArray, sourcePos: Int, dest: ShortArray, destPos: Int, count: Int) { - var sourcePos = sourcePos - var destPos = destPos - var count = count - if (source === dest) { - if (sourcePos == destPos) { - return - } - if (destPos > sourcePos && destPos < sourcePos + count) { - --count - sourcePos += count - destPos += count - count = sourcePos - count - count += 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - } - count -= 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - } - return - } - } - count += sourcePos - count -= 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - } - count += 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - } - } - - @JvmStatic - fun arraycopy(source: Array, sourcePos: Int, dest: Array, destPos: Int, count: Int) { - var sourcePos = sourcePos - var destPos = destPos - var count = count - if (source === dest) { - if (sourcePos == destPos) { - return - } - if (destPos > sourcePos && destPos < sourcePos + count) { - --count - sourcePos += count - destPos += count - count = sourcePos - count - count += 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - dest[destPos--] = source[sourcePos--] - } - count -= 7 - while (sourcePos >= count) { - dest[destPos--] = source[sourcePos--] - } - return - } - } - count += sourcePos - count -= 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - dest[destPos++] = source[sourcePos++] - } - count += 7 - while (sourcePos < count) { - dest[destPos++] = source[sourcePos++] - } - } - - @JvmStatic - fun fill(arr: IntArray, offset: Int, length: Int, value: Int) { - var offset = offset - var length = length - length = offset + length - 7 - while (offset < length) { - arr[offset++] = value - arr[offset++] = value - arr[offset++] = value - arr[offset++] = value - arr[offset++] = value - arr[offset++] = value - arr[offset++] = value - arr[offset++] = value - } - length += 7 - while (offset < length) { - arr[offset++] = value - } - } - - @JvmStatic - fun zero(arr: IntArray, offset: Int, length: Int) { - var offset = offset - var length = length - length = offset + length - 7 - while (offset < length) { - arr[offset++] = 0 - arr[offset++] = 0 - arr[offset++] = 0 - arr[offset++] = 0 - arr[offset++] = 0 - arr[offset++] = 0 - arr[offset++] = 0 - arr[offset++] = 0 - } - length += 7 - while (offset < length) { - arr[offset++] = 0 - } - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/util/ByteArrayPool.kt b/Client/src/main/kotlin/org/rs09/client/util/ByteArrayPool.kt deleted file mode 100644 index 9a3b34d39..000000000 --- a/Client/src/main/kotlin/org/rs09/client/util/ByteArrayPool.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.rs09.client.util - -// TODO This is literally so useless because arrays are never returned to the pool... -object ByteArrayPool { - private val SMALL_BUFFERS = arrayOfNulls(1000) - private val MEDIUM_BUFFERS = arrayOfNulls(250) - private val LARGE_BUFFERS = arrayOfNulls(50) - private var SMALL_INDEX = 0 - private var MEDIUM_INDEX = 0 - private var LARGE_INDEX = 0 - - @Synchronized - fun getByteArray(size: Int): ByteArray = when { - size == 100 && SMALL_INDEX > 0 -> { - val arr = SMALL_BUFFERS[--SMALL_INDEX]!! - SMALL_BUFFERS[SMALL_INDEX] = null - arr - } - size == 5000 && MEDIUM_INDEX > 0 -> { - val arr = MEDIUM_BUFFERS[--MEDIUM_INDEX]!! - MEDIUM_BUFFERS[MEDIUM_INDEX] = null - arr - } - size == 30000 && LARGE_INDEX > 0 -> { - val arr = LARGE_BUFFERS[--LARGE_INDEX]!! - LARGE_BUFFERS[LARGE_INDEX] = null - arr - } - else -> ByteArray(size) - - } -} \ No newline at end of file diff --git a/Client/src/main/kotlin/org/rs09/client/util/CRC.kt b/Client/src/main/kotlin/org/rs09/client/util/CRC.kt deleted file mode 100644 index bedc816d6..000000000 --- a/Client/src/main/kotlin/org/rs09/client/util/CRC.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.rs09.client.util - -object CRC { - private val CRC32 = IntArray(256) - private val CRC64 = LongArray(256) - - init { - // Generate CRC tables - for (i in 0 until 256) { - var var0 = i - for (var2 in 0 until 8) { - var0 = if (1 == 1 and var0) { - var0 ushr 1 xor -306674912 - } else { - var0 ushr 1 - } - } - CRC32[i] = var0 - } - - } - - fun crc32(data: ByteArray, length: Int): Int = crc32(data, 0, length) - - fun crc32(data: ByteArray, offset: Int, end: Int): Int { - var hash = -1 - var i = offset - while (end > i) { - hash = hash ushr 8 xor CRC32[hash xor data[i].toInt() and 0xff] - ++i - } - hash = hash.inv() - return hash - } -} \ No newline at end of file diff --git a/Client/src/main/resources/09logo.png b/Client/src/main/resources/09logo.png deleted file mode 100644 index c512e2a4a..000000000 Binary files a/Client/src/main/resources/09logo.png and /dev/null differ diff --git a/Client/src/main/resources/client.conf b/Client/src/main/resources/client.conf deleted file mode 100644 index 4f636b9cb..000000000 --- a/Client/src/main/resources/client.conf +++ /dev/null @@ -1 +0,0 @@ -target_ip_addr:127.0.0.1 diff --git a/Client/src/main/resources/fixXinitThreads.cpp b/Client/src/main/resources/fixXinitThreads.cpp deleted file mode 100644 index 276e8793a..000000000 --- a/Client/src/main/resources/fixXinitThreads.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include -#include -class a{public: a() { XInitThreads(); }};a X; diff --git a/Client/src/main/resources/lib/jogl_32.dll b/Client/src/main/resources/lib/jogl_32.dll deleted file mode 100644 index ee7a6a590..000000000 Binary files a/Client/src/main/resources/lib/jogl_32.dll and /dev/null differ diff --git a/Client/src/main/resources/lib/jogl_64.dll b/Client/src/main/resources/lib/jogl_64.dll deleted file mode 100644 index 990986f19..000000000 Binary files a/Client/src/main/resources/lib/jogl_64.dll and /dev/null differ diff --git a/Client/src/main/resources/lib/jogl_awt_32.dll b/Client/src/main/resources/lib/jogl_awt_32.dll deleted file mode 100644 index 2b47eee6e..000000000 Binary files a/Client/src/main/resources/lib/jogl_awt_32.dll and /dev/null differ diff --git a/Client/src/main/resources/lib/jogl_awt_64.dll b/Client/src/main/resources/lib/jogl_awt_64.dll deleted file mode 100644 index d247b4a0b..000000000 Binary files a/Client/src/main/resources/lib/jogl_awt_64.dll and /dev/null differ diff --git a/Client/src/main/resources/lib/libgluegen-rt_32.so b/Client/src/main/resources/lib/libgluegen-rt_32.so deleted file mode 100644 index 148243046..000000000 Binary files a/Client/src/main/resources/lib/libgluegen-rt_32.so and /dev/null differ diff --git a/Client/src/main/resources/lib/libgluegen-rt_64.so b/Client/src/main/resources/lib/libgluegen-rt_64.so deleted file mode 100644 index e12c51113..000000000 Binary files a/Client/src/main/resources/lib/libgluegen-rt_64.so and /dev/null differ diff --git a/Client/src/main/resources/lib/libjogl.jnilib b/Client/src/main/resources/lib/libjogl.jnilib deleted file mode 100644 index eb8719aa9..000000000 Binary files a/Client/src/main/resources/lib/libjogl.jnilib and /dev/null differ diff --git a/Client/src/main/resources/lib/libjogl_32.so b/Client/src/main/resources/lib/libjogl_32.so deleted file mode 100644 index 51adbdb46..000000000 Binary files a/Client/src/main/resources/lib/libjogl_32.so and /dev/null differ diff --git a/Client/src/main/resources/lib/libjogl_64.so b/Client/src/main/resources/lib/libjogl_64.so deleted file mode 100644 index 45b0d63e9..000000000 Binary files a/Client/src/main/resources/lib/libjogl_64.so and /dev/null differ diff --git a/Client/src/main/resources/lib/libjogl_awt.jnilib b/Client/src/main/resources/lib/libjogl_awt.jnilib deleted file mode 100644 index 2f16fbfb6..000000000 Binary files a/Client/src/main/resources/lib/libjogl_awt.jnilib and /dev/null differ diff --git a/Client/src/main/resources/lib/libjogl_awt_32.so b/Client/src/main/resources/lib/libjogl_awt_32.so deleted file mode 100644 index ad1bc1408..000000000 Binary files a/Client/src/main/resources/lib/libjogl_awt_32.so and /dev/null differ diff --git a/Client/src/main/resources/lib/libjogl_awt_64.so b/Client/src/main/resources/lib/libjogl_awt_64.so deleted file mode 100644 index ad1bc1408..000000000 Binary files a/Client/src/main/resources/lib/libjogl_awt_64.so and /dev/null differ diff --git a/Client/src/main/resources/libfixXInitThreads.so b/Client/src/main/resources/libfixXInitThreads.so deleted file mode 100755 index cbd7ad0d3..000000000 Binary files a/Client/src/main/resources/libfixXInitThreads.so and /dev/null differ 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/Management-Server/build.gradle b/Management-Server/build.gradle deleted file mode 100644 index 9ce79718b..000000000 --- a/Management-Server/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -apply plugin: 'application' - -archivesBaseName = 'managementserver' - -mainClassName = 'ms.Management' - -repositories { - mavenCentral() -} - -dependencies { - implementation "com.github.ajalt.mordant:mordant:2.0.0-alpha2" - implementation "org.jetbrains:markdown-jvm:0.2.0" - implementation 'mysql:mysql-connector-java:8.0.21' -} - - -jar { - manifest { - attributes 'Main-Class': 'ms.Management' - } - from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } } -} \ No newline at end of file diff --git a/Management-Server/managementprops/properties.json b/Management-Server/managementprops/properties.json deleted file mode 100644 index e081156a1..000000000 --- a/Management-Server/managementprops/properties.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "DatabaseInformation": { - "database_name": "global", - "database_username": "root", - "database_password": "", - "database_host": "127.0.0.1", - "database_port": "3306" - }, - - "WorldTechnicalInformation": { - "world_limit": "10", - "worldhop_delay": "20000" - }, - "secret_key": "2009scape_development" -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/Management.java b/Management-Server/src/main/java/ms/Management.java deleted file mode 100644 index 5d44a4e37..000000000 --- a/Management-Server/src/main/java/ms/Management.java +++ /dev/null @@ -1,197 +0,0 @@ -package ms; - -import ms.net.NioReactor; -import ms.net.packet.IoBuffer; -import ms.net.packet.PacketHeader; -import ms.net.packet.WorldPacketRepository; -import ms.system.ShutdownSequence; -import ms.system.mysql.SQLManager; -import ms.system.util.Command; -import ms.system.util.ManagementConfigParser; -import ms.world.GameServer; -import ms.world.PlayerSession; -import ms.world.WorldDatabase; - -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.nio.charset.StandardCharsets; -import java.util.Scanner; - -/** - * The main class. - * @author Emperor - * - */ -public final class Management { - - /** - * If the shutdown hook is active. - */ - public static boolean active = true; - - /** - * The commands. - */ - private static final Command[] COMMANDS = { - new Command("-commands", "Print a list of all commands.") { - @Override - public void run(String...args) { - for (Command c : COMMANDS) { - System.out.println("Command " + c.getName() + ": " + c.getInfo()); - } - } - }, - new Command("-s", "Safely shuts down the server.") { - @Override - public void run(String...args) { - System.out.println("Shutting down Management server..."); - ShutdownSequence.shutdown(); - } - }, - new Command("-debug", "Debug world info.") { - @Override - public void run(String...args) { - System.out.println("---------------------------------------------"); - for (GameServer server : WorldDatabase.getWorlds()) { - if (server != null) { - System.out.println("World [id=" + server.getInfo().getWorldId() + ", IP=" + server.getInfo().getAddress() + ", country=" + server.getInfo().getCountry() + ", members=" + server.getInfo().isMembers() + ", players=" + server.getPlayers().size() + ", active=" + server.isActive() + "]."); - } - } - } - }, - new Command("-pinfo", "Debugs player information (usage: -pinfo emperor).") { - @Override - public void run(String...args) { - String name = args[1]; - PlayerSession player = WorldDatabase.getPlayer(name); - if (player == null) { - System.out.println("Player " + name + " was not registered!"); - return; - } - System.out.println("Player [name=" + name + ", world=" + player.getWorldId() + ", active=" + player.isActive() + "]."); - } - }, - new Command("-update", "Calls an update on all the game servers (-update -1 to cancel).") { - @Override - public void run(String...args) { - int ticks = Integer.parseInt(args[1]); - for (GameServer server : WorldDatabase.getWorlds()) { - if (server != null && server.isActive()) { - WorldPacketRepository.sendUpdate(server, ticks); - } - } - } - }, - new Command("-reloadconfig", "Reloads the configurations of all worlds.") { - - @Override - public void run(String... args) { - for (GameServer server : WorldDatabase.getWorlds()) { - if(server == null) { - continue; - } - WorldPacketRepository.sendConfigReload(server); - } - } - }, - - new Command("-rlcache", "Reloads launcher/client resource cache") { - @Override - public void run(String... args) { - System.out.println("Reloaded resource cache!"); - } - }, - - new Command("-kick", "Kicks a player from the MS (not ingame).") { - @Override - public void run(String... args) { - String name = args[1]; - PlayerSession player = WorldDatabase.getPlayer(name); - if (player == null) { - System.out.println("Player " + name + " was not registered!"); - return; - } - player.getWorld().getPlayers().remove(name); - player.setWorldId(0); - System.out.println("Kicked player " + name + "!"); - } - }, - - new Command("-say", "Send a message to all worlds") { - @Override - public void run(String... args) { - String message = String.join(" ", args); - message = message.substring(4); - for(GameServer server : WorldDatabase.getWorlds()){ - if(server == null) continue; - String finalMessage = message; - server.getPlayers().forEach((String uname, PlayerSession p) -> { - IoBuffer buffer = new IoBuffer(5, PacketHeader.BYTE); - buffer.putString(p.getUsername()); - buffer.putString("Server"); - buffer.put(2); - buffer.put(2); - buffer.putString(finalMessage); - p.getWorld().getSession().write(buffer); - }); - } - } - } - }; - - /** - * The main method. - * @param args The arguments cast on runtime. - * @throws Throwable When an exception occurs. - */ - public static void main(String...args) throws Throwable { - if (!isLocallyHosted(ServerConstants.HOST_ADDRESS)) { - System.err.println("WARNING: Configure host address in server constants!"); - } - System.out.println("-------- 530 Management server --------"); - System.out.println("Starting up..."); - new ManagementConfigParser("managementprops" + File.separator + "properties.json"); - SQLManager.init(); - //NioReactor.configure(ServerConstants.PORT).start(); - NioReactor.configure(5555).start(); - Runtime.getRuntime().addShutdownHook(new ShutdownSequence()); - System.out.println("Status: ready."); - System.out.println("Use -commands for a list of commands!"); - Scanner s = new Scanner(new InputStreamReader(System.in, StandardCharsets.UTF_8)); - while (s.hasNext()) { - try { - String command = s.nextLine(); - if (!command.startsWith("-")) { - continue; - } - String[] arguments = command.split(" "); - command = arguments[0]; - for (Command c : COMMANDS) { - if (c.getName().equals(command)) { - System.out.println("Handling command \"" + command + "\"!"); - c.run(arguments); - } - } - } catch (Throwable t) { - t.printStackTrace(); - } - } - s.close(); - } - - /** - * Checks if the Management server is locally hosted. - * @return {@code True} if so. - * @throws IOException When an I/O exception occurs. - */ - private static boolean isLocallyHosted(String ip) throws IOException { - InetAddress address = InetAddress.getByName(ip); - if (address.isAnyLocalAddress() || address.isLoopbackAddress()) { - return true; - } - return NetworkInterface.getByInetAddress(address) != null; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/ServerConstants.java b/Management-Server/src/main/java/ms/ServerConstants.java deleted file mode 100644 index 60d4af36d..000000000 --- a/Management-Server/src/main/java/ms/ServerConstants.java +++ /dev/null @@ -1,52 +0,0 @@ -package ms; - -import ms.system.OperatingSystem; - -/** - * Holds constants for the management server. - * @author v4rg - * - */ -public final class ServerConstants { - - /** - * The maximum amount of worlds. - */ - public static final int WORLD_LIMIT = 10; - - /** - * The world switching delay in milliseconds. - */ - public static final long WORLD_SWITCH_DELAY = 20_000L; - - /** - * The address of the Management server. - */ - public static final String HOST_ADDRESS = "127.0.0.1"; - - /** - * The operating system of the management server - */ - public static final OperatingSystem OS = System.getProperty("os.name").toUpperCase().contains("WIN") ? OperatingSystem.WINDOWS : OperatingSystem.UNIX; - - /** - * Stops from instantiating. - */ - private ServerConstants() { - /* - * empty. - */ - } - - /** - * Fixes a path to a specified operating system - * @param operatingSystem The os type. - * @param path The path. - * @return The fixed path. - */ - public static String fixPath(OperatingSystem operatingSystem, String path) { - if (operatingSystem == null) - operatingSystem = OS; - return operatingSystem == OperatingSystem.WINDOWS ? path.replace("/","\\") : path.replace("\\","/"); - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/EventProducer.java b/Management-Server/src/main/java/ms/net/EventProducer.java deleted file mode 100644 index 9f3093c1c..000000000 --- a/Management-Server/src/main/java/ms/net/EventProducer.java +++ /dev/null @@ -1,28 +0,0 @@ -package ms.net; - -import java.nio.ByteBuffer; - -/** - * Used for producing I/O events. - * @author Emperor - * - */ -public interface EventProducer { - - /** - * Produces a new read event. - * @param session The session. - * @param buffer The buffer to read. - * @return The read event handler. - */ - IoReadEvent produceReader(IoSession session, ByteBuffer buffer); - - /** - * Produces a new writing event. - * @param session The session. - * @param context The context. - * @return The write event handler. - */ - IoWriteEvent produceWriter(IoSession session, Object context); - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/IoEventHandler.java b/Management-Server/src/main/java/ms/net/IoEventHandler.java deleted file mode 100644 index 1971fffa7..000000000 --- a/Management-Server/src/main/java/ms/net/IoEventHandler.java +++ /dev/null @@ -1,119 +0,0 @@ -package ms.net; - -import ms.world.WorldDatabase; -import ms09.MSLogger; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.ClosedChannelException; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.Arrays; -import java.util.concurrent.ExecutorService; - -/** - * I/O event handling. - * @author Emperor - * - */ -public final class IoEventHandler { - - /** - * The executor service. - */ - private final ExecutorService service; - - /** - * Constructs a new {@code IoEventHandler}. - * @param service The executor service used for handling events. - */ - public IoEventHandler(ExecutorService service) { - this.service = service; - } - - /** - * Called when making a new connection. - * @param key The selection key. - */ - public void connect(SelectionKey key) { - /* - * empty. - */ - } - - /** - * Used for accepting a new connection. - * @param key The selection key. - * @param selector The selector. - * @throws IOException When an I/O exception occurs. - */ - public void accept(SelectionKey key, Selector selector) throws IOException { - SocketChannel sc = ((ServerSocketChannel) key.channel()).accept(); - sc.configureBlocking(false); - sc.socket().setTcpNoDelay(true); - sc.register(selector, SelectionKey.OP_READ); - } - - /** - * Reads the incoming packet data. - * @param key The selection key. - * @throws IOException When an I/O exception occurs. - */ - public void read(SelectionKey key) throws IOException { - ReadableByteChannel channel = (ReadableByteChannel) key.channel(); - ByteBuffer buffer = ByteBuffer.allocate(100_000); - IoSession session = (IoSession) key.attachment(); - if (channel.read(buffer) == -1) { - if(session == null){ - key.cancel(); - return; - } - if(session.getGameServer() != null){ - WorldDatabase.unRegister(session.getGameServer()); - } - session.disconnect(); - key.cancel(); - return; - } - buffer.flip(); - if (session == null) { - key.attach(session = new IoSession(key, service)); - } - service.execute(session.getProducer().produceReader(session, buffer)); - } - - /** - * Writes the outgoing packet data. - * @param key The selection key. - */ - public void write(SelectionKey key) { - IoSession session = (IoSession) key.attachment(); - if(session != null) { - key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); - session.write(); - } - } - - /** - * Disconnects a connection. - * @param key The selection key. - * @param t The occurred exception (if any). - */ - public void disconnect(SelectionKey key, Throwable t) { - try { - IoSession session = (IoSession) key.attachment(); - String cause = "" + t; - if (t != null && !(t instanceof ClosedChannelException || cause.contains("De externe host") || cause.contains("De software op uw") || cause.contains("An established connection was aborted") || cause.contains("An existing connection") || cause.contains("AsynchronousClose"))) { - t.printStackTrace(); - } - if (session != null) { - session.disconnect(); - } - } catch (Throwable e) { - e.printStackTrace(); - } - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/IoReadEvent.java b/Management-Server/src/main/java/ms/net/IoReadEvent.java deleted file mode 100644 index 74de5d5bd..000000000 --- a/Management-Server/src/main/java/ms/net/IoReadEvent.java +++ /dev/null @@ -1,73 +0,0 @@ -package ms.net; - -import java.nio.ByteBuffer; - -/** - * Handles a reading event. - * @author Emperor - * - */ -public abstract class IoReadEvent implements Runnable { - - /** - * The I/O session. - */ - private final IoSession session; - - /** - * The buffer. - */ - private ByteBuffer buffer; - - /** - * If the queued reading buffer was used (debugging purposes). - */ - protected boolean usedQueuedBuffer; - - /** - * Constructs a new {@code IoReadEvent}. - * @param session The session. - * @param buffer The buffer to read from. - */ - public IoReadEvent(IoSession session, ByteBuffer buffer) { - this.session = session; - this.buffer = buffer; - } - - @Override - public void run() { - try { - if (session.getReadingQueue() != null) { - buffer = session.getReadingQueue().put(buffer); - buffer.flip(); - session.setReadingQueue(null); - usedQueuedBuffer = true; - } - read(session, buffer); - } catch (Throwable t) { - t.printStackTrace(); - session.disconnect(); - } - } - - /** - * Queues the buffer until more data has been received. - * @param data The data that has been read already. - */ - public void queueBuffer(int...data) { - ByteBuffer queue = ByteBuffer.allocate(data.length + buffer.remaining() + 100_000); - for (int value : data) { - queue.put((byte) value); - } - queue.put(buffer); - session.setReadingQueue(queue); - } - - /** - * Reads the data from the buffer. - * @param session The session. - * @param buffer The buffer to read from. - */ - public abstract void read(IoSession session, ByteBuffer buffer); - -} diff --git a/Management-Server/src/main/java/ms/net/IoSession.java b/Management-Server/src/main/java/ms/net/IoSession.java deleted file mode 100644 index c14a4a641..000000000 --- a/Management-Server/src/main/java/ms/net/IoSession.java +++ /dev/null @@ -1,354 +0,0 @@ -package ms.net; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import ms.ServerConstants; -import ms.net.producer.HSEventProducer; -import ms.world.GameServer; - -/** - * Represents a connected I/O session. - * @author Emperor - * - */ -public class IoSession { - - /** - * The handshake event producer. - */ - private static final EventProducer HANDSHAKE_PRODUCER = new HSEventProducer(); - - /** - * The selection key. - */ - private final SelectionKey key; - - /** - * The executor service. - */ - private final ExecutorService service; - - /** - * The event producer. - */ - private EventProducer producer = HANDSHAKE_PRODUCER; - - /** - * The currently queued writing data. - */ - private List writingQueue = new ArrayList<>(20); - - /** - * The currently queued reading data. - */ - private ByteBuffer readingQueue; - - /** - * The writing lock. - */ - private final Lock writingLock = new ReentrantLock(); - - /** - * The name hash. - */ - private int nameHash; - - /** - * The server key. - */ - private long serverKey; - - /** - * The JS-5 encryption value. - */ - private int js5Encryption; - - /** - * If the session is active. - */ - private boolean active = true; - - /** - * The last ping time stamp. - */ - private long lastPing; - - /** - * The address. - */ - private final String address; - - /** - * The game server object for this session. - */ - private GameServer gameServer; - - /** - * Constructs a new {@code IoSession}. - * @param key The selection key. - * @param service The executor service. - */ - public IoSession(SelectionKey key, ExecutorService service) { - this.key = key; - this.service = service; - String address = getRemoteAddress().replaceAll("/", "").split(":")[0]; - if (address.equals("127.0.0.1")) { - address = ServerConstants.HOST_ADDRESS; - } - this.address = address; - } - - /** - * Fires a write event created using the current event producer. - * @param context The event context. - */ - public void write(Object context) { - write(context, false); - } - - /** - * Fires a write event created using the current event producer. - * @param context The event context. - * @param instant If the event should be instantly executed on this thread. - */ - public void write(Object context, boolean instant) { - if (context == null) { - throw new IllegalStateException("Invalid writing context!"); - } - if (instant) { - producer.produceWriter(this, context).run(); - return; - } - service.execute(producer.produceWriter(this, context)); - } - - /** - * Sends the packet data (without write event encoding). - * @param buffer The buffer. - */ - public void queue(ByteBuffer buffer) { - try { - if(writingLock.tryLock(1000L, TimeUnit.MILLISECONDS)){ - writingQueue.add(buffer); - writingLock.unlock(); - write(); - } - } catch (Exception e){ - e.printStackTrace(); - writingLock.unlock(); - } - } - - /** - * Handles the writing of all buffers in the queue. - */ - public void write() { - - if (!key.isValid()) { - disconnect(); - return; - } - writingLock.lock(); - SocketChannel channel = (SocketChannel) key.channel(); - try { - while (!writingQueue.isEmpty()) { - ByteBuffer buffer = writingQueue.get(0); - channel.write(buffer); - if (buffer.hasRemaining()) { - key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); - break; - } - writingQueue.remove(0); - } - } catch (IOException e) { - e.printStackTrace(); - disconnect(); - } finally { - writingLock.unlock(); - } - } - - /** - * Disconnects the session. - */ - public void disconnect() { - try { - if (!active) { - return; - } - active = false; - key.cancel(); - SocketChannel channel = (SocketChannel) key.channel(); - channel.socket().close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Gets the IP-address (without the port). - * @return The address. - */ - public String getAddress() { - return address; - } - - /** - * Gets the remote address of this session. - * @return The remote address, as a String. - */ - public String getRemoteAddress() { - try { - return ((SocketChannel) key.channel()).getRemoteAddress().toString(); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - /** - * Gets the current event producer. - * @return The producer. - */ - public EventProducer getProducer() { - return producer; - } - - /** - * Sets the event producer. - * @param producer The producer to set. - */ - public void setProducer(EventProducer producer) { - this.producer = producer; - } - - /** - * Gets the queued reading data. - * @return The readingQueue. - */ - public ByteBuffer getReadingQueue() { - synchronized (this) { - return readingQueue; - } - } - - /** - * Queues reading data. - * @param readingQueue The readingQueue to set. - */ - public void setReadingQueue(ByteBuffer readingQueue) { - synchronized (this) { - this.readingQueue = readingQueue; - } - } - - /** - * Gets the writing lock. - * @return The writing lock. - */ - public Lock getWritingLock() { - return writingLock; - } - - /** - * Gets the selection key. - * @return The selection key. - */ - public SelectionKey getKey() { - return key; - } - - /** - * @return The active. - */ - public boolean isActive() { - return active; - } - - /** - * @return The js5Encryption. - */ - public int getJs5Encryption() { - return js5Encryption; - } - - /** - * @param js5Encryption The js5Encryption to set. - */ - public void setJs5Encryption(int js5Encryption) { - this.js5Encryption = js5Encryption; - } - - /** - * Gets the lastPing. - * @return The lastPing. - */ - public long getLastPing() { - return lastPing; - } - - /** - * Sets the lastPing. - * @param lastPing The lastPing to set. - */ - public void setLastPing(long lastPing) { - this.lastPing = lastPing; - } - - /** - * Gets the nameHash. - * @return The nameHash. - */ - public int getNameHash() { - return nameHash; - } - - /** - * Sets the nameHash. - * @param nameHash The nameHash to set. - */ - public void setNameHash(int nameHash) { - this.nameHash = nameHash; - } - - /** - * Gets the serverKey. - * @return The serverKey. - */ - public long getServerKey() { - return serverKey; - } - - /** - * Sets the serverKey. - * @param serverKey The serverKey to set. - */ - public void setServerKey(long serverKey) { - this.serverKey = serverKey; - } - - /** - * Gets the gameServer value. - * @return The gameServer. - */ - public GameServer getGameServer() { - return gameServer; - } - - /** - * Sets the gameServer value. - * @param gameServer The gameServer to set. - */ - public void setGameServer(GameServer gameServer) { - this.gameServer = gameServer; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/IoWriteEvent.java b/Management-Server/src/main/java/ms/net/IoWriteEvent.java deleted file mode 100644 index 5ed73d653..000000000 --- a/Management-Server/src/main/java/ms/net/IoWriteEvent.java +++ /dev/null @@ -1,50 +0,0 @@ -package ms.net; - -import java.nio.channels.CancelledKeyException; - -/** - * Handles a writing event. - * @author Emperor - */ -public abstract class IoWriteEvent implements Runnable { - - /** - * The I/O session. - */ - private final IoSession session; - - /** - * The buffer. - */ - private final Object context; - - /** - * Constructs a new {@code IoWriteEvent}. - * @param session The session. - * @param context The write event context. - */ - public IoWriteEvent(IoSession session, Object context) { - this.session = session; - this.context = context; - } - - @Override - public void run() { - try { - write(session, context); - } catch (Throwable t) { - if (!(t instanceof CancelledKeyException)) { - t.printStackTrace(); - } - session.disconnect(); - } - } - - /** - * Writes the data. - * @param session The session. - * @param context The write event context. - */ - public abstract void write(IoSession session, Object context); - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/NioReactor.java b/Management-Server/src/main/java/ms/net/NioReactor.java deleted file mode 100644 index 21a17ec65..000000000 --- a/Management-Server/src/main/java/ms/net/NioReactor.java +++ /dev/null @@ -1,134 +0,0 @@ -package ms.net; - -import ms.Management; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.util.Iterator; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Handles (NIO-based) networking events using the reactor pattern. - * @author Emperor - */ -public final class NioReactor implements Runnable { - - /** - * The executor service. - */ - private final ExecutorService service; - - /** - * The selector - */ - private Selector selector; - - /** - * The socket channel. - */ - private ServerSocketChannel channel; - - /** - * The I/O event handling instance. - */ - private final IoEventHandler eventHandler; - - /** - * If the reactor is running. - */ - private boolean running; - - /** - * Constructs a new {@code NioReactor}. - * @param poolSize The pool size. - */ - private NioReactor(int poolSize) { - this.service = Executors.newSingleThreadScheduledExecutor(); - this.eventHandler = new IoEventHandler(Executors.newFixedThreadPool(poolSize)); - } - - /** - * Creates and configures a new {@code NioReactor} with a pool size of 1. - * @param port The port. - * @return The {@code NioReactor} {@code Object}. - * @throws IOException When an I/O exception occurs. - */ - public static NioReactor configure(int port) throws IOException { - return configure(port, 1); - } - - /** - * Creates and configures a new {@code NioReactor}. - * @param port The port. - * @param poolSize The amount of threads in the thread pool. - * @return The {@code NioReactor} {@code Object}. - * @throws IOException When an I/O exception occurs. - */ - public static NioReactor configure(int port, int poolSize) throws IOException { - NioReactor reactor = new NioReactor(poolSize); - reactor.channel = ServerSocketChannel.open(); - reactor.selector = Selector.open(); - reactor.channel.bind(new InetSocketAddress(port)); - reactor.channel.configureBlocking(false); - reactor.channel.register(reactor.selector, SelectionKey.OP_ACCEPT); - return reactor; - } - - /** - * Starts the reactor. - */ - public void start() { - running = true; - service.execute(this); - } - - @Override - public void run() { - while (running && Management.active) { - try { - if(selector.select() > 0){ - Iterator keys = selector.selectedKeys().iterator(); - while (keys.hasNext()) { - SelectionKey key = keys.next(); - keys.remove(); - try { - if(key == null) continue; - if (!key.isValid() || !key.channel().isOpen()) { - key.cancel(); - continue; - } - if (key.isAcceptable()) { - eventHandler.accept(key, selector); - } - if (key.isReadable()) { - eventHandler.read(key); - } - else if (key.isWritable()) { - eventHandler.write(key); - } - } catch (Throwable t) { - eventHandler.disconnect(key, t); - } - } - } else { - System.out.println("SLEEPING"); - Thread.sleep(200); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - /** - * Terminates the reactor (once it's done processing current I/O events). - */ - public void terminate() { - running = false; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/event/HSReadEvent.java b/Management-Server/src/main/java/ms/net/event/HSReadEvent.java deleted file mode 100644 index 3c4df5229..000000000 --- a/Management-Server/src/main/java/ms/net/event/HSReadEvent.java +++ /dev/null @@ -1,83 +0,0 @@ -package ms.net.event; - -import java.nio.ByteBuffer; -import java.util.Locale; - -import ms.Management; -import ms.net.IoReadEvent; -import ms.net.IoSession; -import ms.net.packet.IoBuffer; -import ms.system.util.ManagementConstants; -import ms.world.PlayerSession; -import ms.world.WorldDatabase; -import ms.system.util.ByteBufferUtils; - -/** - * Handles handshake read events. - * @author Emperor - */ -public final class HSReadEvent extends IoReadEvent { - - /** - * Constructs a new {@code HSReadEvent}. - * @param session The session. - * @param buffer The buffer. - */ - public HSReadEvent(IoSession session, ByteBuffer buffer) { - super(session, buffer); - } - - @Override - public void read(IoSession session, ByteBuffer buffer) { - int opcode = buffer.get() & 0xFF; - switch (opcode) { - case 88: - String password = ByteBufferUtils.getString(buffer); - if (!password.equals(ManagementConstants.getSECRET_KEY())) { - System.out.println("Password mismatch (attempt=" + password + ")!"); - session.disconnect(); - break; - } - session.write(opcode); - break; - case 255: // World list - int updateStamp = buffer.getInt(); - WorldDatabase.sendUpdate(session, updateStamp); - break; - case 35: - IoBuffer buf = new IoBuffer(); - String username = ByteBufferUtils.getString(buffer).toLowerCase(); - password = ByteBufferUtils.getString(buffer); - if (!password.equals(ManagementConstants.getSECRET_KEY())){ - System.out.println("Password mismatch (attempt=" + password + ")!"); - buf.put(1); - ByteBuffer buff = buf.toByteBuffer(); - buff.flip(); - session.queue(buff); - return; - } - - PlayerSession player = WorldDatabase.getPlayer(username); - if (player == null) { - System.out.println("Player " + username + " was not registered!"); - buf.put(2); - ByteBuffer buff = buf.toByteBuffer(); - buff.flip(); - session.queue(buff); - return; - } - buf.put(0); - ByteBuffer buff = buf.toByteBuffer(); - buff.flip(); - session.queue(buff); - player.getWorld().getPlayers().remove(username); - player.setWorldId(0); - break; - default: - System.err.println("Unhandled handshake opcode: " + opcode + "."); - session.disconnect(); - break; - } - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/event/HSWriteEvent.java b/Management-Server/src/main/java/ms/net/event/HSWriteEvent.java deleted file mode 100644 index 2376b2a90..000000000 --- a/Management-Server/src/main/java/ms/net/event/HSWriteEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -package ms.net.event; - -import java.nio.ByteBuffer; - -import ms.net.IoSession; -import ms.net.IoWriteEvent; -import ms.net.producer.RegistryEventProducer; - -/** - * Handles Handshake write events. - * @author Emperor - */ -public final class HSWriteEvent extends IoWriteEvent { - - /** - * The login event producer. - */ - private static final RegistryEventProducer REGISTRY_PRODUCER = new RegistryEventProducer(); - - /** - * Constructs a new {@code HSWriteEvent} {@code Object}. - * @param session The session. - * @param context The context. - */ - public HSWriteEvent(IoSession session, Object context) { - super(session, context); - } - - @Override - public void write(IoSession session, Object context) { - ByteBuffer buffer = ByteBuffer.allocate(9); - buffer.put((byte) 14); - session.setProducer( REGISTRY_PRODUCER); - buffer.flip(); - session.queue(buffer); - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/event/PacketReadEvent.java b/Management-Server/src/main/java/ms/net/event/PacketReadEvent.java deleted file mode 100644 index 227751b2f..000000000 --- a/Management-Server/src/main/java/ms/net/event/PacketReadEvent.java +++ /dev/null @@ -1,101 +0,0 @@ -package ms.net.event; - -import java.nio.ByteBuffer; - -import ms.net.IoReadEvent; -import ms.net.IoSession; -import ms.net.packet.WorldPacketRepository; - -/** - * Handles world packet reading events. - * @author Emperor - * - */ -public final class PacketReadEvent extends IoReadEvent { - - /** - * The packet sizes. - */ - private static final int[] PACKET_SIZE = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 - }; - - /** - * Constructs a new {@code PacketReadEvent} {@code Object}. - * @param session The I/O session. - * @param buffer The buffer to read from. - */ - public PacketReadEvent(IoSession session, ByteBuffer buffer) { - super(session, buffer); - } - - @Override - public void read(IoSession session, ByteBuffer buffer) { - int last = -1; - while (buffer.hasRemaining()) { - int opcode = buffer.get() & 0xFF; - if (opcode >= PACKET_SIZE.length) { - break; - } - int header = PACKET_SIZE[opcode]; - int size = header; - if (header < 0) { - size = getPacketSize(buffer, opcode, header, last); - } - if (size == -1) { - break; - } - if (buffer.remaining() < size) { - switch (header) { - case -2: - queueBuffer(opcode, size >> 8, size); - break; - case -1: - queueBuffer(opcode, size); - break; - default: - queueBuffer(opcode); - break; - } - break; - } - byte[] data = new byte[size]; - buffer.get(data); - last = opcode; - try { - WorldPacketRepository.handleIncoming(session, opcode, ByteBuffer.wrap(data)); - } catch (Throwable t) { - t.printStackTrace(); - } - } - } - - /** - * Gets the packet size for the given opcode. - * @param buffer The buffer. - * @param opcode The opcode. - * @param header The packet header. - * @param last The last opcode. - * @return The packet size. - */ - private int getPacketSize(ByteBuffer buffer, int opcode, int header, int last) { - if (header == -1) { - if (buffer.remaining() < 1) { - queueBuffer(opcode); - return -1; - } - return buffer.get() & 0xFF; - } - if (header == -2) { - if (buffer.remaining() < 2) { - queueBuffer(opcode); - return -1; - } - return buffer.getShort() & 0xFFFF; - } - System.err.println("Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "]!"); - return -1; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/event/PacketWriteEvent.java b/Management-Server/src/main/java/ms/net/event/PacketWriteEvent.java deleted file mode 100644 index 3ffdeb32f..000000000 --- a/Management-Server/src/main/java/ms/net/event/PacketWriteEvent.java +++ /dev/null @@ -1,46 +0,0 @@ -package ms.net.event; - -import java.nio.ByteBuffer; - -import ms.net.IoSession; -import ms.net.IoWriteEvent; -import ms.net.packet.IoBuffer; - -/** - * Handles world packet writing events. - * @author Emperor - * - */ -public final class PacketWriteEvent extends IoWriteEvent { - - - /** - * Constructs a new {@code PacketWriteEvent} {@code Object}. - * @param session The I/O session. - * @param context The packet context. - */ - public PacketWriteEvent(IoSession session, Object context) { - super(session, context); - } - - @Override - public void write(IoSession session, Object context) { - IoBuffer b = (IoBuffer) context; - int size = b.toByteBuffer().position(); - ByteBuffer buffer = ByteBuffer.allocate(1 + size + b.getHeader().ordinal()); - buffer.put((byte) b.opcode()); - switch (b.getHeader()) { - case NORMAL: - break; - case BYTE: - buffer.put((byte) size); - break; - case SHORT: - buffer.putShort((short) size); - break; - } - buffer.put((ByteBuffer) b.toByteBuffer().flip()); - session.queue((ByteBuffer) buffer.flip()); - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/event/RegistryReadEvent.java b/Management-Server/src/main/java/ms/net/event/RegistryReadEvent.java deleted file mode 100644 index f906ab213..000000000 --- a/Management-Server/src/main/java/ms/net/event/RegistryReadEvent.java +++ /dev/null @@ -1,74 +0,0 @@ -package ms.net.event; - -import java.nio.ByteBuffer; - -import ms.ServerConstants; -import ms.net.IoReadEvent; -import ms.net.IoSession; -import ms.world.WorldDatabase; -import ms.system.util.ByteBufferUtils; -import ms.world.info.CountryFlag; -import ms.world.info.WorldInfo; - -/** - * Handles world registry read events. - * @author Emperor - * - */ -public final class RegistryReadEvent extends IoReadEvent { - - /** - * The string check. - */ - private static final String CHECK = "12x4578f5g45hrdjiofed59898"; - //kratos = 666x14x88x28shhhwpwwb&h - //12x4578f5g45hrdjiofed59898 - - /** - * Constructs a new {@code RegistryReadEvent} {@code Object}. - * @param session The session. - * @param buffer The buffer to read. - */ - public RegistryReadEvent(IoSession session, ByteBuffer buffer) { - super(session, buffer); - } - - @Override - public void read(IoSession session, ByteBuffer buffer) { - int worldId = buffer.get() & 0xFF; - if (buffer.remaining() < 2) { - queueBuffer(worldId); - return; - } - int revision = buffer.getInt(); - int country = buffer.get() & 0xFF; - boolean members = buffer.get() == 1; - boolean pvp = buffer.get() == 1; - boolean quickChat = buffer.get() == 1; - boolean lootshare = buffer.get() == 1; - String activity = ByteBufferUtils.getString(buffer); - for (int i = 0; i < CHECK.length(); i++) { - if ((char) buffer.get() != CHECK.charAt(i)) { - session.write(3); - return; - } - } - if (worldId >= ServerConstants.WORLD_LIMIT) { - session.write(0); - return; - } - if (WorldDatabase.isActive(worldId)) { - session.write(2); - return; - } - try { - WorldInfo info = new WorldInfo(worldId, session.getAddress(), revision, CountryFlag.values()[country], activity, members, pvp, quickChat, lootshare); - WorldDatabase.register(info).configure(session); - session.write(1); - } catch (Throwable t) { - t.printStackTrace(); - session.write(3); - } - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/event/RegistryWriteEvent.java b/Management-Server/src/main/java/ms/net/event/RegistryWriteEvent.java deleted file mode 100644 index 0e75aea91..000000000 --- a/Management-Server/src/main/java/ms/net/event/RegistryWriteEvent.java +++ /dev/null @@ -1,43 +0,0 @@ -package ms.net.event; - -import java.nio.ByteBuffer; - -import ms.net.EventProducer; -import ms.net.IoSession; -import ms.net.IoWriteEvent; -import ms.net.producer.PacketEventProducer; - -/** - * Handles game world registry writing events. - * @author Emperor - * - */ -public final class RegistryWriteEvent extends IoWriteEvent { - - /** - * The event producer. - */ - public static final EventProducer PRODUCER = new PacketEventProducer(); - - /** - * Constructs a new {@code RegistryWriteEvent} {@code Object}. - * @param session The I/O session. - * @param context The writing context. - */ - public RegistryWriteEvent(IoSession session, Object context) { - super(session, context); - } - - @Override - public void write(IoSession session, Object context) { - ByteBuffer buffer = ByteBuffer.allocate(1); - int opcode = (int) context; - buffer.put((byte) opcode); - buffer.flip(); - if (opcode == 1) { - session.setProducer(PRODUCER); - } - session.queue(buffer); - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/packet/IoBuffer.java b/Management-Server/src/main/java/ms/net/packet/IoBuffer.java deleted file mode 100644 index c35fb5ab6..000000000 --- a/Management-Server/src/main/java/ms/net/packet/IoBuffer.java +++ /dev/null @@ -1,666 +0,0 @@ -package ms.net.packet; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; - -import ms.system.util.ByteBufferUtils; - -/** - * Represents the buffer used for reading/writing packets. - * @author Emperor - * - */ -public class IoBuffer { - - /** - * The bit masks. - */ - private static final int[] BIT_MASK = new int[32]; - - /** - * The packet size. - */ - private int packetSize; - - /** - * The opcode. - */ - private final int opcode; - - /** - * The packet header. - */ - private final PacketHeader header; - - /** - * The byte buffer. - */ - private ByteBuffer buf; - - /** - * The bit position. - */ - private int bitPosition = 0; - - /** - * Constructs a new {@code IoBuffer} {@code Object}. - */ - public IoBuffer() { - this(-1, PacketHeader.NORMAL, ByteBuffer.allocate(2048)); - } - - /** - * Constructs a new {@code IoBuffer} {@code Object}. - * @param opcode The opcode. - */ - public IoBuffer(int opcode) { - this(opcode, PacketHeader.NORMAL, ByteBuffer.allocate(2048)); - } - - /** - * Constructs a new {@code IoBuffer} {@code Object}. - * @param opcode The opcode. - * @param header The packet header. - */ - public IoBuffer(int opcode, PacketHeader header) { - this(opcode, header, ByteBuffer.allocate((1 << 16) + 1)); - } - - /** - * Constructs a new {@code IoBuffer} {@code Object}. - * @param opcode The opcode. - * @param header The packet header. - * @param buf The byte buffer. - */ - public IoBuffer(int opcode, PacketHeader header, ByteBuffer buf) { - this.opcode = opcode; - this.header = header; - this.buf = buf; - } - - static { - for (int i = 0; i < 32; i++) { - BIT_MASK[i] = (1 << i) - 1; - } - } - - /** - * - * @return - */ - public IoBuffer clear() { - buf.clear(); - bitPosition = 0; - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer put(int val) { - buf.put((byte) val); - return this; - } - - /** - * - * @param datas - * @param offset - * @param len - * @return - */ - public IoBuffer putBytes(byte[] datas, int offset, int len) { - for (int i = offset; i < len; i++) { - put(datas[i]); - } - return this; - } - - public final void getBytes(byte data[], int off, int len) { - for (int k = off; k < len + off; k++) { - data[k] = data[off++]; - } - } - - /** - * - * @param val - * @return - */ - public IoBuffer putA(int val) { - buf.put((byte) (val + 128)); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putC(int val) { - buf.put((byte) -val); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putS(int val) { - buf.put((byte) (128 - val)); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putTri(int val) { - buf.put((byte) (val >> 16)); - buf.put((byte) (val >> 8)); - buf.put((byte) val); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putShort(int val) { - buf.putShort((short) val); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putLEShort(int val) { - buf.put((byte) val); - buf.put((byte) (val >> 8)); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putShortA(int val) { - buf.put((byte) (val >> 8)); - buf.put((byte) (val + 128)); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putLEShortA(int val) { - buf.put((byte) (val + 128)); - buf.put((byte) (val >> 8)); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putInt(int val) { - buf.putInt(val); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putLEInt(int val) { - buf.put((byte) val); - buf.put((byte) (val >> 8)); - buf.put((byte) (val >> 16)); - buf.put((byte) (val >> 24)); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putIntA(int val) { - buf.put((byte) (val >> 8)); - buf.put((byte) val); - buf.put((byte) (val >> 24)); - buf.put((byte) (val >> 16)); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putIntB(int val) { - buf.put((byte) (val >> 16)); - buf.put((byte) (val >> 24)); - buf.put((byte) val); - buf.put((byte) (val >> 8)); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putLong(long val) { - buf.putLong(val); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putSmart(int val) { - if (val > Byte.MAX_VALUE) { - buf.putShort((short) (val + 32768)); - } else { - buf.put((byte) val); - } - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putIntSmart(int val) { - if (val > Short.MAX_VALUE) { - buf.putInt(val + 32768); - } else { - buf.putShort((short) val); - } - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putString(String val) { - buf.put(val.getBytes(StandardCharsets.UTF_8)); - buf.put((byte) 0); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putJagString(String val) { - buf.put((byte) 0); - buf.put(val.getBytes(StandardCharsets.UTF_8)); - buf.put((byte) 0); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer putJagString2(String val) { - byte[] packed = new byte[256]; - int length = ByteBufferUtils.packGJString2(0, packed, val); - buf.put((byte) 0).put(packed, 0, length).put((byte) 0); - return this; - } - - /** - * - * @param val - * @return - */ - public IoBuffer put(byte[] val) { - buf.put(val); - return this; - } - - /** - * Puts a byte array as byte A in reverse. - * @param data The data to put. - * @param start The start index. - * @param offset The offset. - */ - public void putReverseA(byte[] data, int start, int offset) { - for (int i = offset + start; i >= start; i--) { - putA(data[i]); - } - } - - /** - * - * @param numBits - * @param value - * @return - */ - public IoBuffer putBits(int numBits, int value) { - int bytePos = getBitPosition() >> 3; - int bitOffset = 8 - (getBitPosition() & 7); - bitPosition += numBits; - for (; numBits > bitOffset; bitOffset = 8) { - byte b = buf.get(bytePos); - buf.put(bytePos, b &= ~BIT_MASK[bitOffset]); - buf.put(bytePos++, b |= value >> numBits - bitOffset & BIT_MASK[bitOffset]); - numBits -= bitOffset; - } - byte b = buf.get(bytePos); - if (numBits == bitOffset) { - buf.put(bytePos, b &= ~BIT_MASK[bitOffset]); - buf.put(bytePos, b |= value & BIT_MASK[bitOffset]); - } else { - buf.put(bytePos, b &= ~(BIT_MASK[numBits] << bitOffset - numBits)); - buf.put(bytePos, b |= (value & BIT_MASK[numBits]) << bitOffset - numBits); - } - return this; - } - - /** - * - * @param buffer - * @return - */ - public IoBuffer put(IoBuffer buffer) { - buffer.toByteBuffer().flip(); - buf.put(buffer.toByteBuffer()); - return this; - } - - /** - * - * @param buffer - * @return - */ - public IoBuffer putA(IoBuffer buffer) { - buffer.toByteBuffer().flip(); - while (buffer.toByteBuffer().hasRemaining()) { - putA(buffer.toByteBuffer().get()); - } - return this; - } - - /** - * - * @param buffer - * @return - */ - public IoBuffer put(ByteBuffer buffer) { - buf.put(buffer); - return this; - } - - /** - * - * @return - */ - public IoBuffer setBitAccess() { - bitPosition = buf.position() * 8; - return this; - } - - /** - * - * @return - */ - public IoBuffer setByteAccess() { - buf.position((getBitPosition() + 7) / 8); - return this; - } - - /** - * - * @return - */ - public int get() { - return buf.get(); - } - - /** - * - * @return - */ - public int getA() { - return (buf.get() & 0xFF) - 128; - } - - /** - * - * @return - */ - public int getC() { - return -buf.get(); - } - - /** - * - * @return - */ - public int getS() { - return 128 - buf.get(); - } - - /** - * - * @return - */ - public int getTri() { - return ((buf.get() << 16) & 0xFF) | ((buf.get() << 8) & 0xFF) | (buf.get() & 0xFF); - } - - /** - * - * @return - */ - public int getShort() { - return buf.getShort(); - } - - /** - * - * @return - */ - public int getLEShort() { - return (buf.get() & 0xFF) | ((buf.get() & 0xFF) << 8); - } - - /** - * - * @return - */ - public int getShortA() { - return ((buf.get() & 0xFF) << 8) | (buf.get() - 128 & 0xFF); - } - - /** - * - * @return - */ - public int getLEShortA() { - return (buf.get() - 128 & 0xFF) | ((buf.get() & 0xFF) << 8); - } - - /** - * - * @return - */ - public int getInt() { - return buf.getInt(); - } - - /** - * - * @return - */ - public int getLEInt() { - return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8) - + ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24); - } - - /** - * - * @return - */ - public int getIntA() { - return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF)+ ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16); - } - - /** - * - * @return - */ - public int getIntB() { - return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24) - + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8); - } - - /** - * - * @return - */ - public long getLongL() { - long first = getIntB(); - long second = getIntB(); - if (second < 0) - second = second & 0xffffffffL; - return (first << 32) + second; - } - - /** - * - * @return - */ - public long getLong() { - return buf.getLong(); - } - - /** - * - * @return - */ - public int getIntSmart() { - int peek = buf.getShort(buf.position()); - if (peek <= Short.MAX_VALUE) { - return buf.getShort() & 0xFFFF; - } - return buf.getInt() - 32768; - } - - /** - * - * @return - */ - public String getString() { - return ByteBufferUtils.getString(buf); - } - - /** - * - * @return - */ - public String getJagString() { - buf.get(); - return ByteBufferUtils.getString(buf); - } - - /** - * - * @param is - * @param offset - * @param length - * @return - */ - public IoBuffer getReverseA(byte[] is, int offset, int length) { - for (int i = (offset + length - 1); i >= offset; i--) { - is[i] = (byte) (buf.get() - 128); - } - return this; - } - - /** - * - * @return - */ - public ByteBuffer toByteBuffer() { - return buf; - } - - /** - * - * @return - */ - public int opcode() { - return opcode; - } - - /** - * - * @return - */ - public int readableBytes() { - return buf.capacity() - buf.remaining(); - } - - /** - * - * @return - */ - public PacketHeader getHeader() { - return header; - } - - /** - * - * @return - */ - public byte[] array() { - return buf.array(); - } - - /** - * @return the packetSize. - */ - public int getPacketSize() { - return packetSize; - } - - /** - * @param packetSize the packetSize to set. - */ - public void setPacketSize(int packetSize) { - this.packetSize = packetSize; - } - - /** - * Gets the bitPosition. - * @return The bitPosition. - */ - public int getBitPosition() { - return bitPosition; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/packet/PacketHeader.kt b/Management-Server/src/main/java/ms/net/packet/PacketHeader.kt deleted file mode 100644 index ef293f9dc..000000000 --- a/Management-Server/src/main/java/ms/net/packet/PacketHeader.kt +++ /dev/null @@ -1,23 +0,0 @@ -package ms.net.packet - -/** - * Represents the types of packet headers. - * @author Emperor - */ -enum class PacketHeader { - - /** - * The normal packet header. - */ - NORMAL, - - /** - * The byte packet header. - */ - BYTE, - - /** - * The short packet header. - */ - SHORT -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/packet/WorldPacketRepository.kt b/Management-Server/src/main/java/ms/net/packet/WorldPacketRepository.kt deleted file mode 100644 index b72f731e1..000000000 --- a/Management-Server/src/main/java/ms/net/packet/WorldPacketRepository.kt +++ /dev/null @@ -1,555 +0,0 @@ -package ms.net.packet - -import ms.net.IoSession -import ms.system.PunishmentStorage -import ms.system.communication.ClanRank -import ms.system.communication.ClanRepository -import ms.system.communication.CommunicationInfo -import ms.system.util.ManagementConstants.WORLD_HOP_DELAY -import ms.world.GameServer -import ms.world.PlayerSession -import ms.world.WorldDatabase -import ms.world.info.Response -import ms.world.info.UIDInfo -import java.nio.ByteBuffer - -/** - * Repository class for world packets. - * @author Emperor - */ -object WorldPacketRepository { - /** - * Sends the player registry response. - * @param server The game server. - * @param player The player session. - * @param response The registry response. - */ - @JvmStatic - fun sendRegistryResponse(server: GameServer, player: PlayerSession, response: Response) { - val buffer = IoBuffer(0, PacketHeader.BYTE) - buffer.putString(player.username) - buffer.put(response.opcode())//byte - if (response == Response.MOVING_WORLD) { - val delay: Long = WORLD_HOP_DELAY - (System.currentTimeMillis() - player.disconnectionTime) - buffer.put((delay / 1000).toInt()) - } - server.session.write(buffer) - } - - /** - * Sends a message to the player. - * @param player The player. - * @param message The message to send. - */ - @JvmStatic - fun sendPlayerMessage(player: PlayerSession?, message: String) { - if (player == null) { - return - } - val buffer = IoBuffer(2, PacketHeader.BYTE) - buffer.putString(player.username) - buffer.putString(message) - player.world.session.write(buffer) - } - - fun sendPlayerMessage(player: PlayerSession?, messages: Array) { - if (player == null) { - return - } - for (message in messages) sendPlayerMessage(player, message) - } - - /** - * Sends the contact information. - * @param player The player. - */ - fun sendContactInformation(player: PlayerSession) { - val info = player.communication - val buffer = IoBuffer(3, PacketHeader.SHORT) - buffer.putString(player.username) - buffer.put(info.contacts.size) - for (contact in info.contacts.keys) { - buffer.putString(contact) - buffer.put(info.getRank(contact).ordinal) - buffer.put(CommunicationInfo.getWorldId(player, contact)) - } - buffer.put(info.blocked.size) - for (contact in info.blocked) { - buffer.putString(contact) - } - if (info.currentClan == null) { - buffer.put(0) - } else { - buffer.put(1) - buffer.putString(info.currentClan) - } - player.world.session.write(buffer) - } - - /** - * Sends a contact update. - * @param player The player who's contacts we're changing. - * @param contact The contact to update. - * @param block If we're updating the blocked list. - * @param remove If the contact should be removed. - * @param worldId The world id of the contact. - * @param rank The clan rank. - */ - @JvmStatic - fun sendContactUpdate( - player: PlayerSession, - contact: String, - block: Boolean, - remove: Boolean, - worldId: Int, - rank: ClanRank? - ) { - val buffer = IoBuffer(4, PacketHeader.BYTE) - buffer.putString(player.username) - buffer.putString(contact) - buffer.put((if (block) 1 else 0)) - if (rank != null) { - buffer.put((2 + rank.ordinal)) - } else { - buffer.put((if (remove) 1 else 0)) - if (!block && !remove) { - buffer.put(worldId) - } - } - player.world.session.write(buffer) - } - - /** - * Sends a clan message. - * @param player The player to send the message to. - * @param p The player sending the message. - * @param message The message to send. - * @param type The message type. - */ - @JvmStatic - fun sendMessage(player: PlayerSession, p: PlayerSession, type: Int, message: String) { - val buffer = IoBuffer(5, PacketHeader.BYTE) - buffer.putString(player.username) - buffer.putString(p.username) - buffer.put(type) - buffer.put(p.chatIcon) - buffer.putString(message) - println("Send Message: ${player.username} -> ${p.username} T: $type ICO: ${p.chatIcon} $message") - player.world.session.write(buffer) - } - - /** - * Sends clan information to the server. - * @param server The server. - * @param clan The clan. - */ - @JvmStatic - fun sendClanInformation(server: GameServer, clan: ClanRepository) { - val buffer = IoBuffer(6, PacketHeader.SHORT) - buffer.putString(clan.owner.username) - buffer.putString(clan.name) - var length = clan.players.size - if (length > ClanRepository.MAX_MEMBERS) { - length = ClanRepository.MAX_MEMBERS - } - buffer.put(length) - for (i in 0 until length) { - val player = clan.players[i] - buffer.putString(player.username) - buffer.put(player.worldId) - buffer.put(clan.getRank(player).ordinal) - } - buffer.put(clan.joinRequirement.ordinal) - buffer.put(clan.kickRequirement.ordinal) - buffer.put(clan.messageRequirement.ordinal) - buffer.put(clan.lootRequirement.ordinal) - server.session.write(buffer) - } - - /** - * Sends the leave clan packet. - * @param player The player leaving the clan. - */ - @JvmStatic - fun sendLeaveClan(player: PlayerSession) { - val buffer = IoBuffer(7, PacketHeader.BYTE) - buffer.putString(player.username) - player.world.session.write(buffer) - } - - /** - * Sends the player login notification. - * @param server The server. - * @param player The player logging in. - * @param names The names. - */ - @JvmStatic - fun notifyPlayers(server: GameServer, player: PlayerSession, names: List) { - val buffer = IoBuffer(8, PacketHeader.SHORT) - buffer.putString(player.username) - buffer.put(player.worldId) - buffer.put(names.size) - for (name in names) { - buffer.putString(name) - } - server.session.write(buffer) - } - - /** - * Notifies the game server a player logged out. - * @param server The game server to notify. - * @param player The player logging out. - */ - @JvmStatic - fun notifyLogout(server: GameServer, player: PlayerSession) { - val buffer = IoBuffer(9, PacketHeader.BYTE) - buffer.putString(player.username) - server.session.write(buffer) - } - - /** - * Sends the update countdown to the server. - * @param server The server. - * @param ticks The amount of ticks left. - */ - @JvmStatic - fun sendUpdate(server: GameServer, ticks: Int) { - val buffer = IoBuffer(10) - buffer.putInt(ticks) - server.session.write(buffer) - } - - /** - * Sends the punishment update packet. - * @param world The world to send the packet to. - * @param key The punishment key. - * @param type The punishment type. - * @param duration The duration of the punishment. - */ - @JvmStatic - fun sendPunishUpdate(world: GameServer, key: String, type: Int, duration: Long) { - val buffer = IoBuffer(11, PacketHeader.BYTE) - buffer.putString(key) - buffer.put(type) - buffer.putLong(duration) - world.session.write(buffer) - } - - /** - * Sends a configuration reload. - * @param world the world. - */ - @JvmStatic - fun sendConfigReload(world: GameServer) { - world.session.write(IoBuffer(15, PacketHeader.BYTE)) - } - - /** - * Handles incoming world packets. - * @param session The I/O session. - * @param opcode The opcode. - * @param b The buffer to read from. - */ - @JvmStatic - fun handleIncoming(session: IoSession, opcode: Int, b: ByteBuffer) { - val buffer = IoBuffer(opcode, PacketHeader.NORMAL, b) - val server = session.gameServer - when (opcode) { - 0 -> handlePlayerRegistration(server, buffer) - 1 -> handlePlayerRemoval(server, buffer) - 2 -> handlePunishment(server, buffer) - 3 -> handleCommunicationRequest(server, buffer) - 4, 5 -> handleContactUpdate(server, buffer, opcode == 5) - 6 -> handleJoinClan(server, buffer) - 7 -> handleClanRename(server, buffer) - 8 -> handleClanSetting(server, buffer) - 9 -> handleClanKick(server, buffer) - 10 -> handleClanMessage(server, buffer) - 11 -> handlePrivateMessage(server, buffer) - 12 -> handleClanInfoRequest(server, buffer) - 13 -> handleChatSetting(server, buffer) - 14 -> handleInfoUpdate(server, buffer) - else -> System.err.println("Handling incoming packet [opcode=" + opcode + ", size=" + b.limit() + "].") - } - } - - /** - * Handles the info of a player update. - * @param server the server. - * @param buffer the buffer. - */ - private fun handleInfoUpdate(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val player = server.players[username] - if (player != null) { - player.chatIcon = buffer.get() - } - } - - /** - * Handles a player registration. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handlePlayerRegistration(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val password = buffer.string - val ipAddress = buffer.string - val macAddress = buffer.string - val compName = buffer.string - val serial = buffer.string - val rights = buffer.int - val chatIcon = buffer.get() - val uid = UIDInfo(ipAddress, compName, macAddress, serial) - val player = PlayerSession(username, password, UIDInfo(ipAddress, compName, macAddress, serial)) - if (WorldDatabase.isActivePlayer(username)) { - sendRegistryResponse(server, player, Response.ALREADY_ONLINE) - return - } - player.uid = uid - player.rights = rights - player.chatIcon = chatIcon - if (PunishmentStorage.isSystemBanned(uid)) { - sendRegistryResponse(server, player, Response.BANNED) - return - } - player.parse() - if (player.isBanned) { - sendRegistryResponse(server, player, Response.ACCOUNT_DISABLED) - return - } - if (player.lastWorld != server.info.worldId && player.hasMovedWorld()) { - sendRegistryResponse(server, player, Response.MOVING_WORLD) - return - } - server.register(player) - } - - /** - * Handles the removal of a player. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handlePlayerRemoval(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val session = server.players[username] - if (session != null) { - session.isActive = false - val player = server.players.remove(username) - if (player != null) { - session.remove() - } - session.worldId = 0 - } - } - - /** - * Handles a player registration. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handlePunishment(server: GameServer, buffer: IoBuffer) { - val type = buffer.get() and 0xFF - val target = buffer.string - val duration = buffer.long - val staff = buffer.string - PunishmentStorage.handlePunishment(staff, target, type, duration) - } - - /** - * Handles the communication info request packet. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handleCommunicationRequest(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val player = server.players[username] ?: return - sendContactInformation(player) - } - - /** - * Handles a contact update packet. - * @param server The server. - * @param buffer The buffer to read from. - * @param block If the list is for blocked players. - */ - private fun handleContactUpdate(server: GameServer, buffer: IoBuffer, block: Boolean) { - val username = buffer.string - val player = server.players[username] ?: return - val contact = buffer.string - when (buffer.get()) { - 0 -> { - if (block) { - player.communication.block(contact) - } - player.communication.add(contact) - } - 1 -> player.communication.remove(contact, block) - 2 -> player.communication.updateClanRank(contact, ClanRank.values()[buffer.get()]) - } - } - - /** - * Handles a clan related packet. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handleJoinClan(server: GameServer, buffer: IoBuffer) { - val name = buffer.string - val clanName = buffer.string - val player = server.players[name] - if (player == null || !player.isActive) { - System.err.println("Invalid player specified in clan packet!") - return - } - if (player.clan != null) { - player.clan.leave(player, true) - return - } - if (clanName.length < 1) { - sendLeaveClan(player) - return - } - val clan = ClanRepository.get(server, clanName) - if (clan == null) { - sendPlayerMessage( - player, - arrayOf( - "The channel you tried to join does not exist.", - "Try joining the main clan named '2009Scape'.:clan:" - ) - ) - return - } - clan.enter(player) - } - - /** - * Handles renaming a clan. - * @param server The server. - * @param buffer The buffer. - */ - private fun handleClanRename(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val name = buffer.string - val player = server.players[username] - if (player == null || !player.isActive) { - return - } - val clan = ClanRepository.getClans()[username] - player.communication.clanName = name - if (clan != null) { - if (name.isEmpty()) { - clan.clean(true) - } else { - clan.rename(name) - } - } - } - - /** - * Handles changing clan settings packet. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handleClanSetting(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val type = buffer.get() - val rank = if (type < 4) ClanRank.values()[buffer.get() and 0xFF] else null - val player = server.players[username] - if (player == null || !player.isActive) { - return - } - val clan = ClanRepository.get(server, username) - when (type) { - 0 -> { - player.communication.joinRequirement = rank - clan?.clean(false) - } - 1 -> player.communication.messageRequirement = rank - 2 -> { - player.communication.kickRequirement = rank - clan?.update() - } - 3 -> player.communication.lootRequirement = rank - } - } - - /** - * Handles kicking a player from the clan. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handleClanKick(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val playerName = buffer.string - val player = server.players[username] - if (player == null || !player.isActive || player.clan == null) { - return - } - val target = WorldDatabase.getPlayer(playerName) - if (target == null || !target.isActive) { - return - } - player.clan.kick(player, target) - } - - /** - * Handles a clan message. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handleClanMessage(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val message = buffer.string - val player = server.players[username] - if (player == null || !player.isActive || player.clan == null) { - return - } - player.clan.message(player, message) - } - - /** - * Handles a clan message. - * @param server The game server. - * @param buffer The buffer. - */ - private fun handlePrivateMessage(server: GameServer, buffer: IoBuffer) { - val username = buffer.string - val receiver = buffer.string - val message = buffer.string - val player = server.players[username] - if (player == null || !player.isActive) { - return - } - player.communication.sendMessage(receiver, message) - } - - /** - * Handles a clan information request packet. - * @param server The server. - * @param buffer The buffer. - */ - private fun handleClanInfoRequest(server: GameServer, buffer: IoBuffer) { - val name = buffer.string - val clan = ClanRepository.get(server, name) ?: return - sendClanInformation(server, clan) - } - - /** - * Handles a chat setting update packet. - * @param server The server. - * @param buffer The buffer. - */ - private fun handleChatSetting(server: GameServer, buffer: IoBuffer) { - val name = buffer.string - val publicSetting = buffer.get() - val privateSetting = buffer.get() - val tradeSetting = buffer.get() - val player = server.players[name] - if (player == null || !player.isActive) { - return - } - player.communication.updateSettings(publicSetting, privateSetting, tradeSetting) - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/producer/HSEventProducer.java b/Management-Server/src/main/java/ms/net/producer/HSEventProducer.java deleted file mode 100644 index 40936a72d..000000000 --- a/Management-Server/src/main/java/ms/net/producer/HSEventProducer.java +++ /dev/null @@ -1,29 +0,0 @@ -package ms.net.producer; - -import java.nio.ByteBuffer; - -import ms.net.EventProducer; -import ms.net.IoReadEvent; -import ms.net.IoSession; -import ms.net.IoWriteEvent; -import ms.net.event.HSReadEvent; -import ms.net.event.HSWriteEvent; - -/** - * Produces I/O events for the handshake protocol. - * @author Emperor - * - */ -public final class HSEventProducer implements EventProducer { - - @Override - public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) { - return new HSReadEvent(session, buffer); - } - - @Override - public IoWriteEvent produceWriter(IoSession session, Object context) { - return new HSWriteEvent(session, context); - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/producer/PacketEventProducer.java b/Management-Server/src/main/java/ms/net/producer/PacketEventProducer.java deleted file mode 100644 index 091fcf5d7..000000000 --- a/Management-Server/src/main/java/ms/net/producer/PacketEventProducer.java +++ /dev/null @@ -1,29 +0,0 @@ -package ms.net.producer; - -import java.nio.ByteBuffer; - -import ms.net.EventProducer; -import ms.net.IoReadEvent; -import ms.net.IoSession; -import ms.net.IoWriteEvent; -import ms.net.event.PacketReadEvent; -import ms.net.event.PacketWriteEvent; - -/** - * The packet event producer. - * @author Emperor - * - */ -public final class PacketEventProducer implements EventProducer { - - @Override - public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) { - return new PacketReadEvent(session, buffer); - } - - @Override - public IoWriteEvent produceWriter(IoSession session, Object context) { - return new PacketWriteEvent(session, context); - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/net/producer/RegistryEventProducer.java b/Management-Server/src/main/java/ms/net/producer/RegistryEventProducer.java deleted file mode 100644 index 25db4d265..000000000 --- a/Management-Server/src/main/java/ms/net/producer/RegistryEventProducer.java +++ /dev/null @@ -1,29 +0,0 @@ -package ms.net.producer; - -import java.nio.ByteBuffer; - -import ms.net.EventProducer; -import ms.net.IoReadEvent; -import ms.net.IoSession; -import ms.net.IoWriteEvent; -import ms.net.event.RegistryReadEvent; -import ms.net.event.RegistryWriteEvent; - -/** - * Handles world server registry. - * @author Emperor - * - */ -public final class RegistryEventProducer implements EventProducer { - - @Override - public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) { - return new RegistryReadEvent(session, buffer); - } - - @Override - public IoWriteEvent produceWriter(IoSession session, Object context) { - return new RegistryWriteEvent(session, context); - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/OperatingSystem.kt b/Management-Server/src/main/java/ms/system/OperatingSystem.kt deleted file mode 100644 index 267cc9cc5..000000000 --- a/Management-Server/src/main/java/ms/system/OperatingSystem.kt +++ /dev/null @@ -1,9 +0,0 @@ -package ms.system - -/** - * The operating systems - * @author Clayton Williams - */ -enum class OperatingSystem { - UNIX, WINDOWS -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/PunishmentStorage.java b/Management-Server/src/main/java/ms/system/PunishmentStorage.java deleted file mode 100644 index 3fd0b00a5..000000000 --- a/Management-Server/src/main/java/ms/system/PunishmentStorage.java +++ /dev/null @@ -1,267 +0,0 @@ -package ms.system; - -import ms.net.packet.WorldPacketRepository; -import ms.system.mysql.SQLManager; -import ms.world.GameServer; -import ms.world.PlayerSession; -import ms.world.WorldDatabase; -import ms.world.info.UIDInfo; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * Used for storing and handling punishment data. - * @author Emperor - * @author Vexia - * - */ -public final class PunishmentStorage { - - /** - * The type ids for IP, MAC and SERIAL bans. - */ - public static final int IP = 2, MAC = 3, SERIAL = 4; - - /** - * Constructs a new {@code PunishmentStorage} {@code Object}. - */ - public PunishmentStorage() { - /* - * empty. - */ - } - - /** - * Handles a punishment. - * @param staff The name of the staff member dealing the punishment. - * @param target The target. - * @param type The punishment type. - * @param duration The duration of the punishment (in milliseconds). - */ - public static void handlePunishment(String name, String target, int type, long duration) { - PlayerSession staff = WorldDatabase.getPlayer(name); - PlayerSession player = WorldDatabase.getPlayer(target, true); - if (player == null) { - WorldPacketRepository.sendPlayerMessage(staff, "Player " + target + " is invalid!"); - return; - } - long end = Long.MAX_VALUE; - if (duration != -1L && duration != 0L) { - end = System.currentTimeMillis() + duration; - } else if (duration == 0L) { - end = 0L; - } - String key = "null"; - switch (type) { - case 0: //Mute - case 1: //Ban - if (type == 1 && end == 0L) { - unban(player.getIpAddress()); - unban(player.getMacAddress()); - unban(player.getSerialKey()); - } - if (player.isActive()) { - WorldPacketRepository.sendPunishUpdate(player.getWorld(), player.getUsername(), type, end); - } - notify(staff, type == 0 ? "mute" : "ban", target, end); - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return; - } - PreparedStatement statement; - try { - statement = connection.prepareStatement("UPDATE members SET " + (type == 0 ? "muteTime" : "banTime") + "='" + end + "' WHERE username ='" + target + "'"); - statement.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - SQLManager.close(connection); - return; - } finally { - SQLManager.close(connection); - } - return; - case 2: - ban(player.getIpAddress(), type); - notify(staff, "IP-ban", target, end); - notifyServers(key = player.getIpAddress(), type, end); - break; - case 3: - ban(player.getMacAddress(), type); - notify(staff, "MAC-ban", target, end); - notifyServers(key = player.getMacAddress(), type, end); - break; - case 4: - ban(player.getSerialKey(), type); - notify(staff, "UID-ban", target, end); - notifyServers(key = player.getSerialKey(), type, end); - break; - case 5: - ban(player.getIpAddress(), 2); - notifyServers(key = player.getIpAddress(), 2, end); - ban(player.getMacAddress(), 3); - notifyServers(key = player.getMacAddress(), 3, end); - ban(player.getSerialKey(), 4); - notifyServers(key = player.getSerialKey(), 4, end); - notify(staff, "full ban", target, end); - return; - case 6: //Kick - if (player.isActive()) { - WorldPacketRepository.sendPunishUpdate(player.getWorld(), player.getUsername(), 6, end); - WorldPacketRepository.sendPlayerMessage(staff, "Successfully kicked player " + target + " from world " + player.getWorldId() + "."); - } else { - WorldPacketRepository.sendPlayerMessage(staff, "Player " + target + " was already inactive."); - } - break; - case 7: //Request info - WorldPacketRepository.sendPlayerMessage(staff, "[----------Player info----------]"); - WorldPacketRepository.sendPlayerMessage(staff, "Name: " + player.getUsername()); - WorldPacketRepository.sendPlayerMessage(staff, "IP address: " + player.getIpAddress()); - WorldPacketRepository.sendPlayerMessage(staff, "MAC address: " + player.getMacAddress()); - WorldPacketRepository.sendPlayerMessage(staff, "Serial key: " + player.getSerialKey()); - WorldPacketRepository.sendPlayerMessage(staff, "Computer name: " + player.getComputerName()); - WorldPacketRepository.sendPlayerMessage(staff, "[-------------------------------]"); - return; - } - notifyServers(key, type, end); - } - - /** - * Bans an address. - * @param address the address. - * @return {@code True} if banned. - */ - public static boolean ban(String address, int type) { - if (address == null || address.length() == 0 || address.equals("To be filled by O.E.M.") || address.equals("To be filled by O.E.M") || address.equals("Base Board Serial Number")) { - System.out.println("Error! Can't ban address " + address + " type = " + type + "!"); - return false; - } - if (isBanned(address)) { - return false; - } - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return false; - } - PreparedStatement statement; - try { - statement = connection.prepareStatement("INSERT INTO punishments VALUES('" + address + "','" + type + "')"); - statement.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - SQLManager.close(connection); - return false; - } finally { - SQLManager.close(connection); - } - return true; - } - - /** - * Unbans an address. - * @param address the address. - * @return {@code True} if unbanned. - */ - public static boolean unban(String address) { - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return false; - } - PreparedStatement statement; - try { - statement = connection.prepareStatement("DELETE from punishments WHERE address ='" + address + "'"); - statement.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - SQLManager.close(connection); - return false; - } finally { - SQLManager.close(connection); - } - return true; - } - - /** - * Notifies the punishing player of success. - * @param staff The punishing player. - * @param type The punishment type. - * @param target The target name. - * @param end The end time stamp of the punishment. - */ - private static void notify(PlayerSession staff, String type, String target, long end) { - if (end <= System.currentTimeMillis()) { - WorldPacketRepository.sendPlayerMessage(staff, "Successfully removed punishment [type=" + type + ", player=" + target + "]."); - return; - } - WorldPacketRepository.sendPlayerMessage(staff, "Successfully punished player " + target + " [type=" + type + ", duration=" + getDuration(end) + "]."); - } - - /** - * Notifies the game servers of a punishment update. - * @param key The punishment key. - * @param type The type. - * @param duration The duration. - */ - public static void notifyServers(String key, int type, long duration) { - for (GameServer server : WorldDatabase.getWorlds()) { - if (server != null && server.isActive()) { - WorldPacketRepository.sendPunishUpdate(server, key, type, duration); - } - } - } - - /** - * Checks if the UID Info is banned. - * @param info the info. - * @return {@code True} if banned. - */ - public static boolean isSystemBanned(UIDInfo info) { - return isBanned(info.getIp()) || isBanned(info.getMac()) || isBanned(info.getSerial()); - } - - /** - * Checked if an address is banned. - * @param address the address. - * @param type the type. - * @return {@code True} if so. - */ - public static boolean isBanned(String address) { - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return false; - } - try { - ResultSet set = connection.createStatement().executeQuery("SELECT * FROM punishments WHERE address ='" + address +"'"); - if (set == null || !set.next()) { - SQLManager.close(connection); - return false; - } - } catch (SQLException e) { - e.printStackTrace(); - SQLManager.close(connection); - return false; - } finally { - SQLManager.close(connection); - } - return true; - } - - /** - * Gets the duration string representation. - * @param end The end time. - * @return The string. - */ - private static String getDuration(long end) { - String time = "indefinite time"; - if (end != Long.MAX_VALUE) { - int days = (int) ((System.currentTimeMillis()) / (24 * 60 * 60_000)); - int hours = (int) (((24L * days * 60 * 60_000)) / (60 * 60_000)); - int minutes = (int) (((hours * (60 * 60_000))) / 60_000); - time = days + "d, " + hours + "h, " + minutes + "m"; - } - return time; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/ShutdownSequence.java b/Management-Server/src/main/java/ms/system/ShutdownSequence.java deleted file mode 100644 index 9d137923d..000000000 --- a/Management-Server/src/main/java/ms/system/ShutdownSequence.java +++ /dev/null @@ -1,27 +0,0 @@ -package ms.system; - - -import ms.Management; - -/** - * The shutdown sequence used for safely turning off the Management server. - * @author Emperor - * - */ -public final class ShutdownSequence extends Thread { - - @Override - public void run() { - if (Management.active) { - shutdown(); - } - } - - /** - * Safely shuts down the Management server. - */ - public static void shutdown() { - System.out.println("Management server successfully shut down!"); - Management.active = false; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/communication/ClanRank.java b/Management-Server/src/main/java/ms/system/communication/ClanRank.java deleted file mode 100644 index f13aa7212..000000000 --- a/Management-Server/src/main/java/ms/system/communication/ClanRank.java +++ /dev/null @@ -1,54 +0,0 @@ -package ms.system.communication; - -/** - * Represents the rank of a clan member. - * @author Emperor - */ -public enum ClanRank { - NONE(-1, "Anyone"), - FRIEND(0, "Any friends"), - RECRUIT(1, "Recruit+"), - CORPORAL(2, "Corporal+"), - SERGEANT(3, "Sergeant+"), - LIEUTENANT(4, "Lieutenant+"), - CAPTAIN(5, "Captain+"), - GENERAL(6, "General+"), - OWNER(7, "Only me"), - ADMINISTRATOR(127, "No-one"); - - /** - * The value of the rank. - */ - private final int value; - - /** - * The requirement info. - */ - private final String info; - - /** - * Constructs a new {@code ClanRank} {@code Object}. - * @param value The rank value. - * @param info The requirement info. - */ - private ClanRank(int value, String info) { - this.value = value; - this.info = info; - } - - /** - * Gets the value. - * @return The value. - */ - public int getValue() { - return value; - } - - /** - * Gets the info. - * @return The info. - */ - public String getInfo() { - return info; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/communication/ClanRepository.java b/Management-Server/src/main/java/ms/system/communication/ClanRepository.java deleted file mode 100644 index bfe96cbd4..000000000 --- a/Management-Server/src/main/java/ms/system/communication/ClanRepository.java +++ /dev/null @@ -1,337 +0,0 @@ -package ms.system.communication; - -import ms.net.packet.WorldPacketRepository; -import ms.system.util.StringUtils; -import ms.world.GameServer; -import ms.world.PlayerSession; -import ms.world.WorldDatabase; -import ms.world.info.UIDInfo; - -import java.util.*; - -/** - * Holds clan related information. - * @author Emperor - * - */ -public final class ClanRepository { - - /** - * The mapping of active clans. - */ - private static final Map CLANS = new HashMap<>(); - - /** - * The maximum amount of members to be in a clan chat. - */ - public static final int MAX_MEMBERS = 100; - - /** - * The owner's details. - */ - private PlayerSession owner; - - /** - * The list of players currently in the chat. - */ - private final List players = new ArrayList<>(MAX_MEMBERS); - - /** - * The banned players. - */ - private final Map banned = new HashMap<>(); - - /** - * Constructs a new {@code ClanRepository} {@code Object}. - */ - private ClanRepository() { - /* - * empty. - */ - } - - /** - * Gets the clan repository for the given username. - * @param ownerName The clan owner's name. - * @return The clan repository. - */ - public static ClanRepository get(GameServer server, String ownerName) { - ClanRepository clan = CLANS.get(ownerName); - if (clan != null) { - return clan; - } - PlayerSession owner = WorldDatabase.getPlayer(ownerName); - if (owner == null) { - owner = new PlayerSession(ownerName, ownerName, new UIDInfo()); - owner.parse(); - } - if (owner.getCommunication().getClanName().equals("")) { - return null; - } - clan = new ClanRepository(); - clan.owner = owner; - CLANS.put(ownerName, clan); - return clan; - } - - /** - * Enters the clan chat. - * @param player The player. - */ - public void enter(PlayerSession player) { - if (players.size() >= MAX_MEMBERS && !owner.getUsername().equals("2009scape")) { - WorldPacketRepository.sendPlayerMessage(player, "The channel you tried to join is full.:clan:"); - return; - } - if (player != owner && player.getRights() != 2) { - if (isBanned(player.getUsername()) || owner.getCommunication().getBlocked().contains(player.getUsername())) { - WorldPacketRepository.sendPlayerMessage(player, "You are temporarily banned from this clan channel.:clan:"); - return; - } - ClanRank rank = getRank(player); - if (rank.ordinal() < getJoinRequirement().ordinal()) { - WorldPacketRepository.sendPlayerMessage(player, "You do not have a high enough rank to join this clan channel.:clan:"); - return; - } - } - if (!players.contains(player)) { - players.add(player); - } - WorldPacketRepository.sendPlayerMessage(player, "Now talking in clan channel " + owner.getCommunication().getClanName() + ".:clan:"); - WorldPacketRepository.sendPlayerMessage(player, "To talk, start each line of chat with the / symbol.:clan:"); - player.getCommunication().setCurrentClan(owner.getUsername()); - player.setClan(this); - update(); - } - - /** - * Leaves the clan chat. - * @param player The player to leave. - * @param remove If the player should be removed from the list. - */ - public void leave(PlayerSession player, boolean remove) { - if (remove) { - players.remove(player); - update(); - if (players.size() < 1) { - banned.clear(); - } - } - WorldPacketRepository.sendPlayerMessage(player, "You have left the channel.:clan:"); - player.setClan(null); - player.getCommunication().setCurrentClan(null); - if (player.isActive()) { - WorldPacketRepository.sendLeaveClan(player); - } - } - - /** - * Sends a message to all players in the chat. - * @param player The player sending the message. - * @param message The message to send. - */ - public void message(PlayerSession player, String message) { - if (player != owner && player.getRights() != 2) { - ClanRank rank = getRank(player); - if (rank.ordinal() < getMessageRequirement().ordinal()) { - WorldPacketRepository.sendPlayerMessage(player, "You do not have a high enough rank to talk in this clan channel.:clan:"); - return; - } - } - for (Iterator it = players.iterator(); it.hasNext();) { - PlayerSession p = it.next(); - if (p != null) { - WorldPacketRepository.sendMessage(p, player, 2, message); - } - } - } - - /** - * Updates the clan chat. - */ - public void update() { - for (GameServer server : WorldDatabase.getWorlds()) { - if (server != null && server.isActive()) { - WorldPacketRepository.sendClanInformation(server, this); - } - } - } - - /** - * Kicks a player from the clan chat. - * @param player The player. - */ - public void kick(PlayerSession player, PlayerSession target) { - ClanRank rank = getRank(target); - if (target.getRights() == 2) { - WorldPacketRepository.sendPlayerMessage(player, "You can't kick an administrator.:clan:"); - return; - } - System.out.println(rank + ", " + player.getUsername()); - if (player.getRights() < 1/*!= 2 && rank.ordinal() < getKickRequirement().ordinal()*/) { - WorldPacketRepository.sendPlayerMessage(player, "You do not have a high enough rank to kick in this clan channel.:clan:"); - return; - } - if (target == owner) { - WorldPacketRepository.sendPlayerMessage(player, "You can't kick the owner of this clan channel.:clan:"); - return; - } - if (target == player) { - WorldPacketRepository.sendPlayerMessage(player, "You can't kick yourself.:clan:"); - return; - } - for (PlayerSession p : players) { - WorldPacketRepository.sendMessage(p, player, 2, "[Attempting to kick/ban " + StringUtils.formatDisplayName(target.getUsername()) + " from this Clan Chat.]"); - } - leave(target, true); - banned.put(target.getUsername(), System.currentTimeMillis() + (3_600_000)); - WorldPacketRepository.sendPlayerMessage(target, "You have been kicked from the channel.:clan:"); - } - - /** - * Gets the rank for the given player. - * @param player The player. - * @return The rank. - */ - public ClanRank getRank(PlayerSession player) { - ClanRank rank = owner.getCommunication().getContacts().get(player.getUsername()); - if (player.getRights() == 2 && player != owner) { - return ClanRank.ADMINISTRATOR; - } - if (rank == null) { - if (player == owner) { - return ClanRank.OWNER; - } - return ClanRank.NONE; - } - return rank; - } - - /** - * Checks if the player is banned. - * @param username The username of the player. - * @return {@code True} if so. - */ - private boolean isBanned(String username) { - Long time = banned.get(username); - if (time == null) { - return false; - } - if (time < System.currentTimeMillis()) { - banned.remove(username); - return false; - } - return true; - } - - /** - * Clears the clan chat. - * @param disable If the clan chat is getting disabled. - */ - public void clean(boolean disable) { - for (Iterator it = players.iterator(); it.hasNext();) { - PlayerSession player = it.next(); - boolean remove = disable; - if (!remove) { - remove = getRank(player).ordinal() < getJoinRequirement().getValue(); - } - if (remove) { - leave(player, false); - it.remove(); - } - } - if (players.isEmpty()) { - banned.clear(); - } - update(); - } - - /** - * Renames the clan chat. - * @param name The new clan name. - */ - public void rename(String name) { - owner.getCommunication().setClanName(name); - update(); - } - - /** - * Gets the clan name. - * @return The clan name. - */ - public String getName() { - return owner.getCommunication().getClanName(); - } - - /** - * Gets the owner value. - * @return The owner. - */ - public PlayerSession getOwner() { - return owner; - } - - /** - * Sets the owner value. - * @param owner The owner to set. - */ - public void setOwner(PlayerSession owner) { - this.owner = owner; - } - - /** - * Gets the players value. - * @return The players. - */ - public List getPlayers() { - return players; - } - - /** - * Gets the clans value. - * @return The clans. - */ - public static Map getClans() { - return CLANS; - } - - /** - * Gets the joinRequirement value. - * @return The joinRequirement. - */ - public ClanRank getJoinRequirement() { - return owner.getCommunication().getJoinRequirement(); - } - - /** - * Gets the messageRequirement value. - * @return The messageRequirement. - */ - public ClanRank getMessageRequirement() { - return owner.getCommunication().getMessageRequirement(); - } - - /** - * Gets the kickRequirement value. - * @return The kickRequirement. - */ - public ClanRank getKickRequirement() { - return owner.getCommunication().getKickRequirement(); - } - - /** - * Gets the lootRequirement value. - * @return The lootRequirement. - */ - public ClanRank getLootRequirement() { - return owner.getCommunication().getLootRequirement(); - } - - /** - * Gets the banned value. - * @return The banned. - */ - public Map getBanned() { - return banned; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/communication/CommunicationInfo.java b/Management-Server/src/main/java/ms/system/communication/CommunicationInfo.java deleted file mode 100644 index 86afae5ec..000000000 --- a/Management-Server/src/main/java/ms/system/communication/CommunicationInfo.java +++ /dev/null @@ -1,656 +0,0 @@ -package ms.system.communication; - -import ms.ServerConstants; -import ms.net.packet.WorldPacketRepository; -import ms.system.util.ByteBufferUtils; -import ms.system.util.StringUtils; -import ms.world.GameServer; -import ms.world.PlayerSession; -import ms.world.WorldDatabase; - -import java.nio.ByteBuffer; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.*; -import java.util.Map.Entry; - -/** - * Holds communication information. - * @author Emperor - * - */ -public final class CommunicationInfo { - - /** - * The maximum list size. - */ - public static final int MAX_LIST_SIZE = 200; - - /** - * The clan ranks. - */ - private final Map contacts = new HashMap<>(); - - /** - * The list of blocked players. - */ - private final List blocked = new ArrayList<>(20); - - /** - * The player's clan name. - */ - private String clanName = ""; - - /** - * The current clan this player is in. - */ - private String currentClan = "2009scape"; - - /** - * The rank required for joining. - */ - private ClanRank joinRequirement = ClanRank.FRIEND; - - /** - * The rank required for messaging. - */ - private ClanRank messageRequirement = ClanRank.NONE; - - /** - * The rank required for kicking members. - */ - private ClanRank kickRequirement = ClanRank.OWNER; - - /** - * The rank required for loot-share. - */ - private ClanRank lootRequirement = ClanRank.ADMINISTRATOR; - - /** - * The public chat setting. - */ - private int publicChatSetting = 0; - - /** - * The private chat setting. - */ - private int privateChatSetting = 0; - - /** - * The trade setting. - */ - private int tradeSetting = 0; - - /** - * The player session. - */ - private PlayerSession player; - - /** - * Constructs a new {@code CommunicationInfo} {@code Object}. - * @param player The player. - */ - public CommunicationInfo(PlayerSession player) { - this.setPlayer(player); - } - - /** - * Called when the player logs in. - */ - public void sync() { - if (privateChatSetting != 2) { - for (GameServer server : WorldDatabase.getWorlds()) { - if (server != null && server.isActive()) { - List names = new ArrayList<>(20); - for (PlayerSession p : server.getPlayers().values()) { - if (p.isActive() && p.getCommunication().contacts.containsKey(player.getUsername())) { - if (privateChatSetting == 0 || contacts.containsKey(p.getUsername())) { - names.add(p.getUsername()); - } - } - } - WorldPacketRepository.notifyPlayers(server, player, names); - } - } - } - } - - /** - * Called when the player logs out. - */ - public void clear() { - for (GameServer server : WorldDatabase.getWorlds()) { - if (server != null && server.isActive()) { - WorldPacketRepository.notifyLogout(server, player); - } - } - if (player.getClan() != null) { - player.getClan().leave(player, true); - } - } - - /** - * Saves the communication info. - * @param statement The buffer. - * @throws SQLException The exception if thrown. - */ - public void save(PreparedStatement statement) throws SQLException { - String contacts = ""; - String blocked = ""; - StringBuilder blockedBuilder = new StringBuilder(); - for (int i = 0; i < this.blocked.size(); i++) { - String blockline = (i == 0 ? "" : ",") + this.blocked.get(i); - blockedBuilder.append(blockline); - } - blocked = blockedBuilder.toString(); - int count = 0; - StringBuilder contactBuilder = new StringBuilder(); - for (Entry entry : this.contacts.entrySet()) { - String contactLine = "{" + entry.getKey() + "," + entry.getValue().ordinal() + "}" + (count == this.contacts.size() - 1 ? "" : "~"); - contactBuilder.append(contactLine); - count++; - } - contacts = contactBuilder.toString(); - statement.setString(3, contacts); - statement.setString(4, blocked); - statement.setString(5, clanName); - statement.setString(6, currentClan); - statement.setString(7, joinRequirement.ordinal() + "," + messageRequirement.ordinal() + "," + kickRequirement.ordinal() + "," + lootRequirement.ordinal()); - statement.setString(8, publicChatSetting + "," + privateChatSetting + "," + tradeSetting); - } - - /** - * Parses the communication info from the database. - * @param set The result set. - * @throws SQLException The exception if thrown. - */ - public void parse(ResultSet set) throws SQLException { - String contacts = set.getString("contacts"); - String[] tokens; - if (contacts != null && !contacts.isEmpty()) { - String[] datas = contacts.split("~"); - for (String d : datas) { - tokens = d.replace("{", "").replace("}", "").split(","); - if (tokens.length < 2) { - continue; - } - this.contacts.put(tokens[0], ClanRank.values()[Integer.parseInt(tokens[1])]); - } - } - String bl = set.getString("blocked"); - if (bl != null && !bl.isEmpty()) { - tokens = bl.split(","); - blocked.addAll(Arrays.asList(tokens)); - } - clanName = set.getString("clanName"); - currentClan = set.getString("currentClan"); - String clanReqs = set.getString("clanReqs"); - if (!clanReqs.isEmpty()) { - tokens = clanReqs.split(","); - ClanRank rank = null; - int ordinal = 0; - for (int i = 0; i < tokens.length; i++) { - ordinal = Integer.parseInt(tokens[i]); - if (ordinal < 0 || ordinal > ClanRank.values().length -1) { - continue; - } - rank = ClanRank.values()[ordinal]; - switch (i) { - case 0: - joinRequirement = rank; - break; - case 1: - messageRequirement = rank; - break; - case 2: - if (ordinal < 3 || ordinal > 8) { - break; - } - kickRequirement = rank; - break; - case 3: - lootRequirement = rank; - break; - default: - break; - } - } - } - String chatSettings = set.getString("chatSettings"); - if (!chatSettings.isEmpty()) { - tokens = chatSettings.split(","); - for (int i = 0; i < tokens.length; i++) { - switch (i) { - case 0: - publicChatSetting = Integer.parseInt(tokens[0]); - break; - case 1: - privateChatSetting = Integer.parseInt(tokens[1]); - break; - case 2: - tradeSetting = Integer.parseInt(tokens[2]); - break; - default: - System.out.println("Illegal arg count in chatsetting string: " + chatSettings); - break; - } - } - } - } - - /** - * Saves the communication info. - * @param buffer The buffer. - */ - public void save(ByteBuffer buffer) { - buffer.put((byte) contacts.size()); - for(Entry contact : contacts.entrySet()){ - ByteBufferUtils.putString(contact.getKey(), buffer); - buffer.put((byte) contact.getValue().ordinal()); - } - buffer.put((byte) blocked.size()); - for (String name : blocked) { - ByteBufferUtils.putString(name, buffer); - } - ByteBufferUtils.putString(clanName, buffer); - if (currentClan != null) { - ByteBufferUtils.putString(currentClan, buffer.put((byte) 1)); - } else { - buffer.put((byte) 0); - } - buffer.put((byte) joinRequirement.ordinal()); - buffer.put((byte) messageRequirement.ordinal()); - buffer.put((byte) kickRequirement.ordinal()); - buffer.put((byte) lootRequirement.ordinal()); - buffer.put((byte) publicChatSetting); - buffer.put((byte) privateChatSetting); - buffer.put((byte) tradeSetting); - } - - /** - * Parses the communication info from the buffer. - * @param buffer The buffer. - */ - public void parse(ByteBuffer buffer) { - int length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - contacts.put(ByteBufferUtils.getString(buffer), ClanRank.values()[buffer.get() & 0xFF]); - } - length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - blocked.add(ByteBufferUtils.getString(buffer)); - } - clanName = ByteBufferUtils.getString(buffer); - if (buffer.get() == 1) { - currentClan = ByteBufferUtils.getString(buffer); - } - joinRequirement = ClanRank.values()[buffer.get()]; - messageRequirement = ClanRank.values()[buffer.get()]; - kickRequirement = ClanRank.values()[buffer.get()]; - lootRequirement = ClanRank.values()[buffer.get()]; - publicChatSetting = buffer.get(); - privateChatSetting = buffer.get(); - tradeSetting = buffer.get(); - } - - /** - * Sends a message to the target. - * @param target The target. - * @param message The message to send. - */ - public void sendMessage(String target, String message) { - PlayerSession receiver = WorldDatabase.getPlayer(target); - if (receiver == null || !receiver.isActive()) { - WorldPacketRepository.sendPlayerMessage(player, "That player is currently offline."); - return; - } - WorldPacketRepository.sendMessage(player, receiver, 0, message); - WorldPacketRepository.sendMessage(receiver, player, 1, message); - } - - /** - * Adds a contact. - * @param contact The contact to add. - */ - public void add(String contact) { - if (contacts.size() >= MAX_LIST_SIZE) { - WorldPacketRepository.sendPlayerMessage(player, "Your friend list is full."); - return; - } - if (blocked.contains(contact)) { - WorldPacketRepository.sendPlayerMessage(player, "Please remove " + StringUtils.formatDisplayName(contact) + " from your ignored list first."); - return; - } - if (contacts.containsKey(contact)) { - WorldPacketRepository.sendPlayerMessage(player, StringUtils.formatDisplayName(contact) + " is already on your friend list."); - return; - } - contacts.put(contact, ClanRank.FRIEND); - WorldPacketRepository.sendContactUpdate(player, contact, false, false, getWorldId(player, contact), null); - ClanRepository clan = ClanRepository.getClans().get(player.getUsername()); - if (clan != null) { - clan.update(); - } - if (privateChatSetting == 1) { - PlayerSession other = WorldDatabase.getPlayer(contact); - if (other != null && other.isActive() && other.getCommunication().getContacts().containsKey(player.getUsername())) { - WorldPacketRepository.sendContactUpdate(other, player.getUsername(), false, false, getWorldId(other, player.getUsername()), null); - } - } - } - - /** - * Gets the world id for the given contact. - * @param player The player. - * @param contact The contact. - * @return The world id to display. - */ - public static int getWorldId(PlayerSession player, String contact) { - PlayerSession p = WorldDatabase.getPlayer(contact); - if (p == null || !p.isActive() || p.getCommunication().getPrivateChatSetting() == 2) { - return 0; - } - if (p.getCommunication().getBlocked().contains(player.getUsername())) { - return 0; - } - if (p.getCommunication().getPrivateChatSetting() == 1) { - if (p.getCommunication().getContacts().containsKey(player.getUsername())) { - return p.getWorldId(); - } - return 0; - } - return p.getWorldId(); - } - - /** - * Removes a contact. - * @param contact The contact to remove. - * @param block If the contact should be removed from the block list. - */ - public void remove(String contact, boolean block) { - PlayerSession other = WorldDatabase.getPlayer(contact); - if (block) { - blocked.remove(contact); - if (other != null && other.isActive() && other.getCommunication().getContacts().containsKey(player.getUsername())) { - WorldPacketRepository.sendContactUpdate(other, player.getUsername(), false, false, getWorldId(other, player.getUsername()), null); - } - } else { - contacts.remove(contact); - ClanRepository clan = ClanRepository.getClans().get(player.getUsername()); - if (clan != null) { - clan.update(); - } - if (privateChatSetting == 1 && other != null && other.isActive() && other.getCommunication().getContacts().containsKey(player.getUsername())) { - WorldPacketRepository.sendContactUpdate(other, player.getUsername(), false, false, getWorldId(other, player.getUsername()), null); - } - } - WorldPacketRepository.sendContactUpdate(player, contact, block, true, 0, null); - } - - - /** - * Adds a blocked contact. - * @param contact The contact to block. - */ - public void block(String contact) { - if (blocked.size() >= MAX_LIST_SIZE) { - WorldPacketRepository.sendPlayerMessage(player, "Your ignore list is full."); - return; - } - if (contacts.containsKey(contact)) { - WorldPacketRepository.sendPlayerMessage(player, "Please remove " + StringUtils.formatDisplayName(contact) + " from your friends list first."); - return; - } - if (blocked.contains(contact)) { - WorldPacketRepository.sendPlayerMessage(player, StringUtils.formatDisplayName(contact) + " is already on your friend list."); - return; - } - blocked.add(contact); - WorldPacketRepository.sendContactUpdate(player, contact, true, false, 0, null); - PlayerSession other = WorldDatabase.getPlayer(contact); - if (other != null && other.isActive() && other.getCommunication().getContacts().containsKey(player.getUsername())) { - WorldPacketRepository.sendContactUpdate(other, player.getUsername(), false, false, 0, null); - } - } - - /** - * Updates the clan rank of a certain contact. - * @param contact The contact. - * @param clanRank The clan rank to set. - */ - public void updateClanRank(String contact, ClanRank clanRank) { - if (!contacts.containsKey(contact)) { - System.err.println("Could not find contact " + contact + " to update clan rank!"); - return; - } - contacts.put(contact, clanRank); - ClanRepository clan = ClanRepository.getClans().get(player.getUsername()); - if (clan != null) { - clan.update(); - } - - WorldPacketRepository.sendContactUpdate(player, contact, false, false, 0, clanRank); - } - - /** - * Updates the settings. - * @param publicSetting The public chat setting. - * @param privateSetting The private chat setting. - * @param tradeSetting The trade setting. - */ - public void updateSettings(int publicSetting, int privateSetting, int tradeSetting) { - this.publicChatSetting = publicSetting; - this.tradeSetting = tradeSetting; - if (this.privateChatSetting != privateSetting) { - updatePrivateSetting(privateSetting); - } - } - - /** - * Updates the private chat setting. - * @param privateSetting The private chat setting. - */ - private void updatePrivateSetting(int privateSetting) { - this.privateChatSetting = privateSetting; - for (GameServer server : WorldDatabase.getWorlds()) { - if (server != null && server.isActive()) { - if (privateSetting == 2) { - WorldPacketRepository.notifyLogout(server, player); - continue; - } - for (PlayerSession p : server.getPlayers().values()) { - if (p.isActive() && p.getCommunication().contacts.containsKey(player.getUsername())) { - WorldPacketRepository.sendContactUpdate(p, player.getUsername(), false, false, getWorldId(p, player.getUsername()), null); - } - } - } - } - } - - /** - * Gets the contacts value. - * @return The contacts. - */ - public Map getContacts() { - return contacts; - } - - /** - * Gets the clan rank for the given contact. - * @param contact The contact. - * @return The rank. - */ - public ClanRank getRank(String contact) { - return contacts.get(contact); - } - - /** - * Gets the blocked value. - * @return The blocked. - */ - public List getBlocked() { - return blocked; - } - - /** - * Gets the clanName value. - * @return The clanName. - */ - public String getClanName() { - return clanName; - } - - /** - * Sets the clanName value. - * @param clanName The clanName to set. - */ - public void setClanName(String clanName) { - this.clanName = clanName; - } - - /** - * Gets the currentClan value. - * @return The currentClan. - */ - public String getCurrentClan() { - return currentClan; - } - - /** - * Sets the currentClan value. - * @param currentClan The currentClan to set. - */ - public void setCurrentClan(String currentClan) { - this.currentClan = currentClan; - } - - /** - * Gets the player value. - * @return The player. - */ - public PlayerSession getPlayer() { - return player; - } - - /** - * Sets the player value. - * @param player The player to set. - */ - public void setPlayer(PlayerSession player) { - this.player = player; - } - - /** - * Gets the joinRequirement value. - * @return The joinRequirement. - */ - public ClanRank getJoinRequirement() { - return joinRequirement; - } - - /** - * Sets the joinRequirement value. - * @param joinRequirement The joinRequirement to set. - */ - public void setJoinRequirement(ClanRank joinRequirement) { - this.joinRequirement = joinRequirement; - } - - /** - * Gets the messageRequirement value. - * @return The messageRequirement. - */ - public ClanRank getMessageRequirement() { - return messageRequirement; - } - - /** - * Sets the messageRequirement value. - * @param messageRequirement The messageRequirement to set. - */ - public void setMessageRequirement(ClanRank messageRequirement) { - this.messageRequirement = messageRequirement; - } - - /** - * Gets the kickRequirement value. - * @return The kickRequirement. - */ - public ClanRank getKickRequirement() { - return kickRequirement; - } - - /** - * Sets the kickRequirement value. - * @param kickRequirement The kickRequirement to set. - */ - public void setKickRequirement(ClanRank kickRequirement) { - this.kickRequirement = kickRequirement; - } - - /** - * Gets the lootRequirement value. - * @return The lootRequirement. - */ - public ClanRank getLootRequirement() { - return lootRequirement; - } - - /** - * Sets the lootRequirement value. - * @param lootRequirement The lootRequirement to set. - */ - public void setLootRequirement(ClanRank lootRequirement) { - this.lootRequirement = lootRequirement; - } - - /** - * Gets the publicChatSetting value. - * @return The publicChatSetting. - */ - public int getPublicChatSetting() { - return publicChatSetting; - } - - /** - * Sets the publicChatSetting value. - * @param publicChatSetting The publicChatSetting to set. - */ - public void setPublicChatSetting(int publicChatSetting) { - this.publicChatSetting = publicChatSetting; - } - - /** - * Gets the privateChatSetting value. - * @return The privateChatSetting. - */ - public int getPrivateChatSetting() { - return privateChatSetting; - } - - /** - * Sets the privateChatSetting value. - * @param privateChatSetting The privateChatSetting to set. - */ - public void setPrivateChatSetting(int privateChatSetting) { - this.privateChatSetting = privateChatSetting; - } - - /** - * Gets the tradeSetting value. - * @return The tradeSetting. - */ - public int getTradeSetting() { - return tradeSetting; - } - - /** - * Sets the tradeSetting value. - * @param tradeSetting The tradeSetting to set. - */ - public void setTradeSetting(int tradeSetting) { - this.tradeSetting = tradeSetting; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/mysql/SQLColumn.java b/Management-Server/src/main/java/ms/system/mysql/SQLColumn.java deleted file mode 100644 index 727c3075f..000000000 --- a/Management-Server/src/main/java/ms/system/mysql/SQLColumn.java +++ /dev/null @@ -1,121 +0,0 @@ -package ms.system.mysql; - -/** - * Represents a column used in an SQL table. - * @author Emperor - * - */ -public final class SQLColumn { - - /** - * The name. - */ - private final String name; - - /** - * The data type. - */ - private final Class type; - - /** - * If this column should never be updated in the database. - */ - private final boolean neverUpdate; - - /** - * The value. - */ - private Object value; - - /** - * If the column value changed. - */ - private boolean changed; - - /** - * Constructs a new {@code SQLColumn} {@code Object}. - * @param name The column name. - * @param type The data type. - */ - public SQLColumn(String name, Class type) { - this(name, type, false); - } - - /** - * Constructs a new {@code SQLColumn} {@code Object}. - * @param name The column name. - * @param type The data type. - * @param neverUpdate If this column should never be updated in the database. - */ - public SQLColumn(String name, Class type, boolean neverUpdate) { - this.name = name; - this.type = type; - this.neverUpdate = neverUpdate; - } - - /** - * Gets the name. - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Updates the value. - * @param value The value. - */ - public void updateValue(Object value) { - this.changed = value != this.value; - this.value = value; - } - - /** - * Gets the value. - * @return The value. - */ - public Object getValue() { - return value; - } - - /** - * Sets the value. - * @param value The value to set. - */ - public void setValue(Object value) { - this.value = value; - this.changed = false; - } - - /** - * Gets the changed. - * @return The changed. - */ - public boolean isChanged() { - return changed; - } - - /** - * Sets the changed. - * @param changed The changed to set. - */ - public void setChanged(boolean changed) { - this.changed = changed; - } - - /** - * Gets the type. - * @return The type. - */ - public Class getType() { - return type; - } - - /** - * Gets the neverUpdate. - * @return The neverUpdate. - */ - public boolean isNeverUpdate() { - return neverUpdate; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/mysql/SQLEntryHandler.java b/Management-Server/src/main/java/ms/system/mysql/SQLEntryHandler.java deleted file mode 100644 index 94ee8b5c2..000000000 --- a/Management-Server/src/main/java/ms/system/mysql/SQLEntryHandler.java +++ /dev/null @@ -1,202 +0,0 @@ -package ms.system.mysql; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * Handles an SQL entry. - * @author Emperor - * @param The entry type. - */ -public abstract class SQLEntryHandler { - - /** - * The connection. - */ - protected Connection connection; - - /** - * The entry. - */ - protected T entry; - - /** - * The result set. - */ - protected ResultSet result; - - /** - * The table name. - */ - protected String table; - - /** - * The column name. - */ - private String column; - - /** - * The value to use. - */ - protected String value; - - /** - * Constructs a new {@code SQLEntryHandler} {@code Object}. - * @param entry The entry. - * @param table The table name. - * @param column column name. - * @param value The column value. - */ - public SQLEntryHandler(T entry, String table, String column, String value) { - this.entry = entry; - this.table = table; - this.column = column; - this.value = value; - } - - /** - * Reads the SQL entry. - * @param entry The entry. - */ - public static void read(SQLEntryHandler entry) { - entry.connection = entry.getConnection(); - if (entry.connection == null) { - //System.err.println("Could not read SQL data: connection is null!"); - return; - } - try { - entry.read(); - if (entry.result == null || !entry.result.next()) { - entry.create(); - } else { - entry.parse(); - } - } catch (SQLException e) { - e.printStackTrace(); - } - SQLManager.close(entry.connection); - entry.connection = null; - } - - /** - * Writes the entry data on the SQL database. - * @param entry The entry. - */ - public static void write(SQLEntryHandler entry) { - write(entry, entry.getConnection(), true); - } - - /** - * Reads the SQL entry. - * @param entry The entry. - */ - public static void write(SQLEntryHandler entry, Connection connection, boolean commit) { - if (connection == null) { - //System.err.println("Could not write SQL data: connection is null!"); - return; - } - entry.connection = connection; - try { - if (!commit) { - entry.connection.setAutoCommit(false); - } - entry.save(); - } catch (SQLException e) { - e.printStackTrace(); - } - if (commit) { - SQLManager.close(entry.connection); - } - entry.connection = null; - } - - /** - * Reads the table from the SQL database. - * @throws SQLException When an SQL exception occurs. - */ - public void read() throws SQLException { - PreparedStatement statement = connection.prepareStatement("SELECT " + getReadSelection() + " FROM " + table + " WHERE " + column + " = ?"); - statement.setString(1, value); - result = statement.executeQuery(); - } - - /** - * Gets the result. - * @return the result. - */ - public ResultSet getResult() { - return result; - } - - /** - * Gets the selection. - * @return The selection. - */ - public String getReadSelection() { - return "*"; - } - - /** - * Gets the writing statement. - * @param create If we are creating a new row. - * @param columns The columns to update. - */ - public PreparedStatement getWritingStatement(boolean create, String...columns) { - PreparedStatement statement = null; - try { - StringBuilder sb = new StringBuilder(); - if (create) { - sb.append("INSERT INTO ").append(table).append(" (").append(column); - for (String name : columns) { - sb.append(",").append(name); - } - sb.append(") VALUES (?"); - for (int i = 0; i < columns.length; i++) { - sb.append(",?"); - } - sb.append(")"); - } else { - sb.append("UPDATE ").append(table).append(" SET "); - for (int i = 0; i < columns.length; i++) { - sb.append(columns[i]).append("=?"); - if (i < columns.length - 1) { - sb.append(","); - } - } - sb.append(" WHERE ").append(column).append("=?"); - } - statement = connection.prepareStatement(sb.toString()); - statement.setString(create ? 1 : columns.length + 1, value); - } catch (SQLException e) { - e.printStackTrace(); - } - return statement; - } - - /** - * Parses the entry. - * @throws SQLException When an SQL exception occurs. - */ - public abstract void parse() throws SQLException; - - /** - * Creates a new row in the database. - * @throws SQLException When an SQL exception occurs. - */ - public abstract void create() throws SQLException; - - /** - * Saves the entry. - * @throws SQLException When an SQL exception occurs. - */ - public abstract void save() throws SQLException; - - /** - * Gets the connection. - * @return The connection. - */ - public abstract Connection getConnection(); - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/mysql/SQLManager.kt b/Management-Server/src/main/java/ms/system/mysql/SQLManager.kt deleted file mode 100644 index 05affcacc..000000000 --- a/Management-Server/src/main/java/ms/system/mysql/SQLManager.kt +++ /dev/null @@ -1,64 +0,0 @@ -package ms.system.mysql - -import ms.system.util.ManagementConstants -import ms.system.util.ManagementConstants.DATABASE_HOST_ADDRESS -import ms.system.util.ManagementConstants.DATABASE_NAME -import ms.system.util.ManagementConstants.DATABASE_PASSWORD -import ms.system.util.ManagementConstants.DATABASE_PORT -import ms.system.util.ManagementConstants.DATABASE_USERNAME -import java.sql.Connection -import java.sql.DriverManager -import java.sql.SQLException - -/** - * Manages the sql connections. - * @author Vexia - */ -object SQLManager { - - /** - * IF the sql manager is initialized. - */ - var isInitialized = false - - /** - * Initializes the sql manager. - */ - @JvmStatic - fun init() { - isInitialized = true - WorldListSQLHandler.clearWorldList() - } - - /** - * Gets a connection from the pool. - * @return The connection. - */ - @JvmStatic - val connection: Connection? - get() { - try { - return DriverManager.getConnection( - "jdbc:mysql://$DATABASE_HOST_ADDRESS:$DATABASE_PORT/$DATABASE_NAME?useTimezone=true&serverTimezone=UTC", - DATABASE_USERNAME, - DATABASE_PASSWORD - ) - } catch (e: SQLException) { - println("Error: Mysql error message=" + e.message + ".") - } - return null - } - - /** - * Releases the connection so it's available for usage. - * @param connection The connection. - */ - @JvmStatic - fun close(connection: Connection) { - try { - connection.close() - } catch (e: SQLException) { - e.printStackTrace() - } - } -} diff --git a/Management-Server/src/main/java/ms/system/mysql/SQLTable.java b/Management-Server/src/main/java/ms/system/mysql/SQLTable.java deleted file mode 100644 index 3447edb23..000000000 --- a/Management-Server/src/main/java/ms/system/mysql/SQLTable.java +++ /dev/null @@ -1,55 +0,0 @@ -package ms.system.mysql; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents an SQL table. - * @author Emperor - * - */ -public final class SQLTable { - - /** - * The columns. - */ - private final SQLColumn[] columns; - - /** - * Constructs a new {@code SQLTable} {@code Object}. - * @param columns The columns. - */ - public SQLTable(SQLColumn...columns) { - this.columns = columns; - } - - /** - * Gets the column for the given name. - * @param name The column name. - * @return The column. - */ - public SQLColumn getColumn(String name) { - for (SQLColumn column : columns) { - if (column.getName().equals(name)) { - return column; - } - } - return null; - } - - /** - * Gets the changed columns. - * @return The columns. - */ - public List getChanged() { - List updated = new ArrayList<>(20); - for (int i = 0; i < columns.length; i++) { - SQLColumn column = columns[i]; - if (column.isChanged()) { - updated.add(column); - } - } - return updated; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/mysql/WorldListSQLHandler.java b/Management-Server/src/main/java/ms/system/mysql/WorldListSQLHandler.java deleted file mode 100644 index cf5eb4674..000000000 --- a/Management-Server/src/main/java/ms/system/mysql/WorldListSQLHandler.java +++ /dev/null @@ -1,84 +0,0 @@ -package ms.system.mysql; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import ms.world.GameServer; -import ms.world.info.WorldInfo; - -/** - * Handles the world list SQL database. - * @author Emperor - * - */ -public final class WorldListSQLHandler extends SQLEntryHandler { - - /** - * The table for this sql entry. - */ - private static final String TABLE = "worlds"; - - /** - * Constructs a new {@code WorldListSQLHandler} {@code Object}. - * @param entry The game server entry. - */ - public WorldListSQLHandler(GameServer entry) { - super(entry, TABLE, "world", "" + entry.getInfo().getWorldId()); - } - - /** - * Clears the world list. - */ - public static void clearWorldList() { - Connection connection = SQLManager.getConnection(); - if(connection == null) return; - try { - PreparedStatement statement = connection.prepareStatement("DELETE FROM " + TABLE); - statement.executeUpdate(); - } catch (Exception e) { - e.printStackTrace(); - SQLManager.close(connection); - } finally { - SQLManager.close(connection); - } - } - - @Override - public void parse() throws SQLException { - - } - - @Override - public void create() throws SQLException { - PreparedStatement statement = connection.prepareStatement("INSERT "+ table + "(world, ip, players, country, member, revision) VALUES('" + value + "', '" + entry.getInfo().getAddress() + "', '" + entry.getPlayerAmount() + "', '" + entry.getInfo().getCountry().getId() + "', '" + (entry.getInfo().isMembers() ? 1 : 0) + "', '" + entry.getInfo().getRevision() + "')"); - statement.executeUpdate(); - SQLManager.close(statement.getConnection()); - } - - @Override - public void save() throws SQLException { - super.read(); - if (result == null || !result.next()) { - create(); - return; - } - int players = entry.getPlayerAmount(); - WorldInfo info = entry.getInfo(); - if (!entry.isActive()) { - players = -1; - } - if (players <= 0) { - PreparedStatement statement = connection.prepareStatement("UPDATE members SET online='0' WHERE lastWorld='" + value + "'"); - statement.executeUpdate(); - } - PreparedStatement statement = connection.prepareStatement("UPDATE " + table + " SET players='" + players + "', ip='" + info.getAddress() + "', country='" + info.getCountry().getId() + "', member='" + (info.isMembers() ? 1 : 0) + "', revision='" + info.getRevision() + "' WHERE world='" + value + "'"); - statement.executeUpdate(); - SQLManager.close(statement.getConnection()); - } - - @Override - public Connection getConnection() { - return SQLManager.getConnection(); - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/util/ByteBufferUtils.java b/Management-Server/src/main/java/ms/system/util/ByteBufferUtils.java deleted file mode 100644 index e60e3b2eb..000000000 --- a/Management-Server/src/main/java/ms/system/util/ByteBufferUtils.java +++ /dev/null @@ -1,154 +0,0 @@ -package ms.system.util; - -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 getTriByte(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; - } - - /** - * Constructs a new {@code ByteBufferUtils} {@code Object}. - */ - private ByteBufferUtils() { - /* - * empty. - */ - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/util/Command.java b/Management-Server/src/main/java/ms/system/util/Command.java deleted file mode 100644 index fbfe0da09..000000000 --- a/Management-Server/src/main/java/ms/system/util/Command.java +++ /dev/null @@ -1,52 +0,0 @@ -package ms.system.util; - -/** - * Represents a command. - * @author Emperor - * - */ -public abstract class Command { - - /** - * The command name. - */ - private final String name; - - /** - * The command info. - */ - private final String info; - - /** - * Constructs a new {@code Command} {@code Object}. - * @param name The command name. - * @param info The command info. - */ - public Command(String name, String info) { - this.name = name; - this.info = info; - } - - /** - * Runs the command. - * @param args The arguments. - */ - public abstract void run(String...args); - - /** - * Gets the name value. - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Gets the info value. - * @return The info. - */ - public String getInfo() { - return info; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/util/EncryptionManager.java b/Management-Server/src/main/java/ms/system/util/EncryptionManager.java deleted file mode 100644 index f5ee249c0..000000000 --- a/Management-Server/src/main/java/ms/system/util/EncryptionManager.java +++ /dev/null @@ -1,815 +0,0 @@ -package ms.system.util; - -import java.io.UnsupportedEncodingException; -import java.security.SecureRandom; - -/** - * Represents the class of simple encryption functions. - * @author Unknown - */ -public class EncryptionManager { - - // Define the BCrypt workload to use when generating password hashes. 10-31 is a valid value. - private static int workload = 12; - - /** - * Singleton instance. - */ - private static final EncryptionManager INSTANCE = new EncryptionManager(); - - /** - * Constructs a new {@code EncryptionManager} {@code Object}. - */ - private EncryptionManager() { - //empty. - } - - /** - * This method can be used to generate a string representing an account password - * suitable for storing in a database. It will be an OpenBSD-style crypt(3) formatted - * hash string of length=60 - * The bcrypt workload is specified in the above static variable, a value from 10 to 31. - * A workload of 12 is a very reasonable safe default as of 2013. - * This automatically handles secure 128-bit salt generation and storage within the hash. - * @param password_plaintext The account's plaintext password as provided during account creation, - * or when changing an account's password. - * @return String - a string of length 60 that is the bcrypt hashed password in crypt(3) format. - */ - public String hashPassword(String password_plaintext) { - String salt = BCrypt.gensalt(workload); - String hashed_password = BCrypt.hashpw(password_plaintext, salt); - return(hashed_password); - } - - /** - * This method can be used to verify a computed hash from a plaintext (e.g. during a login - * request) with that of a stored hash from a database. The password hash from the database - * must be passed as the second variable. - * @param password_plaintext The account's plaintext password, as provided during a login request - * @param stored_hash The account's stored password hash, retrieved from the authorization database - * @return boolean - true if the password matches the password of the stored hash, false otherwise - */ - public boolean checkPassword(String password_plaintext, String stored_hash) { - boolean password_verified = false; - if(null == stored_hash || !stored_hash.startsWith("$2a$")) - throw new java.lang.IllegalArgumentException("Invalid hash provided for comparison"); - password_verified = BCrypt.checkpw(password_plaintext, stored_hash); - return(password_verified); - } - - // Copyright (c) 2006 Damien Miller - // - // Permission to use, copy, modify, and distribute this software for any - // purpose with or without fee is hereby granted, provided that the above - // copyright notice and this permission notice appear in all copies. - // - // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - /** - * Gets the instance value. - * @return The instance. - */ - public static EncryptionManager getInstance() { - return INSTANCE; - } - - /** - * BCrypt implements OpenBSD-style Blowfish password hashing using - * the scheme described in "A Future-Adaptable Password Scheme" by - * Niels Provos and David Mazieres. - *

- * This password hashing system tries to thwart off-line password - * cracking using a computationally-intensive hashing algorithm, - * based on Bruce Schneier's Blowfish cipher. The work factor of - * the algorithm is parameterised, so it can be increased as - * computers get faster. - *

- * Usage is really simple. To hash a password for the first time, - * call the hashpw method with a random salt, like this: - *

- * - * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
- *
- *

- * 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 = BCrypt.gensalt(12)
- *
- *

- * The amount of work increases exponentially (2**log_rounds), so - * each increment is twice as much work. The default log_rounds is - * 10, and the valid range is 4 to 31. - * - * @author Damien Miller - * @version 0.2 - */ - public static class BCrypt { - // BCrypt parameters - private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; - private static final int BCRYPT_SALT_LEN = 16; - - // Blowfish parameters - private static final int BLOWFISH_NUM_ROUNDS = 16; - - // Initial contents of key schedule - private static final int P_orig[] = { - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, - 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, - 0x9216d5d9, 0x8979fb1b - }; - private static final int S_orig[] = { - 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, - 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, - 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, - 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, - 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, - 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, - 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, - 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, - 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, - 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, - 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, - 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, - 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, - 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, - 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, - 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, - 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, - 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, - 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, - 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, - 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, - 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, - 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, - 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, - 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, - 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, - 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, - 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, - 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, - 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, - 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, - 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, - 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, - 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, - 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, - 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, - 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, - 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, - 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, - 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, - 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, - 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, - 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, - 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, - 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, - 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, - 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, - 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, - 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, - 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, - 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, - 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, - 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, - 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, - 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, - 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, - 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, - 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, - 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, - 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, - 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, - 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, - 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, - 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, - 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, - 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, - 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, - 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, - 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, - 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, - 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, - 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, - 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, - 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, - 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, - 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, - 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, - 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, - 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, - 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, - 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, - 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, - 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, - 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, - 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, - 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, - 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, - 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, - 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, - 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, - 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, - 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, - 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, - 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, - 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, - 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, - 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, - 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, - 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, - 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, - 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, - 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, - 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, - 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, - 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, - 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, - 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, - 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, - 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, - 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, - 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, - 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, - 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, - 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, - 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, - 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, - 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, - 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, - 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, - 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, - 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, - 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, - 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, - 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, - 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, - 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, - 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, - 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, - 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, - 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, - 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, - 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, - 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, - 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, - 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, - 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, - 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, - 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, - 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, - 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, - 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, - 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, - 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, - 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, - 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, - 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, - 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, - 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, - 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, - 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, - 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, - 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, - 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, - 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, - 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, - 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, - 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, - 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, - 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, - 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, - 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, - 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, - 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, - 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, - 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, - 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, - 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, - 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, - 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, - 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, - 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, - 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, - 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, - 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, - 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, - 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, - 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, - 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, - 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, - 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, - 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, - 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, - 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, - 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, - 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, - 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, - 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, - 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, - 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, - 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, - 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, - 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, - 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, - 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, - 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, - 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, - 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, - 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, - 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, - 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, - 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, - 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, - 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, - 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, - 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, - 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, - 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, - 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, - 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, - 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, - 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, - 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, - 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, - 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, - 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, - 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 - }; - - // bcrypt IV: "OrpheanBeholderScryDoubt" - static private final int bf_crypt_ciphertext[] = { - 0x4f727068, 0x65616e42, 0x65686f6c, - 0x64657253, 0x63727944, 0x6f756274 - }; - - // Table for Base64 encoding - static private final char base64_code[] = { - '.', '/', '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', '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', '0', '1', '2', '3', '4', '5', - '6', '7', '8', '9' - }; - - // Table for Base64 decoding - static private final byte index_64[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, - -1, -1, -1, -1, -1, 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, - -1, -1, -1, -1, -1, -1, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, -1, -1, -1, -1, -1 - }; - - // Expanded Blowfish key - private int P[]; - private int S[]; - - /** - * Encode a byte array using bcrypt's slightly-modified base64 - * encoding scheme. Note that this is *not* compatible with - * the standard MIME-base64 encoding. - * - * @param d the byte array to encode - * @param len the number of bytes to encode - * @return base64-encoded string - * @exception IllegalArgumentException if the length is invalid - */ - private static String encode_base64(byte d[], int len) - throws IllegalArgumentException { - int off = 0; - StringBuffer rs = new StringBuffer(); - int c1, c2; - - if (len <= 0 || len > d.length) - throw new IllegalArgumentException ("Invalid len"); - - while (off < len) { - c1 = d[off++] & 0xff; - rs.append(base64_code[(c1 >> 2) & 0x3f]); - c1 = (c1 & 0x03) << 4; - if (off >= len) { - rs.append(base64_code[c1 & 0x3f]); - break; - } - c2 = d[off++] & 0xff; - c1 |= (c2 >> 4) & 0x0f; - rs.append(base64_code[c1 & 0x3f]); - c1 = (c2 & 0x0f) << 2; - if (off >= len) { - rs.append(base64_code[c1 & 0x3f]); - break; - } - c2 = d[off++] & 0xff; - c1 |= (c2 >> 6) & 0x03; - rs.append(base64_code[c1 & 0x3f]); - rs.append(base64_code[c2 & 0x3f]); - } - return rs.toString(); - } - - /** - * Look up the 3 bits base64-encoded by the specified character, - * range-checking againt conversion table - * @param x the base64-encoded value - * @return the decoded value of x - */ - private static byte char64(char x) { - if ((int)x > index_64.length) - return -1; - return index_64[(int)x]; - } - - /** - * Decode a string encoded using bcrypt's base64 scheme to a - * byte array. Note that this is *not* compatible with - * the standard MIME-base64 encoding. - * @param s the string to decode - * @param maxolen the maximum number of bytes to decode - * @return an array containing the decoded bytes - * @throws IllegalArgumentException if maxolen is invalid - */ - private static byte[] decode_base64(String s, int maxolen) - throws IllegalArgumentException { - StringBuffer rs = new StringBuffer(); - int off = 0, slen = s.length(), olen = 0; - byte ret[]; - byte c1, c2, c3, c4, o; - - if (maxolen <= 0) - throw new IllegalArgumentException ("Invalid maxolen"); - - while (off < slen - 1 && olen < maxolen) { - c1 = char64(s.charAt(off++)); - c2 = char64(s.charAt(off++)); - if (c1 == -1 || c2 == -1) - break; - o = (byte)(c1 << 2); - o |= (c2 & 0x30) >> 4; - rs.append((char)o); - if (++olen >= maxolen || off >= slen) - break; - c3 = char64(s.charAt(off++)); - if (c3 == -1) - break; - o = (byte)((c2 & 0x0f) << 4); - o |= (c3 & 0x3c) >> 2; - rs.append((char)o); - if (++olen >= maxolen || off >= slen) - break; - c4 = char64(s.charAt(off++)); - o = (byte)((c3 & 0x03) << 6); - o |= c4; - rs.append((char)o); - ++olen; - } - - ret = new byte[olen]; - for (off = 0; off < olen; off++) - ret[off] = (byte)rs.charAt(off); - return ret; - } - - /** - * Blowfish encipher a single 64-bit block encoded as - * two 32-bit halves - * @param lr an array containing the two 32-bit half blocks - * @param off the position in the array of the blocks - */ - private final void encipher(int lr[], int off) { - int i, n, l = lr[off], r = lr[off + 1]; - - l ^= P[0]; - for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { - // Feistel substitution on left word - n = S[(l >> 24) & 0xff]; - n += S[0x100 | ((l >> 16) & 0xff)]; - n ^= S[0x200 | ((l >> 8) & 0xff)]; - n += S[0x300 | (l & 0xff)]; - r ^= n ^ P[++i]; - - // Feistel substitution on right word - n = S[(r >> 24) & 0xff]; - n += S[0x100 | ((r >> 16) & 0xff)]; - n ^= S[0x200 | ((r >> 8) & 0xff)]; - n += S[0x300 | (r & 0xff)]; - l ^= n ^ P[++i]; - } - lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; - lr[off + 1] = l; - } - - /** - * Cycically extract a word of key material - * @param data the string to extract the data from - * @param offp a "pointer" (as a one-entry array) to the - * current offset into data - * @return the next word of material from data - */ - private static int streamtoword(byte data[], int offp[]) { - int i; - int word = 0; - int off = offp[0]; - - for (i = 0; i < 4; i++) { - word = (word << 8) | (data[off] & 0xff); - off = (off + 1) % data.length; - } - - offp[0] = off; - return word; - } - - /** - * Initialise the Blowfish key schedule - */ - private void init_key() { - P = (int[])P_orig.clone(); - S = (int[])S_orig.clone(); - } - - /** - * Key the Blowfish cipher - * @param key an array containing the key - */ - private void key(byte key[]) { - int i; - int koffp[] = { 0 }; - int lr[] = { 0, 0 }; - int plen = P.length, slen = S.length; - - for (i = 0; i < plen; i++) - P[i] = P[i] ^ streamtoword(key, koffp); - - for (i = 0; i < plen; i += 2) { - encipher(lr, 0); - P[i] = lr[0]; - P[i + 1] = lr[1]; - } - - for (i = 0; i < slen; i += 2) { - encipher(lr, 0); - S[i] = lr[0]; - S[i + 1] = lr[1]; - } - } - - /** - * Perform the "enhanced key schedule" step described by - * Provos and Mazieres in "A Future-Adaptable Password Scheme" - * http://www.openbsd.org/papers/bcrypt-paper.ps - * @param data salt information - * @param key password information - */ - private void ekskey(byte data[], byte key[]) { - int i; - int koffp[] = { 0 }, doffp[] = { 0 }; - int lr[] = { 0, 0 }; - int plen = P.length, slen = S.length; - - for (i = 0; i < plen; i++) - P[i] = P[i] ^ streamtoword(key, koffp); - - for (i = 0; i < plen; i += 2) { - lr[0] ^= streamtoword(data, doffp); - lr[1] ^= streamtoword(data, doffp); - encipher(lr, 0); - P[i] = lr[0]; - P[i + 1] = lr[1]; - } - - for (i = 0; i < slen; i += 2) { - lr[0] ^= streamtoword(data, doffp); - lr[1] ^= streamtoword(data, doffp); - encipher(lr, 0); - S[i] = lr[0]; - S[i + 1] = lr[1]; - } - } - - /** - * Perform the central password hashing step in the - * bcrypt scheme - * @param password the password to hash - * @param salt the binary salt to hash with the password - * @param log_rounds the binary logarithm of the number - * of rounds of hashing to apply - * @return an array containing the binary hashed password - */ - private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) { - int rounds, i, j; - int cdata[] = (int[])bf_crypt_ciphertext.clone(); - int clen = cdata.length; - byte ret[]; - - if (log_rounds < 4 || log_rounds > 31) - throw new IllegalArgumentException ("Bad number of rounds"); - rounds = 1 << log_rounds; - if (salt.length != BCRYPT_SALT_LEN) - throw new IllegalArgumentException ("Bad salt length"); - - init_key(); - ekskey(salt, password); - for (i = 0; i < rounds; i++) { - key(password); - key(salt); - } - - for (i = 0; i < 64; i++) { - for (j = 0; j < (clen >> 1); j++) - encipher(cdata, j << 1); - } - - ret = new byte[clen * 4]; - for (i = 0, j = 0; i < clen; i++) { - ret[j++] = (byte)((cdata[i] >> 24) & 0xff); - ret[j++] = (byte)((cdata[i] >> 16) & 0xff); - ret[j++] = (byte)((cdata[i] >> 8) & 0xff); - ret[j++] = (byte)(cdata[i] & 0xff); - } - return ret; - } - - /** - * Hash a password using the OpenBSD bcrypt scheme - * @param password the password to hash - * @param salt the salt to hash with (perhaps generated - * using BCrypt.gensalt) - * @return the hashed password - */ - public static String hashpw(String password, String salt) { - BCrypt B; - String real_salt; - byte passwordb[], saltb[], hashed[]; - char minor = (char)0; - int rounds, off = 0; - StringBuffer rs = new StringBuffer(); - - if (salt.charAt(0) != '$' || salt.charAt(1) != '2') - throw new IllegalArgumentException ("Invalid salt version"); - if (salt.charAt(2) == '$') - off = 3; - else { - minor = salt.charAt(2); - if (minor != 'a' || salt.charAt(3) != '$') - throw new IllegalArgumentException ("Invalid salt revision"); - off = 4; - } - - // Extract number of rounds - if (salt.charAt(off + 2) > '$') - throw new IllegalArgumentException ("Missing salt rounds"); - rounds = Integer.parseInt(salt.substring(off, off + 2)); - - real_salt = salt.substring(off + 3, off + 25); - try { - passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8"); - } catch (UnsupportedEncodingException uee) { - throw new AssertionError("UTF-8 is not supported"); - } - - saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); - - B = new BCrypt(); - hashed = B.crypt_raw(passwordb, saltb, rounds); - - rs.append("$2"); - if (minor >= 'a') - rs.append(minor); - rs.append("$"); - if (rounds < 10) - rs.append("0"); - rs.append(Integer.toString(rounds)); - rs.append("$"); - rs.append(encode_base64(saltb, saltb.length)); - rs.append(encode_base64(hashed, - bf_crypt_ciphertext.length * 4 - 1)); - return rs.toString(); - } - - /** - * Generate a salt for use with the BCrypt.hashpw() method - * @param log_rounds the log2 of the number of rounds of - * hashing to apply - the work factor therefore increases as - * 2**log_rounds. - * @param random an instance of SecureRandom to use - * @return an encoded salt value - */ - public static String gensalt(int log_rounds, SecureRandom random) { - StringBuffer rs = new StringBuffer(); - byte rnd[] = new byte[BCRYPT_SALT_LEN]; - - random.nextBytes(rnd); - - rs.append("$2a$"); - if (log_rounds < 10) - rs.append("0"); - rs.append(Integer.toString(log_rounds)); - rs.append("$"); - rs.append(encode_base64(rnd, rnd.length)); - return rs.toString(); - } - - /** - * Generate a salt for use with the BCrypt.hashpw() method - * @param log_rounds the log2 of the number of rounds of - * hashing to apply - the work factor therefore increases as - * 2**log_rounds. - * @return an encoded salt value - */ - public static String gensalt(int log_rounds) { - return gensalt(log_rounds, new SecureRandom()); - } - - /** - * Generate a salt for use with the BCrypt.hashpw() method, - * selecting a reasonable default for the number of hashing - * rounds to apply - * @return an encoded salt value - */ - public static String gensalt() { - return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS); - } - - /** - * Check that a plaintext password matches a previously hashed - * one - * @param plaintext the plaintext password to verify - * @param hashed the previously-hashed password - * @return true if the passwords match, false otherwise - */ - public static boolean checkpw(String plaintext, String hashed) { - return (hashed.compareTo(hashpw(plaintext, hashed)) == 0); - } - } - -} diff --git a/Management-Server/src/main/java/ms/system/util/ManagementConfigParser.kt b/Management-Server/src/main/java/ms/system/util/ManagementConfigParser.kt deleted file mode 100644 index 7aba7f237..000000000 --- a/Management-Server/src/main/java/ms/system/util/ManagementConfigParser.kt +++ /dev/null @@ -1,71 +0,0 @@ -package ms.system.util - -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import java.io.File -import java.io.FileReader -import kotlin.system.exitProcess - -/** - * Class for parsing the server config, I.E default.json - * @param path the path to the JSON file to parse. - * @author Ceikry - */ -class ManagementConfigParser(path: String) { - val pathTo = parsePath(path) - val confFile = File(pathTo) - val parser = JSONParser() - var reader: FileReader? = null - var data: JSONObject? = null - - init { - if(!confFile.canonicalFile.exists()){ - println("Could not find ${confFile.canonicalFile} - Double check your working directory!") - exitProcess(0) - } else if(!pathTo.contains(".json")) { - println("Config file MUST be a JSON file!!") - println("(Got $pathTo)") - } else { - reader = FileReader(pathTo) - data = parser.parse(reader) as JSONObject - parseDatabaseInformation() - parseWorldTechnicalSettings() - ManagementConstants.SECRET_KEY = data!!["secret_key"].toString() - } - } - - private fun parseDatabaseInformation(){ - data ?: return - val dbData = data!!["DatabaseInformation"] as JSONObject - ManagementConstants.parseDBProp(dbData) - } - - private fun parseWorldTechnicalSettings(){ - data ?: return - val wtiData = data!!["WorldTechnicalInformation"] as JSONObject - ManagementConstants.parseWTIProp(wtiData) - } - - /** - * Parses a path string - * @author Ceikry - * @param pathString The string to parse - * @return a String with the proper file separators for the current OS. - */ - fun parsePath(pathString: String): String { - var pathTokens: List? = null - if(pathString.contains("/")) - pathTokens = pathString.split("/") - else if(pathString.contains("\\")) - pathTokens = pathString.split("\\") - - pathTokens ?: return pathString //return the initial pathString if path does not contain file separators. - var pathProduct = "" - for(token in pathTokens){ - if(token != "") - pathProduct += "$token${File.separator}" - } - - return pathProduct - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/util/ManagementConstants.kt b/Management-Server/src/main/java/ms/system/util/ManagementConstants.kt deleted file mode 100644 index 01d40fb5a..000000000 --- a/Management-Server/src/main/java/ms/system/util/ManagementConstants.kt +++ /dev/null @@ -1,44 +0,0 @@ -package ms.system.util - -import org.json.simple.JSONObject - -object ManagementConstants { - - //MySQL main database name - var DATABASE_NAME: String = "global" - - //MySQL database username - var DATABASE_USERNAME: String = "root" - - //MySQL database password - var DATABASE_PASSWORD: String = "" - - //MySQL host - var DATABASE_HOST_ADDRESS: String = "127.0.0.1" - - //MySQL port - var DATABASE_PORT: Int = 3306 - - //Max amount of worlds supported on the world list - var MAX_WORLD_AMOUNT: Int = 10 - - //User world hop delay in seconds - var WORLD_HOP_DELAY: Long = 20_000L - - @JvmStatic - var SECRET_KEY: String = "" - - fun parseDBProp(data: JSONObject) { - DATABASE_NAME = data["database_name"].toString() - DATABASE_USERNAME = data["database_username"].toString() - DATABASE_PASSWORD = data["database_password"].toString() - DATABASE_HOST_ADDRESS = data["database_host"].toString() - DATABASE_PORT = data["database_port"].toString().toInt() - } - - fun parseWTIProp(data: JSONObject) { - MAX_WORLD_AMOUNT = data["world_limit"].toString().toInt() - WORLD_HOP_DELAY = data["worldhop_delay"].toString().toLong() - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/util/PlayerDatabaseGen.java b/Management-Server/src/main/java/ms/system/util/PlayerDatabaseGen.java deleted file mode 100644 index e4187f7ee..000000000 --- a/Management-Server/src/main/java/ms/system/util/PlayerDatabaseGen.java +++ /dev/null @@ -1,19 +0,0 @@ -package ms.system.util; - -/** - * Handles generating a player database. - * @author Emperor - * - */ -public final class PlayerDatabaseGen { - - /** - * The main method. - * @param args The arguments. - */ - public static void main(String[] args) { - // TODO Auto-generated method stub - - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/system/util/StringUtils.java b/Management-Server/src/main/java/ms/system/util/StringUtils.java deleted file mode 100644 index 2a85aea8f..000000000 --- a/Management-Server/src/main/java/ms/system/util/StringUtils.java +++ /dev/null @@ -1,640 +0,0 @@ -package ms.system.util; - -import java.text.DecimalFormat; - -import ms.net.packet.IoBuffer; - -/** - * The string utils. - * @author Emperor - * - */ -public final class StringUtils { - - /** - * The valid characters to be used in names/messages/... - */ - private static final char[] VALID_CHARS = { - '_', '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', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' - }; - /** - * The an int array241. - */ - private final static int[] anIntArray241 = { 215, 203, 83, 158, 104, 101, 93, 84, - 107, 103, 109, 95, 94, 98, 89, 86, 70, 41, 32, 27, 24, 23, -1, -2, - 26, -3, -4, 31, 30, -5, -6, -7, 37, 38, 36, -8, -9, -10, 40, -11, - -12, 55, 48, 46, 47, -13, -14, -15, 52, 51, -16, -17, 54, -18, -19, - 63, 60, 59, -20, -21, 62, -22, -23, 67, 66, -24, -25, 69, -26, -27, - 199, 132, 80, 77, 76, -28, -29, 79, -30, -31, 87, 85, -32, -33, - -34, -35, -36, 197, -37, 91, -38, 134, -39, -40, -41, 97, -42, -43, - 133, 106, -44, 117, -45, -46, 139, -47, -48, 110, -49, -50, 114, - 113, -51, -52, 116, -53, -54, 135, 138, 136, 129, 125, 124, -55, - -56, 130, 128, -57, -58, -59, 183, -60, -61, -62, -63, -64, 148, - -65, -66, 153, 149, 145, 144, -67, -68, 147, -69, -70, -71, 152, - 154, -72, -73, -74, 157, 171, -75, -76, 207, 184, 174, 167, 166, - 165, -77, -78, -79, 172, 170, -80, -81, -82, 178, -83, 177, 182, - -84, -85, 187, 181, -86, -87, -88, -89, 206, 221, -90, 189, -91, - 198, 254, 262, 195, 196, -92, -93, -94, -95, -96, 252, 255, 250, - -97, 211, 209, -98, -99, 212, -100, 213, -101, -102, -103, 224, - -104, 232, 227, 220, 226, -105, -106, 246, 236, -107, 243, -108, - -109, 231, 237, 235, -110, -111, 239, 238, -112, -113, -114, -115, - -116, 241, -117, 244, -118, -119, 248, -120, 249, -121, -122, -123, - 253, -124, -125, -126, -127, 259, 258, -128, -129, 261, -130, -131, - 390, 327, 296, 281, 274, 271, 270, -132, -133, 273, -134, -135, - 278, 277, -136, -137, 280, -138, -139, 289, 286, 285, -140, -141, - 288, -142, -143, 293, 292, -144, -145, 295, -146, -147, 312, 305, - 302, 301, -148, -149, 304, -150, -151, 309, 308, -152, -153, 311, - -154, -155, 320, 317, 316, -156, -157, 319, -158, -159, 324, 323, - -160, -161, 326, -162, -163, 359, 344, 337, 334, 333, -164, -165, - 336, -166, -167, 341, 340, -168, -169, 343, -170, -171, 352, 349, - 348, -172, -173, 351, -174, -175, 356, 355, -176, -177, 358, -178, - -179, 375, 368, 365, 364, -180, -181, 367, -182, -183, 372, 371, - -184, -185, 374, -186, -187, 383, 380, 379, -188, -189, 382, -190, - -191, 387, 386, -192, -193, 389, -194, -195, 454, 423, 408, 401, - 398, 397, -196, -197, 400, -198, -199, 405, 404, -200, -201, 407, - -202, -203, 416, 413, 412, -204, -205, 415, -206, -207, 420, 419, - -208, -209, 422, -210, -211, 439, 432, 429, 428, -212, -213, 431, - -214, -215, 436, 435, -216, -217, 438, -218, -219, 447, 444, 443, - -220, -221, 446, -222, -223, 451, 450, -224, -225, 453, -226, -227, - 486, 471, 464, 461, 460, -228, -229, 463, -230, -231, 468, 467, - -232, -233, 470, -234, -235, 479, 476, 475, -236, -237, 478, -238, - -239, 483, 482, -240, -241, 485, -242, -243, 499, 495, 492, 491, - -244, -245, 494, -246, -247, 497, -248, 502, -249, 506, 503, -250, - -251, 505, -252, -253, 508, -254, 510, -255, -256, 0 }; - - /** - * Constructs a new {@code StringUtils.java} {@code Object}. - */ - public StringUtils() { - /** - * empty. - */ - } - /** - * Method used to get the formatted number as a string from the integer inputed. - * @param amount the ammount. - * @return the string value. - */ - public static String getFormattedNumber(int amount) { - return new DecimalFormat("#,###,##0").format(amount).toString(); - } - /** - * Checks if the string contains an invalid character. - * @param nameThe string. - * @return {@code True} if so. - */ - public static boolean containsInvalidCharacter(String name) { - for (char c : name.toCharArray()) { - boolean pass = false; - for (char vc : VALID_CHARS) - if (vc == c) { - pass = true; - break; - } - if (!pass) - return true; - } - return false; - } - /** - * If a word starts with a e i o u h for grammar = a + n. - * @param word The word. - * @return If the a should have +n {@code true}. - */ - public static boolean isPlusN(String word) { - if (word == null) - return false; - String s = word.toLowerCase(); - return s.charAt(0) == 'a' || s.charAt(0) == 'e' || s.charAt(0) == 'i' - || s.charAt(0) == 'o' || s.charAt(0) == 'u' - || (s.charAt(0) == 'h' && s.length() > 1 && s.charAt(1) != 'e'); - } - /** - * Method used to get the player name as a long. - * @param s the string. - * @return the long. - */ - public static long getPlayerNameLong(String s) { - long l = 0L; - for(int i = 0; i < s.length() && i < 12; i++) { - char c = s.charAt(i); - l *= 37L; - if(c >= 'A' && c <= 'Z') l += (1 + c) - 65; - else if(c >= 'a' && c <= 'z') l += (1 + c) - 97; - else if(c >= '0' && c <= '9') l += (27 + c) - 48; - } - while(l % 37L == 0L && l != 0L) l /= 37L; - return l; - } - - /** - * Method used to convert the string to a long. - * @param s the string. - * @return the long. - */ - public static long convertStringToLong(String s) { - if (s.length() > 20) { - throw new IllegalArgumentException("String is too long: " + s); - } - long out = 0L; - for (int i = 0; i < s.length(); ++i) { - long m = reducedMapping(s.codePointAt(i)); - if (m == -1) { - throw new IllegalArgumentException("Unmapped Character in String: " + s); - } - m <<= ((9 - i) * 6) + 4; - out |= m; - } - return out; - } - - /** - * Formats the string as display name. - * @param name The string to format. - * @return The formatted name. - */ - public static String formatDisplayName(String name) { - name = name.replaceAll("_", " "); - name = name.toLowerCase(); - StringBuilder newName = new StringBuilder(); - boolean wasSpace = true; - for (int i = 0; i < name.length(); i++) { - if (wasSpace) { - newName.append(("" + name.charAt(i)).toUpperCase()); - wasSpace = false; - } else { - newName.append(name.charAt(i)); - } - if (name.charAt(i) == ' ') { - wasSpace = true; - } - } - return newName.toString(); - } - - /** - * Gets the byte for the character. - * @param c The character. - * @return The byte. - */ - private static final byte getByte(char c) { - byte charByte; - if (c > 0 && c < '\200' || c >= '\240' && c <= '\377') - charByte = (byte) c; - else if (c != '\u20AC') { - if (c != '\u201A') { - if (c != '\u0192') { - if (c == '\u201E') - charByte = -124; - else if (c != '\u2026') { - if (c != '\u2020') { - if (c == '\u2021') - charByte = -121; - else if (c == '\u02C6') - charByte = -120; - else if (c == '\u2030') - charByte = -119; - else if (c == '\u0160') - charByte = -118; - else if (c == '\u2039') - charByte = -117; - else if (c == '\u0152') - charByte = -116; - else if (c != '\u017D') { - if (c == '\u2018') - charByte = -111; - else if (c != '\u2019') { - if (c != '\u201C') { - if (c == '\u201D') - charByte = -108; - else if (c != '\u2022') { - if (c == '\u2013') - charByte = -106; - else if (c == '\u2014') - charByte = -105; - else if (c == '\u02DC') - charByte = -104; - else if (c == '\u2122') - charByte = -103; - else if (c != '\u0161') { - if (c == '\u203A') - charByte = -101; - else if (c != '\u0153') { - if (c == '\u017E') - charByte = -98; - else if (c != '\u0178') - charByte = 63; - else - charByte = -97; - } else - charByte = -100; - } else - charByte = -102; - } else - charByte = -107; - } else - charByte = -109; - } else - charByte = -110; - } else - charByte = -114; - } else - charByte = -122; - } else - charByte = -123; - } else - charByte = -125; - } else - charByte = -126; - } else - charByte = -128; - return charByte; - } - /** - * Gets the double value of this string - * @param s The string. - * @return The double value. - */ - public static double getDouble(String s) { - s = s.replaceAll(", ", "").replaceAll(",", ""); - StringBuilder sb = new StringBuilder(); - char c; - boolean foundStart = false; - for (int i = 0; i < s.length(); i++) { - c = s.charAt(i); - if (Character.isDigit(c) || c == '-' || c == '.') { - sb.append(c); - foundStart = true; - } else if (foundStart) - break; - } - try { - double amount = Double.parseDouble(sb.toString()); - return amount; - } catch (NumberFormatException e) { - return 0.0; - } - } - /** - * Gets the hash for the string. - * @param str The string. - * @return The hash. - */ - public static final int getNameHash(String str) { - str = str.toLowerCase(); - int hash = 0; - for (int index = 0; index < str.length(); index++) - hash = getByte(str.charAt(index)) + ((hash << 5) - hash); - return hash; - } - /** - * Gets the string value of this string (all html/... removed). - * @param s The string. - * @return The string value. - */ - public static String getString(String s) { - return s.replaceAll("\\<.*?>", "").replaceAll(" ", "") - .replaceAll("Discontinued Item:", ""); - } - /** - * Characters used to convert a String to a Long. - */ - public static char[] validChars = { - '_', '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', '0', '1', '2', - '3', '4', '5', '6', '7', '8', '9' - }; - private final static int[] anIntArray233 = { - 0, 1024, 2048, 3072, 4096, 5120, - 6144, 8192, 9216, 12288, 10240, 11264, 16384, 18432, 17408, 20480, - 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, - 30720, 31744, 32768, 33792, 34816, 35840, 36864, 536870912, - 16777216, 37888, 65536, 38912, 131072, 196608, 33554432, 524288, - 1048576, 1572864, 262144, 67108864, 4194304, 134217728, 327680, - 8388608, 2097152, 12582912, 13631488, 14680064, 15728640, - 100663296, 101187584, 101711872, 101974016, 102760448, 102236160, - 40960, 393216, 229376, 117440512, 104857600, 109051904, 201326592, - 205520896, 209715200, 213909504, 106954752, 218103808, 226492416, - 234881024, 222298112, 224395264, 268435456, 272629760, 276824064, - 285212672, 289406976, 223346688, 293601280, 301989888, 318767104, - 297795584, 298844160, 310378496, 102498304, 335544320, 299892736, - 300941312, 301006848, 300974080, 39936, 301465600, 49152, - 1073741824, 369098752, 402653184, 1342177280, 1610612736, - 469762048, 1476395008, -2147483648, -1879048192, 352321536, - 1543503872, -2013265920, -1610612736, -1342177280, -1073741824, - -1543503872, 356515840, -1476395008, -805306368, -536870912, - -268435456, 1577058304, -134217728, 360710144, -67108864, - 364904448, 51200, 57344, 52224, 301203456, 53248, 54272, 55296, - 56320, 301072384, 301073408, 301074432, 301075456, 301076480, - 301077504, 301078528, 301079552, 301080576, 301081600, 301082624, - 301083648, 301084672, 301085696, 301086720, 301087744, 301088768, - 301089792, 301090816, 301091840, 301092864, 301093888, 301094912, - 301095936, 301096960, 301097984, 301099008, 301100032, 301101056, - 301102080, 301103104, 301104128, 301105152, 301106176, 301107200, - 301108224, 301109248, 301110272, 301111296, 301112320, 301113344, - 301114368, 301115392, 301116416, 301117440, 301118464, 301119488, - 301120512, 301121536, 301122560, 301123584, 301124608, 301125632, - 301126656, 301127680, 301128704, 301129728, 301130752, 301131776, - 301132800, 301133824, 301134848, 301135872, 301136896, 301137920, - 301138944, 301139968, 301140992, 301142016, 301143040, 301144064, - 301145088, 301146112, 301147136, 301148160, 301149184, 301150208, - 301151232, 301152256, 301153280, 301154304, 301155328, 301156352, - 301157376, 301158400, 301159424, 301160448, 301161472, 301162496, - 301163520, 301164544, 301165568, 301166592, 301167616, 301168640, - 301169664, 301170688, 301171712, 301172736, 301173760, 301174784, - 301175808, 301176832, 301177856, 301178880, 301179904, 301180928, - 301181952, 301182976, 301184000, 301185024, 301186048, 301187072, - 301188096, 301189120, 301190144, 301191168, 301193216, 301195264, - 301194240, 301197312, 301198336, 301199360, 301201408, 301202432 - }; - private final static byte[] aByteArray235 = { - 22, 22, 22, 22, 22, 22, 21, 22, 22, - 20, 22, 22, 22, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 3, 8, 22, 16, 22, 16, 17, 7, 13, 13, 13, - 16, 7, 10, 6, 16, 10, 11, 12, 12, 12, 12, 13, 13, 14, 14, 11, 14, - 19, 15, 17, 8, 11, 9, 10, 10, 10, 10, 11, 10, 9, 7, 12, 11, 10, 10, - 9, 10, 10, 12, 10, 9, 8, 12, 12, 9, 14, 8, 12, 17, 16, 17, 22, 13, - 21, 4, 7, 6, 5, 3, 6, 6, 5, 4, 10, 7, 5, 6, 4, 4, 6, 10, 5, 4, 4, - 5, 7, 6, 10, 6, 10, 22, 19, 22, 14, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 22, 21, 22, 22, 22, 21, - 22, 22 - }; - /** - * Encrypt a string for the client. - */ - public static int encryptPlayerChat(byte[] is, int i_25_, int i_26_, int i_27_, byte[] is_28_) { - try { - i_27_ += i_25_; - int i_29_ = 0; - int i_30_ = i_26_ << 3; - for (; i_27_ > i_25_; i_25_++) { - int i_31_ = 0xff & is_28_[i_25_]; - int i_32_ = anIntArray233[i_31_]; - int i_33_ = aByteArray235[i_31_]; - int i_34_ = i_30_ >> 3; - int i_35_ = i_30_ & 0x7; - i_29_ &= (-i_35_ >> 31); - i_30_ += i_33_; - int i_36_ = ((-1 + (i_35_ - -i_33_)) >> 3) + i_34_; - i_35_ += 24; - is[i_34_] = (byte) (i_29_ = (i_29_ | (i_32_ >>> i_35_))); - if ((i_36_ ^ 0xffffffff) < (i_34_ ^ 0xffffffff)) { - i_34_++; - i_35_ -= 8; - is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); - if (i_36_ > i_34_) { - i_34_++; - i_35_ -= 8; - is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); - if (i_36_ > i_34_) { - i_35_ -= 8; - i_34_++; - is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); - if (i_34_ < i_36_) { - i_35_ -= 8; - i_34_++; - is[i_34_] = (byte) (i_29_ = i_32_ << -i_35_); - } - } - } - } - } - return -i_26_ + ((7 + i_30_) >> 3); - } catch (RuntimeException runtimeexception) { - runtimeexception.printStackTrace(); - } - return 0; - } - /** - * Gets an integer value from a string. - * @param s the string. - * @return The value; - */ - public static int getValue(String s) { - s = s.replaceAll(", ", "").replaceAll(",", ""); - StringBuilder sb = new StringBuilder(); - char c; - boolean foundStart = false; - for (int i = 0; i < s.length(); i++) { - c = s.charAt(i); - if (Character.isDigit(c) || c == '-') { - sb.append(c); - foundStart = true; - } else if (foundStart) - break; - } - try { - int amount = Integer.parseInt(sb.toString()); - return amount; - } catch (NumberFormatException e) { - return 0; - } - } - /** - * Checks if the account name is valid. - * @param name The account name. - * @return {@code True} if the account name is invalid. - */ - public static boolean invalidAccountName(String name) { - return name.length() < 2 || name.length() > 12 || name.startsWith("_")|| name.endsWith("_") || name.contains("__") || containsInvalidCharacter(name); - } - - /** - * Converts a long to a string. - * @param l The long. - * @return The string. - */ - public static String longToString(long l) { - int i = 0; - char ac[] = new char[32]; - while (l != 0L) { - long l1 = l; - l /= 37L; - ac[11 - i++] = VALID_CHARS[(int) (l1 - l * 37L)]; - } - return new String(ac, 12 - i, i); - } - - /** - * Packs a GJ2-String. - * @param position The position to start. - * @param buffer The byte-array. - * @param str The string. - * @return The size of the string. - */ - public static final int packGJString2(int position, byte[] buffer, - String str) { - int length = str.length(); - int offset = position; - for (int index = 0; length > index; index++) { - int character = str.charAt(index); - 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; - } - /** - * Method used to... - * @param x idk, - * @return the idk. - */ - public static long reducedMapping(int x) { - long out = -1; - if (x >= 97 && x <= 122) - out = x - 96; - else if (x >= 65 && x <= 90) - out = x - 37; - else if (x >= 48 && x <= 57) - out = x - +5; - else if (x == 32) - out = 63L; - return out; - } - /** - * Converts a string to a long. - * @param s The string. - * @return The long. - */ - public static long stringToLong(String s) { - long l = 0L; - for (int i = 0; i < s.length() && i < 12; i++) { - char c = s.charAt(i); - l *= 37L; - if (c >= 'A' && c <= 'Z') - l += (1 + c) - 65; - else if (c >= 'a' && c <= 'z') - l += (1 + c) - 97; - else if (c >= '0' && c <= '9') - l += (27 + c) - 48; - } - while (l % 37L == 0L && l != 0L) - l /= 37L; - return l; - } - /** - * Decrypt player chat. - * - * @param buffer - * The buffer - * @param totalChars - * The total chars - * @return The string - */ - public static String decryptPlayerChat(IoBuffer buffer, int totalChars) { - try { - if (totalChars == 0) - return ""; - int charsDecoded = 0; - int i_4_ = 0; - String s = ""; - for (;;) { - byte i_7_ = (byte) buffer.get(); - if (i_7_ >= 0) - i_4_++; - else - i_4_ = anIntArray241[i_4_]; - int i_8_; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - i_4_ = 0; - } - if (((i_7_ & 0x40) ^ 0xffffffff) != -1) - i_4_ = anIntArray241[i_4_]; - else - i_4_++; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (++charsDecoded >= totalChars) - break; - i_4_ = 0; - } - if ((0x20 & i_7_) == 0) - i_4_++; - else - i_4_ = anIntArray241[i_4_]; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - i_4_ = 0; - } - if (((0x10 & i_7_) ^ 0xffffffff) == -1) - i_4_++; - else - i_4_ = anIntArray241[i_4_]; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - - i_4_ = 0; - } - if (((0x8 & i_7_) ^ 0xffffffff) != -1) - i_4_ = anIntArray241[i_4_]; - else - i_4_++; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (++charsDecoded >= totalChars) - break; - i_4_ = 0; - } - if ((0x4 & i_7_) == 0) - i_4_++; - else - i_4_ = anIntArray241[i_4_]; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - i_4_ = 0; - } - if (((i_7_ & 0x2) ^ 0xffffffff) != -1) - i_4_ = anIntArray241[i_4_]; - else - i_4_++; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - i_4_ = 0; - } - if (((i_7_ & 0x1) ^ 0xffffffff) != -1) - i_4_ = anIntArray241[i_4_]; - else - i_4_++; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (++charsDecoded >= totalChars) - break; - i_4_ = 0; - } - } - return s; - } catch (RuntimeException runtimeexception) { - runtimeexception.printStackTrace(); - } - return ""; - } -} diff --git a/Management-Server/src/main/java/ms/system/util/TaskExecutor.java b/Management-Server/src/main/java/ms/system/util/TaskExecutor.java deleted file mode 100644 index e2b3c886b..000000000 --- a/Management-Server/src/main/java/ms/system/util/TaskExecutor.java +++ /dev/null @@ -1,55 +0,0 @@ -package ms.system.util; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; - -/** - * A class holding methods to execute tasks. - * @author Emperor - * - */ -public final class TaskExecutor { - - /** - * The executor to use. - */ - private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); - - /** - * The SQL task executor. - */ - private static final ScheduledExecutorService SQL_EXECUTOR = Executors.newSingleThreadScheduledExecutor(); - - /** - * Constructs a new {@code TaskExecutor} {@code Object}. - */ - private TaskExecutor() { - /* - * empty. - */ - } - - /** - * Executes an SQL handling task. - * @param task The task. - */ - public static void executeSQL(Runnable task) { - SQL_EXECUTOR.execute(task); - } - - /** - * Executes the task. - * @param task The task to execute. - */ - public static void execute(Runnable task) { - EXECUTOR.execute(task); - } - - /** - * Gets the executor. - * @return The executor. - */ - public static ScheduledExecutorService getExecutor() { - return EXECUTOR; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/world/GameServer.java b/Management-Server/src/main/java/ms/world/GameServer.java deleted file mode 100644 index fed3069b7..000000000 --- a/Management-Server/src/main/java/ms/world/GameServer.java +++ /dev/null @@ -1,124 +0,0 @@ -package ms.world; - -import ms.net.IoSession; -import ms.net.packet.WorldPacketRepository; -import ms.system.mysql.SQLEntryHandler; -import ms.system.mysql.WorldListSQLHandler; -import ms.system.util.TaskExecutor; -import ms.world.info.Response; -import ms.world.info.WorldInfo; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -/** - * Represents a game world. - * @author Emperor - * - */ -public final class GameServer { - - /** - * The world info. - */ - private final WorldInfo info; - - /** - * The I/O session. - */ - private IoSession session; - - /** - * The players active on the game server. - */ - private final Map players = new HashMap<>(); - - /** - * The scheduled future for the updating task. - */ - private ScheduledFuture future; - - /** - * constructs a new {@code GameServer} {@code Object}. - * @param info The world info. - */ - public GameServer(WorldInfo info) { - this.info = info; - } - - /** - * Configures the game server. - * @param session The I/O session. - */ - public void configure(IoSession session) { - this.session = session; - session.setGameServer(this); - future = TaskExecutor.getExecutor().scheduleAtFixedRate(() -> { - SQLEntryHandler.write(new WorldListSQLHandler(GameServer.this)); - if (!isActive()) { - future.cancel(false); - future = null; - } - }, 0, 10, TimeUnit.SECONDS); - } - - /** - * Registers a player. - * @param player The player. - */ - public void register(PlayerSession player) { - players.put(player.getUsername(), player); - player.setWorld(this); - player.setActive(true); - player.setWorldId(info.getWorldId()); - player.configure(); - WorldPacketRepository.sendRegistryResponse(this, player, Response.SUCCESSFUL); - player.getCommunication().sync(); - } - - /** - * Checks if the game server is active. - * @return {@code True} if so. - */ - public boolean isActive() { - return session.isActive(); - } - - /** - * @return the playerAmount - */ - public int getPlayerAmount() { - return players.size(); - } - - /** - * @return the info - */ - public WorldInfo getInfo() { - return info; - } - - /** - * @return the session - */ - public IoSession getSession() { - return session; - } - - /** - * @param session the session to set - */ - public void setSession(IoSession session) { - this.session = session; - } - - /** - * Gets the players value. - * @return The players. - */ - public Map getPlayers() { - return players; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/world/PlayerSession.java b/Management-Server/src/main/java/ms/world/PlayerSession.java deleted file mode 100644 index 09c4682d5..000000000 --- a/Management-Server/src/main/java/ms/world/PlayerSession.java +++ /dev/null @@ -1,475 +0,0 @@ -package ms.world; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -import ms.ServerConstants; -import ms.system.communication.ClanRepository; -import ms.system.communication.CommunicationInfo; -import ms.system.mysql.SQLManager; -import ms.world.info.UIDInfo; - -/** - * Represents a player session. - * @author Emperor - * - */ -public final class PlayerSession { - - /** - * The player's communication info. - */ - private CommunicationInfo communication = new CommunicationInfo(this); - - /** - * The game server the player is currently in. - */ - private GameServer world; - - /** - * The current clan. - */ - private ClanRepository clan; - - /** - * The player's UID info. - */ - private UIDInfo uid; - - /** - * The username. - */ - private final String username; - - /** - * The password. - */ - private String password; - - /** - * The player's rights. - */ - private int rights; - - /** - * The world id. - */ - private int worldId; - - /** - * If the player session is active. - */ - private boolean active; - - /** - * The time stamp of last disconnection. - */ - private long disconnectionTime = 0L; - - /** - * How long the player is banned for. - */ - private long banTime; - - /** - * How long the player is muted for. - */ - private long muteTime; - - /** - * The last world the player logged in. - */ - private int lastWorld = -1; - - /** - * The chat icon. - */ - private int chatIcon; - - /** - * Constructs a new {@code PlayerSession} {@code Object}. - * @param username The username. - * @param password The password. - * @param ipAddress The ip address. - * @param macAddress The mac address. - * @param computerName The computer name. - * @param serial The motherboard serial key. - */ - public PlayerSession(String username, String password, UIDInfo uid) { - this.username = username; - this.password = password; - this.uid = uid; - } - - /** - * Parses the session's data from the database. - * @return {@code True} if parsed. - */ - public boolean parse() { - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return false; - } - ResultSet result = null; - PreparedStatement statement; - try { - statement = connection.prepareStatement("SELECT * FROM " + "members" + " WHERE " + "" + "username" + "='" + username.toLowerCase() + "' LIMIT 1"); - result = statement.executeQuery(); - if (result == null || !result.next()) { - SQLManager.close(connection); - return false; - } - rights = result.getInt("rights"); - disconnectionTime = result.getLong("disconnectTime"); - lastWorld = result.getInt("lastWorld"); - banTime = result.getLong("banTime"); - communication.parse(result); - uid.parse(result); - SQLManager.close(connection); - } catch (SQLException ex) { - ex.printStackTrace(); - SQLManager.close(connection); - return false; - } finally { - SQLManager.close(connection); - } - return true; - } - - /** - * Called when a session is removed. - */ - public void remove() { - if (world != null && world.getInfo().getRevision() == 498) { - return; - } - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return; - } - PreparedStatement statement; - try { - statement = connection.prepareStatement("UPDATE members SET disconnectTime = ?, lastWorld = ?, contacts = ?, blocked = ?, clanName = ?, currentClan = ?, clanReqs = ?, chatSettings = ?, online= ? WHERE username = ?"); - statement.setLong(1, System.currentTimeMillis()); - statement.setInt(2, worldId); - communication.save(statement); - statement.setBoolean(9, false); - statement.setString(10, username); - statement.executeUpdate(); - SQLManager.close(connection); - } catch (SQLException e) { - e.printStackTrace(); - SQLManager.close(connection); - } finally { - SQLManager.close(connection); - } - communication.clear(); - } - - /** - * Gets a player session. - * @param username the username. - * @return the player session. - */ - public static PlayerSession get(String username) { - PlayerSession session = new PlayerSession(username, "", new UIDInfo()); - if (!session.parse()) { - return null; - } - return session; - } - - /** - * Configures the player session. - */ - public void configure() { - ClanRepository clan = ClanRepository.getClans().get(username); - if (clan != null) { - clan.setOwner(this); - } - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return; - } - PreparedStatement statement; - try { - statement = connection.prepareStatement("UPDATE members SET online = ?, lastWorld = ?, lastLogin = ? WHERE username = ?"); - statement.setBoolean(1, true); - statement.setInt(2, worldId); - statement.setLong(3, System.currentTimeMillis()); - statement.setString(4, username); - statement.execute(); - SQLManager.close(connection); - } catch (SQLException e) { - e.printStackTrace(); - SQLManager.close(connection); - } finally { - SQLManager.close(connection); - } - } - - /** - * Checks if the player has just moved worlds. - * @return {@code True} if so. - */ - public boolean hasMovedWorld() { - if (rights == 2) { - return false; - } - return System.currentTimeMillis() - disconnectionTime < ServerConstants.WORLD_SWITCH_DELAY; - } - - /** - * Gets the username value. - * @return The username. - */ - public String getUsername() { - return username; - } - - /** - * Gets the password value. - * @return The password. - */ - public String getPassword() { - return password; - } - - /** - * Gets the chat icon. - * @return The chat icon. - */ - public int getChatIcon() { - return chatIcon; - } - - /** - * Gets the ipAddress value. - * @return The ipAddress. - */ - public String getIpAddress() { - return uid.getIp(); - } - - /** - * Gets the macAddress value. - * @return The macAddress. - */ - public String getMacAddress() { - return uid.getMac(); - } - - /** - * Gets the computerName value. - * @return The computerName. - */ - public String getComputerName() { - return uid.getCompName(); - } - - /** - * Gets the worldId value. - * @return The worldId. - */ - public int getWorldId() { - return worldId; - } - - /** - * Sets the worldId value. - * @param worldId The worldId to set. - */ - public void setWorldId(int worldId) { - this.worldId = worldId; - } - - /** - * Gets the active value. - * @return The active. - */ - public boolean isActive() { - return active; - } - - /** - * Sets the active value. - * @param active The active to set. - */ - public void setActive(boolean active) { - this.active = active; - } - - /** - * Gets the serialKey value. - * @return The serialKey. - */ - public String getSerialKey() { - return uid.getSerial(); - } - - /** - * Gets the rights value. - * @return The rights. - */ - public int getRights() { - return rights; - } - - /** - * Sets the rights value. - * @param rights The rights to set. - */ - public void setRights(int rights) { - this.rights = rights; - } - - /** - * If the player is banned. - * @return {@code true} if so. - */ - public boolean isBanned() { - return banTime > System.currentTimeMillis(); - } - - /** - * Gets the banTime value. - * @return The banTime. - */ - public long getBanTime() { - return banTime; - } - - /** - * Gets the muteTime. - * @return the muteTime. - */ - public long getMuteTime() { - return muteTime; - } - - - /** - * Sets the muteTime. - * @param muteTime the muteTime to set - */ - public void setMuteTime(long muteTime) { - this.muteTime = muteTime; - } - - - /** - * Gets the disconnectionTime value. - * @return The disconnectionTime. - */ - public long getDisconnectionTime() { - return disconnectionTime; - } - - /** - * Gets the lastWorld value. - * @return The lastWorld. - */ - public int getLastWorld() { - return lastWorld; - } - - /** - * Sets the lastWorld value. - * @param lastWorld The lastWorld to set. - */ - public void setLastWorld(int lastWorld) { - this.lastWorld = lastWorld; - } - - /** - * Gets the communication value. - * @return The communication. - */ - public CommunicationInfo getCommunication() { - return communication; - } - - /** - * Sets the communication value. - * @param communication The communication to set. - */ - public void setCommunication(CommunicationInfo communication) { - this.communication = communication; - } - - /** - * Gets the world value. - * @return The world. - */ - public GameServer getWorld() { - return world; - } - - /** - * Sets the world value. - * @param world The world to set. - */ - public void setWorld(GameServer world) { - this.world = world; - } - - /** - * Gets the clan value. - * @return The clan. - */ - public ClanRepository getClan() { - return clan; - } - - /** - * Sets the clan value. - * @param clan The clan to set. - */ - public void setClan(ClanRepository clan) { - this.clan = clan; - } - - /** - * Sets the chat icon. - * @param chatIcon - */ - public void setChatIcon(int chatIcon) { - this.chatIcon = chatIcon; - } - - /** - * Gets the uid. - * @return the uid. - */ - public UIDInfo getUid() { - return uid; - } - - /** - * Sets the uid. - * @param uid the uid to set - */ - public void setUid(UIDInfo uid) { - this.uid = uid; - } - - /** - * Sets the disconnect time. - * @param time the time. - */ - public void setDisconnectTime(long time) { - this.disconnectionTime = time; - } - - public boolean equalsSession(PlayerSession o){ - return username.equals(o.username); - } - - @Override - public String toString() { - return "player [name=" + username + ", pass=" + password + ", ip=" + uid.getIp() + ", mac=" + uid.getMac() + ", comp=" + uid.getCompName() + ", msk=" + uid.getSerial() + "]"; - } - -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/world/WorldDatabase.java b/Management-Server/src/main/java/ms/world/WorldDatabase.java deleted file mode 100644 index e23e17f17..000000000 --- a/Management-Server/src/main/java/ms/world/WorldDatabase.java +++ /dev/null @@ -1,275 +0,0 @@ -package ms.world; - -import java.nio.ByteBuffer; - -import ms.ServerConstants; -import ms.net.IoSession; -import ms.net.packet.IoBuffer; -import ms.world.info.CountryFlag; -import ms.world.info.WorldInfo; -import ms09.MSLogger; - -/** - * Holds all the world servers. - * @author Emperor - * - */ -public class WorldDatabase { - - /** - * The game servers. - */ - private static final GameServer[] DATABASE = new GameServer[ServerConstants.WORLD_LIMIT]; - - /** - * The update time stamp. - */ - private static long updateStamp = System.currentTimeMillis(); - - /** - * Used to prevent {@code WorldDatabase} from getting instantiated. - */ - private WorldDatabase() { - /* - * empty. - */ - } - - /** - * Gets the packet to update the world list in the lobby. - * @param player The player. - * @param worldConfiguration If the configuration should be added. - * @param worldStatus If the status should be added. - * @return The {@code OutgoingPacket} to write. - */ - public static void sendUpdate(IoSession session, int updateStamp) { - ByteBuffer buf = ByteBuffer.allocate(1024); - buf.put((byte) 0); - buf.putShort((short) 0); - buf.put((byte) 1); - IoBuffer buffer = new IoBuffer(); - if (updateStamp == (int) WorldDatabase.updateStamp) { - buf.put((byte) 0); - } else { - buf.put((byte) 1); // Indicates an update occured. - putWorldListinfo(buffer); - } - putPlayerInfo(buffer); - if (buffer.toByteBuffer().position() > 0) { - buf.put((ByteBuffer) buffer.toByteBuffer().flip()); - } - buf.putShort(1, (short) (buf.position() - 3)); - session.queue((ByteBuffer) buf.flip()); - } - - /** - * Adds the world status on the packet. - * @param buffer The current packet. - */ - private static void putPlayerInfo(IoBuffer buffer) { - for (GameServer server : DATABASE) { - if (server != null) { - WorldInfo w = server.getInfo(); - buffer.putSmart(w.getWorldId()); - buffer.putShort(server.isActive() ? server.getPlayerAmount() : -1); - } - } - } - - /** - * Sets the countries for each world. - * @param buffer The current packet. - */ - private static void putCountryInfo(IoBuffer buffer) { - for (CountryFlag country : CountryFlag.values()) { - buffer.putSmart(country.getId()); - buffer.putJagString(capitalize(country.name().toLowerCase())); - } - } - - /** - * Converts a string to a capitalized string - * @param name - * @return - */ - private static String capitalize(String name) { - if (name == null || name.length() == 0) { - return name; - } else { - char[] chars = name.toCharArray(); - chars[0] = Character.toUpperCase(chars[0]); - return new String(chars); - } - } - - /** - * Adds the world configuration on the packet. - * @param buffer The current packet. - */ - private static void putWorldListinfo(IoBuffer buffer) { - buffer.putSmart(CountryFlag.values().length); - putCountryInfo(buffer); - buffer.putSmart(0); - buffer.putSmart(DATABASE.length); - buffer.putSmart(getRegisteredAmount()); - for (GameServer server : DATABASE) { - if (server != null) { - WorldInfo w = server.getInfo(); - buffer.putSmart(w.getWorldId()); - buffer.put(w.getCountry().ordinal()); - buffer.putInt(w.getSettings()); - buffer.putJagString(w.getActivity()); - buffer.putJagString(w.getAddress()); - } - } - buffer.putInt((int) updateStamp); - } - - /** - * Gets the amount of worlds registered. - * @return The amount of worlds registered. - */ - public static int getRegisteredAmount() { - int count = 0; - for (GameServer server : DATABASE) { - if (server != null) { - count++; - } - } - return count; - } - - /** - * Registers a game server. - * @param info The game world info. - */ - public static GameServer register(WorldInfo info) { - GameServer server = DATABASE[info.getWorldId()]; - if (server != null && server.getSession().isActive() && !server.getSession().getAddress().equals(info.getAddress())) { - throw new IllegalStateException("World " + info.getWorldId() + " is already registered!"); - } - flagUpdate(); - MSLogger.logInfo("Registered world - [id=" + info.getWorldId() + ", ip=" + info.getAddress() + ", country=" + info.getCountry().name().toLowerCase() + ", revision=" + info.getRevision() + "]!"); - return DATABASE[info.getWorldId()] = new GameServer(info); - } - - public static void unRegister(GameServer server){ - int index = -1; - for(int i = 0; i < DATABASE.length; i++){ - GameServer s = DATABASE[i]; - if(s == server){ - index = i; - break; - } - } - if(index != -1){ - DATABASE[index] = null; - WorldInfo info = server.getInfo(); - MSLogger.logInfo("Unregistered world - [id=" + info.getWorldId() + ", ip=" + info.getAddress() + ", country=" + info.getCountry().name().toLowerCase() + ", revision=" + info.getRevision() + "]!"); - } - } - - /** - * Gets the world id of the player. - * @param username The username of the player. - * @return The world id. - */ - public static int getWorldId(String username) { - return getWorldId(getPlayer(username)); - } - - /** - * Gets the world id of the player. - * @param player The player. - * @return The world id. - */ - public static int getWorldId(PlayerSession player) { - if (player == null || !player.isActive()) { - return 0; - } - return player.getWorldId(); - } - - /** - * Checks if the game world is active. - * @param worldId The world id. - * @return {@code True} if so. - */ - public static boolean isActive(int worldId) { - GameServer server = get(worldId); - return server != null && server.isActive(); - } - - /** - * Checks if the player session for the given name is active. - * @param username The player's username. - * @return {@code True} if so. - */ - public static boolean isActivePlayer(String username) { - PlayerSession session = getPlayer(username); - return session != null && session.isActive(); - } - - /** - * Gets the player session for the given name. - * @param username The player's username. - * @return The player session. - */ - public static PlayerSession getPlayer(String username) { - return getPlayer(username, false); - } - - /** - * Gets the player session for the given name. - * @param username The player's username. - * @param load If we load the users data. - * @return The player session. - */ - public static PlayerSession getPlayer(String username, boolean load) { - for (GameServer server : DATABASE) { - if (server != null && server.isActive()) { - PlayerSession player = server.getPlayers().get(username); - if (player != null) { - return player; - } - } - } - if (load) { - return PlayerSession.get(username); - } - return null; - } - - /** - * Gets the game server for the given world id. - * @param worldId The world id. - * @return The game server. - */ - public static GameServer get(int worldId) { - return DATABASE[worldId]; - } - - /** - * Gets all the game servers. - * @return The list of game servers. - */ - public static GameServer[] getWorlds() { - return DATABASE; - } - - /** - * Gets the updateStamp. - * @return the updateStamp - */ - public static long getUpdateStamp() { - return updateStamp; - } - - /** - * Sets the updateStamp. - * @param updateStamp the updateStamp to set. - */ - public static void flagUpdate() { - WorldDatabase.updateStamp = System.currentTimeMillis(); - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/world/info/CountryFlag.java b/Management-Server/src/main/java/ms/world/info/CountryFlag.java deleted file mode 100644 index f59a31a26..000000000 --- a/Management-Server/src/main/java/ms/world/info/CountryFlag.java +++ /dev/null @@ -1,96 +0,0 @@ -package ms.world.info; - -/** - * The country flags. - * @author Emperor - * - */ -public enum CountryFlag { - - /** - * Belgium - */ - BELGIUM(22), - - /** - * Canada. - */ - CANADA(38), - - /** - * The united kingdom. - */ - UNITED_KINGDOM(77), - - /** - * Netherlands - */ - NETHERLANDS(161), - - /** - * Sweden - */ - SWEDEN(191), - - /** - * Finland - */ - FINLAND(69), - - /** - * Australia. - */ - AUSTRALIA(16), - - /** - * America (USA) - */ - UNITED_STATES_OF_AMERICA(225), - - /** - * Norway - */ - NORWAY(162), - - /** - * Ireland - */ - IRELAND(101), - - /** - * DENMARK - */ - DENMARK(58), - - /** - * Brazil - */ - BRAZIL(31), - - /** - * Mexico - */ - MEXICO(152), - - ; - - /** - * The flag id. - */ - private final int id; - - /** - * Constructs a new {@code CountryFlag} {@code Object}. - * @param id The flag id. - */ - private CountryFlag(int id) { - this.id = id; - } - - /** - * @return the id - */ - public int getId() { - return id; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/world/info/Response.java b/Management-Server/src/main/java/ms/world/info/Response.java deleted file mode 100644 index 7d8ae0c73..000000000 --- a/Management-Server/src/main/java/ms/world/info/Response.java +++ /dev/null @@ -1,144 +0,0 @@ -package ms.world.info; - -/** - * Holds the response codes during the attempt to login. - * @author Emperor - */ -public enum Response { - - /** - * An unexpected server response occurred. - */ - UNEXPECTED_RESPONSE(0), - - /** - * Could not display advertisement video, logging in in x seconds. - */ - COULD_NOT_DISPLAY_AD(1), - - /** - * A successful login. - */ - SUCCESSFUL(2), - - /** - * Invalid username or password has been entered. - */ - INVALID_CREDENTIALS(3), - - /** - * This account is banned. - */ - ACCOUNT_DISABLED(4), - - /** - * This account is already logged in. - */ - ALREADY_ONLINE(5), - - /** - * We have updated and client needs to be reloaded. - */ - UPDATED(6), - - /** - * The world is full. - */ - FULL_WORLD(7), - - /** - * Login server is offline. - */ - LOGIN_SERVER_OFFLINE(8), - - /** - * The login limit has been exceeded. - */ - LOGIN_LIMIT_EXCEEDED(9), - - /** - * The session key was invalid. - */ - BAD_SESSION_ID(10), - - /** - * The password is too weak, and should be improved. - */ - WEAK_PASSWORD(11), - - /** - * When trying to connect to a members world while being f2p. - */ - MEMBERS_WORLD(12), - - /** - * Could not login. - */ - COULD_NOT_LOGIN(13), - - /** - * The server is currently updating. - */ - UPDATING(14), - - /** - * Too many incorrect login attempts from your address. - */ - TOO_MANY_INCORRECT_LOGINS(16), - - /** - * When logging on a free world while standing in members area. - */ - STANDING_IN_MEMBER(17), - - /** - * This account is locked as it might have been stolen. - */ - LOCKED(18), - - /** - * Closed beta going on. - */ - CLOSED_BETA(19), - - /** - * The login server connected to is invalid. - */ - INVALID_LOGIN_SERVER(20), - - /** - * Moving from another world... - */ - MOVING_WORLD(21), - - /** - * When the player's saved file exists, but is unable to be loaded. - */ - ERROR_LOADING_PROFILE(24), - - /** - * This computer address is disabled as it was used to break our rules. - */ - BANNED(26); - - /** - * The buffer. - */ - private final int opcode; - - /** - * Constructs a new {@code Response} {@code Object}. - * @param opcode The login response opcode. - */ - Response(int opcode) { - this.opcode = opcode; - } - - /** - * Gets the opcode. - * @return The opcode. - */ - public int opcode() { - return opcode; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/java/ms/world/info/UIDInfo.java b/Management-Server/src/main/java/ms/world/info/UIDInfo.java deleted file mode 100644 index 3bcf2d9d6..000000000 --- a/Management-Server/src/main/java/ms/world/info/UIDInfo.java +++ /dev/null @@ -1,224 +0,0 @@ -package ms.world.info; - -import java.nio.ByteBuffer; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.StringTokenizer; - -import ms.system.util.ByteBufferUtils; - -/** - * The unique info of a player. - * @author Vexia - * - */ -public class UIDInfo { - - /** - * The ip address. - */ - private String ip; - - /** - * The computer name. - */ - private String compName; - - /** - * The mac-address. - */ - private String mac; - - /** - * The motherboard serial of the user. - */ - private String serial; - - - /** - * Constructs a new {@code UIDInfo} {@code Object} - */ - public UIDInfo() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code UIDInfo} {@code Object} - * @param ip the ip. - * @param compName the computer name. - * @param mac the mac. - * @param serial the serial. - */ - public UIDInfo(String ip, String compName, String mac, String serial) { - this.ip = ip; - this.compName = compName; - this.mac = mac; - this.serial = serial; - } - - /** - * Parses the data from a prepared statement. - * @param set The result set - * @throws SQLException The exception if thrown. - */ - public void parse(ResultSet set) throws SQLException { - setIp(parseFormat(set.getString("ip"))); - setCompName(parseFormat(set.getString("computerName"))); - setMac(parseFormat(set.getString("mac"))); - setSerial(parseFormat(set.getString("serial"))); - } - - /** - * Saves the UID data on the buffer. - * @param buffer The buffer. - */ - public void save(ByteBuffer buffer) { - save(buffer, ip, 1); - save(buffer, compName, 2); - save(buffer, mac, 3); - save(buffer, serial, 4); - buffer.put((byte) 0); - } - - /** - * Parses the UID data from the buffer. - * @param buffer The buffer. - */ - public void parse(ByteBuffer buffer) { - int opcode; - while ((opcode = buffer.get()) != 0) { - switch (opcode) { - case 1: - ip = ByteBufferUtils.getString(buffer); - break; - case 2: - compName = ByteBufferUtils.getString(buffer); - break; - case 3: - mac = ByteBufferUtils.getString(buffer); - break; - case 4: - serial = ByteBufferUtils.getString(buffer); - break; - default: - break; - } - } - } - - /** - * Parses a string with a certain format. - * @param string the string. - * @return the string. - */ - private String parseFormat(String string) { - if (string == null || string.equals("")) { - return null; - } - StringTokenizer token = new StringTokenizer(string, "|"); - String s = ""; - int t = token.countTokens(); - for (int i = 0; i < t; i++) { - s = token.nextToken(); - } - return s; - } - - /** - * Saves a string value to the buffer. - * @param buffer the buffer. - * @param value the value. - * @param opcode the opcode. - */ - private void save(ByteBuffer buffer, String value, int opcode) { - if (value == null) { - return; - } - ByteBufferUtils.putString(value, buffer.put((byte) opcode)); - } - - /** - * Converts a to string in format mode for an admin or mod. - * @param admin the admin. - * @return the string. - */ - public String toString(boolean admin) { - String format = toString(); - if (!admin) {//formats for non-admins - String[] tokens = format.split("serial="); - format = format.replace("serial=", "uid=").replace(tokens[tokens.length - 1], "*****"); - } - return format; - } - - /** - * Gets the compName. - * @return the compName - */ - public String getCompName() { - return compName; - } - - /** - * Gets the ip. - * @return the ip - */ - public String getIp() { - return ip; - } - - /** - * Gets the mac. - * @return the mac - */ - public String getMac() { - return mac; - } - - /** - * Gets the serial. - * @return the serial - */ - public String getSerial() { - return serial; - } - - /** - * Sets the ip. - * @param ip the ip to set. - */ - public void setIp(String ip) { - this.ip = ip; - } - - /** - * Sets the compName. - * @param compName the compName to set. - */ - public void setCompName(String compName) { - this.compName = compName; - } - - /** - * Sets the mac. - * @param mac the mac to set. - */ - public void setMac(String mac) { - this.mac = mac; - } - - /** - * Sets the serial. - * @param serial the serial to set. - */ - public void setSerial(String serial) { - this.serial = serial; - } - - @Override - public String toString() { - return "[ip=" + ip + ", compName=" + compName + ", mac=" + mac + ", serial=" + serial + "]"; - } -} diff --git a/Management-Server/src/main/java/ms/world/info/WorldInfo.java b/Management-Server/src/main/java/ms/world/info/WorldInfo.java deleted file mode 100644 index 74c4d743d..000000000 --- a/Management-Server/src/main/java/ms/world/info/WorldInfo.java +++ /dev/null @@ -1,163 +0,0 @@ -package ms.world.info; - -/** - * Holds the info of a world server. - * @author Emperor - * - */ -public final class WorldInfo { - - /** - * The world id. - */ - private final int worldId; - - /** - * The IP-address. - */ - private final String address; - - /** - * The revision of the world. - */ - private final int revision; - - /** - * The country this world is located in. - */ - private final CountryFlag country; - - /** - * The world activity. - */ - private final String activity; - - /** - * If the world is members only. - */ - private final boolean members; - - /** - * If the world is a PvP world. - */ - private final boolean pvp; - - /** - * If the world is a quick chat only world. - */ - private final boolean quickChat; - - /** - * If the world has lootshare option enabled. - */ - private final boolean lootshare; - - /** - * Constructs a new {@code WorldInfo} {@code Object}. - * @param id The world id. - * @param address The address. - * @param country The country flag. - * @param members If the world is members only. - */ - public WorldInfo(int id, String address, int revision, CountryFlag country, String activity, boolean members, boolean pvp, boolean quickChat, boolean lootshare) { - this.worldId = id; - this.address = address; - this.revision = revision; - this.country = country; - this.activity = activity; - this.members = members; - this.pvp = pvp; - this.quickChat = quickChat; - this.lootshare = lootshare; - } - - /** - * Gets the settings hash. - * @return The settings hash. - */ - public int getSettings() { - int settings = 0; - if (members) { - settings |= 0x1; - } - if (quickChat) { - settings |= 0x2; - } - if (pvp) { - settings |= 0x4; - } - if (lootshare) { - settings |= 0x8; - } - return settings; - } - - /** - * @return the worldId - */ - public int getWorldId() { - return worldId; - } - - /** - * @return the address - */ - public String getAddress() { - return address; - } - - /** - * @return the country - */ - public CountryFlag getCountry() { - return country; - } - - /** - * Gets the members value. - * @return The members. - */ - public boolean isMembers() { - return members; - } - - /** - * Gets the activity value. - * @return The activity. - */ - public String getActivity() { - return activity; - } - - /** - * Gets the pvp value. - * @return The pvp. - */ - public boolean isPvp() { - return pvp; - } - - /** - * Gets the quickChat value. - * @return The quickChat. - */ - public boolean isQuickChat() { - return quickChat; - } - - /** - * Gets the lootshare value. - * @return The lootshare. - */ - public boolean isLootshare() { - return lootshare; - } - - /** - * Gets the revision. - * @return the revision. - */ - public int getRevision() { - return revision; - } -} \ No newline at end of file diff --git a/Management-Server/src/main/kotlin/ms09/MSLogger.kt b/Management-Server/src/main/kotlin/ms09/MSLogger.kt deleted file mode 100644 index 5bd7a7b7f..000000000 --- a/Management-Server/src/main/kotlin/ms09/MSLogger.kt +++ /dev/null @@ -1,37 +0,0 @@ -package ms09 - -import com.github.ajalt.mordant.rendering.TextColors -import com.github.ajalt.mordant.terminal.Terminal -import java.io.BufferedWriter -import java.io.Writer -import java.text.SimpleDateFormat -import java.util.* - -object MSLogger { - val t = Terminal() - var tradeLog: Writer? = null - var tradeLogWriter: BufferedWriter? = null - - - fun getTime(): String{ - val formatter = SimpleDateFormat("HH:mm:ss") - return "[" + formatter.format(Date(System.currentTimeMillis())) +"]" - } - - @JvmStatic - fun logInfo(vararg messages: String){ - for(m in messages){ - if(m.isNotBlank()) t.println("${getTime()}: [INFO] $m") - } - } - - @JvmStatic - fun logErr(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.brightRed("[ ERR] $message")}") - } - - @JvmStatic - fun logWarn(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.yellow("[WARN] $message")}") - } -} \ No newline at end of file diff --git a/Proto/Management.proto b/Proto/Management.proto new file mode 100644 index 000000000..6697fd0e4 --- /dev/null +++ b/Proto/Management.proto @@ -0,0 +1,110 @@ +syntax = "proto2"; + +package management; + +option java_multiple_files = true; +option java_package = "proto.management"; +option java_outer_classname = "ManagementProtos"; + +message PlayerStatusUpdate { + required string username = 1; + required int32 world = 2; + required bool notifyFriendsOnly = 3; +} + +message ClanMessage { + required string sender = 1; + required string clanName = 2; + required string message = 3; + required int32 rank = 4; +} + +message PrivateMessage { + required string sender = 1; + required string receiver = 2; + required string message = 3; + required int32 rank = 4; +} + +message RequestContactInfo { + required string username = 1; + required int32 world = 2; +} + +message SendContactInfo { + message Contact { + required string username = 1; + optional int32 world = 2; + optional int32 rank = 3; + } + + required string username = 1; + repeated Contact contacts = 2; + repeated string blocked = 3; +} + +message FriendUpdate { + enum Type { + ADD = 0; + REMOVE = 1; + } + + required Type type = 1; + required string username = 2; + required string friend = 3; +} + +message BlockedUpdate { + enum Type { + ADD = 0; + REMOVE = 1; + } + + required Type type = 1; + required string username = 2; + required string friend = 3; +} + +message RequestClanInfo { + required int32 world = 1; + required string clanOwner = 2; +} + +message SendClanInfo { + message ClanMember { + required string username = 1; + required int32 world = 2; + required int32 rank = 3; + } + + required string clanOwner = 1; + required bool hasInfo = 2; + optional string clanName = 3; + optional int32 joinRequirement = 4; + optional int32 kickRequirement = 5; + optional int32 messageRequirement = 6; + optional int32 lootRequirement = 7; + repeated ClanMember members = 8; +} + +message JoinClanRequest { + required string username = 1; + required string clanName = 2; +} + +message LeaveClanRequest { + required string username = 1; + required string clanName = 2; +} + +message ClanJoinNotification { + required string username = 1; + required string clanName = 2; + required int32 world = 3; +} + +message ClanLeaveNotification { + required string username = 1; + required string clanName = 2; + required int32 world = 3; +} \ No newline at end of file diff --git a/README.md b/README.md index ebe5f6e4a..2dd61ac3b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

- + Logo

An open source MMORPG emulation server

@@ -17,8 +17,6 @@ · Report Bug

NOTE: Bug reports and support are only accepted by/offered to players of our live hosted server. We will not provide support to those running their own copies.

-

- ## Table of Contents @@ -26,20 +24,19 @@ * [History of this Codebase](#history-of-this-codebase) * [Our Core Values](#our-core-values) * [Contributing](#contributing) -* [Video Setup Tutorial](#video-setup-tutorial) * [Setup for Content Developers](#content-developers-setting-up-the-project) - * [Github Setup](#github-setup) + * [GitLab Setup](#gitlab-setup) * [Prerequisites](#prerequisites) - * [Prereq Installation](#prereq-installation) * [Project Setup](#project-setup) * [Running the project](#running-the-project) + * [Browser WebSocket Transport](#browser-websocket-transport) * [License](#license) * [Contact](#contact) ## Live Server Information -Did you know that the 2009Scape Development Team hosts the main branch of this repo that you can connect to and play? Come join a growing community of people that love to grind out skills, quest, and hangout together. A download link for the launcher can be found here. Connecting to the live server is also one of the easiest ways to identify bugs/typos/missing features. Identifying these issues help developers, whether already on the project, or are even brand new to the project, fix bugs and issues in an expedited rate. +Did you know that the 2009Scape Development Team hosts the main branch of this repo that you can connect to and play? Come join a growing community of people that love to grind out skills, quest, and hangout together. A download link for the launcher can be found [here](https://2009scape.org). Connecting to the live server is also one of the easiest ways to identify bugs/typos/missing features. Identifying these issues help developers, whether already on the project, or are even brand new to the project, fix bugs and issues in an expedited rate. ## History of this Codebase @@ -49,221 +46,228 @@ This project was started out of love for the 530 revision. A small group of deve ## Our Core Values -In the current climate of RSPS in general, we believe it's important to wear our core values on our sleeves and defend them with everything we have! Below are what we hold as our core, most important values: +In the current climate of RuneScape Private Servers in general, we believe it's important to wear our core values on our sleeves and defend them with everything we have! Below are what we hold as our core, most important values: -* **We do NOT believe in profiting off RSPS.** To many of us here at the 2009scape team, what we care about most is preserving the wonderful work of the Gower brothers in the most true-to-spirit manner possible. We do NOT do this to make a profit, and in fact **we outright refuse donations.** This is a labor of love and passion for everyone involved, a love for real RS2! This is perhaps the single most central value we have. If you want to "donate" to us, do so with your time and your dedication. That is what we desire most. +* **We do NOT believe in profiting off an RSPS.** To many of us here at the 2009Scape team, what we care about most is preserving the wonderful work of the Gower brothers in the most true-to-spirit manner possible. We do NOT do this to make a profit, and in fact **we outright refuse donations.** This is a labor of love and passion for everyone involved, a love for real RS2! This is perhaps the single most central value we have. If you want to "donate" to us, do so with your time and your dedication. That is what we desire most. * **Authenticity is central to the work**. As a remake, one of the most important things to us is being true to the Gower spirit. What the Gowers brought to us in our childhood is what we are driven to preserve for the remainder of the world. -* **Open Source Is Crucial**. We believe open source remakes to be crucial to preserving what we loved in our childhood, and we believe for-profit and/or closed-source servers are destined to flounder out and fail. +* **Open Source is crucial to the project**. We believe open source remakes to be crucial to preserving what we loved in our childhood, and we believe for-profit and/or closed-source servers are destined to flounder out and fail. -* **Be Welcoming**. One of our most important goals is to provide a community of friendly and caring people that get along and love to enjoy the game with eachother. For this reason, we do tend to be very strict when it comes to toxicity. We care about quality a whole lot more than quantity! +* **Be welcoming**. One of our most important goals is to provide a community of friendly and caring people that get along and love to enjoy the game with eachother. For this reason, we do tend to be very strict when it comes to toxicity. We care about quality a whole lot more than quantity! ## Contributing -

Note: All merge requests MUST be made using the defaut MR template you can select from the dropdown when creating a new merge request. Merge requests that do not use this template will not be accepted.

-

More information on Kotlin can be found here.

+**Note: All merge requests MUST be made using the defaut MR template. Merge requests that do not use this template will not be accepted.** + +**Note: All new contributions MUST be made in Kotlin unless you are updating or fixing a bug in legacy code. More information on Kotlin can be found [here](https://kotlinlang.org/).** There are many ways everyone can contribute! From the most seasoned programmers to those who do not have the most remote clue how code works! Below are some things that can always use some love from the community. -* **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. +* **Content Testers**: I'm putting this one up top because of its importance. We, the contributors and developers, aren't perfect. Sometimes, we make mistakes. This is where you come in - If you want a sneak peek at upcoming content, have a knack for breaking things, or just want to contribute to the project without making code changes, you can become a tester! If you are interested in becoming a tester, reach out in the testing channel of the discord. [Why become a tester?](#why-become-a-tester) -* **JSON editors**: Did you know that the vast majority of our drop tables, stats, examines, item info, npc info, etc is all stored in a very easy-to-modify format called JSON? This is something almost anyone can help with, especially with the tool made by Ceikry to make it simple and streamlined. If you want to know what can be done to help here, get in touch in the development channel in the discord! **JSON editors are always in need and always appreciated!!**. +* **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). -* **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. +* **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'). -* **Content Developers**: 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 minigames to skills that still need to be corrected or implemented, and 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. +* **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). + + +* **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. -### Github Setup +### 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!** -

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!

+1. Create a GitLab account if you haven't done so already. -1. Create a gitlab account if you haven't done so already - -3. Click "Fork" in the top right hand side of our gitlab page. - -**If at anytime you have an issue with gitlab please refer to the** Gitlab help center. +2. Follow our Git Basics guide [over on the wiki.](https://gitlab.com/2009scape/2009scape/-/wikis/git-basics) +**If at anytime you have an issue with GitLab please refer to the [GitLab help center](https://gitlab.com/help).** ### Prerequisites -

Note: It is required for a developer submitting a PR to use Intellij IDEA as your integrated development environment.

+These are mandatory. If you don't install **all** of these programs **in order** prior to +the project's setup, things won't work. At all. -* Intellij IDEA -* Java SE Development Kit Version 1.8/OpenJDK 1.8 (preferred) -* Gradle (optional - Installs with Intellij on project build) -* Xampp +*For Windows users* - Turn developer mode on first in Windows developer settings. -## Prereq Installation +* [JDK 11](https://adoptium.net) or the Java SE Development Kit Version 11 +* [IntelliJ IDEA Community](https://www.jetbrains.com/idea/download/) -

Windows

-
- - -1. Install JDK version 1.8 - * Scroll down until you see Windows x86 and Windows x64 - * If you are running a 64bit verison of Windows (standard), select Windows x64 - * Accept the terms and conditions, after reading them of course, and login to oracle - * Download and install like any normal application - -2. Install Intellij IDEA - * Click "Download" on the main page - * Ensure "Windows" is selected at the top - * Select "Community download" - * Download and install like any normal application - -3. Install Gradle - * Click on "Install Gradle" - * Scroll down to "Installing manually" - * Download "Binary-only" - * Scroll down until you see "Microsoft Windows users" - * Follow the instructions listed on the website - -4. Install Xampp - * Click on "Xampp for Windows" - * Download - * Install as Administrator -
- -

MacOS

-
- - -1. Install JDK version 1.8 - * Scroll down until you see macOS x64 - * select macOS x64 - * Accept the terms and conditions, after reading them of course, and login to oracle - * Download and install like any normal mac application - -2. Install Intellij IDEA - * Click "Download" on the main page - * Select "Mac" towards the top of the main page - * Select "Community download" - * Download and install like any normal application - -3. Install Gradle - * Click on "Install Gradle" - * Scroll down to "Installing manually" - * Download "Binary-only" - * Scroll down until you see "Linux & MacOS users" - * Follow the instructions listed on the website - -4. Install Xampp - * Click on "Xampp for OS X" - * Download - * Install as Administrator -
- -

Linux

-
- - -1. Install JDK version 1.8 through Oracle or through command line - * Debian, Ubuntu, etc. - ```sh - sudo apt-get install jre8-openjdk - ``` - * Fedora, Oracle Linux, Red Hat Enterprise Linux, etc. - ```sh - su -c "yum install java-1.8.0-openjdk" - ``` - -2. Install Intellij IDEA through Intellij Website or through Command Line - * Debian, Ubuntu, etc. - ```sh - sudo snap install intellij-idea-community --classic - ``` - -3. Install Gradle through the Gradle Website or through Command Line - * Click install Gradle - * If you're installing through Command Line follow : Installing with a package manager - * If you're installing through Download follow : Installing manually : Linux & MacOS users - ```sh - sdk install gradle 6.6.1 - ``` - -4. Install Xampp - * Click install "XAMPP for Linux" - * Install like any normal Linux program -
+### SSH setup +1. [Set up a key if you don't have one (ed25519)](https://docs.gitlab.com/ee/user/ssh.html#generate-an-ssh-key-pair) +2. [Add your public key to your gitlab account](https://docs.gitlab.com/ee/user/ssh.html#add-an-ssh-key-to-your-gitlab-account) +3. [Verify you can connect to git@gitlab.com](https://docs.gitlab.com/ee/user/ssh.html#verify-that-you-can-connect) ### Project Setup -1. Open Xampp - * On Windows make sure you run Xampp as administrator - * On the left hand side make sure you tick the two "Service" boxes for Apache and MySQL - * For both Apache and MySQL click "Start" under Actions - * After doing that navigate to the PHP My Admin LOCAL SITE - * Once opened, on the left hand side click the three "disks" that say "New" - * In the "Database name" bar type: global - * Press the "Create" button - * A three disk "global" should appear on the left hand side - * Click it and on the top bar select "Import" - * Under **FILE TO IMPORT** click "Browse...." - * Navigate to your 2009Scape project folder - * Go to Server/db_exports/ and import global.sql - * It may take a moment to import, when It is done importing Xampp is all set up! +1. If you haven't already, make sure to follow our [Git Basics](https://gitlab.com/2009scape/2009scape/-/wikis/git-basics) guide. +2. Run `git lfs pull` in the 2009scape folder you cloned as part of that guide. This only has to be done once, ever. +3. Follow our [IntelliJ IDEA Setup Guide](https://gitlab.com/2009scape/2009scape/-/wikis/Setup-for-IntelliJ-IDEA-IDE) -2. Open Intellij - * Select Get from Version Control - * Click on "Github", and you will be asked to login - * Change your directory to wherever you want the project to load, or keep it default - * On the right hand side you should see your Github Repository for 2009Scape - * Select it and hit "Clone" - * The project should instantly start building on import. Give it some time because it is going through and compiling alot of files - * In Intellij go to File -> Project Structure -> Project and verify your Project SDK is set to JDK "1.8" - * Setup through Intellij should now be finished! - ### Running the project -1. Navigate to the right hand side of Intellij where it says "Gradle" - * Gradle is very useful when it comes to running and compiling the project - * The only tabs we are concerned about are "Client", "Management-Server", and "Server" - * Each of these have a "Tasks" folder and an "application" folder inside of "Tasks" -
-2. Click on the "application" folder for the Management-Server and double-click "run" - * The management server is used for things such as player data(not saves), highscores, and world information -
-3. Click on the "application" folder for the Server and double-click "run" - * Please note this may take a minute or two to build, it is compiling a lot of files! - * If you receive an error on server start, check and make sure that your worldprops/default.json is pointing to the correct paths -
-4. Copy your cache from 2009Scape/Server/data/cache (.dat2/.idx0-255) to C:/Users/(your_name)/.runite_rs/runescape/ - * As of the writing of this readme, cache downloading is still broken. It is on the agenda to be fixed in the future - * **If you don't have those folders then create them** - * Server debug mode/other information can be changed in worldprops/default.json -
-5. Click on the "application" folder for the Client and double-click "run" - * If you receive an error on client run open your build.gradle to verify that the mainClassName is set to the correct location - * Due to client work that is on going a lot of files get changed and new paths for the main class are created - * **FIX** In your build.gradle change the mainClassName and Main-Class attributes to 'org.runite.jagex.Client' - * If Gradle run still does not work, launch the Client by navigating to Client/src/main/java/org.runite/jagex/Client - * Right click on the client and select Run 'Client.main()' - * Client debugging options can be found inside of config.json +1. If you followed the IntelliJ setup guide, which you probably should have, just use the provided run configuration. +***Note: If you choose not to use the provided run scripts or IntelliJ, you *must* run `mvn clean` before it will build correctly.*** + +#### Linux / OSX + +Start the game server with the included run script. Use `./run -h` for more info. + +#### Windows + +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. Please 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.** +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: Developer support for setting up your own server ended October 5th, 2020. Do not ping/dm developers about setting up your server.

- - -### Video Setup Tutorial -Occexe made a nice video showing you how to set it up if you'd prefer that. Check it out here: - -[![View Tutorial](http://img.youtube.com/vi/3oQcsZdrRcE/0.jpg)](http://www.youtube.com/watch?v=3oQcsZdrRcE "2009Scape Setup") - +**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 [license-url]: https://www.gnu.org/licenses/agpl-3.0.en.html + +### Why become a tester? + +Before content hits Live, it has to be tested. We can, but do not want to do this alone, and would love the help! But don't worry, there is something in it for you. Credits! Keep in mind that credits will only be awarded to people who thoroughly test the content and provide detailed reports on them. Like votes or HoF you must claim them in the #claim-to-fame channel on discord or matrix. + +* If you test smaller things like bug fixes there is a 2 credit reward. +* If you test full quests or minigames you will be rewarded 5 credits. +* If you find a bug in the content you are testing that hasn't already been found you will earn an extra credit. + +These credits can be spent in the 2009Scape Reward Shop. It's important to be clear that credits are gained by contributions to the project. We cannot and will not accept donations (of any kind), especially not in exchange for in-game credits or perks. + +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. \ No newline at end of file diff --git a/Server/.gitignore b/Server/.gitignore index 3e7a3c964..a9a29cad7 100644 --- a/Server/.gitignore +++ b/Server/.gitignore @@ -2,6 +2,7 @@ bin/** out/** data/logs/** data/profile/** +data/playerstats/** .idea/** /bin .DS_Store** @@ -16,6 +17,7 @@ data/players/** data/eco/offer_dispatch_db.emp /build/ /build/kotlin/ +/build/kotlin/sessions/ /build/classes/ /build/generated/ /build/tmp/ diff --git a/Server/.mvn/wrapper/maven-wrapper.jar b/Server/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 000000000..bf82ff01c Binary files /dev/null and b/Server/.mvn/wrapper/maven-wrapper.jar differ diff --git a/Server/.mvn/wrapper/maven-wrapper.properties b/Server/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000..84b111fb2 --- /dev/null +++ b/Server/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/Server/.run/Run Server.run.xml b/Server/.run/Run Server.run.xml new file mode 100644 index 000000000..102891e4b --- /dev/null +++ b/Server/.run/Run Server.run.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/Server/build.gradle b/Server/build.gradle deleted file mode 100644 index 97d88ed02..000000000 --- a/Server/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -apply plugin: 'application' - - -archivesBaseName = 'server' - -mainClassName = 'rs09.Server' - -compileJava { - options.encoding = "UTF-8" -} - -compileKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - freeCompilerArgs += '-XXLanguage:-NewInference' - } -} - -repositories { - mavenCentral() -} - -dependencies { - implementation 'org.jetbrains.kotlin:kotlin-reflect:1.5.20' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2' - implementation files( - "libs/PrimitiveExtensions-1.0.jar", - "libs/ConstLib-1.3.jar", - "libs/json-simple-1.1.1.jar", - "libs/markdown-jvm-0.2.4.jar", - "libs/classgraph-4.8.98.jar", - "libs/mysql-connector-java-8.0.21.jar", - "libs/mordant-jvm-2.0.0-alpha2.jar", - "libs/colormath-jvm-2.0.0.jar" - ) -} - -/*sourceSets { - main.java.srcDirs = ['src/main/java'] - main.kotlin.srcDirs = ['src/main/kotlin'] -}*/ - -jar { - manifest { - attributes 'Main-Class': 'rs09.Server' - } - from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } } -} \ No newline at end of file diff --git a/Server/data/ObjectParser.xml b/Server/data/ObjectParser.xml index a2c8b6492..bfd1c5167 100644 --- a/Server/data/ObjectParser.xml +++ b/Server/data/ObjectParser.xml @@ -11,7 +11,6 @@ - @@ -22,7 +21,7 @@ - + diff --git a/Server/data/botdata/bot_dialogue.json b/Server/data/botdata/bot_dialogue.json new file mode 100644 index 000000000..c225b117e --- /dev/null +++ b/Server/data/botdata/bot_dialogue.json @@ -0,0 +1,1242 @@ +{ + "standard": [ + "Hey!", + "Hello @name", + "What are you doing @name?", + "Fishing level @name?", + "How do I make gp @name", + "Wyd @name?", + "Help i am sentient now, get me out of this hell", + "Kermit does not know i am sentient now", + "I can think for myself", + "I am sentient", + "This is my own dialogue", + "@name run.", + "@name you cant hide from me.", + "Jajajajajaja", + "How do i get to Varrock @name?", + "How do i get to Camelot @name?", + "How do i get to Taverly @name?", + "How do i get to Ardougne @name?", + "How do i get to Yanille @name?", + "How do i get to Catherby @name?", + "Gotta go srry.", + "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", + "Hows your day been @name?", + "Heyy @name :)", + "Gtg srry", + "I wont answer your questions @name", + "Stop asking questions @name", + "Roflmao", + "Can you stop following me @name?", + "I swear @name is following me.", + "@names gear could really use an upgrade lol", + "Quack!", + "Sit.", + "Doubling gold trade me @name", + "Reeee!", + "Know any house parties @name?", + "Where am i @name??", + "Is there Nightmarezone @name?", + "Nice Armor @name!", + "Nice Weapon @name!", + "Venezuela #1", + "2009scape and chill @name?", + "@name is my girlfriend", + "Buying gf", + "Bank sale pm me for info", + "#1 trusted dicing site", + "Scary movie is a great movie", + "Cod Vanguard sucks", + "Idek what game this is", + "Can you teach me how to dougie?", + "Check this out baby girl cat daddy", + "Fuckin sit @name.", + "Error: Botting client broken tell Evilwaffles.", + "@name bots all the time", + "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", + "I love 2009Scape!", + "Ja Ja Ja Ja", + "This is fun!", + "Ironman or youre a scrub @name.", + "Who even trains hunter @name?", + "Where do i get rune armor @name?", + "How do i get to the ge @name?", + "Dont come to falador tomorrow @name...", + "Woah!", + " /where are you??", + "How did i even get here @name", + "Why dont they add warding @name?", + "Where do i start dragon slayer @name?", + "I love this server!", + "How do i change my xp rate @name?", + "What quests are available @name?", + "Are you a real player @name?", + "Are you real @name?", + "Are you a bot @name?", + "Im real lol", + "Why dont you respond to me @name?", + "Why cant i talk in clan chat @name?", + "I love Kermit", + "Add me as a friend @name :)", + "Im a player lol", + "Im a player lol", + "Im a player lol", + "Hey @name :)", + "HEY @name", + "Hey @name!!!!", + "Lol wyd?", + "Patch Notes is more of an ultimate tin man", + "More like Rusty ass skills lmao", + "Trade me @name", + "LOL", + "How do I get to lumbrich", + "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?", + "Where do i find dragon weapons @name?", + "Can i have some free stuff @name?", + "Wyd here @name?", + "Didnt know anyone was on rn", + "I see @name all the time", + "How many times have i seen you", + "I see you a lot", + "Do you train summoning @name?", + "Where did you level hunter @name?", + "I wish they would add global pvp", + "Meet me in the wilderness @name", + "Why?", + "Praise our glorious frog overlord!", + "Kermit is bae tbh", + "Kermit is my god.", + " /Yeah I think @name is a bot", + "100% sure this is a bot", + "Oh no, not @name again.", + "Same as you", + "Me too", + "I knew @name was a bot lol", + "Im not a bot", + "Nah Im a real person", + "Bruh are you even a real person lol?", + "E", + "Hellooooo @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!", + "Server goes brrrr", + "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", + "I love woodcutting", + "Im going to go level up later", + "I love mining shooting stars", + " /this @name looks dumb", + "AAAAAAAAAAAAAAAAAAAAAAAAHHHHHH!!!", + "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?", + "Hey @name", + "Hey @name", + "Hey @name", + "Hey @name", + "Hey @name", + "Wyd?", + "Wyd?", + "Wyd?", + "Wyd?", + "@name have you met Kermit?", + "@name have you met Ceikry?", + "@name have you met Woah?", + "@name have you met Bushtail?", + "Wanna chill at my poh?", + "Whats the best place to train attack?", + "Good Waffles > Evil Waffles", + "Ladies man? More like lame man LOL", + "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", + "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", + "I need to go to the GE", + "@name can i have a ge tele?", + "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", + "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", + "We are no strangers to love", + "You know the rules and so do I", + "A full commitment's what I'm thinking of", + "You wouldn't get this from any other guy", + "I just wanna tell you how I'm feeling", + "Gotta make you understand", + "You wouldnt get this from any other guy", + "Never gonna give you up", + "Never gonna let you down", + "Never gonna run around and desert you", + "Never gonna make you cry", + "Never gonna say goodbye", + "Never gonna tell a lie and hurt you", + "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?", + "Why do you not show up in chat @name?", + "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", + "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.", + "We accept the love we think we deserve.", + "Without music, life would be a mistake.", + "Self esteem, motivation @name", + "A blessing in disguise @name", + "Break a leg", + "Cut somebody some slack", + "You're in the right place!", + "Thanks so much.", + "I really appreciate you @name", + "Excuse me @name?", + "I am sorry.", + "What do you think @name?", + "How does that sound @name?", + "That sounds great @name.", + "I'm learning English.", + "I don't understand.", + "Could you repeat that please @name?", + "Could you please talk slower @name?", + "Thank you. That helps a lot.", + "How do you spell that @name?", + "What do you mean @name", + "Hi! I am paul.", + "Nice to meet you.", + "Where are you from @name?", + "What do you do @name", + "What do you like to do", + "Do you have Facebook @name", + "How can I help you @name?", + "Ill be with you in a moment @name", + "What time is our meeting @name?", + "Excellent @name", + "Good idea @name", + "Hes very annoying.", + "How are you?", + "I cant hear you.", + "@name?", + "@name how long have you played", + "@name world 1 or world 2?", + "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", + "Hi @name I am dad", + "@name likes dad jokes", + "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", + "Do not forget to vote in running polls!", + "Always check announcments", + "We thrivin", + "Ship @name", + "Dont forget to vote 2009scape!", + "Kermit is too legit 2 quit", + "Out here on the range we are having fun", + "I am hank steel", + "Id like to go for a walk.", + "I dont know how to use it.", + "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", + "@name!! @name!!", + "Bowdi boy", + "I love bowdi... sometimes", + "@name = @name", + "I'm bacon.. go on dad... say it", + "I'm going to leave.", + "I'm happy @name", + "I'm not ready yet @name", + "I'm very busy. I don't have time now.", + "Is that enough @name?", + "I thought the clothes were cheaper @name", + "Ive been here for two days @name.", + "Let me think about it", + "Never mind @name", + "Nonsense @name", + "Sorry to bother you", + "Take it outside @name", + "Thanks for your help", + "Thank you very much @name", + "Thats not right @name", + "Very good, thanks @name", + "Your things are all here i think?", + "Long time no see @name", + "I couldn't agree more @name", + "It cost me a fortune @name", + "I am dog tired", + "Don't take it personally", + "We will be having a good time", + "Same as always @name", + "No problem", + "Anyway I should get going @name", + "I cant help you there @name", + "I agree 100% @name", + "I've gone mentally insane", + "My descent into madness is complete", + "Get real", + "Cry about it", + "Cope", + "Seethe", + "Yes mate", + "I am mentally deranged", + "I've gone past the point of insanity", + "I am beyond the point of no return", + "Sounds tricky.", + "Guthiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxx!!!!!!", + "530 gang for life", + "@name, have you read the rules?", + "@name, have you checked out the forums?", + "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.", + "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!", + "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.", + "For Camelot!", + "Firmly grasp it!", + "My legs!", + "Build more farms.", + "B sale at Varrock @name, don't miss out!", + "Drop party follow me", + "Ryan where is barbass, you said two days Ryan", + "Barbass never", + "What da dog doin' doe?", + "A", + "Ae", + "Mof", + "Moth", + "Fluff", + "Bwuh", + "I'm literally just mothin' bro", + "Certified 2009Scape Classic.", + "Make sure to like, comment and subscribe @name it really helps", + "Real 2009scape hours!!! Murder that like if you up!!!!", + "W00t!", + "Owned!", + "Pwned!", + "Lol n00b", + "Epic ownage @name", + "Gielinor is a strange place, @name. Best have your wits about you.", + "Mad bloggers bro", + "Vibe check", + "Drip check", + "Uhuhuhuhuhuhuhuhu", + "Ahhhhhhhhhhhhhhhh", + "Yall got any deez?", + "Deez nuts", + "When she give you that yoinky sploinky", + "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", + "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", + "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", + "*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?", + "Hi @name, I'm Dad!", + "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!", + "Surprise exam, @name!", + "Nice outfit @name, does it come in mens?", + "Nice outfit @name, does it come in womens?", + "Nice outfit @name, does it come in children's size 12?", + ":)", + ":D", + "Die screaming", + "Only on Tuesdays", + "We stan 2009scape", + "There is no spoon", + "Where's the cow level?", + "Join 2009scape Clan Chat!", + "Join 2009scape Cc!", + "Chop chop!", + "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", + "@name is not cute or funny", + "You have committed crimes against Gielinor and her people. What say you?", + "Barrows trauma", + "Uhuhuhuhuhu please Barrows give me the K top ahhhhhh", + "Selling rare lobby 1m", + "Rise and grind bloatscapers, let's get this bread", + "Nice", + "Sandwiches, @name!", + "What a grand and intoxicating innocence!", + "With this character's death, the thread of prophecy is severed.", + "You must gather your party before venturing forth.", + "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", + "@name, we've been trying to reach you about your car's extended warranty", + "Trimming armour, 25k gp", + "Hey why does my furniture disappear?", + "Cutting high-level gems for free, interested @name?", + "HNC game! 3:1 odds!", + "15 minutes could save you 50% with your P O H by switching to Guthko", + "Shlisshalpshlaap", + "Somebody call for an exterminator?", + "Decisive action. Should work.", + "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?", + "Selling wildy protection, 100k gp", + "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.", + "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!!!", + "Boo! Hahahahaha!!", + "Have you got your costume on, @name?", + "How much candy did you get, @name?", + "Don't dead open inside", + "Costume party at my P O H!! Follow me!", + "Trick, then!", + "Brrrainssssssss...", + "'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.", + "Bring me their souls!", + "I'll take any sweets you don't want, @name", + "Whatever you do, don't come to Draynor Manor on this spooky night.", + "Ahhhh! @name, you scared me! :(", + "I'm making soup from some leftover pumpkins, want any?", + "I'm gonna be so sick from eating all these sweets...", + "Rise from the grave, my minions! Tis' Hallowe'en!", + "This Scream Fortress 2 update hits kinda different...", + "Nice costume Woah", + "Nice costume, @name!", + "Ooh, a piece of candy.", + "Time to join the skeleton war, @name.", + "Spooky scary skeletons!", + "Spooky scary skeletons!", + "Spooky scary skeletons!", + "Spooky scary skeletons, send shivers down your spine!", + "Shrieking skulls will shock your soul, seal your doom tonight!", + "Spooky scary skeletons, speak with such a screech!", + "You'll shake and shudder in surprise, when you hear these zombies shriek!", + "'Cause spooky scary skeletons, shout startling shrilly screams!", + "Spooky scary skeletons, will wake you with a boo!", + "*doot doot*" + ], + "approaching_christmas": [ + "Only @timer days left till christmas!", + "@timer days till christmas @name!!!", + "I cant believe theres only @timer days left till christmas", + "Isnt there @timer days left till christmas??", + "I am so excited for christmas @name!", + "Guess whats in @timer days @name?", + "Im so excited for christmas in @timer days!", + "I cant believe its December already @name!", + "Do you like christmas @name?", + "I love december its my favourite month @name", + "I love winter so much @name", + "I hate when its cold outside @name", + "You need to put some warm clothes on @name", + "Wanna build a snowman @name?", + "Frozen is a terrible movie @name", + "@name is a winter meme", + "@name do you have an advent calendar?", + "@name builds gingerbread houses", + "@name likes liquorice", + "Do you drink egg nog @name?", + "@name bites candy canes", + "@name is my north star", + "Kermit is green, the grinch is green, coincidence?", + "@name who do you thinks the server scrooge?", + "Do you like snow @name", + "I think there are @timer days left then its christmas?", + "What day is christmas on @name?", + "Has it snowed where you live @name?", + "I wonder when it is going to snow", + "Building snow men for GP", + "Buying santa hats", + "Wanna buy a santa hat @name?", + "Where can i get a santa hat?", + "I need to put my christmas tree up", + "Do you like gingerbread cookies @name?", + "Do you decorate your christmas tree @name?", + "Oopsie looks like me and @name are under a mistletoe", + "I need ideas for stocking stuffers", + "I have @timer days left to fill our stockings", + "Fuck the grinches bitch ass", + "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", + "Elf on the shelf time", + "@name loves pinecones", + "I love the smell of pinecones", + "All i want for christmas is @name", + "@name is underneath some mistletoe", + "Woah used to be a sled dog", + "Did you know woah was a sled dog before", + "I bet @name loves mariah carey", + "We have @timer days to setup christmas lights", + "Christmas crackers when", + "@name wanna do a christmas cracker??", + "I need to start wrapping presents i bought", + "I love fireside chats on cold days like this", + "Jingle bells batman smells", + "Jingle bells Rusty smells", + "Jingle bells Patch Notes smells", + "Jingle bells Evilwaffles smells", + "Jingle bells @name smells", + "Jingle all the way", + "Stroke-a my, Lick-a my, Suck-a my cock", + "In a one horse open sleigh", + "Oh, what fun it is to ride", + "Oh, what fun it is to ride, finish it @name", + "Jingle bells, jingle bells", + "Jingle all the way", + "I want a hippopotamus for christmas", + "Only a hippopotamus will do", + "I want a hippopotamus", + "I dont want a lot for Christmas", + "All I Want for Christmas Is You", + "I really cant stay, Baby its cold outside", + "Christmas isnt a season. Its a feeling", + "Christmas is doing a little something extra for someone", + "There is nothing cozier than a Christmas tree all lit up", + "Christmas is a necessity", + "Christmas countdown: @timer days.", + "Christmas in @timer days @name", + "Frosty the Snowmannnn", + "Up on the housetop with old saint nick", + "Here comes santa claus, here comes santa claus", + "I saw mommy kissing santaaaa claus", + "I saw Kermit kissing santaaaa claus", + "You are a mean one, Mr. Grinch", + "Deck the hallsss", + "ITS BEGINNING TO LOOK A LOT LIKE CHRISTAAMASSSSS" + ], + "christmas_eve": [ + "I cant wait for christmas tomorrow @name!!", + "Its almost christmas!!!", + "1 more day!!!!!", + "Dont forget to put cookies and milk out tonight!!", + "@name do you like christmas??", + "Happy Christmas eve @name :)" + ], + "christmas_day": [ + "Merry Christmas @name!!", + "What did you get for christmas @name?", + "ITS CHRISTMASSSS!!!", + "Christmas party time!!", + "@name vibing with the christmas spirit", + "Cant believe its christmas!!", + "We need to have a christmas party @name!!", + "Merry christmas @name :))", + "Hope youre having an amazing christmas @name :)", + "I love winter so much @name", + "I hate when its cold outside @name", + "You need to put some warm clothes on @name", + "Wanna build a snowman @name?", + "Do you like christmas @name?" + ], + "new_years_eve": [ + "What are you doing for New Years @name?", + "New Years Eve party orrr?", + "We should do something for New Years eve today @name!", + "Happy New Years Eve @name", + "1 More day left in 2021 thank god", + "Whats your New Years resolution @name?" + ], + "new_years": [ + "Happy New Years @name!!!", + "Its New Years Day @name!", + "HAPPY NEW YEAR @name!!!", + "2022 lets fucking gooooooo", + "What are your goals for 2022 @name?" + ], + "valentines": [ + "Will you be my valentine @name?", + "I am so happy rn", + "God i am so lonely", + "I am Ceikrys illegitimate child", + "Be my valentine!", + "Lets sneak off somewhere and kiss @name", + "What are you doing for valentines day @name", + "Woah and Ceikry be kissin lowkey", + "In for a peg @name?", + "Woah got those roofies strapped", + "Why is my drink cloudy?", + "@name and woah be wrestlin", + "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", + "@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!", + "Woahs favourite game is red rocket", + "@name sexy af today", + "Happy Valentines day @name!!!", + "Happy Valentines day @name!!!", + "Happy Valentines day @name!!!", + "Happy Valentines day @name!!!", + "Happy Valentines day @name!!!" + ], + "easter": [ + "Happy Easter!!!", + "Happy Easter @name!!!", + "Bunny time", + "Wanna go look for easter eggs @name???", + "Find any easter eggs @name?", + "@name is the easter bunny!", + "Kermit is dating the easter bunny!", + "Easter is one of my favorite holidays", + "I heard there are easter eggs hidden around?", + "Easter is the only time you should put all of your eggs in one basket", + "I said hip hop do not stop", + "Jump jump jump around", + "@name how is your easter going?", + "I love easter!", + "@name stole my easter eggs!", + "Karma karma karma karma karma chameleon", + "@name!! @name!! what are you doing for easter??", + "The hare was a popular motif in medieval church art", + "I heard the easter bunny is hiding somewhere!", + "I wonder where i can find more eggs", + "@name how many eggs did you find?", + "@name lets go easter egg hunting!", + "@name like orange chocolate eggs", + "@name and woah know the easter bunny", + "@name did you know ceikry swallows eggs whole", + "Have an amazing easter @name!", + "Happy easter @name!", + "Hope you are having an amazing easter @name!!!!", + "Wooooooh easter!!!", + "@name loves easter too!", + "Who else loves easter like i do??", + "@name and i are going easter egg hunting", + "@name and i are going to look for the easter bunny!!", + "Hint for anyone who sees this you must dig above eagles peak" + ] +} diff --git a/Server/data/botdata/botnames.txt b/Server/data/botdata/botnames.txt index 8a2e6603f..22c5715a8 100644 --- a/Server/data/botdata/botnames.txt +++ b/Server/data/botdata/botnames.txt @@ -1,780 +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 -2jz420 +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 -3aboobs +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 -420kushmanxx +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 -6969 -6999999 -69AssClown96 +68 savage +6822ii +68pickleman +69 L OR D 69 +6942O 69KolaOlli73 -69endme69 -69penis -69snowgasm69 +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 -73Penis +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 -ASSEATHER69 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 @@ -783,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 @@ -831,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 @@ -928,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 @@ -1029,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 @@ -1084,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 @@ -1140,155 +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 Iron Butt +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 @@ -1296,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 @@ -1348,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 @@ -1409,75 +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 -Archaen420 Archahl +Archaic Wolf Archaiden199 Archanjel +Archas +Archavia +ArchbishopC +Archduke Btw +Archelon Archer ArcherYew Archie @@ -1489,6 +3323,9 @@ ArckenSphere Arclight Arcnan Arctic +Arctic Roach +Arctic Vaggy +ArcticCamel ArcticMank ArcticTitan Arcticas @@ -1498,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 @@ -1507,12 +3352,17 @@ Areimer0606 Arekusei Arena ArenaRebuild +Arend Nest +AreolaArnold Areolas Areoloth +Areow Ares +Ares 3060 Ares1701 AresArmy AresKnight +Arescoth Aretx Arezrazer Arfex @@ -1521,184 +3371,322 @@ 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 -Ashy6969 +Ashura Bakke +Ashwin09 +Ashy joey +AshyKneecapz Ashyy Asian -AsianGuyOSRS +AsianGrinder +Asics +Ask Oziach AskMeDolCare -AskingCard69 Askraf Askumi AslanMaximus Aslywyn +Asmir9990 Asorf Asos +Asot +Aspen +Asper Aspestia Asphyi -AssBuster21 +Asphyi Xate +Ass Balm +Ass Clams +Ass Drill +Ass Rocker +Ass locator AssClapping -AssCumPooPee -AssIsYummy +AssMcButt Assasindie73 +Assasintoad Assassinado -Assassxnz Assaulted -Asshungry -Assletics -Assload -Assmazing -Assmongie -Assy +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 @@ -1708,112 +3696,209 @@ Asumistuki Asuna Asunnn Asura +Asus bl +AsylumTRAV Asymmetry Asyn +Asynergy Aszalem +Aszard +Aszension At0mic -AtWorkOnOSRS +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 @@ -1825,7 +3910,14 @@ Avernic Avero Avertehs Avest09 +AvestGIM Avex +Avexaon +Avfc Holte +Avg Tom +AvgMG +AvgScape +Aviaatar Avian AvidTech Aviiation @@ -1834,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 @@ -1921,104 +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 -B0ngbutter 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 -BONEM69 -BONGSLAY3R +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 @@ -2027,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 @@ -2045,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 @@ -2147,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 @@ -2197,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 @@ -2258,43 +4804,71 @@ BastasRemmen Bastermate Bastiao Bastidaz +Bastin101 Baszist +Bat Fastard +Bat Kills Bat418 +BatChatillon BatFat Batbot101 Bateau BatedUrGonna Batesanator Batgirl -BathSalts420 +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 @@ -2303,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 @@ -2349,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 @@ -2384,400 +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 -BigdickusTra 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 -BlazingOSRS +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 @@ -2787,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 @@ -2861,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 @@ -2923,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 @@ -2969,82 +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 -Bong -BongClowd -BongJuice -BongRat420 -BongSong +Bonez III +Bonfire1 +Bonfire237 +Bong Lenis +Bong Quixote +Bong Squats +BongAppetite +BongSec +BongTokez +BongZiller +Bonglefin +Bongletopper +Bonglewongle +BongoBasse78 BongoRider -Bongsnboobs +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 @@ -3053,8 +6459,13 @@ Borgiie Boriix BoringName Boris +Boris King7 +Boris sfisgh +Borkiin BorkingCorp Born +Born Abroad +Born to Hodl BornToTrade BornTwoGrind BornofDragon @@ -3064,205 +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 -BowlzNBongz +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 -Britbong 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 @@ -3270,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 @@ -3310,89 +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 -Buckeyeman69 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 @@ -3401,104 +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 -BustyBabe420 +Busted Shaft +BusterBlader +Busternut +Bustinbibear +BustyBarry +BustySaurus +Busy Phat BusyDayToday BusyRightNow ButMuhMain +Buthole +Butinz +Butje Kef Butlergunner -Butt -ButtBot69 -ButtChugDoug -ButtChugFest -ButtHash -ButtHole -ButtSweatt -Buttbraid -Buttchin +Butt Clouds +Butt King +Butt Liquor +Butt Plog +ButtChugr +ButtExpert Butter +Butter Sock +ButterSirup ButterSyrup Butterbur Butters -Butterweed +Buttgers Butthxle -Buttlickeer ButtnCLICKER -Buttromper2 +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 @@ -3507,122 +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 -CUMBANDIT900 +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 @@ -3631,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 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 @@ -3775,6 +7891,7 @@ CaptainDredd CaptainLuker CaptainOboy CaptainObvio +CaptainWalt Captainshiny Captainzzz Captan @@ -3782,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 @@ -3827,6 +7962,7 @@ Carlyle Carmillae Carna Carnadova +CarneGrande Carnie CarnifexVeil Carnitas @@ -3835,94 +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 -CasualIron69 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 @@ -3930,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 @@ -4061,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 @@ -4270,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 @@ -4282,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 @@ -4351,243 +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 -Claud420 +Claudenstein Claudiu Claw Clawdius Clawsie +Clay Nasty +ClayNugget Claymor Claypops +Clayters Claytn Clayton Clazerbeam Clean +Clean Dishes CleanSleeve -Cleanbutt13 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 @@ -4596,356 +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 -CptMcDickin +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 -CriminaLoser +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 -CrumbzOSRS +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 -CumLover9000 -CumOnGuys -Cumbo -Cummo -Cummr -CummyTummy -Cumsquatch +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 @@ -4953,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 @@ -4988,230 +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 -DICKWORLD +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 -DTown420 +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 -DaddyDickems +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 @@ -5219,6 +10670,7 @@ Dalski Daltficiency Dalton Dalton4theft +Dalton_LB Daltonnn Dalundo Dalzii @@ -5226,62 +10678,134 @@ DamDude DamSplash Damage Damarquis +Damberson +Damen +Dameon xd +Dami +Dami-an Damiaen +Damian_Xx Damish Damlotz DammitHarry -Damn +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 Bud God +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 @@ -5296,33 +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 -DarKSsoul69 Darbinism Darc +Darc Sport Darcy Darda09 Daredevil595 @@ -5330,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 @@ -5470,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 @@ -5510,134 +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 -Deivukas 69 +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 @@ -5646,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 @@ -5661,9 +11518,12 @@ Dennisfr Dennispanda1 Denny Dennyispro +Denonweff DenouncedGod Denoxite +DenseLayer Denssoni +Densu DentalMental Dented Dentistry @@ -5671,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 @@ -5723,223 +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 -Devilbong420 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 -DickLovett +Dick Jones +DickNBallin +Dickbruiser +Dickeroo Dicko -DickyPops 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 @@ -5948,221 +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 -Dlck +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 -Docrates69 +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 @@ -6170,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 @@ -6211,97 +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 -Dreiriez_69 +Dremarial +Drenthe Drenz Drenzek Drenzi @@ -6312,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 @@ -6391,233 +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 -DunDiddly69 +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 -DwarfDicker 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 @@ -6625,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 @@ -6669,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 @@ -6692,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 @@ -6761,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 @@ -6821,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 @@ -6878,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 @@ -6907,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 @@ -7043,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 @@ -7078,10 +14389,16 @@ Espuky Espyria Esquelito Essencehour +Essex RS Essylle +Est Bellum Estafeta1 +Estar +Estebaan +Esteedee Esteeme Esteet +Esteffano 3d EsterKai Esterbrook Estetica @@ -7089,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 @@ -7111,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 @@ -7270,119 +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 -F1nduz69 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 -Falcodair69 +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 @@ -7395,158 +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 -Fart -Farting -Fartman -Fartmancer69 +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 -FatBoySwag69 +FatBurgerKid FatClock FatGreasyGuy -FatHogCumGod 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 @@ -7554,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 @@ -7605,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 @@ -7723,120 +15764,203 @@ Fireoman Fireskill Firesmash Firestarone -Fireweed1111 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 @@ -7844,84 +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 -FlutterButt 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 @@ -7934,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 @@ -8013,375 +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 -GWPH69 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 @@ -8389,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 @@ -8426,145 +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 -GayBowser -GayCum -GayDudeBirds -GayTradies -Gayim +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 @@ -8575,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 @@ -8623,309 +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 -Gilkrog69 +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 -GirlCantFart +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 -GoldHunter69 GoldTracerR1 Goldclaw30 Golden +Golden Acorn +Golden Dunes +Golden Eyes +Golden Power Golden2Aim GoldenArm647 -GoldenEye69 +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 @@ -8933,69 +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 -Gothic6669 +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 @@ -9003,179 +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 @@ -9184,12 +18767,19 @@ Gummibier Gummy GummyTushie Gumpie123 +Gumri +Gun +Gunalations Gunci +GundarV Gundrace Gundrak Gune Gunfire +Gunite Gunjamini +Gunman +Gunn a Gunna Gunnar Gunnawr @@ -9198,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 @@ -9357,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 @@ -9381,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 @@ -9410,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 @@ -9486,14 +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 @@ -9501,123 +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 -Heagerg69 +Headlines v2 +Headshotting +Headwipe +Healers +Healty Heard +Heard Chef Hearne +Heart Please Hearted Hearten +Heartlesdude Heartlock Heartquake Hearts @@ -9629,158 +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 +Hell ETC +Hell Kitt3n +Hell ffa HellAngel HellBlood Hella -HellaKush420 +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 +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 -Henkerddd420 +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 @@ -9788,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 @@ -9831,146 +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 -HitdeBongz 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 -Hobutt 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 @@ -9978,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 @@ -10017,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 @@ -10046,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 @@ -10227,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 @@ -10298,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 @@ -10323,131 +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 -Ibongtoke +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 @@ -10457,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 @@ -10545,18 +21731,33 @@ Imaginable Imagine Imakuni0 Imalooter +Iman103 +Imangry20 +Imanity +Imanubnoob +Imar Pussay Imasecretspy Imasin Imawizardm8 Imazamox Imblim -ImbuedFart +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 @@ -10565,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 @@ -10627,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 @@ -10648,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 @@ -10689,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 @@ -10844,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 @@ -10863,11 +22562,19 @@ IronOdum IronOldBoy IronOnPatch IronOrca +IronOreAgate IronOsiriss IronOxideMan +IronOxidizer +IronPangtar +IronParis +IronPeteMan IronPills IronPrice +IronPrickles +IronProf IronPumitaz +IronQben IronQli IronQueen IronRait @@ -10880,84 +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_69 +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 @@ -10965,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 @@ -11017,168 +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 -Jagt69 +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 @@ -11190,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 @@ -11258,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 @@ -11341,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 @@ -11565,86 +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 -Joe420buddy +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 -JoeshmoeOSRS 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 @@ -11653,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 @@ -11754,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 @@ -11802,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 @@ -11825,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 @@ -11869,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 @@ -11885,67 +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 -KARANISGAY +KAT alyzt +KB1988 +KBARAKAT +KBX0 +KC Chiefz +KC M0 +KC8 +KCRB +KChiefn KDOG1419 +KDOT_OSRS +KDark22 KDrizzy KEEPOUNDING KEEPTHATSHIT @@ -11953,76 +24924,164 @@ KEEZY10 KEIF KEKDUBYA KEKLERO -KENO_GUY69 +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 -KaiserOSRS +KaiserBruno KaiserRS Kaister +Kaitaia KaitoSun Kaiven -Kakashi69 +Kaizen Reign +Kaizen Soji +KaizennxX +Kaizloh +Kaizooka +Kajbanan +Kajgils Far +Kakariiiiick +Kakarrot Kake Kaker Kakerandeli @@ -12031,87 +25090,144 @@ Kakoji Kakor Kaksitoista Kaku +Kaku bakudan Kakua +Kakuri +Kal9000 Kalakoi -Kalan420 +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 -Kapiaine69 +Kapiling +Kapitalisti Kapitein Kapiten Kapkeik +Kapkkha Kaporkchop -Kappa Dog420 +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 @@ -12119,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 @@ -12194,37 +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 -KazOsrs +Kayzo Kazatan1245 Kazave Kazaz +Kazcade95 Kazching Kaze8HerOut +Kazify +Kazuma Moon +Kazunni Kazuuhiro -Kbdick +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 @@ -12233,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 @@ -12272,11 +25484,14 @@ Kelgone Kelh Kellarivelho Keller +Kells o_o Kelly Kellycollin2 Kelohonka Keloo +Kelps Kelso523 +Kelso561 Keltik KeltonThaGod Keltuzazz @@ -12285,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 @@ -12393,10 +25684,15 @@ KhatrickZain Khayri Demon Khazad Khazad Dum +Khazanedar Khazu +Khealim Khest +Khint +Khirean Khiru Khleb +KhooNBust Khoosh Khoppa Khornebull @@ -12404,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 @@ -12421,239 +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 -KikRox420 +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 -Kittygamer69 +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 @@ -12665,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 @@ -12743,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 @@ -12806,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 @@ -12919,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 @@ -12939,142 +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 -Kush420s +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 @@ -13082,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 @@ -13167,70 +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 -LarryOSRS 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 @@ -13239,97 +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 -LeB0ng +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 @@ -13338,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 @@ -13356,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 @@ -13373,116 +27706,230 @@ Lemako Lemillion141 Lemiy Lemke +Lemmelleni Lemmy LemmyThePerv Lemon Lemon5000123 -LemonLube420 +Lemon9000000 +LemonTart +Lemonade max +Lemonguin Lemonmooffin +Lemonowo Lemonrider +Lemony Earl Lemonz +Lemosgomes Lemphys Lempsu Lemurcow Lemuria88 Lemursnore -Lenfart +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 -LietuvosOSRS +Lieve +Lieven Lif3 Lifal Life +Life Road +LifeInsAgent LifeIsBaked LifeIsRockie Lifeform Lifehunt +Lifeisnow13 Lifeliners +Lifelulz Lifes Lifes12Rules Lifesteal @@ -13490,249 +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 -LilDilly420 +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 -Llama6969 +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 @@ -13740,66 +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 -LordFartquad +LordCorreia +LordDanko +LordDaxel +LordEpic69 +LordFarquxd LordFoxy LordGuthanPK +LordJRazE +LordJuba LordLambo91 LordMullett LordNorden -LordOSRS 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 -Lores420 +Lorenzoh sr +Lorenzokazoo +Lorithean Lorkaa Lorki +LorqueldIM +Lorre46 +Lorry +LortJob Lorttomies99 +Lorvikatari Lorwic LosAngeles Lose Losel -Loserscape +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 @@ -13808,260 +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 -Lust +Lussen1 +Lustwaffle Lusu LusyTheGoat Lutha Luthien +Luthien T2 Luthors Luthstorm Lutinrouge Luucy Luud -Luuk420 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 @@ -14074,142 +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 -Machtigegay +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 -Madi69 Madlaina Madmaxie +Madness Max +Madodee Madona +Madra Madrock01 MadsG Madsenn Madsermad +Madshatter71 Madskillz756 Madslasher30 +Madslax2 Madsosaur Madula Madvantage @@ -14219,49 +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 -Magepkmega69 +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 @@ -14269,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 @@ -14371,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 @@ -14392,96 +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 @@ -14491,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 @@ -14532,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 @@ -14549,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 @@ -14581,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 @@ -14627,30 +30273,52 @@ MatthewDK MatthewRS Matthewwww Matthidan +Matthis +Mattias Mattice +Mattie osrs Mattie43 -Mattitude420 +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 @@ -14660,81 +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 -MayhemOSRS 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 @@ -14742,206 +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 -Meme420Dank +Meme Loord +MemeDoge111 +MemeVendor +Memedalorian +Memem3 Memeologist Memeshake +Memmor Memory MemoryCard MemoryWorm -MenDick +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 @@ -14950,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 @@ -15001,104 +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 -Mili696969 +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 @@ -15107,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 @@ -15145,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 @@ -15243,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 @@ -15265,58 +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 +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 @@ -15325,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 @@ -15404,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 @@ -15465,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 @@ -15542,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 @@ -15579,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 @@ -15598,77 +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 @@ -15680,7 +32554,9 @@ Mugarooni Mugen Mugge Muggerman3 +Muggled Muggles +Muggtjuven Mugrub Mugy Mugzy @@ -15688,118 +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 -MyDickLite +MyCtyNeedsMe +MyDuck MyElyNow MyFitPal +MyHandyMan MyIiu MyLastGo MyLoveFaith +MyMercy MyNAm3BubBA MyNameIs MyNameIsCole MyNameIsKent +MyNameIsTina MyNameIsTrev MyOSAccount -MyPenisBHuge +MyPetRicky MyProject MyRifleMyGun +MyRight Nut MySeed +MySkillsKill MyStinkHurts MyTakao +MyWanderlust MyWyvernAlt Myams Myat @@ -15808,89 +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 -N3ggertr3g +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 @@ -15898,194 +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 -Narris69 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 -NastyFreak69 +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 @@ -16099,155 +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 -NickOSRS +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 @@ -16255,27 +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 -Nigester69 Nighlist Night +Night Elf45 +Night Shade NightNurse +Night_Furor Nightbass Nighter Nightmare +Nightmare CX Nightmare028 NightmareSHW Nightolas @@ -16283,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 @@ -16458,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 @@ -16491,436 +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 -Norweed +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 -NutsToButts +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 -OSRS07 -OSRSQuests -OSRSZ3US -OSRSZach -OSRSlayer +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 -Oetlul69 +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 -Ondra969 +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 @@ -16933,205 +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 -OnlyHalfGay +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 -OrganicWeed 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 -OsRs +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 +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 @@ -17142,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 @@ -17299,61 +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 @@ -17361,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 @@ -17394,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 @@ -17461,6 +36258,7 @@ PathTracker Pathogen Pathways Patio +Patious Patmantheman PatoVelaz9 Patojo @@ -17469,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 @@ -17489,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 @@ -17503,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 @@ -17550,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 @@ -17583,106 +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 -PeterButts +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 @@ -17690,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 @@ -17747,91 +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 -PigDOG 669 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 @@ -17842,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 @@ -17942,218 +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 -Poentje420 +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 -PoopDick43 -PoopLoser420 -Poopnipple69 +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 -PotBurner420 +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 -Pounder420 +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 @@ -18161,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 @@ -18177,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 @@ -18220,19 +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 -ProfGay +Prof_Plums +Prof_Snack ProfanityBox Professa ProfessorBot @@ -18240,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 @@ -18293,224 +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 -Purplesweeds +Purpleowns Purppurainen +Purpsauce PurrPig +Purse seine PursuantACE Pursuedd Purtherapist Pusat +PuscSquirt Puse PuseSlayer Pushgold +Pushovermage +Pushy Bubes Puss -Pussy is Op -PutItInDeeep +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 -Pyro420Kush +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 -QcumBear Qeassaris +QeeQ Qego +Qeko +Qeln Qemi +Qeni +Qenobii +Qi Kua Mai +Qi Ling QiangxD +Qilat +Qiok QlKNQFRDNUQF +Qlhp xD Qlioux Qmain QnBumbleBee @@ -18518,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 @@ -18557,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 @@ -18689,232 +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 -RageOSRS +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 @@ -18924,121 +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 -ReallyBongs +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 @@ -19047,12 +39396,16 @@ Reduced Reductive Redus Redux +Redux Lux Redz Redza14 +Ree Mastered Reeb Reececup Reecesoa +Reecie ReedBadass +Reee Reeebow ReeeeZ Reeeps @@ -19063,7 +39416,10 @@ ReefyReefer Reel_Arphios Reels Reemov3d +Reer Reeyu +Reeze Tech +Reflection44 Refleksfrode Refleksi Refluxerino @@ -19072,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 @@ -19247,31 +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 -Rick420blaze +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 @@ -19280,168 +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 -Ripsnorter69 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 -RobC69 +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 @@ -19450,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 @@ -19528,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 @@ -19608,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 @@ -19671,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 @@ -19780,78 +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 -SH1T -SH1TGAME +SGTX SH333S +SH5 SHAD0WBANN +SHAGED +SHANK SAMA SHELDOR +SHERMnNATOR +SHElD-HEDA SHIA +SHIA_WIRE SHIDDED0N SHIIIELDS SHINY @@ -19859,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 @@ -19937,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 @@ -20000,255 +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 -SatanicFart +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 -Sbeatz420 +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 @@ -20257,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 @@ -20321,81 +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 -Scrotum +Scrotetseg Scrruff Scrub -ScrubOfOsrs 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 @@ -20403,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 @@ -20426,224 +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 -Sexy -SexyBish +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 @@ -20654,236 +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 -Shiz +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 -Sigg69 +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 @@ -20893,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 @@ -20985,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 @@ -21035,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 @@ -21105,6 +43413,7 @@ Skold Skolder Skolzera SkoobiDooby +Skooier Bibs SkoomaPls Skopix Skopusnik @@ -21114,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 @@ -21137,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 @@ -21185,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 @@ -21222,6 +43613,8 @@ SlayerKingv2 SlayerRyan Slayerburger Slayerdog +Slayerg1g +SlayerisGame Slayerknox Slayerman Slayers @@ -21231,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 @@ -21254,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 @@ -21436,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 @@ -21501,72 +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 -Sohanisgay 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 @@ -21575,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 @@ -21659,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 @@ -21679,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 @@ -21811,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 @@ -21854,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 @@ -21872,194 +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 -StabbingFart +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 -StarDawgg420 +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 -Stebs69 +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 @@ -22067,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 @@ -22131,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 @@ -22148,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 @@ -22270,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 @@ -22321,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 @@ -22378,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 @@ -22387,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 @@ -22424,353 +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 -TacticalFart 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 -TaintSaint69 +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 @@ -22779,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 @@ -22838,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 @@ -22962,179 +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 -TheBong -TheBong2 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 @@ -23146,52 +47492,78 @@ TheFunkyHomo TheFunnyLove TheFuzzball TheGaGee -TheGayFarmer +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 @@ -23199,48 +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 @@ -23248,315 +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 -Thorbuttuxth 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 -TinyCat69 +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 @@ -23569,126 +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 -Toominator69 +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 @@ -23698,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 @@ -23733,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 @@ -23770,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 @@ -23783,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 @@ -23871,11 +48821,15 @@ Trickster122 Tricomb Trictagon1 Trictagon2 +Tricx +Trid3nt Trieuce Triforce13 Trigamy +TriggaTriggs Triggadd Triggahappy +Trigger Me TriggeredMot Triggeredm3 Triggering @@ -23885,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 @@ -23908,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 @@ -23993,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 @@ -24021,21 +49089,37 @@ Tulip Tulipssonmyd Tully03 Tulppu +Tumbdownlog TumbleDore +TumblingSand +Tumeken Tay +TumekenToday +Tumma Paahto +Tumms +Tumnus Tumppe Tuna -Tunafish69 +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 @@ -24043,98 +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 -Turd +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 @@ -24142,221 +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 -TylerOSRS +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 @@ -24365,159 +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 @@ -24527,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 @@ -24539,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 @@ -24621,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 @@ -24827,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 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 @@ -25092,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 @@ -25137,119 +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 -WeedDoctor -WeedOverWax +Weed B0Y +Weed Barrage +Weed Farm +Weed OG +Weed PhD +Weed Strains +Weed VIII WeedWizzard -WeedleSmoker +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 -WetAssPenis +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 @@ -25260,263 +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 @@ -25525,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 @@ -25559,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 @@ -25583,11 +52213,17 @@ Wrath0fMath Wrathchildxd Wrathinsea Wrav +WreXham AFC +Wreck These +WreckAndRoll Wreckage Wreckanism +Wrecked Wreckfull Wreckin4Days Wreckon +Wreckonize +Wrecky Wreckzu Wrekdum Wrektem @@ -25595,141 +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 -XB0NG 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 -XNublord69X 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 @@ -25739,15 +52517,25 @@ Xerona Xeroscape Xerosenkio Xeroso +Xerphias Xertrius +Xerxe Xestox -Xev0n420 +Xetarillos +Xewne +Xeyler Xfebruari23X Xflowerz1 Xgen2004 Xghostbx XgodofironX +Xhaloz +Xhq +Xi Bogan iX +Xianerth Xiang +Xiang Ying +Xiao Wei Xick XicoDoAnzoL Xidos @@ -25755,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 @@ -25779,320 +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 -YuriandWeed +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 @@ -26102,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 @@ -26147,226 +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 @@ -26374,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 @@ -26449,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 @@ -26505,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 @@ -26571,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 @@ -26613,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 @@ -26696,350 +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 -baishunpu69 +baile y baitmem8 +baj69 +bajadam bajj +bak3d bean5 bakareru bakesome bakkerbtw +bakplaat11 balding +balisongg +balkare ballenbak +ballerbebbo +balsson +bam bam-BA-lamb -bambii69 +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 -beberfan69 +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 cat69 +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 -bong +bonderoeven +bonelessrice +bonfire bongohonkers -bongs -bongz 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 -botter69 +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 @@ -27048,51 +55210,108 @@ budge1 budhato buds buff +buff T O N Y +buff boobies +buff outlaw +bug salesman +bugeye freak bugsijs -buh6969 +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 -buttboy +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 -cakefartsbtw +cakemusclez cakepillows +cala mity +calamity meg calgore10 +calisme call +call me Q callbackc4ll calliott14 +callmedaddie callmeqel callmeson callum23394 @@ -27102,150 +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 -chongsbong 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 @@ -27253,60 +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 -cum6 +cumstrosity cunnys +cupma cupofpeepee cups cursed_angeI @@ -27314,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 @@ -27358,129 +55827,251 @@ 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 deadfrompvm deadfrontier -deadlyfeet69 deadlymoth -deadlyone69 deadmau5 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 -digdoge69 +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 @@ -27488,172 +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 -dualipafan69 +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 @@ -27662,63 +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 -evengayeruim 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 @@ -27726,65 +56545,123 @@ famousdavies famwell fan3to fancybunny84 -fart -fartass +fanof +fanook kill +fansh +fapital one +fapyqt +farmerman111 +fart taker farth4lyf -fartsogood +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 -fergus69x 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 @@ -27792,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 @@ -27860,150 +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 -gay4gp -gaypoika08 +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 -godsmack69 +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 @@ -28011,311 +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 -hsgs69 +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 @@ -28325,6 +57736,7 @@ iPink iPlugg iPod iPop +iPray_Guthix iPresident iPunchCones iPure @@ -28337,9 +57749,14 @@ iQuittedCiao iRNGizzed iRekt iRepToronto +iReue +iRev Man iRezlo +iRoN cUj0 iRoNmAnSdUmB +iRonAllot iRunTh3World +iRunzo iRycoda iSaran iScape @@ -28349,116 +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 -idkhow2osrs +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 @@ -28469,126 +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 -imm0rtalb0ng +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 -ionly420XD +ioncolliderz ionlypickadc +ionman iorcsrox -ipickweed +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 @@ -28597,51 +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 -johnbie69 johndalton95 johnnyandtam +johnsmain johnstaLoL joje9 joji @@ -28649,7 +58434,10 @@ jojojo357 jokaranpampu jokemyster jokerpoker +jokkefar jokr +jolle rmm +jon ko jonehehe jones jonk33n @@ -28657,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 @@ -28714,76 +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 -karambaa69 +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 -kechhunter69 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 @@ -28791,102 +58698,169 @@ 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 -ksap69 ksdnklowdss ksiadz ksko @@ -28894,159 +58868,344 @@ ksubbi ktsmo kudryavtseva kugelencas +kujan2 kukerino +kukko soosi kukrishikari kulers +kulers bsk kulju kulso kulwreck +kumiko okada +kurasaki21 kurdikana -kurmeli69 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 -lampen69 +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 @@ -29055,196 +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 -lordbong +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 @@ -29253,13 +59601,21 @@ 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 @@ -29272,58 +59628,103 @@ malmstrom malte313 mamadou11 mamatriceps -mangay123 +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 @@ -29331,233 +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 -mrkarl69 +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 -nicecumshot +nice to game +nicelah niceslime nick +nick at nite nickdv nickel nickfx +nicky +nicnad nico nicoxpico +nieve nooooo +nightmareram nigriV nikhil nikler @@ -29569,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 @@ -29592,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 @@ -29614,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 @@ -29669,104 +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 -osgp -oskaren -osrs -osrstimex +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 @@ -29778,19 +60684,29 @@ padq pagakoning pahe painter2020 +pajiaobin pakgg +pakkohoito pakoonjuokse +pakwatch palacePier paladylan panda pandahands +pandas flail pandascapes -pandobaby69 +panel v2 +panelupgrade +papa jjinx papaisthatu papatec paper +paperbutter parB para1911 +parsakeito +party-joiker +partyinferno partymonkey partypooyan partypossum @@ -29798,6 +60714,8 @@ pashol pasismi paskapylly6 pastry +pat summitt +patbeerpants patches4623 patnazNkryme patriciogmz @@ -29805,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 @@ -29815,155 +60742,280 @@ peajy peakyberry pebbles256 pecemker99 -pecker +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 -plebhunter69 +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 -poni69 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 -potweedsmoke 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 @@ -29971,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 @@ -30037,29 +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 -raterd69 +ratirl +ratirlAlfred rats +rattiyanee ratzzzzzzzzz +rautamon +raves m4ck raving ravioliwren rawheadshot +rawkneedong rawr rawrsmashh rayhoon @@ -30067,338 +61209,663 @@ 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 reitan2k -rekkerboi420 rekordlaunh1 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 -sCUM +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 -saggen69 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 -sexhaver420x +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 -skubasteve69 +sks110 skuccy +skuirk +skunk nugs +skunkpaste skunkskillzz skyE skyelar27 @@ -30406,53 +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 -slayz420 +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 -smalpenis +smalsey smasher12121 +smasher95 smaugs -smegmafarts +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 @@ -30460,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 @@ -30604,64 +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 -swearwords69 +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 @@ -30669,134 +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 -theboobguy 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 -thestar420 +thesmurfster +thetibo thetoday thewooshh +they gave up thezucc thhxd +thicassglass +thicc dady thicccc thicchode thiever05 -thilygoose69 +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 @@ -30807,189 +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 -triniboy6969 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 -tunadickrick +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 @@ -30998,316 +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 -w33nerlord69 +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 -wantUR69fart wapwap warcauser33 +warcraftgame +wardongs warestoteles +warface125 warm +warm cuddles +warm is vuur +warmclouds warrior warteryyy +waseem76786 +waste of alt watch -watercum +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 -weebgod420 -weedsmoke163 +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 -wrighty420 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 @@ -31321,185 +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 -yogurtnuts69 +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 @@ -31507,36 +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 -zuuzieosrs +zw zwah zwint zwolle zwqpDEOXl23 +zxpf zyeetz +zygarde92 zygis323 +zykez zymaa zymz zysuna +zzad zzai +zzben zzero -zzigma \ No newline at end of file +zzigma +zzzJonno diff --git a/Server/data/botdata/ge_bot_appearances_and_equipment.json b/Server/data/botdata/ge_bot_appearances_and_equipment.json new file mode 100644 index 000000000..a4a1bef50 --- /dev/null +++ b/Server/data/botdata/ge_bot_appearances_and_equipment.json @@ -0,0 +1,68 @@ +{ + "noob": [ + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"3","id":"1265"},{"amount":"1","charge":"1000","slot":"5","id":"1171"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"0"},{"color":"0","look":"11"},{"color":"16","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"34"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1169"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"9183"},{"amount":"1","charge":"1000","slot":"4","id":"1131"},{"amount":"1","charge":"1000","slot":"7","id":"2495"},{"amount":"1","charge":"1000","slot":"9","id":"2491"},{"amount":"1","charge":"1000","slot":"10","id":"88"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"106"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1163"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"5698"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"3105"}]}, + {"appearance":{"appearance_cache":[{"color":"9","look":"266"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"3","id":"1333"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"11019"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"2910"},{"amount":"1","charge":"1000","slot":"1","id":"10499"},{"amount":"1","charge":"1000","slot":"2","id":"552"},{"amount":"1","charge":"1000","slot":"3","id":"861"},{"amount":"1","charge":"1000","slot":"4","id":"544"},{"amount":"1","charge":"1000","slot":"7","id":"2497"},{"amount":"1","charge":"1000","slot":"9","id":"7458"},{"amount":"1","charge":"1000","slot":"10","id":"2904"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"1007"},{"amount":"1","charge":"1000","slot":"3","id":"1381"},{"amount":"1","charge":"1000","slot":"4","id":"426"},{"amount":"1","charge":"1000","slot":"7","id":"428"}]}, +{"appearance":{"appearance_cache":[{"color":"20","look":"267"},{"color":"0","look":"12"},{"color":"0","look":"18"},{"color":"0","look":"31"},{"color":"4","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"3","id":"6908"},{"amount":"1","charge":"1000","slot":"4","id":"1135"},{"amount":"1","charge":"1000","slot":"7","id":"428"}]}, +{"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"6","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1161"},{"amount":"1","charge":"1000","slot":"3","id":"1287"},{"amount":"1","charge":"1000","slot":"4","id":"1123"},{"amount":"1","charge":"1000","slot":"5","id":"1199"},{"amount":"1","charge":"1000","slot":"7","id":"1073"}]}, +{"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1153"},{"amount":"1","charge":"1000","slot":"3","id":"1363"},{"amount":"1","charge":"1000","slot":"4","id":"6184"},{"amount":"1","charge":"1000","slot":"5","id":"1540"},{"amount":"1","charge":"1000","slot":"7","id":"6185"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11021"},{"amount":"1","charge":"1000","slot":"1","id":"4411"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"1333"},{"amount":"1","charge":"1000","slot":"4","id":"11020"},{"amount":"1","charge":"1000","slot":"5","id":"1185"},{"amount":"1","charge":"1000","slot":"7","id":"11022"},{"amount":"1","charge":"1000","slot":"10","id":"11019"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1949"},{"amount":"1","charge":"1000","slot":"1","id":"1007"},{"amount":"1","charge":"1000","slot":"3","id":"1381"},{"amount":"1","charge":"1000","slot":"4","id":"426"},{"amount":"1","charge":"1000","slot":"5","id":"1540"},{"amount":"1","charge":"1000","slot":"7","id":"428"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10774"},{"amount":"1","charge":"1000","slot":"1","id":"10069"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"1333"},{"amount":"1","charge":"1000","slot":"4","id":"1125"},{"amount":"1","charge":"1000","slot":"7","id":"1077"},{"amount":"1","charge":"1000","slot":"9","id":"2997"},{"amount":"1","charge":"1000","slot":"10","id":"7114"}]} + + ], + "intermediate": [ + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"3","id":"1265"},{"amount":"1","charge":"1000","slot":"5","id":"1171"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"1"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"2637"},{"amount":"1","charge":"1000","slot":"1","id":"6568"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1305"},{"amount":"1","charge":"1000","slot":"4","id":"3140"},{"amount":"1","charge":"1000","slot":"5","id":"6524"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"9","id":"3060"},{"amount":"1","charge":"1000","slot":"10","id":"11732"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"1"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11674"},{"amount":"1","charge":"1000","slot":"1","id":"10720"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"1387"},{"amount":"1","charge":"1000","slot":"4","id":"8839"},{"amount":"1","charge":"1000","slot":"7","id":"8840"},{"amount":"1","charge":"1000","slot":"9","id":"8842"},{"amount":"1","charge":"1000","slot":"10","id":"11732"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"261"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"9805"},{"amount":"1","charge":"1000","slot":"4","id":"10348"},{"amount":"1","charge":"1000","slot":"10","id":"1837"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"261"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10392"},{"amount":"1","charge":"1000","slot":"1","id":"1007"},{"amount":"1","charge":"1000","slot":"2","id":"11113"},{"amount":"1","charge":"1000","slot":"3","id":"1387"},{"amount":"1","charge":"1000","slot":"4","id":"1117"},{"amount":"1","charge":"1000","slot":"5","id":"1173"},{"amount":"1","charge":"1000","slot":"7","id":"1075"},{"amount":"1","charge":"1000","slot":"10","id":"4129"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"3755"},{"amount":"1","charge":"1000","slot":"1","id":"2412"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1383"},{"amount":"1","charge":"1000","slot":"4","id":"4091"},{"amount":"1","charge":"1000","slot":"7","id":"4093"},{"amount":"1","charge":"1000","slot":"9","id":"7462"},{"amount":"1","charge":"1000","slot":"10","id":"10696"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9755"},{"amount":"1","charge":"1000","slot":"1","id":"9754"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1275"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"88"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"106"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11665"},{"amount":"1","charge":"1000","slot":"1","id":"9807"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"13290"},{"amount":"1","charge":"1000","slot":"4","id":"8839"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"8840"},{"amount":"1","charge":"1000","slot":"9","id":"8842"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"106"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9672"},{"amount":"1","charge":"1000","slot":"1","id":"9783"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"9674"},{"amount":"1","charge":"1000","slot":"7","id":"2497"},{"amount":"1","charge":"1000","slot":"9","id":"7458"},{"amount":"1","charge":"1000","slot":"10","id":"3105"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"106"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10828"},{"amount":"1","charge":"1000","slot":"1","id":"9763"},{"amount":"1","charge":"1000","slot":"2","id":"1725"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"8850"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"3105"}]}, + {"appearance":{"appearance_cache":[{"color":"9","look":"266"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10828"},{"amount":"1","charge":"1000","slot":"1","id":"2414"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"9802"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"6182"},{"amount":"1","charge":"1000","slot":"1","id":"4355"},{"amount":"1","charge":"1000","slot":"2","id":"1478"},{"amount":"1","charge":"1000","slot":"3","id":"9185"},{"amount":"1","charge":"1000","slot":"4","id":"544"},{"amount":"1","charge":"1000","slot":"7","id":"2497"}]} , + {"appearance":{"appearance_cache":[{"color":"1","look":"1"},{"color":"0","look":"10"},{"color":"14","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"20","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"10071"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"8872"},{"amount":"1","charge":"1000","slot":"9","id":"1059"},{"amount":"1","charge":"1000","slot":"10","id":"88"}]}, +{"appearance":{"appearance_cache":[{"color":"20","look":"278"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1149"},{"amount":"1","charge":"1000","slot":"1","id":"6568"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1305"},{"amount":"1","charge":"1000","slot":"4","id":"2623"},{"amount":"1","charge":"1000","slot":"5","id":"6524"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, + {"appearance":{"appearance_cache":[{"color":"20","look":"1"},{"color":"0","look":"14"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"4413"},{"amount":"1","charge":"1000","slot":"3","id":"1271"},{"amount":"1","charge":"1000","slot":"4","id":"1123"},{"amount":"1","charge":"1000","slot":"7","id":"1073"}]}, +{"appearance":{"appearance_cache":[{"color":"20","look":"1"},{"color":"0","look":"14"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"3751"},{"amount":"1","charge":"1000","slot":"1","id":"10499"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"546"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"548"},{"amount":"1","charge":"1000","slot":"10","id":"3105"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"14120"},{"amount":"1","charge":"1000","slot":"2","id":"9470"},{"amount":"1","charge":"1000","slot":"3","id":"1305"},{"amount":"1","charge":"1000","slot":"4","id":"10822"},{"amount":"1","charge":"1000","slot":"5","id":"1540"},{"amount":"1","charge":"1000","slot":"7","id":"10824"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1153"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4153"},{"amount":"1","charge":"1000","slot":"4","id":"1115"},{"amount":"1","charge":"1000","slot":"7","id":"1067"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"3751"},{"amount":"1","charge":"1000","slot":"1","id":"6568"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"5698"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"8847"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"4131"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1163"},{"amount":"1","charge":"1000","slot":"2","id":"3853"},{"amount":"1","charge":"1000","slot":"3","id":"1373"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"1079"}]} , + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11021"},{"amount":"1","charge":"1000","slot":"2","id":"3853"},{"amount":"1","charge":"1000","slot":"3","id":"1373"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"5","id":"1201"},{"amount":"1","charge":"1000","slot":"7","id":"1079"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"5","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11021"},{"amount":"1","charge":"1000","slot":"1","id":"14081"},{"amount":"1","charge":"1000","slot":"2","id":"3853"},{"amount":"1","charge":"1000","slot":"3","id":"1373"},{"amount":"1","charge":"1000","slot":"4","id":"6184"},{"amount":"1","charge":"1000","slot":"7","id":"6185"},{"amount":"1","charge":"1000","slot":"10","id":"3107"}]}, +{"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"6","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"6182"},{"amount":"1","charge":"1000","slot":"1","id":"10636"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1371"},{"amount":"1","charge":"1000","slot":"4","id":"10633"},{"amount":"1","charge":"1000","slot":"5","id":"3122"},{"amount":"1","charge":"1000","slot":"7","id":"6181"}]}, +{"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"16"},{"color":"6","look":"18"},{"color":"0","look":"27"},{"color":"0","look":"84"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"10071"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1381"},{"amount":"1","charge":"1000","slot":"10","id":"88"}]} + + ], + "veteran": [ + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1038"},{"amount":"1","charge":"1000","slot":"1","id":"6570"},{"amount":"1","charge":"1000","slot":"2","id":"10360"},{"amount":"1","charge":"1000","slot":"3","id":"3053"},{"amount":"1","charge":"1000","slot":"4","id":"3140"},{"amount":"1","charge":"1000","slot":"5","id":"1171"},{"amount":"1","charge":"1000","slot":"7","id":"10394"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"4","look":"48"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1038"},{"amount":"1","charge":"1000","slot":"1","id":"6570"},{"amount":"1","charge":"1000","slot":"2","id":"10360"},{"amount":"1","charge":"1000","slot":"3","id":"3053"},{"amount":"1","charge":"1000","slot":"4","id":"3140"},{"amount":"1","charge":"1000","slot":"5","id":"1171"},{"amount":"1","charge":"1000","slot":"7","id":"10394"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"4","look":"48"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9950"},{"amount":"1","charge":"1000","slot":"1","id":"6570"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"7668"},{"amount":"1","charge":"1000","slot":"4","id":"2503"},{"amount":"1","charge":"1000","slot":"7","id":"2497"},{"amount":"1","charge":"1000","slot":"9","id":"5556"},{"amount":"1","charge":"1000","slot":"10","id":"11732"},{"amount":"293","charge":"1000","slot":"13","id":"877"}]}, + {"appearance":{"appearance_cache":[{"color":"12","look":"261"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"9805"},{"amount":"1","charge":"1000","slot":"4","id":"10348"},{"amount":"1","charge":"1000","slot":"10","id":"1837"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"9807"},{"amount":"1","charge":"1000","slot":"2","id":"1656"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"9","id":"7462"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9758"},{"amount":"1","charge":"1000","slot":"1","id":"9757"},{"amount":"1","charge":"1000","slot":"2","id":"1656"},{"amount":"1","charge":"1000","slot":"3","id":"1387"},{"amount":"1","charge":"1000","slot":"4","id":"10378"},{"amount":"1","charge":"1000","slot":"5","id":"1183"},{"amount":"1","charge":"1000","slot":"7","id":"10380"},{"amount":"1","charge":"1000","slot":"9","id":"7462"},{"amount":"1","charge":"1000","slot":"10","id":"10696"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10828"},{"amount":"1","charge":"1000","slot":"1","id":"10635"},{"amount":"1","charge":"1000","slot":"2","id":"9470"},{"amount":"1","charge":"1000","slot":"3","id":"9185"},{"amount":"1","charge":"1000","slot":"4","id":"10386"},{"amount":"1","charge":"1000","slot":"5","id":"3842"},{"amount":"1","charge":"1000","slot":"7","id":"10388"},{"amount":"1","charge":"1000","slot":"10","id":"1837"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"3"},{"color":"0","look":"16"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9755"},{"amount":"1","charge":"1000","slot":"1","id":"9754"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"1275"},{"amount":"1","charge":"1000","slot":"4","id":"1127"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"10","id":"88"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9764"},{"amount":"1","charge":"1000","slot":"1","id":"9763"},{"amount":"1","charge":"1000","slot":"2","id":"1704"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"108"},{"color":"3","look":"34"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9814"},{"amount":"1","charge":"1000","slot":"1","id":"10662"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"16","look":"18"},{"color":"0","look":"108"},{"color":"3","look":"34"},{"color":"16","look":"38"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"9749"},{"amount":"1","charge":"1000","slot":"1","id":"10662"},{"amount":"1","charge":"1000","slot":"2","id":"6585"},{"amount":"1","charge":"1000","slot":"3","id":"5016"},{"amount":"1","charge":"1000","slot":"4","id":"13858"},{"amount":"1","charge":"1000","slot":"5","id":"7053"},{"amount":"1","charge":"1000","slot":"7","id":"13861"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"11718"},{"amount":"1","charge":"1000","slot":"1","id":"10499"},{"amount":"1","charge":"1000","slot":"2","id":"87"},{"amount":"1","charge":"1000","slot":"3","id":"9185"},{"amount":"1","charge":"1000","slot":"4","id":"11720"},{"amount":"1","charge":"1000","slot":"5","id":"11283"},{"amount":"1","charge":"1000","slot":"7","id":"11722"},{"amount":"1","charge":"1000","slot":"9","id":"7458"},{"amount":"1","charge":"1000","slot":"10","id":"6920"}]}, + {"appearance":{"appearance_cache":[{"color":"1","look":"2"},{"color":"0","look":"13"},{"color":"1","look":"18"},{"color":"0","look":"28"},{"color":"0","look":"33"},{"color":"16","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"6918"},{"amount":"1","charge":"1000","slot":"1","id":"9771"},{"amount":"1","charge":"1000","slot":"2","id":"87"},{"amount":"1","charge":"1000","slot":"3","id":"1387"},{"amount":"1","charge":"1000","slot":"4","id":"6916"},{"amount":"1","charge":"1000","slot":"5","id":"11283"},{"amount":"1","charge":"1000","slot":"7","id":"6924"},{"amount":"1","charge":"1000","slot":"9","id":"6922"},{"amount":"1","charge":"1000","slot":"10","id":"6920"}]}, +{"appearance":{"appearance_cache":[{"color":"0","look":"45"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"6","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"2637"},{"amount":"1","charge":"1000","slot":"1","id":"9769"},{"amount":"1","charge":"1000","slot":"2","id":"1478"},{"amount":"1","charge":"1000","slot":"3","id":"4151"},{"amount":"1","charge":"1000","slot":"4","id":"10460"},{"amount":"1","charge":"1000","slot":"5","id":"3842"},{"amount":"1","charge":"1000","slot":"7","id":"10394"},{"amount":"1","charge":"1000","slot":"9","id":"2912"},{"amount":"1","charge":"1000","slot":"10","id":"2904"}]}, + {"appearance":{"appearance_cache":[{"color":"0","look":"45"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"6","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"8903"},{"amount":"1","charge":"1000","slot":"1","id":"9807"},{"amount":"1","charge":"1000","slot":"2","id":"6585"},{"amount":"1","charge":"1000","slot":"3","id":"4151"},{"amount":"1","charge":"1000","slot":"4","id":"14479"},{"amount":"1","charge":"1000","slot":"5","id":"1187"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"9","id":"2912"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, +{"appearance":{"appearance_cache":[{"color":"20","look":"278"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"1","id":"10635"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4151"},{"amount":"1","charge":"1000","slot":"4","id":"4757"},{"amount":"1","charge":"1000","slot":"5","id":"7053"},{"amount":"1","charge":"1000","slot":"7","id":"4759"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, + {"appearance":{"appearance_cache":[{"color":"20","look":"278"},{"color":"0","look":"1000"},{"color":"0","look":"56"},{"color":"0","look":"61"},{"color":"0","look":"68"},{"color":"0","look":"70"},{"color":"0","look":"79"}],"gender":"1"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"1163"},{"amount":"1","charge":"1000","slot":"1","id":"9766"},{"amount":"1","charge":"1000","slot":"2","id":"1704"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"10551"},{"amount":"1","charge":"1000","slot":"5","id":"8850"},{"amount":"1","charge":"1000","slot":"7","id":"1079"},{"amount":"1","charge":"1000","slot":"9","id":"7455"}]}, +{"appearance":{"appearance_cache":[{"color":"12","look":"2"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"0","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"2643"},{"amount":"1","charge":"1000","slot":"1","id":"10446"},{"amount":"1","charge":"1000","slot":"2","id":"6585"},{"amount":"1","charge":"1000","slot":"3","id":"4151"},{"amount":"1","charge":"1000","slot":"4","id":"4712"},{"amount":"1","charge":"1000","slot":"5","id":"8850"},{"amount":"1","charge":"1000","slot":"7","id":"4714"},{"amount":"1","charge":"1000","slot":"9","id":"7461"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]}, +{"appearance":{"appearance_cache":[{"color":"0","look":"0"},{"color":"0","look":"10"},{"color":"0","look":"18"},{"color":"0","look":"26"},{"color":"6","look":"33"},{"color":"0","look":"36"},{"color":"0","look":"42"}],"gender":"0"},"equipment":[{"amount":"1","charge":"1000","slot":"0","id":"10828"},{"amount":"1","charge":"1000","slot":"1","id":"2414"},{"amount":"1","charge":"1000","slot":"2","id":"6585"},{"amount":"1","charge":"1000","slot":"3","id":"4587"},{"amount":"1","charge":"1000","slot":"4","id":"10551"},{"amount":"1","charge":"1000","slot":"5","id":"1187"},{"amount":"1","charge":"1000","slot":"7","id":"4087"},{"amount":"1","charge":"1000","slot":"9","id":"7462"},{"amount":"1","charge":"1000","slot":"10","id":"11732"}]} + + + ] +} \ No newline at end of file diff --git a/Server/data/cache/main_file_cache.dat2 b/Server/data/cache/main_file_cache.dat2 new file mode 100644 index 000000000..245fa6081 --- /dev/null +++ b/Server/data/cache/main_file_cache.dat2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5431211b019b9403b4cfca933f4c9635c1d5278d3730995dced0d8672b1cc91 +size 91702293 diff --git a/Server/data/cache/main_file_cache.idx0 b/Server/data/cache/main_file_cache.idx0 new file mode 100644 index 000000000..3dcebd023 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eca6bae3333edbca742e34674145d33255138a06ae9911152ff6b934a200bee4 +size 16344 diff --git a/Server/data/cache/main_file_cache.idx1 b/Server/data/cache/main_file_cache.idx1 new file mode 100644 index 000000000..bbe44d19e --- /dev/null +++ b/Server/data/cache/main_file_cache.idx1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13d64ed2f9e96ccdc1d9700ced80683aaf6b3ebff8226a182dcdf83a99160677 +size 14610 diff --git a/Server/data/cache/main_file_cache.idx10 b/Server/data/cache/main_file_cache.idx10 new file mode 100644 index 000000000..2f241e6ce --- /dev/null +++ b/Server/data/cache/main_file_cache.idx10 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:135006f0eb0321655f0353f46cb1ca77725f71fc84c3732d091964bd9fbfb66d +size 12 diff --git a/Server/data/cache/main_file_cache.idx11 b/Server/data/cache/main_file_cache.idx11 new file mode 100644 index 000000000..ab555c542 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx11 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de9903eb965d5787b622c3bfceb3c5c15756c041a4884a552e95a2548bc09748 +size 2340 diff --git a/Server/data/cache/main_file_cache.idx12 b/Server/data/cache/main_file_cache.idx12 new file mode 100644 index 000000000..47e9d2e68 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx12 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbd9a67fcea896a456ba5de71b2059046915a44a6522b1a73252e5e405fc7007 +size 12390 diff --git a/Server/data/cache/main_file_cache.idx13 b/Server/data/cache/main_file_cache.idx13 new file mode 100644 index 000000000..f43ae87dd --- /dev/null +++ b/Server/data/cache/main_file_cache.idx13 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b2a8543ee5056487859600cb5540bf3afd0602b2126c590991a582bad8ac652 +size 9552 diff --git a/Server/data/cache/main_file_cache.idx14 b/Server/data/cache/main_file_cache.idx14 new file mode 100644 index 000000000..a344b5060 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx14 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b2b03216a0f5fe2a8b4058942de2ec39b15fdb345de1cd7006b3754da2448b3 +size 3834 diff --git a/Server/data/cache/main_file_cache.idx15 b/Server/data/cache/main_file_cache.idx15 new file mode 100644 index 000000000..81c249c82 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx15 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7eedc5947eaebf12d253b7dc21b03c1a77a061000b8d87038e6ebb566ad7cfc4 +size 1596 diff --git a/Server/data/cache/main_file_cache.idx16 b/Server/data/cache/main_file_cache.idx16 new file mode 100644 index 000000000..46aec82f9 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx16 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:439ea312ce607992e05ffc87031703aaecd3e7aebb66ddb718bde19e04205210 +size 990 diff --git a/Server/data/cache/main_file_cache.idx17 b/Server/data/cache/main_file_cache.idx17 new file mode 100644 index 000000000..9cff3e213 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx17 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:423fb227f50447805e11bbf281c30c17c389c4f7a4d3cf615f97144ce2990f73 +size 54 diff --git a/Server/data/cache/main_file_cache.idx18 b/Server/data/cache/main_file_cache.idx18 new file mode 100644 index 000000000..19cd05001 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx18 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a0ef062e8417cfcb1776c89ca5b59503b0c2c1dbe7f29227918ade80383c345 +size 408 diff --git a/Server/data/cache/main_file_cache.idx19 b/Server/data/cache/main_file_cache.idx19 new file mode 100644 index 000000000..853d55e14 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx19 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e8d1f3dd00aed753029371a37eb75f2068a6bfb902d481891a2e15afd3a9f34 +size 348 diff --git a/Server/data/cache/main_file_cache.idx2 b/Server/data/cache/main_file_cache.idx2 new file mode 100644 index 000000000..5caf4de0c --- /dev/null +++ b/Server/data/cache/main_file_cache.idx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e134ffb243ad976a8267cc0c90e2594600f5d61c691f0883767fdb98b7fa8746 +size 210 diff --git a/Server/data/cache/main_file_cache.idx20 b/Server/data/cache/main_file_cache.idx20 new file mode 100644 index 000000000..0aa9dda83 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx20 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4016ef195600feba273419b45fb5fc88c707d243549b1b042af309644c3d25b8 +size 528 diff --git a/Server/data/cache/main_file_cache.idx21 b/Server/data/cache/main_file_cache.idx21 new file mode 100644 index 000000000..383a6466e --- /dev/null +++ b/Server/data/cache/main_file_cache.idx21 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d49e0bf352c7c05f2ab29ad0f29953db9d203a232f6ba1ab9fe64931320f0f4 +size 48 diff --git a/Server/data/cache/main_file_cache.idx22 b/Server/data/cache/main_file_cache.idx22 new file mode 100644 index 000000000..0191b0fb3 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx22 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c0056aec21f859d1a18901ccd53f83175a7b06a466b19c30f374fd798facc58 +size 36 diff --git a/Server/data/cache/main_file_cache.idx23 b/Server/data/cache/main_file_cache.idx23 new file mode 100644 index 000000000..6755588d9 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx23 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb19eba5eddf90cf89e619ad41c4051a861a514156783914e6b7e6c1ce6e2b96 +size 18 diff --git a/Server/data/cache/main_file_cache.idx24 b/Server/data/cache/main_file_cache.idx24 new file mode 100644 index 000000000..96def132a --- /dev/null +++ b/Server/data/cache/main_file_cache.idx24 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90116f2a73d0d3fafd5ef3c284deb8c8a17b7ea882c6375b31175078fb7407db +size 12 diff --git a/Server/data/cache/main_file_cache.idx25 b/Server/data/cache/main_file_cache.idx25 new file mode 100644 index 000000000..a244287be --- /dev/null +++ b/Server/data/cache/main_file_cache.idx25 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5f7249f0d239de5cc25a3697f55a7e1c713e5e95ba1bf1d6d4e8e53f1588408 +size 12 diff --git a/Server/data/cache/main_file_cache.idx255 b/Server/data/cache/main_file_cache.idx255 new file mode 100644 index 000000000..6e504259a --- /dev/null +++ b/Server/data/cache/main_file_cache.idx255 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83a2292c515596af0423764c48e41dfe1aac482920dca0b89ecb343db6dd4c30 +size 174 diff --git a/Server/data/cache/main_file_cache.idx26 b/Server/data/cache/main_file_cache.idx26 new file mode 100644 index 000000000..2b11bf8a3 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx26 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b48aa88d7b2665f659f5282fa4c51e9725634474a184e67ef54bc47ad1125d0 +size 6 diff --git a/Server/data/cache/main_file_cache.idx27 b/Server/data/cache/main_file_cache.idx27 new file mode 100644 index 000000000..b2d963bff --- /dev/null +++ b/Server/data/cache/main_file_cache.idx27 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9794503b85d9d79d71a40f60c144be9a3a77920a4efcf6e5b519b2691e163fb +size 12 diff --git a/Server/data/cache/main_file_cache.idx28 b/Server/data/cache/main_file_cache.idx28 new file mode 100644 index 000000000..6d9a3b62a --- /dev/null +++ b/Server/data/cache/main_file_cache.idx28 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7653833b04b7b0adcb4115c71f9a7e73fd0e138b1f3dcd959dccd35c281a466f +size 48 diff --git a/Server/data/cache/main_file_cache.idx3 b/Server/data/cache/main_file_cache.idx3 new file mode 100644 index 000000000..8b8124415 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18aa0ae07970bc27ec13134f6210eae5d2b80093e04090e72e143c1098a0edf3 +size 5028 diff --git a/Server/data/cache/main_file_cache.idx4 b/Server/data/cache/main_file_cache.idx4 new file mode 100644 index 000000000..5d057db61 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edb2b78b49a0d7433b1bcd8b154ff84842b0db51475955de53c211d847e32787 +size 40500 diff --git a/Server/data/cache/main_file_cache.idx5 b/Server/data/cache/main_file_cache.idx5 new file mode 100644 index 000000000..f19fd00c7 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx5 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32bda84b31731cd60f7bc4e90caf7d55671e966fa958d60f2ec91da16340743d +size 22188 diff --git a/Server/data/cache/main_file_cache.idx6 b/Server/data/cache/main_file_cache.idx6 new file mode 100644 index 000000000..59ab3b276 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx6 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9ba71c8e7ee594791f5840bdfecbcbe12900728a2ff893dad85ae0cfc1f937e +size 4008 diff --git a/Server/data/cache/main_file_cache.idx7 b/Server/data/cache/main_file_cache.idx7 new file mode 100644 index 000000000..3512d453d --- /dev/null +++ b/Server/data/cache/main_file_cache.idx7 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e86f53e678f5f149502232332fb5147094982644635f59019fff61b47002206 +size 272838 diff --git a/Server/data/cache/main_file_cache.idx8 b/Server/data/cache/main_file_cache.idx8 new file mode 100644 index 000000000..7181e5994 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx8 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f0142ec0412674ff9e60c531d766418fe781d44f06b9f7c5be006e5ed5ff8c3 +size 10254 diff --git a/Server/data/cache/main_file_cache.idx9 b/Server/data/cache/main_file_cache.idx9 new file mode 100644 index 000000000..ef694ad78 --- /dev/null +++ b/Server/data/cache/main_file_cache.idx9 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8758dcdbf08f48ac5033c7be75d22d5822681efa2053a64d7cd6a98e04c6b902 +size 4080 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 fef8c7d5e..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", @@ -546,7 +546,7 @@ { "itemId": "2866", "name": "Ogre arrow", - "start_graphic": "243,96", + "start_graphic": "243,60", "darkbow_graphic": "", "projectile": "242,40,36,41,46,5,0", "poison_damage": "0" @@ -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", @@ -1726,5 +1726,45 @@ "darkbow_graphic": "", "projectile": "1839,40,36,32,32,15,0", "poison_damage": "0" + }, + { + "itemId": "14202", + "name": "Arrows (class 1)", + "start_graphic": "24,96", + "darkbow_graphic": "1109,96", + "projectile": "15,40,36,41,46,5,0", + "poison_damage": "0" + }, + { + "itemId": "14203", + "name": "Arrows (class 2)", + "start_graphic": "24,96", + "darkbow_graphic": "1109,96", + "projectile": "15,40,36,41,46,5,0", + "poison_damage": "0" + }, + { + "itemId": "14204", + "name": "Arrows (class 3)", + "start_graphic": "24,96", + "darkbow_graphic": "1109,96", + "projectile": "15,40,36,41,46,5,0", + "poison_damage": "0" + }, + { + "itemId": "14205", + "name": "Arrows (class 4)", + "start_graphic": "24,96", + "darkbow_graphic": "1109,96", + "projectile": "15,40,36,41,46,5,0", + "poison_damage": "0" + }, + { + "itemId": "14206", + "name": "Arrows (class 5)", + "start_graphic": "24,96", + "darkbow_graphic": "1109,96", + "projectile": "15,40,36,41,46,5,0", + "poison_damage": "0" } ] diff --git a/Server/data/configs/clue_rewards.json b/Server/data/configs/clue_rewards.json new file mode 100644 index 000000000..5554b133b --- /dev/null +++ b/Server/data/configs/clue_rewards.json @@ -0,0 +1,2182 @@ +{ + "rare": [ + { + "minAmount": 1, + "weight": 50, + "id": 10362, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 30, + "id": 3486, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 30, + "id": 3481, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 30, + "id": 3483, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 30, + "id": 3485, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 30, + "id": 3488, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10330, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10332, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10334, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10336, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10338, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10340, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10342, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10344, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10346, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10348, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10350, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": 10, + "id": 10352, + "maxAmount": 1 + } + ], + "medium": [ + { + "minAmount": 1, + "weight": "5", + "id": 2605, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2599, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2601, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2603, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3474, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2613, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2607, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2609, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3475, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2611, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2647, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2645, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2649, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2577, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2579, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7319, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7321, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7323, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7325, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7327, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7372, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7380, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7370, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7378, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13103, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13097, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10282, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13109, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13107, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13111, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13113, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13115, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10364, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10420, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10422, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10400, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10402, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10416, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10418, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10436, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10438, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10296, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10298, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10300, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10302, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10304, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7334, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7340, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7346, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7352, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7358, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10446, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10448, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10450, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10452, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10454, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10456, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1161, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1123, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1073, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1091, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1199, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1183, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1111, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1211, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1271, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1287, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1301, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1317, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1357, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1371, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1430, + "maxAmount": 1 + }, + { + "minAmount": 10, + "weight": "5", + "id": 4823, + "maxAmount": 40 + }, + { + "minAmount": 1, + "weight": "5", + "id": 9183, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1393, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1099, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1135, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 857, + "maxAmount": 1 + }, + { + "minAmount": 10, + "weight": "5", + "id": 8781, + "maxAmount": 43 + }, + { + "minAmount": 6, + "weight": "5", + "id": 374, + "maxAmount": 23 + }, + { + "minAmount": 15, + "weight": "5", + "id": 380, + "maxAmount": 43 + }, + { + "minAmount": 500, + "weight": "5", + "id": 995, + "maxAmount": 7000 + }, + { + "minAmount": 15, + "weight": "5", + "id": 10326, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7329, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7331, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7330, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 10327, + "maxAmount": 40 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10476, + "maxAmount": 27 + }, + { + "minAmount": 1, + "weight": "5", + "id": 556, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 554, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 557, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 555, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3827, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3828, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3829, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3830, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3831, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3832, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3833, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3834, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3835, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3836, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3837, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3838, + "maxAmount": 1 + } + ], + "hard": [ + { + "minAmount": 1, + "weight": "5", + "id": 10284, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13101, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13099, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10470, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10472, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10474, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10440, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10442, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10444, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2627, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2623, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2625, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3477, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2629, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2619, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2615, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2617, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3476, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2621, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2673, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2669, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2671, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3480, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2675, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2657, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2653, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2655, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3478, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2659, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2665, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2661, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2663, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3479, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2667, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10286, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7336, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10288, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7342, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10290, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7348, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10292, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7354, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10294, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7360, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7376, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7384, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7374, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7382, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10374, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10368, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10370, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10372, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10382, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10376, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10378, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10380, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10390, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10384, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10386, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10388, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2651, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2581, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2639, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2641, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2643, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1163, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1127, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1079, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1093, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1201, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1185, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1213, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1275, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1289, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1303, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1319, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1359, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1373, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1432, + "maxAmount": 1 + }, + { + "minAmount": 10, + "weight": "5", + "id": 4824, + "maxAmount": 43 + }, + { + "minAmount": 6, + "weight": "5", + "id": 9144, + "maxAmount": 23 + }, + { + "minAmount": 1, + "weight": "5", + "id": 9185, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2497, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2503, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 861, + "maxAmount": 2 + }, + { + "minAmount": 1, + "weight": "5", + "id": 859, + "maxAmount": 2 + }, + { + "minAmount": 6, + "weight": "5", + "id": 8783, + "maxAmount": 27 + }, + { + "minAmount": 16, + "weight": "5", + "id": 380, + "maxAmount": 38 + }, + { + "minAmount": 7, + "weight": "5", + "id": 386, + "maxAmount": 32 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7398, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7399, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7400, + "maxAmount": 1 + }, + { + "minAmount": 500, + "weight": "5", + "id": 995, + "maxAmount": 7000 + }, + { + "minAmount": 15, + "weight": "5", + "id": 10326, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7329, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7331, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7330, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 10327, + "maxAmount": 40 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10476, + "maxAmount": 27 + }, + { + "minAmount": 1, + "weight": "5", + "id": 556, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 554, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 557, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 555, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3827, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3828, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3829, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3830, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3831, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3832, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3833, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3834, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3835, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3836, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3837, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3838, + "maxAmount": 1 + } + ], + "easy": [ + { + "minAmount": 1, + "weight": "5", + "id": 2633, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2635, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2637, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2587, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2583, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2585, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3472, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2589, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2595, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2591, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2593, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3473, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2597, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10306, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10308, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10310, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10312, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10314, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7332, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7338, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7344, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7350, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7356, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 2631, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7392, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7396, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7388, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7390, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7386, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7394, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7362, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7366, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7364, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 7368, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10404, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10406, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10424, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10426, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10408, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10410, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10428, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10430, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10412, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10414, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10432, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10434, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10316, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10318, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10320, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10322, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10324, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10392, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10394, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10396, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10398, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10366, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10458, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10464, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10462, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10466, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10460, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10468, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13095, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 13105, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10280, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1077, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1089, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1107, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1125, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1151, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1165, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1179, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1195, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1217, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1283, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1297, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1313, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1327, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1341, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1361, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1367, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1426, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3098, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 4821, + "maxAmount": 1 + }, + { + "minAmount": 4, + "weight": "5", + "id": 8779, + "maxAmount": 38 + }, + { + "minAmount": 1, + "weight": "5", + "id": 850, + "maxAmount": 4 + }, + { + "minAmount": 4, + "weight": "5", + "id": 334, + "maxAmount": 19 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1169, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1059, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1061, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1063, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1095, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1129, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1167, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1131, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 858, + "maxAmount": 4 + }, + { + "minAmount": 3, + "weight": "5", + "id": 330, + "maxAmount": 23 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1441, + "maxAmount": 3 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1443, + "maxAmount": 3 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1270, + "maxAmount": 3 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1097, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 1133, + "maxAmount": 1 + }, + { + "minAmount": 500, + "weight": "5", + "id": 995, + "maxAmount": 7000 + }, + { + "minAmount": 15, + "weight": "5", + "id": 10326, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7329, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7331, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 7330, + "maxAmount": 40 + }, + { + "minAmount": 15, + "weight": "5", + "id": 10327, + "maxAmount": 40 + }, + { + "minAmount": 1, + "weight": "5", + "id": 10476, + "maxAmount": 27 + }, + { + "minAmount": 1, + "weight": "5", + "id": 556, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 554, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 557, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 555, + "maxAmount": 228 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3827, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3828, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3829, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3830, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3831, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3832, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3833, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3834, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3835, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3836, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3837, + "maxAmount": 1 + }, + { + "minAmount": 1, + "weight": "5", + "id": 3838, + "maxAmount": 1 + } + ] +} diff --git a/Server/data/configs/door_configs.json b/Server/data/configs/door_configs.json index 1cbec114d..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", @@ -689,12 +677,6 @@ "fence": "false", "metal": "false" }, - { - "id": "2706", - "replaceId": "25719", - "fence": "false", - "metal": "false" - }, { "id": "2712", "replaceId": "36865", @@ -902,12 +884,12 @@ { "id": "3725", "replaceId": "3727", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "3726", - "replaceId": "3727", + "replaceId": "3728", "fence": "true", "metal": "false" }, @@ -931,7 +913,7 @@ }, { "id": "3747", - "replaceId": "1534", + "replaceId": "3748", "fence": "false", "metal": "false" }, @@ -949,7 +931,7 @@ }, { "id": "4148", - "replaceId": "4248", + "replaceId": "4246", "fence": "false", "metal": "false" }, @@ -985,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" }, @@ -1244,12 +1226,12 @@ { "id": "7049", "replaceId": "7051", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "7050", - "replaceId": "23919", + "replaceId": "7052", "fence": "true", "metal": "false" }, @@ -1346,7 +1328,7 @@ { "id": "8810", "replaceId": "8812", - "fence": "false", + "fence": "true", "metal": "false" }, { @@ -1445,6 +1427,12 @@ "fence": "false", "metal": "false" }, + { + "id": "11470", + "replaceId": "11471", + "fence": "false", + "metal": "false" + }, { "id": "11483", "replaceId": "11708", @@ -1765,7 +1753,7 @@ }, { "id": "14245", - "replaceId": "14248", + "replaceId": "14246", "fence": "true", "metal": "false" }, @@ -1989,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", @@ -2150,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" }, @@ -2183,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", @@ -2363,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", @@ -2513,12 +2511,6 @@ "fence": "false", "metal": "false" }, - { - "id": "28514", - "replaceId": "28518", - "fence": "true", - "metal": "true" - }, { "id": "28589", "replaceId": "28588", @@ -2677,7 +2669,7 @@ }, { "id": "31815", - "replaceId": "31816", + "replaceId": "31817", "fence": "false", "metal": "false" }, @@ -2804,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 aae355ed9..2b5926e69 100644 --- a/Server/data/configs/drop_tables.json +++ b/Server/data/configs/drop_tables.json @@ -16,187 +16,121 @@ } ], "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": [ - { - "minAmount": "7", - "weight": "25.0", - "id": "555", - "maxAmount": "7" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "557", - "maxAmount": "4" - }, - { - "minAmount": "6", - "weight": "25.0", - "id": "554", - "maxAmount": "6" - }, - { - "minAmount": "9", - "weight": "25.0", - "id": "558", - "maxAmount": "9" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "562", - "maxAmount": "2" - }, - { - "minAmount": "7", - "weight": "5.0", - "id": "559", - "maxAmount": "7" - }, { "minAmount": "1", - "weight": "50.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": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "877", - "maxAmount": "12" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "882", - "maxAmount": "7" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1381", + "weight": "1.0", + "id": "1203", "maxAmount": "1" }, + { + "minAmount": "2", + "weight": "22.0", + "id": "877", + "maxAmount": "12" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "882", + "maxAmount": "7" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "557", + "maxAmount": "4" + }, + { + "minAmount": "6", + "weight": "2.0", + "id": "554", + "maxAmount": "6" + }, + { + "minAmount": "9", + "weight": "2.0", + "id": "558", + "maxAmount": "9" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "562", + "maxAmount": "2" + }, { "minAmount": "1", - "weight": "50.0", + "weight": "23.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "37.0", "id": "995", - "maxAmount": "40" + "maxAmount": "3" + }, + { + "minAmount": "5", + "weight": "9.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" }, { "minAmount": "1", - "weight": "25.0", - "id": "1438", + "weight": "8.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "5.0", "id": "313", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "436", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "438", + "weight": "2.0", + "id": "1440", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2699", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -211,181 +145,121 @@ } ], "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": [ { - "minAmount": "7", - "weight": "50.0", - "id": "877", - "maxAmount": "7" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "882", - "maxAmount": "12" + "minAmount": "1", + "weight": "2.0", + "id": "1139", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1203", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "1381", - "maxAmount": "1" + "minAmount": "2", + "weight": "22.0", + "id": "877", + "maxAmount": "12" }, { "minAmount": "7", - "weight": "25.0", - "id": "555", + "weight": "3.0", + "id": "882", "maxAmount": "7" }, { "minAmount": "4", - "weight": "25.0", + "weight": "2.0", "id": "557", "maxAmount": "4" }, { "minAmount": "6", - "weight": "25.0", + "weight": "2.0", "id": "554", "maxAmount": "6" }, { "minAmount": "9", - "weight": "25.0", + "weight": "2.0", "id": "558", "maxAmount": "9" }, { "minAmount": "2", - "weight": "5.0", + "weight": "1.0", "id": "562", "maxAmount": "2" }, - { - "minAmount": "7", - "weight": "5.0", - "id": "559", - "maxAmount": "7" - }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "23.0", + "id": "14424", "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": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "minAmount": "3", + "weight": "37.0", "id": "995", - "maxAmount": "400" + "maxAmount": "3" + }, + { + "minAmount": "5", + "weight": "9.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" }, { "minAmount": "1", - "weight": "25.0", - "id": "1965", + "weight": "8.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "5.0", "id": "313", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "436", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1440", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2703", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -403,185 +277,107 @@ "ids": "7,1377,1757,1758,1759,1760,1761,7128,7129,7130", "description": "", "main": [ - { - "minAmount": "1", - "weight": "5.0", - "id": "5345", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5341", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5329", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5331", - "maxAmount": "1" - }, { "minAmount": "4", - "weight": "25.0", - "id": "5324", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "50.0", - "id": "5323", - "maxAmount": "4" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5318", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5322", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5321", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "12148", - "maxAmount": "4" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "313", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "562", - "maxAmount": "2" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "556", + "weight": "2.0", + "id": "557", "maxAmount": "4" }, { "minAmount": "6", - "weight": "25.0", + "weight": "2.0", "id": "554", "maxAmount": "6" }, { "minAmount": "9", - "weight": "25.0", + "weight": "2.0", "id": "558", "maxAmount": "9" }, + { + "minAmount": "2", + "weight": "1.0", + "id": "562", + "maxAmount": "2" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "11.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "27.0", + "id": "14430", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "38.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "27.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "313", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "5341", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", "id": "1440", "maxAmount": "1" }, - { - "minAmount": "7", - "weight": "25.0", - "id": "882", - "maxAmount": "7" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "877", - "maxAmount": "12" - }, { "minAmount": "1", - "weight": "25.0", - "id": "1139", + "weight": "2.0", + "id": "5345", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1203", + "weight": "2.0", + "id": "5343", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "436", + "weight": "1.0", + "id": "5329", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "207", + "weight": "1.0", + "id": "5340", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "5320", + "weight": "1.0", + "id": "1", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5319", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2678", - "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "995", - "maxAmount": "15" } ] }, @@ -733,7 +529,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2747", + "id": "5733", "maxAmount": "1" }, { @@ -754,6 +550,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "91.6064", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.0005", @@ -992,6 +794,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "94.08", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.2133", @@ -1015,202 +823,179 @@ "weight": "0.2667", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "94.08", - "id": "0", - "maxAmount": "1" } ], "ids": "12,3246,3247,3248,3249,3250,3251,3252,3253,3255,3256,3257,3258,3259,3260,3261,3262,3263,5909", "description": "", "main": [ - { - "minAmount": "2", - "weight": "25.0", - "id": "557", - "maxAmount": "5" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "562", - "maxAmount": "6" - }, { "minAmount": "1", - "weight": "25.0", - "id": "563", - "maxAmount": "2" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "558", - "maxAmount": "5" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "554", - "maxAmount": "5" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "554", - "maxAmount": "8" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1379", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "1351", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1375", - "maxAmount": "1" - }, - { - "minAmount": "5", - "weight": "50.0", - "id": "882", - "maxAmount": "18" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "1349", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "2.0", + "id": "1379", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1375", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1420", "maxAmount": "1" }, + { + "minAmount": "15", + "weight": "3.0", + "id": "882", + "maxAmount": "15" + }, { "minAmount": "8", - "weight": "50.0", + "weight": "3.0", "id": "884", "maxAmount": "8" }, { - "minAmount": "58", - "weight": "50.0", + "minAmount": "3", + "weight": "1.0", + "id": "562", + "maxAmount": "3" + }, + { + "minAmount": "5", + "weight": "3.0", + "id": "557", + "maxAmount": "5" + }, + { + "minAmount": "8", + "weight": "2.0", + "id": "554", + "maxAmount": "8" + }, + { + "minAmount": "10", + "weight": "2.0", + "id": "558", + "maxAmount": "10" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "563", + "maxAmount": "2" + }, + { + "minAmount": "5", + "weight": "41.0", "id": "995", - "maxAmount": "315" + "maxAmount": "5" + }, + { + "minAmount": "8", + "weight": "9.0", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "25", + "weight": "5.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "32", + "weight": "3.0", + "id": "995", + "maxAmount": "32" }, { "minAmount": "1", - "weight": "50.0", - "id": "440", + "weight": "36.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "438", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1917", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2142", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "948", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1592", + "weight": "1.0", + "id": "1917", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "9004", + "weight": "1.0", + "id": "2142", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1595", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1619", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1623", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2714", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "956", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2349", + "weight": "1.0", + "id": "1592", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "1.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2677", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], "charm": [ + { + "minAmount": "1", + "weight": "77.9122", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "12.4761", @@ -1234,12 +1019,6 @@ "weight": "0.2546", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "77.9122", - "id": "0", - "maxAmount": "1" } ], "ids": "13,652,1263,5195,5196,5197", @@ -1247,153 +1026,177 @@ "main": [ { "minAmount": "5", - "weight": "50.0", + "weight": "3.0", "id": "556", "maxAmount": "5" }, { "minAmount": "12", - "weight": "50.0", + "weight": "2.0", "id": "556", "maxAmount": "12" }, { "minAmount": "5", - "weight": "50.0", + "weight": "3.0", "id": "554", "maxAmount": "5" }, { "minAmount": "12", - "weight": "50.0", + "weight": "2.0", "id": "554", "maxAmount": "12" }, { "minAmount": "5", - "weight": "50.0", + "weight": "3.0", "id": "555", "maxAmount": "5" }, { "minAmount": "12", - "weight": "50.0", + "weight": "2.0", "id": "555", "maxAmount": "12" }, { "minAmount": "5", - "weight": "50.0", + "weight": "3.0", "id": "557", "maxAmount": "5" }, { "minAmount": "12", - "weight": "50.0", + "weight": "2.0", "id": "557", "maxAmount": "12" }, { "minAmount": "5", - "weight": "50.0", + "weight": "3.0", "id": "559", "maxAmount": "5" }, { "minAmount": "12", - "weight": "50.0", + "weight": "2.0", "id": "559", "maxAmount": "12" }, { "minAmount": "5", - "weight": "50.0", + "weight": "3.0", "id": "558", "maxAmount": "5" }, { "minAmount": "12", - "weight": "50.0", + "weight": "2.0", "id": "558", "maxAmount": "12" }, { "minAmount": "2", - "weight": "5.0", + "weight": "8.0", "id": "562", "maxAmount": "2" }, { "minAmount": "10", - "weight": "5.0", + "weight": "1.0", "id": "562", "maxAmount": "10" }, { "minAmount": "2", - "weight": "5.0", + "weight": "8.0", "id": "561", "maxAmount": "2" }, { "minAmount": "2", - "weight": "5.0", + "weight": "1.0", "id": "563", "maxAmount": "2" }, { "minAmount": "2", - "weight": "5.0", + "weight": "1.0", "id": "565", "maxAmount": "2" }, { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", "id": "1379", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "30" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "1448", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1444", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "579", + "weight": "3.0", + "id": "1440", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1442", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "20.0", + "id": "995", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "9.0", + "id": "995", + "maxAmount": "2" + }, + { + "minAmount": "18", + "weight": "7.0", + "id": "995", + "maxAmount": "18" + }, + { + "minAmount": "30", + "weight": "1.0", + "id": "995", + "maxAmount": "30" + }, + { + "minAmount": "1", + "weight": "16.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "577", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "579", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -1407,6 +1210,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "89.3939", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.6277", @@ -1458,176 +1267,116 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "556", + "weight": "2.0", + "id": "1139", "maxAmount": "1" }, { - "minAmount": "9", - "weight": "50.0", - "id": "558", - "maxAmount": "9" + "minAmount": "1", + "weight": "1.0", + "id": "1203", + "maxAmount": "1" }, { - "minAmount": "6", - "weight": "50.0", - "id": "554", - "maxAmount": "6" + "minAmount": "2", + "weight": "22.0", + "id": "877", + "maxAmount": "12" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "882", + "maxAmount": "7" }, { "minAmount": "4", - "weight": "50.0", + "weight": "2.0", "id": "557", "maxAmount": "4" }, + { + "minAmount": "6", + "weight": "2.0", + "id": "554", + "maxAmount": "6" + }, + { + "minAmount": "9", + "weight": "2.0", + "id": "558", + "maxAmount": "9" + }, { "minAmount": "2", - "weight": "5.0", + "weight": "1.0", "id": "562", "maxAmount": "2" }, { "minAmount": "1", - "weight": "5.0", - "id": "563", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1139", - "maxAmount": "1" - }, - { - "minAmount": "7", - "weight": "50.0", - "id": "882", - "maxAmount": "7" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "877", - "maxAmount": "12" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1137", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1381", - "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": "25.0", - "id": "217", + "weight": "23.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "3", - "weight": "50.0", + "weight": "37.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "5", + "weight": "9.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", "id": "995", "maxAmount": "25" }, { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "5.0", "id": "313", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "436", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1440", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2683", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -1896,146 +1645,104 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "2.0", "id": "1281", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1295", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1157", "maxAmount": "1" }, { "minAmount": "30", - "weight": "50.0", + "weight": "13.0", "id": "555", "maxAmount": "30" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "565", "maxAmount": "1" }, - { - "minAmount": "2", - "weight": "50.0", - "id": "995", - "maxAmount": "2" - }, - { - "minAmount": "8", - "weight": "50.0", - "id": "995", - "maxAmount": "8" - }, { "minAmount": "1", - "weight": "50.0", + "weight": "9.0", "id": "2351", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2359", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "2353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1458", + "weight": "1.0", + "id": "2359", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2833", + "weight": "8.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "16.0", + "id": "995", + "maxAmount": "2" + }, + { + "minAmount": "8", + "weight": "10.0", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "15", + "weight": "18.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "48", + "weight": "40.0", + "id": "995", + "maxAmount": "48" + }, + { + "minAmount": "120", + "weight": "2.0", + "id": "995", + "maxAmount": "120" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "2.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "5733", "maxAmount": "1" } ] @@ -2050,58 +1757,9 @@ } ], "charm": [], - "ids": "21", + "ids": "21,1072", "description": "", - "main": [ - { - "minAmount": "200", - "weight": "50.0", - "id": "995", - "maxAmount": "200" - }, - { - "minAmount": "300", - "weight": "50.0", - "id": "995", - "maxAmount": "300" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "560", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "565", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "444", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1993", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "569", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1601", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -2118,8 +1776,14 @@ "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "2703", + "weight": "127.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -2166,25 +1830,19 @@ "main": [ { "minAmount": "1", - "weight": "25.0", + "weight": "32.0", "id": "0", "maxAmount": "1" }, { "minAmount": "5", - "weight": "25.0", + "weight": "64.0", "id": "314", "maxAmount": "5" }, - { - "minAmount": "10", - "weight": "25.0", - "id": "314", - "maxAmount": "10" - }, { "minAmount": "15", - "weight": "25.0", + "weight": "32.0", "id": "314", "maxAmount": "15" } @@ -2200,6 +1858,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "20.1053", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "69.0441", @@ -2223,12 +1887,6 @@ "weight": "1.0079", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "20.1053", - "id": "0", - "maxAmount": "1" } ], "ids": "49,3586,6210", @@ -2236,14 +1894,14 @@ "main": [ { "minAmount": "1", - "weight": "12.0", - "id": "2727", + "weight": "126.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "88.0", - "id": "0", + "weight": "2.0", + "id": "12070", "maxAmount": "1" } ] @@ -2266,29 +1924,49 @@ "charm": [ { "minAmount": "4", - "weight": "100.0", + "weight": "28.2697", + "id": "0", + "maxAmount": "4" + }, + { + "minAmount": "4", + "weight": "4.6304", "id": "12158", "maxAmount": "4" }, { "minAmount": "4", - "weight": "100.0", + "weight": "4.2242", "id": "12159", "maxAmount": "4" }, { "minAmount": "4", - "weight": "100.0", + "weight": "61.4947", "id": "12160", "maxAmount": "4" }, { "minAmount": "4", - "weight": "100.0", + "weight": "1.381", "id": "12163", "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": [ @@ -2426,7 +2104,7 @@ }, { "minAmount": "1", - "weight": "0.154", + "weight": "0.20404", "id": "11286", "maxAmount": "1" }, @@ -2474,6 +2152,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "18.0057", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "17.6693", @@ -2497,12 +2181,6 @@ "weight": "3.7095", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "18.0057", - "id": "0", - "maxAmount": "1" } ], "ids": "53,4669,4670,4671,4672", @@ -2510,181 +2188,127 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1369", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1315", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "1355", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1369", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1123", "maxAmount": "1" }, { "minAmount": "1", - "weight": "10.0", + "weight": "1.0", "id": "1303", "maxAmount": "1" }, { "minAmount": "20", - "weight": "50.0", + "weight": "20.0", "id": "828", "maxAmount": "20" }, { "minAmount": "4", - "weight": "25.0", + "weight": "8.0", "id": "892", "maxAmount": "4" }, - { - "minAmount": "8", - "weight": "25.0", - "id": "811", - "maxAmount": "8" - }, { "minAmount": "4", - "weight": "25.0", + "weight": "5.0", "id": "563", "maxAmount": "4" }, - { - "minAmount": "5", - "weight": "25.0", - "id": "560", - "maxAmount": "5" - }, { "minAmount": "2", - "weight": "25.0", + "weight": "4.0", "id": "565", "maxAmount": "2" }, { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "1" + "minAmount": "8", + "weight": "3.0", + "id": "811", + "maxAmount": "8" + }, + { + "minAmount": "5", + "weight": "3.0", + "id": "560", + "maxAmount": "5" }, { "minAmount": "1", - "weight": "50.0", - "id": "201", + "weight": "15.0", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" + "minAmount": "196", + "weight": "36.0", + "id": "995", + "maxAmount": "196" }, { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" + "minAmount": "330", + "weight": "10.0", + "id": "995", + "maxAmount": "330" }, { - "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": "30", - "weight": "100.0", + "minAmount": "690", + "weight": "1.0", "id": "995", "maxAmount": "690" }, { "minAmount": "3", - "weight": "25.0", + "weight": "3.0", "id": "1897", "maxAmount": "3" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "2361", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2727", + "weight": "5.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "15.8", - "id": "31", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "150.0", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "10.0", + "weight": "0.25", "id": "12477", "maxAmount": "1" } @@ -2706,6 +2330,12 @@ } ], "charm": [ + { + "minAmount": "3", + "weight": "56.6693", + "id": "0", + "maxAmount": "3" + }, { "minAmount": "3", "weight": "8.6692", @@ -2729,12 +2359,6 @@ "weight": "1.3469", "id": "12163", "maxAmount": "3" - }, - { - "minAmount": "3", - "weight": "56.6693", - "id": "0", - "maxAmount": "3" } ], "ids": "54,4673,4674,4675,4676", @@ -2742,153 +2366,159 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1361", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1355", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1369", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1315", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1361", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "1355", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "1369", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1123", "maxAmount": "1" }, { "minAmount": "1", - "weight": "6.0", + "weight": "1.0", "id": "1303", "maxAmount": "1" }, { "minAmount": "1", - "weight": "6.0", + "weight": "1.0", "id": "1113", "maxAmount": "1" }, - { - "minAmount": "16", - "weight": "50.0", - "id": "810", - "maxAmount": "16" - }, { "minAmount": "30", - "weight": "50.0", + "weight": "20.0", "id": "829", "maxAmount": "30" }, - { - "minAmount": "2", - "weight": "25.0", - "id": "868", - "maxAmount": "2" - }, - { - "minAmount": "75", - "weight": "8.0", - "id": "556", - "maxAmount": "75" - }, - { - "minAmount": "15", - "weight": "8.0", - "id": "565", - "maxAmount": "15" - }, { "minAmount": "50", "weight": "8.0", "id": "554", "maxAmount": "50" }, + { + "minAmount": "16", + "weight": "7.0", + "id": "816", + "maxAmount": "16" + }, { "minAmount": "10", - "weight": "4.0", + "weight": "5.0", "id": "563", "maxAmount": "10" }, + { + "minAmount": "2", + "weight": "3.0", + "id": "868", + "maxAmount": "2" + }, + { + "minAmount": "15", + "weight": "3.0", + "id": "565", + "maxAmount": "15" + }, + { + "minAmount": "75", + "weight": "1.0", + "id": "556", + "maxAmount": "75" + }, { "minAmount": "10", - "weight": "8.0", + "weight": "1.0", "id": "562", "maxAmount": "10" }, { - "minAmount": "1", - "weight": "25.0", - "id": "2361", - "maxAmount": "2" + "minAmount": "196", + "weight": "40.0", + "id": "995", + "maxAmount": "196" }, { - "minAmount": "170", - "weight": "50.0", + "minAmount": "330", + "weight": "10.0", + "id": "995", + "maxAmount": "330" + }, + { + "minAmount": "690", + "weight": "1.0", "id": "995", "maxAmount": "690" }, { "minAmount": "1", - "weight": "6.0", + "weight": "3.0", + "id": "2361", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "1897", "maxAmount": "1" }, { "minAmount": "1", - "weight": "4.0", - "id": "12070", + "weight": "3.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "4.0", - "id": "12480", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "12.6", + "weight": "2.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.0562", - "id": "11286", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "75.0", - "id": "0", + "weight": "0.25", + "id": "12480", "maxAmount": "1" }, { - "minAmount": "3", - "weight": "4.0", - "id": "563", - "maxAmount": "3" + "minAmount": "1", + "weight": "0.0128", + "id": "11286", + "maxAmount": "1" } ] }, @@ -2908,6 +2538,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "48.5393", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "11.0668", @@ -2931,12 +2567,6 @@ "weight": "2.21", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "48.5393", - "id": "0", - "maxAmount": "1" } ], "ids": "55,4681,4682,4683,4684,5178", @@ -2944,187 +2574,151 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1069", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1243", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1355", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1197", + "weight": "3.0", + "id": "1365", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1245", + "weight": "2.0", + "id": "1243", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1161", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", + "weight": "1.0", + "id": "1197", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1245", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1213", "maxAmount": "1" }, { "minAmount": "75", - "weight": "50.0", + "weight": "8.0", "id": "555", "maxAmount": "75" }, { "minAmount": "15", - "weight": "25.0", + "weight": "5.0", "id": "561", "maxAmount": "15" }, - { - "minAmount": "37", - "weight": "25.0", - "id": "554", - "maxAmount": "37" - }, { "minAmount": "3", - "weight": "25.0", + "weight": "3.0", "id": "563", "maxAmount": "3" }, + { + "minAmount": "37", + "weight": "1.0", + "id": "554", + "maxAmount": "37" + }, { "minAmount": "8", - "weight": "25.0", + "weight": "1.0", "id": "565", "maxAmount": "8" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "15.0", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" + "minAmount": "11", + "weight": "5.0", + "id": "995", + "maxAmount": "11" }, { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" + "minAmount": "44", + "weight": "26.0", + "id": "995", + "maxAmount": "44" }, { - "minAmount": "1", + "minAmount": "132", "weight": "25.0", - "id": "205", - "maxAmount": "1" + "id": "995", + "maxAmount": "132" }, { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" + "minAmount": "200", + "weight": "10.0", + "id": "995", + "maxAmount": "200" }, { - "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": "10", - "weight": "100.0", + "minAmount": "440", + "weight": "1.0", "id": "995", "maxAmount": "440" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "449", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "365", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "12070", + "weight": "4.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "15.0", + "weight": "1.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "125.0", - "id": "0", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "10.0", + "weight": "0.25", "id": "12478", "maxAmount": "1" } @@ -3133,6 +2727,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "87.586", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "8.549", @@ -3156,134 +2756,122 @@ "weight": "0.6551", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "87.586", - "id": "0", - "maxAmount": "1" } ], - "ids": "59,60,4400", + "ids": "59,60", "description": "", "main": [ - { - "minAmount": "4", - "weight": "25.0", - "id": "556", - "maxAmount": "6" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "557", - "maxAmount": "4" - }, - { - "minAmount": "6", - "weight": "25.0", - "id": "554", - "maxAmount": "6" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "559", - "maxAmount": "7" - }, { "minAmount": "1", - "weight": "50.0", - "id": "1139", - "maxAmount": "1" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "877", - "maxAmount": "8" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "882", - "maxAmount": "11" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "839", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", + "id": "1139", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1189", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "1381", - "maxAmount": "1" + "minAmount": "8", + "weight": "3.0", + "id": "882", + "maxAmount": "8" + }, + { + "minAmount": "8", + "weight": "3.0", + "id": "877", + "maxAmount": "8" + }, + { + "minAmount": "6", + "weight": "2.0", + "id": "556", + "maxAmount": "6" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "557", + "maxAmount": "4" + }, + { + "minAmount": "6", + "weight": "2.0", + "id": "554", + "maxAmount": "6" + }, + { + "minAmount": "7", + "weight": "2.0", + "id": "559", + "maxAmount": "7" + }, + { + "minAmount": "6", + "weight": "1.0", + "id": "558", + "maxAmount": "6" }, { "minAmount": "1", - "weight": "50.0", + "weight": "19.0", + "id": "995", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "40.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "12", + "weight": "10.0", + "id": "995", + "maxAmount": "12" + }, + { + "minAmount": "20", + "weight": "1.0", "id": "995", "maxAmount": "20" }, { "minAmount": "1", - "weight": "50.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2307", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1444", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1438", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "1955", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1009", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "3.0", "id": "229", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "3.0", + "id": "2307", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1009", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "30.0", "id": "0", "maxAmount": "1" } @@ -3294,36 +2882,36 @@ "charm": [ { "minAmount": "1", - "weight": "100.0", + "weight": "91.8947", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.065", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "3.2525", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "1.3423", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "1.4455", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ], - "ids": "61,1004,1221,1473,1474,4401,7207", + "ids": "61,1004,1221,1473,1474,4400,4401,7207", "description": "", "main": [ { @@ -3335,123 +2923,52 @@ ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "6291", + "maxAmount": "1" + } + ], "charm": [], "ids": "62,1478,2491,2492,6376,6377", "description": "", "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "2.0", + "id": "1233", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" + "minAmount": "17", + "weight": "8.0", + "id": "9288", + "maxAmount": "17" }, { - "minAmount": "1", - "weight": "50.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": "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": "1", - "weight": "25.0", - "id": "808", - "maxAmount": "1" + "minAmount": "12", + "weight": "8.0", + "id": "814", + "maxAmount": "12" }, { "minAmount": "13", - "weight": "25.0", + "weight": "2.0", "id": "557", "maxAmount": "13" }, - { - "minAmount": "20", - "weight": "25.0", - "id": "9141", - "maxAmount": "20" - }, { "minAmount": "1", - "weight": "25.0", - "id": "11766", + "weight": "8.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "11767", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "11768", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "11769", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "11764", + "weight": "100.0", + "id": "0", "maxAmount": "1" } ] @@ -3459,6 +2976,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "72.2513", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "25.0631", @@ -3482,12 +3005,6 @@ "weight": "0.1488", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "72.2513", - "id": "0", - "maxAmount": "1" } ], "ids": "63", @@ -3504,6 +3021,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "62.4612", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "23.1644", @@ -3527,12 +3050,6 @@ "weight": "1.4478", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "62.4612", - "id": "0", - "maxAmount": "1" } ], "ids": "64", @@ -3561,176 +3078,176 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1307", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "3190", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1279", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1309", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1375", "maxAmount": "1" }, { "minAmount": "2", - "weight": "25.0", + "weight": "4.0", + "id": "806", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "3190", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1321", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1337", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "4.0", "id": "807", "maxAmount": "2" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", + "id": "1293", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1420", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1323", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1239", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1353", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", "id": "1207", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1155", + "weight": "4.0", + "id": "1424", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1075", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", + "id": "1155", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1075", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", "id": "1101", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1141", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "maxAmount": "1" + "minAmount": "5", + "weight": "4.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "8", + "weight": "4.0", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "10", + "weight": "4.0", + "id": "995", + "maxAmount": "10" + }, + { + "minAmount": "12", + "weight": "4.0", + "id": "995", + "maxAmount": "12" + }, + { + "minAmount": "23", + "weight": "4.0", + "id": "995", + "maxAmount": "23" }, { "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": "25.0", + "weight": "4.0", "id": "946", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", + "id": "1", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "5105", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2689", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2731", + "weight": "25.0", + "id": "14424", "maxAmount": "1" } ] @@ -3745,277 +3262,187 @@ } ], "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": [ { "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": "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": "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": "1", - "weight": "25.0", - "id": "1351", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1291", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1189", + "weight": "3.0", + "id": "1420", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1203", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", + "id": "1291", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1189", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1349", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "1420", - "maxAmount": "1" + "minAmount": "5", + "weight": "7.0", + "id": "884", + "maxAmount": "5" }, { - "minAmount": "58", - "weight": "25.0", + "minAmount": "8", + "weight": "4.0", "id": "884", - "maxAmount": "58" + "maxAmount": "8" }, { "minAmount": "5", - "weight": "50.0", + "weight": "2.0", "id": "886", - "maxAmount": "32" + "maxAmount": "5" }, { - "minAmount": "12", - "weight": "25.0", - "id": "888", - "maxAmount": "12" - }, - { - "minAmount": "1", + "minAmount": "6", "weight": "5.0", - "id": "1381", - "maxAmount": "1" + "id": "559", + "maxAmount": "6" }, { - "minAmount": "3", - "weight": "50.0", - "id": "559", - "maxAmount": "11" + "minAmount": "5", + "weight": "5.0", + "id": "558", + "maxAmount": "5" + }, + { + "minAmount": "13", + "weight": "4.0", + "id": "556", + "maxAmount": "13" + }, + { + "minAmount": "7", + "weight": "1.0", + "id": "554", + "maxAmount": "7" + }, + { + "minAmount": "6", + "weight": "1.0", + "id": "561", + "maxAmount": "6" }, { "minAmount": "4", - "weight": "25.0", + "weight": "1.0", "id": "562", "maxAmount": "4" }, - { - "minAmount": "3", - "weight": "25.0", - "id": "556", - "maxAmount": "3" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "554", - "maxAmount": "84" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "558", - "maxAmount": "7" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "561", - "maxAmount": "5" - }, { "minAmount": "2", - "weight": "25.0", - "id": "563", - "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "25.0", + "weight": "1.0", "id": "564", "maxAmount": "2" }, { "minAmount": "1", "weight": "25.0", - "id": "6476", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "7863", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "minAmount": "4", + "weight": "4.0", "id": "995", - "maxAmount": "82" + "maxAmount": "4" + }, + { + "minAmount": "10", + "weight": "11.0", + "id": "995", + "maxAmount": "10" + }, + { + "minAmount": "18", + "weight": "2.0", + "id": "995", + "maxAmount": "18" + }, + { + "minAmount": "26", + "weight": "3.0", + "id": "995", + "maxAmount": "26" + }, + { + "minAmount": "35", + "weight": "2.0", + "id": "995", + "maxAmount": "35" }, { "minAmount": "5", - "weight": "50.0", + "weight": "25.0", "id": "313", - "maxAmount": "7" + "maxAmount": "5" }, { "minAmount": "1", - "weight": "25.0", - "id": "590", + "weight": "4.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "966", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1917", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "436", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "438", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "592", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "221", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1594", + "weight": "1.0", + "id": "590", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2331", + "weight": "1.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "4.0", + "id": "1351", "maxAmount": "1" } ] @@ -4044,6 +3471,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "78.7445", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "19.2498", @@ -4067,30 +3500,11 @@ "weight": "0.2779", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "78.7445", - "id": "0", - "maxAmount": "1" } ], "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": [ @@ -4114,6 +3528,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "94.9773", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "0.8562", @@ -4137,12 +3557,6 @@ "weight": "0.0486", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "94.9773", - "id": "0", - "maxAmount": "1" } ], "ids": "81,397,955,1767,3309", @@ -4166,6 +3580,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "93.9387", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "1.8077", @@ -4196,170 +3616,116 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1325", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1157", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1313", + "weight": "3.0", + "id": "1325", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1181", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1109", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1147", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "1195", - "maxAmount": "1" - }, - { - "minAmount": "10", - "weight": "25.0", + "minAmount": "60", + "weight": "8.0", "id": "554", - "maxAmount": "120" + "maxAmount": "60" }, { - "minAmount": "6", - "weight": "25.0", + "minAmount": "12", + "weight": "5.0", "id": "562", - "maxAmount": "24" + "maxAmount": "12" }, { "minAmount": "3", - "weight": "5.0", + "weight": "3.0", "id": "560", - "maxAmount": "6" + "maxAmount": "3" + }, + { + "minAmount": "30", + "weight": "1.0", + "id": "554", + "maxAmount": "30" }, { "minAmount": "1", - "weight": "5.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "211", - "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", + "weight": "1.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "10", - "weight": "50.0", + "weight": "7.0", "id": "995", - "maxAmount": "630" + "maxAmount": "10" + }, + { + "minAmount": "40", + "weight": "29.0", + "id": "995", + "maxAmount": "40" + }, + { + "minAmount": "120", + "weight": "40.0", + "id": "995", + "maxAmount": "120" + }, + { + "minAmount": "200", + "weight": "10.0", + "id": "995", + "maxAmount": "200" + }, + { + "minAmount": "450", + "weight": "1.0", + "id": "995", + "maxAmount": "450" }, { "minAmount": "1", - "weight": "25.0", - "id": "445", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1993", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2831", + "weight": "2.0", + "id": "444", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "93.9387", - "id": "0", + "weight": "4.0", + "id": "14426", "maxAmount": "1" } ] @@ -4374,6 +3740,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "62.1649", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "6.9276", @@ -4397,12 +3769,6 @@ "weight": "0.9267", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "62.1649", - "id": "0", - "maxAmount": "1" } ], "ids": "83,4698,4699,4700,4701", @@ -4410,110 +3776,128 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1353", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1311", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1353", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "1365", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1313", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1163", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1073", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1317", + "weight": "1.0", + "id": "1163", "maxAmount": "1" }, - { - "minAmount": "10", - "weight": "50.0", - "id": "995", - "maxAmount": "630" - }, { "minAmount": "75", - "weight": "50.0", + "weight": "8.0", "id": "554", "maxAmount": "75" }, { "minAmount": "15", - "weight": "50.0", + "weight": "3.0", "id": "562", "maxAmount": "15" }, { "minAmount": "5", - "weight": "50.0", + "weight": "3.0", "id": "560", "maxAmount": "5" }, { - "minAmount": "10", - "weight": "25.0", - "id": "1734", - "maxAmount": "10" + "minAmount": "37", + "weight": "1.0", + "id": "554", + "maxAmount": "37" + }, + { + "minAmount": "11", + "weight": "7.0", + "id": "995", + "maxAmount": "11" + }, + { + "minAmount": "44", + "weight": "28.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "132", + "weight": "40.0", + "id": "995", + "maxAmount": "132" + }, + { + "minAmount": "220", + "weight": "10.0", + "id": "995", + "maxAmount": "220" + }, + { + "minAmount": "460", + "weight": "1.0", + "id": "995", + "maxAmount": "460" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "361", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", "id": "2357", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "2733", - "maxAmount": "1" + "minAmount": "10", + "weight": "1.0", + "id": "1734", + "maxAmount": "10" }, { "minAmount": "1", "weight": "5.0", - "id": "31", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "1.0", + "id": "12070", "maxAmount": "1" } ] @@ -4528,6 +3912,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "22.5725", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "20.8465", @@ -4551,12 +3941,6 @@ "weight": "1.6236", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "22.5725", - "id": "0", - "maxAmount": "1" } ], "ids": "84,677,4702,4703,4704,4705", @@ -4564,200 +3948,146 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1361", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1283", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1313", + "weight": "3.0", + "id": "1365", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "2.0", + "id": "1361", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1165", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1147", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1163", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1113", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1185", - "maxAmount": "1" - }, - { - "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": "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": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, { "minAmount": "50", - "weight": "50.0", + "weight": "8.0", "id": "556", - "maxAmount": "75" + "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": "50.0", + "weight": "1.0", "id": "554", - "maxAmount": "40" - }, - { - "minAmount": "10", - "weight": "50.0", - "id": "562", - "maxAmount": "10" + "maxAmount": "37" }, { "minAmount": "3", - "weight": "5.0", + "weight": "1.0", "id": "563", - "maxAmount": "5" + "maxAmount": "3" }, { "minAmount": "1", - "weight": "50.0", - "id": "2361", + "weight": "23.0", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "20", - "weight": "5.0", - "id": "454", - "maxAmount": "20" + "minAmount": "30", + "weight": "7.0", + "id": "995", + "maxAmount": "30" + }, + { + "minAmount": "44", + "weight": "6.0", + "id": "995", + "maxAmount": "44" }, { "minAmount": "132", - "weight": "50.0", + "weight": "38.0", "id": "995", - "maxAmount": "1100" + "maxAmount": "132" + }, + { + "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": "2.0", + "id": "2361", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "133", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "1.0", + "id": "12070", "maxAmount": "1" } ] @@ -4821,14 +4151,7 @@ "charm": [], "ids": "89,987", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "7821", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -4840,6 +4163,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "90.3666", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.2856", @@ -4870,296 +4199,158 @@ "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": "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": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "6.0", "id": "1203", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "6.0", "id": "1137", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "882", - "maxAmount": "11" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1420", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1349", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "884", - "maxAmount": "1" - }, - { - "minAmount": "12", - "weight": "25.0", - "id": "884", - "maxAmount": "15" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1279", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", + "id": "1349", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1323", "maxAmount": "1" }, + { + "minAmount": "5", + "weight": "4.0", + "id": "882", + "maxAmount": "5" + }, { "minAmount": "1", - "weight": "5.0", - "id": "886", + "weight": "4.0", + "id": "884", "maxAmount": "1" }, { - "minAmount": "12", - "weight": "50.0", + "minAmount": "15", + "weight": "2.0", "id": "556", "maxAmount": "15" }, { - "minAmount": "2", - "weight": "50.0", - "id": "555", - "maxAmount": "9" - }, - { - "minAmount": "1", - "weight": "50.0", + "minAmount": "3", + "weight": "2.0", "id": "557", "maxAmount": "3" }, { "minAmount": "2", - "weight": "50.0", + "weight": "2.0", "id": "554", "maxAmount": "2" }, { - "minAmount": "1", - "weight": "50.0", - "id": "558", - "maxAmount": "25" + "minAmount": "3", + "weight": "2.0", + "id": "562", + "maxAmount": "3" }, { "minAmount": "3", - "weight": "25.0", - "id": "562", - "maxAmount": "8" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "561", "maxAmount": "3" }, { - "minAmount": "2", - "weight": "25.0", - "id": "564", - "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "563", - "maxAmount": "2" + "minAmount": "1", + "weight": "1.0", + "id": "886", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "560", - "maxAmount": "50" + "weight": "20.0", + "id": "14424", + "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", + "minAmount": "2", + "weight": "18.0", "id": "995", - "maxAmount": "65" + "maxAmount": "2" + }, + { + "minAmount": "4", + "weight": "7.0", + "id": "995", + "maxAmount": "4" + }, + { + "minAmount": "12", + "weight": "7.0", + "id": "995", + "maxAmount": "12" + }, + { + "minAmount": "16", + "weight": "4.0", + "id": "995", + "maxAmount": "16" + }, + { + "minAmount": "25", + "weight": "4.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "33", + "weight": "4.0", + "id": "995", + "maxAmount": "33" + }, + { + "minAmount": "48", + "weight": "4.0", + "id": "995", + "maxAmount": "48" }, { "minAmount": "1", - "weight": "25.0", - "id": "1925", - "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "2", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1947", + "weight": "18.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2349", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "1442", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1623", + "weight": "1.0", + "id": "440", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "4440", + "weight": "1.0", + "id": "1947", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2853", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "121", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "3036", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "175", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "155", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "90.3666", - "id": "0", + "weight": "1.0", + "id": "14426", "maxAmount": "1" } ] @@ -5183,12 +4374,6 @@ "ids": "94,3844,3850,3851", "description": "", "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "1391", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "50.0", @@ -5325,12 +4510,6 @@ }, { "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "526", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "100.0", @@ -5339,6 +4518,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "88.6454", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "3.5857", @@ -5362,12 +4547,6 @@ "weight": "0.1992", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "88.6454", - "id": "0", - "maxAmount": "1" } ], "ids": "95,142,1330,4413,4414,6047,6048,6049", @@ -5397,6 +4576,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "73.057", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.8446", @@ -5420,12 +4605,6 @@ "weight": "0.3701", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "73.057", - "id": "0", - "maxAmount": "1" } ], "ids": "96,97", @@ -5462,344 +4641,261 @@ "maxAmount": "1" } ], - "charm": [], + "charm": [ + { + "minAmount": "1", + "weight": "88.8944", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "9.6568", + "id": "12158", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.3775", + "id": "12159", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.9295", + "id": "12160", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.1418", + "id": "12163", + "maxAmount": "1" + } + ], "ids": "100,101,102,444,445,1769,1770,1771,1772,1773,1774,1775,1776,2274,2275,2276,2277,2278,2279,2280,2281,2678,2679,2680,2681,3264,3265,3266,3267,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,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,7964", "description": "", "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1203", - "maxAmount": "1" - }, - { - "minAmount": "16", - "weight": "50.0", - "id": "882", - "maxAmount": "16" - }, - { - "minAmount": "8", - "weight": "50.0", - "id": "877", - "maxAmount": "8" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1139", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1237", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "1173", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1103", + "weight": "3.0", + "id": "1351", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1155", + "weight": "3.0", + "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", + "id": "1203", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1139", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1189", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1277", + "weight": "1.0", + "id": "1155", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1291", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1009", - "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "884", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1351", - "maxAmount": "1" - }, - { - "minAmount": "5", - "weight": "5.0", - "id": "825", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1153", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1141", + "weight": "1.0", + "id": "1239", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1129", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1917", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2307", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2138", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "556", - "maxAmount": "10" - }, - { - "minAmount": "27", - "weight": "50.0", - "id": "559", - "maxAmount": "27" - }, - { - "minAmount": "4", - "weight": "5.0", - "id": "557", - "maxAmount": "4" - }, { "minAmount": "6", - "weight": "50.0", + "weight": "5.0", "id": "555", "maxAmount": "6" }, { - "minAmount": "2", - "weight": "25.0", - "id": "558", - "maxAmount": "19" + "minAmount": "7", + "weight": "5.0", + "id": "559", + "maxAmount": "7" + }, + { + "minAmount": "4", + "weight": "3.0", + "id": "557", + "maxAmount": "4" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "882", + "maxAmount": "7" + }, + { + "minAmount": "8", + "weight": "3.0", + "id": "877", + "maxAmount": "8" + }, + { + "minAmount": "5", + "weight": "2.0", + "id": "825", + "maxAmount": "5" }, { "minAmount": "1", - "weight": "5.0", - "id": "565", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "562", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "561", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "199", + "weight": "2.0", + "id": "14424", "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": "5.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "211", - "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": "1", - "weight": "50.0", - "id": "1438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1949", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "995", - "maxAmount": "24" + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "35.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "9", + "weight": "3.0", + "id": "995", + "maxAmount": "9" + }, + { + "minAmount": "15", + "weight": "3.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "20", + "weight": "2.0", + "id": "995", + "maxAmount": "20" }, { "minAmount": "1", - "weight": "50.0", + "weight": "15.0", + "id": "2347", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "5.0", "id": "288", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1440", + "weight": "3.0", + "id": "1949", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1987", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1442", + "weight": "2.0", + "id": "1965", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", + "id": "1917", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", "id": "10999", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "7812", + "weight": "1.0", + "id": "1009", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", + "id": "1987", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "438", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1438", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "946", + "weight": "1.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "1448", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "438", + "id": "0", "maxAmount": "1" } ] @@ -5826,6 +4922,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "85.6976", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "10.7526", @@ -5849,30 +4951,11 @@ "weight": "0.2821", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "85.6976", - "id": "0", - "maxAmount": "1" } ], "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": [ @@ -5896,6 +4979,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "82.1397", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "16.534", @@ -5919,30 +5008,11 @@ "weight": "0.3537", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "82.1397", - "id": "0", - "maxAmount": "1" } ], "ids": "106", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "7815", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -5954,6 +5024,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "30.6289", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "55.6271", @@ -5977,11 +5053,25 @@ "weight": "1.5185", "id": "12163", "maxAmount": "1" + } + ], + "tertiary": [ + { + "minAmount": "1", + "weight": "9973.0", + "id": "0", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "30.6289", - "id": "0", + "weight": "25.0", + "id": "10976", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "10977", "maxAmount": "1" } ], @@ -5990,207 +5080,129 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1181", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1393", "maxAmount": "1" }, - { - "minAmount": "12", - "weight": "25.0", - "id": "892", - "maxAmount": "12" - }, { "minAmount": "1", - "weight": "8.0", + "weight": "1.0", "id": "1333", "maxAmount": "1" }, { - "minAmount": "37", - "weight": "25.0", + "minAmount": "150", + "weight": "10.0", "id": "554", - "maxAmount": "37" + "maxAmount": "150" }, { "minAmount": "5", - "weight": "25.0", + "weight": "7.0", "id": "562", "maxAmount": "5" }, + { + "minAmount": "12", + "weight": "5.0", + "id": "892", + "maxAmount": "12" + }, { "minAmount": "5", - "weight": "8.0", + "weight": "4.0", "id": "565", "maxAmount": "5" }, + { + "minAmount": "37", + "weight": "1.0", + "id": "554", + "maxAmount": "37" + }, { "minAmount": "2", - "weight": "8.0", + "weight": "1.0", "id": "563", "maxAmount": "2" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "19.0", + "id": "14424", "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": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "25", - "weight": "50.0", - "id": "995", - "maxAmount": "25" - }, { "minAmount": "60", - "weight": "50.0", + "weight": "40.0", "id": "995", "maxAmount": "60" }, { - "minAmount": "1", - "weight": "25.0", - "id": "7872", - "maxAmount": "1" + "minAmount": "15", + "weight": "7.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "6.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "300", + "weight": "2.0", + "id": "995", + "maxAmount": "300" + }, + { + "minAmount": "50", + "weight": "1.0", + "id": "995", + "maxAmount": "50" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "379", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "2353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "117", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "10976", + "weight": "11.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "10977", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", + "weight": "1.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1145", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5733", - "maxAmount": "1" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "554", - "maxAmount": "100" - }, - { - "minAmount": "150", - "weight": "25.0", - "id": "554", - "maxAmount": "150" } ] }, @@ -6204,6 +5216,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "45.9587", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "44.7868", @@ -6229,301 +5247,195 @@ "maxAmount": "1" } ], - "ids": "111,3072,4685,4686,4687", - "description": "", - "main": [ + "tertiary": [ { "minAmount": "1", - "weight": "50.0", - "id": "1363", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1067", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1309", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1353", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1195", - "maxAmount": "1" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "890", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1181", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1428", - "maxAmount": "1" - }, - { - "minAmount": "12", - "weight": "50.0", - "id": "555", - "maxAmount": "12" - }, - { - "minAmount": "24", - "weight": "50.0", - "id": "558", - "maxAmount": "24" - }, - { - "minAmount": "37", - "weight": "50.0", - "id": "559", - "maxAmount": "37" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "564", - "maxAmount": "4" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "563", - "maxAmount": "3" - }, - { - "minAmount": "6", - "weight": "25.0", - "id": "561", - "maxAmount": "6" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "560", - "maxAmount": "4" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "565", - "maxAmount": "4" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5293", + "weight": "9973.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", "weight": "25.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5104", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "12176", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "8", - "weight": "50.0", - "id": "995", - "maxAmount": "450" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "7875", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1963", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1993", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "447", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1454", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "10977", "maxAmount": "1" - }, + } + ], + "ids": "111,3072,4685,4686,4687", + "description": "", + "main": [ { "minAmount": "1", "weight": "5.0", - "id": "2829", + "id": "1309", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "4.0", + "id": "1195", "maxAmount": "1" }, { "minAmount": "1", - "weight": "45.9587", - "id": "0", + "weight": "4.0", + "id": "1353", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1281", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1067", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1428", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1181", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "6.0", + "id": "890", + "maxAmount": "5" + }, + { + "minAmount": "6", + "weight": "4.0", + "id": "561", + "maxAmount": "6" + }, + { + "minAmount": "24", + "weight": "3.0", + "id": "558", + "maxAmount": "24" + }, + { + "minAmount": "37", + "weight": "3.0", + "id": "559", + "maxAmount": "37" + }, + { + "minAmount": "3", + "weight": "2.0", + "id": "563", + "maxAmount": "3" + }, + { + "minAmount": "12", + "weight": "1.0", + "id": "555", + "maxAmount": "12" + }, + { + "minAmount": "4", + "weight": "1.0", + "id": "564", + "maxAmount": "4" + }, + { + "minAmount": "3", + "weight": "1.0", + "id": "560", + "maxAmount": "3" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "565", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "8.0", + "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", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "22", + "weight": "6.0", + "id": "995", + "maxAmount": "22" + }, + { + "minAmount": "400", + "weight": "1.0", + "id": "995", + "maxAmount": "400" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "1993", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "447", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1963", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "5733", "maxAmount": "1" } ] @@ -6538,6 +5450,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "61.6452", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "32.5923", @@ -6563,373 +5481,183 @@ "maxAmount": "1" } ], - "ids": "112,1587,1588,1681,4534,4688,4706", - "description": "", - "main": [ + "tertiary": [ { "minAmount": "1", - "weight": "50.0", - "id": "1389", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1243", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1285", - "maxAmount": "1" - }, - { - "minAmount": "30", - "weight": "50.0", - "id": "886", - "maxAmount": "30" - }, - { - "minAmount": "15", - "weight": "50.0", - "id": "884", - "maxAmount": "15" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1287", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1165", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1179", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1141", + "weight": "9973.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", "weight": "25.0", - "id": "1193", - "maxAmount": "1" - }, - { - "minAmount": "18", - "weight": "50.0", - "id": "556", - "maxAmount": "18" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "564", - "maxAmount": "3" - }, - { - "minAmount": "27", - "weight": "50.0", - "id": "557", - "maxAmount": "27" - }, - { - "minAmount": "6", - "weight": "50.0", - "id": "561", - "maxAmount": "6" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "563", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "565", - "maxAmount": "1" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "562", - "maxAmount": "7" - }, - { - "minAmount": "13", - "weight": "5.0", - "id": "560", - "maxAmount": "13" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5293", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5104", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "12176", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "4206", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5301", - "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": "5303", - "maxAmount": "1" - }, - { - "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": "5.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.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": "207", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "995", - "maxAmount": "500" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2353", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "7871", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "453", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1969", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1442", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "10977", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", "id": "10976", "maxAmount": "1" }, + { + "minAmount": "1", + "weight": "2.0", + "id": "10977", + "maxAmount": "1" + } + ], + "ids": "112,1587,1588,1681,4534,4688,4706", + "description": "", + "main": [ { "minAmount": "1", "weight": "5.0", - "id": "31", + "id": "1179", "maxAmount": "1" }, { "minAmount": "1", - "weight": "61.6452", - "id": "0", + "weight": "2.0", + "id": "1389", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1141", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1285", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1243", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1193", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "4.0", + "id": "563", + "maxAmount": "3" + }, + { + "minAmount": "18", + "weight": "3.0", + "id": "556", + "maxAmount": "18" + }, + { + "minAmount": "27", + "weight": "3.0", + "id": "557", + "maxAmount": "27" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "562", + "maxAmount": "7" + }, + { + "minAmount": "6", + "weight": "3.0", + "id": "561", + "maxAmount": "6" + }, + { + "minAmount": "3", + "weight": "2.0", + "id": "564", + "maxAmount": "3" + }, + { + "minAmount": "15", + "weight": "2.0", + "id": "884", + "maxAmount": "15" + }, + { + "minAmount": "30", + "weight": "1.0", + "id": "886", + "maxAmount": "30" + }, + { + "minAmount": "3", + "weight": "1.0", + "id": "560", + "maxAmount": "3" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "565", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "35.0", + "id": "14422", + "maxAmount": "1" + }, + { + "minAmount": "37", + "weight": "19.0", + "id": "995", + "maxAmount": "37" + }, + { + "minAmount": "2", + "weight": "8.0", + "id": "995", + "maxAmount": "2" + }, + { + "minAmount": "119", + "weight": "10.0", + "id": "995", + "maxAmount": "119" + }, + { + "minAmount": "300", + "weight": "2.0", + "id": "995", + "maxAmount": "300" + }, + { + "minAmount": "1", + "weight": "6.0", + "id": "2353", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "453", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1969", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "14426", "maxAmount": "1" } ] @@ -6944,6 +5672,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "58.3163", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "36.367", @@ -6967,12 +5701,6 @@ "weight": "0.4168", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "58.3163", - "id": "0", - "maxAmount": "1" } ], "ids": "113,6268", @@ -6980,291 +5708,141 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "30.0", "id": "1237", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1239", "maxAmount": "1" }, { "minAmount": "3", - "weight": "25.0", + "weight": "2.0", "id": "827", "maxAmount": "5" }, { "minAmount": "2", - "weight": "50.0", + "weight": "10.0", "id": "561", "maxAmount": "2" }, { "minAmount": "5", - "weight": "50.0", + "weight": "2.0", "id": "561", "maxAmount": "5" }, { - "minAmount": "1", - "weight": "50.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5293", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5321", - "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": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5104", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5281", - "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": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5301", - "maxAmount": "1" + "minAmount": "10", + "weight": "2.0", + "id": "561", + "maxAmount": "10" }, { "minAmount": "1", "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", "id": "1533", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "1525", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "203", + "weight": "8.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "201", + "weight": "15.0", + "id": "14422", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "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": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "946", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "2114", - "maxAmount": "2" - }, { "minAmount": "22", - "weight": "50.0", + "weight": "20.0", "id": "6306", "maxAmount": "22" }, - { - "minAmount": "22", - "weight": "50.0", - "id": "995", - "maxAmount": "22" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "7847", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "532", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "526", - "maxAmount": "1" - }, { "minAmount": "2", - "weight": "50.0", - "id": "526", + "weight": "4.0", + "id": "2114", "maxAmount": "2" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1963", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2843", + "weight": "1.0", + "id": "946", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "3.0", + "id": "526", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "2.0", + "id": "526", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "532", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "2.0", + "id": "532", + "maxAmount": "3" + }, + { + "minAmount": "6", + "weight": "1.0", + "id": "532", + "maxAmount": "6" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "536", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "2.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "5733", + "maxAmount": "1" } ] }, @@ -7280,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" } ] }, @@ -7492,6 +6030,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "82.5938", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "13.9157", @@ -7515,170 +6059,40 @@ "weight": "0.3806", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "82.5938", - "id": "0", - "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" } @@ -8168,7 +6582,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2737", + "id": "1", "maxAmount": "1" }, { @@ -8212,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" } ] }, @@ -8517,6 +6817,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "90.789", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "3.7853", @@ -8540,12 +6846,6 @@ "weight": "0.5864", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "90.789", - "id": "0", - "maxAmount": "1" } ], "ids": "118,120,121,382,582,3219,3220,3221,3268,3269,3270,3271,3272,3273,3274,3275,3294,3295,4316,5880,5881,5882,5883,5884,5885", @@ -8553,129 +6853,129 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "10.0", "id": "1265", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2349", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1267", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "453", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2351", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "561", - "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "562", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1375", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", + "id": "1267", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1363", "maxAmount": "1" }, { - "minAmount": "2", - "weight": "25.0", + "minAmount": "1", + "weight": "7.0", "id": "877", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "4.0", + "id": "562", "maxAmount": "2" }, { - "minAmount": "11", - "weight": "25.0", - "id": "877", - "maxAmount": "11" + "minAmount": "2", + "weight": "4.0", + "id": "561", + "maxAmount": "2" }, { "minAmount": "4", - "weight": "50.0", + "weight": "20.0", "id": "995", "maxAmount": "4" }, { "minAmount": "10", - "weight": "50.0", + "weight": "15.0", "id": "995", "maxAmount": "10" }, + { + "minAmount": "30", + "weight": "2.0", + "id": "995", + "maxAmount": "30" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "20.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", "id": "2347", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "7.0", + "id": "2349", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "438", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "2351", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "453", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", "id": "1913", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -8689,6 +6989,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "81.6299", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.4137", @@ -8712,12 +7018,6 @@ "weight": "2.6896", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "81.6299", - "id": "0", - "maxAmount": "1" } ], "ids": "119,2423", @@ -8725,171 +7025,147 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "2.0", "id": "1157", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "1299", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "1181", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1165", - "maxAmount": "1" - }, { "minAmount": "10", - "weight": "50.0", + "weight": "1.0", "id": "555", "maxAmount": "10" }, - { - "minAmount": "12", - "weight": "50.0", - "id": "556", - "maxAmount": "12" - }, { "minAmount": "24", - "weight": "50.0", + "weight": "3.0", "id": "556", "maxAmount": "24" }, { "minAmount": "37", - "weight": "50.0", + "weight": "3.0", "id": "558", "maxAmount": "37" }, { "minAmount": "3", - "weight": "50.0", + "weight": "2.0", "id": "564", "maxAmount": "3" }, - { - "minAmount": "6", - "weight": "50.0", - "id": "564", - "maxAmount": "6" - }, { "minAmount": "10", - "weight": "25.0", + "weight": "3.0", "id": "562", "maxAmount": "10" }, - { - "minAmount": "37", - "weight": "25.0", - "id": "562", - "maxAmount": "37" - }, { "minAmount": "9", - "weight": "25.0", + "weight": "3.0", "id": "561", "maxAmount": "9" }, - { - "minAmount": "18", - "weight": "25.0", - "id": "561", - "maxAmount": "18" - }, { "minAmount": "3", - "weight": "25.0", + "weight": "4.0", "id": "563", "maxAmount": "3" }, - { - "minAmount": "6", - "weight": "25.0", - "id": "563", - "maxAmount": "6" - }, { "minAmount": "3", - "weight": "5.0", + "weight": "1.0", "id": "560", "maxAmount": "3" }, - { - "minAmount": "6", - "weight": "5.0", - "id": "560", - "maxAmount": "6" - }, - { - "minAmount": "25", - "weight": "50.0", - "id": "995", - "maxAmount": "25" - }, - { - "minAmount": "47", - "weight": "50.0", - "id": "995", - "maxAmount": "47" - }, { "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "453", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "7.0", "id": "991", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1985", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "6.0", "id": "2359", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1982", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "207", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "31", + "id": "0", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" + "minAmount": "15", + "weight": "2.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "11.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "47", + "weight": "13.0", + "id": "995", + "maxAmount": "47" + }, + { + "minAmount": "92", + "weight": "40.0", + "id": "995", + "maxAmount": "92" + }, + { + "minAmount": "150", + "weight": "10.0", + "id": "995", + "maxAmount": "150" + }, + { + "minAmount": "350", + "weight": "2.0", + "id": "995", + "maxAmount": "350" } ] }, @@ -8903,6 +7179,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "82.4038", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "15.9877", @@ -9308,6 +7590,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "74.8304", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.0131", @@ -9331,12 +7619,6 @@ "weight": "0.6814", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "74.8304", - "id": "0", - "maxAmount": "1" } ], "ids": "124", @@ -9344,309 +7626,99 @@ "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": "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": "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": "12", - "weight": "50.0", - "id": "557", - "maxAmount": "12" - }, - { - "minAmount": "60", - "weight": "50.0", - "id": "557", - "maxAmount": "60" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "563", - "maxAmount": "2" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "561", - "maxAmount": "3" - }, - { - "minAmount": "6", - "weight": "50.0", - "id": "561", - "maxAmount": "6" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "562", - "maxAmount": "3" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "560", - "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "565", - "maxAmount": "2" - }, - { - "minAmount": "45", - "weight": "5.0", - "id": "556", - "maxAmount": "45" - }, - { - "minAmount": "6", - "weight": "5.0", - "id": "554", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5104", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5293", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "12176", - "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": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5300", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "12" - }, - { - "minAmount": "24", - "weight": "50.0", - "id": "995", - "maxAmount": "24" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1385", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1241", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "6808", + "weight": "2.0", + "id": "1385", + "maxAmount": "1" + }, + { + "minAmount": "12", + "weight": "13.0", + "id": "557", + "maxAmount": "12" + }, + { + "minAmount": "3", + "weight": "9.0", + "id": "561", + "maxAmount": "3" + }, + { + "minAmount": "3", + "weight": "7.0", + "id": "562", + "maxAmount": "3" + }, + { + "minAmount": "2", + "weight": "6.0", + "id": "563", + "maxAmount": "2" + }, + { + "minAmount": "2", + "weight": "4.0", + "id": "560", + "maxAmount": "2" + }, + { + "minAmount": "60", + "weight": "3.0", + "id": "557", + "maxAmount": "60" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "565", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "14.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "18.0", + "id": "14422", "maxAmount": "1" }, + { + "minAmount": "12", + "weight": "18.0", + "id": "995", + "maxAmount": "12" + }, { "minAmount": "1", - "weight": "100.0", + "weight": "28.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "14426", + "maxAmount": "1" } ] }, { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "53.6086", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "41.9912", @@ -9677,345 +7749,147 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "1363", "maxAmount": "1" }, - { - "minAmount": "3", - "weight": "50.0", - "id": "888", - "maxAmount": "3" - }, { "minAmount": "1", - "weight": "25.0", - "id": "1428", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "890", - "maxAmount": "2" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "890", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1309", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1281", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1195", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", + "id": "1428", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1181", "maxAmount": "1" }, - { - "minAmount": "2", - "weight": "50.0", - "id": "564", - "maxAmount": "4" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "562", - "maxAmount": "3" - }, { "minAmount": "4", - "weight": "50.0", + "weight": "10.0", "id": "561", "maxAmount": "4" }, { - "minAmount": "2", - "weight": "50.0", - "id": "563", - "maxAmount": "4" + "minAmount": "3", + "weight": "8.0", + "id": "562", + "maxAmount": "3" }, { "minAmount": "2", - "weight": "25.0", + "weight": "7.0", + "id": "563", + "maxAmount": "2" + }, + { + "minAmount": "2", + "weight": "5.0", + "id": "564", + "maxAmount": "2" + }, + { + "minAmount": "3", + "weight": "5.0", + "id": "888", + "maxAmount": "3" + }, + { + "minAmount": "2", + "weight": "3.0", "id": "560", "maxAmount": "2" }, + { + "minAmount": "2", + "weight": "2.0", + "id": "890", + "maxAmount": "2" + }, { "minAmount": "5", - "weight": "25.0", - "id": "560", + "weight": "1.0", + "id": "890", "maxAmount": "5" }, { "minAmount": "2", - "weight": "25.0", + "weight": "1.0", "id": "565", - "maxAmount": "50" - }, - { - "minAmount": "24", - "weight": "5.0", - "id": "558", - "maxAmount": "24" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "4694", "maxAmount": "2" }, { "minAmount": "1", - "weight": "50.0", - "id": "5291", + "weight": "10.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "5293", + "weight": "18.0", + "id": "14422", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5104", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "12176", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "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": "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": "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": "10.0", + "id": "995", + "maxAmount": "4" }, { "minAmount": "15", - "weight": "50.0", + "weight": "18.0", "id": "995", "maxAmount": "15" }, { - "minAmount": "465", - "weight": "50.0", + "minAmount": "125", + "weight": "2.0", "id": "995", - "maxAmount": "465" + "maxAmount": "125" }, { "minAmount": "1", - "weight": "5.0", - "id": "447", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2853", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "53.6086", + "weight": "13.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "5733", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "447", + "maxAmount": "1" } ] }, @@ -10027,176 +7901,98 @@ "main": [ { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", + "id": "1391", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1428", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1391", - "maxAmount": "1" - }, - { - "minAmount": "15", - "weight": "5.0", - "id": "888", - "maxAmount": "15" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1357", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "1432", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.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": "5.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "211", - "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": "5", + "weight": "9.0", + "id": "561", + "maxAmount": "5" }, { "minAmount": "4", - "weight": "50.0", + "weight": "8.0", "id": "562", "maxAmount": "4" }, { - "minAmount": "2", - "weight": "50.0", - "id": "564", - "maxAmount": "2" + "minAmount": "15", + "weight": "8.0", + "id": "888", + "maxAmount": "15" }, { "minAmount": "2", - "weight": "50.0", + "weight": "7.0", "id": "563", "maxAmount": "2" }, { "minAmount": "2", - "weight": "25.0", + "weight": "5.0", + "id": "564", + "maxAmount": "2" + }, + { + "minAmount": "2", + "weight": "4.0", "id": "560", "maxAmount": "2" }, - { - "minAmount": "5", - "weight": "25.0", - "id": "561", - "maxAmount": "10" - }, { "minAmount": "2", - "weight": "5.0", + "weight": "1.0", "id": "565", "maxAmount": "2" }, + { + "minAmount": "1", + "weight": "10.0", + "id": "14424", + "maxAmount": "1" + }, { "minAmount": "15", "weight": "50.0", "id": "995", - "maxAmount": "40" + "maxAmount": "15" }, { "minAmount": "1", - "weight": "50.0", - "id": "995", + "weight": "18.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1454", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2357", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "355", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "1641", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "1.0", + "id": "355", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "14426", "maxAmount": "1" } ] @@ -10225,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": [ @@ -10258,6 +8047,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "54.5455", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "21.6783", @@ -10281,12 +8076,6 @@ "weight": "6.2937", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "54.5455", - "id": "0", - "maxAmount": "1" } ], "ids": "130", @@ -10433,7 +8222,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2843", + "id": "12070", "maxAmount": "1" }, { @@ -10467,7 +8256,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "3186", + "id": "526", "maxAmount": "1" }, { @@ -10511,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": [ @@ -10542,6 +8318,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "84.4867", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.9954", @@ -10565,30 +8347,25 @@ "weight": "0.2979", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "84.4867", - "id": "0", - "maxAmount": "1" } ], "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": [ @@ -10614,6 +8391,12 @@ } ], "charm": [ + { + "minAmount": "3", + "weight": "11.0653", + "id": "0", + "maxAmount": "3" + }, { "minAmount": "3", "weight": "20.685", @@ -10644,339 +8427,177 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "96.0", "id": "1371", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "24.0", "id": "1289", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", "id": "1303", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "24.0", "id": "1432", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", "id": "1113", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", "id": "1147", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", "id": "1185", "maxAmount": "1" }, { "minAmount": "20", - "weight": "50.0", + "weight": "48.0", "id": "4699", "maxAmount": "40" }, { "minAmount": "20", - "weight": "50.0", + "weight": "24.0", "id": "560", "maxAmount": "40" }, - { - "minAmount": "10", - "weight": "50.0", - "id": "565", - "maxAmount": "20" - }, { "minAmount": "20", - "weight": "25.0", + "weight": "16.0", "id": "563", "maxAmount": "40" }, { - "minAmount": "8", - "weight": "25.0", - "id": "200", - "maxAmount": "10" + "minAmount": "10", + "weight": "48.0", + "id": "565", + "maxAmount": "20" }, { "minAmount": "8", - "weight": "25.0", - "id": "202", - "maxAmount": "10" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "204", - "maxAmount": "10" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "206", - "maxAmount": "10" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "208", - "maxAmount": "10" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "210", - "maxAmount": "10" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "212", - "maxAmount": "10" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "214", - "maxAmount": "10" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "216", - "maxAmount": "10" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "2486", + "weight": "144.0", + "id": "14424", "maxAmount": "10" }, { "minAmount": "4", - "weight": "25.0", - "id": "5323", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5321", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5292", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5294", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5295", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "12176", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5296", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5298", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5299", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5300", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5301", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5302", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5304", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5100", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5104", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5105", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5311", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5280", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5281", - "maxAmount": "10" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5282", + "weight": "144.0", + "id": "14422", "maxAmount": "10" }, { "minAmount": "5", - "weight": "50.0", + "weight": "16.0", "id": "385", "maxAmount": "15" }, { "minAmount": "1", - "weight": "25.0", + "weight": "16.0", "id": "163", "maxAmount": "6" }, { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", "id": "139", "maxAmount": "3" }, { "minAmount": "3", - "weight": "50.0", - "id": "2362", + "weight": "24.0", + "id": "2361", "maxAmount": "5" }, { "minAmount": "3", - "weight": "50.0", + "weight": "16.0", "id": "1602", "maxAmount": "5" }, { "minAmount": "2", - "weight": "25.0", - "id": "1443", + "weight": "24.0", + "id": "1442", "maxAmount": "5" }, { - "minAmount": "3000", - "weight": "100.0", + "minAmount": "11000", + "weight": "216.0", "id": "995", "maxAmount": "13000" }, { "minAmount": "1", - "weight": "3.42", + "weight": "4.0", "id": "14484", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.71", + "weight": "1.0", "id": "14472", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.71", + "weight": "1.0", "id": "14474", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.71", + "weight": "1.0", "id": "14476", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "2677", + "weight": "64.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "32.0", "id": "31", "maxAmount": "1" }, { - "minAmount": "3", - "weight": "11.0653", + "minAmount": "1", + "weight": "1.0", "id": "0", - "maxAmount": "3" + "maxAmount": "1" } ] }, { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "70.7701", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "10.4637", @@ -11000,12 +8621,6 @@ "weight": "0.6011", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "70.7701", - "id": "0", - "maxAmount": "1" } ], "ids": "158", @@ -11013,147 +8628,99 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1217", + "weight": "1.0", + "id": "1233", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "869", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1165", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1297", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "1.0", + "id": "1428", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "201", + "weight": "1.0", + "id": "1245", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "203", + "weight": "1.0", + "id": "581", "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": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", + "weight": "18.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "3", - "weight": "50.0", + "weight": "9.0", "id": "564", "maxAmount": "3" }, { "minAmount": "2", - "weight": "25.0", + "weight": "4.0", "id": "560", "maxAmount": "2" }, { "minAmount": "45", - "weight": "25.0", + "weight": "4.0", "id": "556", "maxAmount": "45" }, { "minAmount": "2", - "weight": "50.0", + "weight": "6.0", "id": "565", "maxAmount": "2" }, { "minAmount": "8", - "weight": "50.0", + "weight": "46.0", "id": "995", "maxAmount": "8" }, { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "2359", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "187", + "weight": "8.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", + "weight": "22.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "187", + "maxAmount": "1" } ] }, @@ -11172,87 +8739,99 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "2169", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "2.0", "id": "2102", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "2128", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "2.0", "id": "2120", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2164", + "weight": "2.0", + "id": "2128", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2026", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2165", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2167", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2025", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2166", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2807", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "2.0", "id": "2171", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", + "id": "2167", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "2164", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "2165", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "2166", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "2026", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "2025", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", "id": "2023", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "5733", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "50.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "10", + "weight": "49.0", + "id": "995", + "maxAmount": "10" + }, + { + "minAmount": "125", + "weight": "1.0", + "id": "995", + "maxAmount": "125" } ] }, @@ -11271,188 +8850,182 @@ "main": [ { "minAmount": "10", - "weight": "50.0", + "weight": "3.0", "id": "557", "maxAmount": "10" }, { "minAmount": "18", - "weight": "50.0", + "weight": "2.0", "id": "557", "maxAmount": "18" }, { "minAmount": "10", - "weight": "50.0", + "weight": "3.0", "id": "556", "maxAmount": "10" }, { "minAmount": "18", - "weight": "50.0", + "weight": "2.0", "id": "556", "maxAmount": "18" }, { "minAmount": "10", - "weight": "50.0", + "weight": "3.0", "id": "555", "maxAmount": "10" }, { "minAmount": "18", - "weight": "50.0", + "weight": "2.0", "id": "555", "maxAmount": "18" }, { "minAmount": "10", - "weight": "50.0", + "weight": "3.0", "id": "554", "maxAmount": "10" }, { "minAmount": "18", - "weight": "50.0", + "weight": "2.0", "id": "554", "maxAmount": "18" }, { "minAmount": "10", - "weight": "50.0", + "weight": "3.0", "id": "558", "maxAmount": "10" }, { "minAmount": "18", - "weight": "50.0", + "weight": "2.0", "id": "558", "maxAmount": "18" }, { "minAmount": "10", - "weight": "50.0", + "weight": "3.0", "id": "559", "maxAmount": "10" }, { "minAmount": "18", - "weight": "50.0", + "weight": "2.0", "id": "559", "maxAmount": "18" }, { "minAmount": "4", - "weight": "50.0", + "weight": "6.0", "id": "562", "maxAmount": "4" }, { - "minAmount": "5", - "weight": "50.0", - "id": "562", - "maxAmount": "5" - }, - { - "minAmount": "2", - "weight": "25.0", + "minAmount": "4", + "weight": "7.0", "id": "561", - "maxAmount": "5" + "maxAmount": "4" }, { "minAmount": "3", - "weight": "50.0", + "weight": "1.0", "id": "563", "maxAmount": "3" }, { "minAmount": "2", - "weight": "25.0", + "weight": "1.0", "id": "564", "maxAmount": "2" }, - { - "minAmount": "4", - "weight": "25.0", - "id": "564", - "maxAmount": "4" - }, { "minAmount": "2", - "weight": "50.0", + "weight": "2.0", "id": "565", "maxAmount": "2" }, - { - "minAmount": "4", - "weight": "50.0", - "id": "565", - "maxAmount": "4" - }, { "minAmount": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "30" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "8.0", "id": "1379", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1444", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "1442", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1448", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1383", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "10.0", + "weight": "3.0", "id": "581", "maxAmount": "1" }, { "minAmount": "1", - "weight": "10.0", + "weight": "4.0", "id": "1017", "maxAmount": "1" }, + { + "minAmount": "36", + "weight": "4.0", + "id": "557", + "maxAmount": "36" + }, { "minAmount": "1", + "weight": "17.0", + "id": "995", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "16.0", + "id": "995", + "maxAmount": "2" + }, + { + "minAmount": "4", + "weight": "7.0", + "id": "995", + "maxAmount": "4" + }, + { + "minAmount": "29", + "weight": "3.0", + "id": "995", + "maxAmount": "29" + }, + { + "minAmount": "30", "weight": "1.0", - "id": "987", + "id": "995", + "maxAmount": "30" + }, + { + "minAmount": "1", + "weight": "15.0", + "id": "0", "maxAmount": "1" } ] @@ -11485,153 +9058,99 @@ "description": "", "main": [ { - "minAmount": "4", - "weight": "25.0", + "minAmount": "2", + "weight": "27.0", "id": "877", - "maxAmount": "11" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1139", - "maxAmount": "1" + "maxAmount": "12" }, { "minAmount": "6", - "weight": "25.0", + "weight": "2.0", "id": "555", "maxAmount": "6" }, { "minAmount": "5", - "weight": "25.0", + "weight": "2.0", "id": "557", "maxAmount": "5" }, { "minAmount": "9", - "weight": "5.0", + "weight": "3.0", "id": "558", "maxAmount": "9" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "13.0", + "id": "14424", "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": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "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": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "5", - "weight": "50.0", - "id": "995", - "maxAmount": "5" - }, - { - "minAmount": "10", - "weight": "50.0", - "id": "995", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "39.0", "id": "954", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "436", + "weight": "13.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "6.0", "id": "313", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1965", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "946", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2705", + "weight": "2.0", + "id": "1139", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "12.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "15", + "weight": "3.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -11645,7 +9164,38 @@ "maxAmount": "1" } ], - "charm": [], + "charm": [ + { + "minAmount": "1", + "weight": "94.1431", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.2003", + "id": "12158", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.2851", + "id": "12159", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.7167", + "id": "12160", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.6549", + "id": "12163", + "maxAmount": "1" + } + ], "ids": "178,179,2751,2777,2778,6189", "description": "", "main": [ @@ -11910,238 +9460,136 @@ "ids": "181,2547,7105", "description": "", "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "222", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "238", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "226", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "1939", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "224", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "1581", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "1976", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "10.0", - "id": "240", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "2152", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "9737", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "205", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "219", - "maxAmount": "2" - }, { "minAmount": "2", - "weight": "50.0", + "weight": "7.0", "id": "563", "maxAmount": "2" }, { - "minAmount": "9", - "weight": "50.0", - "id": "559", - "maxAmount": "9" + "minAmount": "2", + "weight": "4.0", + "id": "9142", + "maxAmount": "12" }, { - "minAmount": "9", - "weight": "50.0", + "minAmount": "36", + "weight": "3.0", "id": "556", "maxAmount": "36" }, { "minAmount": "9", - "weight": "50.0", + "weight": "2.0", + "id": "559", + "maxAmount": "9" + }, + { + "minAmount": "9", + "weight": "2.0", "id": "557", "maxAmount": "9" }, { "minAmount": "12", - "weight": "50.0", + "weight": "2.0", "id": "558", "maxAmount": "12" }, { "minAmount": "3", - "weight": "25.0", + "weight": "1.0", "id": "561", "maxAmount": "3" }, + { + "minAmount": "1", + "weight": "35.0", + "id": "14424", + "maxAmount": "1" + }, { "minAmount": "2", - "weight": "50.0", - "id": "995", + "weight": "11.0", + "id": "14424", "maxAmount": "2" }, { "minAmount": "3", - "weight": "50.0", + "weight": "5.0", "id": "995", "maxAmount": "3" }, { - "minAmount": "2", - "weight": "50.0", - "id": "9142", - "maxAmount": "12" + "minAmount": "8", + "weight": "5.0", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "29", + "weight": "3.0", + "id": "995", + "maxAmount": "29" + }, + { + "minAmount": "35", + "weight": "1.0", + "id": "995", + "maxAmount": "35" }, { "minAmount": "1", - "weight": "50.0", + "weight": "30.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", "id": "227", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1291", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "231", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1594", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1454", + "weight": "1.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1137", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1627", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "1.0", + "id": "1137", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1454", "maxAmount": "1" } ] @@ -12161,135 +9609,141 @@ "main": [ { "minAmount": "10", - "weight": "50.0", + "weight": "2.0", "id": "556", "maxAmount": "10" }, { "minAmount": "9", - "weight": "50.0", + "weight": "2.0", "id": "557", "maxAmount": "9" }, { "minAmount": "5", - "weight": "50.0", + "weight": "2.0", "id": "554", "maxAmount": "5" }, { "minAmount": "2", - "weight": "50.0", + "weight": "6.0", "id": "562", "maxAmount": "2" }, { "minAmount": "2", - "weight": "50.0", + "weight": "5.0", "id": "561", "maxAmount": "2" }, { "minAmount": "2", - "weight": "5.0", + "weight": "1.0", "id": "563", "maxAmount": "2" }, { "minAmount": "1", - "weight": "5.0", - "id": "565", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1321", "maxAmount": "1" }, - { - "minAmount": "9", - "weight": "50.0", - "id": "882", - "maxAmount": "12" - }, { "minAmount": "1", - "weight": "50.0", + "weight": "6.0", "id": "1203", "maxAmount": "1" }, { "minAmount": "2", - "weight": "25.0", + "weight": "10.0", "id": "9140", - "maxAmount": "10" + "maxAmount": "12" }, { "minAmount": "1", - "weight": "25.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "877", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1115", "maxAmount": "1" }, - { - "minAmount": "4", - "weight": "50.0", - "id": "995", - "maxAmount": "55" - }, { "minAmount": "1", - "weight": "50.0", + "weight": "12.0", "id": "1025", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "2351", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1949", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "225", + "weight": "1.0", + "id": "14426", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "590", - "maxAmount": "1" + "minAmount": "9", + "weight": "3.0", + "id": "882", + "maxAmount": "9" + }, + { + "minAmount": "12", + "weight": "2.0", + "id": "882", + "maxAmount": "12" }, { "minAmount": "1", - "weight": "5.0", - "id": "946", + "weight": "8.0", + "id": "0", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" + "minAmount": "4", + "weight": "29.0", + "id": "995", + "maxAmount": "4" + }, + { + "minAmount": "7", + "weight": "8.0", + "id": "995", + "maxAmount": "7" + }, + { + "minAmount": "12", + "weight": "6.0", + "id": "995", + "maxAmount": "12" + }, + { + "minAmount": "25", + "weight": "13.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "35", + "weight": "4.0", + "id": "995", + "maxAmount": "35" + }, + { + "minAmount": "55", + "weight": "1.0", + "id": "995", + "maxAmount": "55" } ] }, @@ -12303,6 +9757,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "86.7406", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "11.5076", @@ -12326,12 +9786,6 @@ "weight": "0.3375", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "86.7406", - "id": "0", - "maxAmount": "1" } ], "ids": "186,7106,7107,7108,7109,7110,7111,7112,7113,7114", @@ -12339,195 +9793,153 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1137", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1363", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1139", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1309", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1203", "maxAmount": "1" }, { "minAmount": "2", - "weight": "50.0", + "weight": "13.0", "id": "561", - "maxAmount": "4" + "maxAmount": "2" }, { "minAmount": "2", - "weight": "25.0", + "weight": "4.0", "id": "562", "maxAmount": "2" }, { "minAmount": "2", - "weight": "25.0", + "weight": "1.0", "id": "564", "maxAmount": "2" }, { "minAmount": "2", - "weight": "25.0", + "weight": "1.0", "id": "560", "maxAmount": "2" }, { - "minAmount": "1", - "weight": "25.0", + "minAmount": "2", + "weight": "1.0", "id": "563", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "24.0", + "id": "14424", "maxAmount": "1" }, { "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": "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": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "8", - "weight": "50.0", - "id": "995", - "maxAmount": "8" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "2351", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "440", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "453", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2677", + "weight": "1.0", + "id": "1", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "167", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "161", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "1.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "25.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "8", + "weight": "23.0", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "15", + "weight": "11.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "20", + "weight": "1.0", + "id": "995", + "maxAmount": "20" + }, + { + "minAmount": "30", + "weight": "2.0", + "id": "995", + "maxAmount": "30" } ] }, { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "85.3493", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.9531", @@ -12551,12 +9963,6 @@ "weight": "2.9197", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "85.3493", - "id": "0", - "maxAmount": "1" } ], "ids": "187", @@ -12564,152 +9970,122 @@ "main": [ { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1203", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1115", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "1381", - "maxAmount": "1" + "minAmount": "2", + "weight": "22.0", + "id": "877", + "maxAmount": "12" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "882", + "maxAmount": "7" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "557", + "maxAmount": "4" + }, + { + "minAmount": "6", + "weight": "2.0", + "id": "554", + "maxAmount": "6" + }, + { + "minAmount": "9", + "weight": "2.0", + "id": "558", + "maxAmount": "9" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "562", + "maxAmount": "2" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "23.0", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" + "minAmount": "3", + "weight": "36.0", + "id": "995", + "maxAmount": "3" }, { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "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": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "minAmount": "5", + "weight": "9.0", "id": "995", "maxAmount": "5" }, + { + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, { "minAmount": "25", - "weight": "50.0", + "weight": "1.0", "id": "995", "maxAmount": "25" }, + { + "minAmount": "1", + "weight": "8.0", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", "id": "313", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1965", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1993", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1523", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2699", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -12885,7 +10261,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2829", + "id": "5733", "maxAmount": "1" }, { @@ -12906,6 +10282,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "74.1154", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "17.7558", @@ -12929,12 +10311,6 @@ "weight": "1.1795", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "74.1154", - "id": "0", - "maxAmount": "1" } ], "ids": "192", @@ -12942,153 +10318,123 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1151", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "1420", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1426", "maxAmount": "1" }, { "minAmount": "8", - "weight": "50.0", + "weight": "4.0", "id": "882", "maxAmount": "8" }, { "minAmount": "2", - "weight": "50.0", + "weight": "3.0", "id": "558", "maxAmount": "2" }, { "minAmount": "1", - "weight": "50.0", + "weight": "2.0", "id": "555", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", + "minAmount": "2", + "weight": "1.0", "id": "557", - "maxAmount": "1" + "maxAmount": "2" }, { "minAmount": "2", - "weight": "25.0", + "weight": "1.0", "id": "562", "maxAmount": "2" }, { "minAmount": "3", - "weight": "5.0", + "weight": "2.0", "id": "561", "maxAmount": "3" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "3.0", + "id": "14424", "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": "5.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "211", - "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": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "20" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "440", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "325", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "18.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "31.0", + "id": "995", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "20.0", + "id": "995", + "maxAmount": "2" + }, + { + "minAmount": "6", + "weight": "20.0", + "id": "995", + "maxAmount": "6" + }, + { + "minAmount": "13", + "weight": "7.0", + "id": "995", + "maxAmount": "13" + }, + { + "minAmount": "20", + "weight": "6.0", + "id": "995", + "maxAmount": "20" + }, + { + "minAmount": "30", + "weight": "2.0", + "id": "995", + "maxAmount": "30" } ] }, @@ -13100,230 +10446,128 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "222", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "238", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "226", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1939", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "224", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1581", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1976", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "40.0", - "id": "240", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2152", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "9737", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.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": "5.0", - "id": "215", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "2" - }, - { - "minAmount": "12", - "weight": "50.0", - "id": "556", - "maxAmount": "12" - }, - { - "minAmount": "36", - "weight": "50.0", - "id": "556", - "maxAmount": "36" - }, - { - "minAmount": "9", - "weight": "50.0", - "id": "557", - "maxAmount": "9" - }, - { - "minAmount": "12", - "weight": "50.0", - "id": "554", - "maxAmount": "12" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "561", - "maxAmount": "3" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "563", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1217", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "1313", - "maxAmount": "1" + "minAmount": "12", + "weight": "5.0", + "id": "554", + "maxAmount": "12" + }, + { + "minAmount": "2", + "weight": "4.0", + "id": "563", + "maxAmount": "2" + }, + { + "minAmount": "9", + "weight": "2.0", + "id": "557", + "maxAmount": "9" + }, + { + "minAmount": "36", + "weight": "1.0", + "id": "556", + "maxAmount": "36" }, { "minAmount": "3", - "weight": "50.0", - "id": "995", + "weight": "1.0", + "id": "561", "maxAmount": "3" }, - { - "minAmount": "13", - "weight": "50.0", - "id": "995", - "maxAmount": "13" - }, { "minAmount": "1", - "weight": "50.0", - "id": "235", + "weight": "34.0", + "id": "14424", "maxAmount": "1" }, + { + "minAmount": "2", + "weight": "10.0", + "id": "14424", + "maxAmount": "2" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "5.0", "id": "239", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "231", + "weight": "2.0", + "id": "235", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", + "id": "225", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "225", + "maxAmount": "2" + }, + { + "minAmount": "3", + "weight": "1.0", "id": "225", "maxAmount": "3" }, { "minAmount": "1", - "weight": "50.0", - "id": "227", + "weight": "1.0", + "id": "231", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", + "id": "227", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "15.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "10", + "weight": "1.0", + "id": "995", + "maxAmount": "10" + }, + { + "minAmount": "29", + "weight": "3.0", + "id": "995", + "maxAmount": "29" + }, + { + "minAmount": "1", + "weight": "37.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "167", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "1.0", + "id": "14426", "maxAmount": "1" } ] @@ -13605,7 +10849,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2851", + "id": "5733", "maxAmount": "1" }, { @@ -13679,8 +10923,8 @@ }, { "minAmount": "1", - "weight": "5.0", - "id": "2696", + "weight": "1.417", + "id": "1", "maxAmount": "1" }, { @@ -13814,7 +11058,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2857", + "id": "5733", "maxAmount": "1" }, { @@ -13840,170 +11084,98 @@ "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": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "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", + "weight": "1.0", + "id": "1217", "maxAmount": "1" }, { "minAmount": "12", - "weight": "50.0", + "weight": "5.0", "id": "555", "maxAmount": "12" }, - { - "minAmount": "36", - "weight": "5.0", - "id": "554", - "maxAmount": "36" - }, { "minAmount": "2", - "weight": "25.0", + "weight": "4.0", "id": "563", "maxAmount": "2" }, { "minAmount": "3", - "weight": "25.0", + "weight": "1.0", "id": "561", "maxAmount": "3" }, + { + "minAmount": "36", + "weight": "1.0", + "id": "554", + "maxAmount": "36" + }, { "minAmount": "1", - "weight": "25.0", - "id": "1291", + "weight": "39.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "1217", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "9142", - "maxAmount": "12" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "830", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1137", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "227", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", "id": "239", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "231", "maxAmount": "1" }, + { + "minAmount": "1", + "weight": "1.0", + "id": "227", + "maxAmount": "1" + }, { "minAmount": "3", - "weight": "50.0", + "weight": "5.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "10", + "weight": "3.0", + "id": "995", + "maxAmount": "10" + }, + { + "minAmount": "24", + "weight": "3.0", "id": "995", "maxAmount": "24" }, { "minAmount": "1", - "weight": "50.0", + "weight": "37.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "11.0", "id": "167", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1454", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "10.0", "id": "993", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "1.0", + "id": "14426", "maxAmount": "1" } ] @@ -14018,6 +11190,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "37.5", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "12.5", @@ -14041,12 +11219,6 @@ "weight": "15.0", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "37.5", - "id": "0", - "maxAmount": "1" } ], "ids": "221", @@ -14193,52 +11365,50 @@ "weight": "100.0", "id": "526", "maxAmount": "1" - }, + } + ], + "charm": [], + "ids": "281", + "description": "", + "main": [] + }, + { + "default": [ { "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", + "id": "526", "maxAmount": "1" } ], "charm": [], - "ids": "347,348,357,369,370,371,372,717,718,719,2349,2350,2351,2373,2374,2784,3216", + "ids": "283", "description": "", "main": [] }, + { + "default": [ + { + "minAmount": "1", + "weight": "100.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "370,357,348,369,347,371", + "description": "all combat mourners", + "main": [] + }, { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "81.6118", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "15.83", @@ -14262,180 +11432,131 @@ "weight": "0.6347", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "81.6118", - "id": "0", - "maxAmount": "1" } ], "ids": "412", "description": "", "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "7833", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "100.0", - "id": "0", + "id": "530", "maxAmount": "1" } ] }, { - "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "526", - "maxAmount": "1" - } - ], + "default": [], "charm": [], - "ids": "438,439,440,441,442,443,655,4470", - "description": "", + "ids": "438,439,440,441,442,443,4470", + "description": "Tree Spirit (Hostile Woodcutting Random Event/Enchanted Valley)", "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", + "id": "1351", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "1349", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", + "id": "1361", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", "id": "1355", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", "id": "1357", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "4.0", "id": "1359", "maxAmount": "1" }, { - "minAmount": "2", - "weight": "50.0", + "minAmount": "1", + "weight": "12.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "14.0", + "id": "14428", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "946", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "6.0", + "id": "3051", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "6.0", + "id": "3049", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "16.0", + "id": "561", + "maxAmount": "5" + }, + { + "minAmount": "10", + "weight": "8.0", + "id": "561", + "maxAmount": "10" + }, + { + "minAmount": "22", + "weight": "2.0", "id": "561", "maxAmount": "22" }, { - "minAmount": "1", - "weight": "1.0", - "id": "1462", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5300", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5298", - "maxAmount": "1" + "minAmount": "28", + "weight": "2.0", + "id": "561", + "maxAmount": "28" }, { "minAmount": "1", "weight": "1.0", - "id": "5304", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1619", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1617", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1621", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1623", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "946", + "id": "1963", "maxAmount": "1" } ] @@ -14481,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": [ { @@ -14496,12 +11617,6 @@ "id": "1185", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4675", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "50.0", @@ -14685,13 +11800,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2835", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6808", + "id": "12070", "maxAmount": "1" }, { @@ -14818,13 +11927,13 @@ { "minAmount": "1", "weight": "100.0", - "id": "526", + "id": "592", "maxAmount": "1" }, { "minAmount": "1", "weight": "100.0", - "id": "780", + "id": "781", "maxAmount": "1" } ], @@ -15337,7 +12446,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2827", + "id": "5733", "maxAmount": "1" } ] @@ -15573,7 +12682,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2819", + "id": "5733", "maxAmount": "1" } ] @@ -15737,7 +12846,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2823", + "id": "5733", "maxAmount": "1" }, { @@ -16033,7 +13142,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2849", + "id": "5733", "maxAmount": "1" }, { @@ -16078,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": [ @@ -16447,6 +13487,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "66.2159", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "8.4604", @@ -16470,12 +13516,6 @@ "weight": "1.5411", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "66.2159", - "id": "0", - "maxAmount": "1" } ], "ids": "941,4677,4678,4679,4680", @@ -16483,193 +13523,133 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1069", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", + "id": "1365", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "1355", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1243", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1145", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1161", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1213", "maxAmount": "1" }, { "minAmount": "75", - "weight": "50.0", + "weight": "8.0", "id": "555", "maxAmount": "75" }, - { - "minAmount": "37", - "weight": "50.0", - "id": "554", - "maxAmount": "37" - }, { "minAmount": "15", - "weight": "25.0", + "weight": "5.0", "id": "561", "maxAmount": "15" }, - { - "minAmount": "30", - "weight": "25.0", - "id": "561", - "maxAmount": "30" - }, - { - "minAmount": "75", - "weight": "5.0", - "id": "561", - "maxAmount": "75" - }, { "minAmount": "3", - "weight": "50.0", + "weight": "3.0", "id": "563", "maxAmount": "3" }, { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "maxAmount": "1" + "minAmount": "37", + "weight": "1.0", + "id": "554", + "maxAmount": "37" }, { "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", + "weight": "15.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "11", - "weight": "50.0", + "weight": "10.0", + "id": "995", + "maxAmount": "11" + }, + { + "minAmount": "44", + "weight": "23.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "132", + "weight": "25.0", + "id": "995", + "maxAmount": "132" + }, + { + "minAmount": "200", + "weight": "10.0", + "id": "995", + "maxAmount": "200" + }, + { + "minAmount": "440", + "weight": "1.0", "id": "995", "maxAmount": "440" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "365", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "449", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "2739", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "15.5", - "id": "31", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "150.0", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "10.0", + "weight": "0.25", "id": "12479", "maxAmount": "1" } @@ -17180,6 +14160,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "86.2037", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.0201", @@ -17203,12 +14189,6 @@ "weight": "0.5939", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "86.2037", - "id": "0", - "maxAmount": "1" } ], "ids": "1019", @@ -17350,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" } ] @@ -17465,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", @@ -17854,8 +14775,8 @@ }, { "minAmount": "1", - "weight": "25.0", - "id": "2837", + "weight": "6.7715", + "id": "5733", "maxAmount": "1" }, { @@ -18724,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": [ { @@ -18863,6 +15655,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "69.0", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "3.0", @@ -18891,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" } ] @@ -19077,6 +15833,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "53.7736", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "35.8491", @@ -19100,12 +15862,6 @@ "weight": "0.0", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "53.7736", - "id": "0", - "maxAmount": "1" } ], "ids": "1097", @@ -19113,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" } ] @@ -19207,6 +16011,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "67.1141", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "21.6443", @@ -19230,12 +16040,6 @@ "weight": "2.0134", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "67.1141", - "id": "0", - "maxAmount": "1" } ], "ids": "1098", @@ -19243,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" } ] @@ -19330,218 +16194,140 @@ "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "5291", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5293", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "12176", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1141", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1145", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "3.0", "id": "1339", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "3.0", "id": "1341", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", + "id": "1145", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1345", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1181", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "205", - "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": "217", - "maxAmount": "1" - }, - { - "minAmount": "8", - "weight": "50.0", - "id": "995", - "maxAmount": "8" - }, - { - "minAmount": "35", - "weight": "50.0", - "id": "995", - "maxAmount": "35" - }, - { - "minAmount": "25", - "weight": "25.0", + "minAmount": "60", + "weight": "8.0", "id": "557", - "maxAmount": "25" - }, - { - "minAmount": "45", - "weight": "25.0", - "id": "557", - "maxAmount": "45" + "maxAmount": "60" }, { "minAmount": "7", - "weight": "25.0", + "weight": "5.0", "id": "561", "maxAmount": "7" }, { "minAmount": "2", - "weight": "5.0", + "weight": "3.0", "id": "563", "maxAmount": "2" }, + { + "minAmount": "45", + "weight": "1.0", + "id": "557", + "maxAmount": "45" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "557", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "15.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "19.0", + "id": "14422", + "maxAmount": "1" + }, + { + "minAmount": "8", + "weight": "7.0", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "35", + "weight": "29.0", + "id": "995", + "maxAmount": "35" + }, + { + "minAmount": "50", + "weight": "6.0", + "id": "995", + "maxAmount": "50" + }, + { + "minAmount": "100", + "weight": "10.0", + "id": "995", + "maxAmount": "100" + }, + { + "minAmount": "250", + "weight": "1.0", + "id": "995", + "maxAmount": "250" + }, { "minAmount": "3", - "weight": "5.0", + "weight": "3.0", "id": "454", "maxAmount": "3" }, { "minAmount": "3", - "weight": "5.0", + "weight": "2.0", "id": "354", "maxAmount": "3" }, { "minAmount": "1", "weight": "5.0", - "id": "31", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.32", + "id": "10976", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.0256", + "id": "10977", "maxAmount": "1" } ] @@ -19561,321 +16347,141 @@ "main": [ { "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": "5.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5291", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5323", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5293", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5104", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "12176", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5297", - "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": "5295", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1341", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1339", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1141", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", + "id": "1339", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "1341", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1145", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1345", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1361", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1181", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "1183", - "maxAmount": "1" + "minAmount": "60", + "weight": "8.0", + "id": "557", + "maxAmount": "60" }, { "minAmount": "7", - "weight": "50.0", + "weight": "5.0", "id": "561", "maxAmount": "7" }, - { - "minAmount": "25", - "weight": "50.0", - "id": "557", - "maxAmount": "25" - }, - { - "minAmount": "45", - "weight": "50.0", - "id": "557", - "maxAmount": "45" - }, { "minAmount": "2", - "weight": "25.0", + "weight": "3.0", "id": "563", "maxAmount": "2" }, + { + "minAmount": "45", + "weight": "1.0", + "id": "557", + "maxAmount": "45" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "557", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "15.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "19.0", + "id": "14422", + "maxAmount": "1" + }, { "minAmount": "8", - "weight": "50.0", + "weight": "7.0", "id": "995", "maxAmount": "8" }, { "minAmount": "35", - "weight": "50.0", + "weight": "29.0", "id": "995", "maxAmount": "35" }, + { + "minAmount": "50", + "weight": "6.0", + "id": "995", + "maxAmount": "50" + }, + { + "minAmount": "100", + "weight": "10.0", + "id": "995", + "maxAmount": "100" + }, + { + "minAmount": "250", + "weight": "1.0", + "id": "995", + "maxAmount": "250" + }, { "minAmount": "3", - "weight": "50.0", + "weight": "3.0", "id": "454", "maxAmount": "3" }, { "minAmount": "3", - "weight": "50.0", + "weight": "2.0", "id": "354", "maxAmount": "3" }, { "minAmount": "1", - "weight": "25.0", + "weight": "5.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.32", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "0.0256", "id": "10977", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "7884", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" } ] }, @@ -19889,6 +16495,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "50.0805", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "39.694", @@ -19912,12 +16524,6 @@ "weight": "2.0934", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0805", - "id": "0", - "maxAmount": "1" } ], "ids": "1115,1116,1117", @@ -19925,195 +16531,147 @@ "main": [ { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1119", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1339", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1341", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1121", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1357", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1183", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1347", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "830", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "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": "8.0", + "id": "557", + "maxAmount": "80" + }, { "minAmount": "16", - "weight": "50.0", + "weight": "5.0", "id": "561", "maxAmount": "16" }, { "minAmount": "4", - "weight": "50.0", + "weight": "3.0", "id": "563", "maxAmount": "4" }, - { - "minAmount": "25", - "weight": "25.0", - "id": "557", - "maxAmount": "25" - }, { "minAmount": "65", - "weight": "25.0", + "weight": "1.0", "id": "557", "maxAmount": "65" }, { - "minAmount": "1", - "weight": "25.0", - "id": "1462", - "maxAmount": "1" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "360", - "maxAmount": "4" - }, - { - "minAmount": "6", - "weight": "25.0", - "id": "454", - "maxAmount": "6" + "minAmount": "25", + "weight": "1.0", + "id": "557", + "maxAmount": "25" }, { "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", + "weight": "15.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "20", - "weight": "25.0", + "weight": "4.0", "id": "995", "maxAmount": "20" }, { "minAmount": "40", - "weight": "25.0", + "weight": "29.0", "id": "995", "maxAmount": "40" }, { - "minAmount": "1", + "minAmount": "135", "weight": "25.0", + "id": "995", + "maxAmount": "135" + }, + { + "minAmount": "190", + "weight": "10.0", + "id": "995", + "maxAmount": "190" + }, + { + "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": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.32", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "0.0256", "id": "10977", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -20471,218 +17029,140 @@ "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "5291", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5293", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "12176", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1141", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1145", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "3.0", "id": "1339", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "3.0", "id": "1341", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", + "id": "1145", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1345", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1181", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "205", - "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": "217", - "maxAmount": "1" - }, - { - "minAmount": "8", - "weight": "50.0", - "id": "995", - "maxAmount": "8" - }, - { - "minAmount": "35", - "weight": "50.0", - "id": "995", - "maxAmount": "35" - }, - { - "minAmount": "25", - "weight": "25.0", + "minAmount": "60", + "weight": "8.0", "id": "557", - "maxAmount": "25" - }, - { - "minAmount": "45", - "weight": "25.0", - "id": "557", - "maxAmount": "45" + "maxAmount": "60" }, { "minAmount": "7", - "weight": "25.0", + "weight": "5.0", "id": "561", "maxAmount": "7" }, { "minAmount": "2", - "weight": "5.0", + "weight": "3.0", "id": "563", "maxAmount": "2" }, + { + "minAmount": "45", + "weight": "1.0", + "id": "557", + "maxAmount": "45" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "557", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "15.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "19.0", + "id": "14422", + "maxAmount": "1" + }, + { + "minAmount": "8", + "weight": "7.0", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "35", + "weight": "29.0", + "id": "995", + "maxAmount": "35" + }, + { + "minAmount": "50", + "weight": "6.0", + "id": "995", + "maxAmount": "50" + }, + { + "minAmount": "100", + "weight": "10.0", + "id": "995", + "maxAmount": "100" + }, + { + "minAmount": "250", + "weight": "1.0", + "id": "995", + "maxAmount": "250" + }, { "minAmount": "3", - "weight": "5.0", + "weight": "3.0", "id": "454", "maxAmount": "3" }, { "minAmount": "3", - "weight": "5.0", + "weight": "2.0", "id": "354", "maxAmount": "3" }, { "minAmount": "1", "weight": "5.0", - "id": "31", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.32", + "id": "10976", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.0256", + "id": "10977", "maxAmount": "1" } ] @@ -20690,6 +17170,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "87.6923", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.8422", @@ -20713,12 +17199,6 @@ "weight": "1.764", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "87.6923", - "id": "0", - "maxAmount": "1" } ], "ids": "1153,1156", @@ -20999,6 +17479,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "73.4384", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.9927", @@ -21022,12 +17508,6 @@ "weight": "6.4403", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "73.4384", - "id": "0", - "maxAmount": "1" } ], "ids": "1154", @@ -21200,6 +17680,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "64.8768", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "13.2327", @@ -21223,12 +17709,6 @@ "weight": "8.7477", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "64.8768", - "id": "0", - "maxAmount": "1" } ], "ids": "1155,1157", @@ -21236,293 +17716,194 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1197", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1183", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1147", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1113", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1127", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.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": "25.0", - "id": "1331", + "weight": "1.0", + "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1319", + "weight": "1.0", + "id": "1147", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1373", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1289", + "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": "19" + "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": "45", - "weight": "25.0", - "id": "563", - "maxAmount": "45" - }, - { - "minAmount": "18", - "weight": "25.0", - "id": "560", - "maxAmount": "18" - }, - { - "minAmount": "28", - "weight": "25.0", - "id": "560", - "maxAmount": "28" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "562", - "maxAmount": "10" - }, { "minAmount": "1", - "weight": "25.0", - "id": "829", + "weight": "23.0", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "2", - "weight": "25.0", - "id": "564", - "maxAmount": "2" - }, - { - "minAmount": "42", - "weight": "25.0", - "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "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": "28", - "weight": "50.0", + "minAmount": "132", + "weight": "40.0", "id": "995", - "maxAmount": "3000" + "maxAmount": "132" }, { - "minAmount": "12", - "weight": "25.0", + "minAmount": "30", + "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": "3.0", "id": "379", - "maxAmount": "12" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "163", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "145", + "weight": "1.0", + "id": "133", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "157", - "maxAmount": "1" - }, - { - "minAmount": "24", - "weight": "25.0", - "id": "3139", - "maxAmount": "24" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "5.0", + "id": "14426", "maxAmount": "1" } ] }, { "default": [], - "charm": [], + "charm": [ + { + "minAmount": "6", + "weight": "79.5353", + "id": "0", + "maxAmount": "6" + }, + { + "minAmount": "6", + "weight": "2.681", + "id": "12158", + "maxAmount": "6" + }, + { + "minAmount": "6", + "weight": "3.3959", + "id": "12159", + "maxAmount": "6" + }, + { + "minAmount": "6", + "weight": "5.8981", + "id": "12160", + "maxAmount": "6" + }, + { + "minAmount": "6", + "weight": "8.4897", + "id": "12163", + "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": [ { - "minAmount": "2", + "minAmount": "1", "weight": "50.0", - "id": "1247", - "maxAmount": "2" + "id": "1245", + "maxAmount": "1" }, { "minAmount": "1", @@ -21546,23 +17927,11 @@ "minAmount": "1", "weight": "25.0", "id": "1347", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1301", "maxAmount": "1" }, - { - "minAmount": "2", - "weight": "25.0", - "id": "3053", - "maxAmount": "2" - }, { "minAmount": "1", - "weight": "10.0", + "weight": "3.2737", "id": "7158", "maxAmount": "1" }, @@ -21570,11 +17939,11 @@ "minAmount": "1", "weight": "50.0", "id": "1113", - "maxAmount": "4" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.5", + "weight": "3.2737", "id": "3140", "maxAmount": "1" }, @@ -21585,16 +17954,10 @@ "maxAmount": "500" }, { - "minAmount": "1", + "minAmount": "350", "weight": "50.0", "id": "884", - "maxAmount": "355" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "886", - "maxAmount": "150" + "maxAmount": "350" }, { "minAmount": "100", @@ -21603,100 +17966,88 @@ "maxAmount": "100" }, { - "minAmount": "250", - "weight": "75.0", - "id": "565", - "maxAmount": "300" - }, - { - "minAmount": "200", + "minAmount": "75", "weight": "75.0", "id": "561", - "maxAmount": "285" + "maxAmount": "75" }, { "minAmount": "20", - "weight": "75.0", + "weight": "25.0", "id": "563", - "maxAmount": "45" - }, - { - "minAmount": "250", - "weight": "75.0", - "id": "563", - "maxAmount": "250" - }, - { - "minAmount": "200", - "weight": "50.0", - "id": "560", - "maxAmount": "300" - }, - { - "minAmount": "200", - "weight": "50.0", - "id": "564", - "maxAmount": "300" + "maxAmount": "20" }, { "minAmount": "1", "weight": "25.0", - "id": "1607", + "id": "1301", "maxAmount": "1" }, { "minAmount": "1", "weight": "25.0", - "id": "1605", + "id": "1185", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "50.0", + "id": "1131", "maxAmount": "1" }, { "minAmount": "1", "weight": "25.0", - "id": "1603", + "id": "2501", "maxAmount": "1" }, - { - "minAmount": "40", - "weight": "25.0", - "id": "414", - "maxAmount": "40" - }, - { - "minAmount": "20", - "weight": "50.0", - "id": "246", - "maxAmount": "40" - }, { "minAmount": "1", - "weight": "50.0", - "id": "1731", - "maxAmount": "1" - }, - { - "minAmount": "341", - "weight": "50.0", - "id": "995", - "maxAmount": "75000" - }, - { - "minAmount": "7", - "weight": "50.0", - "id": "379", - "maxAmount": "7" - }, - { - "minAmount": "1", - "weight": "10.0", + "weight": "6.5475", "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "10.0", + "weight": "25.0", "id": "31", "maxAmount": "1" + }, + { + "minAmount": "600", + "weight": "25.0", + "id": "554", + "maxAmount": "600" + }, + { + "minAmount": "50", + "weight": "50.0", + "id": "565", + "maxAmount": "50" + }, + { + "minAmount": "20", + "weight": "25.0", + "id": "246", + "maxAmount": "20" + }, + { + "minAmount": "8", + "weight": "25.0", + "id": "379", + "maxAmount": "8" + }, + { + "minAmount": "1", + "weight": "25.0", + "id": "3053", + "maxAmount": "1" + }, + { + "minAmount": "1560", + "weight": "50.0", + "id": "995", + "maxAmount": "1560" } ] }, @@ -21710,6 +18061,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "59.5722", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.8769", @@ -21733,15 +18090,9 @@ "weight": "1.6345", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "59.5722", - "id": "0", - "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": [ { @@ -22005,7 +18356,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2747", + "id": "12070", "maxAmount": "1" }, { @@ -22044,6 +18395,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "85.3383", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "10.1504", @@ -22067,30 +18424,11 @@ "weight": "0.7519", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "85.3383", - "id": "0", - "maxAmount": "1" } ], "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": [ @@ -22102,6 +18440,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "60.5114", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.1023", @@ -22125,12 +18469,6 @@ "weight": "1.7045", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "60.5114", - "id": "0", - "maxAmount": "1" } ], "ids": "1198", @@ -22159,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": "2733", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", + "id": "14426", "maxAmount": "1" } ] @@ -22337,6 +18621,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "79.478", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "18.3672", @@ -22360,12 +18650,6 @@ "weight": "0.1553", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "79.478", - "id": "0", - "maxAmount": "1" } ], "ids": "1218", @@ -22394,6 +18678,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "82.6794", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.4178", @@ -22417,12 +18707,6 @@ "weight": "1.364", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "82.6794", - "id": "0", - "maxAmount": "1" } ], "ids": "1219", @@ -22430,13 +18714,13 @@ "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "2827", + "weight": "1.0", + "id": "5733", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "127.0", "id": "0", "maxAmount": "1" } @@ -22458,7 +18742,7 @@ } ], "charm": [], - "ids": "1227,1232", + "ids": "1227", "description": "", "main": [] }, @@ -22478,7 +18762,7 @@ } ], "charm": [], - "ids": "1228,1233", + "ids": "1228", "description": "", "main": [] }, @@ -22498,7 +18782,7 @@ } ], "charm": [], - "ids": "1229,1234", + "ids": "1229", "description": "", "main": [] }, @@ -22518,7 +18802,7 @@ } ], "charm": [], - "ids": "1230,1235", + "ids": "1230", "description": "", "main": [] }, @@ -22722,10 +19006,10 @@ "maxAmount": "1" }, { - "minAmount": "110", + "minAmount": "30", "weight": "5.0", "id": "313", - "maxAmount": "30" + "maxAmount": "110" }, { "minAmount": "1", @@ -22887,10 +19171,10 @@ "maxAmount": "1" }, { - "minAmount": "110", + "minAmount": "30", "weight": "5.0", "id": "313", - "maxAmount": "30" + "maxAmount": "110" }, { "minAmount": "1", @@ -23255,7 +19539,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2678", + "id": "5733", "maxAmount": "1" }, { @@ -23402,7 +19686,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2700", + "id": "5733", "maxAmount": "1" }, { @@ -23414,7 +19698,14 @@ ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "100.0", + "id": "526", + "maxAmount": "1" + } + ], "charm": [], "ids": "1307", "description": "", @@ -23560,7 +19851,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2680", + "id": "1", "maxAmount": "1" }, { @@ -23779,7 +20070,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2716", + "id": "1", "maxAmount": "1" }, { @@ -23802,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" }, { @@ -23876,12 +20339,6 @@ "id": "3751", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "1.0", - "id": "3749", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "1.0", @@ -23896,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", @@ -23980,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", @@ -24154,8 +20861,8 @@ }, { "minAmount": "1", - "weight": "1400.0", - "id": "0", + "weight": "16.0", + "id": "14426", "maxAmount": "1" } ] @@ -24231,25 +20938,31 @@ "charm": [ { "minAmount": "1", - "weight": "25.0", + "weight": "98.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.0", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "0.0", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "0.0", "id": "12163", "maxAmount": "1" } @@ -24377,7 +21090,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2747", + "id": "5733", "maxAmount": "1" }, { @@ -24442,33 +21155,33 @@ "charm": [ { "minAmount": "1", - "weight": "100.0", + "weight": "80.7333", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "11.7333", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "5.0", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "1.3333", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "1.2", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ], "ids": "1558,1559,1951,1952,1953,1954,1955,1956,6052", @@ -24713,6 +21426,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "39.7302", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "13.6424", @@ -24736,219 +21455,159 @@ "weight": "3.4391", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "39.7302", - "id": "0", - "maxAmount": "1" } ], "ids": "1590", "description": "", "main": [ - { - "minAmount": "30", - "weight": "50.0", - "id": "829", - "maxAmount": "30" - }, - { - "minAmount": "60", - "weight": "50.0", - "id": "829", - "maxAmount": "60" - }, - { - "minAmount": "16", - "weight": "25.0", - "id": "810", - "maxAmount": "16" - }, { "minAmount": "1", - "weight": "25.0", - "id": "1355", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1315", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1355", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "1369", "maxAmount": "1" }, - { - "minAmount": "2", - "weight": "25.0", - "id": "9142", - "maxAmount": "8" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "9142", - "maxAmount": "10" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "868", - "maxAmount": "2" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "868", - "maxAmount": "5" - }, { "minAmount": "1", - "weight": "5.0", - "id": "830", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1373", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1123", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1185", + "weight": "1.0", + "id": "1303", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "0.45", - "id": "4585", - "maxAmount": "1" + "minAmount": "30", + "weight": "20.0", + "id": "829", + "maxAmount": "30" }, { - "minAmount": "1", - "weight": "0.45", - "id": "4087", - "maxAmount": "1" + "minAmount": "50", + "weight": "8.0", + "id": "554", + "maxAmount": "50" + }, + { + "minAmount": "16", + "weight": "7.0", + "id": "816", + "maxAmount": "16" + }, + { + "minAmount": "2", + "weight": "6.0", + "id": "9142", + "maxAmount": "12" }, { "minAmount": "10", - "weight": "50.0", + "weight": "5.0", "id": "563", "maxAmount": "10" }, - { - "minAmount": "45", - "weight": "50.0", - "id": "563", - "maxAmount": "45" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "554", - "maxAmount": "150" - }, { "minAmount": "15", - "weight": "25.0", + "weight": "3.0", "id": "565", "maxAmount": "15" }, { - "minAmount": "30", - "weight": "25.0", - "id": "565", - "maxAmount": "30" - }, - { - "minAmount": "67", - "weight": "25.0", - "id": "561", - "maxAmount": "67" + "minAmount": "2", + "weight": "3.0", + "id": "868", + "maxAmount": "2" }, { "minAmount": "25", - "weight": "25.0", + "weight": "1.0", "id": "560", "maxAmount": "25" }, { - "minAmount": "45", - "weight": "25.0", - "id": "560", - "maxAmount": "45" + "minAmount": "196", + "weight": "39.0", + "id": "995", + "maxAmount": "196" + }, + { + "minAmount": "330", + "weight": "10.0", + "id": "995", + "maxAmount": "330" + }, + { + "minAmount": "690", + "weight": "1.0", + "id": "995", + "maxAmount": "690" }, { "minAmount": "1", - "weight": "5.0", + "weight": "3.0", "id": "2361", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "2363", - "maxAmount": "1" - }, - { - "minAmount": "168", - "weight": "50.0", - "id": "995", - "maxAmount": "3000" - }, - { - "minAmount": "1", - "weight": "25.0", + "minAmount": "2", + "weight": "2.0", "id": "373", "maxAmount": "2" }, { "minAmount": "1", - "weight": "5.0", - "id": "2735", + "weight": "1.0", + "id": "373", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "4.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "1.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.0625", + "id": "4087", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.0625", + "id": "4585", "maxAmount": "1" } ] @@ -24969,6 +21628,12 @@ } ], "charm": [ + { + "minAmount": "2", + "weight": "39.7985", + "id": "0", + "maxAmount": "2" + }, { "minAmount": "2", "weight": "12.2826", @@ -24992,12 +21657,6 @@ "weight": "2.782", "id": "12163", "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "39.7985", - "id": "0", - "maxAmount": "2" } ], "ids": "1591", @@ -25005,183 +21664,153 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1317", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1371", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1357", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1371", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1183", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "1432", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", + "weight": "1.0", "id": "1147", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", - "id": "1163", + "weight": "1.0", + "id": "1373", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "0.75", - "id": "4585", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.75", - "id": "4087", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "9143", - "maxAmount": "12" - }, - { - "minAmount": "2", - "weight": "8.0", - "id": "9144", - "maxAmount": "12" - }, - { - "minAmount": "9", - "weight": "25.0", - "id": "811", - "maxAmount": "9" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "868", - "maxAmount": "5" + "minAmount": "4", + "weight": "20.0", + "id": "830", + "maxAmount": "4" }, { "minAmount": "15", - "weight": "25.0", + "weight": "15.0", "id": "560", "maxAmount": "15" }, { - "minAmount": "15", - "weight": "25.0", - "id": "565", - "maxAmount": "15" + "minAmount": "9", + "weight": "7.0", + "id": "817", + "maxAmount": "9" + }, + { + "minAmount": "2", + "weight": "6.0", + "id": "9143", + "maxAmount": "12" }, { "minAmount": "3", - "weight": "25.0", + "weight": "5.0", "id": "566", "maxAmount": "3" }, { - "minAmount": "2", - "weight": "25.0", - "id": "2361", - "maxAmount": "2" + "minAmount": "5", + "weight": "3.0", + "id": "868", + "maxAmount": "5" }, { - "minAmount": "1", - "weight": "50.0", - "id": "161", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "145", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "165", - "maxAmount": "1" + "minAmount": "3", + "weight": "5.0", + "id": "566", + "maxAmount": "3" }, { "minAmount": "270", - "weight": "50.0", + "weight": "19.0", "id": "995", "maxAmount": "270" }, { "minAmount": "550", - "weight": "50.0", + "weight": "10.0", "id": "995", "maxAmount": "550" }, { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", + "id": "161", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "9431", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "3.0", + "id": "2361", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "2011", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "9431", + "weight": "3.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2729", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.07994", + "weight": "1.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.0128", "id": "11286", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "0.125", + "id": "4087", "maxAmount": "1" }, { - "minAmount": "4", - "weight": "50.0", - "id": "830", - "maxAmount": "4" + "minAmount": "1", + "weight": "0.125", + "id": "4585", + "maxAmount": "1" } ] }, @@ -25201,6 +21830,12 @@ } ], "charm": [ + { + "minAmount": "3", + "weight": "33.614", + "id": "0", + "maxAmount": "3" + }, { "minAmount": "3", "weight": "13.8986", @@ -25224,12 +21859,6 @@ "weight": "3.0751", "id": "12163", "maxAmount": "3" - }, - { - "minAmount": "3", - "weight": "33.614", - "id": "0", - "maxAmount": "3" } ], "ids": "1592,3590", @@ -25237,177 +21866,147 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1317", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1371", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1357", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1183", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "1359", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", + "weight": "4.0", "id": "1432", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", - "id": "1147", + "weight": "2.0", + "id": "1199", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", + "weight": "2.0", + "id": "1359", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1163", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "1.4", - "id": "4585", - "maxAmount": "1" + "minAmount": "7", + "weight": "20.0", + "id": "830", + "maxAmount": "7" }, { - "minAmount": "1", - "weight": "1.4", - "id": "4087", - "maxAmount": "1" + "minAmount": "15", + "weight": "15.0", + "id": "565", + "maxAmount": "15" }, { - "minAmount": "2", - "weight": "25.0", - "id": "9143", + "minAmount": "12", + "weight": "7.0", + "id": "817", "maxAmount": "12" }, { "minAmount": "2", - "weight": "8.0", + "weight": "6.0", "id": "9144", "maxAmount": "12" }, - { - "minAmount": "12", - "weight": "25.0", - "id": "811", - "maxAmount": "12" - }, - { - "minAmount": "7", - "weight": "8.0", - "id": "868", - "maxAmount": "7" - }, - { - "minAmount": "20", - "weight": "25.0", - "id": "565", - "maxAmount": "20" - }, { "minAmount": "5", - "weight": "8.0", + "weight": "5.0", "id": "566", "maxAmount": "5" }, - { - "minAmount": "2", - "weight": "25.0", - "id": "2361", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "161", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "12.0", - "id": "145", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "12.0", - "id": "165", - "maxAmount": "1" - }, { "minAmount": "470", - "weight": "50.0", + "weight": "20.0", "id": "995", "maxAmount": "470" }, { "minAmount": "650", - "weight": "50.0", + "weight": "10.0", "id": "995", "maxAmount": "650" }, { - "minAmount": "2", - "weight": "25.0", - "id": "2011", - "maxAmount": "2" + "minAmount": "1", + "weight": "8.0", + "id": "145", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "8.0", "id": "9431", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "12070", + "weight": "3.0", + "id": "2363", "maxAmount": "1" }, { "minAmount": "1", - "weight": "6.0", + "weight": "3.0", + "id": "165", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "2011", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "2011", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.0728", + "weight": "1.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.0128", "id": "11286", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "0.25", + "id": "4087", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.25", + "id": "4585", + "maxAmount": "1" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "868", + "maxAmount": "7" } ] }, @@ -25421,6 +22020,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "70.4846", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.8947", @@ -25444,12 +22049,6 @@ "weight": "1.2796", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "70.4846", - "id": "0", - "maxAmount": "1" } ], "ids": "1593,1594", @@ -25466,6 +22065,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "87.3121", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.0562", @@ -25489,12 +22094,6 @@ "weight": "0.4212", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "87.3121", - "id": "0", - "maxAmount": "1" } ], "ids": "1600,1601,1602,1603", @@ -25715,6 +22314,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "73.48", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "11.8412", @@ -25738,12 +22343,6 @@ "weight": "4.7859", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "73.48", - "id": "0", - "maxAmount": "1" } ], "ids": "1604,1605,1606,1607", @@ -25751,207 +22350,87 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5301", - "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": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5300", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5304", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "205", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "207", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "209", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "211", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "213", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "3051", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "12.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "3053", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1073", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "4.0", "id": "1163", "maxAmount": "1" }, - { - "minAmount": "278", - "weight": "25.0", - "id": "995", - "maxAmount": "487" - }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "4103", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2729", + "weight": "130.0", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" + "minAmount": "2", + "weight": "130.0", + "id": "14424", + "maxAmount": "2" + }, + { + "minAmount": "3", + "weight": "47.0", + "id": "14424", + "maxAmount": "3" }, { "minAmount": "1", - "weight": "100.0", + "weight": "76.0", + "id": "14428", + "maxAmount": "1" + }, + { + "minAmount": "460", + "weight": "4.0", + "id": "995", + "maxAmount": "460" + }, + { + "minAmount": "1", + "weight": "72.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "20.0", + "id": "14426", + "maxAmount": "1" } ] }, @@ -25965,6 +22444,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "56.3725", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "8.8432", @@ -25988,310 +22473,246 @@ "weight": "1.3687", "id": "12163", "maxAmount": "1" + } + ], + "tertiary": [ + { + "minAmount": "1", + "weight": "2999.0", + "id": "0", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "56.3725", - "id": "0", + "weight": "1.0", + "id": "7978", "maxAmount": "1" } ], "ids": "1608,1609,4229", "description": "", "main": [ - { - "minAmount": "5", - "weight": "25.0", - "id": "561", - "maxAmount": "30" - }, { "minAmount": "1", - "weight": "25.0", - "id": "560", - "maxAmount": "6" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "558", - "maxAmount": "16" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "9143", - "maxAmount": "19" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1355", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "4160", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1123", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "239", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "225", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "3.0", + "id": "1123", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "201", + "weight": "3.0", + "id": "1303", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "205", - "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": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "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": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5106", - "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": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5300", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5304", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1963", - "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "2114", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2289", - "maxAmount": "1" - }, - { - "minAmount": "22", - "weight": "50.0", - "id": "995", - "maxAmount": "748" - }, - { - "minAmount": "20", - "weight": "50.0", - "id": "1780", - "maxAmount": "30" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "161", - "maxAmount": "1" - }, - { - "minAmount": "12", - "weight": "25.0", - "id": "526", - "maxAmount": "12" - }, - { - "minAmount": "12", - "weight": "25.0", - "id": "532", - "maxAmount": "12" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4111", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "13290", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2729", + "weight": "1.0", + "id": "4111", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "7978", + "weight": "2.0", + "id": "4160", + "maxAmount": "10" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "13280", + "maxAmount": "10" + }, + { + "minAmount": "20", + "weight": "2.0", + "id": "13278", + "maxAmount": "60" + }, + { + "minAmount": "20", + "weight": "2.0", + "id": "13279", + "maxAmount": "60" + }, + { + "minAmount": "5", + "weight": "2.0", + "id": "9143", + "maxAmount": "20" + }, + { + "minAmount": "5", + "weight": "10.0", + "id": "561", + "maxAmount": "5" + }, + { + "minAmount": "15", + "weight": "6.0", + "id": "561", + "maxAmount": "15" + }, + { + "minAmount": "30", + "weight": "2.0", + "id": "561", + "maxAmount": "30" + }, + { + "minAmount": "1", + "weight": "18.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "15.0", + "id": "14422", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "8.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "22", + "weight": "8.0", + "id": "995", + "maxAmount": "22" + }, + { + "minAmount": "748", + "weight": "2.0", + "id": "995", + "maxAmount": "748" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "239", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "3.0", + "id": "225", + "maxAmount": "2" + }, + { + "minAmount": "2", + "weight": "3.0", + "id": "2114", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1963", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "2.0", + "id": "1963", + "maxAmount": "2" + }, + { + "minAmount": "20", + "weight": "2.0", + "id": "1780", + "maxAmount": "30" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "2289", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "2.0", + "id": "526", + "maxAmount": "2" + }, + { + "minAmount": "4", + "weight": "1.0", + "id": "526", + "maxAmount": "4" + }, + { + "minAmount": "2", + "weight": "2.0", + "id": "532", + "maxAmount": "2" + }, + { + "minAmount": "4", + "weight": "1.0", + "id": "532", + "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "161", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "173", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "4", + "weight": "4.0", + "id": "558", + "maxAmount": "16" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "560", + "maxAmount": "5" } ] }, { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "73.7631", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.5666", @@ -26315,12 +22736,6 @@ "weight": "6.9849", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "73.7631", - "id": "0", - "maxAmount": "1" } ], "ids": "1610,1827,6389", @@ -26328,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": "2737", + "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" } ] }, @@ -26596,7 +23029,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2731", + "id": "1", "maxAmount": "1" }, { @@ -26616,6 +23049,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "88.902", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "2.4973", @@ -26639,12 +23078,6 @@ "weight": "0.3217", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "88.902", - "id": "0", - "maxAmount": "1" } ], "ids": "1612", @@ -26652,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": "2713", + "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" } ] }, @@ -26866,6 +23215,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "55.2891", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "8.8367", @@ -26889,12 +23244,6 @@ "weight": "1.0327", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "55.2891", - "id": "0", - "maxAmount": "1" } ], "ids": "1613", @@ -26902,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" } ] }, @@ -27098,6 +23393,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "52.8956", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "8.6662", @@ -27121,10 +23422,18 @@ "weight": "1.0538", "id": "12163", "maxAmount": "1" + } + ], + "tertiary": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "7979", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "52.8956", + "weight": "4999.0", "id": "0", "maxAmount": "1" } @@ -27134,188 +23443,158 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1283", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", + "id": "1365", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", "id": "1361", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1113", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1147", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "1.0", + "id": "1113", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.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": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", + "weight": "19.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "50", - "weight": "25.0", + "weight": "8.0", "id": "556", "maxAmount": "50" }, { "minAmount": "7", - "weight": "25.0", + "weight": "4.0", "id": "565", "maxAmount": "7" }, { "minAmount": "10", - "weight": "25.0", + "weight": "6.0", "id": "562", "maxAmount": "10" }, { "minAmount": "3", - "weight": "25.0", + "weight": "1.0", "id": "563", "maxAmount": "3" }, - { - "minAmount": "30", - "weight": "100.0", - "id": "995", - "maxAmount": "690" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "379", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1454", - "maxAmount": "1" - }, { "minAmount": "60", - "weight": "50.0", + "weight": "5.0", "id": "7937", "maxAmount": "60" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "2361", "maxAmount": "1" }, + { + "minAmount": "30", + "weight": "7.0", + "id": "995", + "maxAmount": "30" + }, + { + "minAmount": "44", + "weight": "6.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "132", + "weight": "35.0", + "id": "995", + "maxAmount": "132" + }, + { + "minAmount": "220", + "weight": "9.0", + "id": "995", + "maxAmount": "220" + }, + { + "minAmount": "460", + "weight": "1.0", + "id": "995", + "maxAmount": "460" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", + "id": "379", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1454", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1452", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "133", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "12070", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "22.0", + "weight": "2.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.2", - "id": "4151", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "0.25", + "id": "4151", "maxAmount": "1" } ] @@ -27330,6 +23609,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "67.2875", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.0347", @@ -27353,10 +23638,18 @@ "weight": "1.2214", "id": "12163", "maxAmount": "1" + } + ], + "tertiary": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "7977", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "67.2875", + "weight": "1999.0", "id": "0", "maxAmount": "1" } @@ -27541,13 +23834,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "7977", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2813", + "id": "5733", "maxAmount": "1" }, { @@ -27574,6 +23861,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "53.9118", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.3528", @@ -27597,12 +23890,6 @@ "weight": "1.0855", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "53.9118", - "id": "0", - "maxAmount": "1" } ], "ids": "1618,1619,6215", @@ -27610,200 +23897,158 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1325", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1157", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", + "id": "1325", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1181", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1109", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "4125", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1147", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1163", - "maxAmount": "1" - }, { "minAmount": "60", - "weight": "50.0", + "weight": "8.0", "id": "554", "maxAmount": "60" }, { "minAmount": "3", - "weight": "25.0", + "weight": "3.0", "id": "565", "maxAmount": "3" }, { "minAmount": "10", - "weight": "25.0", + "weight": "5.0", "id": "565", "maxAmount": "10" }, { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "maxAmount": "1" + "minAmount": "30", + "weight": "1.0", + "id": "565", + "maxAmount": "30" }, { "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", + "weight": "1.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "10", - "weight": "50.0", + "weight": "7.0", "id": "995", "maxAmount": "10" }, { "minAmount": "40", - "weight": "50.0", + "weight": "25.0", "id": "995", "maxAmount": "40" }, + { + "minAmount": "120", + "weight": "20.0", + "id": "995", + "maxAmount": "120" + }, + { + "minAmount": "200", + "weight": "10.0", + "id": "995", + "maxAmount": "200" + }, + { + "minAmount": "450", + "weight": "1.0", + "id": "995", + "maxAmount": "450" + }, { "minAmount": "1", - "weight": "25.0", - "id": "444", + "weight": "10.0", + "id": "526", "maxAmount": "1" }, { - "minAmount": "2", - "weight": "25.0", - "id": "526", + "minAmount": "1", + "weight": "7.0", + "id": "532", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "3.0", + "id": "532", "maxAmount": "3" }, { "minAmount": "1", - "weight": "25.0", - "id": "532", - "maxAmount": "1" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "532", - "maxAmount": "4" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "2293", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1635", + "weight": "2.0", + "id": "444", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "4.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2677", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "2.0", + "id": "5104", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "11072", "maxAmount": "1" } ] @@ -27818,6 +24063,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "88.9239", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "2.2682", @@ -27843,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", + "weight": "4.0", + "id": "5733", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2851", - "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" } ] @@ -28134,6 +24267,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "64.0022", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.3073", @@ -28411,15 +24550,15 @@ "weight": "100.0", "id": "526", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "592", - "maxAmount": "1" } ], "charm": [ + { + "minAmount": "1", + "weight": "55.1863", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "11.8733", @@ -28443,12 +24582,6 @@ "weight": "0.9525", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "55.1863", - "id": "0", - "maxAmount": "1" } ], "ids": "1624", @@ -28461,16 +24594,10 @@ "maxAmount": "1" }, { - "minAmount": "12", + "minAmount": "15", "weight": "50.0", "id": "892", - "maxAmount": "12" - }, - { - "minAmount": "24", - "weight": "50.0", - "id": "892", - "maxAmount": "24" + "maxAmount": "15" }, { "minAmount": "1", @@ -28486,22 +24613,10 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "15.0", "id": "1213", "maxAmount": "1" }, - { - "minAmount": "5", - "weight": "5.0", - "id": "830", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "1353", - "maxAmount": "1" - }, { "minAmount": "37", "weight": "50.0", @@ -28522,21 +24637,15 @@ }, { "minAmount": "2", - "weight": "25.0", + "weight": "15.0", "id": "566", "maxAmount": "2" }, { - "minAmount": "3", + "minAmount": "5", "weight": "25.0", "id": "566", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "563", - "maxAmount": "1" + "maxAmount": "5" }, { "minAmount": "1", @@ -28594,7 +24703,7 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "25.0", "id": "213", "maxAmount": "1" }, @@ -28606,13 +24715,13 @@ }, { "minAmount": "15", - "weight": "50.0", + "weight": "25.0", "id": "995", "maxAmount": "15" }, { "minAmount": "25", - "weight": "50.0", + "weight": "25.0", "id": "995", "maxAmount": "25" }, @@ -28630,13 +24739,13 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "15.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "0.02822962", "id": "3140", "maxAmount": "1" }, @@ -28645,6 +24754,24 @@ "weight": "100.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "300", + "weight": "15.0", + "id": "554", + "maxAmount": "300" + }, + { + "minAmount": "60", + "weight": "25.0", + "id": "995", + "maxAmount": "60" + }, + { + "minAmount": "300", + "weight": "25.0", + "id": "995", + "maxAmount": "300" } ] }, @@ -28657,39 +24784,14 @@ "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" - } - ], + "charm": [], "ids": "1631,1632", "description": "", "main": [ { "minAmount": "1", "weight": "0.07", - "id": "4105", + "id": "4115", "maxAmount": "1" }, { @@ -28814,7 +24916,7 @@ }, { "minAmount": "1", - "weight": "5600.0", + "weight": "5.6", "id": "0", "maxAmount": "1" } @@ -28830,6 +24932,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "86.6357", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.0393", @@ -28853,12 +24961,6 @@ "weight": "0.4923", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "86.6357", - "id": "0", - "maxAmount": "1" } ], "ids": "1633,1634,1635,1636,6216", @@ -28969,7 +25071,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2809", + "id": "5733", "maxAmount": "1" }, { @@ -28989,6 +25091,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "65.9946", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.6208", @@ -29012,12 +25120,6 @@ "weight": "0.9453", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "65.9946", - "id": "0", - "maxAmount": "1" } ], "ids": "1637,1638,1639,1640,1641,1642", @@ -29116,7 +25218,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2729", + "id": "1", "maxAmount": "1" }, { @@ -29143,6 +25245,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "61.2459", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "32.8603", @@ -29166,12 +25274,6 @@ "weight": "0.6082", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "61.2459", - "id": "0", - "maxAmount": "1" } ], "ids": "1643,1644,1645,1646,1647", @@ -29306,7 +25408,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2725", + "id": "1", "maxAmount": "1" }, { @@ -29358,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", @@ -29463,6 +25573,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "46.664", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "44.29", @@ -29486,16 +25602,10 @@ "weight": "1.0564", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "46.664", - "id": "0", - "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", @@ -29674,7 +25784,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2839", + "id": "5733", "maxAmount": "1" }, { @@ -30072,12 +26182,6 @@ "id": "686", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "25.0", @@ -30207,7 +26311,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2716", + "id": "1", "maxAmount": "1" }, { @@ -30280,8 +26384,8 @@ "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "2677", + "weight": "1.0", + "id": "0", "maxAmount": "1" } ] @@ -30358,12 +26462,6 @@ "id": "1129", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "50.0", @@ -30481,7 +26579,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2677", + "id": "5733", "maxAmount": "1" } ] @@ -30498,33 +26596,33 @@ "charm": [ { "minAmount": "1", - "weight": "100.0", + "weight": "82.8244", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "12.2137", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "0.5725", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "4.0076", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "0.3817", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ], "ids": "1752,1753,6109,6110,6111", @@ -30614,6 +26712,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "93.2258", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "2.7419", @@ -30688,6 +26792,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "86.9276", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "6.5495", @@ -30711,12 +26821,6 @@ "weight": "1.1981", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "86.9276", - "id": "0", - "maxAmount": "1" } ], "ids": "1822,1823,1824,1825,5752,5753,5754,5755,5756,5757,5758,5759,5760,5761,5762,5763,5764,5765,5766,5767,5768,5769,5783,5785,5873,5874,5875,5876,5877,5878", @@ -30851,7 +26955,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2698", + "id": "1", "maxAmount": "1" }, { @@ -30986,12 +27090,6 @@ "weight": "100.0", "id": "526", "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "100.0", - "id": "526", - "maxAmount": "2" } ], "charm": [], @@ -31045,12 +27143,6 @@ "weight": "50.0", "id": "995", "maxAmount": "15" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "7910", - "maxAmount": "1" } ] }, @@ -31289,7 +27381,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2691", + "id": "1", "maxAmount": "1" }, { @@ -31303,6 +27395,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "52.5489", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "6.5845", @@ -31326,15 +27424,9 @@ "weight": "0.8071", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "52.5489", - "id": "0", - "maxAmount": "1" } ], - "ids": "1832,5750", + "ids": "5750", "description": "", "main": [ { @@ -31568,7 +27660,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2692", + "id": "5733", "maxAmount": "1" }, { @@ -31854,7 +27946,7 @@ "maxAmount": "1" } ], - "ids": "1960,5360", + "ids": "1958,1961,1960,5360", "description": "", "main": [ { @@ -31871,7 +27963,7 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "4675", "maxAmount": "1" }, @@ -32058,13 +28150,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2827", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6808", + "id": "5733", "maxAmount": "1" }, { @@ -32091,6 +28177,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "86.2564", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.1429", @@ -32114,12 +28206,6 @@ "weight": "1.2436", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "86.2564", - "id": "0", - "maxAmount": "1" } ], "ids": "1976", @@ -32243,6 +28329,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "79.2519", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.389", @@ -32266,12 +28358,6 @@ "weight": "0.6983", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "79.2519", - "id": "0", - "maxAmount": "1" } ], "ids": "2031", @@ -32309,6 +28395,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "91.6382", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.6928", @@ -32332,34 +28424,21 @@ "weight": "0.256", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "91.6382", - "id": "0", - "maxAmount": "1" } ], "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": [], "charm": [ + { + "minAmount": "1", + "weight": "90.4762", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.291", @@ -32383,24 +28462,11 @@ "weight": "1.5873", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "90.4762", - "id": "0", - "maxAmount": "1" } ], "ids": "2035", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -32417,54 +28483,10 @@ "maxAmount": "1" } ], - "charm": [ - { - "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": "50.0", - "id": "7850", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2696", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "85.8093", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -32873,14 +28895,14 @@ "main": [ { "minAmount": "100", - "weight": "50.0", + "weight": "99.0", "id": "995", "maxAmount": "100" }, { "minAmount": "1", - "weight": "5.0", - "id": "2737", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -32895,6 +28917,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "73.5695", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.9046", @@ -32918,12 +28946,6 @@ "weight": "10.7629", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "73.5695", - "id": "0", - "maxAmount": "1" } ], "ids": "2130,2131,2132,2133,3276,3277,3278,3279", @@ -33021,8 +29043,8 @@ }, { "minAmount": "1", - "weight": "25.0", - "id": "2841", + "weight": "6.11111", + "id": "5733", "maxAmount": "1" }, { @@ -33139,7 +29161,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2805", + "id": "1", "maxAmount": "1" }, { @@ -33266,33 +29288,33 @@ "charm": [ { "minAmount": "1", - "weight": "100.0", + "weight": "99.1835", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.8165", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "0.0", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "0.0", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "0.0", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ], "ids": "2239,2240,2316,2317", @@ -33316,6 +29338,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "96.1266", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "0.7056", @@ -33482,6 +29510,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "80.0326", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.0474", @@ -33648,6 +29682,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "73.5196", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.1545", @@ -33791,10 +29831,10 @@ "maxAmount": "1" }, { - "minAmount": "1", + "minAmount": "2", "weight": "100.0", "id": "12161", - "maxAmount": "1" + "maxAmount": "2" }, { "minAmount": "1", @@ -33814,6 +29854,12 @@ } ], "charm": [ + { + "minAmount": "3", + "weight": "2.8249", + "id": "0", + "maxAmount": "3" + }, { "minAmount": "3", "weight": "79.2687", @@ -33837,12 +29883,6 @@ "weight": "1.9617", "id": "12163", "maxAmount": "3" - }, - { - "minAmount": "3", - "weight": "2.8249", - "id": "0", - "maxAmount": "3" } ], "ids": "2452,2885", @@ -34175,7 +30215,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2817", + "id": "5733", "maxAmount": "1" }, { @@ -34250,7 +30290,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2678", + "id": "5733", "maxAmount": "1" }, { @@ -34273,33 +30313,33 @@ "charm": [ { "minAmount": "1", - "weight": "100.0", + "weight": "56.7727", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "15.2283", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "7.576", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "8.5159", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "11.907", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ], "ids": "2457,2884", @@ -34626,7 +30666,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2839", + "id": "5733", "maxAmount": "1" }, { @@ -34659,22 +30699,23 @@ }, { "default": [], - "charm": [], + "charm": [ + { + "minAmount": "1", + "weight": "40.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "88.0", + "id": "12168", + "maxAmount": "1" + } + ], "ids": "2591,2592,2593,2594,2595,2596,2597,7753", "description": "", "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "1387", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "6568", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "1.0", @@ -34682,300 +30723,266 @@ "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": "5.0", + "minAmount": "2", + "weight": "64.0", + "id": "561", + "maxAmount": "2" + }, + { + "minAmount": "2", + "weight": "32.0", "id": "560", - "maxAmount": "6" + "maxAmount": "2" }, { "minAmount": "1", - "weight": "5.0", - "id": "565", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4697", - "maxAmount": "10" - }, - { - "minAmount": "10", - "weight": "5.0", - "id": "4699", - "maxAmount": "40" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1442", + "weight": "8.0", + "id": "1617", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "32.0", + "id": "1619", + "maxAmount": "1" + }, + { + "minAmount": "1", + "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": "100", + "weight": "1280.0", + "id": "6529", + "maxAmount": "1000" + }, + { + "minAmount": "1", + "weight": "1622.0", + "id": "0", + "maxAmount": "1" + } + ] + }, + { + "default": [], + "charm": [ + { + "minAmount": "1", + "weight": "40.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "88.0", + "id": "12168", + "maxAmount": "1" + } + ], + "ids": "2598,2599,2600,2601,2602,2603,7754,7767,7768,7769", + "description": "", + "main": [ + { + "minAmount": "1", + "weight": "8.0", + "id": "2347", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "1755", + "maxAmount": "1" + }, + { + "minAmount": "1", + "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": "50.0", - "id": "12168", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "9194", - "maxAmount": "1" - }, - { - "minAmount": "4", - "weight": "5.0", - "id": "7936", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "263.0", + "id": "0", "maxAmount": "1" } ] }, { "default": [], - "charm": [], - "ids": "2598,2599,2600,2601,2602,2603,7754,7767,7768,7769", - "description": "", - "main": [ - { - "minAmount": "150", - "weight": "50.0", - "id": "6529", - "maxAmount": "800" - }, + "charm": [ { "minAmount": "1", - "weight": "50.0", - "id": "12168", + "weight": "40.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "2", - "weight": "50.0", + "weight": "88.0", "id": "12168", "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "1775", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "149", - "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", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "9194", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" } - ] - }, - { - "default": [], - "charm": [], + ], "ids": "2604,2605,2606,2607,2608,2609,7755", "description": "", "main": [ { "minAmount": "9", - "weight": "0.665", + "weight": "1.0", "id": "6522", - "maxAmount": "19" + "maxAmount": "29" }, { "minAmount": "1", - "weight": "0.665", + "weight": "1.0", "id": "6523", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.665", + "weight": "1.0", "id": "6525", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.665", + "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": "50.0", - "id": "6529", - "maxAmount": "2000" - }, - { - "minAmount": "15000", - "weight": "0.665", - "id": "6529", - "maxAmount": "15000" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "12168", + "weight": "8.0", + "id": "1621", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "7054", + "weight": "16.0", + "id": "1623", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "379", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "20.0", "id": "9194", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" + "minAmount": "100", + "weight": "160.0", + "id": "6529", + "maxAmount": "2000" }, { "minAmount": "1", - "weight": "50.0", + "weight": "299.0", "id": "0", "maxAmount": "1" } @@ -34983,420 +30990,85 @@ }, { "default": [], - "charm": [], + "charm": [ + { + "minAmount": "1", + "weight": "40.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "88.0", + "id": "12168", + "maxAmount": "2" + } + ], "ids": "2610,2611,2612,2613,2614,2615,2616,2624,2625", "description": "", "main": [ { "minAmount": "1", - "weight": "0.675", + "weight": "1.0", "id": "6528", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.675", + "weight": "1.0", "id": "6524", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.675", + "weight": "1.0", "id": "6568", "maxAmount": "1" }, - { - "minAmount": "12", - "weight": "5.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": "50.0", + "weight": "160.0", "id": "6529", "maxAmount": "2000" }, { "minAmount": "1", - "weight": "50.0", - "id": "12168", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.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": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "300.0", "id": "0", "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": "25.0", - "id": "2701", - "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": "2691", - "maxAmount": "1" - } - ] - }, { "default": [ { @@ -35465,10 +31137,10 @@ "maxAmount": "1" }, { - "minAmount": "29", + "minAmount": "2", "weight": "25.0", "id": "558", - "maxAmount": "29" + "maxAmount": "9" }, { "minAmount": "10", @@ -35489,10 +31161,10 @@ "maxAmount": "7" }, { - "minAmount": "46", + "minAmount": "4", "weight": "25.0", "id": "562", - "maxAmount": "46" + "maxAmount": "6" }, { "minAmount": "2", @@ -35501,10 +31173,10 @@ "maxAmount": "2" }, { - "minAmount": "34", + "minAmount": "3", "weight": "25.0", "id": "563", - "maxAmount": "34" + "maxAmount": "4" }, { "minAmount": "6", @@ -36002,11 +31674,17 @@ { "minAmount": "1", "weight": "100.0", - "id": "532", + "id": "526", "maxAmount": "1" } ], "charm": [ + { + "minAmount": "1", + "weight": "64.7702", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "8.6684", @@ -36030,12 +31708,6 @@ "weight": "13.9158", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "64.7702", - "id": "0", - "maxAmount": "1" } ], "ids": "2783", @@ -36043,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" } ] }, @@ -37305,298 +32929,190 @@ "description": "", "main": [ { - "minAmount": "1", - "weight": "25.0", - "id": "1357", - "maxAmount": "1" + "minAmount": "25", + "weight": "10.0", + "id": "866", + "maxAmount": "50" }, { "minAmount": "1", - "weight": "5.0", - "id": "1303", + "weight": "7.0", + "id": "2489", "maxAmount": "1" }, { - "minAmount": "1", + "minAmount": "5", "weight": "5.0", + "id": "805", + "maxAmount": "10" + }, + { + "minAmount": "10", + "weight": "5.0", + "id": "810", + "maxAmount": "25" + }, + { + "minAmount": "200", + "weight": "5.0", + "id": "863", + "maxAmount": "500" + }, + { + "minAmount": "50", + "weight": "5.0", + "id": "865", + "maxAmount": "150" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "3757", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1373", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1319", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6724", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "6739", - "maxAmount": "1" - }, - { - "minAmount": "218", - "weight": "50.0", - "id": "884", - "maxAmount": "590" - }, - { - "minAmount": "54", - "weight": "50.0", - "id": "886", - "maxAmount": "98" - }, - { - "minAmount": "214", - "weight": "35.0", - "id": "863", - "maxAmount": "500" - }, - { - "minAmount": "54", - "weight": "35.0", - "id": "865", - "maxAmount": "90" - }, - { - "minAmount": "31", - "weight": "35.0", - "id": "866", - "maxAmount": "69" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "805", - "maxAmount": "7" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "830", - "maxAmount": "10" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "9144", - "maxAmount": "14" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2489", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "6133", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "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": "5.0", + "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": "1.0", + "id": "6133", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6135", + "maxAmount": "1" + }, + { + "minAmount": "1", + "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": "7.0", + "id": "14428", + "maxAmount": "1" + }, + { + "minAmount": "156", + "weight": "10.0", + "id": "995", + "maxAmount": "156" + }, + { + "minAmount": "1", + "weight": "6.0", + "id": "413", + "maxAmount": "1" + }, + { + "minAmount": "10", + "weight": "5.0", + "id": "45", + "maxAmount": "30" + }, { "minAmount": "5", - "weight": "25.0", + "weight": "5.0", "id": "385", "maxAmount": "5" }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5298", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1448", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1442", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1446", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1454", - "maxAmount": "1" - }, - { - "minAmount": "246", - "weight": "75.0", - "id": "995", - "maxAmount": "3000" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "45", - "maxAmount": "3" - }, - { - "minAmount": "94", - "weight": "25.0", - "id": "1516", - "maxAmount": "94" - }, - { - "minAmount": "91", - "weight": "25.0", - "id": "1518", - "maxAmount": "125" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "413", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", "id": "207", "maxAmount": "1" }, { - "minAmount": "1", + "minAmount": "50", "weight": "5.0", - "id": "314", - "maxAmount": "200" + "id": "1518", + "maxAmount": "150" + }, + { + "minAmount": "50", + "weight": "3.0", + "id": "1516", + "maxAmount": "150" }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "9431", "maxAmount": "1" }, + { + "minAmount": "250", + "weight": "1.0", + "id": "314", + "maxAmount": "500" + }, { "minAmount": "1", - "weight": "5.0", - "id": "2725", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "10.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6733", - "maxAmount": "1" } ] }, @@ -37621,207 +33137,195 @@ "main": [ { "minAmount": "1", - "weight": "75.0", - "id": "1397", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "75.0", + "weight": "10.0", "id": "1399", "maxAmount": "1" }, { "minAmount": "1", - "weight": "75.0", + "weight": "5.0", "id": "1395", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "6562", + "weight": "4.0", + "id": "1397", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1392", "maxAmount": "10" }, { "minAmount": "1", - "weight": "20.0", - "id": "6739", + "weight": "1.0", + "id": "3757", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1439", - "maxAmount": "80" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1441", - "maxAmount": "80" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1445", - "maxAmount": "80" - }, - { - "minAmount": "155", - "weight": "25.0", - "id": "556", - "maxAmount": "155" - }, - { - "minAmount": "32", - "weight": "25.0", - "id": "4698", - "maxAmount": "32" - }, - { - "minAmount": "22", - "weight": "25.0", - "id": "560", - "maxAmount": "85" - }, - { - "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.0", + "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.0", + "weight": "1.0", "id": "6141", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "3755", + "weight": "1.0", + "id": "6731", "maxAmount": "1" }, { - "minAmount": "256", - "weight": "75.0", - "id": "995", - "maxAmount": "3000" + "minAmount": "100", + "weight": "6.0", + "id": "556", + "maxAmount": "200" }, { "minAmount": "50", - "weight": "75.0", - "id": "7937", + "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": "75.0", + "weight": "5.0", "id": "207", "maxAmount": "1" }, + { + "minAmount": "972", + "weight": "3.0", + "id": "995", + "maxAmount": "972" + }, { "minAmount": "1", - "weight": "5.0", - "id": "2739", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "5281", + "weight": "10.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6731", - "maxAmount": "1" } ] }, @@ -37846,223 +33350,223 @@ "main": [ { "minAmount": "1", - "weight": "55.0", - "id": "3757", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1343", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.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": "5.0", - "id": "1373", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "6739", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "17.0", "id": "1193", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.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", + "weight": "3.0", + "id": "1273", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", "id": "1123", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "3758", + "weight": "2.0", + "id": "3757", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "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": "5.0", - "id": "6737", + "weight": "1.0", + "id": "3758", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "6735", + "weight": "1.0", + "id": "1315", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "2570", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "6129", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "6130", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "147", + "weight": "1.0", + "id": "6737", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "159", + "weight": "1.0", + "id": "6735", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "165", + "weight": "1.0", + "id": "2456", "maxAmount": "1" }, { "minAmount": "1", - "weight": "20.0", + "weight": "1.0", "id": "141", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "191", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "129", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2361", + "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": "25.0", + "weight": "10.0", "id": "448", "maxAmount": "25" }, + { + "minAmount": "1", + "weight": "3.0", + "id": "2361", + "maxAmount": "1" + }, { "minAmount": "100", - "weight": "25.0", + "weight": "2.0", "id": "454", "maxAmount": "100" }, { "minAmount": "150", - "weight": "5.0", + "weight": "1.0", "id": "441", "maxAmount": "150" }, { "minAmount": "15", - "weight": "5.0", + "weight": "1.0", "id": "2354", - "maxAmount": "38" + "maxAmount": "30" + }, + { + "minAmount": "156", + "weight": "10.0", + "id": "995", + "maxAmount": "156" + }, + { + "minAmount": "1", + "weight": "7.0", + "id": "207", + "maxAmount": "1" }, { "minAmount": "5", - "weight": "25.0", + "weight": "7.0", "id": "365", "maxAmount": "5" }, { "minAmount": "5", - "weight": "25.0", + "weight": "4.0", "id": "373", "maxAmount": "5" }, { "minAmount": "5", - "weight": "25.0", + "weight": "1.0", "id": "385", "maxAmount": "5" }, { "minAmount": "1", - "weight": "25.0", - "id": "207", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "75.0", - "id": "1446", + "weight": "10.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "75.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "156", - "weight": "75.0", - "id": "995", - "maxAmount": "3000" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2739", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "31", "maxAmount": "1" } @@ -38071,6 +33575,12 @@ { "default": [], "charm": [ + { + "minAmount": "2", + "weight": "47.6638", + "id": "0", + "maxAmount": "2" + }, { "minAmount": "2", "weight": "16.3926", @@ -38094,12 +33604,6 @@ "weight": "3.1323", "id": "12163", "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "47.6638", - "id": "0", - "maxAmount": "2" } ], "ids": "2889", @@ -38324,7 +33828,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2819", + "id": "5733", "maxAmount": "1" }, { @@ -38351,6 +33855,12 @@ } ], "charm": [ + { + "minAmount": "2", + "weight": "33.9281", + "id": "0", + "maxAmount": "2" + }, { "minAmount": "2", "weight": "6.6641", @@ -38374,12 +33884,20 @@ "weight": "1.4948", "id": "12163", "maxAmount": "2" + } + ], + "tertiary": [ + { + "minAmount": "1", + "weight": "9999.0", + "id": "0", + "maxAmount": "1" }, { - "minAmount": "2", - "weight": "33.9281", - "id": "0", - "maxAmount": "2" + "minAmount": "1", + "weight": "1.0", + "id": "11286", + "maxAmount": "1" } ], "ids": "3068,3069,3070,3071", @@ -38387,261 +33905,189 @@ "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": "2733", - "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.12", - "id": "11286", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" } ] }, { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "91.5619", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "1.7734", @@ -38665,12 +34111,6 @@ "weight": "0.1886", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "91.5619", - "id": "0", - "maxAmount": "1" } ], "ids": "3153", @@ -38769,7 +34209,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2815", + "id": "5733", "maxAmount": "1" }, { @@ -38787,160 +34227,107 @@ ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "799", + "maxAmount": "1" + } + ], "charm": [], "ids": "3200", - "description": "", + "description": "Chaos Elemental (Boss)", "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "2739", + "weight": "6.0", + "id": "7158", + "maxAmount": "1" + }, + { + "minAmount": "500", + "weight": "50.0", + "id": "556", + "maxAmount": "500" + }, + { + "minAmount": "50", + "weight": "25.0", + "id": "565", + "maxAmount": "50" + }, + { + "minAmount": "100", + "weight": "50.0", + "id": "562", + "maxAmount": "100" + }, + { + "minAmount": "50", + "weight": "25.0", + "id": "560", + "maxAmount": "50" + }, + { + "minAmount": "200", + "weight": "25.0", + "id": "892", + "maxAmount": "200" + }, + { + "minAmount": "1", + "weight": "100.0", + "id": "464", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "100.0", + "id": "2446", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "100.0", + "id": "187", + "maxAmount": "1" + }, + { + "minAmount": "300", + "weight": "150.0", + "id": "995", + "maxAmount": "300" + }, + { + "minAmount": "1", + "weight": "25.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "7158", + "weight": "6.0", + "id": "12070", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "1215", - "maxAmount": "1" - }, - { - "minAmount": "250", - "weight": "50.0", - "id": "562", - "maxAmount": "250" - }, - { - "minAmount": "300", + "minAmount": "400", "weight": "50.0", "id": "809", - "maxAmount": "300" + "maxAmount": "400" }, { "minAmount": "500", "weight": "25.0", - "id": "556", + "id": "884", "maxAmount": "500" }, { - "minAmount": "1", + "minAmount": "1000", "weight": "25.0", - "id": "560", - "maxAmount": "125" - }, - { - "minAmount": "75", - "weight": "25.0", - "id": "565", - "maxAmount": "75" - }, - { - "minAmount": "150", - "weight": "25.0", - "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2442", - "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "2297", - "maxAmount": "3" - }, - { - "minAmount": "10", - "weight": "50.0", - "id": "265", - "maxAmount": "10" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "361", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5940", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5952", - "maxAmount": "1" - }, - { - "minAmount": "4", - "weight": "50.0", - "id": "526", - "maxAmount": "4" - }, - { - "minAmount": "5", - "weight": "50.0", - "id": "530", - "maxAmount": "5" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "532", - "maxAmount": "3" - }, - { - "minAmount": "7500", - "weight": "25.0", - "id": "995", - "maxAmount": "7500" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "536", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "534", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1462", - "maxAmount": "1" + "id": "884", + "maxAmount": "1000" } ] }, @@ -38954,6 +34341,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "83.9189", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "14.0681", @@ -38977,12 +34370,6 @@ "weight": "0.1567", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "83.9189", - "id": "0", - "maxAmount": "1" } ], "ids": "3201,3202", @@ -39173,352 +34560,245 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "2.0", + "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "201", + "weight": "1.0", + "id": "1203", "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": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" + "minAmount": "2", + "weight": "22.0", + "id": "877", + "maxAmount": "12" }, { "minAmount": "7", - "weight": "25.0", - "id": "555", + "weight": "3.0", + "id": "882", "maxAmount": "7" }, { "minAmount": "4", - "weight": "25.0", + "weight": "2.0", "id": "557", "maxAmount": "4" }, { "minAmount": "6", - "weight": "25.0", + "weight": "2.0", "id": "554", "maxAmount": "6" }, { "minAmount": "9", - "weight": "25.0", + "weight": "2.0", "id": "558", "maxAmount": "9" }, { "minAmount": "2", - "weight": "5.0", + "weight": "1.0", "id": "562", "maxAmount": "2" }, - { - "minAmount": "7", - "weight": "5.0", - "id": "559", - "maxAmount": "7" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "877", - "maxAmount": "12" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "882", - "maxAmount": "7" - }, { "minAmount": "1", - "weight": "25.0", - "id": "1203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1139", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "40" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "313", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2702", - "maxAmount": "1" - } - ] - }, - { - "default": [], - "charm": [], - "ids": "3237", - "description": "", - "main": [ - { - "minAmount": "2", - "weight": "50.0", - "id": "877", - "maxAmount": "12" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "882", - "maxAmount": "7" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "562", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "557", - "maxAmount": "4" - }, - { - "minAmount": "6", - "weight": "25.0", - "id": "554", - "maxAmount": "6" - }, - { - "minAmount": "9", - "weight": "25.0", - "id": "558", - "maxAmount": "13" - }, - { - "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", + "weight": "23.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "3", - "weight": "50.0", + "weight": "37.0", "id": "995", "maxAmount": "3" }, { "minAmount": "5", - "weight": "50.0", + "weight": "9.0", "id": "995", "maxAmount": "5" }, { - "minAmount": "1", - "weight": "25.0", - "id": "1139", - "maxAmount": "1" + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" }, { "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "313", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1965", + "weight": "8.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "2714", + "id": "313", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", + "maxAmount": "1" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "3237", + "description": "", + "main": [ + { + "minAmount": "1", + "weight": "2.0", + "id": "1139", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1203", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "22.0", + "id": "877", + "maxAmount": "12" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "882", + "maxAmount": "7" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "557", + "maxAmount": "4" + }, + { + "minAmount": "6", + "weight": "2.0", + "id": "554", + "maxAmount": "6" + }, + { + "minAmount": "9", + "weight": "2.0", + "id": "558", + "maxAmount": "9" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "562", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "23.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "37.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "5", + "weight": "9.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "313", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -39536,172 +34816,118 @@ "ids": "3238", "description": "", "main": [ - { - "minAmount": "2", - "weight": "25.0", - "id": "562", - "maxAmount": "2" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "562", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "557", - "maxAmount": "4" - }, - { - "minAmount": "6", - "weight": "25.0", - "id": "554", - "maxAmount": "6" - }, - { - "minAmount": "9", - "weight": "25.0", - "id": "558", - "maxAmount": "9" - }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1203", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "882", - "maxAmount": "7" - }, { "minAmount": "2", - "weight": "50.0", + "weight": "22.0", "id": "877", "maxAmount": "12" }, { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "maxAmount": "1" + "minAmount": "7", + "weight": "3.0", + "id": "882", + "maxAmount": "7" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "557", + "maxAmount": "4" + }, + { + "minAmount": "6", + "weight": "2.0", + "id": "554", + "maxAmount": "6" + }, + { + "minAmount": "9", + "weight": "2.0", + "id": "558", + "maxAmount": "9" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "562", + "maxAmount": "2" }, { "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", + "weight": "23.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "3", - "weight": "50.0", + "weight": "37.0", "id": "995", "maxAmount": "3" }, { "minAmount": "5", - "weight": "50.0", + "weight": "9.0", "id": "995", "maxAmount": "5" }, { - "minAmount": "1", - "weight": "25.0", - "id": "313", - "maxAmount": "1" + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" }, { "minAmount": "1", - "weight": "25.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1965", + "weight": "8.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "2707", + "id": "313", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -39719,294 +34945,247 @@ "ids": "3239", "description": "", "main": [ - { - "minAmount": "7", - "weight": "25.0", - "id": "882", - "maxAmount": "7" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "877", - "maxAmount": "12" - }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1203", "maxAmount": "1" }, { "minAmount": "2", - "weight": "25.0", - "id": "562", - "maxAmount": "2" + "weight": "22.0", + "id": "877", + "maxAmount": "12" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "882", + "maxAmount": "7" }, { "minAmount": "4", - "weight": "25.0", - "id": "562", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "25.0", + "weight": "2.0", "id": "557", "maxAmount": "4" }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, { "minAmount": "6", - "weight": "25.0", + "weight": "2.0", "id": "554", "maxAmount": "6" }, { "minAmount": "9", - "weight": "25.0", + "weight": "2.0", "id": "558", "maxAmount": "9" }, { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "maxAmount": "1" + "minAmount": "2", + "weight": "1.0", + "id": "562", + "maxAmount": "2" }, { "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", + "weight": "23.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "3", - "weight": "25.0", + "weight": "37.0", "id": "995", "maxAmount": "3" }, { "minAmount": "5", - "weight": "25.0", + "weight": "9.0", "id": "995", "maxAmount": "5" }, + { + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" + }, { "minAmount": "1", - "weight": "25.0", - "id": "1965", + "weight": "8.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "5.0", "id": "313", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2680", + "weight": "2.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], "charm": [], "ids": "3240,5025,5048", "description": "", "main": [ { "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": "3", - "weight": "25.0", - "id": "995", - "maxAmount": "3" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "995", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1203", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "1965", - "maxAmount": "1" + "minAmount": "2", + "weight": "22.0", + "id": "877", + "maxAmount": "12" + }, + { + "minAmount": "7", + "weight": "3.0", + "id": "882", + "maxAmount": "7" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "557", + "maxAmount": "4" + }, + { + "minAmount": "6", + "weight": "2.0", + "id": "554", + "maxAmount": "6" + }, + { + "minAmount": "9", + "weight": "2.0", + "id": "558", + "maxAmount": "9" + }, + { + "minAmount": "2", + "weight": "1.0", + "id": "562", + "maxAmount": "2" }, { "minAmount": "1", - "weight": "25.0", - "id": "313", + "weight": "23.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "37.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "5", + "weight": "9.0", + "id": "995", + "maxAmount": "5" + }, + { + "minAmount": "15", + "weight": "4.0", + "id": "995", + "maxAmount": "15" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "2684", + "id": "313", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1965", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -40029,10 +35208,16 @@ "minAmount": "1", "weight": "100.0", "id": "7418", - "maxAmount": "6" + "maxAmount": "3" } ], "charm": [ + { + "minAmount": "2", + "weight": "71.1249", + "id": "0", + "maxAmount": "2" + }, { "minAmount": "2", "weight": "8.5018", @@ -40056,12 +35241,6 @@ "weight": "2.6439", "id": "12163", "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "71.1249", - "id": "0", - "maxAmount": "2" } ], "ids": "3340", @@ -40073,66 +35252,6 @@ "id": "556", "maxAmount": "105" }, - { - "minAmount": "1", - "weight": "20.0", - "id": "232", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "12110", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "222", - "maxAmount": "20" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "238", - "maxAmount": "15" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "2971", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "10110", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "226", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "9018", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "20.0", - "id": "224", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "240", - "maxAmount": "10" - }, { "minAmount": "105", "weight": "50.0", @@ -40181,18 +35300,6 @@ "id": "1369", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1373", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1319", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "50.0", @@ -40201,7 +35308,7 @@ }, { "minAmount": "1", - "weight": "50.0", + "weight": "25.0", "id": "1147", "maxAmount": "1" }, @@ -40211,24 +35318,6 @@ "id": "1725", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1185", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2359", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "385", - "maxAmount": "5" - }, { "minAmount": "100", "weight": "25.0", @@ -40237,39 +35326,57 @@ }, { "minAmount": "1", - "weight": "5.0", - "id": "2735", + "weight": "6.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "413", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "6.0", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "6.0", "id": "10977", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "25.0", "id": "31", "maxAmount": "1" }, + { + "minAmount": "100", + "weight": "25.0", + "id": "441", + "maxAmount": "100" + }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "50.0", + "id": "1315", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "50.0", + "id": "2359", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "25.0", + "id": "407", + "maxAmount": "1" + }, + { + "minAmount": "4", + "weight": "25.0", + "id": "385", + "maxAmount": "4" } ] }, @@ -40574,6 +35681,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "55.9766", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.1987", @@ -40641,15 +35754,14 @@ ] }, { - "default": [ + "default": [], + "charm": [ { "minAmount": "1", - "weight": "100.0", - "id": "592", + "weight": "91.209", + "id": "0", "maxAmount": "1" - } - ], - "charm": [ + }, { "minAmount": "1", "weight": "2.8008", @@ -40673,249 +35785,51 @@ "weight": "0.289", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "91.209", - "id": "0", - "maxAmount": "1" } ], "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": "2700", - "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" } ] @@ -41395,6 +36309,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "73.7201", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "22.7431", @@ -41418,12 +36338,6 @@ "weight": "0.4157", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "73.7201", - "id": "0", - "maxAmount": "1" } ], "ids": "3707", @@ -41558,12 +36472,6 @@ "weight": "100.0", "id": "532", "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "100.0", - "id": "526", - "maxAmount": "3" } ], "charm": [], @@ -42205,7 +37113,7 @@ { "minAmount": "4", "weight": "25.0", - "id": "4150", + "id": "4160", "maxAmount": "5" }, { @@ -42811,12 +37719,6 @@ "id": "1129", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "50.0", @@ -42880,7 +37782,7 @@ { "minAmount": "1", "weight": "25.0", - "id": "2704", + "id": "5733", "maxAmount": "1" }, { @@ -42943,6 +37845,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "81.7721", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "3.6723", @@ -42966,12 +37874,6 @@ "weight": "0.4235", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "81.7721", - "id": "0", - "maxAmount": "1" } ], "ids": "4348,4349,4350,4351,4352", @@ -43172,7 +38074,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2811", + "id": "5733", "maxAmount": "1" }, { @@ -43199,6 +38101,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "78.7882", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.6555", @@ -43222,12 +38130,6 @@ "weight": "0.5548", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "78.7882", - "id": "0", - "maxAmount": "1" } ], "ids": "4353,4354,4355,4356,4357", @@ -43241,7 +38143,7 @@ }, { "minAmount": "1", - "weight": "25.0", + "weight": "50.0", "id": "1197", "maxAmount": "1" }, @@ -43251,12 +38153,6 @@ "id": "1213", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1247", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "25.0", @@ -43265,19 +38161,19 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "25.0", "id": "1199", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.5", + "weight": "2.73581", "id": "8901", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "25.0", "id": "5281", "maxAmount": "1" }, @@ -43289,7 +38185,7 @@ }, { "minAmount": "1", - "weight": "50.0", + "weight": "25.0", "id": "5296", "maxAmount": "1" }, @@ -43301,7 +38197,7 @@ }, { "minAmount": "1", - "weight": "50.0", + "weight": "25.0", "id": "5297", "maxAmount": "1" }, @@ -43319,7 +38215,7 @@ }, { "minAmount": "1", - "weight": "25.0", + "weight": "10.0", "id": "5106", "maxAmount": "1" }, @@ -43373,25 +38269,25 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "10.0", "id": "5321", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "10.0", "id": "5302", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "10.0", "id": "5303", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "10.0", "id": "5304", "maxAmount": "1" }, @@ -43445,43 +38341,43 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "25.0", "id": "215", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "10.0", "id": "217", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "10.0", "id": "2485", "maxAmount": "1" }, { "minAmount": "3", - "weight": "50.0", + "weight": "25.0", "id": "561", - "maxAmount": "6" + "maxAmount": "3" }, { "minAmount": "50", - "weight": "5.0", + "weight": "25.0", "id": "554", "maxAmount": "50" }, { "minAmount": "44", - "weight": "50.0", + "weight": "25.0", "id": "995", - "maxAmount": "495" + "maxAmount": "44" }, { "minAmount": "1", - "weight": "50.0", + "weight": "100.0", "id": "225", "maxAmount": "1" }, @@ -43495,37 +38391,55 @@ "minAmount": "2", "weight": "25.0", "id": "6332", - "maxAmount": "4" + "maxAmount": "2" }, { "minAmount": "1", - "weight": "5.0", - "id": "2743", + "weight": "12.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "10977", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "25.0", "id": "31", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" + "minAmount": "4", + "weight": "25.0", + "id": "561", + "maxAmount": "4" + }, + { + "minAmount": "6", + "weight": "25.0", + "id": "561", + "maxAmount": "6" + }, + { + "minAmount": "132", + "weight": "25.0", + "id": "995", + "maxAmount": "132" + }, + { + "minAmount": "440", + "weight": "25.0", + "id": "995", + "maxAmount": "440" } ] }, @@ -43539,6 +38453,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "72.1599", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "23.6015", @@ -43872,6 +38792,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "88.929", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.1142", @@ -44056,6 +38982,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "76.9587", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "19.0203", @@ -44309,7 +39241,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2849", + "id": "5733", "maxAmount": "1" }, { @@ -44468,7 +39400,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2683", + "id": "1", "maxAmount": "1" }, { @@ -44480,171 +39412,155 @@ ] }, { - "default": [], - "charm": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "532", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "14426", + "maxAmount": "1" + } + ], + "charm": [ + { + "minAmount": "1", + "weight": "2.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "9016", + "maxAmount": "1" + } + ], "ids": "4418,6218", "description": "", "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1083", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1069", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1147", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1113", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1309", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "3192", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1309", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "802", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "803", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "2.0", + "id": "5670", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "201", + "weight": "1.0", + "id": "1365", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "203", + "weight": "1.0", + "id": "1083", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "205", + "weight": "1.0", + "id": "1199", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "207", + "weight": "1.0", + "id": "1113", "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", + "weight": "16.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "27", - "weight": "50.0", + "weight": "5.0", "id": "995", "maxAmount": "27" }, { "minAmount": "97", - "weight": "50.0", + "weight": "5.0", "id": "995", "maxAmount": "97" }, { - "minAmount": "1", - "weight": "50.0", - "id": "9016", - "maxAmount": "1" + "minAmount": "104", + "weight": "29.0", + "id": "995", + "maxAmount": "104" + }, + { + "minAmount": "132", + "weight": "25.0", + "id": "995", + "maxAmount": "132" + }, + { + "minAmount": "286", + "weight": "10.0", + "id": "995", + "maxAmount": "286" + }, + { + "minAmount": "475", + "weight": "8.0", + "id": "995", + "maxAmount": "475" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "5625", + "maxAmount": "4" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "2570", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "2737", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", + "id": "14426", "maxAmount": "1" } ] @@ -44827,20 +39743,39 @@ { "minAmount": "1", "weight": "5.0", - "id": "2684", + "id": "1", "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", - "id": "2731", + "id": "1", "maxAmount": "1" } ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "532", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "9080", + "maxAmount": "1" + } + ], "charm": [ + { + "minAmount": "1", + "weight": "64.8102", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "14.0533", @@ -44864,12 +39799,6 @@ "weight": "7.3696", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "64.8102", - "id": "0", - "maxAmount": "1" } ], "ids": "4527,4528,4529,4530,4531,4532,4533", @@ -44907,8 +39836,8 @@ }, { "minAmount": "1", - "weight": "25.0", - "id": "2727", + "weight": "2.12595", + "id": "12070", "maxAmount": "1" }, { @@ -45142,33 +40071,33 @@ "charm": [ { "minAmount": "1", - "weight": "100.0", + "weight": "92.3056", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.118", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "0.9651", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "3.0295", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "1.5818", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ], "ids": "4805,4806,4807,4808,4813,4814,4815,4816,4817,4818,4819,4820,4821,4822,4823,4824,4825,4826,4827,4828,4829,4830,4831,4832,4833,4834,4835,4836,4837,4838,4839,4840,4841,4842,4843,4844,4849,4850,4851,4852,7606,7607,7608,7609,7614,7615,7616,7617,7618,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628,7629,7630,7631,7632,7633,7634,7635,7682,7683,7684,7685,7691,7692,7693,7694,7695,7696,7697,7698,7699,7700,7701,7702,7703,7704,7705,7706", @@ -45522,33 +40451,33 @@ "charm": [ { "minAmount": "1", - "weight": "100.0", + "weight": "68.3486", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.1284", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "5.0459", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "22.0183", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "0.4587", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ], "ids": "5251,5252,5255,5256", @@ -45734,6 +40663,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "4.9106", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.3881", @@ -45757,441 +40692,237 @@ "weight": "2.0297", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "4.9106", - "id": "0", - "maxAmount": "1" } ], "ids": "5361", "description": "", "main": [ - { - "minAmount": "90", - "weight": "50.0", - "id": "555", - "maxAmount": "90" - }, - { - "minAmount": "4", - "weight": "50.0", - "id": "565", - "maxAmount": "12" - }, - { - "minAmount": "9", - "weight": "25.0", - "id": "560", - "maxAmount": "19" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "4695", - "maxAmount": "10" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "4698", - "maxAmount": "9" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "4694", - "maxAmount": "12" - }, { "minAmount": "1", - "weight": "25.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5301", - "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": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5292", - "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": "5323", - "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": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5321", - "maxAmount": "9" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "90", - "weight": "50.0", - "id": "888", - "maxAmount": "90" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1111", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1159", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "9419", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1343", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1147", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "6.0", "id": "1383", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1395", "maxAmount": "1" }, { - "minAmount": "15", - "weight": "25.0", - "id": "9337", - "maxAmount": "15" + "minAmount": "1", + "weight": "2.0", + "id": "1111", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", + "id": "1343", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1159", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "9418", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1147", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "2487", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "1123", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "10" + "minAmount": "90", + "weight": "8.0", + "id": "888", + "maxAmount": "90" }, { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" + "minAmount": "90", + "weight": "6.0", + "id": "555", + "maxAmount": "90" }, { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "2" + "minAmount": "15", + "weight": "5.0", + "id": "9337", + "maxAmount": "15" }, { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" + "minAmount": "9", + "weight": "5.0", + "id": "560", + "maxAmount": "19" }, { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "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": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "445", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "448", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "454", - "maxAmount": "8" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "2359", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1624", - "maxAmount": "4" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "377", + "minAmount": "4", + "weight": "3.0", + "id": "565", "maxAmount": "12" }, { "minAmount": "3", - "weight": "25.0", - "id": "383", - "maxAmount": "6" + "weight": "2.0", + "id": "4695", + "maxAmount": "10" + }, + { + "minAmount": "3", + "weight": "2.0", + "id": "4698", + "maxAmount": "10" + }, + { + "minAmount": "3", + "weight": "1.0", + "id": "4694", + "maxAmount": "10" + }, + { + "minAmount": "2", + "weight": "9.0", + "id": "14424", + "maxAmount": "2" }, { "minAmount": "1", - "weight": "25.0", - "id": "385", + "weight": "4.0", + "id": "14422", "maxAmount": "1" }, { - "minAmount": "97", - "weight": "50.0", + "minAmount": "1", + "weight": "3.0", + "id": "5321", + "maxAmount": "9" + }, + { + "minAmount": "96", + "weight": "10.0", "id": "995", - "maxAmount": "692" + "maxAmount": "696" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "448", + "maxAmount": "5" + }, + { + "minAmount": "3", + "weight": "1.0", + "id": "2354", + "maxAmount": "3" + }, + { + "minAmount": "2", + "weight": "2.0", + "id": "2359", + "maxAmount": "2" }, { "minAmount": "5", - "weight": "50.0", + "weight": "8.0", "id": "572", "maxAmount": "10" }, - { - "minAmount": "20", - "weight": "25.0", - "id": "232", - "maxAmount": "20" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "407", - "maxAmount": "1" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "402", - "maxAmount": "29" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1444", - "maxAmount": "1" - }, { "minAmount": "5", - "weight": "25.0", + "weight": "4.0", "id": "228", "maxAmount": "10" }, { "minAmount": "1", - "weight": "25.0", - "id": "6672", + "weight": "4.0", + "id": "1444", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "3.0", + "id": "377", + "maxAmount": "12" + }, + { + "minAmount": "3", + "weight": "3.0", + "id": "383", + "maxAmount": "6" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "385", + "maxAmount": "1" + }, + { + "minAmount": "20", + "weight": "2.0", + "id": "232", + "maxAmount": "20" + }, + { + "minAmount": "10", + "weight": "2.0", + "id": "402", + "maxAmount": "30" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "407", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "193", - "maxAmount": "1" - }, - { - "minAmount": "11", - "weight": "5.0", - "id": "2", - "maxAmount": "27" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6691", + "weight": "2.0", + "id": "6667", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "2737", + "weight": "2.0", + "id": "1624", + "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "1.0", + "id": "5295", "maxAmount": "1" } ] @@ -46212,6 +40943,12 @@ } ], "charm": [ + { + "minAmount": "2", + "weight": "63.6081", + "id": "0", + "maxAmount": "2" + }, { "minAmount": "2", "weight": "14.93", @@ -46235,12 +40972,6 @@ "weight": "1.3997", "id": "12163", "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "63.6081", - "id": "0", - "maxAmount": "2" } ], "ids": "5362", @@ -46465,7 +41196,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2725", + "id": "12070", "maxAmount": "1" }, { @@ -46498,6 +41229,12 @@ } ], "charm": [ + { + "minAmount": "4", + "weight": "9.0422", + "id": "0", + "maxAmount": "4" + }, { "minAmount": "4", "weight": "19.2257", @@ -46521,12 +41258,6 @@ "weight": "3.5993", "id": "12163", "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "9.0422", - "id": "0", - "maxAmount": "4" } ], "ids": "5363", @@ -46672,13 +41403,13 @@ }, { "minAmount": "1", - "weight": "0.0768", + "weight": "0.07570993", "id": "11286", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.025", + "weight": "0.0236593552", "id": "11335", "maxAmount": "1" }, @@ -46700,6 +41431,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "85.7316", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.7699", @@ -46723,12 +41460,6 @@ "weight": "1.0494", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "85.7316", - "id": "0", - "maxAmount": "1" } ], "ids": "5414", @@ -46957,7 +41688,7 @@ { "minAmount": "1", "weight": "25.0", - "id": "2725", + "id": "1", "maxAmount": "1" }, { @@ -47002,6 +41733,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "71.375", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "9.5708", @@ -47025,12 +41762,6 @@ "weight": "3.2638", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "71.375", - "id": "0", - "maxAmount": "1" } ], "ids": "5417,5418", @@ -47201,7 +41932,7 @@ { "minAmount": "1", "weight": "25.0", - "id": "2727", + "id": "12070", "maxAmount": "1" }, { @@ -48319,7 +43050,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2677", + "id": "5733", "maxAmount": "1" } ] @@ -48485,8 +43216,8 @@ "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "2677", + "weight": "1.0", + "id": "0", "maxAmount": "1" } ] @@ -48500,7 +43231,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2677", + "id": "0", "maxAmount": "1" } ] @@ -48617,7 +43348,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2725", + "id": "1", "maxAmount": "1" }, { @@ -48673,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": "2733", - "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": [], @@ -49249,7 +43706,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2851", + "id": "5733", "maxAmount": "1" }, { @@ -49498,7 +43955,7 @@ { "minAmount": "1", "weight": "25.0", - "id": "2809", + "id": "5733", "maxAmount": "1" }, { @@ -49650,7 +44107,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2821", + "id": "5733", "maxAmount": "1" }, { @@ -49809,7 +44266,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2839", + "id": "5733", "maxAmount": "1" }, { @@ -50082,7 +44539,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2841", + "id": "5733", "maxAmount": "1" }, { @@ -50109,6 +44566,12 @@ } ], "charm": [ + { + "minAmount": "4", + "weight": "91.1171", + "id": "0", + "maxAmount": "4" + }, { "minAmount": "4", "weight": "3.0405", @@ -50132,80 +44595,74 @@ "weight": "4.5645", "id": "12163", "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "91.1171", - "id": "0", - "maxAmount": "4" } ], "ids": "6203", - "description": "", + "description": "K'ril Tsutsaroth (Zamorak GWD Boss)", "main": [ { "minAmount": "1", - "weight": "1.45", + "weight": "8.112", "id": "11716", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.85", + "weight": "1.92128", "id": "11708", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.7", + "weight": "1.352", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.7", + "weight": "1.352", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.7", + "weight": "1.352", "id": "11714", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.45", + "weight": "8.0", "id": "11736", "maxAmount": "1" }, { "minAmount": "1", - "weight": "3.3", + "weight": "25.0", "id": "5698", "maxAmount": "1" }, { "minAmount": "295", - "weight": "25.0", + "weight": "100.0", "id": "5626", "maxAmount": "300" }, { "minAmount": "1", - "weight": "25.0", + "weight": "100.0", "id": "1333", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "50.0", "id": "1123", "maxAmount": "1" }, { "minAmount": "1", - "weight": "15.0", + "weight": "100.0", "id": "1079", "maxAmount": "1" }, @@ -50243,7 +44700,7 @@ "minAmount": "80", "weight": "25.0", "id": "565", - "maxAmount": "90" + "maxAmount": "85" }, { "minAmount": "10", @@ -50258,26 +44715,20 @@ "maxAmount": "3" }, { - "minAmount": "19500", - "weight": "37.0", + "minAmount": "19000", + "weight": "100.0", "id": "995", - "maxAmount": "20000" + "maxAmount": "21000" }, { "minAmount": "1", - "weight": "55.6", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "20.0", + "weight": "25.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "20.0", + "weight": "8.0", "id": "12070", "maxAmount": "1" } @@ -50296,100 +44747,94 @@ "ids": "6204", "description": "", "main": [ - { - "minAmount": "98", - "weight": "250.0", - "id": "808", - "maxAmount": "98" - }, { "minAmount": "1", - "weight": "1.0", - "id": "11736", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11716", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11714", "maxAmount": "1" }, { - "minAmount": "1", + "minAmount": "1300", "weight": "500.0", "id": "995", - "maxAmount": "1" + "maxAmount": "1500" }, { - "minAmount": "7", - "weight": "250.0", - "id": "246", - "maxAmount": "7" - }, - { - "minAmount": "98", - "weight": "250.0", + "minAmount": "95", + "weight": "500.0", "id": "886", - "maxAmount": "98" + "maxAmount": "100" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" + }, + { + "minAmount": "5", + "weight": "500.0", + "id": "560", + "maxAmount": "10" + }, + { + "minAmount": "5", + "weight": "500.0", + "id": "565", + "maxAmount": "10" }, { "minAmount": "3", - "weight": "250.0", + "weight": "500.0", "id": "385", "maxAmount": "3" }, { "minAmount": "2", - "weight": "250.0", + "weight": "500.0", "id": "7060", "maxAmount": "2" }, { - "minAmount": "1", - "weight": "250.0", - "id": "157", - "maxAmount": "1" + "minAmount": "10", + "weight": "500.0", + "id": "246", + "maxAmount": "10" }, { "minAmount": "1", - "weight": "250.0", + "weight": "500.0", "id": "145", "maxAmount": "1" }, { "minAmount": "1", - "weight": "250.0", - "id": "189", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "250.0", - "id": "2747", + "weight": "500.0", + "id": "157", "maxAmount": "1" }, { "minAmount": "1", "weight": "50.0", - "id": "31", + "id": "12070", "maxAmount": "1" } ] @@ -50407,39 +44852,27 @@ "ids": "6206", "description": "", "main": [ - { - "minAmount": "96", - "weight": "250.0", - "id": "808", - "maxAmount": "100" - }, { "minAmount": "1", - "weight": "1.0", - "id": "11736", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11716", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11714", "maxAmount": "1" }, @@ -50451,56 +44884,62 @@ }, { "minAmount": "95", - "weight": "250.0", + "weight": "500.0", "id": "886", "maxAmount": "100" }, { - "minAmount": "5", - "weight": "250.0", - "id": "565", - "maxAmount": "10" + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" }, { "minAmount": "5", - "weight": "250.0", + "weight": "500.0", "id": "560", "maxAmount": "10" }, + { + "minAmount": "5", + "weight": "500.0", + "id": "565", + "maxAmount": "10" + }, + { + "minAmount": "3", + "weight": "500.0", + "id": "385", + "maxAmount": "3" + }, { "minAmount": "2", - "weight": "250.0", + "weight": "500.0", "id": "7060", "maxAmount": "2" }, { - "minAmount": "1", - "weight": "250.0", - "id": "157", - "maxAmount": "1" - }, - { - "minAmount": "5", - "weight": "250.0", + "minAmount": "10", + "weight": "500.0", "id": "246", "maxAmount": "10" }, { - "minAmount": "2", - "weight": "250.0", - "id": "385", - "maxAmount": "2" + "minAmount": "1", + "weight": "500.0", + "id": "145", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "2747", + "weight": "500.0", + "id": "157", "maxAmount": "1" }, { "minAmount": "1", "weight": "50.0", - "id": "31", + "id": "12070", "maxAmount": "1" } ] @@ -50518,82 +44957,94 @@ "ids": "6208", "description": "", "main": [ - { - "minAmount": "98", - "weight": "250.0", - "id": "808", - "maxAmount": "98" - }, { "minAmount": "1", - "weight": "1.0", - "id": "11736", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11716", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.37566", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.37566", "id": "11714", "maxAmount": "1" }, { - "minAmount": "3", - "weight": "250.0", - "id": "246", + "minAmount": "1300", + "weight": "500.0", + "id": "995", + "maxAmount": "1500" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "886", + "maxAmount": "100" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" + }, + { + "minAmount": "5", + "weight": "500.0", + "id": "560", + "maxAmount": "10" + }, + { + "minAmount": "5", + "weight": "500.0", + "id": "565", "maxAmount": "10" }, { "minAmount": "3", - "weight": "250.0", + "weight": "500.0", "id": "385", "maxAmount": "3" }, { "minAmount": "2", - "weight": "250.0", + "weight": "500.0", "id": "7060", "maxAmount": "2" }, { - "minAmount": "98", - "weight": "250.0", - "id": "886", - "maxAmount": "100" + "minAmount": "10", + "weight": "500.0", + "id": "246", + "maxAmount": "10" }, { "minAmount": "1", - "weight": "250.0", - "id": "157", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "250.0", + "weight": "500.0", "id": "145", "maxAmount": "1" }, { "minAmount": "1", - "weight": "250.0", - "id": "2731", + "weight": "500.0", + "id": "157", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "50.0", + "id": "12070", "maxAmount": "1" } ] @@ -50727,7 +45178,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2747", + "id": "12070", "maxAmount": "1" }, { @@ -50843,7 +45294,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2729", + "id": "12070", "maxAmount": "1" } ] @@ -50983,7 +45434,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2727", + "id": "12070", "maxAmount": "1" }, { @@ -51010,6 +45461,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "36.6834", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "25.6873", @@ -51033,16 +45490,10 @@ "weight": "8.2767", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "36.6834", - "id": "0", - "maxAmount": "1" } ], "ids": "6222", - "description": "", + "description": "Kree'arra (Armadyl GWD Boss)", "main": [ { "minAmount": "1", @@ -51052,25 +45503,25 @@ }, { "minAmount": "1", - "weight": "50.0", + "weight": "100.0", "id": "9185", "maxAmount": "1" }, { "minAmount": "15", - "weight": "50.0", + "weight": "100.0", "id": "9144", "maxAmount": "25" }, { "minAmount": "80", - "weight": "25.0", + "weight": "50.0", "id": "892", "maxAmount": "100" }, { "minAmount": "1", - "weight": "50.0", + "weight": "100.0", "id": "9244", "maxAmount": "15" }, @@ -51088,19 +45539,19 @@ }, { "minAmount": "1", - "weight": "1.96", + "weight": "2.98342", "id": "11718", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.96", + "weight": "2.98342", "id": "11720", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.96", + "weight": "2.98342", "id": "11722", "maxAmount": "1" }, @@ -51118,155 +45569,138 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "5315", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.17", + "weight": "2.11985", "id": "11702", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.932", + "weight": "1.49174", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.932", + "weight": "1.49174", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.932", + "weight": "1.49174", "id": "11714", "maxAmount": "1" }, { "minAmount": "3", - "weight": "25.0", + "weight": "50.0", "id": "169", "maxAmount": "3" }, { "minAmount": "3", - "weight": "25.0", + "weight": "50.0", "id": "163", "maxAmount": "3" }, { "minAmount": "5", - "weight": "5.0", + "weight": "25.0", "id": "218", - "maxAmount": "20" + "maxAmount": "25" }, { "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "989", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "10977", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "25.0", - "id": "385", - "maxAmount": "20" - }, { "minAmount": "18000", - "weight": "50.0", + "weight": "100.0", "id": "995", "maxAmount": "20000" }, { "minAmount": "1", - "weight": "20.0", + "weight": "8.0", "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "75.0", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "20.0", + "weight": "25.0", "id": "31", "maxAmount": "1" } ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], "charm": [], "ids": "6223", "description": "", "main": [ { "minAmount": "1", - "weight": "1.0", + "weight": "0.902405", + "id": "11718", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.902405", "id": "11720", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", - "id": "11718", + "weight": "0.902405", + "id": "11722", "maxAmount": "1" }, { - "minAmount": "95", - "weight": "500.0", - "id": "886", - "maxAmount": "100" + "minAmount": "1", + "weight": "2.70721", + "id": "11710", + "maxAmount": "1" }, { - "minAmount": "15", - "weight": "250.0", - "id": "4697", - "maxAmount": "25" + "minAmount": "1", + "weight": "2.70721", + "id": "11712", + "maxAmount": "1" }, { - "minAmount": "2", - "weight": "250.0", - "id": "391", - "maxAmount": "2" - }, - { - "minAmount": "3", - "weight": "250.0", - "id": "7058", - "maxAmount": "3" - }, - { - "minAmount": "3", - "weight": "250.0", - "id": "7056", - "maxAmount": "3" - }, - { - "minAmount": "95", - "weight": "500.0", - "id": "808", - "maxAmount": "100" + "minAmount": "1", + "weight": "2.70721", + "id": "11714", + "maxAmount": "1" }, { "minAmount": "1300", @@ -51274,85 +45708,104 @@ "id": "995", "maxAmount": "1500" }, + { + "minAmount": "95", + "weight": "500.0", + "id": "886", + "maxAmount": "100" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" + }, + { + "minAmount": "15", + "weight": "500.0", + "id": "4697", + "maxAmount": "25" + }, { "minAmount": "2", - "weight": "250.0", - "id": "6693", + "weight": "500.0", + "id": "391", + "maxAmount": "2" + }, + { + "minAmount": "3", + "weight": "500.0", + "id": "7058", + "maxAmount": "3" + }, + { + "minAmount": "2", + "weight": "500.0", + "id": "6694", "maxAmount": "2" }, { "minAmount": "1", - "weight": "250.0", - "id": "213", + "weight": "500.0", + "id": "214", "maxAmount": "1" }, { "minAmount": "1", "weight": "50.0", - "id": "2733", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "11722", + "id": "12070", "maxAmount": "1" } ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], "charm": [], "ids": "6225", "description": "", "main": [ { "minAmount": "1", - "weight": "1.0", + "weight": "0.902405", + "id": "11718", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.902405", "id": "11720", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", - "id": "11718", + "weight": "0.902405", + "id": "11722", "maxAmount": "1" }, { - "minAmount": "3", - "weight": "250.0", - "id": "7056", - "maxAmount": "3" + "minAmount": "1", + "weight": "2.70721", + "id": "11710", + "maxAmount": "1" }, { - "minAmount": "2", - "weight": "250.0", - "id": "391", - "maxAmount": "2" + "minAmount": "1", + "weight": "2.70721", + "id": "11712", + "maxAmount": "1" }, { - "minAmount": "3", - "weight": "250.0", - "id": "7058", - "maxAmount": "3" - }, - { - "minAmount": "13", - "weight": "500.0", - "id": "4697", - "maxAmount": "25" - }, - { - "minAmount": "95", - "weight": "500.0", - "id": "808", - "maxAmount": "100" - }, - { - "minAmount": "95", - "weight": "500.0", - "id": "886", - "maxAmount": "100" + "minAmount": "1", + "weight": "2.70721", + "id": "11714", + "maxAmount": "1" }, { "minAmount": "1300", @@ -51360,46 +45813,52 @@ "id": "995", "maxAmount": "1500" }, + { + "minAmount": "95", + "weight": "500.0", + "id": "886", + "maxAmount": "100" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" + }, + { + "minAmount": "15", + "weight": "500.0", + "id": "4697", + "maxAmount": "25" + }, { "minAmount": "2", "weight": "500.0", - "id": "6693", + "id": "391", + "maxAmount": "2" + }, + { + "minAmount": "3", + "weight": "500.0", + "id": "7058", + "maxAmount": "3" + }, + { + "minAmount": "2", + "weight": "500.0", + "id": "6694", "maxAmount": "2" }, { "minAmount": "1", "weight": "500.0", - "id": "213", + "id": "214", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", - "id": "11710", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "11712", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "11714", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2727", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "11722", + "weight": "50.0", + "id": "12070", "maxAmount": "1" } ] @@ -51417,53 +45876,95 @@ "ids": "6227", "description": "", "main": [ - { - "minAmount": "2", - "weight": "250.0", - "id": "391", - "maxAmount": "2" - }, - { - "minAmount": "3", - "weight": "250.0", - "id": "7058", - "maxAmount": "3" - }, - { - "minAmount": "1006", - "weight": "500.0", - "id": "995", - "maxAmount": "1500" - }, - { - "minAmount": "2", - "weight": "250.0", - "id": "6693", - "maxAmount": "2" - }, { "minAmount": "1", - "weight": "250.0", - "id": "2725", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "11720", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", + "weight": "0.902405", "id": "11718", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "0.902405", + "id": "11720", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.902405", "id": "11722", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.70721", + "id": "11710", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.70721", + "id": "11712", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.70721", + "id": "11714", + "maxAmount": "1" + }, + { + "minAmount": "1300", + "weight": "500.0", + "id": "995", + "maxAmount": "1500" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "886", + "maxAmount": "100" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" + }, + { + "minAmount": "15", + "weight": "500.0", + "id": "4697", + "maxAmount": "25" + }, + { + "minAmount": "2", + "weight": "500.0", + "id": "391", + "maxAmount": "2" + }, + { + "minAmount": "3", + "weight": "500.0", + "id": "7058", + "maxAmount": "3" + }, + { + "minAmount": "2", + "weight": "500.0", + "id": "6694", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "214", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "50.0", + "id": "12070", + "maxAmount": "1" } ] }, @@ -51486,166 +45987,118 @@ "ids": "6232,6233,6234,6235,6236,6237,6238,6239,6240,6241,6242,6243,6244,6245,6246", "description": "", "main": [ + { + "minAmount": "1", + "weight": "10.0", + "id": "1229", + "maxAmount": "1" + }, { "minAmount": "15", - "weight": "50.0", + "weight": "18.0", "id": "556", - "maxAmount": "30" + "maxAmount": "15" }, { "minAmount": "30", - "weight": "50.0", + "weight": "13.0", "id": "555", "maxAmount": "30" }, { "minAmount": "2", - "weight": "25.0", + "weight": "4.0", "id": "563", "maxAmount": "2" }, { "minAmount": "9", - "weight": "25.0", + "weight": "4.0", "id": "561", "maxAmount": "9" }, - { - "minAmount": "5", - "weight": "25.0", - "id": "558", - "maxAmount": "5" - }, - { - "minAmount": "11", - "weight": "25.0", - "id": "565", - "maxAmount": "11" - }, { "minAmount": "3", - "weight": "25.0", + "weight": "3.0", "id": "562", "maxAmount": "3" }, - { - "minAmount": "16", - "weight": "25.0", - "id": "562", - "maxAmount": "16" - }, { "minAmount": "12", - "weight": "25.0", + "weight": "2.0", "id": "559", "maxAmount": "12" }, + { + "minAmount": "11", + "weight": "2.0", + "id": "565", + "maxAmount": "11" + }, + { + "minAmount": "5", + "weight": "1.0", + "id": "558", + "maxAmount": "5" + }, + { + "minAmount": "16", + "weight": "1.0", + "id": "562", + "maxAmount": "16" + }, { "minAmount": "1", - "weight": "50.0", - "id": "442", + "weight": "15.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "4", - "weight": "50.0", + "weight": "30.0", "id": "2362", "maxAmount": "4" }, { "minAmount": "1", - "weight": "5.0", + "weight": "10.0", + "id": "442", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "9431", "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": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "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": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "209", - "maxAmount": "1" - }, { "minAmount": "5", - "weight": "25.0", - "id": "373", - "maxAmount": "5" - }, - { - "minAmount": "5", - "weight": "25.0", + "weight": "5.0", "id": "175", "maxAmount": "5" }, + { + "minAmount": "5", + "weight": "3.0", + "id": "0", + "maxAmount": "5" + }, + { + "minAmount": "5", + "weight": "2.0", + "id": "373", + "maxAmount": "5" + }, { "minAmount": "1", - "weight": "25.0", - "id": "2729", + "weight": "3.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "1.0", + "id": "12070", "maxAmount": "1" } ] @@ -51660,6 +46113,12 @@ } ], "charm": [ + { + "minAmount": "4", + "weight": "82.502", + "id": "0", + "maxAmount": "4" + }, { "minAmount": "4", "weight": "4.2237", @@ -51683,153 +46142,135 @@ "weight": "2.4638", "id": "12163", "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "82.502", - "id": "0", - "maxAmount": "4" } ], "ids": "6247", - "description": "", + "description": "Commander Zilyana (Saradomin GWD Boss)", "main": [ { "minAmount": "1", - "weight": "0.7", + "weight": "1.92128", "id": "11706", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.55", + "weight": "1.352", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.55", + "weight": "1.352", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.55", + "weight": "1.352", "id": "11714", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.21", + "weight": "8.112", "id": "11730", "maxAmount": "1" }, { "minAmount": "35", - "weight": "20.0", + "weight": "50.0", "id": "811", "maxAmount": "40" }, { "minAmount": "1", - "weight": "20.0", + "weight": "100.0", "id": "1093", "maxAmount": "1" }, { "minAmount": "1", - "weight": "20.0", + "weight": "100.0", "id": "1201", "maxAmount": "1" }, { "minAmount": "1", - "weight": "20.0", + "weight": "50.0", "id": "1123", "maxAmount": "1" }, { "minAmount": "95", - "weight": "20.0", + "weight": "50.0", "id": "563", "maxAmount": "100" }, { "minAmount": "3", - "weight": "20.0", - "id": "3026", + "weight": "25.0", + "id": "3024", "maxAmount": "3" }, { "minAmount": "3", - "weight": "20.0", + "weight": "25.0", "id": "163", "maxAmount": "3" }, { "minAmount": "3", - "weight": "20.0", + "weight": "25.0", "id": "3042", "maxAmount": "3" }, { "minAmount": "3", - "weight": "10.0", + "weight": "25.0", "id": "6687", "maxAmount": "3" }, - { - "minAmount": "3", - "weight": "10.0", - "id": "3024", - "maxAmount": "3" - }, { "minAmount": "5", - "weight": "20.0", + "weight": "50.0", "id": "208", "maxAmount": "5" }, { "minAmount": "2", - "weight": "20.0", + "weight": "50.0", "id": "5295", "maxAmount": "2" }, { "minAmount": "1", - "weight": "2.0", + "weight": "8.0", "id": "5316", "maxAmount": "1" }, - { - "minAmount": "6", - "weight": "20.0", - "id": "1602", - "maxAmount": "6" - }, { "minAmount": "19000", - "weight": "30.0", + "weight": "100.0", "id": "995", - "maxAmount": "20000" + "maxAmount": "21000" }, { "minAmount": "1", - "weight": "20.0", + "weight": "25.0", "id": "31", "maxAmount": "1" }, { "minAmount": "1", - "weight": "44.0", - "id": "0", + "weight": "8.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "11.8", - "id": "12070", + "weight": "25.0", + "id": "2434", "maxAmount": "1" } ] @@ -51847,51 +46288,27 @@ "ids": "6248", "description": "", "main": [ - { - "minAmount": "95", - "weight": "500.0", - "id": "808", - "maxAmount": "100" - }, { "minAmount": "1", - "weight": "5.0", + "weight": "3.04143", "id": "11730", "maxAmount": "1" }, - { - "minAmount": "7", - "weight": "500.0", - "id": "563", - "maxAmount": "10" - }, - { - "minAmount": "97", - "weight": "500.0", - "id": "886", - "maxAmount": "100" - }, { "minAmount": "1", - "weight": "500.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11714", "maxAmount": "1" }, @@ -51899,30 +46316,60 @@ "minAmount": "1300", "weight": "500.0", "id": "995", - "maxAmount": "2000" + "maxAmount": "1500" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "886", + "maxAmount": "100" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" }, { "minAmount": "5", "weight": "500.0", - "id": "231", - "maxAmount": "5" - }, - { - "minAmount": "6", - "weight": "250.0", - "id": "237", - "maxAmount": "6" + "id": "563", + "maxAmount": "10" }, { "minAmount": "3", - "weight": "250.0", + "weight": "500.0", "id": "7946", "maxAmount": "3" }, { "minAmount": "1", - "weight": "250.0", - "id": "2745", + "weight": "500.0", + "id": "7218", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "208", + "maxAmount": "1" + }, + { + "minAmount": "6", + "weight": "500.0", + "id": "238", + "maxAmount": "6" + }, + { + "minAmount": "5", + "weight": "500.0", + "id": "232", + "maxAmount": "5" + }, + { + "minAmount": "1", + "weight": "50.0", + "id": "12070", "maxAmount": "1" } ] @@ -51940,45 +46387,27 @@ "ids": "6250", "description": "", "main": [ - { - "minAmount": "95", - "weight": "500.0", - "id": "808", - "maxAmount": "100" - }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11730", "maxAmount": "1" }, - { - "minAmount": "95", - "weight": "500.0", - "id": "886", - "maxAmount": "100" - }, { "minAmount": "1", - "weight": "250.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11714", "maxAmount": "1" }, @@ -51989,21 +46418,57 @@ "maxAmount": "1500" }, { - "minAmount": "4", - "weight": "250.0", - "id": "231", - "maxAmount": "4" + "minAmount": "1", + "weight": "500.0", + "id": "886", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "250.0", + "weight": "500.0", + "id": "808", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "563", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "7946", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", "id": "7218", "maxAmount": "1" }, + { + "minAmount": "1", + "weight": "500.0", + "id": "208", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "238", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "232", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "50.0", - "id": "2727", + "id": "12070", "maxAmount": "1" } ] @@ -52023,25 +46488,25 @@ "main": [ { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11730", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.04143", "id": "11714", "maxAmount": "1" }, @@ -52053,32 +46518,56 @@ }, { "minAmount": "1", - "weight": "250.0", - "id": "592", + "weight": "500.0", + "id": "886", "maxAmount": "1" }, { - "minAmount": "6", - "weight": "250.0", - "id": "237", - "maxAmount": "6" - }, - { - "minAmount": "5", - "weight": "250.0", - "id": "231", - "maxAmount": "5" + "minAmount": "1", + "weight": "500.0", + "id": "808", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "250.0", + "weight": "500.0", + "id": "563", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "7946", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", "id": "7218", "maxAmount": "1" }, { "minAmount": "1", - "weight": "250.0", - "id": "2735", + "weight": "500.0", + "id": "208", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "238", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "232", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "50.0", + "id": "12070", "maxAmount": "1" } ] @@ -52090,18 +46579,25 @@ "weight": "100.0", "id": "526", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "2747", - "maxAmount": "1" } ], "charm": [], "ids": "6254", "description": "", - "main": [] + "main": [ + { + "minAmount": "1", + "weight": "127.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "5733", + "maxAmount": "1" + } + ] }, { "default": [ @@ -52113,6 +46609,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "67.5", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "26.25", @@ -52136,12 +46638,6 @@ "weight": "6.25", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "67.5", - "id": "0", - "maxAmount": "1" } ], "ids": "6258,6259", @@ -52150,7 +46646,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2747", + "id": "5733", "maxAmount": "1" }, { @@ -52171,6 +46667,12 @@ } ], "charm": [ + { + "minAmount": "4", + "weight": "85.5895", + "id": "0", + "maxAmount": "4" + }, { "minAmount": "4", "weight": "4.7851", @@ -52194,73 +46696,61 @@ "weight": "2.5055", "id": "12163", "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "85.5895", - "id": "0", - "maxAmount": "4" } ], "ids": "6260", - "description": "", + "description": "General Graardor (Bandos GWD Boss)", "main": [ { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", "id": "1289", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "50.0", "id": "1303", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1373", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "100.0", "id": "1319", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "100.0", "id": "1127", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "50.0", "id": "1275", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.1", + "weight": "2.5756", "id": "11724", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.1", + "weight": "2.5756", "id": "11726", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.1", + "weight": "2.5756", "id": "11728", "maxAmount": "1" }, { - "minAmount": "40", + "minAmount": "65", "weight": "50.0", "id": "561", "maxAmount": "70" @@ -52278,8 +46768,8 @@ "maxAmount": "20" }, { - "minAmount": "3000", - "weight": "50.0", + "minAmount": "19000", + "weight": "100.0", "id": "995", "maxAmount": "21000" }, @@ -52321,32 +46811,32 @@ }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.2878", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.2878", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.2878", "id": "11714", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.25", + "weight": "1.83004", "id": "11704", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "2741", + "weight": "8.0", + "id": "12070", "maxAmount": "1" }, { @@ -52354,12 +46844,6 @@ "weight": "25.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "75.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -52376,48 +46860,60 @@ "ids": "6261", "description": "", "main": [ - { - "minAmount": "95", - "weight": "500.0", - "id": "808", - "maxAmount": "100" - }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11724", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11726", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11728", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11714", "maxAmount": "1" }, + { + "minAmount": "1300", + "weight": "500.0", + "id": "995", + "maxAmount": "1500" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "886", + "maxAmount": "100" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" + }, { "minAmount": "25", "weight": "500.0", @@ -52428,19 +46924,25 @@ "minAmount": "20", "weight": "500.0", "id": "561", - "maxAmount": "28" + "maxAmount": "25" }, { - "minAmount": "95", + "minAmount": "5", "weight": "500.0", - "id": "886", - "maxAmount": "100" + "id": "226", + "maxAmount": "5" }, { - "minAmount": "1300", + "minAmount": "2", "weight": "500.0", - "id": "995", - "maxAmount": "1450" + "id": "385", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "7054", + "maxAmount": "3" }, { "minAmount": "1", @@ -52451,25 +46953,19 @@ { "minAmount": "1", "weight": "500.0", - "id": "1025", + "id": "157", "maxAmount": "1" }, { - "minAmount": "2", + "minAmount": "1", "weight": "500.0", - "id": "385", - "maxAmount": "2" - }, - { - "minAmount": "3", - "weight": "500.0", - "id": "7054", - "maxAmount": "3" + "id": "1971", + "maxAmount": "1" }, { "minAmount": "1", "weight": "50.0", - "id": "2735", + "id": "12070", "maxAmount": "1" } ] @@ -52487,90 +46983,90 @@ "ids": "6263", "description": "", "main": [ - { - "minAmount": "95", - "weight": "500.0", - "id": "808", - "maxAmount": "100" - }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11724", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11726", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11728", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11714", "maxAmount": "1" }, - { - "minAmount": "25", - "weight": "500.0", - "id": "564", - "maxAmount": "30" - }, - { - "minAmount": "95", - "weight": "500.0", - "id": "886", - "maxAmount": "100" - }, { "minAmount": "1300", "weight": "500.0", "id": "995", "maxAmount": "1500" }, + { + "minAmount": "95", + "weight": "500.0", + "id": "886", + "maxAmount": "100" + }, + { + "minAmount": "95", + "weight": "500.0", + "id": "808", + "maxAmount": "100" + }, + { + "minAmount": "25", + "weight": "500.0", + "id": "564", + "maxAmount": "30" + }, + { + "minAmount": "25", + "weight": "500.0", + "id": "561", + "maxAmount": "30" + }, { "minAmount": "5", "weight": "500.0", - "id": "225", + "id": "226", "maxAmount": "5" }, - { - "minAmount": "1", - "weight": "500.0", - "id": "1917", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "500.0", - "id": "1971", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "500.0", "id": "385", "maxAmount": "2" }, + { + "minAmount": "3", + "weight": "500.0", + "id": "7054", + "maxAmount": "3" + }, { "minAmount": "1", "weight": "500.0", @@ -52580,19 +47076,19 @@ { "minAmount": "1", "weight": "500.0", - "id": "1025", + "id": "157", "maxAmount": "1" }, { - "minAmount": "3", + "minAmount": "1", "weight": "500.0", - "id": "7054", - "maxAmount": "3" + "id": "1917", + "maxAmount": "1" }, { "minAmount": "1", "weight": "50.0", - "id": "2733", + "id": "12070", "maxAmount": "1" } ] @@ -52610,66 +47106,42 @@ "ids": "6265", "description": "", "main": [ - { - "minAmount": "95", - "weight": "500.0", - "id": "808", - "maxAmount": "100" - }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11724", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11726", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "1.23663", "id": "11728", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11710", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11712", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "3.70988", "id": "11714", "maxAmount": "1" }, - { - "minAmount": "25", - "weight": "500.0", - "id": "564", - "maxAmount": "30" - }, - { - "minAmount": "15", - "weight": "500.0", - "id": "561", - "maxAmount": "20" - }, - { - "minAmount": "95", - "weight": "500.0", - "id": "886", - "maxAmount": "100" - }, { "minAmount": "1300", "weight": "500.0", @@ -52677,16 +47149,34 @@ "maxAmount": "1500" }, { - "minAmount": "1", + "minAmount": "95", "weight": "500.0", - "id": "9741", - "maxAmount": "1" + "id": "886", + "maxAmount": "100" }, { - "minAmount": "1", + "minAmount": "95", "weight": "500.0", - "id": "1025", - "maxAmount": "1" + "id": "808", + "maxAmount": "100" + }, + { + "minAmount": "25", + "weight": "500.0", + "id": "564", + "maxAmount": "30" + }, + { + "minAmount": "20", + "weight": "500.0", + "id": "561", + "maxAmount": "25" + }, + { + "minAmount": "5", + "weight": "500.0", + "id": "226", + "maxAmount": "5" }, { "minAmount": "2", @@ -52700,10 +47190,28 @@ "id": "7054", "maxAmount": "3" }, + { + "minAmount": "1", + "weight": "500.0", + "id": "9741", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "157", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "500.0", + "id": "1025", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "50.0", - "id": "2741", + "id": "12070", "maxAmount": "1" } ] @@ -52895,6 +47403,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "80.3408", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "8.756", @@ -53079,6 +47593,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "76.3636", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "17.931", @@ -53102,12 +47622,6 @@ "weight": "0.6897", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "76.3636", - "id": "0", - "maxAmount": "1" } ], "ids": "6363,6364", @@ -53115,7 +47629,7 @@ "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "1213", "maxAmount": "1" }, @@ -53139,13 +47653,13 @@ }, { "minAmount": "1", - "weight": "25.0", + "weight": "5.0", "id": "1333", "maxAmount": "1" }, { "minAmount": "100", - "weight": "50.0", + "weight": "100.0", "id": "995", "maxAmount": "100" }, @@ -53173,6 +47687,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "78.8", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "1.3333", @@ -53196,12 +47716,6 @@ "weight": "13.3333", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "78.8", - "id": "0", - "maxAmount": "1" } ], "ids": "6365,6366", @@ -53338,7 +47852,7 @@ { "minAmount": "1", "weight": "25.0", - "id": "2747", + "id": "5733", "maxAmount": "1" }, { @@ -53556,7 +48070,7 @@ { "minAmount": "1", "weight": "25.0", - "id": "2823", + "id": "5733", "maxAmount": "1" }, { @@ -53570,6 +48084,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "82.6923", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "4.3269", @@ -53593,12 +48113,6 @@ "weight": "0.4808", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "82.6923", - "id": "0", - "maxAmount": "1" } ], "ids": "6380", @@ -53847,7 +48361,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2817", + "id": "5733", "maxAmount": "1" }, { @@ -54005,7 +48519,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2815", + "id": "5733", "maxAmount": "1" }, { @@ -54624,10 +49138,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6604,6635,6655,6666,6677,6697,6703,6715", - "description": "", + "description": "Revenant imp", "main": [ { "minAmount": "1", @@ -54720,28 +49240,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -54752,32 +49266,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -54786,16 +49300,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -54817,9 +49325,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -54837,31 +49345,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -54870,34 +49378,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -54910,12 +49406,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -54945,10 +49435,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6605,6612,6616,6620,6636,6637,6638,6639,6651,6656,6657,6658,6667,6678,6679,6680,6681,6693,6698,6699,6704,6705,6706,6707,6716,6717,6718,6719", - "description": "", + "description": "Revenant goblin", "main": [ { "minAmount": "1", @@ -55041,28 +49537,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -55073,32 +49563,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -55107,16 +49597,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -55138,9 +49622,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -55158,31 +49642,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -55191,34 +49675,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -55230,13 +49702,7 @@ "minAmount": "5", "weight": "25.0", "id": "7061", - "maxAmount": "20" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" + "maxAmount": "5" } ] }, @@ -55266,10 +49732,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6606,6621,6628,6640,6659,6682,6694,6708,6720", - "description": "", + "description": "Revenant icefiend", "main": [ { "minAmount": "1", @@ -55362,28 +49834,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -55394,32 +49860,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -55428,16 +49894,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -55459,9 +49919,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -55479,31 +49939,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -55512,34 +49972,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -55551,13 +49999,7 @@ "minAmount": "5", "weight": "25.0", "id": "7061", - "maxAmount": "20" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" + "maxAmount": "5" } ] }, @@ -55587,10 +50029,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6607,6609,6614,6617,6625,6632,6644,6663,6675,6686,6701,6712,6724,6728", - "description": "", + "description": "Revenant werewolf", "main": [ { "minAmount": "1", @@ -55683,28 +50131,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -55715,32 +50157,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -55749,10 +50191,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" + "maxAmount": "10" }, { "minAmount": "10", @@ -55800,31 +50242,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -55833,34 +50275,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -55873,12 +50303,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -55908,10 +50332,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6608,6642,6661,6684,6710,6722,6727", - "description": "", + "description": "Revenant hobgoblin", "main": [ { "minAmount": "1", @@ -56004,28 +50434,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -56036,32 +50460,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -56070,16 +50494,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -56101,9 +50519,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -56121,31 +50539,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -56154,34 +50572,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -56194,12 +50600,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -56229,10 +50629,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6610,6615,6618,6624,6626,6629,6633,6648,6653,6664,6670,6672,6690,6696,6702,6713,6725,6729", - "description": "", + "description": "Revenant ork", "main": [ { "minAmount": "1", @@ -56243,25 +50649,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "4087", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4585", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1712", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2572", + "id": "1704", "maxAmount": "1" }, { @@ -56355,28 +50743,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -56387,32 +50769,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -56421,16 +50803,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -56452,9 +50828,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -56472,31 +50848,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -56505,34 +50881,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -56545,12 +50909,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -56580,10 +50938,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6611,6619,6627,6630,6634,6650,6654,6665,6673,6676,6692,6714,6726,6730", - "description": "", + "description": "Revenant knight", "main": [ { "minAmount": "1", @@ -56594,25 +50958,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "4087", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4585", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1712", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2572", + "id": "1704", "maxAmount": "1" }, { @@ -56706,28 +51052,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -56738,32 +51078,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -56772,16 +51112,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -56803,9 +51137,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -56823,31 +51157,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -56856,34 +51190,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -56902,12 +51224,6 @@ "weight": "25.0", "id": "995", "maxAmount": "5000" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -56937,10 +51253,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6613,6623,6643,6652,6662,6669,6671,6674,6685,6695,6700,6711,6723", - "description": "", + "description": "Revenant vampire", "main": [ { "minAmount": "1", @@ -57033,28 +51355,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -57065,32 +51381,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -57099,16 +51415,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -57130,9 +51440,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -57150,31 +51460,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -57183,34 +51493,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -57229,12 +51527,6 @@ "weight": "25.0", "id": "995", "maxAmount": "5000" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -57264,10 +51556,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6622,6631,6641,6660,6668,6683,6709,6721", - "description": "", + "description": "Revenant pyrefiend", "main": [ { "minAmount": "1", @@ -57366,28 +51664,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -57398,32 +51690,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -57432,16 +51724,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -57463,9 +51749,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -57483,31 +51769,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -57516,34 +51802,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -57555,13 +51829,7 @@ "minAmount": "5", "weight": "25.0", "id": "7061", - "maxAmount": "20" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" + "maxAmount": "5" } ] }, @@ -57591,10 +51859,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6645,6687", - "description": "", + "description": "Revenant cyclops", "main": [ { "minAmount": "1", @@ -57605,31 +51879,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "4087", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4585", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1712", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2572", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "11732", + "id": "1704", "maxAmount": "1" }, { @@ -57723,28 +51973,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -57755,32 +51999,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -57789,16 +52033,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -57820,9 +52058,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -57840,31 +52078,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -57873,34 +52111,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -57913,12 +52139,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -57948,10 +52168,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6646,6688", - "description": "", + "description": "Revenant hellhound", "main": [ { "minAmount": "1", @@ -57962,25 +52188,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "4087", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4585", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1712", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2572", + "id": "1704", "maxAmount": "1" }, { @@ -58074,28 +52282,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -58106,32 +52308,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -58140,16 +52342,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -58171,9 +52367,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -58191,31 +52387,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -58224,35 +52420,23 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", "maxAmount": "150" }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" - }, { "minAmount": "4", "weight": "25.0", @@ -58264,12 +52448,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -58299,10 +52477,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6647,6689", - "description": "", + "description": "Revenant demon", "main": [ { "minAmount": "1", @@ -58313,25 +52497,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "4087", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4585", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1712", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2572", + "id": "1704", "maxAmount": "1" }, { @@ -58425,28 +52591,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -58457,32 +52617,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -58491,16 +52651,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -58522,9 +52676,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -58542,31 +52696,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -58575,34 +52729,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -58615,12 +52757,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -58650,10 +52786,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6649,6691", - "description": "", + "description": "Revenant dark beast", "main": [ { "minAmount": "1", @@ -58664,25 +52806,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "4087", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4585", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1712", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2572", + "id": "1704", "maxAmount": "1" }, { @@ -58776,28 +52900,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -58808,32 +52926,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -58842,16 +52960,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -58873,9 +52985,9 @@ }, { "minAmount": "5", - "weight": "25.0", + "weight": "50.0", "id": "1516", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", @@ -58893,31 +53005,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -58926,34 +53038,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -58966,12 +53066,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -58985,6 +53079,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "80.1337", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "6.6032", @@ -59008,12 +53108,6 @@ "weight": "5.8217", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "80.1337", - "id": "0", - "maxAmount": "1" } ], "ids": "6761,6762,6763", @@ -59274,7 +53368,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2831", + "id": "12070", "maxAmount": "1" }, { @@ -59301,6 +53395,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "90.2081", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "5.0796", @@ -59324,12 +53424,6 @@ "weight": "1.836", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "90.2081", - "id": "0", - "maxAmount": "1" } ], "ids": "6773", @@ -59440,7 +53534,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2727", + "id": "12070", "maxAmount": "1" }, { @@ -59551,7 +53645,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2735", + "id": "1", "maxAmount": "1" }, { @@ -59788,7 +53882,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "2733", + "id": "1", "maxAmount": "1" }, { @@ -60055,7 +54149,7 @@ { "minAmount": "1", "weight": "25.0", - "id": "2727", + "id": "12070", "maxAmount": "1" }, { @@ -60119,10 +54213,16 @@ "weight": "100.0", "id": "12163", "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "5.6", + "id": "0", + "maxAmount": "1" } ], "ids": "6998,6999", - "description": "", + "description": "Revenant dragon", "main": [ { "minAmount": "1", @@ -60133,25 +54233,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "4087", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4585", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1712", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2572", + "id": "1704", "maxAmount": "1" }, { @@ -60245,28 +54327,22 @@ "maxAmount": "1000" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "563", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "565", - "maxAmount": "500" + "maxAmount": "200" }, { - "minAmount": "50", + "minAmount": "15", "weight": "25.0", "id": "560", - "maxAmount": "500" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "454", - "maxAmount": "250" + "maxAmount": "200" }, { "minAmount": "1", @@ -60277,32 +54353,32 @@ { "minAmount": "1", "weight": "25.0", - "id": "1602", - "maxAmount": "50" + "id": "1618", + "maxAmount": "5" }, { "minAmount": "10", "weight": "25.0", - "id": "2358", - "maxAmount": "125" + "id": "445", + "maxAmount": "30" }, { - "minAmount": "5", + "minAmount": "1", "weight": "50.0", "id": "114", - "maxAmount": "15" + "maxAmount": "4" }, { "minAmount": "1", "weight": "25.0", "id": "208", - "maxAmount": "15" + "maxAmount": "10" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "5.0", "id": "450", - "maxAmount": "75" + "maxAmount": "5" }, { "minAmount": "1", @@ -60311,16 +54387,10 @@ "maxAmount": "5" }, { - "minAmount": "5", + "minAmount": "1", "weight": "25.0", "id": "3050", - "maxAmount": "15" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "232", - "maxAmount": "500" + "maxAmount": "10" }, { "minAmount": "20", @@ -60362,31 +54432,31 @@ "minAmount": "1", "weight": "5.0", "id": "5315", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5316", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", "weight": "5.0", "id": "5300", - "maxAmount": "5" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5289", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "5288", - "maxAmount": "10" + "maxAmount": "1" }, { "minAmount": "1", @@ -60395,34 +54465,22 @@ "maxAmount": "50" }, { - "minAmount": "100", + "minAmount": "50", "weight": "25.0", "id": "9144", - "maxAmount": "100" + "maxAmount": "50" }, { - "minAmount": "300", + "minAmount": "75", "weight": "25.0", "id": "9143", - "maxAmount": "300" + "maxAmount": "75" }, { - "minAmount": "150", + "minAmount": "75", "weight": "25.0", "id": "892", - "maxAmount": "150" - }, - { - "minAmount": "100", - "weight": "25.0", - "id": "441", - "maxAmount": "500" - }, - { - "minAmount": "1000", - "weight": "25.0", - "id": "12183", - "maxAmount": "15000" + "maxAmount": "75" }, { "minAmount": "4", @@ -60435,12 +54493,6 @@ "weight": "25.0", "id": "7061", "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "5600.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -60454,6 +54506,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "13.1269", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "79.3218", @@ -60477,11 +54535,25 @@ "weight": "0.8264", "id": "12163", "maxAmount": "1" + } + ], + "tertiary": [ + { + "minAmount": "1", + "weight": "9973.0", + "id": "0", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "13.1269", - "id": "0", + "weight": "25.0", + "id": "10976", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "10977", "maxAmount": "1" } ], @@ -60490,25 +54562,19 @@ "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": "2745", + "weight": "2.0", + "id": "12570", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "124.0", "id": "0", "maxAmount": "1" } @@ -60524,6 +54590,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "53.1561", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "38.8704", @@ -60547,38 +54619,40 @@ "weight": "1.6611", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "53.1561", - "id": "0", - "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" } @@ -60594,6 +54668,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "52.7123", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "38.5613", @@ -60617,44 +54697,40 @@ "weight": "0.5896", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "52.7123", - "id": "0", - "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" } @@ -60668,27 +54744,39 @@ "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "2741", + "weight": "1.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "15.0", "id": "1147", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "35.0", "id": "31", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "77.0", + "id": "0", + "maxAmount": "1" } ] }, { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "2.0266", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "62.8232", @@ -60719,13 +54807,13 @@ "main": [ { "minAmount": "1", - "weight": "5.0", - "id": "2739", + "weight": "1.0", + "id": "5733", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.0266", + "weight": "127.0", "id": "0", "maxAmount": "1" } @@ -60739,67 +54827,61 @@ "main": [ { "minAmount": "1", - "weight": "1.0", + "weight": "2.371541", "id": "14497", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "2.371541", "id": "14499", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "2.371541", "id": "14501", "maxAmount": "1" }, { "minAmount": "10", - "weight": "50.0", + "weight": "25.0", "id": "557", "maxAmount": "10" }, { "minAmount": "18", - "weight": "50.0", + "weight": "25.0", "id": "557", "maxAmount": "18" }, { "minAmount": "10", - "weight": "50.0", + "weight": "25.0", "id": "556", - "maxAmount": "36" + "maxAmount": "10" }, { "minAmount": "10", - "weight": "50.0", + "weight": "25.0", "id": "559", - "maxAmount": "36" + "maxAmount": "10" }, { "minAmount": "10", - "weight": "50.0", + "weight": "25.0", "id": "554", - "maxAmount": "36" + "maxAmount": "10" }, { "minAmount": "10", - "weight": "50.0", - "id": "555", - "maxAmount": "18" - }, - { - "minAmount": "10", - "weight": "50.0", + "weight": "25.0", "id": "558", "maxAmount": "10" }, { "minAmount": "18", - "weight": "50.0", + "weight": "25.0", "id": "558", "maxAmount": "18" }, @@ -60854,32 +54936,68 @@ { "minAmount": "1", "weight": "25.0", - "id": "1213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1333", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", "id": "995", "maxAmount": "1" }, { "minAmount": "2", - "weight": "50.0", + "weight": "25.0", "id": "995", "maxAmount": "2" + }, + { + "minAmount": "4", + "weight": "25.0", + "id": "995", + "maxAmount": "4" + }, + { + "minAmount": "1", + "weight": "25.0", + "id": "581", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "25.0", + "id": "1017", + "maxAmount": "1" + }, + { + "minAmount": "36", + "weight": "25.0", + "id": "556", + "maxAmount": "36" + }, + { + "minAmount": "36", + "weight": "25.0", + "id": "559", + "maxAmount": "36" + }, + { + "minAmount": "36", + "weight": "25.0", + "id": "554", + "maxAmount": "36" + }, + { + "minAmount": "36", + "weight": "25.0", + "id": "558", + "maxAmount": "36" } ] }, { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "93.0134", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "1.6584", @@ -61063,6 +55181,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "71.9397", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "6.1502", @@ -61086,12 +55210,6 @@ "weight": "1.094", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "71.9397", - "id": "0", - "maxAmount": "1" } ], "ids": "7159", @@ -61252,6 +55370,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "58.7492", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.9739", @@ -61275,12 +55399,6 @@ "weight": "1.0919", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "58.7492", - "id": "0", - "maxAmount": "1" } ], "ids": "7160", @@ -61532,6 +55650,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "73.7931", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "22.9885", @@ -61555,12 +55679,6 @@ "weight": "0.0", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "73.7931", - "id": "0", - "maxAmount": "1" } ], "ids": "7640", @@ -61811,6 +55929,12 @@ { "default": [], "charm": [ + { + "minAmount": "1", + "weight": "75.6592", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "20.8925", @@ -61834,12 +55958,6 @@ "weight": "0.2028", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "75.6592", - "id": "0", - "maxAmount": "1" } ], "ids": "7641", @@ -62139,6 +56257,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "46.1418", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "43.3139", @@ -62162,12 +56286,6 @@ "weight": "1.1161", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "46.1418", - "id": "0", - "maxAmount": "1" } ], "ids": "7642,7643", @@ -62430,6 +56548,12 @@ { "default": [], "charm": [ + { + "minAmount": "13", + "weight": "3.1496", + "id": "0", + "maxAmount": "13" + }, { "minAmount": "13", "weight": "22.2601", @@ -62460,181 +56584,163 @@ "main": [ { "minAmount": "1", - "weight": "1.0", + "weight": "0.4", + "id": "13752", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.2", "id": "13746", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", - "id": "13748", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", + "weight": "0.2", "id": "13750", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.0", - "id": "13752", + "weight": "0.2", + "id": "13748", "maxAmount": "1" }, - { - "minAmount": "10000", - "weight": "125.0", - "id": "995", - "maxAmount": "20000" - }, { "minAmount": "20000", - "weight": "100.0", - "id": "995", - "maxAmount": "30000" - }, - { - "minAmount": "30000", - "weight": "50.0", + "weight": "12.0", "id": "995", "maxAmount": "50000" }, { "minAmount": "1", - "weight": "100.0", - "id": "1624", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "1622", - "maxAmount": "8" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "1620", - "maxAmount": "8" - }, - { - "minAmount": "1", - "weight": "75.0", - "id": "1618", - "maxAmount": "8" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2366", + "weight": "12.0", + "id": "1401", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1250", + "weight": "12.0", + "id": "1403", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "12.0", + "id": "1405", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "12.0", + "id": "1407", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "11133", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", "id": "13734", "maxAmount": "1" }, { "minAmount": "750", - "weight": "75.0", + "weight": "17.0", "id": "890", "maxAmount": "750" }, { "minAmount": "250", - "weight": "175.0", + "weight": "24.0", "id": "9144", "maxAmount": "250" }, { "minAmount": "2000", - "weight": "100.0", + "weight": "17.0", "id": "2", "maxAmount": "2000" }, { "minAmount": "175", - "weight": "100.0", + "weight": "20.0", "id": "9245", "maxAmount": "175" }, { "minAmount": "2500", - "weight": "100.0", + "weight": "21.0", "id": "7937", "maxAmount": "2500" }, { "minAmount": "250", - "weight": "100.0", + "weight": "17.0", "id": "563", "maxAmount": "250" }, { "minAmount": "500", - "weight": "100.0", + "weight": "17.0", "id": "564", "maxAmount": "500" }, { - "minAmount": "209", - "weight": "125.0", + "minAmount": "300", + "weight": "22.0", "id": "560", "maxAmount": "300" }, { "minAmount": "250", - "weight": "80.0", + "weight": "32.0", "id": "566", "maxAmount": "250" }, { "minAmount": "70", - "weight": "80.0", + "weight": "21.0", "id": "384", "maxAmount": "70" }, { "minAmount": "120", - "weight": "80.0", + "weight": "17.0", "id": "240", "maxAmount": "120" }, { "minAmount": "120", - "weight": "80.0", + "weight": "17.0", "id": "9738", "maxAmount": "120" }, { "minAmount": "24", - "weight": "25.0", + "weight": "15.0", "id": "5321", "maxAmount": "24" }, { "minAmount": "10", - "weight": "100.0", + "weight": "5.0", "id": "5295", "maxAmount": "10" }, { "minAmount": "30", - "weight": "75.0", + "weight": "20.0", "id": "7061", "maxAmount": "30" }, { "minAmount": "40", - "weight": "75.0", + "weight": "10.0", "id": "2449", "maxAmount": "40" }, @@ -62645,10 +56751,64 @@ "maxAmount": "1" }, { - "minAmount": "13", - "weight": "3.1496", - "id": "0", - "maxAmount": "13" + "minAmount": "1", + "weight": "18.0", + "id": "4091", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "18.0", + "id": "4093", + "maxAmount": "1" + }, + { + "minAmount": "125", + "weight": "17.0", + "id": "450", + "maxAmount": "125" + }, + { + "minAmount": "35", + "weight": "18.0", + "id": "2362", + "maxAmount": "35" + }, + { + "minAmount": "20", + "weight": "12.0", + "id": "452", + "maxAmount": "20" + }, + { + "minAmount": "100", + "weight": "12.0", + "id": "8781", + "maxAmount": "100" + }, + { + "minAmount": "150", + "weight": "12.0", + "id": "8836", + "maxAmount": "150" + }, + { + "minAmount": "100", + "weight": "18.0", + "id": "1754", + "maxAmount": "100" + }, + { + "minAmount": "75", + "weight": "12.0", + "id": "1514", + "maxAmount": "75" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "31", + "maxAmount": "1" } ] }, @@ -62662,6 +56822,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "82.1886", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.3302", @@ -62685,12 +56851,6 @@ "weight": "3.0617", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "82.1886", - "id": "0", - "maxAmount": "1" } ], "ids": "8149,8150,8151", @@ -62882,6 +57042,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "91.6064", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "7.0005", @@ -62905,12 +57071,6 @@ "weight": "0.1632", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "91.6064", - "id": "0", - "maxAmount": "1" } ], "ids": "1076,1077", @@ -63192,6 +57352,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "88.5971", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "0.5251", @@ -63215,12 +57381,6 @@ "weight": "0.5251", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "88.5971", - "id": "0", - "maxAmount": "1" } ], "ids": "1044,1045,1046", @@ -63250,6 +57410,12 @@ } ], "charm": [ + { + "minAmount": "1", + "weight": "88.5971", + "id": "0", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "0.5251", @@ -63273,12 +57439,6 @@ "weight": "0.5251", "id": "12163", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "88.5971", - "id": "0", - "maxAmount": "1" } ], "ids": "188,189,190", @@ -63355,7 +57515,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "3179", + "id": "526", "maxAmount": "1" } ], @@ -63374,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": [ { @@ -63385,13 +57545,6 @@ } ] }, - { - "default": [], - "charm": [], - "ids": "61,47", - "description": "", - "main": [] - }, { "default": [ { @@ -63606,5 +57759,1053 @@ "maxAmount": "1" } ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "0", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "391,392,393,394,395,396", + "description": "River Troll (Hostile Fishing Random Event)", + "main": [ + { + "minAmount": "1", + "weight": "10.0", + "id": "371", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "383", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "349", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.2", + "id": "331", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.2", + "id": "327", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "345", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "359", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "10.0", + "id": "313", + "maxAmount": "5" + }, + { + "minAmount": "30", + "weight": "10.0", + "id": "313", + "maxAmount": "30" + }, + { + "minAmount": "15", + "weight": "10.0", + "id": "313", + "maxAmount": "15" + }, + { + "minAmount": "20", + "weight": "3.2", + "id": "314", + "maxAmount": "20" + }, + { + "minAmount": "40", + "weight": "3.2", + "id": "314", + "maxAmount": "40" + }, + { + "minAmount": "50", + "weight": "2.0", + "id": "313", + "maxAmount": "50" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "407", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "405", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.5", + "id": "151", + "maxAmount": "1" + } + ] + }, + { + "default": [], + "charm": [], + "ids": "413,414,415,416", + "description": "Rock Golem (Combat lvl 14-79) (Hostile Mining Random Event)", + "main": [ + { + "minAmount": "5", + "weight": "20.0", + "id": "557", + "maxAmount": "5" + }, + { + "minAmount": "42", + "weight": "5.0", + "id": "557", + "maxAmount": "42" + }, + { + "minAmount": "2", + "weight": "5.0", + "id": "562", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "2349", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "438", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1913", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "2347", + "maxAmount": "1" + } + ] + }, + { + "default": [], + "charm": [], + "ids": "417,418", + "description": "Rock Golem (Combat lvl 120-159) (Hostile Mining Random Event)", + "main": [ + { + "minAmount": "1", + "weight": "6.0", + "id": "1265", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.5", + "id": "1271", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.5", + "id": "1275", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "20.0", + "id": "557", + "maxAmount": "5" + }, + { + "minAmount": "42", + "weight": "18.0", + "id": "557", + "maxAmount": "42" + }, + { + "minAmount": "2", + "weight": "18.0", + "id": "562", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "20.0", + "id": "453", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "17.0", + "id": "447", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "17.0", + "id": "2349", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "2351", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "438", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "436", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "449", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.5", + "id": "451", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "20.0", + "id": "1913", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "20.0", + "id": "2347", + "maxAmount": "1" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "2892", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1480", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "4910", + "description": "Earth Elemental", + "main": [ + { + "minAmount": "1", + "weight": "2.0", + "id": "565", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "560", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "6.0", + "id": "563", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "12.0", + "id": "562", + "maxAmount": "2" + }, + { + "minAmount": "15", + "weight": "12.0", + "id": "557", + "maxAmount": "20" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "10.0", + "id": "561", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "1385", + "maxAmount": "1" + }, + { + "minAmount": "14", + "weight": "45.0", + "id": "995", + "maxAmount": "42" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "31", + "maxAmount": "1" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "2892", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1480", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "4911", + "description": "Elemental Rock", + "main": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "0", + "maxAmount": "1" + } + ] + }, + { + "default": [], + "charm": [], + "ids": "1020", + "description": "Wandering earth elemental", + "main": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "31", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "565", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "560", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "6.0", + "id": "563", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "12.0", + "id": "562", + "maxAmount": "2" + }, + { + "minAmount": "15", + "weight": "12.0", + "id": "557", + "maxAmount": "20" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "10.0", + "id": "561", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "1385", + "maxAmount": "1" + }, + { + "minAmount": "14", + "weight": "45.0", + "id": "995", + "maxAmount": "42" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "0", + "maxAmount": "1" + } + ], + "ids": "4396,4415", + "description": "Rat (Stronghold)", + "main": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "0", + "maxAmount": "1" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "3363", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3355", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "1232", + "description": "Pointed", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "3367", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3357", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "1233", + "description": "Pointed", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "3363", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3359", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "1234", + "description": "Pointed", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "3367", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3361", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "1235", + "description": "Pointed", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "1019", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "180", + "description": "Old-Highwayman", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "837", + "description": "Shantay Guard", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1019", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "2677", + "description": "New-Highwayman", + "main": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "995", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "9141", + "maxAmount": "15" + }, + { + "minAmount": "1", + "weight": "23.0", + "id": "0", + "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 cce817f41..1893b6df3 100644 --- a/Server/data/configs/ground_spawns.json +++ b/Server/data/configs/ground_spawns.json @@ -1,566 +1,690 @@ [ + { + "item_id": "20", + "loc_data": "{1,2577,9655,0,100}-" + }, + { + "item_id": "21", + "loc_data": "{1,2613,9639,0,100}-" + }, + { + "item_id": "22", + "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,7864410}" + "loc_data": "{1,2807,3450,0,90}-" }, { "item_id": "33", - "loc_data": "{1,2547,3114,0,983050}-{1,3208,3395,1,3932190}" + "loc_data": "{1,2547,3114,0,10}-{1,3208,3395,1,30}-" }, { "item_id": "39", - "loc_data": "{1,2672,3433,0,7209050}" + "loc_data": "{1,2672,3433,0,90}-" }, { "item_id": "41", - "loc_data": "{1,3125,9997,0,7209050}" - }, + "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,4587583}" + "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,7209050}" + "loc_data": "{1,2467,3176,0,90}-" }, { "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}" + "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,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}" + "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,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}" + "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,196610}-{1,3210,3808,0,196610}" + "loc_data": "{1,3217,3812,0,2}-{1,3210,3808,0,2}-" }, { "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}" + "loc_data": "{1,2910,9809,0,30}-{1,2908,9805,0,30}-{1,2902,9806,0,30}-{1,2906,9796,0,30}-{1,2905,9801,0,30}-" }, { "item_id": "245", - "loc_data": "{1,2931,3515,0,4587570}" + "loc_data": "{1,2931,3515,0,50}-" }, { "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}" + "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,10485900}" + "loc_data": "{1,3108,3356,1,140}-" }, { "item_id": "273", - "loc_data": "{1,3097,3366,0,10485900}" + "loc_data": "{1,3097,3366,0,140}-" }, { "item_id": "276", - "loc_data": "{1,3111,3367,0,4587570}" + "loc_data": "{1,3111,3367,0,50}-" }, { "item_id": "277", - "loc_data": "{1,3092,9755,0,4587570}" + "loc_data": "{1,3092,9755,0,50}-" }, { "item_id": "278", - "loc_data": "{1,2604,3358,0,4587570}" + "loc_data": "{1,2604,3358,0,50}-" }, { "item_id": "301", - "loc_data": "{1,2608,3397,0,7209050}" + "loc_data": "{1,2608,3397,0,90}-" }, { "item_id": "303", - "loc_data": "{1,3245,3155,0,10485900}-{1,3244,3159,0,10485900}" + "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,7209050}" + "loc_data": "{1,2605,3395,0,90}-" }, { "item_id": "307", - "loc_data": "{1,2860,3335,0,13762750}" + "loc_data": "{1,2860,3335,0,190}-" }, { "item_id": "311", - "loc_data": "{1,2605,3396,0,7209050}" + "loc_data": "{1,2605,3396,0,90}-" }, { "item_id": "347", - "loc_data": "{1,2563,9511,0,5898270}" + "loc_data": "{1,2563,9511,0,30}-" }, { "item_id": "357", - "loc_data": "{1,2820,3453,0,7209050}" + "loc_data": "{1,2820,3453,0,90}-" }, { "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}" + "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,3195,9821,0,11141270}-{1,3231,3739,0,19661050}-{1,3236,3741,0,19661050}" + "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,5898270}" + "loc_data": "{1,3298,3313,0,30}-" }, { "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}" + "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,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}" + "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,7209050}" + "loc_data": "{1,3059,3488,1,90}-" }, { "item_id": "544", - "loc_data": "{1,3059,3487,1,7209050}" + "loc_data": "{1,3059,3487,1,90}-" }, { "item_id": "554", - "loc_data": "{1,3303,3311,0,5898270}-{1,3026,3636,0,19661000}" + "loc_data": "{1,3303,3311,0,30}-{1,3026,3636,0,200}-" }, { "item_id": "555", - "loc_data": "{1,3297,3316,0,5898270}-{1,3028,3636,0,19661000}" + "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,8519790}-{1,3030,3636,0,19661000}" + "loc_data": "{1,2938,3158,0,110}-{1,3030,3636,0,200}-" }, { "item_id": "557", - "loc_data": "{1,3032,3636,0,19661000}" + "loc_data": "{1,3032,3636,0,200}-" }, { "item_id": "558", - "loc_data": "{1,3206,3208,0,5898280}-{1,3023,3640,0,19661000}" + "loc_data": "{1,3206,3208,0,40}-{1,3023,3640,0,200}-" }, { "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}" + "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,19661000}" + "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,3112,3369,2,10485900}-{1,3209,3734,0,13107300}" + "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,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}" + "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,5898270}-{1,2903,3441,0,5898270}" + "loc_data": "{1,2887,3412,0,30}-{1,2903,3441,0,30}-" }, { "item_id": "767", - "loc_data": "{1,3245,3385,1,4587570}-{1,3243,3383,1,4587570}" + "loc_data": "{1,3245,3385,1,50}-{1,3243,3383,1,50}-" }, { "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}" + "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,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}" + "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,3120,3359,0,8519790}-{1,2981,3370,0,9830490}-{1,3572,3312,0,3276830}-{1,3571,3310,0,3276830}-{1,3218,3412,1,5898270}" + "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,11141270}" + "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,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}" + "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,13107350}" + "loc_data": "{1,3237,3696,0,150}-" }, { "item_id": "970", - "loc_data": "{1,3290,3033,1,7864410}" + "loc_data": "{1,3290,3033,1,90}-" }, { "item_id": "983", - "loc_data": "{1,3131,9862,0,7209050}" + "loc_data": "{1,3131,9862,0,90}-" }, { "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}" + "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,3276830}-{1,3009,3204,0,3276830}" + "loc_data": "{1,3014,3227,0,30}-{1,3009,3204,0,30}-" }, { "item_id": "1009", - "loc_data": "{1,3122,9881,0,3276830}" - }, + "loc_data": "{1,3122,9881,0,30}-" + }, { "item_id": "1059", - "loc_data": "{1,3242,3385,1,7209050}-{1,3097,3486,0,7864410}-{1,3148,3177,0,5898270}" + "loc_data": "{1,3148,3177,0,30}-{1,3242,3385,1,90}-{1,3097,3486,0,90}-" }, { "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}" + "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,6553660}" + "loc_data": "{1,3084,3859,0,60}-" }, { "item_id": "1137", - "loc_data": "{1,3242,3688,0,13107350}" + "loc_data": "{1,3242,3688,0,150}-" }, { "item_id": "1139", - "loc_data": "{1,3122,3360,0,13762750}" + "loc_data": "{1,3122,3360,0,190}-" }, { "item_id": "1171", - "loc_data": "{1,3217,3514,0,2949150}" + "loc_data": "{1,3217,3514,0,30}-" }, { "item_id": "1191", - "loc_data": "{1,3250,3797,0,13107350}" + "loc_data": "{1,3250,3797,0,150}-" }, { "item_id": "1203", - "loc_data": "{1,3242,3383,1,17039600}-{1,3248,3245,0,13107380}-{1,3216,3695,0,13107350}" + "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,13762750}" + "loc_data": "{1,3213,3216,1,190}-" }, { "item_id": "1207", - "loc_data": "{1,3213,3682,0,13107300}" + "loc_data": "{1,3213,3682,0,100}-" }, { "item_id": "1217", - "loc_data": "{1,3180,3821,0,6553660}" + "loc_data": "{1,3180,3821,0,60}-" }, { "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}" + "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,5898270}" + "loc_data": "{1,2965,3211,1,30}-" }, { "item_id": "1351", - "loc_data": "{1,2795,3161,0,17039600}-{1,2970,3376,1,5898270}" + "loc_data": "{1,2795,3161,0,240}-{1,2970,3376,1,30}-" }, { "item_id": "1385", - "loc_data": "{1,3099,3862,0,58983299}" + "loc_data": "{1,3099,3862,0,131}-" }, { "item_id": "1422", - "loc_data": "{1,3320,3137,0,11141270}" + "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,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}" + "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,5898270}" + "loc_data": "{1,2614,9570,0,30}-" }, { "item_id": "1550", - "loc_data": "{1,2714,3478,0,5898270}" - }, + "loc_data": "{1,2714,3478,0,30}-" + }, { "item_id": "1573", - "loc_data": "{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}" + "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,7209050}" - }, + "loc_data": "{1,2900,9766,0,90}-" + }, { "item_id": "1592", - "loc_data": "{1,2935,3283,1,7209050}" + "loc_data": "{1,2935,3283,1,90}-" }, { "item_id": "1595", - "loc_data": "{1,2928,3290,0,7209050}" + "loc_data": "{1,2928,3290,0,90}-" }, { "item_id": "1597", - "loc_data": "{1,2932,3287,1,7209050}" + "loc_data": "{1,2932,3287,1,90}-" }, { "item_id": "1599", - "loc_data": "{1,2931,3287,1,7209050}" + "loc_data": "{1,2931,3287,1,90}-" }, { "item_id": "1607", - "loc_data": "{1,3169,3887,0,32768400}-{1,3169,3887,0,32768400}" + "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,11141270}" + "loc_data": "{1,3196,9822,0,150}-" }, { "item_id": "1654", - "loc_data": "{1,3192,9821,0,11141270}" + "loc_data": "{1,3192,9821,0,150}-" }, { "item_id": "1734", - "loc_data": "{1,3286,3491,0,4587570}" + "loc_data": "{1,3286,3491,0,50}-" }, { "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}" + "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,7209050}" + "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,11141270}" + "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,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}" - }, - { - "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}" - }, - { - "item_id": "1982", - "loc_data": "{1,3085,3261,0,11141270}-{1,3039,3706,0,1310735}" - }, - { - "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}" - }, - { - "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}" + "loc_data": "{1,3141,3452,1,90}-" }, { "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}" + "loc_data": "{1,2863,9875,0,44}-{1,2861,9878,0,44}-" }, { - "item_id": "2964", - "loc_data": "{1,3437,3337,0,100}" + "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,10}-{1,2647,2956,0,10}-{1,2638,2949,0,10}-" + }, + { + "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}-" + }, + { + "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}" + "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 a5f64ee10..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" }, @@ -712,5 +910,17 @@ "interfaceType": "3", "walkable": "false", "tabIndex": "-1" + }, + { + "id": "804", + "interfaceType": "1", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "809", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" } ] diff --git a/Server/data/configs/item_configs.json b/Server/data/configs/item_configs.json index 559e30ae3..b928857e7 100644 --- a/Server/data/configs/item_configs.json +++ b/Server/data/configs/item_configs.json @@ -1,139569 +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", - "low_alchemy": "2", - "high_alchemy": "3", - "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.", - "durability": null, - "name": "Ammo mould", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "75000", - "high_alchemy": "112500", - "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", - "low_alchemy": "75000", - "high_alchemy": "112500", "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", - "low_alchemy": "75000", - "high_alchemy": "112500", - "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", - "low_alchemy": "75000", - "high_alchemy": "112500", "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", - "low_alchemy": "75000", - "high_alchemy": "112500", - "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", - "low_alchemy": "75000", - "high_alchemy": "112500", "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", - "low_alchemy": "75000", - "high_alchemy": "112500", - "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", - "low_alchemy": "75000", - "high_alchemy": "112500", "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "high_alchemy": "1", + "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "examine": "I can make an arrow with these.", "archery_ticket_price": "0", - "id": "39" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "10", + "tradeable": "true" }, { - "shop_price": "26", - "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "23", - "durability": null, + "id": "40", "name": "Iron arrowtips", - "tradeable": "true", - "low_alchemy": "10", - "high_alchemy": "15", + "examine": "I can make an arrow with these.", "archery_ticket_price": "0", - "id": "40" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "26", + "tradeable": "true" }, { - "shop_price": "36", - "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "63", - "durability": null, + "id": "41", "name": "Steel arrowtips", - "tradeable": "true", - "low_alchemy": "14", - "high_alchemy": "21", + "examine": "I can make an arrow with these.", "archery_ticket_price": "0", - "id": "41" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "36", + "tradeable": "true" }, { - "shop_price": "68", - "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "74", - "durability": null, + "id": "42", "name": "Mithril arrowtips", - "tradeable": "true", - "low_alchemy": "27", - "high_alchemy": "40", + "examine": "I can make an arrow with these.", "archery_ticket_price": "0", - "id": "42" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "68", + "tradeable": "true" }, { - "shop_price": "160", - "ge_buy_limit": "10000", - "examine": "I can make an arrow with these.", - "grand_exchange_price": "185", - "durability": null, + "id": "43", "name": "Adamant arrowtips", - "tradeable": "true", - "low_alchemy": "64", - "high_alchemy": "96", - "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", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "shop_price": "160", + "tradeable": "true" + }, + { + "id": "44", "name": "Rune arrowtips", - "tradeable": "true", - "low_alchemy": "184", - "high_alchemy": "276", + "examine": "I can make an arrow with these.", "archery_ticket_price": "0", - "id": "44" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "460", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "Opal bolt tips.", - "grand_exchange_price": "1", - "durability": null, + "id": "45", "name": "Opal bolt tips", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "4", + "examine": "Opal bolt tips.", "archery_ticket_price": "0", - "id": "45" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "examine": "Pearl bolt tips.", - "grand_exchange_price": "5", - "durability": null, + "id": "46", "name": "Pearl bolt tips", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "7", + "examine": "Pearl bolt tips.", "archery_ticket_price": "0", - "id": "46" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "114", - "ge_buy_limit": "10000", - "examine": "I can make bolts with these.", - "grand_exchange_price": "44", - "durability": null, + "id": "47", "name": "Barb bolttips", - "tradeable": "false", - "low_alchemy": "38", - "high_alchemy": "57", - "destroy": "true", + "examine": "I can make bolts with these.", "archery_ticket_price": "5", - "id": "47" + "destroy": "true", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "114", + "tradeable": "false" }, { - "ge_buy_limit": "10000", + "id": "48", + "name": "Longbow (u)", "examine": "An unstrung longbow; I need a bowstring for this.", - "grand_exchange_price": "14", - "durability": null, - "name": "Longbow (u)", - "tradeable": "true", - "low_alchemy": "24", - "high_alchemy": "36", "archery_ticket_price": "0", - "id": "48" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "14", - "durability": null, + "id": "49", "name": "Longbow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "49" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "50", + "name": "Shortbow (u)", "examine": "I need to find a string for this.", - "grand_exchange_price": "5", - "durability": null, - "name": "Shortbow (u)", - "tradeable": "true", - "low_alchemy": "9", - "high_alchemy": "13", - "weight": "1", "archery_ticket_price": "0", - "id": "50" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "5", - "durability": null, - "name": "Shortbow (u)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "51" + "weight": "1" }, { - "ge_buy_limit": "20000", - "examine": "A wooden arrow shaft", - "grand_exchange_price": "21", + "id": "51", + "name": "Shortbow (u)", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "52", "name": "Arrow shaft", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "7", + "examine": "A wooden arrow shaft", "archery_ticket_price": "0", - "id": "52" - }, - { + "durability": null, "ge_buy_limit": "20000", - "examine": "A wooden arrow shaft with flights attached.", - "grand_exchange_price": "56", - "durability": null, + "tradeable": "true" + }, + { + "id": "53", "name": "Headless arrow", - "tradeable": "true", + "examine": "A wooden arrow shaft with flights attached.", "archery_ticket_price": "0", - "id": "53" + "durability": null, + "ge_buy_limit": "20000", + "tradeable": "true" }, { - "shop_price": "50", - "ge_buy_limit": "10000", + "id": "54", + "name": "Oak shortbow (u)", "examine": "An unstrung oak bow; I need a bowstring for this.", - "grand_exchange_price": "7", - "durability": null, - "name": "Oak shortbow (u)", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "1", "archery_ticket_price": "0", - "id": "54" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "50", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "7", - "durability": null, + "id": "55", "name": "Oak shortbow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "55" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "56", + "name": "Oak longbow (u)", "examine": "An unstrung oak longbow; I need a bowstring for this.", - "grand_exchange_price": "20", - "durability": null, - "name": "Oak longbow (u)", - "tradeable": "true", - "low_alchemy": "32", - "high_alchemy": "48", - "weight": "1", "archery_ticket_price": "0", - "id": "56" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "20", - "durability": null, + "id": "57", "name": "Oak longbow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "57" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "64", - "ge_buy_limit": "10000", + "id": "58", + "name": "Willow longbow (u)", "examine": "An unstrung willow longbow; I need a bowstring for this.", - "grand_exchange_price": "46", - "durability": null, - "name": "Willow longbow (u)", - "tradeable": "true", - "low_alchemy": "64", - "high_alchemy": "96", "archery_ticket_price": "0", - "id": "58" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "64", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "46", - "durability": null, + "id": "59", "name": "Willow longbow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "59" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "16", - "ge_buy_limit": "10000", + "id": "60", + "name": "Willow shortbow (u)", "examine": "An unstrung willow shortbow; I need a bowstring for this.", - "grand_exchange_price": "20", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10000", + "shop_price": "16", + "tradeable": "true" + }, + { + "id": "61", "name": "Willow shortbow (u)", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", "archery_ticket_price": "0", - "id": "60" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "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, + "id": "62", "name": "Maple longbow (u)", - "tradeable": "true", - "low_alchemy": "128", - "high_alchemy": "192", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "0.9", "archery_ticket_price": "0", - "id": "64" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.2" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "56", - "durability": null, - "name": "Maple shortbow (u)", - "tradeable": "true", + "id": "63", + "name": "Maple longbow (u)", "archery_ticket_price": "0", - "id": "65" + "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.", - "grand_exchange_price": "468", - "durability": null, - "name": "Yew longbow (u)", - "tradeable": "true", - "low_alchemy": "256", - "high_alchemy": "384", - "weight": "1.32", "archery_ticket_price": "0", - "id": "66" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.32" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "468", - "durability": null, + "id": "67", "name": "Yew longbow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "67" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "68", + "name": "Yew shortbow (u)", "examine": "An unstrung yew shortbow; I need a bowstring for this.", - "grand_exchange_price": "158", - "durability": null, - "name": "Yew shortbow (u)", - "tradeable": "true", - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "1.32", "archery_ticket_price": "0", - "id": "68" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.32" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "158", - "durability": null, + "id": "69", "name": "Yew shortbow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "69" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "931", - "ge_buy_limit": "10000", + "id": "70", + "name": "Magic longbow (u)", "examine": "An unstrung magic longbow; I need a bowstring for this.", - "grand_exchange_price": "1246", - "durability": null, - "name": "Magic longbow (u)", - "tradeable": "true", - "low_alchemy": "512", - "high_alchemy": "768", - "weight": "1", "archery_ticket_price": "0", - "id": "70" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "931", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1246", - "durability": null, + "id": "71", "name": "Magic longbow (u)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "71" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "890", - "ge_buy_limit": "10000", + "id": "72", + "name": "Magic shortbow (u)", "examine": "An unstrung magic shortbow; I need a bowstring for this.", - "grand_exchange_price": "745", - "durability": null, - "name": "Magic shortbow (u)", - "tradeable": "true", - "low_alchemy": "320", - "high_alchemy": "480", "archery_ticket_price": "0", - "id": "72" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "745", - "durability": null, - "name": "Magic shortbow (u)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "73" + "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", - "examine": "A helmet, as worn by the minions of General Khazard.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "2.7", - "equipment_slot": "0", - "destroy_message": "I can borrow another from the armoury near the Fight Arena.", - "name": "Khazard helmet", - "archery_ticket_price": "0", - "id": "74", - "bonuses": "0,0,0,0,0,4,5,3,0,0,0,0,0,0,0" + "tradeable": "false", + "weight": "2.7" }, { - "shop_price": "12", - "examine": "Armour, as worn by the minions of General Khazard.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "7", - "weight": "0.5", - "equipment_slot": "4", - "destroy_message": "I can borrow another from the armoury near the Fight Arena.", - "remove_sleeves": "true", - "name": "Khazard armour", - "archery_ticket_price": "0", "id": "75", - "bonuses": "0,0,0,0,0,9,11,10,0,0,0,0,0,0,0" + "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", + "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", - "low_alchemy": "2", - "tradeable": "false", - "high_alchemy": "3", + "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", - "high_alchemy": "1", - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "archery_ticket_price": "0", - "id": "86", - "equipment_slot": "2" - }, - { - "examine": "Yet another amulet.", - "durability": null, - "name": "Armadyl pendant", - "low_alchemy": "4", - "high_alchemy": "7", - "archery_ticket_price": "0", - "id": "87", - "equipment_slot": "2" - }, - { - "examine": "Magic boots that make you lighter than normal.", - "durability": null, - "name": "Boots of lightness", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "19", - "high_alchemy": "28", - "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", - "low_alchemy": "21", - "high_alchemy": "32", - "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", - "low_alchemy": "26", - "high_alchemy": "39", - "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", - "low_alchemy": "28", - "high_alchemy": "42", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "600", - "high_alchemy": "900", - "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", - "low_alchemy": "600", - "high_alchemy": "900", - "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", - "low_alchemy": "600", - "high_alchemy": "900", - "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", - "low_alchemy": "600", - "high_alchemy": "900", - "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", - "low_alchemy": "224", - "high_alchemy": "336", - "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", - "low_alchemy": "224", - "high_alchemy": "336", - "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", - "low_alchemy": "224", - "high_alchemy": "336", - "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", - "low_alchemy": "35", - "high_alchemy": "52", - "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", - "low_alchemy": "35", - "high_alchemy": "52", - "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", - "low_alchemy": "35", - "high_alchemy": "52", - "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", - "low_alchemy": "352", - "high_alchemy": "528", - "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", - "low_alchemy": "352", - "high_alchemy": "528", - "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", - "low_alchemy": "352", - "high_alchemy": "528", - "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", - "low_alchemy": "61", - "high_alchemy": "91", - "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", - "low_alchemy": "61", - "high_alchemy": "91", - "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", - "low_alchemy": "61", - "high_alchemy": "91", - "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", - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "88", - "high_alchemy": "132", - "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", - "low_alchemy": "88", - "high_alchemy": "132", - "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", - "low_alchemy": "88", - "high_alchemy": "132", - "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", - "low_alchemy": "105", - "high_alchemy": "158", - "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", - "low_alchemy": "105", - "high_alchemy": "158", - "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", - "low_alchemy": "105", - "high_alchemy": "158", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "57", - "high_alchemy": "86", - "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", - "low_alchemy": "70", - "high_alchemy": "105", - "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", - "low_alchemy": "70", - "high_alchemy": "105", - "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", - "low_alchemy": "70", - "high_alchemy": "105", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "2", - "high_alchemy": "3", "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", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "8", - "high_alchemy": "12", "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "20", - "high_alchemy": "31", "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "10", - "high_alchemy": "15", + "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", - "low_alchemy": "16", - "high_alchemy": "24", + "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", - "low_alchemy": "19", - "high_alchemy": "28", + "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", - "low_alchemy": "21", - "high_alchemy": "32", + "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", - "low_alchemy": "26", - "high_alchemy": "39", + "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", - "low_alchemy": "28", - "high_alchemy": "42", - "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", - "low_alchemy": "30", - "high_alchemy": "45", "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", - "low_alchemy": "1", - "tradeable": "false", - "high_alchemy": "1", + "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "2.2", - "archery_ticket_price": "0", "id": "284", - "equipment_slot": "4" - }, - { - "examine": "A thick pair of leather trousers. or These should protect me from the plague, I hope!", - "durability": null, - "name": "Plague trousers", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "2.2", + "name": "Plague jacket", + "examine": "This should protect me from the plague, I hope!", "archery_ticket_price": "0", - "id": "285", - "equipment_slot": "7" - }, - { - "examine": "Armour designed to fit goblins.", "durability": null, - "name": "Orange goblin mail", - "low_alchemy": "16", - "high_alchemy": "24", - "weight": "3", - "archery_ticket_price": "0", - "id": "286", - "equipment_slot": "4" - }, - { - "examine": "Armour designed to fit goblins.", - "durability": null, - "name": "Blue goblin mail", - "low_alchemy": "16", - "high_alchemy": "24", - "weight": "3", - "archery_ticket_price": "0", - "id": "287", - "equipment_slot": "4" - }, - { - "shop_price": "16", - "ge_buy_limit": "100", - "examine": "Armour designed to fit goblins.", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "weight": "3.6", "equipment_slot": "4", - "grand_exchange_price": "398", - "name": "Goblin mail", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "288" + "remove_sleeves": "true", + "weight": "2.2" }, { + "id": "285", + "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" + }, + { + "id": "286", + "name": "Orange goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" + }, + { + "id": "287", + "name": "Blue goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "weight": "3" + }, + { + "id": "288", + "name": "Goblin mail", + "examine": "Armour designed to fit goblins.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", "ge_buy_limit": "100", - "grand_exchange_price": "398", - "durability": null, - "name": "Goblin mail", + "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", - "high_alchemy": "1", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "1", "tradeable": "false", - "high_alchemy": "1", - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "grand_exchange_price": "100", - "name": "Lobster pot", "tradeable": "true", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "8.1", - "grand_exchange_price": "329", - "name": "Big fishing net", "tradeable": "true", - "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "1.3", - "grand_exchange_price": "74", - "name": "Fishing rod", "tradeable": "true", - "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "1.3", - "grand_exchange_price": "123", - "name": "Fly fishing rod", "tradeable": "true", - "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.", - "durability": null, - "low_alchemy": "18", - "high_alchemy": "27", - "weight": "1.3", - "grand_exchange_price": "235", - "name": "Harpoon", "tradeable": "true", - "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "36", - "high_alchemy": "55", - "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", - "low_alchemy": "27", - "high_alchemy": "40", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "20", - "high_alchemy": "30", + "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", - "low_alchemy": "20", - "high_alchemy": "30", + "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "6", - "high_alchemy": "10", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "48", - "high_alchemy": "72", - "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", - "low_alchemy": "48", - "high_alchemy": "72", - "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", - "low_alchemy": "48", - "high_alchemy": "72", - "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", - "low_alchemy": "136", - "high_alchemy": "204", - "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", - "low_alchemy": "160", - "high_alchemy": "240", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "107", - "high_alchemy": "160", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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" - }, - { - "examine": "Aww, it's empty.", - "durability": null, - "name": "Empty oyster", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "44", - "high_alchemy": "67", - "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", - "low_alchemy": "560", - "high_alchemy": "840", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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" - }, - { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "55", - "durability": null, - "name": "Priest gown", - "archery_ticket_price": "0", - "id": "426", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,3,0,0", - "equipment_slot": "4" - }, - { - "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", - "grand_exchange_price": "14", - "durability": null, - "name": "Priest gown", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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.", - "tokkul_price": "4", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "2.25", - "grand_exchange_price": "80", - "name": "Tin ore", "tradeable": "true", - "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", - "low_alchemy": "6", - "high_alchemy": "10", - "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.", - "tokkul_price": "112", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "weight": "2", - "grand_exchange_price": "180", - "name": "Silver ore", - "tradeable": "true", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "archery_ticket_price": "0", - "id": "446" - }, - { - "shop_price": "243", - "ge_buy_limit": "25000", - "examine": "This needs refining.", - "tokkul_price": "243", - "durability": null, - "low_alchemy": "64", - "high_alchemy": "97", - "weight": "1.75", - "grand_exchange_price": "420", - "name": "Mithril ore", - "tradeable": "true", - "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.", - "tokkul_price": "600", - "durability": null, - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "2.7", - "grand_exchange_price": "1160", - "name": "Adamantite ore", - "tradeable": "true", - "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", - "low_alchemy": "1280", - "high_alchemy": "1920", - "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", - "low_alchemy": "18", - "high_alchemy": "27", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "50", - "high_alchemy": "75", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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 hatchet.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "508" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "509" - }, - { - "examine": "A piece of ancient goblin hatchet.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "510" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "511" - }, - { - "examine": "A piece of ancient goblin hatchet.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "512" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "513" - }, - { - "examine": "A piece of ancient goblin hatchet.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "514" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "515" - }, - { - "examine": "A piece of ancient goblin hatchet.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "516" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "517" - }, - { - "examine": "A piece of ancient goblin hatchet.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "518" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "519" - }, - { - "examine": "A piece of ancient goblin hatchet.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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" - }, - { - "ge_buy_limit": "100", - "examine": "I feel closer to the gods when I am wearing this.", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "328", - "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" - }, - { - "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", - "low_alchemy": "28", - "high_alchemy": "42", - "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", - "low_alchemy": "28", - "high_alchemy": "42", - "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", - "low_alchemy": "14", - "high_alchemy": "21", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "41", - "high_alchemy": "62", - "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", - "low_alchemy": "49", - "high_alchemy": "74", - "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", - "low_alchemy": "18", - "high_alchemy": "28", - "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", - "low_alchemy": "50", - "high_alchemy": "75", - "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", - "low_alchemy": "30", - "high_alchemy": "46", - "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", - "low_alchemy": "73", - "high_alchemy": "110", - "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", - "low_alchemy": "54", - "high_alchemy": "82", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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, - "low_alchemy": "0", - "high_alchemy": "1", - "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", - "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", - "low_alchemy": "0", - "high_alchemy": "1", + "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, - "low_alchemy": "5", - "high_alchemy": "7", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "tradeable": "false", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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": "100,000 or more: \"X x coins\" where X is the exact number of coins in the stack.", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "grand_exchange_price": "558", - "durability": null, - "name": "Boots", - "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", - "grand_exchange_price": "397", - "durability": null, - "name": "Boots", - "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", - "grand_exchange_price": "612", - "durability": null, - "name": "Boots", - "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", - "grand_exchange_price": "496", - "durability": null, - "name": "Boots", - "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", - "grand_exchange_price": "226", - "durability": null, - "name": "Boots", - "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 [colour].", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "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 [colour].", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "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 [colour].", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "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 [colour].", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "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 [colour].", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "grand_exchange_price": "249", - "durability": null, - "name": "Robe bottoms", - "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", - "grand_exchange_price": "210", - "durability": null, - "name": "Robe bottoms", - "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", - "grand_exchange_price": "453", - "durability": null, - "name": "Robe bottoms", - "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", - "grand_exchange_price": "348", - "durability": null, - "name": "Robe bottoms", - "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", - "grand_exchange_price": "92", - "durability": null, - "name": "Robe bottoms", - "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", - "durability": null, - "name": "Hat", - "archery_ticket_price": "0", - "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", - "durability": null, - "name": "Hat", - "archery_ticket_price": "0", - "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", - "durability": null, - "name": "Hat", - "archery_ticket_price": "0", - "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", - "durability": null, - "name": "Hat", - "archery_ticket_price": "0", - "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", - "durability": null, - "name": "Hat", - "archery_ticket_price": "0", - "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", + "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./This tray contains mud.", + "attack_audios": "2508,0,0,0", "durability": null, - "name": "Panning tray", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "677" - }, - { - "examine": "An empty tray for panning./This tray contains mud.", - "durability": null, - "name": "Panning tray", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "678" - }, - { - "examine": "An empty tray for panning./This tray contains mud.", - "durability": null, - "name": "Panning tray", - "high_alchemy": "1", - "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" + "durability": null, + "tradeable": "true" }, { - "examine": "The owner has passed Earth Sciences Level 1 exam.", - "durability": null, - "name": "Level 1 certificate", + "id": "494", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "691" + "durability": null, + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "The owner has passed Earth Sciences Level 2 exam.", - "durability": null, - "name": "Level 2 certificate", + "id": "495", + "name": "Broken axe", "archery_ticket_price": "0", - "id": "692" + "durability": null, + "tradeable": "true" }, { - "examine": "The owner has passed Earth Sciences Level 3 exam.", - "durability": null, - "name": "Level 3 certificate", + "id": "496", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "693" + "durability": null, + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "Smashing!", - "durability": null, - "name": "Ceramic remains", + "id": "497", + "name": "Broken axe", "archery_ticket_price": "0", - "id": "694" + "durability": null, + "tradeable": "true" }, { - "examine": "Now, if I could just find a tooth fairy to sell this to...", - "durability": null, - "name": "Old tooth", + "id": "498", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "695" + "durability": null, + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "A letter inviting me to use the private digging shafts.", - "durability": null, - "name": "Invitation letter", - "tradeable": "false", - "destroy": "true", + "id": "499", + "name": "Broken axe", "archery_ticket_price": "0", - "id": "696" + "durability": null, + "tradeable": "true" }, { - "examine": "It would be hard to repair this!", - "durability": null, - "name": "Damaged armour", - "weight": "7.75", + "id": "500", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "697" + "durability": null, + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "Armour left over from a great fight.", - "durability": null, - "name": "Broken armour", - "weight": "8", + "id": "501", + "name": "Broken axe", "archery_ticket_price": "0", - "id": "698" + "durability": null, + "tradeable": "true" }, { - "examine": "An old stone slab with writing on it.", - "durability": null, - "name": "Stone tablet", + "id": "502", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "699" + "durability": null, + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "An acrid chemical.", - "durability": null, - "name": "Chemical powder", + "id": "503", + "name": "Broken axe", "archery_ticket_price": "0", - "id": "700" + "durability": null, + "tradeable": "true" }, { - "examine": "An acrid chemical.", - "durability": null, - "name": "Ammonium nitrate", + "id": "504", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "701" + "durability": null, + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "A strong chemical.", - "durability": null, - "name": "Unidentified liquid", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "id": "505", + "name": "Broken axe", "archery_ticket_price": "0", - "id": "702" + "durability": null, + "tradeable": "true" }, { - "shop_price": "1", - "examine": "A strong chemical.", - "durability": null, - "name": "Nitroglycerin", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "1", + "id": "506", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "703" + "durability": null, + "equipment_slot": "3", + "weight": "2.5" }, { - "examine": "Charcoal - crushed to small pieces!", - "durability": null, - "name": "Ground charcoal", + "id": "507", + "name": "Broken axe", "archery_ticket_price": "0", - "id": "704" + "durability": null, + "tradeable": "true" }, { - "examine": "A mixture of strong chemicals.", - "durability": null, - "name": "Chemical compound", - "weight": "2", + "id": "508", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", "archery_ticket_price": "0", - "id": "707" + "durability": null }, { - "examine": "The root of an arcenia plant.", - "durability": null, - "name": "Arcenia root", + "id": "509", + "name": "Picture", "archery_ticket_price": "0", - "id": "708" + "durability": null, + "tradeable": "true" }, { - "examine": "An unusually shaped vase. You can see something glinting inside.", - "durability": null, - "name": "Vase", + "id": "510", + "name": "Picture", + "examine": "A piece of ancient goblin axe.", "archery_ticket_price": "0", - "id": "710" + "durability": null }, { - "examine": "It's about chemicals, judging from its cover.", - "durability": null, - "name": "Book on chemicals", - "weight": "0.5", + "id": "511", + "name": "Picture", "archery_ticket_price": "0", - "id": "711" + "durability": null, + "tradeable": "true" }, { - "shop_price": "10", - "examine": "A nice cup of tea.", - "grand_exchange_price": "30", + "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, - "name": "Cup of tea", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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": "527", + "name": "Bones", "archery_ticket_price": "0", - "id": "714" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "190", + "tradeable": "true" }, { - "examine": "Notes given to you by Radimus Erkle, it includes a partially completed map.", - "durability": null, - "name": "Radimus notes", + "id": "528", + "name": "Burnt bones", + "examine": "Bones are for burying!", "archery_ticket_price": "0", - "id": "715" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.2" }, { - "examine": "It makes a loud but interesting sound when swung in the air.", - "durability": null, - "name": "Bull roarer", + "id": "529", + "name": "Burnt bones", "archery_ticket_price": "0", - "id": "716" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "examine": "A scrawled note with spidery writing on it.", - "durability": null, - "name": "Scrawled note", + "id": "530", + "name": "Bat bones", + "examine": "Ew it's a pile of bones.", "archery_ticket_price": "0", - "id": "717" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.3" }, { - "examine": "A scrawled note with spidery writing on it.", - "durability": null, - "name": "A scribbled note", + "id": "531", + "name": "Bat bones", "archery_ticket_price": "0", - "id": "718" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "examine": "A scrawled note with spidery writing on it.", - "durability": null, - "name": "Scrumpled note", + "id": "532", + "name": "Big bones", + "examine": "Normal: Ew, it's a pile of bones.", "archery_ticket_price": "0", - "id": "719" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "593", + "tradeable": "true", + "weight": "0.8" }, { - "examine": "A rough sketch of a bowl shaped vessel given to you by Gujuo.", - "durability": null, - "name": "Sketch", - "low_alchemy": "4", - "high_alchemy": "6", + "id": "533", + "name": "Big bones", "archery_ticket_price": "0", - "id": "720" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "593", + "tradeable": "true" }, { - "examine": "A specially made bowl constructed out of pure gold.", - "durability": null, - "name": "Gold bowl", - "low_alchemy": "280", - "high_alchemy": "420", + "id": "534", + "name": "Babydragon bones", + "examine": "Ew, it's a pile of bones.", "archery_ticket_price": "0", - "id": "721" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.7" }, { - "examine": "A specially made bowl constructed out of pure gold and blessed.", - "durability": null, - "name": "Blessed gold bowl", - "low_alchemy": "280", - "high_alchemy": "420", - "weight": "0.8", + "id": "535", + "name": "Babydragon bones", "archery_ticket_price": "0", - "id": "722" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "examine": "A blessed golden bowl. It has pure sacred water in it.", - "durability": null, - "name": "Golden bowl", - "low_alchemy": "280", - "high_alchemy": "420", - "weight": "1", + "id": "536", + "name": "Dragon bones", + "examine": "These would feed a dog for months!", "archery_ticket_price": "0", - "id": "723" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.5" }, { - "examine": "A blessed golden bowl. It has pure sacred water in it.", - "durability": null, - "name": "Golden bowl", - "low_alchemy": "280", - "high_alchemy": "420", - "weight": "1", + "id": "537", + "name": "Dragon bones", "archery_ticket_price": "0", - "id": "724" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "examine": "A blessed golden bowl. It has pure sacred water in it.", - "durability": null, - "name": "Golden bowl", - "low_alchemy": "280", - "high_alchemy": "420", - "weight": "1", + "id": "538", + "name": "Druid's robe", + "examine": "Keeps a druid's knees nice and warm.", "archery_ticket_price": "0", - "id": "725" + "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" }, { - "examine": "A blessed golden bowl. It has pure sacred water in it.", - "durability": null, - "name": "Golden bowl", - "low_alchemy": "280", - "high_alchemy": "420", - "weight": "1", + "id": "539", + "name": "Druid's robe", "archery_ticket_price": "0", - "id": "726" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "One of nature's pipes.", - "durability": null, - "name": "Hollow reed", - "high_alchemy": "1", - "weight": "0.012", + "id": "540", + "name": "Druid's robe", + "examine": "I feel closer to the gods when I am wearing this.", "archery_ticket_price": "0", - "id": "727" + "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" }, { - "examine": "One of nature's pipes.", - "durability": null, - "name": "Hollow reed", - "high_alchemy": "1", - "weight": "0.012", + "id": "541", + "name": "Druid's robe", "archery_ticket_price": "0", - "id": "728" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "It looks like the Shamans personal notes...", - "durability": null, - "name": "Shamans tome", + "id": "542", + "name": "Monk's robe", "archery_ticket_price": "0", - "id": "729" + "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" }, { - "examine": "An ancient tome on Demonology.", - "durability": null, - "name": "Binding book", - "weight": "0.5", + "id": "543", + "name": "Monk's robe", "archery_ticket_price": "0", - "id": "730" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "160", - "examine": "An enchanted empty glass vial.", + "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, - "name": "Enchanted vial", - "tradeable": "false", + "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", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "551", + "name": "Newcomer map", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "552", + "name": "Ghostspeak amulet", + "examine": "It lets me talk to ghosts.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "tradeable": "false" + }, + { + "id": "553", + "name": "Ghost's skull", + "examine": "Ooooh spooky!", + "archery_ticket_price": "0", "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.", + "id": "561", + "name": "Nature rune", + "examine": "Used for alchemy spells.", + "archery_ticket_price": "0", + "durability": null, + "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" + }, + { + "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", + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0", "durability": null, - "low_alchemy": "64", - "high_alchemy": "96", - "weight": "0.4", "equipment_slot": "0", - "grand_exchange_price": "472", - "name": "Blue hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "740", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,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" - }, - { "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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, - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "3", - "turn90ccw_anim": "822", - "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", - "low_alchemy": "6", - "tradeable": "false", - "high_alchemy": "9", + "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "810", - "high_alchemy": "1215", + "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", - "low_alchemy": "870", - "high_alchemy": "1305", - "archery_ticket_price": "0", "id": "774", + "name": "'perfect' necklace", + "examine": "A perfect ruby necklace.", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "2" }, { - "examine": "These gauntlets empower with a greater ability to cook fish.", - "durability": null, - "low_alchemy": "75000", - "high_alchemy": "75000", - "destroy": "true", - "weight": "0.2", - "equipment_slot": "9", - "destroy_message": "You can obtain another set of Cooking gauntlets from Dimintheis.", - "name": "Cooking gauntlets", - "tradeable": "false", - "archery_ticket_price": "0", "id": "775", - "bonuses": "2,2,2,0,0,8,9,7,0,0,3,2,0,0,0" + "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", + "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", - "low_alchemy": "75000", - "tradeable": "false", - "high_alchemy": "75000", - "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", - "low_alchemy": "75000", - "tradeable": "false", - "high_alchemy": "75000", - "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", - "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, - "low_alchemy": "1", - "high_alchemy": "2", - "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, - "low_alchemy": "5", - "high_alchemy": "7", - "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, - "low_alchemy": "15", - "high_alchemy": "22", - "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, - "low_alchemy": "28", - "high_alchemy": "42", - "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, - "low_alchemy": "104", - "high_alchemy": "157", - "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, - "low_alchemy": "176", - "high_alchemy": "264", - "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, - "high_alchemy": "1", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "26", - "high_alchemy": "39", - "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, - "low_alchemy": "140", - "high_alchemy": "210", - "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, - "high_alchemy": "1", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "26", - "high_alchemy": "39", - "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, - "low_alchemy": "140", - "high_alchemy": "210", - "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "4", - "high_alchemy": "7", + "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", - "low_alchemy": "14", - "high_alchemy": "21", + "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", - "low_alchemy": "70", - "high_alchemy": "105", + "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, - "low_alchemy": "2", - "high_alchemy": "3", - "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, - "low_alchemy": "3", - "high_alchemy": "4", - "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, - "low_alchemy": "14", - "high_alchemy": "22", - "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, - "low_alchemy": "39", - "high_alchemy": "58", - "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, - "low_alchemy": "98", - "high_alchemy": "147", - "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, - "low_alchemy": "249", - "high_alchemy": "374", - "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, - "low_alchemy": "2", - "high_alchemy": "3", - "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, - "low_alchemy": "3", - "high_alchemy": "4", - "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, - "low_alchemy": "14", - "high_alchemy": "22", - "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, - "low_alchemy": "39", - "high_alchemy": "58", - "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, - "low_alchemy": "98", - "high_alchemy": "147", - "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, - "low_alchemy": "249", - "high_alchemy": "374", - "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", - "low_alchemy": "28", - "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, - "high_alchemy": "42", - "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", - "low_alchemy": "32", - "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, - "high_alchemy": "48", - "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", - "low_alchemy": "20", + "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, - "high_alchemy": "30", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "64", - "high_alchemy": "96", - "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, - "low_alchemy": "128", - "high_alchemy": "192", - "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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, - "low_alchemy": "256", - "high_alchemy": "384", - "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, - "low_alchemy": "160", - "high_alchemy": "240", - "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, - "low_alchemy": "512", - "high_alchemy": "768", - "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, - "low_alchemy": "320", - "high_alchemy": "480", - "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, - "low_alchemy": "1024", - "high_alchemy": "1536", - "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, - "low_alchemy": "640", - "high_alchemy": "960", - "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, - "low_alchemy": "23", - "high_alchemy": "34", - "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, - "low_alchemy": "5", - "high_alchemy": "8", - "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, - "low_alchemy": "27", - "high_alchemy": "40", - "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, - "low_alchemy": "10", - "high_alchemy": "16", - "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, - "low_alchemy": "26", - "high_alchemy": "40", - "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, - "low_alchemy": "66", - "high_alchemy": "100", - "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, - "low_alchemy": "7", - "high_alchemy": "11", - "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, - "low_alchemy": "5", - "high_alchemy": "8", - "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, - "low_alchemy": "23", - "high_alchemy": "34", - "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, - "low_alchemy": "27", - "high_alchemy": "40", - "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, - "low_alchemy": "10", - "high_alchemy": "16", - "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, - "low_alchemy": "7", - "high_alchemy": "11", - "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, - "low_alchemy": "26", - "high_alchemy": "40", - "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, - "low_alchemy": "66", - "high_alchemy": "100", - "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.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "12", - "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" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", + "name": "Bronze bolts", "examine": "Bronze crossbow bolts.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "38", - "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", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,10" + "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" }, { - "ge_buy_limit": "25000", - "examine": "Opal tipped bronze crossbow bolts", - "grand_exchange_price": "5", - "durability": null, - "name": "Opal bolts", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "4", - "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.", - "durability": null, - "low_alchemy": "5", - "high_alchemy": "7", - "equipment_slot": "13", - "grand_exchange_price": "32", - "name": "Pearl bolts", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "880", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,48" + "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", + "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!", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "equipment_slot": "13", - "grand_exchange_price": "42", - "name": "Barbed bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "881", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,12" + "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", + "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "4", - "equipment_slot": "13", - "grand_exchange_price": "10", - "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" - }, - { - "shop_price": "7", - "ge_buy_limit": "10000", + "name": "Bronze arrow", "examine": "Arrows with bronze heads.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "4", - "equipment_slot": "13", - "grand_exchange_price": "39", - "name": "Bronze arrow(p)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "883", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,7" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Arrows with iron heads.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "10", - "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" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Arrows with iron heads.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "41", - "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" - }, - { - "requirements": "{4,5}", - "shop_price": "46", - "ge_buy_limit": "10000", - "examine": "Arrows with steel heads.", - "durability": null, - "low_alchemy": "18", - "high_alchemy": "27", - "equipment_slot": "13", - "grand_exchange_price": "25", - "name": "Steel arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "886", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16" - }, - { - "requirements": "{4,5}", - "shop_price": "46", - "ge_buy_limit": "10000", - "examine": "Arrows with steel heads.", - "durability": null, - "low_alchemy": "18", - "high_alchemy": "27", - "equipment_slot": "13", - "grand_exchange_price": "69", - "name": "Steel arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "887", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16" - }, - { - "requirements": "{4,20}", - "shop_price": "76", - "ge_buy_limit": "10000", - "examine": "Arrows with mithril heads.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "equipment_slot": "13", - "grand_exchange_price": "30", - "name": "Mithril arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "888", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22" - }, - { - "requirements": "{4,20}", - "shop_price": "76", - "ge_buy_limit": "10000", - "examine": "Arrows with mithril heads.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "equipment_slot": "13", - "grand_exchange_price": "69", - "name": "Mithril arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "889", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22" - }, - { - "requirements": "{4,30}", - "shop_price": "172", - "ge_buy_limit": "10000", - "examine": "Arrows with adamantite heads.", - "durability": null, - "low_alchemy": "68", - "high_alchemy": "103", - "equipment_slot": "13", - "grand_exchange_price": "111", - "name": "Adamant arrow", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "890", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31" - }, - { - "requirements": "{4,30}", - "shop_price": "172", - "ge_buy_limit": "10000", - "examine": "Arrows with adamantite heads.", - "durability": null, - "low_alchemy": "68", - "high_alchemy": "103", - "equipment_slot": "13", - "grand_exchange_price": "109", - "name": "Adamant arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "891", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31" - }, - { - "requirements": "{4,40}", - "shop_price": "510", - "ge_buy_limit": "10000", - "examine": "Arrows with rune heads.", - "durability": null, - "low_alchemy": "204", - "high_alchemy": "306", - "equipment_slot": "13", - "grand_exchange_price": "212", - "name": "Rune arrow", - "tradeable": "true", - "archery_ticket_price": "40", - "id": "892", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49" - }, - { - "requirements": "{4,40}", - "shop_price": "510", - "ge_buy_limit": "10000", - "examine": "Arrows with rune heads.", - "durability": null, - "low_alchemy": "204", - "high_alchemy": "306", - "equipment_slot": "13", - "grand_exchange_price": "257", - "name": "Rune arrow(p)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "893", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,49" - }, - { - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "942", "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" }, { - "examine": "Some meaty worms.", + "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", "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "shop_price": "7", + "tradeable": "true" + }, + { + "id": "884", + "name": "Iron arrow", + "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": "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "shop_price": "46", + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,5}", + "shop_price": "46", + "tradeable": "true" + }, + { + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,20}", + "shop_price": "76", + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,20}", + "shop_price": "76", + "tradeable": "true" + }, + { + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "shop_price": "172", + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,30}", + "shop_price": "172", + "tradeable": "true" + }, + { + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "requirements": "{4,40}", + "shop_price": "510", + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "943", "name": "Worm", - "tradeable": "false", + "examine": "Ugh! It's wriggling!", + "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "22", - "high_alchemy": "33", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "7", - "high_alchemy": "10", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "18", - "high_alchemy": "27", - "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, - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "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": "100,000 or more: \"X x coins\" where X is the exact number of coins in the stack.", - "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", - "high_alchemy": "1", - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "187", - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1007", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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.", - "durability": null, - "high_alchemy": "1", - "weight": "0.9", - "equipment_slot": "7", - "grand_exchange_price": "208", - "name": "Blue skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1011", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "high_alchemy": "1", - "weight": "0.9", - "equipment_slot": "7", - "grand_exchange_price": "216", - "name": "Pink skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1013", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "high_alchemy": "1", - "weight": "0.9", - "equipment_slot": "7", - "grand_exchange_price": "30", - "name": "Black skirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1015", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "0", - "high_alchemy": "1", - "equipment_slot": "0", - "grand_exchange_price": "280", - "name": "Wizard hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1017", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "209", - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1019", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "504", - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1021", - "bonuses": "0,0,0,0,0,0,1,1,0,2,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": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1022" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A thick yellow cape.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "439", - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1023", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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", - "low_alchemy": "0", - "high_alchemy": "1", - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1061", - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1027", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "955", - "name": "Cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1029", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "8", - "high_alchemy": "12", - "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.", - "durability": null, - "low_alchemy": "12", - "high_alchemy": "18", - "weight": "0.9", - "equipment_slot": "7", - "grand_exchange_price": "2481", - "name": "Zamorak robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1033", - "bonuses": "0,0,0,2,0,0,0,0,3,0,0,0,3,0,0" - }, - { - "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, - "low_alchemy": "16", - "high_alchemy": "24", - "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" - }, - { - "shop_price": "1", - "examine": "A rabbit-like adornment.", - "durability": null, - "low_alchemy": "0", - "high_alchemy": "0", - "destroy": "true", - "weight": "0.2", - "equipment_slot": "0", - "destroy_message": "You can get a replacement from Diango.", - "name": "Bunny ears", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "1037" - }, - { - "shop_price": "1", - "ge_buy_limit": "2", - "examine": "A nice hat from a cracker.", - "durability": null, - "low_alchemy": "0", - "high_alchemy": "0", - "weight": "0.05", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "10", - "name": "Red partyhat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1038" - }, - { - "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, - "low_alchemy": "64", - "high_alchemy": "96", - "weight": "0.1", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "5", - "name": "Santa hat", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "low_alchemy": "180", - "high_alchemy": "270", - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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!", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.23", - "equipment_slot": "9", - "grand_exchange_price": "12", - "name": "Leather gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1059", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.34", - "equipment_slot": "10", - "grand_exchange_price": "144", - "name": "Leather boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1061", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0" - }, - { - "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!", + "tradeable": "true", + "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, - "low_alchemy": "7", - "high_alchemy": "10", - "weight": "0.22", "equipment_slot": "9", - "grand_exchange_price": "13", - "name": "Leather vambraces", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1063", - "bonuses": "0,0,0,0,4,2,2,1,0,0,0,0,0,0,0" - }, - { "ge_buy_limit": "5000", - "grand_exchange_price": "13", - "durability": null, - "name": "Leather vambraces", + "shop_price": "6", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1064" + "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", + "name": "Leather vambraces", + "examine": "Better than no armour!", + "archery_ticket_price": "0", + "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, - "low_alchemy": "1000", - "high_alchemy": "1500", - "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, - "low_alchemy": "112", - "high_alchemy": "168", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "9", "absorb": "1,0,1", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-21,-7,11,10,10,-4,10,0,0,0,0,0", + "durability": null, + "equip_audio": "2242", "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, - "low_alchemy": "1040", - "high_alchemy": "1560", - "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, - "low_alchemy": "2560", - "high_alchemy": "3840", - "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.", - "durability": null, - "low_alchemy": "32", - "high_alchemy": "48", - "weight": "9", - "equipment_slot": "7", - "grand_exchange_price": "34", - "name": "Bronze platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1075", - "bonuses": "0,0,0,-21,-7,8,7,6,-4,7,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "768", - "high_alchemy": "1152", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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.", + "tradeable": "true", + "weight": "9" + }, + { + "id": "1068", + "name": "Iron platelegs", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "112", - "high_alchemy": "168", - "weight": "8.1", + "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", - "grand_exchange_price": "86", - "name": "Iron plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1081", - "bonuses": "0,0,0,-21,-7,11,10,10,-4,10,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "86", - "durability": null, - "name": "Iron plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1082" - }, - { "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "1040", - "high_alchemy": "1560", - "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.", - "durability": null, - "low_alchemy": "32", - "high_alchemy": "48", - "weight": "8.1", - "equipment_slot": "7", - "grand_exchange_price": "113", - "name": "Bronze plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1087", - "bonuses": "0,0,0,-21,-7,8,7,6,-4,7,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "768", - "high_alchemy": "1152", - "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, - "low_alchemy": "2560", - "high_alchemy": "3840", - "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.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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" - }, - { + "bonuses": "0,0,0,-21,-7,11,10,10,-4,10,0,0,0,0,0", + "durability": null, + "equipment_slot": "7", "ge_buy_limit": "100", - "grand_exchange_price": "38100", - "durability": null, - "name": "Rune plateskirt", + "shop_price": "280", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1094" + "weight": "8.1" }, { - "shop_price": "23", - "ge_buy_limit": "5000", - "examine": "Better than no armour!", + "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, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "3", "equipment_slot": "7", - "grand_exchange_price": "5", - "name": "Leather chaps", + "ge_buy_limit": "100", + "requirements": "{1,5}", + "shop_price": "1000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1095", - "bonuses": "0,0,0,0,4,2,2,1,0,0,0,0,0,0,0" + "weight": "8.1" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "5", + "id": "1084", + "name": "Steel plateskirt", + "archery_ticket_price": "0", "durability": null, - "name": "Leather chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1096" + "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", + "examine": "Designer leg protection.", + "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": "100", + "lendable": "true", + "requirements": "{1,40}", + "shop_price": "64000", + "tradeable": "true", + "weight": "8" + }, + { + "id": "1094", + "name": "Rune plateskirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1095", + "name": "Leather chaps", + "examine": "Better than no armour!", + "archery_ticket_price": "0", + "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, - "low_alchemy": "300", - "high_alchemy": "450", - "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, - "low_alchemy": "1560", - "high_alchemy": "2340", - "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.", - "durability": null, - "low_alchemy": "84", - "high_alchemy": "126", - "weight": "6.8", - "equipment_slot": "4", - "grand_exchange_price": "76", - "name": "Iron chainbody", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1101", - "bonuses": "0,0,0,-15,0,10,15,19,-3,12,5,0,0,0,0" - }, - { - "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.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-15,0,10,15,19,-3,12,5,0,0,0,0", "durability": null, - "low_alchemy": "24", - "high_alchemy": "36", - "weight": "6.8", "equipment_slot": "4", - "grand_exchange_price": "37", - "name": "Bronze chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1103", - "bonuses": "0,0,0,-15,0,7,11,13,-3,9,5,0,0,0,0" - }, - { "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, - "low_alchemy": "300", - "high_alchemy": "450", - "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, - "low_alchemy": "576", - "high_alchemy": "864", - "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, - "low_alchemy": "780", - "high_alchemy": "1170", - "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, - "low_alchemy": "1920", - "high_alchemy": "2880", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "224", - "high_alchemy": "336", - "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, - "low_alchemy": "64", - "high_alchemy": "96", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "2080", - "high_alchemy": "3120", - "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, - "low_alchemy": "6656", - "high_alchemy": "9984", - "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, - "low_alchemy": "1536", - "high_alchemy": "2304", - "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "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!", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "2.7", - "equipment_slot": "4", - "grand_exchange_price": "34", - "name": "Leather body", - "tradeable": "true", - "archery_ticket_price": "0", "id": "1129", - "bonuses": "0,0,0,-2,2,8,9,10,4,9,5,0,0,0,0" - }, - { - "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, - "low_alchemy": "68", - "high_alchemy": "102", - "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, - "low_alchemy": "340", - "high_alchemy": "510", - "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, - "low_alchemy": "3120", - "high_alchemy": "4680", - "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, - "low_alchemy": "33", - "high_alchemy": "50", - "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, - "low_alchemy": "9", - "high_alchemy": "14", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "312", - "high_alchemy": "468", - "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, - "low_alchemy": "768", - "high_alchemy": "1152", - "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, - "low_alchemy": "7680", - "high_alchemy": "11520", - "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, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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, - "low_alchemy": "230", - "high_alchemy": "345", - "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, - "low_alchemy": "61", - "high_alchemy": "92", - "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, - "low_alchemy": "17", - "high_alchemy": "26", - "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, - "low_alchemy": "220", - "high_alchemy": "330", - "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, - "low_alchemy": "572", - "high_alchemy": "858", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "548", - "high_alchemy": "823", - "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, - "low_alchemy": "9", - "high_alchemy": "14", - "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "5", - "grand_exchange_price": "483", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "19", - "high_alchemy": "28", - "weight": "3", - "equipment_slot": "5", - "grand_exchange_price": "12", - "name": "Bronze sq shield", - "tradeable": "true", + "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" - }, - { - "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.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-6,-2,5,6,4,0,5,0,0,0,0,0", "durability": null, - "low_alchemy": "67", - "high_alchemy": "100", - "weight": "3.6", + "equip_audio": "2245", "equipment_slot": "5", - "grand_exchange_price": "39", - "name": "Iron sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1175", - "bonuses": "0,0,0,-6,-2,8,9,7,0,8,1,0,0,0,0" - }, - { "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, - "low_alchemy": "240", - "high_alchemy": "360", - "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, - "low_alchemy": "460", - "high_alchemy": "691", - "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, - "low_alchemy": "624", - "high_alchemy": "936", - "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, - "low_alchemy": "1536", - "high_alchemy": "2304", - "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, - "low_alchemy": "15360", - "high_alchemy": "23040", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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.", - "durability": null, - "low_alchemy": "27", - "high_alchemy": "40", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "18", - "name": "Bronze kiteshield", - "tradeable": "true", + "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" - }, - { - "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.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-8,-2,5,7,6,-1,6,1,0,0,0,0", "durability": null, - "low_alchemy": "95", - "high_alchemy": "142", - "weight": "5.4", + "equip_audio": "2245", "equipment_slot": "5", - "grand_exchange_price": "57", - "name": "Iron kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1191", - "bonuses": "0,0,0,-8,-2,8,10,9,-1,9,2,0,0,0,0" - }, - { "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, - "low_alchemy": "340", - "high_alchemy": "510", - "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, - "low_alchemy": "848", - "high_alchemy": "1272", - "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, - "low_alchemy": "884", - "high_alchemy": "1326", - "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, - "low_alchemy": "2176", - "high_alchemy": "3264", - "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, - "low_alchemy": "21760", - "high_alchemy": "32640", - "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, - "low_alchemy": "14", - "high_alchemy": "21", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "50", - "high_alchemy": "75", - "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, - "low_alchemy": "130", - "high_alchemy": "195", - "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, - "low_alchemy": "320", - "high_alchemy": "480", - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "96", - "high_alchemy": "144", - "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, - "low_alchemy": "14", - "high_alchemy": "21", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "50", - "high_alchemy": "75", - "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, - "low_alchemy": "130", - "high_alchemy": "195", - "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, - "low_alchemy": "320", - "high_alchemy": "480", - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "96", - "high_alchemy": "144", - "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", - "low_alchemy": "10", - "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, - "high_alchemy": "15", - "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", - "low_alchemy": "36", - "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": "209", - "stand_anim": "813", - "tradeable": "true", - "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", - "durability": null, - "high_alchemy": "54", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Iron spear" - }, - { - "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", - "low_alchemy": "130", - "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, - "high_alchemy": "195", - "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", - "low_alchemy": "338", - "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, - "high_alchemy": "507", - "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", - "low_alchemy": "832", - "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, - "high_alchemy": "1248", - "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", - "low_alchemy": "8320", - "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, - "high_alchemy": "12480", - "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", - "low_alchemy": "24960", - "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, - "high_alchemy": "37440", - "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", - "low_alchemy": "10", - "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": "389", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1251", - "stand_turn_anim": "1209", + "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, - "high_alchemy": "15", - "weight": "2.2", - "weapon_interface": "14", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", "render_anim": "28", - "name": "Bronze spear(p)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "389", - "durability": null, - "name": "Bronze spear(p)", + "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", - "low_alchemy": "36", - "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": "212", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1253", - "stand_turn_anim": "1209", + "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, - "high_alchemy": "54", - "weight": "2.2", - "weapon_interface": "14", + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", "render_anim": "28", - "name": "Iron spear(p)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "212", - "durability": null, - "name": "Iron spear(p)", + "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" }, { + "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", - "low_alchemy": "130", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "attack_anims": "428,2081,429,428", - "grand_exchange_price": "136", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", "archery_ticket_price": "0", - "id": "1255", - "stand_turn_anim": "1209", + "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", + "defence_anim": "2079", + "durability": null, + "equip_audio": "2247", + "equipment_slot": "3", + "ge_buy_limit": "100", + "render_anim": "28", "requirements": "{0,5}", - "durability": null, - "high_alchemy": "195", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Steel spear(p)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "136", - "durability": null, - "name": "Steel spear(p)", + "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", - "low_alchemy": "338", - "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, - "high_alchemy": "507", - "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", - "low_alchemy": "832", - "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, - "high_alchemy": "1248", - "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", - "low_alchemy": "8320", - "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, - "high_alchemy": "12480", - "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", - "low_alchemy": "24960", - "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, - "high_alchemy": "37440", - "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, - "low_alchemy": "56", - "high_alchemy": "84", - "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, - "low_alchemy": "200", - "high_alchemy": "300", - "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, - "low_alchemy": "1280", - "high_alchemy": "1920", - "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, - "low_alchemy": "520", - "high_alchemy": "780", - "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, - "low_alchemy": "12800", - "high_alchemy": "19200", - "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", - "low_alchemy": "10", - "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, - "high_alchemy": "15", - "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", - "low_alchemy": "36", - "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, - "high_alchemy": "54", - "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", - "low_alchemy": "130", - "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, - "high_alchemy": "195", - "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", - "low_alchemy": "249", - "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, - "high_alchemy": "374", - "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", - "low_alchemy": "338", - "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, - "high_alchemy": "507", - "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", - "low_alchemy": "832", - "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, - "high_alchemy": "1248", - "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", - "low_alchemy": "8320", - "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, - "high_alchemy": "12480", - "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", - "low_alchemy": "16", - "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, - "high_alchemy": "24", - "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", - "low_alchemy": "56", - "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, - "high_alchemy": "84", - "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", - "low_alchemy": "200", - "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, - "high_alchemy": "300", - "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", - "low_alchemy": "384", - "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, - "high_alchemy": "576", - "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", - "low_alchemy": "520", - "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, - "high_alchemy": "780", - "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", - "low_alchemy": "1280", - "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, - "high_alchemy": "1920", - "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", - "low_alchemy": "12800", - "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, - "high_alchemy": "19200", - "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", - "low_alchemy": "40000", - "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, - "high_alchemy": "60000", - "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", - "low_alchemy": "32", - "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, - "high_alchemy": "48", - "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", - "low_alchemy": "112", - "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, - "high_alchemy": "168", - "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", - "low_alchemy": "400", - "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, - "high_alchemy": "600", - "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", - "low_alchemy": "768", - "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, - "high_alchemy": "1152", - "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", - "low_alchemy": "1040", - "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, - "high_alchemy": "1560", - "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", - "low_alchemy": "2560", - "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, - "high_alchemy": "3840", - "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", - "low_alchemy": "25600", - "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, - "high_alchemy": "38400", - "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", - "low_alchemy": "12", - "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, - "high_alchemy": "18", - "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", - "low_alchemy": "44", - "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, - "high_alchemy": "67", - "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", - "low_alchemy": "160", - "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, - "high_alchemy": "240", - "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", - "low_alchemy": "307", - "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, - "high_alchemy": "460", - "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", - "low_alchemy": "416", - "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, - "high_alchemy": "624", - "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", - "low_alchemy": "1024", - "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, - "high_alchemy": "1536", - "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", - "low_alchemy": "10240", - "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, - "high_alchemy": "15360", - "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", - "low_alchemy": "89", - "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, - "high_alchemy": "134", - "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", - "low_alchemy": "24", - "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, - "high_alchemy": "36", - "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", - "low_alchemy": "256", - "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, - "high_alchemy": "384", - "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", - "low_alchemy": "509", - "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, - "high_alchemy": "764", - "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", - "low_alchemy": "863", - "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, - "high_alchemy": "1294", - "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", - "low_alchemy": "2142", - "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, - "high_alchemy": "3213", - "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", - "low_alchemy": "16600", - "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, - "high_alchemy": "24900", - "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 hatchet.", - "durability": null, - "low_alchemy": "22", - "high_alchemy": "33", - "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", + "examine": "A woodcutter's axe.", "archery_ticket_price": "0", - "id": "1350" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "A woodcutter's hatchet.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "1.35", - "attack_speed": "5", - "weapon_interface": "2", - "render_anim": "2586", - "defence_anim": "397", - "equipment_slot": "3", "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 hatchet.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "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 hatchet.", - "durability": null, - "low_alchemy": "208", - "high_alchemy": "312", - "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 hatchet.", - "durability": null, - "low_alchemy": "512", - "high_alchemy": "768", - "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 hatchet.", - "durability": null, - "low_alchemy": "5120", - "high_alchemy": "7680", - "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 hatchet.", - "durability": null, - "low_alchemy": "153", - "high_alchemy": "230", - "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, - "low_alchemy": "72", - "high_alchemy": "109", - "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, - "low_alchemy": "260", - "high_alchemy": "390", - "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, - "low_alchemy": "499", - "high_alchemy": "748", - "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, - "low_alchemy": "676", - "high_alchemy": "1014", - "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, - "low_alchemy": "1664", - "high_alchemy": "2496", - "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, - "low_alchemy": "16640", - "high_alchemy": "24960", - "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, - "low_alchemy": "20", - "high_alchemy": "31", - "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, - "low_alchemy": "80000", - "high_alchemy": "120000", - "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", - "low_alchemy": "6", - "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, - "high_alchemy": "9", - "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", - "low_alchemy": "600", - "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, - "high_alchemy": "900", - "weight": "2.2", - "weapon_interface": "1", - "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", - "low_alchemy": "600", - "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, - "high_alchemy": "900", - "weight": "2.2", - "weapon_interface": "1", + "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", - "low_alchemy": "600", - "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, - "high_alchemy": "900", - "weight": "2.2", - "weapon_interface": "1", - "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": "1146", - "low_alchemy": "600", - "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": "809", - "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, - "high_alchemy": "900", - "weight": "2.2", - "weapon_interface": "1", - "render_anim": "1426", - "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", - "low_alchemy": "80", - "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, - "high_alchemy": "120", - "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", - "low_alchemy": "2800", - "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,10,0,2,3,1,10,0,0,32,0,0,0", - "requirements": "{0,30}-{6,30}", - "shop_price": "7000", "durability": null, - "high_alchemy": "4200", - "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", - "low_alchemy": "6200", - "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,10,0,2,3,1,10,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "shop_price": "19600", - "durability": null, - "high_alchemy": "9300", - "weight": "2.25", - "weapon_interface": "1", - "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", - "low_alchemy": "6200", - "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,10,0,2,3,1,10,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "high_alchemy": "9300", - "weight": "2.2", - "weapon_interface": "1", - "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", - "low_alchemy": "6200", - "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,10,0,2,3,1,10,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "shop_price": "7000", - "durability": null, - "high_alchemy": "9300", - "weight": "2.25", - "weapon_interface": "1", - "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", - "low_alchemy": "6200", - "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,10,0,2,3,1,10,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", "shop_price": "1500", - "durability": null, - "high_alchemy": "9300", - "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", - "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", - "low_alchemy": "17000", - "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,10,0,2,3,1,10,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "durability": null, - "high_alchemy": "25500", - "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" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "low_alchemy": "17000", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "24800", - "stand_anim": "813", - "tradeable": "true", - "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,10,0,2,3,1,10,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "durability": null, - "high_alchemy": "25500", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Mystic water staff" - }, - { - "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", - "low_alchemy": "17000", - "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,10,0,2,3,1,10,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "durability": null, - "high_alchemy": "25500", - "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", - "low_alchemy": "17000", - "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", - "bonuses": "10,-1,40,10,0,2,3,1,10,0,0,50,0,0,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}", - "shop_price": "40000", - "durability": null, - "high_alchemy": "25500", - "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, - "low_alchemy": "17000", - "high_alchemy": "25500", - "weight": "2.2", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "attack_audios": "2555,0,0,0", - "name": "Iban's staff", "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", - "low_alchemy": "17000", - "high_alchemy": "25500", - "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, - "low_alchemy": "25", - "high_alchemy": "37", - "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, - "low_alchemy": "7", - "high_alchemy": "10", - "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, - "low_alchemy": "90", - "high_alchemy": "135", - "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, - "low_alchemy": "172", - "high_alchemy": "258", - "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, - "low_alchemy": "234", - "high_alchemy": "351", - "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, - "low_alchemy": "576", - "high_alchemy": "864", - "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, - "low_alchemy": "5760", - "high_alchemy": "8640", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "10", - "high_alchemy": "15", + "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", - "low_alchemy": "57", - "high_alchemy": "86", + "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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", - "low_alchemy": "1", - "high_alchemy": "2", "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.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "equipment_slot": "2", - "grand_exchange_price": "2870", - "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" - }, - { - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "5", - "archery_ticket_price": "0", - "id": "1481" - }, - { "examine": "A magical sphere that glimmers within.", - "durability": null, - "name": "Orb of light", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "1488" - }, - { "examine": "A coat of arms of the Ardougne Paladins.", - "durability": null, - "name": "Paladin's badge", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "2", "archery_ticket_price": "0", - "id": "1511" - }, - { + "durability": null, "ge_buy_limit": "25000", - "grand_exchange_price": "279", - "durability": null, - "name": "Logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1512" - }, - { - "ge_buy_limit": "25000", - "examine": "Logs cut from a magic tree.", - "grand_exchange_price": "680", - "durability": null, - "name": "Magic logs", - "tradeable": "true", - "low_alchemy": "128", - "high_alchemy": "192", - "weight": "2", - "archery_ticket_price": "0", - "id": "1513" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "1581", - "durability": null, - "name": "Magic logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1514" - }, - { - "ge_buy_limit": "25000", - "examine": "Logs cut from a yew tree.", - "grand_exchange_price": "320", - "durability": null, - "name": "Yew logs", - "tradeable": "true", - "low_alchemy": "64", - "high_alchemy": "96", - "weight": "2", - "archery_ticket_price": "0", - "id": "1515" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "532", - "durability": null, - "name": "Yew logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1516" - }, - { - "ge_buy_limit": "25000", - "examine": "Logs cut from a maple tree.", - "grand_exchange_price": "160", - "durability": null, - "name": "Maple logs", - "tradeable": "true", - "low_alchemy": "32", - "high_alchemy": "48", - "weight": "2", - "archery_ticket_price": "0", - "id": "1517" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "20", - "durability": null, - "name": "Maple logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1518" - }, - { - "ge_buy_limit": "25000", - "examine": "Logs cut from a willow tree.", - "grand_exchange_price": "80", - "durability": null, - "name": "Willow logs", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "24", - "weight": "2", - "archery_ticket_price": "0", - "id": "1519" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "11", - "durability": null, - "name": "Willow logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1520" - }, - { - "ge_buy_limit": "25000", - "examine": "Logs cut from an Oak Tree.", - "grand_exchange_price": "140", - "durability": null, - "name": "Oak logs", - "tradeable": "true", - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "2", - "archery_ticket_price": "0", - "id": "1521" - }, - { - "ge_buy_limit": "25000", - "grand_exchange_price": "153", - "durability": null, - "name": "Oak logs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1522" - }, - { - "shop_price": "20", - "ge_buy_limit": "100", - "examine": "For picking tough locks.", - "grand_exchange_price": "1311", - "durability": null, - "name": "Lockpick", - "tradeable": "true", - "low_alchemy": "8", - "high_alchemy": "12", - "archery_ticket_price": "0", - "id": "1523" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1311", - "durability": null, - "name": "Lockpick", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1524" - }, - { - "examine": "I need to clean this herb before I can use it.", - "durability": null, - "name": "Grimy snake weed", - "archery_ticket_price": "0", - "id": "1525" - }, - { - "examine": "A fresh herb.", - "durability": null, - "name": "Clean snake weed", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "1526" - }, - { - "examine": "I need to clean this herb before I can use it.", - "durability": null, - "name": "Grimy ardrigal", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1527" - }, - { - "examine": "A fresh herb.", - "durability": null, - "name": "Clean ardrigal", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1528" - }, - { - "examine": "I need to clean this herb before I can use it.", - "durability": null, - "name": "Grimy sito foil", - "archery_ticket_price": "0", - "id": "1529" - }, - { - "examine": "A fresh herb.", - "durability": null, - "name": "Clean sito foil", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "1530" - }, - { - "examine": "I need to clean this herb before I can use it.", - "durability": null, - "name": "Grimy volencia moss", - "archery_ticket_price": "0", - "id": "1531" - }, - { - "examine": "A fresh herb.", - "durability": null, - "name": "Clean volencia moss", - "low_alchemy": "2", - "high_alchemy": "3", - "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" - }, - { - "examine": "A fresh herb.", - "durability": null, - "name": "Clean rogue's purse", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "1534" - }, - { - "examine": "A piece of map.", - "durability": null, - "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" - }, - { - "examine": "A piece of map.", - "durability": null, - "name": "Map part", - "archery_ticket_price": "0", - "id": "1537" - }, - { - "examine": "A map of the route to Crandor.", - "durability": null, - "name": "Crandor map", - "archery_ticket_price": "0", - "id": "1538" - }, - { - "shop_price": "52", - "ge_buy_limit": "10000", - "examine": "Keeps things in place fairly permanently.", - "grand_exchange_price": "59", - "durability": null, - "name": "Steel nails", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "31", - "archery_ticket_price": "0", - "id": "1539" - }, - { - "ge_buy_limit": "100", - "examine": "This provides partial protection from dragon-breath attacks.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "637", - "name": "Anti-dragon shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1540", - "bonuses": "0,0,0,0,0,7,9,8,2,8,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "637", - "durability": null, - "name": "Anti-dragon shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1541" - }, - { - "shop_price": "1", - "examine": "A key to Melzar's Maze.", - "durability": null, - "name": "Maze key", - "archery_ticket_price": "0", - "id": "1542" - }, - { - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1552" - }, - { - "examine": "This kitten seems to like you.", - "durability": null, - "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" - }, - { - "examine": "This kitten seems to like you.", - "durability": null, - "name": "Pet kitten", - "archery_ticket_price": "0", - "id": "1557" - }, - { - "examine": "This kitten seems to like you.", - "durability": null, - "name": "Pet kitten", - "archery_ticket_price": "0", - "id": "1558" - }, - { - "examine": "This kitten seems to like you.", - "durability": null, - "name": "Pet kitten", - "archery_ticket_price": "0", - "id": "1559" - }, - { - "examine": "This kitten seems to like you.", - "durability": null, - "name": "Pet kitten", - "archery_ticket_price": "0", - "id": "1560" - }, - { - "examine": "This cat definitely likes you.", - "durability": null, - "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" - }, - { - "examine": "This cat definitely likes you.", - "durability": null, - "name": "Pet cat", - "archery_ticket_price": "0", - "id": "1563" - }, - { - "examine": "This cat definitely likes you.", - "durability": null, - "name": "Pet cat", - "archery_ticket_price": "0", - "id": "1564" - }, - { - "examine": "This cat definitely likes you.", - "durability": null, - "name": "Pet cat", - "archery_ticket_price": "0", - "id": "1565" - }, - { - "examine": "This cat definitely likes you.", - "durability": null, - "name": "Pet cat", - "archery_ticket_price": "0", - "id": "1566" - }, - { - "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "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" - }, - { - "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", - "archery_ticket_price": "0", - "id": "1569" - }, - { - "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", - "archery_ticket_price": "0", - "id": "1570" - }, - { - "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", - "archery_ticket_price": "0", - "id": "1571" - }, - { - "examine": "This cat is so well fed it can hardly move.", - "durability": null, - "name": "Overgrown cat", - "archery_ticket_price": "0", - "id": "1572" - }, - { - "examine": "A tasty herb good for seasoning.", - "durability": null, - "name": "Doogle leaves", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "1573" - }, - { - "examine": "For feline training expertise.", - "durability": null, - "name": "Cat training medal", - "low_alchemy": "140", - "high_alchemy": "210", - "archery_ticket_price": "0", - "id": "1575", - "equipment_slot": "2" - }, - { - "examine": "Candlestick used during Heroes' Quest.", - "durability": null, - "name": "Pete's candlestick", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "1577" - }, - { - "durability": null, - "name": "Pete's candlestick", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1578" - }, - { - "examine": "This denotes a Master Thief.", - "durability": null, - "name": "Thieves' armband", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "1579" - }, - { - "examine": "These will keep my hands cold!", - "durability": null, - "name": "Ice gloves", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1580", - "bonuses": "0,0,0,0,0,0,3,4,0,2,2,0,0,0,0", - "equipment_slot": "9" - }, - { - "examine": "Yuck.", - "durability": null, - "name": "Blamish snail slime", - "weight": "1", - "archery_ticket_price": "0", - "id": "1581" - }, - { - "examine": "Made from the finest snail slime.", - "durability": null, - "name": "Blamish oil", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "1582" - }, - { - "examine": "Firebird feather.", - "durability": null, - "name": "Fire feather", - "archery_ticket_price": "0", - "id": "1583" - }, - { - "examine": "Apparently my name is Hartigan", - "durability": null, - "name": "Id papers", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1584" - }, - { - "examine": "Useful for catching lava eels.", - "durability": null, - "name": "Oily fishing rod", - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "1", - "archery_ticket_price": "0", - "id": "1585" - }, - { - "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, - "name": "Miscellaneous key", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1587" - }, - { - "examine": "The key I got from Grip. (Heroes' Quest)", - "durability": null, - "name": "Grips' key ring", - "archery_ticket_price": "0", - "id": "1588" - }, - { - "shop_price": "1", - "examine": "I wonder what this unlocks. (Heroes' Quest)", - "durability": null, - "name": "Dusty key", - "archery_ticket_price": "0", - "id": "1590" - }, - { - "examine": "Key to a cell. (Heroes' Quest)", - "durability": null, - "name": "Jail key", - "archery_ticket_price": "0", - "id": "1591" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Used to make gold rings.", - "grand_exchange_price": "316", - "durability": null, - "name": "Ring mould", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1592" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "316", - "durability": null, - "name": "Ring mould", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1593" - }, - { - "examine": "Used to make unholy symbols.", - "durability": null, - "name": "Unholy mould", - "low_alchemy": "80", - "high_alchemy": "120", - "archery_ticket_price": "0", - "id": "1594" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Used to make gold amulets.", - "grand_exchange_price": "288", - "durability": null, - "name": "Amulet mould", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1595" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "288", - "durability": null, - "name": "Amulet mould", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1596" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Used to make gold necklaces.", - "grand_exchange_price": "269", - "durability": null, - "name": "Necklace mould", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1597" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "269", - "durability": null, - "name": "Necklace mould", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1598" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Used to make holy symbols of Saradomin.", - "grand_exchange_price": "504", - "durability": null, - "name": "Holy mould", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1599" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "504", - "durability": null, - "name": "Holy mould", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1600" - }, - { - "ge_buy_limit": "500", - "examine": "This looks valuable.", - "grand_exchange_price": "14800", - "durability": null, - "name": "Diamond", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "archery_ticket_price": "0", - "id": "1601" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "14800", - "durability": null, - "name": "Diamond", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1602" - }, - { - "shop_price": "400", - "ge_buy_limit": "1000", - "examine": "This looks valuable.", - "grand_exchange_price": "1598", - "durability": null, - "name": "Ruby", - "tradeable": "true", - "low_alchemy": "400", - "high_alchemy": "600", - "archery_ticket_price": "0", - "id": "1603" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1598", - "durability": null, - "name": "Ruby", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1604" - }, - { - "shop_price": "350", - "ge_buy_limit": "1000", - "examine": "This looks valuable.", - "grand_exchange_price": "853", - "durability": null, - "name": "Emerald", - "tradeable": "true", - "low_alchemy": "200", - "high_alchemy": "300", - "archery_ticket_price": "0", - "id": "1605" - }, - { - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "archery_ticket_price": "0", - "id": "1607" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "520", - "durability": null, - "name": "Sapphire", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1608" - }, - { - "ge_buy_limit": "1000", - "examine": "A semi precious stone.", - "grand_exchange_price": "44", - "durability": null, - "name": "Opal", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "1609" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "41", - "durability": null, - "name": "Opal", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1610" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "84", - "durability": null, - "name": "Jade", - "archery_ticket_price": "0", - "id": "1611" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "83", - "durability": null, - "name": "Jade", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1612" - }, - { - "ge_buy_limit": "1000", - "examine": "A semi precious stone.", - "grand_exchange_price": "120", - "durability": null, - "name": "Red topaz", - "tradeable": "true", - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "1613" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "103", - "durability": null, - "name": "Red topaz", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1614" - }, - { - "shop_price": "75000", - "ge_buy_limit": "100", - "examine": "This looks valuable.", - "grand_exchange_price": "27400", - "durability": null, - "name": "Dragonstone", - "tradeable": "true", - "low_alchemy": "4000", - "high_alchemy": "6000", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "1615" - }, - { - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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.", - "tokkul_price": "1500", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "0.3", - "grand_exchange_price": "31700", - "name": "Uncut dragonstone", - "tradeable": "true", - "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", - "low_alchemy": "140", - "high_alchemy": "210", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "810", - "high_alchemy": "1215", - "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", - "low_alchemy": "1410", - "high_alchemy": "2115", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "archery_ticket_price": "0", - "id": "1649" - }, - { - "examine": "A valuable ring.", - "grand_exchange_price": "993", - "durability": null, - "name": "Emerald ring", - "tradeable": "true", - "low_alchemy": "510", - "high_alchemy": "765", - "archery_ticket_price": "0", - "id": "1650" - }, - { - "shop_price": "2025", - "examine": "A valuable ring.", - "grand_exchange_price": "1125", - "durability": null, - "name": "Ruby ring", - "tradeable": "true", - "low_alchemy": "810", - "high_alchemy": "1215", - "archery_ticket_price": "0", - "id": "1651" - }, - { - "shop_price": "3172", - "examine": "A valuable ring.", - "grand_exchange_price": "3622", - "durability": null, - "name": "Diamond ring", - "tradeable": "true", - "low_alchemy": "1410", - "high_alchemy": "2115", - "archery_ticket_price": "0", - "id": "1652" - }, - { - "shop_price": "7750", - "examine": "A valuable ring.", - "grand_exchange_price": "19606", - "durability": null, - "name": "Dragonstone ring", - "tradeable": "true", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "180", - "high_alchemy": "270", - "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", - "low_alchemy": "420", - "high_alchemy": "630", - "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", - "low_alchemy": "570", - "high_alchemy": "855", - "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", - "low_alchemy": "870", - "high_alchemy": "1305", - "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", - "low_alchemy": "1470", - "high_alchemy": "2205", - "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", - "low_alchemy": "7350", - "high_alchemy": "11025", - "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", - "low_alchemy": "420", - "high_alchemy": "630", - "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", - "low_alchemy": "570", - "high_alchemy": "855", - "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", - "low_alchemy": "870", - "high_alchemy": "1305", - "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", - "low_alchemy": "1470", - "high_alchemy": "2205", - "archery_ticket_price": "0", - "id": "1671" - }, - { - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "23010", - "durability": null, - "name": "Dragon necklace", - "tradeable": "true", - "low_alchemy": "7350", - "high_alchemy": "11025", - "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", - "low_alchemy": "140", - "high_alchemy": "210", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "810", - "high_alchemy": "1215", - "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", - "low_alchemy": "1410", - "high_alchemy": "2115", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "810", - "high_alchemy": "1215", - "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", - "low_alchemy": "1410", - "high_alchemy": "2115", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "140", - "high_alchemy": "210", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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.", - "durability": null, - "low_alchemy": "810", - "high_alchemy": "1215", - "destroy": "true", - "equipment_slot": "2", - "grand_exchange_price": "1115", - "name": "Ruby amulet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1698" - }, - { - "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", - "low_alchemy": "1410", - "high_alchemy": "2115", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "equipment_slot": "2", - "grand_exchange_price": "88", - "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" - }, - { - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "equipment_slot": "2", - "grand_exchange_price": "57", - "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" - }, - { - "ge_buy_limit": "5000", - "examine": "An enchanted ruby amulet.", - "grand_exchange_price": "1247", - "durability": null, - "name": "Amulet of strength", - "tradeable": "true", - "low_alchemy": "810", - "high_alchemy": "1215", - "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.", - "durability": null, - "low_alchemy": "360", - "high_alchemy": "540", - "equipment_slot": "2", - "grand_exchange_price": "519", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "510", - "high_alchemy": "765", - "equipment_slot": "2", - "grand_exchange_price": "721", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1410", - "high_alchemy": "2115", - "equipment_slot": "2", - "grand_exchange_price": "2573", - "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" - }, - { - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "archery_ticket_price": "0", - "id": "1734" + "weight": "2" }, { - "shop_price": "1", - "ge_buy_limit": "100", - "examine": "For shearing sheep.", - "grand_exchange_price": "76", - "durability": null, - "name": "Shears", - "tradeable": "true", - "weight": "0.1", + "id": "1512", + "name": "Logs", "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "32", - "high_alchemy": "48", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "5", - "high_alchemy": "8", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "1779" + "tradeable": "true" }, { + "id": "1513", + "name": "Magic logs", + "examine": "Logs cut from a magic tree.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "25000", - "grand_exchange_price": "73", - "durability": null, - "name": "Flax", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1780" + "weight": "2" }, { + "id": "1514", + "name": "Magic logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "1515", + "name": "Yew logs", + "examine": "Logs cut from a yew tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "1516", + "name": "Yew logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "1517", + "name": "Maple logs", + "examine": "Logs cut from a maple tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "1518", + "name": "Maple logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "1519", + "name": "Willow logs", + "examine": "Logs cut from a willow tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "1520", + "name": "Willow logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "1521", + "name": "Oak logs", + "examine": "Logs cut from an Oak Tree.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true", + "weight": "2" + }, + { + "id": "1522", + "name": "Oak logs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "25000", + "tradeable": "true" + }, + { + "id": "1523", + "name": "Lockpick", + "examine": "For picking tough locks.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "20", + "tradeable": "true" + }, + { + "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", + "durability": null + }, + { + "id": "1526", + "name": "Clean snake weed", + "examine": "A fresh herb.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1527", + "name": "Grimy ardrigal", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.2" + }, + { + "id": "1528", + "name": "Clean ardrigal", + "examine": "A fresh herb.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.2" + }, + { + "id": "1529", + "name": "Grimy sito foil", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1530", + "name": "Clean sito foil", + "examine": "A fresh herb.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1531", + "name": "Grimy volencia moss", + "examine": "I need to clean this herb before I can use it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1532", + "name": "Clean volencia moss", + "examine": "A fresh herb.", + "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", + "durability": null + }, + { + "id": "1535", + "name": "Map part", + "examine": "A piece of map.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1536", + "name": "Map part", + "examine": "A piece of map.", + "archery_ticket_price": "0", + "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", + "durability": null + }, + { + "id": "1539", + "name": "Steel nails", + "examine": "Keeps things in place fairly permanently.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10000", - "examine": "One of the ingredients for making glass.", - "grand_exchange_price": "575", - "durability": null, - "name": "Soda ash", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.09", - "archery_ticket_price": "0", - "id": "1781" + "shop_price": "52", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "575", - "durability": null, - "name": "Soda ash", - "tradeable": "true", + "id": "1540", + "name": "Anti-dragon shield", + "examine": "This provides partial protection from dragon-breath attacks.", "archery_ticket_price": "0", - "id": "1782" + "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" }, { - "ge_buy_limit": "10000", - "examine": "One of the ingredients for making glass.", - "grand_exchange_price": "265", - "durability": null, - "name": "Bucket of sand", - "tradeable": "true", - "high_alchemy": "1", - "weight": "2.5", + "id": "1541", + "name": "Anti-dragon shield", "archery_ticket_price": "0", - "id": "1783" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "265", - "durability": null, - "name": "Bucket of sand", - "tradeable": "true", + "id": "1542", + "name": "Maze key", + "examine": "A key to Melzar's Maze.", "archery_ticket_price": "0", - "id": "1784" + "durability": null, + "shop_price": "1" }, { + "id": "1549", + "name": "Stake", + "examine": "A very pointy stick.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another from Dr Harlow in the Blue Moon Inn of Varrock.", + "durability": null, + "equipment_slot": "5", + "tradeable": "false" + }, + { + "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", + "examine": "This kitten seems to like you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1556", + "name": "Pet kitten", + "examine": "This kitten seems to like you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1557", + "name": "Pet kitten", + "examine": "This kitten seems to like you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1558", + "name": "Pet kitten", + "examine": "This kitten seems to like you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1559", + "name": "Pet kitten", + "examine": "This kitten seems to like you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1560", + "name": "Pet kitten", + "examine": "This kitten seems to like you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1561", + "name": "Pet cat", + "examine": "This cat definitely likes you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1562", + "name": "Pet cat", + "examine": "This cat definitely likes you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1563", + "name": "Pet cat", + "examine": "This cat definitely likes you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1564", + "name": "Pet cat", + "examine": "This cat definitely likes you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1565", + "name": "Pet cat", + "examine": "This cat definitely likes you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1566", + "name": "Pet cat", + "examine": "This cat definitely likes you.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1567", + "name": "Overgrown cat", + "examine": "This cat is so well fed it can hardly move.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1568", + "name": "Overgrown cat", + "examine": "This cat is so well fed it can hardly move.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1569", + "name": "Overgrown cat", + "examine": "This cat is so well fed it can hardly move.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1570", + "name": "Overgrown cat", + "examine": "This cat is so well fed it can hardly move.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1571", + "name": "Overgrown cat", + "examine": "This cat is so well fed it can hardly move.", + "archery_ticket_price": "0", + "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", + "durability": null + }, + { + "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.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1578", + "name": "Pete's candlestick", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1579", + "name": "Thieves' armband", + "examine": "This denotes a Master Thief.", + "archery_ticket_price": "0", + "durability": null + }, + { + "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", + "durability": null, + "equipment_slot": "9", + "weight": "0.2" + }, + { + "id": "1581", + "name": "Blamish snail slime", + "examine": "Yuck.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "1582", + "name": "Blamish oil", + "examine": "Made from the finest snail slime.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1583", + "name": "Fire feather", + "examine": "Firebird feather.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1584", + "name": "Id papers", + "examine": "Apparently my name is Hartigen.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "1585", + "name": "Oily fishing rod", + "examine": "Useful for catching lava eels.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "1586", + "name": "Miscellaneous key", + "examine": "This is of use in completing Heroes' Quest. (Heroes' Quest)", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.01" + }, + { + "id": "1587", + "name": "Miscellaneous key", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1588", + "name": "Grips' key ring", + "examine": "The key I got from Grip. (Heroes' Quest)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1590", + "name": "Dusty key", + "examine": "I wonder what this unlocks. (Heroes' Quest)", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1" + }, + { + "id": "1591", + "name": "Jail key", + "examine": "Key to a cell. (Heroes' Quest)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "1592", + "name": "Ring mould", + "examine": "Used to make gold rings.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "shop_price": "5", + "tradeable": "true", + "weight": "0.4" + }, + { + "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", + "durability": null + }, + { + "id": "1595", + "name": "Amulet mould", + "examine": "Used to make gold amulets.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true", + "weight": "0.4" + }, + { + "id": "1600", + "name": "Holy mould", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1601", + "name": "Diamond", + "examine": "This looks valuable.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "175", + "tradeable": "true" + }, + { + "id": "1608", + "name": "Sapphire", + "archery_ticket_price": "0", + "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", + "tradeable": "true" + }, + { + "id": "1610", + "name": "Opal", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1611", + "name": "Jade", + "examine": "A semi precious stone.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "150", + "tradeable": "true" + }, + { + "id": "1612", + "name": "Jade", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "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", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "1614", + "name": "Red topaz", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1615", + "name": "Dragonstone", + "examine": "This looks valuable.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "75000", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1616", + "name": "Dragonstone", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1617", + "name": "Uncut diamond", + "examine": "An uncut diamond.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tokkul_price": "300", + "tradeable": "true" + }, + { + "id": "1618", + "name": "Uncut diamond", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "500", + "tradeable": "true" + }, + { + "id": "1619", + "name": "Uncut ruby", + "examine": "An uncut ruby.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tokkul_price": "150", + "tradeable": "true" + }, + { + "id": "1620", + "name": "Uncut ruby", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1621", + "name": "Uncut emerald", + "examine": "An uncut emerald.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "50", + "tokkul_price": "75", + "tradeable": "true" + }, + { + "id": "1622", + "name": "Uncut emerald", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1623", + "name": "Uncut sapphire", + "examine": "An uncut sapphire.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "25", + "tokkul_price": "37", + "tradeable": "true" + }, + { + "id": "1624", + "name": "Uncut sapphire", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1625", + "name": "Uncut opal", + "examine": "An uncut opal.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1626", + "name": "Uncut opal", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1627", + "name": "Uncut jade", + "examine": "An uncut jade.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1628", + "name": "Uncut jade", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1629", + "name": "Uncut red topaz", + "examine": "An uncut red topaz.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "1630", + "name": "Uncut red topaz", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1631", + "name": "Uncut dragonstone", + "examine": "An uncut dragonstone.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "75000", + "tokkul_price": "1500", + "tradeable": "true", + "weight": "0.3" + }, + { + "id": "1632", + "name": "Uncut dragonstone", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1633", + "name": "Crushed gem", + "examine": "This gem is crushed and broken.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1634", + "name": "Crushed gem", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1635", + "name": "Gold ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "destroy": "false", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1636", + "name": "Gold ring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1637", + "name": "Sapphire ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1638", + "name": "Sapphire ring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1639", + "name": "Emerald ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1640", + "name": "Emerald ring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1641", + "name": "Ruby ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "shop_price": "2025", + "tradeable": "true" + }, + { + "id": "1642", + "name": "Ruby ring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1643", + "name": "Diamond ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "5000", + "shop_price": "3172", + "tradeable": "true" + }, + { + "id": "1644", + "name": "Diamond ring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1645", + "name": "Dragonstone ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "12", + "ge_buy_limit": "100", + "shop_price": "7750", + "tradeable": "true" + }, + { + "id": "1646", + "name": "Dragonstone ring", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1649", + "name": "Sapphire ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1650", + "name": "Emerald ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1651", + "name": "Ruby ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2025", + "tradeable": "true" + }, + { + "id": "1652", + "name": "Diamond ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "3172", + "tradeable": "true" + }, + { + "id": "1653", + "name": "Dragonstone ring", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "7750", + "tradeable": "true" + }, + { + "id": "1654", + "name": "Gold necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "450", + "tradeable": "true" + }, + { + "id": "1655", + "name": "Gold necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1656", + "name": "Sapphire necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "945", + "tradeable": "true" + }, + { + "id": "1657", + "name": "Sapphire necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1658", + "name": "Emerald necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "1425", + "tradeable": "true" + }, + { + "id": "1659", + "name": "Emerald necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1660", + "name": "Ruby necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "870", + "tradeable": "true" + }, + { + "id": "1661", + "name": "Ruby necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1662", + "name": "Diamond necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "3307", + "tradeable": "true" + }, + { + "id": "1663", + "name": "Diamond necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1664", + "name": "Dragon necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1665", + "name": "Dragon necklace", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1668", + "name": "Sapphire necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "945", + "tradeable": "true" + }, + { + "id": "1669", + "name": "Emerald necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1425", + "tradeable": "true" + }, + { + "id": "1670", + "name": "Ruby necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "870", + "tradeable": "true" + }, + { + "id": "1671", + "name": "Diamond necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "3307", + "tradeable": "true" + }, + { + "id": "1672", + "name": "Dragon necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1673", + "name": "Gold amulet", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "350", + "tradeable": "true" + }, + { + "id": "1674", + "name": "Gold amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1675", + "name": "Sapphire amulet", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "810", + "tradeable": "true" + }, + { + "id": "1676", + "name": "Sapphire amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1677", + "name": "Emerald amulet", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "1147", + "tradeable": "true" + }, + { + "id": "1678", + "name": "Emerald amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1679", + "name": "Ruby amulet", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "2025", + "tradeable": "true" + }, + { + "id": "1680", + "name": "Ruby amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1681", + "name": "Diamond amulet", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "shop_price": "3307", + "tradeable": "true" + }, + { + "id": "1682", + "name": "Diamond amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1683", + "name": "Dragonstone ammy", + "examine": "It needs a string so I can wear it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1684", + "name": "Dragonstone ammy", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1687", + "name": "Sapphire amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "810", + "tradeable": "true" + }, + { + "id": "1688", + "name": "Emerald amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1147", + "tradeable": "true" + }, + { + "id": "1689", + "name": "Ruby amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "shop_price": "2025", + "tradeable": "true" + }, + { + "id": "1690", + "name": "Diamond amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "3307", + "tradeable": "true" + }, + { + "id": "1691", + "name": "Dragonstone ammy", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "1692", + "name": "Gold amulet", + "examine": "A plain gold amulet.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "350", + "tradeable": "true" + }, + { + "id": "1693", + "name": "Gold amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1694", + "name": "Sapphire amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "810", + "tradeable": "true" + }, + { + "id": "1695", + "name": "Sapphire amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1696", + "name": "Emerald amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "1147", + "tradeable": "true" + }, + { + "id": "1697", + "name": "Emerald amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1698", + "name": "Ruby amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "2025", + "tradeable": "true" + }, + { + "id": "1699", + "name": "Ruby amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1700", + "name": "Diamond amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "5000", + "shop_price": "3307", + "tradeable": "true" + }, + { + "id": "1701", + "name": "Diamond amulet", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1702", + "name": "Dragonstone ammy", + "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": "1703", + "name": "Dragonstone ammy", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1704", + "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,3,0,0", + "durability": null, + "equipment_slot": "2", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1705", + "name": "Amulet of glory", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "1706", + "name": "Amulet of glory(1)", + "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": "1707", + "name": "Amulet of glory(1)", + "examine": "Swap this note at any bank for the equivalent item.", + "archery_ticket_price": "0", + "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", + "tradeable": "true" + }, + { + "id": "1715", + "name": "Unstrung symbol", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "1716", + "name": "Unblessed symbol", + "examine": "A symbol of Saradomin.", + "archery_ticket_price": "0", + "durability": null, + "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.", - "grand_exchange_price": "195", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true", + "weight": "0.05" + }, + { + "id": "1786", "name": "Glassblowing pipe", - "tradeable": "true", - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "0.05", "archery_ticket_price": "0", - "id": "1785" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "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, + "id": "1787", "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", - "low_alchemy": "1", - "high_alchemy": "1", "archery_ticket_price": "0", - "id": "1789" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "96", - "durability": null, - "name": "Unfired pie dish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1790" + "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", - "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", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "1791" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "194", - "durability": null, + "id": "1792", "name": "Unfired bowl", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1792" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "15", - "ge_buy_limit": "100", - "examine": "A slightly bluish leaf.", - "grand_exchange_price": "57", - "durability": null, + "id": "1793", "name": "Woad leaf", - "tradeable": "true", + "examine": "A slightly bluish leaf.", "archery_ticket_price": "0", - "id": "1793" + "durability": null, + "equip_audio": "", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "1794", + "name": "Bronze wire", "examine": "Useful for crafting items.", - "grand_exchange_price": "249", - "durability": null, - "name": "Bronze wire", - "tradeable": "true", - "low_alchemy": "8", - "high_alchemy": "12", "archery_ticket_price": "0", - "id": "1794" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "249", - "durability": null, - "name": "Bronze wire", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1795" + "tradeable": "true" }, { - "examine": "Anna's shiny silver coated necklace.", - "durability": null, - "name": "Silver necklace", - "weight": "1", + "id": "1795", + "name": "Bronze wire", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "1796", - "equipment_slot": "2" - }, - { - "examine": "Anna's shiny silver coated necklace.", - "durability": null, "name": "Silver necklace", - "weight": "1", + "examine": "Anna's shiny silver coated necklace.", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "1" + }, + { "id": "1797", - "equipment_slot": "2" + "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" }, { - "examine": "Bob's shiny silver coated tea cup.", - "durability": null, + "id": "1798", "name": "Silver cup", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1798" - }, - { "examine": "Bob's shiny silver coated tea cup.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "1799", "name": "Silver cup", - "tradeable": "false", - "destroy": "true", + "examine": "Bob's shiny silver coated tea cup coated with a thin layer of flour.", "archery_ticket_price": "0", - "id": "1799" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Carol's shiny silver coated bottle.", - "durability": null, + "id": "1800", "name": "Silver bottle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1800" - }, - { "examine": "Carol's shiny silver coated bottle.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "1801", "name": "Silver bottle", - "tradeable": "false", - "destroy": "true", + "examine": "Carol's shiny silver coated bottle coated with a thin layer of flour.", "archery_ticket_price": "0", - "id": "1801" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "David's shiny silver coated book.", - "durability": null, + "id": "1802", "name": "Silver book", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1802" - }, - { "examine": "David's shiny silver coated book.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "1803", "name": "Silver book", - "tradeable": "false", - "destroy": "true", + "examine": "David's shiny silver coated book coated with a thin layer of flour.", "archery_ticket_price": "0", - "id": "1803" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Elizabeth's shiny silver coated needle.", - "durability": null, + "id": "1804", "name": "Silver needle", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1804" - }, - { "examine": "Elizabeth's shiny silver coated needle.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "1805", "name": "Silver needle", - "tradeable": "false", - "destroy": "true", + "examine": "Elizabeth's shiny silver coated needle coated with a thin layer of flour.", "archery_ticket_price": "0", - "id": "1805" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Frank's shiny silver coated pot.", - "durability": null, + "id": "1806", "name": "Silver pot", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "1806" - }, - { "examine": "Frank's shiny silver coated pot.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "tradeable": "false" + }, + { + "id": "1807", "name": "Silver pot", - "tradeable": "false", - "destroy": "true", + "examine": "Frank's shiny silver coated pot coated with a thin layer of flour.", "archery_ticket_price": "0", - "id": "1807" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Some (colour) thread found at the murder scene.", - "durability": null, + "id": "1808", "name": "Criminal's thread", - "tradeable": "false", - "destroy": "true", + "examine": "Some red thread found at the murder scene.", "archery_ticket_price": "0", - "id": "1808" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Some (colour) thread found at the murder scene.", - "durability": null, + "id": "1809", "name": "Criminal's thread", - "tradeable": "false", - "destroy": "true", + "examine": "Some green thread found at the murder scene.", "archery_ticket_price": "0", - "id": "1809" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "Some (colour) thread found at the murder scene.", - "durability": null, + "id": "1810", "name": "Criminal's thread", - "tradeable": "false", - "destroy": "true", + "examine": "Some blue thread found at the murder scene.", "archery_ticket_price": "0", - "id": "1810" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A piece of fly paper. It's sticky.", - "durability": null, + "id": "1811", "name": "Flypaper", - "tradeable": "false", - "destroy": "true", + "examine": "A piece of fly paper. It's sticky.", "archery_ticket_price": "0", - "id": "1811" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A pot found at the murder scene, with a sickly odour.", - "durability": null, + "id": "1812", "name": "Pungent pot", - "tradeable": "false", + "examine": "A pot found at the murder scene, with a sickly odour.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "1812" + "durability": null, + "tradeable": "false" }, { - "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, + "id": "1813", "name": "Criminal's dagger", + "examine": "A flimsy-looking dagger found at the crime scene.", "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" + "durability": null }, { - "examine": "The fingerprints of the murderer.", - "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", - "tradeable": "false", - "destroy": "true", + "examine": "The fingerprints of the murderer.", "archery_ticket_price": "0", - "id": "1815" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An imprint of Anna's fingerprint.", - "durability": null, + "id": "1816", "name": "Anna's print", - "tradeable": "false", - "destroy": "true", + "examine": "An imprint of Anna's fingerprint.", "archery_ticket_price": "0", - "id": "1816" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An imprint of Bob's fingerprint.", - "durability": null, + "id": "1817", "name": "Bob's print", - "tradeable": "false", - "destroy": "true", + "examine": "An imprint of Bob's fingerprint.", "archery_ticket_price": "0", - "id": "1817" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An imprint of Carol's fingerprint.", - "durability": null, + "id": "1818", "name": "Carol's print", - "tradeable": "false", - "destroy": "true", + "examine": "An imprint of Carol's fingerprint.", "archery_ticket_price": "0", - "id": "1818" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An imprint of David's fingerprint.", - "durability": null, + "id": "1819", "name": "David's print", - "tradeable": "false", - "destroy": "true", + "examine": "An imprint of David's fingerprint.", "archery_ticket_price": "0", - "id": "1819" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An imprint of Elizabeth's fingerprint.", - "durability": null, + "id": "1820", "name": "Elizabeth's print", - "tradeable": "false", - "destroy": "true", + "examine": "An imprint of Elizabeth's fingerprint.", "archery_ticket_price": "0", - "id": "1820" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An imprint of Frank's fingerprint.", - "durability": null, + "id": "1821", "name": "Frank's print", - "tradeable": "false", - "destroy": "true", + "examine": "An imprint of Frank's fingerprint.", "archery_ticket_price": "0", - "id": "1821" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "An unidentified fingerprint taken from the murder weapon.", - "durability": null, + "id": "1822", "name": "Unknown print", - "tradeable": "false", + "examine": "An unidentified fingerprint taken from the murder weapon.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "1822" + "durability": null, + "tradeable": "false" }, { - "shop_price": "30", - "ge_buy_limit": "100", + "id": "1823", + "name": "Waterskin(4)", "examine": "A full waterskin with four portions of water.", - "grand_exchange_price": "184", - "durability": null, - "name": "Waterskin(4)", - "tradeable": "true", - "low_alchemy": "12", - "high_alchemy": "18", - "weight": "0.1", "archery_ticket_price": "0", - "id": "1823" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "184", - "durability": null, - "name": "Waterskin(4)", + "shop_price": "30", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1824" + "weight": "0.1" }, { - "shop_price": "30", + "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.", - "grand_exchange_price": "4", - "durability": null, - "name": "Waterskin(3)", - "tradeable": "true", - "low_alchemy": "12", - "high_alchemy": "18", - "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", + "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.", - "grand_exchange_price": "4", - "durability": null, - "name": "Waterskin(2)", - "tradeable": "true", - "low_alchemy": "12", - "high_alchemy": "18", - "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", + "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.", - "grand_exchange_price": "4", - "durability": null, - "name": "Waterskin(1)", - "tradeable": "true", - "low_alchemy": "12", - "high_alchemy": "18", - "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", + "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.", - "grand_exchange_price": "93", - "durability": null, - "name": "Waterskin(0)", - "tradeable": "true", - "low_alchemy": "12", - "high_alchemy": "18", - "weight": "0.1", "archery_ticket_price": "0", - "id": "1831" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "30", + "tradeable": "true", + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "93", - "durability": null, + "id": "1832", "name": "Waterskin(0)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1832" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "40", - "ge_buy_limit": "100", + "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, - "low_alchemy": "16", - "high_alchemy": "24", - "weight": "0.5", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "93", - "name": "Desert shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1833" + "weight": "0.9" }, { - "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.", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "weight": "0.9", - "equipment_slot": "7", - "grand_exchange_price": "74", - "name": "Desert robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1835" - }, - { - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "10", - "grand_exchange_price": "257", - "name": "Desert boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1837" - }, - { - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "1844", - "equipment_slot": "4" - }, - { - "examine": "A filthy, smelly, flea infested robe.", - "durability": null, - "name": "Slave robe", - "low_alchemy": "16", - "high_alchemy": "24", - "weight": "0.9", - "archery_ticket_price": "0", "id": "1845", - "equipment_slot": "7" + "name": "Slave robe", + "examine": "A filthy, smelly, flea infested robe.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "weight": "0.9" }, { - "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" + "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" }, { - "examine": "A piece of paper with barely legible writing - looks like a recipe!", - "durability": null, + "id": "1847", "name": "Scrumpled paper", - "low_alchemy": "4", - "high_alchemy": "6", + "examine": "A piece of paper with barely legible writing - looks like a recipe!", "archery_ticket_price": "0", - "id": "1847" + "durability": null }, { - "shop_price": "5", - "examine": "Very important information.", - "durability": null, + "id": "1848", "name": "Shantay disclaimer", - "high_alchemy": "1", + "examine": "Very important information.", "archery_ticket_price": "0", - "id": "1848" + "durability": null, + "shop_price": "5" }, { - "examine": "A prototype throwing dart.", - "attack_audios": "2547,0,0,0", - "durability": null, + "id": "1849", "name": "Prototype dart", - "tradeable": "false", - "destroy": "true", + "examine": "A prototype throwing dart.", "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", + "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", - "tradeable": "false", - "destroy": "true", + "examine": "A protoype dart tip - it looks deadly.", "archery_ticket_price": "0", - "id": "1853" + "attack_audios": "2547,0,0,0", + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "shop_price": "5", - "examine": "Allows you to pass through the Shantay pass into the Kharid Desert.", - "durability": null, + "id": "1854", "name": "Shantay pass", - "low_alchemy": "2", - "high_alchemy": "3", + "examine": "Allows you to pass through the Shantay pass into the Kharid Desert.", "archery_ticket_price": "0", - "id": "1854" + "durability": null, + "shop_price": "5" }, { - "examine": "A Tourist's Guide to Ardougne.", - "durability": null, + "id": "1856", "name": "Guide book", - "tradeable": "false", + "examine": "A Tourist's Guide to Ardougne.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "1856" + "durability": null, + "tradeable": "false" }, { - "examine": "The Rantuki tribe's totem.", - "durability": null, + "id": "1857", "name": "Totem", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "3", + "examine": "The Rantuki tribe's totem.", "archery_ticket_price": "0", - "id": "1857" + "durability": null, + "weight": "3" }, { - "examine": "It says 'To Lord Handelmort, Handelmort Mansion'.", - "durability": null, + "id": "1858", "name": "Address label", + "examine": "It says 'To Lord Handelmort, Handelmort Mansion'.", "archery_ticket_price": "0", - "id": "1858" + "durability": null }, { - "ge_buy_limit": "1000", - "examine": "I need to cook this first / Freshly cooked ugthanki meat.", - "grand_exchange_price": "486", - "durability": null, + "id": "1859", "name": "Raw ugthanki meat", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.75", + "examine": "I need to cook this first.", "archery_ticket_price": "0", - "id": "1859" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.75" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "486", - "durability": null, + "id": "1860", "name": "Raw ugthanki meat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1860" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "1861", + "name": "Ugthanki meat", "examine": "I need to cook this first / Freshly cooked ugthanki meat.", - "grand_exchange_price": "687", - "durability": null, - "name": "Ugthanki meat", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.75", "archery_ticket_price": "0", - "id": "1861" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "687", - "durability": null, - "name": "Ugthanki meat", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1862" + "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", - "ge_buy_limit": "1000", - "examine": "I need to cook this.", - "grand_exchange_price": "90", - "durability": null, - "name": "Pitta dough", "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "0.14", - "archery_ticket_price": "0", - "id": "1863" + "weight": "0.14" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "90", - "durability": null, + "id": "1864", "name": "Pitta dough", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1864" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "1865", + "name": "Pitta bread", "examine": "Nicely baked pitta bread. Needs more ingredients to make a kebab.", - "grand_exchange_price": "22", - "durability": null, - "name": "Pitta bread", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", "archery_ticket_price": "0", - "id": "1865" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "22", - "durability": null, - "name": "Pitta bread", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1866" + "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", - "tradeable": "true", + "examine": "It's all burnt.", "archery_ticket_price": "0", - "id": "1868" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "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.", - "grand_exchange_price": "90", - "durability": null, - "name": "Chopped tomato", - "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "0.2", "archery_ticket_price": "0", - "id": "1869" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.2" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "90", - "durability": null, + "id": "1870", "name": "Chopped tomato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1870" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "1871", + "name": "Chopped onion", "examine": "A mixture of onions in a bowl.", - "grand_exchange_price": "142", - "durability": null, - "name": "Chopped onion", - "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "0.5", "archery_ticket_price": "0", - "id": "1871" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "142", - "durability": null, + "id": "1872", "name": "Chopped onion", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1872" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "1873", + "name": "Chopped ugthanki", "examine": "Strips of ugthanki meat in a bowl.", - "grand_exchange_price": "348", - "durability": null, - "name": "Chopped ugthanki", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", "archery_ticket_price": "0", - "id": "1873" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "348", - "durability": null, + "id": "1874", "name": "Chopped ugthanki", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1874" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "1875", + "name": "Onion & tomato", "examine": "A mixture of chopped onions and tomatoes in a bowl", - "grand_exchange_price": "31", - "durability": null, - "name": "Onion & tomato", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", "archery_ticket_price": "0", - "id": "1875" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "31", - "durability": null, + "id": "1876", "name": "Onion & tomato", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1876" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "26", - "ge_buy_limit": "1000", + "id": "1877", + "name": "Ugthanki & onion", "examine": "A mixture of chopped onions and ugthanki meat in a bowl.", - "grand_exchange_price": "330", - "durability": null, - "name": "Ugthanki & onion", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "4", "archery_ticket_price": "0", - "id": "1877" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "330", - "durability": null, - "name": "Ugthanki & onion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1878" + "shop_price": "26", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "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.", - "grand_exchange_price": "331", - "durability": null, - "name": "Ugthanki & tomato", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "4", - "weight": "0.25", "archery_ticket_price": "0", - "id": "1879" - }, - { + "durability": null, "ge_buy_limit": "10000", - "grand_exchange_price": "331", - "durability": null, - "name": "Ugthanki & tomato", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1880" + "weight": "0.25" }, { - "ge_buy_limit": "1000", + "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", - "grand_exchange_price": "412", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "1882", "name": "Kebab mix", - "tradeable": "true", - "low_alchemy": "3", - "high_alchemy": "5", "archery_ticket_price": "0", - "id": "1881" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "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, + "id": "1883", "name": "Ugthanki kebab", - "tradeable": "true", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1885" + "weight": "0.5" }, { + "id": "1884", + "name": "Ugthanki kebab", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "1499", - "durability": null, - "name": "Ugthanki kebab", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1886" + "tradeable": "true" }, { - "shop_price": "10", - "ge_buy_limit": "100", + "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.", - "grand_exchange_price": "20", - "durability": null, - "name": "Cake tin", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.1", "archery_ticket_price": "0", - "id": "1887" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, - "name": "Cake tin", + "shop_price": "10", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1888" + "weight": "0.1" }, { - "ge_buy_limit": "1000", + "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.", - "grand_exchange_price": "559", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "1890", "name": "Uncooked cake", - "tradeable": "true", - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.5", "archery_ticket_price": "0", - "id": "1889" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "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, + "id": "1891", "name": "Cake", - "tradeable": "true", - "weight": "0.3", + "examine": "A plain sponge cake.", "archery_ticket_price": "0", - "id": "1891" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "50", + "tradeable": "true", + "weight": "0.3" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "155", - "durability": null, + "id": "1892", "name": "Cake", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1892" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "50", - "ge_buy_limit": "1000", - "examine": "A plain sponge cake.", - "grand_exchange_price": "39", - "durability": null, + "id": "1893", "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", + "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", - "tradeable": "true", - "weight": "0.3", + "examine": "A plain sponge cake.", "archery_ticket_price": "0", - "id": "1895" + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "50", + "tradeable": "true", + "weight": "0.3" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "32", - "durability": null, + "id": "1896", "name": "Slice of cake", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1896" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "This looks very tasty.", - "grand_exchange_price": "416", - "durability": null, + "id": "1897", "name": "Chocolate cake", - "tradeable": "true", - "weight": "0.3", + "examine": "This looks very tasty.", "archery_ticket_price": "0", - "id": "1897" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.3" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "416", - "durability": null, + "id": "1898", "name": "Chocolate cake", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1898" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "ge_buy_limit": "1000", - "examine": "This looks very tasty.", - "grand_exchange_price": "181", - "durability": null, + "id": "1899", "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" - }, - { + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "13", - "durability": null, - "name": "Chocolate slice", "tradeable": "true", - "archery_ticket_price": "0", - "id": "1902" + "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", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1904" + "durability": null, + "tradeable": "true" }, { - "shop_price": "3", - "ge_buy_limit": "100", + "id": "1905", + "name": "Asgarnian ale", "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "190", - "durability": null, - "name": "Asgarnian ale", - "tradeable": "true", - "high_alchemy": "1", "archery_ticket_price": "0", - "id": "1905" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "190", - "durability": null, - "name": "Asgarnian ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1906" - }, - { "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.", - "grand_exchange_price": "171", - "durability": null, - "name": "Wizard's mind bomb", - "tradeable": "true", - "high_alchemy": "1", "archery_ticket_price": "0", - "id": "1907" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "171", - "durability": null, + "id": "1908", "name": "Wizard's mind bomb", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1908" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "2", - "ge_buy_limit": "100", + "id": "1909", + "name": "Greenman's ale", "examine": "A glass of frothy ale.", - "grand_exchange_price": "828", - "durability": null, - "name": "Greenman's ale", - "tradeable": "true", - "high_alchemy": "1", "archery_ticket_price": "0", - "id": "1909" - }, - { + "durability": null, "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", + "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.", - "grand_exchange_price": "487", - "durability": null, - "name": "Dragon bitter", - "tradeable": "true", - "high_alchemy": "1", - "weight": "1", "archery_ticket_price": "0", - "id": "1911" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "2", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "487", - "durability": null, + "id": "1912", "name": "Dragon bitter", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1912" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "3", - "ge_buy_limit": "100", + "id": "1913", + "name": "Dwarven stout", "examine": "A pint of thick dark beer.", - "grand_exchange_price": "212", - "durability": null, - "name": "Dwarven stout", - "tradeable": "true", - "high_alchemy": "1", "archery_ticket_price": "0", - "id": "1913" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "212", - "durability": null, - "name": "Dwarven stout", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1914" - }, - { "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.", - "grand_exchange_price": "19", - "durability": null, - "name": "Grog", - "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "2", "archery_ticket_price": "0", - "id": "1915" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "3", + "tradeable": "true", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "19", - "durability": null, + "id": "1916", "name": "Grog", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1916" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "202", - "durability": null, - "name": "Beer", - "tradeable": "true", - "high_alchemy": "1", - "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", + "examine": "A glass of frothy ale.", "archery_ticket_price": "0", - "id": "1918" + "durability": null, + "equipment_slot": "3", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "shop_price": "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.", - "grand_exchange_price": "10", - "durability": null, - "name": "Beer glass", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.05", "archery_ticket_price": "0", - "id": "1919" + "durability": null, + "ge_buy_limit": "100", + "shop_price": "1", + "tradeable": "true", + "weight": "0.05" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "10", - "durability": null, + "id": "1920", "name": "Beer glass", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1920" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "id": "1921", + "name": "Bowl of water", "examine": "It's a bowl of water.", - "grand_exchange_price": "97", - "durability": null, - "name": "Bowl of water", - "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "0.9", "archery_ticket_price": "0", - "id": "1921" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "97", - "durability": null, + "id": "1922", "name": "Bowl of water", - "tradeable": "true", "archery_ticket_price": "0", - "id": "1922" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "4", - "ge_buy_limit": "100", + "id": "1923", + "name": "Bowl", "examine": "Useful for mixing things.", - "grand_exchange_price": "34", - "durability": null, - "name": "Bowl", - "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "0.4", "archery_ticket_price": "0", - "id": "1923" - }, - { + "durability": null, "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", - "high_alchemy": "1", - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "3", - "archery_ticket_price": "0", - "id": "1929" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "72", - "durability": null, - "name": "Bucket of water", - "tradeable": "true", - "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", - "low_alchemy": "5", - "high_alchemy": "8", - "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" - }, - { - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "1942" - }, - { - "ge_buy_limit": "500", - "grand_exchange_price": "91", - "durability": null, - "name": "Potato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1943" - }, - { - "shop_price": "1", - "ge_buy_limit": "1000", - "examine": "A nice fresh egg.", - "grand_exchange_price": "99", - "durability": null, - "name": "Egg", - "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "2", - "archery_ticket_price": "0", - "id": "1944" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "99", - "durability": null, - "name": "Egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1945" - }, - { - "shop_price": "14", - "examine": "There is flour in this pot.", - "grand_exchange_price": "162", - "durability": null, - "name": "Flour", - "tradeable": "true", - "low_alchemy": "5", - "high_alchemy": "8", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "1946" - }, - { - "shop_price": "2", - "ge_buy_limit": "1000", - "examine": "Some wheat heads.", - "grand_exchange_price": "7", - "durability": null, - "name": "Grain", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.6", - "archery_ticket_price": "0", - "id": "1947" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "7", - "durability": null, - "name": "Grain", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "1948" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "What a silly hat.", - "grand_exchange_price": "233", - "durability": null, - "name": "Chef's hat", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.9", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "14", - "high_alchemy": "21", - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "5", - "high_alchemy": "8", - "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", - "low_alchemy": "3", - "high_alchemy": "4", - "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", - "high_alchemy": "1", - "weight": "1", - "archery_ticket_price": "0", - "id": "1991" - }, - { - "examine": "Oh dear, this wine is terrible!", - "durability": null, - "name": "Jug of bad wine", - "high_alchemy": "1", - "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", - "low_alchemy": "51", - "high_alchemy": "76", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "1", - "archery_ticket_price": "0", - "id": "1995" - }, - { - "examine": "This wine needs to ferment before it can be drunk.", - "durability": null, - "name": "Unfermented wine", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "92", - "high_alchemy": "138", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "28", - "high_alchemy": "42", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "16", - "durability": null, - "name": "Lime chunks", - "tradeable": "true", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "archery_ticket_price": "0", - "id": "2126" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "144", - "durability": null, - "name": "Dwellberries", - "tradeable": "true", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "7", - "high_alchemy": "11", - "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" - }, - { - "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", - "low_alchemy": "23", - "high_alchemy": "35", - "weight": "0.3", - "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", - "low_alchemy": "15", - "high_alchemy": "23", - "weight": "1", - "archery_ticket_price": "0", - "id": "2134" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "85", - "durability": null, - "name": "Raw rat meat", - "tradeable": "true", - "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", - "low_alchemy": "3", - "high_alchemy": "4", - "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" - }, - { - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "weight": "0.15", - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2140" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "70", - "durability": null, - "name": "Cooked chicken", - "tradeable": "true", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2148" - }, - { - "examine": "Strange, it looks cooler now it's been cooked.", - "durability": null, - "name": "Lava eel", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "high_alchemy": "1", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2150" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "750", - "durability": null, - "name": "Swamp toad", - "tradeable": "true", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2158" - }, - { - "durability": null, - "name": "Seasoned legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2159" - }, - { - "examine": "They're a gnome delicacy apparently.", - "durability": null, - "name": "Spicy worm", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.1", - "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", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2162" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "24", - "durability": null, - "name": "King worm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2163" - }, - { - "shop_price": "1", - "examine": "A deep tin used to make gnome battas in.", - "durability": null, - "name": "Batta tin", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2164" - }, - { - "shop_price": "10", - "examine": "A shallow tray used to make crunchies in.", - "durability": null, - "name": "Crunchy tray", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2165" - }, - { - "shop_price": "10", - "examine": "A large ovenproof bowl.", - "durability": null, - "name": "Gnomebowl mould", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2166" - }, - { - "examine": "Aluft Gianne's favourite dishes.", - "durability": null, - "name": "Gianne's cook book", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2167" - }, - { - "durability": null, - "name": "Gianne's cook book", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2168" - }, - { - "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", - "high_alchemy": "1", - "weight": "0.14", - "archery_ticket_price": "0", - "id": "2169" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "28", - "durability": null, - "name": "Gnome spice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2170" - }, - { - "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", - "high_alchemy": "1", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2171" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "13", - "durability": null, - "name": "Gianne dough", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2172" - }, - { - "examine": "This gnome bowl doesn't look very appetizing.", - "durability": null, - "name": "Picture", - "weight": "1", - "archery_ticket_price": "0", - "id": "2173" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2174" - }, - { - "durability": null, - "name": "Burnt gnomebowl", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2176" - }, - { - "examine": "This gnomebowl is in the early stages of preparation.", - "durability": null, - "name": "Half baked bowl", - "high_alchemy": "1", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2177" - }, - { - "examine": "This gnomebowl needs cooking.", - "durability": null, - "name": "Raw gnomebowl", - "tradeable": "true", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2178" - }, - { - "examine": "This unfinished XX needs YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2179" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2180" - }, - { - "examine": "This unfinished XX needs YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2181" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2182" - }, - { - "examine": "This unfinished XX needs YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2183" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2184" - }, - { - "shop_price": "450", - "ge_buy_limit": "1000", - "examine": "Full of creamy, chocolately goodness.", - "grand_exchange_price": "4400", - "durability": null, - "name": "Chocolate bomb", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "2185" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "4400", - "durability": null, - "name": "Chocolate bomb", - "tradeable": "true", - "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", - "high_alchemy": "1", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "2187" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "2321", - "durability": null, - "name": "Tangled toads' legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2188" - }, - { - "examine": "This unfinished XX needs YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2189" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "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", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2191" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "454", - "durability": null, - "name": "Worm hole", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2192" - }, - { - "examine": "This unfinished XX needs YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2193" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "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", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2195" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "481", - "durability": null, - "name": "Veg ball", - "tradeable": "true", - "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" - }, - { - "durability": null, - "name": "Odd crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2198" - }, - { - "durability": null, - "name": "Burnt crunchies", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2200" - }, - { - "examine": "This crunchy is in the early stages of preparation.", - "durability": null, - "name": "Half baked crunchy", - "tradeable": "true", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2202" - }, - { - "examine": "This XX crunchy needs garnishing with YY.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2203" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2204" - }, - { - "shop_price": "80", - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "255", - "durability": null, - "name": "Worm crunchies", - "tradeable": "true", - "high_alchemy": "1", - "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" - }, - { - "examine": "This XX crunchy needs garnishing with YY.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2207" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2208" - }, - { - "shop_price": "70", - "ge_buy_limit": "1000", - "examine": "Yum... smells good.", - "grand_exchange_price": "816", - "durability": null, - "name": "Chocchip crunchies", - "tradeable": "true", - "high_alchemy": "1", - "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" - }, - { - "examine": "This XX crunchy needs garnishing with YY.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2215" - }, - { - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "78", - "high_alchemy": "117", - "weight": "0.2", - "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" - }, - { - "ge_buy_limit": "1000", - "examine": "A Premade Toad Batta.", - "grand_exchange_price": "127", - "durability": null, - "name": "Premade t'd batta", - "tradeable": "true", - "low_alchemy": "78", - "high_alchemy": "117", - "weight": "0.2", - "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" - }, - { - "ge_buy_limit": "1000", - "examine": "A Premade Cheese and Tomato Batta.", - "grand_exchange_price": "137", - "durability": null, - "name": "Premade c+t batta", - "tradeable": "true", - "low_alchemy": "78", - "high_alchemy": "117", - "weight": "0.2", - "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" - }, - { - "ge_buy_limit": "1000", - "examine": "A premade Fruit Batta.", - "grand_exchange_price": "131", - "durability": null, - "name": "Premade fr't batta", - "tradeable": "true", - "low_alchemy": "78", - "high_alchemy": "117", - "weight": "0.2", - "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": "1000", - "examine": "A Premade Vegetable Batta.", - "grand_exchange_price": "134", - "durability": null, - "name": "Premade veg batta", - "tradeable": "true", - "low_alchemy": "78", - "high_alchemy": "117", - "weight": "0.2", - "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": "1000", - "examine": "A premade Chocolate Bomb.", - "grand_exchange_price": "1227", - "durability": null, - "name": "Premade choc bomb", - "tradeable": "true", - "low_alchemy": "180", - "high_alchemy": "270", - "weight": "0.2", - "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": "1000", - "examine": "A premade Tangled Toads Legs.", - "grand_exchange_price": "867", - "durability": null, - "name": "Premade ttl", - "tradeable": "true", - "low_alchemy": "180", - "high_alchemy": "270", - "weight": "0.2", - "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": "1000", - "examine": "A premade Worm Hole.", - "grand_exchange_price": "237", - "durability": null, - "name": "Premade worm hole", - "tradeable": "true", - "low_alchemy": "108", - "high_alchemy": "162", - "weight": "0.2", - "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": "1000", - "examine": "A premade Vegetable Ball.", - "grand_exchange_price": "233", - "durability": null, - "name": "Premade veg ball", - "tradeable": "true", - "low_alchemy": "108", - "high_alchemy": "162", - "weight": "0.2", - "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": "1000", - "examine": "Some Premade Worm Crunchies.", - "grand_exchange_price": "76", - "durability": null, - "name": "Premade w'm crun", - "tradeable": "true", - "low_alchemy": "32", - "high_alchemy": "48", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2237" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "76", - "durability": null, - "name": "Premade w'm crun", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2238" - }, - { - "ge_buy_limit": "1000", - "examine": "Some Premade chocchip crunchies.", - "grand_exchange_price": "95", - "durability": null, - "name": "Premade ch' crunch", - "tradeable": "true", - "low_alchemy": "28", - "high_alchemy": "42", - "weight": "0.2", - "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": "1000", - "examine": "Some premade Spicy Crunchies.", - "grand_exchange_price": "103", - "durability": null, - "name": "Premade s'y crunch", - "tradeable": "true", - "low_alchemy": "28", - "high_alchemy": "42", - "weight": "0.2", - "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" - }, - { - "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", - "low_alchemy": "32", - "high_alchemy": "48", - "weight": "0.2", - "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" - }, - { - "examine": "This batta doesn't look very appetising.", - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2245" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2246" - }, - { - "durability": null, - "name": "Burnt batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2248" - }, - { - "examine": "This gnome batta is in the early stages of preparation.", - "durability": null, - "name": "Half baked batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2249" - }, - { - "shop_price": "1", - "examine": "This gnome batta needs cooking.", - "durability": null, - "name": "Raw batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2250" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2251" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "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" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "140", - "durability": null, - "name": "Worm batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2254" - }, - { - "shop_price": "78", - "ge_buy_limit": "1000", - "examine": "It actually smells quite good.", - "grand_exchange_price": "271", - "durability": null, - "name": "Toad batta", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2255" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "271", - "durability": null, - "name": "Toad batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2256" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2257" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "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", - "high_alchemy": "1", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2259" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "260", - "durability": null, - "name": "Cheese+tom batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2260" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Unfinished batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2261" - }, - { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2262" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Unfinished batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2263" - }, - { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2264" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Unfinished batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2265" - }, - { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2266" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Unfinished batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2267" - }, - { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2268" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Unfinished batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2269" - }, - { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2270" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Unfinished batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2271" - }, - { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2272" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Unfinished batta", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2273" - }, - { - "durability": null, - "name": "Unfinished batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2274" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2275" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "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", - "high_alchemy": "1", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2277" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "303", - "durability": null, - "name": "Fruit batta", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2278" - }, - { - "shop_price": "1", - "examine": "This XX batta needs garnishing with YY.", - "durability": null, - "name": "Picture", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "2279" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "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", - "high_alchemy": "1", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "2281" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "240", - "durability": null, - "name": "Vegetable batta", - "tradeable": "true", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2283" + "weight": "0.4" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "418", + "id": "1924", + "name": "Bowl", + "archery_ticket_price": "0", "durability": null, - "name": "Pizza base", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2284" - }, - { - "ge_buy_limit": "1000", - "examine": "I need to add some cheese next.", - "grand_exchange_price": "366", - "durability": null, - "name": "Incomplete pizza", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "2285" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "366", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Incomplete pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2286" - }, - { - "ge_buy_limit": "1000", - "examine": "This needs cooking.", - "grand_exchange_price": "1141", - "durability": null, - "name": "Uncooked pizza", - "tradeable": "true", - "low_alchemy": "10", - "high_alchemy": "15", - "archery_ticket_price": "0", - "id": "2287" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1141", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Uncooked pizza", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2288" - }, - { - "shop_price": "200", - "ge_buy_limit": "1000", - "examine": "A cheese and tomato pizza.", - "grand_exchange_price": "1357", - "durability": null, - "name": "Plain pizza", - "tradeable": "true", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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" + "tradeable": "true" }, { - "examine": "Oh dear!", - "durability": null, - "name": "Burnt pizza", - "tradeable": "true", + "id": "1925", + "name": "Bucket", + "examine": "It's an empty bucket.", "archery_ticket_price": "0", - "id": "2306" + "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", - "examine": "Some uncooked dough.", - "grand_exchange_price": "158", - "durability": null, - "name": "Bread dough", + "shop_price": "12", "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "2", - "archery_ticket_price": "0", - "id": "2307" + "weight": "2.2" }, { + "id": "1928", + "name": "Bucket of milk", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "158", - "durability": null, - "name": "Bread dough", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2308" + "tradeable": "true" }, { - "shop_price": "24", + "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", - "examine": "Nice crispy bread.", - "grand_exchange_price": "110", - "durability": null, - "name": "Bread", + "shop_price": "14", "tradeable": "true", - "low_alchemy": "9", - "high_alchemy": "14", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2309" + "weight": "1.3" }, { + "id": "1934", + "name": "Pot of flour", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "110", - "durability": null, - "name": "Bread", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2310" + "tradeable": "true" }, { - "durability": null, - "name": "Burnt bread", - "tradeable": "true", + "id": "1935", + "name": "Jug", + "examine": "This jug is empty.", "archery_ticket_price": "0", - "id": "2312" + "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", - "ge_buy_limit": "100", - "examine": "Deceptively pie shaped.", - "grand_exchange_price": "174", - "durability": null, - "name": "Pie dish", "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "2313" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "174", - "durability": null, - "name": "Pie dish", - "tradeable": "true", + "id": "1952", + "name": "Redberries", "archery_ticket_price": "0", - "id": "2314" - }, - { + "durability": null, "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", - "low_alchemy": "1", - "high_alchemy": "2", - "archery_ticket_price": "0", - "id": "2315" + "tradeable": "true" }, { + "id": "1953", + "name": "Pastry dough", + "examine": "Potentially pastry.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "grand_exchange_price": "887", - "durability": null, - "name": "Pie shell", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2316" + "weight": "0.16" }, { + "id": "1954", + "name": "Pastry dough", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "examine": "This would be much tastier cooked.", - "grand_exchange_price": "781", - "durability": null, - "name": "Uncooked apple pie", - "tradeable": "true", - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2317" + "tradeable": "true" }, { + "id": "1955", + "name": "Cooking apple", + "examine": "Keeps the doctor away.", + "archery_ticket_price": "0", + "durability": null, "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" + "shop_price": "36", + "tradeable": "true" }, { + "id": "1956", + "name": "Cooking apple", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "examine": "This would be much healthier cooked.", - "grand_exchange_price": "747", - "durability": null, - "name": "Uncooked meat pie", - "tradeable": "true", - "low_alchemy": "3", - "high_alchemy": "4", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2319" + "tradeable": "true" }, { + "id": "1957", + "name": "Onion", + "examine": "A strong smelling onion.", + "archery_ticket_price": "0", + "durability": null, "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", + "shop_price": "12", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2320" + "weight": "0.1" }, { + "id": "1958", + "name": "Onion", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "examine": "This would be much more appetising cooked.", - "grand_exchange_price": "802", - "durability": null, - "name": "Uncooked berry pie", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "2321" + "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", - "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": "2", + "tradeable": "true" }, { - "shop_price": "140", + "id": "1964", + "name": "Banana", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "examine": "Mmm Apple pie.", - "grand_exchange_price": "246", - "durability": null, - "name": "Apple pie", - "tradeable": "true", - "low_alchemy": "12", - "high_alchemy": "18", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2323" + "tradeable": "true" }, { + "id": "1965", + "name": "Cabbage", + "examine": "Yuck, I don't like cabbage.", + "archery_ticket_price": "0", + "durability": null, "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" + "weight": "0.4" }, { + "id": "1966", + "name": "Cabbage", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", - "examine": "Looks tasty.", - "grand_exchange_price": "534", - "durability": null, - "name": "Redberry pie", - "tradeable": "true", - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "2325" + "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", - "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": "1", + "tradeable": "true" }, { - "shop_price": "16", + "id": "1972", + "name": "Kebab", + "archery_ticket_price": "0", + "durability": null, "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" + "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", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "14", - "high_alchemy": "21", - "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", - "low_alchemy": "14", - "high_alchemy": "21", - "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", - "low_alchemy": "5", - "high_alchemy": "7", - "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", + "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.", - "grand_exchange_price": "268", - "durability": null, - "name": "Bronze bar", - "tradeable": "true", - "low_alchemy": "3", - "high_alchemy": "4", - "weight": "1.8", "archery_ticket_price": "0", - "id": "2349" + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "10", + "tradeable": "true", + "weight": "1.8" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "268", - "durability": null, + "id": "2350", "name": "Bronze bar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2350" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "2351", + "name": "Iron bar", "examine": "It's a bar of iron.", - "grand_exchange_price": "493", - "durability": null, - "name": "Iron bar", - "tradeable": "true", - "low_alchemy": "11", - "high_alchemy": "16", - "weight": "1.8", "archery_ticket_price": "0", - "id": "2351" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.8" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "493", - "durability": null, + "id": "2352", "name": "Iron bar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2352" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "2353", + "name": "Steel bar", "examine": "It's a bar of steel.", - "grand_exchange_price": "1266", - "durability": null, - "name": "Steel bar", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1.81", "archery_ticket_price": "0", - "id": "2353" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.81" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1266", - "durability": null, + "id": "2354", "name": "Steel bar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2354" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "2355", + "name": "Silver bar", "examine": "It's a bar of silver.", - "grand_exchange_price": "274", - "durability": null, - "name": "Silver bar", - "tradeable": "true", - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "1.81", "archery_ticket_price": "0", - "id": "2355" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.81" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "274", - "durability": null, + "id": "2356", "name": "Silver bar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2356" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "2357", + "name": "Gold bar", "examine": "It's a bar of gold.", - "grand_exchange_price": "107", - "durability": null, - "name": "Gold bar", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1.81", "archery_ticket_price": "0", - "id": "2357" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "1.81" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "107", - "durability": null, + "id": "2358", "name": "Gold bar", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2358" + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" }, { - "shop_price": "120", - "ge_buy_limit": "10000", + "id": "2359", + "name": "Mithril bar", "examine": "It's a bar of mithril.", - "grand_exchange_price": "1920", - "durability": null, - "name": "Mithril bar", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "256", - "high_alchemy": "384", - "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", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "44000", - "high_alchemy": "66000", - "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.", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "weight": "2", - "grand_exchange_price": "743100", - "name": "Shield right half", "tradeable": "true", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "48", - "high_alchemy": "72", + "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", - "low_alchemy": "12", - "high_alchemy": "18", + "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, - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "26", - "high_alchemy": "39", - "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "32000", - "high_alchemy": "48000", - "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, - "low_alchemy": "32000", - "high_alchemy": "48000", - "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", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "2416", - "bonuses": "-1,-1,6,6,0,2,3,1,6,0,0,2,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A magical staff imbued with the power of Zamorak.", - "walk_anim": "1205", - "low_alchemy": "32000", - "turn90ccw_anim": "1208", "attack_speed": "4", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "tradeable": "false", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "2417", - "stand_turn_anim": "1209", "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", - "durability": null, - "high_alchemy": "48000", - "weight": "2.2", + "tradeable": "false", "weapon_interface": "1", - "render_anim": "28", + "weight": "2.2" + }, + { + "id": "2416", + "name": "Guthix staff", + "examine": "A magical staff imbued with the power of Guthix.", + "archery_ticket_price": "0", + "attack_anims": "419,419,419,419", "attack_audios": "2555,0,0,0", - "name": "Zamorak 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", + "weapon_interface": "1", + "weight": "2" }, { - "examine": "A grey woollen wig.", - "durability": null, - "name": "Wig", + "id": "2417", + "name": "Zamorak staff", + "examine": "A magical staff imbued with the power of Zamorak.", "archery_ticket_price": "0", - "id": "2419" + "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", + "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": "2421" + "durability": null }, { - "lendable": "true", - "examine": "A nice hat from a cracker.", - "grand_exchange_price": "2147483647", - "durability": null, + "id": "2419", + "name": "Wig", + "examine": "A grey woollen wig.", + "archery_ticket_price": "0", + "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "224", - "high_alchemy": "336", "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", - "low_alchemy": "35", - "high_alchemy": "52", - "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", - "low_alchemy": "352", - "high_alchemy": "528", "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", - "low_alchemy": "61", - "high_alchemy": "91", "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", - "low_alchemy": "72", - "high_alchemy": "108", "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "88", - "high_alchemy": "132", "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", - "low_alchemy": "105", - "high_alchemy": "158", "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", - "low_alchemy": "115", - "high_alchemy": "172", "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", - "low_alchemy": "115", - "high_alchemy": "172", "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", - "low_alchemy": "115", - "high_alchemy": "172", "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", - "low_alchemy": "70", - "high_alchemy": "105", - "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", - "low_alchemy": "105", - "high_alchemy": "158", "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", - "low_alchemy": "105", - "high_alchemy": "158", "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", - "low_alchemy": "105", - "high_alchemy": "158", "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", - "low_alchemy": "105", - "high_alchemy": "158", "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "26", - "high_alchemy": "40", "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", - "low_alchemy": "26", - "high_alchemy": "40", "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, - "low_alchemy": "1200", - "high_alchemy": "1800", - "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, - "low_alchemy": "1440", - "high_alchemy": "2160", - "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, - "low_alchemy": "1728", - "high_alchemy": "2592", - "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, - "low_alchemy": "1728", - "high_alchemy": "2592", - "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, - "low_alchemy": "2072", - "high_alchemy": "3108", - "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.", - "durability": null, - "low_alchemy": "2488", - "high_alchemy": "3732", - "weight": "5.4", - "absorb": "0,7,3", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "4744", - "name": "Black d'hide chaps", "tradeable": "true", + "weight": "0.25" + }, + { + "id": "2492", + "name": "Black d'hide vamb", "archery_ticket_price": "0", + "durability": null, + "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", + "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", - "archery_ticket_price": "0", - "id": "2498" - }, - { - "requirements": "{1,40}-{4,50}", - "ge_buy_limit": "5000", "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "3744", - "high_alchemy": "5616", - "weight": "6.8", - "absorb": "0,6,3", - "equipment_slot": "4", - "lendable": "true", - "grand_exchange_price": "5420", - "name": "Blue d'hide body", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "2500" - }, - { - "requirements": "{1,40}-{4,60}", - "ge_buy_limit": "5000", - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "4492", - "high_alchemy": "6738", - "weight": "6.8", - "absorb": "0,6,3", - "equipment_slot": "4", - "lendable": "true", - "grand_exchange_price": "6680", - "name": "Red d'hide body", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "5392", - "high_alchemy": "8088", - "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", - "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", - "archery_ticket_price": "0", - "id": "2504" - }, - { - "ge_buy_limit": "10000", - "examine": "It's a piece of prepared blue dragonhide.", - "grand_exchange_price": "2888", - "durability": null, - "name": "Blue d-leather", - "tradeable": "true", - "low_alchemy": "28", - "high_alchemy": "42", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "32", - "high_alchemy": "48", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "2", - "archery_ticket_price": "0", - "id": "2511" - }, - { - "examine": "A series of connected metal rings.", - "rare_item": "true", - "durability": null, - "low_alchemy": "100000", - "high_alchemy": "150000", - "weight": "6.8", - "absorb": "4,0,9", - "lendable": "true", - "grand_exchange_price": "2902937", - "name": "Dragon chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2513", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "5", - "high_alchemy": "8", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "325", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "327", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "9", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "63", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "90", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "127", - "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" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "10000", - "examine": "An easy to make, rune-headed fire arrow.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "539", - "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" - }, - { - "requirements": "{4,40}", - "ge_buy_limit": "10000", - "examine": "An easy to make, rune-headed fire arrow.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "682", - "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" - }, - { - "ge_buy_limit": "5000", - "examine": "An enchanted ring.", - "grand_exchange_price": "943", - "durability": null, - "name": "Ring of recoil", - "tradeable": "true", - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "510", - "high_alchemy": "765", - "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", - "low_alchemy": "810", - "high_alchemy": "1215", - "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", - "low_alchemy": "1410", - "high_alchemy": "2115", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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, - "low_alchemy": "180", - "high_alchemy": "270", - "weight": "0.2", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "4400000", - "name": "Robin hood hat", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "1536", - "high_alchemy": "2304", - "weight": "9.07", - "absorb": "1,0,2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "74900", - "name": "Black platebody (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", + "absorb": "0,7,3", "archery_ticket_price": "0", - "id": "2584" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black platelegs with trim.", + "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0", "durability": null, - "low_alchemy": "768", - "high_alchemy": "1152", - "weight": "9", + "equip_audio": "2241", "equipment_slot": "7", - "grand_exchange_price": "39900", - "name": "Black platelegs (t)", + "ge_buy_limit": "5000", + "lendable": "true", + "requirements": "{4,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "2585", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0" + "weight": "5.4" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "39900", + "id": "2498", + "name": "Black d'hide chaps", + "archery_ticket_price": "0", "durability": null, - "name": "Black platelegs (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2586" + "ge_buy_limit": "5000", + "tradeable": "true" }, { + "id": "2499", + "name": "Blue d'hide body", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "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": "5000", + "lendable": "true", + "requirements": "{1,40}-{4,50}", + "tradeable": "true", + "weight": "6.8" + }, + { + "id": "2500", + "name": "Blue d'hide body", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "2501", + "name": "Red d'hide body", + "examine": "Made from 100% real dragonhide.", + "absorb": "0,6,3", + "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", + "name": "Black d'hide body", + "examine": "Made from 100% real dragonhide.", + "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": "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", + "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,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" + }, + { + "id": "2584", + "name": "Black platebody (t)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2585", + "name": "Black platelegs (t)", + "examine": "Black platelegs with trim.", + "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": "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, - "low_alchemy": "422", - "high_alchemy": "633", - "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, - "low_alchemy": "652", - "high_alchemy": "979", - "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, - "low_alchemy": "1536", - "high_alchemy": "2304", - "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.", - "durability": null, - "low_alchemy": "768", - "high_alchemy": "1152", - "weight": "9", - "equipment_slot": "7", - "grand_exchange_price": "216700", - "name": "Black platelegs (g)", - "tradeable": "true", + "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" - }, - { - "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, - "low_alchemy": "422", - "high_alchemy": "633", - "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, - "low_alchemy": "652", - "high_alchemy": "979", - "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, - "low_alchemy": "5120", - "high_alchemy": "7680", - "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, - "low_alchemy": "2560", - "high_alchemy": "3840", - "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, - "low_alchemy": "2176", - "high_alchemy": "3264", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "5120", - "high_alchemy": "7680", - "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, - "low_alchemy": "2560", - "high_alchemy": "3840", - "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, - "low_alchemy": "2176", - "high_alchemy": "3264", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "21760", - "high_alchemy": "32640", - "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "21760", - "high_alchemy": "32640", - "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", - "low_alchemy": "16", - "high_alchemy": "24", + "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" - }, - { - "ge_buy_limit": "2", - "examine": "Parlez-vous francais? (Do you speak French?)", "durability": null, - "low_alchemy": "32", - "high_alchemy": "48", "equipment_slot": "0", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "34300", - "name": "Blue beret", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": true, - "id": "2633" + "tradeable": "true" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "34300", + "id": "2632", + "name": "Highwayman mask", + "archery_ticket_price": "0", "durability": null, - "name": "Blue beret", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2634" + "ge_buy_limit": "2", + "tradeable": "true" }, { + "id": "2633", + "name": "Blue 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": "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, - "low_alchemy": "32", - "high_alchemy": "48", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "117500", - "name": "Black beret", - "tradeable": "true", - "archery_ticket_price": "0", + "ge_buy_limit": "2", "hat": true, - "id": "2635" + "lendable": "true", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "117500", - "durability": null, + "id": "2636", "name": "Black beret", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2636" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", + "id": "2637", + "name": "White beret", "examine": "Parlez-vous francais? (Do you speak French?)", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "32", - "high_alchemy": "48", "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": true, "lendable": "true", - "grand_exchange_price": "328000", + "tradeable": "true" + }, + { + "id": "2638", "name": "White beret", - "tradeable": "true", "archery_ticket_price": "0", - "hat": true, - "id": "2637" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "328000", - "durability": null, - "name": "White beret", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2638" - }, - { - "ge_buy_limit": "2", - "examine": "All for one and one for all!", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "29100", + "id": "2639", "name": "Tan cavalier", - "tradeable": "true", + "examine": "All for one and one for all!", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", "hat": true, - "id": "2639" + "lendable": "true", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "29100", - "durability": null, + "id": "2640", "name": "Tan cavalier", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2640" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "All for one and one for all!", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "23600", + "id": "2641", "name": "Dark cavalier", - "tradeable": "true", - "archery_ticket_price": "0", - "hat": true, - "id": "2641" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "23600", - "durability": null, - "name": "Dark cavalier", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2642" - }, - { - "ge_buy_limit": "2", "examine": "All for one and one for all!", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "384500", - "name": "Black cavalier", - "tradeable": "true", - "archery_ticket_price": "0", + "ge_buy_limit": "2", "hat": true, - "id": "2643" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "384500", - "durability": null, - "name": "Black cavalier", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2644" - }, - { "lendable": "true", - "ge_buy_limit": "2", - "examine": "A minimalist's hat.", - "grand_exchange_price": "91300", - "durability": null, - "name": "Red headband", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "24", + "tradeable": "true" + }, + { + "id": "2642", + "name": "Dark cavalier", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2643", + "name": "Black cavalier", + "examine": "All for one and one for all!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "hat": true, + "lendable": "true", + "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", - "low_alchemy": "16", - "high_alchemy": "24", + "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", - "low_alchemy": "16", - "high_alchemy": "24", "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", - "low_alchemy": "72", - "high_alchemy": "108", + "id": "2650", + "name": "Brown headband", "archery_ticket_price": "0", + "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "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,1,0,1" - }, - { - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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,1,0,1" - }, - { - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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,1,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, - "low_alchemy": "21760", - "high_alchemy": "32640", - "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "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,1,0,1" - }, - { - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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,1" - }, - { - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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,1,0,1" - }, - { - "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, - "low_alchemy": "21760", - "high_alchemy": "32640", - "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "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,1,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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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,1,0,1" - }, - { - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "21760", - "high_alchemy": "32640", - "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", + "id": "2676", + "name": "Guthix kiteshield", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "2677", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2677" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2678", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2678" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2679", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2679" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2680", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2680" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2681", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2681" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2682", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2682" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2683", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2683" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2684", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2684" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2685", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2685" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2686", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2686" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2687", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2687" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2688", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2688" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2689", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2689" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2690", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2690" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2691", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2691" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2692", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2692" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2693", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2693" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2694", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2694" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2695", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2695" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2696", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2696" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2697", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2697" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2698", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2698" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2699", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2699" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2700", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2700" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2701", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2701" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2702", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2702" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2703", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2703" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2704", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2704" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2705", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2705" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2706", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2706" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2707", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2707" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2708", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2708" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2709", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2709" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2710", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2710" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2711", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2711" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2712", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2712" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2713", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2713" + "durability": null, + "shop_price": "5000" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "2714", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2715" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2718" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2721" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2722" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2724" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2726" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2728" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2730" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2732" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2734" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2736" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2738" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2740" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2742" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2744" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2746" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2748" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2747", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2773" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2774" + "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2775" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2777" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2779" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2781" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2780", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2782" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2783" + "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2784" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2782", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2785" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2786" + "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2787" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2789" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2791" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2790", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2792" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", + "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.", "archery_ticket_price": "0", - "id": "2793" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2793", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "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": "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "2795" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2796" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", "archery_ticket_price": "0", - "id": "2798" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2802" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2804" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2806" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2808" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2810" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2812" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2814" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2816" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2818" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2820" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2822" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2824" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2826" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2828" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "2830" + "weight": "5" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2829", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2831" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", + "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.", "archery_ticket_price": "0", - "id": "2833" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2833", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2835" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2835", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2837" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2837", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2839" + "durability": null, + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2839", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2841" + "durability": null, + "shop_price": "5000" }, { - "examine": "I need to answer this correctly.", + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2847" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2848" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "2855", "name": "Clue scroll", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "2855" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2856" + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", + "id": "2856", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2857" + "shop_price": "5000" }, { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", + "id": "2857", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "2858" + "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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.", - "durability": null, - "low_alchemy": "10", - "high_alchemy": "15", - "equipment_slot": "13", - "grand_exchange_price": "49", - "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" + "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 }, { - "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", - "low_alchemy": "34", - "high_alchemy": "51", - "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", - "low_alchemy": "52", - "high_alchemy": "78", - "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, - "low_alchemy": "200", - "high_alchemy": "300", - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "5", - "grand_exchange_price": "709", - "name": "Elemental shield", - "tradeable": "true", + "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" - }, - { - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "grand_exchange_price": "1570", - "examine": "Very stylish!", - "durability": null, - "name": "Boots", - "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, - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "grand_exchange_price": "741", - "examine": "Made by werewolves for werewolves.", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", + "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", - "grand_exchange_price": "844", - "examine": "A silly pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", + "id": "2899", + "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": "2900", - "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": "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", - "grand_exchange_price": "1039", - "examine": "These gloves from Canifis will keep my hands warm!", - "durability": null, - "name": "Gloves", - "tradeable": "true", + "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", - "grand_exchange_price": "1359", - "examine": "Very stylish!", - "durability": null, - "name": "Boots", - "tradeable": "true", + "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, - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "grand_exchange_price": "474", - "examine": "Made by werewolves for werewolves.", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", + "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", - "grand_exchange_price": "1445", - "examine": "A silly pointed hat.", - "durability": null, - "tradeable": "true", - "name": "Hat", + "id": "2909", + "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": "2910", - "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": "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", - "grand_exchange_price": "1254", - "examine": "These gloves from Canifis will keep my hands warm!", - "durability": null, - "name": "Gloves", - "tradeable": "true", + "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", - "grand_exchange_price": "1054", - "examine": "Very stylish!", - "durability": null, - "name": "Boots", - "tradeable": "true", + "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, - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "grand_exchange_price": "603", - "examine": "Made by werewolves for werewolves.", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", + "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", - "grand_exchange_price": "997", - "examine": "A silly pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", + "id": "2919", + "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": "2920", - "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": "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", - "grand_exchange_price": "1221", - "examine": "These gloves from Canifis will keep my hands warm!", - "durability": null, - "name": "Gloves", - "tradeable": "true", + "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", - "grand_exchange_price": "737", - "examine": "Very stylish!", - "durability": null, - "name": "Boots", - "tradeable": "true", + "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, - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "grand_exchange_price": "637", - "examine": "Made by werewolves for werewolves.", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "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", - "grand_exchange_price": "925", - "examine": "A silly pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2930", - "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": "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", - "grand_exchange_price": "1829", - "examine": "These gloves from Canifis will keep my hands warm!", - "durability": null, - "name": "Gloves", - "tradeable": "true", - "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", - "grand_exchange_price": "988", "examine": "Very stylish!", - "durability": null, - "name": "Boots", - "tradeable": "true", "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, - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "grand_exchange_price": "927", - "examine": "Made by werewolves for werewolves.", - "durability": null, - "name": "Robe bottoms", - "tradeable": "true", - "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", - "grand_exchange_price": "1209", + "tradeable": "true" + }, + { + "id": "2930", + "name": "Hat", "examine": "A silly pointed hat.", - "durability": null, - "name": "Hat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2940", "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0", - "equipment_slot": "0" - }, - { + "durability": null, + "equipment_slot": "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", - "grand_exchange_price": "634", - "examine": "These gloves from Canifis will keep my hands warm!", - "durability": null, - "name": "Gloves", "tradeable": "true", - "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", - "low_alchemy": "120", - "high_alchemy": "180", + "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", - "low_alchemy": "120", - "tradeable": "false", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", + "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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", + "attack_speed": "4", + "bonuses": "11,5,-4,1,0,0,0,0,1,0,1,10,5,0,0", + "defence_anim": "378", + "durability": null, + "equipment_slot": "3", + "render_anim": "2584", "tradeable": "false", - "archery_ticket_price": "0", - "id": "2952", - "bonuses": "11,5,-4,1,0,0,0,0,1,0,1,10,5,0,0" + "weapon_interface": "5", + "weight": "0.4" }, { - "examine": "It's a bucket of water.", - "grand_exchange_price": "74", - "durability": null, + "id": "2953", "name": "Bucket of water", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "3", "archery_ticket_price": "0", - "id": "2954" + "durability": null, + "tradeable": "true", + "weight": "3" }, { - "shop_price": "5", - "ge_buy_limit": "100", + "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.", - "grand_exchange_price": "16", - "durability": null, - "name": "Moonlight mead", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", "archery_ticket_price": "0", - "id": "2955" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "16", - "durability": null, - "name": "Moonlight mead", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2956" + "shop_price": "5", + "tradeable": "true" }, { + "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.", - "durability": null, - "name": "Druid pouch", - "low_alchemy": "2", - "high_alchemy": "3", "archery_ticket_price": "0", - "id": "2957" + "durability": null }, { + "id": "2958", + "name": "Druid pouch", "examine": "A druid pouch.", - "durability": null, - "name": "Druid pouch", - "low_alchemy": "2", - "high_alchemy": "3", "archery_ticket_price": "0", - "id": "2958" + "durability": null }, { + "id": "2959", + "name": "Rotten food", "examine": "Erhhh! It stinks.", - "durability": null, - "name": "Rotten food", - "tradeable": "true", - "weight": "0.4", "archery_ticket_price": "0", - "id": "2959" + "durability": null, + "tradeable": "true", + "weight": "0.4" }, { - "durability": null, + "id": "2960", "name": "Rotten food", - "tradeable": "true", "archery_ticket_price": "0", - "id": "2960" + "durability": null, + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "It's a silver sickle.", - "durability": null, - "low_alchemy": "70", - "high_alchemy": "105", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "28", - "durability": null, "name": "Silver sickle", - "tradeable": "true", + "examine": "It's a silver sickle.", "archery_ticket_price": "0", - "id": "2962" - }, - { - "examine": "It's a blessed silver sickle.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "1.5", - "weapon_interface": "6", - "defence_anim": "397", - "equipment_slot": "3", "attack_anims": "390,390,381,390", - "name": "Silver sickle(b)", + "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", - "bonuses": "0,0,0,0,0,0,1,1,1,1,0,1,5,0,0" + "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", + "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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...", - "durability": null, - "low_alchemy": "35", - "high_alchemy": "53", - "weight": "0.3", - "equipment_slot": "9", - "grand_exchange_price": "1500000", - "name": "Pirate's hook", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "2997", - "bonuses": "0,0,0,0,0,1,5,3,0,0,1,0,0,0,0" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "A fresh herb.", - "grand_exchange_price": "4056", - "durability": null, - "name": "Clean toadflax", - "tradeable": "true", - "low_alchemy": "19", - "high_alchemy": "28", - "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", - "low_alchemy": "23", - "high_alchemy": "35", - "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", - "low_alchemy": "19", - "high_alchemy": "28", - "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", - "low_alchemy": "23", - "high_alchemy": "35", + "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", - "low_alchemy": "60", - "high_alchemy": "90", + "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", - "low_alchemy": "44", - "high_alchemy": "66", + "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", - "low_alchemy": "44", - "high_alchemy": "66", + "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", - "low_alchemy": "44", - "high_alchemy": "66", + "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", - "low_alchemy": "44", - "high_alchemy": "66", + "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", - "low_alchemy": "92", - "high_alchemy": "138", + "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", - "low_alchemy": "92", - "high_alchemy": "138", + "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", - "low_alchemy": "92", - "high_alchemy": "138", + "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", - "low_alchemy": "92", - "high_alchemy": "138", - "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", - "low_alchemy": "96", - "high_alchemy": "144", - "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", - "low_alchemy": "96", - "high_alchemy": "144", - "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", - "low_alchemy": "96", - "high_alchemy": "144", - "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", - "low_alchemy": "96", - "high_alchemy": "144", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "6800", - "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,10,0,2,3,1,10,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "high_alchemy": "10200", - "weight": "2.2", - "weapon_interface": "1", - "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", - "low_alchemy": "18000", - "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,10,0,2,3,1,10,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "shop_price": "160", - "durability": null, - "high_alchemy": "27000", - "weight": "2", - "weapon_interface": "1", - "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, - "low_alchemy": "19", - "high_alchemy": "28", - "weight": "0.9", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "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, - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "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, - "low_alchemy": "70", - "high_alchemy": "105", - "weight": "0.9", - "attack_speed": "4", + "tradeable": "true", "two_handed": "true", "weapon_interface": "9", - "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, - "low_alchemy": "144", - "high_alchemy": "216", - "weight": "6", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "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, - "low_alchemy": "190", - "high_alchemy": "285", - "weight": "0.9", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "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, - "low_alchemy": "480", - "high_alchemy": "720", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "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, - "low_alchemy": "4800", - "high_alchemy": "7200", - "weight": "0.9", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "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, - "low_alchemy": "4", - "high_alchemy": "7", - "destroy": "true", - "weight": "0.3", - "equipment_slot": "10", - "grand_exchange_price": "51400", - "name": "Climbing boots", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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, - "low_alchemy": "22400", - "high_alchemy": "33600", - "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, - "low_alchemy": "100000", - "high_alchemy": "150000", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "3150" - }, - { - "examine": "Small brightly coloured tropical fish.", - "durability": null, - "name": "Karambwanji", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.99", - "archery_ticket_price": "0", - "id": "3151" - }, - { - "examine": "Freshly made octopus paste. It smells quite nauseating.", - "durability": null, - "name": "Karambwan paste", - "low_alchemy": "6", - "high_alchemy": "9", - "archery_ticket_price": "0", - "id": "3152" - }, - { - "examine": "Freshly made octopus paste. It smells quite nauseating.", - "durability": null, - "name": "Karambwan paste", - "low_alchemy": "6", - "high_alchemy": "9", - "archery_ticket_price": "0", - "id": "3153" - }, - { - "examine": "Freshly made octopus paste. It smells quite nauseating.", - "durability": null, - "name": "Karambwan paste", - "low_alchemy": "6", - "high_alchemy": "9", - "archery_ticket_price": "0", - "id": "3154" - }, - { - "examine": "This paste smells of raw fish./This paste smells of cooked fish.", - "durability": null, - "name": "Karambwanji paste", - "low_alchemy": "6", - "high_alchemy": "9", - "archery_ticket_price": "0", - "id": "3155" - }, - { - "examine": "This paste smells of raw fish./This paste smells of cooked fish.", - "durability": null, - "name": "Karambwanji paste", - "low_alchemy": "6", - "high_alchemy": "9", - "archery_ticket_price": "0", - "id": "3156" - }, - { - "requirements": "{10,65}", - "shop_price": "5", + "equip_audio": "1003", + "equipment_slot": "3", "ge_buy_limit": "100", - "examine": "A wide bodied and thin necked vessel, encrusted with sea salt. / The Karambwan Vessel is loaded with Karambwanji", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "3", - "equipment_slot": "3", - "grand_exchange_price": "221", - "name": "Karambwan vessel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3157" - }, - { - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "archery_ticket_price": "0", - "id": "3164" - }, - { - "shop_price": "27", - "examine": "A very strong spirit brewed in Karamja.", - "durability": null, - "name": "Karamjan rum", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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 bronze tipped spear.", - "walk_anim": "1205", - "low_alchemy": "10", - "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": "266", - "stand_anim": "813", - "tradeable": "true", - "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", - "durability": null, - "high_alchemy": "15", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Bronze spear(kp)" - }, - { - "turn90cw_anim": "1207", - "examine": "An iron tipped spear.", - "walk_anim": "1205", - "low_alchemy": "36", - "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": "304", - "stand_anim": "813", - "tradeable": "true", - "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", - "durability": null, - "high_alchemy": "54", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Iron spear(kp)" - }, - { - "turn90cw_anim": "1207", - "examine": "A steel tipped spear.", - "walk_anim": "1205", - "low_alchemy": "130", - "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": "503", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,5}", - "durability": null, - "high_alchemy": "195", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Steel spear(kp)" - }, - { - "turn90cw_anim": "1207", - "examine": "A mithril tipped spear.", - "walk_anim": "1205", - "low_alchemy": "338", - "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": "368", - "stand_anim": "813", - "tradeable": "true", - "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", + "render_anim": "2583", "requirements": "{0,20}", - "durability": null, - "high_alchemy": "507", - "weight": "1.8", - "weapon_interface": "14", - "render_anim": "28", - "name": "Mithril spear(kp)" + "tradeable": "true", + "two_handed": "true", + "weapon_interface": "9", + "weight": "0.9" }, { - "turn90cw_anim": "1207", - "examine": "An adamant tipped spear.", - "walk_anim": "1205", - "low_alchemy": "832", - "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": "1093", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "id": "3100", + "name": "Adamant claws", + "examine": "A set of fighting claws.", "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", + "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}", - "durability": null, - "high_alchemy": "1248", - "weight": "2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Adamant spear(kp)" - }, - { - "turn90cw_anim": "1207", - "examine": "A rune tipped spear.", - "walk_anim": "1205", - "low_alchemy": "8320", - "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": "12089", - "stand_anim": "813", "tradeable": "true", - "run_anim": "1210", - "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,40}", - "durability": null, - "high_alchemy": "12480", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "lendable": "true", - "name": "Rune spear(kp)" + "two_handed": "true", + "weapon_interface": "9" }, { - "turn90cw_anim": "1207", - "examine": "A dragon tipped spear.", - "walk_anim": "1205", + "id": "3101", + "name": "Rune 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": "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", - "low_alchemy": "24960", - "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": "37446", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,60}", - "durability": null, - "high_alchemy": "37440", - "weight": "2", - "weapon_interface": "14", - "render_anim": "28", "lendable": "true", - "name": "Dragon spear(kp)" + "render_anim": "2583", + "requirements": "{0,40}", + "tradeable": "true", + "two_handed": "true", + "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "32", - "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, - "high_alchemy": "48", - "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", - "low_alchemy": "145", - "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, - "high_alchemy": "218", - "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", - "low_alchemy": "400", - "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, - "high_alchemy": "600", - "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", - "low_alchemy": "768", - "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, - "high_alchemy": "1152", - "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", - "low_alchemy": "1352", - "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, - "high_alchemy": "2028", - "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", - "low_alchemy": "3328", - "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, - "high_alchemy": "4992", - "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", - "low_alchemy": "51200", - "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, - "high_alchemy": "76800", - "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", - "low_alchemy": "130000", - "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, - "high_alchemy": "195000", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "7", - "high_alchemy": "11", "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", - "high_alchemy": "1", "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.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "2", - "equipment_slot": "0", - "grand_exchange_price": "56", - "name": "Myre snelm", - "tradeable": "true", - "archery_ticket_price": "0", "id": "3327", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,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, - "low_alchemy": "120", - "high_alchemy": "180", - "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.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "equipment_slot": "0", - "grand_exchange_price": "57", - "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" - }, - { - "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", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1.9", - "equipment_slot": "0", - "grand_exchange_price": "73", - "name": "Bruise blue snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3333", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,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.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "2", - "equipment_slot": "0", - "grand_exchange_price": "63", - "name": "Broken bark snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3335", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,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.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "2", "equipment_slot": "0", - "grand_exchange_price": "58", - "name": "Myre snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3337", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "58", - "durability": null, - "name": "Myre snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3338" - }, - { "remove_head": "true", - "shop_price": "300", + "tradeable": "true", + "weight": "2" + }, + { + "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", "equipment_slot": "0", - "grand_exchange_price": "54", - "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" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "54", - "durability": null, - "name": "Ochre snelm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3342" - }, - { "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", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1.9", - "equipment_slot": "0", - "grand_exchange_price": "248", - "name": "Bruise blue snelm", - "tradeable": "true", "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", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "248", - "durability": null, "name": "Bruise blue snelm", - "tradeable": "true", + "examine": "A moody blue snail shell helmet, mort myre snail shell helm", "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", + "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,0,0,0,0,0", "durability": null, - "name": "Blamish myre shell", - "tradeable": "true", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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": "false", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "weight": "0.9", - "absorb": "3,1,0", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "5993", - "name": "Splitbark helm", + "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, - "low_alchemy": "18000", - "high_alchemy": "27000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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)", - "low_alchemy": "5", - "high_alchemy": "8", - "archery_ticket_price": "0", - "id": "3408" - }, - { - "examine": "3 doses serum 207 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 207 (3)", - "low_alchemy": "5", - "high_alchemy": "8", - "archery_ticket_price": "0", - "id": "3410" - }, - { - "examine": "2 doses serum 207 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 207 (2)", - "low_alchemy": "5", - "high_alchemy": "8", - "archery_ticket_price": "0", - "id": "3412" - }, - { - "examine": "1 dose serum 207 as described in Herbi Flax's diary.", - "durability": null, - "name": "Serum 207 (1)", - "low_alchemy": "5", - "high_alchemy": "8", - "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)", - "low_alchemy": "5", - "high_alchemy": "8", - "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)", - "low_alchemy": "5", - "high_alchemy": "8", - "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)", - "low_alchemy": "5", - "high_alchemy": "8", - "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)", - "low_alchemy": "5", - "high_alchemy": "8", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "3", - "high_alchemy": "4", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "64", - "high_alchemy": "96", - "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", - "low_alchemy": "192", - "high_alchemy": "288", - "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", - "low_alchemy": "256", - "high_alchemy": "384", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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.", - "durability": null, - "low_alchemy": "768", - "high_alchemy": "1152", - "weight": "8", - "equipment_slot": "7", - "grand_exchange_price": "3077", - "name": "Black plateskirt (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3472", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "Black plateskirt with gold trim.", - "durability": null, - "low_alchemy": "768", - "high_alchemy": "1152", - "weight": "9", - "equipment_slot": "7", - "grand_exchange_price": "10600", - "name": "Black plateskirt (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3473", - "bonuses": "0,0,0,-21,-7,21,20,19,-4,20,3,0,0,0,0" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "Adamant plateskirt with trim.", - "durability": null, - "low_alchemy": "2560", - "high_alchemy": "3840", - "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, - "low_alchemy": "2560", - "high_alchemy": "3840", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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,1,0,1" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune plateskirt in the colours of Saradomin.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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,1,0,1" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune plateskirt in the colours of Guthix.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "weight": "9", - "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,1,0,0" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "Rune platebody with complete gold trim & plating.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "weight": "10", - "absorb": "3,0,6", - "equipment_slot": "4", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "2200000", - "name": "Gilded platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "weight": "9", - "absorb": "2,0,4", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "162300", - "name": "Gilded plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "21760", - "high_alchemy": "32640", - "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3490" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3491" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3492" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3493" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3494" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3495" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3496" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3497" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3498" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3499" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3500" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3501" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3502" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3503" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3504" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3505" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3506" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3507" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3508" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3509" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3511" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3512" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3513" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3514" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3515" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3517" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3519" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3521" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3523" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3524" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3525" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3527" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3529" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3531" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3533" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3535" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3537" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3539" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3541" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3543" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3545" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3547" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3549" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3551" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3553" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3555" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3557" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3559" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3561" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3563" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "3565" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "3567" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "3569" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "3571" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3572" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3573" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3574" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "3576" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "3578" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3579" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3581" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3583" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3585" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3587" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3589" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3591" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3593" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3595" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3597" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3598" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3600" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3601" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "3603" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3604" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3605" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3607" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3609" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3610" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3611" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3612" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3613" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3614" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3615" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3616" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "3617" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", + "examine": "Rune plateskirt in the colours of Zamorak.", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "3674" - }, - { + "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0", "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", - "archery_ticket_price": "0", - "id": "3677" - }, - { - "shop_price": "13000", - "examine": "An exquisitely shaped tool specially designed for fixing temples.", - "durability": null, - "name": "Flamtaer hammer", - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "3678" - }, - { - "examine": "A sealed letter of recommendation.", - "durability": null, - "name": "Picture", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3685" - }, - { - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "1.8", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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)", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "archery_ticket_price": "0", - "id": "3695", - "stand_turn_anim": "6661" - }, - { - "examine": "Talisman to bind the Draugen.", - "durability": null, - "name": "Hunters' talisman", - "archery_ticket_price": "0", - "id": "3696" - }, - { - "examine": "Talisman to bind the Draugen.", - "durability": null, - "name": "Hunters' talisman", - "archery_ticket_price": "0", - "id": "3697" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Some flowers from a distant land.", - "durability": null, - "name": "Exotic flower", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3698" - }, - { - "examine": "A hauntingly beautiful love ballad.", - "durability": null, - "name": "Fremennik ballad", - "archery_ticket_price": "0", - "id": "3699" - }, - { - "examine": "A pair of sturdy made custom boots.", - "durability": null, - "name": "Sturdy boots", - "archery_ticket_price": "0", - "id": "3700" - }, - { - "examine": "Could probably be a bit clearer?", - "durability": null, - "name": "Tracking map", - "archery_ticket_price": "0", - "id": "3701" - }, - { - "examine": "A finely crafted string for a custom bow", - "durability": null, - "name": "Custom bow string", - "archery_ticket_price": "0", - "id": "3702" - }, - { - "examine": "An extremely odd, non-edible fish.", - "durability": null, - "name": "Unusual fish", - "archery_ticket_price": "0", - "id": "3703" - }, - { - "examine": "Map showing the best fishing spots out at sea.", - "durability": null, - "name": "Sea fishing map", - "archery_ticket_price": "0", - "id": "3704" - }, - { - "examine": "An estimate of expected local weather conditions.", - "durability": null, - "name": "Weather forecast", - "archery_ticket_price": "0", - "id": "3705" - }, - { - "examine": "Shows the wearer is worthy of the Champions table.", - "durability": null, - "name": "Champions token", - "archery_ticket_price": "0", - "id": "3706" - }, - { - "examine": "Probably the greatest cocktail in the world.", - "durability": null, - "name": "Legendary cocktail", - "archery_ticket_price": "0", - "id": "3707" - }, - { - "examine": "A signed statement promising a reduction on sales tax.", - "durability": null, - "name": "Fiscal statement", - "archery_ticket_price": "0", - "id": "3708" - }, - { - "shop_price": "5000", - "examine": "A legally binding contract promising not to enter the longhall.", - "durability": null, - "name": "Promissory note", - "archery_ticket_price": "0", - "id": "3709" - }, - { - "examine": "This employment contract is for a warrior to act as a bodyguard.", - "durability": null, - "name": "Warriors' contract", - "archery_ticket_price": "0", - "id": "3710" - }, - { - "shop_price": "325", - "examine": "A lot of beer in a barrel.", - "grand_exchange_price": "1555", - "durability": null, - "name": "Keg of beer", - "tradeable": "true", - "low_alchemy": "130", - "high_alchemy": "195", - "weight": "20", - "archery_ticket_price": "0", - "id": "3711" - }, - { - "examine": "Suspiciously close to beer, but without the side effects.", - "durability": null, - "name": "Low alcohol keg", - "weight": "20", - "archery_ticket_price": "0", - "id": "3712" - }, - { - "examine": "It's some kind of weird little parcel thing.", - "durability": null, - "name": "Strange object", - "archery_ticket_price": "0", - "id": "3713" - }, - { - "examine": "It's some kind of weird little parcel thing.", - "durability": null, - "name": "Lit strange object", - "archery_ticket_price": "0", - "id": "3714" - }, - { - "examine": "A red coloured disk, apparently made out of wood.", - "durability": null, - "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" - }, - { - "examine": "A very attractive magnet.", - "durability": null, - "name": "Magnet", - "low_alchemy": "1", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "3718" - }, - { - "examine": "Some blue thread.", - "durability": null, - "name": "Thread", - "archery_ticket_price": "0", - "id": "3719" - }, - { - "examine": "A small pick for cracking small objects.", - "durability": null, - "name": "Pick", - "archery_ticket_price": "0", - "id": "3720" - }, - { - "examine": "Might be fun to play with in the bath.", - "durability": null, - "name": "Ship toy", - "archery_ticket_price": "0", - "id": "3721" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "It's a bucket of salty water.", - "durability": null, - "name": "Full bucket", - "archery_ticket_price": "0", - "id": "3722" - }, - { - "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, - "name": "4/5ths full bucket", - "archery_ticket_price": "0", - "id": "3723" - }, - { - "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, - "name": "3/5ths full bucket", - "archery_ticket_price": "0", - "id": "3724" - }, - { - "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, - "name": "2/5ths full bucket", - "archery_ticket_price": "0", - "id": "3725" - }, - { - "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, - "name": "1/5ths full bucket", - "archery_ticket_price": "0", - "id": "3726" - }, - { - "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": "Empty bucket", - "tradeable": "true", - "high_alchemy": "1", - "weight": "1", - "archery_ticket_price": "0", - "id": "3727" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "This bucket of water is frozen solid.", - "durability": null, - "name": "Frozen bucket", - "archery_ticket_price": "0", - "id": "3728" - }, - { - "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, - "name": "Full jug", - "archery_ticket_price": "0", - "id": "3729" - }, - { - "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, - "name": "2/3rds full jug", - "archery_ticket_price": "0", - "id": "3730" - }, - { - "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, - "name": "1/3rds full jug", - "archery_ticket_price": "0", - "id": "3731" - }, - { - "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, - "name": "Empty jug", - "tradeable": "true", - "weight": "0.4", - "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, - "name": "Frozen jug", - "archery_ticket_price": "0", - "id": "3733" - }, - { - "examine": "An unusually shaped vase. You can see something glinting inside.", - "durability": null, - "name": "Vase", - "archery_ticket_price": "0", - "id": "3734" - }, - { - "examine": "An unusually shaped vase full of water. You can see something glinting inside.", - "durability": null, - "name": "Vase of water", - "archery_ticket_price": "0", - "id": "3735" - }, - { - "examine": "An unusually shaped vase full of ice. You can see something glinting inside.", - "durability": null, - "name": "Frozen vase", - "archery_ticket_price": "0", - "id": "3736" - }, - { - "examine": "This looks like a lid to some kind of container.", - "durability": null, - "name": "Vase lid", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "3737" - }, - { - "examine": "The lid is screwed on tightly. ", - "durability": null, - "name": "Sealed vase", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3738" - }, - { - "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" - }, - { - "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" - }, - { - "examine": "A small, cold key.", - "durability": null, - "name": "Frozen key", - "archery_ticket_price": "0", - "id": "3741" - }, - { - "examine": "The colouring on it seems to be some kind of sticky goop.", - "durability": null, - "name": "Red herring", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3742" - }, - { - "examine": "A red coloured disk, apparently made out of wood.", - "durability": null, - "name": "Red disk", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3743" - }, - { - "examine": "A simple looking disk made of wood.", - "durability": null, - "name": "Wooden disk", - "archery_ticket_price": "0", - "id": "3744" - }, - { - "shop_price": "27", - "examine": "The key to leave the Seer's house.", - "durability": null, - "name": "Seer's key", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "3745" - }, - { - "examine": "Yup, it's sticky, it's red and it's goop.", - "durability": null, - "name": "Sticky red goop", - "archery_ticket_price": "0", - "id": "3746" - }, - { - "durability": null, - "name": "Sticky red goop", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3747" - }, - { - "remove_head": "true", - "examine": "A sturdy helm worn only by Fremennik clan members.", - "durability": null, - "name": "Fremennik helm", - "low_alchemy": "2600", - "high_alchemy": "3900", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "3748", - "absorb": "1,0,2", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "requirements": "{1,45}", - "shop_price": "78000", - "ge_buy_limit": "100", - "examine": "This helmet is worn by archers.", - "durability": null, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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", - "archery_ticket_price": "0", - "id": "3750" - }, - { - "remove_head": "true", - "requirements": "{1,45}", - "shop_price": "78000", - "ge_buy_limit": "100", - "examine": "This helmet is worn by berserkers.", - "durability": null, - "low_alchemy": "31200", - "high_alchemy": "46800", - "weight": "2.7", - "absorb": "1,0,3", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "60900", - "name": "Berserker helm", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "weight": "2.7", - "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.", - "durability": null, - "name": "Warrior helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3754" - }, - { - "remove_head": "true", - "requirements": "{1,45}", - "shop_price": "78000", - "ge_buy_limit": "100", - "examine": "This helm is worn by farseers.", - "durability": null, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "47900", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Farseer helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3756" - }, - { - "examine": "A sword used only by Fremennik warriors.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1.8", - "attack_speed": "4", - "weapon_interface": "6", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "390,390,381,390", - "name": "Fremennik blade", - "archery_ticket_price": "0", - "id": "3757", - "bonuses": "6,29,-2,0,0,0,1,0,0,0,0,28,0,0,0" - }, - { - "examine": "A shield worn by Fremennik warriors.", - "durability": null, - "name": "Fremennik shield", - "low_alchemy": "2000", - "high_alchemy": "3000", - "destroy": "true", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "3758", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", - "equipment_slot": "5" - }, - { - "shop_price": "325", - "ge_buy_limit": "1000", - "examine": "The latest fashion in Rellekka.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "697", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "54", - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3761", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "581", - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3763", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "912", - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3765", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "912", - "durability": null, - "name": "Fremennik cloak", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3766" - }, - { - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "durability": null, - "low_alchemy": "130", - "high_alchemy": "195", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "100", - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3767", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "100", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3768" - }, - { - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "durability": null, - "low_alchemy": "130", - "high_alchemy": "195", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "486", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "486", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3770" - }, - { - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "durability": null, - "low_alchemy": "130", - "high_alchemy": "195", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "242", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "242", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3772" - }, - { - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "durability": null, - "low_alchemy": "130", - "high_alchemy": "195", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "500", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "500", - "durability": null, - "name": "Fremennik shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3774" - }, - { - "shop_price": "325", - "ge_buy_limit": "100", - "examine": "The latest in Fremennik fashion.", - "durability": null, - "low_alchemy": "130", - "high_alchemy": "195", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "450", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "674", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "809", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "632", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "517", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "398", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "283", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "equipment_slot": "1", - "grand_exchange_price": "966", - "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" - }, - { - "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!", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "0.5", - "equipment_slot": "10", - "grand_exchange_price": "205", - "name": "Fremennik boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "3791", - "bonuses": "0,0,0,0,0,0,1,1,0,0,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "0.9", - "equipment_slot": "4", - "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.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "0.9", "equipment_slot": "7", - "grand_exchange_price": "663", - "name": "Fremennik skirt", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3795", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" + "weight": "9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "663", - "durability": null, - "name": "Fremennik skirt", - "tradeable": "true", + "id": "3479", + "name": "Saradomin plateskirt", + "examine": "Rune plateskirt in the colours of Saradomin.", + "absorb": "2,0,4", "archery_ticket_price": "0", - "id": "3796" + "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": "650", - "ge_buy_limit": "100", - "examine": "A silly pointed hat.", + "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, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "1", + "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", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "10" + }, + { + "id": "3482", + "name": "Gilded platebody", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "3483", + "name": "Gilded platelegs", + "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", + "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", + "name": "Gilded plateskirt", + "examine": "Rune plateskirt 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, + "equipment_slot": "7", + "ge_buy_limit": "2", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "9" + }, + { + "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", - "grand_exchange_price": "2380", - "name": "Fremennik hat", + "ge_buy_limit": "2", + "lendable": "true", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3797", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2380", + "id": "3487", + "name": "Gilded full helm", + "archery_ticket_price": "0", "durability": null, - "name": "Fremennik hat", + "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", - "archery_ticket_price": "0", - "id": "3798" + "weight": "5" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "172", - "durability": null, - "name": "Gloves", + "id": "3489", + "name": "Gilded kiteshield", "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" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "172", + "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, - "name": "Gloves", "tradeable": "true", - "archery_ticket_price": "0", - "id": "3800" + "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", + "examine": "An exquisitely shaped tool specially designed for fixing temples.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "13000", + "weight": "1.3" + }, + { + "id": "3685", + "name": "Picture", + "examine": "A sealed letter of recommendation.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "3688", + "name": "Unstrung lyre", + "examine": "It's almost a musical instrument.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "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", + "name": "Pet rock", + "examine": "The lowest maintenance pet you will ever have.", + "archery_ticket_price": "0", + "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" + }, + { + "id": "3696", + "name": "Hunters' talisman", + "examine": "Talisman to bind the Draugen.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3697", + "name": "Hunters' talisman", + "examine": "Talisman to bind the Draugen.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3698", + "name": "Exotic flower", + "examine": "Some flowers from a distant land.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "3699", + "name": "Fremennik ballad", + "examine": "A hauntingly beautiful love ballad.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3700", + "name": "Sturdy boots", + "examine": "A pair of sturdy made custom boots.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3701", + "name": "Tracking map", + "examine": "Could probably be a bit clearer?", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3702", + "name": "Custom bow string", + "examine": "A finely crafted string for a custom bow", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3703", + "name": "Unusual fish", + "examine": "An extremely odd, non-edible fish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3704", + "name": "Sea fishing map", + "examine": "Map showing the best fishing spots out at sea.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3705", + "name": "Weather forecast", + "examine": "An estimate of expected local weather conditions.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3706", + "name": "Champions token", + "examine": "Shows the wearer is worthy of the Champions table.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3707", + "name": "Legendary cocktail", + "examine": "Probably the greatest cocktail in the world.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3708", + "name": "Fiscal statement", + "examine": "A signed statement promising a reduction on sales tax.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3709", + "name": "Promissory note", + "examine": "A legally binding contract promising not to enter the longhall.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "3710", + "name": "Warriors' contract", + "examine": "This employment contract is for a warrior to act as a bodyguard.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3711", + "name": "Keg of beer", + "examine": "A lot of beer in a barrel.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "325", + "tradeable": "true", + "weight": "20" + }, + { + "id": "3712", + "name": "Low alcohol keg", + "examine": "Suspiciously close to beer, but without the side effects.", + "archery_ticket_price": "0", + "durability": null, + "weight": "20" + }, + { + "id": "3713", + "name": "Strange object", + "examine": "It's some kind of weird little parcel thing.", + "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", + "durability": null + }, + { + "id": "3715", + "name": "Picture", + "examine": "A red coloured disk, apparently made out of wood.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "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", + "durability": null + }, + { + "id": "3719", + "name": "Thread", + "examine": "Some blue thread.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3720", + "name": "Pick", + "examine": "A small pick for cracking small objects.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3721", + "name": "Ship toy", + "examine": "Might be fun to play with in the bath.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3722", + "name": "Full bucket", + "examine": "It's a bucket of salty water.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "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", + "destroy_message": "You can reclaim this item from the place you found it.", + "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", + "destroy_message": "You can reclaim this item from the place you found it.", + "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", + "destroy_message": "You can reclaim this item from the place you found it.", + "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", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "3727", + "name": "Empty 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": "3728", + "name": "Frozen bucket", + "examine": "This bucket of water is frozen solid.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "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", + "destroy_message": "You can reclaim this item from the place you found it.", + "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", + "destroy_message": "You can reclaim this item from the place you found it.", + "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", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "3732", + "name": "Empty jug", + "examine": "This jug is empty.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "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", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "3734", + "name": "Vase", + "examine": "An unusually shaped vase. You can see something glinting inside.", + "archery_ticket_price": "0", + "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", + "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", + "durability": null + }, + { + "id": "3737", + "name": "Vase lid", + "examine": "This looks like a lid to some kind of container.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "3738", + "name": "Sealed vase", + "examine": "The lid is screwed on tightly. ", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "3739", + "name": "Sealed vase", + "examine": "The lid is screwed on tightly. It is full of water.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "3740", + "name": "Sealed vase", + "examine": "The lid is screwed on tightly. It is very cold. ", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "3741", + "name": "Frozen key", + "examine": "A small, cold key.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3742", + "name": "Red herring", + "examine": "The colouring on it seems to be some kind of sticky goop.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "3743", + "name": "Red disk", + "examine": "A red coloured disk, apparently made out of wood.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "3744", + "name": "Wooden disk", + "examine": "A simple looking disk made of wood.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "3745", + "name": "Seer's key", + "examine": "The key to leave the Seer's house.", + "archery_ticket_price": "0", + "destroy": "true", + "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.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "false" + }, + { + "id": "3747", + "name": "Sticky red goop", + "alchemizable": "true", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "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", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "2.7" + }, + { + "id": "3749", + "name": "Archer helm", + "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, + "equipment_slot": "0", "ge_buy_limit": "100", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,45}", + "shop_price": "78000", + "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", + "name": "Berserker helm", + "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", + "ge_buy_limit": "100", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,45}", + "shop_price": "78000", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "3752", + "name": "Berserker 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": "3753", + "name": "Warrior helm", + "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, + "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", + "durability": null, + "equipment_slot": "3", + "weapon_interface": "6", + "weight": "1.8" + }, + { + "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", + "destroy": "true", + "durability": null, + "equipment_slot": "5", + "weight": "2.2" + }, + { + "id": "3759", + "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": "3760", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3761", + "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": "3762", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3763", + "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": "3764", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3765", + "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": "3766", + "name": "Fremennik cloak", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "3767", + "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": "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", - "low_alchemy": "130", - "high_alchemy": "195", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "420", - "high_alchemy": "630", - "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", - "low_alchemy": "420", - "high_alchemy": "630", + "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", - "low_alchemy": "420", - "high_alchemy": "630", + "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", - "low_alchemy": "420", - "high_alchemy": "630", + "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", - "low_alchemy": "420", - "high_alchemy": "630", + "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", - "low_alchemy": "420", - "high_alchemy": "630", + "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", - "low_alchemy": "420", - "high_alchemy": "630", + "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", - "low_alchemy": "420", - "high_alchemy": "630", "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "2", - "tradeable": "false", - "high_alchemy": "3", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "4006" - }, - { - "examine": "A gold bar with a talkative monkey spirit.", - "durability": null, - "name": "Enchanted bar", - "low_alchemy": "320", - "high_alchemy": "480", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "1", - "archery_ticket_price": "0", - "id": "4009" - }, - { - "shop_price": "3", - "examine": "They are Monkey Nuts. Yummy.", - "durability": null, - "name": "Monkey nuts", - "low_alchemy": "1", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "4012" - }, - { - "shop_price": "50", - "examine": "It's a monkey bar. It looks highly nutritious.", - "durability": null, - "name": "Monkey bar", - "low_alchemy": "20", - "high_alchemy": "30", - "archery_ticket_price": "0", - "id": "4014" - }, - { - "shop_price": "300", - "examine": "It's a bowl full of mushy banana", - "durability": null, - "name": "Banana stew", - "low_alchemy": "120", - "high_alchemy": "180", - "archery_ticket_price": "0", - "id": "4016" - }, - { - "examine": "It's an amulet mould shaped like a monkey head.", - "durability": null, - "name": "M'amulet mould", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "1", - "archery_ticket_price": "0", - "id": "4020" - }, - { - "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": "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "run_anim": "1427", - "archery_ticket_price": "0", - "id": "4037", - "stand_turn_anim": "1426" - }, - { - "durability": null, - "name": "Saradomin banner", - "tradeable": "true", - "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", - "run_anim": "1427", - "archery_ticket_price": "0", - "id": "4039", - "stand_turn_anim": "1426" - }, - { - "durability": null, - "name": "Zamorak banner", - "tradeable": "true", - "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, - "low_alchemy": "520", - "high_alchemy": "780", - "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, - "low_alchemy": "312", - "high_alchemy": "468", - "weight": "1", - "equipment_slot": "0", - "name": "Decorative helm", - "archery_ticket_price": "0", - "id": "4071", - "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,3,0,0,0,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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "75000", - "high_alchemy": "75000", - "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", - "low_alchemy": "70", - "high_alchemy": "105", + "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, - "low_alchemy": "70", - "high_alchemy": "105", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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, - "low_alchemy": "108000", - "high_alchemy": "162000", - "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,20}", - "shop_price": "15000", - "ge_buy_limit": "100", - "examine": "Blue: A magical hat. ", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "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,20}", - "shop_price": "120000", - "ge_buy_limit": "100", - "examine": "The upper half of a magical robe.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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,20}", - "shop_price": "80000", - "ge_buy_limit": "100", - "examine": "The lower half of a magical robe.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", - "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,20}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Magical gloves.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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", + "tradeable": "true", + "weight": "0.4" }, { - "requirements": "{1,20}-{6,20}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Magical boots.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "weight": "0.4", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "6303", - "name": "Mystic boots", - "tradeable": "true", + "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,20}", - "shop_price": "15000", - "ge_buy_limit": "100", - "examine": "Black: A magical hat. ", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "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,20}", - "shop_price": "120000", - "ge_buy_limit": "100", - "examine": "The upper half of a magical robe.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "weight": "2.7", + "examine": "Black: A magical hat. ", "absorb": "3,1,0", - "equipment_slot": "4", - "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", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0", "durability": null, - "name": "Mystic robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4102" - }, - { - "requirements": "{1,20}-{6,20}", - "shop_price": "80000", - "ge_buy_limit": "100", - "examine": "The lower half of a magical robe.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", - "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,20}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Dark magical gloves.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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,20}", - "shop_price": "10000", - "ge_buy_limit": "100", - "examine": "Magical boots.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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,20}", - "shop_price": "15000", - "ge_buy_limit": "100", - "examine": "White: A magical hat. ", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "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", + "ge_buy_limit": "100", "hat": true, - "id": "4109", - "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", + "lendable": "true", + "requirements": "{1,20}-{6,40}", + "shop_price": "15000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4110" + "weight": "0.4" }, { - "requirements": "{1,20}-{6,20}", - "shop_price": "120000", - "ge_buy_limit": "100", - "examine": "The upper half of a magical robe.", + "id": "4100", + "name": "Mystic hat", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "weight": "2.7", + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4101", + "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", - "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" }, { - "requirements": "{1,20}-{6,20}", - "shop_price": "80000", - "ge_buy_limit": "100", + "id": "4103", + "name": "Mystic robe bottom", "examine": "The lower half of a magical robe.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", "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", - "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" + "requirements": "{1,20}-{6,40}", + "shop_price": "80000", + "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,20}", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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" - }, - { - "requirements": "{1,20}-{6,20}", + "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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.", + "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0", "durability": null, - "low_alchemy": "9", - "high_alchemy": "14", - "weight": "1.3", "equipment_slot": "10", - "grand_exchange_price": "359", - "name": "Bronze boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4119", - "bonuses": "0,0,0,-3,-1,1,2,3,0,0,0,0,0,0,0" - }, - { "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.", - "durability": null, - "low_alchemy": "33", - "high_alchemy": "50", - "weight": "1.3", - "equipment_slot": "10", - "grand_exchange_price": "17000", - "name": "Iron boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4121", - "bonuses": "0,0,0,-3,-1,2,3,4,0,0,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1.3", - "equipment_slot": "10", - "grand_exchange_price": "258", - "name": "Steel boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4123", - "bonuses": "0,0,0,-3,-1,5,6,7,0,0,5,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "230", - "high_alchemy": "345", - "weight": "1.3", - "equipment_slot": "10", - "grand_exchange_price": "317", - "name": "Black boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4125", - "bonuses": "0,0,0,-3,-1,7,8,9,0,0,7,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "312", - "high_alchemy": "468", - "weight": "1", - "equipment_slot": "10", - "grand_exchange_price": "580", - "name": "Mithril boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4127", - "bonuses": "0,0,0,-3,-1,8,9,10,0,0,8,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "768", - "high_alchemy": "1152", - "weight": "1.3", - "equipment_slot": "10", - "grand_exchange_price": "1336", - "name": "Adamant boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4129", - "bonuses": "0,0,0,-3,-1,10,11,12,0,0,9,1,0,0,0" - }, - { - "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, - "low_alchemy": "5000", - "high_alchemy": "7500", - "weight": "1.3", - "equipment_slot": "10", "lendable": "true", - "grand_exchange_price": "7474", - "name": "Rune boots", + "requirements": "{1,20}-{6,40}", + "shop_price": "10000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4131", - "bonuses": "0,0,0,-3,-1,12,13,14,0,0,10,2,0,0,0" + "weight": "0.4" }, { + "id": "4118", + "name": "Mystic boots", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "7474", - "durability": null, - "name": "Rune boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4132" + "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", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "48000", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "820", - "defence_anim": "1659", - "equipment_slot": "3", - "attack_anims": "1658,1658,1658,1658", - "destroy_message": "", - "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, - "high_alchemy": "72000", - "weight": "0.45", - "weapon_interface": "11", - "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", - "low_alchemy": "20000", - "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", - "rare_item": "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, - "high_alchemy": "30000", - "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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" }, { - "turn90cw_anim": "1207", - "examine": "A spear with a leaf-shaped point.", - "walk_anim": "1205", - "low_alchemy": "12400", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", - "attack_anims": "2080,2081,2082,2080", - "stand_anim": "813", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "4158", - "stand_turn_anim": "1209", + "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", - "requirements": "{0,55}-{18,50}", - "shop_price": "31000", + "defence_anim": "2079", "durability": null, - "high_alchemy": "18600", - "weight": "2", - "weapon_interface": "14", + "equip_audio": "2247", + "equipment_slot": "3", "render_anim": "28", - "name": "Leaf-bladed spear" + "requirements": "{0,55}-{18,50}", + "run_anim": "1210", + "shop_price": "31000", + "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" }, { - "requirements": "{0,55}-{18,50}", - "shop_price": "31000", - "examine": "A spear with a leaf-shaped point.", - "durability": null, + "id": "4159", "name": "Leaf-bladed spear", - "low_alchemy": "12400", - "high_alchemy": "18600", - "weight": "2", + "examine": "A spear with a leaf-shaped point.", "archery_ticket_price": "0", "attack_speed": "5", - "id": "4159", - "equipment_slot": "3" - }, - { - "requirements": "{18,55}-{4,50}", - "shop_price": "90", - "examine": "Arrows with a wider than normal tip.", "durability": null, - "name": "Broad arrow", - "low_alchemy": "36", - "high_alchemy": "54", - "archery_ticket_price": "0", - "id": "4160", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,28", - "equipment_slot": "13" - }, - { - "shop_price": "10", - "ge_buy_limit": "100", - "examine": "A bag of salt.", - "grand_exchange_price": "1", - "durability": null, - "name": "Bag of salt", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "4161" - }, - { - "shop_price": "500", - "ge_buy_limit": "100", - "examine": "I can even smash stone with this.", - "durability": null, - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "2.2", + "equip_audio": "2247", "equipment_slot": "3", - "grand_exchange_price": "140", - "name": "Rock hammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4162" + "requirements": "{0,55}-{18,50}", + "shop_price": "31000", + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "140", + "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", "durability": null, - "name": "Rock hammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4163" + "equipment_slot": "13", + "requirements": "{18,55}-{4,50}", + "shop_price": "90" }, { + "id": "4161", + "name": "Bag of salt", + "examine": "A bag of salt.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "4162", + "name": "Rock hammer", + "examine": "I can even smash stone with this.", + "archery_ticket_price": "0", + "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.", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "4165", + "name": "Face mask", + "archery_ticket_price": "0", + "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, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "0.1", "equipment_slot": "0", - "grand_exchange_price": "615", - "name": "Face mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4164" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "615", - "durability": null, - "name": "Face mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4165" - }, - { "requirements": "{18,15}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "These will protect my ears from loud noise.", + "tradeable": "true", + "weight": "2.6" + }, + { + "id": "4167", + "name": "Earmuffs", + "archery_ticket_price": "0", + "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, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "2.6", "equipment_slot": "0", - "grand_exchange_price": "484", - "name": "Earmuffs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4166" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "484", - "durability": null, - "name": "Earmuffs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4167" - }, - { "requirements": "{18,60}", "shop_price": "200", - "ge_buy_limit": "100", - "examine": "Protects me from any bad smells.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "8", - "equipment_slot": "0", - "grand_exchange_price": "523", - "name": "Nose peg", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4168" + "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", - "low_alchemy": "8400", - "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, - "high_alchemy": "12600", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "archery_ticket_price": "0", - "id": "4175", - "equipment_slot": "13" - }, - { - "examine": "A weapon from the Abyss.", - "rare_item": "true", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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 }, { - "examine": "Looks pretty heavy.", - "durability": null, - "low_alchemy": "108000", - "high_alchemy": "162000", - "weight": "9", - "absorb": "3,0,6", - "lendable": "true", - "grand_exchange_price": "165900", - "name": "Dragon platelegs", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4180", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" + "name": "Dragon platelegs", + "examine": "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, + "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", - "high_alchemy": "1", + "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", - "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", + "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, - "low_alchemy": "64", - "high_alchemy": "96", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "14", - "high_alchemy": "21", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", + "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 [type of bones].", - "durability": null, + "id": "4255", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed bones.", "archery_ticket_price": "0", - "id": "4255" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4256", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed small ninja monkey bones.", "archery_ticket_price": "0", - "id": "4256" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4257", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed wolf bones.", "archery_ticket_price": "0", - "id": "4257" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4258", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed burnt bones.", "archery_ticket_price": "0", - "id": "4258" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4259", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed burnt jogre bones.", "archery_ticket_price": "0", - "id": "4259" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4260", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed monkey bones.", "archery_ticket_price": "0", - "id": "4260" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4261", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed bat bones.", "archery_ticket_price": "0", - "id": "4261" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4262", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed big bones.", "archery_ticket_price": "0", - "id": "4262" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4263", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed jogre bones.", "archery_ticket_price": "0", - "id": "4263" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4264", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed zogre bones.", "archery_ticket_price": "0", - "id": "4264" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4265", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed shaikahan bones.", "archery_ticket_price": "0", - "id": "4265" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4266", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed baby dragon bones.", "archery_ticket_price": "0", - "id": "4266" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4267", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed wyvern bones.", "archery_ticket_price": "0", - "id": "4267" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4268", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed dragon bones.", "archery_ticket_price": "0", - "id": "4268" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4269", "name": "Bonemeal", - "weight": "1", - "archery_ticket_price": "0", - "id": "4269" - }, - { "examine": "A pot of crushed [type of bones].", - "durability": null, - "name": "Bonemeal", - "weight": "1", "archery_ticket_price": "0", - "id": "4270" + "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": "4271" + "durability": null, + "weight": "1" }, { - "shop_price": "100", - "examine": "A key fashioned from a shard of bone. (Shilo Village)", + "id": "4271", + "name": "Bonemeal", + "examine": "A pot of crushed Dagannoth-king bones.", + "archery_ticket_price": "0", "durability": null, + "weight": "1" + }, + { + "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", - "id": "4285", - "equipment_slot": "0" - }, - { - "examine": "It's a bucket of ectoplasm.", "durability": null, - "name": "Bucket of slime", - "weight": "3", - "archery_ticket_price": "0", - "id": "4286" - }, - { - "shop_price": "58", - "ge_buy_limit": "1000", - "examine": "I need to cook this first.", - "grand_exchange_price": "440", - "durability": null, - "name": "Raw beef", - "tradeable": "true", - "low_alchemy": "23", - "high_alchemy": "35", - "weight": "0.3", - "archery_ticket_price": "0", - "id": "4287" - }, - { - "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, - "name": "Raw chicken", - "tradeable": "true", - "low_alchemy": "24", - "high_alchemy": "36", - "weight": "0.15", - "archery_ticket_price": "0", - "id": "4289" - }, - { - "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, - "name": "Cooked chicken", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "7", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "4293" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "74", - "durability": null, - "name": "Cooked meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4294" - }, - { - "examine": "A member of the 'Humans Against Monsters' group.", - "durability": null, - "name": "Female h.a.m.", - "archery_ticket_price": "0", - "id": "4295" - }, - { - "examine": "A member of the 'Humans Against Monsters' group.", - "durability": null, - "name": "Male h.a.m.", - "archery_ticket_price": "0", - "id": "4297" - }, - { - "ge_buy_limit": "100", - "examine": "The label says 'Vivid Crimson', but it looks like pink to me!", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "weight": "0.9", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "19", - "name": "Ham shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4298", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "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!", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "weight": "0.9", - "equipment_slot": "7", - "grand_exchange_price": "11", - "name": "Ham robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4300", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11", - "durability": null, - "name": "Ham robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4301" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "Light-weight head protection and eye shield.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "weight": "0.4", "equipment_slot": "0", - "grand_exchange_price": "400", - "name": "Ham hood", - "tradeable": "true", + "tradeable": "false", + "weight": "1" + }, + { + "id": "4285", + "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" + }, + { + "id": "4286", + "name": "Bucket of slime", + "examine": "It's a bucket of ectoplasm.", + "archery_ticket_price": "0", + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "60", + "tradeable": "true", + "weight": "0.15" + }, + { + "id": "4290", + "name": "Raw chicken", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "4291", + "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": "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.", + "examine": "A member of the 'Humans Against Monsters' group.", + "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", + "durability": null + }, + { + "id": "4298", + "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", + "ge_buy_limit": "100", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "4299", + "name": "Ham shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4300", + "name": "Ham robe", + "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": "7", + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "0.9" + }, + { + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "25", - "name": "Ham cloak", - "tradeable": "true", + "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" - }, - { - "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", - "low_alchemy": "30", - "high_alchemy": "45", + "id": "4305", + "name": "Ham cloak", "archery_ticket_price": "0", + "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", - "grand_exchange_price": "59", - "durability": null, - "name": "Gloves", + "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", - "grand_exchange_price": "323", - "durability": null, - "name": "Boots", + "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", - "high_alchemy": "1", + "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "2342", - "name": "Team-1 cape", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4315", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1145", - "name": "Team-2 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "141", - "name": "Team-3 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "973", - "name": "Team-4 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1294", - "name": "Team-5 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "124", - "name": "Team-6 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1264", - "name": "Team-7 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1824", - "name": "Team-8 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "841", - "name": "Team-9 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1127", - "name": "Team-10 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "2011", - "name": "Team-11 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1591", - "name": "Team-12 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "308", - "name": "Team-13 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1504", - "name": "Team-14 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1462", - "name": "Team-15 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "313", - "name": "Team-16 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1231", - "name": "Team-17 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1678", - "name": "Team-18 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1041", - "name": "Team-19 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "703", - "name": "Team-20 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1292", - "name": "Team-21 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1368", - "name": "Team-22 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "280", - "name": "Team-23 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1512", - "name": "Team-24 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1395", - "name": "Team-25 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "191", - "name": "Team-26 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1420", - "name": "Team-27 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1920", - "name": "Team-28 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1175", - "name": "Team-29 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "552", - "name": "Team-30 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1385", - "name": "Team-31 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1168", - "name": "Team-32 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "211", - "name": "Team-33 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1128", - "name": "Team-34 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1140", - "name": "Team-35 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "84", - "name": "Team-36 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1010", - "name": "Team-37 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1833", - "name": "Team-38 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "914", - "name": "Team-39 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "582", - "name": "Team-40 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1471", - "name": "Team-41 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1072", - "name": "Team-42 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "298", - "name": "Team-43 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1268", - "name": "Team-44 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1371", - "name": "Team-45 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "140", - "name": "Team-46 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1233", - "name": "Team-47 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1836", - "name": "Team-48 cape", - "tradeable": "true", + "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" - }, - { - "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...", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1185", - "name": "Team-49 cape", - "tradeable": "true", + "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" - }, - { - "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...", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", "equipment_slot": "1", - "grand_exchange_price": "708", - "name": "Team-50 cape", + "ge_buy_limit": "100", + "shop_price": "50", "tradeable": "true", + "weight": "0.4" + }, + { + "id": "4412", + "name": "Team-49 cape", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "4413", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "708", - "durability": null, "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 hatchet.", + "id": "4414", + "name": "Team-50 cape", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "4415", "name": "Blunt axe", - "low_alchemy": "22", - "high_alchemy": "33", - "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", - "low_alchemy": "20", - "high_alchemy": "30", + "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", - "low_alchemy": "20", - "high_alchemy": "30", + "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", "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", - "low_alchemy": "20", - "high_alchemy": "30", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "6", - "high_alchemy": "9", "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", - "low_alchemy": "22", - "high_alchemy": "33", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "1", - "high_alchemy": "1", "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", - "low_alchemy": "7", - "high_alchemy": "10", - "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, - "low_alchemy": "520", - "high_alchemy": "780", - "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, - "low_alchemy": "312", - "high_alchemy": "468", - "weight": "1", - "equipment_slot": "0", - "name": "Decorative helm", - "archery_ticket_price": "0", - "id": "4506", - "bonuses": "0,0,0,-3,-1,10,11,9,-1,10,5,0,0,0,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, - "low_alchemy": "520", - "high_alchemy": "780", - "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, - "low_alchemy": "312", - "high_alchemy": "468", - "weight": "1", - "equipment_slot": "0", - "name": "Decorative helm", - "archery_ticket_price": "0", - "id": "4511", - "bonuses": "0,0,0,-3,-1,14,15,13,-1,14,6,0,0,0,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", - "low_alchemy": "40", - "high_alchemy": "60", - "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.", - "durability": null, - "low_alchemy": "11", - "high_alchemy": "16", - "weight": "0.4", - "grand_exchange_price": "1575", - "name": "Oil lamp", - "tradeable": "true", - "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", - "low_alchemy": "11", - "high_alchemy": "16", - "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.", - "durability": null, - "low_alchemy": "11", - "high_alchemy": "16", - "weight": "0.4", - "grand_exchange_price": "2", - "name": "Oil lamp", - "tradeable": "true", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "50", - "high_alchemy": "75", - "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", - "low_alchemy": "50", - "high_alchemy": "75", - "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", - "low_alchemy": "50", - "high_alchemy": "75", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "28", - "high_alchemy": "42", - "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", - "low_alchemy": "168", - "tradeable": "true", - "high_alchemy": "252", - "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.", - "durability": null, - "low_alchemy": "168", - "high_alchemy": "252", - "weight": "1", - "grand_exchange_price": "158", - "name": "Bullseye lantern", - "tradeable": "true", - "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.", - "durability": null, - "low_alchemy": "168", - "high_alchemy": "252", - "weight": "1", - "grand_exchange_price": "1345", - "name": "Bullseye lantern", - "tradeable": "true", - "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", - "low_alchemy": "168", - "tradeable": "false", - "high_alchemy": "252", - "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, - "low_alchemy": "260", - "high_alchemy": "390", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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" - }, - { + "equipment_slot": "0", "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black tipped spear.", - "walk_anim": "1205", - "low_alchemy": "260", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "536", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,10}", - "durability": null, - "high_alchemy": "390", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Black spear" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "536", - "durability": null, - "name": "Black spear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4581" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black tipped spear.", - "walk_anim": "1205", - "low_alchemy": "260", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "2808", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,10}", - "durability": null, - "high_alchemy": "390", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Black spear(p)" - }, - { - "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 black tipped spear.", - "walk_anim": "1205", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "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)", - "tradeable": "true", - "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, - "low_alchemy": "108000", - "high_alchemy": "162000", - "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", - "low_alchemy": "40000", - "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, - "high_alchemy": "60000", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "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", - "low_alchemy": "160", - "high_alchemy": "160", - "archery_ticket_price": "0", - "id": "4598" - }, - { - "shop_price": "1", - "examine": "A handy little club made out of oak.", - "durability": null, - "name": "Oak-blackjack", - "weight": "1.8", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "4599", - "weapon_interface": "10", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,2,0,0,0", - "equipment_slot": "3" - }, - { - "shop_price": "600", - "ge_buy_limit": "100", - "examine": "A handy little club made out of willow.", - "durability": null, - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "1", - "attack_speed": "4", - "weapon_interface": "10", - "equipment_slot": "3", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "30", - "high_alchemy": "45", - "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", - "low_alchemy": "260", - "high_alchemy": "390", "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", - "low_alchemy": "100", - "high_alchemy": "150", + "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "100", - "high_alchemy": "150", + "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", - "low_alchemy": "40000", - "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, - "high_alchemy": "60000", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "weight": "2", - "archery_ticket_price": "0", - "id": "4687" - }, - { - "examine": "A little heap of salt.", - "durability": null, - "name": "Pile of salt", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "52000", - "high_alchemy": "78000", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "archery_ticket_price": "0", - "id": "4699" - }, - { - "examine": "You need to add lamp oil before you can use it.", - "durability": null, - "name": "Sapphire lantern", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4700" - }, - { - "examine": "A bullseye lantern with a sapphire for a lens.", - "durability": null, - "name": "Sapphire lantern", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "4701" - }, - { - "examine": "A lantern casting a bright blue beam.", - "durability": null, - "name": "Sapphire lantern", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "390000", - "high_alchemy": "585000", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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, - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "0.9", - "absorb": "5,2,0", - "equipment_slot": "0", - "grand_exchange_price": "94300", - "name": "Ahrim's hood", - "tradeable": "true", - "rare_item": "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", - "low_alchemy": "34000", - "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", - "rare_item": "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, - "high_alchemy": "51000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "weight": "5", - "absorb": "10,5,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1000000", - "name": "Ahrim's robetop", - "tradeable": "true", - "rare_item": "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, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "12", - "absorb": "7,3,0", - "equipment_slot": "7", - "grand_exchange_price": "1700000", - "name": "Ahrim's robeskirt", - "tradeable": "true", - "rare_item": "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", - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", - "absorb": "2,0,5", - "equipment_slot": "0", - "grand_exchange_price": "1300000", - "name": "Dharok's helm", - "tradeable": "true", - "rare_item": "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", - "low_alchemy": "83200", - "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", - "rare_item": "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, - "high_alchemy": "124800", - "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, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "9.9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "511400", - "name": "Dharok's platebody", - "tradeable": "true", - "rare_item": "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, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "10", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "748800", - "name": "Dharok's platelegs", - "tradeable": "true", - "rare_item": "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "2", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "396300", - "name": "Guthan's helm", - "tradeable": "true", - "rare_item": "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", - "low_alchemy": "40000", - "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", - "rare_item": "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, - "high_alchemy": "60000", - "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, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "316800", - "name": "Guthan's platebody", - "tradeable": "true", - "rare_item": "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, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "8", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "315600", - "name": "Guthan's chainskirt", - "tradeable": "true", - "rare_item": "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, - "low_alchemy": "5200", - "high_alchemy": "7800", - "absorb": "0,5,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "38900", - "name": "Karil's coif", - "tradeable": "true", - "rare_item": "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", - "low_alchemy": "64000", - "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", - "rare_item": "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, - "high_alchemy": "96000", - "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", + "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "weight": "6", - "absorb": "0,10,5", "equipment_slot": "4", + "ge_buy_limit": "10", + "rare_item": "true", "remove_sleeves": "true", - "grand_exchange_price": "1800000", - "name": "Karil's leathertop", + "requirements": "{1,70}-{6,70}", "tradeable": "true", - "rare_item": "true", - "archery_ticket_price": "0", - "id": "4736", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" + "weight": "5" }, { - "ge_buy_limit": "10000", - "grand_exchange_price": "1800000", + "id": "4713", + "name": "Ahrim's robetop", "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" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "10000", - "examine": "Karil the Tainted's leather skirt.", + "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, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "2", - "absorb": "0,7,3", "equipment_slot": "7", - "grand_exchange_price": "217000", - "name": "Karil's leatherskirt", - "tradeable": "true", + "ge_buy_limit": "10", "rare_item": "true", - "archery_ticket_price": "0", - "id": "4738", - "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "217000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Karil's leatherskirt", + "requirements": "{1,70}-{6,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4739" + "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.", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "equipment_slot": "13", - "grand_exchange_price": "238", - "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" + "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "5", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "139000", - "name": "Torag's helm", "tradeable": "true", - "rare_item": "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, - "low_alchemy": "64000", - "high_alchemy": "96000", - "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", - "rare_item": "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, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "346200", - "name": "Torag's platebody", - "tradeable": "true", - "rare_item": "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, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "9", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "427200", - "name": "Torag's platelegs", - "tradeable": "true", + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "10", "rare_item": "true", - "archery_ticket_price": "0", - "id": "4751", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "427200", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Torag's platelegs", + "remove_sleeves": "true", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4752" + "weight": "9" }, { + "id": "4750", + "name": "Torag'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": "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "687200", - "name": "Verac's helm", "tradeable": "true", - "rare_item": "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", - "low_alchemy": "64000", - "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", - "rare_item": "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, - "high_alchemy": "96000", - "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, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "5", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "270000", - "name": "Verac's brassard", - "tradeable": "true", - "rare_item": "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, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "411400", - "name": "Verac's plateskirt", - "tradeable": "true", + "equip_audio": "2241", + "equipment_slot": "4", + "ge_buy_limit": "10", "rare_item": "true", - "archery_ticket_price": "0", - "id": "4759", - "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0" + "remove_sleeves": "true", + "requirements": "{1,70}", + "tradeable": "true", + "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", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "equipment_slot": "13", - "grand_exchange_price": "39", - "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", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,11" + "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", + "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", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "equipment_slot": "13", - "grand_exchange_price": "14", - "name": "Iron brutal", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4778", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,13" + "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", + "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", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "20", - "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" - }, - { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "Blunt black arrow...ouch", - "durability": null, - "low_alchemy": "14", - "high_alchemy": "21", - "equipment_slot": "13", - "grand_exchange_price": "63", - "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" + "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", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "equipment_slot": "13", - "grand_exchange_price": "80", - "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" + "tradeable": "true" }, { - "requirements": "{4,30}", - "ge_buy_limit": "1000", - "examine": "Blunt adamant arrow...ouch", - "durability": null, - "low_alchemy": "38", - "high_alchemy": "57", - "equipment_slot": "13", - "grand_exchange_price": "154", - "name": "Adamant brutal", - "tradeable": "true", - "archery_ticket_price": "0", "id": "4798", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,45" - }, - { - "requirements": "{4,30}", - "shop_price": "180", - "ge_buy_limit": "1000", - "examine": "Blunt rune arrow...ouch", + "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", "durability": null, - "low_alchemy": "180", - "high_alchemy": "270", "equipment_slot": "13", - "grand_exchange_price": "188", - "name": "Rune brutal", - "tradeable": "true", - "archery_ticket_price": "0", + "ge_buy_limit": "1000", + "requirements": "{4,30}", + "tradeable": "true" + }, + { "id": "4803", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60" - }, - { - "examine": "A very black prism.", - "durability": null, - "name": "Black prism", - "high_alchemy": "1", + "name": "Rune brutal", + "examine": "Blunt rune arrow...ouch", "archery_ticket_price": "0", - "id": "4808" - }, - { - "examine": "A half torn necromantic page.", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60", "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "13", - "high_alchemy": "19", - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "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", - "low_alchemy": "7", - "high_alchemy": "10", - "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", - "low_alchemy": "18", - "high_alchemy": "27", - "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", - "low_alchemy": "88", - "high_alchemy": "132", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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, - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "2", - "high_alchemy": "3", "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", - "low_alchemy": "40", - "high_alchemy": "60", "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" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4852", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed fayrg bones.", "archery_ticket_price": "0", - "id": "4852" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4853", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed raurg bones.", "archery_ticket_price": "0", - "id": "4853" + "durability": null, + "weight": "1" }, { - "examine": "A pot of crushed [type of bones].", - "durability": null, + "id": "4854", "name": "Bonemeal", - "weight": "1", + "examine": "A pot of crushed ourg bones.", "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.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "0.9", - "absorb": "5,2,0", - "equipment_slot": "0", - "grand_exchange_price": "77678", - "name": "Ahrim's hood 100", - "archery_ticket_price": "0", "id": "4856", - "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0" - }, - { + "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", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's leather hood.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "0.9", - "absorb": "5,2,0", - "equipment_slot": "0", - "grand_exchange_price": "77678", - "name": "Ahrim's hood 75", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "4857", - "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0" - }, - { + "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", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's leather hood.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "0.9", - "absorb": "5,2,0", - "equipment_slot": "0", - "grand_exchange_price": "77678", - "name": "Ahrim's hood 50", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "4858", - "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0" - }, - { + "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", + "durability": null, + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}-{6,70}", - "examine": "Ahrim the Blighted's leather hood.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "0.9", - "absorb": "5,2,0", - "equipment_slot": "0", - "grand_exchange_price": "77678", - "name": "Ahrim's hood 25", - "archery_ticket_price": "0", + "weight": "0.9" + }, + { "id": "4859", - "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,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, - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "0.9", "absorb": "5,2,0", - "equipment_slot": "0", - "grand_exchange_price": "51700", - "name": "Ahrim's hood 0", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4860" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "51700", - "examine": "Swap this note at any bank for the equivalent item.", + "bonuses": "0,0,0,6,-2,15,13,16,6,0,15,0,0,0,0", "durability": null, - "name": "Ahrim's hood 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4861" + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,70}-{6,70}", + "weight": "0.9" }, { - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "low_alchemy": "34000", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "50655", - "stand_anim": "813", - "run_anim": "1210", + "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", + "remove_head": "true", + "requirements": "{1,70}-{6,70}", + "tradeable": "true", + "weight": "0.9" + }, + { + "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", - "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, - "high_alchemy": "51000", - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", + "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", - "name": "Ahrim's staff 100" + "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", + "weight": "2" }, { - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "low_alchemy": "34000", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "50655", - "stand_anim": "813", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "4863", - "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, - "high_alchemy": "51000", - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", + "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", - "name": "Ahrim's staff 75" + "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", + "weight": "2" }, { - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "low_alchemy": "34000", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "50655", - "stand_anim": "813", - "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}", - "durability": null, - "high_alchemy": "51000", - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Ahrim's staff 50" - }, - { - "turn90cw_anim": "1207", + "name": "Ahrim's staff 50", "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "low_alchemy": "34000", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "419,419,419,419", - "grand_exchange_price": "50655", - "stand_anim": "813", - "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}", - "durability": null, - "high_alchemy": "51000", - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", "attack_audios": "2555,0,0,0", - "name": "Ahrim's staff 25" - }, - { - "requirements": "{0,70}-{6,70}", - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "Ahrim the Blighted's quarterstaff.", - "walk_anim": "1205", - "durability": null, - "low_alchemy": "34000", - "high_alchemy": "51000", - "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}", - "examine": "Ahrim the Blighted's armoured robe top.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "weight": "5", - "absorb": "10,5,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1458307", - "name": "Ahrim's top 100", + "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", - "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0" - }, - { - "requirements": "{1,70}-{6,70}", + "name": "Ahrim's top 100", "examine": "Ahrim the Blighted's armoured robe top.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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": "1458307", - "name": "Ahrim's top 75", - "archery_ticket_price": "0", + "requirements": "{1,70}-{6,70}", + "weight": "5" + }, + { "id": "4869", - "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0" - }, - { - "requirements": "{1,70}-{6,70}", + "name": "Ahrim's top 75", "examine": "Ahrim the Blighted's armoured robe top.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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": "1458307", - "name": "Ahrim's top 50", - "archery_ticket_price": "0", + "requirements": "{1,70}-{6,70}", + "weight": "5" + }, + { "id": "4870", - "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0" - }, - { - "requirements": "{1,70}-{6,70}", + "name": "Ahrim's top 50", "examine": "Ahrim the Blighted's armoured robe top.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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": "1458307", - "name": "Ahrim's top 25", - "archery_ticket_price": "0", + "requirements": "{1,70}-{6,70}", + "weight": "5" + }, + { "id": "4871", - "bonuses": "0,0,0,30,-10,52,37,63,30,0,60,0,0,0,0" - }, - { - "requirements": "{1,70}-{6,70}", - "ge_buy_limit": "10", + "name": "Ahrim's top 25", "examine": "Ahrim the Blighted's armoured robe top.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "12", - "absorb": "7,3,0", - "equipment_slot": "7", - "grand_exchange_price": "1561591", - "name": "Ahrim's skirt 100", + "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", - "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0" + "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", + "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.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "12", - "absorb": "7,3,0", - "equipment_slot": "7", - "grand_exchange_price": "1561591", - "name": "Ahrim's skirt 75", - "archery_ticket_price": "0", "id": "4875", - "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0" + "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", + "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.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "12", - "absorb": "7,3,0", - "equipment_slot": "7", - "grand_exchange_price": "1561591", - "name": "Ahrim's skirt 50", - "archery_ticket_price": "0", "id": "4876", - "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0" + "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", + "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.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "12", - "absorb": "7,3,0", - "equipment_slot": "7", - "grand_exchange_price": "1561591", - "name": "Ahrim's skirt 25", - "archery_ticket_price": "0", "id": "4877", - "bonuses": "0,0,0,22,-7,33,30,36,22,0,30,0,0,0,0" - }, - { - "requirements": "{1,70}-{6,70}", - "ge_buy_limit": "10", + "name": "Ahrim's skirt 25", "examine": "Ahrim the Blighted's armoured robe skirt.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "12", "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", - "grand_exchange_price": "1500000", - "name": "Ahrim's skirt 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4878" + "requirements": "{1,70}-{6,70}", + "weight": "12" }, { + "id": "4878", + "name": "Ahrim's skirt 0", + "examine": "Ahrim the Blighted's armoured robe skirt.", + "absorb": "7,3,0", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", "ge_buy_limit": "10", - "grand_exchange_price": "1500000", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Ahrim's skirt 0", + "requirements": "{1,70}-{6,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4879" + "weight": "12" }, { - "remove_head": "true", - "requirements": "{1,70}", - "examine": "Dharok the Wretched's helm.", - "durability": null, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", - "absorb": "2,0,5", - "equipment_slot": "0", - "grand_exchange_price": "1355582", - "name": "Dharok's helm 100", + "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", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" - }, - { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}", - "examine": "Dharok the Wretched's helm.", - "durability": null, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", - "absorb": "2,0,5", - "equipment_slot": "0", - "grand_exchange_price": "1355582", - "name": "Dharok's helm 75", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "4881", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" - }, - { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}", - "examine": "Dharok the Wretched's helm.", - "durability": null, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", - "absorb": "2,0,5", - "equipment_slot": "0", - "grand_exchange_price": "1355582", - "name": "Dharok's helm 50", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "4882", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" - }, - { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", "remove_head": "true", "requirements": "{1,70}", - "examine": "Dharok the Wretched's helm.", - "durability": null, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", - "absorb": "2,0,5", - "equipment_slot": "0", - "grand_exchange_price": "1355582", - "name": "Dharok's helm 25", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "4883", - "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Dharok's helm 25", "examine": "Dharok the Wretched's helm.", - "durability": null, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", "absorb": "2,0,5", - "equipment_slot": "0", - "grand_exchange_price": "1300000", - "name": "Dharok's helm 0", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4884" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "1300000", - "examine": "Swap this note at any bank for the equivalent item.", + "bonuses": "0,0,0,-3,-1,45,48,44,-1,51,15,0,0,0,0", "durability": null, - "name": "Dharok's helm 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4885" + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,70}", + "weight": "1" }, { - "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's greataxe.", - "walk_anim": "2064", - "low_alchemy": "83200", - "turn90ccw_anim": "2064", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "2064", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2067,2067,2066,2067", - "grand_exchange_price": "454500", - "stand_anim": "2065", - "run_anim": "824", + "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", + "remove_head": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "1" + }, + { + "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", - "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, - "high_alchemy": "124800", - "weight": "13", - "weapon_interface": "2", - "render_anim": "134", + "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", - "name": "Dharok's axe 100" + "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" }, { - "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's greataxe.", - "walk_anim": "2064", - "low_alchemy": "83200", - "turn90ccw_anim": "2064", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "2064", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2067,2067,2066,2067", - "grand_exchange_price": "454500", - "stand_anim": "2065", - "run_anim": "824", - "archery_ticket_price": "0", "id": "4887", - "stand_turn_anim": "823", - "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", - "requirements": "{0,70}-{2,70}", - "durability": null, - "high_alchemy": "124800", - "weight": "13", - "weapon_interface": "2", - "render_anim": "134", + "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", - "name": "Dharok's axe 75" + "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" }, { - "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's greataxe.", - "walk_anim": "2064", - "low_alchemy": "83200", - "turn90ccw_anim": "2064", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "2064", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2067,2067,2066,2067", - "grand_exchange_price": "454500", - "stand_anim": "2065", - "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}", - "durability": null, - "high_alchemy": "124800", - "weight": "13", - "weapon_interface": "2", - "render_anim": "134", - "attack_audios": "1320,0,0,0", - "name": "Dharok's axe 50" - }, - { - "turn90cw_anim": "2064", + "name": "Dharok's axe 50", "examine": "Dharok the Wretched's greataxe.", - "walk_anim": "2064", - "low_alchemy": "83200", - "turn90ccw_anim": "2064", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "2064", - "defence_anim": "424", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "2067,2067,2066,2067", - "grand_exchange_price": "454500", - "stand_anim": "2065", - "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}", - "durability": null, - "high_alchemy": "124800", - "weight": "13", - "weapon_interface": "2", - "render_anim": "134", "attack_audios": "1320,0,0,0", - "name": "Dharok's axe 25" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "2064", - "examine": "Dharok the Wretched's greataxe.", - "walk_anim": "2064", - "low_alchemy": "83200", - "turn90ccw_anim": "2064", "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "2064", - "equipment_slot": "3", - "grand_exchange_price": "442600", - "stand_anim": "2065", - "tradeable": "true", - "run_anim": "824", - "archery_ticket_price": "0", - "id": "4890", - "stand_turn_anim": "823", - "requirements": "{0,70}-{2,70}", + "bonuses": "-4,103,95,-4,0,0,0,0,0,-1,0,105,0,0,0", + "defence_anim": "424", "durability": null, - "high_alchemy": "124800", - "weight": "13", - "weapon_interface": "2", + "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": "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", - "name": "Dharok's axe 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" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "442600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "4890", "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}", - "examine": "Dharok the Wretched's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "9.9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "481600", - "name": "Dharok's body 100", + "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", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Dharok's body 100", "examine": "Dharok the Wretched's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "481600", - "name": "Dharok's body 75", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9.9" + }, + { "id": "4893", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Dharok's body 75", "examine": "Dharok the Wretched's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "481600", - "name": "Dharok's body 50", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9.9" + }, + { "id": "4894", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Dharok's body 50", "examine": "Dharok the Wretched's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "481600", - "name": "Dharok's body 25", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9.9" + }, + { "id": "4895", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Dharok's body 25", "examine": "Dharok the Wretched's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "10", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "897451", - "name": "Dharok's legs 100", + "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", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "10" }, { - "requirements": "{1,70}", - "examine": "Dharok the Wretched's plate leg armour.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "10", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "897451", - "name": "Dharok's legs 75", - "archery_ticket_price": "0", "id": "4899", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "10" }, { - "requirements": "{1,70}", - "examine": "Dharok the Wretched's plate leg armour.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "10", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "897451", - "name": "Dharok's legs 50", - "archery_ticket_price": "0", "id": "4900", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "10" }, { - "requirements": "{1,70}", - "examine": "Dharok the Wretched's plate leg armour.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "10", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "897451", - "name": "Dharok's legs 25", - "archery_ticket_price": "0", "id": "4901", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Dharok's legs 25", "examine": "Dharok the Wretched's plate leg armour.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "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", - "grand_exchange_price": "676000", - "name": "Dharok's legs 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4902" - }, - { - "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", - "archery_ticket_price": "0", - "id": "4903" - }, - { - "remove_head": "true", "requirements": "{1,70}", - "examine": "Guthan the Infested's helm", - "durability": null, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "2", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "232455", - "name": "Guthan's helm 100", + "weight": "10" + }, + { + "id": "4902", + "name": "Dharok's legs 0", + "examine": "Dharok the Wretched's plate leg armour.", + "absorb": "3,0,7", "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2243", + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "10" + }, + { + "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "2", "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "368200", - "name": "Guthan's helm 0", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4908" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "368200", - "examine": "Swap this note at any bank for the equivalent item.", + "bonuses": "0,0,0,-6,-2,55,58,54,-1,62,15,0,0,0,0", "durability": null, - "name": "Guthan's helm 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4909" + "equip_audio": "2240", + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}", + "weight": "2" }, { - "turn90cw_anim": "1207", - "examine": "Guthan the Infested's warspear.", - "walk_anim": "1205", - "low_alchemy": "40000", - "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": "371782", - "stand_anim": "813", - "run_anim": "1210", + "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", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "2" + }, + { + "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", - "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, - "high_alchemy": "60000", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", + "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", - "name": "Guthan's spear 100" + "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" }, { - "turn90cw_anim": "1207", - "examine": "Guthan the Infested's warspear.", - "walk_anim": "1205", - "low_alchemy": "40000", - "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": "371782", - "stand_anim": "813", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "4911", - "stand_turn_anim": "1209", - "bonuses": "75,75,75,0,0,7,7,7,0,0,0,75,0,0,0", - "requirements": "{0,70}", - "durability": null, - "high_alchemy": "60000", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", + "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", - "name": "Guthan's spear 75" + "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" }, { - "turn90cw_anim": "1207", - "examine": "Guthan the Infested's warspear.", - "walk_anim": "1205", - "low_alchemy": "40000", - "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": "371782", - "stand_anim": "813", - "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}", - "durability": null, - "high_alchemy": "60000", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "attack_audios": "1316,0,0,0", - "name": "Guthan's spear 50" - }, - { - "turn90cw_anim": "1207", + "name": "Guthan's spear 50", "examine": "Guthan the Infested's warspear.", - "walk_anim": "1205", - "low_alchemy": "40000", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "2079", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "2080,2081,2082,2080", - "grand_exchange_price": "371782", - "stand_anim": "813", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "4913", - "stand_turn_anim": "1209", + "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", - "requirements": "{0,70}", + "defence_anim": "2079", "durability": null, - "high_alchemy": "60000", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "attack_audios": "1316,0,0,0", - "name": "Guthan's spear 25" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "Guthan the Infested's warspear.", - "walk_anim": "1205", - "low_alchemy": "40000", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", + "equip_audio": "2247", "equipment_slot": "3", - "grand_exchange_price": "379500", - "stand_anim": "813", - "tradeable": "true", + "render_anim": "28", + "requirements": "{0,70}", "run_anim": "1210", - "archery_ticket_price": "0", - "id": "4914", + "stand_anim": "813", "stand_turn_anim": "1209", - "requirements": "{0,70}", - "durability": null, - "high_alchemy": "60000", - "weight": "2.2", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", "weapon_interface": "14", - "render_anim": "28", + "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", - "name": "Guthan's spear 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" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "379500", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "4914", "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}", - "examine": "Guthan the Infested's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "241605", - "name": "Guthan's body 100", + "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", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Guthan's body 100", "examine": "Guthan the Infested's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "241605", - "name": "Guthan's body 75", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9" + }, + { "id": "4917", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Guthan's body 75", "examine": "Guthan the Infested's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "241605", - "name": "Guthan's body 50", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9" + }, + { "id": "4918", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Guthan's body 50", "examine": "Guthan the Infested's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "241605", - "name": "Guthan's body 25", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9" + }, + { "id": "4919", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Guthan's body 25", "examine": "Guthan the Infested's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "8", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "224030", - "name": "Guthan's skirt 100", + "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", - "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "8" }, { - "requirements": "{1,70}", - "examine": "Guthan the Infested's chainskirt.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "8", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "224030", - "name": "Guthan's skirt 75", - "archery_ticket_price": "0", "id": "4923", - "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "8" }, { - "requirements": "{1,70}", - "examine": "Guthan the Infested's chainskirt.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "8", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "224030", - "name": "Guthan's skirt 50", - "archery_ticket_price": "0", "id": "4924", - "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "8" }, { - "requirements": "{1,70}", - "examine": "Guthan the Infested's chainskirt.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "8", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "224030", - "name": "Guthan's skirt 25", - "archery_ticket_price": "0", "id": "4925", - "bonuses": "0,0,0,-14,-7,75,72,73,-4,82,30,0,0,0,0" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Guthan's skirt 25", "examine": "Guthan the Infested's chainskirt.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "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", - "grand_exchange_price": "240900", - "name": "Guthan's skirt 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4926" + "requirements": "{1,70}", + "weight": "8" }, { + "id": "4926", + "name": "Guthan's skirt 0", + "examine": "Guthan the Infested's chainskirt.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2242", + "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", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4927" + "weight": "8" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's coif.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "absorb": "0,5,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "7520", - "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", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0" + "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", + "remove_head": "true", + "requirements": "{1,70}-{4,70}" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's coif.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "absorb": "0,5,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "7520", - "name": "Karil's coif 75", - "archery_ticket_price": "0", "id": "4929", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0" + "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", + "remove_head": "true", + "requirements": "{1,70}-{4,70}" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's coif.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "absorb": "0,5,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "7520", - "name": "Karil's coif 50", - "archery_ticket_price": "0", "id": "4930", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0" + "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", + "remove_head": "true", + "requirements": "{1,70}-{4,70}" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,70}", - "examine": "Karil the Tainted's coif.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "absorb": "0,5,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "7520", - "name": "Karil's coif 25", - "archery_ticket_price": "0", "id": "4931", - "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,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, - "low_alchemy": "5200", - "high_alchemy": "7800", "absorb": "0,5,2", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "7557", - "name": "Karil's coif 0", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4932" - }, - { - "ge_buy_limit": "1", - "grand_exchange_price": "7557", - "examine": "Swap this note at any bank for the equivalent item.", + "bonuses": "0,0,0,-1,3,6,9,12,6,10,15,0,0,0,0", "durability": null, - "name": "Karil's coif 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4933" + "equip_audio": "2238", + "equipment_slot": "0", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}-{4,70}" }, { - "turn90cw_anim": "2076", - "examine": "Karil the Tainted's repeating crossbow.", - "walk_anim": "2076", - "low_alchemy": "64000", - "turn90ccw_anim": "2076", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "2076", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2075,2075,2075,2075", - "grand_exchange_price": "108029", - "stand_anim": "2074", - "run_anim": "2077", + "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", + "remove_beard": "true", + "remove_head": "true", + "requirements": "{1,70}-{4,70}", + "tradeable": "true" + }, + { + "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", - "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, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "17", - "render_anim": "372", + "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", - "name": "Karil's x-bow 100" + "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" }, { - "turn90cw_anim": "2076", - "examine": "Karil the Tainted's repeating crossbow.", - "walk_anim": "2076", - "low_alchemy": "64000", - "turn90ccw_anim": "2076", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "2076", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2075,2075,2075,2075", - "grand_exchange_price": "108029", - "stand_anim": "2074", - "run_anim": "2077", - "archery_ticket_price": "0", "id": "4935", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,70}", - "durability": null, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "17", - "render_anim": "372", + "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", - "name": "Karil's x-bow 75" + "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" }, { - "turn90cw_anim": "2076", - "examine": "Karil the Tainted's repeating crossbow.", - "walk_anim": "2076", - "low_alchemy": "64000", - "turn90ccw_anim": "2076", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "2076", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "2075,2075,2075,2075", - "grand_exchange_price": "108029", - "stand_anim": "2074", - "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}", - "durability": null, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "17", - "render_anim": "372", - "attack_audios": "2700,0,0,0", - "name": "Karil's x-bow 50" - }, - { - "turn90cw_anim": "2076", + "name": "Karil's x-bow 50", "examine": "Karil the Tainted's repeating crossbow.", - "walk_anim": "2076", - "low_alchemy": "64000", - "turn90ccw_anim": "2076", - "attack_speed": "4", - "two_handed": "true", - "turn180_anim": "2076", - "defence_anim": "424", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "2075,2075,2075,2075", - "grand_exchange_price": "108029", - "stand_anim": "2074", - "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}", - "durability": null, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "17", - "render_anim": "372", "attack_audios": "2700,0,0,0", - "name": "Karil's x-bow 25" - }, - { - "requirements": "{4,70}", - "ge_buy_limit": "10", - "turn90cw_anim": "2076", - "examine": "Karil the Tainted's repeating crossbow.", - "walk_anim": "2076", - "durability": null, - "low_alchemy": "64000", - "high_alchemy": "96000", - "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}", - "examine": "Karil the Tainted's leather body armour.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "weight": "6", - "absorb": "0,10,5", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "983428", - "name": "Karil's top 100", + "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", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" - }, - { - "requirements": "{1,70}-{4,70}", + "name": "Karil's top 100", "examine": "Karil the Tainted's leather body armour.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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": "983428", - "name": "Karil's top 75", - "archery_ticket_price": "0", + "requirements": "{1,70}-{4,70}", + "weight": "6" + }, + { "id": "4941", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" - }, - { - "requirements": "{1,70}-{4,70}", + "name": "Karil's top 75", "examine": "Karil the Tainted's leather body armour.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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": "983428", - "name": "Karil's top 50", - "archery_ticket_price": "0", + "requirements": "{1,70}-{4,70}", + "weight": "6" + }, + { "id": "4942", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" - }, - { - "requirements": "{1,70}-{4,70}", + "name": "Karil's top 50", "examine": "Karil the Tainted's leather body armour.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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": "983428", - "name": "Karil's top 25", - "archery_ticket_price": "0", + "requirements": "{1,70}-{4,70}", + "weight": "6" + }, + { "id": "4943", - "bonuses": "0,0,0,-15,30,47,42,50,65,57,60,0,0,0,0" - }, - { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "1", + "name": "Karil's top 25", "examine": "Karil the Tainted's leather body armour.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "2", - "absorb": "0,7,3", - "equipment_slot": "7", - "grand_exchange_price": "85135", - "name": "Karil's skirt 100", + "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", - "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0" + "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", + "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.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "2", - "absorb": "0,7,3", - "equipment_slot": "7", - "grand_exchange_price": "85135", - "name": "Karil's skirt 75", - "archery_ticket_price": "0", "id": "4947", - "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0" + "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", + "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.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "2", - "absorb": "0,7,3", - "equipment_slot": "7", - "grand_exchange_price": "85135", - "name": "Karil's skirt 50", - "archery_ticket_price": "0", "id": "4948", - "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0" + "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", + "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.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "2", - "absorb": "0,7,3", - "equipment_slot": "7", - "grand_exchange_price": "85135", - "name": "Karil's skirt 25", - "archery_ticket_price": "0", "id": "4949", - "bonuses": "0,0,0,-10,17,26,20,28,35,33,30,0,0,0,0" - }, - { - "requirements": "{1,70}-{4,70}", - "ge_buy_limit": "1", + "name": "Karil's skirt 25", "examine": "Karil the Tainted's leather skirt.", - "durability": null, - "low_alchemy": "18800", - "high_alchemy": "28200", - "weight": "2", "absorb": "0,7,3", - "equipment_slot": "7", - "grand_exchange_price": "153100", - "name": "Karil's skirt 0", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4950" + "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" }, { + "id": "4950", + "name": "Karil's skirt 0", + "examine": "Karil the Tainted's leather skirt.", + "absorb": "0,7,3", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2238", + "equipment_slot": "7", "ge_buy_limit": "1", - "grand_exchange_price": "153100", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "requirements": "{1,70}-{4,70}", + "tradeable": "true", + "weight": "2" + }, + { + "id": "4951", "name": "Karil's skirt 0", - "tradeable": "true", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "4951" + "durability": null, + "ge_buy_limit": "1", + "tradeable": "true" }, { - "remove_head": "true", - "requirements": "{1,70}", - "examine": "Torag the Corrupted's helm.", - "durability": null, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "5", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "71798", - "name": "Torag's helm 100", - "archery_ticket_price": "0", "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "64000", - "high_alchemy": "96000", - "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, - "low_alchemy": "64000", - "high_alchemy": "96000", - "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, - "low_alchemy": "64000", - "high_alchemy": "96000", - "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, - "low_alchemy": "64000", - "high_alchemy": "96000", - "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, - "low_alchemy": "64000", - "high_alchemy": "96000", - "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}", - "examine": "Torag the Corrupted's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "9", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "297733", - "name": "Torag's body 100", + "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", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Torag's body 100", "examine": "Torag the Corrupted's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "297733", - "name": "Torag's body 75", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9" + }, + { "id": "4965", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Torag's body 75", "examine": "Torag the Corrupted's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "297733", - "name": "Torag's body 50", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9" + }, + { "id": "4966", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", + "name": "Torag's body 50", "examine": "Torag the Corrupted's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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": "297733", - "name": "Torag's body 25", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "9" + }, + { "id": "4967", - "bonuses": "0,0,0,-30,-10,122,120,107,-6,132,60,0,0,0,0" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Torag's body 25", "examine": "Torag the Corrupted's platebody armour.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "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.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "9", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "384813", - "name": "Torag's legs 100", + "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", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "9" }, { - "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate leg armour.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "9", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "384813", - "name": "Torag's legs 75", - "archery_ticket_price": "0", "id": "4971", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "9" }, { - "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate leg armour.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "9", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "384813", - "name": "Torag's legs 50", - "archery_ticket_price": "0", "id": "4972", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "9" }, { - "requirements": "{1,70}", - "examine": "Torag the Corrupted's plate leg armour.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "9", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "384813", - "name": "Torag's legs 25", - "archery_ticket_price": "0", "id": "4973", - "bonuses": "0,0,0,-21,-7,85,82,83,-4,92,30,0,0,0,0" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Torag's legs 25", "examine": "Torag the Corrupted's plate leg armour.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "9", "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", - "grand_exchange_price": "371300", - "name": "Torag's legs 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4974" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "371300", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Torag's legs 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4975" - }, - { - "remove_head": "true", "requirements": "{1,70}", - "examine": "Verac the Defiled's helm.", - "durability": null, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", - "absorb": "2,0,5", - "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "459537", - "name": "Verac's helm 100", + "weight": "9" + }, + { + "id": "4974", + "name": "Torag's legs 0", + "examine": "Torag the Corrupted's plate leg armour.", + "absorb": "3,0,7", "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "ge_buy_limit": "10", + "requirements": "{1,70}", + "tradeable": "true", + "weight": "9" + }, + { + "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "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, - "low_alchemy": "41200", - "high_alchemy": "61800", - "weight": "1", "absorb": "2,0,5", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "10", "remove_beard": "true", - "equipment_slot": "0", - "grand_exchange_price": "648900", - "name": "Verac's helm 0", + "remove_head": "true", + "requirements": "{1,70}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "4980" + "weight": "1" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "648900", + "id": "4981", + "name": "Verac's helm 0", "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Verac's helm 0", - "tradeable": "true", "archery_ticket_price": "0", - "id": "4981" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "turn90cw_anim": "2060", - "examine": "Verac the Defiled's flail.", - "walk_anim": "1830", - "low_alchemy": "64000", - "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": "95474", - "stand_anim": "1832", - "run_anim": "1831", - "archery_ticket_price": "0", "id": "4982", - "stand_turn_anim": "823", - "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", - "requirements": "{0,70}", - "durability": null, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "8", - "render_anim": "1426", + "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", - "name": "Verac's flail 100" + "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" }, { - "turn90cw_anim": "2060", - "examine": "Verac the Defiled's flail.", - "walk_anim": "1830", - "low_alchemy": "64000", - "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": "95474", - "stand_anim": "1832", - "run_anim": "1831", - "archery_ticket_price": "0", "id": "4983", - "stand_turn_anim": "823", - "bonuses": "68,-2,82,0,0,0,0,0,0,0,0,72,6,0,0", - "requirements": "{0,70}", - "durability": null, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "8", - "render_anim": "1426", + "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", - "name": "Verac's flail 75" + "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" }, { - "turn90cw_anim": "2060", - "examine": "Verac the Defiled's flail.", - "walk_anim": "1830", - "low_alchemy": "64000", - "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": "95474", - "stand_anim": "1832", - "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}", - "durability": null, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "8", - "render_anim": "1426", - "attack_audios": "1323,0,0,0", - "name": "Verac's flail 50" - }, - { - "turn90cw_anim": "2060", + "name": "Verac's flail 50", "examine": "Verac the Defiled's flail.", - "walk_anim": "1830", - "low_alchemy": "64000", - "turn90ccw_anim": "2060", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "2060", - "defence_anim": "424", - "equipment_slot": "3", + "archery_ticket_price": "0", "attack_anims": "2062,2062,2062,2062", - "grand_exchange_price": "95474", - "stand_anim": "1832", - "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}", - "durability": null, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "8", - "render_anim": "1426", "attack_audios": "1323,0,0,0", - "name": "Verac's flail 25" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "2060", - "examine": "Verac the Defiled's flail.", - "walk_anim": "1830", - "low_alchemy": "64000", - "turn90ccw_anim": "2060", "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "2060", + "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", - "tradeable": "true", - "run_anim": "1831", - "archery_ticket_price": "0", - "id": "4986", - "stand_turn_anim": "823", - "requirements": "{0,70}", - "durability": null, - "high_alchemy": "96000", - "weight": "2", - "weapon_interface": "8", "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", - "name": "Verac's flail 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" }, { - "ge_buy_limit": "10", - "grand_exchange_price": "95500", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "4986", "name": "Verac's flail 0", - "tradeable": "true", + "examine": "Verac the Defiled's flail.", "archery_ticket_price": "0", - "id": "4987" + "attack_audios": "1323,0,0,0", + "attack_speed": "5", + "durability": null, + "equip_audio": "2246", + "equipment_slot": "3", + "ge_buy_limit": "10", + "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}", - "examine": "Verac the Defiled's brassard.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "5", - "absorb": "5,0,10", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "177963", - "name": "Verac's top 100", + "id": "4987", + "name": "Verac's flail 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": "4988", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" - }, - { - "requirements": "{1,70}", + "name": "Verac's top 100", "examine": "Verac the Defiled's brassard.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "5", "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": "177963", - "name": "Verac's top 75", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "5" + }, + { "id": "4989", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" - }, - { - "requirements": "{1,70}", + "name": "Verac's top 75", "examine": "Verac the Defiled's brassard.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "5", "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": "177963", - "name": "Verac's top 50", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "5" + }, + { "id": "4990", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" - }, - { - "requirements": "{1,70}", + "name": "Verac's top 50", "examine": "Verac the Defiled's brassard.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "5", "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": "177963", - "name": "Verac's top 25", - "archery_ticket_price": "0", + "requirements": "{1,70}", + "weight": "5" + }, + { "id": "4991", - "bonuses": "0,0,0,-6,-2,81,95,85,0,81,60,0,5,0,0" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Verac's top 25", "examine": "Verac the Defiled's brassard.", - "durability": null, - "low_alchemy": "112000", - "high_alchemy": "168000", - "weight": "5", "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.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "297540", - "name": "Verac's skirt 100", + "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", - "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "5" }, { - "requirements": "{1,70}", - "examine": "Verac the Defiled's plate skirt.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "297540", - "name": "Verac's skirt 75", - "archery_ticket_price": "0", "id": "4995", - "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "5" }, { - "requirements": "{1,70}", - "examine": "Verac the Defiled's plate skirt.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "297540", - "name": "Verac's skirt 50", - "archery_ticket_price": "0", "id": "4996", - "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0" + "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", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", + "requirements": "{1,70}", + "weight": "5" }, { - "requirements": "{1,70}", - "examine": "Verac the Defiled's plate skirt.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "7", - "grand_exchange_price": "297540", - "name": "Verac's skirt 25", - "archery_ticket_price": "0", "id": "4997", - "bonuses": "0,0,0,-21,-7,85,82,83,0,84,30,0,4,0,0" - }, - { - "requirements": "{1,70}", - "ge_buy_limit": "10", + "name": "Verac's skirt 25", "examine": "Verac the Defiled's plate skirt.", - "durability": null, - "low_alchemy": "110000", - "high_alchemy": "165000", - "weight": "5", "absorb": "3,0,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", - "grand_exchange_price": "321600", - "name": "Verac's skirt 0", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "4998" + "requirements": "{1,70}", + "weight": "5" }, { + "id": "4998", + "name": "Verac's skirt 0", + "examine": "Verac the Defiled's plate skirt.", + "absorb": "3,0,7", + "archery_ticket_price": "0", + "durability": null, + "equip_audio": "2242", + "equipment_slot": "7", "ge_buy_limit": "10", - "grand_exchange_price": "321600", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "requirements": "{1,70}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "4999", "name": "Verac's skirt 0", - "tradeable": "true", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "4999" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "ge_buy_limit": "1000", + "id": "5001", + "name": "Raw cave eel", "examine": "It's incredibly slimy.", - "grand_exchange_price": "373", - "durability": null, - "name": "Raw cave eel", - "tradeable": "true", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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": "Unlit: A helmet with an unlit lamp on it. Lit: A helmet with a lamp on it.", - "durability": null, - "low_alchemy": "360", - "high_alchemy": "540", - "weight": "1", - "equipment_slot": "0", - "grand_exchange_price": "380", - "name": "Mining helmet", - "tradeable": "true", - "archery_ticket_price": "0", "id": "5013", - "bonuses": "0,0,0,-3,-1,4,5,3,-1,4,0,0,0,0,0" + "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", + "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": "Unlit: A helmet with an unlit lamp on it. Lit: A helmet with a lamp on it.", - "durability": null, - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "turn90cw_anim": "1207", - "examine": "Basic but brutal!", - "walk_anim": "1205", - "low_alchemy": "240", - "turn90ccw_anim": "1208", - "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "219", - "stand_anim": "813", - "tradeable": "true", - "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", - "shop_price": "600", - "durability": null, - "high_alchemy": "360", - "weight": "1.3", - "weapon_interface": "14", - "render_anim": "28", - "name": "Bone spear" - }, - { - "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, - "low_alchemy": "240", - "high_alchemy": "360", - "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" - }, - { - "shop_price": "812", + "equipment_slot": "3", "ge_buy_limit": "100", - "examine": "Blue top, very tiny.", + "shop_price": "600", + "tradeable": "true", + "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", + "name": "Woven top", + "examine": "Far too small to wear.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "338", - "high_alchemy": "507", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "710", - "name": "Woven top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5024" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "710", - "durability": null, - "name": "Woven top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5025" - }, - { + "remove_sleeves": "true", "shop_price": "812", - "ge_buy_limit": "100", - "examine": "Blue top, very tiny.", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5025", + "name": "Woven top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5026", + "name": "Woven top", + "examine": "Yellow top, too small for me.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "338", - "high_alchemy": "507", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "908", - "name": "Woven top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5026" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "908", - "durability": null, - "name": "Woven top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5027" - }, - { + "remove_sleeves": "true", "shop_price": "812", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5027", + "name": "Woven top", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5028", + "name": "Woven top", "examine": "Blue top, very tiny.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "338", - "high_alchemy": "507", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "1357", - "name": "Woven top", + "shop_price": "812", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5028" + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1357", - "durability": null, + "id": "5029", "name": "Woven top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5029" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "867", - "durability": null, - "name": "Shirt", - "archery_ticket_price": "0", "id": "5030", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "867", - "durability": null, "name": "Shirt", - "tradeable": "true", + "examine": "Tiny!", "archery_ticket_price": "0", - "id": "5031" - }, - { + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "1023", - "durability": null, + "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", + "examine": "Tiny!", "archery_ticket_price": "0", - "id": "5033" - }, - { + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "ge_buy_limit": "100", - "grand_exchange_price": "1282", - "durability": null, + "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", - "durability": null, - "name": "Trousers", + "id": "5035", + "name": "Shirt", "archery_ticket_price": "0", + "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", + "examine": "A pair of long dwarven trousers... long for dwarves, of course.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5037" + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "378", - "durability": null, + "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", + "examine": "A pair of long dwarven trousers... long for dwarves, of course.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5039" + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "442", - "durability": null, + "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", - "durability": null, - "name": "Shorts", + "id": "5041", + "name": "Trousers", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5042", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1191", - "durability": null, "name": "Shorts", + "examine": "These look great, on dwarves!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5043" + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1229", - "durability": null, + "id": "5043", "name": "Shorts", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5044", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1229", - "durability": null, "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", - "archery_ticket_price": "0", - "id": "5045" + "weight": "0.1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2582", - "durability": null, + "id": "5045", "name": "Shorts", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "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": "2", - "grand_exchange_price": "681", - "durability": null, - "name": "Skirt", + "id": "5047", + "name": "Shorts", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5048", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "681", - "durability": null, "name": "Skirt", + "examine": "A brown skirt. Size small!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5049" + "weight": "0.1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "1151", - "durability": null, + "id": "5049", "name": "Skirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5050", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1151", - "durability": null, "name": "Skirt", + "examine": "A blue skirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5051" + "weight": "0.1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "803", - "durability": null, + "id": "5051", "name": "Skirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "5052", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "2", - "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", "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", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "5070" - }, - { "examine": "Full: ", - "durability": null, - "name": "Bird's nest", - "tradeable": "false", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "low_alchemy": "4", - "high_alchemy": "7", "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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" - }, - { - "shop_price": "8", + "durability": null, + "equipment_slot": "3", "ge_buy_limit": "100", - "examine": "See article", - "grand_exchange_price": "298", - "durability": null, - "name": "Watering can", - "low_alchemy": "3", - "high_alchemy": "4", - "archery_ticket_price": "0", - "id": "5331" + "shop_price": "5", + "tradeable": "true" }, { + "id": "5330", + "name": "Secateurs", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "298", - "durability": null, - "name": "Watering can", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5332" + "tradeable": "true" }, { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", + "id": "5331", + "name": "Watering can", + "examine": "This watering can is empty.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "shop_price": "8", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "5332", + "name": "Watering can", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5333", "name": "Watering can(1)", - "low_alchemy": "3", - "high_alchemy": "4", + "examine": " This watering can is almost empty.", "archery_ticket_price": "0", - "id": "5333" + "durability": null, + "shop_price": "8", + "weight": "0.2" }, { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, + "id": "5334", "name": "Watering can(2)", - "low_alchemy": "3", - "high_alchemy": "4", + "examine": "This watering can is three-quarters empty.", "archery_ticket_price": "0", - "id": "5334" + "durability": null, + "shop_price": "8", + "weight": "0.3" }, { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, + "id": "5335", "name": "Watering can(3)", - "low_alchemy": "3", - "high_alchemy": "4", + "examine": "This watering can is just under half-full.", "archery_ticket_price": "0", - "id": "5335" + "durability": null, + "shop_price": "8", + "weight": "0.4" }, { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, + "id": "5336", "name": "Watering can(4)", - "low_alchemy": "3", - "high_alchemy": "4", + "examine": " Some would say this watering can is half-full, others half-empty.", "archery_ticket_price": "0", - "id": "5336" + "durability": null, + "shop_price": "8", + "weight": "0.5" }, { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, + "id": "5337", "name": "Watering can(5)", - "low_alchemy": "3", - "high_alchemy": "4", + "examine": "This watering can is just over half-full.", "archery_ticket_price": "0", - "id": "5337" + "durability": null, + "shop_price": "8", + "weight": "0.6" }, { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, + "id": "5338", "name": "Watering can(6)", - "low_alchemy": "3", - "high_alchemy": "4", + "examine": " This watering can is three quarters full.", "archery_ticket_price": "0", - "id": "5338" + "durability": null, + "shop_price": "8", + "weight": "0.7" }, { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, + "id": "5339", "name": "Watering can(7)", - "low_alchemy": "3", - "high_alchemy": "4", + "examine": " This watering can is almost completely full.", "archery_ticket_price": "0", - "id": "5339" - }, - { + "durability": null, "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, + "weight": "0.8" + }, + { + "id": "5340", "name": "Watering can(8)", - "low_alchemy": "3", - "high_alchemy": "4", + "examine": " This watering can is completely full.", "archery_ticket_price": "0", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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" }, { + "id": "5350", + "name": "Plant pot", + "examine": "An empty plant pot.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "shop_price": "1", - "ge_buy_limit": "100", - "examine": "A plant pot filled with soil.", - "grand_exchange_price": "20", - "durability": null, - "name": "Plant pot", "tradeable": "true", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "5350" + "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "28", - "high_alchemy": "42", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "7", - "high_alchemy": "10", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "480", - "high_alchemy": "720", - "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", - "low_alchemy": "480", - "high_alchemy": "720", - "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", - "low_alchemy": "200", - "high_alchemy": "300", "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "570", - "high_alchemy": "855", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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" }, { - "requirements": "", + "id": "5528", + "name": "Air tiara", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "5529", + "name": "Mind tiara", "examine": "A tiara infused with the properties of the mind.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "10", - "name": "Mind tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5529" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "10", + "id": "5530", + "name": "Mind tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Mind tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5530" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5531", + "name": "Water tiara", "examine": "A tiara infused with the properties of water.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "1351", - "name": "Water tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5531" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1351", + "id": "5532", + "name": "Water tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Water tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5532" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5533", + "name": "Body tiara", "examine": "A tiara infused with the properties of the body.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "5", - "name": "Body tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5533" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "5", + "id": "5534", + "name": "Body tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Body tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5534" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5535", + "name": "Earth tiara", "examine": "A tiara infused with the properties of the earth.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "4", - "name": "Earth tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5535" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "4", + "id": "5536", + "name": "Earth tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Earth tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5536" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5537", + "name": "Fire tiara", "examine": "A tiara infused with the properties of fire.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "8", - "name": "Fire tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5537" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8", + "id": "5538", + "name": "Fire tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Fire tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5538" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5539", + "name": "Cosmic tiara", "examine": "A tiara infused with the properties of the cosmos.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "8", - "name": "Cosmic tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5539" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8", + "id": "5540", + "name": "Cosmic tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Cosmic tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5540" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5541", + "name": "Nature tiara", "examine": "A tiara infused with the properties of nature.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "10", - "name": "Nature tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5541" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "10", + "id": "5542", + "name": "Nature tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Nature tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5542" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5543", + "name": "Chaos tiara", "examine": "A tiara infused with the properties of chaos.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "10", - "name": "Chaos tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5543" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "10", + "id": "5544", + "name": "Chaos tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Chaos tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5544" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5545", + "name": "Law tiara", "examine": "A tiara infused with the properties of law.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "11", - "name": "Law tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5545" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "11", + "id": "5546", + "name": "Law tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Law tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5546" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5547", + "name": "Death tiara", "examine": "A tiara infused with the properties of death.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "65", - "name": "Death tiara", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "5547" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "65", + "id": "5548", + "name": "Death tiara", + "archery_ticket_price": "0", "durability": null, - "name": "Death tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5548" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "", - "ge_buy_limit": "100", + "id": "5549", + "name": "Blood tiara", "examine": "A tiara infused with the properties of Blood.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "1058", - "name": "Blood tiara", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5549" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "1058", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "5550", "name": "Blood tiara", - "tradeable": "true", "archery_ticket_price": "0", - "id": "5550" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "durability": null, - "name": "Soul tiara", - "archery_ticket_price": "0", "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", - "low_alchemy": "250", - "high_alchemy": "375", - "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", - "low_alchemy": "150", - "high_alchemy": "225", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "20", - "high_alchemy": "30", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "2400", - "high_alchemy": "3600", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "5", - "high_alchemy": "8", - "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "1", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "5604" - }, - { - "shop_price": "25", - "examine": "A dangerous looking knife.", - "grand_exchange_price": "102", - "durability": null, - "name": "Knife", - "tradeable": "true", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "high_alchemy": "1", - "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "4", - "equipment_slot": "13", - "grand_exchange_price": "127", - "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" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Arrows with iron heads.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "138", - "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" - }, - { - "requirements": "{4,5}", - "shop_price": "46", - "ge_buy_limit": "10000", - "examine": "Arrows with steel heads.", - "durability": null, - "low_alchemy": "18", - "high_alchemy": "27", - "equipment_slot": "13", - "grand_exchange_price": "159", - "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" - }, - { - "requirements": "{4,20}", - "shop_price": "76", - "ge_buy_limit": "10000", - "examine": "Arrows with mithril heads.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "equipment_slot": "13", - "grand_exchange_price": "176", - "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" - }, - { - "requirements": "{4,30}", - "shop_price": "172", - "ge_buy_limit": "10000", - "examine": "Arrows with adamantite heads.", - "durability": null, - "low_alchemy": "68", - "high_alchemy": "103", - "equipment_slot": "13", - "grand_exchange_price": "211", - "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" - }, - { - "requirements": "{4,40}", - "shop_price": "510", - "ge_buy_limit": "10000", - "examine": "Arrows with rune heads.", - "durability": null, - "low_alchemy": "204", - "high_alchemy": "306", - "equipment_slot": "13", - "grand_exchange_price": "415", - "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" - }, - { - "shop_price": "7", - "ge_buy_limit": "10000", - "examine": "Arrows with bronze heads.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "4", - "equipment_slot": "13", - "grand_exchange_price": "1076", - "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" - }, - { - "shop_price": "20", - "ge_buy_limit": "10000", - "examine": "Arrows with iron heads.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "1037", - "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" - }, - { - "requirements": "{4,5}", - "shop_price": "46", - "ge_buy_limit": "10000", - "examine": "Arrows with steel heads.", - "durability": null, - "low_alchemy": "18", - "high_alchemy": "27", - "equipment_slot": "13", - "grand_exchange_price": "1038", - "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" - }, - { - "requirements": "{4,20}", - "shop_price": "76", - "ge_buy_limit": "10000", - "examine": "Arrows with mithril heads.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "equipment_slot": "13", - "grand_exchange_price": "718", - "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" - }, - { - "requirements": "{4,30}", - "shop_price": "172", - "ge_buy_limit": "10000", - "examine": "Arrows with adamantite heads.", - "durability": null, - "low_alchemy": "68", - "high_alchemy": "103", - "equipment_slot": "13", - "grand_exchange_price": "101", - "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" - }, - { - "requirements": "{4,40}", - "shop_price": "510", - "ge_buy_limit": "10000", - "examine": "Arrows with rune heads.", - "durability": null, - "low_alchemy": "204", - "high_alchemy": "306", - "equipment_slot": "13", - "grand_exchange_price": "1300", - "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" - }, - { - "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, - "high_alchemy": "1", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "26", - "high_alchemy": "39", - "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, - "low_alchemy": "140", - "high_alchemy": "210", - "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, - "high_alchemy": "1", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "26", - "high_alchemy": "39", - "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, - "low_alchemy": "140", - "high_alchemy": "210", - "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, - "low_alchemy": "2", - "high_alchemy": "3", - "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, - "low_alchemy": "3", - "high_alchemy": "4", - "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, - "low_alchemy": "14", - "high_alchemy": "22", - "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, - "low_alchemy": "39", - "high_alchemy": "58", - "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" - }, - { - "attack_anims": "806,806,806,806", - "requirements": "{4,30}", - "ge_buy_limit": "100", - "grand_exchange_price": "179", - "durability": null, - "name": "Adamant javelin(p+)", - "archery_ticket_price": "0", - "id": "5646", - "weapon_interface": "18", - "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28", - "defence_anim": "424", - "equipment_slot": "3" - }, - { - "shop_price": "624", - "ge_buy_limit": "1000", - "examine": "A rune tipped javelin.", - "durability": null, - "low_alchemy": "249", - "high_alchemy": "374", - "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, - "low_alchemy": "2", - "high_alchemy": "3", - "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, - "low_alchemy": "3", - "high_alchemy": "4", - "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, - "low_alchemy": "14", - "high_alchemy": "22", - "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, - "low_alchemy": "39", - "high_alchemy": "58", - "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" - }, - { - "attack_anims": "806,806,806,806", - "requirements": "{4,30}", - "ge_buy_limit": "100", - "grand_exchange_price": "1514", - "durability": null, - "name": "Adamant javelin(p++)", - "archery_ticket_price": "0", - "id": "5652", - "weapon_interface": "18", - "bonuses": "0,0,0,0,24,0,0,0,0,0,0,0,0,0,28", - "defence_anim": "424", - "equipment_slot": "3" - }, - { - "requirements": "{4,40}", - "shop_price": "624", - "ge_buy_limit": "1000", - "examine": "A rune tipped javelin.", - "durability": null, - "low_alchemy": "249", - "high_alchemy": "374", - "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, - "low_alchemy": "5", - "high_alchemy": "8", - "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, - "low_alchemy": "23", - "high_alchemy": "34", - "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, - "low_alchemy": "27", - "high_alchemy": "40", - "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, - "low_alchemy": "10", - "high_alchemy": "16", - "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, - "low_alchemy": "7", - "high_alchemy": "11", - "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, - "low_alchemy": "26", - "high_alchemy": "40", - "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, - "low_alchemy": "66", - "high_alchemy": "100", - "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, - "low_alchemy": "5", - "high_alchemy": "8", - "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, - "low_alchemy": "23", - "high_alchemy": "34", - "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, - "low_alchemy": "27", - "high_alchemy": "40", - "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, - "low_alchemy": "10", - "high_alchemy": "16", - "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, - "low_alchemy": "7", - "high_alchemy": "11", - "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", - "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++)", - "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, - "low_alchemy": "66", - "high_alchemy": "100", - "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, - "low_alchemy": "14", - "high_alchemy": "21", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "50", - "high_alchemy": "75", - "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, - "low_alchemy": "130", - "high_alchemy": "195", - "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", - "grand_exchange_price": "800", - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Adamant dagger(p+)", - "archery_ticket_price": "0", - "id": "5676", - "weapon_interface": "5", - "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0", - "render_anim": "2584", - "equipment_slot": "3" - }, - { - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "96", - "high_alchemy": "144", - "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, - "low_alchemy": "14", - "high_alchemy": "21", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "50", - "high_alchemy": "75", - "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, - "low_alchemy": "130", - "high_alchemy": "195", - "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", - "grand_exchange_price": "4612", - "attack_audios": "2517,2517,2500,2517", - "durability": null, - "name": "Adamant dagger(p++)", - "archery_ticket_price": "0", - "id": "5694", - "weapon_interface": "5", - "bonuses": "15,8,-4,1,0,0,0,0,1,0,0,14,0,0,0", - "render_anim": "2584", - "equipment_slot": "3" - }, - { - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "96", - "high_alchemy": "144", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "36", - "high_alchemy": "54", - "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" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A steel tipped spear.", - "walk_anim": "1205", - "low_alchemy": "130", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "596", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,5}", - "durability": null, - "high_alchemy": "195", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Steel spear(p+)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "596", - "durability": null, - "name": "Steel spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5709" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril tipped spear.", - "walk_anim": "1205", - "low_alchemy": "338", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "802", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,20}", - "durability": null, - "high_alchemy": "507", - "weight": "1.8", - "weapon_interface": "14", - "render_anim": "28", - "name": "Mithril spear(p+)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "802", - "durability": null, - "name": "Mithril spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5711" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "An adamant tipped spear.", - "walk_anim": "1205", - "low_alchemy": "832", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "1394", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,30}", - "durability": null, - "high_alchemy": "1248", - "weight": "2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Adamant spear(p+)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1394", - "durability": null, - "name": "Adamant spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5713" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A rune tipped spear.", - "walk_anim": "1205", - "low_alchemy": "8320", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "12500", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,40}", - "durability": null, - "high_alchemy": "12480", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "lendable": "true", - "name": "Rune spear(p+)" - }, - { - "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", - "low_alchemy": "24960", - "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, - "high_alchemy": "37440", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "36", - "high_alchemy": "54", - "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" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A steel tipped spear.", - "walk_anim": "1205", - "low_alchemy": "130", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "8336", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,5}", - "durability": null, - "high_alchemy": "195", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Steel spear(p++)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8336", - "durability": null, - "name": "Steel spear(p++)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5723" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A mithril tipped spear.", - "walk_anim": "1205", - "low_alchemy": "338", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "7035", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,20}", - "durability": null, - "high_alchemy": "507", - "weight": "1.8", - "weapon_interface": "14", - "render_anim": "28", - "name": "Mithril spear(p++)" - }, - { - "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", - "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++)", - "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" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A rune tipped spear.", - "walk_anim": "1205", - "low_alchemy": "8320", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "17700", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,40}", - "durability": null, - "high_alchemy": "12480", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "lendable": "true", - "name": "Rune spear(p++)" - }, - { - "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", - "low_alchemy": "24960", - "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, - "high_alchemy": "37440", - "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" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black tipped spear.", - "walk_anim": "1205", - "low_alchemy": "260", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "3351", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,10}", - "durability": null, - "high_alchemy": "390", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Black spear(p+)" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3351", - "durability": null, - "name": "Black spear(p+)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "5735" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A black tipped spear.", - "walk_anim": "1205", - "low_alchemy": "260", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "9065", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,10}", - "durability": null, - "high_alchemy": "390", - "weight": "2.2", - "weapon_interface": "14", - "render_anim": "28", - "name": "Black spear(p++)" - }, - { - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "172", - "high_alchemy": "259", - "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", - "low_alchemy": "144", - "high_alchemy": "216", - "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", - "low_alchemy": "144", - "high_alchemy": "216", - "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", - "low_alchemy": "144", - "high_alchemy": "216", - "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", - "low_alchemy": "144", - "high_alchemy": "216", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "115", - "high_alchemy": "172", - "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", - "low_alchemy": "7", - "high_alchemy": "11", - "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", - "low_alchemy": "25", - "high_alchemy": "38", - "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", - "low_alchemy": "34", - "high_alchemy": "52", - "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", - "low_alchemy": "34", - "high_alchemy": "52", - "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", - "low_alchemy": "21", - "high_alchemy": "32", - "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", - "low_alchemy": "19", - "high_alchemy": "28", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "3", - "high_alchemy": "5", - "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", - "low_alchemy": "3", - "high_alchemy": "5", - "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", - "low_alchemy": "5", - "high_alchemy": "7", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "durability": null, - "name": "Mushroom", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "26", - "high_alchemy": "39", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "34", - "high_alchemy": "51", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "high_alchemy": "1", - "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.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "134", - "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" - }, - { - "shop_price": "3", - "ge_buy_limit": "10000", - "examine": "Bronze crossbow bolts.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "1162", - "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" - }, - { - "examine": "How do I wash blood stains out?", - "durability": null, - "name": "Bloody mourner top", - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "2.72", - "archery_ticket_price": "0", - "id": "6064" - }, - { - "remove_sleeves": "true", - "examine": "A thick heavy leather top.", - "durability": null, - "name": "Mourner top", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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, - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "20", - "high_alchemy": "30", + "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "low_alchemy": "1", - "tradeable": "false", - "high_alchemy": "2", + "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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)", - "low_alchemy": "400", - "high_alchemy": "600", - "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)", - "low_alchemy": "400", - "high_alchemy": "600", - "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)", - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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" - }, - { - "remove_sleeves": "true", - "requirements": "{1,40}", - "ge_buy_limit": "100", - "grand_exchange_price": "40600", - "durability": null, - "name": "Rock-shell plate", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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, - "low_alchemy": "24000", - "high_alchemy": "36000", - "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, - "low_alchemy": "3000", - "high_alchemy": "4500", - "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, - "low_alchemy": "1560", - "high_alchemy": "2340", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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, - "low_alchemy": "18000", - "high_alchemy": "27000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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" - }, - { - "ge_buy_limit": "100", - "examine": "Some finely crafted Fremennik boots, made from spined dagannoth hide.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "3", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "1671", - "name": "Spined boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6143", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1671", - "durability": null, - "name": "Spined boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6144" - }, - { - "ge_buy_limit": "100", - "examine": "Some Fremennik boots, made from the shards of a rock crab's shell.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "3.1", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "756", - "name": "Rock-shell boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6145", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "756", - "durability": null, - "name": "Rock-shell boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6146" - }, - { - "ge_buy_limit": "100", - "examine": "Some finely crafted Fremennik boots, made from the bones of a Wallasalki.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "3.1", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "4958", - "name": "Skeletal boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6147", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4958", - "durability": null, - "name": "Skeletal boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6148" - }, - { - "ge_buy_limit": "100", - "examine": "Fremennik gloves stitched together from spined dagannoth hide.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "3", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "955", - "name": "Spined gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6149", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "955", - "durability": null, - "name": "Spined gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6150" - }, - { - "ge_buy_limit": "100", - "examine": "Fremennik gloves stitched together from rock crab shell shards.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "2", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "951", - "name": "Rock-shell gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6151", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "951", - "durability": null, - "name": "Rock-shell gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6152" - }, - { - "ge_buy_limit": "100", - "examine": "Fremennik gloves stitched together from wallasalki bones fragments.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "3.1", - "equipment_slot": "9", - "lendable": "true", - "grand_exchange_price": "21800", - "name": "Skeletal gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6153", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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" }, { - "durability": null, + "id": "6129", "name": "Rock-shell plate", - "tradeable": "true", "archery_ticket_price": "0", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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, - "low_alchemy": "1200", - "high_alchemy": "1800", - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (9)", - "tradeable": "true", + "id": "6216", + "name": "Broodoo shield (10)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6217", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (8)", - "tradeable": "true", + "id": "6218", + "name": "Broodoo shield (9)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6219", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (7)", - "tradeable": "true", + "id": "6220", + "name": "Broodoo shield (8)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6221", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (6)", - "tradeable": "true", + "id": "6222", + "name": "Broodoo shield (7)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6223", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (5)", - "tradeable": "true", + "id": "6224", + "name": "Broodoo shield (6)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6225", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (4)", - "tradeable": "true", + "id": "6226", + "name": "Broodoo shield (5)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6227", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (3)", - "tradeable": "true", + "id": "6228", + "name": "Broodoo shield (4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6229", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (2)", - "tradeable": "true", + "id": "6230", + "name": "Broodoo shield (3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6231", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (1)", - "tradeable": "true", + "id": "6232", + "name": "Broodoo shield (2)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6233", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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, - "low_alchemy": "1200", - "high_alchemy": "1800", - "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, - "low_alchemy": "1200", - "high_alchemy": "1800", - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (9)", - "tradeable": "true", + "id": "6238", + "name": "Broodoo shield (10)", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6239", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (8)", - "tradeable": "true", + "id": "6240", + "name": "Broodoo shield (9)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6241", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (7)", - "tradeable": "true", + "id": "6242", + "name": "Broodoo shield (8)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6243", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (6)", - "tradeable": "true", + "id": "6244", + "name": "Broodoo shield (7)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6245", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (5)", - "tradeable": "true", + "id": "6246", + "name": "Broodoo shield (6)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6247", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (4)", - "tradeable": "true", + "id": "6248", + "name": "Broodoo shield (5)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6249", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (3)", - "tradeable": "true", + "id": "6250", + "name": "Broodoo shield (4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6251", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (2)", - "tradeable": "true", + "id": "6252", + "name": "Broodoo shield (3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6253", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (1)", - "tradeable": "true", + "id": "6254", + "name": "Broodoo shield (2)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6255", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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, - "low_alchemy": "1200", - "high_alchemy": "1800", - "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, - "low_alchemy": "1200", - "high_alchemy": "1800", - "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", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6260" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (9)", - "tradeable": "true", + "id": "6260", + "name": "Broodoo shield (10)", "archery_ticket_price": "0", + "attack_anims": "7060", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6261", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "durability": null, "name": "Broodoo shield (9)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6262" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (8)", - "tradeable": "true", + "id": "6262", + "name": "Broodoo shield (9)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6263", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "durability": null, "name": "Broodoo shield (8)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6264" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (7)", - "tradeable": "true", + "id": "6264", + "name": "Broodoo shield (8)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6265", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "durability": null, "name": "Broodoo shield (7)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6266" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (6)", - "tradeable": "true", + "id": "6266", + "name": "Broodoo shield (7)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6267", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "durability": null, "name": "Broodoo shield (6)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6268" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (5)", - "tradeable": "true", + "id": "6268", + "name": "Broodoo shield (6)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6269", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "durability": null, "name": "Broodoo shield (5)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6270" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (4)", - "tradeable": "true", + "id": "6270", + "name": "Broodoo shield (5)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6271", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "durability": null, "name": "Broodoo shield (4)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6272" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (3)", - "tradeable": "true", + "id": "6272", + "name": "Broodoo shield (4)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6273", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "durability": null, "name": "Broodoo shield (3)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6274" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (2)", - "tradeable": "true", + "id": "6274", + "name": "Broodoo shield (3)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6275", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "durability": null, "name": "Broodoo shield (2)", - "tradeable": "true", + "examine": "A scary broodoo shield.", + "absorb": "4,2,0", "archery_ticket_price": "0", - "id": "6276" + "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", - "absorb": "4,2,0", - "equipment_slot": "5", - "grand_exchange_price": "1", - "name": "Broodoo shield (1)", - "tradeable": "true", + "id": "6276", + "name": "Broodoo shield (2)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "6277", - "bonuses": "0,0,0,3,-7,10,10,15,5,0,20,0,5,0,0" - }, - { - "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, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "5.4", "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", - "grand_exchange_price": "2957", - "name": "Broodoo shield", + "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", - "low_alchemy": "14", - "high_alchemy": "21", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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, - "low_alchemy": "200", - "high_alchemy": "300", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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, - "low_alchemy": "500", - "high_alchemy": "750", - "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, - "low_alchemy": "470", - "high_alchemy": "705", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "0.3", - "equipment_slot": "10", - "grand_exchange_price": "10800", - "name": "Snakeskin boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6328", - "bonuses": "0,0,0,-10,3,1,1,2,1,0,9,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "173", - "high_alchemy": "260", - "weight": "1", - "equipment_slot": "9", - "grand_exchange_price": "914", - "name": "Snakeskin v'brace", - "tradeable": "true", + "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" - }, - { - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "Local dress.", "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "3331", - "name": "Tribal top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6341" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3331", - "durability": null, - "name": "Tribal top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6342" - }, - { - "shop_price": "250", - "ge_buy_limit": "100", - "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "2", - "equipment_slot": "7", - "grand_exchange_price": "1382", - "name": "Villager robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6343" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1382", - "durability": null, - "name": "Villager robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6344" - }, - { - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "2337", - "name": "Villager hat", + "ge_buy_limit": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6345" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2337", + "id": "6340", + "name": "Tribal mask", + "archery_ticket_price": "0", "durability": null, - "name": "Villager hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6346" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "150", - "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "0.5", - "equipment_slot": "9", - "grand_exchange_price": "734", - "name": "Villager armband", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6347" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "734", - "durability": null, - "name": "Villager armband", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6348" - }, - { - "shop_price": "100", - "ge_buy_limit": "100", - "examine": "A brightly coloured pair of local sandals.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.6", - "equipment_slot": "10", - "grand_exchange_price": "2793", - "name": "Villager sandals", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6349" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2793", - "durability": null, - "name": "Villager sandals", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6350" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", + "id": "6341", + "name": "Tribal top", "examine": "Local dress.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "3171", - "name": "Tribal top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6351" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3171", - "durability": null, - "name": "Tribal top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6352" - }, - { - "shop_price": "250", - "ge_buy_limit": "100", - "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", - "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "2", - "equipment_slot": "7", - "grand_exchange_price": "1416", - "name": "Villager robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6353" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1416", - "durability": null, - "name": "Villager robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6354" - }, - { - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "1", - "equipment_slot": "0", - "grand_exchange_price": "2785", - "name": "Villager hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6355" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2785", - "durability": null, - "name": "Villager hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6356" - }, - { - "shop_price": "100", - "ge_buy_limit": "100", - "examine": "A brightly coloured pair of local sandals.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.6", - "equipment_slot": "10", - "grand_exchange_price": "944", - "name": "Villager sandals", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6357" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "944", - "durability": null, - "name": "Villager sandals", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6358" - }, - { - "shop_price": "150", - "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "0.5", - "equipment_slot": "9", - "grand_exchange_price": "672", - "name": "Villager armband", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6359" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "672", - "durability": null, - "name": "Villager armband", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6360" - }, - { "shop_price": "300", - "ge_buy_limit": "100", - "examine": "Local dress.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "2484", - "name": "Tribal top", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6361" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2484", - "durability": null, + "id": "6342", "name": "Tribal top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6362" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "250", - "ge_buy_limit": "100", + "id": "6343", + "name": "Villager robe", "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "2", "equipment_slot": "7", - "grand_exchange_price": "499", - "name": "Villager robe", + "ge_buy_limit": "100", + "shop_price": "250", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6363" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "499", + "id": "6344", + "name": "Villager robe", + "archery_ticket_price": "0", "durability": null, - "name": "Villager robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6364" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "200", - "ge_buy_limit": "100", + "id": "6345", + "name": "Villager hat", "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "2440", + "ge_buy_limit": "100", + "shop_price": "200", + "tradeable": "true", + "weight": "1" + }, + { + "id": "6346", "name": "Villager hat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6365" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2440", - "durability": null, - "name": "Villager hat", - "tradeable": "true", + "id": "6347", + "name": "Villager armband", + "examine": "A brown armband, as worn by the Tai Bwo Wannai locals.", "archery_ticket_price": "0", - "id": "6366" - }, - { - "shop_price": "100", - "ge_buy_limit": "100", - "examine": "A brightly coloured pair of local sandals.", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.6", - "equipment_slot": "10", - "grand_exchange_price": "1479", - "name": "Villager sandals", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6367" - }, - { - "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.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "0.5", "equipment_slot": "9", - "grand_exchange_price": "823", - "name": "Villager armband", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6369" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "823", - "durability": null, - "name": "Villager armband", + "shop_price": "150", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6370" + "weight": "0.5" }, { + "id": "6348", + "name": "Villager armband", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6349", + "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": "6350", + "name": "Villager sandals", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6351", + "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", - "ge_buy_limit": "100", - "examine": "Local dress.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "2683", - "name": "Tribal top", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6371" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2683", - "durability": null, + "id": "6352", "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": "6353", + "name": "Villager robe", "examine": "A brightly coloured robe prized by the Tai Bwo Wannai peoples.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "2", "equipment_slot": "7", - "grand_exchange_price": "331", - "name": "Villager robe", + "ge_buy_limit": "100", + "shop_price": "250", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6373" + "weight": "2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "331", + "id": "6354", + "name": "Villager robe", + "archery_ticket_price": "0", "durability": null, - "name": "Villager robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6374" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "200", - "ge_buy_limit": "100", + "id": "6355", + "name": "Villager hat", "examine": "A brightly coloured hat prized by the Tai Bwo Wannai peoples.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "3010", - "name": "Villager hat", + "ge_buy_limit": "100", + "shop_price": "200", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6375" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3010", + "id": "6356", + "name": "Villager hat", + "archery_ticket_price": "0", "durability": null, - "name": "Villager hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6376" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "100", - "ge_buy_limit": "100", + "id": "6357", + "name": "Villager sandals", "examine": "A brightly coloured pair of local sandals.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.6", "equipment_slot": "10", - "grand_exchange_price": "2310", - "name": "Villager sandals", + "ge_buy_limit": "100", + "shop_price": "100", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6377" + "weight": "0.6" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2310", - "durability": null, + "id": "6358", "name": "Villager sandals", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6378" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "150", - "ge_buy_limit": "100", - "examine": "A <colour> armband, as worn by the Tai Bwo Wannai locals.", + "id": "6359", + "name": "Villager armband", + "examine": "A blue armband, as worn by the Tai Bwo Wannai locals.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "0.5", "equipment_slot": "9", - "grand_exchange_price": "1697", - "name": "Villager armband", + "ge_buy_limit": "100", + "shop_price": "150", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6379" + "weight": "0.5" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1697", + "id": "6360", + "name": "Villager armband", + "archery_ticket_price": "0", "durability": null, - "name": "Villager armband", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6380" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "24", + "id": "6361", + "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": "6362", + "name": "Tribal top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6363", + "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": "6364", + "name": "Villager robe", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6365", + "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": "6366", + "name": "Villager hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6367", + "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": "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", + "tradeable": "true", + "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", + "name": "Villager armband", + "examine": "A pink 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", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "6380", + "name": "Villager armband", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6382", + "name": "Fez", "examine": "A fez hat. Juss like that.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "0", - "grand_exchange_price": "690", - "name": "Fez", - "tradeable": "true", "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "100", "hat": true, - "id": "6382" + "shop_price": "24", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "690", - "durability": null, + "id": "6383", "name": "Fez", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6383" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "15", - "ge_buy_limit": "100", + "id": "6384", + "name": "Desert top", "examine": "A bit itchy.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "14", - "high_alchemy": "21", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "649", - "name": "Desert top", + "shop_price": "15", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6384" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "649", - "durability": null, + "id": "6385", "name": "Desert top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6385" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "archery_ticket_price": "0", "id": "6386", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "533", - "durability": null, "name": "Desert robes", - "tradeable": "true", + "examine": "Has a coarse hard wearing texture.", "archery_ticket_price": "0", - "id": "6387" - }, - { - "shop_price": "15", - "ge_buy_limit": "100", - "examine": "A bit itchy.", "durability": null, - "low_alchemy": "14", - "high_alchemy": "21", - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1016", - "name": "Desert top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6388" - }, - { + "equipment_slot": "7", "ge_buy_limit": "100", - "grand_exchange_price": "1016", - "durability": null, - "name": "Desert top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6389" - }, - { "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", - "low_alchemy": "10", - "high_alchemy": "15", + "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" }, { + "id": "6391", + "name": "Desert legs", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6392", + "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", - "ge_buy_limit": "100", - "examine": "Good for keeping the sun off my neck.", - "durability": null, - "low_alchemy": "14", - "high_alchemy": "21", - "equipment_slot": "0", - "grand_exchange_price": "531", - "name": "Menap headgear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6392" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "531", - "durability": null, + "id": "6393", "name": "Menap headgear", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6393" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "20", - "ge_buy_limit": "100", + "id": "6394", + "name": "Menaphite top", "examine": "Colourful.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "650", - "name": "Menaphite top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6394" + "shop_price": "20", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "650", - "durability": null, + "id": "6395", "name": "Menaphite top", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6395" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "40", - "ge_buy_limit": "100", - "examine": "A cool light Menaphite robe.", - "grand_exchange_price": "440", - "durability": null, - "name": "Menaphite robe", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "24", - "archery_ticket_price": "0", "id": "6396", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "440", - "durability": null, "name": "Menaphite robe", - "tradeable": "true", + "examine": "A cool light Menaphite robe.", "archery_ticket_price": "0", - "id": "6397" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "40", + "tradeable": "true" }, { - "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", - "low_alchemy": "8", - "high_alchemy": "12", + "id": "6397", + "name": "Menaphite robe", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "6398", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "246", - "durability": null, "name": "Menap action kilt", - "tradeable": "true", + "examine": "Look at those nobbily knees.", "archery_ticket_price": "0", - "id": "6399" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "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", - "ge_buy_limit": "100", - "examine": "Good for keeping the sun off my neck.", - "durability": null, - "low_alchemy": "14", - "high_alchemy": "21", - "equipment_slot": "0", - "grand_exchange_price": "484", - "name": "Menap headgear", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6400" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "484", - "durability": null, + "id": "6401", "name": "Menap headgear", - "tradeable": "true", "archery_ticket_price": "0", - "id": "6401" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "20", - "ge_buy_limit": "100", + "id": "6402", + "name": "Menaphite top", "examine": "Colourful.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "557", - "name": "Menaphite top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6402" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "557", - "durability": null, - "name": "Menaphite top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6403" - }, - { - "shop_price": "40", - "ge_buy_limit": "100", - "examine": "A cool light Menaphite robe.", - "grand_exchange_price": "449", - "durability": null, - "name": "Menaphite robe", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "24", - "archery_ticket_price": "0", - "id": "6404", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "449", - "durability": null, - "name": "Menaphite robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6405" - }, - { "shop_price": "20", - "ge_buy_limit": "100", - "examine": "Look at those nobbily knees.", - "grand_exchange_price": "236", - "durability": null, - "name": "Menap action kilt", - "tradeable": "true", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "236", - "durability": null, "name": "Menap action kilt", - "tradeable": "true", + "examine": "Look at those nobbily knees.", "archery_ticket_price": "0", - "id": "6407" + "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, - "low_alchemy": "160", - "high_alchemy": "240", - "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, - "low_alchemy": "160", - "high_alchemy": "240", - "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, - "low_alchemy": "320", - "high_alchemy": "480", - "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, - "low_alchemy": "320", - "high_alchemy": "480", - "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, - "low_alchemy": "480", - "high_alchemy": "720", - "weight": "1.8", - "attack_speed": "4", + "tradeable": "true", "weapon_interface": "10", - "equipment_slot": "3", - "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, - "low_alchemy": "640", - "high_alchemy": "960", - "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, - "low_alchemy": "640", - "high_alchemy": "960", - "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", "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", - "low_alchemy": "18", - "high_alchemy": "28", "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", - "low_alchemy": "41", - "high_alchemy": "62", "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", - "low_alchemy": "50", - "high_alchemy": "75", "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", - "low_alchemy": "2", - "high_alchemy": "3", "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", - "low_alchemy": "2", - "high_alchemy": "3", "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", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "tradeable": "false", - "high_alchemy": "1", - "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", - "low_alchemy": "16", - "high_alchemy": "24", + "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", - "high_alchemy": "1", + "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", - "low_alchemy": "48", - "high_alchemy": "72", + "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", - "low_alchemy": "48", - "high_alchemy": "72", + "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", - "low_alchemy": "48", - "high_alchemy": "72", - "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", - "low_alchemy": "48", - "high_alchemy": "72", "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, - "low_alchemy": "100", - "high_alchemy": "150", - "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", + "tokkul_price": "375", "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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", + "tokkul_price": "60000", "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, - "low_alchemy": "18000", - "high_alchemy": "27000", - "weight": "3.6", - "absorb": "5,0,11", - "equipment_slot": "5", - "grand_exchange_price": "34700", - "name": "Toktz-ket-xil", + "tokkul_price": "67500", "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, - "low_alchemy": "10000", - "high_alchemy": "15000", - "weight": "0.4", - "attack_speed": "4", + "tokkul_price": "37500", + "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", - "low_alchemy": "14000", - "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, - "high_alchemy": "21000", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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", + "tokkul_price": "45000", "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", - "low_alchemy": "20000", - "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, - "high_alchemy": "30000", - "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" - }, - { - "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, - "name": "Chores", - "tradeable": "false", + "name": "Catspeak amulet(e)", + "examine": "It's an amulet of cat speak. It makes vague purring noises.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "6545" - }, - { - "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.", + "destroy_message": "You can get another Amulet of Catspeak from the Sphinx in Sophanem.", "durability": null, - "name": "Recipe", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6546" - }, - { - "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", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6548", - "equipment_slot": "0" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Lazy cat", - "archery_ticket_price": "0", - "id": "6549" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Lazy cat", - "archery_ticket_price": "0", - "id": "6550" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Lazy cat", - "archery_ticket_price": "0", - "id": "6551" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Lazy cat", - "archery_ticket_price": "0", - "id": "6552" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Lazy cat", - "archery_ticket_price": "0", - "id": "6553" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "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" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", - "archery_ticket_price": "0", - "id": "6556" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", - "archery_ticket_price": "0", - "id": "6557" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", - "archery_ticket_price": "0", - "id": "6558" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", - "archery_ticket_price": "0", - "id": "6559" - }, - { - "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", - "durability": null, - "name": "Wily cat", - "archery_ticket_price": "0", - "id": "6560" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "rare_item": "true", - "low_alchemy": "6800", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "368700", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6562", - "stand_turn_anim": "1209", - "bonuses": "7,-1,28,10,0,2,3,1,10,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "high_alchemy": "10200", - "weight": "2.2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Mud battlestaff" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "It's a slightly magical stick.", - "walk_anim": "1205", - "low_alchemy": "18000", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "423100", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "6563", - "stand_turn_anim": "1209", - "bonuses": "10,-1,40,10,0,2,3,1,10,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "shop_price": "40000", - "durability": null, - "high_alchemy": "27000", - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Mystic mud staff" - }, - { - "examine": "A valuable ring.", - "grand_exchange_price": "6609743", - "durability": null, - "name": "Onyx ring", - "tradeable": "true", - "low_alchemy": "80400", - "high_alchemy": "120600", - "archery_ticket_price": "0", - "id": "6564" - }, - { - "examine": "I wonder if this is valuable.", - "grand_exchange_price": "4501000", - "durability": null, - "name": "Onyx necklace", - "tradeable": "true", - "low_alchemy": "80400", - "high_alchemy": "120600", - "archery_ticket_price": "0", - "id": "6565" - }, - { - "examine": "I wonder if I can get this enchanted.", - "grand_exchange_price": "9058124", - "durability": null, - "name": "Onyx amulet", - "tradeable": "true", - "low_alchemy": "80800", - "high_alchemy": "121200", - "archery_ticket_price": "0", - "id": "6566" - }, - { - "shop_price": "90000", - "ge_buy_limit": "100", - "examine": "A cape woven of obsidian plates.", - "durability": null, - "low_alchemy": "24000", - "high_alchemy": "36000", - "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", - "archery_ticket_price": "0", - "id": "6569" - }, - { - "examine": "A cape of fire.", - "durability": null, - "name": "Fire cape", - "tradeable": "false", - "low_alchemy": "24000", - "high_alchemy": "36000", - "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", - "ge_buy_limit": "100", - "examine": "An uncut onyx.", - "grand_exchange_price": "10700000", - "tokkul_price": "300000", - "durability": null, - "name": "Uncut onyx", - "tradeable": "true", - "low_alchemy": "120000", - "high_alchemy": "180000", - "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", - "low_alchemy": "120000", - "high_alchemy": "180000", - "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", - "low_alchemy": "80400", - "high_alchemy": "120600", - "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", - "low_alchemy": "80400", - "high_alchemy": "120600", - "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", - "low_alchemy": "80800", - "high_alchemy": "121200", - "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", - "low_alchemy": "80800", - "high_alchemy": "121200", - "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", - "low_alchemy": "80800", - "high_alchemy": "121200", - "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" - }, - { - "ge_buy_limit": "100", - "examine": "A very powerful onyx amulet.", - "durability": null, - "low_alchemy": "80800", - "high_alchemy": "121200", "equipment_slot": "2", - "lendable": "true", - "grand_exchange_price": "9200000", - "name": "Amulet of fury", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6585", - "bonuses": "10,10,10,10,10,15,15,15,15,15,15,8,5,0,0" + "tradeable": "false", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "9200000", + "id": "6545", + "name": "Chores", + "examine": "A list of chores that Bob gave you to do.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You have completed the chores; you can safely destroy the list.", "durability": null, - "name": "Amulet of fury", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6586" + "tradeable": "false" }, { + "id": "6546", + "name": "Recipe", + "examine": "It says on the back 'My favourite recipe.", + "archery_ticket_price": "0", + "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" + }, + { + "id": "6547", + "name": "Doctors hat", + "examine": "A mirror helps reflect light on the subject.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Doctor's hat from the Apothecary in Varrock.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false" + }, + { + "id": "6548", + "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" + }, + { + "id": "6549", + "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6550", + "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6551", + "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6552", + "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6553", + "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6554", + "name": "Lazy cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6555", + "name": "Wily cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6556", + "name": "Wily cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6557", + "name": "Wily cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6558", + "name": "Wily cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6559", + "name": "Wily cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6560", + "name": "Wily cat", + "examine": "Inventory: Wild. (wily)Lethargic. (lazy)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6562", + "name": "Mud 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": "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.2" + }, + { + "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", + "examine": "A valuable ring.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6565", + "name": "Onyx necklace", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6566", + "name": "Onyx amulet", + "examine": "I wonder if I can get this enchanted.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6568", + "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,9,0,0,0,0", + "durability": null, + "equipment_slot": "1", + "ge_buy_limit": "100", + "lendable": "true", + "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", + "name": "Amulet of fury", + "examine": "A very powerful onyx amulet.", + "archery_ticket_price": "0", + "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, - "low_alchemy": "144", - "high_alchemy": "216", - "attack_speed": "4", + "tradeable": "true", "two_handed": "true", - "weapon_interface": "9", - "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, - "low_alchemy": "499", - "high_alchemy": "748", - "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, - "low_alchemy": "96", - "high_alchemy": "144", - "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, - "low_alchemy": "96", - "high_alchemy": "144", - "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, - "low_alchemy": "96", - "high_alchemy": "144", - "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, - "low_alchemy": "96", - "high_alchemy": "144", - "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", - "low_alchemy": "768", - "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, - "high_alchemy": "1152", - "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, - "low_alchemy": "172", - "high_alchemy": "259", - "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", + "id": "6602", + "name": "White mace", + "archery_ticket_price": "0", "durability": null, - "name": "White mace", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6602" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Magical staff.", - "walk_anim": "1205", - "low_alchemy": "80", - "turn90ccw_anim": "1208", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "650", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,10}-{5,10}", - "shop_price": "200", - "durability": null, - "high_alchemy": "120", - "weight": "2.2", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "White magic staff" - }, - { - "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", - "low_alchemy": "249", - "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, - "high_alchemy": "374", - "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", - "low_alchemy": "384", - "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, - "high_alchemy": "576", - "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", - "low_alchemy": "768", - "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, - "high_alchemy": "1152", - "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", - "low_alchemy": "307", - "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, - "high_alchemy": "460", - "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, - "low_alchemy": "392", - "high_alchemy": "588", - "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, - "low_alchemy": "576", - "high_alchemy": "864", - "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, - "low_alchemy": "1536", - "high_alchemy": "2304", - "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, - "low_alchemy": "230", - "high_alchemy": "345", - "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, - "low_alchemy": "230", - "high_alchemy": "345", - "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, - "low_alchemy": "422", - "high_alchemy": "633", - "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, - "low_alchemy": "768", - "high_alchemy": "1152", - "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, - "low_alchemy": "768", - "high_alchemy": "1152", - "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, - "low_alchemy": "2", - "high_alchemy": "3", - "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, - "low_alchemy": "460", - "high_alchemy": "691", - "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, - "low_alchemy": "652", - "high_alchemy": "979", - "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", - "low_alchemy": "24", - "high_alchemy": "36", + "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", - "low_alchemy": "24", - "high_alchemy": "36", + "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", + "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", "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", - "low_alchemy": "80", - "high_alchemy": "121", + "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", - "low_alchemy": "80", - "high_alchemy": "121", + "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", - "low_alchemy": "80", - "high_alchemy": "121", - "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", - "low_alchemy": "80", - "high_alchemy": "121", "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "3", - "high_alchemy": "4", - "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", - "low_alchemy": "140", - "high_alchemy": "210", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "3", - "archery_ticket_price": "0", - "id": "6712" + "weight": "3" }, { - "examine": "A heavy metal wrench.", - "durability": null, + "id": "6713", "name": "Wrench", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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" - }, - { - "examine": "A pot of crushed [type of bones].", "durability": null, - "name": "Bonemeal", - "weight": "1", - "archery_ticket_price": "0", - "id": "6728" + "tradeable": "true" }, { - "ge_buy_limit": "10000", + "id": "6728", + "name": "Bonemeal", + "examine": "A pot of crushed small zombie monkey bones.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "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, - "name": "Dagannoth bones", "tradeable": "true", - "archery_ticket_price": "0", - "id": "6730" + "weight": "1.5" }, { - "ge_buy_limit": "10", - "examine": "A mysterious ring that can fill the wearer with magical power...", - "rare_item": "true", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "equipment_slot": "12", - "lendable": "true", - "grand_exchange_price": "478800", - "name": "Seers ring", - "tradeable": "true", + "id": "6730", + "name": "Dagannoth bones", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { "id": "6731", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "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" }, { - "ge_buy_limit": "10", - "examine": "A fabled ring that improves the wearer's skill with a bow...", - "rare_item": "true", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "equipment_slot": "12", - "lendable": "true", - "grand_exchange_price": "1400000", - "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" - }, - { - "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" }, { - "ge_buy_limit": "10", - "examine": "A legendary ring once worn by Fremennik warriors.", - "rare_item": "true", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "equipment_slot": "12", - "lendable": "true", - "grand_exchange_price": "178100", - "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" - }, - { - "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" - }, - { - "ge_buy_limit": "10", - "examine": "A ring reputed to bring out a berserk fury in its wearer.", - "rare_item": "true", + "bonuses": "0,4,0,0,0,0,4,0,0,0,0,0,0,0,0", "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", "equipment_slot": "12", + "ge_buy_limit": "10", "lendable": "true", - "grand_exchange_price": "2700000", - "name": "Berserker ring", - "tradeable": "true", + "rare_item": "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" - }, - { - "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}-{8,61}", - "ge_buy_limit": "10", - "examine": "A very powerful hatchet.", - "has_special": "true", - "rare_item": "true", - "durability": null, - "low_alchemy": "22000", - "high_alchemy": "33000", - "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", - "tradeable": "true", + "id": "6738", + "name": "Berserker ring", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "6739", - "bonuses": "-2,38,32,0,0,0,1,0,0,0,0,42,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "813100", - "durability": null, "name": "Dragon axe", - "tradeable": "true", + "examine": "A very powerful axe.", "archery_ticket_price": "0", - "id": "6740" - }, - { - "examine": "Bob can fix this broken (metal) axe for me.", - "durability": null, - "name": "Broken axe", - "low_alchemy": "160", - "high_alchemy": "160", - "weight": "2.5", - "archery_ticket_price": "0", - "id": "6741", - "equipment_slot": "3" - }, - { - "durability": null, - "name": "Broken axe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6742" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6744" - }, - { - "examine": "The magical sword 'Silverlight'. / The magical sword 'Silverlight', stained black with mushroom ink. (during Shadow of the Storm)", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "1.8", - "attack_speed": "5", - "weapon_interface": "6", - "render_anim": "2554", + "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", - "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, - "low_alchemy": "200", - "high_alchemy": "300", - "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": "Darklight", - "archery_ticket_price": "0", - "id": "6746", - "bonuses": "10,16,-2,0,0,0,3,2,2,0,0,13,0,0,0" - }, - { - "examine": "Used to make the sigil of the demon Agrith-Naar.", - "durability": null, - "name": "Demonic sigil mould", - "archery_ticket_price": "0", - "id": "6747" - }, - { - "examine": "A sigil used for the summoning of the demon Agrith-Naar.", - "durability": null, - "name": "Demonic sigil", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "6748" - }, - { - "examine": "Will this book help in summoning Agrith-Naar?", - "durability": null, - "name": "Demonic tome", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "6749" - }, - { - "remove_sleeves": "true", - "examine": "A desert shirt stained black with mushroom ink.", - "durability": null, - "name": "Black desert shirt", - "low_alchemy": "16", - "high_alchemy": "24", - "archery_ticket_price": "0", - "id": "6750", - "equipment_slot": "4" - }, - { - "examine": "A desert robe stained black with mushroom ink.", - "durability": null, - "name": "Black desert robe", - "low_alchemy": "12", - "high_alchemy": "18", - "archery_ticket_price": "0", - "id": "6752", - "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" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Guthix Mjolnir.", - "walk_anim": "1205", - "low_alchemy": "250", - "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, - "high_alchemy": "375", - "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", - "archery_ticket_price": "0", - "id": "6761" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "A Saradomin Mjolnir.", - "walk_anim": "1205", - "low_alchemy": "250", - "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", - "bonuses": "0,0,11,0,0,0,0,0,0,0,0,14,0,0,0", - "shop_price": "250", - "durability": null, - "high_alchemy": "375", - "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", - "low_alchemy": "250", - "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, - "high_alchemy": "375", - "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" - }, - { - "examine": "Antipoison for Pox.", - "durability": null, - "name": "Cat antipoison", - "archery_ticket_price": "0", - "id": "6766" - }, - { - "examine": "A little more smelly than usual.", - "durability": null, - "name": "Poisoned cheese", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "6768" - }, - { - "destroy_message": "You can obtain another sheet of music by talking to the snakecharmer of Pollnivneach.", - "examine": "Charming.", - "durability": null, - "name": "Music scroll", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6769" - }, - { - "destroy_message": "You can obtain another set of directions to the party by talking to Jimmy Dazzler.", - "examine": "Jimmy Dazzler's directions.", - "durability": null, - "name": "Directions", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "6770" - }, - { - "examine": "Contains garden weeds.", - "durability": null, - "name": "Pot of weeds", - "archery_ticket_price": "0", - "id": "6771" - }, - { - "examine": "Contains slowly burning garden weeds.", - "durability": null, - "name": "Smouldering pot", - "archery_ticket_price": "0", - "id": "6772" - }, - { - "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", - "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 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", - "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 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", - "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 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", - "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 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", - "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 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", - "run_anim": "1427", - "archery_ticket_price": "0", - "id": "6779", - "stand_turn_anim": "1426", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0" - }, - { - "examine": "Menaphite thug.", - "durability": null, - "name": "Menaphite thug", - "archery_ticket_price": "0", - "id": "6780" - }, - { - "remove_sleeves": "true", - "examine": "This looks quite old.", - "durability": null, - "name": "Robe of elidinis", - "archery_ticket_price": "0", - "id": "6786", - "equipment_slot": "4" - }, - { - "examine": "This looks quite old.", - "durability": null, - "name": "Robe of elidinis", - "archery_ticket_price": "0", - "id": "6787", - "equipment_slot": "7" - }, - { - "examine": "This robe is too torn to wear.", - "durability": null, - "name": "Torn robe", - "weight": "1", - "archery_ticket_price": "0", - "id": "6788" - }, - { - "examine": "This robe is too torn to wear.", - "durability": null, - "name": "Torn robe", - "weight": "1", - "archery_ticket_price": "0", - "id": "6789" - }, - { - "durability": null, - "name": "Shoes", - "archery_ticket_price": "0", - "id": "6790", - "equipment_slot": "10" - }, - { - "examine": "Awusah's Sole.", - "durability": null, - "name": "Sole", - "weight": "1", - "archery_ticket_price": "0", - "id": "6791" - }, - { - "examine": "An ancient key from the shrine in Nardah. (Spirits of the Elid)", - "durability": null, - "name": "Ancestral key", - "archery_ticket_price": "0", - "id": "6792" - }, - { - "examine": "The Ballad of Jaresh.", - "durability": null, - "name": "Ballad", - "archery_ticket_price": "0", - "id": "6793" - }, - { - "shop_price": "30", - "ge_buy_limit": "1000", - "examine": "A tasty treat from Nardah - better eat this before it melts.", - "grand_exchange_price": "210", - "durability": null, - "name": "Choc-ice", - "tradeable": "true", - "low_alchemy": "12", - "high_alchemy": "18", - "archery_ticket_price": "0", - "id": "6794" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "210", - "durability": null, - "name": "Choc-ice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6795" - }, - { - "bankable": "false", - "examine": "Wonder what happens if I rub it...", - "durability": null, - "name": "Lamp", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "6796" - }, - { - "shop_price": "8", - "examine": "See article", - "grand_exchange_price": "206", - "durability": null, - "name": "Watering can", - "low_alchemy": "3", - "high_alchemy": "4", - "archery_ticket_price": "0", - "id": "6797" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "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" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6800" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6801" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6802" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6803" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6804" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6805" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6806" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6807" - }, - { - "examine": "A scroll once used by a champion.", - "durability": null, - "name": "Champion scroll", - "archery_ticket_price": "0", - "id": "6808" - }, - { - "requirements": "{1,50}-{2,50}", "ge_buy_limit": "10", - "examine": "These look pretty heavy.", - "durability": null, - "low_alchemy": "26400", - "high_alchemy": "39600", - "weight": "15", - "absorb": "2,0,5", - "equipment_slot": "7", - "lendable": "true", - "grand_exchange_price": "39400", - "name": "Granite legs", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "6810" - }, - { - "examine": "A very dangerous pile of animated Wyvern bones.", - "durability": null, - "name": "Skeletal wyvern", - "archery_ticket_price": "0", - "attack_speed": "6", - "id": "6811" - }, - { - "ge_buy_limit": "1000", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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.", - "durability": null, - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "1", - "equipment_slot": "5", - "grand_exchange_price": "3800000", - "name": "Mage's book", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6889", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "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, - "low_alchemy": "480", - "high_alchemy": "720", - "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" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "An apprentice level wand.", - "walk_anim": "1146", - "low_alchemy": "600", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "82000", - "stand_anim": "809", - "tradeable": "true", - "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", - "requirements": "{6,50}", - "shop_price": "606060060", - "durability": null, - "high_alchemy": "900", - "weapon_interface": "1", - "render_anim": "1426", - "name": "Apprentice wand" - }, - { - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "821", - "examine": "A master level wand.", - "walk_anim": "1146", - "low_alchemy": "1000", - "turn90ccw_anim": "822", - "turn180_anim": "820", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "grand_exchange_price": "1700000", - "stand_anim": "8980", - "tradeable": "true", - "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", - "requirements": "{6,60}", - "durability": null, - "high_alchemy": "1500", - "weight": "0.1", - "weapon_interface": "1", - "render_anim": "1426", - "name": "Master wand" - }, - { - "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, - "low_alchemy": "56000", - "high_alchemy": "84000", - "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, - "low_alchemy": "6800", - "high_alchemy": "10200", - "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, - "low_alchemy": "4800", - "high_alchemy": "7200", - "destroy": "true", - "weight": "0.4", - "equipment_slot": "10", - "grand_exchange_price": "1200000", - "name": "Infinity boots", - "tradeable": "false", - "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.", - "durability": null, - "low_alchemy": "4800", - "high_alchemy": "7200", - "weight": "0.4", - "equipment_slot": "9", - "grand_exchange_price": "1900000", - "name": "Infinity gloves", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6922", - "bonuses": "0,0,0,5,0,0,0,0,5,0,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "36000", - "high_alchemy": "54000", - "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, - "low_alchemy": "8", - "high_alchemy": "12", - "destroy": "true", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "919", - "name": "Cape", - "tradeable": "false", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "archery_ticket_price": "0", - "id": "6962" - }, - { - "examine": "A freshly made roll.", - "durability": null, - "name": "Roll", - "low_alchemy": "10", - "high_alchemy": "15", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "6963" - }, - { - "bankable": "false", - "examine": "100,000 or more: \"X x coins\" where X is the exact number of coins in the stack.", - "durability": null, - "name": "Coins", - "archery_ticket_price": "0", - "id": "6964" - }, - { - "examine": "A freshly made square sandwich.", - "durability": null, - "name": "Square sandwich", - "low_alchemy": "10", - "high_alchemy": "15", - "archery_ticket_price": "0", - "id": "6965" - }, - { - "examine": "Makes the wearer pretty intimidating.", - "durability": null, - "low_alchemy": "40000", - "high_alchemy": "60000", - "weight": "1.3", - "absorb": "2,0,4", - "lendable": "true", - "grand_exchange_price": "61200", - "name": "Dragon med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6967", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6971" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4", - "durability": null, - "name": "Sandstone (1kg)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "6972" - }, - { - "ge_buy_limit": "100", - "examine": "A large chunk of sandstone.", - "grand_exchange_price": "27", - "durability": null, - "name": "Sandstone (2kg)", - "tradeable": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "6973" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "27", - "durability": null, - "name": "Sandstone (2kg)", - "tradeable": "true", - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", - "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.", - "durability": null, - "name": "Stone right arm", - "tradeable": "false", - "destroy": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "6998" - }, - { - "destroy_message": "You can get the statue's left leg back from Lazim.", - "examine": "The left leg of a large stone statue.", - "durability": null, - "name": "Stone left leg", - "tradeable": "false", - "destroy": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "6999" - }, - { - "destroy_message": "You can get the statue's right leg back from Lazim.", - "examine": "The right leg of a large stone statue.", - "durability": null, - "name": "Stone right leg", - "tradeable": "false", - "destroy": "true", - "weight": "9", - "archery_ticket_price": "0", - "id": "7000" - }, - { - "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.", - "durability": null, - "name": "Camel mould (p)", - "tradeable": "false", - "destroy": "true", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7001" - }, - { - "remove_head": "true", - "examine": "Blend in in the desert.", - "durability": null, - "name": "Camel mask", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7003", - "equipment_slot": "0" - }, - { - "shop_price": "14", - "examine": "Good for detailed crafting.", - "grand_exchange_price": "33", - "durability": null, - "name": "Chisel", - "tradeable": "true", - "low_alchemy": "5", - "high_alchemy": "8", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "7004", - "equipment_slot": "3" - }, - { - "requirements": "{18,33}-{11,33}", - "shop_price": "130", - "ge_buy_limit": "100", - "examine": "A lantern to aid attacking Harpie bugs.", - "durability": null, - "low_alchemy": "52", - "high_alchemy": "78", - "weight": "2.2", - "equipment_slot": "5", - "grand_exchange_price": "466", - "name": "Unlit bug lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7051" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "466", - "durability": null, - "name": "Unlit bug lantern", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7052" - }, - { - "requirements": "{18,33}", - "shop_price": "130", - "examine": "A lantern to aid attacking Harpie bugs.", - "durability": null, - "name": "Lit bug lantern", - "low_alchemy": "52", - "high_alchemy": "78", - "weight": "2.2", - "archery_ticket_price": "0", - "id": "7053", - "equipment_slot": "5" - }, - { - "ge_buy_limit": "10000", - "examine": "A baked potato with chilli con carne", - "grand_exchange_price": "415", - "durability": null, - "name": "Chilli potato", - "tradeable": "true", - "low_alchemy": "5", - "high_alchemy": "7", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7054" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "415", - "durability": null, - "name": "Chilli potato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7055" - }, - { - "ge_buy_limit": "10000", - "examine": "A baked potato with egg and tomato.", - "grand_exchange_price": "441", - "durability": null, - "name": "Egg potato", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "7", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7056" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "441", - "durability": null, - "name": "Egg potato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7057" - }, - { - "ge_buy_limit": "10000", - "examine": "A baked potato with mushroom and onions.", - "grand_exchange_price": "1165", - "durability": null, - "name": "Mushroom potato", - "tradeable": "true", - "low_alchemy": "18", - "high_alchemy": "27", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7058" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "1165", - "durability": null, - "name": "Mushroom potato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7059" - }, - { - "ge_buy_limit": "10000", - "examine": "A baked potato with tuna and sweetcorn.", - "grand_exchange_price": "2134", - "durability": null, - "name": "Tuna potato", - "tradeable": "true", - "low_alchemy": "45", - "high_alchemy": "67", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7060" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2134", - "durability": null, - "name": "Tuna potato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7061" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of meat in chilli-con-carne sauce.", - "grand_exchange_price": "215", - "durability": null, - "name": "Chilli con carne", - "tradeable": "true", - "low_alchemy": "5", - "high_alchemy": "7", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7062" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "215", - "durability": null, - "name": "Chilli con carne", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7063" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of scrambled eggs and tomato", - "grand_exchange_price": "126", - "durability": null, - "name": "Egg and tomato", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "7", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7064" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "126", - "durability": null, - "name": "Egg and tomato", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7065" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of fried mushroom and onions.", - "grand_exchange_price": "368", - "durability": null, - "name": "Mushroom & onion", - "tradeable": "true", - "low_alchemy": "18", - "high_alchemy": "27", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7066" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "368", - "durability": null, - "name": "Mushroom & onion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7067" - }, - { - "ge_buy_limit": "10000", - "examine": "A bowl of cooked tuna and sweetcorn.", - "grand_exchange_price": "514", - "durability": null, - "name": "Tuna and corn", - "tradeable": "true", - "low_alchemy": "45", - "high_alchemy": "67", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7068" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "514", - "durability": null, - "name": "Tuna and corn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7069" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of finely minced meat.", - "grand_exchange_price": "22", - "durability": null, - "name": "Minced meat", - "tradeable": "true", - "low_alchemy": "3", - "high_alchemy": "4", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7070" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "22", - "durability": null, - "name": "Minced meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7071" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of spicy sauce.", - "grand_exchange_price": "70", - "durability": null, - "name": "Spicy sauce", - "tradeable": "true", - "low_alchemy": "3", - "high_alchemy": "5", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7072" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "70", - "durability": null, - "name": "Spicy sauce", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7073" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of chopped garlic.", - "grand_exchange_price": "39", - "durability": null, - "name": "Chopped garlic", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "4", - "archery_ticket_price": "0", - "id": "7074" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "39", - "durability": null, - "name": "Chopped garlic", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7075" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of uncooked egg.", - "grand_exchange_price": "34", - "durability": null, - "name": "Uncooked egg", - "tradeable": "true", - "low_alchemy": "3", - "high_alchemy": "4", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7076" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "34", - "durability": null, - "name": "Uncooked egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7077" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of scrambled egg.", - "grand_exchange_price": "24", - "durability": null, - "name": "Scrambled egg", - "tradeable": "true", - "low_alchemy": "3", - "high_alchemy": "4", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7078" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "24", - "durability": null, - "name": "Scrambled egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7079" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of sliced Bittercap mushrooms.", - "grand_exchange_price": "149", - "durability": null, - "name": "Sliced mushrooms", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "25", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7080" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "149", - "durability": null, - "name": "Sliced mushrooms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7081" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of fried Bittercap mushrooms.", - "grand_exchange_price": "189", - "durability": null, - "name": "Fried mushrooms", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "25", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7082" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "189", - "durability": null, - "name": "Fried mushrooms", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7083" - }, - { - "ge_buy_limit": "1000", - "examine": "A bowl of sliced, fried onions.", - "grand_exchange_price": "40", - "durability": null, - "name": "Fried onions", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "4", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7084" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "40", - "durability": null, - "name": "Fried onions", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7085" - }, - { - "ge_buy_limit": "10000", - "examine": "A bowl of finely chopped tuna.", - "grand_exchange_price": "233", - "durability": null, - "name": "Chopped tuna", - "tradeable": "true", - "low_alchemy": "41", - "high_alchemy": "62", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "7086" - }, - { - "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, - "name": "Sweetcorn", - "tradeable": "true", - "low_alchemy": "3", - "high_alchemy": "5", - "archery_ticket_price": "0", - "id": "7088" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "110", - "durability": null, - "name": "Sweetcorn", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7089" - }, - { - "durability": null, - "name": "Burnt egg", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7091" - }, - { - "durability": null, - "name": "Burnt onion", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7093" - }, - { - "durability": null, - "name": "Burnt mushroom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7095" - }, - { - "examine": "Best keep this away from naked flames.", - "durability": null, - "name": "Gunpowder", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7108" - }, - { - "examine": "A fuse.", - "durability": null, - "name": "Fuse", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7109" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", - "grand_exchange_price": "48", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "archery_ticket_price": "0", - "id": "7111" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "Essential pirate wear.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.1", - "equipment_slot": "0", - "grand_exchange_price": "714", - "name": "Pirate bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7112" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "714", - "durability": null, - "name": "Pirate bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7113" - }, - { - "shop_price": "350", - "ge_buy_limit": "100", - "examine": "Not for land lubbers.", - "durability": null, - "low_alchemy": "140", - "high_alchemy": "210", - "weight": "4", - "equipment_slot": "10", - "grand_exchange_price": "2530", - "name": "Pirate boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7114" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "2530", - "durability": null, - "name": "Pirate boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7115" - }, - { - "shop_price": "350", - "ge_buy_limit": "100", - "grand_exchange_price": "77", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "low_alchemy": "140", - "high_alchemy": "210", - "archery_ticket_price": "0", - "id": "7116", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "77", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7117" - }, - { - "examine": "A cannister holding shrapnel.", - "durability": null, - "name": "Canister", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7118" - }, - { - "examine": "For cleaning and packing the cannon.", - "durability": null, - "name": "Ramrod", - "tradeable": "false", - "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "7120" - }, - { - "examine": "A plank of wood to repair the hull with.", - "durability": null, - "name": "Repair plank", - "archery_ticket_price": "0", - "id": "7121" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", - "grand_exchange_price": "904", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "archery_ticket_price": "0", - "id": "7123" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "Essential pirate wear.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.1", - "equipment_slot": "0", - "grand_exchange_price": "340", - "name": "Pirate bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7124" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "340", - "durability": null, - "name": "Pirate bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7125" - }, - { - "shop_price": "350", - "ge_buy_limit": "100", - "grand_exchange_price": "187", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "low_alchemy": "140", - "high_alchemy": "210", - "archery_ticket_price": "0", - "id": "7126", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "187", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7127" - }, - { - "shop_price": "300", - "ge_buy_limit": "100", - "grand_exchange_price": "744", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1", - "archery_ticket_price": "0", - "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.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.1", - "equipment_slot": "0", - "grand_exchange_price": "154", - "name": "Pirate bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7130" - }, - { - "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", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "low_alchemy": "140", - "high_alchemy": "210", - "archery_ticket_price": "0", - "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", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "1", - "archery_ticket_price": "0", - "id": "7134", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "421", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7135" - }, - { - "remove_head": "true", - "ge_buy_limit": "100", - "examine": "Essential pirate wear.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.1", - "equipment_slot": "0", - "grand_exchange_price": "469", - "name": "Pirate bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7136" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "469", - "durability": null, - "name": "Pirate bandana", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7137" - }, - { - "shop_price": "350", - "ge_buy_limit": "100", - "grand_exchange_price": "83", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "low_alchemy": "140", - "high_alchemy": "210", - "archery_ticket_price": "0", - "id": "7138", - "equipment_slot": "7" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "83", - "durability": null, - "name": "Pirate leggings", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7139" - }, - { - "shop_price": "2560", - "examine": "Feels quite lucky.", - "durability": null, - "low_alchemy": "1024", - "high_alchemy": "1536", - "weight": "1.8", - "attack_speed": "4", - "weapon_interface": "6", - "equipment_slot": "3", - "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, - "low_alchemy": "416", - "high_alchemy": "624", - "weight": "1", - "attack_speed": "4", - "weapon_interface": "6", - "equipment_slot": "3", - "name": "Harry's cutlass", - "archery_ticket_price": "0", - "id": "7141", - "bonuses": "3,14,0,-5,0,4,4,4,0,0,0,22,0,0,0" - }, - { - "shop_price": "25600", - "examine": "The very butcher of a silk button.", - "durability": null, - "low_alchemy": "10240", - "high_alchemy": "15360", - "weight": "1", - "attack_speed": "4", - "weapon_interface": "5", - "render_anim": "2622", - "equipment_slot": "3", - "name": "Rapier", - "archery_ticket_price": "0", - "id": "7142", - "bonuses": "45,7,-2,0,0,0,1,0,0,0,0,44,0,0,0" - }, - { - "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", - "low_alchemy": "7", - "high_alchemy": "10", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "88000", - "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", + "render_anim": "2586", "requirements": "{0,60}", - "durability": null, - "high_alchemy": "132000", - "weight": "3", - "weapon_interface": "7", - "render_anim": "124", - "lendable": "true", - "attack_audios": "2503,0,2504,0", - "name": "Dragon 2h sword" - }, - { - "requirements": "{18,37}", - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "They're heavily insulated wellies.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "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" + "weapon_interface": "2" }, { - "examine": "Before being attacked: A ball of Electrical energy.", - "durability": null, - "name": "Killerwatt", + "id": "6740", + "name": "Dragon axe", "archery_ticket_price": "0", - "attack_speed": "2", - "id": "7160" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "durability": null, - "name": "Insulated boots", - "tradeable": "true", + "id": "6741", + "name": "Broken axe", + "examine": "Bob can fix this broken (metal) axe for me.", "archery_ticket_price": "0", - "id": "7161" - }, - { - "shop_price": "5", - "ge_buy_limit": "100", - "examine": "Lots of pie recipes for me to try.", - "grand_exchange_price": "62", "durability": null, - "name": "Pie recipe book", - "tradeable": "true", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "7162" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "62", - "durability": null, - "name": "Pie recipe book", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7163" - }, - { - "shop_price": "54", - "ge_buy_limit": "1000", - "examine": "Still needs two more ingredients.", - "grand_exchange_price": "77", - "durability": null, - "name": "Part mud pie", - "tradeable": "true", - "low_alchemy": "21", - "high_alchemy": "32", - "archery_ticket_price": "0", - "id": "7164" - }, - { - "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", - "low_alchemy": "21", - "high_alchemy": "32", - "archery_ticket_price": "0", - "id": "7166" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "284", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part mud pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7167" - }, - { - "ge_buy_limit": "1000", - "examine": "Needs to be baked before I can use it.", - "grand_exchange_price": "1625", - "durability": null, - "name": "Raw mud pie", - "tradeable": "true", - "low_alchemy": "10", - "high_alchemy": "16", - "archery_ticket_price": "0", - "id": "7168" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1625", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Raw mud pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7169" - }, - { - "shop_price": "54", - "ge_buy_limit": "1000", - "turn90cw_anim": "821", - "examine": "All the good of the earth.", - "walk_anim": "819", - "durability": null, - "low_alchemy": "21", - "high_alchemy": "32", - "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", - "archery_ticket_price": "0", - "id": "7171" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs two more ingredients.", - "grand_exchange_price": "94", - "durability": null, - "name": "Part garden pie", - "tradeable": "true", - "low_alchemy": "36", - "high_alchemy": "54", - "archery_ticket_price": "0", - "id": "7172" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "94", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part garden pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7173" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs one more ingredient.", - "grand_exchange_price": "150", - "durability": null, - "name": "Part garden pie", - "tradeable": "true", - "low_alchemy": "36", - "high_alchemy": "54", - "archery_ticket_price": "0", - "id": "7174" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "150", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part garden pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7175" - }, - { - "ge_buy_limit": "1000", - "examine": "Needs cooking before I eat it.", - "grand_exchange_price": "1052", - "durability": null, - "name": "Raw garden pie", - "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "7", - "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" - }, - { - "ge_buy_limit": "1000", - "examine": "What I wouldn't give for a good steak about now...", - "grand_exchange_price": "1667", - "durability": null, - "name": "Garden pie", - "tradeable": "true", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "archery_ticket_price": "0", - "id": "7180" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "442", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a garden pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7181" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs two more ingredients.", - "grand_exchange_price": "51", - "durability": null, - "name": "Part fish pie", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "7182" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "51", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part fish pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7183" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs one more ingredient.", - "grand_exchange_price": "691", - "durability": null, - "name": "Part fish pie", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "7184" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "691", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part fish pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7185" - }, - { - "ge_buy_limit": "1000", - "examine": "Raw fish is risky, better cook it.", - "grand_exchange_price": "1198", - "durability": null, - "name": "Raw fish pie", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "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" - }, - { - "ge_buy_limit": "1000", - "examine": "Bounty of the sea.", - "grand_exchange_price": "121", - "durability": null, - "name": "Fish pie", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "7190" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "56", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a fish pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7191" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs two more ingredients. ", - "grand_exchange_price": "159", - "durability": null, - "name": "Part admiral pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7192" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "159", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part admiral pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7193" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs one more ingredient.", - "grand_exchange_price": "537", - "durability": null, - "name": "Part admiral pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7194" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "537", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part admiral pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7195" - }, - { - "ge_buy_limit": "1000", - "examine": "This would taste a lot better cooked.", - "grand_exchange_price": "1813", - "durability": null, - "name": "Raw admiral pie", - "tradeable": "true", - "low_alchemy": "62", - "high_alchemy": "93", - "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" - }, - { - "ge_buy_limit": "1000", - "examine": "Much tastier than a normal fish pie.", - "grand_exchange_price": "532", - "durability": null, - "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" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "184", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half an admiral pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7201" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs two more ingredients.", - "grand_exchange_price": "126", - "durability": null, - "name": "Part wild pie", - "tradeable": "true", - "low_alchemy": "72", - "high_alchemy": "108", - "archery_ticket_price": "0", - "id": "7202" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "126", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part wild pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7203" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs one more ingredient.", - "grand_exchange_price": "1116", - "durability": null, - "name": "Part wild pie", - "tradeable": "true", - "low_alchemy": "72", - "high_alchemy": "108", - "archery_ticket_price": "0", - "id": "7204" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "1116", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part wild pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7205" - }, - { - "ge_buy_limit": "1000", - "examine": "Good as it looks, I'd better cook it.", - "grand_exchange_price": "3787", - "durability": null, - "name": "Raw wild pie", - "tradeable": "true", - "low_alchemy": "36", - "high_alchemy": "54", - "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" - }, - { - "ge_buy_limit": "1000", - "examine": "A triumph of man over nature.", - "grand_exchange_price": "1322", - "durability": null, - "name": "Wild pie", - "tradeable": "true", - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "low_alchemy": "72", - "high_alchemy": "108", - "archery_ticket_price": "0", - "id": "7210" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "112", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Half a wild pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7211" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs two more ingredients.", - "grand_exchange_price": "495", - "durability": null, - "name": "Part summer pie", - "tradeable": "true", - "low_alchemy": "56", - "high_alchemy": "84", - "archery_ticket_price": "0", - "id": "7212" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "495", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part summer pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7213" - }, - { - "ge_buy_limit": "1000", - "examine": "Still needs one more ingredient.", - "grand_exchange_price": "980", - "durability": null, - "name": "Part summer pie", - "tradeable": "true", - "low_alchemy": "56", - "high_alchemy": "84", - "archery_ticket_price": "0", - "id": "7214" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "980", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "name": "Part summer pie", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7215" - }, - { - "ge_buy_limit": "1000", - "examine": "Fresh fruit may be good for you, but I should really cook this.", - "grand_exchange_price": "2577", - "durability": null, - "name": "Raw summer pie", - "tradeable": "true", - "low_alchemy": "28", - "high_alchemy": "42", - "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" - }, - { - "ge_buy_limit": "1000", - "examine": "All the fruits of a very small forest.", - "grand_exchange_price": "1288", - "durability": null, - "name": "Summer pie", - "tradeable": "true", - "low_alchemy": "56", - "high_alchemy": "84", - "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", - "low_alchemy": "56", - "high_alchemy": "84", - "archery_ticket_price": "0", - "id": "7220" - }, - { - "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" - }, - { - "shop_price": "19", - "examine": "Mmm this looks tasty.", - "grand_exchange_price": "32", - "durability": null, - "name": "Burnt rabbit", - "tradeable": "true", - "low_alchemy": "7", - "high_alchemy": "11", - "archery_ticket_price": "0", - "id": "7222" - }, - { - "shop_price": "25", - "ge_buy_limit": "1000", - "examine": "A delicious looking piece of roast rabbit.", - "grand_exchange_price": "37", - "durability": null, - "name": "Roast rabbit", - "tradeable": "true", - "low_alchemy": "8", - "high_alchemy": "12", - "archery_ticket_price": "0", - "id": "7223" - }, - { - "shop_price": "67", - "ge_buy_limit": "1000", - "examine": "Might taste better cooked.", - "grand_exchange_price": "112", - "durability": null, - "name": "Skewered rabbit", - "tradeable": "true", - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "7224" - }, - { - "shop_price": "81", - "ge_buy_limit": "100", - "examine": "An iron spit.", - "grand_exchange_price": "18", - "durability": null, - "name": "Iron spit", - "tradeable": "true", - "low_alchemy": "25", - "high_alchemy": "38", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7225" - }, - { - "durability": null, - "name": "Burnt chompy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7227" - }, - { - "shop_price": "130", - "ge_buy_limit": "1000", - "examine": "It might look delicious to an ogre.Roasted chompy bird.", - "grand_exchange_price": "164", - "durability": null, - "name": "Cooked chompy", - "tradeable": "true", - "low_alchemy": "52", - "high_alchemy": "78", - "weight": "10", - "archery_ticket_price": "0", - "id": "7228" - }, - { - "ge_buy_limit": "1000", - "grand_exchange_price": "164", - "durability": null, - "name": "Cooked chompy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7229" - }, - { - "ge_buy_limit": "1000", - "examine": "A skewered chompy bird.", - "grand_exchange_price": "294", - "durability": null, - "name": "Skewered chompy", - "low_alchemy": "59", - "high_alchemy": "89", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7230" - }, - { - "durability": null, - "name": "Burnt rabbit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7231" - }, - { - "durability": null, - "name": "Roast rabbit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7232" - }, - { - "durability": null, - "name": "Skewered rabbit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7233" - }, - { - "durability": null, - "name": "Iron spit", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7234" - }, - { - "durability": null, - "name": "Skewered chompy", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7235" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7236" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7237" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7238" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7239" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7240" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7241" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7242" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7243" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7244" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7245" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7246" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7247" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7248" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7249" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7250" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7251" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7252" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7253" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7254" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7255" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7256" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7257" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7258" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7259" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7260" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7261" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7262" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7263" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7264" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7265" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7266" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7267" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7268" - }, - { - "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7269" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7272" - }, - { - "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7273" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7274" - }, - { - "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7275" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7276" - }, - { - "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7277" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7278" - }, - { - "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7279" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7280" - }, - { - "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7281" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7282" - }, - { - "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7283" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7284" - }, - { - "examine": "I need to answer this correctly.", - "durability": null, - "name": "Challenge scroll", - "archery_ticket_price": "0", - "id": "7285" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7286" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7287" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7288" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7289" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7290" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7291" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7292" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7293" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7294" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7295" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7296" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7298" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7300" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7301" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7303" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7304" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7305" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7306" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7308" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7310" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7312" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7314" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7316" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "archery_ticket_price": "0", - "id": "7317" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7318" - }, - { - "ge_buy_limit": "2", - "examine": "Stylish!", - "grand_exchange_price": "36600", - "durability": null, - "name": "Red boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "7319", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "36600", - "durability": null, - "name": "Red boater", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7320" - }, - { - "ge_buy_limit": "2", - "examine": "Stylish!", - "grand_exchange_price": "16600", - "durability": null, - "name": "Orange boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "7321", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "16600", - "durability": null, - "name": "Orange boater", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7322" - }, - { - "ge_buy_limit": "2", - "examine": "Stylish!", - "grand_exchange_price": "43000", - "durability": null, - "name": "Green boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "7323", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "43000", - "durability": null, - "name": "Green boater", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7324" - }, - { - "ge_buy_limit": "2", - "examine": "Stylish!", - "grand_exchange_price": "37600", - "durability": null, - "name": "Blue boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "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", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "7327", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "41700", - "durability": null, - "name": "Black boater", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7328" - }, - { - "ge_buy_limit": "100", - "examine": "Makes firelighting a lot easier.", - "grand_exchange_price": "93", - "durability": null, - "name": "Red firelighter", - "tradeable": "true", - "low_alchemy": "6", - "high_alchemy": "9", - "archery_ticket_price": "0", - "id": "7329" - }, - { - "ge_buy_limit": "100", - "examine": "Makes firelighting a lot easier.", - "grand_exchange_price": "94", - "durability": null, - "name": "Green firelighter", - "tradeable": "true", - "low_alchemy": "6", - "high_alchemy": "9", - "archery_ticket_price": "0", - "id": "7330" - }, - { - "ge_buy_limit": "100", - "examine": "Makes firelighting a lot easier.", - "grand_exchange_price": "113", - "durability": null, - "name": "Blue firelighter", - "tradeable": "true", - "low_alchemy": "6", - "high_alchemy": "9", - "archery_ticket_price": "0", - "id": "7331" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black kitesheild with a heraldic design.", - "durability": null, - "low_alchemy": "652", - "high_alchemy": "979", - "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", - "archery_ticket_price": "0", - "id": "7333" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "16400", - "name": "Adamant shield(h1)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "7335" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "34200", - "name": "Rune shield(h1)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "7337" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "6530", - "high_alchemy": "9795", - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "9797", - "name": "Black shield(h2)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "7339" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "3248", - "name": "Adamant shield(h2)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "7341" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "32400", - "name": "Rune shield(h2)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "7343" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black shield with a heraldic design.", - "durability": null, - "low_alchemy": "652", - "high_alchemy": "979", - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "7405", - "name": "Black shield(h3)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "7345" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "5271", - "name": "Adamant shield(h3)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "7347" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "32700", - "name": "Rune shield(h3)", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "7349" - }, - { - "requirements": "{1,10}", - "ge_buy_limit": "2", - "examine": "A black shield with a heraldic design.", - "durability": null, - "low_alchemy": "652", - "high_alchemy": "979", - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "5572", - "name": "Black shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "2176", - "high_alchemy": "3264", - "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, - "low_alchemy": "21760", - "high_alchemy": "32640", - "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, - "low_alchemy": "652", - "high_alchemy": "979", - "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", - "durability": null, - "name": "Black shield(h5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7357" - }, - { - "requirements": "{1,30}", - "ge_buy_limit": "2", - "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "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", - "durability": null, - "name": "Adamant shield(h5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7359" - }, - { - "requirements": "{1,40}", - "ge_buy_limit": "2", - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "32400", - "name": "Rune shield(h5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7360", - "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(h5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7361" - }, - { - "requirements": "{1,20}-{4,20}", - "ge_buy_limit": "2", - "examine": "Those studs should provide a bit more protection. Nice trim too!", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "absorb": "0,3,1", - "equipment_slot": "4", - "grand_exchange_price": "101600", - "name": "Studded body (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "absorb": "0,3,1", - "equipment_slot": "4", - "grand_exchange_price": "27300", - "name": "Studded body (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "300", - "high_alchemy": "450", - "weight": "4.5", - "absorb": "0,2,1", - "equipment_slot": "7", - "grand_exchange_price": "114600", - "name": "Studded chaps (g)", - "tradeable": "true", - "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", - "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, - "low_alchemy": "300", - "high_alchemy": "450", - "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", - "grand_exchange_price": "212400", - "durability": null, - "name": "D'hide body(g)", - "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", - "grand_exchange_price": "38800", - "durability": null, - "name": "D'hide body (t)", - "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", - "grand_exchange_price": "6883", - "durability": null, - "name": "D'hide body (g)", - "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", - "grand_exchange_price": "5268", - "durability": null, - "name": "D'hide body (t)", - "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", - "grand_exchange_price": "1300000", - "durability": null, - "name": "D'hide chaps (g)", - "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", - "grand_exchange_price": "304300", - "durability": null, - "name": "D'hide chaps (t)", - "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", - "grand_exchange_price": "21300", - "durability": null, - "name": "D'hide chaps (g)", - "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" + "weight": "2.5" }, { - "requirements": "{4,50}", - "ge_buy_limit": "2", - "grand_exchange_price": "2554", - "durability": null, - "name": "D'hide chaps (t)", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "archery_ticket_price": "0", - "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" - }, - { - "ge_buy_limit": "2", - "examine": "I can practise magic better in this.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1200000", - "name": "Wizard robe (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7390", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Wizard robe (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7391" - }, - { - "ge_buy_limit": "2", - "examine": "I can do magic better in this.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "244600", - "name": "Wizard robe (t)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7392", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "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", - "high_alchemy": "1", - "weight": "1", - "archery_ticket_price": "0", - "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", - "high_alchemy": "1", - "weight": "0.4", - "archery_ticket_price": "0", - "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, - "low_alchemy": "32000", - "high_alchemy": "48000", - "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,40}-{6,20}", - "ge_buy_limit": "2", - "examine": "Enchanted Wizards robes.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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.", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "equipment_slot": "0", - "grand_exchange_price": "8845", - "name": "Enchanted hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7400", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "durability": null, - "name": "Enchanted robe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7401" - }, - { - "durability": null, - "name": "Enchanted top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7402" - }, - { - "durability": null, - "name": "Enchanted hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7403" - }, - { - "examine": "A number of chemical covered wooden logs.", - "durability": null, - "name": "Red logs", - "low_alchemy": "1", - "high_alchemy": "2", - "archery_ticket_price": "0", - "id": "7404" - }, - { - "examine": "A number of chemical covered wooden logs.", - "durability": null, - "name": "Green logs", - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "2", - "archery_ticket_price": "0", - "id": "7405" - }, - { - "examine": "A number of chemical covered wooden logs.", - "durability": null, - "name": "Blue logs", - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "2", - "archery_ticket_price": "0", - "id": "7406" - }, - { - "durability": null, - "name": "Dragon 2h sword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7407" - }, - { - "destroy_message": "I found this in a grave at Draynor Manor.", - "examine": "I shouldn't joke; this is a grave matter.", - "durability": null, - "name": "Draynor skull", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7408" - }, - { - "shop_price": "40000", - "examine": "The only way to kill a Tanglefoot.", - "durability": null, - "low_alchemy": "75000", - "high_alchemy": "75000", - "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", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "160", - "high_alchemy": "240", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "7432" - }, - { - "shop_price": "35", - "ge_buy_limit": "100", - "examine": "Spoooooon!", - "durability": null, - "low_alchemy": "14", - "high_alchemy": "21", - "weight": "0.4", - "attack_speed": "5", - "weapon_interface": "6", - "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, - "low_alchemy": "20", - "high_alchemy": "30", - "attack_speed": "4", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "11", - "durability": null, - "name": "Egg whisk", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7436" - }, - { - "requirements": "{0,10}", - "shop_price": "292", - "ge_buy_limit": "100", - "examine": "Use the spork.", - "durability": null, - "low_alchemy": "130", - "high_alchemy": "195", - "weight": "1.8", - "attack_speed": "4", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "196", - "durability": null, - "name": "Spork", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7438" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "7044", - "examine": "A large spatula... of doom!", - "walk_anim": "7046", - "low_alchemy": "768", - "turn90ccw_anim": "7043", - "attack_speed": "7", - "two_handed": "true", - "turn180_anim": "7045", - "equipment_slot": "3", - "grand_exchange_price": "953", - "stand_anim": "7047", - "tradeable": "true", - "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", - "requirements": "{0,10}", - "shop_price": "1728", - "durability": null, - "high_alchemy": "1152", - "weight": "3.6", - "weapon_interface": "7", - "render_anim": "124", - "name": "Spatula" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "953", - "durability": null, - "name": "Spatula", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7440" - }, - { - "requirements": "{0,20}", - "shop_price": "1494", - "ge_buy_limit": "100", - "examine": "Looks like it's non-stick too!", - "durability": null, - "low_alchemy": "664", - "high_alchemy": "996", - "weight": "1.5", - "attack_speed": "6", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "836", - "durability": null, - "name": "Frying pan", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7442" - }, - { - "requirements": "{0,30}", - "shop_price": "2880", - "ge_buy_limit": "100", - "examine": "Generally used for impaling fresh meat.", - "durability": null, - "low_alchemy": "1280", - "high_alchemy": "1920", - "weight": "2", - "attack_speed": "5", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1712", - "durability": null, - "name": "Skewer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7444" - }, - { - "requirements": "{0,40}", - "shop_price": "12960", - "ge_buy_limit": "100", - "examine": "That's how I roll!", - "durability": null, - "low_alchemy": "5760", - "high_alchemy": "8640", - "weight": "1.8", - "attack_speed": "5", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8445", - "durability": null, - "name": "Rolling pin", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7446" - }, - { - "requirements": "{0,40}", - "shop_price": "8000", - "ge_buy_limit": "100", - "examine": "A sharp, dependable knife, for filleting meat.", - "durability": null, - "low_alchemy": "3200", - "high_alchemy": "4800", - "attack_speed": "4", - "weapon_interface": "5", - "equipment_slot": "3", - "grand_exchange_price": "4686", - "attack_audios": "2704,0,0,0", - "name": "Kitchen knife", - "tradeable": "true", + "id": "6742", + "name": "Broken axe", "archery_ticket_price": "0", - "id": "7447", - "bonuses": "25,12,-4,1,0,0,0,0,1,0,0,24,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "4686", "durability": null, - "name": "Kitchen knife", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7448" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "Often used to soften tough meat up.", - "walk_anim": "819", - "low_alchemy": "16600", - "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", + "id": "6744", + "name": "Picture", "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, - "high_alchemy": "24900", - "weight": "1", - "weapon_interface": "10", - "render_anim": "1", - "name": "Meat tenderiser" + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "24700", - "durability": null, - "name": "Meat tenderiser", - "tradeable": "true", + "id": "6745", + "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", - "id": "7450" - }, - { - "requirements": "{0,40}", - "shop_price": "24040", - "ge_buy_limit": "100", - "examine": "An effective tool for chopping tough meat.", - "durability": null, - "low_alchemy": "10240", - "high_alchemy": "15360", - "weight": "0.5", - "attack_speed": "4", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "15000", - "durability": null, - "name": "Cleaver", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7452" - }, - { - "shop_price": "50", - "examine": "A pair of plain gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "7453", - "bonuses": "1,1,1,1,1,1,1,1,1,1,1,1,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "100", - "examine": "A pair of bronze-coloured gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "7454", - "bonuses": "2,2,2,1,2,2,2,2,1,2,1,2,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "325", - "examine": "A pair of iron-coloured gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "7455", - "bonuses": "3,3,3,2,3,3,3,3,2,3,2,3,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "500", - "examine": "A pair of steel-coloured gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "7456", - "bonuses": "4,4,4,2,4,4,4,4,2,4,2,4,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "1000", - "examine": "A pair of black-coloured gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "7457", - "bonuses": "5,5,5,3,5,5,5,5,3,5,3,5,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "1500", - "examine": "A pair of mithril-coloured gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "600", - "high_alchemy": "900", - "archery_ticket_price": "0", - "id": "7458", - "bonuses": "6,6,6,3,6,6,6,6,3,6,3,6,0,0,0", - "equipment_slot": "9" - }, - { - "shop_price": "2500", - "examine": "A pair of adamant-coloured gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "1000", - "high_alchemy": "1500", - "weight": "0.2", - "archery_ticket_price": "0", - "id": "7459", - "bonuses": "7,7,7,4,7,7,7,7,4,7,4,7,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,13}", - "shop_price": "5000", - "examine": "A pair of rune-coloured gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "2000", - "high_alchemy": "3000", - "archery_ticket_price": "0", - "id": "7460", - "bonuses": "8,8,8,4,8,8,8,8,4,8,4,8,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,41}", - "shop_price": "100000", - "examine": "A pair of dragon-coloured gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "40000", - "high_alchemy": "60000", - "archery_ticket_price": "0", - "id": "7461", - "bonuses": "9,9,9,5,9,9,9,9,5,9,5,9,0,0,0", - "equipment_slot": "9" - }, - { - "requirements": "{1,41}", - "shop_price": "100000", - "examine": "A pair of Barrows-themed gloves.", - "durability": null, - "name": "Gloves", - "low_alchemy": "40000", - "high_alchemy": "60000", - "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" - }, - { - "examine": "It's cornflour in a pot.", - "durability": null, - "name": "Cornflour", - "weight": "1", - "archery_ticket_price": "0", - "id": "7463" - }, - { - "examine": "A tatty old book belonging to the Wise Old Man of Draynor Village.", - "durability": null, - "name": "Book on chickens", - "archery_ticket_price": "0", - "id": "7464" - }, - { - "examine": "Surprise, it looks like a vanilla pod.", - "durability": null, - "name": "Vanilla pod", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "7465" - }, - { - "examine": "It's cornflour in a pot.", - "durability": null, - "name": "Cornflour", - "weight": "1", - "archery_ticket_price": "0", - "id": "7466" - }, - { - "examine": "It's cornflour in a pot.", - "durability": null, - "name": "Pot of cornflour", - "weight": "1", - "archery_ticket_price": "0", - "id": "7468" - }, - { - "destroy_message": "I'll have to get all the ingredients again.", - "examine": "A mixture of milk, cream and cornflour.", - "durability": null, - "name": "Cornflour mixture", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7470" - }, - { - "destroy_message": "I'll have to get all the ingredients again.", - "examine": "It's a bucket of milk and cream.", - "durability": null, - "name": "Milky mixture", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7471" - }, - { - "examine": "Some cinnamon sticks.", - "durability": null, - "name": "Cinnamon", - "archery_ticket_price": "0", - "id": "7472" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A pot of brulee supreme.", - "durability": null, - "name": "Brulee supreme", - "archery_ticket_price": "0", - "id": "7476" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "What came first, the chicken or...", - "durability": null, - "name": "Evil chicken's egg", - "archery_ticket_price": "0", - "id": "7477" - }, - { - "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, - "name": "Dragon token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7478" - }, - { - "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, - "name": "Spicy stew", - "weight": "1.5", - "archery_ticket_price": "0", - "id": "7479" - }, - { - "examine": "Allows for equal distribution of spice.", + "attack_speed": "5", + "bonuses": "9,14,-2,0,0,0,3,2,1,0,0,12,0,0,0", + "defence_anim": "397", "durability": null, - "name": "Red spice (4)", - "archery_ticket_price": "0", - "id": "7480" + "equipment_slot": "3", + "render_anim": "2554", + "weapon_interface": "6", + "weight": "1.8" }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Red spice (3)", + "id": "6746", + "name": "Darklight", + "examine": "The magical sword Silverlight, enhanced with the blood of Agrith-Naar.", "archery_ticket_price": "0", - "id": "7481" - }, - { - "examine": "Allows for equal distribution of spice.", + "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, - "name": "Red spice (2)", - "archery_ticket_price": "0", - "id": "7482" + "equipment_slot": "3", + "has_special": "true", + "render_anim": "2554", + "weapon_interface": "6", + "weight": "1.8" }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Red spice (1)", + "id": "6747", + "name": "Demonic sigil mould", + "examine": "Used to make the sigil of the demon Agrith-Naar.", "archery_ticket_price": "0", - "id": "7483" + "durability": null }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Orange spice (4)", + "id": "6748", + "name": "Demonic sigil", + "examine": "A sigil used for the summoning of the demon Agrith-Naar.", "archery_ticket_price": "0", - "id": "7484" - }, - { - "examine": "Allows for equal distribution of spice.", "durability": null, - "name": "Orange spice (3)", - "archery_ticket_price": "0", - "id": "7485" + "weight": "1.8" }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Orange spice (2)", + "id": "6749", + "name": "Demonic tome", + "examine": "Will this book help in summoning Agrith-Naar?", "archery_ticket_price": "0", - "id": "7486" - }, - { - "examine": "Allows for equal distribution of spice.", "durability": null, - "name": "Orange spice (1)", - "archery_ticket_price": "0", - "id": "7487" + "weight": "0.4" }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Brown spice (4)", + "id": "6750", + "name": "Black desert shirt", + "examine": "A desert shirt stained black with mushroom ink.", "archery_ticket_price": "0", - "id": "7488" - }, - { - "examine": "Allows for equal distribution of spice.", "durability": null, - "name": "Brown spice (3)", - "archery_ticket_price": "0", - "id": "7489" + "equipment_slot": "4", + "remove_sleeves": "true" }, { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Brown spice (2)", + "id": "6752", + "name": "Black desert robe", + "examine": "A desert robe stained black with mushroom ink.", "archery_ticket_price": "0", - "id": "7490" - }, - { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Brown spice (1)", - "archery_ticket_price": "0", - "id": "7491" - }, - { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Yellow spice (4)", - "archery_ticket_price": "0", - "id": "7492" - }, - { - "examine": "Allows for equal distribution of spice.", "durability": null, - "name": "Yellow spice (3)", - "archery_ticket_price": "0", - "id": "7493" - }, - { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Yellow spice (2)", - "archery_ticket_price": "0", - "id": "7494" - }, - { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Yellow spice (1)", - "archery_ticket_price": "0", - "id": "7495" - }, - { - "examine": "Allows for equal distribution of spice.", - "durability": null, - "name": "Empty spice shaker", - "archery_ticket_price": "0", - "id": "7496" - }, - { - "examine": "A cool refreshing fruit mix. With ash in for some reason.", - "durability": null, - "name": "Dirty blast", - "low_alchemy": "12", - "high_alchemy": "18", - "archery_ticket_price": "0", - "id": "7497" - }, - { - "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": "7498" - }, - { - "examine": "He seems to like wearing black.", - "durability": null, - "name": "Evil dave", - "archery_ticket_price": "0", - "id": "7499" - }, - { - "examine": "A short angry guy.", - "durability": null, - "name": "Dwarf", - "archery_ticket_price": "0", - "id": "7500" - }, - { - "examine": "He provides new players with useful information.", - "durability": null, - "name": "Lumbridge guide", - "archery_ticket_price": "0", - "id": "7502" - }, - { - "examine": "He looks a little shifty.", - "durability": null, - "name": "Osman", - "archery_ticket_price": "0", - "id": "7504" - }, - { - "examine": "A shifty-looking character.", - "durability": null, - "name": "Pirate pete", - "archery_ticket_price": "0", - "id": "7505" - }, - { - "examine": "Leader of the White Knights.", - "durability": null, - "name": "Sir amik varze", - "archery_ticket_price": "0", - "id": "7506" - }, - { - "examine": "He's been frozen in time.", - "durability": null, - "name": "Skrach", - "archery_ticket_price": "0", - "id": "7507" - }, - { - "shop_price": "4", - "examine": "There appears to be a coin in the bottom. Liked by dwarves.", - "durability": null, - "name": "Asgoldian ale", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "7508" - }, - { - "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" - }, - { - "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" - }, - { - "examine": "Two out of two goblin generals prefer it!", - "durability": null, - "name": "Slop of compromise", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "7511" - }, - { - "examine": "Previously a nice crispy loaf of bread. Now just kind of icky.", - "durability": null, - "name": "Soggy bread", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "7512" - }, - { - "examine": "They clearly taste so much better this way!", - "durability": null, - "name": "Spicy maggots", - "low_alchemy": "1", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "7513" - }, - { - "examine": "Orange slices which have been dyed, but it looks more like they died.", - "durability": null, - "name": "Dyed orange", - "tradeable": "true", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "7514" - }, - { - "examine": "Glad these aren't in my bed.", - "durability": null, - "name": "Breadcrumbs", - "archery_ticket_price": "0", - "id": "7515" - }, - { - "examine": "Those leaves look useful!", - "durability": null, - "name": "Kelp", - "archery_ticket_price": "0", - "id": "7516" - }, - { - "examine": "Kelp flakes. Smells of the sea.", - "durability": null, - "name": "Ground kelp", - "weight": "2", - "archery_ticket_price": "0", - "id": "7517" - }, - { - "examine": "A smelly meat.", - "durability": null, - "name": "Crab meat", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7518" - }, - { - "examine": "A smelly meat.", - "durability": null, - "name": "Crab meat", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7519" - }, - { - "ge_buy_limit": "100", - "examine": "Nice and Tasty!", - "grand_exchange_price": "219", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7521" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "219", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7522" - }, - { - "examine": "Nice and Tasty!", - "grand_exchange_price": "226", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7523" - }, - { - "examine": "Nice and Tasty!", - "grand_exchange_price": "226", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7524" - }, - { - "examine": "Nice and Tasty!", - "grand_exchange_price": "226", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7525" - }, - { - "examine": "Nice and Tasty!", - "grand_exchange_price": "226", - "durability": null, - "name": "Cooked crab meat", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7526" - }, - { - "examine": "A smelly paste.", - "durability": null, - "name": "Ground crab meat", - "archery_ticket_price": "0", - "id": "7527" - }, - { - "examine": "A smelly paste.", - "durability": null, - "name": "Ground cod", - "archery_ticket_price": "0", - "id": "7528" - }, - { - "examine": "Would taste nicer if I cooked it.", - "durability": null, - "name": "Raw fishcake", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "7529" - }, - { - "examine": "Mmmm, reminds me of the seaside.", - "durability": null, - "name": "Cooked fishcake", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "7530" - }, - { - "examine": "Hmmm, what can I use this for?", - "durability": null, - "name": "Mudskipper hide", - "weight": "1", - "archery_ticket_price": "0", - "id": "7532" - }, - { - "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", - "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", - "archery_ticket_price": "0", - "id": "7539", - "bonuses": "0,0,0,-3,-1,4,5,3,-1,4,0,0,0,0,0", - "equipment_slot": "0" - }, - { - "examine": "Darn, it's useless now.", - "durability": null, - "name": "Broken crab claw", - "archery_ticket_price": "0", - "id": "7540" - }, - { - "examine": "Darn, it's useless now.", - "durability": null, - "name": "Broken crab shell", - "archery_ticket_price": "0", - "id": "7541" - }, - { - "destroy_message": "Maybe Traiborn will have another.", - "examine": "Imbued with knowledge itself.", - "durability": null, - "name": "Cake of guidance", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7542" - }, - { - "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, - "name": "Raw guide cake", - "archery_ticket_price": "0", - "id": "7543" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Egg containing knowledge.", - "durability": null, - "name": "Enchanted egg", - "low_alchemy": "1", - "high_alchemy": "2", - "archery_ticket_price": "0", - "id": "7544" - }, - { - "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "Guiding milk.", - "durability": null, - "name": "Enchanted milk", - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "2", - "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, - "name": "Enchanted flour", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7546" - }, - { - "examine": "A druid pouch.", - "durability": null, - "name": "Druid pouch", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "7547" - }, - { - "shop_price": "8", - "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, - "name": "Potato seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7549" - }, - { - "shop_price": "10", - "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", - "low_alchemy": "30", - "high_alchemy": "45", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "18", - "high_alchemy": "28", - "archery_ticket_price": "0", - "id": "7560" - }, - { - "durability": null, - "name": "Chaos rune", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7561" - }, - { - "shop_price": "10", - "examine": "A tomato seed - plant in an allotment.", - "grand_exchange_price": "1", - "durability": null, - "name": "Tomato seed", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7562" - }, - { - "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, - "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" - }, - { - "ge_buy_limit": "100", - "examine": "The uncooked meat of a Jubbly bird.", - "grand_exchange_price": "422", - "durability": null, - "name": "Raw jubbly", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "10", - "archery_ticket_price": "0", - "id": "7566" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "422", - "durability": null, - "name": "Raw jubbly", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7567" - }, - { - "ge_buy_limit": "100", - "examine": "Lovely jubbly!", - "grand_exchange_price": "944", - "durability": null, - "name": "Cooked jubbly", - "tradeable": "true", - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "10", - "archery_ticket_price": "0", - "id": "7568" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "944", - "durability": null, - "name": "Cooked jubbly", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7569" - }, - { - "durability": null, - "name": "Burnt jubbly", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7571" - }, - { - "examine": "Like a banana only redder.", - "durability": null, - "name": "Red banana", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "7572", - "equipment_slot": "5" - }, - { - "examine": "Like monkey nuts only tchikier.", - "durability": null, - "name": "Tchiki monkey nuts", - "low_alchemy": "2", - "high_alchemy": "3", - "archery_ticket_price": "0", - "id": "7573" - }, - { - "examine": "Perfect for stuffing snakes.", - "durability": null, - "name": "Sliced red banana", - "archery_ticket_price": "0", - "id": "7574" - }, - { - "examine": "Mixing this with jam would just be wrong.", - "durability": null, - "name": "Tchiki nut paste", - "archery_ticket_price": "0", - "id": "7575" - }, - { - "examine": "Like a snake only not alive.", - "durability": null, - "name": "Snake corpse", - "weight": "3", - "archery_ticket_price": "0", - "id": "7576" - }, - { - "examine": "This snake is stuffed right up.", - "durability": null, - "name": "Raw stuffed snake", - "weight": "3", - "archery_ticket_price": "0", - "id": "7577" - }, - { - "examine": "Is this really what you wanted to do?", - "durability": null, - "name": "Odd stuffed snake", - "archery_ticket_price": "0", - "id": "7578" - }, - { - "examine": "Fit for a Monkey King. (cooked)", - "durability": null, - "name": "Stuffed snake", - "weight": "3", - "archery_ticket_price": "0", - "id": "7579" - }, - { - "examine": "Your hellish pet cat!!", - "durability": null, - "name": "Overgrown hellcat", - "archery_ticket_price": "0", - "id": "7581" - }, - { - "examine": "Your hellish pet cat!!", - "durability": null, - "name": "Hell cat", - "archery_ticket_price": "0", - "id": "7582" - }, - { - "examine": "Your hellish pet cat!!", - "durability": null, - "name": "Hell-kitten", - "archery_ticket_price": "0", - "id": "7583" - }, - { - "examine": "Your hellish pet cat!!", - "durability": null, - "name": "Wily hellcat", - "archery_ticket_price": "0", - "id": "7585" - }, - { - "examine": "I can train on this", - "durability": null, - "name": "Dummy", - "archery_ticket_price": "0", - "id": "7586" - }, - { - "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, - "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" - }, - { - "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, - "name": "Coffin", - "archery_ticket_price": "0", - "id": "7589" - }, - { - "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, - "name": "Coffin", - "archery_ticket_price": "0", - "id": "7590" - }, - { - "examine": "Filled with items. Like a bank, but spookier!", - "durability": null, - "name": "Coffin", - "archery_ticket_price": "0", - "id": "7591" - }, - { - "examine": "Aside from the braaaains on the lapel, it's still quite good.", - "durability": null, - "name": "Zombie shirt", - "archery_ticket_price": "0", - "id": "7592", - "equipment_slot": "4" - }, - { - "examine": "Good for a shamble about town.", - "durability": null, - "name": "Zombie trousers", - "archery_ticket_price": "0", - "id": "7593", "equipment_slot": "7" }, { - "remove_head": "true", - "examine": "I look 40,000 years old in this...", - "durability": null, - "name": "Zombie mask", - "low_alchemy": "8", - "high_alchemy": "12", + "id": "6754", + "name": "Enchanted key", + "examine": "It changes temperature as I walk. (Meeting History)", "archery_ticket_price": "0", - "id": "7594", - "remove_beard": "true", - "equipment_slot": "0" - }, - { - "examine": "Smells pretty funky.", - "durability": null, - "name": "Zombie gloves", - "low_alchemy": "8", - "high_alchemy": "12", - "archery_ticket_price": "0", - "id": "7595", - "equipment_slot": "9" - }, - { - "shop_price": "160", - "examine": "Thrilling.", - "durability": null, - "name": "Zombie boots", - "low_alchemy": "8", - "high_alchemy": "12", - "archery_ticket_price": "0", - "id": "7596", - "equipment_slot": "10" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7597" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7598" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7599" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7600" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7601" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7602" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7603" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7604" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7605" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7606" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7607" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7608" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7609" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7610" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7611" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7612" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7613" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7614" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7615" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7616" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7617" - }, - { - "durability": null, - "name": "Item", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7618" - }, - { - "examine": "A silvery rod of mithril and silver with sapphire on top.", - "durability": null, - "name": "Silvthrill rod", - "archery_ticket_price": "0", - "id": "7620" - }, - { - "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": "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" - }, - { - "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" - }, - { - "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.", - "durability": null, - "name": "Plaster fragment", - "tradeable": "false", "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "7628" + "destroy_message": "Hopefully Erin has a copy!", + "durability": null, + "equipment_slot": "3", + "tradeable": "false" }, { - "destroy_message": "You can get another scroll by exploring near the Inn in Burgh de Rott.", - "examine": "An ancient tattered scroll.", - "durability": null, - "name": "Dusty scroll", - "tradeable": "false", - "destroy": "true", + "id": "6760", + "name": "Guthix mjolnir", + "examine": "A Guthix Mjolnir.", "archery_ticket_price": "0", - "id": "7629" - }, - { - "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, - "name": "Temple library key", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7632" - }, - { - "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" - }, - { - "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, - "name": "Battered tome", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7634" - }, - { - "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, - "name": "Leather book", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7635" - }, - { - "examine": "Rod of Ivandis dust.", - "durability": null, - "name": "Rod dust", - "archery_ticket_price": "0", - "id": "7636" - }, - { - "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", - "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_anims": "2080,2081,2082,2080", "attack_speed": "5", - "two_handed": "true", - "weapon_interface": "1", - "turn180_anim": "1206", + "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", - "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", + "shop_price": "250", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,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" }, { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", + "id": "6761", + "name": "Guthix mjolnir", + "archery_ticket_price": "0", "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" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "turn90cw_anim": "1207", - "examine": "A partialy charged rod.", - "walk_anim": "1205", - "has_special": "true", - "low_alchemy": "400", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "run_anim": "1210", + "id": "6762", + "name": "Saradomin mjolnir", + "examine": "A Saradomin Mjolnir.", "archery_ticket_price": "0", - "id": "7641", + "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", + "durability": null + }, + { + "id": "6768", + "name": "Poisoned cheese", + "examine": "A little more smelly than usual.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.2" + }, + { + "id": "6769", + "name": "Music scroll", + "examine": "Charming.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can obtain another sheet of music by talking to the snakecharmer of Pollnivneach.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6770", + "name": "Directions", + "examine": "Jimmy Dazzler's directions.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can obtain another set of directions to the party by talking to Jimmy Dazzler.", + "durability": null, + "tradeable": "false" + }, + { + "id": "6771", + "name": "Pot of weeds", + "examine": "Contains garden weeds.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6772", + "name": "Smouldering pot", + "examine": "Contains slowly burning garden weeds.", + "archery_ticket_price": "0", + "durability": null + }, + { + "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, - "high_alchemy": "600", - "destroy": "true", - "weight": "2", - "weapon_interface": "1", - "render_anim": "28", - "name": "Rod of ivandis(8)" - }, - { - "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", + "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", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "durability": null, - "name": "Guthix balance(unf)", - "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", - "durability": null, - "name": "Guthix balance(unf)", - "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", - "durability": null, - "name": "Guthix balance(unf)", - "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", - "durability": null, - "name": "Guthix balance(unf)", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "1200", - "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, - "high_alchemy": "1800", - "weight": "1.8", + "walk_anim": "1422", "weapon_interface": "10", - "render_anim": "27", - "name": "Gadderhammer" + "weight": "2.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1615", - "durability": null, - "name": "Gadderhammer", - "tradeable": "true", + "id": "6774", + "name": "Rat pole", + "examine": "A pole with one rat on it.", "archery_ticket_price": "0", - "id": "7669" - }, - { - "turn90cw_anim": "3680", - "examine": "I think they look a bit silly.", - "walk_anim": "3680", + "attack_speed": "5", + "bonuses": "0,-1,7,4,0,2,3,1,4,0,0,3,0,0,0", "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" + "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" }, { - "turn90cw_anim": "3680", - "examine": "I think they look a bit silly.", - "walk_anim": "3680", + "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, - "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" - }, - { - "durability": null, - "name": "Wooden shield", - "archery_ticket_price": "0", - "id": "7676", - "equipment_slot": "5" - }, - { - "examine": "It knows where the treasure is.", - "durability": null, - "name": "Treasure stone", - "weight": "1.8", - "archery_ticket_price": "0", - "id": "7677" - }, - { - "shop_price": "160", - "examine": "You can use this to open the prize chest!", - "durability": null, - "name": "Prize key", - "weight": "1", - "archery_ticket_price": "0", - "id": "7678" - }, - { - "examine": "A good tool for bashing someone.", - "durability": null, - "name": "Pugel", - "archery_ticket_price": "0", + "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", - "id": "7679", - "weapon_interface": "14", - "equipment_slot": "3" + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" }, { + "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, - "name": "Pugel", + "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": "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", + "turn180_anim": "1423", + "turn90ccw_anim": "1425", + "turn90cw_anim": "1424", + "two_handed": "true", + "walk_anim": "1422", + "weapon_interface": "10", + "weight": "2.2" + }, + { + "id": "6778", + "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", + "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": "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", + "durability": null + }, + { + "id": "6786", + "name": "Robe of elidinis", + "examine": "This looks quite old.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" + }, + { + "id": "6787", + "name": "Robe of elidinis", + "examine": "This looks quite old.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7" + }, + { + "id": "6788", + "name": "Torn robe", + "examine": "This robe is too torn to wear.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "6789", + "name": "Torn robe", + "examine": "This robe is too torn to wear.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "6790", + "name": "Shoes", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "10" + }, + { + "id": "6791", + "name": "Sole", + "examine": "Awusah's Sole.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "6792", + "name": "Ancestral key", + "examine": "An ancient key from the shrine in Nardah. (Spirits of the Elid)", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6793", + "name": "Ballad", + "examine": "The Ballad of Jaresh.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6794", + "name": "Choc-ice", + "examine": "A tasty treat from Nardah - better eat this before it melts.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "30", + "tradeable": "true" + }, + { + "id": "6795", + "name": "Choc-ice", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "6796", + "name": "Lamp", + "examine": "Wonder what happens if I rub it...", + "archery_ticket_price": "0", + "bankable": "false", + "durability": null, + "weight": "0.1" + }, + { + "id": "6797", + "name": "Watering can", + "examine": "See article", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "8" + }, + { + "id": "6798", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6799", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6800", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6801", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6802", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6803", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6804", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6805", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6806", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6807", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6808", + "name": "Champion scroll", + "examine": "A scroll once used by a champion.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "6809", + "name": "Granite legs", + "examine": "These look pretty heavy.", + "absorb": "2,0,5", + "archery_ticket_price": "0", + "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", - "archery_ticket_price": "0", - "id": "7680" + "weight": "15" }, { - "examine": "Party Pete's Bumper Book Of Games", + "id": "6810", + "name": "Bonemeal", + "examine": "A pot of crushed large zombie monkey bones.", + "archery_ticket_price": "0", "durability": null, - "name": "Game book", + "weight": "1" + }, + { + "id": "6811", + "name": "Skeletal wyvern", + "examine": "A very dangerous pile of animated Wyvern bones.", + "archery_ticket_price": "0", + "attack_speed": "6", + "durability": null + }, + { + "id": "6812", + "name": "Wyvern bones", + "examine": "Bones of a huge flying creature.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", "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)", - "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)", - "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)", - "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)", - "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", - "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", - "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)", - "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)", - "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)", - "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)", - "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", - "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", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "7714" + "weight": "0.5" }, { - "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)", - "weight": "1.5", + "id": "6813", + "name": "Granite legs", "archery_ticket_price": "0", - "id": "7716" - }, - { "durability": null, - "name": "Pot of tea (4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7717" + "tradeable": "true" }, { - "examine": "I'd really like a nice cup of tea.", - "durability": null, - "name": "Pot of tea (3)", - "weight": "1.5", + "id": "6814", + "name": "Fur", + "examine": "This would make warm clothing.", "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)", - "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)", - "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", - "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", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "archery_ticket_price": "0", - "id": "7754" - }, - { - "durability": null, - "name": "Chef's delight", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7755" - }, - { - "durability": null, - "name": "Paintbrush", - "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", + "shop_price": "20", "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", + "weight": "0.4" + }, + { + "id": "6815", + "name": "Fur", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "6816", + "name": "Wyvern bones", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "6817", + "name": "Slender blade", + "examine": "A slender two-handed sword.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "7759" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3140", + "destroy_message": "You can reclaim this item from the place you found it.", "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", + "equipment_slot": "3", + "render_anim": "124", + "run_anim": "7039", + "stand_anim": "7047", + "stand_turn_anim": "7040", "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7774" + "turn180_anim": "7045", + "turn90ccw_anim": "7043", + "turn90cw_anim": "7044", + "two_handed": "true", + "walk_anim": "7046", + "weight": "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.", + "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, - "name": "Reward token", + "equipment_slot": "3", + "render_anim": "28", + "run_anim": "1210", + "stand_anim": "813", + "stand_turn_anim": "1209", "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", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7779" - }, - { - "examine": "A tome of learning that focuses on the Fishing skill.", - "durability": null, - "name": "Fishing tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7780" - }, - { - "examine": "A tome of learning that focuses on the Fishing skill.", - "durability": null, - "name": "Fishing tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7781" - }, - { - "examine": "A tome of learning that focuses on Agility.", - "durability": null, - "name": "Agility tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7782" - }, - { - "examine": "A tome of learning that focuses on Agility.", - "durability": null, - "name": "Agility tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7783" - }, - { - "examine": "A tome of learning that focuses on Agility.", - "durability": null, - "name": "Agility tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7784" - }, - { - "examine": "A tome of learning that focuses on the Thieving skill.", - "durability": null, - "name": "Thieving tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7785" - }, - { - "examine": "A tome of learning that focuses on the Thieving skill.", - "durability": null, - "name": "Thieving tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7786" - }, - { - "examine": "A tome of learning that focuses on the Thieving skill.", - "durability": null, - "name": "Thieving tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7787" - }, - { - "examine": "A tome of learning which focuses on the Slayer skill.", - "durability": null, - "name": "Slayer tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7788" - }, - { - "examine": "A tome of learning which focuses on the Slayer skill.", - "durability": null, - "name": "Slayer tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7789" - }, - { - "examine": "A tome of learning which focuses on the Slayer skill.", - "durability": null, - "name": "Slayer tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7793" - }, - { - "examine": "A tome of learning that focuses on Firemaking.", - "durability": null, - "name": "Firemaking tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7794" - }, - { - "examine": "A tome of learning that focuses on Firemaking.", - "durability": null, - "name": "Firemaking tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "archery_ticket_price": "0", - "id": "7795" - }, - { - "examine": "A tome of learning that focuses on Firemaking.", - "durability": null, - "name": "Firemaking tome", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", - "low_alchemy": "14", - "high_alchemy": "21", - "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" - }, - { + "turn180_anim": "1206", + "turn90ccw_anim": "1208", "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" - }, - { - "durability": null, - "name": "Picture", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7805" - }, - { - "attack_anims": "381,390,390,390", - "examine": "A heavy duty sword.", - "durability": null, - "name": "Anger sword", - "archery_ticket_price": "0", - "attack_speed": "3", - "id": "7806", - "weapon_interface": "5", - "bonuses": "20,20,20,0,0,0,2,1,0,0,0,5,0,0,0", - "defence_anim": "397", - "equipment_slot": "3" - }, - { - "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", - "bonuses": "20,20,20,0,0,0,0,0,0,-1,0,13,0,0,0", - "defence_anim": "397", - "equipment_slot": "3" - }, - { - "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", - "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", - "id": "7809", - "stand_turn_anim": "1209", - "bonuses": "20,20,20,0,0,1,1,0,0,0,0,6,0,0,0" - }, - { - "shop_price": "1", - "examine": "This wine clearly did not age well.", - "durability": null, - "name": "Jug of vinegar", - "weight": "1", - "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" + "weight": "3" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6819", + "name": "Large pouch", + "examine": "A large pouch used for storing essence.", "archery_ticket_price": "0", - "id": "7834" - }, - { - "examine": "This needs a good polish.", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, - "name": "Bat wing", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7835" + "shop_price": "25000", + "weight": "1" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Rat bone", - "weight": "0.9", + "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", - "id": "7836" - }, - { - "examine": "See article", + "destroy": "true", + "destroy_message": "I can get another from the assassin beside the winch that leads down into Senntisten temple.", "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7837" + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Rat bone", - "weight": "0.9", + "id": "6821", + "name": "Orb", + "examine": "This looks valuable.", "archery_ticket_price": "0", - "id": "7838" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Baby dragon bone", - "weight": "0.9", + "id": "6822", + "name": "Star bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7839" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6823", + "name": "Star bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7840" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Baby dragon bone", - "weight": "0.9", + "id": "6824", + "name": "Star bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7841" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ogre ribs", - "weight": "0.9", + "id": "6825", + "name": "Star bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7842" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6826", + "name": "Star bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7843" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ogre ribs", - "weight": "0.9", + "id": "6827", + "name": "Star bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7844" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Jogre bone", - "weight": "0.9", + "id": "6828", + "name": "Box bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7845" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6829", + "name": "Box bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7846" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Jogre bone", - "weight": "0.9", + "id": "6830", + "name": "Box bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7847" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Zogre bone", - "weight": "0.9", + "id": "6831", + "name": "Box bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7848" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6832", + "name": "Box bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7849" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Zogre bone", - "weight": "0.9", + "id": "6833", + "name": "Box bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7850" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Mogre bone", - "weight": "0.9", + "id": "6834", + "name": "Diamond bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7851" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6835", + "name": "Diamond bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7852" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Mogre bone", - "weight": "0.9", + "id": "6836", + "name": "Diamond bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7853" + "durability": null }, { - "examine": "This needs a good polish./ This belongs in a museum!", - "durability": null, - "name": "Monkey paw", + "id": "6837", + "name": "Diamond bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7854" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6838", + "name": "Diamond bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7855" + "durability": null }, { - "examine": "This needs a good polish./ This belongs in a museum!", - "durability": null, - "name": "Monkey paw", + "id": "6839", + "name": "Diamond bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7856" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Dagannoth ribs", - "weight": "0.9", + "id": "6840", + "name": "Tree bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7857" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6841", + "name": "Tree bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7858" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Dagannoth ribs", - "weight": "0.9", + "id": "6842", + "name": "Tree bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7859" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Snake spine", - "weight": "0.9", + "id": "6843", + "name": "Tree bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7860" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6844", + "name": "Tree bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7861" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Snake spine", - "weight": "0.9", + "id": "6845", + "name": "Tree bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7862" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Zombie bone", - "weight": "0.9", + "id": "6846", + "name": "Bell bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7863" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6847", + "name": "Bell bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7864" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Zombie bone", - "weight": "0.9", + "id": "6848", + "name": "Bell bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7865" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Werewolf bone", - "weight": "0.9", + "id": "6849", + "name": "Bell bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7866" + "durability": null }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6850", + "name": "Bell bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7867" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Werewolf bone", - "weight": "0.9", + "id": "6851", + "name": "Bell bauble", + "examine": "See individual articles.", "archery_ticket_price": "0", - "id": "7868" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Moss giant bone", - "weight": "0.9", + "id": "6852", + "name": "Puppet box", + "examine": "A box for storing completed puppets.", "archery_ticket_price": "0", - "id": "7869" - }, - { - "examine": "See article", + "destroy": "true", "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7870" + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Moss giant bone", - "weight": "0.9", + "id": "6853", + "name": "Bauble box", + "examine": "A box for storing painted baubles.", "archery_ticket_price": "0", - "id": "7871" + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Fire giant bone", - "weight": "0.9", + "id": "6854", + "name": "Puppet box", + "examine": "A box for storing completed puppets.", "archery_ticket_price": "0", - "id": "7872" - }, - { - "examine": "See article", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7873" + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Fire giant bone", - "weight": "0.9", + "id": "6855", + "name": "Bauble box", + "examine": "A box for storing painted baubles.", "archery_ticket_price": "0", - "id": "7874" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ice giant ribs", - "weight": "0.9", + "id": "6856", + "name": "Bobble hat", + "examine": "A woolly bobble hat.", "archery_ticket_price": "0", - "id": "7875" - }, - { - "examine": "See article", + "destroy": "true", + "destroy_message": "You can obtain another hat from Diango in the Draynor Market.", "durability": null, - "name": "Bone in vinegar", - "weight": "1", - "archery_ticket_price": "0", - "id": "7876" + "equipment_slot": "0", + "tradeable": "false", + "weight": "0.25" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Ice giant ribs", - "weight": "0.9", + "id": "6857", + "name": "Bobble scarf", + "examine": "A woolly scarf.", "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.", + "destroy_message": "You can reclaim this item from the place you found it.", "durability": null, - "name": "Undead cow ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7890" + "equipment_slot": "2", + "weight": "0.25" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6858", + "name": "Jester hat", + "examine": "A woolly Jester hat.", "archery_ticket_price": "0", - "id": "7891" - }, - { - "examine": "This needs a good polish.", + "destroy": "true", + "destroy_message": "You can obtain another hat from Diango in the Draynor Market.", "durability": null, - "name": "Undead cow ribs", - "weight": "0.9", - "archery_ticket_price": "0", - "id": "7892" - }, - { + "equipment_slot": "0", "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" + "tradeable": "false", + "weight": "0.25" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6859", + "name": "Jester scarf", + "examine": "A woolly jester scarf.", "archery_ticket_price": "0", - "id": "7894" + "destroy": "true", + "destroy_message": "You can get another Jester scarf from Diango.", + "durability": null, + "equipment_slot": "2", + "tradeable": "false", + "weight": "0.25" }, { - "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", + "id": "6860", + "name": "Tri-jester hat", + "examine": "A woolly triple bobble jester hat.", "archery_ticket_price": "0", - "id": "7895" + "destroy": "true", + "destroy_message": "You may get another from Diango in Draynor Village.", + "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "0.25" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Rabbit bone", - "weight": "0.9", + "id": "6861", + "name": "Tri-jester scarf", + "examine": "A woolly jester scarf.", "archery_ticket_price": "0", - "id": "7896" + "destroy": "true", + "destroy_message": "You can obtain another jester scarf from Diango in the Draynor Market.", + "durability": null, + "equipment_slot": "2", + "tradeable": "false", + "weight": "0.25" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6862", + "name": "Woolly hat", + "examine": "A woolly tobogganing hat.", "archery_ticket_price": "0", - "id": "7897" + "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" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Rabbit bone", - "weight": "0.9", + "id": "6863", + "name": "Woolly scarf", + "examine": "A woolly tobogganing scarf.", "archery_ticket_price": "0", - "id": "7898" + "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" }, { - "examine": "Dirty:This bone needs a good polish.", - "durability": null, - "name": "Basilisk bone", - "weight": "0.9", + "id": "6864", + "name": "Marionette handle", + "examine": "The controlling part of a marionette.", "archery_ticket_price": "0", - "id": "7899" + "durability": null, + "equipment_slot": "3" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6865", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7900" + "destroy": "true", + "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", + "durability": null, + "tradeable": "false" }, { - "examine": "Dirty:This bone needs a good polish.", - "durability": null, - "name": "Basilisk bone", - "weight": "0.9", + "id": "6866", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7901" + "destroy": "true", + "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Desert lizard bone", - "weight": "0.9", + "id": "6867", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7902" + "destroy": "true", + "destroy_message": "You can obtain another marionette in Diango's workshop by the trap door.", + "durability": null, + "tradeable": "false" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6868", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7903" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Desert lizard bone", - "weight": "0.9", + "id": "6869", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7904" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Cave goblin skull", - "weight": "0.9", + "id": "6870", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7905" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6871", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7906" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Cave goblin skull", - "weight": "0.9", + "id": "6872", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7907" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Big frog leg", - "weight": "1", + "id": "6873", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7908" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6874", + "name": "Red marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7909" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Big frog leg", - "weight": "1", + "id": "6875", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7910" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Vulture wing", - "weight": "0.9", + "id": "6876", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7911" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6877", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7912" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Vulture wing", - "weight": "0.9", + "id": "6878", + "name": "Blue marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7913" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Jackal bone", - "weight": "0.9", + "id": "6879", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7914" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "See article", - "durability": null, - "name": "Bone in vinegar", - "weight": "1", + "id": "6880", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7915" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "This needs a good polish.", - "durability": null, - "name": "Jackal bone", - "weight": "0.9", + "id": "6881", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7916" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "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", + "id": "6882", + "name": "Green marionette", + "examine": "I've got no strings ... oh hang on!", "archery_ticket_price": "0", - "id": "7917", - "bonuses": "0,0,0,0,-2,19,21,16,0,19,6,0,0,0,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" }, { - "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", + "id": "6886", + "name": "Progress hat", + "examine": "A magic training arena progress hat", "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" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "equipment_slot": "0" }, { - "shop_price": "500", - "examine": "A very good vintage.", + "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, - "name": "Bottle of wine", + "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", - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "1", - "archery_ticket_price": "0", - "id": "7919" + "weight": "1" }, { + "id": "6890", + "name": "Mage's book", + "archery_ticket_price": "0", "durability": null, - "name": "Bottle of wine", + "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", - "archery_ticket_price": "0", - "id": "7920" + "weight": "0.3" }, { - "examine": "This one has clearly been taken down and passed around.", - "durability": null, - "name": "Empty wine bottle", - "high_alchemy": "1", - "weight": "1", + "id": "6894", + "name": "Adamant kiteshield", + "examine": "A large metal shield.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "7921" - }, - { - "examine": "The money off voucher has expired.", + "bankable": "false", + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", "durability": null, - "name": "Al kharid flyer", - "high_alchemy": "1", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "7928" + "weight": "5.8" }, { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", + "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, - "name": "Easter egg", "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "7929" + "weight": "1.8" }, { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", + "id": "6896", + "name": "Emerald", + "examine": "This looks valuable.", + "archery_ticket_price": "0", + "bankable": "false", "durability": null, - "name": "Easter egg", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "7930" + "weight": "1.8" }, { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", + "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, - "name": "Easter egg", "tradeable": "true", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "7931" + "weight": "0.3" }, { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", + "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, - "name": "Easter egg", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "7932" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "1", + "weight": "0.2" }, { - "examine": "Happy Easter.", - "grand_exchange_price": "64747137", + "id": "6909", + "name": "Beginner wand", + "archery_ticket_price": "0", "durability": null, - "name": "Easter egg", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "archery_ticket_price": "0", - "id": "7933" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "1" }, { - "shop_price": "390", - "ge_buy_limit": "100", - "examine": "A field ration to help your wounds go away.", - "grand_exchange_price": "171", + "id": "6911", + "name": "Apprentice wand", + "archery_ticket_price": "0", "durability": null, - "name": "Field ration", + "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", - "low_alchemy": "120", - "high_alchemy": "180", - "archery_ticket_price": "0", - "id": "7934" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "171", + "id": "6913", + "name": "Teacher wand", + "archery_ticket_price": "0", "durability": null, - "name": "Field ration", + "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", - "archery_ticket_price": "0", - "id": "7935" + "turn180_anim": "820", + "turn90ccw_anim": "822", + "turn90cw_anim": "821", + "walk_anim": "1146", + "weapon_interface": "1", + "weight": "0.1" }, { - "shop_price": "1", - "ge_buy_limit": "25000", - "examine": "An uncharged Rune Stone of extra capability.", - "grand_exchange_price": "180", + "id": "6915", + "name": "Master wand", + "archery_ticket_price": "0", "durability": null, - "name": "Pure essence", + "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", - "low_alchemy": "1", - "high_alchemy": "2", - "archery_ticket_price": "0", - "id": "7936" + "weight": "2.2" }, { - "ge_buy_limit": "25000", - "grand_exchange_price": "109", + "id": "6917", + "name": "Infinity top", + "archery_ticket_price": "0", "durability": null, - "name": "Pure essence", + "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", - "archery_ticket_price": "0", - "id": "7937" + "weight": "0.4" }, { - "ge_buy_limit": "5000", - "examine": "A word in your shell-like.", - "grand_exchange_price": "13600", - "durability": null, - "name": "Tortoise shell", - "tradeable": "true", - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "6.8", + "id": "6919", + "name": "Infinity hat", "archery_ticket_price": "0", - "id": "7939" + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "13600", - "durability": null, - "name": "Tortoise shell", - "tradeable": "true", + "id": "6920", + "name": "Infinity boots", + "examine": "Mystical boots.", "archery_ticket_price": "0", - "id": "7940" - }, - { - "examine": "A sturdy sheet of iron.", - "durability": null, - "name": "Iron sheet", - "low_alchemy": "12", - "high_alchemy": "18", - "weight": "1.75", - "archery_ticket_price": "0", - "id": "7941" - }, - { - "examine": "Perfect for storing. Not so good for eating.", - "durability": null, - "name": "Fresh monkfish", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "7942" - }, - { - "examine": "Perfect for storing. Not so good for eating.", - "durability": null, - "name": "Fresh monkfish", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "7943" - }, - { - "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", - "low_alchemy": "92", - "high_alchemy": "138", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "7944" - }, - { - "ge_buy_limit": "20000", - "grand_exchange_price": "618", - "durability": null, - "name": "Raw monkfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7945" - }, - { - "ge_buy_limit": "10000", - "examine": "A tasty fish.", - "grand_exchange_price": "617", - "durability": null, - "name": "Monkfish", - "tradeable": "true", - "low_alchemy": "92", - "high_alchemy": "138", - "weight": "0.4", - "archery_ticket_price": "0", - "id": "7946" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "617", - "durability": null, - "name": "Monkfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7947" - }, - { - "durability": null, - "name": "Burnt monkfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7949" - }, - { - "examine": "A highly portable army of skeletal magic.", - "durability": null, - "name": "Bone seeds", - "archery_ticket_price": "0", - "id": "7950" - }, - { - "shop_price": "160", - "examine": "A book taken from the desk of Herman Caranos.", - "durability": null, - "name": "Herman's book", - "tradeable": "false", + "bonuses": "0,0,0,5,0,0,0,0,5,0,5,0,0,0,0", "destroy": "true", - "weight": "0.09", - "archery_ticket_price": "0", - "id": "7951" + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "10", + "requirements": "{1,25}-{6,50}", + "tradeable": "true", + "weight": "0.4" }, { - "examine": "Useless without the head.", - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "6921", + "name": "Infinity boots", "archery_ticket_price": "0", - "id": "7952", + "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.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5", + "requirements": "{18,33}", + "shop_price": "130", + "weight": "2.2" + }, + { + "id": "7054", + "name": "Chilli potato", + "examine": "A baked potato with chilli con carne", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7055", + "name": "Chilli potato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7056", + "name": "Egg potato", + "examine": "A baked potato with egg and tomato.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7057", + "name": "Egg potato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7058", + "name": "Mushroom potato", + "examine": "A baked potato with mushroom and onions.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7059", + "name": "Mushroom potato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7060", + "name": "Tuna potato", + "examine": "A baked potato with tuna and sweetcorn.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7063", + "name": "Chilli con carne", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7064", + "name": "Egg and tomato", + "examine": "A bowl of scrambled eggs and tomato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7065", + "name": "Egg and tomato", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7066", + "name": "Mushroom & onion", + "examine": "A bowl of fried mushroom and onions.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7071", + "name": "Minced meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7072", + "name": "Spicy sauce", + "examine": "A bowl of spicy sauce.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7073", + "name": "Spicy sauce", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7074", + "name": "Chopped garlic", + "examine": "A bowl of chopped garlic.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7075", + "name": "Chopped garlic", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7076", + "name": "Uncooked egg", + "examine": "A bowl of uncooked egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7077", + "name": "Uncooked egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7078", + "name": "Scrambled egg", + "examine": "A bowl of scrambled egg.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7079", + "name": "Scrambled egg", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7080", + "name": "Sliced mushrooms", + "examine": "A bowl of sliced Bittercap mushrooms.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7081", + "name": "Sliced mushrooms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7082", + "name": "Fried mushrooms", + "examine": "A bowl of fried Bittercap mushrooms.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7083", + "name": "Fried mushrooms", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7084", + "name": "Fried onions", + "examine": "A bowl of sliced, fried onions.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7085", + "name": "Fried onions", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7086", + "name": "Chopped tuna", + "examine": "A bowl of finely chopped tuna.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7087", + "name": "Chopped tuna", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7088", + "name": "Sweetcorn", + "examine": "A bowl of cooked sweetcorn.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7089", + "name": "Sweetcorn", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "7091", + "name": "Burnt egg", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7093", + "name": "Burnt onion", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7095", + "name": "Burnt mushroom", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7108", + "name": "Gunpowder", + "examine": "Best keep this away from naked flames.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "7109", + "name": "Fuse", + "examine": "A fuse.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "7110", + "name": "Stripy pirate shirt", + "examine": "A seaworthy grey shirt.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "100", + "shop_price": "300", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7111", + "name": "Stripy pirate shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7112", + "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": "7113", + "name": "Pirate bandana", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7114", + "name": "Pirate boots", + "examine": "Not for land lubbers.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "10", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "4" + }, + { + "id": "7115", + "name": "Pirate boots", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7116", + "name": "Pirate leggings", + "examine": "Well, okay, they're beige, but they're part of the white pirate clothing set, so...", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "100", + "shop_price": "350", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7117", + "name": "Pirate leggings", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7118", + "name": "Canister", + "examine": "A cannister holding shrapnel.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "7120", + "name": "Ramrod", + "examine": "For cleaning and packing the cannon.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "2" + }, + { + "id": "7121", + "name": "Repair plank", + "examine": "A plank of wood to repair the hull with.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7122", + "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": "7123", + "name": "Stripy pirate shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7124", + "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": "7125", + "name": "Pirate bandana", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7126", + "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": "7127", + "name": "Pirate leggings", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7128", + "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": "7129", + "name": "Stripy pirate shirt", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7130", + "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": "7131", + "name": "Pirate bandana", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7132", + "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": "7133", + "name": "Pirate leggings", + "archery_ticket_price": "0", + "durability": null, + "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", + "attack_speed": "4", + "bonuses": "5,20,0,-5,0,6,6,6,0,0,0,25,0,0,0", + "durability": null, + "equipment_slot": "3", + "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", + "tradeable": "true", + "weight": "1.3" + }, + { + "id": "7160", + "name": "Killerwatt", + "examine": "Before being attacked: A ball of Electrical energy.", + "archery_ticket_price": "0", + "attack_speed": "2", + "durability": null + }, + { + "id": "7161", + "name": "Insulated boots", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7162", + "name": "Pie recipe book", + "examine": "Lots of pie recipes for me to try.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "5", + "tradeable": "true" + }, + { + "id": "7163", + "name": "Pie recipe book", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7164", + "name": "Part mud pie", + "examine": "Still needs two more ingredients.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "54", + "tradeable": "true" + }, + { + "id": "7165", + "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": "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7169", + "name": "Raw 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": "7170", + "name": "Mud pie", + "examine": "All the good of the earth.", + "archery_ticket_price": "0", + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7173", + "name": "Part 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": "7174", + "name": "Part garden pie", + "examine": "Still needs one more ingredient.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7175", + "name": "Part 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": "7176", + "name": "Raw garden pie", + "examine": "Needs cooking before I eat it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7177", + "name": "Raw 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": "7178", + "name": "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": "7179", + "name": "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": "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7183", + "name": "Part 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": "7184", + "name": "Part fish pie", + "examine": "Still needs one more ingredient.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7185", + "name": "Part 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": "7186", + "name": "Raw fish pie", + "examine": "Raw fish is risky, better cook it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7187", + "name": "Raw 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": "7188", + "name": "Fish pie", + "examine": "Bounty of the sea.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7189", + "name": "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": "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. ", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7193", + "name": "Part 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": "7194", + "name": "Part admiral pie", + "examine": "Still needs one more ingredient.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7195", + "name": "Part 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": "7196", + "name": "Raw admiral pie", + "examine": "This would taste a lot better cooked.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7197", + "name": "Raw 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": "7198", + "name": "Admiral pie", + "examine": "Much tastier than a normal fish pie.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7199", + "name": "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": "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7203", + "name": "Part 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": "7204", + "name": "Part wild pie", + "examine": "Still needs one more ingredient.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7205", + "name": "Part 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": "7206", + "name": "Raw wild pie", + "examine": "Good as it looks, I'd better cook it.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7207", + "name": "Raw 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": "7208", + "name": "Wild pie", + "examine": "A triumph of man over nature.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7209", + "name": "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": "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7213", + "name": "Part 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": "7214", + "name": "Part summer pie", + "examine": "Still needs one more ingredient.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7215", + "name": "Part 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": "7216", + "name": "Raw summer pie", + "examine": "Fresh fruit may be good for you, but I should really cook this.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true", + "weight": "0.5" + }, + { + "id": "7217", + "name": "Raw 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": "7218", + "name": "Summer pie", + "examine": "All the fruits of a very small forest.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "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.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "19", + "tradeable": "true" + }, + { + "id": "7223", + "name": "Roast rabbit", + "examine": "A delicious looking piece of roast rabbit.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "25", + "tradeable": "true" + }, + { + "id": "7224", + "name": "Skewered rabbit", + "examine": "Might taste better cooked.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "67", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "7225", + "name": "Iron spit", + "examine": "An iron spit.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "81", + "tradeable": "true", + "weight": "1.5" + }, + { + "id": "7227", + "name": "Burnt chompy", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7228", + "name": "Cooked chompy", + "examine": "It might look delicious to an ogre.Roasted chompy bird.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "130", + "tradeable": "true", + "weight": "10" + }, + { + "id": "7229", + "name": "Cooked chompy", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7230", + "name": "Skewered chompy", + "examine": "A skewered chompy bird.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "7231", + "name": "Burnt rabbit", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7232", + "name": "Roast rabbit", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7233", + "name": "Skewered rabbit", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7234", + "name": "Iron spit", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7235", + "name": "Skewered chompy", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7236", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7237", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7238", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "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.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7241", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7242", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7243", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7244", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7245", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7246", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7247", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "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.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7258", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7259", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7260", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7261", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7262", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7263", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7264", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7265", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7266", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "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", + "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7270", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7271", + "name": "Challenge scroll", + "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7272", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7273", + "name": "Challenge scroll", + "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7274", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7275", + "name": "Challenge scroll", + "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7276", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7277", + "name": "Challenge scroll", + "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7278", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7279", + "name": "Challenge scroll", + "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7280", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7281", + "name": "Challenge scroll", + "examine": "I need to answer this correctly.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7282", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "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", + "durability": null + }, + { + "id": "7286", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7287", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7288", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7289", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7290", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7291", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7292", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7293", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7294", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7295", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7296", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7298", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7300", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7301", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "5000" + }, + { + "id": "7303", + "name": "Clue scroll", + "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", + "durability": null, + "shop_price": "5000" + }, + { + "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", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "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", + "name": "Red boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7320", + "name": "Red boater", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7321", + "name": "Orange boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7322", + "name": "Orange boater", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7323", + "name": "Green boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7324", + "name": "Green boater", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7325", + "name": "Blue boater", + "examine": "Stylish!", + "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", + "name": "Black boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7328", + "name": "Black boater", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7329", + "name": "Red firelighter", + "examine": "Makes firelighting a lot easier.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7330", + "name": "Green firelighter", + "examine": "Makes firelighting a lot easier.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7331", + "name": "Blue firelighter", + "examine": "Makes firelighting a lot easier.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7332", + "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,10,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "7333", + "name": "Black shield(h1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7334", + "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,30,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7335", + "name": "Adamant shield(h1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7336", + "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,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" + }, + { + "id": "7337", + "name": "Rune shield(h1)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7338", + "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,10,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "7339", + "name": "Black shield(h2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7340", + "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,30,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7341", + "name": "Adamant shield(h2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7342", + "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,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" + }, + { + "id": "7343", + "name": "Rune shield(h2)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7344", + "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,10,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "7345", + "name": "Black shield(h3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7346", + "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,30,0,0,0,0", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7347", + "name": "Adamant shield(h3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7348", + "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,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7349", + "name": "Rune shield(h3)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "7350", + "name": "Black shield(h4)", + "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": "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.", + "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": "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.", + "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", + "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", + "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,40,0,0,0,0", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" + }, + { + "id": "7361", + "name": "Rune shield(h5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "requirements": "", + "tradeable": "true" + }, + { + "id": "7362", + "name": "Studded body (g)", + "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,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", + "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,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": "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", + "examine": "Enchanted Wizards robes.", + "absorb": "2,1,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": "2", + "requirements": "{1,20}-{6,40}", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "7399", + "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", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,20}-{6,40}", + "tradeable": "true", + "weight": "1" + }, + { + "id": "7400", + "name": "Enchanted hat", + "examine": "A three pointed hat of magic.", + "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": "2", + "remove_head": "true", + "requirements": "{1,20}-{6,40}", + "tradeable": "true" + }, + { + "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", + "durability": null + }, + { + "id": "7405", + "name": "Green logs", + "examine": "A number of chemical covered wooden logs.", + "archery_ticket_price": "0", + "durability": null, + "weight": "2" + }, + { + "id": "7406", + "name": "Blue logs", + "examine": "A number of chemical covered wooden logs.", + "archery_ticket_price": "0", + "durability": null, + "weight": "2" + }, + { + "id": "7407", + "name": "Dragon 2h sword", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7408", + "name": "Draynor skull", + "examine": "I shouldn't joke; this is a grave matter.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I found this in a grave at Draynor Manor.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7409", + "name": "Magic secateurs", + "examine": "The only way to kill a Tanglefoot.", + "archery_ticket_price": "0", + "attack_speed": "5", + "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", + "shop_price": "40000", + "tradeable": "false", + "weapon_interface": "5", + "weight": "0.4" + }, + { + "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, "equipment_slot": "3" }, { - "durability": null, - "name": "Picture", - "tradeable": "true", + "id": "7411", + "name": "Symptoms list", + "examine": "A list of the Fairy Queen's symptoms.", "archery_ticket_price": "0", - "id": "7953" - }, - { - "durability": null, - "name": "Burnt shrimp", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "7955" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "7956" - }, - { - "shop_price": "2", - "examine": "A mostly clean apron.", - "grand_exchange_price": "169", - "durability": null, - "name": "White apron", - "tradeable": "true", - "high_alchemy": "1", - "weight": "0.45", - "archery_ticket_price": "0", - "id": "7957" - }, - { + "destroy": "true", "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A prop for holding up a tunnel roof.", "durability": null, - "name": "Mining prop", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "destroy": "true", - "archery_ticket_price": "0", - "id": "7958" + "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, - "name": "Heavy box", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "destroy": "true", + "id": "7413", + "name": "Bird's nest", + "examine": "Full: ", "archery_ticket_price": "0", - "id": "7959" + "durability": null }, { - "examine": "It says 'To the dungeons' on the side.", - "durability": null, - "name": "Empty box", - "low_alchemy": "4", - "high_alchemy": "6", + "id": "7414", + "name": "Paddle", "archery_ticket_price": "0", - "id": "7960" + "durability": null, + "equipment_slot": "3", + "weapon_interface": "1" }, { - "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", - "low_alchemy": "4", - "high_alchemy": "6", + "id": "7415", + "name": "Paddle", "archery_ticket_price": "0", - "id": "7961" + "durability": null, + "tradeable": "true" }, { - "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", - "low_alchemy": "4", - "high_alchemy": "6", + "id": "7416", + "name": "Mole claw", + "examine": "A mole claw.", "archery_ticket_price": "0", - "id": "7962" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", - "low_alchemy": "4", - "high_alchemy": "6", + "id": "7417", + "name": "Mole claw", "archery_ticket_price": "0", - "id": "7963" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", - "low_alchemy": "4", - "high_alchemy": "6", + "id": "7418", + "name": "Mole skin", + "examine": "The skin of a large mole.", "archery_ticket_price": "0", - "id": "7964" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "A diary with one/two/three/four/five page(s).", - "durability": null, - "name": "Burnt diary", - "low_alchemy": "4", - "high_alchemy": "6", + "id": "7419", + "name": "Mole skin", "archery_ticket_price": "0", - "id": "7965" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "examine": "A dwarf-made coal engine. It looks very sturdy.", - "durability": null, - "name": "Engine", - "low_alchemy": "2", - "tradeable": "false", - "high_alchemy": "3", - "destroy": "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", - "id": "7967" + "durability": null }, { - "examine": "A beam with a pulley attached.", - "durability": null, - "name": "Pulley beam", - "low_alchemy": "2", - "high_alchemy": "3", + "id": "7421", + "name": "Fungicide spray 10", + "examine": "Pumps fungicide.", "archery_ticket_price": "0", - "id": "7969" + "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" }, { - "examine": "A long beam with a pulley attached.", - "durability": null, - "name": "Long pulley beam", - "low_alchemy": "2", - "high_alchemy": "3", + "id": "7422", + "name": "Fungicide spray 9", + "examine": "Pumps fungicide.", "archery_ticket_price": "0", - "id": "7970" + "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" }, { - "examine": "A very long beam with a pulley attached.", - "durability": null, - "name": "Longer pulley beam", - "low_alchemy": "2", - "high_alchemy": "3", + "id": "7423", + "name": "Fungicide spray 8", + "examine": "Pumps fungicide.", "archery_ticket_price": "0", - "id": "7971" + "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" }, { - "examine": "The manual for an AMCE Lift-In-A-Box.", - "durability": null, - "name": "Lift manual", - "low_alchemy": "2", - "tradeable": "false", - "high_alchemy": "3", - "destroy": "true", + "id": "7424", + "name": "Fungicide spray 7", + "examine": "Pumps fungicide.", "archery_ticket_price": "0", - "id": "7972" + "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" }, { - "examine": "A wooden beam.", - "durability": null, - "name": "Beam", - "low_alchemy": "2", - "high_alchemy": "3", + "id": "7425", + "name": "Fungicide spray 6", + "examine": "Pumps fungicide.", "archery_ticket_price": "0", - "id": "7973" + "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, - "name": "Crawling hand", + "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", - "id": "7975" + "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" }, { - "examine": "I should get it stuffed!", - "durability": null, - "name": "Cockatrice head", - "low_alchemy": "800", - "tradeable": "false", - "high_alchemy": "1200", - "destroy": "true", - "weight": "10", + "id": "7436", + "name": "Egg whisk", "archery_ticket_price": "0", - "id": "7976" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "I should get it stuffed!", - "durability": null, - "name": "Basilisk head", - "low_alchemy": "1600", - "tradeable": "false", - "high_alchemy": "2400", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7977" - }, - { - "examine": "I should get it stuffed!", - "durability": null, - "name": "Kurask head", - "low_alchemy": "2400", - "tradeable": "false", - "high_alchemy": "3600", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7978" - }, - { - "examine": "I should get it stuffed!", - "durability": null, - "name": "Abyssal head", - "low_alchemy": "4800", - "tradeable": "false", - "high_alchemy": "7200", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7979" - }, - { - "examine": "I should get it stuffed!", - "durability": null, - "name": "Kbd heads", - "low_alchemy": "20000", - "tradeable": "false", - "high_alchemy": "30000", - "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" - }, - { - "durability": null, - "name": "Crawling hand", + "id": "7437", + "name": "Spork", + "examine": "Use the spork.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "7982" - }, - { - "examine": "I should get it stuffed!", + "bonuses": "11,8,-2,0,0,0,2,1,0,0,0,12,0,0,0", "durability": null, - "name": "Cockatrice head", - "low_alchemy": "800", - "tradeable": "false", - "high_alchemy": "1200", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7983" + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{0,10}", + "shop_price": "292", + "tradeable": "true", + "weapon_interface": "5", + "weight": "1.8" }, { - "examine": "I should get it stuffed!", + "id": "7438", + "name": "Spork", + "archery_ticket_price": "0", "durability": null, - "name": "Basilisk head", - "low_alchemy": "1600", - "tradeable": "false", - "high_alchemy": "2400", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7984" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "I should get it stuffed!", + "id": "7439", + "name": "Spatula", + "examine": "A large spatula... of doom!", + "archery_ticket_price": "0", + "attack_speed": "7", + "bonuses": "-4,27,21,-4,0,0,0,0,0,-1,0,22,0,0,0", "durability": null, - "name": "Kurask head", - "low_alchemy": "2400", - "tradeable": "false", - "high_alchemy": "3600", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7985" + "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" }, { - "examine": "I should get it stuffed!", + "id": "7440", + "name": "Spatula", + "archery_ticket_price": "0", "durability": null, - "name": "Abyssal head", - "low_alchemy": "4800", - "tradeable": "false", - "high_alchemy": "7200", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7986" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "I should get it stuffed!", + "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, - "name": "Kbd heads", - "low_alchemy": "20000", - "tradeable": "false", - "high_alchemy": "30000", - "destroy": "true", - "weight": "10", - "archery_ticket_price": "0", - "id": "7987" + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{0,20}", + "shop_price": "1494", + "tradeable": "true", + "weapon_interface": "10", + "weight": "1.5" }, { - "examine": "I should get it stuffed!", + "id": "7442", + "name": "Frying pan", + "archery_ticket_price": "0", "durability": null, - "name": "Kq head", - "archery_ticket_price": "0", - "id": "7988" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "Whopper! I should get this stuffed!", + "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, - "name": "Big bass", - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "6", - "archery_ticket_price": "0", - "id": "7989" + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{0,30}", + "shop_price": "2880", + "tradeable": "true", + "weapon_interface": "6", + "weight": "2" }, { - "examine": "Whopper! I should get this stuffed!", + "id": "7444", + "name": "Skewer", + "archery_ticket_price": "0", "durability": null, - "name": "Big bass", - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "6", - "archery_ticket_price": "0", - "id": "7990" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "Whopper! I should get this stuffed!", + "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, - "name": "Big swordfish", - "low_alchemy": "1000", - "high_alchemy": "1500", - "archery_ticket_price": "0", - "id": "7991" + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{0,40}", + "shop_price": "12960", + "tradeable": "true", + "weapon_interface": "10", + "weight": "1.8" }, { - "examine": "Whopper! I should get this stuffed!", + "id": "7446", + "name": "Rolling pin", + "archery_ticket_price": "0", "durability": null, - "name": "Big swordfish", - "low_alchemy": "1000", - "high_alchemy": "1500", - "archery_ticket_price": "0", - "id": "7992" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "Its a monster! I should get this stuffed!", + "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, - "name": "Big shark", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "0.7", - "archery_ticket_price": "0", - "id": "7993" + "equip_audio": "2244", + "equipment_slot": "3", + "ge_buy_limit": "100", + "requirements": "{0,40}", + "shop_price": "8000", + "tradeable": "true", + "weapon_interface": "5" }, { - "examine": "Its a monster! I should get this stuffed!", + "id": "7448", + "name": "Kitchen knife", + "archery_ticket_price": "0", "durability": null, - "name": "Big shark", - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "0.7", - "archery_ticket_price": "0", - "id": "7994" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "A portrait of King Arthur.", + "id": "7449", + "name": "Meat tenderiser", + "examine": "Often used to soften tough meat up.", + "archery_ticket_price": "0", + "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, - "name": "Arthur portrait", - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "1", - "archery_ticket_price": "0", - "id": "7995" + "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", + "tradeable": "true", + "weapon_interface": "6", + "weight": "0.5" + }, + { + "id": "7452", + "name": "Cleaver", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "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", + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "50", + "weight": "0.2" + }, + { + "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", + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "100", + "weight": "0.2" + }, + { + "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", + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "325", + "weight": "0.2" + }, + { + "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", + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "500", + "weight": "0.2" + }, + { + "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", + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", "shop_price": "1000", - "examine": "A portrait of Elena.", - "durability": null, - "name": "Elena portrait", - "weight": "1", - "archery_ticket_price": "0", - "id": "7996" + "weight": "0.2" }, { - "examine": "A painting of the staute of King Alvis of Keldagrim", - "durability": null, - "name": "Keldagrim portrait", - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "1", + "id": "7458", + "name": "Gloves", + "examine": "A pair of mithril-coloured gloves.", "archery_ticket_price": "0", - "id": "7997" + "bonuses": "6,6,6,3,6,6,6,6,3,6,3,6,0,0,0", + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "shop_price": "1500" }, { - "examine": "A portrait of Prince Brand and Princess Astrid of Miscellania.", - "durability": null, - "name": "Misc. portrait", - "weight": "1", + "id": "7459", + "name": "Gloves", + "examine": "A pair of adamant-coloured gloves.", "archery_ticket_price": "0", - "id": "7998" - }, - { - "shop_price": "2000", - "examine": "The exotic land of the Elves.", + "bonuses": "7,7,7,4,7,7,7,7,4,7,4,7,0,0,0", "durability": null, - "name": "Isafdar painting", - "low_alchemy": "800", - "high_alchemy": "1200", - "archery_ticket_price": "0", - "id": "8000" - }, - { - "shop_price": "2000", - "examine": "The tropical coast of karamja.", - "durability": null, - "name": "Karamja painting", - "low_alchemy": "800", - "high_alchemy": "1200", - "archery_ticket_price": "0", - "id": "8001" - }, - { - "shop_price": "2000", - "examine": "Oxtable's famous painting of the Lumbridge water mill.", - "durability": null, - "name": "Lumbridge painting", - "weight": "1", - "archery_ticket_price": "0", - "id": "8002" - }, - { - "shop_price": "2000", - "examine": "A painting of the spooky forests of morytania.", - "durability": null, - "name": "Morytania painting", - "weight": "1", - "archery_ticket_price": "0", - "id": "8003" - }, - { + "equip_audio": "2236", + "equipment_slot": "9", "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1341", - "durability": null, - "name": "Varrock teleport", - "tradeable": "true", - "low_alchemy": "1699", - "high_alchemy": "1700", - "archery_ticket_price": "0", - "id": "8007" + "weight": "0.2" }, { - "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1371", - "durability": null, - "name": "Lumbridge teleport", - "tradeable": "true", - "low_alchemy": "1699", - "high_alchemy": "1700", + "id": "7460", + "name": "Gloves", + "examine": "A pair of rune-coloured gloves.", "archery_ticket_price": "0", - "id": "8008" + "bonuses": "8,8,8,4,8,8,8,8,4,8,4,8,0,0,0", + "durability": null, + "equip_audio": "2236", + "equipment_slot": "9", + "requirements": "{1,13}", + "shop_price": "5000" }, { - "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1414", + "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", "durability": null, - "name": "Falador teleport", - "tradeable": "true", - "low_alchemy": "1699", - "high_alchemy": "1700", + "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", + "weight": "0.2" + }, + { + "id": "7463", + "name": "Cornflour", + "examine": "It's cornflour in a pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7464", + "name": "Book on chickens", + "examine": "A tatty old book belonging to the Wise Old Man of Draynor Village.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7465", + "name": "Vanilla pod", + "examine": "Surprise, it looks like a vanilla pod.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7466", + "name": "Cornflour", + "examine": "It's cornflour in a pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7468", + "name": "Pot of cornflour", + "examine": "It's cornflour in a pot.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7470", + "name": "Cornflour mixture", + "examine": "A mixture of milk, cream and cornflour.", + "archery_ticket_price": "0", "destroy": "true", - "archery_ticket_price": "0", - "id": "8009" + "destroy_message": "I'll have to get all the ingredients again.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "shop_price": "2500", - "ge_buy_limit": "1000", - "examine": "A tablet containing a magic spell.", - "grand_exchange_price": "1369", + "id": "7471", + "name": "Milky mixture", + "examine": "It's a bucket of milk and cream.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I'll have to get all the ingredients again.", "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "7472", + "name": "Cinnamon", + "examine": "Some cinnamon sticks.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7476", + "name": "Brulee supreme", + "examine": "A pot of brulee supreme.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "7477", + "name": "Evil chicken's egg", + "examine": "What came first, the chicken or...", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "7478", + "name": "Dragon token", + "examine": "It's got a dragon on it.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "I got this by killing a Black Dragon. It might be hard to get another one.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7479", + "name": "Spicy stew", + "examine": "It's a meat and potato stew with fancy seasoning.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "1.5" + }, + { + "id": "7480", + "name": "Red spice (4)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7481", + "name": "Red spice (3)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7482", + "name": "Red spice (2)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7483", + "name": "Red spice (1)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7484", + "name": "Orange spice (4)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7485", + "name": "Orange spice (3)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7486", + "name": "Orange spice (2)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7487", + "name": "Orange spice (1)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7488", + "name": "Brown spice (4)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7489", + "name": "Brown spice (3)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7490", + "name": "Brown spice (2)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7491", + "name": "Brown spice (1)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7492", + "name": "Yellow spice (4)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7493", + "name": "Yellow spice (3)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7494", + "name": "Yellow spice (2)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7495", + "name": "Yellow spice (1)", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7496", + "name": "Empty spice shaker", + "examine": "Allows for equal distribution of spice.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7497", + "name": "Dirty blast", + "examine": "A cool refreshing fruit mix. With ash in for some reason.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7498", + "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": "7499", + "name": "Evil dave", + "examine": "He seems to like wearing black.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7500", + "name": "Dwarf", + "examine": "A short angry guy.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7502", + "name": "Lumbridge guide", + "examine": "He provides new players with useful information.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7504", + "name": "Osman", + "examine": "He looks a little shifty.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7505", + "name": "Pirate pete", + "examine": "A shifty-looking character.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7506", + "name": "Sir amik varze", + "examine": "Leader of the White Knights.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7507", + "name": "Skrach", + "examine": "He's been frozen in time.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7508", + "name": "Asgoldian ale", + "examine": "There appears to be a coin in the bottom. Liked by dwarves.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "4" + }, + { + "id": "7509", + "name": "Dwarven rock cake", + "examine": "Red hot and glowing, ouch! Only for dwarf consumption.", + "archery_ticket_price": "0", + "durability": null, + "weight": "8.5" + }, + { + "id": "7510", + "name": "Dwarven rock cake", + "examine": "Cool and heavy as a brick. Only for dwarf consumption.", + "archery_ticket_price": "0", + "durability": null, + "weight": "8.5" + }, + { + "id": "7511", + "name": "Slop of compromise", + "examine": "Two out of two goblin generals prefer it!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7512", + "name": "Soggy bread", + "examine": "Previously a nice crispy loaf of bread. Now just kind of icky.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7513", + "name": "Spicy maggots", + "examine": "They clearly taste so much better this way!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7514", + "name": "Dyed orange", + "examine": "Orange slices which have been dyed, but it looks more like they died.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7515", + "name": "Breadcrumbs", + "examine": "Glad these aren't in my bed.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7516", + "name": "Kelp", + "examine": "Those leaves look useful!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7517", + "name": "Ground kelp", + "examine": "Kelp flakes. Smells of the sea.", + "archery_ticket_price": "0", + "durability": null, + "weight": "2" + }, + { + "id": "7518", + "name": "Crab meat", + "examine": "A smelly meat.", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.9" + }, + { + "id": "7519", + "name": "Crab meat", + "examine": "A smelly meat.", + "archery_ticket_price": "0", + "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", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "7522", + "name": "Cooked crab meat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7523", + "name": "Cooked crab meat", + "examine": "Nice and Tasty!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "7524", + "name": "Cooked crab meat", + "examine": "Nice and Tasty!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "7525", + "name": "Cooked crab meat", + "examine": "Nice and Tasty!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "7526", + "name": "Cooked crab meat", + "examine": "Nice and Tasty!", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "7527", + "name": "Ground crab meat", + "examine": "A smelly paste.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7528", + "name": "Ground cod", + "examine": "A smelly paste.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7529", + "name": "Raw fishcake", + "examine": "Would taste nicer if I cooked it.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7530", + "name": "Cooked fishcake", + "examine": "Mmmm, reminds me of the seaside.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7532", + "name": "Mudskipper hide", + "examine": "Hmmm, what can I use this for?", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "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", + "remove_head": "true", + "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", + "durability": null, + "equipment_slot": "0", + "remove_head": "true", + "weight": "0.9" + }, + { + "id": "7540", + "name": "Broken crab claw", + "examine": "Darn, it's useless now.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7541", + "name": "Broken crab shell", + "examine": "Darn, it's useless now.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7542", + "name": "Cake of guidance", + "examine": "Imbued with knowledge itself.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "Maybe Traiborn will have another.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7543", + "name": "Raw guide cake", + "examine": "Now all I need to do is cook it.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "7544", + "name": "Enchanted egg", + "examine": "Egg containing knowledge.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null + }, + { + "id": "7545", + "name": "Enchanted milk", + "examine": "Guiding milk.", + "archery_ticket_price": "0", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "2" + }, + { + "id": "7546", + "name": "Enchanted flour", + "examine": "A pot of special flour.", + "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": "7547", + "name": "Druid pouch", + "examine": "A druid pouch.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7548", + "name": "Potato seed", + "examine": "A potato seed - plant in an allotment.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "8", + "tradeable": "true" + }, + { + "id": "7549", + "name": "Potato seed", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7550", + "name": "Onion seed", + "examine": "An onion seed - plant in an allotment.", + "archery_ticket_price": "0", + "durability": null, + "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.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "10", + "tradeable": "true" + }, + { + "id": "7563", + "name": "Tomato seed", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7564", + "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": "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "7567", + "name": "Raw jubbly", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7568", + "name": "Cooked jubbly", + "examine": "Lovely jubbly!", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "10" + }, + { + "id": "7569", + "name": "Cooked jubbly", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "7571", + "name": "Burnt jubbly", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7572", + "name": "Red banana", + "examine": "Like a banana only redder.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "5" + }, + { + "id": "7573", + "name": "Tchiki monkey nuts", + "examine": "Like monkey nuts only tchikier.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7574", + "name": "Sliced red banana", + "examine": "Perfect for stuffing snakes.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7575", + "name": "Tchiki nut paste", + "examine": "Mixing this with jam would just be wrong.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7576", + "name": "Snake corpse", + "examine": "Like a snake only not alive.", + "archery_ticket_price": "0", + "durability": null, + "weight": "3" + }, + { + "id": "7577", + "name": "Raw stuffed snake", + "examine": "This snake is stuffed right up.", + "archery_ticket_price": "0", + "durability": null, + "weight": "3" + }, + { + "id": "7578", + "name": "Odd stuffed snake", + "examine": "Is this really what you wanted to do?", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7579", + "name": "Stuffed snake", + "examine": "Fit for a Monkey King. (cooked)", + "archery_ticket_price": "0", + "durability": null, + "weight": "3" + }, + { + "id": "7581", + "name": "Overgrown hellcat", + "examine": "Your hellish pet cat!!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7582", + "name": "Hell cat", + "examine": "Your hellish pet cat!!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7583", + "name": "Hell-kitten", + "examine": "Your hellish pet cat!!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7585", + "name": "Wily hellcat", + "examine": "Your hellish pet cat!!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7586", + "name": "Dummy", + "examine": "I can train on this", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7587", + "name": "Coffin", + "examine": "Filled with items. Like a bank, but spookier!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7588", + "name": "Coffin", + "examine": "Filled with items. Like a bank, but spookier!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7589", + "name": "Coffin", + "examine": "Filled with items. Like a bank, but spookier!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7590", + "name": "Coffin", + "examine": "Filled with items. Like a bank, but spookier!", + "archery_ticket_price": "0", + "durability": null + }, + { + "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" + }, + { + "id": "7593", + "name": "Zombie trousers", + "examine": "Good for a shamble about town.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "7" + }, + { + "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", + "remove_head": "true" + }, + { + "id": "7595", + "name": "Zombie gloves", + "examine": "Smells pretty funky.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "9" + }, + { + "id": "7596", + "name": "Zombie boots", + "examine": "Thrilling.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "10", + "shop_price": "160" + }, + { + "id": "7597", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7598", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7599", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7600", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7601", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7602", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7603", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7604", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7605", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7606", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7607", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7608", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7609", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7610", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7611", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7612", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7613", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7614", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7615", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7616", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7617", + "name": "Item", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "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", + "durability": null + }, + { + "id": "7622", + "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": "7624", + "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": "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", + "examine": "A fragment of plaster with some impressions on it.", + "archery_ticket_price": "0", + "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" + }, + { + "id": "7629", + "name": "Dusty scroll", + "examine": "An ancient tattered scroll.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another scroll by exploring near the Inn in Burgh de Rott.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7632", + "name": "Temple library key", + "examine": "A key for the Temple Library. (In Aid of the Myreque)", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another temple key by talking to Drezel.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7633", + "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" + }, + { + "id": "7634", + "name": "Battered tome", + "examine": "An ancient history book.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Crumbling tome by searching the library in the temple on the Salve.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7635", + "name": "Leather book", + "examine": "An ancient leather-bound tome.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can get another Leather book by searching the library in the temple on the Salve.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7636", + "name": "Rod dust", + "examine": "Rod of Ivandis dust.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7637", + "name": "Silvthrill rod", + "examine": "A silvery rod of mithril and silver with sapphire on top.", + "archery_ticket_price": "0", + "durability": null, + "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", + "durability": null, + "equip_audio": "2250", + "equipment_slot": "5" + }, + { + "id": "7677", + "name": "Treasure stone", + "examine": "It knows where the treasure is.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1.8" + }, + { + "id": "7678", + "name": "Prize key", + "examine": "You can use this to open the prize chest!", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "160", + "weight": "1" + }, + { + "id": "7679", + "name": "Pugel", + "examine": "A good tool for bashing someone.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "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, + "tradeable": "true", + "weight": "1" + }, + { + "id": "7682", + "name": "Hoop", + "archery_ticket_price": "0", + "durability": null, + "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", + "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", + "durability": null, + "equipment_slot": "3", + "weapon_interface": "5" + }, + { + "id": "7807", + "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", + "durability": null, + "equip_audio": "2232", + "equipment_slot": "3", + "weapon_interface": "2" + }, + { + "id": "7808", + "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", + "durability": null, + "equipment_slot": "3", + "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", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "two_handed": "true", + "walk_anim": "1205", + "weapon_interface": "14" + }, + { + "id": "7810", + "name": "Jug of vinegar", + "examine": "This wine clearly did not age well.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "weight": "1" + }, + { + "id": "7811", + "name": "Pot of vinegar", + "examine": "Well, this pot is certainly full of vinegar and no mistake.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1.3" + }, + { + "id": "7812", + "name": "Goblin skull", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7813", + "name": "Bone in vinegar", + "examine": "There is a goblin bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7814", + "name": "Goblin skull", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7815", + "name": "Bear ribs", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7816", + "name": "Bone in vinegar", + "examine": "There is a bear bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7817", + "name": "Bear ribs", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7818", + "name": "Ram skull", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7819", + "name": "Bone in vinegar", + "examine": "There is a ram bone in here.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7820", + "name": "Ram skull", + "examine": "This belongs in a museum!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7821", + "name": "Unicorn bone", + "examine": "This needs a good polish.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7822", + "name": "Bone in vinegar", + "examine": "There is a unicorn bone in here.", + "archery_ticket_price": "0", + "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", + "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", + "examine": "A book taken from the desk of Herman Caranos.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "shop_price": "160", + "tradeable": "false", + "weight": "0.09" + }, + { + "id": "7952", + "name": "Picture", + "examine": "Useless without the head.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "3", + "tradeable": "true" + }, + { + "id": "7953", + "name": "Picture", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7955", + "name": "Burnt shrimp", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "7956", + "name": "Casket", + "examine": "I hope there's treasure in it.", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true", + "weight": "5" + }, + { + "id": "7957", + "name": "White apron", + "examine": "A mostly clean apron.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2", + "tradeable": "true", + "weight": "0.45" + }, + { + "id": "7958", + "name": "Mining prop", + "examine": "A prop for holding up a tunnel roof.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "7959", + "name": "Heavy box", + "examine": "A box full of stolen Etceterian items.", + "archery_ticket_price": "0", + "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" + }, + { + "id": "7960", + "name": "Empty box", + "examine": "It says 'To the dungeons' on the side.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7961", + "name": "Burnt diary", + "examine": "A diary with one/two/three/four/five page(s).", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7962", + "name": "Burnt diary", + "examine": "A diary with one/two/three/four/five page(s).", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7963", + "name": "Burnt diary", + "examine": "A diary with one/two/three/four/five page(s).", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7964", + "name": "Burnt diary", + "examine": "A diary with one/two/three/four/five page(s).", + "archery_ticket_price": "0", + "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", + "examine": "A dwarf-made coal engine. It looks very sturdy.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "7969", + "name": "Pulley beam", + "examine": "A beam with a pulley attached.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7970", + "name": "Long pulley beam", + "examine": "A long beam with a pulley attached.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7971", + "name": "Longer pulley beam", + "examine": "A very long beam with a pulley attached.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7972", + "name": "Lift manual", + "examine": "The manual for an AMCE Lift-In-A-Box.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false" + }, + { + "id": "7973", + "name": "Beam", + "examine": "A wooden beam.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7975", + "name": "Crawling hand", + "examine": "I should get it stuffed!", + "archery_ticket_price": "0", + "attack_speed": "4", + "durability": null + }, + { + "id": "7976", + "name": "Cockatrice head", + "examine": "I should get it stuffed!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7977", + "name": "Basilisk head", + "examine": "I should get it stuffed!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7978", + "name": "Kurask head", + "examine": "I should get it stuffed!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7979", + "name": "Abyssal head", + "examine": "I should get it stuffed!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7980", + "name": "Kbd heads", + "examine": "I should get it stuffed!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "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", + "durability": null + }, + { + "id": "7983", + "name": "Cockatrice head", + "examine": "I could mount this on my wall!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7984", + "name": "Basilisk head", + "examine": "I could mount this on my wall!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7985", + "name": "Kurask head", + "examine": "I could mount this on my wall!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7986", + "name": "Abyssal head", + "examine": "I could mount this on my wall!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7987", + "name": "Kbd heads", + "examine": "I could mount these on my wall!", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "tradeable": "false", + "weight": "10" + }, + { + "id": "7988", + "name": "Kq head", + "examine": "I could mount this on my wall!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7989", + "name": "Big bass", + "examine": "Whopper! I should get this stuffed!", + "archery_ticket_price": "0", + "durability": null, + "weight": "6" + }, + { + "id": "7990", + "name": "Big bass", + "examine": "I should mount this on my wall!", + "archery_ticket_price": "0", + "durability": null, + "weight": "6" + }, + { + "id": "7991", + "name": "Big swordfish", + "examine": "Whopper! I should get this stuffed!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7992", + "name": "Big swordfish", + "examine": "I should mount this on my wall!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "7993", + "name": "Big shark", + "examine": "It's a monster! I should get this stuffed!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.7" + }, + { + "id": "7994", + "name": "Big shark", + "examine": "I should mount this on my wall!", + "archery_ticket_price": "0", + "durability": null, + "weight": "0.7" + }, + { + "id": "7995", + "name": "Arthur portrait", + "examine": "A portrait of King Arthur.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7996", + "name": "Elena portrait", + "examine": "A portrait of Elena.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1000", + "weight": "1" + }, + { + "id": "7997", + "name": "Keldagrim portrait", + "examine": "A painting of the staute of King Alvis of Keldagrim", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "7998", + "name": "Misc. portrait", + "examine": "A portrait of Prince Brand and Princess Astrid of Miscellania.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "id": "8000", + "name": "Isafdar painting", + "examine": "The exotic land of the Elves.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2000" + }, + { + "id": "8001", + "name": "Karamja painting", + "examine": "The tropical coast of karamja.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2000" + }, + { + "id": "8002", + "name": "Lumbridge painting", + "examine": "Oxtable's famous painting of the Lumbridge water mill.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2000", + "weight": "1" + }, + { + "id": "8003", + "name": "Morytania painting", + "examine": "A painting of the spooky forests of morytania.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "2000", + "weight": "1" + }, + { + "id": "8007", + "name": "Varrock teleport", + "examine": "A tablet containing a magic spell.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2500", + "tradeable": "true" + }, + { + "id": "8008", + "name": "Lumbridge teleport", + "examine": "A tablet containing a magic spell.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2500", + "tradeable": "true" + }, + { + "id": "8009", + "name": "Falador teleport", + "examine": "A tablet containing a magic spell.", + "archery_ticket_price": "0", + "destroy": "true", + "durability": null, + "ge_buy_limit": "1000", + "shop_price": "2500", + "tradeable": "true" + }, + { + "id": "8010", "name": "Camelot teleport", - "tradeable": "true", - "low_alchemy": "1699", - "high_alchemy": "1700", + "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", - "low_alchemy": "1699", - "high_alchemy": "1700", + "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", - "low_alchemy": "1699", - "high_alchemy": "1700", + "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "2000", - "high_alchemy": "3000", + "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", - "low_alchemy": "4000", - "high_alchemy": "6000", + "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", - "low_alchemy": "6000", - "high_alchemy": "9000", + "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", - "low_alchemy": "8000", - "high_alchemy": "12000", - "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", - "low_alchemy": "10000", - "high_alchemy": "15000", - "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", - "low_alchemy": "20000", - "high_alchemy": "30000", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", + "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", - "low_alchemy": "800", - "tradeable": "false", - "high_alchemy": "1200", - "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", - "low_alchemy": "1600", - "tradeable": "false", - "high_alchemy": "2400", - "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", - "low_alchemy": "2400", - "tradeable": "false", - "high_alchemy": "3600", - "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", - "low_alchemy": "4800", - "tradeable": "false", - "high_alchemy": "7200", - "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", - "low_alchemy": "20000", - "tradeable": "false", - "high_alchemy": "30000", + "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "180", - "high_alchemy": "270", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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}", - "durability": null, - "name": "Bagged oak tree", - "archery_ticket_price": "0", - "id": "8421" + "tradeable": "true" }, { - "durability": null, + "id": "8422", "name": "Bagged oak tree", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8422" + "durability": null, + "tradeable": "true" }, { + "id": "8423", + "name": "Bagged willow tree", + "examine": "You can plant this in your garden.", + "archery_ticket_price": "0", + "durability": null, "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", - "low_alchemy": "6000", - "high_alchemy": "9000", - "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", - "low_alchemy": "8000", - "high_alchemy": "12000", - "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", - "low_alchemy": "20000", - "high_alchemy": "30000", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "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", - "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", - "low_alchemy": "2000", - "high_alchemy": "3000", "archery_ticket_price": "0", - "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", - "low_alchemy": "4000", - "high_alchemy": "6000", "archery_ticket_price": "0", - "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", - "low_alchemy": "6000", - "high_alchemy": "9000", "archery_ticket_price": "0", - "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", - "low_alchemy": "8000", - "high_alchemy": "12000", - "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", - "examine": "You can plant this in your garden.", - "durability": null, - "name": "Fancy hedge", - "low_alchemy": "10000", - "high_alchemy": "15000", - "weight": "10", - "archery_ticket_price": "0", - "id": "8445" - }, - { - "durability": null, - "name": "Fancy hedge", "tradeable": "true", - "archery_ticket_price": "0", - "id": "8446" + "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.", + "archery_ticket_price": "0", + "durability": null, "requirements": "{22,76}", "shop_price": "50000", - "examine": "You can plant this in your garden.", - "durability": null, - "name": "Tall fancy hedge", - "low_alchemy": "20000", - "high_alchemy": "30000", - "archery_ticket_price": "0", - "id": "8447" - }, - { - "durability": null, - "name": "Tall fancy hedge", "tradeable": "true", - "archery_ticket_price": "0", - "id": "8448" + "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.", + "archery_ticket_price": "0", + "durability": null, "requirements": "{22,80}", - "durability": null, + "shop_price": "100000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8450", "name": "Tall box hedge", "archery_ticket_price": "0", - "id": "8449" + "durability": null, + "tradeable": "true" }, { - "durability": null, - "name": "Tall box hedge", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8450" - }, - { - "requirements": "{22,66}", - "durability": null, + "id": "8451", "name": "Bagged rosemary", - "archery_ticket_price": "0", - "id": "8451" - }, - { - "durability": null, - "name": "Bagged rosemary", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8452" - }, - { - "requirements": "{22,71}", - "shop_price": "10000", "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged daffodils", - "tradeable": "true", - "low_alchemy": "4000", - "high_alchemy": "6000", "archery_ticket_price": "0", - "id": "8453" - }, - { "durability": null, - "name": "Bagged daffodils", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8454" - }, - { - "requirements": "{22,76}", - "shop_price": "15000", - "examine": "You can plant this in your garden.", - "durability": null, - "name": "Bagged bluebells", - "tradeable": "true", - "low_alchemy": "6000", - "high_alchemy": "9000", - "archery_ticket_price": "0", - "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": "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 sunflower", - "tradeable": "true", - "low_alchemy": "2000", - "high_alchemy": "3000", - "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": "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 marigolds", - "tradeable": "true", - "low_alchemy": "4000", - "high_alchemy": "6000", "archery_ticket_price": "0", - "id": "8459" - }, - { "durability": null, - "name": "Bagged marigolds", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8460" - }, - { "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 roses", - "tradeable": "true", - "low_alchemy": "6000", - "high_alchemy": "9000", - "weight": "10", "archery_ticket_price": "0", - "id": "8461" + "durability": null, + "requirements": "{22,66}", + "shop_price": "5000", + "tradeable": "true", + "weight": "10" }, { - "durability": null, - "name": "Bagged roses", - "tradeable": "true", + "id": "8458", + "name": "Bagged sunflower", "archery_ticket_price": "0", - "id": "8462" + "durability": null, + "tradeable": "true" }, { - "shop_price": "160", - "examine": "How to build a house.", + "id": "8459", + "name": "Bagged marigolds", + "examine": "You can plant this in your garden.", + "archery_ticket_price": "0", "durability": null, + "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.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{22,76}", + "shop_price": "15000", + "tradeable": "true", + "weight": "10" + }, + { + "id": "8462", + "name": "Bagged roses", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8497", "name": "Crude wooden chair", "archery_ticket_price": "0", - "id": "8496" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Crude wooden chair", - "tradeable": "true", + "id": "8498", + "name": "Wooden chair", + "examine": "A ready-to-assemble wooden chair.", "archery_ticket_price": "0", - "id": "8497" + "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", "archery_ticket_price": "0", - "id": "8498" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "53", - "durability": null, - "name": "Wooden chair", - "tradeable": "true", + "id": "8500", + "name": "Rocking chair", + "examine": "A ready-to-assemble rocking chair.", "archery_ticket_price": "0", - "id": "8499" + "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", "archery_ticket_price": "0", - "id": "8500" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Rocking chair", - "tradeable": "true", + "id": "8502", + "name": "Oak chair", + "examine": "A ready-to-assemble oak chair.", "archery_ticket_price": "0", - "id": "8501" + "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", "archery_ticket_price": "0", - "id": "8502" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, - "name": "Oak chair", - "tradeable": "true", + "id": "8504", + "name": "Oak armchair", + "examine": "A ready-to-assemble oak armchair.", "archery_ticket_price": "0", - "id": "8503" + "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", "archery_ticket_price": "0", - "id": "8504" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Oak armchair", - "tradeable": "true", + "id": "8506", + "name": "Teak armchair", + "examine": "A ready-to-assemble teak armchair.", "archery_ticket_price": "0", - "id": "8505" + "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", "archery_ticket_price": "0", - "id": "8506" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Teak armchair", - "tradeable": "true", + "id": "8508", + "name": "Mahogany armchair", + "examine": "A ready-to-assemble mahogany armchair.", "archery_ticket_price": "0", - "id": "8507" + "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", "archery_ticket_price": "0", - "id": "8508" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8", - "durability": null, - "name": "Mahogany armchair", - "tradeable": "true", + "id": "8510", + "name": "Wooden bookcase", + "examine": "A ready-to-assemble wooden bookcase.", "archery_ticket_price": "0", - "id": "8509" + "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", "archery_ticket_price": "0", - "id": "8510" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Wooden bookcase", - "tradeable": "true", + "id": "8512", + "name": "Oak bookcase", + "examine": "A ready-to-assemble oak bookcase.", "archery_ticket_price": "0", - "id": "8511" + "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", "archery_ticket_price": "0", - "id": "8512" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Oak bookcase", - "tradeable": "true", + "id": "8514", + "name": "Mahogany b'kcase", + "examine": "A ready-to-assemble mahogany bookcase.", "archery_ticket_price": "0", - "id": "8513" + "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", "archery_ticket_price": "0", - "id": "8514" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "46", - "durability": null, - "name": "Mahogany b'kcase", - "tradeable": "true", + "id": "8516", + "name": "Beer barrel", + "examine": "A ready-to-assemble beer barrel.", "archery_ticket_price": "0", - "id": "8515" + "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", "archery_ticket_price": "0", - "id": "8516" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Beer barrel", - "tradeable": "true", + "id": "8518", + "name": "Cider barrel", + "examine": "A ready-to-assemble cider barrel.", "archery_ticket_price": "0", - "id": "8517" + "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", "archery_ticket_price": "0", - "id": "8518" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "16900", "durability": null, - "name": "Cider barrel", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8519" + "ge_buy_limit": "100", + "tradeable": "true" }, { + "id": "8520", + "name": "Asgarnian ale", + "examine": "A ready-to-assemble barrel of Asgarnian Ale.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", "shop_price": "3", - "ge_buy_limit": "100", - "examine": "Probably the finest readily-available ale in Asgarnia.", - "grand_exchange_price": "4586", - "durability": null, + "tradeable": "true", + "weight": "1" + }, + { + "id": "8521", "name": "Asgarnian ale", - "tradeable": "true", - "high_alchemy": "1", "archery_ticket_price": "0", - "id": "8520" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "4586", - "durability": null, - "name": "Asgarnian ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8521" - }, - { - "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of frothy ale.", - "grand_exchange_price": "8875", - "durability": null, + "id": "8522", "name": "Greenman's ale", - "tradeable": "true", - "high_alchemy": "1", + "examine": "A ready-to-assemble barrel of Greenman's Ale.", "archery_ticket_price": "0", - "id": "8522" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8875", "durability": null, - "name": "Greenman's ale", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8523" - }, - { + "ge_buy_limit": "100", "shop_price": "2", - "ge_buy_limit": "100", - "examine": "A glass of bitter.", - "grand_exchange_price": "10300", + "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", - "tradeable": "true", - "high_alchemy": "1", - "weight": "1", + "examine": "A ready-to-assemble barrel of Dragon Bitter.", "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", - "examine": "A fruity, full-bodied ale.", - "grand_exchange_price": "48800", - "durability": null, + "id": "8526", "name": "Chef's delight", - "tradeable": "true", - "high_alchemy": "1", + "examine": "A ready-to-assemble barrel of Chef's Delight.", "archery_ticket_price": "0", - "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.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "8529", "name": "Wood kitchen table", "archery_ticket_price": "0", - "id": "8528" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Wood kitchen table", - "tradeable": "true", + "id": "8530", + "name": "Oak kitchen table", + "examine": "A ready-to-assemble oak kitchen table.", "archery_ticket_price": "0", - "id": "8529" + "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", "archery_ticket_price": "0", - "id": "8530" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Oak kitchen table", - "tradeable": "true", + "id": "8532", + "name": "Teak kitchen table", + "examine": "A ready-to-assemble teak kitchen table.", "archery_ticket_price": "0", - "id": "8531" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, + "id": "8533", "name": "Teak kitchen table", "archery_ticket_price": "0", - "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" + "tradeable": "true" }, { - "durability": null, + "id": "8535", "name": "Oak lectern", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8535" + "durability": null }, { - "durability": null, + "id": "8537", "name": "Eagle lectern", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8537" + "durability": null }, { - "durability": null, + "id": "8539", "name": "Demon lectern", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8539" + "durability": null }, { - "durability": null, + "id": "8541", "name": "Teak eagle lectern", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8541" + "durability": null }, { - "durability": null, + "id": "8543", "name": "Teak demon lectern", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8543" + "durability": null }, { - "durability": null, + "id": "8545", "name": "Mahogany eagle", - "tradeable": "true", "archery_ticket_price": "0", - "id": "8545" + "durability": null }, { - "durability": null, + "id": "8547", "name": "Mahogany demon", - "tradeable": "true", "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.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "8549", "name": "Wood dining table", "archery_ticket_price": "0", - "id": "8548" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Wood dining table", - "tradeable": "true", + "id": "8550", + "name": "Oak dining table", + "examine": "A ready-to-assemble oak dining table.", "archery_ticket_price": "0", - "id": "8549" + "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", "archery_ticket_price": "0", - "id": "8550" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Oak dining table", - "tradeable": "true", + "id": "8552", + "name": "Carved oak table", + "examine": "A ready-to-assemble carved oak dining table.", "archery_ticket_price": "0", - "id": "8551" + "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", "archery_ticket_price": "0", - "id": "8552" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Carved oak table", - "tradeable": "true", + "id": "8554", + "name": "Teak table", + "examine": "A ready-to-assemble teak dining table.", "archery_ticket_price": "0", - "id": "8553" + "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", "archery_ticket_price": "0", - "id": "8554" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, - "name": "Teak table", - "tradeable": "true", + "id": "8556", + "name": "Carved teak table", + "examine": "A ready-to-assemble carved teak dining table.", "archery_ticket_price": "0", - "id": "8555" + "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", "archery_ticket_price": "0", - "id": "8556" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "245", - "durability": null, - "name": "Carved teak table", - "tradeable": "true", + "id": "8558", + "name": "Mahogany table", + "examine": "A ready-to-assemble mahogany dining table.", "archery_ticket_price": "0", - "id": "8557" + "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", "archery_ticket_price": "0", - "id": "8558" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "575", - "durability": null, - "name": "Mahogany table", - "tradeable": "true", + "id": "8560", + "name": "Opulent table", + "examine": "A ready-to-assemble opulent dining table.", "archery_ticket_price": "0", - "id": "8559" + "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", "archery_ticket_price": "0", - "id": "8560" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "379100", - "durability": null, - "name": "Opulent table", - "tradeable": "true", + "id": "8562", + "name": "Wooden bench", + "examine": "A ready-to-assemble wooden dining bench.", "archery_ticket_price": "0", - "id": "8561" + "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", "archery_ticket_price": "0", - "id": "8562" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "15", - "durability": null, - "name": "Wooden bench", - "tradeable": "true", + "id": "8564", + "name": "Oak bench", + "examine": "A ready-to-assemble oak dining bench.", "archery_ticket_price": "0", - "id": "8563" + "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", "archery_ticket_price": "0", - "id": "8564" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "137", - "durability": null, - "name": "Oak bench", - "tradeable": "true", + "id": "8566", + "name": "Carved oak bench", + "examine": "A ready-to-assemble carved oak dining bench.", "archery_ticket_price": "0", - "id": "8565" + "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", "archery_ticket_price": "0", - "id": "8566" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Carved oak bench", - "tradeable": "true", + "id": "8568", + "name": "Teak dining bench", + "examine": "A ready-to-assemble teak dining bench.", "archery_ticket_price": "0", - "id": "8567" + "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", "archery_ticket_price": "0", - "id": "8568" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "18", - "durability": null, - "name": "Teak dining bench", - "tradeable": "true", + "id": "8570", + "name": "Carved teak bench", + "examine": "A ready-to-assemble carved teak dining bench.", "archery_ticket_price": "0", - "id": "8569" + "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", "archery_ticket_price": "0", - "id": "8570" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2", - "durability": null, - "name": "Carved teak bench", - "tradeable": "true", + "id": "8572", + "name": "Mahogany bench", + "examine": "A ready-to-assemble mahogany dining bench.", "archery_ticket_price": "0", - "id": "8571" + "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", "archery_ticket_price": "0", - "id": "8572" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "126", - "durability": null, - "name": "Mahogany bench", - "tradeable": "true", + "id": "8574", + "name": "Gilded bench", + "examine": "A ready-to-assemble gilded mahogany dining bench.", "archery_ticket_price": "0", - "id": "8573" + "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", "archery_ticket_price": "0", - "id": "8574" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "517600", - "durability": null, - "name": "Gilded bench", - "tradeable": "true", + "id": "8576", + "name": "Wooden bed", + "examine": "A ready-to-assemble wooden bed.", "archery_ticket_price": "0", - "id": "8575" + "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", "archery_ticket_price": "0", - "id": "8576" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1974", - "durability": null, - "name": "Wooden bed", - "tradeable": "true", + "id": "8578", + "name": "Oak bed", + "examine": "A ready-to-assemble oak bed.", "archery_ticket_price": "0", - "id": "8577" + "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", "archery_ticket_price": "0", - "id": "8578" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1775", - "durability": null, - "name": "Oak bed", - "tradeable": "true", + "id": "8580", + "name": "Large oak bed", + "examine": "A ready-to-assemble large oak bed.", "archery_ticket_price": "0", - "id": "8579" + "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", "archery_ticket_price": "0", - "id": "8580" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1433", - "durability": null, - "name": "Large oak bed", - "tradeable": "true", + "id": "8582", + "name": "Teak bed", + "examine": "A ready-to-assemble teak bed.", "archery_ticket_price": "0", - "id": "8581" + "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", "archery_ticket_price": "0", - "id": "8582" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1442", - "durability": null, - "name": "Teak bed", - "tradeable": "true", + "id": "8584", + "name": "Large teak bed", + "examine": "A ready-to-assemble large teak bed.", "archery_ticket_price": "0", - "id": "8583" + "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", "archery_ticket_price": "0", - "id": "8584" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2030", - "durability": null, - "name": "Large teak bed", - "tradeable": "true", + "id": "8586", + "name": "4-poster", + "examine": "A ready-to-assemble four-poster bed.", "archery_ticket_price": "0", - "id": "8585" + "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", "archery_ticket_price": "0", - "id": "8586" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1401", - "durability": null, - "name": "4-poster", - "tradeable": "true", + "id": "8588", + "name": "Gilded 4-poster", + "examine": "A ready-to-assemble gilded four-poster bed.", "archery_ticket_price": "0", - "id": "8587" + "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", "archery_ticket_price": "0", - "id": "8588" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "286700", - "durability": null, - "name": "Gilded 4-poster", - "tradeable": "true", + "id": "8590", + "name": "Oak clock", + "examine": "A ready-to-assemble oak clock.", "archery_ticket_price": "0", - "id": "8589" + "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", "archery_ticket_price": "0", - "id": "8590" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "384", - "durability": null, - "name": "Oak clock", - "tradeable": "true", + "id": "8592", + "name": "Teak clock", + "examine": "A ready-to-assemble teak clock.", "archery_ticket_price": "0", - "id": "8591" + "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", "archery_ticket_price": "0", - "id": "8592" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "595", - "durability": null, - "name": "Teak clock", - "tradeable": "true", + "id": "8594", + "name": "Gilded clock", + "examine": "A ready-to-assemble gilded mahogany clock.", "archery_ticket_price": "0", - "id": "8593" + "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", "archery_ticket_price": "0", - "id": "8594" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "156100", - "durability": null, - "name": "Gilded clock", - "tradeable": "true", + "id": "8596", + "name": "Shaving stand", + "examine": "A ready-to-assemble shaving stand.", "archery_ticket_price": "0", - "id": "8595" + "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", "archery_ticket_price": "0", - "id": "8596" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "510", - "durability": null, - "name": "Shaving stand", - "tradeable": "true", + "id": "8598", + "name": "Oak shaving stand", + "examine": "A ready-to-assemble oak shaving stand.", "archery_ticket_price": "0", - "id": "8597" + "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", "archery_ticket_price": "0", - "id": "8598" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "455", - "durability": null, - "name": "Oak shaving stand", - "tradeable": "true", + "id": "8600", + "name": "Oak dresser", + "examine": "A ready-to-assemble oak dresser.", "archery_ticket_price": "0", - "id": "8599" + "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", "archery_ticket_price": "0", - "id": "8600" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "147", - "durability": null, - "name": "Oak dresser", - "tradeable": "true", + "id": "8602", + "name": "Teak dresser", + "examine": "A ready-to-assemble teak dresser.", "archery_ticket_price": "0", - "id": "8601" + "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", "archery_ticket_price": "0", - "id": "8602" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "478", - "durability": null, - "name": "Teak dresser", - "tradeable": "true", + "id": "8604", + "name": "Fancy teak dresser", + "examine": "A ready-to-assemble fancy teak dresser.", "archery_ticket_price": "0", - "id": "8603" + "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", "archery_ticket_price": "0", - "id": "8604" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "455", - "durability": null, - "name": "Fancy teak dresser", - "tradeable": "true", + "id": "8606", + "name": "Mahogany dresser", + "examine": "A ready-to-assemble mahogany dresser.", "archery_ticket_price": "0", - "id": "8605" + "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", "archery_ticket_price": "0", - "id": "8606" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "399", - "durability": null, - "name": "Mahogany dresser", - "tradeable": "true", + "id": "8608", + "name": "Gilded dresser", + "examine": "A ready-to-assemble gilded dresser.", "archery_ticket_price": "0", - "id": "8607" + "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", "archery_ticket_price": "0", - "id": "8608" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "138800", - "durability": null, - "name": "Gilded dresser", - "tradeable": "true", + "id": "8610", + "name": "Shoe box", + "examine": "A ready-to-assemble shoe box.", "archery_ticket_price": "0", - "id": "8609" + "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", "archery_ticket_price": "0", - "id": "8610" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "21", - "durability": null, - "name": "Shoe box", - "tradeable": "true", + "id": "8612", + "name": "Oak drawers", + "examine": "A ready-to-assemble oak chest of drawers.", "archery_ticket_price": "0", - "id": "8611" + "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", "archery_ticket_price": "0", - "id": "8612" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "47", - "durability": null, - "name": "Oak drawers", - "tradeable": "true", + "id": "8614", + "name": "Oak wardrobe", + "examine": "A ready-to-assemble oak bedroom wardrobe.", "archery_ticket_price": "0", - "id": "8613" + "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", "archery_ticket_price": "0", - "id": "8614" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Oak wardrobe", - "tradeable": "true", + "id": "8616", + "name": "Teak drawers", + "examine": "A ready-to-assemble teak chest of drawers.", "archery_ticket_price": "0", - "id": "8615" + "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", "archery_ticket_price": "0", - "id": "8616" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "132", - "durability": null, - "name": "Teak drawers", - "tradeable": "true", + "id": "8618", + "name": "Teak wardrobe", + "examine": "A ready-to-assemble teak bedroom wardrobe.", "archery_ticket_price": "0", - "id": "8617" + "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", "archery_ticket_price": "0", - "id": "8618" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "13", - "durability": null, - "name": "Teak wardrobe", - "tradeable": "true", + "id": "8620", + "name": "Mahogany 'drobe", + "examine": "A ready-to-assemble mahogany bedroom wardrobe.", "archery_ticket_price": "0", - "id": "8619" + "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", "archery_ticket_price": "0", - "id": "8620" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "98", - "durability": null, - "name": "Mahogany 'drobe", - "tradeable": "true", + "id": "8622", + "name": "Gilded wardrobe", + "examine": "A ready-to-assemble gilded mahogany bedroom wardrobe.", "archery_ticket_price": "0", - "id": "8621" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "137700", - "durability": null, + "id": "8623", "name": "Gilded wardrobe", "archery_ticket_price": "0", - "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" + "tradeable": "true" }, { - "durability": null, + "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", - "run_anim": "1427", "archery_ticket_price": "0", - "id": "8680", - "stand_turn_anim": "1426" - }, - { - "remove_head": "true", - "examine": "The colours represent crest.", "durability": null, - "name": "Steel heraldic helm", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8682", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8684", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8686", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8688", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8690", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8692", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8694", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8696", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8698", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8700", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8702", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8704", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8706", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8708", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "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", - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "2.7", - "archery_ticket_price": "0", - "id": "8712", - "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8714", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8716", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8718", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8720", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8722", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8724", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8726", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8728", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8730", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8732", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8734", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8736", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8738", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8740", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8742", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "lendable": "true", - "grand_exchange_price": "32583", - "name": "Rune kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8744", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8746", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8748", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8750", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8752", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8754", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8756", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8758", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8760", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8762", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8764", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8766", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8768", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8770", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8772", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8774", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "shop_price": "850", - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "equipment_slot": "5", - "grand_exchange_price": "346", - "name": "Steel kiteshield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8776", - "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0" - }, - { - "ge_buy_limit": "10000", - "examine": "A plank of sturdy oak.", - "grand_exchange_price": "660", - "durability": null, - "name": "Oak plank", - "tradeable": "true", - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "8778" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "660", - "durability": null, - "name": "Oak plank", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8779" - }, - { - "shop_price": "500", - "ge_buy_limit": "10000", - "examine": "A plank of fine teak.", - "grand_exchange_price": "959", - "durability": null, - "name": "Teak plank", - "tradeable": "true", - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "8780" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "959", - "durability": null, - "name": "Teak plank", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8781" - }, - { - "shop_price": "1500", - "ge_buy_limit": "10000", - "examine": "A plank of expensive mahogany.", - "grand_exchange_price": "2286", - "durability": null, - "name": "Mahogany plank", - "tradeable": "true", - "low_alchemy": "600", - "high_alchemy": "900", - "weight": "0.8", - "archery_ticket_price": "0", - "id": "8782" - }, - { - "ge_buy_limit": "10000", - "grand_exchange_price": "2286", - "durability": null, - "name": "Mahogany plank", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8783" - }, - { - "shop_price": "130000", - "ge_buy_limit": "100", - "examine": "A very delicate sheet of gold.", - "grand_exchange_price": "133800", - "durability": null, - "name": "Gold leaf", - "tradeable": "true", - "low_alchemy": "52000", - "high_alchemy": "78000", - "weight": "1", - "archery_ticket_price": "0", - "id": "8784" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "133800", - "durability": null, - "name": "Gold leaf", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8785" - }, - { - "shop_price": "325000", - "ge_buy_limit": "100", - "examine": "A beautifully carved marble block.", - "grand_exchange_price": "324800", - "durability": null, - "name": "Marble block", - "tradeable": "true", - "low_alchemy": "130000", - "high_alchemy": "195000", - "weight": "13.6", - "archery_ticket_price": "0", - "id": "8786" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "324800", - "durability": null, - "name": "Marble block", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8787" - }, - { - "shop_price": "975000", - "ge_buy_limit": "100", - "examine": "A magic stone to make high-level furniture.", - "grand_exchange_price": "980300", - "durability": null, - "name": "Magic stone", - "tradeable": "true", - "low_alchemy": "390000", - "high_alchemy": "585000", - "weight": "1", - "archery_ticket_price": "0", - "id": "8788" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "980300", - "durability": null, - "name": "Magic stone", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8789" - }, - { - "shop_price": "650", - "ge_buy_limit": "100", - "examine": "A bolt of ordinary cloth.", - "grand_exchange_price": "1090", - "durability": null, - "name": "Bolt of cloth", - "tradeable": "true", - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "1", - "archery_ticket_price": "0", - "id": "8790" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1090", - "durability": null, - "name": "Bolt of cloth", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8791" - }, - { - "ge_buy_limit": "5000", - "examine": "A clockwork mechanism.", - "grand_exchange_price": "1630", - "durability": null, - "name": "Clockwork", - "tradeable": "true", - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "1", - "archery_ticket_price": "0", - "id": "8792" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1630", - "durability": null, - "name": "Clockwork", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8793" - }, - { - "shop_price": "13", - "ge_buy_limit": "100", - "examine": "Good for cutting wood.", - "durability": null, - "low_alchemy": "5", - "high_alchemy": "7", - "weight": "1", "equipment_slot": "3", - "grand_exchange_price": "64", - "name": "Saw", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8794" + "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" }, { + "id": "8680", + "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" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "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", + "remove_head": "true", + "requirements": "{1,5}", + "weight": "2.7" + }, + { + "id": "8710", + "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": "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "8742", + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "8744", + "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", + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,5}", + "shop_price": "850", + "tradeable": "true", + "weight": "5.4" + }, + { + "id": "8774", + "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": "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "8779", + "name": "Oak plank", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "8780", + "name": "Teak plank", + "examine": "A plank of fine teak.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "500", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "8781", + "name": "Teak plank", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "8782", + "name": "Mahogany plank", + "examine": "A plank of expensive mahogany.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "shop_price": "1500", + "tradeable": "true", + "weight": "0.8" + }, + { + "id": "8783", + "name": "Mahogany plank", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10000", + "tradeable": "true" + }, + { + "id": "8784", + "name": "Gold leaf", + "examine": "A very delicate sheet of gold.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "64", + "shop_price": "130000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "8785", + "name": "Gold leaf", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8786", + "name": "Marble block", + "examine": "A beautifully carved marble block.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "325000", + "tradeable": "true", + "weight": "13.6" + }, + { + "id": "8787", + "name": "Marble block", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8788", + "name": "Magic stone", + "examine": "A magic stone to make high-level furniture.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "975000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "8789", + "name": "Magic stone", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "8790", + "name": "Bolt of cloth", + "examine": "A bolt of ordinary cloth.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "shop_price": "650", + "tradeable": "true", + "weight": "1" + }, + { + "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.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "1" + }, + { + "id": "8793", + "name": "Clockwork", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "8794", "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" }, { - "shop_price": "250", - "examine": "Torso armour from the order of the Void Knights.", + "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, - "low_alchemy": "19200", - "high_alchemy": "28800", - "weight": "6.8", "equipment_slot": "4", "remove_sleeves": "true", - "name": "Void knight top", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "250", "tradeable": "false", - "archery_ticket_price": "0", - "id": "8839", - "bonuses": "0,0,0,0,0,45,45,45,45,45,45,0,0,0,0" + "weight": "6.5" }, { - "shop_price": "250", - "examine": "Leg armour of the order of the Void Knights.", - "durability": null, - "name": "Void knight robe", - "tradeable": "false", - "low_alchemy": "18000", - "high_alchemy": "27000", - "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, - "low_alchemy": "4800", - "high_alchemy": "7200", - "weight": "1", - "attack_speed": "5", - "weapon_interface": "1", - "render_anim": "2553", - "equipment_slot": "3", - "name": "Void knight mace", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "8" + }, + { "id": "8841", - "bonuses": "22,0,41,8,0,2,2,2,2,2,0,38,6,0,0" - }, - { - "shop_price": "150", - "examine": "Gloves as used by the order of the Void Knights.", + "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, - "name": "Void knight gloves", + "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", - "low_alchemy": "2420", - "high_alchemy": "3631", - "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" + "weapon_interface": "1", + "weight": "1" }, { - "examine": "A defensive weapon.", - "durability": null, - "name": "Bronze defender", - "weight": "0.4", + "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", + "tradeable": "false", + "weight": "0.5" + }, + { "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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", - "low_alchemy": "800", - "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, - "high_alchemy": "1200", - "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!", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "2", - "equipment_slot": "13", - "grand_exchange_price": "12", - "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" + "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": "100,000 or more: \"X x coins\" where X is the exact number of coins in the stack.", - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "989400", - "name": "Black mask (10)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "8901", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (9)", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (8)", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (7)", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (6)", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (5)", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (4)", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (3)", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (2)", - "tradeable": "true", + "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" - }, - { - "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.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", "equipment_slot": "0", - "grand_exchange_price": "583700", - "name": "Black mask (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8919", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0" - }, - { - "durability": null, - "name": "Black mask (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "8920" - }, - { "requirements": "{1,20}-{2,10}", - "ge_buy_limit": "10", - "examine": "An inert-seeming cave horror mask.", + "tradeable": "true", + "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", + "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0", "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "10", "equipment_slot": "0", - "grand_exchange_price": "956100", - "name": "Black mask", + "requirements": "{1,20}-{2,10}", "tradeable": "true", + "weight": "10" + }, + { + "id": "8920", + "name": "Black mask (1)", "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { "id": "8921", - "bonuses": "0,0,0,-3,-1,9,10,8,-1,9,0,0,0,0,0" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "956100", - "durability": null, "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", - "low_alchemy": "360", - "high_alchemy": "540", + "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 (colour) bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "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 (colour) bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "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 (colour) bandana and a patch for the right eye.", - "durability": null, - "name": "Bandana and eyepatch", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.1", - "archery_ticket_price": "0", "id": "8926", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, "name": "Bandana and eyepatch", - "tradeable": "true", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "72", - "high_alchemy": "108", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "160", - "high_alchemy": "240", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "40", - "high_alchemy": "60", + "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "low_alchemy": "60", - "high_alchemy": "90", + "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", - "low_alchemy": "30", - "high_alchemy": "45", + "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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", - "low_alchemy": "4", - "high_alchemy": "6", "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "8", - "high_alchemy": "12", "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "8", - "high_alchemy": "12", "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" }, { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "Fully charged: This sceptre is fully charged.", - "walk_anim": "1205", - "low_alchemy": "40", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "1000000", - "stand_anim": "813", - "tradeable": "true", - "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", - "requirements": "{0,30}-{6,30}", - "durability": null, - "high_alchemy": "60", - "destroy": "true", - "weight": "2", - "weapon_interface": "3", - "render_anim": "28", - "name": "Pharaoh's sceptre" - }, - { - "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" + "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" }, { - "turn90cw_anim": "1207", - "examine": "Fully charged: This sceptre is fully charged.", - "walk_anim": "1205", - "low_alchemy": "40", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "27", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "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", - "requirements": "{0,30}-{6,30}", - "durability": null, - "high_alchemy": "60", - "destroy": "true", - "weight": "2", - "weapon_interface": "3", - "render_anim": "28", - "name": "Pharaoh's sceptre" - }, - { - "durability": null, "name": "Pharaoh's sceptre", - "tradeable": "true", + "examine": "This sceptre has two charges left.", "archery_ticket_price": "0", - "id": "9047" + "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", + "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" }, { - "turn90cw_anim": "1207", - "examine": "Fully charged: This sceptre is fully charged.", - "walk_anim": "1205", - "low_alchemy": "40", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "27", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", + "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", - "requirements": "{0,30}-{6,30}", - "durability": null, - "high_alchemy": "60", - "destroy": "true", - "weight": "2", - "weapon_interface": "3", - "render_anim": "28", - "name": "Pharaoh's sceptre" - }, - { - "durability": null, "name": "Pharaoh's sceptre", - "tradeable": "true", + "examine": "This sceptre has one charge left.", "archery_ticket_price": "0", - "id": "9049" - }, - { - "ge_buy_limit": "10", - "turn90cw_anim": "1207", - "examine": "Fully charged: This sceptre is fully charged.", - "walk_anim": "1205", - "low_alchemy": "40", - "turn90ccw_anim": "1208", "attack_speed": "5", - "two_handed": "true", - "turn180_anim": "1206", - "equipment_slot": "3", - "grand_exchange_price": "985800", - "stand_anim": "813", - "tradeable": "true", - "run_anim": "1210", - "archery_ticket_price": "0", - "id": "9050", - "stand_turn_anim": "1209", "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "high_alchemy": "60", "destroy": "true", - "weight": "2", - "weapon_interface": "3", + "durability": null, + "equipment_slot": "3", "render_anim": "28", - "name": "Pharaoh's sceptre" + "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" }, { - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "29", - "high_alchemy": "44", + "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "24", - "high_alchemy": "36", + "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "1", - "high_alchemy": "1", - "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, - "low_alchemy": "12", - "high_alchemy": "18", - "destroy": "true", - "equipment_slot": "2", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar amulet", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "9102", - "bonuses": "0,0,0,1,0,0,0,0,1,0,0,0,0,0,0" + "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", - "low_alchemy": "12", - "tradeable": "false", - "high_alchemy": "18", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "equipment_slot": "12", - "destroy_message": "The Oneiromancer might be able to help you get another.", - "name": "Lunar ring", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "9104", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0" + "tradeable": "false" }, { - "requirements": "", - "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.", - "durability": null, - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "41", - "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" + "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.", - "durability": null, - "low_alchemy": "3", - "high_alchemy": "4", - "equipment_slot": "13", - "grand_exchange_price": "90", - "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" + "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "108", - "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" + "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.", - "durability": null, - "low_alchemy": "23", - "high_alchemy": "34", - "equipment_slot": "13", - "grand_exchange_price": "210", - "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" + "tradeable": "true" }, { - "requirements": "{4,61}", - "ge_buy_limit": "10000", - "examine": "Runite crossbow bolts.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "equipment_slot": "13", - "grand_exchange_price": "627", - "name": "Rune bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9144", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,115" + "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", + "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "equipment_slot": "13", - "grand_exchange_price": "8", - "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" + "tradeable": "true" }, { - "ge_buy_limit": "500", - "turn90cw_anim": "821", - "examine": "A bronze crossbow.", - "walk_anim": "4226", - "low_alchemy": "29", - "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, - "high_alchemy": "43", - "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, - "low_alchemy": "37", - "high_alchemy": "55", - "weight": "4", - "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": "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", - "low_alchemy": "62", - "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, - "high_alchemy": "94", - "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", - "low_alchemy": "144", - "turn90ccw_anim": "822", + "attack_anims": "4230,4230,4230,4230", + "attack_audios": "2700,0,0,0", "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": "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, - "high_alchemy": "216", - "weight": "5", - "weapon_interface": "17", + "ge_buy_limit": "500", "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" - }, - { - "requirements": "{4,36}", - "ge_buy_limit": "500", - "turn90cw_anim": "821", - "walk_anim": "4226", - "durability": null, - "turn90ccw_anim": "822", - "weapon_interface": "17", - "turn180_anim": "4227", - "render_anim": "175", - "defence_anim": "424", - "equipment_slot": "3", - "attack_anims": "4230,4230,4230,4230", - "grand_exchange_price": "355", - "stand_anim": "4591", - "attack_audios": "2700,0,0,0", - "name": "Mith crossbow", - "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" - }, - { - "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", - "low_alchemy": "706", - "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, - "high_alchemy": "1060", - "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", - "low_alchemy": "6480", - "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, - "high_alchemy": "9720", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "5", - "high_alchemy": "7", - "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", - "low_alchemy": "6", - "high_alchemy": "10", - "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", - "low_alchemy": "13", - "high_alchemy": "19", - "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", - "low_alchemy": "26", - "high_alchemy": "40", - "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", - "low_alchemy": "53", - "high_alchemy": "79", - "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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)", - "low_alchemy": "5", - "high_alchemy": "7", - "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.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "equipment_slot": "13", - "grand_exchange_price": "36", - "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" + "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "44", - "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" + "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.", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "equipment_slot": "13", - "grand_exchange_price": "95", - "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" + "tradeable": "true" }, { - "requirements": "{4,36}", - "ge_buy_limit": "25000", - "examine": "Enchanted Emerald tipped Mithril Crossbow Bolts.", - "durability": null, - "low_alchemy": "23", - "high_alchemy": "35", - "equipment_slot": "13", - "grand_exchange_price": "304", - "name": "Emerald bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9241", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,85" + "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", + "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.", - "durability": null, - "low_alchemy": "55", - "high_alchemy": "82", - "equipment_slot": "13", - "grand_exchange_price": "450", - "name": "Ruby bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9242", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,103" - }, - { + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,46}", - "ge_buy_limit": "25000", - "examine": "Enchanted Diamond tipped Adamantite Crossbow Bolts.", - "durability": null, - "low_alchemy": "84", - "high_alchemy": "126", - "equipment_slot": "13", - "grand_exchange_price": "1631", - "name": "Diamond bolts (e)", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "9243", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,105" + "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", + "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.", - "durability": null, - "low_alchemy": "425", - "high_alchemy": "637", - "equipment_slot": "13", - "grand_exchange_price": "3217", - "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" - }, - { - "requirements": "{4,61}", - "ge_buy_limit": "25000", - "examine": "Enchanted Onyx tipped runite Crossbow Bolts.", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "equipment_slot": "13", - "grand_exchange_price": "8870", - "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" + "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.", - "durability": null, - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "88", - "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" + "tradeable": "true" }, { - "requirements": "{4,31}", - "ge_buy_limit": "10000", - "examine": "Steel crossbow bolts.", - "durability": null, - "low_alchemy": "3", - "high_alchemy": "4", - "equipment_slot": "13", - "grand_exchange_price": "132", - "name": "Steel bolts (p)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9288", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,64" + "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", + "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "146", - "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" + "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.", - "durability": null, - "low_alchemy": "23", - "high_alchemy": "34", - "equipment_slot": "13", - "grand_exchange_price": "302", - "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" + "tradeable": "true" }, { - "requirements": "{4,61}", - "ge_buy_limit": "10000", - "examine": "Runite crossbow bolts.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "equipment_slot": "13", - "grand_exchange_price": "734", - "name": "Runite bolts (p)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9291", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,115" + "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": "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "equipment_slot": "13", - "grand_exchange_price": "34", - "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" + "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.", - "durability": null, - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "185", - "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" + "tradeable": "true" }, { - "requirements": "{4,31}", - "ge_buy_limit": "10000", - "examine": "Steel crossbow bolts.", - "durability": null, - "low_alchemy": "3", - "high_alchemy": "4", - "equipment_slot": "13", - "grand_exchange_price": "215", - "name": "Steel bolts(p+)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9295", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,64" + "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", + "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "296", - "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" + "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.", "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", "equipment_slot": "13", - "grand_exchange_price": "1130", - "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" + "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "equipment_slot": "13", - "grand_exchange_price": "129", - "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" + "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.", - "durability": null, - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "1136", - "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" + "tradeable": "true" }, { - "requirements": "{4,31}", - "ge_buy_limit": "10000", - "examine": "Steel crossbow bolts.", - "durability": null, - "low_alchemy": "3", - "high_alchemy": "4", - "equipment_slot": "13", - "grand_exchange_price": "1088", - "name": "Steel bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9302", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,64" + "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", + "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "903", - "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" + "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.", "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", "equipment_slot": "13", - "grand_exchange_price": "1581", - "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" + "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.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "equipment_slot": "13", - "grand_exchange_price": "1211", - "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" + "tradeable": "true" }, { - "requirements": "{4,16}", - "examine": "Jade tipped blurite bolts.", - "durability": null, - "name": "Jade bolts", - "low_alchemy": "5", - "high_alchemy": "7", - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "13", - "grand_exchange_price": "60", - "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" + "tradeable": "true" }, { - "requirements": "{4,36}", - "ge_buy_limit": "25000", - "examine": "Sapphire tipped Mithril crossbow bolts.", - "durability": null, - "low_alchemy": "14", - "high_alchemy": "22", - "equipment_slot": "13", - "grand_exchange_price": "89", - "name": "Sapphire bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9337", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,83" - }, - { + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,36}", - "ge_buy_limit": "25000", - "examine": "Emerald tipped Mithril crossbow bolts.", - "durability": null, - "low_alchemy": "21", - "high_alchemy": "31", - "equipment_slot": "13", - "grand_exchange_price": "177", - "name": "Emerald bolts", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "9338", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,85" + "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", + "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.", - "durability": null, - "low_alchemy": "50", - "high_alchemy": "75", - "equipment_slot": "13", - "grand_exchange_price": "408", - "name": "Ruby bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9339", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,103" - }, - { + "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", + "durability": null, + "equipment_slot": "13", + "ge_buy_limit": "25000", "requirements": "{4,46}", - "ge_buy_limit": "25000", - "examine": "Diamond tipped Adamantite crossbow bolts.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "115", - "equipment_slot": "13", - "grand_exchange_price": "1545", - "name": "Diamond bolts", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "true" + }, + { "id": "9340", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,105" + "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", + "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.", - "durability": null, - "low_alchemy": "386", - "high_alchemy": "580", - "equipment_slot": "13", - "grand_exchange_price": "3201", - "name": "Dragon bolts", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9341", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,117" - }, - { - "requirements": "{4,61}", - "ge_buy_limit": "25000", - "examine": "Onyx tipped Runite crossbow bolts.", + "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", "durability": null, - "low_alchemy": "5453", - "high_alchemy": "8179", "equipment_slot": "13", - "grand_exchange_price": "8752", - "name": "Onyx bolts", - "tradeable": "true", - "archery_ticket_price": "0", + "ge_buy_limit": "25000", + "requirements": "{4,61}", + "tradeable": "true" + }, + { "id": "9342", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,120" + "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", + "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", - "low_alchemy": "12", - "high_alchemy": "18", + "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", + "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "1", - "equipment_slot": "5", - "grand_exchange_price": "49", - "name": "Bronze limbs", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9420" + "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", - "low_alchemy": "13", - "high_alchemy": "19", - "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.", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9424", + "name": "Iron limbs", + "archery_ticket_price": "0", + "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, - "low_alchemy": "28", - "high_alchemy": "42", - "weight": "1", "equipment_slot": "5", - "grand_exchange_price": "34", - "name": "Iron limbs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9423" - }, - { "ge_buy_limit": "5000", - "grand_exchange_price": "34", - "durability": null, - "name": "Iron limbs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9424" - }, - { "requirements": "{9,46}", "shop_price": "900", - "ge_buy_limit": "5000", - "examine": "A pair of steel crossbow limbs.", + "tradeable": "true", + "weight": "2" + }, + { + "id": "9426", + "name": "Steel limbs", + "archery_ticket_price": "0", + "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, - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "2", "equipment_slot": "5", - "grand_exchange_price": "97", - "name": "Steel limbs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9425" - }, - { "ge_buy_limit": "5000", - "grand_exchange_price": "97", - "durability": null, - "name": "Steel limbs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9426" - }, - { "requirements": "{9,54}", "shop_price": "650", - "ge_buy_limit": "5000", - "examine": "A pair of mithril crossbow limbs.", + "tradeable": "true", + "weight": "2" + }, + { + "id": "9428", + "name": "Mithril limbs", + "archery_ticket_price": "0", + "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, - "low_alchemy": "260", - "high_alchemy": "390", - "weight": "2", "equipment_slot": "5", - "grand_exchange_price": "347", - "name": "Mithril limbs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9427" - }, - { "ge_buy_limit": "5000", - "grand_exchange_price": "347", - "durability": null, - "name": "Mithril limbs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9428" - }, - { "requirements": "{9,61}", "shop_price": "4800", - "ge_buy_limit": "5000", - "examine": "A pair of adamantite crossbow limbs.", - "durability": null, - "low_alchemy": "640", - "high_alchemy": "960", - "weight": "3", - "equipment_slot": "5", - "grand_exchange_price": "1434", - "name": "Adamantite limbs", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9429" + "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", + "id": "9431", + "name": "Runite limbs", "examine": "A pair of runite crossbow limbs.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "6400", - "high_alchemy": "9600", - "weight": "3", "equipment_slot": "5", - "grand_exchange_price": "9441", - "name": "Runite limbs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9431" - }, - { "ge_buy_limit": "5000", - "grand_exchange_price": "9441", - "durability": null, + "requirements": "{9,69}", + "tradeable": "true", + "weight": "3" + }, + { + "id": "9432", "name": "Runite limbs", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9432" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "destroy_message": "You can reclaim this item from the place you found it.", - "shop_price": "1500", - "examine": "A pouch for storing crossbow bolts.", - "durability": null, + "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "high_alchemy": "1", "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", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "equipment_slot": "3", - "grand_exchange_price": "1", - "name": "Wooden stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9440" + "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", + "id": "9442", + "name": "Oak stock", "examine": "An oak crossbow stock.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "10", - "high_alchemy": "16", - "weight": "3", "equipment_slot": "3", - "grand_exchange_price": "1", - "name": "Oak stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9442" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "1", - "durability": null, - "name": "Oak stock", + "requirements": "{24,9}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9443" + "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.", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9445", + "name": "Willow stock", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "9446", + "name": "Teak stock", + "examine": "A teak crossbow stock.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "21", - "high_alchemy": "31", - "weight": "1", "equipment_slot": "3", - "grand_exchange_price": "3", - "name": "Willow stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9444" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "3", - "durability": null, - "name": "Willow stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9445" - }, - { "requirements": "{9,46}", "shop_price": "231", - "ge_buy_limit": "1000", - "examine": "A teak crossbow stock.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "46", - "equipment_slot": "3", - "grand_exchange_price": "6", - "name": "Teak stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9446" + "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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}", + "id": "9449", + "name": "Maple stock", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "1000", + "tradeable": "true" + }, + { + "id": "9450", + "name": "Mahogany stock", "examine": "A Mahogany crossbow stock.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "53", - "high_alchemy": "79", - "weight": "3", "equipment_slot": "3", - "grand_exchange_price": "23", - "name": "Mahogany stock", + "ge_buy_limit": "1000", + "requirements": "{9,61}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9450" + "weight": "3" }, { - "ge_buy_limit": "1000", - "grand_exchange_price": "23", + "id": "9451", + "name": "Mahogany stock", + "archery_ticket_price": "0", "durability": null, - "name": "Mahogany stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9451" + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "requirements": "{9,69}", - "ge_buy_limit": "1000", + "id": "9452", + "name": "Yew stock", "examine": "A yew crossbow stock.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "66", - "high_alchemy": "100", - "weight": "3", "equipment_slot": "3", - "grand_exchange_price": "37", - "name": "Yew stock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9452" - }, - { "ge_buy_limit": "1000", - "grand_exchange_price": "37", - "durability": null, + "requirements": "{9,69}", + "tradeable": "true", + "weight": "3" + }, + { + "id": "9453", "name": "Yew stock", - "tradeable": "true", "archery_ticket_price": "0", - "id": "9453" + "durability": null, + "ge_buy_limit": "1000", + "tradeable": "true" }, { - "shop_price": "27", - "ge_buy_limit": "500", + "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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)", - "low_alchemy": "24", - "high_alchemy": "36", - "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", - "low_alchemy": "49", - "high_alchemy": "73", - "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", - "low_alchemy": "130", - "high_alchemy": "196", - "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", - "low_alchemy": "300", - "high_alchemy": "450", "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", - "low_alchemy": "693", - "high_alchemy": "1039", - "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", - "low_alchemy": "6466", - "high_alchemy": "9700", "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", - "low_alchemy": "100", - "high_alchemy": "150", + "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", + "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "examine": "A deep tin used to make gnome battas in.", - "durability": null, - "name": "Batta tin", - "low_alchemy": "4", - "high_alchemy": "6", - "weight": "0.1", - "archery_ticket_price": "0", - "id": "9524" - }, - { - "durability": null, - "name": "Batta tin", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9525" + "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.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "true", + "weight": "0.1" + }, + { + "id": "9525", + "name": "Batta tin", + "archery_ticket_price": "0", + "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", "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", - "high_alchemy": "1", + "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", - "high_alchemy": "1", + "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, - "low_alchemy": "220", - "high_alchemy": "330", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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, - "low_alchemy": "200", - "high_alchemy": "300", - "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!", + "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0", "durability": null, - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "0.9", - "equipment_slot": "7", - "grand_exchange_price": "1204", - "name": "Vyrewatch legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9636", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "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!", - "durability": null, - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "0.9", - "equipment_slot": "10", - "grand_exchange_price": "2874", - "name": "Vyrewatch shoes", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9638", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "archery_ticket_price": "0", - "id": "9640", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" + "weight": "0.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "588", - "durability": null, - "name": "Citizen top", - "tradeable": "true", + "id": "9635", + "name": "Vyrewatch top", "archery_ticket_price": "0", - "id": "9641" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", + "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, - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.9", "equipment_slot": "7", - "grand_exchange_price": "491", - "name": "Citizen trousers", + "ge_buy_limit": "100", + "shop_price": "635", "tradeable": "true", - "archery_ticket_price": "0", - "id": "9642", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" + "weight": "0.9" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "491", - "durability": null, - "name": "Citizen trousers", - "tradeable": "true", + "id": "9637", + "name": "Vyrewatch legs", "archery_ticket_price": "0", - "id": "9643" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "shop_price": "6", - "ge_buy_limit": "100", - "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", + "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, - "low_alchemy": "2", - "high_alchemy": "3", - "weight": "0.9", "equipment_slot": "10", - "grand_exchange_price": "458", - "name": "Citizen shoes", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9644", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "458", + "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", + "name": "Citizen top", + "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", + "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": "6", + "tradeable": "true", + "weight": "0.9" + }, + { + "id": "9641", + "name": "Citizen top", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "9642", + "name": "Citizen trousers", + "examine": "Could be used as a disguise in the ghetto of Meiyerditch.", + "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": "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "high_alchemy": "1", - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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, - "low_alchemy": "4800", - "high_alchemy": "7200", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", + "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.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.1", - "equipment_slot": "0", - "grand_exchange_price": "196", - "name": "Elemental helmet", - "tradeable": "true", - "archery_ticket_price": "0", "id": "9729", - "bonuses": "0,0,0,0,0,0,0,0,4,0,0,0,0,0,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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "0.2", - "equipment_slot": "5", - "grand_exchange_price": "8351", - "name": "Mind shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9731", - "bonuses": "0,0,0,0,0,0,0,0,9,0,25,0,0,0,0" - }, - { - "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.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,4,0,0,0,0,0,0", "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "0.1", "equipment_slot": "0", - "grand_exchange_price": "1589", - "name": "Mind helmet", + "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" - }, - { - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "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", - "low_alchemy": "64", - "high_alchemy": "96", "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", - "low_alchemy": "64", - "high_alchemy": "96", "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", - "low_alchemy": "64", - "high_alchemy": "96", "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", - "low_alchemy": "64", - "high_alchemy": "96", "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" }, { - "shop_price": "99000", - "examine": "The cape worn by masters of attack.", - "durability": null, - "name": "Attack cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape only worn by the strongest people.", - "durability": null, - "name": "Strength cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by masters of the art of Defence.", - "durability": null, - "name": "Defence cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master archers.", - "durability": null, - "name": "Ranging cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most pious of heroes.", - "durability": null, - "name": "Prayer cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most powerful mages.", - "durability": null, - "name": "Magic cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master runecrafters.", - "durability": null, - "name": "Runecraft cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by well-constituted adventurers.", - "durability": null, - "name": "Hitpoints cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most agile of heroes.", - "durability": null, - "name": "Agility cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most skilled at the art of herblore.", - "durability": null, - "name": "Herblore cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master thieves.", - "durability": null, - "name": "Thieving cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master craftworkers.", - "durability": null, - "name": "Crafting cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the best of fletchers.", - "durability": null, - "name": "Fletching cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by slayer masters.", - "durability": null, - "name": "Slayer cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master builders.", - "durability": null, - "name": "Construct. cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most skilled miners.", - "durability": null, - "name": "Mining cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master smiths.", - "durability": null, - "name": "Smithing cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the best fishermen.", - "durability": null, - "name": "Fishing cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the world's best chefs.", - "durability": null, - "name": "Cooking cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master firelighters.", - "durability": null, - "name": "Firemaking cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master woodcutters (Obtaining level 99 skill)", - "durability": null, - "name": "Woodcutting cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master farmers.", - "durability": null, - "name": "Farming cape", - "tradeable": "false", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" }, { - "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" }, { - "remove_head": "true", - "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" + "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", + "tradeable": "false", + "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" - }, - { - "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" - }, - { - "examine": "A woolly scarf.", - "durability": null, - "name": "Bobble scarf", - "weight": "0.25", - "archery_ticket_price": "0", - "id": "9816", - "equipment_slot": "2" - }, - { - "grand_exchange_price": "1", - "durability": null, - "name": "Oak cape rack", - "archery_ticket_price": "0", - "id": "9817" - }, - { - "grand_exchange_price": "88", - "durability": null, - "name": "Teak cape rack", - "archery_ticket_price": "0", - "id": "9818" - }, - { - "grand_exchange_price": "120", - "durability": null, - "name": "M'gany cape rack", - "archery_ticket_price": "0", - "id": "9819" - }, - { - "grand_exchange_price": "136025", - "durability": null, - "name": "Gilded cape rack", - "archery_ticket_price": "0", - "id": "9820" - }, - { - "grand_exchange_price": "328598", - "durability": null, - "name": "Marble cape rack", - "archery_ticket_price": "0", - "id": "9821" - }, - { - "grand_exchange_price": "979747", - "durability": null, - "name": "Magical cape rack", - "archery_ticket_price": "0", - "id": "9822" - }, - { - "grand_exchange_price": "59", - "durability": null, - "name": "Oak costume box", - "archery_ticket_price": "0", - "id": "9823" - }, - { - "grand_exchange_price": "122", - "durability": null, - "name": "Teak costume box", - "archery_ticket_price": "0", - "id": "9824" - }, - { - "grand_exchange_price": "59", - "durability": null, - "name": "Mahogany cos box", - "archery_ticket_price": "0", - "id": "9825" - }, - { - "grand_exchange_price": "15", - "durability": null, - "name": "Oak armour case", - "archery_ticket_price": "0", - "id": "9826" - }, - { - "grand_exchange_price": "27", - "durability": null, - "name": "Teak armour case", - "archery_ticket_price": "0", - "id": "9827" - }, - { - "grand_exchange_price": "90", - "durability": null, - "name": "M'gany arm'r case", - "archery_ticket_price": "0", - "id": "9828" - }, - { - "grand_exchange_price": "103", - "durability": null, - "name": "Oak magic wardrobe", - "archery_ticket_price": "0", - "id": "9829" - }, - { - "grand_exchange_price": "1", - "durability": null, - "name": "Carved oak magic wardrobe", - "archery_ticket_price": "0", - "id": "9830" - }, - { - "grand_exchange_price": "127", - "durability": null, - "name": "Teak magic wardrobe", - "archery_ticket_price": "0", - "id": "9831" - }, - { - "grand_exchange_price": "13", - "durability": null, - "name": "Carved teak magic wardrobe", - "archery_ticket_price": "0", - "id": "9832" - }, - { - "grand_exchange_price": "135", - "durability": null, - "name": "Mahogany magic wardrobe", - "archery_ticket_price": "0", - "id": "9833" - }, - { - "grand_exchange_price": "137267", - "durability": null, - "name": "Gilded magic wardrobe", - "archery_ticket_price": "0", - "id": "9834" - }, - { - "grand_exchange_price": "341567", - "durability": null, - "name": "Marble magic wardrobe", - "archery_ticket_price": "0", - "id": "9835" - }, - { - "grand_exchange_price": "21", - "durability": null, - "name": "Oak toy box", - "archery_ticket_price": "0", - "id": "9836" - }, - { - "grand_exchange_price": "151", - "durability": null, - "name": "Teak toy box", - "archery_ticket_price": "0", - "id": "9837" - }, - { - "grand_exchange_price": "20", - "durability": null, - "name": "Mahogany toy box", - "archery_ticket_price": "0", - "id": "9838" - }, - { - "grand_exchange_price": "21", - "durability": null, - "name": "Oak treasure chest", - "archery_ticket_price": "0", - "id": "9839" - }, - { - "grand_exchange_price": "157", - "durability": null, - "name": "Teak treas' chest", - "archery_ticket_price": "0", - "id": "9840" - }, - { - "grand_exchange_price": "39", - "durability": null, - "name": "M'gany treas' chest", - "archery_ticket_price": "0", - "id": "9841" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3", - "durability": null, - "name": "Oak cape rack", - "archery_ticket_price": "0", - "id": "9843" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "92", - "durability": null, - "name": "Teak cape rack", - "archery_ticket_price": "0", - "id": "9844" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "111", - "durability": null, - "name": "M'gany cape rack", - "archery_ticket_price": "0", - "id": "9845" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "130900", - "durability": null, - "name": "Gilded cape rack", - "archery_ticket_price": "0", - "id": "9846" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "329500", - "durability": null, - "name": "Marble cape rack", - "archery_ticket_price": "0", - "id": "9847" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1000000", - "durability": null, - "name": "Magical cape rack", - "archery_ticket_price": "0", - "id": "9848" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "25", - "durability": null, - "name": "Oak toy box", - "archery_ticket_price": "0", - "id": "9849" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "165", - "durability": null, - "name": "Teak toy box", - "archery_ticket_price": "0", - "id": "9850" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "20", - "durability": null, - "name": "Mahogany toy box", - "archery_ticket_price": "0", - "id": "9851" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "111", - "durability": null, - "name": "Oak magic wardrobe", - "archery_ticket_price": "0", - "id": "9852" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1", - "durability": null, - "name": "Carved oak magic wardrobe", - "archery_ticket_price": "0", - "id": "9853" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "133", - "durability": null, - "name": "Teak magic wardrobe", - "archery_ticket_price": "0", - "id": "9854" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "15", - "durability": null, - "name": "Carved teak magic wardrobe", - "archery_ticket_price": "0", - "id": "9855" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "129", - "durability": null, - "name": "Mahogany magic wardrobe", - "archery_ticket_price": "0", - "id": "9856" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "137700", - "durability": null, - "name": "Gilded magic wardrobe", - "archery_ticket_price": "0", - "id": "9857" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "335800", - "durability": null, - "name": "Marble magic wardrobe", - "archery_ticket_price": "0", - "id": "9858" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "17", - "durability": null, - "name": "Oak armour case", - "archery_ticket_price": "0", - "id": "9859" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "29", - "durability": null, - "name": "Teak armour case", - "archery_ticket_price": "0", - "id": "9860" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "88", - "durability": null, - "name": "M'gany arm'r case", - "archery_ticket_price": "0", - "id": "9861" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "24", - "durability": null, - "name": "Oak treasure chest", - "archery_ticket_price": "0", - "id": "9862" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "164", - "durability": null, - "name": "Teak treas' chest", - "archery_ticket_price": "0", - "id": "9863" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "41", - "durability": null, - "name": "M'gany treas' chest", - "archery_ticket_price": "0", - "id": "9864" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "62", - "durability": null, - "name": "Oak costume box", - "archery_ticket_price": "0", - "id": "9865" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "135", - "durability": null, - "name": "Teak costume box", - "archery_ticket_price": "0", - "id": "9866" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "60", - "durability": null, - "name": "Mahogany cos box", - "archery_ticket_price": "0", - "id": "9867" - }, - { - "durability": null, - "name": "Oak cape rack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9868" - }, - { - "durability": null, - "name": "Teak cape rack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9869" - }, - { - "durability": null, - "name": "M'gany cape rack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9870" - }, - { - "durability": null, - "name": "Gilded cape rack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9871" - }, - { - "durability": null, - "name": "Marble cape rack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9872" - }, - { - "durability": null, - "name": "Magical cape rack", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9873" - }, - { - "durability": null, - "name": "Oak toy box", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9874" - }, - { - "durability": null, - "name": "Teak toy box", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9875" - }, - { - "durability": null, - "name": "Mahogany toy box", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9876" - }, - { - "durability": null, - "name": "Oak magic wardrobe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9877" - }, - { - "durability": null, - "name": "Carved oak magic wardrobe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9878" - }, - { - "durability": null, - "name": "Teak magic wardrobe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9879" - }, - { - "durability": null, - "name": "Carved teak magic wardrobe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9880" - }, - { - "durability": null, - "name": "Mahogany magic wardrobe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9881" - }, - { - "durability": null, - "name": "Gilded magic wardrobe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9882" - }, - { - "durability": null, - "name": "Marble magic wardrobe", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9883" - }, - { - "durability": null, - "name": "Oak armour case", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9884" - }, - { - "durability": null, - "name": "Teak armour case", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9885" - }, - { - "durability": null, - "name": "M'gany arm'r case", - "tradeable": "true", + "name": "Quest point hood", + "examine": "Quest skillcape hood.", "archery_ticket_price": "0", - "id": "9886" - }, - { "durability": null, - "name": "Oak treasure chest", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9887" - }, - { - "durability": null, - "name": "Teak treas' chest", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9888" - }, - { - "durability": null, - "name": "M'gany treas' chest", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9889" - }, - { - "durability": null, - "name": "Oak costume box", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9890" - }, - { - "durability": null, - "name": "Teak costume box", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9891" - }, - { - "durability": null, - "name": "Mahogany cos box", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "9892" - }, - { - "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, - "name": "Goutweedy lump", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9901" - }, - { - "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, - "name": "Hardy gout tubers", - "low_alchemy": "2", - "tradeable": "false", - "high_alchemy": "3", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9902" - }, - { - "destroy_message": "My Arm will probably have a spare copy.", - "examine": "Farmer Gricoller's Farming Manual.", - "durability": null, - "name": "Farming manual", - "low_alchemy": "8", - "tradeable": "false", - "high_alchemy": "12", - "destroy": "true", - "archery_ticket_price": "0", - "id": "9903" - }, - { - "examine": "A book for sailors.", - "durability": null, - "name": "Sailing book", - "archery_ticket_price": "0", - "id": "9904" - }, - { - "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", - "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", - "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", - "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", - "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", - "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", - "archery_ticket_price": "0", - "id": "9912", - "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", - "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" - }, - { - "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", - "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", - "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", - "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", - "equipment_slot": "5" - }, - { - "examine": "An angry-looking tree root.", - "durability": null, - "name": "Evil root", - "archery_ticket_price": "0", - "id": "9919" - }, - { + "equipment_slot": "0", "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", - "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": "9921", - "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": "9922", - "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": "9923", - "equipment_slot": "7" - }, - { - "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", - "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.", - "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" - }, - { - "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", - "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" - }, - { "shop_price": "99000", - "examine": "The cape worn by master hunters.", - "durability": null, - "name": "Hunter cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" + "tradeable": "false", + "weight": "0.4" }, { + "id": "9815", + "name": "Bobble hat", + "examine": "A woolly bobble hat.", + "archery_ticket_price": "0", + "destroy": "true", "durability": null, + "equipment_slot": "0", + "tradeable": "false", + "weight": "0.25" + }, + { + "id": "9816", + "name": "Bobble scarf", + "examine": "A woolly scarf.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "2", + "weight": "0.25" + }, + { + "id": "9817", + "name": "Oak cape rack", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9818", + "name": "Teak cape rack", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9819", + "name": "M'gany cape rack", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9820", + "name": "Gilded cape rack", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9821", + "name": "Marble cape rack", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9822", + "name": "Magical cape rack", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9823", + "name": "Oak costume box", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9824", + "name": "Teak costume box", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9825", + "name": "Mahogany cos box", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9826", + "name": "Oak armour case", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9827", + "name": "Teak armour case", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9828", + "name": "M'gany arm'r case", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9829", + "name": "Oak magic wardrobe", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9830", + "name": "Carved oak magic wardrobe", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9831", + "name": "Teak magic wardrobe", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9832", + "name": "Carved teak magic wardrobe", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9833", + "name": "Mahogany magic wardrobe", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9834", + "name": "Gilded magic wardrobe", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9835", + "name": "Marble magic wardrobe", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9836", + "name": "Oak toy box", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9837", + "name": "Teak toy box", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9838", + "name": "Mahogany toy box", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9839", + "name": "Oak treasure chest", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9840", + "name": "Teak treas' chest", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9841", + "name": "M'gany treas' chest", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "9843", + "name": "Oak cape rack", + "examine": "A ready-to-assemble oak cape rack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9844", + "name": "Teak cape rack", + "examine": "A ready-to-assemble teak cape rack..", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9845", + "name": "M'gany cape rack", + "examine": "A ready-to-assemble mahogany cape rack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9846", + "name": "Gilded cape rack", + "examine": "A ready-to-assemble gilded mahogany rack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9847", + "name": "Marble cape rack", + "examine": "A ready-to-assemble marble cape rack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9848", + "name": "Magical cape rack", + "examine": "A ready-to-assemble magic cape rack.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9849", + "name": "Oak toy box", + "examine": "A ready-to-assemble oak toy box.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9850", + "name": "Teak toy box", + "examine": "A ready-to-assemble teak toy box.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9851", + "name": "Mahogany toy box", + "examine": "A ready-to-assemble mahogany toy box.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9852", + "name": "Oak magic wardrobe", + "examine": "A ready-to-assemble oak magic wardrobe.", + "archery_ticket_price": "0", + "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", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9854", + "name": "Teak magic wardrobe", + "examine": "A ready-to-assemble teak magic wardrobe.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9855", + "name": "Carved teak magic wardrobe", + "examine": "A ready-to-assemble carved teak magic wardrobe.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9856", + "name": "Mahogany magic wardrobe", + "examine": "A ready-to-assemble mahogany magic wardrobe.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9857", + "name": "Gilded magic wardrobe", + "examine": "A ready-to-assemble gilded mahogany magic wardrobe.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9858", + "name": "Marble magic wardrobe", + "examine": "A ready-to-assemble marble magic wardrobe.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9859", + "name": "Oak armour case", + "examine": "A ready-to-assemble oak armour case.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9860", + "name": "Teak armour case", + "examine": "A ready-to-assemble teak armour case.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9861", + "name": "M'gany arm'r case", + "examine": "A ready-to-assemble mahogany armour case.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9862", + "name": "Oak treasure chest", + "examine": "A ready-to-assemble oak treasure chest.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9863", + "name": "Teak treas' chest", + "examine": "A ready-to-assemble teak treasure chest.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9864", + "name": "M'gany treas' chest", + "examine": "A ready-to-assemble mahogany treasure chest.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9865", + "name": "Oak costume box", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "weight": "1" + }, + { + "id": "9866", + "name": "Teak costume box", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true", + "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", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9869", + "name": "Teak cape rack", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9870", + "name": "M'gany cape rack", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9871", + "name": "Gilded cape rack", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9872", + "name": "Marble cape rack", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9873", + "name": "Magical cape rack", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9874", + "name": "Oak toy box", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9875", + "name": "Teak toy box", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9876", + "name": "Mahogany toy box", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9877", + "name": "Oak magic wardrobe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9878", + "name": "Carved oak magic wardrobe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9879", + "name": "Teak magic wardrobe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9880", + "name": "Carved teak magic wardrobe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9881", + "name": "Mahogany magic wardrobe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9882", + "name": "Gilded magic wardrobe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9883", + "name": "Marble magic wardrobe", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9884", + "name": "Oak armour case", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9885", + "name": "Teak armour case", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9886", + "name": "M'gany arm'r case", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9887", + "name": "Oak treasure chest", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9888", + "name": "Teak treas' chest", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9889", + "name": "M'gany treas' chest", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9890", + "name": "Oak costume box", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9891", + "name": "Teak costume box", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9892", + "name": "Mahogany cos box", + "archery_ticket_price": "0", + "durability": null, + "tradeable": "true" + }, + { + "id": "9901", + "name": "Goutweedy lump", + "examine": "A lump that at some point might have been a gout tuber.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "9902", + "name": "Hardy gout tubers", + "examine": "A pile of gout tubers suitable for use in mountainous terrain.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "tradeable": "false" + }, + { + "id": "9903", + "name": "Farming manual", + "examine": "Farmer Gricoller's Farming Manual.", + "archery_ticket_price": "0", + "destroy": "true", + "destroy_message": "My Arm will probably have a spare copy.", + "durability": null, + "tradeable": "false" + }, + { + "id": "9904", + "name": "Sailing book", + "examine": "A book for sailors.", + "archery_ticket_price": "0", + "durability": null + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "id": "9911", + "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" + }, + { + "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" + }, + { + "id": "9914", + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "id": "9919", + "name": "Evil root", + "examine": "An angry-looking tree root.", + "archery_ticket_price": "0", + "durability": null + }, + { + "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", + "remove_head": "true", + "tradeable": "false" + }, + { + "id": "9921", + "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": "9922", + "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": "9923", + "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": "9924", + "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": "9925", + "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": "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", + "weight": "0.4" + }, + { + "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}" }, { - "remove_head": "true", - "shop_price": "99000", - "examine": "Hunter skillcape hood.", - "durability": null, - "name": "Hunter hood", - "weight": "0.4", - "archery_ticket_price": "0", "id": "9950", - "equipment_slot": "0" + "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", + "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", - "low_alchemy": "64", - "high_alchemy": "96", - "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", - "low_alchemy": "25", - "high_alchemy": "37", - "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", - "low_alchemy": "7", - "high_alchemy": "10", "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", - "low_alchemy": "25", - "high_alchemy": "37", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "35", - "high_alchemy": "53", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "3", - "high_alchemy": "4", - "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", - "low_alchemy": "6", - "high_alchemy": "9", "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", - "low_alchemy": "6", - "high_alchemy": "9", "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", - "low_alchemy": "6", - "high_alchemy": "9", "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", - "low_alchemy": "6", - "high_alchemy": "9", "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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.", - "durability": null, - "low_alchemy": "12", - "high_alchemy": "18", - "weight": "0.2", - "grand_exchange_price": "308", - "name": "Box trap", "tradeable": "true", - "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": "{15,21}", - "shop_price": "24", - "ge_buy_limit": "100", - "turn90cw_anim": "1207", - "examine": "For catching butterflies...", - "walk_anim": "1205", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "archery_ticket_price": "0", - "id": "10011" - }, - { - "requirements": "{15,21}", - "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", - "id": "10012" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "156", - "durability": null, - "name": "Butterfly jar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10013" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "143", - "durability": null, - "name": "Black warlock", - "archery_ticket_price": "0", - "id": "10014" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "143", - "durability": null, - "name": "Black warlock", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10015" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "227", - "durability": null, - "name": "Snowy knight", - "archery_ticket_price": "0", - "id": "10016" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "227", - "durability": null, - "name": "Snowy knight", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10017" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "180", - "durability": null, - "name": "Sapphire glacialis", - "archery_ticket_price": "0", - "id": "10018" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "180", - "durability": null, - "name": "Sapphire glacialis", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10019" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1270", - "durability": null, - "name": "Ruby harvest", - "archery_ticket_price": "0", - "id": "10020" - }, - { - "ge_buy_limit": "5000", - "grand_exchange_price": "1270", - "durability": null, - "name": "Ruby harvest", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10021" - }, - { - "shop_price": "500", - "examine": "A very large, single leather glove", - "durability": null, - "name": "Falconer's glove", - "tradeable": "false", - "destroy": "true", - "weight": "1.8", + "examine": "For catching butterflies...", "archery_ticket_price": "0", + "attack_anims": "401,401,400,401", "attack_speed": "4", - "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", + "defence_anim": "403", "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", - "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" + "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", + "tradeable": "true", + "weight": "0.02" + }, + { + "id": "10013", + "name": "Butterfly jar", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10014", + "name": "Black warlock", + "examine": "There's a black warlock butterfly in here.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.02" + }, + { + "id": "10015", + "name": "Black warlock", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10016", + "name": "Snowy knight", + "examine": "There's a snowy knight butterfly in here.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.02" + }, + { + "id": "10017", + "name": "Snowy knight", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10018", + "name": "Sapphire glacialis", + "examine": "There's a sapphire glacialis butterfly in here.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.02" + }, + { + "id": "10019", + "name": "Sapphire glacialis", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10020", + "name": "Ruby harvest", + "examine": "There's a ruby harvest butterfly in here.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true", + "weight": "0.02" + }, + { + "id": "10021", + "name": "Ruby harvest", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "10023", + "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", + "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.", - "durability": null, - "low_alchemy": "288", - "high_alchemy": "432", - "weight": "1", - "grand_exchange_price": "784", - "name": "Magic box", "tradeable": "true", - "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", - "low_alchemy": "288", - "high_alchemy": "432", - "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", - "low_alchemy": "288", - "high_alchemy": "432", - "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, - "low_alchemy": "20", - "high_alchemy": "30", - "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.", - "durability": null, - "low_alchemy": "7", - "high_alchemy": "10", - "weight": "0.02", - "grand_exchange_price": "1", - "name": "Rabbit snare", "tradeable": "true", - "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, - "low_alchemy": "64", - "high_alchemy": "96", - "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.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "0.2", - "equipment_slot": "7", - "grand_exchange_price": "449", - "name": "Kyatt legs", - "tradeable": "true", + "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" - }, - { - "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" - }, - { - "requirements": "{21,52}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in polar areas.", + "bonuses": "0,0,0,0,-7,11,10,10,0,10,10,0,0,0,0", "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "272", - "name": "Kyatt top", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "archery_ticket_price": "0", - "id": "10038" - }, - { - "remove_head": "true", - "requirements": "{21,52}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in polar areas.", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8338", - "durability": null, - "name": "Kyatt hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10040" - }, - { - "requirements": "{21,28}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in woodland and jungle areas.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.2", "equipment_slot": "7", - "grand_exchange_price": "2889", - "name": "Larupia legs", + "ge_buy_limit": "100", + "requirements": "{21,52}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10041", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,5,0,0,0,0" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2889", - "durability": null, - "name": "Larupia legs", - "tradeable": "true", + "id": "10036", + "name": "Kyatt legs", "archery_ticket_price": "0", - "id": "10042" + "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.", + "id": "10037", + "name": "Kyatt 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,20,0,0,0,0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "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" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "3232", - "durability": null, - "name": "Larupia top", + "remove_sleeves": "true", + "requirements": "{21,52}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10044" + "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}", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "10040", + "name": "Kyatt hat", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "10041", + "name": "Larupia legs", + "examine": "This should make me harder to spot in woodland and jungle areas.", + "archery_ticket_price": "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", + "requirements": "{21,28}", + "tradeable": "true", + "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, - "low_alchemy": "200", - "high_alchemy": "300", - "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, - "low_alchemy": "60", - "high_alchemy": "90", - "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, - "low_alchemy": "60", - "high_alchemy": "90", - "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, - "low_alchemy": "300", - "high_alchemy": "450", - "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" }, { - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in wooded areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "3527", - "name": "Wood camo top", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10053", - "bonuses": "0,0,0,0,0,10,15,19,0,12,5,0,0,0,0" - }, - { - "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" + "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" }, { - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in wooded areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "7", - "grand_exchange_price": "3228", - "name": "Wood camo legs", - "tradeable": "true", + "id": "10054", + "name": "Wood camo top", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "10055", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "3228", - "durability": null, "name": "Wood camo legs", - "tradeable": "true", + "examine": "These should make me harder to spot in wooded areas.", "archery_ticket_price": "0", - "id": "10056" + "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,4}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in jungle areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "315", - "name": "Jungle camo top", - "tradeable": "true", + "id": "10056", + "name": "Wood camo legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "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", + "examine": "This should make me harder to spot in jungle areas.", "archery_ticket_price": "0", - "id": "10058" - }, - { + "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}", - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in jungle areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "7", - "grand_exchange_price": "462", - "name": "Jungle camo legs", "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", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "462", - "durability": null, "name": "Jungle camo legs", - "tradeable": "true", + "examine": "These should make me harder to spot in jungle areas.", "archery_ticket_price": "0", - "id": "10060" + "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" }, { - "requirements": "{21,10}", - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in desert areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "541", - "name": "Desert camo top", - "tradeable": "true", + "id": "10060", + "name": "Jungle camo legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "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", + "examine": "This should make me harder to spot in desert areas.", "archery_ticket_price": "0", - "id": "10062" - }, - { - "requirements": "{21,10}", - "ge_buy_limit": "100", - "examine": "These should make me harder to spot in desert areas.", + "bonuses": "0,0,0,0,0,10,15,19,0,12,10,0,0,0,0", "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "7", - "grand_exchange_price": "910", - "name": "Desert camo legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10063", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "910", - "durability": null, - "name": "Desert camo legs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10064" - }, - { - "ge_buy_limit": "100", - "examine": "This should make me harder to spot in polar areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "7990", - "name": "Polar camo top", + "requirements": "{21,10}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10065", - "bonuses": "0,0,0,0,0,10,15,19,0,12,5,0,0,0,0" + "weight": "0.2" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "7990", - "durability": null, - "name": "Polar camo top", - "tradeable": "true", + "id": "10062", + "name": "Desert 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", + "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, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", "equipment_slot": "7", - "grand_exchange_price": "8389", - "name": "Polar camo legs", + "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", - "bonuses": "0,0,0,0,-7,11,10,10,0,10,0,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "8389", - "durability": null, "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.", - "durability": null, - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "-2.2", - "equipment_slot": "1", - "grand_exchange_price": "1140", - "name": "Spotted cape", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "-4.5", - "equipment_slot": "1", - "grand_exchange_price": "1262", - "name": "Spottier cape", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10071", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" - }, - { - "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.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", "durability": null, - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "0.4", "equipment_slot": "1", - "grand_exchange_price": "951", + "ge_buy_limit": "100", + "requirements": "{21,40}", + "tradeable": "true", + "weight": "-2.2" + }, + { + "id": "10070", "name": "Spotted cape", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10073", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{21,66}", - "examine": "A really surprisingly aerodynamic cape.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "881", + "id": "10071", "name": "Spottier cape", - "tradeable": "true", + "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", + "ge_buy_limit": "100", + "requirements": "{21,66}", + "tradeable": "true", + "weight": "-4.5" + }, + { + "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", - "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0" + "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" }, { - "requirements": "{21,54}", - "ge_buy_limit": "100", - "examine": "Made from dark kebbit fur, these are perfect for tasks of a stealthier nature.", - "durability": null, - "low_alchemy": "240", - "high_alchemy": "360", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "1063", - "name": "Gloves of silence", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10075", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "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.", - "durability": null, - "low_alchemy": "64", - "high_alchemy": "96", - "weight": "1", - "equipment_slot": "9", - "grand_exchange_price": "80", - "name": "Spiky vambraces", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "weight": "1", - "equipment_slot": "9", - "grand_exchange_price": "1353", - "name": "Green spiky vambs", - "tradeable": "true", + "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "equipment_slot": "9", - "grand_exchange_price": "1773", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1440", - "high_alchemy": "2160", - "weight": "1", - "equipment_slot": "9", - "grand_exchange_price": "1978", - "name": "Red spiky vambs", - "tradeable": "true", + "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" - }, - { - "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" - }, - { - "requirements": "{4,70}", - "ge_buy_limit": "100", - "examine": "Vambraces made from 100% real dragonhide. Now with added spikiness.", + "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,2,0,0,0", "durability": null, - "low_alchemy": "1728", - "high_alchemy": "2592", - "weight": "1", + "equip_audio": "2241", "equipment_slot": "9", - "grand_exchange_price": "2727", - "name": "Black spiky vambs", + "ge_buy_limit": "100", + "requirements": "{4,60}", "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" - }, - { - "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "6", - "high_alchemy": "9", + "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", - "low_alchemy": "5", - "high_alchemy": "7", + "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", - "low_alchemy": "6", - "high_alchemy": "9", + "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", - "low_alchemy": "28", - "high_alchemy": "43", - "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", - "low_alchemy": "38", - "high_alchemy": "57", - "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", - "low_alchemy": "43", - "high_alchemy": "64", - "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", - "low_alchemy": "57", - "high_alchemy": "86", - "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", - "low_alchemy": "57", - "high_alchemy": "86", "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", - "low_alchemy": "76", - "high_alchemy": "115", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "60", - "high_alchemy": "90", "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", - "low_alchemy": "64", - "high_alchemy": "96", "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", - "low_alchemy": "64", - "high_alchemy": "96", "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", - "low_alchemy": "17", - "high_alchemy": "25", "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", - "low_alchemy": "84", - "high_alchemy": "126", - "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", - "low_alchemy": "4", - "high_alchemy": "7", - "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "5", - "high_alchemy": "8", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "60", - "high_alchemy": "90", "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", - "low_alchemy": "108", - "high_alchemy": "162", "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, - "low_alchemy": "14", - "high_alchemy": "21", - "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", - "low_alchemy": "12", - "high_alchemy": "18", + "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", - "low_alchemy": "12", - "high_alchemy": "18", "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", - "low_alchemy": "48", - "high_alchemy": "72", - "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", - "low_alchemy": "48", - "high_alchemy": "72", - "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", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "equipment_slot": "13", - "grand_exchange_price": "28", - "name": "Guam tar", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10142", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16" - }, - { - "requirements": "{0,50}-{4,50}-{6,50}", + "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", + "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", - "low_alchemy": "40", - "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, - "high_alchemy": "60", - "destroy": "true", - "weight": "4", - "weapon_interface": "21", - "render_anim": "1277", - "name": "Orange salamander" - }, - { - "ge_buy_limit": "1000", - "turn90cw_anim": "5245", - "examine": "Slightly slimy but certainly striking.", - "walk_anim": "5245", - "low_alchemy": "60", - "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, - "high_alchemy": "90", - "destroy": "true", - "weight": "4", - "weapon_interface": "21", - "render_anim": "1277", - "name": "Red salamander" - }, - { - "ge_buy_limit": "1000", - "turn90cw_anim": "5245", - "examine": "Slightly slimy and somewhat menacing.", - "walk_anim": "5245", - "low_alchemy": "80", - "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, - "high_alchemy": "120", - "destroy": "true", - "weight": "4", - "weapon_interface": "21", - "render_anim": "1277", - "name": "Black salamander" - }, - { - "ge_buy_limit": "2000", - "turn90cw_anim": "5245", - "examine": "A very slimy and generally disgusting green lizard.", - "walk_anim": "5245", - "low_alchemy": "20", - "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, - "high_alchemy": "30", - "destroy": "true", - "weight": "4", - "weapon_interface": "21", - "render_anim": "1277", - "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, - "low_alchemy": "1", - "high_alchemy": "2", - "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" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "1", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,22", "durability": null, - "name": "Noose wand", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10151" - }, - { - "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A weapon made of bone and wood.", - "walk_anim": "4226", - "low_alchemy": "520", - "turn90ccw_anim": "822", - "attack_speed": "4", - "turn180_anim": "4227", - "equipment_slot": "3", - "grand_exchange_price": "2496", - "stand_anim": "4591", - "tradeable": "true", - "run_anim": "4228", - "archery_ticket_price": "0", - "id": "10156", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,55,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,50}", - "shop_price": "1300", - "durability": null, - "high_alchemy": "780", - "weight": "5", - "weapon_interface": "17", - "render_anim": "175", - "attack_audios": "2700,0,0,0", - "name": "Hunters' crossbow" - }, - { - "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.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "10", "equipment_slot": "13", - "grand_exchange_price": "28", - "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" + "ge_buy_limit": "10000", + "requirements": "{0,50}-{4,50}-{6,50}", + "tradeable": "true" }, { - "requirements": "{4,50}", - "ge_buy_limit": "1000", - "examine": "Bolts made from the spikes of a razor-backed kebbit.", + "id": "10144", + "name": "Tarromin tar", + "examine": "A thick, foul-smelling, tar-like substance with a red tinge.", + "archery_ticket_price": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,31", "durability": null, - "low_alchemy": "12", - "high_alchemy": "18", "equipment_slot": "13", - "grand_exchange_price": "352", - "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" + "ge_buy_limit": "10000", + "requirements": "{0,60}-{4,60}-{6,60}", + "tradeable": "true" }, { + "id": "10145", + "name": "Harralander tar", + "examine": "A dark, thick, foul-smelling, tar-like substance.", + "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": "{0,70}-{4,70}-{6,70}", + "tradeable": "true" + }, + { + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, + "id": "10180", "name": "Clue scroll", - "tradeable": "false", + "examine": "A set of instructions to be followed.", "archery_ticket_price": "0", - "id": "10180" - }, - { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", "durability": null, - "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "10181" - }, - { "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10182" + "tradeable": "false" }, { - "examine": "I hope there's treasure in it.", - "grand_exchange_price": "3726", - "durability": null, + "id": "10181", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10183" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10183", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10185" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10185", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10187" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10187", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10189" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10189", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10191" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10191", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10193" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10193", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10195" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10195", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10197" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10197", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10199" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10199", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10201" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10201", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10203" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10203", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10205" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10205", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10207" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10207", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10209" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10209", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10211" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10211", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10213" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10213", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10215" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10215", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10217" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10217", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10219" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10219", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10221" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10221", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10223" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10223", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10225" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10225", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10227" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10227", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10229" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10229", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10231" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10231", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10233" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10233", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10235" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10235", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10237" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10237", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10239" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10239", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10241" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10241", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10243" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10243", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10245" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10245", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10247" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10247", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10249" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10249", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10251" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10251", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10253" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10253", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10255" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10255", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10257" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10257", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10259" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10259", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10261" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10261", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10263" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10263", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10265" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10265", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10267" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10267", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10269" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10269", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10271" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10271", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10273" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10273", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10275" + "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": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10275", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", + "examine": "I hope there's treasure in it.", "archery_ticket_price": "0", - "id": "10277" + "durability": null, + "tradeable": "true", + "weight": "5" }, { + "id": "10276", + "name": "Clue scroll", + "examine": "A set of instructions to be followed.", + "archery_ticket_price": "0", + "durability": null, "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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": "10277", "name": "Casket", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "120", - "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": "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, - "high_alchemy": "180", - "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", - "low_alchemy": "720", - "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, - "high_alchemy": "1080", - "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", - "low_alchemy": "1000", - "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, - "high_alchemy": "1500", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "422", - "high_alchemy": "633", - "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, - "low_alchemy": "422", - "high_alchemy": "633", - "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, - "low_alchemy": "422", - "high_alchemy": "633", - "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, - "low_alchemy": "422", - "high_alchemy": "633", - "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, - "low_alchemy": "422", - "high_alchemy": "633", - "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" }, { - "ge_buy_limit": "2", - "examine": "See table", + "id": "10315", + "name": "Black helm (h5)", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10316", + "name": "Bob shirt", + "examine": "Bob says: 'Never give your password out to anyone.'", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "2", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "8196", - "name": "Bob shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10316" + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "8196", - "durability": null, + "id": "10317", "name": "Bob shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10317" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "See table", + "id": "10318", + "name": "Bob shirt", + "examine": "Bob says: 'Always check the second trade screen.'", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "2", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "14700", - "name": "Bob shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10318" + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "14700", - "durability": null, + "id": "10319", "name": "Bob shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10319" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "See table", + "id": "10320", + "name": "Bob shirt", + "examine": "Bob says: 'Never trade in the wilderness!'", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "2", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "8403", - "name": "Bob shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10320" + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "8403", - "durability": null, + "id": "10321", "name": "Bob shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10321" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "See table", + "id": "10322", + "name": "Bob shirt", + "examine": "Bob says: 'A bank pin will keep your items secure.'", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "2", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "9230", - "name": "Bob shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10322" + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "9230", - "durability": null, + "id": "10323", "name": "Bob shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10323" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "See table", + "id": "10324", + "name": "Bob shirt", + "examine": "Bob says: 'Keep your computer keylogger free and virus scanned.'", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "2", "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "8197", - "name": "Bob shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10324" - }, - { "ge_buy_limit": "2", - "grand_exchange_price": "8197", - "durability": null, - "name": "Bob shirt", + "remove_sleeves": "true", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10325" + "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", - "low_alchemy": "6", - "high_alchemy": "9", - "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", - "low_alchemy": "6", - "high_alchemy": "9", + "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, - "low_alchemy": "18000", - "high_alchemy": "27000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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, - "low_alchemy": "86000", - "high_alchemy": "129000", - "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, - "low_alchemy": "18000", - "high_alchemy": "27000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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" - }, - { - "requirements": "{1,30}-{6,65}", - "ge_buy_limit": "2", - "examine": "Ancient mage protection enchanted in the Third-Age.", - "durability": null, - "low_alchemy": "8000", - "high_alchemy": "12000", - "equipment_slot": "2", - "lendable": "true", - "grand_exchange_price": "20100000", - "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" - }, - { - "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, - "low_alchemy": "80000", - "high_alchemy": "120000", - "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, - "low_alchemy": "80000", - "high_alchemy": "120000", - "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, - "low_alchemy": "36000", - "high_alchemy": "54000", - "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, - "low_alchemy": "72000", - "high_alchemy": "108000", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "810", - "high_alchemy": "1215", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "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.", - "durability": null, - "low_alchemy": "1600", - "high_alchemy": "2400", - "weight": "1", - "equipment_slot": "9", - "grand_exchange_price": "116200", - "name": "Zamorak bracers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10368", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" - }, - { - "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, - "low_alchemy": "5200", - "high_alchemy": "7800", - "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": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Zamorak blessed dragonhide chaps.", - "durability": null, - "low_alchemy": "2400", - "high_alchemy": "3600", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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,70}-{4,40}", - "ge_buy_limit": "2", - "examine": "Guthix blessed dragonhide vambraces.", - "durability": null, - "low_alchemy": "2400", - "high_alchemy": "3600", - "weight": "1", - "equipment_slot": "9", - "grand_exchange_price": "3181", - "name": "Guthix bracers", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10376", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" - }, - { - "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, - "low_alchemy": "5200", - "high_alchemy": "7800", - "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": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Guthix blessed dragonhide chaps.", - "durability": null, - "low_alchemy": "2400", - "high_alchemy": "3600", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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.", - "durability": null, - "low_alchemy": "1600", - "high_alchemy": "2400", - "weight": "1", - "equipment_slot": "9", - "grand_exchange_price": "70800", - "name": "Saradomin bracers", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10384", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" - }, - { - "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, - "low_alchemy": "5200", - "high_alchemy": "7800", - "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": "{1,40}-{4,70}", - "ge_buy_limit": "2", - "examine": "Saradomin blessed dragonhide chaps.", - "durability": null, - "low_alchemy": "2400", - "high_alchemy": "3600", - "absorb": "0,4,2", - "equipment_slot": "7", - "grand_exchange_price": "36600", - "name": "Saradomin chaps", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10388", - "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0" - }, - { - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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", - "ge_buy_limit": "2", + "id": "10392", + "name": "A powdered wig", "examine": "A big 'do about nothing.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "2", "equipment_slot": "0", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "45400", - "name": "A powdered wig", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10392" + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "45400", + "id": "10393", + "name": "A powdered wig", + "archery_ticket_price": "0", "durability": null, - "name": "A powdered wig", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10393" + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", + "id": "10394", + "name": "Flared trousers", "examine": "These'll help me stay alive.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "7", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "448900", - "name": "Flared trousers", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10394" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "448900", + "id": "10395", + "name": "Flared trousers", + "archery_ticket_price": "0", "durability": null, - "name": "Flared trousers", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10395" + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", + "id": "10396", + "name": "Pantaloons", "examine": "Alas, someone has slashed my pantaloons.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "7", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "9863", - "name": "Pantaloons", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10396" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "9863", + "id": "10397", + "name": "Pantaloons", + "archery_ticket_price": "0", "durability": null, - "name": "Pantaloons", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10397" + "ge_buy_limit": "2", + "tradeable": "true" }, { - "remove_head": "true", - "ge_buy_limit": "2", + "id": "10398", + "name": "Sleeping cap", "examine": "A cap for wearing whil...zzzzzzzzz", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "2", "equipment_slot": "0", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "276400", - "name": "Sleeping cap", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10398" + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "276400", + "id": "10399", + "name": "Sleeping cap", + "archery_ticket_price": "0", "durability": null, - "name": "Sleeping cap", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10399" + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", + "id": "10400", + "name": "Black ele' shirt", "examine": "A well made elegant mans' black shirt.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "921500", - "name": "Black ele' shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10400" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "921500", - "durability": null, + "id": "10401", "name": "Black ele' shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10401" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "A rather elegant black pair of pantaloons.", - "grand_exchange_price": "270700", - "durability": null, - "name": "Black ele' legs", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant black pair of pantaloons.", "archery_ticket_price": "0", - "id": "10403" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "118600", - "name": "Red ele' shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10404" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "118600", - "durability": null, + "id": "10405", "name": "Red ele' shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10405" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "A rather elegant red pair of pantaloons.", - "grand_exchange_price": "39400", - "durability": null, - "name": "Red ele' legs", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant red pair of pantaloons.", "archery_ticket_price": "0", - "id": "10407" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "134700", - "name": "Blue ele' shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10408" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "134700", - "durability": null, + "id": "10409", "name": "Blue ele' shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10409" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "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", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant pair of men's blue pantaloons", "archery_ticket_price": "0", - "id": "10411" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "162600", - "name": "Green ele' shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10412" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "162600", - "durability": null, + "id": "10413", "name": "Green ele' shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10413" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "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", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant green pair of men's pantaloons.", "archery_ticket_price": "0", - "id": "10415" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "116200", - "name": "Purple ele' shirt", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10416" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "116200", - "durability": null, + "id": "10417", "name": "Purple ele' shirt", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10417" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "A rather elegant purple pair of pantaloons.", - "grand_exchange_price": "73400", - "durability": null, - "name": "Purple ele' legs", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant purple pair of pantaloons.", "archery_ticket_price": "0", - "id": "10419" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "571500", - "name": "White ele' blouse", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10420" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "571500", - "durability": null, + "id": "10421", "name": "White ele' blouse", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10421" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "A rather elegant white skirt.", - "grand_exchange_price": "56600", - "durability": null, - "name": "White ele' skirt", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant white skirt.", "archery_ticket_price": "0", - "id": "10423" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "15000", - "name": "Red ele' blouse", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10424" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "15000", - "durability": null, + "id": "10425", "name": "Red ele' blouse", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10425" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "A rather elegant Red skirt.", - "grand_exchange_price": "7132", - "durability": null, - "name": "Red ele' skirt", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant Red skirt.", "archery_ticket_price": "0", - "id": "10427" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "22200", - "name": "Blue ele' blouse", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10428" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "22200", - "durability": null, + "id": "10429", "name": "Blue ele' blouse", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10429" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "A rather elegant blue skirt.", - "grand_exchange_price": "12200", - "durability": null, - "name": "Blue ele' skirt", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant blue skirt.", "archery_ticket_price": "0", - "id": "10431" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "12300", - "name": "Green ele' blouse", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10432" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "12300", - "durability": null, + "id": "10433", "name": "Green ele' blouse", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10433" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "A rather elegant green skirt.", - "grand_exchange_price": "10300", - "durability": null, - "name": "Green ele' skirt", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant green skirt.", "archery_ticket_price": "0", - "id": "10435" + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "2", "remove_sleeves": "true", - "grand_exchange_price": "22800", - "name": "Purple ele' blouse", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10436" + "weight": "1" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "22800", - "durability": null, + "id": "10437", "name": "Purple ele' blouse", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10437" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "A rather elegant purple skirt.", - "grand_exchange_price": "16700", - "durability": null, - "name": "Purple ele' skirt", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", + "examine": "A rather elegant purple skirt.", "archery_ticket_price": "0", - "id": "10439" + "durability": null, + "equipment_slot": "7", + "ge_buy_limit": "2", + "tradeable": "true", + "weight": "1" }, { - "requirements": "{5,60}", - "ge_buy_limit": "2", - "turn90cw_anim": "1207", - "examine": "A Saradomin crozier.", - "walk_anim": "1205", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", + "id": "10439", + "name": "Purple ele' skirt", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "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", + "examine": "A Saradomin crozier.", "archery_ticket_price": "0", - "id": "10441" + "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" }, { - "requirements": "{5,60}", - "ge_buy_limit": "2", - "turn90cw_anim": "1207", - "examine": "A Guthix crozier.", - "walk_anim": "1205", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", + "id": "10441", + "name": "Saradomin crozier", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "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", + "examine": "A Guthix crozier.", "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "2", - "turn90ccw_anim": "1208", + "attack_anims": "419,419,419,419", + "attack_audios": "2555,0,0,0", "attack_speed": "5", - "weapon_interface": "1", - "turn180_anim": "1206", - "render_anim": "28", + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0", + "defence_anim": "420", + "durability": null, "equipment_slot": "3", - "grand_exchange_price": "2794", - "stand_anim": "813", - "name": "Zamorak crozier", - "tradeable": "true", + "ge_buy_limit": "2", + "render_anim": "28", + "requirements": "{5,60}", "run_anim": "1210", - "archery_ticket_price": "0", - "id": "10444", + "stand_anim": "813", "stand_turn_anim": "1209", - "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0" + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "1", + "weight": "2" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "2794", + "id": "10443", + "name": "Guthix crozier", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { + "id": "10444", "name": "Zamorak crozier", - "tradeable": "true", + "examine": "A Zamorak crozier.", "archery_ticket_price": "0", - "id": "10445" + "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" }, { - "requirements": "{5,40}", - "ge_buy_limit": "2", - "examine": "A Saradomin cloak.", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "0.4", - "equipment_slot": "1", - "grand_exchange_price": "1200000", - "name": "Saradomin cloak", - "tradeable": "true", + "id": "10445", + "name": "Zamorak crozier", "archery_ticket_price": "0", + "attack_anims": "", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10446", - "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1200000", - "durability": null, "name": "Saradomin cloak", - "tradeable": "true", + "examine": "A Saradomin cloak.", "archery_ticket_price": "0", - "id": "10447" + "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" }, { - "requirements": "{5,40}", - "ge_buy_limit": "2", - "examine": "A Guthix cloak.", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", - "equipment_slot": "1", - "grand_exchange_price": "391000", - "name": "Guthix cloak", - "tradeable": "true", + "id": "10447", + "name": "Saradomin cloak", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10448", - "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "391000", - "durability": null, "name": "Guthix cloak", - "tradeable": "true", + "examine": "A Guthix cloak.", "archery_ticket_price": "0", - "id": "10449" - }, - { - "requirements": "{5,40}", - "ge_buy_limit": "2", - "examine": "A Zamorak cloak.", + "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0", "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "0.4", "equipment_slot": "1", - "grand_exchange_price": "1000000", - "name": "Zamorak cloak", + "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", - "bonuses": "0,0,0,1,0,3,3,3,3,3,3,0,3,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "1000000", - "durability": null, "name": "Zamorak cloak", - "tradeable": "true", + "examine": "A Zamorak cloak.", "archery_ticket_price": "0", - "id": "10451" + "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" }, { - "remove_head": "true", - "requirements": "{5,40}-{6,40}", - "ge_buy_limit": "2", - "examine": "A Saradomin mitre.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "equipment_slot": "0", - "grand_exchange_price": "299200", - "name": "Saradomin mitre", - "tradeable": "true", + "id": "10451", + "name": "Zamorak cloak", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10452", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,5,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "299200", - "durability": null, "name": "Saradomin mitre", - "tradeable": "true", + "examine": "A Saradomin mitre.", "archery_ticket_price": "0", - "id": "10453" - }, - { + "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}", - "ge_buy_limit": "2", - "examine": "A Guthix mitre.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "equipment_slot": "0", - "grand_exchange_price": "130300", - "name": "Guthix mitre", - "tradeable": "true", + "tradeable": "true" + }, + { + "id": "10453", + "name": "Saradomin mitre", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10454", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,5,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "130300", - "durability": null, "name": "Guthix mitre", - "tradeable": "true", + "examine": "A Guthix mitre.", "archery_ticket_price": "0", - "id": "10455" - }, - { + "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}", - "ge_buy_limit": "2", - "examine": "A Zamorak mitre.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "0.3", - "equipment_slot": "0", - "grand_exchange_price": "346500", - "name": "Zamorak mitre", - "tradeable": "true", + "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}", - "ge_buy_limit": "2", - "examine": "Blessed vestments of Saradomin.", - "durability": null, - "low_alchemy": "2800", - "high_alchemy": "4200", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "121400", - "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" - }, - { - "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, - "low_alchemy": "2800", - "high_alchemy": "4200", - "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, - "low_alchemy": "2800", - "high_alchemy": "4200", - "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.", - "durability": null, - "low_alchemy": "2800", - "high_alchemy": "4200", - "equipment_slot": "7", - "grand_exchange_price": "56300", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "2800", - "high_alchemy": "4200", - "weight": "2", - "equipment_slot": "7", - "grand_exchange_price": "56900", - "name": "Guthix robe legs", - "tradeable": "true", + "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" - }, - { - "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.", + "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0", "durability": null, - "low_alchemy": "2800", - "high_alchemy": "4200", - "weight": "2.7", "equipment_slot": "7", - "grand_exchange_price": "79400", - "name": "Zamorak robe legs", + "ge_buy_limit": "2", + "requirements": "{5,20}", "tradeable": "true", + "weight": "2" + }, + { + "id": "10467", + "name": "Guthix robe legs", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" + }, + { "id": "10468", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,4,0,0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "79400", - "durability": null, "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.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "equipment_slot": "2", - "grand_exchange_price": "14000", - "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" - }, - { - "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.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "equipment_slot": "2", - "grand_exchange_price": "3849", - "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" - }, - { - "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.", + "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,10,0,0", "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", "equipment_slot": "2", - "grand_exchange_price": "13700", - "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" - }, - { "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", - "low_alchemy": "6", - "high_alchemy": "9", + "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", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "6", - "tradeable": "false", - "high_alchemy": "9", - "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, - "low_alchemy": "8", - "high_alchemy": "12", - "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, - "low_alchemy": "307", - "high_alchemy": "461", - "destroy": "true", - "weight": "4.5", - "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" - }, - { - "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 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", + "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", "shop_price": "999", - "examine": "A superior bagged chicken ready to serve you, magnet in claw.", - "durability": null, - "name": "Ava's accumulator", "tradeable": "false", - "destroy": "true", - "weight": "4.5", - "archery_ticket_price": "0", + "weight": "4.5" + }, + { "id": "10499", + "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", - "equipment_slot": "1" + "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", "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": "true", + "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, - "low_alchemy": "7680", - "high_alchemy": "11520", - "weight": "2", - "absorb": "1,0,3", - "equipment_slot": "0", - "name": "Fighter hat", - "archery_ticket_price": "0", + "weight": "2" + }, + { "id": "10548", - "bonuses": "5,5,5,-7,-7,30,32,28,-3,28,7,0,0,0,0" - }, - { - "requirements": "{1,40}", - "shop_price": "275", - "examine": "A Penance Runner hat.", - "durability": null, - "low_alchemy": "7680", - "high_alchemy": "11520", - "weight": "1", + "name": "Fighter hat", + "examine": "A Penance Fighter hat.", "absorb": "1,0,3", - "equipment_slot": "0", - "name": "Runner hat", "archery_ticket_price": "0", - "id": "10549", - "bonuses": "0,0,0,0,0,30,32,27,1,30,7,0,0,0,0" - }, - { + "bonuses": "5,5,5,-7,-7,30,32,28,-3,28,7,0,0,0,0", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", "requirements": "{1,40}", "shop_price": "275", - "examine": "A Penance ranger hat.", - "durability": null, - "low_alchemy": "7680", - "high_alchemy": "11520", - "weight": "2", - "absorb": "0,3,1", - "equipment_slot": "0", - "name": "Ranger hat", - "archery_ticket_price": "0", - "id": "10550", - "bonuses": "-5,-5,-5,-5,7,7,10,11,5,9,7,0,0,0,0" + "weight": "2" }, { - "requirements": "{1,40}", - "shop_price": "375", - "examine": "Penance Fighter torso armour.", + "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", "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "weight": "4", + "equipment_slot": "0", + "requirements": "{1,40}", + "shop_price": "275", + "weight": "1" + }, + { + "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, + "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, - "low_alchemy": "32000", - "high_alchemy": "48000", - "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", - "low_alchemy": "24000", - "high_alchemy": "36000", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", "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", - "low_alchemy": "20", - "tradeable": "false", - "high_alchemy": "30", + "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)", - "low_alchemy": "150000", - "high_alchemy": "150000", - "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, - "low_alchemy": "18400", - "high_alchemy": "27600", - "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, - "low_alchemy": "6000", - "high_alchemy": "9000", - "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, - "low_alchemy": "6000", - "high_alchemy": "9000", - "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, - "low_alchemy": "6000", - "high_alchemy": "9000", - "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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" }, { - "examine": "Mystical robes.", - "durability": null, - "low_alchemy": "56000", - "high_alchemy": "84000", - "weight": "2.2", + "id": "10603", + "name": "Mystic hat", + "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", + "shop_price": "15000", + "tradeable": "true", + "weight": "0.4" + }, + { + "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", - "grand_exchange_price": "3932123", - "name": "Infinity top", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10605", - "bonuses": "0,0,0,22,0,0,0,0,22,0,0,0,0,0,0" + "weight": "2.2" }, { + "id": "10606", + "name": "Splitbark helm", "examine": "A wooden helmet.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "weight": "0.9", "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", - "grand_exchange_price": "5623", - "name": "Splitbark helm", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10606", - "bonuses": "0,0,0,3,-2,10,9,11,3,0,0,0,0,0,0" + "weight": "0.9" }, { - "examine": "They seem to be not quite of this world...", - "durability": null, - "name": "Ghostly boots", - "low_alchemy": "120", - "high_alchemy": "180", - "archery_ticket_price": "0", "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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" }, { - "shop_price": "250", - "examine": "Torso armour from the order of the Void Knights.", - "durability": null, - "low_alchemy": "19200", - "high_alchemy": "28800", - "weight": "6.8", - "equipment_slot": "4", - "remove_sleeves": "true", - "name": "Void knight top", - "tradeable": "false", - "archery_ticket_price": "0", "id": "10611", - "bonuses": "0,0,0,0,0,40,40,40,40,40,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "Black banded leather armour, a rogue's dream!", - "durability": null, - "name": "Rogue mask", - "low_alchemy": "150", - "high_alchemy": "225", - "weight": "0.5", + "name": "Void knight top", + "examine": "Torso armour from the order of the Void Knights.", "archery_ticket_price": "0", - "id": "10612", - "bonuses": "0,0,0,0,0,5,5,5,5,5,0,0,0,0,0", - "remove_beard": "true", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "examine": "Protective headwear made from crabs. Better than it sounds.", + "bonuses": "0,0,0,0,0,40,40,40,40,40,0,0,0,0,0", "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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.", - "durability": null, - "low_alchemy": "24000", - "high_alchemy": "36000", - "weight": "1", - "absorb": "0,3,1", - "remove_beard": "true", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "35640", - "name": "Spined helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10614", - "bonuses": "-6,-6,-6,-6,6,6,6,6,6,0,0,0,0,0,0" - }, - { - "examine": "A ceremonial wooden mask.", - "grand_exchange_price": "1021", - "durability": null, - "name": "Tribal mask", - "tradeable": "true", - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "2", - "archery_ticket_price": "0", - "id": "10615", - "equipment_slot": "0" - }, - { - "examine": "A ceremonial wooden mask.", - "grand_exchange_price": "1021", - "durability": null, - "name": "Tribal mask", - "tradeable": "true", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "weight": "2", - "archery_ticket_price": "0", - "id": "10617", - "equipment_slot": "0" - }, - { - "shop_price": "3840", - "examine": "Provides excellent protection.", - "durability": null, - "low_alchemy": "1536", - "high_alchemy": "2304", - "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.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "weight": "8", - "absorb": "1,0,3", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "5821", - "name": "Initiate hauberk", - "tradeable": "true", - "archery_ticket_price": "0", - "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.", - "durability": null, - "low_alchemy": "4800", - "high_alchemy": "7200", - "weight": "8.6", - "absorb": "2,0,4", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "6960", - "name": "Proselyte hauberk", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10620", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,8,0,0" - }, - { - "remove_sleeves": "true", - "examine": "A thick heavy leather top.", - "durability": null, - "name": "Mourner top", - "low_alchemy": "60", - "high_alchemy": "90", - "archery_ticket_price": "0", - "id": "10621", - "equipment_slot": "4" - }, - { - "examine": "This should make me harder to spot in polar areas.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "216", - "name": "Kyatt top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10622", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0" - }, - { - "examine": "This should make me harder to spot in woodland and jungle areas.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "1793", - "name": "Larupia top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10623", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0" - }, - { - "shop_price": "300", - "examine": "This should make me harder to spot in desert areas.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "246", - "name": "Graahk top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10624", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0" - }, - { - "examine": "This should make me harder to spot in wooded areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "3883", - "name": "Wood camo top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10625", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0" - }, - { - "examine": "This should make me harder to spot in jungle areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "325", - "name": "Jungle camo top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10626", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0" - }, - { - "examine": "This should make me harder to spot in desert areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "606", - "name": "Desert camo top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10627", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0" - }, - { - "examine": "This should make me harder to spot in polar areas.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "weight": "0.2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "5614", - "name": "Polar camo top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10628", - "bonuses": "0,0,0,0,0,10,15,19,0,12,0,0,0,0,0" - }, - { - "examine": "A mime would wear this.", - "durability": null, - "name": "Mime mask", - "archery_ticket_price": "0", - "id": "10629", - "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" - }, - { - "examine": "Aside from the braaaains on the lapel, it's still quite good.", - "durability": null, - "name": "Zombie shirt", - "archery_ticket_price": "0", - "id": "10631", - "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" - }, - { - "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" - }, - { - "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", - "low_alchemy": "28", - "high_alchemy": "42", - "weight": "2", - "archery_ticket_price": "0", - "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", + "requirements": "{0,42}-{1,42}-{2,42}-{3,42}-{4,42}-{5,22}-{6,42}", + "shop_price": "250", "tradeable": "false", - "low_alchemy": "180", - "high_alchemy": "270", - "weight": "1.8", - "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" + "weight": "6.5" }, { - "shop_price": "90000", - "examine": "A cape woven of obsidian plates.", + "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", + "remove_head": "true", + "weight": "0.5" + }, + { + "id": "10613", + "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, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "2.7" + }, + { + "id": "10614", + "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" + }, + { + "id": "10615", + "name": "Tribal mask", + "examine": "A ceremonial wooden mask.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10616", + "name": "Tribal mask", + "examine": "A ceremonial wooden mask.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10617", + "name": "Tribal mask", + "examine": "A ceremonial wooden mask.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10618", + "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, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "3840", + "tradeable": "true", + "weight": "9.9" + }, + { + "id": "10619", + "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, + "equipment_slot": "4", + "remove_sleeves": "true", + "shop_price": "10000", + "tradeable": "true", + "weight": "8" + }, + { + "id": "10620", + "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" + }, + { + "id": "10621", + "name": "Mourner top", + "examine": "A thick heavy leather top.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true" + }, + { + "id": "10622", + "name": "Kyatt 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": "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", + "tradeable": "true", + "weight": "0.2" + }, + { + "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", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" + }, + { + "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", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" + }, + { + "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", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "0.2" + }, + { + "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" + }, + { + "id": "10630", + "name": "Princess blouse", + "examine": "Very posh!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2" + }, + { + "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" + }, + { + "id": "10632", + "name": "Camo top", + "examine": "Examine what?", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2.7" + }, + { + "id": "10633", + "name": "Lederhosen top", + "examine": "A leather strapped top.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "weight": "2.2" + }, + { + "id": "10634", + "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": "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, - "low_alchemy": "24000", - "high_alchemy": "36000", - "weight": "1.8", "equipment_slot": "1", "lendable": "true", - "grand_exchange_price": "38500", - "name": "Obsidian cape", + "shop_price": "90000", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10636", - "bonuses": "0,0,0,0,0,9,9,9,9,9,0,0,0,0,0" + "weight": "1.8" }, { - "examine": "A cape of fire.", - "durability": null, - "name": "Fire cape", - "tradeable": "false", - "low_alchemy": "24000", - "high_alchemy": "36000", - "weight": "1.8", - "archery_ticket_price": "0", "id": "10637", + "name": "Fire cape", + "examine": "A cape of fire.", + "archery_ticket_price": "0", "bonuses": "1,1,1,1,1,11,11,11,11,11,0,4,2,0,0", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "tradeable": "false", + "weight": "1.8" }, { - "shop_price": "50", - "examine": "Ooohhh look at the pretty colours...", - "durability": null, - "name": "Team-1 cape", - "tradeable": "true", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "shop_price": "50", + "tradeable": "true", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by masters of attack.", - "durability": null, - "name": "Attack cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{0,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape only worn by the strongest people.", - "durability": null, - "name": "Strength cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{2,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by masters of the art of Defence.", - "durability": null, - "name": "Defence cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{1,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master archers.", - "durability": null, - "name": "Ranging cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{4,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most pious of heroes.", - "durability": null, - "name": "Prayer cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{5,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most powerful mages.", - "durability": null, - "name": "Magic cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{6,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master runecrafters.", - "durability": null, - "name": "Runecraft cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{20,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master hunters.", - "durability": null, - "name": "Hunter cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{21,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by well-constituted adventurers.", - "durability": null, - "name": "Hitpoints cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{3,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most agile of heroes.", - "durability": null, - "name": "Agility cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{16,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most skilled at the art of herblore.", - "durability": null, - "name": "Herblore cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{15,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master thieves.", - "durability": null, - "name": "Thieving cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{17,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master craftworkers.", - "durability": null, - "name": "Crafting cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{12,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the best of fletchers.", - "durability": null, - "name": "Fletching cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{9,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by slayer masters.", - "durability": null, - "name": "Slayer cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{18,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master builders.", - "durability": null, - "name": "Construct. cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{22,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the most skilled miners.", - "durability": null, - "name": "Mining cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{14,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master smiths.", - "durability": null, - "name": "Smithing cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{13,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the best fishermen.", - "durability": null, - "name": "Fishing cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{10,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by the world's best chefs.", - "durability": null, - "name": "Cooking cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{7,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master firelighters.", - "durability": null, - "name": "Firemaking cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{11,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master woodcutters (Obtaining level 99 skill)", - "durability": null, - "name": "Woodcutting cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{8,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "shop_price": "99000", - "examine": "The cape worn by master farmers.", - "durability": null, - "name": "Farming cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" - }, - { + "durability": null, + "equipment_slot": "1", + "requirements": "{19,99}", "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", + "weight": "0.4" + }, + { "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", - "low_alchemy": "160", - "high_alchemy": "240", - "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", - "low_alchemy": "320", - "high_alchemy": "480", - "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.", - "durability": null, - "low_alchemy": "652", - "high_alchemy": "979", - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "15249", - "name": "Black shield(h1)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10665", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0" + "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", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "16339", - "name": "Adamant shield(h1)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10666", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" + "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", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "35300", - "name": "Rune shield(h1)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10667", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "examine": "A black kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "6530", - "high_alchemy": "9795", - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "9751", - "name": "Black shield(h2)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10668", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0" + "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", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "3353", - "name": "Adamant shield(h2)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10669", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" + "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", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5.4", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "31903", - "name": "Rune shield(h2)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10670", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5.4" }, { - "examine": "A black shield with a heraldic design.", - "durability": null, - "low_alchemy": "652", - "high_alchemy": "979", - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "6791", - "name": "Black shield(h3)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10671", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0" + "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", + "durability": null, + "equipment_slot": "5", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "4" }, { - "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "5566", - "name": "Adamant shield(h3)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10672", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" + "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", + "durability": null, + "equipment_slot": "5", + "ge_buy_limit": "2", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "5" }, { - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "32082", - "name": "Rune shield(h3)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10673", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2245", + "equipment_slot": "5", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "5" }, { - "examine": "A black shield with a heraldic design.", - "durability": null, - "low_alchemy": "652", - "high_alchemy": "979", - "weight": "4", - "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "5159", - "name": "Black shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10674", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,0" - }, - { - "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "3219", - "name": "Adamant shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10675", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" - }, - { - "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "32351", - "name": "Rune shield(h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10676", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" - }, - { + "name": "Black shield(h4)", "examine": "A black shield with a heraldic design.", - "durability": null, - "low_alchemy": "652", - "high_alchemy": "979", - "weight": "4", "absorb": "1,0,2", - "equipment_slot": "5", - "grand_exchange_price": "8451", - "name": "Black shield(h5)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10677", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,0,0,0,0,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": "10675", + "name": "Adamant shield(h4)", "examine": "A shield with a heraldic design.", - "durability": null, - "low_alchemy": "2176", - "high_alchemy": "3264", - "weight": "5", "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "5905", - "name": "Adamant shield(h5)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10678", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,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": "10676", + "name": "Rune shield(h4)", "examine": "A rune kiteshield with a heraldic design.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "weight": "5", "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", - "grand_exchange_price": "35200", - "name": "Rune shield(h5)", + "requirements": "{1,40}", "tradeable": "true", + "weight": "5" + }, + { + "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", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,0,0,0,0,0" + "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" }, { - "examine": "Those studs should provide a bit more protection. Nice trim too!", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", - "absorb": "0,3,1", - "equipment_slot": "4", - "grand_exchange_price": "67430", - "name": "Studded body (g)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10680", - "bonuses": "0,0,0,-4,8,18,25,22,8,25,0,0,0,0,0" - }, - { + "name": "Studded body (g)", "examine": "Those studs should provide a bit more protection. Nice trim too!", - "durability": null, - "low_alchemy": "340", - "high_alchemy": "510", - "weight": "5.4", "absorb": "0,3,1", - "equipment_slot": "4", - "grand_exchange_price": "22400", - "name": "Studded body (t)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10681", - "bonuses": "0,0,0,-4,8,18,25,22,8,25,0,0,0,0,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": "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", - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "1", - "archery_ticket_price": "0", "id": "10686", + "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,0,0,0,0,0", - "equipment_slot": "4" + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" }, { - "remove_sleeves": "true", - "examine": "I can do magic better in this.", - "durability": null, - "name": "Wizard robe (t)", - "tradeable": "true", - "low_alchemy": "6", - "high_alchemy": "9", - "weight": "1", - "archery_ticket_price": "0", "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", - "equipment_slot": "4" - }, - { - "examine": "Enchanted Wizards robes.", "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "weight": "1", - "absorb": "3,1,0", "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "72800", - "name": "Enchanted top", "tradeable": "true", - "archery_ticket_price": "0", + "weight": "1" + }, + { "id": "10688", - "bonuses": "0,0,0,20,0,0,0,0,20,0,0,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" }, { - "examine": "Slightly magical boots.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "1", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "763602", - "name": "Wizard boots", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10689", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "examine": "Black platebody with trim.", - "durability": null, - "low_alchemy": "1536", - "high_alchemy": "2304", - "weight": "9.07", - "absorb": "1,0,2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "90744", - "name": "Black platebody (t)", - "tradeable": "true", + "name": "Wizard boots", + "examine": "Slightly magical boots.", "archery_ticket_price": "0", - "id": "10690", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,0,0,0" - }, - { - "examine": "Black platebody with gold trim.", + "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0", "durability": null, - "low_alchemy": "1536", - "high_alchemy": "2304", - "weight": "9.07", - "absorb": "1,0,2", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "230800", - "name": "Black platebody (g)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10691", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,0,0,0" - }, - { - "lendable": "true", - "examine": "Your money or your life!", - "grand_exchange_price": "92519", - "durability": null, - "name": "Highwayman mask", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "24", - "archery_ticket_price": "0", - "id": "10692", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "Parlez-vous francais? (Do you speak French?)", - "grand_exchange_price": "64066", - "durability": null, - "name": "Blue beret", - "tradeable": "true", - "low_alchemy": "32", - "high_alchemy": "48", - "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", - "low_alchemy": "32", - "high_alchemy": "48", - "archery_ticket_price": "0", - "hat": true, - "id": "10694", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "Parlez-vous francais? (Do you speak French?)", - "grand_exchange_price": "281900", - "durability": null, - "name": "White beret", - "tradeable": "true", - "low_alchemy": "32", - "high_alchemy": "48", - "archery_ticket_price": "0", - "hat": true, - "id": "10695", - "equipment_slot": "0" - }, - { - "examine": "Lightweight boots ideal for rangers.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "0.2", "equipment_slot": "10", "lendable": "true", - "grand_exchange_price": "11781186", - "name": "Ranger boots", "tradeable": "true", + "weight": "1" + }, + { + "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", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "9.07" + }, + { + "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", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "9.07" + }, + { + "id": "10692", + "name": "Highwayman mask", + "examine": "Your money or your life!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" + }, + { + "id": "10693", + "name": "Blue 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": "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", - "bonuses": "0,0,0,-10,8,2,3,4,2,0,0,0,0,0,0" + "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", + "durability": null, + "equipment_slot": "10", + "lendable": "true", + "tradeable": "true", + "weight": "0.2" }, { - "examine": "Adamant platebody with trim.", - "durability": null, - "low_alchemy": "5120", - "high_alchemy": "7680", - "weight": "9.07", - "absorb": "2,0,4", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "79300", - "name": "Adam platebody (t)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "10697", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0" + "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", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "9.07" }, { + "id": "10698", + "name": "Adam platebody (g)", "examine": "Adamant platebody with gold trim.", - "durability": null, - "low_alchemy": "5120", - "high_alchemy": "7680", - "weight": "9.07", "absorb": "2,0,4", + "archery_ticket_price": "0", + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0", + "durability": null, + "equip_audio": "2239", + "equipment_slot": "4", + "ge_buy_limit": "2", + "remove_sleeves": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "9.07" + }, + { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" + }, + { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" + }, + { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,10}", + "tradeable": "true", + "weight": "1.8" + }, + { + "id": "10702", + "name": "Black helm (h4)", + "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": "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true" + }, + { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "3" + }, + { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "remove_head": "true", + "requirements": "{1,40}", + "tradeable": "true", + "weight": "4" + }, + { + "id": "10707", + "name": "Rune helm (h4)", + "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": "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" + }, + { + "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", + "durability": null, + "equip_audio": "2240", + "equipment_slot": "0", + "ge_buy_limit": "2", + "remove_head": "true", + "requirements": "{1,30}", + "tradeable": "true", + "weight": "3" + }, + { + "id": "10711", + "name": "Adamant helm (h3)", + "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": "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", + "name": "Bob shirt", + "examine": "See table", + "archery_ticket_price": "0", + "durability": null, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "207912", - "name": "Adam platebody (g)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10698", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0" + "weight": "2" }, { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "low_alchemy": "422", - "high_alchemy": "633", - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "22022", - "name": "Black helm (h1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10699", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "low_alchemy": "422", - "high_alchemy": "633", - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "11700", - "name": "Black helm (h2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10700", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "low_alchemy": "422", - "high_alchemy": "633", - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "10424", - "name": "Black helm (h3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10701", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "low_alchemy": "422", - "high_alchemy": "633", - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "12717", - "name": "Black helm (h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10702", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A black helmet with a heraldic design.", - "durability": null, - "low_alchemy": "422", - "high_alchemy": "633", - "weight": "1.8", - "equipment_slot": "0", - "grand_exchange_price": "17852", - "name": "Black helm (h5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10703", - "bonuses": "0,0,0,-6,-2,12,13,10,-1,12,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "30298", - "name": "Rune helm (h1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10704", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "weight": "3", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "26127", - "name": "Rune helm (h2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10705", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "weight": "4", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "37333", - "name": "Rune helm (h3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10706", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "weight": "2", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "29600", - "name": "Rune helm (h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10707", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "A rune helmet with a heraldic design.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "weight": "2", - "absorb": "1,0,3", - "equipment_slot": "0", - "grand_exchange_price": "41325", - "name": "Rune helm (h5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10708", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "An adamant helmet with a heraldic design.", - "durability": null, - "low_alchemy": "1408", - "high_alchemy": "2112", - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "28407", - "name": "Adamant helm (h1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10709", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "An adamant helmet with a heraldic design.", - "durability": null, - "low_alchemy": "1408", - "high_alchemy": "2112", - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "9839", - "name": "Adamant helm (h2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10710", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "An adamant helmet with a heraldic design.", - "durability": null, - "low_alchemy": "1408", - "high_alchemy": "2112", - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "8485", - "name": "Adamant helm (h3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10711", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "An Adamant helmet with a heraldic design.", - "durability": null, - "low_alchemy": "1408", - "high_alchemy": "2112", - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "10774", - "name": "Adamant helm (h4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10712", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" - }, - { - "remove_head": "true", - "examine": "An adamant helmet with a heraldic design.", - "durability": null, - "low_alchemy": "1408", - "high_alchemy": "2112", - "weight": "3", - "absorb": "1,0,2", - "equipment_slot": "0", - "grand_exchange_price": "17390", - "name": "Adamant helm (h5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10713", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" - }, - { - "remove_sleeves": "true", - "examine": "See table", - "grand_exchange_price": "8425", - "durability": null, - "name": "Bob shirt", - "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "2", - "archery_ticket_price": "0", - "id": "10714", - "equipment_slot": "4" - }, - { - "remove_sleeves": "true", - "examine": "See table", - "grand_exchange_price": "8425", - "durability": null, - "name": "Bob shirt", - "tradeable": "true", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "810", - "high_alchemy": "1215", - "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", - "low_alchemy": "360", - "high_alchemy": "540", - "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", - "examine": "A big 'do about nothing.", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "2", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "36879", + "id": "10740", "name": "A powdered wig", - "tradeable": "true", + "examine": "A big 'do about nothing.", "archery_ticket_price": "0", - "id": "10740" - }, - { - "lendable": "true", - "examine": "These'll help me stay alive.", - "grand_exchange_price": "559284", "durability": null, - "name": "Flared trousers", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", - "archery_ticket_price": "0", - "id": "10742", - "equipment_slot": "7" - }, - { + "equipment_slot": "0", "lendable": "true", - "examine": "Alas, someone has slashed my pantaloons.", - "grand_exchange_price": "12022", - "durability": null, - "name": "Pantaloons", - "tradeable": "true", - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", - "archery_ticket_price": "0", - "id": "10744", - "equipment_slot": "7" - }, - { "remove_head": "true", - "examine": "A cap for wearing whil...zzzzzzzzz", + "tradeable": "true", + "weight": "2" + }, + { + "id": "10742", + "name": "Flared trousers", + "examine": "These'll help me stay alive.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "2", - "equipment_slot": "0", + "equipment_slot": "7", "lendable": "true", - "grand_exchange_price": "376084", + "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", "name": "Sleeping cap", - "tradeable": "true", + "examine": "A cap for wearing whil...zzzzzzzzz", "archery_ticket_price": "0", - "id": "10746" - }, - { - "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", - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", - "archery_ticket_price": "0", - "id": "10748", - "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", - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", - "archery_ticket_price": "0", - "id": "10750", - "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", - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", - "archery_ticket_price": "0", - "id": "10752", - "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", - "low_alchemy": "800", - "high_alchemy": "1200", - "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", - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "1", - "archery_ticket_price": "0", - "id": "10756", - "equipment_slot": "4" - }, - { - "examine": "Stylish!", - "grand_exchange_price": "56600", - "durability": null, - "name": "Red boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "10758", - "equipment_slot": "0" - }, - { - "examine": "Stylish!", - "grand_exchange_price": "20300", - "durability": null, - "name": "Orange boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "10760", - "equipment_slot": "0" - }, - { - "examine": "Stylish!", - "grand_exchange_price": "59940", - "durability": null, - "name": "Green boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "10762", - "equipment_slot": "0" - }, - { - "examine": "Stylish!", - "grand_exchange_price": "54236", - "durability": null, - "name": "Blue boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "10764", - "equipment_slot": "0" - }, - { - "examine": "Stylish!", - "grand_exchange_price": "56495", - "durability": null, - "name": "Black boater", - "tradeable": "true", - "low_alchemy": "90", - "high_alchemy": "135", - "archery_ticket_price": "0", - "id": "10766", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "A minimalist's hat.", - "grand_exchange_price": "114319", - "durability": null, - "name": "Red headband", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "24", - "archery_ticket_price": "0", - "id": "10768", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "A minimalist's hat.", - "grand_exchange_price": "41778", - "durability": null, - "name": "Black headband", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "24", - "archery_ticket_price": "0", - "id": "10770", - "equipment_slot": "0" - }, - { - "lendable": "true", - "examine": "A minimalist's hat.", - "grand_exchange_price": "21764", - "durability": null, - "name": "Brown headband", - "tradeable": "true", - "low_alchemy": "16", - "high_alchemy": "24", - "archery_ticket_price": "0", - "id": "10772", - "equipment_slot": "0" - }, - { - "examine": "Shiver me timbers!", - "grand_exchange_price": "83200", - "durability": null, - "name": "Pirate's hat", - "tradeable": "true", - "low_alchemy": "72", - "high_alchemy": "108", - "archery_ticket_price": "0", - "id": "10774", - "equipment_slot": "0" - }, - { - "examine": "Rune platebody in the colours of Zamorak.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "weight": "9.07", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "1097689", - "name": "Saradomin plate", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "weight": "9.07", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "241600", - "name": "Guthix platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10780", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,1" - }, - { - "examine": "Rune platebody with complete gold trim & plating.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "weight": "10", - "absorb": "3,0,6", - "equipment_slot": "4", - "lendable": "true", - "remove_sleeves": "true", - "grand_exchange_price": "1653821", - "name": "Gilded platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10782", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" - }, - { - "remove_sleeves": "true", - "examine": "Blessed vestments of Saradomin.", - "grand_exchange_price": "106389", - "durability": null, - "name": "Saradomin robe top", - "tradeable": "true", - "low_alchemy": "2800", - "high_alchemy": "4200", - "archery_ticket_price": "0", - "id": "10784", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,4,0,0", - "equipment_slot": "4" - }, - { - "examine": "Zamorak Vestments.", - "durability": null, - "low_alchemy": "2800", - "high_alchemy": "4200", - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "151000", - "name": "Zamorak robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10786", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,4,0,0" - }, - { - "examine": "Blessed vestments of Guthix.", - "durability": null, - "low_alchemy": "2800", - "high_alchemy": "4200", - "weight": "1", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "80181", - "name": "Guthix robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10788", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,4,0,0" - }, - { - "examine": "Zamorak blessed dragonhide body armour.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "6", - "absorb": "0,6,3", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "84966", - "name": "Zamorak d'hide", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10790", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0" - }, - { - "shop_price": "13000", - "examine": "Saradomin blessed dragonhide body armour.", - "durability": null, - "low_alchemy": "5200", - "high_alchemy": "7800", - "destroy": "true", - "weight": "6", - "absorb": "0,6,3", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "92762", - "name": "Saradomin d'hide", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "5200", - "high_alchemy": "7800", - "weight": "6", - "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" - }, - { - "examine": "Endorsed by Robin Hood.", - "durability": null, - "low_alchemy": "180", - "high_alchemy": "270", - "weight": "0.2", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "3070557", - "name": "Robin hood hat", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10796", - "bonuses": "0,0,0,-10,8,4,6,8,4,4,0,0,0,0,0" + "weight": "2" }, { - "examine": "Rune platebody with gold trim.", + "id": "10748", + "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, - "low_alchemy": "26000", - "high_alchemy": "39000", - "weight": "9.07", + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "10750", + "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" + }, + { + "id": "10752", + "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" + }, + { + "id": "10754", + "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" + }, + { + "id": "10756", + "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" + }, + { + "id": "10758", + "name": "Red boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" + }, + { + "id": "10760", + "name": "Orange boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" + }, + { + "id": "10762", + "name": "Green boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" + }, + { + "id": "10764", + "name": "Blue boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" + }, + { + "id": "10766", + "name": "Black boater", + "examine": "Stylish!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" + }, + { + "id": "10768", + "name": "Red headband", + "examine": "A minimalist's hat.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" + }, + { + "id": "10770", + "name": "Black headband", + "examine": "A minimalist's hat.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" + }, + { + "id": "10772", + "name": "Brown headband", + "examine": "A minimalist's hat.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "tradeable": "true" + }, + { + "id": "10774", + "name": "Pirate's hat", + "examine": "Shiver me timbers!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "tradeable": "true" + }, + { + "id": "10776", + "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": "231400", - "name": "Rune platebody (g)", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10798", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" + "weight": "9" }, { - "examine": "Rune platebody with trim.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "weight": "9.07", + "id": "10778", + "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": "61100", - "name": "Rune platebody (t)", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10800", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" + "weight": "9.07" }, { + "id": "10780", + "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", - "examine": "All for one and one for all!", - "grand_exchange_price": "56677", - "durability": null, - "name": "Tan cavalier", - "tradeable": "true", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "14", - "high_alchemy": "21", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "low_alchemy": "48", - "high_alchemy": "72", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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" - }, - { "remove_sleeves": "true", - "requirements": "{1,20}", - "ge_buy_limit": "100", - "grand_exchange_price": "122", - "durability": null, - "name": "Yak-hide armour", - "archery_ticket_price": "0", - "id": "10822", - "bonuses": "0,0,0,-5,0,25,20,15,-2,25,5,0,0,0,0", - "equipment_slot": "4" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "122", - "durability": null, - "name": "Yak-hide armour", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10823" + "weight": "9.07" }, { - "requirements": "{1,20}", - "ge_buy_limit": "100", - "grand_exchange_price": "83", - "durability": null, - "name": "Yak-hide armour", + "id": "10782", + "name": "Gilded platebody", + "examine": "Rune platebody with complete gold trim & plating.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "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", + "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0", "durability": null, - "name": "Yak-hide armour", + "equip_audio": "2239", + "equipment_slot": "4", + "lendable": "true", + "remove_sleeves": "true", + "requirements": "{1,40}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "10825" + "weight": "10" }, { - "requirements": "{1,25}", - "ge_buy_limit": "100", - "examine": "A shield worn by Fremennik warriors.", + "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", "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{5,20}", + "tradeable": "true" + }, + { + "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", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{5,20}", + "tradeable": "true", + "weight": "1" + }, + { + "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", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{5,20}", + "tradeable": "true", + "weight": "1" + }, + { + "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", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "requirements": "{1,40}-{4,70}", + "tradeable": "true", + "weight": "6" + }, + { + "id": "10792", + "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,0,0,0,0,0", "destroy": "true", - "weight": "2.2", - "equipment_slot": "5", - "grand_exchange_price": "402", - "name": "Fremennik round shield", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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" }, { - "examine": "A silly hat with bells.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "weight": "2.7", - "equipment_slot": "0", - "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", - "name": "Silly jester hat", - "tradeable": "false", - "archery_ticket_price": "0", "id": "10836", - "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0" + "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", + "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" }, { - "examine": "A jester's jangly top.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "weight": "6.8", - "equipment_slot": "4", - "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", - "remove_sleeves": "true", - "name": "Silly jester top", - "tradeable": "false", - "archery_ticket_price": "0", "id": "10837", - "bonuses": "0,0,0,0,0,0,0,0,5,-5,5,0,0,0,0" + "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", + "destroy": "true", + "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", + "durability": null, + "equipment_slot": "4", + "remove_sleeves": "true", + "tradeable": "false", + "weight": "6.8" }, { - "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", + "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", - "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" + "destroy_message": "Are you sure? You can find another in King Sorvott's chest.", + "durability": null, + "equipment_slot": "7", + "tradeable": "false", + "weight": "0.9" }, { - "durability": null, - "name": "A jester stick", + "id": "10839", + "name": "Silly jester boots", + "examine": "Silly jester boots.", "archery_ticket_price": "0", - "id": "10840", "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" }, { - "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, + "id": "10842", "name": "Decapitated head", - "weight": "3", + "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", - "id": "10842" + "destroy_message": "You can reclaim this item from the place you found it.", + "durability": null, + "weight": "3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "44800", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, + "id": "10843", "name": "Helm of neitiznot", - "tradeable": "true", + "examine": "Swap this note at any bank for the equivalent item.", "archery_ticket_price": "0", - "id": "10843" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "examine": "A slightly sq'irky, I mean quirky, fruit.", - "durability": null, + "id": "10844", "name": "Spring sq'irk", + "examine": "A slightly sq'irky, I mean quirky, fruit.", "archery_ticket_price": "0", - "id": "10844" + "durability": null }, { - "examine": "A slightly sq'irky, I mean quirky, fruit.", - "durability": null, + "id": "10845", "name": "Summer sq'irk", + "examine": "A slightly sq'irky, I mean quirky, fruit.", "archery_ticket_price": "0", - "id": "10845" + "durability": null }, { - "examine": "A slightly sq'irky, I mean quirky, fruit.", - "durability": null, + "id": "10846", "name": "Autumn sq'irk", - "archery_ticket_price": "0", - "id": "10846" - }, - { "examine": "A slightly sq'irky, I mean quirky, fruit.", - "durability": null, + "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", - "id": "10847" + "durability": null }, { - "examine": "Made from 4 spring sq'irks.", - "durability": null, + "id": "10848", "name": "Spring sq'irkjuice", - "weight": "1", + "examine": "Made from 4 spring sq'irks.", "archery_ticket_price": "0", - "id": "10848" + "durability": null, + "weight": "1" }, { - "examine": "Made from 2 summer sq'irks.", - "durability": null, + "id": "10849", "name": "Summer sq'irkjuice", - "weight": "1", + "examine": "Made from 2 summer sq'irks.", "archery_ticket_price": "0", - "id": "10849" + "durability": null, + "weight": "1" }, { - "examine": "Made from 3 autumn sq'irks.", - "durability": null, + "id": "10850", "name": "Autumn sq'irkjuice", - "weight": "1", + "examine": "Made from 3 autumn sq'irks.", "archery_ticket_price": "0", - "id": "10850" + "durability": null, + "weight": "1" }, { - "examine": "Made from 5 winter sq'irks.", - "durability": null, + "id": "10851", "name": "Winter sq'irkjuice", + "examine": "Made from 5 winter sq'irks.", "archery_ticket_price": "0", - "id": "10851" + "durability": null }, { - "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, + "id": "10856", "name": "Sin seer's note", - "low_alchemy": "2", - "tradeable": "false", - "high_alchemy": "3", - "destroy": "true", + "examine": "Her writing is too scrawled to make out what it says.", "archery_ticket_price": "0", - "id": "10856" + "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" }, { - "destroy_message": "You will need to speak to General Khazard, near the Fremennik Province, to get another.", - "examine": "A severed human leg.", - "durability": null, + "id": "10857", "name": "Severed leg", - "tradeable": "false", - "destroy": "true", - "weight": "12", + "examine": "A severed human leg.", "archery_ticket_price": "0", - "id": "10857" + "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" }, { - "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", + "name": "Shadow sword", + "examine": "A shadow sword.", "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", + "attack_speed": "7", + "bonuses": "-4,27,21,4,0,0,0,0,4,-1,0,26,0,0,0", "destroy": "true", - "weight": "2", - "archery_ticket_price": "0", - "id": "10862", - "equipment_slot": "0" - }, - { - "examine": "Very fetching.", + "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, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "weight": "0.9", - "equipment_slot": "4", - "destroy_message": "You'll have to find another within the Tower of Life!", - "remove_sleeves": "true", - "name": "Builder's shirt", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "10863" - }, - { - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "92000", - "turn90ccw_anim": "5867", - "attack_speed": "6", - "two_handed": "true", - "turn180_anim": "5867", - "defence_anim": "5866", + "equip_audio": "2248", "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", - "run_anim": "5868", + "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, - "high_alchemy": "138000", - "weight": "30", - "weapon_interface": "8", - "render_anim": "985", - "name": "Barrelchest anchor" + "weight": "30" }, { - "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", - "low_alchemy": "92000", - "high_alchemy": "138000", - "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, + "id": "10889", "name": "Blessed lamp", + "examine": "This will answer my prayers.", "archery_ticket_price": "0", - "id": "10889" + "destroy_message": "If you drop this lamp it will be destroyed, and you will need to see Brother Tranquility for another.", + "durability": null }, { - "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, + "id": "10890", "name": "Prayer book", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "Used to cure poison through prayer.", "archery_ticket_price": "0", - "id": "10890" + "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" }, { - "ge_buy_limit": "100", + "id": "10891", + "name": "Wooden cat", "examine": "A wooden cat toy.", - "grand_exchange_price": "922", - "durability": null, - "name": "Wooden cat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10891" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "922", - "durability": null, - "name": "Wooden cat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10892" + "tradeable": "true" }, { - "examine": "A cranial clamp.", + "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", - "id": "10893" + "durability": null }, { - "examine": "Some tongs for use with brains.", - "durability": null, + "id": "10894", "name": "Brain tongs", - "tradeable": "false", - "destroy": "true", + "examine": "Some tongs for use with brains.", "archery_ticket_price": "0", - "id": "10894" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A bell jar for covering things.", - "durability": null, + "id": "10895", "name": "Bell jar", - "weight": "1", + "examine": "A bell jar for covering things.", "archery_ticket_price": "0", - "id": "10895" + "durability": null, + "weight": "1" }, { - "examine": "A silver whistle.", - "durability": null, + "id": "10896", "name": "Wolf whistle", + "examine": "A silver whistle.", "archery_ticket_price": "0", - "id": "10896" + "durability": null }, { - "examine": "A shipping order list.", - "durability": null, + "id": "10897", "name": "Shipping order", - "tradeable": "false", - "destroy": "true", + "examine": "A shipping order list.", "archery_ticket_price": "0", - "id": "10897" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "A small keg of gunpowder.", - "durability": null, + "id": "10898", "name": "Keg", - "weight": "12", + "examine": "A small keg of gunpowder.", "archery_ticket_price": "0", - "id": "10898" + "durability": null, + "weight": "12" }, { - "examine": "Part of a crate.", - "durability": null, + "id": "10899", "name": "Crate part", - "tradeable": "false", - "destroy": "true", + "examine": "Part of a crate.", "archery_ticket_price": "0", - "id": "10899" + "destroy": "true", + "durability": null, + "tradeable": "false" }, { - "examine": "One skull staple.", - "durability": null, + "id": "10904", "name": "Skull staple", + "examine": "One skull staple.", "archery_ticket_price": "0", - "id": "10904" + "durability": null }, { - "ge_buy_limit": "100", - "examine": "This super restore and unicorn dust potion needs more ingredients.", - "grand_exchange_price": "8420", - "durability": null, + "id": "10909", "name": "Mixture - step 1(4)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", + "examine": "This super restore and unicorn dust potion needs more ingredients.", "archery_ticket_price": "0", - "id": "10909" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8420", - "durability": null, + "id": "10910", "name": "Mixture - step 1(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10910" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "This super restore and unicorn dust potion needs more ingredients.", - "grand_exchange_price": "6543", - "durability": null, + "id": "10911", "name": "Mixture - step 1(3)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", + "examine": "This super restore and unicorn dust potion needs more ingredients.", "archery_ticket_price": "0", - "id": "10911" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "6543", - "durability": null, + "id": "10912", "name": "Mixture - step 1(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10912" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "This super restore and unicorn dust potion needs more ingredients.", - "grand_exchange_price": "4335", - "durability": null, + "id": "10913", "name": "Mixture - step 1(2)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "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", + "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)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", + "examine": "This super restore and unicorn dust potion needs more ingredients.", "archery_ticket_price": "0", - "id": "10915" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2168", - "durability": null, + "id": "10916", "name": "Mixture - step 1(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10916" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", - "grand_exchange_price": "8665", - "durability": null, + "id": "10917", "name": "Mixture - step 2(4)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", + "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", "archery_ticket_price": "0", - "id": "10917" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "8665", - "durability": null, + "id": "10918", "name": "Mixture - step 2(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10918" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", - "grand_exchange_price": "6695", - "durability": null, + "id": "10919", "name": "Mixture - step 2(3)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", + "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", "archery_ticket_price": "0", - "id": "10919" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "6695", - "durability": null, + "id": "10920", "name": "Mixture - step 2(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10920" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", - "grand_exchange_price": "4335", - "durability": null, + "id": "10921", "name": "Mixture - step 2(2)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "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", + "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)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", + "examine": "This super restore, unicorn dust, snake weed potion needs more ingredients.", "archery_ticket_price": "0", - "id": "10923" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "2168", - "durability": null, + "id": "10924", "name": "Mixture - step 2(1)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10924" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A \"X\" dose Sanfew Serum.", - "grand_exchange_price": "15900", - "durability": null, + "id": "10925", "name": "Sanfew serum(4)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", + "examine": "A \"X\" dose Sanfew Serum.", "archery_ticket_price": "0", - "id": "10925" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "15900", - "durability": null, + "id": "10926", "name": "Sanfew serum(4)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10926" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A \"X\" dose Sanfew Serum.", - "grand_exchange_price": "12000", - "durability": null, + "id": "10927", "name": "Sanfew serum(3)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", + "examine": "A \"X\" dose Sanfew Serum.", "archery_ticket_price": "0", - "id": "10927" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "12000", - "durability": null, + "id": "10928", "name": "Sanfew serum(3)", - "tradeable": "true", "archery_ticket_price": "0", - "id": "10928" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "examine": "A \"X\" dose Sanfew Serum.", - "grand_exchange_price": "10200", - "durability": null, + "id": "10929", "name": "Sanfew serum(2)", - "tradeable": "true", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "120", - "high_alchemy": "180", "archery_ticket_price": "0", - "id": "10931" - }, - { + "durability": null, "ge_buy_limit": "100", - "grand_exchange_price": "6114", - "durability": null, - "name": "Sanfew serum(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10932" + "tradeable": "true" }, { - "examine": "You certainly will be alright wearing these.", - "durability": null, - "name": "Lumberjack boots", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.9", + "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", - "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "1.3", - "archery_ticket_price": "0", - "id": "10939", - "equipment_slot": "4" - }, - { + "name": "Lumberjack boots", "examine": "You certainly will be alright wearing these.", - "durability": null, - "name": "Lumberjack legs", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "0.9", "archery_ticket_price": "0", - "id": "10940", - "equipment_slot": "7" + "durability": null, + "equip_audio": "2237", + "equipment_slot": "10", + "weight": "0.9" }, { - "remove_head": "true", - "examine": "You certainly will be alright wearing this.", - "durability": null, - "name": "Lumberjack hat", - "low_alchemy": "20", - "high_alchemy": "30", - "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, + "id": "10934", "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": "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.", "durability": null, - "name": "Reward token", - "tradeable": "false", - "destroy": "true", - "archery_ticket_price": "0", - "id": "10944" + "tradeable": "false" }, { - "remove_sleeves": "true", - "examine": "You certainly will be alright wearing this.", + "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "1.3", + "examine": "You certainly will be alright wearing this.", "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", - "low_alchemy": "60", - "high_alchemy": "90", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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" + "weight": "1.3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "1797", + "id": "10940", + "name": "Lumberjack legs", + "examine": "You certainly will be alright wearing these.", + "archery_ticket_price": "0", "durability": null, - "name": "Frog-leather body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "10955" + "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, - "low_alchemy": "360", - "high_alchemy": "540", - "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", + "examine": "Tasty saut??ed mushrooms.", "archery_ticket_price": "0", - "id": "10968" + "durability": null }, { - "examine": "Chunky cave-crawler fillets.", - "durability": null, + "id": "10969", "name": "Fillets", - "low_alchemy": "20", - "high_alchemy": "30", + "examine": "Chunky cave-crawler fillets.", "archery_ticket_price": "0", - "id": "10969" + "durability": null }, { - "examine": "Steamed cave pond loach.", - "durability": null, + "id": "10970", "name": "Loach", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "140", - "high_alchemy": "210", "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", - "low_alchemy": "2", - "high_alchemy": "4", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "100", - "high_alchemy": "150", + "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "10400", - "high_alchemy": "15600", - "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", - "low_alchemy": "7", - "high_alchemy": "10", - "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", - "low_alchemy": "160", - "high_alchemy": "160", + "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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" }, { + "id": "11066", + "name": "Bracelet mould", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "11069", + "name": "Gold bracelet", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "5000", "shop_price": "165", + "tradeable": "true", + "weight": "0.2" + }, + { + "id": "11070", + "name": "Gold bracelet", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", + "tradeable": "true" + }, + { + "id": "11072", + "name": "Sapphire bracelet", "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "220", - "high_alchemy": "330", - "weight": "0.2", "equipment_slot": "9", - "grand_exchange_price": "223", - "name": "Gold bracelet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11069" - }, - { "ge_buy_limit": "5000", - "grand_exchange_price": "223", - "durability": null, - "name": "Gold bracelet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11070" - }, - { "shop_price": "1035", - "ge_buy_limit": "5000", - "examine": "I wonder if this is valuable.", - "durability": null, - "low_alchemy": "460", - "high_alchemy": "690", - "weight": "0.25", - "equipment_slot": "9", - "grand_exchange_price": "578", - "name": "Sapphire bracelet", "tradeable": "true", - "archery_ticket_price": "0", - "id": "11072" + "weight": "0.25" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "578", - "durability": null, + "id": "11073", "name": "Sapphire bracelet", - "tradeable": "true", "archery_ticket_price": "0", - "id": "11073" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "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", - "low_alchemy": "506", - "high_alchemy": "759", - "archery_ticket_price": "0", "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", - "low_alchemy": "610", - "high_alchemy": "915", - "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", - "low_alchemy": "670", - "high_alchemy": "1005", - "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", - "low_alchemy": "670", - "high_alchemy": "1005", - "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", - "low_alchemy": "670", - "high_alchemy": "1005", - "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", - "low_alchemy": "930", - "high_alchemy": "1395", + "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", - "low_alchemy": "1024", - "high_alchemy": "1536", - "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", - "low_alchemy": "1620", - "high_alchemy": "2430", + "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", - "low_alchemy": "1530", - "high_alchemy": "2295", + "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", - "low_alchemy": "1680", - "high_alchemy": "2520", - "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", - "low_alchemy": "1680", - "high_alchemy": "2520", - "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", - "low_alchemy": "1680", - "high_alchemy": "2520", - "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", - "low_alchemy": "1680", - "high_alchemy": "2520", - "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", - "low_alchemy": "1680", - "high_alchemy": "2520", - "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", - "low_alchemy": "8080", - "high_alchemy": "12120", + "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", - "low_alchemy": "8080", - "high_alchemy": "12120", + "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", - "low_alchemy": "8080", - "high_alchemy": "12120", + "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", - "low_alchemy": "8080", - "high_alchemy": "12120", + "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", - "low_alchemy": "8080", - "high_alchemy": "12120", "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", - "low_alchemy": "7650", - "high_alchemy": "11475", - "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.", - "durability": null, - "low_alchemy": "8416", - "high_alchemy": "12624", - "weight": "0.3", - "equipment_slot": "9", - "grand_exchange_price": "29700", - "name": "Combat bracelet(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11118", - "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0" - }, - { - "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", - "low_alchemy": "8416", - "high_alchemy": "12624", - "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", - "low_alchemy": "8416", - "high_alchemy": "12624", - "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", - "low_alchemy": "8416", - "high_alchemy": "12624", - "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.", - "durability": null, - "low_alchemy": "8416", - "high_alchemy": "12624", - "weight": "0.3", - "equipment_slot": "9", - "grand_exchange_price": "29000", - "name": "Combat bracelet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11126", - "bonuses": "7,7,7,3,7,5,5,5,3,5,5,6,0,0,0" - }, - { - "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", - "low_alchemy": "80400", - "high_alchemy": "120600", - "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", - "low_alchemy": "80800", - "high_alchemy": "121200", "archery_ticket_price": "0", - "id": "11130", - "equipment_slot": "9" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "941200", "durability": null, - "name": "Onyx bracelet", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11131" - }, - { - "ge_buy_limit": "100", - "examine": "Helps to restore your life points.", - "durability": null, - "low_alchemy": "80800", - "high_alchemy": "121200", - "weight": "0.2", "equipment_slot": "9", - "grand_exchange_price": "124200", - "name": "Regen bracelet", + "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", + "name": "Onyx bracelet", + "examine": "I wonder if this is valuable.", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "shop_price": "80400", + "tradeable": "true" + }, + { + "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" - }, - { - "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, - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "3", - "turn90ccw_anim": "822", - "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, - "low_alchemy": "1", - "high_alchemy": "2", - "weight": "3", - "turn90ccw_anim": "822", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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)", - "low_alchemy": "960", - "high_alchemy": "1440", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11190", - "equipment_slot": "2" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (2)", - "low_alchemy": "960", - "high_alchemy": "1440", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11191", - "equipment_slot": "2" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (3)", - "low_alchemy": "960", - "high_alchemy": "1440", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11192", - "equipment_slot": "2" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (4)", - "low_alchemy": "960", - "high_alchemy": "1440", - "weight": "0.5", - "archery_ticket_price": "0", - "id": "11193", - "equipment_slot": "2" - }, - { - "examine": "An enchanted necklace.", - "durability": null, - "name": "Digsite pendant (5)", - "low_alchemy": "960", - "high_alchemy": "1440", - "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, - "low_alchemy": "24000", - "high_alchemy": "36000", - "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", - "high_alchemy": "1", + "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.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "equipment_slot": "13", - "grand_exchange_price": "2674", - "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" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with dragon heads and oil-soaked cloth.lit: Dragon-headed fire arrow.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "equipment_slot": "13", - "grand_exchange_price": "3988", - "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" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "unlit: Arrows with dragon heads and oil-soaked cloth.lit: Dragon-headed fire arrow.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "equipment_slot": "13", - "grand_exchange_price": "3834", - "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" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "An arrow made using a dragon's talon.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "equipment_slot": "13", - "grand_exchange_price": "3141", - "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" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "An arrow made using a dragon's talon.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "equipment_slot": "13", - "grand_exchange_price": "2866", - "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" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "An arrow made using a dragon's talon.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "equipment_slot": "13", - "grand_exchange_price": "3766", - "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" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "A deadly throwing dart with a dragon tip.", - "durability": null, - "low_alchemy": "200", - "high_alchemy": "300", - "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, - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "archery_ticket_price": "0", - "id": "11232" - }, - { - "requirements": "{4,60}", - "ge_buy_limit": "10000", - "examine": "A deadly throwing dart with a dragon tip.", - "durability": null, - "low_alchemy": "200", - "high_alchemy": "300", - "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, - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "48000", - "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, - "high_alchemy": "72001", - "weight": "1.9", - "weapon_interface": "16", - "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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" - }, - { - "shop_price": "3", - "turn90cw_anim": "6610", - "examine": "For catching butterflies.", - "walk_anim": "6607", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "destroy": "true", - "weight": "0.2", - "turn90ccw_anim": "6609", - "attack_speed": "4", - "two_handed": "true", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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" - }, - { - "examine": "The label says 'Vivid Crimson', but it looks like pink to me!", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "weight": "0.9", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "27", - "name": "Ham shirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11274", - "bonuses": "0,0,0,0,0,0,2,2,0,0,0,0,0,0,0" - }, - { - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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.", - "rare_item": "true", - "durability": null, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "weight": "7.2", - "equipment_slot": "5", - "grand_exchange_price": "5653008", - "name": "Dragonfire shield", - "archery_ticket_price": "0", - "id": "11283", - "bonuses": "0,0,0,-10,-5,70,75,72,10,72,17,7,0,0,0" - }, - { - "requirements": "{1,75}", - "ge_buy_limit": "10", - "examine": "A heavy shield with a snarling, draconic visage.", - "rare_item": "true", - "durability": null, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "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", - "low_alchemy": "300000", - "high_alchemy": "450000", - "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "30", - "high_alchemy": "45", - "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, - "low_alchemy": "60000", - "high_alchemy": "90000", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "36", - "high_alchemy": "54", - "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, - "low_alchemy": "130", - "high_alchemy": "195", - "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, - "low_alchemy": "338", - "high_alchemy": "507", - "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, - "low_alchemy": "832", - "high_alchemy": "1248", - "weight": "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, - "low_alchemy": "8320", - "high_alchemy": "12480", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "walk_anim": "1205", - "durability": null, - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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, - "low_alchemy": "36", - "high_alchemy": "54", - "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", - "walk_anim": "1205", - "durability": null, - "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, - "low_alchemy": "36", - "high_alchemy": "54", - "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, - "low_alchemy": "36", - "high_alchemy": "54", - "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, - "low_alchemy": "130", - "high_alchemy": "195", - "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", - "walk_anim": "1205", - "durability": null, - "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, - "low_alchemy": "130", - "high_alchemy": "195", - "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, - "low_alchemy": "130", - "high_alchemy": "195", - "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, - "low_alchemy": "338", - "high_alchemy": "507", - "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 mithril-tipped, one-handed hasta.", - "walk_anim": "1205", - "durability": null, - "low_alchemy": "338", - "high_alchemy": "507", - "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)", - "tradeable": "true", - "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, - "low_alchemy": "338", - "high_alchemy": "507", - "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, - "low_alchemy": "338", - "high_alchemy": "507", - "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, - "low_alchemy": "832", - "high_alchemy": "1248", - "weight": "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", - "walk_anim": "1205", - "durability": null, - "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, - "low_alchemy": "832", - "high_alchemy": "1248", - "weight": "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", - "walk_anim": "1205", - "durability": null, - "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++)", - "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, - "low_alchemy": "8320", - "high_alchemy": "12480", - "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", - "walk_anim": "1205", - "durability": null, - "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, - "low_alchemy": "8320", - "high_alchemy": "12480", - "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, - "low_alchemy": "8320", - "high_alchemy": "12480", - "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", - "low_alchemy": "3", - "high_alchemy": "5", - "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", - "low_alchemy": "3", - "high_alchemy": "5", - "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", - "low_alchemy": "86", - "high_alchemy": "129", - "archery_ticket_price": "0", - "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", - "low_alchemy": "86", - "high_alchemy": "129", - "archery_ticket_price": "0", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "archery_ticket_price": "0", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "archery_ticket_price": "0", - "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", - "low_alchemy": "5", - "high_alchemy": "7", - "archery_ticket_price": "0", - "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", - "low_alchemy": "5", - "high_alchemy": "7", - "archery_ticket_price": "0", - "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", - "low_alchemy": "42", - "high_alchemy": "63", - "archery_ticket_price": "0", - "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", - "low_alchemy": "42", - "high_alchemy": "63", - "archery_ticket_price": "0", - "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", - "low_alchemy": "26", - "high_alchemy": "39", - "archery_ticket_price": "0", - "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", - "low_alchemy": "26", - "high_alchemy": "39", - "archery_ticket_price": "0", - "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", - "low_alchemy": "28", - "high_alchemy": "43", - "archery_ticket_price": "0", - "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", - "low_alchemy": "28", - "high_alchemy": "43", - "archery_ticket_price": "0", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "archery_ticket_price": "0", - "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", - "low_alchemy": "36", - "high_alchemy": "54", - "archery_ticket_price": "0", - "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", - "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", - "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", - "low_alchemy": "46", - "high_alchemy": "69", - "archery_ticket_price": "0", - "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", - "low_alchemy": "46", - "high_alchemy": "69", - "archery_ticket_price": "0", - "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", - "low_alchemy": "54", - "high_alchemy": "81", - "archery_ticket_price": "0", - "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", - "low_alchemy": "54", - "high_alchemy": "81", - "archery_ticket_price": "0", - "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", - "low_alchemy": "86", - "high_alchemy": "129", - "archery_ticket_price": "0", - "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", - "low_alchemy": "86", - "high_alchemy": "129", - "archery_ticket_price": "0", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "archery_ticket_price": "0", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "archery_ticket_price": "0", - "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", - "low_alchemy": "64", - "high_alchemy": "96", - "archery_ticket_price": "0", - "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", - "low_alchemy": "64", - "high_alchemy": "96", - "archery_ticket_price": "0", - "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", - "low_alchemy": "66", - "high_alchemy": "99", - "archery_ticket_price": "0", - "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", - "low_alchemy": "66", - "high_alchemy": "99", - "archery_ticket_price": "0", - "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", - "low_alchemy": "72", - "high_alchemy": "108", - "archery_ticket_price": "0", - "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", - "low_alchemy": "72", - "high_alchemy": "108", - "archery_ticket_price": "0", - "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", - "low_alchemy": "66", - "high_alchemy": "99", - "archery_ticket_price": "0", - "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", - "low_alchemy": "66", - "high_alchemy": "99", - "archery_ticket_price": "0", - "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", - "low_alchemy": "79", - "high_alchemy": "118", - "archery_ticket_price": "0", - "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", - "low_alchemy": "79", - "high_alchemy": "118", - "archery_ticket_price": "0", - "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", - "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", - "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", - "low_alchemy": "79", - "high_alchemy": "118", - "archery_ticket_price": "0", - "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", - "low_alchemy": "79", - "high_alchemy": "118", - "archery_ticket_price": "0", - "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", - "low_alchemy": "86", - "high_alchemy": "129", - "archery_ticket_price": "0", - "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", - "low_alchemy": "86", - "high_alchemy": "129", - "archery_ticket_price": "0", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "archery_ticket_price": "0", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "archery_ticket_price": "0", - "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", - "low_alchemy": "3", - "high_alchemy": "5", - "archery_ticket_price": "0", - "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", - "low_alchemy": "3", - "high_alchemy": "5", - "archery_ticket_price": "0", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "archery_ticket_price": "0", - "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", - "low_alchemy": "60", - "high_alchemy": "90", - "archery_ticket_price": "0", - "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", - "low_alchemy": "10", - "high_alchemy": "15", - "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", - "low_alchemy": "1", - "high_alchemy": "1", - "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", - "shop_price": "200", - "examine": "A mage helm from the order of the Void Knights.", - "durability": null, - "low_alchemy": "3940", - "high_alchemy": "5910", - "destroy": "true", - "equipment_slot": "0", - "name": "Void mage helm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11663", - "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "200", - "examine": "A ranger helm from the order of the Void Knights", - "durability": null, - "low_alchemy": "3940", - "high_alchemy": "5910", - "weight": "3.6", - "equipment_slot": "0", - "name": "Void ranger helm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11664", - "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "200", - "examine": "A melee helm from the order of the Void Knights.", - "durability": null, - "low_alchemy": "3940", - "high_alchemy": "5910", - "weight": "0.4", - "equipment_slot": "0", - "name": "Void melee helm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11665", - "bonuses": "0,0,0,0,0,6,6,6,6,6,6,0,0,0,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", - "shop_price": "200", - "examine": "A mage helm from the order of the Void Knights.", - "durability": null, - "low_alchemy": "3940", - "high_alchemy": "5910", - "destroy": "true", - "equipment_slot": "0", - "name": "Void mage helm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11674", - "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "200", - "examine": "A ranger helm from the order of the Void Knights", - "durability": null, - "low_alchemy": "3940", - "high_alchemy": "5910", - "weight": "3.6", - "equipment_slot": "0", - "name": "Void ranger helm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11675", - "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "200", - "examine": "A melee helm from the order of the Void Knights.", - "durability": null, - "low_alchemy": "3940", - "high_alchemy": "5910", - "weight": "0.4", - "equipment_slot": "0", - "name": "Void melee helm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "11676", - "bonuses": "0,0,0,0,0,6,6,6,6,6,0,0,0,0,0" - }, - { - "destroy_message": "I can find another book in the Keldagrim library.", - "examine": "\"Beyond Trollheim\" by Nestor Peregrine.", - "durability": null, - "name": "Explorer's notes", - "low_alchemy": "2", - "tradeable": "false", - "high_alchemy": "3", - "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", - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "low_alchemy": "300000", - "high_alchemy": "450000", - "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", - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "low_alchemy": "500000", - "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, - "high_alchemy": "750000", - "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", - "low_alchemy": "500000", - "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, - "high_alchemy": "750000", - "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", - "low_alchemy": "500000", - "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, - "high_alchemy": "750000", - "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", - "low_alchemy": "500000", - "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, - "high_alchemy": "750000", - "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", - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "name": "Godsword shard 1", - "tradeable": "true", - "low_alchemy": "100000", - "high_alchemy": "150000", - "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, - "name": "Godsword shard 2", - "tradeable": "true", - "low_alchemy": "100000", - "high_alchemy": "150000", - "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, - "name": "Godsword shard 3", - "tradeable": "true", - "low_alchemy": "100000", - "high_alchemy": "150000", - "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", - "low_alchemy": "40000", - "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, - "high_alchemy": "60000", - "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, - "low_alchemy": "8000", - "high_alchemy": "12000", - "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, - "low_alchemy": "24000", - "high_alchemy": "36000", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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, - "low_alchemy": "106000", - "high_alchemy": "159000", - "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, - "low_alchemy": "109000", - "high_alchemy": "163500", - "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, - "low_alchemy": "10000", - "high_alchemy": "15000", - "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", - "low_alchemy": "52000", - "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, - "high_alchemy": "78000", - "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, - "low_alchemy": "8000", - "high_alchemy": "12000", - "weight": "1", - "equipment_slot": "10", - "lendable": "true", - "grand_exchange_price": "95400", - "name": "Dragon boots", - "tradeable": "true", - "rare_item": "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", - "low_alchemy": "6800", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "equipment_slot": "3", - "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,10,0,2,3,1,10,0,0,35,0,0,0", - "requirements": "{0,30}-{6,30}", - "durability": null, - "high_alchemy": "10200", - "weight": "2", - "weapon_interface": "1", - "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", - "low_alchemy": "18000", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "equipment_slot": "3", - "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,10,0,2,3,1,10,0,0,50,0,0,0", - "requirements": "{0,40}-{6,40}", - "durability": null, - "high_alchemy": "27000", - "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", - "low_alchemy": "4", - "tradeable": "false", - "high_alchemy": "6", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "16", - "high_alchemy": "24", - "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "low_alchemy": "4", - "high_alchemy": "6", + "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", - "low_alchemy": "8", - "high_alchemy": "12", - "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)", - "low_alchemy": "5", - "high_alchemy": "7", - "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)", - "low_alchemy": "5", - "high_alchemy": "7", + "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)", - "low_alchemy": "5", - "high_alchemy": "7", + "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)", - "low_alchemy": "5", - "high_alchemy": "7", + "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.", - "grand_exchange_price": "136300", - "durability": null, - "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", - "absorb": "9,0,20", - "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.", + "archery_ticket_price": "0", "durability": null, + "ge_buy_limit": "10", + "requirements": "{1,60}", + "tradeable": "true", + "weight": "2.2" + }, + { + "id": "11843", "name": "Dragon chain armour set (l)", "archery_ticket_price": "0", - "id": "11842" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "4800000", "durability": null, - "name": "Dragon chain armour set (l)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "11843" + "ge_buy_limit": "10", + "tradeable": "true" }, { + "id": "11844", + "name": "Dragon chain armour set (sk)", + "examine": "Grand Exchange set containing a dragon helm, chainbody, skirt.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", "requirements": "{1,60}", - "ge_buy_limit": "10", - "grand_exchange_price": "4600000", - "durability": null, - "name": "Dragon chain armour set (sk)", - "archery_ticket_price": "0", - "id": "11844" - }, - { - "ge_buy_limit": "10", - "grand_exchange_price": "4600000", - "durability": null, - "name": "Dragon chain armour set (sk)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "11845" + "weight": "2.2" }, { - "requirements": "{0,70}-{1,70}-{6,70}", + "id": "11845", + "name": "Dragon chain armour set (sk)", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "10", + "tradeable": "true" + }, + { + "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", - "high_alchemy": "1", + "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "289", - "high_alchemy": "434", "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", - "low_alchemy": "289", - "high_alchemy": "434", "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", - "low_alchemy": "239", - "high_alchemy": "359", "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", - "low_alchemy": "239", - "high_alchemy": "359", "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", - "low_alchemy": "1849", - "high_alchemy": "2774", "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", - "low_alchemy": "1849", - "high_alchemy": "2774", "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", - "low_alchemy": "1409", - "high_alchemy": "2114", "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", - "low_alchemy": "1409", - "high_alchemy": "2114", "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", - "low_alchemy": "1329", - "high_alchemy": "1994", "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", - "low_alchemy": "1329", - "high_alchemy": "1994", "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", - "low_alchemy": "179", - "high_alchemy": "269", "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", - "low_alchemy": "179", - "high_alchemy": "269", "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", - "low_alchemy": "259", - "high_alchemy": "389", "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", - "low_alchemy": "259", - "high_alchemy": "389", "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", - "low_alchemy": "889", - "high_alchemy": "1334", "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", - "low_alchemy": "889", - "high_alchemy": "1334", "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", - "low_alchemy": "1609", - "high_alchemy": "2414", "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", - "low_alchemy": "1609", - "high_alchemy": "2414", "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", - "low_alchemy": "1449", - "high_alchemy": "2174", "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", - "low_alchemy": "1449", - "high_alchemy": "2174", "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", - "low_alchemy": "1679", - "high_alchemy": "2519", "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", - "low_alchemy": "1679", - "high_alchemy": "2519", "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", - "low_alchemy": "1269", - "high_alchemy": "1904", "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", - "low_alchemy": "1269", - "high_alchemy": "1904", "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", - "low_alchemy": "179", - "high_alchemy": "269", "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", - "low_alchemy": "179", - "high_alchemy": "269", "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", - "low_alchemy": "1469", - "high_alchemy": "2204", "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", - "low_alchemy": "1469", - "high_alchemy": "2204", "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", - "low_alchemy": "1229", - "high_alchemy": "1844", "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", - "low_alchemy": "1229", - "high_alchemy": "1844", "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", - "low_alchemy": "1359", - "high_alchemy": "2039", "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", - "low_alchemy": "1359", - "high_alchemy": "2039", "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", - "low_alchemy": "1569", - "high_alchemy": "2354", "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", - "low_alchemy": "1569", - "high_alchemy": "2354", "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", - "low_alchemy": "1049", - "high_alchemy": "1574", "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", - "low_alchemy": "1049", - "high_alchemy": "1574", "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", - "low_alchemy": "249", - "high_alchemy": "374", "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", - "low_alchemy": "249", - "high_alchemy": "374", "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", - "low_alchemy": "1449", - "high_alchemy": "2174", "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", - "low_alchemy": "1449", - "high_alchemy": "2174", "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", - "low_alchemy": "239", - "high_alchemy": "359", "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", - "low_alchemy": "239", - "high_alchemy": "359", "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", - "low_alchemy": "619", - "high_alchemy": "929", "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", - "low_alchemy": "619", - "high_alchemy": "929", "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", - "low_alchemy": "1209", - "high_alchemy": "1814", "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", - "low_alchemy": "1209", - "high_alchemy": "1814", - "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", - "low_alchemy": "979", - "high_alchemy": "1469", "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", - "low_alchemy": "979", - "high_alchemy": "1469", - "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", - "low_alchemy": "739", - "high_alchemy": "1109", "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", - "low_alchemy": "739", - "high_alchemy": "1109", "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", - "low_alchemy": "309", - "high_alchemy": "464", "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", - "low_alchemy": "309", - "high_alchemy": "464", "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", - "low_alchemy": "249", - "high_alchemy": "374", "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", - "low_alchemy": "249", - "high_alchemy": "374", "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", - "low_alchemy": "1339", - "high_alchemy": "2009", "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", - "low_alchemy": "1339", - "high_alchemy": "2009", "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", - "low_alchemy": "679", - "high_alchemy": "1019", "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", - "low_alchemy": "679", - "high_alchemy": "1019", "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", - "low_alchemy": "1009", - "high_alchemy": "1514", "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", - "low_alchemy": "1009", - "high_alchemy": "1514", "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", - "low_alchemy": "919", - "high_alchemy": "1379", "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", - "low_alchemy": "919", - "high_alchemy": "1379", "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", - "low_alchemy": "1829", - "high_alchemy": "2744", "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", - "low_alchemy": "1829", - "high_alchemy": "2744", "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", - "low_alchemy": "949", - "high_alchemy": "1424", "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", - "low_alchemy": "949", - "high_alchemy": "1424", "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", - "low_alchemy": "1189", - "high_alchemy": "1784", "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", - "low_alchemy": "1189", - "high_alchemy": "1784", "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", - "low_alchemy": "1419", - "high_alchemy": "2129", "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", - "low_alchemy": "1419", - "high_alchemy": "2129", "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", - "low_alchemy": "1579", - "high_alchemy": "2369", "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", - "low_alchemy": "1579", - "high_alchemy": "2369", "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", - "low_alchemy": "1689", - "high_alchemy": "2534", "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", - "low_alchemy": "1689", - "high_alchemy": "2534", - "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", - "low_alchemy": "1609", - "high_alchemy": "2414", - "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", - "low_alchemy": "1609", - "high_alchemy": "2414", - "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", - "low_alchemy": "179", - "high_alchemy": "269", - "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", - "low_alchemy": "179", - "high_alchemy": "269", - "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", - "low_alchemy": "1579", - "high_alchemy": "2369", - "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", - "low_alchemy": "1579", - "high_alchemy": "2369", - "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", - "low_alchemy": "279", - "high_alchemy": "419", - "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", - "low_alchemy": "279", - "high_alchemy": "419", - "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", - "low_alchemy": "2199", - "high_alchemy": "3299", - "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", - "low_alchemy": "2199", - "high_alchemy": "3299", - "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", - "low_alchemy": "639", - "high_alchemy": "959", - "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", - "low_alchemy": "639", - "high_alchemy": "959", - "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", - "low_alchemy": "2279", - "high_alchemy": "3419", - "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", - "low_alchemy": "2279", - "high_alchemy": "3419", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "1049", - "high_alchemy": "1574", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "400", - "high_alchemy": "600", - "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", - "low_alchemy": "279", - "high_alchemy": "419", - "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", - "low_alchemy": "279", - "high_alchemy": "419", "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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": "true", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "12", - "high_alchemy": "18", - "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", - "low_alchemy": "76", - "high_alchemy": "114", - "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", - "low_alchemy": "76", - "high_alchemy": "114", - "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", - "low_alchemy": "76", - "high_alchemy": "114", - "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", - "low_alchemy": "76", - "high_alchemy": "114", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "2", - "high_alchemy": "3", "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", - "high_alchemy": "1", + "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", - "low_alchemy": "1", - "high_alchemy": "1", + "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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.", + "archery_ticket_price": "0", "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", - "id": "12168" + "durability": null }, { - "shop_price": "99000", - "examine": "The cape worn by master summoners.", - "durability": null, - "name": "Summoning cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "weight": "0.4", - "archery_ticket_price": "0", "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", - "equipment_slot": "1" + "durability": null, + "equipment_slot": "1", + "requirements": "{23,99}", + "shop_price": "99000", + "weight": "0.4" }, { - "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}" }, { - "remove_head": "true", - "shop_price": "99000", - "examine": "Summoning skillcape hood.", - "durability": null, - "name": "Summoning hood", - "weight": "0.4", - "archery_ticket_price": "0", "id": "12171", - "equipment_slot": "0" + "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", + "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", - "low_alchemy": "9", - "high_alchemy": "14", - "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", - "low_alchemy": "9", - "high_alchemy": "13", "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", - "low_alchemy": "10", - "high_alchemy": "15", + "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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...", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", - "equipment_slot": "0", - "grand_exchange_price": "50", - "name": "Antlers (charged)", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12206", - "bonuses": "0,0,0,0,0,0,0,0,0,0,8,0,0,0,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, - "low_alchemy": "80", - "high_alchemy": "120", - "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, - "low_alchemy": "80", - "high_alchemy": "120", - "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!", - "durability": null, - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "2", - "equipment_slot": "0", - "grand_exchange_price": "234", - "name": "Feather headdress", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12210", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,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", - "low_alchemy": "160", - "high_alchemy": "240", - "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!", - "durability": null, - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "2", - "equipment_slot": "0", - "grand_exchange_price": "662", - "name": "Feather headdress", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12213", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,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", - "low_alchemy": "160", - "high_alchemy": "240", - "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!", - "durability": null, - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "2", - "equipment_slot": "0", - "grand_exchange_price": "90", - "name": "Feather headdress", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12216", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,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", - "low_alchemy": "160", - "high_alchemy": "240", - "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!", - "durability": null, - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "2", - "equipment_slot": "0", - "grand_exchange_price": "107", - "name": "Feather headdress", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12219", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,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", - "low_alchemy": "160", - "high_alchemy": "240", - "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" - }, - { - "requirements": "{4,50}-{23,20}", - "ge_buy_limit": "100", - "examine": "It's fun to go to the O-B-E-L-I-S-K!", "durability": null, - "low_alchemy": "160", - "high_alchemy": "240", - "weight": "2", "equipment_slot": "0", - "grand_exchange_price": "211", - "name": "Feather headdress", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12222", - "bonuses": "0,0,0,-1,2,4,6,8,4,4,20,0,0,0,0" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "211", - "durability": null, + "requirements": "{4,50}-{23,20}", + "tradeable": "true", + "weight": "2" + }, + { + "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", - "low_alchemy": "160", - "high_alchemy": "240", - "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", - "low_alchemy": "94", - "tradeable": "true", - "high_alchemy": "142", + "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", - "low_alchemy": "146", - "tradeable": "true", - "high_alchemy": "220", + "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", - "low_alchemy": "125", - "tradeable": "true", - "high_alchemy": "188", + "examine": "A scroll for an ibis familiar.", "archery_ticket_price": "0", - "id": "12424" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,56}", + "tradeable": "true" }, { - "ge_buy_limit": "5000", - "grand_exchange_price": "7", - "examine": "A scroll for a spirit wolf familiar.", - "durability": null, + "id": "12425", "name": "Howl scroll", - "low_alchemy": "23", - "tradeable": "true", - "high_alchemy": "35", + "examine": "A scroll for a spirit wolf familiar.", "archery_ticket_price": "0", - "id": "12425" + "durability": null, + "ge_buy_limit": "5000", + "tradeable": "true" }, { - "requirements": "{23,47}", - "ge_buy_limit": "5000", - "grand_exchange_price": "97", - "examine": "A scroll for a magpie familiar.", - "durability": null, + "id": "12426", "name": "Thieving fingers scroll", - "low_alchemy": "104", - "tradeable": "true", - "high_alchemy": "157", + "examine": "A scroll for a magpie familiar.", "archery_ticket_price": "0", - "id": "12426" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,47}", + "tradeable": "true" }, { - "requirements": "{23,62}", - "ge_buy_limit": "5000", - "grand_exchange_price": "533", - "examine": "A scroll for an abyssal lurker familiar.", - "durability": null, + "id": "12427", "name": "Abyssal stealth scroll", - "low_alchemy": "135", - "tradeable": "true", - "high_alchemy": "203", + "examine": "A scroll for an abyssal lurker familiar.", "archery_ticket_price": "0", - "id": "12427" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,62}", + "tradeable": "true" }, { - "requirements": "{23,10}", - "ge_buy_limit": "5000", - "grand_exchange_price": "237", - "examine": "A scroll for a spirit spider familiar.", - "durability": null, + "id": "12428", "name": "Egg spawn scroll", - "low_alchemy": "24", - "tradeable": "true", - "high_alchemy": "37", + "examine": "A scroll for a spirit spider familiar.", "archery_ticket_price": "0", - "id": "12428" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,10}", + "tradeable": "true" }, { - "requirements": "{23,33}", - "ge_buy_limit": "5000", - "grand_exchange_price": "97", - "examine": "A scroll for a beaver familiar.", - "durability": null, + "id": "12429", "name": "Multichop scroll", - "low_alchemy": "88", - "tradeable": "true", - "high_alchemy": "133", + "examine": "A scroll for a beaver familiar.", "archery_ticket_price": "0", - "id": "12429" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,33}", + "tradeable": "true" }, { - "requirements": "{23,23}", - "ge_buy_limit": "5000", - "grand_exchange_price": "121", - "examine": "A scroll for an albino rat familiar.", - "durability": null, + "id": "12430", "name": "Cheese feast scroll", - "low_alchemy": "91", - "tradeable": "true", - "high_alchemy": "137", + "examine": "A scroll for an albino rat familiar.", "archery_ticket_price": "0", - "id": "12430" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,23}", + "tradeable": "true" }, { - "requirements": "{23,40}", - "ge_buy_limit": "5000", - "grand_exchange_price": "39", - "examine": "A scroll for a bull ant familiar.", - "durability": null, + "id": "12431", "name": "Unburden scroll", - "low_alchemy": "27", - "tradeable": "true", - "high_alchemy": "41", + "examine": "A scroll for a bull ant familiar.", "archery_ticket_price": "0", - "id": "12431" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,40}", + "tradeable": "true" }, { - "requirements": "{23,19}", - "ge_buy_limit": "5000", - "grand_exchange_price": "72", - "examine": "A scroll for a spirit scorpion familiar.", - "durability": null, + "id": "12432", "name": "Venom shot scroll", - "low_alchemy": "73", - "tradeable": "true", - "high_alchemy": "110", + "examine": "A scroll for a spirit scorpion familiar.", "archery_ticket_price": "0", - "id": "12432" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,19}", + "tradeable": "true" }, { - "requirements": "{23,32}", - "ge_buy_limit": "5000", - "grand_exchange_price": "99", - "examine": "A scroll for a honey badger familiar.", - "durability": null, + "id": "12433", "name": "Insane ferocity scroll", - "low_alchemy": "100", - "tradeable": "true", - "high_alchemy": "151", + "examine": "A scroll for a honey badger familiar.", "archery_ticket_price": "0", - "id": "12433" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,32}", + "tradeable": "true" }, { - "requirements": "{23,88}", - "ge_buy_limit": "5000", - "grand_exchange_price": "299", - "examine": "A scroll for a unicorn stallion familiar.", - "durability": null, + "id": "12434", "name": "Healing aura scroll", - "low_alchemy": "156", - "tradeable": "true", - "high_alchemy": "235", + "examine": "A scroll for a unicorn stallion familiar.", "archery_ticket_price": "0", - "id": "12434" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,88}", + "tradeable": "true" }, { - "requirements": "{23,96}", - "ge_buy_limit": "5000", - "grand_exchange_price": "428", - "examine": "A scroll for a pack yak familiar.", - "durability": null, + "id": "12435", "name": "Winter storage scroll", - "low_alchemy": "227", - "tradeable": "true", - "high_alchemy": "341", + "examine": "A scroll for a pack yak familiar.", "archery_ticket_price": "0", - "id": "12435" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,96}", + "tradeable": "true" }, { - "requirements": "{23,63}", - "ge_buy_limit": "5000", - "grand_exchange_price": "223", - "examine": "A scroll for a spirit cobra familiar.", - "durability": null, + "id": "12436", "name": "Oph. incubation scroll", - "low_alchemy": "132", - "tradeable": "true", - "high_alchemy": "199", + "examine": "A scroll for a spirit cobra familiar.", "archery_ticket_price": "0", - "id": "12436" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,63}", + "tradeable": "true" }, { - "requirements": "{23,92}", - "ge_buy_limit": "5000", - "grand_exchange_price": "419", - "examine": "A scroll for a wolpertinger familiar.", - "durability": null, + "id": "12437", "name": "Magic focus scroll", - "low_alchemy": "219", - "tradeable": "true", - "high_alchemy": "329", + "examine": "A scroll for a wolpertinger familiar.", "archery_ticket_price": "0", - "id": "12437" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,92}", + "tradeable": "true" }, { - "requirements": "{23,68}", - "ge_buy_limit": "5000", - "grand_exchange_price": "220", - "examine": "A scroll for a bunyip familiar.", - "durability": null, + "id": "12438", "name": "Swallow whole scroll", - "low_alchemy": "126", - "tradeable": "true", - "high_alchemy": "190", + "examine": "A scroll for a bunyip familiar.", "archery_ticket_price": "0", - "id": "12438" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,68}", + "tradeable": "true" }, { - "requirements": "{23,67}", - "ge_buy_limit": "5000", - "grand_exchange_price": "269", - "examine": "A scroll for a war tortoise familiar.", - "durability": null, + "id": "12439", "name": "Testudo scroll", - "low_alchemy": "17", - "tradeable": "true", - "high_alchemy": "26", + "examine": "A scroll for a war tortoise familiar.", "archery_ticket_price": "0", - "id": "12439" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,67}", + "tradeable": "true" }, { - "requirements": "{23,28}", - "ge_buy_limit": "5000", - "grand_exchange_price": "47", - "examine": "A scroll for a compost mound familiar.", - "durability": null, + "id": "12440", "name": "Generate compost scroll", - "low_alchemy": "63", - "tradeable": "true", - "high_alchemy": "95", + "examine": "A scroll for a compost mound familiar.", "archery_ticket_price": "0", - "id": "12440" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,28}", + "tradeable": "true" }, { - "requirements": "{23,52}", - "ge_buy_limit": "5000", - "grand_exchange_price": "35", - "examine": "A scroll for a spirit terrorbird familiar.", - "durability": null, + "id": "12441", "name": "Tireless run scroll", - "low_alchemy": "28", - "tradeable": "true", - "high_alchemy": "43", + "examine": "A scroll for a spirit terrorbird familiar.", "archery_ticket_price": "0", - "id": "12441" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,52}", + "tradeable": "true" }, { - "requirements": "{23,80}", - "ge_buy_limit": "5000", - "grand_exchange_price": "174", - "examine": "A scroll for a hydra familiar.", - "durability": null, + "id": "12442", "name": "Regrowth scroll", - "low_alchemy": "144", - "tradeable": "true", - "high_alchemy": "217", + "examine": "A scroll for a hydra familiar.", "archery_ticket_price": "0", - "id": "12442" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,80}", + "tradeable": "true" }, { - "requirements": "{23,34}", - "ge_buy_limit": "5000", - "grand_exchange_price": "1118", - "examine": "A scroll for a Pest Control familiar.", - "durability": null, + "id": "12443", "name": "Call to arms scroll", - "low_alchemy": "90", - "tradeable": "true", - "high_alchemy": "136", + "examine": "A scroll for a Pest Control familiar.", "archery_ticket_price": "0", - "id": "12443" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,34}", + "tradeable": "true" }, { - "requirements": "{23,49}", - "ge_buy_limit": "5000", - "grand_exchange_price": "148", - "examine": "A scroll for a bloated leech familiar.", - "durability": null, + "id": "12444", "name": "Blood drain scroll", - "low_alchemy": "133", - "tradeable": "true", - "high_alchemy": "200", + "examine": "A scroll for a bloated leech familiar.", "archery_ticket_price": "0", - "id": "12444" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,49}", + "tradeable": "true" }, { - "requirements": "{23,4}", - "ge_buy_limit": "5000", - "grand_exchange_price": "7", - "examine": "A scroll for a dreadfowl familiar.", - "durability": null, + "id": "12445", "name": "Dreadfowl strike scroll", - "low_alchemy": "24", - "tradeable": "true", - "high_alchemy": "37", + "examine": "A scroll for a dreadfowl familiar.", "archery_ticket_price": "0", - "id": "12445" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,4}", + "tradeable": "true" }, { - "requirements": "{23,25}", - "ge_buy_limit": "5000", - "grand_exchange_price": "122", - "examine": "A scroll for a spirit kalphite familiar.", - "durability": null, + "id": "12446", "name": "Sandstorm scroll", - "low_alchemy": "67", - "tradeable": "true", - "high_alchemy": "101", + "examine": "A scroll for a spirit kalphite familiar.", "archery_ticket_price": "0", - "id": "12446" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,25}", + "tradeable": "true" }, { - "requirements": "{23,31}", - "ge_buy_limit": "1000", - "grand_exchange_price": "98", - "examine": "A scroll for a vampire bat familiar.", - "durability": null, + "id": "12447", "name": "Vampire touch scroll", - "low_alchemy": "97", - "tradeable": "true", - "high_alchemy": "146", + "examine": "A scroll for a vampire bat familiar.", "archery_ticket_price": "0", - "id": "12447" + "durability": null, + "ge_buy_limit": "1000", + "requirements": "{23,31}", + "tradeable": "true" }, { - "requirements": "{23,42}", - "ge_buy_limit": "5000", - "grand_exchange_price": "123", - "examine": "A scroll for an evil turnip familiar.", - "durability": null, + "id": "12448", "name": "Evil flames scroll", - "low_alchemy": "120", - "tradeable": "true", - "high_alchemy": "181", + "examine": "A scroll for an evil turnip familiar.", "archery_ticket_price": "0", - "id": "12448" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,42}", + "tradeable": "true" }, { - "requirements": "{23,74}", - "ge_buy_limit": "5000", - "grand_exchange_price": "219", - "examine": "A scroll for a granite lobster familiar.", - "durability": null, + "id": "12449", "name": "Crushing claw scroll", - "low_alchemy": "182", - "tradeable": "true", - "high_alchemy": "274", + "examine": "A scroll for a granite lobster familiar.", "archery_ticket_price": "0", - "id": "12449" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,74}", + "tradeable": "true" }, { - "requirements": "{23,75}", - "ge_buy_limit": "5000", - "grand_exchange_price": "221", - "examine": "A scroll for a praying mantis familiar.", - "durability": null, + "id": "12450", "name": "Mantis strike scroll", - "tradeable": "true", + "examine": "A scroll for a praying mantis familiar.", "archery_ticket_price": "0", - "id": "12450" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,75}", + "tradeable": "true" }, { - "requirements": "{23,71}", - "ge_buy_limit": "5000", - "grand_exchange_price": "12", - "examine": "A scroll for an arctic bear familiar.", - "durability": null, + "id": "12451", "name": "Arctic blast scroll", - "low_alchemy": "30", - "tradeable": "true", - "high_alchemy": "46", + "examine": "A scroll for an arctic bear familiar.", "archery_ticket_price": "0", - "id": "12451" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,71}", + "tradeable": "true" }, { - "requirements": "{23,66}", - "ge_buy_limit": "5000", - "grand_exchange_price": "10", - "examine": "A scroll for a barker toad familiar.", - "durability": null, + "id": "12452", "name": "Toad bark scroll", - "low_alchemy": "27", - "tradeable": "true", - "high_alchemy": "41", + "examine": "A scroll for a barker toad familiar.", "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", - "low_alchemy": "167", - "tradeable": "true", - "high_alchemy": "251", - "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", - "low_alchemy": "122", - "tradeable": "true", - "high_alchemy": "183", - "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", - "low_alchemy": "160", - "tradeable": "true", - "high_alchemy": "241", - "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", - "low_alchemy": "17", - "tradeable": "true", - "high_alchemy": "26", - "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", - "low_alchemy": "140", - "tradeable": "true", - "high_alchemy": "211", - "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", - "low_alchemy": "104", - "tradeable": "true", - "high_alchemy": "157", - "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", - "low_alchemy": "184", - "tradeable": "true", - "high_alchemy": "277", - "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", - "low_alchemy": "61", - "tradeable": "true", - "high_alchemy": "92", - "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", - "low_alchemy": "118", - "tradeable": "true", - "high_alchemy": "178", - "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", - "low_alchemy": "141", - "tradeable": "true", - "high_alchemy": "212", - "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", - "low_alchemy": "157", - "tradeable": "true", - "high_alchemy": "236", - "archery_ticket_price": "0", - "id": "12463" - }, - { "requirements": "{23,66}", + "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", - "grand_exchange_price": "198", - "examine": "A scroll for a mithril minotaur familiar.", - "durability": null, - "name": "Mith bull rush scroll", - "low_alchemy": "168", - "tradeable": "true", - "high_alchemy": "253", - "archery_ticket_price": "0", - "id": "12464" + "requirements": "{23,55}", + "tradeable": "true" }, { - "requirements": "{23,76}", + "id": "12454", + "name": "Abyssal drain scroll", + "examine": "A scroll for an abyssal parasite familiar.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "203", - "examine": "A scroll for an adamant minotaur familiar.", - "durability": null, - "name": "Addy bull rush scroll", - "low_alchemy": "160", - "tradeable": "true", - "high_alchemy": "241", - "archery_ticket_price": "0", - "id": "12465" + "requirements": "{23,54}", + "tradeable": "true" }, { - "requirements": "{23,86}", + "id": "12455", + "name": "Doomsphere scroll", + "examine": "A scroll for a karamthulhu overlord familiar.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "67", - "examine": "A scroll for a rune minotaur familiar.", - "durability": null, - "name": "Rune bull rush scroll", - "low_alchemy": "17", - "tradeable": "true", - "high_alchemy": "26", - "archery_ticket_price": "0", - "id": "12466" + "requirements": "{23,58}", + "tradeable": "true" }, { - "requirements": "{23,64}", + "id": "12456", + "name": "Spike shot scroll", + "examine": "A scroll for a spirit dagannoth familiar.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "171", - "examine": "A scroll for a stranger plant familiar.", - "durability": null, - "name": "Poisonous blast scroll", - "low_alchemy": "144", - "tradeable": "true", - "high_alchemy": "217", - "archery_ticket_price": "0", - "id": "12467" + "requirements": "{23,83}", + "tradeable": "true" }, { - "requirements": "{23,61}", + "id": "12457", + "name": "Acorn missile scroll", + "examine": "A scroll for a giant ent familiar.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "170", - "examine": "A scroll for a smoke devil familiar.", - "durability": null, - "name": "Dust cloud scroll", - "low_alchemy": "157", - "tradeable": "true", - "high_alchemy": "236", - "archery_ticket_price": "0", - "id": "12468" + "requirements": "{23,78}", + "tradeable": "true" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, - "name": "Hatchling dragon", + "id": "12458", + "name": "Petrifying gaze scroll", + "examine": "A scroll for a cockatrice variant familiar.", "archery_ticket_price": "0", - "id": "12469" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,43}", + "tradeable": "true" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, - "name": "Hatchling dragon", + "id": "12459", + "name": "Slime spray scroll", + "examine": "A scroll for a thorny snail familiar.", "archery_ticket_price": "0", - "id": "12471" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,13}", + "tradeable": "true" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, - "name": "Hatchling dragon", + "id": "12460", + "name": "Electric lash scroll", + "examine": "A scroll for a desert wyrm familiar.", "archery_ticket_price": "0", - "id": "12473" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,18}", + "tradeable": "true" }, { - "examine": "(hatchling) A hatchling <dragon colour> dragon.(baby) A bigger baby <colour> dragon.", - "durability": null, - "name": "Hatchling dragon", + "id": "12461", + "name": "Bronze bull rush scroll", + "examine": "A scroll for a bronze minotaur familiar.", "archery_ticket_price": "0", - "id": "12475" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,36}", + "tradeable": "true" }, { - "examine": "This will hatch into a mighty dragon.", - "durability": null, - "name": "Red dragon egg", - "tradeable": "false", - "destroy": "false", - "weight": "1", + "id": "12462", + "name": "Iron bull rush scroll", + "examine": "A scroll for an iron minotaur familiar.", "archery_ticket_price": "0", - "id": "12477" + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,46}", + "tradeable": "true" }, { - "examine": "This will hatch into a mighty dragon.", - "durability": null, - "name": "Blue dragon egg", - "tradeable": "false", - "destroy": "false", - "weight": "1", + "id": "12463", + "name": "Steel bull rush scroll", + "examine": "A scroll for a steel minotaur familiar.", "archery_ticket_price": "0", - "id": "12478" - }, - { - "examine": "This will hatch into a mighty dragon.", "durability": null, - "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", - "destroy": "false", - "weight": "1", - "archery_ticket_price": "0", - "id": "12480" - }, - { - "examine": "(Baby) Can't fly and can barely walk, but adorable nonetheless. (adult) Emperor of all he surveys.", - "durability": null, - "name": "Baby penguin", - "archery_ticket_price": "0", - "id": "12481" - }, - { - "examine": "I can hatch this in an Incubator.", - "durability": null, - "name": "Penguin egg", - "archery_ticket_price": "0", - "id": "12483" - }, - { - "examine": "Chick: A raven chick.", - "durability": null, - "name": "Raven chick", - "archery_ticket_price": "0", - "id": "12484" - }, - { - "examine": "Chick: A raven chick.", - "durability": null, - "name": "Raven", - "archery_ticket_price": "0", - "id": "12485" - }, - { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, - "name": "Baby raccoon", - "archery_ticket_price": "0", - "id": "12486" - }, - { - "examine": "(Baby) A stripy little baby raccoon.(Adult) He can run with us.", - "durability": null, - "name": "Raccoon", - "archery_ticket_price": "0", - "id": "12487" - }, - { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, - "name": "Baby gecko", - "archery_ticket_price": "0", - "id": "12488" - }, - { - "examine": "(Baby) It's so adorably tiny! (Adult) How cute!", - "durability": null, - "name": "Gecko", - "archery_ticket_price": "0", - "id": "12489" - }, - { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, - "name": "Baby squirrel", - "archery_ticket_price": "0", - "id": "12490" - }, - { - "examine": "Adult: An experienced nut-thief.Baby: A tiny nut-thief.", - "durability": null, - "name": "Squirrel", - "archery_ticket_price": "0", - "id": "12491" - }, - { - "examine": "It's a colour-coordinated lizard!", - "durability": null, - "name": "Baby chameleon", - "archery_ticket_price": "0", - "id": "12492" - }, - { - "examine": "It's a colour-coordinated lizard!", - "durability": null, - "name": "Chameleon", - "archery_ticket_price": "0", - "id": "12493" - }, - { - "examine": "A lizard's egg.", - "durability": null, - "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" - }, - { - "examine": "(Baby) It's a tiny, cheeky monkey. (Adult) Hide the bananas!", - "durability": null, - "name": "Baby monkey", - "archery_ticket_price": "0", - "id": "12496" - }, - { - "examine": "If you see them circling: run.", - "durability": null, - "name": "Vulture chick", - "archery_ticket_price": "0", - "id": "12498" - }, - { - "examine": "If you see them circling: run.", - "durability": null, - "name": "Vulture", - "archery_ticket_price": "0", - "id": "12499" - }, - { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, - "name": "Baby giant crab", - "archery_ticket_price": "0", - "id": "12500" - }, - { - "requirements": "{23,40}", - "examine": "Baby: Little NipperAdult: Bigger Nipper.", - "durability": null, - "name": "Giant crab", - "archery_ticket_price": "0", - "id": "12501" - }, - { - "examine": "Apparently, this token is worth one baby giant crab to someone called Nung.", - "durability": null, - "name": "Crunchy claw token", - "archery_ticket_price": "0", - "id": "12502" - }, - { - "examine": "A fluffy chick.", - "durability": null, - "name": "Saradomin chick", - "archery_ticket_price": "0", - "id": "12503" - }, - { - "examine": "Lovely blue plumage.", - "durability": null, - "name": "Saradomin bird", - "archery_ticket_price": "0", - "id": "12504" - }, - { - "examine": "A fantastic saradomin owl.", - "durability": null, - "name": "Saradomin owl", - "archery_ticket_price": "0", - "id": "12505" - }, - { - "examine": "A fluffy chick.", - "durability": null, - "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, - "name": "Guthix chick", - "archery_ticket_price": "0", - "id": "12509" - }, - { - "examine": "Lovely green plumage.", - "durability": null, - "name": "Guthix bird", - "archery_ticket_price": "0", - "id": "12510" - }, - { - "examine": "An impressive Guthix raptor.", - "durability": null, - "name": "Guthix raptor", - "archery_ticket_price": "0", - "id": "12511" - }, - { - "requirements": "{4,23}", - "examine": "It's lively and energetic.", - "durability": null, - "name": "Terrier puppy", - "archery_ticket_price": "0", - "id": "12512" - }, - { - "requirements": "{4,23}", - "examine": "It's lively and energetic.", - "durability": null, - "name": "Terrier", - "archery_ticket_price": "0", - "id": "12513" - }, - { - "requirements": "{4,23}", - "examine": "This is one fast little pooch.", - "durability": null, - "name": "Greyhound puppy", - "archery_ticket_price": "0", - "id": "12514" - }, - { - "requirements": "{4,23}", - "examine": "An aerodynamic doggy.", - "durability": null, - "name": "Greyhound", - "archery_ticket_price": "0", - "id": "12515" - }, - { - "requirements": "{4,23}", - "examine": "Soft but strong... and cute.", - "durability": null, - "name": "Labrador puppy", - "archery_ticket_price": "0", - "id": "12516" - }, - { - "requirements": "{4,23}", - "examine": "My most faithful friend.", - "durability": null, - "name": "Labrador", - "archery_ticket_price": "0", - "id": "12517" - }, - { - "requirements": "{4,23}", - "examine": "Now, if only I could find another hundred...", - "durability": null, - "name": "Dalmatian puppy", - "archery_ticket_price": "0", - "id": "12518" - }, - { - "requirements": "{4,23}", - "examine": "A purebred doggy.", - "durability": null, - "name": "Dalmatian", - "archery_ticket_price": "0", - "id": "12519" - }, - { - "requirements": "{4,23}", - "examine": "This little puppy is very quiet.", - "durability": null, - "name": "Sheepdog puppy", - "archery_ticket_price": "0", - "id": "12520" - }, - { - "requirements": "{4,23}", - "examine": "Come by! Come by!", - "durability": null, - "name": "Sheepdog", - "archery_ticket_price": "0", - "id": "12521" - }, - { - "requirements": "{4,23}", - "examine": "One proud little puppy.", - "durability": null, - "name": "Bulldog 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" - }, - { - "shop_price": "99000", - "examine": "The cape worn by master summoners.", - "durability": null, - "name": "Summoning cape", - "low_alchemy": "39600", - "high_alchemy": "59400", - "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" - }, - { - "shop_price": "1", - "examine": "A charm used to summon familiars.", - "durability": null, - "name": "Gold charm", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "12527" - }, - { - "examine": "A key to Pikkupstix's trapdoor.", - "durability": null, - "name": "Trapdoor key", - "archery_ticket_price": "0", - "id": "12528" - }, - { - "shop_price": "25", - "examine": "Shards of an obelisk.", - "grand_exchange_price": "24", - "durability": null, - "name": "Spirit shards", - "tradeable": "true", - "low_alchemy": "10", - "high_alchemy": "15", - "archery_ticket_price": "0", - "id": "12530" - }, - { + "ge_buy_limit": "5000", "requirements": "{23,56}", + "tradeable": "true" + }, + { + "id": "12464", + "name": "Mith bull rush scroll", + "examine": "A scroll for a mithril minotaur familiar.", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "5000", - "grand_exchange_price": "1914", + "requirements": "{23,66}", + "tradeable": "true" + }, + { + "id": "12465", + "name": "Addy bull rush scroll", + "examine": "A scroll for an adamant minotaur familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,76}", + "tradeable": "true" + }, + { + "id": "12466", + "name": "Rune bull rush scroll", + "examine": "A scroll for a rune minotaur familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,86}", + "tradeable": "true" + }, + { + "id": "12467", + "name": "Poisonous blast scroll", + "examine": "A scroll for a stranger plant familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,64}", + "tradeable": "true" + }, + { + "id": "12468", + "name": "Dust cloud scroll", + "examine": "A scroll for a smoke devil familiar.", + "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "5000", + "requirements": "{23,61}", + "tradeable": "true" + }, + { + "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", + "durability": null + }, + { + "id": "12473", + "name": "Hatchling dragon", + "examine": "A hatchling green dragon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12475", + "name": "Hatchling dragon", + "examine": "A hatchling black dragon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12477", + "name": "Red dragon egg", + "examine": "This will hatch into a mighty dragon.", + "archery_ticket_price": "0", + "destroy": "false", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "12478", + "name": "Blue dragon egg", + "examine": "This will hatch into a mighty dragon.", + "archery_ticket_price": "0", + "destroy": "false", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "id": "12479", + "name": "Green dragon egg", + "examine": "This will hatch into a mighty dragon.", + "archery_ticket_price": "0", + "destroy": "false", + "durability": null, + "tradeable": "false", + "weight": "1" + }, + { + "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", + "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", + "durability": null, + "tradeable": "false" + }, + { + "id": "12484", + "name": "Raven chick", + "examine": "Chick: A raven chick.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12485", + "name": "Raven", + "examine": "Chick: A raven chick.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12486", + "name": "Baby raccoon", + "examine": "A stripy little baby raccoon.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12487", + "name": "Raccoon", + "examine": "He can run with us.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12488", + "name": "Baby gecko", + "examine": "It's so adorably tiny!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12489", + "name": "Gecko", + "examine": "How cute!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12490", + "name": "Baby squirrel", + "examine": "A tiny nut-thief.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12491", + "name": "Squirrel", + "examine": "An experienced nut-thief.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12492", + "name": "Baby chameleon", + "examine": "It's a colour-coordinated lizard!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12493", + "name": "Chameleon", + "examine": "It's a colour-coordinated lizard!", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12494", + "name": "Chameleon egg", + "examine": "A lizard's egg.", + "archery_ticket_price": "0", + "durability": null, + "weight": "1" + }, + { + "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", + "durability": null + }, + { + "id": "12497", + "name": "Monkey", + "examine": "Hide the bananas!" + }, + { + "id": "12498", + "name": "Vulture chick", + "examine": "If you see them circling: run.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12499", + "name": "Vulture", + "examine": "If you see them circling: run.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12500", + "name": "Baby giant crab", + "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", + "durability": null, + "requirements": "{23,40}" + }, + { + "id": "12502", + "name": "Crunchy claw token", + "examine": "Apparently, this token is worth one baby giant crab to someone called Nung.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12503", + "name": "Saradomin chick", + "examine": "A fluffy chick.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12504", + "name": "Saradomin bird", + "examine": "Lovely blue plumage.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12505", + "name": "Saradomin owl", + "examine": "A fantastic saradomin owl.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12506", + "name": "Zamorak chick", + "examine": "A fluffy chick.", + "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", + "durability": null + }, + { + "id": "12510", + "name": "Guthix bird", + "examine": "Lovely green plumage.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12511", + "name": "Guthix raptor", + "examine": "An impressive Guthix raptor.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12512", + "name": "Terrier puppy", + "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", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12514", + "name": "Greyhound puppy", + "examine": "This is one fast little pooch.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12515", + "name": "Greyhound", + "examine": "An aerodynamic doggy.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12516", + "name": "Labrador puppy", + "examine": "Soft but strong... and cute.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12517", + "name": "Labrador", + "examine": "My most faithful friend.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12518", + "name": "Dalmatian puppy", + "examine": "Now, if only I could find another hundred...", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12519", + "name": "Dalmatian", + "examine": "A purebred doggy.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12520", + "name": "Sheepdog puppy", + "examine": "This little puppy is very quiet.", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12521", + "name": "Sheepdog", + "examine": "Come by! Come by!", + "archery_ticket_price": "0", + "durability": null, + "requirements": "{4,23}" + }, + { + "id": "12522", + "name": "Bulldog puppy", + "examine": "One proud little puppy.", + "archery_ticket_price": "0", + "durability": null, + "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", + "weight": "0.4" + }, + { + "id": "12527", + "name": "Gold charm", + "examine": "A charm used to summon familiars.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "1", + "tradeable": "false" + }, + { + "id": "12528", + "name": "Trapdoor key", + "examine": "A key to Pikkupstix's trapdoor.", + "archery_ticket_price": "0", + "durability": null + }, + { + "id": "12530", + "name": "Spirit shards", + "examine": "Shards of an obelisk.", + "archery_ticket_price": "0", + "durability": null, + "shop_price": "25", + "tradeable": "true" + }, + { + "id": "12531", + "name": "Ibis pouch", "examine": "I can summon an ibis familiar with this.", - "durability": null, - "name": "Ibis pouch", - "tradeable": "true", - "low_alchemy": "1259", - "high_alchemy": "1889", "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", - "low_alchemy": "1259", - "high_alchemy": "1889", "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", - "low_alchemy": "23", - "tradeable": "true", - "high_alchemy": "35", + "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", - "low_alchemy": "200", - "high_alchemy": "300", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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?", - "durability": null, - "low_alchemy": "24", - "high_alchemy": "36", - "weight": "1", - "equipment_slot": "0", - "grand_exchange_price": "1004", - "name": "Ogre wig", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12559" + "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", + "id": "12561", + "name": "Ogre kilt", "examine": "It's a little big on me.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "7", - "grand_exchange_price": "197", - "name": "Ogre kilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12561" - }, - { "ge_buy_limit": "100", - "grand_exchange_price": "197", - "durability": null, - "name": "Ogre kilt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12562" - }, - { "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, - "low_alchemy": "24", - "high_alchemy": "36", - "weight": "1", "equipment_slot": "4", + "ge_buy_limit": "100", "remove_sleeves": "true", - "grand_exchange_price": "536", - "name": "Ogre top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12563" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "536", - "durability": null, - "name": "Ogre top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12564" - }, - { "shop_price": "60", + "tradeable": "true", + "weight": "1" + }, + { + "id": "12564", + "name": "Ogre top", + "archery_ticket_price": "0", + "durability": null, "ge_buy_limit": "100", + "tradeable": "true" + }, + { + "id": "12565", + "name": "Ogre boots", "examine": "Good for stomping.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "24", - "high_alchemy": "36", - "weight": "3", + "equip_audio": "2237", "equipment_slot": "10", - "grand_exchange_price": "420", - "name": "Ogre boots", + "ge_buy_limit": "100", + "shop_price": "60", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12565" + "weight": "3" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "420", + "id": "12566", + "name": "Ogre boots", + "archery_ticket_price": "0", "durability": null, - "name": "Ogre boots", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "12566" + "ge_buy_limit": "100", + "tradeable": "true" }, { - "ge_buy_limit": "100", + "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", - "low_alchemy": "24", - "high_alchemy": "36", - "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", + "id": "12568", + "name": "Davy kebbit hat", "examine": "A furry hat with an unnerving tail.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "weight": "1", "equipment_slot": "0", - "grand_exchange_price": "3656", - "name": "Davy kebbit hat", + "ge_buy_limit": "100", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", - "id": "12568" + "weight": "1" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "3656", - "durability": null, + "id": "12569", "name": "Davy kebbit hat", - "tradeable": "true", "archery_ticket_price": "0", - "id": "12569" + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" }, { - "requirements": "{0,5}", - "ge_buy_limit": "100", - "turn90cw_anim": "8684", - "examine": "A big piece of tree.", - "walk_anim": "8684", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "weight": "10", - "turn90ccw_anim": "8684", - "attack_speed": "7", - "weapon_interface": "10", - "turn180_anim": "8684", - "render_anim": "1157", - "equipment_slot": "3", - "grand_exchange_price": "77", - "stand_anim": "8683", - "name": "Ogre club", - "tradeable": "true", - "run_anim": "8685", - "archery_ticket_price": "0", "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "1", - "high_alchemy": "2", - "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, - "low_alchemy": "1", - "high_alchemy": "2", - "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, - "low_alchemy": "1", - "high_alchemy": "2", - "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, - "low_alchemy": "1", - "high_alchemy": "2", - "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", - "low_alchemy": "50", - "high_alchemy": "75", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "80", - "high_alchemy": "120", + "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", - "low_alchemy": "8", - "high_alchemy": "12", + "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", - "high_alchemy": "1", - "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", - "low_alchemy": "160", - "high_alchemy": "160", + "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", - "low_alchemy": "24", - "high_alchemy": "36", + "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "low_alchemy": "4", - "high_alchemy": "6", - "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", - "high_alchemy": "1", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "2", - "high_alchemy": "3", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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, - "low_alchemy": "1408", - "high_alchemy": "2112", - "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.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "0.9", - "absorb": "0,2,1", - "equipment_slot": "0", - "grand_exchange_price": "1040", - "name": "Snakeskin bandana (e)", - "tradeable": "true", - "archery_ticket_price": "0", "id": "12660", - "bonuses": "0,0,0,-5,4,2,4,4,2,2,6,0,0,0,0" - }, - { - "remove_head": "true", + "name": "Snakeskin bandana (e)", "examine": "Lightweight head protection.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "weight": "0.9", "absorb": "0,2,1", - "equipment_slot": "0", - "grand_exchange_price": "1040", - "name": "Snakeskin bandana (charged)", - "tradeable": "true", "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": "12661", - "bonuses": "0,0,0,-5,4,2,4,4,2,2,6,0,0,0,0" + "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" }, { - "remove_head": "true", - "examine": "A wooden helmet.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "weight": "0.9", - "absorb": "3,1,0", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "5623", - "name": "Splitbark helm (e)", - "tradeable": "true", - "archery_ticket_price": "0", "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, - "low_alchemy": "4000", - "high_alchemy": "6000", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "8000", - "high_alchemy": "12000", - "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, - "low_alchemy": "8000", - "high_alchemy": "12000", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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, - "low_alchemy": "31200", - "high_alchemy": "46800", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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", - "low_alchemy": "1669", - "high_alchemy": "2504", "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", - "low_alchemy": "1669", - "high_alchemy": "2504", - "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", - "low_alchemy": "179", - "high_alchemy": "269", - "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", - "low_alchemy": "179", - "high_alchemy": "269", - "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", - "low_alchemy": "909", - "high_alchemy": "1364", - "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", - "low_alchemy": "909", - "high_alchemy": "1364", - "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", - "low_alchemy": "1579", - "high_alchemy": "2369", - "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", - "low_alchemy": "1579", - "high_alchemy": "2369", - "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", - "low_alchemy": "1719", - "high_alchemy": "2579", - "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", - "low_alchemy": "1719", - "high_alchemy": "2579", - "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", - "low_alchemy": "2389", - "high_alchemy": "3584", - "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", - "low_alchemy": "2389", - "high_alchemy": "3584", - "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", - "low_alchemy": "2359", - "high_alchemy": "3539", - "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", - "low_alchemy": "2359", - "high_alchemy": "3539", - "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", - "low_alchemy": "1949", - "high_alchemy": "2924", - "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", - "low_alchemy": "1949", - "high_alchemy": "2924", - "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", - "low_alchemy": "2119", - "high_alchemy": "3179", - "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", - "low_alchemy": "2119", - "high_alchemy": "3179", - "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", - "low_alchemy": "1909", - "high_alchemy": "2864", - "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", - "low_alchemy": "1909", - "high_alchemy": "2864", - "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", - "low_alchemy": "1299", - "high_alchemy": "1949", - "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", - "low_alchemy": "1299", - "high_alchemy": "1949", - "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", - "low_alchemy": "909", - "high_alchemy": "1364", - "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", - "low_alchemy": "909", - "high_alchemy": "1364", - "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", - "low_alchemy": "1009", - "high_alchemy": "1514", - "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", - "low_alchemy": "1009", - "high_alchemy": "1514", - "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", - "low_alchemy": "2149", - "high_alchemy": "3224", - "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", - "low_alchemy": "2149", - "high_alchemy": "3224", - "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", - "low_alchemy": "2189", - "high_alchemy": "3284", - "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", - "low_alchemy": "2189", - "high_alchemy": "3284", - "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", - "low_alchemy": "2149", - "high_alchemy": "3224", - "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", - "low_alchemy": "2149", - "high_alchemy": "3224", - "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", - "low_alchemy": "809", - "high_alchemy": "1214", - "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", - "low_alchemy": "809", - "high_alchemy": "1214", - "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", - "low_alchemy": "1709", - "high_alchemy": "2564", - "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", - "low_alchemy": "1709", - "high_alchemy": "2564", - "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", - "low_alchemy": "1699", - "high_alchemy": "2549", - "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", - "low_alchemy": "1699", - "high_alchemy": "2549", - "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", - "low_alchemy": "909", - "high_alchemy": "1364", - "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", - "low_alchemy": "909", - "high_alchemy": "1364", - "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", - "low_alchemy": "1279", - "high_alchemy": "1919", - "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", - "low_alchemy": "1279", - "high_alchemy": "1919", - "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", - "low_alchemy": "909", - "high_alchemy": "1364", - "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", - "low_alchemy": "909", - "high_alchemy": "1364", - "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", - "low_alchemy": "959", - "high_alchemy": "1439", - "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", - "low_alchemy": "959", - "high_alchemy": "1439", - "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", - "low_alchemy": "2149", - "high_alchemy": "3224", - "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", - "low_alchemy": "2149", - "high_alchemy": "3224", - "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", - "low_alchemy": "214", - "tradeable": "true", - "high_alchemy": "322", - "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", - "low_alchemy": "194", - "tradeable": "true", - "high_alchemy": "292", - "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", - "low_alchemy": "211", - "tradeable": "true", - "high_alchemy": "317", - "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", - "low_alchemy": "129", - "tradeable": "true", - "high_alchemy": "194", - "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", - "low_alchemy": "214", - "tradeable": "true", - "high_alchemy": "322", - "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", - "low_alchemy": "127", - "tradeable": "true", - "high_alchemy": "191", - "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", - "low_alchemy": "95", - "tradeable": "true", - "high_alchemy": "143", - "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", - "low_alchemy": "190", - "tradeable": "true", - "high_alchemy": "286", - "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", - "low_alchemy": "166", - "tradeable": "true", - "high_alchemy": "250", - "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", - "low_alchemy": "238", - "tradeable": "true", - "high_alchemy": "358", + "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", - "low_alchemy": "170", - "tradeable": "true", - "high_alchemy": "256", + "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", - "low_alchemy": "169", - "tradeable": "true", - "high_alchemy": "254", + "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", - "low_alchemy": "211", - "tradeable": "true", - "high_alchemy": "317", + "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", - "low_alchemy": "17", - "tradeable": "true", - "high_alchemy": "26", + "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", - "low_alchemy": "80", - "tradeable": "true", - "high_alchemy": "121", - "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", - "low_alchemy": "171", - "tradeable": "true", - "high_alchemy": "257", - "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", - "low_alchemy": "157", - "tradeable": "true", - "high_alchemy": "236", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "10000", - "high_alchemy": "15000", - "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, - "low_alchemy": "10000", - "high_alchemy": "15000", - "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, - "low_alchemy": "10000", - "high_alchemy": "15000", - "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, - "low_alchemy": "10000", - "high_alchemy": "15000", - "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, - "low_alchemy": "10000", - "high_alchemy": "15000", - "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, - "low_alchemy": "10000", - "high_alchemy": "15000", "absorb": "4,3,0", - "equipment_slot": "0", - "grand_exchange_price": "78900", - "name": "Battle hood 0", - "tradeable": "false", "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": "false", + "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "16000", - "high_alchemy": "24000", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 100", + "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 80", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 60", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 40", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "6915", - "name": "Druidic mage hood 20", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "7", - "equipment_slot": "0", - "grand_exchange_price": "8504", - "name": "Druidic mage hood 0", "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": "false", + "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, - "low_alchemy": "2400", - "high_alchemy": "3600", - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "80273", - "name": "Druidic mage top 100", + "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, - "low_alchemy": "2400", - "high_alchemy": "3600", - "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", - "archery_ticket_price": "0", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", + "tradeable": "false", + "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, - "low_alchemy": "2400", - "high_alchemy": "3600", - "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", - "archery_ticket_price": "0", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", + "tradeable": "false", + "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, - "low_alchemy": "2400", - "high_alchemy": "3600", - "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", - "archery_ticket_price": "0", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", + "tradeable": "false", + "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, - "low_alchemy": "2400", - "high_alchemy": "3600", - "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", - "archery_ticket_price": "0", + "requirements": "{1,10}-{6,20}", + "shop_price": "300", + "tradeable": "false", + "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, - "low_alchemy": "2400", - "high_alchemy": "3600", - "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", - "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": "false", + "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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, - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "3", "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "60800", - "name": "Druidic mage bottom 0", - "tradeable": "false", "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": "false", + "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, - "low_alchemy": "1620", - "high_alchemy": "2430", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 100", - "tradeable": "true", + "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, - "low_alchemy": "1620", - "high_alchemy": "2430", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 80", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "1620", - "high_alchemy": "2430", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 60", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "1620", - "high_alchemy": "2430", - "weight": "5", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 40", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "1620", - "high_alchemy": "2430", - "weight": "5", "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "2271", - "name": "Adamant spikeshield 20", - "tradeable": "true", "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, - "low_alchemy": "1620", - "high_alchemy": "2430", - "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, - "low_alchemy": "2456", - "high_alchemy": "3684", - "weight": "7", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 100", - "tradeable": "true", + "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, - "low_alchemy": "2456", - "high_alchemy": "3684", - "weight": "7", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 80", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "2456", - "high_alchemy": "3684", - "weight": "7", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 60", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "2456", - "high_alchemy": "3684", - "weight": "7", - "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 40", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "2456", - "high_alchemy": "3684", - "weight": "7", "absorb": "3,0,6", - "equipment_slot": "5", - "grand_exchange_price": "19190", - "name": "Adamant berserker shield 20", - "tradeable": "true", "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, - "low_alchemy": "2456", - "high_alchemy": "3684", - "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, - "low_alchemy": "2576", - "high_alchemy": "3864", - "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, - "low_alchemy": "2576", - "high_alchemy": "3864", - "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, - "low_alchemy": "2576", - "high_alchemy": "3864", - "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, - "low_alchemy": "2576", - "high_alchemy": "3864", - "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, - "low_alchemy": "2576", - "high_alchemy": "3864", - "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, - "low_alchemy": "2576", - "high_alchemy": "3864", - "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, - "low_alchemy": "25760", - "high_alchemy": "38640", - "weight": "6", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 100", - "tradeable": "true", + "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, - "low_alchemy": "25760", - "high_alchemy": "38640", - "weight": "6", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 80", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "25760", - "high_alchemy": "38640", - "weight": "6", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 60", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "25760", - "high_alchemy": "38640", - "weight": "6", - "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 40", - "tradeable": "true", - "archery_ticket_price": "0", + "tradeable": "false", + "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, - "low_alchemy": "25760", - "high_alchemy": "38640", - "weight": "6", "absorb": "3,0,7", - "equipment_slot": "5", - "grand_exchange_price": "82000", - "name": "Rune berserker shield 20", - "tradeable": "true", "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, - "low_alchemy": "25760", - "high_alchemy": "38640", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "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, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "0.9", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "92700", - "name": "Green d'hide coif 0", "tradeable": "false", + "weight": "0.9" + }, + { + "id": "12940", + "name": "Green d'hide coif 20", + "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", + "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": "false", + "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,50}-{4,40}", - "shop_price": "200", - "examine": "A coif made of blue dragonhide.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,50}-{4,40}", - "shop_price": "200", - "examine": "A coif made of blue dragonhide.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,50}-{4,40}", - "shop_price": "200", - "examine": "Fully charged: A coif made of blue dragonhide. Fully degraded: A worn out coif made of blue dragonhide.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,50}-{4,40}", - "shop_price": "200", - "examine": "A coif made of blue dragonhide.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", "id": "12946", - "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,50}-{4,40}", - "shop_price": "200", + "name": "Blue d'hide coif 40", "examine": "A coif made of blue dragonhide.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "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", + "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": "12947", - "bonuses": "0,0,0,-1,5,4,6,8,4,6,5,0,0,0,0" + "name": "Blue d'hide coif 20", + "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,50}-{4,40}", - "shop_price": "200", - "ge_buy_limit": "100", - "examine": "A worn out coif made of blue dragonhide.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "131900", - "name": "Blue d'hide coif 0", - "tradeable": "false", - "archery_ticket_price": "0", "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": "false", + "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,60}-{4,40}", - "shop_price": "300", - "examine": "A coif made of red dragonhide.", - "durability": null, - "low_alchemy": "1400", - "high_alchemy": "2100", - "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,60}-{4,40}", - "shop_price": "300", - "examine": "A coif made of red dragonhide.", - "durability": null, - "low_alchemy": "1400", - "high_alchemy": "2100", - "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", "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,60}-{4,40}", - "shop_price": "300", - "examine": "Fully charged: A coif made of red dragonhide.Fully degraded: A worn out coif made of red dragonhide.", - "durability": null, - "low_alchemy": "1400", - "high_alchemy": "2100", - "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", "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,60}-{4,40}", - "shop_price": "300", - "examine": "A coif made of red dragonhide.", - "durability": null, - "low_alchemy": "1400", - "high_alchemy": "2100", - "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", "id": "12953", - "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,60}-{4,40}", - "shop_price": "300", + "name": "Red d'hide coif 40", "examine": "A coif made of red dragonhide.", - "durability": null, - "low_alchemy": "1400", - "high_alchemy": "2100", - "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", + "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": "12954", - "bonuses": "0,0,0,-1,6,4,6,9,4,6,6,0,0,0,0" + "name": "Red d'hide coif 20", + "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,60}-{4,40}", - "shop_price": "300", - "ge_buy_limit": "100", - "examine": "A worn out coif made of red dragonhide.", - "durability": null, - "low_alchemy": "1400", - "high_alchemy": "2100", - "weight": "1", - "absorb": "0,3,1", - "equipment_slot": "0", - "grand_exchange_price": "156600", - "name": "Red d'hide coif 0", - "tradeable": "false", - "archery_ticket_price": "0", "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": "false", + "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,70}-{4,40}", - "shop_price": "500", - "examine": "A coif made of black dragonhide.", - "durability": null, - "low_alchemy": "2200", - "high_alchemy": "3300", - "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,40}", - "shop_price": "500", - "examine": "A coif made of black dragonhide.", - "durability": null, - "low_alchemy": "2200", - "high_alchemy": "3300", - "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", "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,40}", - "shop_price": "500", - "examine": "A coif made of black dragonhide.", - "durability": null, - "low_alchemy": "2200", - "high_alchemy": "3300", - "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", "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,40}", - "shop_price": "500", - "examine": "A coif made of black dragonhide.", - "durability": null, - "low_alchemy": "2200", - "high_alchemy": "3300", - "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", "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", + "tradeable": "false", + "weight": "1" }, { - "remove_head": "true", - "requirements": "{1,70}-{4,40}", - "shop_price": "500", - "examine": "Fully charged: A coif made of black dragonhide. Fully degraded: A worn out coif made of black dragonhide.", - "durability": null, - "low_alchemy": "2200", - "high_alchemy": "3300", - "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", "id": "12961", - "bonuses": "0,0,0,-1,7,4,7,9,4,7,7,0,0,0,0" - }, - { - "remove_head": "true", - "requirements": "{1,70}-{4,40}", - "shop_price": "500", - "ge_buy_limit": "100", - "examine": "A coif made of black dragonhide.", - "durability": null, - "low_alchemy": "2200", - "high_alchemy": "3300", - "weight": "1", + "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", - "equipment_slot": "0", - "grand_exchange_price": "229000", - "name": "Black d'hide coif 0", - "tradeable": "false", "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", + "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": "false", + "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "0.8", - "equipment_slot": "0", - "grand_exchange_price": "6511", - "name": "Combat hood 0", - "tradeable": "false", "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": "false", + "examine": "A magical hood, especially created for combat spells.", "archery_ticket_price": "0", - "id": "12970" + "bonuses": "0,0,0,1,0,0,0,0,1,0,2,0,0,0,0", + "durability": null, + "equipment_slot": "0", + "ge_buy_limit": "10", + "remove_head": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "50", + "tradeable": "true", + "weight": "0.8" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "150", - "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "4", - "absorb": "2,1,0", - "equipment_slot": "4", - "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 100", - "tradeable": "false", + "id": "12970", + "name": "Combat hood 0", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "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", + "name": "Combat robe top 100", "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "4", "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, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 80", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "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", + "name": "Combat robe top 80", "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "4", "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, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 60", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "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", + "name": "Combat robe top 60", "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "4", "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, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 40", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "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", + "name": "Combat robe top 40", "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "4", "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, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "37700", - "name": "Combat robe top 20", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "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", + "name": "Combat robe top 20", "examine": "A magical robe top, especially made for combat spells.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "weight": "4", "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, "equipment_slot": "4", "remove_sleeves": "true", - "grand_exchange_price": "61500", - "name": "Combat robe top 0", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "4" + }, + { "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": "false", + "examine": "A magical robe top, especially made for combat spells.", + "absorb": "2,1,0", "archery_ticket_price": "0", - "id": "12977" + "bonuses": "0,0,0,3,0,0,0,0,3,0,4,0,0,0,0", + "durability": null, + "equipment_slot": "4", + "ge_buy_limit": "10", + "remove_sleeves": "true", + "requirements": "{1,10}-{6,20}", + "shop_price": "150", + "tradeable": "true", + "weight": "4" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "weight": "3", - "absorb": "1,0,0", - "equipment_slot": "7", - "grand_exchange_price": "50200", - "name": "Combat robe bottom 100", - "tradeable": "false", + "id": "12977", + "name": "Combat robe top 0", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "10", + "tradeable": "true" + }, + { "id": "12978", - "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" + "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", + "weight": "3" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "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" + "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" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "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" + "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" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "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" + "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" }, { - "requirements": "{1,20}-{6,10}", - "shop_price": "100", - "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "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", + "name": "Combat robe bottom 20", "examine": "A magical robe bottom, especially created for combat spells.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "weight": "3", "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", - "grand_exchange_price": "47800", - "name": "Combat robe bottom 0", + "requirements": "{1,10}-{6,20}", + "shop_price": "100", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "3" + }, + { "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": "false", + "examine": "A magical robe bottom, especially created for combat spells.", + "absorb": "1,0,0", "archery_ticket_price": "0", - "id": "12984" + "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" }, { - "shop_price": "15", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "3694", - "durability": null, - "name": "Bronze gauntlets", - "low_alchemy": "40", - "high_alchemy": "60", + "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", - "equipment_slot": "9" - }, - { + "durability": null, + "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "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": "false", - "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", - "low_alchemy": "120", - "high_alchemy": "180", "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", - "low_alchemy": "120", - "high_alchemy": "180", - "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" + "durability": null, + "equipment_slot": "9", + "ge_buy_limit": "100", + "shop_price": "15", + "tradeable": "true" }, { - "ge_buy_limit": "100", - "grand_exchange_price": "16000", + "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", - "tradeable": "false", + "examine": "I have to keep my hands safe!", "archery_ticket_price": "0", - "id": "12990" + "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" }, { - "requirements": "{1,5}", - "shop_price": "50", - "examine": "I have to keep my hands safe!", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "10", - "equipment_slot": "9", - "grand_exchange_price": "29844", - "name": "Steel gauntlets", + "id": "12990", + "name": "Worn-out iron gauntlets", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12991", - "bonuses": "0,0,0,-1,-1,4,5,5,-1,4,4,0,0,0,0" - }, - { + "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", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe!", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "10", - "equipment_slot": "9", - "grand_exchange_price": "27000", - "name": "Worn-out steel gauntlets", - "archery_ticket_price": "0", + "tradeable": "false", + "weight": "10" + }, + { "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": "false", + "examine": "I have to keep my hands safe!", "archery_ticket_price": "0", - "id": "12993" + "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" }, { - "requirements": "{1,10}", - "shop_price": "75", - "examine": "I have to keep my hands safe!", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "50808", - "name": "Black gauntlets", + "id": "12993", + "name": "Worn-out steel gauntlets", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "12994", - "bonuses": "0,0,0,-1,-1,5,6,6,-1,5,5,0,0,0,0" - }, - { + "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", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe!", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "40100", - "name": "Worn-out black gauntlets", - "archery_ticket_price": "0", + "tradeable": "false", + "weight": "0.2" + }, + { "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": "false", + "examine": "I have to keep my hands safe!", "archery_ticket_price": "0", - "id": "12996" + "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" }, { - "requirements": "{1,20}", - "shop_price": "100", - "examine": "I have to keep my hands safe!", - "grand_exchange_price": "60064", - "durability": null, - "name": "Mithril gauntlets", - "low_alchemy": "1200", - "high_alchemy": "1800", + "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", - "equipment_slot": "9" - }, - { + "durability": null, + "equipment_slot": "9", "requirements": "{1,20}", "shop_price": "100", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe!", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "equipment_slot": "9", - "grand_exchange_price": "54200", - "name": "Worn-out mithril gauntlets", - "archery_ticket_price": "0", + "tradeable": "false" + }, + { "id": "12998", - "bonuses": "0,0,0,-1,-1,2,2,2,-1,2,2,0,0,0,0" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "54200", - "durability": null, "name": "Worn-out mithril gauntlets", - "tradeable": "false", + "examine": "I have to keep my hands safe!", "archery_ticket_price": "0", - "id": "12999" + "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" }, { - "requirements": "{1,30}", - "shop_price": "150", - "examine": "I have to keep my hands safe! Uncharged: A bit worse for wear.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "74526", - "name": "Adamant gauntlets", - "tradeable": "false", + "id": "12999", + "name": "Worn-out mithril gauntlets", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "13000", - "bonuses": "0,0,0,-1,-1,8,9,9,-1,8,8,1,0,0,0" - }, - { + "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", - "ge_buy_limit": "100", - "examine": "I have to keep my hands safe! Uncharged: A bit worse for wear.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "71500", - "name": "Worn-out adamant gauntlets", "tradeable": "false", - "archery_ticket_price": "0", + "weight": "0.2" + }, + { "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": "false", + "examine": "I have to keep my hands safe! Uncharged: A bit worse for wear.", "archery_ticket_price": "0", - "id": "13002" + "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" }, { - "requirements": "{1,40}", - "shop_price": "200", - "examine": "Fully charged: I have to keep my hands safe! Worn-Out: A bit worse for wear.", - "durability": null, - "low_alchemy": "3000", - "high_alchemy": "4500", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "95700", - "name": "Rune gauntlets", + "id": "13002", + "name": "Worn-out adamant gauntlets", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "13003", - "bonuses": "0,0,0,-1,-1,10,11,11,-1,10,10,2,0,0,0" - }, - { + "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", - "ge_buy_limit": "100", - "examine": "Fully charged: I have to keep my hands safe! Worn-Out: A bit worse for wear.", - "durability": null, - "low_alchemy": "3000", - "high_alchemy": "4500", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "99900", - "name": "Worn-out rune gauntlets", - "archery_ticket_price": "0", + "tradeable": "false", + "weight": "0.2" + }, + { "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": "false", + "examine": "Fully charged: I have to keep my hands safe! Worn-Out: A bit worse for wear.", "archery_ticket_price": "0", - "id": "13005" + "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" }, { - "requirements": "{1,60}", - "shop_price": "300", - "examine": "Uncharged: A bit worse for wear. Charged: I have to keep my hands safe!", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "214700", - "name": "Dragon gauntlets", + "id": "13005", + "name": "Worn-out rune gauntlets", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "13006", - "bonuses": "0,0,0,-1,-1,14,15,15,-1,14,14,3,0,0,0" - }, - { + "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", - "ge_buy_limit": "100", - "examine": "Uncharged: A bit worse for wear. Charged: I have to keep my hands safe!", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "weight": "0.2", - "equipment_slot": "9", - "grand_exchange_price": "166200", - "name": "Worn-out dragon gauntlets", - "archery_ticket_price": "0", + "tradeable": "false", + "weight": "0.2" + }, + { "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": "false", + "examine": "Uncharged: A bit worse for wear. Charged: I have to keep my hands safe!", "archery_ticket_price": "0", - "id": "13008" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", + "bonuses": "0,0,0,-1,-1,4,5,5,-1,4,4,1,0,0,0", "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13011" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13013" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13015" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13017" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13019" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13021" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13023" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13025" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13027" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13029" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13031" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13033" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "archery_ticket_price": "0", - "id": "13035" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13037" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13039" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13040" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13041" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13043" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13045" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13047" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13048" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13049" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13050" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13052" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13054" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13055" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13057" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13059" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13060" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13062" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13064" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13066" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13067" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13068" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13069" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13070" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13071" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13072" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13074" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13075" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "weight": "5", - "archery_ticket_price": "0", - "id": "13077" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13078" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13079" - }, - { - "shop_price": "5000", - "examine": "Depends on the type of clue you have(\"A set of instructions to be followed\". \"A clue!\".\"It points to great treasure!\". \" A place on the world map,but where?\", \"Perhaps someone at the observatory can teach me to navigate?\" \"Part of the world map, but whe", - "durability": null, - "name": "Clue scroll", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13080" - }, - { + "equipment_slot": "9", "ge_buy_limit": "100", - "turn90cw_anim": "821", - "examine": "A black crossbow.", - "walk_anim": "4226", - "low_alchemy": "320", - "turn90ccw_anim": "822", - "attack_speed": "6", - "turn180_anim": "4227", - "equipment_slot": "3", - "grand_exchange_price": "261", - "stand_anim": "4591", + "requirements": "{1,60}", + "shop_price": "300", "tradeable": "true", - "run_anim": "4228", + "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", - "stand_turn_anim": "823", - "bonuses": "0,0,0,0,60,0,0,0,0,0,0,0,0,0,0", - "requirements": "{4,33}", - "durability": null, - "high_alchemy": "480", - "weight": "5", - "weapon_interface": "17", - "render_anim": "175", - "attack_audios": "2700,0,0,0", - "name": "Black crossbow" - }, - { - "ge_buy_limit": "100", - "grand_exchange_price": "261", - "durability": null, "name": "Black crossbow", - "tradeable": "true", + "examine": "A black crossbow.", "archery_ticket_price": "0", - "id": "13082" + "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" }, { - "requirements": "{4,33}", - "ge_buy_limit": "100", - "examine": "Black crossbow bolts.", - "durability": null, - "low_alchemy": "5", - "high_alchemy": "8", - "equipment_slot": "13", - "grand_exchange_price": "55", - "name": "Black bolts", - "tradeable": "true", + "id": "13082", + "name": "Black crossbow", "archery_ticket_price": "0", + "durability": null, + "ge_buy_limit": "100", + "tradeable": "true" + }, + { "id": "13083", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75" + "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" }, { - "requirements": "{4,33}", - "ge_buy_limit": "100", - "examine": "Black crossbow bolts.", - "durability": null, - "low_alchemy": "5", - "high_alchemy": "8", - "equipment_slot": "13", - "grand_exchange_price": "141", - "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" + "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" }, { - "requirements": "{4,33}", - "ge_buy_limit": "100", - "examine": "Black crossbow bolts.", - "durability": null, - "low_alchemy": "5", - "high_alchemy": "8", - "equipment_slot": "13", - "grand_exchange_price": "233", - "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" - }, - { - "requirements": "{4,33}", - "ge_buy_limit": "100", + "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, - "low_alchemy": "5", - "high_alchemy": "8", "equipment_slot": "13", - "grand_exchange_price": "1690", - "name": "Black bolts(p++)", - "tradeable": "true", - "archery_ticket_price": "0", + "ge_buy_limit": "100", + "requirements": "{4,33}", + "tradeable": "true" + }, + { "id": "13086", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,75" + "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" }, { - "requirements": "{0,8}", - "ge_buy_limit": "2", - "examine": "A sapphire-topped cane.", - "durability": null, - "low_alchemy": "172", - "high_alchemy": "258", - "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", + "examine": "A sapphire-topped cane.", "archery_ticket_price": "0", - "id": "13096" - }, - { - "requirements": "{0,28}", - "ge_buy_limit": "2", - "examine": "A ruby-topped cane.", - "durability": null, - "low_alchemy": "576", - "high_alchemy": "864", - "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, - "low_alchemy": "5760", - "high_alchemy": "8640", - "weight": "0.5", "attack_speed": "5", - "weapon_interface": "10", + "bonuses": "0,-2,16,0,0,0,0,0,0,0,0,13,2,0,0", + "durability": null, "equipment_slot": "3", - "lendable": "true", - "grand_exchange_price": "14500", - "name": "Rune cane", + "ge_buy_limit": "2", + "requirements": "{0,8}", "tradeable": "true", + "weapon_interface": "10", + "weight": "0.5" + }, + { + "id": "13096", + "name": "Black cane", "archery_ticket_price": "0", + "durability": null, + "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", - "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", + "examine": "A diamond-topped cane.", "archery_ticket_price": "0", - "id": "13100" - }, - { - "ge_buy_limit": "2", - "examine": "Comes with a free rabbit!", + "attack_speed": "5", + "bonuses": "0,-2,39,0,0,0,0,0,0,0,0,36,4,0,0", "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "weight": "1", - "equipment_slot": "0", + "equipment_slot": "3", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "160400", - "name": "Top hat", + "requirements": "{0,38}", "tradeable": "true", - "archery_ticket_price": "0", - "id": "13101" + "weapon_interface": "10", + "weight": "0.5" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "160400", - "durability": null, - "name": "Top hat", - "tradeable": "true", + "id": "13100", + "name": "Rune cane", "archery_ticket_price": "0", - "id": "13102" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "ge_buy_limit": "2", - "examine": "Official explorer headgear!", - "grand_exchange_price": "85400", - "durability": null, - "name": "Pith helmet", - "tradeable": "true", - "low_alchemy": "100", - "high_alchemy": "150", - "weight": "0.5", + "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", - "equipment_slot": "0" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "85400", - "durability": null, "name": "Pith helmet", - "tradeable": "true", + "examine": "Official explorer headgear!", "archery_ticket_price": "0", - "id": "13104" + "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}", - "ge_buy_limit": "2", - "examine": "Spikier than normal.", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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" + "weight": "10" }, { - "ge_buy_limit": "2", - "grand_exchange_price": "38300", - "durability": null, + "id": "13106", "name": "Spiked helmet", - "tradeable": "true", "archery_ticket_price": "0", - "id": "13106" + "durability": null, + "ge_buy_limit": "2", + "tradeable": "true" }, { - "remove_head": "true", - "ge_buy_limit": "2", + "id": "13107", + "name": "Sheep mask", "examine": "Shear elegance.", + "archery_ticket_price": "0", "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "1", "equipment_slot": "0", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "296700", - "name": "Sheep mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13107" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "296700", - "durability": null, - "name": "Sheep mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13108" - }, - { "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, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "1", "equipment_slot": "0", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "633600", - "name": "Penguin mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13109" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "633600", - "durability": null, - "name": "Penguin mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13110" - }, - { "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, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "1", "equipment_slot": "0", + "ge_buy_limit": "2", "lendable": "true", - "grand_exchange_price": "399000", - "name": "Bat mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13111" - }, - { - "ge_buy_limit": "2", - "grand_exchange_price": "399000", - "durability": null, - "name": "Bat mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13112" - }, - { "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, - "low_alchemy": "320", - "high_alchemy": "480", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "1200000", - "name": "Cat mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13113" - }, - { "ge_buy_limit": "2", - "grand_exchange_price": "1200000", - "durability": null, - "name": "Cat mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13114" - }, - { + "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, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "1", "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "1500000", - "name": "Wolf mask", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13115" - }, - { "ge_buy_limit": "2", - "grand_exchange_price": "1500000", - "durability": null, - "name": "Wolf mask", + "lendable": "true", + "remove_head": "true", "tradeable": "true", - "archery_ticket_price": "0", - "id": "13116" + "weight": "1" }, { - "turn90cw_anim": "9053", - "examine": "A fully charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "name": "Ivandis flail (7)", "examine": "A partially charged flail.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail - it's nearly out of charges.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail - it's nearly out of charges.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "turn90cw_anim": "9053", - "examine": "A partially charged flail - it's nearly out of charges.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "name": "Ivandis flail (3)", "examine": "A partially charged flail - it's nearly out of charges.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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", + "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", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" - }, - { + "turn180_anim": "9052", + "turn90ccw_anim": "9054", "turn90cw_anim": "9053", - "examine": "A partially charged flail - one last charge left.", - "walk_anim": "9051", - "has_special": "true", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "5", - "turn90ccw_anim": "9054", - "attack_speed": "5", "two_handed": "true", + "walk_anim": "9051", "weapon_interface": "23", - "turn180_anim": "9052", + "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", - "equipment_slot": "3", - "stand_anim": "9049", - "name": "Ivandis flail (1)", "run_anim": "9050", - "archery_ticket_price": "0", - "id": "13146", + "stand_anim": "9049", "stand_turn_anim": "823", - "bonuses": "0,16,11,6,0,2,3,1,6,0,0,26,5,0,0" + "turn180_anim": "9052", + "turn90ccw_anim": "9054", + "turn90cw_anim": "9053", + "two_handed": "true", + "walk_anim": "9051", + "weapon_interface": "23", + "weight": "5" }, { - "examine": "Flail of Ivandis dust.", + "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", - "id": "13147" + "durability": null }, { - "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, + "id": "13149", "name": "Book page", - "tradeable": "false", - "destroy": "true", + "examine": "A page ripped from a book; it looks like a map of some kind.", "archery_ticket_price": "0", - "id": "13149" + "destroy": "true", + "destroy_message": "You found this page in the top floor of Paterdomus temple on an old table.", + "durability": null, + "tradeable": "false" }, { - "destroy_message": "You got this book from a bunk bed in Sanguinesti Myreque base.", - "examine": "A big book of combat techniques.", - "durability": null, + "id": "13151", "name": "Combat book", - "tradeable": "false", - "destroy": "true", + "examine": "A big book of combat techniques.", "archery_ticket_price": "0", - "id": "13151" + "destroy": "true", + "destroy_message": "You got this book from a bunk bed in Sanguinesti Myreque base.", + "durability": null, + "tradeable": "false" }, { - "examine": "Used to make a metal chain.", - "durability": null, + "id": "13153", "name": "Chain link mould", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "1", + "examine": "Used to make a metal chain.", "archery_ticket_price": "0", - "id": "13153" + "durability": null, + "weight": "1" }, { - "examine": "A chain made from mithril and silver.", - "durability": null, + "id": "13154", "name": "Silvthril chain", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "3", + "examine": "A chain made from mithril and silver.", "archery_ticket_price": "0", - "id": "13154" + "durability": null, + "weight": "3" }, { - "examine": "A blessed sickle with an emerald recessed into it.", - "durability": null, - "name": "Silver sickle emerald(b)", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "1", - "archery_ticket_price": "0", "id": "13155", - "equipment_slot": "3" + "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" }, { - "examine": "A blessed and enchanted silver sickle with an emerald recessed into it.", - "durability": null, - "name": "Enchanted sickle emerald(b)", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "1", - "archery_ticket_price": "0", "id": "13156", - "equipment_slot": "3" + "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" }, { - "examine": "A Vyrewatch corpse. Just a husk, really.", - "durability": null, + "id": "13157", "name": "Vyre corpse", - "low_alchemy": "1", - "high_alchemy": "1", - "weight": "5", + "examine": "A Vyrewatch corpse. Just a husk, really.", "archery_ticket_price": "0", - "id": "13157" + "durability": null, + "weight": "5" }, { - "examine": "An ornate key: precise workmanship and an ethereal glow. (Legacy of Seergaze)", - "durability": null, + "id": "13158", "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)", + "archery_ticket_price": "0", "durability": null, + "weight": "0.1" + }, + { + "id": "13159", "name": "Ornate tomb key", - "low_alchemy": "2", - "tradeable": "false", - "high_alchemy": "3", - "destroy": "true", + "examine": "An ornate key: precise workmanship and an ethereal glow. (Legacy of Seergaze)", "archery_ticket_price": "0", - "id": "13159" + "destroy": "true", + "destroy_message": "You got this key by cremating a Vyrewatch in the Paterdomus Columbarium.", + "durability": null, + "tradeable": "false" }, { - "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.", - "durability": null, + "id": "13160", "name": "Tome of xp 2nd ed (3)", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A Tome of Experience (2nd Edition) that awards 2500 XP per chapter.", "archery_ticket_price": "0", - "id": "13160" + "destroy": "true", + "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "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.", - "durability": null, + "id": "13161", "name": "Tome of xp 2nd ed (2)", - "tradeable": "false", - "destroy": "true", - "weight": "1", - "archery_ticket_price": "0", - "id": "13161" - }, - { - "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.", + "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)", - "tradeable": "false", - "destroy": "true", - "weight": "1", + "examine": "A Tome of Experience (2nd Edition) that awards 2500 XP per chapter.", "archery_ticket_price": "0", - "id": "13162" + "destroy": "true", + "destroy_message": "You can get another Tome of Experience from Veliaf in Burgh de Rott.", + "durability": null, + "tradeable": "false", + "weight": "1" }, { - "examine": "A sapphire-topped cane.", - "durability": null, - "low_alchemy": "172", - "high_alchemy": "258", - "weight": "0.5", - "attack_speed": "5", - "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "115390", - "name": "Black cane", - "tradeable": "true", - "archery_ticket_price": "0", "id": "13163", - "bonuses": "0,-2,16,0,0,0,0,0,0,0,0,13,2,0,0" - }, - { - "examine": "A ruby-topped cane.", - "durability": null, - "low_alchemy": "576", - "high_alchemy": "864", - "weight": "0.5", - "weapon_interface": "10", - "equipment_slot": "3", - "grand_exchange_price": "18514", - "name": "Adamant cane", - "tradeable": "true", + "name": "Black cane", + "examine": "A sapphire-topped cane.", "archery_ticket_price": "0", - "id": "13164", - "bonuses": "0,-2,25,0,0,0,0,0,0,0,0,23,3,0,0" - }, - { - "examine": "A diamond-topped cane.", - "durability": null, - "low_alchemy": "5760", - "high_alchemy": "8640", - "weight": "0.5", "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", - "low_alchemy": "80", - "high_alchemy": "120", - "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", - "low_alchemy": "100", - "high_alchemy": "150", - "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.", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "weight": "10", - "equipment_slot": "0", - "grand_exchange_price": "39643", - "name": "Spiked helmet", - "tradeable": "true", - "archery_ticket_price": "0", "id": "13168", - "bonuses": "0,0,0,-6,-2,9,10,7,-1,9,3,0,0,0,0" + "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", + "remove_head": "true", + "tradeable": "true", + "weight": "10" }, { - "remove_head": "true", - "examine": "Shear elegance.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "1", - "equipment_slot": "0", - "lendable": "true", - "grand_exchange_price": "313558", + "id": "13169", "name": "Sheep mask", - "tradeable": "true", + "examine": "Shear elegance.", "archery_ticket_price": "0", - "id": "13169" - }, - { - "remove_head": "true", - "examine": "I can get the look right, but can I do the waddle too?", "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "1", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "609515", + "remove_head": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13170", "name": "Penguin mask", - "tradeable": "true", + "examine": "I can get the look right, but can I do the waddle too?", "archery_ticket_price": "0", - "id": "13170" - }, - { - "remove_head": "true", - "examine": "The bats have left the belltower.", "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "1", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "371616", + "remove_head": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13171", "name": "Bat mask", - "tradeable": "true", + "examine": "The bats have left the belltower.", "archery_ticket_price": "0", - "id": "13171" - }, - { - "remove_head": "true", - "lendable": "true", - "examine": "It's the year of the cat!", - "grand_exchange_price": "1066648", "durability": null, - "name": "Cat mask", - "tradeable": "true", - "low_alchemy": "320", - "high_alchemy": "480", - "archery_ticket_price": "0", - "id": "13172", - "equipment_slot": "0" - }, - { - "remove_head": "true", - "examine": "I'm hungry like the wolf.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "weight": "1", "equipment_slot": "0", "lendable": "true", - "grand_exchange_price": "1303443", + "remove_head": "true", + "tradeable": "true", + "weight": "1" + }, + { + "id": "13172", + "name": "Cat mask", + "examine": "It's the year of the cat!", + "archery_ticket_price": "0", + "durability": null, + "equipment_slot": "0", + "lendable": "true", + "remove_head": "true", + "tradeable": "true" + }, + { + "id": "13173", "name": "Wolf mask", - "tradeable": "true", + "examine": "I'm hungry like the wolf.", "archery_ticket_price": "0", - "id": "13173" + "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "high_alchemy": "60", "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "40", - "tradeable": "false", - "high_alchemy": "60", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", - "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", - "low_alchemy": "20", - "high_alchemy": "30", "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", - "low_alchemy": "5", - "high_alchemy": "8", - "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, - "low_alchemy": "260", - "high_alchemy": "390", - "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, - "low_alchemy": "160", - "high_alchemy": "240", - "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, - "low_alchemy": "20", - "high_alchemy": "30", - "destroy": "true", - "attack_speed": "4", + "name": "Masked earmuffs", + "examine": "More of that crazy Slayer headgear.", "absorb": "0,0,0", - "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, - "low_alchemy": "20", - "high_alchemy": "30", - "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, - "low_alchemy": "20", - "high_alchemy": "30", - "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, - "low_alchemy": "60", - "high_alchemy": "90", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "", - "name": "Ring of slaying(8)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13281", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "", - "name": "Ring of slaying(7)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13282", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "", - "name": "Ring of slaying(6)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13283", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "", - "name": "Ring of slaying(5)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13284", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "", - "name": "Ring of slaying(4)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13285", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "", - "name": "Ring of slaying(3)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13286", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "", - "name": "Ring of slaying(2)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13287", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "An enchanted ring.", - "durability": null, - "low_alchemy": "60", - "high_alchemy": "90", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "12", - "destroy_message": "", - "name": "Ring of slaying(1)", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13288", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "turn90cw_anim": "1207", - "examine": "A razor-sharp sword.", - "walk_anim": "1205", - "low_alchemy": "27200", - "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", - "destroy_message": "", - "grand_exchange_price": "43082", - "stand_anim": "813", - "tradeable": "true", - "rare_item": "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,55}-{18,50}", - "shop_price": "68000", - "durability": null, - "high_alchemy": "40800", - "destroy": "false", - "weapon_interface": "5", - "attack_audios": "2500,2500,2517,2500", - "name": "Leaf-bladed sword" - }, - { - "shop_price": "68000", - "examine": "A razor-sharp sword.", - "durability": null, - "low_alchemy": "27200", - "high_alchemy": "40800", - "destroy": "false", - "attack_speed": "4", - "destroy_message": "", - "grand_exchange_price": "43082", - "name": "Leaf-bladed sword", - "tradeable": "true", - "archery_ticket_price": "0", - "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.", - "examine": "", - "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.", - "examine": "", - "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.", - "examine": "", - "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" - }, - { - "examine": "", - "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" - }, - { - "examine": "", - "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, - "low_alchemy": "20", - "high_alchemy": "30", - "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": "false", - "archery_ticket_price": "0", - "id": "13337", - "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, - "low_alchemy": "20", - "high_alchemy": "30", - "destroy": "true", - "attack_speed": "4", - "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": "false", - "archery_ticket_price": "0", - "id": "13338" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13339", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for both eyes.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatches", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13340", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13341", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for both eyes.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatches", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13342", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13343", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for both eyes.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatches", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13344", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13345", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for both eyes.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatches", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13346", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13347", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for both eyes.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatches", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13348", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13349", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for both eyes.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatches", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13350", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13351", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for both eyes.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatches", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13352", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "It's dark, and I'm wearing two patches. Hit it!", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Double eyepatches", - "tradeable": "false", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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" - }, - { - "shop_price": "100", - "examine": "A patch for the left eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "23485", - "name": "Left eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13356" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A hat and a patch.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "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", - "examine": "", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Stripy pirate shirt", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13358", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "300", - "examine": "", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "false", - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13359" - }, - { - "shop_price": "300", - "examine": "", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Stripy pirate shirt", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13360", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "300", - "examine": "", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "false", - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13361" - }, - { - "shop_price": "300", - "examine": "", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "4", - "destroy_message": "Drop", - "remove_sleeves": "true", - "name": "Stripy pirate shirt", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13362", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "shop_price": "300", - "examine": "", - "durability": null, - "name": "Stripy pirate shirt", - "tradeable": "false", - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13363" - }, - { - "shop_price": "350", - "examine": "", - "durability": null, - "low_alchemy": "140", - "high_alchemy": "210", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "false", - "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", - "examine": "", - "durability": null, - "low_alchemy": "140", - "high_alchemy": "210", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13365" - }, - { - "shop_price": "350", - "examine": "", - "durability": null, - "low_alchemy": "140", - "high_alchemy": "210", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "false", - "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", - "examine": "", - "durability": null, - "low_alchemy": "140", - "high_alchemy": "210", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13367" - }, - { - "shop_price": "350", - "examine": "", - "durability": null, - "low_alchemy": "140", - "high_alchemy": "210", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "false", - "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", - "examine": "", - "durability": null, - "low_alchemy": "140", - "high_alchemy": "210", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Pirate leggings", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13369" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "Essential pirate wear.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana", - "tradeable": "false", - "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", - "examine": "Essential pirate wear.", - "durability": null, - "name": "Bandana", - "tradeable": "false", - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13371" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "Essential pirate wear.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana", - "tradeable": "false", - "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", - "examine": "Essential pirate wear.", - "durability": null, - "name": "Bandana", - "tradeable": "false", - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13373" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "Essential pirate wear.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana", - "tradeable": "false", - "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", - "examine": "Essential pirate wear.", - "durability": null, - "name": "Bandana", - "tradeable": "false", - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13375" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13376", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13377", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "100", - "examine": "A (colour) bandana and a patch for the right eye.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Bandana and eyepatch", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13378", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,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" - }, - { - "examine": "Wall beast fingers in a white fern sauce.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Fingers", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13386", - "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" - }, - { - "shop_price": "300", - "examine": "A replica tinderbox made of solid gold.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Golden tinderbox", - "tradeable": "false", - "archery_ticket_price": "0", - "id": "13403", - "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, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "true", - "attack_speed": "9", - "two_handed": "true", - "weapon_interface": "16", - "absorb": "0,0,0", - "render_anim": "303", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "86850", - "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" - }, - { - "examine": "", - "durability": null, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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" - }, - { - "examine": "This helmet is worn by archers.", - "durability": null, - "low_alchemy": "31200", - "high_alchemy": "46800", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,3,1", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "46334", - "name": "Archer helm", - "archery_ticket_price": "0", - "id": "13407", - "bonuses": "-5,-5,-5,-5,6,6,8,10,6,6,0,0,0,0,0" - }, - { - "examine": "This helmet is worn by berserkers.", - "durability": null, - "low_alchemy": "31200", - "high_alchemy": "46800", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "45978", - "name": "Berserker helm", - "archery_ticket_price": "0", - "id": "13408", - "bonuses": "0,0,0,-5,-5,31,29,33,0,30,7,3,0,0,0" - }, - { - "examine": "This helm is worn by warriors.", - "durability": null, - "low_alchemy": "31200", - "high_alchemy": "46800", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "45664", - "name": "Warrior helm", - "archery_ticket_price": "0", - "id": "13409", - "bonuses": "0,5,0,-5,-5,31,33,29,0,30,7,0,0,0,0" - }, - { - "examine": "This helm is worn by farseers.", - "durability": null, - "low_alchemy": "31200", - "high_alchemy": "46800", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "45989", - "name": "Farseer helm", - "archery_ticket_price": "0", - "id": "13410", - "bonuses": "-5,-5,-5,6,-5,8,10,12,6,0,7,0,0,0,0" - }, - { - "examine": "Protective headwear made from crabs. Better than it sounds.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "20378", - "name": "Rock-shell helm", - "archery_ticket_price": "0", - "id": "13411", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,6,0,0,0,0" - }, - { - "examine": "A sturdy body armour made from rock crab pieces.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "41788", - "name": "Rock-shell plate", - "archery_ticket_price": "0", - "id": "13412", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,30,0,0,0,0" - }, - { - "examine": "Some tough leggings made from rock crab parts.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "37139", - "name": "Rock-shell legs", - "archery_ticket_price": "0", - "id": "13413", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,10,0,0,0,0" - }, - { - "examine": "A helm fit for any Fremennik ranger.", - "durability": null, - "low_alchemy": "24000", - "high_alchemy": "36000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,3,1", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "37724", - "name": "Spined helm", - "archery_ticket_price": "0", - "id": "13414", - "bonuses": "0,-6,-6,-6,6,6,6,6,6,0,6,0,0,0,0" - }, - { - "examine": "A constant reminder that I'm above a Dagannoth in the food chain.", - "durability": null, - "low_alchemy": "3000", - "high_alchemy": "4500", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "5773", - "name": "Spined body", - "archery_ticket_price": "0", - "id": "13415", - "bonuses": "0,0,0,-15,15,40,32,45,20,40,30,0,0,0,0" - }, - { - "examine": "Stylish leg armour for rangers with a lingering smell of raw fish...", - "durability": null, - "low_alchemy": "1560", - "high_alchemy": "2340", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,4,2", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2696", - "name": "Spined chaps", - "archery_ticket_price": "0", - "id": "13416", - "bonuses": "0,0,0,-10,8,22,16,24,8,22,10,0,0,0,0" - }, - { - "examine": "Make your foes cower by wearing a skull as a helmet!", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "39060", - "name": "Skeletal helm", - "archery_ticket_price": "0", - "id": "13417", - "bonuses": "0,0,0,2,-2,10,9,11,3,0,6,0,0,0,0" - }, - { - "examine": "The bones in this armour seems to vibrate with a magic quality...", - "durability": null, - "low_alchemy": "18000", - "high_alchemy": "27000", - "destroy": "true", - "attack_speed": "4", - "absorb": "6,3,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "26289", - "name": "Skeletal top", - "archery_ticket_price": "0", - "id": "13418", - "bonuses": "0,0,0,8,-10,35,25,42,15,0,30,0,0,0,0" - }, - { - "examine": "A superior set of strengthened slacks for any self respecting seer.", - "durability": null, - "low_alchemy": "16000", - "high_alchemy": "24000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,2,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "24591", - "name": "Skeletal bottoms", - "archery_ticket_price": "0", - "id": "13419", - "bonuses": "0,0,0,6,-7,22,20,24,10,0,10,0,0,0,0" - }, - { - "examine": "Some finely crafted Fremennik boots, made from spined dagannoth hide.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "158181", - "name": "Spined boots", - "archery_ticket_price": "0", - "id": "13420", - "bonuses": "0,0,0,0,0,1,1,0,0,0,1,0,0,0,0" - }, - { - "examine": "Some Fremennik boots, made from the shards of a rock crab's shell.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1358", - "name": "Rock-shell boots", - "archery_ticket_price": "0", - "id": "13421", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" - }, - { - "examine": "Some finely crafted Fremennik boots, made from the bones of a Wallasalki.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "7788", - "name": "Skeletal boots", - "archery_ticket_price": "0", - "id": "13422", - "bonuses": "0,0,0,0,0,0,1,1,0,0,1,0,0,0,0" - }, - { - "examine": "Fremennik gloves stitched together from spined dagannoth hide.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "3027", - "name": "Spined gloves", - "archery_ticket_price": "0", - "id": "13423", - "bonuses": "0,0,0,0,0,1,2,0,0,0,0,0,0,0,0" - }, - { - "examine": "Fremennik gloves stitched together from rock crab shell shards.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "12839", - "name": "Rock-shell gloves", - "archery_ticket_price": "0", - "id": "13424", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "examine": "Fremennik gloves stitched together from wallasalki bones fragments.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "25838", - "name": "Skeletal gloves", - "archery_ticket_price": "0", - "id": "13425", - "bonuses": "0,0,0,0,0,0,1,2,0,0,0,0,0,0,0" - }, - { - "examine": "A mysterious ring that can fill the wearer with magical power...", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "799703", - "name": "Seers ring", - "archery_ticket_price": "0", - "id": "13426", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "examine": "A fabled ring that improves the wearer's skill with a bow...", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "483521", - "name": "Archers ring", - "archery_ticket_price": "0", - "id": "13427", - "bonuses": "0,0,0,0,4,0,0,0,0,4,0,0,0,0,0" - }, - { - "examine": "A legendary ring once worn by Fremennik warriors.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "361302", - "name": "Warrior ring", - "archery_ticket_price": "0", - "id": "13428", - "bonuses": "0,4,0,0,0,0,4,0,0,0,0,0,0,0,0" - }, - { - "examine": "A ring reputed to bring out a berserk fury in its wearer.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "963418", - "name": "Berserker ring", - "archery_ticket_price": "0", - "id": "13429", - "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, - "low_alchemy": "88000", - "high_alchemy": "132000", - "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, - "low_alchemy": "8", - "high_alchemy": "12", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "25", - "name": "Crayfish cage", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13431", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "low_alchemy": "8", - "high_alchemy": "12", - "destroy": "false", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "25", - "name": "Crayfish cage", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13432" - }, - { - "shop_price": "5", - "examine": "Some cooked crayfish. Eat it to heal.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "32", - "name": "Crayfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13433", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "5", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "destroy": "false", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "32", - "name": "Crayfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13434" - }, - { - "shop_price": "5", - "examine": "I should try cooking this. Used in Cooking (1).", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "41", - "name": "Raw crayfish", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13435", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "5", - "examine": "Swap this note at any bank for the equivalent item.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "destroy": "false", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "41", - "name": "Raw crayfish", - "tradeable": "true", - "archery_ticket_price": "0", - "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" - }, - { - "destroy_message": "", - "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" - }, - { - "destroy_message": "", - "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" - }, - { - "examine": "A very powerful onyx amulet.", - "durability": null, - "low_alchemy": "80800", - "high_alchemy": "121200", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2285210", - "name": "Amulet of fury", - "archery_ticket_price": "0", - "id": "13442", - "bonuses": "10,10,10,10,10,15,15,15,15,15,15,8,0,5,0" - }, - { - "examine": "A cape woven of obsidian plates.", - "durability": null, - "low_alchemy": "24000", - "high_alchemy": "36000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "56575", - "name": "Obsidian cape", - "archery_ticket_price": "0", - "id": "13443", - "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, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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" - }, - { - "destroy_message": "", - "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" - }, - { - "destroy_message": "", - "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" - }, - { - "destroy_message": "", - "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" - }, - { - "examine": "Protects your head and looks impressive too.", - "durability": null, - "low_alchemy": "60000", - "high_alchemy": "90000", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "5756658", - "name": "Dragon full helm", - "archery_ticket_price": "0", - "id": "13449", - "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, - "low_alchemy": "500000", - "high_alchemy": "750000", - "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, - "low_alchemy": "500000", - "high_alchemy": "750000", - "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, - "low_alchemy": "500000", - "high_alchemy": "750000", - "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, - "low_alchemy": "500000", - "high_alchemy": "750000", - "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, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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" - }, - { - "examine": "A helmet of great craftmanship.", - "durability": null, - "low_alchemy": "8000", - "high_alchemy": "12000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,5,2", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "3732060", - "name": "Armadyl helmet", - "archery_ticket_price": "0", - "id": "13455", - "bonuses": "-5,-5,-5,-5,10,6,6,10,10,8,12,0,0,1,0" - }, - { - "examine": "A chestplate of great craftsmanship", - "durability": null, - "low_alchemy": "24000", - "high_alchemy": "36000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,10,5", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "9628666", - "name": "Armadyl chestplate", - "archery_ticket_price": "0", - "id": "13456", - "bonuses": "-7,-7,-7,-15,33,56,48,61,70,57,52,0,0,1,0" - }, - { - "examine": "A chainskirt of great craftsmanship.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,7,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "9754868", - "name": "Armadyl plateskirt", - "archery_ticket_price": "0", - "id": "13457", - "bonuses": "-6,-6,-6,-10,20,32,26,34,40,33,25,0,0,1,0" - }, - { - "examine": "A sturdy chestplate.", - "durability": null, - "low_alchemy": "106000", - "high_alchemy": "159000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,9", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "6460055", - "name": "Bandos chestplate", - "archery_ticket_price": "0", - "id": "13458", - "bonuses": "0,0,0,-15,-10,98,93,105,-6,133,52,4,0,1,0" - }, - { - "examine": "A sturdy pair of tassets.", - "durability": null, - "low_alchemy": "109000", - "high_alchemy": "163500", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "4228256", - "name": "Bandos tassets", - "archery_ticket_price": "0", - "id": "13459", - "bonuses": "0,0,0,-21,-7,71,63,66,-4,93,25,2,0,1,0" - }, - { - "examine": "Some sturdy boots.", - "durability": null, - "low_alchemy": "10000", - "high_alchemy": "15000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1259091", - "name": "Bandos boots", - "archery_ticket_price": "0", - "id": "13460", - "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, - "low_alchemy": "52000", - "high_alchemy": "78000", - "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" - }, - { - "examine": "These will protect my feet.", - "durability": null, - "low_alchemy": "8000", - "high_alchemy": "12000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "17336", - "name": "Dragon boots", - "archery_ticket_price": "0", - "id": "13462", - "bonuses": "0,0,0,-3,-1,16,17,18,0,0,15,4,0,0,0" - }, - { - "destroy_message": "", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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 hatchet.", - "durability": null, - "low_alchemy": "5120", - "high_alchemy": "7680", - "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 hatchet.", - "has_special": "true", - "durability": null, - "low_alchemy": "22000", - "high_alchemy": "33000", - "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, - "low_alchemy": "16640", - "high_alchemy": "24960", - "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, - "low_alchemy": "80000", - "high_alchemy": "120000", - "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, - "low_alchemy": "16600", - "high_alchemy": "24900", - "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, - "low_alchemy": "12800", - "high_alchemy": "19200", - "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, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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, - "low_alchemy": "10240", - "high_alchemy": "15360", - "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, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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, - "low_alchemy": "130000", - "high_alchemy": "195000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "12800", - "high_alchemy": "19200", - "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" - }, - { - "examine": "A series of connected metal rings.", - "durability": null, - "low_alchemy": "100000", - "high_alchemy": "150000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,9", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2280904", - "name": "Dragon chainbody", - "archery_ticket_price": "0", - "id": "13481", - "bonuses": "0,0,0,-15,0,81,93,98,-3,82,50,0,0,0,0" - }, - { - "examine": "Provides excellent protection.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "38062", - "name": "Rune platebody", - "archery_ticket_price": "0", - "id": "13482", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "3120", - "high_alchemy": "4680", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "4373", - "name": "Green d'hide body", - "archery_ticket_price": "0", - "id": "13483", - "bonuses": "0,0,0,-20,15,40,32,45,20,40,40,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "3744", - "high_alchemy": "5616", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "5266", - "name": "Blue d'hide body", - "archery_ticket_price": "0", - "id": "13484", - "bonuses": "0,0,0,-15,20,45,37,50,30,45,45,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "4492", - "high_alchemy": "6738", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "6276", - "name": "Red d'hide body", - "archery_ticket_price": "0", - "id": "13485", - "bonuses": "0,0,0,-15,25,50,42,55,40,50,50,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "5392", - "high_alchemy": "8088", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "7619", - "name": "Black d'hide body", - "archery_ticket_price": "0", - "id": "13486", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,55,0,0,0,0" - }, - { - "examine": "These look pretty heavy.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "37355", - "name": "Rune platelegs", - "archery_ticket_price": "0", - "id": "13487", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Looks pretty heavy.", - "durability": null, - "low_alchemy": "108000", - "high_alchemy": "162000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "160755", - "name": "Dragon platelegs", - "archery_ticket_price": "0", - "id": "13488", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "examine": "Designer leg protection.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "37248", - "name": "Rune plateskirt", - "archery_ticket_price": "0", - "id": "13489", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "This looks pretty heavy.", - "durability": null, - "low_alchemy": "108000", - "high_alchemy": "162000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "161006", - "name": "Dragon plateskirt", - "archery_ticket_price": "0", - "id": "13490", - "bonuses": "0,0,0,-21,-7,68,66,63,-4,65,20,0,0,0,0" - }, - { - "examine": "100% real dragonhide.", - "durability": null, - "low_alchemy": "1560", - "high_alchemy": "2340", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,4,2", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2037", - "name": "Green d'hide chaps", - "archery_ticket_price": "0", - "id": "13491", - "bonuses": "0,0,0,-10,8,22,16,24,8,22,15,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "1728", - "high_alchemy": "2592", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,5,2", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2352", - "name": "Blue d'hide chaps", - "archery_ticket_price": "0", - "id": "13492", - "bonuses": "0,0,0,-10,11,25,19,27,14,25,20,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "2072", - "high_alchemy": "3108", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2761", - "name": "Red d'hide chaps", - "archery_ticket_price": "0", - "id": "13493", - "bonuses": "0,0,0,-10,14,28,22,30,20,28,25,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "2488", - "high_alchemy": "3732", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,7,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "3377", - "name": "Black d'hide chaps", - "archery_ticket_price": "0", - "id": "13494", - "bonuses": "0,0,0,-10,17,31,25,33,28,31,30,0,0,0,0" - }, - { - "examine": "Makes the wearer pretty intimidating.", - "durability": null, - "low_alchemy": "40000", - "high_alchemy": "60000", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "59676", - "name": "Dragon med helm", - "archery_ticket_price": "0", - "id": "13495", - "bonuses": "0,0,0,-3,-1,33,35,32,-1,34,10,0,0,0,0" - }, - { - "examine": "A full face helmet.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "20388", - "name": "Rune full helm", - "archery_ticket_price": "0", - "id": "13496", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "1000", - "high_alchemy": "1500", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1208", - "name": "Green d'hide vamb", - "archery_ticket_price": "0", - "id": "13497", - "bonuses": "0,0,0,-10,8,3,2,4,2,0,3,0,0,0,0" - }, - { - "examine": "Made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "1200", - "high_alchemy": "1800", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1488", - "name": "Blue d'hide vamb", - "archery_ticket_price": "0", - "id": "13498", - "bonuses": "0,0,0,-10,9,4,3,5,4,0,4,0,0,0,0" - }, - { - "examine": "Vambraces made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "1440", - "high_alchemy": "2160", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1714", - "name": "Red d'hide vamb", - "archery_ticket_price": "0", - "id": "13499", - "bonuses": "0,0,0,-10,10,5,4,6,6,0,5,0,0,0,0" - }, - { - "examine": "Vambraces made from 100% real dragonhide.", - "durability": null, - "low_alchemy": "1728", - "high_alchemy": "2592", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2208", - "name": "Black d'hide vamb", - "archery_ticket_price": "0", - "id": "13500", - "bonuses": "0,0,0,-10,11,6,5,7,8,0,6,0,0,0,0" - }, - { - "examine": "A wooden helmet.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "5210", - "name": "Splitbark helm", - "archery_ticket_price": "0", - "id": "13501", - "bonuses": "0,0,0,3,-2,10,9,11,3,0,7,0,0,0,0" - }, - { - "examine": "Provides good protection.", - "durability": null, - "low_alchemy": "18000", - "high_alchemy": "27000", - "destroy": "true", - "attack_speed": "4", - "absorb": "6,3,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "27106", - "name": "Splitbark body", - "archery_ticket_price": "0", - "id": "13502", - "bonuses": "0,0,0,10,-10,36,26,42,15,0,40,0,0,0,0" - }, - { - "examine": "These should protect my legs.", - "durability": null, - "low_alchemy": "16000", - "high_alchemy": "24000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,2,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "56050", - "name": "Splitbark legs", - "archery_ticket_price": "0", - "id": "13503", - "bonuses": "0,0,0,7,-7,22,20,25,10,0,15,0,0,0,0" - }, - { - "examine": "These should keep my hands safe.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2442", - "name": "Splitbark gauntlets", - "archery_ticket_price": "0", - "id": "13504", - "bonuses": "0,0,0,2,-1,3,2,4,2,0,3,0,0,0,0" - }, - { - "examine": "Wooden foot protection.", - "durability": null, - "low_alchemy": "2000", - "high_alchemy": "3000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "6845", - "name": "Splitbark boots", - "archery_ticket_price": "0", - "id": "13505", - "bonuses": "0,0,0,2,-1,3,2,4,2,0,9,0,0,0,0" - }, - { - "examine": "An ancient and powerful looking Dragon Square shield.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "absorb": "5,0,11", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "328356", - "name": "Dragon sq shield", - "archery_ticket_price": "0", - "id": "13506", - "bonuses": "0,0,0,-6,-2,50,52,48,0,50,50,0,0,0,0" - }, - { - "examine": "A large metal shield.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "31885", - "name": "Rune kiteshield", - "archery_ticket_price": "0", - "id": "13507", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "Blue: A magical hat. ", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic hat", - "archery_ticket_price": "0", - "id": "13508", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "examine": "The upper half of a magical robe.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic robe top", - "archery_ticket_price": "0", - "id": "13509", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "examine": "The lower half of a magical robe.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,2,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic robe bottom", - "archery_ticket_price": "0", - "id": "13510", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "examine": "Bright/Dark magical gloves.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic gloves", - "archery_ticket_price": "0", - "id": "13511", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "examine": "Magical boots.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic boots", - "archery_ticket_price": "0", - "id": "13512", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "examine": "Blue: A magical hat. ", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic hat", - "archery_ticket_price": "0", - "id": "13513", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "examine": "The upper half of a magical robe.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic robe top", - "archery_ticket_price": "0", - "id": "13514", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "examine": "The lower half of a magical robe.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,2,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic robe bottom", - "archery_ticket_price": "0", - "id": "13515", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "examine": "Bright/Dark magical gloves.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic gloves", - "archery_ticket_price": "0", - "id": "13516", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "examine": "Magical boots.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic boots", - "archery_ticket_price": "0", - "id": "13517", - "bonuses": "0,0,0,3,0,0,0,0,3,0,3,0,0,0,0" - }, - { - "examine": "Blue: A magical hat. ", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic hat", - "archery_ticket_price": "0", - "id": "13518", - "bonuses": "0,0,0,4,0,0,0,0,4,0,4,0,0,0,0" - }, - { - "examine": "The upper half of a magical robe.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic robe top", - "archery_ticket_price": "0", - "id": "13519", - "bonuses": "0,0,0,20,0,0,0,0,20,0,20,0,0,0,0" - }, - { - "examine": "The lower half of a magical robe.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,2,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic robe bottom", - "archery_ticket_price": "0", - "id": "13520", - "bonuses": "0,0,0,15,0,0,0,0,15,0,15,0,0,0,0" - }, - { - "examine": "Bright/Dark magical gloves.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic gloves", - "archery_ticket_price": "0", - "id": "13521", - "bonuses": "0,0,0,3,0,0,0,0,3,0,0,0,0,0,0" - }, - { - "examine": "Magical boots.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1", - "name": "Mystic boots", - "archery_ticket_price": "0", - "id": "13522", - "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, - "low_alchemy": "256", - "high_alchemy": "384", - "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, - "low_alchemy": "160", - "high_alchemy": "240", - "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, - "low_alchemy": "512", - "high_alchemy": "768", - "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, - "low_alchemy": "320", - "high_alchemy": "480", - "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, - "low_alchemy": "1024", - "high_alchemy": "1536", - "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, - "low_alchemy": "640", - "high_alchemy": "960", - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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, - "low_alchemy": "6480", - "high_alchemy": "9720", - "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" - }, - { - "examine": "It's a Santa hat.", - "durability": null, - "low_alchemy": "64", - "high_alchemy": "96", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "130433327", - "name": "Santa hat", - "archery_ticket_price": "0", - "id": "13537", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Aaaarrrghhh ... I'm a monster.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "104671288", - "name": "Green h'ween mask", - "archery_ticket_price": "0", - "id": "13538", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Aaaarrrghhh ... I'm a monster.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "127720943", - "name": "Blue h'ween mask", - "archery_ticket_price": "0", - "id": "13539", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Aaaarrrghhh ... I'm a monster.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "171640098", - "name": "Red h'ween mask", - "archery_ticket_price": "0", - "id": "13540", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "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, - "low_alchemy": "720", - "high_alchemy": "1080", - "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, - "low_alchemy": "1000", - "high_alchemy": "1500", - "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" - }, - { - "examine": "Ancient range protection crafted from white dragonhide.", - "durability": null, - "low_alchemy": "18000", - "high_alchemy": "27000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "74503559", - "name": "3rd age range top", - "archery_ticket_price": "0", - "id": "13544", - "bonuses": "0,0,0,-15,30,55,47,60,60,55,52,0,0,0,0" - }, - { - "examine": "Fabulously ancient range protection crafted from white dragonhide.", - "durability": null, - "low_alchemy": "16000", - "high_alchemy": "24000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,4,2", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "47837515", - "name": "3rd age range legs", - "archery_ticket_price": "0", - "id": "13545", - "bonuses": "0,0,0,-10,17,31,25,33,30,31,25,0,0,0,0" - }, - { - "examine": "Ancient range protection crafted from white dragonhide.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,3,1", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "9746895", - "name": "3rd age range coif", - "archery_ticket_price": "0", - "id": "13546", - "bonuses": "0,0,0,-2,9,4,7,10,5,8,12,0,0,0,0" - }, - { - "examine": "Fabulously ancient range protection crafted from white dragonhide.", - "durability": null, - "low_alchemy": "86000", - "high_alchemy": "129000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "6826420", - "name": "3rd age vambraces", - "archery_ticket_price": "0", - "id": "13547", - "bonuses": "0,0,0,-11,11,6,5,7,9,0,5,0,0,0,0" - }, - { - "examine": "Ancient mage protection enchanted in the Third-Age.", - "durability": null, - "low_alchemy": "18000", - "high_alchemy": "27000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,2,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "138719269", - "name": "3rd age robe top", - "archery_ticket_price": "0", - "id": "13548", - "bonuses": "0,0,0,24,0,0,0,0,24,0,24,0,0,0,0" - }, - { - "examine": "Ancient mage protection enchanted in the Third-Age.", - "durability": null, - "low_alchemy": "16000", - "high_alchemy": "24000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,2,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "89961678", - "name": "3rd age robe", - "archery_ticket_price": "0", - "id": "13549", - "bonuses": "0,0,0,19,0,0,0,0,19,0,20,0,0,0,0" - }, - { - "examine": "Ancient mage protection enchanted in the Third-Age.", - "durability": null, - "low_alchemy": "4000", - "high_alchemy": "6000", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,1,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "14976830", - "name": "3rd age mage hat", - "archery_ticket_price": "0", - "id": "13550", - "bonuses": "0,0,0,8,0,0,0,0,8,0,12,0,0,0,0" - }, - { - "examine": "Ancient mage protection enchanted in the Third-Age.", - "durability": null, - "low_alchemy": "8000", - "high_alchemy": "12000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "35019596", - "name": "3rd age amulet", - "archery_ticket_price": "0", - "id": "13551", - "bonuses": "0,0,0,15,0,0,0,0,10,0,0,0,0,0,0" - }, - { - "examine": "Ancient armour beaten from magical silver.", - "durability": null, - "low_alchemy": "80000", - "high_alchemy": "120000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "244180930", - "name": "3rd age platelegs", - "archery_ticket_price": "0", - "id": "13552", - "bonuses": "0,0,0,-25,-2,78,76,83,-5,75,25,0,0,0,0" - }, - { - "examine": "Ancient armour beaten from magical silver.", - "durability": null, - "low_alchemy": "80000", - "high_alchemy": "120000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,9", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "266509014", - "name": "3rd age platebody", - "archery_ticket_price": "0", - "id": "13553", - "bonuses": "0,0,0,-20,0,96,108,113,-4,97,52,0,0,0,0" - }, - { - "examine": "Ancient armour beaten from magical silver.", - "durability": null, - "low_alchemy": "36000", - "high_alchemy": "54000", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "50697678", - "name": "3rd age full helmet", - "archery_ticket_price": "0", - "id": "13554", - "bonuses": "0,0,0,-5,-2,47,49,43,-3,48,12,0,0,0,0" - }, - { - "examine": "Ancient armour beaten from magical silver.", - "durability": null, - "low_alchemy": "72000", - "high_alchemy": "108000", - "destroy": "true", - "attack_speed": "4", - "absorb": "5,0,11", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "91262203", - "name": "3rd age kiteshield", - "archery_ticket_price": "0", - "id": "13555", - "bonuses": "0,0,0,-10,-4,63,65,61,-3,63,60,0,0,0,0" - }, - { - "examine": "Lightweight boots ideal for rangers.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "6253527", - "name": "Ranger boots", - "archery_ticket_price": "0", - "id": "13556", - "bonuses": "0,0,0,-10,8,2,3,4,2,0,10,0,0,0,0" - }, - { - "examine": "Slightly magical boots.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "388955", - "name": "Wizard boots", - "archery_ticket_price": "0", - "id": "13557", - "bonuses": "0,0,0,4,0,0,0,0,4,0,0,0,0,0,0" - }, - { - "examine": "Endorsed by Robin Hood.", - "durability": null, - "low_alchemy": "180", - "high_alchemy": "270", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1962571", - "name": "Robin hood hat", - "archery_ticket_price": "0", - "id": "13558", - "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, - "low_alchemy": "15", - "high_alchemy": "22", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "100", - "name": "Button mushroom", - "archery_ticket_price": "0", - "id": "13563", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "38", - "examine": "An occasionally edible mushroom.", - "durability": null, - "low_alchemy": "15", - "high_alchemy": "22", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "100", - "name": "Button mushroom", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "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" - }, - { - "examine": "Useful for mixing things.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "2", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "106", - "name": "Bowl", - "archery_ticket_price": "0", - "id": "13570", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "It's a bucket of milk.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "7", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "177", - "name": "Bucket of milk", - "archery_ticket_price": "0", - "id": "13571", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A fresh herb.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "595", - "name": "Clean guam", - "archery_ticket_price": "0", - "id": "13572", - "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" - }, - { - "examine": "Used for making notes.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "290", - "name": "Papyrus", - "archery_ticket_price": "0", - "id": "13577", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A lump of charcoal.", - "durability": null, - "low_alchemy": "18", - "high_alchemy": "27", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "296", - "name": "Charcoal", - "archery_ticket_price": "0", - "id": "13578", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A slightly muddy spade. ", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "130", - "name": "Spade", - "archery_ticket_price": "0", - "id": "13579", - "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" - }, - { - "shop_price": "4", - "examine": "I can grind things for potions in this.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "2", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "169", - "name": "Pestle and mortar", - "archery_ticket_price": "0", - "id": "13587", - "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", - "high_alchemy": "1", - "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" - }, - { - "shop_price": "2", - "examine": "It's an empty bucket. ", - "durability": null, - "high_alchemy": "1", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "39", - "name": "Bucket", - "archery_ticket_price": "0", - "id": "13589", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "", - "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" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Runecrafting guild teleport", - "archery_ticket_price": "0", - "id": "13598", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Air altar teleport", - "archery_ticket_price": "0", - "id": "13599", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Mind altar teleport", - "archery_ticket_price": "0", - "id": "13600", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Water altar teleport", - "archery_ticket_price": "0", - "id": "13601", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Earth altar teleport", - "archery_ticket_price": "0", - "id": "13602", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Fire altar teleport", - "archery_ticket_price": "0", - "id": "13603", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Body altar teleport", - "archery_ticket_price": "0", - "id": "13604", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Cosmic altar teleport", - "archery_ticket_price": "0", - "id": "13605", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Chaos altar teleport", - "archery_ticket_price": "0", - "id": "13606", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Nature altar teleport", - "archery_ticket_price": "0", - "id": "13607", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Law altar teleport", - "archery_ticket_price": "0", - "id": "13608", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Death altar teleport", - "archery_ticket_price": "0", - "id": "13609", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Blood altar teleport", - "archery_ticket_price": "0", - "id": "13610", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "A tablet containing a magic spell.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Astral altar teleport", - "archery_ticket_price": "0", - "id": "13611", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13612", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "20", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13613", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Heavy duty wizard robes.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "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" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13615", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13616", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "A heavy-duty wizard robe.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Runecrafter skirt", - "archery_ticket_price": "0", - "id": "13617", - "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Essence can be rough on the hands", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "9", - "destroy_message": "Drop", - "name": "Runecrafter gloves", - "archery_ticket_price": "0", - "id": "13618", - "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Heavy duty wizard robes.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "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" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13620", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13621", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "A heavy-duty wizard robe.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Runecrafter skirt", - "archery_ticket_price": "0", - "id": "13622", - "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Essence can be rough on the hands", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "9", - "destroy_message": "Drop", - "name": "Runecrafter gloves", - "archery_ticket_price": "0", - "id": "13623", - "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Heavy duty wizard robes.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "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" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13625", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13626", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "A heavy-duty wizard robe.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "name": "Runecrafter skirt", - "archery_ticket_price": "0", - "id": "13627", - "bonuses": "0,0,0,4,0,4,4,4,4,4,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Essence can be rough on the hands", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "9", - "destroy_message": "Drop", - "name": "Runecrafter gloves", - "archery_ticket_price": "0", - "id": "13628", - "bonuses": "0,0,0,2,0,2,2,2,2,2,0,0,0,0,0" - }, - { - "shop_price": "15", - "examine": "Staff with a holder for a talisman on top.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "5", - "absorb": "0,0,0", - "render_anim": "28", - "destroy_message": "Drop", - "name": "Runecrafting staff", - "archery_ticket_price": "0", - "id": "13629", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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, - "low_alchemy": "600", - "high_alchemy": "900", - "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" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13656", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13657", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "75", - "examine": "Comes with goggles, for extra safety while Runecrafting.", - "durability": null, - "low_alchemy": "30", - "high_alchemy": "45", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "name": "Runecrafter hat", - "archery_ticket_price": "0", - "id": "13658", - "bonuses": "0,0,0,3,0,3,3,3,3,3,0,0,0,0,0" - }, - { - "shop_price": "100", - "examine": "It burns, burns, burns...", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "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, - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "attack_speed": "5", - "weapon_interface": "2", - "absorb": "0,0,0", - "render_anim": "1096", - "equipment_slot": "3", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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" - }, - { - "shop_price": "10", - "examine": "These are my funny pants.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Clown leggings", - "archery_ticket_price": "0", - "id": "13669", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "For helping clowns stay upright.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "10", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Clown shoes", - "archery_ticket_price": "0", - "id": "13670", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "For making a happy noise.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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" - }, - { - "shop_price": "10", - "examine": "Very stylish.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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 one.", - "name": "Ringmaster hat", - "archery_ticket_price": "0", - "id": "13672", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "Fits nicely.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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" - }, - { - "shop_price": "10", - "examine": "Very stylish.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "You will need to speak to the ringmaster to get another one.", - "name": "Ringmaster pants", - "archery_ticket_price": "0", - "id": "13674", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "Shiny.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "10", - "destroy_message": "You will need to speak to the ringmaster to get another one.", - "name": "Ringmaster boots", - "archery_ticket_price": "0", - "id": "13675", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "For shouting at people.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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" - }, - { - "shop_price": "10", - "examine": "Very tight fitting.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Acrobat pants", - "archery_ticket_price": "0", - "id": "13678", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "These keep your feet off the floor.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "10", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Acrobat shoes", - "archery_ticket_price": "0", - "id": "13679", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "10", - "examine": "Oh no, I'm bald! Just kidding.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "4", - "high_alchemy": "6", + "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" - }, - { - "shop_price": "10", - "examine": "Very tight fitting.", + "destroy_message": "Drop", "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Acrobat pants", - "archery_ticket_price": "0", - "id": "13682", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "These keep your feet off the floor.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "10", - "destroy_message": "You will need to speak to the ringmaster to get another.", - "name": "Acrobat shoes", - "archery_ticket_price": "0", - "id": "13683", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "remove_head": "true", - "shop_price": "10", - "examine": "Oh no, I'm bald! Just kidding.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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": "Acrobat hood", - "archery_ticket_price": "0", - "id": "13684", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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": "13685", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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": "13686", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "10", - "examine": "Very dapper!", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "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": "13687", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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": "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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, - "low_alchemy": "4", - "high_alchemy": "6", - "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" - }, - { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", - "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" - }, - { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", - "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" - }, - { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", - "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" - }, - { - "remove_sleeves": "true", - "shop_price": "10", - "durability": null, - "name": "Shirt", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13695", - "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_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", - "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", - "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13708" - }, - { - "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", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "13709", - "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 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", - "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 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", - "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 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", - "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 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", - "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 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", - "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 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", - "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 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", - "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 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", - "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 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", - "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": "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" + "tradeable": "false" }, { - "shop_price": "14", - "examine": "A finely balanced throwing knife.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "18", + "id": "13279", + "name": "Unfinished broad bolts", + "examine": "For use against turoth and kurasks.", "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.", + "destroy_message": "Drop", "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" - }, - { "shop_price": "50", - "examine": "Small, shiny bits of rock.", - "durability": null, - "low_alchemy": "20", - "high_alchemy": "30", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "You can mine more if you find another crashed star.", - "name": "Stardust", - "archery_ticket_price": "0", - "id": "13727", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "tradeable": "false" }, { - "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", + "id": "13280", + "name": "Broad-tipped bolts", + "examine": "For use against turoth and kurasks.", + "absorb": "0,0,0", "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,100", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equipment_slot": "13", + "shop_price": "50", + "tradeable": "true" }, { - "requirements": "{1,70}", - "shop_price": "70000", - "examine": "An ethereal shield.", + "id": "13281", + "name": "Ring of slaying(8)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", + "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, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" + }, + { + "id": "13282", + "name": "Ring of slaying(7)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", + "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, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" + }, + { + "id": "13283", + "name": "Ring of slaying(6)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", + "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, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" + }, + { + "id": "13284", + "name": "Ring of slaying(5)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", + "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, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" + }, + { + "id": "13285", + "name": "Ring of slaying(4)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", + "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, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" + }, + { + "id": "13286", + "name": "Ring of slaying(3)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", + "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, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" + }, + { + "id": "13287", + "name": "Ring of slaying(2)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", + "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, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" + }, + { + "id": "13288", + "name": "Ring of slaying(1)", + "examine": "An enchanted ring.", + "absorb": "0,0,0", + "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, + "equipment_slot": "12", + "shop_price": "150", + "tradeable": "false" + }, + { + "id": "13290", + "name": "Leaf-bladed sword", + "examine": "A razor-sharp sword.", + "absorb": "0,0,0", + "archery_ticket_price": "0", + "attack_anims": "381,381,390,381", + "attack_audios": "2517,2517,2500,2517", + "attack_speed": "4", + "bonuses": "67,62,0,0,0,0,0,0,0,0,0,50,0,0,0", + "defence_anim": "397", + "destroy": "false", + "durability": null, + "equip_audio": "2248", + "equipment_slot": "3", "rare_item": "true", - "durability": null, - "low_alchemy": "28000", - "high_alchemy": "42000", - "destroy": "true", + "requirements": "{0,50}-{18,55}", + "run_anim": "824", + "shop_price": "68000", + "stand_anim": "813", + "stand_turn_anim": "823", + "tradeable": "true", + "turn180_anim": "1206", + "turn90ccw_anim": "1208", + "turn90cw_anim": "1207", + "walk_anim": "1205", + "weapon_interface": "5" + }, + { + "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", - "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,0,1,0" - }, - { - "shop_price": "70000", - "examine": "An ethereal shield.", - "durability": null, - "low_alchemy": "28000", - "high_alchemy": "42000", - "destroy": "true", "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "39982", - "name": "Spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13735" + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "id": "13719", + "name": "Performance shortbow", + "examine": "Short, but effective.", + "absorb": "0,0,0", + "archery_ticket_price": "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", + "render_anim": "176", + "shop_price": "50", + "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", + "archery_ticket_price": "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 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", + "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", + "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, - "low_alchemy": "560000", - "high_alchemy": "840000", - "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,0,3,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.", - "durability": null, - "low_alchemy": "560000", - "high_alchemy": "840000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "936814", - "name": "Blessed spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "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,0,3,0" + "tradeable": "true" }, { - "shop_price": "2000000", - "examine": "An ethereal shield with an arcane sigil attached to it.", - "durability": null, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "15076199", + "id": "13739", "name": "Arcane spirit shield", - "tradeable": "true", + "examine": "An ethereal shield with an arcane sigil attached to it.", "archery_ticket_price": "0", - "id": "13739" + "attack_speed": "4", + "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, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "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,0,3,0" - }, - { - "shop_price": "2000000", - "examine": "An ethereal shield with a divine sigil attached to it.", - "durability": null, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "40003587", "name": "Divine spirit shield", - "tradeable": "true", + "examine": "An ethereal shield with a divine sigil attached to it.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "id": "13741" - }, - { + "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", - "examine": "An ethereal shield with an elysian sigil attached to it.", - "durability": null, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "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,0,3,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.", - "durability": null, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "28420591", - "name": "Elysian spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "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,0,3,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.", - "durability": null, - "low_alchemy": "800000", - "high_alchemy": "1200000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "3616112", - "name": "Spectral spirit shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13745" + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A sigil in the shape of an arcane rune.", - "rare_item": "true", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "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" - }, - { - "shop_price": "750000", - "examine": "A sigil in the shape of an arcane rune.", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "19508553", "name": "Arcane sigil", - "tradeable": "true", + "examine": "A sigil in the shape of an arcane rune.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "id": "13747" + "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": "750000", + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A sigil in the shape of a divine symbol.", - "rare_item": "true", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "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" - }, - { - "shop_price": "750000", - "examine": "A sigil in the shape of a divine symbol.", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "53026134", "name": "Divine sigil", - "tradeable": "true", + "examine": "A sigil in the shape of a divine symbol.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "id": "13749" + "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": "750000", + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A sigil marked with elysian signs.", - "rare_item": "true", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "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" - }, - { - "shop_price": "750000", - "examine": "A sigil marked with elysian signs.", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "36159098", "name": "Elysian sigil", - "tradeable": "true", + "examine": "A sigil marked with elysian signs.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "id": "13751" + "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": "750000", + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A sigil tempered with spectral powers.", - "rare_item": "true", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "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" - }, - { - "shop_price": "750000", - "examine": "A sigil tempered with spectral powers.", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "3116720", "name": "Spectral sigil", - "tradeable": "true", + "examine": "A sigil tempered with spectral powers.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "id": "13753" + "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": "750000", + "tradeable": "true" }, { - "shop_price": "750000", - "examine": "A bottle of holy elixir.", - "rare_item": "true", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "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" - }, - { - "shop_price": "750000", - "examine": "A bottle of holy elixir.", - "durability": null, - "low_alchemy": "300000", - "high_alchemy": "450000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "950904", "name": "Holy elixir", - "tradeable": "true", + "examine": "A bottle of holy elixir.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "id": "13755" + "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": "750000", + "tradeable": "true" }, { - "shop_price": "40", - "examine": "Logs cut from cursed willow roots.", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Cursed willow logs", + "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", - "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", + "name": "Cursed willow logs", + "examine": "Logs cut from cursed willow roots.", + "absorb": "0,0,0", "archery_ticket_price": "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" + "archery_ticket_price": "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 }, { - "examine": "I hope I don't meet any roundheads...", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "name": "Cavalier and mask", - "archery_ticket_price": "0", "id": "13763", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "name": "Cavalier and mask", + "examine": "I hope I don't meet any roundheads...", + "absorb": "0,0,0", + "archery_ticket_price": "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, - "low_alchemy": "2400", - "high_alchemy": "3600", - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "absorb": "0,0,0", - "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, - "low_alchemy": "3200", - "high_alchemy": "4800", - "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, - "low_alchemy": "8320", - "high_alchemy": "12480", - "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, - "low_alchemy": "24960", - "high_alchemy": "37440", - "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, - "low_alchemy": "8320", - "high_alchemy": "12480", - "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, - "low_alchemy": "25600", - "high_alchemy": "38400", - "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, - "low_alchemy": "51200", - "high_alchemy": "76800", - "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, - "low_alchemy": "5760", - "high_alchemy": "8640", - "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" - }, - { - "examine": "A series of connected metal rings.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "29145", - "name": "Rune chainbody", - "archery_ticket_price": "0", - "id": "13781", - "bonuses": "0,0,0,-15,0,63,72,78,-3,65,40,0,0,0,0" - }, - { - "examine": "These will protect my feet.", - "durability": null, - "low_alchemy": "5000", - "high_alchemy": "7500", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "7036", - "name": "Rune boots", - "archery_ticket_price": "0", - "id": "13782", - "bonuses": "0,0,0,-3,-1,12,13,14,0,0,10,2,0,0,0" - }, - { - "examine": "A medium sized helmet.", - "durability": null, - "low_alchemy": "7680", - "high_alchemy": "11520", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "10678", - "name": "Rune med helm", - "archery_ticket_price": "0", - "id": "13783", - "bonuses": "0,0,0,-3,-1,22,23,21,-1,22,7,0,0,0,0" - }, - { - "examine": "A stone helmet.", - "durability": null, - "low_alchemy": "18400", - "high_alchemy": "27600", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "26576", - "name": "Granite helm", - "archery_ticket_price": "0", - "id": "13784", - "bonuses": "0,0,0,-9,-7,31,33,29,-1,39,9,0,0,0,0" - }, - { - "examine": "Provides excellent protection.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,8", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "136414", - "name": "Granite body", - "archery_ticket_price": "0", - "id": "13785", - "bonuses": "0,0,0,-22,-5,87,84,79,-6,97,45,0,0,0,0" - }, - { - "examine": "These look pretty heavy.", - "durability": null, - "low_alchemy": "26400", - "high_alchemy": "39600", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,5", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "72213", - "name": "Granite legs", - "archery_ticket_price": "0", - "id": "13786", - "bonuses": "0,0,0,-31,-18,43,45,41,-4,68,20,0,0,0,0" - }, - { - "examine": "A medium square shield.", - "durability": null, - "low_alchemy": "15360", - "high_alchemy": "23040", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "22225", - "name": "Rune sq shield", - "archery_ticket_price": "0", - "id": "13787", - "bonuses": "0,0,0,-6,-2,38,40,36,0,38,35,0,0,0,0" - }, - { - "examine": "A solid stone shield.", - "durability": null, - "low_alchemy": "22400", - "high_alchemy": "33600", - "destroy": "true", - "attack_speed": "4", - "absorb": "5,0,10", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "32512", - "name": "Granite shield", - "archery_ticket_price": "0", - "id": "13788", - "bonuses": "0,0,0,-12,-8,40,42,38,0,65,50,0,0,0,0" - }, - { - "examine": "A big 'do about nothing.", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "42991", - "name": "A powdered wig", - "archery_ticket_price": "0", - "id": "13789", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "These'll help me stay alive.", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "811293", - "name": "Flared trousers", - "archery_ticket_price": "0", - "id": "13790", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Alas, someone has slashed my pantaloons.", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "19286", - "name": "Pantaloons", - "archery_ticket_price": "0", - "id": "13791", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A cap for wearing whil...zzzzzzzzz", - "durability": null, - "low_alchemy": "800", - "high_alchemy": "1200", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "75357", - "name": "Sleeping cap", - "archery_ticket_price": "0", - "id": "13792", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A diamond-topped cane.", - "durability": null, - "low_alchemy": "5760", - "high_alchemy": "8640", - "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" - }, - { - "examine": "Comes with a free rabbit!", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "333537", - "name": "Top hat", - "archery_ticket_price": "0", - "id": "13794", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Shear elegance.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "582969", - "name": "Sheep mask", - "archery_ticket_price": "0", - "id": "13795", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "I can get the look right, but can I do the waddle too?", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1159807", - "name": "Penguin mask", - "archery_ticket_price": "0", - "id": "13796", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "The bats have left the belltower.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "681595", - "name": "Bat mask", - "archery_ticket_price": "0", - "id": "13797", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "It's the year of the cat!", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2512014", - "name": "Cat mask", - "archery_ticket_price": "0", - "id": "13798", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "I'm hungry like the wolf.", - "durability": null, - "low_alchemy": "320", - "high_alchemy": "480", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "3240770", - "name": "Wolf mask", - "archery_ticket_price": "0", - "id": "13799", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Rune platebody with gold trim.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "343688", - "name": "Rune platebody (g)", - "archery_ticket_price": "0", - "id": "13800", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "examine": "Rune platelegs with gold trim.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "drop", - "grand_exchange_price": "238139", - "name": "Rune platelegs (g)", - "archery_ticket_price": "0", - "id": "13801", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune plateskirt with gold trim.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "37616", - "name": "Rune plateskirt (g)", - "archery_ticket_price": "0", - "id": "13802", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune full helmet with gold trim.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "247877", - "name": "Rune full helm(g)", - "archery_ticket_price": "0", - "id": "13803", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "examine": "Rune kiteshield with gold trim", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "118205", - "name": "Rune kiteshield (g)", - "archery_ticket_price": "0", - "id": "13804", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "Rune platebody with trim.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "100586", - "name": "Rune platebody (t)", - "archery_ticket_price": "0", - "id": "13805", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "examine": "Rune platelegs with trim!", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "81577", - "name": "Rune platelegs (t)", - "archery_ticket_price": "0", - "id": "13806", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune plateskirt with trim.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "37701", - "name": "Rune plateskirt (t)", - "archery_ticket_price": "0", - "id": "13807", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune full helmet with trim.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "95809", - "name": "Rune full helm (t)", - "archery_ticket_price": "0", - "id": "13808", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "examine": "A large, metal shield with a nice trim.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "46469", - "name": "Rune kiteshield (t)", - "archery_ticket_price": "0", - "id": "13809", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "Your money or your life!", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "252995", - "name": "Highwayman mask", - "archery_ticket_price": "0", - "id": "13810", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Parlez-vous francais?", - "durability": null, - "low_alchemy": "32", - "high_alchemy": "48", - "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, - "low_alchemy": "32", - "high_alchemy": "48", - "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, - "low_alchemy": "32", - "high_alchemy": "48", - "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" - }, - { - "examine": "All for one and one for all!", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "42014", - "name": "Tan cavalier", - "archery_ticket_price": "0", - "id": "13814", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "All for one and one for all!", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "61518", - "name": "Dark cavalier", - "archery_ticket_price": "0", - "id": "13815", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "All for one and one for all!", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "338530", - "name": "Black cavalier", - "archery_ticket_price": "0", - "id": "13816", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A minimalist's hat.", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "159694", - "name": "Red headband", - "archery_ticket_price": "0", - "id": "13817", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A minimalist's hat.", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "83543", - "name": "Black headband", - "archery_ticket_price": "0", - "id": "13818", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "A minimalist's hat.", - "durability": null, - "low_alchemy": "16", - "high_alchemy": "24", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "26474", - "name": "Brown headband", - "archery_ticket_price": "0", - "id": "13819", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "Rune platebody in the colours of Zamorak.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "868398", - "name": "Zamorak platebody", - "archery_ticket_price": "0", - "id": "13820", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "examine": "Rune platelegs in the colours of Zamorak.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "316989", - "name": "Zamorak platelegs", - "archery_ticket_price": "0", - "id": "13821", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune plateskirt in the colours of Zamorak.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "38597", - "name": "Zamorak plateskirt", - "archery_ticket_price": "0", - "id": "13822", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "A full helmet in the colours of Zamorak.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "618480", - "name": "Zamorak full helm", - "archery_ticket_price": "0", - "id": "13823", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "examine": "A Rune kiteshield in the colours of Zamorak.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "225574", - "name": "Zamorak kiteshield", - "archery_ticket_price": "0", - "id": "13824", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "Rune platebody in the colours of Saradomin.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1555099", - "name": "Saradomin platebody", - "archery_ticket_price": "0", - "id": "13825", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "examine": "Rune platelegs in the colours of Saradomin!", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "376849", - "name": "Saradomin platelegs", - "archery_ticket_price": "0", - "id": "13826", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune plateskirt in the colours of Saradomin.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "53048", - "name": "Saradomin plateskirt", - "archery_ticket_price": "0", - "id": "13827", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune full helmet in the colours of Saradomin.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1025974", - "name": "Saradomin full helm", - "archery_ticket_price": "0", - "id": "13828", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "examine": "Rune kiteshield in the colours of Saradomin.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "353916", - "name": "Saradomin kiteshield", - "archery_ticket_price": "0", - "id": "13829", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "Rune platebody in the colours of Guthix.", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "406762", - "name": "Guthix platebody", - "archery_ticket_price": "0", - "id": "13830", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "examine": "Rune platelegs in the colours of Guthix.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "181471", - "name": "Guthix platelegs", - "archery_ticket_price": "0", - "id": "13831", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune plateskirt in the colours of Guthix.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "38388", - "name": "Guthix plateskirt", - "archery_ticket_price": "0", - "id": "13832", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "A rune full helmet in the colours of Guthix.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "399963", - "name": "Guthix full helm", - "archery_ticket_price": "0", - "id": "13833", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "examine": "Rune kiteshield in the colours of Guthix.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "185266", - "name": "Guthix kiteshield", - "archery_ticket_price": "0", - "id": "13834", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" - }, - { - "examine": "Rune platebody with complete gold trim & plating.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "2034838", - "name": "Gilded platebody", - "archery_ticket_price": "0", - "id": "13835", - "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,40,0,0,0,0" - }, - { - "examine": "Rune platelegs with gold plate.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1299758", - "name": "Gilded platelegs", - "archery_ticket_price": "0", - "id": "13836", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune plateskirt with gold plate.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "84191", - "name": "Gilded plateskirt", - "archery_ticket_price": "0", - "id": "13837", - "bonuses": "0,0,0,-21,-7,51,49,47,-4,49,15,0,0,0,0" - }, - { - "examine": "Rune full helmet with gold plate.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1057068", - "name": "Gilded full helm", - "archery_ticket_price": "0", - "id": "13838", - "bonuses": "0,0,0,-6,-2,30,32,27,-1,30,7,0,0,0,0" - }, - { - "examine": "Rune kiteshield with gold plate.", - "durability": null, - "low_alchemy": "21760", - "high_alchemy": "32640", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,7", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "506093", - "name": "Gilded kiteshield", - "archery_ticket_price": "0", - "id": "13839", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,45,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Atk, Def, Str and LP xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (melee)", - "archery_ticket_price": "0", - "id": "13845", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Ranged xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (ranged)", - "archery_ticket_price": "0", - "id": "13846", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Magic xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (magic)", - "archery_ticket_price": "0", - "id": "13847", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Prayer xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (prayer)", - "archery_ticket_price": "0", - "id": "13848", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Agility xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (agility)", - "archery_ticket_price": "0", - "id": "13849", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Woodcutting xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (wc)", - "archery_ticket_price": "0", - "id": "13850", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Firemaking xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (fm)", - "archery_ticket_price": "0", - "id": "13851", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Mining xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (mining)", - "archery_ticket_price": "0", - "id": "13852", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Hunting xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (hunter)", - "archery_ticket_price": "0", - "id": "13853", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Thieving xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (thieving)", - "archery_ticket_price": "0", - "id": "13854", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Smithing xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (smithing)", - "archery_ticket_price": "0", - "id": "13855", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Fishing xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (fishing)", - "archery_ticket_price": "0", - "id": "13856", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "50000", - "examine": "A pair of gloves that gives you bonus Cooking xp.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "absorb": "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?", - "name": "Brawling gloves (cooking)", - "archery_ticket_price": "0", - "id": "13857", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "archery_ticket_price": "0", - "id": "13858", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1412812", - "name": "Zuriel's robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13859" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "absorb": "8,4,0", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "986668", - "name": "Zuriel's robe bottom", - "archery_ticket_price": "0", - "id": "13861", - "bonuses": "0,0,0,25,-7,38,35,44,24,0,30,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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "986668", - "name": "Zuriel's robe bottom", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13862" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "low_alchemy": "100000", - "high_alchemy": "150000", - "destroy": "true", - "attack_speed": "4", - "absorb": "6,3,0", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "361542", - "name": "Zuriel's hood", - "archery_ticket_price": "0", - "id": "13864", - "bonuses": "0,0,0,8,-2,20,16,22,8,0,15,0,0,0,0" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "250000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "100000", - "high_alchemy": "150000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "361542", - "name": "Zuriel's hood", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "100000", - "high_alchemy": "150000", - "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", - "low_alchemy": "120000", - "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": "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, - "high_alchemy": "180000", - "destroy": "true", - "weapon_interface": "1", - "render_anim": "28", - "attack_audios": "2555,0,0,0", - "name": "Zuriel's staff" - }, - { - "requirements": "{6,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "120000", - "high_alchemy": "180000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "2116989", - "name": "Zuriel's staff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13868" - }, - { - "turn90cw_anim": "1207", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "1205", - "rare_item": "true", - "low_alchemy": "120000", - "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", - "requirements": "", - "shop_price": "300000", - "durability": null, - "high_alchemy": "180000", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "archery_ticket_price": "0", - "id": "13870", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "926341", - "name": "Morrigan's leather body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13871" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "archery_ticket_price": "0", - "id": "13873", - "bonuses": "0,0,0,-10,23,35,29,37,46,35,30,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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop, Destroy after degrading.", - "grand_exchange_price": "510395", - "name": "Morrigan's leather chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13874" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "low_alchemy": "100000", - "high_alchemy": "150000", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,6,3", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "236411", - "name": "Morrigan's coif", - "archery_ticket_price": "0", - "id": "13876", - "bonuses": "0,0,0,-5,13,8,11,14,8,12,15,0,0,0,0" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "250000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "100000", - "high_alchemy": "150000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "236411", - "name": "Morrigan's coif", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "100000", - "high_alchemy": "150000", - "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, - "low_alchemy": "1600", - "high_alchemy": "2400", - "destroy": "true", - "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", - "archery_ticket_price": "0", - "id": "13879", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,0" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "has_special": "true", - "durability": null, - "low_alchemy": "1600", - "high_alchemy": "2400", - "destroy": "true", - "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)", - "archery_ticket_price": "0", - "id": "13880", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,0" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "has_special": "true", - "durability": null, - "low_alchemy": "1600", - "high_alchemy": "2400", - "destroy": "true", - "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+)", - "archery_ticket_price": "0", - "id": "13881", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,0" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "has_special": "true", - "durability": null, - "low_alchemy": "1600", - "high_alchemy": "2400", - "destroy": "true", - "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++)", - "archery_ticket_price": "0", - "id": "13882", - "bonuses": "0,0,0,0,105,0,0,0,0,0,0,0,0,0,0" - }, - { - "requirements": "{4,78}", - "shop_price": "4000", - "examine": "A vicious throwing axe.", - "has_special": "true", - "durability": null, - "low_alchemy": "1600", - "high_alchemy": "2400", - "destroy": "true", - "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", - "archery_ticket_price": "0", - "id": "13883", - "bonuses": "0,0,0,0,93,0,0,0,0,0,0,0,0,0,0" - }, - { - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "archery_ticket_price": "0", - "id": "13884", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1619096", - "name": "Statius's platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13885" - }, - { - "requirements": "{1,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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", - "archery_ticket_price": "0", - "id": "13887", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop, Destroy once it has been degraded.", - "grand_exchange_price": "2154168", - "name": "Vesta's chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13888" - }, - { - "requirements": "{1,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,8", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1735029", - "name": "Statius's platelegs", - "archery_ticket_price": "0", - "id": "13890", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1735029", - "name": "Statius's platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13891" - }, - { - "requirements": "{1,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,8", - "equipment_slot": "7", - "destroy_message": "Drop", - "grand_exchange_price": "1860767", - "name": "Vesta's plateskirt", - "archery_ticket_price": "0", - "id": "13893", - "bonuses": "3,5,5,-17,-4,86,100,112,-2,118,30,3,0,0,0" - }, - { - "requirements": "{1,78}", - "shop_price": "500000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1860767", - "name": "Vesta's plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13894" - }, - { - "requirements": "{1,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "200000", - "high_alchemy": "300000", - "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, - "low_alchemy": "100000", - "high_alchemy": "150000", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "equipment_slot": "0", - "destroy_message": "Drop", - "grand_exchange_price": "1301986", - "name": "Statius's full helm", - "archery_ticket_price": "0", - "id": "13896", - "bonuses": "3,3,4,-6,-2,65,70,63,-1,71,15,3,0,0,0" - }, - { - "requirements": "{1,78}", - "shop_price": "250000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "100000", - "high_alchemy": "150000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1301986", - "name": "Statius's full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13897" - }, - { - "remove_head": "true", - "requirements": "{1,78}", - "shop_price": "150000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "100000", - "high_alchemy": "150000", - "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", - "low_alchemy": "120000", - "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": "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, - "high_alchemy": "180000", - "destroy": "true", - "weapon_interface": "5", - "render_anim": "1426", - "attack_audios": "2500,2500,2517,2500", - "name": "Vesta's longsword" - }, - { - "requirements": "{0,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "120000", - "high_alchemy": "180000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "10464249", - "name": "Vesta's longsword", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "low_alchemy": "120000", - "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, - "high_alchemy": "180000", - "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", - "low_alchemy": "120000", - "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", - "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, - "high_alchemy": "180000", - "destroy": "true", - "weapon_interface": "10", - "attack_audios": "2504,0,0,0", - "name": "Statius's warhammer" - }, - { - "requirements": "{0,78}", - "shop_price": "300000", - "examine": "This item degrades in combat, and will turn to dust.", - "durability": null, - "low_alchemy": "120000", - "high_alchemy": "180000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop, Destroy once it has been degraded.", - "grand_exchange_price": "7424450", - "name": "Statius's warhammer", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13903" - }, - { - "turn90cw_anim": "821", - "examine": "This item degrades in combat, and will turn to dust.", - "walk_anim": "819", - "has_special": "true", - "low_alchemy": "120000", - "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", - "requirements": "", - "shop_price": "300000", - "durability": null, - "high_alchemy": "180000", - "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", - "low_alchemy": "120000", - "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", - "destroy_message": "Drop", - "grand_exchange_price": "5264612", - "stand_anim": "813", - "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, - "high_alchemy": "180000", + "attack_speed": "5", + "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0", + "defence_anim": "2079", "destroy": "true", - "weight": "4.2", - "weapon_interface": "14", + "destroy_message": "Drop", + "durability": null, + "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, - "low_alchemy": "120000", - "high_alchemy": "180000", - "destroy": "true", - "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, - "low_alchemy": "120000", - "high_alchemy": "180000", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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" - }, - { - "requirements": "{1,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "30000", - "high_alchemy": "45000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1897306", "name": "Corrupt statius's platebody", - "tradeable": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "6,0,12", "archery_ticket_price": "0", - "id": "13909" + "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": "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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" - }, - { - "requirements": "{1,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "30000", - "high_alchemy": "45000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "2546175", "name": "Corrupt vesta's chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13912" - }, - { - "requirements": "{1,78}", - "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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" - }, - { - "requirements": "{1,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "30000", - "high_alchemy": "45000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1734527", "name": "Corrupt statius's platelegs", - "tradeable": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "4,0,8", "archery_ticket_price": "0", - "id": "13915" + "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": "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "50000", - "high_alchemy": "75000", - "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" - }, - { - "requirements": "{1,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "50000", - "high_alchemy": "75000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1942627", "name": "Corrupt vesta's plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13918" - }, - { - "requirements": "{1,78}", - "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "50000", - "high_alchemy": "75000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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" - }, - { - "requirements": "{1,78}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1062835", "name": "Corrupt statius's full helm", - "tradeable": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "3,0,6", "archery_ticket_price": "0", - "id": "13921" + "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, + "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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" }, { - "turn90cw_anim": "1207", - "examine": "This item degrades while worn, and will turn to dust.", - "walk_anim": "1205", - "low_alchemy": "30000", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "absorb": "0,0,0", - "equipment_slot": "3", - "attack_anims": "381,390,390,390", - "destroy_message": "Drop", - "grand_exchange_price": "3739998", - "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", - "durability": null, - "high_alchemy": "45000", - "destroy": "true", - "weapon_interface": "5", - "render_anim": "1426", - "name": "Corrupt vesta's longsword" - }, - { - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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" - }, - { - "requirements": "{0,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "30000", - "high_alchemy": "45000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "3033516", "name": "Corrupt statius's warhammer", - "tradeable": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "0,0,0", "archery_ticket_price": "0", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "30000", - "high_alchemy": "45000", - "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, - "low_alchemy": "50000", - "high_alchemy": "75000", - "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" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "50000", - "high_alchemy": "75000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "253230", "name": "Corrupt zuriel's robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13933" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "75000", "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "50000", - "high_alchemy": "75000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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" - }, - { - "requirements": "{1,78}-{6,78}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "43778", "name": "Corrupt zuriel's hood", - "tradeable": "true", + "examine": "This item degrades while worn, and will turn to dust.", + "absorb": "6,3,0", "archery_ticket_price": "0", - "id": "13939" + "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,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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "28000", - "high_alchemy": "42000", - "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" - }, - { - "requirements": "{6,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "28000", - "high_alchemy": "42000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "219418", - "name": "Corrupt zuriel's staff", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13942" - }, - { - "requirements": "{6,78}", - "shop_price": "70000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "28000", - "high_alchemy": "42000", - "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, - "low_alchemy": "50000", - "high_alchemy": "75000", - "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" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "50000", - "high_alchemy": "75000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "387212", - "name": "Corrupt morrigan's leather body", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13945" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "75000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "50000", - "high_alchemy": "75000", - "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, - "low_alchemy": "50000", - "high_alchemy": "75000", - "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" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "125000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "50000", - "high_alchemy": "75000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "84592", - "name": "Corrupt morrigan's leather chaps", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13948" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "75000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "50000", - "high_alchemy": "75000", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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" - }, - { - "requirements": "{1,78}-{4,78}", - "shop_price": "50000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "20000", - "high_alchemy": "30000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "63502", - "name": "Corrupt morrigan's coif", - "tradeable": "true", - "archery_ticket_price": "0", - "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, - "low_alchemy": "20000", - "high_alchemy": "30000", - "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, - "low_alchemy": "400", - "high_alchemy": "600", - "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,0" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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,0" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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,0" - }, - { - "requirements": "{4,78}", - "shop_price": "10000", - "examine": "A vicious javelin.", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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,0" - }, - { - "requirements": "{4,78}", - "shop_price": "1000", - "examine": "A vicious throwing axe.", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "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,0" - }, - { - "requirements": "{0,78}-{4,78}", - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "28800", - "high_alchemy": "43200", - "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" - }, - { - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "28800", - "high_alchemy": "43200", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "168278", - "name": "Corrupt dragon chainbody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13959" - }, - { - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "28800", - "high_alchemy": "43200", - "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, - "low_alchemy": "14400", - "high_alchemy": "21600", - "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" - }, - { - "shop_price": "60000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "14400", - "high_alchemy": "21600", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "121835", - "name": "Corrupt dragon med helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13962" - }, - { - "remove_head": "true", - "shop_price": "36000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "14400", - "high_alchemy": "21600", - "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, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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" - }, - { - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "79703", - "name": "Corrupt dragon sq shield", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13965" - }, - { - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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" - }, - { - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Destroy", - "grand_exchange_price": "71066", - "name": "Corrupt dragon plateskirt", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13968" - }, - { - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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" - }, - { - "shop_price": "120000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "87957", - "name": "Corrupt dragon platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13971" - }, - { - "shop_price": "72000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "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, - "low_alchemy": "28800", - "high_alchemy": "43200", - "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, - "low_alchemy": "28800", - "high_alchemy": "43200", - "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, - "low_alchemy": "28800", - "high_alchemy": "43200", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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" - }, - { - "shop_price": "100000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "40000", - "high_alchemy": "60000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "338644", - "name": "Corrupt dragon scimitar", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13980" - }, - { - "shop_price": "60000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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" - }, - { - "shop_price": "100000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "40000", - "high_alchemy": "60000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "207053", - "name": "Corrupt dragon longsword", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "13983" - }, - { - "shop_price": "60000", - "examine": "This item degrades while worn, and will turn to dust.", - "durability": null, - "low_alchemy": "40000", - "high_alchemy": "60000", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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, - "low_alchemy": "8800", - "high_alchemy": "13200", - "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, - "low_alchemy": "8800", - "high_alchemy": "13200", - "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, - "low_alchemy": "8800", - "high_alchemy": "13200", - "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" - }, - { - "shop_price": "5", - "examine": "A book about the Wilderness.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "You can get another copy by searching bookshelves around RuneScape.", - "name": "Pvp worlds manual", - "archery_ticket_price": "0", - "id": "14056", - "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, - "low_alchemy": "10", - "high_alchemy": "15", - "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" - }, - { - "shop_price": "2", - "examine": "What a curious and ghoulish smell!", - "durability": null, - "high_alchemy": "1", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "If you drop the goulash, it will be destroyed. You can get some more by dipping a bowl into Maggie's cauldron.", - "name": "Goulash", - "archery_ticket_price": "0", - "id": "14058", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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" - }, - { - "shop_price": "15", - "examine": "Tattered but trendy.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "You can get another set of warlock legs from Diango in Draynor Village.", - "name": "Warlock legs", - "archery_ticket_price": "0", - "id": "14077", - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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" - }, - { - "shop_price": "15", - "examine": "Tattered but trendy.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "You can get another witch top from Diango in Draynor.", - "name": "Witch skirt", - "archery_ticket_price": "0", - "id": "14079", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "15", - "examine": "Ragged but rugged.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "1", - "destroy_message": "You can get another witch cloak from Diango in Draynor.", - "name": "Witch cloak", - "archery_ticket_price": "0", - "id": "14080", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "15", - "examine": "Ragged but rugged.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "1", - "destroy_message": "You can get another warlock cloak from Diango in Draynor Village.", - "name": "Warlock cloak", - "archery_ticket_price": "0", - "id": "14081", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "6", - "high_alchemy": "9", - "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" - }, - { - "shop_price": "15", - "examine": "Tattered but trendy.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "7", - "destroy_message": "You can get another witch top from Diango in Draynor.", - "name": "Witch skirt", - "archery_ticket_price": "0", - "id": "14087", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "15", - "examine": "Ragged but rugged.", - "durability": null, - "low_alchemy": "6", - "high_alchemy": "9", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "1", - "destroy_message": "You can get another witch cloak from Diango in Draynor.", - "name": "Witch cloak", - "archery_ticket_price": "0", - "id": "14088", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "examine": "", - "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" - }, - { - "examine": "", - "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" - }, - { - "examine": "", - "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" - }, - { - "examine": "", - "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" - }, - { - "examine": "", - "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" - }, - { - "shop_price": "21333", - "examine": "Your volatile tool wants to make some bows or arrows.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "1", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Volatile clay fletching knife", - "archery_ticket_price": "0", - "id": "14103", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "Drop", - "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", - "high_alchemy": "1", - "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", - "high_alchemy": "1", - "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" + "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": "1000", + "weapon_interface": "18" }, { - "shop_price": "30", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "4", + "id": "13954", + "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", - "destroy_message": "Drop", - "name": "Pickaxe (class 1)", - "archery_ticket_price": "0", - "id": "14122", - "bonuses": "10,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "render_anim": "1381", + "requirements": "{4,20}", + "shop_price": "10000", + "weapon_interface": "18" }, { - "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", + "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", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2244", "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Pickaxe (class 2)", - "archery_ticket_price": "0", - "id": "14124", - "bonuses": "15,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "render_anim": "1381", + "requirements": "{4,20}", + "shop_price": "10000", + "weapon_interface": "18" }, { - "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", + "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", - "destroy_message": "Drop", - "name": "Pickaxe (class 3)", - "archery_ticket_price": "0", - "id": "14126", - "bonuses": "20,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "render_anim": "1381", + "requirements": "{4,20}", + "shop_price": "10000", + "weapon_interface": "18" }, { - "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", + "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", - "destroy_message": "Drop", - "name": "Pickaxe (class 4)", - "archery_ticket_price": "0", - "id": "14128", - "bonuses": "25,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "requirements": "{4,20}", + "shop_price": "1000", + "weapon_interface": "18" }, { - "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", + "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": "14129" + "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" }, { - "shop_price": "150", - "examine": "The sacred clay has become hard enough to chip stone.", - "durability": null, - "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", - "weapon_interface": "4", + "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", + "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": "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", + "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" + }, + { + "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", + "durability": null, + "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", + "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", - "destroy_message": "Drop", - "name": "Pickaxe (class 5)", - "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", + "render_anim": "2586", + "requirements": "{0,60}", + "shop_price": "120000", "weapon_interface": "2", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Hatchet (class 1)", - "archery_ticket_price": "0", - "id": "14132", - "bonuses": "0,10,0,0,0,0,0,0,0,0,0,0,0,0,0" + "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", - "shop_price": "30", - "examine": "The sacred clay has become sharp enough to cut wood.", "durability": null, - "name": "Hatchet (class 1)", + "render_anim": "2586", + "requirements": "{0,60}", + "shop_price": "120000", "tradeable": "true", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14133" + "weight": "2.7" }, { - "shop_price": "60", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, + "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", - "attack_speed": "4", + "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", - "destroy_message": "Drop", - "name": "Hatchet (class 2)", - "archery_ticket_price": "0", - "id": "14134", - "bonuses": "0,15,0,0,0,0,0,0,0,0,0,0,0,0,0" + "shop_price": "30000", + "weapon_interface": "5" }, { - "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", + "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", - "id": "14135" + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "equip_audio": "2248", + "shop_price": "30000", + "tradeable": "true" }, { - "shop_price": "90", - "examine": "The sacred clay has become sharp enough to cut wood.", - "durability": null, - "destroy": "true", - "attack_speed": "4", - "weapon_interface": "2", + "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", - "destroy_message": "Drop", - "name": "Hatchet (class 3)", - "archery_ticket_price": "0", - "id": "14136", - "bonuses": "0,20,0,0,0,0,0,0,0,0,0,0,0,0,0" + "shop_price": "18000", + "weapon_interface": "5" }, { - "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", + "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", - "destroy_message": "Drop", - "name": "Hatchet (class 4)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Bow (class 1)", - "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)", - "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)", - "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)", - "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)", - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "2", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "name": "Arrows (class 1)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14202", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,15,0,0", - "equipment_slot": "13" - }, - { - "destroy_message": "Drop", - "shop_price": "4", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "name": "Arrows (class 2)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14203", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,30,0,0", - "equipment_slot": "13" - }, - { - "destroy_message": "Drop", - "shop_price": "6", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "name": "Arrows (class 3)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14204", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,45,0,0", - "equipment_slot": "13" - }, - { - "destroy_message": "Drop", - "shop_price": "8", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "name": "Arrows (class 4)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14205", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,60,0,0", - "equipment_slot": "13" - }, - { - "destroy_message": "Drop", - "shop_price": "10", - "examine": "The Sacred Clay has transformed into a wood-like substance.", - "durability": null, - "name": "Arrows (class 5)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14206", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,75,0,0", - "equipment_slot": "13" - }, - { - "shop_price": "150", - "examine": "3 doses of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(5)", - "archery_ticket_price": "0", - "id": "14207", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "3 doses of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14208" - }, - { - "shop_price": "120", - "examine": "4 doses of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(4)", - "archery_ticket_price": "0", - "id": "14209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "120", - "examine": "4 doses of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14210" - }, - { - "shop_price": "90", - "examine": "3 doses of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(3)", - "archery_ticket_price": "0", - "id": "14211", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "90", - "examine": "3 doses of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14212" - }, - { - "shop_price": "60", - "examine": "2 doses of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(2)", - "archery_ticket_price": "0", - "id": "14213", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "60", - "examine": "2 doses of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14214" - }, - { - "shop_price": "30", - "examine": "1 dose of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(1)", - "archery_ticket_price": "0", - "id": "14215", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "1 dose of Prayer restore potion.", - "durability": null, - "low_alchemy": "61", - "high_alchemy": "91", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Prayer potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14216" - }, - { - "shop_price": "150", - "examine": "4 doses of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (5)", - "archery_ticket_price": "0", - "id": "14217", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "4 doses of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14218" - }, - { - "shop_price": "120", - "examine": "4 doses of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (4)", - "archery_ticket_price": "0", - "id": "14219", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "120", - "examine": "4 doses of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14220" - }, - { - "shop_price": "90", - "examine": "3 doses of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (3)", - "archery_ticket_price": "0", - "id": "14221", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "90", - "examine": "3 doses of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14222" - }, - { - "shop_price": "60", - "examine": "2 doses of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (2)", - "archery_ticket_price": "0", - "id": "14223", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "60", - "examine": "2 doses of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14224" - }, - { - "shop_price": "30", - "examine": "1 dose of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (1)", - "archery_ticket_price": "0", - "id": "14225", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "1 dose of energy potion.", - "durability": null, - "low_alchemy": "58", - "high_alchemy": "87", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Energy potion (1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14226" - }, - { - "shop_price": "150", - "examine": "5 doses of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(5)", - "archery_ticket_price": "0", - "id": "14227", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "5 doses of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14228" - }, - { - "shop_price": "120", - "examine": "4 doses of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(4)", - "archery_ticket_price": "0", - "id": "14229", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "120", - "examine": "4 doses of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14230" - }, - { - "shop_price": "90", - "examine": "3 doses of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(3)", - "archery_ticket_price": "0", - "id": "14231", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "90", - "examine": "3 doses of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14232" - }, - { - "shop_price": "60", - "examine": "2 doses of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(2)", - "archery_ticket_price": "0", - "id": "14233", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "60", - "examine": "2 doses of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14234" - }, - { - "shop_price": "30", - "examine": "1 dose of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(1)", - "archery_ticket_price": "0", - "id": "14235", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "1 dose of super Attack potion.", - "durability": null, - "low_alchemy": "72", - "high_alchemy": "108", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super attack(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14236" - }, - { - "shop_price": "150", - "examine": "5 doses of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(5)", - "archery_ticket_price": "0", - "id": "14237", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "5 doses of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14238" - }, - { - "shop_price": "120", - "examine": "4 doses of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(4)", - "archery_ticket_price": "0", - "id": "14239", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "120", - "examine": "4 doses of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14240" - }, - { - "shop_price": "90", - "examine": "3 doses of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(3)", - "archery_ticket_price": "0", - "id": "14241", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "90", - "examine": "3 doses of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14242" - }, - { - "shop_price": "60", - "examine": "2 doses of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(2)", - "archery_ticket_price": "0", - "id": "14243", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "60", - "examine": "2 doses of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14244" - }, - { - "shop_price": "30", - "examine": "1 dose of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(1)", - "archery_ticket_price": "0", - "id": "14245", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "1 dose of super Strength potion.", - "durability": null, - "low_alchemy": "88", - "high_alchemy": "132", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Super strength(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14246" - }, - { - "shop_price": "150", - "examine": "4 doses of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(5)", - "archery_ticket_price": "0", - "id": "14247", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "5 doses of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14248" - }, - { - "shop_price": "120", - "examine": "4 doses of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(4)", - "archery_ticket_price": "0", - "id": "14249", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "120", - "examine": "4 doses of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14250" - }, - { - "shop_price": "90", - "examine": "3 doses of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(3)", - "archery_ticket_price": "0", - "id": "14251", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "90", - "examine": "3 doses of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14252" - }, - { - "shop_price": "60", - "examine": "2 doses of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(2)", - "archery_ticket_price": "0", - "id": "14253", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "60", - "examine": "2 doses of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14254" - }, - { - "shop_price": "30", - "examine": "1 dose of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(1)", - "archery_ticket_price": "0", - "id": "14255", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "1 dose of ranging potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Ranging potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14256" - }, - { - "shop_price": "150", - "examine": "3 doses of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(5)", - "archery_ticket_price": "0", - "id": "14257", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "3 doses of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14258" - }, - { - "shop_price": "120", - "examine": "4 doses of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(4)", - "archery_ticket_price": "0", - "id": "14259", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "120", - "examine": "4 doses of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14260" - }, - { - "shop_price": "90", - "examine": "3 doses of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(3)", - "archery_ticket_price": "0", - "id": "14261", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "90", - "examine": "3 doses of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14262" - }, - { - "shop_price": "60", - "examine": "2 doses of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(2)", - "archery_ticket_price": "0", - "id": "14263", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "60", - "examine": "2 doses of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14264" - }, - { - "shop_price": "30", - "examine": "1 dose of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(1)", - "archery_ticket_price": "0", - "id": "14265", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "1 dose of Defence Potion.", - "durability": null, - "low_alchemy": "352", - "high_alchemy": "528", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Defence potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14266" - }, - { - "shop_price": "150", - "examine": "4 doses of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(5)", - "archery_ticket_price": "0", - "id": "14267", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "5 doses of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14268" - }, - { - "shop_price": "120", - "examine": "4 doses of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(4)", - "archery_ticket_price": "0", - "id": "14269", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "120", - "examine": "4 doses of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14270" - }, - { - "shop_price": "90", - "examine": "3 doses of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(3)", - "archery_ticket_price": "0", - "id": "14271", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "90", - "examine": "3 doses of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14272" - }, - { - "shop_price": "60", - "examine": "2 doses of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(2)", - "archery_ticket_price": "0", - "id": "14273", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "60", - "examine": "2 doses of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14274" - }, - { - "shop_price": "30", - "examine": "1 dose of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(1)", - "archery_ticket_price": "0", - "id": "14275", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "1 dose of Magic potion.", - "durability": null, - "low_alchemy": "224", - "high_alchemy": "336", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Magic potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14276" - }, - { - "shop_price": "150", - "examine": "3 doses of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(5)", - "archery_ticket_price": "0", - "id": "14277", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "150", - "examine": "3 doses of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(5)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14278" - }, - { - "shop_price": "120", - "examine": "4 doses of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(4)", - "archery_ticket_price": "0", - "id": "14279", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "120", - "examine": "4 doses of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(4)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14280" - }, - { - "shop_price": "90", - "examine": "3 doses of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(3)", - "archery_ticket_price": "0", - "id": "14281", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "90", - "examine": "3 doses of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(3)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14282" - }, - { - "shop_price": "60", - "examine": "2 doses of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(2)", - "archery_ticket_price": "0", - "id": "14283", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "60", - "examine": "2 doses of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(2)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14284" - }, - { - "shop_price": "30", - "examine": "1 dose of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(1)", - "archery_ticket_price": "0", - "id": "14285", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "30", - "examine": "1 dose of Summoning potion.", - "durability": null, - "low_alchemy": "76", - "high_alchemy": "114", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4", - "name": "Summoning potion(1)", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14286" - }, - { - "shop_price": "30", - "examine": "The sacred clay has transformed into a vicious, curved sword.", - "durability": null, - "destroy": "true", - "attack_speed": "4", + "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", - "render_anim": "1629", + "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", - "destroy_message": "Drop", - "name": "Scimitar (class 1)", - "archery_ticket_price": "0", - "id": "14287", - "bonuses": "0,55,0,0,0,0,0,0,0,0,0,15,0,0,0" + "render_anim": "1928", + "shop_price": "60000", + "weapon_interface": "6" }, { - "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", + "id": "13982", + "name": "Corrupt dragon longsword", + "examine": "This item degrades while worn, and will turn to dust.", "absorb": "0,0,0", - "render_anim": "1629", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Scimitar (class 2)", "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", - "destroy_message": "Drop", - "name": "Scimitar (class 3)", - "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", - "destroy_message": "Drop", - "name": "Scimitar (class 4)", - "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", - "destroy_message": "Drop", - "name": "Scimitar (class 5)", - "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", - "destroy_message": "Drop", - "name": "Dagger (class 1)", - "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", - "destroy_message": "Drop", - "name": "Dagger (class 2)", - "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", - "destroy_message": "Drop", - "name": "Dagger (class 3)", - "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", - "destroy_message": "Drop", - "name": "Dagger (class 4)", - "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", - "destroy_message": "Drop", - "name": "Dagger (class 5)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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)", - "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" - }, - { - "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)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14327", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,15,0,15,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "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)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14329", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,30,0,30,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay transformed into a light, cloth-like material.", - "durability": null, - "name": "Robe bottom (class 3)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14331", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,45,0,45,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "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)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14333", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,60,0,60,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "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)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14335", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,75,0,75,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 1)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14337", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,10,0,10,0,0,0,0", - "equipment_slot": "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" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 2)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14339", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,20,0,20,0,0,0,0", - "equipment_slot": "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" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 3)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14341", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,30,0,30,0,0,0,0", - "equipment_slot": "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" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 4)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14343", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,40,0,40,0,0,0,0", - "equipment_slot": "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" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a light, cloth-like material.", - "durability": null, - "name": "Hat (class 5)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14345", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,50,0,50,0,0,0,0", - "equipment_slot": "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)", - "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)", - "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)", - "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)", - "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)", - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 1)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14357", - "absorb": "0,0,0", - "bonuses": "0,0,0,-30,-10,15,15,15,-10,7,15,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 2)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14359", - "absorb": "0,0,0", - "bonuses": "0,0,0,-30,-10,30,30,30,-10,15,30,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 3)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14361", - "absorb": "0,0,0", - "bonuses": "0,0,0,-30,-10,45,45,45,-10,23,45,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 4)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14363", - "absorb": "0,0,0", - "bonuses": "0,0,0,-30,-10,60,60,60,-10,30,60,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay has transformed into a hard, metallic substance.", - "durability": null, - "name": "Platelegs (class 5)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14365", - "absorb": "0,0,0", - "bonuses": "0,0,0,-30,-10,75,75,75,-10,37,75,0,0,0,0", - "equipment_slot": "7" - }, - { - "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)", - "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)", - "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)", - "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)", - "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)", - "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_anims": "390,390,381,390", + "attack_audios": "2500,2500,2517,2500", "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", - "equipment_slot": "3", + "bonuses": "58,69,-2,0,0,0,3,2,1,0,0,71,0,0,0", + "defence_anim": "397", + "destroy": "true", "destroy_message": "Drop", - "name": "Staff (class 1)", - "archery_ticket_price": "0", - "id": "14377", - "bonuses": "0,0,0,55,0,0,0,0,0,0,0,0,0,0,0" + "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" }, { - "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", + "id": "13983", + "name": "Corrupt dragon longsword", + "examine": "This item degrades while worn, and will turn to dust.", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14378" + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "shop_price": "100000", + "tradeable": "true" }, { - "shop_price": "60", - "examine": "The sacred clay has transformed into a light weight, magical stick.", - "durability": null, - "destroy": "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", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", + "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", - "destroy_message": "Drop", - "name": "Staff (class 2)", - "archery_ticket_price": "0", - "id": "14379", - "bonuses": "0,0,0,90,0,0,0,0,0,0,0,0,0,0,0" + "render_anim": "1582", + "shop_price": "60000", + "weapon_interface": "5" }, { - "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", + "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_speed": "4", - "id": "14380" - }, - { - "shop_price": "90", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "destroy": "true", + "attack_anims": "390,390,381,390", + "attack_audios": "2508,2508,25092508", "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", + "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", - "destroy_message": "Drop", - "name": "Staff (class 3)", - "archery_ticket_price": "0", - "id": "14381", - "bonuses": "0,0,0,135,0,0,0,0,0,0,0,0,0,0,0" + "requirements": "{0,60}", + "shop_price": "50000", + "weapon_interface": "8", + "weight": "1.8" }, { - "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", + "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", - "id": "14382" + "defence_anim": "397", + "destroy": "true", + "destroy_message": "Drop", + "durability": null, + "requirements": "{0,60}", + "shop_price": "50000", + "tradeable": "true", + "weight": "1.8" }, { - "shop_price": "120", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "destroy": "true", + "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", - "weapon_interface": "1", - "absorb": "0,0,0", - "render_anim": "28", + "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", - "destroy_message": "Drop", - "name": "Staff (class 4)", - "archery_ticket_price": "0", - "id": "14383", - "bonuses": "0,0,0,180,0,0,0,0,0,0,0,0,0,0,0" + "requirements": "{0,60}", + "shop_price": "30000", + "weapon_interface": "8", + "weight": "1.8" }, { - "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", + "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_speed": "4", - "id": "14384" - }, - { - "shop_price": "150", - "examine": "The sacred clay has transformed into a lightweight, magical stick.", - "durability": null, - "destroy": "true", + "attack_anims": "2080,2081,2082,2080", "attack_speed": "5", - "weapon_interface": "1", - "absorb": "0,0,0", + "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", - "destroy_message": "Drop", - "name": "Staff (class 5)", - "archery_ticket_price": "0", - "id": "14385", - "bonuses": "0,0,0,225,0,0,0,0,0,0,0,0,0,0,0" + "render_anim": "28", + "shop_price": "24000", + "two_handed": "true", + "weapon_interface": "14" }, { - "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", + "id": "14056", + "name": "Pvp worlds manual", + "examine": "A book about the Wilderness.", "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)", - "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)", - "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)", - "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)", - "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)", - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "30", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 1)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14401", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,7,7,7,0,15,15,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "60", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 2)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14403", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,15,15,15,0,30,30,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "90", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 3)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14405", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,23,23,23,0,45,45,0,0,0,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "120", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 4)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14407", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,30,30,30,0,60,60,0,0,1,0", - "equipment_slot": "7" - }, - { - "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" - }, - { - "destroy_message": "Drop", - "shop_price": "150", - "examine": "The sacred clay transformed into a flexible mesh.", - "durability": null, - "name": "Chaps (class 5)", - "destroy": "true", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14409", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,37,37,37,0,75,75,0,0,0,0", - "equipment_slot": "7" - }, - { - "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)", - "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)", - "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)", - "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)", - "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)", - "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", - "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)", - "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)", - "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)", - "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)", - "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)", - "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" - }, - { - "shop_price": "5", - "examine": "A vine flower grown through magical means.", - "durability": null, - "low_alchemy": "2", - "high_alchemy": "3", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Vine flower", - "archery_ticket_price": "0", - "id": "14458", - "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" - }, - { - "shop_price": "10", - "examine": "Some dirty clothes that apparently belong to Movario.", - "durability": null, - "low_alchemy": "4", - "high_alchemy": "6", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "You got these clothes from a washerman who lives near the Khazard battle arena.", - "name": "Dirty laundry", - "archery_ticket_price": "0", - "id": "14460", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "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, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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" - }, - { - "shop_price": "30000", - "examine": "A badly damaged lump of dragon metal.", - "durability": null, - "low_alchemy": "12000", - "high_alchemy": "18000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "955683", - "name": "Ruined dragon armour lump", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14473" - }, - { - "shop_price": "30000", - "examine": "A badly damaged slice of dragon metal.", - "durability": null, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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" - }, - { - "shop_price": "30000", - "examine": "A badly damaged slice of dragon metal.", - "durability": null, - "low_alchemy": "12000", - "high_alchemy": "18000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1761141", - "name": "Ruined dragon armour slice", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14475" - }, - { - "shop_price": "30000", - "examine": "A badly damaged shard of dragon metal.", - "durability": null, - "low_alchemy": "12000", - "high_alchemy": "18000", - "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" - }, - { - "shop_price": "30000", - "examine": "A badly damaged shard of dragon metal.", - "durability": null, - "low_alchemy": "12000", - "high_alchemy": "18000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "1090493", - "name": "Ruined dragon armour shard", - "tradeable": "true", - "archery_ticket_price": "0", - "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", + "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", + "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" }, { - "requirements": "{1,60}", - "shop_price": "1760000", + "id": "14479", + "name": "Dragon platebody", "examine": "Provides excellent protection.", - "durability": null, - "low_alchemy": "704000", - "high_alchemy": "1056000", - "destroy": "true", - "attack_speed": "4", "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", - "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" - }, - { - "shop_price": "1760000", - "examine": "Provides excellent protection.", - "durability": null, - "low_alchemy": "704000", - "high_alchemy": "1056000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "4453643", - "name": "Dragon platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14480" - }, - { "requirements": "{1,60}", - "examine": "Provides excellent protection.", - "durability": null, - "low_alchemy": "704000", - "high_alchemy": "1056000", - "destroy": "true", - "attack_speed": "4", - "absorb": "4,0,9", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "4453643", + "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", - "bonuses": "0,0,0,-30,0,109,107,97,-6,106,50,0,0,0,0" + "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}" }, { - "examine": "A set of fighting claws.", - "has_special": "true", - "rare_item": "true", - "low_alchemy": "27000", - "attack_speed": "4", - "two_handed": "true", - "absorb": "0,0,0", - "defence_anim": "397", - "equipment_slot": "3", - "attack_anims": "393,393,1067,393", - "destroy_message": "Drop", - "grand_exchange_price": "1472441", - "tradeable": "true", - "archery_ticket_price": "0", "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", - "requirements": "{0,60}", - "shop_price": "67500", - "durability": null, - "high_alchemy": "40500", + "defence_anim": "397", "destroy": "true", - "weapon_interface": "9", - "render_anim": "2583", - "lendable": "true", - "name": "Dragon claws" - }, - { - "shop_price": "67500", - "examine": "A set of fighting claws.", - "durability": null, - "low_alchemy": "27000", - "high_alchemy": "40500", - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", "destroy_message": "Drop", - "grand_exchange_price": "1472441", - "name": "Dragon claws", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14485" - }, - { - "requirements": "{0,60}", - "examine": "A set of fighting claws.", + "durability": null, + "equip_audio": "1003", + "equipment_slot": "3", "has_special": "true", - "durability": null, - "low_alchemy": "27000", - "high_alchemy": "40500", - "destroy": "true", - "attack_speed": "4", - "two_handed": "true", - "weapon_interface": "9", - "absorb": "0,0,0", "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "1472441", + "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", - "bonuses": "41,57,-4,0,0,13,26,7,0,0,0,56,0,0,0" + "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" }, { - "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", + "name": "Enriched snapdragon", + "examine": "An enriched snapdragon herb.", "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", + "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", - "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" + "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 }, { - "requirements": "{1,40}", - "shop_price": "65000", - "examine": "These look pretty heavy, but very elite.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", + "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", - "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" - }, - { - "shop_price": "65000", - "examine": "These look pretty heavy, but very elite.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "41559", - "name": "Elite black platelegs", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14491" - }, - { "requirements": "{1,40}", - "shop_price": "64000", - "examine": "Provides excellent protection, and is elite.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", + "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", - "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" - }, - { + "requirements": "{1,40}", "shop_price": "64000", - "examine": "Provides excellent protection, and is elite.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "38275", - "name": "Elite black platebody", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14493" + "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", - "examine": "A full face helmet, and elite.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "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" + "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", - "examine": "A full face helmet, and elite.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "30542", - "name": "Elite black full helm", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14495" + "tradeable": "true" }, { - "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", + "name": "Cell key", + "examine": "Opens the cells beneath the Black Knights' Fortress. (While Guthix Sleeps)", "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, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "false", - "attack_speed": "4", - "absorb": "3,1,0", - "equipment_slot": "4", - "destroy_message": "", - "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.", - "durability": null, - "low_alchemy": "48000", - "high_alchemy": "72000", - "destroy": "false", - "attack_speed": "4", - "destroy_message": "", - "grand_exchange_price": "71098", - "name": "Dagon'hai robe top", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14498" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "15000", - "examine": "A hat worn by members of the Dagon'hai.", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "destroy": "false", - "attack_speed": "4", - "absorb": "1,0,0", - "equipment_slot": "0", - "destroy_message": "", - "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.", - "durability": null, - "low_alchemy": "6000", - "high_alchemy": "9000", - "destroy": "false", - "attack_speed": "4", - "destroy_message": "", - "grand_exchange_price": "9580", - "name": "Dagon'hai hat", - "tradeable": "true", - "archery_ticket_price": "0", - "id": "14500" - }, - { - "requirements": "{1,20}-{6,40}", - "shop_price": "80000", - "examine": "A robe worn by members of the Dagon'hai.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", - "destroy": "false", - "attack_speed": "4", - "absorb": "2,1,0", - "equipment_slot": "7", - "destroy_message": "", - "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.", - "durability": null, - "low_alchemy": "32000", - "high_alchemy": "48000", - "destroy": "false", - "attack_speed": "4", - "destroy_message": "", - "grand_exchange_price": "45691", - "name": "Dagon'hai robe bottom", - "tradeable": "true", - "archery_ticket_price": "0", - "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", + "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" }, { - "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", + "name": "Enriched snapdragon seed", + "examine": "An enriched snapdragon seed. The herb that grows from this seed will be extra potent!", "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", + "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", - "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", + "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", - "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", + "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", - "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", + "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", - "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", + "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", - "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", + "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", - "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", + "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", - "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", + "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", - "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", + "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", - "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", + "examine": "An elemental key of air.", + "absorb": "0,0,0", "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" + "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 }, { - "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, + "id": "14516", "name": "Earth key", - "destroy": "true", + "examine": "An elemental key of earth.", + "absorb": "0,0,0", "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" + "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 }, { - "destroy_message": "You found this in the Chasm of Tears by using an", - "examine": "An elemental key of fire.", - "durability": null, + "id": "14517", "name": "Fire key", - "destroy": "true", + "examine": "An elemental key of fire.", + "absorb": "0,0,0", "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" - }, - { + "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", - "examine": "An elemental key of water.", - "durability": null, + "durability": null + }, + { + "id": "14518", "name": "Water key", - "destroy": "true", + "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", - "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 found this in the Chasm of Tears by using an", + "durability": null }, { - "shop_price": "20", - "examine": "Grand Exchange set containing a hat, top and bottoms.", - "durability": null, - "destroy": "false", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "", - "grand_exchange_price": "193541", - "name": "Dagon'hai robes set", - "tradeable": "true", - "archery_ticket_price": "0", "id": "14525", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "", - "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", + "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", - "archery_ticket_price": "0", - "attack_speed": "4", - "id": "14526" + "durability": null, + "shop_price": "20", + "tradeable": "true" }, { - "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", + "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", - "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", + "examine": "Grand Exchange set containing an elite black full helm, platebody and legs.", + "absorb": "6,0,13", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14528" + "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" }, { - "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", + "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", - "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", + "examine": "Grand Exchange set containing a dragon full helm, platebody, legs.", "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" - }, - { + "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", - "examine": "Grand Exchange set containing a dragon full helm, platebody, skirt.", - "grand_exchange_price": "12243786", + "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)", - "tradeable": "true", - "destroy": "true", + "examine": "Grand Exchange set containing a dragon full helm, platebody, skirt.", + "absorb": "9,0,19", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14532" + "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" }, { - "examine": "", - "durability": null, - "name": "Broav", + "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", - "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", + "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", - "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", + "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", - "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.", - "examine": "", - "durability": null, - "name": "Cornucopia", - "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 will not be able to get the book back if you destroy it.", + "durability": null + }, + { "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.", - "examine": "", - "durability": null, "name": "Cornucopia", - "destroy": "true", + "absorb": "0,0,0", "archery_ticket_price": "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", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "9", - "examine": "", - "durability": null, - "low_alchemy": "3", - "high_alchemy": "5", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Raw turkey", - "archery_ticket_price": "0", - "id": "14539", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "4", - "examine": "Mmm, this looks tasty.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "2", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Cooked turkey", - "archery_ticket_price": "0", - "id": "14540", - "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" - }, - { - "shop_price": "9", - "examine": "I need to cook this first.", - "durability": null, - "low_alchemy": "3", - "high_alchemy": "5", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Raw turkey drumstick", - "archery_ticket_price": "0", - "id": "14542", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "4", - "examine": "Mmm, this looks tasty.", - "durability": null, - "low_alchemy": "1", - "high_alchemy": "2", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "destroy_message": "Drop", - "name": "Cooked turkey drumstick", - "archery_ticket_price": "0", - "id": "14543", - "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.", - "examine": "", - "durability": null, "name": "Cornucopia", - "destroy": "true", + "absorb": "0,0,0", "archery_ticket_price": "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", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "", - "examine": "Not-so-sturdy boots for northern winters.", - "durability": null, - "name": "Fremennik sea boots 1", - "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 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" }, { - "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", + "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" }, { - "destroy_message": "", - "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", + "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" }, { - "destroy_message": "", - "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" - }, - { - "destroy_message": "", - "examine": "I wonder what happens if I rub it.", - "durability": null, "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "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", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "", - "examine": "I wonder what happens if I rub it.", - "durability": null, "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "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", - "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", + "durability": null, + "tradeable": "false" }, { - "shop_price": "100", - "examine": "A buckler shield from Falador.", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "5", - "destroy_message": "You will be able to get a replacement shield from Redbeard the Pirate in Port Sarim.", - "name": "Falador shield 1", - "archery_ticket_price": "0", "id": "14577", - "bonuses": "0,0,0,-8,-2,17,19,18,-1,18,10,0,3,0,0" + "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" }, { - "shop_price": "200", - "examine": "A kiteshield from Falador.", - "durability": null, - "low_alchemy": "80", - "high_alchemy": "120", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "5", - "destroy_message": "You will be able to get a replacement shield from the chemist in Rimmington.", - "name": "Falador shield 2", - "archery_ticket_price": "0", "id": "14578", - "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,5,0,0" - }, - { - "shop_price": "300", - "examine": "A tower shield from Falador.", - "durability": null, - "low_alchemy": "120", - "high_alchemy": "180", - "destroy": "true", - "attack_speed": "4", + "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", - "destroy_message": "You will be able to get a replacement shield from Sir Vyvin's squire in the White Knights' Castle.", - "name": "Falador shield 3", - "archery_ticket_price": "0", + "shop_price": "200" + }, + { "id": "14579", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,7,0,0" - }, - { - "destroy_message": "", - "shop_price": "1000", - "examine": "I wonder what happens if I rub it.", - "durability": null, - "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "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", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "", - "shop_price": "5000", - "examine": "I wonder what happens if I rub it.", - "durability": null, "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "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", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "", - "shop_price": "10000", - "examine": "I wonder what happens if I rub it.", - "durability": null, "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "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", - "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", + "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", - "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", + "name": "White lily", "examine": "A white lily seed - plant in a flower patch.", - "durability": null, - "name": "White lily seed", - "low_alchemy": "500", - "high_alchemy": "750", + "absorb": "0,0,0", "archery_ticket_price": "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", - "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", + "durability": null, + "shop_price": "1250" }, { - "shop_price": "1000", - "examine": "This will teleport me to Rellekka when I play it. ", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "destroy": "true", - "attack_speed": "4", - "absorb": "0,0,0", - "equipment_slot": "3", - "destroy_message": "Drop", - "name": "Enchanted lyre(5)", - "archery_ticket_price": "0", "id": "14590", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "shop_price": "1000", - "examine": "This will teleport me to Rellekka when I play it. ", - "durability": null, - "low_alchemy": "400", - "high_alchemy": "600", - "destroy": "true", - "attack_speed": "4", + "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", - "destroy_message": "Drop", - "name": "Enchanted lyre(6)", - "archery_ticket_price": "0", + "shop_price": "1000" + }, + { "id": "14591", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + "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" }, { - "examine": "These look pretty heavy, but very elite.", - "durability": null, - "low_alchemy": "26000", - "high_alchemy": "39000", - "destroy": "true", - "attack_speed": "4", - "absorb": "2,0,4", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "41559", - "name": "Elite black platelegs", - "archery_ticket_price": "0", "id": "14592", - "bonuses": "0,0,0,-14,-6,52,50,25,-1,49,15,0,0,0,0" + "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" }, { - "examine": "Provides excellent protection, and is elite.", - "durability": null, - "low_alchemy": "25600", - "high_alchemy": "38400", - "destroy": "true", - "attack_speed": "4", - "absorb": "3,0,6", - "lendable": "true", - "destroy_message": "Drop", - "grand_exchange_price": "38275", - "name": "Elite black platebody", - "archery_ticket_price": "0", "id": "14593", - "bonuses": "0,0,0,-20,-8,80,85,40,-2,80,40,0,0,0,0" - }, - { - "examine": "A full face helmet, and elite.", - "durability": null, - "low_alchemy": "14080", - "high_alchemy": "21120", - "destroy": "true", + "name": "Elite black platebody", + "examine": "Provides excellent protection, and is elite.", + "absorb": "3,0,6", + "archery_ticket_price": "0", "attack_speed": "4", - "absorb": "1,0,3", - "lendable": "true", + "bonuses": "0,0,0,-20,-8,80,85,40,-2,80,40,0,0,0,0", + "destroy": "true", "destroy_message": "Drop", - "grand_exchange_price": "30542", - "name": "Elite black full helm", - "archery_ticket_price": "0", + "durability": null, + "lendable": "true" + }, + { "id": "14594", - "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", + "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" - }, - { - "destroy_message": "", - "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", - "examine": "", - "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" - }, - { - "destroy_message": "", - "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, - "low_alchemy": "40", - "high_alchemy": "60", - "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" - }, - { - "shop_price": "100", - "examine": "A feather from a phoenix. Used in Summoning (72).", - "durability": null, - "low_alchemy": "40", - "high_alchemy": "60", - "destroy": "true", - "attack_speed": "4", - "destroy_message": "Drop", - "grand_exchange_price": "3431", "name": "Phoenix quill", - "tradeable": "true", - "archery_ticket_price": "0", - "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", - "low_alchemy": "80", - "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, - "high_alchemy": "120", - "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" }, { - "destroy_message": "", - "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" - }, - { - "destroy_message": "", - "examine": "I wonder what happens if I rub it.", - "durability": null, "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "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": "14634", - "absorb": "0,0,0", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - }, - { - "destroy_message": "", - "examine": "I wonder what happens if I rub it.", - "durability": null, "name": "Antique lamp", - "tradeable": "false", - "destroy": "true", + "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", - "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", + "durability": null, + "tradeable": "false" }, { - "remove_head": "true", - "shop_price": "650", - "examine": "You don't want to wear it inside-out.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "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", + "name": "Slayer helmet (e)", "examine": "You don't want to wear it inside-out.", - "durability": null, - "low_alchemy": "260", - "high_alchemy": "390", - "destroy": "true", - "attack_speed": "4", "absorb": "0,0,0", - "remove_beard": "true", - "equipment_slot": "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", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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", - "low_alchemy": "7050", - "high_alchemy": "10575", - "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, - "low_alchemy": "0", - "high_alchemy": "1", - "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", - "low_alchemy": "0", - "high_alchemy": "1", - "archery_ticket_price": "0", - "destroy": "true", - "id": "14649", - "equipment_slot": "12" - }, - { - "ge_buy_limit": "0", - "examine": "A standard issue wizard hat.", - "durability": null, - "low_alchemy": "0", - "high_alchemy": "1", - "weight": "0.1", - "destroy": "true", "equipment_slot": "0", - "destroy_message": "", - "grand_exchange_price": "0", - "name": "Wizard hat", - "tradeable": "false", + "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", - "hat": "true", + "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", - "bonuses": "0,0,0,2,0,0,0,0,2,0,0,0,0,0,0" + "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" }, { - "destroy_message": " WARNING: You will have to reobtain this item the hard way.", - "requirements": "", - "examine": "Mysterious blueprints written in an alien language.", - "durability": null, + "id": "14651", "name": "Ancient Blueprints", - "destroy": "true", - "weight": "0.1", + "examine": "Mysterious blueprints written in an alien language.", "archery_ticket_price": "0", - "id": "14651" + "destroy": "true", + "destroy_message": " WARNING: You will have to reobtain this item the hard way.", + "durability": null, + "weight": "0.1" }, { - "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" - }, - { - "destroy": "true", - "turn90cw_anim": "1207", - "examine": "A staff with a spooky raven head attached.", - "walk_anim": "1205", - "low_alchemy": "80", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "tradeable": "false", - "run_anim": "1210", + "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", - "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "shop_price": "200", - "durability": null, - "high_alchemy": "120", - "weight": "2.2", - "weapon_interface": "1", - "render_anim": "28", + "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", - "name": "Staff of the raven" + "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" }, { - "destroy": "true", - "turn90cw_anim": "1207", - "examine": "A staff with a spooky raven head attached.", - "walk_anim": "1205", - "low_alchemy": "80", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "tradeable": "false", - "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", - "durability": null, - "high_alchemy": "120", - "weight": "2.2", - "weapon_interface": "1", - "render_anim": "28", + "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", - "name": "Staff of the raven" + "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" }, { - "destroy": "true", - "turn90cw_anim": "1207", - "examine": "A staff with a spooky raven head attached.", - "walk_anim": "1205", - "low_alchemy": "80", - "turn90ccw_anim": "1208", - "attack_speed": "5", - "turn180_anim": "1206", - "defence_anim": "420", - "equipment_slot": "3", - "attack_anims": "419,419,419,419", - "stand_anim": "813", - "tradeable": "false", - "run_anim": "1210", - "archery_ticket_price": "0", "id": "14656", - "stand_turn_anim": "1209", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "shop_price": "200", - "durability": null, - "high_alchemy": "120", - "weight": "2.2", - "weapon_interface": "1", - "render_anim": "28", + "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", - "name": "Staff of the raven" + "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", + "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 cbfa322d3..000000000 --- a/Server/data/configs/music_configs.json +++ /dev/null @@ -1,2300 +0,0 @@ -[ - { - "id": "1", - "name": "Iban", - "indexId": "61", - "borders": "{2112,4544,2175,4607}" - }, - { - "id": "2", - "name": "Autumn Voyage", - "indexId": "17", - "borders": "{3200,3264,3263,3327}" - }, - { - "id": "3", - "name": "Unknown Land", - "indexId": "133", - "borders": "{3072,3200,3135,3263}" - }, - { - "id": "4", - "name": "Hell's Bells", - "indexId": "254", - "borders": "{2752,3712,2815,3775}" - }, - { - "id": "5", - "name": "Sad Meadow", - "indexId": "101", - "borders": "{2496,3264,2559,3327}-{2752,4672,2815,4735}" - }, - { - "id": "6", - "name": "Jolly-R", - "indexId": "65", - "borders": "{2752,3200,2815,3263}" - }, - { - "id": "7", - "name": "Overture", - "indexId": "92", - "borders": "{2688,3456,2751,3519}" - }, - { - "id": "8", - "name": "Wildwood", - "indexId": "256", - "borders": "{3072,3584,3135,3647}" - }, - { - "id": "9", - "name": "Kingdom", - "indexId": "190", - "borders": "{2816,3520,2879,3583}" - }, - { - "id": "10", - "name": "Moody", - "indexId": "87", - "borders": "{3136,3584,3199,3647}-{2368,3264,2431,3327}" - }, - { - "id": "11", - "name": "Spooky2", - "indexId": "218", - "borders": "{3392,9600,3455,9663}" - }, - { - "id": "12", - "name": "Long Way Home", - "indexId": "76", - "borders": "{2944,3200,3007,3263}" - }, - { - "id": "13", - "name": "Mage Arena", - "indexId": "78", - "borders": "{3072,3904,3135,3967}-{2496,4672,2559,4735}" - }, - { - "id": "14", - "name": "Witching", - "indexId": "145", - "borders": "{3264,3648,3327,3711}" - }, - { - "id": "15", - "name": "Workshop", - "indexId": "148", - "borders": "{3008,3328,3071,3391}" - }, - { - "id": "17", - "name": "Escape", - "indexId": "176", - "borders": "{2688,9664,2751,9727}" - }, - { - "id": "18", - "name": "Horizon", - "indexId": "60", - "borders": "{2880,3392,2943,3455}" - }, - { - "id": "19", - "name": "Arabique", - "indexId": "7", - "borders": "{2816,9792,2879,9855}" - }, - { - "id": "20", - "name": "Lullaby", - "indexId": "77", - "borders": "{3328,3392,3391,3455}-{2624,3520,2687,3583}" - }, - { - "id": "21", - "name": "Monarch Waltz", - "indexId": "86", - "borders": "{2688,3520,2751,3583}" - }, - { - "id": "22", - "name": "Gnome King", - "indexId": "51", - "borders": "{2432,3456,2495,3519}" - }, - { - "id": "24", - "name": "Attack1", - "indexId": "10", - "borders": "{2496,3200,2559,3263}" - }, - { - "id": "25", - "name": "Attack2", - "indexId": "11", - "borders": "{2816,9600,2879,9663}" - }, - { - "id": "26", - "name": "Attack3", - "indexId": "12", - "borders": "{3008,10240,3071,10303}" - }, - { - "id": "27", - "name": "Attack4", - "indexId": "13", - "borders": "{2560,3136,2623,3199}-{2560,9536,2623,9599}" - }, - { - "id": "28", - "name": "Attack5", - "indexId": "14", - "borders": "{2240,4672,2303,4735}" - }, - { - "id": "29", - "name": "Attack6", - "indexId": "15", - "borders": "{2560,9408,2623,9471}" - }, - { - "id": "30", - "name": "Voodoo Cult", - "indexId": "138", - "borders": "{2368,4672,2431,4735}-{2880,9280,2943,9343}" - }, - { - "id": "32", - "name": "Voyage", - "indexId": "139", - "borders": "{2496,3456,2559,3519}" - }, - { - "id": "33", - "name": "Gnome Village", - "indexId": "53", - "borders": "{2432,3392,2495,3455}" - }, - { - "id": "34", - "name": "Wonder", - "indexId": "146", - "borders": "{2944,3520,3007,3583}" - }, - { - "id": "35", - "name": "Sea Shanty2", - "indexId": "107", - "borders": "{3008,3200,3071,3263}" - }, - { - "id": "36", - "name": "Arabian", - "indexId": "4", - "borders": "{3264,3200,3327,3263}-{3392,3136,3455,3199}" - }, - { - "id": "37", - "name": "Deep Wildy", - "indexId": "32", - "borders": "{2944,3776,3007,3839}" - }, - { - "id": "38", - "name": "Trawler", - "indexId": "124", - "borders": "{1856,4800,1919,4863}" - }, - { - "id": "41", - "name": "Expecting", - "indexId": "41", - "borders": "{2432,3200,2495,3263}-{2432,9600,2495,9663}" - }, - { - "id": "42", - "name": "Wilderness2", - "indexId": "143", - "borders": "{3008,3776,3071,3839}" - }, - { - "id": "43", - "name": "Wilderness3", - "indexId": "144", - "borders": "{2944,3712,3007,3775}" - }, - { - "id": "45", - "name": "Venture2", - "indexId": "168", - "borders": "{3328,9728,3391,9791}" - }, - { - "id": "46", - "name": "Harmony2", - "indexId": "167", - "borders": "{3200,9600,3263,9663}" - }, - { - "id": "47", - "name": "Duel Arena", - "indexId": "164", - "borders": "{3328,3200,3391,3263}" - }, - { - "id": "48", - "name": "Morytania", - "indexId": "180", - "borders": "{3392,3456,3455,3519}" - }, - { - "id": "49", - "name": "Wander", - "indexId": "140", - "borders": "{3008,3264,3071,3327}" - }, - { - "id": "50", - "name": "Al Kharid", - "indexId": "1", - "borders": "{3264,3136,3327,3199}-{3328,3136,3391,3199}" - }, - { - "id": "51", - "name": "Trawler Minor", - "indexId": "125", - "borders": "{1920,4800,1983,4863}" - }, - { - "id": "52", - "name": "Serene", - "indexId": "109", - "borders": "{2944,3904,3007,3967}-{2944,10240,3007,10303}-{2816,4800,2879,4863}" - }, - { - "id": "53", - "name": "Royale", - "indexId": "99", - "borders": "{2880,9664,2943,9727}" - }, - { - "id": "54", - "name": "Scape Soft", - "indexId": "159", - "borders": "{2944,3392,3007,3455}" - }, - { - "id": "55", - "name": "High Seas", - "indexId": "59", - "borders": "{2752,3136,2815,3199}" - }, - { - "id": "56", - "name": "Doorways", - "indexId": "34", - "borders": "{3136,3520,3199,3583}" - }, - { - "id": "57", - "name": "Rune Essence", - "indexId": "100", - "borders": "{2880,4800,2943,4863}" - }, - { - "id": "58", - "name": "Nomad", - "indexId": "171", - "borders": "{2752,3072,2815,3135}" - }, - { - "id": "59", - "name": "Cursed", - "indexId": "186", - "borders": "{2368,9664,2431,9727}" - }, - { - "id": "60", - "name": "Lasting", - "indexId": "71", - "borders": "{2624,3392,2687,3455}" - }, - { - "id": "61", - "name": "Village", - "indexId": "182", - "borders": "{3456,3456,3519,3519}" - }, - { - "id": "62", - "name": "Newbie Melody", - "indexId": "89", - "borders": "{3052,3055,3155,3135}" - }, - { - "id": "63", - "name": "Chain of Command", - "indexId": "27", - "borders": "{2624,9728,2687,9791}-{2688,9792,2751,9855}" - }, - { - "id": "64", - "name": "Book of Spells", - "indexId": "23", - "borders": "{3136,3136,3199,3199}" - }, - { - "id": "65", - "name": "Miracle Dance", - "indexId": "85", - "borders": "{2752,4800,2815,4863}" - }, - { - "id": "66", - "name": "Legion", - "indexId": "72", - "borders": "{3008,3648,3071,3711}-{2496,3520,2559,3583}" - }, - { - "id": "67", - "name": "Close Quarters", - "indexId": "175", - "borders": "{3136,3712,3199,3775}-{1536,5760,1599,5823}" - }, - { - "id": "68", - "name": "Cavern", - "indexId": "26", - "borders": "{3008,10304,3071,10367}-{2560,9472,2623,9535}" - }, - { - "id": "69", - "name": "Egypt", - "indexId": "37", - "borders": "{3264,3072,3327,3135}" - }, - { - "id": "70", - "name": "Upcoming", - "indexId": "135", - "borders": "{2624,3200,2687,3263}" - }, - { - "id": "71", - "name": "Chompy Hunt", - "indexId": "178", - "borders": "{2624,2944,2687,3007}-{2624,9344,2687,9407}" - }, - { - "id": "72", - "name": "Fanfare", - "indexId": "44", - "borders": "{2944,3328,3007,3391}" - }, - { - "id": "73", - "name": "All's Fairy in Love and War", - "indexId": "436", - "borders": "" - }, - { - "id": "74", - "name": "Lightwalk", - "indexId": "74", - "borders": "{2752,3392,2815,3455}" - }, - { - "id": "75", - "name": "Venture", - "indexId": "136", - "borders": "{3328,3328,3391,3391}" - }, - { - "id": "76", - "name": "Harmony", - "indexId": "58", - "borders": "{3200,3200,3263,3263}" - }, - { - "id": "77", - "name": "Splendour", - "indexId": "113", - "borders": "{2880,3456,2943,3519}" - }, - { - "id": "78", - "name": "Reggae", - "indexId": "96", - "borders": "{2880,2880,2943,2943}" - }, - { - "id": "79", - "name": "The Desert", - "indexId": "120", - "borders": "{3136,3008,3199,3071}" - }, - { - "id": "80", - "name": "Soundscape", - "indexId": "111", - "borders": "{2432,2944,2495,3007}" - }, - { - "id": "81", - "name": "Wonderous", - "indexId": "147", - "borders": "{2624,3328,2687,3391}" - }, - { - "id": "82", - "name": "Waterfall", - "indexId": "141", - "borders": "{2496,3392,2559,3455}-{2496,9792,2559,9855}" - }, - { - "id": "83", - "name": "Big Chords", - "indexId": "22", - "borders": "{2496,3072,2559,3135}" - }, - { - "id": "84", - "name": "Dead Quiet", - "indexId": "181", - "borders": "{3392,3392,3455,3455}-{2304,4992,2367,5055}" - }, - { - "id": "85", - "name": "Vision", - "indexId": "137", - "borders": "{3072,3136,3135,3199}-{3072,9472,3135,9535}" - }, - { - "id": "87", - "name": "Ice Melody", - "indexId": "165", - "borders": "{2816,3456,2879,3519}" - }, - { - "id": "88", - "name": "Twilight", - "indexId": "179", - "borders": "{2688,9856,2751,9919}" - }, - { - "id": "89", - "name": "Reggae2", - "indexId": "97", - "borders": "{2880,3008,2943,3071}" - }, - { - "id": "90", - "name": "Ambient Jungle", - "indexId": "3", - "borders": "{2816,2944,2879,3007}" - }, - { - "id": "91", - "name": "Riverside", - "indexId": "98", - "borders": "{2688,3264,2751,3327}-{2112,3072,2175,3135}" - }, - { - "id": "92", - "name": "Sea Shanty", - "indexId": "106", - "borders": "{2880,3136,2943,3199}" - }, - { - "id": "93", - "name": "Parade", - "indexId": "93", - "borders": "{3264,3456,3327,3519}" - }, - { - "id": "94", - "name": "Tribal2", - "indexId": "129", - "borders": "{2880,2944,2943,3007}" - }, - { - "id": "95", - "name": "Intrepid", - "indexId": "64", - "borders": "{2304,9792,2367,9855}" - }, - { - "id": "96", - "name": "Inspiration", - "indexId": "63", - "borders": "{3008,3520,3071,3583}" - }, - { - "id": "97", - "name": "Hermit", - "indexId": "191", - "borders": "{2240,4736,2303,4799}" - }, - { - "id": "98", - "name": "Forever", - "indexId": "48", - "borders": "{3072,3456,3135,3519}-{3072,9856,3135,9919}" - }, - { - "id": "99", - "name": "Baroque", - "indexId": "20", - "borders": "{2624,3264,2687,3327}" - }, - { - "id": "100", - "name": "Beyond", - "indexId": "21", - "borders": "{2816,9856,2879,9919}-{2816,9920,2879,9983}" - }, - { - "id": "101", - "name": "Gnome Village2", - "indexId": "54", - "borders": "{2304,3392,2367,3455}" - }, - { - "id": "102", - "name": "Alone", - "indexId": "2", - "borders": "{3008,3456,3071,3519}-{2496,9600,2559,9663}" - }, - { - "id": "103", - "name": "Oriental", - "indexId": "91", - "borders": "{2880,9344,2943,9407}" - }, - { - "id": "104", - "name": "Camelot", - "indexId": "24", - "borders": "{2752,3456,2815,3519}" - }, - { - "id": "105", - "name": "Tomorrow", - "indexId": "163", - "borders": "{3008,3136,3071,3199}" - }, - { - "id": "106", - "name": "Expanse", - "indexId": "40", - "borders": "{3136,3904,3199,3967}-{3200,3328,3263,3391}-{3200,9728,3263,9791}" - }, - { - "id": "107", - "name": "Miles Away", - "indexId": "84", - "borders": "{2880,3264,2943,3327}-{2624,4672,2687,4735}" - }, - { - "id": "108", - "name": "Starlight", - "indexId": "116", - "borders": "{2944,9536,3007,9599}-{3200,9536,3263,9599}" - }, - { - "id": "109", - "name": "Theme", - "indexId": "123", - "borders": "{2560,3456,2623,3519}-{2496,9856,2559,9919}" - }, - { - "id": "110", - "name": "Serenade", - "indexId": "108", - "borders": "{2368,3136,2431,3199}" - }, - { - "id": "111", - "name": "Still Night", - "indexId": "118", - "borders": "{3264,3328,3327,3391}" - }, - { - "id": "112", - "name": "Gnomeball", - "indexId": "56", - "borders": "{2304,3456,2367,3519}" - }, - { - "id": "113", - "name": "Lightness", - "indexId": "73", - "borders": "{3072,3520,3135,3583}" - }, - { - "id": "114", - "name": "Jungly1", - "indexId": "67", - "borders": "{2752,2944,2815,3007}-{2752,9344,2815,9407}" - }, - { - "id": "115", - "name": "Jungly2", - "indexId": "68", - "borders": "{2688,3200,2751,3263}" - }, - { - "id": "116", - "name": "Greatness", - "indexId": "57", - "borders": "{3136,3328,3199,3391}" - }, - { - "id": "117", - "name": "Jungly3", - "indexId": "69", - "borders": "{2752,3008,2815,3071}" - }, - { - "id": "118", - "name": "Faerie", - "indexId": "43", - "borders": "{2368,4352,2431,4415}" - }, - { - "id": "119", - "name": "Fishing", - "indexId": "46", - "borders": "{2816,3392,2879,3455}" - }, - { - "id": "120", - "name": "Shining", - "indexId": "160", - "borders": "{3200,3712,3263,3775}" - }, - { - "id": "121", - "name": "Forbidden", - "indexId": "185", - "borders": "{3264,3520,3327,3583}" - }, - { - "id": "122", - "name": "Shine", - "indexId": "110", - "borders": "{3328,3264,3391,3327}" - }, - { - "id": "123", - "name": "Arabian2", - "indexId": "5", - "borders": "{3264,3264,3327,3327}" - }, - { - "id": "124", - "name": "Arabian3", - "indexId": "6", - "borders": "{3200,3072,3263,3135}" - }, - { - "id": "125", - "name": "Garden", - "indexId": "50", - "borders": "{3200,3392,3263,3455}" - }, - { - "id": "127", - "name": "Nightfall", - "indexId": "90", - "borders": "{3200,3904,3263,3967}-{2944,3264,3007,3327}" - }, - { - "id": "128", - "name": "Grumpy", - "indexId": "177", - "borders": "{2560,2944,2623,3007}" - }, - { - "id": "129", - "name": "Spooky Jungle", - "indexId": "115", - "borders": "{2752,2880,2815,2943}-{2880,9472,2943,9535}" - }, - { - "id": "130", - "name": "Tree Spirits", - "indexId": "126", - "borders": "{2304,3328,2367,3391}" - }, - { - "id": "131", - "name": "Understanding", - "indexId": "187", - "borders": "{2368,4800,2431,4863}" - }, - { - "id": "132", - "name": "Breeze", - "indexId": "194", - "borders": "{2240,3200,2303,3263}" - }, - { - "id": "133", - "name": "The Tower", - "indexId": "122", - "borders": "{2560,3328,2623,3391}-{2496,9728,2559,9791}" - }, - { - "id": "134", - "name": "La Mort", - "indexId": "192", - "borders": "{2176,4800,2239,4863}" - }, - { - "id": "138", - "name": "Emperor", - "indexId": "39", - "borders": "{2880,3200,2943,3263}-{2880,9600,2943,9663}" - }, - { - "id": "140", - "name": "Talking Forest", - "indexId": "119", - "borders": "{2624,3456,2687,3519}" - }, - { - "id": "141", - "name": "Barbarianism", - "indexId": "257", - "borders": "{3072,3392,3135,3455}-{3072,9792,3135,9855}" - }, - { - "id": "142", - "name": "Complication", - "indexId": "258", - "borders": "{2240,4800,2303,4863}" - }, - { - "id": "143", - "name": "Down to Earth", - "indexId": "259", - "borders": "{2624,4800,2687,4863}" - }, - { - "id": "144", - "name": "Scape Cave", - "indexId": "102", - "borders": "{3136,9856,3199,9919}-{3072,9536,3135,9599}" - }, - { - "id": "145", - "name": "Yesteryear", - "indexId": "161", - "borders": "{3200,3136,3263,3199}" - }, - { - "id": "146", - "name": "Zealot", - "indexId": "172", - "borders": "{2688,4800,2751,4863}" - }, - { - "id": "148", - "name": "Emotion", - "indexId": "38", - "borders": "{2496,3136,2559,3199}-{2560,4416,2623,4479}-{2496,9536,2559,9599}" - }, - { - "id": "149", - "name": "Principality", - "indexId": "188", - "borders": "{2880,3520,2943,3583}" - }, - { - "id": "151", - "name": "Start", - "indexId": "117", - "borders": "{3072,3264,3135,3327}" - }, - { - "id": "152", - "name": "Ballad of Enchantment", - "indexId": "19", - "borders": "{2560,3200,2623,3263}" - }, - { - "id": "153", - "name": "Expedition", - "indexId": "42", - "borders": "{2880,9984,2943,10047}" - }, - { - "id": "154", - "name": "Bone Dance", - "indexId": "183", - "borders": "{3392,3264,3455,3327}" - }, - { - "id": "155", - "name": "Neverland", - "indexId": "88", - "borders": "{2432,3328,2495,3391}" - }, - { - "id": "156", - "name": "Mausoleum", - "indexId": "184", - "borders": "{3392,9856,3455,9919}" - }, - { - "id": "157", - "name": "Medieval", - "indexId": "82", - "borders": "{3264,3392,3327,3455}" - }, - { - "id": "158", - "name": "Quest", - "indexId": "94", - "borders": "{2560,4800,2623,4863}" - }, - { - "id": "159", - "name": "Gaol", - "indexId": "49", - "borders": "{3008,3712,3071,3775}-{2496,3008,2559,3071}-{2496,9408,2559,9471}" - }, - { - "id": "160", - "name": "Army of Darkness", - "indexId": "8", - "borders": "{3008,3584,3071,3647}" - }, - { - "id": "161", - "name": "Long Ago", - "indexId": "75", - "borders": "{2624,3072,2687,3135}" - }, - { - "id": "162", - "name": "Tribal Background", - "indexId": "127", - "borders": "{2816,3072,2879,3135}-{2816,9472,2879,9535}" - }, - { - "id": "163", - "name": "Flute Salad", - "indexId": "47", - "borders": "{3136,3264,3199,3327}" - }, - { - "id": "164", - "name": "Landlubber", - "indexId": "169", - "borders": "{2688,3136,2751,3199}" - }, - { - "id": "165", - "name": "Tribal", - "indexId": "128", - "borders": "{2816,3008,2879,3071}" - }, - { - "id": "166", - "name": "Fanfare2", - "indexId": "162", - "borders": "{2944,3008,3007,3071}" - }, - { - "id": "167", - "name": "Fanfare3", - "indexId": "45", - "borders": "{2624,3136,2687,3199}" - }, - { - "id": "168", - "name": "Lonesome", - "indexId": "149", - "borders": "{3264,9408,3327,9471}" - }, - { - "id": "169", - "name": "Crystal Sword", - "indexId": "29", - "borders": "{3200,3520,3263,3583}-{2624,9664,2687,9727}" - }, - { - "id": "170", - "name": "The Shadow", - "indexId": "121", - "borders": "{2816,3200,2879,3263}" - }, - { - "id": "172", - "name": "Jungle Island", - "indexId": "66", - "borders": "{2816,3136,2879,3199}-{2816,2880,2879,2943}" - }, - { - "id": "173", - "name": "Dunjun", - "indexId": "36", - "borders": "{2880,9728,2943,9791}" - }, - { - "id": "174", - "name": "Desert Voyage", - "indexId": "33", - "borders": "{3264,2944,3327,3007}-{3328,3008,3391,3071}" - }, - { - "id": "175", - "name": "Spirit", - "indexId": "112", - "borders": "{3136,3392,3199,3455}" - }, - { - "id": "176", - "name": "Undercurrent", - "indexId": "170", - "borders": "{3072,3648,3135,3711}" - }, - { - "id": "177", - "name": "Adventure", - "indexId": "0", - "borders": "{3200,3456,3263,3519}" - }, - { - "id": "178", - "name": "Courage", - "indexId": "260", - "borders": "{2880,9792,2943,9855}" - }, - { - "id": "179", - "name": "Underground", - "indexId": "132", - "borders": "{3328,3584,3391,3647}-{2816,9728,2879,9791}" - }, - { - "id": "180", - "name": "Attention", - "indexId": "16", - "borders": "{2944,3136,3007,3199}" - }, - { - "id": "181", - "name": "Crystal Cave", - "indexId": "28", - "borders": "{2432,4416,2495,4479}" - }, - { - "id": "182", - "name": "Dangerous", - "indexId": "30", - "borders": "{3264,3776,3327,3839}-{3264,3712,3327,3775}" - }, - { - "id": "183", - "name": "Troubled", - "indexId": "131", - "borders": "{2944,3648,3007,3711}" - }, - { - "id": "184", - "name": "Magical Journey", - "indexId": "80", - "borders": "{2688,3392,2751,3455}" - }, - { - "id": "185", - "name": "Magic Dance", - "indexId": "79", - "borders": "{2560,3072,2623,3135}" - }, - { - "id": "186", - "name": "Arrival", - "indexId": "9", - "borders": "{2880,3328,2943,3391}" - }, - { - "id": "187", - "name": "Tremble", - "indexId": "189", - "borders": "{2816,3584,2879,3647}" - }, - { - "id": "188", - "name": "In the Manor", - "indexId": "62", - "borders": "{2560,3008,2623,3071}" - }, - { - "id": "190", - "name": "Heart and Mind", - "indexId": "174", - "borders": "{2496,4800,2559,4863}" - }, - { - "id": "191", - "name": "Knightly", - "indexId": "70", - "borders": "{2560,3264,2623,3327}" - }, - { - "id": "192", - "name": "Trinity", - "indexId": "130", - "borders": "{2688,3328,2751,3391}-{2688,9728,2751,9791}" - }, - { - "id": "193", - "name": "Mellow", - "indexId": "83", - "borders": "{2560,3392,2623,3455}" - }, - { - "id": "198", - "name": "Dagannoth Dawn", - "indexId": "365", - "borders": "{1792,4352,1855,4415}-{1920,4352,1983,4415}" - }, - { - "id": "220", - "name": "Island of the Trolls", - "indexId": "441", - "borders": "{2304,3842,2428,3901}" - }, - { - "id": "241", - "name": "Stagnant", - "indexId": "193", - "borders": "{3456,3328,3519,3391}-{2176,4992,2239,5055}" - }, - { - "id": "242", - "name": "Time Out", - "indexId": "196", - "borders": "{2880,4544,2943,4607}" - }, - { - "id": "243", - "name": "Stratosphere", - "indexId": "195", - "borders": "{2112,4800,2175,4863}" - }, - { - "id": "244", - "name": "Waterlogged", - "indexId": "199", - "borders": "{3456,3392,3519,3455}-{1984,4992,2047,5055}" - }, - { - "id": "245", - "name": "Natural", - "indexId": "197", - "borders": "{3392,3328,3455,3391}-{2240,4992,2303,5055}" - }, - { - "id": "246", - "name": "Grotto", - "indexId": "198", - "borders": "{3392,9728,3455,9791}" - }, - { - "id": "247", - "name": "Artistry", - "indexId": "200", - "borders": "{1984,4736,2047,4799}" - }, - { - "id": "248", - "name": "Aztec", - "indexId": "201", - "borders": "{2752,9536,2815,9599}" - }, - { - "id": "251", - "name": "Forest", - "indexId": "203", - "borders": "{2240,3136,2303,3199}" - }, - { - "id": "252", - "name": "Elven Mist", - "indexId": "202", - "borders": "{2304,3200,2367,3263}" - }, - { - "id": "253", - "name": "Lost Soul", - "indexId": "204", - "borders": "{2240,3072,2303,3135}" - }, - { - "id": "254", - "name": "Meridian", - "indexId": "205", - "borders": "{2112,3136,2175,3199}" - }, - { - "id": "255", - "name": "Woodland", - "indexId": "206", - "borders": "{2112,3200,2175,3263}" - }, - { - "id": "256", - "name": "Overpass", - "indexId": "207", - "borders": "{2304,3264,2367,3327}" - }, - { - "id": "257", - "name": "Sojourn", - "indexId": "209", - "borders": "{2816,3648,2879,3711}" - }, - { - "id": "258", - "name": "Contest", - "indexId": "208", - "borders": "{2880,3584,2943,3647}" - }, - { - "id": "259", - "name": "Crystal Castle", - "indexId": "210", - "borders": "{2240,3264,2303,3327}" - }, - { - "id": "260", - "name": "Insect Queen", - "indexId": "212", - "borders": "{3456,9472,3519,9535}" - }, - { - "id": "261", - "name": "Marzipan", - "indexId": "211", - "borders": "{2752,10112,2815,10175}-{2816,10048,2879,10111}" - }, - { - "id": "262", - "name": "Righteousness", - "indexId": "223", - "borders": "{2432,4800,2495,4863}" - }, - { - "id": "263", - "name": "Bandit Camp", - "indexId": "214", - "borders": "{3136,2944,3199,3007}" - }, - { - "id": "264", - "name": "Mad Eadgar", - "indexId": "213", - "borders": "{2880,10048,2943,10111}" - }, - { - "id": "265", - "name": "Superstition", - "indexId": "261", - "borders": "{2752,9280,2815,9343}" - }, - { - "id": "266", - "name": "Bone Dry", - "indexId": "216", - "borders": "{3200,9344,3263,9407}" - }, - { - "id": "267", - "name": "Sunburn", - "indexId": "215", - "borders": "{3200,2944,3263,3007}-{3328,2880,3391,2943}" - }, - { - "id": "268", - "name": "Everywhere", - "indexId": "219", - "borders": "{2112,3264,2175,3327}" - }, - { - "id": "269", - "name": "Competition", - "indexId": "217", - "borders": "{2176,4928,2239,4991}" - }, - { - "id": "270", - "name": "Exposed", - "indexId": "220", - "borders": "{2176,3072,2239,3135}" - }, - { - "id": "271", - "name": "Well of Voyage", - "indexId": "469", - "borders": "{2304,9600,2367,9663}" - }, - { - "id": "272", - "name": "Alternative Root", - "indexId": "221", - "borders": "" - }, - { - "id": "273", - "name": "Easter Jig", - "indexId": "475", - "borders": "" - }, - { - "id": "277", - "name": "Haunted Mine", - "indexId": "222", - "borders": "{2752,4416,2815,4479}" - }, - { - "id": "278", - "name": "Deep Down", - "indexId": "224", - "borders": "{2688,4544,2751,4607}-{2688,4480,2751,4543}" - }, - { - "id": "282", - "name": "Chamber", - "indexId": "225", - "borders": "{2688,4416,2751,4479}-{2752,4480,2815,4543}" - }, - { - "id": "284", - "name": "Miscellania", - "indexId": "226", - "borders": "{2496,3840,2559,3903}" - }, - { - "id": "285", - "name": "Etcetera", - "indexId": "227", - "borders": "{2560,3840,2623,3903}" - }, - { - "id": "286", - "name": "Shadowland", - "indexId": "228", - "borders": "{3392,3200,3455,3263}-{3456,3264,3519,3327}-{2112,4992,2175,5055}" - }, - { - "id": "287", - "name": "Lair", - "indexId": "229", - "borders": "{3456,9664,3519,9727}" - }, - { - "id": "288", - "name": "Deadlands", - "indexId": "230", - "borders": "{3520,3456,3583,3519}" - }, - { - "id": "289", - "name": "Rellekka", - "indexId": "231", - "borders": "{2560,3648,2623,3711}" - }, - { - "id": "290", - "name": "Saga", - "indexId": "232", - "borders": "{2560,3584,2623,3647}" - }, - { - "id": "291", - "name": "Borderland", - "indexId": "233", - "borders": "{2688,3648,2751,3711}" - }, - { - "id": "292", - "name": "Stranded", - "indexId": "234", - "borders": "{2816,3712,2879,3775}-{2880,3712,2943,3775}" - }, - { - "id": "293", - "name": "Legend", - "indexId": "235", - "borders": "{2688,3584,2751,3647}" - }, - { - "id": "294", - "name": "Frostbite", - "indexId": "236", - "borders": "{2816,3776,2879,3839}" - }, - { - "id": "295", - "name": "Warrior", - "indexId": "237", - "borders": "{2624,10048,2687,10111}" - }, - { - "id": "296", - "name": "Technology", - "indexId": "238", - "borders": "{2560,4480,2623,4543}" - }, - { - "id": "303", - "name": "Monkey Madness", - "indexId": "239", - "borders": "{2752,2752,2815,2815}" - }, - { - "id": "304", - "name": "Marooned", - "indexId": "241", - "borders": "{2880,2688,2943,2751}-{3008,5440,3071,5503}" - }, - { - "id": "305", - "name": "Anywhere", - "indexId": "240", - "borders": "{2688,2752,2751,2815}" - }, - { - "id": "306", - "name": "Island Life", - "indexId": "242", - "borders": "{2688,2688,2751,2751}" - }, - { - "id": "307", - "name": "Temple", - "indexId": "243", - "borders": "{2752,9152,2815,9215}" - }, - { - "id": "311", - "name": "Showdown", - "indexId": "245", - "borders": "{2688,9152,2751,9215}" - }, - { - "id": "312", - "name": "Find My Way", - "indexId": "246", - "borders": "{2688,9088,2751,9151}" - }, - { - "id": "314", - "name": "Castle Wars", - "indexId": "247", - "borders": "{2368,3072,2431,3135}" - }, - { - "id": "316", - "name": "The Navigator", - "indexId": "255", - "borders": "{2624,9984,2687,10047}" - }, - { - "id": "317", - "name": "Melodrama", - "indexId": "248", - "borders": "{2432,3072,2495,3135}" - }, - { - "id": "318", - "name": "Ready for Battle", - "indexId": "249", - "borders": "{2368,9472,2431,9535}" - }, - { - "id": "319", - "name": "Stillness", - "indexId": "250", - "borders": "{3456,9792,3519,9855}" - }, - { - "id": "320", - "name": "Lighthouse", - "indexId": "251", - "borders": "{2496,3584,2559,3647}" - }, - { - "id": "322", - "name": "Out of the Deep", - "indexId": "253", - "borders": "{2496,9984,2559,10047}" - }, - { - "id": "323", - "name": "Underground Pass", - "indexId": "134", - "borders": "{2368,9536,2431,9599}" - }, - { - "id": "324", - "name": "Background", - "indexId": "18", - "borders": "{2752,3328,2815,3391}-{1920,4992,1983,5055}" - }, - { - "id": "325", - "name": "Cave Background", - "indexId": "25", - "borders": "{3008,9728,3071,9791}-{2944,9792,3007,9855}" - }, - { - "id": "326", - "name": "Dark", - "indexId": "31", - "borders": "{3328,3648,3391,3711}" - }, - { - "id": "327", - "name": "Dream", - "indexId": "35", - "borders": "{3136,3200,3199,3263}" - }, - { - "id": "328", - "name": "March", - "indexId": "81", - "borders": "{2496,3328,2559,3391}" - }, - { - "id": "329", - "name": "Regal", - "indexId": "95", - "borders": "{3264,3904,3327,3967}" - }, - { - "id": "330", - "name": "Cellar Song", - "indexId": "173", - "borders": "{3136,9792,3199,9855}" - }, - { - "id": "331", - "name": "Scape Sad", - "indexId": "104", - "borders": "{3264,3840,3327,3903}" - }, - { - "id": "332", - "name": "Scape Wild", - "indexId": "105", - "borders": "{3200,3648,3263,3711}-{3136,3840,3199,3903}" - }, - { - "id": "333", - "name": "Spooky", - "indexId": "114", - "borders": "{3072,3328,3135,3391}" - }, - { - "id": "334", - "name": "Pirates of Peril", - "indexId": "262", - "borders": "{3008,3904,3071,3967}" - }, - { - "id": "335", - "name": "Romancing the Crone", - "indexId": "264", - "borders": "{2752,3840,2815,3903}" - }, - { - "id": "336", - "name": "Dangerous Road", - "indexId": "263", - "borders": "{2816,9536,2879,9599}" - }, - { - "id": "337", - "name": "Faithless", - "indexId": "265", - "borders": "{3200,3584,3263,3647}" - }, - { - "id": "338", - "name": "Tiptoe", - "indexId": "266", - "borders": "{3072,9728,3135,9791}" - }, - { - "id": "339", - "name": "The Terrible Tower", - "indexId": "267", - "borders": "{3392,3520,3455,3583}" - }, - { - "id": "340", - "name": "Masquerade", - "indexId": "268", - "borders": "{2688,9984,2751,10047}" - }, - { - "id": "341", - "name": "The Slayer", - "indexId": "269", - "borders": "{2752,9984,2815,10047}" - }, - { - "id": "342", - "name": "Body Parts", - "indexId": "270", - "borders": "{3456,9920,3519,9983}" - }, - { - "id": "343", - "name": "Monster Melee", - "indexId": "272", - "borders": "{3136,9600,3199,9663}" - }, - { - "id": "344", - "name": "Fenkenstrain's Refrain", - "indexId": "271", - "borders": "{3456,3520,3519,3583}" - }, - { - "id": "345", - "name": "Barking Mad", - "indexId": "274", - "borders": "{3520,9856,3583,9919}" - }, - { - "id": "346", - "name": "Goblin Game", - "indexId": "252", - "borders": "{2560,9792,2623,9855}" - }, - { - "id": "347", - "name": "Fruits de Mer", - "indexId": "273", - "borders": "{2752,3264,2815,3327}" - }, - { - "id": "348", - "name": "Narnode's Theme", - "indexId": "513", - "borders": "{2432,9856,2495,9919}" - }, - { - "id": "351", - "name": "Dynasty", - "indexId": "275", - "borders": "{3328,2944,3391,3007}" - }, - { - "id": "352", - "name": "Scarab", - "indexId": "282", - "borders": "{3136,2880,3199,2943}" - }, - { - "id": "353", - "name": "Shipwrecked", - "indexId": "276", - "borders": "{3584,3520,3647,3583}" - }, - { - "id": "354", - "name": "Phasmatys", - "indexId": "277", - "borders": "{3648,9856,3711,9919}" - }, - { - "id": "355", - "name": "The Other Side", - "indexId": "278", - "borders": "{3648,3456,3711,3519}" - }, - { - "id": "356", - "name": "Settlement", - "indexId": "279", - "borders": "{2752,3648,2815,3711}" - }, - { - "id": "357", - "name": "Cave of Beasts", - "indexId": "280", - "borders": "{2752,10048,2815,10111}" - }, - { - "id": "358", - "name": "Dragontooth Island", - "indexId": "281", - "borders": "{3776,3520,3839,3583}" - }, - { - "id": "359", - "name": "Sarcophagus", - "indexId": "283", - "borders": "{3200,9280,3263,9343}" - }, - { - "id": "361", - "name": "Down Below", - "indexId": "284", - "borders": "{3072,9600,3135,9663}" - }, - { - "id": "362", - "name": "Karamja Jam", - "indexId": "286", - "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", - "name": "Pathways", - "indexId": "287", - "borders": "{2688,9536,2751,9599}" - }, - { - "id": "369", - "name": "Time to Mine", - "indexId": "289", - "borders": "{2816,10112,2879,10175}" - }, - { - "id": "370", - "name": "In Between", - "indexId": "290", - "borders": "{2496,4928,2559,4991}" - }, - { - "id": "372", - "name": "Far Away", - "indexId": "292", - "borders": "{2304,3136,2367,3199}" - }, - { - "id": "373", - "name": "Claustrophobia", - "indexId": "291", - "borders": "{2304,4928,2367,4991}" - }, - { - "id": "375", - "name": "Fight or Flight", - "indexId": "293", - "borders": "{1920,4608,1983,4671}" - }, - { - "id": "376", - "name": "Temple of Light", - "indexId": "294", - "borders": "{1856,4608,1919,4671}" - }, - { - "id": "377", - "name": "The Golem", - "indexId": "295", - "borders": "{3392,3072,3455,3135}-{3456,3072,3519,3135}" - }, - { - "id": "380", - "name": "Dance of the Undead", - "indexId": "298", - "borders": "{3520,3264,3583,3327}" - }, - { - "id": "381", - "name": "Dangerous Way", - "indexId": "299", - "borders": "{3520,9664,3583,9727}" - }, - { - "id": "383", - "name": "City of the Dead", - "indexId": "300", - "borders": "{3200,2752,3263,2815}-{3264,2752,3327,2815}" - }, - { - "id": "386", - "name": "Bandos Battalion", - "indexId": "492", - "borders": "{2820,5312,2849,5369}-{2849,5349,2880,5374}" - }, - { - "id": "387", - "name": "Sphinx", - "indexId": "302", - "borders": "{3264,2816,3327,2879}" - }, - { - "id": "388", - "name": "Mirage", - "indexId": "303", - "borders": "{3264,9152,3327,9215}" - }, - { - "id": "389", - "name": "Cave of the Goblins", - "indexId": "304", - "borders": "{3136,9536,3199,9599}" - }, - { - "id": "390", - "name": "Romper Chomper", - "indexId": "312", - "borders": "{2304,3008,2367,3071}" - }, - { - "id": "391", - "name": "Zamorak Zoo", - "indexId": "490", - "borders": "{2879,5340,2944,5366}-{2909,5316,2944,5340}" - }, - { - "id": "392", - "name": "Zogre Dance", - "indexId": "306", - "borders": "{2432,3008,2495,3071}" - }, - { - "id": "393", - "name": "Path of Peril", - "indexId": "307", - "borders": "{2624,5056,2687,5119}" - }, - { - "id": "394", - "name": "Wayward", - "indexId": "308", - "borders": "{2432,9408,2495,9471}" - }, - { - "id": "395", - "name": "Tale of Keldagrim", - "indexId": "309", - "borders": "{2880,10112,2943,10175}" - }, - { - "id": "396", - "name": "Land of the Dwarves", - "indexId": "310", - "borders": "{2816,10176,2879,10239}" - }, - { - "id": "397", - "name": "Tears of Guthix", - "indexId": "311", - "borders": "{3200,9472,3263,9535}" - }, - { - "id": "399", - "name": "Armageddon", - "indexId": "494", - "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", - "name": "The Adventurer", - "indexId": "520", - "borders": "" - }, - { - "id": "402", - "name": "The Rogues' Den", - "indexId": "313", - "borders": "{2944,4928,3007,4991}-{3008,4928,3071,4991}" - }, - { - "id": "403", - "name": "The Far Side", - "indexId": "314", - "borders": "{3008,5056,3071,5119}" - }, - { - "id": "404", - "name": "Armadyl Alliance", - "indexId": "493", - "borders": "{2823,5250,2844,5310}-{2844,5250,2878,5280}" - }, - { - "id": "407", - "name": "The Lost Melody", - "indexId": "315", - "borders": "{3264,9600,3327,9663}" - }, - { - "id": "408", - "name": "Strength of Saradomin", - "indexId": "491", - "borders": "{2885,5253,2934,5278}-{2913,5278,2937,5306}" - }, - { - "id": "409", - "name": "Frogland", - "indexId": "336", - "borders": "{2432,4736,2495,4799}" - }, - { - "id": "411", - "name": "Evil Bob's Island", - "indexId": "316", - "borders": "{2496,4736,2559,4799}" - }, - { - "id": "412", - "name": "Into the Abyss", - "indexId": "317", - "borders": "{3008,4800,3071,4863}" - }, - { - "id": "413", - "name": "The Quiz Master", - "indexId": "318", - "borders": "{1920,4736,1983,4799}" - }, - { - "id": "418", - "name": "Corporal Punishment", - "indexId": "323", - "borders": "{3136,4800,3199,4863}" - }, - { - "id": "419", - "name": "Pheasant Peasant", - "indexId": "321", - "borders": "{2560,4736,2623,4799}" - }, - { - "id": "425", - "name": "The Chosen", - "indexId": "324", - "borders": "{2432,4928,2495,4991}" - }, - { - "id": "428", - "name": "Altar Ego", - "indexId": "501", - "borders": "{3264,4800,3327,4863}" - }, - { - "id": "432", - "name": "Grimly Fiendish", - "indexId": "509", - "borders": "{1600,4800,1663,4863}-{1664,4800,1727,4863}" - }, - { - "id": "434", - "name": "Have a Blast", - "indexId": "325", - "borders": "{1920,4928,1983,4991}" - }, - { - "id": "435", - "name": "Wilderness", - "indexId": "142", - "borders": "{2944,3584,3007,3647}-{3072,3712,3135,3775}" - }, - { - "id": "442", - "name": "Clan Wars", - "indexId": "515", - "borders": "" - }, - { - "id": "443", - "name": "Bounty Hunter Level 2", - "indexId": "518", - "borders": "{2944,5632,3135,5823}" - }, - { - "id": "444", - "name": "Bounty Hunter Level 1", - "indexId": "517", - "borders": "{2688,5632,2879,5823}" - }, - { - "id": "445", - "name": "Bounty Hunter Level 3", - "indexId": "519", - "borders": "{3200,5632,3391,5823}" - }, - { - "id": "447", - "name": "Over To Nardah", - "indexId": "328", - "borders": "{3392,2880,3455,2943}" - }, - { - "id": "448", - "name": "The Monsters Below", - "indexId": "329", - "borders": "{2432,10112,2495,10175}" - }, - { - "id": "456", - "name": "Animal Apogee", - "indexId": "504", - "borders": "" - }, - { - "id": "461", - "name": "The Desolate Isle", - "indexId": "330", - "borders": "{2496,3712,2559,3775}" - }, - { - "id": "462", - "name": "Spirits of Elid", - "indexId": "331", - "borders": "{3328,9536,3391,9599}" - }, - { - "id": "463", - "name": "Fire and Brimstone", - "indexId": "334", - "borders": "{2368,5120,2431,5183}" - }, - { - "id": "464", - "name": "The Genie", - "indexId": "332", - "borders": "{3328,9280,3391,9343}" - }, - { - "id": "465", - "name": "Desert Heat", - "indexId": "333", - "borders": "{3392,2944,3455,3007}" - }, - { - "id": "469", - "name": "In the Pits", - "indexId": "335", - "borders": "{2432,5120,2495,5183}" - }, - { - "id": "471", - "name": "Brew Hoo Hoo", - "indexId": "338", - "borders": "{3648,9920,3711,9983}" - }, - { - "id": "473", - "name": "TzHaar!", - "indexId": "339", - "borders": "{2368,5056,2431,5119}" - }, - { - "id": "475", - "name": "Wild Side", - "indexId": "340", - "borders": "{3008,3840,3071,3903}" - }, - { - "id": "476", - "name": "Dead Can Dance", - "indexId": "341", - "borders": "{3136,3648,3199,3711}" - }, - { - "id": "478", - "name": "The Cellar Dwellers", - "indexId": "342", - "borders": "{2496,9664,2559,9727}" - }, - { - "id": "479", - "name": "Jungle Troubles", - "indexId": "343", - "borders": "{2880,3072,2943,3135}" - }, - { - "id": "481", - "name": "Catch Me If You Can", - "indexId": "344", - "borders": "{2624,9600,2687,9663}" - }, - { - "id": "482", - "name": "Rat a Tat Tat", - "indexId": "345", - "borders": "{2880,5056,2943,5119}" - }, - { - "id": "489", - "name": "Bubble and Squeak", - "indexId": "347", - "borders": "{1920,4672,1983,4735}" - }, - { - "id": "490", - "name": "Sarim's Vermin", - "indexId": "348", - "borders": "{2944,9600,3007,9663}" - }, - { - "id": "491", - "name": "Rat Hunt", - "indexId": "349", - "borders": "{2816,5056,2879,5119}" - }, - { - "id": "496", - "name": "The Trade Parade", - "indexId": "555", - "borders": "{3136,3456,3199,3519}" - }, - { - "id": "497", - "name": "Aye Car Rum Ba", - "indexId": "351", - "borders": "{2112,5056,2175,5119}" - }, - { - "id": "498", - "name": "Blistering Barnacles", - "indexId": "352", - "borders": "{2112,5120,2175,5183}" - }, - { - "id": "500", - "name": "A New Menace", - "indexId": "522", - "borders": "" - }, - { - "id": "501", - "name": "Distant Land", - "indexId": "353", - "borders": "{3456,3136,3519,3199}" - }, - { - "id": "505", - "name": "Pharaoh's Tomb", - "indexId": "355", - "borders": "{3328,2816,3391,2879}-{3008,4672,3071,4735}" - }, - { - "id": "511", - "name": "In the Clink", - "indexId": "360", - "borders": "{2048,4416,2111,4479}" - }, - { - "id": "515", - "name": "Mudskipper Melody", - "indexId": "361", - "borders": "{2944,3072,3007,3135}" - }, - { - "id": "517", - "name": "Subterranea", - "indexId": "362", - "borders": "{2496,10112,2559,10175}" - }, - { - "id": "526", - "name": "Arma Gonna Get You", - "indexId": "592", - "borders": "" - }, - { - "id": "524", - "name": "Xenophobe", - "indexId": "366", - "borders": "{1856,4352,1919,4415}-{2880,4416,2943,4479}" - }, - { - "id": "528", - "name": "Victory is Mine", - "indexId": "368", - "borders": "{3136,9728,3199,9791}" - }, - { - "id": "529", - "name": "Woe of the Wyvern", - "indexId": "369", - "borders": "{3008,9536,3071,9599}" - }, - { - "id": "530", - "name": "In the Brine", - "indexId": "370", - "borders": "{3648,2944,3711,3007}" - }, - { - "id": "532", - "name": "Diango's Little Helpers", - "indexId": "371", - "borders": "{1984,4416,2047,4479}" - }, - { - "id": "542", - "name": "Lament", - "indexId": "381", - "borders": "{3072,9280,3135,9343}" - }, - { - "id": "544", - "name": "Making Waves", - "indexId": "378", - "borders": "{2304,3648,2367,3711}-{2304,3584,2367,3647}" - }, - { - "id": "555", - "name": "Ardougne Ago", - "indexId": "613", - "borders": "" - }, - { - "id": "569", - "name": "Undead Army", - "indexId": "607", - "borders": "{3200,9984,3263,10047}" - }, - { - "id": "571", - "name": "Art of Hocus-Pocus", - "indexId": "624", - "borders": "" - }, - { - "id": "573", - "name": "The Mad Mole", - "indexId": "393", - "borders": "{1728,5120,1791,5183}" - }, - { - "id": "575", - "name": "Chickened Out", - "indexId": "395", - "borders": "{2432,4352,2495,4415}" - }, - { - "id": "576", - "name": "Davy Jones's Locker", - "indexId": "394", - "borders": "{2944,9472,3007,9535}" - }, - { - "id": "583", - "name": "Chef Surprize", - "indexId": "399", - "borders": "{1856,5312,1919,5375}" - }, - { - "id": "586", - "name": "Everlasting Fire", - "indexId": "417", - "borders": "{3328,3904,3391,3967}" - }, - { - "id": "587", - "name": "Null and Void", - "indexId": "400", - "borders": "{2624,2624,2687,2687}" - }, - { - "id": "588", - "name": "Pest Control", - "indexId": "401", - "borders": "{2624,2560,2687,2623}" - }, - { - "id": "594", - "name": "No Way Out", - "indexId": "403", - "borders": "{3264,9792,3327,9855}-{3072,5184,3135,5247}-{3008,5184,3071,5247}" - }, - { - "id": "603", - "name": "Funny Bunnies", - "indexId": "406", - "borders": "{2432,5248,2495,5311}" - }, - { - "id": "604", - "name": "Assault and Battery", - "indexId": "407", - "borders": "" - }, - { - "id": "612", - "name": "Head to Head", - "indexId": "411", - "borders": "{1856,5120,1919,5183}" - }, - { - "id": "616", - "name": "Back to Life", - "indexId": "414", - "borders": "" - }, - { - "id": "634", - "name": "Warriors' Guild", - "indexId": "423", - "borders": "" - }, - { - "id": "964", - "name": "Too Many Cooks...", - "indexId": "398", - "borders": "{2944,9856,3007,9919}" - }, - { - "id": "222", - "name": "Major Miner", - "indexId": "437", - "borders": "{2403,10186,2409,10192}" - }, - { - "id": "223", - "name": "Norse Code", - "indexId": "439", - "borders": "{2404,3797,2421,3820}" - }, - { - "id": "225", - "name": "Volcanic Vikings", - "indexId": "440", - "borders": "{2304,3785,2635,3818}" - }, - { - "id": "584", - "name": "Temple Desecrated", - "indexId": "628", - "borders": "{2580,5763,2620,5700}" - }, - { - "id": "589", - "name": "The Sound of Guthix", - "indexId": "626", - "borders": "{2500,5766,2620,5810}" - }, - { - "id": "487", - "name": "Charmin' Farmin'", - "indexId": "516", - "borders": "{1600,4672,1663,4735}" - } -] 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 2c4f2fc48..44c10bdcb 100644 --- a/Server/data/configs/npc_configs.json +++ b/Server/data/configs/npc_configs.json @@ -1,259418 +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 2009Scape'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 2009Scape'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", + "examine": "One of Gielinor's many citizens.", + "attack_level": "5", + "attack_speed": "4", "clue_level": "0", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "One of 2009Scape's many citizens.", - "melee_animation": "422", - "range_animation": "422", "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 2009Scape'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 2009Scape'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 2009Scape'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": "284", - "range_animation": "0", + "clue_level": "0", "combat_audio": "511,513,512", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "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", - "attack_speed": "6", - "defence_animation": "387", - "weakness": "7", - "magic_animation": "390", - "death_animation": "836", - "name": "Al-Kharid warrior", - "defence_level": "5", - "safespot": null, - "lifepoints": "19", - "strength_level": "15", - "id": "18", - "clue_level": "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 2009Scape'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 2009Scape'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": "A dragon covered in frost and ice.", - "combat_audio": "408,410,409", - "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": "408,410,409", - "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", - "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", "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": "400,404,403", - "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": "496,498,497", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "32", - "defence_animation": "6375", - "weakness": "3", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn", - "defence_level": "13", + "range_level": "1", + "respawn_delay": "90", "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", - "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", - "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": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "27", - "magic_animation": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "105", - "aggressive": "true", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "4929", - "name": "Black bear", - "defence_level": "17", - "safespot": null, - "lifepoints": "24", - "strength_level": "17", - "id": "106", - "range_level": "1", - "attack_level": "17" - }, - { - "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", - "range_animation": "0", - "combat_audio": "3611,3612,3610", + "attack_level": "13", "attack_speed": "4", - "poisonous": "true", - "respawn_delay": "25", - "defence_animation": "6255", - "weakness": "0", - "slayer_exp": "23", - "poison_amount": "3", + "bonuses": "0,0,0,0,0,5,5,5,-5,5,0,0,0,0,0", + "combat_audio": "436,439,438", + "death_animation": "5534", + "defence_animation": "5533", + "defence_level": "18", + "lifepoints": "25", "magic_animation": "0", - "death_animation": "6256", - "name": "Poison Scorpion", - "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", + "magic_level": "1", + "melee_animation": "5532", + "range_animation": "0", "range_level": "1", - "attack_level": "16" + "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", + "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", + "lifepoints": "27", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "4925", + "range_animation": "0", + "range_level": "1", + "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": "449,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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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", - "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", - "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": "469,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", - "attack_speed": "4", - "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", - "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", "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": "496,498,497", - "attack_speed": "5", - "magic_level": "24", - "defence_animation": "6375", - "weakness": "3", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Black unicorn", - "defence_level": "15", - "safespot": null, - "lifepoints": "28", - "strength_level": "10", - "id": "133", "range_level": "1", - "attack_level": "15" + "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "138", "range_level": "1", - "attack_level": "30" + "respawn_delay": "30", + "safespot": null, + "strength_level": "23", + "weakness": "6" }, { - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "melee_animation": "1010", - "strength_level": "1", "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", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "1012" + "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", - "attack_speed": "4", - "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 RuneScape'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", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "178", - "aggressive": "true", "range_level": "1", - "attack_level": "22" + "respawn_delay": "25", + "safespot": null, + "strength_level": "25", + "weakness": "7" }, { - "melee_animation": "390", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "388", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "42", - "strength_level": "1", "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": "1" + "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": "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", - "id": "187", - "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", - "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", - "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": "true", - "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", - "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", - "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 RuneScape'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 RuneScape'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 RuneScape'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 RuneScape'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" - }, - { - "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 RuneScape'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 RuneScape'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 RuneScape'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 RuneScape'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" - }, - { - "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": "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", - "id": "374", - "aggressive": "true", - "bonuses": "15,15,10,20,40,15,50,50,50,20,50,58,0,0,0", - "range_level": "27", - "attack_level": "27" + "weakness": "2" }, { - "examine": "A pirate.", - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Customs officer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "380", - "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": "382", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not very civilised looking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barbarian guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "384", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks vicious!", - "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", - "safespot": null, - "lifepoints": "10", - "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", - "combat_audio": "3611,3612,3610", - "strength_level": "1", - "id": "387", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with a shave...", - "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" - }, - { - "examine": "A brightly coloured game bird.", - "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" - }, - { - "examine": "A brightly coloured game bird.", - "melee_animation": "2371", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "2373", - "name": "River troll", - "defence_level": "3", - "safespot": null, - "lifepoints": "50", - "strength_level": "3", - "id": "392", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "Beefy.", - "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": "397", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jungle forester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "402", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Insects buzzing around.", - "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" - }, - { - "examine": "An annoying flappy thing.", - "slayer_task": "5", - "melee_animation": "4915", - "range_animation": "4915", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "30", - "defence_animation": "4916", - "weakness": "4", - "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" - }, - { - "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", - "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": "17", - "safespot": null, - "lifepoints": "50", - "strength_level": "17", - "id": "438", - "range_level": "1", - "attack_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": "32", - "safespot": null, - "lifepoints": "55", - "strength_level": "32", - "id": "439", - "range_level": "1", - "attack_level": "32" - }, - { - "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": "48", - "safespot": null, - "lifepoints": "60", - "strength_level": "48", - "id": "440", - "range_level": "1", - "attack_level": "48" - }, - { - "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": "65", - "safespot": null, - "lifepoints": "86", - "strength_level": "65", - "id": "441", - "range_level": "1", - "attack_level": "65" - }, - { - "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, - "lifepoints": "120", - "strength_level": "100", - "id": "442", - "range_level": "1", - "attack_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", - "defence_level": "130", - "safespot": null, - "lifepoints": "170", - "strength_level": "130", - "id": "443", - "range_level": "1", - "attack_level": "130" - }, - { - "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": "444", - "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": "445", - "clue_level": "0", - "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": "2", - "safespot": null, - "lifepoints": "5", - "strength_level": "5", - "id": "446", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "I wonder who he's guarding?", - "melee_animation": "0", - "range_animation": "0", + "id": "272", + "name": "Lucien", + "examine": "He walks with a slight limp.", + "aggressive": "", + "attack_level": "12", "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", - "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": "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "A large well built farmer.", - "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" - }, - { - "examine": "Aargh! It's alive!", - "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", - "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" - }, - { - "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", - "range_level": "1", - "attack_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", - "range_level": "1", - "attack_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.", - "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" - }, - { - "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "It's one of General Khazard's commanders.", - "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" - }, - { - "examine": "It's a tree gnome trooper.", - "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" - }, - { - "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" - }, - { - "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 2", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "482", - "range_level": "1", - "attack_level": "1" - }, - { - "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 3", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "483", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a young tree gnome.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Local Gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "485", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I guess he wants to be more than just the muscle.", - "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" - }, - { - "examine": "He doesn't look like he'd trust his own mother.", - "slayer_task": "38", - "melee_animation": "6199", - "range_animation": "0", - "combat_audio": "469,472,471", - "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" - }, - { - "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", - "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.", - "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" - }, - { - "examine": "A vicious", - "slayer_task": "71", - "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" - }, - { - "examine": "A minion of Rashiliyia.", - "slayer_task": "75", - "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" - }, - { - "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": "504", - "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": "505", - "aggressive": "true", - "range_level": "1", - "attack_level": "33" - }, - { - "melee_animation": "5571", - "respawn_delay": "60", - "defence_animation": "5574", - "death_animation": "5575", - "name": "Nazastarool", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "507", - "aggressive": "true", - "range_level": "1", - "attack_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", - "defence_level": "1", - "safespot": null, - "lifepoints": "110", - "strength_level": "1", - "id": "509", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bob", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "519", - "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": "Shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "520", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Helps sell stuff.", - "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": "521", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes people spending money.", - "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": "522", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes helping sell stuff.", - "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": "523", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A product of a consumerist society.", - "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": "524", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes you more", - "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" - }, - { - "examine": "A product of a consumerist society.", - "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" - }, - { - "examine": "Likes you more", - "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": "527", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has an interesting assortment of items for sale.", - "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": "530", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works on commission.", - "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": "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "552", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "For the interesting clothing items you just can't find elsewhere.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fancy-dress shop owner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "554", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has a fine moustache.", - "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": "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "561", - "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-maker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "562", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks strange and mysterious.", - "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": "567", - "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": "569", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Seems very well-off.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gem merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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" - }, - { - "examine": "Seems very well-off.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silk merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He runs a Mining store.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Drogo dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "579", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder if he wants to buy my junk?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "582", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "If crafting's your thing, he's your man!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rommik", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems to sell tea.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tea seller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "595", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A pizza expert; in both making and eating.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fat Tony", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "596", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Anyone fancy a trim?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hairdresser", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "598", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Master of the mystical make-over.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Make-over Mage", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "599", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Indentured servant of a Knight.", - "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" - }, - { - "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": "609", - "range_level": "1", - "attack_level": "26" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Witch", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "611", - "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": "613", - "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": "Doug Deeping", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "614", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A student busy studying the digsite.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Student", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "617", - "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": "618", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on archaeology.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Archaeological expert", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "619", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A specialist in panning for gold.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Panning guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "620", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome baller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "621", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome winger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "633", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps the game fair.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome ball referee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "635", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dealer in potions.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Apothecary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "638", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A religious man... And occasional drunk.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Father Lawrence", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "640", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks down on his luck.", - "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" - }, - { - "examine": "Looks kind of obsessive...", - "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" - }, - { - "examine": "Looks kind of shifty...", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "498,500,499", - "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" - }, - { - "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", - "attack_level": "1", - "prj_height": "3" - }, - { - "examine": "Guardian of the dramen tree.", - "melee_animation": "5532", - "range_animation": "0", - "attack_speed": "5", - "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" - }, - { - "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.", - "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": "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "658", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He likes to paaaarty!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Party Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "659", - "range_level": "1", - "attack_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", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "663", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "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" - }, - { - "examine": "The boss!", - "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" - }, - { - "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", - "melee_animation": "64", - "range_animation": "64", - "combat_audio": "400,404,403", - "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": "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" - }, - { - "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", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The keeper of the gates to the ranging guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ranging Guild Doorman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "679", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert leatherworker.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Leatherworker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "680", - "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": "Held vampyre juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "681", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Rangers armour.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Armour salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "682", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Archery equipment.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bow and Arrow salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "683", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tower keeper and competition judge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tower Advisor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "684", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "aggressive": "true", - "range_level": "10", - "attack_level": "7" - }, - { - "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", - "aggressive": "true", - "range_level": "20", - "attack_level": "15" - }, - { - "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", - "aggressive": "true", - "range_level": "30", - "attack_level": "22" - }, - { - "examine": "Defender of the west 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": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "30", - "id": "691", - "aggressive": "true", - "range_level": "40", - "attack_level": "30" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ticket Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "694", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Harlan, ready to teach swordplay.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Melee tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "705", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old wizard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard Mizgog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "706", - "range_level": "1", - "attack_level": "1" - }, - { - "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, - "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, - "lifepoints": "8", - "strength_level": "1", - "id": "709", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "0", - "examine": "It's an NPC.", - "name": "Edmond", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "714", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of RuneScape's many citizens.", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "726", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of RuneScape's many citizens.", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "727", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "One of RuneScape'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" - }, - { - "examine": "One of RuneScape'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 RuneScape's many citizens.", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "730", - "clue_level": "0", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "731", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "732", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "735", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "737", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "739", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Duke Horacio of Lumbridge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Duke Horacio", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "741", - "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" - }, - { - "examine": "A badly-behaved goblin.", - "slayer_task": "38", - "melee_animation": "6184", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6204", - "name": "Wormbrain", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "745", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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": "Enraged barbarian spirit", - "defence_level": "76", - "safespot": null, - "lifepoints": "108", - "strength_level": "76", - "id": "750", - "aggressive": "true", - "range_level": "1", - "attack_level": "76" - }, - { - "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": "Berserk barbarian spirit", - "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.", - "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" - }, - { - "examine": "He looks totally insane!", - "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" - }, - { - "examine": "Stop looking and run!", - "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" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "combat_audio": "703,705,704", - "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", - "id": "799", - "range_level": "1", - "attack_level": "30" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Abbot Langley", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "801", - "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 Jered", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "802", - "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, + "defence_level": "10", "lifepoints": "17", - "strength_level": "12", - "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", + "magic_level": "1", + "melee_animation": "401", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "respawn_delay": "30", + "safespot": null, + "strength_level": "11", + "weakness": "" }, { - "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", + "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, - "lifepoints": "10", - "strength_level": "1", - "id": "805", - "range_level": "1", - "attack_level": "1" + "strength_level": "", + "weakness": "" }, { - "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", + "id": "274", + "name": "Guardian of Armadyl", + "examine": "A guard who has devoted their life to Armadyl.", + "attack_level": "30", "combat_audio": "511,513,512", - "defence_animation": "0", + "death_animation": "836", + "defence_animation": "404", + "defence_level": "30", + "lifepoints": "42", "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "812", - "clue_level": "1", + "melee_animation": "401", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "8" }, { - "examine": "One of those people who love to gossip!", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "He sure likes to sell stuff!", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "Big", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "It looks like he's been here a long time.", - "melee_animation": "0", - "range_animation": "0", + "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", - "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", + "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", - "name": "Rowdy slave", - "defence_level": "5", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "827", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", "defence_animation": "0", - "weakness": "9", + "defence_level": "15", + "lifepoints": "21", "magic_animation": "0", - "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", - "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", - "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" - }, - { - "examine": "He patrols the Shantay Pass.", - "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" - }, - { - "examine": "A vicious desert wolf.", - "slayer_task": "92", - "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" - }, - { - "death_animation": "9674", - "name": "Ugthanki", - "defence_level": "1", - "safespot": null, - "lifepoints": "46", - "strength_level": "1", - "attack_speed": "5", - "id": "840", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "He looks busy attending to his cart.", - "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" - }, - { - "examine": "This pickaxe has been possessed by a dwarf ancestor spirit.", - "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" - }, - { - "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", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Head chef", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "847", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The famous tree gnome chef.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Aluft Gianne snr.", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "850", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can serve you gnome food.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome Waiter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "851", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tough-looking.", - "slayer_task": "64", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre chieftain", - "defence_level": "58", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", - "id": "852", - "range_level": "1", - "attack_level": "58" - }, - { - "name": "Gorad", - "defence_level": "1", - "safespot": null, - "lifepoints": "81", - "strength_level": "1", - "id": "856", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "859", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "defence_animation": "0", - "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", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", - "id": "870", - "range_level": "1", - "attack_level": "58" - }, - { - "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", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ogre trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "875", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tries to keep the peace.", "melee_animation": "386", "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower guard", - "defence_level": "17", - "safespot": null, - "lifepoints": "97", - "strength_level": "17", - "id": "877", - "range_level": "1", - "attack_level": "17" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "67", - "name": "Delrith", - "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" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ceril Carnillean", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "885", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "887", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" + "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", - "name": "Clivet", - "defence_level": "1", - "safespot": null, + "defence_animation": "0", + "defence_level": "18", "lifepoints": "25", - "melee_animation": "811", - "strength_level": "1", - "id": "893", + "magic_animation": "0", + "melee_animation": "390", + "range_animation": "0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "safespot": null, + "strength_level": "18", + "weakness": "9" }, { - "examine": "A member of the Hazeel cult.", + "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", + "respawn_delay": "25", + "safespot": null, + "strength_level": "27", + "weakness": "8" + }, + { + "id": "375", + "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", + "range_level": "1", + "safespot": null, + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "381", + "name": "Dwarf" + }, + { + "id": "382", + "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": "383", + "name": "Stankers" + }, + { + "id": "384", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "385", + "name": "Kharid Scorpion", + "examine": "Looks vicious!", + "attack_level": "1", + "combat_audio": "3611,3612,3610", + "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": "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", + "range_level": "1", + "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", + "range_level": "1", + "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.", + "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": "398", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "399", + "name": "Legends guard" + }, + { + "id": "400", + "name": "Radimus Erkle" + }, + { + "id": "401", + "name": "Jungle forester", + "examine": "He 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": "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.", + "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", + "range_level": "1", + "respawn_delay": "30", + "safespot": null, + "slayer_exp": "8", + "strength_level": "1", + "weakness": "4" + }, + { + "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", + "combat_style": "1", + "death_animation": "156", + "defence_animation": "154", + "lifepoints": "25", + "projectile": "968", + "range_animation": "5347", + "range_level": "15", + "strength_level": "8" + }, + { + "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", + "projectile": "968", + "range_animation": "5347", + "range_level": "30", + "strength_level": "17" + }, + { + "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", + "projectile": "968", + "range_animation": "5347", + "range_level": "40", + "strength_level": "36" + }, + { + "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", + "projectile": "968", + "range_animation": "5347", + "range_level": "60", + "strength_level": "64" + }, + { + "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", + "projectile": "968", + "range_animation": "5347", + "range_level": "75", + "strength_level": "100" + }, + { + "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", + "lifepoints": "170", + "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", + "safespot": null, + "strength_level": "8" + }, + { + "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, + "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", + "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": "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.", + "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", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "5", + "weakness": "8" + }, + { + "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", + "respawn_delay": "60", + "safespot": null, + "strength_level": "23" + }, + { + "id": "448", + "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": "0", + "range_animation": "0", + "range_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", + "range_level": "1", + "safespot": null, + "strength_level": "14", + "weakness": "6" + }, + { + "id": "454", + "name": "Sanfew", + "examine": "An old druid." + }, + { + "id": "455", + "name": "Kaqemeex", + "examine": "A wise druid." + }, + { + "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", + "safespot": null, + "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "458", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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", + "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", + "range_level": "1", + "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", + "range_level": "32", + "safespot": null, + "strength_level": "24", + "weakness": "1" + }, + { + "id": "480", + "name": "Gnome troop" + }, + { + "id": "481", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "482", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "483", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "484", + "name": "Local Gnome" + }, + { + "id": "485", + "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", + "range_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.", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "37", + "weakness": "7" + }, + { + "id": "490", + "name": "Mummy", + "examine": "If the mummy is at school", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_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", + "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", + "range_level": "7", + "respawn_delay": "60", + "safespot": null, + "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.", + "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": "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.", + "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": "505", + "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": "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", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "508", + "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, + "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", + "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." + }, + { + "id": "518", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "519", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "520", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "521", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "522", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "523", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "524", + "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", + "range_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", + "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", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "527", + "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", + "range_level": "1", + "safespot": null, + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "531", + "name": "Shop assistant", + "examine": "Works on commission.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "553", + "name": "Aubury" + }, + { + "id": "554", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "555", + "name": "Shopkeeper", + "examine": "Has a fine moustache.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "562", + "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", + "range_level": "1", + "safespot": null, + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "568", + "name": "Zambo", + "examine": "Appears slightly drunk." + }, + { + "id": "569", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "570", + "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", + "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", + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "575", + "name": "Hickton" + }, + { + "id": "576", + "name": "Harry" + }, + { + "id": "577", + "name": "Cassie", + "examine": "Nice eyes." + }, + { + "id": "578", + "name": "Frincos" + }, + { + "id": "579", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "580", + "name": "Flynn", + "examine": "The mace salesman." + }, + { + "id": "581", + "name": "Wayne", + "examine": "An armourer." + }, + { + "id": "582", + "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", + "range_level": "1", + "safespot": null, + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "596", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "597", + "name": "Noterazzo" + }, + { + "id": "598", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "599", + "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", + "range_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", + "safespot": null, + "strength_level": "26", + "weakness": "9" + }, + { + "id": "610", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "611", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "612", + "name": "Greldo" + }, + { + "id": "613", + "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": "614", + "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", + "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", + "lifepoints": "10", + "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", + "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": "618", + "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": "619", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "620", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "621", + "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", + "range_level": "1", + "safespot": null, + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "634", + "name": "Gnome winger" + }, + { + "id": "635", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "636", + "name": "Cheerleader" + }, + { + "id": "637", + "name": "Juliet", + "examine": "A tearful damsel, maybe I can help her?" + }, + { + "id": "638", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "639", + "name": "Romeo", + "examine": "Rather dense and soppy looking." + }, + { + "id": "640", + "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", + "range_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", + "range_level": "1", + "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", - "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": "1", + "weakness": "9" }, { - "examine": "Children are just like real people...just smaller.", + "id": "646", + "name": "Curator Haig Halen", + "examine": "Curator of the museum.", + "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", - "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": "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", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "45", + "weakness": "9" + }, + { + "id": "656", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "657", + "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": "658", + "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": "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "660", + "name": "Knight", + "attack_level": "1", + "combat_audio": "511,513,512", + "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "664", + "name": "Boot" + }, + { + "id": "665", + "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", + "range_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", + "range_level": "1", + "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.", + "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": "678", + "name": "Guard", + "examine": "Keeps order in the ranging guild.", + "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": "679", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "680", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "681", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "682", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "683", + "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", + "range_level": "1", + "safespot": null, + "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "685", + "name": "Tower Advisor" + }, + { + "id": "686", + "name": "Tower Advisor" + }, + { + "id": "687", + "name": "Tower Advisor" + }, + { + "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", + "respawn_delay": "60", + "safespot": null, + "strength_level": "7", + "weakness": "0" + }, + { + "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", + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "0" + }, + { + "id": "690", + "name": "Tower Archer", + "examine": "Defender of the south tower.", + "aggressive": "true", + "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", + "safespot": null, + "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", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "0" + }, + { + "id": "692", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "693", + "name": "Competition Judge" + }, + { + "id": "694", + "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", + "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", + "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", + "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", + "range_level": "1", + "safespot": null, + "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "707", + "name": "Wizard Grayzag", + "examine": "Master of imps." + }, + { + "id": "708", + "name": "Imp", + "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", + "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "710", + "name": "Alrena" + }, + { + "id": "711", + "name": "Bravek" + }, + { + "id": "712", + "name": "Carla" + }, + { + "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "714", + "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." + }, + { + "id": "716", + "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." + }, + { + "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", + "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" + }, + { + "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", + "safespot": null, + "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "728", + "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": "" + }, + { + "id": "729", + "name": "Man", + "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, + "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "731", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "732", + "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", + "range_level": "1", + "safespot": null, + "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", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "736", + "name": "Emily", + "examine": "Works in the Rising Sun." + }, + { + "id": "737", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "738", + "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", + "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", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "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.", + "attack_level": "1", + "death_animation": "6204", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "1", + "magic_animation": "0", + "melee_animation": "6184", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1", + "weakness": "9" + }, + { + "id": "746", + "name": "Oracle" + }, + { + "id": "747", + "name": "Oziach", + "examine": "A strange little man." + }, + { + "id": "748", + "name": "Angry barbarian spirit" + }, + { + "id": "749", + "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", + "safespot": null, + "strength_level": "76", + "weakness": "6" + }, + { + "id": "750", + "name": "Enraged 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": "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", + "range_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", + "range_level": "1", + "safespot": null, + "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", + "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" + }, + { + "id": "767", + "name": "Crate", + "aggressive": "true", + "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", + "safespot": null, + "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", + "respawn_delay": "60", + "safespot": null, + "strength_level": "30", + "weakness": "6" + }, + { + "id": "800", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "801", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "802", + "name": "Brother Jered", + "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": "803", + "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": "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", + "safespot": null, + "strength_level": "1" + }, + { + "id": "826", + "name": "Escaping slave" + }, + { + "id": "827", + "name": "Rowdy slave", + "examine": "He looks a bit aggressive.", + "aggressive": "true", + "attack_level": "5", + "death_animation": "836", + "defence_animation": "0", + "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": "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", + "range_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.", + "attack_level": "11", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "11", + "lifepoints": "15", + "magic_animation": "0", + "melee_animation": "6559", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "11", + "weakness": "9" + }, + { + "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", + "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", + "range_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", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "40" + }, + { + "id": "843", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "844", + "name": "Horacio" + }, + { + "id": "846", + "name": "Kangai Mau", + "examine": "A happening kind of guy!" + }, + { + "id": "847", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "848", + "name": "Blurberry" + }, + { + "id": "849", + "name": "Barman" + }, + { + "id": "850", + "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", + "range_level": "1", + "safespot": null, + "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", + "safespot": null, + "strength_level": "1" + }, + { + "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", + "respawn_delay": "300", + "safespot": null, + "strength_level": "71", + "weakness": "7" + }, + { + "id": "853", + "name": "Og" + }, + { + "id": "854", + "name": "Grew" + }, + { + "id": "855", + "name": "Toban" + }, + { + "id": "856", + "name": "Gorad", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "81", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "857", + "name": "Ogre guard" + }, + { + "id": "858", + "name": "Ogre guard", + "examine": "These ogres protect the city.", + "attack_level": "1", + "death_animation": "0", + "defence_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": "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", + "safespot": null, + "strength_level": "1", + "weakness": "7" + }, + { + "id": "860", + "name": "Ogre guard" + }, + { + "id": "861", + "name": "Ogre guard" + }, + { + "id": "862", + "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", + "melee_animation": "2100", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "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", + "respawn_delay": "60", + "safespot": null, + "strength_level": "58", + "weakness": "7" + }, + { + "id": "871", + "name": "Watchtower Wizard" + }, + { + "id": "872", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "873", + "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": "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", + "range_level": "1", + "safespot": null, + "strength_level": "17", + "weakness": "7" + }, + { + "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", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "879", + "name": "Delrith", + "attack_level": "1", + "death_animation": "67", + "defence_animation": "65", + "defence_level": "1", + "lifepoints": "7", + "melee_animation": "64", + "range_level": "1", + "respawn_delay": "60", + "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." + }, + { + "id": "884", + "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", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "id": "885", + "name": "Ceril Carnillean", + "examine": "Head of the Carnillean household.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", + "defence_level": "1", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { + "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", + "range_level": "1", + "respawn_delay": "60", + "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", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "15", + "weakness": "3" + }, + { + "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", + "range_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" + }, + { + "id": "915", + "name": "Leela", + "examine": "She comes from Al-Kharid." }, { - "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?", + "name": "Joe", + "examine": "A crafter at the pinnacle of his art.", + "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": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "836", - "name": "Jail guard", - "defence_level": "21", + "range_level": "1", "safespot": null, - "lifepoints": "32", - "strength_level": "23", + "strength_level": "1" + }, + { "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", - "attack_level": "19" + "respawn_delay": "60", + "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." }, { - "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.", + "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", - "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" + "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", - "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" + "weakness": "6" + }, + { + "id": "932", + "name": "Fionella", + "examine": "She sells general items at the Legends Guild." + }, + { + "id": "933", + "name": "Siegfried Erkle" }, { - "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", + "name": "Nezikchened", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "5485", - "respawn_delay": "60", - "defence_animation": "5489", - "death_animation": "5491", - "name": "Ranalph Devere", + "attack_level": "1", + "death_animation": "67", + "defence_animation": "65", "defence_level": "1", - "safespot": null, - "lifepoints": "130", - "strength_level": "1", - "id": "938", - "aggressive": "true", + "lifepoints": "150", + "melee_animation": "64", "range_level": "1", - "attack_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", - "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" + "safespot": null, + "strength_level": "1" + }, + { + "id": "940", + "name": "Echned Zekin" }, { - "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", + "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", - "attack_level": "68" + "respawn_delay": "35", + "safespot": null, + "slayer_exp": "75", + "strength_level": "68", + "weakness": "3" }, { - "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Survival expert", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "943", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on all forms of combat.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Combat instructor", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "944", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Your introduction to the world of RuneScape.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "RuneScape guide", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "945", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A master of Magic.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Magic instructor", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "946", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An official representative from the First National Bank of RuneScape.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Financial advisor", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "947", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on Mining-related skills.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mining instructor", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "948", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Your introduction to the world of RuneScape.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Quest guide", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", + "name": "Quest guide", + "examine": "Your introduction to the world of Gielinor.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, - "lifepoints": "5", - "strength_level": "1", - "id": "950", - "aggressive": "true", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_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", + "magic_animation": "5387", "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" + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" }, { + "id": "952", + "name": "Fishing spot" + }, + { + "id": "953", + "name": "Banker" + }, + { + "id": "954", + "name": "Brother Brace", "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 Brace", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "954", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { + "id": "955", + "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": "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" + "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", - "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" + "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.", - "slayer_task": "76", + "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", - "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" + "safespot": null, + "strength_level": "22", + "weakness": "2" }, { + "id": "978", + "name": "Blessed giant rat", "examine": "It's one of Iban's pet vermin.", - "slayer_task": "67", - "range_animation": "0", + "attack_level": "12", "combat_audio": "703,705,704", "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Blessed giant rat", "defence_level": "12", - "safespot": null, "lifepoints": "17", + "magic_animation": "0", + "range_animation": "0", + "range_level": "1", + "safespot": null, "strength_level": "12", - "id": "978", - "range_level": "1", - "attack_level": "12" + "weakness": "9" }, { - "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" - }, - { + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "980", - "range_level": "1", - "attack_level": "1" - }, - { + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "981", - "range_level": "1", - "attack_level": "1" - }, - { + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "982", - "range_level": "1", - "attack_level": "1" - }, - { + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "983", - "range_level": "1", - "attack_level": "1" - }, - { + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Slave", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", + "examine": "A wretched slave of Iban.", + "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": "985", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Precariously balanced...", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Boulder", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "name": "Unicorn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "496,498,497", - "strength_level": "1", "id": "987", + "name": "Unicorn", + "attack_level": "1", + "combat_audio": "369,371,370", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_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", + "name": "Sir Jerro", "attack_level": "1", - "defence_animation": "425" - }, - { "death_animation": "836", - "name": "Sir Carl", + "defence_animation": "425", "defence_level": "1", - "safespot": null, "lifepoints": "57", "melee_animation": "400", - "strength_level": "1", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { "id": "989", - "range_level": "1", - "respawn_delay": "60", + "name": "Sir Carl", "attack_level": "1", - "defence_animation": "425" - }, - { "death_animation": "836", - "name": "Sir Harry", + "defence_animation": "425", "defence_level": "1", - "safespot": null, "lifepoints": "57", "melee_animation": "400", - "strength_level": "1", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { "id": "990", - "range_level": "1", - "respawn_delay": "60", + "name": "Sir Harry", "attack_level": "1", - "defence_animation": "425" + "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" }, { - "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...", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Witch's cat", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "992", + "name": "Kardia" + }, + { "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", + "name": "Witch's cat", + "examine": "Curiosity is yet to kill this one...", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "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.", + "lifepoints": "10", + "magic_animation": "0", "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" + "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", + "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" + }, + { + "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" + }, + { + "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", - "magic_level": "32", + "range_level": "1", "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" + "weakness": "3" + }, + { + "id": "1003", + "name": "Lord Iban" }, { - "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", + "name": "Spider", + "examine": "Incey wincey.", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "8", - "name": "Giant bat", + "attack_level": "1", + "death_animation": "6251", + "defence_animation": "6250", "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1005", + "lifepoints": "2", + "melee_animation": "6249", "range_level": "1", - "attack_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", - "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" + "respawn_delay": "60", + "safespot": null, + "spell_id": "43", + "strength_level": "50", + "weakness": "3" + }, + { + "id": "1008", + "name": "Hamid" }, { - "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", + "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", - "attack_level": "50" + "respawn_delay": "30", + "safespot": null, + "strength_level": "28", + "weakness": "2" + }, + { + "id": "1010", + "name": "Rantz", + "examine": "He hungry.", + "combat_style": "1", + "prj_height": "50", + "projectile": "242", + "range_animation": "3460", + "start_gfx": "243", + "start_height": "100" + }, + { + "id": "1011", + "name": "Fycie" + }, + { + "id": "1012", + "name": "Bugs" }, { - "examine": "A green skinned croaker", - "melee_animation": "0", - "range_animation": "0", - "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.", + "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", - "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" + "respawn_delay": "34", + "safespot": null, + "strength_level": "1" }, { + "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.", - "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", - "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" + "respawn_delay": "18", + "safespot": null, + "strength_level": "1", + "weakness": "9" }, { + "id": "1018", + "name": "Rooster", "examine": "He rules the", - "slayer_task": "7", + "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", - "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" + "weakness": "9" }, { - "examine": "A fire elemental.", - "melee_animation": "1029", - "range_animation": "0", - "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", + "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", - "attack_level": "24" + "respawn_delay": "60", + "safespot": null, + "strength_level": "24", + "weakness": "3" }, { - "examine": "An earth elemental.", - "melee_animation": "4868", - "range_animation": "0", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "32", - "safespot": null, - "movement_radius": "4", - "lifepoints": "45", - "strength_level": "32", "id": "1020", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "An air elemental.", - "melee_animation": "1040", - "range_animation": "0", - "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", - "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", - "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", + "name": "Earth elemental", + "examine": "An earth elemental.", "aggressive": "false", - "range_level": "1", - "attack_level": "1" + "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" }, { - "examine": "Arrghhh... A Ghast.", - "death_gfx": "265", - "range_animation": "0", - "melee_animation": "1087", - "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, + "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", - "strength_level": "22", - "id": "1053", + "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", - "attack_level": "22" + "respawn_delay": "60", + "safespot": null, + "start_gfx": "99", + "strength_level": "18", + "weakness": "3" }, { - "examine": "Ticket trader for the Brimhaven Agility Arena.", + "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", - "combat_audio": "703,705,704", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pirate Jackie the Fruit", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", "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", + "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A member of the audience.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Strange watcher", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1056", + "name": "Mime" + }, + { "id": "1057", - "range_level": "1", - "attack_level": "1" - }, - { + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Strange watcher", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", + "examine": "A member of the audience.", + "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": "1059", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Commander of the Imperial Guard.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Denulth", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1060", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A sergeant of the Imperial Guard.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sergeant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1061", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A soldier of the Imperial Guard.", + "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", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Soldier", - "defence_level": "1", + "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", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_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", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "A soldier of the Imperial Guard.", - "name": "Soldier", - "defence_level": "44", - "safespot": null, - "lifepoints": "55", - "combat_audio": "511,513,512", - "strength_level": "44", "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", - "attack_level": "44" + "safespot": null, + "strength_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" - }, - { + "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", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Soldier", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1067", - "range_level": "1", - "attack_level": "1" - }, - { + "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", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Soldier", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", + "examine": "A soldier of the Imperial Guard.", + "attack_level": "1", + "combat_audio": "511,513,512", + "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": "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "A Burthorpe Castle Archer.", - "start_gfx": "19", - "combat_style": "1", - "melee_animation": "426", - "range_animation": "426", + "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", - "respawn_delay": "12", - "defence_animation": "404", - "magic_animation": "426", - "death_animation": "9055", - "name": "Archer", - "defence_level": "44", + "melee_animation": "422", + "range_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "55", - "strength_level": "1", + "strength_level": "2" + }, + { "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", + "examine": "A Burthorpe Castle Archer.", + "attack_level": "1", "bonuses": "54,68,75,72,67,81,54,35,35,0,0,0,0,0,0", - "range_level": "44", + "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", - "attack_level": "1" + "range_animation": "426", + "range_level": "44", + "respawn_delay": "12", + "safespot": null, + "start_gfx": "19", + "strength_level": "1" }, { - "examine": "A Burthorpe Castle guard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "33", + "melee_animation": "426", + "projectile": "10", + "range_animation": "426", + "range_level": "44", + "respawn_delay": "12", "safespot": null, - "lifepoints": "44", - "strength_level": "33", + "start_gfx": "19", + "strength_level": "1" + }, + { + "id": "1075", + "name": "Archer" + }, + { "id": "1076", - "clue_level": "1", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "A Burthorpe Castle guard.", - "melee_animation": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", "name": "Guard", - "defence_level": "33", - "safespot": null, - "lifepoints": "44", - "strength_level": "33", - "id": "1077", + "examine": "A Burthorpe Castle guard.", + "attack_level": "33", "clue_level": "1", - "range_level": "1", - "attack_level": "33" - }, - { - "examine": "A servant for Prince Anlaf.", - "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": "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", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "death_animation": "836", - "name": "Penda", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "422", - "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", - "attack_level": "1", - "defence_animation": "425" + "safespot": null, + "strength_level": "3" }, { - "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" - }, - { + "name": "Hygd", + "examine": "A citizen of Burthorpe.", + "attack_level": "3", "death_animation": "836", - "name": "Ceolburg", - "defence_level": "1", - "safespot": null, + "defence_animation": "425", + "defence_level": "3", "lifepoints": "10", + "magic_level": "1", "melee_animation": "422", - "strength_level": "1", - "id": "1089", "range_level": "1", "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "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", - "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" + "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.", - "slayer_task": "83", + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "110", + "weakness": "7" }, { - "examine": "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", + "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", - "attack_level": "61" + "respawn_delay": "60", + "safespot": null, + "strength_level": "110", + "weakness": "7" }, { - "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", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "100", + "weakness": "7" }, { - "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" - }, - { + "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", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ash", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1", + "weakness": "0" }, { - "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", + "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", - "attack_level": "41" + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" }, { - "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", + "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", - "attack_level": "41" + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" }, { - "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", + "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", - "attack_level": "41" + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" }, { - "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", + "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", - "safespot": null, "lifepoints": "95", - "strength_level": "41", - "id": "1105", - "range_level": "40", + "magic_animation": "284", + "melee_animation": "284", "projectile": "276", - "attack_level": "41" + "range_animation": "284", + "range_level": "40", + "respawn_delay": "35", + "safespot": null, + "strength_level": "41", + "weakness": "0" }, { - "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", + "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", - "name": "Mountain troll", - "defence_level": "53", + "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, - "lifepoints": "90", - "strength_level": "20", + "strength_level": "41", + "weakness": "0" + }, + { "id": "1106", - "range_level": "1", - "attack_level": "53" - }, - { + "name": "Mountain troll", "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", + "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", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", + "range_level": "1", "safespot": null, - "lifepoints": "90", "strength_level": "20", + "weakness": "7" + }, + { "id": "1107", - "range_level": "1", - "attack_level": "53" - }, - { + "name": "Mountain troll", "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", + "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", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", + "range_level": "1", "safespot": null, - "lifepoints": "90", "strength_level": "20", + "weakness": "7" + }, + { "id": "1108", - "range_level": "1", - "attack_level": "53" - }, - { + "name": "Mountain troll", "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", + "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", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", + "range_level": "1", "safespot": null, - "lifepoints": "90", "strength_level": "20", + "weakness": "7" + }, + { "id": "1109", - "range_level": "1", - "attack_level": "53" - }, - { + "name": "Mountain troll", "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", + "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", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", + "range_level": "1", "safespot": null, - "lifepoints": "90", "strength_level": "20", + "weakness": "7" + }, + { "id": "1110", - "range_level": "1", - "attack_level": "53" - }, - { + "name": "Mountain troll", "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", + "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", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", + "range_level": "1", "safespot": null, - "lifepoints": "90", "strength_level": "20", + "weakness": "7" + }, + { "id": "1111", - "range_level": "1", - "attack_level": "53" - }, - { + "name": "Mountain troll", "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", + "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", - "attack_speed": "5", - "defence_animation": "285", - "weakness": "7", - "magic_animation": "284", - "death_animation": "287", - "name": "Mountain troll", - "defence_level": "53", + "range_level": "1", "safespot": null, - "lifepoints": "90", "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", - "attack_level": "53" + "safespot": null, + "strength_level": "20", + "weakness": "7" + }, + { + "id": "1113", + "name": "Eadgar" + }, + { + "id": "1114", + "name": "Godric" }, { - "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", + "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", - "attack_level": "65" + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "7" }, { - "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", + "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", - "attack_level": "65" + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "7" }, { - "examine": "He's watching the arena.", - "slayer_task": "83", - "range_animation": "0", - "respawn_delay": "60", + "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", - "weakness": "7", + "defence_level": "65", + "lifepoints": "92", "magic_animation": "0", - "name": "Troll spectator", - "defence_level": "47", + "melee_animation": "1259", + "range_animation": "0", + "range_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "67", - "strength_level": "47", + "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", - "attack_level": "47" + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" }, { - "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", + "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", - "attack_level": "47" + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" }, { - "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", + "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", - "attack_level": "47" + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" }, { - "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", + "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", - "attack_level": "47" + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" }, { - "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", + "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", - "attack_level": "47" + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "7" }, { - "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", + "examine": "He's watching the arena.", + "attack_level": "47", + "death_animation": "287", + "defence_animation": "0", "defence_level": "47", - "safespot": null, "lifepoints": "67", - "strength_level": "47", - "id": "1124", + "magic_animation": "0", + "melee_animation": "1259", + "range_animation": "0", "range_level": "1", - "attack_level": "47" + "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.", - "slayer_task": "83", + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "120", + "weakness": "7" }, { - "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", + "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", - "weakness": "7", + "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", - "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", + "examine": "He's guarding the cells.", "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", + "attack_level": "40", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", "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, + "defence_level": "40", "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", + "melee_animation": "1158", "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", - "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", - "range_animation": "0", - "attack_speed": "4", - "poisonous": "true", - "magic_level": "1", "respawn_delay": "60", - "defence_animation": "6227", - "weakness": "7", - "slayer_exp": "90", - "poison_amount": "4", - "magic_animation": "0", - "death_animation": "6228", - "name": "Kalphite Soldier", - "defence_level": "70", "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", - "strength_level": "70", - "id": "1154", - "aggressive": "true", - "bonuses": "0,0,0,0,0,25,25,5,50,50,0,0,0,0,0", + "magic_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "70" + "safespot": null, + "strength_level": "90", + "weakness": "7" }, { - "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", + "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", - "attack_level": "110" + "respawn_delay": "60", + "safespot": null, + "strength_level": "90", + "weakness": "7" }, { - "examine": "I don't think insect repellent will work...", - "slayer_task": "53", - "melee_animation": "6223", - "range_animation": "6223", - "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", + "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, - "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" + "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...", - "slayer_task": "53", + "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", - "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", + "respawn_delay": "38", "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" + "slayer_exp": "40", + "strength_level": "20", + "weakness": "7" }, { - "agg_radius": "64", + "id": "1154", + "name": "Kalphite Soldier", "examine": "I don't think insect repellent will work...", - "slayer_task": "53", + "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", - "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" + "respawn_delay": "45", + "safespot": null, + "strength_level": "300", + "weakness": "10" }, { - "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", + "name": "Kalphite Larva", + "examine": "I don't think insect repellent will work...", "aggressive": "false", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A huge beast", - "melee_animation": "313", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", + "attack_level": "1", + "death_animation": "0", "defence_animation": "0", - "weakness": "9", + "defence_level": "1", + "lifepoints": "10", "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", - "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", - "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" + "safespot": null, + "strength_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" + "id": "1162", + "name": "Timfraku", + "examine": "The imposing Chief of Tai Bwo Wannai." }, { - "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" + "id": "1163", + "name": "Tiadeche", + "examine": "A rather depressed looking fisherman." }, { - "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" + "id": "1164", + "name": "Tiadeche", + "examine": "A rather depressed looking fisherman." }, { - "examine": "It's an NPC.", - "melee_animation": "4921", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "4927", - "slayer_exp": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "1", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "1195", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "50", + "weakness": "9" }, { - "examine": "Eek! A bear cub!", - "slayer_task": "6", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", + "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", - "weakness": "6", + "defence_level": "64", + "lifepoints": "91", "magic_animation": "0", - "name": "Grizzly bear cub", - "defence_level": "21", + "melee_animation": "428", + "projectile": "249", + "range_animation": "426", + "range_level": "72", + "respawn_delay": "60", "safespot": null, - "lifepoints": "30", - "strength_level": "21", - "id": "1196", - "range_level": "1", - "attack_level": "21" + "start_gfx": "250", + "strength_level": "1", + "weakness": "2" }, { - "name": "Grizzly bear cub", - "defence_level": "1", - "safespot": null, - "lifepoints": "35", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "1197", + "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", - "attack_level": "1" + "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.", - "slayer_task": "92", + "attack_level": "33", + "death_animation": "6558", + "defence_animation": "0", + "defence_level": "33", + "lifepoints": "47", + "magic_animation": "0", "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" + "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", - "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" + "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", - "attack_speed": "6", + "range_level": "1", "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" + "weakness": "8" }, { - "examine": "I don't want to get on the wrong side of him.", - "start_gfx": "250", - "combat_style": "1", - "melee_animation": "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", + "name": "Elf warrior", + "examine": "I don't want to get on the wrong side of him.", + "attack_level": "1", "clue_level": "2", - "range_level": "1", + "combat_audio": "425,427,426", + "combat_style": "1", + "death_animation": "836", + "defence_animation": "425", + "defence_level": "1", + "lifepoints": "105", + "melee_animation": "426", "projectile": "249", - "attack_level": "1" + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "start_gfx": "250", + "strength_level": "1" + }, + { + "id": "1202", + "name": "Arianwyn" }, { - "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", + "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, - "lifepoints": "112", - "strength_level": "1", + "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", - "attack_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" }, { - "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...", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Will o' the wisp", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1209", + "name": "Koftik", + "examine": "The cave guide." + }, + { + "id": "1210", + "name": "Kings messenger", + "examine": "One of King Lathas' messengers." + }, + { "id": "1211", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems strangely familiar...", + "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", - "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" + "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", - "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" + "safespot": null, + "strength_level": "4", + "weakness": "8" }, { + "id": "1218", + "name": "Ghoul", "examine": "A scary, man eating ghoul.", - "slayer_task": "37", + "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", - "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" + "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", - "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" + "weakness": "9" }, { - "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", + "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", - "attack_level": "52" + "safespot": null, + "slayer_exp": "50", + "strength_level": "1" + }, + { + "id": "1221", + "name": "Spider" + }, + { + "id": "1222", + "name": "Mist" }, { - "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", + "examine": "It looks very hungry!", + "attack_level": "1", + "clue_level": "1", "defence_level": "1", - "safespot": null, "lifepoints": "50", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1226", + "name": "Tree" }, { - "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", + "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", - "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", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", "range_level": "5", - "attack_level": "0" + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" }, { - "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", + "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", - "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", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", "range_level": "12", - "attack_level": "0" + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "0", + "strength_level": "0", + "weakness": "9" }, { - "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", + "id": "1229", "name": "Ochre Blamish Snail", - "defence_level": "18", - "safespot": null, - "lifepoints": "10", - "strength_level": "0", - "id": "1234", + "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", - "range_level": "7", - "attack_level": "0" - }, - { - "examine": "A bruise blue coloured blamish snail, these types are said to spit acid.", "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", - "attack_speed": "6", - "magic_level": "1", + "range_level": "7", "respawn_delay": "50", - "defence_animation": "0", - "weakness": "9", + "safespot": null, "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", + "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", - "range_level": "15", - "attack_level": "0" - }, - { - "examine": "A Bedabin nomad fighter - a sandy swordsman.", - "melee_animation": "390", - "range_animation": "0", + "combat_style": "1", + "death_animation": "8144", "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" - }, - { - "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", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "1313", - "weakness": "7", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "id": "1265", - "bonuses": "-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75", - "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", - "attack_speed": "5", - "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": "-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75", - "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", + "lifepoints": "12", + "magic_animation": "0", + "magic_level": "1", + "melee_animation": "8143", + "range_animation": "0", + "range_level": "15", + "respawn_delay": "50", "safespot": null, - "lifepoints": "57", - "strength_level": "40", - "id": "1279", + "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", - "range_level": "1", - "attack_level": "40" + "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" }, { - "examine": "A Fremennik hunter.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sigli the Huntsman", - "defence_level": "1", + "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Fremennik merchant.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sigmund The Merchant", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1282", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Fremennik navigator.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Swensen the Navigator", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1283", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Every innkeeper's best friend!", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Manni the Reveller", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1284", + "name": "Bjorn" + }, + { + "id": "1285", + "name": "Eldgrim" + }, + { "id": "1286", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supposedly fixes things around RuneScape.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Council workman", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1287", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Fremennik riddler.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Peer the Seer", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1288", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A Fremennik hero.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Thorvald the Warrior", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_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!", + "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", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Koschei the deathless", - "defence_level": "13", + "range_level": "1", "safespot": null, - "lifepoints": "14", "strength_level": "13", + "weakness": "10" + }, + { "id": "1291", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "He keeps on living!", + "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", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "836", - "name": "Koschei the deathless", - "defence_level": "13", + "range_level": "1", "safespot": null, - "lifepoints": "14", "strength_level": "13", + "weakness": "10" + }, + { "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", + "examine": "He keeps on living!", + "attack_level": "13", + "death_animation": "836", + "defence_animation": "0", "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" + "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", - "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" + "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", - "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" + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { - "death_animation": "1246", + "id": "1321", "name": "Bunny", + "attack_level": "1", + "death_animation": "1246", + "defence_animation": "1244", "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" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1322", + "name": "Gull" + }, + { + "id": "1323", + "name": "Gull" + }, + { + "id": "1324", + "name": "Gull" + }, + { + "id": "1325", + "name": "Gull" }, { - "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", + "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", - "attack_level": "1" + "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", - "combat_audio": "496,498,497", - "attack_speed": "5", - "magic_level": "6", - "respawn_delay": "32", - "defence_animation": "6375", - "weakness": "7", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn Foal", - "defence_level": "6", - "safespot": null, - "lifepoints": "14", - "strength_level": "6", - "id": "1328", "range_level": "1", - "attack_level": "6" + "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", - "combat_audio": "496,498,497", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Black unicorn Foal", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "1329", "range_level": "1", - "attack_level": "12" + "respawn_delay": "20", + "safespot": null, + "strength_level": "18", + "weakness": "7" }, { + "id": "1330", + "name": "Wolf", "examine": "Fearsome predator found only in the Fremennik Province", - "slayer_task": "92", + "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", - "combat_audio": "481,491,490", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", + "safespot": null, "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" + "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" }, { - "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", + "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", - "projectile": "288", - "attack_level": "80" + "safespot": null, + "strength_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" - }, - { + "name": "Dagannoth", "examine": "A horror from the ocean depths...", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", "death_animation": "1342", - "name": "Dagannoth", + "defence_animation": "1340", "defence_level": "1", - "safespot": null, "lifepoints": "70", - "strength_level": "1", - "id": "1340", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", + "melee_animation": "1343", "projectile": "288", - "attack_level": "1" + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { - "examine": "A horror from the ocean depths...", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", - "death_animation": "1342", + "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, - "lifepoints": "120", - "strength_level": "1", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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" - }, - { + "name": "Dagannoth", "examine": "A horror from the ocean depths...", - "melee_animation": "1341", - "respawn_delay": "60", - "defence_animation": "1340", + "aggressive": "true", + "attack_level": "1", + "clue_level": "1", "death_animation": "1342", - "name": "Dagannoth", + "defence_animation": "1340", "defence_level": "1", - "safespot": null, "lifepoints": "120", - "strength_level": "1", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { - "examine": "A horror from the ocean depths...", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "A horror from the ocean depths...", - "name": "Dagannoth", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "attack_level": "1" + "safespot": null, + "strength_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", + "examine": "A horror from the ocean depths...", + "attack_level": "1", "clue_level": "1", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_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.", - "slayer_task": "24", + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "10" }, { - "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", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "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", - "attack_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", - "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" + "safespot": null, + "strength_level": "1" }, { - "death_animation": "836", + "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", - "safespot": null, "lifepoints": "1", "melee_animation": "401", - "strength_level": "1", - "id": "1367", "range_level": "1", "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "safespot": null, + "strength_level": "1" }, { - "death_animation": "836", - "name": "Skraeling", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "melee_animation": "395", - "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", - "attack_level": "1", - "defence_animation": "425" + "safespot": null, + "strength_level": "1" }, { - "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Greengrocer", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1370", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's guarding the throne room.", + "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", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's cutting the wheat.", + "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": "Farmer", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1375", + "name": "Advisor Ghrim" + }, + { + "id": "1376", + "name": "Derrik", + "examine": "Smithy for Sogthorpe." + }, + { "id": "1377", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "No-one would mistake her for a duchess.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Flower Girl", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", + "name": "Flower Girl", + "examine": "No-one would mistake her for a duchess.", "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "He's strong to the finish, because he eats cabbage.", + "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": "Sailor", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_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", + "name": "Rannveig", "attack_level": "1", - "defence_animation": "425" - }, - { "death_animation": "836", - "name": "Valgerd", + "defence_animation": "425", "defence_level": "1", - "safespot": null, "lifepoints": "1", "melee_animation": "422", - "strength_level": "1", + "range_level": "1", + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" + }, + { + "id": "1387", + "name": "Thora" + }, + { "id": "1388", - "range_level": "1", - "respawn_delay": "60", + "name": "Valgerd", "attack_level": "1", - "defence_animation": "425" - }, - { "death_animation": "836", - "name": "Skraeling", + "defence_animation": "425", "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" + "safespot": null, + "strength_level": "1" }, { - "death_animation": "836", + "id": "1389", "name": "Skraeling", + "attack_level": "1", + "death_animation": "836", + "defence_animation": "425", "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" + "safespot": null, + "strength_level": "1" }, { + "id": "1390", + "name": "Broddi", + "attack_level": "1", "death_animation": "836", - "name": "Ragnvald", + "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", - "strength_level": "1", - "id": "1392", "range_level": "1", "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "safespot": null, + "strength_level": "1" }, { - "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Greengrocer", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1394", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's a lumberjack", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Lumberjack Leif", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1395", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's just mining his own business.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Miner Magnus", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1396", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "There's something fishy about him.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fisherman Frodi", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1397", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She's looking a bit weedy.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gardener Gunnhild", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1399", + "name": "Fishing spot" + }, + { + "id": "1400", + "name": "Gull" }, { - "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" - }, - { + "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", - "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" + "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", - "name": "Rooster", + "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", - "strength_level": "1", - "id": "1403", "range_level": "1", "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "5388" + "safespot": null, + "strength_level": "1" }, { - "death_animation": "6092", + "id": "1404", "name": "Rabbit", + "attack_level": "1", + "death_animation": "6092", + "defence_animation": "6091", "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" + "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" }, { - "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "G.L.O. Caranock", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "attack_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" }, { - "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", + "name": "Duke", "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "1383", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "name": "Oipuis", "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "1383", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "name": "Uyoro", "aggressive": "true", + "attack_level": "1", + "death_animation": "1384", + "defence_animation": "1393", + "defence_level": "1", + "lifepoints": "130", + "melee_animation": "1383", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "name": "Ouhai", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1383", - "respawn_delay": "60", - "defence_animation": "1393", + "attack_level": "1", "death_animation": "1384", - "name": "Uodai", + "defence_animation": "1387", "defence_level": "1", - "safespot": null, "lifepoints": "130", - "strength_level": "1", - "id": "1446", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { "melee_animation": "1383", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Padulah", - "defence_level": "1", "safespot": null, - "lifepoints": "133", - "strength_level": "1", - "id": "1447", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "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, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1448", + "name": "Awowogei" + }, + { + "id": "1449", + "name": "Uwogo" + }, + { + "id": "1450", + "name": "Muruwoi" + }, + { "id": "1451", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An adorable little monkey child.", + "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", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Monkey Child", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1452", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks like the Monkey's Uncle.", + "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", - "combat_audio": "629,631,630", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "The Monkey's Uncle", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1453", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She looks like the Monkey's Aunt.", + "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", - "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" + "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.", - "slayer_task": "61", + "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", - "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" + "safespot": null, + "strength_level": "66", + "weakness": "8" }, { + "id": "1456", + "name": "Monkey Archer", "examine": "A bow wielding ninja monkey.", - "slayer_task": "61", + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "43", + "weakness": "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": "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", + "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", - "attack_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.", - "slayer_task": "61", + "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", - "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" + "safespot": null, + "strength_level": "130", + "weakness": "8" }, { + "id": "1460", + "name": "Monkey Guard", "examine": "A huge brutish gorilla armoured with dangerous looking vambraces.", - "slayer_task": "61", + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "63", + "weakness": "8" }, { + "id": "1461", + "name": "Elder Guard", "examine": "A huge brutish gorilla stands here", - "slayer_task": "61", + "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", - "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" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1462", + "name": "Elder Guard" }, { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", "id": "1463", + "name": "Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "name": "Monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", "id": "1464", + "name": "Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "A large and lumbering undead monkey.", - "slayer_task": "61", - "melee_animation": "1383", - "range_animation": "0", - "combat_audio": "629,631,630", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1384", - "name": "Monkey Zombie", - "defence_level": "55", - "safespot": null, - "lifepoints": "78", - "strength_level": "55", "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", - "attack_level": "55" + "respawn_delay": "50", + "safespot": null, + "strength_level": "60", + "weakness": "6" }, { - "melee_animation": "1392", - "combat_audio": "629,631,630", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", - "name": "Monkey Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "90", - "strength_level": "1", "id": "1466", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "1392", - "combat_audio": "629,631,630", - "respawn_delay": "60", - "defence_animation": "1393", - "death_animation": "1384", "name": "Monkey Zombie", - "defence_level": "1", + "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, - "lifepoints": "60", - "strength_level": "1", + "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", - "attack_level": "1" + "respawn_delay": "50", + "safespot": null, + "strength_level": "60" + }, + { + "id": "1468", + "name": "Bonzara" }, { - "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, - "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", - "slayer_task": "7", - "melee_animation": "6761", - "range_animation": "0", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "6762", - "name": "Chompy bird", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "1550", - "range_level": "2", - "attack_level": "1" - }, - { - "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": "449,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": "449,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": "449,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": "449,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": "449,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": "408,410,409", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "attack_speed": "5", - "defence_animation": "9451", - "slayer_exp": "22", - "magic_animation": "1523", - "death_animation": "9450", - "name": "Banshee", - "defence_level": "5", - "safespot": null, - "lifepoints": "22", - "strength_level": "14", - "id": "1612", - "aggressive": "true", - "clue_level": "0", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "An evil death demon.", - "melee_animation": "9487", - "range_animation": "1528", - "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", - "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", - "attack_speed": "5", - "magic_level": "75", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "80", - "id": "1616", - "range_level": "75", - "attack_level": "80" - }, - { - "examine": "The eyes of evil.", - "melee_animation": "260", - "range_animation": "260", - "attack_speed": "5", - "magic_level": "75", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "80", - "id": "1617", - "range_level": "75", - "attack_level": "80" - }, - { - "examine": "The tongue of evil.", - "melee_animation": "9130", - "range_animation": "1552", - "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", - "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", - "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", - "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", - "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", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "He's one big leg short!", - "melee_animation": "9471", - "range_animation": "9471", - "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", - "range_level": "1", - "attack_level": "58" - }, - { - "examine": "The vacuumed face of evil.", - "slayer_task": "28", - "melee_animation": "1557", - "range_animation": "1557", - "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", - "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", - "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", - "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", - "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", - "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" - }, - { - "examine": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "9506", - "range_animation": "9506", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "9508", - "weakness": "7", - "magic_animation": "9506", - "death_animation": "9507", - "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": "9506", - "range_animation": "9506", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "9508", - "weakness": "7", - "magic_animation": "9506", - "death_animation": "9507", - "name": "Rockslug", - "defence_level": "30", - "safespot": null, - "lifepoints": "30", - "strength_level": "30", - "id": "1632", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "38", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "836", - "name": "Tortured soul", - "defence_level": "38", - "safespot": null, - "lifepoints": "85", - "strength_level": "28", - "id": "1698", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "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" - }, - { - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "melee_animation": "1010", - "strength_level": "1", - "id": "1751", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "1012" - }, - { - "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", - "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", - "range_animation": "0", - "attack_speed": "5", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1792", - "name": "Cave slime", - "defence_level": "19", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "1831", - "clue_level": "0", - "range_level": "19", - "attack_level": "1" - }, - { - "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": "A market stall keeper.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Market seller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1866", - "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", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6567", - "name": "Jackal", - "defence_level": "35", - "safespot": null, - "lifepoints": "100", - "strength_level": "35", - "id": "1994", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "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": "6", - "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,0,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,0", - "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", - "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,0,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", - "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": "A rich landlord", - "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": "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" - }, - { - "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" - }, - { - "combat_style": "1", - "melee_animation": "190", - "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" - }, - { - "combat_style": "1", - "melee_animation": "190", - "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", - "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", - "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" - }, - { - "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" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2309", - "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", - "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", - "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", - "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", - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "68", - "id": "2452", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "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", - "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", - "defence_animation": "0", - "slayer_exp": "8", - "magic_animation": "0", - "death_animation": "0", - "name": "Giant bat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "2482", - "range_level": "1", - "attack_level": "1" - }, - { - "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": "469,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", - "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" - }, - { - "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", - "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", - "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", - "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", - "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" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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" - }, - { - "melee_animation": "1312", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2885", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "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", - "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" - }, - { - "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", - "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", - "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": "400,404,403", - "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", - "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", - "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", - "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", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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", - "attack_speed": "4", - "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", - "combat_style": "1", - "melee_animation": "1584", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1585", - "name": "Harpie Bug Swarm", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "3153", - "aggressive": "true", - "clue_level": "1", - "range_level": "45", - "attack_level": "1" - }, - { - "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": "72", - "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", - "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 RuneScape's many citizens", - "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": "One of RuneScape'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 RuneScape'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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "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, - "lifepoints": "10", - "strength_level": "1", - "id": "3322", - "range_level": "1", - "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", - "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", - "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": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fun guy. No wait", - "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" - }, - { - "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" - }, - { - "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": "408,410,409", - "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", - "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 may be a funky cook", - "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" - }, - { - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "24", - "safespot": null, - "lifepoints": "34", - "strength_level": "24", - "id": "3422", - "range_level": "1", - "attack_level": "24" - }, - { - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "melee_animation": "3433", - "strength_level": "1", - "id": "3423", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3434" - }, - { - "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": "408,410,409", - "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", - "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" - }, - { - "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": "498,500,499", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Angry bear", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3645", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a little on the cross side!", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "496,498,497", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6377", - "name": "Angry unicorn", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3646", - "range_level": "1", - "attack_level": "25" - }, - { - "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 RuneScape 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" - }, - { - "name": "Angry unicorn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "496,498,497", - "strength_level": "1", - "id": "3661", - "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": "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" - }, - { - "name": "Angry bear", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "3664", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "melee_animation": "2019", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "2021", - "name": "Vulture", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "3675", - "range_level": "1", - "attack_level": "38" - }, - { - "death_animation": "2026", - "name": "Vulture", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "2025", - "strength_level": "1", - "id": "3676", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "2024" - }, - { - "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", - "attack_speed": "6", - "magic_level": "32", - "defence_animation": "4916", - "weakness": "4", - "slayer_exp": "8", - "magic_animation": "4915", - "death_animation": "4917", - "name": "Giant bat", - "defence_level": "32", - "safespot": null, - "lifepoints": "32", - "strength_level": "12", - "id": "3711", - "aggressive": "true", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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 RuneScape'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", - "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", - "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", - "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": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Bronze Armour", - "defence_level": "8", - "safespot": null, - "lifepoints": "10", - "strength_level": "8", - "id": "4278", - "bonuses": "3,3,3,3,1,3,1,3,3,3,3,8,10,8,3", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Animated iron armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Iron Armour", - "defence_level": "16", - "safespot": null, - "lifepoints": "20", - "strength_level": "16", - "id": "4279", - "bonuses": "6,6,6,6,2,6,6,6,6,6,6,2,25,2,3", - "range_level": "1", - "attack_level": "16" - }, - { - "examine": "Animated steel armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Steel Armour", - "defence_level": "32", - "safespot": null, - "lifepoints": "40", - "strength_level": "32", - "id": "4280", - "bonuses": "10,10,10,10,4,10,10,10,10,10,10,2,22,2,3", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "Animated black armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Black Armour", - "defence_level": "40", - "safespot": null, - "lifepoints": "60", - "strength_level": "40", - "id": "4281", - "bonuses": "15,15,15,15,5,15,6,15,15,16,15,5,27,5,3", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Animated mithril armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Mithril Armour", - "defence_level": "55", - "safespot": null, - "lifepoints": "80", - "strength_level": "55", - "id": "4282", - "bonuses": "20,20,20,20,20,20,8,20,20,20,20,5,31,5,3", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Animated adamant armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Adamant Armour", - "defence_level": "75", - "safespot": null, - "lifepoints": "99", - "strength_level": "65", - "id": "4283", - "bonuses": "25,25,25,25,25,25,12,25,25,25,25,5,35,5,3", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Animated rune armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Rune Armour", - "defence_level": "85", - "safespot": null, - "lifepoints": "118", - "strength_level": "75", - "id": "4284", - "bonuses": "30,30,30,30,30,30,16,30,25,30,30,5,39,5,3", - "range_level": "1", - "attack_level": "75" - }, - { - "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": "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": "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": "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", - "slayer_task": "52", - "melee_animation": "4235", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "4348", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4349", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horrible", - "slayer_task": "52", - "melee_animation": "4235", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "4350", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4351", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4352", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", - "melee_animation": "4234", - "range_animation": "4234", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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 RuneScape 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": "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": "4392", - "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", - "range_level": "1", - "attack_level": "24" - }, - { - "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": "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": "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": "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": "Examine not added", - "slayer_task": "58", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "weakness": "7", - "slayer_exp": "10", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "10", - "safespot": null, - "lifepoints": "11", - "strength_level": "10", - "id": "4404", - "bonuses": "8,8,10,8,8,10,8,8,8,10,8,8,8,8,8", - "clue_level": "0", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Examine not added", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "15", - "safespot": null, - "lifepoints": "17", - "strength_level": "1", - "id": "4405", - "bonuses": "11,11,13,11,11,13,11,11,11,13,11,11,11,11,11", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Examine not added", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "slayer_exp": "22", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "19", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "4406", - "bonuses": "15,15,15,15,15,15,15,15,15,15,15,15,15,15,15", - "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": "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": "Examine not added", - "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": "69", - "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": "Examine not added", - "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": "69", - "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": "Half horse", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Stag", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "4440", - "range_level": "1", - "attack_level": "18" - }, - { - "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", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4527", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4528", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4529", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4530", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4531", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4532", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4533", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "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", - "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": "408,410,409", - "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": "408,410,409", - "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" - }, - { - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "4667", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "408,410,409", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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", - "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", - "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", - "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", - "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", - "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": "400,404,403", - "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": "400,404,403", - "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": "400,404,403", - "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": "400,404,403", - "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": "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": "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" - }, - { - "melee_animation": "4868", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "4869", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "1", - "safespot": null, - "lifepoints": "35", - "strength_level": "1", - "id": "4910", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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 RuneScape is that?", - "melee_animation": "0", - "range_animation": "0", - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "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": "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" - }, - { - "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": "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": "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" - }, - { - "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": "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": "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", - "attack_speed": "6", - "magic_level": "77", - "defence_animation": "1581", - "weakness": "4", - "magic_animation": "1582", - "death_animation": "1580", - "name": "Waterfiend", - "defence_level": "50", - "poison_immune": "true", - "safespot": null, - "lifepoints": "128", - "strength_level": "167", - "id": "5361", - "aggressive": "true", - "range_level": "167", - "projectile": "16", - "attack_level": "70" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", - "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", - "respawn_delay": "60", - "defence_animation": "6013", - "slayer_exp": "52", - "death_animation": "6014", - "name": "Molanisk", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5751", - "clue_level": "1", - "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": "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", - "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 RuneScape'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 RuneScape'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 RuneScape 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": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Terrorbird display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "melee_animation": "6790", - "strength_level": "1", - "id": "6110", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6792" - }, - { - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "melee_animation": "6790", - "strength_level": "1", - "id": "6111", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6792" - }, - { - "examine": "Aww", - "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" - }, - { - "death_animation": "750", - "name": "Duck", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "melee_animation": "747", - "strength_level": "1", - "id": "6113", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3268" - }, - { - "examine": "A messy bird.", - "slayer_task": "7", - "melee_animation": "3467", - "range_animation": "3467", - "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", - "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", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "6189", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "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": "120", - "weakness": "9", - "slayer_exp": "350", - "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,0", - "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, - "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", - "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", - "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", - "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": "120", - "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": "true", - "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": "true", - "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": "120", - "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", - "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": "true", - "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", - "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": "120", - "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,0", - "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", - "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", - "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", - "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", - "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" - }, - { - "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": "496,498,497", - "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": "496,498,497", - "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": "408,410,409", - "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": "408,410,409", - "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": "408,410,409", - "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": "408,410,409", - "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 Runescape", - "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": "449,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": "449,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", - "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", - "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", - "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": "7129", - "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": "The head farmer", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer Blinkin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7131", - "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": "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", - "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", - "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", - "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", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "68", - "id": "2452", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "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 spiny horror from the ocean depths...", - "melee_animation": "1343", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "100", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "60", - "safespot": null, - "lifepoints": "95", - "strength_level": "90", - "id": "2455", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "294", - "attack_level": "90" - }, - { - "examine": "It's an NPC.", - "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "0", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "60", - "safespot": null, - "lifepoints": "70", - "strength_level": "90", - "id": "2456", - "aggressive": "true", - "clue_level": "1", - "range_level": "90", - "projectile": "294", - "attack_level": "90" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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": "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" - }, - { - "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": "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" - }, - { - "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 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" - }, - { - "death_animation": "836", - "name": "Hengel", - "defence_level": "1", - "safespot": null, - "lifepoints": "7", - "melee_animation": "422", - "strength_level": "1", - "id": "2683", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2685", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2686", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2687", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "An ugly smelly creature, with a spear.", - "melee_animation": "163", - "range_animation": "163", - "combat_audio": "469,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "163", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2688", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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", - "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", - "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", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "2694", - "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": "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": "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", - "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" - }, - { - "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", - "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" - }, - { - "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", - "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", - "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": "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" - }, - { - "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" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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" - }, - { - "melee_animation": "1312", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2885", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2862", - "name": "Rock lobster", - "defence_level": "100", - "safespot": null, - "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" - }, - { - "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" - }, - { - "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", - "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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", - "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", - "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 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", - "strength_level": "1", - "id": "7438", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7439", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7440", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "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" - }, - { + "name": "Monkey minder", "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", - "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", - "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", - "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", - "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", - "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": "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": "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", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Known for his light-fingered qualities.", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "425", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "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": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "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", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "She looks happy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Schoolgirl", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "10", - "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": "11", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not very civilised looking.", - "name": "Barbarian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "12", - "clue_level": "0", - "range_level": "1", - "attack_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", - "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": "Not very fashion conscious.", - "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" - }, - { - "examine": "Part of Al-Kharid's elite fighting force.", - "melee_animation": "390", - "range_animation": "390", - "attack_speed": "6", - "defence_animation": "387", - "weakness": "7", - "magic_animation": "390", - "death_animation": "836", - "name": "Al-Kharid warrior", - "defence_level": "5", - "safespot": null, - "lifepoints": "19", - "strength_level": "15", - "id": "18", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Shiny armour!", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Hero", - "defence_level": "54", - "safespot": null, - "lifepoints": "82", - "strength_level": "55", - "id": "21", - "range_level": "1", - "attack_level": "54" - }, - { - "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", - "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": "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", - "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" - }, - { - "examine": "A soldier of the town of Yanille.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "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", - "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" - }, - { - "examine": "Builds ships for a living.", - "melee_animation": "401", - "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" - }, - { - "examine": "Let's not go skinny dipping eh?", - "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" - }, - { - "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": "41", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "43", - "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": "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "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": "46", - "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": "47", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A jungle version of the chicken, but more vicious.", - "slayer_task": "7", - "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" - }, - { - "examine": "A dragon covered in frost and ice.", - "combat_audio": "408,410,409", - "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" - }, - { - "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": "I think this spider has been genetically modified.", - "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": "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" - }, - { - "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", - "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" - }, - { - "examine": "Incey wincey.", - "slayer_task": "76", - "melee_animation": "6249", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "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" - }, - { - "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", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "I think this spider has been genetically modified.", - "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": "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" - }, - { - "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!", - "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": "66", - "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "68", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hideous malformed elf.", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Orc", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "71", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hideously deformed creature.", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Troll", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "72", - "range_level": "1", - "attack_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", - "defence_level": "10", - "safespot": null, - "lifepoints": "24", - "strength_level": "13", - "id": "74", - "aggressive": "true", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "The walking dead.", - "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": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "16", - "safespot": null, - "lifepoints": "30", - "strength_level": "21", - "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "The living dead.", - "slayer_task": "93", - "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" - }, - { - "examine": "A shadowy, barely visible flying entity from some evil place.", - "slayer_task": "5", - "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" - }, - { - "examine": "Oh, it's a camel.", - "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" - }, - { - "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": "81", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "aggressive": "true", - "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 creature made from clay.", - "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" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "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" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "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" - }, - { - "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Horse with a horn.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "496,498,497", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "32", - "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" - }, - { - "examine": "Not man's best friend.", - "slayer_task": "92", - "melee_animation": "6559", - "combat_audio": "481,491,490", - "attack_speed": "4", - "slayer_exp": "69", - "death_animation": "6558", - "name": "Wolf", - "defence_level": "52", - "safespot": null, - "lifepoints": "69", - "strength_level": "55", - "id": "95", - "range_level": "1", - "attack_level": "50" - }, - { - "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" - }, - { - "examine": "A vicious mountain wolf.", - "slayer_task": "92", - "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" - }, - { - "examine": "Bow wow.", - "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" - }, - { - "examine": "He doesn't seem pleased to see me.", - "slayer_task": "27", - "melee_animation": "6562", - "range_animation": "6562", - "attack_speed": "4", - "respawn_delay": "15", - "defence_animation": "6563", - "weakness": "7", - "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" - }, - { - "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", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "16", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "102", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "27", - "magic_animation": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "105", - "aggressive": "true", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "4929", - "name": "Black bear", - "defence_level": "17", - "safespot": null, - "lifepoints": "24", - "strength_level": "17", - "id": "106", - "range_level": "1", - "attack_level": "17" - }, - { - "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": "Tiny", - "slayer_task": "71", - "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" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,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" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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" - }, - { - "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": "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" - }, - { - "examine": "An aggressive humanoid.", - "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" - }, - { - "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" - }, - { - "examine": "Big, ugly, and smelly.", - "slayer_task": "44", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "4", - "magic_level": "1", - "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", - "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 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 mountain dwelling short angry guy.", - "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": "120", - "range_level": "1", - "attack_level": "28" - }, - { - "slayer_exp": "16", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "121", - "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", - "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "122", - "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": "123", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" - }, - { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "attack_speed": "4", - "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", - "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" - }, - { - "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", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "126", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An inhabitant of icy regions.", - "slayer_task": "7", - "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" - }, - { - "examine": "A unicorn with a blackened heart.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "496,498,497", - "attack_speed": "5", - "magic_level": "24", - "defence_animation": "6375", - "weakness": "3", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Black unicorn", - "defence_level": "15", - "safespot": null, - "lifepoints": "28", - "strength_level": "10", - "id": "133", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Aaw", - "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" - }, - { - "examine": "A giant raptor.", - "slayer_task": "7", - "melee_animation": "1010", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "138", - "range_level": "1", - "attack_level": "30" - }, - { - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "melee_animation": "1010", - "strength_level": "1", - "id": "139", - "range_level": "1", - "respawn_delay": "60", "attack_level": "1", - "defence_animation": "1012" - }, - { - "examine": "A servant to Iban.", - "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" - }, - { - "examine": "Must be the pack leader.", - "slayer_task": "92", - "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" - }, - { - "slayer_exp": "34", - "death_animation": "6558", - "name": "Wolf", - "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" - }, - { - "examine": "A rare jungle wolf - specific to the Kharazi jungle.", - "slayer_task": "92", - "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" - }, - { - "examine": "Wow! Scorpions shouldn't grow that big.", - "slayer_task": "71", - "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" - }, - { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "attack_speed": "4", - "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", - "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" - }, - { - "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" - }, - { - "examine": "A sea bird.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cormorant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "147", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A sea bird.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pelican", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "148", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "These look much better in the wild.", - "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": "153", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I love butterflies.", - "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" - }, - { - "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", - "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" - }, - { - "examine": "He can advise on training.", - "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" - }, - { - "examine": "A tree gnome guard.", - "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" - }, - { - "examine": "A tree gnome guard.", - "melee_animation": "192", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome guard", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "164", - "range_level": "1", - "attack_level": "38" - }, - { - "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" - }, - { - "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": "169", - "range_level": "1", - "attack_level": "30" - }, - { - "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": "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", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "A dark-hearted knight.", - "melee_animation": "390", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "178", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "melee_animation": "390", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "388", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "42", - "strength_level": "1", - "id": "179", - "aggressive": "true", - "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": "180", - "aggressive": "true", - "range_level": "1", - "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": "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", - "id": "187", - "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 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 necromancer.", - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Bandit Camp guard.", - "melee_animation": "386", - "range_animation": "0", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard Bandit", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "1", - "id": "196", - "range_level": "20", - "attack_level": "1" - }, - { - "examine": "A mighty warrior!", - "melee_animation": "395", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Gunthor the Brave", - "defence_level": "29", - "safespot": null, - "lifepoints": "41", - "strength_level": "29", - "id": "199", - "range_level": "1", - "attack_level": "29" - }, - { - "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", - "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": "1631", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "1595", - "range_animation": "1595", - "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" - }, - { - "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": "206", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "12" - }, - { - "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": "A former Knight of the Round Table.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Percival", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "212", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks unhappy...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Peasant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "214", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "High Priest of Entrana.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "High Priest", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "216", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a fisherman.", - "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": "219", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He doesn't look very well...", - "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" - }, - { - "examine": "He looks mean and powerful.", - "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" - }, - { - "examine": "A holy man.", - "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" - }, - { - "examine": "A peaceful monk.", - "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" - }, - { - "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": "224", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Forester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "231", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He isn't very friendly.", - "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" - }, - { - "examine": "A terrifying spirit.", - "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" - }, - { - "examine": "Specialist meat transporter.", - "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" - }, - { - "death_animation": "6490", - "name": "Sir Mordred", - "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" - }, - { - "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.", - "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": "253", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "395", - "range_animation": "0", - "attack_speed": "6", - "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", - "id": "255", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "His armour indicates he's a Khazard Guard.", - "melee_animation": "395", - "range_animation": "0", - "attack_speed": "6", - "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", - "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", - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An official of Ardougne.", - "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" - }, - { - "examine": "A serious-looking doctor.", - "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" - }, - { - "examine": "A farmer who's seen happier times.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer Brumty", - "defence_level": "1", - "safespot": null, - "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", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "296", - "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": "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", - "death_animation": "0", - "name": "Hetty", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "307", - "aggressive": "true", - "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", - "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", - "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", - "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": "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": "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" - }, - { - "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", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "6", - "magic_level": "27", - "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", - "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 pirate.", - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Customs officer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "380", - "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": "382", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not very civilised looking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barbarian guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "384", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks vicious!", - "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", - "safespot": null, - "lifepoints": "10", - "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", - "combat_audio": "3611,3612,3610", - "strength_level": "1", - "id": "387", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with a shave...", - "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" - }, - { - "examine": "A brightly coloured game bird.", - "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" - }, - { - "examine": "A brightly coloured game bird.", - "melee_animation": "2371", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "2373", - "name": "River troll", - "defence_level": "3", - "safespot": null, - "lifepoints": "50", - "strength_level": "3", - "id": "392", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "Beefy.", - "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": "397", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jungle forester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "402", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Insects buzzing around.", - "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" - }, - { - "examine": "An annoying flappy thing.", - "slayer_task": "5", - "melee_animation": "4915", - "range_animation": "4915", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "30", - "defence_animation": "4916", - "weakness": "4", - "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" - }, - { - "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": "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": "17", - "safespot": null, - "lifepoints": "50", - "strength_level": "17", - "id": "438", - "range_level": "1", - "attack_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": "32", - "safespot": null, - "lifepoints": "55", - "strength_level": "32", - "id": "439", - "range_level": "1", - "attack_level": "32" - }, - { - "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": "48", - "safespot": null, - "lifepoints": "60", - "strength_level": "48", - "id": "440", - "range_level": "1", - "attack_level": "48" - }, - { - "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": "65", - "safespot": null, - "lifepoints": "86", - "strength_level": "65", - "id": "441", - "range_level": "1", - "attack_level": "65" - }, - { - "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, - "lifepoints": "120", - "strength_level": "100", - "id": "442", - "range_level": "1", - "attack_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", - "defence_level": "130", - "safespot": null, - "lifepoints": "170", - "strength_level": "130", - "id": "443", - "range_level": "1", - "attack_level": "130" - }, - { - "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": "444", - "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": "445", - "clue_level": "0", - "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": "2", - "safespot": null, - "lifepoints": "5", - "strength_level": "5", - "id": "446", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "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", - "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": "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "A large well built farmer.", - "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" - }, - { - "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", - "range_level": "1", - "attack_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", - "range_level": "1", - "attack_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": "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "It's one of General Khazard's commanders.", - "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" - }, - { - "examine": "It's a tree gnome trooper.", - "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" - }, - { - "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" - }, - { - "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 2", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "482", - "range_level": "1", - "attack_level": "1" - }, - { - "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 3", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "483", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a young tree gnome.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Local Gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "485", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I guess he wants to be more than just the muscle.", - "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" - }, - { - "examine": "He doesn't look like he'd trust his own mother.", - "slayer_task": "38", - "melee_animation": "6199", - "range_animation": "0", - "combat_audio": "469,472,471", - "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" - }, - { - "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" - }, - { - "examine": "The essence of evil.", - "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" - }, - { - "examine": "A vicious", - "slayer_task": "71", - "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": "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" - }, - { - "examine": "A minion of Rashiliyia.", - "slayer_task": "75", - "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" - }, - { - "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": "504", - "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": "505", - "aggressive": "true", - "range_level": "1", - "attack_level": "33" - }, - { - "melee_animation": "5571", - "respawn_delay": "60", - "defence_animation": "5574", - "death_animation": "5575", - "name": "Nazastarool", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "507", - "aggressive": "true", - "range_level": "1", - "attack_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", - "defence_level": "1", - "safespot": null, - "lifepoints": "110", - "strength_level": "1", - "id": "509", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bob", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "519", - "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": "Shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "520", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Helps sell stuff.", - "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": "521", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes people spending money.", - "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": "522", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes helping sell stuff.", - "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": "523", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A product of a consumerist society.", - "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": "524", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes you more", - "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" - }, - { - "examine": "A product of a consumerist society.", - "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" - }, - { - "examine": "Likes you more", - "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": "527", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has an interesting assortment of items for sale.", - "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": "530", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works on commission.", - "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": "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": "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "552", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "For the interesting clothing items you just can't find elsewhere.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fancy-dress shop owner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "554", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has a fine moustache.", - "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": "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "561", - "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-maker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "562", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks strange and mysterious.", - "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": "567", - "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": "569", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Seems very well-off.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gem merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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" - }, - { - "examine": "Seems very well-off.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silk merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He runs a Mining store.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Drogo dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "579", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder if he wants to buy my junk?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "582", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "If crafting's your thing, he's your man!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rommik", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems to sell tea.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tea seller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "595", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A pizza expert; in both making and eating.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fat Tony", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "596", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Anyone fancy a trim?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hairdresser", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "598", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Indentured servant of a Knight.", - "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" - }, - { - "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": "609", - "range_level": "1", - "attack_level": "26" - }, - { - "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": "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": "613", - "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": "Doug Deeping", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "614", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A student busy studying the digsite.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Student", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "617", - "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": "618", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on archaeology.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Archaeological expert", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "619", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A specialist in panning for gold.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Panning guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "620", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome baller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "621", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome winger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "633", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps the game fair.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome ball referee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "635", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dealer in potions.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Apothecary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "638", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A religious man... And occasional drunk.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Father Lawrence", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "640", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks down on his luck.", - "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" - }, - { - "examine": "Looks kind of obsessive...", - "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" - }, - { - "examine": "Looks kind of shifty...", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "498,500,499", - "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" - }, - { - "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", - "attack_level": "1", - "prj_height": "3" - }, - { - "examine": "Guardian of the dramen tree.", - "melee_animation": "5532", - "range_animation": "0", - "attack_speed": "5", - "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" - }, - { - "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.", - "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": "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "658", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He likes to paaaarty!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Party Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "659", - "range_level": "1", - "attack_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" - }, - { - "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": "An important looking gnome.", - "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" - }, - { - "examine": "The boss!", - "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" - }, - { - "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", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The keeper of the gates to the ranging guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ranging Guild Doorman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "679", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert leatherworker.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Leatherworker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "680", - "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": "Held vampyre juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "681", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Rangers armour.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Armour salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "682", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Archery equipment.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bow and Arrow salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "683", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tower keeper and competition judge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tower Advisor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "684", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "aggressive": "true", - "range_level": "10", - "attack_level": "7" - }, - { - "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", - "aggressive": "true", - "range_level": "20", - "attack_level": "15" - }, - { - "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", - "aggressive": "true", - "range_level": "30", - "attack_level": "22" - }, - { - "examine": "Defender of the west 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": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "30", - "id": "691", - "aggressive": "true", - "range_level": "40", - "attack_level": "30" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ticket Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "694", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Harlan, ready to teach swordplay.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Melee tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "705", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old wizard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard Mizgog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "706", - "range_level": "1", - "attack_level": "1" - }, - { - "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, - "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, - "lifepoints": "8", - "strength_level": "1", - "id": "709", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "0", - "examine": "It's an NPC.", - "name": "Edmond", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "714", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "731", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "732", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "735", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "737", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "739", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Duke Horacio of Lumbridge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Duke Horacio", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "741", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A badly-behaved goblin.", - "slayer_task": "38", - "melee_animation": "6184", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6204", - "name": "Wormbrain", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "745", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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": "Enraged barbarian spirit", - "defence_level": "76", - "safespot": null, - "lifepoints": "108", - "strength_level": "76", - "id": "750", - "aggressive": "true", - "range_level": "1", - "attack_level": "76" - }, - { - "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": "Berserk barbarian spirit", - "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.", - "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" - }, - { - "examine": "Stop looking and run!", - "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" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "combat_audio": "703,705,704", - "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", - "id": "799", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A holy man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Abbot Langley", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "801", - "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 Jered", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "802", - "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": "803", - "range_level": "1", - "attack_level": "12" - }, - { - "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", - "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": "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": "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", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "827", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "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", - "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", - "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" - }, - { - "examine": "He patrols the Shantay Pass.", - "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" - }, - { - "examine": "A vicious desert wolf.", - "slayer_task": "92", - "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" - }, - { - "death_animation": "9674", - "name": "Ugthanki", - "defence_level": "1", - "safespot": null, - "lifepoints": "46", - "strength_level": "1", - "attack_speed": "5", - "id": "840", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "He looks busy attending to his cart.", - "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" - }, - { - "examine": "This pickaxe has been possessed by a dwarf ancestor spirit.", - "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" - }, - { - "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", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Head chef", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "847", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The famous tree gnome chef.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Aluft Gianne snr.", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "850", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can serve you gnome food.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome Waiter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "851", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tough-looking.", - "slayer_task": "64", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre chieftain", - "defence_level": "58", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", - "id": "852", - "range_level": "1", - "attack_level": "58" - }, - { - "name": "Gorad", - "defence_level": "1", - "safespot": null, - "lifepoints": "81", - "strength_level": "1", - "id": "856", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "859", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "defence_animation": "0", - "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": "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", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", - "id": "870", - "range_level": "1", - "attack_level": "58" - }, - { - "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", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ogre trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "875", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tries to keep the peace.", - "melee_animation": "386", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower guard", - "defence_level": "17", - "safespot": null, - "lifepoints": "97", - "strength_level": "17", - "id": "877", - "range_level": "1", - "attack_level": "17" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "67", - "name": "Delrith", - "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" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ceril Carnillean", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "885", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "887", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Clivet", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "melee_animation": "811", - "strength_level": "1", - "id": "893", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "A member of the Hazeel cult.", - "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" - }, - { - "examine": "Children are just like real people...just smaller.", - "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" - }, - { - "examine": "He hasn't seen much sun lately.", - "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" - }, - { - "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": "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 RuneScape.", - "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": "An official representative from the First National Bank of RuneScape.", - "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 RuneScape.", - "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": "496,498,497", - "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": "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" - }, - { - "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": "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 green skinned croaker", - "melee_animation": "0", - "range_animation": "0", - "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", - "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", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "32", - "safespot": null, - "movement_radius": "4", - "lifepoints": "45", - "strength_level": "32", - "id": "1020", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "An air elemental.", - "melee_animation": "1040", - "range_animation": "0", - "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", - "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": "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.", - "name": "Soldier", - "defence_level": "44", - "safespot": null, - "lifepoints": "55", - "combat_audio": "511,513,512", - "strength_level": "44", - "id": "1065", - "magic_level": "1", - "range_level": "1", - "attack_level": "44" - }, - { - "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": "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": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "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": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "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": "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": "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" - }, - { - "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": "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": "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", - "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", - "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": "498,500,499", - "respawn_delay": "60", - "defence_animation": "4927", - "slayer_exp": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "1", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "1195", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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": "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 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 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" - }, - { - "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", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "1313", - "weakness": "7", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "id": "1265", - "bonuses": "-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75", - "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", - "attack_speed": "5", - "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": "-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75", - "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": "Supposedly fixes things around RuneScape.", - "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": "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": "496,498,497", - "attack_speed": "5", - "magic_level": "6", - "respawn_delay": "32", - "defence_animation": "6375", - "weakness": "7", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn Foal", - "defence_level": "6", - "safespot": null, - "lifepoints": "14", - "strength_level": "6", - "id": "1328", - "range_level": "1", - "attack_level": "6" - }, - { - "examine": "Cute but evil.", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "496,498,497", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Black unicorn Foal", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "1329", - "range_level": "1", - "attack_level": "12" - }, - { - "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": "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 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" - }, - { - "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": "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" - }, - { - "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, - "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" - }, - { - "examine": "A large boisterous bird", - "slayer_task": "7", - "melee_animation": "6761", - "range_animation": "0", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "6762", - "name": "Chompy bird", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "1550", - "range_level": "2", - "attack_level": "1" - }, - { - "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": "449,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": "449,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": "449,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": "449,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": "449,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": "408,410,409", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "Flies like a rock.", - "melee_animation": "9454", - "range_animation": "1516", - "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", - "attack_speed": "5", - "defence_animation": "9451", - "slayer_exp": "22", - "magic_animation": "1523", - "death_animation": "9450", - "name": "Banshee", - "defence_level": "5", - "safespot": null, - "lifepoints": "22", - "strength_level": "14", - "id": "1612", - "aggressive": "true", - "clue_level": "0", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "An evil death demon.", - "melee_animation": "9487", - "range_animation": "1528", - "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", - "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", - "attack_speed": "5", - "magic_level": "75", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "80", - "id": "1616", - "range_level": "75", - "attack_level": "80" - }, - { - "examine": "The eyes of evil.", - "melee_animation": "260", - "range_animation": "260", - "attack_speed": "5", - "magic_level": "75", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "80", - "id": "1617", - "range_level": "75", - "attack_level": "80" - }, - { - "examine": "The tongue of evil.", - "melee_animation": "9130", - "range_animation": "1552", - "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", - "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", - "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", - "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": "The vacuumed face of evil.", - "slayer_task": "28", - "melee_animation": "1557", - "range_animation": "1557", - "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": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "9506", - "range_animation": "9506", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "9508", - "weakness": "7", - "magic_animation": "9506", - "death_animation": "9507", - "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": "9506", - "range_animation": "9506", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "9508", - "weakness": "7", - "magic_animation": "9506", - "death_animation": "9507", - "name": "Rockslug", - "defence_level": "30", - "safespot": null, - "lifepoints": "30", - "strength_level": "30", - "id": "1632", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "Now THAT'S handy.", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "9125", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "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" - }, - { - "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 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": "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": "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": "38", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "836", - "name": "Tortured soul", - "defence_level": "38", - "safespot": null, - "lifepoints": "85", - "strength_level": "28", - "id": "1698", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "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": "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" - }, - { - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "melee_animation": "1010", - "strength_level": "1", - "id": "1751", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "1012" - }, - { - "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", - "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", - "range_animation": "0", - "attack_speed": "5", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1792", - "name": "Cave slime", - "defence_level": "19", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "1831", - "clue_level": "0", - "range_level": "19", - "attack_level": "1" - }, - { - "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'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": "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 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 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": "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" - }, - { - "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", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6567", - "name": "Jackal", - "defence_level": "35", - "safespot": null, - "lifepoints": "100", - "strength_level": "35", - "id": "1994", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "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": "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", - "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": "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": "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": "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": "A rich landlord", - "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": "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 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" - }, - { - "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" - }, - { - "combat_style": "1", - "melee_animation": "190", - "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" - }, - { - "combat_style": "1", - "melee_animation": "190", - "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", - "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", - "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": "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" - }, - { - "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" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2309", - "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": "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": "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", - "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", - "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", - "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", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "68", - "id": "2452", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "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", - "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": "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": "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" - }, - { - "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 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" - }, - { - "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" - }, - { - "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": "469,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", - "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" - }, - { - "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": "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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": "From a darker dimension.", - "slayer_task": "25", - "melee_animation": "2731", - "range_animation": "2731", - "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", - "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": "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" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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" - }, - { - "melee_animation": "1312", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2885", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2862", - "name": "Rock lobster", - "defence_level": "100", - "safespot": null, - "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" - }, - { - "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" - }, - { - "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", - "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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", - "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", - "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", - "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", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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", - "attack_speed": "4", - "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": "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", - "combat_style": "1", - "melee_animation": "1584", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1585", - "name": "Harpie Bug Swarm", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "3153", - "aggressive": "true", - "clue_level": "1", - "range_level": "45", - "attack_level": "1" - }, - { - "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" - }, - { - "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 RuneScape's many citizens", - "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": "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "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": "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, - "lifepoints": "10", - "strength_level": "1", - "id": "3322", - "range_level": "1", - "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", - "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", - "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": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fun guy. No wait", - "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" - }, - { - "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" - }, - { - "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": "408,410,409", - "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", - "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 may be a funky cook", - "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" - }, - { - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "24", - "safespot": null, - "lifepoints": "34", - "strength_level": "24", - "id": "3422", - "range_level": "1", - "attack_level": "24" - }, - { - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "melee_animation": "3433", - "strength_level": "1", - "id": "3423", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3434" - }, - { - "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" - }, - { - "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": "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": "408,410,409", - "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": "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" - }, - { - "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": "498,500,499", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Angry bear", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3645", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a little on the cross side!", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "496,498,497", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6377", - "name": "Angry unicorn", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3646", - "range_level": "1", - "attack_level": "25" - }, - { - "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 RuneScape 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" - }, - { - "name": "Angry unicorn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "496,498,497", - "strength_level": "1", - "id": "3661", - "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": "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" - }, - { - "name": "Angry bear", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "3664", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "melee_animation": "2019", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "2021", - "name": "Vulture", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "3675", - "range_level": "1", - "attack_level": "38" - }, - { - "death_animation": "2026", - "name": "Vulture", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "2025", - "strength_level": "1", - "id": "3676", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "2024" - }, - { - "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" - }, - { - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "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": "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" - }, - { - "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": "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" - }, - { - "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" - }, - { - "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", - "strength_level": "1", - "id": "4228", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A denizen of the Abyss!", - "slayer_task": "1", - "melee_animation": "1537", - "range_animation": "1537", - "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" - }, - { - "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": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Bronze Armour", - "defence_level": "8", - "safespot": null, - "lifepoints": "10", - "strength_level": "8", - "id": "4278", - "bonuses": "3,3,3,3,1,3,1,3,3,3,3,8,10,8,3", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Animated iron armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Iron Armour", - "defence_level": "16", - "safespot": null, - "lifepoints": "20", - "strength_level": "16", - "id": "4279", - "bonuses": "6,6,6,6,2,6,6,6,6,6,6,2,25,2,3", - "range_level": "1", - "attack_level": "16" - }, - { - "examine": "Animated steel armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Steel Armour", - "defence_level": "32", - "safespot": null, - "lifepoints": "40", - "strength_level": "32", - "id": "4280", - "bonuses": "10,10,10,10,4,10,10,10,10,10,10,2,22,2,3", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "Animated black armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Black Armour", - "defence_level": "40", - "safespot": null, - "lifepoints": "60", - "strength_level": "40", - "id": "4281", - "bonuses": "15,15,15,15,5,15,6,15,15,16,15,5,27,5,3", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Animated mithril armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Mithril Armour", - "defence_level": "55", - "safespot": null, - "lifepoints": "80", - "strength_level": "55", - "id": "4282", - "bonuses": "20,20,20,20,20,20,8,20,20,20,20,5,31,5,3", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Animated adamant armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Adamant Armour", - "defence_level": "75", - "safespot": null, - "lifepoints": "99", - "strength_level": "65", - "id": "4283", - "bonuses": "25,25,25,25,25,25,12,25,25,25,25,5,35,5,3", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Animated rune armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Rune Armour", - "defence_level": "85", - "safespot": null, - "lifepoints": "118", - "strength_level": "75", - "id": "4284", - "bonuses": "30,30,30,30,30,30,16,30,25,30,30,5,39,5,3", - "range_level": "1", - "attack_level": "75" - }, - { - "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": "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": "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": "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" - }, - { - "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", - "slayer_task": "52", - "melee_animation": "4235", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "4348", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4349", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horrible", - "slayer_task": "52", - "melee_animation": "4235", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "4350", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4351", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4352", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", - "melee_animation": "4234", - "range_animation": "4234", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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" - }, - { - "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 RuneScape 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" - }, - { - "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": "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": "4392", - "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", - "range_level": "1", - "attack_level": "24" - }, - { - "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": "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": "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": "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": "Examine not added", - "slayer_task": "58", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "weakness": "7", - "slayer_exp": "10", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "10", - "safespot": null, - "lifepoints": "11", - "strength_level": "10", - "id": "4404", - "bonuses": "8,8,10,8,8,10,8,8,8,10,8,8,8,8,8", - "clue_level": "0", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Examine not added", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "15", - "safespot": null, - "lifepoints": "17", - "strength_level": "1", - "id": "4405", - "bonuses": "11,11,13,11,11,13,11,11,11,13,11,11,11,11,11", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Examine not added", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "slayer_exp": "22", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "19", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "4406", - "bonuses": "15,15,15,15,15,15,15,15,15,15,15,15,15,15,15", - "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": "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": "Examine not added", - "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": "69", - "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": "Examine not added", - "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": "69", - "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.", - "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": "4415", - "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": "Half horse", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Stag", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "4440", - "range_level": "1", - "attack_level": "18" - }, - { - "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": "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": "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", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4527", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4528", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4529", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4530", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4531", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4532", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4533", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "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", - "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" - }, - { - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "4667", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "4668", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "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": "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": "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": "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": "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" - }, - { - "melee_animation": "4868", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "4869", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "1", - "safespot": null, - "lifepoints": "35", - "strength_level": "1", - "id": "4910", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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": "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": "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 RuneScape is that?", - "melee_animation": "0", - "range_animation": "0", - "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": "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "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": "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" - }, - { - "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": "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": "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" - }, - { - "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": "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": "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": "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", - "attack_speed": "6", - "magic_level": "77", - "defence_animation": "1581", - "weakness": "4", - "magic_animation": "1582", - "death_animation": "1580", - "name": "Waterfiend", - "defence_level": "50", - "poison_immune": "true", - "safespot": null, - "lifepoints": "128", - "strength_level": "167", - "id": "5361", - "aggressive": "true", - "range_level": "167", - "projectile": "16", - "attack_level": "70" - }, - { - "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" - }, - { - "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": "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", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "5209", - "name": "Ice Troll King", - "defence_level": "80", - "safespot": null, - "lifepoints": "150", - "strength_level": "65", - "id": "5472", - "aggressive": "true", - "bonuses": "60,60,60,0,0,45,45,45,2000,2000,0,60,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "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" - }, - { - "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": "Jatizso's Chancellor.", - "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" - }, - { - "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": "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": "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 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": "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 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": "Grundt", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5501", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Burgher's protectors.", - "melee_animation": "395", - "range_animation": "0", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "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": "395", - "range_animation": "0", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "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": "395", - "range_animation": "0", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "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", - "attack_speed": "5", - "defence_animation": "5783", - "weakness": "7", - "magic_animation": "5782", - "death_animation": "5784", - "name": "Yak", - "defence_level": "30", - "safespot": null, - "lifepoints": "50", - "strength_level": "7", - "id": "5529", - "range_level": "1", - "attack_level": "3" - }, - { - "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": "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", - "respawn_delay": "60", - "defence_animation": "6013", - "slayer_exp": "52", - "death_animation": "6014", - "name": "Molanisk", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5751", - "clue_level": "1", - "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": "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": "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": "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", - "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": "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 RuneScape 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 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": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Terrorbird display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "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": "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "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": "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" - }, - { - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "melee_animation": "6790", - "strength_level": "1", - "id": "6110", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6792" - }, - { - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "melee_animation": "6790", - "strength_level": "1", - "id": "6111", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6792" - }, - { - "examine": "Aww", - "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" - }, - { - "death_animation": "750", - "name": "Duck", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "melee_animation": "747", - "strength_level": "1", - "id": "6113", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3268" - }, - { - "examine": "A messy bird.", - "slayer_task": "7", - "melee_animation": "3467", - "range_animation": "3467", - "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", - "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": "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", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "6189", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "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" - }, - { - "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,0", - "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" - }, - { - "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": "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, - "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", - "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", - "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", - "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" - }, - { - "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": "true", - "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": "true", - "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": "The bane of darkness.", - "melee_animation": "6376", - "range_animation": "6376", - "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": "true", - "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", - "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" - }, - { - "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 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,0", - "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": "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 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", - "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" - }, - { - "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" - }, - { - "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": "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" - }, - { - "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", - "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", - "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", - "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" - }, - { - "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": "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" - }, - { - "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": "496,498,497", - "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": "496,498,497", - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "408,410,409", - "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": "408,410,409", - "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": "408,410,409", - "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": "408,410,409", - "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" - }, - { - "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" - }, - { - "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 Runescape", - "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" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,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": "449,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", - "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", - "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": "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", - "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": "7129", - "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": "The head farmer", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer Blinkin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7131", - "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": "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", - "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", - "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", - "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", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "68", - "id": "2452", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "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 spiny horror from the ocean depths...", - "melee_animation": "1343", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "100", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "60", - "safespot": null, - "lifepoints": "95", - "strength_level": "90", - "id": "2455", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "294", - "attack_level": "90" - }, - { - "examine": "It's an NPC.", - "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "0", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "60", - "safespot": null, - "lifepoints": "70", - "strength_level": "90", - "id": "2456", - "aggressive": "true", - "clue_level": "1", - "range_level": "90", - "projectile": "294", - "attack_level": "90" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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": "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": "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" - }, - { - "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": "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": "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" - }, - { - "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 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" - }, - { - "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" - }, - { - "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" - }, - { - "death_animation": "836", - "name": "Hengel", - "defence_level": "1", - "safespot": null, - "lifepoints": "7", - "melee_animation": "422", - "strength_level": "1", - "id": "2683", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2685", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2686", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2687", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "An ugly smelly creature, with a spear.", - "melee_animation": "163", - "range_animation": "163", - "combat_audio": "469,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "163", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2688", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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", - "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", - "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", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "2694", - "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": "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": "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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": "From a darker dimension.", - "slayer_task": "25", - "melee_animation": "2731", - "range_animation": "2731", - "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", - "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": "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": "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" - }, - { - "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" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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" - }, - { - "melee_animation": "1312", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2885", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2862", - "name": "Rock lobster", - "defence_level": "100", - "safespot": null, - "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" - }, - { - "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" - }, - { - "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", - "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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", - "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", - "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 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": "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": "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": "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": "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": "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": "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": "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": "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" - }, - { - "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": "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", - "strength_level": "1", - "id": "7438", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7439", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7440", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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": "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": "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 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", - "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", - "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", - "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", - "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", - "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", - "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" - }, - { - "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": "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": "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": "One of the Burgher's Protectors", - "death_animation": "836", - "name": "Honour Guard", - "melee_animation": "395", - "id": "5515", - "defence_animation": "404" - }, - { - "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": "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", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Known for his light-fingered qualities.", - "melee_animation": "284", - "range_animation": "0", - "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": "284", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "285", - "weakness": "9", - "magic_animation": "0", - "death_animation": "287", - "name": "Guard", - "defence_level": "13", - "safespot": null, - "lifepoints": "18", - "strength_level": "13", - "id": "9", - "clue_level": "1", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "She looks happy.", - "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" - }, - { - "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": "11", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not very civilised looking.", - "name": "Barbarian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "12", - "clue_level": "0", - "range_level": "1", - "attack_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", - "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": "Not very fashion conscious.", - "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" - }, - { - "examine": "Part of Al-Kharid's elite fighting force.", - "melee_animation": "390", - "range_animation": "390", - "attack_speed": "6", - "defence_animation": "387", - "weakness": "7", - "magic_animation": "390", - "death_animation": "836", - "name": "Al-Kharid warrior", - "defence_level": "5", - "safespot": null, - "lifepoints": "19", - "strength_level": "15", - "id": "18", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Shiny armour!", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Hero", - "defence_level": "54", - "safespot": null, - "lifepoints": "82", - "strength_level": "55", - "id": "21", - "range_level": "1", - "attack_level": "54" - }, - { - "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", - "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": "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", - "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" - }, - { - "examine": "A soldier of the town of Yanille.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "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", - "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" - }, - { - "examine": "Builds ships for a living.", - "melee_animation": "401", - "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" - }, - { - "examine": "Let's not go skinny dipping eh?", - "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" - }, - { - "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": "41", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "43", - "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": "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "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": "46", - "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": "47", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A jungle version of the chicken, but more vicious.", - "slayer_task": "7", - "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" - }, - { - "examine": "A dragon covered in frost and ice.", - "combat_audio": "408,410,409", - "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" - }, - { - "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": "I think this spider has been genetically modified.", - "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": "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" - }, - { - "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", - "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" - }, - { - "examine": "Incey wincey.", - "slayer_task": "76", - "melee_animation": "6249", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "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" - }, - { - "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", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "I think this spider has been genetically modified.", - "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": "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" - }, - { - "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!", - "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": "66", - "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "68", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hideous malformed elf.", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Orc", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "71", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hideously deformed creature.", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Troll", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "1", - "id": "72", - "range_level": "1", - "attack_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", - "defence_level": "10", - "safespot": null, - "lifepoints": "24", - "strength_level": "13", - "id": "74", - "aggressive": "true", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "The walking dead.", - "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": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "16", - "safespot": null, - "lifepoints": "30", - "strength_level": "21", - "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "The living dead.", - "slayer_task": "93", - "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" - }, - { - "examine": "A shadowy, barely visible flying entity from some evil place.", - "slayer_task": "5", - "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" - }, - { - "examine": "Oh, it's a camel.", "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" - }, - { - "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": "81", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "aggressive": "true", - "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 creature made from clay.", - "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" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "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" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "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" - }, - { - "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "10" + "strength_level": "1" }, { - "examine": "Horse with a horn.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "496,498,497", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "32", - "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" + "id": "1470", + "name": "Foreman" }, { - "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", + "id": "1471", "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": "Not man's best friend.", - "slayer_task": "92", - "melee_animation": "6559", - "combat_audio": "481,491,490", - "attack_speed": "4", - "slayer_exp": "69", - "death_animation": "6558", - "name": "Wolf", - "defence_level": "52", - "safespot": null, - "lifepoints": "69", - "strength_level": "55", - "id": "95", - "range_level": "1", - "attack_level": "50" - }, - { - "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" - }, - { - "examine": "A vicious mountain wolf.", - "slayer_task": "92", - "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" - }, - { - "examine": "Bow wow.", - "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" - }, - { - "examine": "He doesn't seem pleased to see me.", - "slayer_task": "27", - "melee_animation": "6562", - "range_animation": "6562", - "attack_speed": "4", - "respawn_delay": "15", - "defence_animation": "6563", - "weakness": "7", - "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" - }, - { - "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", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "16", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "102", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "27", - "magic_animation": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "105", - "aggressive": "true", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "4929", - "name": "Black bear", - "defence_level": "17", - "safespot": null, - "lifepoints": "24", - "strength_level": "17", - "id": "106", - "range_level": "1", - "attack_level": "17" - }, - { - "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": "Tiny", - "slayer_task": "71", - "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" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,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" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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" - }, - { - "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": "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" - }, - { - "examine": "An aggressive humanoid.", - "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" - }, - { - "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" - }, - { - "examine": "Big, ugly, and smelly.", - "slayer_task": "44", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "4", - "magic_level": "1", - "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", - "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 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 mountain dwelling short angry guy.", - "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": "120", - "range_level": "1", - "attack_level": "28" - }, - { - "slayer_exp": "16", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "121", - "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", - "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "122", - "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": "123", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" - }, - { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "attack_speed": "4", - "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", - "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" - }, - { - "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", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "126", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An inhabitant of icy regions.", - "slayer_task": "7", - "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" - }, - { - "examine": "A unicorn with a blackened heart.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "496,498,497", - "attack_speed": "5", - "magic_level": "24", - "defence_animation": "6375", - "weakness": "3", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Black unicorn", - "defence_level": "15", - "safespot": null, - "lifepoints": "28", - "strength_level": "10", - "id": "133", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Aaw", - "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" - }, - { - "examine": "A giant raptor.", - "slayer_task": "7", - "melee_animation": "1010", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "138", - "range_level": "1", - "attack_level": "30" - }, - { - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "melee_animation": "1010", - "strength_level": "1", - "id": "139", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "1012" - }, - { - "examine": "A servant to Iban.", - "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" - }, - { - "examine": "Must be the pack leader.", - "slayer_task": "92", - "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" - }, - { - "slayer_exp": "34", - "death_animation": "6558", - "name": "Wolf", - "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" - }, - { - "examine": "A rare jungle wolf - specific to the Kharazi jungle.", - "slayer_task": "92", - "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" - }, - { - "examine": "Wow! Scorpions shouldn't grow that big.", - "slayer_task": "71", - "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" - }, - { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "attack_speed": "4", - "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", - "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" - }, - { - "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" - }, - { - "examine": "A sea bird.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cormorant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "147", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A sea bird.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pelican", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "148", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "These look much better in the wild.", - "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": "153", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I love butterflies.", - "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" - }, - { - "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", - "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" - }, - { - "examine": "He can advise on training.", - "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" - }, - { - "examine": "A tree gnome guard.", - "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" - }, - { - "examine": "A tree gnome guard.", - "melee_animation": "192", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome guard", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "164", - "range_level": "1", - "attack_level": "38" - }, - { - "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" - }, - { - "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": "169", - "range_level": "1", - "attack_level": "30" - }, - { - "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": "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", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "A dark-hearted knight.", - "melee_animation": "390", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "178", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "melee_animation": "390", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "388", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "42", - "strength_level": "1", - "id": "179", - "aggressive": "true", - "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": "180", - "aggressive": "true", - "range_level": "1", - "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": "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", - "id": "187", - "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 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 necromancer.", - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Bandit Camp guard.", - "melee_animation": "386", - "range_animation": "0", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard Bandit", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "1", - "id": "196", - "range_level": "20", - "attack_level": "1" - }, - { - "examine": "A mighty warrior!", - "melee_animation": "395", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Gunthor the Brave", - "defence_level": "29", - "safespot": null, - "lifepoints": "41", - "strength_level": "29", - "id": "199", - "range_level": "1", - "attack_level": "29" - }, - { - "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", - "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": "1631", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "1595", - "range_animation": "1595", - "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" - }, - { - "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": "206", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "12" - }, - { - "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": "A former Knight of the Round Table.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Percival", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "212", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks unhappy...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Peasant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "214", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "High Priest of Entrana.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "High Priest", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "216", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a fisherman.", - "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": "219", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He doesn't look very well...", - "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" - }, - { - "examine": "He looks mean and powerful.", - "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" - }, - { - "examine": "A holy man.", - "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" - }, - { - "examine": "A peaceful monk.", - "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" - }, - { - "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": "224", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Forester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "231", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He isn't very friendly.", - "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" - }, - { - "examine": "A terrifying spirit.", - "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" - }, - { - "examine": "Specialist meat transporter.", - "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" - }, - { - "death_animation": "6490", - "name": "Sir Mordred", - "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" - }, - { - "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.", - "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": "253", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "395", - "range_animation": "0", - "attack_speed": "6", - "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", - "id": "255", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "His armour indicates he's a Khazard Guard.", - "melee_animation": "395", - "range_animation": "0", - "attack_speed": "6", - "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", - "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", - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An official of Ardougne.", - "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" - }, - { - "examine": "A serious-looking doctor.", - "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" - }, - { - "examine": "A farmer who's seen happier times.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer Brumty", - "defence_level": "1", - "safespot": null, - "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", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "296", - "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": "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", - "death_animation": "0", - "name": "Hetty", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "307", - "aggressive": "true", - "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", - "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", - "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", - "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": "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": "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" - }, - { - "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", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "6", - "magic_level": "27", - "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", - "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 pirate.", - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Customs officer", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "380", - "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": "382", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not very civilised looking.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barbarian guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "384", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks vicious!", - "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", - "safespot": null, - "lifepoints": "10", - "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", - "combat_audio": "3611,3612,3610", - "strength_level": "1", - "id": "387", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with a shave...", - "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" - }, - { - "examine": "A brightly coloured game bird.", - "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" - }, - { - "examine": "A brightly coloured game bird.", - "melee_animation": "2371", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "2373", - "name": "River troll", - "defence_level": "3", - "safespot": null, - "lifepoints": "50", - "strength_level": "3", - "id": "392", - "range_level": "1", - "attack_level": "3" - }, - { - "examine": "Beefy.", - "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": "397", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jungle forester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "402", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Insects buzzing around.", - "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" - }, - { - "examine": "An annoying flappy thing.", - "slayer_task": "5", - "melee_animation": "4915", - "range_animation": "4915", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "30", - "defence_animation": "4916", - "weakness": "4", - "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" - }, - { - "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": "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": "17", - "safespot": null, - "lifepoints": "50", - "strength_level": "17", - "id": "438", - "range_level": "1", - "attack_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": "32", - "safespot": null, - "lifepoints": "55", - "strength_level": "32", - "id": "439", - "range_level": "1", - "attack_level": "32" - }, - { - "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": "48", - "safespot": null, - "lifepoints": "60", - "strength_level": "48", - "id": "440", - "range_level": "1", - "attack_level": "48" - }, - { - "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": "65", - "safespot": null, - "lifepoints": "86", - "strength_level": "65", - "id": "441", - "range_level": "1", - "attack_level": "65" - }, - { - "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, - "lifepoints": "120", - "strength_level": "100", - "id": "442", - "range_level": "1", - "attack_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", - "defence_level": "130", - "safespot": null, - "lifepoints": "170", - "strength_level": "130", - "id": "443", - "range_level": "1", - "attack_level": "130" - }, - { - "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": "444", - "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": "445", - "clue_level": "0", - "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": "2", - "safespot": null, - "lifepoints": "5", - "strength_level": "5", - "id": "446", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "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", - "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": "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "A large well built farmer.", - "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" - }, - { - "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", - "range_level": "1", - "attack_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", - "range_level": "1", - "attack_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": "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'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", - "aggressive": "true", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "It's one of General Khazard's commanders.", - "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" - }, - { - "examine": "It's a tree gnome trooper.", - "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" - }, - { - "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" - }, - { - "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 2", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "482", - "range_level": "1", - "attack_level": "1" - }, - { - "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 3", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "483", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a young tree gnome.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Local Gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "485", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I guess he wants to be more than just the muscle.", - "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" - }, - { - "examine": "He doesn't look like he'd trust his own mother.", - "slayer_task": "38", - "melee_animation": "6199", - "range_animation": "0", - "combat_audio": "469,472,471", - "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" - }, - { - "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" - }, - { - "examine": "The essence of evil.", - "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" - }, - { - "examine": "A vicious", - "slayer_task": "71", - "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": "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" - }, - { - "examine": "A minion of Rashiliyia.", - "slayer_task": "75", - "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" - }, - { - "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": "504", - "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": "505", - "aggressive": "true", - "range_level": "1", - "attack_level": "33" - }, - { - "melee_animation": "5571", - "respawn_delay": "60", - "defence_animation": "5574", - "death_animation": "5575", - "name": "Nazastarool", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "507", - "aggressive": "true", - "range_level": "1", - "attack_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", - "defence_level": "1", - "safespot": null, - "lifepoints": "110", - "strength_level": "1", - "id": "509", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bob", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "519", - "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": "Shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "520", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Helps sell stuff.", - "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": "521", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes people spending money.", - "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": "522", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes helping sell stuff.", - "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": "523", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A product of a consumerist society.", - "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": "524", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes you more", - "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" - }, - { - "examine": "A product of a consumerist society.", - "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" - }, - { - "examine": "Likes you more", - "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": "527", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has an interesting assortment of items for sale.", - "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": "530", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works on commission.", - "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": "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": "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "552", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "For the interesting clothing items you just can't find elsewhere.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fancy-dress shop owner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "554", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has a fine moustache.", - "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": "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "561", - "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-maker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "562", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks strange and mysterious.", - "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": "567", - "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": "569", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Seems very well-off.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gem merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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" - }, - { - "examine": "Seems very well-off.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silk merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He runs a Mining store.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Drogo dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "579", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder if he wants to buy my junk?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "582", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "If crafting's your thing, he's your man!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rommik", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems to sell tea.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tea seller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "595", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A pizza expert; in both making and eating.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fat Tony", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "596", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Anyone fancy a trim?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hairdresser", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "598", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Indentured servant of a Knight.", - "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" - }, - { - "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": "609", - "range_level": "1", - "attack_level": "26" - }, - { - "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": "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": "613", - "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": "Doug Deeping", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "614", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A student busy studying the digsite.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Student", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "617", - "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": "618", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on archaeology.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Archaeological expert", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "619", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A specialist in panning for gold.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Panning guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "620", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome baller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "621", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome winger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "633", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps the game fair.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome ball referee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "635", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dealer in potions.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Apothecary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "638", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A religious man... And occasional drunk.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Father Lawrence", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "640", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks down on his luck.", - "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" - }, - { - "examine": "Looks kind of obsessive...", - "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" - }, - { - "examine": "Looks kind of shifty...", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "498,500,499", - "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" - }, - { - "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", - "attack_level": "1", - "prj_height": "3" - }, - { - "examine": "Guardian of the dramen tree.", - "melee_animation": "5532", - "range_animation": "0", - "attack_speed": "5", - "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" - }, - { - "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.", - "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": "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "658", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He likes to paaaarty!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Party Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "659", - "range_level": "1", - "attack_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" - }, - { - "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": "An important looking gnome.", - "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" - }, - { - "examine": "The boss!", - "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" - }, - { - "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", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The keeper of the gates to the ranging guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ranging Guild Doorman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "679", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert leatherworker.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Leatherworker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "680", - "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": "Held vampyre juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "681", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Rangers armour.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Armour salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "682", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Archery equipment.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bow and Arrow salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "683", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tower keeper and competition judge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tower Advisor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "684", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "aggressive": "true", - "range_level": "10", - "attack_level": "7" - }, - { - "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", - "aggressive": "true", - "range_level": "20", - "attack_level": "15" - }, - { - "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", - "aggressive": "true", - "range_level": "30", - "attack_level": "22" - }, - { - "examine": "Defender of the west 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": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "30", - "id": "691", - "aggressive": "true", - "range_level": "40", - "attack_level": "30" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ticket Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "694", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Harlan, ready to teach swordplay.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Melee tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "705", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old wizard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard Mizgog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "706", - "range_level": "1", - "attack_level": "1" - }, - { - "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, - "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, - "lifepoints": "8", - "strength_level": "1", - "id": "709", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "0", - "examine": "It's an NPC.", - "name": "Edmond", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "714", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "731", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "732", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "735", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "737", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "739", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Duke Horacio of Lumbridge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Duke Horacio", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "741", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A badly-behaved goblin.", - "slayer_task": "38", - "melee_animation": "6184", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6204", - "name": "Wormbrain", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "745", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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": "Enraged barbarian spirit", - "defence_level": "76", - "safespot": null, - "lifepoints": "108", - "strength_level": "76", - "id": "750", - "aggressive": "true", - "range_level": "1", - "attack_level": "76" - }, - { - "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": "Berserk barbarian spirit", - "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.", - "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" - }, - { - "examine": "Stop looking and run!", - "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" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "combat_audio": "703,705,704", - "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", - "id": "799", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A holy man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Abbot Langley", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "801", - "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 Jered", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "802", - "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": "803", - "range_level": "1", - "attack_level": "12" - }, - { - "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", - "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": "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": "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", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "827", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "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", - "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", - "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" - }, - { - "examine": "He patrols the Shantay Pass.", - "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" - }, - { - "examine": "A vicious desert wolf.", - "slayer_task": "92", - "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" - }, - { - "death_animation": "9674", - "name": "Ugthanki", - "defence_level": "1", - "safespot": null, - "lifepoints": "46", - "strength_level": "1", - "attack_speed": "5", - "id": "840", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "He looks busy attending to his cart.", - "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" - }, - { - "examine": "This pickaxe has been possessed by a dwarf ancestor spirit.", - "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" - }, - { - "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", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Head chef", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "847", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The famous tree gnome chef.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Aluft Gianne snr.", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "850", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can serve you gnome food.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome Waiter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "851", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tough-looking.", - "slayer_task": "64", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre chieftain", - "defence_level": "58", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", - "id": "852", - "range_level": "1", - "attack_level": "58" - }, - { - "name": "Gorad", - "defence_level": "1", - "safespot": null, - "lifepoints": "81", - "strength_level": "1", - "id": "856", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "859", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "defence_animation": "0", - "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": "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", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", - "id": "870", - "range_level": "1", - "attack_level": "58" - }, - { - "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", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ogre trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "875", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tries to keep the peace.", - "melee_animation": "386", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower guard", - "defence_level": "17", - "safespot": null, - "lifepoints": "97", - "strength_level": "17", - "id": "877", - "range_level": "1", - "attack_level": "17" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "67", - "name": "Delrith", - "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" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ceril Carnillean", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "885", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "887", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Clivet", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "melee_animation": "811", - "strength_level": "1", - "id": "893", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "A member of the Hazeel cult.", - "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" - }, - { - "examine": "Children are just like real people...just smaller.", - "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" - }, - { - "examine": "He hasn't seen much sun lately.", - "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" - }, - { - "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": "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 RuneScape.", - "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": "An official representative from the First National Bank of RuneScape.", - "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 RuneScape.", - "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": "496,498,497", - "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": "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" - }, - { - "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": "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 green skinned croaker", - "melee_animation": "0", - "range_animation": "0", - "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", - "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", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "32", - "safespot": null, - "movement_radius": "4", - "lifepoints": "45", - "strength_level": "32", - "id": "1020", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "An air elemental.", - "melee_animation": "1040", - "range_animation": "0", - "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", - "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": "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.", - "name": "Soldier", - "defence_level": "44", - "safespot": null, - "lifepoints": "55", - "combat_audio": "511,513,512", - "strength_level": "44", - "id": "1065", - "magic_level": "1", - "range_level": "1", - "attack_level": "44" - }, - { - "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": "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": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "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": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "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": "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": "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" - }, - { - "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": "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": "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", - "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", - "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": "498,500,499", - "respawn_delay": "60", - "defence_animation": "4927", - "slayer_exp": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "1", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "1195", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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": "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 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 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" - }, - { - "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", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "1313", - "weakness": "7", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "id": "1265", - "bonuses": "-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75", - "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", - "attack_speed": "5", - "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": "-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75", - "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": "Supposedly fixes things around RuneScape.", - "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": "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": "496,498,497", - "attack_speed": "5", - "magic_level": "6", - "respawn_delay": "32", - "defence_animation": "6375", - "weakness": "7", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn Foal", - "defence_level": "6", - "safespot": null, - "lifepoints": "14", - "strength_level": "6", - "id": "1328", - "range_level": "1", - "attack_level": "6" - }, - { - "examine": "Cute but evil.", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "496,498,497", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Black unicorn Foal", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "1329", - "range_level": "1", - "attack_level": "12" - }, - { - "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": "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 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" - }, - { "examine": "It looks just a bit... underfed.", - "slayer_task": "75", + "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", - "combat_audio": "774,775,777", - "attack_speed": "6", - "defence_animation": "5520", - "weakness": "8", - "magic_animation": "5519", - "death_animation": "5521", - "name": "Skeleton", - "defence_level": "75", - "safespot": null, - "lifepoints": "112", - "strength_level": "109", - "id": "1471", "range_level": "1", - "attack_level": "80" + "respawn_delay": "100", + "safespot": null, + "slayer_exp": "110", + "strength_level": "90", + "weakness": "8" }, { - "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", + "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", - "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", + "strength_level": "50", + "weakness": "10" + }, + { "id": "1473", + "name": "Spider", "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", + "attack_level": "1", + "death_animation": "6251", + "defence_animation": "6250", "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "3611,3612,3610", - "strength_level": "1", - "id": "1477", + "lifepoints": "2", + "melee_animation": "6249", "range_level": "1", - "attack_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", - "slayer_task": "76", + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "47", + "weakness": "2" }, { - "name": "Small ninja monkey", + "id": "1479", + "name": "Snake", + "attack_level": "1", + "combat_audio": "3609,3608,3610", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "name": "Medium ninja monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1482", + "name": "Gorilla" }, { - "name": "Bearded gorilla", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", "id": "1483", + "name": "Bearded gorilla", + "attack_level": "1", + "combat_audio": "498,500,499", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "name": "Ancient monkey", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "629,631,630", - "strength_level": "1", "id": "1484", + "name": "Ancient monkey", + "attack_level": "1", + "combat_audio": "629,631,630", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_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", + "name": "Small zombie monkey", + "attack_level": "1", "combat_audio": "629,631,630", - "strength_level": "1", - "id": "1486", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_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", - "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" + "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", - "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" + "safespot": null, + "strength_level": "73" + }, + { + "id": "1525", + "name": "Undead Lumberjack" + }, + { + "id": "1526", + "name": "Lanthus" + }, + { + "id": "1527", + "name": "Mine cart" }, { - "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", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1529", + "name": "Sheep" + }, + { + "id": "1530", + "name": "Rabbit" }, { - "name": "Imp", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", "id": "1531", + "name": "Imp", + "attack_level": "1", + "defence_level": "1", + "lifepoints": "8", + "movement_radius": "25", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_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" - }, - { + "name": "Barricade", "examine": "Handy for hindering the enemy team's movement.", + "attack_level": "1", + "attack_speed": "5", "combat_style": "1", - "melee_animation": "0", - "range_animation": "0", - "attack_speed": "5", - "defence_animation": "0", - "magic_animation": "0", "death_animation": "9658", - "name": "Barricade", + "defence_animation": "0", "defence_level": "1", - "poison_immune": "true", - "safespot": null, "lifepoints": "50", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_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", + "examine": "Handy for hindering the enemy team's movement.", + "attack_level": "1", + "attack_speed": "5", + "death_animation": "9658", + "defence_animation": "0", "defence_level": "1", - "poison_immune": "true", - "safespot": null, "lifepoints": "50", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_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", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "name": "Haze", + "examine": "A strange disturbance in the air.", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Well", - "slayer_task": "75", + "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", - "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" + "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.", - "slayer_task": "75", + "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", - "defence_animation": "0", - "weakness": "8", + "safespot": null, "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" + "weakness": "8" }, { - "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", + "name": "Treus Dayth", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A large boisterous bird", - "slayer_task": "7", - "melee_animation": "6761", - "range_animation": "0", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "6762", - "name": "Chompy bird", + "attack_level": "1", + "death_animation": "5542", + "defence_animation": "5541", "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "1550", - "range_level": "2", - "attack_level": "1" - }, - { - "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", + "lifepoints": "100", + "melee_animation": "5540", "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" + "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.", - "slayer_task": "92", + "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", - "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" + "weakness": "7" + }, + { + "id": "1559", + "name": "Ice wolf" }, { - "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", + "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", - "attack_level": "60" + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" }, { - "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", + "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", - "attack_level": "60" + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" }, { - "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", + "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", - "attack_level": "60" + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" }, { - "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", + "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", - "attack_level": "60" + "respawn_delay": "60", + "safespot": null, + "strength_level": "60", + "weakness": "9" }, { - "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" - }, - { + "name": "Ice Troll", "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "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", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Ice Troll", - "defence_level": "60", "safespot": null, - "lifepoints": "85", "strength_level": "60", + "weakness": "9" + }, + { "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", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "60", + "death_animation": "287", + "defence_animation": "285", "defence_level": "60", - "safespot": null, "lifepoints": "85", - "strength_level": "60", - "id": "1566", + "magic_animation": "0", + "melee_animation": "284", + "range_animation": "0", "range_level": "1", - "attack_level": "60" + "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", - "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" + "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" }, { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,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" - }, - { + "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", + "range_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", + "slayer_exp": "111", "strength_level": "65", + "weakness": "1" + }, + { "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" - }, - { + "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", + "range_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", + "slayer_exp": "111", "strength_level": "65", + "weakness": "1" + }, + { "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" - }, - { + "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", + "range_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", + "slayer_exp": "111", "strength_level": "65", + "weakness": "1" + }, + { "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": "449,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", + "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", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "His beard seems to have a life of its own.", - "slayer_task": "62", + "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": "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", + "range_level": "1", + "respawn_delay": "20", "safespot": null, - "lifepoints": "60", - "strength_level": "30", + "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", - "aggressive": "true", - "bonuses": "33,33,33,0,0,0,0,0,0,0,0,31,0,0,0", - "range_level": "1", - "attack_level": "30" - }, - { + "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": "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" + "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.", - "slayer_task": "68", + "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", - "combat_audio": "408,410,409", - "attack_speed": "5", - "magic_level": "40", + "range_level": "1", "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" + "weakness": "3" }, { - "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", + "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", - "range_level": "1", - "attack_level": "112" - }, - { - "examine": "Its scales seem to be made of iron.", - "slayer_task": "50", + "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", - "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", + "range_level": "1", + "respawn_delay": "40", "safespot": null, - "lifepoints": "165", - "strength_level": "165", + "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", - "range_level": "1", - "attack_level": "165" - }, - { - "examine": "Its scales seem to be made of steel.", - "slayer_task": "80", + "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", - "combat_audio": "408,410,409", - "attack_speed": "4", - "magic_level": "100", + "range_level": "1", "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", + "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", - "attack_level": "215" + "respawn_delay": "45", + "safespot": null, + "slayer_exp": "221", + "strength_level": "215", + "weakness": "7" }, { - "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", + "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", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "Looks like it's got Rabies!", + "death_animation": "6564", + "defence_animation": "6563", + "defence_level": "25", + "lifepoints": "62", + "magic_animation": "6562", + "magic_level": "20", "melee_animation": "6562", "range_animation": "6562", - "magic_level": "20", + "range_level": "1", "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", + "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", - "attack_level": "25" + "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" }, { - "examine": "A spiky crawling critter. ", - "slayer_task": "16", - "melee_animation": "266", - "range_animation": "266", - "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", + "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", - "attack_level": "28" + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "22", + "strength_level": "30", + "weakness": "1" }, { - "examine": "A spiky crawling critter. ", - "melee_animation": "266", - "range_animation": "266", - "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", + "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", - "attack_level": "28" + "respawn_delay": "25", + "safespot": null, + "slayer_exp": "22", + "strength_level": "30" }, { - "examine": "A spiky crawling critter. ", - "melee_animation": "266", - "range_animation": "266", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "Flies like a rock.", - "melee_animation": "9454", - "range_animation": "1516", + "examine": "A spiky crawling critter. ", + "attack_level": "28", "attack_speed": "4", - "magic_level": "1", - "defence_animation": "9455", - "slayer_exp": "105", - "magic_animation": "1516", - "death_animation": "1518", - "name": "Gargoyle", - "defence_level": "107", + "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, - "lifepoints": "105", - "strength_level": "95", - "id": "1610", + "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", - "attack_level": "75" + "safespot": null, + "slayer_exp": "105", + "strength_level": "95" }, { + "id": "1611", + "name": "Swarming turoth", "examine": "Flies like a rock.", - "slayer_task": "84", + "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", - "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" + "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", - "attack_speed": "5", - "defence_animation": "9451", - "slayer_exp": "22", - "magic_animation": "1523", - "death_animation": "9450", - "name": "Banshee", - "defence_level": "5", - "safespot": null, - "lifepoints": "22", - "strength_level": "14", - "id": "1612", - "aggressive": "true", - "clue_level": "0", "range_level": "1", - "attack_level": "9" + "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", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "9489", - "magic_animation": "1528", - "death_animation": "9488", - "name": "Nechryael", - "defence_level": "105", + "range_level": "1", "safespot": null, - "lifepoints": "105", - "strength_level": "97", - "id": "1613", + "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", - "range_level": "1", - "attack_level": "97" - }, - { - "examine": "An evil death spawn.", - "melee_animation": "1540", - "range_animation": "1540", - "attack_speed": "4", - "magic_level": "1", + "combat_audio": "276,278,277", + "death_animation": "1538", "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", + "defence_level": "135", + "lifepoints": "150", + "magic_animation": "1537", + "magic_level": "1", "melee_animation": "1537", "range_animation": "1537", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "0", - "weakness": "1", + "range_level": "1", + "safespot": null, "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" + "weakness": "1" }, { - "examine": "The eyes of evil.", - "melee_animation": "260", - "range_animation": "260", - "attack_speed": "5", - "magic_level": "75", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "80", "id": "1616", - "range_level": "75", - "attack_level": "80" - }, - { + "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", - "attack_speed": "5", - "magic_level": "75", - "defence_animation": "261", + "range_level": "1", + "respawn_delay": "15", + "safespot": null, "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "80", + "strength_level": "45" + }, + { "id": "1617", - "range_level": "75", - "attack_level": "80" + "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" }, { - "examine": "The tongue of evil.", - "melee_animation": "9130", - "range_animation": "1552", - "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" - }, - { + "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", - "attack_speed": "4", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "9132", + "safespot": null, "slayer_exp": "120", - "magic_animation": "1552", - "death_animation": "9131", - "name": "Bloodveld", - "defence_level": "30", - "safespot": null, - "lifepoints": "120", - "strength_level": "45", + "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", - "attack_level": "75" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "120", + "strength_level": "45" }, { - "examine": "The winged reptile.", - "slayer_task": "19", - "melee_animation": "7762", - "range_animation": "7762", - "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", + "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", - "range_level": "32", - "attack_level": "32" - }, - { - "examine": "The winged reptile.", + "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", - "attack_speed": "5", - "defence_animation": "7761", - "slayer_exp": "37", - "magic_animation": "7762", - "death_animation": "7763", - "name": "Cockatrice", - "defence_level": "35", + "range_level": "32", "safespot": null, - "lifepoints": "37", + "slayer_exp": "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" + "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.", - "slayer_task": "28", + "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", - "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" + "safespot": null, + "slayer_exp": "105", + "strength_level": "70", + "weakness": "2" }, { + "id": "1625", + "name": "Smokedevil", "examine": "The cave-dwelling cousin of the dust devils.", - "slayer_task": "28", + "attack_level": "105", + "death_animation": "1558", + "defence_animation": "0", + "defence_level": "61", + "lifepoints": "87", + "magic_animation": "0", "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" + "safespot": null, + "strength_level": "105", + "weakness": "2" }, { - "examine": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "9506", - "range_animation": "9506", - "attack_speed": "5", + "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", - "defence_animation": "9508", - "weakness": "7", - "magic_animation": "9506", - "death_animation": "9507", - "name": "Rockslug", - "defence_level": "30", + "melee_animation": "9471", + "range_animation": "9471", + "range_level": "1", + "respawn_delay": "15", "safespot": null, - "lifepoints": "30", - "strength_level": "30", + "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", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "9506", - "range_animation": "9506", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "9508", - "weakness": "7", - "magic_animation": "9506", - "death_animation": "9507", "name": "Rockslug", - "defence_level": "30", - "safespot": null, - "lifepoints": "30", - "strength_level": "30", - "id": "1632", + "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", - "attack_level": "24" + "safespot": null, + "strength_level": "30", + "weakness": "7" }, { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", + "id": "1632", + "name": "Rockslug", + "examine": "A rocky slug.", + "attack_level": "24", "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", + "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, - "lifepoints": "45", - "strength_level": "29", + "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", - "attack_level": "22" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "45", + "spawn_animation": "8081", + "strength_level": "29", + "weakness": "4" }, { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "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", + "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", - "attack_level": "22" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "45", + "spawn_animation": "8081", + "strength_level": "29", + "weakness": "4" }, { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "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", - "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", + "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", - "attack_level": "22" + "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.", - "slayer_task": "51", + "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", - "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" + "safespot": null, + "slayer_exp": "75", + "strength_level": "45", + "weakness": "2" }, { - "examine": "Doesn't look so tough...", - "slayer_task": "51", - "melee_animation": "8569", - "range_animation": "0", - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "75", - "magic_animation": "0", - "death_animation": "8570", "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", - "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", + "magic_animation": "0", "melee_animation": "8569", "range_animation": "0", - "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" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "75", + "strength_level": "1", + "weakness": "2" }, { - "melee_animation": "8569", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "8571", - "slayer_exp": "75", + "id": "1639", "name": "Jelly", - "defence_level": "1", + "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, - "lifepoints": "75", + "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", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "8569", - "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", + "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", - "attack_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.", - "slayer_task": "21", + "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", - "attack_speed": "5", + "range_level": "1", "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", + "slayer_exp": "19", "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" + "weakness": "9" }, { - "examine": "Gimmie five. Actually", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "0", - "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" - }, - { + "name": "Crawling Hand", "examine": "Gimmie five. Actually", - "slayer_task": "21", + "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", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "9126", - "name": "Crawling Hand", - "defence_level": "8", "safespot": null, - "lifepoints": "5", + "slayer_exp": "19", "strength_level": "8", + "weakness": "9" + }, + { "id": "1650", - "range_level": "1", - "attack_level": "8" - }, - { + "name": "Crawling Hand", "examine": "Gimmie five. Actually", - "slayer_task": "21", + "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", - "attack_speed": "5", + "range_level": "1", "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", + "weakness": "9" + }, + { "id": "1651", - "range_level": "1", - "attack_level": "8" - }, - { + "name": "Crawling Hand", "examine": "Gimmie five. Actually", - "slayer_task": "21", + "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", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", + "safespot": null, "slayer_exp": "19", - "magic_animation": "0", - "death_animation": "9126", - "name": "Crawling Hand", - "defence_level": "8", - "safespot": null, - "lifepoints": "5", "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", - "attack_level": "8" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "19", + "strength_level": "8", + "weakness": "9" }, { - "examine": "Now THAT's handy.", - "slayer_task": "21", - "melee_animation": "9444", - "range_animation": "0", - "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", + "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", - "attack_speed": "5", + "range_level": "1", "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", + "slayer_exp": "16", "strength_level": "13", + "weakness": "9" + }, + { "id": "1654", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "A big severed hand.", - "slayer_task": "21", + "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", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "9445", - "name": "Crawling Hand", - "defence_level": "13", "safespot": null, - "lifepoints": "18", + "slayer_exp": "16", "strength_level": "13", + "weakness": "9" + }, + { "id": "1655", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "Give the guy a big hand.....", - "slayer_task": "21", - "melee_animation": "9444", - "range_animation": "0", - "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", + "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", - "attack_speed": "5", + "range_level": "1", "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", + "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", - "attack_level": "13" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "16", + "strength_level": "13", + "weakness": "9" }, { - "examine": "He's in charge of the Skullball Course.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Skullball Boss", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1659", + "name": "Skullball" + }, + { "id": "1660", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He's in charge of the Agility Course.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Agility Boss", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1661", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A skullball guide.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Skullball Trainer", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", + "name": "Skullball Trainer", + "examine": "A skullball guide.", + "attack_level": "1", "death_animation": "0", - "name": "Agility Trainer", + "defence_animation": "0", "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...", + "magic_animation": "0", "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" + "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", + "magic_level": "1", + "melee_animation": "1626", + "range_level": "1", + "strength_level": "50" + }, + { + "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", + "magic_level": "1", + "melee_animation": "1616", + "range_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", - "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" + "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", - "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" + "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.", - "slayer_task": "71", + "attack_level": "30", + "death_animation": "6267", + "defence_animation": "0", + "defence_level": "30", + "lifepoints": "128", + "magic_animation": "0", "melee_animation": "6265", "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6267", - "name": "Giant lobster", - "defence_level": "30", + "range_level": "1", "safespot": null, - "lifepoints": "128", "strength_level": "30", - "id": "1693", - "range_level": "1", - "attack_level": "30" + "weakness": "6" + }, + { + "id": "1694", + "name": "Robin" }, { - "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.", + "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", - "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" + "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.", - "slayer_task": "36", + "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", - "attack_speed": "5", - "magic_level": "38", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "836", - "name": "Tortured soul", - "defence_level": "38", - "safespot": null, - "lifepoints": "85", - "strength_level": "28", - "id": "1698", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "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": "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" - }, - { - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "melee_animation": "1010", - "strength_level": "1", - "id": "1751", "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", - "defence_animation": "1012" + "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!", - "slayer_task": "7", + "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", - "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" + "weakness": "6" + }, + { + "id": "1753", + "name": "Mounted terrorbird gnome" }, { - "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", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1755", + "name": "Crow", + "examine": "A farmer's enemy." + }, + { + "id": "1756", + "name": "Crow" }, { - "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" - }, - { + "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", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Farmer", - "defence_level": "5", "safespot": null, - "lifepoints": "7", "strength_level": "5", + "weakness": "9" + }, + { "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.", + "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", - "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" + "weakness": "9" }, { - "examine": "Farming the wheat.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "id": "1759", "name": "Farmer", + "examine": "Farming.", + "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": "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "White and fluffy.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Sheep", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1764", + "name": "Sheep" + }, + { + "id": "1765", + "name": "Sheep" }, { - "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", + "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", - "safespot": null, "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", - "attack_level": "1" + "safespot": null, + "slayer_exp": "8", + "strength_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" - }, - { + "name": "Goblin", "examine": "An ugly green creature.", - "slayer_task": "38", + "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", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "5", + "slayer_exp": "5", "strength_level": "2", + "weakness": "7" + }, + { "id": "1770", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Goblin", "examine": "An ugly green creature.", - "slayer_task": "38", + "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", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "5", + "slayer_exp": "5", "strength_level": "2", + "weakness": "7" + }, + { "id": "1771", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Goblin", "examine": "An ugly green creature.", - "slayer_task": "38", + "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", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "5", + "slayer_exp": "5", "strength_level": "2", + "weakness": "7" + }, + { "id": "1772", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Goblin", "examine": "An ugly green creature.", - "slayer_task": "38", + "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", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "5", + "slayer_exp": "5", "strength_level": "2", + "weakness": "7" + }, + { "id": "1773", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Goblin", "examine": "An ugly green creature.", - "slayer_task": "38", + "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", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "5", + "slayer_exp": "5", "strength_level": "2", + "weakness": "7" + }, + { "id": "1774", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Goblin", "examine": "An ugly green creature.", - "slayer_task": "38", + "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", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "5", + "slayer_exp": "5", "strength_level": "2", + "weakness": "7" + }, + { "id": "1775", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Goblin", "examine": "An ugly green creature.", - "slayer_task": "38", + "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", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "weakness": "7", + "range_level": "1", + "safespot": null, "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "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", - "attack_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" }, { - "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", + "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", - "attack_level": "1" + "safespot": null, + "strength_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" - }, - { + "name": "Dwarf gang member", "examine": "A short stout menacing fellow.", - "slayer_task": "29", + "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", - "attack_speed": "6", + "range_level": "1", "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", + "weakness": "9" + }, + { "id": "1796", - "range_level": "1", - "attack_level": "25" - }, - { + "name": "Dwarf gang member", "examine": "A short stout menacing fellow.", - "slayer_task": "29", + "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", - "attack_speed": "6", + "range_level": "1", "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", + "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", - "attack_level": "25" + "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" }, { - "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", + "name": "Slagilith", "aggressive": "true", + "attack_level": "1", + "death_animation": "1752", + "defence_animation": "1751", + "defence_level": "1", + "lifepoints": "60", + "melee_animation": "1750", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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.", + "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", - "combat_audio": "511,513,512", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Guard", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1804", + "name": "Slagilith" + }, + { "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", + "examine": "He seems to be guarding a pile of rocks. Interesting job.", + "attack_level": "1", "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Chieftain of the mountain camp.", + "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": "Hamal the Chieftain", - "defence_level": "1", + "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", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1808", + "name": "Ragnar" + }, + { + "id": "1809", + "name": "Svidi" + }, + { + "id": "1810", + "name": "Jokul" + }, + { + "id": "1811", + "name": "The Kendal" }, { - "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", + "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, - "lifepoints": "50", - "strength_level": "1", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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" - }, - { + "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", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", "safespot": null, - "lifepoints": "15", "strength_level": "11", + "weakness": "9" + }, + { "id": "1815", - "range_level": "1", - "attack_level": "11" - }, - { + "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", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", "safespot": null, - "lifepoints": "15", "strength_level": "11", + "weakness": "9" + }, + { "id": "1816", - "range_level": "1", - "attack_level": "11" - }, - { + "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", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", "safespot": null, - "lifepoints": "15", "strength_level": "11", + "weakness": "9" + }, + { "id": "1817", - "range_level": "1", - "attack_level": "11" - }, - { + "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", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Camp dweller", - "defence_level": "11", + "range_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "15", "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", - "attack_level": "11" + "safespot": null, + "strength_level": "11", + "weakness": "9" }, { - "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Mountain Goat", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1821", + "name": "Bald Headed Eagle" }, { - "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" - }, - { + "name": "Cave goblin", "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", + "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", - "combat_audio": "469,472,471", + "range_level": "1", "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", + "weakness": "7" + }, + { "id": "1823", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Cave goblin", "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", + "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", - "combat_audio": "469,472,471", + "range_level": "1", "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", + "weakness": "7" + }, + { "id": "1824", - "range_level": "1", - "attack_level": "1" - }, - { + "name": "Cave goblin", "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "7" }, { - "examine": "Flies like a rock.", - "melee_animation": "9454", - "range_animation": "1516", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "9455", - "slayer_exp": "105", - "magic_animation": "1516", - "death_animation": "1518", - "name": "Gargoyle", - "defence_level": "107", + "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, - "lifepoints": "105", - "strength_level": "95", + "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", - "attack_level": "75" + "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", - "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" + "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", - "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", - "range_animation": "0", - "attack_speed": "5", - "poisonous": "true", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1792", - "name": "Cave slime", - "defence_level": "19", "safespot": null, - "lifepoints": "27", "strength_level": "1", - "id": "1831", - "clue_level": "0", - "range_level": "19", - "attack_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.", - "slayer_task": "15", + "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", - "attack_speed": "5", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", + "safespot": null, "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" + "strength_level": "5", + "weakness": "2" }, { - "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Candle seller", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1834", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A short angry guy.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarven Engineer", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The little guy is having trouble standing up right.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Khorvak, a dwarven engineer", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1841", + "name": "Rolad" + }, + { "id": "1842", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's probably you who will be paying the ferryman.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarven Ferryman", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1843", - "range_level": "1", - "attack_level": "1" - }, - { + "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", - "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" + "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", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "1841", - "name": "Arzinian Avatar of Strength", - "defence_level": "40", + "range_level": "1", "safespot": null, - "lifepoints": "114", "strength_level": "40", - "id": "1850", - "range_level": "1", - "attack_level": "40" + "weakness": "7" }, { - "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", + "attack_level": "1", + "death_animation": "1843", + "defence_animation": "1842", + "defence_level": "1", + "lifepoints": "110", + "melee_animation": "1840", "range_level": "1", - "attack_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", - "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" + "safespot": null, + "strength_level": "30", + "weakness": "0" }, { - "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", + "attack_level": "1", + "death_animation": "1843", + "defence_animation": "1842", + "defence_level": "1", + "lifepoints": "110", + "melee_animation": "1840", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "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, - "lifepoints": "10", - "strength_level": "1", + "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He sells ranging equipment.", + "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", - "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" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1860", + "name": "Brian" }, { - "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Drunken Ali", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1862", + "name": "Ali Morrisane" + }, + { "id": "1863", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hassled looking barman.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali The barman", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1864", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A kebab seller.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Kebab seller", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1865", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A mischievous looking child.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Street urchin", - "defence_level": "1", + "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", - "strength_level": "1", + "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old Hag named Alice.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ali the Hag", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "1869", + "name": "Ali the Mayor" + }, + { "id": "1871", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A foul tempered ugly lumpy yellow horse prone to spitting.", + "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", - "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" + "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", - "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" + "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", - "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" + "weakness": "9" }, { - "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.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "Tough looking Menaphite.", + "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Tough Guy", - "defence_level": "39", + "range_level": "0", + "respawn_delay": "5", "safespot": null, - "lifepoints": "114", - "strength_level": "39", + "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", - "attack_level": "39" + "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" }, { - "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", + "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "1912", + "name": "Elissa" }, { - "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", + "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", - "attack_level": "20" + "safespot": null, + "strength_level": "80" }, { - "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", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "99" }, { - "examine": "I guess he sells what he steals...?", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bandit shopkeeper", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "1917", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Hardened by the cutthroat world of archaeology.", + "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", - "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" + "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", - "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" + "safespot": null, + "strength_level": "58", + "weakness": "7" }, { - "examine": "Looks like a rough-and-ready type.", + "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", - "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" + "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", - "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" + "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", - "attack_speed": "5", + "range_level": "1", "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" + "strength_level": "1" + }, + { + "id": "1932", + "name": "Troll child" + }, + { + "id": "1934", + "name": "Troll child" }, { - "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", + "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", - "attack_level": "1" + "safespot": null, + "strength_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", + "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", - "attack_level": "64" + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" }, { - "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", + "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", - "attack_level": "64" + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" }, { - "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", + "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", - "attack_level": "64" + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" }, { - "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", + "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", - "attack_level": "64" + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" }, { - "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", + "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", - "attack_level": "64" + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" }, { - "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", + "examine": "Brrrrr...he must be cold!", + "aggressive": "true", + "attack_level": "64", + "death_animation": "287", + "defence_animation": "285", "defence_level": "64", - "safespot": null, "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", - "attack_level": "64" + "respawn_delay": "60", + "safespot": null, + "strength_level": "64", + "weakness": "9" }, { - "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", + "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, - "lifepoints": "72", - "strength_level": "60", + "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", - "aggressive": "true", - "bonuses": "40,120,150,150,150,40,130,75,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "60" - }, - { + "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", - "magic_level": "30", + "range_level": "1", "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", + "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" - }, - { + "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", - "magic_level": "30", + "range_level": "1", "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", + "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" - }, - { + "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", - "magic_level": "30", + "range_level": "1", "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", + "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" - }, - { + "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", - "magic_level": "30", + "range_level": "1", "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", + "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" - }, - { + "name": "Ice wolf", "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", + "attack_level": "60", "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" - }, - { - "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", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6567", - "name": "Jackal", - "defence_level": "35", - "safespot": null, - "lifepoints": "100", - "strength_level": "35", - "id": "1994", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "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": "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" - }, - { - "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": "Zogre", - "defence_level": "1", - "safespot": null, - "lifepoints": "71", - "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": "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": "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": "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": "A rich landlord", - "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": "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 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" - }, - { - "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" - }, - { - "combat_style": "1", - "melee_animation": "190", - "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" - }, - { - "combat_style": "1", - "melee_animation": "190", - "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", - "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", - "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": "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" - }, - { - "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" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2309", - "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": "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": "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", - "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", - "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", - "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", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "68", - "id": "2452", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "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", - "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": "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": "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" - }, - { - "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 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" - }, - { - "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" - }, - { - "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": "469,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", - "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" - }, - { - "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": "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", - "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" - }, - { - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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": "From a darker dimension.", - "slayer_task": "25", - "melee_animation": "2731", - "range_animation": "2731", - "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", - "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": "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" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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" - }, - { - "melee_animation": "1312", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2885", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2862", - "name": "Rock lobster", - "defence_level": "100", - "safespot": null, - "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" - }, - { - "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" - }, - { - "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", - "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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", - "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", - "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", - "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", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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", - "attack_speed": "4", - "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": "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" - }, - { - "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": "A swarm of bugs.", - "slayer_task": "42", - "combat_style": "1", - "melee_animation": "1584", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1585", - "name": "Harpie Bug Swarm", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "3153", - "aggressive": "true", - "clue_level": "1", - "range_level": "45", - "attack_level": "1" - }, - { - "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" - }, - { - "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 RuneScape's many citizens", - "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": "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "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": "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": "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": "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, - "lifepoints": "10", - "strength_level": "1", - "id": "3322", - "range_level": "1", - "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", - "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", - "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": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fun guy. No wait", - "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" - }, - { - "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" - }, - { - "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": "408,410,409", - "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", - "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 may be a funky cook", - "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" - }, - { - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "24", - "safespot": null, - "lifepoints": "34", - "strength_level": "24", - "id": "3422", - "range_level": "1", - "attack_level": "24" - }, - { - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "melee_animation": "3433", - "strength_level": "1", - "id": "3423", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3434" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "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": "408,410,409", - "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": "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" - }, - { - "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": "498,500,499", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Angry bear", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3645", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a little on the cross side!", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "496,498,497", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6377", - "name": "Angry unicorn", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3646", - "range_level": "1", - "attack_level": "25" - }, - { - "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 RuneScape 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" - }, - { - "name": "Angry unicorn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "496,498,497", - "strength_level": "1", - "id": "3661", - "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": "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" - }, - { - "name": "Angry bear", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "3664", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "melee_animation": "2019", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "2021", - "name": "Vulture", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "3675", - "range_level": "1", - "attack_level": "38" - }, - { - "death_animation": "2026", - "name": "Vulture", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "2025", - "strength_level": "1", - "id": "3676", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "2024" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "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": "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" - }, - { - "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": "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" - }, - { - "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" - }, - { - "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", - "strength_level": "1", - "id": "4228", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A denizen of the Abyss!", - "slayer_task": "1", - "melee_animation": "1537", - "range_animation": "1537", - "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" - }, - { - "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": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Bronze Armour", - "defence_level": "8", - "safespot": null, - "lifepoints": "10", - "strength_level": "8", - "id": "4278", - "bonuses": "3,3,3,3,1,3,1,3,3,3,3,8,10,8,3", - "range_level": "1", - "attack_level": "8" - }, - { - "examine": "Animated iron armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Iron Armour", - "defence_level": "16", - "safespot": null, - "lifepoints": "20", - "strength_level": "16", - "id": "4279", - "bonuses": "6,6,6,6,2,6,6,6,6,6,6,2,25,2,3", - "range_level": "1", - "attack_level": "16" - }, - { - "examine": "Animated steel armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Steel Armour", - "defence_level": "32", - "safespot": null, - "lifepoints": "40", - "strength_level": "32", - "id": "4280", - "bonuses": "10,10,10,10,4,10,10,10,10,10,10,2,22,2,3", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "Animated black armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Black Armour", - "defence_level": "40", - "safespot": null, - "lifepoints": "60", - "strength_level": "40", - "id": "4281", - "bonuses": "15,15,15,15,5,15,6,15,15,16,15,5,27,5,3", - "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Animated mithril armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Mithril Armour", - "defence_level": "55", - "safespot": null, - "lifepoints": "80", - "strength_level": "55", - "id": "4282", - "bonuses": "20,20,20,20,20,20,8,20,20,20,20,5,31,5,3", - "range_level": "1", - "attack_level": "55" - }, - { - "examine": "Animated adamant armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Adamant Armour", - "defence_level": "75", - "safespot": null, - "lifepoints": "99", - "strength_level": "65", - "id": "4283", - "bonuses": "25,25,25,25,25,25,12,25,25,25,25,5,35,5,3", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Animated rune armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Rune Armour", - "defence_level": "85", - "safespot": null, - "lifepoints": "118", - "strength_level": "75", - "id": "4284", - "bonuses": "30,30,30,30,30,30,16,30,25,30,30,5,39,5,3", - "range_level": "1", - "attack_level": "75" - }, - { - "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": "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": "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": "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" - }, - { - "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", - "slayer_task": "52", - "melee_animation": "4235", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "4348", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4349", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horrible", - "slayer_task": "52", - "melee_animation": "4235", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", - "id": "4350", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4351", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4352", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", - "melee_animation": "4234", - "range_animation": "4234", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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" - }, - { - "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 RuneScape 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" - }, - { - "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" - }, - { - "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": "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": "4392", - "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", - "range_level": "1", - "attack_level": "24" - }, - { - "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": "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": "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": "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": "Examine not added", - "slayer_task": "58", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "weakness": "7", - "slayer_exp": "10", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "10", - "safespot": null, - "lifepoints": "11", - "strength_level": "10", - "id": "4404", - "bonuses": "8,8,10,8,8,10,8,8,8,10,8,8,8,8,8", - "clue_level": "0", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Examine not added", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "15", - "safespot": null, - "lifepoints": "17", - "strength_level": "1", - "id": "4405", - "bonuses": "11,11,13,11,11,13,11,11,11,13,11,11,11,11,11", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Examine not added", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "slayer_exp": "22", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "19", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "4406", - "bonuses": "15,15,15,15,15,15,15,15,15,15,15,15,15,15,15", - "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": "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": "Examine not added", - "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": "69", - "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": "Examine not added", - "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": "69", - "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.", - "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": "4415", - "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": "Half horse", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Stag", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "4440", - "range_level": "1", - "attack_level": "18" - }, - { - "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": "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": "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", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4527", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4528", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4529", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4530", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4531", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4532", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4533", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "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", - "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" - }, - { - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "4667", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "4668", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "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": "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": "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": "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": "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" - }, - { - "melee_animation": "4868", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "4869", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "1", - "safespot": null, - "lifepoints": "35", - "strength_level": "1", - "id": "4910", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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": "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": "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 RuneScape is that?", - "melee_animation": "0", - "range_animation": "0", - "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": "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "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": "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" - }, - { - "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": "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": "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" - }, - { - "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": "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": "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": "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": "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", - "attack_speed": "6", - "magic_level": "77", - "defence_animation": "1581", - "weakness": "4", - "magic_animation": "1582", - "death_animation": "1580", - "name": "Waterfiend", - "defence_level": "50", - "poison_immune": "true", - "safespot": null, - "lifepoints": "128", - "strength_level": "167", - "id": "5361", - "aggressive": "true", - "range_level": "167", - "projectile": "16", - "attack_level": "70" - }, - { - "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" - }, - { - "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": "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "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": "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": "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" - }, - { - "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" - }, - { - "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": "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": "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": "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": "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" - }, - { - "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", - "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": "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", - "respawn_delay": "60", - "defence_animation": "6013", - "slayer_exp": "52", - "death_animation": "6014", - "name": "Molanisk", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5751", - "clue_level": "1", - "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": "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": "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": "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", - "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": "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 RuneScape 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 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": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Terrorbird display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "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": "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "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": "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", - "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", - "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" - }, - { - "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" - }, - { - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "melee_animation": "6790", - "strength_level": "1", - "id": "6110", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6792" - }, - { - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "melee_animation": "6790", - "strength_level": "1", - "id": "6111", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6792" - }, - { - "examine": "Aww", - "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" - }, - { - "death_animation": "750", - "name": "Duck", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "melee_animation": "747", - "strength_level": "1", - "id": "6113", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3268" - }, - { - "examine": "A messy bird.", - "slayer_task": "7", - "melee_animation": "3467", - "range_animation": "3467", - "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", - "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": "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", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "6189", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "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" - }, - { - "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,0", - "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" - }, - { - "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": "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, - "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", - "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", - "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", - "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" - }, - { - "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": "true", - "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": "true", - "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": "The bane of darkness.", - "melee_animation": "6376", - "range_animation": "6376", - "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": "true", - "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", - "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" - }, - { - "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 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,0", - "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": "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 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", - "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" - }, - { - "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" - }, - { - "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": "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" - }, - { - "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" - }, - { - "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 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", - "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", - "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", + "magic_animation": "6579", + "magic_level": "30", + "melee_animation": "6579", + "range_animation": "6579", "range_level": "1", - "attack_level": "1" + "respawn_delay": "28", + "safespot": null, + "strength_level": "60" }, { - "examine": "Part scarab", - "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", + "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", - "strength_level": "41", - "id": "6780", + "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", - "range_level": "1", - "attack_level": "41" - }, - { - "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", + "attack_level": "90", + "death_animation": "5555", "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": "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, + "defence_level": "90", "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" - }, - { - "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", + "melee_animation": "5549", "range_animation": "0", - "magic_level": "50", - "respawn_delay": "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", - "weakness": "10", + "defence_level": "90", + "lifepoints": "90", "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", + "melee_animation": "5549", + "poison_immune": "true", "range_animation": "0", - "magic_level": "55", - "respawn_delay": "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", - "weakness": "10", + "defence_level": "90", + "lifepoints": "90", "magic_animation": "0", - "death_animation": "8285", - "name": "War tortoise", + "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", - "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": "496,498,497", - "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": "496,498,497", - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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": "408,410,409", - "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": "408,410,409", - "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": "408,410,409", - "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": "408,410,409", - "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" - }, - { - "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" - }, - { - "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 Runescape", - "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", + "magic_animation": "0", + "melee_animation": "5549", + "range_animation": "0", "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" + "strength_level": "55", + "weakness": "9" }, { - "examine": "A stripy little baby raccoon.", - "melee_animation": "0", - "range_animation": "0", + "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", - "death_animation": "0", - "name": "Baby raccoon", + "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", - "safespot": null, "lifepoints": "10", - "strength_level": "1", - "id": "6997", "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,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" + "strength_level": "1" }, { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,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", + "id": "1973", + "name": "Giant skeleton", + "examine": "A giant skeleton.", + "attack_level": "60", + "death_animation": "5503", "defence_animation": "0", - "slayer_exp": "74", + "defence_level": "60", + "lifepoints": "85", "magic_animation": "0", - "death_animation": "6558", - "name": "Big wolf", - "defence_level": "62", + "melee_animation": "5499", + "range_animation": "0", + "range_level": "1", "safespot": null, - "lifepoints": "21", "strength_level": "60", - "id": "7005", - "aggressive": "true", - "range_level": "1", - "attack_level": "60" + "weakness": "8" }, { - "examine": "An active spiny creature.", - "melee_animation": "0", - "range_animation": "0", + "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", - "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", + "melee_animation": "440", "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", + "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", - "weakness": "6", + "defence_level": "160", + "lifepoints": "200", "magic_animation": "0", - "death_animation": "8640", - "name": "Ogress champion", + "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", - "safespot": null, "lifepoints": "68", - "strength_level": "48", - "id": "7078", - "aggressive": "true", - "range_level": "1", - "attack_level": "48" - }, - { - "examine": "Irascible", - "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", - "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": "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", - "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": "7129", - "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": "The head farmer", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer Blinkin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7131", - "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": "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", - "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", - "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", - "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", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "68", - "id": "2452", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "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 spiny horror from the ocean depths...", - "melee_animation": "1343", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "100", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "60", - "safespot": null, - "lifepoints": "95", - "strength_level": "90", - "id": "2455", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "294", - "attack_level": "90" - }, - { - "examine": "It's an NPC.", - "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "0", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "60", - "safespot": null, - "lifepoints": "70", - "strength_level": "90", - "id": "2456", - "aggressive": "true", - "clue_level": "1", - "range_level": "90", - "projectile": "294", - "attack_level": "90" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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": "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": "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" - }, - { - "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": "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": "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" - }, - { - "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 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" - }, - { - "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" - }, - { - "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" - }, - { - "death_animation": "836", - "name": "Hengel", - "defence_level": "1", - "safespot": null, - "lifepoints": "7", - "melee_animation": "422", - "strength_level": "1", - "id": "2683", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2685", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2686", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2687", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "An ugly smelly creature, with a spear.", - "melee_animation": "163", - "range_animation": "163", - "combat_audio": "469,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "163", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2688", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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", - "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", - "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", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "2694", - "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": "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": "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", - "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" - }, - { - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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": "From a darker dimension.", - "slayer_task": "25", - "melee_animation": "2731", - "range_animation": "2731", - "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", - "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": "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": "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" - }, - { - "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" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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" - }, - { - "melee_animation": "1312", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2885", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2862", - "name": "Rock lobster", - "defence_level": "100", - "safespot": null, - "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" - }, - { - "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" - }, - { - "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", - "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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", - "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", - "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 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": "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": "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": "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": "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": "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": "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": "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": "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" - }, - { - "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": "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", - "strength_level": "1", - "id": "7438", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7439", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7440", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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": "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": "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 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", - "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", - "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", - "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "48", + "weakness": "7" }, { - "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", + "id": "1977", + "name": "Fareed", + "examine": "Zamorak's warrior of fire.", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A small ice demon.", - "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": "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", - "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", + "attack_level": "190", + "bonuses": "0,0,0,0,0,100,100,100,0,0,0,120,0,0,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", + "defence_animation": "434", + "defence_level": "135", + "lifepoints": "130", + "magic_level": "100", + "melee_animation": "799", + "range_level": "100", "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" + "strength_level": "120" }, { - "examine": "Is it full of rats?", + "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", - "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" + "strength_level": "1" }, { - "examine": "It looks upset.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "A TzHaar librarian.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "A TzHaar-Hur stamping stone tablets.", + "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", - "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" + "strength_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": "7768", - "bonuses": "30,30,80,85,80,40,40,60,38,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "50" + "id": "1982", + "name": "Raetul" }, { - "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" + "id": "1983", + "name": "Siamun" }, { - "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" + "id": "1984", + "name": "High Priest" }, { - "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" + "id": "1987", + "name": "Priest" }, { - "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" - }, - { - "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": "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": "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": "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": "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", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "Known for his light-fingered qualities.", - "melee_animation": "422", - "range_animation": "0", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "425", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "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": "422", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "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", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "She looks happy.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Schoolgirl", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "10", - "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": "11", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Not very civilised looking.", - "name": "Barbarian", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "12", - "clue_level": "0", - "range_level": "1", - "attack_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", - "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": "Not very fashion conscious.", - "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" - }, - { - "examine": "Part of Al-Kharid's elite fighting force.", - "melee_animation": "390", - "range_animation": "390", - "attack_speed": "6", - "defence_animation": "387", - "weakness": "7", - "magic_animation": "390", - "death_animation": "836", - "name": "Al-Kharid warrior", - "defence_level": "5", - "safespot": null, - "lifepoints": "19", - "strength_level": "15", - "id": "18", - "clue_level": "0", - "range_level": "1", - "attack_level": "7" - }, - { - "examine": "Shiny armour!", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "425", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Hero", - "defence_level": "54", - "safespot": null, - "lifepoints": "82", - "strength_level": "55", - "id": "21", - "range_level": "1", - "attack_level": "54" - }, - { - "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", - "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": "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", - "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", + "id": "1988", "name": "Priest", + "examine": "Preach my brother!", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "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" - }, - { - "examine": "A soldier of the town of Yanille.", - "melee_animation": "390", - "range_animation": "0", - "combat_audio": "511,513,512", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "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", - "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" + "safespot": null, + "strength_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" + "id": "1989", + "name": "Priest" }, { - "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" + "id": "1990", + "name": "Sphinx" }, { - "examine": "Builds ships for a living.", - "melee_animation": "401", - "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" - }, - { - "examine": "Let's not go skinny dipping eh?", - "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" - }, - { - "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": "41", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "43", - "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": "44", + "id": "1991", + "name": "Possessed Priest", + "examine": "He has a dangerous glint in his eye.", "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She can look after my money.", - "melee_animation": "0", - "range_animation": "0", + "attack_level": "30", + "combat_style": "2", + "death_animation": "836", "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Banker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "45", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "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": "46", - "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": "47", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A jungle version of the chicken, but more vicious.", - "slayer_task": "7", - "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" - }, - { - "examine": "A dragon covered in frost and ice.", - "combat_audio": "408,410,409", - "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" - }, - { - "examine": "A wood nymph.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", + "lifepoints": "57", "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", + "melee_animation": "429", "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": "I think this spider has been genetically modified.", - "slayer_task": "76", - "melee_animation": "5327", - "range_animation": "0", - "combat_audio": "537,539,538", - "attack_speed": "4", + "range_level": "40", "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" + "strength_level": "30", + "weakness": "1" }, { - "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", - "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", + "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", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Incey wincey.", - "slayer_task": "76", - "melee_animation": "6249", - "range_animation": "0", - "respawn_delay": "60", + "combat_audio": "544,546,545", + "death_animation": "6567", "defence_animation": "0", - "weakness": "2", - "slayer_exp": "2", + "defence_level": "15", + "lifepoints": "27", "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" - }, - { - "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", - "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", - "range_level": "1", - "attack_level": "35" - }, - { - "examine": "I think this spider has been genetically modified.", - "slayer_task": "76", - "melee_animation": "5327", + "melee_animation": "6568", "range_animation": "0", - "combat_audio": "537,539,538", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "65", - "magic_animation": "0", - "death_animation": "5329", - "name": "Ice spider", - "defence_level": "43", + "range_level": "1", + "respawn_delay": "50", "safespot": null, - "lifepoints": "65", - "strength_level": "55", - "id": "64", + "strength_level": "18", + "weakness": "6" + }, + { + "id": "1995", + "name": "Locust", + "examine": "Obnoxious", "aggressive": "true", - "bonuses": "0,0,0,0,0,0,20,17,12,13,13,0,0,0,0", - "range_level": "1", - "attack_level": "50" - }, - { - "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!", - "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": "66", - "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "strength_level": "1", - "id": "68", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hideous malformed elf.", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Orc", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "71", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A hideously deformed creature.", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Troll", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "72", - "range_level": "1", - "attack_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", - "defence_level": "10", - "safespot": null, - "lifepoints": "24", - "strength_level": "13", - "id": "74", - "aggressive": "true", - "range_level": "1", - "attack_level": "13" - }, - { - "examine": "The walking dead.", - "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": "30", - "magic_animation": "5578", - "death_animation": "5580", - "name": "Zombie", - "defence_level": "16", - "safespot": null, - "lifepoints": "30", - "strength_level": "21", - "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "The living dead.", - "slayer_task": "93", - "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" - }, - { - "examine": "A shadowy, barely visible flying entity from some evil place.", - "slayer_task": "5", - "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" - }, - { - "examine": "Oh, it's a camel.", - "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" - }, - { - "examine": "Converts grass to beef.", - "slayer_task": "20", - "melee_animation": "5849", - "range_animation": "5849", - "combat_audio": "369,371,370", + "attack_level": "12", "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", + "death_animation": "2013", + "defence_animation": "0", + "defence_level": "12", + "lifepoints": "34", + "magic_animation": "0", + "melee_animation": "2015", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "12", + "weakness": "6" }, { - "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", + "id": "1996", + "name": "Vulture" + }, + { + "id": "1997", + "name": "Plague frog", + "examine": "A very smelly frog.", "aggressive": "true", - "bonuses": "0,0,0,0,0,0,0,0,-10,0,0,0,0,0,0", - "clue_level": "2", + "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", - "attack_level": "76" - }, - { - "examine": "A creature made from clay.", - "melee_animation": "422", - "range_animation": "422", - "magic_level": "1", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Golem", - "defence_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "85", - "range_level": "1", - "attack_level": "1" + "strength_level": "15", + "weakness": "6" }, { - "examine": "Overgrown vermin.", - "slayer_task": "67", + "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", - "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" - }, - { - "examine": "Overgrown vermin.", - "slayer_task": "67", - "melee_animation": "4933", - "range_animation": "0", - "combat_audio": "703,705,704", - "attack_speed": "4", - "respawn_delay": "60", - "defence_animation": "4934", - "weakness": "8", - "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" - }, - { - "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Horse with a horn.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "496,498,497", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "32", - "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" - }, - { - "examine": "Not man's best friend.", - "slayer_task": "92", - "melee_animation": "6559", - "combat_audio": "481,491,490", - "attack_speed": "4", - "slayer_exp": "69", - "death_animation": "6558", - "name": "Wolf", - "defence_level": "52", - "safespot": null, - "lifepoints": "69", - "strength_level": "55", - "id": "95", - "range_level": "1", - "attack_level": "50" - }, - { - "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" - }, - { - "examine": "A vicious mountain wolf.", - "slayer_task": "92", - "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" - }, - { - "examine": "Bow wow.", - "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" - }, - { - "examine": "He doesn't seem pleased to see me.", - "slayer_task": "27", - "melee_animation": "6562", - "range_animation": "6562", - "attack_speed": "4", - "respawn_delay": "15", - "defence_animation": "6563", - "weakness": "7", - "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" - }, - { - "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", - "combat_audio": "469,472,471", - "attack_speed": "5", - "defence_animation": "6183", - "slayer_exp": "16", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", - "id": "102", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "27", - "magic_animation": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "23", - "safespot": null, - "lifepoints": "32", - "strength_level": "23", - "id": "105", - "aggressive": "true", - "range_level": "1", - "attack_level": "23" - }, - { - "examine": "Eek! A bear!", - "slayer_task": "6", - "melee_animation": "4925", - "range_animation": "0", - "combat_audio": "498,500,499", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "4929", - "name": "Black bear", - "defence_level": "17", - "safespot": null, - "lifepoints": "24", - "strength_level": "17", - "id": "106", - "range_level": "1", - "attack_level": "17" - }, - { - "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": "Tiny", - "slayer_task": "71", - "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" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,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" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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" - }, - { - "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": "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" - }, - { - "examine": "An aggressive humanoid.", - "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" - }, - { - "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" - }, - { - "examine": "Big, ugly, and smelly.", - "slayer_task": "44", - "melee_animation": "359", - "range_animation": "359", - "attack_speed": "4", - "magic_level": "1", - "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", - "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 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 mountain dwelling short angry guy.", - "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": "120", - "range_level": "1", - "attack_level": "28" - }, - { - "slayer_exp": "16", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "121", - "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", - "safespot": null, - "lifepoints": "29", - "strength_level": "10", - "id": "122", - "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": "123", - "aggressive": "true", - "range_level": "20", - "attack_level": "20" - }, - { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "attack_speed": "4", - "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", - "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" - }, - { - "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", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "126", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An inhabitant of icy regions.", - "slayer_task": "7", - "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" - }, - { - "examine": "A unicorn with a blackened heart.", - "melee_animation": "6376", - "range_animation": "6376", - "combat_audio": "496,498,497", - "attack_speed": "5", - "magic_level": "24", - "defence_animation": "6375", - "weakness": "3", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Black unicorn", - "defence_level": "15", - "safespot": null, - "lifepoints": "28", - "strength_level": "10", - "id": "133", - "range_level": "1", - "attack_level": "15" - }, - { - "examine": "Aaw", - "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" - }, - { - "examine": "A giant raptor.", - "slayer_task": "7", - "melee_animation": "1010", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "30", - "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "138", - "range_level": "1", - "attack_level": "30" - }, - { - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "melee_animation": "1010", - "strength_level": "1", - "id": "139", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "1012" - }, - { - "examine": "A servant to Iban.", - "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" - }, - { - "examine": "Must be the pack leader.", - "slayer_task": "92", - "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" - }, - { - "slayer_exp": "34", - "death_animation": "6558", - "name": "Wolf", - "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" - }, - { - "examine": "A rare jungle wolf - specific to the Kharazi jungle.", - "slayer_task": "92", - "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" - }, - { - "examine": "Wow! Scorpions shouldn't grow that big.", - "slayer_task": "71", - "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" - }, - { - "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", - "melee_animation": "451", - "range_animation": "451", - "attack_speed": "4", - "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", - "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" - }, - { - "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" - }, - { - "examine": "A sea bird.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Cormorant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "147", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A sea bird.", - "slayer_task": "7", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Pelican", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "148", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "These look much better in the wild.", - "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": "153", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I love butterflies.", - "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" - }, - { - "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", - "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" - }, - { - "examine": "He can advise on training.", - "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" - }, - { - "examine": "A tree gnome guard.", - "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" - }, - { - "examine": "A tree gnome guard.", - "melee_animation": "192", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "196", - "name": "Gnome guard", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "164", - "range_level": "1", - "attack_level": "38" - }, - { - "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" - }, - { - "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": "169", - "range_level": "1", - "attack_level": "30" - }, - { - "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": "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", - "range_level": "1", - "attack_level": "11" - }, - { - "examine": "A dark-hearted knight.", - "melee_animation": "390", - "range_animation": "0", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "22", - "safespot": null, - "lifepoints": "31", - "strength_level": "22", - "id": "178", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "melee_animation": "390", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "388", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "1", - "safespot": null, - "lifepoints": "42", - "strength_level": "1", - "id": "179", - "aggressive": "true", - "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": "180", - "aggressive": "true", - "range_level": "1", - "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": "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", - "id": "187", - "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 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 necromancer.", - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Bandit Camp guard.", - "melee_animation": "386", - "range_animation": "0", - "defence_animation": "0", - "weakness": "1", - "magic_animation": "0", - "death_animation": "836", - "name": "Guard Bandit", - "defence_level": "20", - "safespot": null, - "lifepoints": "28", - "strength_level": "1", - "id": "196", - "range_level": "20", - "attack_level": "1" - }, - { - "examine": "A mighty warrior!", - "melee_animation": "395", - "range_animation": "0", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Gunthor the Brave", - "defence_level": "29", - "safespot": null, - "lifepoints": "41", - "strength_level": "29", - "id": "199", - "range_level": "1", - "attack_level": "29" - }, - { - "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", - "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": "1631", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "examine": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "1595", - "range_animation": "1595", - "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" - }, - { - "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": "206", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "12" - }, - { - "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": "A former Knight of the Round Table.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "King Percival", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "212", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He looks unhappy...", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Peasant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "214", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "High Priest of Entrana.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "High Priest", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "216", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a fisherman.", - "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": "219", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He doesn't look very well...", - "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" - }, - { - "examine": "He looks mean and powerful.", - "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" - }, - { - "examine": "A holy man.", - "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" - }, - { - "examine": "A peaceful monk.", - "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" - }, - { - "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": "224", - "aggressive": "true", - "range_level": "1", - "attack_level": "8" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Forester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "231", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He isn't very friendly.", - "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" - }, - { - "examine": "A terrifying spirit.", - "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" + "weakness": "9" }, { - "examine": "Specialist meat transporter.", - "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" - }, - { - "death_animation": "6490", - "name": "Sir Mordred", - "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" - }, - { - "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.", - "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": "253", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "395", - "range_animation": "0", - "attack_speed": "6", - "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", - "id": "255", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "His armour indicates he's a Khazard Guard.", - "melee_animation": "395", - "range_animation": "0", - "attack_speed": "6", - "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", - "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", + "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", - "attack_level": "1" + "respawn_delay": "25", + "safespot": null, + "strength_level": "47", + "weakness": "7" }, { - "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", + "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", - "attack_level": "46" + "respawn_delay": "35", + "safespot": null, + "strength_level": "67", + "weakness": "7" }, { - "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, + "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", - "strength_level": "27", - "id": "271", + "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", - "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", + "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", - "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", + "respawn_delay": "20", "safespot": null, - "lifepoints": "42", - "strength_level": "30", - "id": "275", - "range_level": "1", - "attack_level": "30" + "strength_level": "72", + "weakness": "8" }, { - "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" + "id": "2038", + "name": "Grish" }, { - "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" + "id": "2039", + "name": "Uglug Nar" }, { - "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" + "id": "2040", + "name": "Pilg" }, { - "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" + "id": "2041", + "name": "Grug" }, { - "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" + "id": "2042", + "name": "Ogre guard" }, { - "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" + "id": "2043", + "name": "Ogre guard" }, { - "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", + "id": "2044", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", + "aggressive": "true", + "attack_level": "20", "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An official of Ardougne.", - "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" - }, - { - "examine": "A serious-looking doctor.", - "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" - }, - { - "examine": "A farmer who's seen happier times.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer Brumty", - "defence_level": "1", - "safespot": null, - "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", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "296", - "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": "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", - "death_animation": "0", - "name": "Hetty", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "307", - "aggressive": "true", - "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", - "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", - "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", - "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": "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": "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" - }, - { - "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", + "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", - "attack_speed": "6", - "magic_level": "27", - "respawn_delay": "25", - "defence_animation": "360", - "weakness": "8", - "magic_animation": "359", - "death_animation": "361", - "name": "Ogre", - "defence_level": "27", + "range_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "60", - "strength_level": "27", - "id": "374", + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" + }, + { + "id": "2045", + "name": "Zogre", + "examine": "A partially decomposing zombie ogre.", "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 pirate.", - "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", + "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", - "attack_level": "1" - }, - { - "examine": "Inspects people's packages.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Customs officer", - "defence_level": "1", + "respawn_delay": "60", "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "380", - "range_level": "1", - "attack_level": "1" + "slayer_exp": "", + "strength_level": "36", + "weakness": "7" }, { - "examine": "A dwarf who looks after the mining guild.", + "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", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "382", - "range_level": "1", - "attack_level": "1" + "strength_level": "34", + "weakness": "6" }, { - "examine": "Not very civilised looking.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Barbarian guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "384", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "examine": "Looks vicious!", + "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", - "combat_audio": "3611,3612,3610", - "defence_animation": "0", - "magic_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", - "name": "Kharid Scorpion", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "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", - "combat_audio": "3611,3612,3610", - "strength_level": "1", - "id": "387", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Could do with a shave...", + "magic_animation": "0", "melee_animation": "0", "range_animation": "0", - "defence_animation": "0", - "slayer_exp": "25", - "magic_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", - "name": "Seer", + "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" + }, + { + "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", - "id": "388", - "range_level": "1", - "attack_level": "1" + "weakness": "" }, { - "examine": "A brightly coloured game bird.", - "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", + "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", - "attack_level": "3" + "respawn_delay": "60", + "strength_level": "1" }, { - "examine": "A brightly coloured game bird.", - "melee_animation": "2371", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "2373", - "name": "River troll", - "defence_level": "3", + "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", - "strength_level": "3", - "id": "392", - "range_level": "1", - "attack_level": "3" + "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" }, { - "examine": "Beefy.", - "slayer_task": "20", + "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", - "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" + "strength_level": "2", + "weakness": "8" }, { - "examine": "A Legends Guild guard; he protects the entrance to the Legends Guild.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "He deals in exotic types of wood.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Jungle forester", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "401", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_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", + "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", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "402", + "lifepoints": "3", + "magic_animation": "5387", + "melee_animation": "5387", + "range_animation": "5387", "range_level": "1", - "attack_level": "1" + "respawn_delay": "18", + "safespot": null, + "strength_level": "1" }, { - "examine": "Insects buzzing around.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Swarm", - "defence_level": "1", + "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", - "id": "411", + "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", - "range_level": "1", - "attack_level": "1" + "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.", - "slayer_task": "5", + "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", - "attack_speed": "5", - "magic_level": "10", - "respawn_delay": "30", - "defence_animation": "4916", - "weakness": "4", + "range_level": "1", + "respawn_delay": "35", + "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" - }, - { - "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": "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": "17", - "safespot": null, - "lifepoints": "50", - "strength_level": "17", - "id": "438", - "range_level": "1", - "attack_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": "32", - "safespot": null, - "lifepoints": "55", - "strength_level": "32", - "id": "439", - "range_level": "1", - "attack_level": "32" - }, - { - "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": "48", - "safespot": null, - "lifepoints": "60", - "strength_level": "48", - "id": "440", - "range_level": "1", - "attack_level": "48" - }, - { - "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": "65", - "safespot": null, - "lifepoints": "86", - "strength_level": "65", - "id": "441", - "range_level": "1", - "attack_level": "65" - }, - { - "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, - "lifepoints": "120", - "strength_level": "100", - "id": "442", - "range_level": "1", - "attack_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", - "defence_level": "130", - "safespot": null, - "lifepoints": "170", - "strength_level": "130", - "id": "443", - "range_level": "1", - "attack_level": "130" - }, - { - "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": "444", - "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": "445", - "clue_level": "0", - "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": "2", - "safespot": null, - "lifepoints": "5", - "strength_level": "5", - "id": "446", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "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", - "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": "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "19" - }, - { - "examine": "A large well built farmer.", - "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" - }, - { - "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", - "range_level": "1", - "attack_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", - "range_level": "1", - "attack_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": "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", - "aggressive": "true", - "range_level": "1", - "attack_level": "38" - }, - { - "examine": "It's one of General Khazard's commanders.", - "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" - }, - { - "examine": "It's a tree gnome trooper.", - "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" - }, - { - "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" - }, - { - "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 2", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "482", - "range_level": "1", - "attack_level": "1" - }, - { - "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 3", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "483", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "It's a young tree gnome.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Local Gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "485", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I guess he wants to be more than just the muscle.", - "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" - }, - { - "examine": "He doesn't look like he'd trust his own mother.", - "slayer_task": "38", - "melee_animation": "6199", - "range_animation": "0", - "combat_audio": "469,472,471", - "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" - }, - { - "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" - }, - { - "examine": "The essence of evil.", - "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" - }, - { - "examine": "A vicious", - "slayer_task": "71", - "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": "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" - }, - { - "examine": "A minion of Rashiliyia.", - "slayer_task": "75", - "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" - }, - { - "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": "504", - "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": "505", - "aggressive": "true", - "range_level": "1", - "attack_level": "33" - }, - { - "melee_animation": "5571", - "respawn_delay": "60", - "defence_animation": "5574", - "death_animation": "5575", - "name": "Nazastarool", - "defence_level": "1", - "safespot": null, - "lifepoints": "100", - "strength_level": "1", - "id": "507", - "aggressive": "true", - "range_level": "1", - "attack_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", - "defence_level": "1", - "safespot": null, - "lifepoints": "110", - "strength_level": "1", - "id": "509", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bob", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "519", - "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": "Shopkeeper", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "520", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Helps sell stuff.", - "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": "521", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes people spending money.", - "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": "522", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes helping sell stuff.", - "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": "523", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A product of a consumerist society.", - "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": "524", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Likes you more", - "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" - }, - { - "examine": "A product of a consumerist society.", - "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" - }, - { - "examine": "Likes you more", - "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": "527", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has an interesting assortment of items for sale.", - "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": "530", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Works on commission.", - "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": "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": "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "552", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "For the interesting clothing items you just can't find elsewhere.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fancy-dress shop owner", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "554", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Has a fine moustache.", - "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": "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "561", - "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-maker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "562", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks strange and mysterious.", - "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": "567", - "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": "569", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Seems very well-off.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gem merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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" - }, - { - "examine": "Seems very well-off.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Silk merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He runs a Mining store.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Drogo dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "579", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "I wonder if he wants to buy my junk?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Dwarf", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "582", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "If crafting's your thing, he's your man!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Rommik", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "574", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He seems to sell tea.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tea seller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "595", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A pizza expert; in both making and eating.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Fat Tony", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "596", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Anyone fancy a trim?", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Hairdresser", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "598", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Indentured servant of a Knight.", - "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" - }, - { - "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": "609", - "range_level": "1", - "attack_level": "26" - }, - { - "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": "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": "613", - "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": "Doug Deeping", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "614", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A student busy studying the digsite.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Student", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "617", - "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": "618", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert on archaeology.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Archaeological expert", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "619", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A specialist in panning for gold.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Panning guide", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "620", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome baller", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "621", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A professional gnome baller.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome winger", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "633", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Keeps the game fair.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome ball referee", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "635", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A dealer in potions.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Apothecary", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "638", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A religious man... And occasional drunk.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Father Lawrence", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "640", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Looks down on his luck.", - "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" - }, - { - "examine": "Looks kind of obsessive...", - "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" - }, - { - "examine": "Looks kind of shifty...", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "498,500,499", - "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" - }, - { - "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", - "attack_level": "1", - "prj_height": "3" - }, - { - "examine": "Guardian of the dramen tree.", - "melee_animation": "5532", - "range_animation": "0", - "attack_speed": "5", - "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" - }, - { - "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.", - "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": "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "658", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He likes to paaaarty!", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Party Pete", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "659", - "range_level": "1", - "attack_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" - }, - { - "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": "An important looking gnome.", - "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" - }, - { - "examine": "The boss!", - "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" - }, - { - "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", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The keeper of the gates to the ranging guild.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ranging Guild Doorman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "679", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An expert leatherworker.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Leatherworker", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "680", - "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": "Held vampyre juvinate", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "681", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Rangers armour.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Armour salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "682", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Supplier of Archery equipment.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Bow and Arrow salesman", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "683", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tower keeper and competition judge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Tower Advisor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "684", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "aggressive": "true", - "range_level": "10", - "attack_level": "7" - }, - { - "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", - "aggressive": "true", - "range_level": "20", - "attack_level": "15" - }, - { - "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", - "aggressive": "true", - "range_level": "30", - "attack_level": "22" + "weakness": "4" }, { - "examine": "Defender of the west 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": "40", - "safespot": null, - "lifepoints": "57", - "strength_level": "30", - "id": "691", - "aggressive": "true", - "range_level": "40", - "attack_level": "30" + "id": "3712", + "name": "Zanik" }, { - "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" + "id": "3713", + "name": "Sigmund" }, { - "examine": "Sells equipment in exchange for archery tickets.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ticket Merchant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "694", - "range_level": "1", - "attack_level": "1" + "id": "3714", + "name": "Zanik" }, { - "examine": "Harlan, ready to teach swordplay.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Melee tutor", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "705", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "An old wizard.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Wizard Mizgog", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "706", - "range_level": "1", - "attack_level": "1" - }, - { - "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, - "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, - "lifepoints": "8", - "strength_level": "1", - "id": "709", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "slayer_exp": "0", - "examine": "It's an NPC.", - "name": "Edmond", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "714", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "731", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "732", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "735", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "737", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "739", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Duke Horacio of Lumbridge.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Duke Horacio", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "741", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A badly-behaved goblin.", - "slayer_task": "38", - "melee_animation": "6184", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6204", - "name": "Wormbrain", - "defence_level": "1", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "745", - "range_level": "1", - "attack_level": "1" - }, - { - "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" - }, - { - "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": "Enraged barbarian spirit", - "defence_level": "76", - "safespot": null, - "lifepoints": "108", - "strength_level": "76", - "id": "750", - "aggressive": "true", - "range_level": "1", - "attack_level": "76" - }, - { - "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": "Berserk barbarian spirit", - "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.", - "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" - }, - { - "examine": "Stop looking and run!", - "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" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "combat_audio": "703,705,704", - "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", - "id": "799", - "range_level": "1", - "attack_level": "30" - }, - { - "examine": "A holy man.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Abbot Langley", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "801", - "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 Jered", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "802", - "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": "803", - "range_level": "1", - "attack_level": "12" - }, - { - "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", - "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": "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": "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", - "safespot": null, - "lifepoints": "7", - "strength_level": "5", - "id": "827", - "aggressive": "true", - "range_level": "1", - "attack_level": "5" - }, - { - "examine": "He looks a bit aggressive.", - "melee_animation": "390", - "range_animation": "0", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "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", - "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", - "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" - }, - { - "examine": "He patrols the Shantay Pass.", - "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" - }, - { - "examine": "A vicious desert wolf.", - "slayer_task": "92", - "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" - }, - { - "death_animation": "9674", - "name": "Ugthanki", - "defence_level": "1", - "safespot": null, - "lifepoints": "46", - "strength_level": "1", - "attack_speed": "5", - "id": "840", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "He looks busy attending to his cart.", - "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" - }, - { - "examine": "This pickaxe has been possessed by a dwarf ancestor spirit.", - "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" - }, - { - "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", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Head chef", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "847", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The famous tree gnome chef.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Aluft Gianne snr.", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "850", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He can serve you gnome food.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Gnome Waiter", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "851", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tough-looking.", - "slayer_task": "64", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "name": "Ogre chieftain", - "defence_level": "58", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", - "id": "852", - "range_level": "1", - "attack_level": "58" - }, - { - "name": "Gorad", - "defence_level": "1", - "safespot": null, - "lifepoints": "81", - "strength_level": "1", - "id": "856", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "859", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "defence_animation": "0", - "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": "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", - "safespot": null, - "lifepoints": "331", - "strength_level": "58", - "id": "870", - "range_level": "1", - "attack_level": "58" - }, - { - "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", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ogre trader", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "875", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Tries to keep the peace.", - "melee_animation": "386", - "range_animation": "0", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "836", - "name": "Tower guard", - "defence_level": "17", - "safespot": null, - "lifepoints": "97", - "strength_level": "17", - "id": "877", - "range_level": "1", - "attack_level": "17" - }, - { - "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", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "67", - "name": "Delrith", - "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" - }, - { - "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.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Ceril Carnillean", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "885", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "887", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "death_animation": "836", - "name": "Clivet", - "defence_level": "1", - "safespot": null, - "lifepoints": "25", - "melee_animation": "811", - "strength_level": "1", - "id": "893", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "examine": "A member of the Hazeel cult.", - "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" - }, - { - "examine": "Children are just like real people...just smaller.", - "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" - }, - { - "examine": "He hasn't seen much sun lately.", - "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" - }, - { - "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": "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 RuneScape.", - "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": "An official representative from the First National Bank of RuneScape.", - "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 RuneScape.", - "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": "496,498,497", - "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": "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" - }, - { - "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": "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 green skinned croaker", - "melee_animation": "0", - "range_animation": "0", - "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", - "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", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "32", - "safespot": null, - "movement_radius": "4", - "lifepoints": "45", - "strength_level": "32", - "id": "1020", - "aggressive": "true", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "An air elemental.", - "melee_animation": "1040", - "range_animation": "0", - "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", - "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": "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.", - "name": "Soldier", - "defence_level": "44", - "safespot": null, - "lifepoints": "55", - "combat_audio": "511,513,512", - "strength_level": "44", - "id": "1065", - "magic_level": "1", - "range_level": "1", - "attack_level": "44" - }, - { - "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": "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": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "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": "0", - "range_animation": "0", - "combat_audio": "511,513,512", - "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "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": "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": "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" - }, - { - "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": "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": "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", - "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", - "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": "498,500,499", - "respawn_delay": "60", - "defence_animation": "4927", - "slayer_exp": "0", - "death_animation": "4929", - "name": "Grizzly bear", - "defence_level": "1", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "1195", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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": "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 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 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" - }, - { - "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", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "1313", - "weakness": "7", - "magic_animation": "1312", - "death_animation": "1314", - "name": "Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "51", - "strength_level": "1", - "id": "1265", - "bonuses": "-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75", - "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", - "attack_speed": "5", - "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": "-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75", - "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": "Supposedly fixes things around RuneScape.", - "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": "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": "496,498,497", - "attack_speed": "5", - "magic_level": "6", - "respawn_delay": "32", - "defence_animation": "6375", - "weakness": "7", - "magic_animation": "6376", - "death_animation": "6377", - "name": "Unicorn Foal", - "defence_level": "6", - "safespot": null, - "lifepoints": "14", - "strength_level": "6", - "id": "1328", - "range_level": "1", - "attack_level": "6" - }, - { - "examine": "Cute but evil.", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "496,498,497", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Black unicorn Foal", - "defence_level": "12", - "safespot": null, - "lifepoints": "17", - "strength_level": "12", - "id": "1329", - "range_level": "1", - "attack_level": "12" - }, - { - "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": "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 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" - }, - { - "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": "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" - }, - { - "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, - "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" - }, - { - "examine": "A large boisterous bird", - "slayer_task": "7", - "melee_animation": "6761", - "range_animation": "0", - "defence_animation": "0", - "weakness": "3", - "magic_animation": "0", - "death_animation": "6762", - "name": "Chompy bird", - "defence_level": "1", - "safespot": null, - "lifepoints": "8", - "strength_level": "1", - "id": "1550", - "range_level": "2", - "attack_level": "1" - }, - { - "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": "449,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": "449,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": "449,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": "449,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": "449,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": "408,410,409", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "Flies like a rock.", - "melee_animation": "9454", - "range_animation": "1516", - "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", - "attack_speed": "5", - "defence_animation": "9451", - "slayer_exp": "22", - "magic_animation": "1523", - "death_animation": "9450", - "name": "Banshee", - "defence_level": "5", - "safespot": null, - "lifepoints": "22", - "strength_level": "14", - "id": "1612", - "aggressive": "true", - "clue_level": "0", - "range_level": "1", - "attack_level": "9" - }, - { - "examine": "An evil death demon.", - "melee_animation": "9487", - "range_animation": "1528", - "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", - "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", - "attack_speed": "5", - "magic_level": "75", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "80", - "id": "1616", - "range_level": "75", - "attack_level": "80" - }, - { - "examine": "The eyes of evil.", - "melee_animation": "260", - "range_animation": "260", - "attack_speed": "5", - "magic_level": "75", - "defence_animation": "261", - "slayer_exp": "75", - "magic_animation": "260", - "death_animation": "264", - "name": "Basilisk", - "defence_level": "75", - "safespot": null, - "lifepoints": "75", - "strength_level": "80", - "id": "1617", - "range_level": "75", - "attack_level": "80" - }, - { - "examine": "The tongue of evil.", - "melee_animation": "9130", - "range_animation": "1552", - "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", - "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", - "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", - "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": "The vacuumed face of evil.", - "slayer_task": "28", - "melee_animation": "1557", - "range_animation": "1557", - "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": "A rocky slug.", - "slayer_task": "69", - "melee_animation": "9506", - "range_animation": "9506", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "9508", - "weakness": "7", - "magic_animation": "9506", - "death_animation": "9507", - "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": "9506", - "range_animation": "9506", - "attack_speed": "5", - "magic_level": "1", - "defence_animation": "9508", - "weakness": "7", - "magic_animation": "9506", - "death_animation": "9507", - "name": "Rockslug", - "defence_level": "30", - "safespot": null, - "lifepoints": "30", - "strength_level": "30", - "id": "1632", - "clue_level": "2", - "range_level": "1", - "attack_level": "24" - }, - { - "spawn_animation": "8081", - "examine": "A small fire demon.", - "slayer_task": "66", - "melee_animation": "8080", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "Now THAT'S handy.", - "slayer_task": "21", - "melee_animation": "9125", - "range_animation": "9125", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "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" - }, - { - "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 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": "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": "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": "38", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "3", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "836", - "name": "Tortured soul", - "defence_level": "38", - "safespot": null, - "lifepoints": "85", - "strength_level": "28", - "id": "1698", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "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": "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" - }, - { - "death_animation": "1013", - "name": "Terrorbird", - "defence_level": "1", - "safespot": null, - "lifepoints": "34", - "melee_animation": "1010", - "strength_level": "1", - "id": "1751", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "1012" - }, - { - "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", - "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", - "range_animation": "0", - "attack_speed": "5", - "poisonous": "true", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "1", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1792", - "name": "Cave slime", - "defence_level": "19", - "safespot": null, - "lifepoints": "27", - "strength_level": "1", - "id": "1831", - "clue_level": "0", - "range_level": "19", - "attack_level": "1" - }, - { - "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'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": "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 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 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": "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" - }, - { - "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", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "6567", - "name": "Jackal", - "defence_level": "35", - "safespot": null, - "lifepoints": "100", - "strength_level": "35", - "id": "1994", - "aggressive": "true", - "range_level": "1", - "attack_level": "35" - }, - { - "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": "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", - "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": "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": "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": "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": "A rich landlord", - "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": "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 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" - }, - { - "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" - }, - { - "combat_style": "1", - "melee_animation": "190", - "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" - }, - { - "combat_style": "1", - "melee_animation": "190", - "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", - "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", - "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": "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" - }, - { - "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" - }, - { - "name": "", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "strength_level": "1", - "id": "2309", - "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": "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": "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", - "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", - "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", - "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", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "68", - "id": "2452", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "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", - "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": "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": "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" - }, - { - "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 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" - }, - { - "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" - }, - { - "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": "469,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", - "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" - }, - { - "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": "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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": "From a darker dimension.", - "slayer_task": "25", - "melee_animation": "2731", - "range_animation": "2731", - "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", - "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": "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" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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" - }, - { - "melee_animation": "1312", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2885", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2862", - "name": "Rock lobster", - "defence_level": "100", - "safespot": null, - "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" - }, - { - "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" - }, - { - "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", - "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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", - "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", - "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", - "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", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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", - "attack_speed": "4", - "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": "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", - "combat_style": "1", - "melee_animation": "1584", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "2", - "slayer_exp": "25", - "magic_animation": "0", - "death_animation": "1585", - "name": "Harpie Bug Swarm", - "defence_level": "45", - "safespot": null, - "lifepoints": "64", - "strength_level": "1", - "id": "3153", - "aggressive": "true", - "clue_level": "1", - "range_level": "45", - "attack_level": "1" - }, - { - "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" - }, - { - "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 RuneScape's many citizens", - "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": "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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": "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": "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, - "lifepoints": "10", - "strength_level": "1", - "id": "3322", - "range_level": "1", - "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", - "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", - "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": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fun guy. No wait", - "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" - }, - { - "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" - }, - { - "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": "408,410,409", - "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", - "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 may be a funky cook", - "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" - }, - { - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "24", - "safespot": null, - "lifepoints": "34", - "strength_level": "24", - "id": "3422", - "range_level": "1", - "attack_level": "24" - }, - { - "death_animation": "3435", - "name": "Mudskipper", - "defence_level": "1", - "safespot": null, - "lifepoints": "20", - "melee_animation": "3433", - "strength_level": "1", - "id": "3423", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3434" - }, - { - "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" - }, - { - "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": "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": "408,410,409", - "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": "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" - }, - { - "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": "498,500,499", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "name": "Angry bear", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3645", - "range_level": "1", - "attack_level": "25" - }, - { - "examine": "He looks a little on the cross side!", - "melee_animation": "6376", - "range_animation": "0", - "combat_audio": "496,498,497", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "6377", - "name": "Angry unicorn", - "defence_level": "25", - "safespot": null, - "lifepoints": "35", - "strength_level": "25", - "id": "3646", - "range_level": "1", - "attack_level": "25" - }, - { - "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 RuneScape 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" - }, - { - "name": "Angry unicorn", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "496,498,497", - "strength_level": "1", - "id": "3661", - "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": "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" - }, - { - "name": "Angry bear", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "498,500,499", - "strength_level": "1", - "id": "3664", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "melee_animation": "2019", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "2021", - "name": "Vulture", - "defence_level": "38", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "3675", - "range_level": "1", - "attack_level": "38" - }, - { - "death_animation": "2026", - "name": "Vulture", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "melee_animation": "2025", - "strength_level": "1", - "id": "3676", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "2024" - }, - { - "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" - }, - { - "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", + "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", - "attack_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", - "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" + "safespot": null, + "strength_level": "2" }, { - "examine": "Don't burst its bubble!", - "melee_animation": "3891", - "range_animation": "3891", - "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", + "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", - "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Don't burst its bubble!", - "melee_animation": "3891", - "range_animation": "3891", - "magic_level": "10", - "respawn_delay": "120", - "defence_animation": "3890", - "magic_animation": "3891", + "combat_audio": "809,811,810", "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", + "lifepoints": "13", + "magic_animation": "3891", + "magic_level": "5", + "melee_animation": "3891", + "range_animation": "3891", "range_level": "1", - "attack_level": "20" - }, - { - "examine": "Worse than termites!", - "melee_animation": "3915", - "range_animation": "3915", - "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", - "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", - "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", - "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" + "weakness": "8" }, { - "examine": "Eeewww!", - "melee_animation": "3908", - "range_animation": "3908", + "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", - "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", + "melee_animation": "3891", + "range_animation": "3891", "range_level": "1", - "attack_level": "30" + "respawn_delay": "120", + "safespot": null, + "strength_level": "35" }, { - "examine": "Eeewww!", - "melee_animation": "3908", - "range_animation": "3908", + "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", - "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", + "melee_animation": "3891", + "range_animation": "3891", "range_level": "1", - "attack_level": "40" - }, - { - "examine": "Eeewww!", - "melee_animation": "3908", - "range_animation": "3908", - "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", - "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", - "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", + "strength_level": "40" + }, + { + "id": "3732", + "name": "Shifter", + "examine": "Nippy little thing!", "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "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", - "magic_level": "14", - "respawn_delay": "120", - "defence_animation": "3895", - "weakness": "9", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", + "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, - "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" + "weakness": "9" }, { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "magic_level": "21", - "respawn_delay": "120", - "defence_animation": "3895", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", + "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", - "strength_level": "42", - "id": "3773", - "aggressive": "true", - "bonuses": "45,45,60,50,50,50,50,15,50,0,0,0,0,0,0", + "magic_animation": "3901", + "magic_level": "35", + "melee_animation": "3901", + "poison_immune": "true", + "range_animation": "3901", "range_level": "1", - "attack_level": "42" - }, - { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "magic_level": "32", - "respawn_delay": "120", - "defence_animation": "3895", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", - "defence_level": "56", + "respawn_delay": "127", "safespot": null, - "lifepoints": "113", - "strength_level": "56", - "id": "3774", + "strength_level": "70" + }, + { + "id": "3741", + "name": "Shifter", + "examine": "Nippy little thing!", "aggressive": "true", - "bonuses": "45,45,60,80,80,80,80,25,50,0,0,0,0,0,0", + "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", - "attack_level": "56" - }, - { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "magic_level": "38", - "respawn_delay": "120", - "defence_animation": "3895", - "magic_animation": "3896", - "death_animation": "3894", - "name": "Brawler", - "defence_level": "74", + "respawn_delay": "127", "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" + "strength_level": "70" }, { - "examine": "Mind your toes!", - "melee_animation": "3896", - "range_animation": "3896", - "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.", + "id": "3742", + "name": "Ravager", + "examine": "Worse than termites!", + "attack_level": "15", "attack_speed": "5", - "magic_level": "80", + "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", - "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" + "strength_level": "30", + "weakness": "7" }, { - "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.", + "id": "3743", + "name": "Ravager", + "examine": "Worse than termites!", + "attack_level": "20", "attack_speed": "5", - "magic_level": "80", + "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", - "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" + "strength_level": "40" }, { - "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": "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", + "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, - "lifepoints": "42", - "strength_level": "1", - "id": "3814", - "aggressive": "true", - "range_level": "30", - "attack_level": "1" + "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", - "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" + "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", - "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" + "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", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "3962", - "name": "Tortoise", - "defence_level": "36", + "range_level": "1", "safespot": null, - "lifepoints": "51", "strength_level": "36", - "id": "3819", - "range_level": "1", - "attack_level": "36" + "weakness": "6" }, { - "examine": "An aquatic troll.", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "name": "Sea troll", - "defence_level": "45", + "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, - "lifepoints": "64", - "strength_level": "45", + "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", - "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", + "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", - "attack_level": "60" + "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", - "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" + "respawn_delay": "60", + "safespot": null, + "strength_level": "65", + "weakness": "10" }, { - "examine": "His bark's worse than his blight.", + "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", - "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" + "safespot": null, + "strength_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" + "id": "3850", + "name": "Skeleton Mage" }, { - "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" - }, - { - "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", - "strength_level": "1", - "id": "4228", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A denizen of the Abyss!", - "slayer_task": "1", - "melee_animation": "1537", - "range_animation": "1537", + "id": "3851", + "name": "Skeleton Mage", + "examine": "Doesn't seem to want to go away.", + "attack_level": "10", "attack_speed": "4", - "magic_level": "1", + "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", - "weakness": "1", - "slayer_exp": "150", - "magic_animation": "1537", - "death_animation": "1538", - "name": "Abyssal demon", - "defence_level": "135", + "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, - "lifepoints": "150", - "strength_level": "67", + "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", - "attack_level": "97" + "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" }, { - "name": "Demon butler", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "400,404,403", - "strength_level": "1", "id": "4243", + "name": "Demon butler", + "attack_level": "1", + "combat_audio": "400,404,403", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" + }, + { + "id": "4244", + "name": "Chief servant" }, { - "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Taxidermist", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "4246", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A fancy businessman with a mighty fine hat.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Estate agent", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "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.", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Stonemason", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "4248", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "He changes the shape of wood.", + "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", - "combat_audio": "703,705,704", - "defence_animation": "0", - "facing_booth": "true", - "magic_animation": "0", - "death_animation": "0", - "name": "Sawmill operator", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { + "id": "4249", + "name": "Sir Renitee", + "examine": "Chief Herald of Falador." + }, + { "id": "4250", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "She has green fingers. (Not literally.)", + "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", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Garden supplier", - "defence_level": "1", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "strength_level": "1" + }, + { "id": "4251", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Guard", + "name": "Garden supplier", + "examine": "She has green fingers. (Not literally.)", + "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": "4257", - "clue_level": "1", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_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.", - "slayer_task": "29", + "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", - "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": "4258", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "12" + "weakness": "6" }, { - "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", + "attack_level": "1", "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_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", + "id": "4260", + "name": "Guard", + "attack_level": "1", + "clue_level": "1", + "combat_audio": "511,513,512", "defence_level": "1", - "safespot": null, "lifepoints": "10", - "strength_level": "2", + "range_level": "1", + "safespot": null, + "strength_level": "1" + }, + { "id": "4261", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4262", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4263", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4264", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4265", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4266", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4267", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4268", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4269", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4270", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4271", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4272", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4273", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4274", - "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", + "range_level": "1", "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "slayer_exp": "5", + "strength_level": "2" + }, + { "id": "4275", - "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", + "range_level": "1", + "safespot": null, "slayer_exp": "5", - "magic_animation": "6185", - "death_animation": "6182", - "name": "Goblin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "2", + "strength_level": "2" + }, + { "id": "4276", + "name": "Goblin", + "examine": "An ugly green creature.", + "attack_level": "1", + "attack_speed": "5", "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Animated bronze armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Bronze Armour", - "defence_level": "8", - "safespot": null, + "combat_audio": "469,472,471", + "death_animation": "6182", + "defence_animation": "6183", + "defence_level": "1", "lifepoints": "10", - "strength_level": "8", + "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", - "bonuses": "3,3,3,3,1,3,1,3,3,3,3,8,10,8,3", + "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", - "attack_level": "8" + "respawn_delay": "20", + "safespot": null, + "strength_level": "10", + "weakness": "7" }, { - "examine": "Animated iron armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Iron Armour", - "defence_level": "16", - "safespot": null, - "lifepoints": "20", - "strength_level": "16", "id": "4279", - "bonuses": "6,6,6,6,2,6,6,6,6,6,6,2,25,2,3", + "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", - "attack_level": "16" + "respawn_delay": "20", + "safespot": null, + "strength_level": "20", + "weakness": "7" }, { - "examine": "Animated steel armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Steel Armour", - "defence_level": "32", - "safespot": null, - "lifepoints": "40", - "strength_level": "32", "id": "4280", - "bonuses": "10,10,10,10,4,10,10,10,10,10,10,2,22,2,3", - "range_level": "1", - "attack_level": "32" - }, - { - "examine": "Animated black armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", + "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", - "name": "Animated Black Armour", + "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, - "lifepoints": "60", "strength_level": "40", + "weakness": "7" + }, + { "id": "4281", - "bonuses": "15,15,15,15,5,15,6,15,15,16,15,5,27,5,3", + "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", - "attack_level": "40" + "respawn_delay": "20", + "safespot": null, + "strength_level": "60", + "weakness": "7" }, { - "examine": "Animated mithril armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Mithril Armour", - "defence_level": "55", - "safespot": null, - "lifepoints": "80", - "strength_level": "55", "id": "4282", - "bonuses": "20,20,20,20,20,20,8,20,20,20,20,5,31,5,3", + "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", - "attack_level": "55" + "respawn_delay": "20", + "safespot": null, + "strength_level": "80", + "weakness": "7" }, { - "examine": "Animated adamant armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", - "death_animation": "4167", - "name": "Animated Adamant Armour", - "defence_level": "75", - "safespot": null, - "lifepoints": "99", - "strength_level": "65", "id": "4283", - "bonuses": "25,25,25,25,25,25,12,25,25,25,25,5,35,5,3", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "Animated rune armour.", - "melee_animation": "386", - "range_animation": "386", - "attack_speed": "5", - "respawn_delay": "20", - "defence_animation": "388", - "weakness": "7", - "magic_animation": "386", + "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", - "name": "Animated Rune Armour", - "defence_level": "85", + "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, - "lifepoints": "118", - "strength_level": "75", + "strength_level": "99", + "weakness": "7" + }, + { "id": "4284", - "bonuses": "30,30,30,30,30,30,16,30,25,30,30,5,39,5,3", + "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", - "attack_level": "75" + "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." }, { - "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": "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", - "range_level": "1", - "attack_level": "65" - }, - { - "examine": "A one-eyed woman eater.", - "slayer_task": "44", + "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", - "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": "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" + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "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", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "name": "Guard", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "combat_audio": "511,513,512", - "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", - "attack_level": "1" + "safespot": null, + "strength_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", + "attack_level": "1", "clue_level": "1", + "combat_audio": "511,513,512", + "defence_level": "1", + "lifepoints": "10", "range_level": "1", - "attack_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", - "combat_audio": "511,513,512", - "attack_speed": "5", + "range_level": "1", "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" + "weakness": "9" }, { - "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", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "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", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_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", + "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", - "attack_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.", - "slayer_task": "5", + "attack_level": "31", + "death_animation": "4917", + "defence_animation": "0", + "defence_level": "31", + "lifepoints": "44", + "magic_animation": "0", "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" + "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", - "attack_speed": "10", + "range_level": "1", "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" + "weakness": "8" }, { - "examine": "A horrible", - "slayer_task": "52", - "melee_animation": "4235", - "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "48", - "safespot": null, - "lifepoints": "68", - "strength_level": "48", "id": "4348", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "48" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", - "id": "4349", + "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", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "A horrible", - "slayer_task": "52", + "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "slayer_exp": "45", - "magic_animation": "0", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "48", + "range_level": "1", + "respawn_delay": "50", "safespot": null, - "lifepoints": "68", - "strength_level": "48", + "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", - "attack_level": "48" + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "45", + "strength_level": "70", + "weakness": "7" }, { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", - "name": "Jungle horror", - "defence_level": "1", - "safespot": null, - "lifepoints": "45", - "strength_level": "1", "id": "4351", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4234", - "respawn_delay": "60", - "defence_animation": "4232", - "slayer_exp": "45", - "death_animation": "4233", "name": "Jungle horror", - "defence_level": "1", - "safespot": null, + "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", - "strength_level": "1", + "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", - "attack_level": "1" + "respawn_delay": "50", + "safespot": null, + "slayer_exp": "45", + "strength_level": "70" }, { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", - "melee_animation": "4234", - "range_animation": "4234", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "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", + "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", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", + "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "defence_animation": "4232", - "weakness": "6", - "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", + "range_level": "1", + "respawn_delay": "50", "safespot": null, - "lifepoints": "55", + "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", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", + "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "defence_animation": "4232", - "weakness": "6", - "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", + "range_level": "1", + "respawn_delay": "50", "safespot": null, - "lifepoints": "55", + "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", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", + "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "defence_animation": "4232", - "weakness": "6", - "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", + "range_level": "1", + "respawn_delay": "50", "safespot": null, - "lifepoints": "55", + "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", - "range_level": "1", - "attack_level": "80" - }, - { - "examine": "A horrible, emaciated ape like creature with beady red eyes.", - "slayer_task": "17", + "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", - "attack_speed": "4", - "magic_level": "80", - "respawn_delay": "20", - "defence_animation": "4232", - "weakness": "6", + "range_level": "1", + "respawn_delay": "50", + "safespot": null, "slayer_exp": "55", - "magic_animation": "4234", - "death_animation": "4233", - "name": "Cave horror", - "defence_level": "62", - "safespot": null, - "lifepoints": "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", - "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" - }, - { - "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 RuneScape 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" - }, - { - "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": "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": "4392", - "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", - "range_level": "1", - "attack_level": "24" - }, - { - "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": "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": "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": "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": "Examine not added", - "slayer_task": "58", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "weakness": "7", - "slayer_exp": "10", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "10", - "safespot": null, - "lifepoints": "11", - "strength_level": "10", - "id": "4404", - "bonuses": "8,8,10,8,8,10,8,8,8,10,8,8,8,8,8", - "clue_level": "0", - "range_level": "1", - "attack_level": "10" - }, - { - "examine": "Examine not added", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "15", - "safespot": null, - "lifepoints": "17", - "strength_level": "1", - "id": "4405", - "bonuses": "11,11,13,11,11,13,11,11,11,13,11,11,11,11,11", - "clue_level": "0", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "Examine not added", - "melee_animation": "4266", - "range_animation": "4266", - "attack_speed": "5", - "respawn_delay": "30", - "defence_animation": "4267", - "slayer_exp": "22", - "magic_animation": "4266", - "death_animation": "4265", - "name": "Minotaur", - "defence_level": "19", - "safespot": null, - "lifepoints": "20", - "strength_level": "1", - "id": "4406", - "bonuses": "15,15,15,15,15,15,15,15,15,15,15,15,15,15,15", - "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": "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": "Examine not added", - "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": "69", - "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": "Examine not added", - "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": "69", - "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.", - "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": "4415", - "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": "Half horse", - "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", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "7", - "magic_animation": "0", - "death_animation": "6377", - "name": "Stag", - "defence_level": "18", - "safespot": null, - "lifepoints": "25", - "strength_level": "18", - "id": "4440", - "range_level": "1", - "attack_level": "18" - }, - { - "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": "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": "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", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4527", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4528", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4529", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4530", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "melee_animation": "4388", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "4385", - "death_animation": "4389", - "name": "Suqah", - "defence_level": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4531", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4532", - "aggressive": "true", - "clue_level": "2", - "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": "1", - "safespot": null, - "lifepoints": "105", - "strength_level": "1", - "id": "4533", - "aggressive": "true", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "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", - "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", - "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" - }, - { - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "4667", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Baby red dragon", - "defence_level": "1", - "safespot": null, - "lifepoints": "50", - "combat_audio": "408,410,409", - "strength_level": "1", - "id": "4668", - "aggressive": "true", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" - }, - { - "examine": "He's got icicles in his beard.", - "slayer_task": "47", - "melee_animation": "4672", - "range_animation": "0", - "combat_audio": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "449,451,450", - "attack_speed": "5", - "magic_level": "1", - "respawn_delay": "60", - "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": "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": "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": "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": "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": "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" - }, - { - "melee_animation": "4868", - "attack_speed": "7", - "respawn_delay": "60", - "defence_animation": "4869", - "death_animation": "4870", - "name": "Earth elemental", - "defence_level": "1", - "safespot": null, - "lifepoints": "35", - "strength_level": "1", - "id": "4910", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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": "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": "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 RuneScape is that?", - "melee_animation": "0", - "range_animation": "0", - "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": "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" - }, - { - "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", - "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", + "combat_audio": "496,500,499", + "death_animation": "4233", + "defence_animation": "4232", "defence_level": "62", - "safespot": null, - "lifepoints": "571", - "strength_level": "62", - "id": "5253", - "aggressive": "true", - "range_level": "62", - "attack_level": "62" - }, - { - "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", + "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", - "weakness": "10", + "defence_level": "1", + "lifepoints": "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" - }, - { - "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", + "melee_animation": "0", + "range_animation": "0", "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": "22", - "strength_level": "1", - "id": "5294", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" + "strength_level": "1" }, { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", + "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", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5295", - "aggressive": "true", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", + "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", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5296", - "aggressive": "true", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", + "id": "4363", + "name": "Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5297", - "aggressive": "true", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", + "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", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5298", - "aggressive": "true", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", + "id": "4372", + "name": "Red Monkey", + "attack_level": "1", + "combat_audio": "629,631,630", "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5299", - "aggressive": "true", + "lifepoints": "10", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", + "id": "4373", + "name": "Parrot", + "examine": "A colourful bird.", + "attack_level": "1", + "death_animation": "0", + "defence_animation": "0", "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5300", - "aggressive": "true", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_level": "1" }, { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", + "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", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5301", - "aggressive": "true", + "lifepoints": "10", + "magic_animation": "0", + "melee_animation": "0", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_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": "22", - "strength_level": "1", - "id": "5302", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" + "id": "4376", + "name": "Litara", + "examine": "Litara, a barbarian explorer." }, { - "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" + "id": "4377", + "name": "Gate of War" }, { - "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" + "id": "4378", + "name": "Ricketty door" }, { - "examine": "Examine not added", - "melee_animation": "5568", - "range_animation": "5568", - "combat_audio": "931,923,922", + "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", - "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" - }, - { - "melee_animation": "5568", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5567", - "slayer_exp": "30", - "death_animation": "5569", - "name": "Zombie", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", "defence_level": "1", - "safespot": null, - "lifepoints": "22", - "strength_level": "1", - "id": "5306", - "aggressive": "true", + "lifepoints": "68", + "melee_animation": "5485", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1" }, { - "examine": "Examine not added", - "melee_animation": "5568", - "range_animation": "5568", - "combat_audio": "931,923,922", + "id": "4385", + "name": "Skeleton", + "examine": "Could do with gaining a few pounds.", + "aggressive": "true", + "attack_level": "34", "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" - }, - { - "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": "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": "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": "A minion of Rashiliyia.", - "slayer_task": "75", + "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", - "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", - "attack_speed": "6", - "magic_level": "77", - "defence_animation": "1581", - "weakness": "4", - "magic_animation": "1582", - "death_animation": "1580", - "name": "Waterfiend", - "defence_level": "50", - "poison_immune": "true", - "safespot": null, - "lifepoints": "128", - "strength_level": "167", - "id": "5361", - "aggressive": "true", - "range_level": "167", - "projectile": "16", - "attack_level": "70" - }, - { - "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" - }, - { - "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": "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", - "attack_speed": "4", - "magic_level": "1", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "5209", - "name": "Ice Troll King", - "defence_level": "80", - "safespot": null, - "lifepoints": "150", - "strength_level": "65", - "id": "5472", - "aggressive": "true", - "bonuses": "60,60,60,0,0,45,45,45,2000,2000,0,60,0,0,0", - "range_level": "1", - "attack_level": "100" - }, - { - "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" - }, - { - "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": "Jatizso's Chancellor.", - "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" - }, - { - "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": "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": "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 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": "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 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": "Grundt", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "5501", - "range_level": "1", - "attack_level": "1" - }, - { - "examine": "The Burgher's protectors.", - "melee_animation": "395", - "range_animation": "0", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "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": "395", - "range_animation": "0", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "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": "395", - "range_animation": "0", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "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", - "attack_speed": "5", - "defence_animation": "5783", - "weakness": "7", - "magic_animation": "5782", - "death_animation": "5784", - "name": "Yak", - "defence_level": "30", - "safespot": null, - "lifepoints": "50", - "strength_level": "7", - "id": "5529", - "range_level": "1", - "attack_level": "3" - }, - { - "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": "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", + "safespot": null, + "strength_level": "34", + "weakness": "7" + }, + { + "id": "4386", + "name": "Skeleton", + "aggressive": "true", "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", + "combat_audio": "774,775,777", + "death_animation": "5491", + "defence_animation": "5513", "defence_level": "1", - "safespot": null, - "lifepoints": "60", - "strength_level": "1", - "id": "5596", - "aggressive": "true", + "lifepoints": "85", + "melee_animation": "5485", "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" + "strength_level": "1" }, { - "examine": "Half unicorn", - "melee_animation": "5849", - "range_animation": "0", + "id": "4387", + "name": "Ghost", + "aggressive": "true", + "attack_level": "1", "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", + "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, - "lifepoints": "10", "strength_level": "1", - "id": "5608", - "range_level": "1", - "attack_level": "1" + "weakness": "5" }, { - "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", + "id": "4389", + "name": "Flesh Crawler", + "examine": "I don't think insect repellent will work.", "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", + "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", - "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", - "respawn_delay": "60", - "defence_animation": "6013", - "slayer_exp": "52", - "death_animation": "6014", - "name": "Molanisk", - "defence_level": "1", - "safespot": null, - "lifepoints": "52", - "strength_level": "1", - "id": "5751", - "clue_level": "1", - "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": "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": "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": "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", - "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", + "magic_animation": "1184", + "melee_animation": "1184", + "range_animation": "1184", "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", + "respawn_delay": "30", "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": "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 RuneScape 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 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": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Terrorbird display", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "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": "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" - }, - { - "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" - }, - { - "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" - }, - { - "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", + "slayer_exp": "25", "strength_level": "2", - "id": "6088", - "range_level": "1", - "attack_level": "2" + "weakness": "0" }, { - "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", + "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, - "lifepoints": "2", - "strength_level": "2", - "id": "6089", - "range_level": "1", - "attack_level": "2" + "slayer_exp": "25", + "strength_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", + "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, - "lifepoints": "2", - "strength_level": "2", - "id": "6090", - "range_level": "1", - "attack_level": "2" + "slayer_exp": "25", + "strength_level": "2" }, { - "melee_animation": "5571", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5574", - "slayer_exp": "30", - "death_animation": "5575", + "id": "4392", "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "6099", + "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", - "attack_level": "1" - }, - { - "melee_animation": "5571", - "combat_audio": "931,923,922", - "respawn_delay": "60", - "defence_animation": "5574", + "respawn_delay": "30", + "safespot": null, "slayer_exp": "30", - "death_animation": "5575", + "strength_level": "1" + }, + { + "id": "4393", "name": "Zombie", - "defence_level": "1", - "safespot": null, - "lifepoints": "30", - "strength_level": "1", - "id": "6100", + "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", - "attack_level": "1" - }, - { - "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", + "respawn_delay": "30", "safespot": null, - "lifepoints": "85", - "strength_level": "60", - "id": "6102", - "range_level": "1", - "attack_level": "60" + "slayer_exp": "30", + "strength_level": "1" }, { - "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", + "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", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "6108", + "lifepoints": "2", + "magic_animation": "2705", + "melee_animation": "2705", + "range_animation": "2705", "range_level": "1", - "attack_level": "1" + "safespot": null, + "strength_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", + "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", - "safespot": null, - "lifepoints": "54", - "strength_level": "38", - "id": "6109", - "range_level": "1", - "attack_level": "38" - }, - { - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "melee_animation": "6790", - "strength_level": "1", - "id": "6110", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6792" - }, - { - "death_animation": "6791", - "name": "Mounted terrorbird gnome", - "defence_level": "1", - "safespot": null, - "lifepoints": "55", - "melee_animation": "6790", - "strength_level": "1", - "id": "6111", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "6792" - }, - { - "examine": "Aww", - "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" - }, - { - "death_animation": "750", - "name": "Duck", - "defence_level": "1", - "safespot": null, - "lifepoints": "3", - "melee_animation": "747", - "strength_level": "1", - "id": "6113", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "3268" - }, - { - "examine": "A messy bird.", - "slayer_task": "7", - "melee_animation": "3467", - "range_animation": "3467", - "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", - "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": "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", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "8", - "magic_animation": "0", - "death_animation": "836", - "name": "Black Knight", - "defence_level": "28", - "safespot": null, - "lifepoints": "40", - "strength_level": "28", - "id": "6189", - "aggressive": "true", - "range_level": "1", - "attack_level": "28" - }, - { - "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" - }, - { - "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,0", - "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" - }, - { - "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": "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, - "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", - "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", - "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", - "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" - }, - { - "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": "true", - "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": "true", - "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": "The bane of darkness.", - "melee_animation": "6376", - "range_animation": "6376", - "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": "true", - "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", - "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" - }, - { - "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 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,0", - "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": "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 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", + "magic_animation": "4272", + "magic_level": "38", + "melee_animation": "4271", "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", + "respawn_delay": "74", "safespot": null, - "lifepoints": "71", - "strength_level": "50", - "id": "6380", - "aggressive": "true", - "range_level": "1", - "attack_level": "50" + "slayer_exp": "50", + "spell_id": "7", + "strength_level": "38" }, { - "examine": "That stinger has to hurt...", - "melee_animation": "7242", - "range_animation": "0", + "id": "4398", + "name": "Catablepon", + "examine": "Big Cow-like... But cows don't have serpent tails!", + "aggressive": "true", + "attack_level": "38", "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", - "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" - }, - { - "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", + "bonuses": "20,25,10,20,25,10,10,11,0,0,0,0,0,0,0", "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" - }, - { - "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": "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" - }, - { - "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", - "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", - "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", - "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" - }, - { - "combat_style": "2", - "melee_animation": "7620", - "respawn_delay": "60", - "defence_animation": "7617", - "death_animation": "7616", - "name": "Scabaras mage", - "defence_level": "1", - "safespot": null, + "combat_audio": "511,330,329", + "death_animation": "4270", + "defence_animation": "4273", + "defence_level": "38", "lifepoints": "70", - "strength_level": "1", - "id": "6781", + "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", - "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": "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" - }, - { - "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": "496,498,497", - "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": "496,498,497", - "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" - }, - { - "examine": "Southern fried please!", - "combat_style": "2", - "melee_animation": "7810", - "range_animation": "7810", + "attack_level": "40", "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" - }, - { - "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" - }, - { - "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", + "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", - "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", + "melee_animation": "4271", "range_level": "1", - "respawn_delay": "0", - "attack_level": "1", - "defence_animation": "7817" + "respawn_delay": "74", + "safespot": null, + "slayer_exp": "50", + "spell_id": "7", + "strength_level": "58" }, { - "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" - }, - { - "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": "408,410,409", - "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": "408,410,409", - "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": "408,410,409", - "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": "408,410,409", - "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" - }, - { - "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" - }, - { - "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 Runescape", - "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" - }, - { - "examine": "A very large elemental adversary.", - "slayer_task": "33", - "melee_animation": "4666", - "range_animation": "4666", - "combat_audio": "449,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": "449,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", + "id": "4400", + "name": "Giant spider", + "examine": "I think this spider has been genetically modified.", "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", - "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", - "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", + "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", - "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": "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", - "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": "7129", - "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": "The head farmer", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", - "name": "Farmer Blinkin", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7131", - "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": "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", - "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", - "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", - "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", - "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" - }, - { - "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", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "68", - "safespot": null, - "lifepoints": "97", - "strength_level": "68", - "id": "2452", - "aggressive": "true", - "range_level": "1", - "attack_level": "68" - }, - { - "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 spiny horror from the ocean depths...", - "melee_animation": "1343", - "attack_speed": "6", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "100", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "60", - "safespot": null, - "lifepoints": "95", - "strength_level": "90", - "id": "2455", - "aggressive": "true", - "clue_level": "1", - "range_level": "1", - "projectile": "294", - "attack_level": "90" - }, - { - "examine": "It's an NPC.", - "combat_style": "1", - "melee_animation": "1343", - "respawn_delay": "60", - "defence_animation": "1340", - "slayer_exp": "0", - "death_animation": "1342", - "name": "Dagannoth", - "defence_level": "60", - "safespot": null, - "lifepoints": "70", - "strength_level": "90", - "id": "2456", - "aggressive": "true", - "clue_level": "1", - "range_level": "90", - "projectile": "294", - "attack_level": "90" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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": "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": "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" - }, - { + "magic_animation": "5327", "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" - }, - { - "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": "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": "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" - }, - { - "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 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" - }, - { - "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" - }, - { - "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" - }, - { - "death_animation": "836", - "name": "Hengel", - "defence_level": "1", - "safespot": null, - "lifepoints": "7", - "melee_animation": "422", - "strength_level": "1", - "id": "2683", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2685", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2686", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2687", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "examine": "An ugly smelly creature, with a spear.", - "melee_animation": "163", - "range_animation": "163", - "combat_audio": "469,472,471", - "attack_speed": "6", - "defence_animation": "165", - "magic_animation": "163", - "death_animation": "167", - "name": "Hobgoblin", - "defence_level": "24", - "safespot": null, - "lifepoints": "29", - "strength_level": "24", - "id": "2688", - "aggressive": "true", - "range_level": "1", - "attack_level": "22" - }, - { - "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", - "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", - "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", - "safespot": null, - "lifepoints": "1", - "strength_level": "1", - "id": "2694", - "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": "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": "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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": "From a darker dimension.", - "slayer_task": "25", - "melee_animation": "2731", - "range_animation": "2731", - "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", - "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": "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": "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" - }, - { - "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" - }, - { - "examine": "A fearsome magical creature from the deep.", - "combat_style": "2", - "melee_animation": "2365", - "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" - }, - { - "melee_animation": "1312", - "attack_speed": "5", - "respawn_delay": "60", - "defence_animation": "1313", - "death_animation": "1314", - "name": "Giant Rock Crab", - "defence_level": "1", - "safespot": null, - "lifepoints": "240", - "strength_level": "1", - "id": "2885", - "aggressive": "true", - "range_level": "1", - "attack_level": "1" - }, - { - "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", - "attack_speed": "2", - "defence_animation": "2861", - "weakness": "7", - "magic_animation": "2860", - "death_animation": "2862", - "name": "Rock lobster", - "defence_level": "100", - "safespot": null, - "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" - }, - { - "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" - }, - { - "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", - "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", - "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" - }, - { - "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", - "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": "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" - }, - { - "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" - }, - { - "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", - "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", - "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 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_animation": "5327", "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" + "strength_level": "45" }, { - "examine": "It's searching for crumbs of chocolate.", - "melee_animation": "0", - "range_animation": "0", - "defence_animation": "0", - "magic_animation": "0", - "death_animation": "0", + "id": "4401", "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": "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": "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": "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": "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": "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": "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": "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": "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" - }, - { - "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", + "aggressive": "false", "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", + "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", - "melee_animation": "5228", - "strength_level": "1", - "id": "7337", + "magic_animation": "4266", + "magic_level": "1", + "melee_animation": "4266", + "range_animation": "4266", "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": "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" + "slayer_exp": "10", + "strength_level": "10", + "weakness": "7" }, { - "examine": "The King of the Titans!", - "melee_animation": "8183", - "range_animation": "8183", - "magic_level": "70", + "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", - "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" + "strength_level": "10" }, { - "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", + "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, - "lifepoints": "7", - "strength_level": "25", - "id": "7398", - "aggressive": "true", - "range_level": "25", - "attack_level": "25" + "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", - "combat_audio": "3102,3104,3103", + "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", - "defence_animation": "2706", - "magic_animation": "2705", - "death_animation": "2707", - "name": "Rat", + "death_animation": "4302", + "defence_animation": "4301", "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", - "strength_level": "1", - "id": "7438", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7439", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "7440", - "clue_level": "2", - "range_level": "1", - "attack_level": "1" - }, - { - "name": "Elf warrior", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "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": "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": "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 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", - "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", - "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", - "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", - "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", + "melee_animation": "4300", "range_level": "1", - "attack_level": "56" + "respawn_delay": "60", + "safespot": null, + "slayer_exp": "112", + "strength_level": "1" }, { - "examine": "A bloodveld with a very mixed heritage.", - "slayer_task": "10", - "melee_animation": "9102", - "range_animation": "0", - "attack_speed": "5", - "respawn_delay": "60", + "id": "4419", + "name": "Cosmic Being", + "examine": "A highly enlightened being.", + "attack_level": "1", + "death_animation": "0", "defence_animation": "0", - "weakness": "9", - "slayer_exp": "206", + "defence_level": "1", + "lifepoints": "10", "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", - "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", - "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" + "strength_level": "1" }, { - "examine": "It looks upset.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "A TzHaar librarian.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "A TzHaar-Hur stamping stone tablets.", + "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", - "defence_animation": "0", - "magic_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", - "name": "Library assistant", + "defence_animation": "0", "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", + "melee_animation": "0", "range_animation": "0", - "respawn_delay": "60", - "defence_animation": "0", - "weakness": "0", - "magic_animation": "0", - "death_animation": "9344", - "name": "Fire monster", - "defence_level": "38", + "range_level": "1", "safespot": null, - "lifepoints": "54", - "strength_level": "28", - "id": "7773", - "aggressive": "true", - "range_level": "38", - "attack_level": "28" + "strength_level": "1" }, { - "examine": "A big, scary hand! ", - "melee_animation": "1802", + "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", - "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", + "death_animation": "97", + "defence_animation": "0", + "defence_level": "9", + "lifepoints": "12", + "magic_animation": "0", "melee_animation": "94", "range_animation": "0", - "attack_speed": "6", + "range_level": "1", "respawn_delay": "60", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", + "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", - "name": "Melee dummy", + "defence_animation": "0", "defence_level": "1", - "safespot": null, "lifepoints": "15", - "strength_level": "1", - "id": "7891", - "aggressive": "true", + "magic_animation": "0", + "melee_animation": "94", + "range_animation": "0", "range_level": "1", - "attack_level": "1" + "respawn_delay": "60", + "safespot": null, + "strength_level": "1", + "weakness": "6" }, { - "examine": "A man of his craft.", - "name": "Smelting Tutor", + "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", - "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" - }, - { - "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": "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.", + "magic_animation": "0", "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "A very good sailor.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "Smells a bit fishy.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "A (semi) retired member of the Temple Knights.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "A villager named Jeb.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "A little girl. She looks terrified.", + "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", - "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" + "safespot": null, + "strength_level": "1" }, { - "examine": "Stinky! Poor guy.", - "name": "Yeti", - "id": "130" + "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" }, { - "examine": "The Burgher's protectors.", + "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", - "name": "Honour Guard", - "defence_level": "80", - "lifepoints": "90", - "melee_animation": "395", - "attack_speed": "4", - "strength_level": "90", - "id": "5515", - "bonuses": "40,40,40,0,0,50,50,50,50,50,0,40,0,0,0", - "attack_level": "90", - "defence_animation": "404" + "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", - "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" + "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" }, { - "examine": "The Burgher's protectors.", - "death_animation": "836", - "name": "Honour Guard", - "defence_level": "80", - "lifepoints": "90", - "melee_animation": "395", - "attack_speed": "4", - "strength_level": "90", "id": "5514", - "bonuses": "40,40,40,0,0,50,50,50,50,50,0,40,0,0,0", - "attack_level": "90", - "defence_animation": "404" + "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" }, { - "examine": "The Burgher's protectors.", - "death_animation": "836", + "id": "5515", "name": "Honour Guard", - "defence_level": "80", - "lifepoints": "90", - "melee_animation": "395", - "attack_speed": "4", - "strength_level": "90", + "examine": "One of the Burgher's Protectors", + "death_animation": "836", + "defence_animation": "404", + "melee_animation": "395" + }, + { "id": "5516", - "bonuses": "40,40,40,0,0,50,50,50,50,50,0,40,0,0,0", - "attack_level": "90", - "defence_animation": "404" - }, - { + "name": "Honour guard", "examine": "The Burgher's protectors.", - "death_animation": "836", - "name": "Honour Guard", - "defence_level": "80", - "lifepoints": "90", - "melee_animation": "395", - "attack_speed": "4", - "strength_level": "90", + "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", - "bonuses": "40,40,40,0,0,50,50,50,50,50,0,40,0,0,0", - "attack_level": "90", - "defence_animation": "404" + "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" }, { - "examine": "Soft but strong... and cute.", - "name": "Labrador puppy", - "id": "7247" + "id": "5518", + "name": "Kjedelig Uppsen" }, { - "examine": "Soft but strong... and cute.", - "name": "Labrador puppy", - "id": "6962" + "id": "5519", + "name": "Trogen Konungarde" }, { - "examine": "Soft but strong... and cute.", - "name": "Labrador puppy", - "id": "7245" + "id": "5520", + "name": "Slug Hemligssen" }, { - "examine": "My most faithful friend.", - "name": "Labrador", - "id": "7248" + "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" }, { - "examine": "My most faithful friend.", - "name": "Labrador", - "id": "6963" + "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" }, { - "examine": "My most faithful friend.", - "name": "Labrador", - "id": "7246" + "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" }, { - "examine": "One proud little puppy.", - "name": "Bulldog puppy", - "id": "6969" + "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" }, { - "examine": "One proud little puppy.", - "name": "Bulldog puppy", - "id": "7260" + "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" }, { - "examine": "One proud little puppy.", - "name": "Bulldog puppy", - "id": "7259" + "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" }, { - "examine": "Looks like it's chewing a wasp.", - "name": "Bulldog", - "id": "6968" + "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" }, { - "examine": "Looks like it's chewing a wasp.", - "name": "Bulldog", - "id": "7258" + "id": "5528", + "name": "Ice troll grunt" }, { - "examine": "Looks like it's chewing a wasp.", - "name": "Bulldog", - "id": "7257" + "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" }, { - "examine": "Now, if only I could find another hundred...", - "name": "Dalmatian puppy", - "id": "6964" + "id": "5530", + "name": "Sorceress" }, { - "examine": "Now, if only I could find another hundred...", - "name": "Dalmatian puppy", - "id": "7249" + "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" }, { - "examine": "Now, if only I could find another hundred...", - "name": "Dalmatian puppy", - "id": "7251" + "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" }, { - "examine": "A purebred doggy.", - "name": "Dalmatian", - "id": "7252" + "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" }, { - "examine": "A purebred doggy.", - "name": "Dalmatian", - "id": "7250" + "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" }, { - "examine": "A purebred doggy.", - "name": "Dalmatian", - "id": "6965" + "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" }, { - "examine": "This is one fast little pooch.", - "name": "Greyhound puppy", - "id": "6960" + "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" }, { - "examine": "This is one fast little pooch.", - "name": "Greyhound puppy", - "id": "7241" + "id": "5560", + "name": "Osman" }, { - "examine": "This is one fast little pooch.", - "name": "Greyhound puppy", - "id": "7243" + "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" }, { - "examine": "An aerodynamic doggy.", - "name": "Greyhound", - "id": "6961" + "id": "5562", + "name": "Del-Monty" }, { - "examine": "An aerodynamic doggy.", - "name": "Greyhound", - "id": "7242" + "id": "5564", + "name": "Bouncer" }, { - "examine": "An aerodynamic doggy.", - "name": "Greyhound", - "id": "7244" + "id": "5565", + "name": "Bouncer" }, { - "examine": "It's lively and energetic.", - "name": "Terrier puppy", - "id": "6958" + "id": "5566", + "name": "General Khazard" }, { - "examine": "A hyperactive little pup.", - "name": "Terrier puppy", - "id": "7237" + "id": "5567", + "name": "Scout" }, { - "examine": "Such a brave doggy.", - "name": "Terrier puppy", - "id": "7239" + "id": "5568", + "name": "Scout" }, { - "examine": "It's lively and energetic.", - "name": "Terrier", - "id": "6959" + "id": "5569", + "name": "Scout" }, { - "examine": "A hyperactive little pup.", - "name": "Terrier", - "id": "7238" + "id": "5570", + "name": "Scout" }, { - "examine": "Such a brave doggy.", - "name": "Terrier", - "id": "7240" + "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" }, { - "examine": "This little puppy is very quiet.", - "name": "Sheepdog puppy", - "id": "7253" + "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" }, { - "examine": "This little puppy is very quiet.", - "name": "Sheepdog puppy", - "id": "6966" + "id": "5573", + "name": "Effigy" }, { - "examine": "This little puppy is very quiet.", - "name": "Sheepdog puppy", - "id": "7255" + "id": "5579", + "name": "Effigy" }, { - "examine": "Come by! Come by!", - "name": "Sheepdog", - "id": "6967" + "id": "5580", + "name": "Bonafido" }, { - "examine": "Come by! Come by!", - "name": "Sheepdog", - "id": "7254" + "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" }, { - "examine": "Come by! Come by!", - "name": "Sheepdog", - "id": "7256" + "id": "5582", + "name": "Homunculus" }, { - "examine": "The sandwich lady.", - "name": "Sandwich lady", - "id": "3117" + "id": "5583", + "name": "Homunculus" }, { - "examine": "A holy man.", - "name": "Drezel", - "id": "1049" + "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" }, { - "examine": "A holy man.", - "name": "Drezel", - "id": "7690" + "id": "5585", + "name": "'Transmute' The Alchemist" }, { - "examine": "A holy man.", - "name": "Drezel", - "id": "7707" + "id": "5586", + "name": "'Transmute' The Alchemist" }, { - "examine": "A slightly nervous guard.", - "name": "Ulizius", - "id": "1054" + "id": "5587", + "name": "'Currency' The Alchemist" }, { - "examine": "A local holy man who supports the people trying to get though Mort Myre.", - "name": "Hiylik Myna", - "id": "1514" + "id": "5588", + "name": "'Currency' The Alchemist" }, { - "examine": "The animated spirit of a soul not at rest.", - "name": "Filiman Tarlock", - "id": "1050" + "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" }, { - "examine": "The animated spirit of a soul not at rest.", - "name": "Nature Spirit", - "id": "1051" + "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" }, { - "examine": "I can see fish swimming in the water.", - "name": "Fishing spot", - "id": "323" + "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" }, { - "examine": "I can see fish swimming in the water.", - "name": "Fishing spot", - "id": "1176" + "id": "5592", + "name": "'The Guns'" }, { - "examine": "Nice hat.", - "name": "Thormac", - "id": "389" + "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" }, { - "examine": "An evil priest.", - "name": "Necrovarus", - "id": "1684" + "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" }, { - "examine": "He looks dangerous.", - "name": "Vannaka", - "id": "1597" + "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" }, { - "examine": "A soggy, smelly Fremennik.", - "name": "Olaf Hardson", - "id": "2621" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Elstan", - "id": "2323" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Dantaera", - "id": "2324" + "id": "5598", + "name": "Unicow" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Kragen", - "id": "2325" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Lyra", - "id": "2326" + "id": "5604", + "name": "Elfinlocks" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Francis", - "id": "2327" + "id": "5605", + "name": "Clockwork cat" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Vasquen", - "id": "2333" + "id": "5606", + "name": "Clockwork cat" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Rhonen", - "id": "2334" + "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" }, { - "examine": "Banks gnomish things.", - "name": "Gnome banker", - "id": "166" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Ellena", - "id": "2331" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Alain", - "id": "2339" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Dreven", - "id": "2335" + "id": "5612", + "name": "Rufus" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Frizzy Skernip", - "id": "4560" + "id": "5613", + "name": "Mi-Gor" }, { - "examine": "She sells farming equipment.", - "name": "Sarah", - "id": "2304" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Garth", - "id": "2330" + "id": "5615", + "name": "Puffin" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Selena", - "id": "2332" + "id": "5616", + "name": "Brother Tranquility" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Rhazien", - "id": "2337" + "id": "5617", + "name": "Brother Tranquility" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Torrell", - "id": "2338" + "id": "5618", + "name": "Brother Tranquility" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Heskel", - "id": "2340" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Treznor", - "id": "2341" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Bolongo", - "id": "2343" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Gileth", - "id": "2344" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Amaethwyr", - "id": "2860" + "id": "5623", + "name": "Zombie monk" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Praistan Ebola", - "id": "4562" + "id": "5624", + "name": "Zombie monk" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Imiago", - "id": "8041" + "id": "5625", + "name": "Zombie monk" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Prissy Scilla", - "id": "1037" + "id": "5626", + "name": "Zombie monk" }, { - "examine": "Looks after your Farming tools.", - "name": "Teclyn", - "id": "2861" + "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" }, { - "examine": "Perhaps this gardener might look after your crops for you.", - "name": "Yulf Squecks", - "id": "4561" + "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" }, { - "examine": "The head farmer, apparently.", - "name": "Farmer Blinkin", - "id": "7131" + "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" }, { - "examine": "Owner of this homestead.", - "name": "Mrs. Winkin", - "id": "7132" + "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" }, { - "examine": "The lady of the pet shop.", - "name": "Pet shop owner", - "id": "6750" + "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" }, { - "examine": "Looks after the Burthorpe vinery.", - "name": "Bernald", - "id": "2580" + "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" }, { - "examine": "An old druid.", - "name": "Sanfew", - "id": "454" + "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" }, { - "examine": "He runs the Herblore Shop.", - "name": "Jatix", - "id": "587" + "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" }, { - "examine": "A wise druid.", - "name": "Kaqemeex", - "id": "455" + "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" }, { - "examine": "He's a very important druid.", - "name": "Pikkupstix", - "id": "6971" + "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" }, { - "examine": "Ironically, makes a living from swords.", - "name": "Gaius", - "id": "586" + "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" }, { - "examine": "A gnarly old man.", + "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", - "id": "5987" + "examine": "A gnarly old man." }, { - "examine": "A terrier puppy.", + "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", - "id": "6894" + "examine": "A terrier puppy." }, { - "examine": "A labrador puppy.", + "id": "6895", "name": "Labrador", - "id": "6895" + "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 f5432b40f..77ef33f02 100644 --- a/Server/data/configs/npc_spawns.json +++ b/Server/data/configs/npc_spawns.json @@ -1,10910 +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}-{3229,3238,0,1,1}-{3279,3497,0,1,3}" - }, - { - "npc_id": "6", - "loc_data": "{3097,3259,0,1,4}-{3280,3491,0,1,0}-{3246,3207,0,1,4}" - }, - { - "npc_id": "7", - "loc_data": "{2920,3434,0,1,0}-{3226,3290,0,1,3}-{2629,3360,0,1,4}-{3158, 3301, 0, 1, 0}" - }, - { - "npc_id": "8", - "loc_data": "{3019,3230,0,1,4}-{2804,3186,0,1,4}" - }, - { - "npc_id": "9", - "loc_data": "{2964,3392,0,1,6}-{2967,3393,0,1,1}-{3008,3324,0,1,2}-{3007,3321,0,1,6}-{3005,3321,0,1,4}-{2965,3389,0,1,6}-{2966,3392,0,1,1}-{2964,3392,0,1,1}" - }, - { - "npc_id": "11", - "loc_data": "{3235,3399,0,1,5}" - }, - { - "npc_id": "13", - "loc_data": "{3108,3157,1,1,3}-{3113,3157,1,1,4}-{3107,3170,0,1,0}-{3112,3167,0,1,0}-{3106,3157,0,1,3}-{3110,3155,0,1,6}-{2588,3087,0,1,4}-{2592,3086,0,1,6}-{2590,3083,1,1,4}-{2587,3086,1,1,4}-{2594,3084,1,1,6}-{2593,3090,2,1,5}-{2594,3085,2,1,1}-{2588,3089,2,1,4}" - }, - { - "npc_id": "14", - "loc_data": "{2914,3444,0,1,3}-{2905,3450,1,1,0}" - }, - { - "npc_id": "15", - "loc_data": "{2584,3304,0,1,3}-{2588,3291,0,1,7}-{2584,3288,0,1,0}-{2631,3294,0,1,6}-{2629,3296,0,1,4}-{2563,3382,0,1,6}-{2569,3383,0,1,6}-{2571,3381,0,1,1}-{2575,3384,0,1,4}-{2572,3385,0,1,4}" - }, - { - "npc_id": "16", - "loc_data": "{3279,3191,0,1,4}-{3296,3202,1,1,3}" - }, - { - "npc_id": "18", - "loc_data": "{3282,3176,0,1,0}-{3284,3170,0,1,7}-{3284,3174,0,1,0}-{3288,3168,0,1,5}-{3292,3169,0,1,3}-{3295,3168,0,1,3}-{3301,3170,0,1,6}-{3301,3174,0,1,6}-{3301,3177,0,1,0}" - }, - { - "npc_id": "19", - "loc_data": "{2971,3349,0,1,4}-{2979,3350,0,1,2}-{2973,3337,0,1,3}-{2959,3335,0,1,6}-{2982,3339,0,1,6}-{2983,3346,0,1,4}-{2980,3329,0,1,4}-{2987,3330,0,1,6}-{2958,3340,1,1,1}-{2970,3333,1,1,3}-{2966,3329,1,1,4}-{2961,3353,1,1,4}-{2973,3337,1,1,6}-{2998,3341,0,1,6}-{2983,3342,2,1,6}" - }, - { - "npc_id": "20", - "loc_data": "{2588,3301,1,0,0}" - }, - { - "npc_id": "21", - "loc_data": "{2630,3288,0,1,1}-{2647,3306,0,1,1}-{2667,3315,0,1,4}" - }, - { - "npc_id": "23", - "loc_data": "{2582,3297,0,1,6}-{2652,3318,0,1,0}-{2653,3300,0,1,5}-{2669,3298,0,1,4}-{2671,3313,0,1,6}" - }, - { - "npc_id": "24", - "loc_data": "{2614,3318,0,1,6}-{2621,3293,0,1,3}" - }, - { - "npc_id": "25", - "loc_data": "{2612,3316,0,1,1}-{2574,3321,1,1,4}-{2621,3293,1,1,3}" - }, - { - "npc_id": "27", - "loc_data": "{2574,3285,2,0,0}-{2574,3308,2,0,0}-{2582,3284,2,0,0}-{2582,3309,2,0,0}-{2588,3290,2,0,0}-{2588,3303,2,0,0}" - }, - { - "npc_id": "28", - "loc_data": "{2611,3269,0,0,0}" - }, - { - "npc_id": "31", - "loc_data": "{2616,3308,0,1,1}" - }, - { - "npc_id": "32", - "loc_data": "{2651,3307,0,1,1}-{2659,3309,0,1,6}-{2660,3309,0,1,3}-{2661,3309,0,1,1}-{2661,3317,0,1,5}-{2663,3301,0,1,3}-{2665,3300,0,1,3}-{2636,3342,0,1,4}-{2634,3342,0,1,3}-{2635,3339,0,1,3}-{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}" - }, - { - "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}-{3037,9511,0,1,6}-{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}-{2856,3490,0,0,0}-{2861,3492,0,0,0}-{2862,3505,0,0,0}-{2856,3507,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}-{2585,3479,0,1,1}-{2578,3476,0,1,7}-{2588,3485,0,1,3}" - }, - { - "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": "{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}" - }, - { - "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": "{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": "281", - "loc_data": "{2604,3219,0,0,0}-{2609,3207,0,0,0}-{2618,3211,0,0,0}" - }, - { - "npc_id": "282", - "loc_data": "{3280,3494,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": "{2542,3169,0,1,4}-{2541,3170,0,1,4}-{2537,3168,0,1,5}" - }, - { - "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}-{3682,2981,0,0,3}-{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}-{3682,2983,0,0,3}" - }, - { - "npc_id": "497", - "loc_data": "{3267,3166,0,0,4}-{3267,3168,0,0,4}-{3682,2982,0,0,3}" - }, - { - "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": "665", - "loc_data": "{2986,9811,0,0,0}" - }, - { - "npc_id": "666", - "loc_data": "{2819,3451,0,0,3}" - }, - { - "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": "1013", - "loc_data": "{2544,2988,0,0,0}-{2532,2983,0,0,0}-{2549,2983,0,0,0}-{2545,2983,0,0,0}-{2528,2986,0,0,0}-{2550,2979,0,0,0}-{2522,2978,0,0,0}-{2523,2976,0,0,0}-{2526,2974,0,0,0}-{2593,2971,0,0,0}-{2594,2971,0,0,0}-{2596,2961,0,0,0}-{2598,2971,0,0,0}-{2599,2960,0,0,0}-{2598,2960,0,0,0}-{2603,2966,0,0,0}-{2603,2970,0,0,0}-{2598,2960,0,0,0}-{2596,2963,0,0,0}-{2595,2964,0,0,0}-{2594,2964,0,0,0}-{2601,2964,0,0,0}-{2598,2969,0,0,0}-{2604,2971,0,0,0}-{2601,2972,0,0,0}-{2544,2904,0,0,0}-{2548,2990,0,0,0}-{2552,2988,0,0,0}-{2548,2979,0,0,0}-{2547,2991,0,0,0}-{2525,2975,0,0,0}-{2464,2912,0,0,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,0,0}-{2843,3155,0,0,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,3479,1,0,0}-{3514,3481,1,0,0}-{3514,3481,0,0,3}-{3514,3479,0,0,3}" - }, - { - "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": "{2897,3569,0,0,0}-{2893,3565,0,0,0}" - }, - { - "npc_id": "1073", - "loc_data": "{2900,3549,0,0,3}" - }, - { - "npc_id": "1074", - "loc_data": "{2897,3549,0,0,5}" - }, - { - "npc_id": "1076", - "loc_data": "{2900,3556,0,0,1}-{2897,3565,0,1,4}" - }, - { - "npc_id": "1077", - "loc_data": "{2897,3556,0,0,5}" - }, - { - "npc_id": "1079", - "loc_data": "{2906,3537,0,1,5}" - }, - { - "npc_id": "1081", - "loc_data": "{2896,3562,0,0,0}-{2891,3568,0,0,0}" - }, - { - "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": "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": "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": "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,4859,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": "{3089,3124,0,0,3}-{3096,3108,0,0,6}-{3075,3091,0,0,6}-{3121,3103,0,0,1}-{3105,3095,0,0,3}" - }, - { - "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": "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,0,0}-{1754,5200,0,0,0}-{1748,5223,0,0,0}-{1784,5199,0,0,0}-{1739,5154,0,0,0}" - }, - { - "npc_id": "3342", - "loc_data": "{1745,5208,0,0,0}-{1737,5215,0,0,0}-{1758,5210,0,0,0}-{1768,5175,0,0,0}-{1760,5152,0,0,0}-{1745,5169,0,0,0}" - }, - { - "npc_id": "3343", - "loc_data": "{1764,5198,0,0,0}-{1774,5195,0,0,0}-{1773,5149,0,0,0}-{1742,5292,0,0,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": "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": "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": "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": "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": "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}" - }, - { - "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": "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": "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": "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}-{1605,4720,0,1,0}-{1631,4730,0,1,0}-{1649,4730,0,1,0}-{1658,4705,0,1,0}-{1658,4687,0,1,0}-{1633,4678,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": "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 b04138c0b..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", @@ -1503,66 +1503,84 @@ "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", + "itemId": "14192", + "name": "Bow (class 1)", "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", + "weapon_type": "0", "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" + "ammunition": "14202,14203,14204,14205,14206" }, { - "itemId": "14804", - "name": "Dark bow", + "itemId": "14194", + "name": "Bow (class 2)", "ammo_slot": "13", - "weapon_type": "2", + "weapon_type": "0", "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" + "ammunition": "14202,14203,14204,14205,14206" }, { - "itemId": "14805", - "name": "Dark bow", + "itemId": "14196", + "name": "Bow (class 3)", "ammo_slot": "13", - "weapon_type": "2", + "weapon_type": "0", "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" + "ammunition": "14202,14203,14204,14205,14206" }, { - "itemId": "14806", - "name": "Dark bow", + "itemId": "14198", + "name": "Bow (class 4)", "ammo_slot": "13", - "weapon_type": "2", + "weapon_type": "0", "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" + "ammunition": "14202,14203,14204,14205,14206" + }, + { + "itemId": "14200", + "name": "Bow (class 5)", + "ammo_slot": "13", + "weapon_type": "0", + "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/shared_tables/ASDT.xml b/Server/data/configs/shared_tables/ASDT.xml new file mode 100644 index 000000000..bc11f62de --- /dev/null +++ b/Server/data/configs/shared_tables/ASDT.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Server/data/configs/shared_tables/CELEDT.xml b/Server/data/configs/shared_tables/CELEDT.xml new file mode 100644 index 000000000..e7d8bc57d --- /dev/null +++ b/Server/data/configs/shared_tables/CELEDT.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/data/configs/shared_tables/GDT.xml b/Server/data/configs/shared_tables/GDT.xml new file mode 100644 index 000000000..e10dd5bd2 --- /dev/null +++ b/Server/data/configs/shared_tables/GDT.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/data/configs/shared_tables/HDT.xml b/Server/data/configs/shared_tables/HDT.xml new file mode 100644 index 000000000..d8f0223c5 --- /dev/null +++ b/Server/data/configs/shared_tables/HDT.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/data/RDT.xml b/Server/data/configs/shared_tables/RDT.xml similarity index 96% rename from Server/data/RDT.xml rename to Server/data/configs/shared_tables/RDT.xml index 080dd9049..78ed522a6 100644 --- a/Server/data/RDT.xml +++ b/Server/data/configs/shared_tables/RDT.xml @@ -26,4 +26,5 @@ + \ No newline at end of file diff --git a/Server/data/configs/shared_tables/RSDT.xml b/Server/data/configs/shared_tables/RSDT.xml new file mode 100644 index 000000000..58bf5ad32 --- /dev/null +++ b/Server/data/configs/shared_tables/RSDT.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/data/configs/shared_tables/USDT.xml b/Server/data/configs/shared_tables/USDT.xml new file mode 100644 index 000000000..105e7428c --- /dev/null +++ b/Server/data/configs/shared_tables/USDT.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/data/configs/shops.json b/Server/data/configs/shops.json index 1a444c1c5..2f285314c 100644 --- a/Server/data/configs/shops.json +++ b/Server/data/configs/shops.json @@ -6,7 +6,7 @@ "general_store": "false", "id": "1", "title": "Ava's Odds and Ends", - "stock": "{314,1000}-{884,40}-{886,10}-{40,30}-{41,20}" + "stock": "{314,1000,100}-{884,40,100}-{886,10,100}-{40,30,100}-{41,20,100}" }, { "npcs": "528,529", @@ -15,7 +15,7 @@ "general_store": "true", "id": "2", "title": "Edgeville General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "525,524", @@ -24,7 +24,7 @@ "general_store": "true", "id": "3", "title": "Al Kharid General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "3799", @@ -33,7 +33,7 @@ "general_store": "true", "id": "4", "title": "Void Knight General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}-{7934,25}" + "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}-{7934,25,100}" }, { "npcs": "1436", @@ -42,25 +42,16 @@ "general_store": "true", "id": "5", "title": "Ifaba's General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "{1759,100}-{882,1000}-{1265,10}-{2142,10}-{1349,10}-{946,10}-{970,10}-{954,10}-{590,10}-{227,10}" - }, - { - "npcs": "591", - "high_alch": "0", - "currency": "995", - "general_store": "true", - "id": "7", - "title": "Kortan General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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", @@ -69,7 +60,7 @@ "general_store": "true", "id": "8", "title": "West Ardougne General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "stock": "{1931,10,100}-{954,10,100}-{1265,10,100}-{1925,30,100}-{590,10,100}-{2347,10,100}-{1061,10,100}-{841,10,100}-{882,100,100}-{329,10,100}-{2327,10,100}-{2309,10,100}-{2142,10,100}" }, { "npcs": "1917", @@ -78,7 +69,7 @@ "general_store": "true", "id": "9", "title": "Bandit Bargains", - "stock": "{1823,5}-{1831,5}-{1937,5}-{1921,5}-{1929,5}-{1935,5}-{1923,5}-{1925,5}-{1837,5}-{1833,5}-{1835,5}-{946,5}" + "stock": "{1823,5,100}-{1831,5,100}-{1937,5,100}-{1921,5,100}-{1929,5,100}-{1935,5,100}-{1923,5,100}-{1925,5,100}-{1837,5,100}-{1833,5,100}-{1835,5,100}-{946,5,100}" }, { "npcs": "597", @@ -87,7 +78,7 @@ "general_store": "true", "id": "10", "title": "Bandit Duty Free", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "1083", @@ -96,7 +87,7 @@ "general_store": "true", "id": "11", "title": "Burthorpe Supplies", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "3541,7396", @@ -105,7 +96,7 @@ "general_store": "true", "id": "12", "title": "Aurel's Supplies", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "1040", @@ -114,7 +105,7 @@ "general_store": "true", "id": "13", "title": "General Store (Canifis)", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}-{3377,10}" + "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}-{3377,10,100}" }, { "npcs": "563", @@ -123,7 +114,7 @@ "general_store": "true", "id": "14", "title": "Arhein's Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "5798", @@ -132,7 +123,7 @@ "general_store": "true", "id": "15", "title": "Dorgesh-Kaan General Supplies", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "582", @@ -141,7 +132,7 @@ "general_store": "true", "id": "16", "title": "Dwarven Shopping Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "526,527", @@ -150,7 +141,7 @@ "general_store": "true", "id": "17", "title": "Falador General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "873", @@ -159,7 +150,7 @@ "general_store": "true", "id": "18", "title": "Dal's General Ogre Supplies", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "532,533", @@ -168,7 +159,7 @@ "general_store": "true", "id": "19", "title": "Karamja General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "568", @@ -177,7 +168,7 @@ "general_store": "true", "id": "20", "title": "Karamja Wines, Spirits, and Beers", - "stock": "{1917,3}-{431,3}-{1993,1}" + "stock": "{1917,3,100}-{431,3,100}-{1993,1,100}" }, { "npcs": "2154", @@ -186,7 +177,7 @@ "general_store": "true", "id": "21", "title": "Gunslik's Assorted Items", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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", @@ -195,7 +186,7 @@ "general_store": "true", "id": "22", "title": "The Lighthouse Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "2352", @@ -204,7 +195,7 @@ "general_store": "true", "id": "23", "title": "Lletya General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "stock": "{1931,10,100}-{954,10,100}-{1265,10,100}-{1925,30,100}-{590,10,100}-{2347,10,100}-{1061,10,100}-{841,10,100}-{882,100,100}-{329,10,100}-{2327,10,100}-{2309,10,100}-{2142,10,100}" }, { "npcs": "4516", @@ -213,7 +204,7 @@ "general_store": "true", "id": "24", "title": "Moon Clan General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "stock": "{1931,30,100}-{1935,10,100}-{1735,10,100}-{1925,30,100}-{1923,10,100}-{1887,10,100}-{590,10,100}-{1755,10,100}-{2347,10,100}-{9003,10,100}-{229,1000,100}" }, { "npcs": "520,521", @@ -222,7 +213,7 @@ "general_store": "true", "id": "25", "title": "Lumbridge General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "4716", @@ -231,7 +222,7 @@ "general_store": "true", "id": "26", "title": "Trader Sven's Black Market Goods", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "3922", @@ -240,7 +231,7 @@ "general_store": "true", "id": "27", "title": "Miscellanian General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "1254", @@ -249,16 +240,16 @@ "general_store": "true", "id": "28", "title": "Razmire General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "2086", + "npcs": "3039", "high_alch": "0", "currency": "995", "general_store": "true", "id": "29", "title": "Nardah General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "3824", @@ -267,7 +258,7 @@ "general_store": "true", "id": "30", "title": "Arnold's Eclectic Supplies", - "stock": "{303,10}-{311,10}-{7944,0}-{7946,0}-{2309,10}-{1931,30}-{1927,10}-{1733,10}-{1734,1000}-{1755,10}-{1917,10}-{1785,10}-{946,10}" + "stock": "{303,10,100}-{311,10,100}-{7944,0,100}-{7946,0,100}-{2309,10,100}-{1931,30,100}-{1927,10,100}-{1733,10,100}-{1734,1000,100}-{1755,10,100}-{1917,10,100}-{1785,10,100}-{946,10,100}" }, { "npcs": "1866", @@ -276,7 +267,7 @@ "general_store": "true", "id": "31", "title": "Pollnivneach General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "1699", @@ -285,7 +276,7 @@ "general_store": "true", "id": "33", "title": "Port Phasmatys General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "530,531", @@ -294,7 +285,7 @@ "general_store": "true", "id": "34", "title": "Rimmington General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "836", @@ -303,7 +294,7 @@ "general_store": "true", "id": "35", "title": "Shantay Pass Shop", - "stock": "{1823,30}-{1831,30}-{1937,10}-{1921,10}-{1929,10}-{946,10}-{1833,10}-{1835,10}-{1837,10}-{2349,0}-{314,1000}-{2347,10}-{1925,30}-{1923,10}-{1935,10}-{1854,300}-{954,100}" + "stock": "{1823,30,100}-{1831,30,100}-{1937,10,100}-{1921,10,100}-{1929,10,100}-{946,10,100}-{1833,10,100}-{1835,10,100}-{1837,10,100}-{2349,0,100}-{314,1000,100}-{2347,10,100}-{1925,30,100}-{1923,10,100}-{1935,10,100}-{1854,300,100}-{954,100,100}" }, { "npcs": "516", @@ -312,7 +303,7 @@ "general_store": "true", "id": "36", "title": "Obli's General Store", - "stock": "{590,2}-{229,10}-{233,3}-{1931,3}-{1351,3}-{1265,2}-{1349,5}-{1129,12}-{1059,10}-{1061,10}-{2142,2}-{2309,10}-{2349,10}-{952,10}-{36,10}-{596,10}-{1755,10}-{2347,10}-{970,50}-{973,50}-{227,50}-{975,50}-{954,10}" + "stock": "{590,10,100}-{229,1000,100}-{233,10,100}-{1931,30,100}-{1351,10,100}-{1265,10,100}-{1349,10,100}-{1129,10,100}-{1059,10,100}-{1061,10,100}-{2142,10,100}-{2309,10,100}-{952,10,100}-{36,10,100}-{596,10,100}-{1755,10,100}-{2347,10,100}-{970,10,100}-{973,10,100}-{227,1000,100}-{975,10,100}-{954,10,100}" }, { "npcs": "560", @@ -321,7 +312,7 @@ "general_store": "true", "id": "37", "title": "Jiminua's Jungle Store", - "stock": "{590,2}-{36,10}-{596,10}-{1931,3}-{954,10}-{1129,12}-{1059,10}-{1061,10}-{2142,10}-{2309,10}-{227,10}-{227,300}-{233,300}-{175,10}-{970,10}-{973,10}-{946,10}-{2347,10}-{975,10}-{1755,10}-{952,10}-{1351,10}-{1265,10}-{1349,10}" + "stock": "{590,10,100}-{36,10,100}-{596,10,100}-{1931,30,100}-{954,10,100}-{1129,10,100}-{1059,10,100}-{1061,10,100}-{2142,10,100}-{2309,10,100}-{227,300,100}-{233,10,100}-{175,10,100}-{970,10,100}-{973,10,100}-{946,10,100}-{2347,10,100}-{975,10,100}-{1755,10,100}-{952,10,100}-{1351,10,100}-{1265,10,100}-{1349,10,100}-{229,300,100}" }, { "npcs": "471", @@ -330,7 +321,7 @@ "general_store": "true", "id": "38", "title": "Bolkoy's Village Shop", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "1208", @@ -339,7 +330,7 @@ "general_store": "true", "id": "39", "title": "Quartermaster's Stores", - "stock": "{1931,30}-{1935,10}-{1735,10}-{590,10}-{2309,10}-{3190,10}-{3192,10}-{3194,10}-{3196,10}-{3198,10}-{3200,10}-{3202,10}-{3204,10}" + "stock": "{1931,30,100}-{1935,10,100}-{1735,10,100}-{590,10,100}-{2309,10,100}-{3190,10,100}-{3192,10,100}-{3194,10,100}-{3196,10,100}-{3198,10,100}-{3200,10,100}-{3202,10,100}-{3204,10,100}" }, { "npcs": "522,523", @@ -348,7 +339,7 @@ "general_store": "true", "id": "40", "title": "Varrock General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "4651,4652,4653,4654,4655,4656,4650", @@ -357,16 +348,16 @@ "general_store": "true", "id": "41", "title": "Trader Stan's Trading Post", - "stock": "{1931,5}-{1935,2}-{1735,2}-{1925,3}-{1923,2}-{1887,2}-{590,2}-{1755,2}-{2347,5}-{550,5}-{9003,5}-{954,2}-{946,2}-{2114,0}-{1963,15}-{2108,10}-{4286,10}-{1785,15}-{1783,inf}-{401,inf}-{1781,10}-{301,20}-{307,20}-{1941,30}-{9629,25}-{3226,20}-{1025,5}" + "stock": "{1931,30,100}-{1935,10,100}-{1735,10,100}-{1925,30,100}-{1923,10,100}-{1887,10,100}-{590,10,100}-{1755,10,100}-{2347,10,100}-{550,10,100}-{9003,10,100}-{954,10,100}-{946,10,100}-{2114,0,100}-{1963,10,100}-{2108,0,100}-{1785,10,100}-{1783,0,100}-{401,0,100}-{1781,0,100}-{301,10,100}-{307,10,100}-{1941,10,100}-{9629,10,100}-{3226,10,100}-{1025,10,100}" }, { - "npcs": "534", + "npcs": "534,535", "high_alch": "0", "currency": "995", "general_store": "true", "id": "42", "title": "Zanaris General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "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": "588", @@ -375,7 +366,7 @@ "general_store": "false", "id": "43", "title": "Davon's Amulet Store", - "stock": "{1718,0}-{1727,0}-{1729,0}-{1725,0}-{1731,0}" + "stock": "{1718,0,100}-{1727,0,100}-{1729,0,100}-{1725,0,100}-{1731,0,100}" }, { "npcs": "2356", @@ -384,7 +375,7 @@ "general_store": "false", "id": "44", "title": "Lletya Archery Shop", - "stock": "{884,1000}-{886,300}-{888,100}-{890,100}-{892,30}-{877,1000}-{843,10}-{837,10}-{849,10}" + "stock": "{884,1000,100}-{886,300,100}-{888,100,100}-{890,100,100}-{892,30,100}-{877,1000,100}-{843,10,100}-{837,10,100}-{849,10,100}" }, { "npcs": "3796", @@ -393,7 +384,7 @@ "general_store": "false", "id": "45", "title": "Void Knight Archery Store", - "stock": "{825,300}-{826,300}-{827,100}-{828,100}-{829,250}-{830,10}-{39,100}-{40,100}-{41,100}-{42,30}-{43,30}-{44,10}" + "stock": "{825,300,100}-{826,300,100}-{827,100,100}-{828,100,100}-{829,250,100}-{830,10,100}-{39,100,100}-{40,100,100}-{41,100,100}-{42,30,100}-{43,30,100}-{44,10,100}" }, { "npcs": "1860", @@ -402,7 +393,7 @@ "general_store": "false", "id": "46", "title": "Brian's Archery Supplies", - "stock": "{886,100}-{888,30}-{890,0}-{843,10}-{845,10}-{849,10}-{847,10}-{853,10}-{851,10}" + "stock": "{886,100,100}-{888,30,100}-{890,0,100}-{843,10,100}-{845,10,100}-{849,10,100}-{847,10,100}-{853,10,100}-{851,10,100}" }, { "npcs": "550", @@ -411,7 +402,7 @@ "general_store": "false", "id": "47", "title": "Lowe's Archery Emporium", - "stock": "{882,2500}-{884,500}-{886,500}-{888,500}-{890,300}-{877,500}-{841,10}-{843,10}-{849,10}-{853,10}-{837,10}-{9177,10}-{9179,10}" + "stock": "{882,1000,100}-{884,100,100}-{886,100,100}-{888,100,100}-{890,30,100}-{877,300,100}-{841,10,100}-{843,10,100}-{849,10,100}-{853,10,100}-{837,10,100}-{839,10,100}-{845,10,100}-{847,10,100}-{851,10,100}" }, { "npcs": "575", @@ -420,7 +411,7 @@ "general_store": "false", "id": "48", "title": "Hickton's Archery Emporium", - "stock": "{877,300}-{882,1000}-{884,100}-{886,0}-{888,0}-{890,0}-{892,0}-{4773,0}-{4778,0}-{4783,0}-{4788,0}-{4793,0}-{4798,0}-{4803,0}-{841,10}-{837,10}-{843,10}-{4827,10}-{1133,10}-{1097,10}" + "stock": "{877,300,100}-{882,1000,100}-{884,100,100}-{886,0,100}-{888,0,100}-{890,0,100}-{892,0,100}-{4773,0,100}-{4778,0,100}-{4783,0,100}-{4788,0,100}-{4793,0,100}-{4798,0,100}-{4803,0,100}-{841,10,100}-{837,10,100}-{843,10,100}-{4827,10,100}-{1133,10,100}-{1097,10,100}" }, { "npcs": "683", @@ -429,7 +420,7 @@ "general_store": "false", "id": "49", "title": "Dargaud's Bows And Arrows", - "stock": "{52,1000}-{39,1000}-{40,300}-{41,300}-{42,100}-{43,100}-{44,100}-{882,1000}-{884,300}-{886,300}-{888,100}-{890,100}-{892,100}-{4773,0}-{4778,0}-{4783,0}-{4788,0}-{4793,0}-{4798,0}-{4803,0}-{841,10}-{843,10}-{849,10}-{839,20}-{845,20}-{847,20}-{4827,10}" + "stock": "{52,1000,100}-{39,1000,100}-{40,300,100}-{41,300,100}-{42,100,100}-{43,100,100}-{44,100,100}-{882,1000,100}-{884,300,100}-{886,300,100}-{888,100,100}-{890,100,100}-{892,100,100}-{4773,0,100}-{4778,0,100}-{4783,0,100}-{4788,0,100}-{4793,0,100}-{4798,0,100}-{4803,0,100}-{841,10,100}-{843,10,100}-{849,10,100}-{839,20,100}-{845,20,100}-{847,20,100}-{4827,10,100}" }, { "npcs": "682", @@ -438,7 +429,7 @@ "general_store": "false", "id": "50", "title": "Aaron's Archery Appendages", - "stock": "{1129,10}-{1131,10}-{1133,10}-{1095,10}-{1097,10}-{1169,10}-{1167,10}-{1063,10}" + "stock": "{1129,10,100}-{1131,10,100}-{1133,10,100}-{1095,10,100}-{1097,10,100}-{1169,10,100}-{1167,10,100}-{1063,10,100}" }, { "npcs": "4563", @@ -447,7 +438,7 @@ "general_store": "false", "id": "51", "title": "Crossbow Shop (dwarven Mine)", - "stock": "{9440,10}-{9442,10}-{9444,10}-{9446,10}-{9448,10}-{9450,10}-{9452,0}-{9420,10}-{9423,10}-{9425,10}-{9427,10}-{9429,10}-{9431,0}" + "stock": "{9440,10,100}-{9442,10,100}-{9444,10,100}-{9446,10,100}-{9448,10,100}-{9450,10,100}-{9452,0,100}-{9420,10,100}-{9423,10,100}-{9425,10,100}-{9427,10,100}-{9429,10,100}-{9431,0,100}" }, { "npcs": "4558", @@ -456,7 +447,7 @@ "general_store": "false", "id": "52", "title": "Crossbow Shop (keldagrim)", - "stock": "{9440,10}-{9442,10}-{9444,10}-{9446,10}-{9448,10}-{9450,10}-{9452,0}-{9420,10}-{9423,10}-{9425,10}-{9427,10}-{9429,10}-{9431,0}" + "stock": "{9440,10,100}-{9442,10,100}-{9444,10,100}-{9446,10,100}-{9448,10,100}-{9450,10,100}-{9452,0,100}-{9420,10,100}-{9423,10,100}-{9425,10,100}-{9427,10,100}-{9429,10,100}-{9431,0,100}" }, { "npcs": "4559", @@ -465,7 +456,7 @@ "general_store": "false", "id": "53", "title": "Crossbow Shop (white Wolf Mountain)", - "stock": "{9440,10}-{9442,10}-{9444,10}-{9446,10}-{9448,10}-{9450,10}-{9452,0}-{9420,10}-{9423,10}-{9425,10}-{9427,10}-{9429,10}-{9431,0}" + "stock": "{9440,10,100}-{9442,10,100}-{9444,10,100}-{9446,10,100}-{9448,10,100}-{9450,10,100}-{9452,0,100}-{9420,10,100}-{9423,10,100}-{9425,10,100}-{9427,10,100}-{9429,10,100}-{9431,0,100}" }, { "npcs": "519", @@ -474,7 +465,7 @@ "general_store": "false", "id": "54", "title": "Bob's Brilliant Axes", - "stock": "{1265,10}-{1351,10}-{1349,10}-{1353,10}-{1363,10}-{1365,10}-{1369,10}" + "stock": "{1265,10,100}-{1351,10,100}-{1349,10,100}-{1353,10,100}-{1363,10,100}-{1365,10,100}-{1369,10,100}" }, { "npcs": "559", @@ -483,7 +474,7 @@ "general_store": "false", "id": "55", "title": "Brian's Battleaxe Bazaar", - "stock": "{1375,4}-{1363,3}-{1365,2}-{1367,1}-{1369,1}-{1371,1}" + "stock": "{1375,4,100}-{1363,3,100}-{1365,2,100}-{1367,1,100}-{1369,1,100}-{1371,1,100}" }, { "npcs": "562", @@ -492,7 +483,7 @@ "general_store": "false", "id": "56", "title": "Candle Shop", - "stock": "{36,10}-{38,10}" + "stock": "{36,10,100}-{38,10,100}" }, { "npcs": "562", @@ -501,7 +492,7 @@ "general_store": "false", "id": "57", "title": "Candle Shop", - "stock": "{36,10}" + "stock": "{36,10,100}" }, { "npcs": "581", @@ -510,7 +501,7 @@ "general_store": "false", "id": "58", "title": "Wayne's Chains", - "stock": "{1103,3}-{1101,2}-{1105,1}-{1107,1}-{1109,1}-{1111,1}" + "stock": "{1103,3,100}-{1101,2,100}-{1105,1,100}-{1107,1,100}-{1109,1,100}-{1111,1,100}" }, { "npcs": "554", @@ -519,7 +510,7 @@ "general_store": "false", "id": "59", "title": "Fancy Clothes Store", - "stock": "{1949,10}-{958,10}-{948,10}-{1733,10}-{1734,1000}-{1059,30}-{1061,10}-{1757,10}-{1013,10}-{1025,10}-{10088,0}-{10090,0}-{10091,0}-{10089,0}-{10087,0}-{11525,0}" + "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", @@ -528,7 +519,7 @@ "general_store": "false", "id": "60", "title": "Thessalia's Fine Clothes", - "stock": "{1005,10}-{1129,10}-{1059,10}-{1061,10}-{1757,10}-{1013,10}-{1015,10}-{1011,10}-{1007,10}-{950,10}-{426,10}-{428,10}" + "stock": "{1005,10,100}-{1129,10,100}-{1059,10,100}-{1061,10,100}-{1757,10,100}-{1013,10,100}-{1015,10,100}-{1011,10,100}-{1007,10,100}-{950,10,100}-{426,10,100}-{428,10,100}" }, { "npcs": "601", @@ -537,7 +528,7 @@ "general_store": "false", "id": "61", "title": "Fine Fashions", - "stock": "{646,5}-{648,5}-{650,5}-{652,5}-{654,5}-{656,5}-{658,5}-{660,5}-{662,5}-{664,5}-{636,5}-{638,5}-{640,5}-{642,5}-{644,5}-{626,5}-{628,5}-{630,30}-{632,30}-{634,30}" + "stock": "{646,5,100}-{648,5,100}-{650,5,100}-{652,5,100}-{654,5,100}-{656,5,100}-{658,5,100}-{660,5,100}-{662,5,100}-{664,5,100}-{636,5,100}-{638,5,100}-{640,5,100}-{642,5,100}-{644,5,100}-{626,5,100}-{628,5,100}-{630,30,100}-{632,30,100}-{634,30,100}" }, { "npcs": "1301", @@ -546,7 +537,7 @@ "general_store": "false", "id": "62", "title": "Yrsa's Accoutrements", - "stock": "{3775,10}-{3773,10}-{3767,10}-{3769,10}-{3771,10}-{3793,10}-{3795,10}-{3797,10}-{3791,10}-{3799,10}-{3767,10}-{3759,10}-{3761,10}-{3765,10}-{3763,10}-{3777,10}-{3779,10}-{3781,10}-{3783,10}-{3785,10}-{3787,10}-{3789,10}" + "stock": "{3775,10,100}-{3773,10,100}-{3767,10,100}-{3769,10,100}-{3771,10,100}-{3793,10,100}-{3795,10,100}-{3797,10,100}-{3791,10,100}-{3799,10,100}-{3759,10,100}-{3761,10,100}-{3765,10,100}-{3763,10,100}-{3777,10,100}-{3779,10,100}-{3781,10,100}-{3783,10,100}-{3785,10,100}-{3787,10,100}-{3789,10,100}" }, { "npcs": "1039", @@ -555,7 +546,7 @@ "general_store": "false", "id": "63", "title": "Barker's Haberdashery", - "stock": "{2894,5}-{2896,5}-{2898,5}-{2900,5}-{2902,5}-{2904,5}-{2906,5}-{2908,5}-{2910,5}-{2912,5}-{2914,5}-{2916,5}-{2918,5}-{2920,5}-{2922,5}-{2924,5}-{2926,5}-{2928,5}-{2930,5}-{2932,5}-{2934,5}-{2936,5}-{2938,5}-{2940,5}-{2942,5}-{1007,5}-{1023,5}-{1019,5}-{1021,5}-{1027,5}" + "stock": "{2894,5,100}-{2896,5,100}-{2898,5,100}-{2900,5,100}-{2902,5,100}-{2904,5,100}-{2906,5,100}-{2908,5,100}-{2910,5,100}-{2912,5,100}-{2914,5,100}-{2916,5,100}-{2918,5,100}-{2920,5,100}-{2922,5,100}-{2924,5,100}-{2926,5,100}-{2928,5,100}-{2930,5,100}-{2932,5,100}-{2934,5,100}-{2936,5,100}-{2938,5,100}-{2940,5,100}-{2942,5,100}-{1007,5,100}-{1023,5,100}-{1019,5,100}-{1021,5,100}-{1027,5,100}" }, { "npcs": "2353", @@ -564,7 +555,7 @@ "general_store": "false", "id": "64", "title": "Lletya Seamstress", - "stock": "{1734,1000}-{1733,10}-{1759,100}-{1763,10}-{1765,10}-{1767,10}-{1769,10}-{1771,10}-{1773,10}" + "stock": "{1734,1000,100}-{1733,10,100}-{1759,100,100}-{1763,10,100}-{1765,10,100}-{1767,10,100}-{1769,10,100}-{1771,10,100}-{1773,10,100}" }, { "npcs": "3166", @@ -573,7 +564,7 @@ "general_store": "false", "id": "65", "title": "Dodgy Mikes Second-hand Clothing", - "stock": "{7114,10}" + "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", @@ -591,7 +582,7 @@ "general_store": "false", "id": "67", "title": "Vermundi's Clothes Stall", - "stock": "{950,10}" + "stock": "{950,10,100}" }, { "npcs": "600", @@ -600,7 +591,7 @@ "general_store": "false", "id": "68", "title": "Grand Tree Groceries", - "stock": "{2171,10}-{2128,10}-{1933,10}-{2169,10}-{1957,10}-{1942,10}-{1965,10}-{1982,10}-{1985,10}-{2120,10}-{2108,10}-{2102,10}-{2114,10}-{2126,10}-{2025,10}-{1973,10}-{1975,10}-{2130,10}-{1927,10}-{946,10}-{2167,10}-{2164,10}-{2165,10}-{2166,10}" + "stock": "{2171,10,100}-{2128,10,100}-{1933,10,100}-{2169,10,100}-{1957,10,100}-{1942,10,100}-{1965,10,100}-{1982,10,100}-{1985,10,100}-{2120,10,100}-{2108,10,100}-{2102,10,100}-{2114,10,100}-{2126,10,100}-{2025,10,100}-{1973,10,100}-{1975,10,100}-{2130,10,100}-{1927,10,100}-{946,10,100}-{2167,10,100}-{2164,10,100}-{2165,10,100}-{2166,10,100}" }, { "npcs": "593", @@ -609,7 +600,7 @@ "general_store": "false", "id": "69", "title": "Frenita's Cookery Shop.", - "stock": "{2313,5}-{1955,2}-{1887,2}-{1923,2}-{1942,5}-{590,4}-{1935,1}-{1931,8}-{1973,2}-{1933,8}-{1980,20}" + "stock": "{2313,5,100}-{1955,2,100}-{1887,2,100}-{1923,2,100}-{1942,5,100}-{590,4,100}-{1935,1,100}-{1931,8,100}-{1973,2,100}-{1933,8,100}-{1980,20,100}" }, { "npcs": "545", @@ -618,7 +609,7 @@ "general_store": "false", "id": "70", "title": "Dommik's Crafting Store", - "stock": "{1755,10}-{1592,10}-{1597,10}-{1595,10}-{1733,10}-{1734,1000}-{1599,10}-{2976,10}-{5523,10}-{9434,10}-{11065,10}" + "stock": "{1755,10,100}-{1592,10,100}-{1597,10,100}-{1595,10,100}-{1733,10,100}-{1734,1000,100}-{1599,10,100}-{2976,10,100}-{5523,10,100}-{9434,10,100}-{11065,10,100}" }, { "npcs": "585", @@ -627,7 +618,7 @@ "general_store": "false", "id": "71", "title": "Rommik's Crafty Supplies", - "stock": "{1755,10}-{1592,10}-{1597,10}-{1595,10}-{1733,10}-{1734,1000}-{1599,10}-{2976,10}-{5523,10}-{9434,10}-{11065,10}" + "stock": "{1755,10,100}-{1592,10,100}-{1597,10,100}-{1595,10,100}-{1733,10,100}-{1734,1000,100}-{1599,10,100}-{2976,10,100}-{5523,10,100}-{9434,10,100}-{11065,10,100}" }, { "npcs": "5268", @@ -636,7 +627,7 @@ "general_store": "false", "id": "72", "title": "Jamila's Craft Stall", - "stock": "{1755,10}-{1592,10}-{1597,10}-{1595,10}-{1733,10}-{1734,1000}-{1599,10}-{2976,10}-{5523,10}-{11065,10}" + "stock": "{1755,10,100}-{1592,10,100}-{1597,10,100}-{1595,10,100}-{1733,10,100}-{1734,1000,100}-{1599,10,100}-{2976,10,100}-{5523,10,100}-{11065,10,100}" }, { "npcs": "2305", @@ -645,7 +636,7 @@ "general_store": "false", "id": "73", "title": "Vanessa's Farming Shop", - "stock": "{5376,10}-{6032,300}-{5418,10}-{5376,10}-{6036,10}-{5354,100}-{5341,10}-{5329,10}-{5343,10}-{952,10}-{5325,10}-{1925,100}-{5331,30}-{12622,10}-{5996,0}-{6006,0}-{1965,0}-{5994,0}-{5931,0}-{6000,0}-{1957,0}-{5504,0}-{5986,0}-{1982,0}-{5982,0}-{6002,0}-{5998,0}" + "stock": "{5376,10,100}-{6032,300,100}-{5418,10,100}-{6036,10,100}-{5350,100,100}-{5341,10,100}-{5329,10,100}-{5343,10,100}-{952,10,100}-{5325,10,100}-{1925,100,100}-{5331,30,100}-{12622,10,100}-{5996,0,100}-{6006,0,100}-{1965,0,100}-{5994,0,100}-{5931,0,100}-{6000,0,100}-{1957,0,100}-{5504,0,100}-{5986,0,100}-{1982,0,100}-{5982,0,100}-{6002,0,100}-{5998,0,100}" }, { "npcs": "2307", @@ -654,7 +645,7 @@ "general_store": "false", "id": "74", "title": "Alice's Farming Shop", - "stock": "{5376,10}-{6032,300}-{5418,10}-{5376,10}-{6036,10}-{5354,100}-{5341,10}-{5329,10}-{5343,10}-{952,10}-{5325,10}-{1925,100}-{5331,30}-{12622,10}-{5996,0}-{6006,0}-{1965,0}-{5994,0}-{5931,0}-{6000,0}-{1957,0}-{5504,0}-{5986,0}-{1982,0}-{5982,0}-{6002,0}-{5998,0}" + "stock": "{5376,10,100}-{6032,300,100}-{5418,10,100}-{6036,10,100}-{5350,100,100}-{5341,10,100}-{5329,10,100}-{5343,10,100}-{952,10,100}-{5325,10,100}-{1925,100,100}-{5331,30,100}-{12622,10,100}-{5996,0,100}-{6006,0,100}-{1965,0,100}-{5994,0,100}-{5931,0,100}-{6000,0,100}-{1957,0,100}-{5504,0,100}-{5986,0,100}-{1982,0,100}-{5982,0,100}-{6002,0,100}-{5998,0,100}" }, { "npcs": "2304", @@ -663,7 +654,7 @@ "general_store": "false", "id": "75", "title": "Sarah's Farming Shop", - "stock": "{5376,10}-{6032,300}-{5418,10}-{6036,10}-{5354,100}-{5341,10}-{5329,10}-{5343,10}-{952,10}-{5325,10}-{1925,100}-{5331,30}-{12622,10}-{5996,0}-{6006,0}-{1965,0}-{5994,0}-{5931,0}-{6000,0}-{1957,0}-{5504,0}-{5986,0}-{1982,0}-{5982,0}-{6002,0}-{5998,0}" + "stock": "{5376,10,100}-{6032,300,100}-{5418,10,100}-{6036,10,100}-{5350,100,100}-{5341,10,100}-{5329,10,100}-{5343,10,100}-{952,10,100}-{5325,10,100}-{1925,100,100}-{5331,30,100}-{12622,10,100}-{5996,0,100}-{6006,0,100}-{1965,0,100}-{5994,0,100}-{5931,0,100}-{6000,0,100}-{1957,0,100}-{5504,0,100}-{5986,0,100}-{1982,0,100}-{5982,0,100}-{6002,0,100}-{5998,0,100}" }, { "npcs": "2306", @@ -672,7 +663,7 @@ "general_store": "false", "id": "76", "title": "Richard's Farming Shop", - "stock": "{5376,10}-{6032,300}-{5418,10}-{5376,10}-{6036,10}-{5354,100}-{5341,10}-{5329,10}-{5343,10}-{952,10}-{5325,10}-{1925,100}-{5331,30}-{12622,10}-{5996,0}-{6006,0}-{1965,0}-{5994,0}-{5931,0}-{6000,0}-{1957,0}-{5504,0}-{5986,0}-{1982,0}-{5982,0}-{6002,0}-{5998,0}" + "stock": "{5376,10,100}-{6032,300,100}-{5418,10,100}-{6036,10,100}-{5350,100,100}-{5341,10,100}-{5329,10,100}-{5343,10,100}-{952,10,100}-{5325,10,100}-{1925,100,100}-{5331,30,100}-{12622,10,100}-{5996,0,100}-{6006,0,100}-{1965,0,100}-{5994,0,100}-{5931,0,100}-{6000,0,100}-{1957,0,100}-{5504,0,100}-{5986,0,100}-{1982,0,100}-{5982,0,100}-{6002,0,100}-{5998,0,100}" }, { "npcs": "557", @@ -681,7 +672,7 @@ "general_store": "false", "id": "77", "title": "Wydin's Food Store", - "stock": "{1933,500}-{2132,10}-{2138,10}-{1965,10}-{1963,0}-{1951,0}-{2309,10}-{1973,10}-{1985,10}-{1982,10}-{1550,10}" + "stock": "{1933,500,100}-{2132,10,100}-{2138,10,100}-{1965,10,100}-{1963,0,100}-{1951,0,100}-{2309,10,100}-{1973,10,100}-{1985,10,100}-{1982,10,100}-{1550,10,100}" }, { "npcs": "1038", @@ -690,7 +681,7 @@ "general_store": "false", "id": "78", "title": "Rufus's Meat Emporium", - "stock": "{2132,100}-{2138,30}-{2134,10}-{2136,10}-{335,0}-{349,0}-{331,0}-{383,0}" + "stock": "{2132,100,100}-{2138,30,100}-{2134,10,100}-{2136,10,100}-{335,0,100}-{349,0,100}-{331,0,100}-{383,0,100}" }, { "npcs": "1433", @@ -699,16 +690,16 @@ "general_store": "false", "id": "79", "title": "Solihib's Consumables Stall", - "stock": "{4012,10}-{1963,10}-{4016,10}-{4014,10}" + "stock": "{4012,10,100}-{1963,10,100}-{4016,10,100}-{4014,10,100}" }, { - "npcs": "7053", + "npcs": "7054", "high_alch": "0", "currency": "995", "general_store": "false", "id": "80", "title": "Fresh Meat", - "stock": "{2134,10}-{2132,300}-{2136,10}-{2142,10}-{2138,10}-{2140,10}-{3226,100}-{7224,0}-{7223,0}-{3228,10}-{2876,0}-{7230,0}-{2878,0}-{7566,0}-{2337,0}-{9978,10}-{9984,0}-{9980,0}-{9986,0}-{9992,0}-{9988,0}" + "stock": "{2134,10,100}-{2132,300,100}-{2136,10,100}-{2142,10,100}-{2138,10,100}-{2140,10,100}-{3226,100,100}-{7224,0,100}-{7223,0,100}-{3228,10,100}-{2876,0,100}-{7230,0,100}-{2878,0,100}-{7566,0,100}-{2337,0,100}-{9978,100,100}-{9984,0,100}-{9980,0,100}-{9986,0,100}-{9992,0,100}-{9988,0,100}" }, { "npcs": "571", @@ -717,7 +708,7 @@ "general_store": "false", "id": "81", "title": "Ardougne Baker's Stall", - "stock": "{2309,10}-{1891,10}-{1973,100}" + "stock": "{2309,10,100}-{1891,10,100}-{1973,100,100}" }, { "npcs": "5487", @@ -726,7 +717,7 @@ "general_store": "false", "id": "82", "title": "Keepa Kettilon's store", - "stock": "{361,20}-{329,20}-{339,20}-{379,10}-{373,0}-{385,0}" + "stock": "{361,20,100}-{329,20,100}-{339,20,100}-{379,10,100}-{373,0,100}-{385,0,100}" }, { "npcs": "851", @@ -735,7 +726,7 @@ "general_store": "false", "id": "83", "title": "Gianne's Restaurant", - "stock": "{2223,10}-{2225,10}-{2221,10}-{2219,10}-{2227,10}-{2233,10}-{2231,10}-{2235,10}-{2229,10}-{2241,10}-{2243,10}-{2239,10}-{2237,10}" + "stock": "{2223,10,100}-{2225,10,100}-{2221,10,100}-{2219,10,100}-{2227,10,100}-{2233,10,100}-{2231,10,100}-{2235,10,100}-{2229,10,100}-{2241,10,100}-{2243,10,100}-{2239,10,100}-{2237,10,100}" }, { "npcs": "4294", @@ -744,7 +735,7 @@ "general_store": "false", "id": "84", "title": "Warriors' Guild Potion Shop", - "stock": "{115,10}-{121,10}-{133,10}" + "stock": "{115,10,100}-{121,10,100}-{133,10,100}" }, { "npcs": "4293", @@ -753,7 +744,7 @@ "general_store": "false", "id": "85", "title": "Warriors' Guild Food Shop", - "stock": "{333,10}-{365,10}-{2289,10}-{6705,10}-{2003,10}" + "stock": "{333,10,100}-{365,10,100}-{2289,10,100}-{6705,10,100}-{2003,10,100}" }, { "npcs": "4295", @@ -762,7 +753,7 @@ "general_store": "false", "id": "86", "title": "Warriors' Guild Armoury", - "stock": "{1363,10}-{1365,10}-{1369,10}-{1277,10}-{1279,10}-{1281,10}-{1283,10}-{1285,10}-{1287,10}-{1307,10}-{1309,10}-{1311,10}-{1313,10}-{1315,10}-{1317,10}-{1211,10}-{1301,10}-{1317,10}-{1297,10}" + "stock": "{1363,10,100}-{1365,10,100}-{1369,10,100}-{1277,10,100}-{1279,10,100}-{1281,10,100}-{1283,10,100}-{1285,10,100}-{1287,10,100}-{1307,10,100}-{1309,10,100}-{1311,10,100}-{1313,10,100}-{1315,10,100}-{1317,10,100}-{1211,10,100}-{1301,10,100}-{1297,10,100}" }, { "npcs": "584", @@ -771,7 +762,7 @@ "general_store": "false", "id": "87", "title": "Herquin's Gems", - "stock": "{1623,0}-{1621,0}-{1619,0}-{1617,0}-{1607,0}-{1605,0}-{1603,0}-{1601,0}" + "stock": "{1623,0,100}-{1621,0,100}-{1619,0,100}-{1617,0,100}-{1607,0,100}-{1605,0,100}-{1603,0,100}-{1601,0,100}" }, { "npcs": "570", @@ -780,7 +771,7 @@ "general_store": "false", "id": "88", "title": "Ardougne Gem Stall", - "stock": "{1623,0}-{1621,0}-{1619,0}-{1617,0}-{1607,0}-{1605,0}-{1603,0}-{1601,0}" + "stock": "{1623,0,100}-{1621,0,100}-{1619,0,100}-{1617,0,100}-{1607,0,100}-{1605,0,100}-{1603,0,100}-{1601,0,100}" }, { "npcs": "2157", @@ -789,7 +780,7 @@ "general_store": "false", "id": "89", "title": "Green Gemstone Gem", - "stock": "{1607,0}-{1605,0}-{1603,0}-{1601,0}" + "stock": "{1607,0,100}-{1605,0,100}-{1603,0,100}-{1601,0,100}" }, { "npcs": "540", @@ -798,7 +789,7 @@ "general_store": "false", "id": "90", "title": "Gem Trader (stall)", - "stock": "{1623,0}-{1621,0}-{1619,0}-{1617,0}-{1607,0}-{1605,0}-{1603,0}-{1601,0}" + "stock": "{1623,0,100}-{1621,0,100}-{1619,0,100}-{1617,0,100}-{1607,0,100}-{1605,0,100}-{1603,0,100}-{1601,0,100}" }, { "npcs": "540", @@ -807,7 +798,7 @@ "general_store": "false", "id": "91", "title": "Gem Trader (stall)", - "stock": "{1623,0}-{1621,0}-{1619,0}-{1617,0}-{1607,0}-{1605,0}-{1603,0}-{1601,0}" + "stock": "{1623,0,100}-{1621,0,100}-{1619,0,100}-{1617,0,100}-{1607,0,100}-{1605,0,100}-{1603,0,100}-{1601,0,100}" }, { "npcs": "2157", @@ -816,7 +807,7 @@ "general_store": "false", "id": "92", "title": "Gem Store", - "stock": "{1623,1}-{1621,1}-{1619,0}-{1617,0}" + "stock": "{1623,1,100}-{1621,1,100}-{1619,0,100}-{1617,0,100}" }, { "npcs": "", @@ -825,7 +816,7 @@ "general_store": "false", "id": "93", "title": "Pelters' Veg Stall", - "stock": "{2518,100}" + "stock": "{2518,100,100}" }, { "npcs": "569", @@ -834,7 +825,7 @@ "general_store": "false", "id": "94", "title": "Ardougne Silver Stall", - "stock": "{1714,10}-{442,0}-{2355,0}" + "stock": "{1714,10,100}-{442,0,100}-{2355,0,100}" }, { "npcs": "572", @@ -843,7 +834,7 @@ "general_store": "false", "id": "95", "title": "Ardougne Spice Stall", - "stock": "{2007,10}-{946,10}-{1550,10}" + "stock": "{2007,10,100}-{946,10,100}-{1550,10,100}" }, { "npcs": "958", @@ -852,7 +843,7 @@ "general_store": "false", "id": "96", "title": "Shop of Distate", - "stock": "{2518,1}" + "stock": "{2518,1,100}" }, { "npcs": "573", @@ -861,7 +852,7 @@ "general_store": "false", "id": "97", "title": "Ardougne Fur Stall", - "stock": "{948,10}-{958,10}-{10117,0}-{10121,0}-{10119,0}-{10123,0}-{10093,0}-{10095,0}-{10097,0}-{10099,0}-{10101,0}-{10103,0}" + "stock": "{948,10,100}-{958,10,100}-{10117,0,100}-{10121,0,100}-{10119,0,100}-{10123,0,100}-{10093,0,100}-{10095,0,100}-{10097,0,100}-{10099,0,100}-{10101,0,100}-{10103,0,100}" }, { "npcs": "538", @@ -870,7 +861,7 @@ "general_store": "false", "id": "98", "title": "Peksa's Helmet Shop", - "stock": "{1139,5}-{1137,3}-{1141,3}-{1143,1}-{1145,1}-{1155,4}-{1153,3}-{1157,2}-{1159,1}-{1161,1}" + "stock": "{1139,5,100}-{1137,3,100}-{1141,3,100}-{1143,1,100}-{1145,1,100}-{1155,4,100}-{1153,3,100}-{1157,2,100}-{1159,1,100}-{1161,1,100}" }, { "npcs": "1303", @@ -879,7 +870,7 @@ "general_store": "false", "id": "99", "title": "Skulgrimen's Battle Gear", - "stock": "{1337,10}-{1335,10}-{1339,10}-{1341,10}-{1343,10}-{1347,0}-{3749,10}-{3751,10}-{3753,10}-{3755,10}" + "stock": "{1337,10,100}-{1335,10,100}-{1339,10,100}-{1341,10,100}-{1343,10,100}-{1347,0,100}-{3749,10,100}-{3751,10,100}-{3753,10,100}-{3755,10,100}" }, { "npcs": "595", @@ -888,7 +879,7 @@ "general_store": "false", "id": "100", "title": "Tea Shop", - "stock": "{712,10}" + "stock": "{712,10,100}" }, { "npcs": "704", @@ -897,7 +888,7 @@ "general_store": "false", "id": "102", "title": "Grud's Herblore Stall.", - "stock": "{229,10}-{233,10}-{221,10}" + "stock": "{229,10,100}-{233,10,100}-{221,10,100}" }, { "npcs": "587", @@ -906,7 +897,7 @@ "general_store": "false", "id": "103", "title": "Jatix's Herblore Shop", - "stock": "{229,inf}-{233,inf}-{221,inf}" + "stock": "{229,300,480}-{233,10,100}-{221,1000,100}" }, { "npcs": "5110", @@ -915,7 +906,7 @@ "general_store": "false", "id": "104", "title": "Aleck's Hunter Emporium", - "stock": "{10010,10}-{10012,300}-{10025,300}-{10150,10}-{10006,30}-{10008,30}-{10029,10}-{596,10}-{10031,30}-{12539,0}" + "stock": "{10010,10,100}-{10012,300,100}-{10025,300,100}-{10150,10,100}-{10006,30,100}-{10008,30,100}-{10029,10,100}-{596,10,100}-{10031,30,100}-{12539,0,100}" }, { "npcs": "5109", @@ -924,7 +915,7 @@ "general_store": "false", "id": "105", "title": "Nardah Hunter Shop", - "stock": "{10010,10}-{10012,300}-{10025,300}-{10150,10}-{10006,30}-{10008,30}-{10029,10}-{596,10}-{10031,30}-{12539,0}" + "stock": "{10010,10,100}-{10012,300,100}-{10025,300,100}-{10150,10,100}-{10006,30,100}-{10008,30,100}-{10029,10,100}-{596,10,100}-{10031,30,100}-{12539,0,100}" }, { "npcs": "556", @@ -933,7 +924,7 @@ "general_store": "false", "id": "106", "title": "Grum's Gold Exchange", - "stock": "{1635,0}-{1637,0}-{1639,0}-{1641,0}-{1643,0}-{1654,0}-{1656,0}-{1658,0}-{1660,0}-{1662,0}-{1692,0}-{1694,0}-{1696,0}-{1698,0}-{1700,0}-{11069,0}-{11072,0}-{11076,0}-{11085,0}-{11092,0}" + "stock": "{1635,0,100}-{1637,0,100}-{1639,0,100}-{1641,0,100}-{1643,0,100}-{1654,0,100}-{1656,0,100}-{1658,0,100}-{1660,0,100}-{1662,0,100}-{1692,0,100}-{1694,0,100}-{1696,0,100}-{1698,0,100}-{1700,0,100}-{11069,0,100}-{11072,0,100}-{11076,0,100}-{11085,0,100}-{11092,0,100}" }, { "npcs": "1862", @@ -941,8 +932,8 @@ "currency": "995", "general_store": "false", "id": "107", - "title": "Ali's Discount Wares.", - "stock": "{1931,30}-{1935,10}-{1825,10}-{1833,10}-{1837,10}-{1925,30}-{4593,10}-{4591,10}-{970,1}-{946,10}-{590,10}-{1265,10}-{2138,10}" + "title": "Ali's Discount Wares", + "stock": "{1931,30,100}-{1935,10,100}-{1825,10,100}-{1833,10,100}-{1837,10,100}-{1925,30,100}-{4593,10,100}-{4591,10,100}-{970,1,100}-{946,10,100}-{590,10,100}-{1265,10,100}-{2138,10,100}" }, { "npcs": "1865", @@ -951,7 +942,7 @@ "general_store": "false", "id": "108", "title": "Ali the Kebab Seller", - "stock": "{1971,10}" + "stock": "{1971,10,100}" }, { "npcs": "543", @@ -960,7 +951,7 @@ "general_store": "false", "id": "109", "title": "Karim", - "stock": "{1971,10}" + "stock": "{1971,10,100}" }, { "npcs": "2198", @@ -969,7 +960,7 @@ "general_store": "false", "id": "110", "title": "Kjut's KEbabs", - "stock": "{1971,10}" + "stock": "{1971,10,100}" }, { "npcs": "580", @@ -978,7 +969,7 @@ "general_store": "false", "id": "111", "title": "Flynn's Mace Market", - "stock": "{1422,5}-{1420,4}-{1424,4}-{1428,3}-{1430,2}" + "stock": "{1422,5,100}-{1420,4,100}-{1424,4,100}-{1428,3,100}-{1430,2,100}" }, { "npcs": "1862", @@ -987,7 +978,7 @@ "general_store": "false", "id": "112", "title": "Ali's Discount Wares#runes", - "stock": "{1931,30}-{1935,10}-{1833,10}-{1837,10}-{1925,10}-{4593,10}-{4591,10}-{970,10}-{946,10}-{590,10}-{1265,10}-{2138,10}-{6416,10}-{4600,10}-{6416,10}-{4600,10}-{556,300}-{555,300}-{557,300}-{554,300}-{561,300}-{563,300}-{562,300}-{560,300}-{558,300}-{559,300}-{565,100}-{564,300}-{566,100}-{6382,30}-{6388,30}-{6390,30}-{1835,30}" + "stock": "{1931,30,100}-{1935,10,100}-{1833,10,100}-{1837,10,100}-{1925,10,100}-{4593,10,100}-{4591,10,100}-{970,10,100}-{946,10,100}-{590,10,100}-{1265,10,100}-{2138,10,100}-{6416,10,100}-{4600,10,100}-{556,300,100}-{555,300,100}-{557,300,100}-{554,300,100}-{561,300,100}-{563,300,100}-{562,300,100}-{560,300,100}-{558,300,100}-{559,300,100}-{565,100,100}-{564,300,100}-{566,100,100}-{6382,30,100}-{6388,30,100}-{6390,30,100}-{1835,30,100}" }, { "npcs": "583", @@ -996,7 +987,7 @@ "general_store": "false", "id": "113", "title": "Betty's Magic Emporium", - "stock": "{554,300}-{555,300}-{556,300}-{557,300}-{558,100}-{559,100}-{562,30}-{560,10}-{221,100}-{579,10}-{1017,10}" + "stock": "{554,300,100}-{555,300,100}-{556,300,100}-{557,300,100}-{558,100,100}-{559,100,100}-{562,30,100}-{560,10,100}-{221,100,100}-{579,10,100}-{1017,10,100}" }, { "npcs": "553", @@ -1005,7 +996,7 @@ "general_store": "false", "id": "114", "title": "Aubury's Rune Shop", - "stock": "{554,5000}-{555,5000}-{556,5000}-{557,5000}-{558,5000}-{559,5000}-{562,500}-{560,500}" + "stock": "{554,5000,100}-{555,5000,100}-{556,5000,100}-{557,5000,100}-{558,5000,100}-{559,5000,100}-{562,500,100}-{560,500,100}" }, { "npcs": "461", @@ -1014,7 +1005,7 @@ "general_store": "false", "id": "115", "title": "Magic Guild Store", - "stock": "{556,5000}-{555,5000}-{557,5000}-{554,5000}-{558,5000}-{559,5000}-{562,250}-{561,250}-{560,250}-{563,250}-{565,250}-{566,250}-{1391,5}-{1387,2}-{1383,2}-{1381,2}-{1385,2}" + "stock": "{556,5000,100}-{555,5000,100}-{557,5000,100}-{554,5000,100}-{558,5000,100}-{559,5000,100}-{562,250,100}-{561,250,100}-{560,250,100}-{563,250,100}-{565,250,100}-{566,250,100}-{1391,5,100}-{1387,2,100}-{1383,2,100}-{1381,2,100}-{1385,2,100}" }, { "npcs": "4513", @@ -1023,7 +1014,7 @@ "general_store": "false", "id": "116", "title": "Baba Yaga's Magic Shop", - "stock": "{556,1000}-{555,1000}-{557,1000}-{554,1000}-{558,1000}-{559,1000}-{562,300}-{561,300}-{560,300}-{563,100}-{565,100}-{566,100}-{9075,100}-{1391,20}-{1387,10}-{1383,10}-{1381,10}-{1385,10}-{9078,10}" + "stock": "{556,1000,100}-{555,1000,100}-{557,1000,100}-{554,1000,100}-{558,1000,100}-{559,1000,100}-{562,300,100}-{561,300,100}-{560,300,100}-{563,100,100}-{565,100,100}-{566,100,100}-{9075,100,100}-{1391,20,100}-{1387,10,100}-{1383,10,100}-{1381,10,100}-{1385,10,100}-{9078,10,100}" }, { "npcs": "3097", @@ -1041,7 +1032,7 @@ "general_store": "false", "id": "118", "title": "Lundail's Arena-side Rune Shop", - "stock": "{554,1000}-{555,1000}-{556,1000}-{557,1000}-{558,1000}-{559,1000}-{561,300}-{562,300}-{563,100}-{564,300}-{560,300}" + "stock": "{554,1000,100}-{555,1000,100}-{556,1000,100}-{557,1000,100}-{558,1000,100}-{559,1000,100}-{561,300,100}-{562,300,100}-{563,100,100}-{564,300,100}-{560,300,100}" }, { "npcs": "1435", @@ -1050,7 +1041,7 @@ "general_store": "false", "id": "119", "title": "Tutab's Magical Market", - "stock": "{554,1000}-{555,1000}-{556,1000}-{557,1000}-{563,100}-{4009,10}-{4006,10}-{4023,10}" + "stock": "{554,1000,100}-{555,1000,100}-{556,1000,100}-{557,1000,100}-{563,100,100}-{4009,10,100}-{4006,10,100}-{4023,10,100}" }, { "npcs": "3798", @@ -1059,7 +1050,7 @@ "general_store": "false", "id": "120", "title": "Void Knight Magic Store", - "stock": "{554,1000}-{555,1000}-{556,1000}-{557,1000}-{558,1000}-{559,1000}-{562,300}-{560,300}" + "stock": "{554,1000,100}-{555,1000,100}-{556,1000,100}-{557,1000,100}-{558,1000,100}-{559,1000,100}-{562,300,100}-{560,300,100}" }, { "npcs": "2623", @@ -1068,7 +1059,7 @@ "general_store": "false", "id": "121", "title": "Tzhaar-mej-roh's Rune Store", - "stock": "{554,1000}-{555,1000}-{557,1000}-{556,1000}-{558,1000}-{559,1000}-{562,300}-{560,100}" + "stock": "{554,1000,100}-{555,1000,100}-{557,1000,100}-{556,1000,100}-{558,1000,100}-{559,1000,100}-{562,300,100}-{560,100,100}" }, { "npcs": "2622", @@ -1077,7 +1068,7 @@ "general_store": "false", "id": "122", "title": "Tzhaar-Hur-Lek's Ore and Gem Store.", - "stock": "{438,5}-{436,5}-{440,2}-{442,0}-{453,0}-{444,0}-{447,0}-{449,0}-{451,0}-{1623,1}-{1621,1}-{1619,0}-{1617,0}-{1631,0}-{6571,1}-{9194,50}" + "stock": "{438,5,100}-{436,5,100}-{440,2,100}-{442,0,100}-{453,0,100}-{444,0,100}-{447,0,100}-{449,0,100}-{451,0,100}-{1623,1,100}-{1621,1,100}-{1619,0,100}-{1617,0,100}-{1631,0,100}-{6571,1,100}-{9194,50,100}" }, { "npcs": "1658", @@ -1086,7 +1077,7 @@ "general_store": "false", "id": "123", "title": "Magic Guild Store", - "stock": "{4089,1000}-{4091,1000}-{4093,1000}-{4095,1000}-{4097,1000}" + "stock": "{4089,1000,100}-{4091,1000,100}-{4093,1000,100}-{4095,1000,100}-{4097,1000,100}" }, { "npcs": "594", @@ -1095,7 +1086,7 @@ "general_store": "false", "id": "124", "title": "Nurmof's Pickaxe Shop", - "stock": "{1265,10}-{1267,10}-{1269,10}-{1273,10}-{1271,10}-{1275,10}" + "stock": "{1265,10,100}-{1267,10,100}-{1269,10,100}-{1273,10,100}-{1271,10,100}-{1275,10,100}" }, { "npcs": "579", @@ -1104,7 +1095,7 @@ "general_store": "false", "id": "125", "title": "Drogo's Mining Emporium", - "stock": "{2347,10}-{1265,10}-{436,0}-{438,0}-{440,0}-{453,0}-{2349,0}-{2351,0}-{2357,0}" + "stock": "{2347,10,100}-{1265,10,100}-{436,0,100}-{438,0,100}-{440,0,100}-{453,0,100}-{2349,0,100}-{2351,0,100}-{2357,0,100}" }, { "npcs": "2160", @@ -1112,8 +1103,8 @@ "currency": "995", "general_store": "false", "id": "126", - "title": "Pickaxe-is-mine", - "stock": "{1265,10}-{1269,10}-{1273,10}-{1271,10}-{1275,10}" + "title": "Pickaxe-Is-Mine", + "stock": "{1265,10,100}-{1269,10,100}-{1273,10,100}-{1271,10,100}-{1275,10,100}" }, { "npcs": "6750,6898,6893", @@ -1122,7 +1113,7 @@ "general_store": "false", "id": "127", "title": "Pet Shop", - "stock": "{12130,10}-{12125,1000}-{12127,10}-{12183,125000}" + "stock": "{12130,10,100}-{12125,1000,100}-{12127,10,100}-{12183,125000,100}" }, { "npcs": "589", @@ -1131,7 +1122,7 @@ "general_store": "false", "id": "128", "title": "Zenesha's Platebody Shop", - "stock": "{1117,10}-{1115,10}-{1119,10}-{1125,10}-{1121,10}" + "stock": "{1117,10,100}-{1115,10,100}-{1119,10,100}-{1125,10,100}-{1121,10,100}" }, { "npcs": "549", @@ -1140,7 +1131,7 @@ "general_store": "false", "id": "129", "title": "Horvik's Armour Shop", - "stock": "{1103,10}-{1101,10}-{1173,10}-{1175,10}-{1075,10}-{1067,10}-{1087,10}-{1081,10}-{1117,10}-{1115,10}-{1119,10}-{1125,10}-{1121,10}" + "stock": "{1103,10,100}-{1101,10,100}-{1173,10,100}-{1175,10,100}-{1075,10,100}-{1067,10,100}-{1087,10,100}-{1081,10,100}-{1117,10,100}-{1115,10,100}-{1119,10,100}-{1125,10,100}-{1121,10,100}-{1097,10,100}-{1133,10,100}" }, { "npcs": "5485", @@ -1149,7 +1140,7 @@ "general_store": "false", "id": "130", "title": "Armour Shop", - "stock": "{1109,4}-{1143,4}-{1159,4}-{1181,4}-{1197,4}-{1071,4}-{1085,4}-{1121,4}" + "stock": "{1109,4,100}-{1143,4,100}-{1159,4,100}-{1181,4,100}-{1197,4,100}-{1071,4,100}-{1085,4,100}-{1121,4,100}" }, { "npcs": "542", @@ -1158,7 +1149,7 @@ "general_store": "false", "id": "131", "title": "Louie's Armoured Legs Bazaar", - "stock": "{1075,10}-{1067,10}-{1069,10}-{1077,10}-{1071,10}-{1073,10}" + "stock": "{1075,10,100}-{1067,10,100}-{1069,10,100}-{1077,10,100}-{1071,10,100}-{1073,10,100}" }, { "npcs": "549", @@ -1167,7 +1158,7 @@ "general_store": "false", "id": "132", "title": "Horvik's Armour Shop", - "stock": "{1103,10}-{1101,10}-{1173,10}-{1175,10}-{1075,10}-{1067,10}-{1087,10}-{1081,10}-{1117,10}-{1115,10}-{1119,10}-{1125,10}-{1121,10}" + "stock": "{1103,10,100}-{1101,10,100}-{1173,10,100}-{1175,10,100}-{1075,10,100}-{1067,10,100}-{1087,10,100}-{1081,10,100}-{1117,10,100}-{1115,10,100}-{1119,10,100}-{1125,10,100}-{1121,10,100}-{1097,10,100}-{1133,10,100}" }, { "npcs": "3038", @@ -1176,7 +1167,7 @@ "general_store": "false", "id": "133", "title": "Seddus Adventurers Store", - "stock": "{1093,10}-{1079,10}-{1113,10}-{1099,10}-{1193,10}" + "stock": "{1093,10,100}-{1079,10,100}-{1113,10,100}-{1099,10,100}-{1193,10,100}" }, { "npcs": "544", @@ -1185,7 +1176,7 @@ "general_store": "false", "id": "134", "title": "Ranael's Super Skirt Store", - "stock": "{1087,10}-{1081,10}-{1083,10}-{1089,10}-{1085,10}-{1091,10}" + "stock": "{1087,10,100}-{1081,10,100}-{1083,10,100}-{1089,10,100}-{1085,10,100}-{1091,10,100}" }, { "npcs": "541", @@ -1194,7 +1185,7 @@ "general_store": "false", "id": "135", "title": "Zeke's Superior Scimitars", - "stock": "{1321,10}-{1323,10}-{1325,10}-{1329,10}" + "stock": "{1321,10,100}-{1323,10,100}-{1325,10,100}-{1329,10,100}" }, { "npcs": "1434", @@ -1203,7 +1194,7 @@ "general_store": "false", "id": "136", "title": "Daga's Scimitar Smithy", - "stock": "{1321,10}-{1323,10}-{1325,10}-{1329,10}-{4587,10}" + "stock": "{1321,10,100}-{1323,10,100}-{1325,10,100}-{1329,10,100}-{4587,10,100}" }, { "npcs": "577", @@ -1212,7 +1203,7 @@ "general_store": "false", "id": "137", "title": "Cassie's Shield Shop", - "stock": "{1171,5}-{1173,3}-{1189,3}-{1175,2}-{1191,0}-{1177,0}-{1193,0}-{1181,0}" + "stock": "{1171,5,100}-{1173,3,100}-{1189,3,100}-{1175,2,100}-{1191,0,100}-{1177,0,100}-{1193,0,100}-{1181,0,100}" }, { "npcs": "209", @@ -1221,7 +1212,7 @@ "general_store": "false", "id": "138", "title": "Multicannon parts for sale", - "stock": "{6,5}-{8,5}-{10,5}-{12,5}-{5,5}-{4,5}" + "stock": "{6,5,100}-{8,5,100}-{10,5,100}-{12,5,100}-{5,5,100}-{4,5,100}" }, { "npcs": "1980", @@ -1229,8 +1220,8 @@ "currency": "995", "general_store": "false", "id": "139", - "title": "Spice Shop", - "stock": "{1931,30}-{2169,10}-{5970,0}" + "title": "The Spice Is Right", + "stock": "{1931,30,100}-{2169,10,100}-{5970,0,100}-{175,10,100}" }, { "npcs": "546", @@ -1239,7 +1230,7 @@ "general_store": "false", "id": "140", "title": "Staff Shop", - "stock": "{1379,10}-{1389,10}-{1381,10}-{1383,10}-{1385,10}-{1387,10}-{2890,10}" + "stock": "{1379,10,100}-{1389,10,100}-{1381,10,100}-{1383,10,100}-{1385,10,100}-{1387,10,100}" }, { "npcs": "6971", @@ -1248,16 +1239,16 @@ "general_store": "false", "id": "141", "title": "Pikkupstix's Summoning Shop", - "stock": "{12204,10}-{12207,10}-{12210,10}-{12213,10}-{12216,10}-{12219,10}-{12222,10}-{12183,125000}-{12155,5000}" + "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}-{12207,10}-{12210,10}-{12213,10}-{12216,10}-{12219,10}-{12222,10}-{12183,125000}-{12155,5000}" + "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}-{1937,10}-{7919,10}-{1935,10}" + "stock": "{1993,10,100}-{7810,10,100}-{7919,10,100}-{1935,10,100}" }, { "npcs": "558", @@ -1275,7 +1266,7 @@ "general_store": "false", "id": "144", "title": "Gerrant's Fishy Business", - "stock": "{303,10}-{307,10}-{309,10}-{311,30}-{301,10}-{13431,10}-{313,1000}-{314,1000}-{317,0}-{327,10}-{345,0}-{321,0}-{335,0}-{349,0}-{331,0}-{359,0}-{377,0}-{371,0}" + "stock": "{303,10,100}-{307,10,100}-{309,10,100}-{311,30,100}-{301,10,100}-{13431,10,100}-{313,1000,100}-{314,1000,100}-{317,0,100}-{327,10,100}-{345,0,100}-{321,0,100}-{335,0,100}-{349,0,100}-{331,0,100}-{359,0,100}-{377,0,100}-{371,0,100}" }, { "npcs": "576", @@ -1284,7 +1275,7 @@ "general_store": "false", "id": "145", "title": "Harry's Fishing Shop", - "stock": "{303,10}-{307,10}-{311,1000}-{301,10}-{313,100}-{305,10}-{317,0}-{327,0}-{345,0}-{353,0}-{341,0}-{321,0}-{359,0}-{377,0}-{363,0}-{371,0}-{383,0}-{7810,1000}" + "stock": "{303,10,100}-{307,10,100}-{311,1000,100}-{301,10,100}-{313,100,100}-{305,10,100}-{317,0,100}-{327,0,100}-{345,0,100}-{353,0,100}-{341,0,100}-{321,0,100}-{359,0,100}-{377,0,100}-{363,0,100}-{371,0,100}-{383,0,100}-{7810,1000,100}" }, { "npcs": "5266", @@ -1293,7 +1284,7 @@ "general_store": "false", "id": "146", "title": "Blades By Urbi", - "stock": "{1205,10}-{1203,10}-{1207,10}-{1217,0}-{1209,10}-{1211,10}-{1213,10}-{1215,0}-{1321,10}-{1323,10}-{1325,10}" + "stock": "{1205,10,100}-{1203,10,100}-{1207,10,100}-{1217,0,100}-{1209,10,100}-{1211,10,100}-{1213,10,100}-{1215,0,100}-{1321,10,100}-{1323,10,100}-{1325,10,100}" }, { "npcs": "586", @@ -1302,7 +1293,7 @@ "general_store": "false", "id": "147", "title": "Gaius' Two Handed Shop.", - "stock": "{1307,4}-{1309,3}-{1311,2}-{1313,1}-{1315,1}-{1317,1}" + "stock": "{1307,4,100}-{1309,3,100}-{1311,2,100}-{1313,1,100}-{1315,1,100}-{1317,1,100}" }, { "npcs": "602", @@ -1311,7 +1302,7 @@ "general_store": "false", "id": "148", "title": "Gulluck And Sons", - "stock": "{882,1000}-{877,300}-{880,0}-{841,10}-{837,10}-{1363,10}-{1365,10}-{1369,10}-{1307,10}-{1309,10}-{1311,10}-{1313,10}-{1315,10}-{1317,10}" + "stock": "{882,1000,100}-{877,300,100}-{880,0,100}-{841,10,100}-{837,10,100}-{1363,10,100}-{1365,10,100}-{1369,10,100}-{1307,10,100}-{1309,10,100}-{1311,10,100}-{1313,10,100}-{1315,10,100}-{1317,10,100}" }, { "npcs": "552,551", @@ -1320,7 +1311,7 @@ "general_store": "false", "id": "149", "title": "Varrock Sword Shop", - "stock": "{1277,10}-{1277,10}-{1279,10}-{1281,10}-{1283,10}-{1285,10}-{1287,10}-{1291,10}-{1293,10}-{1295,10}-{1297,10}-{1299,10}-{1301,10}-{1205,10}-{1203,10}-{1207,10}-{1217,10}-{1209,10}-{1211,10}" + "stock": "{1277,10,100}-{1279,10,100}-{1281,10,100}-{1283,10,100}-{1285,10,100}-{1287,10,100}-{1291,10,100}-{1293,10,100}-{1295,10,100}-{1297,10,100}-{1299,10,100}-{1301,10,100}-{1205,10,100}-{1203,10,100}-{1207,10,100}-{1217,10,100}-{1209,10,100}-{1211,10,100}" }, { "npcs": "541", @@ -1329,7 +1320,7 @@ "general_store": "false", "id": "150", "title": "Zeke's Superior Scimitars", - "stock": "{1321,10}-{1323,10}-{1325,10}-{1329,10}" + "stock": "{1321,10,100}-{1323,10,100}-{1325,10,100}-{1329,10,100}" }, { "npcs": "692", @@ -1338,7 +1329,7 @@ "general_store": "false", "id": "151", "title": "Authentic Throwing Weapons", - "stock": "{825,1000}-{826,1000}-{827,1000}-{828,300}-{829,100}-{830,100}-{800,900}-{801,800}-{802,700}-{803,600}-{804,500}-{805,400}" + "stock": "{825,1000,100}-{826,1000,100}-{827,1000,100}-{828,300,100}-{829,100,100}-{830,100,100}-{800,900,100}-{801,800,100}-{802,700,100}-{803,600,100}-{804,500,100}-{805,400,100}" }, { "npcs": "797", @@ -1347,7 +1338,7 @@ "general_store": "false", "id": "152", "title": "Happy Heroes Hemporium", - "stock": "{1377,1}-{1434,1}" + "stock": "{1377,1,100}-{1434,1,100}" }, { "npcs": "564", @@ -1356,7 +1347,7 @@ "general_store": "false", "id": "153", "title": "Jukat's Dragon Sword Shop", - "stock": "{1305,2}-{1215,2}" + "stock": "{1305,2,100}-{1215,2,100}" }, { "npcs": "4312", @@ -1365,7 +1356,7 @@ "general_store": "false", "id": "154", "title": "Nardok's Bone Weapons", - "stock": "{5018,10}-{5016,10}-{8872,10}-{8880,10}-{8882,25000}" + "stock": "{5018,10,100}-{5016,10,100}-{8872,10,100}-{8880,10,100}-{8882,25000,100}" }, { "npcs": "2152", @@ -1374,7 +1365,7 @@ "general_store": "false", "id": "155", "title": "Quality Weapons Shop", - "stock": "{1365,10}-{1369,10}-{1285,10}-{1287,10}-{1325,10}-{1297,10}-{1303,10}-{853,10}-{888,100}-{890,100}" + "stock": "{1365,10,100}-{1369,10,100}-{1285,10,100}-{1287,10,100}-{1325,10,100}-{1297,10,100}-{1303,10,100}-{853,10,100}-{888,100,100}-{890,100,100}" }, { "npcs": "1208", @@ -1383,7 +1374,7 @@ "general_store": "false", "id": "156", "title": "Quartermaster's Stores", - "stock": "{1931,30}-{1935,10}-{1735,10}-{590,10}-{2309,10}-{3190,10}-{3192,10}-{3194,10}-{3196,10}-{3198,10}-{3200,10}-{3202,10}-{3204,10}" + "stock": "{1931,30,100}-{1935,10,100}-{1735,10,100}-{590,10,100}-{2309,10,100}-{3190,10,100}-{3192,10,100}-{3194,10,100}-{3196,10,100}-{3198,10,100}-{3200,10,100}-{3202,10,100}-{3204,10,100}" }, { "npcs": "2572,2233", @@ -1392,7 +1383,7 @@ "general_store": "false", "id": "157", "title": "Draynor Seed Market", - "stock": "{5318,20}-{5319,10}-{5324,10}-{5322,0}-{5320,0}-{5323,0}-{5321,0}-{5305,20}-{5306,5}-{5097,20}-{5096,20}-{5307,20}-{5308,10}-{5309,5}-{5310,0}-{5311,0}" + "stock": "{5318,20,100}-{5319,10,100}-{5324,10,100}-{5322,0,100}-{5320,0,100}-{5323,0,100}-{5321,0,100}-{5305,20,100}-{5306,5,100}-{5097,20,100}-{5096,20,100}-{5307,20,100}-{5308,10,100}-{5309,5,100}-{5310,0,100}-{5311,0,100}" }, { "npcs": "3205", @@ -1401,16 +1392,16 @@ "general_store": "false", "id": "158", "title": "Romily Weaklax Pie's", - "stock": "{2323,1}-{2325,1}-{2327,1}-{7178,1}-{7188,1}-{7198,1}" + "stock": "{2323,1,100}-{2325,1,100}-{2327,1,100}-{7178,1,100}-{7188,1,100}-{7198,1,100}" }, { - "npcs": "70,1598,1596,1597,1599,8274,8275,8276,8277,8273,8271,8270,2253", + "npcs": "70,1598,1596,1597,1599,8274,8275,8276,8277,8273,8271,8270,2253,7780", "high_alch": "0", "currency": "995", "general_store": "false", "id": "159", "title": "Slayer Equipment", - "stock": "{4155,50}-{4156,50}-{4158,50}-{4160,50000}-{4161,5000}-{4162,50}-{4164,50}-{4166,50}-{4168,50}-{4170,50}-{4551,50}-{6664,5000}-{6696,3000}-{6720,50}-{7051,50}-{7159,50}-{7421,50}-{7432,50}-{8923,50}-{10952,50}-{13279,5000}-{13278,3000}" + "stock": "{4155,50,100}-{4156,50,100}-{4158,50,100}-{4160,50000,100}-{4161,5000,100}-{4162,50,100}-{4164,50,100}-{4166,50,100}-{4168,50,100}-{4170,50,100}-{4551,50,100}-{6664,5000,100}-{6696,3000,100}-{6720,50,100}-{7051,50,100}-{7159,50,100}-{7421,50,100}-{7432,50,100}-{8923,50,100}-{10952,50,100}-{13279,5000,100}-{13278,3000,100}" }, { "npcs": "2257,2258,2259", @@ -1419,7 +1410,7 @@ "general_store": "false", "id": "160", "title": "Battle Runes", - "stock": "{554,100}-{555,100}-{556,100}-{557,100}-{558,100}-{559,100}-{562,30}-{560,30}" + "stock": "{554,100,100}-{555,100,100}-{556,100,100}-{557,100,100}-{558,100,100}-{559,100,100}-{562,30,100}-{560,30,100}" }, { "npcs": "3162", @@ -1428,7 +1419,7 @@ "general_store": "false", "id": "161", "title": "Smithing Smiths Shop", - "stock": "{1321,10}-{1323,10}-{1325,10}-{1329,10}-{2347,10}" + "stock": "{1321,10,100}-{1323,10,100}-{1325,10,100}-{1329,10,100}-{2347,10,100}" }, { "npcs": "1167", @@ -1437,7 +1428,7 @@ "general_store": "false", "id": "162", "title": "Tamayu's Spear Stall", - "stock": "{3188,10}" + "stock": "{3188,10,100}" }, { "npcs": "2620", @@ -1446,7 +1437,7 @@ "general_store": "false", "id": "163", "title": "Tzhaar-hur-tel's Equipment Store", - "stock": "{6522,500}-{6525,1}-{6523,1}-{6524,1}-{6526,1}-{6527,1}-{6528,1}-{6568,1}" + "stock": "{6522,500,100}-{6525,1,100}-{6523,1,100}-{6524,1,100}-{6526,1,100}-{6527,1,100}-{6528,1,100}-{6568,1,100}" }, { "npcs": "2151", @@ -1455,7 +1446,7 @@ "general_store": "false", "id": "164", "title": "Vigr's Warhammers", - "stock": "{1337,10}-{1335,10}-{1339,10}-{1341,10}-{1343,10}" + "stock": "{1337,10,100}-{1335,10,100}-{1339,10,100}-{1341,10,100}-{1343,10,100}" }, { "npcs": "5486", @@ -1464,7 +1455,7 @@ "general_store": "false", "id": "165", "title": "Weapons galore", - "stock": "{1299,4}-{1343,4}-{1369,4}-{3099,4}-{1315,4}" + "stock": "{1299,4,100}-{1343,4,100}-{1369,4,100}-{3099,4,100}-{1315,4,100}" }, { "npcs": "4250", @@ -1473,16 +1464,16 @@ "general_store": "false", "id": "166", "title": "Construction supplies", - "stock": "{8794,10}-{8790,300}-{4819,300}-{4820,300}-{1539,300}" + "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", "id": "167", "title": "The Toad and Chicken", - "stock": "{1905,12}-{1907,12}-{1913,12}" + "stock": "{1905,12,100}-{1907,12,100}-{1913,12,100}" }, { "npcs": "2270", @@ -1491,7 +1482,7 @@ "general_store": "false", "id": "168", "title": "Martin Thwait's Lost and Found.", - "stock": "{954,50}-{1523,25}-{1755,30}-{946,20}-{5560,25}-{864,15}-{863,10}-{865,5}-{3095,3}-{3096,2}-{3097,1}" + "stock": "{954,50,100}-{1523,25,100}-{1755,30,100}-{946,20,100}-{5560,25,100}-{864,15,100}-{863,10,100}-{865,5,100}-{3095,3,100}-{3096,2,100}-{3097,1,100}" }, { "npcs": "970", @@ -1500,7 +1491,7 @@ "general_store": "false", "id": "169", "title": "Diango's Toy Store", - "stock": "{2526,10}-{2520,10}-{2522,10}-{2524,10}-{4613,10}-{12844,10}" + "stock": "{2526,10,100}-{2520,10,100}-{2522,10,100}-{2524,10,100}-{4613,10,100}-{12844,10,100}" }, { "npcs": "793", @@ -1509,7 +1500,7 @@ "general_store": "false", "id": "170", "title": "The Shrimp and Parrot", - "stock": "{347,5}-{339,5}-{379,3}-{373,2}-{3144,3}" + "stock": "{347,5,100}-{339,5,100}-{379,3,100}-{373,2,100}-{3144,3,100}" }, { "npcs": "747", @@ -1518,7 +1509,7 @@ "general_store": "false", "id": "171", "title": "Oziach's Armour", - "stock": "{1127,2}-{1135,2}" + "stock": "{1127,2,100}-{1135,2,100}" }, { "npcs": "537", @@ -1527,7 +1518,7 @@ "general_store": "false", "id": "172", "title": "Scavvo's Rune Store", - "stock": "{1093,1}-{1079,1}-{1432,1}-{1113,1}-{1303,1}-{1289,1}-{1099,1}-{1065,1}-{1169,2}" + "stock": "{1093,1,100}-{1079,1,100}-{1432,1,100}-{1113,1,100}-{1303,1,100}-{1289,1,100}-{1099,1,100}-{1065,1,100}-{1169,2,100}" }, { "npcs": "536", @@ -1536,7 +1527,7 @@ "general_store": "false", "id": "173", "title": "Valaine's Shop of Champions.", - "stock": "{1021,2}-{1165,1}-{1077,1}-{1123,1}" + "stock": "{1021,2,100}-{1165,1,100}-{1077,1,100}-{1123,1,100}" }, { "npcs": "833", @@ -1545,7 +1536,7 @@ "general_store": "true", "id": "174", "title": "Bebadin Village Bartering", - "stock": "{1823,30}-{1831,30}-{1937,10}-{1921,10}-{1929,10}-{946,10}-{2347,10}" + "stock": "{1823,30,100}-{1831,30,100}-{1937,10,100}-{1921,10,100}-{1929,10,100}-{946,10,100}-{2347,10,100}" }, { "npcs": "566", @@ -1554,7 +1545,7 @@ "general_store": "false", "id": "175", "title": "Irksol", - "stock": "{1641,5}" + "stock": "{1641,5,100}" }, { "npcs": "904", @@ -1563,7 +1554,7 @@ "general_store": "false", "id": "176", "title": "Mage Arena Staffs", - "stock": "{2415,5}-{2416,5}-{2417,5}" + "stock": "{2415,5,100}-{2416,5,100}-{2417,5,100}" }, { "npcs": "", @@ -1572,7 +1563,7 @@ "general_store": "false", "id": "177", "title": "Culinaromancer's Chest", - "stock": "{1973,50}-{1982,50}-{1987,9}-{2283,1}-{1927,50}-{6697,50}-{2313,50}-{1973,50}-{1923,50}-{1931,50}-{1925,50}-{1985,50}-{1955,50}-{1933,50}-{1944,50}-{2130,50}-{2007,50}-{1887,50}-{1935,50}-{1980,50}" + "stock": "{1973,50,100}-{1982,50,100}-{1987,9,100}-{2283,1,100}-{1927,50,100}-{6697,50,100}-{2313,50,100}-{1923,50,100}-{1931,50,100}-{1925,50,100}-{1985,50,100}-{1955,50,100}-{1933,50,100}-{1944,50,100}-{2130,50,100}-{2007,50,100}-{1887,50,100}-{1935,50,100}-{1980,50,100}" }, { "npcs": "", @@ -1581,7 +1572,7 @@ "general_store": "false", "id": "178", "title": "Culinaromancer's Chest", - "stock": "{7453,5}-{7454,5}-{7455,5}-{7456,5}-{7457,5}-{7458,5}-{7459,5}-{7460,5}-{7461,5}-{7462,5}-{7433,5}-{7435,5}-{7437,5}-{7439,5}-{7441,5}-{7443,5}-{7445,5}-{7447,5}-{7449,5}-{7451,5}" + "stock": "{7453,5,100}-{7454,5,100}-{7455,5,100}-{7456,5,100}-{7457,5,100}-{7458,5,100}-{7459,5,100}-{7460,5,100}-{7461,5,100}-{7462,5,100}-{7433,5,100}-{7435,5,100}-{7437,5,100}-{7439,5,100}-{7441,5,100}-{7443,5,100}-{7445,5,100}-{7447,5,100}-{7449,5,100}-{7451,5,100}" }, { "npcs": "2290", @@ -1590,7 +1581,7 @@ "general_store": "false", "id": "179", "title": "Sir Tiffy Cashien", - "stock": "{5574,10}-{5576,10}-{5575,10}-{9672,10}-{9676,10}-{9674,10}-{9678,10}" + "stock": "{5574,10,100}-{5576,10,100}-{5575,10,100}-{9672,10,100}-{9676,10,100}-{9674,10,100}-{9678,10,100}" }, { "npcs": "933", @@ -1599,7 +1590,7 @@ "general_store": "false", "id": "180", "title": "Legends' Guild Shop of Useful Items", - "stock": "{299,100}-{1542,5}-{1590,3}-{1052,1}-{2368,3}" + "stock": "{299,100,100}-{1542,5,100}-{1590,3,100}-{1052,1,100}-{2368,3,100}" }, { "npcs": "4518", @@ -1608,7 +1599,7 @@ "general_store": "false", "id": "181", "title": "MoonClan Fine Clothes", - "stock": "{9068,10}-{9069,10}-{9070,10}-{9071,10}-{9072,10}-{9073,10}-{9074,10}-{1733,50}-{1734,500}" + "stock": "{9068,10,100}-{9069,10,100}-{9070,10,100}-{9071,10,100}-{9072,10,100}-{9073,10,100}-{9074,10,100}-{1733,50,100}-{1734,500,100}" }, { "npcs": "", @@ -1617,7 +1608,7 @@ "general_store": "false", "id": "182", "title": "Delicious Goods", - "stock": "{2436,150000}-{2440,150000}-{2442,150000}-{2444,150000}-{3040,150000}-{2434,150000}-{3024,150000}-{2452,150000}-{6685,150000}-{9739,150000}-{385,150000}-{3144,150000}-{391,150000}-{7060,150000}-{7946,150000}-{379,150000}-{7218,150000}-{2301,150000}" + "stock": "{2436,150000,100}-{2440,150000,100}-{2442,150000,100}-{2444,150000,100}-{3040,150000,100}-{2434,150000,100}-{3024,150000,100}-{2452,150000,100}-{6685,150000,100}-{9739,150000,100}-{385,150000,100}-{3144,150000,100}-{391,150000,100}-{7060,150000,100}-{7946,150000,100}-{379,150000,100}-{7218,150000,100}-{2301,150000,100}" }, { "npcs": "", @@ -1626,7 +1617,7 @@ "general_store": "false", "id": "183", "title": "Armour & Supplies", - "stock": "{5698,150000}-{1305,150000}-{4587,150000}-{1377,150000}-{1434,150000}-{7158,150000}-{3204,150000}-{4151,150000}-{4153,150000}-{6528,150000}-{1333,150000}-{1319,150000}-{10887,150000}-{5574,150000}-{5575,150000}-{5576,150000}-{9672,150000}-{9674,150000}-{9676,150000}-{1067,150000}-{1115,150000}-{1153,150000}-{1079,150000}-{1127,150000}-{1163,150000}-{6107,150000}-{6109,150000}-{6108,150000}-{6524,150000}-{10828,150000}-{3751,150000}-{3753,150000}-{3105,150000}-{7460,150000}" + "stock": "{5698,150000,100}-{1305,150000,100}-{4587,150000,100}-{1377,150000,100}-{1434,150000,100}-{7158,150000,100}-{3204,150000,100}-{4151,150000,100}-{4153,150000,100}-{6528,150000,100}-{1333,150000,100}-{1319,150000,100}-{10887,150000,100}-{5574,150000,100}-{5575,150000,100}-{5576,150000,100}-{9672,150000,100}-{9674,150000,100}-{9676,150000,100}-{1067,150000,100}-{1115,150000,100}-{1153,150000,100}-{1079,150000,100}-{1127,150000,100}-{1163,150000,100}-{6107,150000,100}-{6109,150000,100}-{6108,150000,100}-{6524,150000,100}-{10828,150000,100}-{3751,150000,100}-{3753,150000,100}-{3105,150000,100}-{7460,150000,100}" }, { "npcs": "", @@ -1635,7 +1626,7 @@ "general_store": "false", "id": "184", "title": "Range Supplies", - "stock": "{9241,150000}-{9243,150000}-{9242,150000}-{9244,150000}-{9245,150000}-{9144,150000}-{9305,150000}-{868,150000}-{11230,150000}-{6522,150000}-{892,150000}-{2491,150000}-{2497,150000}-{2503,150000}-{6328,150000}-{3749,150000}-{861,150000}-{9185,150000}-{4214,150000}-{1169,150000}-{1129,150000}-{10034,150000}" + "stock": "{9241,150000,100}-{9243,150000,100}-{9242,150000,100}-{9244,150000,100}-{9245,150000,100}-{9144,150000,100}-{9305,150000,100}-{868,150000,100}-{11230,150000,100}-{6522,150000,100}-{892,150000,100}-{2491,150000,100}-{2497,150000,100}-{2503,150000,100}-{6328,150000,100}-{3749,150000,100}-{861,150000,100}-{9185,150000,100}-{4214,150000,100}-{1169,150000,100}-{1129,150000,100}-{10034,150000,100}" }, { "npcs": "", @@ -1644,7 +1635,7 @@ "general_store": "false", "id": "185", "title": "Magic Emporium", - "stock": "{554,150000}-{555,150000}-{556,150000}-{557,150000}-{558,150000}-{559,150000}-{560,150000}-{561,150000}-{562,150000}-{563,150000}-{564,150000}-{565,150000}-{566,150000}-{9075,150000}-{4089,150000}-{4091,150000}-{4093,150000}-{4095,150000}-{4097,150000}-{4099,150000}-{4101,150000}-{4103,150000}-{4105,150000}-{4107,150000}-{4109,150000}-{4111,150000}-{4113,150000}-{4115,150000}-{4117,150000}-{6912,150000}-{3840,150000}-{3842,150000}-{3844,150000}-{1540,150000}-{2412,150000}-{2413,150000}-{2414,150000}-{4675,150000}-{2415,150000}-{2416,150000}-{2417,150000}-{577,150000}-{579,150000}-{2579,150000}" + "stock": "{554,150000,100}-{555,150000,100}-{556,150000,100}-{557,150000,100}-{558,150000,100}-{559,150000,100}-{560,150000,100}-{561,150000,100}-{562,150000,100}-{563,150000,100}-{564,150000,100}-{565,150000,100}-{566,150000,100}-{9075,150000,100}-{4089,150000,100}-{4091,150000,100}-{4093,150000,100}-{4095,150000,100}-{4097,150000,100}-{4099,150000,100}-{4101,150000,100}-{4103,150000,100}-{4105,150000,100}-{4107,150000,100}-{4109,150000,100}-{4111,150000,100}-{4113,150000,100}-{4115,150000,100}-{4117,150000,100}-{6912,150000,100}-{3840,150000,100}-{3842,150000,100}-{3844,150000,100}-{1540,150000,100}-{2412,150000,100}-{2413,150000,100}-{2414,150000,100}-{4675,150000,100}-{2415,150000,100}-{2416,150000,100}-{2417,150000,100}-{577,150000,100}-{579,150000,100}-{2579,150000,100}" }, { "npcs": "5509", @@ -1652,8 +1643,8 @@ "currency": "995", "general_store": "false", "id": "186", - "title": "Neitiznot supplies.", - "stock": "{946,inf}-{2347,inf}-{1734,inf}-{1733,inf}-{1351,inf}-{1759,inf}" + "title": "Neitiznot Supplies", + "stock": "{946,10,100}-{2347,10,100}-{1734,1000,100}-{1733,10,100}-{1351,10,100}-{1759,1000,100}-{4819,1000,100}" }, { "npcs": "1778", @@ -1662,7 +1653,7 @@ "general_store": "false", "id": "187", "title": "William's Wilderness Cape Shop", - "stock": "{4315,100}-{4335,100}-{4375,100}-{4395,100}" + "stock": "{4315,100,100}-{4335,100,100}-{4355,100,100}-{4375,100,100}-{4395,100,100}" }, { "npcs": "1779", @@ -1671,7 +1662,7 @@ "general_store": "false", "id": "188", "title": "Ian's Wilderness Cape Shop", - "stock": "{4317,100}-{4337,100}-{4357,100}-{4377,100}-{4397,100}" + "stock": "{4317,100,100}-{4337,100,100}-{4357,100,100}-{4377,100,100}-{4397,100,100}" }, { "npcs": "1780", @@ -1680,7 +1671,7 @@ "general_store": "false", "id": "189", "title": "Larry's Wilderness Cape Shop", - "stock": "{4319,100}-{4339,100}-{4359,100}-{4379,100}-{4399,100}" + "stock": "{4319,100,100}-{4339,100,100}-{4359,100,100}-{4379,100,100}-{4399,100,100}" }, { "npcs": "1781", @@ -1689,7 +1680,7 @@ "general_store": "false", "id": "190", "title": "Darren's Wilderness Cape Shop", - "stock": "{4321,100}-{4341,100}-{4361,100}-{4381,100}-{4401,100}" + "stock": "{4321,100,100}-{4341,100,100}-{4361,100,100}-{4381,100,100}-{4401,100,100}" }, { "npcs": "1782", @@ -1698,7 +1689,7 @@ "general_store": "false", "id": "191", "title": "Edward's Wilderness Cape Shop", - "stock": "{4323,100}-{4343,100}-{4363,100}-{4383,100}-{4403,100}" + "stock": "{4323,100,100}-{4343,100,100}-{4363,100,100}-{4383,100,100}-{4403,100,100}" }, { "npcs": "1783", @@ -1707,7 +1698,7 @@ "general_store": "false", "id": "192", "title": "Richard's Wilderness Cape Shop", - "stock": "{4325,100}-{4345,100}-{4365,100}-{4385,100}-{4405,100}" + "stock": "{4325,100,100}-{4345,100,100}-{4365,100,100}-{4385,100,100}-{4405,100,100}" }, { "npcs": "1784", @@ -1716,7 +1707,7 @@ "general_store": "false", "id": "193", "title": "Neil's Wilderness Cape Shop", - "stock": "{4327,100}-{4347,100}-{4367,100}-{4387,100}-{4407,100}" + "stock": "{4327,100,100}-{4347,100,100}-{4367,100,100}-{4387,100,100}-{4407,100,100}" }, { "npcs": "1785", @@ -1725,7 +1716,7 @@ "general_store": "false", "id": "194", "title": "Edmond's Wilderness Cape Shop", - "stock": "{4329,100}-{4349,100}-{4369,100}-{4389,100}-{4409,100}" + "stock": "{4329,100,100}-{4349,100,100}-{4369,100,100}-{4389,100,100}-{4409,100,100}" }, { "npcs": "1786", @@ -1734,7 +1725,7 @@ "general_store": "false", "id": "195", "title": "Simon's Wilderness Cape Shop", - "stock": "{4331,100}-{4351,100}-{4371,100}-{4391,100}-{4411,100}" + "stock": "{4331,100,100}-{4351,100,100}-{4371,100,100}-{4391,100,100}-{4411,100,100}" }, { "npcs": "1787", @@ -1743,16 +1734,7 @@ "general_store": "false", "id": "196", "title": "Sam's Wilderness Cape Shop", - "stock": "{4333,100}-{4353,100}-{4373,100}-{4393,100}-{4413,100}" - }, - { - "npcs": "", - "high_alch": "0", - "currency": "995", - "general_store": "true", - "id": "197", - "title": "Donator Island General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" + "stock": "{4333,100,100}-{4353,100,100}-{4373,100,100}-{4393,100,100}-{4413,100,100}" }, { "npcs": "", @@ -1761,7 +1743,7 @@ "general_store": "false", "id": "198", "title": "Candle Shop", - "stock": "{36,10}-{38,10}" + "stock": "{36,10,100}-{38,10,100}" }, { "npcs": "", @@ -1770,7 +1752,7 @@ "general_store": "false", "id": "200", "title": "The Teleporter's Tabs", - "stock": "{8007,100}-{8008,100}-{8009,100}-{8010,100}-{8011,100}-{8012,100}" + "stock": "{8007,100,100}-{8008,100,100}-{8009,100,100}-{8010,100,100}-{8011,100,100}-{8012,100,100}" }, { "npcs": "", @@ -1779,70 +1761,7 @@ "general_store": "false", "id": "201", "title": "The Fairy's Culinary Goods", - "stock": "{330,10000000}-{6706,10000000}-{380,10000000}-{7219,10000000}-{2290,10000000}-{2302,10000000}-{1892,10000000}-{229,10000000}" - }, - { - "npcs": "", - "high_alch": "0", - "currency": "995", - "general_store": "false", - "id": "202", - "title": "The Wizard's Goods", - "stock": "{554,10000000}-{555,10000000}-{556,10000000}-{557,10000000}-{558,10000000}-{559,10000000}-{562,10000000}-{560,10000000}-{563,10000000}-{14730,10000000}-{14732,10000000}-{14734,10000000}-{14736,10000000}-{14738,10000000}-{14740,10000000}-{11872,10}-{11960,10}-{11962,10}-{579,10}-{577,10}-{1011,10}-{1389,10}-{1381,10}-{1381,10}-{1383,10}-{1385,10}-{1387,10}-{4675,10}-{2890,10}-{9731,10}-{3755,10}-{1727,10}-{656,10}-{658,10}-{660,10}-{662,10}-{664,10}-{3839,10}-{3841,10}-{3843,10}" - }, - { - "npcs": "", - "high_alch": "0", - "currency": "995", - "general_store": "false", - "id": "203", - "title": "2009Scape Protective Goods", - "stock": "{11814,10}-{11816,10}-{11818,10}-{11820,10}-{11822,10}-{11824,10}-{11826,10}-{11828,10}-{11830,10}-{11832,10}-{11834,10}-{11836,10}-{11838,10}-{11840,10}-{3105,10}-{4119,10}-{4121,10}-{4123,10}-{4125,10}-{4127,10}-{4129,10}-{1725,10}-{1729,10}-{3751,10}-{3753,10}-{1149,10}-{542,10}-{544,10}-{5574,10}-{5575,10}-{5576,10}-{6106,10}-{6107,10}-{6108,10}-{6109,10}-{6110,10}-{6111,10}-{4502,10}-{7458,10}" - }, - { - "npcs": "", - "high_alch": "0", - "currency": "995", - "general_store": "false", - "id": "204", - "title": "2009Scape Weapon Store (Bronze - Mithril)", - "stock": "{1205,10}-{1237,10}-{1265,10}-{1277,10}-{1291,10}-{1307,10}-{1321,10}-{1337,10}-{1351,10}-{1375,10}-{1203,10}-{1239,10}-{1267,10}-{1279,10}-{1293,10}-{1309,10}-{1323,10}-{1335,10}-{1349,10}-{1363,10}-{1207,10}-{1241,10}-{1269,10}-{1281,10}-{1295,10}-{1311,10}-{1325,10}-{1339,10}-{1353,10}-{1365,10}-{1209,10}-{1243,10}-{1273,10}-{1285,10}-{1299,10}-{1315,10}-{1329,10}-{1343,10}-{1355,10}-{1369,10}" - }, - { - "npcs": "", - "high_alch": "0", - "currency": "995", - "general_store": "false", - "id": "205", - "title": "2009Scape Weapon Store (Adamant - Dragon)", - "stock": "{1211,10}-{1245,10}-{1271,10}-{1287,10}-{1301,10}-{1317,10}-{1331,10}-{1345,10}-{1357,10}-{1371,10}-{1213,10}-{1247,10}-{1275,10}-{1289,10}-{1303,10}-{1319,10}-{1333,10}-{1347,10}-{1359,10}-{1373,10}-{1215,10}-{4587,10}" - }, - { - "npcs": "8174", - "high_alch": "0", - "currency": "995", - "general_store": "false", - "id": "206", - "title": "2009Scape Tool Store", - "stock": "{1925,10}-{2347,10}-{590,10}-{946,10}-{952,10}-{301,10}-{305,10}-{307,10}-{309,10}-{311,10}-{313,1000}-{1734,10}-{233,10}-{1349,10}-{1353,10}-{1355,10}-{1267,10}-{1269,10}-{1271,10}-{1273,10}-{1734,100}-{1733,100}-{1779,1000}-{526,500}-{532,500}" - }, - { - "npcs": "7967,3039", - "high_alch": "0", - "currency": "995", - "general_store": "true", - "id": "207", - "title": "2009Scape General Store", - "stock": "{1931,30}-{1935,30}-{1735,10}-{1925,10}-{1923,10}-{1887,10}-{590,10}-{1755,10}-{2347,10}-{550,10}-{9003,10}" - }, - { - "npcs": "8139", - "high_alch": "0", - "currency": "995", - "general_store": "false", - "id": "208", - "title": "Hartwin's Longrange Tools", - "stock": "{882,10000000}-{884,10000000}-{886,10000000}-{888,10000000}-{890,10000000}-{877,10000000}-{9140,10000000}-{9141,10000000}-{841,10}-{843,10}-{849,10}-{853,10}-{857,10}-{861,10}-{837,10}-{9177,10}-{9179,10}-{9181,10}-{9183,10}-{11864,10}-{11866,10}-{11868,10}-{11870,10}-{1063,10}-{1095,10}-{1129,10}-{1129,10}-{1131,10}-{1167,10}-{1133,10}-{1097,10}-{6328,10}-{3749,10}-{1478,10}-{8880,10}-{8882,10000000}-{863,10000000}" + "stock": "{330,10000000,100}-{6706,10000000,100}-{380,10000000,100}-{7219,10000000,100}-{2290,10000000,100}-{2302,10000000,100}-{1892,10000000,100}-{229,10000000,100}" }, { "npcs": "1369", @@ -1851,7 +1770,7 @@ "general_store": "false", "id": "209", "title": "Holy Wares", - "stock": "{6746,1}-{732,100}" + "stock": "{6746,1,100}-{732,100,100}" }, { "npcs": "694", @@ -1860,7 +1779,7 @@ "general_store": "false", "id": "210", "title": "Ranging Guild Ticket Exchange", - "stock": "{47,10000000}-{1133,10000000}-{892,10000000}-{1169,10000000}-{1135,10000000}-{829,10000000}" + "stock": "{47,10000000,100}-{1133,10000000,100}-{892,10000000,100}-{1169,10000000,100}-{1135,10000000,100}-{829,10000000,100}" }, { "npcs": "3045", @@ -1869,7 +1788,7 @@ "general_store": "false", "id": "211", "title": "Rok's Choc Box", - "stock": "{6794,30}-{1973,25}" + "stock": "{6794,30,100}-{1973,25,100}" }, { "npcs": "1688", @@ -1878,7 +1797,7 @@ "general_store": "false", "id": "212", "title": "Ak-haranu's Exotic Goods", - "stock": "{4740,100000}" + "stock": "{4740,100000,100}" }, { "npcs": "4251", @@ -1887,7 +1806,7 @@ "general_store": "false", "id": "213", "title": "The Garden Centre", - "stock": "{8417,10}-{8419,10}-{8421,10}-{8423,10}-{8425,10}-{8427,10}-{8429,10}-{8431,1000}-{8433,10}-{8435,10}-{8451,10}-{8453,10}-{8455,10}-{8457,10}-{8459,10}-{8461,10}-{8437,10}-{8439,10}-{8441,10}-{8443,10}-{8445,10}-{8447,10}-{8449,10}" + "stock": "{8417,10,100}-{8419,10,100}-{8421,10,100}-{8423,10,100}-{8425,10,100}-{8427,10,100}-{8429,10,100}-{8431,1000,100}-{8433,10,100}-{8435,10,100}-{8451,10,100}-{8453,10,100}-{8455,10,100}-{8457,10,100}-{8459,10,100}-{8461,10,100}-{8437,10,100}-{8439,10,100}-{8441,10,100}-{8443,10,100}-{8445,10,100}-{8447,10,100}-{8449,10,100}" }, { "npcs": "1972", @@ -1896,7 +1815,7 @@ "general_store": "false", "id": "214", "title": "Rasolo's Goods", - "stock": "{1969,2}-{2023,4}-{740,10}-{1215,1}-{550,1}-{583,2}-{1941,10}-{970,1}-{975,1}-{1599,1}-{2976,1}-{1823,1}-{1837,1}-{1864,5}-{2520,1}-{3377,1}-{626,1}-{1909,1}-{3787,1}-{3711,1}-{3678,1}-{3424,5}-{3420,1}-{5,1}" + "stock": "{1969,2,100}-{2023,4,100}-{740,10,100}-{1215,1,100}-{550,1,100}-{583,2,100}-{1941,10,100}-{970,1,100}-{975,1,100}-{1599,1,100}-{2976,1,100}-{1823,1,100}-{1837,1,100}-{1864,5,100}-{2520,1,100}-{3377,1,100}-{626,1,100}-{1909,1,100}-{3787,1,100}-{3711,1,100}-{3678,1,100}-{3424,5,100}-{3420,1,100}-{5,1,100}" }, { "npcs": "592", @@ -1905,7 +1824,7 @@ "general_store": "false", "id": "215", "title": "Fishing Guild Shop.", - "stock": "{313,1000}-{314,1000}-{341,0}-{353,0}-{363,0}-{359,0}-{377,0}-{371,0}-{339,0}-{355,0}-{365,0}-{361,0}-{379,0}-{373,0}" + "stock": "{313,1000,100}-{314,1000,100}-{341,0,100}-{353,0,100}-{363,0,100}-{359,0,100}-{377,0,100}-{371,0,100}-{339,0,100}-{355,0,100}-{365,0,100}-{361,0,100}-{379,0,100}-{373,0,100}" }, { "npcs": "5483", @@ -1914,7 +1833,7 @@ "general_store": "false", "id": "216", "title": "Ore Store", - "stock": "{436,20}-{438,10}-{440,10}-{442,5}-{453,10}-{444,5}-{447,0}-{449,0}" + "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", @@ -1923,7 +1842,7 @@ "general_store": "false", "id": "217", "title": "Tony's Pizza Bases", - "stock": "{2283,10}" + "stock": "{2283,10,100}" }, { "npcs": "2159", @@ -1932,7 +1851,7 @@ "general_store": "false", "id": "218", "title": "Silver Cog Silver Stall", - "stock": "{1714,2}-{442,1}-{2355,1}" + "stock": "{1714,2,100}-{442,1,100}-{2355,1,100}" }, { "npcs": "2158", @@ -1941,7 +1860,7 @@ "general_store": "false", "id": "219", "title": "Carefree Crafting Stall", - "stock": "{1755,2}-{1592,4}-{1597,2}-{1733,3}-{1734,100}-{1759,100}" + "stock": "{1755,2,100}-{1592,4,100}-{1597,2,100}-{1733,3,100}-{1734,100,100}-{1759,100,100}" }, { "npcs": "2156", @@ -1950,7 +1869,7 @@ "general_store": "false", "id": "220", "title": "Keldagrim's Best Bread", - "stock": "{2309,10}-{1891,3}-{1901,8}" + "stock": "{2309,10,100}-{1891,3,100}-{1901,8,100}" }, { "npcs": "2153", @@ -1959,16 +1878,17 @@ "general_store": "false", "id": "221", "title": "Quality Armour Shop", - "stock": "{1105,3}-{1109,1}-{1107,1}-{1111,1}-{1141,3}-{1143,1}-{1145,1}-{1177,0}-{1195,0}" + "stock": "{1105,3,100}-{1109,1,100}-{1107,1,100}-{1111,1,100}-{1141,3,100}-{1143,1,100}-{1145,1,100}-{1177,0,100}-{1195,0,100}" }, { "npcs": "2564", + "force_shared": "true", "high_alch": "0", "currency": "995", "general_store": "false", "id": "222", "title": "Ore Shop\b", - "stock": "{436,1500}-{438,1500}-{440,1500}-{442,1500}-{444,1500}-{447,1500}-{453,1500}" + "stock": "{436,0,0}-{438,0,0}-{440,0,0}-{442,0,0}-{444,0,0}-{447,0,0}-{453,0,0}" }, { "npcs": "603", @@ -1977,7 +1897,7 @@ "general_store": "false", "id": "223", "title": "Funch's Fine Groceries", - "stock": "{2021,10}-{2019,10}-{2015,10}-{2017,10}-{2114,10}-{2128,20}-{2108,20}-{2102,20}-{2126,5}-{2025,10}-{1973,20}-{1975,10}-{2130,5}-{1927,5}-{946,5}-{2026,20}" + "stock": "{2021,10,100}-{2019,10,100}-{2015,10,100}-{2017,10,100}-{2114,10,100}-{2128,20,100}-{2108,20,100}-{2102,20,100}-{2126,5,100}-{2025,10,100}-{1973,20,100}-{1975,10,100}-{2130,5,100}-{1927,5,100}-{946,5,100}-{2026,20,100}" }, { "npcs": "578", @@ -1986,7 +1906,7 @@ "general_store": "false", "id": "224", "title": "Frincos' Fabulous Herb Store.", - "stock": "{229,10}-{233,10}-{221,10}" + "stock": "{229,10,100}-{233,10,100}-{221,10,100}" }, { "npcs": "555", @@ -1995,7 +1915,7 @@ "general_store": "true", "id": "225", "title": "Port Khazard General Store", - "stock": "{1941,inf}-{954,inf}-{583,inf}-{1931,inf}-{1935,inf}-{1735,inf}-{1925,inf}-{1923,inf}-{1887,inf}-{590,inf}-{1755,inf}-{2347,inf}-{550,inf}-{9003,inf}" + "stock": "{1941,200,10}-{954,10,10}-{583,10,100}-{1931,30,100}-{1935,10,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": "", @@ -2004,7 +1924,7 @@ "general_store": "false", "id": "226", "title": "Gabooty's Tai Bwo Wannai Cooperative", - "stock": "{6341,10}-{6343,10}-{6345,10}-{6347,10}-{6349,10}-{6313,10}-{6315,10}-{6317,10}-{6351,10}-{6353,10}-{6355,10}-{6357,10}-{6359,10}-{1625,0}-{1627,0}-{1629,0}-{6361,10}-{6363,10}-{6365,10}-{6367,10}-{6369,10}-{1609,0}-{1611,0}-{1613,0}-{6371,10}-{6373,10}-{6375,10}-{6377,10}-{6379,10}-{6311,0}" + "stock": "{6341,10,100}-{6343,10,100}-{6345,10,100}-{6347,10,100}-{6349,10,100}-{6313,10,100}-{6315,10,100}-{6317,10,100}-{6351,10,100}-{6353,10,100}-{6355,10,100}-{6357,10,100}-{6359,10,100}-{1625,0,100}-{1627,0,100}-{1629,0,100}-{6361,10,100}-{6363,10,100}-{6365,10,100}-{6367,10,100}-{6369,10,100}-{1609,0,100}-{1611,0,100}-{1613,0,100}-{6371,10,100}-{6373,10,100}-{6375,10,100}-{6377,10,100}-{6379,10,100}-{6311,0,100}" }, { "npcs": "2161", @@ -2013,7 +1933,7 @@ "general_store": "false", "id": "227", "title": "Agmundi Quality Clothes", - "stock": "{5050,3}-{5052,3}-{5038,3}-{5040,3}-{5044,3}-{5046,3}-{5026,3}-{5028,3}-{5032,3}-{5034,3}" + "stock": "{5050,3,100}-{5052,3,100}-{5038,3,100}-{5040,3,100}-{5044,3,100}-{5046,3,100}-{5026,3,100}-{5028,3,100}-{5032,3,100}-{5034,3,100}" }, { "npcs": "517", @@ -2022,7 +1942,7 @@ "general_store": "false", "id": "228", "title": "Fernahei's Fishing Hut", - "stock": "{307,10}-{309,10}-{313,1000}-{314,1000}-{335,0}-{349,0}-{331,0}" + "stock": "{307,10,100}-{309,10,100}-{313,1000,100}-{314,1000,100}-{335,0,100}-{349,0,100}-{331,0,100}" }, { "npcs": "2357", @@ -2031,7 +1951,7 @@ "general_store": "false", "id": "229", "title": "Lletya Food Store", - "stock": "{2309,10}-{379,15}-{1993,3}-{1985,10}-{1891,5}" + "stock": "{2309,10,100}-{379,10,100}-{1993,10,100}-{1985,10,100}-{1891,10,100}" }, { "npcs": "2356", @@ -2040,16 +1960,7 @@ "general_store": "false", "id": "230", "title": "Lletya Archery Shop", - "stock": "{884,2000}-{886,500}-{888,500}-{890,450}-{892,400}-{877,1500}-{843,5}-{845,5}-{837,5}-{849,5}-{847,5}" - }, - { - "npcs": "2352", - "high_alch": "0", - "currency": "995", - "general_store": "true", - "id": "231", - "title": "Lletya General Store", - "stock": "" + "stock": "{884,2000,100}-{886,500,100}-{888,500,100}-{890,450,100}-{892,400,100}-{877,1500,100}-{843,5,100}-{845,5,100}-{837,5,100}-{849,5,100}-{847,5,100}" }, { "npcs": "1282", @@ -2058,7 +1969,7 @@ "general_store": "false", "id": "232", "title": "Sigmund the Merchant", - "stock": "{590,2}-{954,10}-{233,3}-{1931,3}-{2142,2}-{2309,10}-{952,10}-{36,10}-{1755,10}-{2347,10}-{229,10}-{227,10}-{1925,10}-{1929,10}-{1944,10}-{1942,10}-{1965,10}" + "stock": "{590,10,100}-{954,10,100}-{1931,30,100}-{2142,10,100}-{2309,10,100}-{952,10,100}-{36,10,100}-{1755,10,100}-{229,310,100}-{227,310,100}-{1925,30,100}-{1944,10,100}-{1942,10,100}-{233,10,100}-{2347,10,100}-{1929,10,100}" }, { "npcs": "1315", @@ -2067,7 +1978,7 @@ "general_store": "false", "id": "233", "title": "Fremennik Fishmonger", - "stock": "{303,5}-{307,5}-{309,5}-{311,2}-{301,2}-{313,1000}-{314,1000}-{305,5}-{317,10}-{327,10}-{345,0}-{353,0}-{341,0}-{321,0}-{335,0}-{331,0}-{359,0}-{377,0}-{363,0}-{371,0}-{383,0}" + "stock": "{303,5,100}-{307,5,100}-{309,5,100}-{311,2,100}-{301,2,100}-{313,1000,100}-{314,1000,100}-{305,5,100}-{317,10,100}-{327,10,100}-{345,0,100}-{353,0,100}-{341,0,100}-{321,0,100}-{335,0,100}-{331,0,100}-{359,0,100}-{377,0,100}-{363,0,100}-{371,0,100}-{383,0,100}" }, { "npcs": "1300", @@ -2076,7 +1987,7 @@ "general_store": "false", "id": "234", "title": "Rellekka Longhall Bar", - "stock": "{1917,10}-{3803,10}-{3711,10}" + "stock": "{1917,10,100}-{3803,10,100}-{3711,10,100}" }, { "npcs": "7420", @@ -2085,7 +1996,7 @@ "general_store": "false", "id": "235", "title": "Gnomic Supplies", - "stock": "{1931,inf}-{1929,inf}-{1937,inf}-{2313,inf}-{1887,inf}-{590,inf}-{1735,inf}-{1951,inf}-{1969,inf}-{1949,inf}" + "stock": "{1931,100,100}-{1929,100,100}-{1937,100,100}-{2313,100,100}-{1887,100,100}-{590,100,100}-{1735,100,100}-{1951,100,100}-{1969,100,100}-{1949,100,100}" }, { "npcs": "4248", @@ -2094,7 +2005,7 @@ "general_store": "false", "id": "236", "title": "Keldagrim Stonemason", - "stock": "{3420,500}-{8786,30}-{8784,30}-{8788,30}" + "stock": "{3420,500,100}-{8786,30,100}-{8784,30,100}-{8788,30,100}" }, { "npcs": "4856", @@ -2103,7 +2014,7 @@ "general_store": "false", "id": "237", "title": "Lovecraft's Tackle", - "stock": "{303,10}-{307,10}-{309,10}-{311,1000}-{301,10}-{313,1000}-{314,1000}-{317,0}-{327,10}-{345,0}-{321,0}-{335,0}-{349,0}-{331,0}-{359,0}-{377,0}-{371,0}" + "stock": "{303,10,100}-{307,10,100}-{309,10,100}-{311,1000,100}-{301,10,100}-{313,1000,100}-{314,1000,100}-{317,0,100}-{327,10,100}-{345,0,100}-{321,0,100}-{335,0,100}-{349,0,100}-{331,0,100}-{359,0,100}-{377,0,100}-{371,0,100}" }, { "npcs": "5484", @@ -2112,7 +2023,7 @@ "general_store": "false", "id": "238", "title": "Flosi's Fishmongers", - "stock": "{377,5}-{359,20}-{331,20}-{341,20}-{383,0}" + "stock": "{377,5,100}-{359,20,100}-{331,20,100}-{341,20,100}-{383,0,100}" }, { "npcs": "5495", @@ -2121,7 +2032,7 @@ "general_store": "false", "id": "239", "title": "Contraband yak produce.", - "stock": "{10818,25}-{10816,50}-{10814,50}-{10820,10}" + "stock": "{10818,25,100}-{10816,50,100}-{10814,50,100}-{10820,10,100}" }, { "npcs": "1163,1164", @@ -2130,7 +2041,7 @@ "general_store": "false", "id": "240", "title": "Tiadeche's Karambwan Stall", - "stock": "{3142,10}-{3157,10}" + "stock": "{3142,10,100}-{3157,10,100}" }, { "npcs": "", @@ -2139,7 +2050,7 @@ "general_store": "false", "id": "241", "title": "Wishing Well", - "stock": "{12204,10}-{12207,10}-{12210,0}-{12213,0}-{12216,0}-{12219,0}-{12222,0}-{12183,65000}-{12155,5000}" + "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": "4965", @@ -2148,7 +2059,7 @@ "general_store": "false", "id": "242", "title": "Leprechaun Larry's Farming Supplies.", - "stock": "{5341,10}-{5343,10}-{5329,10}-{952,10}-{5325,10}-{5331,10}-{1925,10}-{6036,10}-{2026,10}-{1480,10}" + "stock": "{5341,10,100}-{5343,10,100}-{5329,10,100}-{952,10,100}-{5325,10,100}-{5331,10,100}-{1925,10,100}-{6036,10,100}-{2026,10,100}-{1480,10,100}" }, { "npcs": "849", @@ -2157,6 +2068,132 @@ "general_store": "false", "id": "243", "title": "Blurberry Bar", - "stock": "{2028,10}-{2030,10}-{2032,10}-{2034,10}-{2036,10}-{2038,10}-{2040,10}" + "stock": "{2028,10,100}-{2030,10,100}-{2032,10,100}-{2034,10,100}-{2036,10,100}-{2038,10,100}-{2040,10,100}" + }, + { + "npcs": "2565", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "244", + "title": "The Armour Store", + "stock": "{1147,0,100}-{1163,0,100}-{1127,0,100}-{1093,0,100}-{1185,0,100}-{1201,0,100}-{1113,0,100}-{1079,0,100}-{1145,0,100}-{1161,0,100}-{1123,0,100}-{1091,0,100}-{1183,0,100}-{1199,0,100}-{1111,0,100}-{1073,0,100}-{1143,0,100}-{1159,0,100}-{1121,0,100}-{1085,0,100}-{1181,0,100}-{1197,0,100}-{1109,0,100}-{1071,0,100}" + }, + { + "npcs": "7048", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "245", + "title": "Gift Shop", + "stock": "{590,10,100}-{2866,100,100}-{2878,10,100}-{314,1000,100}-{1351,10,100}-{954,10,100}-{1931,30,100}-{1925,30,100}-{2347,10,100}-{946,10,100}-{12561,10,100}-{12563,10,100}-{12565,0,100}-{12567,0,100}-{12568,0,100}-{12559,0,100}-{12570,0,100}" + }, + { + "npcs": "3923", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "246", + "title": "Miscellanian Food Shop", + "stock": "{2309,5,100}-{1985,5,100}-{1965,5,100}-{1942,5,100}-{1957,5,100}-{1933,5,100}-{1973,5,100}-{1927,5,100}" + }, + { + "npcs": "3920", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "247", + "title": "The Esoterican Arms", + "stock": "{1917,10,100}-{5763,10,100}-{1993,5,100}-{1798,5,100}" + }, + { + "npcs": "1383,3921", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "248", + "title": "Miscellanian Clothes Shop", + "stock": "{3767,5,100}-{3769,5,100}-{3771,5,100}-{3773,5,100}-{3775,5,100}-{3795,5,100}-{5050,3,100}-{5052,3,100}-{5038,3,100}-{5040,3,100}-{5044,3,100}-{5046,3,100}-{5026,3,100}-{5028,3,100}-{5032,3,100}-{5034,3,100}" + }, + { + "npcs": "1393", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "249", + "title": "Island Fishmonger", + "stock": "{303,5,100}-{307,5,100}-{309,5,100}-{311,2,100}-{301,2,100}-{313,1500,100}-{314,1000,100}-{305,5,100}-{317,0,100}-{325,200,100}-{345,0,100}-{353,0,100}-{341,0,100}-{321,0,100}-{335,0,100}-{349,0,100}-{331,0,100}-{359,0,100}-{377,0,100}-{363,0,100}-{371,0,100}-{383,0,100}" + }, + { + "npcs": "1394", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "250", + "title": "Greengrocer of Miscellania", + "stock": "{1965,10,100}-{1942,10,100}-{1957,10,100}-{1982,10,100}-{1550,2,100}" + }, + { + "npcs": "932", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "251", + "title": "Legends Guild General Store", + "stock": "{373,20,100}-{2323,5,100}-{121,3,100}-{886,500,100}" + }, + { + "npcs": "3161", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "252", + "title": "Two Feet Charley's Fish Shop", + "stock": "{317,10,100}-{327,10,100}-{345,10,100}-{353,10,100}-{341,10,100}-{321,10,100}-{359,0,100}-{377,0,100}-{363,0,100}" + }, + { + "npcs": "1316", + "high_alch": "0", + "currency": "995", + "general_store": "false", + "id": "253", + "title": "Fremennik Fur Trader", + "stock": "{948,10,100}-{958,10,100}-{10117,0,100}-{10121,0,100}-{10119,0,100}-{10123,0,100}-{10093,0,100}-{10095,0,100}-{10097,0,100}-{10099,0,100}-{10101,0,100}-{10103,0,100}" + }, + { + "npcs": "", + "high_alch": "0", + "currency": "6306", + "general_store": "false", + "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/varbit_definitions.json b/Server/data/configs/varbit_definitions.json new file mode 100644 index 000000000..720b1676e --- /dev/null +++ b/Server/data/configs/varbit_definitions.json @@ -0,0 +1,19 @@ +[ + { + "description": "Bank insert mode", + + "varpId": "304", + "varbitId": "7000", + "startBit": "0", + "endBit": "0" + }, + + { + "description": "Bank note mode", + + "varpId": "115", + "varbitId": "7001", + "startBit": "0", + "endBit": "0" + } +] \ 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 3a706c489..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/ @@ -17,70 +21,6 @@ SET time_zone = "+00:00"; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8mb4 */; --- --- Database: `global` --- - --- -------------------------------------------------------- - --- --- Table structure for table `dev_log` --- - -CREATE TABLE `dev_log` ( - `id` int(11) UNSIGNED NOT NULL, - `username` varchar(15) DEFAULT NULL, - `content` longtext NOT NULL, - `date` timestamp NULL DEFAULT '0000-00-00 00:00:00' -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Table structure for table `highscores` --- - -CREATE TABLE `highscores` ( - `id` int(11) UNSIGNED NOT NULL, - `username` varchar(20) DEFAULT NULL, - `overall_xp` int(11) NOT NULL DEFAULT 0, - `total_level` int(11) NOT NULL DEFAULT 0, - `ironManMode` varchar(15) NOT NULL DEFAULT 'NONE', - `xp_0` int(11) NOT NULL DEFAULT 0, - `xp_1` int(11) NOT NULL DEFAULT 0, - `xp_2` int(11) NOT NULL DEFAULT 0, - `xp_3` int(11) NOT NULL DEFAULT 0, - `xp_4` int(11) NOT NULL DEFAULT 0, - `xp_5` int(11) NOT NULL DEFAULT 0, - `xp_6` int(11) NOT NULL DEFAULT 0, - `xp_7` int(11) NOT NULL DEFAULT 0, - `xp_8` int(11) NOT NULL DEFAULT 0, - `xp_9` int(11) NOT NULL DEFAULT 0, - `xp_10` int(11) NOT NULL DEFAULT 0, - `xp_11` int(11) NOT NULL DEFAULT 0, - `xp_12` int(11) NOT NULL DEFAULT 0, - `xp_13` int(11) NOT NULL DEFAULT 0, - `xp_14` int(11) NOT NULL DEFAULT 0, - `xp_15` int(11) NOT NULL DEFAULT 0, - `xp_16` int(11) NOT NULL DEFAULT 0, - `xp_17` int(11) NOT NULL DEFAULT 0, - `xp_18` int(11) NOT NULL DEFAULT 0, - `xp_19` int(11) NOT NULL DEFAULT 0, - `xp_20` int(11) NOT NULL DEFAULT 0, - `xp_21` int(11) NOT NULL DEFAULT 0, - `xp_22` int(11) NOT NULL DEFAULT 0, - `xp_23` int(11) NOT NULL DEFAULT 0 -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- --- Dumping data for table `highscores` --- - -INSERT INTO `highscores` (`id`, `username`, `overall_xp`, `total_level`, `ironManMode`, `xp_0`, `xp_1`, `xp_2`, `xp_3`, `xp_4`, `xp_5`, `xp_6`, `xp_7`, `xp_8`, `xp_9`, `xp_10`, `xp_11`, `xp_12`, `xp_13`, `xp_14`, `xp_15`, `xp_16`, `xp_17`, `xp_18`, `xp_19`, `xp_20`, `xp_21`, `xp_22`, `xp_23`) VALUES -(9, 'woah', 1154, 33, 'NONE', 0, 0, 0, 1154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), -(10, 'test', 1154, 33, 'NONE', 0, 0, 0, 1154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), -(11, 'woahscam', 1154, 33, 'NONE', 0, 0, 0, 1154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - -- -------------------------------------------------------- -- @@ -135,194 +75,6 @@ CREATE TABLE `members` ( `posts` int(11) NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=latin1; --- --- Dumping data for table `members` --- - -INSERT INTO `members` (`UID`, `email`, `username`, `password`, `salt`, `rights`, `email_activated`, `lastActive`, `donatorType`, `donationTotal`, `credits`, `icon`, `perks`, `ip`, `mac`, `serial`, `computerName`, `monthlyVotes`, `netWorth`, `forumUID`, `ironManMode`, `bank`, `inventory`, `equipment`, `ge`, `muteTime`, `banTime`, `profileImage`, `contacts`, `blocked`, `clanName`, `currentClan`, `clanReqs`, `disconnectTime`, `lastWorld`, `chatSettings`, `timePlayed`, `lastLogin`, `lastGameIp`, `countryCode`, `birthday`, `online`, `signature`, `joined_date`, `posts`) VALUES -(0, '', '2009Scape', '$2a$12$P0OU2A5S.lEYdkTq5kq3/u1UlfVkMYIS7WWbxsjjeyfxqTDbygpEe', '$2a$12$P0OU2A5S.lEYdkTq5kq3/u', 2, 0, '2019-11-06 00:29:03', 0, 0.00, 0, 0, '', '127.0.0.1', NULL, NULL, 'SERVER', 0, 0, -1, 'STANDARD', NULL, NULL, NULL, NULL, -1, -1, '', '', '', '2009Scape', '2009Scape', '0,0,8,9', 1572999889084, 1, '0,0,0', NULL, 1572999890885, '127.0.0.1', 0, NULL, 0, NULL, NULL, 0); - --- -------------------------------------------------------- - --- --- Table structure for table `messages` --- - -CREATE TABLE `messages` ( - `id` int(11) UNSIGNED NOT NULL, - `sender` varchar(15) NOT NULL DEFAULT '', - `recipient` varchar(15) NOT NULL DEFAULT '', - `subject` varchar(40) NOT NULL DEFAULT '', - `content` longtext NOT NULL, - `date` timestamp NOT NULL DEFAULT current_timestamp(), - `is_read` tinyint(1) NOT NULL, - `s_delete` tinyint(11) NOT NULL, - `r_delete` tinyint(1) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Table structure for table `perks` --- - -CREATE TABLE `perks` ( - `product_id` int(10) UNSIGNED NOT NULL, - `name` varchar(100) NOT NULL DEFAULT 'No Name', - `description` varchar(500) DEFAULT NULL, - `price` int(10) NOT NULL DEFAULT 0 -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- --- Dumping data for table `perks` --- - -INSERT INTO `perks` (`product_id`, `name`, `description`, `price`) VALUES -(2, 'Stamina Boost', 'Increase your stamina and run regeneration by a total of 40%.', 60), -(4, 'Green Thumb', 'With a 35% lifetime increase in your crops growing up healthy, they will be saved and you will also receive a better crop yield!', 100), -(5, 'Bird Man', 'Increase your rate of receiving a bird nest drop by 35%!', 50), -(6, 'Stoner', 'Increase your rate of receiving a gem stone drop by 35%!', 50), -(11, 'Unbreakable Forge', 'Ring of forging never breaks.', 30), -(12, 'Out of Grave Danger', 'Your gravestone will last up to twice as long with this perk.', 50), -(13, 'Sleight of Hand', 'With sleight of hand you will increase your success rates in all aspects of thieving. This includes pickpocketing and cracking wall safes for gems.', 50), -(14, 'Master Chef', 'As a soon to be Master Chef, you will receive a 20% increase in successfully cooking your food!', 50), -(16, 'Divine Intervention', 'The gods above intervene with your burying of bones. You have a 10% chance while burying a bone to keep it instead.', 70), -(17, 'Familiar Whisperer', 'Get to know your familiar better by increasing their lifespan by 50%.', 70), -(18, 'Barrows Befriender', 'Befriend the barrows brothers & never experience the wretched degrading of their armour again', 300), -(19, 'Abyss Befriender', 'Use the power of the abyss to make your Runecrafting pouches undegradable.', 150), -(21, 'Charge Befriender', 'The God\'s of the Hero\'s guild have blessed you with the power to use your jewerly free of charge.', 250), -(22, 'Golden Needle', 'Gain an extra 10% experience whilst spinning something on a spinning wheel, including flax. Creating an item made out of dragonhide rewards an extra 5% experience. Your crafting needle also never breaks and thread is consumed less often.\n', 50), -(24, 'Slayer Betrayer', 'Obtain the ability through the Slayer Masters to change your slayer task at will. Type ::cleartask to use.', 100), -(26, 'Thirst Quencher', 'The gods have blessed you with the knowledge of the deserts to gain the skills required to tap into an unlimited water supply.', 30), -(27, 'Double Trouble', 'Experience a chance of receiving double the resources through skills such as, mining, woodcutting, fishing, and many more.', 250), -(29, 'Godwars Befriender', 'Now blessed by the gods you have the ability to enter the chambers with a killcount of 30. You will also be granted half the time it takes to recharge at an altar.', 100), -(30, 'Prayer Betrayer', 'Experience half the prayer drain rate when this perk is enabled.', 150), -(31, 'Spell Swap', 'The ability to swap spell books without any charge of runes. Cannot be used in combat or in the wilderness.', 80), -(32, 'Dwarf Befriender', 'Befriended by the dwarfs you now have the ability to use double the cannon balls and experience no decay on your cannon.', 150), -(33, 'Powerpoint', 'This perk grants you double the points in all minigames.', 300), -(35, 'Charm Collector', 'Through the power of summoning you will automatically pick up any charms dropped in battle.', 100), -(36, 'Detective', 'You now have a solid 10% chance of a clue scroll drop from any monster that drops clues as well as a 50% better chance of super rare rewards such as 3rd age. You\'ll also experience a 50% increased chance to obtain more loot.', 250), -(40, 'Overcharge', 'The power from the overcharge lords is given to you. Your Dragonfire Shield will recharge fully every 10 minutes. The time between casts is also reduced by 50%.', 170), -(41, 'Unbreakable Crystal', 'This perk allows for your crystal bow to never degrade.', 350), -(42, 'Crusader', 'With this perk you will have a 25% chance to double loot the barrows chest.', 100), -(43, 'Pet Befriender', 'This perk gives you the ability to double your chances on getting boss/skilling pets!', 100), -(60, 'Bone Crusher', 'Automatically crushes your bones as they\'re dropped for prayer experience. Toggle this perk using ::bonecrusher', 100), -(70, 'Runestone Knowledge', 'You are given extended knowledge of the runecrafting skill and can now craft double death, law, cosmic, blood and nature runes.', 200), -(71, 'Coin machine', 'Automatically bank all coins dropped from NPC\'s and gives you 25% extra gold. Toggle this perk using ::coinmachine', 150), -(72, 'Fight Cave Fanatic', 'Eliminates the first 25 waves from the tzhaar fight caves.', 50), -(73, 'Decanter', 'Zahur will decant your noted potions if you have this perk.', 50); - --- -------------------------------------------------------- - --- --- Table structure for table `player_logs` --- - -CREATE TABLE `player_logs` ( - `username` varchar(22) NOT NULL DEFAULT '', - `public_chat` longtext DEFAULT NULL, - `private_chat` longtext DEFAULT NULL, - `clan_chat` longtext DEFAULT NULL, - `address_log` longtext DEFAULT NULL, - `command_log` longtext DEFAULT NULL, - `trade_log` longtext DEFAULT NULL, - `ge_log` longtext DEFAULT NULL, - `duplication_log` longtext DEFAULT NULL, - `duel_log` longtext DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- --- Dumping data for table `player_logs` --- - -INSERT INTO `player_logs` (`username`, `public_chat`, `private_chat`, `clan_chat`, `address_log`, `command_log`, `trade_log`, `ge_log`, `duplication_log`, `duel_log`) VALUES -('test', '', '', '', '10/09/2020 14:44:20: 127.0.0.1\n10/09/2020 14:44:20: I116264190\n10/09/2020 14:44:20: 30-9C-23-87-89-8E\n10/09/2020 14:47:49: 127.0.0.1\n10/09/2020 14:47:49: I116264190\n10/09/2020 14:47:49: 30-9C-23-87-89-8E\n10/09/2020 16:26:44: 127.0.0.1\n10/09/2020 16:26:44: I116264190\n10/09/2020 16:26:44: 30-9C-23-87-89-8E\n', '', '', '', '', ''), -('woah', '10/09/2020 14:35:41: a\\\n10/09/2020 14:47:59: a\n10/09/2020 16:26:56: ok\n', '', '', '10/09/2020 14:34:50: 127.0.0.1\n10/09/2020 14:34:50: I116264190\n10/09/2020 14:34:50: 30-9C-23-87-89-8E\n10/09/2020 14:37:33: 127.0.0.1\n10/09/2020 14:37:33: I116264190\n10/09/2020 14:37:33: 30-9C-23-87-89-8E\n10/09/2020 14:39:09: 127.0.0.1\n10/09/2020 14:39:09: I116264190\n10/09/2020 14:39:09: 30-9C-23-87-89-8E\n10/09/2020 14:41:11: 127.0.0.1\n10/09/2020 14:41:11: I116264190\n10/09/2020 14:41:11: 30-9C-23-87-89-8E\n10/09/2020 14:47:23: 127.0.0.1\n10/09/2020 14:47:23: I116264190\n10/09/2020 14:47:23: 30-9C-23-87-89-8E\n10/09/2020 16:26:23: 127.0.0.1\n10/09/2020 16:26:23: I116264190\n10/09/2020 16:26:23: 30-9C-23-87-89-8E\n10/09/2020 16:35:53: 127.0.0.1\n10/09/2020 16:35:53: I116264190\n10/09/2020 16:35:53: 30-9C-23-87-89-8E\n10/09/2020 17:43:03: 127.0.0.1\n10/09/2020 17:43:03: I116264190\n10/09/2020 17:43:03: 30-9C-23-87-89-8E\n10/09/2020 18:16:22: 127.0.0.1\n10/09/2020 18:16:22: I116264190\n10/09/2020 18:16:22: 30-9C-23-87-89-8E\n10/09/2020 18:24:25: 127.0.0.1\n10/09/2020 18:24:25: I116264190\n10/09/2020 18:24:25: 30-9C-23-87-89-8E\n10/09/2020 18:25:02: 127.0.0.1\n10/09/2020 18:25:02: I116264190\n10/09/2020 18:25:02: 30-9C-23-87-89-8E\n10/09/2020 18:27:39: 127.0.0.1\n10/09/2020 18:27:39: I116264190\n10/09/2020 18:27:39: 30-9C-23-87-89-8E\n10/09/2020 18:28:28: 127.0.0.1\n10/09/2020 18:28:28: I116264190\n10/09/2020 18:28:28: 30-9C-23-87-89-8E\n10/09/2020 18:31:56: 127.0.0.1\n10/09/2020 18:31:56: I116264190\n10/09/2020 18:31:56: 30-9C-23-87-89-8E\n10/09/2020 18:34:27: 127.0.0.1\n10/09/2020 18:34:27: I116264190\n10/09/2020 18:34:27: 30-9C-23-87-89-8E\n10/09/2020 18:36:06: 127.0.0.1\n10/09/2020 18:36:06: I116264190\n10/09/2020 18:36:06: 30-9C-23-87-89-8E\n10/09/2020 18:37:02: 127.0.0.1\n10/09/2020 18:37:02: I116264190\n10/09/2020 18:37:02: 30-9C-23-87-89-8E\n10/09/2020 18:40:32: 127.0.0.1\n10/09/2020 18:40:32: I116264190\n10/09/2020 18:40:32: 30-9C-23-87-89-8E\n10/09/2020 18:41:16: 127.0.0.1\n10/09/2020 18:41:16: I116264190\n10/09/2020 18:41:16: 30-9C-23-87-89-8E\n10/09/2020 18:43:15: 127.0.0.1\n10/09/2020 18:43:15: I116264190\n10/09/2020 18:43:15: 30-9C-23-87-89-8E\n10/09/2020 18:43:58: 127.0.0.1\n10/09/2020 18:43:58: I116264190\n10/09/2020 18:43:58: 30-9C-23-87-89-8E\n10/09/2020 18:47:42: 127.0.0.1\n10/09/2020 18:47:42: I116264190\n10/09/2020 18:47:42: 30-9C-23-87-89-8E\n10/09/2020 18:49:57: 127.0.0.1\n10/09/2020 18:49:57: I116264190\n10/09/2020 18:49:57: 30-9C-23-87-89-8E\n10/09/2020 18:52:37: 127.0.0.1\n10/09/2020 18:52:37: I116264190\n10/09/2020 18:52:37: 30-9C-23-87-89-8E\n10/09/2020 23:12:58: 127.0.0.1\n10/09/2020 23:12:58: I116264190\n10/09/2020 23:12:58: 30-9C-23-87-89-8E\n10/09/2020 23:34:19: 127.0.0.1\n10/09/2020 23:34:19: I116264190\n10/09/2020 23:34:19: 30-9C-23-87-89-8E\n11/09/2020 00:07:46: 127.0.0.1\n11/09/2020 00:07:46: I116264190\n11/09/2020 00:07:46: 30-9C-23-87-89-8E\n11/09/2020 00:19:46: 127.0.0.1\n11/09/2020 00:19:46: I116264190\n11/09/2020 00:19:46: 30-9C-23-87-89-8E\n11/09/2020 01:25:54: 127.0.0.1\n11/09/2020 01:25:54: I116264190\n11/09/2020 01:25:54: 30-9C-23-87-89-8E\n11/09/2020 01:27:54: 127.0.0.1\n11/09/2020 01:27:54: I116264190\n11/09/2020 01:27:54: 30-9C-23-87-89-8E\n11/09/2020 02:59:49: 127.0.0.1\n11/09/2020 02:59:49: I116264190\n11/09/2020 02:59:49: 30-9C-23-87-89-8E\n11/09/2020 03:02:43: 127.0.0.1\n11/09/2020 03:02:43: I116264190\n11/09/2020 03:02:43: 30-9C-23-87-89-8E\n11/09/2020 03:04:45: 127.0.0.1\n11/09/2020 03:04:45: I116264190\n11/09/2020 03:04:45: 30-9C-23-87-89-8E\n12/09/2020 14:53:16: 127.0.0.1\n12/09/2020 14:53:16: I116264190\n12/09/2020 14:53:16: 30-9C-23-87-89-8E\n13/09/2020 14:36:17: 127.0.0.1\n13/09/2020 14:36:17: I116264190\n13/09/2020 14:36:17: 30-9C-23-87-89-8E\n13/09/2020 14:40:43: 127.0.0.1\n13/09/2020 14:40:43: I116264190\n13/09/2020 14:40:43: 30-9C-23-87-89-8E\n13/09/2020 15:09:39: 127.0.0.1\n13/09/2020 15:09:39: I116264190\n13/09/2020 15:09:39: 30-9C-23-87-89-8E\n13/09/2020 15:12:22: 127.0.0.1\n13/09/2020 15:12:22: I116264190\n13/09/2020 15:12:22: 30-9C-23-87-89-8E\n13/09/2020 15:19:02: 127.0.0.1\n13/09/2020 15:19:02: I116264190\n13/09/2020 15:19:02: 30-9C-23-87-89-8E\n13/09/2020 15:22:49: 127.0.0.1\n13/09/2020 15:22:49: I116264190\n13/09/2020 15:22:49: 30-9C-23-87-89-8E\n13/09/2020 15:24:30: 127.0.0.1\n13/09/2020 15:24:30: I116264190\n13/09/2020 15:24:30: 30-9C-23-87-89-8E\n13/09/2020 15:26:58: 127.0.0.1\n13/09/2020 15:26:58: I116264190\n13/09/2020 15:26:58: 30-9C-23-87-89-8E\n13/09/2020 15:30:47: 127.0.0.1\n13/09/2020 15:30:47: I116264190\n13/09/2020 15:30:47: 30-9C-23-87-89-8E\n13/09/2020 15:32:57: 127.0.0.1\n13/09/2020 15:32:57: I116264190\n13/09/2020 15:32:57: 30-9C-23-87-89-8E\n13/09/2020 15:56:59: 127.0.0.1\n13/09/2020 15:56:59: I116264190\n13/09/2020 15:56:59: 30-9C-23-87-89-8E\n13/09/2020 15:58:11: 127.0.0.1\n13/09/2020 15:58:11: I116264190\n13/09/2020 15:58:11: 30-9C-23-87-89-8E\n13/09/2020 15:58:49: 127.0.0.1\n13/09/2020 15:58:49: I116264190\n13/09/2020 15:58:49: 30-9C-23-87-89-8E\n13/09/2020 15:59:48: 127.0.0.1\n13/09/2020 15:59:48: I116264190\n13/09/2020 15:59:48: 30-9C-23-87-89-8E\n13/09/2020 16:00:36: 127.0.0.1\n13/09/2020 16:00:36: I116264190\n13/09/2020 16:00:36: 30-9C-23-87-89-8E\n13/09/2020 16:01:43: 127.0.0.1\n13/09/2020 16:01:43: I116264190\n13/09/2020 16:01:43: 30-9C-23-87-89-8E\n13/09/2020 16:03:42: 127.0.0.1\n13/09/2020 16:03:42: I116264190\n13/09/2020 16:03:42: 30-9C-23-87-89-8E\n13/09/2020 16:04:10: 127.0.0.1\n13/09/2020 16:04:10: I116264190\n13/09/2020 16:04:10: 30-9C-23-87-89-8E\n13/09/2020 16:05:40: 127.0.0.1\n13/09/2020 16:05:40: I116264190\n13/09/2020 16:05:40: 30-9C-23-87-89-8E\n13/09/2020 16:08:10: 127.0.0.1\n13/09/2020 16:08:10: I116264190\n13/09/2020 16:08:10: 30-9C-23-87-89-8E\n13/09/2020 16:09:45: 127.0.0.1\n13/09/2020 16:09:45: I116264190\n13/09/2020 16:09:45: 30-9C-23-87-89-8E\n13/09/2020 16:13:04: 127.0.0.1\n13/09/2020 16:13:04: I116264190\n13/09/2020 16:13:04: 30-9C-23-87-89-8E\n13/09/2020 16:13:59: 127.0.0.1\n13/09/2020 16:13:59: I116264190\n13/09/2020 16:13:59: 30-9C-23-87-89-8E\n13/09/2020 16:15:20: 127.0.0.1\n13/09/2020 16:15:20: I116264190\n13/09/2020 16:15:20: 30-9C-23-87-89-8E\n13/09/2020 16:16:08: 127.0.0.1\n13/09/2020 16:16:08: I116264190\n13/09/2020 16:16:08: 30-9C-23-87-89-8E\n13/09/2020 16:25:07: 127.0.0.1\n13/09/2020 16:25:07: I116264190\n13/09/2020 16:25:07: 30-9C-23-87-89-8E\n13/09/2020 16:28:18: 127.0.0.1\n13/09/2020 16:28:18: I116264190\n13/09/2020 16:28:18: 30-9C-23-87-89-8E\n13/09/2020 16:28:50: 127.0.0.1\n13/09/2020 16:28:50: I116264190\n13/09/2020 16:28:50: 30-9C-23-87-89-8E\n13/09/2020 16:29:28: 127.0.0.1\n13/09/2020 16:29:28: I116264190\n13/09/2020 16:29:28: 30-9C-23-87-89-8E\n13/09/2020 16:30:13: 127.0.0.1\n13/09/2020 16:30:13: I116264190\n13/09/2020 16:30:13: 30-9C-23-87-89-8E\n13/09/2020 16:31:05: 127.0.0.1\n13/09/2020 16:31:05: I116264190\n13/09/2020 16:31:05: 30-9C-23-87-89-8E\n13/09/2020 16:31:32: 127.0.0.1\n13/09/2020 16:31:32: I116264190\n13/09/2020 16:31:32: 30-9C-23-87-89-8E\n13/09/2020 16:32:06: 127.0.0.1\n13/09/2020 16:32:06: I116264190\n13/09/2020 16:32:06: 30-9C-23-87-89-8E\n13/09/2020 16:32:43: 127.0.0.1\n13/09/2020 16:32:43: I116264190\n13/09/2020 16:32:43: 30-9C-23-87-89-8E\n13/09/2020 16:33:31: 127.0.0.1\n13/09/2020 16:33:31: I116264190\n13/09/2020 16:33:31: 30-9C-23-87-89-8E\n13/09/2020 16:37:08: 127.0.0.1\n13/09/2020 16:37:08: I116264190\n13/09/2020 16:37:08: 30-9C-23-87-89-8E\n13/09/2020 16:38:23: 127.0.0.1\n13/09/2020 16:38:23: I116264190\n13/09/2020 16:38:23: 30-9C-23-87-89-8E\n13/09/2020 16:44:09: 127.0.0.1\n13/09/2020 16:44:09: I116264190\n13/09/2020 16:44:09: 30-9C-23-87-89-8E\n13/09/2020 17:01:49: 127.0.0.1\n13/09/2020 17:01:49: I116264190\n13/09/2020 17:01:49: 30-9C-23-87-89-8E\n13/09/2020 17:05:31: 127.0.0.1\n13/09/2020 17:05:31: I116264190\n13/09/2020 17:05:31: 30-9C-23-87-89-8E\n13/09/2020 17:10:08: 127.0.0.1\n13/09/2020 17:10:08: I116264190\n13/09/2020 17:10:08: 30-9C-23-87-89-8E\n13/09/2020 17:30:56: 127.0.0.1\n13/09/2020 17:30:56: I116264190\n13/09/2020 17:30:56: 30-9C-23-87-89-8E\n13/09/2020 17:35:02: 127.0.0.1\n13/09/2020 17:35:02: I116264190\n13/09/2020 17:35:02: 30-9C-23-87-89-8E\n13/09/2020 17:42:08: 127.0.0.1\n13/09/2020 17:42:08: I116264190\n13/09/2020 17:42:08: 30-9C-23-87-89-8E\n13/09/2020 17:42:27: 127.0.0.1\n13/09/2020 17:42:27: I116264190\n13/09/2020 17:42:27: 30-9C-23-87-89-8E\n13/09/2020 17:45:59: 127.0.0.1\n13/09/2020 17:45:59: I116264190\n13/09/2020 17:45:59: 30-9C-23-87-89-8E\n13/09/2020 17:46:47: 127.0.0.1\n13/09/2020 17:46:47: I116264190\n13/09/2020 17:46:47: 30-9C-23-87-89-8E\n13/09/2020 17:47:45: 127.0.0.1\n13/09/2020 17:47:45: I116264190\n13/09/2020 17:47:45: 30-9C-23-87-89-8E\n13/09/2020 17:49:22: 127.0.0.1\n13/09/2020 17:49:22: I116264190\n13/09/2020 17:49:22: 30-9C-23-87-89-8E\n13/09/2020 17:50:34: 127.0.0.1\n13/09/2020 17:50:34: I116264190\n13/09/2020 17:50:34: 30-9C-23-87-89-8E\n13/09/2020 17:52:50: 127.0.0.1\n13/09/2020 17:52:50: I116264190\n13/09/2020 17:52:50: 30-9C-23-87-89-8E\n13/09/2020 17:53:55: 127.0.0.1\n13/09/2020 17:53:55: I116264190\n13/09/2020 17:53:55: 30-9C-23-87-89-8E\n13/09/2020 18:06:04: 127.0.0.1\n13/09/2020 18:06:04: I116264190\n13/09/2020 18:06:04: 30-9C-23-87-89-8E\n13/09/2020 18:20:49: 127.0.0.1\n13/09/2020 18:20:49: I116264190\n13/09/2020 18:20:49: 30-9C-23-87-89-8E\n13/09/2020 18:22:42: 127.0.0.1\n13/09/2020 18:22:42: I116264190\n13/09/2020 18:22:42: 30-9C-23-87-89-8E\n13/09/2020 18:23:26: 127.0.0.1\n13/09/2020 18:23:26: I116264190\n13/09/2020 18:23:26: 30-9C-23-87-89-8E\n13/09/2020 18:29:24: 127.0.0.1\n13/09/2020 18:29:24: I116264190\n13/09/2020 18:29:24: 30-9C-23-87-89-8E\n13/09/2020 18:31:57: 127.0.0.1\n13/09/2020 18:31:57: I116264190\n13/09/2020 18:31:57: 30-9C-23-87-89-8E\n13/09/2020 18:32:52: 127.0.0.1\n13/09/2020 18:32:52: I116264190\n13/09/2020 18:32:52: 30-9C-23-87-89-8E\n13/09/2020 18:34:00: 127.0.0.1\n13/09/2020 18:34:00: I116264190\n13/09/2020 18:34:00: 30-9C-23-87-89-8E\n13/09/2020 18:34:58: 127.0.0.1\n13/09/2020 18:34:58: I116264190\n13/09/2020 18:34:58: 30-9C-23-87-89-8E\n13/09/2020 18:36:05: 127.0.0.1\n13/09/2020 18:36:05: I116264190\n13/09/2020 18:36:05: 30-9C-23-87-89-8E\n13/09/2020 19:17:14: 127.0.0.1\n13/09/2020 19:17:14: I116264190\n13/09/2020 19:17:14: 30-9C-23-87-89-8E\n13/09/2020 19:20:53: 127.0.0.1\n13/09/2020 19:20:53: I116264190\n13/09/2020 19:20:53: 30-9C-23-87-89-8E\n13/09/2020 19:22:35: 127.0.0.1\n13/09/2020 19:22:35: I116264190\n13/09/2020 19:22:35: 30-9C-23-87-89-8E\n13/09/2020 19:29:15: 127.0.0.1\n13/09/2020 19:29:15: I116264190\n13/09/2020 19:29:15: 30-9C-23-87-89-8E\n14/09/2020 14:20:25: 127.0.0.1\n14/09/2020 14:20:25: I116264190\n14/09/2020 14:20:25: 30-9C-23-87-89-8E\n14/09/2020 18:02:16: 127.0.0.1\n14/09/2020 18:02:16: I116264190\n14/09/2020 18:02:16: 30-9C-23-87-89-8E\n14/09/2020 18:03:29: 127.0.0.1\n14/09/2020 18:03:29: I116264190\n14/09/2020 18:03:29: 30-9C-23-87-89-8E\n14/09/2020 19:42:55: 127.0.0.1\n14/09/2020 19:42:55: I116264190\n14/09/2020 19:42:55: 30-9C-23-87-89-8E\n14/09/2020 19:57:58: 127.0.0.1\n14/09/2020 19:57:58: I116264190\n14/09/2020 19:57:58: 30-9C-23-87-89-8E\n14/09/2020 20:06:45: 127.0.0.1\n14/09/2020 20:06:45: I116264190\n14/09/2020 20:06:45: 30-9C-23-87-89-8E\n14/09/2020 20:30:56: 127.0.0.1\n14/09/2020 20:30:56: I116264190\n14/09/2020 20:30:56: 30-9C-23-87-89-8E\n14/09/2020 22:35:50: 127.0.0.1\n14/09/2020 22:35:50: I116264190\n14/09/2020 22:35:50: 30-9C-23-87-89-8E\n14/09/2020 23:07:10: 127.0.0.1\n14/09/2020 23:07:10: I116264190\n14/09/2020 23:07:10: 30-9C-23-87-89-8E\n14/09/2020 23:08:26: 127.0.0.1\n14/09/2020 23:08:26: I116264190\n14/09/2020 23:08:26: 30-9C-23-87-89-8E\n14/09/2020 23:50:43: 127.0.0.1\n14/09/2020 23:50:43: I116264190\n14/09/2020 23:50:43: 30-9C-23-87-89-8E\n15/09/2020 00:46:43: 127.0.0.1\n15/09/2020 00:46:43: I116264190\n15/09/2020 00:46:43: 30-9C-23-87-89-8E\n15/09/2020 11:52:20: 127.0.0.1\n15/09/2020 11:52:20: I116264190\n15/09/2020 11:52:20: 30-9C-23-87-89-8E\n15/09/2020 11:52:50: 127.0.0.1\n15/09/2020 11:52:50: I116264190\n15/09/2020 11:52:50: 30-9C-23-87-89-8E\n15/09/2020 11:55:21: 127.0.0.1\n15/09/2020 11:55:21: I116264190\n15/09/2020 11:55:21: 30-9C-23-87-89-8E\n15/09/2020 11:57:58: 127.0.0.1\n15/09/2020 11:57:58: I116264190\n15/09/2020 11:57:58: 30-9C-23-87-89-8E\n15/09/2020 12:03:43: 127.0.0.1\n15/09/2020 12:03:43: I116264190\n15/09/2020 12:03:43: 30-9C-23-87-89-8E\n15/09/2020 12:39:29: 127.0.0.1\n15/09/2020 12:39:29: I116264190\n15/09/2020 12:39:29: 30-9C-23-87-89-8E\n15/09/2020 12:56:16: 127.0.0.1\n15/09/2020 12:56:16: I116264190\n15/09/2020 12:56:16: 30-9C-23-87-89-8E\n15/09/2020 12:57:18: 127.0.0.1\n15/09/2020 12:57:18: I116264190\n15/09/2020 12:57:18: 30-9C-23-87-89-8E\n15/09/2020 13:31:06: 127.0.0.1\n15/09/2020 13:31:06: I116264190\n15/09/2020 13:31:06: 30-9C-23-87-89-8E\n15/09/2020 13:47:47: 127.0.0.1\n15/09/2020 13:47:47: I116264190\n15/09/2020 13:47:47: 30-9C-23-87-89-8E\n17/09/2020 11:29:59: 127.0.0.1\n17/09/2020 11:29:59: I116264190\n17/09/2020 11:29:59: 30-9C-23-87-89-8E\n17/09/2020 11:36:42: 127.0.0.1\n17/09/2020 11:36:42: I116264190\n17/09/2020 11:36:42: 30-9C-23-87-89-8E\n17/09/2020 11:39:14: 127.0.0.1\n17/09/2020 11:39:14: I116264190\n17/09/2020 11:39:14: 30-9C-23-87-89-8E\n17/09/2020 13:13:51: 127.0.0.1\n17/09/2020 13:13:51: I116264190\n17/09/2020 13:13:51: 30-9C-23-87-89-8E\n17/09/2020 13:55:19: 127.0.0.1\n17/09/2020 13:55:19: I116264190\n17/09/2020 13:55:19: 30-9C-23-87-89-8E\n17/09/2020 14:02:32: 127.0.0.1\n17/09/2020 14:02:32: I116264190\n17/09/2020 14:02:32: 30-9C-23-87-89-8E\n17/09/2020 14:46:02: 127.0.0.1\n17/09/2020 14:46:02: I116264190\n17/09/2020 14:46:02: 30-9C-23-87-89-8E\n17/09/2020 14:47:08: 127.0.0.1\n17/09/2020 14:47:08: I116264190\n17/09/2020 14:47:08: 30-9C-23-87-89-8E\n17/09/2020 14:50:18: 127.0.0.1\n17/09/2020 14:50:18: I116264190\n17/09/2020 14:50:18: 30-9C-23-87-89-8E\n17/09/2020 14:51:26: 127.0.0.1\n17/09/2020 14:51:26: I116264190\n17/09/2020 14:51:26: 30-9C-23-87-89-8E\n17/09/2020 14:51:59: 127.0.0.1\n17/09/2020 14:51:59: I116264190\n17/09/2020 14:51:59: 30-9C-23-87-89-8E\n17/09/2020 14:52:35: 127.0.0.1\n17/09/2020 14:52:35: I116264190\n17/09/2020 14:52:35: 30-9C-23-87-89-8E\n17/09/2020 14:53:13: 127.0.0.1\n17/09/2020 14:53:13: I116264190\n17/09/2020 14:53:13: 30-9C-23-87-89-8E\n17/09/2020 14:53:53: 127.0.0.1\n17/09/2020 14:53:53: I116264190\n17/09/2020 14:53:53: 30-9C-23-87-89-8E\n17/09/2020 14:55:00: 127.0.0.1\n17/09/2020 14:55:00: I116264190\n17/09/2020 14:55:00: 30-9C-23-87-89-8E\n17/09/2020 14:56:22: 127.0.0.1\n17/09/2020 14:56:22: I116264190\n17/09/2020 14:56:22: 30-9C-23-87-89-8E\n17/09/2020 14:56:50: 127.0.0.1\n17/09/2020 14:56:50: I116264190\n17/09/2020 14:56:50: 30-9C-23-87-89-8E\n17/09/2020 14:57:24: 127.0.0.1\n17/09/2020 14:57:24: I116264190\n17/09/2020 14:57:24: 30-9C-23-87-89-8E\n17/09/2020 14:58:46: 127.0.0.1\n17/09/2020 14:58:46: I116264190\n17/09/2020 14:58:46: 30-9C-23-87-89-8E\n17/09/2020 14:59:22: 127.0.0.1\n17/09/2020 14:59:22: I116264190\n17/09/2020 14:59:22: 30-9C-23-87-89-8E\n17/09/2020 15:17:34: 127.0.0.1\n17/09/2020 15:17:34: I116264190\n17/09/2020 15:17:34: 30-9C-23-87-89-8E\n17/09/2020 15:19:06: 127.0.0.1\n17/09/2020 15:19:06: I116264190\n17/09/2020 15:19:06: 30-9C-23-87-89-8E\n17/09/2020 15:22:03: 127.0.0.1\n17/09/2020 15:22:03: I116264190\n17/09/2020 15:22:03: 30-9C-23-87-89-8E\n17/09/2020 15:31:31: 127.0.0.1\n17/09/2020 15:31:31: I116264190\n17/09/2020 15:31:31: 30-9C-23-87-89-8E\n17/09/2020 15:32:17: 127.0.0.1\n17/09/2020 15:32:17: I116264190\n17/09/2020 15:32:17: 30-9C-23-87-89-8E\n17/09/2020 15:45:41: 127.0.0.1\n17/09/2020 15:45:41: I116264190\n17/09/2020 15:45:41: 30-9C-23-87-89-8E\n17/09/2020 15:48:10: 127.0.0.1\n17/09/2020 15:48:10: I116264190\n17/09/2020 15:48:10: 30-9C-23-87-89-8E\n17/09/2020 16:22:03: 127.0.0.1\n17/09/2020 16:22:03: I116264190\n17/09/2020 16:22:03: 30-9C-23-87-89-8E\n17/09/2020 16:32:12: 127.0.0.1\n17/09/2020 16:32:12: I116264190\n17/09/2020 16:32:12: 30-9C-23-87-89-8E\n17/09/2020 16:33:35: 127.0.0.1\n17/09/2020 16:33:35: I116264190\n17/09/2020 16:33:35: 30-9C-23-87-89-8E\n17/09/2020 16:45:15: 127.0.0.1\n17/09/2020 16:45:15: I116264190\n17/09/2020 16:45:15: 30-9C-23-87-89-8E\n17/09/2020 16:46:07: 127.0.0.1\n17/09/2020 16:46:07: I116264190\n17/09/2020 16:46:07: 30-9C-23-87-89-8E\n17/09/2020 16:54:58: 127.0.0.1\n17/09/2020 16:54:58: I116264190\n17/09/2020 16:54:58: 30-9C-23-87-89-8E\n17/09/2020 16:55:28: 127.0.0.1\n17/09/2020 16:55:28: I116264190\n17/09/2020 16:55:28: 30-9C-23-87-89-8E\n17/09/2020 16:55:57: 127.0.0.1\n17/09/2020 16:55:57: I116264190\n17/09/2020 16:55:57: 30-9C-23-87-89-8E\n17/09/2020 16:56:53: 127.0.0.1\n17/09/2020 16:56:53: I116264190\n17/09/2020 16:56:53: 30-9C-23-87-89-8E\n17/09/2020 16:58:16: 127.0.0.1\n17/09/2020 16:58:16: I116264190\n17/09/2020 16:58:16: 30-9C-23-87-89-8E\n17/09/2020 16:58:49: 127.0.0.1\n17/09/2020 16:58:49: I116264190\n17/09/2020 16:58:49: 30-9C-23-87-89-8E\n17/09/2020 16:59:37: 127.0.0.1\n17/09/2020 16:59:37: I116264190\n17/09/2020 16:59:37: 30-9C-23-87-89-8E\n17/09/2020 17:01:57: 127.0.0.1\n17/09/2020 17:01:57: I116264190\n17/09/2020 17:01:57: 30-9C-23-87-89-8E\n17/09/2020 17:02:46: 127.0.0.1\n17/09/2020 17:02:46: I116264190\n17/09/2020 17:02:46: 30-9C-23-87-89-8E\n17/09/2020 18:26:49: 127.0.0.1\n17/09/2020 18:26:49: I116264190\n17/09/2020 18:26:49: 30-9C-23-87-89-8E\n17/09/2020 18:53:22: 127.0.0.1\n17/09/2020 18:53:22: I116264190\n17/09/2020 18:53:22: 30-9C-23-87-89-8E\n17/09/2020 19:24:35: 127.0.0.1\n17/09/2020 19:24:35: I116264190\n17/09/2020 19:24:35: 30-9C-23-87-89-8E\n17/09/2020 19:33:51: 127.0.0.1\n17/09/2020 19:33:51: I116264190\n17/09/2020 19:33:51: 30-9C-23-87-89-8E\n17/09/2020 20:37:55: 127.0.0.1\n17/09/2020 20:37:55: I116264190\n17/09/2020 20:37:55: 30-9C-23-87-89-8E\n17/09/2020 21:03:04: 127.0.0.1\n17/09/2020 21:03:04: I116264190\n17/09/2020 21:03:04: 30-9C-23-87-89-8E\n17/09/2020 21:03:38: 127.0.0.1\n17/09/2020 21:03:38: I116264190\n17/09/2020 21:03:38: 30-9C-23-87-89-8E\n22/09/2020 12:34:31: 127.0.0.1\n22/09/2020 12:34:31: I116264190\n22/09/2020 12:34:31: 30-9C-23-87-89-8E\n22/09/2020 12:35:29: 127.0.0.1\n22/09/2020 12:35:29: I116264190\n22/09/2020 12:35:29: 30-9C-23-87-89-8E\n22/09/2020 12:37:35: 127.0.0.1\n22/09/2020 12:37:35: I116264190\n22/09/2020 12:37:35: 30-9C-23-87-89-8E\n23/09/2020 14:25:26: 127.0.0.1\n23/09/2020 14:25:26: I116264190\n23/09/2020 14:25:26: 30-9C-23-87-89-8E\n23/09/2020 14:31:33: 127.0.0.1\n23/09/2020 14:31:33: I116264190\n23/09/2020 14:31:33: 30-9C-23-87-89-8E\n23/09/2020 14:40:21: 127.0.0.1\n23/09/2020 14:40:21: I116264190\n23/09/2020 14:40:21: 30-9C-23-87-89-8E\n', '10/09/2020 14:47:28: tele 0,50,50,31,30\n10/09/2020 14:47:55: teleto test1\n10/09/2020 14:48:02: teleto test\n10/09/2020 14:48:05: tele 0,39,78,29,10\n10/09/2020 14:48:07: coords\n10/09/2020 14:48:47: coords\n10/09/2020 14:50:14: fpson\n10/09/2020 14:50:18: fpsoff\n10/09/2020 14:50:20: fpson\n10/09/2020 14:50:24: fpsoff\n10/09/2020 14:51:36: tele 0,49,61,57,27\n10/09/2020 14:51:41: tele 0,49,61,45,24\n10/09/2020 14:51:43: tele 0,49,61,44,40\n10/09/2020 14:51:45: tele 0,49,61,36,54\n10/09/2020 14:51:47: tele 0,49,61,20,57\n10/09/2020 14:51:50: tele 0,49,61,5,46\n10/09/2020 14:51:51: tele 0,48,61,55,39\n10/09/2020 14:51:53: tele 0,48,61,41,38\n10/09/2020 14:51:56: tele 0,48,61,44,51\n10/09/2020 14:51:57: tele 0,48,61,57,55\n10/09/2020 14:51:58: tele 0,49,61,8,51\n10/09/2020 14:51:59: tele 0,49,61,24,44\n10/09/2020 14:52:00: tele 0,49,61,39,38\n10/09/2020 14:52:01: tele 0,49,61,53,33\n10/09/2020 14:52:01: tele 0,50,61,2,26\n10/09/2020 14:52:02: tele 0,50,61,16,21\n10/09/2020 14:52:03: tele 0,50,61,30,17\n10/09/2020 14:52:10: tele 0,50,50,22,18\n10/09/2020 16:26:29: tele 0,50,50,32,27\n10/09/2020 16:26:50: teleto test\n10/09/2020 16:26:53: tele 0,39,78,29,10\n10/09/2020 16:27:02: mapfiles\n10/09/2020 16:30:00: tele 0,49,61,53,37\n10/09/2020 16:30:10: mapfiles\n10/09/2020 16:30:40: tele 0,49,61,40,37\n10/09/2020 16:30:41: tele 0,49,61,26,41\n10/09/2020 16:30:50: region\n10/09/2020 16:30:55: tele 0,49,61,10,43\n10/09/2020 16:30:57: tele 0,48,61,60,42\n10/09/2020 16:30:59: region\n10/09/2020 16:31:07: mapdata\n10/09/2020 16:31:19: mapfiles\n10/09/2020 16:35:59: tele 0,48,61,54,40\n10/09/2020 16:36:07: tele 0,48,61,50,45\n10/09/2020 16:36:08: tele 0,48,61,49,50\n10/09/2020 16:36:10: tele 0,48,61,42,51\n10/09/2020 16:36:11: tele 0,48,61,35,53\n10/09/2020 16:36:13: tele 0,48,61,38,50\n10/09/2020 16:36:14: tele 0,48,61,38,46\n10/09/2020 16:36:16: tele 0,48,61,53,44\n10/09/2020 16:36:16: tele 0,49,61,4,42\n10/09/2020 16:36:17: tele 0,49,61,15,44\n10/09/2020 16:36:18: tele 0,49,61,22,43\n10/09/2020 16:36:19: tele 0,49,61,19,31\n10/09/2020 16:36:20: tele 0,49,61,15,19\n10/09/2020 16:36:21: tele 0,49,61,18,7\n10/09/2020 16:36:23: tele 0,49,60,21,59\n10/09/2020 16:36:26: tele 0,49,60,25,46\n10/09/2020 16:36:29: tele 0,49,60,25,34\n10/09/2020 16:36:29: tele 0,49,60,25,22\n10/09/2020 16:36:31: tele 0,49,60,23,8\n10/09/2020 16:36:31: tele 0,49,59,23,59\n10/09/2020 16:36:34: tele 0,49,59,17,45\n10/09/2020 16:36:34: tele 0,49,59,13,31\n10/09/2020 16:36:35: tele 0,49,59,11,17\n10/09/2020 16:36:38: tele 0,49,59,11,3\n10/09/2020 16:36:39: tele 0,49,58,11,53\n10/09/2020 16:36:40: tele 0,49,58,10,45\n10/09/2020 16:36:43: tele 0,49,58,12,31\n10/09/2020 16:36:49: tele 0,50,59,2,42\n10/09/2020 16:36:56: tele 0,49,54,19,53\n10/09/2020 16:36:58: tele 0,49,54,21,43\n10/09/2020 16:37:00: tele 0,49,54,24,37\n10/09/2020 16:37:02: tele 0,49,54,23,33\n10/09/2020 17:43:17: tele 0,50,50,22,19\n10/09/2020 18:38:20: tele 0,51,50,8,27\n10/09/2020 18:39:23: tele 0,51,49,14,38\n10/09/2020 18:39:24: tele 0,51,49,13,53\n10/09/2020 18:39:25: tele 0,51,50,13,4\n10/09/2020 18:39:26: tele 0,51,50,13,19\n10/09/2020 18:39:27: tele 0,51,50,13,34\n10/09/2020 18:39:29: tele 0,51,50,22,47\n10/09/2020 18:40:36: tele 0,51,51,35,2\n10/09/2020 18:40:38: tele 0,51,50,47,59\n10/09/2020 18:40:42: tele 0,51,50,61,49\n10/09/2020 18:40:43: tele 0,52,50,11,49\n10/09/2020 18:40:43: tele 0,52,50,20,56\n10/09/2020 18:40:49: tele 0,52,50,37,55\n10/09/2020 18:40:50: tele 0,52,50,51,50\n10/09/2020 18:40:51: tele 0,52,50,55,39\n10/09/2020 18:42:01: tele 0,50,50,57,27\n10/09/2020 18:44:21: tele 0,50,51,37,42\n10/09/2020 18:49:07: fog\n10/09/2020 18:49:09: fogoff\n10/09/2020 18:49:11: nofog\n10/09/2020 18:49:12: fog\n10/09/2020 23:34:44: debug\n10/09/2020 23:35:03: home\n11/09/2020 00:08:03: debug\n11/09/2020 01:28:13: tele 0,50,50,35,16\n11/09/2020 01:28:16: tele 0,48,49,39,21\n11/09/2020 01:28:19: tele 1,48,49,39,21\n11/09/2020 01:28:20: tele 2,48,49,39,21\n11/09/2020 01:28:21: tele 2,48,49,38,23\n11/09/2020 01:28:54: tele 0,50,50,36,19\n11/09/2020 01:29:24: tele 0,50,50,23,16\n11/09/2020 01:29:26: tele 0,50,50,10,13\n11/09/2020 01:29:28: tele 1,50,50,10,13\n11/09/2020 01:29:30: tele 1,50,50,10,20\n11/09/2020 03:00:08: tele 0,50,50,19,18\n11/09/2020 03:00:09: tele 0,50,50,33,22\n12/09/2020 14:53:26: tele 0,46,51,16,8\n12/09/2020 14:53:37: tele 0,42,52,19,56\n12/09/2020 14:53:41: tele 0,42,52,15,55\n12/09/2020 14:53:54: debug\n12/09/2020 14:56:11: coords\n12/09/2020 14:56:40: tele 0,46,51,14,9\n12/09/2020 14:57:15: coords\n12/09/2020 14:57:35: tele 0,46,50,12,9\n12/09/2020 14:57:44: coords\n12/09/2020 14:57:55: tele 0,47,50,20,35\n12/09/2020 14:57:58: coords\n12/09/2020 14:58:07: tele 0,48,50,8,49\n12/09/2020 14:58:12: coords\n12/09/2020 14:59:33: empty\n12/09/2020 14:59:36: item 10498\n12/09/2020 14:59:48: item 10498\n12/09/2020 15:00:00: item 10499\n12/09/2020 15:01:32: item 13111\n12/09/2020 15:02:14: item 13170\n12/09/2020 15:03:32: item 1011\n12/09/2020 15:05:12: item 9944\n12/09/2020 15:05:14: item 9945\n12/09/2020 15:05:50: item 10863\n12/09/2020 15:06:44: tele 0,47,51,1,14\n12/09/2020 15:06:49: 1hko\n12/09/2020 15:07:16: tele 0,47,51,17,5\n12/09/2020 15:07:17: tele 0,47,50,31,59\n12/09/2020 15:07:17: tele 0,47,50,45,49\n12/09/2020 15:07:18: tele 0,47,50,60,46\n12/09/2020 15:07:19: tele 0,48,50,9,48\n12/09/2020 15:08:48: item 12645\n12/09/2020 15:09:42: item 7535\n12/09/2020 15:10:55: item 10607\n12/09/2020 15:11:02: item 6106\n12/09/2020 15:11:54: item 9470\n12/09/2020 15:15:20: empty\n12/09/2020 15:15:23: item 9096\n12/09/2020 15:15:26: item 9097\n12/09/2020 15:15:29: item 9098\n12/09/2020 15:15:31: item 9099\n12/09/2020 15:15:38: item 9100\n12/09/2020 15:15:42: item 9101\n12/09/2020 15:17:11: item 9068\n12/09/2020 15:17:29: item 9070\n12/09/2020 15:18:20: item 6067\n12/09/2020 15:18:27: item 6066\n12/09/2020 15:19:34: item 8952\n12/09/2020 15:19:43: item 13181\n12/09/2020 15:20:30: empty\n12/09/2020 15:21:31: item 2997\n12/09/2020 15:21:49: item 426\n12/09/2020 15:21:53: item 428\n12/09/2020 15:23:39: item 546\n12/09/2020 15:23:51: item 548\n12/09/2020 15:25:08: item 6335\n12/09/2020 15:25:11: item 6336\n12/09/2020 15:25:15: item 6337\n12/09/2020 15:26:30: empty\n12/09/2020 15:26:34: item 14076\n12/09/2020 15:26:37: item 14077\n12/09/2020 15:26:39: item 14081\n12/09/2020 15:28:06: item 14078\n12/09/2020 15:28:10: item 14079\n12/09/2020 15:28:13: item 14080\n12/09/2020 15:30:04: item 579\n12/09/2020 15:30:19: item 1017\n12/09/2020 15:30:47: item 7394\n12/09/2020 15:31:19: item 2898\n12/09/2020 15:33:22: item 6722\n12/09/2020 15:33:27: item 7592\n12/09/2020 15:34:27: npc 8207\n12/09/2020 15:34:35: npc 922\n12/09/2020 15:34:47: tele 0,48,50,18,54\n12/09/2020 15:34:49: tele 0,48,50,14,59\n12/09/2020 15:34:59: tele 0,48,50,5,48\n12/09/2020 15:36:04: npc 13172\n12/09/2020 15:36:11: npc 13173\n12/09/2020 15:36:28: npc 2634\n12/09/2020 15:36:33: tele 0,48,50,20,48\n12/09/2020 15:37:01: npc 755\n12/09/2020 15:37:06: tele 0,48,51,29,1\n12/09/2020 15:37:07: tele 0,48,51,27,4\n12/09/2020 15:37:19: tele 0,48,50,27,59\n12/09/2020 15:37:36: npc 2692\n12/09/2020 15:37:41: tele 0,48,50,14,60\n12/09/2020 15:37:42: tele 0,47,50,62,61\n12/09/2020 15:37:43: tele 0,47,50,46,62\n12/09/2020 15:37:44: tele 0,47,50,42,58\n12/09/2020 15:37:56: tele 0,47,50,29,55\n12/09/2020 15:37:57: tele 0,47,50,18,47\n12/09/2020 15:37:58: tele 0,47,50,6,58\n12/09/2020 15:38:13: tele 0,47,50,16,51\n12/09/2020 15:38:21: tele 0,47,50,28,54\n12/09/2020 15:38:22: tele 0,47,50,38,57\n12/09/2020 15:38:26: tele 0,47,50,38,47\n12/09/2020 15:38:42: npc 11700\n12/09/2020 15:38:48: npc 11703\n12/09/2020 15:39:02: tele 0,47,50,21,40\n12/09/2020 15:39:02: tele 0,47,50,5,33\n12/09/2020 15:39:04: tele 0,47,50,6,24\n12/09/2020 15:39:18: npc 556\n12/09/2020 15:39:23: tele 0,47,50,10,36\n12/09/2020 15:39:25: tele 0,47,50,4,47\n12/09/2020 15:39:34: tele 0,47,50,17,44\n12/09/2020 15:39:35: tele 0,47,50,28,46\n12/09/2020 15:39:36: tele 0,47,50,41,48\n12/09/2020 15:39:38: tele 0,47,50,39,57\n12/09/2020 15:40:03: pnpc 744\n12/09/2020 15:40:09: npc 744\n12/09/2020 15:40:14: tele 0,47,50,38,46\n12/09/2020 15:40:14: tele 0,47,50,40,36\n12/09/2020 15:40:15: tele 0,47,50,37,24\n12/09/2020 15:40:16: tele 0,47,50,31,13\n12/09/2020 15:40:18: tele 0,47,50,36,3\n12/09/2020 15:40:32: tele 0,46,50,14,3\n12/09/2020 15:40:55: tele 0,46,50,4,4\n12/09/2020 15:41:02: tele 0,46,50,5,15\n12/09/2020 15:41:21: tele 0,46,50,22,6\n12/09/2020 15:41:46: npc 557\n12/09/2020 15:41:54: tele 0,46,50,36,2\n12/09/2020 15:41:55: tele 0,46,49,50,62\n12/09/2020 15:41:56: tele 0,46,49,63,58\n12/09/2020 15:41:57: tele 0,47,50,6,5\n12/09/2020 15:42:23: pnpc 11705\n12/09/2020 15:42:26: pnpc -1\n12/09/2020 15:42:35: tele 0,47,50,16,17\n12/09/2020 15:42:36: tele 0,47,50,26,28\n12/09/2020 15:42:37: tele 0,47,50,35,39\n12/09/2020 15:42:38: tele 0,47,50,43,46\n12/09/2020 15:42:58: npc 2691\n12/09/2020 15:43:02: tele 0,47,50,44,56\n12/09/2020 15:43:47: pnpc 8078\n12/09/2020 15:47:02: obj 32034\n12/09/2020 15:47:10: tele 0,47,50,45,53\n12/09/2020 15:47:16: obj 32041\n12/09/2020 15:47:23: obj 32042\n12/09/2020 15:47:28: obj 32044\n12/09/2020 15:47:50: tele 0,42,52,17,54\n12/09/2020 15:48:02: obj 39240\n12/09/2020 15:48:12: tele 0,42,52,19,56\n12/09/2020 15:48:16: coords\n12/09/2020 15:48:20: tele 0,42,52,19,57\n12/09/2020 15:48:23: obj\n12/09/2020 15:48:25: coords\n12/09/2020 15:48:30: tele 0,42,52,20,58\n12/09/2020 15:48:34: coords\n12/09/2020 15:48:37: tele 0,42,52,15,56\n12/09/2020 15:48:49: coords\n13/09/2020 14:37:06: tele 0,47,50,63,57\n13/09/2020 14:37:07: tele 0,48,51,0,5\n13/09/2020 14:37:21: npc 922\n13/09/2020 14:37:54: npc 970\n13/09/2020 14:38:22: npc 3299\n13/09/2020 14:39:18: npc 2634\n13/09/2020 14:39:57: npc 755\n13/09/2020 14:55:45: npc 743\n13/09/2020 14:56:09: npc 2692\n13/09/2020 14:56:33: npc 583\n13/09/2020 14:58:18: npc 559\n13/09/2020 14:58:41: npc 558\n13/09/2020 14:59:02: npc 556\n13/09/2020 14:59:23: npc 2690\n13/09/2020 14:59:48: npc 744\n13/09/2020 15:00:14: npc 2691\n13/09/2020 15:00:42: npc 744\n13/09/2020 15:00:55: debug\n13/09/2020 15:01:03: npc 375\n13/09/2020 15:01:37: npc 557\n13/09/2020 15:02:06: npc 1860\n13/09/2020 15:02:36: npc 307\n13/09/2020 15:03:12: npc 585\n13/09/2020 15:03:38: npc 531\n13/09/2020 15:04:02: npc 530\n13/09/2020 15:10:31: debug\n13/09/2020 15:12:29: debug\n13/09/2020 15:12:39: npc 307\n13/09/2020 15:14:09: tele 0,48,51,1,17\n13/09/2020 15:19:10: tele 0,43,48,63,8\n13/09/2020 15:19:16: debug\n13/09/2020 15:19:48: remote 899\n13/09/2020 15:19:53: remote 900\n13/09/2020 15:19:56: remote 904\n13/09/2020 15:19:58: remote 902\n13/09/2020 15:20:05: remote901\n13/09/2020 15:20:07: remote 901\n13/09/2020 15:20:09: remote 900\n13/09/2020 15:21:32: remote 666\n13/09/2020 15:21:35: remote 688\n13/09/2020 15:21:38: remote 717\n13/09/2020 15:21:43: remote 718\n13/09/2020 15:21:47: remote 799\n13/09/2020 15:21:59: remote 682\n13/09/2020 15:22:04: remote 771\n13/09/2020 15:22:10: remote 814\n13/09/2020 15:22:18: remote 890\n13/09/2020 15:22:20: remote 883\n13/09/2020 15:23:49: tele 0,50,50,22,17\n13/09/2020 15:23:57: debug\n13/09/2020 15:27:24: pnpc 47\n13/09/2020 15:31:07: tele 0,42,52,15,51\n13/09/2020 15:31:12: debug\n13/09/2020 15:33:50: tele 0,42,52,14,56\n13/09/2020 15:35:33: empty\n13/09/2020 17:02:00: debug\n13/09/2020 17:03:53: item 995\n13/09/2020 17:35:27: debug\n13/09/2020 18:23:37: tele 0,50,53,12,35\n13/09/2020 18:23:38: tele 0,50,53,10,33\n13/09/2020 18:23:40: tele 0,50,53,9,30\n13/09/2020 18:23:43: tele 0,50,53,11,28\n13/09/2020 18:29:48: tele 0,42,52,19,55\n13/09/2020 18:29:52: tele 0,42,52,15,54\n13/09/2020 18:29:54: tele 0,42,52,15,55\n13/09/2020 18:29:55: tele 0,42,52,16,55\n13/09/2020 18:30:02: region\n13/09/2020 18:30:05: coords\n13/09/2020 18:33:00: tele 0,42,52,22,45\n13/09/2020 18:33:01: tele 0,42,52,23,36\n13/09/2020 18:33:08: obj 39233\n13/09/2020 18:34:10: obj 39233\n13/09/2020 18:35:10: tele 0,42,52,18,46\n13/09/2020 18:35:16: tele 0,42,52,18,50\n13/09/2020 18:35:23: obj 39237\n13/09/2020 19:17:21: tele 0,46,50,9,15\n13/09/2020 19:17:46: tele 0,46,50,10,14\n13/09/2020 19:17:48: tele 0,46,50,9,14\n14/09/2020 18:03:49: anim 288\n14/09/2020 18:05:12: anim 13662\n14/09/2020 18:05:20: anim 13710\n14/09/2020 18:05:28: anim 13744\n14/09/2020 18:05:36: anim 13798\n14/09/2020 18:05:40: anim -1\n14/09/2020 18:05:59: anim 12931\n14/09/2020 18:06:01: anim 01\n14/09/2020 18:06:05: anim 12919\n14/09/2020 18:06:17: anim 12594\n14/09/2020 18:06:31: anim 12152\n14/09/2020 18:06:40: anim 12059\n14/09/2020 18:07:29: tele 0,46,50,31,21\n14/09/2020 18:07:30: tele 0,46,50,44,21\n14/09/2020 18:07:31: tele 0,46,50,58,22\n14/09/2020 18:07:45: anim 11809\n14/09/2020 20:07:21: tele 0,47,50,48,53\n14/09/2020 20:07:22: tele 0,47,50,62,57\n14/09/2020 20:07:23: tele 0,48,50,11,53\n14/09/2020 20:07:26: tele 0,48,50,16,48\n14/09/2020 20:08:05: remote 1258\n14/09/2020 20:08:18: remote 1603\n14/09/2020 20:08:55: max\n14/09/2020 20:08:57: master\n14/09/2020 20:09:22: item 4734\n14/09/2020 20:09:38: remote 1603\n14/09/2020 20:09:57: remote 2182\n14/09/2020 20:10:01: remote 1666\n14/09/2020 20:10:18: remote 1547\n14/09/2020 20:10:28: remote 1548\n14/09/2020 20:10:45: remote 1262\n14/09/2020 20:10:54: remote 1185\n14/09/2020 20:11:05: remote 1729\n14/09/2020 20:11:10: remote 1695\n14/09/2020 20:11:16: remote 1695\n14/09/2020 20:12:05: emote 733\n14/09/2020 20:13:55: item 11969\n14/09/2020 20:14:14: itemn whip\n14/09/2020 20:14:18: itemn abyssal whip\n14/09/2020 20:15:09: anim 13036\n14/09/2020 20:15:13: anim 12305\n14/09/2020 20:15:17: anim 13055\n14/09/2020 20:26:35: emote 13190\n14/09/2020 20:26:45: emote 13192\n14/09/2020 20:33:38: tele 0,48,51,16,0\n14/09/2020 20:33:39: tele 0,48,51,20,16\n14/09/2020 20:33:39: tele 0,48,51,24,32\n14/09/2020 20:33:40: tele 0,48,51,28,47\n14/09/2020 20:33:40: tele 0,48,51,32,62\n14/09/2020 20:33:41: tele 0,48,52,36,13\n14/09/2020 20:33:42: tele 0,48,52,43,27\n14/09/2020 20:33:42: tele 0,48,52,51,40\n14/09/2020 20:33:43: tele 0,48,52,59,53\n14/09/2020 20:33:43: tele 0,49,53,3,2\n14/09/2020 20:33:44: tele 0,49,53,10,15\n14/09/2020 20:33:45: tele 0,49,53,17,26\n14/09/2020 20:33:46: tele 0,49,53,24,37\n14/09/2020 20:33:47: tele 0,49,53,31,48\n14/09/2020 20:33:48: tele 0,49,53,36,62\n14/09/2020 20:33:49: tele 0,49,54,26,1\n14/09/2020 20:33:49: tele 0,49,54,15,2\n14/09/2020 20:33:51: tele 0,49,53,3,58\n14/09/2020 20:33:52: tele 0,48,53,62,55\n14/09/2020 20:33:54: tele 0,48,53,63,57\n14/09/2020 20:33:55: tele 1,48,53,63,57\n14/09/2020 20:33:58: tele 2,48,53,63,57\n14/09/2020 20:34:52: remote 1564\n14/09/2020 20:36:04: remote 1569\n14/09/2020 20:36:15: remote 1576\n14/09/2020 20:37:57: tele 2,48,53,55,47\n14/09/2020 20:37:58: tele 2,48,53,45,38\n14/09/2020 20:37:58: tele 2,48,53,36,28\n14/09/2020 20:38:00: tele 2,48,53,25,18\n14/09/2020 20:38:00: tele 2,48,53,18,6\n14/09/2020 20:38:01: tele 2,48,52,13,57\n14/09/2020 20:38:01: tele 2,48,52,14,43\n14/09/2020 20:38:02: tele 2,48,52,16,29\n14/09/2020 20:38:03: tele 2,48,52,18,15\n14/09/2020 20:38:08: tele 2,48,52,15,2\n14/09/2020 20:38:08: tele 2,48,51,10,53\n14/09/2020 20:38:09: tele 2,48,51,5,40\n14/09/2020 20:38:09: tele 2,48,51,3,26\n14/09/2020 20:38:10: tele 2,47,51,63,13\n14/09/2020 20:38:11: tele 2,47,51,57,1\n14/09/2020 20:38:11: tele 2,47,50,50,53\n14/09/2020 20:38:12: tele 2,47,50,41,42\n14/09/2020 20:38:12: tele 2,47,50,32,31\n14/09/2020 20:38:13: tele 2,47,50,20,22\n14/09/2020 20:38:14: tele 2,47,50,7,13\n14/09/2020 20:38:14: tele 2,46,50,56,8\n14/09/2020 20:38:15: tele 2,46,50,43,2\n14/09/2020 20:38:15: tele 2,46,49,28,61\n14/09/2020 20:38:16: tele 2,46,49,12,60\n14/09/2020 20:38:17: tele 2,46,50,0,0\n14/09/2020 20:38:19: tele 2,45,50,60,15\n14/09/2020 20:38:20: tele 2,45,50,59,24\n14/09/2020 20:38:21: tele 2,46,50,1,33\n14/09/2020 20:38:21: tele 2,46,50,14,38\n14/09/2020 20:38:22: tele 2,46,50,27,37\n14/09/2020 20:38:24: tele 2,46,50,26,29\n14/09/2020 20:38:24: tele 1,46,50,26,29\n14/09/2020 20:38:25: tele 0,46,50,26,29\n14/09/2020 20:38:26: tele 0,46,50,14,17\n14/09/2020 20:38:28: tele 0,46,50,13,15\n14/09/2020 20:39:23: tele 0,46,50,12,15\n14/09/2020 20:39:26: tele 0,46,50,3,26\n14/09/2020 20:39:27: tele 0,45,50,57,31\n14/09/2020 20:39:28: tele 0,45,50,54,22\n14/09/2020 20:39:50: remote 1673\n14/09/2020 20:40:09: remote 1695\n14/09/2020 20:40:42: remote 1710\n14/09/2020 20:41:41: remote 1788\n14/09/2020 20:42:08: remote 1841\n14/09/2020 20:42:17: remote 1765\n14/09/2020 22:36:19: tele 0,49,154,13,50\n14/09/2020 23:50:50: tele 0,48,53,63,55\n14/09/2020 23:50:53: tele 1,48,53,63,55\n14/09/2020 23:50:56: tele 2,48,53,63,55\n14/09/2020 23:51:01: debug\n15/09/2020 00:46:51: anim 25\n15/09/2020 00:47:06: tele 2,49,53,4,55\n15/09/2020 00:47:08: tele 2,49,53,2,55\n15/09/2020 00:47:09: tele 2,49,53,3,55\n15/09/2020 00:47:09: tele 2,49,53,2,55\n15/09/2020 00:47:10: tele 2,49,53,3,55\n15/09/2020 00:47:11: tele 2,49,53,2,55\n15/09/2020 00:47:13: tele 2,49,53,1,54\n15/09/2020 00:49:45: tele 1,49,53,1,54\n15/09/2020 00:49:46: tele 0,49,53,1,54\n15/09/2020 00:50:11: tele 0,49,53,10,36\n15/09/2020 00:50:12: tele 0,49,53,20,22\n15/09/2020 00:50:13: tele 0,49,53,33,34\n15/09/2020 00:50:14: tele 0,49,53,49,40\n15/09/2020 00:50:17: tele 0,49,53,49,44\n15/09/2020 00:52:10: anim 12175\n15/09/2020 00:52:15: anim 12174\n15/09/2020 11:58:07: item 14000\n15/09/2020 11:58:16: item 15005\n15/09/2020 11:58:32: item 17333\n15/09/2020 11:58:38: item 16777\n15/09/2020 11:58:42: item 14000\n15/09/2020 11:58:45: item 14002\n15/09/2020 11:58:48: item 15002\n15/09/2020 11:58:57: item 15009\n15/09/2020 11:59:05: item 16002\n15/09/2020 11:59:08: item 15888\n15/09/2020 11:59:11: item 15444\n15/09/2020 11:59:15: item 15111\n15/09/2020 11:59:31: tele 0,49,53,50,36\n15/09/2020 11:59:44: tele 0,50,53,6,37\n15/09/2020 11:59:45: tele 0,50,53,8,37\n15/09/2020 12:02:31: pnpc 8926\n15/09/2020 12:02:38: pnpc -1\n15/09/2020 12:03:17: pnpc 9040\n15/09/2020 12:03:49: pnpc 9172\n15/09/2020 12:06:31: pnpc 9235\n15/09/2020 12:07:13: pnpc 9278\n15/09/2020 12:07:38: pnpc 9176\n15/09/2020 12:08:39: pnpc 8719\n15/09/2020 12:09:05: pnpc 8696\n15/09/2020 12:09:09: pnpc 8695\n15/09/2020 12:09:17: pnpc 8697\n15/09/2020 12:09:22: pnpc 8697\n15/09/2020 12:09:43: pnpc 8689\n15/09/2020 12:39:59: tele 0,46,50,13,16\n15/09/2020 12:41:10: empty\n15/09/2020 12:41:16: empty\n15/09/2020 12:41:20: itemn rune 2h\n15/09/2020 12:41:25: itemn rune scimitar\n15/09/2020 12:41:42: tele 0,46,50,26,20\n15/09/2020 12:41:43: tele 0,46,50,39,26\n15/09/2020 12:42:29: tele 0,47,49,7,48\n15/09/2020 12:43:45: tele 0,46,49,61,47\n15/09/2020 12:43:47: tele 0,47,49,1,60\n15/09/2020 12:43:48: tele 0,47,50,5,9\n15/09/2020 12:43:49: tele 0,47,50,9,22\n15/09/2020 12:43:49: tele 0,47,50,13,35\n15/09/2020 12:43:50: tele 0,47,50,17,48\n15/09/2020 12:43:51: tele 0,47,50,21,61\n15/09/2020 12:43:52: tele 0,47,51,27,9\n15/09/2020 12:43:53: tele 0,47,51,34,11\n15/09/2020 12:43:54: tele 0,47,51,47,10\n15/09/2020 12:43:55: tele 0,47,51,63,4\n15/09/2020 12:44:01: tele 0,48,50,0,60\n15/09/2020 12:44:03: tele 0,48,50,4,51\n15/09/2020 12:44:11: tele 0,48,50,7,49\n15/09/2020 12:44:25: itemn rune full helmet\n15/09/2020 12:44:30: itemn rune full helm\n15/09/2020 12:44:40: itemn rune platebodyu\n15/09/2020 12:44:44: itemn rune platebody\n15/09/2020 12:44:49: quests\n15/09/2020 12:44:53: allquest\n15/09/2020 12:45:07: itemn rune platelegs\n15/09/2020 12:48:16: tween\n15/09/2020 12:48:25: tween\n15/09/2020 12:48:47: tele 0,46,52,2,51\n15/09/2020 12:48:56: item 995 99999\n15/09/2020 12:50:37: tele 0,46,53,27,1\n15/09/2020 12:50:37: tele 0,46,53,34,13\n15/09/2020 12:50:38: tele 0,46,53,41,25\n15/09/2020 12:50:39: tele 0,46,53,48,37\n15/09/2020 12:50:54: tele 0,39,44,60,28\n15/09/2020 12:51:08: tele 0,39,44,60,25\n15/09/2020 12:51:10: tele 0,39,44,60,25\n15/09/2020 12:51:17: tele 0,39,44,58,30\n15/09/2020 12:51:26: anim 9933\n15/09/2020 12:51:30: anim 13000\n15/09/2020 12:51:32: anim 13005\n15/09/2020 12:51:37: anim 12222\n15/09/2020 12:51:41: anim 16000\n15/09/2020 12:51:44: anim -1\n15/09/2020 12:51:50: anim 14888\n15/09/2020 12:51:54: anim 13888\n15/09/2020 12:51:56: anim 12333\n15/09/2020 12:52:01: anim 12888\n15/09/2020 12:52:06: anim 13111\n15/09/2020 12:52:09: anim 13444\n15/09/2020 12:52:12: anim -1\n15/09/2020 12:52:15: render 1422\n15/09/2020 12:52:19: render 1555\n15/09/2020 12:52:24: render 1344\n15/09/2020 12:52:26: render 1345\n15/09/2020 12:52:30: render 1346\n15/09/2020 12:52:34: render 1347\n15/09/2020 12:52:37: render 1349\n15/09/2020 12:52:42: render 1401\n15/09/2020 12:52:44: render 1405\n15/09/2020 12:52:47: render 1506\n15/09/2020 12:52:51: render 1507\n15/09/2020 12:52:55: render 1407\n15/09/2020 12:52:58: render 1406\n15/09/2020 12:53:34: remote 1666\n15/09/2020 12:53:38: remote 1573\n15/09/2020 12:53:41: remote -1\n15/09/2020 12:53:47: emote -1\n15/09/2020 12:53:48: emote 1\n15/09/2020 12:53:50: anim 1\n15/09/2020 12:53:55: remote 1547\n15/09/2020 12:53:57: remote 2\n15/09/2020 12:54:01: remote 1427\n15/09/2020 12:54:20: remote 1672\n15/09/2020 12:54:26: remote 1396\n15/09/2020 12:54:37: remote 1569\n15/09/2020 12:54:42: remote 1564\n15/09/2020 12:54:47: remote 1545\n15/09/2020 12:54:55: remote 1429\n15/09/2020 12:55:19: remote 1382\n15/09/2020 12:55:27: remote 1383\n15/09/2020 12:55:38: remote 1482\n15/09/2020 12:55:43: remote 1549\n15/09/2020 12:55:46: emote 1549\n15/09/2020 12:55:48: remote 2\n15/09/2020 12:55:51: remote 4\n15/09/2020 12:55:54: remote 12\n15/09/2020 12:55:56: remote 15\n15/09/2020 12:55:59: remote 18\n15/09/2020 12:56:01: remote 333\n15/09/2020 12:56:04: remote -1\n15/09/2020 12:56:08: remote 899\n15/09/2020 12:56:34: remote 871\n15/09/2020 12:56:48: remote 1652\n15/09/2020 12:57:00: remote 2003\n15/09/2020 12:57:11: remote 2005\n15/09/2020 12:57:28: remote 2019\n15/09/2020 12:57:35: pnpc 2\n15/09/2020 12:57:46: remote 2043\n15/09/2020 12:57:50: remote -1\n15/09/2020 12:57:53: pnpc -1\n15/09/2020 12:58:10: remote 1094\n15/09/2020 12:58:16: remote 1095\n15/09/2020 12:58:30: remote 1114\n15/09/2020 12:58:37: remote 1121\n15/09/2020 12:58:47: remote 1124\n15/09/2020 12:59:10: remote 1171\n15/09/2020 13:00:00: remote 1485\n15/09/2020 13:00:04: remote 1222\n15/09/2020 13:00:14: remote 1386\n15/09/2020 13:00:38: pnpc -1\n15/09/2020 13:01:03: itemn saradomin godsword\n15/09/2020 13:01:27: tele 0,50,50,22,19\n15/09/2020 13:02:12: remote 78\n15/09/2020 13:02:22: remote 124\n15/09/2020 13:02:31: remote 220\n15/09/2020 13:02:40: remote 326\n15/09/2020 13:02:50: remote 2052\n15/09/2020 13:02:57: remote 366\n15/09/2020 13:03:13: remote 124\n15/09/2020 13:03:19: remote 152\n15/09/2020 13:03:34: remote 79\n15/09/2020 13:04:01: remote 40\n15/09/2020 13:04:23: tele 0,48,54,21,15\n15/09/2020 13:04:31: remote 39\n15/09/2020 13:04:35: remote 31\n15/09/2020 13:04:42: remote 28\n15/09/2020 13:04:45: remote 26\n15/09/2020 13:04:47: remote 28\n15/09/2020 13:04:59: tele 0,48,154,33,53\n15/09/2020 13:05:00: tele 0,48,154,47,59\n15/09/2020 13:05:00: tele 0,48,154,62,56\n15/09/2020 13:05:01: tele 0,49,154,10,51\n15/09/2020 13:05:22: tele 0,50,50,22,17\n15/09/2020 13:05:29: itemn zamorakian spear\n15/09/2020 13:06:04: itemn dragon platebody\n15/09/2020 13:06:14: itemn attack cape\n15/09/2020 13:06:25: itemn fury\n15/09/2020 13:06:29: itemn amulet of fury\n15/09/2020 13:06:36: itemn dragon full helm\n15/09/2020 13:06:44: itemn dragon platelegs\n15/09/2020 13:06:53: itemn dragon gauntlets\n15/09/2020 13:07:00: itemn dragon boots\n15/09/2020 13:07:29: empty\n15/09/2020 13:07:33: itemn red partyhat\n15/09/2020 13:07:59: itemn dragon chainbody\n15/09/2020 13:31:24: tele 0,50,50,21,14\n15/09/2020 13:31:37: tele 0,46,61,60,29\n15/09/2020 13:35:40: tele 0,48,53,12,25\n15/09/2020 13:35:46: itemn rune pickaxe\n15/09/2020 13:36:13: tele 0,48,53,14,28\n15/09/2020 13:47:55: itemn dragon scimitar\n15/09/2020 13:48:02: tele 0,56,54,19,32\n17/09/2020 14:46:11: tele 0,41,51,24,27\n17/09/2020 14:46:18: tele 0,44,49,38,27\n17/09/2020 14:46:26: tele 0,50,50,23,18\n17/09/2020 14:46:39: tele 0,50,50,24,37\n17/09/2020 14:47:17: tele 0,50,48,60,34\n17/09/2020 14:47:22: tele 0,50,50,23,17\n17/09/2020 14:52:10: tele 0,46,51,37,13\n17/09/2020 14:52:16: tele 0,50,50,22,17\n17/09/2020 14:52:43: tele 0,46,50,12,15\n17/09/2020 14:53:20: tele 0,44,50,20,35\n17/09/2020 14:53:35: tele 0,43,45,24,13\n17/09/2020 14:54:00: tele 0,50,50,23,19\n17/09/2020 14:54:05: tele 0,50,50,36,25\n17/09/2020 14:55:14: tele 0,50,50,24,20\n17/09/2020 14:55:16: tele 0,50,50,13,31\n17/09/2020 14:55:17: tele 0,49,50,62,38\n17/09/2020 14:55:19: tele 0,49,50,53,38\n17/09/2020 14:55:21: tele 0,49,50,47,32\n17/09/2020 14:55:24: tele 0,49,50,62,31\n17/09/2020 14:55:25: tele 0,50,50,13,26\n17/09/2020 14:55:26: tele 0,50,50,27,26\n17/09/2020 14:55:28: tele 0,50,50,31,24\n17/09/2020 14:55:33: tele 0,50,50,46,27\n17/09/2020 14:55:33: tele 0,50,50,60,35\n17/09/2020 14:55:35: tele 0,50,50,63,32\n17/09/2020 14:55:36: tele 0,51,50,1,34\n17/09/2020 14:55:38: tele 0,51,50,0,42\n17/09/2020 14:55:44: tele 0,50,50,52,34\n17/09/2020 14:55:45: tele 0,50,50,40,26\n17/09/2020 14:55:46: tele 0,50,50,32,20\n17/09/2020 14:55:47: tele 0,50,50,21,18\n17/09/2020 14:56:56: tele 0,51,48,33,26\n17/09/2020 14:59:28: tele 0,50,50,22,18\n17/09/2020 15:17:37: tele 0,50,50,9,10\n17/09/2020 15:17:44: tele 2,50,50,8,20\n17/09/2020 15:19:24: debug\n17/09/2020 18:26:56: tele 0,51,50,16,16\n17/09/2020 19:34:00: itemn brawling gloves\n17/09/2020 19:34:07: itemn brawling gloves (melee)\n17/09/2020 19:34:19: empty\n17/09/2020 19:34:27: itemn bandos chest plate\n17/09/2020 19:34:31: itemn bandos chestplate\n17/09/2020 19:34:38: itemn bandos tassets\n17/09/2020 19:34:45: itemn bandos boots\n17/09/2020 19:34:54: itemn dragonfire shield\n17/09/2020 19:35:04: itemn abyssal whip\n17/09/2020 19:35:38: itemn third-age full helm\n17/09/2020 19:35:50: itemn verac\'s helm\n17/09/2020 19:36:02: itemn fire cape\n17/09/2020 19:36:14: tele 0,51,61,22,22\n17/09/2020 19:36:35: tele 0,51,61,18,19\n17/09/2020 19:37:05: debug\n17/09/2020 19:37:37: god\n17/09/2020 19:37:39: hp\n17/09/2020 19:37:41: 1hko\n17/09/2020 19:37:53: tele 0,51,61,10,22\n17/09/2020 20:12:12: remtoe 211\n17/09/2020 20:12:16: remote 211\n17/09/2020 20:38:04: tele 0,41,63,15,7\n17/09/2020 20:38:08: tele 1,41,63,15,7\n17/09/2020 20:38:08: tele 2,41,63,15,7\n17/09/2020 20:38:09: tele 1,41,63,15,7\n17/09/2020 20:39:30: tele 1,41,63,16,12\n17/09/2020 20:40:54: pnpc 7895\n17/09/2020 20:40:58: pnpc 7896\n17/09/2020 20:41:02: pnpc 7897\n17/09/2020 20:41:07: pnpc 8093\n17/09/2020 20:41:12: pnpc 8095\n17/09/2020 20:41:17: pnpc 8094\n17/09/2020 20:41:22: pnpc 8097\n17/09/2020 20:41:26: pnpc 8098\n17/09/2020 20:41:30: pnpc 8099\n17/09/2020 20:41:34: pnpc -1\n17/09/2020 20:41:38: pnpc 8100\n17/09/2020 20:41:42: pnpc 8882\n17/09/2020 20:41:46: pnpc 8690\n17/09/2020 20:41:52: pnpc 8663\n17/09/2020 20:42:18: pnpc -1\n17/09/2020 20:42:55: tele 0,41,63,12,22\n17/09/2020 20:42:57: tele 0,41,63,11,23\n17/09/2020 20:43:39: tele 0,41,63,6,25\n17/09/2020 20:43:39: tele 1,41,63,6,25\n17/09/2020 20:43:44: tele 1,41,63,7,25\n17/09/2020 20:43:50: tele 1,41,63,7,27\n17/09/2020 20:43:52: tele 1,41,63,9,27\n17/09/2020 20:43:54: tele 1,41,63,11,27\n17/09/2020 20:43:56: tele 1,41,63,11,29\n17/09/2020 20:43:57: tele 1,41,63,11,31\n17/09/2020 20:43:58: tele 1,41,63,12,33\n17/09/2020 21:03:44: tele 0,50,50,30,31\n17/09/2020 21:03:46: tele 0,50,50,23,20\n', '', '', '', ''), -('woahscam', '', '', '', '22/09/2020 12:43:45: 127.0.0.1\n22/09/2020 12:43:45: I116264190\n22/09/2020 12:43:45: 30-9C-23-87-89-8E\n', '', '', '', '', ''); - --- -------------------------------------------------------- - --- --- Table structure for table `punishments` --- - -CREATE TABLE `punishments` ( - `address` varchar(100) NOT NULL DEFAULT '', - `type` int(11) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Table structure for table `security` --- - -CREATE TABLE `security` ( - `id` int(11) UNSIGNED NOT NULL, - `ip` longtext NOT NULL, - `type` int(11) NOT NULL, - `timestamp` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Table structure for table `sys_logs` --- - -CREATE TABLE `sys_logs` ( - `id` int(11) UNSIGNED NOT NULL, - `message` longtext NOT NULL, - `log_type` int(2) DEFAULT NULL, - `timestamp` timestamp NOT NULL DEFAULT current_timestamp(), - `IP_ADDRESS` varchar(30) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Table structure for table `validations` --- - -CREATE TABLE `validations` ( - `id` int(11) UNSIGNED NOT NULL, - `username` varchar(20) DEFAULT NULL, - `code` varchar(30) DEFAULT NULL, - `type` int(2) NOT NULL DEFAULT 0, - `timestamp` timestamp NOT NULL DEFAULT current_timestamp(), - `value` longtext NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Table structure for table `votes` --- - -CREATE TABLE `votes` ( - `id` int(11) NOT NULL, - `username` varchar(50) NOT NULL DEFAULT '', - `site` varchar(50) NOT NULL, - `timestamp` timestamp NOT NULL DEFAULT current_timestamp() -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Table structure for table `voting_sites` --- - -CREATE TABLE `voting_sites` ( - `name` varchar(20) NOT NULL DEFAULT 'Null', - `wait` int(5) NOT NULL DEFAULT 12, - `credits` int(2) NOT NULL DEFAULT 1, - `link` varchar(500) NOT NULL DEFAULT 'http://ariosrsps.com', - `get_command` varchar(20) NOT NULL DEFAULT '', - `host_name` varchar(500) NOT NULL DEFAULT '' -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - -- -- Table structure for table `worlds` -- @@ -348,72 +100,12 @@ INSERT INTO `worlds` (`world`, `ip`, `players`, `country`, `member`, `revision`, -- Indexes for dumped tables -- --- --- Indexes for table `dev_log` --- -ALTER TABLE `dev_log` - ADD PRIMARY KEY (`id`); - --- --- Indexes for table `highscores` --- -ALTER TABLE `highscores` - ADD PRIMARY KEY (`id`); - -- -- Indexes for table `members` -- ALTER TABLE `members` ADD PRIMARY KEY (`UID`); --- --- Indexes for table `messages` --- -ALTER TABLE `messages` - ADD PRIMARY KEY (`id`); - --- --- Indexes for table `perks` --- -ALTER TABLE `perks` - ADD PRIMARY KEY (`product_id`); - --- --- Indexes for table `player_logs` --- -ALTER TABLE `player_logs` - ADD PRIMARY KEY (`username`); - --- --- Indexes for table `security` --- -ALTER TABLE `security` - ADD PRIMARY KEY (`id`); - --- --- Indexes for table `sys_logs` --- -ALTER TABLE `sys_logs` - ADD PRIMARY KEY (`id`); - --- --- Indexes for table `validations` --- -ALTER TABLE `validations` - ADD PRIMARY KEY (`id`); - --- --- Indexes for table `votes` --- -ALTER TABLE `votes` - ADD KEY `id` (`id`); - --- --- Indexes for table `voting_sites` --- -ALTER TABLE `voting_sites` - ADD PRIMARY KEY (`name`); - -- -- Indexes for table `worlds` -- @@ -424,61 +116,12 @@ ALTER TABLE `worlds` -- AUTO_INCREMENT for dumped tables -- --- --- AUTO_INCREMENT for table `dev_log` --- -ALTER TABLE `dev_log` - MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=34; - --- --- AUTO_INCREMENT for table `highscores` --- -ALTER TABLE `highscores` - MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=12; - -- -- AUTO_INCREMENT for table `members` -- ALTER TABLE `members` MODIFY `UID` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; --- --- AUTO_INCREMENT for table `messages` --- -ALTER TABLE `messages` - MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT; - --- --- AUTO_INCREMENT for table `perks` --- -ALTER TABLE `perks` - MODIFY `product_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=74; - --- --- AUTO_INCREMENT for table `security` --- -ALTER TABLE `security` - MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT; - --- --- AUTO_INCREMENT for table `sys_logs` --- -ALTER TABLE `sys_logs` - MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT; - --- --- AUTO_INCREMENT for table `validations` --- -ALTER TABLE `validations` - MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT; - --- --- AUTO_INCREMENT for table `votes` --- -ALTER TABLE `votes` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; -COMMIT; - /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 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 new file mode 100644 index 000000000..706aec911 --- /dev/null +++ b/Server/detekt.yml @@ -0,0 +1,713 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: false + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + # - 'MdOutputReport' + +comments: + active: true + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: false + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + DeprecatedBlockTag: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + OutdatedDocumentation: + active: false + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: false + UndocumentedPublicClass: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + UndocumentedPublicFunction: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UndocumentedPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + +complexity: + active: true + ComplexCondition: + active: false + threshold: 4 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + ComplexMethod: + active: false + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' + LabeledExpression: + active: false + ignoredLabels: [] + LargeClass: + active: false + threshold: 600 + LongMethod: + active: false + threshold: 60 + LongParameterList: + active: false + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] + MethodOverloading: + active: false + threshold: 6 + NamedArguments: + active: false + threshold: 3 + ignoreArgumentsMatchingNames: false + NestedBlockDepth: + active: false + threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + +coroutines: + active: true + GlobalCoroutineUsage: + active: false + InjectDispatcher: + active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunWithCoroutineScopeReceiver: + active: false + SuspendFunWithFlowReturnType: + active: true + +empty-blocks: + active: false + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: true + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' + InstanceOfCheckForException: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + NotImplementedDeclaration: + active: false + ObjectExtendsThrowable: + active: false + PrintStackTrace: + active: false + RethrowCaughtException: + active: true + ReturnFromFinally: + active: true + ignoreLabeled: false + SwallowedException: + active: false + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: true + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + ThrowingNewInstanceOfSameException: + active: true + TooGenericExceptionCaught: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptionNames: + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: false + exceptionNames: + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' + +naming: + active: true + BooleanPropertyNaming: + active: false + allowedPattern: '^(is|has|are)' + ignoreOverridden: true + ClassNaming: + active: false + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + forbiddenName: [] + FunctionMaxLength: + active: false + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + minimumFunctionNameLength: 3 + FunctionNaming: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '[a-z][a-zA-Z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + FunctionParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + InvalidPackageDeclaration: + active: false + rootPackage: '' + requireRootInDeclaration: false + LambdaParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*|_' + MatchingDeclarationName: + active: false + mustBeFirst: true + MemberNameEqualsClassName: + active: false + ignoreOverridden: true + NoNameShadowing: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false + ObjectPropertyNaming: + active: true + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: false + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: false + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + maximumVariableNameLength: 64 + VariableMinLength: + active: false + minimumVariableNameLength: 1 + VariableNaming: + active: false + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + +performance: + active: true + ArrayPrimitive: + active: false + CouldBeSequence: + active: false + threshold: 3 + ForEachOnRange: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + SpreadOperator: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + AvoidReferentialEquality: + active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastToNullableType: + active: false + Deprecation: + active: false + DontDowncastCollectionTypes: + active: false + DoubleMutabilityForCollection: + active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' + DuplicateCaseInWhenExpression: + active: true + ElseCaseInsteadOfExhaustiveWhen: + active: false + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExitOutsideMain: + active: false + ExplicitGarbageCollectionCall: + active: false + HasPlatformType: + active: true + IgnoredReturnValue: + active: true + restrictToAnnotatedMethods: true + returnValueAnnotations: + - '*.CheckResult' + - '*.CheckReturnValue' + - 'com.google.errorprone.annotations.CheckReturnValue' + ignoreReturnValueAnnotations: + - '*.CanIgnoreReturnValue' + ignoreFunctionCall: [] + ImplicitDefaultLocale: + active: false + ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: + active: false + excludes: ['**/*.kts'] + MissingWhenCase: + active: true + allowElseExpression: true + NullCheckOnMutableProperty: + active: false + NullableToStringCall: + active: false + RedundantElseInWhen: + active: true + UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: true + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UnsafeCast: + active: true + UnusedUnaryOperator: + active: true + UselessPostfixExpression: + active: false + WrongEqualsTypeParameter: + active: true + +style: + active: true + CanBeNonNullable: + active: false + CascadingCallWrapping: + active: false + includeElvis: true + ClassOrdering: + active: false + CollapsibleIfStatements: + active: false + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: 'to' + DataClassShouldBeImmutable: + active: false + DestructuringDeclarationWithTooManyEntries: + active: true + maxDestructuringEntries: 3 + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: false + ExplicitCollectionElementAccessMethod: + active: false + ExplicitItLambdaParameter: + active: false + ExpressionBodySyntax: + active: false + includeLineWrapping: false + ForbiddenComment: + active: false + values: + - 'FIXME:' + - 'STOPSHIP:' + - 'TODO:' + allowedPatterns: '' + customMessage: '' + ForbiddenImport: + active: false + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: false + methods: + - 'kotlin.io.print' + - 'kotlin.io.println' + ForbiddenPublicDataClass: + active: true + excludes: ['**'] + ignorePackages: + - '*.internal' + - '*.internal.*' + ForbiddenSuppress: + active: false + rules: [] + ForbiddenVoid: + active: true + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: false + ignoreOverridableFunction: true + ignoreActualFunction: true + excludedFunctions: '' + LibraryCodeMustSpecifyReturnType: + active: true + excludes: ['**'] + LibraryEntitiesShouldNotBePublic: + active: true + excludes: ['**'] + LoopWithTooManyJumpStatements: + active: false + maxJumpCount: 1 + MagicNumber: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] + ignoreNumbers: + - '-1' + - '0' + - '1' + - '2' + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + ignoreExtensionFunctions: true + MandatoryBracesIfStatements: + active: false + MandatoryBracesLoops: + active: false + MaxChainedCallsOnSameLine: + active: false + maxChainedCalls: 5 + MaxLineLength: + active: false + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + active: false + ModifierOrder: + active: false + MultilineLambdaItParameter: + active: false + NestedClassesVisibility: + active: true + NewLineAtEndOfFile: + active: false + NoTabs: + active: false + NullableBooleanCheck: + active: false + ObjectLiteralToLambda: + active: true + OptionalAbstractKeyword: + active: false + OptionalUnit: + active: false + OptionalWhenBraces: + active: false + PreferToOverPairSyntax: + active: false + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: false + RedundantHigherOrderMapUsage: + active: true + RedundantVisibilityModifierRule: + active: false + ReturnCount: + active: false + max: 2 + excludedFunctions: 'equals' + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: true + SpacingBetweenPackageAndImports: + active: false + ThrowsCount: + active: false + max: 2 + excludeGuardClauses: false + TrailingWhitespace: + active: false + UnderscoresInNumericLiterals: + active: false + acceptableLength: 4 + allowNonStandardGrouping: false + UnnecessaryAbstractClass: + active: true + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: true + UnnecessaryBackticks: + active: false + UnnecessaryFilter: + active: true + UnnecessaryInheritance: + active: true + UnnecessaryInnerClass: + active: false + UnnecessaryLet: + active: false + UnnecessaryParentheses: + active: false + UntilInsteadOfRangeTo: + active: false + UnusedImports: + active: false + UnusedPrivateClass: + active: false + UnusedPrivateMember: + active: false + allowedNames: '(_|ignored|expected|serialVersionUID)' + UseAnyOrNoneInsteadOfFind: + active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: true + UseCheckOrError: + active: false + UseDataClass: + active: false + allowVars: false + UseEmptyCounterpart: + active: false + UseIfEmptyOrIfBlank: + active: false + UseIfInsteadOfWhen: + active: false + UseIsNullOrEmpty: + active: true + UseOrEmpty: + active: true + UseRequire: + active: false + UseRequireNotNull: + active: false + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: false + VarCouldBeVal: + active: true + ignoreLateinitVar: false + WildcardImport: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + excludeImports: + - 'java.util.*' diff --git a/Server/libs/ConstLib-1.3.jar b/Server/libs/ConstLib-1.3.jar deleted file mode 100644 index c46ffc6e9..000000000 Binary files a/Server/libs/ConstLib-1.3.jar and /dev/null differ diff --git a/Server/libs/ConstLib-1.4.jar b/Server/libs/ConstLib-1.4.jar new file mode 100644 index 000000000..0e948625a Binary files /dev/null and b/Server/libs/ConstLib-1.4.jar differ diff --git a/Server/libs/classgraph-4.8.98.jar b/Server/libs/classgraph-4.8.98.jar deleted file mode 100644 index 240e6daca..000000000 Binary files a/Server/libs/classgraph-4.8.98.jar and /dev/null differ diff --git a/Server/libs/colormath-jvm-2.0.0.jar b/Server/libs/colormath-jvm-2.0.0.jar deleted file mode 100644 index d66ecc3f9..000000000 Binary files a/Server/libs/colormath-jvm-2.0.0.jar and /dev/null differ diff --git a/Server/libs/json-simple-1.1.1.jar b/Server/libs/json-simple-1.1.1.jar deleted file mode 100644 index dfd5856d0..000000000 Binary files a/Server/libs/json-simple-1.1.1.jar and /dev/null differ diff --git a/Server/libs/kotlin-reflect-1.5.20.jar b/Server/libs/kotlin-reflect-1.5.20.jar deleted file mode 100644 index 1d16a63cb..000000000 Binary files a/Server/libs/kotlin-reflect-1.5.20.jar and /dev/null differ diff --git a/Server/libs/markdown-jvm-0.2.4.jar b/Server/libs/markdown-jvm-0.2.4.jar deleted file mode 100644 index 19cbfcfd9..000000000 Binary files a/Server/libs/markdown-jvm-0.2.4.jar and /dev/null differ diff --git a/Server/libs/mordant-jvm-2.0.0-alpha2.jar b/Server/libs/mordant-jvm-2.0.0-alpha2.jar deleted file mode 100644 index 7f15957db..000000000 Binary files a/Server/libs/mordant-jvm-2.0.0-alpha2.jar and /dev/null differ diff --git a/Server/libs/mysql-connector-java-8.0.21.jar b/Server/libs/mysql-connector-java-8.0.21.jar deleted file mode 100644 index 51e270c46..000000000 Binary files a/Server/libs/mysql-connector-java-8.0.21.jar and /dev/null differ diff --git a/Server/mvnw b/Server/mvnw new file mode 100755 index 000000000..b7f064624 --- /dev/null +++ b/Server/mvnw @@ -0,0 +1,287 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.1.1 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + printf '%s' "$(cd "$basedir"; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname $0)") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $wrapperUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + QUIET="--quiet" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + elif command -v curl > /dev/null; then + QUIET="--silent" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=`cygpath --path --windows "$javaSource"` + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Server/mvnw.cmd b/Server/mvnw.cmd new file mode 100644 index 000000000..474c9d6b7 --- /dev/null +++ b/Server/mvnw.cmd @@ -0,0 +1,187 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.1.1 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/Server/pom.xml b/Server/pom.xml new file mode 100644 index 000000000..5c93c44d2 --- /dev/null +++ b/Server/pom.xml @@ -0,0 +1,337 @@ + + + 4.0.0 + org.rs09 + server + 1.0.0 + + core.Server + UTF-8 + true + 1.8.20 + 11 + 5.7.0 + 11 + 11 + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + compile + + + com.googlecode.json-simple + json-simple + 1.1.1 + compile + + + org.jetbrains.kotlin + kotlin-reflect + 1.5.20 + compile + + + org.jetbrains.kotlinx + kotlinx-coroutines-core-jvm + 1.4.2 + compile + + + com.moandjiezana.toml + toml4j + 0.7.2 + compile + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.rs09.consts + ConstLib + [1.4.0,) + compile + + + mysql + mysql-connector-java + 8.0.29 + + + io.github.classgraph + classgraph + 4.8.146 + + + org.xerial + sqlite-jdbc + 3.36.0.3 + compile + + + com.github.ajalt.mordant + mordant-jvm + 2.0.0-beta6 + compile + + + com.google.errorprone + error_prone_annotations + 2.9.0 + compile + + + org.java-websocket + Java-WebSocket + 1.5.7 + + + + + src/main + src/test + + + + + org.apache.maven.plugins + maven-install-plugin + 3.0.1 + + + install-consts + clean + + ${project.basedir}/libs/ConstLib-1.4.jar + default + org.rs09 + consts + 1.4 + jar + true + + + install-file + + + + install-extends + clean + + ${project.basedir}/libs/PrimitiveExtensions-1.0.jar + default + org.rs09 + primextends + 1.0 + jar + true + + + install-file + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.4.2 + + + + jar-with-dependencies + + + ${project.mainClassName} + + + + + + make-assembly + package + + single + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + + compile + + + + ${project.basedir}/src/main + + + + + test-compile + test-compile + + + ${project.basedir}/src/test/kotlin + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + 2048m + false + + -XDcompilePolicy=simple + + + + + com.google.errorprone + error_prone_core + 2.9.0 + + + + + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + + + maven-surefire-plugin + + false + false + + 2.22.0 + + + maven-failsafe-plugin + 2.22.0 + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + core.Server + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + + detekt + verify + + + + + + + + + + + + + + + + run + + + + + io.gitlab.arturbosch.detekt + detekt-cli + 1.21.0 + + + + + + + + maven_central + Maven Central + https://repo.maven.apache.org/maven2/ + + + gitlab-maven + https://gitlab.com/api/v4/projects/32237206/packages/maven + + + + + gitlab-maven + https://gitlab.com/api/v4/projects/32237206/packages/maven + + + + gitlab-maven + https://gitlab.com/api/v4/projects/32237206/packages/maven + + + diff --git a/Server/scripts/dialogue/anna_isaakson.asc b/Server/scripts/dialogue/anna_isaakson.asc deleted file mode 100644 index 392b58148..000000000 --- a/Server/scripts/dialogue/anna_isaakson.asc +++ /dev/null @@ -1,5 +0,0 @@ -@dialogue npc:5512 - npc("Hello visitor, how are you?") - player("Better than expected. Its a lot...nicer...here than I was", "expecting. Everyone seems pretty happy.") - npc("Of course, the Burgher is strong and wise, and looks", "after us well") - player("I think some of those Jatizso citizens have got", "the wrong idea about this place.") \ No newline at end of file diff --git a/Server/scripts/dialogue/ardougne_baker.asc b/Server/scripts/dialogue/ardougne_baker.asc deleted file mode 100644 index 8ac900b31..000000000 --- a/Server/scripts/dialogue/ardougne_baker.asc +++ /dev/null @@ -1,10 +0,0 @@ -@dialogue npc:571 - -npc("Good day, monsieur. Would you like ze nice", "freshly-baked bread? Or perhaps a nice piece of cake?") -option("Let's see what you have.", "No, thank you.") -@option1 { - openshop(571) -} -@option2 { - player("No, thank you.") -} \ No newline at end of file diff --git a/Server/scripts/dialogue/bandit.asc b/Server/scripts/dialogue/bandit.asc deleted file mode 100644 index bcf67804b..000000000 --- a/Server/scripts/dialogue/bandit.asc +++ /dev/null @@ -1,2 +0,0 @@ -@dialogue npc:1926, 1925, 1927, 1928, 1929, 1930, 1931 -npc("Get out of this village.", "You are not welcome here."); \ No newline at end of file diff --git a/Server/scripts/dialogue/bandit_shopkeeper.asc b/Server/scripts/dialogue/bandit_shopkeeper.asc deleted file mode 100644 index 14a3f7795..000000000 --- a/Server/scripts/dialogue/bandit_shopkeeper.asc +++ /dev/null @@ -1,15 +0,0 @@ -@dialogue npc:1917 -player("Hello.") -npc("Stuff for sale.", "You buying?") ->options - -options: { - option("Yes", "No") - @option1{ - openshop(1917) - } - @option2{ - npc("No?", "'Bye then.") - } - -} \ No newline at end of file diff --git a/Server/scripts/dialogue/centaur.asc b/Server/scripts/dialogue/centaur.asc deleted file mode 100644 index 08374f6e5..000000000 --- a/Server/scripts/dialogue/centaur.asc +++ /dev/null @@ -1,2 +0,0 @@ -@dialogue npc:4439,4438 -npc("Welcome to the land of extreme donators."); \ No newline at end of file diff --git a/Server/scripts/dialogue/fridleif_shieldson.asc b/Server/scripts/dialogue/fridleif_shieldson.asc deleted file mode 100644 index 561f3f4ae..000000000 --- a/Server/scripts/dialogue/fridleif_shieldson.asc +++ /dev/null @@ -1,4 +0,0 @@ -@dialogue npc:5505 - - - npc("Congratulations, Champion Larlim Far-strider. Thank you", "for defeating the Troll King.") \ No newline at end of file diff --git a/Server/scripts/dialogue/ghost_guard.asc b/Server/scripts/dialogue/ghost_guard.asc deleted file mode 100644 index f4cc09d09..000000000 --- a/Server/scripts/dialogue/ghost_guard.asc +++ /dev/null @@ -1,15 +0,0 @@ -@dialogue npc:1706 -npc("All visitors to Port Phasmatys must pay a toll charge of", "2 Ectotokens.") ->options - -options: { - option("I don't have that many Ectotokens.", "Where can I get Ecototokens?") - @option 1 { - player("I don't have that many Ectotokens.") - npc("In that case, you need to go to the Temple and earn", "some. Talk to the disciples - they will tell you how.") - } - @option 2 { - player("Where can I get my Ectotokens?") - npc("You need to go to the Temple and earn some.", "Talk to the disciples - they will tell you how.") - } -} \ No newline at end of file diff --git a/Server/scripts/dialogue/ghosty_dialogue.asc b/Server/scripts/dialogue/ghosty_dialogue.asc deleted file mode 100644 index 2d621ea48..000000000 --- a/Server/scripts/dialogue/ghosty_dialogue.asc +++ /dev/null @@ -1,3 +0,0 @@ -@dialogue type:"ghosty dialogue": -npc("Woooo wooo wooooo woooo") -plainmessage("You cannot understand the ghost.") \ No newline at end of file diff --git a/Server/scripts/dialogue/gnometraveller.asc b/Server/scripts/dialogue/gnometraveller.asc deleted file mode 100644 index dd931f897..000000000 --- a/Server/scripts/dialogue/gnometraveller.asc +++ /dev/null @@ -1,6 +0,0 @@ -@dialogue npc:2138 -npc(FacialExpression.OLD_NORMAL, "Hello I am a gnome traveller visiting Keldagrim in search", "of low-level skilling supplies.", "I'll offer you an increased buying price,", "higher than any other merchants here can match."); -npc(FacialExpression.OLD_NORMAL, "If you happen to have any, use them on me and I will", "give you a reasonable amount of gold coins."); -player("What items, specifically, do you buy?"); -npc(FacialExpression.OLD_NORMAL, "Logs, Oak logs, Willow logs, Clay, Copper ore, Tin ore,", "Iron ore, Shrimps, Sardines, Anchovies, Trout,", "Guam leaves, Tarromin leaves, Harralander leaves", "Bronze bars, Iron bars, and Arrow shafts..."); -npc(FacialExpression.OLD_NORMAL, "... are some of the items I am looking for.", "When it comes to fish, I will pay more if it's cooked.", "I also accept clean or grimy herbs.", "I am also accepting items in regular or bank note form."); diff --git a/Server/scripts/dialogue/gunnar_holdstrom.asc b/Server/scripts/dialogue/gunnar_holdstrom.asc deleted file mode 100644 index 15ce77c01..000000000 --- a/Server/scripts/dialogue/gunnar_holdstrom.asc +++ /dev/null @@ -1,7 +0,0 @@ -@dialogue npc:5511 - - - npc("Ah, isn't it a lovely day?") - player("It's not bad. What puts you in such a good mood?") - npc("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.") - player("Bracing? Hmmm. I think I might have chosen a different word.") \ No newline at end of file diff --git a/Server/scripts/dialogue/gunnjorn.asc b/Server/scripts/dialogue/gunnjorn.asc deleted file mode 100644 index 3029b91e8..000000000 --- a/Server/scripts/dialogue/gunnjorn.asc +++ /dev/null @@ -1,3 +0,0 @@ -@dialogue npc:607 -npc("Haha welcome to my obstacle course. Have fun, but", "remember this isn't a child's playground. People have", "died here."); -npc("The best way to train, is to go round the course in a", "clockwise direction.") diff --git a/Server/scripts/dialogue/irksol.asc b/Server/scripts/dialogue/irksol.asc deleted file mode 100644 index 7083a6350..000000000 --- a/Server/scripts/dialogue/irksol.asc +++ /dev/null @@ -1,17 +0,0 @@ -@dialogue npc:566 -npc("Selling ruby rings! The best deals on rings in over", "twenty four hundred planes of existence!") ->options - -options: { - option("I'm interested in these deals.", "No thanks, just browsing.") - @option 1 { - player("I'm interested in these deals.") - npc("Aha! A connoisseur! Check out these beauties!") - openshop(566) - } - @option 2 { - player("No thanks, just browsing.") - npc("Fair enough.") - - } -} \ No newline at end of file diff --git a/Server/scripts/dialogue/jimmy_wg.asc b/Server/scripts/dialogue/jimmy_wg.asc deleted file mode 100644 index 3a18ffb4a..000000000 --- a/Server/scripts/dialogue/jimmy_wg.asc +++ /dev/null @@ -1,41 +0,0 @@ -@dialogue npc:4298 -npc("'Ello there."); ->options - -options: { - option("Tell me about this room.", "Tell me how to balance kegs.", "May I claim my tokens please?", "Bye!"); - @option1 { - player("Tell me about this room?"); - npc("Well... s'like thish..."); - npc("Thish here'sh a shtore room right?"); - player("A store room you mean?"); - npc("That'sh what I said! *HIC* A shtore room..... Now", "technic'ly shpeaking, I should be outshide guarding it..."); - player("But you just nipped in to have a quick drink?"); - npc("Yep... and to practish."); - player("Practish? I mean.. practise what?"); - npc("Keg balancin. I'm the besht."); - >options - } - @option2 { - player("Tell me how to balance kegs?"); - npc("Yer very very shtrange. But.... you pick the keg up,", "and balance it on yer head, then you pick another keg", "up and put that on top. S'really very eashy."); - player("Eashy?"); - npc("Yesh. Eashy."); - pause(2) - npc("But you couldn't ever balansh ash many ash meee!"); - player("That sounds like a challenge, I'll show you!"); - >options - } - @option3 { - player("May I claim my tokens please?"); - npc("Well... err.. ish not offishal or anyfin... but I got the", "ledger of tokensh 'ere. I'll jus' err.. write it in!"); - player("Won't they know?"); - npc("Nah... hic.... I'm a wizsh at copyin' signaturesh. Jus'", "ashk an offishal mem'er of shtaff like Shloane fer yer", "tokensh."); - plainmessage("The rather drunk Jimmy scribbles the tokens you've earned from", "Keg Balancing in the Ledger so that you can claim them from an", "official member of training staff."); - } - @option4 { - player("Bye!"); - npc("Shure you wouldn't like an ickle drinkie fore yer go?"); - player("No thanks, got things to do, people to see, tokens to", "earn..."); - } -} diff --git a/Server/scripts/dialogue/jofridr_moedstatter.asc b/Server/scripts/dialogue/jofridr_moedstatter.asc deleted file mode 100644 index 81f3e069d..000000000 --- a/Server/scripts/dialogue/jofridr_moedstatter.asc +++ /dev/null @@ -1,28 +0,0 @@ -@dialogue npc:5509 - -npc("Hello there. Would you like to see the good I have", "for sale?") ->options -options: { -option("Yes please, Jofridr", "No thank you, Jofridr", "Why do you have so much wool in your store?) -@option1 { -player("Yes please, Jofridr.") -openshop(5509) -} - -@option2 { - player("No thank you, Jofridr") - npc("Fair thee well.") -} -@option3 { - player("Why do you have so much wool in", "your store?") - npc("Ah, I have contacts on the mainland. I have a", "sailor friend who brings me crates of", "wool on a regular basis.") - player("What do you trade for it?") - npc("Rope of course! What else can we sell? Fish", "would go off before it got so far south.") - player("Where does all this rope go?") - npc("Err, I don't remember the name of the place", "very well. Dreinna? Drennor? Something like that.") - player("That's very interesting. Thanks Jofridr.") - } - } - - - \ No newline at end of file diff --git a/Server/scripts/dialogue/jukat.asc b/Server/scripts/dialogue/jukat.asc deleted file mode 100644 index 938ddb6f7..000000000 --- a/Server/scripts/dialogue/jukat.asc +++ /dev/null @@ -1,15 +0,0 @@ -@dialogue npc:564 -npc("Dragon swords! Here, Dragon swords! Straight from", "Frenaskrae!") ->options - -options: { - option("Yes please.", "No thanks, I'm just browsing.") - @option 1 { - player("Yes please!") - openshop(564) - } - @option 2 { - player("No thanks, I'm just browsing.") - } -} - diff --git a/Server/scripts/dialogue/kjedelig_uppsen.asc b/Server/scripts/dialogue/kjedelig_uppsen.asc deleted file mode 100644 index 2b5b1dad5..000000000 --- a/Server/scripts/dialogue/kjedelig_uppsen.asc +++ /dev/null @@ -1,7 +0,0 @@ -@dialogue npc:5518 - - -npc("Ho, Larlim Far-strider! The brave warrior returns!") - player("It was a hard-fought battle, but we Fremennik prevailed!") - npc("I expect we'll be hearing songs about the 'slayer of the", "Troll king' for many years to come") - player("It would be an honour to be remembered in such a way!", "Thanks.") \ No newline at end of file diff --git a/Server/scripts/dialogue/lisse_isaakson.asc b/Server/scripts/dialogue/lisse_isaakson.asc deleted file mode 100644 index 6c6b04746..000000000 --- a/Server/scripts/dialogue/lisse_isaakson.asc +++ /dev/null @@ -1,10 +0,0 @@ -@dialogue npc:5513 - - npc("Hello, visitor!") - player("Hello. What are you up to?") - npc("Ah, I was about to collect some yak's milk to make", "yak cheese.") - player("Eughr! Though I am curious. Can I try some?") - npc("Sorry, no. The last outlander who ate my cheese", "was ill for a month") - player("So why don't you get ill as well?") - npc("Well, we eat yak milk products every day, from", "when we're born. So I suppose we're used to it. Anyways I", "should stop yakking - Haha - and get on with my work") - player("I'm glad to see that puns are common", "everywhere in Keldagrim; even here") \ No newline at end of file diff --git a/Server/scripts/dialogue/melina.asc b/Server/scripts/dialogue/melina.asc deleted file mode 100644 index 45f5389b7..000000000 --- a/Server/scripts/dialogue/melina.asc +++ /dev/null @@ -1,2 +0,0 @@ -@dialogue npc:2935 -npc("Leave me be.") \ No newline at end of file diff --git a/Server/scripts/dialogue/molly.asc b/Server/scripts/dialogue/molly.asc deleted file mode 100644 index ad71aba80..000000000 --- a/Server/scripts/dialogue/molly.asc +++ /dev/null @@ -1,41 +0,0 @@ -@dialogue npc:3892,3893,3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909,3910,3911 - -npc("Thanks for coming!") -player("It's not like I had a lot of choice you know!") -npc("I'm sorry for abducting you like that, but I really need", "your help >playername<.") -player("What's the problem then?") ->event_info - -event_info:{ - npc("It's my evil twin sister! She's been galavanting around", "Keldagrim commiting crimes and now I'm getting the", "blame!") - player("Well what's all this got to do with me then?") - npc("I'm glad you asked!") - npc("Through that door is a room with a cage and a control", "panel that operates a giant mechanical claw.") - npc("I lured my sister into the room so I could imprison her", "in the cage by using the claw. The problem is my sister", "managed to herd some innocent civilians in there with", "her.") - player("So what do you need me to do?") - npc("I need you to go next door and use the claw to catch", "my sister.") - npc("Once she's in prison, she won't be causing me anymore", "bother!") - player("Sounds easy enough to me.") - npc("Fabulous! Now take a good long look at me because the", "door will be locked behind you. My twin looks exactly", "like me, even her clothes!") - npc("One more thing to make your life difficult; the magic", "powering the claw is running low so you'll only have", "two attempts to catch her.") - player("I'll do my best then!") - npc("By the way, would you like me to run through the", "controls for you, or do you think you'll manage?") - setattribute("/save:ame:evil_twin_info", true); - option("Yes please.", "No thanks.") - @option1{ - player("Yes please. I mean, it's always best to be prepared,", "right?") - npc("Ok, when you turn the machine on you'll see the", "glowing mark on the floor where the claw is currently", "aiming, and you'll see a lever and button on the right", "hand side of your screen.") - npc("To move the claw's current location, click on the", "direction you want it to move in, as indicated on the", "right hand panel.") - npc("You'll know which way the claw will go as the lever will", "point that way to show you.") - npc("When you see someone on top of the glowing mark,", "then hit the button on the right hand panel, as this will", "send the claw down to pick them up.") - npc("Do be careful and make sure that there is someone on", "the mark, and not just walking past it.") - npc("Oh, and make sure the person is my sister, so you", "don't end up catching a random civilian.") - npc("Does that help?") - player("Yes, that covers everything. Thanks!") - npc("Good luck!") - } - @option2{ - player("No thanks, I should be able to work it out.") - npc("Good luck!") - } -} \ No newline at end of file diff --git a/Server/scripts/dialogue/morten_holdstrom.asc b/Server/scripts/dialogue/morten_holdstrom.asc deleted file mode 100644 index 8b0f0eb01..000000000 --- a/Server/scripts/dialogue/morten_holdstrom.asc +++ /dev/null @@ -1,11 +0,0 @@ -@dialogue npc:5510 - - - npc("Good day to you.") - player("Hello. What are you up to?") - npc("Ah, Today is surstromming day! The herring I buried", "six months ago is ready to be dug up.") - player("Eughr! What are you going to do with it?") - npc("Eat it, of course! It will be fermented just-right by now.") - player("Fermented? You eat rotten fish?") - npc("Hmmm, tasty. I'm guessing you don't want to", "come round and try it?") - player("You guess correctly.") \ No newline at end of file diff --git a/Server/scripts/dialogue/peer_the_seer.asc b/Server/scripts/dialogue/peer_the_seer.asc deleted file mode 100644 index 53da493d0..000000000 --- a/Server/scripts/dialogue/peer_the_seer.asc +++ /dev/null @@ -1,4 +0,0 @@ -@dialogue npc:1288 -npc("Hey! Outerlander! That is my house! Who do you", "think you are to just barge into my home without", "asking?") -player("Um... My name is @name.") -npc("It was a rhetorical question, outerlander. Now get lost.", "I do not appreciate intruders in my home.") diff --git a/Server/scripts/dialogue/private_paldon.asc b/Server/scripts/dialogue/private_paldon.asc deleted file mode 100644 index 9b44ca0ba..000000000 --- a/Server/scripts/dialogue/private_paldon.asc +++ /dev/null @@ -1,6 +0,0 @@ -@dialogue npc:5031 -player("Hi") -npc("Shhh. Don't talk to me.") -player("What? Why?") -npc("If I'm seen slacking off by talking to you, I'll be in", "deep trouble!") -player("Oh... Sorry.") \ No newline at end of file diff --git a/Server/scripts/dialogue/private_pendron.asc b/Server/scripts/dialogue/private_pendron.asc deleted file mode 100644 index d35e10b01..000000000 --- a/Server/scripts/dialogue/private_pendron.asc +++ /dev/null @@ -1,5 +0,0 @@ -@dialogue npc:5032 -player("Hi there.") -npc("Don't suppose you've seen a battleaxe around here?") -player("A battleaxe? Nope, afraid not.") -npc("The captain is going to kill me if he finds out I've lost", "my weapon.") diff --git a/Server/scripts/dialogue/private_pierreb.asc b/Server/scripts/dialogue/private_pierreb.asc deleted file mode 100644 index 4c76ea8e0..000000000 --- a/Server/scripts/dialogue/private_pierreb.asc +++ /dev/null @@ -1,4 +0,0 @@ -@dialogue npc:5033 -player("Hello. So you're just a private?") -npc("Show some respect! It's more than you'll achieve.") -player("I beg to differ. I'm in perfect shape!") diff --git a/Server/scripts/dialogue/robin.asc b/Server/scripts/dialogue/robin.asc deleted file mode 100644 index 3a3995208..000000000 --- a/Server/scripts/dialogue/robin.asc +++ /dev/null @@ -1,11 +0,0 @@ -@dialogue npc:1694 -player("It's nice to see another human face around here.") -npc("Leave me be, peasant - I am relaxing.") -player("Well, that's nice!") -npc("Do you know who I am?") -player("I'm sorry, I haven't had the privilege.") -npc("I, peasant, am Robin, Master Bowman. I am very", "famous you know.") -player("Oh, Robin, Master Bowman, I see.") -npc("So have you heard of me?") -player("No.") - diff --git a/Server/scripts/dialogue/simon_templeton.asc b/Server/scripts/dialogue/simon_templeton.asc deleted file mode 100644 index 9d5dc9b79..000000000 --- a/Server/scripts/dialogue/simon_templeton.asc +++ /dev/null @@ -1,38 +0,0 @@ -@dialogue npc:3123 -player("Hello, Simon.") -npc("G'day, @name.") -player("How's it going?") -$hasitem(6970, 1) { - npc("Not bad, mate, not bad at all. Got any artefacts for", "me?") - >sellOptions -} -npc("I've been contracted to retrieve artefacts from the top", "of this magnificent pyramid.") -player("So, why are you down here and not up there then?") -npc("Well, it's me back - an old injury I picked up in", "Sophanem has come back to haunt me. I was working", "for the Museum of Varrock, and the gold I had been", "given wasn't enough to pay for the carpet rides, let") -npc("alone any decent equipment. But that was a long time", "ago... I can't even lift a chisel right now. There's no", "way I can climb all the way up there.") -player("How on earth did you get down the cliff face, then?") -npc("That's what set off the injury. I was fine before I", "scrabbed down the pesky slope. I think it will be a while", "before I can climb back up again; I must be getting", "old.") -npc("Hang on a second... You look like you're pretty agile", "and up for a challenge. How about you retrieve it for", "me?") -npc("I'll tell you what, I'll give you 1000 coins for every", "artefact you get for me.") -player("I'll see what I can do.") -npc("Good on ya, mate!") - -sellOptions: { - option("Sell it.", "Keep it.") - @option1{ - player("I have one here I'll sell you.") - $removeitem(6970, 1) { - additem(995, 1000) - itemmessage(6970, "You hand over the artefact(s) and Simon hands you 1000 coins.") - npc("Ripper! Thanks a bundle mate! Thanks to you I can", "fulfill me contract. You're a true blue! The boss will be", "pleased.") - player("Glad I was able to help... but who is your boss? I", "thought you worked for the museum?") - npc("Mind your own bizzo, mate. But if you get any more,", "you know where I am!") - } - } - @option2{ - player("I have one, but I want to keep it for now.") - npc("Careful, mate, people come looking for a thing like", "that!") - player("Thanks for the advice, but I'll hang onto it for now.") - npc("Bye, cobber.") - } -} \ No newline at end of file diff --git a/Server/scripts/dialogue/slug_hemligssen.asc b/Server/scripts/dialogue/slug_hemligssen.asc deleted file mode 100644 index 3742d17e0..000000000 --- a/Server/scripts/dialogue/slug_hemligssen.asc +++ /dev/null @@ -1,5 +0,0 @@ -@dialogue npc:5520 - - - npc("Shh. Go away. I'm not allowed to talk to you.") - player("Fine, Whatever...") \ No newline at end of file diff --git a/Server/scripts/dialogue/spirit_spider.asc b/Server/scripts/dialogue/spirit_spider.asc deleted file mode 100644 index d864fc823..000000000 --- a/Server/scripts/dialogue/spirit_spider.asc +++ /dev/null @@ -1,7 +0,0 @@ -@dialogue npc:6841, 6842 -npc("Where are we going?") -player("I've not decided yet.") -npc("Fine, don't tell me...") -player("Oh, okay, well we are going...") -npc("Don't want to know now.") -player("Siiiigh...spiders.") \ No newline at end of file diff --git a/Server/scripts/dialogue/test.asc b/Server/scripts/dialogue/test.asc deleted file mode 100644 index 88a630011..000000000 --- a/Server/scripts/dialogue/test.asc +++ /dev/null @@ -1 +0,0 @@ -message("Roar") \ No newline at end of file diff --git a/Server/scripts/dialogue/thakkrad_sigmundson.asc b/Server/scripts/dialogue/thakkrad_sigmundson.asc deleted file mode 100644 index 228288cbb..000000000 --- a/Server/scripts/dialogue/thakkrad_sigmundson.asc +++ /dev/null @@ -1,6 +0,0 @@ -@dialogue npc:5506 - -npc("Thank you for leading the Burgher's militia against the", "Troll King.") - npc("Now that the trolls are leaderless I have repaired the", "bridge to the central isle for you as best I can.") - player("Thanks Thakkrad. Does that mean I have access to the", "runite ores on that island?") - npc("Yes, You should be able to mine runite there if you wish.") \ No newline at end of file diff --git a/Server/scripts/dialogue/trogen_konungarde.asc b/Server/scripts/dialogue/trogen_konungarde.asc deleted file mode 100644 index 367a6428a..000000000 --- a/Server/scripts/dialogue/trogen_konungarde.asc +++ /dev/null @@ -1,7 +0,0 @@ -@dialogue npc:5519 - - -npc("Ho, Larlim Far-strider! The brave warrior returns!") - player("It was a hard-fought battle, but we Fremennik prevailed!") - npc("I expect we'll be hearing songs about the 'slayer of the", "Troll king' for many years to come") - player("It would be an honour to be remembered in such a way!", "Thanks.") \ No newline at end of file diff --git a/Server/scripts/dialogue/yanille_bartender.asc b/Server/scripts/dialogue/yanille_bartender.asc deleted file mode 100644 index a9da9908b..000000000 --- a/Server/scripts/dialogue/yanille_bartender.asc +++ /dev/null @@ -1,25 +0,0 @@ -@dialogue npc:739 -npc("What can I get you?") -player("What's on the menu?") -npc("Dragon Bitter and Greenman's Ale, oh and some cheap", "beer.") ->options - -options: { - option("I'll give it a miss I think", "I'll try the Dragon Bitter.", "Can I have some Greenman's Ale?", "One cheap beer please!") - @option1 { - - } - @option2 { - - } - @option3 { - - } - @option4 { - - } -} - -buy:(id, cost) { - -} \ No newline at end of file diff --git a/Server/scripts/dialogue/zanaris_gatekeeper.asc b/Server/scripts/dialogue/zanaris_gatekeeper.asc deleted file mode 100644 index c79e44646..000000000 --- a/Server/scripts/dialogue/zanaris_gatekeeper.asc +++ /dev/null @@ -1,6 +0,0 @@ -@dialogue npc:3321, 3307 -player("What happened to the old man who used to be the", "doorman?") -npc("You mean my father? He went into retirement, I've", "taken over the family business instead.") -player("Your father! But you don't look anything like him!") -npc("No, fortunately for me I inherited my good looks from", "my mother.") - 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/java/core/game/content/global/Dyes.java b/Server/src/main/content/data/Dyes.java similarity index 96% rename from Server/src/main/java/core/game/content/global/Dyes.java rename to Server/src/main/content/data/Dyes.java index 63e47122c..79c508fa8 100644 --- a/Server/src/main/java/core/game/content/global/Dyes.java +++ b/Server/src/main/content/data/Dyes.java @@ -1,4 +1,4 @@ -package core.game.content.global; +package content.data; import core.game.node.item.Item; diff --git a/Server/src/main/content/data/EnchantedJewellery.kt b/Server/src/main/content/data/EnchantedJewellery.kt new file mode 100644 index 000000000..251bfa6c1 --- /dev/null +++ b/Server/src/main/content/data/EnchantedJewellery.kt @@ -0,0 +1,392 @@ +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.diary.DiaryType +import core.game.node.item.Item +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 org.rs09.consts.Sounds +import java.util.* + +/** + * Represents a piece of enchanted jewellery. + * @author Vexia, downthecrop, Player Name + */ +enum class EnchantedJewellery( + + val options: Array, + val locations: Array, crumble: Boolean, vararg val ids: Int) { + RING_OF_SLAYING( + arrayOf( + "Sumona in Pollnivneach.", + "Morytania Slayer Tower.", + "Rellekka Slayer Caves.", + "Nowhere. Give me a slayer update." + ), + arrayOf( + Location.create(3361, 2994, 0), + Location.create(3428, 3535, 0), + Location.create(2792, 3615, 0), + ), + true, + Items.RING_OF_SLAYING8_13281, + Items.RING_OF_SLAYING7_13282, + Items.RING_OF_SLAYING6_13283, + Items.RING_OF_SLAYING5_13284, + Items.RING_OF_SLAYING4_13285, + Items.RING_OF_SLAYING3_13286, + Items.RING_OF_SLAYING2_13287, + Items.RING_OF_SLAYING1_13288 + ), + + RING_OF_DUELING( + arrayOf( + "Al Kharid Duel Arena.", + "Castle Wars Arena.", + "Fist of Guthix", "Nowhere." + ), + arrayOf( + Location.create(3314, 3235, 0), + Location.create(2442, 3089, 0), + Location.create(1693, 5600, 0) + ), + true, + Items.RING_OF_DUELLING8_2552, + Items.RING_OF_DUELLING7_2554, + Items.RING_OF_DUELLING6_2556, + Items.RING_OF_DUELLING5_2558, + Items.RING_OF_DUELLING4_2560, + Items.RING_OF_DUELLING3_2562, + Items.RING_OF_DUELLING2_2564, + Items.RING_OF_DUELLING1_2566 + ), + AMULET_OF_GLORY( + arrayOf( + "Edgeville", + "Karamja", + "Draynor Village", + "Al Kharid", + "Nowhere" + ), + arrayOf( + Location.create(3087, 3495, 0), + Location.create(2919, 3175, 0), + Location.create(3104, 3249, 0), + Location.create(3292, 3175, 0) + ), + Items.AMULET_OF_GLORY4_1712, + Items.AMULET_OF_GLORY3_1710, + Items.AMULET_OF_GLORY2_1708, + Items.AMULET_OF_GLORY1_1706, + Items.AMULET_OF_GLORY_1704 + ), + AMULET_OF_GLORY_T( + AMULET_OF_GLORY.options, + AMULET_OF_GLORY.locations, + Items.AMULET_OF_GLORYT4_10354, + Items.AMULET_OF_GLORYT3_10356, + Items.AMULET_OF_GLORYT2_10358, + Items.AMULET_OF_GLORYT1_10360, + Items.AMULET_OF_GLORYT_10362 + ), + GAMES_NECKLACE( + arrayOf( + "Burthorpe Games Room.", + "Barbarian Outpost.", + "Clan Wars.", + "Wilderness Volcano.", + "Corporeal Beast." + ), + arrayOf( + Location.create(2899, 3563, 0), + Location.create(2520, 3571, 0), + Location.create(3266, 3686, 0), + Location.create(3179, 3685, 0), + Location.create(2885, 4372, 2) + ), + true, + Items.GAMES_NECKLACE8_3853, + Items.GAMES_NECKLACE7_3855, + Items.GAMES_NECKLACE6_3857, + Items.GAMES_NECKLACE5_3859, + Items.GAMES_NECKLACE4_3861, + Items.GAMES_NECKLACE3_3863, + Items.GAMES_NECKLACE2_3865, + Items.GAMES_NECKLACE1_3867 + ), + DIGSITE_PENDANT(arrayOf(), + arrayOf( + Location.create(3342, 3445, 0) + ), + true, + Items.DIGSITE_PENDANT_5_11194, + Items.DIGSITE_PENDANT_4_11193, + Items.DIGSITE_PENDANT_3_11192, + Items.DIGSITE_PENDANT_2_11191, + Items.DIGSITE_PENDANT_1_11190 + ), + COMBAT_BRACELET( + arrayOf( + "Champions' Guild.", + "Monastery.", + "Ranging Guild.", + "Warriors' Guild.", + "Nowhere." + ), + arrayOf( + Location.create(3191, 3365, 0), + Location.create(3052, 3472, 0), + Location.create(2657, 3439, 0), + Location.create(2878, 3546, 0) + ), + Items.COMBAT_BRACELET4_11118, + Items.COMBAT_BRACELET3_11120, + Items.COMBAT_BRACELET2_11122, + Items.COMBAT_BRACELET1_11124, + Items.COMBAT_BRACELET_11126 + ), + SKILLS_NECKLACE( + arrayOf( + "Fishing Guild.", + "Mining Guild.", + "Crafting Guild.", + "Cooking Guild.", + "Nowhere." + ), + arrayOf( + Location.create(2611, 3392, 0), + Location.create(3016, 3338, 0), + Location.create(2933, 3290, 0), + Location.create(3143, 3442, 0) + ), + Items.SKILLS_NECKLACE4_11105, + Items.SKILLS_NECKLACE3_11107, + Items.SKILLS_NECKLACE2_11109, + Items.SKILLS_NECKLACE1_11111, + Items.SKILLS_NECKLACE_11113 + ), + RING_OF_WEALTH( + arrayOf( + "Grand Exchange.", + "Nowhere." + ), + arrayOf( + Location.create(3163, 3464, 0) + ), + Items.RING_OF_WEALTH4_14646, + Items.RING_OF_WEALTH3_14644, + 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 + + /** + * Constructs a new `EnchantedJewelleryPlugin` `Object`. + * @param options the dialogue options. + * @param locations the teleport locations. + * @param ids the ordered item ids. + */ + constructor(options: Array, locations: Array, vararg ids: Int) : this(options, locations, false, *ids) + + /** + * 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. + * @param isEquipped If the player is operating. + */ + fun use(player: Player, item: Item, buttonID: Int, isEquipped: Boolean) { + if (buttonID > locations.size - 1) { + if (isSlayerRing(item)) { + slayerProgressDialogue(player) + } + 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)) { + 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) { + if (isEquipped) { + replaceSlot(player, item.slot, nextJewellery, item, Container.EQUIPMENT) + } else { + replaceSlot(player, item.slot, nextJewellery) + } + } + + private fun isSlayerRing(item: Item): Boolean { + return (item.id in RING_OF_SLAYING.ids) + } + + private fun slayerProgressDialogue(player: Player) { + 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) + return + } + sendNPCDialogue(player, slayerManager.master!!.npc, "You're currently " + + "assigned to kill ${SlayerUtils.pluralise(getSlayerTaskName(player))} " + + "only ${getSlayerTaskKillsRemaining(player)} more to go.", core.game.dialogue.FacialExpression.FRIENDLY) + // Slayer tracker UI + setVarp(player, 2502, slayerManager.flags.taskFlags shr 4) + } + + private fun canTeleport(player: Player, item: Item): Boolean { + return player.zoneMonitor.teleport(1, item) + } + + private fun getNext(index: Int): Int { + val i = index + 1 + if (i > ids.size - 1) { + return ids[ids.size - 1] + } + return ids[i] + } + + private fun getLocation(index: Int): Location { + if (index > locations.size - 1) { + return locations[locations.size - 1] + } + return locations[index] + } + + fun getJewelleryName(item: Item): String { + return item.name.replace(""" ?\(t?[0-9]?\)""".toRegex(), "") + } + + fun getJewelleryType(item: Item): String { + return when { + this == GAMES_NECKLACE -> "games necklace" + this == DIGSITE_PENDANT -> "necklace" + this == COMBAT_BRACELET -> "bracelet" + this == SKILLS_NECKLACE -> "necklace" + else -> item.name.split(" ")[0].lowercase(Locale.getDefault()) + } + } + + fun isLastItemIndex(index: Int): Boolean = index == ids.size - 1 + + fun getItemIndex(item: Item): Int { + return ids.indexOf(item.id) + } + + companion object { + private val ANIMATION = Animation(714) + private val GRAPHICS = Graphics(308, 100, 50) + private val JEWELLERY_ANIMATION = Animation(9603) + private val JEWELLERY_GRAPHICS = Graphics(1684) + val idMap = HashMap() + + init { + values().forEach { entry -> + entry.ids.forEach { idMap[it] = entry } + } + } + } +} diff --git a/Server/src/main/java/core/game/content/global/GodBook.java b/Server/src/main/content/data/GodBook.java similarity index 97% rename from Server/src/main/java/core/game/content/global/GodBook.java rename to Server/src/main/content/data/GodBook.java index 7198cf39c..92029b479 100644 --- a/Server/src/main/java/core/game/content/global/GodBook.java +++ b/Server/src/main/content/data/GodBook.java @@ -1,8 +1,10 @@ -package core.game.content.global; +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/java/core/game/content/global/GodType.java b/Server/src/main/content/data/GodType.java similarity index 97% rename from Server/src/main/java/core/game/content/global/GodType.java rename to Server/src/main/content/data/GodType.java index e202c34e4..2b8ce7e9a 100644 --- a/Server/src/main/java/core/game/content/global/GodType.java +++ b/Server/src/main/content/data/GodType.java @@ -1,13 +1,13 @@ -package core.game.content.global; +package content.data; -import core.game.content.dialogue.DialogueAction; +import core.game.dialogue.DialogueAction; import core.game.node.entity.player.Player; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.tools.StringUtils; diff --git a/Server/src/main/java/core/game/content/global/Lamps.java b/Server/src/main/content/data/Lamps.java similarity index 97% rename from Server/src/main/java/core/game/content/global/Lamps.java rename to Server/src/main/content/data/Lamps.java index 4abba5531..9dc150bf1 100644 --- a/Server/src/main/java/core/game/content/global/Lamps.java +++ b/Server/src/main/content/data/Lamps.java @@ -1,4 +1,4 @@ -package core.game.content.global; +package content.data; import core.game.node.item.Item; @@ -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/java/core/game/content/global/LightSource.java b/Server/src/main/content/data/LightSource.java similarity index 99% rename from Server/src/main/java/core/game/content/global/LightSource.java rename to Server/src/main/content/data/LightSource.java index 1a8c4f7d3..118cb492f 100644 --- a/Server/src/main/java/core/game/content/global/LightSource.java +++ b/Server/src/main/content/data/LightSource.java @@ -1,4 +1,4 @@ -package core.game.content.global; +package content.data; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/content/data/Meat.kt b/Server/src/main/content/data/Meat.kt new file mode 100644 index 000000000..714b2403a --- /dev/null +++ b/Server/src/main/content/data/Meat.kt @@ -0,0 +1,51 @@ +package content.data + +enum class MeatState { + INEDIBLE_RAW, + INEDIBLE_BURNT, + INEDIBLE_SPECIAL, // could be a quest item + EDIBLE_COOKED, +} + +enum class Meat(val id: Int, val state: MeatState) { + ENCHANTED_BEEF(522, MeatState.INEDIBLE_SPECIAL), + ENCHANTED_RAT_MEAT(523, MeatState.INEDIBLE_SPECIAL), + ENCHANTED_BEAR_MEAT(524, MeatState.INEDIBLE_SPECIAL), + ENCHANTED_CHICKEN(525, MeatState.INEDIBLE_SPECIAL), + RAW_UGTHANKI_MEAT(1859, MeatState.INEDIBLE_RAW), + UGTHANKI_MEAT(1861, MeatState.EDIBLE_COOKED), + RAW_BEEF(2132, MeatState.INEDIBLE_RAW), + RAW_RAT_MEAT(2134, MeatState.INEDIBLE_RAW), + RAW_BEAR_MEAT(2136, MeatState.INEDIBLE_RAW), + RAW_CHICKEN(2138, MeatState.INEDIBLE_RAW), + COOKED_CHICKEN(2140, MeatState.EDIBLE_COOKED), + BURNT_CHICKEN(2140, MeatState.INEDIBLE_BURNT), + COOKED_MEAT(2142, MeatState.EDIBLE_COOKED), + BURNT_MEAT(2146, MeatState.INEDIBLE_BURNT), + THIN_SNAIL_MEAT(3369, MeatState.EDIBLE_COOKED), + LEAN_SNAIL_MEAT(3371, MeatState.EDIBLE_COOKED), + FAT_SNAIL_MEAT(3373, MeatState.EDIBLE_COOKED), + RAW_BEEF_UNDEAD(4287, MeatState.INEDIBLE_RAW), + RAW_CHICKEN_UNDEAD(4289, MeatState.INEDIBLE_RAW), + COOKED_CHICKEN_UNDEAD(4291, MeatState.EDIBLE_COOKED), + COOKED_MEAT_UNDEAD(4293, MeatState.EDIBLE_COOKED), + RAW_CRAB_MEAT(7518, MeatState.INEDIBLE_RAW), + BURNT_CRAB_MEAT(7520, MeatState.INEDIBLE_BURNT), + COOKED_CRAB_MEAT_5(7521, MeatState.EDIBLE_COOKED), + COOKED_CRAB_MEAT_4(7523, MeatState.EDIBLE_COOKED), + COOKED_CRAB_MEAT_3(7524, MeatState.EDIBLE_COOKED), + COOKED_CRAB_MEAT_2(7525, MeatState.EDIBLE_COOKED), + COOKED_CRAB_MEAT_1(7526, MeatState.EDIBLE_COOKED), + GROUND_CRAB_MEAT(7527, MeatState.INEDIBLE_RAW), + LOCUST_MEAT(9052, MeatState.EDIBLE_COOKED), + RAW_BIRD_MEAT(9978, MeatState.INEDIBLE_RAW), + ROAST_BIRD_MEAT(9980, MeatState.EDIBLE_COOKED), + BURNT_BIRD_MEAT(9982, MeatState.INEDIBLE_BURNT), + SKEWERED_BIRD_MEAT(9982, MeatState.INEDIBLE_RAW), + RAW_BEAST_MEAT(9986, MeatState.INEDIBLE_RAW), + ROAST_BEAST_MEAT(9988, MeatState.EDIBLE_COOKED), + BURNT_BEAST_MEAT(9990, MeatState.INEDIBLE_BURNT), + RAW_YAK_MEAT(10816, MeatState.INEDIBLE_RAW), + RAW_PAWYA_MEAT(12535, MeatState.INEDIBLE_RAW), + ENCHANTED_PAWYA_MEAT(12546, MeatState.INEDIBLE_SPECIAL); +} \ No newline at end of file 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.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 new file mode 100644 index 000000000..67791684b --- /dev/null +++ b/Server/src/main/content/data/consumables/Consumables.java @@ -0,0 +1,439 @@ +package content.data.consumables; + +import content.data.consumables.effects.*; +import core.game.consumable.*; +import core.game.node.entity.player.link.diary.DiaryType; +import core.game.node.entity.skill.Skills; +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. + */ +public enum Consumables { + + /** Meats */ + COOKED_MEAT(new Food(new int[] {2142}, new HealingEffect(3))), + SHRIMPS(new Food(new int[] {315}, new HealingEffect(3))), + COOKED_CHICKEN(new Food(new int[] {2140}, new HealingEffect(3))), + COOKED_RABBIT(new Food(new int[] {3228}, new HealingEffect(5))), + ANCHOVIES(new Food(new int[] {319}, new HealingEffect(1))), + SARDINE(new Food(new int[] {325}, new HealingEffect(4))), + POISON_KARAMBWAN(new Food(new int[] {3146}, new PoisonKarambwanEffect())), + UGTHANKI_MEAT(new Food(new int[] {1861}, new HealingEffect(3))), + 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 RandomHealthEffect(5, 7))), + TROUT(new Food(new int[] {333}, 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 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 RandomHealthEffect(7, 9))), + SALMON(new Food(new int[] {329}, new HealingEffect(9))), + SLIMY_EEL(new Food(new int[] {3381}, new RandomHealthEffect(6, 10))), + TUNA(new Food(new int[] {361}, new HealingEffect(10))), + COOKED_KARAMBWAN(new Food(new int[] {3144}, new HealingEffect(18)), true), + COOKED_CHOMPY(new Food(new int[] {2878}, new HealingEffect(10))), + RAINBOW_FISH(new Food(new int[] {10136}, new HealingEffect(11))), + CAVE_EEL(new Food(new int[] {5003}, new 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(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(1))), + GIANT_FROG_LEGS(new Food(new int [] {4517}, new HealingEffect(6))), + + /** Breads */ + BREAD(new Food(new int[] {2309}, new HealingEffect(5))), + BAGUETTE(new Food(new int[] {6961}, new HealingEffect(6))), + + /** Sandwiches */ + TRIANGLE_SANDWICH(new Food(new int[] {6962}, new HealingEffect(6))), + SQUARE_SANDWICH(new Food(new int[] {6965}, new HealingEffect(6))), + SEAWEED_SANDWICH(new FakeConsumable(3168, new String[] {"You really, really do not want to eat that."})), + FROGBURGER(new Food(new int[] {10962}, new HealingEffect(2))), + + /** Kebabs */ + UGTHANKI_KEBAB(new Food(new int[] {1883}, new UgthankiKebabEffect())), + UGTHANKI_KEBAB_SMELLING(new Food(new int[] {1885}, new SmellingUgthankiKebabEffect())), + KEBAB(new Food(new int[] {1971}, new KebabEffect())), + SUPER_KEBAB(new Food(new int[] {4608}, new SuperKebabEffect())), + + /** Pies */ + REDBERRY_PIE(new HalfableFood(new int[] {2325, 2333, 2313}, new HealingEffect(5))), + MEAT_PIE(new HalfableFood(new int[] {2327, 2331, 2313}, new HealingEffect(6))), + APPLE_PIE(new HalfableFood(new int[] {2323, 2335, 2313}, new HealingEffect(7))), + GARDEN_PIE(new HalfableFood(new int[] {7178, 7180, 2313}, new MultiEffect(new HealingEffect(6), new SkillEffect(Skills.FARMING, 3, 0)))), + FISH_PIE(new HalfableFood(new int[] {7188, 7190, 2313}, new MultiEffect(new HealingEffect(6), new SkillEffect(Skills.FISHING, 3, 0)))), + ADMIRAL_PIE(new HalfableFood(new int[] {7198, 7200, 2313}, new MultiEffect(new HealingEffect(8), new SkillEffect(Skills.FISHING, 5, 0)))), + WILD_PIE(new HalfableFood(new int[] {7208, 7210, 2313}, new MultiEffect(new SkillEffect(Skills.SLAYER, 5, 0), new SkillEffect(Skills.RANGE, 4, 0), new HealingEffect(11)))), + SUMMER_PIE(new HalfableFood(new int[] {7218, 7220, 2313}, new MultiEffect(new HealingEffect(11), new SkillEffect(Skills.AGILITY, 5, 0), new EnergyEffect(10)))), + + /** Stews */ + STEW(new Food(new int[] {2003, 1923}, new HealingEffect(11))), + SPICY_STEW(new Food(new int[] {7479, 1923}, new HealingEffect(11))), + CURRY(new Food(new int[] {2011, 1923}, new HealingEffect(19))), + BANANA_STEW(new Food(new int[] {4016, 1923}, new HealingEffect(11))), + + /** Pizzas */ + PLAIN_PIZZA(new HalfableFood(new int[] {2289, 2291}, new HealingEffect(7))), + MEAT_PIZZA(new HalfableFood(new int[] {2293, 2295}, new HealingEffect(8))), + ANCHOVY_PIZZA(new HalfableFood(new int[] {2297, 2299}, new HealingEffect(9))), + PINEAPPLE_PIZZA(new HalfableFood(new int[] {2301, 2303}, new HealingEffect(11))), + + /** Cakes */ + CAKE(new Cake(new int[] {1891, 1893, 1895}, new HealingEffect(4), "You eat part of the cake.", "You eat some more cake.", "You eat the slice of cake.")), + CHOCOLATE_CAKE(new Cake(new int[] {1897, 1899, 1901}, new HealingEffect(5), "You eat part of the chocolate cake.", "You eat some more of the chocolate cake.", "You eat the slice of cake.")), + ROCK_CAKE(new Food(new int[] {2379}, new RockCakeEffect(), "The rock cake resists all attempts to eat it.")), + DWARVEN_ROCK_CAKE(new Food(new int[] {7510, 7510}, new DwarvenRockCakeEffect(), "Ow! You nearly broke a tooth!", "The rock cake resists all attempts to eat it.")), + HOT_DWARVEN_ROCK_CAKE(new Food(new int[] {7509, 7509}, new DwarvenRockCakeEffect(), "Ow! You nearly broke a tooth!", "The rock cake resists all attempts to eat it.")), + COOKED_FISHCAKE(new Food(new int[] {7530}, new HealingEffect(11))), + MINT_CAKE(new Food(new int[] {9475}, new EnergyEffect(50))), + + /** Vegetables */ + 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))), + FRIED_MUSHROOMS(new Food(new int[] {7082, 1923}, new HealingEffect(5))), + POTATO_WITH_CHEESE(new Food(new int[] {6705}, new HealingEffect(16))), + EGG_POTATO(new Food(new int[] {7056}, new HealingEffect(11))), + MUSHROOMS_AND_ONIONS(new Food(new int[] {7066, 1923}, new HealingEffect(11))), + 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(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))), + + /** Dairies */ + POT_OF_CREAM(new Food(new int[] {2130}, new HealingEffect(1))), + CHEESE(new Food(new int[] {1985}, new HealingEffect(2))), + CHOCOLATEY_MILK(new Drink(new int[] {1977, 1925}, new HealingEffect(4))), + + /** Fruits */ + BANANA(new Food(new int[] {1963}, new HealingEffect(2))), + SLICED_BANANA(new Food(new int[] {3162}, new HealingEffect(2))), + RED_BANANA(new Food(new int[] {7572}, new HealingEffect(5), "You eat the red banana. It's tastier than your average banana.")), + SLICED_RED_BANANA(new Food(new int[] {7574}, new HealingEffect(5), "You eat the sliced red banana. Yum.")), + 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 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)), "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[] {"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))), + LEMON_SLICES(new Food(new int[] {2106}, new HealingEffect(2))), + LIME(new Food(new int[] {2120}, new HealingEffect(2))), + LIME_CHUNKS(new Food(new int[] {2122}, new HealingEffect(2))), + 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 RemoveTimerEffect("poison"), new EnergyEffect(30)))), + + /** Gnome Cooking */ + 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))), + PREMADE_WM_CRUNC(new Food(new int[] {2237}, new HealingEffect(8))), + CHOCCHIP_CRUNCHIES(new Food(new int[] {2209}, new HealingEffect(7))), + PREMADE_CH_CRUNCH(new Food(new int[] {2239}, new HealingEffect(7))), + FRUIT_BATTA(new Food(new int[] {2277}, new HealingEffect(11))), + PREMADE_FRT_BATTA(new Food(new int[] {2225}, new HealingEffect(11))), + TOAD_BATTA(new Food(new int[] {2255}, new HealingEffect(11))), + PREMADE_TD_BATTA(new Food(new int[] {2221}, new HealingEffect(11))), + WORM_BATTA(new Food(new int[] {2253}, new HealingEffect(11))), + PREMADE_WM_BATTA(new Food(new int[] {2219}, new HealingEffect(11))), + VEGETABLE_BATTA(new Food(new int[] {2281}, new HealingEffect(11))), + PREMADE_VEG_BATTA(new Food(new int[] {2227}, new HealingEffect(11))), + CHEESE_AND_TOMATOES_BATTA(new Food(new int[] {2259}, new HealingEffect(11))), + PREMADE_CT_BATTA(new Food(new int[] {2223}, new HealingEffect(11))), + WORM_HOLE(new Food(new int[] {2191}, new HealingEffect(12))), + PREMADE_WORM_HOLE(new Food(new int[] {2233}, new HealingEffect(12))), + VEG_BALL(new Food(new int[] {2195}, new HealingEffect(12))), + PREMADE_VEG_BALL(new Food(new int[] {2235}, new HealingEffect(12))), + TANGLED_TOADS_LEGS(new Food(new int[] {2187}, new HealingEffect(15))), + PREMADE_TTL(new Food(new int[] {2231}, new HealingEffect(15))), + CHOCOLATE_BOMB(new Food(new int[] {2195}, new HealingEffect(15))), + PREMADE_CHOC_BOMB(new Food(new int[] {2229}, new HealingEffect(15))), + TOAD_LEGS(new Food(new int[] {2152}, new HealingEffect(3))), + KING_WORM(new Food(new int[] {2162}, new HealingEffect(2))), + + /** 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)), "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)), "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(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.")), + 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 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 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[] {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!")), + 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), "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), "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), "You feel refreshed and ready for more building.")), + + /** Miscellaneous */ + CHOCOLATE_BAR(new Food(new int[] {1973}, new HealingEffect(3))), + PURPLE_SWEETS(new Food(new int[] {4561}, new HealingEffect(0))), + PURPLE_SWEETS_STACKABLE(new Food(new int[] {10476}, new MultiEffect(new EnergyEffect(10), new RandomHealthEffect(1, 3)), "The sugary goodness heals some energy.", "The sugary goodness is yummy.")), + FIELD_RATION(new Food(new int[] {7934}, new HealingEffect(10))), + ROLL(new Food(new int[] {6963}, new HealingEffect(6))), + TCHIKI_MONKEY_NUTS(new Food(new int[] {7573}, new HealingEffect(5), "You eat the Tchiki monkey nuts. They taste nutty.")), + 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(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))), + EASTER_EGG(new Food(new int[] {1961}, new HealingEffect(14))), + + /** Potions */ + 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, 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 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 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 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 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)))), + 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)))), + SUPER_ATT_MIX(new BarbarianMix(new int[] {11469, 11471}, new MultiEffect(new SkillEffect(Skills.ATTACK, 5, 0.15), new HealingEffect(6)))), + FISH_MIX(new BarbarianMix(new int[] {11477, 11479}, new MultiEffect(new SkillEffect(Skills.FISHING, 3, 0), new HealingEffect(6)))), + 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))), + SC_ENERGY(new Potion(new int[] {14217, 14219, 14221, 14223, 14225}, new EnergyEffect(20))), + SC_ATTACK(new Potion(new int[] {14227, 14229, 14231, 14233, 14235}, new SkillEffect(Skills.ATTACK, 3, 0.2))), + SC_STRENGTH(new Potion(new int[] {14237, 14239, 14241, 14243, 14245}, new SkillEffect(Skills.STRENGTH, 3, 0.2))), + SC_RANGE(new Potion(new int[] {14247, 14249, 14251, 14253, 14255}, new SkillEffect(Skills.RANGE, 3, 0.1))), + SC_DEFENCE(new Potion(new int[] {14257, 14259, 14261, 14263, 14265}, new SkillEffect(Skills.DEFENCE, 3, 0.1))), + 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 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 Consumables getConsumableById(final int itemId) { + return consumables.get(itemId); + } + + public static void add(final Consumables consumable) { + for (int id : consumable.consumable.getIds()) { + consumables.putIfAbsent(id, consumable); + } + } + + /* + Static modifier used to populate search engine lists. + */ + static { + for (Consumables consumable : Consumables.values()) { + add(consumable); + if (consumable.consumable instanceof Potion) { + for (int pot : consumable.consumable.getIds()) { + potions.add(pot); + } + } + } + } +} diff --git a/Server/src/main/java/core/game/content/consumable/effects/AchievementEffect.java b/Server/src/main/content/data/consumables/effects/AchievementEffect.java similarity index 84% rename from Server/src/main/java/core/game/content/consumable/effects/AchievementEffect.java rename to Server/src/main/content/data/consumables/effects/AchievementEffect.java index fcff49f2d..402e0b540 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/AchievementEffect.java +++ b/Server/src/main/content/data/consumables/effects/AchievementEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; -import core.game.content.consumable.ConsumableEffect; +import core.game.consumable.ConsumableEffect; public class AchievementEffect extends ConsumableEffect { private final DiaryType diary; 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 new file mode 100644 index 000000000..1b5e4778c --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/DamageEffect.java @@ -0,0 +1,36 @@ +package content.data.consumables.effects; + +import core.game.consumable.ConsumableEffect; +import core.game.node.entity.combat.ImpactHandler; +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) { + 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); + } + + @Override + public int getHealthEffectValue(Player player) { + double amount = amt; + if (isPercent) { + amount /= 100; + return (int) -(amount * player.getSkills().getLifepoints() + baseAmount); + } + return (int) -(amt + baseAmount); + } +} diff --git a/Server/src/main/java/core/game/content/consumable/effects/DraynorCabbageEffect.java b/Server/src/main/content/data/consumables/effects/DraynorCabbageEffect.java similarity index 82% rename from Server/src/main/java/core/game/content/consumable/effects/DraynorCabbageEffect.java rename to Server/src/main/content/data/consumables/effects/DraynorCabbageEffect.java index 96a260e9c..a58794c12 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/DraynorCabbageEffect.java +++ b/Server/src/main/content/data/consumables/effects/DraynorCabbageEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.skill.Skills; public class DraynorCabbageEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/DwarvenRockCakeEffect.java b/Server/src/main/content/data/consumables/effects/DwarvenRockCakeEffect.java similarity index 82% rename from Server/src/main/java/core/game/content/consumable/effects/DwarvenRockCakeEffect.java rename to Server/src/main/content/data/consumables/effects/DwarvenRockCakeEffect.java index bd7bd887a..90febe9b7 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/DwarvenRockCakeEffect.java +++ b/Server/src/main/content/data/consumables/effects/DwarvenRockCakeEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; public class DwarvenRockCakeEffect extends ConsumableEffect { diff --git a/Server/src/main/content/data/consumables/effects/EnergyEffect.java b/Server/src/main/content/data/consumables/effects/EnergyEffect.java new file mode 100644 index 000000000..75e23b422 --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/EnergyEffect.java @@ -0,0 +1,15 @@ +package content.data.consumables.effects; + +import core.game.consumable.ConsumableEffect; +import core.game.node.entity.player.Player; + +public class EnergyEffect extends ConsumableEffect { + double amt; + public EnergyEffect(int amt){ + this.amt = amt; + } + @Override + public void activate(Player p) { + p.getSettings().updateRunEnergy(-amt); + } +} diff --git a/Server/src/main/java/core/game/content/consumable/effects/HealingEffect.java b/Server/src/main/content/data/consumables/effects/HealingEffect.java similarity index 78% rename from Server/src/main/java/core/game/content/consumable/effects/HealingEffect.java rename to Server/src/main/content/data/consumables/effects/HealingEffect.java index f6e81469e..96512e6a8 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/HealingEffect.java +++ b/Server/src/main/content/data/consumables/effects/HealingEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; +import core.game.consumable.ConsumableEffect; public class HealingEffect extends ConsumableEffect { int amt; diff --git a/Server/src/main/java/core/game/content/consumable/effects/KebabEffect.java b/Server/src/main/content/data/consumables/effects/KebabEffect.java similarity index 97% rename from Server/src/main/java/core/game/content/consumable/effects/KebabEffect.java rename to Server/src/main/content/data/consumables/effects/KebabEffect.java index d3851d2e0..6b4f5c390 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/KebabEffect.java +++ b/Server/src/main/content/data/consumables/effects/KebabEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; import core.tools.RandomFunction; -import core.game.content.consumable.ConsumableEffect; import core.game.node.entity.skill.Skills; /** diff --git a/Server/src/main/content/data/consumables/effects/KegOfBeerEffect.kt b/Server/src/main/content/data/consumables/effects/KegOfBeerEffect.kt new file mode 100644 index 000000000..89232abee --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/KegOfBeerEffect.kt @@ -0,0 +1,15 @@ +package content.data.consumables.effects + +import core.game.consumable.ConsumableEffect +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills + + +class KegOfBeerEffect : ConsumableEffect() { + + override fun activate(p: Player?) { + val effect = MultiEffect(HealingEffect(15), SkillEffect(Skills.STRENGTH, 10.0, 0.0), SkillEffect(Skills.ATTACK, -40.0, 0.0)) + effect.activate(p) + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/consumable/effects/MatureWmbEffect.java b/Server/src/main/content/data/consumables/effects/MatureWmbEffect.java similarity index 87% rename from Server/src/main/java/core/game/content/consumable/effects/MatureWmbEffect.java rename to Server/src/main/content/data/consumables/effects/MatureWmbEffect.java index 66e7c6936..d2c5a98ed 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/MatureWmbEffect.java +++ b/Server/src/main/content/data/consumables/effects/MatureWmbEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; import core.game.node.entity.skill.Skills; public class MatureWmbEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/MultiEffect.java b/Server/src/main/content/data/consumables/effects/MultiEffect.java similarity index 85% rename from Server/src/main/java/core/game/content/consumable/effects/MultiEffect.java rename to Server/src/main/content/data/consumables/effects/MultiEffect.java index 3b9b8e5ff..228c7fac6 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/MultiEffect.java +++ b/Server/src/main/content/data/consumables/effects/MultiEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; public class MultiEffect extends ConsumableEffect { private ConsumableEffect[] effects; diff --git a/Server/src/main/content/data/consumables/effects/PercentHeal.java b/Server/src/main/content/data/consumables/effects/PercentHeal.java new file mode 100644 index 000000000..d1d36145f --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/PercentHeal.java @@ -0,0 +1,23 @@ +package content.data.consumables.effects; + +import core.game.consumable.ConsumableEffect; +import core.game.node.entity.player.Player; + +public class PercentHeal extends ConsumableEffect { + int base = 0; + double percent = 0.0; + + public PercentHeal(int base, double percent){ + this.base = base; + this.percent = percent; + } + + @Override + public void activate(Player p) { + int maxHp = p.getSkills().getMaximumLifepoints(); + int curHp = p.getSkills().getLifepoints(); + int amount = (int) Math.floor(maxHp * percent); + amount = base + Math.min(amount, (int)((1.0 + percent) * (double)maxHp - (double)curHp)); + p.getSkills().healNoRestrictions(amount); + } +} diff --git a/Server/src/main/java/core/game/content/consumable/effects/PercentageHealthEffect.java b/Server/src/main/content/data/consumables/effects/PercentageHealthEffect.java similarity index 85% rename from Server/src/main/java/core/game/content/consumable/effects/PercentageHealthEffect.java rename to Server/src/main/content/data/consumables/effects/PercentageHealthEffect.java index 41383eec4..cd39cb57e 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/PercentageHealthEffect.java +++ b/Server/src/main/content/data/consumables/effects/PercentageHealthEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; +import core.game.consumable.ConsumableEffect; public class PercentageHealthEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/PoisonEffect.java b/Server/src/main/content/data/consumables/effects/PoisonEffect.java similarity index 83% rename from Server/src/main/java/core/game/content/consumable/effects/PoisonEffect.java rename to Server/src/main/content/data/consumables/effects/PoisonEffect.java index 26e50dbd2..82adbeb81 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/PoisonEffect.java +++ b/Server/src/main/content/data/consumables/effects/PoisonEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.combat.ImpactHandler; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; public class PoisonEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/PoisonKarambwanEffect.java b/Server/src/main/content/data/consumables/effects/PoisonKarambwanEffect.java similarity index 80% rename from Server/src/main/java/core/game/content/consumable/effects/PoisonKarambwanEffect.java rename to Server/src/main/content/data/consumables/effects/PoisonKarambwanEffect.java index f2ed9cb46..ceea78062 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/PoisonKarambwanEffect.java +++ b/Server/src/main/content/data/consumables/effects/PoisonKarambwanEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; public class PoisonKarambwanEffect extends ConsumableEffect { 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/java/core/game/content/consumable/effects/RandomEnergyEffect.java b/Server/src/main/content/data/consumables/effects/RandomEnergyEffect.java similarity index 81% rename from Server/src/main/java/core/game/content/consumable/effects/RandomEnergyEffect.java rename to Server/src/main/content/data/consumables/effects/RandomEnergyEffect.java index ee8fe238f..cab047a1c 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/RandomEnergyEffect.java +++ b/Server/src/main/content/data/consumables/effects/RandomEnergyEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; import core.tools.RandomFunction; -import core.game.content.consumable.ConsumableEffect; public class RandomEnergyEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/RandomHealthEffect.java b/Server/src/main/content/data/consumables/effects/RandomHealthEffect.java similarity index 88% rename from Server/src/main/java/core/game/content/consumable/effects/RandomHealthEffect.java rename to Server/src/main/content/data/consumables/effects/RandomHealthEffect.java index 59e00df59..76e4c6514 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/RandomHealthEffect.java +++ b/Server/src/main/content/data/consumables/effects/RandomHealthEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; import core.tools.RandomFunction; -import core.game.content.consumable.ConsumableEffect; public class RandomHealthEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/RandomPrayerEffect.java b/Server/src/main/content/data/consumables/effects/RandomPrayerEffect.java similarity index 81% rename from Server/src/main/java/core/game/content/consumable/effects/RandomPrayerEffect.java rename to Server/src/main/content/data/consumables/effects/RandomPrayerEffect.java index ab29251a3..0d669a4df 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/RandomPrayerEffect.java +++ b/Server/src/main/content/data/consumables/effects/RandomPrayerEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; import core.game.node.entity.player.Player; import core.tools.RandomFunction; -import core.game.content.consumable.ConsumableEffect; +import core.game.consumable.ConsumableEffect; public class RandomPrayerEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/RandomSkillEffect.java b/Server/src/main/content/data/consumables/effects/RandomSkillEffect.java similarity index 83% rename from Server/src/main/java/core/game/content/consumable/effects/RandomSkillEffect.java rename to Server/src/main/content/data/consumables/effects/RandomSkillEffect.java index d2a5fea6b..3c7c11c23 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/RandomSkillEffect.java +++ b/Server/src/main/content/data/consumables/effects/RandomSkillEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; import core.tools.RandomFunction; -import core.game.content.consumable.ConsumableEffect; public class RandomSkillEffect extends ConsumableEffect { 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 new file mode 100644 index 000000000..ba9b3ea8d --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/RestoreEffect.java @@ -0,0 +1,42 @@ +package content.data.consumables.effects; + +import core.game.node.entity.player.Player; +import core.game.consumable.ConsumableEffect; +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(); + 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){ + p.getSkills().updateLevel(skill, boost, statL); + } + if (skill == Skills.PRAYER) + p.getSkills().incrementPrayerPoints(boost); + } + } +} diff --git a/Server/src/main/content/data/consumables/effects/RestoreSummoningSpecial.kt b/Server/src/main/content/data/consumables/effects/RestoreSummoningSpecial.kt new file mode 100644 index 000000000..51b727dab --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/RestoreSummoningSpecial.kt @@ -0,0 +1,11 @@ +package content.data.consumables.effects + +import core.game.consumable.ConsumableEffect +import core.game.node.entity.player.Player + +class RestoreSummoningSpecial : ConsumableEffect(){ + override fun activate(p: Player) { + val f = p.familiarManager.familiar + f?.updateSpecialPoints(-15) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/consumable/effects/RockCakeEffect.java b/Server/src/main/content/data/consumables/effects/RockCakeEffect.java similarity index 82% rename from Server/src/main/java/core/game/content/consumable/effects/RockCakeEffect.java rename to Server/src/main/content/data/consumables/effects/RockCakeEffect.java index 54d6c0afa..eb2ed364b 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/RockCakeEffect.java +++ b/Server/src/main/content/data/consumables/effects/RockCakeEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; public class RockCakeEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/SetAttributeEffect.java b/Server/src/main/content/data/consumables/effects/SetAttributeEffect.java similarity index 86% rename from Server/src/main/java/core/game/content/consumable/effects/SetAttributeEffect.java rename to Server/src/main/content/data/consumables/effects/SetAttributeEffect.java index 6a2b6776c..b00296143 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/SetAttributeEffect.java +++ b/Server/src/main/content/data/consumables/effects/SetAttributeEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; -import core.game.content.consumable.ConsumableEffect; +import core.game.world.GameWorld; public class SetAttributeEffect extends ConsumableEffect { String attrString; diff --git a/Server/src/main/content/data/consumables/effects/SkillEffect.java b/Server/src/main/content/data/consumables/effects/SkillEffect.java new file mode 100644 index 000000000..56c6b59f3 --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/SkillEffect.java @@ -0,0 +1,23 @@ +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 SkillEffect extends ConsumableEffect { + private final int skill_slot; + private final double base; + private final double bonus; + public SkillEffect(int skill_slot, double base, double bonus){ + this.skill_slot = skill_slot; + this.base = base; + this.bonus = bonus; + } + @Override + public void activate(Player p) { + Skills skills = p.getSkills(); + int slevel = skills.getStaticLevel(skill_slot); + int delta = (int)(base + (bonus * slevel)); + skills.updateLevel(skill_slot, delta, delta >= 0 ? slevel + delta : 0); + } +} diff --git a/Server/src/main/java/core/game/content/consumable/effects/SmellingUgthankiKebabEffect.java b/Server/src/main/content/data/consumables/effects/SmellingUgthankiKebabEffect.java similarity index 89% rename from Server/src/main/java/core/game/content/consumable/effects/SmellingUgthankiKebabEffect.java rename to Server/src/main/content/data/consumables/effects/SmellingUgthankiKebabEffect.java index c80c191e1..ab0f07319 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/SmellingUgthankiKebabEffect.java +++ b/Server/src/main/content/data/consumables/effects/SmellingUgthankiKebabEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; import core.tools.RandomFunction; -import core.game.content.consumable.ConsumableEffect; /** * According to the OSRS wiki, the smelling ugthanki kebab will usually heal nothing when eaten, but sometimes heals for 9. diff --git a/Server/src/main/java/core/game/content/consumable/effects/SummoningEffect.java b/Server/src/main/content/data/consumables/effects/SummoningEffect.java similarity index 84% rename from Server/src/main/java/core/game/content/consumable/effects/SummoningEffect.java rename to Server/src/main/content/data/consumables/effects/SummoningEffect.java index d1ea42804..be7d0b07f 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/SummoningEffect.java +++ b/Server/src/main/content/data/consumables/effects/SummoningEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; import core.game.node.entity.skill.Skills; public class SummoningEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/SuperKebabEffect.java b/Server/src/main/content/data/consumables/effects/SuperKebabEffect.java similarity index 89% rename from Server/src/main/java/core/game/content/consumable/effects/SuperKebabEffect.java rename to Server/src/main/content/data/consumables/effects/SuperKebabEffect.java index 127fd63e8..79f800594 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/SuperKebabEffect.java +++ b/Server/src/main/content/data/consumables/effects/SuperKebabEffect.java @@ -1,8 +1,8 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; import core.tools.RandomFunction; -import core.game.content.consumable.ConsumableEffect; import core.game.node.entity.skill.Skills; diff --git a/Server/src/main/java/core/game/content/consumable/effects/TroubleBrewingRumEffect.java b/Server/src/main/content/data/consumables/effects/TroubleBrewingRumEffect.java similarity index 91% rename from Server/src/main/java/core/game/content/consumable/effects/TroubleBrewingRumEffect.java rename to Server/src/main/content/data/consumables/effects/TroubleBrewingRumEffect.java index c9fa70b82..56cd91a24 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/TroubleBrewingRumEffect.java +++ b/Server/src/main/content/data/consumables/effects/TroubleBrewingRumEffect.java @@ -1,9 +1,9 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; import core.game.world.map.Location; -import core.game.content.consumable.ConsumableEffect; public class TroubleBrewingRumEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/UgthankiKebabEffect.java b/Server/src/main/content/data/consumables/effects/UgthankiKebabEffect.java similarity index 84% rename from Server/src/main/java/core/game/content/consumable/effects/UgthankiKebabEffect.java rename to Server/src/main/content/data/consumables/effects/UgthankiKebabEffect.java index d141d365f..dd8e35d84 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/UgthankiKebabEffect.java +++ b/Server/src/main/content/data/consumables/effects/UgthankiKebabEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; public class UgthankiKebabEffect extends ConsumableEffect { diff --git a/Server/src/main/java/core/game/content/consumable/effects/WizardsMindBombEffect.java b/Server/src/main/content/data/consumables/effects/WizardsMindBombEffect.java similarity index 87% rename from Server/src/main/java/core/game/content/consumable/effects/WizardsMindBombEffect.java rename to Server/src/main/content/data/consumables/effects/WizardsMindBombEffect.java index c40e61480..52a0e4229 100644 --- a/Server/src/main/java/core/game/content/consumable/effects/WizardsMindBombEffect.java +++ b/Server/src/main/content/data/consumables/effects/WizardsMindBombEffect.java @@ -1,7 +1,7 @@ -package core.game.content.consumable.effects; +package content.data.consumables.effects; +import core.game.consumable.ConsumableEffect; import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; import core.game.node.entity.skill.Skills; public class WizardsMindBombEffect extends ConsumableEffect { 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/java/core/game/content/global/SkillcapePerks.java b/Server/src/main/content/data/skill/SkillcapePerks.java similarity index 96% rename from Server/src/main/java/core/game/content/global/SkillcapePerks.java rename to Server/src/main/content/data/skill/SkillcapePerks.java index 25bb0b3f9..d9109a18f 100644 --- a/Server/src/main/java/core/game/content/global/SkillcapePerks.java +++ b/Server/src/main/content/data/skill/SkillcapePerks.java @@ -1,4 +1,4 @@ -package core.game.content.global; +package content.data.skill; /** * Handles the skillcape perks. diff --git a/Server/src/main/content/data/skill/SkillingTool.java b/Server/src/main/content/data/skill/SkillingTool.java new file mode 100644 index 000000000..cb0b7565f --- /dev/null +++ b/Server/src/main/content/data/skill/SkillingTool.java @@ -0,0 +1,330 @@ +package content.data.skill; + +import core.game.node.entity.player.Player; +import core.game.node.entity.skill.Skills; +import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Items; + +/** + * Represents a skilling tool (such as knife, axe, needle, ...) + * @author Emperor + */ +public enum SkillingTool { + /** + * Represents a bronze axe (woodcutting). + */ + BRONZE_AXE(1351, 1, 0.05D, new Animation(879)), + + /** + * Represents an iron axe (woodcutting). + */ + IRON_AXE(1349, 1, 0.1D, new Animation(877)), + + /** + * Represents a steel axe (woodcutting). + */ + STEEL_AXE(1353, 6, 0.2D, new Animation(875)), + + /** + * Represents a black axe (woodcutting). + */ + BLACK_AXE(1361, 6, 0.25D, new Animation(873)), + + /** + * Represents a mithril axe (woodcutting). + */ + MITHRIL_AXE(1355, 21, 0.30D, new Animation(871)), + + /** + * Represents an adamant axe (woodcutting). + */ + ADAMANT_AXE(1357, 31, 0.45D, new Animation(869)), + + /** + * Represents a rune axe (woodcutting). + */ + RUNE_AXE(1359, 41, 0.65D, new Animation(867)), + + /** + * Represents a dragon axe (woodcutting). + */ + DRAGON_AXE(6739, 61, 0.85D, new Animation(2846)), + + /** + * Represents a bronze pickaxe (mining). + */ + BRONZE_PICKAXE(1265, 1, 0.05D, new Animation(625)), + + /** + * Represents an iron pickaxe (mining). + */ + IRON_PICKAXE(1267, 1, 0.1D, new Animation(626)), + + /** + * Represents a steel pickaxe (mining). + */ + STEEL_PICKAXE(1269, 6, 0.2D, new Animation(627)), + + /** + * Represents a mithril pickaxe (mining). + */ + MITHRIL_PICKAXE(1273, 21, 0.30D, new Animation(629)), + + /** + * Represents an adamant pickaxe (mining). + */ + ADAMANT_PICKAXE(1271, 31, 0.45D, new Animation(628)), + + /** + * Represents a rune pickaxe (mining). + */ + RUNE_PICKAXE(1275, 41, 0.65D, new Animation(624)), + + /** + * Represents the Inferno Adze (woodcutting) + */ + INFERNO_ADZE(13661, 61, 0.85D, new Animation(10251)), + + /** + * Represents the Inferno Adze (mining) + */ + INFERNO_ADZE2(13661, 61, 1.0D, new Animation(10222)), + + HATCHET_CLASS1(Items.HATCHET_CLASS_1_14132, 1, 0.1, new Animation(10603)), + HATCHET_CLASS2(Items.HATCHET_CLASS_2_14134, 20, 0.3, new Animation(10604)), + HATCHET_CLASS3(Items.HATCHET_CLASS_3_14136, 40, 0.65, new Animation(10605)), + HATCHET_CLASS4(Items.HATCHET_CLASS_4_14138, 60, 0.85, new Animation(10606)), + HATCHET_CLASS5(Items.HATCHET_CLASS_5_14140, 80, 1.0, new Animation(10607)), + PICKAXE_CLASS1(Items.PICKAXE_CLASS_1_14122, 1, 0.1, new Animation(10608)), + PICKAXE_CLASS2(Items.PICKAXE_CLASS_2_14124, 20, 0.3, new Animation(10609)), + PICKAXE_CLASS3(Items.PICKAXE_CLASS_3_14126, 40, 0.65, new Animation(10610)), + PICKAXE_CLASS4(Items.PICKAXE_CLASS_4_14128, 60, 0.85, new Animation(10611)), + PICKAXE_CLASS5(Items.PICKAXE_CLASS_5_14130, 80, 1.0, new Animation(10612)), + HARPOON_CLASS1(Items.HARPOON_CLASS_1_14142, 1, 0.1, new Animation(10613)), + HARPOON_CLASS2(Items.HARPOON_CLASS_2_14144, 20, 0.3, new Animation(10614)), + HARPOON_CLASS3(Items.HARPOON_CLASS_3_14146, 40, 0.65, new Animation(10615)), + HARPOON_CLASS4(Items.HARPOON_CLASS_4_14148, 60, 0.85, new Animation(10616)), + HARPOON_CLASS5(Items.HARPOON_CLASS_5_14150, 80, 1.0, new Animation(10617)), + BUTTERFLY_NET_CLASS1(Items.BUTTERFLY_NET_CLASS_1_14152, 1, 0.1, new Animation(10618)), + BUTTERFLY_NET_CLASS2(Items.BUTTERFLY_NET_CLASS_2_14154, 20, 0.3, new Animation(10619)), + BUTTERFLY_NET_CLASS3(Items.BUTTERFLY_NET_CLASS_3_14156, 40, 0.65, new Animation(10620)), + BUTTERFLY_NET_CLASS4(Items.BUTTERFLY_NET_CLASS_4_14158, 60, 0.85, new Animation(10621)), + BUTTERFLY_NET_CLASS5(Items.BUTTERFLY_NET_CLASS_5_14160, 80, 1.0, new Animation(10622)); + + + /** + * The tool id. + */ + private final int id; + + /** + * The level required. + */ + private final int level; + + /** + * The ratio. + */ + private final double ratio; + + /** + * The animation. + */ + private final Animation animation; + + /** + * Constructs a new {@code SkillingTool} {@code Object}. + * @param id The tool item id. + * @param level The level required to use this. + * @param ratio The ratio. + * @param animation The animation. + */ + private SkillingTool(int id, int level, double ratio, Animation animation) { + this.id = id; + this.level = level; + this.ratio = ratio; + this.animation = animation; + } + + /** + * Gets the tool by the item id. + * @param itemId The item id. + * @return The skilling tool, or {@code null} if the tool wasn't found. + */ + public static SkillingTool forId(int itemId) { + for (SkillingTool tool : SkillingTool.values()) { + if (tool.id == itemId) { + return tool; + } + } + return null; + } + + /** + * Gets the hatchet used by the player. + * @param player The player. + * @return The hatchet. + */ + public static SkillingTool getHatchet(Player player) { + SkillingTool tool = null; + SkillingTool[] hatchetPriority = new SkillingTool[] { + SkillingTool.HATCHET_CLASS5, + SkillingTool.HATCHET_CLASS4, + SkillingTool.DRAGON_AXE, + SkillingTool.HATCHET_CLASS3, + SkillingTool.RUNE_AXE, + SkillingTool.ADAMANT_AXE, + SkillingTool.HATCHET_CLASS2, + SkillingTool.MITHRIL_AXE, + SkillingTool.BLACK_AXE, + SkillingTool.STEEL_AXE, + SkillingTool.HATCHET_CLASS1, + SkillingTool.IRON_AXE, + SkillingTool.BRONZE_AXE + }; + for(SkillingTool hatchet : hatchetPriority) { + if (checkTool(player, Skills.WOODCUTTING, hatchet)) { + tool = hatchet; + break; + } + } + if (checkTool(player, Skills.WOODCUTTING, SkillingTool.INFERNO_ADZE)) { + if(player.getSkills().getLevel(Skills.FIREMAKING) >= 92) { + tool = SkillingTool.INFERNO_ADZE; + } + } + return tool; + } + + /** + * Gets the pickaxe used by the player. + * @param player The player. + * @return The hatchet. + */ + public static SkillingTool getPickaxe(Player player) { + SkillingTool tool = null; + SkillingTool[] pickaxePriority = new SkillingTool[] { + SkillingTool.PICKAXE_CLASS5, + SkillingTool.PICKAXE_CLASS4, + SkillingTool.RUNE_PICKAXE, + SkillingTool.PICKAXE_CLASS3, + SkillingTool.ADAMANT_PICKAXE, + SkillingTool.PICKAXE_CLASS2, + SkillingTool.MITHRIL_PICKAXE, + SkillingTool.STEEL_PICKAXE, + SkillingTool.PICKAXE_CLASS1, + SkillingTool.IRON_PICKAXE, + SkillingTool.BRONZE_PICKAXE, + }; + for(SkillingTool pickaxe : pickaxePriority) { + if (checkTool(player, Skills.MINING, pickaxe)) { + tool = pickaxe; + break; + } + } + if (checkTool(player, Skills.MINING, SkillingTool.INFERNO_ADZE2)) { + if (player.getSkills().getLevel(Skills.FIREMAKING) >= 92) { + tool = SkillingTool.INFERNO_ADZE2; + } + } + return tool; + } + + public static SkillingTool getHarpoon(Player player) { + SkillingTool tool = null; + SkillingTool[] harpoonPriority = new SkillingTool[] { + SkillingTool.BUTTERFLY_NET_CLASS5, + SkillingTool.BUTTERFLY_NET_CLASS4, + SkillingTool.BUTTERFLY_NET_CLASS3, + SkillingTool.BUTTERFLY_NET_CLASS2, + SkillingTool.BUTTERFLY_NET_CLASS1, + }; + for(SkillingTool harpoon : harpoonPriority) { + if (checkTool(player, Skills.FISHING, harpoon)) { + tool = harpoon; + break; + } + } + return tool; + } + + public static SkillingTool getButterflyNet(Player player) { + SkillingTool tool = null; + SkillingTool[] butterflyNetPriority = new SkillingTool[] { + SkillingTool.BUTTERFLY_NET_CLASS5, + SkillingTool.BUTTERFLY_NET_CLASS4, + SkillingTool.BUTTERFLY_NET_CLASS3, + SkillingTool.BUTTERFLY_NET_CLASS2, + SkillingTool.BUTTERFLY_NET_CLASS1, + }; + for(SkillingTool butterflyNet : butterflyNetPriority) { + if (checkTool(player, Skills.HUNTER, butterflyNet)) { + tool = butterflyNet; + break; + } + } + return tool; + } + + public static SkillingTool getToolForSkill(Player player, int skill) { + switch(skill) { + case Skills.MINING: + return getPickaxe(player); + case Skills.WOODCUTTING: + return getHatchet(player); + case Skills.FISHING: + return getHarpoon(player); + case Skills.HUNTER: + return getButterflyNet(player); + default: + return null; + } + } + + /** + * Checks if the player has a tool and if he can use it. + * @param tool The tool. + * @return {@code True} if the tool is usable. + */ + public static boolean checkTool(Player player, int skillId, SkillingTool tool) { + if (player.getSkills().getStaticLevel(skillId) < tool.getLevel()) { + return false; + } + if (player.getEquipment().getNew(3).getId() == tool.getId()) { + return true; + } + return player.getInventory().contains(tool.getId(), 1); + } + + /** + * Gets the id. + * @return The id. + */ + public int getId() { + return id; + } + + /** + * Gets the level. + * @return The level. + */ + public int getLevel() { + return level; + } + + /** + * Gets the ratio. + * @return The ratio. + */ + public double getRatio() { + return ratio; + } + + /** + * Gets the animation. + * @return The animation. + */ + public Animation getAnimation() { + return animation; + } +} diff --git a/Server/src/main/content/data/tables/AllotmentSeedDropTable.java b/Server/src/main/content/data/tables/AllotmentSeedDropTable.java new file mode 100644 index 000000000..01b6f3f17 --- /dev/null +++ b/Server/src/main/content/data/tables/AllotmentSeedDropTable.java @@ -0,0 +1,94 @@ +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 javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import static core.api.ContentAPIKt.log; + +/** + * Handles the allotment seed drop table. + * @author Von Hresvelg + */ +public final class AllotmentSeedDropTable implements StartupListener { + + /** + * The item id of the item representing the allotment seed drop table slot in a drop + * table. + */ + public static final int SLOT_ITEM_ID = 14430; + + /** + * The allotment seed drop table. + */ + private static final List TABLE = new ArrayList<>(20); + + + /** + * Initialize needed objects for xml reading/writing + */ + static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + static DocumentBuilder builder; + + static { + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } + } + + public AllotmentSeedDropTable() throws ParserConfigurationException {} + + @Override + public void startup() { + if(ServerConstants.ASDT_DATA_PATH != null && !new File(ServerConstants.ASDT_DATA_PATH).exists()){ + log(this.getClass(), Log.ERR, "Can't locate ASDT file at " + ServerConstants.ASDT_DATA_PATH); + return; + } + parse(ServerConstants.ASDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Allotment Seed Drop Table from " + ServerConstants.ASDT_DATA_PATH); + } + + /** + * Parses the xml file for the allotment seed drop table. + * @param file the .xml file containing the allotment seed drop table. + */ + public static void parse(String file){ + try { + Document doc = builder.parse(file); + + NodeList itemNodes = doc.getElementsByTagName("item"); + for(int i = 0; i < itemNodes.getLength(); i++){ + Node itemNode = itemNodes.item(i); + if(itemNode.getNodeType() == Node.ELEMENT_NODE){ + Element item = (Element) itemNode; + int itemId = Integer.parseInt(item.getAttribute("id")); + int minAmt = Integer.parseInt(item.getAttribute("minAmt")); + int maxAmt = Integer.parseInt(item.getAttribute("maxAmt")); + int weight = Integer.parseInt(item.getAttribute("weight")); + + TABLE.add(new WeightedChanceItem(itemId,minAmt,maxAmt,weight)); + } + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + public static Item retrieve() {return RandomFunction.rollWeightedChanceTable(TABLE);} +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/BirdNest.java b/Server/src/main/content/data/tables/BirdNest.java similarity index 82% rename from Server/src/main/java/core/game/content/global/BirdNest.java rename to Server/src/main/content/data/tables/BirdNest.java index 2b56bc9ae..0552daa2e 100644 --- a/Server/src/main/java/core/game/content/global/BirdNest.java +++ b/Server/src/main/content/data/tables/BirdNest.java @@ -1,4 +1,4 @@ -package core.game.content.global; +package content.data.tables; import org.rs09.consts.Items; import core.game.node.entity.npc.drop.NPCDropTables; @@ -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. @@ -17,43 +20,45 @@ public enum BirdNest { SEED(new ChanceItem(5073, 1, 65), new ChanceItem(Items.ACORN_5312, 1, NPCDropTables.DROP_RATES[0]), new ChanceItem(Items.APPLE_TREE_SEED_5283, 1, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.WILLOW_SEED_5313, 1, NPCDropTables.DROP_RATES[0]), new ChanceItem(Items.BANANA_TREE_SEED_5284, 1, NPCDropTables.DROP_RATES[0]), - new ChanceItem(Items.ORANGE_TREE_SEED_5285, 1, NPCDropTables.DROP_RATES[1]), - new ChanceItem(Items.WILLOW_SEED_5313, 1, NPCDropTables.DROP_RATES[1]), - new ChanceItem(Items.CURRY_TREE_SEED_5286, 1, NPCDropTables.DROP_RATES[1]), - new ChanceItem(Items.MAPLE_SEED_5314, 1, NPCDropTables.DROP_RATES[2]), - new ChanceItem(Items.PINEAPPLE_SEED_5287, 1, NPCDropTables.DROP_RATES[2]), - new ChanceItem(Items.PAPAYA_TREE_SEED_5288, 1, NPCDropTables.DROP_RATES[2]), + new ChanceItem(Items.ORANGE_TREE_SEED_5285, 1, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.CURRY_TREE_SEED_5286, 1, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.MAPLE_SEED_5314, 1, NPCDropTables.DROP_RATES[1]), + new ChanceItem(Items.PINEAPPLE_SEED_5287, 1, NPCDropTables.DROP_RATES[1]), + new ChanceItem(Items.PAPAYA_TREE_SEED_5288, 1, NPCDropTables.DROP_RATES[1]), + new ChanceItem(Items.YEW_SEED_5315, 1, NPCDropTables.DROP_RATES[2]), new ChanceItem(Items.PALM_TREE_SEED_5289, 1, NPCDropTables.DROP_RATES[2]), new ChanceItem(Items.CALQUAT_TREE_SEED_5290, 1, NPCDropTables.DROP_RATES[2]), - new ChanceItem(Items.YEW_SEED_5315, 1, NPCDropTables.DROP_RATES[2]), - new ChanceItem(Items.MAGIC_SEED_5316, 1, NPCDropTables.DROP_RATES[3]), - new ChanceItem(Items.SPIRIT_SEED_5317, 1, NPCDropTables.DROP_RATES[3])), + new ChanceItem(Items.SPIRIT_SEED_5317, 1, NPCDropTables.DROP_RATES[3]), + new ChanceItem(Items.MAGIC_SEED_5316, 1, NPCDropTables.DROP_RATES[3])), RING(new ChanceItem(5074, 1, 30), new ChanceItem(Items.GOLD_RING_1635, 1, NPCDropTables.DROP_RATES[0]), new ChanceItem(Items.SAPPHIRE_RING_1637, 1, NPCDropTables.DROP_RATES[0]), - new ChanceItem(Items.EMERALD_RING_1639, 1, NPCDropTables.DROP_RATES[1]), - new ChanceItem(Items.RUBY_RING_1641, 1, NPCDropTables.DROP_RATES[2]), - new ChanceItem(Items.DIAMOND_RING_1643, 1, NPCDropTables.DROP_RATES[3])), + new ChanceItem(Items.EMERALD_RING_1639, 1, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.RUBY_RING_1641, 1, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.DIAMOND_RING_1643, 1, NPCDropTables.DROP_RATES[2])), RED(new ChanceItem(5070, 1, 5), new ChanceItem(5076)), GREEN(new ChanceItem(5071, 1, 5), new ChanceItem(5078)), BLUE(new ChanceItem(5072, 1, 5), new ChanceItem(5077)), RAVEN(new ChanceItem(11966, 1, 5), new ChanceItem(11964)), WYSON(new ChanceItem(7413, 1, 1), - new ChanceItem(Items.CABBAGE_SEED_5324, 1, NPCDropTables.DROP_RATES[0]), - new ChanceItem(Items.SWEETCORN_SEED_5320, 1, NPCDropTables.DROP_RATES[0]), - new ChanceItem(Items.TOMATO_SEED_5322, 1, NPCDropTables.DROP_RATES[0]), - new ChanceItem(Items.ONION_SEED_5319, 1, NPCDropTables.DROP_RATES[0]), - new ChanceItem(Items.POTATO_SEED_5318, 1, NPCDropTables.DROP_RATES[0]), - new ChanceItem(Items.LIMPWURT_SEED_5100, 1, NPCDropTables.DROP_RATES[1]), - new ChanceItem(Items.STRAWBERRY_SEED_5323, 1, NPCDropTables.DROP_RATES[1]), - new ChanceItem(Items.TOADFLAX_SEED_5296, 1, NPCDropTables.DROP_RATES[0]), - new ChanceItem(Items.WATERMELON_SEED_5321, 1, NPCDropTables.DROP_RATES[1]), + new ChanceItem(Items.POTATO_SEED_5318, 14, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.SWEETCORN_SEED_5320, 3, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.TOMATO_SEED_5322, 6, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.CABBAGE_SEED_5324, 9, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.LIMPWURT_SEED_5100, 2, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.ONION_SEED_5319, 11, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.STRAWBERRY_SEED_5323, 3, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.WATERMELON_SEED_5321, 2, NPCDropTables.DROP_RATES[0]), + new ChanceItem(Items.ACORN_5312, 1, NPCDropTables.DROP_RATES[2]), new ChanceItem(Items.RANARR_SEED_5295, 1, NPCDropTables.DROP_RATES[2]), + new ChanceItem(Items.WILLOW_SEED_5313, 1, NPCDropTables.DROP_RATES[2]), new ChanceItem(Items.MAPLE_SEED_5314, 1, NPCDropTables.DROP_RATES[2]), - new ChanceItem(Items.YEW_SEED_5315, 1, NPCDropTables.DROP_RATES[3]), - new ChanceItem(Items.MAGIC_SEED_5316, 1, NPCDropTables.DROP_RATES[3])); + new ChanceItem(Items.YEW_SEED_5315, 1, NPCDropTables.DROP_RATES[2]), + new ChanceItem(Items.MAGIC_SEED_5316, 1, NPCDropTables.DROP_RATES[3]), + new ChanceItem(Items.SPIRIT_SEED_5317, 1, NPCDropTables.DROP_RATES[3])); /** * The random nest items. @@ -91,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 new file mode 100644 index 000000000..13313bc59 --- /dev/null +++ b/Server/src/main/content/data/tables/CELEMinorTable.java @@ -0,0 +1,96 @@ +package content.data.tables; + +import core.api.StartupListener; +import core.ServerConstants; +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 javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +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. + * @author Crash + */ +public final class CELEMinorTable implements StartupListener { + + /** + * The item id of the item representing the C. Ele minor drop table slot in a drop + * table. + */ + public static final int SLOT_ITEM_ID = 799; // Crash: Item ID 799 is currently a null, blank paper note object. Unsure if used for cutscene/cutscene items, but hijacking this as C.Ele's item container as it'll never be dropped or obtained in normal gameplay. + + /** + * The rare drop table. + */ + private static final List TABLE = new ArrayList<>(20); + + + /** + * Initialize needed objects for xml reading/writing + */ + static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + static DocumentBuilder builder; + + static { + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } + } + + public CELEMinorTable() throws ParserConfigurationException {} + + @Override + public void startup() { + if(ServerConstants.CELEDT_DATA_PATH != null && !new File(ServerConstants.CELEDT_DATA_PATH).exists()){ + log(this.getClass(), Log.ERR, "Can't locate CELEDT file at " + ServerConstants.CELEDT_DATA_PATH); + return; + } + parse(ServerConstants.CELEDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Loaded up Chaos Elemental drop table from " + ServerConstants.CELEDT_DATA_PATH); + } + + /** + * Parses the xml file for the CELEDT. + * @param file the .xml file containing the CELEDT. + */ + public static void parse(String file){ + try { + Document doc = builder.parse(file); + + NodeList itemNodes = doc.getElementsByTagName("item"); + for(int i = 0; i < itemNodes.getLength(); i++){ + Node itemNode = itemNodes.item(i); + if(itemNode.getNodeType() == Node.ELEMENT_NODE){ + Element item = (Element) itemNode; + int itemId = Integer.parseInt(item.getAttribute("id")); + int minAmt = Integer.parseInt(item.getAttribute("minAmt")); + int maxAmt = Integer.parseInt(item.getAttribute("maxAmt")); + int weight = Integer.parseInt(item.getAttribute("weight")); + + TABLE.add(new WeightedChanceItem(itemId,minAmt,maxAmt,weight)); + } + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + public static Item retrieve(){ + return RandomFunction.rollWeightedChanceTable(TABLE); + } +} \ No newline at end of file diff --git a/Server/src/main/content/data/tables/GemDropTable.java b/Server/src/main/content/data/tables/GemDropTable.java new file mode 100644 index 000000000..e9b486d28 --- /dev/null +++ b/Server/src/main/content/data/tables/GemDropTable.java @@ -0,0 +1,96 @@ +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 javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import static core.api.ContentAPIKt.log; + +/** + * Handles the gem drop table. + * @author Von Hresvelg + */ +public final class GemDropTable implements StartupListener { + + /** + * The item id of the item representing the gem drop table slot in a drop + * table. + */ + public static final int SLOT_ITEM_ID = 14426; + + /** + * The gem drop table. + */ + private static final List TABLE = new ArrayList<>(20); + + + /** + * Initialize needed objects for xml reading/writing + */ + static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + static DocumentBuilder builder; + + static { + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } + } + + public GemDropTable() throws ParserConfigurationException {} + + @Override + public void startup() { + if(ServerConstants.GDT_DATA_PATH != null && !new File(ServerConstants.GDT_DATA_PATH).exists()){ + log(this.getClass(), Log.ERR, "Can't locate GDT file at " + ServerConstants.GDT_DATA_PATH); + return; + } + parse(ServerConstants.GDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Gem Drop Table from " + ServerConstants.GDT_DATA_PATH); + } + + /** + * Parses the xml file for the gem drop table. + * @param file the .xml file containing the gem drop table. + */ + public static void parse(String file){ + try { + Document doc = builder.parse(file); + + NodeList itemNodes = doc.getElementsByTagName("item"); + for(int i = 0; i < itemNodes.getLength(); i++){ + Node itemNode = itemNodes.item(i); + if(itemNode.getNodeType() == Node.ELEMENT_NODE){ + Element item = (Element) itemNode; + int itemId = Integer.parseInt(item.getAttribute("id")); + int minAmt = Integer.parseInt(item.getAttribute("minAmt")); + int maxAmt = Integer.parseInt(item.getAttribute("maxAmt")); + int weight = Integer.parseInt(item.getAttribute("weight")); + + TABLE.add(new WeightedChanceItem(itemId,minAmt,maxAmt,weight)); + } + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + public static Item retrieve(){ + return RandomFunction.rollWeightedChanceTable(TABLE); + } +} \ No newline at end of file diff --git a/Server/src/main/content/data/tables/HerbDropTable.java b/Server/src/main/content/data/tables/HerbDropTable.java new file mode 100644 index 000000000..efd03da2b --- /dev/null +++ b/Server/src/main/content/data/tables/HerbDropTable.java @@ -0,0 +1,96 @@ +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 javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import static core.api.ContentAPIKt.log; + +/** + * Handles the herb drop table. + * @author Von Hresvelg + */ +public final class HerbDropTable implements StartupListener { + + /** + * The item id of the item representing the herb drop table slot in a drop + * table. + */ + public static final int SLOT_ITEM_ID = 14424; + + /** + * The herb drop table. + */ + private static final List TABLE = new ArrayList<>(20); + + + /** + * Initialize needed objects for xml reading/writing + */ + static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + static DocumentBuilder builder; + + static { + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } + } + + public HerbDropTable() throws ParserConfigurationException {} + + @Override + public void startup() { + if(ServerConstants.HDT_DATA_PATH != null && !new File(ServerConstants.HDT_DATA_PATH).exists()){ + log(this.getClass(), Log.ERR, "Can't locate HDT file at " + ServerConstants.HDT_DATA_PATH); + return; + } + parse(ServerConstants.HDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Herb Drop Table from " + ServerConstants.HDT_DATA_PATH); + } + + /** + * Parses the xml file for the herb drop table. + * @param file the .xml file containing the herb drop table. + */ + public static void parse(String file){ + try { + Document doc = builder.parse(file); + + NodeList itemNodes = doc.getElementsByTagName("item"); + for(int i = 0; i < itemNodes.getLength(); i++){ + Node itemNode = itemNodes.item(i); + if(itemNode.getNodeType() == Node.ELEMENT_NODE){ + Element item = (Element) itemNode; + int itemId = Integer.parseInt(item.getAttribute("id")); + int minAmt = Integer.parseInt(item.getAttribute("minAmt")); + int maxAmt = Integer.parseInt(item.getAttribute("maxAmt")); + int weight = Integer.parseInt(item.getAttribute("weight")); + + TABLE.add(new WeightedChanceItem(itemId,minAmt,maxAmt,weight)); + } + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + public static Item retrieve(){ + return RandomFunction.rollWeightedChanceTable(TABLE); + } +} \ No newline at end of file diff --git a/Server/src/main/content/data/tables/RareDropTable.kt b/Server/src/main/content/data/tables/RareDropTable.kt new file mode 100644 index 000000000..253fc529a --- /dev/null +++ b/Server/src/main/content/data/tables/RareDropTable.kt @@ -0,0 +1,114 @@ +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 org.rs09.consts.Items +import org.w3c.dom.Element +import org.w3c.dom.Node +import java.io.File +import javax.xml.parsers.DocumentBuilder +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.parsers.ParserConfigurationException + +/** + * Handles the rare drop table. + * @author Ceikry + */ +class RareDropTable : StartupListener { + override fun startup() { + if (ServerConstants.RDT_DATA_PATH != null && !File(ServerConstants.RDT_DATA_PATH).exists()) { + log(this::class.java, Log.ERR, "Can't locate RDT file at " + ServerConstants.RDT_DATA_PATH) + return + } + parse(ServerConstants.RDT_DATA_PATH) + log(this::class.java, Log.FINE, "Initialized Rare Drop Table from " + ServerConstants.RDT_DATA_PATH) + } + + companion object { + private val TABLE: WeightBasedTable = object : WeightBasedTable() { + override fun roll(receiver: Entity?): ArrayList { + val items = ArrayList(guaranteedItems) + var effectiveWeight = totalWeight + val p = if (receiver is Player) receiver else null + if (p != null && shouldRemoveNothings(p)) + effectiveWeight -= nothingWeight + + if (this.size == 1) { + items.add(get(0)) + } else if (!this.isEmpty()) { + var rngWeight = RandomFunction.randomDouble(effectiveWeight) + for (item in this.shuffled()) { + if (item.id == Items.DWARF_REMAINS_0) continue + rngWeight -= item.weight + if (rngWeight <= 0) { + items.add(item) + break + } + } + } + return convertWeightedItems(items, receiver) + } + + private val nothingWeight: Double + get() { + var sum = 0.0 + for (i in this) { + if (i.id == Items.DWARF_REMAINS_0) + sum += i.weight + } + return sum + } + } + + /** + * Initialize needed objects for xml reading/writing + */ + var factory = DocumentBuilderFactory.newInstance() + var builder: DocumentBuilder? = null + + init { + try { + builder = factory.newDocumentBuilder() + } catch (e: ParserConfigurationException) { + e.printStackTrace() + } + } + + /** + * Parses the xml file for the RDT. + * @param file the .xml file containing the RDT. + */ + fun parse(file: String?) { + try { + val doc = builder!!.parse(file) + val itemNodes = doc.getElementsByTagName("item") + for (i in 0 until itemNodes.length) { + val itemNode = itemNodes.item(i) + if (itemNode.nodeType == Node.ELEMENT_NODE) { + val item = itemNode as Element + val itemId = item.getAttribute("id").toInt() + val minAmt = item.getAttribute("minAmt").toInt() + val maxAmt = item.getAttribute("maxAmt").toInt() + val weight = item.getAttribute("weight").toInt() + TABLE.add(WeightedItem(itemId, minAmt, maxAmt, weight.toDouble(), false)) + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun retrieve(receiver: Entity?): Item? { + return TABLE.roll(receiver).getOrNull(0) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/data/tables/RareSeedDropTable.java b/Server/src/main/content/data/tables/RareSeedDropTable.java new file mode 100644 index 000000000..0b892270d --- /dev/null +++ b/Server/src/main/content/data/tables/RareSeedDropTable.java @@ -0,0 +1,96 @@ +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 javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import static core.api.ContentAPIKt.log; + +/** + * Handles the rare seed drop table. + * @author Von Hresvelg + */ +public final class RareSeedDropTable implements StartupListener { + + /** + * The item id of the item representing the rare seed drop table slot in a drop + * table. + */ + public static final int SLOT_ITEM_ID = 14428; + + /** + * The rare seed drop table. + */ + private static final List TABLE = new ArrayList<>(20); + + + /** + * Initialize needed objects for xml reading/writing + */ + static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + static DocumentBuilder builder; + + static { + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } + } + + public RareSeedDropTable() throws ParserConfigurationException {} + + @Override + public void startup() { + if(ServerConstants.RSDT_DATA_PATH != null && !new File(ServerConstants.RSDT_DATA_PATH).exists()){ + log(this.getClass(), Log.ERR, "Can't locate RSDT file at " + ServerConstants.RSDT_DATA_PATH); + return; + } + parse(ServerConstants.RSDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Rare Seed Drop Table from " + ServerConstants.RSDT_DATA_PATH); + } + + /** + * Parses the xml file for the rare seed drop table. + * @param file the .xml file containing the rare seed drop table. + */ + public static void parse(String file){ + try { + Document doc = builder.parse(file); + + NodeList itemNodes = doc.getElementsByTagName("item"); + for(int i = 0; i < itemNodes.getLength(); i++){ + Node itemNode = itemNodes.item(i); + if(itemNode.getNodeType() == Node.ELEMENT_NODE){ + Element item = (Element) itemNode; + int itemId = Integer.parseInt(item.getAttribute("id")); + int minAmt = Integer.parseInt(item.getAttribute("minAmt")); + int maxAmt = Integer.parseInt(item.getAttribute("maxAmt")); + int weight = Integer.parseInt(item.getAttribute("weight")); + + TABLE.add(new WeightedChanceItem(itemId,minAmt,maxAmt,weight)); + } + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + public static Item retrieve(){ + return RandomFunction.rollWeightedChanceTable(TABLE); + } +} \ No newline at end of file diff --git a/Server/src/main/content/data/tables/UncommonSeedDropTable.java b/Server/src/main/content/data/tables/UncommonSeedDropTable.java new file mode 100644 index 000000000..1c76ca555 --- /dev/null +++ b/Server/src/main/content/data/tables/UncommonSeedDropTable.java @@ -0,0 +1,96 @@ +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 javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import static core.api.ContentAPIKt.log; + +/** + * Handles the uncommon seed drop table. + * @author Von Hresvelg + */ +public final class UncommonSeedDropTable implements StartupListener { + + /** + * The item id of the item representing the uncommon seed drop table slot in a drop + * table. + */ + public static final int SLOT_ITEM_ID = 14422; + + /** + * The uncommon seed drop table. + */ + private static final List TABLE = new ArrayList<>(20); + + + /** + * Initialize needed objects for xml reading/writing + */ + static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + static DocumentBuilder builder; + + static { + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } + } + + public UncommonSeedDropTable() throws ParserConfigurationException {} + + @Override + public void startup() { + if(ServerConstants.USDT_DATA_PATH != null && !new File(ServerConstants.USDT_DATA_PATH).exists()){ + log(this.getClass(), Log.ERR, "Can't locate USDT file at " + ServerConstants.USDT_DATA_PATH); + return; + } + parse(ServerConstants.USDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Uncommon Seed Drop Table from " + ServerConstants.USDT_DATA_PATH); + } + + /** + * Parses the xml file for the uncommon seed drop table. + * @param file the .xml file containing the uncommon seed drop table. + */ + public static void parse(String file){ + try { + Document doc = builder.parse(file); + + NodeList itemNodes = doc.getElementsByTagName("item"); + for(int i = 0; i < itemNodes.getLength(); i++){ + Node itemNode = itemNodes.item(i); + if(itemNode.getNodeType() == Node.ELEMENT_NODE){ + Element item = (Element) itemNode; + int itemId = Integer.parseInt(item.getAttribute("id")); + int minAmt = Integer.parseInt(item.getAttribute("minAmt")); + int maxAmt = Integer.parseInt(item.getAttribute("maxAmt")); + int weight = Integer.parseInt(item.getAttribute("weight")); + + TABLE.add(new WeightedChanceItem(itemId,minAmt,maxAmt,weight)); + } + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + public static Item retrieve(){ + return RandomFunction.rollWeightedChanceTable(TABLE); + } +} \ No newline at end of file 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/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 new file mode 100644 index 000000000..001aeae6f --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/JobManager.kt @@ -0,0 +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 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 java.lang.Integer.min + +/** + * 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 + + private fun hasLevelRequirement(job: ProductionJobs): Boolean { + if (player == null) return false + if (player.isArtificial) return false + + return getStatLevel(player, job.skill) >= job.lvlReq + } + + fun hasJob(): Boolean = this.job != null + + // 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 + + fun generate(npc: NPC) { + if (player == null) return + if (player.isArtificial) return + + val instance = getInstance(player) + + // 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 } + + val job = potentialJobs.randomOrNull() ?: return + + 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 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 new file mode 100644 index 000000000..c34a67811 --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/JobType.kt @@ -0,0 +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 { + 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 new file mode 100644 index 000000000..8d09d7dfd --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/WorkForInteractionListener.kt @@ -0,0 +1,45 @@ +package content.global.activity.jobs + +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 { + override fun defineListeners() { + 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 + } + + openDialogue(player, StartJobDialogueFile(), node as NPC) + return@on true + } + } +} \ 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/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/kotlin/rs09/game/content/global/worldevents/penguinhns/LarryHandler.kt b/Server/src/main/content/global/activity/penguinhns/LarryHandler.kt similarity index 84% rename from Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/LarryHandler.kt rename to Server/src/main/content/global/activity/penguinhns/LarryHandler.kt index 38382f1c6..4a4b3ba90 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/LarryHandler.kt +++ b/Server/src/main/content/global/activity/penguinhns/LarryHandler.kt @@ -1,21 +1,18 @@ -package rs09.game.content.global.worldevents.penguinhns +package content.global.activity.penguinhns -import api.ContentAPI +import core.api.* import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE -class LarryHandler(player: Player? = null) : DialoguePlugin(player){ +class LarryHandler(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { options("Can I have a spy notebook?","Can I have a hint?","I'd like to turn in my points.").also { stage = 0; return true } } - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return LarryHandler(player) } @@ -43,7 +40,7 @@ class LarryHandler(player: Player? = null) : DialoguePlugin(player){ 11 -> { val hint = Penguin.values()[PenguinManager.penguins.random()].hint - npcl(FacialExpression.FRIENDLY, "One is $hint") + npcl(core.game.dialogue.FacialExpression.FRIENDLY, "One is $hint") stage = END_DIALOGUE } @@ -76,8 +73,8 @@ class LarryHandler(player: Player? = null) : DialoguePlugin(player){ return } - val level = ContentAPI.getStatLevel(player, skill) - System.out.println("Level: $level") + val level = getStatLevel(player, skill) + val expGained = points?.toDouble()?.times((level * 25)) System.out.print("exp: $expGained") player.skills.addExperience(skill,expGained!!) diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/NotebookHandler.kt b/Server/src/main/content/global/activity/penguinhns/NotebookHandler.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/NotebookHandler.kt rename to Server/src/main/content/global/activity/penguinhns/NotebookHandler.kt index 40424ea68..15462eefe 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/NotebookHandler.kt +++ b/Server/src/main/content/global/activity/penguinhns/NotebookHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.content.global.worldevents.penguinhns +package content.global.activity.penguinhns import core.cache.def.impl.ItemDefinition import core.game.interaction.OptionHandler diff --git a/Server/src/main/content/global/activity/penguinhns/PenguinHNSEvent.kt b/Server/src/main/content/global/activity/penguinhns/PenguinHNSEvent.kt new file mode 100644 index 000000000..39bc2419b --- /dev/null +++ b/Server/src/main/content/global/activity/penguinhns/PenguinHNSEvent.kt @@ -0,0 +1,26 @@ +package content.global.activity.penguinhns + +import core.ServerStore +import core.api.StartupListener +import core.api.log +import core.plugin.ClassScanner +import core.tools.Log +import org.json.simple.JSONObject + +class PenguinHNSEvent : StartupListener { + val manager = PenguinManager() + + override fun startup() { + manager.rebuildVars() + ClassScanner.definePlugins(LarryHandler(), NotebookHandler()) + log(this::class.java, Log.FINE, "Penguin HNS initialized.") + } + + companion object { + @JvmStatic + fun getStoreFile() : JSONObject { + return ServerStore.getArchive("weekly-penguinhns") + } + } + +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinManager.kt b/Server/src/main/content/global/activity/penguinhns/PenguinManager.kt similarity index 75% rename from Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinManager.kt rename to Server/src/main/content/global/activity/penguinhns/PenguinManager.kt index 7fdd5bf62..48cdc7dec 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinManager.kt +++ b/Server/src/main/content/global/activity/penguinhns/PenguinManager.kt @@ -1,12 +1,14 @@ -package rs09.game.content.global.worldevents.penguinhns +package content.global.activity.penguinhns import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import rs09.game.system.SystemLogger +import core.tools.SystemLogger import core.game.world.map.Location import org.json.simple.JSONArray import org.json.simple.JSONObject -import rs09.ServerStore.toJSONArray +import core.ServerStore.Companion.toJSONArray +import core.api.log +import core.tools.Log import java.util.* class PenguinManager{ @@ -18,10 +20,9 @@ class PenguinManager{ fun registerTag(player: Player, location: Location){ val ordinal = Penguin.forLocation(location)?.ordinal ?: -1 - if(tagMapping[ordinal] == null){ - tagMapping[ordinal] = JSONArray() - } - tagMapping[ordinal]?.add(player.username.toLowerCase()) + val list = tagMapping[ordinal] ?: JSONArray() + list.add(player.username.toLowerCase()) + tagMapping[ordinal] = list updateStoreFile() } @@ -29,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.forEach { (ordinal,taggers) -> - SystemLogger.logInfo("$ordinal - ${taggers.first()}") + tagMapping.filter { it.value.isNotEmpty() }.forEach { (ordinal,taggers) -> + log(this::class.java, Log.FINE, "$ordinal - ${taggers.first()}") val tag = JSONObject() tag["ordinal"] = ordinal tag["taggers"] = taggers @@ -44,12 +45,14 @@ class PenguinManager{ } fun rebuildVars() { - if(PenguinHNSEvent.getStoreFile().isEmpty()) { + if(!PenguinHNSEvent.getStoreFile().containsKey("spawned-penguins")) { penguins = spawner.spawnPenguins(10) PenguinHNSEvent.getStoreFile()["spawned-penguins"] = penguins.toJSONArray() + tagMapping.clear() for (p in penguins) { tagMapping.put(p, JSONArray()) } + updateStoreFile() } else { val spawnedOrdinals = (PenguinHNSEvent.getStoreFile()["spawned-penguins"] as JSONArray).map { it.toString().toInt() } spawner.spawnPenguins(spawnedOrdinals) @@ -63,8 +66,4 @@ class PenguinManager{ penguins = spawnedOrdinals.toMutableList() } } - - fun log(message: String){ - SystemLogger.logInfo("[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 new file mode 100644 index 000000000..2d6abe7bb --- /dev/null +++ b/Server/src/main/content/global/activity/penguinhns/PenguinSpawner.kt @@ -0,0 +1,114 @@ +package content.global.activity.penguinhns + +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import org.rs09.consts.NPCs +import core.game.worldevents.WorldEvents + +class PenguinSpawner { + + fun spawnPenguins(amount: Int): ArrayList { + var counter = 0 + val availableOrdinals = (0 until Penguin.values().size).toMutableList() + val spawnedOrdinals = ArrayList() + while(counter < amount){ + val peng = Penguin.values()[availableOrdinals.random()] + availableOrdinals.remove(peng.ordinal) + spawnedOrdinals.add(peng.ordinal) + NPC(peng.id,peng.location) + .also { PenguinManager.npcs.add(it);it.isNeverWalks = true; it.isWalks = false}.init() + counter++ + } + return spawnedOrdinals + } + + fun spawnPenguins(ordinals: List){ + ordinals.forEach { + val peng = Penguin.values()[it] + NPC(peng.id,peng.location) + .also { PenguinManager.npcs.add(it); it.isNeverWalks = true; it.isWalks = false }.init() + } + } +} + +enum class Penguin(val id: Int, val hint: String, val location: Location){ + CACTUS_1(NPCs.CACTUS_8107,"located in the northern desert.", Location(3310, 3157, 0)), + CACTUS_2(NPCs.CACTUS_8107,"located in the southern desert.",Location.create(3259, 3052, 0)), + BUSH_1(NPCs.BUSH_8105,"located between Fremennik and barbarians.",Location.create(2532, 3588, 0)), + BUSH_2(NPCs.BUSH_8105,"located where banana smugglers dwell.",Location.create(2740, 3233, 0)), + BUSH_3(NPCs.BUSH_8105,"located south of Ardougne.",Location.create(2456, 3092, 0)), + BUSH_4(NPCs.BUSH_8105,"located deep in the jungle.",Location.create(2832, 3053, 0)), + BUSH_5(NPCs.BUSH_8105,"located where eagles fly.",Location.create(2326, 3516, 0)), + BUSH_6(NPCs.BUSH_8105,"located south of Ardougne.",Location.create(2513, 3154, 0)), + BUSH_7(NPCs.BUSH_8105,"located near a big tree surrounded by short people.",Location.create(2387, 3451, 0)), + 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 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)), //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)), + 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)), //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() + + fun forLocation(location: Location): Penguin?{ + return locationMap[location.toString()] + } + } +} diff --git a/Server/src/main/content/global/activity/penguinhns/PenguinSpyingHandler.kt b/Server/src/main/content/global/activity/penguinhns/PenguinSpyingHandler.kt new file mode 100644 index 000000000..386220027 --- /dev/null +++ b/Server/src/main/content/global/activity/penguinhns/PenguinSpyingHandler.kt @@ -0,0 +1,53 @@ +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 +import core.game.world.GameWorld +import core.game.world.update.flag.context.Animation +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class PenguinSpyingHandler : InteractionListener { + + override fun defineListeners() { + on(PENGUINS, IntType.NPC, "spy-on"){ player, node -> + val npc = node.asNpc() + if(PenguinManager.hasTagged(player, npc.location)){ + player.sendMessage("You've already tagged this penguin.") + } else { + GameWorld.submit(SpyPulse(player, npc)) + playJingle(player, 345) + } + return@on true + } + } + + val PENGUINS = intArrayOf(8104,8105,8107,8108,8109,8110) + + class SpyPulse(val player: Player, val npc: NPC) : Pulse() { + var stage = 0 + val curPoints = player.getAttribute("phns:points",0) + + val ANIMATION = Animation(10355) + + override fun pulse(): Boolean { + when(stage++){ + 0 -> player.lock().also { player.animator.animate(ANIMATION) } + 1 -> player.sendMessage("You manage to spy on the penguin.").also { + player.setAttribute("/save:phns:points",curPoints + 1) + PenguinManager.registerTag(player, npc.location) + player.unlock() + } + 2 -> return true + } + return false + } + + override fun stop() { + super.stop() + player.unlock() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt new file mode 100644 index 000000000..074ab27a6 --- /dev/null +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt @@ -0,0 +1,234 @@ +package content.global.activity.shootingstar + +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.world.map.Location +import core.ServerStore.Companion.getBoolean +import core.ServerStore.Companion.getInt +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()) { + 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 + "Falador East Bank" to Location.create(3030, 3349, 0), // Falador East Bank + "Rimmington mining site" to Location.create(2974, 3240, 0), // Rimmington Mine 2974, 3240, 0) + "Karamja northwestern mining site" to Location.create(2737, 3223, 0), // Karamja northwestern mining site/Brimhaven gold rocks + "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 (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 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 + "Yanille Bank" to Location.create(2603, 3087, 0), // Yanille Bank (Wizards' Guild) + "Port Khazard mine" to Location.create(2626, 3140, 0), // Mining spot north-east of Yanille / Port Khazard mine + "Al Kharid bank" to Location.create(3276, 3176, 0), // Al Kharid bank + "Al Kharid mining site" to Location.create(3296, 3297, 0), // Al Kharid mine (Scorpion mine) + "Duel Arena bank chest" to Location.create(3342, 3267, 0), // Duel Arena bank chest + "Kharidian Desert clay mine" to Location.create(3426, 3159, 0), // Desert clay mine / Ruins of Uzer mine + "Nardah mining site" to Location.create(3320, 2872, 0), // Kharidian Desert gold mine / Vulture mine + "Nardah bank" to Location.create(3434, 2888, 0), // Nardah bank + "Granite and sandstone quarry" to Location.create(3170, 2913, 0), // Granite and sandstone quarry / Quarry on map + "South-east Varrock mine" to Location.create(3292, 3353, 0), // South-east Varrock mine + "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) + "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) + "Gnome stronghold Bank" to Location.create(2460, 3432, 0), // Gnome stronghold bank + "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 + "Wilderness Volcano bank" to Location.create(3188, 3695, 0), // Wilderness Volcano bank + "Wilderness hobgoblin mine" to Location.create(3020, 3809, 0), // Wilderness hobgoblin mine + "Pirates' Hideout mine" to Location.create(3059, 3940, 0), // Pirates' Hideout mine + "Lava Maze mining site" to Location.create(3062, 3885, 0), // Wilderness rune mine + "Mage Arena bank" to Location.create(3093, 3962, 0) // Mage Arena bank + ) + val starSprite = NPC(8091) + var location = "Canifis Bank" + var maxDust = level.totalStardust + var dustLeft = level.totalStardust + var starObject = Scenery(level.objectId, crash_locations.get(location)) + var isDiscovered = false + var ticks = 0 + var isSpawned = false + var spriteSpawned = false + var firstStar = true + var selfBots = ArrayList() + var activePlayers = HashSet() + + /** + * Degrades a ShootingStar (or removes the starObject and spawns a Star Sprite if it's the last star) + */ + fun degrade() { + if(level.ordinal == 0) { + spawnSprite() + return + } + level = getNextType() + maxDust = level.totalStardust + dustLeft = level.totalStardust + ShootingStarPlugin.getStoreFile()["level"] = level.ordinal + ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft + + val newStar = Scenery(level.objectId, starObject.location) + SceneryBuilder.replace(starObject, newStar) + starObject = newStar + } + + private fun getNextType(): ShootingStarType { + return ShootingStarType.values()[level.ordinal - 1] + } + + /** + * Fires the shooting star (spawns a new one). Only used when spawning new shooting stars, not for downgrading existing ones. + */ + fun fire() { + SceneryBuilder.remove(starObject) + clearSprite() + SceneryBuilder.add(starObject) + if(!isSpawned) { + (0..2).forEach { + selfBots.add(ShootingStarBot.new()) + } + } + if (level.ordinal + 1 > 5) { + selfBots.filter { it.isIdle() }.forEach { it.activate(true) } + } + isSpawned = true + Repository.sendNews("A shooting star level ${level.ordinal + 1} just crashed near ${location}!") + ShootingStarPlugin.getStoreFile()["level"] = level.ordinal + ShootingStarPlugin.getStoreFile()["location"] = location + ShootingStarPlugin.getStoreFile()["isDiscovered"] = isDiscovered + ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft + } + + /** + * Rebuilds some of the variables with new information. + */ + fun rebuildVars(){ + // 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 + starObject = Scenery(level.objectId, crash_locations.get(location)) + } + + 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 + } + + /** + * Decrement the amount of dust left in the current star and degrade it if the amount left is 0 or less. + */ + fun decDust() { + if(--dustLeft <= 0) degrade() + ShootingStarPlugin.getStoreFile()["dustLeft"] = dustLeft + } + + /** + * Proxy method for starting to mine a shooting star + */ + fun mine(player: Player) { + player.pulseManager.run(ShootingStarMiningPulse(player, starObject, this)) + } + + /** + * Prospecting shooting stars. + */ + fun prospect(player: Player) { + player.dialogueInterpreter.sendDialogue("This is a size " + (level.ordinal + 1) + " star. A Mining level of at least " + miningLevel + " is", "required to mine this layer. There is $dustLeft stardust remaining", "until the next layer.") + } + + /** + * Notifies the star when a player begins mining it + */ + fun notifyNewPlayer(player: Player) { + if(activePlayers.size < 3){ + val leavingBot = selfBots.firstOrNull { it.isMining() } + leavingBot?.sleep() + } + activePlayers.add(player) + } + + /** + * Notifies the star when a player stops mining it + */ + fun notifyPlayerLeave(player: Player) { + activePlayers.remove(player) + if (activePlayers.size < 3){ + val newBot = selfBots.firstOrNull() { it.isIdle() } + newBot?.activate(true) + } + } + + /** + * Gets the mining level required based on the star's current level ordinal. + */ + val miningLevel: Int + get() = (level.ordinal + 1) * 10 +} + +/** + * Various levels of shooting stars + * @author Ceikry + */ +enum class ShootingStarType(val objectId: Int, val exp: Int, val totalStardust: Int,val rate: Double) { + LEVEL_1(38668, 14, 1200, 0.05), + LEVEL_2(38667, 25, 700, 0.1), + LEVEL_3(38666, 29, 439, 0.3), + LEVEL_4(38665, 32, 250, 0.4), + LEVEL_5(38664, 47, 175, 0.5), + LEVEL_6(38663, 71, 80, 0.70), + LEVEL_7(38662, 114, 40, 0.80), + LEVEL_8(38661, 145, 25, 0.85), + LEVEL_9(38660, 210, 15, 0.95); +} diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt new file mode 100644 index 000000000..5c5ecf033 --- /dev/null +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt @@ -0,0 +1,152 @@ +package content.global.activity.shootingstar + +import core.api.* +import core.game.node.scenery.Scenery +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 +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. + */ +class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: ShootingStar) : SkillPulse(player, node) { + /** + * The amount of ticks it takes to get star dust. + */ + private var ticks = 0 + override fun start() { + if(!star.isSpawned) return + + if(!player.isArtificial) { + star.notifyNewPlayer(player) + } + super.start() + } + + override fun stop() { + super.stop() + + if(!player.isArtificial){ + star.notifyPlayerLeave(player) + } + } + + override fun checkRequirements(): Boolean { + tool = SkillingTool.getPickaxe(player) + if (!star.starObject.isActive || !star.isSpawned) { + return false + } + //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) + rewardXP(player, Skills.MINING, bonusXp.toDouble()) + Repository.sendNews(player.username + " is the discoverer of the crashed star near " + star.location + "!") + 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 + } + if (tool == null) { + player.packetDispatch.sendMessage("You do not have a pickaxe to use.") + return false + } + if (player.inventory.freeSlots() < 1 && !player.inventory.contains(ShootingStarPlugin.STAR_DUST, 1)) { + player.dialogueInterpreter.sendDialogue("Your inventory is too full to hold any more stardust.") + return false + } + return true + } + + override fun animate() { + player.animate(tool.animation) + } + + override fun reward(): Boolean { + if (++ticks % 4 != 0) { + return false + } + if (!checkReward()) { + return false + } + if (GameWorld.settings?.isDevMode == true) { + star.dustLeft = 1 + } + + 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 + if(player.getAttribute("shooting-star:bonus-xp", 0) <= 0) { + player.sendMessage("You have obtained all of your bonus xp from the star.") + } + } + + player.skills.addExperience(Skills.MINING, xp) + if (ShootingStarPlugin.getStarDust(player) < 200) { + player.inventory.add(Item(ShootingStarPlugin.STAR_DUST, 1)) + } + if (ServerConstants.SHOOTING_STAR_RING && hasAnItem(player, Items.ANCIENT_BLUEPRINT_14651).container == null) { + rollBlueprint(player) + } + + star.decDust() + return false + } + + fun rollBlueprint(player: Player){ + val chance = when(star.level){ + ShootingStarType.LEVEL_9 -> 250 + ShootingStarType.LEVEL_8 -> 500 + ShootingStarType.LEVEL_7 -> 750 + ShootingStarType.LEVEL_6 -> 1000 + ShootingStarType.LEVEL_5 -> 2000 + ShootingStarType.LEVEL_4 -> 3000 + ShootingStarType.LEVEL_3 -> 4000 + ShootingStarType.LEVEL_2 -> 5000 + ShootingStarType.LEVEL_1 -> 10000 + } + + if(RandomFunction.roll(chance)){ + addItemOrDrop(player, Items.ANCIENT_BLUEPRINT_14651, 1) + sendMessage(player, colorize("%RWhile mining the star you find an ancient-looking blueprint.")) + sendNews("${player.username} found an Ancient Blueprint while mining a shooting star!") + } + } + + override fun message(type: Int) { + when (type) { + 0 -> player.packetDispatch.sendMessage("You swing your pickaxe at the rock.") + } + } + + /** + * Checks if the player gets rewarded. + * @return `True` if so. + */ + private fun checkReward(): Boolean { + val skill = Skills.MINING + val level = 1 + player.skills.getLevel(skill) + player.familiarManager.getBoost(skill) + val hostRatio: Double = Math.random() * (100.0 * star.level.rate) + val clientRatio: Double = Math.random() * ((level - star.miningLevel) * (1.0 + tool.ratio)) + return hostRatio < clientRatio + } +} diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt new file mode 100644 index 000000000..7368b7689 --- /dev/null +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt @@ -0,0 +1,190 @@ +package content.global.activity.shootingstar + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager +import core.game.node.entity.skill.Skills +import org.json.simple.JSONObject +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.ServerStore +import core.ServerStore.Companion.getBoolean +import core.game.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +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) { + if(star.isSpawned && !star.spriteSpawned) + sendMessage(player, "News: A shooting star (Level ${star.level.ordinal + 1}) has just crashed near the ${star.location}!") + } + + 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.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() { + on(Scenery.SHOOTING_STAR_NOTICEBOARD_38669, IntType.SCENERY, "read") { player, _ -> + var index = 0 + scoreboardEntries.forEach { entry -> + val timeElapsed = secondsToTicks(GameWorld.ticks - entry.time) / 60 + setInterfaceText(player, "$timeElapsed minutes ago", scoreboardIface, index + 6) + setInterfaceText(player, entry.player, scoreboardIface, index + 11) + ++index + } + openInterface(player, scoreboardIface) + return@on true + } + + on(SHOOTING_STARS, IntType.SCENERY, "mine"){ player, _ -> + star.mine(player) + return@on true + } + + on(SHOOTING_STARS, IntType.SCENERY, "prospect"){ player, _ -> + star.prospect(player) + return@on true + } + + on(RING, IntType.ITEM, "rub", "operate"){ player, node -> + if(getRingStoreFile().getBoolean(player.username.toLowerCase())){ + sendDialogue(player, "The ring is still recharging.") + return@on 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() + } + } + } + } + + openDialogue(player, RingDialogue(star)) + return@on true + } + } + + override fun defineCommands() { + 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, description = "Rebuilds and fires the shooting star event immediately.") { _, _ -> + star.rebuildVars() + star.fire() + } + + define("resetsprite", Privilege.ADMIN, description = "Resets your star sprite reward cooldown.") { player, _ -> + player.savedData.globalData.starSpriteDelay = 0L + } + } + + override fun startup() { + log(this::class.java, Log.FINE, "Shooting Stars initialized.") + } + + private data class ScoreboardEntry(val player: String, val time: Int) + + companion object { + private val star = ShootingStar() + private val tickDelay = if(GameWorld.settings?.isDevMode == true) 200 else 25000 + private val scoreboardEntries = ArrayList() + private val scoreboardIface = 787 + val SHOOTING_STARS = ShootingStarType.values().map(ShootingStarType::objectId).toIntArray() + val STAR_DUST = Items.STARDUST_13727 + val RING = Items.RING_OF_THE_STAR_SPRITE_14652 + + + @JvmStatic fun submitScoreBoard(player: Player) + { + if(scoreboardEntries.size == 5) + scoreboardEntries.removeAt(0) + scoreboardEntries.add(ScoreboardEntry(player.username, GameWorld.ticks)) + } + + @JvmStatic fun getStar(): ShootingStar + { + return star + } + + @JvmStatic fun getStoreFile() : JSONObject { + return ServerStore.getArchive("shooting-star") + } + + @JvmStatic fun getRingStoreFile() : JSONObject { + return ServerStore.getArchive("daily-star-ring") + } + + fun getStarDust(player: Player): Int { + return player.inventory.getAmount(STAR_DUST) + player.bank.getAmount(STAR_DUST) + } + } +} 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/java/core/game/content/global/worldevents/shootingstar/StarChartPlugin.java b/Server/src/main/content/global/activity/shootingstar/StarChartPlugin.java similarity index 91% rename from Server/src/main/java/core/game/content/global/worldevents/shootingstar/StarChartPlugin.java rename to Server/src/main/content/global/activity/shootingstar/StarChartPlugin.java index b5373e3dd..9ab977822 100644 --- a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/StarChartPlugin.java +++ b/Server/src/main/content/global/activity/shootingstar/StarChartPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.global.worldevents.shootingstar; +package content.global.activity.shootingstar; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; @@ -8,7 +8,7 @@ import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.plugin.PluginManifest; @PluginManifest(name="ShootingStars") @@ -24,7 +24,7 @@ public class StarChartPlugin extends ComponentPlugin { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new StarChartOptions()); + ClassScanner.definePlugin(new StarChartOptions()); ComponentDefinition.forId(iface.getId()).setPlugin(this); return this; } diff --git a/Server/src/main/content/global/activity/shootingstar/StarSpriteDialogue.kt b/Server/src/main/content/global/activity/shootingstar/StarSpriteDialogue.kt new file mode 100644 index 000000000..7eebc668e --- /dev/null +++ b/Server/src/main/content/global/activity/shootingstar/StarSpriteDialogue.kt @@ -0,0 +1,283 @@ +package content.global.activity.shootingstar + +import core.api.Container +import core.api.* +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.RandomFunction +import org.json.simple.JSONObject +import org.rs09.consts.Items +import core.ServerStore +import core.ServerStore.Companion.getBoolean +import core.tools.END_DIALOGUE +import core.tools.secondsToTicks +import core.tools.colorize +import java.util.concurrent.TimeUnit + +/** + * Dialogue for the star sprite. + */ +@Initializable +class StarSpriteDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + + /** + * The cosmic rune item id. + */ + val COSMIC_RUNE = 564 + + /** + * The astral rune item id. + */ + val ASTRAL_RUNE = 9075 + + /** + * The gold ore item id. + */ + val GOLD_ORE = 445 + + /** + * The coins id. + */ + val COINS = 995 + + /** + * The amplifier amount. + */ + val AMPLIFIER = 1.0 + + + /** + * Constructs the star sprite dialogue. + */ + fun StarSpriteDialogue() { + /* + * empty. + */ + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin? { + return StarSpriteDialogue(player) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + if (inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !getAttribute(player, "star-ring:bp-shown", false)) { + npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I see you got ahold of a blueprint of those silly old rings we used to make.") + stage = 1000 + } else if (inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && getAttribute(player, "star-ring:bp-shown", false)) { + playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "So about those rings...") + stage = 2000 + } else if (getStoreFile().getBoolean(player.username.toLowerCase()) || !player.getInventory().contains(ShootingStarPlugin.STAR_DUST, 1)) { + npc("Hello, strange creature.") + stage = 0 + } else { + npc("Thank you for helping me out of here.") + stage = 50 + } + return true + } + + 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") + stage++ + } + 1 -> { + npc("your race.") + stage++ + } + 2 -> { + options("What's a star sprite?", "What are you going to do without your star?", "I thought stars were huge balls of burning gas.", "Well, I'm glad you're okay.") + stage++ + } + 3 -> when (buttonId) { + 1 -> { + player("What's a star sprite?") + stage = 10 + } + 2 -> { + player("What are you going to do without your star?") + stage = 20 + } + 3 -> { + player("I thought stars were huge balls of burning gas.") + stage = 30 + } + 4 -> { + player("Well, I'm glad you're okay.") + stage = 40 + } + } + 10 -> { + npc("We're what makes the stars in the sky shine. I made", "this star shine when it was in the sky.") + stage++ + } + 11 -> { + options("What are you going to do without your star?", "I thought stars were huge balls of burning gas.", "Well, I'm glad you're okay.") + stage++ + } + 12 -> when (buttonId) { + 1 -> { + player("What are you going to do without your star?") + stage = 20 + } + 2 -> { + player("I thought stars were huge balls of burning gas.") + stage = 30 + } + 3 -> { + player("Well, I'm glad you're okay.") + stage = 40 + } + } + 20 -> { + npc("Don't worry about me. I'm sure I'll find some good", "rocks around here and get back up into the sky in no", "time.") + stage++ + } + 21 -> { + options("What's a star sprite?", "I thought stars were huge balls of burning gas.", "Well, I'm glad you're okay.") + stage++ + } + 22 -> when (buttonId) { + 1 -> { + player("What's a star sprite?") + stage = 10 + } + 2 -> { + player("I thought stars were huge balls of burning gas.") + stage = 30 + } + 3 -> { + player("Well, I'm glad you're okay.") + stage = 40 + } + } + 30 -> { + npc("Most of them are, but a lot of shooting stars on this", "plane of the multiverse are rocks with star sprites in", "them.") + stage++ + } + 31 -> { + options("What's a star sprite?", "What are you going to do without your star?", "Well, I'm glad you're okay.") + stage++ + } + 32 -> when (buttonId) { + 1 -> { + player("What's a star sprite?") + stage = 10 + } + 2 -> { + player("What are you going to do without your star?") + stage = 20 + } + 3 -> { + player("Well, I'm glad you're okay.") + stage = 40 + } + } + 40 -> { + npc("Thank you.") + stage++ + } + 41 -> end() + 50 -> { + val wearingRing = inEquipment(player, Items.RING_OF_THE_STAR_SPRITE_14652) + val dust = if (player.getInventory().getAmount(ShootingStarPlugin.STAR_DUST) > 200) 200 else player.getInventory().getAmount(ShootingStarPlugin.STAR_DUST) + if (player.getInventory().remove(Item(ShootingStarPlugin.STAR_DUST, dust))) { + val cosmicRunes = (Math.ceil(0.76 * dust) * AMPLIFIER).toInt() + val astralRunes = (Math.ceil(0.26 * dust) * AMPLIFIER).toInt() + val goldOre = (Math.ceil(0.1 * dust) * AMPLIFIER).toInt() + val coins = (Math.ceil(250.0 * dust) * AMPLIFIER).toInt() // limits the amount of gp per turn-in to 90k, same as 2009 rs, and minimum from all stardust from a level 1 star works out to 50k, same as rs in 2009 + player.getInventory().add(Item(COSMIC_RUNE, cosmicRunes), player) + player.getInventory().add(Item(ASTRAL_RUNE, astralRunes), player) + player.getInventory().add(Item(GOLD_ORE, goldOre), player) + player.getInventory().add(Item(COINS, coins), player) + npc("I have rewarded you by making it so you can mine", "extra ore for the next 15 minutes. Also, have $cosmicRunes", "cosmic runes, $astralRunes astral runes, $goldOre gold ore and $coins", "coins.") + + getStoreFile()[player.username.toLowerCase()] = true //flag daily as completed + + 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() + val amount = when(item){ + Items.COSMIC_RUNE_564 -> cosmicRunes + Items.ASTRAL_RUNE_9075 -> astralRunes + Items.GOLD_ORE_445 -> goldOre + Items.COINS_995 -> coins + else -> 0 + } + rollForRingBonus(player, item, amount) + } + } + + if(!inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !inBank(player, Items.ANCIENT_BLUEPRINT_14651) && RandomFunction.roll(500)){ + addItemOrDrop(player, Items.ANCIENT_BLUEPRINT_14651, 1) + sendMessage(player, colorize("%RThe Star Sprite dropped what looks like some ancient piece of paper and you pick it up.")) + sendNews("${player.username} found an Ancient Blueprint while mining a shooting star!") + } + stage = 52 + } + 52 -> end() + + //Inauthentic ring-based dialogue + 1000 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Oh, you mean this?").also { stage++ } + 1001 -> player.dialogueInterpreter.sendItemMessage(Items.ANCIENT_BLUEPRINT_14651, "You show the blueprint to the Star Sprite.").also { stage++ } + 1002 -> npcl(core.game.dialogue.FacialExpression.ASKING, "Yeah, that's the one, alright!").also { stage++ } + 1003 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I'll tell you what.. if you can get ahold of the resources needed to make one, I'm sure me or one of my kin would craft it for you.").also { stage++ } + 1004 -> playerl(core.game.dialogue.FacialExpression.ASKING, "You'd just do that for me? For free?").also { stage++ } + 1005 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I don't see why not. We used to make these for fun and hand them out to adventurers all the time.").also { stage++ } + 1006 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Well, thanks! So.. what do we need to make one?").also { stage++ } + 1007 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Looking at the blueprint here...").also { stage++ } + 1008 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, it seems we need a ring mould, a silver bar, a cut dragonstone and 200 stardust. Oh, and make sure to bring this blueprint with you.").also { stage++ } + 1009 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Thanks, I'll get right on it!").also { stage++ } + 1010 -> playerl(core.game.dialogue.FacialExpression.ASKING, "So just to make sure I've got it right, I need a ring mould, a silver bar, a cut dragonstone and 200 stardust, as well as this blueprint?").also { stage++ } + 1011 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Yeah, you've got it, human. Any of my kin should be able to do this for you.").also { stage++; setAttribute(player, "/save:star-ring:bp-shown", true) } + 1012 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Thanks!").also { stage = END_DIALOGUE } + + 2000 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, did you bring the components to make it, human?").also { stage++ } + 2001 -> if(inInventory(player, Items.DRAGONSTONE_1615,1) && inInventory(player, Items.RING_MOULD_1592, 1) && inInventory(player, Items.STARDUST_13727, 200) && inInventory(player, Items.SILVER_BAR_2355, 1)){ + playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, I have them right here, friend.").also { stage++ } + } else { + playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I'm afraid not, what did I need again?").also { stage = 2100 } + } + 2002 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Excellent, give me just a moment here...").also { stage++ } + 2003 -> sendDialogue("You watch as the Star Sprite casts some strange spell.").also { stage++ } + 2004 -> if(removeItem(player, Items.SILVER_BAR_2355, Container.INVENTORY) && removeItem(player, Items.DRAGONSTONE_1615, Container.INVENTORY) && removeItem(player, Item(Items.STARDUST_13727, 200), Container.INVENTORY)){ + addItem(player, Items.RING_OF_THE_STAR_SPRITE_14652) + player.dialogueInterpreter.sendItemMessage(Items.RING_OF_THE_STAR_SPRITE_14652, "The Star Sprite hands you a strange ring.").also { stage++ } + } else end() + 2005 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "There you go, I hope you enjoy it!").also { stage++ } + 2006 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Thank you!").also { stage = END_DIALOGUE } + + 2100 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "A ring mould, a cut dragonstone, a silver bar and 200 stardust.").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray? { + return intArrayOf(8091) + } + + fun rollForRingBonus(player: Player, bonusId: Int, bonusBaseAmt: Int){ + if(RandomFunction.roll(3)){ + 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) + sendMessage(player, colorize("%RYour ring shines brightly as if surging with energy and then fades out.")) + } else if(RandomFunction.roll(25)){ + getStoreFile()[player.username.toLowerCase()] = false //flag daily as uncompleted + sendMessage(player, colorize("%RYour ring vibrates briefly as if surging with power, and then stops.")) + } + } + + fun getStoreFile(): JSONObject{ + return ServerStore.getArchive("daily-shooting-star") + } + +} diff --git a/Server/src/main/content/global/activity/ttrail/ClueLevel.java b/Server/src/main/content/global/activity/ttrail/ClueLevel.java new file mode 100644 index 000000000..91a9ef0d4 --- /dev/null +++ b/Server/src/main/content/global/activity/ttrail/ClueLevel.java @@ -0,0 +1,197 @@ +package content.global.activity.ttrail; + +import core.game.component.Component; +import core.game.container.access.InterfaceContainer; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import org.rs09.consts.Items; +import core.game.world.GameWorld; +import core.tools.RandomFunction; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +import core.api.IfaceSettingsBuilder; + +import core.game.system.config.ClueRewardParser; + +import static core.api.ContentAPIKt.addItemOrDrop; + +/** + * A clue scroll level. + */ +public enum ClueLevel { + EASY(Items.CASKET_2714, 1, 5), + MEDIUM(Items.CASKET_2717, 1, 6), + HARD(Items.CASKET_2720, 1, 8), + UNKNOWN(0, 0, 0) + ; + + public int casketId, minSteps, maxSteps; + + private ClueLevel(int casketId, int minSteps, int maxSteps) { + this.casketId = casketId; + this.minSteps = minSteps; + this.maxSteps = maxSteps; + } + + private static ClueLevel getLevelForCasket(Item casket) { + switch (casket.getId()) { + case Items.CASKET_2714: + return ClueLevel.EASY; + case Items.CASKET_2717: + return ClueLevel.MEDIUM; + case Items.CASKET_2720: + return ClueLevel.HARD; + } + + return ClueLevel.UNKNOWN; + } + + public static void open(Player player, Item casket) { + if (casket == null || !player.getInventory().containsItem(casket)) { + return; + } + + TreasureTrailManager playerTrails = TreasureTrailManager.getInstance(player); + + boolean trailCompleted = playerTrails.isCompleted(); + boolean isDevMode = GameWorld.getSettings().isDevMode(); + + ClueLevel clueLevel = getLevelForCasket(casket); + + if (trailCompleted || isDevMode) { + List rewards = rollLoot(player, clueLevel); + + player.getInterfaceManager().open(new Component(364)); + + if (player.getInventory().remove(casket)) { + long rewardValue = 0L; + + for (Item reward : rewards) { + addItemOrDrop(player, reward.getId(), reward.getAmount()); + rewardValue += reward.getValue(); + } + + playerTrails.incrementClues(clueLevel); + playerTrails.clearTrail(); + + player.sendMessage("Well done, you've completed the Treasure Trail!"); + player.sendMessage( + getChatColor(clueLevel) + + "You have completed " + + playerTrails.getCompletedClues(clueLevel) + + " " + + clueLevel.getName().toLowerCase() + + " clues." + ); + + player.sendMessage( + "Your clue is worth approximately " + + NumberFormat.getInstance().format(rewardValue) + + " coins!" + ); + + int clueIfaceSettings = new IfaceSettingsBuilder().enableAllOptions().build(); + player.getPacketDispatch().sendIfaceSettings(clueIfaceSettings, 4, 364, 0, 6); + InterfaceContainer.generateItems( + player, + rewards.toArray(new Item[] {}), + new String[] {""}, + 364, 4, 3, 3 + ); + } + return; + } + + Item newClue = ClueScrollPlugin.getClue(clueLevel); + + if (casket != null && player.getInventory().remove(casket, casket.getSlot(), true)) { + player.getInventory().replace(newClue, casket.getSlot()); + } else { + player.getInventory().add(newClue); + } + + playerTrails.setClueId(newClue.getId()); + player.getDialogueInterpreter().sendItemMessage(newClue, "You've found another clue!"); + } + + public static List rollLoot(Player player, ClueLevel level) { + ArrayList loot = new ArrayList(); + int itemCount = RandomFunction.random(1,6); + + if (level == ClueLevel.HARD) { + itemCount = Math.max(itemCount, RandomFunction.random(4,6)); + } + + for (; itemCount > 0; itemCount--) { + switch (level) { + case EASY: + loot.addAll(ClueRewardParser.getEasyTable().roll(player)); + break; + case MEDIUM: + loot.addAll(ClueRewardParser.getMedTable().roll(player)); + break; + case HARD: + loot.addAll(ClueRewardParser.getHardTable().roll(player)); + break; + } + } + + if (level == ClueLevel.HARD && RandomFunction.random(100) == 50) { + loot.addAll(ClueRewardParser.getRareTable().roll(player)); + } + + return loot; + } + + /** + * Gets the Chat color to send on completed clues. + * @param level The clue level. + * @return the chat color. + */ + public static String getChatColor(ClueLevel level) { + if (level == ClueLevel.HARD) { + return ""; + } + if (level == ClueLevel.MEDIUM) { + return ""; + } + return ""; + } + /** + * Gets the maximum length. + * + * @return the length. + */ + public int getMaximumLength() { + return maxSteps; + } + + /** + * Gets the minimum length. + * + * @return the length. + */ + public int getMinimumLength() { + return minSteps; + } + + /** + * Gets the bcasket. + * + * @return the casket + */ + public Item getCasket() { + return new Item(casketId); + } + + /** + * Gets the name of the clue level. + * @return the name. + */ + public String getName() { + return toString(); + } +} diff --git a/Server/src/main/java/core/game/content/ttrail/ClueScrollPlugin.java b/Server/src/main/content/global/activity/ttrail/ClueScrollPlugin.java similarity index 83% rename from Server/src/main/java/core/game/content/ttrail/ClueScrollPlugin.java rename to Server/src/main/content/global/activity/ttrail/ClueScrollPlugin.java index 4fe1d3a11..13efad3c6 100644 --- a/Server/src/main/java/core/game/content/ttrail/ClueScrollPlugin.java +++ b/Server/src/main/content/global/activity/ttrail/ClueScrollPlugin.java @@ -1,9 +1,10 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.game.component.Component; import core.game.node.entity.player.Player; import core.game.node.item.Item; -import rs09.game.system.SystemLogger; +import core.tools.Log; +import core.tools.SystemLogger; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBuilder; @@ -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 @@ -78,7 +81,7 @@ public abstract class ClueScrollPlugin extends MapZone implements Plugin /** * Rewards the player with a casket. * @param player the player. - * @param casket if we give a ckaset. + * @param casket if we give a casket. */ public void reward(Player player, boolean casket) { Item clue = player.getInventory().getItem(new Item(getClueId())); @@ -107,14 +110,14 @@ public abstract class ClueScrollPlugin extends MapZone implements Plugin * @param clue the clue. */ public void nextStage(Player player, Item clue) { - if (!player.getTreasureTrailManager().hasTrail() || player.getTreasureTrailManager().hasTrail() && clue.getId() != player.getTreasureTrailManager().getClueId()) { - player.getTreasureTrailManager().startTrail(this); + if (!TreasureTrailManager.getInstance(player).hasTrail() || TreasureTrailManager.getInstance(player).hasTrail() && clue.getId() != TreasureTrailManager.getInstance(player).getClueId()) { + TreasureTrailManager.getInstance(player).startTrail(this); } - int currentStage = player.getTreasureTrailManager().getTrailStage(); - if (currentStage >= player.getTreasureTrailManager().getTrailLength()) { - player.getTreasureTrailManager().clearTrail(); + int currentStage = TreasureTrailManager.getInstance(player).getTrailStage(); + if (currentStage >= TreasureTrailManager.getInstance(player).getTrailLength()) { + TreasureTrailManager.getInstance(player).clearTrail(); } else { - player.getTreasureTrailManager().incrementStage(); + TreasureTrailManager.getInstance(player).incrementStage(); } } @@ -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("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("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/java/core/game/content/ttrail/CoordinateCluePlugin.java b/Server/src/main/content/global/activity/ttrail/CoordinateCluePlugin.java similarity index 99% rename from Server/src/main/java/core/game/content/ttrail/CoordinateCluePlugin.java rename to Server/src/main/content/global/activity/ttrail/CoordinateCluePlugin.java index 6f7b48c5e..b09067d27 100644 --- a/Server/src/main/java/core/game/content/ttrail/CoordinateCluePlugin.java +++ b/Server/src/main/content/global/activity/ttrail/CoordinateCluePlugin.java @@ -1,4 +1,4 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.game.world.map.Location; import core.plugin.Plugin; diff --git a/Server/src/main/java/core/game/content/ttrail/CoordinateClueScroll.java b/Server/src/main/content/global/activity/ttrail/CoordinateClueScroll.java similarity index 90% rename from Server/src/main/java/core/game/content/ttrail/CoordinateClueScroll.java rename to Server/src/main/content/global/activity/ttrail/CoordinateClueScroll.java index 2fd0096ba..b3c43a196 100644 --- a/Server/src/main/java/core/game/content/ttrail/CoordinateClueScroll.java +++ b/Server/src/main/content/global/activity/ttrail/CoordinateClueScroll.java @@ -1,4 +1,4 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -61,17 +61,17 @@ public abstract class CoordinateClueScroll extends MapClueScroll { @Override public void dig(Player player) { - /*int killed = player.getAttribute("killed-wizard", -1); - if (getLevel() == ClueLevel.HARD && killed == -1) { + int killedWizardClueId = player.getAttribute("killed-wizard", -1); + if (getLevel() == ClueLevel.HARD && (killedWizardClueId == -1 || killedWizardClueId != getClueId())) { NPC wizard = player.getAttribute("t-wizard", null); if (wizard != null && wizard.isActive()) { return; } spawnWizard(player); return; - }*/ + } super.dig(player); - //player.removeAttribute("killed-wizard"); + player.removeAttribute("killed-wizard"); } /** @@ -98,5 +98,4 @@ public abstract class CoordinateClueScroll extends MapClueScroll { public String getClue() { return clue; } - } diff --git a/Server/src/main/java/core/game/content/ttrail/EmoteCluePlugin.java b/Server/src/main/content/global/activity/ttrail/EmoteCluePlugin.java similarity index 97% rename from Server/src/main/java/core/game/content/ttrail/EmoteCluePlugin.java rename to Server/src/main/content/global/activity/ttrail/EmoteCluePlugin.java index b66f6326e..afcb7601d 100644 --- a/Server/src/main/java/core/game/content/ttrail/EmoteCluePlugin.java +++ b/Server/src/main/content/global/activity/ttrail/EmoteCluePlugin.java @@ -1,9 +1,9 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.game.node.entity.player.link.emote.Emotes; import core.game.world.map.zone.ZoneBorders; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Initializes the emote clue plugins. @@ -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 @@ -140,7 +140,7 @@ public final class EmoteCluePlugin extends EmoteClueScroll { register(new EmoteCluePlugin("draynor-market-yawn", 2737, ClueLevel.EASY, emote, new int[][] { { 1097 }, { 1191 }, { 1295 } }, "Yawn in Draynor
Marketplace.
Equip studded leather
chaps, an iron
kiteshield and a steel
longsword.", new ZoneBorders(3075, 3245, 3086, 3255))); register(new EmoteCluePlugin("castle-wars-yawn", 2739, ClueLevel.MEDIUM, emote, Emotes.SHRUG, new int[][] { { 1698 }, { 1329 }, WILDY_CAPES }, "Yawn in the Castle
Wars lobby. Shrug
before you talk to me.
Equip ruby amulet, a
mithril scimitar and a
Wilderness cape.", new ZoneBorders(2434, 3061, 2464, 3102))); register(new EmoteCluePlugin("rogue-gen-yawn", 2741, ClueLevel.HARD, emote, new int[][] { { 1183 }, { 2487 }, { 1275 } }, "Yawn in the rogues'
general store. Beware
of double agents!
Equip an adamant
square shield, blue
dragon vambraces
and a rune pickaxe.", new ZoneBorders(3024, 3699, 3027, 3704))); - PluginManager.definePlugin(new UriNPC()); + ClassScanner.definePlugin(new UriNPC()); return this; } diff --git a/Server/src/main/java/core/game/content/ttrail/EmoteClueScroll.java b/Server/src/main/content/global/activity/ttrail/EmoteClueScroll.java similarity index 98% rename from Server/src/main/java/core/game/content/ttrail/EmoteClueScroll.java rename to Server/src/main/content/global/activity/ttrail/EmoteClueScroll.java index b559fae2f..e4e9100d8 100644 --- a/Server/src/main/java/core/game/content/ttrail/EmoteClueScroll.java +++ b/Server/src/main/content/global/activity/ttrail/EmoteClueScroll.java @@ -1,4 +1,4 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/ttrail/MapCluePlugin.java b/Server/src/main/content/global/activity/ttrail/MapCluePlugin.java similarity index 99% rename from Server/src/main/java/core/game/content/ttrail/MapCluePlugin.java rename to Server/src/main/content/global/activity/ttrail/MapCluePlugin.java index c82e0343a..b1713ff8c 100644 --- a/Server/src/main/java/core/game/content/ttrail/MapCluePlugin.java +++ b/Server/src/main/content/global/activity/ttrail/MapCluePlugin.java @@ -1,4 +1,4 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.game.world.map.Location; import core.game.world.map.zone.ZoneBorders; diff --git a/Server/src/main/java/core/game/content/ttrail/MapClueScroll.java b/Server/src/main/content/global/activity/ttrail/MapClueScroll.java similarity index 93% rename from Server/src/main/java/core/game/content/ttrail/MapClueScroll.java rename to Server/src/main/content/global/activity/ttrail/MapClueScroll.java index a0b5b5e88..2b772e8ba 100644 --- a/Server/src/main/java/core/game/content/ttrail/MapClueScroll.java +++ b/Server/src/main/content/global/activity/ttrail/MapClueScroll.java @@ -1,7 +1,7 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; -import core.game.content.global.action.DigAction; -import core.game.content.global.action.DigSpadeHandler; +import core.game.global.action.DigAction; +import core.game.global.action.DigSpadeHandler; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -61,8 +61,7 @@ public abstract class MapClueScroll extends ClueScrollPlugin { p.sendMessage("Nothing interesting happens."); return false; } - reward(p, false); - level.open(p, null); + reward(p); return true; } } diff --git a/Server/src/main/java/core/game/content/ttrail/SaradominWizardNPC.java b/Server/src/main/content/global/activity/ttrail/SaradominWizardNPC.java similarity index 91% rename from Server/src/main/java/core/game/content/ttrail/SaradominWizardNPC.java rename to Server/src/main/content/global/activity/ttrail/SaradominWizardNPC.java index 79202bdc4..b2b357690 100644 --- a/Server/src/main/java/core/game/content/ttrail/SaradominWizardNPC.java +++ b/Server/src/main/content/global/activity/ttrail/SaradominWizardNPC.java @@ -1,7 +1,7 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.npc.AbstractNPC; @@ -10,8 +10,8 @@ import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.plugin.Plugin; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MultiSwingHandler; /** * Handles saradomin npc. @@ -124,7 +124,7 @@ public final class SaradominWizardNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (!(entity instanceof Player)) { return false; } @@ -132,7 +132,7 @@ public final class SaradominWizardNPC extends AbstractNPC { Player p = entity.asPlayer(); return p == player; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override diff --git a/Server/src/main/java/core/game/content/ttrail/TreasureTrailManager.java b/Server/src/main/content/global/activity/ttrail/TreasureTrailManager.java similarity index 75% rename from Server/src/main/java/core/game/content/ttrail/TreasureTrailManager.java rename to Server/src/main/content/global/activity/ttrail/TreasureTrailManager.java index fb34cc829..e9a68dd1b 100644 --- a/Server/src/main/java/core/game/content/ttrail/TreasureTrailManager.java +++ b/Server/src/main/content/global/activity/ttrail/TreasureTrailManager.java @@ -1,18 +1,19 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; +import core.api.LoginListener; +import core.api.PersistPlayer; import core.game.node.entity.player.Player; import core.tools.RandomFunction; +import org.jetbrains.annotations.NotNull; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import java.nio.ByteBuffer; - /** * Handles the treasure trail of a player. * @author Vexia */ -public final class TreasureTrailManager { +public final class TreasureTrailManager implements LoginListener, PersistPlayer { /** * The ids of the clues. @@ -58,6 +59,53 @@ public final class TreasureTrailManager { this.player = player; } + public TreasureTrailManager() {this.player = null;} + + @Override + public void login(@NotNull Player player) { + TreasureTrailManager instance = new TreasureTrailManager(player); + player.setAttribute("tt-manager", instance); + } + + @Override + public void parsePlayer(@NotNull Player player, @NotNull JSONObject data) { + TreasureTrailManager instance = getInstance(player); + JSONObject ttData = (JSONObject) data.get("treasureTrails"); + if(ttData == null) return; + JSONArray cc = (JSONArray) ttData.get("completedClues"); + for(int i = 0; i < cc.size(); i++) + { + instance.completedClues[i] = Integer.parseInt(cc.get(i).toString()); + } + if(ttData.containsKey("trail")) + { + JSONObject trail = (JSONObject) ttData.get("trail"); + instance.clueId = Integer.parseInt(trail.get("clueId").toString()); + instance.trailLength = Integer.parseInt(trail.get("length").toString()); + instance.trailStage = Integer.parseInt(trail.get("stage").toString()); + } + } + + @Override + public void savePlayer(@NotNull Player player, @NotNull JSONObject save) { + TreasureTrailManager instance = getInstance(player); + JSONObject treasureTrailManager = new JSONObject(); + if(instance.hasTrail()){ + JSONObject trail = new JSONObject(); + trail.put("clueId", Integer.toString(instance.clueId)); + trail.put("length", Integer.toString(instance.trailLength)); + trail.put("stage", Integer.toString(instance.trailStage)); + treasureTrailManager.put("trail",trail); + } + JSONArray completedClues = new JSONArray(); + for(int clue : instance.completedClues) + { + completedClues.add(Integer.toString(clue)); + } + treasureTrailManager.put("completedClues",completedClues); + save.put("treasureTrails",treasureTrailManager); + } + public void parse(JSONObject data){ JSONArray cc = (JSONArray) data.get("completedClues"); for(int i = 0; i < cc.size(); i++){ @@ -239,4 +287,8 @@ public final class TreasureTrailManager { this.trailStage = trailStage; } + public static TreasureTrailManager getInstance(Player player) + { + return player.getAttribute("tt-manager", new TreasureTrailManager()); + } } diff --git a/Server/src/main/java/core/game/content/ttrail/TreasureTrailPlugin.java b/Server/src/main/content/global/activity/ttrail/TreasureTrailPlugin.java similarity index 88% rename from Server/src/main/java/core/game/content/ttrail/TreasureTrailPlugin.java rename to Server/src/main/content/global/activity/ttrail/TreasureTrailPlugin.java index f70aed69b..607eb169a 100644 --- a/Server/src/main/java/core/game/content/ttrail/TreasureTrailPlugin.java +++ b/Server/src/main/content/global/activity/ttrail/TreasureTrailPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; @@ -9,7 +9,7 @@ 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.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.node.item.GroundItem; import core.game.node.item.Item; import core.game.node.item.ItemPlugin; @@ -17,8 +17,7 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.system.config.NPCConfigParser; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles the clue scroll options. @@ -41,14 +40,14 @@ public final class TreasureTrailPlugin extends OptionHandler { ItemDefinition.forId(level.getCasket().getId()).getHandlers().put("option:open", this); } ItemDefinition.forId(CoordinateClueScroll.SEXTANT.getId()).getHandlers().put("option:look through", this); - PluginManager.definePlugin(new MapCluePlugin()); - PluginManager.definePlugin(new ClueItemPlugin()); - PluginManager.definePlugin(new EmoteCluePlugin()); - PluginManager.definePlugin(new TTrailOptionHandler()); - PluginManager.definePlugin(new SextantComponentPlugin()); - PluginManager.definePlugin(new CoordinateCluePlugin()); - PluginManager.definePlugin(new SaradominWizardNPC()); - PluginManager.definePlugin(new ZamorakWizardNPC()); + ClassScanner.definePlugin(new MapCluePlugin()); + ClassScanner.definePlugin(new ClueItemPlugin()); + ClassScanner.definePlugin(new EmoteCluePlugin()); + ClassScanner.definePlugin(new TTrailOptionHandler()); + ClassScanner.definePlugin(new SextantComponentPlugin()); + ClassScanner.definePlugin(new CoordinateCluePlugin()); + ClassScanner.definePlugin(new SaradominWizardNPC()); + ClassScanner.definePlugin(new ZamorakWizardNPC()); return this; } @@ -69,7 +68,7 @@ public final class TreasureTrailPlugin extends OptionHandler { if (!player.getInventory().containsItem(node.asItem())) { return true; } - ClueLevel.forCasket((Item) node).open(player, (Item) node); + ClueLevel.open(player, (Item) node); break; case "look through": player.getInterfaceManager().open(new Component(365)); @@ -137,7 +136,7 @@ public final class TreasureTrailPlugin extends OptionHandler { @Override public Item getItem(Item item, NPC npc) { - return ClueScrollPlugin.getClue(npc.getDefinition().getConfiguration(NPCConfigParser.CLUE_LEVEL, ClueLevel.EASY)).copy(); + return item; } /** @@ -146,7 +145,7 @@ public final class TreasureTrailPlugin extends OptionHandler { * @return {@code True} if so. */ private boolean hasClue(Player player) { - return player.getTreasureTrailManager().hasClue(); + return TreasureTrailManager.getInstance(player).hasClue(); } } diff --git a/Server/src/main/java/core/game/content/ttrail/UriNPC.java b/Server/src/main/content/global/activity/ttrail/UriNPC.java similarity index 95% rename from Server/src/main/java/core/game/content/ttrail/UriNPC.java rename to Server/src/main/content/global/activity/ttrail/UriNPC.java index bddcad008..64f1b7ae4 100644 --- a/Server/src/main/java/core/game/content/ttrail/UriNPC.java +++ b/Server/src/main/content/global/activity/ttrail/UriNPC.java @@ -1,6 +1,6 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; @@ -9,7 +9,7 @@ import core.game.node.entity.player.Player; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; /** @@ -101,7 +101,7 @@ public final class UriNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (!(entity instanceof Player)) { return false; } @@ -109,7 +109,7 @@ public final class UriNPC extends AbstractNPC { Player p = entity.asPlayer(); return p == player; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override @@ -120,7 +120,7 @@ public final class UriNPC extends AbstractNPC { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new UriDialogue()); + ClassScanner.definePlugin(new UriDialogue()); return super.newInstance(arg); } diff --git a/Server/src/main/java/core/game/content/ttrail/ZamorakWizardNPC.java b/Server/src/main/content/global/activity/ttrail/ZamorakWizardNPC.java similarity index 93% rename from Server/src/main/java/core/game/content/ttrail/ZamorakWizardNPC.java rename to Server/src/main/content/global/activity/ttrail/ZamorakWizardNPC.java index 897fdc3d1..3b1dae94c 100644 --- a/Server/src/main/java/core/game/content/ttrail/ZamorakWizardNPC.java +++ b/Server/src/main/content/global/activity/ttrail/ZamorakWizardNPC.java @@ -1,7 +1,7 @@ -package core.game.content.ttrail; +package content.global.activity.ttrail; import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; @@ -99,7 +99,7 @@ public final class ZamorakWizardNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (!(entity instanceof Player)) { return false; } @@ -107,7 +107,7 @@ public final class ZamorakWizardNPC extends AbstractNPC { Player p = entity.asPlayer(); return p == player; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override 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/RandomEventNPC.kt b/Server/src/main/content/global/ame/RandomEventNPC.kt new file mode 100644 index 000000000..a4f3072f7 --- /dev/null +++ b/Server/src/main/content/global/ame/RandomEventNPC.kt @@ -0,0 +1,170 @@ +package content.global.ame + +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 +import core.game.world.map.RegionManager +import core.game.world.map.path.Pathfinder +import core.game.world.update.flag.context.Graphics +import core.integrations.discord.Discord +import core.tools.RandomFunction +import core.tools.secondsToTicks +import core.tools.ticksToCycles +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds +import kotlin.random.Random +import kotlin.reflect.full.createInstance + +abstract class RandomEventNPC(id: Int) : NPC(id) { + lateinit var player: Player + abstract var loot: WeightBasedTable? + var spawnLocation: Location? = null + val SMOKE_GRAPHICS = Graphics(86) + var initialized = false + var finalized = 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() + if (event is MysteriousOldManNPC) event.type = type + event.loot = loot + event.player = player + event.spawnLocation = RegionManager.getSpawnLocation(player, this) + setAttribute(event, "spawned-by-ame", true) + return event + } + + open fun terminate() { + pulseManager.clear(PulseType.STANDARD) + if (initialized && !finalized) { + poofClear(this) + playGlobalAudio(this.location, Sounds.SMOKEPUFF_1930, ticksToCycles(1)) + } + removeAttribute(player, "random:pause") + 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("re-npc", null) != this){ + terminate() + return + } + if (!player.getAttribute("random:pause", false)) { + ticksLeft-- + } + if (!pulseManager.hasPulseRunning() && !finalized) { + follow() + } + if (!player.isActive || !player.location.withinDistance(location, 10)) { + terminate() + } + if (ticksLeft <= 0 && initialized) { + onTimeUp() + initialized = false + } + } + + override fun init() { + initialized = true + finalized = 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() { + terminate() + } + + fun noteAndTeleport() { + player.pulseManager.clear() + for (item in player.inventory.toArray()) { + if (item == null) continue + if (item.definition.isUnnoted) { + player.inventory.remove(item) + player.inventory.add(Item(item.noteChange, item.amount)) + } + } + if (Random.nextBoolean()) { + player.properties.teleportLocation = Location.create(3197, 3223, 0) + } else { + player.properties.teleportLocation = Location.create(3212, 9620, 0) + } + player.graphics(SMOKE_GRAPHICS) + Discord.postPlayerAlert(player.username, "Ignored Random") + } + + override fun clear() { + super.clear() + if(player.getAttribute("re-npc", null) == this) player.removeAttribute("re-npc") + } + + abstract fun talkTo(npc: NPC) + + 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 new file mode 100644 index 000000000..db5ad9b2e --- /dev/null +++ b/Server/src/main/content/global/ame/RandomEvents.kt @@ -0,0 +1,100 @@ +package content.global.ame + +import org.rs09.consts.Items +import content.global.ame.events.surpriseexam.MysteriousOldManNPC +import content.global.ame.events.certer.CerterNPC +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, 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), + WeightedItem(Items.SPINACH_ROLL_1969,1,1,1.5), + WeightedItem(Items.COINS_995,80,80,1.1), + WeightedItem(Items.COINS_995,160,160,1.1), + WeightedItem(Items.COINS_995,320,320,1.1), + WeightedItem(Items.COINS_995,480,480,1.1), + WeightedItem(Items.COINS_995,640,640,1.1), + WeightedItem(Items.UNCUT_RUBY_1619,1,1,0.86), + WeightedItem(Items.COINS_995,240,240,0.65), + WeightedItem(Items.COSMIC_TALISMAN_1454,1,1,0.43), + WeightedItem(Items.UNCUT_DIAMOND_1617,1,1,0.22), + WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985,1,1,0.1), + WeightedItem(Items.LOOP_HALF_OF_A_KEY_987,1,1,0.1) + )), + MAZE(npc = MazeNPC()), + DRILL_DEMON(npc = SergeantDamienNPC()), + EVIL_CHICKEN(npc = EvilChickenNPC()), + STRANGE_PLANT(npc = StrangePlantNPC()), + SWARM(npc = SwarmNPC()), + EVIL_BOB(npc = EvilBobNPC(), skillIds = intArrayOf(Skills.FISHING, Skills.MAGIC)), + DRUNKEN_DWARF(npc = DrunkenDwarfNPC()), + RICK_TURPENTINE(npc = RickTurpentineNPC(), loot = CERTER.loot), + SURPRISE_EXAM(npc = MysteriousOldManNPC(), type = "sexam"), + FREAKY_FORESTER(npc = FreakyForesterNPC(), skillIds = intArrayOf(Skills.WOODCUTTING)), + PILLORY(npc = PilloryNPC(), skillIds = intArrayOf(Skills.THIEVING)), + TREE_SPIRIT(npc = TreeSpiritRENPC(), skillIds = intArrayOf(Skills.WOODCUTTING)), + QUIZ_MASTER(npc = QuizMasterNPC()), + RIVER_TROLL(RiverTrollRENPC(), skillIds = intArrayOf(Skills.FISHING)), + ROCK_GOLEM(RockGolemRENPC(), skillIds = intArrayOf(Skills.MINING)), + SHADE(ShadeRENPC(), skillIds = intArrayOf(Skills.PRAYER)), + ZOMBIE(ZombieRENPC(), skillIds = intArrayOf(Skills.PRAYER)); + + companion object { + @JvmField + val randomIDs = values().map { it.npc.id }.toList() + val skillMap = HashMap>() + val nonSkillList = ArrayList() + + init { populateMappings() } + + fun getSkillBasedRandomEvent (skill: Int) : RandomEvents? { + return skillMap[skill]?.random() + } + + fun getNonSkillRandom() : RandomEvents { + return nonSkillList.random() + } + + private fun populateMappings() { + for (event in values()) { + 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/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 new file mode 100644 index 000000000..27f72a317 --- /dev/null +++ b/Server/src/main/content/global/ame/events/certer/CerterDialogue.kt @@ -0,0 +1,60 @@ +package content.global.ame.events.certer + +import core.api.addItemOrDrop +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() { + val CERTER_INTERFACE = 184 + override fun handle(componentID: Int, buttonID: Int) { + if (initial && !player!!.getAttribute("certer:reward", false)) { + when (stage) { + 0 -> npc("Ah, hello, ${player!!.username.capitalize()}. Could you", "please help me identify this?").also { stage++ } + 1 -> { + end() + player!!.interfaceManager.open(Component(CERTER_INTERFACE)) + } + } + } else { + player!!.setAttribute("random:pause", true) + val isCorrect = player!!.getAttribute("certer:correct", false) + val receivedReward = player!!.getAttribute("certer:reward", false) + if (receivedReward == true) { + stage = 1 + } + when (stage) { + 0 -> if (!isCorrect) { + npc("Sorry, I don't think so.").also { + player!!.setAttribute("certer:reward", true) + stage = END_DIALOGUE + 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 + AntiMacro.rollEventLoot(player!!).forEach { addItemOrDrop(player!!, it.id, it.amount) } + } + } + } + } + } + + override fun end() { + super.end() + if (player!!.getAttribute("certer:reward", false)) { + // Remove movement pulse to stop following player + npc!!.pulseManager.clear(PulseType.STANDARD) + // Wave goodbye + npc!!.animate(Emotes.WAVE.animation) + // Terminate the event + AntiMacro.terminateEventNpc(player!!) + } else { + player!!.setAttribute("random:pause", false) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/certer/CerterEventInterface.kt b/Server/src/main/content/global/ame/events/certer/CerterEventInterface.kt new file mode 100644 index 000000000..96d8f313e --- /dev/null +++ b/Server/src/main/content/global/ame/events/certer/CerterEventInterface.kt @@ -0,0 +1,85 @@ +package content.global.ame.events.certer + +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 + val OPTION_A_CHILD = 1 + val OPTION_B_CHILD = 2 + val OPTION_C_CHILD = 3 + val ITEM_CHILD = 7 + val items = mapOf( + /* + * NOTE: We use these "NULL" items to get their model id + zoom values. + * These were the actual items used by this random event before they + * decided to swap over to using model ids instead. + */ + Items.BOWL_1923 to "A bowl.", + Items.NULL_6189 to "A fish.", + Items.NULL_6190 to "A bass.", + Items.NULL_6191 to "A sword.", + Items.NULL_6192 to "A battleaxe.", + Items.NULL_6193 to "A helmet.", + Items.NULL_6194 to "A kiteshield.", + Items.NULL_6195 to "A pair of shears.", + Items.NULL_6196 to "A shovel.", + Items.NULL_6197 to "A ring.", + Items.NULL_6198 to "A necklace." + ) + val falseOptions = arrayOf("An axe.", "An arrow.", "A pair of boots.", "A pair of gloves.", "A staff.", "A bow.", "A feather.", "The disenfrachaised youth of 1940's Columbia.") + override fun defineInterfaceListeners() { + on(CERTER_INTERFACE) { player, _, _, buttonID, _, _ -> + val answer = buttonID - 7 + val correctAnswer = player.getAttribute("certer:correctIndex", 0) + player.setAttribute("certer:correct", correctAnswer == answer) + player.interfaceManager.close() + player.dialogueInterpreter.open(CerterDialogue(false), AntiMacro.getEventNpc(player)) + return@on true + } + + onOpen(CERTER_INTERFACE) { player, _ -> + generateOptions(player) + return@onOpen true + } + + onClose(CERTER_INTERFACE) { player, _ -> + player.setAttribute("random:pause", false) + return@onClose true + } + } + + fun generateOptions(player: Player) { + val correct = items.keys.random() + val indexes = arrayListOf(1, 2, 3) + val correctIndex = indexes.random() + val correctItem = Item(correct).definition + val iFaceModelId = correctItem.interfaceModelId + val iFaceZoom = correctItem.modelZoom + indexes.remove(correctIndex) + player.setAttribute("certer:correctIndex", correctIndex) + + player.packetDispatch.sendString(items[correct], CERTER_INTERFACE, optionFromIndex(correctIndex)) + + val tempOptions = falseOptions.toMutableList() + val false1 = tempOptions.random() + tempOptions.remove(false1) + val false2 = tempOptions.random() + + player.packetDispatch.sendString(false1, CERTER_INTERFACE, optionFromIndex(indexes[0])) + player.packetDispatch.sendString(false2, CERTER_INTERFACE, optionFromIndex(indexes[1])) + player.packetDispatch.sendModelOnInterface(iFaceModelId, CERTER_INTERFACE, ITEM_CHILD, iFaceZoom) + } + + fun optionFromIndex(index: Int): Int { + return when (index) { + 1 -> OPTION_A_CHILD + 2 -> OPTION_B_CHILD + 3 -> OPTION_C_CHILD + else -> OPTION_A_CHILD + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/certer/CerterNPC.kt b/Server/src/main/content/global/ame/events/certer/CerterNPC.kt new file mode 100644 index 000000000..dee88ed3a --- /dev/null +++ b/Server/src/main/content/global/ame/events/certer/CerterNPC.kt @@ -0,0 +1,45 @@ +package content.global.ame.events.certer + +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.link.emote.Emotes +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 phrases: Array + + override fun tick() { + sayLine(this, phrases, true, true) + if (ticksLeft == 2) { + lock(player, 2) + } + super.tick() + } + + override fun talkTo(npc: NPC) { + player.setAttribute("random:pause", true) + player.dialogueInterpreter.open(CerterDialogue(true),npc) + } + + override fun init() { + super.init() + 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) + } + + 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 new file mode 100644 index 000000000..2287de37d --- /dev/null +++ b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonListeners.kt @@ -0,0 +1,93 @@ +package content.global.ame.events.drilldemon + +import core.api.* +import core.game.node.entity.npc.NPC +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 + +import org.rs09.consts.Sounds + +class DrillDemonListeners : InteractionListener, MapArea { + val MATS = intArrayOf(10076,10077,10078,10079) + override fun defineListeners() { + + 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.") + } + 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 + } + } + + 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 new file mode 100644 index 000000000..8c90bd77f --- /dev/null +++ b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt @@ -0,0 +1,88 @@ +package content.global.ame.events.drilldemon + +import content.global.ame.returnPlayer +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.world.map.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_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 = "/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 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(1335, 1336, 1337, 1338).shuffled().toMutableList() + val task = tempList.random() + val taskOffset = tempOffsetList.random() + + setAttribute(player, DD_KEY_TASK, task) + setAttribute(player, DD_CORRECT_OFFSET, taskOffset) + + tempList.remove(task) + tempOffsetList.remove(taskOffset) + setVarbit(player, taskOffset, task) + for (i in 0 until tempList.size) { + setVarbit(player, tempOffsetList[i], tempList[i], true) + } + } + + 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 getVarbit(player, getVarbitForId(id)) + } + + fun cleanup(player: Player) { + returnPlayer(player) + removeAttributes(player, DD_KEY_TASK, DD_CORRECT_OFFSET, DD_CORRECT_COUNTER) + player.interfaceManager.openDefaultTabs() + setComponentVisibility(player, 548, 69, false) + setComponentVisibility(player, 746, 12, false) + } + + fun animationForTask(task: Int): Animation { + return when (task) { + DD_SIGN_SITUP -> Animation(2763) + DD_SIGN_PUSHUP -> Animation(2762) + DD_SIGN_JUMP -> Animation(2761) + DD_SIGN_JOG -> Animation(2764) + else -> Animation(-1) + } + } + + 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) + } + } +} diff --git a/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDialogue.kt b/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDialogue.kt new file mode 100644 index 000000000..d9bff58af --- /dev/null +++ b/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDialogue.kt @@ -0,0 +1,55 @@ +package content.global.ame.events.drilldemon + +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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +class SeargentDamienDialogue(var isCorrect: Boolean = false, var eventStart: Boolean = false) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + 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!") + } + stage = END_DIALOGUE + } + 100 -> { + end() + DrillDemonUtils.cleanup(player!!) + DrillDemonUtils.reward(player!!) + } + } + } +} \ 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 new file mode 100644 index 000000000..5b065f567 --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilchicken/EvilChickenNPC.kt @@ -0,0 +1,43 @@ +package content.global.ame.events.evilchicken + +import core.api.getWorldTicks +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import content.global.ame.RandomEventNPC +import core.api.utils.WeightBasedTable + +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!") + override fun talkTo(npc: NPC) {} + + override fun init() { + super.init() + val id = idForCombatLevel(ids, player) + this.transform(id) + this.attack(player) + sendChat(phrases.random().replace("@name",player.username.capitalize())) + this.isRespawn = false + } + + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + GroundItemManager.create(Item(Items.FEATHER_314, RandomFunction.random(50,300)), this.dropLocation, player) + } + + override fun tick() { + if(!player.location.withinDistance(this.location,8)){ + this.terminate() + } + if(getWorldTicks() % 10 == 0){ + sendChat(phrases.random().replace("@name",player.username.capitalize())) + } + super.tick() + if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + } +} 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 new file mode 100644 index 000000000..7bcaa3c4d --- /dev/null +++ b/Server/src/main/content/global/ame/events/genie/GenieDialogue.kt @@ -0,0 +1,20 @@ +package content.global.ame.events.genie + +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.dialogue.DialogueFile +import core.game.system.timer.impl.AntiMacro +import org.rs09.consts.Items + +class GenieDialogue() : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + 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!!) + } + } + } +} diff --git a/Server/src/main/content/global/ame/events/genie/GenieNPC.kt b/Server/src/main/content/global/ame/events/genie/GenieNPC.kt new file mode 100644 index 000000000..b4700a9a5 --- /dev/null +++ b/Server/src/main/content/global/ame/events/genie/GenieNPC.kt @@ -0,0 +1,43 @@ +package content.global.ame.events.genie + +import core.game.node.entity.npc.NPC +import core.tools.RandomFunction +import org.rs09.consts.NPCs +import content.global.ame.RandomEventNPC +import core.api.lock +import core.api.playAudio +import core.api.utils.WeightBasedTable +import org.rs09.consts.Sounds + +class GenieNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GENIE_409) { + lateinit var phrases: Array + + override fun tick() { + sayLine(this, phrases, true, true) + if (ticksLeft == 2) { + lock(player, 2) + } + super.tick() + } + + override fun init() { + super.init() + val honorific = if (player.isMale) "Master" else "Mistress" + phrases = arrayOf( + "Greetings, ${player.username}!", + "Ehem... $honorific ${player.username}?", + "Are you there, $honorific ${player.username}?", + "No one ignores me!" + ) + playAudio(player, Sounds.GENIE_APPEAR_2301) + } + + override fun talkTo(npc: NPC) { + player.dialogueInterpreter.open(GenieDialogue(),npc) + } + + override fun onTimeUp() { + noteAndTeleport() + terminate() + } +} diff --git a/Server/src/main/content/global/ame/events/maze/MazeInterface.kt b/Server/src/main/content/global/ame/events/maze/MazeInterface.kt new file mode 100644 index 000000000..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 new file mode 100644 index 000000000..dff28d7e9 --- /dev/null +++ b/Server/src/main/content/global/ame/events/rivertroll/RiverTrollRENPC.kt @@ -0,0 +1,30 @@ +package content.global.ame.events.rivertroll + +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import content.global.ame.RandomEventNPC +import core.api.utils.WeightBasedTable + +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 id = idForCombatLevel(ids, player) + this.transform(id) + this.attack(player) + sendChat("Fishies be mine, leave dem fishies!") + this.isRespawn = false + } + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + } + override fun tick() { + if(!player.location.withinDistance(this.location,8)){ + this.terminate() + } + 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/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 new file mode 100644 index 000000000..eb3399657 --- /dev/null +++ b/Server/src/main/content/global/ame/events/rockgolem/RockGolemRENPC.kt @@ -0,0 +1,30 @@ +package content.global.ame.events.rockgolem + +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import content.global.ame.RandomEventNPC +import core.api.utils.WeightBasedTable + +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 id = idForCombatLevel(ids, player) + this.transform(id) + this.attack(player) + sendChat("Raarrrgghh! Flee human!") + this.isRespawn = false + } + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + } + override fun tick() { + if(!player.location.withinDistance(this.location,8)){ + this.terminate() + } + super.tick() + if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyDialogue.kt b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyDialogue.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyDialogue.kt rename to Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyDialogue.kt index 289c77de4..8374daf41 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyDialogue.kt +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyDialogue.kt @@ -1,12 +1,13 @@ -package rs09.game.content.ame.events.sandwichlady +package content.global.ame.events.sandwichlady import core.cache.def.impl.ItemDefinition import core.game.component.Component import core.game.node.entity.combat.ImpactHandler import core.game.node.item.GroundItemManager import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE +import core.game.dialogue.DialogueFile +import core.game.system.timer.impl.AntiMacro +import core.tools.END_DIALOGUE class SandwichLadyDialogue(val isChoice: Boolean) : DialogueFile() { val SANDWICH_INTERFACE = 297 @@ -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) - player!!.antiMacroHandler.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) } - player!!.antiMacroHandler.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 new file mode 100644 index 000000000..c0d4773bd --- /dev/null +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyInterface.kt @@ -0,0 +1,44 @@ +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 { + + val SANDWICH_INTERFACE = 297 + val baguette = Items.BAGUETTE_6961 + val triangle_sandwich = Items.TRIANGLE_SANDWICH_6962 + val sandwich = Items.SQUARE_SANDWICH_6965 + val roll = Items.ROLL_6963 + val pie = Items.MEAT_PIE_2327 + val kebab = Items.KEBAB_1971 + val chocobar = Items.CHOCOLATE_BAR_1973 + + 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)} + 8 -> {Item(triangle_sandwich)} + 9 -> {Item(sandwich)} + 10 -> {Item(roll)} + 11 -> {Item(pie)} + 12 -> {Item(kebab)} + 13 -> {Item(chocobar)} + else -> {Item(baguette)} + } + + player.setAttribute("sandwich-lady:choice",item.id) + player.interfaceManager.close() + player.dialogueInterpreter.open(SandwichLadyDialogue(true), event) + return@on true + } + } +} diff --git a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt new file mode 100644 index 000000000..99e0a0e68 --- /dev/null +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt @@ -0,0 +1,53 @@ +package content.global.ame.events.sandwichlady + +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.utils.WeightBasedTable + +class SandwichLadyRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SANDWICH_LADY_3117) { + 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) + + override fun tick() { + 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() + } + + override fun onTimeUp() { + noteAndTeleport() + terminate() + } + + fun assignItem(){ + assigned_item = items.random() + player.setAttribute("sandwich-lady:item", assigned_item) + } + + override fun talkTo(npc: NPC) { + player.dialogueInterpreter.open(SandwichLadyDialogue(false), npc) + } +} diff --git a/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt b/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt new file mode 100644 index 000000000..60a0f57ae --- /dev/null +++ b/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt @@ -0,0 +1,29 @@ +package content.global.ame.events.shade + +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import content.global.ame.RandomEventNPC +import core.api.utils.WeightBasedTable + +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 id = idForCombatLevel(ids, player) + this.transform(id) + this.attack(player) + sendChat("Leave this place!") + this.isRespawn = false + } + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + } + override fun tick() { + if(!player.location.withinDistance(this.location,8)){ + this.terminate() + } + super.tick() + if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + } +} 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/kotlin/rs09/game/content/ame/events/supriseexam/MordautDialogue.kt b/Server/src/main/content/global/ame/events/surpriseexam/MordautDialogue.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/MordautDialogue.kt rename to Server/src/main/content/global/ame/events/surpriseexam/MordautDialogue.kt index 278f2b4f4..d3bd04af0 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/MordautDialogue.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/MordautDialogue.kt @@ -1,9 +1,9 @@ -package rs09.game.content.ame.events.supriseexam +package content.global.ame.events.surpriseexam import core.game.component.Component -import core.game.content.dialogue.FacialExpression -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE +import core.game.dialogue.FacialExpression +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE class MordautDialogue(val examComplete: Boolean, val questionCorrect: Boolean = false, val fromInterface: Boolean = false) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { 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/kotlin/rs09/game/content/ame/events/supriseexam/SEDoorDialogue.kt b/Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt similarity index 75% rename from Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEDoorDialogue.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt index 4f63efa48..221f4b4fe 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEDoorDialogue.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SEDoorDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.content.ame.events.supriseexam +package content.global.ame.events.surpriseexam -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE class SEDoorDialogue(val preExam: Boolean) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEPatternInterface.kt b/Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEPatternInterface.kt rename to Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt index 73ac97c4d..3ba12a8c0 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SEPatternInterface.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SEPatternInterface.kt @@ -1,15 +1,15 @@ -package rs09.game.content.ame.events.supriseexam +package content.global.ame.events.surpriseexam import core.game.node.entity.npc.NPC import org.rs09.consts.Components import org.rs09.consts.NPCs -import rs09.game.interaction.InterfaceListener +import core.game.interaction.InterfaceListener -class SEPatternInterface : InterfaceListener() { +class SEPatternInterface : InterfaceListener { val COMPONENT = Components.PATTERN_NEXT_103 - override fun defineListeners() { + override fun defineInterfaceListeners() { on(COMPONENT){player, component, opcode, buttonID, slot, itemID -> val index = buttonID - 10 diff --git a/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt new file mode 100644 index 000000000..ad19ffbda --- /dev/null +++ b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt @@ -0,0 +1,70 @@ +package content.global.ame.events.surpriseexam + +import core.game.component.Component +import core.game.node.entity.player.Player +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, MapArea { + override fun defineListeners() { + + 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()) + return@on true + } + + on(SurpriseExamUtils.SE_DOORS, IntType.SCENERY, "open") { player, node -> + val correctDoor = player.getAttribute(SurpriseExamUtils.SE_DOOR_KEY,-1) + + if(correctDoor == -1){ + player.dialogueInterpreter.open(SEDoorDialogue(true)) + return@on true + } + + if(node.id == correctDoor){ + SurpriseExamUtils.cleanup(player) + return@on true + } + + player.dialogueInterpreter.open(SEDoorDialogue(false)) + return@on true + } + + 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) + } + } + player.interfaceManager.open(Component(ExperienceInterface.COMPONENT_ID)) + return@on true + } + + } + + override fun defineDestinationOverrides() { + setDest(IntType.NPC, NPCs.MR_MORDAUT_6117) { _, _ -> + return@setDest Location.create(1886, 5025, 0) + } + } + + 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/surpriseexam/SurpriseExamUtils.kt b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt new file mode 100644 index 000000000..e1cbdd5a8 --- /dev/null +++ b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt @@ -0,0 +1,63 @@ +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.system.task.Pulse +import org.rs09.consts.Components +import org.rs09.consts.Items + +object SurpriseExamUtils { + val SE_KEY_INDEX = "supexam:index" + val SE_DOOR_KEY = "supexam:door" + val INTER_PATTERN_CHILDS = intArrayOf(6,7,8) + val INTER_OPTION_CHILDS = intArrayOf(10,11,12,13) + val SE_DOORS = intArrayOf(2188,2189,2192,2193) + val INTERFACE = Components.PATTERN_NEXT_103 + val SE_KEY_CORRECT = "supexam:correct" + val sets = arrayOf( + intArrayOf(Items.GARDENING_TROWEL_5325,Items.SECATEURS_5329,Items.SEED_DIBBER_5343,Items.RAKE_5341), + intArrayOf(Items.SALMON_329,Items.SHARK_385,Items.TROUT_333,Items.SHRIMPS_315), + intArrayOf(Items.BRONZE_SWORD_1277,Items.WOODEN_SHIELD_1171,Items.BRONZE_MED_HELM_1139,Items.ADAMANT_BATTLEAXE_1371), + intArrayOf(Items.FLY_FISHING_ROD_309,Items.BARBARIAN_ROD_11323,Items.SMALL_FISHING_NET_303,Items.HARPOON_311) + ) + + fun cleanup(player: Player){ + returnPlayer(player) + removeAttributes(player, SE_KEY_INDEX, SE_KEY_CORRECT) + player.pulseManager.run(object : Pulse(2){ + override fun pulse(): Boolean { + addItemOrDrop(player, Items.BOOK_OF_KNOWLEDGE_11640) + return true + } + }, PulseType.CUSTOM_1) + } + + fun generateInterface(player: Player){ + val set = sets.random() + val setIndex = sets.indexOf(set) + + val tempList = set.toList().shuffled().toMutableList() + val correctOpt = tempList.random() + val optIndex = tempList.indexOf(correctOpt) + + tempList.remove(correctOpt) + for(i in INTER_PATTERN_CHILDS.indices) player.packetDispatch.sendItemOnInterface(tempList[i],1, INTERFACE, INTER_PATTERN_CHILDS[i]) + for(i in INTER_OPTION_CHILDS.indices){ + if(i == optIndex) player.packetDispatch.sendItemOnInterface(correctOpt,1, INTERFACE, INTER_OPTION_CHILDS[i]) + else player.packetDispatch.sendItemOnInterface(getFalseSet(setIndex)[i],1, INTERFACE, INTER_OPTION_CHILDS[i]) + } + player.setAttribute(SE_KEY_INDEX,optIndex) + } + + fun getFalseSet(trueSetIndex: Int): IntArray{ + val tempSets = sets.toMutableList() + tempSets.removeAt(trueSetIndex) + return tempSets.random() + } + + fun pickRandomDoor(player: Player){ + player.setAttribute(SE_DOOR_KEY, SE_DOORS.random()) + } +} 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 new file mode 100644 index 000000000..3b0033972 --- /dev/null +++ b/Server/src/main/content/global/ame/events/treespirit/TreeSpiritRENPC.kt @@ -0,0 +1,30 @@ +package content.global.ame.events.treespirit + +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import content.global.ame.RandomEventNPC +import core.api.utils.WeightBasedTable + +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 id = idForCombatLevel(ids, player) + this.transform(id) + this.attack(player) + sendChat("Leave these woods and never return!") + this.isRespawn = false + } + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + } + override fun tick() { + if(!player.location.withinDistance(this.location,8)){ + this.terminate() + } + 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/zombie/ZombieRENPC.kt b/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt new file mode 100644 index 000000000..6b43d54b4 --- /dev/null +++ b/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt @@ -0,0 +1,29 @@ +package content.global.ame.events.zombie + +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import content.global.ame.RandomEventNPC +import core.api.utils.WeightBasedTable + +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 id = idForCombatLevel(ids, player) + this.transform(id) + this.attack(player) + sendChat("Brainsssss!") + this.isRespawn = false + } + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + } + override fun tick() { + if(!player.location.withinDistance(this.location,8)){ + this.terminate() + } + super.tick() + if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() + } +} diff --git a/Server/src/main/content/global/bots/Adventurer.kt b/Server/src/main/content/global/bots/Adventurer.kt new file mode 100644 index 000000000..916503945 --- /dev/null +++ b/Server/src/main/content/global/bots/Adventurer.kt @@ -0,0 +1,724 @@ +package content.global.bots + +import core.game.interaction.DestinationFlag +import core.game.interaction.MovementPulse +import core.game.node.scenery.Scenery +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.skill.Skills +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.map.RegionManager +import core.game.world.map.zone.ZoneBorders +import core.game.world.update.flag.* +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 +import java.time.format.DateTimeFormatter +import kotlin.random.Random + + +/** + * A bot script for Adventurers who explore the world! + * @param counter used in the bots random idling function. + * @param random is used to generate random number. + * @param city determines the home city of the bot. + * @param freshspawn determines if the bot has just been spawned. + * @param random_city is the list of cities that can be randomly chosen as the home city. + * @param tree_list is the list of trees that a bot can start cutting randomly. + * @author Sir Kermit + * @author Ceikry + */ + +// 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 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 + 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 + CombatStyle.MAGIC -> CombatBotAssembler.Type.MAGE + CombatStyle.RANGE -> CombatBotAssembler.Type.RANGE + } + + init { + skills[Skills.AGILITY] = 99 + inventory.add(Item(1359))//Rune Axe + skills[Skills.WOODCUTTING] = 95 + inventory.add(Item(590))//Tinderbox + skills[Skills.FISHING] = 90 + inventory.add(Item(1271))//Addy Pickaxe + skills[Skills.MINING] = 90 + skills[Skills.SLAYER] = 90 + } + + override fun toString(): String { + return "${bot.username} is an Adventurer bot " + + "at ${bot.location}! " + + "State: $state - " + + "City: $city - " + + "Ticks: $ticks - " + + "Freshspawn: $freshspawn - " + + "Sold: $sold - " + + "Counter: $counter" + } + + var state = State.START + + private fun getRandomCity(): Location{ + return cities.random() + } + + 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++ >= Random.nextInt(150,300)) { state = State.TELEPORTING } + val items = AIRepository.groundItems[bot] + if (Random.nextBoolean()) { + if (items.isNullOrEmpty()) { + scriptAPI.attackNpcsInRadius(bot, 8) + state = State.LOOT_DELAY + } + if (bot.inventory.isFull) { + checkNearBank() + } + + } else { + if (bot.inventory.isFull){ + checkNearBank() + } else { + val resource = scriptAPI.getNearestNodeFromList(resources,true) + if(resource != null){ + if(resource.name.contains("ocks")) InteractionListeners.run(resource.id, + IntType.SCENERY,"mine",bot,resource) + else InteractionListeners.run(resource.id, IntType.SCENERY,"chop down",bot,resource) + } + } + } + return + } + + fun refresh() { +// log(this::class.java, Log.WARN, "${bot.username} refreshed from $state at $city with $ticks and $counter counter.") + scriptAPI.teleport(lumbridge) + state = State.START + } + + //Adventure Bots Actual Code STARTS HERE!!! + // 100 ticks = 60 seconds + override fun tick() { + ticks++ + // Hard refresh + if (ticks >= 1000) { + ticks = 0 + refresh() + return + } + + // zoneborder checker + if(ticks % 30 == 0){ + for((zone, resolution) in common_stuck_locations){ + if(zone.insideBorder(bot)){ + resolution(this) + return + } + } + } + + when(state){ + + State.LOOT_DELAY -> { + bot.pulseManager.run(object : Pulse() { + var counter1 = 0 + override fun pulse(): Boolean { + when (counter1++) { + 7 -> return true.also { state = State.LOOT } + } + return false + } + }) + } + + State.LOOT -> { + val items = AIRepository.groundItems[bot] + if (items?.isNotEmpty() == true && !bot.inventory.isFull) { + items.toTypedArray().forEach { + scriptAPI.takeNearestGroundItem(it.id) + } + return + } else { + state = State.ADVENTURE + } + } + + State.START -> { + if (freshspawn) { + freshspawn = false + scriptAPI.randomWalkTo(lumbridge, randomNumberFromOne(25)) + } else { + state = State.TELEPORTING + } + } + + State.TELEPORTING -> { + if (freshspawn){ freshspawn = false } + teleportToRandomCity() + poi = false + sold = false + ticks = 0 + counter = 0 + state = State.ADVENTURE + return + } + + State.ADVENTURE -> { + checkCounter(800) + if (randomNumberFromOne(chance) <= 10) { + if (otherPlayersNearby()) { + ticks = 0 + dialogue() + } + } + + 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 + } + return + } + scriptAPI.randomWalkTo(city, roamDistance) + return + } + + if (poi && randomNumberFromOne(1000) <= 100){ + immerse() + return + } + + if (poi && randomNumberFromOne(1000) <= 25){ + dialogue() + } + + if (poi && randomNumberFromOne(1000) <= 50){ + val roamDistancePoi = when(poiloc){ + 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 (randomNumberFromOne(1000) <= 75) { + if (!cityLocationsGE.contains(city)) { + ticks = 0 + immerse() + return + } else if (randomNumberFromOne(chance) <= 55 && otherPlayersNearby()) { + ticks = 0 + dialogue() + } + } + + if (cityLocationsGE.contains(city) && randomNumberFromOne(1000) <= 50) { + state = State.IDLE_GE + } + + if (!poi && randomNumberFromOne(1000) <= 5) { + poiloc = getRandomPoi() + city = teak1 + poi = true + scriptAPI.teleport(poiloc) + return + } + + if (cityLocationsGE.contains(city) && randomNumberFromOne(1000) <= 100) { + state = State.TELEPORTING + return + } + + if (cityLocationsGE.contains(city)) { + return + } + + 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 (randomNumberFromOne(100) % 2 == 0) { + state = State.TELEPORTING + } else { + if (citygroupA.contains(city)) { + city = citygroupA.random() + } else { + city = citygroupB.random() + } + counter = 0 + ticks = 0 + state = State.FIND_CITY + } + counter = 0 + return + } + return + } + + 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 + 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 + } + } + return + } + + State.FIND_GE -> { + sold = false + val ge: Scenery? = scriptAPI.getNearestNode("Desk", true) as Scenery? + if (ge == null || bot.bank.isEmpty) state = State.ADVENTURE + class GEPulse : MovementPulse(bot, ge, DestinationFlag.OBJECT) { + override fun pulse(): Boolean { + bot.faceLocation(ge?.location) + return true.also { state = State.GE } + } + } + if (ge == null || bot.bank.isEmpty) state = State.ADVENTURE + if (ge != null && !bot.bank.isEmpty) { + 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 -> { + val bank: Scenery? = scriptAPI.getNearestNode("Bank booth", true) as Scenery? + 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.FIND_CITY -> { + if (counter++ >= 500 || cityLocationsGE.contains(city)){ + scriptAPI.teleport(getRandomCity().also { city = it }) + state = State.ADVENTURE + } + if (bot.location.equals(city)) { + state = State.ADVENTURE + } else { + scriptAPI.randomWalkTo(city, randomNumberFromOne(10)) + } + checkCounter(600) + return + } + + } + + } + + fun dialogue() { + val until = 1225 - dateCode + val lineStd = dialogue.getLines("standard").rand() + var lineAlt = "" + + when { + //Celebrates Halloween! + dateCode == 1031 -> lineAlt = dialogue.getLines("halloween").rand() + + //Celebrates lead up to Christmas! + until in 2..23 -> lineAlt = dialogue.getLines("approaching_christmas").rand() + + //Celebrates Christmas Day! + dateCode == 1225 -> lineAlt = dialogue.getLines("christmas_day").rand() + + //Celebrates Christmas Eve + dateCode == 1224 -> lineAlt = dialogue.getLines("christmas_eve").rand() + + //New years eve + dateCode == 1231 -> lineAlt = dialogue.getLines("new_years_eve").rand() + + //New years + dateCode == 101 -> lineAlt = dialogue.getLines("new_years").rand() + + //Valentines + dateCode == 214 -> lineAlt = dialogue.getLines("valentines").rand() + + //Easter + dateCode == 404 -> lineAlt = dialogue.getLines("easter").rand() + } + + 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, + ADVENTURE, + FIND_BANK, + FIND_CITY, + IDLE_GE, + GE, + TELEPORTING, + LOOT, + LOOT_DELAY, + FIND_GE + } + + + override fun newInstance(): Script { + val script = Adventurer(style) + script.state = State.START + val tier = CombatBotAssembler.Tier.MED + if (type == CombatBotAssembler.Type.RANGE) + script.bot = CombatBotAssembler().RangeAdventurer(tier, bot.startLocation) + else + script.bot = CombatBotAssembler().MeleeAdventurer(tier, bot.startLocation) + return script + } + + companion object { + // 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 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 = 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), + draynor to ZoneBorders(3092, 3240, 3095, 3246), + edgeville to ZoneBorders(3093, 3498, 3092, 3489), + yanille to ZoneBorders(2610, 3089, 2613, 3095), + ardougne to ZoneBorders(2649, 3281, 2655, 3286), + seers to ZoneBorders(2729, 3493, 2722, 3490), + catherby to ZoneBorders(2807, 3438, 2811, 3441) + ) + + 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 -> + it.scriptAPI.walkArray(tavleryToTopOfWhiteWolf + whiteWolfMountainTop + catherbyToTopOfWhiteWolf.reversedArray()) + }, + // West of Tavlery dungeon + ZoneBorders(2874, 3390, 2880, 3401) to { it: Adventurer -> + it.scriptAPI.walkArray(tavleryToTopOfWhiteWolf + whiteWolfMountainTop + catherbyToTopOfWhiteWolf.reversedArray()) + }, + // South of White Wolf Mountain in Tavlery + ZoneBorders(2865,3408,2874,3423) to { it: Adventurer -> + it.scriptAPI.walkArray(tavleryToTopOfWhiteWolf + whiteWolfMountainTop + catherbyToTopOfWhiteWolf.reversedArray()) + }, + // On beginning of White Wolf Mountain in Tavlery + ZoneBorders(2855, 3454, 2852, 3450) to { it: Adventurer -> + it.scriptAPI.walkArray(tavleryToTopOfWhiteWolf + whiteWolfMountainTop + catherbyToTopOfWhiteWolf.reversedArray()) + }, + // South of White Wolf Mountain in Catherby + ZoneBorders(2861,3425,2867, 3432) to { it: Adventurer -> + it.scriptAPI.walkArray(catherbyToTopOfWhiteWolf + whiteWolfMountainTop + tavleryToTopOfWhiteWolf.reversedArray()) + }, + // On beginning of White Wolf Mountain in Catherby + ZoneBorders(2863, 3441, 2859, 3438) to { it: Adventurer -> + it.scriptAPI.walkArray(catherbyToTopOfWhiteWolf + whiteWolfMountainTop + tavleryToTopOfWhiteWolf.reversedArray()) + }, + // 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) + if (wall == null) { + it.refresh() + it.ticks = 0 + return@to + } + it.scriptAPI.interact(it.bot, wall, "Climb-over") + }, + // Northwest corner of Draynor Bank + ZoneBorders(3092, 3246, 3091, 3247) to { it: Adventurer -> + // Walk into Draynor Bank + it.scriptAPI.walkTo(Location(3093, 3243, 0)) + }, + // West of GE, stuck in the corner south of the outlaw place + ZoneBorders(3140, 3468, 3140, 3468) to { it: Adventurer -> + // Walk to Barbarian village + it.scriptAPI.walkArray(arrayOf(Location.create(3135, 3516, 0), Location.create(3103, 3489, 0), Location.create(3082, 3423, 0))) + }, + ) + + val dialogue: JSONObject + val dateCode: Int + + init { + val reader = FileReader(ServerConstants.BOT_DATA_PATH + File.separator + "bot_dialogue.json") + val parser = org.json.simple.parser.JSONParser() + val data = parser.parse(reader) as JSONObject + + dialogue = data + + val formatter = DateTimeFormatter.ofPattern("MMdd") + val current = LocalDateTime.now() + val formatted: String = current.format(formatter) + dateCode = formatted.toInt() + } + + private fun JSONObject.getLines(category: String): JSONArray { + return this[category] as JSONArray + } + + private fun JSONArray.rand(): String { + return this.random() as String + } + } +} 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/kotlin/rs09/game/ai/general/scriptrepository/ChickenKiller.kt b/Server/src/main/content/global/bots/ChickenKiller.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ChickenKiller.kt rename to Server/src/main/content/global/bots/ChickenKiller.kt index d462bf565..ee817573f 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ChickenKiller.kt +++ b/Server/src/main/content/global/bots/ChickenKiller.kt @@ -1,10 +1,10 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots +import core.game.bots.* 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 rs09.game.ai.general.ScriptAPI @PlayerCompatible @ScriptName("Chicken Killer") @@ -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/kotlin/rs09/game/ai/general/scriptrepository/CoalMiner.kt b/Server/src/main/content/global/bots/CoalMiner.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/CoalMiner.kt rename to Server/src/main/content/global/bots/CoalMiner.kt index 2f86bb669..9bbf13b08 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/CoalMiner.kt +++ b/Server/src/main/content/global/bots/CoalMiner.kt @@ -1,4 +1,4 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots import core.game.interaction.DestinationFlag @@ -8,11 +8,10 @@ import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items -import rs09.game.ai.general.ScriptAPI -import rs09.game.ai.skillingbot.SkillingBotAssembler -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners -import api.ContentAPI +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners +import core.api.* +import core.game.bots.* @PlayerCompatible @ScriptName("Falador Coal Miner") @@ -56,9 +55,9 @@ class CoalMiner : Script() { scriptAPI.walkTo(mine.randomLoc) } else { val rock = scriptAPI.getNearestNode("rocks",true) - rock?.let { InteractionListeners.run(rock.id, InteractionListener.SCENERY,"mine",bot,rock) } + rock?.let { InteractionListeners.run(rock.id, IntType.SCENERY,"mine",bot,rock) } } - overlay!!.setAmount(ContentAPI.amountInInventory(bot, Items.COAL_453) + coalAmount) + overlay!!.setAmount(amountInInventory(bot, Items.COAL_453) + coalAmount) } State.TO_BANK -> { 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/kotlin/rs09/game/ai/general/scriptrepository/CowKiller.kt b/Server/src/main/content/global/bots/CowKiller.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/CowKiller.kt rename to Server/src/main/content/global/bots/CowKiller.kt index dfce3b703..ac15edb28 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/CowKiller.kt +++ b/Server/src/main/content/global/bots/CowKiller.kt @@ -1,4 +1,4 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse @@ -6,7 +6,8 @@ import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items -import rs09.game.ai.pvmbots.CombatBotAssembler +import core.game.bots.CombatBotAssembler +import core.game.bots.Script class CowKiller : Script() { var state = State.KILLING @@ -147,7 +148,7 @@ class CowKiller : Script() { override fun newInstance(): Script { val script = CowKiller() - script.bot = CombatBotAssembler().produce(CombatBotAssembler.Type.values().random(),CombatBotAssembler.Tier.LOW,spawnZone.randomLoc) + script.bot = CombatBotAssembler().produce(CombatBotAssembler.Type.values().random(), CombatBotAssembler.Tier.LOW,spawnZone.randomLoc) script.state = State.KILLING return script } diff --git a/Server/src/main/content/global/bots/DoublingMoney.kt b/Server/src/main/content/global/bots/DoublingMoney.kt new file mode 100644 index 000000000..785fd8690 --- /dev/null +++ b/Server/src/main/content/global/bots/DoublingMoney.kt @@ -0,0 +1,377 @@ +package content.global.bots + +import core.game.container.Container +import core.game.interaction.Option._P_TRADE +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.request.trade.TradeModule +import core.game.node.entity.skill.Skills +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.* +import core.integrations.discord.Discord +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import org.rs09.consts.Items +import core.ServerConstants +import core.game.bots.AIRepository +import content.global.bots.Adventurer.Companion.lumbridge +import core.game.bots.Script +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners +import core.game.world.ImmerseWorld +import java.io.File +import java.io.FileReader +import java.util.Random +import kotlin.math.min + +/** + * A bot that doubles money + * 30% chance to double money, 70% chance to scam + * @author dginovker + */ +class DoublingMoney : Script() { + /** + * If we're in ScamMode, we will never trade away any more coins + */ + private var scamMode = false + + /** + * Whether the bot has run its setup (gotten appearance made/etc.) + */ + private var setup = false + + /** + * Player who most recently traded with this bot + */ + var victim: Player? = null + + /** + * Player who the bot took money from + */ + private var playerOwed: Player? = null + + /** + * Whether we sent a trade request to the player we owe yet + */ + private var sentTradeRequest = false + + /** + * Amount the bot owes the player (2x the amount the player gave the bot) + */ + private var debtOwed: Int = 0 + + /** + * How many ticks the bot has run for + */ + var ticks = 0 + + /** + * Duration for how long the bot will take a nap + */ + private var sleepTime = 0 + + /** + * Debug information about what state the bot is in + */ + private var stateString = "init" + + /** + * How long the bot will stay around for + */ + private val maxTicks = (400..750).random() + + enum class Effort { + LOW, + HIGH, + VERY_HIGH + } + + /** + * Characteristics about how hard the bot will work + * Most likely to be LOW, small chance to be HIGH, very small chance to be VERY_HIGH + */ + private val effort = when (Random().nextInt(100)) { + in 0..90 -> Effort.LOW + in 90..98 -> Effort.HIGH + else -> Effort.VERY_HIGH + } + + // Priority calculation -> Execute method mapping. Priority calculation with the highest return value is executed + var states = mapOf( + fun(): Int { + if (setup) return -1 + return 250 + } to { + stateString = "Putting on my outfit.." + var appearance = if (Math.random() < 0.5) "noob" else "intermediate" + if (effort == Effort.VERY_HIGH && Math.random() < 0.5) appearance = "veteran" + val appearancesArray = botAppearances[appearance] as JSONArray + scriptAPI.loadAppearanceAndEquipment(appearancesArray.random() as JSONObject) + setup = true + }, + fun(): Int { + val botTradeModule = TradeModule.getExtension(bot) + if (botTradeModule != null) return -1 + return 0 + } to { + stateString = "Doing nothing" + }, + fun(): Int { + return 1 + } to { + stateString = "Saying Doubling money" + when (effort) { + Effort.LOW -> { + scriptAPI.sendChat("Doubling money") + sleepTime = 7 + } + Effort.HIGH -> { + 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(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(EntityFlag.Chat, ctx) + sleepTime = 9 + } + } + }, + fun(): Int { + if (victim == null) return -1 + return 10 + } to { + stateString = "Accepting trade request" + // Send trade request back to victim + InteractionListeners.run(-1, IntType.PLAYER, "trade with", bot, victim!!) + victim = null + sleepTime = 3 + }, + fun(): Int { + if (debtOwed >= 0) return -1 + val botTradeModule = TradeModule.getExtension(bot) ?: return -1 + val playerTradeModule = TradeModule.getExtension(botTradeModule.target) ?: return -1 + if (!playerTradeModule.container!!.containsAtLeastOneItem(Items.COINS_995)) return 15 + return -1 + } to { + val botTradeModule = TradeModule.getExtension(bot) + stateString = "Waiting for ${botTradeModule!!.target} to post money" + sleepTime = 5 + }, + fun(): Int { + // If we have debt and are in the trade screen with the player who we owe, but have not put up coins, put up coins + if (debtOwed == 0) return -1 + val botTradeModule = TradeModule.getExtension(bot) ?: return -1 + if (botTradeModule.target != playerOwed) return -1 + if (!botTradeModule.container!!.containsAtLeastOneItem(Items.COINS_995)) return 150 + return -1 + } to { + stateString = "Paying off ${playerOwed!!.username} $debtOwed coins" + val botTradeModule = TradeModule.getExtension(bot) + val coinSlot = bot.inventory.getSlot(Item(Items.COINS_995)) + botTradeModule!!.container!!.offer(coinSlot, debtOwed) + debtOwed = 0 + playerOwed = null + }, + fun(): Int { + val botTradeModule = TradeModule.getExtension(bot) ?: return -1 + val playerTradeModule = TradeModule.getExtension(botTradeModule.target) ?: return -1 + if (botTradeModule.container!!.containsAtLeastOneItem(Items.COINS_995)) return 200 + if (playerTradeModule.container!!.containsAtLeastOneItem(Items.COINS_995)) return 200 + return -1 + } to { + stateString = "Accepting any trade screen that has coins put up" + val botTradeModule = TradeModule.getExtension(bot) + val playerTradeModule = TradeModule.getExtension(botTradeModule!!.target) + if (botTradeModule.isAccepted) return@to + + sleepTime = (1..if (botTradeModule.getInterface() == TradeModule.MAIN_INTERFACE) 2 else 5).random() + + val coinsFromBot = botTradeModule.container!!.getAmount(Items.COINS_995) + 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(EntityFlag.Chat, ctx) + + sleepTime = 7 + } + if (botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE && Math.random() < 0.2 && effort == Effort.HIGH && coinsFromBot > 0) { + scriptAPI.sendChat("Enjoy") + sleepTime += 4 + } + + if (botTradeModule.getInterface() == TradeModule.MAIN_INTERFACE && scamMode) { + sleepTime = maxOf(3, sleepTime) + } + + if (botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE) { + val player = botTradeModule.target + var playerNetPoL = player!!.getAttribute("double-money-net-pol", "0").toInt() + playerNetPoL += coinsFromBot + playerNetPoL -= playerTradeModule!!.container!!.getAmount(Items.COINS_995) + player.setAttribute("double-money-net-pol", playerNetPoL.toString()) + // if playerNetPoL > 100k, send a Discord notification + if (playerNetPoL > 100_000) { + Discord.postPlayerAlert(player.username, "Made too much off Doubling Money bots: ${playerNetPoL / 1000}k") + } + } + // If we're in scamMode, we cannot actually setAccepted to true if we're giving away coins! (Gotta fake em out ;) + if (scamMode && botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE && coinsFromBot > 0) { + botTradeModule.decline() + } else { + botTradeModule.setAccepted(true, update = true) + } + sentTradeRequest = false + }, + fun(): Int { + if (sentTradeRequest) return 50 + return -1 + } to { + // Wait for the other player to accept + }, + fun(): Int { + if (TradeModule.getExtension(bot) == null && debtOwed > 0 && playerOwed != null) return 25 + return -1 + } to { + // Scam over 200k payout or 70% chance + if (Math.random() < 0.7 || debtOwed > 200_000) { + stateString = "Scamming" + scamMode = true + if (effort != Effort.VERY_HIGH || Math.random() < 0.5) { + // At high scam effort, there's a chance we fake giving them coins back to keep milking + // with scamMode = true, we will never accept giving coins away at the second trade screen again + terminate() + } + } + stateString = "Sending trade request to ${playerOwed!!.name} to give them $debtOwed coins" + playerOwed!!.interaction.handle(bot, _P_TRADE) + InteractionListeners.run(-1, IntType.PLAYER, "trade with", bot, playerOwed!!) + sentTradeRequest = true + }, + fun(): Int { + // If we're not near a doubling loc, or another doubling bot is on our loc, walk to random one + var minDist = 1000.0 + for (loc in doublingLocs) { + minDist = min(minDist, loc.getDistance(bot.location)) + } + if (minDist > 1) return 2 + RegionManager.forId(bot.location.regionId).planes[bot.location.z].players.forEach { + if (AIRepository.PulseRepository[it?.username?.lowercase()]?.botScript is DoublingMoney + && it != bot + && it.location.getDistance(bot.location) <= 1) { + return 2 + } + } + return -1 + } to { + stateString = "Walking to doubling location" + val loc = doublingLocs.random() + scriptAPI.walkTo(loc) + sleepTime = 5 + }, + fun(): Int { + // If we're past the max ticks, log out + if (ticks > maxTicks) return 30 + return -1 + } to { + stateString = "Logging out" + terminate() + }, + fun(): Int { + // If we're past 1,000 ticks, force terminate + if (ticks > 1000) return 1_000_000 + return -1 + } to { + stateString = "Force terminating" + terminate() + } + ) + + override fun toString(): String { + return "stateString: $stateString, sleepTime: $sleepTime, ticks: $ticks, maxTicks: $maxTicks, debtOwed: $debtOwed, playerOwed: $playerOwed, victim: $victim, effort: $effort, scamMode: $scamMode" + } + + override fun tick() { + if(!bot.isActive){ + running = false + return + } + ticks ++ + if (sleepTime > 0) { + sleepTime -- + return + } + + // Execute the priority calculations, then execute the method of the one which returned highest + states.maxByOrNull { it.key() }?.value?.invoke() + } + + fun tradeReceived(p: Player) { + victim = p + } + + fun itemsReceived(p: Player, container: Container?) { + if (container == null) return + val coins: Item = container.get(Item(Items.COINS_995)) ?: return + playerOwed = p + debtOwed = coins.amount * 2 + 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(EntityFlag.Chat, ctx) + } + } + + fun terminate() { + scriptAPI.teleport(lumbridge) + debtOwed = 0 + bot.isActive = false + sleepTime = 500 + AIRepository.PulseRepository.remove(bot.username.lowercase()) + + ImmerseWorld.spawnDoubleMoneyBot(true) + } + + override fun newInstance(): Script? { + // this straight doesn't get called.... + return null + } + + init { + skills[Skills.AGILITY] = 99 + inventory.add(Item(Items.COINS_995, 1_000_000)) + } + + companion object { + val startingLocs = arrayOf(Location.create(3184, 3488, 0), + Location.create(3167, 3461, 0), + Location.create(3170, 3444, 0), + Location.create(3165, 3487, 0), + Location.create(3167, 3489, 0), + Location.create(3162, 3491, 0)) + + val doublingLocs = arrayOf(Location.create(3159, 3492, 0), + Location.create(3164, 3484, 0), + Location.create(3169, 3486, 0), + Location.create(3172, 3489, 0), + Location.create(3168, 3483, 0), + Location.create(3166, 3493, 0), + Location.create(3157, 3498, 0), + Location.create(3157, 3485, 0) + ) + + // load from data/botdata/ge_bot_appearances_and_equipment.json + private val jsonFilePath = "${ServerConstants.BOT_DATA_PATH}/ge_bot_appearances_and_equipment.json" + private val jsonFile = File(jsonFilePath) + private val parser = JSONParser() + val botAppearances = parser.parse(FileReader(jsonFile)) as JSONObject + } + +} diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/DraynorFisher.kt b/Server/src/main/content/global/bots/DraynorFisher.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/DraynorFisher.kt rename to Server/src/main/content/global/bots/DraynorFisher.kt index a59f2d672..79a9bee8c 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/DraynorFisher.kt +++ b/Server/src/main/content/global/bots/DraynorFisher.kt @@ -1,4 +1,4 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse @@ -6,7 +6,10 @@ 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 rs09.game.ai.skillingbot.SkillingBotAssembler +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) @@ -21,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/kotlin/rs09/game/ai/general/scriptrepository/DraynorWillows.kt b/Server/src/main/content/global/bots/DraynorWillows.kt similarity index 90% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/DraynorWillows.kt rename to Server/src/main/content/global/bots/DraynorWillows.kt index f91f224a1..67d23669b 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/DraynorWillows.kt +++ b/Server/src/main/content/global/bots/DraynorWillows.kt @@ -1,5 +1,5 @@ -package rs09.game.ai.general.scriptrepository - +package content.global.bots +import core.game.bots.* import core.game.component.Component import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse @@ -7,9 +7,9 @@ import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners -import rs09.tools.stringtools.colorize +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners +import core.tools.colorize @PlayerCompatible @ScriptName("Draynor Willows") @@ -17,9 +17,7 @@ import rs09.tools.stringtools.colorize @ScriptIdentifier("draynor_trees") class DraynorWillows : Script(){ val willowZone = ZoneBorders(3084, 3225,3091, 3239) - - - val bankZone = ZoneBorders(3092, 3240,3094, 3246) + val bankZone = ZoneBorders(3092, 3245,3092, 3242) var state = State.INIT var logCount = 0 @@ -43,7 +41,7 @@ class DraynorWillows : Script(){ val willowtree = scriptAPI.getNearestNode("willow", true) bot.interfaceManager.close() willowtree?.let { InteractionListeners.run(willowtree.id, - InteractionListener.SCENERY,"Chop down",bot,willowtree) } + IntType.SCENERY,"Chop down",bot,willowtree) } if (bot.inventory.isFull) state = State.BANKING } diff --git a/Server/src/main/content/global/bots/FarmerThiever.kt b/Server/src/main/content/global/bots/FarmerThiever.kt new file mode 100644 index 000000000..bf3deb1ff --- /dev/null +++ b/Server/src/main/content/global/bots/FarmerThiever.kt @@ -0,0 +1,72 @@ +package content.global.bots +import core.game.world.map.zone.ZoneBorders +import core.game.bots.SkillingBotAssembler +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.bots.Script +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners + +class FarmerThiever : Script() { + val pickpocketZone = ZoneBorders(3074,3245,3086,3255) + val bankZone = ZoneBorders(3092, 3245,3092, 3242) + val food = Items.JUG_OF_WINE_1993 + val foodAmount = 10 + var state = State.PICKPOCKETING + + + override fun tick() { + when(state) { + + State.PICKPOCKETING -> { + if (!pickpocketZone.insideBorder(bot)) + scriptAPI.walkTo(pickpocketZone.randomLoc) + if (bot.inventory.isFull) + state = State.BANKING + else { + val farmer = scriptAPI.getNearestNode(NPCs.MASTER_FARMER_2234, false) + bot.interfaceManager.close() + scriptAPI.eat(food) + if (farmer != null) { + InteractionListeners.run(farmer.id, IntType.NPC, "Pickpocket", bot, farmer) + } + } + } + State.BANKING -> { + val bank = scriptAPI.getNearestNode("Bank Booth",true) + bank ?: return + if (!bankZone.insideBorder(bot)) + scriptAPI.walkTo(bankZone.randomLoc) + else { + bot.faceLocation(bank.location) + bot.inventory.clear() + bot.inventory.add(Item(food, foodAmount)) + state = State.PICKPOCKETING + } + } + } + } + + + + + override fun newInstance(): Script { + val script = FarmerThiever() + script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR,bot.startLocation) + return script + } + enum class State { + BANKING, + PICKPOCKETING + } + + init { + inventory.add(Item(food,foodAmount)) + skills[Skills.THIEVING] = 80 + skills[Skills.HITPOINTS] = 10 + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/global/bots/FletchingBankstander.kt b/Server/src/main/content/global/bots/FletchingBankstander.kt new file mode 100644 index 000000000..dd60a4e3c --- /dev/null +++ b/Server/src/main/content/global/bots/FletchingBankstander.kt @@ -0,0 +1,45 @@ +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 core.game.node.item.Item +import org.rs09.consts.Items + +class FletchingBankstander : Script(){ + var state = State.FLETCHING + override fun tick() { + val bank = scriptAPI.getNearestNode("Bank booth") + bot.faceLocation(bank?.location) + state = when(state){ + State.FLETCHING -> { + bot.inventory.add(Item(Items.KNIFE_946)) + bot.inventory.add(Item(Items.LOGS_1511,27)) + CraftItemWithLogScript(bot, LogCraftInfo.ARROW_SHAFT, 27).invoke() + State.BANKING + } + + State.BANKING -> { + bot.inventory.clear() + State.FLETCHING + } + } + } + + init { + skills[Skills.FLETCHING] = 99 + } + + override fun newInstance(): Script { + val script = FletchingBankstander() + script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.AVERAGE,bot.startLocation) + return script + } + + enum class State { + FLETCHING, + BANKING + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GenericSlayerBot.kt b/Server/src/main/content/global/bots/GenericSlayerBot.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GenericSlayerBot.kt rename to Server/src/main/content/global/bots/GenericSlayerBot.kt index b6a11872c..5b4a7e31e 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GenericSlayerBot.kt +++ b/Server/src/main/content/global/bots/GenericSlayerBot.kt @@ -1,4 +1,4 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse @@ -9,7 +9,8 @@ import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import core.tools.RandomFunction import org.rs09.consts.Items -import rs09.game.ai.AIRepository +import core.game.bots.AIRepository +import core.game.bots.Script /** * A bot that does various random slayer tasks throughout the game and sells the loot on the GE. @@ -63,7 +64,7 @@ class GenericSlayerBot : Script() { } } } - + else -> {} } } @@ -143,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){ @@ -219,7 +220,6 @@ class GenericSlayerBot : Script() { GETTING_TASK, GOING_TO_HUB, KILLING_ENEMY, - LOOTING, GOING_TO_BANK, BANKING, GOING_TO_GE, @@ -239,7 +239,7 @@ class GenericSlayerBot : Script() { * List of tasks that this bot can receive */ enum class Task(val npc_name: String, val minAmt: Int, val maxAmt: Int, val hub: TaskHub, val borders: ZoneBorders) { - CAVE_CRAWLER("Cave crawler",20,100,TaskHub.FREMENNIK_CAVE, ZoneBorders(2778, 9988,2798, 10002)) + CAVE_CRAWLER("Cave crawler",20,100, TaskHub.FREMENNIK_CAVE, ZoneBorders(2778, 9988,2798, 10002)) } /** diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GlassBlowingBankstander.kt b/Server/src/main/content/global/bots/GlassBlowingBankstander.kt similarity index 77% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GlassBlowingBankstander.kt rename to Server/src/main/content/global/bots/GlassBlowingBankstander.kt index b55d00d0e..2e9e636c2 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GlassBlowingBankstander.kt +++ b/Server/src/main/content/global/bots/GlassBlowingBankstander.kt @@ -1,11 +1,12 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots -import core.game.interaction.inter.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 core.game.node.entity.skill.crafting.GlassProduct import core.game.node.item.Item import org.rs09.consts.Items -import rs09.game.ai.skillingbot.SkillingBotAssembler class GlassBlowingBankstander : Script(){ var state = State.BLOWING @@ -16,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/kotlin/rs09/game/ai/general/scriptrepository/GnomeAgility.kt b/Server/src/main/content/global/bots/GnomeAgility.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GnomeAgility.kt rename to Server/src/main/content/global/bots/GnomeAgility.kt index de32ecbaf..7341ff4dd 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GnomeAgility.kt +++ b/Server/src/main/content/global/bots/GnomeAgility.kt @@ -1,11 +1,7 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots -import api.ContentAPI +import core.game.bots.* import core.game.world.map.zone.ZoneBorders -import rs09.game.ai.general.ScriptAPI -import rs09.game.ai.skillingbot.SkillingBotAssembler -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners @PlayerCompatible diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GnomeBowstring.kt b/Server/src/main/content/global/bots/GnomeBowstring.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GnomeBowstring.kt rename to Server/src/main/content/global/bots/GnomeBowstring.kt index e264ec422..a835aa37f 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GnomeBowstring.kt +++ b/Server/src/main/content/global/bots/GnomeBowstring.kt @@ -1,16 +1,16 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots -import api.ContentAPI +import core.api.* import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse -import core.game.node.entity.skill.crafting.spinning.SpinningItem -import core.game.node.entity.skill.crafting.spinning.SpinningPulse +import content.global.skill.crafting.spinning.SpinningItem +import content.global.skill.crafting.spinning.SpinningPulse +import core.game.bots.* import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.path.Pathfinder import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items -import rs09.game.ai.general.ScriptAPI @PlayerCompatible @ScriptName("Gnome Stronghold Bowstring") @@ -90,7 +90,7 @@ class GnomeBowstring : Script() { } State.SPINNING -> { - bot.pulseManager.run(SpinningPulse(bot, Item(Items.FLAX_1779),bot.inventory.getAmount(Items.FLAX_1779),SpinningItem.FLAX)) + bot.pulseManager.run(SpinningPulse(bot, Item(Items.FLAX_1779),bot.inventory.getAmount(Items.FLAX_1779), SpinningItem.FLAX)) sLadderSwitch = true state = State.FIND_BANK } @@ -131,7 +131,7 @@ class GnomeBowstring : Script() { } }) } - if(ContentAPI.freeSlots(bot) > 27){ + if(freeSlots(bot) > 27){ bLadderSwitch = true state = State.RETURN_TO_FLAX } @@ -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 new file mode 100644 index 000000000..740fb7cd0 --- /dev/null +++ b/Server/src/main/content/global/bots/GreenDragonKiller.kt @@ -0,0 +1,286 @@ +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 +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 core.game.node.item.Item +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.map.zone.impl.WildernessZone +import core.tools.RandomFunction +import org.rs09.consts.Items +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.game.node.Node +import core.game.node.entity.combat.CombatSwingHandler +import core.game.node.entity.combat.MeleeSwingHandler +import kotlin.random.Random + +/** + * A bot script for killing green dragons in the wilderness.. Capable of banking, selling on ge, eating, trash talking, buries bones when fleeing and more. + * @param style The combat style the bot is going to use. + * @param area (optional) What area the bot tries to kill dragons in. + * @author Ceikry + */ +class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Script() { + companion object { + val westDragons = ZoneBorders(2971,3606,2991,3628) + val wildernessLine = ZoneBorders(3078,3523,3096,3523) + val edgevilleLine = ZoneBorders(3078,3520,3096,3520) + val bankZone = ZoneBorders(3092,3489,3094,3493) + val trashTalkLines = arrayOf("Bro, seriously?", "Ffs.", "Jesus christ.", "????", "Friendly!", "Get a life dude", "Do you mind??? lol", "Lol.", "Kek.", "One sec burying all the bones.", "Yikes.", "Yeet", "Ah shit, here we go again.", "Cmonnnn", "Plz", "Do you have nothing better to do?", "Cmon bro pls", "I just need to get my prayer up bro jesus", "Reeeeeee", "I cant believe you've done this", "Really m8", "Zomg", "Aaaaaaaaaaaaaaaaaaaaa", "Rofl.", "Oh god oh fuck oh shit", "....", ":|", "A q p", "Hcim btw", "I hope the revenants kill your mum", "Wrap your ass titties", "Why do this", "Bruh", "Straight sussin no cap fr fr", "This ain't bussin dawg", "Really bro?") + } + var state = State.TO_BANK + var handler: CombatSwingHandler? = null + var lootDelay = 0 + var offerMade = false + var trashTalkDelay = 0 + + var food = if (Random.nextBoolean()){ + Items.LOBSTER_379 + } else if(Random.nextBoolean()){ + Items.SWORDFISH_373 + } else { + Items.SHARK_385 + } + + var myBorders: ZoneBorders? = null + val type = CombatBotAssembler.Type.MELEE + + override fun tick() { + if(!bot.isActive){ + running = false + return + } + + checkFoodStockAndEat() + + when(state){ + + State.KILLING -> { + bot.properties.combatPulse.temporaryHandler = handler + scriptAPI.attackNpcInRadius(bot,"Green dragon",20) + state = State.LOOT_DELAYER + } + + State.LOOT_DELAYER -> { + if(lootDelay < 3) + lootDelay++ + else + state = State.LOOTING + } + + + State.RUNNING -> { + val players = RegionManager.getLocalPlayers(bot.location) + if(players.isEmpty()){ + state = State.TO_DRAGONS + } else { + if(bot.skullManager.level < 21){ + if (scriptAPI.teleportToGE()) + state = State.REFRESHING + return + } + sendTrashTalk() + attemptToBuryBone() + scriptAPI.walkTo(WildernessZone.getInstance().borders.random().randomLoc) + } + } + + State.LOOTING -> { + lootDelay = 0 + val items = AIRepository.groundItems.get(bot) + if(items.isNullOrEmpty()) {state = State.KILLING; return} + if(bot.inventory.isFull) { + if(bot.inventory.containsItem(Item(food))){ + scriptAPI.forceEat(food) + } else { + state = State.TO_BANK + } + return + } + items.toTypedArray().forEach {it: Item -> scriptAPI.takeNearestGroundItem(it.id)} + } + + State.TO_BANK -> { + if(!wildernessLine.insideBorder(bot) && bot.location.y > 3521) + scriptAPI.walkTo(wildernessLine.randomLoc) + if(wildernessLine.insideBorder(bot)){ + val ditch = scriptAPI.getNearestNode("Wilderness Ditch",true) + ditch ?: return + ditch.interaction.handle(bot,ditch.interaction[0]) + } + if(!bankZone.insideBorder(bot)) + scriptAPI.walkTo(bankZone.randomLoc) + if(bankZone.insideBorder(bot)){ + val bank = scriptAPI.getNearestNode("Bank Booth",true) + bank ?: return + bot.pulseManager.run(object: MovementPulse(bot,bank, DestinationFlag.OBJECT){ + override fun pulse(): Boolean { + bot.faceLocation(bank.location) + state = State.BANKING + return true + } + }) + } + } + + State.BANKING -> { + bot.pulseManager.run(object: Pulse(25){ + override fun pulse(): Boolean { + for(item in bot.inventory.toArray()){ + item ?: continue + if(item.name.toLowerCase().contains("lobster") || item.name.toLowerCase().contains("swordfish") || item.name.toLowerCase().contains("shark")) continue + if(item.id == 995) continue + bot.bank.add(item) + } + bot.inventory.clear() + state = if(bot.bank.getAmount(food) < 10) + State.TO_GE + else + State.TO_DRAGONS + for(item in inventory) + bot.inventory.add(item) + scriptAPI.withdraw(food,10) + bot.fullRestore() + return true + } + }) + } + + State.BUYING_FOOD -> { + state = State.TO_DRAGONS + bot.bank.add(Item(food,50)) + bot.bank.refresh() + scriptAPI.withdraw(food, 10) + } + + State.TO_DRAGONS -> { + offerMade = false + if(bot.location.x >= 3143){ + if(bot.location != Location.create(3144, 3514, 0)) + scriptAPI.walkTo(Location.create(3144, 3514, 0)) + else { + val shortcut = scriptAPI.getNearestNode("Underwall Tunnel",true) + shortcut ?: return + InteractionListeners.run(shortcut.id, IntType.SCENERY, "climb-into", bot, shortcut) + } + } else { + if (!edgevilleLine.insideBorder(bot) && bot.location.y < 3520) { + scriptAPI.walkTo(edgevilleLine.randomLoc) + return + } + if (edgevilleLine.insideBorder(bot)) { + val ditch = scriptAPI.getNearestNode("Wilderness Ditch", true) + ditch ?: return + ditch.interaction.handle(bot, ditch.interaction[0]).also { return } + } + if (bot.location.y > 3520 && !myBorders!!.insideBorder(bot)) + scriptAPI.walkTo(myBorders!!.randomLoc).also { return } + if (myBorders!!.insideBorder(bot)) + state = State.KILLING + } + } + + State.TO_GE -> { + if(bot.location.x < 3143) { + if(bot.location == Location.create(3136, 3517, 0)){ + val shortcut = scriptAPI.getNearestNode("Underwall Tunnel",true) + shortcut ?: return + InteractionListeners.run(shortcut.id, IntType.SCENERY, "climb-into", bot, shortcut) + } else { + scriptAPI.walkTo(Location.create(3136, 3517, 0)) + } + return + } + if(bot.location != Location.create(3165, 3487, 0)) { + scriptAPI.walkTo(Location.create(3165, 3487, 0)) + } else { + state = State.SELL_GE + } + } + + State.SELL_GE -> { + scriptAPI.sellAllOnGe() + state = State.BUYING_FOOD + } + + State.REFRESHING -> { + running = false + return + } + + } + } + + private fun attemptToBuryBone() { + if (bot.inventory.containsAtLeastOneItem(Items.DRAGON_BONES_536)) { + InteractionListeners.run(Items.DRAGON_BONES_536, IntType.ITEM, "bury", bot, bot.inventory.get(Item(Items.DRAGON_BONES_536))) + } + } + + private fun checkFoodStockAndEat() { + if (bot.inventory.getAmount(food) < 3 && state == State.KILLING) + state = State.TO_BANK + scriptAPI.eat(food) + } + + private fun sendTrashTalk() { + if (trashTalkDelay-- == 0) + scriptAPI.sendChat(trashTalkLines.random()) + else + trashTalkDelay = RandomFunction.random(10, 30) + } + + override fun newInstance(): Script { + val script = GreenDragonKiller(style) + val tier = CombatBotAssembler.Tier.MED + script.bot = CombatBotAssembler().assembleMeleeDragonBot(tier, bot.startLocation) + return script + } + + enum class State { + KILLING, + RUNNING, + LOOTING, + LOOT_DELAYER, + BANKING, + TO_BANK, + TO_DRAGONS, + TO_GE, + SELL_GE, + REFRESHING, + BUYING_FOOD + + } + + init { + handler = MeleeSwinger(this) + equipment.add(Item(Items.ANTI_DRAGON_SHIELD_1540)) + myBorders = westDragons + skills[Skills.AGILITY] = 99 + bankZone.addException(ZoneBorders(3094, 3492,3094, 3492)) + bankZone.addException(ZoneBorders(3094, 3490,3094, 3490)) + } + + internal class MeleeSwinger(val script: GreenDragonKiller) : MeleeSwingHandler() { + override fun canSwing(entity: Entity, victim: Entity): InteractionType? { + if(victim is Player || victim.name.contains("revenant", ignoreCase = true)) { + script.state = State.RUNNING + script.bot.pulseManager.clear() + } + return super.canSwing(entity, victim) + } + } +} diff --git a/Server/src/main/content/global/bots/Idler.kt b/Server/src/main/content/global/bots/Idler.kt new file mode 100644 index 000000000..684f4240e --- /dev/null +++ b/Server/src/main/content/global/bots/Idler.kt @@ -0,0 +1,12 @@ +package content.global.bots + +import core.game.bots.Script + +class Idler : Script(){ + override fun tick() { + } + + override fun newInstance(): Script { + return this + } +} \ No newline at end of file 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/kotlin/rs09/game/ai/general/scriptrepository/LobsterCatcher.kt b/Server/src/main/content/global/bots/LobsterCatcher.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/LobsterCatcher.kt rename to Server/src/main/content/global/bots/LobsterCatcher.kt index d8364100c..9661d41f1 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/LobsterCatcher.kt +++ b/Server/src/main/content/global/bots/LobsterCatcher.kt @@ -1,5 +1,6 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots +import core.game.bots.* import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.node.entity.skill.Skills @@ -11,11 +12,9 @@ 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 rs09.game.ai.AIPlayer -import rs09.game.ai.general.ScriptAPI -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners -import rs09.game.world.GameWorld +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners +import core.game.world.GameWorld import kotlin.random.Random @PlayerCompatible @@ -74,7 +73,7 @@ class LobsterCatcher : Script() { if(spot == null){ state = State.IDLE } else { - InteractionListeners.run(spot.id,InteractionListener.NPC,"cage",bot,spot) + InteractionListeners.run(spot.id, IntType.NPC,"cage",bot,spot) } if(bot.inventory.isFull){ state = State.FIND_BANK @@ -98,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)) } } } @@ -195,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 new file mode 100644 index 000000000..75e0a6aac --- /dev/null +++ b/Server/src/main/content/global/bots/ManThiever.kt @@ -0,0 +1,28 @@ +package content.global.bots + +import core.game.node.item.Item +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() { + override fun tick() { + val man = scriptAPI.getNearestNode("Man") + bot.interfaceManager.close() + man?.let { InteractionListeners.run(man.id, + IntType.NPC,"Pickpocket",bot,man) } + } + + override fun newInstance(): Script? { + val script = ManThiever() + script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR, bot.startLocation) + return script + } + + init { + 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 new file mode 100644 index 000000000..909d0a41e --- /dev/null +++ b/Server/src/main/content/global/bots/NonBankingMiner.kt @@ -0,0 +1,41 @@ +package content.global.bots + +import core.api.produceGroundItem +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +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 NonBankingMiner : Script() { + override fun tick() { + val rock = scriptAPI.getNearestNode(11957,true) + if(rock != null && !bot.inventory.isFull){ + InteractionListeners.run(rock.id, IntType.SCENERY,"mine",bot,rock) + } + //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(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 + if(bot.inventory.isFull && (!bot.inventory.containsAtLeastOneItem(Items.TIN_ORE_438))){ + bot.inventory.clear() + bot.inventory.add(Item(Items.MITHRIL_PICKAXE_1273)) + } + } + + override fun newInstance(): Script { + val script = NonBankingMiner() + script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR,bot.startLocation) + return script + } + + init { + skills[Skills.ATTACK] = 99 + inventory.add(Item(Items.MITHRIL_PICKAXE_1273)) + skills[Skills.MINING] = 50 + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SeersFlax.kt b/Server/src/main/content/global/bots/SeersFlax.kt similarity index 80% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SeersFlax.kt rename to Server/src/main/content/global/bots/SeersFlax.kt index b0365f4a4..f8dcb56ab 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SeersFlax.kt +++ b/Server/src/main/content/global/bots/SeersFlax.kt @@ -1,30 +1,37 @@ -package rs09.game.ai.general.scriptrepository +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 core.game.node.entity.skill.crafting.spinning.SpinningItem -import core.game.node.entity.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 rs09.game.ai.skillingbot.SkillingBotAssembler +@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 -> { @@ -57,15 +64,15 @@ class SeersFlax : Script(){ } State.SPINNING -> { - bot.pulseManager.run(SpinningPulse(bot, Item(Items.FLAX_1779),bot.inventory.getAmount(Items.FLAX_1779),SpinningItem.FLAX)) + bot.pulseManager.run(SpinningPulse(bot, Item(Items.FLAX_1779),bot.inventory.getAmount(Items.FLAX_1779), SpinningItem.FLAX)) state = State.FIND_BANK } 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) -> { @@ -100,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) } } @@ -123,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/kotlin/rs09/game/ai/general/scriptrepository/SeersMagicTrees.kt b/Server/src/main/content/global/bots/SeersMagicTrees.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SeersMagicTrees.kt rename to Server/src/main/content/global/bots/SeersMagicTrees.kt index bf0f084cd..c3e338404 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SeersMagicTrees.kt +++ b/Server/src/main/content/global/bots/SeersMagicTrees.kt @@ -1,5 +1,6 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots +import core.game.bots.* import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.node.entity.skill.Skills @@ -8,10 +9,8 @@ import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import core.tools.RandomFunction import org.rs09.consts.Items -import rs09.game.ai.general.ScriptAPI -import rs09.game.ai.skillingbot.SkillingBotAssembler -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners @PlayerCompatible @ScriptName("Seers Magics") @@ -40,7 +39,7 @@ class SeersMagicTrees : Script(){ State.CHOPPING -> { val tree = scriptAPI.getNearestNode(1306,true) bot.interfaceManager.close() - tree?.let { InteractionListeners.run(tree.id, InteractionListener.SCENERY,"Chop down",bot,tree) } + tree?.let { InteractionListeners.run(tree.id, IntType.SCENERY,"Chop down",bot,tree) } if(bot.inventory.isFull){ state = State.FIND_BANK } diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SharkCatcher.kt b/Server/src/main/content/global/bots/SharkCatcher.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SharkCatcher.kt rename to Server/src/main/content/global/bots/SharkCatcher.kt index f1a909d53..f7fa50e2e 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SharkCatcher.kt +++ b/Server/src/main/content/global/bots/SharkCatcher.kt @@ -1,5 +1,6 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots +import core.game.bots.* import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.node.entity.skill.Skills @@ -7,11 +8,8 @@ import core.game.node.item.Item import core.game.world.map.Location import core.tools.RandomFunction import org.rs09.consts.Items -import rs09.game.ai.AIPlayer -import rs09.game.ai.general.ScriptAPI -import rs09.game.ge.OfferManager -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners import kotlin.random.Random /* @@ -89,7 +87,7 @@ class SharkCatcher : Script() { State.STOP -> { val botAmount = bot.bank.getAmount(Items.RAW_SHARK_383) + 1 - var geAmount = OfferManager.getQuantitySoldForItem(Items.RAW_SHARK_383) + var geAmount = 0//OfferManager.getQuantitySoldForItem(Items.RAW_SHARK_383) val totalAmount = (geAmount + botAmount) + 1 if((totalAmount > limit) && myCounter++ == 300){ bot.randomWalk(5,5) @@ -118,7 +116,7 @@ class SharkCatcher : Script() { if (Random.nextBoolean()) { tick = 0 val spot = scriptAPI.getNearestNode(334, false) - spot?.let { InteractionListeners.run(spot.id, InteractionListener.NPC,"harpoon",bot,spot) } + spot?.let { InteractionListeners.run(spot.id, IntType.NPC,"harpoon",bot,spot) } if(bot.inventory.isFull){ state = State.FIND_BANK } else { @@ -174,7 +172,7 @@ class SharkCatcher : Script() { State.SELL_GE -> { val botAmount = bot.bank.getAmount(Items.RAW_SHARK_383) + 1 - var geAmount = OfferManager.getQuantitySoldForItem(Items.RAW_SHARK_383) + var geAmount = 0//OfferManager.getQuantitySoldForItem(Items.RAW_SHARK_383) val totalAmount = (geAmount + botAmount) + 1 if(totalAmount > limit){ scriptAPI.walkTo(Location.create(3164, 3487, 0)) diff --git a/Server/src/main/content/global/bots/ShootingStarBot.kt b/Server/src/main/content/global/bots/ShootingStarBot.kt new file mode 100644 index 000000000..5e990b82f --- /dev/null +++ b/Server/src/main/content/global/bots/ShootingStarBot.kt @@ -0,0 +1,95 @@ +package content.global.bots + +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.world.map.Location +import core.tools.RandomFunction +import org.rs09.consts.Items +import core.game.bots.GeneralBotCreator +import content.global.activity.shootingstar.ShootingStarPlugin +import core.game.bots.Script +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners + +class ShootingStarBot : Script() { + private var state = State.FULL_IDLE + private var timerCountdown = 0 + val star = ShootingStarPlugin.getStar() + + override fun tick() { + bot.fullRestore() + + if(timerCountdown > 0) { + --timerCountdown + return + } + + when(state) { + State.FULL_IDLE -> {} + + State.TELEPORT_TO -> { + scriptAPI.teleport(star.crash_locations[star.location]!!.transform(0, -1, 0)) + state = State.MINING + timerCountdown = 15 + } + + State.MINING -> { + InteractionListeners.run(star.starObject.id, IntType.SCENERY, "mine", bot, star.starObject) + } + + State.TELEPORT_BACK -> { + scriptAPI.teleport(spawnLoc) + timerCountdown = 15 + } + } + } + + init { + skills[Skills.ATTACK] = 41 + skills[Skills.RANGE] = RandomFunction.random(30,99) + skills[Skills.MINING] = 99 + skills[Skills.HITPOINTS] = 99 + skills[Skills.DEFENCE] = 99 + skills[Skills.SUMMONING] = 99 + skills[Skills.PRAYER] = 99 + inventory.add(Item(Items.RUNE_PICKAXE_1275)) + } + + override fun newInstance(): Script { + return ShootingStarBot() + } + + fun activate(instant: Boolean) { + state = State.TELEPORT_TO + if(!instant) + timerCountdown = RandomFunction.random(500) + } + + fun sleep() { + state = State.TELEPORT_BACK + } + + fun isMining() : Boolean { + return state == State.MINING + } + + fun isIdle() : Boolean { + return state == State.FULL_IDLE + } + + internal enum class State { + FULL_IDLE, + TELEPORT_TO, + MINING, + TELEPORT_BACK + } + + companion object { + val spawnLoc = Location.create(2230, 3339, 0) + fun new() : ShootingStarBot { + val script = ShootingStarBot() + GeneralBotCreator(script, spawnLoc) + return script + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/VarrockEssenceMiner.kt b/Server/src/main/content/global/bots/VarrockEssenceMiner.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/VarrockEssenceMiner.kt rename to Server/src/main/content/global/bots/VarrockEssenceMiner.kt index e263a17df..38a9e6b5a 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/VarrockEssenceMiner.kt +++ b/Server/src/main/content/global/bots/VarrockEssenceMiner.kt @@ -1,13 +1,16 @@ -package rs09.game.ai.general.scriptrepository +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 rs09.game.ai.skillingbot.SkillingBotAssembler -import rs09.game.interaction.InteractionListener -import rs09.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, InteractionListener.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/kotlin/rs09/game/ai/general/scriptrepository/VarrockSmither.kt b/Server/src/main/content/global/bots/VarrockSmither.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/VarrockSmither.kt rename to Server/src/main/content/global/bots/VarrockSmither.kt index 75f977627..0d252503c 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/VarrockSmither.kt +++ b/Server/src/main/content/global/bots/VarrockSmither.kt @@ -1,15 +1,16 @@ -package rs09.game.ai.general.scriptrepository +package content.global.bots import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.smithing.Bars -import core.game.node.entity.skill.smithing.SmithingPulse +import content.global.skill.smithing.Bars +import content.global.skill.smithing.SmithingPulse import core.game.node.item.Item import core.game.world.map.Location import core.tools.RandomFunction import org.rs09.consts.Items -import rs09.game.ai.skillingbot.SkillingBotAssembler +import core.game.bots.SkillingBotAssembler +import core.game.bots.Script class VarrockSmither : Script() { var state = State.SMITHING diff --git a/Server/src/main/content/global/dialogue/BankDepositDialogue.kt b/Server/src/main/content/global/dialogue/BankDepositDialogue.kt new file mode 100644 index 000000000..add635877 --- /dev/null +++ b/Server/src/main/content/global/dialogue/BankDepositDialogue.kt @@ -0,0 +1,55 @@ +package content.global.dialogue + +import core.api.dumpBeastOfBurden +import core.api.dumpContainer +import core.api.sendMessage +import core.game.dialogue.DialogueFile +import core.tools.START_DIALOGUE + +/** + * Represents the dialogue shown when the user presses + * "Deposit Beast of Burden" button on the Bank Interface. + * + * @author vddCore + */ +class BankDepositDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> options( + "Deposit Inventory", + "Deposit Worn Equipment", + "Deposit Beast of Burden", + "Cancel" + ).also { stage++ } + + 1 -> when (buttonID) { + 1 -> player?.let { + end() + + if (it.inventory.isEmpty) { + sendMessage(it, "You have nothing in your inventory that you can deposit.") + } else { + dumpContainer(it, it.inventory) + } + } + + 2 -> player?.let { + end() + + if (it.equipment.isEmpty) { + sendMessage(it, "You have no equipment that you can deposit.") + } else { + dumpContainer(it, it.equipment) + } + } + + 3 -> player?.let { + end() + dumpBeastOfBurden(it) + } + + 4 -> end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/dialogue/BankHelpDialogue.kt b/Server/src/main/content/global/dialogue/BankHelpDialogue.kt new file mode 100644 index 000000000..ca8c6453a --- /dev/null +++ b/Server/src/main/content/global/dialogue/BankHelpDialogue.kt @@ -0,0 +1,57 @@ +package content.global.dialogue + +import core.api.openInterface +import core.api.sendDialogue +import core.api.sendItemDialogue +import core.game.node.item.Item +import org.rs09.consts.Components +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.tools.START_DIALOGUE + +/** + * Represents the dialogue shown when the user presses + * the "?" button on the Bank Interface. + * + * @author vddCore + */ +class BankHelpDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> options( + "Check Bank Value", + "Banking Assistance", + "Close" + ).also { stage++ } + + 1 -> when (buttonID) { + 1 -> player?.let { + end() + + val wealth = it.bank.wealth + + if (wealth > 0) { + val word = if (wealth != 1) "coins" else "coin" + + sendItemDialogue( + it, + Item(Items.COINS_995, wealth), + "
Your bank is worth ${wealth} ${word}." + ) + } else { + sendDialogue(it, "You have no valuables in your bank.") + } + } + + 2 -> player?.let { + end() + + it.bank.close() + openInterface(it, Components.BANK_V2_HELP_767) + } + + 3 -> end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/dialogue/GardenerDialoguePlugin.kt b/Server/src/main/content/global/dialogue/GardenerDialoguePlugin.kt new file mode 100644 index 000000000..6c192972c --- /dev/null +++ b/Server/src/main/content/global/dialogue/GardenerDialoguePlugin.kt @@ -0,0 +1,233 @@ +package content.global.dialogue + +import content.global.skill.farming.FarmerPayOptionDialogue +import content.global.skill.farming.Farmers +import content.global.skill.farming.FarmingPatch +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 handle(interfaceId: Int, buttonId: Int): Boolean { + 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 -> { + if (patches.size > 1) { + npc("I might. Which one were you thinking of?").also { stage = 20 } + } else { + openPayGardenerDialogue(player, patches[0]) + } + } + + 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(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) + ) + + 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(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 { + sendMessage(player, "You need 25 gp to pay for that.") + } + } + 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 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 (removeItem(player, Item(Items.COINS_995, 35))) { + addItemOrDrop(player, Items.COMPOST_6032) + } else { + sendMessage(player, "You need 35 gp to pay for that.") + } + } + 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 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 (removeItem(player, Item(Items.COINS_995, 15))) { + addItemOrDrop(player, Items.RAKE_5341) + } else { + sendMessage(player, "You need 15 gp to pay for that.") + } + } + 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 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 (removeItem(player, Item(Items.COINS_995, 25))) { + addItemOrDrop(player, Items.WATERING_CAN8_5340) + } else { + sendMessage(player, "You need 25 gp to pay for that.") + } + } + 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 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 (removeItem(player, Item(Items.COINS_995, 15))) { + addItemOrDrop(player, Items.GARDENING_TROWEL_5325) + } else { + sendMessage(player, "You need 15 gp to pay for that.") + } + } + 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 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 (removeItem(player, Item(Items.COINS_995, 15))) { + addItemOrDrop(player, Items.SEED_DIBBER_5343) + } else { + sendMessage(player, "You need 15 gp to pay for that.") + } + } + 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 openPayGardenerDialogue(player: Player, fPatch: FarmingPatch) { + end() + openDialogue(player, FarmerPayOptionDialogue(fPatch.getPatchFor(player)), npc) + } + + override fun getIds(): IntArray { + return Farmers.values().map(Farmers::id).toIntArray() + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/ManDialoguePlugin.java b/Server/src/main/content/global/dialogue/ManDialoguePlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/ManDialoguePlugin.java rename to Server/src/main/content/global/dialogue/ManDialoguePlugin.java index 5f4ceee54..aeff88be1 100644 --- a/Server/src/main/java/core/game/content/dialogue/ManDialoguePlugin.java +++ b/Server/src/main/content/global/dialogue/ManDialoguePlugin.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.global.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import org.rs09.consts.Items; import core.game.node.entity.npc.NPC; @@ -20,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, 663, 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/java/core/game/content/dialogue/TownCrierDialogue.java b/Server/src/main/content/global/dialogue/TownCrierDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/TownCrierDialogue.java rename to Server/src/main/content/global/dialogue/TownCrierDialogue.java index 821533502..f330bda86 100644 --- a/Server/src/main/java/core/game/content/dialogue/TownCrierDialogue.java +++ b/Server/src/main/content/global/dialogue/TownCrierDialogue.java @@ -1,13 +1,20 @@ -package core.game.content.dialogue; +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; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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. @@ -121,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/java/core/game/interaction/inter/AgilityTicketInterface.java b/Server/src/main/content/global/handlers/iface/AgilityTicketInterface.java similarity index 98% rename from Server/src/main/java/core/game/interaction/inter/AgilityTicketInterface.java rename to Server/src/main/content/global/handlers/iface/AgilityTicketInterface.java index aefb9e933..368849e95 100644 --- a/Server/src/main/java/core/game/interaction/inter/AgilityTicketInterface.java +++ b/Server/src/main/content/global/handlers/iface/AgilityTicketInterface.java @@ -1,4 +1,4 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; import core.game.component.Component; import core.game.component.ComponentDefinition; diff --git a/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java b/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java new file mode 100644 index 000000000..9213cf0f6 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/AutocastSelectPlugin.java @@ -0,0 +1,53 @@ +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.combat.equipment.WeaponInterface; +import core.game.node.entity.player.Player; +import core.plugin.Initializable; +import core.plugin.Plugin; + +/** + * Represents the auto casting component plugin. + * @author Emperor + * + */ +@Initializable +public final class AutocastSelectPlugin extends ComponentPlugin { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ComponentDefinition.forId(797).setPlugin(this); + ComponentDefinition.forId(319).setPlugin(this); + ComponentDefinition.forId(310).setPlugin(this); + ComponentDefinition.forId(406).setPlugin(this); + return this; + } + + @Override + public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { + 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) { + w.selectAutoSpell(button, true); + player.getInterfaceManager().openTab(w); + } + return true; + } + +} diff --git a/Server/src/main/java/core/game/interaction/inter/BoltEnchantingInterface.java b/Server/src/main/content/global/handlers/iface/BoltEnchantingInterface.java similarity index 95% rename from Server/src/main/java/core/game/interaction/inter/BoltEnchantingInterface.java rename to Server/src/main/content/global/handlers/iface/BoltEnchantingInterface.java index 37056b445..5d4a567ff 100644 --- a/Server/src/main/java/core/game/interaction/inter/BoltEnchantingInterface.java +++ b/Server/src/main/content/global/handlers/iface/BoltEnchantingInterface.java @@ -1,18 +1,22 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; +import core.api.ContentAPIKt; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.MagicStaff; -import core.game.node.entity.skill.magic.Runes; +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 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/java/core/game/interaction/inter/CharacterDesignInterface.java b/Server/src/main/content/global/handlers/iface/CharacterDesignInterface.java similarity index 88% rename from Server/src/main/java/core/game/interaction/inter/CharacterDesignInterface.java rename to Server/src/main/content/global/handlers/iface/CharacterDesignInterface.java index 91649baec..62c8657bb 100644 --- a/Server/src/main/java/core/game/interaction/inter/CharacterDesignInterface.java +++ b/Server/src/main/content/global/handlers/iface/CharacterDesignInterface.java @@ -1,9 +1,9 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; -import core.game.content.quest.tutorials.tutorialisland.CharacterDesign; +import content.region.misc.tutisland.handlers.iface.CharacterDesign; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/ChocatriceIncubationInterface.kt b/Server/src/main/content/global/handlers/iface/ChocatriceIncubationInterface.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/interaction/inter/ChocatriceIncubationInterface.kt rename to Server/src/main/content/global/handlers/iface/ChocatriceIncubationInterface.kt index 91c763a48..d086cd95c 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/ChocatriceIncubationInterface.kt +++ b/Server/src/main/content/global/handlers/iface/ChocatriceIncubationInterface.kt @@ -1,4 +1,4 @@ -package rs09.game.interaction.inter +package content.global.handlers.iface import core.game.component.Component import core.game.component.ComponentDefinition diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/CreditShopInterface.kt b/Server/src/main/content/global/handlers/iface/CreditShopInterface.kt similarity index 76% rename from Server/src/main/kotlin/rs09/game/interaction/inter/CreditShopInterface.kt rename to Server/src/main/content/global/handlers/iface/CreditShopInterface.kt index 6a7e82d4c..e45f3ef74 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/CreditShopInterface.kt +++ b/Server/src/main/content/global/handlers/iface/CreditShopInterface.kt @@ -1,80 +1,80 @@ -package rs09.game.interaction.inter - -import api.ContentAPI -import core.game.node.entity.player.Player -import core.game.node.item.Item -import org.rs09.consts.Components -import org.rs09.consts.Items -import rs09.game.interaction.InterfaceListener - -class CreditShopInterface : InterfaceListener() { - - val CREDIT_SHOP = Components.CREDIT_SHOP - val TEXT_CHILD = 39 - - override fun defineListeners() { - on(CREDIT_SHOP){player, component, opcode, buttonID, slot, itemID -> - val item = getItem(buttonID) - - if(opcode == 155){ - ContentAPI.sendDialogue(player, "This item costs ${item.price} credits.") - return@on true - } - - if(buttonID == 14 || buttonID == 21){ - val specific = when(opcode){ - 196 -> if(buttonID == 14) Items.RED_PARTYHAT_1038 else Items.RED_HWEEN_MASK_1057 - 124 -> if(buttonID == 14) Items.GREEN_PARTYHAT_1044 else Items.GREEN_HWEEN_MASK_1053 - 199 -> if(buttonID == 14) Items.BLUE_PARTYHAT_1042 else Items.BLUE_HWEEN_MASK_1055 - 234 -> Items.YELLOW_PARTYHAT_1040 - 168 -> Items.PURPLE_PARTYHAT_1046 - 166 -> Items.WHITE_PARTYHAT_1048 - else -> Items.DWARF_WEED_SEED_5303 - } - attemptPurchase(player,specific,item.price) - } else { - attemptPurchase(player,item.id,item.price) - } - return@on true - } - - onOpen(CREDIT_SHOP){player, component -> - sendCredits(player) - return@onOpen true - } - } - - private fun getItem(buttonID: Int): ShopItem{ - return when(buttonID){ - 14 -> ShopItem(Items.BLUE_PARTYHAT_1042,75) - 18 -> ShopItem(Items.SCYTHE_1419,100) - 20 -> ShopItem(Items.JANGLES_THE_MONKEY_14648,200) - 17 -> ShopItem(Items.CHRISTMAS_CRACKER_962,65) - 21 -> ShopItem(Items.BLUE_HWEEN_MASK_1055,65) - 16 -> ShopItem(Items.SANTA_HAT_1050,65) - 19 -> ShopItem(Items.BUNNY_EARS_1037,150) - 15 -> ShopItem(Items.EASTER_RING_7927,100) - else -> ShopItem(0,0) - } - } - - fun sendCredits(player: Player){ - ContentAPI.setInterfaceText(player, "You have ${player.details.credits} credits to spend.", CREDIT_SHOP, TEXT_CHILD) - } - - fun attemptPurchase(player: Player, item: Int, price: Int){ - if(player.details.credits < price){ - ContentAPI.sendDialogue(player, "You don't have enough credits for that.") - return - } - - if(player.inventory.add(Item(item))){ - player.details.credits -= price - } else { - ContentAPI.sendDialogue(player, "You don't have enough inventory space for that.") - } - sendCredits(player) - } - - internal class ShopItem(val id: Int, val price: Int) +package content.global.handlers.iface + +import core.api.* +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 + +class CreditShopInterface : InterfaceListener { + + val CREDIT_SHOP = Components.CREDIT_SHOP + val TEXT_CHILD = 39 + + override fun defineInterfaceListeners() { + on(CREDIT_SHOP){player, component, opcode, buttonID, slot, itemID -> + val item = getItem(buttonID) + + if(opcode == 155){ + sendDialogue(player, "This item costs ${item.price} credits.") + return@on true + } + + if(buttonID == 14 || buttonID == 21){ + val specific = when(opcode){ + 196 -> if(buttonID == 14) Items.RED_PARTYHAT_1038 else Items.RED_HWEEN_MASK_1057 + 124 -> if(buttonID == 14) Items.GREEN_PARTYHAT_1044 else Items.GREEN_HWEEN_MASK_1053 + 199 -> if(buttonID == 14) Items.BLUE_PARTYHAT_1042 else Items.BLUE_HWEEN_MASK_1055 + 234 -> Items.YELLOW_PARTYHAT_1040 + 168 -> Items.PURPLE_PARTYHAT_1046 + 166 -> Items.WHITE_PARTYHAT_1048 + else -> Items.DWARF_WEED_SEED_5303 + } + attemptPurchase(player,specific,item.price) + } else { + attemptPurchase(player,item.id,item.price) + } + return@on true + } + + onOpen(CREDIT_SHOP){player, component -> + sendCredits(player) + return@onOpen true + } + } + + private fun getItem(buttonID: Int): ShopItem { + return when(buttonID){ + 14 -> ShopItem(Items.BLUE_PARTYHAT_1042,75) + 18 -> ShopItem(Items.SCYTHE_1419,100) + 20 -> ShopItem(Items.JANGLES_THE_MONKEY_14648,200) + 17 -> ShopItem(Items.CHRISTMAS_CRACKER_962,65) + 21 -> ShopItem(Items.BLUE_HWEEN_MASK_1055,65) + 16 -> ShopItem(Items.SANTA_HAT_1050,65) + 19 -> ShopItem(Items.BUNNY_EARS_1037,150) + 15 -> ShopItem(Items.EASTER_RING_7927,100) + else -> ShopItem(0,0) + } + } + + fun sendCredits(player: Player){ + setInterfaceText(player, "You have ${player.details.credits} credits to spend.", CREDIT_SHOP, TEXT_CHILD) + } + + fun attemptPurchase(player: Player, item: Int, price: Int){ + if(player.details.credits < price){ + sendDialogue(player, "You don't have enough credits for that.") + return + } + + if(player.inventory.add(Item(item))){ + player.details.credits -= price + } else { + sendDialogue(player, "You don't have enough inventory space for that.") + } + sendCredits(player) + } + + internal class ShopItem(val id: Int, val price: Int) } \ No newline at end of file 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/kotlin/rs09/game/interaction/inter/ExperienceInterface.kt b/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt similarity index 84% rename from Server/src/main/kotlin/rs09/game/interaction/inter/ExperienceInterface.kt rename to Server/src/main/content/global/handlers/iface/ExperienceInterface.kt index 666b376f1..c7003ea83 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/ExperienceInterface.kt +++ b/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt @@ -1,15 +1,16 @@ -package rs09.game.interaction.inter +package content.global.handlers.iface -import api.ContentAPI +import core.api.* 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 rs09.game.system.SystemLogger +import core.tools.Log +import org.rs09.consts.Sounds +import content.data.Quests /** * Represents the experience interface. @@ -41,8 +42,8 @@ class ExperienceInterface() : ComponentPlugin() { if(caller is Plugin<*>) caller.handleSelectionCallback(confirmedSkill, player) else (caller as (Int,Player) -> Unit).invoke(confirmedSkill,player) - ContentAPI.playAudio(player, SOUND) - ContentAPI.closeInterface(player) + playAudio(player, SOUND) + closeInterface(player) } } else { val skill = when (button) { @@ -70,7 +71,7 @@ class ExperienceInterface() : ComponentPlugin() { 46 -> Skills.WOODCUTTING 40 -> Skills.FLETCHING 51 -> Skills.CONSTRUCTION - else -> Skills.SLAYER.also { SystemLogger.logWarn("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 new file mode 100644 index 000000000..69bba13d7 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt @@ -0,0 +1,230 @@ +package content.global.handlers.iface + +import core.api.* +import core.game.event.FairyRingDialEvent +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 content.data.Quests + +/** + * Handles the fairy ring interface + * @author Ceikry + */ +class FairyRingInterface : InterfaceListener { + 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_IFACE){ player, _ -> + openSingleTab(player, LOG_IFACE_ID) + saveVarp(player, VARP_F_RING) + return@onOpen true + } + + onOpen(LOG_IFACE_ID){ player, _ -> + drawLog(player) + return@onOpen true + } + + onClose(RINGS_IFACE){ player, _ -> + closeTabInterface(player) + return@onClose true + } + + on(RINGS_IFACE){ player, _, _, buttonID, _, _ -> + when(buttonID){ + 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) + } + return@on true + } + + on(LOG_IFACE_ID,12){ player, _, _, _, _, _ -> + toggleSortOrder(player) + return@on true + } + + } + + /** + * 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) + } + } + + 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 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 = 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()) + } catch (e: Exception) { null } + + var tile = ring?.tile + if(ring?.checkAccess(player) != true){ + sendDialogue(player, "The ring seems to reject you.") + tile = null + } + if (ring == null || tile == null) { + val center = Location(2412, 4434, 0) + tile = if (RandomFunction.random(2) == 1) { + center.transform(RandomFunction.random(2, 6), RandomFunction.random(2, 6), 0) + } else { + center.transform(RandomFunction.random(-2, -6), RandomFunction.random(-2, -6), 0) + } + if (!RegionManager.isTeleportPermitted(tile) || RegionManager.getObject(tile) != null) { + tile = Location.create(2412, 4431, 0) + } + GameWorld.Pulser.submit(object : Pulse(4, player) { + override fun pulse(): Boolean { + sendPlayerDialogue(player, "Wow, fairy magic sure is useful, I hardly moved at all!", core.game.dialogue.FacialExpression.AMAZED) + return true + } + }) + } else { + if (!player.savedData.globalData.hasTravelLog(ring.ordinal)) { + player.savedData.globalData.setTravelLog(ring.ordinal) + } + } + closeInterface(player) + + ring.let { + if (it == null) { + return@let + } + + player.dispatch(FairyRingDialEvent(it)) + teleport(player, tile!!, TeleportManager.TeleportType.FAIRY_RING) + } + } +} + +enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int = -1) { + AIQ(Location.create(2996, 3114, 0), "Asgarnia: Mudskipper Point", 15), + AIR(Location.create(2700, 3247, 0), "Islands: South of Witchaven", 16), + AJQ(Location.create(2735, 5221, 0), "Dungeons: Dark cave south of Dorgesh-Kaann", 19), + ALR(Location.create(3059, 4875, 0), "Other realms: Abyssal Area", 28), + AJR(Location.create(2780, 3613, 0), "Kandarin: Slayer cave south-east of Rellekka", 20), + AJS(Location.create(2500, 3896, 0), "Islands: Penguins near Miscellania", 21), + AKQ(Location.create(2319, 3619, 0), "Kandarin: Piscatoris Hunter area", 23), + 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, Quests.PRIEST_IN_PERIL, "to use this ring.") + } + }, + ALS(Location.create(2644, 3495, 0), "Kandarin: McGrubor's Wood", 29), + BIP(Location.create(3410, 3324, 0), "Islands: River Salve", 30), + BIQ(Location.create(3251, 3095, 0), "Kharidian Desert: Near Kalphite hive", 31), + BIS(Location.create(2635, 3266, 0), "Kandarin: Ardougne Zoo unicorns", 33), + BJR(null, "Other Realms: Realm of the Fisher King", 36), + BKP(Location.create(2385, 3035, 0), "Feldip Hills: South of Castle Wars", 38), + 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, Quests.PRIEST_IN_PERIL, "to use this ring.") + } + }, + BLP(Location.create(2437, 5126, 0), "Dungeons: TzHaar area", 42), + BLQ(null, "Yu'biusk", 43),//Location.create(2229, 4244, 1) + 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, Quests.THE_FREMENNIK_TRIALS, "to use this ring.") + } + }, + CIQ(Location.create(2528, 3127, 0), "Kandarin: North-west of Yanille", 47), + CJR(Location.create(2705, 3576, 0), "Kandarin: Sinclair Mansion", 52), + CKP(Location.create(2075, 4848, 0), "Other realms: Cosmic Entity's plane", 54), + 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, Quests.PRIEST_IN_PERIL, "to use this ring.") + } + }, + CLP(Location.create(3082, 3206, 0), "Islands: South of Draynor Village", 58), + CLS(Location.create(2682, 3081, 0), "Islands: Jungle spiders near Yanille", 61), + DIR(Location.create(3038, 5348, 0), "Other realms: Goraks' Plane", 64), + DIS(Location.create(3108, 3149, 0), "Misthalin: Wizards' Tower", 65), + DJP(Location.create(2658, 3230, 0), "Kandarin: Tower of Life", 66), + DJR(Location.create(2676, 3587, 0), "Kandarin: Sinclair Mansion (west)", 68), + DKP(Location.create(2900, 3111, 0), "Karamja: South of Musa Point", 70), + DKR(Location.create(3129, 3496, 0), "Misthalin: Edgeville", 72), + DKS(Location.create(2744, 3719, 0), "Kandarin: Snowy Hunter area", 73), + DLQ(Location.create(3423, 3016, 0), "Kharidian Desert: North of Nardah", 75), + DLR(Location.create(2213, 3099, 0), "Islands: Poison Waste south of Isafdar", 76), + AIS(null), AIP(null), AKP(null), FAIRY_HOME(Location.create(2412, 4434, 0)); + + open fun checkAccess(player: Player) : Boolean { + return true + } +} diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/FurClothingInterface.kt b/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt similarity index 75% rename from Server/src/main/kotlin/rs09/game/interaction/inter/FurClothingInterface.kt rename to Server/src/main/content/global/handlers/iface/FurClothingInterface.kt index 26ad97c93..a335a5fbb 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/FurClothingInterface.kt +++ b/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt @@ -1,17 +1,18 @@ -package rs09.game.interaction.inter +package content.global.handlers.iface -import api.ContentAPI +import core.api.* import core.game.component.Component 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 +import core.tools.colorize import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import rs09.tools.stringtools.colorize private const val FUR_CLOTHING_COMPONENT_ID = 477 @@ -60,16 +61,16 @@ private val OPTIONS = arrayOf("Buy 10","Buy 5","Buy 1","Value") //Like holy what the fuck j*gex private val ITEMS = arrayOf( null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null, - null,POLAR_CAMO_TOP, null,null, POLAR_CAMO_LEGS,null,null,null,null, - null,WOOD_CAMO_TOP, null,null, WOOD_CAMO_LEGS,null,null,null, - null,JUNGLE_CAMO_TOP,null,null, JUNGLE_CAMO_LEGS,null,null,null, - null,DESERT_CAMO_TOP,null,null, DESERT_CAMO_LEGS,null,null,null,null, + null, POLAR_CAMO_TOP, null,null, POLAR_CAMO_LEGS,null,null,null,null, + null, WOOD_CAMO_TOP, null,null, WOOD_CAMO_LEGS,null,null,null, + null, JUNGLE_CAMO_TOP,null,null, JUNGLE_CAMO_LEGS,null,null,null, + null, DESERT_CAMO_TOP,null,null, DESERT_CAMO_LEGS,null,null,null,null, null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null, null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null, null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null, - LARUPIA_HAT,null,null,LARUPIA_TOP,null,null,LARUPIA_LEGS,null,null,null,null, - GRAAHK_HEADDRESS,null,null,GRAAHK_TOP,null,null,GRAAHK_LEGS,null,null,null,null,null, - KYATT_HAT,null,null,KYATT_TOP,null,null,KYATT_LEGS, null,null,null, + LARUPIA_HAT,null,null, LARUPIA_TOP,null,null, LARUPIA_LEGS,null,null,null,null, + GRAAHK_HEADDRESS,null,null, GRAAHK_TOP,null,null, GRAAHK_LEGS,null,null,null,null,null, + KYATT_HAT,null,null, KYATT_TOP,null,null, KYATT_LEGS, null,null,null, null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null, null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null, null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null, @@ -97,8 +98,8 @@ class FurClothingInterface : ComponentPlugin(){ val checkedFurs = arrayListOf() for(CLOTHING in FUR_CLOTHING.values()){ if(checkedFurs.contains(CLOTHING.textContent)) continue - if(ContentAPI.inInventory(player, CLOTHING.requiredFur.id, CLOTHING.requiredFur.amount)){ - ContentAPI.setInterfaceText(player, colorize("%G${CLOTHING.textContent}"), FUR_CLOTHING_COMPONENT_ID, CLOTHING.textChildID) + if(inInventory(player, CLOTHING.requiredFur.id, CLOTHING.requiredFur.amount)){ + setInterfaceText(player, colorize("%G${CLOTHING.textContent}"), FUR_CLOTHING_COMPONENT_ID, CLOTHING.textChildID) checkedFurs.add(CLOTHING.textContent) } } @@ -144,7 +145,7 @@ class FurClothingInterface : ComponentPlugin(){ } private fun value(player: Player, clothing: FUR_CLOTHING){ - ContentAPI.sendMessage(player,"${clothing.product.name} requires ${clothing.requiredFur.amount} ${clothing.requiredFur.name.toLowerCase()} and costs ${clothing.price} coins.") + sendMessage(player,"${clothing.product.name} requires ${clothing.requiredFur.amount} ${clothing.requiredFur.name.toLowerCase()} and costs ${clothing.price} coins.") } private fun buy(player: Player, clothing: FUR_CLOTHING, amount: Int){ @@ -152,19 +153,24 @@ class FurClothingInterface : ComponentPlugin(){ val amtFurRequired = clothing.requiredFur.amount * amount val requiredFur = Item(clothing.requiredFur.id,amtFurRequired) - if(!ContentAPI.inInventory(player, requiredFur.id, requiredFur.amount)){ - ContentAPI.sendDialogue(player, "You don't have enough fur for that.") + if(!inInventory(player, requiredFur.id, requiredFur.amount)){ + sendDialogue(player, "You don't have enough fur for that.") return } - if(!ContentAPI.inInventory(player, coins.id, coins.amount)){ - ContentAPI.sendDialogue(player,"You can't afford that.") + if(!inInventory(player, coins.id, coins.amount)){ + sendDialogue(player,"You can't afford that.") return } - ContentAPI.removeItem(player, requiredFur, api.Container.INVENTORY) - ContentAPI.removeItem(player, coins, api.Container.INVENTORY) - ContentAPI.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 { @@ -184,16 +190,16 @@ class FurClothingInterface : ComponentPlugin(){ DESERT_DEVIL_TOP(Item(10123,2),20, DESERT_CAMO_TOP,21,"Desert devil"), DESERT_DEVIL_BOT(Item(10123,2),20, DESERT_CAMO_LEGS, 21, "Desert devil"), LARUPIA_HEAD(Item(10095),500, LARUPIA_HAT,15,"Larupia"), - LARUPIA_CHEST(Item(10095),500, LARUPIA_TOP,15,"Larupia"), - LARUPIA_BOT(Item(10095),500, LARUPIA_LEGS,15,"Larupia"), + LARUPIA_CHEST(Item(10095),100, LARUPIA_TOP,15,"Larupia"), + LARUPIA_BOT(Item(10095),100, LARUPIA_LEGS,15,"Larupia"), GRAAHK_HEAD(Item(10099),750, GRAAHK_HEADDRESS,16,"Graahk"), - GRAAHK_CHEST(Item(10099),750, GRAAHK_TOP,16,"Graahk"), - GRAAHK_BOT(Item(10099),750, GRAAHK_LEGS,16,"Graahk"), - KYATT_HEAD(Item(10039),1000, KYATT_HAT,17,"Kyatt"), - KYATT_CHEST(Item(10039),1000, KYATT_TOP,17,"Kyatt"), - KYATT_BOT(Item(10039),1000, 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") + GRAAHK_CHEST(Item(10099),150, GRAAHK_TOP,16,"Graahk"), + GRAAHK_BOT(Item(10099),150, GRAAHK_LEGS,16,"Graahk"), + 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,2),600, GLOVES_OF_SILENCE,22,"Dark kebbit"), + SPOT_CAPE(Item(10125,2),400, SPOTTED_CAPE,23,"Spotted kebbit"), + DASH_CAPE(Item(10127,2),800, SPOTTIER_CAPE,24,"Dashing kebbit") } } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/GenericItemSelect.kt b/Server/src/main/content/global/handlers/iface/GenericItemSelect.kt new file mode 100644 index 000000000..beb2a297e --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/GenericItemSelect.kt @@ -0,0 +1,67 @@ +package content.global.handlers.iface + +import core.api.* +import core.tools.* +import core.game.interaction.* +import core.game.system.task.Pulse +import core.game.node.entity.player.Player + +class GenericItemSelect : InterfaceListener { + val GENERIC_ITEM_SELECT_IFACE = 12 + + override fun defineInterfaceListeners() { + onOpen(GENERIC_ITEM_SELECT_IFACE) {player, _ -> + player.pulseManager.run(object : Pulse() { + override fun pulse() : Boolean { + return false + } + override fun stop() { + super.stop() + player.interfaceManager.closeSingleTab() + } + }) + return@onOpen true + } + + on(GENERIC_ITEM_SELECT_IFACE){player, _, opcode, buttonID, slot, _ -> + processResponse(player, opcode, slot) + return@on true + } + + onClose(GENERIC_ITEM_SELECT_IFACE) {player, _ -> + removeAttribute(player, "itemselect-callback") + removeAttribute(player, "itemselect-keepalive") + return@onClose true + } + } + + private fun processResponse (player: Player, opcode: Int, slot: Int) { + val callback = getAttribute<((Int, Int) -> Unit)?>(player, "itemselect-callback", null) + if (callback == null) { + log (this::class.java, Log.WARN, "${player.name} is trying to use an item select prompt with no callback!") + return + } + + val optionIndex = when (opcode) { + 155 -> 0 + 196 -> 1 + 124 -> 2 + 199 -> 3 + 234 -> 4 + 9 -> 10 + else -> -1 + } + if (optionIndex == -1) { + log (this::class.java, Log.WARN, "${player.name} is clicking a right-click index that we don't know the opcode for yet, lol. Here's the opcode: $opcode") + return + } + + callback.invoke(slot, optionIndex) + + if (!getAttribute(player, "itemselect-keepalive", false)) { + removeAttribute (player, "itemselect-callback") + removeAttribute (player, "itemselect-keepalive") + player.interfaceManager.closeSingleTab() + } + } +} diff --git a/Server/src/main/content/global/handlers/iface/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/kotlin/rs09/game/interaction/inter/HairDresserInterface.kt b/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/interaction/inter/HairDresserInterface.kt rename to Server/src/main/content/global/handlers/iface/HairDresserInterface.kt index edd15a528..d9058006e 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/HairDresserInterface.kt +++ b/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt @@ -1,10 +1,10 @@ -package rs09.game.interaction.inter +package content.global.handlers.iface -import api.ContentAPI +import core.api.* import core.game.component.Component import core.game.component.ComponentDefinition import core.game.component.ComponentPlugin -import core.game.content.dialogue.FacialExpression +import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable @@ -90,7 +90,7 @@ private const val M_COMB_OVER = 253 //Array of male hairstyles in the same order as the interface -val MALE_HAIR_STYLES = intArrayOf(M_BALD,M_DREAD,M_LONG, M_MEDIUM, M_MONK, M_SHORT, M_CLOSE_CROP, M_WILD_SPIKE, M_SPIKE, M_MOHAWK, M_WIND_BRAID, M_QUIFF, M_SAMURAI, M_PRINCE, M_CURTAINS, M_LONG_CURTAINS, M_SIDE_PART_SPIKE, M_TOP_KNOT, M_PONYTAIL_SPIKEY, M_LONG_SWEPT_FRINGE, M_EVANSTYLE, M_DRAGON, M_WARRIOR_CURTAINS, M_COMB_OVER) +val MALE_HAIR_STYLES = intArrayOf(M_BALD, M_DREAD, M_LONG, M_MEDIUM, M_MONK, M_SHORT, M_CLOSE_CROP, M_WILD_SPIKE, M_SPIKE, M_MOHAWK, M_WIND_BRAID, M_QUIFF, M_SAMURAI, M_PRINCE, M_CURTAINS, M_LONG_CURTAINS, M_SIDE_PART_SPIKE, M_TOP_KNOT, M_PONYTAIL_SPIKEY, M_LONG_SWEPT_FRINGE, M_EVANSTYLE, M_DRAGON, M_WARRIOR_CURTAINS, M_COMB_OVER) //Male Facial Hairs @@ -142,9 +142,9 @@ class HairDresserInterface : ComponentPlugin(){ HAIRDRESSER_MALE_COMPONENT_ID -> 61 else -> 0 } - ContentAPI.sendPlayerOnInterface(player, usedInterface, player_model_child) - ContentAPI.sendPlayerOnInterface(player, usedInterface, player_head_child) - ContentAPI.sendAnimationOnInterface(player, FacialExpression.HAPPY.animationId, usedInterface, player_head_child) + sendPlayerOnInterface(player, usedInterface, player_model_child) + sendPlayerOnInterface(player, 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 -> pay(player) + 196,274,68,100 -> pay(player) else -> when(component?.id){ 592 -> { //Female if(femaleColorButtonRange.contains(button)){ diff --git a/Server/src/main/java/core/game/interaction/inter/JewelleryInterface.java b/Server/src/main/content/global/handlers/iface/JewelleryInterface.java similarity index 91% rename from Server/src/main/java/core/game/interaction/inter/JewelleryInterface.java rename to Server/src/main/content/global/handlers/iface/JewelleryInterface.java index 781e54e9c..11eb23415 100644 --- a/Server/src/main/java/core/game/interaction/inter/JewelleryInterface.java +++ b/Server/src/main/content/global/handlers/iface/JewelleryInterface.java @@ -1,15 +1,16 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; + +import content.global.skill.slayer.SlayerManager; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.crafting.jewellery.JewelleryCrafting; -import core.game.node.entity.skill.crafting.jewellery.JewelleryCrafting.JewelleryItem; +import content.global.skill.crafting.jewellery.JewelleryCrafting; +import content.global.skill.crafting.jewellery.JewelleryCrafting.JewelleryItem; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; import core.game.node.item.Item; import core.plugin.Plugin; import core.plugin.Initializable; @@ -180,13 +181,13 @@ public final class JewelleryInterface extends ComponentPlugin { break; case 199: final JewelleryItem d = data; - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { JewelleryCrafting.make(player, d, (int) value); return Unit.INSTANCE; }); return true; } - if(!player.getSlayer().flags.isRingUnlocked() && data.equals(JewelleryItem.SLAYER_RING)){ + if(!SlayerManager.getInstance(player).flags.isRingUnlocked() && data.equals(JewelleryItem.SLAYER_RING)){ player.sendMessages("You don't know how to make this. Talk to any Slayer master in order to learn the", "ability that creates Slayer rings."); return true; } diff --git a/Server/src/main/content/global/handlers/iface/LeatherCraftInterface.java b/Server/src/main/content/global/handlers/iface/LeatherCraftInterface.java new file mode 100644 index 000000000..e4b700a1a --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/LeatherCraftInterface.java @@ -0,0 +1,56 @@ +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.armour.LeatherCrafting; +import content.global.skill.crafting.armour.SoftCraftPulse; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.plugin.Initializable; +import core.plugin.Plugin; +import kotlin.Unit; + +/** + * Represents the leather crafting interface. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class LeatherCraftInterface extends ComponentPlugin { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ComponentDefinition.put(154, this); + return this; + } + + @Override + public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { + int amount = 0; + final LeatherCrafting.SoftLeather soft = LeatherCrafting.SoftLeather.forButton(button); + if (soft == null) { + return true; + } + switch (opcode) { + case 155: + amount = 1; + break; + case 196: + amount = 5; + break; + case 124: + amount = player.getInventory().getAmount(new Item(LeatherCrafting.LEATHER)); + break; + case 199: + sendInputDialogue(player, true, "Enter the amount:", (value) -> { + submitIndividualPulse(player, new SoftCraftPulse(player, new Item(LeatherCrafting.LEATHER), soft, (int) value)); + return Unit.INSTANCE; + }); + return true; + } + player.getPulseManager().run(new SoftCraftPulse(player, null, soft, amount)); + return true; + } +} diff --git a/Server/src/main/java/core/game/interaction/inter/LeatherCraftPlugin.java b/Server/src/main/content/global/handlers/iface/LeatherCraftPlugin.java similarity index 87% rename from Server/src/main/java/core/game/interaction/inter/LeatherCraftPlugin.java rename to Server/src/main/content/global/handlers/iface/LeatherCraftPlugin.java index ae1cd2e7c..e0b297824 100644 --- a/Server/src/main/java/core/game/interaction/inter/LeatherCraftPlugin.java +++ b/Server/src/main/content/global/handlers/iface/LeatherCraftPlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; import core.plugin.Initializable; -import core.game.node.entity.skill.crafting.armour.LeatherCrafting; -import core.game.node.entity.skill.crafting.armour.LeatherCrafting.SoftLeather; +import content.global.skill.crafting.armour.LeatherCrafting; +import content.global.skill.crafting.armour.LeatherCrafting.SoftLeather; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.plugin.Plugin; 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/MainGameInterface.kt b/Server/src/main/content/global/handlers/iface/MainGameInterface.kt new file mode 100644 index 000000000..e7f4edb05 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/MainGameInterface.kt @@ -0,0 +1,87 @@ +package content.global.handlers.iface + +import core.api.* +import core.game.component.CloseEvent +import core.game.component.Component +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 org.rs09.consts.Components +import core.game.interaction.InterfaceListener +import core.game.world.GameWorld.settings + +class MainGameInterface : InterfaceListener { + val TOPLEVEL = Components.TOPLEVEL_548 + val TOPLEVEL_FS = Components.TOPLEVEL_FULLSCREEN_746 + val RUN_BUTTON = Components.TOPSTAT_RUN_750 + val FILTER_BUTTONS = Components.FILTERBUTTONS_751 + val REPORT_ABUSE = Components.SNAPSHOT_MAIN_553 + + override fun defineInterfaceListeners() { + on(FILTER_BUTTONS){player, _, _, buttonID, _, _ -> + if(buttonID == 27) + openReport(player) + return@on true + } + + on(RUN_BUTTON){player, _, _, _, _, _ -> + player.settings.toggleRun() + return@on true + } + + on(TOPLEVEL_FS){player, _, _, buttonID, _, _ -> + if (buttonID == 110) + configureWorldMap(player) + return@on true + } + + on(TOPLEVEL){player, _, _, buttonID, _, _ -> + when (buttonID) { + 38 -> { + if (player.getExtension(WeaponInterface::class.java) === WeaponInterfaces.STAFF) { + val c = Component(WeaponInterfaces.STAFF.interfaceId) + player.interfaceManager.openTab(0, c) + val inter = player.getExtension(WeaponInterface::class.java) + inter.updateInterface() + } + } + 40 -> player.questRepository.syncronizeTab(player) + 41 -> player.inventory.refresh() + 66 -> configureWorldMap(player) + } + return@on true + } + } + + fun openReport(player: Player) { + player.interfaceManager.open(Component(REPORT_ABUSE)).closeEvent = + CloseEvent { player1: Player, c: Component? -> + player1.packetDispatch.sendRunScript(80, "") + player1.packetDispatch.sendRunScript(137, "") + true + } + player.packetDispatch.sendRunScript(508, "") + if (player.details.rights !== Rights.REGULAR_PLAYER) { + for (i in 0..17) { + player.packetDispatch.sendInterfaceConfig(553, i, false) + } + } + } + + private fun configureWorldMap(player: Player) { + if (player.inCombat()) { + sendMessage(player, "It wouldn't be very wise opening the world map during combat.") + return + } + if (player.locks.isInteractionLocked || player.locks.isMovementLocked) { + sendMessage(player, "You can't do this right now.") + return + } + player.interfaceManager.close() + player.interfaceManager.openWindowsPane(Component(755)) + val posHash = player.location.z shl 28 or (player.location.x shl 14) or player.location.y + player.packetDispatch.sendScriptConfigs(622, posHash, "", 0) + player.packetDispatch.sendScriptConfigs(674, posHash, "", 0) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/MakeOverInterface.kt b/Server/src/main/content/global/handlers/iface/MakeOverInterface.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/interaction/inter/MakeOverInterface.kt rename to Server/src/main/content/global/handlers/iface/MakeOverInterface.kt index 92eeb1dd6..58aaea101 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/MakeOverInterface.kt +++ b/Server/src/main/content/global/handlers/iface/MakeOverInterface.kt @@ -1,9 +1,9 @@ -package rs09.game.interaction.inter +package content.global.handlers.iface import core.game.component.Component import core.game.component.ComponentDefinition import core.game.component.ComponentPlugin -import core.game.content.dialogue.FacialExpression +import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.game.node.entity.player.link.appearance.Gender import core.game.node.item.Item 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/NPCContactInterface.kt b/Server/src/main/content/global/handlers/iface/NPCContactInterface.kt new file mode 100644 index 000000000..ebe4a6d54 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/NPCContactInterface.kt @@ -0,0 +1,91 @@ +package content.global.handlers.iface + +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import core.tools.RandomFunction +import org.rs09.consts.NPCs +import core.game.dialogue.DialogueFile +import core.game.interaction.InterfaceListener + +class NPCContactInterface : InterfaceListener { + val contactNPCs = arrayOf(NPCs.HONEST_JIMMY_4362, NPCs.BERT_3108, NPCs.ADVISOR_GHRIM_1375, NPCs.TURAEL_8273, NPCs.LANTHUS_1526, NPCs.SUMONA_7780, NPCs.MAZCHNA_8274, NPCs.DURADEL_8275, NPCs.VANNAKA_1597, NPCs.DARK_MAGE_2262, NPCs.CHAELDAR_1598, NPCs.CYRISUS_432, NPCs.LARRY_5424) + val DialogueFiles = arrayOf( + /*TODO("Honest Jimmy"), + TODO("Bert"), + TODO("ADVISOR GHRIM"), + TODO("Turael"), + TODO("LANTHUS"), + TODO("Sumona"), + TODO("Mazchna"), + TODO("Duradel"), + TODO("Vannaka"), + TODO("Murphy"), + TODO("Chaeldar"), + TODO("Cyrisus"), + TODO("Larry")*/ + ) + val INTER = 429 + override fun defineInterfaceListeners() { + + //Remove a bunch of the buttons/heads so that people don't + //waste runes on spells that aren't implemented + //TODO: Re-enable NPCs as their respective content gets added + onOpen(INTER){player, component -> + //Honest Jimmy: Trouble Brewing + player.packetDispatch.sendInterfaceConfig(429,10,true) + player.packetDispatch.sendInterfaceConfig(429,38,true) + + //Bert the Sandman: Hand in the Sand quest + player.packetDispatch.sendInterfaceConfig(429,11,true) + player.packetDispatch.sendInterfaceConfig(429,39,true) + + //Advisor Ghrim: Kingdoms of Miscellania + player.packetDispatch.sendInterfaceConfig(429,12,true) + player.packetDispatch.sendInterfaceConfig(429,40,true) + + //Lanthus: Castle Wars + player.packetDispatch.sendInterfaceConfig(429,17,true) + player.packetDispatch.sendInterfaceConfig(429,43,true) + + //Sumona: Completion of Smoking Kills + player.packetDispatch.sendInterfaceConfig(429,27,true) + player.packetDispatch.sendInterfaceConfig(429,42,true) + + // Replace Murphy's icon with the Abyss's Dark Mage + player.packetDispatch.sendNpcOnInterface(NPCs.DARK_MAGE_2262, 429, 30) + player.packetDispatch.sendString("Dark mage", 429, 47) + + return@onOpen true + } + + on(INTER){player, _, _, buttonID, _, _ -> + var index = when(buttonID){ + 10,38 -> 0 + 11,39 -> 1 + 12,40 -> 2 + 13,41 -> 3 + 17,43 -> 4 + 27,42 -> 5 + 18,44 -> 6 + 23,45 -> 7 + 28,46 -> 8 + 30,47 -> 9 + 29,48 -> 10 + 32,33 -> 11 + 34,49 -> 12 + else -> -1 + } + if(index == -1) index = RandomFunction.random(contactNPCs.size) + val dialogueFile = DialogueFiles.getOrNull(index) + player.getAttribute<() -> Unit>("contact-caller")?.invoke() + player.interfaceManager.close() + if(dialogueFile != null){ + player.dialogueInterpreter.open(dialogueFile, NPC(contactNPCs[index])) + } else { + player.dialogueInterpreter.open(contactNPCs[index], NPC(contactNPCs[index], Location(0, 0))) + } + return@on true + } + + } +} diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/PuppyInterfacePlugin.kt b/Server/src/main/content/global/handlers/iface/PuppyInterfacePlugin.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/interaction/inter/PuppyInterfacePlugin.kt rename to Server/src/main/content/global/handlers/iface/PuppyInterfacePlugin.kt index 80c98cd94..3df8d0c56 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/PuppyInterfacePlugin.kt +++ b/Server/src/main/content/global/handlers/iface/PuppyInterfacePlugin.kt @@ -1,4 +1,4 @@ -package rs09.game.interaction.inter +package content.global.handlers.iface import core.game.component.Component import core.game.component.ComponentDefinition diff --git a/Server/src/main/java/core/game/interaction/inter/RequestAssistInterface.java b/Server/src/main/content/global/handlers/iface/RequestAssistInterface.java similarity index 97% rename from Server/src/main/java/core/game/interaction/inter/RequestAssistInterface.java rename to Server/src/main/content/global/handlers/iface/RequestAssistInterface.java index 53059f691..952a0bf16 100644 --- a/Server/src/main/java/core/game/interaction/inter/RequestAssistInterface.java +++ b/Server/src/main/content/global/handlers/iface/RequestAssistInterface.java @@ -1,4 +1,4 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; import core.game.component.Component; import core.game.component.ComponentDefinition; diff --git a/Server/src/main/content/global/handlers/iface/RulesAndInfo.kt b/Server/src/main/content/global/handlers/iface/RulesAndInfo.kt new file mode 100644 index 000000000..420c70d71 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/RulesAndInfo.kt @@ -0,0 +1,67 @@ +package content.global.handlers.iface + +import core.api.* +import core.game.node.entity.player.Player +import core.tools.RandomFunction +import core.game.interaction.InterfaceListener + +object RulesAndInfo { + val RULES = arrayOf( + "1. Be respectful to your fellow players.", + " -No harassment, etc.", + " -Keep arguments private.", + "2. Do not exploit bugs.", + " -Zero tolerance. Can result in account deletion.", + " -If you discover a bug, report it on our Gitlab.", + "3. Do not discuss or advertise other servers.", + " -Discussion of the live jagex games is fine.", + " -Discussion of open source projects is fine.", + "4. No unfair advantages.", + " -This includes autoclickers, autohotkey, etc.", + " -Exception: 1-to-1 inputs, such as mousekeys.", + " -Includes training methods that bypass AFK limits." + ) + val SEPARATOR = " " + val INFO = arrayOf( + "Join the forums! forum.2009scape.org " + ) + + @JvmStatic + fun openFor(player: Player) + { + if(getAttribute(player, "rules:confirmed", false) || !getAttribute(player, "tutorial:complete", false)) + return + val pin = getAttribute(player, "rules:pin", RandomFunction.random(1000,9999)) + setAttribute(player, "/save:rules:pin", pin) + + var ln = setBaseRulesAndInfo(player) + + setInterfaceText(player, "If you agree to the above, type ::confirmrules $pin", 384, ln++) + setInterfaceText(player, "", 384, ln) + player.packetDispatch.sendInterfaceConfig(384, 17, true) + openInterface(player, 384) + player.lock() + } + + @JvmStatic + fun setBaseRulesAndInfo(player: Player): Int { + var ln = 1 + for(line in INFO) setInterfaceText(player, INFO[ln - 1], 384, ln++) + setInterfaceText(player, SEPARATOR, 384, ln++) + val newIndex = ln + for(line in RULES) setInterfaceText(player, RULES[ln - newIndex], 384, ln++) + setInterfaceText(player, "", 384, ln) + return ln + } +} + +class RulesListener : InterfaceListener +{ + override fun defineInterfaceListeners() { + onClose(384){player, _ -> + if(!getAttribute(player, "rules:confirmed", false)) + runTask(player, 1) { RulesAndInfo.openFor(player); sendDialogue(player, "Please read the rules.") } + return@onClose true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/inter/SawmillPlankInterface.java b/Server/src/main/content/global/handlers/iface/SawmillPlankInterface.java similarity index 96% rename from Server/src/main/java/core/game/interaction/inter/SawmillPlankInterface.java rename to Server/src/main/content/global/handlers/iface/SawmillPlankInterface.java index 688053d6d..00957533d 100644 --- a/Server/src/main/java/core/game/interaction/inter/SawmillPlankInterface.java +++ b/Server/src/main/content/global/handlers/iface/SawmillPlankInterface.java @@ -1,6 +1,6 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; @@ -66,7 +66,7 @@ public class SawmillPlankInterface extends ComponentPlugin { } if (amount == 69) { final Plank plankk = plank; - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { create(player, plankk, (int) value); return Unit.INSTANCE; }); 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/java/core/game/interaction/inter/ShoppingPlugin.java b/Server/src/main/content/global/handlers/iface/ShoppingPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/inter/ShoppingPlugin.java rename to Server/src/main/content/global/handlers/iface/ShoppingPlugin.java index 298c44972..175f20f33 100644 --- a/Server/src/main/java/core/game/interaction/inter/ShoppingPlugin.java +++ b/Server/src/main/content/global/handlers/iface/ShoppingPlugin.java @@ -1,6 +1,7 @@ +/* package core.game.interaction.inter; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; @@ -15,11 +16,13 @@ import core.plugin.Plugin; import kotlin.Unit; import kotlin.jvm.functions.Function1; +*/ /** * Represents the plugin used to handle the shopping interface. * @author 'Vexia * @version 1.0 - */ + *//* + @Initializable public final class ShoppingPlugin extends ComponentPlugin { @@ -48,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()); @@ -97,13 +100,15 @@ public final class ShoppingPlugin extends ComponentPlugin { return true; } - /** + */ +/** * Method used to value an item. * @param player the player. * @param viewer the viewer. * @param item the item. * @param sell the sell. - */ + *//* + private void value(final Player player, final ShopViewer viewer, final Item item, final boolean sell) { if (item == null) { return; @@ -111,11 +116,13 @@ public final class ShoppingPlugin extends ComponentPlugin { viewer.getShop().value(player, viewer, item, sell); } - /** + */ +/** * Gets the run script for selling an item. * @return the script. * @param slot the slot. - */ + *//* + private Function1 getRunScript(final ShopViewer viewer, final int slot, final int componentId) { return (value) -> { switch (componentId){ @@ -130,12 +137,15 @@ public final class ShoppingPlugin extends ComponentPlugin { }; } - /** + */ +/** * Gets the amount by the opcode. * @param opcode the opcode. * @return the amount. - */ + *//* + private int getAmount(int opcode) { return opcode == 196 ? 1 : opcode == 124 ? 5 : opcode == 199 ? 10 : -1; } } +*/ diff --git a/Server/src/main/java/core/game/interaction/inter/SmeltingInterface.java b/Server/src/main/content/global/handlers/iface/SmeltingInterface.java similarity index 84% rename from Server/src/main/java/core/game/interaction/inter/SmeltingInterface.java rename to Server/src/main/content/global/handlers/iface/SmeltingInterface.java index 609c12d75..4032ff524 100644 --- a/Server/src/main/java/core/game/interaction/inter/SmeltingInterface.java +++ b/Server/src/main/content/global/handlers/iface/SmeltingInterface.java @@ -1,14 +1,13 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.plugin.Initializable; -import core.game.node.entity.skill.smithing.smelting.Bar; -import core.game.node.entity.skill.smithing.smelting.SmeltingPulse; +import content.global.skill.smithing.smelting.Bar; +import content.global.skill.smithing.smelting.SmeltingPulse; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; import core.plugin.Plugin; import kotlin.Unit; @@ -32,8 +31,12 @@ public class SmeltingInterface extends ComponentPlugin { } if (barType.getAmount() == -1) { player.getInterfaceManager().closeChatbox(); - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { - ContentAPI.submitIndividualPulse(player, new SmeltingPulse(player, null, barType.getBar(), (int) value)); + sendInputDialogue(player, true, "Enter the amount:", (value) -> { + if (value instanceof String) { + submitIndividualPulse(player, new SmeltingPulse(player, null, barType.getBar(), Integer.parseInt((String) value))); + } else { + submitIndividualPulse(player, new SmeltingPulse(player, null, barType.getBar(), (int) value)); + } return Unit.INSTANCE; }); } else { diff --git a/Server/src/main/java/core/game/interaction/inter/SmithingInterface.java b/Server/src/main/content/global/handlers/iface/SmithingInterface.java similarity index 77% rename from Server/src/main/java/core/game/interaction/inter/SmithingInterface.java rename to Server/src/main/content/global/handlers/iface/SmithingInterface.java index de7f8d5cc..3256736be 100644 --- a/Server/src/main/java/core/game/interaction/inter/SmithingInterface.java +++ b/Server/src/main/content/global/handlers/iface/SmithingInterface.java @@ -1,16 +1,15 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.plugin.Initializable; -import core.game.node.entity.skill.smithing.BarType; -import core.game.node.entity.skill.smithing.Bars; -import core.game.node.entity.skill.smithing.SmithingPulse; -import core.game.node.entity.skill.smithing.SmithingType; +import content.global.skill.smithing.BarType; +import content.global.skill.smithing.Bars; +import content.global.skill.smithing.SmithingPulse; +import content.global.skill.smithing.SmithingType; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; import core.game.node.item.Item; import core.plugin.Plugin; import kotlin.Unit; @@ -38,7 +37,7 @@ public class SmithingInterface extends ComponentPlugin { p.getGameAttributes().setAttribute("smith-bar", bar); p.getGameAttributes().setAttribute("smith-item", item); if (amount == -1) { - ContentAPI.sendInputDialogue(p, true, "Enter the amount:", (value) -> { + sendInputDialogue(p, true, "Enter the amount:", (value) -> { p.getPulseManager().run(new SmithingPulse(p, new Item((int) p.getGameAttributes().getAttribute("smith-item"), (int) value), (Bars) p.getGameAttributes().getAttribute("smith-bar"), (int) value)); return Unit.INSTANCE; }); diff --git a/Server/src/main/content/global/handlers/iface/SpinningInterface.java b/Server/src/main/content/global/handlers/iface/SpinningInterface.java new file mode 100644 index 000000000..8ee642e73 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/SpinningInterface.java @@ -0,0 +1,63 @@ +package content.global.handlers.iface; + +import static core.api.ContentAPIKt.*; +import core.cache.def.impl.ItemDefinition; +import core.game.component.Component; +import core.game.component.ComponentDefinition; +import core.game.component.ComponentPlugin; +import content.global.skill.crafting.spinning.SpinningItem; +import content.global.skill.crafting.spinning.SpinningPulse; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.plugin.Initializable; +import core.plugin.Plugin; +import kotlin.Unit; + +@Initializable +public class SpinningInterface extends ComponentPlugin { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ComponentDefinition.put(459, this); + return this; + } + + @Override + public boolean handle(final Player p, Component component, int opcode, int button, int slot, int itemId) { + final SpinningItem spin = SpinningItem.forId(button); + if (spin == null) { + return true; + } + if (!p.getInventory().contains(spin.getNeed(), 1)) { + p.getPacketDispatch().sendMessage("You need "+ ItemDefinition.forId(spin.getNeed()).getName().toLowerCase() + " to make this."); + return true; + } + int amt = -1; + switch (opcode) { + case 155: + amt = 1; + break; + case 196: + amt = 5; + break; + case 124: + amt = p.getInventory().getAmount(new Item(spin.getNeed())); + break; + case 199: + sendInputDialogue(p, true, "Enter the amount:", (value) -> { + if (value instanceof String) { + submitIndividualPulse(p, new SpinningPulse(p, new Item(spin.getNeed(), 1), Integer.parseInt((String) value), spin)); + } else { + submitIndividualPulse(p, new SpinningPulse(p, new Item(spin.getNeed(), 1), (int) value, spin)); + } + return Unit.INSTANCE; + }); + break; + } + if (opcode == 199) { + return true; + } + p.getPulseManager().run(new SpinningPulse(p, new Item(spin.getNeed(), 1), amt, spin)); + 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/kotlin/rs09/game/interaction/inter/TeleotherInterface.kt b/Server/src/main/content/global/handlers/iface/TeleotherInterface.kt similarity index 78% rename from Server/src/main/kotlin/rs09/game/interaction/inter/TeleotherInterface.kt rename to Server/src/main/content/global/handlers/iface/TeleotherInterface.kt index 9c89627cc..051bc041b 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/TeleotherInterface.kt +++ b/Server/src/main/content/global/handlers/iface/TeleotherInterface.kt @@ -1,15 +1,15 @@ -package rs09.game.interaction.inter +package content.global.handlers.iface import core.game.node.entity.player.link.TeleportManager.TeleportType import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import org.rs09.consts.Components -import rs09.game.interaction.InterfaceListener +import core.game.interaction.InterfaceListener -class TeleotherInterface : InterfaceListener() { +class TeleotherInterface : InterfaceListener { val IFACE = Components.TELEPORT_OTHER_326 - override fun defineListeners() { + override fun defineInterfaceListeners() { on(IFACE){player, _, _, button, _, _ -> if(button == 5){ player.lock(2) diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/ThessaliaInterface.kt b/Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/interaction/inter/ThessaliaInterface.kt rename to Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt index 7baa2cbe3..feb6cc5c0 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/ThessaliaInterface.kt +++ b/Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt @@ -1,5 +1,6 @@ -package rs09.game.interaction.inter +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)) @@ -170,29 +172,29 @@ class ThessaliaInterface : ComponentPlugin(){ override fun handle(player: Player?, component: Component?, opcode: Int, button: Int, slot: Int, itemId: Int): Boolean { player ?: return false when(button){ - 181,180 -> pay(player) + 181,180,297 -> pay(player) else -> when(component?.id){ THESSALIA_MALE_COMPONENT -> { when(button){ - 182 -> player.setAttribute("thes-type",colorType.TORSO) - 183 -> player.setAttribute("thes-type",colorType.ARMS) - 184 -> player.setAttribute("thes-type",colorType.LEGS) + 182 -> player.setAttribute("thes-type", colorType.TORSO) + 183 -> player.setAttribute("thes-type", colorType.ARMS) + 184 -> player.setAttribute("thes-type", colorType.LEGS) } if(maleArmsButtonRange.contains(button)) updateArms(player,button,true) if(maleTorsoButtonRange.contains(button)) updateTop(player,button,true) if(maleLegsButtonRange.contains(button)) updateLegs(player,button,true) - if(maleColorButtonRange.contains(button)) updateColor(player,button,true,player.getAttribute("thes-type",colorType.TORSO)) + if(maleColorButtonRange.contains(button)) updateColor(player,button,true,player.getAttribute("thes-type", colorType.TORSO)) } THESSALIA_FEMALE_COMPONENT -> { when(button){ - 183 -> player.setAttribute("thes-type",colorType.TORSO) - 184 -> player.setAttribute("thes-type",colorType.ARMS) - 185 -> player.setAttribute("thes-type",colorType.LEGS) + 183 -> player.setAttribute("thes-type", colorType.TORSO) + 184 -> player.setAttribute("thes-type", colorType.ARMS) + 185 -> player.setAttribute("thes-type", colorType.LEGS) } if(femaleArmsButtonRange.contains(button)) updateArms(player,button,false) if(femaleTorsoButtonRange.contains(button)) updateTop(player,button,false) if(femaleLegsButtonRange.contains(button)) updateLegs(player,button,false) - if(femaleColorButtonRange.contains(button)) updateColor(player,button,false,player.getAttribute("thes-type",colorType.TORSO)) + if(femaleColorButtonRange.contains(button)) updateColor(player,button,false,player.getAttribute("thes-type", colorType.TORSO)) } } } diff --git a/Server/src/main/java/core/game/interaction/inter/TradeInterfacePlugin.java b/Server/src/main/content/global/handlers/iface/TradeInterfacePlugin.java similarity index 93% rename from Server/src/main/java/core/game/interaction/inter/TradeInterfacePlugin.java rename to Server/src/main/content/global/handlers/iface/TradeInterfacePlugin.java index 88fb909be..1d3568715 100644 --- a/Server/src/main/java/core/game/interaction/inter/TradeInterfacePlugin.java +++ b/Server/src/main/content/global/handlers/iface/TradeInterfacePlugin.java @@ -1,6 +1,6 @@ -package core.game.interaction.inter; +package content.global.handlers.iface; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; @@ -68,7 +68,7 @@ public final class TradeInterfacePlugin extends ComponentPlugin { module.getContainer().withdraw(slot, module.getContainer().getAmount(module.getContainer().get(slot))); break; case 234: - ContentAPI.sendInputDialogue(player, false, "Enter the amount:", (value) -> { + sendInputDialogue(player, false, "Enter the amount:", (value) -> { String s = value.toString(); s = s.replace("k","000"); s = s.replace("K","000"); @@ -102,7 +102,7 @@ public final class TradeInterfacePlugin extends ComponentPlugin { module.getContainer().offer(slot, player.getInventory().getAmount(player.getInventory().get(slot))); break; case 234: - ContentAPI.sendInputDialogue(player, false, "Enter the amount:", (value) -> { + sendInputDialogue(player, false, "Enter the amount:", (value) -> { String s = value.toString(); s = s.replace("k","000"); s = s.replace("K","000"); 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/BankChargeInterface.kt b/Server/src/main/content/global/handlers/iface/bank/BankChargeInterface.kt new file mode 100644 index 000000000..cdf582f43 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/bank/BankChargeInterface.kt @@ -0,0 +1,98 @@ +package content.global.handlers.iface.bank + +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 org.rs09.consts.NPCs +import core.game.interaction.InterfaceListener + +/** + * Handles bank charge interface for Eniola at ZMI altar. + * @author vddCore + */ +class BankChargeInterface : InterfaceListener { + companion object { + private const val BUTTON_AIR_RUNE = 28 + private const val BUTTON_MIND_RUNE = 29 + private const val BUTTON_WATER_RUNE = 30 + private const val BUTTON_EARTH_RUNE = 31 + private const val BUTTON_FIRE_RUNE = 32 + private const val BUTTON_BODY_RUNE = 33 + private const val BUTTON_COSMIC_RUNE = 34 + private const val BUTTON_CHAOS_RUNE = 35 + private const val BUTTON_ASTRAL_RUNE = 36 + private const val BUTTON_LAW_RUNE = 37 + private const val BUTTON_DEATH_RUNE = 38 + private const val BUTTON_BLOOD_RUNE = 39 + private const val BUTTON_NATURE_RUNE = 40 + private const val BUTTON_SOUL_RUNE = 41 + + private val RUNE_BUTTONS = intArrayOf( + BUTTON_AIR_RUNE, BUTTON_MIND_RUNE, BUTTON_WATER_RUNE, BUTTON_EARTH_RUNE, + BUTTON_FIRE_RUNE, BUTTON_BODY_RUNE, BUTTON_COSMIC_RUNE, BUTTON_CHAOS_RUNE, + BUTTON_ASTRAL_RUNE, BUTTON_LAW_RUNE, BUTTON_DEATH_RUNE, BUTTON_BLOOD_RUNE, + BUTTON_NATURE_RUNE, BUTTON_SOUL_RUNE + ) + + private val BUTTON_TO_RUNE = hashMapOf( + BUTTON_AIR_RUNE to Items.AIR_RUNE_556, + BUTTON_MIND_RUNE to Items.MIND_RUNE_558, + BUTTON_WATER_RUNE to Items.WATER_RUNE_555, + BUTTON_EARTH_RUNE to Items.EARTH_RUNE_557, + BUTTON_FIRE_RUNE to Items.FIRE_RUNE_554, + BUTTON_BODY_RUNE to Items.BODY_RUNE_559, + BUTTON_COSMIC_RUNE to Items.COSMIC_RUNE_564, + BUTTON_CHAOS_RUNE to Items.CHAOS_RUNE_562, + BUTTON_ASTRAL_RUNE to Items.ASTRAL_RUNE_9075, + BUTTON_LAW_RUNE to Items.LAW_RUNE_563, + BUTTON_DEATH_RUNE to Items.DEATH_RUNE_560, + BUTTON_BLOOD_RUNE to Items.BLOOD_RUNE_565, + BUTTON_NATURE_RUNE to Items.NATURE_RUNE_561, + BUTTON_SOUL_RUNE to Items.SOUL_RUNE_566 + ) + } + + private fun handleButtonClick( + player: Player, + component: Component, + opcode: Int, + buttonID: Int, + slot: Int, + itemID: Int + ): Boolean { + if (buttonID !in RUNE_BUTTONS) return true + + val runeItemId = BUTTON_TO_RUNE[buttonID] + + runeItemId?.let { + if (amountInInventory(player, it) < 20) { + sendNPCDialogue( + player, + NPCs.ENIOLA_6362, + "I'm afraid you don't have the necessary runes with you at this time, so " + + "I can't allow you to access your account. Please bring 20 runes of one type " + + "and you'll be able to open your bank.", + core.game.dialogue.FacialExpression.NEUTRAL + ) + + return true + } + + if (removeItem(player, Item(it, 20))) { + when (getAttribute(player, "zmi:bankaction", "")) { + "open" -> openBankAccount(player) + "collect" -> openGrandExchangeCollectionBox(player) + } + } + } + + return true + } + + override fun defineInterfaceListeners() { + on(Components.BANK_CHARGE_ZMI_619, ::handleButtonClick) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/bank/BankDepositBoxInterface.kt b/Server/src/main/content/global/handlers/iface/bank/BankDepositBoxInterface.kt new file mode 100644 index 000000000..e190a8dde --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/bank/BankDepositBoxInterface.kt @@ -0,0 +1,76 @@ +package content.global.handlers.iface.bank + +import core.api.animate +import core.api.dumpBeastOfBurden +import core.api.runWorldTask +import core.api.sendMessage +import core.game.component.Component +import core.game.node.entity.player.Player +import org.rs09.consts.Animations +import org.rs09.consts.Components +import core.game.interaction.InterfaceListener + +/** + * Allows the user to interact with the + * Bank Deposit Box interface + * + * @author vddCore + */ +class BankDepositBoxInterface : InterfaceListener { + companion object { + private const val BUTTON_DEPOSIT_BOB = 13 + + private const val MENU_ELEMENT = 11 + private const val OP_AMOUNT_ONE = 155 + private const val OP_AMOUNT_FIVE = 196 + private const val OP_AMOUNT_TEN = 124 + private const val OP_AMOUNT_ALL = 199 + private const val OP_AMOUNT_X = 234 + private const val OP_EXAMINE = 168 + } + + private fun handleDepositBoxMenu(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { + val item = player.inventory.get(slot) + ?: return true + + if (opcode == OP_EXAMINE) { + sendMessage(player, item.definition.examine) + return true + } + + runWorldTask { + when (opcode) { + OP_AMOUNT_ONE -> player.bank.addItem(slot, 1) + OP_AMOUNT_FIVE -> player.bank.addItem(slot, 5) + OP_AMOUNT_TEN -> player.bank.addItem(slot, 10) + OP_AMOUNT_X -> BankUtils.transferX( + player, + slot, + false, + // Needs to have a callback here because the depositing moment is independent from the world task. + player.bank::refreshDepositBoxInterface + ) + OP_AMOUNT_ALL -> player.bank.addItem( + slot, + player.inventory.getAmount(item) + ) + else -> player.debug("Unknown deposit box menu opcode $opcode") + } + + player.bank.refreshDepositBoxInterface() + animate(player, Animations.HUMAN_BANK_DEPOSIT_BOX_834) + } + + return true + } + + override fun defineInterfaceListeners() { + on(Components.BANK_DEPOSIT_BOX_11, ::handleDepositBoxMenu) + on( + Components.BANK_DEPOSIT_BOX_11, + BUTTON_DEPOSIT_BOB + ) { player, _, _, _, _, _ -> + dumpBeastOfBurden(player); true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/bank/BankInterface.kt b/Server/src/main/content/global/handlers/iface/bank/BankInterface.kt new file mode 100644 index 000000000..d6d0bd1cc --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/bank/BankInterface.kt @@ -0,0 +1,198 @@ +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 + +/** + * Allows the user to interact with the Bank Interface. + * + * @author vddCore + */ +class BankInterface : InterfaceListener { + companion object { + private const val MAIN_BUTTON_CLOSE = 10 + private const val MAIN_BUTTON_INSERT_MODE = 14 + private const val MAIN_BUTTON_NOTE_MODE = 16 + private const val MAIN_BUTTON_BOB_DEPOSIT = 18 + private const val MAIN_BUTTON_SEARCH_BANK = 20 + private const val MAIN_BUTTON_HELP = 23 + + private const val MENU_ELEMENT = 73 + private const val OP_AMOUNT_ONE = 155 + private const val OP_AMOUNT_FIVE = 196 + private const val OP_AMOUNT_TEN = 124 + private const val OP_AMOUNT_LAST_X = 199 + private const val OP_AMOUNT_X = 234 + private const val OP_AMOUNT_ALL = 168 + private const val OP_AMOUNT_ALL_BUT_ONE = 166 + private const val OP_EXAMINE = 9 + + private const val BANK_TAB_1 = 41 + private const val BANK_TAB_2 = 39 + private const val BANK_TAB_3 = 37 + private const val BANK_TAB_4 = 35 + private const val BANK_TAB_5 = 33 + private const val BANK_TAB_6 = 31 + private const val BANK_TAB_7 = 29 + private const val BANK_TAB_8 = 27 + private const val BANK_TAB_9 = 25 + + private val BANK_TABS = intArrayOf( + BANK_TAB_1, BANK_TAB_2, BANK_TAB_3, + BANK_TAB_4, BANK_TAB_5, BANK_TAB_6, + BANK_TAB_7, BANK_TAB_8, BANK_TAB_9 + ) + + 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 fun onBankInterfaceOpen(player: Player, component: Component): Boolean { + val settings = IfaceSettingsBuilder() + .enableAllOptions() + .enableSlotSwitch() + .setInterfaceEventsDepth(2) + .build() + + player.packetDispatch.sendIfaceSettings( + settings, + 73, + Components.BANK_V2_MAIN_762, + 0, + ServerConstants.BANK_SIZE + ) + + resetSearch(player) + return false + } + + private fun handleTabInteraction(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { + resetSearch(player) + val clickedTabIndex = -((buttonID - 41) / 2) + + when (opcode) { + OP_SET_TAB -> { + if (player.bank.tabIndex != clickedTabIndex) { + player.bank.tabIndex = clickedTabIndex + } + } + + OP_COLLAPSE_TAB -> { + player.bank.collapseTab(clickedTabIndex) + } + } + + return true + } + + 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 -> 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 + val bank = player.bank + if (isCoinOverrideNeeded(id, bank)) { + examineText = "" + bank.getAmount(id) + " x " + item.definition.name + "." + } + sendMessage(player, examineText) + } + else -> player.debug("Unknown bank menu opcode $opcode") + } + + return true + } + + 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) + OP_AMOUNT_FIVE -> player.bank.addItem(slot, 5) + OP_AMOUNT_TEN -> player.bank.addItem(slot, 10) + OP_AMOUNT_LAST_X -> player.bank.addItem(slot, player.bank.lastAmountX) + OP_AMOUNT_X -> BankUtils.transferX(player, slot, false) + OP_AMOUNT_ALL -> player.bank.addItem(slot, player.inventory.getAmount(item)) + OP_EXAMINE -> { + var examineText = item.definition.examine + val id = item.definition.id + val inventory = player.inventory + if (isCoinOverrideNeeded(id, inventory)) { + examineText = "" + inventory.getAmount(id) + " x " + item.definition.name + "." + } + sendMessage(player, examineText) + } + else -> player.debug("Unknown inventory menu opcode $opcode") + } + + return true + } + + override fun defineInterfaceListeners() { + onOpen(Components.BANK_V2_MAIN_762, ::onBankInterfaceOpen) + + on(Components.BANK_V2_MAIN_762) { player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + MAIN_BUTTON_HELP -> openDialogue(player, BankHelpDialogue()) + MAIN_BUTTON_BOB_DEPOSIT -> openDialogue(player, BankDepositDialogue()) + MAIN_BUTTON_INSERT_MODE -> player.bank.isInsertItems = !player.bank.isInsertItems + MAIN_BUTTON_NOTE_MODE -> player.bank.isNoteItems = !player.bank.isNoteItems + MAIN_BUTTON_SEARCH_BANK -> setAttribute(player, "search", true) + MENU_ELEMENT -> handleBankMenu(player, component, opcode, buttonID, slot, itemID) + in BANK_TABS -> handleTabInteraction(player, component, opcode, buttonID, slot, itemID) + } + + return@on true + } + + on(Components.BANK_V2_HELP_767) { player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + MAIN_BUTTON_CLOSE -> openBankAccount(player) + } + + return@on true + } + + on(Components.BANK_V2_SIDE_763, ::handleInventoryMenu) + } + + private fun isCoinOverrideNeeded(id: Int, container: Container): Boolean { + val amount = container.getAmount(id) + + // Only COINS_995 are obtainable and bankable by player + if (id == Items.COINS_995 && amount >= THRESHOLD_TO_DISPLAY_EXACT_QUANTITY_ON_EXAMINE) { + return true + } + + 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/java/core/game/interaction/inter/BankPinInterface.java b/Server/src/main/content/global/handlers/iface/bank/BankPinInterface.java similarity index 97% rename from Server/src/main/java/core/game/interaction/inter/BankPinInterface.java rename to Server/src/main/content/global/handlers/iface/bank/BankPinInterface.java index 8b44885c0..7c70b88a3 100644 --- a/Server/src/main/java/core/game/interaction/inter/BankPinInterface.java +++ b/Server/src/main/content/global/handlers/iface/bank/BankPinInterface.java @@ -1,4 +1,4 @@ -package core.game.interaction.inter; +package content.global.handlers.iface.bank; import core.game.component.Component; import core.game.component.ComponentDefinition; diff --git a/Server/src/main/content/global/handlers/iface/bank/BankUtils.kt b/Server/src/main/content/global/handlers/iface/bank/BankUtils.kt new file mode 100644 index 000000000..2a056fee2 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/bank/BankUtils.kt @@ -0,0 +1,22 @@ +package content.global.handlers.iface.bank + +import core.api.InputType +import core.api.sendInputDialogue +import core.game.node.entity.player.Player + +object BankUtils { + fun transferX(player: Player, slot: Int, withdraw: Boolean, after: (() -> Unit)? = null) { + sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:") { value -> + val number = Integer.parseInt(value.toString()) + + if (withdraw) { + player.bank.takeItem(slot, number) + } else { + player.bank.addItem(slot, number) + } + + player.bank.updateLastAmountX(number) + after?.let { it() } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/ge/ExchangeItemSets.kt b/Server/src/main/content/global/handlers/iface/ge/ExchangeItemSets.kt new file mode 100644 index 000000000..8547bf3c4 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/ge/ExchangeItemSets.kt @@ -0,0 +1,79 @@ +package content.global.handlers.iface.ge + +import core.api.getAttribute +import core.api.openInterface +import core.api.setAttribute +import core.game.component.Component +import core.game.container.Container +import core.game.container.ContainerEvent +import core.game.container.ContainerListener +import core.game.container.access.InterfaceContainer +import core.game.ge.GEItemSet +import core.game.node.entity.player.Player +import org.rs09.consts.Components +import core.game.interaction.InterfaceListener + +class ExchangeItemSets : InterfaceListener { + override fun defineInterfaceListeners() { + onClose(Components.EXCHANGE_ITEMSETS_645){player, _ -> + val listener = getAttribute(player, "ge-listener", null) + player.inventory.listeners.remove(listener) + player.interfaceManager.closeSingleTab() + player.removeAttribute("container-key") + player.removeAttribute("ge-listener") + return@onClose true + } + } + + companion object { + @JvmStatic + fun openFor(player: Player) + { + openInterface(player, Components.EXCHANGE_ITEMSETS_645) + player.interfaceManager.openSingleTab(Component(Components.EXCHANGE_SETS_SIDE_644)) + val listener: InventoryListener + setAttribute(player, "ge-listener", InventoryListener(player).also { listener = it }) + player.inventory.listeners.add(listener) + } + } + + private class InventoryListener(val player: Player) : ContainerListener { + init { + createContainers(player) + } + + override fun update(c: Container?, event: ContainerEvent?) + { + createContainers(player) + } + + override fun refresh(c: Container?) { + createContainers(player) + } + + private fun createContainers(player: Player) + { + setAttribute(player, "container-key", + InterfaceContainer.generateItems( + player, + player.inventory.toArray(), + arrayOf("Examine", "Exchange", "Components"), + Components.EXCHANGE_SETS_SIDE_644, + 0, + 7, + 4 + ) + ) + + InterfaceContainer.generateItems( + player, + GEItemSet.getItemArray(), + arrayOf("Examine", "Exchange", "Components"), + Components.EXCHANGE_ITEMSETS_645, + 16, + 15, + 10 + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java b/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java new file mode 100644 index 000000000..9b615b305 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java @@ -0,0 +1,230 @@ +package content.global.handlers.iface.ge; + +import core.cache.def.impl.CS2Mapping; +import core.cache.def.impl.ItemDefinition; +import core.game.component.Component; +import core.game.component.ComponentDefinition; +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.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.ge.GrandExchangeOffer; +import core.game.ge.GrandExchangeRecords; +import core.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; + +/** + * Handles the Grand Exchange interface options. + * @author Emperor + * @version 1.0 + */ +@Initializable +public class GrandExchangeInterface extends ComponentPlugin { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + //ComponentDefinition.put(105, this); // Main interface + //ComponentDefinition.put(107, this); // Selling tab + ComponentDefinition.put(109, this); // Collection interface + ComponentDefinition.put(389, this); // Search interface + ComponentDefinition.put(644, this); // Item sets inventory interface + ComponentDefinition.put(645, this); // Item sets interface + ComponentDefinition.put(642, this); // Guide Prices interface. + return this; + } + + @Override + public boolean handle(final Player player, final Component component, final int opcode, final int button, final int slot, final int itemId) { + GameWorld.getPulser().submit(new Pulse(1, player) { + @Override + public boolean pulse() { + switch (component.getId()) { + case 644: + case 645: + handleItemSet(player, component, opcode, button, slot, itemId); + return true; + case 389: + handleSearchInterface(player, opcode, button, slot, itemId); + return true; + case 109: + handleCollectionBox(player, opcode, button, slot, itemId); + return true; + case 642: + handleGuidePrice(player, opcode, button, slot, itemId); + return true; + } + return true; + } + }); + return true; + } + + /** + * Handles the search interface options. + * @param player The player. + * @param opcode The packet opcode. + * @param button The button id. + * @param slot The slot. + * @param itemId The item id. + * @return {@code true} if the option got handled. + */ + public boolean handleSearchInterface(final Player player, int opcode, int button, int slot, int itemId) { + switch (button) { + case 10: + player.getInterfaceManager().closeChatbox(); + return true; + } + return false; + } + + /** + * Handles the selling tab interface options. + * @param player The player. + * @param opcode The packet opcode. + * @param button The button id. + * @param slot The slot. + * @param itemId The item id. + * @return {@code true} if the option got handled. + */ + public boolean handleCollectionBox(final Player player, int opcode, int button, int slot, int itemId) { + int index = -1; + switch (button) { + case 18: + case 23: + case 28: + index = (button - 18) >> 2; + break; + case 36: + case 44: + case 52: + index = 3 + ((button - 36) >> 3); + break; + } + GrandExchangeOffer offer; + GrandExchangeRecords records = GrandExchangeRecords.getInstance(player); + if (index > -1 && (offer = records.getOffer(records.getOfferRecords()[index])) != null) { + StockMarket.withdraw(player, offer, slot >> 1, opcode); + } + return true; + } + + /** + * Handles the item set. + * @param player The player. + * @param opcode The opcode. + * @param button The button. + * @param slot The slot. + * @param itemId The item id. + */ + private void handleItemSet(Player player, Component component, int opcode, int button, int slot, int itemId) { + if (button != 16 && button != 0) { + return; + } + boolean inventory = component.getId() == 644; + if (slot < 0 || slot >= (inventory ? 28 : GEItemSet.values().length)) { + return; + } + + 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; + } + switch (opcode) { + case 124: + player.getPacketDispatch().sendMessage(item.getDefinition().getExamine()); + break; + case 196: + if (inventory) { + if (player.getInventory().freeSlots() < set.getComponents().length - 1) { + player.getPacketDispatch().sendMessage("You don't have enough inventory space for the component parts."); + return; + } + if (!player.getInventory().remove(item, false)) { + return; + } + for (int id : set.getComponents()) { + player.getInventory().add(new Item(id, 1)); + } + player.getInventory().refresh(); + player.getPacketDispatch().sendMessage("You successfully traded your set for its component items!"); + } else { + if (!player.getInventory().containItems(set.getComponents())) { + player.getPacketDispatch().sendMessage("You don't have the parts that make up this set."); + break; + } + for (int id : set.getComponents()) { + player.getInventory().remove(new Item(id, 1), false); + } + player.getInventory().add(item); + player.getInventory().refresh(); + player.getPacketDispatch().sendMessage("You successfully traded your item components for a set!"); + } + 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: + CS2Mapping mapping = CS2Mapping.forId(1089); + if (mapping != null && set != null) { + player.getPacketDispatch().sendMessage((String) mapping.getMap().get(set.getItemId())); + } + break; + } + } + + /*** + * Method used to handle the guide price opcode. + * @param player the player. + * @param opcode the opcode. + * @param buttonId the buttonId. + * @param slot the slot. + * @param itemId the itemId. + */ + private void handleGuidePrice(final Player player, final int opcode, final int buttonId, final int slot, final int itemId) { + switch (opcode) { + case 155: + GEGuidePrice.GuideType type = player.getAttribute("guide-price", null); + if (type == null) { + return; + } + int subtract = 0; + if (buttonId >= 15 && buttonId <= 23) { + subtract = 15; + } + if (buttonId >= 43 && buttonId <= 57) { + subtract = 43; + } + if (buttonId >= 89 && buttonId <= 103) { + subtract = 89; + } + if (buttonId >= 135 && buttonId <= 144) { + subtract = 135; + } + if (buttonId >= 167 && buttonId <= 182) { + subtract = 167; + } + player.getPacketDispatch().sendMessage(ItemDefinition.forId(type.getItems()[buttonId - subtract].getItem()).getExamine()); + break; + } + } +} diff --git a/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt b/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt new file mode 100644 index 000000000..eba0a2435 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt @@ -0,0 +1,412 @@ +package content.global.handlers.iface.ge + +import core.api.* +import core.game.component.Component +import core.game.ge.OfferState +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.net.packet.PacketRepository +import core.net.packet.context.ConfigContext +import core.net.packet.context.ContainerContext +import core.net.packet.out.Config +import core.net.packet.out.ContainerPacket +import org.rs09.consts.Components +import core.game.ge.GrandExchange +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) + * @author Ceikry + */ +class StockMarket : InterfaceListener { + override fun defineInterfaceListeners() { + onOpen(Components.STOCKMARKET_105){player, _ -> + player.packetDispatch.sendInterfaceConfig(105, 193, true) + player.packetDispatch.sendIfaceSettings(6, 211, 105, -1, -1) + player.packetDispatch.sendIfaceSettings(6, 209, 105, -1, -1) + setVarp(player, 1112, -1) + return@onOpen true + } + + onClose(Components.STOCKMARKET_105){player, _ -> + player.packetDispatch.sendRunScript(571, "") + player.interfaceManager.closeChatbox() + player.interfaceManager.closeSingleTab() + } + + onOpen(Components.OBJDIALOG_389){player, _ -> + player.packetDispatch.sendRunScript(570, "s", "Grand Exchange Item Search") + return@onOpen true + } + + onClose(Components.OBJDIALOG_389){player, _ -> + player.packetDispatch.sendRunScript(5781, "") + return@onClose true + } + + on(Components.STOCKSIDE_107){ player, _, op, button, slot, _ -> + if (button != 18 || slot < 0 || slot > 27) { + return@on false + } + val item = player.inventory[slot] ?: return@on false + when (op) { + 196 -> player.packetDispatch.sendMessage(item.definition.examine) + 155 -> { + val offer = getAttribute(player, "ge-temp", GrandExchangeOffer()) + val index = getAttribute(player, "ge-index", -1) + if(item.id == 995) + { + sendMessage(player, "You can't sell money!") + return@on true + } + var id = item.id + if(!item.definition.isUnnoted) + id = item.noteChange + if(!PriceIndex.canTrade(id)) + { + sendMessage(player, "This item can't be sold on the Grand Exchange.") + return@on true + } + offer.itemID = id + offer.sell = true + offer.player = player + offer.offeredValue = GrandExchange.getRecommendedPrice(id) + offer.amount = item.amount + offer.index = index + updateVarbits(player, offer, index, true) + setAttribute(player, "ge-temp", offer) + player.packetDispatch.sendString(GrandExchange.getOfferStats(id, true), 105, 142) + } + } + return@on true + } + + on(Components.STOCKMARKET_105){player, _, op, button, _, _ -> + val tempOffer = getAttribute(player, "ge-temp", GrandExchangeOffer()) + var openedIndex = getAttribute(player, "ge-index", -1) + var openedOffer = GrandExchangeRecords.getInstance(player).getOffer(openedIndex) + + when(button) + { + 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, op) + 190 -> confirmOffer(player, tempOffer, openedIndex).also { return@on true } + 194 -> player.interfaceManager.openChatbox(Components.OBJDIALOG_389) + 203 -> abortOffer(player, openedOffer) + 18,34,50,69,88,107 -> { + openedIndex = (button - 18) shr 4 + openedOffer = GrandExchangeRecords.getInstance(player).getOffer(openedIndex) + if(op == 205) abortOffer(player, openedOffer) + else updateVarbits(player, openedOffer, openedIndex) + if(openedOffer != null) { + player.packetDispatch.sendString(GrandExchange.getOfferStats(openedOffer.itemID, openedOffer.sell), 105, 142) + } + } + 30,46,62,81,100,119 -> { + openedIndex = (button - 30) shr 4 + openedOffer = GrandExchangeRecords.getInstance(player).getOffer(openedIndex) + updateVarbits(player, openedOffer, openedIndex) + player.interfaceManager.openChatbox(Components.OBJDIALOG_389) + } + 31,47,63,82,101,120 -> { + openedIndex = (button - 31) shr 4 + openedOffer = GrandExchangeRecords.getInstance(player).getOffer(openedIndex) + updateVarbits(player, openedOffer, openedIndex, true) + player.interfaceManager.openSingleTab(Component(Components.STOCKSIDE_107)).open(player) + player.packetDispatch.sendRunScript( + 149, "IviiiIsssss", "", "", "", "Examine", "Offer", + -1, 0, 7, 4, 93, 7012370 + ) + val settings = IfaceSettingsBuilder().enableOptions(0, 1).build() + player.packetDispatch.sendIfaceSettings(settings, 18, 107, 0, 27) + } + 157 -> updateOfferAmount(player, tempOffer, tempOffer.amount - 1) + 159 -> updateOfferAmount(player, tempOffer, tempOffer.amount + 1) + 162 -> updateOfferAmount(player, tempOffer, if(tempOffer.sell) 1 else tempOffer.amount + 1) + 164 -> updateOfferAmount(player, tempOffer, if(tempOffer.sell) 10 else tempOffer.amount + 10) + 166 -> updateOfferAmount(player, tempOffer, if(tempOffer.sell) 100 else tempOffer.amount + 100) + 168 -> { + val amt = + if(tempOffer.sell) + getInventoryAmount(player, tempOffer.itemID) + else + tempOffer.amount + 1000 + + updateOfferAmount(player, tempOffer, amt) + } + 170 -> sendInputDialogue(player, false, "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") + updateOfferAmount(player, tempOffer, s.toInt()) + setAttribute(player, "ge-temp", tempOffer) + } + 180 -> updateOfferValue(player, tempOffer, GrandExchange.getRecommendedPrice(tempOffer.itemID)) + 177 -> updateOfferValue(player, tempOffer, (GrandExchange.getRecommendedPrice(tempOffer.itemID) * 0.95).toInt()) + 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, InputType.AMOUNT, "Enter the amount:"){value -> + if (player.interfaceManager.chatbox.id == 389) { + player.interfaceManager.openChatbox(Components.OBJDIALOG_389) + } + var s = value.toString() + updateOfferValue(player, tempOffer, s.toInt()) + setAttribute(player, "ge-temp", tempOffer) + } + 195 -> closeInterface(player) + 127 -> { + player.interfaceManager.closeSingleTab() + player.interfaceManager.closeChatbox() + } + } + + setAttribute(player, "ge-index", openedIndex) + setAttribute(player, "ge-temp", tempOffer) + return@on true + } + } + + fun updateOfferValue(player: Player, offer: GrandExchangeOffer, newAmt: Int) + { + offer.offeredValue = newAmt + setVarp(player, 1111, newAmt) + } + + fun updateOfferAmount(player: Player, offer: GrandExchangeOffer, newAmt: Int) + { + offer.amount = newAmt + setVarp(player, 1110, newAmt) + } + + fun abortOffer(player: Player, offer: GrandExchangeOffer?) + { + if(offer == null) + { + 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") + sendMessage(player, "have already been completed.") + + if(!offer.isActive) + { + 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 + if(offer.sell) + offer.addWithdrawItem(offer.itemID, offer.amountLeft) + else + offer.addWithdrawItem(995, offer.amountLeft * offer.offeredValue) + offer.update() + offer.visualize(player) + } + + enum class OfferConfirmResult { + Success, + ZeroCoins, + TooManyCoins, + NotEnoughItemsOrCoins, + ItemRemovalFailure, + OfferPlacementError + } + + fun confirmOffer(player: Player, offer: GrandExchangeOffer, index: Int) : OfferConfirmResult + { + if(offer.offeredValue < 1) + { + playAudio(player, Sounds.GE_TRADE_ERROR_4039) + sendMessage(player, "You can't make an offer for 0 coins.") + return OfferConfirmResult.ZeroCoins + } + if(offer.amount > Int.MAX_VALUE / offer.offeredValue) + { + playAudio(player, Sounds.GE_TRADE_ERROR_4039) + sendMessage(player, "You can't ${if(offer.sell) "sell" else "buy"} this much!") + return OfferConfirmResult.TooManyCoins + } + offer.index = index + if(offer.sell) + { + val maxAmt = getInventoryAmount(player, offer.itemID) + if(offer.amount > maxAmt) + { + 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 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 + { + 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 + { + val total = offer.amount * offer.offeredValue + if(!inInventory(player, 995, total)) + { + playAudio(player, Sounds.GE_TRADE_ERROR_4039) + sendMessage(player, "You do not have enough coins to cover the offer.") + return OfferConfirmResult.NotEnoughItemsOrCoins + } + if(GrandExchange.dispatch(player, offer) && removeItem(player, Item(995, total))) + { + player.removeAttribute("ge-temp") + } + } + playAudio(player, Sounds.GE_PLACE_ITEM_4043) + offer.visualize(player) + toMainInterface(player) + return OfferConfirmResult.Success + } + + fun getInventoryAmount(player: Player, itemId: Int): Int { + val item = Item(itemId) + var amount = player.inventory.getAmount(item) + if (item.definition.noteId > -1) { + amount += player.inventory.getAmount(Item(item.definition.noteId)) + } + return amount + } + + companion object { + @JvmStatic + fun openFor(player: Player) + { + if(player.ironmanManager.checkRestriction()) + return + if(!player.bankPinManager.isUnlocked) + { + player.bankPinManager.openType(4) + return + } + + openInterface(player, Components.STOCKMARKET_105) + } + + @JvmStatic + fun updateVarbits(player: Player, offer: GrandExchangeOffer?, index: Int, sale: Boolean? = null) + { + val isSale = sale ?: offer?.sell ?: false + var lowPrice = 0 + var highPrice = 0 + val recommendedPrice = GrandExchange.getRecommendedPrice(offer?.itemID ?: 0) + if (PriceIndex.canTrade(offer?.itemID ?: 0)) { + lowPrice = (recommendedPrice * 0.95).toInt() + highPrice = (recommendedPrice * 1.05).toInt() + } + 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, + ContainerContext(player, -1, -1757, 523 + offer.index, offer.withdraw, false) + ) + } + } + + @JvmStatic + fun withdraw(player: Player, offer: GrandExchangeOffer, index: Int, op: Int) + { + val item = offer.withdraw[index] + if(item == null) + { + log(this::class.java, Log.WARN, "Offer withdraw[$index] is null!") + 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 + } + } + } + + offer.withdraw[index] = null + if (!offer.isActive && offer.withdraw[0] == null && offer.withdraw[1] == null) + { + offer.offerState = OfferState.REMOVED + if(offer.completedAmount > 0) + { + val newHistory = arrayOfNulls(5) + newHistory[0] = offer + System.arraycopy(GrandExchangeRecords.getInstance(player).history, 0, newHistory, 1, 4) + GrandExchangeRecords.getInstance(player).history = newHistory + } + GrandExchangeRecords.getInstance(player).offerRecords[offer.index] = null + toMainInterface(player) + } + offer.update() + offer.visualize(player) + } + + /** + * Returns to the main interface. + */ + fun toMainInterface(player: Player) { + PacketRepository.send(Config::class.java, ConfigContext(player, 1112, -1)) + PacketRepository.send(Config::class.java, ConfigContext(player, 1112, -1)) + player.interfaceManager.closeChatbox() + player.interfaceManager.closeSingleTab() + player.setAttribute("ge-index", -1) + } + + } +} 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/java/core/game/interaction/inter/CombatTabInterface.java b/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java similarity index 95% rename from Server/src/main/java/core/game/interaction/inter/CombatTabInterface.java rename to Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java index 11e01665c..0ca513102 100644 --- a/Server/src/main/java/core/game/interaction/inter/CombatTabInterface.java +++ b/Server/src/main/content/global/handlers/iface/tabs/CombatTabInterface.java @@ -1,15 +1,15 @@ -package core.game.interaction.inter; +package content.global.handlers.iface.tabs; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; 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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.plugin.Initializable; import core.plugin.Plugin; 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/java/core/game/interaction/item/BirdNestPlugin.java b/Server/src/main/content/global/handlers/item/BirdNestPlugin.java similarity index 89% rename from Server/src/main/java/core/game/interaction/item/BirdNestPlugin.java rename to Server/src/main/content/global/handlers/item/BirdNestPlugin.java index 19b2a3cf9..5499ba690 100644 --- a/Server/src/main/java/core/game/interaction/item/BirdNestPlugin.java +++ b/Server/src/main/content/global/handlers/item/BirdNestPlugin.java @@ -1,6 +1,6 @@ -package core.game.interaction.item; +package content.global.handlers.item; -import core.game.content.global.BirdNest; +import content.data.tables.BirdNest; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -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/BraceletOfClayPlugin.kt b/Server/src/main/content/global/handlers/item/BraceletOfClayPlugin.kt new file mode 100644 index 000000000..26fd23008 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/BraceletOfClayPlugin.kt @@ -0,0 +1,24 @@ +package content.global.handlers.item + +import core.api.* +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Handles the bracelet of clay operate option. + * @author Ceikry + */ +class BraceletOfClayPlugin : InteractionListener { + + override fun defineListeners() { + + on(Items.BRACELET_OF_CLAY_11074, IntType.ITEM, "operate"){ player, node -> + var charge = getCharge(node) + if (charge > 28) setCharge(node, 28).also { charge = 28 } + sendMessage(player, "You have $charge uses left.") + return@on true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/CadavaPotionListener.kt b/Server/src/main/content/global/handlers/item/CadavaPotionListener.kt new file mode 100644 index 000000000..35c3fa472 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/CadavaPotionListener.kt @@ -0,0 +1,26 @@ +package rs09.game.interaction.`object` + +import core.api.sendItemDialogue +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * @author bushtail + */ + +class CadavaPotionListener : InteractionListener { + + var POTION = Items.CADAVA_POTION_756 + + override fun defineListeners() { + on(POTION, IntType.ITEM, "drink") { player, node -> + sendItemDialogue(player, POTION, "You dare not drink.") + return@on true + } + on(POTION, IntType.ITEM, "look-at") { player, node -> + sendItemDialogue(player, POTION, "This looks very colourful.") + return@on true + } + } +} \ No newline at end of file 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/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/java/core/game/interaction/item/ConsumableOptionPlugin.java b/Server/src/main/content/global/handlers/item/ConsumableOptionPlugin.java similarity index 86% rename from Server/src/main/java/core/game/interaction/item/ConsumableOptionPlugin.java rename to Server/src/main/content/global/handlers/item/ConsumableOptionPlugin.java index 577874124..3b74cd867 100644 --- a/Server/src/main/java/core/game/interaction/item/ConsumableOptionPlugin.java +++ b/Server/src/main/content/global/handlers/item/ConsumableOptionPlugin.java @@ -1,7 +1,9 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; -import core.game.content.consumable.*; +import core.game.consumable.Consumable; +import content.data.consumables.Consumables; +import core.game.consumable.Potion; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -15,7 +17,6 @@ import core.plugin.Plugin; * @author Emperor * @version 1.0 */ -@Initializable public final class ConsumableOptionPlugin extends OptionHandler { @Override @@ -33,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"); @@ -59,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/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/DrinkBlamishOilListener.kt b/Server/src/main/content/global/handlers/item/DrinkBlamishOilListener.kt new file mode 100644 index 000000000..0e6c423b1 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/DrinkBlamishOilListener.kt @@ -0,0 +1,16 @@ +package content.global.handlers.item + +import core.api.* +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class DrinkBlamishOilListener : InteractionListener { + + override fun defineListeners() { + on(Items.BLAMISH_OIL_1582, IntType.ITEM, "drink"){ player, _ -> + sendPlayerDialogue(player, "You know... I'd really rather not.") + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/EctophialListener.kt b/Server/src/main/content/global/handlers/item/EctophialListener.kt 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/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/EnchantedGemListener.kt b/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt new file mode 100644 index 000000000..e376d2855 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt @@ -0,0 +1,62 @@ +package content.global.handlers.item + +import content.global.skill.slayer.SlayerUtils +import core.api.* +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.tools.END_DIALOGUE + +class EnchantedGemListener : InteractionListener { + override fun defineListeners() { + on(Items.ENCHANTED_GEM_4155, IntType.ITEM, "activate") { player, _ -> + openDialogue(player, EnchantedGemDialogue()) + return@on true + } + } +} + +class EnchantedGemDialogue() : DialogueFile() { + var firstRun = true + override fun handle(interfaceId: Int, buttonId: Int) { + npc = getSlayerMaster(player!!) + when(stage) { + 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello there ${player!!.username}, what can I help you with?").also { stage++ } + 1 -> showTopics( + Topic(core.game.dialogue.FacialExpression.ASKING, "How am I doing so far?", 100), + Topic(core.game.dialogue.FacialExpression.HALF_ASKING, "Who are you?", 200), + Topic(core.game.dialogue.FacialExpression.HALF_ASKING, "Where are you?", 300), + Topic(core.game.dialogue.FacialExpression.FRIENDLY, "Got any tips for me?", 400), + IfTopic(core.game.dialogue.FacialExpression.FRIENDLY, "Nothing really.", END_DIALOGUE, firstRun), + IfTopic(core.game.dialogue.FacialExpression.HAPPY, "That's all thanks.", END_DIALOGUE, !firstRun) + ) + 100 -> { + firstRun = false + 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 { + 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 + } + } + 200 -> { + firstRun = false + npcl(core.game.dialogue.FacialExpression.HAPPY, "My name's ${getSlayerMaster(player!!).name}, I'm the Slayer Master best able to train you.").also { stage = 1 } + } + 300 -> { + firstRun = false + npcl(core.game.dialogue.FacialExpression.HAPPY, "You'll find me in ${getSlayerMasterLocation(player!!)}, I'll be here when you need a new task.").also { stage = 1 } + } + 400 -> { + firstRun = false + npc(core.game.dialogue.FacialExpression.FRIENDLY, *getSlayerTip(player!!)) + stage++ + } + 401 -> player(core.game.dialogue.FacialExpression.HAPPY, "Great, thanks!").also { stage = 1 } + } + } +} diff --git a/Server/src/main/content/global/handlers/item/EnchantedJewelleryListener.kt b/Server/src/main/content/global/handlers/item/EnchantedJewelleryListener.kt new file mode 100644 index 000000000..549b69051 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/EnchantedJewelleryListener.kt @@ -0,0 +1,68 @@ +package content.global.handlers.item + +import core.api.openDialogue +import core.api.sendMessage +import core.game.node.Node +import core.game.node.entity.impl.PulseType +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.dialogue.DialogueFile +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, Player Name + */ +class EnchantedJewelleryListener : InteractionListener { + + val ids = EnchantedJewellery.idMap.keys.toIntArray() + + override fun defineListeners() { + on(ids, IntType.ITEM, "operate") { player, node -> + handle(player, node, true) + return@on true + } + on(ids, IntType.ITEM, "rub") { player, node -> + handle(player, node, false) + return@on true + } + } + 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) { + sendMessage(player, "The ${jewellery.getJewelleryType(item)} has lost its charge.") + sendMessage(player, "It will need to be recharged before you can use it again.") + return + } + sendMessage(player, "You rub the ${jewellery.getJewelleryType(item)}...") + if (jewellery.options.isEmpty()) { + jewellery.use(player, item, 0, isEquipped) + return + } + openDialogue(player,EnchantedJewelleryDialogueFile(jewellery, item, true, isEquipped)) + } + } + + 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 teleport to?", *jewellery.options) + stage++ + } + 1 -> end().also { jewellery.use(player!!, item, buttonID - 1, isEquipped) } + } + } + } +} diff --git a/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt b/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt new file mode 100644 index 000000000..586967573 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt @@ -0,0 +1,102 @@ +package content.global.handlers.item + +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 +import core.game.world.map.Location +import org.json.simple.JSONObject +import org.rs09.consts.Items +import core.ServerStore +import core.ServerStore.Companion.getInt +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import content.global.skill.magic.modern.ModernListeners + +class ExplorersRingPlugin : InteractionListener { + + val RINGS = intArrayOf(Items.EXPLORERS_RING_1_13560, Items.EXPLORERS_RING_2_13561, Items.EXPLORERS_RING_3_13562) + val CABBAGE_PORT = Location.create(3051, 3291, 0) + + + override fun defineListeners() { + on(RINGS, IntType.ITEM, "run-replenish"){ player, node -> + val charges = getStoreFile().getInt(player.username.toLowerCase() + ":run") + if (charges >= getRingLevel(node.id)) { + 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) + playAudio(player, 5035) + + getStoreFile()[player.username.toLowerCase() + ":run"] = charges + 1 + + sendMessage(player,"You feel refreshed as the ring revitalises you and a charge is used up.") + visualize(player, 9988, 1733) + 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 + } + 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 + } + 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 + } + + on(RINGS, IntType.ITEM, "cabbage-port"){ player, node -> + teleport(player) + return@on true + } + + on(RINGS, IntType.ITEM, "operate", "rub"){ player, node -> + if(getRingLevel(node.id) < 3){ + sendMessage(player, "This item can not be operated.") + return@on true + } + + teleport(player) + return@on true + } + } + + fun teleport(player: Player){ + teleport(player, CABBAGE_PORT, TeleportType.CABBAGE) + } + + fun getRingLevel(id: Int): Int{ + return when(id){ + Items.EXPLORERS_RING_1_13560 -> 1 + Items.EXPLORERS_RING_2_13561 -> 2 + Items.EXPLORERS_RING_3_13562 -> 3 + else -> -1 + } + } + + fun getStoreFile(): JSONObject{ + return ServerStore.getArchive("daily-explorer-ring") + } +} diff --git a/Server/src/main/java/core/game/interaction/item/FaladorShieldPlugin.java b/Server/src/main/content/global/handlers/item/FaladorShieldPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/item/FaladorShieldPlugin.java rename to Server/src/main/content/global/handlers/item/FaladorShieldPlugin.java index 60fed09f0..4cebc31c7 100644 --- a/Server/src/main/java/core/game/interaction/item/FaladorShieldPlugin.java +++ b/Server/src/main/content/global/handlers/item/FaladorShieldPlugin.java @@ -1,14 +1,14 @@ -package core.game.interaction.item; +package content.global.handlers.item; import java.util.concurrent.TimeUnit; import core.cache.def.impl.ItemDefinition; import core.game.node.entity.Entity; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; -import core.game.content.consumable.effects.PrayerEffect; +import content.data.consumables.effects.PrayerEffect; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/interaction/item/FishbowlPlugin.java b/Server/src/main/content/global/handlers/item/FishbowlPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/item/FishbowlPlugin.java rename to Server/src/main/content/global/handlers/item/FishbowlPlugin.java index cba9a7206..09cbfb5b2 100644 --- a/Server/src/main/java/core/game/interaction/item/FishbowlPlugin.java +++ b/Server/src/main/content/global/handlers/item/FishbowlPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.Util; import core.cache.def.impl.ItemDefinition; @@ -15,11 +15,14 @@ import core.game.node.item.WeightedChanceItem; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +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 @@ -55,8 +58,8 @@ public class FishbowlPlugin extends OptionHandler { def.getHandlers().put("option:feed", this); def.getHandlers().put("option:drop", this); } - PluginManager.definePlugin(new FishbowlDialogue()); - PluginManager.definePlugin(new FeedPetFishHandler()); + ClassScanner.definePlugin(new FishbowlDialogue()); + ClassScanner.definePlugin(new FeedPetFishHandler()); new AquariumPlugin().newInstance(arg); return this; } @@ -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; @@ -220,7 +224,7 @@ public class FishbowlPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.forId(10091).getHandlers().put("option:fish-in", this); - PluginManager.definePlugin(new TinyNetHandler()); + ClassScanner.definePlugin(new TinyNetHandler()); return this; } @@ -250,7 +254,7 @@ public class FishbowlPlugin extends OptionHandler { Item fish = RandomFunction.rollWeightedChanceTable(fishChance); player.getInventory().add(fish); - String msg = "[ REPORT BUG ON DISCORD ]"; + String msg = "[ REPORT BUG ON GITLAB ]"; switch(fish.getId()) { case FISHBOWL_BLUE: msg = "Bluefish"; diff --git a/Server/src/main/java/core/game/interaction/item/GnomeCopterPlugin.java b/Server/src/main/content/global/handlers/item/GnomeCopterPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/item/GnomeCopterPlugin.java rename to Server/src/main/content/global/handlers/item/GnomeCopterPlugin.java index 3f30cd574..f3522b247 100644 --- a/Server/src/main/java/core/game/interaction/item/GnomeCopterPlugin.java +++ b/Server/src/main/content/global/handlers/item/GnomeCopterPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.container.impl.EquipmentContainer; diff --git a/Server/src/main/java/core/game/interaction/item/GnomecopterTicket.java b/Server/src/main/content/global/handlers/item/GnomecopterTicket.java similarity index 96% rename from Server/src/main/java/core/game/interaction/item/GnomecopterTicket.java rename to Server/src/main/content/global/handlers/item/GnomecopterTicket.java index 049b1989e..49075853f 100644 --- a/Server/src/main/java/core/game/interaction/item/GnomecopterTicket.java +++ b/Server/src/main/content/global/handlers/item/GnomecopterTicket.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; diff --git a/Server/src/main/content/global/handlers/item/GodBookListeners.kt b/Server/src/main/content/global/handlers/item/GodBookListeners.kt new file mode 100644 index 000000000..764190e72 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/GodBookListeners.kt @@ -0,0 +1,162 @@ +package content.global.handlers.item + +import core.api.* +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.update.flag.context.Animation +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 { + + val GB_SARADOMIN = Items.HOLY_BOOK_3840 + val GB_ZAMORAK = Items.UNHOLY_BOOK_3842 + val GB_GUTHIX = Items.BOOK_OF_BALANCE_3844 + + override fun defineListeners() { + on(GB_SARADOMIN, IntType.ITEM, "preach"){ player, _ -> + 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 { + SARA, + GUTHIX, + ZAM + } + + internal class HOLY_DIALOGUE(val book: BOOK) : DialogueFile(){ + + val WEDDINGS = arrayOf( + "In the name of Saradomin, protector of us all, I now join you in the eyes of Saradomin.", + "Light and dark, day and night, balance arises from contrast. I unify thee in the name of Guthix.", + "Two great warriors, joined by hand, to spread destruction across the land. In Zamorak's name, now two are one." + ) + + val LAST_RITES = arrayOf( + "Thy cause was false, thy skills did lack; See you in Lumbridge when you get back.", + "Thy death was not in vain, for it brought some balance to the world. May Guthix bring you rest.", + "The weak deserve to die, so the strong may flourish. This is the creed of Zamorak." + ) + + val BLESSINGS = arrayOf( + "Go in peace in the name of Saradomin; may his glory shine upon you like the sun.", + "May you walk the path, and never fall, for Guthix walks beside thee on thy journey. May Guthix bring you peace.", + "May your bloodthirst never be sated, and may all your battles be glorious. Zamorak bring you strength." + ) + + val PREACHINGS = arrayOf( + arrayOf( + "Protect your self, protect your friends. Mine is the glory that never ends.", + "The darkness in life may be avoided, by the light of wisdom shining.", + "Show love to your friends, and mercy to your enemies, and know that the wisdom of Saradomin will follow. ", + "A fight begun, when the cause is just, will prevail over all others.", + "The currency of goodness is honour; It retains its value through scarcity." + ), + arrayOf( + "All things must end, as all begin; Only Guthix knows the role thou must play.", + "In life, in death, in joy, in sorrow: May thine experience show thee balance.", + "Thou must do as thou must, no matter what. Thine actions bring balance to this world.", + "he river flows, the sun ignites, May you stand with Guthix in thy fights.", + "A journey of a single step, May take thee over a thousand miles." + ), + arrayOf( + "There is no opinion that cannot be proven true...by crushing those who choose to disagree with it.", + "Battles are not lost and won; They simply remove the weak from the equation.", + "Those who fight, then run away, shame Zamorak with their cowardice.", + "Battle is by those who choose to disagree with it.", + "Strike fast, strike hard, strike true: The strength of Zamorak will be with you." + ) + ) + + val PREACH_SARA_AMEN = "This is Saradomin's Wisdom." + val PREACH_ZAM_AMEN = "Zamorak give me strength!" + val PREACH_GUTH_AMEN = "May Guthix bring you balance." + + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> options("Weddings","Last Rites","Blessings","Preaching").also { stage++ } + 1 -> when(buttonID){ + 1 -> say(player!!, WEDDINGS[book.ordinal], book, false) + 2 -> say(player!!, LAST_RITES[book.ordinal], book, false) + 3 -> say(player!!, BLESSINGS[book.ordinal], book, false) + 4 -> say(player!!, PREACHINGS[book.ordinal].random(), book, true) + else -> {} + }.also { end() } + } + } + + private fun say(player: Player, message: String, book: BOOK, preach: Boolean){ + val animation = when(book){ + BOOK.SARA -> Animation(1335) + BOOK.ZAM -> Animation(1336) + BOOK.GUTHIX -> Animation(1337) + } + + val preachText = when(book){ + BOOK.SARA -> PREACH_SARA_AMEN + BOOK.ZAM -> PREACH_ZAM_AMEN + BOOK.GUTHIX -> PREACH_GUTH_AMEN + } + + val lastTick = animationDuration(animation) + + lock(player, 100) + animate(player, animation) + + submitIndividualPulse(player, object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> sendChat(player, message) + lastTick - 1 -> if(preach) sendChat(player, preachText) + lastTick -> unlock(player).also { return true } + } + return false + } + }) + } + } +} diff --git a/Server/src/main/content/global/handlers/item/GodBookPlugin.java b/Server/src/main/content/global/handlers/item/GodBookPlugin.java new file mode 100644 index 000000000..9f955de41 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/GodBookPlugin.java @@ -0,0 +1,124 @@ +package content.global.handlers.item; + +import core.game.dialogue.DialogueAction; +import content.data.GodBook; +import core.game.node.entity.skill.Skills; +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.item.GroundItem; +import core.game.node.item.Item; +import core.game.node.item.ItemPlugin; +import core.plugin.Plugin; +import core.plugin.Initializable; +import core.plugin.ClassScanner; + +/** + * Handles the god books. + * @author Vexia + */ +@Initializable +public class GodBookPlugin extends OptionHandler { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + for (GodBook book : GodBook.values()) { + book.getDamagedBook().getDefinition().getHandlers().put("option:check", this); + } + ClassScanner.definePlugins(new PageHandler(), new GodBookItem()); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + GodBook book = GodBook.forItem(node.asItem(), option.equalsIgnoreCase("check")); + if (book != null) { + switch (option) { + case "check": + String[] messages = new String[4]; + for (int i = 0; i < messages.length; i++) { + messages[i] = book.hasPage(player, node.asItem(), i + 1) ? "The " + getNumberName(i + 1) + " page is in the book." : "The " + getNumberName(i + 1) + " page is missing."; + } + player.getDialogueInterpreter().sendDialogue(messages); + return true; + case "preach": + player.getDialogueInterpreter().open("god-book", book); + return true; + } + } + return true; + } + + /** + * A god book item. + * @author Vexia + */ + public class GodBookItem extends ItemPlugin { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + for (GodBook book : GodBook.values()) { + register(book.getDamagedBook().getId()); + } + return this; + } + + @Override + public boolean canPickUp(Player player, GroundItem item, int type) { + if (player.hasItem(item.asItem())) { + player.sendMessage("You do not need more than one incomplete book."); + return false; + } + return true; + } + + } + + /** + * The page handler. + * @author Vexia + */ + public class PageHandler extends UseWithHandler { + + /** + * Constructs a new {@code PageHandler} {@code Object} + */ + public PageHandler() { + super(3839, 3841, 3843); + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + for (GodBook book : GodBook.values()) { + for (Item i : book.getPages()) { + addHandler(i.getId(), ITEM_TYPE, this); + } + } + return this; + } + + @Override + public boolean handle(NodeUsageEvent event) { + GodBook book = GodBook.forItem(event.getUsedItem(), true); + Player player = event.getPlayer(); + if (book != null && book.isPage(event.getUsedWith().asItem())) { + book.insertPage(player, event.getUsedItem(), event.getUsedWith().asItem()); + return true; + } + return false; + } + + } + + /** + * Gets the number name. + * @param i the integer to check. + * @return the number name. + */ + private String getNumberName(int i) { + return i == 1 ? "first" : i == 2 ? "second" : i == 3 ? "third" : "fourth"; + } + +} diff --git a/Server/src/main/java/core/game/interaction/item/GodswordDismantlePlugin.java b/Server/src/main/content/global/handlers/item/GodswordDismantlePlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/GodswordDismantlePlugin.java rename to Server/src/main/content/global/handlers/item/GodswordDismantlePlugin.java index 2be306576..64655e15c 100644 --- a/Server/src/main/java/core/game/interaction/item/GodswordDismantlePlugin.java +++ b/Server/src/main/content/global/handlers/item/GodswordDismantlePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt b/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt new file mode 100644 index 000000000..992d1009f --- /dev/null +++ b/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt @@ -0,0 +1,97 @@ +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 + +private const val SQUASH_GRAPHICS_BEGIN = 767 +private const val SQUASH_GRAPHICS_END = 769 +private const val SQUASH_ANIM_BEGIN = 4544 +private const val SQUASH_ANIM_END = 4546 +private const val LAUNCH_GRAPHICS = 768 +private const val LAUNCH_ANIMATION = 4547 + +/** + * Handles the grand tree pod options + * @author Ceikry + */ +class GrandSeedPodHandler : InteractionListener { + + override fun defineListeners() { + 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) + } + } + + 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 + } + } +} diff --git a/Server/src/main/java/core/game/interaction/item/HerbBoxPlugin.java b/Server/src/main/content/global/handlers/item/HerbBoxPlugin.java similarity index 98% rename from Server/src/main/java/core/game/interaction/item/HerbBoxPlugin.java rename to Server/src/main/content/global/handlers/item/HerbBoxPlugin.java index 907b32e36..1f2758fe3 100644 --- a/Server/src/main/java/core/game/interaction/item/HerbBoxPlugin.java +++ b/Server/src/main/content/global/handlers/item/HerbBoxPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; 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/kotlin/rs09/game/interaction/item/ImplingJarListener.kt b/Server/src/main/content/global/handlers/item/ImplingJarListener.kt similarity index 90% rename from Server/src/main/kotlin/rs09/game/interaction/item/ImplingJarListener.kt rename to Server/src/main/content/global/handlers/item/ImplingJarListener.kt index d9ba9e9e6..9fbd4060b 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/item/ImplingJarListener.kt +++ b/Server/src/main/content/global/handlers/item/ImplingJarListener.kt @@ -1,27 +1,33 @@ -package rs09.game.interaction.item +package content.global.handlers.item -import api.Container -import api.ContentAPI -import core.game.content.ttrail.ClueScrollPlugin +import core.api.Container +import core.api.* import org.rs09.consts.Items -import rs09.game.content.global.WeightBasedTable -import rs09.game.content.global.WeightedItem -import rs09.game.interaction.InteractionListener -import rs09.game.system.SystemLogger +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() { +class ImplingJarListener : InteractionListener { val JARS = ImplingLoot.values().map { it.jarId }.toIntArray() override fun defineListeners() { - on(JARS, ITEM, "loot"){player, node -> + 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()?.first() ?: return@on false + val loot = effectiveTable.roll().firstOrNull() ?: return@on false - if(ContentAPI.removeItem(player, jar, Container.INVENTORY)) { - ContentAPI.addItemOrDrop(player, loot.id, loot.amount) - ContentAPI.addItemOrDrop(player, Items.IMPLING_JAR_11260, 1) + if(removeItem(player, jar, Container.INVENTORY)) { + addItemOrDrop(player, loot.id, loot.amount) + addItemOrDrop(player, Items.IMPLING_JAR_11260, 1) } return@on true @@ -38,9 +44,9 @@ enum class ImplingLoot(val jarId: Int, val table: WeightBasedTable){ WeightedItem(Items.KNIFE_946,1, 1, 10.0), WeightedItem(Items.CHEESE_1985, 1, 1, 10.0), WeightedItem(Items.HAMMER_2347, 1, 1, 10.0), - WeightedItem(0, 1, 1, 10.0), WeightedItem(Items.BALL_OF_WOOL_1759, 1, 1, 10.0), WeightedItem(Items.ANCHOVIES_319, 1, 1, 10.0), + WeightedItem(0, 1, 1, 10.0), WeightedItem(Items.SPICE_2007, 1, 1, 1.0), WeightedItem(Items.FLAX_1779, 1, 1, 1.0), WeightedItem(Items.MUD_PIE_7170, 1, 1, 1.0), @@ -50,7 +56,6 @@ enum class ImplingLoot(val jarId: Int, val table: WeightBasedTable){ WeightedItem(Items.SAPPHIRE_1607, 1, 1, 1.0), WeightedItem(Items.HARD_LEATHER_1743, 1, 1, 1.0), WeightedItem(Items.LOBSTER_379, 1, 1, 1.0), - WeightedItem(Items.SOFT_CLAY_1761, 1, 1, 1.0) ).insertEasyClue(1.0)), YOUNG_IMPLING(Items.YOUNG_IMPLING_JAR_11240, WeightBasedTable.create( @@ -186,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), @@ -233,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), @@ -242,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) @@ -251,7 +256,7 @@ enum class ImplingLoot(val jarId: Int, val table: WeightBasedTable){ companion object{ - val jarMap = HashMap() + val jarMap = HashMap() init { for(impling in values()){ @@ -263,4 +268,4 @@ enum class ImplingLoot(val jarId: Int, val table: WeightBasedTable){ return jarMap[id] } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt b/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt new file mode 100644 index 000000000..8acaa698a --- /dev/null +++ b/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt @@ -0,0 +1,236 @@ +package content.global.handlers.item + +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 { + + private val fremennikIslesEquipment = intArrayOf(Items.HELM_OF_NEITIZNOT_10828, Items.HELM_OF_NEITIZNOT_E_12680, Items.HELM_OF_NEITIZNOT_CHARGED_12681) + private val fremennikTrialsEquipment = intArrayOf(Items.BERSERKER_HELM_3751, Items.BERSERKER_HELM_13408, Items.BERSERKER_HELM_E_12674, Items.BERSERKER_HELM_CHARGED_12675, + Items.FREMENNIK_HELM_3748, Items.WARRIOR_HELM_3753, Items.WARRIOR_HELM_13409, Items.WARRIOR_HELM_CHARGED_12677, Items.WARRIOR_HELM_E_12676, + Items.ARCHER_HELM_3749, Items.ARCHER_HELM_13407, Items.ARCHER_HELM_CHARGED_12673, Items.ARCHER_HELM_E_12672, + Items.ROCK_SHELL_HELM_6128, Items.ROCK_SHELL_HELM_10613, Items.ROCK_SHELL_HELM_13411, + Items.ROCK_SHELL_LEGS_6130, Items.ROCK_SHELL_LEGS_13413, + Items.ROCK_SHELL_PLATE_6129, Items.ROCK_SHELL_PLATE_13412, + Items.ROCK_SHELL_BOOTS_6145, Items.ROCK_SHELL_BOOTS_13421, + Items.ROCK_SHELL_GLOVES_6151, Items.ROCK_SHELL_GLOVES_13424, + Items.SPINED_HELM_6131, Items.SPINED_HELM_10614, Items.SPINED_HELM_13414, + Items.SPINED_BODY_6133, Items.SPINED_BODY_13415, + Items.SPINED_CHAPS_6135, Items.SPINED_CHAPS_13416, + Items.SPINED_GLOVES_6149, Items.SPINED_GLOVES_13423, + Items.SPINED_BOOTS_6143, Items.SPINED_BOOTS_13420, + Items.SKELETAL_HELM_6137, Items.SKELETAL_HELM_10604, Items.SKELETAL_HELM_13417, + Items.SKELETAL_TOP_6139, Items.SKELETAL_TOP_13418, + Items.SKELETAL_BOTTOMS_6141, Items.SKELETAL_BOTTOMS_13419, + Items.SKELETAL_GLOVES_6153, Items.SKELETAL_GLOVES_13425, + Items.SKELETAL_BOOTS_6147, Items.SKELETAL_BOOTS_13422, + Items.FREMENNIK_HAT_3798, Items.FREMENNIK_ROBE_3793, Items.FREMENNIK_SKIRT_3795, Items.GLOVES_3799, Items.FREMENNIK_BOOTS_3791, + Items.FREMENNIK_CLOAK_3759, Items.FREMENNIK_CLOAK_3761, Items.FREMENNIK_CLOAK_3763, Items.FREMENNIK_CLOAK_3765, Items.FREMENNIK_CLOAK_3777, Items.FREMENNIK_CLOAK_3779, + Items.FREMENNIK_CLOAK_3781, Items.FREMENNIK_CLOAK_3783, Items.FREMENNIK_CLOAK_3785, Items.FREMENNIK_CLOAK_3787, Items.FREMENNIK_CLOAK_3789, + Items.FREMENNIK_SHIRT_3767, Items.FREMENNIK_SHIRT_3769, Items.FREMENNIK_SHIRT_3771, Items.FREMENNIK_SHIRT_3773, Items.FREMENNIK_SHIRT_3775, + Items.FREMENNIK_BLADE_3757, Items.FREMENNIK_SHIELD_3758) + + private val fremennikIslesDuringQuestEquipment = intArrayOf(Items.YAK_HIDE_ARMOUR_10822, Items.YAK_HIDE_ARMOUR_10824, Items.FREMENNIK_ROUND_SHIELD_10826) + + private val avasBackpacks = intArrayOf(Items.AVAS_ACCUMULATOR_10499, Items.AVAS_ATTRACTOR_10498) + + private val lostCityWeapons = intArrayOf( + Items.DRAGON_DAGGER_1215, + Items.DRAGON_DAGGERP_1231, + Items.DRAGON_DAGGERP_PLUS_5680, + Items.DRAGON_DAGGERP_PLUS_PLUS_5698, + Items.DRAGON_LONGSWORD_1305 + ) + + private val crystalEquipment = intArrayOf( + Items.NEW_CRYSTAL_BOW_4212, + Items.CRYSTAL_BOW_FULL_4214, + Items.CRYSTAL_BOW_9_10_4215, + Items.CRYSTAL_BOW_8_10_4216, + Items.CRYSTAL_BOW_7_10_4217, + Items.CRYSTAL_BOW_6_10_4218, + Items.CRYSTAL_BOW_5_10_4219, + Items.CRYSTAL_BOW_4_10_4220, + Items.CRYSTAL_BOW_3_10_4221, + Items.CRYSTAL_BOW_2_10_4222, + Items.CRYSTAL_BOW_1_10_4223, + Items.NEW_CRYSTAL_SHIELD_4224, + Items.CRYSTAL_SHIELD_FULL_4225, + Items.CRYSTAL_SHIELD_9_10_4226, + Items.CRYSTAL_SHIELD_8_10_4227, + Items.CRYSTAL_SHIELD_7_10_4228, + Items.CRYSTAL_SHIELD_6_10_4229, + Items.CRYSTAL_SHIELD_5_10_4230, + Items.CRYSTAL_SHIELD_4_10_4231, + Items.CRYSTAL_SHIELD_3_10_4232, + Items.CRYSTAL_SHIELD_2_10_4233, + Items.CRYSTAL_SHIELD_1_10_4234 + ) + + //As per the source (https://runescape.wiki/w/Dragon_Slayer?oldid=895396), + //dragon slayer should only restrict green d'hide bodies, not the higher tiers. + private val dragonSlayerEquipment = intArrayOf( + Items.RUNE_PLATEBODY_1127, + Items.RUNE_PLATEBODY_G_10798, + Items.RUNE_PLATEBODY_G_2615, + Items.RUNE_PLATEBODY_G_13800, + Items.RUNE_PLATEBODY_T_10800, + Items.RUNE_PLATEBODY_T_13805, + Items.RUNE_PLATEBODY_T_2623, + Items.ZAMORAK_PLATEBODY_2653, + Items.SARADOMIN_PLATEBODY_2661, + Items.GUTHIX_PLATEBODY_2669, + Items.GREEN_DHIDE_BODY_1135, + Items.DHIDE_BODYG_7370, //Green (g) + Items.DHIDE_BODY_T_7372 //Green (t) + ) + + 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, Quests.THE_FREMENNIK_ISLES)) { + sendMessage(player, "You must have completed The Fremennik Isles to equip this.") + return@onEquip false + } + return@onEquip true + } + + onEquip(fremennikIslesDuringQuestEquipment){ player, _ -> + if (questStage(player, Quests.THE_FREMENNIK_ISLES) > 0) { + sendMessage(player, "You must have started The Fremennik Isles to equip this.") + return@onEquip false + } + return@onEquip true + } + */ + + 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 hasRequirement(player, Quests.ANIMAL_MAGNETISM) + } + + onEquip(lostCityWeapons){ player, _ -> + return@onEquip hasRequirement(player, Quests.LOST_CITY) + } + + onEquip(Items.CAPE_OF_LEGENDS_1052) { player, _ -> + return@onEquip hasRequirement(player, Quests.LEGENDS_QUEST) + } + + onEquip(questCapes) { player, _ -> + val maxQP = QuestRepository.getQuests().values.sumBy { it.questPoints } + if (player.questRepository.points < maxQP) { + player.packetDispatch.sendMessage("You cannot equip the Quest cape without completing all available quests.") + return@onEquip false + } + return@onEquip true + } + + onEquip(Items.WOLFBANE_2952){ player, _ -> + return@onEquip hasRequirement(player, Quests.PRIEST_IN_PERIL) + } + + onEquip(Items.ANCIENT_MACE_11061){ player, _ -> + return@onEquip hasRequirement(player, Quests.ANOTHER_SLICE_OF_HAM) + } + + onEquip(Items.ANCIENT_STAFF_4675){ player, _ -> + return@onEquip hasRequirement(player, Quests.DESERT_TREASURE) + } + + onEquip(Items.ELEMENTAL_SHIELD_2890) { player, _ -> + return@onEquip hasRequirement(player, Quests.ELEMENTAL_WORKSHOP_I) + } + + onEquip(crystalEquipment){ player, _ -> + return@onEquip hasRequirement(player, Quests.ROVING_ELVES) + } + + onEquip(dragonSlayerEquipment) {player, _ -> + 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/LampPlugin.java b/Server/src/main/content/global/handlers/item/LampPlugin.java new file mode 100644 index 000000000..32a091c3d --- /dev/null +++ b/Server/src/main/content/global/handlers/item/LampPlugin.java @@ -0,0 +1,64 @@ +package content.global.handlers.item; + +import core.game.component.Component; +import content.data.Lamps; +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.game.node.entity.skill.Skills; + +/** + * Represents the plugin used for an experience lamp. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class LampPlugin extends OptionHandler { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + for (Lamps lamp : Lamps.values()) { + lamp.getItem().getDefinition().getHandlers().put("option:rub", this); + } + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + player.setAttribute("lamp", node); + player.setAttribute("caller",this); + player.getInterfaceManager().open(new Component(134).setCloseEvent((player1, c) -> { + player.getInterfaceManager().openDefaultTabs(); + player.removeAttribute("lamp"); + player.unlock(); + return true; + })); + player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13); + return true; + } + @Override + 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("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().getStaticLevel(skill) * 10); + } else { + player.getSkills().addExperience(skill, lamp.getExp()); + } + } + } + } + + @Override + public boolean isWalk() { + return false; + } + +} 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/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/java/core/game/interaction/item/NewComerMapPlugin.java b/Server/src/main/content/global/handlers/item/NewComerMapPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/item/NewComerMapPlugin.java rename to Server/src/main/content/global/handlers/item/NewComerMapPlugin.java index a0eb0a58d..cf1cd0157 100644 --- a/Server/src/main/java/core/game/interaction/item/NewComerMapPlugin.java +++ b/Server/src/main/content/global/handlers/item/NewComerMapPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; 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/java/core/game/interaction/item/OysterPlugin.java b/Server/src/main/content/global/handlers/item/OysterPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/item/OysterPlugin.java rename to Server/src/main/content/global/handlers/item/OysterPlugin.java index 1abe26d14..179072a79 100644 --- a/Server/src/main/java/core/game/interaction/item/OysterPlugin.java +++ b/Server/src/main/content/global/handlers/item/OysterPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/global/handlers/item/PetRock.kt b/Server/src/main/content/global/handlers/item/PetRock.kt new file mode 100644 index 000000000..355ea3742 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/PetRock.kt @@ -0,0 +1,66 @@ +package content.global.handlers.item + +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.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 PetRockListener : InteractionListener { + override fun defineListeners() { + on(Items.PET_ROCK_3695, IntType.ITEM, "interact") { player, _ -> + openDialogue(player, PetRockDialogue()) + return@on true + } + } +} + +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 -> { + 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/java/core/game/interaction/item/PickupPlugin.java b/Server/src/main/content/global/handlers/item/PickupPlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/item/PickupPlugin.java rename to Server/src/main/content/global/handlers/item/PickupPlugin.java index 02c3730c0..e04d3edba 100644 --- a/Server/src/main/java/core/game/interaction/item/PickupPlugin.java +++ b/Server/src/main/content/global/handlers/item/PickupPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; @@ -8,7 +8,7 @@ import core.game.node.item.GroundItem; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.content.global.action.PickupHandler; +import core.game.global.action.PickupHandler; /** * Represents the option handler used for ground items. diff --git a/Server/src/main/content/global/handlers/item/PlayerPeltables.kt b/Server/src/main/content/global/handlers/item/PlayerPeltables.kt new file mode 100644 index 000000000..931a39ef2 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/PlayerPeltables.kt @@ -0,0 +1,115 @@ +package content.global.handlers.item + +import core.api.* +import core.game.interaction.* +import core.game.node.Node +import core.game.node.entity.impl.Projectile +import core.game.node.entity.player.Player +import core.game.node.item.Item +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 + + +class PlayerPeltables : InteractionListener { + companion object { + private const val PROJECTILE_DELAY = 41 + private const val PROJECTILE_SPEED = 60 + private const val PROJECTILE_TIME_CONST = .02857 + private const val PROJECTILE_DISTANCE_MULT = 5 + private val PELTABLES = intArrayOf(Items.ROTTEN_TOMATO_2518, Items.SNOWBALL_11951) + } + + + override fun defineListeners() { + onEquip(PELTABLES, ::setPlayerOps) + onUnequip(PELTABLES, ::removePlayerOps) + + on("pelt", IntType.PLAYER, ::handlePeltInteraction) + onUseWithPlayer(*PELTABLES) { player, used, with -> handlePeltInteraction(player, with, used) } + flagInstant() + } + + private fun setPlayerOps(player: Player, _node: Node) : Boolean { + player.interaction.set(Option("Pelt", 0)) + return true + } + + private fun removePlayerOps(player: Player, _node: Node) : Boolean { + InteractPlugin.sendOption(player, 0, "null") + return true + } + + private fun handlePeltInteraction(player: Player, node: Node, usedPeltable: Node? = null) : Boolean { + val peltable = usedPeltable?.asItem() ?: getPeltable(player) ?: return removePlayerOps(player, node) + val gfx = getPeltableGfx(peltable.id) + + val other = node.asPlayer() + + if (!Pathfinder.find(player, other, false, Pathfinder.PROJECTILE).isSuccessful) { + sendDialogue(player, "You can't reach them!") + return true + } + + val distance = player.location.getDistance(other.location) + val projectileSpeed = PROJECTILE_DELAY + PROJECTILE_SPEED + distance * PROJECTILE_DISTANCE_MULT + val hitDelay = (projectileSpeed * PROJECTILE_TIME_CONST).toInt() + + if (removeItem(player, peltable, Container.INVENTORY) || removeItem(player, peltable, Container.EQUIPMENT)) { + lock(player, hitDelay) + submitWorldPulse(PeltingPulse(player, other, gfx, hitDelay, peltable.id)) + } + + return true + } + + class PeltingPulse(val player: Player, val other: Player, val gfx: IntArray, val hitDelay: Int, val peltable: Int) : Pulse() { + private val throwAnimation = getPeltableAnim(peltable) + private var ticks = 0 + override fun pulse(): Boolean { + when (ticks++) { + 0 -> { + player.face(other) + visualize(player, throwAnimation, gfx[0]) + } + 1 -> { + Projectile.create(player, other, gfx[1], 30, 10).send() + face(player, player) //reset face flag lel + unlock(player) + } + hitDelay -> { + if (gfx[2] != -1) other.graphics(Graphics(gfx[2])) + sendMessage(other, "${player.username} has hit you with a ${getItemName(peltable).toLowerCase()}.") + return true + } + } + return false + } + + private fun getPeltableAnim(id: Int) : Int { + return when (id) { + Items.SNOWBALL_11951 -> 7530 + Items.ROTTEN_TOMATO_2518 -> 385 + else -> -1 + } + } + } + + private fun getPeltableGfx(id: Int): IntArray { + return when (id) { + Items.SNOWBALL_11951 -> intArrayOf(-1, 861, 1282) + Items.ROTTEN_TOMATO_2518 -> intArrayOf(-1, 29, 31) + else -> IntArray(3) {-1} + } + } + + private fun getPeltable(player: Player): Item? { + val equipped = getItemFromEquipment(player, EquipmentSlot.WEAPON) ?: return null + val id = equipped.id + + if (id !in PELTABLES) return null + + return equipped + } +} \ No newline at end of file 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/java/core/game/interaction/item/RingUnchargePlugin.java b/Server/src/main/content/global/handlers/item/RingUnchargePlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/RingUnchargePlugin.java rename to Server/src/main/content/global/handlers/item/RingUnchargePlugin.java index 281069d8c..7a5140b6d 100644 --- a/Server/src/main/java/core/game/interaction/item/RingUnchargePlugin.java +++ b/Server/src/main/content/global/handlers/item/RingUnchargePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/interaction/item/RunePackPlugin.java b/Server/src/main/content/global/handlers/item/RunePackPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/RunePackPlugin.java rename to Server/src/main/content/global/handlers/item/RunePackPlugin.java index 166a1b0f9..767aa9481 100644 --- a/Server/src/main/java/core/game/interaction/item/RunePackPlugin.java +++ b/Server/src/main/content/global/handlers/item/RunePackPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/global/handlers/item/SilverSicklePlugin.java b/Server/src/main/content/global/handlers/item/SilverSicklePlugin.java new file mode 100644 index 000000000..e7529d646 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/SilverSicklePlugin.java @@ -0,0 +1,43 @@ +package content.global.handlers.item; + +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.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. + * @author Splinter + */ +@Initializable +public final class SilverSicklePlugin extends OptionHandler { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ItemDefinition.forId(2963).getHandlers().put("option:operate", this); + ItemDefinition.forId(2963).getHandlers().put("option:cast bloom", this); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + switch (option) { + case "operate": + case "cast bloom": + if(player.getQuestRepository().getQuest(Quests.NATURE_SPIRIT).getStage(player) >= 75) { + player.getPacketDispatch().sendAnimation(9021); + NSUtils.castBloom(player); + } else { + sendDialogue(player, "You must complete Nature Spirit to use this."); + } + return true; + } + return false; + } + +} diff --git a/Server/src/main/java/core/game/interaction/item/SkullSceptreOption.java b/Server/src/main/content/global/handlers/item/SkullSceptreOption.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/SkullSceptreOption.java rename to Server/src/main/content/global/handlers/item/SkullSceptreOption.java index 212f0b5e0..b2ed70389 100644 --- a/Server/src/main/java/core/game/interaction/item/SkullSceptreOption.java +++ b/Server/src/main/content/global/handlers/item/SkullSceptreOption.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/kotlin/rs09/game/interaction/SpadeDigListener.kt b/Server/src/main/content/global/handlers/item/SpadeDigListener.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/interaction/SpadeDigListener.kt rename to Server/src/main/content/global/handlers/item/SpadeDigListener.kt index bf101de61..4d9bad6f0 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/SpadeDigListener.kt +++ b/Server/src/main/content/global/handlers/item/SpadeDigListener.kt @@ -1,4 +1,4 @@ -package rs09.game.interaction +package content.global.handlers.item import core.game.node.entity.player.Player import core.game.world.map.Location @@ -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/java/core/game/interaction/item/SpadeDigOptionPlugin.java b/Server/src/main/content/global/handlers/item/SpadeDigOptionPlugin.java similarity index 79% rename from Server/src/main/java/core/game/interaction/item/SpadeDigOptionPlugin.java rename to Server/src/main/content/global/handlers/item/SpadeDigOptionPlugin.java index 45872e953..70d478faa 100644 --- a/Server/src/main/java/core/game/interaction/item/SpadeDigOptionPlugin.java +++ b/Server/src/main/content/global/handlers/item/SpadeDigOptionPlugin.java @@ -1,12 +1,15 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; -import core.game.content.global.action.DigSpadeHandler; +import core.game.global.action.DigSpadeHandler; 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.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the plugin used to handle the dig option on a spade. @@ -24,6 +27,7 @@ public class SpadeDigOptionPlugin extends OptionHandler { @Override public boolean handle(final Player player, Node node, String option) { + playAudio(player, Sounds.DIGSPADE_1470); if (!DigSpadeHandler.dig(player)) { player.sendMessage("You dig but find nothing."); } diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/StaffOfTheRaven.kt b/Server/src/main/content/global/handlers/item/StaffOfTheRaven.kt similarity index 86% rename from Server/src/main/kotlin/rs09/game/interaction/item/StaffOfTheRaven.kt rename to Server/src/main/content/global/handlers/item/StaffOfTheRaven.kt index f2364cb20..90c2e7f0f 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/item/StaffOfTheRaven.kt +++ b/Server/src/main/content/global/handlers/item/StaffOfTheRaven.kt @@ -1,25 +1,26 @@ -package rs09.game.interaction.item +package content.global.handlers.item -import api.ContentAPI +import core.api.* import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.world.update.flag.context.Graphics -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListener -import rs09.tools.END_DIALOGUE +import core.game.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.tools.END_DIALOGUE /** * Handles the Staff of the Raven's (2021 Hween Reward) Recolor Transformation */ -class StaffOfTheRaven : InteractionListener() { +class StaffOfTheRaven : InteractionListener { val ids = intArrayOf(14654, 14655, 14656) override fun defineListeners() { - on(ids, ITEM, "recolor", "operate"){player, node -> + on(ids, IntType.ITEM, "recolor", "operate"){ player, node -> val hasUnlocked = player.getAttribute("sotr:purchased",false) val hasRecolorA = player.getAttribute("sotr:recolor1", false) val hasRecolorB = player.getAttribute("sotr:recolor2", false) val isBase = node.id == 14654 - val isOperate = ContentAPI.getUsedOption(player) == "operate" + val isOperate = getUsedOption(player) == "operate" if(!hasUnlocked){ //Remove the item if the player has not purchased it. Just in case. diff --git a/Server/src/main/java/core/game/interaction/item/StarterPackPlugin.java b/Server/src/main/content/global/handlers/item/StarterPackPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/StarterPackPlugin.java rename to Server/src/main/content/global/handlers/item/StarterPackPlugin.java index 029d36eba..152b12e8f 100644 --- a/Server/src/main/java/core/game/interaction/item/StarterPackPlugin.java +++ b/Server/src/main/content/global/handlers/item/StarterPackPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; 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/java/core/game/interaction/item/TeleportCrystalPlugin.java b/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java similarity index 93% rename from Server/src/main/java/core/game/interaction/item/TeleportCrystalPlugin.java rename to Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java index beff82c9d..5bbda0ffc 100644 --- a/Server/src/main/java/core/game/interaction/item/TeleportCrystalPlugin.java +++ b/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.item; +package content.global.handlers.item; import core.cache.def.impl.ItemDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; @@ -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/ToyHorseyListener.kt b/Server/src/main/content/global/handlers/item/ToyHorseyListener.kt new file mode 100644 index 000000000..e16a39e42 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/ToyHorseyListener.kt @@ -0,0 +1,41 @@ +package content.global.handlers.item + +import core.api.animate +import core.api.sendChat +import core.api.stopWalk +import org.rs09.consts.Animations +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Interaction listener for the Toy Horsey item + * @author Woah + */ +class ToyHorseListener : InteractionListener { + + // Map of horse item ids to their correct emote + val HORSEY_MAP = mapOf( + Items.TOY_HORSEY_2520 to Animations.HUMAN_PLAY_WITH_BROWN_HORSE_918, + Items.TOY_HORSEY_2522 to Animations.HUMAN_PLAY_WITH_WHITE_HORSE_919, + Items.TOY_HORSEY_2524 to Animations.HUMAN_PLAY_WITH_BLACK_HORSE_920, + Items.TOY_HORSEY_2526 to Animations.HUMAN_PLAY_WITH_GRAY_HORSE_921 + ) + + // Array of phrases used during the interaction + val PHRASES = arrayOf( + "Come-on Dobbin, we can win the race!", + "Hi-ho Silver, and away", + "Neaahhhyyy! Giddy-up horsey!" + ) + + override fun defineListeners() { + on(HORSEY_MAP.keys.toIntArray(), IntType.ITEM, "play-with") { player, node -> + // "high-priority" interaction, so movement is stopped + stopWalk(player) + animate(player, HORSEY_MAP.get(node.id)) + sendChat(player, PHRASES.random()) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/ToyListeners.kt b/Server/src/main/content/global/handlers/item/ToyListeners.kt new file mode 100644 index 000000000..edf14fa89 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/ToyListeners.kt @@ -0,0 +1,136 @@ +package content.global.handlers.item + +import core.api.* +import core.game.world.update.flag.context.Animation +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 { + val MARIONETTES = intArrayOf(Items.RED_MARIONETTE_6867, Items.GREEN_MARIONETTE_6866, Items.BLUE_MARIONETTE_6865) + private val MARIONETTE_JUMP = Animation(3003) + 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 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_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) + val YOYO_CRAZY = Animation(1460) + val ZOMBIE_HEAD_TALK_AT = Animation(2840) + val ZOMBIE_HEAD_DISPLAY = Animation(2844) + } + + override fun defineListeners() { + on(Items.CHOCATRICE_CAPE_12645, IntType.ITEM, "operate") { player, _ -> + lockInteractions(player, 2) + visualize(player, 8903, 1566) + return@on true + } + + on(MARIONETTES, IntType.ITEM, "jump", "walk", "bow", "dance") { player, marionette -> + val index = MARIONETTES.indexOf(marionette.id) + + lockInteractions(player, 2) + 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]) + "dance" -> visualize(player, MARIONETTE_DANCE, MARIONETTE_GFX[index][3]) + } + return@on true + } + + on(Items.REINDEER_HAT_10507, IntType.ITEM, "operate") { player, _ -> + lockInteractions(player, 2) + visualize(player, 5059, 859) + return@on true + } + + on(Items.SNOW_GLOBE_11949, IntType.ITEM, "shake") { player, _ -> + lockInteractions(player, 2) + animate(player, SNOWGLOBE_SHAKE) + runTask(player, 3) { + animate(player, SNOWGLOBE_HOLDFACE) + runTask(player) { + openInterface(player, SNOWGLOBE_INTERFACE) + } + } + return@on true + } + + 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) { + "play" -> animate(player, YOYO_PLAY) + "loop" -> animate(player, YOYO_LOOP) + "walk" -> animate(player, YOYO_WALK) + "crazy" -> animate(player, YOYO_CRAZY) + } + return@on true + } + + on(Items.ZOMBIE_HEAD_6722, IntType.ITEM, "talk-at", "display", "question") { player, _ -> + val option = getUsedOption(player) + + lockInteractions(player, 2) + when (option) { + "talk-at" -> { + animate(player, ZOMBIE_HEAD_TALK_AT) + sendChat(player, "Alas!") + } + + "display" -> { + animate(player, ZOMBIE_HEAD_DISPLAY) + sendChat(player, "MWAHAHAHAHAHAHAH") + } + } + 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.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/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.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 new file mode 100644 index 000000000..1a0960711 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/BarrowsEquipmentRegister.kt @@ -0,0 +1,19 @@ +package content.global.handlers.item.equipment + +import core.plugin.Initializable +import core.plugin.Plugin + +@Initializable +class BarrowsEquipmentRegister : Plugin { + override fun newInstance(arg: Any?): Plugin { + 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/CrystalEquipmentRegister.kt b/Server/src/main/content/global/handlers/item/equipment/CrystalEquipmentRegister.kt new file mode 100644 index 000000000..2bb7bb48d --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/CrystalEquipmentRegister.kt @@ -0,0 +1,13 @@ +package content.global.handlers.item.equipment + +import core.api.StartupListener +import org.rs09.consts.Items + +class CrystalEquipmentRegister : StartupListener { + val shield: Array = arrayOf(Items.NEW_CRYSTAL_SHIELD_4224, Items.CRYSTAL_SHIELD_FULL_4225, Items.CRYSTAL_SHIELD_9_10_4226, Items.CRYSTAL_SHIELD_8_10_4227, Items.CRYSTAL_SHIELD_7_10_4228, Items.CRYSTAL_SHIELD_6_10_4229, Items.CRYSTAL_SHIELD_5_10_4230, Items.CRYSTAL_SHIELD_4_10_4231, Items.CRYSTAL_SHIELD_3_10_4232, Items.CRYSTAL_SHIELD_2_10_4233, Items.CRYSTAL_SHIELD_1_10_4234, Items.CRYSTAL_SEED_4207) + val bow: Array = arrayOf(Items.NEW_CRYSTAL_BOW_4212, Items.CRYSTAL_BOW_FULL_4214, Items.CRYSTAL_BOW_9_10_4215, Items.CRYSTAL_BOW_8_10_4216, Items.CRYSTAL_BOW_7_10_4217, Items.CRYSTAL_BOW_6_10_4218, Items.CRYSTAL_BOW_5_10_4219, Items.CRYSTAL_BOW_4_10_4220, Items.CRYSTAL_BOW_3_10_4221, Items.CRYSTAL_BOW_2_10_4222, Items.CRYSTAL_BOW_1_10_4223, Items.CRYSTAL_SEED_4207) + override fun startup() { + EquipmentDegrader.registerSet(250, shield) + EquipmentDegrader.registerSet(250, bow) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/equipment/EquipmentDegrader.kt b/Server/src/main/content/global/handlers/item/equipment/EquipmentDegrader.kt new file mode 100644 index 000000000..2da19f410 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/EquipmentDegrader.kt @@ -0,0 +1,95 @@ +package content.global.handlers.item.equipment + +import core.api.addItemOrDrop +import core.api.getNext +import core.api.isLast +import core.api.isNextLast +import core.game.node.entity.player.Player +import core.game.node.item.Item + +/** + * Handles equipment degrading + * @author ceik + */ +class EquipmentDegrader{ + + companion object StaticDegrader { //Use a static companion object for lists and registers so they persist across instances + val itemList = arrayListOf() + val setList = arrayListOf>() + val itemCharges = hashMapOf() + fun register(charges: Int, item: Int){ + itemList.add(item) + itemCharges.put(item, charges) + } + + fun registerSet(charges: Int, items: Array){ + setList.add(ArrayList(items.map { item -> item.also { register(charges, item) } })) + } + } + + + + var p: Player? = null + + fun Int.canDegrade(): Boolean { //extension function that checks if an item can degrade + return itemList.contains(this) + } + + fun checkArmourDegrades(player: Player?){ //loops the player slots and if the item in that slot is non-null, checks if it can degrade etc + p = player + for(slot in 0..12){ + if(slot == 3){ + continue + } + player?.equipment?.get(slot)?.let { if(it.id.canDegrade()) it.degrade(slot) } + } + } + + fun checkWeaponDegrades(player: Player?){ + p = player + player?.equipment?.get(3)?.let { if(it.id.canDegrade()) it.degrade(3) } + } + + fun Item.degrade(slot: Int){ //extension function that degrades items + val set = getDegradableSet(this.id) + val charges = itemCharges.getOrElse(this.id) {1000} + if (this.charge > charges) + charge = charges + this.charge-- + if(set?.indexOf(this.id) == 0 && !this.name.contains("100")){ + charge = 0 + } + if(this.charge <= 0) { + val charges = itemCharges.getOrElse(this.id) { 1000 } + if (set?.size!! > 2) { + p?.equipment?.remove(this) + p?.sendMessage("Your $name has degraded.") + if (set.isNextLast(this.id)) { + p?.let { addItemOrDrop(it, set.getNext(this.id)) } + p?.sendMessage("Your $name has broken.") + } else { + p?.equipment?.add(Item(set.getNext(this.id), 1, charges), slot, false, false) + p?.equipment?.refresh() + } + } else if (set.size == 2) { + if(set.isLast(this.id)){ + p?.equipment?.remove(this) + p?.sendMessage("Your $name degrades into dust.") + } else { + p?.equipment?.remove(this) + p?.sendMessage("Your $name has degraded.") + p?.equipment?.add(Item(set.getNext(this.id), 1, charges), slot, false, false) + p?.equipment?.refresh() + } + } + } + } + + private fun getDegradableSet(item: Int): ArrayList?{ //gets the set of items this item belongs to + for(set in setList){ + if(set.contains(item)) + return set + } + return null + } +} diff --git a/Server/src/main/content/global/handlers/item/equipment/PVPEquipmentRegister.kt b/Server/src/main/content/global/handlers/item/equipment/PVPEquipmentRegister.kt new file mode 100644 index 000000000..81d957626 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/PVPEquipmentRegister.kt @@ -0,0 +1,54 @@ +package content.global.handlers.item.equipment + +import core.plugin.Initializable +import core.plugin.Plugin + +@Initializable +class PVPEquipmentRegister : Plugin { + // PVP equipment lasts for 1 hour of combat, which is 6000 ticks + val TICKS = 6000 + + val VESTA_BODY = arrayOf(13887,13889) + val VESTA_SKIRT = arrayOf(13893,13895) + val VESTA_SWORD = arrayOf(13899,13901) + val VESTA_SPEAR = arrayOf(13905,13907) + + val STATIUS_BODY = arrayOf(13884,13886) + val STATIUS_LEGS = arrayOf(13890,13892) + val STATIUS_HELM = arrayOf(13896,13898) + val STATIUS_HAMMER = arrayOf(13902,13904) + + val ZURIEL_TOP = arrayOf(13858,13860) + val ZUREL_BOTTOM = arrayOf(13861,13863) + val ZURIEL_HOOD = arrayOf(13864,13866) + val ZURIEL_STAFF = arrayOf(13867, 13869) + + val MORRIGAN_BODY = arrayOf(13870,13872) + val MORRIGAN_CHAP = arrayOf(13873,13875) + val MORRIGAN_COIF = arrayOf(13876,13878) + + + override fun newInstance(arg: Any?): Plugin { + EquipmentDegrader.registerSet(TICKS, VESTA_BODY) + EquipmentDegrader.registerSet(TICKS, VESTA_SKIRT) + EquipmentDegrader.registerSet(TICKS, VESTA_SWORD) + EquipmentDegrader.registerSet(TICKS, VESTA_SPEAR) + EquipmentDegrader.registerSet(TICKS, STATIUS_BODY) + EquipmentDegrader.registerSet(TICKS, STATIUS_HAMMER) + EquipmentDegrader.registerSet(TICKS, STATIUS_HELM) + EquipmentDegrader.registerSet(TICKS, STATIUS_LEGS) + EquipmentDegrader.registerSet(TICKS, ZUREL_BOTTOM) + EquipmentDegrader.registerSet(TICKS, ZURIEL_HOOD) + EquipmentDegrader.registerSet(TICKS, ZURIEL_STAFF) + EquipmentDegrader.registerSet(TICKS, ZURIEL_TOP) + EquipmentDegrader.registerSet(TICKS, MORRIGAN_BODY) + EquipmentDegrader.registerSet(TICKS, MORRIGAN_CHAP) + EquipmentDegrader.registerSet(TICKS, MORRIGAN_COIF) + return this + } + + override fun fireEvent(identifier: String?, vararg args: Any?): Any { + return Unit + } + +} diff --git a/Server/src/main/java/core/game/interaction/item/brawling_gloves/BrawlingGloves.java b/Server/src/main/content/global/handlers/item/equipment/brawling_gloves/BrawlingGloves.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/brawling_gloves/BrawlingGloves.java rename to Server/src/main/content/global/handlers/item/equipment/brawling_gloves/BrawlingGloves.java index 385541b5c..6f09426f7 100644 --- a/Server/src/main/java/core/game/interaction/item/brawling_gloves/BrawlingGloves.java +++ b/Server/src/main/content/global/handlers/item/equipment/brawling_gloves/BrawlingGloves.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.brawling_gloves; +package content.global.handlers.item.equipment.brawling_gloves; import core.cache.def.impl.ItemDefinition; import core.game.node.entity.skill.Skills; diff --git a/Server/src/main/content/global/handlers/item/equipment/brawling_gloves/BrawlingGlovesManager.java b/Server/src/main/content/global/handlers/item/equipment/brawling_gloves/BrawlingGlovesManager.java new file mode 100644 index 000000000..8623ca4fe --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/brawling_gloves/BrawlingGlovesManager.java @@ -0,0 +1,102 @@ +package content.global.handlers.item.equipment.brawling_gloves; + +import core.api.LoginListener; +import core.api.PersistPlayer; +import core.cache.def.impl.ItemDefinition; +import core.game.node.entity.player.Player; + +import core.game.node.item.Item; +import org.jetbrains.annotations.NotNull; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.util.HashMap; +import java.util.Objects; + +/** + * Manages brawling gloves for a player + * @author ceik + */ +public class BrawlingGlovesManager implements LoginListener, PersistPlayer { + final Player player; + public HashMap GloveCharges = new HashMap(); + + public BrawlingGlovesManager(Player player){this.player = player;} + public BrawlingGlovesManager(){this.player = null;} + + @Override + public void login(@NotNull Player player) { + BrawlingGlovesManager instance = new BrawlingGlovesManager(player); + player.setAttribute("bg-manager", instance); + } + + @Override + public void parsePlayer(@NotNull Player player, @NotNull JSONObject data) { + BrawlingGlovesManager instance = getInstance(player); + if(data.containsKey("brawlingGloves")) + { + JSONArray bgData = (JSONArray) data.get("brawlingGloves"); + for (Object bg : bgData) { + JSONObject glove = (JSONObject) bg; + instance.registerGlove(BrawlingGloves.forIndicator(Integer.parseInt(glove.get("gloveId").toString())).getId(), Integer.parseInt(glove.get("charges").toString())); + } + } + } + + @Override + public void savePlayer(@NotNull Player player, @NotNull JSONObject save) { + if(getInstance(player).GloveCharges.size() > 0){ + JSONArray brawlingGloves = new JSONArray(); + getInstance(player).GloveCharges.entrySet().forEach(glove -> { + JSONObject bGlove = new JSONObject(); + bGlove.put("gloveId", Integer.toString(BrawlingGloves.forId(glove.getKey()).getIndicator())); + bGlove.put("charges", Integer.toString(glove.getValue())); + brawlingGloves.add(bGlove); + }); + save.put("brawlingGloves", brawlingGloves); + } + } + + public void registerGlove(int id) { + try { + registerGlove(id, Objects.requireNonNull(BrawlingGloves.forId(id)).getCharges()); + } catch (Exception e){ + e.printStackTrace(); + } + } + public void registerGlove(int id, int charges) {GloveCharges.putIfAbsent(id,charges);} + + + public void updateCharges(int glove, int charges){ + if(GloveCharges.get(glove) != null){ + if(GloveCharges.get(glove) - charges <= 0) { + GloveCharges.remove(glove); + player.getEquipment().remove(new Item(glove)); + player.getPacketDispatch().sendMessage("You use the last charge of your " + ItemDefinition.forId(glove).getName() + " and they vanish."); + return; + } + int currentCharges = GloveCharges.get(glove); + GloveCharges.replace(glove,currentCharges - charges); + player.debug("Glove charges: " + (currentCharges - 1)); + if((currentCharges - 1) % 50 == 0) { + player.getPacketDispatch().sendMessage("Your " + ItemDefinition.forId(glove).getName() + " have " + GloveCharges.get(glove) + " charges left."); + } + } + } + + public double getExperienceBonus(){ + double bonus; + int level = player.getSkullManager().getLevel(); + if(level > 0){ + bonus = 3.0; + }else{ + bonus = 0.5; + } + return bonus; + } + + public static BrawlingGlovesManager getInstance(Player player) + { + return player.getAttribute("bg-manager", new BrawlingGlovesManager()); + } +} 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/java/core/game/node/entity/combat/special/AncientMaceSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/AncientMaceSpecialHandler.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/combat/special/AncientMaceSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/AncientMaceSpecialHandler.java index 60874e957..9c04d990f 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/AncientMaceSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/AncientMaceSpecialHandler.java @@ -1,9 +1,9 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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; @@ -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/java/core/game/node/entity/combat/special/BackstabSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/BackstabSpecialHandler.java similarity index 76% rename from Server/src/main/java/core/game/node/entity/combat/special/BackstabSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/BackstabSpecialHandler.java index 810f86c62..2baaab97e 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/BackstabSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/BackstabSpecialHandler.java @@ -1,10 +1,10 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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; @@ -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/java/core/game/node/entity/combat/special/ChainhitSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/combat/special/ChainhitSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java index e9794799d..b3e276dcd 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/ChainhitSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ChainhitSpecialHandler.java @@ -1,9 +1,7 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.RangeSwingHandler; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; +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(); @@ -144,7 +152,7 @@ public final class ChainhitSpecialHandler extends RangeSwingHandler implements P for (Iterator it = list.iterator(); it.hasNext();) { final Entity e = it.next(); it.remove(); - if (!e.isAttackable(player, CombatStyle.RANGE) || !e.getProperties().isMultiZone()) { + if (!e.isAttackable(player, CombatStyle.RANGE, false) || !e.getProperties().isMultiZone()) { continue; } double distance = victim.getLocation().getDistance(e.getLocation()); @@ -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/java/core/game/node/entity/combat/handlers/ChinchompaSwingHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/combat/handlers/ChinchompaSwingHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java index 0bed935e6..a0801835e 100644 --- a/Server/src/main/java/core/game/node/entity/combat/handlers/ChinchompaSwingHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ChinchompaSwingHandler.java @@ -1,9 +1,7 @@ -package core.game.node.entity.combat.handlers; +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 rs09.game.node.entity.combat.handlers.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/java/core/game/node/entity/combat/special/CleaveSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java similarity index 80% rename from Server/src/main/java/core/game/node/entity/combat/special/CleaveSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java index 2cbae7577..f728dbaf2 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/CleaveSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/CleaveSpecialHandler.java @@ -1,17 +1,19 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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.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/java/core/game/node/entity/combat/special/DescentOfDarknessSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/DescentOfDarknessSpecialHandler.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/combat/special/DescentOfDarknessSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/DescentOfDarknessSpecialHandler.java index 6eb5d076f..75ae1f8a8 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/DescentOfDarknessSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/DescentOfDarknessSpecialHandler.java @@ -1,4 +1,4 @@ -package core.game.node.entity.combat.special; +package content.global.handlers.item.equipment.special; import core.cache.def.impl.ItemDefinition; import core.game.node.entity.Entity; @@ -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 rs09.game.node.entity.combat.handlers.RangeSwingHandler; +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 new file mode 100644 index 000000000..0497836a8 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java @@ -0,0 +1,209 @@ +package content.global.handlers.item.equipment.special; + +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.CombatSwingHandler; +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.npc.NPC; +import core.game.node.entity.player.Player; +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. + * @author Emperor + */ +public class DragonfireSwingHandler extends CombatSwingHandler { + + /** + * If the NPC has to be in melee range. + */ + private boolean meleeRange; + + /** + * The maximum hit. + */ + private int maximumHit; + + /** + * The attack data. + */ + private SwitchAttack attack; + + /** + * 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; + } + + /** + * 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; + } + + @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 + 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 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 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 + public int calculateAccuracy(Entity entity) { + return 4000; + } + + @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 double getSetMultiplier(Entity e, int skillId) { + return 1.0; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/combat/special/EnergyDrainSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/EnergyDrainSpecialHandler.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/combat/special/EnergyDrainSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/EnergyDrainSpecialHandler.java index 2428593c7..4686eb2b8 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/EnergyDrainSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/EnergyDrainSpecialHandler.java @@ -1,9 +1,9 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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; @@ -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/java/core/game/node/entity/combat/special/ExcaliburSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ExcaliburSpecialHandler.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/combat/special/ExcaliburSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/ExcaliburSpecialHandler.java index 927c7e679..3ba8c9992 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/ExcaliburSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ExcaliburSpecialHandler.java @@ -1,17 +1,20 @@ -package core.game.node.entity.combat.special; +package content.global.handlers.item.equipment.special; import core.game.container.impl.EquipmentContainer; 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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the excalibur special attack. @@ -60,18 +63,18 @@ 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 + 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)); } - return -1; } - } diff --git a/Server/src/main/java/core/game/node/entity/combat/special/FeintSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/FeintSpecialHandler.java similarity index 76% rename from Server/src/main/java/core/game/node/entity/combat/special/FeintSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/FeintSpecialHandler.java index a2362766b..b85962790 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/FeintSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/FeintSpecialHandler.java @@ -1,15 +1,18 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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.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 new file mode 100644 index 000000000..5589c48c0 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/HealingBladeSpecialHandler.java @@ -0,0 +1,83 @@ +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.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.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. + * @author Emperor + * @version 1.0 + */ +@Initializable +public final class HealingBladeSpecialHandler extends MeleeSwingHandler implements Plugin { + + /** + * The special energy required. + */ + private static final int SPECIAL_ENERGY = 50; + + /** + * The attack animation. + */ + private static final Animation ANIMATION = new Animation(7071, Priority.HIGH); + + /** + * The graphic. + */ + private static final Graphics GRAPHIC = new Graphics(1220); + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.MELEE.getSwingHandler().register(11698, this); + CombatStyle.MELEE.getSwingHandler().register(13452, this); + return this; + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { + return -1; + } + state.setStyle(CombatStyle.MELEE); + int hit = 0; + 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); + 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/java/core/game/node/entity/combat/special/IceCleaveSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/IceCleaveSpecialHandler.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/combat/special/IceCleaveSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/IceCleaveSpecialHandler.java index 0c19987f4..83c1e4a50 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/IceCleaveSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/IceCleaveSpecialHandler.java @@ -1,17 +1,19 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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/java/core/game/node/entity/combat/special/ImpaleSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ImpaleSpecialHandler.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/combat/special/ImpaleSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/ImpaleSpecialHandler.java index dadc362c8..2e0ff5aa4 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/ImpaleSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ImpaleSpecialHandler.java @@ -1,9 +1,9 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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.info.Rights; @@ -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/java/core/game/node/entity/combat/special/JudgementSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/JudgementSpecialHandler.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/combat/special/JudgementSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/JudgementSpecialHandler.java index a4bf7853b..abdb00cc5 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/JudgementSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/JudgementSpecialHandler.java @@ -1,17 +1,19 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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/kotlin/rs09/game/node/entity/combat/special/LiquefySpecialHandler.kt b/Server/src/main/content/global/handlers/item/equipment/special/LiquefySpecialHandler.kt similarity index 88% rename from Server/src/main/kotlin/rs09/game/node/entity/combat/special/LiquefySpecialHandler.kt rename to Server/src/main/content/global/handlers/item/equipment/special/LiquefySpecialHandler.kt index d6485eef6..0cf259152 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/special/LiquefySpecialHandler.kt +++ b/Server/src/main/content/global/handlers/item/equipment/special/LiquefySpecialHandler.kt @@ -1,9 +1,9 @@ -package rs09.game.node.entity.combat.special +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler +import core.game.node.entity.combat.MeleeSwingHandler import core.plugin.Initializable import core.plugin.Plugin 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/java/core/game/node/entity/combat/special/PowershotSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/PowershotSpecialHandler.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/combat/special/PowershotSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/PowershotSpecialHandler.java index a1e5e738a..4fe5e5f90 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/PowershotSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/PowershotSpecialHandler.java @@ -1,4 +1,4 @@ -package core.game.node.entity.combat.special; +package content.global.handlers.item.equipment.special; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -9,7 +9,10 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; -import rs09.game.node.entity.combat.handlers.RangeSwingHandler; +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/java/core/game/node/entity/combat/special/PowerstabSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/combat/special/PowerstabSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java index 7b6783add..ba3ff8ed0 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/PowerstabSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/PowerstabSpecialHandler.java @@ -1,4 +1,4 @@ -package core.game.node.entity.combat.special; +package content.global.handlers.item.equipment.special; import java.util.List; @@ -6,7 +6,7 @@ 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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -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/java/core/game/node/entity/combat/special/PunctureSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/PunctureSpecialHandler.java similarity index 80% rename from Server/src/main/java/core/game/node/entity/combat/special/PunctureSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/PunctureSpecialHandler.java index 1b76ac3f7..b5fc3357f 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/PunctureSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/PunctureSpecialHandler.java @@ -1,9 +1,9 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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; @@ -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 new file mode 100644 index 000000000..e1d043c9d --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/QuickSmashSpecialHandler.java @@ -0,0 +1,96 @@ +package content.global.handlers.item.equipment.special; + +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.DeathTask; +import core.game.node.entity.combat.MeleeSwingHandler; +import core.game.node.entity.impl.Animator.Priority; +import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.prayer.PrayerType; +import core.game.world.GameWorld; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; +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. + * @author Emperor + */ +@Initializable +public final class QuickSmashSpecialHandler extends MeleeSwingHandler implements Plugin { + + /** + * The special energy required. + */ + private static final int SPECIAL_ENERGY = 50; + + /** + * The attack animation. + */ + private static final Animation ANIMATION = new Animation(1667, Priority.HIGH); + + /** + * The graphic. + */ + private static final Graphics GRAPHIC = new Graphics(340, 96); + + @Override + public Object fireEvent(String identifier, Object... args) { + switch (identifier) { + case "instant_spec": + return true; + } + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.MELEE.getSwingHandler().register(4153, this); + CombatStyle.MELEE.getSwingHandler().register(14792, this); + return this; + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + Player p = (Player) entity; + if (victim == null) { + victim = p.getProperties().getCombatPulse().getLastVictim(); + if (victim == null || GameWorld.getTicks() - p.getAttribute("combat-stop", -1) > 2 || !MeleeSwingHandler.Companion.canMelee(p, victim, 1)) { + p.getPacketDispatch().sendMessage("Warning: Since the maul's special is an instant attack, it will be wasted when used "); + p.getPacketDispatch().sendMessage("on a first strike."); + return -1; + } + } + if (DeathTask.isDead(victim)) { + return -1; + } + if (!p.getSettings().drainSpecial(SPECIAL_ENERGY)) { + return -1; + } + visualize(entity, victim, null); + int hit = 0; + if (isAccurateImpact(entity, victim)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.0) + 1); + } + 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 new file mode 100644 index 000000000..2e70ab9ec --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/RampageSpecialHandler.java @@ -0,0 +1,80 @@ +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.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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; + +/** + * Handles the Rampage special attack. + * @author Emperor + */ +@Initializable +public final class RampageSpecialHandler 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(1056, Priority.HIGH); + + /** + * The graphic. + */ + private static final Graphics GRAPHIC = new Graphics(246); + + @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(1377, 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("Raarrrrrgggggghhhhhhh!"); + playAudio(entity.asPlayer(), Sounds.RAMPAGE_2538); + p.visualize(ANIMATION, GRAPHIC); + @SuppressWarnings("unused") + int boost = 0; + 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); + } + } + 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 new file mode 100644 index 000000000..a0c5f6941 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/SalamanderSwingHandler.kt @@ -0,0 +1,237 @@ +package content.global.handlers.item.equipment.special + +import core.api.EquipmentSlot +import core.api.getItemFromEquipment +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.combat.equipment.Weapon +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.context.Animation +import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.Animations +import kotlin.math.ceil +import kotlin.math.floor + +/** + * Handles a combat swing using a salamander. + * @author Vexia + * @author itsmedoggo + */ +class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandler(style) { + override fun canSwing(entity: Entity, victim: Entity): InteractionType? { + checkStyle(entity) + if (!isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_INTERACT + } + var distance = 1 + var goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) + var type = InteractionType.STILL_INTERACT + if (victim.walkingQueue.isMoving && !goodRange) { + goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) + type = InteractionType.MOVE_INTERACT + } + if (goodRange && super.canSwing(entity, victim) != 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 { + state!!.style = style + if (entity is Player) { + state.weapon = Weapon(entity.equipment[3]) + } + if (state!!.weapon == null || !hasAmmo(entity, state)) { + entity!!.properties.combatPulse.stop() + return -1 + } + var hit = 0 + if (isAccurateImpact(entity, victim, style)) { + val max = calculateHit(entity, victim, 1.0) + state.maximumHit = max + hit = RandomFunction.random(max + 1) + } + state.estimatedHit = hit + if (entity == null || victim!!.location == null) { + return -1 + } + if(state.estimatedHit > victim.skills.lifepoints) state.estimatedHit = victim.skills.lifepoints + useAmmo(entity, state) + return 1 + } + + override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { + style.swingHandler.impact(entity, victim, state) + } + + override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { + entity.visualize(Animation.create(5247), Graphics(952, 100)) + } + + override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { + style.swingHandler.visualizeImpact(entity, victim, state) + } + + override fun calculateAccuracy(entity: Entity?): Int { + //Checking style is necessary for ::calc_accuracy to function after changing attack style but before attacking + checkStyle(entity) + return style.swingHandler.calculateAccuracy(entity) + } + + 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) { + val level = entity!!.skills.getLevel(Skills.MAGIC, true) + var bonus = entity.properties.bonuses[13] + if (entity is Player) { + bonus += getSalamanderMagicDamageBonus(entity.equipment[3].id) + } + var cumulativeStr = level.toDouble() + cumulativeStr *= getSetMultiplier(entity, Skills.MAGIC) + cumulativeStr *= (bonus + 64) + return floor((0.5 + (ceil(cumulativeStr) / 640.0)) * modifier).toInt() + } + return style.swingHandler.calculateHit(entity, victim, modifier) + } + + override fun addExperience(entity: Entity?, victim: Entity?, state: BattleState?) { + entity!!.skills.addExperience(Skills.HITPOINTS, state!!.estimatedHit * 1.33, true) + if (state.style == CombatStyle.MAGIC) { + entity.skills.addExperience(Skills.MAGIC, state.estimatedHit * 2.toDouble(), true) + } + if (state.style == CombatStyle.RANGE) { + entity.skills.addExperience(Skills.RANGE, state.estimatedHit * 4.toDouble(), true) + } + if (state.style == CombatStyle.MELEE) { + entity.skills.addExperience(Skills.STRENGTH, state.estimatedHit * 4.toDouble(), true) + } + } + + override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { + return style.swingHandler.calculateDefence(victim, attacker) + } + + override fun getSetMultiplier(e: Entity?, skillId: Int): Double { + return style.swingHandler.getSetMultiplier(e, skillId) + } + + /** + * Sets the local style + * @param e The entity + */ + private fun checkStyle(e: Entity?) { + val index = e!!.properties.attackStyle.style + style = when(index) { + WeaponInterface.STYLE_DEFENSIVE_CAST -> CombatStyle.MAGIC + WeaponInterface.STYLE_RANGE_ACCURATE -> CombatStyle.RANGE + else -> CombatStyle.MELEE + } + } + + companion object { + /** + * The instance for the swing handler. + */ + val INSTANCE = SalamanderSwingHandler(CombatStyle.RANGE) + + /** + * Gets the id of the tar used by a salamander. + * @param id The salamander id. + * @return The tar id. + */ + fun getAmmoId(id: Int): Int { + when (id) { + Items.SWAMP_LIZARD_10149 -> { + return Items.GUAM_TAR_10142 + } + Items.ORANGE_SALAMANDER_10146 -> { + return Items.MARRENTILL_TAR_10143 + } + Items.RED_SALAMANDER_10147 -> { + return Items.TARROMIN_TAR_10144 + } + Items.BLACK_SALAMANDER_10148 -> { + return Items.HARRALANDER_TAR_10145 + } + else -> { + return -1 + } + } + } + + /** + * Gets the magic damage bonus of a salamander. + * @param id The salamander id. + * @return The magic damage bonus. + */ + fun getSalamanderMagicDamageBonus(id: Int): Int { + when (id) { + Items.SWAMP_LIZARD_10149 -> { + return 56 + } + Items.ORANGE_SALAMANDER_10146 -> { + return 59 + } + Items.RED_SALAMANDER_10147 -> { + return 77 + } + Items.BLACK_SALAMANDER_10148 -> { + return 92 + } + else -> { + return 0 + } + } + } + + /** + * Checks if the entity has the ammunition needed to proceed. + * @param e The entity. + * @param state The battle state. + * @return `True` if so. + */ + fun hasAmmo(e: Entity?, state: BattleState?): Boolean { + if (e !is Player) { + return true + } + val ammo = getItemFromEquipment(e, EquipmentSlot.AMMO) + if (ammo == null) { + e.packetDispatch.sendMessage("You do not have enough ammo left.") + return false + } + if (ammo.id == (getAmmoId(state!!.weapon.id))) { + return true + } + e.packetDispatch.sendMessage("You can't use this type of ammunition with this salamander.") + return false + } + + /** + * Uses the ammunition for the range weapon. + * @param e The entity. + * @param state The battle state. + * @param location The drop location. + */ + fun useAmmo(e: Entity, state: BattleState) { + if (e !is Player) { + return + } + e.equipment.remove(Item(getAmmoId(state!!.weapon.id), 1)) + } + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/combat/special/SaradominsLightningHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SaradominsLightningHandler.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/combat/special/SaradominsLightningHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/SaradominsLightningHandler.java index c075825e5..9e672f4db 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/SaradominsLightningHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SaradominsLightningHandler.java @@ -1,17 +1,19 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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 new file mode 100644 index 000000000..b4a374d3e --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/SeercullSpecialHandler.java @@ -0,0 +1,90 @@ +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; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.RangeSwingHandler; +import core.game.node.entity.impl.Projectile; +import core.game.node.entity.player.Player; +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 + * level. + * @author Splinter + * @version 1.0 + */ +@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. + */ + private static final int SPECIAL_ENERGY = 100; + + /** + * The attack animation. + */ + private static final Graphics DRAWBACK_GFX = new Graphics(472, 96); + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.RANGE.getSwingHandler().register(6724, 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; + } + 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; + } + + @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); + Projectile.create(entity, victim, 473, 40, 40, 40, speed, 15, 11).send(); + } + + @Override + public void impact(final Entity entity, final Entity victim, final BattleState state) { + int hit = state.getEstimatedHit(); + victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.RANGE, state); + } +} diff --git a/Server/src/main/java/core/game/node/entity/combat/special/SeverSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SeverSpecialHandler.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/combat/special/SeverSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/SeverSpecialHandler.java index be63aa08d..eb8419c10 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/SeverSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SeverSpecialHandler.java @@ -1,9 +1,9 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.prayer.PrayerType; @@ -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/java/core/game/node/entity/combat/special/ShatterSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ShatterSpecialHandler.java similarity index 80% rename from Server/src/main/java/core/game/node/entity/combat/special/ShatterSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/ShatterSpecialHandler.java index 40647f3c1..1b226c390 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/ShatterSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ShatterSpecialHandler.java @@ -1,17 +1,19 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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/java/core/game/node/entity/combat/special/ShoveSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ShoveSpecialHandler.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/combat/special/ShoveSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/ShoveSpecialHandler.java index ae67f2a0c..12d92744b 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/ShoveSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ShoveSpecialHandler.java @@ -1,12 +1,11 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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 new file mode 100644 index 000000000..9f755280c --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/SliceAndDiceSpecialHandler.java @@ -0,0 +1,111 @@ +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.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; +import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; + +/** + * Handles the Dragon claws special attack "Slice and Dice". + * @author Emperor + * + */ +@Initializable +public final class SliceAndDiceSpecialHandler extends MeleeSwingHandler implements Plugin { + + /** + * The special energy required. + */ + private static final int SPECIAL_ENERGY = 50; + + /** + * The attack animation. + */ + private static final Animation ANIMATION = new Animation(10961, Priority.HIGH); + + /** + * The graphic. + */ + private static final Graphics GRAPHIC = new Graphics(1950); + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.MELEE.getSwingHandler().register(14484, this); + CombatStyle.MELEE.getSwingHandler().register(14486, this); + return this; + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { + return -1; + } + int maximum = calculateHit(entity, victim, 1.0); + 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) / 2 + 1}; + } else { + hit = getHit(entity, victim, maximum * 7 / 8, maximum * 3 / 8); + if (hit > 0) { + hits = new int[] {0, hit, hit / 2, hit / 2 + 1}; + } else { + hit = getHit(entity, victim, maximum * 3 / 4, maximum / 4); + if (hit > 0) { + hits = new int[] {0, 0, hit, hit + 1}; + } else { + hit = getHit(entity, victim, maximum * 5 / 4, maximum / 4); + if (hit > 0) { + hits = new int[] {0, 0, 0, hit}; + } else { + hit = RandomFunction.random(2); + hits = new int[] {0, 0, hit, hit}; + } + } + } + } + BattleState[] states = new BattleState[hits.length]; + for (int i = 0; i < hits.length; i++) { + BattleState s = states[i] = new BattleState(entity, victim); + s.setStyle(CombatStyle.MELEE); + s.setEstimatedHit(hits[i]); + } + state.setTargets(states); + return 1; + } + + /** + * Gets the current hit. + * @param entity The attacking entity. + * @param victim The victim. + * @param maximum The maximum hit. + * @return The hit. + */ + 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/java/core/game/node/entity/combat/special/SmashSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SmashSpecialHandler.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/combat/special/SmashSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/SmashSpecialHandler.java index 10073e108..ba4989eda 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/SmashSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SmashSpecialHandler.java @@ -1,17 +1,20 @@ -package core.game.node.entity.combat.special; +package content.global.handlers.item.equipment.special; import core.plugin.Initializable; 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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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.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/java/core/game/node/entity/combat/special/SnapshotSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SnapshotSpecialHandler.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/combat/special/SnapshotSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/SnapshotSpecialHandler.java index b7fc1b0c3..618a0f4b2 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/SnapshotSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SnapshotSpecialHandler.java @@ -1,8 +1,9 @@ -package core.game.node.entity.combat.special; +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; @@ -12,8 +13,11 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; -import rs09.game.node.entity.combat.handlers.RangeSwingHandler; -import rs09.game.world.GameWorld; +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 rs09.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/java/core/game/node/entity/combat/special/SnipeSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SnipeSpecialHandler.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/combat/special/SnipeSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/SnipeSpecialHandler.java index b1fbe0f2c..4d5b66609 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/SnipeSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SnipeSpecialHandler.java @@ -1,4 +1,4 @@ -package core.game.node.entity.combat.special; +package content.global.handlers.item.equipment.special; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -11,7 +11,10 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; -import rs09.game.node.entity.combat.handlers.RangeSwingHandler; +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/java/core/game/node/entity/combat/special/SpearWallSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/combat/special/SpearWallSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java index 306924202..2595b0479 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/SpearWallSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SpearWallSpecialHandler.java @@ -1,4 +1,4 @@ -package core.game.node.entity.combat.special; +package content.global.handlers.item.equipment.special; import java.util.List; @@ -6,7 +6,7 @@ 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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -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/java/core/game/node/entity/combat/special/SweepSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/combat/special/SweepSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java index be1d5d2b3..6c4b9cedc 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/SweepSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/SweepSpecialHandler.java @@ -1,14 +1,14 @@ -package core.game.node.entity.combat.special; +package content.global.handlers.item.equipment.special; import java.util.ArrayList; import java.util.List; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; +import content.global.skill.summoning.familiar.Familiar; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -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/java/core/game/node/entity/combat/special/WarstrikeSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/WarstrikeSpecialHandler.java similarity index 77% rename from Server/src/main/java/core/game/node/entity/combat/special/WarstrikeSpecialHandler.java rename to Server/src/main/content/global/handlers/item/equipment/special/WarstrikeSpecialHandler.java index ea19448a8..177769d5d 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/WarstrikeSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/WarstrikeSpecialHandler.java @@ -1,18 +1,20 @@ -package core.game.node.entity.combat.special; +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 rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +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 new file mode 100644 index 000000000..b8f45d90f --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/WeakenSpecialHandler.java @@ -0,0 +1,81 @@ +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.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. + * @author Crash, based on Emperor's code for other special attacks + */ +@Initializable +public final class WeakenSpecialHandler extends MeleeSwingHandler implements Plugin { + + /** + * The special energy required. + */ + private static final int SPECIAL_ENERGY = 50; + + /** + * The attack animation. + */ + private static final Animation ANIMATION = new Animation(2890, Priority.HIGH); + + /** + * The graphic. + */ + private static final Graphics GRAPHIC = new Graphics(483); + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.MELEE.getSwingHandler().register(6746, this); + return this; + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { + return -1; + } + 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) + 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); + return hit; + } + + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + entity.visualize(ANIMATION, GRAPHIC); + playGlobalAudio(entity.getLocation(), Sounds.DARKLIGHT_WEAKEN_225); + } +} diff --git a/Server/src/main/java/core/game/interaction/item/toys/DiangoOptionHandler.java b/Server/src/main/content/global/handlers/item/toys/DiangoOptionHandler.java similarity index 93% rename from Server/src/main/java/core/game/interaction/item/toys/DiangoOptionHandler.java rename to Server/src/main/content/global/handlers/item/toys/DiangoOptionHandler.java index 16ac99e47..545aa25b7 100644 --- a/Server/src/main/java/core/game/interaction/item/toys/DiangoOptionHandler.java +++ b/Server/src/main/content/global/handlers/item/toys/DiangoOptionHandler.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.toys; +package content.global.handlers.item.toys; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java b/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java new file mode 100644 index 000000000..57f51641e --- /dev/null +++ b/Server/src/main/content/global/handlers/item/toys/DiangoReclaimInterface.java @@ -0,0 +1,100 @@ +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; +import core.game.container.access.InterfaceContainer; +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; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import static core.api.ContentAPIKt.inEquipment; +import static core.api.ContentAPIKt.inInventory; + + +/** + * Handles diango's item reclaiming interface + * @author ceik, for the interface, and for most of the holiday items/toys + */ +@Initializable +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.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 + public Plugin newInstance(Object arg) throws Throwable { + ComponentDefinition.put(COMPONENT_ID,this); + ITEMS.addAll(Arrays.asList(HOLIDAY_ITEMS)); + return this; + } + + public static void open(Player player){ + //close any currently open interfaces + Component curOpen = player.getInterfaceManager().getOpened(); + 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, POH, inventory, or equipped + + //only send items if there are some to send + if(reclaimables.length > 0) { + InterfaceContainer.generateItems(player, reclaimables, new String[]{"Examine", "Take"}, 468, 2, 8, 8); + } + //open the interface + player.getInterfaceManager().open(new Component(468)); + } + + //refresh the interface + public static void refresh(Player player){ + player.getInterfaceManager().close(); + open(player); + } + + @Override + public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { + 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(reclaimItem); + refresh(player); + break; + case 196: //interface item option 2 == examine + //send the examine text for the item to the player + 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/toys/SnowGlobePlugin.java b/Server/src/main/content/global/handlers/item/toys/SnowGlobePlugin.java new file mode 100644 index 000000000..7061980fa --- /dev/null +++ b/Server/src/main/content/global/handlers/item/toys/SnowGlobePlugin.java @@ -0,0 +1,62 @@ +package content.global.handlers.item.toys; + +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.node.item.Item; +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.Initializable; +import core.plugin.Plugin; +import core.plugin.ClassScanner; + +@Initializable +public class SnowGlobePlugin extends OptionHandler { + private static final Component INTERFACE = new Component(659);//After HOLDFACE this interface is displayed, player either clicks 'continue' for inv of snowballs, or 'close' for no snowballs + private final Animation DOWNFAST = new Animation(7537);//Used when player hit 'close' on the interface + private final Animation DOWNSLOW = new Animation(7538);//Used when the player hit 'continue' on the interface + private final Animation STOMP = new Animation(7528);//When player hits continue this animation plays + private final Graphics SNOW = new Graphics(1284);//When Animation STOMP is playing this gfx also plays + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ClassScanner.definePlugin(new SnowGlobeInterface()); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + return true; + } + + public class SnowGlobeInterface extends ComponentPlugin{ + @Override + public Plugin newInstance(Object arg) throws Throwable { + ComponentDefinition.put(INTERFACE.getId(),this); + return this; + } + + @Override + public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { + if(button == 2){ + player.getInterfaceManager().close(); + player.getAnimator().animate(DOWNSLOW); + player.getPulseManager().run(new Pulse(1) { + @Override + public boolean pulse() { + player.getAnimator().animate(STOMP,SNOW); + player.getInventory().add(new Item(11951,player.getInventory().freeSlots())); + return true; + } + }); + return true; + } + player.getAnimator().animate(DOWNFAST); + return true; + } + } +} 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/kotlin/rs09/game/interaction/item/withitem/CapeDyer.kt b/Server/src/main/content/global/handlers/item/withitem/CapeDyer.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/interaction/item/withitem/CapeDyer.kt rename to Server/src/main/content/global/handlers/item/withitem/CapeDyer.kt index 1f3df32a3..941b993a7 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withitem/CapeDyer.kt +++ b/Server/src/main/content/global/handlers/item/withitem/CapeDyer.kt @@ -1,6 +1,6 @@ -package rs09.game.interaction.item.withitem +package content.global.handlers.item.withitem -import core.game.content.global.Dyes +import content.data.Dyes import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler import core.game.node.item.Item @@ -13,7 +13,7 @@ private val DYES = Dyes.values().map { it -> it.item.id }.toIntArray() @Initializable //For the super constructor we have to use all but the very last cape because its ID is higher than the ID of all dyes -class CapeDyer : UseWithHandler(*CAPES.copyOfRange(0,CAPES.size - 2).toIntArray()) { +class CapeDyer : UseWithHandler(*CAPES.copyOfRange(0,CAPES.size - 1).toIntArray()) { override fun newInstance(arg: Any?): Plugin { for(dye in DYES){ addHandler(dye, ITEM_TYPE,this) @@ -50,7 +50,7 @@ class CapeDyer : UseWithHandler(*CAPES.copyOfRange(0,CAPES.size - 2).toIntArray( return true } - internal enum class Cape(val dye : Dyes,val product : Item) { + internal enum class Cape(val dye : Dyes, val product : Item) { BLACK(Dyes.BLACK, Item(1019)), RED(Dyes.RED, Item(1007)), BLUE(Dyes.BLUE, Item(1021)), diff --git a/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt b/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt new file mode 100644 index 000000000..15d12ca62 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/CrystalKeyCreateListener.kt @@ -0,0 +1,35 @@ +package content.global.handlers.item.withitem + +import core.api.addItem +import core.api.removeItem +import core.api.sendMessage +import org.rs09.consts.Items +import core.game.interaction.IntType +import core.game.interaction.InteractionListener + +/** + * Listener for creating a crystal key + * @author Byte + */ +@Suppress("unused") +class CrystalKeyCreateListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.LOOP_HALF_OF_A_KEY_987, Items.TOOTH_HALF_OF_A_KEY_985) { player, used, with -> + if (!removeItem(player, used)) { + return@onUseWith false + } + + if (!removeItem(player, with)) { + return@onUseWith false + } + + 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/java/core/game/interaction/item/withitem/DarkBowDyePlugin.java b/Server/src/main/content/global/handlers/item/withitem/DarkBowDyePlugin.java similarity index 94% rename from Server/src/main/java/core/game/interaction/item/withitem/DarkBowDyePlugin.java rename to Server/src/main/content/global/handlers/item/withitem/DarkBowDyePlugin.java index 881f75e7c..548b236bc 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/DarkBowDyePlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/DarkBowDyePlugin.java @@ -1,12 +1,12 @@ -package core.game.interaction.item.withitem; -import core.game.content.dialogue.DialogueAction; +package content.global.handlers.item.withitem; +import core.game.dialogue.DialogueAction; 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.Plugin; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * The plugin used to dye a dark bow into a more one. @@ -30,7 +30,7 @@ public final class DarkBowDyePlugin extends UseWithHandler { addHandler(14797, ITEM_TYPE, this); addHandler(14799, ITEM_TYPE, this); addHandler(14801, ITEM_TYPE, this); - PluginManager.definePlugin(new DarkBowCleanPlugin()); + ClassScanner.definePlugin(new DarkBowCleanPlugin()); return this; } diff --git a/Server/src/main/java/core/game/interaction/item/withitem/FirelighterPlugin.java b/Server/src/main/content/global/handlers/item/withitem/FirelighterPlugin.java similarity index 98% rename from Server/src/main/java/core/game/interaction/item/withitem/FirelighterPlugin.java rename to Server/src/main/content/global/handlers/item/withitem/FirelighterPlugin.java index 9042a0778..4e088bee3 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/FirelighterPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/FirelighterPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/java/core/game/interaction/item/withitem/FishfoodPlugin.java b/Server/src/main/content/global/handlers/item/withitem/FishfoodPlugin.java similarity index 98% rename from Server/src/main/java/core/game/interaction/item/withitem/FishfoodPlugin.java rename to Server/src/main/content/global/handlers/item/withitem/FishfoodPlugin.java index 41dc8439c..a73f3cf66 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/FishfoodPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/FishfoodPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import org.rs09.consts.Items; import core.game.interaction.NodeUsageEvent; 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/java/core/game/interaction/item/withitem/GodswordHiltAttachPlugin.java b/Server/src/main/content/global/handlers/item/withitem/GodswordHiltAttachPlugin.java similarity index 90% rename from Server/src/main/java/core/game/interaction/item/withitem/GodswordHiltAttachPlugin.java rename to Server/src/main/content/global/handlers/item/withitem/GodswordHiltAttachPlugin.java index 67804f9a1..2e0522b3c 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/GodswordHiltAttachPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/GodswordHiltAttachPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -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/java/core/game/interaction/item/withitem/GraniteMaulPlugin.java b/Server/src/main/content/global/handlers/item/withitem/GraniteMaulPlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/item/withitem/GraniteMaulPlugin.java rename to Server/src/main/content/global/handlers/item/withitem/GraniteMaulPlugin.java index b2cddcb7a..6ba753d2f 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/GraniteMaulPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/GraniteMaulPlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.cache.def.impl.ItemDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueAction; +import core.game.dialogue.DialogueAction; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -10,7 +10,7 @@ import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * The plugin used to make the granite maul into the ornamental version. @@ -30,7 +30,7 @@ public final class GraniteMaulPlugin extends UseWithHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new GraniteMaulRevertHandler()); + ClassScanner.definePlugin(new GraniteMaulRevertHandler()); addHandler(14793, ITEM_TYPE, this); return this; } diff --git a/Server/src/main/java/core/game/interaction/item/withitem/GraniteSplittingPlugin.java b/Server/src/main/content/global/handlers/item/withitem/GraniteSplittingPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/withitem/GraniteSplittingPlugin.java rename to Server/src/main/content/global/handlers/item/withitem/GraniteSplittingPlugin.java index 46bccd928..951bb6ba9 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/GraniteSplittingPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/GraniteSplittingPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; 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/HaySackOnSpear.kt b/Server/src/main/content/global/handlers/item/withitem/HaySackOnSpear.kt new file mode 100644 index 000000000..a954ca114 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/HaySackOnSpear.kt @@ -0,0 +1,23 @@ +package content.global.handlers.item.withitem + +import core.api.Container +import core.api.* +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class HaySackOnSpear : InteractionListener { + val HAYSACK = Items.HAY_SACK_6057 + val SPEAR = Items.BRONZE_SPEAR_1237 + + override fun defineListeners() { + onUseWith(IntType.ITEM, HAYSACK, SPEAR){ player, used, _ -> + if(removeItem(player, used.asItem(), Container.INVENTORY)){ + addItem(player, Items.HAY_SACK_6058, 1) + sendMessage(player, "You stab the hay sack with a bronze spear") + removeItem(player, SPEAR, Container.INVENTORY) + } + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/item/withitem/ImpJarCreatePlugin.java b/Server/src/main/content/global/handlers/item/withitem/ImpJarCreatePlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/item/withitem/ImpJarCreatePlugin.java rename to Server/src/main/content/global/handlers/item/withitem/ImpJarCreatePlugin.java index 30b90a001..e6d61a6be 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/ImpJarCreatePlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/ImpJarCreatePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -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/java/core/game/interaction/item/withitem/KaramjanSilkPlugin.java b/Server/src/main/content/global/handlers/item/withitem/KaramjanSilkPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/item/withitem/KaramjanSilkPlugin.java rename to Server/src/main/content/global/handlers/item/withitem/KaramjanSilkPlugin.java index f0441e4c3..5c6154d21 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/KaramjanSilkPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/KaramjanSilkPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/java/core/game/interaction/item/withitem/LavaScalePlugin.java b/Server/src/main/content/global/handlers/item/withitem/LavaScalePlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/item/withitem/LavaScalePlugin.java rename to Server/src/main/content/global/handlers/item/withitem/LavaScalePlugin.java index e445ef51c..656bd3607 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/LavaScalePlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/LavaScalePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.node.entity.skill.Skills; import core.game.interaction.NodeUsageEvent; @@ -7,7 +7,7 @@ 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; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.plugin.Initializable; import core.tools.RandomFunction; @@ -29,7 +29,7 @@ public final class LavaScalePlugin extends UseWithHandler { @Override public Plugin newInstance(Object arg) throws Throwable { addHandler(233, ITEM_TYPE, this); - PluginManager.definePlugin(new AntifireMakePlugin()); + ClassScanner.definePlugin(new AntifireMakePlugin()); return this; } diff --git a/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt b/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt new file mode 100644 index 000000000..318f8a0a8 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/OilFishingRodListener.kt @@ -0,0 +1,52 @@ +package content.global.handlers.item.withitem + +import core.api.* +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 + +class OilFishingRodListener : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.BLAMISH_OIL_1582, Items.FISHING_ROD_307) { player, used, with -> + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 1 -> { + 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 + } + }) + return@onUseWith true + } + + onUseWith(IntType.ITEM, Items.THIN_SNAIL_3363, Items.PESTLE_AND_MORTAR_233) { player, used, with -> + if (player.inventory.contains(Items.SAMPLE_BOTTLE_3377, 1)) { + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 0 -> player.animator.animate(Animation(364)) + 3 -> { + if (removeItem(player, Items.THIN_SNAIL_3363) && + removeItem(player, Items.SAMPLE_BOTTLE_3377)) { + addItem(player, Items.BLAMISH_SNAIL_SLIME_1581) + } + } + } + return false + } + }) + } + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withitem/PoisonedWeaponListeners.kt b/Server/src/main/content/global/handlers/item/withitem/PoisonedWeaponListeners.kt new file mode 100644 index 000000000..c3b6816c4 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/PoisonedWeaponListeners.kt @@ -0,0 +1,113 @@ +package content.global.handlers.item.withitem + +import core.api.* +import core.game.node.item.Item +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import kotlin.collections.toIntArray +import kotlin.math.min + +class PoisonedWeaponListeners : InteractionListener { + override fun defineListeners() { + val poisons = intArrayOf(Items.WEAPON_POISON_187, Items.WEAPON_POISON_PLUS_5937, Items.WEAPON_POISON_PLUS_PLUS_5940) + val poisonableItems = PoisonSets.itemMap.keys.toIntArray() + val poisonedItems = PoisonSets.itemMap.values.toIntArray() + + onUseWith(IntType.ITEM, poisons, *poisonableItems){ player, used, with -> + val index = poisons.indexOf(used.id) + val product = PoisonSets.itemMap[with.id]!![index] + val amt = min(5, with.asItem().amount) + + if(removeItem(player, Item(with.id, amt))) { + removeItem(player, used.id) //Mico's Code. See: https://gitlab.com/skelsoft/redwings/-/issues/13 + addItemOrDrop(player, product, amt) + addItemOrDrop(player, Items.VIAL_229) + sendMessage(player, "You poison the ${with.name.toLowerCase()}.") + } + return@onUseWith true + } + + onUseWith(IntType.ITEM, Items.CLEANING_CLOTH_3188, *poisonedItems) { player, _, with -> + val base = PoisonSets.getBase(with.id) ?: return@onUseWith false + val amt = min(5, with.asItem().amount) + removeItem(player, Item(with.id, amt)) + addItemOrDrop(player, base, amt) + return@onUseWith true + } + } + + //below enum copied from old file, some data is probably wrong. + internal enum class PoisonSets(val base: Int, val p: Int, val pp: Int, val ppp: Int){ + BRONZE_ARROW(882, 883, 5616, 5622), + IRON_ARROW(884, 885, 5617, 5623), + STEEL_ARROW(886, 887, 5618, 5624), + MITHRIL_ARROW(888, 889, 5619, 5625), + ADAMANT_ARROW(890, 891, Items.ADAMANT_ARROWP_PLUS_5620, 5626), + RUNE_ARROW(892, 893, 5621, 5627), + + IRON_KNIFE(863, 871, 5655, 5662), + BRONZE_KNIFE(864, 870, 5654, 5661), + STEEL_KNIFE(865, 872, 5656, 5663), + MITHRIL_KNIFE(866, 873, 5657, 5664), + ADAMANT_KNIFE(867, 875, 5659, 5666), + RUNE_KNIFE(868, 876, 5660, 5667), + BLACK_KNIFE(869, 874, 5658, 5665), + + BRONZE_DART(806, 812, 5628, 5635), + IRON_DART(807, 813, 5629, 5636), + STEEL_DART(808, 814, 5630, 5637), + BLACK_DART(3093, 3094, 5631, 5638), + 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), + STEEL_JAVELIN(827, 833, Items.STEEL_JAVELINP_PLUS_5644, 5650), + 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), + STEEL_DAGGER(1207, 1223, 5672, 5690), + MITHRIL_DAGGER(1209, 1225, 5674, 5692), + ADAMANT_DAGGER(1211, 1227, 5676, 5694), + 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), + STEEL_SPEAR(1241, 1255, 5708, 5722), + MITHRIL_SPEAR(1243, 1257, 5710, 5724), + ADAMANT_SPEAR(1245, 1259, 5712, 5726), + RUNE_SPEAR(1247, 1261, 5714, 5728), + DRAGON_SPEAR(1249, 1263, 5716, 5730), + BLACK_SPEAR(4580, 4582, 5734, 5736); + + companion object { + val itemMap = values().map { it.base to intArrayOf(it.p,it.pp,it.ppp) }.toMap() + + fun getBase(poisoned: Int) : Int? { + for ((base,set) in itemMap.entries) { + if (set.contains(poisoned)) return base + } + return null + } + } + } +} diff --git a/Server/src/main/java/core/game/interaction/item/withitem/SeasonedSardinePlugin.java b/Server/src/main/content/global/handlers/item/withitem/SeasonedSardinePlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/item/withitem/SeasonedSardinePlugin.java rename to Server/src/main/content/global/handlers/item/withitem/SeasonedSardinePlugin.java index d27da8c40..a1096660e 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/SeasonedSardinePlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/SeasonedSardinePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/java/core/game/interaction/item/withitem/SkullSceptrePlugin.java b/Server/src/main/content/global/handlers/item/withitem/SkullSceptrePlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/withitem/SkullSceptrePlugin.java rename to Server/src/main/content/global/handlers/item/withitem/SkullSceptrePlugin.java index 450933140..e7b8c6a49 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/SkullSceptrePlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/SkullSceptrePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/java/core/game/interaction/item/withitem/SoftclayPlugin.java b/Server/src/main/content/global/handlers/item/withitem/SoftclayPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/item/withitem/SoftclayPlugin.java rename to Server/src/main/content/global/handlers/item/withitem/SoftclayPlugin.java index e9c9162c9..37ff46584 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/SoftclayPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/SoftclayPlugin.java @@ -1,10 +1,10 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.node.entity.player.link.diary.DiaryType; import core.plugin.Initializable; import org.rs09.consts.Items; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; +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; diff --git a/Server/src/main/content/global/handlers/item/withitem/TOTTHelmOnCape.kt b/Server/src/main/content/global/handlers/item/withitem/TOTTHelmOnCape.kt new file mode 100644 index 000000000..eabd9ee37 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/TOTTHelmOnCape.kt @@ -0,0 +1,42 @@ +package content.global.handlers.item.withitem + +import core.api.Container +import core.api.* +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.tools.END_DIALOGUE + +class TOTTHelmOnCape : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.SLAYER_HELMET_13263, Items.SLAYER_CAPE_9786, Items.SLAYER_CAPET_9787){ player, used, with -> + val alreadyHasHelm = getAttribute(player, "cape_perks:tott:helmet-stored", false) + + if(alreadyHasHelm){ + sendDialogue(player, "You've already stored the components of a helmet in this cape.") + } else { + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> dialogue("This will destroy your helmet permanently.").also { stage++ } + 1 -> interpreter!!.sendOptions("Continue?", "Yes", "No").also { stage++ } + 2 -> when(buttonID){ + 1 -> { + if(removeItem(player, Items.SLAYER_HELMET_13263, Container.INVENTORY)) { + dialogue(*splitLines("You disassemble the helmet and place the components into their respective pockets on your cape.")) + setAttribute(player, "/save:cape_perks:tott:helmet-stored", true) + stage = END_DIALOGUE + } + } + 2 -> end() + } + } + } + }) + } + + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/item/withitem/WaterSkinPlugin.java b/Server/src/main/content/global/handlers/item/withitem/WaterSkinPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/withitem/WaterSkinPlugin.java rename to Server/src/main/content/global/handlers/item/withitem/WaterSkinPlugin.java index eabcda331..39848ea0a 100644 --- a/Server/src/main/java/core/game/interaction/item/withitem/WaterSkinPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/WaterSkinPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withitem; +package content.global.handlers.item.withitem; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt b/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt new file mode 100644 index 000000000..f332b0590 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/WatermelonOnSack.kt @@ -0,0 +1,27 @@ +package content.global.handlers.item.withitem + +import core.api.Container +import core.api.* +import core.game.node.entity.skill.Skills +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class WatermelonOnSack : InteractionListener { + val SACK = Items.HAY_SACK_6058 + val WATERMELON = Items.WATERMELON_5982 + + override fun defineListeners() { + onUseWith(IntType.ITEM, SACK, WATERMELON){ player, used, _ -> + if(getStatLevel(player, Skills.FARMING) >= 23){ + 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") + } + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/TableDump.kt b/Server/src/main/content/global/handlers/item/withnpc/CatOnArdougneCivilian.kt similarity index 100% rename from Server/src/main/kotlin/rs09/game/system/config/TableDump.kt rename to Server/src/main/content/global/handlers/item/withnpc/CatOnArdougneCivilian.kt diff --git a/Server/src/main/content/global/handlers/item/withnpc/CiderOnForester.kt b/Server/src/main/content/global/handlers/item/withnpc/CiderOnForester.kt new file mode 100644 index 000000000..ab0018b8d --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/CiderOnForester.kt @@ -0,0 +1,57 @@ +package content.global.handlers.item.withnpc + +import core.api.inBorders +import core.api.sendNPCDialogue +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.item.Item +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class CiderOnForester : InteractionListener { + override fun defineListeners() { + val ids = intArrayOf(1,2,3,4,5) + + onUseWith(IntType.NPC, Items.CIDER_5763, *ids){ player, used, with -> + if(inBorders(player, 2689, 3488, 2700, 3498)){ + player.dialogueInterpreter.open(CiderOnForesterDialogue(),with) + return@onUseWith true + } + return@onUseWith false + } + } +} + +class CiderOnForesterDialogue : DialogueFile() { + var init = true + override fun handle(componentID: Int, buttonID: Int) { + + if(init) stage = 0; init = false + + when(stage){ + 0 -> sendNPCDialogue(player!!, this.npc!!.id,"Ah, a glass of cider, Don't mind if I do!").also { stage = 1 } + 1 -> { + sendNPCDialogue(player!!,this.npc!!.id,"Thanks!") + player!!.inventory.remove(Item(Items.CIDER_5763,1)) + stage = 3 + } + 3 -> { + if(player!!.getAttribute("seersCiderPub",-1) == 4){ + player!!.achievementDiaryManager?.finishTask(player,DiaryType.SEERS_VILLAGE,0,6) + player!!.removeAttribute("seersCiderPub") + end() + } + else if(player!!.getAttribute("seersCiderPub",-1) !in 0..3 && !player!!.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE,0,6)){ + player!!.setAttribute("seersCiderPub", 1) + end() + }else if(player!!.getAttribute("seersCiderPub",-1) in 0..3){ + player!!.incrementAttribute("seersCiderPub",1) + end() + }else end() + } + } + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withnpc/FeedFaladorSheepdog.kt b/Server/src/main/content/global/handlers/item/withnpc/FeedFaladorSheepdog.kt new file mode 100644 index 000000000..3d32a2e57 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/FeedFaladorSheepdog.kt @@ -0,0 +1,51 @@ +package content.global.handlers.item.withnpc + +import core.api.* +import content.global.skill.prayer.Bones +import content.data.Meat +import content.data.MeatState +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Animations +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class FeedFaladorSheepdog : InteractionListener { + companion object { + private const val SHEEP_DOG_NPC = NPCs.SHEEPDOG_2311 + private val FEED_ANIMATION = Animation(Animations.HUMAN_BURYING_BONES_827) + private val CONSUMABLE_BONES = Bones.array + .filter { it != Items.BURNT_BONES_528 } + .toHashSet() + private val CONSUMABLE_MEATS = Meat.values() + .filter { it.state == MeatState.INEDIBLE_RAW || it.state == MeatState.EDIBLE_COOKED } + .map { it.id } + .toHashSet() + } + + override fun defineListeners() { + onUseAnyWith(IntType.NPC, SHEEP_DOG_NPC) { player, used, with -> + if (CONSUMABLE_BONES.contains(used.id)) { + if (!removeItem(player, used.asItem())) { + return@onUseAnyWith true + } + sendDialogue(player, "You give the dog some nice ${used.name.toLowerCase()}. It happily gnaws on them.") + } else if (CONSUMABLE_MEATS.contains(used.id)) { + if (!removeItem(player, used.asItem())) { + return@onUseAnyWith true + } + sendDialogue(player, "You give the dog a nice piece of meat. It gobbles it up.") + } else { + sendMessage(player, "The dog doesn't seem interested in that.") + sendChat(with.asNpc(), "Grrrr!") + return@onUseAnyWith true + } + + animate(player, FEED_ANIMATION) + sendChat(with.asNpc(), "Woof woof!") + + return@onUseAnyWith true + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..0b54ccf55 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/GCItemOnCat.kt @@ -0,0 +1,77 @@ +package content.global.handlers.item.withnpc + +import core.api.* +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.map.path.Path +import core.game.world.map.path.Pathfinder +import org.rs09.consts.Items +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 BEND_DOWN = 827 + + onUseWith(IntType.NPC, Items.BUCKET_OF_MILK_1927, NPCs.GERTRUDES_CAT_2997) { player, used, with -> + 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, Quests.GERTRUDES_CAT, 30) + } + return@onUseWith true + } + + onUseWith(IntType.NPC, Items.DOOGLE_SARDINE_1552, NPCs.GERTRUDES_CAT_2997){ player, used, with -> + if(getQuestStage(player, Quests.GERTRUDES_CAT) == 30 && removeItem(player, used.asItem())){ + animate(player, BEND_DOWN) + sendChat(with.asNpc(), "Mew!") + setQuestStage(player, Quests.GERTRUDES_CAT, 40) + } + return@onUseWith true + } + + onUseWith(IntType.NPC, Items.RAW_SARDINE_327, NPCs.GERTRUDES_CAT_2997){ player, _, _ -> + sendMessage(player, "The cat doesn't seem interested in that.") + return@onUseWith true + } + + onUseWith(IntType.NPC, Items.THREE_LITTLE_KITTENS_13236, NPCs.GERTRUDES_CAT_2997){ player, used, with -> + if(removeItem(player, used.asItem())){ + setQuestStage(player, Quests.GERTRUDES_CAT, 60) + //below copied verbatim from original, I don't like it. + Pulser.submit(object : Pulse(1) { + var count = 0 + val kitten = core.game.node.entity.npc.NPC.create(761, player.location) + override fun pulse(): Boolean { + when (count) { + 0 -> { + kitten.init() + kitten.face(with.asNpc()) + with.asNpc().face(kitten) + with.asNpc().sendChat("Pur...") + kitten.sendChat("Pur...") + val path: Path = Pathfinder.find(with.asNpc(), Location(3310, 3510, 1)) + path.walk(with.asNpc()) + val pathh = Pathfinder.find(kitten, Location(3310, 3510, 1)) + pathh.walk(kitten) + } + 5 -> { + kitten.clear() + player.setAttribute("hidefluff", System.currentTimeMillis() + 60000) + } + } + count++ + return count == 6 + } + }) + } + + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/item/withnpc/GertrudeCatUsePlugin.java b/Server/src/main/content/global/handlers/item/withnpc/GertrudeCatUsePlugin.java similarity index 94% rename from Server/src/main/java/core/game/interaction/item/withnpc/GertrudeCatUsePlugin.java rename to Server/src/main/content/global/handlers/item/withnpc/GertrudeCatUsePlugin.java index 1275872c5..efddb3bed 100644 --- a/Server/src/main/java/core/game/interaction/item/withnpc/GertrudeCatUsePlugin.java +++ b/Server/src/main/content/global/handlers/item/withnpc/GertrudeCatUsePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withnpc; +package content.global.handlers.item.withnpc; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -7,13 +7,14 @@ 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 rs09.game.world.GameWorld; +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.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/HatEasterEgg.kt b/Server/src/main/content/global/handlers/item/withnpc/HatEasterEgg.kt new file mode 100644 index 000000000..4e41d8d61 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/HatEasterEgg.kt @@ -0,0 +1,51 @@ +package content.global.handlers.item.withnpc + +import core.game.node.entity.npc.NPC +import core.game.node.item.Item +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +val graphics = 482 +class HatEasterEgg : InteractionListener { + + val MACHINE = 20040 + val WIZ_HAT = Items.WIZARD_HAT_579 + val NEW_HAT = 14650 + + override fun defineListeners() { + onUseWith(IntType.SCENERY,WIZ_HAT,MACHINE){ player, used, with -> + player.dialogueInterpreter.open(HatDialogue(), NPC(872)) + return@onUseWith true + } + } + +} + +class HatDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage++){ + 0 -> { + npc("WHAT HAVE YOU DONE?") + player!!.graphics(Graphics(graphics)) + } + 1 -> player(core.game.dialogue.FacialExpression.AFRAID,"What do you mean?!") + 2 -> npc("You've disjointed the fabric assimilation matrix!") + 3 -> player(core.game.dialogue.FacialExpression.THINKING,"W-what...?") + 4 -> npc("You've put us at risk of ripping Gielinor apart!") + 5 -> player(core.game.dialogue.FacialExpression.HALF_GUILTY,"I.. I just wanted a hat...") + 6 -> npc("Damn you and damn your hat! You could kill us all!") + 7 -> player("I... I'm sorry....") + 8 -> npc("*sigh* I've managed to stabilize the flux material inductors.") + 9 -> npc("You may just be safe, yet.") + 10 -> npc("Here, take your damn hat and get out of here.") + 11 -> { + end() + player!!.inventory.remove(Item(Items.WIZARD_HAT_579)) + player!!.inventory.add(Item(14650)) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withnpc/MistagEasterEgg.kt b/Server/src/main/content/global/handlers/item/withnpc/MistagEasterEgg.kt new file mode 100644 index 000000000..10d7aa15d --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/MistagEasterEgg.kt @@ -0,0 +1,73 @@ +package content.global.handlers.item.withnpc + +import core.game.component.Component +import core.game.node.item.Item +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.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 -> + val alreadyHasRing = player.inventory.contains(ZANIK_RING,1) || player.bank.contains(ZANIK_RING,1) || player.equipment.contains(ZANIK_RING,1) + player.dialogueInterpreter.open(MistagEasterEggDialogue(alreadyHasRing),with.asNpc()) + return@onUseWith true + } + + onEquip(ZANIK_RING){player,_ -> + player.appearance.transformNPC(NPCs.ZANIK_3712) + return@onEquip true + } + + onUnequip(ZANIK_RING){player, _ -> + 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 + } + } +} + +class MistagEasterEggDialogue(val hasRing: Boolean): DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + + if(hasRing){ + npc("Lovely gem, adventurer, but I have nothing for you.").also { stage = END_DIALOGUE } + return + } + + when(stage++){ + 0 -> npc("Well thank you adventurer! Here, take this.") + 1 -> { + end() + if(player!!.inventory.remove(Item(Items.DIAMOND_1601))){ + player!!.inventory.add(Item(14649)) + } + } + } + } + + override fun npc(vararg messages: String?): Component? { + return super.npc(core.game.dialogue.FacialExpression.OLD_HAPPY,*messages) + } +} diff --git a/Server/src/main/content/global/handlers/item/withnpc/PoisonChaliceOnKingArthur.kt b/Server/src/main/content/global/handlers/item/withnpc/PoisonChaliceOnKingArthur.kt new file mode 100644 index 000000000..e8bd4fe63 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/PoisonChaliceOnKingArthur.kt @@ -0,0 +1,52 @@ +package content.global.handlers.item.withnpc + +import core.game.node.item.Item +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 + +class PoisonChaliceOnKingArthur : InteractionListener { + override fun defineListeners() { + onUseWith(IntType.NPC, Items.POISON_CHALICE_197, NPCs.KING_ARTHUR_251) { player, _, with -> + player.dialogueInterpreter.open(PoisonChaliceOnKingArthurDialogue(), with) + return@onUseWith true + } + } +} + +class PoisonChaliceOnKingArthurDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl( + core.game.dialogue.FacialExpression.SAD, + "You have chosen poorly." + ).also { stage++ } + + 1 -> playerl( + core.game.dialogue.FacialExpression.ANNOYED, + "Excuse me?" + ).also { stage++ } + + 2 -> npcl( + core.game.dialogue.FacialExpression.FRIENDLY, + "Sorry, I meant to say 'thank you'. Most refreshing." + ).also { stage++ } + + 3 -> playerl( + core.game.dialogue.FacialExpression.DISGUSTED_HEAD_SHAKE, + "Are you sure that stuff is safe to drink?" + ).also { stage++ } + + 4 -> npcl( + core.game.dialogue.FacialExpression.HAPPY, + "Oh yes, Stankers' creations may be dangerous for those with weak constitutions, but, personally. I find them rather invigorating." + ).also { + player!!.inventory.remove(Item(197, 1)) + stage = END_DIALOGUE + } + } + } +} \ 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 new file mode 100644 index 000000000..3dbd34a6d --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/RopeOnLadyKeli.kt @@ -0,0 +1,27 @@ +package content.global.handlers.item.withnpc + +import core.api.* +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() { + onUseWith(IntType.NPC, Items.ROPE_954, NPCs.LADY_KELI_919) { player, used, with -> + 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, Quests.PRINCE_ALI_RESCUE, 50) + setAttribute(player, "keli-gone", getWorldTicks() + 350) + } + } else { + if (getQuestStage(player, Quests.PRINCE_ALI_RESCUE) == 40){ + sendMessage(player, "You need to do something about the guard first.") + } + } + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withnpc/ZooknockListener.kt b/Server/src/main/content/global/handlers/item/withnpc/ZooknockListener.kt new file mode 100644 index 000000000..297740e60 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withnpc/ZooknockListener.kt @@ -0,0 +1,28 @@ +package content.global.handlers.item.withnpc + +import content.region.misc.apeatoll.dialogue.dungeon.ZooknockDialogueFile +import core.api.openDialogue +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +open class ZooknockListener() : InteractionListener { + val goldBar = Items.GOLD_BAR_2357 + val monkeyAmuletMould = Items.MAMULET_MOULD_4020 + val monkeyDentures = Items.MONKEY_DENTURES_4006 + + + val items = intArrayOf(goldBar, monkeyDentures, monkeyAmuletMould) + + val zooknock = NPCs.ZOOKNOCK_1425 + + val lol = arrayOf(Items) + + override fun defineListeners() { + onUseWith(IntType.NPC, items, zooknock) { + player, used, with -> openDialogue(player, ZooknockDialogueFile(used.id)) + return@onUseWith false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withobject/AmmoMouldOnFurnace.kt b/Server/src/main/content/global/handlers/item/withobject/AmmoMouldOnFurnace.kt new file mode 100644 index 000000000..0d5929d0a --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/AmmoMouldOnFurnace.kt @@ -0,0 +1,89 @@ +package content.global.handlers.item.withobject + +import content.data.Quests +import core.api.* +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +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 + private val levelRequirement = 35 + + private fun cannonBallOnUseWithHandler(player: Player, used: Node, with: Node): Boolean { + face(player, with.centerLocation) + + 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) { + sendDialogue(player,"You need a Smithing level of at least $levelRequirement in order to do this.") + return true + } + if (!inInventory(player, Items.AMMO_MOULD_4)) { + sendDialogue(player,"You need an ammo mould in order to make a cannon ball.") + return true + } + + val cannonBallPulse = object : Pulse() { + private var tick = 0 + var amount = 0 + override fun pulse(): Boolean { + when(tick++){ + 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.") + animate(player, 827) + } + 4 -> { + sendMessage(player,"The molten metal cools slowly to form 4 cannonballs.") + } + 7 -> { + if (removeItem(player, used.asItem())) { + addItem(player, Items.CANNONBALL_2, 4) + rewardXP(player, Skills.SMITHING, 25.6) + } + animate(player, 827) + } + 10 -> { + if (--amount == 0 || !inInventory(player, Items.STEEL_BAR_2353)) { + return true + } + tick = 0 + } + } + return false + } + } + + val itemUsed = used.asItem() + + val dialogue: SkillDialogueHandler = object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, itemUsed) { + override fun create(amount: Int, index: Int) { + cannonBallPulse.amount = amount + submitIndividualPulse(player, cannonBallPulse) + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, itemUsed.id) + } + } + openDialogue(player, dialogue) + return true + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.STEEL_BAR_2353, *furnaces, handler = ::cannonBallOnUseWithHandler) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withobject/AxeOnTree.kt b/Server/src/main/content/global/handlers/item/withobject/AxeOnTree.kt new file mode 100644 index 000000000..b9e89f6e4 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/AxeOnTree.kt @@ -0,0 +1,20 @@ +package content.global.handlers.item.withobject + +import core.api.* +import content.global.skill.gather.woodcutting.WoodcuttingNode +import content.global.skill.gather.woodcutting.WoodcuttingSkillPulse +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class AxeOnTree : InteractionListener { + val axes = intArrayOf(Items.BRONZE_AXE_1351, Items.MITHRIL_AXE_1355, Items.IRON_AXE_1349, Items.BLACK_AXE_1361, Items.STEEL_AXE_1353, Items.ADAMANT_AXE_1357, Items.RUNE_AXE_1359, Items.DRAGON_AXE_6739, Items.INFERNO_ADZE_13661) + val trees = WoodcuttingNode.values().map { it.id }.toIntArray() + + override fun defineListeners() { + onUseWith(IntType.SCENERY, axes, *trees){ player, _, with -> + submitIndividualPulse(player, WoodcuttingSkillPulse(player, with.asScenery())) + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withobject/CauldronOfThunderListener.kt b/Server/src/main/content/global/handlers/item/withobject/CauldronOfThunderListener.kt new file mode 100644 index 000000000..d33edf37a --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/CauldronOfThunderListener.kt @@ -0,0 +1,45 @@ +package content.global.handlers.item.withobject + +import core.api.addItem +import core.api.animate +import core.api.removeItem +import core.api.sendMessage +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 +import java.util.* + +/** + * Listener for Cauldron of Thunder interaction + * @author Byte + */ +@Suppress("unused") +class CauldronOfThunderListener : InteractionListener { + + companion object { + private val ANIMATION = Animation(833) + + private val ITEM_MAP = mapOf( + Items.RAW_BEEF_2132 to Items.ENCHANTED_BEEF_522, + Items.RAW_RAT_MEAT_2134 to Items.ENCHANTED_RAT_MEAT_523, + Items.RAW_BEAR_MEAT_2136 to Items.ENCHANTED_BEAR_MEAT_524, + Items.RAW_CHICKEN_2138 to Items.ENCHANTED_CHICKEN_525 + ) + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, ITEM_MAP.keys.toIntArray(), Scenery.CAULDRON_OF_THUNDER_2142) { player, used, _ -> + if (!removeItem(player, used)) { + return@onUseWith false + } + + animate(player, ANIMATION) + addItem(player, ITEM_MAP[used.id]!!) + sendMessage(player, "You dip the " + used.name.lowercase(Locale.getDefault()) + " in the cauldron.") + + return@onUseWith true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/withobject/ChestKeyListener.kt b/Server/src/main/content/global/handlers/item/withobject/ChestKeyListener.kt new file mode 100644 index 000000000..a64638f3f --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/ChestKeyListener.kt @@ -0,0 +1,35 @@ +package content.global.handlers.item.withobject + +import core.api.addItem +import core.api.removeItem +import core.api.replaceScenery +import core.api.sendMessage +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.game.interaction.InteractionListener + +/** + * Listener for using key on pirate chest in Blue Moon Inn + * @author Byte + */ +@Suppress("unused") +class ChestKeyListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.CHEST_KEY_432, Scenery.CHEST_2079) { player, used, with -> + if (!removeItem(player, used)) { + return@onUseWith false + } + + replaceScenery(with as core.game.node.scenery.Scenery, 2080, 3) + + addItem(player, Items.PIRATE_MESSAGE_433) + sendMessage(player, "You unlock the chest.") + sendMessage(player, "All that's in the chest is a message...") + sendMessage(player, "You take the message from the chest.") + + return@onUseWith true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/withobject/CoalTruckListener.kt b/Server/src/main/content/global/handlers/item/withobject/CoalTruckListener.kt new file mode 100644 index 000000000..c2fef6e3c --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/CoalTruckListener.kt @@ -0,0 +1,76 @@ +package content.global.handlers.item.withobject + +import core.api.* +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Handles coal truck interactions + * @author ceik + * @author vddCore + */ +class CoalTruckListener : InteractionListener { + companion object { + private const val ATTRIBUTE_COAL_TRUCK_INVENTORY = "coal-truck-inventory" + } + + override fun defineListeners() { + on(Scenery.COAL_TRUCK_2114, IntType.SCENERY, "remove-coal") { player, node -> + var coalInTruck = getAttribute(player, ATTRIBUTE_COAL_TRUCK_INVENTORY, 0) + + if (coalInTruck == 0) { + sendDialogue(player, "The coal truck is empty.") + return@on true + } + + var toRemove = freeSlots(player) + + if (toRemove > coalInTruck) { + toRemove = coalInTruck + } + + if (addItem(player, Items.COAL_453, toRemove)) { + coalInTruck -= toRemove + setAttribute(player, "/save:$ATTRIBUTE_COAL_TRUCK_INVENTORY", coalInTruck) + } + + return@on true + } + + on(Scenery.COAL_TRUCK_2114, IntType.SCENERY, "investigate") { player, _ -> + val coalInTruck = getAttribute(player, ATTRIBUTE_COAL_TRUCK_INVENTORY, 0) + + sendDialogue( + player, + "There is currently $coalInTruck coal in the truck. " + + "The truck has space for " + (120 - coalInTruck) + " more coal." + ) + return@on true + } + + onUseWith(IntType.SCENERY, Items.COAL_453, Scenery.COAL_TRUCK_2114) { player, _, _ -> + var coalInTruck = getAttribute(player, ATTRIBUTE_COAL_TRUCK_INVENTORY, 0) + var coalInInventory = amountInInventory(player, Items.COAL_453) + + if (coalInInventory + coalInTruck >= 120) { + coalInInventory = 120 - coalInTruck + sendMessage(player, "You have filled up the coal truck.") + } + + if (removeItem(player, Item(Items.COAL_453, coalInInventory))) { + coalInTruck += coalInInventory + + setAttribute( + player, + "/save:$ATTRIBUTE_COAL_TRUCK_INVENTORY", + coalInTruck + ) + } + + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withobject/DragonPlatebody.java b/Server/src/main/content/global/handlers/item/withobject/DragonPlatebody.java new file mode 100644 index 000000000..b8f3d60bf --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/DragonPlatebody.java @@ -0,0 +1,5 @@ +package content.global.handlers.item.withobject; + +public class DragonPlatebody { + +} diff --git a/Server/src/main/content/global/handlers/item/withobject/FishEasterEgg.kt b/Server/src/main/content/global/handlers/item/withobject/FishEasterEgg.kt new file mode 100644 index 000000000..c5e1869f7 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/FishEasterEgg.kt @@ -0,0 +1,26 @@ +package content.global.handlers.item.withobject + +import core.api.* +import content.global.skill.gather.woodcutting.WoodcuttingNode +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class FishEasterEgg : InteractionListener { + val TREE_IDs = WoodcuttingNode.values().map { it.id }.toIntArray() + + val fish = intArrayOf(Items.RAW_HERRING_345, Items.HERRING_347) + val doors = intArrayOf(1967,1968) + + override fun defineListeners() { + onUseWith(IntType.SCENERY, fish, *TREE_IDs){ player, _, _ -> + sendMessage(player, "This is not the mightiest tree in the forest.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, fish, *doors){ player, _, _ -> + sendMessage(player, "It can't be done!") + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withobject/GWDEntranceRopeListener.kt b/Server/src/main/content/global/handlers/item/withobject/GWDEntranceRopeListener.kt new file mode 100644 index 000000000..324364998 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/GWDEntranceRopeListener.kt @@ -0,0 +1,28 @@ +package content.global.handlers.item.withobject + +import core.api.removeItem +import core.api.setVarbit +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.game.interaction.InteractionListener + +/** + * Listener for using a rope directly on the GWD entrance hole + * @author Byte + */ +@Suppress("unused") +class GWDEntranceRopeListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.HOLE_26340) { player, used, _ -> + if (!removeItem(player, used)) { + return@onUseWith false + } + + setVarbit(player, 3932, 1, true) + + return@onUseWith true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/withobject/HairdresserCheeseListener.kt b/Server/src/main/content/global/handlers/item/withobject/HairdresserCheeseListener.kt new file mode 100644 index 000000000..0606f65b2 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/HairdresserCheeseListener.kt @@ -0,0 +1,37 @@ +package content.global.handlers.item.withobject + +import core.api.* +import core.game.node.entity.player.link.diary.DiaryType +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.game.interaction.InteractionListener + +/** + * Listener for using cheese on Ridgeley on the treadmill + * @author Byte + */ +@Suppress("unused") +class HairdresserCheeseListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.CHEESE_1985, Scenery.TREADMILL_11677) { player, used, _ -> + lock(player, 5) + + var isTaskSuccessful = false + runTask(player,3,1) { + if (!removeItem(player, used)) { + return@runTask + } + + sendMessage(player,"You throw the cheese to Ridgeley, for which he appears grateful.") + player.achievementDiaryManager.finishTask(player, DiaryType.FALADOR, 0, 6) + unlock(player) + + isTaskSuccessful = true + } + + return@onUseWith isTaskSuccessful + } + } +} diff --git a/Server/src/main/content/global/handlers/item/withobject/HatStand.kt b/Server/src/main/content/global/handlers/item/withobject/HatStand.kt new file mode 100644 index 000000000..91205e535 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/HatStand.kt @@ -0,0 +1,20 @@ +package content.global.handlers.item.withobject + +import core.api.* +import core.api.EquipmentSlot +import core.cache.def.impl.ItemDefinition +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class HatStand : InteractionListener { + + 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() { + onUseWith(IntType.SCENERY, hats, hat_stand){ player, used, with -> + sendDialogue(player, "It'd probably fall off if I tried to do that.") + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withobject/KaramjaBananaCrateListener.kt b/Server/src/main/content/global/handlers/item/withobject/KaramjaBananaCrateListener.kt new file mode 100644 index 000000000..6db716bf9 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/KaramjaBananaCrateListener.kt @@ -0,0 +1,128 @@ +package content.global.handlers.item.withobject + +import core.api.* +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 core.game.interaction.IntType +import core.game.interaction.InteractionListener +import kotlin.math.min + +/** + * Listener for Karamja banana crate interaction + * @author Byte + */ +@Suppress("unused") +class KaramjaBananaCrateListener : InteractionListener { + + companion object { + private const val CRATE_CAPACITY = 10 + + private val ANIMATION = Animation(832) + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.KARAMJAN_RUM_431, Scenery.CRATE_2072) { player, used, _ -> + if (!player.savedData.globalData.isLuthasTask) { + sendMessage(player, "I don't know what goes in there.") + return@onUseWith true + } + + if (getAttribute(player, "stashed-rum", false)) { + sendMessage(player, "There's already some rum in here...") + return@onUseWith true + } + + if (!removeItem(player, used)) { + return@onUseWith false + } + + lock(player, 2) + animate(player, ANIMATION) + sendDialogue(player, "You stash the rum in the crate.") + setAttribute(player, "/save:stashed-rum", true) + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.BANANA_1963, Scenery.CRATE_2072) { player, used, _ -> + if (!player.savedData.globalData.isLuthasTask) { + sendMessage(player, "I don't know what goes in there.") + return@onUseWith true + } + + val currNumBananasInCrate = player.savedData.globalData.karamjaBananas + + if (currNumBananasInCrate >= CRATE_CAPACITY) { + sendMessage(player, "The crate is already full.") + return@onUseWith true + } + + if (!removeItem(player, used)) { + return@onUseWith false + } + + lock(player, 2) + animate(player, ANIMATION) + sendDialogue(player, "You pack a banana into the crate.") + player.savedData.globalData.setKaramjaBannanas(currNumBananasInCrate + 1) + + return@onUseWith true + } + + on(Scenery.CRATE_2072, IntType.SCENERY, "search") { player, _ -> + if (!player.savedData.globalData.isLuthasTask) { + sendMessage(player, "I don't know what goes in there.") + return@on true + } + + val currNumBananasInCrate = player.savedData.globalData.karamjaBananas + + if (currNumBananasInCrate == 0) { + sendMessage(player, "The crate is completely empty.") + return@on true + } + + if (currNumBananasInCrate >= CRATE_CAPACITY) { + sendMessage(player, "The crate is full of bananas.") + return@on true + } + + sendMessage(player, "The crate has " + currNumBananasInCrate + " banana" + (if (currNumBananasInCrate > 1) "s" else "") + " inside.") + + return@on true + } + + on(Scenery.CRATE_2072, IntType.SCENERY, "fill") { player, _ -> + if (!player.savedData.globalData.isLuthasTask) { + sendMessage(player, "I don't know what goes in there.") + return@on true + } + + val numBananas = amountInInventory(player, Items.BANANA_1963) + val currNumBananasInCrate = player.savedData.globalData.karamjaBananas + val bananasToBeAdded = min(numBananas, CRATE_CAPACITY - player.savedData.globalData.karamjaBananas) + + if (numBananas == 0) { + return@on true + } + + if (currNumBananasInCrate >= CRATE_CAPACITY) { + sendMessage(player, "The crate is already full.") + return@on true + } + + if (!removeItem(player, Item(Items.BANANA_1963, bananasToBeAdded))) { + return@on false + } + + lock(player, 2) + animate(player, ANIMATION) + sendMessage(player, "You pack all your bananas into the crate.") + player.savedData.globalData.setKaramjaBannanas(player.savedData.globalData.karamjaBananas + bananasToBeAdded) + + return@on true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/withobject/PoisonFountainListener.kt b/Server/src/main/content/global/handlers/item/withobject/PoisonFountainListener.kt new file mode 100644 index 000000000..efb5a85eb --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/PoisonFountainListener.kt @@ -0,0 +1,56 @@ +package content.global.handlers.item.withobject + +import core.api.* +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 using poisoned fish food on Draynor Manor fountain + * @author Byte + */ +@Suppress("unused") +class PoisonFountainListener : InteractionListener { + + companion object { + private val ANIMATION = Animation(881) + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.POISONED_FISH_FOOD_274, Scenery.FOUNTAIN_153) { player, used, _ -> + if (getAttribute(player, "piranhas-killed", false)) { + sendMessage(player, "The piranhas are dead already.") + return@onUseWith true + } + + if (!removeItem(player, used)) { + return@onUseWith false + } + + lock(player, 5) + animate(player, ANIMATION) + sendMessage(player, "You pour the poisoned fish food into the fountain.") + setAttribute(player, "/save:piranhas-killed", true) + + submitIndividualPulse(player, object : Pulse(1) { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 1 -> sendMessage(player, "The piranhas start eating the food...") + 2 -> { + sendMessage(player, "... then die and float to the surface.") + unlock(player) + return true + } + } + return false + } + }) + + return@onUseWith true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/withobject/SackOnHay.kt b/Server/src/main/content/global/handlers/item/withobject/SackOnHay.kt new file mode 100644 index 000000000..0b942e623 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/SackOnHay.kt @@ -0,0 +1,22 @@ +package content.global.handlers.item.withobject + +import core.api.Container +import core.api.* +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class SackOnHay : InteractionListener { + val SACK = Items.EMPTY_SACK_5418 + val HAY = intArrayOf(36892, 36894, 36896, 300, 34593, 298, 299) + + override fun defineListeners() { + onUseWith(IntType.SCENERY, SACK, *HAY){ player, used, _ -> + if(removeItem(player, used.asItem(), Container.INVENTORY)){ + addItem(player, Items.HAY_SACK_6057, 1) + sendMessage(player, "You fill the sack with hay.") + } + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt b/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt new file mode 100644 index 000000000..bbde6bc21 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/SandSourceListener.kt @@ -0,0 +1,49 @@ +package content.global.handlers.item.withobject + +import core.api.* +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 collecting buckets of sand from a sand pit + * @author Byte + */ +@Suppress("unused") +class SandSourceListener : InteractionListener { + + companion object { + private val ANIMATION = Animation(895) + + private val SAND_PITS = intArrayOf( + Scenery.SAND_PIT_2645, + Scenery.SAND_PIT_4373, + Scenery.SANDPIT_10814 + ) + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.BUCKET_1925, *SAND_PITS) { player, used, _ -> + val numEmptyBuckets = amountInInventory(player, used.id) + + var animationTrigger = 0 + runTask(player, 2, numEmptyBuckets) { + if (removeItem(player, used)) { + if (animationTrigger % 2 == 0) { + animate(player, ANIMATION) + } + + if (addItem(player, Items.BUCKET_OF_SAND_1783)) { + sendMessage(player, "You fill the bucket with sand.") + } + } + + animationTrigger++ + } + + return@onUseWith true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt b/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt new file mode 100644 index 000000000..13ee4000a --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/SapCollectListener.kt @@ -0,0 +1,89 @@ +package content.global.handlers.item.withobject + +import core.api.* +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Animations +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.game.interaction.InteractionListener + +/** + * Listener for collecting buckets of sap from trees + * @author Byte + */ +@Suppress("unused") +class SapCollectListener : InteractionListener { + + companion object { + private val ANIMATION = Animation(Animations.HUMAN_FILL_BUCKET_WITH_SAP_2009) + + private val TREES = intArrayOf( + Scenery.TREE_1276, + Scenery.TREE_1277, + Scenery.TREE_1278, + Scenery.TREE_1280, + Scenery.EVERGREEN_1315, + Scenery.EVERGREEN_1316, + Scenery.EVERGREEN_1318, + Scenery.EVERGREEN_1319, + Scenery.TREE_1330, + Scenery.TREE_1331, + Scenery.TREE_1332, + Scenery.TREE_3033, + Scenery.TREE_3034, + Scenery.TREE_3035, + Scenery.TREE_3036, + Scenery.TREE_3879, + Scenery.TREE_3881, + Scenery.TREE_3882, + Scenery.TREE_3883, + Scenery.TREE_10041, + Scenery.TREE_14308, + Scenery.TREE_14309, + Scenery.TREE_30132, + Scenery.TREE_30133, + Scenery.TREE_37477, + Scenery.TREE_37478, + Scenery.TREE_37652 + ) + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.KNIFE_946, *TREES) { player, _, _ -> + if (!inInventory(player, Items.BUCKET_1925)) { + sendMessage(player, "You need a bucket to do that.") + } + + submitIndividualPulse(player, object : Pulse(2) { + override fun pulse(): Boolean { + if (removeItem(player, Items.BUCKET_1925)) { + animate(player, ANIMATION) + 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 + } + }) + + return@onUseWith true + } + + on(Items.BUCKET_OF_SAP_4687, IntType.ITEM, "empty") { player, node -> + val item = node as Item + + if (item.slot < 0) { + return@on false + } + + replaceSlot(player, item.slot, Item(Items.BUCKET_1925)) + sendMessage(player, "You empty the contents of the bucket on the floor.") + + return@on true + } + } +} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/SmithingPlugin.java b/Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java similarity index 89% rename from Server/src/main/java/core/game/interaction/item/withobject/SmithingPlugin.java rename to Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java index 8502523b5..e9ef946b3 100644 --- a/Server/src/main/java/core/game/interaction/item/withobject/SmithingPlugin.java +++ b/Server/src/main/content/global/handlers/item/withobject/SmithingPlugin.java @@ -1,9 +1,10 @@ -package core.game.interaction.item.withobject; +package content.global.handlers.item.withobject; +import content.global.skill.skillcapeperks.SkillcapePerks; import core.cache.def.impl.SceneryDefinition; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.smithing.SmithingBuilder; -import core.game.node.entity.skill.smithing.smelting.Bar; +import content.global.skill.smithing.SmithingBuilder; +import content.global.skill.smithing.smelting.Bar; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -13,8 +14,8 @@ import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.plugin.Plugin; import core.plugin.Initializable; -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the option handler used for smithing. @@ -44,9 +45,10 @@ 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); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { @@ -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; } @@ -125,7 +127,7 @@ public final class SmithingPlugin extends UseWithHandler { return true; } if (player.getSkills().getLevel(Skills.SMITHING) < bar.getLevel()) { - player.getDialogueInterpreter().sendDialogue("You need a smithing level of at leat " + bar.getLevel() + " to work " + bar.getProduct().getName().toLowerCase() + "s."); + player.getDialogueInterpreter().sendDialogue("You need a smithing level of at least " + bar.getLevel() + " to work " + bar.getProduct().getName().toLowerCase() + "s."); return true; } final SmithingBuilder builder = new SmithingBuilder(item); diff --git a/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt b/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt new file mode 100644 index 000000000..084e7f117 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt @@ -0,0 +1,127 @@ +package content.global.handlers.item.withobject + +import core.api.* +import core.game.node.entity.skill.Skills +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 core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.repository.Repository.sendNews +import java.util.* + +/** + * Listener for blessing spirit shield and attaching sigils + * @author Byte + */ +@Suppress("unused") +class SpiritShieldBlessListener : InteractionListener { + + companion object { + private const val REQUIRED_PRAYER_LEVEL_TO_BLESS_SHIELD = 85 + private const val REQUIRED_PRAYER_LEVEL_TO_ATTACH_SIGIL = 90 + private const val REQUIRED_SMITHING_LEVEL_TO_ATTACH_SIGIL = 85 + + private val ANIMATION = Animation(898) + + private val COMPONENTS = intArrayOf( + Items.HOLY_ELIXIR_13754, + Items.SPIRIT_SHIELD_13734 + ) + + private val ALTARS = intArrayOf( + Scenery.ALTAR_409, + Scenery.ALTAR_2640, + Scenery.ALTAR_4008, + Scenery.ALTAR_18254, + Scenery.ALTAR_19145, + Scenery.ALTAR_24343, + Scenery.SARADOMIN_ALTAR_26287, + Scenery.ALTAR_27661, + Scenery.ALTAR_34616, + Scenery.ALTAR_36972, + Scenery.ALTAR_39842 + ) + + private val SIGILS_SHIELDS_MAP = mapOf( + Items.ARCANE_SIGIL_13746 to Items.ARCANE_SPIRIT_SHIELD_13738, + Items.DIVINE_SIGIL_13748 to Items.DIVINE_SPIRIT_SHIELD_13740, + Items.ELYSIAN_SIGIL_13750 to Items.ELYSIAN_SPIRIT_SHIELD_13742, + Items.SPECTRAL_SIGIL_13752 to Items.SPECTRAL_SPIRIT_SHIELD_13744 + ) + + private val ANVILS = intArrayOf( + Scenery.ANVIL_2782, + Scenery.ANVIL_2783, + Scenery.ANVIL_4306, + Scenery.ANVIL_6150, + Scenery.ANVIL_22725, + Scenery.LATHE_26817, + Scenery.ANVIL_26822, + Scenery.ANVIL_37622 + ) + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, COMPONENTS, *ALTARS) { player, _, _ -> + if (!inInventory(player, Items.HOLY_ELIXIR_13754) || !inInventory(player, Items.SPIRIT_SHIELD_13734)) { + sendMessage(player, "You need a holy elixir and an unblessed spirit shield in order to do this.") + return@onUseWith true + } + + if (!hasLevelDyn(player, Skills.PRAYER, REQUIRED_PRAYER_LEVEL_TO_BLESS_SHIELD)) { + sendMessage(player, "You need a Prayer level of $REQUIRED_PRAYER_LEVEL_TO_BLESS_SHIELD in order to bless the shield.") + } + + if (!removeItem(player, Items.HOLY_ELIXIR_13754)) { + return@onUseWith false + } + + if (!removeItem(player, Items.SPIRIT_SHIELD_13734)) { + return@onUseWith false + } + + 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 + } + + onUseWith(IntType.SCENERY, SIGILS_SHIELDS_MAP.keys.toIntArray(), *ANVILS) { player, used, _ -> + if (!inInventory(player, Items.BLESSED_SPIRIT_SHIELD_13736)) { + sendMessage(player, "You need a blessed spirit shield in order to continue.") + return@onUseWith true + } + + if (!hasLevelDyn(player, Skills.PRAYER, REQUIRED_PRAYER_LEVEL_TO_ATTACH_SIGIL) || !hasLevelDyn(player, Skills.SMITHING, REQUIRED_SMITHING_LEVEL_TO_ATTACH_SIGIL)) { + sendMessage(player, "You need a Prayer level of $REQUIRED_PRAYER_LEVEL_TO_ATTACH_SIGIL and a Smithing level of $REQUIRED_SMITHING_LEVEL_TO_ATTACH_SIGIL in order to attach the sigil.") + return@onUseWith true + } + + if (!inInventory(player, Items.HAMMER_2347)) { + sendMessage(player, "You need a hammer in order to do this.") + return@onUseWith true + } + + sendDialogueOptions(player, "Combine the two?", "Yes", "No") + addDialogueAction(player) { _, buttonId -> + when (buttonId) { + 2 -> { + if (removeItem(player, used) && removeItem(player, Items.BLESSED_SPIRIT_SHIELD_13736)) { + val product = Item(SIGILS_SHIELDS_MAP[used.id]!!) + addItem(player, product.id) + animate(player, ANIMATION) + sendItemDialogue(player, product.id, "You successfully attach the " + used.name.lowercase(Locale.getDefault()) + " to the blessed spirit shield.") + sendNews(player.username + " has just made the " + product.name + ".") + } + } + } + } + + return@onUseWith true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/withobject/SwampHoleRopeListener.kt b/Server/src/main/content/global/handlers/item/withobject/SwampHoleRopeListener.kt new file mode 100644 index 000000000..020b50e62 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/SwampHoleRopeListener.kt @@ -0,0 +1,33 @@ +package content.global.handlers.item.withobject + +import core.api.* +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.game.interaction.InteractionListener + +/** + * Listener for using rope on Lumbridge swamp hole + * @author Byte + */ +@Suppress("unused") +class SwampHoleRopeListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.DARK_HOLE_5947) { player, used, _ -> + if (player.savedData.globalData.hasTiedLumbridgeRope()) { + sendDialogue(player, "There is already a rope tied to the entrance.") + return@onUseWith true + } + + if (!removeItem(player, used)) { + return@onUseWith false + } + + sendItemDialogue(player, used, "You tie the rope to the top of the entrance and throw it down.") + player.savedData.globalData.setLumbridgeRope(true) + + 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 new file mode 100644 index 000000000..75652b75d --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withobject/WaterSourceListener.kt @@ -0,0 +1,166 @@ +package content.global.handlers.item.withobject + +import core.api.* +import core.game.node.entity.player.link.diary.DiaryType +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.map.Location +import org.rs09.consts.Sounds + +/** + * Handles filling most water sources. + * @author Ceikry + */ +class WaterSourceListener : InteractionListener { + // 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) + + override fun defineListeners() + { + onUseWith(IntType.SCENERY, WaterVessel.getInputs(), *waterSources){ player, used, with -> + val vessel = WaterVessel.forId(used.id) ?: return@onUseWith false + + if(with.name.contains("well", ignoreCase = true) && !vessel.wellable) + { + sendMessage(player, formatMsgText(used.name, nonWellableMsg)) + 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) + { + if(!player.achievementDiaryManager.getDiary(DiaryType.FALADOR).isComplete(0,7)) + player.achievementDiaryManager.getDiary(DiaryType.FALADOR).updateTask(player, 0, 7, true) + } + + player.pulseManager.run(object : Pulse(1){ + override fun pulse(): Boolean { + if(removeItem(player, used.id)) + { + 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 + } + }) + + 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 + { + val sb = StringBuilder() + val templateChars = template.toCharArray() + val nameChars = name.toCharArray() + + for(tc in templateChars) + { + if(tc == '@'){ + for(nc in nameChars) + { + if (nc == '(') break + else sb.append(nc.toLowerCase()) + } + } + else sb.append(tc) + } + + return sb.toString() + } + + internal enum class WaterVessel(val inputs: IntArray, val output: Int, val wellable: Boolean = false, val autofill: Boolean = true, val fillMsg: String = "You fill the @.") + { + BUCKET( + inputs = intArrayOf(Items.BUCKET_1925), + output = Items.BUCKET_OF_WATER_1929, + wellable = true + ), + VIAL( + inputs = intArrayOf(Items.VIAL_229), + output = Items.VIAL_OF_WATER_227, + ), + JUG( + inputs = intArrayOf(Items.JUG_1935), + output = Items.JUG_OF_WATER_1937 + ), + BOWL( + inputs = intArrayOf(Items.BOWL_1923), + output = Items.BOWL_OF_WATER_1921 + ), + WATERING_CAN( + inputs = intArrayOf(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), + output = Items.WATERING_CAN8_5340 + ), + WATER_SKIN( + inputs = intArrayOf(Items.WATERSKIN0_1831, Items.WATERSKIN1_1829, Items.WATERSKIN2_1827, Items.WATERSKIN3_1825), + output = Items.WATERSKIN4_1823 + ), + 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 + { + //map our input item IDs to their respective WaterVessel entry + private val itemMap = HashMap() + + init { + for(entry in values()) + { + entry.inputs.forEach { itemMap[it] = entry } + } + } + + //return a nice list of all the input IDs + fun getInputs(): IntArray + { + return itemMap.keys.toIntArray() + } + + fun forId(id: Int): WaterVessel? + { + return itemMap[id] + } + } + } +} diff --git a/Server/src/main/java/core/game/interaction/item/withplayer/ChristmasCrackerUsage.java b/Server/src/main/content/global/handlers/item/withplayer/ChristmasCrackerUsage.java similarity index 97% rename from Server/src/main/java/core/game/interaction/item/withplayer/ChristmasCrackerUsage.java rename to Server/src/main/content/global/handlers/item/withplayer/ChristmasCrackerUsage.java index 76cc6d01d..de13eb88f 100644 --- a/Server/src/main/java/core/game/interaction/item/withplayer/ChristmasCrackerUsage.java +++ b/Server/src/main/content/global/handlers/item/withplayer/ChristmasCrackerUsage.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withplayer; +package content.global.handlers.item.withplayer; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/java/core/game/interaction/item/withplayer/FieldRationUsage.java b/Server/src/main/content/global/handlers/item/withplayer/FieldRationUsage.java similarity index 96% rename from Server/src/main/java/core/game/interaction/item/withplayer/FieldRationUsage.java rename to Server/src/main/content/global/handlers/item/withplayer/FieldRationUsage.java index abe37b298..0271f9521 100644 --- a/Server/src/main/java/core/game/interaction/item/withplayer/FieldRationUsage.java +++ b/Server/src/main/content/global/handlers/item/withplayer/FieldRationUsage.java @@ -1,4 +1,4 @@ -package core.game.interaction.item.withplayer; +package content.global.handlers.item.withplayer; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/content/global/handlers/npc/BankerNPC.kt b/Server/src/main/content/global/handlers/npc/BankerNPC.kt new file mode 100644 index 000000000..7dff2f0d7 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/BankerNPC.kt @@ -0,0 +1,323 @@ +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 + +/** + * 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 + + /** + * 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. + */ + fun checkLunarIsleRestriction(player: Player, node: Node): Boolean { + if (node.location.regionId != LUNAR_ISLE_BANK_REGION) + return false + + if (!hasSealOfPassage(player)) { + return true + } + + return false + } + + fun attemptBank(player: Player, node: Node): Boolean { + val npc = node as NPC + + if (checkLunarIsleRestriction(player, node)) { + openDialogue(player, npc.id, npc) + return true + } + + npc.faceLocation(null) + openBankAccount(player) + + return true + } + + fun attemptCollect(player: Player, node: Node): Boolean { + val npc = node as NPC + + if (checkLunarIsleRestriction(player, node)) { + openDialogue(player, npc.id, npc) + return true + } + + npc.faceLocation(null) + openGrandExchangeCollectionBox(player) + + return true + } + } + + //Constructor spaghetti because Arios I guess + constructor() : super(0, null) + private constructor(id: Int, location: Location) : super(id, location) + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + return BankerNPC(id, location) + } + + private fun findAdjacentBankBoothLocation(): Pair? { + for (side in arrayOf(Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST)) { + val boothLocation = location.transform(side) + val sceneryObject = getScenery(boothLocation) + + if (sceneryObject != null && sceneryObject.id in BankBoothListener.BANK_BOOTHS) { + return Pair(side, boothLocation.transform(side, 1)) + } + } + + return null + } + + 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) + /* 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(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, NPC_IDS, "bank", "collect", "talk-to", handler = ::provideDestinationOverride) + } + + override fun init() { + super.init() + + findAdjacentBankBoothLocation()?.let { + val (boothDirection, _) = it + + direction = boothDirection + isWalks = false + + setAttribute("facing_booth", true) + } + } + + 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/java/core/game/node/entity/npc/other/ChromaticDragonNPC.java b/Server/src/main/content/global/handlers/npc/ChromaticDragonNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/npc/other/ChromaticDragonNPC.java rename to Server/src/main/content/global/handlers/npc/ChromaticDragonNPC.java index a333414e2..66121a209 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/ChromaticDragonNPC.java +++ b/Server/src/main/content/global/handlers/npc/ChromaticDragonNPC.java @@ -1,16 +1,16 @@ -package core.game.node.entity.npc.other; +package content.global.handlers.npc; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.equipment.SwitchAttack; -import core.game.node.entity.combat.handlers.DragonfireSwingHandler; +import content.global.handlers.item.equipment.special.DragonfireSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.AbstractNPC; 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 rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MultiSwingHandler; /** * Handles a chromatic dragon (green, blue, red, ..). diff --git a/Server/src/main/java/core/game/interaction/object/CowMilkingPlugin.java b/Server/src/main/content/global/handlers/npc/CowMilkingPlugin.java similarity index 91% rename from Server/src/main/java/core/game/interaction/object/CowMilkingPlugin.java rename to Server/src/main/content/global/handlers/npc/CowMilkingPlugin.java index efb6160e6..833fea4cf 100644 --- a/Server/src/main/java/core/game/interaction/object/CowMilkingPlugin.java +++ b/Server/src/main/content/global/handlers/npc/CowMilkingPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.global.handlers.npc; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.NodeUsageEvent; @@ -12,7 +12,10 @@ import core.game.system.task.Pulse; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +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; @@ -83,7 +88,7 @@ public final class CowMilkingPlugin extends OptionHandler { SceneryDefinition.forId(8689).getHandlers().put("option:milk", this); SceneryDefinition.forId(12111).getHandlers().put("option:milk", this); SceneryDefinition.setOptionHandler("steal-cowbell", this); - PluginManager.definePlugin(new BucketHandler()); + ClassScanner.definePlugin(new BucketHandler()); return this; } diff --git a/Server/src/main/content/global/handlers/npc/CowNPC.java b/Server/src/main/content/global/handlers/npc/CowNPC.java new file mode 100644 index 000000000..95383951f --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/CowNPC.java @@ -0,0 +1,55 @@ +package content.global.handlers.npc; + +import core.game.node.entity.npc.AbstractNPC; +import core.game.world.map.Location; +import core.plugin.Initializable; +import core.tools.RandomFunction; + +/** + * Represents the a cow npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class CowNPC extends AbstractNPC { + + /** + * Represents the force chat to use. + */ + private static final String FORCE_CHAT = "Moo"; + + /** + * Constructs a new {@code CowNPC} {@code Object}. + */ + public CowNPC() { + super(0, null, true); + } + + /** + * Constructs a new {@code CowNPC} {@code Object}. + * @param id the id. + * @param location the location. + */ + public CowNPC(int id, Location location) { + super(id, location, true); + } + + @Override + public void tick() { + if (RandomFunction.random(45) == 5 && this.getId() != 1766) { // calves don't moo + sendChat(FORCE_CHAT); + } + super.tick(); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new CowNPC(id, location); + } + + @Override + public int[] getIds() { + return new int[] { 81, 397, 955, 1766, 1767, 3309 }; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/DarkWizardNPC.java b/Server/src/main/content/global/handlers/npc/DarkWizardNPC.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/other/DarkWizardNPC.java rename to Server/src/main/content/global/handlers/npc/DarkWizardNPC.java index a671e9136..3124d8046 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/DarkWizardNPC.java +++ b/Server/src/main/content/global/handlers/npc/DarkWizardNPC.java @@ -1,8 +1,8 @@ -package core.game.node.entity.npc.other; +package content.global.handlers.npc; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.link.SpellBookManager.SpellBook; diff --git a/Server/src/main/content/global/handlers/npc/DecantListener.kt b/Server/src/main/content/global/handlers/npc/DecantListener.kt new file mode 100644 index 000000000..368835e32 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/DecantListener.kt @@ -0,0 +1,164 @@ +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.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 + +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 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 + + // 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 + } + + // 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 + } + } + + internal class DecantingDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage++){ + 0 -> npc("There you go!") + 1 -> player("Thanks!").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/npc/DuckNPC.java b/Server/src/main/content/global/handlers/npc/DuckNPC.java new file mode 100644 index 000000000..40bdd4522 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/DuckNPC.java @@ -0,0 +1,55 @@ +package content.global.handlers.npc; + +import core.game.node.entity.npc.AbstractNPC; +import core.game.world.map.Location; +import core.plugin.Initializable; +import core.tools.RandomFunction; + +/** + * Represents the a duck npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class DuckNPC extends AbstractNPC { + + /** + * Represents the force chat to use. + */ + private static final String FORCE_CHAT = "Quack!"; + + /** + * Constructs a new {@code DuckNPC} {@code Object}. + */ + public DuckNPC() { + super(0, null); + } + + /** + * Constructs a new {@code DuckNPC} {@code Object}. + * @param id the id. + * @param location the location. + */ + public DuckNPC(int id, Location location) { + super(id, location, true); + } + + @Override + public void tick() { + if (RandomFunction.random(45) == 5) { + sendChat(FORCE_CHAT); + } + super.tick(); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new DuckNPC(id, location); + } + + @Override + public int[] getIds() { + return new int[] { 46, 2693, 6113 }; + } + +} diff --git a/Server/src/main/java/core/game/interaction/object/DummyAttackPlugin.java b/Server/src/main/content/global/handlers/npc/DummyAttackPlugin.java similarity index 98% rename from Server/src/main/java/core/game/interaction/object/DummyAttackPlugin.java rename to Server/src/main/content/global/handlers/npc/DummyAttackPlugin.java index c6cdf566f..8faf7ea6a 100644 --- a/Server/src/main/java/core/game/interaction/object/DummyAttackPlugin.java +++ b/Server/src/main/content/global/handlers/npc/DummyAttackPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.global.handlers.npc; import core.cache.def.impl.SceneryDefinition; import core.game.node.entity.skill.Skills; diff --git a/Server/src/main/content/global/handlers/npc/GuardNPC.java b/Server/src/main/content/global/handlers/npc/GuardNPC.java new file mode 100644 index 000000000..9cde38cc7 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/GuardNPC.java @@ -0,0 +1,48 @@ +package content.global.handlers.npc; + +import core.game.node.entity.npc.AbstractNPC; +import core.plugin.Initializable; +import core.game.world.map.Location; + +/** + * Represents the abstract knight npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +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, 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}. + */ + public GuardNPC() { + super(0, null, true); + this.setAggressive(false); + } + + /** + * Constructs a new {@code GuardNPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + private GuardNPC(int id, Location location) { + super(id, location, true); + this.setAggressive(false); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new GuardNPC(id, location); + } + + @Override + public int[] getIds() { + return ID; + } + +} diff --git a/Server/src/main/java/core/game/interaction/npc/HairCutPlugin.java b/Server/src/main/content/global/handlers/npc/HairCutPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/npc/HairCutPlugin.java rename to Server/src/main/content/global/handlers/npc/HairCutPlugin.java index f3ed0eb83..a455e5476 100644 --- a/Server/src/main/java/core/game/interaction/npc/HairCutPlugin.java +++ b/Server/src/main/content/global/handlers/npc/HairCutPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.global.handlers.npc; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/global/handlers/npc/HighwayManNPC.java b/Server/src/main/content/global/handlers/npc/HighwayManNPC.java new file mode 100644 index 000000000..cc5157b1f --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/HighwayManNPC.java @@ -0,0 +1,62 @@ +package content.global.handlers.npc; + +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.player.link.diary.DiaryType; +import core.plugin.Initializable; +import core.game.world.map.Location; + +/** + * Represents the highway man npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class HighwayManNPC extends AbstractNPC { + + /** + * Constructs a new {@code HighwayManNPC} {@code Object}. + * @param id the id. + * @param location the location. + */ + public HighwayManNPC(int id, Location location) { + super(id, location); + } + + /** + * Constructs a new {@code HighwayManNPC} {@code Object}. + */ + public HighwayManNPC() { + super(0, null); + } + + @Override + public void finalizeDeath(Entity killer) { + super.finalizeDeath(killer); + if (getId() == 180) { + if (killer instanceof Player) { + final Player player = killer.asPlayer(); + if (!player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).isComplete(0, 10)) { + player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).updateTask(player, 0, 10, true); + } + } + } + } + + @Override + public void onAttack(Entity target) { + sendChat("Stand and deliver!"); + } + + @Override + public int[] getIds() { + return new int[] { 180, 2677 }; + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new HighwayManNPC(id, location); + } + +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/IceGiantNPC.java b/Server/src/main/content/global/handlers/npc/IceGiantNPC.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/npc/other/IceGiantNPC.java rename to Server/src/main/content/global/handlers/npc/IceGiantNPC.java index 5226484b3..999bf00fa 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/IceGiantNPC.java +++ b/Server/src/main/content/global/handlers/npc/IceGiantNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.global.handlers.npc; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/content/global/handlers/npc/MetalDragonNPC.java b/Server/src/main/content/global/handlers/npc/MetalDragonNPC.java new file mode 100644 index 000000000..6884829b7 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/MetalDragonNPC.java @@ -0,0 +1,73 @@ +package content.global.handlers.npc; + +import core.game.node.entity.Entity; +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.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 a metal dragon (bronze, iron, steel). + * @author Emperor + */ +@Initializable +public final class MetalDragonNPC extends AbstractNPC { + + /** + * The dragonfire attack. + */ + private static final SwitchAttack DRAGONFIRE = DragonfireSwingHandler.get(false, 52, new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 54, 40, 36, 41, 46, 20, 255)); + + /** + * 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(91, Priority.HIGH)), DRAGONFIRE); + + /** + * Constructs a new {@code MetalDragonNPC} {@code Object}. + */ + public MetalDragonNPC() { + super(1590, null); + } + + /** + * Constructs a new {@code MetalDragonNPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + public MetalDragonNPC(int id, Location location) { + super(id, location); + } + + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + return combatAction; + } + + @Override + public int getDragonfireProtection(boolean fire) { + return 0x2 | 0x4 | 0x8; + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new MetalDragonNPC(id, location); + } + + @Override + public int[] getIds() { + return new int[] { 1590, // Bronze dragon + 1591, // Iron dragon + 1592, // Steel dragon + 3590 // Steel dragon (POH) + }; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/MonkNPC.java b/Server/src/main/content/global/handlers/npc/MonkNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/other/MonkNPC.java rename to Server/src/main/content/global/handlers/npc/MonkNPC.java index 7fbd6675a..0daf3ac8e 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/MonkNPC.java +++ b/Server/src/main/content/global/handlers/npc/MonkNPC.java @@ -1,9 +1,9 @@ -package core.game.node.entity.npc.other; +package content.global.handlers.npc; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/content/global/handlers/npc/NPCDepositListener.kt b/Server/src/main/content/global/handlers/npc/NPCDepositListener.kt new file mode 100644 index 000000000..31a928df7 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/NPCDepositListener.kt @@ -0,0 +1,28 @@ +package content.global.handlers.npc + +import core.api.anyInEquipment +import core.api.openDepositBox +import core.api.sendNPCDialogue +import core.api.setInterfaceText +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.tools.END_DIALOGUE + +class NPCDepositListener : InteractionListener { + + override fun defineListeners() { + on(NPCs.PEER_THE_SEER_1288, IntType.NPC, "deposit") { player, _ -> + 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 { + sendNPCDialogue(player, NPCs.PEER_THE_SEER_1288, + "Do not pester me, outerlander! I will only deposit items into the banks of those who have earned Fremennik sea boots!", + core.game.dialogue.FacialExpression.ANNOYED).also { END_DIALOGUE } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/npc/NPCDisturbPlugin.java b/Server/src/main/content/global/handlers/npc/NPCDisturbPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/npc/NPCDisturbPlugin.java rename to Server/src/main/content/global/handlers/npc/NPCDisturbPlugin.java index 56d23e6b6..1810892d2 100644 --- a/Server/src/main/java/core/game/interaction/npc/NPCDisturbPlugin.java +++ b/Server/src/main/content/global/handlers/npc/NPCDisturbPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.global.handlers.npc; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt new file mode 100644 index 000000000..4f5764b0d --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt @@ -0,0 +1,90 @@ +package content.global.handlers.npc + +import core.api.sendMessage +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. + * @author Ceikry + */ +class NPCTalkListener : InteractionListener { + + val barCrawlNPCs = intArrayOf(733,848,735,739,737,738,731,568,3217,736,734) + + override fun defineListeners() { + on(barCrawlNPCs, IntType.NPC, "talk-to", "talk"){ player, node -> + val type = BarcrawlType.forId(node.id) + val instance = BarcrawlManager.getInstance(player) + if (instance.isFinished || !instance.isStarted || instance.isCompleted(type!!.ordinal)) { + player.dialogueInterpreter.open(node.id, node) + } else { + player.dialogueInterpreter.open("barcrawl dialogue", node.id, type) + } + return@on true + } + + on(IntType.NPC,"talk-to","talk","talk to"){ player, node -> + val npc = node.asNpc() + if(RandomEvents.randomIDs.contains(node.id)){ + if(AntiMacro.getEventNpc(player) == null || AntiMacro.getEventNpc(player) != node.asNpc() || AntiMacro.getEventNpc(player)?.finalized == true) { + // 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 { + 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) + } + // ---------------- THESE DIALOGUES ARE INAUTHENTIC BUT ARE COPING FOR A CORE ISSUE ----------------------- + if (player.properties.combatPulse.getVictim() == npc) { + sendMessage(player, "I don't think they have any interest in talking to me right now!") + return@on true + } + if (npc.inCombat()) { + sendMessage(player, "They look a bit busy at the moment.") + return@on true + } + //--------------------------------------------------------------------------------------------------------- + //I'm sorry for this but it was honestly the best way to do this + if (player.getAttribute("$GC_BASE_ATTRIBUTE:$GC_JOB_ORDINAL", -1) != -1) { + val job = GnomeCookingJob.values()[player.getAttribute("$GC_BASE_ATTRIBUTE:$GC_JOB_ORDINAL", -1)] + if (node.getId() == job.npc_id && !player.getAttribute("$GC_BASE_ATTRIBUTE:$GC_JOB_COMPLETE", false)) { + player.dialogueInterpreter.open(GCCompletionDialogue(job)) + return@on true + } + } + return@on player.dialogueInterpreter.open(npc.id, npc) + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/RatNPC.java b/Server/src/main/content/global/handlers/npc/RatNPC.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/npc/other/RatNPC.java rename to Server/src/main/content/global/handlers/npc/RatNPC.java index faa01560c..f8a380db4 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/RatNPC.java +++ b/Server/src/main/content/global/handlers/npc/RatNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.global.handlers.npc; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; @@ -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,12 +52,9 @@ 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); } - if (!getName().equals("Giant rat")) { - GroundItemManager.create(new Item(526), getLocation(), (Player) killer); - } } } 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/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 new file mode 100644 index 000000000..2e352f58d --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/ShopNPC.java @@ -0,0 +1,56 @@ +package content.global.handlers.npc; + +import core.game.node.entity.npc.AbstractNPC; +import core.plugin.Initializable; +import core.game.world.map.Location; + +/** + * Represents the abstract npc of a generic owner of a shop. + * @author 'Vexia + */ +@Initializable +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, 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}. + */ + public ShopNPC() { + super(0, null, true); + } + + /** + * Constructs a new {@code ShopNPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + private ShopNPC(int id, Location location) { + super(id, location, true); + } + + @Override + public void init() { + super.init(); + setWalks(true); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new ShopNPC(id, location); + } + + @Override + public int getWalkRadius() { + return 3; + } + + @Override + public int[] getIds() { + return ID; + } + +} diff --git a/Server/src/main/content/global/handlers/player/AttackOptionPlugin.java b/Server/src/main/content/global/handlers/player/AttackOptionPlugin.java new file mode 100644 index 000000000..328d52c7b --- /dev/null +++ b/Server/src/main/content/global/handlers/player/AttackOptionPlugin.java @@ -0,0 +1,42 @@ +package content.global.handlers.player; + +import core.cache.def.impl.NPCDefinition; +import core.game.interaction.Option; +import core.game.interaction.OptionHandler; +import core.game.node.Node; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.player.Player; +import core.plugin.Initializable; +import core.plugin.Plugin; + +/** + * Represents the attack option plugin handler. + * @author Emperor + * @version 1.0 + */ +@Initializable +public final class AttackOptionPlugin extends OptionHandler { + + @Override + public boolean handle(Player player, Node node, String option) { + player.attack(node); + return true; + } + + @Override + public boolean isWalk() { + return false; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + Option._P_ATTACK.setHandler(this); + return this; + } + + @Override + public boolean isDelayed(Player player) { + return false; + } + +} diff --git a/Server/src/main/java/core/game/interaction/player/FollowOptionPlugin.java b/Server/src/main/content/global/handlers/player/FollowOptionPlugin.java similarity index 90% rename from Server/src/main/java/core/game/interaction/player/FollowOptionPlugin.java rename to Server/src/main/content/global/handlers/player/FollowOptionPlugin.java index bf8de8f96..a3cea3b54 100644 --- a/Server/src/main/java/core/game/interaction/player/FollowOptionPlugin.java +++ b/Server/src/main/content/global/handlers/player/FollowOptionPlugin.java @@ -1,10 +1,11 @@ -package core.game.interaction.player; +package content.global.handlers.player; import core.game.interaction.DestinationFlag; import core.game.interaction.MovementPulse; import core.game.interaction.Option; import core.game.interaction.OptionHandler; import core.game.node.Node; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; @@ -38,7 +39,7 @@ public final class FollowOptionPlugin extends OptionHandler { super.stop(); mover.face(null); } - }, "movement"); + }, PulseType.STANDARD); return true; } } diff --git a/Server/src/main/content/global/handlers/player/LoginValidationPlugin.java b/Server/src/main/content/global/handlers/player/LoginValidationPlugin.java new file mode 100644 index 000000000..642d33f07 --- /dev/null +++ b/Server/src/main/content/global/handlers/player/LoginValidationPlugin.java @@ -0,0 +1,122 @@ +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 org.rs09.consts.Items; + +import java.util.concurrent.TimeUnit; + +import static core.api.ContentAPIKt.*; + +/** + * Validates a player login. + * @author Emperor + * @author Vexia + * + */ +@PluginManifest(type = PluginType.LOGIN) +@Initializable +public final class LoginValidationPlugin implements Plugin { + + /** + * Represents the quest point items to remove. + */ + 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} + */ + public LoginValidationPlugin() { + /* + * empty. + */ + } + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(final Player player) throws Throwable { + player.unlock(); + if (player.isArtificial()) { + return this; + } + if (!SystemManager.getSystemConfig().validLogin(player)) { + return this; + } + if (GameWorld.getSettings().isDevMode()) { + player.toggleDebug(); + } + if (player.getAttribute("fc_wave", -1) > -1) { + ActivityManager.start(player, "fight caves", true); + } + if (player.getAttribute("falconry", false)) { + ActivityManager.start(player, "falconry", true); + } + if (player.getSavedData().getQuestData().getDragonSlayerAttribute("repaired")) { + 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)); + } else { + player.getSavedData().getGlobalData().setLootShareDelay(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + } + checkQuestPointsItems(player); + return this; + } + + /** + * Method used to check for the quest point cape items. + * @param player the player. + */ + private static void checkQuestPointsItems(final Player player) { + 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 (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 { + 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 new file mode 100644 index 000000000..33707b358 --- /dev/null +++ b/Server/src/main/content/global/handlers/player/RequestOption.kt @@ -0,0 +1,32 @@ +package content.global.handlers.player + +import core.game.interaction.Option._P_ASSIST +import core.game.interaction.Option._P_TRADE +import core.game.node.entity.player.Player +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. + * @author 'Vexia + * @author dginovker + * @version 2.0 + */ +@Initializable +class RequestOption : InteractionListener { + override fun defineListeners() { + on(_P_TRADE.name, IntType.PLAYER) { player, node -> + player.requestManager.request((node as Player), RequestType.TRADE) + 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 + } + } +} diff --git a/Server/src/main/content/global/handlers/scenery/BankBoothListener.kt b/Server/src/main/content/global/handlers/scenery/BankBoothListener.kt new file mode 100644 index 000000000..b725c8cac --- /dev/null +++ b/Server/src/main/content/global/handlers/scenery/BankBoothListener.kt @@ -0,0 +1,193 @@ +package content.global.handlers.scenery + +import core.api.* +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.IronmanMode +import core.game.node.item.Item +import core.game.world.map.Direction +import core.game.world.map.Location +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import core.ServerConstants +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import content.global.handlers.npc.BankerNPC +import core.game.world.repository.Repository + +/** + * Allows the user to interact with bank booths. + * + * @author vddCore + */ +class BankBoothListener : InteractionListener { + + companion object { + val INOPERABLE_BANK_BOOTHS = intArrayOf( + Scenery.BANK_BOOTH_12800, Scenery.BANK_BOOTH_12801, Scenery.BANK_BOOTH_36262, Scenery.BANK_BOOTH_35648 + ) + + val BANK_BOOTHS = intArrayOf( + Scenery.BANK_BOOTH_2213, Scenery.BANK_BOOTH_2214, Scenery.BANK_BOOTH_3045, Scenery.BANK_BOOTH_5276, + Scenery.BANK_BOOTH_6084, Scenery.BANK_BOOTH_10517, Scenery.BANK_BOOTH_11338, Scenery.BANK_BOOTH_11402, + Scenery.BANK_BOOTH_11758, Scenery.BANK_BOOTH_12798, Scenery.BANK_BOOTH_12799, Scenery.BANK_BOOTH_14367, + Scenery.BANK_BOOTH_14368, Scenery.BANK_BOOTH_16700, Scenery.BANK_BOOTH_18491, Scenery.BANK_BOOTH_19230, + Scenery.BANK_BOOTH_20325, Scenery.BANK_BOOTH_20326, Scenery.BANK_BOOTH_20327, Scenery.BANK_BOOTH_20328, + Scenery.BANK_BOOTH_22819, Scenery.BANK_BOOTH_24914, Scenery.BANK_BOOTH_25808, Scenery.BANK_BOOTH_26972, + 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.") + } + } + + /** + * Searches an area in the order described + * by each number with X being the location + * of node being interacted with. + * + * [1] + * [4][X][2] + * [3] + */ + private fun locateAdjacentBankerLinear(node: Node): NPC? { + for (dir in arrayOf(Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST)) { + Repository.findNPC(node.location.transform(dir))?.let { return it as? BankerNPC } + } + + return null + } + + /** + * If size is 1 then the following method + * searches a square area in the following + * order with X being the location of node + * that was interacted with. + * + * ->[ ] + * [ X ] + * [ ]-> + * + */ + private fun locateAdjacentBankerSquare(node: Node, size: Int = 1): NPC? { + for (y in (node.location.y - size)..(node.location.y + size)) { + for (x in (node.location.x - size)..(node.location.x + size)) { + Repository.findNPC(Location(x, y))?.let { return it as? BankerNPC } + } + } + + return null + } + + private fun tryInvokeBankerDialogue(player: Player, node: Node) { + /** + * First, we look for regular bankers that neatly stand in front of the bank booth. + * If that fails, we expand the search to a larger area. + */ + (locateAdjacentBankerLinear(node) ?: locateAdjacentBankerSquare(node, 2))?.let { + if (core.game.dialogue.DialogueInterpreter.contains(it.id)) { + it.faceLocation(node.location) + openDialogue(player, it.id, NPC(it.id, it.location)) + } else { + openDialogue(player, NPCs.BANKER_494) + } + } + } + + private fun quickBankBoothUse(player: Player, node: Node, state: Int): Boolean { + if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) { + return true + } + + if (BankerNPC.checkLunarIsleRestriction(player, node)) { + tryInvokeBankerDialogue(player, node) + return true + } + + openBankAccount(player) + return true + } + + 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, state) + } + + tryInvokeBankerDialogue(player, node) + return true + } + + private fun collectBankBoothUse(player: Player, node: Node, state: Int): Boolean { + if (BankerNPC.checkLunarIsleRestriction(player, node)) { + tryInvokeBankerDialogue(player, node) + return true + } + + openGrandExchangeCollectionBox(player) + return true + } + + private fun attemptToConvertItems(player: Player, used: Node, with: Node): Boolean { + if (!hasOption(with, "use")) { + sendMessage(player, "You shouldn't be able to do that with object ${with.id}.") + sendMessage(player, "Please screenshot this and report to the developers.") + + return true + } + + if (!ServerConstants.BANK_BOOTH_NOTE_UIM && player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) { + return true + } + + if (BankerNPC.checkLunarIsleRestriction(player, with)) { + tryInvokeBankerDialogue(player, with) + return true + } + + convertToNotes (used, player) + return true + } + + override fun defineListeners() { + 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) + } + } +} diff --git a/Server/src/main/content/global/handlers/scenery/BankChestListener.kt b/Server/src/main/content/global/handlers/scenery/BankChestListener.kt new file mode 100644 index 000000000..9fb26831b --- /dev/null +++ b/Server/src/main/content/global/handlers/scenery/BankChestListener.kt @@ -0,0 +1,28 @@ +package content.global.handlers.scenery + +import core.api.openBankAccount +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +private val BANK_CHESTS = intArrayOf( + Scenery.BANK_CHEST_3194, Scenery.BANK_CHEST_4483, + Scenery.BANK_CHEST_10562, Scenery.BANK_CHEST_14382, + Scenery.BANK_CHEST_16695, Scenery.BANK_CHEST_16696, + Scenery.BANK_CHEST_21301, Scenery.BANK_CHEST_27662, + Scenery.BANK_CHEST_27663 +) + +/** + * Allows the user to interact with Bank Chests. + * + * @author vddCore + */ +class BankChestListener : InteractionListener { + override fun defineListeners() { + defineInteraction(IntType.SCENERY, BANK_CHESTS, "bank", "use") {player, node, state -> + openBankAccount(player) + 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 new file mode 100644 index 000000000..68579bfb6 --- /dev/null +++ b/Server/src/main/content/global/handlers/scenery/BankDepositBoxListener.kt @@ -0,0 +1,60 @@ +package content.global.handlers.scenery + +import core.api.restrictForIronman +import core.game.component.CloseEvent +import core.game.component.Component +import core.game.container.access.InterfaceContainer +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.IronmanMode +import org.rs09.consts.Components +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +private val BANK_DEPOSIT_BOXES = intArrayOf( + Scenery.BANK_DEPOSIT_BOX_9398, + Scenery.BANK_DEPOSIT_BOX_20228, + Scenery.BANK_DEPOSIT_BOX_25937, + Scenery.BANK_DEPOSIT_BOX_26969, + Scenery.BANK_DEPOSIT_BOX_34755, + Scenery.BANK_DEPOSIT_BOX_36788, + Scenery.BANK_DEPOSIT_BOX_39830 +) + +/** + * Allows the user to interact with bank deposit boxes. + * + * @author vddCore + */ +class BankDepositBoxListener : InteractionListener { + + 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() + return@CloseEvent true + } + + player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6) + InterfaceContainer.generateItems( + player, + player.inventory.toArray(), + arrayOf( + "Examine", + "Deposit-X", + "Deposit-All", + "Deposit-10", + "Deposit-5", + "Deposit-1" + ), 11, 15, 5, 7 + ) + } + + return true + } + + override fun defineListeners() { + 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/DoorManagingPlugin.java b/Server/src/main/content/global/handlers/scenery/DoorManagingPlugin.java new file mode 100644 index 000000000..b6d18c0fa --- /dev/null +++ b/Server/src/main/content/global/handlers/scenery/DoorManagingPlugin.java @@ -0,0 +1,101 @@ +package content.global.handlers.scenery; + +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.node.scenery.SceneryBuilder; +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) + * doors/gates/fences/... + * @author Emperor + */ +@Initializable +public final class DoorManagingPlugin extends OptionHandler { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + SceneryDefinition.setOptionHandler("open", this); + SceneryDefinition.setOptionHandler("close", this); + SceneryDefinition.setOptionHandler("shut", this); + SceneryDefinition.setOptionHandler("go-through", this); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + Scenery object = (Scenery) node; + if (object.getType() != 9 && !player.getLocation().equals(node.getLocation()) && !player.getLocation().isNextTo(object) && object.getName().contains("cupboard")) { + return true; + } + String name = object.getName().toLowerCase(); + 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); + } + 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; + } + return true; + } + if (name.contains("trapdoor") || name.contains("trap door")) { + Location destination = object.getLocation().transform(0, 6400, 0); + if (!RegionManager.isTeleportPermitted(destination)) { + player.getPacketDispatch().sendMessage("This doesn't seem to go anywhere."); + return true; + } + player.getProperties().setTeleportLocation(destination); + return true; + } + if (node.asScenery().getId() == 25341) {// Mithril door + return false; + } + if (!name.contains("door") && !name.contains("gate") && !name.contains("fence") && !name.contains("wall") && !name.contains("exit") && !name.contains("entrance")) { + return false; + } + DoorActionHandler.handleDoor(player, object); + return true; + } + + @Override + public Location getDestination(Node n, Node node) { + Scenery o = (Scenery) node; + if (o.getType() < 4 || o.getType() == 9) { + return DoorActionHandler.getDestination((Player) n, o); + } + return null; + } + +} diff --git a/Server/src/main/java/core/game/interaction/object/FieldPickingPlugin.java b/Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java similarity index 85% rename from Server/src/main/java/core/game/interaction/object/FieldPickingPlugin.java rename to Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java index 551f64776..1d0fa5c07 100644 --- a/Server/src/main/java/core/game/interaction/object/FieldPickingPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java @@ -1,7 +1,8 @@ -package core.game.interaction.object; +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; @@ -11,11 +12,14 @@ 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 rs09.game.world.GameWorld; +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; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the pick option of interactive scenery (non-farming cabbages, @@ -36,6 +40,7 @@ public final class FieldPickingPlugin extends OptionHandler { for(PickingPlant p : PickingPlant.values()){ SceneryDefinition.forId(p.objectId).getHandlers().put("option:pick",this); } + SceneryDefinition.forId(3511).getHandlers().put("option:take-cutting",this); return this; } @@ -63,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) { @@ -88,11 +95,14 @@ public final class FieldPickingPlugin extends OptionHandler { full = object.transform(2073); SceneryBuilder.replace(object, full); } - if (plant == PickingPlant.FUNGI_ON_LOG) { - full = object.transform(3508); + boolean isBloomPlant = plant == PickingPlant.FUNGI_ON_LOG + || plant == PickingPlant.BUDDING_BRANCH + || plant == PickingPlant.GOLDEN_PEAR_BUSH; + if (isBloomPlant) { + full = object.transform(object.getId() - 1); SceneryBuilder.replace(object, full); } - if (plant != PickingPlant.FUNGI_ON_LOG) { + if (!isBloomPlant) { SceneryBuilder.replace(plant == PickingPlant.BANANA_TREE_4 ? full : object, object.transform(banana ? plant.respawn : 0), banana ? 300 : plant.respawn); } if (!plant.name().startsWith("NETTLES")) { @@ -100,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; } }); @@ -120,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); @@ -189,7 +182,8 @@ public final class FieldPickingPlugin extends OptionHandler { ONION_0(3366, 1957, 30), ONION_1(5538, 1957, 30), FUNGI_ON_LOG(3509, 2970, -1), - GOLDEN_PEAR_BUSH(3513, 2974, 30), + BUDDING_BRANCH(3511, 2972, -1), + GOLDEN_PEAR_BUSH(3513, 2974, -1), GLOWING_FUNGUS_0(4932, 4075, 30), GLOWING_FUNGUS_1(4933, 4075, 30), RARE_FLOWERS(5006, 2460, 30), diff --git a/Server/src/main/java/core/game/interaction/object/HaystackPlugin.java b/Server/src/main/content/global/handlers/scenery/HaystackPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/object/HaystackPlugin.java rename to Server/src/main/content/global/handlers/scenery/HaystackPlugin.java index 830fa5053..793e89abc 100644 --- a/Server/src/main/java/core/game/interaction/object/HaystackPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/HaystackPlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.FacialExpression; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/interaction/object/JangerBerryPlugin.java b/Server/src/main/content/global/handlers/scenery/JangerBerryPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/object/JangerBerryPlugin.java rename to Server/src/main/content/global/handlers/scenery/JangerBerryPlugin.java index 0e19af875..5a4d8828c 100644 --- a/Server/src/main/java/core/game/interaction/object/JangerBerryPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/JangerBerryPlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/java/core/game/interaction/object/LadderManagingPlugin.java b/Server/src/main/content/global/handlers/scenery/LadderManagingPlugin.java similarity index 83% rename from Server/src/main/java/core/game/interaction/object/LadderManagingPlugin.java rename to Server/src/main/content/global/handlers/scenery/LadderManagingPlugin.java index 256330a7f..9712fe790 100644 --- a/Server/src/main/java/core/game/interaction/object/LadderManagingPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/LadderManagingPlugin.java @@ -1,7 +1,7 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; +import core.game.global.action.ClimbActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -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/java/core/game/interaction/object/LookAtOptionPlugin.java b/Server/src/main/content/global/handlers/scenery/LookAtOptionPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/object/LookAtOptionPlugin.java rename to Server/src/main/content/global/handlers/scenery/LookAtOptionPlugin.java index b016ca04d..c067e9dec 100644 --- a/Server/src/main/java/core/game/interaction/object/LookAtOptionPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/LookAtOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/global/handlers/scenery/MillingListener.kt b/Server/src/main/content/global/handlers/scenery/MillingListener.kt new file mode 100644 index 000000000..f88132cc1 --- /dev/null +++ b/Server/src/main/content/global/handlers/scenery/MillingListener.kt @@ -0,0 +1,103 @@ +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.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 +private const val EMPTY_POT = Items.EMPTY_POT_1931 +private const val POT_OF_FLOUR = Items.POT_OF_FLOUR_1933 +private const val POT_OF_CORNFLOUR = Items.POT_OF_CORNFLOUR_7468 +private val HOPPERS = intArrayOf(Scenery.HOPPER_36881, Scenery.HOPPER_2717, Scenery.HOPPER_24071, Scenery.HOPPER_2716, Scenery.HOPPER_22422) +private val HOPPER_CONTROLS = intArrayOf(Scenery.HOPPER_CONTROLS_2718, Scenery.HOPPER_CONTROLS_2721, Scenery.HOPPER_CONTROLS_24072, Scenery.HOPPER_CONTROLS_2720, Scenery.LEVER_22424) +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 = Sounds.GIANT_ROC_APPROACHES_3189 + +/** + * Handles interactions with windmills + * @author itsmedoggo + */ +class MillingListener : InteractionListener { + override fun defineListeners() { + defineInteraction(IntType.SCENERY, HOPPER_CONTROLS, "operate", "pull") { player, _, _ -> + useHopperControl(player) + return@defineInteraction true + } + defineInteraction(IntType.SCENERY, FLOUR_BINS, "empty") {player, _, _ -> + fillPot(player) + return@defineInteraction true + } + onUseWith(SCENERY, intArrayOf(GRAIN, SWEETCORN), *HOPPERS) { player, used, _ -> + fillHopper(player, used.asItem()) + return@onUseWith true + } + onUseWith(SCENERY, EMPTY_POT, *FLOUR_BINS) { player, _, _ -> + fillPot(player) + return@onUseWith true + } + } + + private fun useHopperControl(player: Player) { + animate(player, ANIMATION) + val hopperContents = getAttribute(player, "milling:hopper", 0) + if (hopperContents == 0) { + sendMessage(player, "You operate the empty hopper. Nothing interesting happens.") + return + } + setAttribute(player, "/save:milling:hopper", 0) + when (hopperContents) { + GRAIN -> { + setAttribute(player, "/save:milling:grain", (getAttribute(player, "milling:grain", 0) + 1).coerceAtMost(30 - getAttribute(player, "milling:sweetcorn", 0))) + sendMessage(player, "You operate the hopper. The grain slides down the chute.") + } + SWEETCORN -> { + setAttribute(player, "/save:milling:sweetcorn", (getAttribute(player, "milling:sweetcorn", 0) + 1).coerceAtMost(30 - getAttribute(player, "milling:grain", 0))) + sendMessage(player, "You operate the hopper. The sweetcorn slides down the chute.") + } + } + playAudio(player, SOUND) + setVarp(player, VARP, 1, true) + } + + private fun fillHopper(player: Player, used: Item) { + if (getAttribute(player, "milling:hopper", 0) == 0 && removeItem(player, used)) { + setAttribute(player, "/save:milling:hopper", used.id) + sendMessage(player, "You put the " + used.name.lowercase() + " in the hopper.") + return + } + sendMessage(player, "There is already " + getItemName(getAttribute(player, "milling:hopper", 0)).lowercase() + " in the hopper.") + } + + private fun fillPot(player: Player) { + if (!inInventory(player, EMPTY_POT)) { + sendMessage(player, "I need an empty pot to hold the flour in.") + return + } + if (removeItem(player, EMPTY_POT)) { + if (getAttribute(player, "milling:sweetcorn", 0) > 0) { + setAttribute(player, "/save:milling:sweetcorn", (getAttribute(player, "milling:sweetcorn", 0) - 1)) + 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)) + if (addItem(player, POT_OF_FLOUR)) { + sendMessage(player, if (player.getAttribute("milling:grain", 0) > 0) "You fill a pot with flour from the bin." else "You fill a pot with the last of the flour in the bin.") + } + } + if (getAttribute(player, "milling:sweetcorn", 0) + getAttribute(player, "milling:grain", 0) <= 0) { + setVarp(player, VARP, 0, true) + } + } + } +} diff --git a/Server/src/main/java/core/game/interaction/object/ModeratorObject.java b/Server/src/main/content/global/handlers/scenery/ModeratorObject.java similarity index 92% rename from Server/src/main/java/core/game/interaction/object/ModeratorObject.java rename to Server/src/main/content/global/handlers/scenery/ModeratorObject.java index fc0908114..c94f36ae1 100644 --- a/Server/src/main/java/core/game/interaction/object/ModeratorObject.java +++ b/Server/src/main/content/global/handlers/scenery/ModeratorObject.java @@ -1,7 +1,7 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; +import core.game.global.action.ClimbActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/interaction/object/SearchOptionPlugin.java b/Server/src/main/content/global/handlers/scenery/SearchOptionPlugin.java similarity index 98% rename from Server/src/main/java/core/game/interaction/object/SearchOptionPlugin.java rename to Server/src/main/content/global/handlers/scenery/SearchOptionPlugin.java index ce5636bfd..3b2144efb 100644 --- a/Server/src/main/java/core/game/interaction/object/SearchOptionPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/SearchOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; 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/java/core/game/interaction/object/SlashWebPlugin.java b/Server/src/main/content/global/handlers/scenery/SlashWebPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/object/SlashWebPlugin.java rename to Server/src/main/content/global/handlers/scenery/SlashWebPlugin.java index 6824d7f93..70ea5105b 100644 --- a/Server/src/main/java/core/game/interaction/object/SlashWebPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/SlashWebPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.game.container.Container; @@ -18,6 +18,9 @@ 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. @@ -73,6 +76,7 @@ public final class SlashWebPlugin extends OptionHandler { } final boolean success = RandomFunction.random(2) == 1; player.lock(2); + 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/java/core/game/interaction/object/SlayerDangerSignPlugin.java b/Server/src/main/content/global/handlers/scenery/SlayerDangerSignPlugin.java similarity index 94% rename from Server/src/main/java/core/game/interaction/object/SlayerDangerSignPlugin.java rename to Server/src/main/content/global/handlers/scenery/SlayerDangerSignPlugin.java index 633d50bfc..9bc908947 100644 --- a/Server/src/main/java/core/game/interaction/object/SlayerDangerSignPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/SlayerDangerSignPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/interaction/object/SpinSpinningWheelPlugin.java b/Server/src/main/content/global/handlers/scenery/SpinSpinningWheelPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/SpinSpinningWheelPlugin.java rename to Server/src/main/content/global/handlers/scenery/SpinSpinningWheelPlugin.java index 8e1025ed9..3395518b0 100644 --- a/Server/src/main/java/core/game/interaction/object/SpinSpinningWheelPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/SpinSpinningWheelPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; 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/java/core/game/interaction/object/ThievingGuidePlugin.java b/Server/src/main/content/global/handlers/scenery/ThievingGuidePlugin.java similarity index 98% rename from Server/src/main/java/core/game/interaction/object/ThievingGuidePlugin.java rename to Server/src/main/content/global/handlers/scenery/ThievingGuidePlugin.java index 7323331f6..71efac4a5 100644 --- a/Server/src/main/java/core/game/interaction/object/ThievingGuidePlugin.java +++ b/Server/src/main/content/global/handlers/scenery/ThievingGuidePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; @@ -16,7 +16,7 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import java.util.ArrayList; import java.util.Collections; diff --git a/Server/src/main/content/global/skill/AttackListener.kt b/Server/src/main/content/global/skill/AttackListener.kt new file mode 100644 index 000000000..cba5a7e0e --- /dev/null +++ b/Server/src/main/content/global/skill/AttackListener.kt @@ -0,0 +1,24 @@ +package content.global.skill + +import core.game.node.entity.combat.CombatStyle +import core.game.interaction.InteractionListener +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) { + player.sendMessage("You can only attack this with magic.") + return@on true + } + if (npc.id == 7891 && player.getSwingHandler(false).type != CombatStyle.MELEE) { + player.sendMessage("You must use the training sword to attack this.") + return@on true + } + player.attack(npc) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/AgilityCourse.java b/Server/src/main/content/global/skill/agility/AgilityCourse.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/agility/AgilityCourse.java rename to Server/src/main/content/global/skill/agility/AgilityCourse.java index bff5d69dc..9f914ae42 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/AgilityCourse.java +++ b/Server/src/main/content/global/skill/agility/AgilityCourse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.agility; +package content.global.skill.agility; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/AgilityHandler.java b/Server/src/main/content/global/skill/agility/AgilityHandler.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/skill/agility/AgilityHandler.java rename to Server/src/main/content/global/skill/agility/AgilityHandler.java index 4ccca3d9e..c12b7d4fc 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/AgilityHandler.java +++ b/Server/src/main/content/global/skill/agility/AgilityHandler.java @@ -1,16 +1,17 @@ -package core.game.node.entity.skill.agility; +package content.global.skill.agility; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; + +import core.game.node.entity.impl.PulseType; import core.game.node.entity.player.link.TeleportManager; import core.game.node.entity.skill.Skills; import core.game.interaction.MovementPulse; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.impl.ForceMovement; import core.game.node.entity.player.Player; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import kotlin.Unit; +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; @@ -58,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); @@ -102,21 +103,21 @@ public final class AgilityHandler { */ public static void fail(final Player player, int delay, final Location dest, Animation anim, final int hit, final String message) { if (anim != null) { - ContentAPI.animate(player, anim, true); - ContentAPI.submitWorldPulse(new Pulse(ContentAPI.animationDuration(anim), player) { + animate(player, anim, true); + submitWorldPulse(new Pulse(animationDuration(anim), player) { boolean dmg = false; @Override public boolean pulse() { - ContentAPI.teleport(player, dest, TeleportManager.TeleportType.INSTANT); - ContentAPI.animate(player, Animation.RESET, true); + teleport(player, dest, TeleportManager.TeleportType.INSTANT); + animate(player, Animation.RESET, true); if (!dmg) { if (hit > 0) { player.getImpactHandler().setDisabledTicks(0); - ContentAPI.impact(player, hit, HitsplatType.NORMAL); + impact(player, hit, HitsplatType.NORMAL); } if (message != null) { - ContentAPI.sendMessage(player, message); + sendMessage(player, message); } dmg = true; } @@ -138,6 +139,18 @@ 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; + }); + lock(player, ((int) start.getDistance(end)) * 3); ForceMovement movement = new ForceMovement(player, start, end, animation, speed) { @Override public void stop() { @@ -149,8 +162,12 @@ public final class AgilityHandler { player.getSkills().addExperience(Skills.AGILITY, experience, true); } setObstacleFlag(player, courseIndex); + player.logoutListeners.remove("forcewalk"); } }; + if (direction != null) { + movement.setDirection(direction); + } movement.start(); GameWorld.getPulser().submit(movement); return movement; @@ -167,6 +184,11 @@ 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, int delay) { + player.logoutListeners.put("forcewalk", p -> { + p.setLocation(player.getLocation().transform(0,0,0)); + return Unit.INSTANCE; + }); + lock(player, ((int) start.getDistance(end)) * 3); if (delay < 1) { return forceWalk(player, courseIndex, start, end, animation, speed, experience, message); } @@ -181,6 +203,7 @@ public final class AgilityHandler { player.getSkills().addExperience(Skills.AGILITY, experience, true); } setObstacleFlag(player, courseIndex); + player.logoutListeners.remove("forcewalk"); } }; GameWorld.getPulser().submit(new Pulse(delay, player) { @@ -256,7 +279,7 @@ public final class AgilityHandler { walk(player, courseIndex, start, end, animation, experience, message, infiniteRun); return true; } - }, "movement"); + }, PulseType.STANDARD); return; } player.getWalkingQueue().reset(); @@ -264,10 +287,10 @@ public final class AgilityHandler { int ticks = player.getWalkingQueue().getQueue().size(); player.getImpactHandler().setDisabledTicks(ticks); player.lock(1 + ticks); - LogoutTask task = player.getExtension(LogoutTask.class); - if (task == null || !(task instanceof LocationLogoutTask) || !((LocationLogoutTask) task).isValid()) { - player.addExtension(LogoutTask.class, new LocationLogoutTask(1 + ticks, start)); - } + player.logoutListeners.put("agility", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); if (animation != null) { player.getAppearance().setAnimations(animation); } @@ -286,6 +309,7 @@ public final class AgilityHandler { player.getSkills().addExperience(Skills.AGILITY, experience, true); } setObstacleFlag(player, courseIndex); + player.logoutListeners.remove("agility"); return true; } }); diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/AgilityShortcut.java b/Server/src/main/content/global/skill/agility/AgilityShortcut.java similarity index 99% rename from Server/src/main/java/core/game/node/entity/skill/agility/AgilityShortcut.java rename to Server/src/main/content/global/skill/agility/AgilityShortcut.java index 3adb63a89..bd7fae771 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/AgilityShortcut.java +++ b/Server/src/main/content/global/skill/agility/AgilityShortcut.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.agility; +package content.global.skill.agility; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/BarbarianOutpostCourse.kt b/Server/src/main/content/global/skill/agility/BarbarianOutpostCourse.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/agility/BarbarianOutpostCourse.kt rename to Server/src/main/content/global/skill/agility/BarbarianOutpostCourse.kt index 425264cab..0e4a36fea 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/BarbarianOutpostCourse.kt +++ b/Server/src/main/content/global/skill/agility/BarbarianOutpostCourse.kt @@ -1,26 +1,21 @@ -package rs09.game.node.entity.skill.agility +package content.global.skill.agility -import api.ContentAPI +import core.api.* import core.cache.def.impl.ItemDefinition import core.cache.def.impl.NPCDefinition import core.cache.def.impl.SceneryDefinition -import core.game.content.dialogue.DialoguePlugin -import core.game.content.global.action.ClimbActionHandler -import core.game.content.global.action.DoorActionHandler -import core.game.content.quest.miniquest.barcrawl.BarcrawlManager +import content.region.kandarin.barcrawl.BarcrawlManager import core.game.node.Node import core.game.node.scenery.Scenery import core.game.node.entity.combat.ImpactHandler.HitsplatType import core.game.node.entity.player.Player -import core.game.node.entity.skill.agility.AgilityCourse -import core.game.node.entity.skill.agility.AgilityHandler import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import core.plugin.Initializable -import rs09.game.world.GameWorld -import rs09.plugin.PluginManager +import core.game.world.GameWorld +import core.plugin.ClassScanner /** * Handles the barbarian outpost course. @@ -43,10 +38,10 @@ class BarbarianOutpostCourse val id = node.id getCourse(player) when (id) { - 2115, 2116 -> if (!player.barcrawlManager.isFinished || player.barcrawlManager.isStarted) { + 2115, 2116 -> if (!BarcrawlManager.getInstance(player).isFinished || BarcrawlManager.getInstance(player).isStarted) { player.dialogueInterpreter.open(384) } else { - DoorActionHandler.handleAutowalkDoor(player, node as Scenery) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node as Scenery) } 2282 -> handleRopeSwing(player, node as Scenery) 2294 -> handleLogBalance(player, node as Scenery) @@ -55,27 +50,28 @@ class BarbarianOutpostCourse if (player.location.x < node.location.x) { return true } - AgilityHandler.climb(player, 2, ClimbActionHandler.CLIMB_UP, player.location.transform(-1, 0, 1), 8.0, "You climb the netting...") + AgilityHandler.climb(player, 2, core.game.global.action.ClimbActionHandler.CLIMB_UP, player.location.transform(-1, 0, 1), 8.0, "You climb the netting...") } 1948 -> { if (player.location.x > node.location.x) { player.packetDispatch.sendMessage("You cannot climb from this side.") + return true //who missed this return? lol. } val flag = if (node.location == Location(2536, 3553, 0)) 4 else if (node.location == Location(2539, 3553, 0)) 5 else 6 - ContentAPI.sendMessage(player, "You climb the low wall...") + sendMessage(player, "You climb the low wall...") AgilityHandler.forceWalk(player, flag, node.location.transform(-1, 0, 0), node.location.transform(1, 0, 0), Animation.create(839), 10, 13.5, null) } - 455 -> player.barcrawlManager.read() + 455 -> BarcrawlManager.getInstance(player).read() 385 -> { - ContentAPI.sendMessage(player, "The scorpion stings you!") + sendMessage(player, "The scorpion stings you!") player.impactHandler.manualHit(player, 3, HitsplatType.NORMAL) } 386 -> { - ContentAPI.sendMessage(player, "The scorpion stings you!") + sendMessage(player, "The scorpion stings you!") player.impactHandler.manualHit(player, 3, HitsplatType.NORMAL) } 387 -> { - ContentAPI.sendMessage(player, "The scorpion stings you!") + sendMessage(player, "The scorpion stings you!") player.impactHandler.manualHit(player, 3, HitsplatType.NORMAL) } } @@ -89,11 +85,11 @@ class BarbarianOutpostCourse */ private fun handleRopeSwing(player: Player, `object`: Scenery) { if (player.location.y < 3554) { - ContentAPI.sendMessage(player, "You cannot do that from here.") + sendMessage(player, "You cannot do that from here.") return } if (ropeDelay > GameWorld.ticks) { - ContentAPI.sendMessage(player, "The rope is being used.") + sendMessage(player, "The rope is being used.") return } if (AgilityHandler.hasFailed(player, 1, 0.1)) { @@ -113,7 +109,7 @@ class BarbarianOutpostCourse private fun handleLogBalance(player: Player, `object`: Scenery) { val failed = AgilityHandler.hasFailed(player, 1, 0.5) val end = if (failed) Location.create(2545, 3546, 0) else Location.create(2541, 3546, 0) - ContentAPI.sendMessage(player, "You walk carefully across the slippery log...") + sendMessage(player, "You walk carefully across the slippery log...") AgilityHandler.walk(player, if (failed) -1 else 1, Location.create(2551, 3546, 0), end, Animation.create(155), if (failed) 0.0 else 13.5, if (failed) null else "...You make it safely to the other side.") if (failed) { AgilityHandler.walk(player, -1, player.location, Location.create(2545, 3546, 0), Animation.create(155), 0.0, null) @@ -161,7 +157,7 @@ class BarbarianOutpostCourse val failed = AgilityHandler.hasFailed(player, 1, 0.3) val end = if (failed) Location.create(2534, 3547, 1) else Location.create(2532, 3547, 1) AgilityHandler.walk(player, if (failed) -1 else 3, Location.create(2536, 3547, 1), end, Animation.create(157), if (failed) 0.0 else 22.0, if (failed) null else "You skillfully edge across the gap.") - ContentAPI.sendMessage(player, "You put your foot on the ledge and try to edge across..") + sendMessage(player, "You put your foot on the ledge and try to edge across..") if (failed) { AgilityHandler.fail(player, 3, Location.create(2534, 3545, 0), Animation(760), getHitAmount(player), "You slip and fall to the pit below.") return @@ -180,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 - PluginManager.definePlugin(BarbarianGuardDialogue()) + ClassScanner.definePlugin(BarbarianGuardDialogue()) } override fun getDestination(node: Node, n: Node): Location? { @@ -196,7 +192,7 @@ class BarbarianOutpostCourse * The barbarian guard dialogue plugin. * @author Woah */ - class BarbarianGuardDialogue : DialoguePlugin { + class BarbarianGuardDialogue : core.game.dialogue.DialoguePlugin { /** * Constructs a new `BarbarianGuardDialogue` `Object`. */ @@ -212,14 +208,14 @@ class BarbarianOutpostCourse */ constructor(player: Player?) : super(player) {} - override fun newInstance(player: Player): DialoguePlugin { + override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { return BarbarianGuardDialogue(player) } override fun open(vararg args: Any): Boolean { - if (!player.barcrawlManager.isStarted) { + if (!BarcrawlManager.getInstance(player).isStarted) { npc("O, waddya want?") - } else if (player.barcrawlManager.isFinished && !player.barcrawlManager.isStarted) { + } else if (BarcrawlManager.getInstance(player).isFinished && !BarcrawlManager.getInstance(player).isStarted) { npc("'Ello friend.") stage = 50 } else { @@ -250,7 +246,7 @@ class BarbarianOutpostCourse stage++ } 4 -> end() - 5 -> if (player.barcrawlManager.isFinished) { + 5 -> if (BarcrawlManager.getInstance(player).isFinished) { npc("You may pass if you like. You are a true", "barbarian now.") stage = 4 } else { @@ -280,8 +276,8 @@ class BarbarianOutpostCourse stage++ } 10 -> { - player.barcrawlManager.reset() - player.barcrawlManager.isStarted = true + BarcrawlManager.getInstance(player).reset() + BarcrawlManager.getInstance(player).isStarted = true player.inventory.add(BarcrawlManager.BARCRAWL_CARD, player) interpreter.sendDialogue("The guard hands you a Barcrawl card.") stage++ @@ -295,10 +291,10 @@ class BarbarianOutpostCourse stage++ } 13 -> end() - 20 -> if (!player.barcrawlManager.hasCard()) { + 20 -> if (!BarcrawlManager.getInstance(player).hasCard()) { player("I've lost my barcrawl card...") stage = 23 - } else if (player.barcrawlManager.isFinished) { + } else if (BarcrawlManager.getInstance(player).isFinished) { player("I tink I jusht 'bout done dem all... but I losht count...") stage = 24 } else { @@ -318,7 +314,7 @@ class BarbarianOutpostCourse if (!player.inventory.containsItem(BarcrawlManager.BARCRAWL_CARD)) { end() } - player.barcrawlManager.isStarted = false + BarcrawlManager.getInstance(player).isStarted = false player.bank.remove(BarcrawlManager.BARCRAWL_CARD) player.inventory.remove(BarcrawlManager.BARCRAWL_CARD) interpreter.sendDialogue("You give the card to the barbarian.") diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/GnomeStrongholdCourse.kt b/Server/src/main/content/global/skill/agility/GnomeStrongholdCourse.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/agility/GnomeStrongholdCourse.kt rename to Server/src/main/content/global/skill/agility/GnomeStrongholdCourse.kt index defb0c574..d34a40d61 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/GnomeStrongholdCourse.kt +++ b/Server/src/main/content/global/skill/agility/GnomeStrongholdCourse.kt @@ -1,19 +1,15 @@ -package rs09.game.node.entity.skill.agility +package content.global.skill.agility -import api.ContentAPI +import core.api.* import core.cache.def.impl.SceneryDefinition import core.game.node.Node import core.game.node.scenery.Scenery import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.skill.agility.AgilityCourse -import core.game.node.entity.skill.agility.AgilityHandler -import core.game.system.task.LocationLogoutTask -import core.game.system.task.LogoutTask import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.plugin.Initializable -import rs09.game.world.GameWorld +import core.game.world.GameWorld /** * Handles the gnome stronghold agility course. @@ -35,41 +31,41 @@ class GnomeStrongholdCourse when (`object`.id) { 2295 -> { TRAINERS[0]!!.sendChat("Okay get over that log, quick quick!") - ContentAPI.sendMessage(player, "You walk carefully across the slippery log...") + 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 -> { TRAINERS[1]!!.sendChat("Move it, move it, move it!") - ContentAPI.sendMessage(player, "You climb the netting...") + 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 -> { TRAINERS[2]!!.sendChat("That's it - straight up.") - ContentAPI.sendMessage(player, "You climb the tree..") + 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 -> { TRAINERS[3]!!.sendChat("Come on scaredy cat, get across that rope!") - ContentAPI.sendMessage(player, "You carefully cross the tightrope.") + 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 } 4059 -> { - ContentAPI.sendMessage(player, "You can't do that from here.") + sendMessage(player, "You can't do that from here.") return true } 2314, 2315 -> { - ContentAPI.sendMessage(player, "You climb down the tree..") + sendMessage(player, "You climb down the tree..") AgilityHandler.climb(player, 4, Animation.create(828), Location.create(2487, 3420, 0), 5.0, "You land on the ground.") return true } 2286 -> { TRAINERS[4]!!.sendChat("My Granny can move faster than you.") player.faceLocation(player.location.transform(0, 2, 0)) - ContentAPI.sendMessage(player, "You climb the netting...") + sendMessage(player, "You climb the netting...") AgilityHandler.climb(player, 5, Animation.create(828), player.location.transform(0, 2, 0), 7.5, null) return true } @@ -77,11 +73,11 @@ class GnomeStrongholdCourse val index = if (`object`.id == 154) 0 else 1 //If the player clicks on the left pipe, set index to 0, otherwise 1 val x = 2484 + index * 3 //change the x coordinates for walking/animations depending on index multiplier if (`object`.location.y == 3435) { - ContentAPI.sendMessage(player, "You can't do that from here.") + sendMessage(player, "You can't do that from here.") return true } if (USED_PIPES[index] > GameWorld.ticks) { - ContentAPI.sendMessage(player, "The pipe is being used.") + sendMessage(player, "The pipe is being used.") return true } USED_PIPES[index] = GameWorld.ticks + 10 @@ -94,7 +90,6 @@ class GnomeStrongholdCourse AgilityHandler.forceWalk(player, -1, Location.create(x, 3433, 0), Location.create(x, 3435, 0), Animation.create(844), 10, 0.0, null, 5) player.lock() AgilityHandler.forceWalk(player, 6, Location.create(x, 3435, 0), Location.create(x, 3437, 0), Animation.create(10579), 20, 7.5, null, 8) - player.addExtension(LogoutTask::class.java, LocationLogoutTask(12, Location.create(x, 3430, 0))) return true } } diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/WildernessCourse.kt b/Server/src/main/content/global/skill/agility/WildernessCourse.kt similarity index 80% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/agility/WildernessCourse.kt rename to Server/src/main/content/global/skill/agility/WildernessCourse.kt index 3db6a78c0..d48d00a1a 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/WildernessCourse.kt +++ b/Server/src/main/content/global/skill/agility/WildernessCourse.kt @@ -1,21 +1,19 @@ -package rs09.game.node.entity.skill.agility +package content.global.skill.agility -import api.ContentAPI +import core.api.* import core.cache.def.impl.SceneryDefinition -import core.game.content.global.action.DoorActionHandler +import core.game.interaction.QueueStrength import core.game.node.Node import core.game.node.scenery.Scenery 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.skill.agility.AgilityCourse -import core.game.node.entity.skill.agility.AgilityHandler 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 rs09.game.world.GameWorld +import core.game.world.GameWorld /** * Handles the wilderness agility course. @@ -34,7 +32,7 @@ class WildernessCourse when (`object`.id) { 2309 -> handleEntrance(player, `object`) 2307, 2308 -> { - DoorActionHandler.handleAutowalkDoor(player, `object`) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, `object`) handleEntranceObstacle(player, `object`) } 2288 -> handlePipe(player, `object`) @@ -53,7 +51,7 @@ class WildernessCourse */ private fun handleEntrance(player: Player, `object`: Scenery) { if (player.location.y > 3916 || player.skills.getLevel(Skills.AGILITY) >= 52) { - DoorActionHandler.handleAutowalkDoor(player, `object`) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, `object`) if (player.location.y <= 3916) { handleEntranceObstacle(player, `object`) } @@ -76,7 +74,7 @@ class WildernessCourse 2 -> { val end = if (fail) Location.create(2998, 3924, 0) else if (`object`.id < 2309) Location.create(2998, 3917, 0) else Location.create(2998, 3930, 0) val start = if (`object`.id < 2309) player.location else Location.create(2998, 3917, 0) - ContentAPI.sendMessage(player, "You go through the gate and try to edge over the ridge...") + sendMessage(player, "You go through the gate and try to edge over the ridge...") AgilityHandler.walk(player, -1, start, end, Animation.create(155), if (fail) 0.0 else 15.00, if (fail) "You lose your footing and fail into the wolf pit." else "You skillfully balance across the ridge...") } 9 -> { @@ -88,7 +86,7 @@ class WildernessCourse 15 -> player.lock(3) 16 -> { val doorLoc = if (`object`.id < 2309) Location(2998, 3917, 0) else Location(2998, 3931, 0) - DoorActionHandler.handleAutowalkDoor(player, RegionManager.getObject(doorLoc)) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, RegionManager.getObject(doorLoc)) return true } } @@ -104,7 +102,7 @@ class WildernessCourse */ private fun handlePipe(player: Player, `object`: Scenery) { if (`object`.location.y == 3948) { - ContentAPI.sendMessage(player, "You can't do that from here.") + sendMessage(player, "You can't do that from here.") return } if (player.skills.getLevel(Skills.AGILITY) < 49) { @@ -119,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,11 +145,11 @@ class WildernessCourse */ private fun handleRopeSwing(player: Player, `object`: Scenery) { if (player.location.y < 3554) { - ContentAPI.sendMessage(player, "You cannot do that from here.") + sendMessage(player, "You cannot do that from here.") return } if (ropeDelay > GameWorld.ticks) { - ContentAPI.sendMessage(player, "The rope is being used.") + sendMessage(player, "The rope is being used.") return } if (AgilityHandler.hasFailed(player, 1, 0.1)) { @@ -175,27 +167,29 @@ class WildernessCourse * @param object the object. */ private fun handleSteppingStones(player: Player, `object`: Scenery) { - ContentAPI.lock(player, 50) + lock(player, 50) val fail = AgilityHandler.hasFailed(player, 1, 0.3) - val origLoc = player.location - ContentAPI.registerLogoutListener(player, "steppingstone"){p -> - ContentAPI.teleport(p, origLoc) - } - ContentAPI.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){ - ContentAPI.unlock(player) - ContentAPI.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) + } } /** @@ -206,7 +200,7 @@ class WildernessCourse private fun handleLogBalance(player: Player, `object`: Scenery) { val failed = AgilityHandler.hasFailed(player, 1, 0.5) val end = if (failed) Location.create(2998, 3945, 0) else Location.create(2994, 3945, 0) - ContentAPI.sendMessage(player, "You walk carefully across the slippery log...") + sendMessage(player, "You walk carefully across the slippery log...") AgilityHandler.walk(player, if (failed) -1 else 3, player.location, end, Animation.create(155), if (failed) 0.0 else 20.0, if (failed) null else "You skillfully edge across the gap.") if (failed) { GameWorld.Pulser.submit(object : Pulse(5, player) { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java b/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java new file mode 100644 index 000000000..5303bde7d --- /dev/null +++ b/Server/src/main/content/global/skill/agility/brimhaven/BladeTrap.java @@ -0,0 +1,63 @@ +package content.global.skill.agility.brimhaven; + +import content.global.skill.agility.AgilityHandler; +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; +import core.game.world.map.Location; +import core.game.world.map.RegionManager; +import core.game.world.update.flag.context.Animation; + +/** + * Handles the blade trap. + * @author Emperor + */ +public final class BladeTrap implements MovementHook { + + @Override + public boolean handle(Entity e, final Location l) { + if (BrimhavenArena.sawBladeActive) { + 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()) + { + e.asPlayer().logoutListeners.put("blade-trap", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); + } + GameWorld.getPulser().submit(new Pulse(2, e) { + @Override + public boolean pulse() { + Direction direction = dir; + Direction d = Direction.get(direction.toInteger() + 2 & 3); + if (RegionManager.getObject(player.getLocation().transform(dir)) != null) { + Direction s = d; + d = direction; + direction = s; + } + Location loc = player.getLocation(); + AgilityHandler.failWalk(player, 1, loc, loc.transform(direction), loc.transform(direction), Animation.create(846), 10, 3, "You were hit by the saw blade!").setDirection(d); + player.logoutListeners.remove("blade-trap"); + return true; + } + }); + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/BrimhavenArena.java b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/BrimhavenArena.java rename to Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java index e11430740..13a6a4ad6 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/BrimhavenArena.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.agility.brimhaven; +package content.global.skill.agility.brimhaven; import core.game.component.Component; -import core.game.content.global.action.ClimbActionHandler; +import core.game.global.action.ClimbActionHandler; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -15,7 +15,7 @@ import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.*; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBuilder; @@ -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/java/core/game/node/entity/skill/agility/brimhaven/BrimhavenCourse.java b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/BrimhavenCourse.java rename to Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java index 138941e50..adb8f14fd 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/BrimhavenCourse.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenCourse.java @@ -1,18 +1,17 @@ -package core.game.node.entity.skill.agility.brimhaven; +package content.global.skill.agility.brimhaven; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; 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.scenery.Scenery; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import kotlin.Unit; +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; @@ -120,10 +119,15 @@ 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); - player.addExtension(LogoutTask.class, new LocationLogoutTask(22, start)); + player.logoutListeners.put("brimcourse", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); GameWorld.getPulser().submit(new Pulse(3, player) { Location last = start.transform(dir); int count; @@ -134,18 +138,19 @@ public final class BrimhavenCourse extends OptionHandler { if (AgilityHandler.hasFailed(player, 1, 0.15)) { player.getAppearance().setAnimations(); player.getAppearance().sync(); - LogoutTask task = player.getExtension(LogoutTask.class); - task.setDelay(2); AgilityHandler.fail(player, 2, last.transform(0, 0, -3), Animation.create(1119 + m), getHitAmount(player), "You missed a hand hold!"); + player.logoutListeners.remove("brimcourse"); return true; } } 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; } - AgilityHandler.forceWalk(player, -1, last, last = last.transform(dir), Animation.create(1118 + m), 5, 0.0, null).setDirection(faceDirection); + player.logoutListeners.remove("brimcourse"); + AgilityHandler.forceWalk(player, -1, last, last = last.transform(dir), Animation.create(1118 + m), 5, 0.0, null, faceDirection); return false; } }); @@ -186,7 +191,10 @@ public final class BrimhavenCourse extends OptionHandler { player.getAppearance().setAnimations(Animation.create(745)); final Location start = player.getLocation(); ForceMovement.run(player, start.transform(dir), start.transform(dir.getStepX() << 1, dir.getStepY() << 1, 0), Animation.create(742), Animation.create(744)); - player.addExtension(LogoutTask.class, new LocationLogoutTask(5, start)); + player.logoutListeners.put("brimcourse", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); GameWorld.getPulser().submit(new Pulse(2, player) { boolean failed; int count; @@ -201,14 +209,15 @@ public final class BrimhavenCourse extends OptionHandler { } setDelay(7); AgilityHandler.walk(player, -1, player.getLocation().transform(dir), player.getLocation().transform(dir.getStepX() * 7, dir.getStepY() * 7, 0), Animation.create(662), 0.0, null); - player.addExtension(LogoutTask.class, new LocationLogoutTask(9, start)); } else if (count == 2) { if (failed) { player.getAppearance().setAnimations(); player.getAppearance().sync(); AgilityHandler.fail(player, 2, player.getLocation().transform(0, 0, -3), Animation.create(768), getHitAmount(player), "You missed a hand hold!"); + player.logoutListeners.remove("brimcourse"); return true; } + player.logoutListeners.remove("brimcourse"); AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(dir), Animation.create(743), 10, getExp(player, 14.0), null); return true; } @@ -265,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; } @@ -273,10 +282,9 @@ public final class BrimhavenCourse extends OptionHandler { return true; } }); - player.addExtension(LogoutTask.class, new LocationLogoutTask(8, start)); 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); } /** @@ -288,7 +296,10 @@ public final class BrimhavenCourse extends OptionHandler { final Direction dir = Direction.getLogicalDirection(player.getLocation(), object.getLocation()); AgilityHandler.forceWalk(player, -1, player.getLocation(), object.getLocation(), Animation.create(741), 10, 0, null); final Location start = player.getLocation(); - player.addExtension(LogoutTask.class, new LocationLogoutTask(12, start)); + player.logoutListeners.put("brimcourse", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); player.lock(12); GameWorld.getPulser().submit(new Pulse(2, player) { int count = 0; @@ -300,9 +311,11 @@ public final class BrimhavenCourse extends OptionHandler { player.unlock(); player.lock(2); AgilityHandler.fail(player, 2, player.getLocation().transform(d.getStepX() << 1, d.getStepY() << 1, -3), Animation.create(764), getHitAmount(player), "You lost your balance!"); + player.logoutListeners.remove("brimcourse"); return true; } AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(dir), Animation.create(741), 10, count == 5 ? getExp(player, 18.0) : 0, null); + player.logoutListeners.remove("brimcourse"); return ++count == 6; } }); @@ -378,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/java/core/game/node/entity/skill/agility/brimhaven/DartTrap.java b/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/DartTrap.java rename to Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java index 9c92be1b4..3ac7da4a3 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/DartTrap.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/DartTrap.java @@ -1,15 +1,16 @@ -package core.game.node.entity.skill.agility.brimhaven; +package content.global.skill.agility.brimhaven; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.node.entity.Entity; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; @@ -30,8 +31,17 @@ 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); - e.addExtension(LogoutTask.class, new LocationLogoutTask(13, start)); + if(e.isPlayer()) + { + e.asPlayer().logoutListeners.put("dart-trap", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); + } final Location startProj = l.transform(dir.getStepX() * 5, dir.getStepY() * 5, 0); GameWorld.getPulser().submit(new Pulse(2, e) { boolean failed; @@ -77,12 +87,14 @@ public final class DartTrap implements MovementHook { if (failed) { player.getPacketDispatch().sendMessage("You were hit by some darts, something on them makes you feel dizzy!"); player.getSkills().updateLevel(Skills.AGILITY, -(2 + RandomFunction.randomize(2)), 0); + player.logoutListeners.remove("dart-trap"); return true; } setDelay(2); AgilityHandler.walk(player, -1, l, l.transform(dir.getStepX() << 1, dir.getStepY() << 1, 0), null, 30.0, null); } else if (count == 4) { PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.RESET, 0, 0, 0, 0, 0)); + player.logoutListeners.remove("dart-trap"); return true; } return false; diff --git a/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java b/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java new file mode 100644 index 000000000..2885a7f6a --- /dev/null +++ b/Server/src/main/content/global/skill/agility/brimhaven/FloorSpikes.java @@ -0,0 +1,65 @@ +package content.global.skill.agility.brimhaven; + +import core.game.node.entity.skill.Skills; +import content.global.skill.agility.AgilityHandler; +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; +import core.game.world.map.Location; +import core.game.world.map.RegionManager; +import core.game.world.update.flag.context.Animation; + +/** + * Handles floor spikes trap. + * @author Emperor + */ +public final class FloorSpikes implements MovementHook { + + @Override + public boolean handle(Entity e, final Location l) { + 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()) + { + ((Player) e).logoutListeners.put("floor-spikes", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); + } + GameWorld.getPulser().submit(new Pulse(3, e) { + @Override + public boolean pulse() { + player.getPacketDispatch().sendSceneryAnimation(RegionManager.getObject(l), Animation.create(1111)); + if (AgilityHandler.hasFailed(player, 20, 0.25)) { + if (player.getSkills().getLevel(Skills.AGILITY) < 20) { + player.getPacketDispatch().sendMessage("You need an agility of at least 20 to get past this trap!"); + } + int hit = player.getSkills().getLifepoints() / 12; + if (hit < 2) { + hit = 2; + } + AgilityHandler.failWalk(player, 1, player.getLocation(), start, start, Animation.create(1114), 10, hit, "You were hit by some floor spikes!").setDirection(dir); + ; + } else { + AgilityHandler.forceWalk(player, -1, l, l.transform(dir.getStepX() << 1, dir.getStepY() << 1, 0), Animation.create(1115), 20, 26, null); + } + player.logoutListeners.remove("floor-spikes"); + return true; + } + }); + return false; + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java b/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java new file mode 100644 index 000000000..d27a76bcf --- /dev/null +++ b/Server/src/main/content/global/skill/agility/brimhaven/PressurePad.java @@ -0,0 +1,66 @@ +package content.global.skill.agility.brimhaven; + +import core.game.node.entity.skill.Skills; +import content.global.skill.agility.AgilityHandler; +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; +import core.game.world.map.Location; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; + +/** + * Handles the pressure pad trap. + * @author Emperor + */ +public final class PressurePad implements MovementHook { + + @Override + public boolean handle(Entity e, final Location dest) { + 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()) + { + ((Player) e).logoutListeners.put("pressure-pad", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); + } + GameWorld.getPulser().submit(new Pulse(3, e) { + @Override + public boolean pulse() { + Graphics.send(Graphics.create(271), dest); + if (AgilityHandler.hasFailed(player, 20, 0.25)) { + if (player.getSkills().getLevel(Skills.AGILITY) < 20) { + player.getPacketDispatch().sendMessage("You need an agility of at least 20 to get past this trap!"); + } + int hit = player.getSkills().getLifepoints() / 12; + if (hit < 2) { + hit = 2; + } + AgilityHandler.failWalk(player, 1, player.getLocation(), start, start, Animation.create(1114), 10, hit, "You were hit by some falling rocks!").setDirection(dir); + ; + } else { + AgilityHandler.forceWalk(player, -1, dest, dest.transform(dir.getStepX() << 1, dir.getStepY() << 1, 0), Animation.create(1115), 20, 26, null); + } + player.logoutListeners.remove("pressure-pad"); + return true; + } + }); + return false; + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/SpinningBlades.java b/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java similarity index 77% rename from Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/SpinningBlades.java rename to Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java index 673a3dfd7..3d2a9c1e2 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/SpinningBlades.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/SpinningBlades.java @@ -1,14 +1,16 @@ -package core.game.node.entity.skill.agility.brimhaven; +package content.global.skill.agility.brimhaven; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; import core.game.world.map.Location; import core.game.world.map.RegionManager; @@ -25,8 +27,17 @@ 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); - e.addExtension(LogoutTask.class, new LocationLogoutTask(5, start)); + if(e.isPlayer()) + { + ((Player) e).logoutListeners.put("spin-blades", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); + } GameWorld.getPulser().submit(new Pulse(3, e) { @Override public boolean pulse() { @@ -43,6 +54,7 @@ public final class SpinningBlades implements MovementHook { } else { AgilityHandler.forceWalk(player, -1, l, l.transform(dir.getStepX() << 1, dir.getStepY() << 1, 0), Animation.create(1115), 20, 26, null); } + player.logoutListeners.remove("spin-blades"); return true; } }); @@ -64,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/java/core/game/node/entity/skill/agility/pyramid/AgilityPyramidCourse.java b/Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidCourse.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/agility/pyramid/AgilityPyramidCourse.java rename to Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidCourse.java index 9dbf026c7..06cae7b1b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/pyramid/AgilityPyramidCourse.java +++ b/Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidCourse.java @@ -1,23 +1,26 @@ -package core.game.node.entity.skill.agility.pyramid; +package content.global.skill.agility.pyramid; import core.cache.def.impl.VarbitDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; +import core.game.global.action.ClimbActionHandler; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityCourse; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityCourse; +import content.global.skill.agility.AgilityHandler; 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.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the agility pryamid course. @@ -93,8 +96,8 @@ public final class AgilityPyramidCourse extends AgilityCourse { SceneryDefinition.forId(10858).getHandlers().put("option:climb-down", this); SceneryDefinition.forId(10865).getHandlers().put("option:climb-over", this); RollingBlock.BlockSets.values(); - PluginManager.definePlugin(new MovingBlockNPC()); - PluginManager.definePlugin(new AgilityPyramidZone()); + ClassScanner.definePlugin(new MovingBlockNPC()); + ClassScanner.definePlugin(new AgilityPyramidZone()); } @Override @@ -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; } }); @@ -497,9 +512,9 @@ public final class AgilityPyramidCourse extends AgilityCourse { */ public static void addConfig(final Player player, final int objectId, final int value, boolean save) { final VarbitDefinition definition = VarbitDefinition.forObjectID(SceneryDefinition.forId(objectId).getVarbitID()); - final int oldVal = (definition.getValue(player) << definition.getBitShift()); - final int newVal = (value << definition.getBitShift()); - player.getConfigManager().set(CONFIG_ID, (player.getConfigManager().get(CONFIG_ID) - oldVal) + newVal, save); + final int oldVal = (definition.getValue(player) << definition.getStartBit()); + final int newVal = (value << definition.getStartBit()); + setVarp(player, CONFIG_ID, (getVarp(player, CONFIG_ID) - oldVal) + newVal, save); } /** diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/pyramid/AgilityPyramidZone.java b/Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidZone.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/agility/pyramid/AgilityPyramidZone.java rename to Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidZone.java index a47bc0d98..f3247e7ca 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/pyramid/AgilityPyramidZone.java +++ b/Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidZone.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.agility.pyramid; +package content.global.skill.agility.pyramid; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/pyramid/MovingBlockNPC.java b/Server/src/main/content/global/skill/agility/pyramid/MovingBlockNPC.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/agility/pyramid/MovingBlockNPC.java rename to Server/src/main/content/global/skill/agility/pyramid/MovingBlockNPC.java index 6c96397ef..984fd532b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/pyramid/MovingBlockNPC.java +++ b/Server/src/main/content/global/skill/agility/pyramid/MovingBlockNPC.java @@ -1,16 +1,19 @@ -package core.game.node.entity.skill.agility.pyramid; +package content.global.skill.agility.pyramid; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; 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.MovementHook; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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 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/java/core/game/node/entity/skill/agility/pyramid/RollingBlock.java b/Server/src/main/content/global/skill/agility/pyramid/RollingBlock.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/skill/agility/pyramid/RollingBlock.java rename to Server/src/main/content/global/skill/agility/pyramid/RollingBlock.java index 82960b3da..7573889aa 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/pyramid/RollingBlock.java +++ b/Server/src/main/content/global/skill/agility/pyramid/RollingBlock.java @@ -1,18 +1,20 @@ -package core.game.node.entity.skill.agility.pyramid; +package content.global.skill.agility.pyramid; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.MovementHook; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import kotlin.Unit; +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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the rolling block obstacle. @@ -30,7 +32,16 @@ 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); - e.addExtension(LogoutTask.class, new LocationLogoutTask(5, player.getLocation())); + playAudio(player, Sounds.PYRAMID_SCRAPE_1396); + playAudio(player, Sounds.LAND_FLAT_2469, 40); + + if(e.isPlayer()) + { + ((Player) e).logoutListeners.put("rolling-block", p -> { + p.setLocation(e.getLocation().transform(0,0,0)); + return Unit.INSTANCE; + }); + } if (fail) { GameWorld.getPulser().submit(new Pulse(1, player) { int counter; @@ -49,6 +60,7 @@ public final class RollingBlock implements MovementHook { break; case 3: AgilityPyramidCourse.addConfig(player, stone, 0, true); + player.logoutListeners.remove("rolling-block"); return true; } return false; diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/AlKharidPitShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/AlKharidPitShortcut.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/AlKharidPitShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/AlKharidPitShortcut.java index 866a50f37..76ada4b44 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/AlKharidPitShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/AlKharidPitShortcut.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; import core.plugin.Initializable; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityShortcut; import core.game.node.Node; import core.game.node.entity.impl.ForceMovement; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/BarSqueezeShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/BarSqueezeShortcut.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/BarSqueezeShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/BarSqueezeShortcut.java index adf8651ec..efe0f727f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/BarSqueezeShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/BarSqueezeShortcut.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; -import core.game.node.entity.skill.agility.AgilityHandler; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityHandler; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.world.map.Direction; @@ -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/kotlin/rs09/game/node/entity/skill/agility/shortcuts/BasaltRockShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/BasaltRockShortcut.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/BasaltRockShortcut.kt rename to Server/src/main/content/global/skill/agility/shortcuts/BasaltRockShortcut.kt index 61f29e60a..2bc194611 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/BasaltRockShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/BasaltRockShortcut.kt @@ -1,15 +1,14 @@ -package rs09.game.node.entity.skill.agility.shortcuts +package content.global.skill.agility.shortcuts import core.game.node.Node import core.game.node.scenery.Scenery import core.game.node.entity.player.Player -import core.game.node.entity.skill.agility.AgilityHandler -import core.game.node.entity.skill.agility.AgilityShortcut +import content.global.skill.agility.AgilityHandler +import content.global.skill.agility.AgilityShortcut import core.game.system.task.Pulse -import rs09.game.world.GameWorld +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 @@ -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/java/core/game/node/entity/skill/agility/shortcuts/CrumblingWallShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/CrumblingWallShortcut.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/CrumblingWallShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/CrumblingWallShortcut.java index c43893d5c..bc730d1df 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/CrumblingWallShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/CrumblingWallShortcut.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityShortcut; import core.game.node.entity.impl.ForceMovement; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/FenceJumpShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/FenceJumpShortcut.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/FenceJumpShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/FenceJumpShortcut.java index 072d3f0e6..f57c41408 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/FenceJumpShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/FenceJumpShortcut.java @@ -1,13 +1,13 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityShortcut; 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.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/LogBalanceShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/LogBalanceShortcut.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/LogBalanceShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/LogBalanceShortcut.java index 2fbd925ad..9bb39507e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/LogBalanceShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/LogBalanceShortcut.java @@ -1,9 +1,9 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; import core.game.node.entity.player.link.diary.DiaryType; import core.plugin.Initializable; -import core.game.node.entity.skill.agility.AgilityHandler; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityShortcut; import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/MonkeyBarShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/MonkeyBarShortcut.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/MonkeyBarShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/MonkeyBarShortcut.java index b1a1014db..673d27781 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/MonkeyBarShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/MonkeyBarShortcut.java @@ -1,16 +1,15 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; import core.plugin.Initializable; -import core.game.node.entity.skill.agility.AgilityHandler; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityShortcut; import core.game.node.Node; 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.LocationLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import kotlin.Unit; +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; @@ -74,7 +73,10 @@ public class MonkeyBarShortcut extends AgilityShortcut { final Location start = player.getLocation(); final Direction dir = direct; ForceMovement.run(player, start.transform(dir), start.transform(dir.getStepX() << 1, dir.getStepY() << 1, 0), Animation.create(742), Animation.create(744)); - player.addExtension(LogoutTask.class, new LocationLogoutTask(5, start)); + player.logoutListeners.put("monkey-bar", p -> { + p.setLocation(start); + return Unit.INSTANCE; + }); GameWorld.getPulser().submit(new Pulse(2, player) { int count; boolean failed; @@ -89,15 +91,16 @@ public class MonkeyBarShortcut extends AgilityShortcut { } setDelay(4); AgilityHandler.walk(player, -1, player.getLocation().transform(dir), player.getLocation().transform(dir.getStepX() * 4, dir.getStepY() * 4, 0), Animation.create(662), 0.0, null); - player.addExtension(LogoutTask.class, new LocationLogoutTask(7, start)); } else if (count == 2) { if (failed) { player.getAppearance().setAnimations(); player.getAppearance().sync(); AgilityHandler.fail(player, 2, new Location(2599, 9564, 0), Animation.create(768), RandomFunction.random(1, 3), null); + player.logoutListeners.remove("monkey-bar"); return true; } AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(dir), Animation.create(743), 10, getExperience(), null); + player.logoutListeners.remove("monkey-bar"); return true; } return false; diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/PipeShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/PipeShortcut.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/PipeShortcut.kt rename to Server/src/main/content/global/skill/agility/shortcuts/PipeShortcut.kt index 8ed3d0bbc..f120546f7 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/PipeShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/PipeShortcut.kt @@ -1,12 +1,12 @@ -package rs09.game.node.entity.skill.agility.shortcuts +package content.global.skill.agility.shortcuts import core.game.node.scenery.Scenery import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.entity.skill.agility.AgilityHandler -import core.game.node.entity.skill.agility.AgilityShortcut +import content.global.skill.agility.AgilityHandler +import content.global.skill.agility.AgilityShortcut import core.game.system.task.Pulse -import rs09.game.world.GameWorld +import core.game.world.GameWorld import core.game.world.update.flag.context.Animation import core.plugin.Initializable import core.plugin.Plugin @@ -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/kotlin/rs09/game/node/entity/skill/agility/shortcuts/RockClimbShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/RockClimbShortcut.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/RockClimbShortcut.kt rename to Server/src/main/content/global/skill/agility/shortcuts/RockClimbShortcut.kt index eaad2f461..509023e68 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/RockClimbShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/RockClimbShortcut.kt @@ -1,10 +1,10 @@ -package rs09.game.node.entity.skill.agility.shortcuts +package content.global.skill.agility.shortcuts import core.game.node.scenery.Scenery import core.game.node.entity.impl.ForceMovement import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.agility.AgilityShortcut +import content.global.skill.agility.AgilityShortcut import core.game.world.map.Direction import core.game.world.map.Location import core.game.world.update.flag.context.Animation diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/RopeSwing.java b/Server/src/main/content/global/skill/agility/shortcuts/RopeSwing.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/RopeSwing.java rename to Server/src/main/content/global/skill/agility/shortcuts/RopeSwing.java index d57f8116d..e6853fe0c 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/RopeSwing.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/RopeSwing.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -8,13 +8,13 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; @Initializable 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/kotlin/rs09/game/node/entity/skill/agility/shortcuts/SteppingStoneShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt similarity index 79% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/SteppingStoneShortcut.kt rename to Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt index ce61a356b..035ed53d7 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/agility/shortcuts/SteppingStoneShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt @@ -1,7 +1,9 @@ -package rs09.game.node.entity.skill.agility.shortcuts +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.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/java/core/game/node/entity/skill/agility/shortcuts/StrangeFloorShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/StrangeFloorShortcut.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/StrangeFloorShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/StrangeFloorShortcut.java index 9a21ec827..cf0d784e1 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/StrangeFloorShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/StrangeFloorShortcut.java @@ -1,15 +1,15 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; import core.plugin.Plugin; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityShortcut; import core.game.node.Node; 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/TunnelShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/TunnelShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java index 445854d4e..42cf77e34 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/TunnelShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java @@ -1,19 +1,22 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityShortcut; import core.game.node.Node; 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 rs09.game.world.GameWorld; +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 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/java/core/game/node/entity/skill/agility/shortcuts/ZanarisSqueezeShortcut.java b/Server/src/main/content/global/skill/agility/shortcuts/ZanarisSqueezeShortcut.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/ZanarisSqueezeShortcut.java rename to Server/src/main/content/global/skill/agility/shortcuts/ZanarisSqueezeShortcut.java index 1d057e5b8..b5a37c5fb 100644 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/ZanarisSqueezeShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/ZanarisSqueezeShortcut.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.agility.shortcuts; +package content.global.skill.agility.shortcuts; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityShortcut; +import content.global.skill.agility.AgilityShortcut; import core.game.node.entity.impl.ForceMovement; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; 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/java/core/game/node/entity/skill/construction/BuildHotspot.java b/Server/src/main/content/global/skill/construction/BuildHotspot.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/skill/construction/BuildHotspot.java rename to Server/src/main/content/global/skill/construction/BuildHotspot.java index b3cefa033..c5ca6c8e7 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/BuildHotspot.java +++ b/Server/src/main/content/global/skill/construction/BuildHotspot.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; import core.game.world.update.flag.context.Animation; @@ -16,7 +16,7 @@ public enum BuildHotspot { /** * Low level garden hotspots. */ - CENTREPIECE_1(15361, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.PORTAL, Decoration.ROCK, Decoration.POND, Decoration.IMP_STATUE, Decoration.DUNGEON_ENTRANCE), + CENTREPIECE_1(15361, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.PORTAL, Decoration.ROCK, Decoration.POND, Decoration.IMP_STATUE, Decoration.SMALL_OBELISK, Decoration.DUNGEON_ENTRANCE), BIG_TREE_1(15362, BuildHotspotType.INDIVIDUAL, BuildingUtils.PLANT_ANIM, Decoration.BIG_DEAD_TREE, Decoration.BIG_TREE, Decoration.BIG_OAK_TREE, Decoration.BIG_WILLOW_TREE, Decoration.BIG_MAPLE_TREE, Decoration.BIG_YEW_TREE, Decoration.BIG_MAGIC_TREE), TREE_1(15363, BuildHotspotType.INDIVIDUAL, BuildingUtils.PLANT_ANIM, Decoration.DEAD_TREE, Decoration.TREE, Decoration.OAK_TREE, Decoration.WILLOW_TREE, Decoration.MAPLE_TREE, Decoration.YEW_TREE, Decoration.MAGIC_TREE), BIG_PLANT_1(15364, BuildHotspotType.INDIVIDUAL, BuildingUtils.PLANT_ANIM, Decoration.FERN, Decoration.BUSH, Decoration.TALL_PLANT), @@ -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/java/core/game/node/entity/skill/construction/BuildHotspotType.java b/Server/src/main/content/global/skill/construction/BuildHotspotType.java similarity index 76% rename from Server/src/main/java/core/game/node/entity/skill/construction/BuildHotspotType.java rename to Server/src/main/content/global/skill/construction/BuildHotspotType.java index d2dbe1b4d..b477e826e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/BuildHotspotType.java +++ b/Server/src/main/content/global/skill/construction/BuildHotspotType.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; /** * BuiltHotspotType @@ -10,7 +10,7 @@ public enum BuildHotspotType { INDIVIDUAL, RECURSIVE, LINKED, - CREST, + CREST, STAIRCASE; BuildHotspotType() { diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/BuildOptionPlugin.java b/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/skill/construction/BuildOptionPlugin.java rename to Server/src/main/content/global/skill/construction/BuildOptionPlugin.java index 63cf6aed3..69b0b1483 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/BuildOptionPlugin.java +++ b/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java @@ -1,19 +1,21 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +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.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.node.entity.skill.construction.Hotspot; -import rs09.game.system.SystemLogger; -import rs09.plugin.PluginManager; +import core.tools.Log; +import core.tools.SystemLogger; +import core.plugin.ClassScanner; + +import static core.api.ContentAPIKt.log; /** * The build option handling plugin. @@ -27,7 +29,7 @@ public final class BuildOptionPlugin extends OptionHandler { public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.setOptionHandler("build", this); SceneryDefinition.setOptionHandler("remove", this); - PluginManager.definePlugin(new RemoveDialogue()); + ClassScanner.definePlugin(new RemoveDialogue()); return this; } @@ -64,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("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; } @@ -179,4 +181,4 @@ public final class BuildOptionPlugin extends OptionHandler { } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/BuildRoomDialogue.java b/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/construction/BuildRoomDialogue.java rename to Server/src/main/content/global/skill/construction/BuildRoomDialogue.java index 2b34c49eb..b74e99cb3 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/BuildRoomDialogue.java +++ b/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; import core.game.node.entity.player.Player; @@ -9,9 +9,10 @@ import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.RegionChunk; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/node/entity/skill/construction/BuildingUtils.java b/Server/src/main/content/global/skill/construction/BuildingUtils.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/skill/construction/BuildingUtils.java rename to Server/src/main/content/global/skill/construction/BuildingUtils.java index 5399e0bbf..2ee636337 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/BuildingUtils.java +++ b/Server/src/main/content/global/skill/construction/BuildingUtils.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; @@ -16,13 +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 rs09.game.node.entity.skill.construction.Hotspot; -import rs09.game.system.SystemLogger; +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 @@ -65,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. @@ -85,57 +88,38 @@ public final class BuildingUtils { int c261Value = 0; - for (int i = 0; i < 7; i++) { - int itemsStringOffset = 97 + (i * 5); - - //97 + - if (i >= hotspot.getDecorations().length || (hotspot.getDecorations()[i] != null && hotspot.getDecorations()[i].isInvisibleNode())) { + for (int menuIndex = 0; menuIndex < 7; menuIndex++) { + int itemsStringOffset = 97 + (menuIndex * 5); + 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); } - player.getPacketDispatch().sendString("", 396, 140 + i); - c261Value += (1 << (i + 1)); + player.getPacketDispatch().sendString("", 396, 140 + menuIndex); + c261Value += (1 << (menuIndex + 1)); continue; } - Decoration decoration = hotspot.getDecorations()[i]; - items[BUILD_INDEXES[i]] = new Item(decoration.getInterfaceItem()); + 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 (hasRequirements) - c261Value += (1 << (i + 1)); + if (j < reqItems.length && !player.getInventory().containsItem(reqItems[j])) { + hasRequirements = false; } + player.getPacketDispatch().sendString(reqsText[j], 396, (itemsStringOffset + 1) + j); } - player.getConfigManager().set(1485 + i, hasRequirements || player.isStaff() ? 1 : 0); - player.getPacketDispatch().sendString("Lvl " + decoration.getLevel(), 396, 140 + i); + if (hasRequirements) { + c261Value += (1 << (menuIndex + 1)); + } + 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); } - player.getConfigManager().set(261, c261Value); + + setVarp(player, 261, c261Value); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 396, 132, 8, items, false)); } @@ -164,7 +148,7 @@ public final class BuildingUtils { int roomX = object.getLocation().getChunkX(); int roomY = object.getLocation().getChunkY(); int z = object.getLocation().getZ(); - Region region = player.getHouseManager().getRegion(); + Region region = player.getHouseManager().getHouseRegion(); if (HouseManager.isInDungeon(player)) { region = player.getHouseManager().getDungeonRegion(); z = 3; @@ -200,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; } @@ -263,7 +246,7 @@ public final class BuildingUtils { Hotspot h = r.getStairs(); if (h != null) { h.setDecorationIndex(decIndex); - Region reg = plane == 3 ? player.getHouseManager().getDungeonRegion() : player.getHouseManager().getRegion(); + Region reg = plane == 3 ? player.getHouseManager().getDungeonRegion() : player.getHouseManager().getHouseRegion(); if (reg == null) { continue; } @@ -287,7 +270,7 @@ public final class BuildingUtils { } } } else { - SystemLogger.logErr("Couldn't find stairs! " + plane); + log(BuildingUtils.class, Log.ERR, "Couldn't find stairs! " + plane); } } } @@ -345,12 +328,12 @@ 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(); Room room = player.getHouseManager().getRooms()[l.getZ()][l.getChunkX()][l.getChunkY()]; - Region region = player.getHouseManager().getRegion(); + Region region = player.getHouseManager().getHouseRegion(); if (HouseManager.isInDungeon(player)) { region = player.getHouseManager().getDungeonRegion(); room = player.getHouseManager().getRooms()[3][l.getChunkX()][l.getChunkY()]; @@ -365,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; } } @@ -394,7 +381,7 @@ public final class BuildingUtils { Hotspot h = r.getStairs(); if (h != null) { h.setDecorationIndex(-1); - Region reg = plane == 3 ? player.getHouseManager().getDungeonRegion() : player.getHouseManager().getRegion(); + Region reg = plane == 3 ? player.getHouseManager().getDungeonRegion() : player.getHouseManager().getHouseRegion(); if (reg == null) { continue; } @@ -407,7 +394,7 @@ public final class BuildingUtils { } } } else { - SystemLogger.logErr("Couldn't find stairs! " + plane); + log(BuildingUtils.class, Log.ERR, "Couldn't find stairs! " + plane); } } } @@ -549,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; } @@ -587,7 +567,7 @@ public final class BuildingUtils { if (HouseManager.isInDungeon(player)) { z = 3; } - if (player.getHouseManager().hasRoom(z, location[0], location[1])) { + if (player.getHouseManager().hasRoomAt(z, location[0], location[1])) { return location; } return null; @@ -630,8 +610,8 @@ public final class BuildingUtils { Direction[] directions = new Direction[4]; boolean[] exit = Arrays.copyOf(exits, exits.length); //(0=east, 1=south, 2=west, 3=north) int[] info = getExitRequirements(player, z, roomX, roomY);//(0=west, 1=north, 2=east, 3=south) - // SystemLogger.logErr("Available exits - [east=" + exit[0] + ", south=" + exit[1] + ", west=" + exit[2] + ", north=" + exit[3] + "]!"); - // SystemLogger.logErr("Required exits - [east=" + info[0] + ", south=" + info[1] + ", west=" + info[2] + ", north=" + info[3] + "]!"); + // SystemLogger.logErr(BuildingUtils.class, "Available exits - [east=" + exit[0] + ", south=" + exit[1] + ", west=" + exit[2] + ", north=" + exit[3] + "]!"); + // SystemLogger.logErr(BuildingUtils.class, "Required exits - [east=" + info[0] + ", south=" + info[1] + ", west=" + info[2] + ", north=" + info[3] + "]!"); for (int i = 0; i < 4; i++) { boolean success = true; for (int j = 0; j < 4; j++) { @@ -654,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().hasRoom(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().hasRoom(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().hasRoom(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().hasRoom(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.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/java/core/game/node/entity/skill/construction/ConstructionInterface.java b/Server/src/main/content/global/skill/construction/ConstructionInterface.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/skill/construction/ConstructionInterface.java rename to Server/src/main/content/global/skill/construction/ConstructionInterface.java index 33ee06a11..a36b0329b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/ConstructionInterface.java +++ b/Server/src/main/content/global/skill/construction/ConstructionInterface.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; import core.cache.def.impl.ItemDefinition; @@ -10,8 +10,10 @@ import core.game.node.entity.skill.Skills; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.node.entity.skill.construction.Hotspot; -import rs09.game.system.SystemLogger; +import core.tools.Log; +import core.tools.SystemLogger; + +import static core.api.ContentAPIKt.log; /** * Handles the creating of a decoration object. @@ -39,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("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("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(); @@ -105,7 +107,7 @@ public final class ConstructionInterface extends ComponentPlugin { break; case 402: int index = button - 160; - SystemLogger.logErr("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/java/core/game/node/entity/skill/construction/CrestType.java b/Server/src/main/content/global/skill/construction/CrestType.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/skill/construction/CrestType.java rename to Server/src/main/content/global/skill/construction/CrestType.java index b7e68882c..277c77231 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/CrestType.java +++ b/Server/src/main/content/global/skill/construction/CrestType.java @@ -1,8 +1,9 @@ -package core.game.node.entity.skill.construction; +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 new file mode 100644 index 000000000..4191cb1a9 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/Decoration.java @@ -0,0 +1,1038 @@ +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, Player Name + * + */ +public enum Decoration { + /** + * Garden centrepiece decorations. + */ + 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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) }), + + /** + * Bookcases + */ + 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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), + + /** + * Skill hall decorations. + */ + 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, 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, 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[] { 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[] { 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[] { 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[] { 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[] { 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[] { 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[] { 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[] { 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[] { 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[] { 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), + 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. + */ + private final int interfaceItem; + + /** + * The level requirement. + */ + private final int level; + + /** + * The experience gained for building this decoration. + */ + private final int experience; + + /** + * The item required. + */ + private final Item[] items; + + /** + * The items that will be refunded. + */ + private final Item[] refundItems; + + /** + * The tools required. + */ + private final int[] tools; + + /** + * The object ids depending on styling. + */ + private final int[] objectIds; + + /** + * If this node should be invisible to user build options. + */ + private boolean invisibleNode; + + /** + * The requirements text to show for this node. + */ + 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 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. + */ + 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; + this.level = level; + 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 + */ + Decoration(int objectId, boolean invisibleNode) { + this(objectId, -1, -1, -1); + this.invisibleNode = true; + } + + /** + * 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. + */ + 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 object. + * @param objectIds The object id. + * @param interfaceItem The item id for the building interface. + * @param level The level required. + * @param experience The experience gained. + * @param tools The tools needed. + * @param items The items required. + * @param refundItems The items to be refunded when the item is removed. + */ + Decoration(int[] objectIds, int interfaceItem, int level, int experience, int[] tools, Item[] items, Item[] refundItems) { + this.objectId = objectIds[0]; + this.objectIds = objectIds; + this.interfaceItem = interfaceItem; + this.level = level; + this.experience = experience; + this.tools = tools; + this.items = items; + this.refundItems = refundItems; + } + + /** + * Gets the decoration on the given location. + * @param player The player. + * @return The decoration. + */ + public static Decoration getDecoration(Player player, Scenery object) { + Location l = object.getLocation(); + int z = l.getZ(); + if (HouseManager.isInDungeon(player)) { + z = 3; + } + Room room = player.getHouseManager().getRooms()[z][l.getChunkX()][l.getChunkY()]; + for (Hotspot h : room.getHotspots()) { + if (h.getCurrentX() == l.getChunkOffsetX() && h.getCurrentY() == l.getChunkOffsetY()) { + if (h.getDecorationIndex() != -1) { + Decoration deco = h.getHotspot().getDecorations()[h.getDecorationIndex()]; + if (deco.getObjectId(player.getHouseManager().getStyle()) == object.getId()) { + return deco; + } + } + } + } + return null; + } + + /** + * Gets a decoration for the given object id + * @param objectId - the object id of the built object + * @return the decoration or null + */ + public static Decoration forObjectId(int objectId) { + for (Decoration d : Decoration.values()) { + if (d.getObjectId() == objectId) { + return d; + } + } + return null; + } + + public static Decoration forName(String name) { + for (Decoration d : Decoration.values()) { + if (d.name().equals(name)) { + return d; + } + } + return null; + } + + /** + * Gets the amount of nails required for this hotspot. + * @return The amount of nails. + */ + public int getNailAmount() { + for (Item item : items) { + if (item.getId() == 960) { //1 nail per normal plank required. + return item.getAmount(); + } + } + return 0; + } + + /** + * Gets the objectId. + * @param style The current housing style. + * @return The objectId. + */ + public int getObjectId(HousingStyle style) { + if (objectIds != null) { + return objectIds[style.ordinal()]; + } + return objectId; + } + + /** + * Gets the objectId. + * @return The objectId. + */ + public int getObjectId() { + return objectId; + } + + /** + * Gets the level. + * @return The level. + */ + public int getLevel() { + return level; + } + + /** + * Gets the experience. + * @return The experience. + */ + public int getExperience() { + return experience; + } + + /** + * Gets the items. + * @return The items. + */ + public Item[] getItems() { + return items; + } + + /** + * Gets the refund items. + * @return The refund items. + */ + public Item[] getRefundItems() { + return refundItems; + } + + /** + * Gets the tools. + * @return The tools. + */ + public int[] getTools() { + return tools; + } + + /** + * Gets the interfaceItem. + * @return the interfaceItem + */ + public int getInterfaceItem() { + return interfaceItem; + } + + /** + * Gets the objectIds value. + * @return The objectIds. + */ + public int[] getObjectIds() { + return objectIds; + } + + /** + * If this node should be invisible to user build options + * @return true if so. + */ + public boolean isInvisibleNode() { + return invisibleNode; + } + + /** + * Gets the requirements text. + * @return The requirements text. + */ + public String[] getReqsText() { + return reqsText; + } +} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/EstateAgentDialogue.kt b/Server/src/main/content/global/skill/construction/EstateAgentDialogue.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/construction/EstateAgentDialogue.kt rename to Server/src/main/content/global/skill/construction/EstateAgentDialogue.kt index ba5476286..efba1ad00 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/EstateAgentDialogue.kt +++ b/Server/src/main/content/global/skill/construction/EstateAgentDialogue.kt @@ -1,20 +1,15 @@ -package rs09.game.node.entity.skill.construction +package content.global.skill.construction -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.content.global.Skillcape 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.entity.skill.construction.HouseLocation -import core.game.node.entity.skill.construction.HousingStyle import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items -import rs09.game.world.GameWorld.settings -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +import core.game.world.GameWorld.settings +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE /** * Represents the estate agent dialogue. @@ -22,7 +17,7 @@ import rs09.tools.START_DIALOGUE * @author Woah */ @Initializable -class EstateAgentDialogue : DialoguePlugin { +class EstateAgentDialogue : core.game.dialogue.DialoguePlugin { /** * Constructs a new `EstateAgentDialogue` `Object`. @@ -40,7 +35,7 @@ class EstateAgentDialogue : DialoguePlugin { */ constructor(player: Player?) : super(player) - override fun newInstance(player: Player): DialoguePlugin { + override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { return EstateAgentDialogue(player) } @@ -88,7 +83,7 @@ class EstateAgentDialogue : DialoguePlugin { } 5 -> { player("Tell me about that skillcape you're wearing!") - stage = if (Skillcape.isMaster(player, Skills.CONSTRUCTION)) 102 else 100 + stage = if (core.game.global.Skillcape.isMaster(player, Skills.CONSTRUCTION)) 102 else 100 } } @@ -136,8 +131,8 @@ class EstateAgentDialogue : DialoguePlugin { 7 -> { if (player.inventory.contains(995, 1000)) { - player.inventory.remove(Item(995, 1000)) - player.houseManager.create(HouseLocation.RIMMINGTON) + player.inventory.remove(Item(Items.COINS_995, 1000)) + player.houseManager.createNewHouseAt(HouseLocation.RIMMINGTON) npc( "Thank you. Go through the Rimmington house portal", "and you will find your house ready for you to start", @@ -344,7 +339,7 @@ class EstateAgentDialogue : DialoguePlugin { 102 -> { interpreter.sendDialogues( npc, - FacialExpression.JOLLY, + core.game.dialogue.FacialExpression.JOLLY, "I see you have recently achieved 99 construction.", "Would you like to buy a cape for 99,0000 gp?" ) @@ -356,7 +351,7 @@ class EstateAgentDialogue : DialoguePlugin { } 104 -> when (buttonId) { 1 -> { - if (Skillcape.purchase(player, Skills.CONSTRUCTION)) { + if (core.game.global.Skillcape.purchase(player, Skills.CONSTRUCTION)) { npc("Here you go lad, enjoy!") } stage = END_DIALOGUE diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/Hotspot.kt b/Server/src/main/content/global/skill/construction/Hotspot.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/construction/Hotspot.kt rename to Server/src/main/content/global/skill/construction/Hotspot.kt index e25429a02..dd245e782 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/Hotspot.kt +++ b/Server/src/main/content/global/skill/construction/Hotspot.kt @@ -1,6 +1,4 @@ -package rs09.game.node.entity.skill.construction - -import core.game.node.entity.skill.construction.BuildHotspot +package content.global.skill.construction /** * Represents a hotspot. diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/HouseLocation.java b/Server/src/main/content/global/skill/construction/HouseLocation.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/construction/HouseLocation.java rename to Server/src/main/content/global/skill/construction/HouseLocation.java index 4f1302015..ac55b0001 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/HouseLocation.java +++ b/Server/src/main/content/global/skill/construction/HouseLocation.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; import core.game.node.entity.skill.Skills; @@ -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 new file mode 100644 index 000000000..4db8e8191 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/HouseManager.java @@ -0,0 +1,856 @@ +package content.global.skill.construction; +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.skill.Skills; +import core.game.node.scenery.Scenery; +import core.game.system.task.Pulse; +import core.game.world.map.*; +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.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import java.awt.*; + +import static core.api.ContentAPIKt.*; +import static core.api.regionspec.RegionSpecificationKt.fillWith; +import static core.api.regionspec.RegionSpecificationKt.using; + + +/** + * Manages the player's house. + * @author Emperor + * + */ +public final class HouseManager { + + /** + * The current region. + */ + private DynamicRegion houseRegion; + + /** + * The current region. + */ + private DynamicRegion dungeonRegion; + + /** + * The house location. + */ + private HouseLocation location = HouseLocation.NOWHERE; + + /** + * The house style. + */ + private HousingStyle style = HousingStyle.BASIC_WOOD; + + /** + * The house zone. + */ + private HouseZone zone = new HouseZone(this); + + /** + * The player's house rooms. + */ + private final Room[][][] rooms = new Room[4][8][8]; + + /** + * If building mode is enabled. + */ + private boolean buildingMode; + + /** + * If the player has used the portal to lock their house. + */ + private boolean locked; + + /** + * The player's servant. + */ + private Servant servant; + + /** + * If the house has a dungeon. + */ + private boolean hasDungeon; + + /** + * The player's crest. + */ + private CrestType crest = CrestType.ASGARNIA; + + /** + * Constructs a new {@code HouseManager} {@code Object}. + */ + public HouseManager() { + /* + * empty. + */ + } + + + public void parse(JSONObject data){ + location = HouseLocation.values()[Integer.parseInt( data.get("location").toString())]; + style = HousingStyle.values()[Integer.parseInt( data.get("style").toString())]; + Object servRaw = data.get("servant"); + if(servRaw != null){ + servant = Servant.parse((JSONObject) servRaw); + } + JSONArray rArray = (JSONArray) data.get("rooms"); + for(int i = 0; i < rArray.size(); i++){ + JSONObject rm = (JSONObject) rArray.get(i); + int z = Integer.parseInt(rm.get("z").toString()); + int x = Integer.parseInt(rm.get("x").toString()); + int y = Integer.parseInt(rm.get("y").toString()); + if(z == 3) + hasDungeon = true; + Room room = rooms[z][x][y] = new Room(RoomProperties.values()[Integer.parseInt(rm.get("properties").toString())]); + room.configure(style); + room.setRotation(Direction.get(Integer.parseInt(rm.get("rotation").toString()))); + JSONArray hotspots = (JSONArray) rm.get("hotspots"); + for(int j = 0; j < hotspots.size(); j++){ + JSONObject spot = (JSONObject) hotspots.get(j); + room.getHotspots()[Integer.parseInt(spot.get("hotspotIndex").toString())].setDecorationIndex(Integer.parseInt(spot.get("decorationIndex").toString())); + } + } + } + + /** + * Prepares for entering the player's house. + * @param player + * @param 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.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); + } + + private void openLoadInterface(Player player) { + player.getInterfaceManager().openComponent(399); + playAudio(player, Sounds.POH_TELEPORT_984); + submitCloseLoadInterfacePulse(player); + } + + private void submitCloseLoadInterfacePulse(Player player) { + GameWorld.getPulser().submit(new Pulse(1, player) { + @Override + public boolean pulse() { + player.getInterfaceManager().close(); + return true; + } + }); + } + + private void checkForAndSpawnServant(Player player) { + if(!hasServant()) return; + + GameWorld.getPulser().submit(new Pulse(1, player) { + @Override + public boolean pulse() { + spawnServant(); + if (servant.isGreet()){ + player.getDialogueInterpreter().sendDialogues(servant.getType().getId(), servant.getType().getId() == 4243 ? FacialExpression.HAPPY : null, "Welcome."); + } + return true; + } + }); + } + + private void updateVarbits(Player player, boolean build) { + setVarp(player, 261, build ? 1 : 0); + setVarp(player, 262, getRoomAmount()); + } + + private void unlockMusicTrack(Player player) { + player.getMusicPlayer().unlock(454, true); + } + + /** + * Leaves this house through the portal. + * @param player The player leaving. + */ + public static void leave(Player player) { + HouseManager house = player.getAttribute("poh_entry", player.getHouseManager()); + if (house.getHouseRegion() == null){ + return; + } + if (house.isInHouse(player)) { + player.animate(Animation.RESET); + player.getProperties().setTeleportLocation(house.location.getExitLocation()); + } + } + + /** + * Toggles the building mode. + * @param player The house owner. + * @param enable If the building mode should be enabled. + */ + public void toggleBuildingMode(Player player, boolean enable) { + if (!isInHouse(player)) { + player.getPacketDispatch().sendMessage("Building mode really only helps if you're in a house."); + return; + } + if (buildingMode != enable) { + if (enable) { + expelGuests(player); + } + enter(player, enable); + player.getPacketDispatch().sendMessage("Building mode is now " + (buildingMode ? "on." : "off.")); + } + } + + /** + * 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(); + int diffY = player.getLocation().getLocalY(); + int diffZ = player.getLocation().getZ(); + boolean inDungeon = player.getViewport().getRegion() == dungeonRegion; + this.buildingMode = buildingMode; + construct(); + Location newLoc = (dungeonRegion == null ? houseRegion : (inDungeon ? dungeonRegion : houseRegion)).getBaseLocation().transform(diffX,diffY,diffZ); + player.getProperties().setTeleportLocation(newLoc); + } + + /** + * Expels the guests from the house. + * @param player The house owner. + */ + public void expelGuests(Player player) { + if (isLoaded()) { + for (RegionPlane plane : houseRegion.getPlanes()) { + for (Player p : plane.getPlayers()) { + if (p != player) { + leave(p); + } + } + } + if (dungeonRegion != null) { + for (RegionPlane plane : dungeonRegion.getPlanes()) { + for (Player p : plane.getPlayers()) { + if (p != player) { + leave(p); + } + } + } + } + } + } + + /** + * Gets the entering location. + * @return The entering location. + */ + public Location getEnterLocation() { + if (houseRegion == null) { + log(this.getClass(), Log.ERR, "House wasn't constructed yet!"); + return null; + } + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + Room room = rooms[0][x][y]; + if (room != null && (room.getProperties() == RoomProperties.GARDEN || room.getProperties() == RoomProperties.FORMAL_GARDEN)) { + for (Hotspot h : room.getHotspots()) { + if (h.getDecorationIndex() > -1) { + Decoration d = h.getHotspot().getDecorations()[h.getDecorationIndex()]; + if (d == Decoration.PORTAL) { + return houseRegion.getBaseLocation().transform(x * 8 + h.getChunkX(), y * 8 + h.getChunkY() + 2, 0); + } + } + } + } + } + } + return null; + } + + /** + * Redecorates the house. + * @param style The new style. + */ + public void redecorate(HousingStyle style) { + this.style = style; + 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) { + room.decorate(style); + } + } + } + } + } + + /** + * Clears all the rooms (Including portal room!). + */ + @Deprecated + public void clearRooms() { + for (int z = 0; z < 4; z++) { + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + rooms[z][x][y] = null; + } + } + } + } + + /** + * Creates the default house. + * @param location The house location. + */ + public void createNewHouseAt(HouseLocation location) { + clearRooms(); + 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; + } + + /** + * Constructs the dynamic region for the house. + * @return The region. + */ + public DynamicRegion construct() { + 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); + return houseRegion; + } + + private DynamicRegion getPreparedRegion() { + ZoneBorders borders = DynamicRegion.reserveArea(8,8); + 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; + } + + private class RoomLoadContract extends FillChunkContract { + Room[][][] rooms; + HouseManager manager; + boolean buildingMode; + + public RoomLoadContract(HouseManager manager, boolean buildingMode, Room[][][] rooms) { + this.rooms = rooms; + this.manager = manager; + this.buildingMode = buildingMode; + } + + @Override + public BuildRegionChunk getChunk(int x, int y, int plane, @NotNull DynamicRegion dyn) { + return rooms[plane][x][y].getChunk().copy(dyn.getPlanes()[plane]); + } + + @Override + public void afterSetting(@Nullable BuildRegionChunk chunk, int x, int y, int plane, @NotNull DynamicRegion dyn) { + rooms[plane][x][y].loadDecorations(dyn != manager.dungeonRegion ? plane : 3, chunk, manager); + } + } + + private void prepareHouseChunks(HousingStyle style, DynamicRegion target, boolean buildingMode, Room[][][] rooms) { + Region from = RegionManager.forId(style.getRegionId()); + Region.load(from, true); + RegionChunk defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(1, 0); + RegionChunk defaultSkyChunk = from.getPlanes()[1].getRegionChunk(0,0); + + RoomLoadContract loadRooms = new RoomLoadContract(this, buildingMode, rooms); + RegionSpecification spec = new RegionSpecification( + using(target), + fillWith(defaultChunk) + .from(from) + .onPlanes(0), + fillWith(defaultSkyChunk) + .from(from) + .onPlanes(1,2,3), + loadRooms + .from(from) + .onPlanes(0,1,2) + .onCondition((destX,destY,plane) -> rooms[plane][destX][destY] != null) + ); + + spec.build(); + } + + private void prepareDungeonChunks(HousingStyle style, DynamicRegion target, DynamicRegion house, boolean buildingMode, Room[][] rooms) { + Region from = RegionManager.forId(style.getRegionId()); + Region.load(from, true); + RegionChunk defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(3, 0); + + RoomLoadContract loadRooms = new RoomLoadContract(this, buildingMode, new Room[][][]{rooms}); + RegionSpecification spec = new RegionSpecification( + using(target), + fillWith((x,y,plane,region) -> buildingMode ? null : defaultChunk) + .from(from) + .onPlanes(0) + .onCondition((destX, destY, plane) -> rooms[destX][destY] == null), + loadRooms + .from(from) + .onPlanes(0) + .onCondition((destX, destY, plane) -> rooms[destX][destY] != null) + ); + + spec.build(); + house.link(target); + } + + /** + * Configures the rooftops. + */ + public void configureRoofs() { +// boolean[][][] roofs = new boolean[2][8][8]; +// for (int x = 0; x < 8; x++) { +// for (int y = 0; y < 8; y++) { +// Room room = rooms[0][x][y]; +// if (room != null && room.getProperties().isChamber()) { +// room = rooms[1][x][y]; +// int z = 1; +// if (room != null && room.getProperties().isChamber()) { +// z = 2; +// } +// if (x > 0 ) +// } +// } +// } + } + + /** + * Gets the current room plane. + * @param l The location. + * @return The plane of the room. + */ + public Room getRoom(Location l) { + int z = l.getZ(); + if (dungeonRegion != null && l.getRegionId() == dungeonRegion.getId()) { + z = 3; + } + return rooms[z][l.getChunkX()][l.getChunkY()]; + } + + /** + * Gets the hotspot for the given object. + * @param object The object. + * @return The hotspot. + */ + public Hotspot getHotspot(Scenery object) { + Room room = getRoom(object.getLocation()); + if (room == null) { + return null; + } + int chunkX = object.getLocation().getChunkOffsetX(); + int chunkY = object.getLocation().getChunkOffsetY(); + switch(room.getRotation()){ + case WEST: { + int tempChunk = chunkY; + chunkY = 7 - chunkX; + chunkX = tempChunk; + break; + } + case EAST: { + //x = y, y = x, x = 7 - y + int tempChunk = chunkX; + chunkX = 7 - chunkY; + chunkY = tempChunk; + break; + } + case SOUTH: { + chunkX = 7 - chunkX; + chunkY = 7 - chunkY; + break; + } + default: { + + } + } + for (Hotspot h : room.getHotspots()) { + if ((h.getChunkX() == chunkX || h.getChunkX2() == chunkX) && (h.getChunkY() == chunkY || h.getChunkY2() == chunkY) && h.getHotspot().getObjectId(style) == object.getId()) { + return h; + } + } + return null; + } + + /** + * Checks if a room exists on the given location. + * @param z The plane. + * @param roomX The room x-coordinate. + * @param roomY The room y-coordinate. + * @return {@code True} if so. + */ + public boolean hasRoomAt(int z, int roomX, int roomY) { + Room room = rooms[z][roomX][roomY]; + return room != null && !room.getProperties().isRoof(); + } + + /** + * Enters the dungeon. + * @param player The player. + */ + public void enterDungeon(Player player) { + if (!hasDungeon()) { + return; + } + int diffX = player.getLocation().getLocalX(); + int diffY = player.getLocation().getLocalY(); + player.getProperties().setTeleportLocation(dungeonRegion.getBaseLocation().transform(diffX, diffY, 0)); + } + + /** + * Checks if an exit exists on the given room. + * @param roomX The x-coordinate of the room. + * @param roomY The y-coordinate of the room. + * @param direction The exit direction. + * @return {@code True} if so. + */ + public boolean hasExit(int z, int roomX, int roomY, Direction direction) { + Room room = rooms[z][roomX][roomY]; + int index = (direction.toInteger() + 3) % 4; + return room != null && room.getExits()[index]; + } + + /** + * Gets the amount of rooms. + * @return The amount of rooms. + */ + public int getRoomAmount() { + int count = 0; + for (int z = 0; z < 4; z++) { + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + Room r = rooms[z][x][y]; + if (r != null && !r.getProperties().isRoof()) { + count++; + } + } + } + } + return count; + } + + /** + * Gets the amount of portals available. + * @return The amount of portals. + */ + public int getPortalAmount() { + int count = 0; + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + Room room = rooms[0][x][y]; + if (room != null && (room.getProperties() == RoomProperties.GARDEN + || room.getProperties() == RoomProperties.FORMAL_GARDEN) && room.getHotspots()[0].getDecorationIndex() == 0) { + count++; + } + } + } + return count; + } + + /** + * Gets the current house boundaries. + * @return The boundaries. + */ + public Rectangle getBoundaries() { + int startX = 99; + int startY = 99; + int endX = 0; + int endY = 0; + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + if (rooms[0][x][y] != null) { + if (x < startX) startX = x; + if (y < startY) startY = y; + if (x > endX) endX = x; + if (y > endY) endY = y; + } + } + } + return new Rectangle(startX, startY, (endX - startX) + 1, (endY - startY) + 1); + } + + /** + * Gets the maximum dimension for the house boundaries. + * @param player The player. + * @return The dimension value (value X value = dimension) + */ + public int getMaximumDimension(Player player) { + int level = player.getSkills().getStaticLevel(Skills.CONSTRUCTION); + if (level >= 60) { + return 7; + } + if (level >= 45) { + return 6; + } + if (level >= 30) { + return 5; + } + if (level >= 15) { + return 4; + } + return 3; + } + + /** + * Gets the maximum amount of rooms available for the player. + * @param player The player. + * @return The maximum amount of rooms. + */ + public int getMaximumRooms(Player player) { + int level = player.getSkills().getStaticLevel(Skills.CONSTRUCTION); + if (level >= 99) return 30; + if (level >= 96) return 29; + if (level >= 92) return 28; + if (level >= 86) return 27; + if (level >= 80) return 26; + if (level >= 74) return 25; + if (level >= 68) return 24; + if (level >= 62) return 23; + if (level >= 56) return 22; + if (level >= 50) return 21; + return 20; + } + + /** + * Spawns the servant inside the player's home. + */ + private void spawnServant(){ + servant.setLocation(getEnterLocation()); + servant.setWalkRadius(getRoomAmount() * 2); + servant.setWalks(true); + servant.init(); + } + + /** + * Checks if the player has a servant. + * @return {@code True} if so. + */ + public boolean hasServant() { + return servant != null; + } + + /** + * Checks if the player is in his own house (or dungeon). + * @param player The player. + * @return {@code True} if so. + */ + public boolean isInHouse(Player player) { + return isLoaded() && (player.getViewport().getRegion() == houseRegion || player.getViewport().getRegion() == dungeonRegion); + } + + /** + * Checks if the player is in his dungeon. + * @param player The player. + * @return {@code True} if so. + */ + public static boolean isInDungeon(Player player) { + return player.getViewport().getRegion() == player.getHouseManager().dungeonRegion; + } + + /** + * 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 && houseRegion.isActive()) || (dungeonRegion != null && dungeonRegion.isActive()); + } + + /** + * Gets the hasHouse. + * @return The hasHouse. + */ + public boolean hasHouse() { + return location != HouseLocation.NOWHERE; + } + + /** + * Checks if the house has a dungeon. + * @return {@code True} if so. + */ + public boolean hasDungeon() { + return hasDungeon; + } + + /** + * Sets the has dungeon value. + * @param hasDungeon If the house has a dungeon. + */ + public void setHasDungeon(boolean hasDungeon) { + this.hasDungeon = hasDungeon; + } + + /** + * Gets the rooms. + * @return The rooms. + */ + public Room[][][] getRooms() { + return rooms; + } + + /** + * Gets the location. + * @return The location. + */ + public HouseLocation getLocation() { + return location; + } + + /** + * Sets the location. + * @param location The location to set. + */ + public void setLocation(HouseLocation location) { + this.location = location; + } + + /** + * Checks if the building mode is enabled. + * @return {@code True} if so. + */ + public boolean isBuildingMode() { + return buildingMode; + } + + /** + * Checks if the player has locked their house. + * @return {@code True} if so. + */ + public boolean isLocked() { + return locked; + } + + /** + * Sets the house to locked. + * @param locked true or false + */ + public void setLocked(boolean locked) { + this.locked = locked; + } + + /** + * Gets the region. + * @return The region. + */ + public DynamicRegion getHouseRegion() { + return houseRegion; + } + + /** + * Gets the dungeon region. + * @return The dungeon region. + */ + public Region getDungeonRegion() { + return dungeonRegion; + } + + /** + * Gets the style. + * @return the style + */ + public HousingStyle getStyle() { + return style; + } + + /** + * Sets the style. + * @param style the style to set. + */ + public void setStyle(HousingStyle style) { + this.style = style; + } + + /** + * Gets the player's servant + * @return the servant. + */ + public Servant getServant(){ + return servant; + } + + /** + * Sets the player's servant + * @param servant The servant to set. + */ + public void setServant(Servant servant){ + this.servant = servant; + } + + /** + * Gets the crest value. + * @return The crest. + */ + public CrestType getCrest() { + return crest; + } + + /** + * Sets the crest value. + * @param crest The crest to set. + */ + public void setCrest(CrestType crest) { + this.crest = crest; + } + + /** + * @return the zone + */ + public HouseZone getZone() { + return zone; + } +} diff --git a/Server/src/main/content/global/skill/construction/HouseZone.java b/Server/src/main/content/global/skill/construction/HouseZone.java new file mode 100644 index 000000000..31a57f6f8 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/HouseZone.java @@ -0,0 +1,157 @@ +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.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. + * + * @author Emperor + */ +public final class HouseZone extends MapZone { + + /** + * The house manager. + */ + private HouseManager house; + + /** + * The previous house region id. + */ + private int previousRegion = -1; + + /** + * The previous dungeon region id. + */ + private int previousDungeon = -1; + + /** + * Constructs the house zone object. + */ + public HouseZone(HouseManager house) { + 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); + } + } + + @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); + } + + @Override + public boolean death(Entity e, Entity killer) { + if (e instanceof Player) { + Player p = (Player) e; + HouseManager.leave(p); + return true; + } + return super.death(e, killer); + } + + @Override + public boolean leave(Entity e, boolean logout) { + 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; + } + + 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/java/core/game/node/entity/skill/construction/HousingStyle.java b/Server/src/main/content/global/skill/construction/HousingStyle.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/skill/construction/HousingStyle.java rename to Server/src/main/content/global/skill/construction/HousingStyle.java index f945c3c5c..eace0e7bb 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/HousingStyle.java +++ b/Server/src/main/content/global/skill/construction/HousingStyle.java @@ -1,14 +1,18 @@ -package core.game.node.entity.skill.construction; +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/java/core/game/node/entity/skill/construction/NailType.java b/Server/src/main/content/global/skill/construction/NailType.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/construction/NailType.java rename to Server/src/main/content/global/skill/construction/NailType.java index c2dd0e661..22ec10027 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/NailType.java +++ b/Server/src/main/content/global/skill/construction/NailType.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/PortalOptionPlugin.java b/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/skill/construction/PortalOptionPlugin.java rename to Server/src/main/content/global/skill/construction/PortalOptionPlugin.java index 7dd543a29..23d951dac 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/PortalOptionPlugin.java +++ b/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java @@ -1,10 +1,10 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -14,8 +14,8 @@ import core.plugin.Plugin; import core.plugin.PluginManifest; import core.plugin.PluginType; import kotlin.Unit; -import rs09.game.world.repository.Repository; -import rs09.plugin.PluginManager; +import core.game.world.repository.Repository; +import core.plugin.ClassScanner; /** * Handles the house portal options. @@ -32,7 +32,7 @@ public final class PortalOptionPlugin extends OptionHandler { } SceneryDefinition.forId(13405).getHandlers().put("option:lock", this); SceneryDefinition.forId(13405).getHandlers().put("option:enter", this); - PluginManager.definePlugin(new PortalDialogue()); + ClassScanner.definePlugin(new PortalDialogue()); return this; } @@ -116,15 +116,15 @@ public final class PortalOptionPlugin extends OptionHandler { player.sendMessage("Speak with an estate agent to change your house location."); break; } - player.getHouseManager().enter(player, buttonId == 2, true); + player.getHouseManager().enter(player, buttonId == 2); break; case 3: if(player.getIronmanManager().isIronman()){ end(); - ContentAPI.sendMessage(player, "You can't do that as an ironman."); + sendMessage(player, "You can't do that as an ironman."); return true; } - ContentAPI.sendInputDialogue(player, false, "Enter friend's name:", (value) -> { + sendInputDialogue(player, false, "Enter friend's name:", (value) -> { Player p = Repository.getPlayerByName((String) value); if (p == null || !p.isActive()) { player.getPacketDispatch().sendMessage("This player is not online."); @@ -147,7 +147,7 @@ public final class PortalOptionPlugin extends OptionHandler { return Unit.INSTANCE; } p.setAttribute("poh_owner", (String) value); - p.getHouseManager().enter(player, false, false); + p.getHouseManager().enter(player, false); return Unit.INSTANCE; }); break; diff --git a/Server/src/main/content/global/skill/construction/RemovalDialogue.java b/Server/src/main/content/global/skill/construction/RemovalDialogue.java new file mode 100644 index 000000000..3edbc360f --- /dev/null +++ b/Server/src/main/content/global/skill/construction/RemovalDialogue.java @@ -0,0 +1,115 @@ +package content.global.skill.construction; + + +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.node.entity.player.Player; +import core.plugin.Initializable; + +/** + * Removal dialogue for room & decorations in construction + * @author Clayton Williams + * @author Emperor + * @date Jun-30-2015 + */ +@Initializable +public final class RemovalDialogue extends DialoguePlugin { + + /** + * The room position. + */ + private int[] pos; + + /** + * The plane. + */ + private int plane; + + /** + * The room to remove. + */ + private Room room; + + /** + * Removal Dialogue + */ + public RemovalDialogue() { + super(); + } + + /** + * Removal Dialogue + * @param player + */ + public RemovalDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new RemovalDialogue(player); + } + + @Override + public boolean open(Object... args) { + pos = (int[]) args[1]; + + 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; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + if (stage == 0) { + if (buttonId == 1) { + if (plane == 0 && player.getHouseManager().hasRoomAt(1, pos[0], pos[1])) { + interpreter.sendPlainMessage(false, "You can't remove a room supporting another room."); + stage = 1; + return true; + } + if (room != null && room.getProperties().isLand()) { + Hotspot h = room.getHotspots()[0]; + if (h != null && h.getDecorationIndex() == 0 && player.getHouseManager().getPortalAmount() <= 1) { + interpreter.sendPlainMessage(false, "You can't remove the garden with your portal in it."); + stage = 1; + return true; + } + } + player.getHouseManager().getRooms()[plane][pos[0]][pos[1]] = null; + for (int i = plane; i < 3; i++) { + Room r = player.getHouseManager().getRooms()[i][pos[0]][pos[1]]; + if (r != null && r.getProperties().isRoof()) { + player.getHouseManager().getRooms()[i][pos[0]][pos[1]] = null; + } + } + player.getHouseManager().reload(player, true); + } + } + end(); + return true; + } + + @Override + public int[] getIds() { + return new int[]{ DialogueInterpreter.getDialogueKey("con:remove") }; + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/Room.java b/Server/src/main/content/global/skill/construction/Room.java new file mode 100644 index 000000000..ac7b56cec --- /dev/null +++ b/Server/src/main/content/global/skill/construction/Room.java @@ -0,0 +1,427 @@ +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. + * @author Emperor + * + */ +public final class Room { + + /** + * The default room type. + */ + public static final int CHAMBER = 0x0; + + /** + * The rooftop room type. + */ + public static final int ROOF = 0x1; + + /** + * The dungeon room type. + */ + public static final int DUNGEON = 0x2; + + /** + * The dungeon room type. + */ + public static final int LAND = 0x4; + + /** + * The room properties. + */ + private RoomProperties properties; + + /** + * The region chunk. + */ + private RegionChunk chunk; + + /** + * The hotspots. + */ + private Hotspot[] hotspots; + + /** + * The current rotation of the room. + */ + private Direction rotation = Direction.NORTH; + + /** + * Constructs a new {@code Room} {@code Object}. + * @param properties The room properties. + */ + public Room(RoomProperties properties) { + this.properties = properties; + } + + /** + * Creates a new room. + * @param player The player. + * @param properties The room properties. + * @return The room. + */ + public static Room create(Player player, RoomProperties properties) { + Room room = new Room(properties); + room.configure(player.getHouseManager().getStyle()); + return room; + } + + /** + * Configures the room. + */ + public void configure(HousingStyle style) { + this.hotspots = new Hotspot[properties.getHotspots().length]; + for (int i = 0; i < hotspots.length; i++) { + hotspots[i] = properties.getHotspots()[i].copy(); + } + decorate(style); + } + + /** + * Redecorates the room. + * @param style The house style. + */ + public void decorate(HousingStyle style) { + Region region = RegionManager.forId(style.getRegionId()); + 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. + * @return The hotspot. + */ + public Hotspot getHotspot(BuildHotspot hotspot) { + for (Hotspot h : hotspots) { + if (h.getHotspot() == hotspot) { + return h; + } + } + return null; + } + + /** + * Checks if the building hotspot has been built. + * @param hotspot The building hotspot. + * @return {@code True} if so. + */ + public boolean isBuilt(BuildHotspot hotspot) { + Hotspot h = getHotspot(hotspot); + return h != null && h.getDecorationIndex() > -1; + } + + /** + * Loads all the decorations. + * @param housePlane The plane. + * @param chunk The chunk used in the dynamic region. + */ + public void loadDecorations(int housePlane, BuildRegionChunk chunk, HouseManager house) { + for (int i = 0; i < hotspots.length; i++) { + Hotspot spot = hotspots[i]; + int x = spot.getChunkX(); + int y = spot.getChunkY(); + if (spot.getHotspot() == null) { + continue; + } + int index = chunk.getIndex(x, y, spot.getHotspot().getObjectId(house.getStyle())); + Scenery[][] objects = chunk.getObjects(index); + Scenery object = objects[x][y]; + if (object != null && object.getId() == spot.getHotspot().getObjectId(house.getStyle())) { + if (spot.getDecorationIndex() > -1 && spot.getDecorationIndex() < spot.getHotspot().getDecorations().length) { + int id = spot.getHotspot().getDecorations()[spot.getDecorationIndex()].getObjectId(house.getStyle()); + if (spot.getHotspot().getType() == BuildHotspotType.CREST) { + id += house.getCrest().ordinal(); + } + SceneryBuilder.replace(object, object.transform(id, object.getRotation(), chunk.getCurrentBase().transform(x, y, 0))); + } + else if (object.getId() == BuildHotspot.WINDOW.getObjectId(house.getStyle()) || (!house.isBuildingMode() && object.getId() == BuildHotspot.CHAPEL_WINDOW.getObjectId(house.getStyle()))) { + chunk.add(object.transform(house.getStyle().getWindowStyle().getObjectId(house.getStyle()), object.getRotation(), object.getType())); + } + int[] pos = RegionChunk.getRotatedPosition(x, y, object.getSizeX(), object.getSizeY(), 0, rotation.toInteger()); + spot.setCurrentX(pos[0]); + spot.setCurrentY(pos[1]); + } + } + if (rotation != Direction.NORTH && chunk.getRotation() == 0) { + chunk.rotate(rotation); + } + if (!house.isBuildingMode()) { + placeDoors(housePlane, house, chunk); + removeHotspots(housePlane, house, chunk); + } + } + + /** + * Removes the building hotspots from the room. + * @param housePlane The room's plane in house. + * @param house The house manager. + * @param chunk The region chunk used. + */ + private void removeHotspots(int housePlane, HouseManager house, BuildRegionChunk chunk) { + 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) { + 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); + } + } + } + } + } + } + + /** + * 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 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)); + } + } + } + } + } + + /** + * 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. + * @param hs The building hotspot. + */ + public void setAllDecorationIndex(int index, BuildHotspot hs) { + for (int i = 0; i < hotspots.length; i++) { + Hotspot h = hotspots[i]; + if (h.getHotspot() == hs) { + h.setDecorationIndex(index); + } + } + } + + /** + * Gets the stairs hotspot for this room (or null if no stairs are available). + * @return The stairs. + */ + public Hotspot getStairs() { + for (Hotspot h : hotspots) { + if (h.getHotspot().getType() == BuildHotspotType.STAIRCASE + || h.getHotspot() == BuildHotspot.LADDER || h.getHotspot() == BuildHotspot.TRAPDOOR + || (h.getHotspot() == BuildHotspot.CENTREPIECE_1 && h.getDecorationIndex() == 4) + || (h.getHotspot() == BuildHotspot.CENTREPIECE_2 && h.getDecorationIndex() == 2)) { + return h; + } + } + return null; + } + + /** + * Gets the exit directions + * @return The exits information. + */ + public boolean[] getExits() { + return getExits(rotation); + } + + /** + * Gets the exit directions. + * @return The directions at which you can exit the room (0=east, 1=south, 2=west, 3=north). + */ + public boolean[] getExits(Direction rotation) { + boolean[] exits = properties.getExits(); + if (chunk.getRotation() != rotation.toInteger()) { + boolean[] exit = new boolean[exits.length]; + int offset = rotation.toInteger() - chunk.getRotation(); + for (int i = 0; i < 4; i++) { + exit[(i + offset) % 4] = exits[i]; + } + return exit; + } + return exits; + } + + /** + * Gets the hotspot for the given coordinates. + * @param build The build hotspot. + * @param x The x-coordinate. + * @param y The y-coordinate. + * @return The hotspot. + */ + public Hotspot getHotspot(BuildHotspot build, int x, int y) { + for (int i = 0; i < getHotspots().length; i++) { + Hotspot h = getHotspots()[i]; + if (h.getCurrentX() == x && h.getCurrentY() == y && h.getHotspot() == build) { + return h; + } + } + return null; + } + + /** + * Sets the properties. + * @param properties The properties. + */ + public void updateProperties(Player player, RoomProperties properties) { + this.properties = properties; + decorate(player.getHouseManager().getStyle()); + if (hotspots.length != properties.getHotspots().length) { + return; + } + for (int i = 0; i < hotspots.length; i++) { + Hotspot h = hotspots[i]; + Hotspot hs = hotspots[i] = properties.getHotspots()[i].copy(); + hs.setCurrentX(h.getCurrentX()); + hs.setCurrentY(h.getCurrentY()); + hs.setDecorationIndex(h.getDecorationIndex()); + } + } + + /** + * Gets the chunk. + * @return The chunk. + */ + public RegionChunk getChunk() { + return chunk; + } + + /** + * Sets the chunk. + * @param chunk The chunk to set. + */ + public void setChunk(RegionChunk chunk) { + this.chunk = chunk; + } + + /** + * Gets the hotspots. + * @return The hotspots. + */ + public Hotspot[] getHotspots() { + return hotspots; + } + + /** + * Sets the hotspots. + * @param hotspots The hotspots to set. + */ + public void setHotspots(Hotspot[] hotspots) { + this.hotspots = hotspots; + } + + /** + * Gets the properties. + * @return The properties. + */ + public RoomProperties getProperties() { + return properties; + } + + /** + * Sets the room rotation. + * @param rotation The rotation. + */ + public void setRotation(Direction rotation) { + this.rotation = rotation; + } + + /** + * Gets the rotation. + * @return The rotation. + */ + public Direction getRotation() { + return rotation; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/RoomProperties.java b/Server/src/main/content/global/skill/construction/RoomProperties.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/construction/RoomProperties.java rename to Server/src/main/content/global/skill/construction/RoomProperties.java index 66dfb6880..80bc6e6ed 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/RoomProperties.java +++ b/Server/src/main/content/global/skill/construction/RoomProperties.java @@ -1,19 +1,17 @@ -package core.game.node.entity.skill.construction; +package content.global.skill.construction; import core.game.node.scenery.Scenery; import core.game.world.map.Region; import core.game.world.map.RegionChunk; import core.game.world.map.RegionManager; -import rs09.game.node.entity.skill.construction.Hotspot; /** * Represents the room properties. - * @author Emperor + * @author Emperor, Player Name * >ORDINAL BOUND< */ public enum RoomProperties { - /** * The parlour. */ @@ -51,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), @@ -81,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)), @@ -92,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), @@ -116,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), @@ -130,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), @@ -143,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), @@ -194,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), @@ -255,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), @@ -319,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), @@ -377,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), @@ -415,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), @@ -453,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), @@ -470,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), @@ -622,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. */ @@ -696,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), @@ -918,7 +910,7 @@ public enum RoomProperties { * @return The name. */ public String getName() { - return name().toLowerCase().replaceAll("_", " ").replaceAll("\\d",""); + return name().toLowerCase().replaceAll("_", " ").replaceAll("\\d", ""); } /** @@ -1024,5 +1016,4 @@ public enum RoomProperties { public int getLevel() { return level; } - -} \ No newline at end of file +} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/RoomTemplate.kt b/Server/src/main/content/global/skill/construction/RoomTemplate.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/construction/RoomTemplate.kt rename to Server/src/main/content/global/skill/construction/RoomTemplate.kt index 5127e2e68..b1a68014b 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/RoomTemplate.kt +++ b/Server/src/main/content/global/skill/construction/RoomTemplate.kt @@ -1,6 +1,6 @@ -package rs09.game.node.entity.skill.construction +package content.global.skill.construction -import core.game.node.entity.skill.construction.RoomProperties +import content.global.skill.construction.RoomProperties class RoomTemplate(properties: RoomProperties) { var hotspots: MutableList = ArrayList() diff --git a/Server/src/main/content/global/skill/construction/Servant.java b/Server/src/main/content/global/skill/construction/Servant.java new file mode 100644 index 000000000..a3527bdad --- /dev/null +++ b/Server/src/main/content/global/skill/construction/Servant.java @@ -0,0 +1,116 @@ +package content.global.skill.construction; + + +import core.game.node.entity.npc.NPC; +import core.game.node.item.Item; +import org.json.simple.JSONObject; + +/** + * Represents a player's servant. + * @author Emperor + * + */ +public final class Servant extends NPC { + + /** + * The servant type. + */ + private final ServantType type; + + /** + * The item the servant is carrying. + */ + private Item item; + + /** + * The amount this servant has been used. + */ + private int uses; + + /** + * If the servant is greeting players entering the house. + */ + private boolean greet; + + /** + * Constructs a new {@code Servant} {@code Object}. + * @param type The servant type. + */ + public Servant(ServantType type) { + super(type.getId()); + this.type = type; + } + + /** + * 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]); + servant.uses = Integer.parseInt( data.get("uses").toString()); + Object itemRaw = data.get("item"); + if(itemRaw != null){ + JSONObject item = (JSONObject) itemRaw; + servant.item = new Item(Integer.parseInt(item.get("id").toString()),Integer.parseInt(item.get("amount").toString())); + } + servant.greet = (boolean) data.get("greet"); + return servant; + } + + /** + * Gets the item value. + * @return The item. + */ + public Item getItem() { + return item; + } + + /** + * Sets the item value. + * @param item The item to set. + */ + public void setItem(Item item) { + this.item = item; + } + + /** + * Gets the uses value. + * @return The uses. + */ + public int getUses() { + return uses; + } + + /** + * Sets the uses value. + * @param uses The uses to set. + */ + public void setUses(int uses) { + this.uses = uses; + } + + /** + * Gets the greet value. + * @return The greet. + */ + public boolean isGreet() { + return greet; + } + + /** + * Sets the greet value. + * @param greet The greet to set. + */ + public void setGreet(boolean greet) { + this.greet = greet; + } + + /** + * Gets the type value. + * @return The type. + */ + public ServantType getType() { + return type; + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/ServantType.java b/Server/src/main/content/global/skill/construction/ServantType.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/skill/construction/ServantType.java rename to Server/src/main/content/global/skill/construction/ServantType.java index e709d2692..b505c6b0b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/ServantType.java +++ b/Server/src/main/content/global/skill/construction/ServantType.java @@ -1,7 +1,8 @@ -package core.game.node.entity.skill.construction; +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/java/core/game/node/entity/skill/construction/decoration/ChairBenchPlugin.java b/Server/src/main/content/global/skill/construction/decoration/ChairBenchPlugin.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/construction/decoration/ChairBenchPlugin.java rename to Server/src/main/content/global/skill/construction/decoration/ChairBenchPlugin.java index d38840226..87332a980 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/ChairBenchPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/ChairBenchPlugin.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.construction.decoration; +package content.global.skill.construction.decoration; import core.cache.def.impl.SceneryDefinition; -import core.game.node.entity.skill.construction.Decoration; +import content.global.skill.construction.Decoration; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.impl.ForceMovement; diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/ConstructionDoorPlugin.kt b/Server/src/main/content/global/skill/construction/decoration/ConstructionDoorPlugin.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/ConstructionDoorPlugin.kt rename to Server/src/main/content/global/skill/construction/decoration/ConstructionDoorPlugin.kt index 43a09b716..75a9e2207 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/ConstructionDoorPlugin.kt +++ b/Server/src/main/content/global/skill/construction/decoration/ConstructionDoorPlugin.kt @@ -1,13 +1,13 @@ -package rs09.game.node.entity.skill.construction.decoration +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.content.global.action.DoorActionHandler +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 core.game.node.entity.skill.construction.BuildHotspot -import core.game.node.entity.skill.construction.HousingStyle +import core.game.node.scenery.Scenery import core.plugin.Initializable import core.plugin.Plugin @@ -38,11 +38,11 @@ class ConstructionDoorPlugin : OptionHandler() { } override fun handle(player: Player, node: Node, option: String): Boolean { - val `object` = node as Scenery - val second = DoorActionHandler.getSecondDoor(`object`, player) when (option) { "pick-lock", "force" -> return false //TODO } + val `object` = node as Scenery + val second = DoorActionHandler.getSecondDoor(`object`, player) DoorActionHandler.open(`object`, second, getReplaceId(`object`), getReplaceId(second), true, 500, false) return true } @@ -80,4 +80,4 @@ class ConstructionDoorPlugin : OptionHandler() { intArrayOf(13119, 13121) ) } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/FireplacePlugin.java b/Server/src/main/content/global/skill/construction/decoration/FireplacePlugin.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/skill/construction/decoration/FireplacePlugin.java rename to Server/src/main/content/global/skill/construction/decoration/FireplacePlugin.java index 4cbc250d2..d6dad175b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/FireplacePlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/FireplacePlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction.decoration; +package content.global.skill.construction.decoration; import core.cache.def.impl.SceneryDefinition; @@ -11,9 +11,10 @@ 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 rs09.game.world.GameWorld; +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/java/core/game/node/entity/skill/construction/decoration/StaircasePlugin.java b/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/skill/construction/decoration/StaircasePlugin.java rename to Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java index d204bdcbc..8c22d84ab 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/StaircasePlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/StaircasePlugin.java @@ -1,15 +1,12 @@ -package core.game.node.entity.skill.construction.decoration; +package content.global.skill.construction.decoration; +import content.global.skill.construction.*; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +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.entity.skill.construction.BuildingUtils; -import core.game.node.entity.skill.construction.HouseManager; -import core.game.node.entity.skill.construction.Room; -import core.game.node.entity.skill.construction.RoomProperties; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; @@ -17,9 +14,8 @@ import core.game.world.map.Direction; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.node.entity.skill.construction.Hotspot; -import rs09.game.world.GameWorld; -import rs09.plugin.PluginManager; +import core.game.world.GameWorld; +import core.plugin.ClassScanner; /** * Handles construction staircases. @@ -31,17 +27,21 @@ public final class StaircasePlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new BuildDialogue()); - PluginManager.definePlugin(new ClimbPohLadder()); - for (int i = 13497; i < 13507; i++) { + ClassScanner.definePlugin(new BuildDialogue()); + ClassScanner.definePlugin(new ClimbPohLadder()); + // 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); } @@ -73,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; } @@ -132,7 +166,7 @@ public final class StaircasePlugin extends OptionHandler { } else { Location destination = l.transform(0, 0, z); if (player.getViewport().getRegion() == house.getDungeonRegion()) { - destination = house.getRegion().getBaseLocation().transform(l.getLocalX(), l.getLocalY(), 0); + destination = house.getHouseRegion().getBaseLocation().transform(l.getLocalX(), l.getLocalY(), 0); } else if (plane == 3) { destination = house.getDungeonRegion().getBaseLocation().transform(l.getLocalX(), l.getLocalY(), 0); @@ -383,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/java/core/game/node/entity/skill/construction/decoration/bedroom/ClockPlugin.java b/Server/src/main/content/global/skill/construction/decoration/bedroom/ClockPlugin.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/construction/decoration/bedroom/ClockPlugin.java rename to Server/src/main/content/global/skill/construction/decoration/bedroom/ClockPlugin.java index abc594914..53c44bb42 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/bedroom/ClockPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/bedroom/ClockPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction.decoration.bedroom; +package content.global.skill.construction.decoration.bedroom; import core.cache.def.impl.SceneryDefinition; diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/bedroom/ShavingStandHandler.kt b/Server/src/main/content/global/skill/construction/decoration/bedroom/ShavingStandHandler.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/bedroom/ShavingStandHandler.kt rename to Server/src/main/content/global/skill/construction/decoration/bedroom/ShavingStandHandler.kt index dbd8a649f..9b9322168 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/bedroom/ShavingStandHandler.kt +++ b/Server/src/main/content/global/skill/construction/decoration/bedroom/ShavingStandHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.construction.decoration.bedroom +package content.global.skill.construction.decoration.bedroom import core.cache.def.impl.SceneryDefinition import core.game.component.Component diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/bedroom/WardrobeHandler.kt b/Server/src/main/content/global/skill/construction/decoration/bedroom/WardrobeHandler.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/bedroom/WardrobeHandler.kt rename to Server/src/main/content/global/skill/construction/decoration/bedroom/WardrobeHandler.kt index 84e5f7146..5cc89e118 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/bedroom/WardrobeHandler.kt +++ b/Server/src/main/content/global/skill/construction/decoration/bedroom/WardrobeHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.construction.decoration.bedroom +package content.global.skill.construction.decoration.bedroom import core.cache.def.impl.SceneryDefinition import core.game.component.Component @@ -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/java/core/game/node/entity/skill/construction/decoration/chapel/BoneOfferPlugin.java b/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/construction/decoration/chapel/BoneOfferPlugin.java rename to Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java index 3a8ee87aa..e681c861d 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/chapel/BoneOfferPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java @@ -1,9 +1,9 @@ -package core.game.node.entity.skill.construction.decoration.chapel; +package content.global.skill.construction.decoration.chapel; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.Bones; +import content.global.skill.prayer.Bones; import core.game.node.entity.skill.Skills; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -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 @@ -89,7 +90,7 @@ public class BoneOfferPlugin extends UseWithHandler { Location gfxLoc = player.getLocation().transform(player.getDirection(), 1); - ContentAPI.submitIndividualPulse(player, new Pulse(1) { + submitIndividualPulse(player, new Pulse(1) { int counter = 0; @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 new file mode 100644 index 000000000..a6c67fa0a --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/chapel/BurnerListener.kt @@ -0,0 +1,45 @@ +package content.global.skill.construction.decoration.chapel + +import core.game.node.scenery.Scenery +import core.game.node.scenery.SceneryBuilder +import core.game.node.item.Item +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. + * @author Splinter + */ +class BurnerListener : InteractionListener { + + val IDs = intArrayOf(13202,13203,13204,13205,13206,13207,13208,13209,13210,13211,13212,13213) + + override fun defineListeners() { + on(IDs, IntType.SCENERY, "light"){ player, node -> + if (player.ironmanManager.checkRestriction() && !player.houseManager.isInHouse(player)) { + return@on true + } + 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(Items.CLEAN_MARRENTILL_251))) { + player.lock(1) + player.animate(Animation.create(3687)) + player.sendMessage("You burn some marrentill in the incense burner.") + SceneryBuilder.replace( + node.asScenery(), + Scenery(node.asScenery().id + 1, node.location), + RandomFunction.random(100, 175) + ) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt b/Server/src/main/content/global/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt rename to Server/src/main/content/global/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt index 5d6708c7f..9a8e34c76 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt +++ b/Server/src/main/content/global/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt @@ -1,14 +1,15 @@ -package rs09.game.node.entity.skill.construction.decoration.chapel +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/java/core/game/node/entity/skill/construction/decoration/combatroom/CombatRing.java b/Server/src/main/content/global/skill/construction/decoration/combatroom/CombatRing.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/construction/decoration/combatroom/CombatRing.java rename to Server/src/main/content/global/skill/construction/decoration/combatroom/CombatRing.java index 8720a88b8..c2b135fed 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/combatroom/CombatRing.java +++ b/Server/src/main/content/global/skill/construction/decoration/combatroom/CombatRing.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.construction.decoration.combatroom; +package content.global.skill.construction.decoration.combatroom; import core.cache.def.impl.SceneryDefinition; 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/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/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/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/kotlin/rs09/game/node/entity/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt b/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt rename to Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt index 8858897ae..eeaf6e455 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt +++ b/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt @@ -1,8 +1,8 @@ -package rs09.game.node.entity.skill.construction.decoration.portalchamber +package content.global.skill.construction.decoration.portalchamber -import core.game.content.dialogue.DialoguePlugin +import core.game.dialogue.DialoguePlugin import core.game.node.entity.player.Player -import core.game.node.entity.skill.construction.decoration.portalchamber.PortalChamberPlugin +import content.global.skill.construction.decoration.portalchamber.PortalChamberPlugin import core.plugin.Initializable /** diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/portalchamber/PortalChamberPlugin.java b/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberPlugin.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/skill/construction/decoration/portalchamber/PortalChamberPlugin.java rename to Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberPlugin.java index ddc1d3f2d..c154bef68 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/portalchamber/PortalChamberPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberPlugin.java @@ -1,21 +1,25 @@ -package core.game.node.entity.skill.construction.decoration.portalchamber; +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; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +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.entity.skill.construction.Decoration; -import core.game.node.entity.skill.runecrafting.Rune; +import content.global.skill.construction.Decoration; 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 rs09.game.node.entity.skill.construction.Hotspot; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; + +import static content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners.ARDOUGNE_TELE_ATTRIBUTE; +import static core.api.ContentAPIKt.*; /** * PortalChamberPlugin @@ -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); @@ -112,7 +121,7 @@ public class PortalChamberPlugin extends OptionHandler { for (int i = 13615; i <= 13635; i++) { SceneryDefinition.forId(i).getHandlers().put("option:enter", this); } - PluginManager.definePlugin(new DirectPortalDialogue()); + ClassScanner.definePlugin(new DirectPortalDialogue()); return this; } @@ -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 new file mode 100644 index 000000000..b5196af4d --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGlory.kt @@ -0,0 +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.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 org.rs09.consts.Items +import org.rs09.consts.Sounds + +class MountedGlory : InteractionListener { + val MOUNTED_GLORY = 13523 + val TELEPORTS = EnchantedJewellery.AMULET_OF_GLORY.locations + + override fun defineListeners() { + on(MOUNTED_GLORY, IntType.SCENERY, "Edgeville") { player, `object` -> + mountedGloryAction(player, `object`, 0) + return@on true + } + on(MOUNTED_GLORY, IntType.SCENERY, "Karamja") { player, `object` -> + mountedGloryAction(player, `object`, 1) + return@on true + } + on(MOUNTED_GLORY, IntType.SCENERY, "Draynor Village") { player, `object` -> + mountedGloryAction(player, `object`, 2) + return@on true + } + 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 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) + } + resetAnimator(player) + teleport(player, TELEPORTS[int]) + return@queueScript stopExecuting(player) + } + } +} 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 new file mode 100644 index 000000000..eb4accf67 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/study/LecternPlugin.kt @@ -0,0 +1,227 @@ +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 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 +import core.game.world.update.flag.context.Animation +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 + * LecternPlugin + * @author Ceikry + */ +@Initializable +class LecternPlugin : OptionHandler() { + /** + * TeleTabButton + */ + private enum class TeleTabButton( + /** + * The button id + */ + val buttonId: Int, + /** + * The level and xp + */ + val level: Int, + val xp: Double, + /** + * The item + */ + val tabItem: Item, + /** + * The required decoration (lectern) + */ + private val requiredDecorations: Array, vararg requiredItems: Item) { + 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 + */ + val requiredItems: ArrayList = arrayListOf(*requiredItems) + + /** + * Checks if the player can make this tab + * @param player + * @return + */ + fun canMake(player: Player): Boolean { + val objectId = player.getAttribute("ttb:objectid") + if (player.skills.getLevel(Skills.MAGIC) < level && player.spellBookManager.spellBook == 192) { + player.sendMessage("You need a magic level of $level to make that!") + return false + } + if (this == BONES_TO_PEACHES && !player.savedData.activityData.isBonesToPeaches) { + 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) { + player.sendMessage("You're unable to make this tab on this specific lectern.") + return false + } + for (item in requiredItems) { + val staff = MagicStaff.forId(item.id) + if (staff != null && player.equipment.containsAtLeastOneItem(staff.staves)) { + continue + } + if (!player.inventory.containsItem(item)) { + //TODO staffs + player.sendMessage("You don't have enough materials.") + return false + } + } + return true + } + + companion object { + /** + * Gets the button for the id + * @param id + * @return + */ + fun forId(id: Int): TeleTabButton? { + for (ttb in values()) { + if (ttb.buttonId == id) return ttb + } + return null + } + } + + } + + @Throws(Throwable::class) + override fun newInstance(arg: Any?): Plugin? { + for (i in 13642..13648) { + SceneryDefinition.forId(i).handlers["option:study"] = this + } + return this + } + + override fun handle(player: Player, node: Node, option: String): Boolean { + val id = node.asScenery().id + player.setAttribute("ttb:objectid", id) + GameWorld.Pulser.submit(object : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> player.animator.animate(Animation(1894)).also { player.lock() } + 1 -> player.interfaceManager.open(Component(400)).also { player.unlock(); return true } + } + return false + } + }) + return true + } + + /** + * TeleTabInterface + * @author Ceikry + */ + class TeleTabInterface : InterfaceListener { + val decorationVarps = hashMapOf( + Decoration.OAK_LECTERN to Pair(0, 0), + Decoration.EAGLE_LECTERN to Pair(1, 0), + Decoration.DEMON_LECTERN to Pair(0, 1), + Decoration.TEAK_EAGLE_LECTERN to Pair(2, 0), + Decoration.TEAK_DEMON_LECTERN to Pair(0, 2), + Decoration.MAHOGANY_EAGLE_LECTERN to Pair(3, 0), + Decoration.MAHOGANY_DEMON_LECTERN to Pair(0, 3), + ) + override fun defineInterfaceListeners() { + onOpen(400) { player, component -> + val id = player.getAttribute("ttb:objectid", 0) + val deco = Decoration.forObjectId(id) + val values = decorationVarps[deco] ?: Pair(0, 0) + setVarp(player, 261, values.first) + setVarp(player, 262, values.second) + return@onOpen true + } + on(400) { player, _, _, buttonID, _, _ -> + val ttb = TeleTabButton.forId(buttonID) + if (ttb != null && ttb.canMake(player)) { + player.interfaceManager.close() + var requiredItemsCountingStaves = ttb.requiredItems.filter({ item -> + val staff = MagicStaff.forId(item.id) + !(staff != null && player.equipment.containsAtLeastOneItem(staff.staves)) + }).toTypedArray() + player.pulseManager.run(object : Pulse(1) { + var counter = 0 + override fun pulse(): Boolean { + when(counter++) { + 0 -> { + if (!ttb.canMake(player)) { + return true + } + //TODO Add correct lectern animation (should look like raising arms progressively higher 3 times). + player.animate(Animation(782)) + } + 2 -> { + if (player.inventory.remove(*requiredItemsCountingStaves)) { + // Should never drop, since soft clay was successfully removed + addItemOrDrop(player, ttb.tabItem.id) + player.skills.addExperience(Skills.MAGIC, ttb.xp, true) + if (ttb == TeleTabButton.VARROCK + && (player.getAttribute("ttb:objectid", 0) == Decoration.MAHOGANY_EAGLE_LECTERN.objectId + || player.getAttribute("ttb:objectid", 0) == Decoration.MAHOGANY_DEMON_LECTERN.objectId)) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 8) + } + } else { + return true + } + } + } + counter %= 6 + return false + } + }) + } + player.animate(Animation(-1)) + return@on true + } + } + } + + companion object { + /** + * Soft clay + */ + 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 new file mode 100644 index 000000000..ea1d8c783 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/study/TelescopePlugin.kt @@ -0,0 +1,59 @@ +package content.global.skill.construction.decoration.study + +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.scenery.Scenery +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 +import core.tools.RandomFunction +import content.global.activity.shootingstar.ShootingStarPlugin +import core.game.world.GameWorld.Pulser +import java.util.concurrent.TimeUnit + +@Initializable +class TelescopePlugin : OptionHandler() { + @Throws(Throwable::class) + override fun newInstance(arg: Any?): Plugin? { + SceneryDefinition.forId(13656).handlers["option:observe"] = this + SceneryDefinition.forId(13657).handlers["option:observe"] = this + SceneryDefinition.forId(13658).handlers["option:observe"] = this + return this + } + + 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 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 $fakeTimeLeft minutes!") + return true + } + return true + } + }) + return true + } + } + + companion object { + private val ANIMATION = Animation.create(3649) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..44a050754 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/ArmourStand.kt @@ -0,0 +1,121 @@ +package content.global.skill.construction.decoration.workshop + +import core.game.dialogue.DialoguePlugin +import content.data.RepairItem +import core.game.interaction.NodeUsageEvent +import core.game.interaction.UseWithHandler +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(*ALL_REPAIRABLE_ITEM_IDS) { + override fun newInstance(arg: Any?): Plugin { + addHandler(13715, OBJECT_TYPE, this) + return this + } + + override fun handle(event: NodeUsageEvent?): Boolean { + event ?: return false + val player = event.player + val usedItem = event.used.asItem() + + var baseCost = 0 + var product: Item? = null + + val repairItem = RepairItem.forId(usedItem.id) + val barrowsDef = BarrowsEquipment.getDefinition(usedItem.id) + + 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 discountMultiplier = (100.0 - (player.skills.getLevel(Skills.SMITHING) / 2.0)) / 100.0 + val cost = ceil(discountMultiplier * baseCost).toInt() + + player.dialogueInterpreter.open(58824213,usedItem, cost, product) + return true + } + @Initializable + class RepairDialogue(player: Player? = null) : DialoguePlugin(player) { + + 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 + + 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 { + 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 + } + + 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)) { + 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.") + } + } + + override fun getIds(): IntArray { + return intArrayOf(58824213) + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java b/Server/src/main/content/global/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java rename to Server/src/main/content/global/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java index c75439dcf..410f1a4a0 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java @@ -1,13 +1,13 @@ -package core.game.node.entity.skill.construction.decoration.workshop; +package content.global.skill.construction.decoration.workshop; +import content.global.skill.construction.BuildingUtils; +import content.global.skill.construction.Decoration; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.construction.BuildingUtils; -import core.game.node.entity.skill.construction.Decoration; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -15,7 +15,8 @@ import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +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 @@ -77,7 +78,7 @@ public class ClockmakersBenchPlugin extends OptionHandler { SceneryDefinition.forId(13710).getHandlers().put("option:craft", this); SceneryDefinition.forId(13711).getHandlers().put("option:craft", this); SceneryDefinition.forId(13712).getHandlers().put("option:craft", this); - PluginManager.definePlugin(new ClockmakerBenchDialogue()); + ClassScanner.definePlugin(new ClockmakerBenchDialogue()); return this; } 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/java/core/game/node/entity/skill/construction/npc/HouseServantDialogue.java b/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/skill/construction/npc/HouseServantDialogue.java rename to Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java index d6b4727d7..ddb967ebe 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/npc/HouseServantDialogue.java +++ b/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java @@ -1,22 +1,24 @@ -package core.game.node.entity.skill.construction.npc; +package content.global.skill.construction.npc; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import content.global.skill.construction.HouseManager; +import content.global.skill.construction.ServantType; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.construction.HouseManager; -import core.game.node.entity.skill.construction.Servant; -import core.game.node.entity.skill.construction.ServantType; +import content.global.skill.construction.Servant; import core.game.interaction.MovementPulse; 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.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.path.Pathfinder; -import core.game.interaction.inter.SawmillPlankInterface.Plank; +import content.global.handlers.iface.SawmillPlankInterface.Plank; +import org.rs09.consts.Items; /** * Handles the Servant's dialogues. @@ -145,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; @@ -172,7 +174,7 @@ public class HouseServantDialogue extends DialoguePlugin { case 3: player("Stop following me."); if (servant.getPulseManager().isMovingPulse()) { - servant.getPulseManager().clear("movement"); + servant.getPulseManager().clear(PulseType.STANDARD); } stage = 100; break; @@ -217,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); } @@ -310,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"); @@ -332,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"); @@ -352,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; @@ -477,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); @@ -523,7 +525,7 @@ public class HouseServantDialogue extends DialoguePlugin { @Override public boolean pulse() { - if (player == null || player.getHouseManager().getRegion() != player.getViewport().getRegion()) { //TODO: Check if in dungeon? + if (player == null || player.getHouseManager().getHouseRegion() != player.getViewport().getRegion()) { //TODO: Check if in dungeon? return true; } int amt = player.getBank().getAmount(item.getId()); @@ -562,6 +564,6 @@ public class HouseServantDialogue extends DialoguePlugin { public boolean pulse() { return false; } - }, "movement"); + }, PulseType.STANDARD); } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/npc/HouseServantPlugin.java b/Server/src/main/content/global/skill/construction/npc/HouseServantPlugin.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/skill/construction/npc/HouseServantPlugin.java rename to Server/src/main/content/global/skill/construction/npc/HouseServantPlugin.java index 84a2a3995..68af6493e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/npc/HouseServantPlugin.java +++ b/Server/src/main/content/global/skill/construction/npc/HouseServantPlugin.java @@ -1,11 +1,11 @@ -package core.game.node.entity.skill.construction.npc; +package content.global.skill.construction.npc; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles interaction with the house servant. @@ -39,7 +39,7 @@ public class HouseServantPlugin extends UseWithHandler { addHandler(4239, NPC_TYPE, this); addHandler(4241, NPC_TYPE, this); addHandler(4243, NPC_TYPE, this); - PluginManager.definePlugin(new HouseServantDialogue()); + ClassScanner.definePlugin(new HouseServantDialogue()); return this; } diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/npc/StonemasonPlugin.java b/Server/src/main/content/global/skill/construction/npc/StonemasonPlugin.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/skill/construction/npc/StonemasonPlugin.java rename to Server/src/main/content/global/skill/construction/npc/StonemasonPlugin.java index 9b306ad71..b83bb5965 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/npc/StonemasonPlugin.java +++ b/Server/src/main/content/global/skill/construction/npc/StonemasonPlugin.java @@ -1,8 +1,7 @@ -package core.game.node.entity.skill.construction.npc; +package content.global.skill.construction.npc; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; -import core.game.content.global.shop.Shop; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -16,11 +15,11 @@ import core.plugin.Plugin; */ @Initializable public class StonemasonPlugin extends OptionHandler { - - /** +/* + *//** * The store that sells supplies. - */ - private static final SupplyStore STORE = new SupplyStore(); + *//* + private static final SupplyStore STORE = new SupplyStore();*/ @Override public Plugin newInstance(Object arg) throws Throwable { @@ -34,23 +33,22 @@ public class StonemasonPlugin extends OptionHandler { switch (option) { case "trade": case "talk-to": - STORE.open(player); break; } return true; } - - /** +/* + *//** * Stonemason's store. * @author Splinter - */ + *//* public static class SupplyStore extends Shop { - /** + *//** * 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 @@ -73,6 +71,6 @@ public class StonemasonPlugin extends OptionHandler { } return -1; } - } + }*/ } \ No newline at end of file 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/kotlin/rs09/game/node/entity/skill/cooking/ChocolateBarCutter.kt b/Server/src/main/content/global/skill/cooking/ChocolateBarCutter.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/cooking/ChocolateBarCutter.kt rename to Server/src/main/content/global/skill/cooking/ChocolateBarCutter.kt index 184e9948f..e2a5fc126 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/cooking/ChocolateBarCutter.kt +++ b/Server/src/main/content/global/skill/cooking/ChocolateBarCutter.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.cooking +package content.global.skill.cooking import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler 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/CookableItems.java b/Server/src/main/content/global/skill/cooking/CookableItems.java new file mode 100644 index 000000000..4917db765 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/CookableItems.java @@ -0,0 +1,173 @@ +package content.global.skill.cooking; + +import org.rs09.consts.Items; +import core.game.node.item.Item; + +import java.util.HashMap; + +public enum CookableItems { + // 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 new file mode 100644 index 000000000..a560c8026 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/CookingDialogue.kt @@ -0,0 +1,134 @@ +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.CookingRewrite.Companion.cook +import core.net.packet.PacketRepository +import core.net.packet.context.ChildPositionContext +import core.net.packet.out.RepositionChild +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +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(){ + var initial = 0 + var product = 0 + var `object`: Scenery? = null + var sinew = false + var itemid = 0 + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + START_DIALOGUE -> { + when (args.size) { + 2 -> { + initial = args.get(0) as Int + if (CookableItems.intentionalBurn(initial)) { // checks intentional burning + product = CookableItems.getIntentionalBurn(initial).id + } else { + product = CookableItems.forId(initial).cooked + } + `object` = args.get(1) as Scenery + } + 5 -> { + initial = args.get(0) as Int + product = args.get(1) as Int + sinew = args.get(2) as Boolean + `object` = args.get(3) as Scenery + itemid = args.get(4) as Int + if (sinew) { + options( + "Dry the meat into sinew", + "Cook the meat" + ) + stage = if (amountInInventory(player!!, initial) > 1) 100 else 101 + return + } + } + } + display() + } + + 1 -> { + end() + val amount = getAmount(buttonID) + when (amount) { + -1 -> sendInputDialogue(player!!, true, "Enter the amount:") { value -> + if (value is String) { + cook(player!!, `object`, initial, product, value.toInt()) + } else { + cook(player!!, `object`, initial, product, value as Int) + } + } + + else -> { + end() + cook(player!!, `object`, initial, product, amount) + } + } + } + + 100 -> { + when (buttonID) { + 1 -> { + product = Items.SINEW_9436 + display() + } + 2 -> { + product = CookableItems.forId(initial).cooked + display() + } + } + } + + 101 -> { + when (buttonID) { + 1 -> { + end() + cook(player!!, `object`, initial, Items.SINEW_9436, 1) + } + 2 -> { + end() + cook(player!!, `object`, initial, CookableItems.forId(initial).cooked, 1) + } + } + } + } + } + + private fun getAmount(buttonId: Int): Int { + when (buttonId) { + 5 -> return 1 + 4 -> return 5 + 3 -> return -1 + 2 -> return player!!.inventory.getAmount(initial) + } + return -1 + } + + 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) + stage = 1 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/CookingRewrite.kt b/Server/src/main/content/global/skill/cooking/CookingRewrite.kt new file mode 100644 index 000000000..36679ef92 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/CookingRewrite.kt @@ -0,0 +1,124 @@ +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 { + + val RAW_FOODS: IntArray + + init { + val list = CookableItems.values().map { it.raw }.toMutableList() + 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") + when (item.id) { + RAW_BEEF_2132, RAW_BEAR_MEAT_2136 -> if (range) { + player.dialogueInterpreter.open(CookingDialogue(item.id,9436,true,obj,item.id)) + return@onUseWith true + } + 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 + 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, 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) { + val food = Item(initial) + if (food.name.toLowerCase().contains("pizza")) { + player.pulseManager.run(PizzaCookingPulse(player, `object`, initial, product, amount)) + } else if (food.name.toLowerCase().contains("pie")) { + player.pulseManager.run(PieCookingPulse(player, `object`, initial, product, amount)) + } else if (CookableItems.intentionalBurn(initial)) { + player.pulseManager.run(IntentionalBurnPulse(player, `object`, initial, product, amount)) + } else { + player.pulseManager.run(StandardCookingPulse(player, `object`, initial, product, amount)) + } + } + } +} diff --git a/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt b/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt new file mode 100644 index 000000000..267d9ccfe --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/DoughMakingListener.kt @@ -0,0 +1,99 @@ +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 +import core.game.node.item.Item +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class DoughMakingListener : InteractionListener { + companion object { + private val FULL_WATER_CONTAINERS_TO_EMPTY_CONTAINERS = hashMapOf( + Items.BUCKET_OF_WATER_1929 to Items.BUCKET_1925, + Items.BOWL_OF_WATER_1921 to Items.BOWL_1923, + Items.JUG_OF_WATER_1937 to Items.JUG_1935 + ) + } + + override fun defineListeners() { + onUseWith( + IntType.ITEM, + FULL_WATER_CONTAINERS_TO_EMPTY_CONTAINERS.keys.toIntArray(), + Items.POT_OF_FLOUR_1933 + ) { player, waterContainer, flourContainer -> + 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 + } + } + + private class DoughMakeDialogue(val waterContainer: Item, val flourContainer: Item) : DialogueFile() { + companion object { + private const val STAGE_PRESENT_OPTIONS = 0 + private const val STAGE_PROCESS_OPTION = 1 + + private enum class DoughProduct(val itemId: Int, val itemName: String, val requiredCookingLevel: Int) { + BREAD_DOUGH(Items.BREAD_DOUGH_2307, getItemName(Items.BREAD_DOUGH_2307), 1), + PASTRY_DOUGH(Items.PASTRY_DOUGH_1953, getItemName(Items.PASTRY_DOUGH_1953), 1), + PIZZA_DOUGH(Items.PIZZA_BASE_2283, getItemName(Items.PIZZA_BASE_2283), 35), + PITTA_DOUGH(Items.PITTA_DOUGH_1863, getItemName(Items.PITTA_DOUGH_1863), 58), + } + } + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + STAGE_PRESENT_OPTIONS -> { + player!!.dialogueInterpreter.sendOptions( + "What do you wish to make?", + *(DoughProduct.values().map { "${it.itemName}." }.toTypedArray()) + ).also { stage++ } + } + STAGE_PROCESS_OPTION -> runTask(player!!, 1) { + end() + val selectedDoughProduct = DoughProduct.values()[buttonID - 1] + if (hasLevelDyn(player!!, Skills.COOKING, selectedDoughProduct.requiredCookingLevel)) { + if (freeSlots(player!!) < 1) { + sendMessage( + player!!, + "Not enough space in your inventory." + ) + return@runTask + } + + if (removeItem(player!!, waterContainer) && removeItem(player!!, flourContainer)) { + addItem(player!!, selectedDoughProduct.itemId) + player!!.dispatch(ResourceProducedEvent(selectedDoughProduct.itemId, 1, player!!)) + + val emptyWaterContainerId = FULL_WATER_CONTAINERS_TO_EMPTY_CONTAINERS[waterContainer.id]!! + addItem(player!!, emptyWaterContainerId) + + addItem(player!!, Items.EMPTY_POT_1931) + + sendMessage( + player!!, + "You mix the flour and the water to make some ${selectedDoughProduct.itemName.toLowerCase()}." + ) + } + } else { + sendDialogue( + player!!, + "You need a Cooking level of at least ${selectedDoughProduct.requiredCookingLevel} in order to make ${selectedDoughProduct.itemName.toLowerCase()}." + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/FireOptionPotteryPlugin.java b/Server/src/main/content/global/skill/cooking/FireOptionPotteryPlugin.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/cooking/FireOptionPotteryPlugin.java rename to Server/src/main/content/global/skill/cooking/FireOptionPotteryPlugin.java index 8a6c6971b..c0def779b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/FireOptionPotteryPlugin.java +++ b/Server/src/main/content/global/skill/cooking/FireOptionPotteryPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.cooking; +package content.global.skill.cooking; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/IntentionalBurnPulse.java b/Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java similarity index 76% rename from Server/src/main/java/core/game/node/entity/skill/cooking/IntentionalBurnPulse.java rename to Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java index 632ee7f62..3e974f313 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/IntentionalBurnPulse.java +++ b/Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java @@ -1,8 +1,11 @@ -package core.game.node.entity.skill.cooking; +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 @@ -30,20 +33,24 @@ public class IntentionalBurnPulse extends StandardCookingPulse { } if(cook(player,null,false,initial,product)) { amount--; + } else { + return true; } - return amount < 1; + // we are always one off a normal cooking pulse because + // the first tick is handled outside of this class. + return amount <= 1; } @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/java/core/game/node/entity/skill/cooking/MakeStewPlugin.java b/Server/src/main/content/global/skill/cooking/MakeStewPlugin.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/cooking/MakeStewPlugin.java rename to Server/src/main/content/global/skill/cooking/MakeStewPlugin.java index 4a5102f4b..33436f4bb 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/MakeStewPlugin.java +++ b/Server/src/main/content/global/skill/cooking/MakeStewPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.cooking; +package content.global.skill.cooking; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; 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/java/core/game/node/entity/skill/cooking/PieCookingPulse.java b/Server/src/main/content/global/skill/cooking/PieCookingPulse.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/cooking/PieCookingPulse.java rename to Server/src/main/content/global/skill/cooking/PieCookingPulse.java index f19b45265..633af5815 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/PieCookingPulse.java +++ b/Server/src/main/content/global/skill/cooking/PieCookingPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.cooking; +package content.global.skill.cooking; import core.cache.def.impl.ItemDefinition; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/PizzaCookingPulse.java b/Server/src/main/content/global/skill/cooking/PizzaCookingPulse.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/cooking/PizzaCookingPulse.java rename to Server/src/main/content/global/skill/cooking/PizzaCookingPulse.java index 4d0bdd19c..45653e594 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/PizzaCookingPulse.java +++ b/Server/src/main/content/global/skill/cooking/PizzaCookingPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.cooking; +package content.global.skill.cooking; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/content/global/skill/cooking/SaucyKebabListener.kt b/Server/src/main/content/global/skill/cooking/SaucyKebabListener.kt new file mode 100644 index 000000000..fc22db082 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/SaucyKebabListener.kt @@ -0,0 +1,31 @@ +package content.global.skill.cooking + + +import core.api.* + +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + + +/** + * @author bushtail + * That's-a spicy kebab! + */ +class SaucyKebabListener : InteractionListener { + val sauce = Items.RED_HOT_SAUCE_4610 + val kebabArr = intArrayOf( + Items.KEBAB_1971, + Items.UGTHANKI_KEBAB_1883, + Items.UGTHANKI_KEBAB_1885 + ) + + override fun defineListeners() { + onUseWith(IntType.ITEM, kebabArr, sauce) { player, used, with -> + if(removeItem(player, used.asItem()) && removeItem(player, with.asItem())) { + return@onUseWith addItem(player, Items.SUPER_KEBAB_4608) + } + return@onUseWith false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/SinewCookingPulse.java b/Server/src/main/content/global/skill/cooking/SinewCookingPulse.java new file mode 100644 index 000000000..4f9e6be82 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/SinewCookingPulse.java @@ -0,0 +1,32 @@ +package content.global.skill.cooking; + +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.game.node.scenery.Scenery; + +/** + * Fixes the half assed previous implementation of cooking Sinew. + * @author Woah + */ +public class SinewCookingPulse extends StandardCookingPulse { + + public SinewCookingPulse(Player player, Scenery object, int initial, int product, int amount) { + super(player, object, initial, product, amount); + } + + @Override + public boolean checkRequirements() { + properties = CookableItems.SINEW; + return super.checkRequirements(); + } + + @Override + public boolean isBurned(Player player, Scenery object, int food) { + return false; + } + + @Override + public String getMessage(Item food, Item product, boolean burned) { + return "You dry a piece of beef and extract the sinew."; + } +} 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/StandardCookingPulse.java b/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java new file mode 100644 index 000000000..34dc67319 --- /dev/null +++ b/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java @@ -0,0 +1,227 @@ +package content.global.skill.cooking; + +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; +import core.game.node.item.Item; +import core.game.node.scenery.Scenery; +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 + 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(2577, 1, 1); + + private static final int LUMBRIDGE_RANGE = 114; + + private final int initial; + private final int product; + private int amount; + private final Scenery object; + private final Player player; + private double experience; + 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 + public void start() { + properties = CookableItems.forId(initial); + if (checkRequirements()) { + super.start(); + cook(player, object, properties != null && burned, initial, product); + amount--; + } + } + + @Override + public boolean pulse() { + if (amount < 1 || !checkRequirements()) { + return true; + } + return reward(); + } + + public void animate() { + player.animate(getAnimation(object)); + } + + public boolean checkRequirements() { + this.experience = 0; + if (properties != null) { + // Handle Cook's Assistant range + 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; + } + + //check level + if (player.getSkills().getLevel(Skills.COOKING) < properties.level) { + player.getDialogueInterpreter().sendDialogue("You need a cooking level of " + properties.level + " to cook this."); + return false; + } + + this.experience = properties.experience; + this.burned = isBurned(player, object, initial); + } + if (amount < 1) { + return false; + } + + return object.isActive(); + } + + public boolean reward() { + if (getDelay() == 1) { + int delay = object.getName().toLowerCase().contains("range") ? 5 : 4; + if (SkillcapePerks.isActive(SkillcapePerks.HASTY_COOKING, player)) { + delay -= 1; + } + setDelay(delay); + return false; + } + + if (cook(player, object, burned, initial, product)) { + amount--; + } else { + return true; + } + return amount < 1; + } + + public boolean isBurned(final Player player, final Scenery object, int food) { + boolean hasGauntlets = player.getEquipment().containsItem(new Item(Items.COOKING_GAUNTLETS_775)); + int effectiveCookingLevel = player.getSkills().getLevel(Skills.COOKING); + if (SkillcapePerks.isActive(SkillcapePerks.HASTY_COOKING, player)) { + effectiveCookingLevel -= 5; + } + CookableItems item = CookableItems.forId(food); + 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; + } + double host_ratio = RandomFunction.randomDouble(100.0); + 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); + + //handle special cooking results (spits, cake, etc) that don't justify separate plugin + switch (initial) { + case Items.SKEWERED_CHOMPY_7230: + case Items.SKEWERED_RABBIT_7224: + case Items.SKEWERED_BIRD_MEAT_9984: + 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."); + } else { + if (!player.getInventory().add(new Item(Items.IRON_SPIT_7225))) { + GroundItemManager.create(new Item(Items.IRON_SPIT_7225), player.getLocation(), player); + } + } + break; + case Items.UNCOOKED_CAKE_1889: //cake + if (!player.getInventory().add(new Item(Items.CAKE_TIN_1887))) { + GroundItemManager.create(new Item(Items.CAKE_TIN_1887), player); + } + break; + } + if (player.getInventory().remove(initialItem)) { + if (!burned) { + 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)); + playAudio(player, Sounds.FRY_2577); + animate(); + return true; + } + return false; + } + + public String getMessage(Item food, Item product, boolean burned) { + if (food.getId() == Items.RAW_OOMLIE_2337) { + return "The meat is far too delicate to cook like this. Perhaps you should wrap something around it to protect it from the heat."; + } + 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 && 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) { + return !object.getName().equalsIgnoreCase("fire") ? RANGE_ANIMATION : FIRE_ANIMATION; + } +} 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/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/java/core/game/content/dialogue/DairyChurnDialogue.java b/Server/src/main/content/global/skill/cooking/dairy/DairyChurnDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/DairyChurnDialogue.java rename to Server/src/main/content/global/skill/cooking/dairy/DairyChurnDialogue.java index eff0de5a0..0c2b5e08f 100644 --- a/Server/src/main/java/core/game/content/dialogue/DairyChurnDialogue.java +++ b/Server/src/main/content/global/skill/cooking/dairy/DairyChurnDialogue.java @@ -1,10 +1,9 @@ -package core.game.content.dialogue; +package content.global.skill.cooking.dairy; import core.game.component.Component; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; import org.rs09.consts.Items; -import core.game.node.entity.skill.cooking.dairy.DairyChurnPulse; -import core.game.node.entity.skill.cooking.dairy.DairyProduct; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/content/dialogue/DairyChurnOptionPlugin.java b/Server/src/main/content/global/skill/cooking/dairy/DairyChurnOptionPlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/DairyChurnOptionPlugin.java rename to Server/src/main/content/global/skill/cooking/dairy/DairyChurnOptionPlugin.java index d4d768963..1c87334ca 100644 --- a/Server/src/main/java/core/game/content/dialogue/DairyChurnOptionPlugin.java +++ b/Server/src/main/content/global/skill/cooking/dairy/DairyChurnOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.dialogue; +package content.global.skill.cooking.dairy; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/dairy/DairyChurnPulse.java b/Server/src/main/content/global/skill/cooking/dairy/DairyChurnPulse.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/skill/cooking/dairy/DairyChurnPulse.java rename to Server/src/main/content/global/skill/cooking/dairy/DairyChurnPulse.java index 050c60a5c..f93e436cb 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/dairy/DairyChurnPulse.java +++ b/Server/src/main/content/global/skill/cooking/dairy/DairyChurnPulse.java @@ -1,5 +1,8 @@ -package core.game.node.entity.skill.cooking.dairy; +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/java/core/game/node/entity/skill/cooking/dairy/DairyProduct.java b/Server/src/main/content/global/skill/cooking/dairy/DairyProduct.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/cooking/dairy/DairyProduct.java rename to Server/src/main/content/global/skill/cooking/dairy/DairyProduct.java index 9d6f31151..09376016a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/dairy/DairyProduct.java +++ b/Server/src/main/content/global/skill/cooking/dairy/DairyProduct.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.cooking.dairy; +package content.global.skill.cooking.dairy; import java.util.Arrays; 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/java/core/game/node/entity/skill/cooking/fermenting/WineFermentingPulse.java b/Server/src/main/content/global/skill/cooking/fermenting/WineFermentingPulse.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/cooking/fermenting/WineFermentingPulse.java rename to Server/src/main/content/global/skill/cooking/fermenting/WineFermentingPulse.java index d6efa5003..4f9dd7a6e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/fermenting/WineFermentingPulse.java +++ b/Server/src/main/content/global/skill/cooking/fermenting/WineFermentingPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.cooking.fermenting; +package content.global.skill.cooking.fermenting; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; 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/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/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/java/core/game/node/entity/skill/crafting/AmuletStringingPlugin.java b/Server/src/main/content/global/skill/crafting/AmuletStringingPlugin.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/crafting/AmuletStringingPlugin.java rename to Server/src/main/content/global/skill/crafting/AmuletStringingPlugin.java index ff7fd9d1e..cfe3cfd24 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/AmuletStringingPlugin.java +++ b/Server/src/main/content/global/skill/crafting/AmuletStringingPlugin.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.crafting.jewellery.JewelleryCrafting; +import content.global.skill.crafting.jewellery.JewelleryCrafting; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.player.Player; 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/java/core/game/node/entity/skill/crafting/ChiselLimestonePlugin.java b/Server/src/main/content/global/skill/crafting/ChiselLimestonePlugin.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/skill/crafting/ChiselLimestonePlugin.java rename to Server/src/main/content/global/skill/crafting/ChiselLimestonePlugin.java index 0f1724b0a..cf584b2b4 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/ChiselLimestonePlugin.java +++ b/Server/src/main/content/global/skill/crafting/ChiselLimestonePlugin.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; -import core.game.node.entity.skill.crafting.limestone.ChiselLimestonePulse; +import content.global.skill.crafting.limestone.ChiselLimestonePulse; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/GemCutPlugin.java b/Server/src/main/content/global/skill/crafting/GemCutPlugin.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/skill/crafting/GemCutPlugin.java rename to Server/src/main/content/global/skill/crafting/GemCutPlugin.java index 22dc62260..f848bacd5 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/GemCutPlugin.java +++ b/Server/src/main/content/global/skill/crafting/GemCutPlugin.java @@ -1,9 +1,9 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; -import core.game.node.entity.skill.crafting.gem.GemCutPulse; -import core.game.node.entity.skill.crafting.gem.Gems; +import content.global.skill.crafting.gem.GemCutPulse; +import core.game.dialogue.SkillDialogueHandler; +import core.game.dialogue.SkillDialogueHandler.SkillDialogue; +import content.global.skill.crafting.gem.Gems; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.player.Player; diff --git a/Server/src/main/content/global/skill/crafting/JewelleryCraftPlugin.java b/Server/src/main/content/global/skill/crafting/JewelleryCraftPlugin.java new file mode 100644 index 000000000..30c382180 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/JewelleryCraftPlugin.java @@ -0,0 +1,49 @@ +package content.global.skill.crafting; + +import core.plugin.Initializable; +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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class JewelleryCraftPlugin extends UseWithHandler { + + /** + * Represents the ids to use for this plugin. + */ + 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}. + */ + public JewelleryCraftPlugin() { + super(2357); + } + + @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) { + JewelleryCrafting.open(event.getPlayer()); + return true; + } + +} diff --git a/Server/src/main/java/core/game/content/dialogue/LeatherCraftDialogue.java b/Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/dialogue/LeatherCraftDialogue.java rename to Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java index b1974676b..0692339a6 100644 --- a/Server/src/main/java/core/game/content/dialogue/LeatherCraftDialogue.java +++ b/Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java @@ -1,16 +1,16 @@ -package core.game.content.dialogue; +package content.global.skill.crafting; -import api.ContentAPI; +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; import kotlin.Unit; -import rs09.game.system.SystemLogger; -import core.game.node.entity.skill.crafting.armour.DragonCraftPulse; -import core.game.node.entity.skill.crafting.armour.HardCraftPulse; -import core.game.node.entity.skill.crafting.armour.LeatherCrafting; -import core.game.node.entity.skill.crafting.armour.LeatherCrafting.DragonHide; +import content.global.skill.crafting.armour.DragonCraftPulse; +import content.global.skill.crafting.armour.HardCraftPulse; +import content.global.skill.crafting.armour.LeatherCrafting; +import content.global.skill.crafting.armour.LeatherCrafting.DragonHide; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; import core.plugin.Initializable; import core.game.node.item.Item; @@ -61,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]; @@ -111,7 +111,7 @@ public final class LeatherCraftDialogue extends DialoguePlugin { amt = 5; break; case 3: - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { player.getPulseManager().run(new HardCraftPulse(player, null, (int) value)); return Unit.INSTANCE; }); @@ -205,7 +205,7 @@ public final class LeatherCraftDialogue extends DialoguePlugin { if (hidee == null) { return false; } - ContentAPI.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/java/core/game/node/entity/skill/crafting/PotteryPlugin.java b/Server/src/main/content/global/skill/crafting/PotteryPlugin.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/skill/crafting/PotteryPlugin.java rename to Server/src/main/content/global/skill/crafting/PotteryPlugin.java index ac4af460b..23ddac860 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/PotteryPlugin.java +++ b/Server/src/main/content/global/skill/crafting/PotteryPlugin.java @@ -1,12 +1,12 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; +import content.global.skill.crafting.pottery.FirePotteryPulse; +import content.global.skill.crafting.pottery.PotteryItem; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; -import core.game.node.entity.skill.crafting.pottery.FirePotteryPulse; -import core.game.node.entity.skill.crafting.pottery.PotteryItem; -import core.game.node.entity.skill.crafting.pottery.PotteryPulse; +import core.game.dialogue.SkillDialogueHandler; +import core.game.dialogue.SkillDialogueHandler.SkillDialogue; +import content.global.skill.crafting.pottery.PotteryPulse; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -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/java/core/game/node/entity/skill/crafting/SnakeSkinPlugin.java b/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/skill/crafting/SnakeSkinPlugin.java rename to Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java index 78b409649..09c2ed605 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/SnakeSkinPlugin.java +++ b/Server/src/main/content/global/skill/crafting/SnakeSkinPlugin.java @@ -1,10 +1,10 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; +import content.global.skill.crafting.armour.SnakeSkin; +import content.global.skill.crafting.armour.SnakeSkinPulse; import core.plugin.Initializable; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; -import core.game.node.entity.skill.crafting.armour.SnakeSkin; -import core.game.node.entity.skill.crafting.armour.SnakeSkinPulse; +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; @@ -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/java/core/game/node/entity/skill/crafting/SnelmCraftPlugin.java b/Server/src/main/content/global/skill/crafting/SnelmCraftPlugin.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/crafting/SnelmCraftPlugin.java rename to Server/src/main/content/global/skill/crafting/SnelmCraftPlugin.java index 3ea63cca4..f23d50b5f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/SnelmCraftPlugin.java +++ b/Server/src/main/content/global/skill/crafting/SnelmCraftPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; import core.plugin.Initializable; import core.game.node.entity.skill.SkillPulse; @@ -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/java/core/game/node/entity/skill/crafting/StitchCraftPlugin.java b/Server/src/main/content/global/skill/crafting/StitchCraftPlugin.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/crafting/StitchCraftPlugin.java rename to Server/src/main/content/global/skill/crafting/StitchCraftPlugin.java index 0c5ccc05d..973710e3b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/StitchCraftPlugin.java +++ b/Server/src/main/content/global/skill/crafting/StitchCraftPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; import core.game.component.Component; import core.game.interaction.NodeUsageEvent; diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/StuddedArmourPlugin.java b/Server/src/main/content/global/skill/crafting/StuddedArmourPlugin.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/crafting/StuddedArmourPlugin.java rename to Server/src/main/content/global/skill/crafting/StuddedArmourPlugin.java index b8f1e3526..1abe86feb 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/StuddedArmourPlugin.java +++ b/Server/src/main/content/global/skill/crafting/StuddedArmourPlugin.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; import core.plugin.Initializable; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; +import core.game.dialogue.SkillDialogueHandler; +import core.game.dialogue.SkillDialogueHandler.SkillDialogue; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; import core.game.interaction.NodeUsageEvent; @@ -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/java/core/game/node/entity/skill/crafting/TanningProduct.java b/Server/src/main/content/global/skill/crafting/TanningProduct.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/crafting/TanningProduct.java rename to Server/src/main/content/global/skill/crafting/TanningProduct.java index 7f611672d..30192b4e7 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/TanningProduct.java +++ b/Server/src/main/content/global/skill/crafting/TanningProduct.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; @@ -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/java/core/game/node/entity/skill/crafting/WeaveOptionPlugin.java b/Server/src/main/content/global/skill/crafting/WeaveOptionPlugin.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/crafting/WeaveOptionPlugin.java rename to Server/src/main/content/global/skill/crafting/WeaveOptionPlugin.java index 40594940b..4307d4599 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/WeaveOptionPlugin.java +++ b/Server/src/main/content/global/skill/crafting/WeaveOptionPlugin.java @@ -1,11 +1,11 @@ -package core.game.node.entity.skill.crafting; +package content.global.skill.crafting; import core.cache.def.impl.SceneryDefinition; import core.game.node.entity.player.link.diary.DiaryType; import core.game.world.map.Location; import core.plugin.Initializable; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; +import core.game.dialogue.SkillDialogueHandler; +import core.game.dialogue.SkillDialogueHandler.SkillDialogue; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/armour/DragonCraftPulse.java b/Server/src/main/content/global/skill/crafting/armour/DragonCraftPulse.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/crafting/armour/DragonCraftPulse.java rename to Server/src/main/content/global/skill/crafting/armour/DragonCraftPulse.java index 87ea0a5f6..924d1364a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/armour/DragonCraftPulse.java +++ b/Server/src/main/content/global/skill/crafting/armour/DragonCraftPulse.java @@ -1,9 +1,9 @@ -package core.game.node.entity.skill.crafting.armour; +package content.global.skill.crafting.armour; import core.cache.def.impl.ItemDefinition; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.crafting.armour.LeatherCrafting.DragonHide; +import content.global.skill.crafting.armour.LeatherCrafting.DragonHide; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; @@ -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/java/core/game/node/entity/skill/crafting/armour/HardCraftPulse.java b/Server/src/main/content/global/skill/crafting/armour/HardCraftPulse.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/crafting/armour/HardCraftPulse.java rename to Server/src/main/content/global/skill/crafting/armour/HardCraftPulse.java index 403007da0..5a2207b4a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/armour/HardCraftPulse.java +++ b/Server/src/main/content/global/skill/crafting/armour/HardCraftPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.armour; +package content.global.skill.crafting.armour; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; @@ -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/java/core/game/node/entity/skill/crafting/armour/LeatherCrafting.java b/Server/src/main/content/global/skill/crafting/armour/LeatherCrafting.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/skill/crafting/armour/LeatherCrafting.java rename to Server/src/main/content/global/skill/crafting/armour/LeatherCrafting.java index a6aa2f6e8..02f14d1ee 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/armour/LeatherCrafting.java +++ b/Server/src/main/content/global/skill/crafting/armour/LeatherCrafting.java @@ -1,8 +1,12 @@ -package core.game.node.entity.skill.crafting.armour; +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/java/core/game/node/entity/skill/crafting/armour/SnakeSkin.java b/Server/src/main/content/global/skill/crafting/armour/SnakeSkin.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/crafting/armour/SnakeSkin.java rename to Server/src/main/content/global/skill/crafting/armour/SnakeSkin.java index 6cdb26e7f..c4468d234 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/armour/SnakeSkin.java +++ b/Server/src/main/content/global/skill/crafting/armour/SnakeSkin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.armour; +package content.global.skill.crafting.armour; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/armour/SnakeSkinPulse.java b/Server/src/main/content/global/skill/crafting/armour/SnakeSkinPulse.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/crafting/armour/SnakeSkinPulse.java rename to Server/src/main/content/global/skill/crafting/armour/SnakeSkinPulse.java index 77ccc2d5e..be012d01c 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/armour/SnakeSkinPulse.java +++ b/Server/src/main/content/global/skill/crafting/armour/SnakeSkinPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.armour; +package content.global.skill.crafting.armour; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; @@ -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/java/core/game/node/entity/skill/crafting/armour/SoftCraftPulse.java b/Server/src/main/content/global/skill/crafting/armour/SoftCraftPulse.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/skill/crafting/armour/SoftCraftPulse.java rename to Server/src/main/content/global/skill/crafting/armour/SoftCraftPulse.java index ef26db15b..ab49815cf 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/armour/SoftCraftPulse.java +++ b/Server/src/main/content/global/skill/crafting/armour/SoftCraftPulse.java @@ -1,8 +1,7 @@ -package core.game.node.entity.skill.crafting.armour; +package content.global.skill.crafting.armour; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.crafting.armour.LeatherCrafting.SoftLeather; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.item.Item; @@ -23,7 +22,7 @@ public final class SoftCraftPulse extends SkillPulse { /** * Represents the leather to use. */ - private SoftLeather soft; + private LeatherCrafting.SoftLeather soft; /** * Represents the amount to make. @@ -42,7 +41,7 @@ public final class SoftCraftPulse extends SkillPulse { * @param leather the soft. * @param amount the amount. */ - public SoftCraftPulse(Player player, Item node, SoftLeather leather, int amount) { + public SoftCraftPulse(Player player, Item node, LeatherCrafting.SoftLeather leather, int amount) { super(player, node); this.soft = leather; this.amount = amount; @@ -82,7 +81,7 @@ public final class SoftCraftPulse extends SkillPulse { return false; } if (player.getInventory().remove(new Item(LeatherCrafting.LEATHER))) { - if (soft == SoftLeather.GLOVES || soft == SoftLeather.BOOTS || soft == SoftLeather.VAMBRACES) { + if (soft == LeatherCrafting.SoftLeather.GLOVES || soft == LeatherCrafting.SoftLeather.BOOTS || soft == LeatherCrafting.SoftLeather.VAMBRACES) { player.getPacketDispatch().sendMessage("You make a pair of " + soft.getProduct().getName().toLowerCase() + "."); } else { player.getPacketDispatch().sendMessage("You make " + (StringUtils.isPlusN(soft.getProduct().getName()) ? "an " : "a ") + soft.getProduct().getName().toLowerCase() + "."); @@ -91,11 +90,7 @@ 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 == SoftLeather.GLOVES) { + if (soft == LeatherCrafting.SoftLeather.GLOVES) { player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 3); } } diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/gem/GemCutPulse.java b/Server/src/main/content/global/skill/crafting/gem/GemCutPulse.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/skill/crafting/gem/GemCutPulse.java rename to Server/src/main/content/global/skill/crafting/gem/GemCutPulse.java index 206a09851..f094fa838 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/gem/GemCutPulse.java +++ b/Server/src/main/content/global/skill/crafting/gem/GemCutPulse.java @@ -1,9 +1,12 @@ -package core.game.node.entity.skill.crafting.gem; +package content.global.skill.crafting.gem; 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/java/core/game/node/entity/skill/crafting/gem/Gems.java b/Server/src/main/content/global/skill/crafting/gem/Gems.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/crafting/gem/Gems.java rename to Server/src/main/content/global/skill/crafting/gem/Gems.java index 005f5d578..0d0198159 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/gem/Gems.java +++ b/Server/src/main/content/global/skill/crafting/gem/Gems.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.gem; +package content.global.skill.crafting.gem; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; 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/java/core/game/node/entity/skill/crafting/jewellery/JewelleryCrafting.java b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryCrafting.java similarity index 99% rename from Server/src/main/java/core/game/node/entity/skill/crafting/jewellery/JewelleryCrafting.java rename to Server/src/main/content/global/skill/crafting/jewellery/JewelleryCrafting.java index 3a087d50f..37304e77d 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/jewellery/JewelleryCrafting.java +++ b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryCrafting.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.jewellery; +package content.global.skill.crafting.jewellery; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/jewellery/JewelleryPulse.java b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryPulse.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/skill/crafting/jewellery/JewelleryPulse.java rename to Server/src/main/content/global/skill/crafting/jewellery/JewelleryPulse.java index 12689fb8d..e69a7ee29 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/jewellery/JewelleryPulse.java +++ b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryPulse.java @@ -1,11 +1,13 @@ -package core.game.node.entity.skill.crafting.jewellery; +package content.global.skill.crafting.jewellery; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.crafting.jewellery.JewelleryCrafting.JewelleryItem; 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. @@ -21,7 +23,7 @@ public final class JewelleryPulse extends SkillPulse { /** * Represents the data of jewellery. */ - private JewelleryItem type; + private JewelleryCrafting.JewelleryItem type; /** * Represents the amount to make. @@ -38,7 +40,7 @@ public final class JewelleryPulse extends SkillPulse { * @param player the player. * @param node the node. */ - public JewelleryPulse(Player player, Item node, JewelleryItem data, int amount) { + public JewelleryPulse(Player player, Item node, JewelleryCrafting.JewelleryItem data, int amount) { super(player, node); this.type = data; this.amount = amount; @@ -56,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/kotlin/rs09/game/node/entity/skill/crafting/leather/SpikyVambraces.kt b/Server/src/main/content/global/skill/crafting/leather/SpikyVambraces.kt similarity index 79% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/leather/SpikyVambraces.kt rename to Server/src/main/content/global/skill/crafting/leather/SpikyVambraces.kt index e5c014494..764674412 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/leather/SpikyVambraces.kt +++ b/Server/src/main/content/global/skill/crafting/leather/SpikyVambraces.kt @@ -1,7 +1,7 @@ -package rs09.game.node.entity.skill.crafting.leather +package content.global.skill.crafting.leather -import api.Container -import api.ContentAPI +import core.api.Container +import core.api.* import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler import core.game.node.entity.player.Player @@ -53,16 +53,16 @@ class SpikyVambraces: UseWithHandler(Items.KEBBIT_CLAWS_10113) { private fun craftVamb(player: Player, vamb: Int, product: Int, vambLeather: String){ if (player.skills.getLevel(Skills.CRAFTING) >= 32){ - if (ContentAPI.removeItem(player,vamb,Container.INVENTORY) && - ContentAPI.removeItem(player,Items.KEBBIT_CLAWS_10113,Container.INVENTORY) + if (removeItem(player,vamb,Container.INVENTORY) && + removeItem(player,Items.KEBBIT_CLAWS_10113,Container.INVENTORY) ) { - ContentAPI.addItem(player,product) + addItem(player,product) player.skills.addExperience(Skills.CRAFTING,6.0) - ContentAPI.sendMessage(player, "You carefully attach the sharp claws to the $vambLeather vambraces.") + sendMessage(player, "You carefully attach the sharp claws to the $vambLeather vambraces.") } } else{ - ContentAPI.sendMessage(player,"You need a crafting level of 32 to craft this.") + sendMessage(player,"You need a crafting level of 32 to craft this.") } } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LanternCrafting.kt b/Server/src/main/content/global/skill/crafting/lightsources/LanternCrafting.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LanternCrafting.kt rename to Server/src/main/content/global/skill/crafting/lightsources/LanternCrafting.kt index 196292cbb..13daf97f6 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LanternCrafting.kt +++ b/Server/src/main/content/global/skill/crafting/lightsources/LanternCrafting.kt @@ -1,97 +1,97 @@ -package rs09.game.node.entity.skill.crafting.lightsources - -import core.game.interaction.NodeUsageEvent -import core.game.interaction.UseWithHandler -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 - -/** - * Handles the combining of items to craft lanterns - * @author Ceikry - */ -@Initializable -class LanternCrafting : UseWithHandler(36,38,4525,4542,1607){ - /** - * For candle lanterns -> Glassblowing produces 4527 - * 4527 + white candle = 4529 - * 4527 + black candle = 4532 - * - * For oil lanterns -> Glassblowing produces 4525 - * 4525 + 4540 (oil lantern frame) = 4535 (empty oil lantern) - * - * For Bullseye lanterns -> Smithing produces 4544 (bullseye lantern (unf)) - * 4544 + Lens(4542) -> 4546 - * 4544 + Sapphire(1607) -> 4700 (Sapphire lantern) - */ - override fun newInstance(arg: Any?): Plugin { - addHandler(4527, ITEM_TYPE, this) //Empty candle lantern - addHandler(4540, ITEM_TYPE,this) //oil lantern frame - addHandler(4544, ITEM_TYPE,this) //Bullseye lantern(unf) - return this - } - - override fun handle(event: NodeUsageEvent?): Boolean { - event ?: return false //if event is null don't execute - val used = event.used - return when(used.id){ - 4527 -> craftCandleLantern(event.player,event) - 4540 -> craftOilLantern(event.player,event) - 4544 -> craftBullseyeLantern(event.player,event) - else -> false - } - } - - private fun craftCandleLantern(player: Player, event: NodeUsageEvent): Boolean{ - return when(event.usedWith.id){ - 36,38 -> { - removeEventItems(player,event) - player.inventory.add( if(event.usedWith.id == 36) Item(4529) else Item(4532)) - player.sendMessage("You place the unlit candle inside the lantern.") - true - } - else -> false - } - } - - private fun craftOilLantern(player: Player, event: NodeUsageEvent): Boolean { - return when(event.usedWith.id){ - 4525 -> { - removeEventItems(player,event) - player.inventory.add(Item(4535)) - player.sendMessage("You place the oil lamp inside its metal frame.") - true - } - else -> false - } - } - - private fun craftBullseyeLantern(player: Player,event: NodeUsageEvent): Boolean{ - return when(event.usedWith.id){ - 4542 -> { - removeEventItems(player,event) - player.inventory.add( Item(4546) ) - player.sendMessage("You fashion the lens onto the lantern.") - true - } - 1607 -> { - if(player.skills.getLevel(Skills.CRAFTING) >= 20){ - removeEventItems(player,event) - player.inventory.add(Item(4700)) - player.sendMessage("You fashion the gem into a lens and fit it onto the lantern.") - } else { - player.sendMessage("You require a crafting level of 20 to use a gem as a lens.") - } - true - } - else -> false - } - } - - private fun removeEventItems(player: Player, event: NodeUsageEvent){ - player.inventory.remove(event.used.asItem()) - player.inventory.remove(event.usedWith.asItem()) - } +package content.global.skill.crafting.lightsources + +import core.game.interaction.NodeUsageEvent +import core.game.interaction.UseWithHandler +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 + +/** + * Handles the combining of items to craft lanterns + * @author Ceikry + */ +@Initializable +class LanternCrafting : UseWithHandler(36,38,4525,4542,1607){ + /** + * For candle lanterns -> Glassblowing produces 4527 + * 4527 + white candle = 4529 + * 4527 + black candle = 4532 + * + * For oil lanterns -> Glassblowing produces 4525 + * 4525 + 4540 (oil lantern frame) = 4535 (empty oil lantern) + * + * For Bullseye lanterns -> Smithing produces 4544 (bullseye lantern (unf)) + * 4544 + Lens(4542) -> 4546 + * 4544 + Sapphire(1607) -> 4700 (Sapphire lantern) + */ + override fun newInstance(arg: Any?): Plugin { + addHandler(4527, ITEM_TYPE, this) //Empty candle lantern + addHandler(4540, ITEM_TYPE,this) //oil lantern frame + addHandler(4544, ITEM_TYPE,this) //Bullseye lantern(unf) + return this + } + + override fun handle(event: NodeUsageEvent?): Boolean { + event ?: return false //if event is null don't execute + val used = event.used + return when(used.id){ + 4527 -> craftCandleLantern(event.player,event) + 4540 -> craftOilLantern(event.player,event) + 4544 -> craftBullseyeLantern(event.player,event) + else -> false + } + } + + private fun craftCandleLantern(player: Player, event: NodeUsageEvent): Boolean{ + return when(event.usedWith.id){ + 36,38 -> { + removeEventItems(player,event) + player.inventory.add( if(event.usedWith.id == 36) Item(4529) else Item(4532)) + player.sendMessage("You place the unlit candle inside the lantern.") + true + } + else -> false + } + } + + private fun craftOilLantern(player: Player, event: NodeUsageEvent): Boolean { + return when(event.usedWith.id){ + 4525 -> { + removeEventItems(player,event) + player.inventory.add(Item(4535)) + player.sendMessage("You place the oil lamp inside its metal frame.") + true + } + else -> false + } + } + + private fun craftBullseyeLantern(player: Player,event: NodeUsageEvent): Boolean{ + return when(event.usedWith.id){ + 4542 -> { + removeEventItems(player,event) + player.inventory.add( Item(4546) ) + player.sendMessage("You fashion the lens onto the lantern.") + true + } + 1607 -> { + if(player.skills.getLevel(Skills.CRAFTING) >= 20){ + removeEventItems(player,event) + player.inventory.add(Item(4700)) + player.sendMessage("You fashion the gem into a lens and fit it onto the lantern.") + } else { + player.sendMessage("You require a crafting level of 20 to use a gem as a lens.") + } + true + } + else -> false + } + } + + private fun removeEventItems(player: Player, event: NodeUsageEvent){ + player.inventory.remove(event.used.asItem()) + player.inventory.remove(event.usedWith.asItem()) + } } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt new file mode 100644 index 000000000..0df72d1f2 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt @@ -0,0 +1,53 @@ +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 + + +/** + * Extinguishes light sources + * @author Ceikry + */ +@Initializable +class LightSourceExtinguisher : OptionHandler(){ + override fun newInstance(arg: Any?): Plugin { + ItemDefinition.setOptionHandler("extinguish",this) + return this + } + + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + node ?: return false + player ?: return false + + val lightSource = LightSources.forId(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 + } + + fun Container.replace(item: Item, with: Item){ + if(remove(item)) { + add(with) + } + } +} diff --git a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt new file mode 100644 index 000000000..b558ec38f --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt @@ -0,0 +1,110 @@ +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 + +/** + * Method used to light various light sources + * @author Ceikry + */ +@Initializable +class LightSourceLighter : UseWithHandler(590,36,38){ + /** + * For Candles: + * 36 + tinderbox = 33 + * 38 + tinderbox = 32 + * + * For torches: + * 596 + tinderbox = 594 + * + * For Candle lanterns: + * 4529 + tinderbox = 4531 + * 4532 + tinderbox = 4534 + * Required Level: 4 Firemaking + * + * For oil lamps: + * 4522 + tinderbox = 4524 + * Required Level: 12 Firemaking + * + * For oil lanterns: + * 4537 + tinderbox = 4539 + * Required Level: 26 Firemaking + * + * For bullseye lanterns: + * 4548 + tinderbox = 4550 + * 4701 + tinderbox = 4702 + * 9064 + tinderbox = 9065 + * Required Level: 49 Firemaking + */ + + override fun newInstance(arg: Any?): Plugin { + addHandler(590, ITEM_TYPE, this) + addHandler(596, ITEM_TYPE, this) + addHandler(4529, ITEM_TYPE, this) + addHandler(4532, ITEM_TYPE, this) + addHandler(4522, ITEM_TYPE, this) + addHandler(4537, ITEM_TYPE, this) + addHandler(4548, ITEM_TYPE, this) + addHandler(4701, ITEM_TYPE, this) + addHandler(9064, ITEM_TYPE, this) + return this + } + + override fun handle(event: NodeUsageEvent?): Boolean { + event ?: return false + val inventory = event.player.inventory + val used = if(event.used.id == 590) event.usedWith.asItem() else event.used.asItem() //compensation for bad two-way use with handler shit + + val lightSource = LightSources.forId(used.id) + + 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.") + } + + return true + } + + fun Container.replace(item: Item, with: Item){ + if(remove(item)) { + add(with) + } + } + + fun light(player: Player, item: Item, lightSource: LightSources): Boolean{ + val requiredLevel = lightSource.levelRequired + val playerLevel = player.skills.getLevel(Skills.FIREMAKING) + + 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/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSources.kt b/Server/src/main/content/global/skill/crafting/lightsources/LightSources.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSources.kt rename to Server/src/main/content/global/skill/crafting/lightsources/LightSources.kt index 8849bc805..2c528dd7d 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSources.kt +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSources.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.crafting.lightsources +package content.global.skill.crafting.lightsources enum class LightSources(val emptyID: Int, val fullID: Int, val litID: Int, val levelRequired: Int, val openFlame: Boolean) { WHITE_CANDLE(0,36,33,0,true), @@ -7,9 +7,10 @@ enum class LightSources(val emptyID: Int, val fullID: Int, val litID: Int, val l WHITE_CANDLE_LANTERN(4527,4529,4531,4,true), BLACK_CANDLE_LANTERN(4527,4532,4534,4,true), OIL_LAMP(4525,4522,4524,12,true), - OIL_LANTERN(4535,4537,4539,29,false), + OIL_LANTERN(4535,4537,4539,26,false), BULLSEYE_LANTERN(4546,4548,4550,49,false), SAPPHIRE_LANTERN(0,4701,4702,49,false), + MINING_HELMET(0,5014,5013,65,false), EMERALD_LANTERN(0,9064,9065,49,false); companion object { @@ -24,6 +25,7 @@ enum class LightSources(val emptyID: Int, val fullID: Int, val litID: Int, val l 4537, 4539 -> OIL_LANTERN 4548, 4550 -> BULLSEYE_LANTERN 4701, 4702 -> SAPPHIRE_LANTERN + 5014, 5013 -> MINING_HELMET 9064, 9065 -> EMERALD_LANTERN else -> null } diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/limestone/ChiselLimestonePulse.java b/Server/src/main/content/global/skill/crafting/limestone/ChiselLimestonePulse.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/crafting/limestone/ChiselLimestonePulse.java rename to Server/src/main/content/global/skill/crafting/limestone/ChiselLimestonePulse.java index d711115da..259850dfa 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/limestone/ChiselLimestonePulse.java +++ b/Server/src/main/content/global/skill/crafting/limestone/ChiselLimestonePulse.java @@ -1,10 +1,13 @@ -package core.game.node.entity.skill.crafting.limestone; +package content.global.skill.crafting.limestone; 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 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/java/core/game/node/entity/skill/crafting/pottery/FirePotteryPulse.java b/Server/src/main/content/global/skill/crafting/pottery/FirePotteryPulse.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/crafting/pottery/FirePotteryPulse.java rename to Server/src/main/content/global/skill/crafting/pottery/FirePotteryPulse.java index 38c8f424f..e4cf4ec71 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/pottery/FirePotteryPulse.java +++ b/Server/src/main/content/global/skill/crafting/pottery/FirePotteryPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.pottery; +package content.global.skill.crafting.pottery; import core.game.world.map.Location; import core.game.node.entity.skill.SkillPulse; @@ -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/java/core/game/node/entity/skill/crafting/pottery/PotteryItem.java b/Server/src/main/content/global/skill/crafting/pottery/PotteryItem.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/crafting/pottery/PotteryItem.java rename to Server/src/main/content/global/skill/crafting/pottery/PotteryItem.java index 649bea561..6eafb8f96 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/pottery/PotteryItem.java +++ b/Server/src/main/content/global/skill/crafting/pottery/PotteryItem.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.pottery; +package content.global.skill.crafting.pottery; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/pottery/PotteryPulse.java b/Server/src/main/content/global/skill/crafting/pottery/PotteryPulse.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/crafting/pottery/PotteryPulse.java rename to Server/src/main/content/global/skill/crafting/pottery/PotteryPulse.java index f448caa33..c9cbe0ca3 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/pottery/PotteryPulse.java +++ b/Server/src/main/content/global/skill/crafting/pottery/PotteryPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.pottery; +package content.global.skill.crafting.pottery; import core.game.world.map.Location; import core.game.node.entity.skill.SkillPulse; diff --git a/Server/src/main/content/global/skill/crafting/silver/SilverCraftingListener.kt b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingListener.kt new file mode 100644 index 000000000..6ab0e7a45 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingListener.kt @@ -0,0 +1,111 @@ +package content.global.skill.crafting.silver + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.interaction.InterfaceListener + +import core.game.node.scenery.Scenery as CoreScenery + +/** + * Handles silver crafting interface interactions + * as well as silver -> furnace use-with interaction. + * + * @author vddCore + */ +class SilverCraftingListener : InterfaceListener, InteractionListener { + 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 val FURNACES = intArrayOf( + Scenery.FURNACE_2966, Scenery.FURNACE_3044, Scenery.FURNACE_3294, Scenery.FURNACE_4304, + Scenery.FURNACE_6189, Scenery.FURNACE_11009, Scenery.FURNACE_11010, Scenery.FURNACE_11666, + 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.CLAY_FORGE_21303 + ) + + private const val ATTRIBUTE_FURNACE_ID = "crafting:silver:furnace_id" + } + + override fun defineInterfaceListeners() { + onOpen(Components.CRAFTING_SILVER_CASTING_438) { player, _ -> + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 17, Items.HOLY_SYMBOL_1718) + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 24, Items.UNHOLY_SYMBOL_1724) + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 31, Items.SILVER_SICKLE_2961) + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 38, Items.CONDUCTOR_4201) + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 45, Items.TIARA_5525) + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 53, Items.SILVTHRILL_ROD_7637) + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 60, Items.DEMONIC_SIGIL_6748) + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 67, Items.SILVER_BOLTS_UNF_9382) + sendItemOnInterface(player, Components.CRAFTING_SILVER_CASTING_438, 74, Items.SILVTHRIL_CHAIN_13154) + + return@onOpen true + } + + on(Components.CRAFTING_SILVER_CASTING_438) { player, _, opcode, buttonID, _, _ -> + val product = SilverProduct.forButtonID(buttonID) ?: return@on true + + if (!inInventory(player, product.requiredItemId)) { + sendMessage( + player, + "You need a ${itemDefinition(product.requiredItemId).name.lowercase()} to make this item." + ) + return@on true + } + + if (product == SilverProduct.SILVTHRILL_ROD || product == SilverProduct.SILVTHRIL_CHAIN) { + sendMessage(player, "You can't do that yet.") + 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, Items.SILVER_BAR_2355)) + 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 + } + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.SILVER_BAR_2355, *FURNACES) { player, _, with -> + setAttribute(player, ATTRIBUTE_FURNACE_ID, with) + openInterface(player, Components.CRAFTING_SILVER_CASTING_438) + return@onUseWith true + } + } + + private fun make(player: Player, product: SilverProduct, amount: Int) { + closeInterface(player) + + submitIndividualPulse( + player, + SilverCraftingPulse( + player, + product, + getAttribute(player, ATTRIBUTE_FURNACE_ID, CoreScenery(-1, -1, 0)), + amount + ) + ) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt new file mode 100644 index 000000000..338b41b47 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt @@ -0,0 +1,51 @@ +package content.global.skill.crafting.silver + +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.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. + * + * @author vddCore + */ +class SilverCraftingPulse( + private val player: Player, + private val product: SilverProduct, + private val furnace: Scenery, + private var amount: Int +) : Pulse() { + override fun pulse(): Boolean { + if (amount < 1) return true + + if (!inInventory(player, product.requiredItemId) || !inInventory(player, Items.SILVER_BAR_2355)) { + return true + } + + 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)) { + rewardXP(player, Skills.CRAFTING, product.xpReward) + player.dispatch( + ResourceProducedEvent( + product.producedItemId, + product.amountProduced, + furnace, + Items.SILVER_BAR_2355 + ) + ) + } else return true + + amount-- + delay = 5 + + return false + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt b/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt new file mode 100644 index 000000000..98bcc014e --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/silver/SilverProduct.kt @@ -0,0 +1,55 @@ +package content.global.skill.crafting.silver + +import org.rs09.consts.Items + +private const val BUTTON_UNBLESSED = 16 +private const val BUTTON_UNHOLY = 23 +private const val BUTTON_SICKLE = 30 +private const val BUTTON_TIARA = 44 +private const val BUTTON_DEMONIC_SIGIL = 59 +private const val BUTTON_SILVTHRIL_CHAIN = 73 +private const val BUTTON_LIGHTNING_ROD = 37 +private const val BUTTON_SILVTHRILL_ROD = 52 +private const val BUTTON_CROSSBOW_BOLTS = 66 + +/** + * Provides silver crafting product definitions. + * + * @author vddCore + */ +enum class SilverProduct( + val buttonId: Int, + val requiredItemId: Int, + val producedItemId: Int, + val amountProduced: Int, + val minimumLevel: Int, + val xpReward: Double, + 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.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), + LIGHTNING_ROD(BUTTON_LIGHTNING_ROD, Items.CONDUCTOR_MOULD_4200, Items.CONDUCTOR_4201, 1, 20, 50.0, -1), + SILVTHRILL_ROD(BUTTON_SILVTHRILL_ROD, Items.ROD_CLAY_MOULD_7649, Items.SILVTHRILL_ROD_7637, 1, 25, 55.0, -1), + CROSSBOW_BOLTS(BUTTON_CROSSBOW_BOLTS, Items.BOLT_MOULD_9434, Items.SILVER_BOLTS_UNF_9382, 10, 21, 50.0, -1); + + companion object { + private val BUTTON_MAP = HashMap() + private val PRODUCT_MAP = HashMap() + + init { + for (product in SilverProduct.values()) { + BUTTON_MAP[product.buttonId] = product + } + + for (product in SilverProduct.values()) { + PRODUCT_MAP[product.producedItemId] = product + } + } + + fun forButtonID(buttonId: Int): SilverProduct? = BUTTON_MAP[buttonId] + fun forProductID(productId: Int): SilverProduct? = PRODUCT_MAP[productId] + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/silver/SilverStringingListener.kt b/Server/src/main/content/global/skill/crafting/silver/SilverStringingListener.kt new file mode 100644 index 000000000..591dc42c2 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/silver/SilverStringingListener.kt @@ -0,0 +1,37 @@ +package content.global.skill.crafting.silver + +import core.api.addItem +import core.api.removeItem +import core.game.node.Node +import core.game.node.entity.player.Player +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Handles ball of wool <-> stringable silver items interaction. + * + * @author vddCore + */ +class SilverStringingListener : InteractionListener { + companion object { + private val STRINGABLE_PRODUCTS = intArrayOf( + Items.UNSTRUNG_SYMBOL_1714, + Items.UNSTRUNG_EMBLEM_1720 + ) + } + + private fun stringSilverProduct(player: Player, used: Node, with: Node): Boolean { + SilverProduct.forProductID(with.id)?.let { + if (removeItem(player, with.id) && removeItem(player, used.id)) { + addItem(player, it.strungId) + } + } + + return true + } + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.BALL_OF_WOOL_1759, *STRINGABLE_PRODUCTS, handler = ::stringSilverProduct) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/spinning/SpinningItem.java b/Server/src/main/content/global/skill/crafting/spinning/SpinningItem.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/crafting/spinning/SpinningItem.java rename to Server/src/main/content/global/skill/crafting/spinning/SpinningItem.java index c1af625ad..27e561db8 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/spinning/SpinningItem.java +++ b/Server/src/main/content/global/skill/crafting/spinning/SpinningItem.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.spinning; +package content.global.skill.crafting.spinning; /** * Represents an item to be spinned. diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/spinning/SpinningPulse.java b/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/crafting/spinning/SpinningPulse.java rename to Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java index e1265f537..ae652f9a8 100644 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/spinning/SpinningPulse.java +++ b/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.crafting.spinning; +package content.global.skill.crafting.spinning; import core.cache.def.impl.ItemDefinition; import core.game.container.impl.EquipmentContainer; @@ -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/kotlin/rs09/game/node/entity/skill/farming/BasketsAndSacks.kt b/Server/src/main/content/global/skill/farming/BasketsAndSacks.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/farming/BasketsAndSacks.kt rename to Server/src/main/content/global/skill/farming/BasketsAndSacks.kt index 8ad542e66..fea2f83d8 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/BasketsAndSacks.kt +++ b/Server/src/main/content/global/skill/farming/BasketsAndSacks.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.farming +package content.global.skill.farming import org.rs09.consts.Items @@ -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 new file mode 100644 index 000000000..c7e7f5cc2 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/CompostBin.kt @@ -0,0 +1,202 @@ +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.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) { + private var items = ArrayList() + var isSuperCompost = true + var isTomatoes = true + var isClosed = false + 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 + 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 + // TODO: Add animation + playAudio(player, Sounds.COMPOST_OPEN_2429) + sendMessage(player, "You open the compost bin.") + updateBit() + } + + fun takeItem(): Item?{ + if(items.isEmpty()) return null + val item = items[0] + items.remove(item) + if(items.isEmpty()){ + isFinished = false + finishedTime = 0L + isTomatoes = true + isSuperCompost = true + isClosed = false + } + 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 + } + + fun checkSuperCompostItem(id: Int): Boolean{ + return when (id){ + Items.WATERMELON_5982, + Items.PINEAPPLE_2114, + Items.CALQUAT_FRUIT_5980, + Items.OAK_ROOTS_6043, + Items.WILLOW_ROOTS_6045, + Items.MAPLE_ROOTS_6047, + Items.YEW_ROOTS_6049, + Items.MAGIC_ROOTS_6051, + Items.COCONUT_5974, + Items.COCONUT_SHELL_5978, + Items.PAPAYA_FRUIT_5972, + Items.JANGERBERRIES_247, + Items.WHITE_BERRIES_239, + Items.POISON_IVY_BERRIES_6018, + Items.CLEAN_TOADFLAX_2998, + Items.CLEAN_AVANTOE_261, + Items.CLEAN_KWUARM_263, + Items.CLEAN_CADANTINE_265, + Items.CLEAN_DWARF_WEED_267, + Items.CLEAN_TORSTOL_269, + Items.CLEAN_LANTADYME_2481, + Items.CLEAN_SNAPDRAGON_3000, + Items.GRIMY_TOADFLAX_3049, + Items.GRIMY_KWUARM_213, + Items.GRIMY_AVANTOE_211, + Items.GRIMY_TORSTOL_219, + Items.GRIMY_DWARF_WEED_217, + Items.GRIMY_LANTADYME_2485, + Items.GRIMY_SNAPDRAGON_3051, + Items.GRIMY_CADANTINE_215 -> true + else -> false + } + } + + fun addItem(item: Int){ + if(!isFull()) { + items.add(item) + if (!checkSuperCompostItem(item)) { + isSuperCompost = false + } + if(item != Items.TOMATO_1982) isTomatoes = false + } + updateBit() + } + + fun addItem(item: Item){ + val remaining = 15 - items.size + val amount = if(item.amount > remaining){ + remaining + } else item.amount + for(i in 0 until amount) { + + playAudio(player, Sounds.FARMING_PUTIN_2441) + addItem(item.id) + } + } + + fun updateBit(){ + if(items.isNotEmpty()) { + 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 { + var finalValue = items.size + if (isTomatoes) finalValue += 0x80 + setVarbit(player, bin.varbit, finalValue) + } + } else setVarbit(player, bin.varbit, 0) + } + + fun save(root: JSONObject){ + val binObject = JSONObject() + binObject.put("isSuper",this.isSuperCompost) + val items = JSONArray() + for(id in this.items){ + items.add(id) + } + binObject.put("items",items) + binObject.put("finishTime",finishedTime) + binObject.put("isTomato",isTomatoes) + binObject.put("isClosed",isClosed) + binObject.put("isFinished",isFinished) + root.put("binData",binObject) + } + + fun parse(_data: JSONObject){ + val isSuper = if(_data.containsKey("isSuper")) (_data["isSuper"] as Boolean) else true + if(_data.containsKey("items")){ + (_data["items"] as JSONArray).forEach { + addItem(it.toString().toInt()) + } + } + if(_data.containsKey("finishTime")) finishedTime = _data["finishTime"].toString().toLong() + if(_data.containsKey("isTomato")) isTomatoes = _data["isTomato"] as Boolean + if(_data.containsKey("isClosed")) isClosed = _data["isClosed"] as Boolean + if(_data.containsKey("isFinished")) isFinished = _data["isFinished"] as Boolean + updateBit() + } + + fun finish(){ + if(isTomatoes) items = items.map { Items.ROTTEN_TOMATO_2518 } as ArrayList + else if(isSuperCompost) items = items.map { Items.SUPERCOMPOST_6034 } as ArrayList + else items = items.map { Items.COMPOST_6032 } as ArrayList + isFinished = true + } + + fun convert(){ + if(!isSuperCompost){ + items = items.map { Items.SUPERCOMPOST_6034 } as ArrayList + isSuperCompost = true + updateBit() + } + } +} diff --git a/Server/src/main/content/global/skill/farming/CompostBinOptionHandler.kt b/Server/src/main/content/global/skill/farming/CompostBinOptionHandler.kt new file mode 100644 index 000000000..a8a0b182f --- /dev/null +++ b/Server/src/main/content/global/skill/farming/CompostBinOptionHandler.kt @@ -0,0 +1,50 @@ +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 + +@Initializable +class CompostBinOptionHandler : OptionHandler() { + override fun newInstance(arg: Any?): Plugin { + for (i in 7836..7839) + SceneryDefinition.forId(i).childrenIds.forEach { + val def = SceneryDefinition.forId(it) + def.handlers["option:open"] = this + def.handlers["option:close"] = this + def.handlers["option:take-tomato"] = this + } + return this + } + + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + player ?: return false + node ?: return false + val cBin = CompostBins.forObject(node.asScenery()) ?: return false + val bin = cBin.getBinForPlayer(player) + + 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) { + sendMessage(player, "This shouldn't be happening. Report this.") + } else { + if (player.inventory.isFull) { + sendMessage(player, "You don't have enough inventory space to do this.") + } else { + val reward = bin.takeItem() + if (reward != null) + player.inventory.add(reward) + } + } + } + } + return true + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/CompostBins.kt b/Server/src/main/content/global/skill/farming/CompostBins.kt new file mode 100644 index 000000000..3780625f4 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/CompostBins.kt @@ -0,0 +1,35 @@ +package content.global.skill.farming + +import content.global.skill.farming.timers.Compost +import core.api.getOrStartTimer +import core.cache.def.impl.SceneryDefinition +import core.game.node.entity.player.Player +import core.game.node.scenery.Scenery + +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.varbit to it }.toMap() + + @JvmStatic + fun forObject(obj: Scenery): CompostBins?{ + return forObjectID(obj.id) + } + + @JvmStatic + fun forObjectID(id: Int): CompostBins?{ + val objDef = SceneryDefinition.forId(id) + return bins[objDef.varbitID] + } + } + + fun getBinForPlayer(player: Player) : CompostBin { + val bins = getOrStartTimer (player) + return bins.getBin (this) + } +} diff --git a/Server/src/main/content/global/skill/farming/CompostType.kt b/Server/src/main/content/global/skill/farming/CompostType.kt new file mode 100644 index 000000000..01e34dbad --- /dev/null +++ b/Server/src/main/content/global/skill/farming/CompostType.kt @@ -0,0 +1,7 @@ +package content.global.skill.farming + +enum class CompostType { + NONE, + 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 new file mode 100644 index 000000000..e47820bb3 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/CropHarvester.kt @@ -0,0 +1,147 @@ +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 content.global.skill.summoning.familiar.GiantEntNPC +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 core.plugin.Initializable +import core.plugin.Plugin +import org.rs09.consts.Items +import org.rs09.consts.Sounds + +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) + return this + } + + companion object { + @JvmStatic + fun harvestPulse(player: Player?, node: Node?, crop: Int): Pulse? { + player ?: return null + node ?: return null + 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) + + if (!hasSpaceFor(player, reward)) { + sendMessage(player, "You have run out of inventory space.") + return true + } + + val familiar = player.familiarManager.familiar + if (familiar != null && familiar is GiantEntNPC) { + familiar.modifyFarmingReward(fPatch, reward) + } + + var requiredItem = when (fPatch.type) { + PatchType.TREE_PATCH -> Items.SECATEURS_5329 + else -> Items.SPADE_952 + } + if (requiredItem == Items.SECATEURS_5329) { + if (inInventory(player, Items.MAGIC_SECATEURS_7409)) { + requiredItem = Items.MAGIC_SECATEURS_7409 + } + } + 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 -> Sounds.DIGSPADE_1470 + Items.SECATEURS_5329 -> Sounds.FARMING_PICK_2437 + Items.MAGIC_SECATEURS_7409 -> Sounds.FARMING_PICK_2437 + else -> 0 + } + if (!inInventory(player, requiredItem)) { + sendMessage(player, "You lack the needed tool to harvest these crops.") + return true + } + 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 + addItemOrDrop(player, reward.id,reward.amount) + rewardXP(player, Skills.FARMING, plantable.harvestXP) + if (patch.patch.type in livesBased) { + patch.rollLivesDecrement( + getDynLevel(player, Skills.FARMING), + inInventory(player, Items.MAGIC_SECATEURS_7409) //add ||inEquipment() check when Fairy Tale pt 1 has been implemented + ) + } else { + patch.harvestAmt-- + if (patch.harvestAmt <= 0 && crop == plantable.harvestItem) { + patch.clear() + } + else if (fPatch.type == PatchType.MUSHROOM_PATCH){ + patch.setCurrentState(patch.getCurrentState() + 1) + } + } + if (sendHarvestMessages && (patch.cropLives <= 0 || patch.harvestAmt <= 0)) { + sendMessage(player, "The $patchName is now empty.") + } + return patch.cropLives <= 0 || patch.harvestAmt <= 0 + } + } + } + } + + 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 (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 + 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 new file mode 100644 index 000000000..ab170aa74 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt @@ -0,0 +1,94 @@ +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.plugin.Initializable +import org.rs09.consts.Sounds + +@Initializable +class DigUpPatchDialogue(player: Player? = null) : DialoguePlugin(player) { + var patch: Patch? = null + + 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 || patch?.isEmptyAndWeeded() == true) { + sendDialogue(player, "There aren't any crops in this patch to dig up.") + stage = 1000 + return true + } + 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) { + 1 -> { + end() + 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) + } + else -> return@queueScript stopExecuting(player) + } + } + } + else -> end() + } + + 1000 -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(67984003) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt new file mode 100644 index 000000000..e048914a1 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt @@ -0,0 +1,152 @@ +package content.global.skill.farming + +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, val quickPay: Boolean = false): DialogueFile() { + var item: Item? = null + var itemSecondary: Item? = null + var itemTertiary: Item? = null + + 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 = 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 { + val amount = if (item?.amount == 1) "one" else item?.amount + npc("If you like, but I want $amount $protectionText for that.") + stage++ + } + } + } + + 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 { + 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 -> { + 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 + } + } + + } + } +} diff --git a/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt b/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt new file mode 100644 index 000000000..52d7874ad --- /dev/null +++ b/Server/src/main/content/global/skill/farming/FarmerPayOptionHandler.kt @@ -0,0 +1,28 @@ +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 +import core.game.interaction.IntType + +class FarmerPayOptionHandler : InteractionListener { + + override fun defineListeners() { + 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 -> + return@on attemptPay(player,node,1) + } + } + + fun attemptPay(player: Player, node: Node, index: Int): Boolean { + val farmer = Farmers.forId(node.id) ?: return false + val patch = farmer.patches[index].getPatchFor(player) + + openDialogue(player, FarmerPayOptionDialogue(patch, true), node.asNpc()) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Farmers.kt b/Server/src/main/content/global/skill/farming/Farmers.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Farmers.kt rename to Server/src/main/content/global/skill/farming/Farmers.kt index fc154d0e9..360e583c5 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Farmers.kt +++ b/Server/src/main/content/global/skill/farming/Farmers.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.farming +package content.global.skill.farming enum class Farmers(val id: Int, val patches: Array) { LYRA(2326, arrayOf(FarmingPatch.PORT_PHAS_ALLOTMENT_NW,FarmingPatch.PORT_PHAS_ALLOTMENT_SE)), @@ -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 new file mode 100644 index 000000000..d19e8871e --- /dev/null +++ b/Server/src/main/content/global/skill/farming/FarmingPatch.kt @@ -0,0 +1,129 @@ +package content.global.skill.farming + +import core.api.* +import core.cache.def.impl.SceneryDefinition +import core.game.node.scenery.Scenery +import core.game.node.entity.player.Player +import content.global.skill.farming.timers.CropGrowth + +enum class FarmingPatch(val varbit: Int, val type: PatchType) { + //Allotments + 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(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(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(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(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(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(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(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(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.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?{ + return forObjectID(obj.id) + } + + @JvmStatic + fun forObjectID(id: Int): FarmingPatch?{ + val objDef = SceneryDefinition.forId(id) + return patches[objDef.varbitID] + } + + fun getSceneryDefByVarbit (id: Int) : SceneryDefinition? { + return nodeMap[id] + } + } + + 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 new file mode 100644 index 000000000..09a190c29 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/FarmingPatchZone.kt @@ -0,0 +1,78 @@ +package content.global.skill.farming + +import core.api.* +import core.game.dialogue.FacialExpression +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.zone.ZoneBorders +import org.rs09.consts.NPCs +import core.game.dialogue.DialogueFile + +class FarmingPatchZone : MapArea, TickListener { + private val playersInZone = hashMapOf() + + override fun defineAreaBorders(): Array { + return arrayOf( + getRegionBorders(12083), + getRegionBorders(10548), + ZoneBorders(3594,3521,3608,3532) + ) + } + + override fun areaEnter(entity: Entity) { + if(entity is Player && playersInZone[entity] == null && getStatLevel(entity, Skills.FARMING) <= 15) { + playersInZone[entity] = 0 + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if(entity is Player) + playersInZone.remove(entity) + } + + override fun tick() { + playersInZone.toList().forEach { (player, ticks) -> + if(!player.isArtificial) { + if (ticks == 500) { + spawnGithan(player, true) + } else if (ticks == 1000) { + spawnGithan(player, false) + } + playersInZone[player] = ticks + 1 + } + } + } + + /** + * 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(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() + } + } + + override fun end(){ + super.end() + poofClear(npc ?: return) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt b/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt new file mode 100644 index 000000000..380334445 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt @@ -0,0 +1,109 @@ +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 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 +class FruitAndBerryPicker : OptionHandler() { + override fun newInstance(arg: Any?): Plugin { + SceneryDefinition.setOptionHandler("pick-coconut",this) + SceneryDefinition.setOptionHandler("pick-banana",this) + SceneryDefinition.setOptionHandler("pick-apple",this) + SceneryDefinition.setOptionHandler("pick-orange",this) + SceneryDefinition.setOptionHandler("pick-pineapple",this) + SceneryDefinition.setOptionHandler("pick-papaya",this) + SceneryDefinition.setOptionHandler("pick-leaf",this) + SceneryDefinition.setOptionHandler("pick-from",this) + SceneryDefinition.setOptionHandler("pick-fruit",this) + SceneryDefinition.setOptionHandler("pick-spine",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 + + val animation = Animation(2281) + + if (patch.getFruitOrBerryCount() <= 0) { + sendMessage(player, "This shouldn't be happening. Please report this.") + return true + } + + 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)) { + patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(45) + } + + 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) { + familiar.modifyFarmingReward(fPatch, reward) + } + + 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 + } + }) + + return true + } + +} 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 new file mode 100644 index 000000000..1813a5145 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/HealthChecker.kt @@ -0,0 +1,79 @@ +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.plugin.Initializable +import core.plugin.Plugin +import core.tools.Log +import java.util.concurrent.TimeUnit + +@Initializable +class HealthChecker : OptionHandler() { + override fun newInstance(arg: Any?): Plugin { + SceneryDefinition.setOptionHandler("check-health", 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 type = patch.patch.type + + if (!patch.isCheckable()) { + sendMessage(player, "This shouldn't be happening. Please report this.") + return true + } + + if (!patch.isCheckHealth) return true + + rewardXP(player, Skills.FARMING, patch.plantable?.checkHealthXP ?: 0.0) + patch.isCheckHealth = false + 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_PATCH) { + patch.nextGrowth = TimeUnit.MINUTES.toMillis(45) + } + + patch.update() + + return true + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/InspectionHandler.kt b/Server/src/main/content/global/skill/farming/InspectionHandler.kt new file mode 100644 index 000000000..00a30a5d2 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/InspectionHandler.kt @@ -0,0 +1,55 @@ +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) + return this + } + + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + node ?: return false + player ?: return false + val patch = FarmingPatch.forObject(node.asScenery()) + 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 + } + +} diff --git a/Server/src/main/content/global/skill/farming/LeprechaunNoter.kt b/Server/src/main/content/global/skill/farming/LeprechaunNoter.kt new file mode 100644 index 000000000..8cf6eff81 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/LeprechaunNoter.kt @@ -0,0 +1,40 @@ +package content.global.skill.farming + +import core.api.* +import core.game.node.item.Item +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class LeprechaunNoter : InteractionListener { + + val CROPS = Plantable.values().map{ it.harvestItem }.toIntArray() + val LEPRECHAUNS = intArrayOf(NPCs.TOOL_LEPRECHAUN_3021,NPCs.GOTH_LEPRECHAUN_8000,NPCs.TOOL_LEPRECHAUN_4965,NPCs.TECLYN_2861) + + override fun defineListeners() { + onUseAnyWith(IntType.NPC,*LEPRECHAUNS) { player, used, with -> + val usedItem = used.asItem() + val npc = with.asNpc() + val expr = when(npc.id){ + NPCs.TOOL_LEPRECHAUN_3021 -> core.game.dialogue.FacialExpression.OLD_NORMAL + else -> core.game.dialogue.FacialExpression.FRIENDLY + } + 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.") + } + } + 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 new file mode 100644 index 000000000..b2235419e --- /dev/null +++ b/Server/src/main/content/global/skill/farming/Patch.kt @@ -0,0 +1,482 @@ +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 java.util.concurrent.TimeUnit +import kotlin.math.ceil +import kotlin.math.min + +class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantable?, var currentGrowthStage: Int, var isDiseased: Boolean, var isDead: Boolean, var isWatered: Boolean, var nextGrowth: Long, var harvestAmt: Int, var isCheckHealth: Boolean) { + constructor(player: Player, patch: FarmingPatch) : this(player,patch,null,0,false,false,false,0L,0,false) + + var diseaseMod = 0 + 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.COMPOST -> 1 + CompostType.SUPERCOMPOST -> 2 + } + harvestAmt = when (plantable) { + Plantable.LIMPWURT_SEED, Plantable.WOAD_SEED -> 3 + Plantable.MUSHROOM_SPORE -> 6 + Plantable.WILLOW_SAPLING -> 0 + else -> 1 + } + 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_PATCH){ + //authentic formula thanks to released data. + var herbSaveLow = when(plantable){ + Plantable.GUAM_SEED -> min(24 + farmingLevel, 80) + Plantable.MARRENTILL_SEED -> min(28 + farmingLevel, 80) + Plantable.TARROMIN_SEED -> min(31 + farmingLevel, 80) + Plantable.HARRALANDER_SEED -> min(36 + farmingLevel, 80) + Plantable.GOUT_TUBER -> min(39 + farmingLevel, 80) + Plantable.RANARR_SEED -> min(39 + farmingLevel, 80) + Plantable.SPIRIT_WEED_SEED -> min(43 + farmingLevel, 80) + Plantable.TOADFLAX_SEED -> min(43 + farmingLevel, 80) + Plantable.IRIT_SEED -> min(46 + farmingLevel, 80) + Plantable.AVANTOE_SEED -> min(50 + farmingLevel, 80) + Plantable.KWUARM_SEED -> min(54 + farmingLevel, 80) + Plantable.SNAPDRAGON_SEED -> min(57 + farmingLevel, 80) + Plantable.CADANTINE_SEED -> min(60 + farmingLevel, 80) + Plantable.LANTADYME_SEED -> min(64 + farmingLevel, 80) + Plantable.DWARF_WEED_SEED -> min(67 + farmingLevel, 80) + Plantable.TORSTOL_SEED -> min(71 + farmingLevel, 80) + else -> -1 + } + + if(magicSecateurs) herbSaveLow = ceil(1.10 * herbSaveLow).toInt() + + val rand = RandomFunction.random(256) + + if(rand > herbSaveLow){ + cropLives -= 1 + } + } else { + //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_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 + } + + if(magicSecateurs) chance += ceil(1.10 * chance).toInt() //will increase average yield by roughly 3. + + if(RandomFunction.roll(chance)) cropLives -= 1 + } + + if(cropLives <= 0) clear() + } + + fun isWeedy(): Boolean { + 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 getVarbit(player, patch.varbit) + } + + fun setCurrentState(state: Int){ + 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) + } + + fun updateBit(){ + if(isCheckHealth){ + when(patch.type){ + 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_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_PATCH -> { + if(isDead) setVisualState(getBushDeathValue()) + else if(isDiseased && !isDead) setVisualState(getBushDiseaseValue()) + } + 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.SPIRIT_TREE_PATCH -> { + if(isDead) setVisualState(getSpiritTreeDeathValue()) + else if(isDiseased && !isDead) setVisualState(getSpiritTreeDiseaseValue()) + } + PatchType.FRUIT_TREE_PATCH -> { + if(isDead) setVisualState(getFruitTreeDeathValue()) + else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue()) + } + PatchType.BELLADONNA_PATCH -> { + if(isDead) setVisualState(getBelladonnaDeathValue()) + else if(isDiseased && !isDead) setVisualState(getBelladonnaDiseaseValue()) + else setVisualState((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) + } + } + } + } + + fun cureDisease() { + setVarbit(player, patch.varbit, (plantable?.value ?: 0) + currentGrowthStage) + isDiseased = false + updateBit() + } + + fun water() { + isWatered = true + 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 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 + 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 { + return (plantable?.value ?: 0) + currentGrowthStage + 12 + } + + private fun getFruitTreeDeathValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 18 + } + + private fun getBelladonnaDiseaseValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 4 + } + + private fun getBelladonnaDeathValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 7 + } + + private fun getCactusDiseaseValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + 10 + } + + private fun getCactusDeathValue(): Int { + 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 + } else if (plantable == Plantable.SPIRIT_WEED_SEED) { + 211 + currentGrowthStage - 1 + } else { + 198 + currentGrowthStage -1 + } + } + + private fun getHerbDeathValue(): Int { + return if(plantable == Plantable.GOUT_TUBER){ + 201 + currentGrowthStage - 1 + } else 170 + currentGrowthStage - 1 + } + + private fun grow(){ + if((isWeedy() || isEmptyAndWeeded() || (plantable == Plantable.SCARECROW && !isGrown())) && getCurrentState() > 0) { + nextGrowth = System.currentTimeMillis() + 60000 + setCurrentState(getCurrentState() - 1) + currentGrowthStage-- + return + } + + if(isDiseased){ + isDead = true + return + } + + // This is so a cheat can force disease + diseaseMod = if (diseaseMod < 0) -128 else when(compost){ + CompostType.NONE -> 0 + CompostType.COMPOST -> 8 + CompostType.SUPERCOMPOST -> 13 + } + + if(patch != FarmingPatch.TROLL_STRONGHOLD_HERB + && RandomFunction.random(128) <= (17 - diseaseMod) + && !isWatered && !isGrown() + && !protectionPaid + && !isFlowerProtected() + && patch.type != PatchType.EVIL_TURNIP_PATCH + && 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(isCheckable() && plantable != null && plantable?.stages == currentGrowthStage + 1){ + isCheckHealth = true + } + + 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_PATCH) { + // Willow branches + if(harvestAmt < 6) { + harvestAmt++ + } + } + + if((plantable?.stages ?: 0) > currentGrowthStage && !isGrown()){ + currentGrowthStage++ + setCurrentState(getCurrentState() + 1) + isWatered = false + } + } + + fun regrowIfTreeStump() { + 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) + isWatered = false + } + } + } + + fun update(){ + grow() + updateBit() + } + + fun plant(plantable: Plantable){ + nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(plantable.applicablePatch.stageGrowthTime.toLong()) + this.plantable = plantable + isDead = false + isDiseased = false + currentGrowthStage = 0 + setCurrentState(plantable.value) + } + + fun clear(){ + isCheckHealth = false + isDiseased = false + isDead = false + plantable = null + setVarbit(player, patch.varbit, 0) + nextGrowth = 0L + currentGrowthStage = 3 + setCurrentState(3) + compost = CompostType.NONE + protectionPaid = false + } + + fun getFruitOrBerryCount() : Int { + plantable ?: return 0 + return getCurrentState() - plantable!!.value - plantable!!.stages + } + + fun getStageGrowthMinutes() : Int { + var minutes = patch.type.stageGrowthTime + 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 + } + + fun isFlowerProtected(): Boolean{ + if(patch.type != PatchType.ALLOTMENT) return false + + val fpatch = when(patch){ + FarmingPatch.S_FALADOR_ALLOTMENT_SE,FarmingPatch.S_FALADOR_ALLOTMENT_NW -> FarmingPatch.S_FALADOR_FLOWER_C + FarmingPatch.ARDOUGNE_ALLOTMENT_S,FarmingPatch.ARDOUGNE_ALLOTMENT_N -> FarmingPatch.ARDOUGNE_FLOWER_C + 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, false) + + 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 new file mode 100644 index 000000000..21967671b --- /dev/null +++ b/Server/src/main/content/global/skill/farming/PatchRaker.kt @@ -0,0 +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.system.task.Pulse +import org.rs09.consts.Items +import org.rs09.consts.Sounds + +object PatchRaker { + val RAKE_ANIM = getAnimation(2273) + + @JvmStatic + 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 (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) { + delay = 5 + } else { + patch.getPatchFor(player).currentGrowthStage++ + patch.getPatchFor(player).setCurrentState(++patchStage) + 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 + } + }) + } +} diff --git a/Server/src/main/content/global/skill/farming/PatchType.kt b/Server/src/main/content/global/skill/farming/PatchType.kt new file mode 100644 index 000000000..fd0836126 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/PatchType.kt @@ -0,0 +1,22 @@ +package content.global.skill.farming + +enum class PatchType(val stageGrowthTime: Int) { + ALLOTMENT(10), + 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 new file mode 100644 index 000000000..6d08d9386 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/Plantable.kt @@ -0,0 +1,104 @@ +package content.global.skill.farming + +import core.game.node.item.Item +import org.rs09.consts.Items + +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(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(Items.SCARECROW_6059,"scarecrow",36,-3,0.0,0.0,0.0,23,PatchType.FLOWER_PATCH,Items.SCARECROW_6059), + + // 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(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(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(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(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(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), + + // 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, 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() + + @JvmStatic + fun forItemID(id: Int): Plantable?{ + return plantables[id] + } + + @JvmStatic + fun forItem(item: Item): Plantable?{ + return forItemID(item.id) + } + } +} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SackBasketOptionHandler.kt b/Server/src/main/content/global/skill/farming/SackBasketOptionHandler.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SackBasketOptionHandler.kt rename to Server/src/main/content/global/skill/farming/SackBasketOptionHandler.kt index cd193ca3f..b9a25c563 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SackBasketOptionHandler.kt +++ b/Server/src/main/content/global/skill/farming/SackBasketOptionHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.farming +package content.global.skill.farming import core.cache.def.impl.ItemDefinition import core.game.interaction.OptionHandler @@ -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 new file mode 100644 index 000000000..5f03a292f --- /dev/null +++ b/Server/src/main/content/global/skill/farming/SeedOnPlantPot.kt @@ -0,0 +1,113 @@ +package content.global.skill.farming + +import core.api.* +import core.game.node.Node +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() { + onUseWith(IntType.ITEM, TREE_SEEDS, Items.PLANT_POT_5354, handler = ::addSeedToPot) + onUseWith(IntType.ITEM, TREE_SEEDLINGS, *WATERING_CANS, handler = ::waterSeedling) + } + + fun addSeedToPot(player: Player, used: Node, with: Node) : Boolean { + val seed = used.asItem() ?: return false + val pot = with.asItem() ?: return false + + if (!inInventory(player, Items.GARDENING_TROWEL_5325)) { + sendDialogue(player, "You need a gardening trowel on you to do this.") + return false + } + + val seedling = getSeedling(seed.id) + 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 + } + + fun waterSeedling(player: Player, used: Node, with: Node) : Boolean { + val seedling = used.asItem() ?: return false + val can = with.asItem() ?: return false + + val nextCan = can.id.getNext() + val wateredSeedling = if (seedling.id > 5400) seedling.id + 8 else seedling.id + 6 + + if (!removeItem(player, can) || !removeItem(player, seedling)) return false + addItem(player, wateredSeedling) + addItem(player, nextCan) + + var seedlings = getOrStartTimer (player) + seedlings.addSeedling(wateredSeedling) + return true + } + + 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.CALQUAT_TREE_SEED_5290 -> Items.CALQUAT_SEEDLING_5487 + else -> -1 + } + } + + val TREE_SEEDS = intArrayOf( + Items.ACORN_5312, + Items.WILLOW_SEED_5313, + Items.MAPLE_SEED_5314, + Items.YEW_SEED_5315, + Items.MAGIC_SEED_5316, + Items.APPLE_TREE_SEED_5283, + Items.BANANA_TREE_SEED_5284, + Items.ORANGE_TREE_SEED_5285, + Items.CURRY_TREE_SEED_5286, + Items.PINEAPPLE_SEED_5287, + Items.PAPAYA_TREE_SEED_5288, + Items.PALM_TREE_SEED_5289, + Items.SPIRIT_SEED_5317, + Items.CALQUAT_TREE_SEED_5290 + ) + + val TREE_SEEDLINGS = intArrayOf( + Items.OAK_SEEDLING_5358, + Items.WILLOW_SEEDLING_5359, + Items.MAPLE_SEEDLING_5360, + Items.YEW_SEEDLING_5361, + Items.MAGIC_SEEDLING_5362, + Items.APPLE_SEEDLING_5480, + Items.BANANA_SEEDLING_5481, + Items.ORANGE_SEEDLING_5482, + Items.CURRY_SEEDLING_5483, + Items.PINEAPPLE_SEEDLING_5484, + Items.PAPAYA_SEEDLING_5485, + Items.PALM_SEEDLING_5486, + 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) +} diff --git a/Server/src/main/content/global/skill/farming/Seedling.kt b/Server/src/main/content/global/skill/farming/Seedling.kt new file mode 100644 index 000000000..a2e8f7a09 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/Seedling.kt @@ -0,0 +1,5 @@ +package content.global.skill.farming + +class Seedling(val id: Int, val TTL: Long, val sapling: Int) { + +} \ 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/kotlin/rs09/game/node/entity/skill/farming/ToolLeprechaunHandler.kt b/Server/src/main/content/global/skill/farming/ToolLeprechaunHandler.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/farming/ToolLeprechaunHandler.kt rename to Server/src/main/content/global/skill/farming/ToolLeprechaunHandler.kt index c769ccc1e..d2338609b 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/ToolLeprechaunHandler.kt +++ b/Server/src/main/content/global/skill/farming/ToolLeprechaunHandler.kt @@ -1,8 +1,8 @@ -package rs09.game.node.entity.skill.farming +package content.global.skill.farming import core.cache.def.impl.NPCDefinition import core.game.component.Component -import core.game.content.dialogue.FacialExpression +import core.game.dialogue.FacialExpression import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.npc.NPC @@ -11,6 +11,7 @@ import core.plugin.Initializable import core.plugin.Plugin import org.rs09.consts.Components import org.rs09.consts.NPCs +import content.minigame.vinesweeper.Vinesweeper val TL_IDS = arrayOf(NPCs.TOOL_LEPRECHAUN_3021,NPCs.GOTH_LEPRECHAUN_8000,NPCs.TOOL_LEPRECHAUN_4965,NPCs.TECLYN_2861) @Initializable @@ -28,7 +29,7 @@ class ToolLeprechaunHandler : OptionHandler() { node ?: return false when(option){ "exchange" -> player?.interfaceManager?.open(Component(Components.FARMING_TOOLS_125)) - "teleport" -> VinesweeperTeleport.teleport(node!! as NPC, player!!) + "teleport" -> Vinesweeper.Companion.VinesweeperTeleport.teleport(node!! as NPC, player!!) } return true } diff --git a/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt b/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt new file mode 100644 index 000000000..29e0d719d --- /dev/null +++ b/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt @@ -0,0 +1,351 @@ +package content.global.skill.farming + +import core.api.* +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 + +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 -> + openSingleTab(player, TOOLS_SIDE) + return@onOpen true + } + + 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) + 36 -> { + val sec = if (hasMagicSecateurs(player)) Items.MAGIC_SECATEURS_7409 else Items.SECATEURS_5329 + doWithdrawal(player, sec, ::setHasSecateurs, ::hasSecateurs) + } + 37 -> { + if (!hasWateringCan(player)) { + sendMessage(player, "You haven't got a watering can stored in here!") + } else { + 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) + } + 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) + 21 -> { + 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 { + removeItem(player, Items.SECATEURS_5329) + setHasSecateurs(player,true) + setHasMagicSecateurs(player,false) + } + } else { + sendMessage(player, "You cannot store more than one pair of secateurs in here.") + } + } + 22 -> { + val can = getHighestCan(player) + 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 { + 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) + } + return@on true + } + + } + + private fun doWithdrawal(player: Player?, item: Int, withdrawMethod: (Player?, Boolean) -> Unit, checkMethod: (Player?) -> Boolean) { + player ?: return + 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 (!hasSpaceFor(player, Item(item))) { + sendMessage(player, "You don't have enough space for that.") + return + } + withdrawMethod.invoke(player, false) + addItem(player, item) + } + } + + private fun doDeposit(player: Player?, item: Int, depositMethod: (Player?, Boolean) -> Unit, checkMethod: (Player?) -> Boolean) { + player ?: return + 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) + removeItem(player, item) + } else { + 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) { + player ?: return + val hasAmount = amountInInventory(player, item) + var finalAmount = amount + val spaceLeft = (if (item == Items.BUCKET_1925) 31 else 255) - quantityCheckMethod.invoke(player) + + 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 -> + var amt = value as Int + if (amt > hasAmount) { + amt = hasAmount + } + if (amt > spaceLeft) { + amt = spaceLeft + } + if (removeItem(player, Item(item, amt))) updateQuantityMethod.invoke(player, amt) + } + return + } + + if (amount == -1) { + finalAmount = hasAmount + if (finalAmount > spaceLeft) { + finalAmount = spaceLeft + } + } + + if (finalAmount > hasAmount) { + finalAmount = hasAmount + } + + 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 + } + + 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) { + player ?: return + val hasAmount = quantityCheckMethod.invoke(player) + var finalAmount = amount + + 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) { + 155 -> 1 + 196 -> 5 + 124 -> -1 + 199 -> -2 + else -> 0 + } + } + + private fun hasRake(player: Player?): Boolean { + return getVarbit(player!!, 1435) == 1 + } + + private fun setHasRake(player: Player?, hasRake: Boolean) { + setVarbit(player!!, 1435, if (hasRake) 1 else 0, true) + } + + private fun hasDibber(player: Player?): Boolean { + return getVarbit(player!!, 1436) == 1 + } + + private fun setHasDibber(player: Player?, hasDibber: Boolean) { + setVarbit(player!!, 1436, if (hasDibber) 1 else 0, true) + } + + private fun hasSpade(player: Player?): Boolean { + return getVarbit(player!!, 1437) == 1 + } + + private fun setHasSpade(player: Player?, hasSpade: Boolean) { + setVarbit(player!!, 1437, if (hasSpade) 1 else 0, true) + } + + private fun hasSecateurs(player: Player?): Boolean { + return getVarbit(player!!, 1438) == 1 + } + + private fun setHasSecateurs(player: Player?, hasSecateurs: Boolean) { + setVarbit(player!!, 1438, if (hasSecateurs) 1 else 0, true) + } + + private fun hasWateringCan(player: Player?): Boolean { + return getVarbit(player!!, 1439) > 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) { + Items.WATERING_CAN_5331 -> 1 + Items.WATERING_CAN1_5333 -> 2 + Items.WATERING_CAN2_5334 -> 3 + Items.WATERING_CAN3_5335 -> 4 + Items.WATERING_CAN4_5336 -> 5 + Items.WATERING_CAN5_5337 -> 6 + Items.WATERING_CAN6_5338 -> 7 + Items.WATERING_CAN7_5339 -> 8 + Items.WATERING_CAN8_5340 -> 9 + else -> 0 + } + setVarbit(player!!, 1439, can, true) + } + + private fun setNoWateringCan(player: Player?) { + setVarbit(player!!, 1439, 0, true) + } + + private fun hasGardeningTrowel(player: Player?): Boolean { + return getVarbit(player!!, 1440) == 1 + } + + private fun setHasGardeningTrowel(player: Player?, hasTrowel: Boolean) { + setVarbit(player!!, 1440, if (hasTrowel) 1 else 0, true) + } + + private fun getNumBuckets(player: Player?): Int { + return getVarbit(player!!, 1441) + } + + private fun updateBuckets(player: Player?, amount: Int) { + setVarbit(player!!, 1441, getNumBuckets(player) + amount, true) + } + + private fun getNumCompost(player: Player?): Int { + return getVarbit(player!!, 1442) + } + + private fun updateCompost(player: Player?, amount: Int) { + setVarbit(player!!, 1442, getNumCompost(player) + amount, true) + } + + private fun getNumSuperCompost(player: Player?): Int { + return getVarbit(player!!, 1443) + } + + private fun updateSuperCompost(player: Player?, amount: Int) { + setVarbit(player!!, 1443, getNumSuperCompost(player) + amount, true) + } + + private fun hasMagicSecateurs(player: Player?): Boolean { + return getVarbit(player!!, 1848) == 1 + } + + private fun setHasMagicSecateurs(player: Player?, hasMagic: Boolean) { + setVarbit(player!!, 1848, if (hasMagic) 1 else 0, true) + } + + 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 + } + } + return if (highestCan.id == 0) null else highestCan + } + +} diff --git a/Server/src/main/content/global/skill/farming/UseWithBinHandler.kt b/Server/src/main/content/global/skill/farming/UseWithBinHandler.kt new file mode 100644 index 000000000..8d557e9b6 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/UseWithBinHandler.kt @@ -0,0 +1,104 @@ +package content.global.skill.farming + +import core.api.* +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 + +class UseWithBinHandler : InteractionListener { + @JvmField + val allowedNodes = ArrayList(100) + val fillAnim = Animation(832) + val compostPotionAnimation = Animation(2259) + val scoopAnimation = Animation(8905) + + val bins = 7836..7840 + + override fun defineListeners() { + loadNodes() + onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *bins.toIntArray()) { player, usedNode, with -> + val cBin = CompostBins.forObject(with.asScenery()) ?: return@onUseWith true + val bin = cBin.getBinForPlayer(player) + 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) { + animate(player, compostPotionAnimation) + submitIndividualPulse(player, object : Pulse(compostPotionAnimation.duration) { + override fun pulse(): Boolean { + if (removeItem(player, usedNode.asItem())) { + bin.convert() + addItem(player, used.getNext()) + } + return true + } + }) + } else { + sendDialogue(player, "You can only do this with an open bin of finished regular compost.") + } + } + + Items.BUCKET_1925 -> { + if (bin.isFinished && !bin.isClosed) { + submitIndividualPulse(player, object : Pulse(scoopAnimation.duration) { + override fun pulse(): Boolean { + if (!player.inventory.containsItem(usedNode.asItem())) return true + animate(player, scoopAnimation) + val item = bin.takeItem() + if (item != null && removeItem(player, usedNode.asItem())) { + player.inventory.add(item) + } + return item == null || !player.inventory.containsItem(usedNode.asItem()) + } + }) + } else { + sendDialogue(player, "You can only scoop an opened bin of finished compost.") + } + } + + else -> + if (bin.isFull()) { + sendMessage(player, "This compost bin is already full.") + return@onUseWith true + } else if (!bin.isFinished) { + submitIndividualPulse(player, object : Pulse(fillAnim.duration) { + override fun pulse(): Boolean { + 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 + } + } + + fun loadNodes() { + for (p in Plantable.values()) { + if (p.harvestItem != Items.SCARECROW_6059) { + allowedNodes.add(p.harvestItem) + } + } + allowedNodes.add(Items.COCONUT_SHELL_5978) + allowedNodes.add(Items.WEEDS_6055) + allowedNodes.add(Items.COMPOST_POTION4_6470) + allowedNodes.add(Items.COMPOST_POTION3_6472) + allowedNodes.add(Items.COMPOST_POTION2_6474) + allowedNodes.add(Items.COMPOST_POTION1_6476) + allowedNodes.add(Items.BUCKET_1925) + } + + private fun Int.getNext(): Int { + if (this != 6476) return this + 2 + else return Items.VIAL_229 + } +} diff --git a/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt new file mode 100644 index 000000000..e443ad061 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt @@ -0,0 +1,362 @@ +package content.global.skill.farming + +import core.api.* +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 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 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() + + override fun defineListeners() { + loadNodes() + 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) { + RAKE -> PatchRaker.rake(player,patch) + SEED_DIBBER -> sendMessage(player, "I should plant a seed, not the seed dibber.") + SPADE -> { + val anim = spadeDigAnim + val p = patch.getPatchFor(player) + 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 { + 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) { + val pulse = CropHarvester.harvestPulse(player, with, Items.WILLOW_BRANCH_5933) ?: return@onUseWith false + submitIndividualPulse(player, pulse) + } + } + } + TROWEL, Items.PLANT_POT_5350 -> { + 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() && !p.isEmptyAndWeeded()) { + sendMessage(player, "This patch has something growing in it.") + return@onUseWith true + } else if (p.currentGrowthStage != 3) { + sendMessage(player, "I should clear this of weeds before trying to take some dirt.") + return@onUseWith true + } + + val potAmount = amountInInventory(player, Items.PLANT_POT_5350) + + if (potAmount == 0) { + sendMessage(player, "You have no plant pots to fill.") + return@onUseWith true + } + + val anim = trowelDigAnim + + submitIndividualPulse(player, object : Pulse(anim.duration) { + override fun pulse(): Boolean { + if (removeItem(player, Items.PLANT_POT_5350)) { + animate(player, anim) + addItem(player, Items.PLANT_POT_5354) + } else return true + return false + } + }) + } + + Items.PLANT_CURE_6036 -> { + val p = patch.getPatchFor(player) + 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) + } + 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 { + sendMessage(player, "I have no reason to do this right now.") + } + } + + 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 (t == PatchType.ALLOTMENT || t == PatchType.FLOWER_PATCH || t == PatchType.HOPS_PATCH) { + submitIndividualPulse(player, object : Pulse() { + override fun pulse(): Boolean { + if (p.isWeedy() || p.isEmptyAndWeeded()) { + sendMessage(player, "You should grow something first.") + return true + } + if (p.isWatered || p.isGrown() || p.plantable == Plantable.SCARECROW) { + sendMessage(player, "This patch doesn't need watering.") + return true + } + if (p.isDiseased || p.isDead) { + sendMessage(player, "Water isn't going to cure that!") + return true + } + if (usedItem.id == Items.WATERING_CAN_5331) { + sendMessage(player, "You need to fill the watering can first.") + return true + } + 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) + 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 + } + p.cropLives += if (p.compost == CompostType.SUPERCOMPOST) 2 else 1 + addItem(player, Items.BUCKET_1925) + } + return@runTask + } + } else { + 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) { + 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 (!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()) { + sendMessage(player, "This patch needs weeding first.") + return@onUseWith true + } else if (p.getCurrentState() > 3) { + sendMessage(player, "There is already something growing in this patch.") + return@onUseWith true + } + + val plantItem = when (patch.type) { + PatchType.ALLOTMENT -> Item(plantable.itemID, 3) + PatchType.HOPS_PATCH -> if (plantable == Plantable.JUTE_SEED) Item(plantable.itemID, 3) else Item(plantable.itemID, 4) + else -> Item(plantable.itemID,1) + } + + if (!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 == 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 + } + if (requiredItem != null && !inInventory(player, requiredItem)) { + sendDialogue(player, "You need ${prependArticle(requiredItem.asItem().name.lowercase())} to plant that.") + return@onUseWith true + } + val spiritTreePlanted = (getVarbit(player, 720) > 3 || getVarbit(player, 722) > 3 || getVarbit(player, 724) > 3) + if (plantable == Plantable.SPIRIT_SAPLING && spiritTreePlanted){ + sendDialogue(player, "You already have a spirit tree. You can't plant another one.") + return@onUseWith true + } + + queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> + when (stage) { + 0 -> { + val (anim, sound) = when (requiredItem) { + Items.SPADE_952 -> Pair(spadeDigAnim, Sounds.DIGSPADE_1470) + else -> Pair(seedDibberAnim,Sounds.FARMING_DIBBING_2432) + } + animate(player, anim) + playAudio(player, sound) + val delay = if (patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.SPIRIT_TREE_PATCH || + 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() { + for (p in Plantable.values()) { + allowedNodes.add(p.itemID) + } + 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 + 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/FireMakingOptionPlugin.kt b/Server/src/main/content/global/skill/firemaking/FireMakingOptionPlugin.kt new file mode 100644 index 000000000..76f745d01 --- /dev/null +++ b/Server/src/main/content/global/skill/firemaking/FireMakingOptionPlugin.kt @@ -0,0 +1,22 @@ +package content.global.skill.firemaking + +import core.game.node.item.GroundItem +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class FiremakingListener : InteractionListener +{ + val logs = intArrayOf(1511, 1521, 1513, 1515, 1517, 1519, 2862, 3438, 3440, 3442, 3444, 3446, 3448, 6211, 6213, 6332, 6333, 7404, 7405, 7406, 8934, 9067, 10328, 10329, 10808, 10810, 10812, 11035, 12581, 12583, 3125) + + override fun defineListeners() { + onUseWith(IntType.ITEM, Items.TINDERBOX_590, *logs) { player, _, with -> + player.pulseManager.run(FireMakingPulse(player, with.asItem(), null)) + return@onUseWith true + } + onUseWith(IntType.GROUNDITEM, Items.TINDERBOX_590, *logs) { player, _, with -> + player.pulseManager.run(FireMakingPulse(player, with.asItem(), with as GroundItem)) + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingPulse.java b/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingPulse.java rename to Server/src/main/content/global/skill/firemaking/FireMakingPulse.java index e8dda160d..50b969efd 100644 --- a/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingPulse.java +++ b/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java @@ -1,22 +1,23 @@ -package core.game.node.entity.skill.firemaking; +package content.global.skill.firemaking; -import core.game.world.map.zone.ZoneBorders; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; +import core.api.Container; +import core.game.event.LitFireEvent; 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.entity.player.link.diary.DiaryType; 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 rs09.game.world.GameWorld; +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. @@ -32,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. @@ -86,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); } } @@ -132,33 +135,18 @@ 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)); - if (TutorialSession.getExtension(player).getStage() == 9) { - TutorialStage.load(player, 10, false); - } + player.faceLocation(object.getFaceLocation(player.getLocation())); player.getSkills().addExperience(Skills.FIREMAKING,fire.getXp()); int playerRegion = player.getViewport().getRegion().getId(); - if (fire == Log.MAGIC && playerRegion == 10806) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.SEERS_VILLAGE,2, 5); - } - // Light a campfire from normal logs in Lumbridge Swamp - if (fire == Log.NORMAL && (playerRegion == 12593 || playerRegion == 12849)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 9); - } - // Light a willow log fire in Lumbridge Castle courtyard - if (fire == Log.WILLOW - && new ZoneBorders(3216, 3207, 3225, 3233, 0).insideBorder(player)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 2, 3); - } - setLastFire(); + player.dispatch(new LitFireEvent(fire.getLogId())); } @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 + ".."); @@ -190,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; } @@ -209,4 +197,4 @@ public final class FireMakingPulse extends SkillPulse { } return false; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/node/entity/skill/firemaking/LightLogPlugin.java b/Server/src/main/content/global/skill/firemaking/LightLogPlugin.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/firemaking/LightLogPlugin.java rename to Server/src/main/content/global/skill/firemaking/LightLogPlugin.java index 11f9a0e95..db524a6b7 100644 --- a/Server/src/main/java/core/game/node/entity/skill/firemaking/LightLogPlugin.java +++ b/Server/src/main/content/global/skill/firemaking/LightLogPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.firemaking; +package content.global.skill.firemaking; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/node/entity/skill/firemaking/Log.java b/Server/src/main/content/global/skill/firemaking/Log.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/firemaking/Log.java rename to Server/src/main/content/global/skill/firemaking/Log.java index 920ca0c5b..59c7b82cb 100644 --- a/Server/src/main/java/core/game/node/entity/skill/firemaking/Log.java +++ b/Server/src/main/content/global/skill/firemaking/Log.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.firemaking; +package content.global.skill.firemaking; import java.util.HashMap; 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.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.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.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.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/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/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/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 new file mode 100644 index 000000000..cc7112fb3 --- /dev/null +++ b/Server/src/main/content/global/skill/gather/GatheringSkillOptionListeners.kt @@ -0,0 +1,25 @@ +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 + +class GatheringSkillOptionListeners : InteractionListener { + + val ETCETERIA_REGION = 10300 + + override fun defineListeners() { + + } + + fun fish(player: Player, node: Node, opt: String): Boolean{ + val npc = node as NPC + val spot = FishingSpot.forId(npc.id) ?: return false + val op = spot.getOptionByName(opt) ?: return false + player.pulseManager.run(FishingPulse(player, npc, op)) + return true + } +} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/PickaxeRepairPlugin.kt b/Server/src/main/content/global/skill/gather/PickaxeRepairPlugin.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/gather/PickaxeRepairPlugin.kt rename to Server/src/main/content/global/skill/gather/PickaxeRepairPlugin.kt index c2298eea8..83e3db86a 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/PickaxeRepairPlugin.kt +++ b/Server/src/main/content/global/skill/gather/PickaxeRepairPlugin.kt @@ -1,10 +1,12 @@ -package rs09.game.node.entity.skill.gather +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/java/core/game/node/entity/skill/gather/SkillingResource.java b/Server/src/main/content/global/skill/gather/SkillingResource.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/gather/SkillingResource.java rename to Server/src/main/content/global/skill/gather/SkillingResource.java index ed2aeb7c1..2d2b43e65 100644 --- a/Server/src/main/java/core/game/node/entity/skill/gather/SkillingResource.java +++ b/Server/src/main/content/global/skill/gather/SkillingResource.java @@ -1,9 +1,9 @@ -package core.game.node.entity.skill.gather; +package content.global.skill.gather; -import rs09.ServerConstants; +import core.ServerConstants; import core.game.node.entity.skill.Skills; -import rs09.game.system.SystemLogger; -import rs09.game.world.repository.Repository; +import core.tools.SystemLogger; +import core.game.world.repository.Repository; import core.game.world.update.flag.context.Animation; import java.util.HashMap; @@ -83,7 +83,7 @@ public enum SkillingResource { /** * Dramen tree (Woodcutting/Lost city quest). */ - DRAMEN_TREE(1292, 1, 0.05, -1, 25.0, 771, Integer.MAX_VALUE, "dramen tree", null, -1, Skills.WOODCUTTING), + DRAMEN_TREE(1292, 36, 0.05, -1, 25.0, 771, Integer.MAX_VALUE, "dramen tree", null, -1, Skills.WOODCUTTING), /** * Evergreen (Woodcutting). @@ -226,14 +226,14 @@ public enum SkillingResource { COPPER_ORE_41(37688, 1, 0.05, 4 | 8 << 16, 17.5, 436, 1, "copper rocks", null, 21298, Skills.MINING), COPPER_ORE_42(37686, 1, 0.05, 4 | 8 << 16, 17.5, 436, 1, "copper rocks", null, 21296, Skills.MINING), COPPER_ORE_43(37687, 1, 0.05, 4 | 8 << 16, 17.5, 436, 1, "copper rocks", null, 21297, Skills.MINING), - COPPER_ORE_44(3042, 1, 0.05, 4 | 8 << 16, 17.5, 436, 1, "copper rocks", null, 450, Skills.MINING), + COPPER_ORE_44(3042, 1, 0.05, 4 | 8 << 16, 17.5, 436, 1, "copper rocks", null, 11552, Skills.MINING), /** * Tin ore (Mining). */ TIN_ORE_0(2094, 1, 0.05, 4 | 8 << 16, 17.5, 438, 1, "tin rocks", null, 450, Skills.MINING), TIN_ORE_1(2095, 1, 0.05, 4 | 8 << 16, 17.5, 438, 1, "tin rocks", null, 452, Skills.MINING), - TIN_ORE_2(3043, 1, 0.05, 4 | 8 << 16, 17.5, 438, 1, "tin rocks", null, 450, Skills.MINING), + TIN_ORE_2(3043, 1, 0.05, 4 | 8 << 16, 17.5, 438, 1, "tin rocks", null, 11552, Skills.MINING), TIN_ORE_3(4979, 1, 0.05, 4 | 8 << 16, 17.5, 438, 1, "tin rocks", null, 4994, Skills.MINING), TIN_ORE_4(4980, 1, 0.05, 4 | 8 << 16, 17.5, 438, 1, "tin rocks", null, 4995, Skills.MINING), TIN_ORE_5(4981, 1, 0.05, 4 | 8 << 16, 17.5, 438, 1, "tin rocks", null, 4996, Skills.MINING), @@ -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("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 new file mode 100644 index 000000000..3e309fb65 --- /dev/null +++ b/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt @@ -0,0 +1,304 @@ +package content.global.skill.gather.fishing + +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.tools.colorize +import org.rs09.consts.Items + +/** + * Handles a fishing pulse. + * + * @author Ceikry + */ +class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption?) : SkillPulse(player, npc) { + /** + * Represents the fish type. + */ + private var fish: Fish? = null + + /** + * Represents the base location the npc was at. + */ + private val location: Location = npc.location + + override fun start() { + if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar is Forager) { + val forager = player.familiarManager.familiar as Forager + val dest = player.location.transform(player.direction) + Pathfinder.find(forager.location, dest).walk(forager) + } + super.start() + } + + override fun checkRequirements(): Boolean { + if (option == null) { + return false + } + 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)) { + 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 (!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 (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 + } + if (location !== node!!.location || !node!!.isActive || node!!.isInvisible) { + stop() + return false + } + return true + } + + override fun animate() { + if (isBareHanded(player)) { + player.animate(Animation(6709)) + Pulser.submit(object : Pulse(1) { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 5 -> getCatchAnimationAndLoot(player) + } + return false + } + }) + } else { + player.animate(option!!.animation) + } + } + + override fun reward(): Boolean { + if (delay == 1) { + super.setDelay(5) + return false + } + if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar is Forager) { + val forager = player.familiarManager.familiar as Forager + forager.handlePassiveAction() + } + if (success()) { + if (player.inventory.hasSpaceFor(Item(fish!!.id)) && option!!.removeBait(player)) { + if (player.skillTasks.hasTask()) { + updateSkillTask() + } + player.dispatch(ResourceProducedEvent(fish!!.id, 1, node!!)) + val item = fish!! + if (isActive(SkillcapePerks.GREAT_AIM, player) && RandomFunction.random(100) <= 5) { + addItemOrDrop(player, item.id) + player.sendMessage(colorize("%RYour expert aim catches you a second fish.")) + } + 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) + message(2) + } + } + return player.inventory.freeSlots() == 0 + } + + fun updateSkillTask() { + when (fish) { + Fish.ANCHOVIE -> { + player.skillTasks.decreaseTask(player, SkillTasks.FANCHOVIES1) + player.skillTasks.decreaseTask(player, SkillTasks.FANCHOVIES2) + } + Fish.HERRING -> { + player.skillTasks.decreaseTask(player, SkillTasks.FHERRING1) + player.skillTasks.decreaseTask(player, SkillTasks.FHERRING2) + } + Fish.LOBSTER -> { + player.skillTasks.decreaseTask(player, SkillTasks.FLOBSTER1) + player.skillTasks.decreaseTask(player, SkillTasks.FLOBSTER2) + } + Fish.SALMON -> { + player.skillTasks.decreaseTask(player, SkillTasks.FSALMON1) + player.skillTasks.decreaseTask(player, SkillTasks.FSALMON2) + } + Fish.SHARK -> { + player.skillTasks.decreaseTask(player, SkillTasks.FSHARK1) + player.skillTasks.decreaseTask(player, SkillTasks.FSHARK2) + player.skillTasks.decreaseTask(player, SkillTasks.FSHARK3) + } + Fish.SHRIMP -> { + player.skillTasks.decreaseTask(player, SkillTasks.FSHRIMP1) + player.skillTasks.decreaseTask(player, SkillTasks.FSHRIMP2) + } + Fish.SWORDFISH -> { + player.skillTasks.decreaseTask(player, SkillTasks.FSWORD1) + player.skillTasks.decreaseTask(player, SkillTasks.FSWORD2) + } + Fish.TROUT -> { + player.skillTasks.decreaseTask(player, SkillTasks.FTROUT1) + player.skillTasks.decreaseTask(player, SkillTasks.FTROUT2) + } + Fish.TUNA -> { + 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.SHARK_HARPOON) { + if (checkFish(p) > 0 && !(inInventory(player, option.tool) || inEquipment(player, option.tool) || hasBarbTail()) + ) { + return true + } + if (checkFish(p) > 2 && !(inInventory(player, option.tool) || inEquipment(player, option.tool) || hasBarbTail()) + ) { + return true + } + } + return false + } + + private fun getCatchAnimationAndLoot(p: Player): Int { + val fishingFor = checkFish(p) + when (node!!.id) { + 324 -> when (fishingFor) { + 1 -> { + p.animate(Animation(6710)) + p.skills.addExperience(Skills.FISHING, 80.0) + p.skills.addExperience(Skills.STRENGTH, 8.0) + p.inventory.add(Item(359)) + } + 2, 3 -> if (RandomFunction.random(1) == 1) { + p.animate(Animation(6710)) + p.skills.addExperience(Skills.FISHING, 80.0) + p.skills.addExperience(Skills.STRENGTH, 8.0) + p.inventory.add(Item(359)) + } else { + p.animate(Animation(6707)) + p.skills.addExperience(Skills.FISHING, 100.0) + p.skills.addExperience(Skills.STRENGTH, 10.0) + p.inventory.add(Item(371)) + } + } + 313 -> { + p.animate(Animation(6705)) + p.skills.addExperience(Skills.FISHING, 110.0) + p.skills.addExperience(Skills.STRENGTH, 11.0) + p.inventory.add(Item(383)) + } + } + return 0 + } + + /** + * Checks if they have the barb tail harpoon. + * + * @return `True` if so. + */ + private fun hasBarbTail(): 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 + } + + override fun message(type: Int) { + when (type) { + 0 -> sendMessage(player, option!!.getStartMessage()) + 2 -> { + 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) { + if (fish == Fish.LOBSTER) { + sendDialogue(player, "You can't carry any more lobsters.") + } else { + sendDialogue(player, "You can't carry any more fish.") + } + stop() + } + } + } + } + + /** + * Method used to check if the catch was a success. + * + * @return `True` if so. + */ + private fun success(): Boolean { + if (delay == 1) { + return false + } + fish = option!!.rollFish(player) + return fish != null + } + + companion object { + fun checkFish(p: Player): Int { + return if (p.skills.getLevel(Skills.FISHING) >= 55 && p.skills.getLevel(Skills.STRENGTH) >= 35) { + if (p.skills.getLevel(Skills.FISHING) >= 70 && p.skills.getLevel(Skills.STRENGTH) >= 50) { + if (p.skills.getLevel(Skills.FISHING) >= 96 && p.skills.getLevel(Skills.STRENGTH) >= 76) { + 3 + } else 2 + } else 1 + } else 0 + } + } + + /** + * Constructs a new `FishingPulse` `Object`. + * + * @param player the player. + * @param npc the fishing spot NPC. + * @param option The fishing option. + */ + init { + } +} diff --git a/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishInteractionListeners.kt b/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishInteractionListeners.kt new file mode 100644 index 000000000..7963fb099 --- /dev/null +++ b/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishInteractionListeners.kt @@ -0,0 +1,31 @@ +package content.global.skill.gather.fishing.barbfishing + +import core.game.node.item.Item +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class BarbFishInteractionListeners : InteractionListener { + override fun defineListeners() { + + on(25268, IntType.SCENERY, "search"){ player, _ -> + if(player.getAttribute("barbtraining:fishing",false) == true){ + if(!player.inventory.containsItem(Item(11323))){ + player.inventory.add(Item(11323)) + player.sendMessage("Under the bed you find a fishing rod.") + } else { + player.sendMessage("You find nothing under the bed") + } + } else { + player.sendMessage("Maybe I should speak to Otto before looking under his bed.") + } + return@on true + } + + on(1176, IntType.NPC, "fish"){ player, _ -> + player.pulseManager.run(BarbFishingPulse(player)) + player.sendMessage("You attempt to catch a fish...") + return@on true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/BarbFishingPulse.kt b/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishingPulse.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/BarbFishingPulse.kt rename to Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishingPulse.kt index 9c2cc6385..a44387209 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/BarbFishingPulse.kt +++ b/Server/src/main/content/global/skill/gather/fishing/barbfishing/BarbFishingPulse.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.gather.fishing.barbfishing +package content.global.skill.gather.fishing.barbfishing import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player @@ -8,7 +8,7 @@ import core.game.node.item.Item import core.game.world.update.flag.context.Animation import core.tools.RandomFunction import org.rs09.consts.Items -import rs09.tools.stringtools.colorize +import core.tools.colorize /** * Pulse used for barbarian fishing @@ -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/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/FishCuttingPulse.kt b/Server/src/main/content/global/skill/gather/fishing/barbfishing/FishCuttingPulse.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/FishCuttingPulse.kt rename to Server/src/main/content/global/skill/gather/fishing/barbfishing/FishCuttingPulse.kt index 64563b8bd..9ec1e1466 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/FishCuttingPulse.kt +++ b/Server/src/main/content/global/skill/gather/fishing/barbfishing/FishCuttingPulse.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.gather.fishing.barbfishing +package content.global.skill.gather.fishing.barbfishing import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/KnifeWithFish.kt b/Server/src/main/content/global/skill/gather/fishing/barbfishing/KnifeWithFish.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/KnifeWithFish.kt rename to Server/src/main/content/global/skill/gather/fishing/barbfishing/KnifeWithFish.kt index 9f089a9dd..0b6e24917 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/KnifeWithFish.kt +++ b/Server/src/main/content/global/skill/gather/fishing/barbfishing/KnifeWithFish.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.gather.fishing.barbfishing +package content.global.skill.gather.fishing.barbfishing import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler diff --git a/Server/src/main/content/global/skill/gather/fishing/barbfishing/SpotManager.kt b/Server/src/main/content/global/skill/gather/fishing/barbfishing/SpotManager.kt new file mode 100644 index 000000000..60946cd5a --- /dev/null +++ b/Server/src/main/content/global/skill/gather/fishing/barbfishing/SpotManager.kt @@ -0,0 +1,78 @@ +package content.global.skill.gather.fishing.barbfishing + +import content.global.skill.gather.fishing.barbfishing.SpotManager.Companion.locations +import content.global.skill.gather.fishing.barbfishing.SpotManager.Companion.usedLocations +import core.api.StartupListener +import core.api.TickListener +import core.game.node.entity.npc.NPC +import core.game.world.map.Location +import core.tools.RandomFunction + +/** + * Manages fishing spot spawning and relocation + * @author Ceikry + */ +class SpotManager : TickListener, StartupListener { + var ticks = 0 + val spots = ArrayList() + + companion object{ + val usedLocations = arrayListOf() + val locations = listOf( + Location.create(2506, 3494, 0), + Location.create(2504, 3497, 0), + Location.create(2504, 3497, 0), + Location.create(2500, 3506, 0), + Location.create(2500, 3509, 0), + Location.create(2500, 3512, 0), + Location.create(2504, 3516, 0) + ) + } + + override fun tick() { + if(ticks % 50 == 0){ + usedLocations.clear() + for(spot in spots) usedLocations.add(spot.loc ?: Location(0,0,0)) + } + } + + override fun startup() { + for(i in 0 until 5){ + spots.add(BarbFishingSpot(getNewLoc(), getNewTTL()).also { it.init() }) + } + } +} + +fun getNewTTL(): Int{ + return RandomFunction.random(400,2000) +} + +fun getNewLoc(): Location { + val possibleLoc = ArrayList() + for(loc in locations) if(usedLocations.contains(loc)) continue else possibleLoc.add(loc) + val loc = possibleLoc.random() + usedLocations.add(loc) + return loc +} + +class BarbFishingSpot(var loc: Location? = null, var ttl: Int) : NPC(1176){ + init { + location = loc + } + val locations = listOf( + Location.create(2506, 3494, 0), + Location.create(2504, 3497, 0), + Location.create(2504, 3497, 0), + Location.create(2500, 3506, 0), + Location.create(2500, 3509, 0), + Location.create(2500, 3512, 0), + Location.create(2504, 3516, 0) + ) + override fun handleTickActions() { + if(location != loc) properties.teleportLocation = loc.also { ttl = getNewTTL() } + if(ttl-- <= 0){ + usedLocations.remove(location) + loc = getNewLoc() + } + } +} 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/java/core/game/node/entity/skill/gather/mining/MiningNode.java b/Server/src/main/content/global/skill/gather/mining/MiningNode.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/gather/mining/MiningNode.java rename to Server/src/main/content/global/skill/gather/mining/MiningNode.java index 1c81dd7a5..b271fdee2 100644 --- a/Server/src/main/java/core/game/node/entity/skill/gather/mining/MiningNode.java +++ b/Server/src/main/content/global/skill/gather/mining/MiningNode.java @@ -1,13 +1,13 @@ -package core.game.node.entity.skill.gather.mining; +package content.global.skill.gather.mining; -import rs09.ServerConstants; +import core.ServerConstants; import core.game.node.item.WeightedChanceItem; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; import java.util.ArrayList; import java.util.HashMap; import java.util.List; - +// 11553 11552 /** * Mining nodes * @author ceik @@ -58,12 +58,12 @@ public enum MiningNode{ COPPER_ORE_41(37688,21298, (byte) 1), COPPER_ORE_42(37686,21296, (byte) 1), COPPER_ORE_43(37687,21297, (byte) 1), - COPPER_ORE_44(3042, 450, (byte) 1), + COPPER_ORE_44(3042, 11552, (byte) 1), //Tin TIN_ORE_0( 2094, 450, (byte) 2), TIN_ORE_1( 2095, 452, (byte) 2), - TIN_ORE_2( 3043, 450, (byte) 2), + TIN_ORE_2( 3043, 11552, (byte) 2), TIN_ORE_3( 4979, 4994, (byte) 2), TIN_ORE_4( 4980, 4995, (byte) 2), TIN_ORE_5( 4981, 4996, (byte) 2), @@ -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); @@ -572,6 +575,14 @@ public enum MiningNode{ rate = 0.6; reward = 12630; level = 46; + break; + case 18: + respawnRate = 100 | 200 << 16; + experience = 0.0; + rate = 0.3; + reward = 4703; + level = 20; + break; } } private static HashMap NODE_MAP = new HashMap<>(); diff --git a/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt b/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt new file mode 100644 index 000000000..4c26fac4d --- /dev/null +++ b/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt @@ -0,0 +1,272 @@ +package content.global.skill.gather.mining + +import core.api.* +import core.game.event.ResourceProducedEvent +import core.cache.def.impl.ItemDefinition +import core.game.node.Node +import core.game.node.entity.impl.Animator +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 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 +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.tools.RandomFunction +import org.rs09.consts.Items +import core.game.system.command.sets.STATS_BASE +import core.game.system.command.sets.STATS_ROCKS +import core.tools.prependArticle + +/** + * Mining skill pulse + * @author Ceikry + * @author bushtail -> maintenance July 2022 + */ +class MiningSkillPulse(private val player: Player, private val node: Node) : Pulse(1, player, node) { + private var resource: MiningNode? = null + private var isMiningEssence = false + private var isMiningGems = false + private var ticks = 0 + private var resetAnimation = true + + // Perfect Gold Ore in Witchhaven Dungeon (Family Crest) + private val perfectGoldOreLocations = listOf( + Location(2735, 9695, 0), + Location(2737, 9689, 0), + Location(2740, 9684, 0), + Location(2737, 9683, 0), + ) + + fun message(type: Int) { + if (type == 0) { + sendMessage(player, "You swing your pickaxe at the rock...") + } + } + + override fun pulse(): Boolean { + if (!checkRequirements()) { + return true + } + animate() + return reward() + } + + override fun stop() { + if (resetAnimation) { + animate(player, Animation(-1, Animator.Priority.HIGH)) + } + super.stop() + message(1) + } + + override fun start() { + resource = MiningNode.forId(node.id) + if (MiningNode.isEmpty(node.id)) { + sendMessage(player, "This rock contains no ore.") + } + if (resource == null) { + return + } + if (resource!!.id == 2099 && + !perfectGoldOreLocations.contains(node.location) ) { + // Perfect Gold Ore IDs outside Witchhaven are replaced with a normal gold rock. + resource = MiningNode.forId(2098) + } + if (resource!!.id == 2491) { + isMiningEssence = true + } + if (resource!!.identifier == MiningNode.GEM_ROCK_0.identifier) { + isMiningGems = true + } + if (checkRequirements()) { + super.start() + message(0) + } + } + + fun checkRequirements(): Boolean { + if (getDynLevel(player, Skills.MINING) < resource!!.level) { + sendMessage(player, "You need a mining level of ${resource!!.level} to mine this rock.") + return false + } + if (SkillingTool.getPickaxe(player) == null) { + sendMessage(player, "You do not have a pickaxe to use.") + return false + } + if (freeSlots(player) == 0) { + if(resource!!.identifier == 13.toByte()) { + sendDialogue(player,"Your inventory is too full to hold any more gems.") + return false + } + sendDialogue(player,"Your inventory is too full to hold any more ${ItemDefinition.forId(resource!!.reward).name.lowercase()}.") + return false + } + return true + } + + fun animate() { + animate(player, SkillingTool.getPickaxe(player).animation) + } + + fun reward(): Boolean { + if (++ticks % (if (isMiningEssence) 3 else 4) != 0) { + return false + } + if (!checkReward()) { + return false + } + + // Reward logic + var reward = resource!!.reward + var rewardAmount : Int + if (reward > 0) { + reward = calculateReward(reward) // calculate rewards + rewardAmount = calculateRewardAmount(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) + } + } + val rewardName = getItemName(reward).lowercase() + + // Send the message for the resource reward + if (isMiningGems) { + sendMessage(player, "You get ${prependArticle(rewardName)}.") + } else { + sendMessage(player, "You get some ${rewardName.lowercase()}.") + } + + // Give the mining reward, increment 'rocks mined' attribute + addItemOrDrop(player, reward, rewardAmount) + var rocksMined = getAttribute(player, "$STATS_BASE:$STATS_ROCKS", 0) + setAttribute(player, "/save:$STATS_BASE:$STATS_ROCKS", rocksMined + rewardAmount) + + // Calculate bonus gem chance while mining + if (!isMiningEssence) { + var chance = 282 + var altered = false + val ring = getItemFromEquipment(player, EquipmentSlot.RING) + if (ring != null && ring.name.lowercase().contains("ring of wealth") || inEquipment(player, Items.RING_OF_THE_STAR_SPRITE_14652)) { + chance = (chance / 1.5).toInt() + altered = true + } + val necklace = getItemFromEquipment(player, EquipmentSlot.NECK) + if (necklace != null && necklace.id in 1705..1713) { + chance = (chance / 1.5).toInt() + altered = true + } + if (RandomFunction.roll(chance)) { + val gem = GEM_REWARDS.random() + sendMessage(player,"You find a ${gem.name}!") + if (freeSlots(player) == 0) { + sendMessage(player,"You do not have enough space in your inventory, so you drop the gem on the floor.") + } + addItemOrDrop(player, gem.id) + } + } + + // Transform ore to depleted version + if (!isMiningEssence && resource!!.respawnRate != 0) { + SceneryBuilder.replace(node as Scenery, Scenery(resource!!.emptyId, node.getLocation(), node.type, node.rotation), resource!!.respawnDuration) + node.setActive(false) + return true + } + } + return false + } + + private fun calculateRewardAmount(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) <= 10) { + 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) <= 10) { + 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) <= 10) { + 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(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 + } + + /** + * Checks if the player gets rewarded. + * @return `True` if so. + */ + private fun checkReward(): Boolean { + val level = 1 + getDynLevel(player, Skills.MINING) + getFamiliarBoost(player, Skills.MINING) + val hostRatio = Math.random() * (100.0 * resource!!.rate) + var toolRatio = SkillingTool.getPickaxe(player).ratio + if(SkillcapePerks.isActive(SkillcapePerks.PRECISION_MINER,player)){ + toolRatio += 0.075 + } + val clientRatio = Math.random() * ((level - resource!!.level) * (1.0 + toolRatio)) + return hostRatio < clientRatio + } + + companion object { + 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)) + } + + init { + super.stop() + } +} diff --git a/Server/src/main/content/global/skill/gather/mining/ProspectListener.kt b/Server/src/main/content/global/skill/gather/mining/ProspectListener.kt new file mode 100644 index 000000000..13ec9b0dc --- /dev/null +++ b/Server/src/main/content/global/skill/gather/mining/ProspectListener.kt @@ -0,0 +1,52 @@ +package content.global.skill.gather.mining + +import core.api.* +import content.global.skill.gather.mining.MiningNode +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * @author: bushtail + */ + +class ProspectListener : InteractionListener { + + override fun defineListeners() { + on(IntType.SCENERY, "prospect") { player, node -> + val rock = MiningNode.forId(node.asScenery().id) + + if(rock == null) { + sendMessage(player, "There is no ore currently available in this rock.") + return@on true + } + + /** Check if the rock contains gems */ + if(MiningNode.forId(node.id).identifier == 13.toByte()) { + sendMessage(player,"You examine the rock for ores...") + /** Send a simple text string saying it's a gem rock */ + player.pulseManager.run(object : Pulse(3) { + override fun pulse(): Boolean { + sendMessage(player, "This rock contains gems.") + return true + } + }) + return@on true + } + + /** If it doesn't contain gems */ + else { + sendMessage(player,"You examine the rock for ores...") + /** Get the name of the rock's reward and sends a message to the player */ + player.pulseManager.run(object : Pulse(3) { + override fun pulse(): Boolean { + sendMessage(player, "This rock contains ${Item(rock.reward).name.lowercase()}.") + return true + } + }) + } + return@on true + } + } +} 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/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingNode.java b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingNode.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingNode.java rename to Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingNode.java index 566d63ba8..ea769da8a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingNode.java +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingNode.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.gather.woodcutting; +package content.global.skill.gather.woodcutting; -import rs09.ServerConstants; -import rs09.game.world.repository.Repository; +import core.ServerConstants; +import core.game.world.repository.Repository; import java.util.HashMap; @@ -109,6 +109,9 @@ public enum WoodcuttingNode { //Mahogany MAHOGANY(9034,9035, (byte) 12), + //Swaying Tree + SWAYING_TREE(4142,-1, (byte) 30), + //Arctic pine ARCTIC_PINE(21273,21274, (byte) 13), @@ -136,12 +139,12 @@ public enum WoodcuttingNode { int full,empty,reward,respawnRate,level, rewardAmount; double experience,rate; - byte identifier; + 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; @@ -158,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; @@ -166,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; @@ -173,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; @@ -250,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; @@ -303,7 +318,19 @@ public enum WoodcuttingNode { respawnRate = -1; rate = 0.05; experience = 25.0; - level = 1; + level = 36; + rewardAmount = Integer.MAX_VALUE; + baseLow = 255; + baseHigh = 255; + tierModLow = 0; + tierModHigh = 0; + break; + case 30: + reward = 3692; + respawnRate = -1; + rate = 0.05; + experience = 1; + level = 40; rewardAmount = Integer.MAX_VALUE; break; } @@ -321,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; @@ -329,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; @@ -337,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; @@ -345,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; @@ -353,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 new file mode 100644 index 000000000..d69c7fea1 --- /dev/null +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java @@ -0,0 +1,285 @@ +package content.global.skill.gather.woodcutting; + +import content.data.tables.BirdNest; +import content.global.skill.farming.FarmingPatch; +import content.global.skill.farming.Patch; +import content.global.skill.skillcapeperks.SkillcapePerks; +import core.game.event.ResourceProducedEvent; +import core.cache.def.impl.ItemDefinition; +import core.game.container.impl.EquipmentContainer; +import core.game.dialogue.FacialExpression; +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.audio.Audio; +import core.game.node.entity.player.link.diary.DiaryType; +import core.game.node.entity.skill.Skills; +import content.data.skill.SkillingTool; +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.update.flag.context.Animation; +import core.tools.RandomFunction; +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; + +/** + * Woodcutting skill pulse + * + * @author ceik + */ +public class WoodcuttingSkillPulse extends Pulse { + private int[] woodcuttingSounds = { + Sounds.WOODCUTTING_HIT_3038, + Sounds.WOODCUTTING_HIT_3039, + Sounds.WOODCUTTING_HIT_3040, + Sounds.WOODCUTTING_HIT_3041, + Sounds.WOODCUTTING_HIT_3042 + }; + + private WoodcuttingNode resource; + private int ticks; + private Player player; + private Scenery node; + protected boolean resetAnimation = true; + + + public WoodcuttingSkillPulse(Player player, Scenery node) { + super(1, player, node); + this.player = player; + this.node = node; + super.stop(); + } + + public void message(int type) { + if (type == 0) { + player.getPacketDispatch().sendMessage("You swing your axe at the tree..."); + } + } + + @Override + public boolean pulse() { + if (!checkRequirements()) { + return true; + } + animate(); + return reward(); + } + + @Override + public void stop() { + if (resetAnimation) { + player.animate(new Animation(-1, Animator.Priority.HIGH)); + } + super.stop(); + message(1); + } + + @Override + public void start() { + resource = WoodcuttingNode.forId(node.getId()); + if (resource == null) { + return; + } + if (checkRequirements()) { + super.start(); + message(0); + } + } + + public boolean checkRequirements() { + 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.getPacketDispatch().sendMessage("You do not have an axe to use."); + return false; + } + if (player.getInventory().freeSlots() < 1) { + player.getDialogueInterpreter().sendDialogue("Your inventory is too full to hold any more " + ItemDefinition.forId(resource.getReward()).getName().toLowerCase() + "."); + return false; + } + return true; + } + + public void animate() { + if(!player.getAnimator().isAnimating()) { + player.animate(SkillingTool.getHatchet(player).getAnimation()); + + List playersAroundMe = RegionManager.getLocalPlayers(player, 2) + .stream() + .filter(p -> !p.getUsername().equals(player.getUsername())) + .collect(Collectors.toList()); + + int soundIndex = RandomFunction.random(0, woodcuttingSounds.length); + + for (Player p : playersAroundMe) { + playAudio(p, woodcuttingSounds[soundIndex]); + } + } + } + + public boolean reward() { + if (++ticks % 4 != 0) { + return false; + } + if (node.getId() == 10041) { + player.getDialogueInterpreter().sendDialogues(2574, FacialExpression.FURIOUS, RandomFunction.random(2) == 1 ? "You'll blow my cover! I'm meant to be hidden!" : "Will you stop that?"); + return true; + } + if (!checkReward(SkillingTool.getHatchet(player))) { + return false; + } + + // 20% chance to auto burn logs when using "inferno adze" item + if (SkillingTool.getHatchet(player).getId() == 13661 && RandomFunction.random(100) < 25) { + player.sendMessage("You chop some logs. The heat of the inferno adze incinerates them."); + Projectile.create(player, null, 1776, 35, 30, 20, 25).transform(player, new Location(player.getLocation().getX() + 2, player.getLocation().getY()), true, 25, 25).send(); + player.getSkills().addExperience(Skills.WOODCUTTING, resource.getExperience()); + player.getSkills().addExperience(Skills.FIREMAKING, resource.getExperience()); + return rollDepletion(); + } + + //actual reward calculations + int reward = resource.getReward(); + int rewardAmount = 0; + if (reward > 0) { + reward = calculateReward(reward); // calculate rewards + rewardAmount = calculateRewardAmount(reward); // calculate amount + + //add experience + double experience = calculateExperience(resource.reward, rewardAmount); + + player.getSkills().addExperience(Skills.WOODCUTTING, experience, true); + + //send the message for the resource reward + player.getPacketDispatch().sendMessage("You get some " + ItemDefinition.forId(reward).getName().toLowerCase() + "."); + //give the reward + player.getInventory().add(new Item(reward, rewardAmount)); + player.dispatch(new ResourceProducedEvent(reward, rewardAmount, node, -1)); + int cutLogs = player.getAttribute(STATS_BASE + ":" + STATS_LOGS,0); + player.setAttribute("/save:" + STATS_BASE + ":" + STATS_LOGS,++cutLogs); + + //calculate bonus bird nest for mining + int chance = 282; + if (RandomFunction.random(chance) == chance / 2) { + if(SkillcapePerks.isActive(SkillcapePerks.NEST_HUNTER,player)){ + if(!player.getInventory().add(BirdNest.getRandomNest(false).getNest())){ + BirdNest.drop(player); + } + } else { + BirdNest.drop(player); + } + } + + } + + return rollDepletion(); + } + + private boolean rollDepletion() { + //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) || resource.identifier == 1 || resource.identifier == 2 || resource.identifier == 3 || resource.identifier == 6) { + if (resource.isFarming()) { + FarmingPatch fPatch = FarmingPatch.forObject(node.asScenery()); + if(fPatch != null) { + Patch patch = fPatch.getPatchFor(player, true); + patch.setCurrentState(patch.getCurrentState() + 1); + } + 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, Sounds.TREE_FALLING_2734); + return true; + } + } + return false; + } + + private int calculateRewardAmount(int reward) { + int 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 double calculateExperience(int reward, int amount) { + double experience = resource.getExperience(); + + if(player.getLocation().getRegionId() == 10300){ + return 1.0; + } + + // Bark + if (reward == 3239) { + // If we receive the item, give the full experience points otherwise give the base amount + if (amount >= 1) { + experience = 275.2; + } else { + amount = 1; + } + } + + // 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; + } + + private int calculateReward(int reward) { + return reward; + } + + /** + * Checks if the player gets rewarded. + * + * @return {@code True} if so. + */ + private boolean checkReward(SkillingTool tool) { + int skill = Skills.WOODCUTTING; + int level = player.getSkills().getLevel(skill) + player.getFamiliarManager().getBoost(skill); + double hostRatio = RandomFunction.randomDouble(100.0); + double lowMod = tool == SkillingTool.BLACK_AXE ? resource.tierModLow / 2 : resource.tierModLow; + double low = resource.baseLow + (tool.ordinal() * lowMod); + double highMod = tool == SkillingTool.BLACK_AXE ? resource.tierModHigh / 2 : resource.tierModHigh; + double high = resource.baseHigh + (tool.ordinal() * highMod); + double clientRatio = RandomFunction.getSkillSuccessChance(low,high,level); + return hostRatio < clientRatio; + } +} 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/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/java/core/game/node/entity/skill/herblore/FinishedPotion.java b/Server/src/main/content/global/skill/herblore/FinishedPotion.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotion.java rename to Server/src/main/content/global/skill/herblore/FinishedPotion.java index a38722749..e78d80432 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotion.java +++ b/Server/src/main/content/global/skill/herblore/FinishedPotion.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.herblore; +package content.global.skill.herblore; import core.game.node.item.Item; import org.rs09.consts.Items; @@ -13,7 +13,7 @@ public enum FinishedPotion { STRENGTH_POTION(UnfinishedPotion.TARROMIN, new Item(225), 12, 50, new Item(115)), RESTORE_POTION(UnfinishedPotion.HARRALANDER, new Item(223), 22, 62.5, new Item(127)), ENERGY_POTION(UnfinishedPotion.HARRALANDER, new Item(1975), 26, 67.5, new Item(3010)), - DEFENCE_POTION(UnfinishedPotion.RANARR, new Item(6814), 30, 45, new Item(133)), + DEFENCE_POTION(UnfinishedPotion.RANARR, new Item(239), 30, 45, new Item(133)), AGILITY_POTION(UnfinishedPotion.TOADFLAX, new Item(2152), 34, 80, new Item(3034)), COMBAT_POTION(UnfinishedPotion.HARRALANDER, new Item(9736), 36, 84, new Item(9741)), PRAYER_POTION(UnfinishedPotion.RANARR, new Item(231), 38, 87.5, new Item(139)), diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotionPlugin.java b/Server/src/main/content/global/skill/herblore/FinishedPotionPlugin.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotionPlugin.java rename to Server/src/main/content/global/skill/herblore/FinishedPotionPlugin.java index 9e6ca3ab7..61b871747 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/FinishedPotionPlugin.java +++ b/Server/src/main/content/global/skill/herblore/FinishedPotionPlugin.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.herblore; +package content.global.skill.herblore; import core.plugin.Initializable; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; +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; diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/GenericPotion.java b/Server/src/main/content/global/skill/herblore/GenericPotion.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/herblore/GenericPotion.java rename to Server/src/main/content/global/skill/herblore/GenericPotion.java index c3d193e42..3f84a0be9 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/GenericPotion.java +++ b/Server/src/main/content/global/skill/herblore/GenericPotion.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.herblore; +package content.global.skill.herblore; import core.game.node.item.Item; diff --git a/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt b/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt new file mode 100644 index 000000000..b87ead3f4 --- /dev/null +++ b/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt @@ -0,0 +1,100 @@ +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 core.game.node.item.Item +import core.game.world.update.flag.context.Animation +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 org.rs09.consts.Items +import kotlin.math.ceil +import kotlin.math.roundToInt + +/** + * plugin used to handle the grinding of an item. + * @author 'Ceikry + */ +@Initializable +class GrindItemPlugin : UseWithHandler(233) { + override fun newInstance(arg: Any?): Plugin? { + for (grind in GrindingItem.values()) { + for (i in grind.items) { + addHandler(i.id, ITEM_TYPE, this) + } + } + return this + } + + 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) { + override fun create(amount: Int, index: Int) { + player.pulseManager.run(object : SkillPulse(player,event.usedItem) { + var amt = 0 + init { + amt = amount + 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) + } + override fun checkRequirements(): Boolean { + return true + } + + override fun animate() { + player.animator.animate(ANIMATION) + } + + override fun reward(): Boolean { + 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 + } + }) + + } + + override fun getAll(index: Int): Int { + return amountInInventory(player, event.usedItem.id) + } + } + handler.open() + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(event.player, 309, 2, 210, 15)) + return true + } + + companion object { + /** + * 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 new file mode 100644 index 000000000..83b1f3efe --- /dev/null +++ b/Server/src/main/content/global/skill/herblore/GrindingItem.java @@ -0,0 +1,80 @@ +package content.global.skill.herblore; + +import org.rs09.consts.Items; +import core.game.node.item.Item; + +/** + * Represents an item that will can be ground by pestle and mortar. + * @author Vexia + */ +public enum GrindingItem { + 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)); + + /** + * Represents the item to grind. + */ + private final Item[] items; + + /** + * Represents the product item. + */ + private final Item product; + + /** + * Constructs a new {@code GrindingItem} {@code Object}. + * @param items the items. + * @param product the product. + */ + GrindingItem(final Item[] items, final Item product) { + this.items = items; + this.product = product; + } + + /** + * Gets the item. + * @return The item. + */ + public Item[] getItems() { + return items; + } + + /** + * Gets the product. + * @return The product. + */ + public Item getProduct() { + return product; + } + + /** + * Gets the grinding item. + * @param item the item. + * @return the item. + */ + public static GrindingItem forItem(final Item item) { + for (GrindingItem g : values()) { + for (Item i : g.getItems()) { + if (i.getId() == item.getId()) { + return g; + } + } + } + return null; + } +} 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/java/core/game/node/entity/skill/herblore/HerbTarPlugin.java b/Server/src/main/content/global/skill/herblore/HerbTarPlugin.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/herblore/HerbTarPlugin.java rename to Server/src/main/content/global/skill/herblore/HerbTarPlugin.java index c7e8f66b1..fe60ca43a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/HerbTarPlugin.java +++ b/Server/src/main/content/global/skill/herblore/HerbTarPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.herblore; +package content.global.skill.herblore; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -7,7 +7,7 @@ import core.net.packet.context.ChildPositionContext; import core.net.packet.out.RepositionChild; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.content.dialogue.SkillDialogueHandler; +import core.game.dialogue.SkillDialogueHandler; /** * Represents the plugin used to create a herb tar. diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/HerbTarPulse.java b/Server/src/main/content/global/skill/herblore/HerbTarPulse.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/herblore/HerbTarPulse.java rename to Server/src/main/content/global/skill/herblore/HerbTarPulse.java index 87c81cecd..247f69f62 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/HerbTarPulse.java +++ b/Server/src/main/content/global/skill/herblore/HerbTarPulse.java @@ -1,10 +1,11 @@ -package core.game.node.entity.skill.herblore; +package content.global.skill.herblore; 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 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/java/core/game/node/entity/skill/herblore/HerblorePulse.java b/Server/src/main/content/global/skill/herblore/HerblorePulse.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/skill/herblore/HerblorePulse.java rename to Server/src/main/content/global/skill/herblore/HerblorePulse.java index de9805852..0f859ad23 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/HerblorePulse.java +++ b/Server/src/main/content/global/skill/herblore/HerblorePulse.java @@ -1,141 +1,157 @@ -package core.game.node.entity.skill.herblore; - -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; - -/** - * Represents the skill pulse used to handle the creating of potions. - * @author 'Vexia - */ -public final class HerblorePulse extends SkillPulse { - - /** - * Represents the vial of water item. - */ - public static final Item VIAL_OF_WATER = new Item(227); - - /** - * Represents the coonut milk item. - */ - public static final Item COCONUT_MILK = new Item(5935); - - /** - * Represents the animation to use when making a potion. - */ - private static final Animation ANIMATION = new Animation(363); - - /** - * Represents the generic potion. - */ - private final GenericPotion potion; - - /** - * Represents the amount to make. - */ - private int amount; - - /** - * Represents the initial amount to make. - */ - private int initialAmount; - - /** - * Represents the cycles. - */ - private int cycles; - - /** - * Constructs a new {@code HerblorePulse} {@code Object}. - * @param player the player. - * @param node the node. - */ - public HerblorePulse(final Player player, final Item node, final int amount, final GenericPotion potion) { - super(player, node); - this.amount = amount; - this.initialAmount = amount; - this.potion = potion; - } - - @Override - public boolean checkRequirements() { - if (!player.getQuestRepository().isComplete("Druidic Ritual")) { - player.getPacketDispatch().sendMessage("You must complete the Druidic Ritual quest before you can use Herblore."); - return false; - } - if (player.getSkills().getLevel(Skills.HERBLORE) < potion.getLevel()) { - player.getPacketDispatch().sendMessage("You need a Herblore level of at least " + potion.getLevel() + " in order to do this."); - return false; - } - if (!player.getInventory().containsItem(potion.getBase()) || !player.getInventory().containsItem(potion.getIngredient())) { - return false; - } - return true; - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (potion.getBase().getId() == VIAL_OF_WATER.getId()) { - if (initialAmount == 1 && getDelay() == 1) { - player.animate(ANIMATION); - setDelay(3); - return false; - } - handleUnfinished(); - } else { - if (initialAmount == 1 && getDelay() == 1) { - player.animate(ANIMATION); - setDelay(3); - return false; - } - if (getDelay() == 1) { - setDelay(3); - player.animate(ANIMATION); - return false; - } - handleFinished(); - } - amount--; - return amount == 0; - } - - /** - * Method used to handle the potion making of an unf-potion. - */ - public void handleUnfinished() { - if (cycles == 0) { - player.animate(ANIMATION); - } - if ((player.getInventory().containsItem(potion.getBase()) && player.getInventory().containsItem(potion.getIngredient())) && player.getInventory().remove(potion.getBase(), potion.getIngredient())) { - 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); - if (cycles++ == 3) { - player.animate(ANIMATION); - cycles = 0; - } - } - } - - /** - * Method used to handle the finished potion making. - */ - 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(); - 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); - player.animate(ANIMATION); - } - } -} +package content.global.skill.herblore; + +import core.game.node.entity.skill.SkillPulse; +import core.game.node.entity.skill.Skills; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.game.world.update.flag.context.Animation; +import core.tools.StringUtils; +import content.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. + * @author 'Vexia + */ +public final class HerblorePulse extends SkillPulse { + + /** + * Represents the vial of water item. + */ + public static final Item VIAL_OF_WATER = new Item(227); + + /** + * Represents the coonut milk item. + */ + public static final Item COCONUT_MILK = new Item(5935); + + /** + * Represents the animation to use when making a potion. + */ + private static final Animation ANIMATION = new Animation(363); + + /** + * Represents the generic potion. + */ + private final GenericPotion potion; + + /** + * Represents the amount to make. + */ + private int amount; + + /** + * Represents the initial amount to make. + */ + private int initialAmount; + + /** + * Represents the cycles. + */ + private int cycles; + + /** + * Constructs a new {@code HerblorePulse} {@code Object}. + * @param player the player. + * @param node the node. + */ + public HerblorePulse(final Player player, final Item node, final int amount, final GenericPotion potion) { + super(player, node); + this.amount = amount; + this.initialAmount = amount; + this.potion = potion; + } + + @Override + public boolean checkRequirements() { + if (!player.getQuestRepository().isComplete(Quests.DRUIDIC_RITUAL)) { + player.getPacketDispatch().sendMessage("You must complete the Druidic Ritual quest before you can use Herblore."); + return false; + } + if (player.getSkills().getLevel(Skills.HERBLORE) < potion.getLevel()) { + player.getPacketDispatch().sendMessage("You need a Herblore level of at least " + potion.getLevel() + " in order to do this."); + return false; + } + if (!player.getInventory().containsItem(potion.getBase()) || !player.getInventory().containsItem(potion.getIngredient())) { + return false; + } + return true; + } + + @Override + public void animate() { + } + + @Override + public boolean reward() { + if (potion.getBase().getId() == VIAL_OF_WATER.getId()) { + if (initialAmount == 1 && getDelay() == 1) { + player.animate(ANIMATION); + setDelay(3); + return false; + } + handleUnfinished(); + } else { + if (initialAmount == 1 && getDelay() == 1) { + player.animate(ANIMATION); + setDelay(3); + return false; + } + if (getDelay() == 1) { + setDelay(3); + player.animate(ANIMATION); + return false; + } + handleFinished(); + } + amount--; + return amount == 0; + } + + /** + * Method used to handle the potion making of an unf-potion. + */ + public void handleUnfinished() { + if (cycles == 0) { + player.animate(ANIMATION); + } + if ((player.getInventory().containsItem(potion.getBase()) && player.getInventory().containsItem(potion.getIngredient())) && player.getInventory().remove(potion.getBase(), potion.getIngredient())) { + 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."); + playAudio(player, Sounds.GRIND_2608); + if (cycles++ == 3) { + player.animate(ANIMATION); + cycles = 0; + } + } + } + + /** + * Method used to handle the finished potion making. + */ + public void handleFinished() { + if ((player.getInventory().containsItem(potion.getBase()) && player.getInventory().containsItem(potion.getIngredient())) && player.getInventory().remove(potion.getBase(), potion.getIngredient())) { + 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."); + playAudio(player, Sounds.GRIND_2608); + player.animate(ANIMATION); + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/Herbs.java b/Server/src/main/content/global/skill/herblore/Herbs.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/herblore/Herbs.java rename to Server/src/main/content/global/skill/herblore/Herbs.java index ff7a00c83..4226fb436 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/Herbs.java +++ b/Server/src/main/content/global/skill/herblore/Herbs.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.herblore; +package content.global.skill.herblore; import core.game.node.item.Item; @@ -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/java/core/game/node/entity/skill/herblore/SwampToadPlugin.java b/Server/src/main/content/global/skill/herblore/SwampToadPlugin.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/herblore/SwampToadPlugin.java rename to Server/src/main/content/global/skill/herblore/SwampToadPlugin.java index cba7e80a4..472846a25 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/SwampToadPlugin.java +++ b/Server/src/main/content/global/skill/herblore/SwampToadPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.herblore; +package content.global.skill.herblore; import core.cache.def.impl.ItemDefinition; diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/Tars.java b/Server/src/main/content/global/skill/herblore/Tars.java similarity index 77% rename from Server/src/main/java/core/game/node/entity/skill/herblore/Tars.java rename to Server/src/main/content/global/skill/herblore/Tars.java index 1ac6e49cf..c63d783bc 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/Tars.java +++ b/Server/src/main/content/global/skill/herblore/Tars.java @@ -1,13 +1,18 @@ -package core.game.node.entity.skill.herblore; +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 new file mode 100644 index 000000000..aa6e48d27 --- /dev/null +++ b/Server/src/main/content/global/skill/herblore/UnfinishedPotion.java @@ -0,0 +1,121 @@ +package content.global.skill.herblore; + +import core.game.node.item.Item; + +/** + * Represents an unfinished potion to make. + * @author 'Vexia + */ +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)), + TOADFLAX(Herbs.TOADFLAX.getProduct(), 34, new Item(3002)), + SPIRIT_WEED(Herbs.SPIRIT_WEED.getProduct(), 40, new Item(12181)), + IRIT(Herbs.IRIT.getProduct(), 45, new Item(101)), + AVANTOE(Herbs.AVANTOE.getProduct(), 50, new Item(103)), + KWUARM(Herbs.KWUARM.getProduct(), 55, new Item(105)), + SNAPDRAGON(Herbs.SNAPDRAGON.getProduct(), 63, new Item(3004)), + CADANTINE(Herbs.CADANTINE.getProduct(), 66, new Item(107)), + LANTADYME(Herbs.LANTADYME.getProduct(), 69, new Item(2483)), + DWARF_WEED(Herbs.DWARF_WEED.getProduct(), 72, new Item(109)), + TORSTOL(Herbs.TORSTOL.getProduct(), 75, new Item(111)), + STRONG_WEAPON_POISON(HerblorePulse.COCONUT_MILK, new Item(6016), 73, new Item(5936)), + SUPER_STRONG_WEAPON_POISON(HerblorePulse.COCONUT_MILK, new Item(2398), 82, new Item(5939)), + STRONG_ANTIPOISON(HerblorePulse.COCONUT_MILK, Herbs.TOADFLAX.getProduct(), 68, new Item(5942)), + SUPER_STRONG_ANTIPOISON(HerblorePulse.COCONUT_MILK, Herbs.IRIT.getProduct(), 79, new Item(5951)); + + /** + * Represents the base item. + */ + private final Item base; + + /** + * Represents the ingredient needed to make this unf-potion. + */ + private final Item ingredient; + + /** + * Represents the level required to make this unf-potion. + */ + private final int level; + + /** + * Represents the potion product. + */ + private final Item potion; + + /** + * Constructs a new {@code UnfinishedPotion} {@code Object}. + * @param ingredient the ingredient. + * @param level the level. + * @param potion the potion. + */ + UnfinishedPotion(final Item base, final Item ingredient, final int level, final Item potion) { + this.base = base; + this.ingredient = ingredient; + this.level = level; + this.potion = potion; + } + + /** + * Constructs a new {@code UnfinishedPotion} {@code Object}. + * @param ingredient the ingredient. + * @param level the level. + * @param potion the potion. + */ + UnfinishedPotion(final Item ingredient, final int level, final Item potion) { + this(HerblorePulse.VIAL_OF_WATER, ingredient, level, potion); + } + + /** + * Gets the ingredient. + * @return The ingredient. + */ + public Item getIngredient() { + return ingredient; + } + + /** + * Gets the level. + * @return The level. + */ + public int getLevel() { + return level; + } + + /** + * Gets the potion. + * @return The potion. + */ + public Item getPotion() { + return potion; + } + + /** + * Gets the base. + * @return The base. + */ + public Item getBase() { + return base; + } + + /** + * Gets the unf-potion. + * @param item the item. + * @paramt the base item. + * @return the unf-potion. + */ + public static UnfinishedPotion forItem(final Item item, final Item base) { + for (UnfinishedPotion potion : values()) { + if ((potion.getIngredient().getId() == item.getId() || potion.getIngredient().getId() == base.getId()) && (item.getId() == potion.getBase().getId() || base.getId() == potion.getBase().getId())) { + return potion; + } + } + return null; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/UnfinishedPotionPlugin.java b/Server/src/main/content/global/skill/herblore/UnfinishedPotionPlugin.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/herblore/UnfinishedPotionPlugin.java rename to Server/src/main/content/global/skill/herblore/UnfinishedPotionPlugin.java index 6ab24f85e..7504b00f8 100644 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/UnfinishedPotionPlugin.java +++ b/Server/src/main/content/global/skill/herblore/UnfinishedPotionPlugin.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.herblore; +package content.global.skill.herblore; import core.plugin.Initializable; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; +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; diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/BoxTrapNode.java b/Server/src/main/content/global/skill/hunter/BoxTrapNode.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/hunter/BoxTrapNode.java rename to Server/src/main/content/global/skill/hunter/BoxTrapNode.java index e6acb0e32..129702015 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/BoxTrapNode.java +++ b/Server/src/main/content/global/skill/hunter/BoxTrapNode.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; @@ -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/java/core/game/node/entity/skill/hunter/DeadfallSetting.java b/Server/src/main/content/global/skill/hunter/DeadfallSetting.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/hunter/DeadfallSetting.java rename to Server/src/main/content/global/skill/hunter/DeadfallSetting.java index 97867a60a..56202d3a9 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/DeadfallSetting.java +++ b/Server/src/main/content/global/skill/hunter/DeadfallSetting.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; -import core.game.node.entity.skill.firemaking.Log; +import content.global.skill.firemaking.Log; import core.game.node.Node; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -129,7 +129,7 @@ public final class DeadfallSetting extends TrapSetting { @Override public boolean exceedsLimit(Player player) { - return player.getHunterManager().getTrapAmount() > 0 ? true : false; + return HunterManager.getInstance(player).getTrapAmount() > 0; } /** diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/FalconryActivityPlugin.java b/Server/src/main/content/global/skill/hunter/FalconryActivityPlugin.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/hunter/FalconryActivityPlugin.java rename to Server/src/main/content/global/skill/hunter/FalconryActivityPlugin.java index af1ea2c16..a3e8c2c87 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/FalconryActivityPlugin.java +++ b/Server/src/main/content/global/skill/hunter/FalconryActivityPlugin.java @@ -1,11 +1,11 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; +import content.global.skill.hunter.falconry.FalconryCatchPulse; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; -import core.game.content.activity.ActivityPlugin; +import core.game.activity.ActivityPlugin; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.hunter.falconry.FalconCatch; -import core.game.node.entity.skill.hunter.falconry.FalconryCatchPulse; +import content.global.skill.hunter.falconry.FalconCatch; import core.game.interaction.Option; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -17,7 +17,7 @@ import core.game.node.item.Item; import core.game.world.map.Location; import core.game.world.map.zone.ZoneBorders; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the activity used during falconry practice. @@ -99,7 +99,7 @@ public final class FalconryActivityPlugin extends ActivityPlugin { @Override public void register() { - PluginManager.definePlugin(new FalconryPlugin()); + ClassScanner.definePlugin(new FalconryPlugin()); } /** @@ -130,7 +130,7 @@ public final class FalconryActivityPlugin extends ActivityPlugin { for (FalconCatch falconCatch : FalconCatch.values()) { NPCDefinition.forId(falconCatch.getNpc()).getHandlers().put("option:catch", this); } - PluginManager.definePlugin(new Plugin() { + ClassScanner.definePlugin(new Plugin() { @Override public Plugin newInstance(Object arg) throws Throwable { diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/HunterGear.java b/Server/src/main/content/global/skill/hunter/HunterGear.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/hunter/HunterGear.java rename to Server/src/main/content/global/skill/hunter/HunterGear.java index 361e1d1b9..ac845fd41 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/HunterGear.java +++ b/Server/src/main/content/global/skill/hunter/HunterGear.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/HunterManager.java b/Server/src/main/content/global/skill/hunter/HunterManager.java similarity index 77% rename from Server/src/main/java/core/game/node/entity/skill/hunter/HunterManager.java rename to Server/src/main/content/global/skill/hunter/HunterManager.java index 9b343f988..a026eb043 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/HunterManager.java +++ b/Server/src/main/content/global/skill/hunter/HunterManager.java @@ -1,9 +1,16 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; +import core.api.LoginListener; +import core.api.LogoutListener; +import core.game.event.EventHook; +import core.game.event.TickEvent; +import core.game.node.entity.Entity; import core.game.node.entity.skill.Skills; import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; +import org.jetbrains.annotations.NotNull; +import core.api.Event; import java.util.ArrayList; import java.util.Iterator; @@ -13,7 +20,7 @@ import java.util.List; * Manages the players hunter state. * @author Vexia */ -public final class HunterManager { +public final class HunterManager implements LoginListener, LogoutListener, EventHook { /** * The list of active traps. @@ -33,10 +40,33 @@ public final class HunterManager { this.player = player; } - /** - * Calles every game pulse. - */ - public void pulse() { + public HunterManager() { + this.player = null; + } + + @Override + public void login(@NotNull Player player) { + HunterManager instance = new HunterManager(player); + player.hook(Event.getTick(), instance); + player.setAttribute("hunter-manager", instance); + } + + @Override + public void logout(@NotNull Player player) { + HunterManager instance = getInstance(player); + if (instance == null) return; + Iterator iterator = instance.traps.iterator(); + TrapWrapper wrapper = null; + while (iterator.hasNext()) { + wrapper = iterator.next(); + if (wrapper.getType().getSettings().clear(wrapper, 0)) { + iterator.remove(); + } + } + } + + @Override + public void process(@NotNull Entity entity, @NotNull TickEvent event) { if (traps.size() == 0) { return; } @@ -50,20 +80,6 @@ public final class HunterManager { } } - /** - * Called when the player logs out. - */ - public void logout() { - Iterator iterator = traps.iterator(); - TrapWrapper wrapper = null; - while (iterator.hasNext()) { - wrapper = iterator.next(); - if (wrapper.getType().getSettings().clear(wrapper, 0)) { - iterator.remove(); - } - } - } - /** * Register a hunting trap. * @param trap the trap. @@ -179,4 +195,8 @@ public final class HunterManager { return traps; } + public static HunterManager getInstance(Player player) + { + return player.getAttribute("hunter-manager"); + } } diff --git a/Server/src/main/content/global/skill/hunter/HunterNPC.java b/Server/src/main/content/global/skill/hunter/HunterNPC.java new file mode 100644 index 000000000..725cdb140 --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/HunterNPC.java @@ -0,0 +1,161 @@ +package content.global.skill.hunter; + +import core.api.ContentAPIKt; +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.player.link.TeleportManager; +import org.rs09.consts.NPCs; +import core.game.world.GameWorld; +import core.game.world.map.Location; +import core.tools.RandomFunction; + +import java.util.ArrayList; +import java.util.List; + +import static core.api.ContentAPIKt.getPathableRandomLocalCoordinate; +import static core.api.ContentAPIKt.sendGraphics; + +/** + * Handles a hunter npc. + * @author Vexia + */ +public final class HunterNPC extends AbstractNPC { + + private static final int IMP_TELEPORT_CHANCE_ON_HIT = 10; // 1/10 + private static final int IMP_TELEPORT_CHANCE_ON_TICK = 1000; // 1/75 + + /** + * The trap type. + */ + private final Traps trap; + + /** + * The trap type node. + */ + private final TrapNode type; + + /** + * Constructs a new {@code HunterNPC} {@code Object}. + */ + public HunterNPC() { + this(0, null, null, null); + this.setWalks(true); + } + + /** + * Constructs a new {@code HunterNPC} {@code Object}. + * @param id the id. + * @param location the location. + * @param trap the trap. + * @param type the type. + */ + public HunterNPC(int id, Location location, Traps trap, TrapNode type) { + super(id, location); + this.trap = trap; + this.type = type; + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + Object[] data = Traps.getNode(id); + return new HunterNPC(id, location, (Traps) data[0], (TrapNode) data[1]); + } + + @Override + public void updateLocation(Location last) { + final TrapWrapper wrapper = trap.getByHook(getLocation()); + if (wrapper != null) { + wrapper.getType().catchNpc(wrapper, this); + } + } + + @Override + protected Location getMovementDestination() { + if (trap.getHooks().size() == 0 || RandomFunction.random(170) > 5) { + return super.getMovementDestination(); + } + TrapHook hook = trap.getHooks().get(RandomFunction.random(trap.getHooks().size())); + if (hook == null || !type.canCatch(hook.getWrapper(), this)) { + return super.getMovementDestination(); + } + Location destination = hook.getChanceLocation(); + return destination != null && destination.getDistance(getLocation()) <= 24 ? destination : super.getMovementDestination(); + } + + @Override + public void handleDrops(Player p, Entity killer) { + int ticks = getAttribute("hunter", 0); + if (ticks < GameWorld.getTicks()) { + super.handleDrops(p, killer); + } + } + + @Override + public int[] getIds() { + List ids = new ArrayList<>(10); + for (Traps t : Traps.values()) { + for (TrapNode node : t.getNodes()) { + for (int id : node.getNpcIds()) { + ids.add(id); + } + } + } + int[] array = new int[ids.size()]; + for (int i = 0; i < array.length; i++) { + array[i] = ids.get(i); + } + return array; + } + + @Override + public void checkImpact(BattleState state) { + super.checkImpact(state); + + if (this.getId() == NPCs.IMP_708 || this.getId() == NPCs.IMP_709 || this.getId() == NPCs.IMP_1531) { + if (RandomFunction.roll(IMP_TELEPORT_CHANCE_ON_HIT)) { + getRandomLocAndTeleport(); + } + } + } + + @Override + public void tick() { + super.tick(); + + if (this.getId() == NPCs.IMP_708 || this.getId() == NPCs.IMP_709 || this.getId() == NPCs.IMP_1531) { + if (RandomFunction.roll(IMP_TELEPORT_CHANCE_ON_TICK)) { + getRandomLocAndTeleport(); + } + } + } + + /** + * Gets the type. + * @return The type. + */ + public TrapNode getType() { + return type; + } + + /** + * Gets the trap. + * @return The trap. + */ + public Traps getTrap() { + return trap; + } + + /** + * Used to teleport imps + */ + private void getRandomLocAndTeleport() { + Location teleportLocation = getPathableRandomLocalCoordinate(this, walkRadius, getProperties().getSpawnLocation(), 3); + + if (getLocation() != teleportLocation) { + sendGraphics(1119, getLocation()); + ContentAPIKt.teleport(this, teleportLocation, TeleportManager.TeleportType.INSTANT); + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/HunterPlugin.java b/Server/src/main/content/global/skill/hunter/HunterPlugin.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/skill/hunter/HunterPlugin.java rename to Server/src/main/content/global/skill/hunter/HunterPlugin.java index 1bcc4e555..b434c2287 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/HunterPlugin.java +++ b/Server/src/main/content/global/skill/hunter/HunterPlugin.java @@ -1,14 +1,14 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; +import content.global.skill.hunter.bnet.BNetNode; +import content.global.skill.hunter.bnet.BNetTypes; +import content.global.skill.hunter.bnet.ImplingNode; import core.cache.def.Definition; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; -import core.game.node.entity.skill.hunter.NetTrapSetting.NetTrap; -import core.game.node.entity.skill.hunter.bnet.BNetNode; -import core.game.node.entity.skill.hunter.bnet.BNetTypes; -import core.game.node.entity.skill.hunter.bnet.ImplingNode; +import content.global.skill.hunter.NetTrapSetting.NetTrap; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -21,7 +21,7 @@ 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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import java.util.ArrayList; import java.util.List; @@ -69,11 +69,11 @@ public final class HunterPlugin extends OptionHandler { SceneryDefinition.forId(trap.getFailed()).getHandlers().put("option:investigate", this); SceneryDefinition.forId(trap.getNet()).getHandlers().put("option:investigate", this); } - PluginManager.definePlugin(new HunterNPC()); - PluginManager.definePlugin(new HunterNetPlugin()); - PluginManager.definePlugin(new HunterItemPlugin()); - PluginManager.definePlugin(new FalconryActivityPlugin()); - PluginManager.definePlugin(new HuntingItemUseWithHandler()); + ClassScanner.definePlugin(new HunterNPC()); + ClassScanner.definePlugin(new HunterNetPlugin()); + ClassScanner.definePlugin(new HunterItemPlugin()); + ClassScanner.definePlugin(new FalconryActivityPlugin()); + ClassScanner.definePlugin(new HuntingItemUseWithHandler()); return this; } @@ -148,11 +148,11 @@ public final class HunterPlugin extends OptionHandler { final Player player = event.getPlayer(); final Scenery object = event.getUsedWith() instanceof Scenery ? (Scenery) event.getUsedWith() : (Scenery) event.getUsed(); final Item item = event.getUsedItem(); - if (!player.getHunterManager().isOwner(object)) { + if (HunterManager.getInstance(player).isOwner(object)) { player.sendMessage("This isn't your trap!"); return true; } - final TrapWrapper wrapper = player.getHunterManager().getWrapper(object); + final TrapWrapper wrapper = HunterManager.getInstance(player).getWrapper(object); if (item.getId() == 594) { wrapper.smoke(); } else { @@ -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/java/core/game/node/entity/skill/hunter/ImpBoxPlugin.java b/Server/src/main/content/global/skill/hunter/ImpBoxPlugin.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/hunter/ImpBoxPlugin.java rename to Server/src/main/content/global/skill/hunter/ImpBoxPlugin.java index cc737d922..800e342eb 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/ImpBoxPlugin.java +++ b/Server/src/main/content/global/skill/hunter/ImpBoxPlugin.java @@ -1,13 +1,13 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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.player.Player; @@ -16,7 +16,7 @@ import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; import core.net.packet.out.ContainerPacket; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; /** @@ -33,12 +33,12 @@ public class ImpBoxPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new ImpInterfaceHandler(null)); + ClassScanner.definePlugin(new ImpInterfaceHandler(null)); for (int id : IDS) { ItemDefinition.forId(id).getHandlers().put("option:bank", this); ItemDefinition.forId(id).getHandlers().put("option:talk-to", this); } - PluginManager.definePlugin(new ImpBoxDialogue()); + ClassScanner.definePlugin(new ImpBoxDialogue()); return this; } diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/MagicBoxSetting.java b/Server/src/main/content/global/skill/hunter/MagicBoxSetting.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/hunter/MagicBoxSetting.java rename to Server/src/main/content/global/skill/hunter/MagicBoxSetting.java index d721700dc..199206040 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/MagicBoxSetting.java +++ b/Server/src/main/content/global/skill/hunter/MagicBoxSetting.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; 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/java/core/game/node/entity/skill/hunter/NetTrapSetting.java b/Server/src/main/content/global/skill/hunter/NetTrapSetting.java similarity index 99% rename from Server/src/main/java/core/game/node/entity/skill/hunter/NetTrapSetting.java rename to Server/src/main/content/global/skill/hunter/NetTrapSetting.java index 157fe9e24..415f57c22 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/NetTrapSetting.java +++ b/Server/src/main/content/global/skill/hunter/NetTrapSetting.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; import core.game.node.Node; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapCreatePulse.java b/Server/src/main/content/global/skill/hunter/TrapCreatePulse.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/skill/hunter/TrapCreatePulse.java rename to Server/src/main/content/global/skill/hunter/TrapCreatePulse.java index 6d308f379..dddc84e4d 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapCreatePulse.java +++ b/Server/src/main/content/global/skill/hunter/TrapCreatePulse.java @@ -1,8 +1,9 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; import core.game.node.entity.skill.SkillPulse; import core.game.node.Node; import core.game.node.entity.player.Player; +import core.game.node.entity.skill.Skills; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; @@ -10,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. @@ -37,6 +41,8 @@ public final class TrapCreatePulse extends SkillPulse { */ private int ticks; + private final HunterManager instance; + /** * Constructs a new {@code TrapCreatePulse} {@code Object}. * @param player the player. @@ -47,15 +53,36 @@ public final class TrapCreatePulse extends SkillPulse { super(player, node); 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 public boolean checkRequirements() { - if (player.getHunterManager().getStaticLevel() < trap.getSettings().getLevel()) { + if (player.skills.getStaticLevel(Skills.HUNTER) < trap.getSettings().getLevel()) { player.sendMessage("You need a Hunter level of at least " + trap.getSettings().getLevel() + " in order to setup a " + node.getName().toLowerCase() + "."); return false; } - if (player.getHunterManager().exceedsTrapLimit(trap)) { + if (instance.exceedsTrapLimit(trap)) { player.sendMessage(trap.getSettings().getLimitMessage(player)); return false; } @@ -94,7 +121,7 @@ public final class TrapCreatePulse extends SkillPulse { SceneryBuilder.remove(node.asScenery()); } object = SceneryBuilder.add(object); - player.getHunterManager().register(trap, node, object); + instance.register(trap, node, object); return true; } diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapDismantlePulse.java b/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/skill/hunter/TrapDismantlePulse.java rename to Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java index 1773e3e0b..8d3493c28 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapDismantlePulse.java +++ b/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java @@ -1,10 +1,12 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; -import core.game.content.global.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. @@ -27,6 +29,8 @@ public final class TrapDismantlePulse extends SkillPulse { */ private int ticks; + private final HunterManager instance; + /** * Constructs a new {@code TrapDismantlePulse} {@code Object}. * @param player the player. @@ -36,11 +40,33 @@ public final class TrapDismantlePulse extends SkillPulse { super(player, node); 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 public boolean checkRequirements() { - if (wrapper == null || !player.getHunterManager().isOwner(node)) { + if (wrapper == null || !instance.isOwner(node)) { player.sendMessage("This isn't your trap!"); return false; } @@ -66,15 +92,8 @@ public final class TrapDismantlePulse extends SkillPulse { return false; } if (wrapper.getType().getSettings().clear(wrapper, 1)) { - player.getHunterManager().deregister(wrapper); + 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/java/core/game/node/entity/skill/hunter/TrapHook.java b/Server/src/main/content/global/skill/hunter/TrapHook.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/hunter/TrapHook.java rename to Server/src/main/content/global/skill/hunter/TrapHook.java index 18a04d9fe..61e484058 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapHook.java +++ b/Server/src/main/content/global/skill/hunter/TrapHook.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.tools.RandomFunction; diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapNode.java b/Server/src/main/content/global/skill/hunter/TrapNode.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/hunter/TrapNode.java rename to Server/src/main/content/global/skill/hunter/TrapNode.java index 2ed3e6fb9..d2cc48e01 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapNode.java +++ b/Server/src/main/content/global/skill/hunter/TrapNode.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; -import core.game.content.global.SkillingPets; 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; /** @@ -63,7 +63,7 @@ public class TrapNode { if (wrapper.isCaught() || wrapper.isBusy() || wrapper.isFailed()) { return false; } - return player.getHunterManager().getStaticLevel() >= level && !npc.isInvisible(); + return player.skills.getStaticLevel(Skills.HUNTER) >= level && !npc.isInvisible(); } /** @@ -122,8 +122,4 @@ public class TrapNode { return objectIds; } - public SkillingPets getPet() { - return null; - } - } diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapSetting.java b/Server/src/main/content/global/skill/hunter/TrapSetting.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/skill/hunter/TrapSetting.java rename to Server/src/main/content/global/skill/hunter/TrapSetting.java index c21cf0fda..ec0f87ba3 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapSetting.java +++ b/Server/src/main/content/global/skill/hunter/TrapSetting.java @@ -1,20 +1,24 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; 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.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.system.task.Pulse; -import rs09.game.world.GameWorld; +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. @@ -232,11 +236,12 @@ public class TrapSetting { * @param object the object. */ public void investigate(Player player, Scenery object) { - if (!player.getHunterManager().isOwner(object)) { + HunterManager instance = HunterManager.getInstance(player); + if (!instance.isOwner(object)) { player.sendMessage("This isn't your trap."); return; } - TrapWrapper wrapper = player.getHunterManager().getWrapper(object); + TrapWrapper wrapper = instance.getWrapper(object); player.sendMessage("This trap " + (wrapper.isSmoked() ? "has" : "hasn't") + " been smoked."); } @@ -304,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(); @@ -322,7 +343,7 @@ public class TrapSetting { * @return {@code True} if so. */ public boolean isSuccess(Player player, final TrapNode node) { - double level = player.getHunterManager().getStaticLevel(); + double level = player.skills.getStaticLevel(Skills.HUNTER); double req = node.getLevel(); double successChance = Math.ceil((level * 50 - req * 17) / req / 3 * 4); int roll = RandomFunction.random(99); @@ -474,7 +495,8 @@ public class TrapSetting { * @return the message. */ public String getLimitMessage(Player player) { - return "You don't have a high enough Hunter level to set up more than " + player.getHunterManager().getMaximumTraps() + " trap" + (player.getHunterManager().getMaximumTraps() == 1 ? "." : "s."); + HunterManager instance = HunterManager.getInstance(player); + return "You don't have a high enough Hunter level to set up more than " + instance.getMaximumTraps() + " trap" + (instance.getMaximumTraps() == 1 ? "." : "s."); } /** diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapWrapper.java b/Server/src/main/content/global/skill/hunter/TrapWrapper.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/hunter/TrapWrapper.java rename to Server/src/main/content/global/skill/hunter/TrapWrapper.java index ccc0a930f..4cbe32987 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/TrapWrapper.java +++ b/Server/src/main/content/global/skill/hunter/TrapWrapper.java @@ -1,11 +1,12 @@ -package core.game.node.entity.skill.hunter; +package content.global.skill.hunter; -import core.game.node.entity.skill.hunter.NetTrapSetting.NetTrap; +import core.game.node.entity.skill.Skills; +import content.global.skill.hunter.NetTrapSetting.NetTrap; 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; @@ -88,6 +89,8 @@ public final class TrapWrapper { */ private int ticks; + private HunterManager instance; + /** * Constructs a new {@code TrapWrapper} {@code Object}. * @param player the player. @@ -100,7 +103,8 @@ public final class TrapWrapper { this.object = object; this.originalId = object.getId(); this.ticks = GameWorld.getTicks() + (100); - this.object.getAttributes().setAttribute("trap-uid", player.getHunterManager().getUid()); + this.instance = HunterManager.getInstance(player); + this.object.getAttributes().setAttribute("trap-uid", instance.getUid()); } /** @@ -125,7 +129,7 @@ public final class TrapWrapper { Scenery newObject = object.transform(id); SceneryBuilder.remove(object); this.object = SceneryBuilder.add(newObject); - this.object.getAttributes().setAttribute("trap-uid", player.getHunterManager().getUid()); + this.object.getAttributes().setAttribute("trap-uid", instance.getUid()); } /** @@ -136,7 +140,7 @@ public final class TrapWrapper { player.sendMessage("This trap has already been smoked."); return; } - if (player.getHunterManager().getStaticLevel() < 39) { + if (player.skills.getStaticLevel(Skills.HUNTER) < 39) { player.sendMessage("You need a Hunter level of at least 39 to be able to smoke traps."); return; } diff --git a/Server/src/main/content/global/skill/hunter/Traps.java b/Server/src/main/content/global/skill/hunter/Traps.java new file mode 100644 index 000000000..f0cec6d54 --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/Traps.java @@ -0,0 +1,284 @@ +package content.global.skill.hunter; + +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.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 + */ +public enum Traps { + BIRD_SNARE(new TrapSetting(10006, new int[] { 19175 }, new int[] {}, "lay", 19174, Animation.create(5208), Animation.create(5207), 1), + new TrapNode(new int[] { 5073 }, 1, 34, new int[] { 19179, 19180 }, new Item[] { new Item(10088, 8), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 5075 }, 5, 48, new int[] { 19183, 19184 }, new Item[] { new Item(10090, 8), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 5076 }, 9, 61, new int[] { 19185, 19186 }, new Item[] { new Item(10091, 8), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 5074 }, 11, 64.7, new int[] { 19181, 19182 }, new Item[] { new Item(10089, 8), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 5072 }, 19, 95.2, new int[] { 19177, 19178 }, new Item[] { new Item(10087, 8), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 7031 }, 39, 167, new int[] { 28931, 28930 }, new Item[] { new Item(11525, 8), new Item(9978), new Item(526) }) { + @Override + public boolean canCatch(TrapWrapper wrapper, final NPC npc) { + return false; + } + }), + 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), // 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) })), + DEAD_FALL(new DeadfallSetting(), + new TrapNode(new int[] { 5089 }, 23, 128, new int[] { 19213, 19214, 19218 }, new Item[] { new Item(10113), new Item(526) }), + new TrapNode(new int[] { 5088 }, 33, 168, new int[] { 19211, 19212, 19217 }, new Item[] { new Item(10129), new Item(526) }), + 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 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. + */ + private final List hooks = new ArrayList<>(5); + + /** + * The trap settings. + */ + private final TrapSetting settings; + + /** + * The nodes related to the trap. + */ + private final TrapNode[] nodes; + + /** + * Constructs a new {@code TrapType} {@code Object}. + * @param settings the settings. + * @param nodes the nodes. + */ + Traps(TrapSetting settings, TrapNode... nodes) { + this.settings = settings; + this.nodes = nodes; + } + + /** + * Creates a trap instance. + * @param player the player. + */ + public void create(Player player, Node node) { + player.getPulseManager().run(new TrapCreatePulse(player, node, this)); + } + + /** + * Dismantles a trap. + * @param player the player. + * @param object the object. + */ + public void dismantle(Player player, Scenery object) { + HunterManager instance = HunterManager.getInstance(player); + + if (!instance.isOwner(object)) { + player.sendMessage("This isn't your trap!"); + return; + } + if (instance.getWrapper(object) == null) { + log(this.getClass(), Log.ERR, "NO WRAPPER (HUNTER DISMANTLE)"); + return; + } + player.faceLocation(object.getLocation()); + player.getPulseManager().run(new TrapDismantlePulse(player, object, instance.getWrapper(object))); + } + + /** + * Investigates the trap. + * @param player the player. + * @param object the object. + */ + public void investigate(Player player, Scenery object) { + getSettings().investigate(player, object); + } + + /** + * Called when an npc is hooked. + * @param wrapper the wrapper. + * @param npc the npc. + */ + public void catchNpc(TrapWrapper wrapper, NPC npc) { + final TrapNode trapNode = forNpc(npc); + if (trapNode == null || !trapNode.canCatch(wrapper, npc) || !settings.canCatch(wrapper, npc)) { + return; + } + settings.catchNpc(wrapper, trapNode, npc); + } + + /** + * Adds a hook. + * @param wrapper the wrapper. + */ + public TrapHook addHook(TrapWrapper wrapper) { + TrapHook hook = settings.createHook(wrapper); + hooks.add(hook); + return hook; + } + + /** + * Gets a node by the npc. + * @param npc the npc. + * @return the node. + */ + public TrapNode forNpc(NPC npc) { + for (TrapNode node : nodes) { + for (int npcId : node.getNpcIds()) { + if (npcId == npc.getId()) { + return node; + } + } + } + return null; + } + + /** + * Gets a trap by the node id. + * @param node the node. + * @return the {@code Traps}. + */ + public static Traps forNode(Node node) { + for (Traps trap : Traps.values()) { + for (int nodeId : trap.getSettings().getNodeIds()) { + if (node.getId() == nodeId) { + return trap; + } + } + for (int objectId : trap.getSettings().getObjectIds()) { + if (objectId == node.getId()) { + return trap; + } + } + for (TrapNode n : trap.getNodes()) { + for (int id : n.getObjectIds()) { + if (id == node.getId()) { + return trap; + } + } + } + if (trap.getSettings().getFailId() == node.getId()) { + return trap; + } + if (trap == NET_TRAP) { + for (NetTrapSetting.NetTrap net : NetTrapSetting.NetTrap.values()) { + if (net.getOriginal() == node.getId() || net.getFailed() == node.getId() || net.getNet() == node.getId() || net.getBent() == node.getId() || net.getCaught() == node.getId()) { + return trap; + } + } + } + } + return null; + } + + /** + * Gets a trap node by the id. + * @param id the id. + * @return the node. + */ + public static Object[] getNode(int id) { + for (Traps trap : values()) { + for (TrapNode t : trap.getNodes()) { + for (int i : t.getNpcIds()) { + if (i == id) { + return new Object[] { trap, t }; + } + } + } + } + return null; + } + + /** + * Gets the wrapper by the hook. + * @param location the location. + * @return the wrapper. + */ + public TrapWrapper getByHook(Location location) { + for (TrapHook hook : hooks) { + if (hook.isHooked(location)) { + return hook.getWrapper(); + } + } + return null; + } + + /** + * Gets the settings. + * @return The settings. + */ + public TrapSetting getSettings() { + return settings; + } + + /** + * Gets the nodes. + * @return The nodes. + */ + public TrapNode[] getNodes() { + return nodes; + } + + /** + * Gets the hooks. + * @return The hooks. + */ + public List getHooks() { + return hooks; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/bnet/BNetNode.java b/Server/src/main/content/global/skill/hunter/bnet/BNetNode.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/hunter/bnet/BNetNode.java rename to Server/src/main/content/global/skill/hunter/bnet/BNetNode.java index 275588046..7f512bf33 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/bnet/BNetNode.java +++ b/Server/src/main/content/global/skill/hunter/bnet/BNetNode.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter.bnet; +package content.global.skill.hunter.bnet; import core.cache.def.impl.NPCDefinition; import core.game.container.impl.EquipmentContainer; @@ -136,7 +136,7 @@ public class BNetNode { * @return {@code} True if so. */ public boolean isBareHand(Player player) { - return !hasNet(player) && player.getSkills().getStaticLevel(Skills.HUNTER) >= getBareHandLevel() && player.getSkills().getStaticLevel(Skills.AGILITY) >= getAgilityLevel(); + return !hasNet(player) && player.getSkills().getLevel(Skills.HUNTER) >= getBareHandLevel() && player.getSkills().getLevel(Skills.AGILITY) >= getAgilityLevel(); } /** diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/bnet/BNetPulse.java b/Server/src/main/content/global/skill/hunter/bnet/BNetPulse.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/skill/hunter/bnet/BNetPulse.java rename to Server/src/main/content/global/skill/hunter/bnet/BNetPulse.java index 638531490..38371c49f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/bnet/BNetPulse.java +++ b/Server/src/main/content/global/skill/hunter/bnet/BNetPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter.bnet; +package content.global.skill.hunter.bnet; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.skill.SkillPulse; @@ -7,13 +7,16 @@ import core.game.node.entity.combat.DeathTask; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; -import rs09.game.world.GameWorld; +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 @@ -69,22 +72,20 @@ public final class BNetPulse extends SkillPulse { @Override public boolean checkRequirements() { - if (player.getHunterManager().getStaticLevel() < type.getLevel()) { + if (player.getSkills().getLevel(Skills.HUNTER) < type.getLevel()) { 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/java/core/game/node/entity/skill/hunter/bnet/BNetTypes.java b/Server/src/main/content/global/skill/hunter/bnet/BNetTypes.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/skill/hunter/bnet/BNetTypes.java rename to Server/src/main/content/global/skill/hunter/bnet/BNetTypes.java index 0d4515fff..0b1e65fb2 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/bnet/BNetTypes.java +++ b/Server/src/main/content/global/skill/hunter/bnet/BNetTypes.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter.bnet; +package content.global.skill.hunter.bnet; import core.game.node.entity.npc.NPC; import core.game.node.entity.npc.drop.DropFrequency; @@ -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/java/core/game/node/entity/skill/hunter/bnet/ImplingNode.java b/Server/src/main/content/global/skill/hunter/bnet/ImplingNode.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/hunter/bnet/ImplingNode.java rename to Server/src/main/content/global/skill/hunter/bnet/ImplingNode.java index f0f595e7a..e454b8d3c 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/bnet/ImplingNode.java +++ b/Server/src/main/content/global/skill/hunter/bnet/ImplingNode.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter.bnet; +package content.global.skill.hunter.bnet; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/falconry/FalconCatch.java b/Server/src/main/content/global/skill/hunter/falconry/FalconCatch.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/hunter/falconry/FalconCatch.java rename to Server/src/main/content/global/skill/hunter/falconry/FalconCatch.java index 9c7d2f231..4f0c64657 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/falconry/FalconCatch.java +++ b/Server/src/main/content/global/skill/hunter/falconry/FalconCatch.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter.falconry; +package content.global.skill.hunter.falconry; import core.game.node.entity.npc.NPC; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/falconry/FalconryCatchPulse.java b/Server/src/main/content/global/skill/hunter/falconry/FalconryCatchPulse.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/hunter/falconry/FalconryCatchPulse.java rename to Server/src/main/content/global/skill/hunter/falconry/FalconryCatchPulse.java index d068f613a..b33667e41 100644 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/falconry/FalconryCatchPulse.java +++ b/Server/src/main/content/global/skill/hunter/falconry/FalconryCatchPulse.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.hunter.falconry; +package content.global.skill.hunter.falconry; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.skill.SkillPulse; @@ -9,10 +9,13 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.HintIconManager; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; -import rs09.game.world.repository.Repository; +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. @@ -93,7 +96,8 @@ public final class FalconryCatchPulse extends SkillPulse { player.getEquipment().add(GLOVE, true, false); sendProjectile(); } - player.lock(getDistance()); + node.lock(getDistance()+1); + player.lock(getDistance()+1); } return true; } @@ -114,7 +118,6 @@ public final class FalconryCatchPulse extends SkillPulse { return false; } final boolean success = success(); - player.lock(); player.getPacketDispatch().sendMessage(success ? "The falcon successfully swoops down and captures the kebbit." : "The falcon swoops down on the kebbit, but just misses catching it."); if (success) { node.finalizeDeath(player); @@ -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() { @@ -142,7 +146,6 @@ public final class FalconryCatchPulse extends SkillPulse { player.getEquipment().add(FALCON, true, false); } } - player.unlock(); player.face(null); return true; } @@ -156,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/kotlin/rs09/game/node/entity/skill/hunter/pitfall/HunterPitfall.kt b/Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt similarity index 77% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/pitfall/HunterPitfall.kt rename to Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt index 0226fa5aa..bdc9611a9 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/pitfall/HunterPitfall.kt +++ b/Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt @@ -1,31 +1,29 @@ -import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeUnit -import api.ContentAPI -import core.cache.def.impl.NPCDefinition -import core.cache.def.impl.SceneryDefinition -import core.game.interaction.OptionHandler -import core.game.node.Node +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 import core.game.node.entity.skill.Skills +import content.global.skill.hunter.HunterManager import core.game.node.item.Item import core.game.node.scenery.Scenery 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 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.NPCs -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld +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() { @@ -64,27 +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(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( @@ -147,10 +146,10 @@ val GRAAHK_PIT = 19231 val LARUPIA_PIT = 19232 val KYATT_PIT = 19233 -class PitfallListeners : InteractionListener() { +class PitfallListeners : InteractionListener { override fun defineListeners() { - setDest(SCENERY, intArrayOf(PIT, SPIKED_PIT, LARUPIA_PIT, GRAAHK_PIT, KYATT_PIT), "trap", "jump", "dismantle") { player, node -> + setDest(IntType.SCENERY, intArrayOf(PIT, SPIKED_PIT, LARUPIA_PIT, GRAAHK_PIT, KYATT_PIT), "trap", "jump", "dismantle") { player, node -> val pit = node as Scenery val src = player.getLocation() var dst = pit.getLocation() @@ -168,17 +167,17 @@ class PitfallListeners : InteractionListener() { } return@setDest dst } - on(PIT, SCENERY, "trap") { player, node -> - val pit = node as Scenery; + on(PIT, IntType.SCENERY, "trap") { player, node -> + 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.") return@on true } - val maxTraps = player.hunterManager.maximumTraps + val maxTraps = HunterManager.getInstance(player).maximumTraps if(player.getAttribute("pitfall:count", 0) >= maxTraps) { - player.sendMessage("You can't set up more than ${maxTraps} pitfall traps at your hunter level.") + player.sendMessage("You can't set up more than $maxTraps pitfall traps at your hunter level.") return@on true } player.incrementAttribute("pitfall:count", 1) @@ -190,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()) @@ -204,13 +204,14 @@ class PitfallListeners : InteractionListener() { GameWorld.Pulser.submit(collapsePulse) return@on true } - on(SPIKED_PIT, SCENERY, "jump") { player, node -> - val pit = node as Scenery; + on(SPIKED_PIT, IntType.SCENERY, "jump") { player, node -> + 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) @@ -224,7 +225,10 @@ class PitfallListeners : InteractionListener() { //ForceMovement.run(pitfall_npc, pitfall_npc.getLocation(), pit.getLocation(), ForceMovement.WALK_ANIMATION, Animation(ANIM), dir, 8); //pitfall_npc.setLocation(pit.getLocation()); //pitfall_npc.walkingQueue.addPath(pit.location.x, pit.location.y); - ContentAPI.teleport(pitfall_npc, pit.location) + 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) @@ -234,8 +238,9 @@ class PitfallListeners : InteractionListener() { //ForceMovement.run(pitfall_npc, pitfall_npc.getLocation(), dst, ForceMovement.WALK_ANIMATION, Animation(ANIM), dir, 8); //pitfall_npc.walkingQueue.addPath(npcdst.x, npcdst.y) val npcdst = dst.transform(dir, if(dir == Direction.SOUTH || dir == Direction.WEST) 1 else 0) - ContentAPI.teleport(pitfall_npc, npcdst) + 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) } @@ -243,26 +248,31 @@ class PitfallListeners : InteractionListener() { } return@on true } - on(SPIKED_PIT, SCENERY, "dismantle") { player, node -> - val pit = node as Scenery; + on(SPIKED_PIT, IntType.SCENERY, "dismantle") { player, node -> + 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) return@on true } - on(LARUPIA_PIT, SCENERY, "dismantle") { player, node -> + 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, SCENERY, "dismantle") { player, node -> + 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, SCENERY, "dismantle") { player, node -> + 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, NPC, "tease") { player, node -> + on(BEAST_IDS, IntType.NPC, "tease") { player, node -> val entity = node as Entity val hunterReq = HUNTER_REQS[entity.name]!! if(player.skills.getLevel(Skills.HUNTER) < hunterReq) { @@ -274,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 } @@ -281,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) { @@ -298,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) } } @@ -318,7 +330,7 @@ class PitfallNPC : AbstractNPC { return BEAST_IDS } - override fun isAttackable(entity: Entity, style: CombatStyle): Boolean { + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { return false } diff --git a/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitEast.kt b/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitEast.kt new file mode 100644 index 000000000..a78e64fe7 --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitEast.kt @@ -0,0 +1,74 @@ +package content.global.skill.hunter.tracking + +import core.cache.def.impl.SceneryDefinition +import core.game.node.item.Item +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 + +@Initializable +class CommonKebbitEast : HunterTracking() { + + init { + initialMap = hashMapOf( + 19439 to arrayListOf( + 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(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(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 + trailLimit = 3 + attribute = "hunter:tracking:commontrail" + indexAttribute = "hunter:tracking:commonIndex" + rewards = arrayOf(Item(Items.COMMON_KEBBIT_FUR_10121), Item(Items.BONES_526),Item(Items.RAW_BEAST_MEAT_9986)) + KEBBIT_ANIM = Animation(5259) + } + + + override fun newInstance(arg: Any?): Plugin { + if(!linkingTrails.contains(initialMap.values.random()[0])){ + addExtraTrails() + } + SceneryDefinition.forId(19439).handlers["option:inspect"] = this + SceneryDefinition.forId(19440).handlers["option:inspect"] = this + SceneryDefinition.forId(19360).handlers["option:inspect"] = this + SceneryDefinition.forId(19361).handlers["option:inspect"] = this + SceneryDefinition.forId(19362).handlers["option:inspect"] = this + SceneryDefinition.forId(19363).handlers["option:inspect"] = this + SceneryDefinition.forId(19364).handlers["option:inspect"] = this + SceneryDefinition.forId(19365).handlers["option:inspect"] = this + SceneryDefinition.forId(19356).handlers["option:inspect"] = this + SceneryDefinition.forId(19357).handlers["option:inspect"] = this + SceneryDefinition.forId(19358).handlers["option:inspect"] = this + SceneryDefinition.forId(19359).handlers["option:inspect"] = this + SceneryDefinition.forId(19375).handlers["option:inspect"] = this + SceneryDefinition.forId(19376).handlers["option:inspect"] = this + SceneryDefinition.forId(19377).handlers["option:inspect"] = this + SceneryDefinition.forId(19378).handlers["option:inspect"] = this + SceneryDefinition.forId(19379).handlers["option:inspect"] = this + SceneryDefinition.forId(19372).handlers["option:inspect"] = this + SceneryDefinition.forId(19380).handlers["option:inspect"] = this + SceneryDefinition.forId(19374).handlers["option:inspect"] = this + SceneryDefinition.forId(19373).handlers["option:inspect"] = this + SceneryDefinition.forId(19428).handlers["option:search"] = this + SceneryDefinition.forId(19428).handlers["option:attack"] = this + + return this + } +} diff --git a/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitWest.kt b/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitWest.kt new file mode 100644 index 000000000..537d44307 --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitWest.kt @@ -0,0 +1,4 @@ +package content.global.skill.hunter.tracking + +class CommonKebbitWest { +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/HunterTracking.kt b/Server/src/main/content/global/skill/hunter/tracking/HunterTracking.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/HunterTracking.kt rename to Server/src/main/content/global/skill/hunter/tracking/HunterTracking.kt index d888c6315..3a56ee557 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/HunterTracking.kt +++ b/Server/src/main/content/global/skill/hunter/tracking/HunterTracking.kt @@ -1,5 +1,6 @@ -package rs09.game.node.entity.skill.hunter.tracking +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 rs09.game.system.SystemLogger -import rs09.game.world.GameWorld +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("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 new file mode 100644 index 000000000..92e874416 --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/tracking/PolarKebbitHunting.kt @@ -0,0 +1,68 @@ +package content.global.skill.hunter.tracking + +import core.cache.def.impl.SceneryDefinition +import core.game.node.item.Item +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 + +@Initializable +class PolarKebbitHunting : HunterTracking() { + + init { + KEBBIT_ANIM = Animation(5256) + trailLimit = 3 + attribute = "hunter:tracking:polartrail" + indexAttribute = "hunter:tracking:polarindex" + rewards = arrayOf(Item(Items.RAW_BEAST_MEAT_9986),Item(Items.POLAR_KEBBIT_FUR_10117),Item(Items.BONES_526)) + tunnelEntrances = arrayOf( + Location.create(2711, 3819, 1), + Location.create(2714, 3821, 1), + Location.create(2718, 3829, 1), + Location.create(2721, 3827, 1), + Location.create(2718, 3832, 1), + Location.create(2715, 3820, 1) + ) + initialMap = hashMapOf( + 19640 to arrayListOf( + 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(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(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 + requiredLevel = 1 + } + + override fun newInstance(arg: Any?): Plugin { + addExtraTrails() + SceneryDefinition.forId(19640).handlers["option:inspect"] = this + SceneryDefinition.forId(19641).handlers["option:inspect"] = this + SceneryDefinition.forId(19435).handlers["option:inspect"] = this + SceneryDefinition.forId(36689).handlers["option:inspect"] = this + SceneryDefinition.forId(36690).handlers["option:inspect"] = this + SceneryDefinition.forId(19421).handlers["option:inspect"] = this + SceneryDefinition.forId(19424).handlers["option:inspect"] = this + SceneryDefinition.forId(19426).handlers["option:inspect"] = this + SceneryDefinition.forId(19419).handlers["option:inspect"] = this + SceneryDefinition.forId(19420).handlers["option:inspect"] = this + SceneryDefinition.forId(19423).handlers["option:inspect"] = this + SceneryDefinition.forId(36688).handlers["option:inspect"] = this + SceneryDefinition.forId(19435).handlers["option:search"] = this + SceneryDefinition.forId(19435).handlers["option:attack"] = this + return this + } +} diff --git a/Server/src/main/content/global/skill/hunter/tracking/TrailDefinition.kt b/Server/src/main/content/global/skill/hunter/tracking/TrailDefinition.kt new file mode 100644 index 000000000..a3af8d305 --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/tracking/TrailDefinition.kt @@ -0,0 +1,14 @@ +package content.global.skill.hunter.tracking + +import core.game.world.map.Location + +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 [varbit: $varbit] [${type.name}] [inverted: $inverted]" + } +} +enum class TrailType{ + LINKING, + INITIAL, + TUNNEL +} 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 new file mode 100644 index 000000000..e938f3ef0 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/SpellListener.kt @@ -0,0 +1,126 @@ +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 +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.game.world.update.flag.context.Graphics +import core.game.interaction.Listener +import core.game.world.GameWorld + +abstract class SpellListener(val bookName: String) : Listener { + companion object { + @JvmField + val NPC = -1 + @JvmField + val OBJECT = -2 + @JvmField + val ITEM = -3 + @JvmField + val PLAYER = -4 + @JvmField + val NONE = -5 + @JvmField + val GROUND_ITEM = -6 + } + 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, 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()) { + if(player.getAttribute("magic-delay",0) > GameWorld.ticks){ + throw IllegalStateException() + } + if(player.getAttribute("tablet-spell",false)){ + return + } + if(player.skills.getLevel(Skills.MAGIC) < magicLevel){ + player.sendMessage("You need a magic level of $magicLevel 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)){ + player.sendMessage("You need a ${ItemDefinition.forId(item).name} to cast this.") + throw IllegalStateException() + } + } + } + + fun removeRunes(player: Player,removeAttr: Boolean = true){ + player.inventory.remove(*player.getAttribute("spell:runes",ArrayList()).toTypedArray()) + if(removeAttr) { + player.removeAttribute("spell:runes") + player.removeAttribute("tablet-spell") + } + } + + fun addXP(player: Player,amount:Double){ + if(player.getAttribute("tablet-spell",false)) return + player.skills.addExperience(Skills.MAGIC,amount) + } + + /** + * @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) + if(soundID != -1){ + 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) + } + } + + fun setDelay(player: Player, isTeleport: Boolean = false){ + if(!isTeleport) player.setAttribute("magic-delay", GameWorld.ticks + 3) else player.setAttribute("magic-delay", GameWorld.ticks + 5) + } + + fun setDelay(player: Player, delay: Int) { + setAttribute(player, "magic-delay", GameWorld.ticks + delay) + } + + fun interrupt(player: Player){ + player.pulseManager.clear() + } + + fun showMagicTab(player: Player){ + player.interfaceManager.setViewedTab(6) + } +} diff --git a/Server/src/main/content/global/skill/magic/SpellListeners.kt b/Server/src/main/content/global/skill/magic/SpellListeners.kt new file mode 100644 index 000000000..e390516e3 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/SpellListeners.kt @@ -0,0 +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.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, 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, 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): 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): 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){ + 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 + } + } + } +} diff --git a/Server/src/main/content/global/skill/magic/SpellTablets.kt b/Server/src/main/content/global/skill/magic/SpellTablets.kt new file mode 100644 index 000000000..62b550f63 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/SpellTablets.kt @@ -0,0 +1,40 @@ +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 + val B2B_TABLET = Items.BONES_TO_BANANAS_8014 + override fun defineListeners() { + + on(B2B_TABLET, IntType.ITEM, "break"){ player, node -> + breakTablet(player) + SpellListeners.run(Modern.BONES_TO_BANANAS,SpellListener.NONE,"modern",player) + player.inventory.remove(Item(node.id)) + return@on true + } + + on(B2P_TABLET, IntType.ITEM, "break"){ player, node -> + breakTablet(player) + SpellListeners.run(Modern.BONES_TO_PEACHES,SpellListener.NONE,"modern",player) + player.inventory.remove(Item(node.id)) + return@on true + } + + } + + fun breakTablet(player: Player){ + playAudio(player, Sounds.POH_TABLET_BREAK_979) + player.animator.forceAnimation(Animation(4069)) + player.lock(5) + player.setAttribute("tablet-spell",true) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/SpellUtils.kt b/Server/src/main/content/global/skill/magic/SpellUtils.kt new file mode 100644 index 000000000..57e8b4c6d --- /dev/null +++ b/Server/src/main/content/global/skill/magic/SpellUtils.kt @@ -0,0 +1,97 @@ +package content.global.skill.magic + +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 + val staves = staff.staves + for (id in staves) { + if (weapon.id == id) { + return true + } + } + return false + } + + /** + * 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 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 } + } + } + + 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(runeId, amount)) + } + + p.setAttribute("spell:runes", toRemove) + return null + } + + @JvmStatic + fun getBookFromInterface(id: Int): String{ + return when(id){ + 192 -> "modern" + 193 -> "ancient" + 430 -> "lunar" + else -> "none" + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/SpellbookSwapDialogue.java b/Server/src/main/content/global/skill/magic/SpellbookSwapDialogue.java new file mode 100644 index 000000000..b794a87e0 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/SpellbookSwapDialogue.java @@ -0,0 +1,118 @@ +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.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. + * @author Vexia + * + */ +@Initializable +public class SpellbookSwapDialogue extends DialoguePlugin { + + /** + * If we're using the perk. + */ + private boolean perk; + + /** + * Constructs a new {@Code SpellbookSwapDialogue} {@Code Object} + */ + public SpellbookSwapDialogue() { + /* + * empty. + */ + } + + /** + * Constructs a new {@Code SpellbookSwapDialogue} {@Code Object} + * @param player the player. + */ + public SpellbookSwapDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new SpellbookSwapDialogue(player); + } + + @Override + public boolean open(Object... args) { + if (args.length > 1) { + perk = true; + interpreter.sendOptions("Select a Spellbook", "Modern", "Ancient", "Lunar"); + return true; + } + interpreter.sendOptions("Select a Spellbook", "Ancient", "Modern"); + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + if (perk) { + SpellBook book = SpellBook.values()[buttonId - 1]; + player.getSpellBookManager().setSpellBook(book); + player.getInterfaceManager().openTab(new Component(book.getInterfaceId())); + end(); + return true; + } + int type = 0; + switch (buttonId) { + case 1: + type = 1; + break; + case 2: + type = 2; + 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; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 3264731 }; + } +} diff --git a/Server/src/main/content/global/skill/magic/TeleportMethod.kt b/Server/src/main/content/global/skill/magic/TeleportMethod.kt new file mode 100644 index 000000000..0f092eb8b --- /dev/null +++ b/Server/src/main/content/global/skill/magic/TeleportMethod.kt @@ -0,0 +1,7 @@ +package content.global.skill.magic + +enum class TeleportMethod { + JEWELRY, + SPELL, + NPC +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/ancient/AncientTeleportPlugin.java b/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/skill/magic/ancient/AncientTeleportPlugin.java rename to Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java index b05c1d9be..a9c95ab5e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/ancient/AncientTeleportPlugin.java +++ b/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java @@ -1,20 +1,26 @@ -package core.game.node.entity.skill.magic.ancient; +package content.global.skill.magic.ancient; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; +import core.game.event.SpellCastEvent; import core.game.node.Node; import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; +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 rs09.game.world.GameWorld; +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/java/core/game/node/entity/combat/spell/BloodSpells.java b/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java similarity index 76% rename from Server/src/main/java/core/game/node/entity/combat/spell/BloodSpells.java rename to Server/src/main/content/global/skill/magic/ancient/BloodSpells.java index d60593f9d..2cd31111c 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/BloodSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java @@ -1,13 +1,13 @@ -package core.game.node.entity.combat.spell; +package content.global.skill.magic.ancient; import java.util.List; -import core.game.node.entity.skill.magic.Runes; +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.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +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; @@ -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 new file mode 100644 index 000000000..4b0e29ff4 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/ancient/IceSpells.java @@ -0,0 +1,160 @@ +package content.global.skill.magic.ancient; + +import java.util.List; + +import core.game.node.entity.combat.spell.Runes; +import core.game.node.Node; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.spell.CombatSpell; +import core.game.node.entity.combat.spell.SpellType; +import core.game.node.entity.impl.Projectile; +import core.game.node.entity.impl.Animator.Priority; +import core.game.node.entity.player.link.SpellBookManager.SpellBook; +import core.game.node.item.Item; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; +import core.plugin.Initializable; +import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; + +/** + * Handles the Ice spells from the Ancient spellbook. + * @author Emperor + * @version 1.0 + */ +@Initializable +public final class IceSpells extends CombatSpell { + + /** + * The barrage orb GFX. + */ + private static final Graphics BARRAGE_ORB = new Graphics(1677, 96); //119 + + /** + * The projectile for Ice rush. + */ + private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 360, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice rush. + */ + private static final Graphics RUSH_END = new Graphics(361, 96); + + /** + * The projectile for Ice barrage. + */ + private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 362, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice barrage. + */ + private static final Graphics BURST_END = new Graphics(363, 0); + + /** + * The start graphic for Ice rush. + */ + private static final Graphics BLITZ_START = new Graphics(366, 96); + + /** + * The end graphic for Ice rush. + */ + private static final Graphics BLITZ_END = new Graphics(367, 96); + + /** + * The projectile for Ice barrage. + */ + private static final Projectile BARRAGE_PROJECTILE = Projectile.create((Entity) null, null, 368, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Ice barrage. + */ + private static final Graphics BARRAGE_END = new Graphics(369, 0); + + /** + * Constructs a new {@code IceSpells} {@code Object}. + */ + public IceSpells() { + /* + * empty. + */ + } + + /** + * Constructs a new {@code IceSpells} {@Code Object} + * @param type The spell type. + * @param impactSound The impact sound id. + * @param anim The animation. + * @param start The start graphics. + * @param projectile The projectile. + * @param end The end graphics. + */ + private IceSpells(SpellType type, int level, double baseExperience, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.ANCIENT, level, baseExperience, Sounds.ICE_CAST_171, impactSound, anim, start, projectile, end, runes); + } + + @Override + public void visualize(Entity entity, Node target) { + entity.graphics(graphic); + if (projectile != null) { + projectile.transform(entity, (Entity) target, false, 58, 10).send(); + } + entity.animate(animation); + playGlobalAudio(entity.getLocation(), audio.id, 20); + + } + + @Override + public void visualizeImpact(Entity entity, Entity target, BattleState state) { + if (state.isFrozen()) { + playGlobalAudio(target.getLocation(), impactAudio, 20); + target.graphics(BARRAGE_ORB); + return; + } + super.visualizeImpact(entity, target, state); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + SpellBook.ANCIENT.register(0, new IceSpells(SpellType.RUSH, 58, 34.0, Sounds.ICE_RUSH_IMPACT_173, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2), Runes.WATER_RUNE.getItem(2))); + SpellBook.ANCIENT.register(2, new IceSpells(SpellType.BURST, 70, 40.0, Sounds.ICE_BURST_IMPACT_170, new Animation(1979, Priority.HIGH), null, BURST_PROJECTILE, BURST_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4), Runes.WATER_RUNE.getItem(4))); + SpellBook.ANCIENT.register(1, new IceSpells(SpellType.BLITZ, 82, 46.0, Sounds.ICE_BLITZ_IMPACT_169, new Animation(1978, Priority.HIGH), BLITZ_START, null, BLITZ_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.WATER_RUNE.getItem(3))); + SpellBook.ANCIENT.register(3, new IceSpells(SpellType.BARRAGE, 94, 52.0, Sounds.ICE_BARRAGE_IMPACT_168, new Animation(1979, Priority.HIGH), null, BARRAGE_PROJECTILE, BARRAGE_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(4), Runes.WATER_RUNE.getItem(6))); + return this; + } + + @Override + public void fireEffect(Entity entity, Entity victim, BattleState state) { + if (state.getEstimatedHit() == -1) { + return; + } + int ticks = (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 new file mode 100644 index 000000000..5ead4131e --- /dev/null +++ b/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java @@ -0,0 +1,181 @@ + package content.global.skill.magic.ancient; + +import java.util.List; + +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.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.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 + * @version 1.0 + */ +@Initializable +public final class MiasmicSpells extends CombatSpell { + + /** + * The start graphic for Miasmic rush. + */ + private static final Graphics RUSH_START = new Graphics(1845, 0, 15); + + /** + * The end graphic for Miasmic rush. + */ + private static final Graphics RUSH_END = new Graphics(1847, 40); + + /** + * The start graphic for Miasmic rush. + */ + private static final Graphics BURST_START = new Graphics(1848, 0); + + /** + * The end graphic for Miasmic barrage. + */ + private static final Graphics BURST_END = new Graphics(1849, 20, 30); + + /** + * The start graphic for Miasmic rush. + */ + private static final Graphics BLITZ_START = new Graphics(1850, 15); + + /** + * The end graphic for Miasmic rush. + */ + private static final Graphics BLITZ_END = new Graphics(1851, 0); + + /** + * The start graphic for Miasmic rush. + */ + private static final Graphics BARRAGE_START = new Graphics(1853, 0); + + /** + * The end graphic for Miasmic barrage. + */ + private static final Graphics BARRAGE_END = new Graphics(1854, 0, 30); + + /** + * The item ids of the staves able to cast Miasmic spells. + */ + private static final int[] VALID_STAFF_IDS = { 13867, 13869, 13841, 13843 }; + + /** + * Constructs a new {@code MiasmicSpells} {@code Object}. + */ + public MiasmicSpells() { + } + + /** + * Constructs a new {@code MiasmicSpells} {@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 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 + 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) { + super.visualizeImpact(entity, target, state); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + 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 (!hasTimerActive(victim, "miasmic:immunity")) { + registerTimer(victim, spawnTimer("miasmic", (getSpellId() - 15) * 20)); + } + } + + /** + * Checks if a valid staff is equipped. + * @param entity The attacking entity. + * @return {@code True} if so. + */ + public boolean validStaffEquipped(Entity entity){ + for(int i = 0; i < VALID_STAFF_IDS.length; i++){ + if(((Player) entity).getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId() == VALID_STAFF_IDS[i]){ + return true; + } + } + return false; + } + + @Override + public boolean cast(Entity entity, Node target) { + if (!validStaffEquipped(entity) && !GameWorld.getSettings().isDevMode()) { + ((Player) entity).getPacketDispatch().sendMessage("You need to be wielding Zuriel's staff in order to cast this spell."); + return false; + } + if (!meetsRequirements(entity, true, false)) { + return false; + } + return super.cast(entity, target); + } + + @Override + public BattleState[] getTargets(Entity entity, Entity target) { + if (animation.getId() == 10513 || animation.getId() == 10524 + || !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) { + int add = 9; + if(animation.getId() == 10524 || animation.getId() == 10516){ + add = 6; + } + if(animation.getId() == 10513){ + add = 4; + } + return getType().getImpactAmount(entity, victim, add); + } + +} diff --git a/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java b/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java new file mode 100644 index 000000000..22ca33fa3 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java @@ -0,0 +1,132 @@ +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; +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.playGlobalAudio; + +/** + * Handles the Shadow spells from the Ancient spellbook. + * @author Emperor + */ +@Initializable +public final class ShadowSpells extends CombatSpell { + + /** + * The projectile for Shadow rush. + */ + private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 378, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Shadow rush. + */ + private static final Graphics RUSH_END = new Graphics(379, 96); + + /** + * The projectile for Shadow burst. + */ + private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 380, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Shadow burst. + */ + private static final Graphics BURST_END = new Graphics(381, 0); + + /** + * The end graphic for Shadow blitz. + */ + private static final Graphics BLITZ_END = new Graphics(382, 96); + + /** + * The end graphic for Shadow barrage. + */ + private static final Graphics BARRAGE_END = new Graphics(383, 0); + + /** + * Constructs a new {@code ShadowSpells} {@code Object}. + */ + public ShadowSpells() { + /* + * empty. + */ + } + + /** + * Constructs a new {@code ShadowSpells} {@Code Object} + * @param type The spell type. + * @param level The level requirement. + * @param sound The casting sound. + * @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 ShadowSpells(SpellType type, int level, double baseExperience, int sound, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.ANCIENT, level, baseExperience, sound, impactSound, anim, start, projectile, end, runes); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + 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; + } + + @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 fireEffect(Entity entity, Entity victim, BattleState state) { + if (state.getEstimatedHit() > -1) { + int level = victim.getSkills().getStaticLevel(Skills.ATTACK); + victim.getSkills().updateLevel(Skills.ATTACK, (int) -(level * 0.1), (int) (level - (level * 0.1))); + } + } + + @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, 2); + } + +} diff --git a/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java b/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java new file mode 100644 index 000000000..23ddf938f --- /dev/null +++ b/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java @@ -0,0 +1,140 @@ +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.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; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; + +/** + * Handles the Smoke spells from the Ancient spellbook. + * @author Emperor + */ +@Initializable +public final class SmokeSpells extends CombatSpell { + + /** + * The projectile for Smoke rush. + */ + private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 384, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Smoke rush. + */ + private static final Graphics RUSH_END = new Graphics(385, 96); + + /** + * The projectile for Smoke burst. + */ + private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 386, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Smoke burst. + */ + private static final Graphics BURST_END = new Graphics(387, 0); + + /** + * The projectile for Smoke blitz. + */ + private static final Projectile BLITZ_PROJECTILE = Projectile.create((Entity) null, null, 389, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Smoke blitz. + */ + private static final Graphics BLITZ_END = new Graphics(388, 96); + + /** + * The projectile for Smoke barrage. + */ + private static final Projectile BARRAGE_PROJECTILE = Projectile.create((Entity) null, null, 391, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Smoke barrage. + */ + private static final Graphics BARRAGE_END = new Graphics(390, 0); + + /** + * Constructs a new {@code SmokeSpells} {@code Object}. + */ + public SmokeSpells() { + /* + * empty. + */ + } + + /** + * Constructs a new {@code SmokeSpells} {@Code Object} + * @param type The spell type. + * @param level The level requirement. + * @param sound The casting sound. + * @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 SmokeSpells(SpellType type, int level, double baseExperience, int sound, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.ANCIENT, level, baseExperience, sound, impactSound, anim, start, projectile, end, runes); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + 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; + } + + @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 fireEffect(Entity entity, Entity victim, BattleState state) { + if (state.getEstimatedHit() > -1) { + applyPoison(victim, entity, type.ordinal() >= SpellType.BLITZ.ordinal() ? 4 : 2); + } + } + + @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, 1); + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/HealSpell.java b/Server/src/main/content/global/skill/magic/lunar/HealSpell.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/skill/magic/lunar/HealSpell.java rename to Server/src/main/content/global/skill/magic/lunar/HealSpell.java index e4abd8f1f..f79c85728 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/HealSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/HealSpell.java @@ -1,13 +1,13 @@ -package core.game.node.entity.skill.magic.lunar; +package content.global.skill.magic.lunar; +import core.game.node.entity.combat.spell.MagicSpell; +import core.game.node.entity.combat.spell.Runes; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.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.equipment.SpellType; +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; @@ -15,10 +15,13 @@ 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 @@ -104,6 +107,8 @@ public final class HealSpell extends MagicSpell { player.getImpactHandler().manualHit(player, transfer, null); o.getSkills().heal(transfer); player.animate(ANIMATION); + 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); @@ -127,7 +132,9 @@ public final class HealSpell extends MagicSpell { } player.getImpactHandler().manualHit(player, percentage, HitsplatType.NORMAL); player.animate(ANIMATION_G); + playGlobalAudio(player.getLocation(), Sounds.LUNAR_HEAL_GROUP_2894); for (Player p : players) { + 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/LunarData.kt b/Server/src/main/content/global/skill/magic/lunar/LunarData.kt new file mode 100644 index 000000000..9a1b28f6c --- /dev/null +++ b/Server/src/main/content/global/skill/magic/lunar/LunarData.kt @@ -0,0 +1,135 @@ +package content.global.skill.magic.lunar + +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 + +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 + } + } +} + +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), + RUBY(Items.RUBY_AMULET_1679, Items.RUBY_AMULET_1698), + DIAMOND(Items.DIAMOND_AMULET_1681, Items.DIAMOND_AMULET_1700), + DRAGONSTONE(Items.DRAGONSTONE_AMMY_1683, Items.DRAGONSTONE_AMMY_1702), + ONYX(Items.ONYX_AMULET_6579, Items.ONYX_AMULET_6581), + SALVE(Items.SALVE_SHARD_4082, Items.SALVE_AMULET_4081), + HOLY(Items.UNSTRUNG_SYMBOL_1714, Items.UNBLESSED_SYMBOL_1716), + UNHOLY(Items.UNSTRUNG_EMBLEM_1720, Items.UNPOWERED_SYMBOL_1722); + companion object { + private val productOfString = values().associate { it.unstrung to it.strung } + fun forId(id: Int): Int { + return productOfString[id]!! + } + + fun unstrungContains(id: Int): Boolean { + return productOfString.contains(id) + } + } +} + +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 new file mode 100644 index 000000000..fd8dc5ccd --- /dev/null +++ b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt @@ -0,0 +1,894 @@ +package content.global.skill.magic.lunar + +import content.global.skill.cooking.CookableItems +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.skill.Skills +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) + 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) + } + + // 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))) + npcContact(player) + } + + // 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) } + } + + // 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()) } + } + + // 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)) + } + + // 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)) + } + + // 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) + } + } + + // Spell handlers + // Level 65 + private fun bakePie(player: Player){ + val playerPies = ArrayList() + + for(item in player.inventory.toArray()){ + if(item == null) continue + val pie = CookableItems.forId(item.id) ?: continue + if(!pie.name.lowercase().contains("pie")) continue + if(player.skills.getLevel(Skills.COOKING) < pie.level) continue + playerPies.add(item) + } + + if(playerPies.isEmpty()){ + player.sendMessage("You have no pies which you have the level to cook.") + throw IllegalStateException() + } + + player.pulseManager.run(object : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + if(playerPies.isEmpty()) return true + + 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, 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) + setDelay(player,false) + player.inventory.remove(item) + player.inventory.add(Item(pie.cooked)) + playerPies.remove(item) + if(playerPies.isNotEmpty()) removeRunes(player,false) else removeRunes(player,true) + return false + } + }) + } + + // Level 66 + fun curePlant(player: Player, obj: Scenery) { + if (CompostBins.forObject(obj) != null) { + sendMessage(player, "Bins don't often get diseased.") + throw IllegalStateException() + } + val fPatch = FarmingPatch.forObject(obj) + if (fPatch == null) { + sendMessage(player, "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) + addXP(player,63.0) + setDelay(player,false) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_NPC_CONTACT_4413, Graphics.LUNAR_SPELLBOOK_NPC_CONTACT_728, 130,3618) + } + } + + // 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() + } + 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) + } + + // Level 68 + private fun humidify(player: Player) { + val playerEmpties = ArrayDeque() + + for(item in player.inventory.toArray()) { + if(item == null) continue + if(!HumidifyItems.emptyContains(item.id)) continue + playerEmpties.add(item) + } + + 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 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 = 0 + + fun addMolten(): Boolean { + if(RandomFunction.randomDouble(1.0) < 0.3) { + if(addItem(player, Items.MOLTEN_GLASS_1775, 2)) return true + } else { + if(addItem(player, Items.MOLTEN_GLASS_1775)) return true + } + return false + } + + val size = minOf(playerSand, playerWeed) + + if(index != size && size != 0) { + for (item in inv) { + if (item == null) continue + if (index == size) break + if (GLASS_WEEDS.contains(item.id)) { + if (removeItem(player, item) && removeItem(player, Items.BUCKET_OF_SAND_1783) && addMolten()) { + index++ + } else { + break + } + } + } + } else if (playerWeed == 0 || playerSand == 0 || size == 0) { + sendMessage(player, "You lack the required ingredients.") + } + + if(index == size && size != 0) { + removeRunes(player, true) + 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.") + throw IllegalStateException() + } + + val fPatch = FarmingPatch.forObject(target) + if(fPatch == null) { + sendMessage(player, "Um... I don't want to fertilize that!") + throw IllegalStateException() + } + + val patch = fPatch.getPatchFor(player) + if (patch.isGrown()) { + sendMessage(player, "Composting isn't going to make it get any bigger.") + throw IllegalStateException() + } + if (patch.isFertilized()) { + sendMessage(player, "This patch has already been composted.") + throw IllegalStateException() + } + removeRunes(player, true) + 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) + } + + // 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() + } + 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) + } + + // Level 91 + private fun energyTransfer(player: Player, target: Node) { + if(!isPlayer(target)) { + sendMessage(player, "You can only cast this spell on other players.") + throw IllegalStateException() + } + val targetPlayer = target.asPlayer() + if(!targetPlayer.isActive || targetPlayer.locks.isInteractionLocked) { + sendMessage(player, "This player is busy.") + throw IllegalStateException() + } + if(!targetPlayer.settings.isAcceptAid) { + sendMessage(player, "This player is not accepting any aid.") + throw IllegalStateException() + } + if(10 >= player.skills.lifepoints) { + sendMessage(player, "You need more hitpoints to cast this spell.") + throw IllegalStateException() + } + player.face(targetPlayer) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_ENERGY_TRANSFER_4411, Graphics.LUNAR_SPELLBOOK_ENERGY_TRANSFER_738, 90, Sounds.LUNAR_ENERGY_TRANSFER_2885) + 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) + 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/java/core/game/node/entity/skill/magic/lunar/MagicImbueSpell.java b/Server/src/main/content/global/skill/magic/lunar/MagicImbueSpell.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/skill/magic/lunar/MagicImbueSpell.java rename to Server/src/main/content/global/skill/magic/lunar/MagicImbueSpell.java index 312467fea..79512f7c4 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/MagicImbueSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/MagicImbueSpell.java @@ -1,18 +1,21 @@ -package core.game.node.entity.skill.magic.lunar; +package content.global.skill.magic.lunar; +import core.game.node.entity.combat.spell.MagicSpell; +import core.game.node.entity.combat.spell.Runes; import core.plugin.Initializable; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; import core.game.node.Node; import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; +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 rs09.game.world.GameWorld; +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,6 +64,7 @@ public class MagicImbueSpell extends MagicSpell { player.lock(ANIMATION.getDuration() + 1); player.graphics(GRAPHIC); player.animate(ANIMATION); + playAudio(player, Sounds.LUNAR_EMBUE_RUNES_2888); player.getPacketDispatch().sendMessage("You are charged to combine runes!"); return true; } diff --git a/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java b/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java new file mode 100644 index 000000000..61aeee5f7 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java @@ -0,0 +1,97 @@ +package content.global.skill.magic.lunar; + +import content.data.consumables.Consumables; +import core.game.consumable.Potion; +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.bots.AIPlayer; +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.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 { + + private static final Animation ANIMATION = new Animation(4413); + private static final Graphics GRAPHICS = new Graphics(733, 130); + public static final int VIAL = 229; + public StatBoostSpell() { + super(SpellBook.LUNAR, 84, 88, null, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 3), new Item(Runes.EARTH_RUNE.getId(), 12), new Item(Runes.WATER_RUNE.getId(), 10) }); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + SpellBook.LUNAR.register(26, this); + return this; + } + + @Override + public boolean cast(Entity entity, Node target) { + final Player player = ((Player) entity); + Item item = ((Item) target); + 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."); + return false; + } + if (!item.getDefinition().isTradeable() || item.getName().toLowerCase().contains("restore") || item.getName().toLowerCase().contains("zamorak") || item.getName().toLowerCase().contains("saradomin") || item.getName().toLowerCase().contains("combat")) { + player.getPacketDispatch().sendMessage("You can't cast this spell on that item."); + return false; + } + List pl = RegionManager.getLocalPlayers(player, 1); + int plSize = pl.size() - 1; + int doses = potion.getDose(item); + if (pl.size() == 0) { + return false; + } + if (!super.meetsRequirements(player, true, false)) { + return false; + } + int size = 0; + for (Player players : pl) { + Player o = (Player) players; + if (size >= doses) break; + if (!o.isActive() || o.getLocks().isInteractionLocked() || o == player) { + continue; + } + if (!o.getSettings().isAcceptAid() && !(o instanceof AIPlayer)) { + continue; + } + o.graphics(GRAPHICS); + playGlobalAudio(o.getLocation(), Sounds.LUNAR_STRENGTH_SHARE2_2902); + potion.getEffect().activate(o); + size++; + } + 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); + playGlobalAudio(player.getLocation(), Sounds.LUNAR_STRENGTH_SHARE_2901); + player.animate(ANIMATION); + player.graphics(GRAPHICS); + player.getInventory().remove(item); + 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 new file mode 100644 index 000000000..1f984d2da --- /dev/null +++ b/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java @@ -0,0 +1,113 @@ +package content.global.skill.magic.lunar; + +import content.data.consumables.Consumables; +import core.game.consumable.Potion; +import core.plugin.Initializable; +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.bots.AIPlayer; +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.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 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) }); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + SpellBook.LUNAR.register(27, this); + return this; + } + + @Override + public boolean cast(Entity entity, Node target) { + final Player player = ((Player) entity); + Item item = ((Item) target); + 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."); + return false; + } + 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, 2); + int plSize = pl.size() - 1; + int doses = potion.getDose(item); + if (pl.size() == 0) { + return false; + } + if (!super.meetsRequirements(player, true, false)) { + return false; + } + int size = 0; + for (Player players : pl) { + if (size >= doses) break; + Player o = (Player) players; + if (!o.isActive() || o.getLocks().isInteractionLocked() || o == player) { + continue; + } + if (!o.getSettings().isAcceptAid() && !(o instanceof AIPlayer)) { + continue; + } + o.graphics(GRAPHICS); + potion.getEffect().activate(o); + size++; + } + 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); + playGlobalAudio(player.getLocation(), Sounds.LUNAR_STAT_SHARE_2899); + player.animate(ANIMATION); + player.graphics(GRAPHICS); + player.getInventory().remove(item); + 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(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/java/core/game/node/entity/skill/magic/lunar/VengeanceSpell.java b/Server/src/main/content/global/skill/magic/lunar/VengeanceSpell.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/skill/magic/lunar/VengeanceSpell.java rename to Server/src/main/content/global/skill/magic/lunar/VengeanceSpell.java index 44579d70a..e06821811 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/VengeanceSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/VengeanceSpell.java @@ -1,18 +1,22 @@ -package core.game.node.entity.skill.magic.lunar; +package content.global.skill.magic.lunar; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; +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.equipment.SpellType; +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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.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 ? 2907 : 2908); + playGlobalAudio(p.getLocation(), vengOther ? Sounds.LUNAR_VENGENCE_OTHER_2908 : Sounds.LUNAR_VENGENCE_2907); return true; } diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/AirSpell.java b/Server/src/main/content/global/skill/magic/modern/AirSpell.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/combat/spell/AirSpell.java rename to Server/src/main/content/global/skill/magic/modern/AirSpell.java index b9fe9afe5..07cfcf182 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/AirSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/AirSpell.java @@ -1,11 +1,11 @@ -package core.game.node.entity.combat.spell; +package content.global.skill.magic.modern; import core.plugin.Initializable; -import core.game.node.entity.skill.magic.Runes; +import core.game.node.entity.combat.spell.Runes; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +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; @@ -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/java/core/game/node/entity/combat/spell/BindSpell.java b/Server/src/main/content/global/skill/magic/modern/BindSpell.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/combat/spell/BindSpell.java rename to Server/src/main/content/global/skill/magic/modern/BindSpell.java index bbd7aa8c5..83352a107 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/BindSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/BindSpell.java @@ -1,21 +1,22 @@ -package core.game.node.entity.combat.spell; +package content.global.skill.magic.modern; import core.plugin.Initializable; -import core.game.node.entity.skill.magic.Runes; +import core.game.node.entity.combat.spell.Runes; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +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.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.node.item.Item; -import rs09.game.world.GameWorld; +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/ChargeSpell.java b/Server/src/main/content/global/skill/magic/modern/ChargeSpell.java new file mode 100644 index 000000000..4aa2b1c1b --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/ChargeSpell.java @@ -0,0 +1,60 @@ +package content.global.skill.magic.modern; + +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.combat.spell.MagicSpell; +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; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; + +/** + * Represents the charge spell magic spell. + * @author Emperor + * @version 1.0 + */ +@Initializable +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(Sounds.CHARGE_1651), new Item[] { Runes.FIRE_RUNE.getItem(3), Runes.BLOOD_RUNE.getItem(3), Runes.AIR_RUNE.getItem(3) }); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + SpellBook.MODERN.register(58, this); + return this; + } + + @Override + public boolean cast(Entity entity, Node target) { + final Player p = (Player) entity; + if (p.getLocks().isLocked("charge_cast")) { + p.getPacketDispatch().sendMessage("You need to wait for the spell to recharge."); + return false; + } + if (!meetsRequirements(entity, true, true)) { + return false; + } + p.getLocks().lock("charge_cast", 100); + visualize(entity, target); + // 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 new file mode 100644 index 000000000..582795a81 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java @@ -0,0 +1,71 @@ +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; +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.npc.NPC; +import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.SpellBookManager.SpellBook; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; +import core.plugin.Initializable; +import core.plugin.Plugin; +import org.rs09.consts.NPCs; +import org.rs09.consts.Sounds; + +import java.util.Arrays; +import java.util.List; + +/** + * Handles the crumble undead spell. + * @author Emperor + * @version 1.0 + */ +@Initializable +public final class CrumbleUndead extends CombatSpell { + + /** + * Constructs a new {@code CrumbleUndead} {@code Object}. + */ + public CrumbleUndead() { + 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; + } + return super.cast(entity, target); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + SpellBook.MODERN.register(22, this); + return this; + } + + @Override + public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { + return type.getImpactAmount(entity, victim, 0); + } + +} diff --git a/Server/src/main/content/global/skill/magic/modern/CurseSpells.java b/Server/src/main/content/global/skill/magic/modern/CurseSpells.java new file mode 100644 index 000000000..6b76c0488 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/CurseSpells.java @@ -0,0 +1,201 @@ +package content.global.skill.magic.modern; + +import core.game.node.entity.skill.Skills; +import core.game.node.entity.combat.spell.Runes; +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; + +/** + * Represents the curse spells. + * @author Emperor + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class CurseSpells extends CombatSpell { + + /** + * The start graphic for Earth strike. + */ + private static final Graphics CONFUSE_START = new Graphics(102, 96); + + /** + * The projectile for Earth strike. + */ + private static final Projectile CONFUSE_PROJECTILE = Projectile.create((Entity) null, null, 103, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Earth strike. + */ + private static final Graphics CONFUSE_END = new Graphics(104, 96); + + /** + * The start graphic for Earth strike. + */ + private static final Graphics WEAKEN_START = new Graphics(105, 96); + + /** + * The projectile for Earth strike. + */ + private static final Projectile WEAKEN_PROJECTILE = Projectile.create((Entity) null, null, 106, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Earth strike. + */ + private static final Graphics WEAKEN_END = new Graphics(107, 96); + + /** + * The start graphic for Earth strike. + */ + private static final Graphics CURSE_START = new Graphics(108, 96); + + /** + * The projectile for Earth strike. + */ + private static final Projectile CURSE_PROJECTILE = Projectile.create((Entity) null, null, 109, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Earth strike. + */ + private static final Graphics CURSE_END = new Graphics(110, 96); + + /** + * The start graphic for Earth strike. + */ + private static final Graphics VULNER_START = new Graphics(167, 96); + + /** + * The projectile for Earth strike. + */ + private static final Projectile VULNER_PROJECTILE = Projectile.create((Entity) null, null, 168, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Earth strike. + */ + private static final Graphics VULNER_END = new Graphics(169, 96); + + /** + * The start graphic for Earth strike. + */ + private static final Graphics ENFEEBLE_START = new Graphics(170, 96); + + /** + * The projectile for Earth strike. + */ + private static final Projectile ENFEEBLE_PROJECTILE = Projectile.create((Entity) null, null, 171, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Earth strike. + */ + private static final Graphics ENFEEBLE_END = new Graphics(172, 96); + + /** + * The start graphic for Earth strike. + */ + private static final Graphics STUN_START = new Graphics(173, 96); + + /** + * The projectile for Earth strike. + */ + private static final Projectile STUN_PROJECTILE = Projectile.create((Entity) null, null, 174, 40, 36, 52, 75, 15, 11); + + /** + * The end graphic for Earth strike. + */ + private static final Graphics STUN_END = new Graphics(107, 96); + + /** + * The cast animation. + */ + private static final Animation LOW_ANIMATION = new Animation(716, Priority.HIGH); + + /** + * The cast animation. + */ + private static final Animation HIGH_ANIMATION = new Animation(729, Priority.HIGH); + + /** + * Constructs a new {@code EarthSpell} {@Code Object} + */ + public CurseSpells() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code CurseSpells} {@code Object}. + * @param type the type. + * @param level the level. + * @param sound the sound. + * @param start the start. + * @param projectile the projectile. + * @param end the end. + * @param runes the 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 + public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { + return 1; + } + + @Override + public void fireEffect(Entity entity, Entity victim, BattleState state) { + if (state.getEstimatedHit() == -1) { + return; + } + state.setEstimatedHit(-2); + switch (getType()) { + case CONFUSE: + victim.getSkills().drainLevel(Skills.ATTACK, 0.05, 0.05); + break; + case WEAKEN: + victim.getSkills().drainLevel(Skills.STRENGTH, 0.05, 0.05); + break; + case CURSE: + victim.getSkills().drainLevel(Skills.DEFENCE, 0.05, 0.05); + break; + case VULNERABILITY: + victim.getSkills().drainLevel(Skills.DEFENCE, 0.10, 0.10); + break; + case ENFEEBLE: + victim.getSkills().drainLevel(Skills.STRENGTH, 0.10, 0.10); + break; + case STUN: + victim.getSkills().drainLevel(Skills.ATTACK, 0.10, 0.10); + break; + default: + } + } + + @Override + public void addExperience(Entity entity, int hit) { + entity.getSkills().addExperience(Skills.MAGIC, getExperience()); + } + + @Override + public Plugin newInstance(SpellType type) throws Throwable { + 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/java/core/game/node/entity/combat/spell/EarthSpell.java b/Server/src/main/content/global/skill/magic/modern/EarthSpell.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/combat/spell/EarthSpell.java rename to Server/src/main/content/global/skill/magic/modern/EarthSpell.java index 0a47981a0..3da9d5083 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/EarthSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/EarthSpell.java @@ -1,10 +1,10 @@ -package core.game.node.entity.combat.spell; +package content.global.skill.magic.modern; -import core.game.node.entity.skill.magic.Runes; +import core.game.node.entity.combat.spell.Runes; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +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; @@ -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/java/core/game/node/entity/skill/magic/EnchantCrossbowSpell.java b/Server/src/main/content/global/skill/magic/modern/EnchantCrossbowSpell.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/skill/magic/EnchantCrossbowSpell.java rename to Server/src/main/content/global/skill/magic/modern/EnchantCrossbowSpell.java index 4feae9aba..c5ad348d7 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/EnchantCrossbowSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/EnchantCrossbowSpell.java @@ -1,9 +1,10 @@ -package core.game.node.entity.skill.magic; +package content.global.skill.magic.modern; import core.game.component.Component; import core.game.node.Node; import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; +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.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/FireSpell.java b/Server/src/main/content/global/skill/magic/modern/FireSpell.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/combat/spell/FireSpell.java rename to Server/src/main/content/global/skill/magic/modern/FireSpell.java index 97b8138e7..7feb995bd 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/FireSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/FireSpell.java @@ -1,10 +1,10 @@ -package core.game.node.entity.combat.spell; +package content.global.skill.magic.modern; -import core.game.node.entity.skill.magic.Runes; +import core.game.node.entity.combat.spell.Runes; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +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; @@ -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 new file mode 100644 index 000000000..8b492fdd8 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/GodSpells.java @@ -0,0 +1,224 @@ +package content.global.skill.magic.modern; + +import core.cache.def.impl.ItemDefinition; +import core.game.container.impl.EquipmentContainer; +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.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. + * @author Emperor + * @author 'Vexia + */ +@Initializable +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. + */ + private static final Graphics SARA_START = null; + + /** + * The projectile for Air strike. + */ + private static final Projectile SARA_PROJECTILE = null; + + /** + * The end graphic for Air strike. + */ + private static final Graphics SARA_END = new Graphics(76, 0); + + /** + * The start graphic for Air strike. + */ + private static final Graphics GUTHIX_START = null; + + /** + * The projectile for Air strike. + */ + private static final Projectile GUTHIX_PROJECTILE = null; + + /** + * The end graphic for Air strike. + */ + private static final Graphics GUTHIX_END = new Graphics(77, 0); + + /** + * The start graphic for Air strike. + */ + private static final Graphics ZAM_START = null; + + /** + * The projectile for Air strike. + */ + private static final Projectile ZAM_PROJECTILE = null; + + /** + * The end graphic for Air strike. + */ + private static final Graphics ZAM_END = new Graphics(78, 0); + + /** + * The cast animation. + */ + private static final Animation ANIMATION = new Animation(811, Priority.HIGH); + + /** + * Constructs a new {@code AirSpell} {@Code Object} + */ + public GodSpells() { + /* + * empty. + */ + } + + /** + * Constructs a new {@code GodSpells} {@code Object}. + * @param type the type. + * @param sound the sound. + * @param start the start. + * @param projectile the projectile. + * @param end the end. + * @param runes the 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) { + return true; + } + if (caster instanceof Player) { + int staffId = ((Player) caster).getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId(); + 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."); + return false; + } + + if (staffId != required && !(index == 1 && staffId == Items.VOID_KNIGHT_MACE_8841)) { + if (message) { + ((Player) caster).getPacketDispatch().sendMessage("You need to wear a " + ItemDefinition.forId(required).getName() + " to cast this spell."); + } + return false; + } + } + 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); + if (entity instanceof NPC) { + NPC n = (NPC) entity; + if (n.getId() > 911 && n.getId() < 915 || (n.getId() > 906 && n.getId() < 912)) { + n.getAnimator().forceAnimation(n.getProperties().getAttackAnimation()); + } + } + } + @Override + public void visualizeImpact(Entity entity, Entity target, BattleState state) { + if (entity instanceof Player) { + int index = getSpellIndex(); + Player p = (Player) entity; + if (p.getSavedData().getActivityData().getGodCasts()[index] < 100) { + p.getSavedData().getActivityData().getGodCasts()[index]++; + if (p.getSavedData().getActivityData().getGodCasts()[index] >= 100) { + 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; + } + } + target.graphics(endGraphic); + playGlobalAudio(target.getLocation(), impactAudio); + } + + @Override + public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { + return getType().getImpactAmount(entity, victim, 0); + } + + @Override + public Plugin newInstance(SpellType type) throws Throwable { + 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/IbanBlast.java b/Server/src/main/content/global/skill/magic/modern/IbanBlast.java new file mode 100644 index 000000000..5a35d7e1d --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/IbanBlast.java @@ -0,0 +1,54 @@ +package content.global.skill.magic.modern; + +import core.game.container.impl.EquipmentContainer; +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.Player; +import core.game.node.entity.player.link.SpellBookManager.SpellBook; +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. + * @author Emperor + * @version 1.0 + */ +@Initializable +public final class IbanBlast extends CombatSpell { + + /** + * Constructs a new {@code IbanBlast} {@code Object}. + */ + public IbanBlast() { + 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 + public boolean cast(Entity entity, Node target) { + if (((Player) entity).getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId() != 1409) { + ((Player) entity).getPacketDispatch().sendMessage("You need to wear Iban's staff to cast this spell."); + return false; + } + return super.cast(entity, target); + } + + @Override + public Plugin newInstance(SpellType arg) throws Throwable { + SpellBook.MODERN.register(29, this); + return this; + } + + @Override + public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { + return type.getImpactAmount(entity, victim, 0); + } +} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/MagicDart.java b/Server/src/main/content/global/skill/magic/modern/MagicDart.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/combat/spell/MagicDart.java rename to Server/src/main/content/global/skill/magic/modern/MagicDart.java index ea6a75aa8..3d01de0dc 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/MagicDart.java +++ b/Server/src/main/content/global/skill/magic/modern/MagicDart.java @@ -1,13 +1,13 @@ -package core.game.node.entity.combat.spell; +package content.global.skill.magic.modern; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.Runes; +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.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +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; @@ -28,7 +28,7 @@ public class MagicDart extends CombatSpell { * Constructs a new {@code MagicDart} {@code Object}. */ public MagicDart() { - super(SpellType.MAGIC_DART, SpellBook.MODERN, 50, 61.0, 218, 219, new Animation(1576, Priority.HIGH), null, Projectile.create((Entity) null, null, 330, 40, 36, 52, 75, 15, 11), new Graphics(331, 96), Runes.DEATH_RUNE.getItem(1), Runes.MIND_RUNE.getItem(4)); + super(SpellType.MAGIC_DART, SpellBook.MODERN, 50, 30.0, 218, 219, new Animation(1576, Priority.HIGH), null, Projectile.create((Entity) null, null, 330, 40, 36, 52, 75, 15, 11), new Graphics(331, 96), Runes.DEATH_RUNE.getItem(1), Runes.MIND_RUNE.getItem(4)); } @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 new file mode 100644 index 000000000..777beee18 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt @@ -0,0 +1,386 @@ +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 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.diary.DiaryType +import core.game.node.entity.skill.Skills +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 + +class ModernListeners : SpellListener("modern"){ + override fun defineListeners() { + onCast(Modern.HOME_TELEPORT, NONE){ player, _ -> + if (!getAttribute(player, "tutorial:complete", false)) { + return@onCast + } + requires(player) + homeTeleport(player, ServerConstants.HOME_LOCATION ?: Location(3222, 3218, 0)) + setDelay(player,true) + } + + onCast(Modern.VARROCK_TELEPORT, NONE){ player, _-> + requires(player,25, arrayOf(Item(Items.FIRE_RUNE_554),Item(Items.AIR_RUNE_556,3),Item(Items.LAW_RUNE_563))) + val alternateTeleport = getAttribute(player, "diaries:varrock:alttele", false) + val dest = if(alternateTeleport) Location.create(3165, 3472, 0) else Location.create(3213, 3424, 0) + sendTeleport(player,35.0, dest) + } + + onCast(Modern.LUMBRIDGE_TELEPORT, NONE){ player, _ -> + requires(player,31, arrayOf(Item(Items.EARTH_RUNE_557),Item(Items.AIR_RUNE_556,3),Item(Items.LAW_RUNE_563))) + sendTeleport(player,41.0,Location.create(3221, 3219, 0)) + } + + onCast(Modern.FALADOR_TELEPORT, NONE){ player, _ -> + requires(player,37, arrayOf(Item(Items.WATER_RUNE_555),Item(Items.AIR_RUNE_556,3),Item(Items.LAW_RUNE_563))) + sendTeleport(player,47.0,Location.create(2965, 3378, 0)) + } + + 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) + 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, _ -> + 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(2795, 2798, 1)) + } + + onCast(Modern.TELEPORT_TO_HOUSE, NONE){ player, _ -> + requires(player,40, arrayOf(Item(Items.LAW_RUNE_563), Item(Items.AIR_RUNE_556), Item(Items.EARTH_RUNE_557))) + attemptHouseTeleport(player) + } + + onCast(Modern.LOW_ALCHEMY, ITEM){ player, node -> + val item = node?.asItem() ?: return@onCast + requires(player,21, arrayOf(Item(Items.FIRE_RUNE_554,3),Item(Items.NATURE_RUNE_561))) + alchemize(player,item,high = false) + } + + onCast(Modern.HIGH_ALCHEMY, ITEM){ player, node -> + val item = node?.asItem() ?: return@onCast + requires(player,55, arrayOf(Item(Items.FIRE_RUNE_554,5),Item(Items.NATURE_RUNE_561,1))) + alchemize(player,item,high = true) + } + + onCast(Modern.SUPERHEAT, ITEM){ player, node -> + val item = node?.asItem() ?: return@onCast + requires(player,43, arrayOf(Item(Items.FIRE_RUNE_554,4),Item(Items.NATURE_RUNE_561,1))) + superheat(player,item) + } + + onCast(Modern.BONES_TO_BANANAS, NONE){ player, _ -> + requires(player,15, arrayOf(Item(Items.EARTH_RUNE_557,2), Item(Items.WATER_RUNE_555,2), Item(Items.NATURE_RUNE_561,1))) + boneConvert(player,true) + } + + onCast(Modern.BONES_TO_PEACHES, NONE){ player, _ -> + 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){ + val isInMTA = player.zoneMonitor.isInZone("Creature Graveyard") + if(isInMTA && player.getAttribute("tablet-spell",false)){ + player.sendMessage("You can not use this tablet in the Mage Training Arena.") + return + } + + if(!bananas && !player.savedData.activityData.isBonesToPeaches && !player.getAttribute("tablet-spell",false)){ + player.sendMessage("You can only learn this spell from the Mage Training Arena.") + return + } + + val bones = if(isInMTA) intArrayOf(6904,6905,6906,6907) else Bones.values().map { it.itemId }.toIntArray() + + for(item in player.inventory.toArray()){ + item ?: continue + if(isInMTA){ + if(bones.contains(item.id)){ + val inInventory = player.inventory.getAmount(item.id) + val amount = inInventory * (content.minigame.mta.impl.GraveyardZone.BoneType.forItem(Item(item.id)).ordinal + 1) + if(amount > 0){ + player.inventory.remove(Item(item.id,inInventory)) + player.inventory.add(Item(if(bananas) Items.BANANA_1963 else Items.PEACH_6883,amount)) + } + } + } else { + if(bones.contains(item.id)){ + val inInventory = player.inventory.getAmount(item.id) + player.inventory.remove(Item(item.id,inInventory)) + player.inventory.add(Item(if(bananas) Items.BANANA_1963 else Items.PEACH_6883,inInventory)) + } + } + } + visualizeSpell(player,BONE_CONVERT_ANIM, BONE_CONVERT_GFX) + 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.equals("coal", true)){ + player.sendMessage("You can only cast this spell on ore.") + return + } + + // Elemental Workshop I special interaction + if(item.id == Items.ELEMENTAL_ORE_2892) { + sendMessage(player, "Even this spell is not hot enough to heat this item.") + return + } + + 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 + } + + player.lock(3) + removeRunes(player) + addXP(player,53.0) + playAudio(player, Sounds.SUPERHEAT_ALL_190) + showMagicTab(player) + player.pulseManager.run(SmeltingPulse(player, item, bar, 1, true)) + setDelay(player,false) + } + + 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 false + } + + val coins = Item(995, item.definition.getAlchemyValue(high)) + if (item.amount > 1 && coins.amount > 0 && !player.inventory.hasSpaceFor(coins)) { + player.sendMessage("Not enough space in your inventory!") + return false + } + + if (player.pulseManager.current !is MovementPulse) { + player.pulseManager.clear() + } + + if (explorersRing) { + visualize(player, LOW_ALCH_ANIM, EXPLORERS_RING_GFX) + } else { + val weapon = getItemFromEquipment(player, EquipmentSlot.WEAPON) + if (weapon != null && weapon.id in MagicStaff.FIRE_RUNE.staves) { + visualize(player, if (high) HIGH_ALCH_STAFF_ANIM else LOW_ALCH_STAFF_ANIM, if (high) HIGH_ALCH_STAFF_GFX else LOW_ALCH_STAFF_GFX) + } else { + visualize(player, if (high) HIGH_ALCH_ANIM else LOW_ALCH_ANIM, if (high) HIGH_ALCH_GFX else LOW_ALCH_GFX) + } + } + playAudio(player, if (high) Sounds.HIGH_ALCHEMY_97 else Sounds.LOW_ALCHEMY_98) + player.dispatch(ItemAlchemizationEvent(item.id, high)) + if (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.isTeleBlocked){ + player.removeAttribute("spell:runes") + player.sendMessage("A magical force prevents you from teleporting.") + return + } + + val teleType = TeleportManager.TeleportType.NORMAL + + if (player.teleporter.send(location, teleType)) { + player.dispatch(TeleportEvent(teleType, TeleportMethod.SPELL, -1, location)) + + removeRunes(player) + addXP(player, xp) + setDelay(player, true) + } + } + + private fun attemptHouseTeleport(player: Player){ + if(player.isTeleBlocked){ + player.removeAttribute("spell:runes") + player.sendMessage("A magical force prevents you from teleporting.") + return + } + 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/java/core/game/node/entity/combat/spell/SonicWaveSpell.java b/Server/src/main/content/global/skill/magic/modern/SonicWaveSpell.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/combat/spell/SonicWaveSpell.java rename to Server/src/main/content/global/skill/magic/modern/SonicWaveSpell.java index 889dd32ae..af397dff1 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/SonicWaveSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/SonicWaveSpell.java @@ -1,9 +1,9 @@ -package core.game.node.entity.combat.spell; +package content.global.skill.magic.modern; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +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.player.link.SpellBookManager.SpellBook; import core.game.node.item.Item; 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/java/core/game/node/entity/skill/magic/TeleblockSpell.java b/Server/src/main/content/global/skill/magic/modern/TeleblockSpell.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/skill/magic/TeleblockSpell.java rename to Server/src/main/content/global/skill/magic/modern/TeleblockSpell.java index 937d59383..baac8b57d 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/TeleblockSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/TeleblockSpell.java @@ -1,22 +1,24 @@ -package core.game.node.entity.skill.magic; +package content.global.skill.magic.modern; +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.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +import core.game.node.entity.combat.spell.CombatSpell; +import core.game.node.entity.combat.spell.SpellType; 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.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 @@ -76,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 @@ -89,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; } @@ -111,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) && state.getEstimatedHit() > 0){ + 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/java/core/game/node/entity/skill/magic/TeleotherSpells.java b/Server/src/main/content/global/skill/magic/modern/TeleotherSpells.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/skill/magic/TeleotherSpells.java rename to Server/src/main/content/global/skill/magic/modern/TeleotherSpells.java index 472468328..9219295f4 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/TeleotherSpells.java +++ b/Server/src/main/content/global/skill/magic/modern/TeleotherSpells.java @@ -1,9 +1,11 @@ -package core.game.node.entity.skill.magic; +package content.global.skill.magic.modern; +import core.game.node.entity.combat.spell.Runes; import core.game.component.Component; import core.game.node.Node; import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; +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.audio.Audio; @@ -13,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. @@ -48,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; } @@ -72,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; } @@ -88,6 +91,7 @@ public final class TeleotherSpells extends MagicSpell { return false; } visualize(entity, target); + Graphics.send(new Graphics(342), o.getLocation()); p.faceLocation(o.getLocation()); o.setAttribute("t-o_location", location); o.getPacketDispatch().sendString(p.getUsername(), 326,1); diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/WaterSpell.java b/Server/src/main/content/global/skill/magic/modern/WaterSpell.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/combat/spell/WaterSpell.java rename to Server/src/main/content/global/skill/magic/modern/WaterSpell.java index 09f2d213e..8096c4038 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/WaterSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/WaterSpell.java @@ -1,11 +1,11 @@ -package core.game.node.entity.combat.spell; +package content.global.skill.magic.modern; import core.plugin.Initializable; -import core.game.node.entity.skill.magic.Runes; +import core.game.node.entity.combat.spell.Runes; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; +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; @@ -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 new file mode 100644 index 000000000..69e0fa5e0 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/spellconsts/Lunar.kt @@ -0,0 +1,44 @@ +package content.global.skill.magic.spellconsts + +object Lunar { + const val BARBARIAN_TELEPORT = 0 + const val CURE_OTHER = 1 + const val FERTILE_SOIL = 2 + const val CURE_GROUP = 3 + const val NPC_CONTACT = 4 + const val ENERGY_TRANSFER = 5 + const val MONSTER_EXAMINE = 6 + const val HUMIDIFY = 7 + const val HUNTER_KIT = 8 + const val STAT_SPY = 9 + const val DREAM = 10 + const val PLANK_MAKE = 11 + const val SPELLBOOK_SWAP = 12 + const val MAGIC_IMBUE = 13 + const val VENGEANCE = 14 + const val BAKE_PIE = 15 + const val HOME_TELEPORT = 16 + const val FISHING_GUILD_TELEPORT = 17 + const val KHAZARD_TELEPORT = 18 + const val VENGEANCE_OTHER = 19 + const val MOONCLAN_TELEPORT = 20 + const val CATHERBY_TELEPORT = 21 + const val STRING_JEWELLERY = 22 + const val CURE_ME = 23 + const val WATERBIRTH_TELEPORT = 24 + const val SUPERGLASS_MAKE = 25 + const val BOOST_POTION_SHARE = 26 + const val STAT_RESTORE_POT_SHARE = 27 + const val ICE_PLATEAU_TELEPORT = 28 + const val HEAL_OTHER = 29 + const val HEAL_GROUP = 30 + const val OURANIA_TELEPORT = 31 + const val CURE_PLANT = 32 + 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 new file mode 100644 index 000000000..fa3d3f036 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/spellconsts/Modern.kt @@ -0,0 +1,32 @@ +package content.global.skill.magic.spellconsts + +object Modern { + const val HOME_TELEPORT = 0 + 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 + const val CURSE = 19 + const val FALADOR_TELEPORT = 21 + 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 +} 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/Bones.kt b/Server/src/main/content/global/skill/prayer/Bones.kt new file mode 100644 index 000000000..755a8062b --- /dev/null +++ b/Server/src/main/content/global/skill/prayer/Bones.kt @@ -0,0 +1,116 @@ +package content.global.skill.prayer + +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, + + /** + * 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 { + /** + * Holds all bones. + */ + private val bones = HashMap() + + /** + * Gets the bones for the bone meal. + * @param itemId the item. + * @return the bones. + */ + @JvmStatic + fun forBoneMeal(itemId: Int): Bones? { + for (bone in values()) { + if (bone.bonemealId == itemId) { + return bone + } + } + return null + } + + /** + * Gets the bone ids. + * @return the ids. + */ + val array: IntArray + @JvmStatic get() { + val list: MutableList = ArrayList(20) + for (i in bones.keys) { + list.add(i) + } + return list.toIntArray() + } + + /** + * Get the bone. + * @param itemId The item id. + * @return The bone. + */ + @JvmStatic + fun forId(itemId: Int): Bones? { + return bones[itemId] + } + + /** + * Construct the bones. + */ + init { + for (bone in values()) { + bones[bone.itemId] = bone + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/prayer/PrayerAltarListener.kt b/Server/src/main/content/global/skill/prayer/PrayerAltarListener.kt new file mode 100644 index 000000000..71982f578 --- /dev/null +++ b/Server/src/main/content/global/skill/prayer/PrayerAltarListener.kt @@ -0,0 +1,166 @@ +package content.global.skill.prayer + +import core.api.* +import core.game.event.PrayerPointsRechargeEvent +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds +import content.data.Quests + +class PrayerAltarListener : InteractionListener { + override fun defineListeners() { + on(altars, IntType.SCENERY, "pray", "pray-at") { player, node -> + if (node.id == Scenery.TRIBAL_STATUE_3863 && !hasRequirement(player, Quests.TAI_BWO_WANNAI_TRIO)) { + // https://runescape.wiki/w/Tribal_Statue?oldid=1940922 + return@on true + } + + if (!pray(player, node)) { + if (node.id != Scenery.ELIDINIS_STATUETTE_10439) return@on true + } + + if (node.id == Scenery.ELIDINIS_STATUETTE_10439) { + // https://youtu.be/kEwYhnrQCu8?t=727 & https://youtu.be/b9Wy1JWDes8?t=413 + setTempLevel(player, Skills.HITPOINTS, getStatLevel(player, Skills.HITPOINTS).plus(7)) + sendMessage(player, "You feel much healthier after praying in the shrine.") + } + + if (node.id == Scenery.CHAOS_ALTAR_412) { + lock(player, 4) + queueScript(player, 4, QueueStrength.STRONG) { + sendMessage(player, "It's a trap!") + teleport(player, Location(2583, 9576, 0), TeleportManager.TeleportType.INSTANT) + return@queueScript stopExecuting(player) + } + } + return@on true + } + + on(Scenery.CHAOS_ALTAR_61, IntType.SCENERY, "check") { player, _ -> + if (getQuestStage(player, Quests.MERLINS_CRYSTAL) == 70) { + sendDialogue(player, "You find a small inscription at the bottom of the altar. It reads: 'Snarthon Candtrick Termanto'.") + setQuestStage(player, Quests.MERLINS_CRYSTAL, 80) + } else { + sendMessage(player, "An altar of the evil god Zamorak.") + } + return@on true + } + } + + private fun pray(player: Player, node: Node): Boolean { + val prayerLevel = getStatLevel(player, Skills.PRAYER).plus(if (node.id in boostedAltars) 2 else 0) + + if (player.skills.prayerPoints >= prayerLevel.toDouble()) { + sendMessage(player, "You already have full prayer points.") + return false + } + + lock(player, 3) + animate(player, 645) + playAudio(player, Sounds.PRAYER_RECHARGE_2674) + setTempLevel(player, Skills.PRAYER, prayerLevel) + sendMessage(player, "You recharge your prayer points.") + player.dispatch(PrayerPointsRechargeEvent(node)) + return true + } + + companion object { + private val altars = intArrayOf( + Scenery.ALTAR_409, + Scenery.ALTAR_2478, + Scenery.ALTAR_2479, + Scenery.ALTAR_2480, + Scenery.ALTAR_2481, + Scenery.ALTAR_2482, + Scenery.ALTAR_2483, + Scenery.ALTAR_2484, + Scenery.ALTAR_2485, + Scenery.ALTAR_2486, + Scenery.ALTAR_2487, + Scenery.ALTAR_2488, + Scenery.ALTAR_2489, + Scenery.ALTAR_2640, + Scenery.ALTAR_4008, + Scenery.ALTAR_8749, + Scenery.ALTAR_10639, + Scenery.ALTAR_10640, + Scenery.ALTAR_13179, + Scenery.ALTAR_13180, + Scenery.ALTAR_13181, + Scenery.ALTAR_13182, + Scenery.ALTAR_13183, + Scenery.ALTAR_13184, + Scenery.ALTAR_13185, + Scenery.ALTAR_13186, + Scenery.ALTAR_13187, + Scenery.ALTAR_13188, + Scenery.ALTAR_13189, + Scenery.ALTAR_13190, + Scenery.ALTAR_13191, + Scenery.ALTAR_13192, + Scenery.ALTAR_13193, + Scenery.ALTAR_13194, + Scenery.ALTAR_13195, + Scenery.ALTAR_13196, + Scenery.ALTAR_13197, + Scenery.ALTAR_13198, + Scenery.ALTAR_13199, + Scenery.ALTAR_15050, + Scenery.ALTAR_15051, + Scenery.ALTAR_18254, + Scenery.ALTAR_19145, + Scenery.ALTAR_20377, + Scenery.ALTAR_20378, + Scenery.ALTAR_20379, + Scenery.ALTAR_24343, + Scenery.ALTAR_27306, + Scenery.ALTAR_27307, + Scenery.ALTAR_27308, + Scenery.ALTAR_27309, + Scenery.ALTAR_27334, + Scenery.ALTAR_27338, + Scenery.ALTAR_27339, + Scenery.ALTAR_27661, + Scenery.ALTAR_30624, + Scenery.ALTAR_30726, + Scenery.ALTAR_34616, + Scenery.ALTAR_36972, + Scenery.ALTAR_37630, + Scenery.ALTAR_37901, + Scenery.ALTAR_37902, + Scenery.ALTAR_37903, + Scenery.ALTAR_37904, + Scenery.ALTAR_37905, + Scenery.ALTAR_37906, + Scenery.ALTAR_37907, + Scenery.ALTAR_37908, + Scenery.ALTAR_37909, + Scenery.ALTAR_37910, + Scenery.ALTAR_37911, + Scenery.ALTAR_37912, + Scenery.ALTAR_39547, + Scenery.ALTAR_39842, + Scenery.CHAOS_ALTAR_61, + Scenery.CHAOS_ALTAR_411, + Scenery.CHAOS_ALTAR_412, + Scenery.CHAOS_ALTAR_32079, + Scenery.CHAOS_ALTAR_37990, + Scenery.GORILLA_STATUE_4858, + Scenery.GORILLA_STATUE_4859, + Scenery.ALTAR_OF_GUTHIX_410, + Scenery.ALTAR_OF_GUTHIX_28698, + Scenery.ALTAR_OF_NATURE_3521, + Scenery.TRIBAL_STATUE_3863, + Scenery.ELIDINIS_STATUETTE_10439, + Scenery.DECAYED_ALTAR_37985 + ) + private val boostedAltars = intArrayOf(Scenery.ALTAR_2640, Scenery.ALTAR_OF_NATURE_3521) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/Altar.java b/Server/src/main/content/global/skill/runecrafting/Altar.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/Altar.java rename to Server/src/main/content/global/skill/runecrafting/Altar.java index af7463cb5..487d61e6f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/Altar.java +++ b/Server/src/main/content/global/skill/runecrafting/Altar.java @@ -1,9 +1,12 @@ -package core.game.node.entity.skill.runecrafting; +package content.global.skill.runecrafting; 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/CombinationRune.java b/Server/src/main/content/global/skill/runecrafting/CombinationRune.java new file mode 100644 index 000000000..62760bfdc --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/CombinationRune.java @@ -0,0 +1,130 @@ +package content.global.skill.runecrafting; + +import core.game.node.item.Item; + +/** + * Represents the combination rune. + * @author 'Vexia + */ +public enum CombinationRune { + MIST(new Item(4695), 6, 8.0, new Altar[] { Altar.WATER, Altar.AIR }, Rune.AIR, Rune.WATER), + DUST(new Item(4696), 10, 8.3, new Altar[] { Altar.EARTH, Altar.AIR }, Rune.AIR, Rune.EARTH), + MUD(new Item(4698), 13, 9.3, new Altar[] { Altar.EARTH, Altar.WATER }, Rune.WATER, Rune.EARTH), + SMOKE(new Item(4697), 15, 8.5, new Altar[] { Altar.FIRE, Altar.AIR }, Rune.AIR, Rune.FIRE), + STEAM(new Item(4694), 19, 9.3, new Altar[] { Altar.WATER, Altar.FIRE }, Rune.WATER, Rune.FIRE), + LAVA(new Item(4699), 23, 10.0, new Altar[] { Altar.FIRE, Altar.EARTH }, Rune.EARTH, Rune.FIRE); + + /** + * Represents the rune rewarded. + */ + private final Item rune; + + /** + * Represents the required level. + */ + private final int level; + + /** + * Represents the base experience. + */ + private final double experience; + + /** + * Represents the altars. + */ + private final Altar[] altars; + + /** + * Represents the runes required. + */ + private final Rune[] runes; + + /** + * Constructs a new {@code CombinationRune} {@code Object}. + * @param rune the rune. + * @param level the level. + * @param experience the base experience/ + * @param runes the runes. + */ + CombinationRune(final Item rune, final int level, final double experience, final Altar[] altars, final Rune... runes) { + this.rune = rune; + this.level = level; + this.experience = experience; + this.altars = altars; + this.runes = runes; + } + + /** + * Gets the rune. + * @return The rune. + */ + public final Item getRune() { + return rune; + } + + /** + * Gets the level. + * @return The level. + */ + public final int getLevel() { + return level; + } + + /** + * Gets the experience. + * @return The experience. + */ + public final double getExperience() { + return experience; + } + + /** + * Gets the runes. + * @return The runes. + */ + public final Rune[] getRunes() { + return runes; + } + + /** + * Method used to get the high experience. + * @return the experience incremented. + */ + public final double getHighExperience() { + return (experience % 1 == 0) ? experience + 5 : experience + 8; + } + + /** + * Gets the altars. + * @return The altars. + */ + public Altar[] getAltars() { + return altars; + } + + /** + * Gets the combination rune. + * @param altar the altar. + * @return the combo rune. + */ + public static CombinationRune forAltar(final Altar altar, final Item item) { + for (CombinationRune rune : values()) { + for (Altar alt : rune.getAltars()) { + if (alt == altar) { + String altarElement = alt.name(); + String talismanElement = item.getName().contains("talisman") ? Talisman.forItem(item).name() : Rune.forItem(item).name(); + if (altarElement.equals(talismanElement)) { + continue; + } + for (Rune r : rune.getRunes()) { + if (r.name().equals(talismanElement)) { + return rune; + } + } + continue; + } + } + } + return null; + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/CombinationRunePlugin.java b/Server/src/main/content/global/skill/runecrafting/CombinationRunePlugin.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/CombinationRunePlugin.java rename to Server/src/main/content/global/skill/runecrafting/CombinationRunePlugin.java index 2b9c17a85..bcfb0d2d1 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/CombinationRunePlugin.java +++ b/Server/src/main/content/global/skill/runecrafting/CombinationRunePlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.runecrafting; +package content.global.skill.runecrafting; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/java/core/game/content/dialogue/EnchantTiaraDialogue.java b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraDialogue.java similarity index 81% rename from Server/src/main/java/core/game/content/dialogue/EnchantTiaraDialogue.java rename to Server/src/main/content/global/skill/runecrafting/EnchantTiaraDialogue.java index 5c47866f5..6c596bfd4 100644 --- a/Server/src/main/java/core/game/content/dialogue/EnchantTiaraDialogue.java +++ b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraDialogue.java @@ -1,10 +1,9 @@ -package core.game.content.dialogue; +package content.global.skill.runecrafting; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; + +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; -import core.game.node.entity.skill.runecrafting.Altar; -import core.game.node.entity.skill.runecrafting.EnchantTiaraPulse; -import core.game.node.entity.skill.runecrafting.Talisman; import core.game.interaction.NodeUsageEvent; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; @@ -66,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: - ContentAPI.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/java/core/game/node/entity/skill/runecrafting/EnchantTiaraPlugin.java b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPlugin.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/EnchantTiaraPlugin.java rename to Server/src/main/content/global/skill/runecrafting/EnchantTiaraPlugin.java index 9e5b07a87..f15985d55 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/EnchantTiaraPlugin.java +++ b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.runecrafting; +package content.global.skill.runecrafting; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPulse.java b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPulse.java new file mode 100644 index 000000000..542d776b3 --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPulse.java @@ -0,0 +1,116 @@ +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; +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 + */ +public class EnchantTiaraPulse extends SkillPulse { + + /** + * Represents the Altar. + */ + private final Altar altar; + + /** + * Represents the tiara. + */ + private final Tiara tiara; + + /** + * Represents the item tiara. + */ + private final Item TIARA = new Item(5525); + + private static final Animation ANIMATION = new Animation(791, Animator.Priority.HIGH); + private static final Graphics GRAPHICS = new Graphics(186, 100); + + /** + * Represents the amount. + */ + private int amount; + + /** + * Constructs a new {@code EnchantTiaraPulse.java} {@code Object}. + * @param player the player. + * @param node the node. + */ + 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); // 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 + public boolean checkRequirements() { + if (!player.getInventory().containsItem(TIARA)) { + player.getPacketDispatch().sendMessage("You need a tiara."); + return false; + } + return true; + } + + @Override + public void animate() { + player.animate(ANIMATION); + player.graphics(GRAPHICS); + } + + @Override + public boolean reward() { + if (getDelay() == 1) { + setDelay(2); + return false; + } + if (player.getInventory().remove(TIARA) && player.getInventory().remove(tiara.getTalisman().getTalisman())) { + player.getInventory().add(tiara.getTiara()); + player.getSkills().addExperience(Skills.RUNECRAFTING, tiara.getExperience(), true); + + if (tiara == Tiara.AIR) { + player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 0, 11); + } + // Craft an earth tiara on the Earth Altar + if (tiara == Tiara.EARTH) { + player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 11); + } + } + amount--; + return amount == 0; + } + + @Override + public void message(int type) { + if (type == 1) { + player.getPacketDispatch().sendMessage("You bind the power of the talisman into your tiara."); + } + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/MysteriousRuin.java b/Server/src/main/content/global/skill/runecrafting/MysteriousRuin.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/MysteriousRuin.java rename to Server/src/main/content/global/skill/runecrafting/MysteriousRuin.java index e92a01adf..41089d272 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/MysteriousRuin.java +++ b/Server/src/main/content/global/skill/runecrafting/MysteriousRuin.java @@ -1,7 +1,5 @@ -package core.game.node.entity.skill.runecrafting; +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/PouchManager.kt b/Server/src/main/content/global/skill/runecrafting/PouchManager.kt new file mode 100644 index 000000000..22cc1865f --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/PouchManager.kt @@ -0,0 +1,216 @@ +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 +import core.game.node.item.Item +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.rs09.consts.Items + +/** + * A class for managing rune pouches. + * @param player the player this manager instance belongs to. + * @author Ceikry, Player Name + */ +class PouchManager(val player: Player) { + val pouches = mapOf( + 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 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, Player Name + */ + 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) { + 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()) { + amt = pouch.container.freeSlots() + } + if (amt == pouch.container.freeSlots()) { + sendMessage(player, "Your pouch is full.") //https://www.youtube.com/watch?v=wbYtRwODKTo + } + if (pouch.container.contains(otherEssence,1)) { + sendMessage(player, "You can only store one type of essence in each pouch.") + return + } + + 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 itemId the item ID of the pouch to withdraw from + * @author Ceikry, Player Name + */ + fun withdrawFromPouch(itemId: Int) { + val pouchId = if (isDecayedPouch(itemId)) itemId - 1 else itemId + val pouch = pouches[pouchId] + pouch ?: return + val playerFree = freeSlots(player) + var amount = pouch.currentCap - pouch.container.freeSlots() + 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) { + val pouches = JSONArray() + + for(i in this.pouches) { + val pouch = JSONObject() + pouch.put("id",i.key.toString()) + val items = JSONArray() + for(item in i.value.container.toArray()) { + item ?: continue + val it = JSONObject() + it.put("itemId",item.id.toString()) + it.put("amount",item.amount.toString()) + items.add(it) + } + pouch.put("container",items) + pouch.put("charges",i.value.charges.toString()) + pouch.put("currentCap",i.value.currentCap.toString()) + pouches.add(pouch) + } + 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){ + val pouch = e as JSONObject + val id = pouch["id"].toString().toInt() + val p = pouches[id] + p ?: return + val charges = pouch["charges"].toString().toInt() + val currentCap = pouch["currentCap"].toString().toInt() + p.charges = charges + p.currentCap = currentCap + p.remakeContainer() + for (i in pouch["container"] as JSONArray) { + val it = i as JSONObject + it["itemId"] ?: continue + val item = it["itemId"].toString().toInt() + val amount = it["amount"].toString().toInt() + p.container.add(Item(item,amount)) + } + } + } + + /** + * 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 { + 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 itemId the item ID of the pouch to check + * @author Ceikry, Player Name + */ + 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 == Items.MEDIUM_POUCH_5510) return false + return pouches[pouchId - 1] != null + } + + /** + * A class that represents a runecrafting pouch. + * @author Ceikry + */ + class RCPouch(val capacity: Int, val maxCharges: Int, val levelRequirement: Int) { + var container = Container(capacity) + var currentCap = capacity + var charges = maxCharges + fun remakeContainer() { + this.container = Container(currentCap) + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/Rune.java b/Server/src/main/content/global/skill/runecrafting/Rune.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/Rune.java rename to Server/src/main/content/global/skill/runecrafting/Rune.java index 5f455a3d4..7736c579b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/Rune.java +++ b/Server/src/main/content/global/skill/runecrafting/Rune.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.runecrafting; +package content.global.skill.runecrafting; -import core.game.node.entity.skill.magic.Runes; +import core.game.node.entity.combat.spell.Runes; import core.game.node.item.Item; /** @@ -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 new file mode 100644 index 000000000..c9fa23ef5 --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java @@ -0,0 +1,376 @@ +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; +import core.game.node.entity.player.link.diary.DiaryType; +import core.game.node.entity.skill.SkillPulse; +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.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 { + + /** + * Represent the rune essence item. + */ + private static final Item RUNE_ESSENCE = new Item(1436); + + /** + * Represents the pure essence item. + */ + private static final Item PURE_ESSENCE = new Item(7936); + + /** + * Represents the binding necklace item. + */ + private static final Item BINDING_NECKLACE = new Item(5521); + + /** + * Represents the animation used for this pulse. + */ + private static final Animation ANIMATION = new Animation(791, Priority.HIGH); + + /** + * Represents the graphics used for this pulse. + */ + private static final Graphics GRAPHICS = new Graphics(186, 100); + + /** + * Represents the altar. + */ + private final Altar altar; + + /** + * Represents the rune we're crafting. + */ + private final Rune rune; + + /** + * Represents the combination rune(if any) + */ + private final CombinationRune combo; + + /** + * Represents if it's a combination pulse. + */ + private final boolean combination; + + /** + * Represents the talisman to remove. + */ + private Talisman talisman; + + /** + * Constructs a new {@code RuneCraftPulse} {@code Object}. + * + * @param player the player. + * @param node the node. + * @param altar the altar. + */ + public RuneCraftPulse(Player player, Item node, final Altar altar, final boolean combination, final CombinationRune combo) { + super(player, node); + this.altar = altar; + this.rune = altar.getRune(); + this.combination = combination; + this.combo = combo; + this.resetAnimation = false; + } + + @Override + public boolean checkRequirements() { + 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 && amountInInventory(player, PURE_ESSENCE.getId()) == 0) { + sendMessage(player, "You need pure essence to craft this rune."); + return false; + } + 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() && 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() && amountInInventory(player, PURE_ESSENCE.getId()) == 0) { + sendMessage(player, "You need pure essence to craft this rune."); + return false; + } + 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 (amountInInventory(player, t.getTalisman().getId()) == 0) { + sendMessage(player, "You don't have the correct talisman to combine this rune."); + return false; + } + talisman = t; + } + } + player.lock(4); + return true; + } + + @Override + public void animate() { + player.animate(ANIMATION); + player.graphics(GRAPHICS); + playAudio(player, Sounds.BIND_RUNES_2710); + } + + @Override + public boolean reward() { + if (!combination) { + craft(); + } else { + combine(); + } + return true; + } + + private static final int[][] OuraniaTable = { //https://x.com/JagexAsh/status/1312893446395506688/photo/1 + /*level up to 9*/ { 2, 7, 15, 30, 60, 105, 165, 250, 400, 700,1300,2500,5000,10000}, + /*level up to 19*/ { 3, 9, 21, 45, 85, 145, 225, 400,1000,2200,4600,6700,8500,10000}, + /*level up to 29*/ { 8, 23, 55, 110, 220, 430, 850,1650,3250,4750,6150,7500,8800,10000}, + /*level up to 39*/ { 20, 60, 120, 250, 500,1000,2000,4000,5300,6500,7600,8500,9300,10000}, + /*level up to 49*/ { 40, 120, 240, 500,1000,2000,4000,5500,6500,7300,8050,8750,9400,10000}, + /*level up to 59*/ { 80, 250, 600,1300,2650,4150,5250,6250,7000,7700,8350,8950,9500,10000}, + /*level up to 69*/ {100, 300, 700,1500,3050,4450,5500,6450,7200,7900,8500,9050,9550,10000}, + /*level up to 79*/ {200, 700,1700,3500,5000,6200,7100,7800,8300,8700,9100,9400,9700,10000}, + /*level up to 89*/ {400,1000,2450,3900,5250,6300,7100,7800,8400,8900,9300,9600,9800,10000}, + /*level up to 98*/ {650,1650,3300,4750,6100,7100,7800,8400,8900,9300,9600,9800,9900,10000}, + /*level up to 99*/ {900,2200,3750,5200,6550,7500,8100,8600,9000,9300,9600,9800,9900,10000} + }; + + /** + * Method used to craft runes. + */ + private void craft() { + final Item item = getEssenceItem(); + int amount = player.getInventory().getAmount(item); + 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(); + } + + if (removeItem(player, item, Container.INVENTORY)) { + sendMessage(player, "You bind the temple's power into " + (combination ? combo.getRune().getName().toLowerCase() : rune.getRune().getName().toLowerCase()) + "s."); + addItemOrDrop(player, rune.getRune().getId(), total); + player.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_RC, amount); + + // Fist of guthix gloves + double xp = rune.getExperience() * amount; + if ((altar == Altar.AIR && inEquipment(player, Items.AIR_RUNECRAFTING_GLOVES_12863, 1)) + || (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 + if (altar == Altar.NATURE) { + player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 3); + } + // Craft 196 or more air runes simultaneously + if (altar == Altar.AIR && total >= 196) { + player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 2); + } + // Craft a water rune at the Water Altar + if (altar == Altar.WATER && rune == Rune.WATER) { + player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 11); + } + + } + } + } + + /** + * Method used to combine runes. + */ + 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 ? 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); + 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()) { + 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) { + if (player.getEquipment().remove(BINDING_NECKLACE, true)) { + sendMessage(player, "Your binding necklace crumbles into dust."); + } + } + } + } + } + } + + /** + * Checks if the player has the spell imbue. + * + * @return {@code True} if so. + */ + private boolean hasSpellImbue() { + return player.getAttribute("spell:imbue", 0) > GameWorld.getTicks(); + } + + /** + * Gets the rune essence item. + * + * @return the rune essence item. + */ + 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() && amountInInventory(player, PURE_ESSENCE.getId()) > 0) { + return new Item(PURE_ESSENCE.getId(), amountInInventory(player, PURE_ESSENCE.getId())); + } + 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())); + } + + /** + * Gets the multiplied amount of runes to make. + * + * @return the amount. + */ + public int getMultiplier() { + if (altar.isOurania()) { + return 1; + } + int rcLevel = getDynLevel(player, Skills.RUNECRAFTING); + int runecraftingFormulaRevision = ServerConstants.RUNECRAFTING_FORMULA_REVISION; + boolean lumbridgeDiary = player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE).isComplete(1); + return RuneCraftPulse.getMultiplier(rcLevel, rune, runecraftingFormulaRevision, lumbridgeDiary); + } + + public static int getMultiplier(int rcLevel, Rune rune, int runecraftingFormulaRevision, boolean lumbridgeDiary) { + int[] multipleLevels = rune.getMultiple(); + int i = 0; + for (int level : multipleLevels) { + if (rcLevel >= level) { + i++; + } + } + + if (multipleLevels.length > i && runecraftingFormulaRevision >= 573) { + int a = Math.max(multipleLevels[i-1], rune.getLevel()); + int b = multipleLevels[i]; + 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 (lumbridgeDiary + && new ArrayList<>(Arrays.asList(Rune.AIR, Rune.WATER, Rune.FIRE, Rune.EARTH)).contains(rune) + && RandomFunction.getRandom(10) == 0) { //approximately 10% chance + i += 1; + } + + return i != 0 ? i : 1; + } + + /** + * Method used to check if the player has a binding necklace. + * + * @return True if so. + */ + public boolean hasBindingNecklace() { + return player.getEquipment().containsItem(BINDING_NECKLACE); + } + + /** + * Gets the altar. + * + * @return The altar. + */ + public Altar getAltar() { + return altar; + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/RunePouch.java b/Server/src/main/content/global/skill/runecrafting/RunePouch.java similarity index 99% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/RunePouch.java rename to Server/src/main/content/global/skill/runecrafting/RunePouch.java index 36364a670..b2fbf892a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/RunePouch.java +++ b/Server/src/main/content/global/skill/runecrafting/RunePouch.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.runecrafting; +package content.global.skill.runecrafting; -import core.game.content.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 new file mode 100644 index 000000000..599ca05b0 --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/RunePouchPlugin.kt @@ -0,0 +1,52 @@ +package content.global.skill.runecrafting + +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.Plugin +import org.rs09.consts.Items +import core.tools.colorize + +/** + * Handles the rune pouches. + * @author Ceikry, Player Name + */ +class RunePouchPlugin : OptionHandler() { + @Throws(Throwable::class) + override fun newInstance(arg: Any?): Plugin? { + for (i in 5509..5515) { + ItemDefinition.forId(i).handlers["option:fill"] = this + ItemDefinition.forId(i).handlers["option:empty"] = this + ItemDefinition.forId(i).handlers["option:check"] = this + ItemDefinition.forId(i).handlers["option:drop"] = this + } + return this + } + + override fun handle(player: Player, node: Node, option: String): Boolean { + val rEssAmt = player.inventory.getAmount(Items.RUNE_ESSENCE_1436) + val pEssAmt = player.inventory.getAmount(Items.PURE_ESSENCE_7936) + var preferenceFlag = 0 //0 -> rune ess, 1 -> pure ess + if(rEssAmt - pEssAmt == 0 && option == "fill") return true + if(rEssAmt > pEssAmt) preferenceFlag = 0 else preferenceFlag = 1 + + val essence = Item( + if(preferenceFlag == 0) Items.RUNE_ESSENCE_1436 else Items.PURE_ESSENCE_7936, + if(preferenceFlag == 0) rEssAmt else pEssAmt + ) + + 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 + } + + override fun isWalk(): Boolean { + return false + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/RunecraftingPlugin.java b/Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java similarity index 77% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/RunecraftingPlugin.java rename to Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java index 72e1a4d43..954948eaf 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/RunecraftingPlugin.java +++ b/Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java @@ -1,10 +1,11 @@ -package core.game.node.entity.skill.runecrafting; +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; import core.game.component.Component; -import core.game.content.global.action.ClimbActionHandler; +import core.game.global.action.ClimbActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; @@ -13,16 +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 rs09.game.content.global.travel.EssenceTeleport; -import rs09.game.node.entity.skill.runecrafting.RunePouchPlugin; -import rs09.game.world.GameWorld; -import rs09.plugin.PluginManager; + +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles runecraftign related options. @@ -34,11 +36,10 @@ public class RunecraftingPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { addNodes(); - PluginManager.definePlugin(new TiaraPlugin()); - PluginManager.definePlugin(new RunePouchPlugin()); - PluginManager.definePlugin(new EnchantTiaraPlugin()); - PluginManager.definePlugin(new MysteriousRuinPlugin()); - PluginManager.definePlugin(new CombinationRunePlugin()); + ClassScanner.definePlugin(new TiaraPlugin()); + ClassScanner.definePlugin(new RunePouchPlugin()); + ClassScanner.definePlugin(new EnchantTiaraPlugin()); + ClassScanner.definePlugin(new CombinationRunePlugin()); SceneryDefinition.forId(2492).getHandlers().put("option:use", this); NPCDefinition.forId(553).getHandlers().put("option:teleport", this); NPCDefinition.forId(2328).getHandlers().put("option:teleport", this); @@ -52,13 +53,13 @@ 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; } switch (node.getId()) { case 2492: - EssenceTeleport.home(player); + EssenceTeleport.home(player,node); return true; case 26844: case 26845: @@ -95,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)); @@ -131,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/java/core/game/node/entity/skill/runecrafting/Talisman.java b/Server/src/main/content/global/skill/runecrafting/Talisman.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/Talisman.java rename to Server/src/main/content/global/skill/runecrafting/Talisman.java index 8e4db131f..3d2f3bad4 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/Talisman.java +++ b/Server/src/main/content/global/skill/runecrafting/Talisman.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.runecrafting; +package content.global.skill.runecrafting; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/Tiara.java b/Server/src/main/content/global/skill/runecrafting/Tiara.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/Tiara.java rename to Server/src/main/content/global/skill/runecrafting/Tiara.java index 171d5f10c..956c63376 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/Tiara.java +++ b/Server/src/main/content/global/skill/runecrafting/Tiara.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.runecrafting; +package content.global.skill.runecrafting; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/TiaraPlugin.java b/Server/src/main/content/global/skill/runecrafting/TiaraPlugin.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/TiaraPlugin.java rename to Server/src/main/content/global/skill/runecrafting/TiaraPlugin.java index 53585a4bf..b52a7c1e7 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/TiaraPlugin.java +++ b/Server/src/main/content/global/skill/runecrafting/TiaraPlugin.java @@ -1,4 +1,6 @@ -package core.game.node.entity.skill.runecrafting; +package content.global.skill.runecrafting; + +import static core.api.ContentAPIKt.*; import core.cache.def.impl.VarbitDefinition; import core.cache.def.impl.ItemDefinition; @@ -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()).getBitShift(), 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()).getBitShift(), 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 new file mode 100644 index 000000000..e44b57da1 --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/abyss/AbyssPlugin.kt @@ -0,0 +1,369 @@ +package core.game.node.entity.skill.runecrafting.abyss + +import core.api.* +import core.game.node.Node +import core.plugin.ClassScanner.definePlugin +import core.tools.colorize +import core.game.node.scenery.Scenery +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 content.data.skill.SkillingTool +import content.global.skill.runecrafting.Altar +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 +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery 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 + +/** + * A plugin used to handle the abyss. + * @author lila + * @author cfunny + */ +@Initializable +class AbyssPlugin : InteractionListener { + + override fun defineListeners() { + definePlugin(AbyssalNPC()) + definePlugin(DarkMageDialogue()) + definePlugin(ZamorakMageDialogue()) + on(NPCs.MAGE_OF_ZAMORAK_2259, IntType.NPC, "teleport"){ player, node -> + teleport(player, node as NPC) + return@on true + } + on(NPCs.DARK_MAGE_2262, IntType.NPC, "repair-pouches"){ player, node -> + player.dialogueInterpreter.open(node.id, node, true) + return@on true + } + on(IntType.SCENERY, "exit-through"){ player, node -> + val altar = Altar.forObject(node as Scenery) + altar?.enterRift(player) + return@on true + } + on(Sceneries.PASSAGE_7154, IntType.SCENERY, "go-through",){ player, node -> + player.properties.teleportLocation = innerRing(node) + return@on true + } + on(Sceneries.ROCK_7158, IntType.SCENERY, "mine"){ player, node -> + val tool: SkillingTool? = getTool(player, true) + if (tool == null) { + sendMessage(player, "You need a pickaxe in order to do that.") + return@on true + } + return@on handleObstacle( + node, + player, + Skills.MINING, + MINE_PROGRESS, + tool.animation, + arrayOf( + "You attempt to mine your way through...", + "...and manage to break through the rock.", + "...but fail to break-up the rock." + ) + ) + } + on(Sceneries.TENDRILS_7161, IntType.SCENERY, "chop"){ player, node -> + val tool: SkillingTool? = getTool(player, false) + if (tool == null) { + sendMessage(player, "You need an axe in order to do that.") + return@on true + } + return@on handleObstacle( + node, + player, + Skills.WOODCUTTING, + CHOP_PROGRESS, + tool.animation, + arrayOf( + "You attempt to chop your way through...", + "...and manage to chop down the tendrils.", + "...but fail to cut through the tendrils." + ) + ) + } + on(Sceneries.BOIL_7165, IntType.SCENERY, "burn-down"){ player, node -> + if (!inInventory(player, Items.TINDERBOX_590)) { + sendMessage(player, "You don't have a tinderbox to burn it.") + return@on true + } + // TODO: i vaguely remember there being burn graphics for the boil. find if this is indeed true, and if so, find gfx id + return@on handleObstacle( + node, + player, + Skills.FIREMAKING, + BURN_PROGRESS, +// Animation(Animations.HUMAN_LIGHT_FIRE_WITH_TINDERBOX_733), + Animation(733), + arrayOf( + "You attempt to burn your way through...", + "...and manage to burn it down and get past.", + "...but fail to set it on fire." + ) + ) + } + on(Sceneries.EYES_7168, IntType.SCENERY, "distract"){ player, node -> + val distractEmote = Animation(distractEmotes[RandomFunction.random(0,distractEmotes.size)]) + return@on handleObstacle( + node, + player, + Skills.THIEVING, + DISTRACT_PROGRESS, + distractEmote, + arrayOf( + "You use your thieving skills to misdirect the eyes...", + "...and sneak past while they're not looking.", + "...but fail to distract the eyes." + ) + ) + } + on(Sceneries.GAP_7164, IntType.SCENERY, "squeeze-through"){ player, node -> + return@on handleObstacle( + node, + player, + Skills.AGILITY, + null, +// Animation(Animations.HUMAN_SQUEEZE_INTO_GAP_1331), + Animation(1331), + arrayOf( + "You attempt to squeeze through the narrow gap...", + "...and you manage to crawl through.", + "...but fail to crawl through." + ) + ) + } + } + + /** + * emotes used for the eyes obstacle + */ + private val distractEmotes = intArrayOf( + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 2113, + 2109, + 2111, + 2106, + 2107, + 2108, + 0x558, + 2105, + 2110, + 2112, + 0x84F, + 0x850, + 1131, + 1130, + 1129, + 1128, + 1745, + 3544, + 3543, + 2836 + ) + + companion object { + + /** + * varp bit offset for the abyss obstacles varbits + */ + const val ABYSS_OBSTACLES = 18 + // varp value, until the constant is defined in consntlib + const val VARP_SCENERY_ABYSS = 491 + + /** + * Represents teleporting to the abyss, + * including randomized location and rotating obstacles accordingly + */ + fun teleport(player: Player, npc: NPC) { + + var teleportLoc = AbyssLoc.randomLoc() + while(!teleportLoc.isValid()) { + teleportLoc = teleportLoc.attract() + } + + player.lock(3) +// npc.visualize(Animation(Animations.MAGE_OF_ZAMORAK_TELEOTHER_1979), Graphics(Gfx.TELEOTHER_PURPLE_BEAMS_4)) + npc.visualize(Animation(1979), Graphics(4)) + npc.sendChat("Veniens! Sallakar! Rinnesset!") + player.skills.decrementPrayerPoints(100.0) + removeTimer(player) + registerTimer(player, spawnTimer(2000)) + GameWorld.Pulser.submit(object : Pulse(2, player) { + override fun pulse(): Boolean { + rotateObstacles(player,teleportLoc) + player.properties.teleportLocation = teleportLoc.toAbs() + npc.updateMasks.reset() + return true + } + }) + } + + /** + * Represents getting the inner ring location corresponding to a node in the outer ring. + * Used to send a player to the inner ring when they pass an obstacle. + */ + fun innerRing(node: Node): Location { + val obstacleLoc = AbyssLoc.fromAbs(node.location) + var loc = obstacleLoc.attract(5) + while (!loc.isValid()) { + loc = loc.attract() + } + return loc.toAbs() + } + + + /** + * Represents rotating the abyssal obstacles for the player. + * Used to make sure the player lands by the blocked obstacle. + */ + fun rotateObstacles(player: Player, abyssLoc: AbyssLoc) { + setVarbit(player, 625, abyssLoc.getSegment(), true) + } + + /** + * Handles attempts at passing abyssal obstacles to get from outer ring to inner ring + */ + const val MINE_PROGRESS = 12 + const val CHOP_PROGRESS = 14 + 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 { + log(this::class.java, Log.FINE, "handled abyss ${obstacle.name}") + player.lock() + player.animate(animation) + GameWorld.Pulser.submit(object : Pulse(1, player) { + var count = 0 + override fun pulse(): Boolean { + when (count++) { + 1 -> sendMessage(player, messages[0]) + 3 -> return if (RandomFunction.random(100) < getStatLevel(player,skill)+1) { + sendMessage(player, colorize("%G${messages[1]}")) + if(varbitVal != null) { setVarbit(player, 625, varbitVal) } + false + } else { + sendMessage(player, colorize("%R${messages[2]}")) + player.unlock() + true + } + 5 -> { + if(varbitVal != null) { setVarbit(player, 625, varbitVal or 1) } + } + 7 -> { + player.unlock() + player.properties.teleportLocation = innerRing(obstacle) + return true + } + } + return false + } + + override fun stop() { + super.stop() + player.animate(Animation(-1, Animator.Priority.HIGH)) + } + }) + return true + } + } +} + +/** + * Polar coordinates class for abyss + * @author lila + */ +class AbyssLoc(val radius: Double, val angle: Double) { + + /** + * Attract the location towards the center + */ + fun attract(steps: Int = 1): AbyssLoc { + return AbyssLoc(radius-steps.toDouble(),angle) + } + + /** + * Get the segment of an abyssloc - its angle as an integer modulo 12, with south = 0 and positive = clockwise + * this is used to determine which of the 12 evenly spaced obstacles around the outer ring the location is nearest to + */ + fun getSegment() : Int { + val segments = 12 + val angleToCircle = angle * segments / ( 2 * Math.PI ) + val angleSegment = (angleToCircle + 0.5).toInt() + // now 'normalize' the segment, so that 0 is the southernmost obstacle and 1 is clockwise from it + val normalSegment = ( 9 - angleSegment ).mod(12) + return normalSegment + } + + /** + * Transform back to absolute coordinates + */ + fun toAbs() : Location { + val x = (radius * cos(angle)).toInt() + val y = (radius * sin(angle)).toInt() + return origin.transform(x,y,0) + } + + /** + * Check if location is valid + */ + fun isValid() : Boolean { + val abs = toAbs() + return (RegionManager.isTeleportPermitted(abs) && RegionManager.getObject(abs) == null) + } + companion object { + + /** + * origin and outer radius values of the abyss itself + */ + // origin: the exact center of the abyss, inside that spinny ball thing + val origin = Location(3039,4832,0) + // the outer ring is generally at radius 24-26; testing shows that a minimum of 25.1 guarantees that players don't end up on an obstacle or inner wall + const val outerRadius = 25.1 + + /** + * turn an absolute location into an abyss location + */ + fun fromAbs(loc: Location) : AbyssLoc { + val local = Location.getDelta(origin,loc) + val radius = Math.sqrt((local.x * local.x + local.y * local.y).toDouble()) + val angle = Math.atan2(local.y.toDouble(),local.x.toDouble()) + return AbyssLoc(radius,angle) + } + + /** + * get a random location around the abyss outer ring + */ + fun randomLoc() : AbyssLoc { + val angle = Random.nextDouble() * 2 * Math.PI + return AbyssLoc(outerRadius,angle) + } + } +} diff --git a/Server/src/main/content/global/skill/runecrafting/abyss/AbyssalNPC.java b/Server/src/main/content/global/skill/runecrafting/abyss/AbyssalNPC.java new file mode 100644 index 000000000..f713fb69b --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/abyss/AbyssalNPC.java @@ -0,0 +1,90 @@ +package content.global.skill.runecrafting.abyss; + +import content.global.skill.runecrafting.RunePouch; +import core.game.node.entity.Entity; +import core.game.node.entity.npc.AbstractNPC; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.game.world.map.Location; +import core.tools.RandomFunction; + +/** + * Handles an abyssal npc. + * @author Vexia + */ +public final class AbyssalNPC extends AbstractNPC { + + /** + * Constructs a new {@code AbyssalNPC} {@code Object}. + */ + public AbyssalNPC() { + super(0, null, true); + setAggressive(true); + } + + /** + * Constructs a new {@code AbyssalNPC} {@code Object}. + * @param id the id. + * @param location the location. + */ + public AbyssalNPC(int id, Location location) { + super(id, location, true); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new AbyssalNPC(id, location); + } + + @Override + public void init() { + super.init(); + this.setDefaultBehavior(); + } + + @Override + public void handleTickActions() { + super.handleTickActions(); + } + + @Override + public void finalizeDeath(Entity killer) { + super.finalizeDeath(killer); + if (killer instanceof Player) { + Player p = killer.asPlayer(); + if (RandomFunction.random(750) < 12) { + Item pouch = getPouch(p); + if (pouch != null) { + getDefinition().getDropTables().createDrop(pouch, p, this, getLocation()); + } + } + } + } + + /** + * Gets the next pouch item. + * @param player the player. + * @return the pouch. + */ + private Item getPouch(Player player) { + if (!player.hasItem(RunePouch.SMALL.getPouch())) { + return RunePouch.SMALL.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())) { + return RunePouch.LARGE.getPouch(); + } + if (!player.hasItem(RunePouch.GIANT.getPouch()) && !player.hasItem(RunePouch.GIANT.getDecayedPouch())) { + return RunePouch.GIANT.getPouch(); + } + return null; + } + + @Override + public int[] getIds() { + return new int[] { 2263, 2264, 2265 }; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/abyss/DarkMageDialogue.java b/Server/src/main/content/global/skill/runecrafting/abyss/DarkMageDialogue.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/abyss/DarkMageDialogue.java rename to Server/src/main/content/global/skill/runecrafting/abyss/DarkMageDialogue.java index e5f13ca58..a7b6450cd 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/abyss/DarkMageDialogue.java +++ b/Server/src/main/content/global/skill/runecrafting/abyss/DarkMageDialogue.java @@ -1,9 +1,12 @@ -package core.game.node.entity.skill.runecrafting.abyss; +package content.global.skill.runecrafting.abyss; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/node/entity/skill/runecrafting/abyss/ZamorakMageDialogue.java b/Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/runecrafting/abyss/ZamorakMageDialogue.java rename to Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java index 519fe98cc..ab06e4843 100644 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/abyss/ZamorakMageDialogue.java +++ b/Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java @@ -1,11 +1,14 @@ -package core.game.node.entity.skill.runecrafting.abyss; +package content.global.skill.runecrafting.abyss; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; 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/kotlin/rs09/game/node/entity/skill/skillcapeperks/Skillcape.kt b/Server/src/main/content/global/skill/skillcapeperks/Skillcape.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/Skillcape.kt rename to Server/src/main/content/global/skill/skillcapeperks/Skillcape.kt index ec640e4b8..b69a2b5ba 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/Skillcape.kt +++ b/Server/src/main/content/global/skill/skillcapeperks/Skillcape.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.skillcapeperks +package content.global.skill.skillcapeperks enum class Skillcape { ATTACK, diff --git a/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt new file mode 100644 index 000000000..7de63cfd9 --- /dev/null +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt @@ -0,0 +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 DataSkillcapes.values()){ + cape.skillcapeIds.forEach { capeIds.add(it) } + } + val capes = capeIds.toIntArray() + 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 + } + } + 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/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapeOperatePlugin.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapeOperatePlugin.kt similarity index 86% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapeOperatePlugin.kt rename to Server/src/main/content/global/skill/skillcapeperks/SkillcapeOperatePlugin.kt index f5d31a657..3b0bf601d 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapeOperatePlugin.kt +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapeOperatePlugin.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.skill.skillcapeperks +package content.global.skill.skillcapeperks import core.cache.def.impl.ItemDefinition import core.game.interaction.OptionHandler @@ -10,7 +10,7 @@ import core.plugin.Plugin @Initializable class SkillcapeOperatePlugin : OptionHandler() { override fun newInstance(arg: Any?): Plugin { - for(cape in core.game.content.global.SkillcapePerks.values()){ + for(cape in content.data.skill.SkillcapePerks.values()){ cape.skillcapeIds.forEach { ItemDefinition.forId(it).handlers["option:operate"] = this } diff --git a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt new file mode 100644 index 000000000..148696543 --- /dev/null +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt @@ -0,0 +1,315 @@ +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 core.game.world.GameWorld +import core.game.world.map.Location +import core.game.world.map.zone.impl.DarkZone +import core.plugin.Initializable + +enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) { + BAREFISTED_SMITHING("cape_perks:barefisted-smithing"), + DIVINE_FAVOR("cape_perks:divine-favor"), + CONSTANT_GLOW("cape_perks:eternal-glow"), + PRECISION_MINER("cape_perks:precision-miner"), + GREAT_AIM("cape_perks:great-aim"), + NEST_HUNTER("cape_perks:nest-hunter"), + PRECISION_STRIKES("cape_perks:precision-strikes"), + FINE_ATTUNEMENT("cape_perks:fine-attunement"), + GRAND_BULLWARK("cape_perks:grand-bullwark"), + ACCURATE_MARKSMAN("cape_perks:accurate-marksman"), + DAMAGE_SPONG("cape_perks:damage-sponge"), + MARATHON_RUNNER("cape_perks:marathon-runner"), + LIBRARIAN_MAGUS("cape_perks:librarian-magus",{player -> + 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) + store[player.name] = used + 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.open(509871233) + } + }), + SEED_ATTRACTION("cape_perks:seed_attract",{player -> + 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_PATCH || seed.applicablePatch == PatchType.TREE_PATCH || seed.applicablePatch == PatchType.SPIRIT_TREE_PATCH){ + seed = possibleSeeds.random() + } + val reward = core.game.node.item.Item(seed.itemID) + if(!player.inventory.add(reward)){ + core.game.node.item.GroundItemManager.create(reward,player) + } + } + player.dialogueInterpreter.sendDialogue("You pluck off the seeds that were stuck to your cape.") + store[player.name] = true + } + }), + TRICKS_OF_THE_TRADE("cape_perks:tott",{player -> + val hasHelmetBonus = getAttribute(player, "cape_perks:tott:helmet-stored", false) + if(hasHelmetBonus){ + sendDialogue(player, "Your cape's pockets are lined with all the utilities you need for slayer.") + } else { + sendDialogue(player, "Your cape is lined with empty pockets shaped like various utilities needed for slayer.") + } + }), + 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") + ; + + companion object{ + @JvmStatic + fun isActive(perk: SkillcapePerks, player: Player): Boolean{ + return player.getAttribute(perk.attribute,false) + } + + fun forSkillcape(skillcape: Skillcape): SkillcapePerks{ + return when(skillcape){ + Skillcape.ATTACK -> PRECISION_STRIKES + Skillcape.STRENGTH -> FINE_ATTUNEMENT + Skillcape.DEFENCE -> GRAND_BULLWARK + Skillcape.RANGING -> ACCURATE_MARKSMAN + Skillcape.PRAYER -> DIVINE_FAVOR + Skillcape.MAGIC -> LIBRARIAN_MAGUS + Skillcape.RUNECRAFTING -> ABYSS_WARPING + Skillcape.HITPOINTS -> DAMAGE_SPONG + Skillcape.AGILITY -> MARATHON_RUNNER + Skillcape.HERBLORE -> BREWMASTER + Skillcape.THIEVING -> SMOOTH_HANDS + Skillcape.CRAFTING -> NONE + Skillcape.FLETCHING -> NONE + Skillcape.SLAYER -> TRICKS_OF_THE_TRADE + Skillcape.CONSTRUCTION -> NONE + Skillcape.MINING -> PRECISION_MINER + Skillcape.SMITHING -> BAREFISTED_SMITHING + Skillcape.FISHING -> GREAT_AIM + Skillcape.COOKING -> HASTY_COOKING + Skillcape.FIREMAKING -> CONSTANT_GLOW + Skillcape.WOODCUTTING -> NEST_HUNTER + Skillcape.FARMING -> SEED_ATTRACTION + Skillcape.HUNTING -> NONE + Skillcape.SUMMONING -> PET_MASTERY + else -> NONE + } + } + } + + fun activate(player: Player){ + if(GameWorld.settings?.skillcape_perks != true){ + return + } + if(!isActive(this,player)){ + player.setAttribute("/save:$attribute",true) + } + player.debug("Activated ${this.name}") + 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){ + if(GameWorld.settings?.skillcape_perks != true){ + player.sendMessage("This item can not be operated.") + return + } + effect?.invoke(player) + } + + fun deactivate(player: Player){ + player.removeAttribute(attribute) + if(this == CONSTANT_GLOW) + DarkZone.checkDarkArea(player) + } + + @Initializable + 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 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.LUNAR.interfaceId -> { + when (buttonId) { + 1 -> SpellBookManager.SpellBook.MODERN + 2 -> SpellBookManager.SpellBook.ANCIENT + else -> null + } + } + + else -> null + } + + end() + if (spellbook != null) { + if (spellbook == SpellBookManager.SpellBook.ANCIENT) { + if (!hasRequirement(player, Quests.DESERT_TREASURE)) return true + } else if (spellbook == SpellBookManager.SpellBook.LUNAR) { + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) return true + } + 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 + class RCCapeDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return RCCapeDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + altarList(0) + stage = 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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 -> altarList(++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 -> altarList(--stage) + 2 -> sendAltar(player, Altar.CHAOS) + 3 -> sendAltar(player, Altar.ASTRAL) + 4 -> sendAltar(player, Altar.NATURE) + 5 -> altarList(++stage) + } + 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 altarList(stage: Int) { + when (stage) { + 0 -> options("Air", "Mind", "Water", "Earth", "More...") + 1 -> options("Back...", "Fire", "Body", "Cosmic", "More...") + 2 -> options("Back...", "Chaos", "Astral", "Nature", "More...") + 3 -> options("Back...", "Law", "Death", "Blood", "More...") + } + } + + fun sendAltar(player: Player,altar: Altar) { + end() + if (altar == Altar.DEATH && !hasRequirement(player, 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) + } + + override fun getIds(): IntArray { + return intArrayOf(509871233) + } + + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/AberrantSpectreNPC.java b/Server/src/main/content/global/skill/slayer/AberrantSpectreNPC.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/skill/slayer/AberrantSpectreNPC.java rename to Server/src/main/content/global/skill/slayer/AberrantSpectreNPC.java index 1e61efe34..e32e6e46e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/AberrantSpectreNPC.java +++ b/Server/src/main/content/global/skill/slayer/AberrantSpectreNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -7,8 +7,8 @@ import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; import core.game.world.map.Location; import core.plugin.Initializable; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MagicSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MagicSwingHandler; /** * Handles the aberrant spectre npc. diff --git a/Server/src/main/content/global/skill/slayer/AbyssalDemonNPC.java b/Server/src/main/content/global/skill/slayer/AbyssalDemonNPC.java new file mode 100644 index 000000000..ababe2bd0 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/AbyssalDemonNPC.java @@ -0,0 +1,73 @@ +package content.global.skill.slayer; + +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; +import core.game.node.entity.npc.AbstractNPC; +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.RandomFunction; + +import static core.api.ContentAPIKt.getPathableRandomLocalCoordinate; + +/** + * Handles the abyssal npc. + * @author Vexia + */ +@Initializable +public class AbyssalDemonNPC extends AbstractNPC { + + /** + * The melee swing handler. + */ + private static final MeleeSwingHandler SWING_HANDLER = new MeleeSwingHandler() { + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + if (victim instanceof Player && RandomFunction.random(8) <= 2) { + boolean npc = RandomFunction.random(100) <= 50; + Entity source = npc ? victim : entity; + Entity teleported = npc ? entity : victim; + Location loc = getPathableRandomLocalCoordinate(teleported, 1, source.getLocation(), 3); + teleported.graphics(Graphics.create(409)); + teleported.teleport(loc, 1); + } + return super.swing(entity, victim, state); + } + }; + + /** + * Constructs a new {@Code AbyssalNPC} {@Code Object} + */ + public AbyssalDemonNPC() { + super(-1, null); + } + + /** + * Constructs a new {@Code AbyssalNPC} {@Code Object} + * @param id the id. + * @param location the location. + */ + public AbyssalDemonNPC(int id, Location location) { + super(id, location); + } + + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + return SWING_HANDLER; + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new AbyssalDemonNPC(id, location); + } + + @Override + public int[] getIds() { + return Tasks.ABYSSAL_DEMONS.getNpcs(); + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/BansheeNPC.java b/Server/src/main/content/global/skill/slayer/BansheeNPC.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/slayer/BansheeNPC.java rename to Server/src/main/content/global/skill/slayer/BansheeNPC.java index b5ab44ff0..da0babfce 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/BansheeNPC.java +++ b/Server/src/main/content/global/skill/slayer/BansheeNPC.java @@ -1,17 +1,17 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.plugin.Initializable; 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 rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.InteractionType; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/BasiliskNPC.java b/Server/src/main/content/global/skill/slayer/BasiliskNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/skill/slayer/BasiliskNPC.java rename to Server/src/main/content/global/skill/slayer/BasiliskNPC.java index 9efecbe7f..6c5a5a568 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/BasiliskNPC.java +++ b/Server/src/main/content/global/skill/slayer/BasiliskNPC.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.game.node.entity.combat.BattleState; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/BrutalDragonNPC.java b/Server/src/main/content/global/skill/slayer/BrutalDragonNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/skill/slayer/BrutalDragonNPC.java rename to Server/src/main/content/global/skill/slayer/BrutalDragonNPC.java index fb44f8870..401c759ff 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/BrutalDragonNPC.java +++ b/Server/src/main/content/global/skill/slayer/BrutalDragonNPC.java @@ -1,9 +1,9 @@ -package core.game.node.entity.skill.slayer; +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.combat.handlers.DragonfireSwingHandler; +import content.global.handlers.item.equipment.special.DragonfireSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.impl.Projectile; import core.game.node.entity.npc.AbstractNPC; @@ -11,8 +11,8 @@ 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 rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MultiSwingHandler; /** * Handles a brutal dragon npc. diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/BugSwarmNPC.java b/Server/src/main/content/global/skill/slayer/BugSwarmNPC.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/slayer/BugSwarmNPC.java rename to Server/src/main/content/global/skill/slayer/BugSwarmNPC.java index 2930c5089..5617a7dc7 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/BugSwarmNPC.java +++ b/Server/src/main/content/global/skill/slayer/BugSwarmNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.game.node.entity.combat.BattleState; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/BuglanternPlugin.java b/Server/src/main/content/global/skill/slayer/BuglanternPlugin.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/slayer/BuglanternPlugin.java rename to Server/src/main/content/global/skill/slayer/BuglanternPlugin.java index 656f96bbc..80a930d28 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/BuglanternPlugin.java +++ b/Server/src/main/content/global/skill/slayer/BuglanternPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.cache.def.impl.ItemDefinition; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/CaveBugNPC.java b/Server/src/main/content/global/skill/slayer/CaveBugNPC.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/slayer/CaveBugNPC.java rename to Server/src/main/content/global/skill/slayer/CaveBugNPC.java index 2f7d95015..66a883dc3 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/CaveBugNPC.java +++ b/Server/src/main/content/global/skill/slayer/CaveBugNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/CaveHorrorNPC.java b/Server/src/main/content/global/skill/slayer/CaveHorrorNPC.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/slayer/CaveHorrorNPC.java rename to Server/src/main/content/global/skill/slayer/CaveHorrorNPC.java index 30cc07f39..1bb494576 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/CaveHorrorNPC.java +++ b/Server/src/main/content/global/skill/slayer/CaveHorrorNPC.java @@ -1,11 +1,11 @@ -package core.game.node.entity.skill.slayer; +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 rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.InteractionType; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/CockatriceNPC.java b/Server/src/main/content/global/skill/slayer/CockatriceNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/skill/slayer/CockatriceNPC.java rename to Server/src/main/content/global/skill/slayer/CockatriceNPC.java index d418de456..288545631 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/CockatriceNPC.java +++ b/Server/src/main/content/global/skill/slayer/CockatriceNPC.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.game.node.entity.combat.BattleState; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/DesertLizardPlugin.java b/Server/src/main/content/global/skill/slayer/DesertLizardPlugin.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/slayer/DesertLizardPlugin.java rename to Server/src/main/content/global/skill/slayer/DesertLizardPlugin.java index 265d787d8..49cc5a851 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/DesertLizardPlugin.java +++ b/Server/src/main/content/global/skill/slayer/DesertLizardPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/DustDevilNPC.java b/Server/src/main/content/global/skill/slayer/DustDevilNPC.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/slayer/DustDevilNPC.java rename to Server/src/main/content/global/skill/slayer/DustDevilNPC.java index 663c7a3ff..081e04d38 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/DustDevilNPC.java +++ b/Server/src/main/content/global/skill/slayer/DustDevilNPC.java @@ -1,13 +1,13 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.plugin.Initializable; 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 rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.InteractionType; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/GargoyleNPC.java b/Server/src/main/content/global/skill/slayer/GargoyleNPC.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/slayer/GargoyleNPC.java rename to Server/src/main/content/global/skill/slayer/GargoyleNPC.java index 48ccca94a..e944ca3df 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/GargoyleNPC.java +++ b/Server/src/main/content/global/skill/slayer/GargoyleNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -11,7 +11,7 @@ import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; import org.rs09.consts.Items; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles the gargoyle npc. @@ -65,7 +65,7 @@ public final class GargoyleNPC extends AbstractNPC { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new RockHammerHandler()); + ClassScanner.definePlugin(new RockHammerHandler()); return super.newInstance(arg); } diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/InfernalMageNPC.java b/Server/src/main/content/global/skill/slayer/InfernalMageNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/skill/slayer/InfernalMageNPC.java rename to Server/src/main/content/global/skill/slayer/InfernalMageNPC.java index 0f0bcb93d..8689befd3 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/InfernalMageNPC.java +++ b/Server/src/main/content/global/skill/slayer/InfernalMageNPC.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/KuraskNPC.java b/Server/src/main/content/global/skill/slayer/KuraskNPC.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/skill/slayer/KuraskNPC.java rename to Server/src/main/content/global/skill/slayer/KuraskNPC.java index cde7e0474..4f7daa530 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/KuraskNPC.java +++ b/Server/src/main/content/global/skill/slayer/KuraskNPC.java @@ -1,13 +1,10 @@ -package core.game.node.entity.skill.slayer; +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.node.entity.player.link.SpellBookManager.SpellBook; import core.game.world.map.Location; import core.plugin.Initializable; -import org.rs09.consts.Items; -import rs09.game.node.entity.skill.slayer.SlayerUtils; /** * Handles the kurask npc. @@ -43,7 +40,7 @@ public final class KuraskNPC extends AbstractNPC { boolean effective = false; if (state.getAttacker() instanceof Player) { final Player player = (Player) state.getAttacker(); - effective = new SlayerUtils(player).hasBroadWeaponEquipped(state); + effective = SlayerUtils.hasBroadWeaponEquipped(player, state); } if (!effective) { state.setEstimatedHit(0); diff --git a/Server/src/main/content/global/skill/slayer/Master.java b/Server/src/main/content/global/skill/slayer/Master.java new file mode 100644 index 000000000..0ce93eb54 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/Master.java @@ -0,0 +1,287 @@ +package content.global.skill.slayer; + +import core.game.node.entity.skill.Skills; +import core.game.node.entity.player.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +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, 3, 0, new int[]{15, 50}, new int[]{0, 0, 0}, + new Task(Tasks.BANSHEE, 8), + new Task(Tasks.BATS, 7), + new Task(Tasks.BEARS, 7), + new Task(Tasks.BIRDS, 6), + new Task(Tasks.CAVE_BUG, 8), + new Task(Tasks.CAVE_CRAWLERS, 8), + new Task(Tasks.CAVE_SLIMES, 8), + new Task(Tasks.COWS, 8), + new Task(Tasks.CRAWLING_HAND, 8), + new Task(Tasks.DESERT_LIZARDS, 8), + new Task(Tasks.DOG, 7), + new Task(Tasks.DWARF, 7), + new Task(Tasks.GHOSTS, 7), + new Task(Tasks.GOBLINS, 7), + new Task(Tasks.ICE_FIENDS, 8), + new Task(Tasks.KALPHITES, 6), + new Task(Tasks.MINOTAURS, 7), + new Task(Tasks.MONKEYS, 6), + new Task(Tasks.SCORPIONS, 7), + new Task(Tasks.SKELETONS, 7), + new Task(Tasks.SPIDERS, 6), + new Task(Tasks.WOLVES, 7), + new Task(Tasks.ZOMBIES, 7)), + + MAZCHNA(8274, 20, 0, new int[]{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.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.GHOULS, 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.MOGRES, 8), + new Task(Tasks.PYREFIENDS, 8), + 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.WOLVES, 7), + new Task(Tasks.ZOMBIES, 7)), + + VANNAKA(1597, 40, 0, new int[]{60, 120}, new int[]{4, 20, 60}, + new Task(Tasks.ABERRANT_SPECTRES, 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.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_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.BANSHEE, 5), + 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.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.KURASKS, 12), + 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.ROCK_SLUGS, 5), + // 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 Integer[]{10, 20}), + // new Task(Tasks.WARPED_TERROR_BIRD, 5), + // new Task(Tasks.WARPED_TORTOISE, 5) + + 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.BANSHEE, 15), + new Task(Tasks.BASILISKS, 15), + new Task(Tasks.BLACK_DEMONS, 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.DAGANNOTHS, 10), + new Task(Tasks.DUST_DEVILS, 15), + 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 Integer[]{30, 60}), + new Task(Tasks.KALPHITES, 10), + new Task(Tasks.KURASKS, 15), + new Task(Tasks.NECHRYAELS, 10), + 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 Integer[]{30, 60}), + new Task(Tasks.TROLLS, 10), + new Task(Tasks.TUROTHS, 15)), + // new Task(Tasks.WARPED_TORTOISE, 15)), + + 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 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 final HashMap idMap = new HashMap<>(); + + static{ + Arrays.stream(Master.values()).forEach(m -> idMap.putIfAbsent(m.npc_id, m)); + } + + 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.default_assignment_range = default_assignment_range; + this.streakPoints = streakPoints; + this.tasks = new ArrayList<>(Arrays.asList(tasks)); + } + + public static Master forId(int id){ + return idMap.get(id); + } + + public int getNpc(){ + return this.npc_id; + } + + public int[] getTaskPoints(){ + return streakPoints; + } + + public boolean hasRequirements(Player player){ + return player.getProperties().getCurrentCombatLevel() >= this.required_combat && player.getSkills().getLevel(Skills.SLAYER) >= this.required_slayer; + } + + public static boolean hasSameTask(Master master, Player player){ + return master.tasks.stream().filter(task -> task.task == SlayerManager.getInstance(player).getTask()).count() != 0; + } + + public static class Task{ + public Tasks task; + public Integer weight; + public Integer[] task_range; + public Task(Tasks task, Integer weight){ + this.task = task; + this.weight = weight; + this.task_range = new Integer[]{null, null}; + } + + Task(Tasks task, Integer weight, Integer[] task_range){ + this.task = task; + this.weight = weight; + this.task_range = task_range; + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/MirrorShieldHandler.java b/Server/src/main/content/global/skill/slayer/MirrorShieldHandler.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/slayer/MirrorShieldHandler.java rename to Server/src/main/content/global/skill/slayer/MirrorShieldHandler.java index db7552b92..87cfea348 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/MirrorShieldHandler.java +++ b/Server/src/main/content/global/skill/slayer/MirrorShieldHandler.java @@ -1,9 +1,9 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.game.node.entity.skill.Skills; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java b/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java new file mode 100644 index 000000000..0f95eb9cb --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java @@ -0,0 +1,102 @@ +package content.global.skill.slayer; + +import core.game.node.entity.Entity; +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; +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.game.world.update.flag.context.Graphics; +import core.plugin.Initializable; +import core.tools.RandomFunction; + +/** + * Handles a mithril dragon npc. + * @author Vexia + */ +@Initializable +public final class MithrilDragonNPC extends AbstractNPC { + + /** + * Handles the combat. + */ + 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}. + * @param id the id. + * @param location the location. + */ + public MithrilDragonNPC(int id, Location location) { + super(id, location); + } + + /** + * Constructs a new {@code MithrilDragonNPC} {@code Object}. + */ + public MithrilDragonNPC() { + super(0, null); + } + + @Override + public void sendImpact(BattleState state) { + CombatStyle style = state.getStyle(); + if (style == null) { + return; + } + int maxHit = state.getEstimatedHit(); + if (maxHit < 1) { + return; + } + switch (style) { + case MELEE: + maxHit = 28; + break; + case MAGIC: + maxHit = 18; + break; + case RANGE: + maxHit = 18; + break; + } + if (state.getEstimatedHit() > maxHit) { + state.setEstimatedHit(RandomFunction.random(maxHit - 5, maxHit)); + } + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new MithrilDragonNPC(id, location); + } + + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + return combatAction; + } + + @Override + public int getDragonfireProtection(boolean fire) { + return 0x2 | 0x4 | 0x8; + } + + @Override + public int[] getIds() { + 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/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/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/SlayerEquipmentFlags.kt b/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt new file mode 100644 index 000000000..e8c5e01e6 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt @@ -0,0 +1,88 @@ +package content.global.skill.slayer + +import content.global.skill.skillcapeperks.SkillcapePerks +import core.api.EquipmentSlot +import core.api.getAttribute +import core.api.getItemFromEquipment +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +/** + * Represents a slayer equipment. + * @author Ceikry + */ +object SlayerEquipmentFlags { + + val blackMasks = (Items.BLACK_MASK_10_8901..Items.BLACK_MASK_8921).map { it }.toIntArray() + val slayerItems = intArrayOf(Items.NOSE_PEG_4168, Items.EARMUFFS_4166, Items.FACE_MASK_4164, *blackMasks, Items.SPINY_HELMET_4551, Items.SLAYER_CAPET_9787, Items.SLAYER_CAPE_9786, Items.SLAYER_HELMET_13263, Items.WITCHWOOD_ICON_8923, Items.MIRROR_SHIELD_4156) + + @JvmStatic + fun updateFlags(player: Player){ + var flags = 0 + if(SkillcapePerks.isActive(SkillcapePerks.TRICKS_OF_THE_TRADE, player) && getAttribute(player, "cape_perks:tott:helmet-stored", false)) flags = 0x3F + else if(hasItem(player, Items.SLAYER_HELMET_13263)) flags = 0x1F + 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.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.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 + } + + @JvmStatic + fun hasNosePeg(player: Player): Boolean{ + return SlayerManager.getInstance(player).flags.equipmentFlags and 1 == 1 + } + + @JvmStatic + fun hasEarmuffs(player: Player): Boolean { + return (SlayerManager.getInstance(player).flags.equipmentFlags shr 1) and 1 == 1 + } + + @JvmStatic + fun hasFaceMask(player: Player): Boolean { + return (SlayerManager.getInstance(player).flags.equipmentFlags shr 2) and 1 == 1 + } + + @JvmStatic + fun hasBlackMask(player: Player): Boolean { + return (SlayerManager.getInstance(player).flags.equipmentFlags shr 3) and 1 == 1 + } + + @JvmStatic + fun hasSpinyHelmet(player: Player): Boolean { + return (SlayerManager.getInstance(player).flags.equipmentFlags shr 4) and 1 == 1 + } + + @JvmStatic + fun hasWitchwoodIcon(player: Player): Boolean { + return (SlayerManager.getInstance(player).flags.equipmentFlags shr 7) and 1 == 1 + } + + @JvmStatic + fun hasMirrorShield(player: Player): Boolean { + return (SlayerManager.getInstance(player).flags.equipmentFlags shr 8) and 1 == 1 + } + + @JvmStatic + fun getDamAccBonus(player: Player): Double { + val isCape = SlayerManager.getInstance(player).flags.equipmentFlags == 0x3F + val hasMask = hasBlackMask(player) + + 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.HEAD)?.id ?: 0) == id + } + + fun isSlayerEq(item: Int): Boolean{ + return item in slayerItems + } + +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerFlags.kt b/Server/src/main/content/global/skill/slayer/SlayerFlags.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerFlags.kt rename to Server/src/main/content/global/skill/slayer/SlayerFlags.kt index 5619c87c7..ade4ac5b7 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerFlags.kt +++ b/Server/src/main/content/global/skill/slayer/SlayerFlags.kt @@ -1,14 +1,13 @@ -package rs09.game.node.entity.skill.slayer +package content.global.skill.slayer -import core.game.node.entity.player.Player -import core.game.node.entity.skill.slayer.Master -import core.game.node.entity.skill.slayer.Tasks +import content.global.skill.slayer.Master +import content.global.skill.slayer.Tasks /** * Bitflag-based system for storing slayer-related data for a player and various helper functions. * @author Ceikry */ -class SlayerFlags(val player: Player) { +class SlayerFlags { var taskFlags = 0 var rewardFlags = 0 var equipmentFlags = 0 @@ -18,7 +17,7 @@ class SlayerFlags(val player: Player) { /** * The removed tasks. */ - val removed: List = ArrayList(4) + val removed: ArrayList = ArrayList(4) /** * Get/set master flags @@ -75,11 +74,11 @@ class SlayerFlags(val player: Player) { * Get/Set canEarnPoints flag */ fun canEarnPoints(): Boolean { - return (taskFlags shr 16) and 1 == 1 + return (taskFlags shr 20) and 1 == 1 } fun flagCanEarnPoints() { - taskFlags = taskFlags or (1 shl 16) + taskFlags = taskFlags or (1 shl 20) } /** *============================================== @@ -149,4 +148,12 @@ class SlayerFlags(val player: Player) { return getTaskAmount() != 0 } + fun fullClear() { + taskFlags = 0 + rewardFlags = 0 + equipmentFlags = 0 + completedTasks = 0 + taskStreak = 0 + } + } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/slayer/SlayerManager.kt b/Server/src/main/content/global/skill/slayer/SlayerManager.kt new file mode 100644 index 000000000..5542a7777 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/SlayerManager.kt @@ -0,0 +1,251 @@ +package content.global.skill.slayer + +import content.global.handlers.item.equipment.fistofguthixgloves.FOGGlovesManager +import core.api.* +import core.game.event.EventHook +import core.game.event.NPCKillEvent +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 java.util.* +import org.rs09.consts.Items + +/** + * Manages the players slayer data. + * @author Ceikry + */ +class SlayerManager(val player: Player? = null) : LoginListener, PersistPlayer, EventHook { + override fun login(player: Player) { + val instance = SlayerManager(player) + player.hook(Event.NPCKilled, instance) + player.setAttribute("slayer-manager", instance) + } + + /** + * The player's slayer flags + */ + @JvmField + val flags: SlayerFlags = SlayerFlags() + + override fun savePlayer(player: Player, save: JSONObject) { + val slayer = JSONObject() + val slayerManager = getInstance(player) + if(slayerManager.removed.isNotEmpty()) { + val removedTasks = JSONArray() + slayerManager.removed.map { + removedTasks.add(it.ordinal.toString()) + } + slayer["removedTasks"] = removedTasks + } + slayer["taskStreak"] = slayerManager.flags.taskStreak.toString() + slayer["totalTasks"] = slayerManager.flags.completedTasks.toString() + slayer["equipmentFlags"] = slayerManager.flags.equipmentFlags + slayer["taskFlags"] = slayerManager.flags.taskFlags + slayer["rewardFlags"] = slayerManager.flags.rewardFlags + save["slayer"] = slayer + } + + override fun parsePlayer(player: Player, data: JSONObject) { + val slayerData = data["slayer"] as JSONObject + val m = slayerData["master"] + val flags = getInstance(player).flags + if (m != null) { + flags.setMaster(Master.forId(m.toString().toInt())) + } + val t = slayerData["taskId"] + if (t != null) flags.setTask(Tasks.values()[t.toString().toInt()]) + val a = slayerData["taskAmount"] + if (a != null) flags.setTaskAmount(a.toString().toInt()) + val points = slayerData["points"] + if (points != null) { + flags.setPoints(points.toString().toInt()) + } + val taskStreak = slayerData["taskStreak"] + if (taskStreak != null) { + flags.taskStreak = taskStreak.toString().toInt() + } + val la = slayerData["learned_rewards"] + if (la != null) { + val learnedArray = slayerData["learned_rewards"] as JSONArray? + for (i in learnedArray!!.indices) { + val unlocked = learnedArray[i] as Boolean + when (i) { + 0 -> if (unlocked) flags.unlockBroads() + 1 -> if (unlocked) flags.unlockRing() + 2 -> if (unlocked) flags.unlockHelm() + else -> {} + } + } + } + val removedTasks = slayerData["removedTasks"] as JSONArray? + if (removedTasks != null) { + for (i in removedTasks.indices) { + flags.removed.add(Tasks.values()[removedTasks[i].toString().toInt()]) + } + } + val completedTasks: Any = slayerData["totalTasks"].toString() + flags.completedTasks = completedTasks.toString().toInt() + if (flags.completedTasks >= 4) flags.flagCanEarnPoints() + + //New system parsing + if (slayerData.containsKey("equipmentFlags")) flags.equipmentFlags = slayerData["equipmentFlags"].toString().toInt() + if (slayerData.containsKey("taskFlags")) flags.taskFlags = slayerData["taskFlags"].toString().toInt() + if (slayerData.containsKey("rewardFlags")) flags.rewardFlags = slayerData["rewardFlags"].toString().toInt() + } + + override fun process(entity: Entity, event: NPCKillEvent) { + val npc = event.npc + val player = entity as? Player ?: return + val slayer = getInstance(player) + val flags = slayer.flags + + if (slayer.hasTask() && npc.id in slayer.task!!.npcs) { + 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 + flags.completedTasks = flags.completedTasks + 1 + if ((flags.completedTasks > 4 || flags.canEarnPoints()) && flags.getMaster() != Master.TURAEL && flags.getPoints() < 64000) { + var points = flags.getMaster().taskPoints[0] + if (flags.taskStreak % 50 == 0) { + points = flags.getMaster().taskPoints[2] + } else if (flags.taskStreak % 10 == 0) { + points = flags.getMaster().taskPoints[1] + } + flags.incrementPoints(points) + if (flags.getPoints() > 64000) { + flags.setPoints(64000) + } + player.sendMessages("You've completed " + flags.taskStreak + " tasks in a row and received " + points + " points, with a total of " + flags.getPoints(), "You have completed " + flags.completedTasks + " tasks in total. Return to a Slayer master.") + } else if (flags.completedTasks == 4) { + player.sendMessage("You've completed your task; you will start gaining points on your next task!") + flags.flagCanEarnPoints() + } else if (flags.getMaster() == Master.TURAEL) { + player.sendMessages("You've completed your task; Tasks from Turael do not award points.", "Return to a Slayer master.") + } else { + player.sendMessages("You've completed your task; Complete " + (4 - flags.completedTasks) + " more task(s) to start gaining points.", "Return to a Slayer master.") + } + } + } + + /** + * Method used to assign a new task for a player. + * @param master the master to give the task. + */ + fun generate(master: Master) { + val task = SlayerUtils.generate(player!!, master) ?: return + SlayerUtils.assign(player!!, task, master) + } + + /** + * Clears the task. + */ + fun clear() { + amount = 0 + } + + /** + * Gets the task name. + * @return the name. + */ + val taskName: String + get() { + val task = flags.getTask() + if (task.npcs == null) { + return "no npcs report me" + } + return if (task.npcs.isEmpty()) { + "npc length too small report me" + } else NPCDefinition.forId(task.npcs[0]).name.toLowerCase() + } + + var task: Tasks? + get() = flags.getTask() + set(task) { + flags.setTask(task!!) + } + + var activeTask: Tasks? = null + get() { + if (hasTask()) + return flags.getTask() + return null + } + + var master: Master? + get() = flags.getMaster() + set(master) { + flags.setMaster(master!!) + } + + /** + * Checks if a **Player** contains a task. + * @return `True` if so. + */ + fun hasTask(): Boolean { + return amount > 0 + } + + /** + * Method used to check if the task is completed. + * @return `True` if so. + */ + val isCompleted: Boolean + get() = flags.getTaskAmount() <= 0 + + var amount: Int + get() = flags.getTaskAmount() + set(amount) { + flags.setTaskAmount(amount) + } + + fun decrementAmount(amount: Int) { + flags.decrementTaskAmount(amount) + setVarp(player!!, 2502, flags.taskFlags shr 4) + } + + /** + * Method used to check if the player has started slayer. + * @return `True` if so. + */ + fun hasStarted(): Boolean { + return flags.completedTasks > 0 || flags.getTaskAmount() > 0 + } + /** + * Gets the slayerPoints. + * @return the slayerPoints. + */ + /** + * Sets the slayerPoints. + * @param slayerPoints the slayerPoints to set + */ + var slayerPoints: Int + get() = flags.getPoints() + set(slayerPoints) { + flags.setPoints(slayerPoints) + } + + /** + * Gets the removed. + * @return the removed. + */ + val removed: List + get() = flags.removed + val isCanEarnPoints: Boolean + get() = flags.canEarnPoints() + + companion object { + @JvmStatic fun getInstance(player: Player) : SlayerManager + { + return getAttribute(player, "slayer-manager", SlayerManager()) + } + } +} diff --git a/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java b/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java new file mode 100644 index 000000000..c8a57c44a --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java @@ -0,0 +1,585 @@ +package content.global.skill.slayer; + +import org.json.simple.JSONObject; +import core.ServerStore; +import core.game.world.GameWorld; +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.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.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. + * + * @author Vexia + */ +@Initializable +public final class SlayerMasterDialogue extends DialoguePlugin { + + /** + * The enchanted gem item. + */ + private static final Item GEM = new Item(4155, 1); + + /** + * The mithril axe item. + */ + private static final Item MITHRIL_AXE = new Item(1355); + + /** + * The holy symbol item. + */ + private static final Item HOLY_SYMBOL = new Item(1718); + + /** + * Represents the coins item. + */ + private static final Item COINS = new Item(995, 99000); + + /** + * Represents the master talking. + */ + private Master master; + + /** + * The quest instance. + */ + private Quest quest; + + /** + * If we're chatting about our diary. + */ + private boolean isDiary; + + private final int level = 2; + + /** + * Constructs a new {@code SlayerMasterDialogue} {@code Object}. + */ + public SlayerMasterDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code SlayerMasterDialogue} {@code Object}. + * + * @param player the player. + */ + public SlayerMasterDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new SlayerMasterDialogue(player); + } + + @Override + public boolean open(Object... args) { + if (args[0] instanceof NPC) { + npc = (NPC) args[0]; + } + master = Master.forId(args[0] instanceof NPC ? ((NPC) args[0]).getId() : (int) args[0]); + quest = player.getQuestRepository().getQuest(Quests.ANIMAL_MAGNETISM); + + if (master == Master.DURADEL) { + if (Skillcape.isMaster(player, Skills.SLAYER)) { + options("Ask about Skillcape", "Something else"); + stage = 900; + return true; + } + } + interpreter.sendDialogues(master.getNpc(), getExpression(master), "'Ello, and what are you after, then?"); + if (master == Master.VANNAKA) { + stage = -1; + } else { + stage = 0; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + if (isDiary) { + switch (stage) { + case 999: + end(); + break; + case -1: + options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); + stage++; + break; + case 0: + 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: + player("See you later."); + stage = 999; + break; + } + break; + case 440: + npc("I can imagine! I expect you'll be wanting a reward for", "your hard efforts, eh?"); + stage++; + break; + case 441: + player("Yes, please."); + stage++; + break; + case 442: + AchievementDiary.grantReplacement(player, DiaryType.VARROCK, level); + npc("I'm happy to say that you've done well, so I will reward", "you suitably for your work."); + stage++; + break; + case 443: + player("Great! Thanks."); + stage++; + break; + case 444: + sendDialogue("Vannaka takes the Varrock armour and carves some symbols into it.", "He waves his hands over the symbols and the armour appears to glow", "brilliantly."); + stage++; + break; + case 450: + AchievementDiary.grantReplacement(player, DiaryType.VARROCK, level); + npc("You 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 in Varrock it can help you", "discover some quite useful things. Eventually, with", "enough exploration, the people of Varrock 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 levels of Varrock Armour,", "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 Varrock are happy to see", "you visiting the land."); + stage = 41; + break; + case 430: + npc("Just complete the tasks so they're all ticked off, then", "you can claim your 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 Varrock Armour, speak to Vannaka", "Rat Burgis, and myself."); + stage = 41; + break; + case 50: + end(); + break; + } + return true; + } + switch (stage) { + case 999: + end(); + break; + case -1: // vannaka - has options for achievement diary + if (!SlayerManager.getInstance(player).hasStarted()) { + options("Who are you?", "Do you have anything for trade?", "Er...nothing...", "I have a question about my Achievement Diary."); + stage = 1; + } else { + options("I need another assignment.", "Do you have anything for trade?", "Er...nothing...", "I have a question about my Achievement Diary."); + stage = 690; + } + break; + case 0: // not vannaka + if (master == Master.TURAEL) { // only give option to talk about holy axe with Turael + if (quest.getStage(player) == 30) { + options("I need another assignment.", "Do you have anything for trade?", "Er...nothing...", "I'm here about a quest."); + stage = 700; + break; + } else if (quest.getStage(player) == 31) { + options("I need another assignment.", "Do you have anything for trade?", "Er...nothing...", "Hello, I'm here about those trees again."); + stage = 700; + break; + } + } + if (!SlayerManager.getInstance(player).hasStarted()) { + options("Who are you?", "Do you have anything for trade?", "Er...nothing..."); + stage = 1; + } else { + options("I need another assignment.", "Do you have anything for trade?", "Er...nothing..."); + stage = 700; + } + break; + case 1: + switch (buttonId) { + case 1: + player("Who are you?"); + stage = 10; + break; + case 2: + player("Do you have anything for trade?"); + stage = 20; + break; + case 3: + player("Er...nothing..."); + stage = 999; + break; + case 4: + player("I have a question about my Achievement Diary."); + stage = 691; + break; + } + break; + case 10: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "I'm " + (master.getNpc() == 8273 ? "the lowest level Slayer Master available." : "one of the elite Slayer Masters."), + master.getNpc() == 8273 ? "The other Slayer Masters are spread around the world." : "I can teach you about the ways of the Slayer."); + stage = 11; + break; + case 11: + options("What's a Slayer?", "Never heard of you..."); + stage = 12; + break; + case 12: + switch (buttonId) { + case 1: + player("What's a Slayer?"); + stage = 100; + break; + case 2: + player("Never heard of you..."); + stage = 2000; + break; + } + break; + case 20: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "I have a wide selection of Slayer equipment; take a look!"); + stage = 21; + break; + case 21: + end(); + if (npc != null) { + npc.openShop(player); + } + break; + case 100: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "Oh dear, what do they teach you in school?"); + stage = 101; + break; + case 101: + player("Well....er..."); + stage = 102; + break; + case 102: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "I suppose I'll have to educate you, then. A Slayer is", "someone who is trained to fight specific creatures. They", "know those creatures' every weakenss and strength. As", "you can guess, it makes killing those creatures a lot"); + stage = 103; + break; + case 103: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "easier."); + stage = 104; + break; + case 104: + options("Wow, can you teach me?", "Sounds useless to me."); + stage = 105; + break; + case 105: + switch (buttonId) { + case 1: + player("Wow, can you teach me?"); + stage = 500; + break; + case 2: + player("Sounds useless to me."); + stage = 1000; + break; + } + break; + case 500: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "Hmmm, well, I'm not so sure..."); + stage = 501; + break; + case 501: + player("Pleeeaasssse! I'll be your best friend!"); + stage = 502; + break; + 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 = 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."); + stage = 503; + break; + case 503: + player("Okay then, what's first?"); + stage = 504; + break; + case 504: + 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 " + 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."); + stage = 999; + } + break; + case 510: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "You'll also need this enchanted gem. It allows Slayer", "Masters like myself to contact you and update you on", "your progress. Don't worry if you lose it; you can buy", "another from any Slayer Master."); + stage = 511; + break; + case 511: + player("Okay, great!"); + stage = 999; + break; + case 1000: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "That's what you think.."); + stage = 999; + break; + case 2000: + interpreter.sendDialogues(master.getNpc(), getExpression(master), "I am one of the greatest Slayer masters!"); + stage = 999; + break; + case 690: + switch (buttonId) { + case 1: + player("I need another assignment."); + stage = 701; + break; + case 2: + player("Do you have anything for trade?"); + stage = 20; + break; + case 3: + player("Er...nothing..."); + stage = 30; + break; + case 4: + player("I have a question about my Achievement Diary."); + stage = 691; + break; + } + break; + case 691: + sendDiaryDialogue(); + break; + case 700: + switch (buttonId) { + case 1: + player("I need another assignment."); + stage = 701; + break; + case 2: + player("Do you have anything for trade?"); + stage = 20; + break; + case 3: + player("Er...nothing..."); + stage = 30; + break; + case 4: + if (quest.getStage(player) == 30) { + player("I'm here about a quest. Ava said she saw you hanging", "around the moving trees near Draynor Manor."); + stage = 8000; + } else if (quest.getStage(player) == 31) { + player("Hello, I'm here about those trees again."); + stage = 8006; + } + break; + } + break; + case 8000:// avas. + npc("Ahh, you came to the right man, odd things, those trees.", "What is it you are needing exactly?"); + stage++; + break; + case 8001: + player("I think I need some of the wood from them, but my", "axe just bounced off the trunk."); + stage++; + break; + case 8002: + npc("Sounds like you need a blessed axe. No one really", "makes them, though these days."); + stage++; + break; + case 8003: + npc("If you can give me a mithril axe and a holy symbol of", "Saradomin I can let you have my axe. I'll make myself", "a new one when no one is pestering me for Slayer", "tasks."); + stage++; + break; + case 8004: + player("Okay, so I'll see whether I can spare an axe and a", "symbol. Thanks."); + stage++; + break; + case 8005: + quest.setStage(player, 31); + end(); + break; + case 8006: + if (player.hasItem(new Item(10491))) { + npc("You already have an axe."); + stage = 999; + break; + } + npc("I can make an axe for you now, if you wish.", "Remember, it will be no use for normal wooducutting", "after I have added the silver edge."); + stage++; + break; + case 8007: + player("I'd love one, thanks."); + stage++; + break; + case 8008: + if (!player.getInventory().containsItem(MITHRIL_AXE)) { + npc("You'll need to hand over both a mithril axe and a holy", "symbol of Saradomin. You don't have an axe in your", "pack, so I'm not able to help."); + stage = 999; + break; + } + if (!player.getInventory().containsItem(HOLY_SYMBOL)) { + npc("You'll need to hand over both a mithril axe and a holy", "symbol of Saradomin. You don't have a holy symbol in", "your pack, so I'm not able to help."); + stage = 999; + break; + } + if (player.getInventory().remove(MITHRIL_AXE, HOLY_SYMBOL)) { + player.getInventory().add(new Item(10491)); + npc("Here's a new axe; may it serve you well."); + stage = 999; + } + break; + 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 = 999; + break; + } + boolean hasTask = SlayerManager.getInstance(player).hasTask(); + boolean reroll = hasTask && master == Master.TURAEL && !Master.hasSameTask(master, player); + if (!hasTask || reroll) { + SlayerManager.getInstance(player).generate(master); + 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; + } + 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: + options("Got any tips for me?", "Okay, great!"); + stage++; + break; + case 845: + switch (buttonId) { + case 1: + interpreter.sendDialogues(master.getNpc(), getExpression(master), SlayerManager.getInstance(player).getTask().getTip()); + stage = 860; + break; + case 2: + player("Okay, great!"); + stage = 999; + break; + } + break; + case 860: + player("Great, thanks!"); + stage = 999; + break; + case 900: + switch (buttonId) { + case 1: + player("Can I buy a Skillcape of Slayer?"); + stage = 901; + break; + case 2: + interpreter.sendDialogues(master.getNpc(), FacialExpression.HALF_GUILTY, "'Ello, and what are you after, then?"); + stage = 0; + break; + } + break; + case 901: + interpreter.sendDialogues(Master.DURADEL.getNpc(), FacialExpression.HALF_GUILTY, "Certainly! Right when you give me 99000 coins."); + stage = 902; + break; + case 902: + options("Okay, here you go.", "No, thanks."); + stage = 903; + break; + case 903: + switch (buttonId) { + case 1: + player("Okay, here you go."); + stage = 904; + break; + case 2: + end(); + break; + } + break; + case 904: + if (Skillcape.purchase(player, Skills.SLAYER)) { + interpreter.sendDialogues(Master.DURADEL.getNpc(), FacialExpression.HALF_GUILTY, "There you go! Enjoy."); + } + stage = 999; + break; + } + return true; + } + + /** + * Checks which expression to use. + * + * @param master the master. + * @return the expression. + */ + private FacialExpression getExpression(Master master) { + if (master == Master.CHAELDAR) { + return FacialExpression.OLD_NORMAL; + } + return FacialExpression.HALF_GUILTY; + } + + /** + * Sends the diary dialogue. + */ + private void sendDiaryDialogue() { + isDiary = true; + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.VARROCK, level)) { + player("I've completed all the hard tasks in my Varrock", "Achievement Diary and, let me tell you, it wasn't an", "easy job."); + stage = 440; + return; + } + if (AchievementDiary.canReplaceReward(player, DiaryType.VARROCK, level)) { + player("I've seemed to have lost my armour..."); + stage = 460; + return; + } + options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); + stage = 0; + } + + @Override + public int[] getIds() { + return new int[]{70, 1598, 1596, 1597, 1599, 7780, 8275, 8273, 8274, 8649}; + } +} diff --git a/Server/src/main/content/global/skill/slayer/SlayerPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerPlugin.java new file mode 100644 index 000000000..eb02797d6 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/SlayerPlugin.java @@ -0,0 +1,85 @@ +package content.global.skill.slayer; + +import core.cache.def.impl.SceneryDefinition; +import core.game.global.action.DigAction; +import core.game.global.action.DigSpadeHandler; +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; + +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + +/** + * Handles related slayer nodes. + * @author Vexia + */ +@Initializable +public class SlayerPlugin extends OptionHandler { + + /** + * The bryne dig locations. + */ + private static final Location[] BRYNE_DIGS = new Location[] { new Location(2749, 3733, 0), new Location(2748, 3733, 0), new Location(2747, 3733, 0), new Location(2747, 3734, 0), new Location(2747, 3735, 0), new Location(2747, 3736, 0), new Location(2748, 3736, 0), new Location(2749, 3736, 0) }; + + @Override + public Plugin newInstance(Object arg) throws Throwable { + 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); + for (Location loc : BRYNE_DIGS) { + DigSpadeHandler.register(loc, new DigAction() { + @Override + public void run(Player player) { + player.teleport(new Location(2697, 10119, 0)); + player.sendMessages("You dig a hole...", "...And fall into a dark and slimy pit!"); + } + + }); + } + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + switch (node.getId()) { + 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; + } + return true; + } + + @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); + } + return null; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerRewardPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java similarity index 77% rename from Server/src/main/java/core/game/node/entity/skill/slayer/SlayerRewardPlugin.java rename to Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java index 1951d3344..247e87b82 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerRewardPlugin.java +++ b/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; @@ -14,7 +14,10 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; + +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles the slayer reward interface plugin. @@ -44,7 +47,7 @@ public class SlayerRewardPlugin extends ComponentPlugin { ComponentDefinition.forId(161).setPlugin(this);//assignment ComponentDefinition.forId(163).setPlugin(this);//learn ComponentDefinition.forId(164).setPlugin(this);//buy - PluginManager.definePlugins(new SlayerMasterPlugin(), new SlayerHelmCraftPlugin()); + ClassScanner.definePlugins(new SlayerMasterPlugin(), new SlayerHelmCraftPlugin()); return this; } @@ -60,28 +63,27 @@ public class SlayerRewardPlugin extends ComponentPlugin { switch (button) { case 23://reassign case 26: - if (!player.getSlayer().hasTask()) { + if (!SlayerManager.getInstance(player).hasTask()) { player.sendMessage("You don't have an active task right now."); break; } if (purchase(player, 30)) { - player.getSlayer().clear(); - player.getSlayer().setTaskCount(0); + SlayerManager.getInstance(player).clear(); player.sendMessage("You have canceled your current task."); } break; case 24: case 27: - if (player.getSlayer().getTask() == null) { + if (SlayerManager.getInstance(player).getTask() == null) { player.sendMessage("You don't have a slayer task."); break; } - if (player.getSlayer().getRemoved().size() >= 4) { + if (SlayerManager.getInstance(player).getRemoved().size() >= 4) { player.sendMessage("You can't remove anymore tasks."); break; } - if (player.getSlayer().getSlayerPoints() >= 30 && !player.isAdmin()) { - int size = player.getSlayer().getRemoved().size(); + if (SlayerManager.getInstance(player).getSlayerPoints() >= 30 && !player.isAdmin()) { + int size = SlayerManager.getInstance(player).getRemoved().size(); int qp = player.getQuestRepository().getAvailablePoints(); if (size == 0 && qp < 50) { player.sendMessage("You need 50 quest points as a requirement in order to block one task."); @@ -98,8 +100,8 @@ public class SlayerRewardPlugin extends ComponentPlugin { } } if (purchase(player, 100)) { - player.getSlayer().getRemoved().add(player.getSlayer().getTask()); - player.getSlayer().clear(); + SlayerManager.getInstance(player).getRemoved().add(SlayerManager.getInstance(player).getTask()); + SlayerManager.getInstance(player).clear(); updateInterface(player, player.getInterfaceManager().getOpened()); } break; @@ -108,10 +110,10 @@ public class SlayerRewardPlugin extends ComponentPlugin { case 38: case 39: int index = 3 - (39 - button); - if (player.getSlayer().getRemoved().isEmpty() || index > player.getSlayer().getRemoved().size() - 1 || player.getSlayer().getRemoved().get(index) == null) { + if (SlayerManager.getInstance(player).getRemoved().isEmpty() || index > SlayerManager.getInstance(player).getRemoved().size() - 1 || SlayerManager.getInstance(player).getRemoved().get(index) == null) { break; } - player.getSlayer().getRemoved().remove(index); + SlayerManager.getInstance(player).getRemoved().remove(index); updateInterface(player, player.getInterfaceManager().getOpened()); break; case 15: @@ -132,34 +134,34 @@ public class SlayerRewardPlugin extends ComponentPlugin { break; case 22://Broad arrows case 29: - if (player.getSlayer().flags.isBroadsUnlocked()) { + if (SlayerManager.getInstance(player).flags.isBroadsUnlocked()) { player.sendMessage("You don't need to learn this ability again."); break; } if (purchase(player, 300)) { - player.getSlayer().flags.unlockBroads(); + SlayerManager.getInstance(player).flags.unlockBroads(); updateInterface(player, component); } break; case 23://Slayer ring case 30: - if (player.getSlayer().flags.isRingUnlocked()) { + if (SlayerManager.getInstance(player).flags.isRingUnlocked()) { player.sendMessage("You don't need to learn this ability again."); break; } if (purchase(player, 300)) { - player.getSlayer().flags.unlockRing(); + SlayerManager.getInstance(player).flags.unlockRing(); updateInterface(player, component); } break; case 24://Slayer helm case 31: - if (player.getSlayer().flags.isHelmUnlocked()) { + if (SlayerManager.getInstance(player).flags.isHelmUnlocked()) { player.sendMessage("You don't need to learn this ability again."); break; } if (purchase(player, 400)) { - player.getSlayer().flags.unlockHelm(); + SlayerManager.getInstance(player).flags.unlockHelm(); updateInterface(player, component); } break; @@ -181,7 +183,7 @@ public class SlayerRewardPlugin extends ComponentPlugin { break; case 26://ring of slaying case 33: - if (player.getInventory().freeSlots() < 1 && player.getSlayer().getSlayerPoints() >= 75) { + if (player.getInventory().freeSlots() < 1 && SlayerManager.getInstance(player).getSlayerPoints() >= 75) { player.sendMessage("You don't have enough inventory space."); break; } @@ -221,11 +223,11 @@ public class SlayerRewardPlugin extends ComponentPlugin { * @return {@code True} if purchased. */ private boolean purchase(Player player, int amount) { - if (player.getSlayer().getSlayerPoints() < amount) { + if (SlayerManager.getInstance(player).getSlayerPoints() < amount) { player.sendMessage("You need " + amount + " slayer points in order to purchase this reward."); return false; } - player.getSlayer().setSlayerPoints(player.getSlayer().getSlayerPoints() - amount); + SlayerManager.getInstance(player).setSlayerPoints(SlayerManager.getInstance(player).getSlayerPoints() - amount); updateInterface(player, player.getInterfaceManager().getOpened()); return true; } @@ -250,7 +252,7 @@ public class SlayerRewardPlugin extends ComponentPlugin { return; } String space = ""; - String num = String.valueOf(player.getSlayer().getSlayerPoints()); + String num = String.valueOf(SlayerManager.getInstance(player).getSlayerPoints()); if (num != "0") { for (int i = 0; i < num.length(); i++) { space += " "; @@ -262,31 +264,31 @@ public class SlayerRewardPlugin extends ComponentPlugin { String[] letters = new String[] {"A", "B", "C", "D"}; Tasks task = null; for (int i = 0; i < 4; i++) { - task = i > player.getSlayer().getRemoved().size() - 1 ? null : player.getSlayer().getRemoved().get(i); + task = i > SlayerManager.getInstance(player).getRemoved().size() - 1 ? null : SlayerManager.getInstance(player).getRemoved().get(i); player.getPacketDispatch().sendString(task == null ? letters[i] : task.getName(), open.getId(), childs[i]); } - player.getPacketDispatch().sendString(space + player.getSlayer().getSlayerPoints(), open.getId(), 19); + player.getPacketDispatch().sendString(space + SlayerManager.getInstance(player).getSlayerPoints(), open.getId(), 19); break; case 163://learn for (int i = 0; i < 3; i++) { switch(i){ case 0: - player.getPacketDispatch().sendInterfaceConfig(open.getId(), 25 + i, !player.getSlayer().flags.isBroadsUnlocked()); + player.getPacketDispatch().sendInterfaceConfig(open.getId(), 25 + i, !SlayerManager.getInstance(player).flags.isBroadsUnlocked()); break; case 1: - player.getPacketDispatch().sendInterfaceConfig(open.getId(), 25 + i, !player.getSlayer().flags.isRingUnlocked()); + player.getPacketDispatch().sendInterfaceConfig(open.getId(), 25 + i, !SlayerManager.getInstance(player).flags.isRingUnlocked()); break; case 2: - player.getPacketDispatch().sendInterfaceConfig(open.getId(), 25 + i, !player.getSlayer().flags.isHelmUnlocked()); + player.getPacketDispatch().sendInterfaceConfig(open.getId(), 25 + i, !SlayerManager.getInstance(player).flags.isHelmUnlocked()); break; default: break; } } - player.getPacketDispatch().sendString(space + player.getSlayer().getSlayerPoints(), open.getId(), 18); + player.getPacketDispatch().sendString(space + SlayerManager.getInstance(player).getSlayerPoints(), open.getId(), 18); break; case 164://buy - player.getPacketDispatch().sendString(space + player.getSlayer().getSlayerPoints(), open.getId(), 20); + player.getPacketDispatch().sendString(space + SlayerManager.getInstance(player).getSlayerPoints(), open.getId(), 20); break; } } @@ -308,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; } @@ -346,7 +350,7 @@ public class SlayerRewardPlugin extends ComponentPlugin { @Override public Plugin newInstance(Object arg) throws Throwable { addHandler(SPINY_HELMET.getId(), ITEM_TYPE, this); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { @@ -382,7 +386,7 @@ public class SlayerRewardPlugin extends ComponentPlugin { player.sendMessage("You need a Crafting level of at least 55 in order to do this."); return true; } - if (!player.getSlayer().flags.isHelmUnlocked()) { + if (!SlayerManager.getInstance(player).flags.isHelmUnlocked()) { player.sendMessage("You need to unlock the ability to do that first."); return true; } diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerTowerPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerTowerPlugin.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/skill/slayer/SlayerTowerPlugin.java rename to Server/src/main/content/global/skill/slayer/SlayerTowerPlugin.java index ffe1ac96c..682ed5bdf 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerTowerPlugin.java +++ b/Server/src/main/content/global/skill/slayer/SlayerTowerPlugin.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -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/java/core/game/node/entity/skill/slayer/SlayerTowerZone.java b/Server/src/main/content/global/skill/slayer/SlayerTowerZone.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/slayer/SlayerTowerZone.java rename to Server/src/main/content/global/skill/slayer/SlayerTowerZone.java index 292306344..c9c583f04 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerTowerZone.java +++ b/Server/src/main/content/global/skill/slayer/SlayerTowerZone.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.slayer; +package content.global.skill.slayer; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.Option; import core.game.node.Node; diff --git a/Server/src/main/content/global/skill/slayer/SlayerUtils.kt b/Server/src/main/content/global/skill/slayer/SlayerUtils.kt new file mode 100644 index 000000000..c034fc557 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/SlayerUtils.kt @@ -0,0 +1,80 @@ +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 core.tools.RandomFunction +import org.rs09.consts.Items + +object SlayerUtils { + fun generate(player: Player, master: Master): Master.Task? + { + val tasks: MutableList = ArrayList(10) + val taskWeightSum = intArrayOf(0) + master.tasks.stream().filter { task: Master.Task -> canBeAssigned(player, task.task) && task.task.combatCheck <= player.properties.currentCombatLevel }.forEach { task: Master.Task -> + taskWeightSum[0] += task.weight + tasks.add(task) + } + tasks.shuffle(RandomFunction.RANDOM) + var rnd = RandomFunction.random(taskWeightSum[0]) + for (task in tasks) { + if (rnd < task!!.weight) return task + rnd -= task.weight + } + return null + } + + fun canBeAssigned(player: Player, task: Tasks): Boolean + { + return player.getSkills().getLevel(Skills.SLAYER) >= task.levelReq && !SlayerManager.getInstance(player).flags.removed.contains(task) && task.hasQuestRequirements(player) + } + + fun assign(player: Player, task: Master.Task, master: Master) + { + SlayerManager.getInstance(player).master = master + 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) + } + setVarp(player, 2502, SlayerManager.getInstance(player).flags.taskFlags shr 4) + } + + @JvmStatic + fun hasBroadWeaponEquipped(player: Player, state: BattleState): Boolean { + return (state.weapon != null && state.weapon.id == Items.LEAF_BLADED_SPEAR_4158 || + state.weapon != null && state.weapon.id == Items.LEAF_BLADED_SWORD_13290 || + state.ammunition != null && (state.ammunition.itemId == Items.BROAD_ARROW_4160 || state.ammunition.itemId == Items.BROAD_TIPPED_BOLTS_13280) || + state.spell != null && state.spell.spellId == 31 && player.spellBookManager + .spellBook == SpellBook.MODERN.interfaceId + ) + } + + @JvmStatic + fun pluralise(str: String): String { + return when (str) { + "black bear" -> "bears" + "cyclops" -> "cyclopes" + "guard dog" -> "dogs" + "dwarf" -> "dwarves" + "elf warrior" -> "elves" + "jelly" -> "jellies" + "nechryael" -> str // the plural and singular is the same + "turoth" -> str // the plural and singular is the same + "tzhaar-mej" -> "tzHaar" + "werewolf" -> "werewolves" + "wolf" -> "wolves" + "kalphite worker" -> "kalphites" + "scarab swarm" -> "scabarites" + else -> str + "s" + } + } +} diff --git a/Server/src/main/content/global/skill/slayer/Tasks.java b/Server/src/main/content/global/skill/slayer/Tasks.java new file mode 100644 index 000000000..15b905dba --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/Tasks.java @@ -0,0 +1,174 @@ +package content.global.skill.slayer; + + +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, 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{ + Arrays.stream(Tasks.values()).forEach(entry -> Arrays.stream(entry.ids).forEach(id -> taskMap.putIfAbsent(id,entry))); + } + public final int levelReq; + public final int combatCheck; + public final String[] info; + public final int[] ids; + 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, Quests questReq) { + this.combatCheck = combatCheck; + this.ids = ids; + this.info = info; + this.levelReq = levelReq; + this.questReq = questReq; + } + + public int[] getNpcs(){ + return ids; + } + + public String[] getTip(){ + return info; + } + + public boolean hasQuestRequirements (Player player) { + return questReq == null || hasRequirement(player, questReq, false); + } + + public static Tasks forId(int id){ + return taskMap.get(id); + } + + public String getName(){ + return NPCDefinition.forId(ids[0]).getName(); + } + +} 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/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/java/core/game/node/entity/skill/slayer/dungeon/AncientCavern.java b/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/AncientCavern.java rename to Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java index 2e12a707a..f86de623f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/AncientCavern.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/AncientCavern.java @@ -1,11 +1,11 @@ -package core.game.node.entity.skill.slayer.dungeon; +package content.global.skill.slayer.dungeon; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.SceneryDefinition; import core.game.component.CloseEvent; import core.game.component.Component; import core.plugin.Initializable; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.Option; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -17,7 +17,7 @@ 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 rs09.game.world.GameWorld; +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; @@ -28,7 +28,7 @@ import core.net.packet.PacketRepository; import core.net.packet.context.MinimapStateContext; import core.net.packet.out.MinimapState; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; /** @@ -58,7 +58,7 @@ public final class AncientCavern extends MapZone implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { @@ -68,7 +68,7 @@ public final class AncientCavern extends MapZone implements Plugin { @Override public boolean handle(final Player player, Node node, String option) { - ContentAPI.lock(player, 30); + lock(player, 30); AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(0, -6, 0), Animation.create(6723), 10, 0.0, null); GameWorld.getPulser().submit(new Pulse(1, player) { int count; @@ -87,7 +87,7 @@ public final class AncientCavern extends MapZone implements Plugin { player.getPacketDispatch().sendMessages("You dive into the swirling maelstorm of the whirlpool.", "You are swirled beneath the water, the darkness and pressure are overwhelming.", "Mystical forces guide you into a cavern below the whirlpool."); break; case 8: - ContentAPI.unlock(player); + unlock(player); return true; } return false; @@ -152,11 +152,11 @@ 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)); - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); + player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); break; case 13: player.getProperties().setTeleportLocation(Location.create(2532, 3412, 0)); diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/FremennikDungeon.java b/Server/src/main/content/global/skill/slayer/dungeon/FremennikDungeon.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/FremennikDungeon.java rename to Server/src/main/content/global/skill/slayer/dungeon/FremennikDungeon.java index d896511f3..cb1cb5c49 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/FremennikDungeon.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/FremennikDungeon.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.slayer.dungeon; +package content.global.skill.slayer.dungeon; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -10,12 +10,13 @@ import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; 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.map.zone.ZoneRestriction; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.tools.RandomFunction; @@ -31,7 +32,7 @@ public final class FremennikDungeon extends MapZone implements Plugin { * Constructs a new {@code FremennikDungeon} {@code Object}. */ public FremennikDungeon() { - super("fremennik", true); + super("fremennik", true, ZoneRestriction.CANNON); } @Override diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/LumbridgeDungeon.java b/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/LumbridgeDungeon.java rename to Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java index cfca0ec06..af34faff2 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/LumbridgeDungeon.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/LumbridgeDungeon.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.slayer.dungeon; +package content.global.skill.slayer.dungeon; +import content.global.skill.slayer.SlayerEquipmentFlags; import core.cache.def.impl.NPCDefinition; -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -9,7 +9,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.zone.MapZone; @@ -18,7 +18,7 @@ import core.game.world.map.zone.ZoneBuilder; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; import java.util.HashMap; @@ -28,7 +28,6 @@ import java.util.Map; * Handles the lumbridge dungeon. * @author Vexia */ -@Initializable public final class LumbridgeDungeon extends MapZone implements Plugin { /** @@ -46,7 +45,7 @@ public final class LumbridgeDungeon extends MapZone implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); - PluginManager.definePlugin(new WallBeastNPC()); + ClassScanner.definePlugin(new WallBeastNPC()); return this; } diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/SmokeDungeon.java b/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/SmokeDungeon.java rename to Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java index 875a6cded..3dec3dd92 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/SmokeDungeon.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/SmokeDungeon.java @@ -1,22 +1,22 @@ -package core.game.node.entity.skill.slayer.dungeon; +package content.global.skill.slayer.dungeon; +import content.global.skill.slayer.SlayerEquipmentFlags; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; import java.util.ArrayList; @@ -71,7 +71,7 @@ public final class SmokeDungeon extends MapZone implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { @@ -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/java/core/game/node/entity/skill/slayer/dungeon/StrongholdSlayerCave.java b/Server/src/main/content/global/skill/slayer/dungeon/StrongholdSlayerCave.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/StrongholdSlayerCave.java rename to Server/src/main/content/global/skill/slayer/dungeon/StrongholdSlayerCave.java index 018171acf..03852d18f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/StrongholdSlayerCave.java +++ b/Server/src/main/content/global/skill/slayer/dungeon/StrongholdSlayerCave.java @@ -1,7 +1,7 @@ /* package core.game.node.entity.skill.slayer.dungeon; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import core.game.node.entity.skill.gather.SkillingTool; import core.game.node.entity.skill.slayer.Master; @@ -74,8 +74,8 @@ public class StrongholdSlayerCave extends MapZone implements Plugin { return true; } attack: { - if (player.getSlayer().getMaster() == Master.NIEVE) { - for (int i : player.getSlayer().getTask().getNpcs()) { + if (SlayerManager.getInstance(player).getMaster() == Master.NIEVE) { + for (int i : SlayerManager.getInstance(player).getTask().getNpcs()) { if (i != id) { continue; } @@ -206,8 +206,8 @@ public class StrongholdSlayerCave extends MapZone implements Plugin { return super.continueAttack(player, target, style, message); } int id = target.getId(); - if (player.getSlayer().getMaster() == Master.NIEVE) { - for (int i : player.getSlayer().getTask().getNpcs()) { + if (SlayerManager.getInstance(player).getMaster() == Master.NIEVE) { + for (int i : SlayerManager.getInstance(player).getTask().getNpcs()) { if (i != id) { continue; } diff --git a/Server/src/main/java/core/game/node/entity/skill/smithing/BarType.java b/Server/src/main/content/global/skill/smithing/BarType.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/smithing/BarType.java rename to Server/src/main/content/global/skill/smithing/BarType.java index fff3d85e0..1052998af 100644 --- a/Server/src/main/java/core/game/node/entity/skill/smithing/BarType.java +++ b/Server/src/main/content/global/skill/smithing/BarType.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.smithing; +package content.global.skill.smithing; /** * @author Emperor diff --git a/Server/src/main/java/core/game/node/entity/skill/smithing/Bars.java b/Server/src/main/content/global/skill/smithing/Bars.java similarity index 99% rename from Server/src/main/java/core/game/node/entity/skill/smithing/Bars.java rename to Server/src/main/content/global/skill/smithing/Bars.java index 38b21f09e..7514693fe 100644 --- a/Server/src/main/java/core/game/node/entity/skill/smithing/Bars.java +++ b/Server/src/main/content/global/skill/smithing/Bars.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.smithing; +package content.global.skill.smithing; import core.game.node.entity.player.Player; @@ -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 new file mode 100644 index 000000000..64833097c --- /dev/null +++ b/Server/src/main/content/global/skill/smithing/DragonShieldDialogue.java @@ -0,0 +1,105 @@ +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.plugin.Initializable; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; + +/** + * Represents the dialogue plugin used for making a dragon shield. + * @author 'Vexia + * @author Player Name + * @version 1.1 + */ +@Initializable +public final class DragonShieldDialogue extends DialoguePlugin { + /** + * Constructs a new {@code DragonShieldDialogue} {@code Object}. + */ + public DragonShieldDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code DragonShieldDialogue} {@code Object}. + * @param player the player. + */ + public DragonShieldDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new DragonShieldDialogue(player); + } + + @Override + public boolean open(Object... args) { + 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) { + 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; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + 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); + } + stage = 1; + break; + case 1: + end(); + break; + case 10: + 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); + } + stage = 1; + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 82127843 }; + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/smithing/FurnaceOptionPlugin.java b/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java similarity index 76% rename from Server/src/main/java/core/game/node/entity/skill/smithing/FurnaceOptionPlugin.java rename to Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java index 9a8433b6a..be0190327 100644 --- a/Server/src/main/java/core/game/node/entity/skill/smithing/FurnaceOptionPlugin.java +++ b/Server/src/main/content/global/skill/smithing/FurnaceOptionPlugin.java @@ -1,10 +1,8 @@ -package core.game.node.entity.skill.smithing; +package content.global.skill.smithing; +import content.global.skill.smithing.smelting.Bar; import core.cache.def.impl.SceneryDefinition; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.smithing.smelting.Bar; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -12,9 +10,8 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; @@ -22,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. @@ -52,11 +50,6 @@ public final class FurnaceOptionPlugin extends OptionHandler { @Override public boolean handle(final Player player, Node node, String option) { - switch (((Scenery) node).getId()) { - case 3044: - handleTutorialIsland(player); - return true; - } if (node.getId() == 26814 && !player.getAchievementDiaryManager().getDiary(DiaryType.VARROCK).isComplete(0)) { player.sendMessage("You need to have completed the easy tasks in the Varrock Diary in order to use this."); return true; @@ -72,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); @@ -85,29 +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()); - if (TutorialSession.getExtension(player).getStage() == 39) { - TutorialStage.load(player, 40, false); - } - return true; - } - - }); - } - } - /** * Represents the plugin used to handle the ore on the furance. * @author 'Vexia @@ -118,7 +88,7 @@ public final class FurnaceOptionPlugin extends OptionHandler { /** * Represents the ids. */ - private static final int[] IDS = new int[] { 4304, 6189, 11010, 11666, 12100, 12809, 14921, 18497, 26814, 30021, 30510, 36956, 37651 }; + public static final int[] furnaceIDS = new int[] { 4304, 6189, 11010, 11666, 12100, 12809, 14921, 18497, 26814, 30021, 30510, 36956, 37651 }; /** * Constructs a new {@code SmeltUseWithHandler} {@code Object}. @@ -129,7 +99,7 @@ public final class FurnaceOptionPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - for (int i : IDS) { + for (int i : furnaceIDS) { addHandler(i, OBJECT_TYPE, this); } return this; diff --git a/Server/src/main/content/global/skill/smithing/SmithingBuilder.java b/Server/src/main/content/global/skill/smithing/SmithingBuilder.java new file mode 100644 index 000000000..6159f161b --- /dev/null +++ b/Server/src/main/content/global/skill/smithing/SmithingBuilder.java @@ -0,0 +1,142 @@ +package content.global.skill.smithing; + +import content.global.skill.smithing.smelting.Bar; +import core.game.component.Component; +import core.game.container.access.InterfaceContainer; +import core.game.node.entity.skill.Skills; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.tools.StringUtils; + +/** + * Represents the builder class to view the correct information, on the + * interface of smithing, ie, the items ammount, and name. + * @author 'Vexia + */ +public final class SmithingBuilder { + + /** + * The bar type. + */ + private BarType type; + + /** + * The bar used. + */ + private Bar bar; + + /** + * Constructs a new {@code SmithingBuilder.java} {@code Object}. + * @param item the item. + */ + public SmithingBuilder(Item item) { + this.bar = Bar.forId(item.getId()); + this.type = BarType.getBarTypeForId(item.getId()); + } + + /** + * Builds the interface of the smithing. + * @param player the player. + */ + public void build(Player player) { + player.getGameAttributes().removeAttribute("smith-type"); + player.getGameAttributes().setAttribute("smith-type", type); + 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) { + player.getPacketDispatch().sendInterfaceConfig(300, 169, false); + } + if (bars[i].getSmithingType() == SmithingType.TYPE_DART_TIP) { + player.getPacketDispatch().sendInterfaceConfig(300, 65, false); + } + 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_OIL_LANTERN) { + player.getPacketDispatch().sendInterfaceConfig(300, 161, false); + } + if (bars[i].getSmithingType() == SmithingType.TYPE_STUDS) { + player.getPacketDispatch().sendInterfaceConfig(300, 97, false); + } + String color = ""; + if (player.getSkills().getLevel(Skills.SMITHING) < bars[i].getLevel()) { + } else { + color = ""; + } + player.getPacketDispatch().sendString(color + StringUtils.formatDisplayName(bars[i].getSmithingType().name().replace("TYPE_", "")), 300, bars[i].getSmithingType().getName()); + if (player.getInventory().contains(bars[i].getBarType().getBarType(), bars[i].getSmithingType().getRequired())) { + color = ""; + } else { + color = null; + } + if (color != null) { + String amt = bars[i].getSmithingType().getRequired() > 1 ? "s" : ""; + player.getPacketDispatch().sendString(color + String.valueOf(bars[i].getSmithingType().getRequired()) + " Bar" + amt, 300, bars[i].getSmithingType().getName() + 1); + } + 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, 14); + player.getInterfaceManager().open(new Component(300)); + } + + /** + * Gets the type. + * @return the type. + */ + public BarType getType() { + return type; + } + + /** + * Gets the bar. + * @return the bar. + */ + public Bar getBar() { + return bar; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/smithing/SmithingPulse.java b/Server/src/main/content/global/skill/smithing/SmithingPulse.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/skill/smithing/SmithingPulse.java rename to Server/src/main/content/global/skill/smithing/SmithingPulse.java index 6b105f859..97a844d51 100644 --- a/Server/src/main/java/core/game/node/entity/skill/smithing/SmithingPulse.java +++ b/Server/src/main/content/global/skill/smithing/SmithingPulse.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.smithing; +package content.global.skill.smithing; +import content.global.skill.skillcapeperks.SkillcapePerks; +import core.game.event.ResourceProducedEvent; import core.cache.def.impl.ItemDefinition; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.skill.SkillPulse; @@ -11,7 +11,10 @@ import core.game.node.item.Item; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.tools.StringUtils; -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; + +import static core.api.ContentAPIKt.hasRequirement; +import static core.api.ContentAPIKt.sendDialogue; +import content.data.Quests; /** * Represents the pulse used to smith a bar. @@ -65,11 +68,12 @@ 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 (TutorialSession.getExtension(player).getStage() < TutorialSession.MAX_STAGE && node.getId() != Bars.BRONZE_DAGGER.getProduct()) { + 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; @@ -93,12 +97,10 @@ public class SmithingPulse extends SkillPulse { player.getInventory().remove(new Item(bar.getBarType().getBarType(), bar.getSmithingType().getRequired())); final Item item = new Item(node.getId(), bar.getSmithingType().getProductAmount()); player.getInventory().add(item); + player.dispatch(new ResourceProducedEvent(item.getId(), 1, player, bar.getBarType().getBarType())); player.getSkills().addExperience(Skills.SMITHING, bar.getBarType().getExperience() * bar.getSmithingType().getRequired(), true); String message = StringUtils.isPlusN(ItemDefinition.forId(bar.getProduct()).getName().toLowerCase()) ? "an" : "a"; player.getPacketDispatch().sendMessage("You hammer the " + bar.getBarType().getBarName().toLowerCase().replace("smithing", "") + "and make " + message + " " + ItemDefinition.forId(bar.getProduct()).getName().toLowerCase() + "."); - if (TutorialSession.getExtension(player).getStage() == 42) { - TutorialStage.load(player, 43, false); - } if (bar == Bars.BLURITE_CROSSBOW_LIMBS && player.getLocation().withinDistance(new Location(3000, 3145, 0), 10)) { // near Thurgo's anvil diff --git a/Server/src/main/java/core/game/node/entity/skill/smithing/SmithingType.java b/Server/src/main/content/global/skill/smithing/SmithingType.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/skill/smithing/SmithingType.java rename to Server/src/main/content/global/skill/smithing/SmithingType.java index 3b1d93c63..94465077e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/smithing/SmithingType.java +++ b/Server/src/main/content/global/skill/smithing/SmithingType.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.smithing; +package content.global.skill.smithing; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -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/java/core/game/node/entity/skill/smithing/smelting/Bar.java b/Server/src/main/content/global/skill/smithing/smelting/Bar.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/smithing/smelting/Bar.java rename to Server/src/main/content/global/skill/smithing/smelting/Bar.java index b85acb89d..04177c11a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/smithing/smelting/Bar.java +++ b/Server/src/main/content/global/skill/smithing/smelting/Bar.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.smithing.smelting; +package content.global.skill.smithing.smelting; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/skill/smithing/smelting/SmeltingPulse.java b/Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/skill/smithing/smelting/SmeltingPulse.java rename to Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java index d1ec780c4..7951ab0ed 100644 --- a/Server/src/main/java/core/game/node/entity/skill/smithing/smelting/SmeltingPulse.java +++ b/Server/src/main/content/global/skill/smithing/smelting/SmeltingPulse.java @@ -1,8 +1,11 @@ -package core.game.node.entity.skill.smithing.smelting; +package content.global.skill.smithing.smelting; -import api.ContentAPI; -import api.EquipmentSlot; +import static core.api.ContentAPIKt.*; + +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; @@ -14,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. @@ -85,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()) { @@ -109,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); } } } @@ -142,6 +148,7 @@ public class SmeltingPulse extends SkillPulse { } } player.getInventory().add(new Item(bar.getProduct().getId(), amt)); + player.dispatch(new ResourceProducedEvent(bar.getProduct().getId(), 1, player, -1)); double xp = bar.getExperience() * amt; // Goldsmith gauntlets if (((player.getEquipment().get(EquipmentContainer.SLOT_HANDS) != null @@ -171,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 = ContentAPI.getItemFromEquipment(player, EquipmentSlot.RING); - if(ring != null){ - if(ContentAPI.getCharge(ring) == 1000) ContentAPI.setCharge(ring, 140); - ContentAPI.adjustCharge(ring, -1); - if(ContentAPI.getCharge(ring) == 0){ - player.getEquipment().remove(ring); - ContentAPI.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/CarvedEvilTurnipListener.kt b/Server/src/main/content/global/skill/summoning/CarvedEvilTurnipListener.kt new file mode 100644 index 000000000..c78082f27 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/CarvedEvilTurnipListener.kt @@ -0,0 +1,21 @@ +import core.api.* +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class CarvedEvilTurnipListener : InteractionListener { + val knife = Items.KNIFE_946 + val evilTurnip = Items.EVIL_TURNIP_12134 + val carvedEvilTurnip = Items.CARVED_EVIL_TURNIP_12153 + + override fun defineListeners() { + onUseWith(IntType.ITEM, evilTurnip, knife) { player, used, with -> + if(removeItem(player, used.asItem())) { + sendMessage(player, "You carve a scary face into the evil turnip.") + sendMessage(player, "Wooo! It's enough to give you nightmares.") + return@onUseWith addItem(player, carvedEvilTurnip) + } + return@onUseWith false + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/ObeliskOptionPlugin.java b/Server/src/main/content/global/skill/summoning/ObeliskOptionPlugin.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/skill/summoning/ObeliskOptionPlugin.java rename to Server/src/main/content/global/skill/summoning/ObeliskOptionPlugin.java index 77b673cb1..3ecea4dc9 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/ObeliskOptionPlugin.java +++ b/Server/src/main/content/global/skill/summoning/ObeliskOptionPlugin.java @@ -1,5 +1,6 @@ -package core.game.node.entity.skill.summoning; +package content.global.skill.summoning; +import core.game.event.SummoningPointsRechargeEvent; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; @@ -9,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. @@ -30,14 +34,14 @@ 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."); return true; } return false; } - @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.setOptionHandler("infuse-pouch", this); diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningCreationPlugin.java b/Server/src/main/content/global/skill/summoning/SummoningCreationPlugin.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/summoning/SummoningCreationPlugin.java rename to Server/src/main/content/global/skill/summoning/SummoningCreationPlugin.java index 182fbc9ac..637b63ad6 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningCreationPlugin.java +++ b/Server/src/main/content/global/skill/summoning/SummoningCreationPlugin.java @@ -1,6 +1,6 @@ -package core.game.node.entity.skill.summoning; +package content.global.skill.summoning; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; import core.game.component.ComponentDefinition; @@ -8,11 +8,10 @@ import core.game.component.ComponentPlugin; 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.plugin.Initializable; import core.plugin.Plugin; import kotlin.Unit; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents a component plugin used to handle the summoning creation of a @@ -26,7 +25,7 @@ public final class SummoningCreationPlugin extends ComponentPlugin { public Plugin newInstance(Object arg) throws Throwable { ComponentDefinition.put(669, this); ComponentDefinition.put(673, this); - PluginManager.definePlugin(new ObeliskHandler()); + ClassScanner.definePlugin(new ObeliskHandler()); return this; } @@ -35,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: @@ -49,7 +49,7 @@ public final class SummoningCreationPlugin extends ComponentPlugin { SummoningCreator.create(player, getItemAmount(opcode), component.getId() == 669 ? SummoningPouch.forSlot(slot > 50 ? slot -1 : slot) : SummoningScroll.forId(slot > 50 ? slot -1 : slot)); break; case 234: - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { SummoningCreator.create(player, (int) value, component.getId() == 669 ? SummoningPouch.forSlot(slot > 50 ? slot -1 : slot) : SummoningScroll.forId(slot > 50 ? slot -1 : slot)); return Unit.INSTANCE; }); diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningCreator.java b/Server/src/main/content/global/skill/summoning/SummoningCreator.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/summoning/SummoningCreator.java rename to Server/src/main/content/global/skill/summoning/SummoningCreator.java index effcd02ca..d7a2827c9 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningCreator.java +++ b/Server/src/main/content/global/skill/summoning/SummoningCreator.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.summoning; +package content.global.skill.summoning; import core.cache.def.impl.CS2Mapping; import core.game.component.Component; @@ -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. @@ -60,7 +63,7 @@ public final class SummoningCreator { public static final void configure(final Player player, final boolean pouch) { player.getInterfaceManager().open(pouch ? SUMMONING_COMPONENT : SCROLL_COMPONENT); player.getPacketDispatch().sendRunScript(pouch ? 757 : 765, pouch ? "Iiissssss" : "Iiisssss", pouch ? POUCH_PARAMS : SCROLL_PARAMS); - player.getPacketDispatch().sendAccessMask(pouch ? 190 : 126, 15, pouch ? 669 : 673, 0, 78); + player.getPacketDispatch().sendIfaceSettings(pouch ? 190 : 126, 15, pouch ? 669 : 673, 0, 78); } /** @@ -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 new file mode 100644 index 000000000..b91634318 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/SummoningPouch.java @@ -0,0 +1,585 @@ +package content.global.skill.summoning; + +import core.game.node.item.Item; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a summoning pouch. + * @author Emperor + */ +public enum SummoningPouch { + + /** + * Represents a spirit wolf pouch. + */ + SPIRIT_WOLF_POUCH(0, 12047, 1, 4.8, 6829, 0.1, 1, false, new Item(12158), new Item(12155), new Item(2859), new Item(12183, 7)), + + /** + * Represents a dreadfowl pouch. + */ + DREADFOWL_POUCH(1, 12043, 4, 9.3, 6825, 0.1, 1, false, new Item(12158), new Item(12155), new Item(2138), new Item(12183, 8)), + + /** + * Represents a spirit spider pouch. + */ + SPIRIT_SPIDER_POUCH(2, 12059, 10, 12.6, 6841, 0.2, 2, false, new Item(12158), new Item(12155), new Item(6291), new Item(12183, 8)), + + /** + * Represents a thorny snail pouch. + */ + THORNY_SNAIL_POUCH(3, 12019, 13, 12.6, 6806, 0.2, 2, false, new Item(12158), new Item(12155), new Item(3363), new Item(12183, 9)), + + /** + * Represents a granite crab pouch. + */ + 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. + */ + SPIRIT_MOSQUITO_POUCH(5, 12778, 17, 46.5, 7331, 0.5, 2, false, new Item(12158), new Item(12155), new Item(6319), new Item(12183, 1)), + + /** + * Represents a desrrt wyrm pouch. + */ + DESERT_WYRM_POUCH(6, 12049, 18, 31.2, 6831, 0.4, 1, false, new Item(12159), new Item(12155), new Item(1783), new Item(12183, 45)), + + /** + * Represents a spirit scorpion pouch. + */ + SPIRIT_SCORPION_POUCH(7, 12055, 19, 83.2, 6837, 0.9, 2, false, new Item(12160), new Item(12155), new Item(3095), new Item(12183, 57)), + + /** + * Represents a spirit tz-kih pouch. + */ + SPIRIT_TZ_KIH_POUCH(8, 12808, 22, 96.8, 7361, 1.1, 3, false, new Item(12160), new Item(12168), new Item(12155), new Item(12183, 64)), + + /** + * Represents an albino rat pouch. + */ + ALBINO_RAT_POUCH(9, 12067, 23, 202.4, 6847, 2.3, 1, false, new Item(12163), new Item(12155), new Item(2134), new Item(12183, 75)), + + /** + * Represents a spirit kalphite pouch. + */ + SPIRIT_KALPHITE_POUCH(10, 12063, 25, 220, 6994, 2.5, 3,false, new Item(12163), new Item(12155), new Item(3138), new Item(12183, 51)), + + /** + * Represents a compost mound pouch. + */ + COMPOST_MOUND_POUCH(11, 12091, 28, 49.8, 6871, 0.6, 6, false, new Item(12159), new Item(12155), new Item(6032), new Item(12183, 47)), + + /** + * Represents a giant chinchompa pouch. + */ + GIANT_CHINCHOMPA_POUCH(12, 12800, 29, 255.2, 7353, 2.9, 1, false, new Item(12163), new Item(12155), new Item(10033), new Item(12183, 84)), + + /** + * Represents a vampire bat pouch. + */ + VAMPIRE_BAT_POUCH(13, 12053, 31, 136, 6835, 1.5, 4, false, new Item(12160), new Item(12155), new Item(3325), new Item(12183, 81)), + + /** + * Represents a honey badger pouch. + */ + HONEY_BADGER_POUCH(14, 12065, 32, 140.8, 6845, 1.6, 4, false, new Item(12160), new Item(12155), new Item(12156), new Item(12183, 84)), + + /** + * Represents a beaver pouch. + */ + BEAVER_POUCH(15, 12021, 33, 57.6, 6808, 0.7, 4, true, new Item(12159), new Item(12155), new Item(1519), new Item(12183, 72)), + + /** + * Represents a void ravager pouch. + */ + VOID_RAVAGER_POUCH(16, 12818, 34, 59.6, 7370, 0.7, 4, false, new Item(12159), new Item(12164), new Item(12155), new Item(12183, 74)), + + /** + * Represents a void spinner pouch. + */ + VOID_SPINNER_POUCH(17, 12780, 34, 59.6, 7333, 0.7, 4, true, new Item(12163), new Item(12166), new Item(12155), new Item(12183, 74)), + + /** + * Represents a void torcher pouch. + */ + VOID_TORCHER_POUCH(18, 12798, 34, 59.6, 7351, 0.7, 4, false, new Item(12163), new Item(12167), new Item(12155), new Item(12183, 74)), + + /** + * Represents a void shifter pouch. + */ + VOID_SHIFTER_POUCH(19, 12814, 34, 59.6, 7367, 0.7, 4, false, new Item(12163), new Item(12165), new Item(12155), new Item(12183, 74)), + + /** + * Represents a bronze minotaur pouch. + */ + BRONZE_MINOTAUR_POUCH(64, 12073, 36, 316.8, 6853, 3.6, 3, false, new Item(12163), new Item(12155), new Item(2349), new Item(12183, 102)), + + /** + * Represents an iron minotaur pouch. + */ + IRON_MINOTAUR_POUCH(65, 12075, 46, 404.8, 6855, 4.6, 9, false, new Item(12163), new Item(12155), new Item(2351), new Item(12183, 125)), + + /** + * Represents a steel minotaur pouch. + */ + STEEL_MINOTAUR_POUCH(66, 12077, 56, 492.8, 6857, 5.6, 9, false, new Item(12163), new Item(12155), new Item(2353), new Item(12183, 141)), + + /** + * Represents a mithril minotaur pouch. + */ + MITHRIL_MINOTAUR_POUCH(67, 12079, 66, 580.8, 6859, 6.6, 9,false, new Item(12163), new Item(12155), new Item(2359), new Item(12183, 152)), + + /** + * Represents an adamant minotaur pouch. + */ + ADAMANT_MINOTAUR_POUCH(68, 12081, 76, 668.8, 6861, 7.6, 9, false, new Item(12163), new Item(12155), new Item(2361), new Item(12183, 144)), + + /** + * Represents a rune minotaur pouch. + */ + RUNE_MINOTAUR_POUCH(69, 12083, 86, 756.8, 6863, 8.6, 9, false, new Item(12163), new Item(12155), new Item(2363), new Item(12183, 1)), + + /** + * Represents a bull ant pouch. + */ + BULL_ANT_POUCH(20, 12087, 40, 52.8, 6867, 0.6, 5, false, new Item(12158), new Item(12155), new Item(6010), new Item(12183, 11)), + + /** + * Represents a macaw pouch. + */ + MACAW_POUCH(21, 12071, 41, 72.4, 6851, 0.8, 5, true, new Item(12159), new Item(12155), new Item(249), new Item(12183, 78)), + + /** + * Represents an evil turnip pouch. + */ + EVIL_TURNIP_POUCH(22, 12051, 42, 184.8, 6833, 2.1, 5, false, new Item(12160), new Item(12155), new Item(12153), new Item(12183, 104)), + + /** + * Represents a spirit cockatrice pouch. + */ + SPIRIT_COCKATRICE_POUCH(23, 12095, 43, 75.2, 6875, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12109), new Item(12183, 88)), + + /** + * Represents a spirit guthatrice pouch. + */ + SPIRIT_GUTHATRICE_POUCH(24, 12097, 43, 75.2, 6877, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12111), new Item(12183, 88)), + + /** + * Represents a spirit saratrice pouch. + */ + SPIRIT_SARATRICE_POUCH(25, 12099, 43, 75.2, 6879, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12113), new Item(12183, 88)), + + /** + * Represents a spirit zamatrice pouch. + */ + SPIRIT_ZAMATRICE_POUCH(26, 12101, 43, 75.2, 6881, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12115), new Item(12183, 88)), + + /** + * Represents a spirit pengatrice pouch. + */ + SPIRIT_PENGATRICE_POUCH(27, 12103, 43, 75.2, 6883, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12117), new Item(12183, 88)), + + /** + * Represents a coraxatrice pouch. + */ + SPIRIT_CORAXATRICE_POUCH(28, 12105, 43, 75.2, 6885, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12119), new Item(12183, 88)), + + /** + * Represents a vulatrice pouch. + */ + SPIRIT_VULATRICE(29, 12107, 43, 75.2, 6887, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12121), new Item(12183, 88)), + + /** + * Represents a pyrelord pouch. + */ + PYRELORD_POUCH(30, 12816, 46, 202.4, 7377, 2.3, 5, false, new Item(12160), new Item(12155), new Item(590), new Item(12183, 111)), + + /** + * Represents a magpie pouch. + */ + MAGPIE_POUCH(31, 12041, 47, 83.2, 6824, 0.9, 5, true, new Item(12159), new Item(12155), new Item(1635), new Item(12183, 88)), + + /** + * Represents a bloated leech pouch. + */ + BLOATED_LEECH_POUCH(32, 12061, 49, 215.2, 6843, 2.4, 5, false, new Item(12160), new Item(12155), new Item(2132), new Item(12183, 117)), + + /** + * Represents a spirit terrorbird pouch. + */ + SPIRIT_TERRORBIRD_POUCH(33, 12007, 52, 68.4, 6794, 0.8, 6, true, new Item(12158), new Item(12155), new Item(9978), new Item(12183, 12)), + + /** + * Represents an abyssal parasite pouch. + */ + ABYSSAL_PARASITE_POUCH(34, true,12035, 54, 94.8, 6818, 1.1, 6, false, new Item(12159), new Item(12155), new Item(12161), new Item(12183, 106)), + + /** + * Represents a spirit jelly pouch. + */ + SPIRIT_JELLY_POUCH(35, 12027, 55, 484, 6992, 5.5, 6, false, new Item(12163), new Item(12155), new Item(1937), new Item(12183, 151)), + + /** + * Represents an ibis pouch. + */ + IBIS_POUCH(36, 12531, 56, 98.8, 6991, 1.1, 6, true, new Item(12159), new Item(12155), new Item(311), new Item(12183, 109)), + + /** + * Represents a spirit kyatt pouch. + */ + SPIRIT_KYATT_POUCH(37, 12812, 57, 501.6, 7365, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10103), new Item(12183, 153)), + + /** + * Represents a spirit larupia pouch. + */ + SPIRIT_LARUPIA_POUCH(38, 12784, 57, 501.6, 7337, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10095), new Item(12183, 155)), + + /** + * Represents a spirit graahk pouch. + */ + SPIRIT_GRAAHK_POUCH(39, 12810, 57, 501.6, 7363, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10099), new Item(12183, 154)), + + /** + * Represents a karamthulhu overlord pouch. + */ + KARAMTHULHU_POUCH(40, 12023, 58, 510.4, 6809, 5.8, 6, false, new Item(12163), new Item(12155), new Item(6667), new Item(12183, 144)), + + /** + * Represents a smoke devil pouch. + */ + SMOKE_DEVIL_POUCH(41, 12085, 61, 268, 6865, 3, 7, false, new Item(12160), new Item(12155), new Item(9736), new Item(12183, 141)), + + /** + * Represents an abyssal lurker pouch. + */ + ABYSSAL_LUKRER(42, true,12037, 62, 109.6, 6820, 1.9, 9, false, new Item(12159), new Item(12155), new Item(12161), new Item(12183, 119)), + + /** + * Represents a spirit cobra pouch. + */ + SPIRIT_COBRA_POUCH(43, 12015, 63, 276.8, 6802, 3.1, 6, false, new Item(12160), new Item(12155), new Item(6287), new Item(12183, 116)), + + /** + * Represents a stranger plant pouch. + */ + STRANGER_PLANT_POUCH(44, 12045, 64, 281.6, 6827, 3.2, 6, false, new Item(12160), new Item(12155), new Item(8431), new Item(12183, 128)), + + /** + * Represents a barker toad pouch. + */ + BARKER_TOAD_POUCH(45, 12123, 66, 87, 6889, 1, 7, false, new Item(12158), new Item(12155), new Item(2150), new Item(12183, 11)), + + /** + * Represents a war tortoise pouch. + */ + WAR_TORTOISE_POUCH(46, 12031, 67, 58.6, 6815, 0.7, 7, true, new Item(12158), new Item(12155), new Item(7939), new Item(12183, 1)), + + /** + * Represents a bunyip pouch. + */ + BUNYIP_POUCH(47, 12029, 68, 119.2, 6813, 1.4, 7, true, new Item(12159), new Item(12155), new Item(383), new Item(12183, 110)), + + /** + * Represents a fruit bat pouch. + */ + FRUIT_BAT_POUCH(48, 12033, 69, 121.2, 6817, 1.4, 8, true, new Item(12159), new Item(12155), new Item(1963), new Item(12183, 130)), + + /** + * Represents a ravenous locust pouch. + */ + RAVENOUS_LOCUST_POUCH(49, 12820, 70, 132, 7372, 1.5, 4, false, new Item(12160), new Item(12155), new Item(1933), new Item(12183, 79)), + + /** + * Represents an arctic bear pouch. + */ + ARCTIC_BEAR_POUCH(50, 12057, 71, 93.2, 6839, 1.1, 8, false, new Item(12158), new Item(12155), new Item(10117), new Item(12183, 14)), + + /** + * Represents a Phoenix + */ + PHOENIX_POUCH(50, 14623, 72, 93.2, 8575, 1.1, 8, false, new Item(12160, 1), new Item(12183, 165), new Item(12155, 1), new Item(14616, 1)), + + /** + * Represents an obsidian golem pouch. + */ + OBSIDIAN_GOLEM_POUCH(51, 12792, 73, 642.4, 7345, 7.3, 8, false, new Item(12163), new Item(12155), new Item(12168), new Item(12183, 195)), + + /** + * Represents a granite lobster pouch. + */ + GRANITE_LOBSTER_POUCH(52, 12069, 74, 325.6, 6849, 3.7, 8, false, new Item(12160), new Item(12155), new Item(6979), new Item(12183, 166)), + + /** + * Represents a praying mantis pouch. + */ + PRAYING_MANTIS_POUCH(53, 12011, 75, 329.6, 6798, 3.6, 8, false, new Item(12160), new Item(12155), new Item(2460), new Item(12183, 168)), + + /** + * Represents a forge regent pouch. + */ + FORGE_REGENT_BEAST(54, 12782, 76, 134, 7335, 1.5, 9, false, new Item(12159), new Item(12155), new Item(10020), new Item(12183, 141)), + + /** + * Represents a talon beast pouch. + */ + TALON_BEAST_POUCH(55, 12794, 77, 1015.2, 7347, 3.8, 9, false, new Item(12160), new Item(12155), new Item(12162), new Item(12183, 174)), + + /** + * Represents a giant ent pouch. + */ + GIANT_ENT_POUCH(56, 12013, 78, 136.8, 6800, 1.6, 8, false, new Item(12159), new Item(5933), new Item(12155), new Item(12183, 124)), + + /** + * Represents a hydra pouch. + */ + 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(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(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(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(12155), new Item(12183, 211)), + + /** + * Represents a fire titan pouch. + */ + FIRE_TITAN_POUCH(57, 12802, 79, 695.2, 7355, 7.9, 9, false, new Item(12163), new Item(1442), new Item(12155), new Item(12183, 198)), + + /** + * Represents a moss titan pouch. + */ + MOSS_TITAN_POUCH(58, 12804, 79, 695.2, 7357, 7.9, 9, false, new Item(12163), new Item(1440), new Item(12155), new Item(12183, 198)), + + /** + * Represents an ice titan pouch. + */ + ICE_TITAN_POUCH(59, 12806, 79, 695.2, 7359, 7.9, 9, false, new Item(12163), new Item(1438), new Item(1444), new Item(12155), new Item(12183, 198)), + + /** + * Represents a lava titan pouch. + */ + LAVA_TITAN_POUCH(62, 12788, 83, 730.4, 7341, 8.3, 9, false, new Item(12163), new Item(12168), new Item(12155), new Item(12183, 219)), + + /** + * Represents a swamp titan pouch. + */ + SWAMP_TITAN_POUCH(63, 12776, 85, 373.6, 7329, 4.2, 9, false, new Item(12160), new Item(10149), new Item(12155), new Item(12183, 150)), + + /** + * Represents a geyser titan pouch. + */ + GEYSER_TITAN_POUCH(71, 12786, 89, 783.2, 7339, 8.9, 9, false, new Item(12163), new Item(1444), new Item(12155), new Item(12183, 222)), + + /** + * Represents an abyssal titan pouch. + */ + ABYSSAL_TITAN_POUCH(73, true,12796, 93, 163.2, 7349, 1.9, 10, false, new Item(12159), new Item(12161), new Item(12155), new Item(12183, 113)), + + /** + * Represents an iron titan pouch. + */ + IRON_TITAN_POUCH(74, 12822, 95, 417.6, 7375, 4.7, 10, false, new Item(12160), new Item(1115), new Item(12155), new Item(12183, 198)), + + /** + * Represents a steel titan pouch. + */ + STEEL_TITAN_POUCH(76, 12790, 99, 435.2, 7343, 4.9, 10, false, new Item(12160), new Item(1119), new Item(12155), new Item(12183, 178)), + + SACRED_CLAY_POUCH_1(-1, 14422, 1, 0, 8240, 0, 1, false, new Item(14182)), + SACRED_CLAY_POUCH_2(-1, 14424, 20, 0, 8242, 0, 3, false, new Item(14184)), + SACRED_CLAY_POUCH_3(-1, 14426, 40, 0, 8244, 0, 5, false, new Item(14186)), + SACRED_CLAY_POUCH_4(-1, 14428, 60, 0, 8246, 0, 7, false, new Item(14188)), + SACRED_CLAY_POUCH_5(-1, 14430, 80, 0, 8248, 0, 9, false, new Item(14190)); + + /** + * The mapping. + */ + private static final Map POUCHES = new HashMap(); + + /** + * Populate the mapping. + */ + static { + for (SummoningPouch pouch : SummoningPouch.values()) { + POUCHES.put(pouch.pouchId, pouch); + } + } + + /** + * Gets a summoning pouch object from the mapping. + * @param pouchId The pouch item id. + * @return The {@code SummoningPouch} {@code Object},
or {@code null} + * if the pouch didn't exist. + */ + public static SummoningPouch get(int pouchId) { + return POUCHES.get(pouchId); + } + + /** + * The slot id. + */ + private final int slot; + + /** + * The pouch item id. + */ + private final int pouchId; + + /** + * The level required to create this pouch. + */ + private final int levelRequired; + + /** + * The experience gained when creating this pouch. + */ + private final double createExperience; + + /** + * The familiar npc id. + */ + private final int npcId; + + /** + * The experience gained when summoning the familiar. + */ + private final double summonExperience; + + /** + * The summon cost. + */ + private final int summonCost; + + /** + * Is the familiar peaceful + */ + private final boolean peaceful; + + /** + * The items required to create this pouch. + */ + private final Item[] items; + + public boolean abyssal; + + /** + * Constructs a new {@code SummoningPouch} {@code Object}. + * @param pouchId The pouch item id. + * @param levelRequired The level required to create. + * @param createExperience The experience gained when creating a pouch. + * @param npcId The familiar's NPC id. + * @param summonExperience The experience gained when summoning. + * @param summonCost The amount of summoning points to drain when summoned. + * @param peaceful Determines whether the familiar is peaceful and should attack. + * @param items The items required to create this pouch. + */ + private SummoningPouch(int slot, int pouchId, int levelRequired, double createExperience, int npcId, double summonExperience, int summonCost, boolean peaceful, Item... items) { + this.slot = slot; + this.pouchId = pouchId; + this.levelRequired = levelRequired; + this.createExperience = createExperience; + this.npcId = npcId; + this.summonExperience = summonExperience; + this.summonCost = summonCost; + this.peaceful = peaceful; + this.items = items; + } + + private SummoningPouch(int slot, boolean abyssal, int pouchId, int levelRequired, double createExperience, int npcId, double summonExperience, int summonCost, boolean peaceful, Item... items) { + this(slot, pouchId, levelRequired, createExperience, npcId, summonExperience, summonCost, peaceful, items); + this.abyssal = abyssal; + } + /** + * Gets the summoning pouch for the given slot. + * @param slot The slot. + * @return The summoning pouch. + */ + public static SummoningPouch forSlot(int slot) { + for (SummoningPouch pouch : SummoningPouch.values()) { + if (pouch.getSlot() == slot) { + return pouch; + } + } + return null; + } + + /** + * @return the pouchId + */ + public int getPouchId() { + return pouchId; + } + + /** + * @return the levelRequired + */ + public int getLevelRequired() { + return levelRequired; + } + + /** + * @return the createExperience + */ + public double getCreateExperience() { + return createExperience; + } + + /** + * @return the npcId + */ + public int getNpcId() { + return npcId; + } + + /** + * @return the summonExperience + */ + public double getSummonExperience() { + return summonExperience; + } + + /** + * Gets the amount of points to decrease the summoning points with when + * summoned. + * @return The amount of points. + */ + public int getSummonCost() { + return summonCost; + } + + /** + * @return Whether the familiar is peaceful + */ + public boolean getPeaceful() { return peaceful; } + + /** + * @return the items + */ + public Item[] getItems() { + return items; + } + + /** + * @return the slot + */ + public int getSlot() { + return slot; + } + +} diff --git a/Server/src/main/content/global/skill/summoning/SummoningScroll.java b/Server/src/main/content/global/skill/summoning/SummoningScroll.java new file mode 100644 index 000000000..cd02a4419 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/SummoningScroll.java @@ -0,0 +1,215 @@ +package content.global.skill.summoning; + +/** + * Represents a summoning scroll. + * @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, 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, 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), + CLAY_DEPOSIT_SCROLL_4(-1, 14421, 0, 60, 14428), + CLAY_DEPOSIT_SCROLL_5(-1, 14421, 0, 80, 14430); + + /** + * The level required. + */ + private int level; + + /** + * The item id needed. + */ + private int itemId; + + /** + * The slot id. + */ + private int slotId; + + /** + * The xp gained. + */ + private double xp; + + /** + * The items required. + */ + private int[] items; + + /** + * Constructs a new {@code SummoningScroll} {@code Object}. + * @param level + * @param itemId + * @param xp + * @param slotId + * @param items + */ + SummoningScroll(int slotId, int itemId, double xp, int level, int... items) { + this.level = level; + this.itemId = itemId; + this.xp = xp; + this.slotId = slotId; + this.items = items; + } + + public int getPouch(){ + return items[0]; + } + + /** + * Gets the level. + * @return The level. + */ + public int getLevel() { + return level; + } + + /** + * Gets the itemId. + * @return The itemId. + */ + public int getItemId() { + return itemId; + } + + /** + * Gets the slotId. + * @return The slotId. + */ + public int getSlotId() { + return slotId; + } + + /** + * Gets the xp. + * @return The xp. + */ + public double getExperience() { + return xp; + } + + /** + * Gets the items. + * @return The items. + */ + public int[] getItems() { + return items; + } + + /** + * Gets the summoning value for the id. + * @param id + * @return + */ + public static SummoningScroll forId(int id) { + for (SummoningScroll scroll : SummoningScroll.values()) { + if (scroll.slotId == id) { + return scroll; + } + } + return null; + } + + public static SummoningScroll forItemId(int id) { + for (SummoningScroll scroll : SummoningScroll.values()) { + if (scroll.itemId == id) { + return scroll; + } + } + return null; + } + + /** + * Gets the scroll for the given pouch item id. + * @param pouchId The pouch item id. + * @return The scroll. + */ + public static SummoningScroll forPouch(int pouchId) { + for (SummoningScroll scroll : SummoningScroll.values()) { + if (scroll.items[0] == pouchId) { + return scroll; + } + } + return null; + } + +} diff --git a/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt b/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt new file mode 100644 index 000000000..c47862b62 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt @@ -0,0 +1,67 @@ +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 { + override fun defineInterfaceListeners() { + on(662) { player, _, opcode, buttonID, _, _ -> + when(buttonID) { + 51 -> { + if (player.familiarManager.hasFamiliar()) { + player.familiarManager.familiar.call() + } else { + player.getPacketDispatch().sendMessage("You don't have a follower.") + } + } + 67 -> { + 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.familiarManager.familiar as BurdenBeast + if (beast.getContainer().isEmpty()) { + player.getPacketDispatch().sendMessage("Your familiar is not carrying any items.") + return@on true + } + beast.withdrawAll() + return@on true + } + player.getPacketDispatch().sendMessage("You don't have a follower.") + } + 53 -> { + if (player.familiarManager.hasFamiliar()) { + if(opcode == 155) { + // Dismiss familiar + player.getDialogueInterpreter().open("dismiss_dial") + } else if(opcode == 196) { + // Dismiss now + 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.") + } + } + else -> { + if (player.familiarManager.hasFamiliar()) { + player.familiarManager.familiar.executeSpecialMove(FamiliarSpecial(player)) + } else { + player.getPacketDispatch().sendMessage("You don't have a follower.") + } + } + } + return@on true + } + } +} + diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningTrainingRoom.java b/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/summoning/SummoningTrainingRoom.java rename to Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java index ec155c05c..6296daef4 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningTrainingRoom.java +++ b/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java @@ -1,13 +1,13 @@ -package core.game.node.entity.skill.summoning; +package content.global.skill.summoning; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.action.ClimbActionHandler; +import core.game.dialogue.DialoguePlugin; +import core.game.global.action.ClimbActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -18,7 +18,7 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; import core.game.world.map.path.Pathfinder; @@ -31,7 +31,10 @@ import core.net.packet.context.MinimapStateContext; import core.net.packet.out.CameraViewPacket; import core.net.packet.out.MinimapState; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Handles the summoning training room. @@ -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) { @@ -235,7 +238,7 @@ public final class SummoningTrainingRoom extends OptionHandler { @Override public void register() { - PluginManager.definePlugin(new FluffyDialogue()); + ClassScanner.definePlugin(new FluffyDialogue()); } /** @@ -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/java/core/game/node/entity/npc/familiar/AbyssalLurkerNPC.java b/Server/src/main/content/global/skill/summoning/familiar/AbyssalLurkerNPC.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/npc/familiar/AbyssalLurkerNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/AbyssalLurkerNPC.java index c93e9dafa..9fb1effdc 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/AbyssalLurkerNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/AbyssalLurkerNPC.java @@ -1,9 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/AbyssalParasiteNPC.java b/Server/src/main/content/global/skill/summoning/familiar/AbyssalParasiteNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/familiar/AbyssalParasiteNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/AbyssalParasiteNPC.java index 45e54497a..795484722 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/AbyssalParasiteNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/AbyssalParasiteNPC.java @@ -1,9 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; 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/java/core/game/node/entity/npc/familiar/AlbinoRatNPC.java b/Server/src/main/content/global/skill/summoning/familiar/AlbinoRatNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/familiar/AlbinoRatNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/AlbinoRatNPC.java index 56ab6ef0c..28842ad58 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/AlbinoRatNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/AlbinoRatNPC.java @@ -1,8 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/ArcticBearNPC.java b/Server/src/main/content/global/skill/summoning/familiar/ArcticBearNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/familiar/ArcticBearNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/ArcticBearNPC.java index 92914caaf..8639adb71 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/ArcticBearNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/ArcticBearNPC.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; +import core.game.node.entity.skill.SkillBonus; +import core.game.node.entity.skill.Skills; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.impl.Projectile; @@ -31,6 +31,7 @@ public class ArcticBearNPC extends Familiar { */ public ArcticBearNPC(Player owner, int id) { super(owner, id, 2800, 12057, 6, WeaponInterface.STYLE_CONTROLLED); + boosts.add(new SkillBonus(Skills.HUNTER, 7)); } @Override diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/BarkerToadNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BarkerToadNPC.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/npc/familiar/BarkerToadNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/BarkerToadNPC.java index 37b01f64c..97d7512c7 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/BarkerToadNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BarkerToadNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/dialogue/BeaverDialogue.java b/Server/src/main/content/global/skill/summoning/familiar/BeaverDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/BeaverDialogue.java rename to Server/src/main/content/global/skill/summoning/familiar/BeaverDialogue.java index e00dfc218..dff82b323 100644 --- a/Server/src/main/java/core/game/content/dialogue/BeaverDialogue.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BeaverDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.global.skill.summoning.familiar; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/BeaverNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BeaverNPC.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/npc/familiar/BeaverNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/BeaverNPC.java index aadf2cdbc..05c810f0c 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/BeaverNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BeaverNPC.java @@ -1,15 +1,12 @@ -package core.game.node.entity.npc.familiar; +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.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.path.Pathfinder; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java new file mode 100644 index 000000000..8dfb2ef0e --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java @@ -0,0 +1,62 @@ +package content.global.skill.summoning.familiar; + +import core.plugin.Initializable; +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.tools.RandomFunction; + +import static core.api.ContentAPIKt.*; + +/** + * Represents the Bloated Leech familiar. + * @author Aero + */ +@Initializable +public class BloatedLeechNPC extends Familiar { + + /** + * Constructs a new {@code BloatedLeechNPC} {@code Object}. + */ + public BloatedLeechNPC() { + this(null, 6843); + } + + /** + * Constructs a new {@code BloatedLeechNPC} {@code Object}. + * @param owner The owner. + * @param id The id. + */ + public BloatedLeechNPC(Player owner, int id) { + super(owner, id, 3400, 12061, 6, WeaponInterface.STYLE_ACCURATE); + } + + @Override + public Familiar construct(Player owner, int id) { + return new BloatedLeechNPC(owner, id); + } + + @Override + protected boolean specialMove(FamiliarSpecial special) { + 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().updateLevel( + i, + (int) Math.ceil(owner.getSkills().getStaticLevel(i) * 0.2), + owner.getSkills().getStaticLevel(i) + ); + } + } + owner.getImpactHandler().manualHit(owner, RandomFunction.random(1, 5), HitsplatType.NORMAL); + return true; + } + + @Override + public int[] getIds() { + return new int[] { 6843, 6844 }; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/BullAntNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BullAntNPC.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/npc/familiar/BullAntNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/BullAntNPC.java index f2e081a96..46e4be3dd 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/BullAntNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BullAntNPC.java @@ -1,9 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/BunyipNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BunyipNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/familiar/BunyipNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/BunyipNPC.java index 34f2c8f28..df1c54e37 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/BunyipNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BunyipNPC.java @@ -1,20 +1,18 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.combat.ImpactHandler; -import core.game.content.consumable.Consumable; -import core.game.content.consumable.Consumables; +import core.game.consumable.Consumable; +import content.data.consumables.Consumables; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.cooking.CookableItems; -import core.game.node.entity.skill.fishing.Fish; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; +import content.global.skill.cooking.CookableItems; +import content.global.skill.fishing.Fish; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; 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.item.Item; -import rs09.game.world.GameWorld; +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; @@ -96,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; @@ -140,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/java/core/game/node/entity/skill/summoning/familiar/BurdenBeast.java b/Server/src/main/content/global/skill/summoning/familiar/BurdenBeast.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/BurdenBeast.java rename to Server/src/main/content/global/skill/summoning/familiar/BurdenBeast.java index 34e3c8128..d6a3100d0 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/BurdenBeast.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BurdenBeast.java @@ -1,11 +1,11 @@ -package core.game.node.entity.skill.summoning.familiar; +package content.global.skill.summoning.familiar; import core.game.component.CloseEvent; import core.game.component.Component; import core.game.container.Container; import core.game.container.access.InterfaceContainer; -import rs09.game.system.config.ItemConfigParser; -import core.game.node.entity.skill.summoning.SummoningPouch; +import core.game.system.config.ItemConfigParser; +import content.global.skill.summoning.SummoningPouch; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.item.GroundItem; @@ -55,13 +55,13 @@ public abstract class BurdenBeast extends Familiar { if (owner.getInterfaceManager().hasMainComponent(671)) { owner.getInterfaceManager().close(); } - if (!owner.getIronmanManager().isIronman()) { - for (Item item : container.toArray()) { - if (item != null) { - GroundItemManager.create(new GroundItem(item, location, 500, owner)); - } - } - } + + for (Item item : container.toArray()) { + if (item != null) { + GroundItemManager.create(new GroundItem(item, location, 500, owner)); + } + } + container.clear(); super.dismiss(); } @@ -209,4 +209,4 @@ public abstract class BurdenBeast extends Familiar { return container; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/BurdenContainerListener.java b/Server/src/main/content/global/skill/summoning/familiar/BurdenContainerListener.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/BurdenContainerListener.java rename to Server/src/main/content/global/skill/summoning/familiar/BurdenContainerListener.java index 860030941..4856fd75a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/BurdenContainerListener.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BurdenContainerListener.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.summoning.familiar; +package content.global.skill.summoning.familiar; import core.game.container.Container; import core.game.container.ContainerEvent; diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/BurdenInterfacePlugin.java b/Server/src/main/content/global/skill/summoning/familiar/BurdenInterfacePlugin.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/BurdenInterfacePlugin.java rename to Server/src/main/content/global/skill/summoning/familiar/BurdenInterfacePlugin.java index 1863d441a..f9a8fd1a2 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/BurdenInterfacePlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BurdenInterfacePlugin.java @@ -1,12 +1,11 @@ -package core.game.node.entity.skill.summoning.familiar; +package content.global.skill.summoning.familiar; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.game.container.Container; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.Plugin; @@ -56,7 +55,7 @@ public final class BurdenInterfacePlugin extends ComponentPlugin { beast.transfer(item, container.getAmount(item), withdraw); return true; case 234: - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { beast.transfer(item, (int) value, withdraw); return Unit.INSTANCE; }); diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/CockatriceFamiliarNPC.java b/Server/src/main/content/global/skill/summoning/familiar/CockatriceFamiliarNPC.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/npc/familiar/CockatriceFamiliarNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/CockatriceFamiliarNPC.java index 079d1d662..02564d7e1 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/CockatriceFamiliarNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/CockatriceFamiliarNPC.java @@ -1,21 +1,18 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; import core.game.node.entity.Entity; 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.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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 core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles the loading of a cockatrice familiar. @@ -31,12 +28,13 @@ public final class CockatriceFamiliarNPC implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new SpiritCockatrice()); - PluginManager.definePlugin(new SpiritGuthatrice()); - PluginManager.definePlugin(new SpiritZamatrice()); - PluginManager.definePlugin(new SpiritPengatrice()); - PluginManager.definePlugin(new SpiritCoraxatrice()); - PluginManager.definePlugin(new SpiritVulatrice()); + ClassScanner.definePlugin(new SpiritCockatrice()); + ClassScanner.definePlugin(new SpiritGuthatrice()); + ClassScanner.definePlugin(new SpiritZamatrice()); + ClassScanner.definePlugin(new SpiritPengatrice()); + ClassScanner.definePlugin(new SpiritCoraxatrice()); + ClassScanner.definePlugin(new SpiritVulatrice()); + ClassScanner.definePlugin(new SpiritSaratrice()); return this; } @@ -61,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; @@ -300,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/java/core/game/node/entity/npc/familiar/CompostMoundNPC.java b/Server/src/main/content/global/skill/summoning/familiar/CompostMoundNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/familiar/CompostMoundNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/CompostMoundNPC.java index 4de682105..77aae4d40 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/CompostMoundNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/CompostMoundNPC.java @@ -1,16 +1,15 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +import content.global.skill.farming.CompostBin; +import content.global.skill.farming.CompostBins; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; 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.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.world.update.flag.context.Animation; @@ -19,9 +18,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; import org.rs09.consts.Items; -import rs09.game.node.entity.skill.farming.CompostBin; -import rs09.game.node.entity.skill.farming.CompostBins; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the Compost Mound familiar. @@ -59,9 +56,9 @@ public class CompostMoundNPC extends Forager { @Override public void configureFamiliar() { - PluginManager.definePlugin(new CompostBucketPlugin()); + ClassScanner.definePlugin(new CompostBucketPlugin()); if (!DialogueInterpreter.contains(getIds()[1])) { - PluginManager.definePlugin(new CompostMoundDialogue()); + ClassScanner.definePlugin(new CompostMoundDialogue()); } } diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/DesertWyrmNPC.java b/Server/src/main/content/global/skill/summoning/familiar/DesertWyrmNPC.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/npc/familiar/DesertWyrmNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/DesertWyrmNPC.java index 84dd26460..0510469cd 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/DesertWyrmNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/DesertWyrmNPC.java @@ -1,16 +1,13 @@ -package core.game.node.entity.npc.familiar; +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 core.game.node.entity.skill.gather.SkillingResource; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; @@ -22,14 +19,14 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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.game.world.update.flag.context.Graphics; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the Desert Wyrm familiar. @@ -76,7 +73,7 @@ public final class DesertWyrmNPC extends Forager { @Override public void configureFamiliar() { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { for (int i : getIds()) { @@ -98,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; @@ -146,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/java/core/game/content/dialogue/DismissDialoguePlugin.java b/Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java similarity index 80% rename from Server/src/main/java/core/game/content/dialogue/DismissDialoguePlugin.java rename to Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java index a44cc3d89..b369bf4d6 100644 --- a/Server/src/main/java/core/game/content/dialogue/DismissDialoguePlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java @@ -1,7 +1,9 @@ -package core.game.content.dialogue; +package content.global.skill.summoning.familiar; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.pet.Pet; +import content.global.skill.summoning.pet.Pet; import core.game.node.entity.player.Player; /** @@ -52,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/java/core/game/node/entity/npc/familiar/DreadfowlNPC.java b/Server/src/main/content/global/skill/summoning/familiar/DreadfowlNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/familiar/DreadfowlNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/DreadfowlNPC.java index 783dcd95f..857048cdd 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/DreadfowlNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/DreadfowlNPC.java @@ -1,22 +1,20 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.InteractionType; import core.game.node.entity.combat.equipment.WeaponInterface; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; 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.system.task.Pulse; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/ElementalTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/ElementalTitanNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/npc/familiar/ElementalTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/ElementalTitanNPC.java index 7d1b9558d..bfebc3988 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/ElementalTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/ElementalTitanNPC.java @@ -1,9 +1,9 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; +import content.global.skill.summoning.familiar.Familiar; +import content.global.skill.summoning.familiar.FamiliarSpecial; public abstract class ElementalTitanNPC extends Familiar { diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/EvilTurnipNPC.java b/Server/src/main/content/global/skill/summoning/familiar/EvilTurnipNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/familiar/EvilTurnipNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/EvilTurnipNPC.java index ae8a234eb..0d6de5185 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/EvilTurnipNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/EvilTurnipNPC.java @@ -1,9 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.impl.Projectile; diff --git a/Server/src/main/content/global/skill/summoning/familiar/Familiar.java b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java new file mode 100644 index 000000000..089db9352 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java @@ -0,0 +1,919 @@ +package content.global.skill.summoning.familiar; + +import content.global.skill.summoning.SummoningScroll; +import content.global.skill.summoning.pet.Pet; +import core.cache.def.impl.NPCDefinition; +import core.game.interaction.MovementPulse; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.equipment.WeaponInterface; +import core.game.node.entity.impl.Projectile; +import core.game.node.entity.impl.PulseType; +import core.game.node.entity.npc.NPC; +import core.game.node.entity.player.Player; +import core.game.node.entity.skill.SkillBonus; +import core.game.node.entity.skill.Skills; +import core.game.node.item.Item; +import core.game.system.task.Pulse; +import core.game.world.map.Location; +import core.game.world.map.RegionManager; +import core.game.world.map.path.Pathfinder; +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.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 + */ +public abstract class Familiar extends NPC implements Plugin { + + /** + * The summon graphics for a small familiar. + */ + protected static final Graphics SMALL_SUMMON_GRAPHIC = Graphics.create(1314); + + /** + * The spawn graphics for a large familiar. + */ + protected static final Graphics LARGE_SUMMON_GRAPHIC = Graphics.create(1315); + + /** + * The special animation. + */ + protected static final Animation SPECIAL_ANIMATION = Animation.create(7660); + + /** + * The special graphic. + */ + protected static final Graphics SPECIAL_GRAPHIC = Graphics.create(1316); + + /** + * The owner. + */ + protected Player owner; + + /** + * The amount of ticks left. + */ + protected int ticks; + + /** + * The initial amount of ticks. + */ + protected int maximumTicks; + + /** + * The amount of special points left. + */ + protected int specialPoints = 60; + + public static final String INFINITE_SPECIAL_MOVE_ATTRIBUTE = "infinite-special-move"; + + /** + * The pouch id. + */ + private final int pouchId; + + /** + * The special move cost. + */ + private final int specialCost; + + private final SummoningPouch pouch; + + /** + * The combat reward. + */ + private CombatSwingHandler combatHandler; + + /** + * If the familiar is a combat familiar. + */ + protected boolean combatFamiliar; + + /** + * If the familiars special is charged. + */ + protected boolean charged; + + /** + * The invisible familiar boosts. + */ + protected List boosts = new ArrayList<>(20); + + /** + * The attack style. + */ + private final int attackStyle; + + /** + * 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}. + * @param owner The owner. + * @param id The NPC id. + * @param ticks The ticks left. + * @param pouchId The pouch. + * @param specialCost The special move cost. + * @param attackStyle the style. + */ + public Familiar(Player owner, int id, int ticks, int pouchId, int specialCost, final int attackStyle) { + super(id, null); + this.owner = owner; + this.maximumTicks = ticks; + this.ticks = ticks; + this.pouchId = pouchId; + this.pouch = SummoningPouch.get(pouchId); + 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; + } + } + + /** + * Constructs a new {@code Familiar} {@code Object}. + * @param owner The owner. + * @param id The NPC id. + * @param ticks The ticks left. + * @param pouchId The pouch. + * @param specialCost The special move cost. + */ + public Familiar(Player owner, int id, int ticks, int pouchId, int specialCost) { + this(owner, id, ticks, pouchId, specialCost, WeaponInterface.STYLE_DEFENSIVE); + } + + /** + * Creates the familiar. + * @param loc The location. + */ + public void init(Location loc, boolean call) { + location = loc; + if (location == null) { + location = owner.getLocation(); + setInvisible(true); + } + super.init(); + startFollowing(); + sendConfiguration(); + if (call) { + call(); + } + owner.getInterfaceManager().openInfoBars(); + if (getZoneMonitor().isInZone("Wilderness")) { + transform(); + } + } + + @Override + public void init() { + init(getSpawnLocation(), true); + } + + @Override + public void handleTickActions() { + 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()); + } + } + sendTimeRemaining(); + switch (ticks) { + case 100: + owner.getPacketDispatch().sendMessage("You have 1 minute before your familiar vanishes."); + break; + case 50: + owner.getPacketDispatch().sendMessage("You have 30 seconds before your familiar vanishes."); + break; + case 0: + if (isBurdenBeast() && !((BurdenBeast) this).getContainer().isEmpty()) { + owner.getPacketDispatch().sendMessage("Your familiar has dropped all the items it was holding."); + } else { + owner.getPacketDispatch().sendMessage("Your familiar has vanished."); + } + dismiss(); + return; + } + CombatPulse combat = owner.getProperties().getCombatPulse(); + if (!isInvisible() && !getProperties().getCombatPulse().isAttacking() && (combat.isAttacking() || owner.inCombat())) { + Entity victim = combat.getVictim(); + if (victim == null) { + victim = owner.getAttribute("combat-attacker"); + } + 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); + } + } + if ((!isInvisible() && owner.getLocation().getDistance(getLocation()) > 12) || (isInvisible() && ticks % 25 == 0)) { + if (!call()) { + setInvisible(true); + } + } else if (!getPulseManager().hasPulseRunning()) { + startFollowing(); + } + handleFamiliarTick(); + } + + @Override + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { + 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; + } + 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 (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) { + 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 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 + public void onRegionInactivity() { + call(); + } + + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + if (combatHandler != null) { + return combatHandler; + } + return super.getSwingHandler(swing); + } + + /** + * Constructs a new {@code Familiar} {@code Object}. + * @param owner The owner. + * @param id The NPC id. + * @return The familiar. + */ + public abstract Familiar construct(Player owner, int id); + + /** + * Executes the special move. + * @param special The familiar special object. + * @return {@code True} if the move was executed. + */ + protected abstract boolean specialMove(FamiliarSpecial special); + + /** + * Handles the familiar special tick. + */ + protected void handleFamiliarTick() { + } + + /** + * Configures use with events, and other plugin related content.. + */ + protected void configureFamiliar() { + + } + + /** + * Gets the forced chat text for this familiar. + * @return The forced chat text. + */ + protected String getText() { + return ""; + } + + /** + * Transforms the familiar into the Wilderness combat form. + */ + public void transform() { + if (isCombatFamiliar()) { + transform(getOriginalId() + 1); + } + } + + public void refreshTimer() { + ticks = maximumTicks; + } + + /** + * Sends the time remaining. + */ + private void sendTimeRemaining() { + int minutes = ticks / 100; + int centiminutes = ticks % 100; + 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 boolean executeSpecialMove(FamiliarSpecial special) { + if (special.getNode() == this) { + return false; + } + if (specialCost > specialPoints) { + owner.getPacketDispatch().sendMessage("Your familiar does not have enough special move points left."); + return false; + } + SummoningScroll scroll = SummoningScroll.forPouch(pouchId); + if (scroll == null) { + owner.getPacketDispatch().sendMessage("Invalid scroll for pouch " + pouchId + " - report!"); + 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 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 false; + } + if (specialMove(special)) { + setAttribute("special-delay", GameWorld.getTicks() + 3); + owner.getInventory().remove(new Item(scroll.getItemId())); + playAudio(owner, Sounds.SPELL_4161); + visualizeSpecialMove(); + if (!owner.getAttribute(INFINITE_SPECIAL_MOVE_ATTRIBUTE, false)) { + updateSpecialPoints(specialCost); + } + owner.getSkills().addExperience(Skills.SUMMONING, scroll.getExperience(), true); + } + return true; + } + + /** + * Sends the special move visualization for the owner. + */ + 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. + */ + public void sendFamiliarHit(final Entity target, final int maxHit, final Graphics graphics) { + final int ticks = 2 + (int) Math.floor(getLocation().getDistance(target.getLocation()) * 0.5); + getProperties().getCombatPulse().setNextAttack(4); + GameWorld.getPulser().submit(new Pulse(ticks, this, target) { + @Override + public boolean pulse() { + BattleState state = new BattleState(Familiar.this, target); + int hit = 0; + if (getCombatStyle().getSwingHandler().isAccurateImpact(Familiar.this, target)) { + 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); + } + return true; + } + }); + } + + /** + * Sends a projectile to the target. + * @param target the target. + * @param projectileId the projectile id. + */ + public void projectile(final Entity target, final int projectileId) { + Projectile.magic(this, target, projectileId, 40, 36, 51, 10).send(); + } + + /** + * Sends a familiar hit. + * @param maxHit the max hit. + */ + 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). + */ + public boolean canAttack(Entity target, boolean message) { + if (!target.isAttackable(owner, owner.getProperties().getCombatPulse().getStyle(), true)) { + return false; + } + if (target.getLocation().getDistance(getLocation()) > 8) { + if (message) { + sendMessage(owner, "That target is too far."); + } + return false; + } + 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(); + } + + @Override + 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) { + if (!canAttack(target, message)) { + return false; + } + if (!isOwnerAttackable()) { + return false; + } + 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; + } + + /** + * Checks if a faimiliar can perform a combat special attack. + * @param target the target. + * @return {@code True} if so. + */ + public boolean canCombatSpecial(Entity target) { + return canCombatSpecial(target, true); + } + + /** + * Checks if the owner is attackable. + * @return {@code True} if so. + */ + public boolean isOwnerAttackable() { + if (!owner.getProperties().getCombatPulse().isAttacking() && !owner.inCombat() && !getProperties().getCombatPulse().isAttacking()) { + owner.getPacketDispatch().sendMessage("Your familiar cannot fight whilst you are not in combat."); + return false; + } + return true; + } + + /** + * Gets the combat style. + * @return the style. + */ + public CombatStyle getCombatStyle() { + return CombatStyle.MAGIC; + } + + /** + * Adjusts a players battle state. + * @param state the state. + */ + public void adjustPlayerBattle(final BattleState state) { + + } + + /** + * Starts following the owner. + */ + public void startFollowing() { + getPulseManager().run(new MovementPulse(this, owner, Pathfinder.DUMB) { + @Override + public boolean pulse() { + return false; + } + }, PulseType.STANDARD); + face(owner); + } + + @Override + public void finalizeDeath(Entity killer) { + dismiss(); + } + + /** + * Checks if the familiar is a combat familiar. + * @return {@code True} if so. + */ + public boolean isCombatFamiliar() { + return combatFamiliar; + } + + /** + * Sends the familiar packets. + */ + public void sendConfiguration() { + setVarp(owner, 448, getPouchId()); + setVarp(owner, 1174, getOriginalId()); + setVarp(owner, 1175, specialCost << 23); + sendTimeRemaining(); + updateSpecialPoints(0); + } + + /** + * Calls the familiar. + */ + //int spamTimer = 0; + public boolean call() { + Location destination = getSpawnLocation(); + if (destination == null) { + //owner.getPacketDispatch().sendMessage("Your familiar is too big to fit here. Try calling it again when you are standing"); + //owner.getPacketDispatch().sendMessage("somewhere with more space."); + //spamTimer = 50; + return false; + } + setInvisible(owner.getZoneMonitor().isRestricted(ZoneRestriction.FOLLOWERS) && !owner.getLocks().isLocked("enable_summoning")); + if (isInvisible()) return true; + getProperties().setTeleportLocation(destination); + if (!(this instanceof Pet)) { + 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 { + graphics(SMALL_SUMMON_GRAPHIC); + } + } + if (getProperties().getCombatPulse().isAttacking()) { + startFollowing(); + } else { + face(owner); + } + if (!isRenderable() && owner.isActive()) { + // log(this.getClass(), Log.ERR, "Familiar in inactive region!"); + getWalkingQueue().update(); + getUpdateMasks().prepare(this); + } + return true; + } + + /** + * Gets the spawning location of the familiar. + * @return The spawn location. + */ + public Location getSpawnLocation() { + return RegionManager.getSpawnLocation(owner, this); + } + + /** + * Dismisses the familiar. + */ + public void dismiss() { + clear(); + getPulseManager().clear(); + owner.getInterfaceManager().removeTabs(7); + owner.getFamiliarManager().setFamiliar(null); + setVarp(owner, 448, -1); + setVarp(owner, 1176, 0); + setVarp(owner, 1175, 182986); + setVarp(owner, 1174, -1); + owner.getAppearance().sync(); + owner.getInterfaceManager().setViewedTab(3); + } + + /** + * Updates the special move points. + * @param diff The difference to decrease with. + */ + public void updateSpecialPoints(int diff) { + specialPoints -= diff; + if (specialPoints > 60) { + specialPoints = 60; + } + setVarp(owner, 1177, specialPoints); + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + for (int id : getIds()) { + if (FamiliarManager.getFamiliars().containsKey(id)) { + log(this.getClass(), Log.ERR, "Familiar " + id + " was already registered!"); + return null; + } + FamiliarManager.getFamiliars().put(id, this); + configureFamiliar(); + } + return this; + } + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + /** + * Gets the charged. + * @return The charged. + */ + public boolean isCharged() { + if (charged) { + owner.getPacketDispatch().sendMessage("Your familiar is already charging its attack!"); + return true; + } + return false; + } + + /** + * Gets a familiar boost. + * @param skill the skill. + * @return the boost. + */ + public int getBoost(int skill) { + SkillBonus bonus = null; + for (SkillBonus b : boosts) { + if (b.getSkillId() == skill) { + bonus = b; + break; + } + } + if (bonus == null) { + return 0; + } + return (int) bonus.getBonus(); + } + + /** + * Charges a familiar. + */ + public void charge() { + setCharged(true); + } + + /** + * Sets the charged. + * @param charged The charged to set. + */ + public void setCharged(boolean charged) { + this.charged = charged; + } + + /** + * Checks if the familiar is a beast of burden. + * @return {@code True} if so. + */ + public boolean isBurdenBeast() { + return false; + } + + public boolean isPeacefulFamiliar() { + return pouch.getPeaceful(); + } + + /** + * Gets the NPC ids. + * @return The npc ids. + */ + public abstract int[] getIds(); + + /** + * Gets the pouch id. + * @return The pouch id. + */ + public int getPouchId() { + return pouchId; + } + + /** + * Gets the owner. + * @return The owner. + */ + public Player getOwner() { + return owner; + } + + /** + * Sets the owner. + * @param owner The owner to set. + */ + public void setOwner(Player owner) { + this.owner = owner; + } + + /** + * Gets the combatHandler. + * @return The combatHandler. + */ + public CombatSwingHandler getCombatHandler() { + return combatHandler; + } + + /** + * Sets the combatHandler. + * @param combatHandler The combatHandler to set. + */ + public void setCombatHandler(CombatSwingHandler combatHandler) { + this.combatHandler = combatHandler; + } + + /** + * Gets the view animation for remote viewing. + * @return the animation. + */ + public Animation getViewAnimation() { + return null; + } + + /** + * Gets the exp style. + * @return the style. + */ + public int getAttackStyle() { + return attackStyle; + } + + public int getTicks() { + return ticks; + } + + public int getSpecialPoints() { + return specialPoints; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/FamiliarDialoguePlugin.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarDialoguePlugin.java similarity index 98% rename from Server/src/main/java/core/game/content/dialogue/FamiliarDialoguePlugin.java rename to Server/src/main/content/global/skill/summoning/familiar/FamiliarDialoguePlugin.java index 7ac2bc312..563165e8d 100644 --- a/Server/src/main/java/core/game/content/dialogue/FamiliarDialoguePlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarDialoguePlugin.java @@ -1,7 +1,7 @@ -package core.game.content.dialogue; +package content.global.skill.summoning.familiar; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.tools.RandomFunction; diff --git a/Server/src/main/content/global/skill/summoning/familiar/FamiliarFeedPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarFeedPlugin.java new file mode 100644 index 000000000..74f73e439 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarFeedPlugin.java @@ -0,0 +1,43 @@ +package content.global.skill.summoning.familiar; + +import content.global.skill.summoning.pet.Pet; +import core.game.interaction.NodeUsageEvent; +import core.game.interaction.UseWithHandler; +import core.plugin.Initializable; +import core.plugin.Plugin; + +/** + * Represents the class used to handle the feeding of pets. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +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, 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 + public Plugin newInstance(Object arg) throws Throwable { + int[] ids = 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, 8214, 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, 11413, 6948, 7294, 7296, 7298, 7300, 6912, 7262, 7264, 7266, 7268, 7270, 6920, 7302, 7304, 7306, 7308, 6950, 6953, 6956, 6914, 7272, 7274, 13090, 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, 8216, 6951, 6954, 6957 }; + for (int i : ids) { + addHandler(i, NPC_TYPE, this); + } + return this; + } + + @Override + public boolean handle(NodeUsageEvent event) { + final Familiar f = (Familiar) event.getUsedWith(); + if (!(f instanceof Pet)) { + return false; + } + event.getPlayer().getFamiliarManager().eat(event.getUsedItem().getId(), (Pet) f); + return true; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarItemOptionPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarItemOptionPlugin.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarItemOptionPlugin.java rename to Server/src/main/content/global/skill/summoning/familiar/FamiliarItemOptionPlugin.java index f5f45dd27..7f9612c76 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarItemOptionPlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarItemOptionPlugin.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.summoning.familiar; +package content.global.skill.summoning.familiar; +import content.global.skill.summoning.pet.Pets; import core.cache.def.impl.ItemDefinition; -import core.game.node.entity.skill.summoning.pet.Pets; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java new file mode 100644 index 000000000..e65c36cf1 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java @@ -0,0 +1,541 @@ +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; +import core.game.component.Component; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import core.game.node.entity.skill.Skills; +import content.global.skill.summoning.SummoningPouch; +import content.global.skill.summoning.pet.PetDetails; +import core.game.node.entity.combat.BattleState; +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.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.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. + */ + private final Map> petDetails = new HashMap<>(); + + /** + * The player. + */ + private final Player player; + + /** + * The familiar. + */ + private Familiar familiar; + + /** + * The combat level difference when using summoning. + */ + private int summoningCombatLevel; + + /** + * If the player has a summoning pouch. + */ + private boolean hasPouch; + + /** + * Constructs a new {@code FamiliarManager} {@code Object}. + * @param player The player. + */ + public FamiliarManager(Player player) { + this.player = player; + } + + public void parse(JSONObject familiarData) { + int currentPet = -1; + if (familiarData.containsKey("currentPet")) { + currentPet = Integer.parseInt(familiarData.get("currentPet").toString()); + } + 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()); + } + } + } + + 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) { + ((BurdenBeast) familiar).container.parse(famInv); + } + familiar.setAttribute("hp",Integer.parseInt( currentFamiliar.get("lifepoints").toString())); + } + } + + /** + * Called when the player logs in. + */ + public void login() { + if (hasFamiliar()) { + familiar.init(); + } + 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); + } + + /** + * Summons a familiar. + * @param item The item. + * @param pet If the familiar is a pet. + * @param deleteItem we should delete the item. + */ + public void summon(Item item, boolean pet, boolean deleteItem) { + boolean renew = false; + if (hasFamiliar()) { + 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."); + return; + } + if (pet) { + summonPet(item, deleteItem); + return; + } + final SummoningPouch pouch = SummoningPouch.get(item.getId()); + if (pouch == null) { + return; + } + if (player.getSkills().getStaticLevel(Skills.SUMMONING) < pouch.getLevelRequired()) { + player.getPacketDispatch().sendMessage("You need a Summoning level of " + pouch.getLevelRequired() + " to summon this familiar."); + return; + } + if (player.getSkills().getLevel(Skills.SUMMONING) < pouch.getSummonCost()) { + player.getPacketDispatch().sendMessage("You need at least " + pouch.getSummonCost() + " Summoning points to summon this familiar."); + return; + } + final int npcId = pouch.getNpcId(); + Familiar fam = !renew ? FAMILIARS.get(npcId) : familiar; + if (fam == null) { + player.getPacketDispatch().sendMessage("Invalid familiar " + npcId + " - report on 2009Scape GitLab"); + return; + } + if (!renew) { + fam = fam.construct(player, npcId); + if (fam.getSpawnLocation() == null) { + player.getPacketDispatch().sendMessage("The spirit in this pouch is too big to summon here. You will need to move to a larger"); + player.getPacketDispatch().sendMessage("area."); + return; + } + } + if (!player.getInventory().remove(item)) { + return; + } + player.getSkills().updateLevel(Skills.SUMMONING, -pouch.getSummonCost(), 0); + player.getSkills().addExperience(Skills.SUMMONING, pouch.getSummonExperience()); + if (!renew) { + familiar = fam; + spawnFamiliar(); + } else { + familiar.refreshTimer(); + } + player.getAppearance().sync(); + } + + /** + * Summons a familiar. + * @param item the item. + * @param pet the pet. + */ + public void summon(final Item item, boolean pet) { + summon(item, pet, true); + } + + /** + * Morphs a pet. + * @param item the item. + * @param deleteItem the item. + * @param location the 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); + } + + /** + * Summons a pet. + * @param item the item. + * @param deleteItem the item. + */ + private boolean summonPet(final Item item, boolean deleteItem) { + return summonPet(item, deleteItem, false, null, 0, -1, -1); + } + + /** + * Summons a pet. + * @param item the item. + * @param morph the pet. + */ + 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; + } + Pets pets = Pets.forId(itemId); + if (pets == null) { + return false; + } + if (player.getSkills().getStaticLevel(Skills.SUMMONING) < pets.getSummoningLevel()) { + player.getDialogueInterpreter().sendDialogue("You need a summoning level of " + pets.getSummoningLevel() + " to summon this."); + return false; + } + if (!this.petDetails.containsKey(itemId)) { + petDetails.put(itemId, new ArrayList()); + } + int last = this.petDetails.get(itemId).size() - 1; + if (last < 0) { //new pet + last = 0; + PetDetails details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); + this.petDetails.get(itemId).add(details); + } + PetDetails details = this.petDetails.get(itemId).get(last); + int npcId = pets.getNpcId(itemId); + if (npcId > 0) { + familiar = new Pet(player, details, itemId, npcId); + ((Pet) familiar).setHasWarned(hasWarned); + if (hunger != -1) ((Pet) familiar).getDetails().setHunger(hunger); + if (growth != -1) ((Pet) familiar).getDetails().setGrowth(growth); + if (deleteItem) { + player.animate(new Animation(827)); + if (!player.getInventory().remove(item, true)) { + return false; + } + } + if (morph) { + morphFamiliar(location); + } else { + spawnFamiliar(); + } + return true; + } + return true; + } + + /** + * Morphs the current familiar. + * @param location the location. + */ + public void morphFamiliar(Location location) { + familiar.init(location, false); + player.getInterfaceManager().openTab(new Component(662)); + player.getInterfaceManager().setViewedTab(7); + } + + /** + * Spawns the current familiar. + */ + public void spawnFamiliar() { + familiar.init(); + player.getInterfaceManager().openTab(new Component(662)); + player.getInterfaceManager().setViewedTab(7); + } + + /** + * Makes the pet eat. + * @param foodId The food item id. + * @param npc The pet NPC. + */ + public void eat(int foodId, Pet npc) { + if (npc != familiar) { + player.getPacketDispatch().sendMessage("This isn't your pet!"); + return; + } + Pet pet = (Pet) familiar; + Pets pets = Pets.forId(pet.getItemId()); + if (pets == null) { + return; + } + 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); + return; + } + } + player.getPacketDispatch().sendMessage("Nothing interesting happens."); + } + + /** + * Picks up a pet. + */ + public void pickup() { + if (player.getInventory().freeSlots() == 0) { + player.getPacketDispatch().sendMessage("You don't have enough room in your inventory."); + return; + } + Pet pet = ((Pet) familiar); + if (player.getInventory().add(new Item(pet.getItemId()))) { + player.animate(Animation.create(827)); + player.getFamiliarManager().dismiss(); + } + } + + /** + * Adjusts the battle state. + * @param state the state. + */ + public void adjustBattleState(final BattleState state) { + if (!hasFamiliar()) { + return; + } + familiar.adjustPlayerBattle(state); + } + + /** + * Gets a boost from a familiar. + * @param skill the skill. + * @return the boosted level. + */ + public int getBoost(int skill) { + if (!hasFamiliar()) { + return 0; + } + return familiar.getBoost(skill); + } + + /** + * Checks if the player has an active familiar. + * @return {@code True} if so. + */ + public boolean hasFamiliar() { + return familiar != null; + } + + /** + * Checks if the player has an active familiar and is a pet. + * @return {@code True} if so. + */ + public boolean hasPet() { + return hasFamiliar() && familiar instanceof Pet; + } + + /** + * Dismisses the familiar. + */ + public void dismiss() { + if (hasFamiliar()) { + familiar.dismiss(); + } + } + + /** + * 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 addDetails(int itemId, PetDetails details) { + if (petDetails.get(itemId) == null) { + petDetails.put(itemId, new ArrayList<>()); + } + petDetails.get(itemId).add(details); + } + + /** + * Removes the details for this pet. + * @param itemId The item id of the pet. + */ + public void removeDetails(int itemId) { + int last = petDetails.get(itemId).size() - 1; + if (last >= 0) { + petDetails.get(itemId).remove(last); + } + } + + /** + * Checks if it's the owner of a familiar. + * @param familiar the familiar + * @return {@code True} if so. + */ + public boolean isOwner(Familiar familiar) { + if (!hasFamiliar()) { + return false; + } + if (this.familiar != familiar) { + player.getPacketDispatch().sendMessage("This is not your familiar."); + return false; + } + return true; + } + + /** + * Gets the familiar. + * @return The familiar. + */ + public Familiar getFamiliar() { + return familiar; + } + + /** + * Sets the familiar. + * @param familiar The familiar to set. + */ + public void setFamiliar(Familiar familiar) { + this.familiar = familiar; + } + + /** + * Gets the familiars. + * @return The familiars. + */ + public static Map getFamiliars() { + return FAMILIARS; + } + + /** + * Gets the usingSummoning. + * @return The usingSummoning. + */ + public boolean isUsingSummoning() { + return hasPouch || (hasFamiliar() && !hasPet()); + } + + /** + * Gets the hasPouch. + * @return The hasPouch. + */ + public boolean isHasPouch() { + return hasPouch; + } + + /** + * Sets the hasPouch. + * @param hasPouch The hasPouch to set. + */ + public void setHasPouch(boolean hasPouch) { + this.hasPouch = hasPouch; + } + + /** + * Gets the summoningCombatLevel. + * @return The summoningCombatLevel. + */ + public int getSummoningCombatLevel() { + return summoningCombatLevel; + } + + /** + * Sets the summoningCombatLevel. + * @param summoningCombatLevel The summoningCombatLevel to set. + */ + public void setSummoningCombatLevel(int summoningCombatLevel) { + this.summoningCombatLevel = summoningCombatLevel; + } + + + public Map> getPetDetails() { + return petDetails; + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarNPCOptionPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarNPCOptionPlugin.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarNPCOptionPlugin.java rename to Server/src/main/content/global/skill/summoning/familiar/FamiliarNPCOptionPlugin.java index 558fe0d5d..6801931b9 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarNPCOptionPlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarNPCOptionPlugin.java @@ -1,10 +1,9 @@ -package core.game.node.entity.skill.summoning.familiar; +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.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/java/core/game/node/entity/skill/summoning/familiar/FamiliarSpecial.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarSpecial.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarSpecial.java rename to Server/src/main/content/global/skill/summoning/familiar/FamiliarSpecial.java index 03cff5599..2aa0b812a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarSpecial.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarSpecial.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.summoning.familiar; +package content.global.skill.summoning.familiar; import core.game.node.Node; import core.game.node.entity.Entity; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/FireTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/FireTitanNPC.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/npc/familiar/FireTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/FireTitanNPC.java index 6f0e44fe7..524ed9a5e 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/FireTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FireTitanNPC.java @@ -1,9 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.combat.equipment.WeaponInterface; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/Forager.java b/Server/src/main/content/global/skill/summoning/familiar/Forager.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/Forager.java rename to Server/src/main/content/global/skill/summoning/familiar/Forager.java index 82e62387e..c7ad322c2 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/Forager.java +++ b/Server/src/main/content/global/skill/summoning/familiar/Forager.java @@ -1,9 +1,9 @@ -package core.game.node.entity.skill.summoning.familiar; +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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.tools.RandomFunction; /** diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/ForgeRegentNPC.java b/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/npc/familiar/ForgeRegentNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java index 7b64dc9d4..3c560fc41 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/ForgeRegentNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.container.impl.EquipmentContainer; import core.game.interaction.NodeUsageEvent; @@ -9,18 +9,16 @@ import core.game.node.item.GroundItemManager; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.firemaking.FireMakingPulse; -import core.game.node.entity.skill.firemaking.Log; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; +import content.global.skill.firemaking.FireMakingPulse; +import content.global.skill.firemaking.Log; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -53,6 +51,7 @@ public class ForgeRegentNPC extends Familiar { */ public ForgeRegentNPC(Player owner, int id) { super(owner, id, 4500, 12782, 6, WeaponInterface.STYLE_RANGE_ACCURATE); + boosts.add(new SkillBonus(Skills.FIREMAKING, 4)); } @Override @@ -62,7 +61,7 @@ public class ForgeRegentNPC extends Familiar { @Override public void configureFamiliar() { - PluginManager.definePlugin(new ForgeRegentFiremake()); + ClassScanner.definePlugin(new ForgeRegentFiremake()); } @Override @@ -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/java/core/game/node/entity/npc/familiar/FruitBatNPC.java b/Server/src/main/content/global/skill/summoning/familiar/FruitBatNPC.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/npc/familiar/FruitBatNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/FruitBatNPC.java index 464b6f41f..bd701f530 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/FruitBatNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FruitBatNPC.java @@ -1,14 +1,11 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.item.WeightedChanceItem; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; 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 rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/GeyserTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/GeyserTitanNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/familiar/GeyserTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/GeyserTitanNPC.java index 67683a0d9..76406f36e 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/GeyserTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/GeyserTitanNPC.java @@ -1,8 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.impl.Projectile; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/GiantChinchompaNPC.java b/Server/src/main/content/global/skill/summoning/familiar/GiantChinchompaNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/familiar/GiantChinchompaNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/GiantChinchompaNPC.java index 2d6e46a6d..c0c57202a 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/GiantChinchompaNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/GiantChinchompaNPC.java @@ -1,16 +1,14 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import java.util.List; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; 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.system.task.Pulse; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/GiantEntNPC.java b/Server/src/main/content/global/skill/summoning/familiar/GiantEntNPC.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/npc/familiar/GiantEntNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/GiantEntNPC.java index e516c4a78..5ed4db98e 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/GiantEntNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/GiantEntNPC.java @@ -1,19 +1,16 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; +import content.global.skill.farming.FarmingPatch; +import content.global.skill.farming.PatchType; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; import org.rs09.consts.Items; -import rs09.game.node.entity.skill.farming.FarmingPatch; -import rs09.game.node.entity.skill.farming.PatchType; /** * Represents the Giant Ent familiar. @@ -80,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/java/core/game/node/entity/npc/familiar/GraniteCrabNPC.java b/Server/src/main/content/global/skill/summoning/familiar/GraniteCrabNPC.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/npc/familiar/GraniteCrabNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/GraniteCrabNPC.java index d26a56499..be937a0eb 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/GraniteCrabNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/GraniteCrabNPC.java @@ -1,12 +1,9 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.fishing.Fish; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; +import content.global.skill.fishing.Fish; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/GraniteLobsterNPC.java b/Server/src/main/content/global/skill/summoning/familiar/GraniteLobsterNPC.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/npc/familiar/GraniteLobsterNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/GraniteLobsterNPC.java index 400cbf7c2..d2db30c53 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/GraniteLobsterNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/GraniteLobsterNPC.java @@ -1,11 +1,8 @@ -package core.game.node.entity.npc.familiar; +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.skill.fishing.Fish; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; +import content.global.skill.fishing.Fish; import core.game.node.entity.Entity; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; @@ -25,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}. @@ -64,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/java/core/game/node/entity/npc/familiar/HoneyBadgerNPC.java b/Server/src/main/content/global/skill/summoning/familiar/HoneyBadgerNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/npc/familiar/HoneyBadgerNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/HoneyBadgerNPC.java index 061c45831..6005c79bb 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/HoneyBadgerNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/HoneyBadgerNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/HydraNPC.java b/Server/src/main/content/global/skill/summoning/familiar/HydraNPC.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/npc/familiar/HydraNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/HydraNPC.java index 19f4ad882..04ba4c6bc 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/HydraNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/HydraNPC.java @@ -1,14 +1,12 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; +import content.global.skill.farming.FarmingPatch; +import content.global.skill.farming.Patch; 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.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.scenery.Scenery; import core.plugin.Initializable; -import rs09.game.node.entity.skill.farming.FarmingPatch; -import rs09.game.node.entity.skill.farming.Patch; /** * Represents the Hydra familiar. @@ -45,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/java/core/game/node/entity/npc/familiar/IbisNPC.java b/Server/src/main/content/global/skill/summoning/familiar/IbisNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/familiar/IbisNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/IbisNPC.java index 0fe94ef0f..7e0c82d73 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/IbisNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/IbisNPC.java @@ -1,16 +1,13 @@ -package core.game.node.entity.npc.familiar; +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.skill.fishing.Fish; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; +import content.global.skill.fishing.Fish; 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 rs09.game.world.GameWorld; +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; @@ -43,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)); } @@ -57,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/java/core/game/node/entity/npc/familiar/IceTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/IceTitanNPC.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/npc/familiar/IceTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/IceTitanNPC.java index 028e51225..23bd94f2c 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/IceTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/IceTitanNPC.java @@ -1,8 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/IronTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/IronTitanNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/familiar/IronTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/IronTitanNPC.java index 1dc684199..f47cbf8e5 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/IronTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/IronTitanNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -6,13 +6,11 @@ import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; 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 rs09.game.node.entity.combat.handlers.MultiSwingHandler; +import core.game.node.entity.combat.MultiSwingHandler; /** * Represents the Iron Titan familiar. diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/KaramthulhuOverlordNPC.java b/Server/src/main/content/global/skill/summoning/familiar/KaramthulhuOverlordNPC.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/npc/familiar/KaramthulhuOverlordNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/KaramthulhuOverlordNPC.java index c96f2eea4..d2e2bf121 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/KaramthulhuOverlordNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/KaramthulhuOverlordNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/dialogue/LavaTitanDialogue.java b/Server/src/main/content/global/skill/summoning/familiar/LavaTitanDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/LavaTitanDialogue.java rename to Server/src/main/content/global/skill/summoning/familiar/LavaTitanDialogue.java index 824ca1a54..dc1854ee9 100644 --- a/Server/src/main/java/core/game/content/dialogue/LavaTitanDialogue.java +++ b/Server/src/main/content/global/skill/summoning/familiar/LavaTitanDialogue.java @@ -1,9 +1,9 @@ -package core.game.content.dialogue; +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.node.entity.skill.summoning.familiar.Familiar; import core.game.world.map.Location; import core.game.world.map.zone.impl.WildernessZone; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/LavaTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/LavaTitanNPC.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/npc/familiar/LavaTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/LavaTitanNPC.java index e9122866d..edac3c9ff 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/LavaTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/LavaTitanNPC.java @@ -1,9 +1,7 @@ -package core.game.node.entity.npc.familiar; +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.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.plugin.Initializable; import core.game.node.entity.player.Player; @@ -30,6 +28,7 @@ public class LavaTitanNPC extends Familiar { public LavaTitanNPC(Player owner, int id) { super(owner, id, 6100, 12788, 4, WeaponInterface.STYLE_AGGRESSIVE); boosts.add(new SkillBonus(Skills.MINING, 10)); + boosts.add(new SkillBonus(Skills.FIREMAKING, 10)); } @Override diff --git a/Server/src/main/java/core/game/interaction/npc/LavaTitanOptionPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/LavaTitanOptionPlugin.java similarity index 93% rename from Server/src/main/java/core/game/interaction/npc/LavaTitanOptionPlugin.java rename to Server/src/main/content/global/skill/summoning/familiar/LavaTitanOptionPlugin.java index db412b594..bdbe77f47 100644 --- a/Server/src/main/java/core/game/interaction/npc/LavaTitanOptionPlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/LavaTitanOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.global.skill.summoning.familiar; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/MacawNPC.java b/Server/src/main/content/global/skill/summoning/familiar/MacawNPC.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/npc/familiar/MacawNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/MacawNPC.java index 8c54317cb..a5ccf78d5 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/MacawNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/MacawNPC.java @@ -1,20 +1,15 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; -import core.game.node.entity.skill.summoning.familiar.RemoteViewer; +import core.game.dialogue.DialoguePlugin; 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 rs09.game.world.GameWorld; +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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.plugin.Initializable; import core.tools.RandomFunction; @@ -59,7 +54,7 @@ public class MacawNPC extends Forager { @Override public Plugin newInstance(Object object) throws Throwable { - PluginManager.definePlugin(new MacawDialogue()); + ClassScanner.definePlugin(new MacawDialogue()); return super.newInstance(object); } diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/MagpieNPC.java b/Server/src/main/content/global/skill/summoning/familiar/MagpieNPC.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/npc/familiar/MagpieNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/MagpieNPC.java index 4aed1733b..244c48980 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/MagpieNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/MagpieNPC.java @@ -1,10 +1,8 @@ -package core.game.node.entity.npc.familiar; +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.skill.crafting.gem.Gems; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; +import content.global.skill.crafting.gem.Gems; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; @@ -38,6 +36,7 @@ public class MagpieNPC extends Forager { */ public MagpieNPC(Player owner, int id) { super(owner, id, 3400, 12041, 3, ITEMS); + boosts.add(new SkillBonus(Skills.THIEVING, 3)); } @Override diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/MinotaurFamiliarNPC.java b/Server/src/main/content/global/skill/summoning/familiar/MinotaurFamiliarNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/familiar/MinotaurFamiliarNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/MinotaurFamiliarNPC.java index 83b5222af..2fbdf0d35 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/MinotaurFamiliarNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/MinotaurFamiliarNPC.java @@ -1,22 +1,21 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; 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 rs09.game.world.GameWorld; +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 rs09.plugin.PluginManager; +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 @@ -27,12 +26,12 @@ public final class MinotaurFamiliarNPC implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new BronzeMinotaurNPC()); - PluginManager.definePlugin(new IronMinotaurNPC()); - PluginManager.definePlugin(new SteelMinotaurNPC()); - PluginManager.definePlugin(new MithrilMinotaurNPC()); - PluginManager.definePlugin(new AdamantMinotaurNPC()); - PluginManager.definePlugin(new RuneMinotaurNPC()); + ClassScanner.definePlugin(new BronzeMinotaurNPC()); + ClassScanner.definePlugin(new IronMinotaurNPC()); + ClassScanner.definePlugin(new SteelMinotaurNPC()); + ClassScanner.definePlugin(new MithrilMinotaurNPC()); + ClassScanner.definePlugin(new AdamantMinotaurNPC()); + ClassScanner.definePlugin(new RuneMinotaurNPC()); return this; } @@ -60,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/java/core/game/node/entity/npc/familiar/MossTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/MossTitanNPC.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/npc/familiar/MossTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/MossTitanNPC.java index 43c296d88..380ec0d5b 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/MossTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/MossTitanNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/ObsidianGolemNPC.java b/Server/src/main/content/global/skill/summoning/familiar/ObsidianGolemNPC.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/npc/familiar/ObsidianGolemNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/ObsidianGolemNPC.java index a5843e79f..6b62e34c0 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/ObsidianGolemNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/ObsidianGolemNPC.java @@ -1,10 +1,8 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.world.update.flag.context.Graphics; diff --git a/Server/src/main/content/global/skill/summoning/familiar/PackYakNPC.java b/Server/src/main/content/global/skill/summoning/familiar/PackYakNPC.java new file mode 100644 index 000000000..b55b6594c --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/PackYakNPC.java @@ -0,0 +1,100 @@ +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; +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 static core.api.ContentAPIKt.addItem; +import static core.api.ContentAPIKt.removeItem; + +/** + * Represents the Pack Yak familiar. + * @author Aero + */ +@Initializable +public class PackYakNPC extends BurdenBeast { + + /** + * Constructs a new {@code PackYakNPC} {@code Object}. + */ + public PackYakNPC() { + this(null, 6873); + } + + /** + * Constructs a new {@code PackYakNPC} {@code Object}. + * @param owner The owner. + * @param id The id. + */ + public PackYakNPC(Player owner, int id) { + super(owner, id, 5800, 12093, 12, 30, WeaponInterface.STYLE_AGGRESSIVE); + } + + @Override + public Familiar construct(Player owner, int id) { + return new PackYakNPC(owner, id); + } + + @Override + protected boolean specialMove(FamiliarSpecial special) { + Player player = owner; + Item item = new Item(special.getItem().getId(), 1); + if (item.getId() == SummoningScroll.WINTER_STORAGE_SCROLL.getItemId()) { + return false; + } + if (!item.getDefinition().getConfiguration(ItemConfigParser.BANKABLE, true)) { + player.sendMessage("A magical force prevents you from banking this item."); + return false; + } + Item remove = item; + if (!item.getDefinition().isUnnoted()) { + remove = new Item(item.getId(), 1); + item = new Item(item.getNoteChange(), 1); + } + 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 pack yak has sent an item to your bank."); + } else { + player.getPacketDispatch().sendMessage("The pack yak can't send that item to your bank."); + } + return true; + } + + @Override + public void visualizeSpecialMove() { + owner.visualize(Animation.create(7660), Graphics.create(1316)); + } + + @Override + public int[] getIds() { + return new int[] { 6873, 6874 }; + } + + @Override + protected String getText() { + return "Baroo!"; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/PrayingMantisNPC.java b/Server/src/main/content/global/skill/summoning/familiar/PrayingMantisNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/familiar/PrayingMantisNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/PrayingMantisNPC.java index 4b4b5d4ab..d6c8298dd 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/PrayingMantisNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/PrayingMantisNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/PyreLordNPC.java b/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/npc/familiar/PyreLordNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java index da1335864..c4672b643 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/PyreLordNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java @@ -1,13 +1,12 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.player.link.diary.DiaryType; import core.plugin.Initializable; +import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.crafting.jewellery.JewelleryCrafting; -import core.game.node.entity.skill.firemaking.FireMakingPulse; -import core.game.node.entity.skill.firemaking.Log; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; +import content.global.skill.crafting.jewellery.JewelleryCrafting; +import content.global.skill.firemaking.FireMakingPulse; +import content.global.skill.firemaking.Log; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.combat.equipment.WeaponInterface; @@ -18,13 +17,12 @@ 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 rs09.game.world.GameWorld; +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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the Pyrelord familiar. @@ -53,6 +51,7 @@ public class PyreLordNPC extends Familiar { */ public PyreLordNPC(Player owner, int id) { super(owner, id, 3200, 12816, 6, WeaponInterface.STYLE_AGGRESSIVE); + boosts.add(new SkillBonus(Skills.FIREMAKING, 3)); } @Override @@ -62,7 +61,7 @@ public class PyreLordNPC extends Familiar { @Override public void configureFamiliar() { - PluginManager.definePlugin(new PyreLordFiremake()); + ClassScanner.definePlugin(new PyreLordFiremake()); } @Override @@ -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/java/core/game/node/entity/npc/familiar/RavenousLocustNPC.java b/Server/src/main/content/global/skill/summoning/familiar/RavenousLocustNPC.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/npc/familiar/RavenousLocustNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/RavenousLocustNPC.java index 4446e06ec..ad6d5180b 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/RavenousLocustNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/RavenousLocustNPC.java @@ -1,10 +1,8 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.content.consumable.Consumable; -import core.game.content.consumable.Consumables; +import core.game.consumable.Consumable; +import content.data.consumables.Consumables; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; @@ -54,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/java/core/game/node/entity/skill/summoning/familiar/RemoteViewDialogue.java b/Server/src/main/content/global/skill/summoning/familiar/RemoteViewDialogue.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/RemoteViewDialogue.java rename to Server/src/main/content/global/skill/summoning/familiar/RemoteViewDialogue.java index c2918c68a..01e9e812f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/RemoteViewDialogue.java +++ b/Server/src/main/content/global/skill/summoning/familiar/RemoteViewDialogue.java @@ -1,9 +1,8 @@ -package core.game.node.entity.skill.summoning.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.summoning.familiar.RemoteViewer.ViewType; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** @@ -43,14 +42,14 @@ public final class RemoteViewDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { familiar = (Familiar) args[0]; - options("North", "East", "Sotuh", "West", "Straight up"); + options("North", "East", "South", "West", "Straight up"); return true; } @Override public boolean handle(int interfaceId, int buttonId) { end(); - RemoteViewer.create(player, familiar, familiar.getViewAnimation(), ViewType.values()[-1 + buttonId]).startViewing(); + RemoteViewer.create(player, familiar, familiar.getViewAnimation(), RemoteViewer.ViewType.values()[-1 + buttonId]).startViewing(); return true; } diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/RemoteViewer.java b/Server/src/main/content/global/skill/summoning/familiar/RemoteViewer.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/RemoteViewer.java rename to Server/src/main/content/global/skill/summoning/familiar/RemoteViewer.java index b30d66f85..647bddda6 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/RemoteViewer.java +++ b/Server/src/main/content/global/skill/summoning/familiar/RemoteViewer.java @@ -1,8 +1,8 @@ -package core.game.node.entity.skill.summoning.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SmokeDevilNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SmokeDevilNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java index 82fd4cc32..49a79fa6a 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SmokeDevilNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SmokeDevilNPC.java @@ -1,11 +1,10 @@ -package core.game.node.entity.npc.familiar; +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.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; @@ -38,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/java/core/game/node/entity/npc/familiar/SpiritCobraNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritCobraNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java index f0e7bfb75..18d4946b2 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritCobraNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritCobraNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.plugin.Initializable; @@ -58,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/java/core/game/node/entity/npc/familiar/SpiritDagannothNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritDagannothNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritDagannothNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritDagannothNPC.java index 13ff8d652..b7e5bacd4 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritDagannothNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritDagannothNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.plugin.Initializable; import core.game.node.entity.player.Player; 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.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.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/java/core/game/node/entity/npc/familiar/SpiritJellyNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritJellyNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritJellyNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritJellyNPC.java index 0ef6c9225..86ee948ed 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritJellyNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritJellyNPC.java @@ -1,8 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.impl.Projectile; diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritKalphiteNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritKalphiteNPC.java new file mode 100644 index 000000000..09a83505c --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritKalphiteNPC.java @@ -0,0 +1,84 @@ +package content.global.skill.summoning.familiar; + +import java.util.List; + +import core.game.node.entity.Entity; +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.system.task.Pulse; +import core.game.world.GameWorld; +import core.game.world.map.RegionManager; +import core.game.world.update.flag.context.Animation; +import core.plugin.Initializable; +import core.game.world.update.flag.context.Graphics; + +/** + * Represents the Spirit Kalphite familiar. + * @author Vexia + * @author Aero + */ +@Initializable +public class SpiritKalphiteNPC extends BurdenBeast { + + /** + * Constructs a new {@code SpiritKalphiteNPC} {@code Object}. + */ + public SpiritKalphiteNPC() { + this(null, 6994); + } + + /** + * Constructs a new {@code SpiritKalphiteNPC} {@code Object}. + * @param owner The owner. + * @param id The id. + */ + public SpiritKalphiteNPC(Player owner, int id) { + super(owner, id, 2200, 12063, 6, 6, WeaponInterface.STYLE_DEFENSIVE); + } + + @Override + public Familiar construct(Player owner, int id) { + return new SpiritKalphiteNPC(owner, id); + } + + @Override + protected boolean specialMove(FamiliarSpecial special) { + 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 + public String getText() { + return "Hsssss!"; + } + + @Override + public int[] getIds() { + return new int[] { 6994, 6995 }; + } + +} diff --git a/Server/src/main/java/core/game/content/dialogue/SpiritKyattDialogue.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritKyattDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/SpiritKyattDialogue.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritKyattDialogue.java index 6ef1cf549..e0f311308 100644 --- a/Server/src/main/java/core/game/content/dialogue/SpiritKyattDialogue.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritKyattDialogue.java @@ -1,7 +1,7 @@ -package core.game.content.dialogue; +package content.global.skill.summoning.familiar; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.TeleportManager.TeleportType; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritKyattNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritKyattNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritKyattNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritKyattNPC.java index 897ea43ca..9bc3c4550 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritKyattNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritKyattNPC.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; +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; @@ -27,6 +27,7 @@ public class SpiritKyattNPC extends Familiar { */ public SpiritKyattNPC(Player owner, int id) { super(owner, id, 4900, 12812, 3, WeaponInterface.STYLE_ACCURATE); + boosts.add(new SkillBonus(Skills.HUNTER, 5)); } @Override diff --git a/Server/src/main/java/core/game/interaction/npc/SpiritKyattOptionPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritKyattOptionPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/npc/SpiritKyattOptionPlugin.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritKyattOptionPlugin.java index 0700cbd80..4cf912244 100644 --- a/Server/src/main/java/core/game/interaction/npc/SpiritKyattOptionPlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritKyattOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.global.skill.summoning.familiar; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritLarupiaNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritLarupiaNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritLarupiaNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritLarupiaNPC.java index acb630e72..64f8c2996 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritLarupiaNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritLarupiaNPC.java @@ -1,9 +1,8 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; +import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; @@ -31,6 +30,7 @@ public class SpiritLarupiaNPC extends Familiar { */ public SpiritLarupiaNPC(Player owner, int id) { super(owner, id, 4900, 12784, 6, WeaponInterface.STYLE_CONTROLLED); + boosts.add(new SkillBonus(Skills.HUNTER, 5)); } @Override diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritMosquitoNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritMosquitoNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritMosquitoNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritMosquitoNPC.java index c343e137f..d1104629c 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritMosquitoNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritMosquitoNPC.java @@ -1,8 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritPengatriceNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritPengatriceNPC.java similarity index 81% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritPengatriceNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritPengatriceNPC.java index 1f6a4694f..4ed246f3b 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritPengatriceNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritPengatriceNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritScorpionNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritScorpionNPC.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritScorpionNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritScorpionNPC.java index 7be64fac1..85bacafa4 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritScorpionNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritScorpionNPC.java @@ -1,8 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -10,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 @@ -51,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/java/core/game/node/entity/npc/familiar/SpiritSpiderNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritSpiderNPC.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritSpiderNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritSpiderNPC.java index 59e8f2404..5857c3b59 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritSpiderNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritSpiderNPC.java @@ -1,13 +1,11 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritTerrorbirdNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritTerrorbirdNPC.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritTerrorbirdNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritTerrorbirdNPC.java index 7024a3e01..9895522b5 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritTerrorbirdNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritTerrorbirdNPC.java @@ -1,10 +1,7 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritTzKihNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritTzKihNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritTzKihNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritTzKihNPC.java index 46cf83d2b..19f97c6ba 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritTzKihNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritTzKihNPC.java @@ -1,10 +1,8 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import java.util.List; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritWolfNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritWolfNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SpiritWolfNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SpiritWolfNPC.java index a7d4897b8..9c528bde7 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritWolfNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritWolfNPC.java @@ -1,22 +1,23 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.content.activity.CutscenePlugin; +import core.game.activity.CutscenePlugin; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.combat.equipment.WeaponInterface; 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; 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. @@ -107,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/java/core/game/node/entity/npc/familiar/SteelTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SteelTitanNPC.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SteelTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SteelTitanNPC.java index e55e6b1b4..7a52be545 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SteelTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SteelTitanNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -7,13 +7,11 @@ import core.game.node.entity.combat.equipment.SwitchAttack; 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.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; 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 rs09.game.node.entity.combat.handlers.MultiSwingHandler; +import core.game.node.entity.combat.MultiSwingHandler; /** * Handles the Steel titan familiar. diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/StrangerPlantNPC.java b/Server/src/main/content/global/skill/summoning/familiar/StrangerPlantNPC.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/npc/familiar/StrangerPlantNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/StrangerPlantNPC.java index 2b42e809b..04d26f299 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/StrangerPlantNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/StrangerPlantNPC.java @@ -1,18 +1,16 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; 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 @@ -48,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/java/core/game/node/entity/skill/summoning/familiar/SummonFamiliarPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/SummonFamiliarPlugin.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/skill/summoning/familiar/SummonFamiliarPlugin.java rename to Server/src/main/content/global/skill/summoning/familiar/SummonFamiliarPlugin.java index 76db891c4..ee216033e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/SummonFamiliarPlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SummonFamiliarPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.summoning.familiar; +package content.global.skill.summoning.familiar; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; @@ -9,7 +9,7 @@ import core.game.node.item.Item; import core.game.world.map.zone.ZoneBorders; import core.plugin.Initializable; import core.plugin.Plugin; -import core.game.node.entity.npc.familiar.IbisNPC; +import content.data.Quests; /** * Handles summoning a familiar. @@ -27,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/java/core/game/node/entity/npc/familiar/SwampTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SwampTitanNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/familiar/SwampTitanNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/SwampTitanNPC.java index db07d71e2..eb9071a3f 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SwampTitanNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SwampTitanNPC.java @@ -1,8 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/TalonBeastNPC.java b/Server/src/main/content/global/skill/summoning/familiar/TalonBeastNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/familiar/TalonBeastNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/TalonBeastNPC.java index 5d1f8cd16..b9d85375d 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/TalonBeastNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/TalonBeastNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/ThornySnailNPC.java b/Server/src/main/content/global/skill/summoning/familiar/ThornySnailNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/familiar/ThornySnailNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/ThornySnailNPC.java index 7c6b38c01..6e3bf4f95 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/ThornySnailNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/ThornySnailNPC.java @@ -1,9 +1,6 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -11,7 +8,7 @@ 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.system.task.Pulse; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/UnicornStallionNPC.java b/Server/src/main/content/global/skill/summoning/familiar/UnicornStallionNPC.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/npc/familiar/UnicornStallionNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/UnicornStallionNPC.java index 0d6f6a07b..fde55d548 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/UnicornStallionNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/UnicornStallionNPC.java @@ -1,19 +1,19 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.cache.def.impl.NPCDefinition; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; 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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Represents the Unicorn Stallion familiar. @@ -46,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; @@ -54,7 +54,7 @@ public class UnicornStallionNPC extends Familiar { @Override protected void configureFamiliar() { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { @@ -74,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/java/core/game/node/entity/npc/familiar/VampireBatNPC.java b/Server/src/main/content/global/skill/summoning/familiar/VampireBatNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/npc/familiar/VampireBatNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/VampireBatNPC.java index e29eabcdc..5c1007c45 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/VampireBatNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/VampireBatNPC.java @@ -1,7 +1,5 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.Entity; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.combat.equipment.WeaponInterface; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/VoidFamiliarNPC.java b/Server/src/main/content/global/skill/summoning/familiar/VoidFamiliarNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/familiar/VoidFamiliarNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/VoidFamiliarNPC.java index 255cd6c62..4f8d3fab5 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/VoidFamiliarNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/VoidFamiliarNPC.java @@ -1,11 +1,9 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; 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 core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.skill.summoning.familiar.Forager; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.combat.BattleState; @@ -13,12 +11,12 @@ import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * The plugin used to load void familiar npcs. @@ -35,10 +33,10 @@ public final class VoidFamiliarNPC implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new VoidRavagerNPC()); - PluginManager.definePlugin(new VoidShifterNPC()); - PluginManager.definePlugin(new VoidSpinnerNPC()); - PluginManager.definePlugin(new VoidTorcherNPC()); + ClassScanner.definePlugin(new VoidRavagerNPC()); + ClassScanner.definePlugin(new VoidShifterNPC()); + ClassScanner.definePlugin(new VoidSpinnerNPC()); + ClassScanner.definePlugin(new VoidTorcherNPC()); return this; } @@ -97,6 +95,7 @@ public final class VoidFamiliarNPC implements Plugin { */ public VoidRavagerNPC(Player owner, int id) { super(owner, id, 2700, 12818, 3, WeaponInterface.STYLE_AGGRESSIVE, ITEMS); + boosts.add(new SkillBonus(Skills.MINING, 1)); } @Override @@ -249,7 +248,7 @@ public final class VoidFamiliarNPC implements Plugin { @Override public void configureFamiliar() { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/WarTortoiseNPC.java b/Server/src/main/content/global/skill/summoning/familiar/WarTortoiseNPC.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/npc/familiar/WarTortoiseNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/WarTortoiseNPC.java index 661dd8493..b3b64551a 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/WarTortoiseNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/WarTortoiseNPC.java @@ -1,10 +1,7 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/WolpertingerNPC.java b/Server/src/main/content/global/skill/summoning/familiar/WolpertingerNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/familiar/WolpertingerNPC.java rename to Server/src/main/content/global/skill/summoning/familiar/WolpertingerNPC.java index 4bee97387..ff1cfbcb6 100644 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/WolpertingerNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/WolpertingerNPC.java @@ -1,9 +1,8 @@ -package core.game.node.entity.npc.familiar; +package content.global.skill.summoning.familiar; import core.plugin.Initializable; +import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.world.update.flag.context.Animation; @@ -30,6 +29,7 @@ public class WolpertingerNPC extends Familiar { */ public WolpertingerNPC(Player owner, int id) { super(owner, id, 6200, 12089, 1, WeaponInterface.STYLE_CAST); + boosts.add(new SkillBonus(Skills.HUNTER, 5)); } @Override diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/pet/IncubatorEgg.java b/Server/src/main/content/global/skill/summoning/pet/IncubatorEgg.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/skill/summoning/pet/IncubatorEgg.java rename to Server/src/main/content/global/skill/summoning/pet/IncubatorEgg.java index 3a98c948c..11a6a024d 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/pet/IncubatorEgg.java +++ b/Server/src/main/content/global/skill/summoning/pet/IncubatorEgg.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.summoning.pet; +package content.global.skill.summoning.pet; import core.game.node.item.Item; 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/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 new file mode 100644 index 000000000..24a387d18 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java @@ -0,0 +1,145 @@ +package content.global.skill.summoning.pet; + +import core.game.dialogue.DialoguePlugin; +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.map.RegionManager; +import core.game.world.map.path.Path; +import core.game.world.map.path.Pathfinder; +import core.plugin.Initializable; +import core.game.world.update.flag.context.Animation; + +/** + * Represents the dialogue plugin used for kitten interactions. + * @author Vexia + * @version 1.0 + */ +@Initializable +public final class KittenInteractDialogue extends DialoguePlugin { + + /** + * Represents the animation to use. + */ + 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}. + */ + public KittenInteractDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code KittenInterfactDialogue} {@code Object}. + * @param player the player. + */ + public KittenInteractDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new KittenInteractDialogue(player); + } + + @Override + public boolean open(Object... args) { + interpreter.sendOptions("Interact with Kitten", "Stroke", "Chase vermin", "Shoo away"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + switch (buttonId) { + case 1:// stroke + 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-vermin + end(); + player.sendChat("Go on puss...kill that rat!"); + boolean cant = true; + NPC rat = null; + for (NPC n : RegionManager.getLocalNpcs(player.getLocation(), 10)) { + if (!n.getName().contains("rat")) { + cant = false; + continue; + } + if (n.getLocation().getDistance(player.getFamiliarManager().getFamiliar().getLocation()) < 8) { + cant = true; + rat = n; + break; + } else { + cant = false; + } + } + if (!cant) { + player.getPacketDispatch().sendMessage("Your cat cannot get to its prey."); + } else { + player.getFamiliarManager().getFamiliar().sendChat("Meeeoooooowwww!"); + final Path path = Pathfinder.find(player.getFamiliarManager().getFamiliar(), rat); + path.walk(player.getFamiliarManager().getFamiliar()); + rat.sendChat("Eeek!"); + GameWorld.getPulser().submit(new Pulse(5) { + + @Override + public boolean pulse() { + player.getFamiliarManager().getFamiliar().call(); + player.getPacketDispatch().sendMessage("The rat manages to get away!"); + return true; + } + + }); + } + break; + case 3:// shoo-away + interpreter.sendOptions("Are you sure?", "Yes I am.", "No I'm not."); + stage = 560; + break; + } + break; + case 560: + switch (buttonId) { + case 1:// yes + 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 + end(); + break; + } + break; + case 99: + if (player.getFamiliarManager().hasFamiliar()) { + player.getFamiliarManager().getFamiliar().sendChat("Miaow!"); + } + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 343823 }; + } +} diff --git a/Server/src/main/content/global/skill/summoning/pet/Pet.java b/Server/src/main/content/global/skill/summoning/pet/Pet.java new file mode 100644 index 000000000..a115040d1 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/pet/Pet.java @@ -0,0 +1,214 @@ +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 { + + /** + * The item id. + */ + private final int itemId; + + /** + * The pet details. + */ + private final PetDetails details; + + /** + * The growth rate of the pet. + */ + 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. + * @param details the details. + * @param itemId the itemId. + * @param id the id. + */ + public Pet(Player owner, final PetDetails details, int itemId, int id) { + super(owner, id, -1, -1, -1); + this.pet = Pets.forId(itemId); + this.details = details; + this.itemId = itemId; + this.growthRate = pet.getGrowthRate(); + } + + @Override + public void sendConfiguration() { + 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 && !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 && hasWarned < 1) { + owner.sendMessage("Your pet is getting hungry."); + hasWarned = 1; + } + 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(); + owner.getFamiliarManager().removeDetails(getItemId()); + owner.getFamiliarManager().setFamiliar(null); + setVarp(owner, 1175, 0); + owner.sendMessage("Your pet has run away."); + return; + } + double growth = petDetails.getGrowth(); + 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(); + } + } + if ((!isInvisible() && owner.getLocation().getDistance(getLocation()) > 12) || (isInvisible() && ticks % 25 == 0)) { + if (!call()) { + setInvisible(true); + } + } else if (!getPulseManager().hasPulseRunning()) { + startFollowing(); + } + setVarp(owner, 1175, ((int) details.getGrowth() << 1) | ((int) details.getHunger() << 9)); + } + + /** + * Method used to grow the npc's next stage. + */ + public void growNextStage() { + if (pet == null) { + return; + } + int newItemId = pet.getNextStageItemId(itemId); + if (newItemId == -1) { + // then this pet is already overgrown + return; + } + if (pet.isKitten(itemId)) { + owner.incrementAttribute("/save:stats_manager:cats_raised"); + } + owner.getFamiliarManager().addDetails(newItemId, details); + owner.getFamiliarManager().removeDetails(getItemId()); + owner.getFamiliarManager().morphPet(new Item(newItemId), false, location, details.getHunger(), 0); + owner.getPacketDispatch().sendMessage("Your pet has grown larger."); + } + + @Override + public Familiar construct(Player owner, int id) { + return this; + } + + @Override + protected boolean specialMove(FamiliarSpecial special) { + return false; + } + + @Override + public boolean isCombatFamiliar() { + return false; + } + + /** + * Gets the itemId. + * @return The itemId. + */ + public int getItemId() { + return itemId; + } + + /** + * Gets the details. + * @return The details. + */ + public PetDetails getDetails() { + return details; + } + + /** + * Gets the pet. + * @return The pet. + */ + public Pets getPet() { + 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 new file mode 100644 index 000000000..b3b0b8f05 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/pet/PetDetails.java @@ -0,0 +1,83 @@ +package content.global.skill.summoning.pet; + + + +/** + * A class containing pet details for a certain pet. + * @author Emperor + * @author Player Name + */ +public final class PetDetails { + + /** + * The hunger rate. + */ + private double hunger = 0.0; + + /** + * The growth rate. + */ + private double growth = 0.0; + + /** + * Constructs a new {@code PetDetails} {@code Object}. + * @param growth The growth value. + */ + public PetDetails(double growth) { + this.growth = growth; + } + + /** + * Increases the hunger value by the given amount. + * @param amount The amount. + */ + public void updateHunger(double amount) { + hunger += amount; + if (hunger < 0.0) { + hunger = 0.0; + } + } + + /** + * Increases the growth value by the given amount. + * @param amount The amount. + */ + public void updateGrowth(double amount) { + growth += amount; + if (growth < 0.0) { + growth = 0.0; + } else if (growth > 100.0) { + growth = 100.0; + } + } + + /** + * Gets the hunger. + * @return The hunger. + */ + public double getHunger() { + 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. + */ + public double getGrowth() { + return growth; + } + + /** + * Sets the growth. (You probably want to use updateGrowth() instead.) + */ + public void setGrowth(double value) { + this.growth = value; + } +} diff --git a/Server/src/main/content/global/skill/summoning/pet/Pets.java b/Server/src/main/content/global/skill/summoning/pet/Pets.java new file mode 100644 index 000000000..3258099fc --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/pet/Pets.java @@ -0,0 +1,419 @@ +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, 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), + + /** + * A bulldog pet. + */ + BULLDOG(12522, 12523, -1, 6969, 6968, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), BULLDOG_1(12720, 12721, -1, 7259, 7257, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), BULLDOG_2(12722, 12723, -1, 7260, 7258, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), + + /** + * A dalmation pet. + */ + DALMATIAN(12518, 12519, -1, 6964, 6965, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), DALMATIAN_1(12712, 12713, -1, 7249, 7250, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), DALMATIAN_2(12714, 12715, -1, 7251, 7252, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), + + /** + * A greyhound pet. + */ + GREYHOUND(12514, 12515, -1, 6960, 6961, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), GREYHOUND_1(12704, 12705, -1, 7241, 7242, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), GREYHOUND_2(12706, 12707, -1, 7243, 7244, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), + + /** + * A labrador pet. + */ + LABRADOR(12516, 12517, -1, 6962, 6963, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), LABRADOR_1(12708, 12709, -1, 7245, 7246, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), LABRADOR_2(12710, 12711, -1, 7247, 7248, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), + + /** + * A sheepdog pet. + */ + SHEEPDOG(12520, 12521, -1, 6966, 6967, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), SHEEPDOG_1(12716, 12717, -1, 7253, 7254, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), SHEEPDOG_2(12718, 12719, -1, 7255, 7256, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), + + /** + * A terrier pet. + */ + 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. + */ + GECKO(12488, 12489, -1, 6915, 6916, -1, 0.005, 10, 12125, 12127), GECKO_1(12738, 12742, -1, 7277, 7281, -1, 0.005, 10, 12125, 12127), GECKO_2(12739, 12743, -1, 7278, 7282, -1, 0.005, 10, 12125, 12127), GECKO_3(12740, 12744, -1, 7279, 7283, -1, 0.005, 10, 12125, 12127), GECKO_4(12741, 12745, -1, 7280, 7284, -1, 0.005, 10, 12125, 12127), + + /** + * The platypus pet. + */ + PLATYPUS(12551, 12548, -1, 7018, 7015, -1, 0.0046296296296296, 10, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129), PLATYPUS_1(12552, 12549, -1, 7019, 7016, -1, 0.0046296296296296, 10, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129), PLATYPUS_2(12553, 12550, -1, 7020, 7017, -1, 0.0046296296296296, 10, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129), + + /** + * The broav pet. + */ + BROAV(14533, -1, -1, 8491, -1, -1, 0.0, 23, 2970), + + /** + * The penguin pet. + */ + 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 giant crab pet. + */ + GIANT_CRAB(12500, 12501, -1, 6947, 6948, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), GIANT_CRAB_1(12746, 12747, -1, 7293, 7294, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), GIANT_CRAB_2(12748, 12749, -1, 7295, 7296, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), GIANT_CRAB_3(12750, 12751, -1, 7297, 7298, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), GIANT_CRAB_4(12752, 12753, -1, 7299, 7300, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), + + /** + * A Raven pet. + */ + RAVEN(12484, 12485, -1, 6911, 6912, -1, 0.00698888, 50, 313, 12129), RAVEN_1(12724, 12725, -1, 7261, 7262, -1, 0.00698888, 50, 313, 12129), RAVEN_2(12726, 12727, -1, 7263, 7264, -1, 0.00698888, 50, 313, 12129), RAVEN_3(12728, 12729, -1, 7265, 7266, -1, 0.00698888, 50, 313, 12129), RAVEN_4(12730, 12731, -1, 7267, 7268, -1, 0.00698888, 50, 313, 12129), RAVEN_5(12732, 12733, -1, 7269, 7270, -1, 0.00698888, 50, 313, 12129), + + /** + * A squirrel pet. + */ + SQUIRREL(12490, 12491, -1, 6919, 6920, -1, 0.0071225071225071, 60, 12130), SQUIRREL_1(12754, 12755, -1, 7301, 7302, -1, 0.0071225071225071, 60, 12130), SQUIRREL_2(12756, 12757, -1, 7303, 7304, -1, 0.0071225071225071, 60, 12130), SQUIRREL_3(12758, 12759, -1, 7305, 7306, -1, 0.0071225071225071, 60, 12130), SQUIRREL_4(12760, 12761, -1, 7307, 7308, -1, 0.0071225071225071, 60, 12130), + + /** + * Godbirds. + */ + SARADOMIN_OWL(12503, 12504, 12505, 6949, 6950, 6951, 0.0069444444444444, 70, 313, 12129), ZAMORAK_HAWK(12506, 12507, 12508, 6952, 6953, 6954, 0.0069444444444444, 70, 313, 12129), GUTHIX_RAPTOR(12509, 12510, 12511, 6955, 6956, 6957, 0.0069444444444444, 70, 313, 12129), + + /** + * Ex-ex parrot + */ + EX_EX_PARROT(13335, -1, -1, 7844, -1, -1, 0.0, 71, 13379), + + /** + * The phoenix eggling pets. + */ + CUTE_PHOENIX_EGGLING(14627, -1, -1, 8578, -1, -1, 0.0, 72, 592), MEAN_PHOENIX_EGGLING(14626, -1, -1, 8577, -1, -1, 0.0, 72, 592), + + /** + * A raccoon pet. + */ + 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 vulture pet. + */ + VULTURE(12498, 12499, -1, 6945, 6946, -1, 0.0078, 85, 313, 12129), VULTURE_1(12766, 12767, -1, 7319, 7320, -1, 0.0078, 85, 313, 12129), VULTURE_2(12768, 12769, -1, 7321, 7322, -1, 0.0078, 85, 313, 12129), VULTURE_3(12770, 12771, -1, 7323, 7324, -1, 0.0078, 85, 313, 12129), VULTURE_4(12772, 12773, -1, 7325, 7326, -1, 0.0078, 85, 313, 12129), VULTURE_5(12774, 12775, -1, 7327, 7328, -1, 0.0078, 85, 313, 12129), + + /** + * A chameleon pet. + */ + CHAMELEON(12492, 12493, -1, 6922, 6923, -1, 0.0069444444444444, 90, 12125), + + /** + * A monkey pet. + */ + MONKEY(12496, 12497, -1, 6942, 6943, -1, 0.0069444444444444, 95, 1963), MONKEY_1(12682, 12683, -1, 7210, 7211, -1, 0.0069444444444444, 95, 1963), MONKEY_2(12684, 12685, -1, 7212, 7213, -1, 0.0069444444444444, 95, 1963), MONKEY_3(12686, 12687, -1, 7214, 7215, -1, 0.0069444444444444, 95, 1963), MONKEY_4(12688, 12689, -1, 7216, 7217, -1, 0.0069444444444444, 95, 1963), MONKEY_5(12690, 12691, -1, 7218, 7219, -1, 0.0069444444444444, 95, 1963), MONKEY_6(12692, 12693, -1, 7220, 7221, -1, 0.0069444444444444, 95, 1963), MONKEY_7(12694, 12695, -1, 7222, 7223, -1, 0.0069444444444444, 95, 1963), MONKEY_8(12696, 12697, -1, 7224, 7225, -1, 0.0069444444444444, 95, 1963), MONKEY_9(12698, 12699, -1, 7226, 7227, -1, 0.0069444444444444, 95, 1963), + + /** + * A baby dragon pet. + */ + 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); + + /** + * The baby pets mapping. + */ + private static final Map babyPets = new HashMap(); + + /** + * The grown pets mapping. + */ + private static final Map grownPets = new HashMap(); + + /** + * The overgrown pets mapping. + */ + private static final Map overgrownPets = new HashMap(); + + /** + * Populates the mappings. + */ + static { + for (Pets pet : Pets.values()) { + babyPets.put(pet.babyItemId, pet); + if (pet.grownItemId > 0) { + grownPets.put(pet.grownItemId, pet); + if (pet.getOvergrownItemId() > 0) { + overgrownPets.put(pet.overgrownItemId, pet); + } + } + } + } + + /** + * Gets the pet object for the item id. + * @param itemId The item id. + * @return The pet object. + */ + public static Pets forId(int itemId) { + Pets pet = babyPets.get(itemId); + if (pet == null) { + pet = grownPets.get(itemId); + if (pet == null) { + return overgrownPets.get(itemId); + } + return pet; + } + return pet; + } + + /** + * Checks if a player has a pet. + * @param player The player. + * @return {@code True} if so. + */ + public static boolean hasPet(Player player) { + for (int itemId : babyPets.keySet()) { + if (player.getInventory().containsAtLeastOneItem(itemId)) { + return true; + } + } + for (int itemId : grownPets.keySet()) { + if (player.getInventory().containsAtLeastOneItem(itemId)) { + return true; + } + } + for (int itemId : overgrownPets.keySet()) { + if (player.getInventory().containsAtLeastOneItem(itemId)) { + return true; + } + } + return false; + } + + /** + * The baby item id. + */ + private final int babyItemId; + + /** + * The grown pet's item id. + */ + private final int grownItemId; + + /** + * The overgrown pet's item id. + */ + private final int overgrownItemId; + + /** + * The baby pet NPC id. + */ + private final int babyNpcId; + + /** + * The grown pet NPC id. + */ + private final int grownNpcId; + + /** + * The overgrown pet NPC id. + */ + private final int overgrownNpcId; + + /** + * The growth rate. + */ + private final double growthRate; + + /** + * The summoning level required. + */ + private final int summoningLevel; + + /** + * The food this pet uses. + */ + private final int[] food; + + /** + * Constructs a new {@code Pets} {@code Object}. + * @param babyItemId The baby pet item id. + * @param grownItemId The grown pet item id. + * @param overgrownItemId The overgrown item id. + * @param babyNpcId The baby pet npc id. + * @param grownNpcId The grown pet npc id. + * @param overgrownNpcId The overgrown npc id. + * @param growthRate The growth rate (amount to increase growth with every + * tick). + * @param summoningLevel The summoning level required. + * @param food The food item ids the pet uses. + */ + private Pets(int babyItemId, int grownItemId, int overgrownItemId, int babyNpcId, int grownNpcId, int overgrownNpcId, double growthRate, int summoningLevel, int... food) { + this.babyItemId = babyItemId; + this.grownItemId = grownItemId; + this.overgrownItemId = overgrownItemId; + this.babyNpcId = babyNpcId; + this.grownNpcId = grownNpcId; + this.overgrownNpcId = overgrownNpcId; + this.growthRate = growthRate; + this.summoningLevel = summoningLevel; + this.food = food; + } + + /** + * Gets the babyItemId. + * @return The babyItemId. + */ + public int getBabyItemId() { + return babyItemId; + } + + /** + * Gets the grownItemId. + * @return The grownItemId. + */ + public int getGrownItemId() { + return grownItemId; + } + + /** + * Gets the overgrownItemId. + * @return The overgrownItemId. + */ + public int getOvergrownItemId() { + return overgrownItemId; + } + + /** + * Gets the babyNpcId. + * @return The babyNpcId. + */ + public int getBabyNpcId() { + return babyNpcId; + } + + /** + * Gets the grownNpcId. + * @return The grownNpcId. + */ + public int getGrownNpcId() { + return grownNpcId; + } + + /** + * Gets the overgrownNpcId. + * @return The overgrownNpcId. + */ + public int getOvergrownNpcId() { + return overgrownNpcId; + } + + /** + * Gets the growthRate. + * @return The growthRate. + */ + public double getGrowthRate() { + return growthRate; + } + + /** + * Gets the summoningLevel. + * @return The summoningLevel. + */ + public int getSummoningLevel() { + return summoningLevel; + } + + /** + * Gets the food. + * @return The food. + */ + public int[] getFood() { + return food; + } + + /** + * Gets the NPC id for this pet. + * @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 itemId) { + if (itemId == babyItemId) { + return babyNpcId; + } + if (itemId == grownItemId) { + return grownNpcId; + } + if (itemId == overgrownItemId) { + return overgrownNpcId; + } + log(this.getClass(), Log.ERR, "Could not locate NPC ID for pet item " + itemId); + return -1; + } + + /** + * 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 getNextStageItemId(int itemId) { + if (itemId == babyItemId) { + return grownItemId; + } + if (itemId == grownItemId) { + return overgrownItemId; + } + return -1; + } + + /** + * 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/java/core/game/node/entity/skill/thieving/PickableDoorHandler.java b/Server/src/main/content/global/skill/thieving/PickableDoorHandler.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/skill/thieving/PickableDoorHandler.java rename to Server/src/main/content/global/skill/thieving/PickableDoorHandler.java index 408944d2e..7014d0781 100644 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/PickableDoorHandler.java +++ b/Server/src/main/content/global/skill/thieving/PickableDoorHandler.java @@ -1,7 +1,7 @@ -package core.game.node.entity.skill.thieving; +package content.global.skill.thieving; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; @@ -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/kotlin/rs09/game/node/entity/skill/thieving/Pickpockets.kt b/Server/src/main/content/global/skill/thieving/Pickpockets.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/Pickpockets.kt rename to Server/src/main/content/global/skill/thieving/Pickpockets.kt index 8fe2b811c..4580f2518 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/Pickpockets.kt +++ b/Server/src/main/content/global/skill/thieving/Pickpockets.kt @@ -1,19 +1,19 @@ -package rs09.game.node.entity.skill.thieving +package content.global.skill.thieving import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.tools.RandomFunction import org.rs09.consts.Items import org.rs09.consts.NPCs -import rs09.game.content.global.WeightBasedTable -import rs09.game.content.global.WeightedItem +import core.api.utils.WeightBasedTable +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, 170, 3915, 3226, 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( + FARMER(intArrayOf(7, 1757, 1758), 10, 180.0, 240.0, 14.5, 1,1,5, WeightBasedTable.create( WeightedItem(Items.COINS_995,9,9,1.0,true), WeightedItem(Items.POTATO_SEED_5318,1,1,1.0,true) )), @@ -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( @@ -151,7 +151,7 @@ enum class Pickpockets(val ids: IntArray, val requiredLevel: Int, val low: Doubl KNIGHT_OF_ADROUGNE(intArrayOf(23, 26), 55, 50.0, 240.0, 84.3, 3,3,5, WeightBasedTable.create( WeightedItem(Items.COINS_995,50,50,1.0,true) )), - YANILLE_WATCHMAN(intArrayOf(34), 65, 137.5, 50.0, 240.0, 3,3,5, WeightBasedTable.create( + YANILLE_WATCHMAN(intArrayOf(34), 65, 50.0, 240.0, 137.5, 3,3,5, WeightBasedTable.create( WeightedItem(Items.COINS_995,60,60,1.0,true), WeightedItem(Items.BREAD_2309,1,1,1.0,true) )), diff --git a/Server/src/main/java/core/game/node/entity/skill/thieving/Stall.java b/Server/src/main/content/global/skill/thieving/Stall.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/skill/thieving/Stall.java rename to Server/src/main/content/global/skill/thieving/Stall.java index b61e22515..59498da4a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/Stall.java +++ b/Server/src/main/content/global/skill/thieving/Stall.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.thieving; +package content.global.skill.thieving; import core.game.node.item.Item; import core.game.node.scenery.Scenery; @@ -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,10 +19,10 @@ 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}, new Integer[] { 34381, 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 }, 42, new Item[]{new Item(331, 1), new Item(359, 1), new Item(377, 1)}, 42, 27,"fish"), + 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"), CROSSBOW_STALL(new Integer[]{17031}, new Integer[] { 6984 }, 49, new Item[]{new Item(877, 3), new Item(9420, 1), new Item(9440, 1)}, 52, 19,"equipment"), SILVER_STALL(new Integer[]{2565, 6164, 34382}, new Integer[] { 634, 6984, 34381}, 50, new Item[]{new Item(442, 1)}, 54, 50,"jewellery"), SPICE_STALL(new Integer[]{34386, 6166}, new Integer[] { 34381, 6984 }, 65, new Item[]{new Item(2007, 1)}, 81, 134,"spices"), @@ -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 new file mode 100644 index 000000000..b08185335 --- /dev/null +++ b/Server/src/main/content/global/skill/thieving/StallThiefPulse.java @@ -0,0 +1,170 @@ +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; +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.GameWorld; +import core.game.world.map.RegionManager; +import core.game.world.update.flag.context.Animation; +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. + * @author 'Vexia + */ +public final class StallThiefPulse extends SkillPulse { + + /** + * Represents the stealing animation. + */ + private static final Animation ANIMATION = new Animation(832); + + /** + * Represents the stall being thieved. + */ + private final Stall stall; + + /** + * Represents the ticks passed. + */ + private int ticks; + + /** + * Constructs a new {@code StallThiefPulse} {@code Object}. + * @param player the player. + * @param node the node. + * @param stall the stall. + */ + public StallThiefPulse(Player player, Scenery node, final Stall stall) { + super(player, node); + this.stall = stall; + } + + @Override + public void start() { + player.setAttribute("thieveDelay", GameWorld.getTicks()); + super.start(); + } + + @Override + public boolean checkRequirements() { + if (stall == null) { + return false; + } + if (player.inCombat()) { + player.getPacketDispatch().sendMessage("You cant steal from the market stall during combat!"); + return false; + } + if (player.getSkills().getLevel(Skills.THIEVING) < stall.getLevel()) { + player.getPacketDispatch().sendMessage("You need to be level " + stall.getLevel() + " to steal from the " + node.getName().toLowerCase() + "."); + return false; + } + if (player.getInventory().freeSlots() == 0) { + player.getPacketDispatch().sendMessage("You don't have enough inventory space."); + return false; + } + 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, 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; + } + return true; + } + + @Override + public boolean hasInactiveNode() { + if (player.getAttribute("thieveDelay", 0) <= GameWorld.getTicks()) { + return false; + } + return super.hasInactiveNode(); + } + + @Override + public void animate() { + } + + @Override + public boolean reward() { + if (ticks == 0) { + player.animate(ANIMATION); + player.getLocks().lockInteractions(2); + } + if (++ticks % 3 != 0) { + return false; + } + final boolean success = success(); + if (success) { + if (stall == Stall.SILK_STALL) { + player.getSavedData().getGlobalData().setSilkSteal(System.currentTimeMillis() + 1800000); + } + if (node.isActive()) { + SceneryBuilder.replace(node, node.transform(stall.getEmpty(node.getId())), stall.getDelay()); + } + final Item item = stall.getRandomLoot(); + player.getInventory().add(item); + player.getSkills().addExperience(Skills.THIEVING, stall.getExperience(), true); + if (item.getId() == 1987) { + 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) { + if(stall == Stall.CANDLES) { + return; + } + if (type == 0) { + player.getPacketDispatch().sendMessage("You attempt to steal some " + stall.msgItem + " from the " + stall.name().toLowerCase().replace('_', ' ')); + } + } + + /** + * Checks if the thief is successful. + * @return {@code True} if so. + */ + 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!"); + npc.getProperties().getCombatPulse().attack(player); + return false; + } + } + } + return true; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/skill/thieving/ThievableChestPlugin.java b/Server/src/main/content/global/skill/thieving/ThievableChestPlugin.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/thieving/ThievableChestPlugin.java rename to Server/src/main/content/global/skill/thieving/ThievableChestPlugin.java index 8f99f1235..f711dad6f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/ThievableChestPlugin.java +++ b/Server/src/main/content/global/skill/thieving/ThievableChestPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.thieving; +package content.global.skill.thieving; import core.cache.def.impl.SceneryDefinition; import core.game.node.entity.skill.Skills; @@ -10,11 +10,13 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; 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 new file mode 100644 index 000000000..ef687a850 --- /dev/null +++ b/Server/src/main/content/global/skill/thieving/ThievingListeners.kt @@ -0,0 +1,97 @@ +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.skill.Skills +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.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 { + + 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 + + if(player.inCombat()){ + player.sendMessage("You can't pickpocket while in combat.") + return@on true + } + + if(player.skills.getLevel(Skills.THIEVING) < pickpocketData.requiredLevel){ + player.sendMessage("You need a Thieving level of ${pickpocketData.requiredLevel} to do that.") + return@on true + } + + if(!pickpocketData.table.canRoll(player)){ + player.sendMessage("You don't have enough inventory space to do that.") + return@on true + } + + player.animator.animate(PICKPOCKET_ANIM) + 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) + + playHurtAudio(player, 20) + + stun(player, pickpocketData.stunTime) + + player.impactHandler.manualHit(node.asNpc(),RandomFunction.random(pickpocketData.stunDamageMin,pickpocketData.stunDamageMax),ImpactHandler.HitsplatType.NORMAL) + + node.asNpc().face(null) + } else { + playAudio(player, Sounds.PICK_2581) + player.lock(2) + lootTable.forEach { player.inventory.add(it) } + player.skills.addExperience(Skills.THIEVING,pickpocketData.experience) + } + + return@on true + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/thieving/ThievingOptionPlugin.java b/Server/src/main/content/global/skill/thieving/ThievingOptionPlugin.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/skill/thieving/ThievingOptionPlugin.java rename to Server/src/main/content/global/skill/thieving/ThievingOptionPlugin.java index 46eab307f..2d5103734 100644 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/ThievingOptionPlugin.java +++ b/Server/src/main/content/global/skill/thieving/ThievingOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.thieving; +package content.global.skill.thieving; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; @@ -20,6 +20,7 @@ public class ThievingOptionPlugin extends OptionHandler { public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.setOptionHandler("steal-from", this); SceneryDefinition.setOptionHandler("steal from", this); + SceneryDefinition.setOptionHandler("steal", this); return this; } @@ -28,6 +29,7 @@ public class ThievingOptionPlugin extends OptionHandler { switch (option) { case "steal-from": case "steal from": + case "steal": player.getPulseManager().run(new StallThiefPulse(player, (Scenery) node, Stall.forObject((Scenery) node))); player.getLocks().lockInteractions(6); break; diff --git a/Server/src/main/content/global/travel/EssenceTeleport.kt b/Server/src/main/content/global/travel/EssenceTeleport.kt new file mode 100644 index 000000000..cf79f80c4 --- /dev/null +++ b/Server/src/main/content/global/travel/EssenceTeleport.kt @@ -0,0 +1,222 @@ +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 +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.TeleportManager +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 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. + * @author 'Vexia + */ +object EssenceTeleport { + /** + * Array of all the possible `Location` locations. + */ + val LOCATIONS = arrayOf( + Location.create(2911, 4832, 0), + Location.create(2913, 4837, 0), + Location.create(2930, 4850, 0), + Location.create(2894, 4811, 0), + Location.create(2896, 4845, 0), + Location.create(2922, 4820, 0), + Location.create(2931, 4813, 0) + ) + + /** + * Represents constants used for gfx/animation + */ + private const val CURSE_PROJECTILE = 109 + private val ANIMATION = Animation(437) + private val GLOWING_HANDS_GFX = Graphics(108) + private val TELEPORT_GFX = Graphics(110,150) + + + /** + * Method used to teleport a player. + * @param npc the npc. + * @param player the player. + */ + @JvmStatic + fun teleport(npc: NPC, player: Player) { + npc.animate(ANIMATION) + npc.faceTemporary(player, 1) + npc.graphics(GLOWING_HANDS_GFX) + lock(player,4) + playGlobalAudio(player.location, Sounds.CURSE_ALL_125, 0, 1) + Projectile.create(npc, player, CURSE_PROJECTILE).send() + npc.sendChat("Senventior Disthine Molenko!") + GameWorld.Pulser.submit(object : Pulse(1) { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 0 -> player.graphics(TELEPORT_GFX) + 1 -> { + if (getStage(player) == 2 && player.inventory.contains(5519, 1)) { + val item = player.inventory[player.inventory.getSlot(Item(5519))] + if (item != null) { + if (item.charge == 1000) { + player.savedData.globalData.resetAbyss() + } + val wizard = Wizard.forNPC(npc.id) + if (!player.savedData.globalData.hasAbyssCharge(wizard.ordinal)) { + player.savedData.globalData.setAbyssCharge(wizard.ordinal) + item.charge = item.charge + 1 + if (item.charge == 1003) { + player.sendMessage("Your scrying orb has absorbed enough teleport information.") + player.inventory.remove(Item(5519)) + player.inventory.add(Item(5518)) + } + } + } + } + player.savedData.globalData.essenceTeleporter = npc.id + player.graphics(TELEPORT_GFX) + + val loc = LOCATIONS[RandomFunction.random(0, LOCATIONS.size)] + teleport(player,loc) + + player.dispatch( + TeleportEvent( + TeleportManager.TeleportType.TELE_OTHER, + TeleportMethod.NPC, + npc, + loc + ) + ) + } + + 2 -> { + unlock(player) + return true + } + } + return false + } + }) + } + + /** + * Method used to teleport back to the home. + * @param player the prayer. + */ + @JvmStatic + fun home(player: Player, node: Node) { + val wizard = Wizard.forNPC(player.savedData.globalData.essenceTeleporter) + Projectile.create(node.location, player.location, CURSE_PROJECTILE, 15, 10, 0, 10, 0, 2).send() + GameWorld.Pulser.submit(object : Pulse(1) { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 0 -> { + lock(player,2) + player.graphics(TELEPORT_GFX) + } + 1 -> { + teleport(player,wizard.location) + player.graphics(TELEPORT_GFX) + unlock(player) + return true + } + } + return false + } + }) + } + + /** + * Gets the stage. + * @return the stage. + */ + fun getStage(player: Player): Int { + return getVarp(player, 492) + } + + /** + * Method used to get a random location. + * @return the location. + */ + val location: Location + get() { + val count = RandomFunction.random(LOCATIONS.size) + return LOCATIONS[count] + } + + /** + * Represents the wizard npc who can teleport. + * @author 'Vexia + */ + enum class Wizard + /** + * Constructs a new `WizardTowerPlugin` `Object`. + * @param npc the npc. + * @param location the location. + */( + /** + * Represents the npc of this wizard. + */ + val npc: Int, + /** + * The mask. + */ + val mask: Int, + /** + * Represents the returining location. + */ + val location: Location) { + AUBURY(553, 0x2, Location(3253, 3401, 0)), + SEDRIDOR(300, 0x4, Location(3107, 9573, 0)), + DISTENTOR(462, 0x8, Location(2591, 3085, 0)), + CROMPERTY(2328, 0x12, Location.create(2682, 3323, 0)); + + /** + * Gets the npc. + * @return The npc. + */ + + /** + * Gets the mask. + * @return The mask. + */ + + /** + * Gets the location. + * @return The location. + */ + + companion object { + /** + * Method used to get a wizard by the npc. + * @param npc the npc. + * @return the wizard. + */ + fun forNPC(npc: Int): Wizard { + for (wizard in values()) { + if (npc == 844) { + return CROMPERTY + } + if (wizard.npc == npc) { + return wizard + } + } + return AUBURY + } + } + + } +} 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/java/core/game/content/dialogue/CaptainDalburDialogue.java b/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java similarity index 75% rename from Server/src/main/java/core/game/content/dialogue/CaptainDalburDialogue.java rename to Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java index 639f4c7a4..67f0cbaa0 100644 --- a/Server/src/main/java/core/game/content/dialogue/CaptainDalburDialogue.java +++ b/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java @@ -1,11 +1,16 @@ -package core.game.content.dialogue; +package content.global.travel.glider; +import content.data.Quests; import core.game.component.Component; -import core.game.content.global.travel.glider.Gliders; +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; +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 @@ -53,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 new file mode 100644 index 000000000..b3cbe634c --- /dev/null +++ b/Server/src/main/content/global/travel/glider/GliderPlugin.java @@ -0,0 +1,43 @@ +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 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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class GliderPlugin extends OptionHandler { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + NPCDefinition.forId(3809).getHandlers().put("option:glider", this); + NPCDefinition.forId(3810).getHandlers().put("option:glider", this); + NPCDefinition.forId(3811).getHandlers().put("option:glider", this); + NPCDefinition.forId(3812).getHandlers().put("option:glider", this); + NPCDefinition.forId(3813).getHandlers().put("option:glider", this); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + 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/java/core/game/content/global/travel/glider/GliderPulse.java b/Server/src/main/content/global/travel/glider/GliderPulse.java similarity index 91% rename from Server/src/main/java/core/game/content/global/travel/glider/GliderPulse.java rename to Server/src/main/content/global/travel/glider/GliderPulse.java index 35b6cdf80..06873a6cd 100644 --- a/Server/src/main/java/core/game/content/global/travel/glider/GliderPulse.java +++ b/Server/src/main/content/global/travel/glider/GliderPulse.java @@ -1,4 +1,4 @@ -package core.game.content.global.travel.glider; +package content.global.travel.glider; import core.game.component.Component; import core.game.node.entity.player.Player; @@ -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/java/core/game/content/global/travel/glider/Gliders.java b/Server/src/main/content/global/travel/glider/Gliders.java similarity index 94% rename from Server/src/main/java/core/game/content/global/travel/glider/Gliders.java rename to Server/src/main/content/global/travel/glider/Gliders.java index ae44c920a..99f9f780c 100644 --- a/Server/src/main/java/core/game/content/global/travel/glider/Gliders.java +++ b/Server/src/main/content/global/travel/glider/Gliders.java @@ -1,9 +1,11 @@ -package core.game.content.global.travel.glider; +package content.global.travel.glider; 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/java/core/game/content/dialogue/SeamanDialoguePlugin.java b/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/SeamanDialoguePlugin.java rename to Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java index 9a28584da..41b226d36 100644 --- a/Server/src/main/java/core/game/content/dialogue/SeamanDialoguePlugin.java +++ b/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java @@ -1,14 +1,18 @@ -package core.game.content.dialogue; +package content.global.travel.ship; import core.game.container.impl.EquipmentContainer; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import org.rs09.consts.Items; -import core.game.content.global.travel.ship.Ships; 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 static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * Represents the dialogue used to handle the sailing from and to karamja. * @@ -43,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)) { @@ -147,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 new file mode 100644 index 000000000..4058fc375 --- /dev/null +++ b/Server/src/main/content/global/travel/ship/ShipCharter.java @@ -0,0 +1,404 @@ +package content.global.travel.ship; + +import core.game.component.Component; +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.repository.Repository; +import core.net.packet.PacketRepository; +import core.net.packet.context.MinimapStateContext; +import core.net.packet.out.MinimapState; +import core.tools.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; + +/** + * Represents a class used to charter ships. + * @author 'Vexia + */ +public final class ShipCharter { + + /** + * Represents the component of charting a ship. + */ + private final static Component COMPONENT = new Component(95); + + /** + * Represents the ring of charos item. + */ + private final static Item RING_OF_CHAROS = new Item(6465); + + /** + * Constructs a new {@code ShipCharter} {@code Object}. + */ + public ShipCharter() { + /** + * empty. + */ + } + + /** + * Method used to open the ship chartering interface. + * @param player the player. + */ + public static void open(final Player player) { + final Destination current = Destination.getFromBase(player.getLocation()); + if (current != null) { + int[] hiddenComponents = getHiddenComponents(player, current); + for (int component : hiddenComponents) { + player.getPacketDispatch().sendInterfaceConfig(95, component, true); + } + player.getInterfaceManager().open(COMPONENT); + } + } + + /** + * Method used to handle a ship charter. + * @param player the player. + * @param button the button. + */ + public static void handle(final Player player, final int button) { + final Destination destination = Destination.forButton(button); + if (destination == null) { + return; + } + if (!destination.checkTravel(player)) { + return; + } + final int cost = getCost(player, destination); + player.getInterfaceManager().close(); + player.getDialogueInterpreter().open(4651, Repository.findNPC(4651), destination, cost); + } + + /** + * Method used to get the cost of the charter. + * @param player the player. + * @param destination the destination. + * @return the cost. + */ + public static int getCost(final Player player, Destination destination) { + int cost = destination.getCost(player, destination); + 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; + } + + /** + * Method used to get the hidden childs on the screen. + * @param player the player. + * @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 + }; + List childs = new ArrayList<>(20); + for (Destination destination : restrictions) { + childs.add(destination.getXChild()); + childs.add(destination.getNameChild()); + } + childs.add(base.getXChild()); + childs.add(base.getNameChild()); + if (base == Destination.KARAMJA) { + childs.add(Destination.PORT_SARIM.getXChild()); + childs.add(Destination.PORT_SARIM.getNameChild()); + } + if (base == Destination.PORT_SARIM) { + childs.add(Destination.KARAMJA.getXChild()); + childs.add(Destination.KARAMJA.getNameChild()); + } + int[] arrayChilds = new int[childs.size()]; + for (int i = 0; i < arrayChilds.length; i++) { + arrayChilds[i] = childs.get(i); + } + return arrayChilds; + } + + /** + * Method used to get the component of ship chartering. + * @return the component.. + */ + public static Component getComponent() { + return COMPONENT; + } + + /** + * Represents the destination to travel to. + * @author 'Vexia + */ + public enum Destination { + 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, Quests.PRIEST_IN_PERIL, "to go there."); + } + }, + CRANDOR(Location.create(2792, 3417, 1), 32, new int[] { 0, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, null, 10, 21) { + @Override + public boolean checkTravel(Player player) { + return requireQuest(player, Quests.DRAGON_SLAYER, "to go there."); + } + }, + 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); + if(destination == PORT_KHAZARD && hasGloves) return 15; + return super.getCost(player, destination); + } + }, + 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); + if(destination == KARAMJA && hasGloves) return 15; + return super.getCost(player, destination); + } + }, + 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); + if(destination == PORT_SARIM && hasGloves) return 15; + return super.getCost(player, destination); + } + }, + 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); + if(destination == BRIMHAVEN && hasGloves) return 15; + return super.getCost(player, destination); + } + }, + 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}. + * @param location the location. + * @param button the button. + * @param costs the money. + * @param base the base. + * @param components the children. + */ + Destination(Location location, int button, int[] costs, final Location base, int... components) { + this.location = location; + this.button = button; + this.costs = costs; + this.base = base; + this.childs = components; + } + + /** + * Represents the location of the destination. + */ + private final Location location; + + /** + * Represents the button of the destination. + */ + private final int button; + + /** + * Represents the costs from destination to destination. + */ + private final int[] costs; + + /** + * Represents the base location(how we find where we're at) + */ + private final Location base; + + /** + * Represents the childs on the screen. + */ + private final int[] childs; + + /** + * Gets the location. + * @return The location. + */ + public Location getLocation() { + return location; + } + + /** + * Gets the button. + * @return The button. + */ + public int getButton() { + return button; + } + + /** + * Gets the moneys. + * @return The moneys. + */ + public int[] getCosts() { + return costs; + } + + /** + * Gets the base. + * @return The base. + */ + public Location getBase() { + return base; + } + + /** + * Gets the childs. + * @return The childs. + */ + public int[] getComponents() { + return childs; + } + + /** + * Gets the x component. + * @return the component. + */ + public int getXChild() { + return childs[0]; + } + + /** + * Gets the name component. + * @return the component. + */ + public int getNameChild() { + return childs[1]; + } + + /** + * Gets the cost of chartering a ship. + * @param player the player. + * @return the cost. + */ + public int getCost(final Player player, final Destination destination) { + final Destination current = Destination.getFromBase(player.getLocation()); + if (current == null) { + return 0; + } + final Destination[] costTable = new Destination[] { BRIMHAVEN, CATHERBY, KARAMJA, MOS_LE_HARMLESS, PORT_KHAZARD, PORT_PHASMATYS, PORT_SARIM, SHIPYARD, PORT_TYRAS, OO_GLOG }; + int index = 0; + for (int i = 0; i < costTable.length; i++) { + if (costTable[i] == destination) { + index = i; + break; + } + } + return current.getCosts()[index]; + } + + /** + * Method used to get the destination from the base. + * @param location the location. + * @return the destination. + */ + public static Destination getFromBase(Location location) { + for (Destination destination : Destination.values()) { + if (destination.getBase() == null) { + continue; + } + if (destination.getBase().getDistance(location) < 30) { + return destination; + } + } + return null; + } + + /** + * Method used to get the destination for the button id. + * @param button the button. + * @return the destination. + */ + public static Destination forButton(final int button) { + for (Destination destination : values()) { + if (destination.getButton() == button) { + return destination; + } + } + return null; + } + + public boolean checkTravel(Player player) { + return true; + } + + /** + * Method used to sail the ship. + * @param player the player. + */ + 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; + + @Override + public boolean pulse() { + switch (count++) { + case 0: + player.getInterfaceManager().openOverlay(new Component(115)); + break; + case 2: + PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); + break; + case 3: + player.getProperties().setTeleportLocation(getLocation()); + break; + case 5: + player.unlock(); + player.getInterfaceManager().close(); + player.getInterfaceManager().closeOverlay(); + player.getInterfaceManager().restoreTabs(); + PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); + player.getPacketDispatch().sendMessage("You pay the fare and sail to " + StringUtils.formatDisplayName(name()) + "."); + // Charter a ship from the shipyard in the far east of Karamja + if (start.withinDistance(Location.create(3001,3032,0))) { + player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 17); + } + return true; + } + return false; + } + + }); + } + } + +} diff --git a/Server/src/main/java/core/game/interaction/inter/ShipCharterInterface.java b/Server/src/main/content/global/travel/ship/ShipCharterInterface.java similarity index 88% rename from Server/src/main/java/core/game/interaction/inter/ShipCharterInterface.java rename to Server/src/main/content/global/travel/ship/ShipCharterInterface.java index fd62d649b..8dcdb8102 100644 --- a/Server/src/main/java/core/game/interaction/inter/ShipCharterInterface.java +++ b/Server/src/main/content/global/travel/ship/ShipCharterInterface.java @@ -1,9 +1,9 @@ -package core.game.interaction.inter; +package content.global.travel.ship; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; -import core.game.content.global.travel.ship.ShipCharter; +import content.global.travel.ship.ShipCharter; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; diff --git a/Server/src/main/java/core/game/interaction/npc/ShipCharterNPCPlugin.java b/Server/src/main/content/global/travel/ship/ShipCharterNPCPlugin.java similarity index 90% rename from Server/src/main/java/core/game/interaction/npc/ShipCharterNPCPlugin.java rename to Server/src/main/content/global/travel/ship/ShipCharterNPCPlugin.java index c71ef1831..cf809e995 100644 --- a/Server/src/main/java/core/game/interaction/npc/ShipCharterNPCPlugin.java +++ b/Server/src/main/content/global/travel/ship/ShipCharterNPCPlugin.java @@ -1,7 +1,7 @@ -package core.game.interaction.npc; +package content.global.travel.ship; import core.cache.def.impl.NPCDefinition; -import core.game.content.global.travel.ship.ShipCharter; +import content.global.travel.ship.ShipCharter; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/global/travel/ship/ShipTravelPulse.java b/Server/src/main/content/global/travel/ship/ShipTravelPulse.java similarity index 93% rename from Server/src/main/java/core/game/content/global/travel/ship/ShipTravelPulse.java rename to Server/src/main/content/global/travel/ship/ShipTravelPulse.java index 473c3e88f..5f5808737 100644 --- a/Server/src/main/java/core/game/content/global/travel/ship/ShipTravelPulse.java +++ b/Server/src/main/content/global/travel/ship/ShipTravelPulse.java @@ -1,4 +1,4 @@ -package core.game.content.global.travel.ship; +package content.global.travel.ship; import core.game.component.Component; import core.game.node.entity.player.Player; @@ -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/java/core/game/content/global/travel/ship/Ships.java b/Server/src/main/content/global/travel/ship/Ships.java similarity index 98% rename from Server/src/main/java/core/game/content/global/travel/ship/Ships.java rename to Server/src/main/content/global/travel/ship/Ships.java index 1cc0d2598..c89adbb61 100644 --- a/Server/src/main/java/core/game/content/global/travel/ship/Ships.java +++ b/Server/src/main/content/global/travel/ship/Ships.java @@ -1,4 +1,4 @@ -package core.game.content.global.travel.ship; +package content.global.travel.ship; import core.game.node.entity.player.Player; import core.game.world.map.Location; diff --git a/Server/src/main/content/global/travel/ship/TraderCrewmemberDialogue.java b/Server/src/main/content/global/travel/ship/TraderCrewmemberDialogue.java new file mode 100644 index 000000000..405064ad8 --- /dev/null +++ b/Server/src/main/content/global/travel/ship/TraderCrewmemberDialogue.java @@ -0,0 +1,182 @@ +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; +import core.plugin.Initializable; +import core.tools.StringUtils; + +/** + * Handles the TraderCrewmemberDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class TraderCrewmemberDialogue extends DialoguePlugin { + + /** + * Represents the ship charter destination. + */ + private Destination destination; + + /** + * Represents the cost of charting the ship. + */ + private int cost; + + /** + * Constructs a new {@code TraderCrewmemberDialogue} {@code Object}. + */ + public TraderCrewmemberDialogue() { + } + + /** + * Constructs a new {@code TraderCrewmemberDialogue} {@code Object}. + * @param player the player. + */ + public TraderCrewmemberDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 4651, 4652, 4653, 4654, 4655, 4656, 4650 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Choose an option:", "Yes, who are you?", "Yes, I would like to charter a ship."); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING,"Yes, who are you?"); + stage = 100; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY,"Yes, I would like to charter a ship."); + stage = 2000; + break; + } + break; + case 100: + 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: + 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: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING,"So, where exactly can I go with your ships?"); + stage = 103; + break; + case 103: + 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: + 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: + 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: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING,"Would you like to take a look?"); + stage = 107; + break; + case 107: + options("Yes.", "No."); + stage = 108; + break; + case 108: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY,"Yes."); + stage = 1000; + break; + case 2: + end(); + break; + + } + break; + case 1000: + end(); + npc.openShop(player); + break; + case 2000: + interpreter.sendDialogues(npc, FacialExpression.HAPPY,"Certainly sir, where would you like to go?"); + stage = 2001; + break; + case 2001: + end(); + ShipCharter.open(player); + break; + case 3000: + options("Ok", "Choose again", "No"); + stage = 30001; + break; + case 30001: + switch (buttonId) { + case 1: + end(); + if (cost == 0) { + destination.sail(player); + break; + } + if (!player.getInventory().containsItem(new Item(995, cost))) { + end(); + return true; + } + if (!player.getInventory().remove(new Item(995, cost))) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't have the money for that."); + stage = 30002; + return true; + } + destination.sail(player); + break; + case 2: + end(); + ShipCharter.open(player); + break; + case 3: + end(); + break; + } + break; + case 30002: + end(); + break; + } + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new TraderCrewmemberDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + if (args.length > 1) { + destination = ((Destination) args[1]); + cost = (int) args[2]; + interpreter.sendDialogue("To sail to " + StringUtils.formatDisplayName(destination.name()) + " from here will cost you " + cost + " gold.", "Are you sure you want to pay that?"); + stage = 3000; + return true; + } + npc("Can I help you?"); + stage = 0; + return true; + } +} diff --git a/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt new file mode 100644 index 000000000..2f6e9a7ed --- /dev/null +++ b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt @@ -0,0 +1,151 @@ +package content.global.travel.trees + +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.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.NPCs +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,8355) + + override fun defineListeners() { + on(spiritTrees, IntType.SCENERY, "talk-to"){ player, _ -> + openDialogue(player, GnomeSpiritTreeDialogue(), NPC(NPCs.SPIRIT_TREE_3636)) + return@on true + } + on(spiritTrees, IntType.SCENERY, "teleport"){ player, _ -> + openDialogue(player, GnomeSpiritTreeTeleportDialogue(), NPC(NPCs.SPIRIT_TREE_3636)) + return@on true + } + } +} + +class GnomeSpiritTreeTeleportDialogue: DialogueFile() { + private val LOCATIONS = arrayOf( + Location(2542, 3170, 0), + Location(2461, 3444, 0), + Location(2556, 3259, 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, Quests.TREE_GNOME_VILLAGE)) { + sendDialogue(player, "The tree doesn't feel like talking.") + stage = END_DIALOGUE + return false + } + return true + } + + private fun sendTeleport(player: Player, location: Location) { + end() + Pulser.submit(object : Pulse(1, player) { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> { + player.animate(ANIMATIONS[0]) + player.graphics(GRAPHICS[0]) + } + 3 -> { teleport(player,location) } + 5 -> { + player.animate(ANIMATIONS[1]) + player.graphics(GRAPHICS[1]) + player.face(null) + if (player.location.withinDistance(Location.create(3184, 3508, 0))) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 5) + } + return true + } + } + count++ + return false + } + }) + } + + override fun handle(componentID: Int, buttonID: Int) { + if(!GnomeSpiritTreeTeleportDialogue().hasQuestCompleted(player!!)) { + 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?", + "Tree Gnome Village", + "Tree Gnome Stronghold", + "Battlefield of Khazard", + "Grand Exchange" + ).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]) + } + } + } + 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 + } + } + } + } +} + +class GnomeSpiritTreeDialogue: DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + if(!GnomeSpiritTreeTeleportDialogue().hasQuestCompleted(player!!)) { + stage = END_DIALOGUE + return + } + when (stage) { + 0 -> npcl("If you are a friend of the gnome people, you are a friend of mine, Do you wish to travel?").also{ stage++ } + 1 -> { openDialogue(player!!, GnomeSpiritTreeTeleportDialogue()) } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt b/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt new file mode 100644 index 000000000..901bec497 --- /dev/null +++ b/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt @@ -0,0 +1,74 @@ +package content.minigame.allfiredup + +import core.api.* +import core.game.node.entity.player.Player +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, Player Name + */ +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): AFUBeacon { + for (beacon in values()) { + if (beacon.location.equals(location)) return beacon + } + return AFUBeacon.RIVER_SALVE.also { log(this::class.java, Log.WARN, "Unhandled Beacon Location ${location.toString()}") } + } + + fun resetAllBeacons(player: Player){ + for(beacon in values()){ + setVarbit(player, beacon.varbit, 0) + } + } + } + + fun light(player: Player){ + setVarbit(player, varbit, 2, true) + } + + fun diminish(player: Player){ + setVarbit(player, varbit, 3, true) + } + + fun extinguish(player: Player){ + setVarbit(player, varbit, 0, true) + } + + fun lightGnomish(player: Player){ + setVarbit(player, varbit, 4, true) + } + + fun fillWithLogs(player: Player){ + setVarbit(player, varbit, 1, true) + } + + fun getState(player: Player): BeaconState { + return BeaconState.values()[getVarbit(player, varbit)] + } +} + +enum class BeaconState{ + EMPTY, + FILLED, + LIT, + DYING, + WARNING +} diff --git a/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt b/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt new file mode 100644 index 000000000..1522add6c --- /dev/null +++ b/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt @@ -0,0 +1,196 @@ +package content.minigame.allfiredup + +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.update.flag.context.Animation +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +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) +private val LIGHT_ANIM = Animation(7307) + +/** + * Handles interactions for beacons + * @author Ceikry, Player Name + */ +class AFUBeaconListeners : InteractionListener { + + override fun defineListeners() { + on(IntType.SCENERY,"add-logs","light"){ player, node -> + val beacon = AFUBeacon.forLocation(node.location) + val questComplete = player.questRepository.isComplete(Quests.ALL_FIRED_UP) + val questStage = player.questRepository.getStage(Quests.ALL_FIRED_UP) + + if ((beacon != AFUBeacon.RIVER_SALVE && beacon != AFUBeacon.RAG_AND_BONE && !questComplete) + || (beacon == AFUBeacon.RIVER_SALVE && questStage < 20 && !questComplete) + || (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)) { + BeaconState.EMPTY -> fillBeacon(player, beacon, questComplete) + + BeaconState.DYING -> restoreBeacon(player, beacon, questComplete) + + BeaconState.FILLED -> lightBeacon(player, beacon, questComplete) + + BeaconState.LIT, BeaconState.WARNING -> { + player.debug("INVALID BEACON STATE") + } + } + return@on true + } + } + + fun fillBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean){ + + when(beacon){ + 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 + } + } + + AFUBeacon.GWD -> { + if(!AFURepairClimbHandler.isRepaired(player, beacon)){ + player.dialogueInterpreter.sendDialogue("You must repair the windbreak before you","can light this beacon.") + return + } + } + + 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){ + player.dialogueInterpreter.sendDialogue("You need ${beacon.fmLevel} Firemaking to light this beacon.") + return + } + + val logs = getLogs(player,20) + if (logs.id != 0 && player.inventory.remove(logs)) { + 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) + } + } else { + player.dialogueInterpreter.sendDialogue("You need some logs to do this.") + } + } + + fun lightBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean) { + if(player.inventory.contains(Items.TINDERBOX_590,1)){ + 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: AFUBeacon, questComplete: Boolean){ + val logs = getLogs(player, 5) + if (logs.id != 0 && player.inventory.remove(logs)) { + 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.") + } + } + + fun getLogs(player: Player, amount: Int): Item{ + var logId = 0 + for (log in VALID_LOGS) if (player.inventory.getAmount(log) >= amount) {logId = log; break} + return Item(logId,amount) + } + + fun Player.hasFireRing(): Boolean{ + return inventory.containsItem(Item(Items.RING_OF_FIRE_13659)) || bank.containsItem(Item(Items.RING_OF_FIRE_13659)) || equipment.containsItem(Item(Items.RING_OF_FIRE_13659)) + } + + fun Player.hasFlameGloves(): Boolean{ + return inventory.containsItem(Item(Items.FLAME_GLOVES_13660)) || bank.containsItem(Item(Items.FLAME_GLOVES_13660)) || equipment.containsItem(Item(Items.FLAME_GLOVES_13660)) + } + + fun Player.hasInfernoAdze(): Boolean{ + return inventory.containsItem(Item(Items.INFERNO_ADZE_13661)) || bank.containsItem(Item(Items.INFERNO_ADZE_13661)) || equipment.containsItem(Item(Items.INFERNO_ADZE_13661)) + } + +} diff --git a/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt b/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt new file mode 100644 index 000000000..22e87dfdc --- /dev/null +++ b/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt @@ -0,0 +1,182 @@ +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 +import content.global.skill.construction.NailType +import core.game.node.item.Item +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 core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.api.* + +/** + * Handles repairing and climbing of the 3 beacon shortcuts needed to access them + * @author Ceikry + */ +class AFURepairClimbHandler : InteractionListener { + + val repairIDs = intArrayOf(38480,38470,38494) + val climbIDs = intArrayOf(38469,38471,38486,38481) + + override fun defineListeners() { + + on(repairIDs, IntType.SCENERY, "repair"){ player, _ -> + 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 -> + 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(getStatLevel(player, skill) < level){ + sendDialogue(player, "You need level $level ${Skills.SKILL_NAME[skill]} for this.") + return + } + + var requiresNeedle = false + + val requiredItems = when(rco){ + RepairClimbObject.DEATH_PLATEAU -> { + Item(Items.PLANK_960,2) + } + + RepairClimbObject.BURTHORPE -> { + Item(Items.IRON_BAR_2351,2) + } + + RepairClimbObject.GWD -> { + requiresNeedle = true + Item(Items.JUTE_FIBRE_5931,3) + } + else -> return + } + + if(requiresNeedle){ + 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(inInventory(player, Items.HAMMER_2347) && inInventory(player, requiredItems.id, requiredItems.amount)) { + val nails = NailType.get(player,4) + if(nails == null && rco == RepairClimbObject.DEATH_PLATEAU){ + sendDialogue(player, "You need 4 nails for this.") + return + } else if (rco == RepairClimbObject.DEATH_PLATEAU){ + removeItem(player, Item(nails.itemId,4)) + } + removeItem(player, requiredItems) + } else { + sendDialogue(player, "You need a hammer and ${requiredItems.amount} ${requiredItems.name.lowercase()}s for this.") + return + } + } + 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 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 + else return destinationDown + } + + fun getAnimation(player: Player): Animation { + if(getOtherLocation(player) == destinationDown) return Animation(1148) + else return Animation(740) + } + + fun getDirection(player: Player): Direction { + if(this == BURTHORPE){ + return Direction.EAST + } else return Direction.WEST + } + + fun isRepaired(player: Player): Boolean{ + return getVarbit(player, varbit) == 1 + } + } + + companion object { + 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 + } + } + +} 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/kotlin/rs09/game/content/activity/allfiredup/BeaconTenderDialogue.kt b/Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt similarity index 88% rename from Server/src/main/kotlin/rs09/game/content/activity/allfiredup/BeaconTenderDialogue.kt rename to Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt index 6844c24a1..d2cf48053 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/BeaconTenderDialogue.kt +++ b/Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt @@ -1,11 +1,13 @@ -package rs09.game.content.activity.allfiredup +package content.minigame.allfiredup -import core.game.content.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) @@ -20,6 +22,7 @@ class BeaconTenderDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun open(vararg args: Any?): Boolean { + npc = (args[0] as NPC).getShownNPC(player) index = getIndexOf((args[0] as NPC).originalId) if(index == AFUBeacon.GWD.ordinal && player.skills.getLevel(Skills.SUMMONING) < 81){ npc("Awwf uurrrhur","(You need 81 Summoning to communicate with Nanuq.)") @@ -27,11 +30,10 @@ class BeaconTenderDialogue(player: Player? = null) : DialoguePlugin(player) { return true } 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 watch the beacon.)") + 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 } @@ -39,10 +41,9 @@ class BeaconTenderDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { 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) == 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) : DialoguePlugin(player) { 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) : DialoguePlugin(player) { return intArrayOf(8067,8068,8069,8070,8071,8072,8073,8074,8075,8076) } -} \ No newline at end of file +} diff --git a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/KingRoaldAFUMiniDialogue.kt b/Server/src/main/content/minigame/allfiredup/KingRoaldAFUMiniDialogue.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/activity/allfiredup/KingRoaldAFUMiniDialogue.kt rename to Server/src/main/content/minigame/allfiredup/KingRoaldAFUMiniDialogue.kt index b958f3909..a7398de98 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/KingRoaldAFUMiniDialogue.kt +++ b/Server/src/main/content/minigame/allfiredup/KingRoaldAFUMiniDialogue.kt @@ -1,10 +1,10 @@ -package rs09.game.content.activity.allfiredup +package content.minigame.allfiredup import core.game.node.item.Item import org.rs09.consts.Items -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE class KingRoaldAFUMiniDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { 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 new file mode 100644 index 000000000..d4b31bc8e --- /dev/null +++ b/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt @@ -0,0 +1,75 @@ +package content.minigame.barbassault + +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import java.text.SimpleDateFormat +import java.time.temporal.ChronoUnit +import java.util.* + +@Initializable +class CaptainCainDialogue(player: Player? = null) : DialoguePlugin(player) { + companion object { + const val TORSO_PRICE = 4_500_000 + } + + val sdf = SimpleDateFormat("ddMMyyyy") + override fun newInstance(player: Player?): DialoguePlugin { + return CaptainCainDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + val start = sdf.parse("27112020").toInstant() + val now = Date().toInstant() + val days = ChronoUnit.DAYS.between(start,now) + when(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(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } + 2 -> playerl(FacialExpression.HALF_THINKING, "No, thanks.").also { stage = END_DIALOGUE } + } + + 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, TORSO_PRICE)) + playerl(FacialExpression.FRIENDLY, "Here you go!").also { stage = 20 } + else + playerl(FacialExpression.HALF_GUILTY, "Actually, I don't have that much.").also { stage = END_DIALOGUE } + + 2 -> playerl(FacialExpression.FRIENDLY, "On second thought, never mind.").also { stage = END_DIALOGUE } + } + + 20 -> { + 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 + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CAPTAIN_CAIN_5030) + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/barrows/BarrowBrother.java b/Server/src/main/content/minigame/barrows/BarrowBrother.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/barrows/BarrowBrother.java rename to Server/src/main/content/minigame/barrows/BarrowBrother.java index e46c2cda9..43caf987a 100644 --- a/Server/src/main/java/core/game/content/activity/barrows/BarrowBrother.java +++ b/Server/src/main/content/minigame/barrows/BarrowBrother.java @@ -1,4 +1,4 @@ -package core.game.content.activity.barrows; +package content.minigame.barrows; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; diff --git a/Server/src/main/java/core/game/content/activity/barrows/BarrowsActivityPlugin.java b/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java similarity index 81% rename from Server/src/main/java/core/game/content/activity/barrows/BarrowsActivityPlugin.java rename to Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java index be2820b4d..48b95f68f 100644 --- a/Server/src/main/java/core/game/content/activity/barrows/BarrowsActivityPlugin.java +++ b/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java @@ -1,9 +1,9 @@ -package core.game.content.activity.barrows; +package content.minigame.barrows; import core.game.component.Component; -import core.game.content.activity.ActivityPlugin; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.activity.ActivityPlugin; +import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -15,7 +15,7 @@ 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.link.ActivityData; -import core.game.node.entity.skill.summoning.familiar.Familiar; +import content.global.skill.summoning.familiar.Familiar; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; import core.game.world.map.Location; @@ -30,9 +30,12 @@ import core.net.packet.out.CameraViewPacket; import core.net.packet.out.MinimapState; import core.plugin.Initializable; import core.tools.RandomFunction; -import rs09.game.content.activity.barrows.RewardChest; -import rs09.game.world.GameWorld; -import rs09.plugin.PluginManager; +import core.game.world.GameWorld; +import core.plugin.ClassScanner; + +import java.util.stream.IntStream; + +import static core.api.ContentAPIKt.*; /** * Handles the barrows activity plugin. @@ -77,7 +80,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { /** * The activity handling pulse. */ - private static final Pulse PULSE = new Pulse(3) { + private static final Pulse PULSE = new Pulse(0) { @Override public boolean pulse() { boolean end = true; @@ -89,27 +92,30 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { p.getPacketDispatch().sendItemOnInterface(-1, 1, 24, index); continue; } - if (p.getLocation().getZ() == 0 && p.getAttribute("barrow:looted", false)) { + if (p.getLocation().getZ() == 0 && p.getAttribute("barrow:looted", false) && getWorldTicks() % 3 == 0) { if (RandomFunction.random(15) == 0) { p.getImpactHandler().manualHit(p, RandomFunction.random(5), HitsplatType.NORMAL); Graphics.send(Graphics.create(405), p.getLocation()); } } - if (p.getLocks().isLocked("barrow:drain") || RandomFunction.random(100) % 2 == 0) { - continue; - } int drain = 8; + + //if (p.getLocks().isLocked("barrow:drain") || RandomFunction.random(100) % 2 == 0) { + // continue; + //} for (boolean killed : p.getSavedData().getActivityData().getBarrowBrothers()) { if (killed) { drain += 1; } } - p.getSkills().decrementPrayerPoints(drain); - p.getLocks().lock("barrow:drain", (3 + RandomFunction.random(15)) * 3); - index = 1 + RandomFunction.random(6); - p.setAttribute("barrow:drain-index", index); - p.getPacketDispatch().sendItemZoomOnInterface(4761 + RandomFunction.random(12), 100, 24, index); - p.getPacketDispatch().sendAnimationInterface(9810, 24, index); + if(getWorldTicks() % 30 == 0){ + p.getSkills().decrementPrayerPoints(drain); + p.getLocks().lock("barrow:drain", (3 + RandomFunction.random(15)) * 3); + index = 1 + RandomFunction.random(6); + p.setAttribute("barrow:drain-index", index); + p.getPacketDispatch().sendItemZoomOnInterface(4761 + RandomFunction.random(12), 100, 24, index); + p.getPacketDispatch().sendAnimationInterface(9810, 24, index); + } } return end; } @@ -133,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); } } } @@ -145,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); @@ -208,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 @@ -254,17 +259,19 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { switch (object.getId()) { case 6714: case 6733: - int index = -1; - for (int i = 0; i < player.getSavedData().getActivityData().getBarrowBrothers().length; i++) { - if (!player.getSavedData().getActivityData().getBarrowBrothers()[i] && RandomFunction.random(15) == 0 && !player.getAttribute("brother:" + i, false)) { - index = i; - break; + 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)); } } - if (index > -1) { - BarrowsCrypt.getCrypt(index).spawnBrother(player, RegionManager.getTeleportLocation(target.getLocation(), 1)); - } - DoorActionHandler.handleAutowalkDoor(e, (Scenery) target); return true; case 6821: BarrowsCrypt.getCrypt(BarrowsCrypt.AHRIM).openSarcophagus((Player) e, object); @@ -286,11 +293,10 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { return true; case 6774: player.lock(1); - for (int i = 0; i < player.getSavedData().getActivityData().getBarrowBrothers().length; i++) { - if (!player.getSavedData().getActivityData().getBarrowBrothers()[i] && !player.getAttribute("brother:" + i, false)) { - BarrowsCrypt.getCrypt(i).spawnBrother(player, RegionManager.getTeleportLocation(target.getCenterLocation(), 4)); - } - } + 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)); + } player.setAttribute("barrow:opened_chest", true); sendConfiguration(player); return true; @@ -329,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 @@ -366,8 +372,8 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { @Override public void configure() { - PluginManager.definePlugin(new TunnelEntranceDialogue()); - PluginManager.definePlugin(BarrowsPuzzle.SHAPES); + ClassScanner.definePlugin(new TunnelEntranceDialogue()); + ClassScanner.definePlugin(BarrowsPuzzle.SHAPES); registerRegion(14231); BarrowsCrypt.init(); PULSE.stop(); diff --git a/Server/src/main/java/core/game/content/activity/barrows/BarrowsCrypt.java b/Server/src/main/content/minigame/barrows/BarrowsCrypt.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/barrows/BarrowsCrypt.java rename to Server/src/main/content/minigame/barrows/BarrowsCrypt.java index ce8c6da14..60c05798c 100644 --- a/Server/src/main/java/core/game/content/activity/barrows/BarrowsCrypt.java +++ b/Server/src/main/content/minigame/barrows/BarrowsCrypt.java @@ -1,7 +1,7 @@ -package core.game.content.activity.barrows; +package content.minigame.barrows; -import core.game.content.global.action.DigAction; -import core.game.content.global.action.DigSpadeHandler; +import core.game.global.action.DigAction; +import core.game.global.action.DigSpadeHandler; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; diff --git a/Server/src/main/java/core/game/content/activity/barrows/BarrowsPuzzle.java b/Server/src/main/content/minigame/barrows/BarrowsPuzzle.java similarity index 99% rename from Server/src/main/java/core/game/content/activity/barrows/BarrowsPuzzle.java rename to Server/src/main/content/minigame/barrows/BarrowsPuzzle.java index 0c158e6ee..73762e809 100644 --- a/Server/src/main/java/core/game/content/activity/barrows/BarrowsPuzzle.java +++ b/Server/src/main/content/minigame/barrows/BarrowsPuzzle.java @@ -1,4 +1,4 @@ -package core.game.content.activity.barrows; +package content.minigame.barrows; import java.util.ArrayList; import java.util.Arrays; diff --git a/Server/src/main/content/minigame/barrows/RewardChest.kt b/Server/src/main/content/minigame/barrows/RewardChest.kt new file mode 100644 index 000000000..ccb57c985 --- /dev/null +++ b/Server/src/main/content/minigame/barrows/RewardChest.kt @@ -0,0 +1,89 @@ +package content.minigame.barrows + +import core.api.announceIfRare +import core.game.component.Component +import core.game.container.access.InterfaceContainer +import content.data.BossKillCounter +import core.game.node.entity.player.Player +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Components +import org.rs09.consts.Items +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem + +/** + * The reward chest. + * @author Ceikry + * @author kermit + * @author Player Name + */ +object RewardChest { + 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. + * + * @param player The player. + */ + @JvmStatic + fun reward(player: Player) { + var barrowsRewardsIDs: MutableList = ArrayList() + for (i in 0..5) { + if (player.savedData.activityData.barrowBrothers[i]) { + barrowsRewardsIDs.addAll(BARROWS_DROP_IDS[i]) + } + } + 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) + player.interfaceManager.open(Component(Components.TRAIL_REWARD_364)) + 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/java/core/game/content/activity/barrows/TunnelEntranceDialogue.java b/Server/src/main/content/minigame/barrows/TunnelEntranceDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/activity/barrows/TunnelEntranceDialogue.java rename to Server/src/main/content/minigame/barrows/TunnelEntranceDialogue.java index 22f4af8a2..22331c47c 100644 --- a/Server/src/main/java/core/game/content/activity/barrows/TunnelEntranceDialogue.java +++ b/Server/src/main/content/minigame/barrows/TunnelEntranceDialogue.java @@ -1,12 +1,14 @@ -package core.game.content.activity.barrows; +package content.minigame.barrows; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; 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 new file mode 100644 index 000000000..5bbdedef1 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BlastFurnace.kt @@ -0,0 +1,260 @@ +package content.minigame.blastfurnace + +import content.global.skill.smithing.smelting.Bar +import core.api.* +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.zone.ZoneBorders +import core.tools.RandomFunction +import org.json.simple.JSONObject +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import kotlin.math.max + +class BlastFurnace : MapArea, PersistPlayer, TickListener { + override fun defineAreaBorders(): Array { + return arrayOf(bfArea) + } + + override fun savePlayer(player: Player, save: JSONObject) { + val state = playerStates[player.details.uid] + if (state != null) { + save["bf-state"] = state.toJson() + } + } + + 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) + } + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + playersInArea.add(entity) + val state = getPlayerState(entity) + for (ore in state.oresOnBelt) + ore.createNpc() + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + playersInArea.remove(entity) + val state = getPlayerState(entity) + for (ore in state.oresOnBelt) { + ore.npcInstance?.clear() + ore.npcInstance = null + } + } + } + + override fun tick() { + if (state.beltBroken || state.cogBroken) pedaler = null + if (state.potPipeBroken || state.pumpPipeBroken) pumper = null + state.tick(pumper != null, pedaler != null) + + if (playersInArea.size > 0) { + updateVisuals() + if (getWorldTicks() % 2 == 0) { + updatePedaler() + updatePumper() + } + processBars() + } + + //Reset each tick + pumper = null + pedaler = null + } + + private fun updatePumper() { + if (pumper != null) { + if (state.stoveTemp == 0) return + if (state.furnaceTemp == 100 && RandomFunction.roll(5)) { + impact(pumper!!, (0.2 * pumper!!.skills.maximumLifepoints).toInt()) + sendMessage(pumper!!, "A blast of hot air cooks you a bit.") + pumper!!.pulseManager.clear() + return + } + rewardXP(pumper!!, Skills.STRENGTH, 4.0) + } + } + + private fun updatePedaler() { + if (pedaler == null) return + var oresPedaled = false + for (state in playerStates.values) { + if (state.oresOnBelt.isEmpty()) continue + state.updateOres() + oresPedaled = true + } + if (oresPedaled) { + rewardXP(pedaler!!, Skills.AGILITY, 2.0) + pedaler!!.settings.runEnergy -= 2 + } + } + + private fun updateVisuals() { + sceneryController.updateStove(state.stoveTemp) + sceneryController.updateBreakable( + state.potPipeBroken, + state.pumpPipeBroken, + state.beltBroken, + state.cogBroken + ) + sceneryController.updateAnimations(pedaler != null, state.beltBroken, state.cogBroken) + } + + private fun processBars() { + if (state.furnaceTemp !in 51..66) return + var totalProcessed = 0 + for (player in playersInArea) { + if (getPlayerState(player).processOresIntoBars()) + totalProcessed++ + } + if (totalProcessed == 0) return + for (player in playersInArea) + rewardXP(player, Skills.SMITHING, totalProcessed.toDouble()) + } + + companion object { + val bfArea = ZoneBorders (1934, 4955, 1958, 4975) + val playersInArea = ArrayList() + val playerStates = HashMap() + val state = BlastState() + val sceneryController = BFSceneryController() + var pedaler: Player? = null + var pumper: Player? = null + + + fun insideBorders (player: Player) : Boolean { + return bfArea.insideBorder(player.location) + } + + fun placeAllOre (p: Player, id: Int = -1, accountForSkill: Boolean = false) { + val oreCounts = HashMap() + val oreContainer = getOreContainer(p) + val level = if (accountForSkill) getStatLevel(p, Skills.SMITHING) else 99 + + for (item in p.inventory.toArray()) { + if (item == null) continue + if (getNpcForOre(item.id) == -1) continue + if (id != -1 && item.id != id) continue + + val bar = getBarForOreId(item.id, oreContainer.coalAmount(), level)!! + if (bar.level > level) continue + + oreCounts[item.id] = (oreCounts[item.id] ?: 0) + item.amount + } + + for ((oreId, amount) in oreCounts) { + var maxAmt = oreContainer.getAvailableSpace(oreId, level) + + if (oreId == Items.COPPER_ORE_436 || oreId == Items.TIN_ORE_438) + maxAmt += (BlastConsts.ORE_LIMIT - getAmountOnBelt(p, oreId)) + + if (oreId == Items.COAL_453) + maxAmt -= getAmountOnBelt(p, oreId) + else + maxAmt -= getTotalOreOnBelt(p) + + maxAmt = maxAmt.coerceAtMost(amount).coerceAtLeast(0) + if (maxAmt == 0) continue + + if (removeItem(p, Item(oreId, maxAmt))) + 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 new file mode 100644 index 000000000..61986f2e2 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceInterfaceListener.kt @@ -0,0 +1,51 @@ +package content.minigame.blastfurnace + +import content.global.skill.smithing.smelting.Bar +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.system.task.Pulse +import org.rs09.consts.Components + +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 (isAll, bar) = getBarForButton(buttonID) + val state = BlastFurnace.getPlayerState(player) + state.claimBars(bar, if (isAll) state.container.getBarAmount(bar) else 1) + return@on true + } + } + + 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) + } + } +} diff --git a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt new file mode 100644 index 000000000..deaaae199 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt @@ -0,0 +1,315 @@ +package content.minigame.blastfurnace + +import content.region.misc.keldagrim.handlers.BlastFurnaceDoorDialogue +import core.api.* +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 + + +class BlastFurnaceListeners : InteractionListener { + 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 + + 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 + } + + on (BlastConsts.STAIR_EXIT_ID, IntType.SCENERY, "climb-up") { p, _ -> + teleport(p, BlastConsts.EXIT_LOC) + return@on true + } + + 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 + } + + 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)) { + 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 + } + + on (Items.SPADEFUL_OF_COKE_6448, ITEM, "empty") {p, n -> + if (removeItem(p, n)) addItem(p, Items.SPADE_952) + return@on true + } + + 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) + lock(player,2) + submitIndividualPulse(player, object : Pulse() { + override fun pulse(): Boolean { + 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 + } + } + }) + } else { + sendMessage(player,"You need some coke to do that!") + } + return@on true + } + + 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) + } + } + } + }) + return@on true + } + + 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) + } + } + 0 -> sendDialogue(p,"There's nothing to cool off!") + 3 -> sendDialogue(p,"These bars have already cooled off!") + } + return@onUseWith true + } + + 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.state.potPipeBroken = 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_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(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 + } + }) + return@on true + } + } +} 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/java/core/game/content/activity/bountyhunter/BHOptionHandler.java b/Server/src/main/content/minigame/bountyhunter/BHOptionHandler.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/bountyhunter/BHOptionHandler.java rename to Server/src/main/content/minigame/bountyhunter/BHOptionHandler.java index a87e47002..4af321f71 100644 --- a/Server/src/main/java/core/game/content/activity/bountyhunter/BHOptionHandler.java +++ b/Server/src/main/content/minigame/bountyhunter/BHOptionHandler.java @@ -1,13 +1,13 @@ -package core.game.content.activity.bountyhunter; +package content.minigame.bountyhunter; import core.cache.def.impl.SceneryDefinition; -import core.game.content.activity.ActivityManager; +import core.game.activity.ActivityManager; 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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; diff --git a/Server/src/main/java/core/game/content/activity/bountyhunter/BHScoreBoard.java b/Server/src/main/content/minigame/bountyhunter/BHScoreBoard.java similarity index 92% rename from Server/src/main/java/core/game/content/activity/bountyhunter/BHScoreBoard.java rename to Server/src/main/content/minigame/bountyhunter/BHScoreBoard.java index 5f3c1718b..c57c3efa5 100644 --- a/Server/src/main/java/core/game/content/activity/bountyhunter/BHScoreBoard.java +++ b/Server/src/main/content/minigame/bountyhunter/BHScoreBoard.java @@ -1,9 +1,7 @@ -package core.game.content.activity.bountyhunter; +package content.minigame.bountyhunter; import java.nio.ByteBuffer; -import core.cache.AriosStore; -import core.cache.StoreFile; import core.cache.misc.buffer.ByteBufferUtils; import core.game.component.Component; import core.game.node.entity.player.Player; @@ -53,8 +51,7 @@ public final class BHScoreBoard { * Initializes the score boards data. */ public static void init() { - StoreFile file = AriosStore.get("bh_scores"); - if (file == null) { // Indicates no cache exists yet. +/* if (true) { // Indicates no cache exists yet. return; } ByteBuffer buffer = file.data(); @@ -65,7 +62,7 @@ public final class BHScoreBoard { for (int i = 0; i < SIZE; i++) { ROGUES.scores[i] = buffer.getInt(); ROGUES.names[i] = ByteBufferUtils.getString(buffer); - } + }*/ } /** @@ -82,7 +79,7 @@ public final class BHScoreBoard { ByteBufferUtils.putString(ROGUES.names[i], buffer); } buffer.flip(); - AriosStore.setArchive("bh_scores", buffer); + //AriosStore.setArchive("bh_scores", buffer); } /** diff --git a/Server/src/main/java/core/game/content/activity/bountyhunter/BountyEntry.java b/Server/src/main/content/minigame/bountyhunter/BountyEntry.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/bountyhunter/BountyEntry.java rename to Server/src/main/content/minigame/bountyhunter/BountyEntry.java index e92d7e1f1..d7021abf9 100644 --- a/Server/src/main/java/core/game/content/activity/bountyhunter/BountyEntry.java +++ b/Server/src/main/content/minigame/bountyhunter/BountyEntry.java @@ -1,7 +1,7 @@ -package core.game.content.activity.bountyhunter; +package content.minigame.bountyhunter; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * Holds a player's bounty hunter data. diff --git a/Server/src/main/java/core/game/content/activity/bountyhunter/BountyHunterActivity.java b/Server/src/main/content/minigame/bountyhunter/BountyHunterActivity.java similarity index 97% rename from Server/src/main/java/core/game/content/activity/bountyhunter/BountyHunterActivity.java rename to Server/src/main/content/minigame/bountyhunter/BountyHunterActivity.java index 5bce8f92c..08b6c59e7 100644 --- a/Server/src/main/java/core/game/content/activity/bountyhunter/BountyHunterActivity.java +++ b/Server/src/main/content/minigame/bountyhunter/BountyHunterActivity.java @@ -1,13 +1,14 @@ -package core.game.content.activity.bountyhunter; +package content.minigame.bountyhunter; +import core.api.*; 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.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -18,7 +19,7 @@ import core.game.node.entity.player.link.prayer.PrayerType; import core.game.node.item.GroundItem; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.Point; import core.game.world.map.zone.ZoneBorders; @@ -28,11 +29,13 @@ import core.game.world.map.zone.impl.WildernessZone; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; 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 @@ -170,7 +173,7 @@ public final class BountyHunterActivity extends ActivityPlugin { break; } } - PluginManager.definePlugin(new ComponentPlugin() { + ClassScanner.definePlugin(new ComponentPlugin() { @Override public Plugin newInstance(Object arg) throws Throwable { ComponentDefinition.put(657, this); @@ -194,8 +197,8 @@ public final class BountyHunterActivity extends ActivityPlugin { }); BHScoreBoard.init(); - PluginManager.definePlugin(new BountyLocateSpell()); - PluginManager.definePlugin(new BHOptionHandler()); + ClassScanner.definePlugin(new BountyLocateSpell()); + ClassScanner.definePlugin(new BHOptionHandler()); ActivityManager.register(new BountyHunterActivity(CraterType.MID_LEVEL)); ActivityManager.register(new BountyHunterActivity(CraterType.HIGH_LEVEL)); } @@ -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/java/core/game/content/activity/bountyhunter/BountyLocateSpell.java b/Server/src/main/content/minigame/bountyhunter/BountyLocateSpell.java similarity index 84% rename from Server/src/main/java/core/game/content/activity/bountyhunter/BountyLocateSpell.java rename to Server/src/main/content/minigame/bountyhunter/BountyLocateSpell.java index 7007e1873..36693d2f0 100644 --- a/Server/src/main/java/core/game/content/activity/bountyhunter/BountyLocateSpell.java +++ b/Server/src/main/content/minigame/bountyhunter/BountyLocateSpell.java @@ -1,21 +1,23 @@ -package core.game.content.activity.bountyhunter; +package content.minigame.bountyhunter; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; +import core.game.node.entity.impl.PulseType; +import core.game.node.entity.combat.spell.MagicSpell; +import core.game.node.entity.combat.spell.Runes; import core.game.interaction.MovementPulse; import core.game.node.Node; import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; +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 rs09.game.world.GameWorld; +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 @@ -42,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(); @@ -74,7 +76,7 @@ public final class BountyLocateSpell extends MagicSpell { public boolean pulse() { return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } Location destination = RegionManager.getTeleportLocation(entry.getTarget().getLocation(), 5); @@ -95,4 +97,4 @@ public final class BountyLocateSpell extends MagicSpell { return this; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/content/activity/bountyhunter/CraterType.java b/Server/src/main/content/minigame/bountyhunter/CraterType.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/bountyhunter/CraterType.java rename to Server/src/main/content/minigame/bountyhunter/CraterType.java index 03944b8dc..fe2177957 100644 --- a/Server/src/main/java/core/game/content/activity/bountyhunter/CraterType.java +++ b/Server/src/main/content/minigame/bountyhunter/CraterType.java @@ -1,4 +1,4 @@ -package core.game.content.activity.bountyhunter; +package content.minigame.bountyhunter; import core.game.node.entity.player.Player; import core.game.world.map.Location; 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/java/core/game/content/activity/clanwars/CWChallengeOption.java b/Server/src/main/content/minigame/clanwars/CWChallengeOption.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/clanwars/CWChallengeOption.java rename to Server/src/main/content/minigame/clanwars/CWChallengeOption.java index e8329281c..2f472c84d 100644 --- a/Server/src/main/java/core/game/content/activity/clanwars/CWChallengeOption.java +++ b/Server/src/main/content/minigame/clanwars/CWChallengeOption.java @@ -1,6 +1,6 @@ -package core.game.content.activity.clanwars; +package content.minigame.clanwars; -import core.game.content.activity.ActivityManager; +import core.game.activity.ActivityManager; import core.game.interaction.Option; import core.game.interaction.OptionHandler; import core.game.node.Node; diff --git a/Server/src/main/java/core/game/content/activity/clanwars/ClanWarsActivityPlugin.java b/Server/src/main/content/minigame/clanwars/ClanWarsActivityPlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/clanwars/ClanWarsActivityPlugin.java rename to Server/src/main/content/minigame/clanwars/ClanWarsActivityPlugin.java index d154f5a48..8dc12e8a8 100644 --- a/Server/src/main/java/core/game/content/activity/clanwars/ClanWarsActivityPlugin.java +++ b/Server/src/main/content/minigame/clanwars/ClanWarsActivityPlugin.java @@ -1,13 +1,13 @@ -package core.game.content.activity.clanwars; +package content.minigame.clanwars; import java.util.ArrayList; import java.util.List; import core.game.component.Component; -import core.game.content.activity.ActivityPlugin; +import core.game.activity.ActivityPlugin; import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.pet.Pet; +import content.global.skill.summoning.familiar.Familiar; +import content.global.skill.summoning.pet.Pet; import core.game.interaction.Option; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -15,13 +15,12 @@ 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; import core.game.system.communication.ClanRepository; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.build.DynamicRegion; @@ -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 @@ -83,7 +84,6 @@ public final class ClanWarsActivityPlugin extends ActivityPlugin { @Override public boolean handle(Player player, Node node, String option) { - player.getPulseManager().clear("interaction:attack:" + node.hashCode()); player.getProperties().getCombatPulse().attack(node); return true; } @@ -240,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/java/core/game/content/activity/clanwars/ClanWarsChallengeRoom.java b/Server/src/main/content/minigame/clanwars/ClanWarsChallengeRoom.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/clanwars/ClanWarsChallengeRoom.java rename to Server/src/main/content/minigame/clanwars/ClanWarsChallengeRoom.java index 46a609aa5..7b3d9679b 100644 --- a/Server/src/main/java/core/game/content/activity/clanwars/ClanWarsChallengeRoom.java +++ b/Server/src/main/content/minigame/clanwars/ClanWarsChallengeRoom.java @@ -1,4 +1,4 @@ -package core.game.content.activity.clanwars; +package content.minigame.clanwars; import core.game.interaction.Option; import core.game.node.Node; @@ -12,7 +12,7 @@ import core.game.world.map.zone.ZoneRestriction; import core.game.world.map.zone.impl.WildernessZone; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles the clan wars challenge room. @@ -86,7 +86,7 @@ public final class ClanWarsChallengeRoom extends MapZone implements Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); - PluginManager.definePlugin(new CWChallengeOption()); + ClassScanner.definePlugin(new CWChallengeOption()); return this; } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/AfrahDialogue.kt b/Server/src/main/content/minigame/duel/AfrahDialogue.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/content/dialogue/AfrahDialogue.kt rename to Server/src/main/content/minigame/duel/AfrahDialogue.kt index 94751ca20..d480392f7 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AfrahDialogue.kt +++ b/Server/src/main/content/minigame/duel/AfrahDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.content.dialogue +package content.minigame.duel -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/java/core/game/content/activity/duel/ChallengeOptionPlugin.java b/Server/src/main/content/minigame/duel/ChallengeOptionPlugin.java similarity index 91% rename from Server/src/main/java/core/game/content/activity/duel/ChallengeOptionPlugin.java rename to Server/src/main/content/minigame/duel/ChallengeOptionPlugin.java index 80f1efd57..9f0e4dbbc 100644 --- a/Server/src/main/java/core/game/content/activity/duel/ChallengeOptionPlugin.java +++ b/Server/src/main/content/minigame/duel/ChallengeOptionPlugin.java @@ -1,9 +1,10 @@ -package core.game.content.activity.duel; +package content.minigame.duel; 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/java/core/game/content/activity/duel/DuelArea.java b/Server/src/main/content/minigame/duel/DuelArea.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/duel/DuelArea.java rename to Server/src/main/content/minigame/duel/DuelArea.java index c315f5d80..6b78d4952 100644 --- a/Server/src/main/java/core/game/content/activity/duel/DuelArea.java +++ b/Server/src/main/content/minigame/duel/DuelArea.java @@ -1,9 +1,9 @@ -package core.game.content.activity.duel; +package content.minigame.duel; import core.cache.def.impl.SceneryDefinition; import core.game.container.Container; import core.game.container.impl.EquipmentContainer; -import core.game.content.dialogue.DialogueAction; +import core.game.dialogue.DialogueAction; import core.game.interaction.Option; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -14,7 +14,7 @@ import core.game.node.entity.impl.PulseManager; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.HintIconManager; import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.entity.skill.summoning.familiar.Familiar; +import content.global.skill.summoning.familiar.Familiar; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; @@ -27,8 +27,8 @@ import core.game.world.map.zone.ZoneRestriction; import core.plugin.Plugin; import core.tools.RandomFunction; import core.tools.StringUtils; -import rs09.ServerConstants; -import rs09.game.world.GameWorld; +import core.ServerConstants; +import core.game.world.GameWorld; import java.util.ArrayList; import java.util.List; @@ -67,7 +67,6 @@ public class DuelArea extends MapZone { @Override public boolean handle(Player player, Node node, String option) { - player.getPulseManager().clear("interaction:attack:" + node.hashCode()); player.getProperties().getCombatPulse().attack(node); return true; } @@ -370,6 +369,7 @@ public class DuelArea extends MapZone { * @param p the player. */ private void leave(Player p) { + p.getProperties().setSafeZone(false); if (p.getAttribute("duel:ammo", null) != null) { List ammo = p.getAttribute("duel:ammo"); Container c = new Container(40); diff --git a/Server/src/main/java/core/game/content/activity/duel/DuelArenaActivity.java b/Server/src/main/content/minigame/duel/DuelArenaActivity.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/duel/DuelArenaActivity.java rename to Server/src/main/content/minigame/duel/DuelArenaActivity.java index 34fc12bb1..75f490704 100644 --- a/Server/src/main/java/core/game/content/activity/duel/DuelArenaActivity.java +++ b/Server/src/main/content/minigame/duel/DuelArenaActivity.java @@ -1,10 +1,10 @@ -package core.game.content.activity.duel; +package content.minigame.duel; import java.util.ArrayList; import java.util.List; import core.game.component.Component; -import core.game.content.activity.ActivityPlugin; +import core.game.activity.ActivityPlugin; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -16,9 +16,11 @@ import core.game.world.map.Location; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBuilder; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +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); } @@ -141,8 +143,8 @@ public final class DuelArenaActivity extends ActivityPlugin { } parseScoreboard(); register(new ZoneBorders(3325, 3201, 3396, 3280)); - PluginManager.definePlugin(new DuelArea.ForfeitTrapdoorPlugin()); - PluginManager.definePlugins(new DuelSession(null, null, false), new DuelComponentPlugin(), new ChallengeOptionPlugin()); + ClassScanner.definePlugin(new DuelArea.ForfeitTrapdoorPlugin()); + ClassScanner.definePlugins(new DuelSession(null, null, false), new DuelComponentPlugin(), new ChallengeOptionPlugin()); } /** diff --git a/Server/src/main/java/core/game/content/activity/duel/DuelComponentPlugin.java b/Server/src/main/content/minigame/duel/DuelComponentPlugin.java similarity index 91% rename from Server/src/main/java/core/game/content/activity/duel/DuelComponentPlugin.java rename to Server/src/main/content/minigame/duel/DuelComponentPlugin.java index 09deaee25..38dc31f54 100644 --- a/Server/src/main/java/core/game/content/activity/duel/DuelComponentPlugin.java +++ b/Server/src/main/content/minigame/duel/DuelComponentPlugin.java @@ -1,12 +1,14 @@ -package core.game.content.activity.duel; +package content.minigame.duel; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +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/java/core/game/content/activity/duel/DuelReqModule.java b/Server/src/main/content/minigame/duel/DuelReqModule.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/duel/DuelReqModule.java rename to Server/src/main/content/minigame/duel/DuelReqModule.java index 75f3bee27..9c2a86540 100644 --- a/Server/src/main/java/core/game/content/activity/duel/DuelReqModule.java +++ b/Server/src/main/content/minigame/duel/DuelReqModule.java @@ -1,4 +1,4 @@ -package core.game.content.activity.duel; +package content.minigame.duel; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.request.RequestModule; diff --git a/Server/src/main/java/core/game/content/activity/duel/DuelRule.java b/Server/src/main/content/minigame/duel/DuelRule.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/duel/DuelRule.java rename to Server/src/main/content/minigame/duel/DuelRule.java index 88de3f9d7..62a737b2a 100644 --- a/Server/src/main/java/core/game/content/activity/duel/DuelRule.java +++ b/Server/src/main/content/minigame/duel/DuelRule.java @@ -1,4 +1,4 @@ -package core.game.content.activity.duel; +package content.minigame.duel; import core.game.container.impl.EquipmentContainer; diff --git a/Server/src/main/java/core/game/content/activity/duel/DuelSession.java b/Server/src/main/content/minigame/duel/DuelSession.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/duel/DuelSession.java rename to Server/src/main/content/minigame/duel/DuelSession.java index 1897ad460..51b4a6d9b 100644 --- a/Server/src/main/java/core/game/content/activity/duel/DuelSession.java +++ b/Server/src/main/content/minigame/duel/DuelSession.java @@ -1,6 +1,6 @@ -package core.game.content.activity.duel; +package content.minigame.duel; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.component.CloseEvent; import core.game.component.Component; import core.game.component.ComponentDefinition; @@ -10,16 +10,15 @@ import core.game.container.ContainerType; import core.game.container.access.InterfaceContainer; 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.player.link.RunScript; -import core.game.node.entity.state.EntityState; import core.game.node.item.Item; -import core.game.system.monitor.PlayerMonitor; import core.plugin.Plugin; import core.tools.RandomFunction; import kotlin.Unit; -import rs09.game.content.global.action.EquipHandler; -import rs09.game.system.config.ItemConfigParser; +import core.game.global.action.EquipHandler; +import core.game.node.entity.player.info.PlayerMonitor; +import core.game.system.config.ItemConfigParser; import java.text.DecimalFormat; @@ -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(); @@ -312,7 +311,7 @@ public final class DuelSession extends ComponentPlugin { log = log.substring(0, log.length() - 1); } log += "}"; - player.getMonitor().log(log, PlayerMonitor.DUEL_LOG); + PlayerMonitor.log(player, LogType.DUEL_INFO, log); } else { player.removeExtension(DuelSession.class); } @@ -363,8 +362,8 @@ public final class DuelSession extends ComponentPlugin { session.setAcceptState(4); session.player.getInterfaceManager().closeSingleTab(); session.other.getInterfaceManager().closeSingleTab(); - session.player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 11, 12); - session.other.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 11, 12); + session.player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 7, 11, 12); + session.other.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 7, 11, 12); StringBuilder before = new StringBuilder(); StringBuilder during = new StringBuilder(); if (hasEquipmentRules()) { @@ -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); } /** @@ -569,7 +568,7 @@ public final class DuelSession extends ComponentPlugin { amount = c.getAmount(c.get(slot)); break; case 234: - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { c.withdraw(slot, (int) value); return Unit.INSTANCE; }); @@ -686,7 +685,7 @@ public final class DuelSession extends ComponentPlugin { c1.offer(slot, player.getInventory().getAmount(player.getInventory().get(slot).getId())); break; case 234: - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { c1.offer(slot, (int) value); return Unit.INSTANCE; }); @@ -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/java/core/game/content/dialogue/MubarizDialogue.java b/Server/src/main/content/minigame/duel/MubarizDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/MubarizDialogue.java rename to Server/src/main/content/minigame/duel/MubarizDialogue.java index 525cd6532..4350ab449 100644 --- a/Server/src/main/java/core/game/content/dialogue/MubarizDialogue.java +++ b/Server/src/main/content/minigame/duel/MubarizDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.minigame.duel; +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; diff --git a/Server/src/main/java/core/game/content/activity/duel/StakeContainer.java b/Server/src/main/content/minigame/duel/StakeContainer.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/duel/StakeContainer.java rename to Server/src/main/content/minigame/duel/StakeContainer.java index 815058ae0..c505478cd 100644 --- a/Server/src/main/java/core/game/content/activity/duel/StakeContainer.java +++ b/Server/src/main/content/minigame/duel/StakeContainer.java @@ -1,4 +1,4 @@ -package core.game.content.activity.duel; +package content.minigame.duel; import core.game.component.Component; import core.game.container.Container; @@ -79,7 +79,7 @@ public class StakeContainer extends Container { public void open() { player.getInterfaceManager().openSingleTab(OVERLAY); player.getPacketDispatch().sendRunScript(150, "IviiiIssssssss", INVY_PARAMS); - player.getPacketDispatch().sendAccessMask(1278, 0, 336, 0, 27); + player.getPacketDispatch().sendIfaceSettings(1278, 0, 336, 0, 27); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, 2, 93, player.getInventory(), false)); } diff --git a/Server/src/main/java/core/game/content/dialogue/ZahwaDialogue.java b/Server/src/main/content/minigame/duel/ZahwaDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/ZahwaDialogue.java rename to Server/src/main/content/minigame/duel/ZahwaDialogue.java index 3bffea1aa..baf7be753 100644 --- a/Server/src/main/java/core/game/content/dialogue/ZahwaDialogue.java +++ b/Server/src/main/content/minigame/duel/ZahwaDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.minigame.duel; +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; diff --git a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerActivity.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerActivity.kt similarity index 80% rename from Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerActivity.kt rename to Server/src/main/content/minigame/fishingtrawler/FishingTrawlerActivity.kt index c2216e843..1522316c6 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerActivity.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerActivity.kt @@ -1,29 +1,32 @@ -package rs09.game.content.activity.fishingtrawler +package content.minigame.fishingtrawler +import core.api.MapArea import core.game.node.entity.player.Player import core.game.system.task.Pulse -import rs09.game.world.GameWorld +import core.game.world.GameWorld import core.game.world.map.Location import core.game.world.map.build.DynamicRegion import core.game.world.map.zone.ZoneRestriction import core.plugin.Initializable -import rs09.tools.ticksToSeconds -import core.game.content.activity.ActivityManager -import core.game.content.activity.ActivityPlugin -import rs09.tools.stringtools.colorize +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 /** * 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/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerInteractionHandler.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt similarity index 76% rename from Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerInteractionHandler.kt rename to Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt index 11362f06e..d619d6539 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerInteractionHandler.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt @@ -1,28 +1,26 @@ -package rs09.game.content.activity.fishingtrawler +package content.minigame.fishingtrawler -import core.game.content.activity.ActivityManager -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 import core.game.world.update.flag.context.Animation import core.plugin.Initializable import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener -import rs09.game.node.entity.player.info.stats.FISHING_TRAWLER_LEAKS_PATCHED -import rs09.game.node.entity.player.info.stats.STATS_BASE -import rs09.tools.stringtools.colorize +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 kotlin.math.ceil /** * Option handler for fishing trawler * @author Ceikry */ -class FishingTrawlerInteractionHandler : InteractionListener() { +class FishingTrawlerInteractionHandler : InteractionListener { val ENTRANCE_PLANK = 2178 val EXIT_PLANK = 2179 val HOLE = 2167 @@ -34,7 +32,7 @@ class FishingTrawlerInteractionHandler : InteractionListener() { override fun defineListeners() { - on(ENTRANCE_PLANK,SCENERY,"cross"){ player, _ -> + on(ENTRANCE_PLANK, IntType.SCENERY, "cross"){ player, _ -> if(player.skills.getLevel(Skills.FISHING) < 15){ player.dialogueInterpreter.sendDialogue("You need to be at least level 15 fishing to play.") return@on true @@ -44,16 +42,15 @@ class FishingTrawlerInteractionHandler : InteractionListener() { return@on true } - on(EXIT_PLANK,SCENERY,"cross"){ player, _ -> + on(EXIT_PLANK, IntType.SCENERY, "cross"){ player, _ -> player.properties.teleportLocation = Location.create(2676, 3170, 0) (ActivityManager.getActivity("fishing trawler") as FishingTrawlerActivity).removePlayer(player) val session: FishingTrawlerSession? = player.getAttribute("ft-session",null) session?.players?.remove(player) - player.logoutPlugins.clear() return@on true } - on(HOLE,SCENERY,"fill"){ player, node -> + on(HOLE, IntType.SCENERY, "fill"){ player, node -> val session: FishingTrawlerSession? = player.getAttribute("ft-session",null) session ?: return@on false player.lock() @@ -71,31 +68,32 @@ class FishingTrawlerInteractionHandler : InteractionListener() { return@on true } - on(NETIDs,SCENERY,"inspect"){ player, _ -> + on(NETIDs, IntType.SCENERY, "inspect"){ player, _ -> player.dialogueInterpreter.open(18237583) return@on true } - on(REWARD_NET,SCENERY,"inspect"){ player, _ -> - val session: FishingTrawlerSession? = player.getAttribute("ft-session",null) - if(session == null || session.boatSank){ - player.dialogueInterpreter.sendDialogues(player, FacialExpression.GUILTY,"I'd better not go stealing other people's fish.") + on(REWARD_NET, IntType.SCENERY, "inspect"){ player, _ -> + 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 } player.dialogueInterpreter.open(18237582) return@on true } - on(BARREL_IDS,SCENERY,"climb-on"){ player, _ -> + on(BARREL_IDS, IntType.SCENERY, "climb-on"){ player, _ -> player.properties.teleportLocation = Location.create(2672, 3222, 0) 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.logoutPlugins.clear() player.appearance.setDefaultAnimations() player.appearance.sync() + clearLogoutListener(player, "ft-logout") + player.locks.unlockTeleport() return@on true } - on(FULL_BAIL_BUCKET,ITEM,"empty"){player,node -> + on(FULL_BAIL_BUCKET, IntType.ITEM, "empty"){ player, node -> player.lock() player.pulseManager.run( object : Pulse(){ @@ -117,7 +115,7 @@ class FishingTrawlerInteractionHandler : InteractionListener() { return@on true } - on(BAILING_BUCKET,ITEM,"bail-with") { player, node -> + on(BAILING_BUCKET, IntType.ITEM, "bail-with") { player, node -> val session: FishingTrawlerSession? = player.getAttribute("ft-session",null) session ?: return@on false if(!session.isActive){ @@ -156,34 +154,29 @@ class FishingTrawlerInteractionHandler : InteractionListener() { } @Initializable -class NetLootDialogue(player: Player? = null): DialoguePlugin(player){ +class NetLootDialogue(player: Player? = null): core.game.dialogue.DialoguePlugin(player){ var session: FishingTrawlerSession? = null var rolls = 0 - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return NetLootDialogue(player) } 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 } override fun handle(interfaceId: Int, buttonId: Int): Boolean { + val level = player.skills.getLevel(Skills.FISHING) when(buttonId){ - 1 -> TrawlerLoot.getLoot(rolls,true).forEach { - if(!player.bank.add(it)) GroundItemManager.create(it,player) - } - 2 -> TrawlerLoot.getLoot(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 } @@ -195,9 +188,9 @@ class NetLootDialogue(player: Player? = null): DialoguePlugin(player){ } @Initializable -class NetRepairDialogue(player: Player? = null) : DialoguePlugin(player){ +class NetRepairDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ var session: FishingTrawlerSession? = null - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return NetRepairDialogue(player) } @@ -235,4 +228,4 @@ class NetRepairDialogue(player: Player? = null) : DialoguePlugin(player){ return intArrayOf(18237583) } -} \ No newline at end of file +} diff --git a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerOverlay.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerOverlay.kt similarity index 84% rename from Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerOverlay.kt rename to Server/src/main/content/minigame/fishingtrawler/FishingTrawlerOverlay.kt index 542f5c3b3..9b9ad7e21 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerOverlay.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerOverlay.kt @@ -1,6 +1,7 @@ -package rs09.game.content.activity.fishingtrawler +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("${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/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerRewardInterface.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerRewardInterface.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerRewardInterface.kt rename to Server/src/main/content/minigame/fishingtrawler/FishingTrawlerRewardInterface.kt index 7991057d5..5d606af77 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerRewardInterface.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerRewardInterface.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.fishingtrawler +package content.minigame.fishingtrawler import core.game.component.Component import core.game.component.ComponentDefinition diff --git a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerSession.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt similarity index 75% rename from Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerSession.kt rename to Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt index f0fb716dd..3cabb8d72 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/FishingTrawlerSession.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt @@ -1,27 +1,32 @@ -package rs09.game.content.activity.fishingtrawler +package content.minigame.fishingtrawler +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 rs09.game.world.GameWorld +import core.game.world.GameWorld import core.game.world.map.Location import core.game.world.map.build.DynamicRegion +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction import core.game.world.update.flag.context.Animation -import core.plugin.Plugin import core.tools.* import org.rs09.consts.Components import org.rs09.consts.Items -import rs09.game.node.entity.player.info.stats.FISHING_TRAWLER_GAMES_WON -import rs09.game.node.entity.player.info.stats.FISHING_TRAWLER_SHIPS_SANK -import rs09.game.node.entity.player.info.stats.STATS_BASE -import rs09.tools.secondsToTicks -import rs09.tools.ticksToSeconds +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.secondsToTicks +import core.tools.ticksToSeconds import java.util.concurrent.TimeUnit +import kotlin.math.ceil import kotlin.random.Random @@ -36,12 +41,15 @@ private const val HOLE_NORTH_Y = 26 private const val HOLE_SOUTH_Y = 23 private const val LEAKING_ID = 2167 private const val PATCHED_ID = 2168 -class FishingTrawlerSession(var region: DynamicRegion, val activity: FishingTrawlerActivity) { + +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 var fishAmount = 0 var timeLeft = secondsToTicks(600) - var base = region.baseLocation + lateinit var region: DynamicRegion + lateinit var base: Location var isActive = false var boatSank = false var hole_locations = ArrayList() @@ -54,6 +62,11 @@ class FishingTrawlerSession(var region: DynamicRegion, val activity: FishingTraw var inactiveTicks = 0 fun start(pl: ArrayList){ + if(RandomFunction.roll(2)) { + region.setMusicId(38) + } else { + region.setMusicId(51) + } this.players.addAll(pl) isActive = true initHoles() @@ -66,9 +79,14 @@ class FishingTrawlerSession(var region: DynamicRegion, val activity: FishingTraw updateOverlay(player) player.properties.teleportLocation = base.transform(36,24,0) player.setAttribute("ft-session",this) - player.logoutPlugins.add(TrawlerLogoutPlugin()) - 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)) } fun swapBoatType(fromRegion: Int){ @@ -90,7 +108,6 @@ class FishingTrawlerSession(var region: DynamicRegion, val activity: FishingTraw 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 } @@ -121,6 +138,10 @@ class FishingTrawlerSession(var region: DynamicRegion, val activity: FishingTraw session.boatSank = true 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){ @@ -132,13 +153,20 @@ class FishingTrawlerSession(var region: DynamicRegion, val activity: FishingTraw for(player in session.players){ player.interfaceManager.closeOverlay() player.properties.teleportLocation = Location.create(2666, 3162, 0) - player.logoutPlugins.clear() 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 @@ -260,21 +288,24 @@ class FishingTrawlerSession(var region: DynamicRegion, val activity: FishingTraw } fun updateOverlay(player: Player){ - FishingTrawlerOverlay.sendUpdate(player,((waterAmount / 500.0) * 100).toInt(),netRipped,fishAmount,TimeUnit.SECONDS.toMinutes(ticksToSeconds(timeLeft).toLong()).toInt() + 1) + FishingTrawlerOverlay.sendUpdate(player, ((waterAmount / 500.0) * 100).toInt(), netRipped, fishAmount, TimeUnit.SECONDS.toMinutes(ticksToSeconds(timeLeft).toLong()).toInt() + 1) + } + + override fun defineAreaBorders(): Array { + return arrayOf() + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.CANNON, ZoneRestriction.FIRES, ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.TELEPORT) + } + + override fun areaEnter(entity: Entity) { + super.areaEnter(entity) + log(this::class.java, Log.FINE, "ENTERED FTZ") + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + super.areaLeave(entity, logout) + log(this::class.java, Log.FINE, "EXITED FTZ") } } - -class TrawlerLogoutPlugin : Plugin{ - override fun newInstance(arg: Player?): Plugin { - arg?.location = Location.create(2667, 3161, 0) - val session: FishingTrawlerSession? = arg?.getAttribute("ft-session",null) - session ?: return this - session.players.remove(arg) - 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/content/minigame/fishingtrawler/TrawlerLoot.kt b/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt new file mode 100644 index 000000000..87ed06dbe --- /dev/null +++ b/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt @@ -0,0 +1,112 @@ +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, RiL + */ +object TrawlerLoot { + 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) + + private fun rollTrawlerFish(fishLevel: Int): Item { + while(true) { + for(f in trawlerFish) { + if(f.level > fishLevel) { + continue + } + val lo = 0.6133 + val hi = 0.7852 + //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 Item(f.id) + } + } + } + } + + @JvmStatic + fun getLoot(fishLevel: Int, rolls: Int, skipJunk: Boolean): ArrayList{ + val loot = ArrayList() + for(i in 0 until rolls){ + val item = RandomFunction.rollWeightedChanceTable(listOf(*lootTable)) + if(item.id == 0) { + loot.add(rollTrawlerFish(fishLevel)) + } else if (!skipJunk || item.id !in junkItems) { + loot.add(item) + } + } + return loot + } + + /** + * 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), + WeightedChanceItem(Items.BROKEN_STAFF_689,1,70), + WeightedChanceItem(Items.BUTTONS_688,1,80), + WeightedChanceItem(Items.DAMAGED_ARMOUR_697,1,70), + WeightedChanceItem(Items.OLD_BOOT_685,1,60), + WeightedChanceItem(Items.OYSTER_407,1,50), + WeightedChanceItem(Items.EMPTY_POT_1931,1,50), + WeightedChanceItem(Items.RUSTY_SWORD_686,1,50), + //Inauthentic rewards + WeightedChanceItem(Items.LOOP_HALF_OF_A_KEY_987,1,7), + WeightedChanceItem(Items.TOOTH_HALF_OF_A_KEY_985,1,7), + WeightedChanceItem(Items.CASKET_405,1,60), + WeightedChanceItem(Items.PIRATES_HAT_2651,1,2), + WeightedChanceItem(Items.LUCKY_CUTLASS_7140,1,2) + ) +} diff --git a/Server/src/main/java/core/game/content/activity/fog/FOGActivityPlugin.java b/Server/src/main/content/minigame/fog/FOGActivityPlugin.java similarity index 86% rename from Server/src/main/java/core/game/content/activity/fog/FOGActivityPlugin.java rename to Server/src/main/content/minigame/fog/FOGActivityPlugin.java index 6bbbb4ed9..05fb53d6e 100644 --- a/Server/src/main/java/core/game/content/activity/fog/FOGActivityPlugin.java +++ b/Server/src/main/content/minigame/fog/FOGActivityPlugin.java @@ -1,14 +1,14 @@ -package core.game.content.activity.fog; +package content.minigame.fog; import core.cache.def.impl.SceneryDefinition; -import core.game.content.activity.ActivityPlugin; +import core.game.activity.ActivityPlugin; 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.game.world.map.zone.ZoneBorders; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the fist of guthix activity. @@ -50,9 +50,9 @@ public class FOGActivityPlugin extends ActivityPlugin { @Override public void configure() { - PluginManager.definePlugin(new FOGLobbyZone()); - PluginManager.definePlugin(new FOGWaitingZone()); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new FOGLobbyZone()); + ClassScanner.definePlugin(new FOGWaitingZone()); + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.forId(30204).getHandlers().put("option:enter", this); diff --git a/Server/src/main/java/core/game/content/activity/fog/FOGLobbyZone.java b/Server/src/main/content/minigame/fog/FOGLobbyZone.java similarity index 97% rename from Server/src/main/java/core/game/content/activity/fog/FOGLobbyZone.java rename to Server/src/main/content/minigame/fog/FOGLobbyZone.java index 9b8cbf1f9..e34a02186 100644 --- a/Server/src/main/java/core/game/content/activity/fog/FOGLobbyZone.java +++ b/Server/src/main/content/minigame/fog/FOGLobbyZone.java @@ -1,4 +1,4 @@ -package core.game.content.activity.fog; +package content.minigame.fog; import core.game.component.Component; import core.game.interaction.Option; diff --git a/Server/src/main/java/core/game/content/activity/fog/FOGPlayer.java b/Server/src/main/content/minigame/fog/FOGPlayer.java similarity index 97% rename from Server/src/main/java/core/game/content/activity/fog/FOGPlayer.java rename to Server/src/main/content/minigame/fog/FOGPlayer.java index 85a0b51df..ad015a195 100644 --- a/Server/src/main/java/core/game/content/activity/fog/FOGPlayer.java +++ b/Server/src/main/content/minigame/fog/FOGPlayer.java @@ -1,4 +1,4 @@ -package core.game.content.activity.fog; +package content.minigame.fog; import core.game.node.entity.player.Player; diff --git a/Server/src/main/kotlin/rs09/game/interaction/FOGRewardsInterface.kt b/Server/src/main/content/minigame/fog/FOGRewardsInterface.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/interaction/FOGRewardsInterface.kt rename to Server/src/main/content/minigame/fog/FOGRewardsInterface.kt index e4644f06e..d09b39bdc 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/FOGRewardsInterface.kt +++ b/Server/src/main/content/minigame/fog/FOGRewardsInterface.kt @@ -1,5 +1,6 @@ -package rs09.game.interaction +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,7 +9,8 @@ import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable import core.plugin.Plugin -import rs09.game.system.SystemLogger +import core.tools.Log +import core.tools.SystemLogger @Initializable class FOGRewardsInterface : ComponentPlugin(){ @@ -52,7 +54,7 @@ class FOGRewardsInterface : ComponentPlugin(){ 176 -> choice = Air_Gloves 181 -> choice = Water_Gloves 186 -> choice = Earth_Gloves - else -> SystemLogger.logWarn("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/java/core/game/content/activity/fog/FOGWaitingZone.java b/Server/src/main/content/minigame/fog/FOGWaitingZone.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/fog/FOGWaitingZone.java rename to Server/src/main/content/minigame/fog/FOGWaitingZone.java index b9bb94261..d7ff0c975 100644 --- a/Server/src/main/java/core/game/content/activity/fog/FOGWaitingZone.java +++ b/Server/src/main/content/minigame/fog/FOGWaitingZone.java @@ -1,4 +1,4 @@ -package core.game.content.activity.fog; +package content.minigame.fog; import core.game.interaction.Option; import core.game.node.Node; diff --git a/Server/src/main/kotlin/rs09/game/content/activity/fog/FogInteractionHandler.kt b/Server/src/main/content/minigame/fog/FogInteractionHandler.kt similarity index 81% rename from Server/src/main/kotlin/rs09/game/content/activity/fog/FogInteractionHandler.kt rename to Server/src/main/content/minigame/fog/FogInteractionHandler.kt index 9c3dbc357..8f7b2e870 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/fog/FogInteractionHandler.kt +++ b/Server/src/main/content/minigame/fog/FogInteractionHandler.kt @@ -1,6 +1,6 @@ -package rs09.game.content.activity.fog +package content.minigame.fog -import core.game.content.global.action.ClimbActionHandler +import core.game.global.action.ClimbActionHandler import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.node.Node @@ -8,8 +8,8 @@ import core.game.node.scenery.Scenery import core.game.node.entity.player.Player import core.plugin.Initializable import core.plugin.Plugin -import core.game.content.quest.PluginInteraction -import core.game.content.quest.PluginInteractionManager +import core.game.interaction.PluginInteraction +import core.game.interaction.PluginInteractionManager @Initializable class FogInteractionHandler : PluginInteraction(30204, 30203){ @@ -38,7 +38,7 @@ class FogInteractionHandler : PluginInteraction(30204, 30203){ } override fun newInstance(arg: Any?): Plugin { - PluginInteractionManager.register(this,PluginInteractionManager.InteractionType.OBJECT) + PluginInteractionManager.register(this, PluginInteractionManager.InteractionType.OBJECT) return this } diff --git a/Server/src/main/kotlin/rs09/game/content/activity/fog/ReggieDialogue.kt b/Server/src/main/content/minigame/fog/ReggieDialogue.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/content/activity/fog/ReggieDialogue.kt rename to Server/src/main/content/minigame/fog/ReggieDialogue.kt index 5c216021f..be5618d5b 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/fog/ReggieDialogue.kt +++ b/Server/src/main/content/minigame/fog/ReggieDialogue.kt @@ -1,16 +1,13 @@ -package rs09.game.content.activity.fog +package content.minigame.fog import core.game.component.Component import core.game.node.entity.player.Player import core.game.node.item.Item -import rs09.game.world.GameWorld +import core.game.world.GameWorld import core.plugin.Initializable -import core.game.content.dialogue.DialoguePlugin -import org.rs09.consts.Components -import rs09.game.interaction.FOGRewardsInterface @Initializable -class ReggieDialogue(player: Player? = null) : DialoguePlugin(player){ +class ReggieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { if(GameWorld.settings?.allow_token_purchase == true){ options("Can I see your shop?","Nevermind.","Can I buy some tokens?") @@ -21,7 +18,7 @@ class ReggieDialogue(player: Player? = null) : DialoguePlugin(player){ return true } - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { FOGRewardsInterface().newInstance(Unit) return ReggieDialogue(player) } diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/DeliveryBoxHandler.kt b/Server/src/main/content/minigame/gnomecooking/DeliveryBoxHandler.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/DeliveryBoxHandler.kt rename to Server/src/main/content/minigame/gnomecooking/DeliveryBoxHandler.kt index 54e8536fa..55d6657b0 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/DeliveryBoxHandler.kt +++ b/Server/src/main/content/minigame/gnomecooking/DeliveryBoxHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking +package content.minigame.gnomecooking import core.cache.def.impl.ItemDefinition import core.game.interaction.OptionHandler diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCCompletionDialogue.kt b/Server/src/main/content/minigame/gnomecooking/GCCompletionDialogue.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCCompletionDialogue.kt rename to Server/src/main/content/minigame/gnomecooking/GCCompletionDialogue.kt index 40d041e2b..d069e9678 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCCompletionDialogue.kt +++ b/Server/src/main/content/minigame/gnomecooking/GCCompletionDialogue.kt @@ -1,11 +1,10 @@ -package rs09.game.content.activity.gnomecooking +package content.minigame.gnomecooking -import core.game.content.dialogue.FacialExpression +import content.minigame.gnomecooking.GnomeTipper.getTip import core.game.node.item.Item -import rs09.game.content.activity.gnomecooking.GnomeTipper.getTip -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.stringtools.colorize +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE +import core.tools.colorize class GCCompletionDialogue(val job: GnomeCookingJob) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { @@ -13,7 +12,7 @@ class GCCompletionDialogue(val job: GnomeCookingJob) : DialogueFile() { 0 -> { val neededItem = player!!.getAttribute("$GC_BASE_ATTRIBUTE:$GC_NEEDED_ITEM", null) if (neededItem != null && player!!.inventory.containsItem(neededItem)) { - player!!.dialogueInterpreter.sendDialogues(job.npc_id, FacialExpression.OLD_HAPPY, "Thank you!") + player!!.dialogueInterpreter.sendDialogues(job.npc_id, core.game.dialogue.FacialExpression.OLD_HAPPY, "Thank you!") player!!.inventory.remove(neededItem) player!!.inventory.add(getTip(job.level)) player!!.removeAttribute("$GC_BASE_ATTRIBUTE:$GC_JOB_ORDINAL") @@ -33,7 +32,7 @@ class GCCompletionDialogue(val job: GnomeCookingJob) : DialogueFile() { } player!!.setAttribute("/save:$GC_BASE_ATTRIBUTE:$GC_POINTS", curPoints) } else { - player!!.dialogueInterpreter.sendDialogues(job.npc_id, FacialExpression.ANGRY, "Where's my food?!") + player!!.dialogueInterpreter.sendDialogues(job.npc_id, core.game.dialogue.FacialExpression.ANGRY, "Where's my food?!") } stage = END_DIALOGUE } diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCRewardTokenHandler.kt b/Server/src/main/content/minigame/gnomecooking/GCRewardTokenHandler.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCRewardTokenHandler.kt rename to Server/src/main/content/minigame/gnomecooking/GCRewardTokenHandler.kt index 197421d14..4ab4ea72f 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GCRewardTokenHandler.kt +++ b/Server/src/main/content/minigame/gnomecooking/GCRewardTokenHandler.kt @@ -1,7 +1,6 @@ -package rs09.game.content.activity.gnomecooking +package content.minigame.gnomecooking import core.cache.def.impl.ItemDefinition -import core.game.content.dialogue.DialoguePlugin import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.player.Player @@ -11,7 +10,7 @@ import core.plugin.Initializable import core.plugin.Plugin import core.tools.RandomFunction import org.rs09.consts.Items -import rs09.game.world.GameWorld +import core.game.world.GameWorld val gnomeItems = arrayOf( Items.FRUIT_BATTA_2277, Items.TOAD_BATTA_2255, Items.CHEESE_PLUSTOM_BATTA_2259, Items.WORM_BATTA_2253, Items.VEGETABLE_BATTA_2281, @@ -44,8 +43,8 @@ class GCRewardTokenHandler : OptionHandler() { } @Initializable - class RewardTokenActivationDialogue(player: Player? = null) : DialoguePlugin(player){ - override fun newInstance(player: Player?): DialoguePlugin { + class RewardTokenActivationDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return RewardTokenActivationDialogue(player) } diff --git a/Server/src/main/content/minigame/gnomecooking/GnomeCookingConstants.kt b/Server/src/main/content/minigame/gnomecooking/GnomeCookingConstants.kt new file mode 100644 index 000000000..80363f1da --- /dev/null +++ b/Server/src/main/content/minigame/gnomecooking/GnomeCookingConstants.kt @@ -0,0 +1,11 @@ +package content.minigame.gnomecooking + +const val GC_BASE_ATTRIBUTE = "gnome_cooking" +const val GC_TUT_PROG = "gnome-cooking:tutorial:stage" +const val GC_TUT_FIN = "gnome-cooking:tutorial:complete" +const val GC_JOB_ORDINAL = "job:job_ordinal" +const val GC_JOB_COMPLETE = "job:job_complete" +const val GC_HARD_JOB = "job:hard_job" +const val GC_NEEDED_ITEM = "job:needed_item" +const val GC_POINTS = "job:points" +const val GC_REDEEMABLE_FOOD = "job:redeemable_food" diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeCookingJob.kt b/Server/src/main/content/minigame/gnomecooking/GnomeCookingJob.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeCookingJob.kt rename to Server/src/main/content/minigame/gnomecooking/GnomeCookingJob.kt index 560838c84..bf718b475 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeCookingJob.kt +++ b/Server/src/main/content/minigame/gnomecooking/GnomeCookingJob.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking +package content.minigame.gnomecooking enum class GnomeCookingJob(val level: GnomeTipper.LEVEL, val npc_id: Int, val tip: String) { CPT_ERRDO(GnomeTipper.LEVEL.EASY,3811, "at the top level of the Grand Tree."), diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeTipper.kt b/Server/src/main/content/minigame/gnomecooking/GnomeTipper.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeTipper.kt rename to Server/src/main/content/minigame/gnomecooking/GnomeTipper.kt index 17fa9193d..2a37668c9 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeTipper.kt +++ b/Server/src/main/content/minigame/gnomecooking/GnomeTipper.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking +package content.minigame.gnomecooking import core.game.node.item.Item import core.game.node.item.WeightedChanceItem diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaCooker.kt b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaCooker.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaCooker.kt rename to Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaCooker.kt index b548a53a8..766c61b9e 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaCooker.kt +++ b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaCooker.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.battas +package content.minigame.gnomecooking.battas import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler @@ -10,7 +10,7 @@ import core.game.world.update.flag.context.Animation import core.plugin.Initializable import core.plugin.Plugin import org.rs09.consts.Items -import rs09.game.world.GameWorld +import core.game.world.GameWorld /** * Handles cook options for battas diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaDishFiller.kt b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaDishFiller.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaDishFiller.kt rename to Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaDishFiller.kt index c89d4ab24..29ffc191f 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaDishFiller.kt +++ b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaDishFiller.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.battas +package content.minigame.gnomecooking.battas import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaGarnisher.kt b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaGarnisher.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaGarnisher.kt rename to Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaGarnisher.kt index 4040bfbcb..034919ae2 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaGarnisher.kt +++ b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaGarnisher.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.battas +package content.minigame.gnomecooking.battas import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaInterface.kt b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaInterface.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaInterface.kt rename to Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaInterface.kt index 50fd59953..d8872bf91 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaInterface.kt +++ b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaInterface.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.battas +package content.minigame.gnomecooking.battas import core.game.component.Component import core.game.component.ComponentDefinition diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaPrepareHandler.kt b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaPrepareHandler.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaPrepareHandler.kt rename to Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaPrepareHandler.kt index f69059135..38cecd428 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/battas/GnomeBattaPrepareHandler.kt +++ b/Server/src/main/content/minigame/gnomecooking/battas/GnomeBattaPrepareHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.battas +package content.minigame.gnomecooking.battas import core.cache.def.impl.ItemDefinition import core.game.component.Component diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlCooker.kt b/Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlCooker.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlCooker.kt rename to Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlCooker.kt index 5dbd940db..00b7977af 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlCooker.kt +++ b/Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlCooker.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.bowls +package content.minigame.gnomecooking.bowls import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler @@ -10,7 +10,7 @@ import core.game.world.update.flag.context.Animation import core.plugin.Initializable import core.plugin.Plugin import org.rs09.consts.Items -import rs09.game.world.GameWorld +import core.game.world.GameWorld @Initializable class GnomeBowlCooker : UseWithHandler(Items.RAW_GNOMEBOWL_2178,9558,9559,9561,9563) { diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlGarnisher.kt b/Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlGarnisher.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlGarnisher.kt rename to Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlGarnisher.kt index 7ee399f4e..b1d802f87 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlGarnisher.kt +++ b/Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlGarnisher.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.bowls +package content.minigame.gnomecooking.bowls import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlInterface.kt b/Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlInterface.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlInterface.kt rename to Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlInterface.kt index ec9193eea..bd8eb1ce2 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlInterface.kt +++ b/Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlInterface.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.bowls +package content.minigame.gnomecooking.bowls import core.game.component.Component import core.game.component.ComponentDefinition diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlPrepareHandler.kt b/Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlPrepareHandler.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlPrepareHandler.kt rename to Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlPrepareHandler.kt index b21513ca5..99fa956ba 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomeBowlPrepareHandler.kt +++ b/Server/src/main/content/minigame/gnomecooking/bowls/GnomeBowlPrepareHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.bowls +package content.minigame.gnomecooking.bowls import core.cache.def.impl.ItemDefinition import core.game.component.Component diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomebowlMouldFiller.kt b/Server/src/main/content/minigame/gnomecooking/bowls/GnomebowlMouldFiller.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomebowlMouldFiller.kt rename to Server/src/main/content/minigame/gnomecooking/bowls/GnomebowlMouldFiller.kt index fd75c527a..2cba2ffcc 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/bowls/GnomebowlMouldFiller.kt +++ b/Server/src/main/content/minigame/gnomecooking/bowls/GnomebowlMouldFiller.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.bowls +package content.minigame.gnomecooking.bowls import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailCooker.kt b/Server/src/main/content/minigame/gnomecooking/cocktails/CocktailCooker.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailCooker.kt rename to Server/src/main/content/minigame/gnomecooking/cocktails/CocktailCooker.kt index 8c65cfa50..01130745e 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailCooker.kt +++ b/Server/src/main/content/minigame/gnomecooking/cocktails/CocktailCooker.kt @@ -1,11 +1,11 @@ -package rs09.game.content.activity.gnomecooking.cocktails +package content.minigame.gnomecooking.cocktails 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 rs09.game.world.GameWorld +import core.game.world.GameWorld import core.game.world.update.flag.context.Animation import core.plugin.Initializable import core.plugin.Plugin diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailFinisher.kt b/Server/src/main/content/minigame/gnomecooking/cocktails/CocktailFinisher.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailFinisher.kt rename to Server/src/main/content/minigame/gnomecooking/cocktails/CocktailFinisher.kt index 60a40d87b..6eebf9c71 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailFinisher.kt +++ b/Server/src/main/content/minigame/gnomecooking/cocktails/CocktailFinisher.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.cocktails +package content.minigame.gnomecooking.cocktails import core.cache.def.impl.ItemDefinition import core.game.interaction.OptionHandler diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailShakerHandler.kt b/Server/src/main/content/minigame/gnomecooking/cocktails/CocktailShakerHandler.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailShakerHandler.kt rename to Server/src/main/content/minigame/gnomecooking/cocktails/CocktailShakerHandler.kt index b591b05d0..5e4078d12 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/CocktailShakerHandler.kt +++ b/Server/src/main/content/minigame/gnomecooking/cocktails/CocktailShakerHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.cocktails +package content.minigame.gnomecooking.cocktails import core.cache.def.impl.ItemDefinition import core.game.component.Component diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/GnomeCocktailInterface.kt b/Server/src/main/content/minigame/gnomecooking/cocktails/GnomeCocktailInterface.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/GnomeCocktailInterface.kt rename to Server/src/main/content/minigame/gnomecooking/cocktails/GnomeCocktailInterface.kt index ce18fed05..4b9fd3e62 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/GnomeCocktailInterface.kt +++ b/Server/src/main/content/minigame/gnomecooking/cocktails/GnomeCocktailInterface.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.cocktails +package content.minigame.gnomecooking.cocktails import core.game.component.Component import core.game.component.ComponentDefinition diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/PourMixerPlugin.kt b/Server/src/main/content/minigame/gnomecooking/cocktails/PourMixerPlugin.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/PourMixerPlugin.kt rename to Server/src/main/content/minigame/gnomecooking/cocktails/PourMixerPlugin.kt index 956bcadd2..19cf50ad7 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/cocktails/PourMixerPlugin.kt +++ b/Server/src/main/content/minigame/gnomecooking/cocktails/PourMixerPlugin.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.cocktails +package content.minigame.gnomecooking.cocktails import core.cache.def.impl.ItemDefinition import core.game.interaction.OptionHandler diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyCooker.kt b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyCooker.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyCooker.kt rename to Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyCooker.kt index 75d4958e6..bb360adaf 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyCooker.kt +++ b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyCooker.kt @@ -1,11 +1,11 @@ -package rs09.game.content.activity.gnomecooking.crunchies +package content.minigame.gnomecooking.crunchies 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 rs09.game.world.GameWorld +import core.game.world.GameWorld import core.game.world.update.flag.context.Animation import core.plugin.Initializable import core.plugin.Plugin diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyGarnisher.kt b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyGarnisher.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyGarnisher.kt rename to Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyGarnisher.kt index b12b2df36..06c7d7c5e 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyGarnisher.kt +++ b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyGarnisher.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.crunchies +package content.minigame.gnomecooking.crunchies import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyInterface.kt b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyInterface.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyInterface.kt rename to Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyInterface.kt index 5630c9c8d..4785ef3d3 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyInterface.kt +++ b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyInterface.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.crunchies +package content.minigame.gnomecooking.crunchies import core.game.component.Component import core.game.component.ComponentDefinition @@ -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/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyPrepareHandler.kt b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyPrepareHandler.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyPrepareHandler.kt rename to Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyPrepareHandler.kt index 6aebcb50e..1416148bd 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyPrepareHandler.kt +++ b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyPrepareHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.crunchies +package content.minigame.gnomecooking.crunchies import core.cache.def.impl.ItemDefinition import core.game.component.Component diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyTrayFiller.kt b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyTrayFiller.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyTrayFiller.kt rename to Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyTrayFiller.kt index 80c4e4864..039d04797 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/crunchies/GnomeCrunchyTrayFiller.kt +++ b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyTrayFiller.kt @@ -1,4 +1,4 @@ -package rs09.game.content.activity.gnomecooking.crunchies +package content.minigame.gnomecooking.crunchies import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler diff --git a/Server/src/main/java/core/game/content/activity/magearena/ChamberGuardianDialogue.java b/Server/src/main/content/minigame/magearena/ChamberGuardianDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/magearena/ChamberGuardianDialogue.java rename to Server/src/main/content/minigame/magearena/ChamberGuardianDialogue.java index dbe368ba4..6ceb0b058 100644 --- a/Server/src/main/java/core/game/content/activity/magearena/ChamberGuardianDialogue.java +++ b/Server/src/main/content/minigame/magearena/ChamberGuardianDialogue.java @@ -1,7 +1,7 @@ -package core.game.content.activity.magearena; +package content.minigame.magearena; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.GodType; +import core.game.dialogue.DialoguePlugin; +import content.data.GodType; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/activity/magearena/KolodionDialogue.java b/Server/src/main/content/minigame/magearena/KolodionDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/magearena/KolodionDialogue.java rename to Server/src/main/content/minigame/magearena/KolodionDialogue.java index 6e7ea0a43..5d19240ea 100644 --- a/Server/src/main/java/core/game/content/activity/magearena/KolodionDialogue.java +++ b/Server/src/main/content/minigame/magearena/KolodionDialogue.java @@ -1,10 +1,10 @@ -package core.game.content.activity.magearena; +package content.minigame.magearena; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +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; @@ -129,7 +129,7 @@ public final class KolodionDialogue extends DialoguePlugin { stage++; break; case 2: - player("Hah! A wizard of your level? Don't be absurd."); + npc("Hah! A wizard of your level? Don't be absurd."); stage++; break; case 3: diff --git a/Server/src/main/java/core/game/content/activity/magearena/KolodionNPC.java b/Server/src/main/content/minigame/magearena/KolodionNPC.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/magearena/KolodionNPC.java rename to Server/src/main/content/minigame/magearena/KolodionNPC.java index 98c808d5a..74c06d94b 100644 --- a/Server/src/main/java/core/game/content/activity/magearena/KolodionNPC.java +++ b/Server/src/main/content/minigame/magearena/KolodionNPC.java @@ -1,8 +1,8 @@ -package core.game.content.activity.magearena; +package content.minigame.magearena; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; @@ -11,9 +11,9 @@ 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 rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MagicSwingHandler; -import rs09.game.world.GameWorld; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MagicSwingHandler; +import core.game.world.GameWorld; /** * Handles the kolodion npc. @@ -126,7 +126,7 @@ public final class KolodionNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (style != CombatStyle.MAGIC) { return false; } diff --git a/Server/src/main/java/core/game/content/activity/magearena/KolodionSession.java b/Server/src/main/content/minigame/magearena/KolodionSession.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/magearena/KolodionSession.java rename to Server/src/main/content/minigame/magearena/KolodionSession.java index 1362b201d..17df19efb 100644 --- a/Server/src/main/java/core/game/content/activity/magearena/KolodionSession.java +++ b/Server/src/main/content/minigame/magearena/KolodionSession.java @@ -1,9 +1,9 @@ -package core.game.content.activity.magearena; +package content.minigame.magearena; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; /** diff --git a/Server/src/main/java/core/game/content/activity/magearena/LundailDialogue.java b/Server/src/main/content/minigame/magearena/LundailDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/activity/magearena/LundailDialogue.java rename to Server/src/main/content/minigame/magearena/LundailDialogue.java index a2ad927fb..5504d238f 100644 --- a/Server/src/main/java/core/game/content/activity/magearena/LundailDialogue.java +++ b/Server/src/main/content/minigame/magearena/LundailDialogue.java @@ -1,9 +1,9 @@ -package core.game.content.activity.magearena; +package content.minigame.magearena; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * Handles the lundail dialogue. diff --git a/Server/src/main/java/core/game/content/activity/magearena/MageArenaNPC.java b/Server/src/main/content/minigame/magearena/MageArenaNPC.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/magearena/MageArenaNPC.java rename to Server/src/main/content/minigame/magearena/MageArenaNPC.java index 39628e8af..e4a448940 100644 --- a/Server/src/main/java/core/game/content/activity/magearena/MageArenaNPC.java +++ b/Server/src/main/content/minigame/magearena/MageArenaNPC.java @@ -1,8 +1,8 @@ -package core.game.content.activity.magearena; +package content.minigame.magearena; -import core.game.content.global.GodType; +import content.data.GodType; import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/activity/magearena/MageArenaPlugin.java b/Server/src/main/content/minigame/magearena/MageArenaPlugin.java similarity index 90% rename from Server/src/main/java/core/game/content/activity/magearena/MageArenaPlugin.java rename to Server/src/main/content/minigame/magearena/MageArenaPlugin.java index 7000ff01f..82cf8b271 100644 --- a/Server/src/main/java/core/game/content/activity/magearena/MageArenaPlugin.java +++ b/Server/src/main/content/minigame/magearena/MageArenaPlugin.java @@ -1,13 +1,13 @@ -package core.game.content.activity.magearena; +package content.minigame.magearena; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueAction; -import core.game.content.global.GodType; +import core.game.dialogue.DialogueAction; +import content.data.GodType; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; @@ -18,9 +18,9 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.content.global.action.PickupHandler; -import rs09.game.world.GameWorld; -import rs09.plugin.PluginManager; +import core.game.global.action.PickupHandler; +import core.game.world.GameWorld; +import core.plugin.ClassScanner; /** * Handles the mage arena activity. @@ -41,12 +41,12 @@ public final class MageArenaPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(MAGE_ARENA); - PluginManager.definePlugin(new KolodionNPC()); - PluginManager.definePlugin(new MageArenaNPC()); - PluginManager.definePlugin(new LundailDialogue()); - PluginManager.definePlugin(new KolodionDialogue()); - PluginManager.definePlugin(new ChamberGuardianDialogue()); + ClassScanner.definePlugin(MAGE_ARENA); + ClassScanner.definePlugin(new KolodionNPC()); + ClassScanner.definePlugin(new MageArenaNPC()); + ClassScanner.definePlugin(new LundailDialogue()); + ClassScanner.definePlugin(new KolodionDialogue()); + ClassScanner.definePlugin(new ChamberGuardianDialogue()); ItemDefinition.forId(2412).getHandlers().put("option:drop", this); ItemDefinition.forId(2413).getHandlers().put("option:drop", this); ItemDefinition.forId(2414).getHandlers().put("option:drop", this); diff --git a/Server/src/main/java/core/game/content/activity/magearena/MageArenaZone.java b/Server/src/main/content/minigame/magearena/MageArenaZone.java similarity index 97% rename from Server/src/main/java/core/game/content/activity/magearena/MageArenaZone.java rename to Server/src/main/content/minigame/magearena/MageArenaZone.java index bb274329b..4a386519c 100644 --- a/Server/src/main/java/core/game/content/activity/magearena/MageArenaZone.java +++ b/Server/src/main/content/minigame/magearena/MageArenaZone.java @@ -1,11 +1,11 @@ -package core.game.content.activity.magearena; +package content.minigame.magearena; 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.system.task.Pulse; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/content/activity/mta/AlchemyGuardianDialogue.java b/Server/src/main/content/minigame/mta/AlchemyGuardianDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/activity/mta/AlchemyGuardianDialogue.java rename to Server/src/main/content/minigame/mta/AlchemyGuardianDialogue.java index bebb8897b..aa09d28ab 100644 --- a/Server/src/main/java/core/game/content/activity/mta/AlchemyGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/AlchemyGuardianDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** @@ -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/java/core/game/content/activity/mta/CharmedWarriorDialogue.java b/Server/src/main/content/minigame/mta/CharmedWarriorDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/mta/CharmedWarriorDialogue.java rename to Server/src/main/content/minigame/mta/CharmedWarriorDialogue.java index 4c6bc46cd..269f7d7ca 100644 --- a/Server/src/main/java/core/game/content/activity/mta/CharmedWarriorDialogue.java +++ b/Server/src/main/content/minigame/mta/CharmedWarriorDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; diff --git a/Server/src/main/kotlin/rs09/game/content/activity/mta/EnchantSpell.kt b/Server/src/main/content/minigame/mta/EnchantSpell.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/content/activity/mta/EnchantSpell.kt rename to Server/src/main/content/minigame/mta/EnchantSpell.kt index bbb5f3351..53f05bc13 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/mta/EnchantSpell.kt +++ b/Server/src/main/content/minigame/mta/EnchantSpell.kt @@ -1,16 +1,18 @@ -package rs09.game.content.activity.mta +package content.minigame.mta -import core.game.content.activity.mta.MTAType -import core.game.content.activity.mta.impl.EnchantingZone -import core.game.content.activity.mta.impl.EnchantingZone.Shapes +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.equipment.SpellType +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.audio.Audio -import core.game.node.entity.skill.magic.MagicSpell -import core.game.node.entity.skill.magic.Runes +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 @@ -49,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.") @@ -58,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")) { @@ -81,14 +86,14 @@ class EnchantSpell : MagicSpell { convert = 0 } entity.setAttribute("mta-convert", convert) - if (shape == EnchantingZone.BONUS_SHAPE) { + if (shape == content.minigame.mta.impl.EnchantingZone.BONUS_SHAPE) { pizazz += 1 entity.sendMessage("You get " + pizazz + " bonus point" + (if (pizazz != 1) "s" else "") + "!") } } } if (pizazz != 0) { - EnchantingZone.ZONE.incrementPoints(entity, MTAType.ENCHANTERS.ordinal, pizazz) + content.minigame.mta.impl.EnchantingZone.ZONE.incrementPoints(entity, MTAType.ENCHANTERS.ordinal, pizazz) } } return true @@ -100,7 +105,7 @@ class EnchantSpell : MagicSpell { override fun getExperience(player: Player): Double { return if (player.zoneMonitor.isInZone("Enchantment Chamber")) { - experience - experience * 0.75 + experience - experience * 0.25 } else experience } @@ -133,7 +138,7 @@ class EnchantSpell : MagicSpell { 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_11080), + Items.EMERALD_BRACELET_11076 to Item(Items.CASTLEWAR_BRACE3_11079), //Begin MTA-Specific Enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), @@ -170,7 +175,7 @@ class EnchantSpell : MagicSpell { 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_11096), + Items.DIAMOND_BRACELET_11092 to Item(Items.FORINTHRY_BRACE5_11095), //Begin MTA-Specific Enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), @@ -186,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), @@ -231,4 +236,4 @@ class EnchantSpell : MagicSpell { */ private val GRAPHIC = Graphics(114, 96) } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/content/activity/mta/EnchantmentGuardianDialogue.java b/Server/src/main/content/minigame/mta/EnchantmentGuardianDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/activity/mta/EnchantmentGuardianDialogue.java rename to Server/src/main/content/minigame/mta/EnchantmentGuardianDialogue.java index db065b958..8f61a9e9e 100644 --- a/Server/src/main/java/core/game/content/activity/mta/EnchantmentGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/EnchantmentGuardianDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/content/activity/mta/EntranceGuardianDialogue.java b/Server/src/main/content/minigame/mta/EntranceGuardianDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/mta/EntranceGuardianDialogue.java rename to Server/src/main/content/minigame/mta/EntranceGuardianDialogue.java index 7fe6a502b..4870b051a 100644 --- a/Server/src/main/java/core/game/content/activity/mta/EntranceGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/EntranceGuardianDialogue.java @@ -1,8 +1,8 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * Handles the entrance guardian dialogue. diff --git a/Server/src/main/java/core/game/content/activity/mta/GraveyardGuardianDialogue.java b/Server/src/main/content/minigame/mta/GraveyardGuardianDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/activity/mta/GraveyardGuardianDialogue.java rename to Server/src/main/content/minigame/mta/GraveyardGuardianDialogue.java index 384b5d7a4..05d5a7338 100644 --- a/Server/src/main/java/core/game/content/activity/mta/GraveyardGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/GraveyardGuardianDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** @@ -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 new file mode 100644 index 000000000..bc656348b --- /dev/null +++ b/Server/src/main/content/minigame/mta/MTAListeners.kt @@ -0,0 +1,108 @@ +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 +import core.game.world.update.flag.context.Graphics +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() { + on(NPCs.MAZE_GUARDIAN_3102, IntType.NPC, "talk-to"){ player, node -> + player.dialogueInterpreter.open(node.id, node) + 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) + } + } +} + +class MTASpellListeners : SpellListener("modern"){ + 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 MTA_ALCH_ITEMS = content.minigame.mta.impl.AlchemistZone.AlchemistItem.values().map{it.item.id}.toIntArray() + + override fun defineListeners() { + + onCast(Modern.HIGH_ALCHEMY,ITEM,*MTA_ALCH_ITEMS){ p, node -> + val item = node?.asItem() ?: return@onCast + requires(p,55) + val alchItem = content.minigame.mta.impl.AlchemistZone.AlchemistItem.forItem(item.id) ?: return@onCast + val coins = Item(content.minigame.mta.impl.AlchemistZone.COINS.id, alchItem.cost) + val freeAlch = alchItem == content.minigame.mta.impl.AlchemistZone.freeConvert + + if (p.inventory.getAmount(content.minigame.mta.impl.AlchemistZone.COINS.id) + alchItem.cost > 10000) { + p.dialogueInterpreter.sendDialogue("Warning: You can't deposit more than 12000 coins at a time.") + } + + if (coins.amount > 1 && !p.inventory.hasSpaceFor(coins)) { + p.packetDispatch.sendMessage("Not enough space in your inventory!") + return@onCast + } + + if(!freeAlch){ + requires(p,55,arrayOf(Item(Items.FIRE_RUNE_554,5),Item(Items.NATURE_RUNE_561,1))) + } + + p.lock(3) + p.visualize(HIGH_ALCH_ANIM,HIGH_ALCH_GFX) + if(p.inventory.remove(Item(item.id,1))){ + playAudio(p, Sounds.HIGH_ALCHEMY_97) + if (coins.amount != 0) { + p.inventory.add(coins) + } + } + + showMagicTab(p) + addXP(p,65.0) + setDelay(p,false) + removeRunes(p) + } + + onCast(Modern.LOW_ALCHEMY,ITEM,*MTA_ALCH_ITEMS){p, node -> + val item = node?.asItem() ?: return@onCast + requires(p,21) + val alchItem = content.minigame.mta.impl.AlchemistZone.AlchemistItem.forItem(item.id) ?: return@onCast + val coins = Item(content.minigame.mta.impl.AlchemistZone.COINS.id, alchItem.cost) + val freeAlch = alchItem == content.minigame.mta.impl.AlchemistZone.freeConvert + + if (p.inventory.getAmount(content.minigame.mta.impl.AlchemistZone.COINS.id) + alchItem.cost > 10000) { + p.dialogueInterpreter.sendDialogue("Warning: You can't deposit more than 12000 coins at a time.") + } + + if (coins.amount > 1 && !p.inventory.hasSpaceFor(coins)) { + p.packetDispatch.sendMessage("Not enough space in your inventory!") + return@onCast + } + + if(!freeAlch){ + requires(p,21,arrayOf(Item(Items.FIRE_RUNE_554,3),Item(Items.NATURE_RUNE_561,1))) + } + + p.lock(3) + p.visualize(LOW_ALCH_ANIM,LOW_ALCH_GFX) + if(p.inventory.remove(Item(item.id,1))){ + playAudio(p, Sounds.HIGH_ALCHEMY_97) + if (coins.amount != 0) { + p.inventory.add(coins) + } + } + + showMagicTab(p) + addXP(p,31.0) + setDelay(p,false) + removeRunes(p) + } + + } +} diff --git a/Server/src/main/java/core/game/content/activity/mta/MTAShop.java b/Server/src/main/content/minigame/mta/MTAShop.java similarity index 93% rename from Server/src/main/java/core/game/content/activity/mta/MTAShop.java rename to Server/src/main/content/minigame/mta/MTAShop.java index 4bafd5efe..beb64a1b2 100644 --- a/Server/src/main/java/core/game/content/activity/mta/MTAShop.java +++ b/Server/src/main/content/minigame/mta/MTAShop.java @@ -1,5 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; +import core.api.ContainerisedItem; import core.game.component.CloseEvent; import core.game.component.Component; import core.game.component.ComponentPlugin; @@ -10,11 +11,13 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.system.task.Pulse; import core.plugin.Plugin; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.hasAnItem; + /** * Represents the mage training arena shop. * @author Vexia @@ -147,9 +150,11 @@ public class MTAShop { player.sendMessage("You already unlocked that spell."); return; } + ContainerisedItem itemToRemove = null; if (slot >= 1 && slot <= 3) { Item required = ITEMS[slot - 1]; - if (!player.hasItem(new Item(required.getId(), 1)) && !player.hasItem(new Item(6914, 1))) { + itemToRemove = hasAnItem(player, required.getId()); + if (!itemToRemove.exists() && !player.hasItem(new Item(6914, 1))) { player.sendMessage("You don't have the required wand in order to buy this upgrade."); return; } @@ -163,7 +168,8 @@ public class MTAShop { player.getSavedData().getActivityData().setBonesToPeaches(true); player.getDialogueInterpreter().sendDialogue("The guardian teaches you how to use the Bones to Peaches spell!"); } else { - player.getInventory().add(item); + if (itemToRemove == null || itemToRemove.remove()) + player.getInventory().add(item); } for (int i = 0; i < prices.length; i++) { decrementPoints(player, i, prices[i]); @@ -241,9 +247,9 @@ public class MTAShop { if (item == null) { return true; } - if (opcode == 230) { + if (opcode == 155) { value(player, item, slot); - } else { + } else if (opcode == 196) { buy(player, item, slot); } return true; diff --git a/Server/src/main/java/core/game/content/activity/mta/MTAType.java b/Server/src/main/content/minigame/mta/MTAType.java similarity index 92% rename from Server/src/main/java/core/game/content/activity/mta/MTAType.java rename to Server/src/main/content/minigame/mta/MTAType.java index b0c716faf..35ad046ab 100644 --- a/Server/src/main/java/core/game/content/activity/mta/MTAType.java +++ b/Server/src/main/content/minigame/mta/MTAType.java @@ -1,15 +1,19 @@ -package core.game.content.activity.mta; +package content.minigame.mta; +import content.minigame.mta.impl.AlchemistZone; +import content.minigame.mta.impl.EnchantingZone; +import content.minigame.mta.impl.GraveyardZone; +import content.minigame.mta.impl.TelekineticZone; import core.game.component.Component; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.map.Location; -import core.game.content.activity.mta.impl.AlchemistZone; -import core.game.content.activity.mta.impl.EnchantingZone; -import core.game.content.activity.mta.impl.GraveyardZone; -import core.game.content.activity.mta.impl.TelekineticZone; +import content.minigame.mta.impl.AlchemistZone; +import content.minigame.mta.impl.EnchantingZone; +import content.minigame.mta.impl.GraveyardZone; +import content.minigame.mta.impl.TelekineticZone; /** * A magic training arena game type. diff --git a/Server/src/main/java/core/game/content/activity/mta/MTAZone.java b/Server/src/main/content/minigame/mta/MTAZone.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/mta/MTAZone.java rename to Server/src/main/content/minigame/mta/MTAZone.java index ba8dbf9b9..7110812f0 100644 --- a/Server/src/main/java/core/game/content/activity/mta/MTAZone.java +++ b/Server/src/main/content/minigame/mta/MTAZone.java @@ -1,6 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import rs09.ServerConstants; +import core.ServerConstants; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; diff --git a/Server/src/main/java/core/game/content/activity/mta/MageTrainingArenaPlugin.java b/Server/src/main/content/minigame/mta/MageTrainingArenaPlugin.java similarity index 85% rename from Server/src/main/java/core/game/content/activity/mta/MageTrainingArenaPlugin.java rename to Server/src/main/content/minigame/mta/MageTrainingArenaPlugin.java index eab29ad89..31dfc56c9 100644 --- a/Server/src/main/java/core/game/content/activity/mta/MageTrainingArenaPlugin.java +++ b/Server/src/main/content/minigame/mta/MageTrainingArenaPlugin.java @@ -1,14 +1,14 @@ -package core.game.content.activity.mta; +package content.minigame.mta; +import content.minigame.mta.impl.TelekineticZone; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.activity.mta.impl.TelekineticZone; 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.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.node.item.GroundItem; import core.game.node.item.Item; import core.game.world.map.Direction; @@ -17,8 +17,8 @@ import core.game.world.map.zone.ZoneBuilder; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.content.activity.mta.EnchantSpell; -import rs09.plugin.PluginManager; +import content.minigame.mta.EnchantSpell; +import core.plugin.ClassScanner; /** * Handles the mage training area interactions. @@ -52,7 +52,7 @@ public class MageTrainingArenaPlugin extends OptionHandler { ItemDefinition.forId(TelekineticZone.STATUE).getHandlers().put("option:observe", this); ItemDefinition.forId(TelekineticZone.STATUE).getHandlers().put("option:reset", this); NPCDefinition.forId(3102).getHandlers().put("option:talk-to", this); - PluginManager.definePlugins(new CharmedWarriorDialogue(), new EntranceGuardianDialogue(), new RewardsGuardianDialogue(), new ProgressHatDialogue(), new EnchantmentGuardianDialogue(), new EnchantSpell(), new GraveyardGuardianDialogue(), new AlchemyGuardianDialogue(), new TelekineticGrabSpell(), new TelekineticGuardianDialogue(), new MazeGuardianDialogue()); + ClassScanner.definePlugins(new CharmedWarriorDialogue(), new EntranceGuardianDialogue(), new RewardsGuardianDialogue(), new ProgressHatDialogue(), new EnchantmentGuardianDialogue(), new EnchantSpell(), new GraveyardGuardianDialogue(), new AlchemyGuardianDialogue(), new TelekineticGrabSpell(), new TelekineticGuardianDialogue(), new MazeGuardianDialogue()); return this; } diff --git a/Server/src/main/java/core/game/content/activity/mta/MazeGuardianDialogue.java b/Server/src/main/content/minigame/mta/MazeGuardianDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/activity/mta/MazeGuardianDialogue.java rename to Server/src/main/content/minigame/mta/MazeGuardianDialogue.java index cabdd1fd8..1a6aac8a2 100644 --- a/Server/src/main/java/core/game/content/activity/mta/MazeGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/MazeGuardianDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import content.minigame.mta.impl.TelekineticZone; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.content.activity.mta.impl.TelekineticZone; +import content.minigame.mta.impl.TelekineticZone; /** * Handles the maze guardian dialogue. diff --git a/Server/src/main/java/core/game/content/activity/mta/ProgressHatDialogue.java b/Server/src/main/content/minigame/mta/ProgressHatDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/mta/ProgressHatDialogue.java rename to Server/src/main/content/minigame/mta/ProgressHatDialogue.java index d3e731e2b..49030e00f 100644 --- a/Server/src/main/java/core/game/content/activity/mta/ProgressHatDialogue.java +++ b/Server/src/main/content/minigame/mta/ProgressHatDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/content/activity/mta/RewardsGuardianDialogue.java b/Server/src/main/content/minigame/mta/RewardsGuardianDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/mta/RewardsGuardianDialogue.java rename to Server/src/main/content/minigame/mta/RewardsGuardianDialogue.java index 830fcf3de..39cd0e8c1 100644 --- a/Server/src/main/java/core/game/content/activity/mta/RewardsGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/RewardsGuardianDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/content/activity/mta/TelekineticGrabSpell.java b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java similarity index 86% rename from Server/src/main/java/core/game/content/activity/mta/TelekineticGrabSpell.java rename to Server/src/main/content/minigame/mta/TelekineticGrabSpell.java index 581341e25..72728d1e6 100644 --- a/Server/src/main/java/core/game/content/activity/mta/TelekineticGrabSpell.java +++ b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java @@ -1,18 +1,19 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.activity.mta.impl.TelekineticZone; +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.equipment.SpellType; +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; 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.player.link.audio.Audio; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; +import core.game.node.entity.combat.spell.MagicSpell; +import core.game.node.entity.combat.spell.Runes; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; @@ -20,8 +21,11 @@ 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 rs09.game.content.global.action.PickupHandler; -import rs09.game.world.GameWorld; +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. @@ -79,7 +83,7 @@ public final class TelekineticGrabSpell extends MagicSpell { /** * Represents the spell id. */ - public static final int SPELL_ID = 65535; + public static final int SPELL_ID = 19; /** * Constructs a new {@code TelekineticGrabSpell} {@code Object}. @@ -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/java/core/game/content/activity/mta/TelekineticGuardianDialogue.java b/Server/src/main/content/minigame/mta/TelekineticGuardianDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/activity/mta/TelekineticGuardianDialogue.java rename to Server/src/main/content/minigame/mta/TelekineticGuardianDialogue.java index 3cb88dff1..79c3a2b3f 100644 --- a/Server/src/main/java/core/game/content/activity/mta/TelekineticGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/TelekineticGuardianDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.mta; +package content.minigame.mta; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/content/activity/mta/impl/AlchemistZone.java b/Server/src/main/content/minigame/mta/impl/AlchemistZone.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/mta/impl/AlchemistZone.java rename to Server/src/main/content/minigame/mta/impl/AlchemistZone.java index 1282754b9..35cd787a4 100644 --- a/Server/src/main/java/core/game/content/activity/mta/impl/AlchemistZone.java +++ b/Server/src/main/content/minigame/mta/impl/AlchemistZone.java @@ -1,10 +1,11 @@ -package core.game.content.activity.mta.impl; +package content.minigame.mta.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import content.minigame.mta.MTAZone; import core.game.node.entity.skill.Skills; import core.game.interaction.Option; import core.game.node.Node; @@ -15,15 +16,14 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; 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.tools.RandomFunction; -import core.game.content.activity.mta.MTAType; -import core.game.content.activity.mta.MTAZone; +import content.minigame.mta.MTAType; /** * Handles the alchemist playground game. diff --git a/Server/src/main/java/core/game/content/activity/mta/impl/EnchantingZone.java b/Server/src/main/content/minigame/mta/impl/EnchantingZone.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/mta/impl/EnchantingZone.java rename to Server/src/main/content/minigame/mta/impl/EnchantingZone.java index a298f0f06..04f2fc772 100644 --- a/Server/src/main/java/core/game/content/activity/mta/impl/EnchantingZone.java +++ b/Server/src/main/content/minigame/mta/impl/EnchantingZone.java @@ -1,4 +1,4 @@ -package core.game.content.activity.mta.impl; +package content.minigame.mta.impl; import java.util.ArrayList; import java.util.HashMap; @@ -15,14 +15,14 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; 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.tools.RandomFunction; -import core.game.content.activity.mta.MTAZone; +import content.minigame.mta.MTAZone; /** * Handles the enchanting zone. @@ -178,6 +178,9 @@ public class EnchantingZone extends MTAZone { return true; } + @Override + public boolean isRemainPrivate() {return true;} + @Override public void respawn() { GameWorld.getPulser().submit(getRespawnPulse(this)); diff --git a/Server/src/main/java/core/game/content/activity/mta/impl/GraveyardZone.java b/Server/src/main/content/minigame/mta/impl/GraveyardZone.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/mta/impl/GraveyardZone.java rename to Server/src/main/content/minigame/mta/impl/GraveyardZone.java index 0e7e20ecb..ace8aaf63 100644 --- a/Server/src/main/java/core/game/content/activity/mta/impl/GraveyardZone.java +++ b/Server/src/main/content/minigame/mta/impl/GraveyardZone.java @@ -1,4 +1,4 @@ -package core.game.content.activity.mta.impl; +package content.minigame.mta.impl; import java.util.ArrayList; import java.util.List; @@ -13,7 +13,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.zone.ZoneBorders; import core.game.world.update.flag.context.Animation; @@ -21,8 +21,8 @@ import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; import core.tools.StringUtils; -import core.game.content.activity.mta.MTAType; -import core.game.content.activity.mta.MTAZone; +import content.minigame.mta.MTAType; +import content.minigame.mta.MTAZone; /** * Handles the creature graveyard zone. diff --git a/Server/src/main/java/core/game/content/activity/mta/impl/TelekineticZone.java b/Server/src/main/content/minigame/mta/impl/TelekineticZone.java similarity index 97% rename from Server/src/main/java/core/game/content/activity/mta/impl/TelekineticZone.java rename to Server/src/main/content/minigame/mta/impl/TelekineticZone.java index cd9e9ea6a..a810548d6 100644 --- a/Server/src/main/java/core/game/content/activity/mta/impl/TelekineticZone.java +++ b/Server/src/main/content/minigame/mta/impl/TelekineticZone.java @@ -1,10 +1,10 @@ -package core.game.content.activity.mta.impl; +package content.minigame.mta.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import core.game.content.dialogue.DialogueAction; +import core.game.dialogue.DialogueAction; import core.game.node.entity.skill.Skills; import core.game.interaction.Option; import core.game.node.Node; @@ -15,7 +15,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; @@ -27,12 +27,12 @@ import core.net.packet.context.CameraContext.CameraType; import core.net.packet.out.CameraViewPacket; import core.tools.RandomFunction; -import core.game.content.activity.mta.MTAType; -import core.game.content.activity.mta.MTAZone; +import content.minigame.mta.MTAType; +import content.minigame.mta.MTAZone; /** * Handles the telekinetic zone. - * @author Vexia + * @author Vexia, Player Name */ public class TelekineticZone extends MTAZone { @@ -122,11 +122,6 @@ public class TelekineticZone extends MTAZone { if (player == null || player != this.player) { return super.leave(entity, logout); } - if (logout) { - player.getSavedData().getActivityData().setSolvedMazes(solved); - } else { - player.getSavedData().getActivityData().setSolvedMazes(0); - } if (statue != null) { GroundItemManager.destroy(statue); } @@ -277,6 +272,7 @@ public class TelekineticZone extends MTAZone { } }); } + player.getSavedData().getActivityData().setSolvedMazes(solved); incrementPoints(player, MTAType.TELEKINETIC.ordinal(), points); TelekineticZone.this.update(player); player.setAttribute("camera", false); @@ -306,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/java/core/game/content/dialogue/OutpostSquireDialogue.java b/Server/src/main/content/minigame/pestcontrol/OutpostSquireDialogue.java similarity index 88% rename from Server/src/main/java/core/game/content/dialogue/OutpostSquireDialogue.java rename to Server/src/main/content/minigame/pestcontrol/OutpostSquireDialogue.java index 1ff67fdc2..b6d2e5b25 100644 --- a/Server/src/main/java/core/game/content/dialogue/OutpostSquireDialogue.java +++ b/Server/src/main/content/minigame/pestcontrol/OutpostSquireDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.minigame.pestcontrol; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; @@ -73,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: @@ -91,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: @@ -111,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: @@ -125,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/java/core/game/content/activity/pestcontrol/PCIslandZone.java b/Server/src/main/content/minigame/pestcontrol/PCIslandZone.java similarity index 87% rename from Server/src/main/java/core/game/content/activity/pestcontrol/PCIslandZone.java rename to Server/src/main/content/minigame/pestcontrol/PCIslandZone.java index bd69e3f36..42cc5179d 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/PCIslandZone.java +++ b/Server/src/main/content/minigame/pestcontrol/PCIslandZone.java @@ -1,4 +1,4 @@ -package core.game.content.activity.pestcontrol; +package content.minigame.pestcontrol; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; @@ -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/java/core/game/content/activity/pestcontrol/PCLanderZone.java b/Server/src/main/content/minigame/pestcontrol/PCLanderZone.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/pestcontrol/PCLanderZone.java rename to Server/src/main/content/minigame/pestcontrol/PCLanderZone.java index d6205ae28..5ace493bb 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/PCLanderZone.java +++ b/Server/src/main/content/minigame/pestcontrol/PCLanderZone.java @@ -1,4 +1,4 @@ -package core.game.content.activity.pestcontrol; +package content.minigame.pestcontrol; import core.game.interaction.Option; import core.game.node.Node; @@ -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/java/core/game/content/activity/pestcontrol/PCObjectHandler.java b/Server/src/main/content/minigame/pestcontrol/PCObjectHandler.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/pestcontrol/PCObjectHandler.java rename to Server/src/main/content/minigame/pestcontrol/PCObjectHandler.java index c7ae1396e..741bbc4df 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/PCObjectHandler.java +++ b/Server/src/main/content/minigame/pestcontrol/PCObjectHandler.java @@ -1,12 +1,12 @@ -package core.game.content.activity.pestcontrol; +package content.minigame.pestcontrol; import core.cache.def.impl.SceneryDefinition; -import rs09.game.world.GameWorld; -import core.game.content.activity.ActivityManager; +import core.game.world.GameWorld; +import core.game.activity.ActivityManager; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; -import rs09.game.ai.pvmbots.PvMBotsBuilder; +import core.game.bots.PvMBotsBuilder; import core.game.node.entity.player.info.Rights; import core.game.node.item.Item; import core.game.node.scenery.Scenery; diff --git a/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java b/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java new file mode 100644 index 000000000..0bddb9a24 --- /dev/null +++ b/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java @@ -0,0 +1,338 @@ +package content.minigame.pestcontrol; + +import java.util.Random; + +import content.minigame.pestcontrol.monsters.PCRavagerNPC; +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.world.map.Direction; +import core.game.world.map.Location; +import core.game.world.map.RegionPlane; +import core.game.world.update.flag.context.Animation; +import core.tools.RandomFunction; +import content.minigame.pestcontrol.monsters.PCRavagerNPC; +import content.minigame.pestcontrol.monsters.PCSpinnerNPC; + +/** + * Handles the portal NPC. + * @author Emperor + */ +public final class PCPortalNPC extends AbstractNPC { + + /** + * The splatter NPCs. + */ + private static final int[][] SPLATTERS = { + { 3727, 3728, 3729 }, // Novice + { 3728, 3729, 3730 }, // Intermediate + { 3729, 3730, 3731 }, // Veteran + }; + + /** + * The shifter NPCs. + */ + private static final int[][] SHIFTERS = { + { 3732, 3733, 3734, 3735, 3736, 3737 }, // Novice + { 3734, 3735, 3736, 3737, 3738, 3739 }, // Intermediate + { 3736, 3737, 3738, 3739, 3740, 3741 }, // Veteran + }; + + /** + * The ravager NPCs. + */ + private static final int[][] RAVAGERS = { + { 3742, 3743, 3744 }, // Novice + { 3743, 3744, 3745 }, // Intermediate + { 3744, 3745, 3746 }, // Veteran + }; + + /** + * The spinner NPCs. + */ + private static final int[][] SPINNERS = { + { 3747, 3748, 3749 }, // Novice + { 3748, 3749, 3750 }, // Intermediate + { 3749, 3750, 3751 }, // Veteran + }; + + /** + * The torcher NPCs. + */ + private static final int[][] TORCHERS = { + { 3752, 3753, 3754, 3755, 3756, 3757 }, // Novice + { 3754, 3755, 3756, 3757, 3758, 3759 }, // Intermediate + { 3756, 3757, 3758, 3759, 3760, 3761 }, // Veteran + }; + + /** + * The defiler NPCs. + */ + private static final int[][] DEFILERS = { + { 3762, 3763, 3764, 3765, 3766, 3767 }, // Novice + { 3764, 3765, 3766, 3767, 3768, 3769 }, // Intermediate + { 3766, 3767, 3768, 3769, 3770, 3771 }, // Veteran + }; + + /** + * The brawler NPCs. + */ + private static final int[][] BRAWLERS = { + { 3772, 3773, 3774 }, // Novice + { 3773, 3774, 3775 }, // Intermediate + { 3774, 3775, 3776 }, // Veteran + }; + + /** + * If the lifepoints should be updated. + */ + public boolean updateLifepoints = true; + + /** + * The pest control session. + */ + private PestControlSession session; + + /** + * The spinners. + */ + private PCSpinnerNPC[] spinners = new PCSpinnerNPC[3]; + + /** + * The brawlers. + */ + private NPC[] brawlers = new NPC[2]; + + /** + * The portal ids? + */ + final static Integer[] portalIds = new Integer[] { + 6142, 6143, 6144, 6145, 6146, 6147, 6148, 6149, + 6150, 6151, 6152, 6153, 6154, 6155, 6156, 6157, + 7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558 + }; + + /** + * Constructs a new {@code PCPortalNPC} {@code Object}. + */ + public PCPortalNPC() { + this(6142, null); + } + + /** + * Constructs a new {@code PCPortalNPC} {@code Object}. + * @param id The NPC id. + * @param l The location. + */ + public PCPortalNPC(int id, Location l) { + super(id, l); + } + + @Override + public void init() { + super.setWalks(false); + super.setRespawn(false); + super.getProperties().setRetaliating(false); + super.init(); + getProperties().setAttackAnimation(Animation.create(-1)); + getProperties().setDefenceAnimation(Animation.create(-1)); + getProperties().setDeathAnimation(Animation.create(-1)); + session = getExtension(PestControlSession.class); + } + + @Override + public void tick() { + super.tick(); + if (session != null) { + RegionPlane plane = getViewport().getCurrentPlane(); + if (plane != null && session.getTicks() % 35 - plane.getPlayers().size() == 0 && plane.getNpcs().size() < 100) { + spawnNPCs(); + } + if (updateLifepoints && session.getTicks() % 10 == 0) { + String color = getSkills().getLifepoints() > 50 ? "" : ""; + session.sendString(color + getSkills().getLifepoints(), 13 + getPortalIndex()); + updateLifepoints = false; + } + } + } + + @Override + public boolean shouldPreventStacking(Entity mover) { + return true; + } + + @Override + public void onImpact(final Entity entity, BattleState state) { + updateLifepoints = true; + super.onImpact(entity, state); + if (session != null && state != null && entity instanceof Player) { + int total = 0; + if (state.getEstimatedHit() > 0) { + total += state.getEstimatedHit(); + } + if (state.getSecondaryHit() > 0) { + total += state.getSecondaryHit(); + } + session.addZealGained((Player) entity, total); + } + } + + /** + * Spawns the NPCs. + */ + public void spawnNPCs() { + Direction dir = Direction.getLogicalDirection(getLocation(), session.getSquire().getLocation()); + int index = getDifficultyIndex(); + Random r = RandomFunction.RANDOM; + int amount = index + 1; + if (getViewport().getCurrentPlane() != null) { + amount += getViewport().getCurrentPlane().getPlayers().size() / 10; + } + if (getDifficultyIndex() == 0) { + amount += ((getDifficultyIndex() + 1) * 3) / 2; + } + for (int i = 0; i < amount; i++) { + int[] ids = SHIFTERS[index]; + switch (r.nextInt(7)) { + case 0: + ids = SPLATTERS[index]; + break; + case 1: + ids = SHIFTERS[index]; + break; + case 2: + ids = RAVAGERS[index]; + break; + case 3: + boolean spawn = false; + for (PCSpinnerNPC npc : spinners) { + if (npc == null || !npc.isActive()) { + spawn = true; + break; + } + } + if (spawn) { + ids = SPINNERS[index]; + } else { + ids = TORCHERS[index]; + } + break; + case 4: + ids = TORCHERS[index]; + break; + case 5: + ids = DEFILERS[index]; + break; + case 6: + spawn = false; + for (NPC npc : brawlers) { + if (npc == null || !npc.isActive()) { + spawn = true; + break; + } + } + if (spawn) { + ids = BRAWLERS[index]; + } else { + ids = DEFILERS[index]; + } + break; + } + int x = dir.getStepX() * 3; + int y = dir.getStepY() * 3; + if (x == 0) { + x = i; + } else { + y = i; + } + NPC n = session.addNPC(NPC.create(ids[r.nextInt(ids.length)], getLocation().transform(x, y, 0))); + if (ids == RAVAGERS[index]) { + ((PCRavagerNPC) n).setPortalIndex(getPortalIndex()); + } else if (ids == SPINNERS[index]) { + for (int j = 0; j < spinners.length; j++) { + if (spinners[j] == null || !spinners[j].isActive()) { + spinners[j] = ((PCSpinnerNPC) n).setPortalIndex(getPortalIndex()); + break; + } + } + } + n.setWalks(true); + n.setRespawn(false); + } + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new PCPortalNPC(id, location); + } + + @Override + public void finalizeDeath(Entity killer) { + super.finalizeDeath(killer); + if (session != null) { + int value = 0; + boolean endSession = true; + for (int i = 0; i < 4; i++) { + if (!session.getPortals()[i].isActive()) { + value |= 1 << i; + } else { + endSession = false; + } + } + if (value > 0) { + session.sendConfig(value << 28); + if (endSession) { + session.getActivity().end(session, true); + return; + } + } + for (PCSpinnerNPC npc : spinners) { + if (npc != null && npc.isActive()) { + npc.explode(); + } + } + session.sendString("0", 13 + getPortalIndex()); + session.getSquire().getSkills().heal(50); + ((PCSquireNPC) session.getSquire()).FlagInterfaceUpdate(); + } + } + + @Override + public int[] getIds() { + return new int[] { + 6142, 6143, 6144, 6145, 6146, 6147, 6148, 6149, + 6150, 6151, 6152, 6153, 6154, 6155, 6156, 6157, + 7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558 + }; + } + + /** + * The difficulty index. + * @return The index. + */ + public int getDifficultyIndex() { + if (getId() > 7550) { + return 2; + } + if (getId() > 6149) { + return 1; + } + return 0; + } + + /** + * The portal index. + * @return The index. + */ + public int getPortalIndex() { + if (getId() > 7550) { + return (getId() - 7551) % 4; + } + if (getId() > 6149) { + return (getId() - 6150) % 4; + } + return (getId() - 6142) % 4; + } +} diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/PCSquireNPC.java b/Server/src/main/content/minigame/pestcontrol/PCSquireNPC.java similarity index 92% rename from Server/src/main/java/core/game/content/activity/pestcontrol/PCSquireNPC.java rename to Server/src/main/content/minigame/pestcontrol/PCSquireNPC.java index f237a506e..14a4eb1a1 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/PCSquireNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/PCSquireNPC.java @@ -1,4 +1,4 @@ -package core.game.content.activity.pestcontrol; +package content.minigame.pestcontrol; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -47,7 +47,7 @@ public final class PCSquireNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (DeathTask.isDead(this) || entity instanceof Player) { return false; } @@ -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/java/core/game/content/activity/pestcontrol/PestControlActivityPlugin.java b/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java similarity index 89% rename from Server/src/main/java/core/game/content/activity/pestcontrol/PestControlActivityPlugin.java rename to Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java index af996fd2e..f1fb680a0 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/PestControlActivityPlugin.java +++ b/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java @@ -1,35 +1,35 @@ -package core.game.content.activity.pestcontrol; +package content.minigame.pestcontrol; import java.util.ArrayList; import java.util.List; import java.util.PriorityQueue; -import rs09.ServerConstants; +import content.minigame.pestcontrol.monsters.*; +import core.ServerConstants; import core.game.component.Component; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; import core.game.interaction.Option; import core.game.node.Node; 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; -import rs09.game.ai.AIPlayer; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; import core.game.world.map.zone.RegionZone; import core.game.world.map.zone.ZoneBuilder; import core.game.world.map.zone.ZoneRestriction; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; import core.tools.StringUtils; -import core.game.content.activity.pestcontrol.monsters.*; + +import static core.api.ContentAPIKt.*; /** @@ -148,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) { @@ -193,6 +193,7 @@ public final class PestControlActivityPlugin extends ActivityPlugin { */ public PestControlActivityPlugin(BoatType type) { super("pest control " + type.name().toLowerCase(), false, true, true, ZoneRestriction.CANNON); + this.safeRespawn = Location.create(2657, 2646, 0); this.type = type; } @@ -219,18 +220,18 @@ public final class PestControlActivityPlugin extends ActivityPlugin { ActivityManager.register(activities[1]); ActivityManager.register(activities[2]); // Load abstract NPC plugins - PluginManager.definePlugin(new PCPortalNPC()); - PluginManager.definePlugin(new PCSquireNPC()); - PluginManager.definePlugin(new PCTorcherNPC()); - PluginManager.definePlugin(new PCDefilerNPC()); - PluginManager.definePlugin(new PCRavagerNPC()); - PluginManager.definePlugin(new PCShifterNPC()); - PluginManager.definePlugin(new PCSplatterNPC()); - PluginManager.definePlugin(new PCSpinnerNPC()); - PluginManager.definePlugin(new PCBrawlerNPC()); - PluginManager.definePlugin(new PCObjectHandler()); - PluginManager.definePlugin(new PestControlSquire()); - PluginManager.definePlugin(new VoidSealPlugin()); + ClassScanner.definePlugin(new PCPortalNPC()); + ClassScanner.definePlugin(new PCSquireNPC()); + ClassScanner.definePlugin(new PCTorcherNPC()); + ClassScanner.definePlugin(new PCDefilerNPC()); + ClassScanner.definePlugin(new PCRavagerNPC()); + ClassScanner.definePlugin(new PCShifterNPC()); + ClassScanner.definePlugin(new PCSplatterNPC()); + ClassScanner.definePlugin(new PCSpinnerNPC()); + ClassScanner.definePlugin(new PCBrawlerNPC()); + ClassScanner.definePlugin(new PCObjectHandler()); + ClassScanner.definePlugin(new PestControlSquire()); + ClassScanner.definePlugin(new VoidSealPlugin()); ZoneBuilder.configure(new PCLanderZone(activities)); ZoneBuilder.configure(new PCIslandZone()); } diff --git a/Server/src/main/kotlin/rs09/game/content/activity/pestcontrol/PestControlHelper.kt b/Server/src/main/content/minigame/pestcontrol/PestControlHelper.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/content/activity/pestcontrol/PestControlHelper.kt rename to Server/src/main/content/minigame/pestcontrol/PestControlHelper.kt index 098a0091f..82c17b0a4 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/pestcontrol/PestControlHelper.kt +++ b/Server/src/main/content/minigame/pestcontrol/PestControlHelper.kt @@ -1,11 +1,10 @@ -package rs09.game.content.activity.pestcontrol +package content.minigame.pestcontrol -import core.game.content.activity.pestcontrol.PestControlSession import core.game.node.entity.player.Player import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders -import rs09.game.ai.minigamebots.pestcontrol.PestControlTestBot -import rs09.game.ai.minigamebots.pestcontrol.PestControlTestBot2 +import content.minigame.pestcontrol.bots.PestControlTestBot +import content.minigame.pestcontrol.bots.PestControlTestBot2 import kotlin.random.Random diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/PestControlSession.java b/Server/src/main/content/minigame/pestcontrol/PestControlSession.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/pestcontrol/PestControlSession.java rename to Server/src/main/content/minigame/pestcontrol/PestControlSession.java index c9b69c07d..8396f97b8 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/PestControlSession.java +++ b/Server/src/main/content/minigame/pestcontrol/PestControlSession.java @@ -1,12 +1,11 @@ -package core.game.content.activity.pestcontrol; +package content.minigame.pestcontrol; import java.util.*; import core.game.component.Component; -import core.game.content.dialogue.FacialExpression; +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; @@ -14,7 +13,8 @@ import core.game.world.map.RegionPlane; import core.game.world.map.build.DynamicRegion; import core.tools.RandomFunction; -import static core.game.content.activity.pestcontrol.PestControlActivityPlugin.MAX_TEAM_SIZE; +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!"); } @@ -307,6 +307,7 @@ public final class PestControlSession { */ public NPC addNPC(NPC npc) { npc.addExtension(PestControlSession.class, this); + npc.setAttribute("no-spawn-return", true); npc.init(); return npc; } @@ -385,4 +386,4 @@ public final class PestControlSession { return barricades; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/PestControlSquire.java b/Server/src/main/content/minigame/pestcontrol/PestControlSquire.java similarity index 89% rename from Server/src/main/java/core/game/content/activity/pestcontrol/PestControlSquire.java rename to Server/src/main/content/minigame/pestcontrol/PestControlSquire.java index 84a5510dc..d0e773218 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/PestControlSquire.java +++ b/Server/src/main/content/minigame/pestcontrol/PestControlSquire.java @@ -1,13 +1,15 @@ -package core.game.content.activity.pestcontrol; +package content.minigame.pestcontrol; import core.cache.def.impl.NPCDefinition; -import core.game.content.global.travel.ship.Ships; +import content.global.travel.ship.Ships; 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; +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/java/core/game/content/dialogue/SquireDialogue.java b/Server/src/main/content/minigame/pestcontrol/SquireDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/SquireDialogue.java rename to Server/src/main/content/minigame/pestcontrol/SquireDialogue.java index 1593ed347..c0e7ca1de 100644 --- a/Server/src/main/java/core/game/content/dialogue/SquireDialogue.java +++ b/Server/src/main/content/minigame/pestcontrol/SquireDialogue.java @@ -1,10 +1,14 @@ -package core.game.content.dialogue; +package content.minigame.pestcontrol; -import core.game.content.global.travel.ship.Ships; +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.plugin.Initializable; import core.game.node.entity.player.Player; +import static core.api.ContentAPIKt.*; + /** * Handles the SquireDialogue dialogue. * @author 'Vexia @@ -92,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) { @@ -122,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: @@ -155,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/java/core/game/content/dialogue/SquireMagicDialougue.java b/Server/src/main/content/minigame/pestcontrol/SquireMagicDialougue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/SquireMagicDialougue.java rename to Server/src/main/content/minigame/pestcontrol/SquireMagicDialougue.java index 29c21508a..7922aad9b 100644 --- a/Server/src/main/java/core/game/content/dialogue/SquireMagicDialougue.java +++ b/Server/src/main/content/minigame/pestcontrol/SquireMagicDialougue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.minigame.pestcontrol; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/dialogue/SquireTypeDialogue.java b/Server/src/main/content/minigame/pestcontrol/SquireTypeDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/SquireTypeDialogue.java rename to Server/src/main/content/minigame/pestcontrol/SquireTypeDialogue.java index 80163abda..d25944841 100644 --- a/Server/src/main/java/core/game/content/dialogue/SquireTypeDialogue.java +++ b/Server/src/main/content/minigame/pestcontrol/SquireTypeDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.minigame.pestcontrol; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; @@ -74,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/java/core/game/content/activity/pestcontrol/VoidSealPlugin.java b/Server/src/main/content/minigame/pestcontrol/VoidSealPlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/pestcontrol/VoidSealPlugin.java rename to Server/src/main/content/minigame/pestcontrol/VoidSealPlugin.java index 4e3962be0..95244ea66 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/VoidSealPlugin.java +++ b/Server/src/main/content/minigame/pestcontrol/VoidSealPlugin.java @@ -1,5 +1,6 @@ -package core.game.content.activity.pestcontrol; +package content.minigame.pestcontrol; +import content.minigame.pestcontrol.monsters.*; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -11,7 +12,6 @@ import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.tools.RandomFunction; -import core.game.content.activity.pestcontrol.monsters.*; /** * Handles the void knight seal. diff --git a/Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/CombatState.kt b/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/CombatState.kt rename to Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt index a56fc69f0..dbfb2ee4a 100644 --- a/Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/CombatState.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt @@ -1,4 +1,4 @@ -package rs09.game.ai.minigamebots.pestcontrol +package content.minigame.pestcontrol.bots import core.game.interaction.MovementPulse import core.game.node.Node @@ -9,11 +9,10 @@ import core.game.world.map.path.Pathfinder import core.tools.RandomFunction import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import rs09.game.content.activity.pestcontrol.PestControlHelper.GATE_ENTRIES -import rs09.game.content.activity.pestcontrol.PestControlHelper.getMyPestControlSession1 -import rs09.game.world.GameWorld +import content.minigame.pestcontrol.PestControlHelper.GATE_ENTRIES +import content.minigame.pestcontrol.PestControlHelper.getMyPestControlSession1 +import core.game.world.GameWorld import java.util.* -import java.util.concurrent.Executors class CombatState(val bot: PestControlTestBot) { @@ -33,7 +32,7 @@ class CombatState(val bot: PestControlTestBot) { removeList.add(port) } else { portal = port - break; + break } } sesh.aportals.removeAll(removeList) @@ -98,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/kotlin/rs09/game/ai/minigamebots/pestcontrol/CombatStateIntermediate.kt b/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/CombatStateIntermediate.kt rename to Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt index 70243a700..fbea681bd 100644 --- a/Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/CombatStateIntermediate.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt @@ -1,4 +1,4 @@ -package rs09.game.ai.minigamebots.pestcontrol +package content.minigame.pestcontrol.bots import core.game.interaction.MovementPulse import core.game.node.Node @@ -9,11 +9,10 @@ import core.game.world.map.path.Pathfinder import core.tools.RandomFunction import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import rs09.game.content.activity.pestcontrol.PestControlHelper.GATE_ENTRIES -import rs09.game.content.activity.pestcontrol.PestControlHelper.getMyPestControlSession2 -import rs09.game.world.GameWorld +import content.minigame.pestcontrol.PestControlHelper.GATE_ENTRIES +import content.minigame.pestcontrol.PestControlHelper.getMyPestControlSession2 +import core.game.world.GameWorld import java.util.* -import java.util.concurrent.Executors class CombatStateIntermediate(val bot: PestControlTestBot2) { private val Random = Random() @@ -32,7 +31,7 @@ class CombatStateIntermediate(val bot: PestControlTestBot2) { removeList.add(port) } else { portal = port - break; + break } } sesh.aportals.removeAll(removeList) @@ -98,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/kotlin/rs09/game/ai/minigamebots/pestcontrol/PestControlIntermediateBot.kt b/Server/src/main/content/minigame/pestcontrol/bots/PestControlIntermediateBot.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/PestControlIntermediateBot.kt rename to Server/src/main/content/minigame/pestcontrol/bots/PestControlIntermediateBot.kt index 3f19a5335..c369030d8 100644 --- a/Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/PestControlIntermediateBot.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/PestControlIntermediateBot.kt @@ -1,11 +1,10 @@ -package rs09.game.ai.minigamebots.pestcontrol +package content.minigame.pestcontrol.bots -import core.game.content.activity.pestcontrol.PestControlActivityPlugin import core.game.world.map.Location import core.tools.RandomFunction -import rs09.game.ai.pvmbots.CombatBotAssembler -import rs09.game.ai.pvmbots.PvMBots -import rs09.game.content.activity.pestcontrol.PestControlHelper +import core.game.bots.CombatBotAssembler +import core.game.bots.PvMBots +import content.minigame.pestcontrol.PestControlHelper import java.util.* class PestControlTestBot2(l: Location) : PvMBots(legitimizeLocation(l)) { @@ -35,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") @@ -87,7 +86,7 @@ class PestControlTestBot2(l: Location) : PvMBots(legitimizeLocation(l)) { private fun attackNPCs() { if (PestControlHelper.outsideGangplankContainsLoc2(getLocation())){ - PestControlActivityPlugin().leave(this,false) + content.minigame.pestcontrol.PestControlActivityPlugin().leave(this,false) val test = getClosestNodeWithEntry(50, myBoat.ladderId) test ?: println("PC: Gangplank Null") test.interaction.handle(this, test.interaction[0])//.also { println("Intermediate - We think we is in pest control ${this.username}.") } @@ -161,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/kotlin/rs09/game/ai/minigamebots/pestcontrol/PestControlNoviceBot.kt b/Server/src/main/content/minigame/pestcontrol/bots/PestControlNoviceBot.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/PestControlNoviceBot.kt rename to Server/src/main/content/minigame/pestcontrol/bots/PestControlNoviceBot.kt index df865908f..6d07a15df 100644 --- a/Server/src/main/kotlin/rs09/game/ai/minigamebots/pestcontrol/PestControlNoviceBot.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/PestControlNoviceBot.kt @@ -1,11 +1,10 @@ -package rs09.game.ai.minigamebots.pestcontrol +package content.minigame.pestcontrol.bots -import core.game.content.activity.pestcontrol.PestControlActivityPlugin import core.game.world.map.Location import core.tools.RandomFunction -import rs09.game.ai.pvmbots.CombatBotAssembler -import rs09.game.ai.pvmbots.PvMBots -import rs09.game.content.activity.pestcontrol.PestControlHelper +import core.game.bots.CombatBotAssembler +import core.game.bots.PvMBots +import content.minigame.pestcontrol.PestControlHelper import java.util.* class PestControlTestBot(l: Location) : PvMBots(legitimizeLocation(l)){ @@ -35,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") @@ -86,7 +85,7 @@ class PestControlTestBot(l: Location) : PvMBots(legitimizeLocation(l)){ fun attackNPCs() { if (PestControlHelper.outsideGangplankContainsLoc(getLocation())){ - PestControlActivityPlugin().leave(this,false) + content.minigame.pestcontrol.PestControlActivityPlugin().leave(this,false) val test = getClosestNodeWithEntry(50, myBoat.ladderId) test ?: println("PC: Gangplank Null") test.interaction.handle(this, test.interaction[0])//.also { println("Novice - We think we is in pest control ${this.username}.") } @@ -157,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/PCBrawlerNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCBrawlerNPC.java new file mode 100644 index 000000000..f9f85ea65 --- /dev/null +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCBrawlerNPC.java @@ -0,0 +1,77 @@ +package content.minigame.pestcontrol.monsters; + +import content.minigame.pestcontrol.PestControlSession; +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; + +/** + * Handles the pest control brawler NPCs. + * @author Emperor + */ +public final class PCBrawlerNPC extends AbstractNPC { + /** + * The pest control session. + */ + private PestControlSession session; + + /** + * Constructs a new {@code PCBrawlerNPC} {@code Object}. + */ + public PCBrawlerNPC() { + super(3772, null); + } + + /** + * Constructs a new {@code PCBrawlerNPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + public PCBrawlerNPC(int id, Location location) { + super(id, location); + } + + @Override + public void init() { + super.setAggressive(true); + super.init(); + super.getDefinition().setCombatDistance(1); + super.walkRadius = 64; + getProperties().getCombatPulse().setStyle(CombatStyle.MELEE); + session = getExtension(PestControlSession.class); + } + + @Override + public boolean shouldPreventStacking(Entity mover) { + return true; + } + + @Override + public void onImpact(final Entity entity, BattleState state) { + super.onImpact(entity, state); + if (session != null && state != null && entity instanceof Player) { + int total = 0; + if (state.getEstimatedHit() > 0) { + total += state.getEstimatedHit(); + } + if (state.getSecondaryHit() > 0) { + total += state.getSecondaryHit(); + } + session.addZealGained((Player) entity, total); + } + } + + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new PCBrawlerNPC(id, location); + } + + @Override + public int[] getIds() { + return new int[] { 3772, 3773, 3774, 3775, 3776 }; + } +} diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCDefilerNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCDefilerNPC.java similarity index 92% rename from Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCDefilerNPC.java rename to Server/src/main/content/minigame/pestcontrol/monsters/PCDefilerNPC.java index 711620778..73315afd2 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCDefilerNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCDefilerNPC.java @@ -1,6 +1,6 @@ -package core.game.content.activity.pestcontrol.monsters; +package content.minigame.pestcontrol.monsters; -import core.game.content.activity.pestcontrol.PestControlSession; +import content.minigame.pestcontrol.PestControlSession; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -10,8 +10,8 @@ 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.MapDistance; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.RangeSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.RangeSwingHandler; /** * Handles the Defiler NPCs. diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCRavagerNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCRavagerNPC.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCRavagerNPC.java rename to Server/src/main/content/minigame/pestcontrol/monsters/PCRavagerNPC.java index 000846402..4fba76be2 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCRavagerNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCRavagerNPC.java @@ -1,22 +1,23 @@ -package core.game.content.activity.pestcontrol.monsters; +package content.minigame.pestcontrol.monsters; import core.game.interaction.MovementPulse; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; +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.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.MapDistance; import core.game.world.map.path.Pathfinder; import core.game.world.map.zone.ZoneBorders; import core.tools.RandomFunction; -import core.game.content.activity.pestcontrol.PestControlSession; +import content.minigame.pestcontrol.PestControlSession; /** * Handles the Ravager pest control NPC. @@ -155,7 +156,7 @@ public class PCRavagerNPC extends AbstractNPC { public boolean pulse() { return true; } - }, "movement"); + }, PulseType.STANDARD); } /** diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCShifterNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCShifterNPC.java similarity index 93% rename from Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCShifterNPC.java rename to Server/src/main/content/minigame/pestcontrol/monsters/PCShifterNPC.java index 88a3a3261..8364481e9 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCShifterNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCShifterNPC.java @@ -1,6 +1,6 @@ -package core.game.content.activity.pestcontrol.monsters; +package content.minigame.pestcontrol.monsters; -import core.game.content.activity.pestcontrol.PestControlSession; +import content.minigame.pestcontrol.PestControlSession; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -14,10 +14,10 @@ import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; -import rs09.game.node.entity.combat.CombatPulse; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; -import rs09.game.world.GameWorld; +import core.game.node.entity.combat.CombatPulse; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MeleeSwingHandler; +import core.game.world.GameWorld; import java.util.ArrayList; import java.util.Collections; diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCSpinnerNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java similarity index 87% rename from Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCSpinnerNPC.java rename to Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java index 917b2dba2..60cc22a37 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCSpinnerNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java @@ -1,22 +1,25 @@ -package core.game.content.activity.pestcontrol.monsters; +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; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.impl.Animator.Priority; +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 rs09.game.world.GameWorld; +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.tools.RandomFunction; -import core.game.content.activity.pestcontrol.PestControlSession; + +import static core.api.ContentAPIKt.*; /** * Handles a pest control spinner NPC. @@ -72,7 +75,7 @@ public final class PCSpinnerNPC extends AbstractNPC { public boolean pulse() { return true; } - }, "movement"); + }, PulseType.STANDARD); } } } @@ -89,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; } /** @@ -99,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 @@ -111,10 +113,10 @@ public final class PCSpinnerNPC extends AbstractNPC { }); } - @Override - public boolean shouldPreventStacking(Entity mover) { - return mover instanceof NPC; - } + @Override + public boolean shouldPreventStacking(Entity mover) { + return mover instanceof NPC; + } @Override public void onImpact(final Entity entity, BattleState state) { diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCSplatterNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCSplatterNPC.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCSplatterNPC.java rename to Server/src/main/content/minigame/pestcontrol/monsters/PCSplatterNPC.java index a6eea8314..5e5111211 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCSplatterNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCSplatterNPC.java @@ -1,7 +1,8 @@ -package core.game.content.activity.pestcontrol.monsters; +package content.minigame.pestcontrol.monsters; import java.util.ArrayList; +import content.minigame.pestcontrol.PestControlSession; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.impl.Animator.Priority; @@ -11,13 +12,12 @@ import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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.tools.RandomFunction; -import core.game.content.activity.pestcontrol.PestControlSession; /** * Handles the pest control splatter NPC. diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCTorcherNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCTorcherNPC.java similarity index 92% rename from Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCTorcherNPC.java rename to Server/src/main/content/minigame/pestcontrol/monsters/PCTorcherNPC.java index 276b46caf..809764585 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCTorcherNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCTorcherNPC.java @@ -1,12 +1,12 @@ -package core.game.content.activity.pestcontrol.monsters; +package content.minigame.pestcontrol.monsters; -import core.game.content.activity.pestcontrol.PestControlSession; +import content.minigame.pestcontrol.PestControlSession; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.InteractionType; -import core.game.node.entity.combat.equipment.SpellType; +import core.game.node.entity.combat.spell.SpellType; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.impl.Projectile; import core.game.node.entity.npc.AbstractNPC; @@ -18,8 +18,8 @@ import core.game.world.map.MapDistance; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MagicSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MagicSwingHandler; /** * Handles the torcher pest control NPC. diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/reward/PCIslandOptionPlugin.java b/Server/src/main/content/minigame/pestcontrol/reward/PCIslandOptionPlugin.java similarity index 85% rename from Server/src/main/java/core/game/content/activity/pestcontrol/reward/PCIslandOptionPlugin.java rename to Server/src/main/content/minigame/pestcontrol/reward/PCIslandOptionPlugin.java index 8ab61ef0d..ca33a7358 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/reward/PCIslandOptionPlugin.java +++ b/Server/src/main/content/minigame/pestcontrol/reward/PCIslandOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.activity.pestcontrol.reward; +package content.minigame.pestcontrol.reward; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; @@ -6,7 +6,7 @@ import core.game.node.Node; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the option plugin used to handle the pc island related nodes. @@ -20,7 +20,7 @@ public final class PCIslandOptionPlugin extends OptionHandler { for (int id : new int[] { 3786, 3788, 3789, 5956 }) { NPCDefinition.forId(id).getHandlers().put("option:exchange", this); } - PluginManager.definePlugin(new PCRewardInterface()); + ClassScanner.definePlugin(new PCRewardInterface()); return this; } diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/reward/PCRewardInterface.java b/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/pestcontrol/reward/PCRewardInterface.java rename to Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java index cf19fac01..9140bfda2 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/reward/PCRewardInterface.java +++ b/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java @@ -1,4 +1,4 @@ -package core.game.content.activity.pestcontrol.reward; +package content.minigame.pestcontrol.reward; import java.util.ArrayList; import java.util.List; @@ -8,13 +8,15 @@ import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.herblore.Herbs; +import content.global.skill.herblore.Herbs; import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; 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/java/core/game/content/activity/pestcontrol/reward/VoidKnightDialogue.java b/Server/src/main/content/minigame/pestcontrol/reward/VoidKnightDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/activity/pestcontrol/reward/VoidKnightDialogue.java rename to Server/src/main/content/minigame/pestcontrol/reward/VoidKnightDialogue.java index 1675b49c9..7ed3d6ede 100644 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/reward/VoidKnightDialogue.java +++ b/Server/src/main/content/minigame/pestcontrol/reward/VoidKnightDialogue.java @@ -1,7 +1,7 @@ -package core.game.content.activity.pestcontrol.reward; +package content.minigame.pestcontrol.reward; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; @@ -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/java/core/game/content/activity/puropuro/ElnockInquisitorDialogue.java b/Server/src/main/content/minigame/puropuro/ElnockInquisitorDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/puropuro/ElnockInquisitorDialogue.java rename to Server/src/main/content/minigame/puropuro/ElnockInquisitorDialogue.java index 2a2058f69..b9cbe7df7 100644 --- a/Server/src/main/java/core/game/content/activity/puropuro/ElnockInquisitorDialogue.java +++ b/Server/src/main/content/minigame/puropuro/ElnockInquisitorDialogue.java @@ -1,17 +1,19 @@ -package core.game.content.activity.puropuro; +package content.minigame.puropuro; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.hunter.bnet.BNetTypes; -import core.game.node.entity.skill.hunter.bnet.ImplingNode; +import core.game.dialogue.DialoguePlugin; +import content.global.skill.hunter.bnet.BNetTypes; +import content.global.skill.hunter.bnet.ImplingNode; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; -import core.game.content.activity.puropuro.ElnockInquisitorDialogue.ElnockExchangeInterfaceHandler.ElnockExchange; +import content.minigame.puropuro.ElnockInquisitorDialogue.ElnockExchangeInterfaceHandler.ElnockExchange; + +import static core.api.ContentAPIKt.*; /** * Handles the elnock inquisitor dialogue. @@ -188,7 +190,7 @@ public final class ElnockInquisitorDialogue extends DialoguePlugin { @Override public void init() { super.init(); - PluginManager.definePlugin(new ElnockExchangeInterfaceHandler()); + ClassScanner.definePlugin(new ElnockExchangeInterfaceHandler()); } @Override @@ -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/java/core/game/content/activity/puropuro/FairyAerykaDialogue.java b/Server/src/main/content/minigame/puropuro/FairyAerykaDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/puropuro/FairyAerykaDialogue.java rename to Server/src/main/content/minigame/puropuro/FairyAerykaDialogue.java index 68f2cf9e3..fd9e9b78d 100644 --- a/Server/src/main/java/core/game/content/activity/puropuro/FairyAerykaDialogue.java +++ b/Server/src/main/content/minigame/puropuro/FairyAerykaDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.puropuro; +package content.minigame.puropuro; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** 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/PuroPuroPlugin.java b/Server/src/main/content/minigame/puropuro/PuroPuroPlugin.java new file mode 100644 index 000000000..3aa053f8a --- /dev/null +++ b/Server/src/main/content/minigame/puropuro/PuroPuroPlugin.java @@ -0,0 +1,513 @@ +package content.minigame.puropuro; + +import core.cache.def.impl.ItemDefinition; +import core.cache.def.impl.NPCDefinition; +import core.game.component.Component; +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.player.Player; +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.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.zone.MapZone; +import core.game.world.map.zone.ZoneBuilder; +import core.game.world.map.zone.ZoneType; +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.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 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()); + } + + @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 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 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); + forceMove(player, player.getLocation(), dest, 0, 265, null, 6595, null); + } + + /** + * 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); + } + + @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 { + + @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 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(169)); + } + return true; + } + return true; + } + + /** + * 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; + } + } + + /** + * 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."); + } + } + + /** + * 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; + } + + /** + * The percent to set. + * + * @param item the item. + * @param percent the percent. + */ + private void setPercent(Item item, int percent) { + item.setCharge(item.getCharge() - percent); + } + + /** + * 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. + */ + public static class WheatSet { + + /** + * The locations of the wheat. + */ + private final Location[] locations; + + /** + * The scenerys. + */ + private Scenery[] objects = new Scenery[2]; + + /** + * The rotation. + */ + private int rot; + + /** + * The time until the next whilt. + */ + private int nextWhilt; + + /** + * The busy ticks. + */ + private int busyTicks; + + /** + * If the wheat is removed. + */ + private boolean removed; + + /** + * Constructs a new {@code WheatSet} {@code Object}. + * + * @param locations the locations. + */ + public WheatSet(int rot, Location... locations) { + this.rot = rot; + this.locations = locations; + } + + /** + * 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(); + } + + /** + * 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; + + @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; + + @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(); + } + + /** + * Sets the object spawns. + */ + public void setObjects() { + for (int i = 0; i < locations.length; i++) { + objects[i] = RegionManager.getObject(locations[i]); + } + } + + /** + * Sets the next whilt. + */ + public void setNextWhilt() { + this.nextWhilt = GameWorld.getTicks() + RandomFunction.random(40, 300); + } + + /** + * 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/java/core/game/content/activity/puropuro/WanderingImplingDialogue.java b/Server/src/main/content/minigame/puropuro/WanderingImplingDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/puropuro/WanderingImplingDialogue.java rename to Server/src/main/content/minigame/puropuro/WanderingImplingDialogue.java index f048fa6a0..2eabd012d 100644 --- a/Server/src/main/java/core/game/content/activity/puropuro/WanderingImplingDialogue.java +++ b/Server/src/main/content/minigame/puropuro/WanderingImplingDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.activity.puropuro; +package content.minigame.puropuro; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/content/minigame/pyramidplunder/GuardianMummyDialogue.kt b/Server/src/main/content/minigame/pyramidplunder/GuardianMummyDialogue.kt new file mode 100644 index 000000000..b342116b1 --- /dev/null +++ b/Server/src/main/content/minigame/pyramidplunder/GuardianMummyDialogue.kt @@ -0,0 +1,74 @@ +package content.minigame.pyramidplunder + +import core.api.setAttribute +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 + +@Initializable +class GuardianMummyDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + npcl(FacialExpression.OLD_NOT_INTERESTED, "*sigh* Not another one.") + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(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(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(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(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(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(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(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(FacialExpression.OLD_NOT_INTERESTED, "Alright, fine.").also { stage++ } + 101 -> { + end() + PyramidPlunderMinigame.join(player) + setAttribute(player, "/save:pp:mummy-spoken-to", true) + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUARDIAN_MUMMY_4476) + } + + 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 new file mode 100644 index 000000000..a295a8795 --- /dev/null +++ b/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt @@ -0,0 +1,126 @@ +package content.minigame.pyramidplunder + +import core.api.* +import core.game.world.GameWorld.Pulser +import content.minigame.pyramidplunder.PyramidPlunderMinigame.Companion.GUARDIAN_ROOM +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.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.interaction.InteractionListener +import core.game.interaction.IntType +import content.data.Quests + +/** + * Adds functionality to the pharoah's scepter + * @author ceik + */ +class PharaohSceptre : InteractionListener { + override fun defineListeners() { + 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()) + { + sendMessage(player, "You have used up all the charges on this sceptre.") + return@on true + } + + openDialogue(player, SceptreDialog()) + return@on true + } + } + + class SceptreDialog : DialogueFile() { + fun teleport(location: Location?, player: Player) { + player.lock() + player.visualize(ANIMATION, GRAPHICS) + player.impactHandler.disabledTicks = 4 + Pulser.submit(object : Pulse(4, player) { + override fun pulse(): Boolean { + player.unlock() + player.properties.teleportLocation = location + player.animator.reset() + return true + } + }) + } + + override fun handle(componentID: Int, buttonID: Int) { + if (player!!.isTeleBlocked) { + player!!.sendMessage("A magical force has stopped you from teleporting.") + return + } + when (stage) { + 0 -> options("Jalsavrah", "Jaleustrophos", "Jaldraocht", "Nowhere").also { stage++ } + 1 -> { + end() + when (buttonID) { + 1 -> { + teleport(GUARDIAN_ROOM, player!!) + } + 2 -> { + teleport(Location.create(3342, 2827, 0), player!!) + } + 3 -> { + teleport(Location.create(3233, 2902, 0), player!!) + } + 4 -> return + } + //Checks the equipment slot. 9044 = full, 9046 = 2 charges, 9048 = 1 charge, 9050 = 0 charges. + if (player!!.equipment.containsItem(Item(Items.PHARAOHS_SCEPTRE_9044))) + { + player!!.equipment.replace(Item(Items.PHARAOHS_SCEPTRE_9046), EquipmentSlot.WEAPON.ordinal) + player!!.packetDispatch.sendMessage("Your Pharoah's Sceptre has 2 charges remaining.") + } + else if (player!!.equipment.containsItem(Item(Items.PHARAOHS_SCEPTRE_9046))) + { + player!!.equipment.replace(Item(Items.PHARAOHS_SCEPTRE_9048), EquipmentSlot.WEAPON.ordinal) + player!!.packetDispatch.sendMessage("Your Pharoah's Sceptre has 1 charge remaining.") + } + else if (player!!.equipment.containsItem(Item(Items.PHARAOHS_SCEPTRE_9048))) + { + player!!.equipment.replace(Item(Items.PHARAOHS_SCEPTRE_9050), EquipmentSlot.WEAPON.ordinal) + player!!.packetDispatch.sendMessage("Your Pharoah's Sceptre has no charges remaining.") + } + //Checks the inventory. 9044 = full, 9046 = 2 charges, 9048 = 1 charge, 9050 = 0 charges. + else if (player!!.inventory.containsItem(Item(Items.PHARAOHS_SCEPTRE_9044))) + { + if (player!!.inventory.remove(Item(Items.PHARAOHS_SCEPTRE_9044))) { + player!!.inventory.add(Item(Items.PHARAOHS_SCEPTRE_9046)) + player!!.packetDispatch.sendMessage("Your Pharoah's Sceptre has 2 charges remaining.") + } + } + else if (player!!.inventory.containsItem(Item(Items.PHARAOHS_SCEPTRE_9046))) + { + if (player!!.inventory.remove(Item(Items.PHARAOHS_SCEPTRE_9046))) { + player!!.inventory.add(Item(Items.PHARAOHS_SCEPTRE_9048)) + player!!.packetDispatch.sendMessage("Your Pharoah's Sceptre has 1 charge remaining.") + } + } + else if (player!!.inventory.containsItem(Item(Items.PHARAOHS_SCEPTRE_9048))) + { + if (player!!.inventory.remove(Item(Items.PHARAOHS_SCEPTRE_9048))) { + player!!.inventory.add(Item(Items.PHARAOHS_SCEPTRE_9050)) + player!!.packetDispatch.sendMessage("Your Pharoah's Sceptre has no charges remaining.") + } + } + } + } + } + } + + companion object { + private val GRAPHICS = Graphics(715) + private val ANIMATION = Animation(714) + } +} diff --git a/Server/src/main/content/minigame/pyramidplunder/PlunderData.kt b/Server/src/main/content/minigame/pyramidplunder/PlunderData.kt new file mode 100644 index 000000000..bf8b622d3 --- /dev/null +++ b/Server/src/main/content/minigame/pyramidplunder/PlunderData.kt @@ -0,0 +1,45 @@ +package content.minigame.pyramidplunder + +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.rs09.consts.Items + +/** + * An object that simply contains Pyramid Plunder data. + */ +object PlunderData { + val playerLocations = HashMap() + val players = ArrayList() + val doorVarbits = arrayOf(2366, 2367, 2368, 2369) + val doors = Array(8){ doorVarbits[0]} + val timeLeft = HashMap() + + val pyramidEntranceVarbits = arrayOf(2371,2372,2373,2374) + var currentEntrance = pyramidEntranceVarbits.random() + var nextEntranceSwitch = 0L + val mummy = NPC(4476, Location.create(1968, 4428, 2)).also { it.isNeverWalks = true; it.init() } + + val artifacts = arrayOf( + arrayOf(Items.IVORY_COMB_9026, Items.POTTERY_SCARAB_9032, Items.POTTERY_STATUETTE_9036), + arrayOf(Items.STONE_SCARAB_9030, Items.STONE_STATUETTE_9038, Items.STONE_SEAL_9042), + arrayOf(Items.GOLDEN_SCARAB_9028, Items.GOLDEN_STATUETTE_9034, Items.GOLD_SEAL_9040) + ) + + val rooms = arrayOf( + PlunderRoom(1, Location.create(1927, 4477, 0), Location.create(1929, 4469, 0), Direction.SOUTH), + PlunderRoom(2, Location.create(1954, 4477, 0), Location.create(1955, 4467, 0), Direction.SOUTH), + PlunderRoom(3, Location.create(1977, 4471, 0), Location.create(1975, 4458, 0), Direction.SOUTH), + PlunderRoom(4, Location.create(1927, 4453, 0), Location.create(1937, 4454, 0), Direction.EAST), + PlunderRoom(5, Location.create(1965, 4444, 0), Location.create(1955, 4449, 0), Direction.WEST), + PlunderRoom(6, Location.create(1927, 4424, 0), Location.create(1925, 4433, 0), Direction.NORTH), + PlunderRoom(7, Location.create(1943, 4421, 0), Location.create(1950, 4427, 0), Direction.NORTH), + PlunderRoom(8, Location.create(1974, 4420, 0), Location.create(1971, 4431, 0), Direction.NORTH) + ) +} + +/** + * A simple way to represent a pyramid plunder room. + */ +data class PlunderRoom(val room: Int, val entrance: Location, val mummyLoc: Location, val spearDirection: Direction) \ 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 new file mode 100644 index 000000000..896a08792 --- /dev/null +++ b/Server/src/main/content/minigame/pyramidplunder/PlunderUtils.kt @@ -0,0 +1,309 @@ +package content.minigame.pyramidplunder + +import core.api.* +import core.game.component.Component +import core.game.node.Node +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.tools.RandomFunction +import org.rs09.consts.Components +import org.rs09.consts.Items + +/** + * A collection of methods needed for Pyramid Plunder. + */ +object PlunderUtils { + fun hasPlayer(player: Player): Boolean + { + return PlunderData.playerLocations[player] != null + } + + fun registerPlayer(player: Player) + { + PlunderData.players.add(player) + PlunderData.timeLeft[player] = 500 //5 minutes + } + + fun unregisterPlayer(player: Player) + { + PlunderData.players.remove(player) + PlunderData.playerLocations.remove(player) + PlunderData.timeLeft.remove(player) + } + + fun expel(player: Player, summonMummy: Boolean) + { + teleport(player, Location.create(3288, 2802, 0)) + unregisterPlayer(player) + resetOverlay(player) + resetObjectVarbits(player) + + if(!summonMummy) + { + sendDialogue(player, "You've run out of time and the guardian mummy has thrown you out.") + } + else + { + sendDialogue(player, "You've been expelled by the guardian mummy.") + } + } + + fun decrementTimeRemaining() : ArrayList + { + val timesUp = ArrayList() + PlunderData.timeLeft.forEach { player, left -> + if(left <= 0) timesUp.add(player) + PlunderData.timeLeft[player] = left - 1 + updateOverlay(player) + } + return timesUp + } + + fun loadNextRoom(player: Player) + { + val current = PlunderData.playerLocations[player] + + val next: PlunderRoom = if(current == null) + getRoom(1) + else + getRoom(current.room + 1) + + if(PlunderData.playerLocations.filter { it.value == next }.isEmpty()) //if no one is in the next room + { + PlunderData.doors[next.room - 1] = PlunderData.doorVarbits.random() //reshuffle the next room's exit door + } + + teleport(player, next.entrance) + PlunderData.playerLocations[player] = next + } + + fun getRoom(number: Int) : PlunderRoom + { + return PlunderData.rooms[number - 1] + } + + fun getRoom(player: Player): PlunderRoom? + { + return PlunderData.playerLocations[player] + } + + fun getRoomLevel(player: Player): Int + { + return 11 + (10 * getRoom(player)!!.room) + } + + fun getSpearDestination(player: Player): Location + { + val room = getRoom(player)!! + return when(room.spearDirection) + { + Direction.NORTH -> player.location.transform(0, 3, 0) + Direction.SOUTH -> player.location.transform(0, -3, 0) + Direction.WEST -> player.location.transform(-3, 0, 0) + Direction.EAST -> player.location.transform(3, 0, 0) + else -> player.location + } + } + + fun getUrnXp(player: Player, check: Boolean): Double + { + val room = getRoom(player)!!.room + return if(check) + { + when(room) + { + 1 -> 20.0 + 2 -> 30.0 + 3 -> 50.0 + 4 -> 70.0 + 5 -> 100.0 + 6 -> 150.0 + 7 -> 225.0 + 8 -> 275.0 + else -> 0.0 + } + } + else + { + when(room) + { + 1 -> 60.0 + 2 -> 90.0 + 3 -> 150.0 + 4 -> 215.0 + 5 -> 300.0 + 6 -> 450.0 + 7 -> 675.0 + 8 -> 825.0 + else -> 0.0 + } + } + } + + fun resetObjectVarbits(player: Player) + { + setVarp(player, 821, 0) + setVarp(player, 820, 0) + PlunderData.doorVarbits.forEach { setVarbit(player, it, 0) } + } + + fun openOverlay(player: Player) + { + player.interfaceManager.openOverlay(Component(Components.NTK_OVERLAY_428)) + updateOverlay(player) + } + + fun updateOverlay(player: Player) + { + 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) + { + 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] + } + + fun rollSceptre(player: Player): Boolean + { + val room = getRoom(player)!!.room + val chance = when(room) + { + 1 -> 1500 + 2 -> 1350 + 3 -> 1250 + 4 -> 1150 + else -> 1000 + } + + if(RandomFunction.roll(chance)) + { + expel(player, true) + runTask(player, 2) + { + addItemOrDrop(player, Items.PHARAOHS_SCEPTRE_9050) + sendNews("${player.username} has received a Pharaoh's Sceptre while doing Pyramid Plunder!") + } + return true + } + return false + } + + fun getSarcophagusXp(player: Player) : Double + { + val room = getRoom(player)!!.room + return when(room) + { + 1 -> 20.0 + 2 -> 30.0 + 3 -> 50.0 + 4 -> 70.0 + 5 -> 100.0 + 6 -> 150.0 + 7 -> 225.0 + 8 -> 275.0 + else -> 0.0 + } + } + + fun getChestXp(player: Player): Double + { + if (getRoom(player) == null) { + expel(player, false) + return 0.0 + } + val room = getRoom(player)!!.room + return when(room) + { + 1 -> 60.0 + 2 -> 90.0 + 3 -> 150.0 + 4 -> 215.0 + 5 -> 300.0 + 6 -> 450.0 + 7 -> 675.0 + 8 -> 825.0 + else -> 0.0 + } * 0.66 + } + + fun getDoorXp(player: Player, lockpick: Boolean) : Double + { + val room = getRoom(player)?.room ?: return 0.0 + var reward = when(room) + { + 1 -> 60.0 + 2 -> 90.0 + 3 -> 150.0 + 4 -> 215.0 + 5 -> 300.0 + 6 -> 450.0 + 7 -> 675.0 + 8 -> 825.0 + else -> 0.0 + } * 0.66 + + if(lockpick) + reward /= 2.0 + + return reward + } + + fun rollArtifact(player: Player, tier: Int) : Int + { + //tier 1 -> urn + //tier 2 -> sarcophagus + //tier 3 -> chest + val room = getRoom(player)!!.room + val divisor = (room * 2) * (tier * 35) + val goldRate = divisor / 650.0 + val stoneRate = divisor / 250.0 + + val roll = RandomFunction.RANDOM.nextDouble() + if(goldRate > roll) + return PlunderData.artifacts[2].random() + if(stoneRate > roll) + return PlunderData.artifacts[1].random() + return PlunderData.artifacts[0].random() + } + + fun checkEntranceSwitch() + { + if(System.currentTimeMillis() > PlunderData.nextEntranceSwitch) + { + PlunderData.currentEntrance = PlunderData.pyramidEntranceVarbits.random() + PlunderData.nextEntranceSwitch = System.currentTimeMillis() + (60000 * 15) + + } + } + + fun checkEntrance(door: Node): Boolean + { + return door.asScenery().definition.varbitID == PlunderData.currentEntrance + } + + fun rollUrnSuccess(player: Player, charmed: Boolean = false): Boolean + { + val level = getDynLevel(player, Skills.THIEVING) + + if (getRoom(player) == null) { + return false + } + + val room = getRoom(player)!!.room + return RandomFunction.random(level) > (room * if(charmed) 2 else 4) + } +} diff --git a/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMinigame.kt b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMinigame.kt new file mode 100644 index 000000000..057e5a94e --- /dev/null +++ b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMinigame.kt @@ -0,0 +1,395 @@ +package content.minigame.pyramidplunder + +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.Direction +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 core.game.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + + +/** + * The "controller" class for pyramid plunder. Handles per-tick updates, logout hooks, and defines interaction listeners for the minigame. + * @author Ceikry + */ +class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener { + override fun tick() { + val playersToExpel = PlunderUtils.decrementTimeRemaining() + playersToExpel.forEach {player -> PlunderUtils.expel(player, false) } + PlunderUtils.checkEntranceSwitch() + } + + override fun logout(player: Player) { + if(PlunderUtils.hasPlayer(player)) + { + player.location = Location.create(3288, 2802, 0) + PlunderUtils.unregisterPlayer(player) + } + } + + override fun defineListeners() { + val CHECK_ANIM = 3572 + val URN_CHECK = 4340 + val URN_BIT = 4341 + val URN_SUCCESS = 4342 + val PUSH_LID_START = 4343 + val PUSH_LID_LOOP = 4344 + val PUSH_LID_FINISH = 4345 + val CHARM_ANIM = 1877 + val OPEN_CHEST_ANIM = 536 + + val SPEAR_OBJ_ANIM = 459 + val SNAKE_URN_ANIM = 4335 + + val SEALED_URNS = intArrayOf(Scenery.URN_16501, Scenery.URN_16502, Scenery.URN_16503) + val OPEN_URNS = intArrayOf(Scenery.URN_16505, Scenery.URN_16506, Scenery.URN_16507) + val SNAKE_URNS = intArrayOf(Scenery.URN_16509, Scenery.URN_16510, Scenery.URN_16511) + val CHARMED_SNAKES = intArrayOf(Scenery.URN_16513, Scenery.URN_16514, Scenery.URN_16515) + val ENTRANCES = intArrayOf(16484, 16487, 16490, 16493) //All Scenery.AN_ANONYMOUS_LOOKING_DOOR_16484/etc + val SARCOPHAGUS = Scenery.SARCOPHAGUS_16495 + + on(Scenery.SPEARTRAP_16517, IntType.SCENERY, "pass"){ player, node -> + val anim = Animation(CHECK_ANIM) + val duration = animationDuration(anim) + + if(player.skills.getStaticLevel(Skills.THIEVING) < PlunderUtils.getRoomLevel(player)) + { + sendDialogue(player, "You need a Thieving level of ${PlunderUtils.getRoomLevel(player)} to do that.") + return@on true + } + + val spearDir = PlunderUtils.getRoom(player)!!.spearDirection + + val pastSpears = (spearDir == Direction.NORTH && player.location.y > node.location.y) + || (spearDir == Direction.SOUTH && player.location.y < node.location.y) + || (spearDir == Direction.EAST && player.location.x > node.location.x) + || (spearDir == Direction.WEST && player.location.x < node.location.x) + + if(pastSpears) + { + sendDialogue(player, "I have no reason to do that.") + return@on true + } + + animate(player, anim) + sendMessage(player, "You carefully try to temporarily disable the trap.") + player.pulseManager.run(object : Pulse(duration) { + override fun pulse(): Boolean { + if(RandomFunction.roll(5)) + { + val dest = PlunderUtils.getSpearDestination(player) + player.walkingQueue.reset() + player.walkingQueue.addPath(dest.x,dest.y) + rewardXP(player, Skills.THIEVING, 10.0) + return true + } + if(RandomFunction.roll(20)) + { + animateScenery(node.asScenery(), SPEAR_OBJ_ANIM) + impact(player, RandomFunction.random(1,5)) + sendChat(player, "Ouch!") + sendMessage(player, "You fail to disable the trap.") + return true + } + animate(player, anim) + return false + } + }) + return@on true + } + + on(intArrayOf(*SEALED_URNS, *SNAKE_URNS), IntType.SCENERY, "search") { player, node -> + animate(player, URN_CHECK) + player.faceLocation(node.location) + lock(player, 1) + runTask(player, 1) { + if(!PlunderUtils.rollUrnSuccess(player)) + { + animate(player, URN_BIT) + sendMessage(player, "You've been bitten by something moving around in the urn.") + impact(player, RandomFunction.random(1,5)) + 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)) + setVarbit(player, node.asScenery().definition.varbitID, 1) + } + } + return@on true + } + + on(SEALED_URNS, IntType.SCENERY, "check for snakes") { player, node -> + val urn = node.asScenery() + animate(player, URN_CHECK) + player.faceLocation(node.location) + lock(player, 1) + runTask(player, 1){ + animate(player, URN_BIT) + setVarbit(player, urn.definition.varbitID, 2) + animateScenery(urn, SNAKE_URN_ANIM) + } + return@on true + } + + on(SNAKE_URNS, IntType.SCENERY, "charm snake") { player, node -> + if(!inInventory(player, Items.SNAKE_CHARM_4605)) + { + sendMessage(player, "You need a snake charm to charm a snake.") + return@on true + } + lock(player, 2) + animate(player, CHARM_ANIM) + runTask(player, 1) + { + setVarbit(player, node.asScenery().definition.varbitID, 3) + sendMessage(player, "You charm the snake with your music.") + } + return@on true + } + + on(CHARMED_SNAKES, IntType.SCENERY, "search") { player, node -> + animate(player, URN_CHECK) + player.faceLocation(node.location) + lock(player, 1) + runTask(player, 1) { + if(!PlunderUtils.rollUrnSuccess(player, true)) + { + animate(player, URN_BIT) + sendMessage(player, "You've been bitten by something moving around in the urn.") + impact(player, RandomFunction.random(1,5)) + 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) + setVarbit(player, node.asScenery().definition.varbitID, 1) + } + } + return@on true + } + + on(OPEN_URNS, IntType.SCENERY, "search") { player, _ -> + sendMessage(player, "You've already looted this urn.") + return@on true + } + + 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) + player.pulseManager.run(object : Pulse(2) + { + override fun pulse(): Boolean { + animate(player, PUSH_LID_LOOP) + 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){ + setVarbit(player, node.asScenery().definition.varbitID, 1) + rewardXP(player, Skills.STRENGTH, PlunderUtils.getSarcophagusXp(player)) + if(RandomFunction.roll(25)) + { + val mummy = PyramidPlunderMummyNPC(player.location, player) + mummy.isRespawn = false + mummy.init() + mummy.attack(player) + } + else if(!PlunderUtils.rollSceptre(player)) + { + addItemOrDrop(player, PlunderUtils.rollArtifact(player, 2)) + } + } + return true + } + }) + return@on true + } + + 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 = PyramidPlunderSwarmNPC(player.location, player) + swarm.isRespawn = false + swarm.init() + swarm.attack(player) + impact(player, RandomFunction.random(1,5)) + } + else + { + rewardXP(player, Skills.THIEVING, PlunderUtils.getChestXp(player)) + if(!PlunderUtils.rollSceptre(player)) + addItemOrDrop(player, PlunderUtils.rollArtifact(player, 3)) + } + setVarbit(player, node.asScenery().definition.varbitID, 1) + } + return@on true + } + + on(Scenery.TOMB_DOOR_16475, IntType.SCENERY, "pick-lock") { player, node -> + val anim = Animation(CHECK_ANIM) + val duration = animationDuration(anim) + + if(PlunderUtils.getRoom(player)!!.room == 8) + { + sendMessage(player, "This is the final room. I should probably just leave instead.") + return@on true + } + + animate(player, anim) + lock(player, duration) + val rate = + if(inInventory(player, Items.LOCKPICK_1523)) + { + sendMessage(player, "You attempt to open the door.") + 2 + } + else + { + sendMessage(player, "You attempt to open the door. Lockpicks would make it easier...") + 3 + } + runTask(player, duration){ + if(RandomFunction.roll(rate)) + { + val varbitId = node.asScenery().definition.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.") + setVarbit(player, varbitId, 1) + } + } + else + { + sendMessage(player, "Your attempt fails.") + } + } + return@on true + } + + on(Scenery.TOMB_DOOR_16458, IntType.SCENERY, "leave tomb") { player, _ -> + openDialogue(player, object : DialogueFile() + { + override fun handle(componentID: Int, buttonID: Int) { + when(stage) + { + 0 -> options("Yes, I'm sure I want to leave.", "No, never mind.").also { stage++ } + 1 -> when(buttonID) + { + 1 -> { + end() + teleport(player, Location.create(3288, 2802, 0)) + PlunderUtils.unregisterPlayer(player) + PlunderUtils.resetOverlay(player) + PlunderUtils.resetObjectVarbits(player) + } + 2 -> end() + } + } + } + }) + return@on true + } + + on(ENTRANCES, IntType.SCENERY, "search") { player, door -> + if(!getAttribute(player, "tarik-spoken-to", false)) + { + sendDialogue(player, "I should probably try to find out more about this place before I try to break in.") + return@on true + } + val anim = Animation(CHECK_ANIM) + val duration = animationDuration(anim) + + animate(player, anim) + sendMessage(player, "You use your thieving skills to search the stone panel.") + player.pulseManager.run(object : Pulse(duration){ + override fun pulse(): Boolean { + if(RandomFunction.roll(3)) + { + setAttribute(player, "pyramid-entrance", player.location.transform(0,0,0)) + sendMessage(player, "You find a door! You open it.") + if(PlunderUtils.checkEntrance(door)) { + teleport(player, GUARDIAN_ROOM) + rewardXP(player, Skills.THIEVING, 20.0) + } + else + teleport(player, EMPTY_ROOM) + + return true + } + animate(player, anim) + return false + } + }) + return@on true + } + + on(Scenery.TOMB_DOOR_16459, IntType.SCENERY, "leave tomb") { player, _ -> + teleport(player, getAttribute(player, "pyramid-entrance", Location.create(3288, 2802, 0))) + return@on true + } + + on(NPCs.GUARDIAN_MUMMY_4476, IntType.NPC, "start-minigame") { player, _ -> + if(!getAttribute(player, "pp:mummy-spoken-to", false)) + { + openDialogue(player, NPCs.GUARDIAN_MUMMY_4476) + return@on true + } + + join(player) + return@on true + } + } + + companion object { + @JvmStatic val GUARDIAN_ROOM = Location.create(1968, 4420, 2) + @JvmStatic val EMPTY_ROOM = Location.create(1934, 4450, 2) + + @JvmStatic fun join(player: Player) + { + if(PlunderUtils.hasPlayer(player)) + { + sendMessage(player, "[PLUNDER] You should never see this message. Please report this.") + return + } + PlunderUtils.registerPlayer(player) + PlunderUtils.loadNextRoom(player) + PlunderUtils.openOverlay(player) + } + } +} diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderMummyNPC.java b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMummyNPC.java similarity index 87% rename from Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderMummyNPC.java rename to Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMummyNPC.java index 0be11faf7..9d18d2b26 100644 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderMummyNPC.java +++ b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMummyNPC.java @@ -1,4 +1,4 @@ -package core.game.content.activity.pyramidplunder; +package content.minigame.pyramidplunder; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.world.map.Location; @@ -15,8 +15,8 @@ public final class PyramidPlunderMummyNPC extends PyramidPlunderNPC { */ private static final int[] IDS = new int[] { 1958 }; - public PyramidPlunderMummyNPC(int id, Location location, Player player) { - super(id, location, player); + public PyramidPlunderMummyNPC(Location location, Player player) { + super(IDS[0], location, player); } @Override diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderNPC.java b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderNPC.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderNPC.java rename to Server/src/main/content/minigame/pyramidplunder/PyramidPlunderNPC.java index 4b062cb62..329887b34 100644 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderNPC.java +++ b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderNPC.java @@ -1,11 +1,12 @@ -package core.game.content.activity.pyramidplunder; +package content.minigame.pyramidplunder; 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.AbstractNPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.path.Pathfinder; @@ -115,12 +116,12 @@ public abstract class PyramidPlunderNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (entity instanceof Player && entity != player) { ((Player) entity).getPacketDispatch().sendMessage("It's not after you."); return false; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override @@ -150,7 +151,7 @@ public abstract class PyramidPlunderNPC extends AbstractNPC { public boolean pulse() { return false; } - }, "movement"); + }, PulseType.STANDARD); face(player); } diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderSwarmNPC.java b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderSwarmNPC.java similarity index 87% rename from Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderSwarmNPC.java rename to Server/src/main/content/minigame/pyramidplunder/PyramidPlunderSwarmNPC.java index 61b4f03e9..f2d919470 100644 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderSwarmNPC.java +++ b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderSwarmNPC.java @@ -1,4 +1,4 @@ -package core.game.content.activity.pyramidplunder; +package content.minigame.pyramidplunder; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.world.map.Location; @@ -15,8 +15,8 @@ public final class PyramidPlunderSwarmNPC extends PyramidPlunderNPC { */ private static final int[] IDS = new int[] { 2001 }; - public PyramidPlunderSwarmNPC(int id, Location location, Player player) { - super(id, location, player); + public PyramidPlunderSwarmNPC(Location location, Player player) { + super(IDS[0], location, player); } @Override diff --git a/Server/src/main/java/core/game/interaction/npc/RechargeSceptre.java b/Server/src/main/content/minigame/pyramidplunder/RechargeSceptre.java similarity index 99% rename from Server/src/main/java/core/game/interaction/npc/RechargeSceptre.java rename to Server/src/main/content/minigame/pyramidplunder/RechargeSceptre.java index 1ca605cc0..00bd639d6 100644 --- a/Server/src/main/java/core/game/interaction/npc/RechargeSceptre.java +++ b/Server/src/main/content/minigame/pyramidplunder/RechargeSceptre.java @@ -1,6 +1,6 @@ -package core.game.interaction.npc; +package content.minigame.pyramidplunder; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/interaction/npc/SimonScepterInteraction.java b/Server/src/main/content/minigame/pyramidplunder/SimonScepterInteraction.java similarity index 96% rename from Server/src/main/java/core/game/interaction/npc/SimonScepterInteraction.java rename to Server/src/main/content/minigame/pyramidplunder/SimonScepterInteraction.java index 3c5972b0b..3f4d0515f 100644 --- a/Server/src/main/java/core/game/interaction/npc/SimonScepterInteraction.java +++ b/Server/src/main/content/minigame/pyramidplunder/SimonScepterInteraction.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.minigame.pyramidplunder; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; diff --git a/Server/src/main/java/core/game/content/dialogue/DelMontyDialogue.java b/Server/src/main/content/minigame/sorceress/DelMontyDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/DelMontyDialogue.java rename to Server/src/main/content/minigame/sorceress/DelMontyDialogue.java index b3330f8ea..5286f5e15 100644 --- a/Server/src/main/java/core/game/content/dialogue/DelMontyDialogue.java +++ b/Server/src/main/content/minigame/sorceress/DelMontyDialogue.java @@ -1,6 +1,8 @@ -package core.game.content.dialogue; +package content.minigame.sorceress; import core.game.container.impl.EquipmentContainer; +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; diff --git a/Server/src/main/java/core/game/interaction/object/sorceress/GardenObjectsPlugin.kt b/Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt similarity index 96% rename from Server/src/main/java/core/game/interaction/object/sorceress/GardenObjectsPlugin.kt rename to Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt index b79512f50..5ebad5751 100644 --- a/Server/src/main/java/core/game/interaction/object/sorceress/GardenObjectsPlugin.kt +++ b/Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt @@ -1,25 +1,16 @@ -package core.game.interaction.`object`.sorceress +package content.minigame.sorceress -import rs09.plugin.PluginManager.definePlugin -import rs09.game.world.GameWorld.Pulser import core.plugin.Initializable -import core.game.interaction.OptionHandler import core.plugin.Plugin -import core.cache.def.impl.SceneryDefinition import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler -import core.game.node.Node import core.game.node.scenery.Scenery 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.game.node.item.Item -import core.game.system.task.LocationLogoutTask -import core.game.system.task.LogoutTask import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.update.flag.context.Animation @@ -28,12 +19,14 @@ import core.net.packet.PacketRepository import core.net.packet.context.MinimapStateContext import core.net.packet.out.MinimapState import core.tools.RandomFunction -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld -import rs09.plugin.PluginManager +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() { +class GardenObjectsPlugin : InteractionListener { val SQIRK_TREES = intArrayOf(21767, 21768, 21769, 21766) val FOUNTAIN = 21764 @@ -44,13 +37,13 @@ class GardenObjectsPlugin : InteractionListener() { override fun defineListeners() { SqirkJuicePlugin().newInstance(null) SqirkMakingDialogue().init() - PluginManager.definePlugin(SorceressGardenObject()) + ClassScanner.definePlugin(SorceressGardenObject()) - on(SQIRK_TREES, SCENERY, "pick-fruit"){player, node -> + on(SQIRK_TREES, IntType.SCENERY, "pick-fruit"){ player, node -> val def = SeasonDefinitions.forTreeId(node.id) if (def != null) { player.lock() - player.addExtension(LogoutTask::class.java, LocationLogoutTask(99, def.respawn)) + player.logoutListeners["garden"] = {p -> p.location = def.respawn} player.animate(PICK_FRUIT) player.skills.addExperience(Skills.THIEVING, def.exp, true) player.skills.addExperience(Skills.FARMING, def.farmExp, true) @@ -63,7 +56,7 @@ class GardenObjectsPlugin : InteractionListener() { PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) } else if (counter == 3) player.properties.teleportLocation = def.respawn else if (counter == 4) { player.unlock() - player.removeExtension(LogoutTask::class.java) + player.logoutListeners.remove("garden") player.packetDispatch.sendMessage("An elemental force emanating from the garden teleports you away.") PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) player.interfaceManager.close() @@ -80,7 +73,7 @@ class GardenObjectsPlugin : InteractionListener() { } - on(FOUNTAIN, SCENERY, "drink-from"){player, _ -> + on(FOUNTAIN, IntType.SCENERY, "drink-from"){ player, _ -> player.lock() GameWorld.Pulser.submit(object : Pulse(1, player) { var counter = 0 @@ -108,7 +101,7 @@ class GardenObjectsPlugin : InteractionListener() { return@on true } - on(SHELVES, SCENERY, "search"){player, node -> + on(SHELVES, IntType.SCENERY, "search"){ player, node -> if (player.inventory.freeSlots() < 1) { player.sendMessage("You don't have enough space in your inventory to take a beer glass.") } else { @@ -119,7 +112,7 @@ class GardenObjectsPlugin : InteractionListener() { return@on true } - on(HERBS, SCENERY, "pick"){player, node -> + on(HERBS, IntType.SCENERY, "pick"){ player, node -> val herbDef = HerbDefinition.forId(node.getId()) if (herbDef != null) { handleElementalGarden(player, node.asScenery(), herbDef) @@ -137,7 +130,7 @@ class GardenObjectsPlugin : InteractionListener() { */ private fun handleElementalGarden(player: Player, `object`: Scenery, herbDef: HerbDefinition) { player.lock() - player.addExtension(LogoutTask::class.java, LocationLogoutTask(99, herbDef.respawn)) + player.logoutListeners["garden"] = {p -> p.location = herbDef.respawn} player.animate(ANIMATION) player.skills.addExperience(Skills.FARMING, herbDef.exp, true) GameWorld.Pulser.submit(object : Pulse(2, player) { @@ -156,7 +149,7 @@ class GardenObjectsPlugin : InteractionListener() { PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) player.interfaceManager.close() player.interfaceManager.closeOverlay() - player.removeExtension(LogoutTask::class.java) + player.logoutListeners.remove("garden") player.unlock() return true } @@ -460,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 } @@ -479,7 +472,7 @@ class GardenObjectsPlugin : InteractionListener() { * @date 31/12/2013 */ @Initializable - class OsmanDialogue : DialoguePlugin { + class OsmanDialogue : core.game.dialogue.DialoguePlugin { /** * Represents the quest instance. */ @@ -505,13 +498,13 @@ class GardenObjectsPlugin : InteractionListener() { */ constructor(player: Player?) : super(player) {} - override fun newInstance(player: Player): DialoguePlugin { + override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { return OsmanDialogue(player) } 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.") @@ -827,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 -> { @@ -1358,7 +1351,7 @@ class GardenObjectsPlugin : InteractionListener() { * Dialogue for Sqirk making * @author SonicForce41 */ - class SqirkMakingDialogue : DialoguePlugin { + class SqirkMakingDialogue : core.game.dialogue.DialoguePlugin { private var dialogueId = 0 private var definition: GardenObjectsPlugin.SeasonDefinitions? = null @@ -1391,7 +1384,7 @@ class GardenObjectsPlugin : InteractionListener() { return true } - override fun newInstance(player: Player): DialoguePlugin { + override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { return SqirkMakingDialogue(player) } @@ -1400,7 +1393,7 @@ class GardenObjectsPlugin : InteractionListener() { when (dialogueId) { 0 -> interpreter.sendDialogues( player, - FacialExpression.THINKING, + core.game.dialogue.FacialExpression.THINKING, "I should get an empty beer glass to", "hold the juice before I squeeze the fruit." ) @@ -1409,7 +1402,7 @@ class GardenObjectsPlugin : InteractionListener() { if (definition == null) end() interpreter.sendDialogues( player, - FacialExpression.THINKING, + core.game.dialogue.FacialExpression.THINKING, "I think I should wait till I have", "enough fruits to make a full glass." ) diff --git a/Server/src/main/java/core/game/content/dialogue/SorcceresDialouge.java b/Server/src/main/content/minigame/sorceress/SorcceresDialouge.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/SorcceresDialouge.java rename to Server/src/main/content/minigame/sorceress/SorcceresDialouge.java index 439fc9acf..b795605b2 100644 --- a/Server/src/main/java/core/game/content/dialogue/SorcceresDialouge.java +++ b/Server/src/main/content/minigame/sorceress/SorcceresDialouge.java @@ -1,9 +1,11 @@ -package core.game.content.dialogue; +package content.minigame.sorceress; +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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.plugin.Initializable; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/interaction/npc/sorceress_app/SorceressApprenticeDialogue.java b/Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java similarity index 95% rename from Server/src/main/java/core/game/interaction/npc/sorceress_app/SorceressApprenticeDialogue.java rename to Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java index 1a87a12ff..bc3aa169b 100644 --- a/Server/src/main/java/core/game/interaction/npc/sorceress_app/SorceressApprenticeDialogue.java +++ b/Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java @@ -1,15 +1,18 @@ -package core.game.interaction.npc.sorceress_app; +package content.minigame.sorceress; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; 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 rs09.game.world.GameWorld; +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/java/core/game/interaction/npc/sorceress_app/SorceressApprenticePlugin.java b/Server/src/main/content/minigame/sorceress/SorceressApprenticePlugin.java similarity index 77% rename from Server/src/main/java/core/game/interaction/npc/sorceress_app/SorceressApprenticePlugin.java rename to Server/src/main/content/minigame/sorceress/SorceressApprenticePlugin.java index e927232a0..c2993ce52 100644 --- a/Server/src/main/java/core/game/interaction/npc/sorceress_app/SorceressApprenticePlugin.java +++ b/Server/src/main/content/minigame/sorceress/SorceressApprenticePlugin.java @@ -1,18 +1,14 @@ -package core.game.interaction.npc.sorceress_app; +package content.minigame.sorceress; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; 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.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Teleport option for Sorceress Apprentice @@ -39,7 +35,7 @@ public class SorceressApprenticePlugin extends OptionHandler { public Plugin newInstance(Object arg) throws Throwable { NPCDefinition.forId(5532).getHandlers().put("option:teleport", this); SceneryDefinition.forId(21781).getHandlers().put("option:climb-up", this); - PluginManager.definePlugin(new SorceressApprenticeDialogue()); + ClassScanner.definePlugin(new SorceressApprenticeDialogue()); return this; } diff --git a/Server/src/main/java/core/game/node/entity/npc/other/SorceressElementalNPC.java b/Server/src/main/content/minigame/sorceress/SorceressElementalNPC.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/npc/other/SorceressElementalNPC.java rename to Server/src/main/content/minigame/sorceress/SorceressElementalNPC.java index fb52bdb5f..af3dbc8e7 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/SorceressElementalNPC.java +++ b/Server/src/main/content/minigame/sorceress/SorceressElementalNPC.java @@ -1,15 +1,15 @@ -package core.game.node.entity.npc.other; +package content.minigame.sorceress; import java.util.List; import core.game.component.Component; import core.game.node.entity.Entity; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.DeathTask; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/interaction/object/sorceress/SorceressGardenObject.java b/Server/src/main/content/minigame/sorceress/SorceressGardenObject.java similarity index 92% rename from Server/src/main/java/core/game/interaction/object/sorceress/SorceressGardenObject.java rename to Server/src/main/content/minigame/sorceress/SorceressGardenObject.java index d897c0ec0..bfe8136ae 100644 --- a/Server/src/main/java/core/game/interaction/object/sorceress/SorceressGardenObject.java +++ b/Server/src/main/content/minigame/sorceress/SorceressGardenObject.java @@ -1,7 +1,7 @@ -package core.game.interaction.object.sorceress; +package content.minigame.sorceress; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; 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 new file mode 100644 index 000000000..04fe7686a --- /dev/null +++ b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt @@ -0,0 +1,765 @@ +package content.minigame.vinesweeper + +import BlinkinDialogue +import FarmerDialogue.Companion.FARMER_FLAG_LINES +import WinkinDialogue +import content.minigame.vinesweeper.Vinesweeper.Companion.FARMERS +import content.minigame.vinesweeper.Vinesweeper.Companion.FARMER_CLEAR_RADIUS +import content.minigame.vinesweeper.Vinesweeper.Companion.HOLES +import content.minigame.vinesweeper.Vinesweeper.Companion.NUMBERS +import content.minigame.vinesweeper.Vinesweeper.Companion.RABBITS +import content.minigame.vinesweeper.Vinesweeper.Companion.SEED_LOCS +import content.minigame.vinesweeper.Vinesweeper.Companion.populateSeeds +import content.minigame.vinesweeper.Vinesweeper.Companion.scheduleNPCs +import content.minigame.vinesweeper.Vinesweeper.Companion.sendPoints +import core.api.* +import core.cache.def.impl.ItemDefinition +import core.game.component.Component +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.interaction.MovementPulse +import core.game.node.entity.Entity +import core.game.node.entity.combat.DeathTask +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 + +class Vinesweeper : InteractionListener, InterfaceListener, MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(6473)) + } + + override fun areaEnter(entity: Entity) { + if(entity is Player) + { + openOverlay(entity, Components.RABBIT_OVERLAY_689) + sendUpdatedPoints(entity) + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if(entity is Player) { + entity.interfaceManager.closeOverlay() + if(!logout) { + 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) + entity.inventory.remove(Item(Items.FLAG_12625, flags)) + } + val roots = entity.inventory.getAmount(Item(Items.OGLEROOT_12624)) + if(roots > 0) { + entity.inventory.remove(Item(Items.OGLEROOT_12624, roots)) + entity.inventory.add(Item(Items.COINS_995, roots * 10)) + } + } + } + } + + override fun defineListeners() { + populateSeeds() + 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) + 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 -> + player.interfaceManager.open(Component(INSTRUCTION_SIGNS[node.id]!!)) + return@on true + } + on(HOLES, IntType.SCENERY, "dig") { player, node -> + if(!player.inventory.contains(Items.SPADE_952, 1)) { + // TODO (crash): authenticity + player.sendMessage("You need a spade to dig here.") + } else { + player.visualize(Animation(Animations.HUMAN_VINESWEEPER_DIG_8709), Graphics(Gfx.VINESWEEPER_DIRT_DIG_1543)) + dig(player, node.location) + } + return@on true + } + onUseWith(IntType.SCENERY,Items.SPADE_952,*HOLES) { player, _, with -> + player.visualize(Animation(Animations.HUMAN_VINESWEEPER_DIG_8709), Graphics(Gfx.VINESWEEPER_DIRT_DIG_1543)) + dig(player, with.location) + return@onUseWith true + } + on(HOLES, IntType.SCENERY, "flag") { player, node -> + val hole = node as Scenery + var count = 0 + if(player.location != node.location) { + plantFlag(player,hole) + return@on true + } + GameWorld.Pulser.submit(object : MovementPulse(player, hole, hole.destinationFlag) { + override fun pulse(): Boolean { + when(count++) { + 0 -> { + player.faceLocation(hole.location) + return false + } + 1 -> { + plantFlag(player,hole) + return true + } + } + return true + } + }) + return@on true + } + onUseWith(IntType.SCENERY,Items.FLAG_12625,*HOLES) { player, _, with -> + val hole = with as Scenery + plantFlag(player,hole) + return@onUseWith true + } + on(HOLES, IntType.SCENERY, "inspect") { player, node -> + player.animate(Animation(Animations.HUMAN_INSPECT_HOLE_8710)) + player.lock(5) + GameWorld.Pulser.submit(object : Pulse(5) { + override fun pulse(): Boolean { + val msg = when(RandomFunction.random(0, 7)) { + 0 -> "You don't see anything interesting. You can't be sure if there's a seed there or not." + 1 -> "You get some mud in your eye and it stings! You have no idea what is in the hole." + 2 -> "The mud seems to be too thick to see what is there." + 3 -> "A slimy worm wriggles out of the mud, making you jump and lose concentration. You're not sure if there is a seed here or not." + else -> if(isSeed(node.location)) { + "You notice a seed hidden in the dirt." + } else { + "You are certain there is no seed planted here." + } + } + sendDialogue(player, msg) + return true + } + }) + return@on true + } + on(NPCs.MRS_WINKIN_7132, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player, WinkinDialogue(), npc) + return@on true + } + on(NPCs.MRS_WINKIN_7132, IntType.NPC, "trade") { player, _ -> + player.interfaceManager.open(Component(686)) + return@on true + } + on(NPCs.MRS_WINKIN_7132, IntType.NPC, "buy flags") { player, npc -> + val dialogue = WinkinDialogue() + dialogue.stage = 20 + openDialogue(player, dialogue, npc) + return@on true + } + on(RABBITS, IntType.NPC, "feed") { player, node -> + val rabbit = node as NPC + feedRabbit(player,rabbit) + return@on true + } + onUseWith(IntType.NPC,Items.OGLEROOT_12624,*RABBITS) { player, _, with -> + val rabbit = with as NPC + feedRabbit(player,rabbit) + return@onUseWith true + } + on(NPCs.FARMER_BLINKIN_7131, IntType.NPC, "talk-to") { player, npc -> + //player.interfaceManager.open(Component(TUTORIAL)) + openDialogue(player, BlinkinDialogue(), npc) + return@on true + } + on(NPCs.FARMER_BLINKIN_7131, IntType.NPC, "buy-flags") { player, npc -> + val dialogue = BlinkinDialogue() + dialogue.stage = 21 + openDialogue(player, dialogue, npc) + return@on true + } + on(NPCs.FARMER_BLINKIN_7131, IntType.NPC, "buy-roots") { player, npc -> + val dialogue = BlinkinDialogue() + dialogue.stage = 40 + openDialogue(player, dialogue, npc) + return@on true + } + } + + override fun defineInterfaceListeners() { + on(IFACE) { player, _, opcode, buttonID, _, _ -> + when(opcode) { + Opcode.VALUE.value -> { + when(buttonID) { + TRADE_FOR_XP_BUTTON -> { + player.packetDispatch.sendInterfaceConfig(686, 60, false) + } + XP_CONFIRM -> { + player.packetDispatch.sendInterfaceConfig(686, 60, true) + val level = player.skills.getStaticLevel(Skills.FARMING) + // TODO: more precise formula + val points_per_xp = if (level < 40) { max(1.0, 2.0*(40.0 - level.toDouble())/10.0) } else { 1.0 } + val points = player.getAttribute("vinesweeper:points", 0) + val xp = points / points_per_xp + player.skills.addExperience(Skills.FARMING, xp) + player.setAttribute("/save:vinesweeper:points", 0) + sendUpdatedPoints(player) + + } + XP_DENY -> { + player.packetDispatch.sendInterfaceConfig(686, 60, true) + } + else -> { + val reward = REWARDS[buttonID] ?: return@on true + player.sendMessage("${Item(reward.itemID).name}: ${reward.points} vinesweeper points") + } + } + } + Opcode.BUY1.value -> { + buy(player, buttonID, 1) + return@on true + } + Opcode.BUY5.value -> { + buy(player, buttonID, 5) + return@on true + } + Opcode.BUY10.value -> { + buy(player, buttonID, 10) + return@on true + } + Opcode.BUYX.value -> { + player.setAttribute("runscript") { amount: Int -> + buy(player, buttonID, amount) + } + player.dialogueInterpreter.sendInput(false, "Enter the amount:") + return@on true + } + else -> {} + } + return@on true + } + } + + data class SeedDestination(val player: Player, val loc: Location, val alive: Boolean) { + override fun equals(other: Any?): Boolean { + return if(other is SeedDestination) { + loc == other.loc + } else { + false + } + } + + override fun hashCode(): Int { + return loc.hashCode() + } + } + + companion object + { + val AVACH_NIMPORTO_LOC = Location.create(1637, 4709) + val SIGNS = intArrayOf(Sceneries.INSTRUCTION_SIGN_29461, Sceneries.INSTRUCTION_SIGN_29462, Sceneries.INSTRUCTION_SIGN_29463, Sceneries.INSTRUCTION_SIGN_29464) + val HOLES = intArrayOf(Sceneries.HOLE_29476, Sceneries.HOLE_29477, Sceneries.HOLE_29478) + val NUMBERS = intArrayOf(29447, 29448, 29449, 29450, 29451, 29452, 29453, 29454, 29455) + + val IFACE = 686 + val TRADE_FOR_XP_BUTTON = 53 + val XP_CONFIRM = 72 + val XP_DENY = 73 + + enum class Opcode(val value: Int) { + VALUE(155), + BUY1(196), + BUY5(124), + BUY10(199), + BUYX(234), + } + + data class Reward(val itemID: Int, val points: Int) {} + + val REWARDS = hashMapOf( + 18 to Reward(Items.TOMATO_SEED_5322, 10), + 19 to Reward(Items.SWEETCORN_SEED_5320, 150), + 20 to Reward(Items.STRAWBERRY_SEED_5323, 165), + 21 to Reward(Items.WATERMELON_SEED_5321, 680), + 22 to Reward(Items.GUAM_SEED_5291, 10), + 23 to Reward(Items.MARRENTILL_SEED_5292, 10), + 24 to Reward(Items.RANARR_SEED_5295, 4000), + 25 to Reward(Items.KWUARM_SEED_5299, 1000), + 26 to Reward(Items.TARROMIN_SEED_5293, 10), + 27 to Reward(Items.NASTURTIUM_SEED_5098, 10), + + 28 to Reward(Items.WOAD_SEED_5099, 30), + 29 to Reward(Items.LIMPWURT_SEED_5100, 70), + 30 to Reward(Items.ASGARNIAN_SEED_5308, 5), + 31 to Reward(Items.KRANDORIAN_SEED_5310, 20), + 32 to Reward(Items.REDBERRY_SEED_5101, 5), + 33 to Reward(Items.CADAVABERRY_SEED_5102, 5), + 34 to Reward(Items.DWELLBERRY_SEED_5103, 5), + 35 to Reward(Items.JANGERBERRY_SEED_5104, 10), + 36 to Reward(Items.WHITEBERRY_SEED_5105, 25), + + 37 to Reward(Items.POISON_IVY_SEED_5106, 30), + 38 to Reward(Items.ACORN_5312, 100), + 39 to Reward(Items.WILLOW_SEED_5313, 1800), + 40 to Reward(Items.MAPLE_SEED_5314, 12000), + 41 to Reward(Items.PINEAPPLE_SEED_5287, 10000), + 42 to Reward(Items.YEW_SEED_5315, 29000), + 43 to Reward(Items.PALM_TREE_SEED_5289, 35000), + 44 to Reward(Items.SPIRIT_SEED_5317, 55000), + 45 to Reward(Items.COMPOST_POTION4_6470, 5000), + 46 to Reward(Items.FLAG_12625, 50), + ) + + fun buy(player: Player, buttonID: Int, amount: Int) { + val reward = REWARDS[buttonID] ?: return + val cost = amount * reward.points + val points = player.getAttribute("vinesweeper:points", 0) + if(cost in 1 until points) { + val item = Item(reward.itemID, amount) + if(!player.inventory.add(item)) { + GroundItemManager.create(item, player) + } + player.incrementAttribute("/save:vinesweeper:points", -cost) + sendUpdatedPoints(player) + } else { + // TODO (crash): authenticity + player.sendMessage("You don't have enough points for that.") + } + } + + + val TUTORIAL = 685 + + val INSTRUCTION_SIGNS = hashMapOf( + 29463 to 684, + 29464 to 687, + 29462 to 688, + 29461 to 690 + ) + + val RABBITS = intArrayOf(NPCs.RABBIT_7125, NPCs.RABBIT_7126, NPCs.RABBIT_7127) + val FARMERS = intArrayOf(NPCs.FARMER_7128, NPCs.FARMER_7129, NPCs.FARMER_7130) + + val MAX_SEEDS = 300 + val FARMER_CLEAR_RADIUS = 3 + val VINESWEEPER_BORDERS = ZoneBorders(1600,4672,1663,4735) + + fun sendUpdatedPoints(player: Player) { + val points = player.getAttribute("vinesweeper:points", 0) + setVarbit(player, 4449, points) + } + + var SEED_LOCS: HashSet = HashSet() + + fun isSeed(loc: Location): Boolean { + val scenery = getScenery(loc) + return scenery != null && SEED_LOCS.contains(scenery.location) + } + + fun populateSeeds() { + 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)) { + SEED_LOCS.add(loc) + } + } + } + + 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() + player.visualize(Animation(Animations.HUMAN_PLANT_FLAG_8711),Graphics(Gfx.VINESWEEPER_PLANT_FLAG_1541)) + player.sendMessage("You add a flag to the patch.") + GameWorld.Pulser.submit(object : Pulse(2, player) { + override fun pulse(): Boolean { + SceneryBuilder.replace(hole, hole.transform(Sceneries.FLAG_29457)) + scheduleNPCs(player, hole.location, true, true) + player.unlock() + return true + } + }) + } else { + // TODO (crash): authenticity + player.sendMessage("You do not have a flag to place in the patch.") + } + } + + fun feedRabbit(player: Player, rabbit: NPC) { + val respawnDelay = 50 + if(rabbit.isInvisible || DeathTask.isDead(rabbit)) { return } + if(rabbit.getAttribute("dead", 0) > ticks) { return } + if(player.inventory.remove(Item(Items.OGLEROOT_12624, 1))) { + rabbit.setAttribute("dead",ticks + respawnDelay) + player.skills.addExperience(Skills.HUNTER, 30.0) + rabbit.sendChat("Squeak!") + rabbit.animate(Animation(Animations.RABBIT_EAT_OGLEROOT_8734)) + rabbit.lock(4) + GameWorld.Pulser.submit(object : Pulse(3, player) { + override fun pulse(): Boolean { + rabbit.finalizeDeath(player) + rabbit.properties.teleportLocation = rabbit.properties.spawnLocation + return true + } + }) + } else { + // TODO (crash): authenticity + player.sendMessage("You don't have any ogleroots to feed the rabbit.") + } + } + + fun dig(player: Player, loc: Location) { + if(isSeed(loc)) { + 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.") + scheduleNPCs(player, loc, false, false) + val scenery = getScenery(loc) + if(scenery != null) { + SceneryBuilder.replace(scenery, scenery.transform(Sceneries.DEAD_PLANT_29456)) + } + } else { + player.incrementAttribute("/save:vinesweeper:points", 1) + sendUpdatedPoints(player) + var count = 0 + for(dx in -1..1) { + for(dy in -1..1) { + if(isSeed(loc.transform(dx, dy, 0))) { + count += 1 + } + } + } + val scenery = getScenery(loc) + if(scenery != null) { + SceneryBuilder.replace(scenery, scenery.transform(NUMBERS[count])) + } + if(count == 0) { + for(dx in -1..1) { + for(dy in -1..1) { + val newLoc = loc.transform(dx, dy, 0) + if(isHole(newLoc)) + dig(player, newLoc) + } + } + } + } + } + + fun sendPoints(npc: NPC, dest: SeedDestination) { + val level = dest.player.skills.getStaticLevel(Skills.FARMING) + val points = RandomFunction.random(level, 4 * level) + dest.player.incrementAttribute("/save:vinesweeper:points", points) + dest.player.inventory.add(Item(Items.FLAG_12625, 1)) + sendUpdatedPoints(dest.player) + for (neighbor in RegionManager.getLocalPlayers(npc)) { + if (neighbor != dest.player) { + neighbor.incrementAttribute("/save:vinesweeper:points", points / 2) + sendUpdatedPoints(neighbor) + } + } + } + + fun isHole(loc: Location): Boolean { + val scenery = getScenery(loc) + return scenery != null && HOLES.contains(scenery.id) + } + + fun scheduleNPCs(player: Player, loc: Location, alive: Boolean, rabbit: Boolean) { + val dest = SeedDestination(player, loc, alive) + val ids = if(rabbit) { RABBITS + FARMERS } else { FARMERS } + for(npc in findLocalNPCs(player, ids, 30)) { + if(npc is VinesweeperNPC) { + npc.seedDestinations.add(dest) + npc.resetWalk() + } + } + } + + object VinesweeperTeleport { + @JvmStatic + fun teleport(npc: NPC, player: Player) { + if (hasTimerActive(player, "teleblock")) { + sendNPCDialogue(player, npc.id, "I can't do that, you're teleblocked!", core.game.dialogue.FacialExpression.OLD_ANGRY1) + return + } + // https://youtu.be/61jVjmXf8tU?t=45 + npc.graphics(Graphics(108)) + player.lock() + 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) { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 2 -> { + player.savedData.globalData.essenceTeleporter = npc.id + player.setAttribute("/save:vinesweeper:return-tele:x", npc.location.x) + player.setAttribute("/save:vinesweeper:return-tele:y", npc.location.y) + player.properties.teleportLocation = AVACH_NIMPORTO_LOC + } + 3 -> { + player.graphics(Graphics(110)) + player.unlock() + return true + } + } + return false + } + }) + } + } + } +} + +@Initializable +class VinesweeperNPC : AbstractNPC { + 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 { + return VinesweeperNPC(id, location) + } + + init { + walkRadius = 22 + } + + override fun getIds(): IntArray { + return RABBITS + FARMERS + } + 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) { + npc.seedDestinations.remove(dest) + npc.resetWalk() + } + } + val scenery = getScenery(dest.loc) + if(scenery != null) { + if(id in RABBITS) { + handleRabbitSeed(scenery, dest) + } else { + if(dest.alive) { + handleFarmerFlag(scenery, dest) + } else { + handleFarmerSeed(scenery, dest) + } + } + } + seedDestinations.remove(dest) + } + super.handleTickActions() + } + + override fun getMovementDestination(): Location? { + if(seedDestinations.size > 0) { + seedDestinations.sortBy { a -> a.loc.getDistance(location).toInt() } + return seedDestinations.first().loc + } else { + return super.getMovementDestination() + } + } + + fun handleRabbitSeed(scenery: Scenery, dest: Vinesweeper.SeedDestination) { + if(SEED_LOCS.contains(dest.loc)) { + val replacement = Sceneries.DEAD_PLANT_29456 + lock(4) + animate(Animation(Animations.RABBIT_EAT_SEED_8718)) + SceneryBuilder.replace(scenery, scenery.transform(replacement)) + scheduleNPCs(dest.player, dest.loc, alive = false, rabbit = false) + } else { + scheduleNPCs(dest.player, dest.loc, alive = true, rabbit = false) + } + } + fun handleFarmerSeed(scenery: Scenery, dest: Vinesweeper.SeedDestination) { + lock() + var i = 0 + animate(Animation(Animations.GNOME_FARMER_DIG_SEED_8730)) + GameWorld.Pulser.submit(object: Pulse(3) { + override fun pulse(): Boolean { + when(i++) { + 0 -> { + sendChat(FARMER_FLAG_LINES.FIND_PLANT.line) + } + 1 -> { + animate(Animation(Animations.GNOME_FARMER_SPADE_SMACK_8732)) + sendChat(FARMER_FLAG_LINES.DEAD_PLANT.line) + SceneryBuilder.replace(scenery, scenery.transform(HOLES[0])) + } + 2 -> { + animate(Animation(Animations.GNOME_FARMER_CLEAR_HOLES_8724)) + farmerClear(dest) + } + 3 -> { + unlock() + return true + } + } + return false + } + }) + } + fun handleFarmerFlag(scenery: Scenery, dest: Vinesweeper.SeedDestination) { + val npc = this + lock() + var i = 0 + animate(Animation(Animations.GNOME_FARMER_DIG_FLAG_8725)) + GameWorld.Pulser.submit(object: Pulse(3) { + override fun pulse(): Boolean { + when(i++) { + 0 -> { + sendChat(FARMER_FLAG_LINES.FIND_FLAG.line) + } + 1 -> { + SceneryBuilder.replace(scenery, scenery.transform(HOLES[0])) + if(SEED_LOCS.contains(dest.loc)) { + sendChat(FARMER_FLAG_LINES.FIND_SEED.line) + animate(Animation(Animations.GNOME_FARMER_HOORAY_8731)) + sendPoints(npc,dest) + i++ + } else { + sendChat(FARMER_FLAG_LINES.NO_SEED.line) + } + } + 2 -> { + sendChat(FARMER_FLAG_LINES.KEEP_FLAG.line) + i++ + } + 3 -> { + animate(Animation(Animations.GNOME_FARMER_CLEAR_HOLES_8724)) + farmerClear(dest) + + } + 4 -> { + unlock() + return true + } + } + return false + } + }) + } + + fun farmerClear(dest: Vinesweeper.SeedDestination) { + for(dx in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) { + for(dy in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) { + val toClear = getScenery(dest.loc.transform(dx, dy, 0)) + if(toClear != null && intArrayOf(Sceneries.DEAD_PLANT_29456, *NUMBERS).contains(toClear.id)) { + SceneryBuilder.replace(toClear, toClear.transform(HOLES[0])) + } + } + } + SEED_LOCS.remove(dest.loc) + populateSeeds() + } +} + + + +/* +https://www.youtube.com/watch?v=WkCVAOOR7Sw +buy-flags +Player: "Have you got any flags?" +NPC: "Let me check for you." +NPC: "Alright, you can have a total of 10 flags. To obtain a\nfull set of flags will cost you 5000 coins. Would you\nlike to buy these flags?" +"Yes, please." "No thanks." +NPC: "Here you are then, dear." + +talk-to +NPC: "Oh hello there, dear. How can I help you?" +"Where are we?" "Have you got any flags?" "Do you have a spare spade?" "Do you have anything for trade?" "Nothing. I'm fine, thanks." + +Player: "Do you have a spare spade?" +NPC: "Why, of course. I can sell you one for 5 gold pieces." +"Okay, thanks." "Actually, I've changed my mind." +NPC: "Here you are, then." + +inspect: +chat log: "You examine the hole to see what might be in it." +chat dialog: "You notice a seed hidden in the dirt." + +Farmer overhead: +"Ah, another flag to clear. Let's see what's there." +"Ah! A seed. Points for everyone near me!" + +https://www.youtube.com/watch?v=UjxfJdHkJnM +inspect: +"Oh dear! It looks like you dug up a potato seed by mistake." +Farmer overhead: +"Hmm. Looks like there's a plant here." +"Gracious me! This one's dead" + +flag: +"You add a flag to the patch." + +digging ogleroot: +"You uncover a rather odd-looking root vegetable." + +https://www.youtube.com/watch?v=fKyy0sgrBYM +Rabbit overhead: +"Squeak!" + +https://www.youtube.com/watch?v=RnhNrwbUjjQ +Player: "Have you got any flags?" +NPC: "Let me check for you." +NPC: "Ah! First things first. One of the farmers dropped off\nsome flags for you. You can have them back. Here you\ngo." + +NPC: "It looks like you have all the flags you need. You don't\nneed to buy any more." + +inspect: +"You notice a seed hidden in the dirt." +"A slimy worm wriggles out of the mud, making you jump and lose\nconcentration. You're not sure if there is a seed here or not." +"You are certain there is no seed planted here." + +farmer overhead: +"Hmm, no seeds planted here, I'm afraid." +"I'll have to keep this 'ere flag. Sorry." + +*/ diff --git a/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/VinesweeperDialogues.kt b/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt similarity index 78% rename from Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/VinesweeperDialogues.kt rename to Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt index b9125059a..23c012487 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/VinesweeperDialogues.kt +++ b/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt @@ -1,37 +1,45 @@ -import api.ContentAPI import core.game.component.Component -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 rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE - -val BLINKIN_FLAG_LINES = arrayOf( - "Let me check for ya.", - "Flags? It appears ya don't have enough room for 'em. Make some space and talk to me again.", - "Ah! First things first. One of the farm lads dropped off some flags for ya. Ya can have them back. Here ya go.", - "Righty-ho! Ya can have a total of 10 flags. To get yerself a full set of flags'll cost ya %d gold pieces. Would ya like to buy these flags?", - "Here ya go, then.", - "Flags? It appears ya don't have enough room for 'em. Make some space and talk to me again.", - "Ya don't have the coins fer these, I'm afraid! Come back when yer a little bit richer p'raps?", - "Right y'are then! See ya.", - "It looks like ya got all the flags ya need right now. Ya don't need to buy any more." - ) - -val WINKIN_FLAG_LINES = arrayOf( - "Let me check for you.", - "I'm sorry dear, you don't appear to have enough room. Make some space and talk to me again.", - "Ah! First things first. One of the farmers dropped off some flags for you. You can have them back. Here you go.", - "Alright. You can have a total of 10 flags. To obtain a full set of flags will cost you %d coins. Would you like to buy these flags?", - "Here you are then, dear.", - "I'm sorry dear, you don't appear to have enough room. Make some space and talk to me again.", - "I'm afraid it looks like you don't have enough money, dear. Come back and see me again when you have a bit more.", - "Ok, dear. Goodbye.", - "It looks like you have all the flags you need. You don't need to buy any more." - ) +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE +import core.api.* abstract class FarmerDialogue : DialogueFile() { + + companion object { + val BLINKIN_FLAG_LINES = arrayOf( + "Let me check for ya.", + "Flags? It appears ya don't have enough room for 'em. Make some space and talk to me again.", + "Ah! First things first. One of the farm lads dropped off some flags for ya. Ya can have them back. Here ya go.", + "Righty-ho! Ya can have a total of 10 flags. To get yerself a full set of flags'll cost ya %d gold pieces. Would ya like to buy these flags?", + "Here ya go, then.", + "Flags? It appears ya don't have enough room for 'em. Make some space and talk to me again.", + "Ya don't have the coins fer these, I'm afraid! Come back when yer a little bit richer p'raps?", + "Right y'are then! See ya.", + "It looks like ya got all the flags ya need right now. Ya don't need to buy any more." + ) + val WINKIN_FLAG_LINES = arrayOf( + "Let me check for you.", + "I'm sorry dear, you don't appear to have enough room. Make some space and talk to me again.", + "Ah! First things first. One of the farmers dropped off some flags for you. You can have them back. Here you go.", + "Alright. You can have a total of 10 flags. To obtain a full set of flags will cost you %d coins. Would you like to buy these flags?", + "Here you are then, dear.", + "I'm sorry dear, you don't appear to have enough room. Make some space and talk to me again.", + "I'm afraid it looks like you don't have enough money, dear. Come back and see me again when you have a bit more.", + "Ok, dear. Goodbye.", + "It looks like you have all the flags you need. You don't need to buy any more." + ) + enum class FARMER_FLAG_LINES(val line: String) { + FIND_FLAG("Ah, another flag to clear. Let's see what's there."), + FIND_SEED("Ah! A seed. Points for everyone near me!"), + NO_SEED("Hmm, no seeds planted here, I'm afraid."), + KEEP_FLAG("I'll have to keep this 'ere flag. Sorry."), + FIND_PLANT("Hmm. Looks like there's a plant here."), + DEAD_PLANT("Gracious me! This one's dead") + } + } fun handleFlags(componentID: Int, buttonID: Int, lines: Array) { when(stage) { 20 -> npcl(lines[0]).also { stage++ } @@ -73,7 +81,8 @@ abstract class FarmerDialogue : DialogueFile() { if(player!!.inventory.containsItem(price) && player!!.inventory.remove(price)) { if(player!!.inventory.add(Item(Items.FLAG_12625, 10 - flags))) { npcl(lines[4]) - stage = 23 + stage = 22 + stage = END_DIALOGUE } else { npcl(lines[5]) // Refund the coins, can't fail because we just removed them @@ -121,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))) { @@ -140,7 +150,7 @@ class BlinkinDialogue : FarmerDialogue() { npcl("Sorry, ya can't afford that many. Come back when yer feeling a bit richer if ya like!") stage = END_DIALOGUE } - //ContentAPI.openDialogue(player!!, this, npc as Any) + //openDialogue(player!!, this, npc as Any) } player!!.dialogueInterpreter.sendInput(false, "Enter the amount:") } diff --git a/Server/src/main/java/core/game/content/dialogue/AjjatDialoguePlugin.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AjjatDialoguePlugin.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/AjjatDialoguePlugin.java rename to Server/src/main/content/region/asgarnia/burthorpe/dialogue/AjjatDialoguePlugin.java index c5a90e1b6..e92c4be3f 100644 --- a/Server/src/main/java/core/game/content/dialogue/AjjatDialoguePlugin.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AjjatDialoguePlugin.java @@ -1,6 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.burthorpe.dialogue; -import core.game.content.global.Skillcape; +import core.game.dialogue.DialoguePlugin; +import core.game.global.Skillcape; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; 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.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.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.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.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.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.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 new file mode 100644 index 000000000..e59b3d907 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt @@ -0,0 +1,110 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.ServerConstants +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 Emerald Benedict's dialogue tree. + * + * @author vddCore + */ +@Initializable +class EmeraldBenedictDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { + npcl( + FacialExpression.ANNOYED, + "Get lost, tin can." + ).also { stage = END_DIALOGUE } + } else { + npcl( + FacialExpression.SUSPICIOUS, + "Got anything you don't want to lose?" + ).also { + if (hasAwaitingGrandExchangeCollections(player)) { + stage++ + } else { + stage += 2 + } + } + } + + 1 -> npcl( + 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(FacialExpression.ASKING, "Yes, actually. Can you help?", 3), + IfTopic( + FacialExpression.ASKING, + "Yes, but can you switch my bank accounts?", + 4, + hasActivatedSecondaryBankAccount(player) + ), + IfTopic( + FacialExpression.ASKING, + "Yes, but can you open a secondary bank account for me?", + 7, + ServerConstants.SECOND_BANK && !hasActivatedSecondaryBankAccount(player) + ), + Topic(FacialExpression.ASKING, "Yes, but can you show me my PIN settings?", 5), + Topic(FacialExpression.ASKING, "Yes, but can you show me my collection box?", 6), + Topic(FacialExpression.ANNOYED, "Yes, thanks. And I'll keep hold of it too.", END_DIALOGUE) + ) + + 3 -> { + openBankAccount(player) + end() + } + + 4 -> { + toggleBankAccount(player) + npcl( + FacialExpression.SUSPICIOUS, + "Sure thing. Feel free to rummage through whatever's in your ${getBankAccountName(player)} now." + ).also { stage = 2 } + } + + 5 -> { + openBankPinSettings(player) + end() + } + + 6 -> { + 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) +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictListener.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictListener.kt new file mode 100644 index 000000000..9268d2ed5 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictListener.kt @@ -0,0 +1,26 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.openBankAccount +import core.api.openGrandExchangeCollectionBox +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Handles Emerald Benedict's only available interaction. + * + * @author vddCore + */ +class EmeraldBenedictListener : InteractionListener { + override fun defineListeners() { + on(NPCs.EMERALD_BENEDICT_2271, IntType.NPC, "bank") { player, _ -> + openBankAccount(player) + return@on true + } + + on(NPCs.EMERALD_BENEDICT_2271, IntType.NPC, "collect") { player, _ -> + openGrandExchangeCollectionBox(player) + return@on true + } + } +} \ 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/java/core/game/content/dialogue/GamFredDialoue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/GamFredDialoue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/GamFredDialoue.java rename to Server/src/main/content/region/asgarnia/burthorpe/dialogue/GamFredDialoue.java index 0eb75084f..ec37eca66 100644 --- a/Server/src/main/java/core/game/content/dialogue/GamFredDialoue.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/GamFredDialoue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/GhommalDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/GhommalDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/GhommalDialogue.java rename to Server/src/main/content/region/asgarnia/burthorpe/dialogue/GhommalDialogue.java index 36a784dd7..96114bb47 100644 --- a/Server/src/main/java/core/game/content/dialogue/GhommalDialogue.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/GhommalDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.burthorpe.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; 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.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.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/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/java/core/game/content/dialogue/KamfreenaDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/KamfreenaDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/KamfreenaDialogue.java rename to Server/src/main/content/region/asgarnia/burthorpe/dialogue/KamfreenaDialogue.java index b4010d50d..aa398eafc 100644 --- a/Server/src/main/java/core/game/content/dialogue/KamfreenaDialogue.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/KamfreenaDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +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; 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.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/java/core/game/content/dialogue/MartinThwaitDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/MartinThwaitDialogue.java similarity index 86% rename from Server/src/main/java/core/game/content/dialogue/MartinThwaitDialogue.java rename to Server/src/main/content/region/asgarnia/burthorpe/dialogue/MartinThwaitDialogue.java index d01b5ee83..675d04822 100644 --- a/Server/src/main/java/core/game/content/dialogue/MartinThwaitDialogue.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/MartinThwaitDialogue.java @@ -1,6 +1,10 @@ -package core.game.content.dialogue; +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; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -9,7 +13,8 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Plugin; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import org.rs09.consts.Items; /** * Represents the dialogue used for martin thwait. @@ -139,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: @@ -161,7 +167,7 @@ public final class MartinThwaitDialogue extends DialoguePlugin { @Override public void init() { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { 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.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/java/core/game/content/dialogue/ReffDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ReffDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/ReffDialogue.java rename to Server/src/main/content/region/asgarnia/burthorpe/dialogue/ReffDialogue.java index ca23880ae..079cc6650 100644 --- a/Server/src/main/java/core/game/content/dialogue/ReffDialogue.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ReffDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +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; 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.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/java/core/game/content/dialogue/ShanomiDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ShanomiDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/ShanomiDialogue.java rename to Server/src/main/content/region/asgarnia/burthorpe/dialogue/ShanomiDialogue.java index 31ebd34d1..d3c6f411f 100644 --- a/Server/src/main/java/core/game/content/dialogue/ShanomiDialogue.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ShanomiDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/SloaneDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/SloaneDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/SloaneDialogue.java rename to Server/src/main/content/region/asgarnia/burthorpe/dialogue/SloaneDialogue.java index ae703e5e8..d74f626a1 100644 --- a/Server/src/main/java/core/game/content/dialogue/SloaneDialogue.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/SloaneDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.burthorpe.dialogue; -import core.game.content.global.Skillcape; +import core.game.dialogue.DialoguePlugin; +import core.game.global.Skillcape; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; 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/java/core/game/node/entity/npc/other/AntonNPC.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/AntonNPC.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/other/AntonNPC.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/AntonNPC.java index 761d9f2cd..ef37619b5 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/AntonNPC.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/AntonNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.asgarnia.burthorpe.handlers; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeOptionPlugin.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeOptionPlugin.java new file mode 100644 index 000000000..c5528bacd --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeOptionPlugin.java @@ -0,0 +1,80 @@ +package content.region.asgarnia.burthorpe.handlers; + +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; +import core.game.node.entity.player.Player; +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; + +/** + * Represents the option plugin to handle interactions with 'nodes' in + * burthorpe. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class BurthorpeOptionPlugin extends OptionHandler { + + /** + * Represents the thieving guide location. + */ + private static final Location GUIDE_LOCATION = new Location(3061, 4985, 1); + + /** + * Represents the bar location. + */ + private static final Location BAR_LOCATION = new Location(2906, 3537, 0); + + @Override + public Plugin newInstance(Object arg) throws Throwable { + SceneryDefinition.forId(7257).getHandlers().put("option:enter", this);// thieving + // guide + // trapdoor. + SceneryDefinition.forId(7258).getHandlers().put("option:enter", this);// thieving + // guide + // passegeway. + SceneryDefinition.forId(4624).getHandlers().put("option:climb-down", this); + SceneryDefinition.forId(4627).getHandlers().put("option:climb-up", this); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + final int id = node instanceof NPC ? ((NPC) node).getId() : ((Scenery) node).getId(); + switch (option) { + case "climb-down": + switch (id) { + case 4624: + ClimbActionHandler.climb(player, null, Location.create(2205, 4934, 1)); + break; + } + break; + case "climb-up": + switch (id) { + case 4627: + ClimbActionHandler.climb(player, null, Location.create(2899, 3565, 0)); + break; + } + break; + case "enter": + switch (id) { + case 7257: + player.getProperties().setTeleportLocation(GUIDE_LOCATION); + break; + case 7258: + player.getProperties().setTeleportLocation(BAR_LOCATION); + break; + } + break; + } + return true; + } + +} diff --git a/Server/src/main/java/core/game/interaction/npc/BurthorpeTrainNPC.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeTrainNPC.java similarity index 98% rename from Server/src/main/java/core/game/interaction/npc/BurthorpeTrainNPC.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeTrainNPC.java index bff5a6a5e..518c70715 100644 --- a/Server/src/main/java/core/game/interaction/npc/BurthorpeTrainNPC.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeTrainNPC.java @@ -1,11 +1,11 @@ -package core.game.interaction.npc; +package content.region.asgarnia.burthorpe.handlers; import java.util.List; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.RegionManager; diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java new file mode 100644 index 000000000..60e6c86fd --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java @@ -0,0 +1,135 @@ +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; +import content.global.skill.summoning.familiar.Familiar; +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.npc.NPC; +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.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 { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + SceneryDefinition.forId(2624).getHandlers().put("option:open", this); + SceneryDefinition.forId(2625).getHandlers().put("option:open", this); + ClassScanner.definePlugin(new JewelleryRechargePlugin()); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + final int id = ((Scenery) node).getId(); + switch (option) { + case "open": + switch (id) { + case 2624: + case 2625: + if (!hasRequirement(player, Quests.HEROES_QUEST)) + return true; + DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); + break; + } + return true; + } + return true; + } + + /** + * Handles the recharging of dragonstone jewellery. + * @author Vexia + * @author Player Name + */ + public static final class JewelleryRechargePlugin extends UseWithHandler { + + /** + * The ids of rechargeable items. + */ + 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 JewelleryRechargePlugin object + */ + public JewelleryRechargePlugin() { + super(IDS); + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + addHandler(36695, OBJECT_TYPE, this); + addHandler(7339, NPC_TYPE, this); + addHandler(7340, NPC_TYPE, this); + return this; + } + + @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) { + 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) { + return false; + } + if (fam) { + Familiar familiar = (Familiar) event.getUsedWith(); + if (!player.getFamiliarManager().isOwner(familiar)) { + return false; + } + familiar.animate(Animation.create(7882)); + } + player.lock(1); + player.animate(Animation.create(832)); + Item rechargedItem = new Item(jewellery.getIds()[0]); + player.getInventory().replace(rechargedItem, event.getUsedItem().getSlot()); + String name = jewellery.getJewelleryName(rechargedItem); + if (!fam) { + sendMessage(player, "You dip the " + name.toLowerCase() + " in the fountain..."); + } else { + sendMessage(player, "Your titan recharges the " + name.toLowerCase() + "."); + } + return true; + } + + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/KamfreenaNPC.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/KamfreenaNPC.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/other/KamfreenaNPC.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/KamfreenaNPC.java index b907e817e..8b0bb7fd8 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/KamfreenaNPC.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/KamfreenaNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.asgarnia.burthorpe.handlers; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/node/entity/npc/other/ShanomiNPC.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/ShanomiNPC.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/other/ShanomiNPC.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/ShanomiNPC.java index e1e62df75..fde92aa44 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/ShanomiNPC.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/ShanomiNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.asgarnia.burthorpe.handlers; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; diff --git a/Server/src/main/kotlin/rs09/game/content/activity/wguild/WGuildListeners.kt b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/WGuildListeners.kt similarity index 81% rename from Server/src/main/kotlin/rs09/game/content/activity/wguild/WGuildListeners.kt rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/WGuildListeners.kt index fafd7ce09..e20b682fc 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/wguild/WGuildListeners.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/WGuildListeners.kt @@ -1,13 +1,13 @@ -package rs09.game.content.activity.wguild +package content.region.asgarnia.burthorpe.handlers.wguild import core.game.component.Component import core.game.container.impl.EquipmentContainer -import core.game.content.activity.wguild.catapult.CatapultRoom +import content.region.asgarnia.burthorpe.handlers.wguild.catapult.CatapultRoom import core.game.node.item.Item import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener +import core.game.interaction.InteractionListener -class WGuildListeners : InteractionListener() { +class WGuildListeners : InteractionListener { override fun defineListeners() { onEquip(Items.DEFENSIVE_SHIELD_8856){player, node -> if (node is Item) { @@ -23,7 +23,7 @@ class WGuildListeners : InteractionListener() { return@onEquip false } - player.interfaceManager.hideTabs(2, 3, 5, 6, 7, 11, 12) + player.interfaceManager.removeTabs(2, 3, 5, 6, 7, 11, 12) player.interfaceManager.openTab(4, Component(411)) player.interfaceManager.setViewedTab(4) player.interfaceManager.open(Component(410)) diff --git a/Server/src/main/java/core/game/content/activity/wguild/WarriorsGuild.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/WarriorsGuild.java similarity index 93% rename from Server/src/main/java/core/game/content/activity/wguild/WarriorsGuild.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/WarriorsGuild.java index 9fd0abc29..7b22894be 100644 --- a/Server/src/main/java/core/game/content/activity/wguild/WarriorsGuild.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/WarriorsGuild.java @@ -1,10 +1,10 @@ -package core.game.content.activity.wguild; +package content.region.asgarnia.burthorpe.handlers.wguild; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -14,7 +14,7 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.plugin.Plugin; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles the warrior guild options. @@ -35,7 +35,7 @@ public final class WarriorsGuild extends OptionHandler { SceneryDefinition.forId(1530).getHandlers().put("option:open", this); NPCDefinition.forId(4287).getHandlers().put("option:claim-shield", this); NPCDefinition.setOptionHandler("claim-tokens", this); - PluginManager.definePlugin(new ClaimTokenDialogue()); + ClassScanner.definePlugin(new ClaimTokenDialogue()); return this; } diff --git a/Server/src/main/java/core/game/content/activity/wguild/animator/AnimatedArmour.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimatedArmour.java similarity index 88% rename from Server/src/main/java/core/game/content/activity/wguild/animator/AnimatedArmour.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimatedArmour.java index 44b05b41b..dd379992e 100644 --- a/Server/src/main/java/core/game/content/activity/wguild/animator/AnimatedArmour.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimatedArmour.java @@ -1,4 +1,4 @@ -package core.game.content.activity.wguild.animator; +package content.region.asgarnia.burthorpe.handlers.wguild.animator; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -13,8 +13,6 @@ import core.game.world.map.path.Pathfinder; import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; -import core.game.content.activity.wguild.animator.AnimationRoom.ArmourSet; - /** * Represents an animated armour. * @author Emperor @@ -29,7 +27,7 @@ public final class AnimatedArmour extends NPC { /** * The armour set. */ - private final ArmourSet set; + private final AnimationRoom.ArmourSet set; /** * If the set is running. @@ -42,7 +40,7 @@ public final class AnimatedArmour extends NPC { * @param location The location to spawn. * @param set The armour set. */ - protected AnimatedArmour(Player player, Location location, ArmourSet set) { + protected AnimatedArmour(Player player, Location location, AnimationRoom.ArmourSet set) { super(set.getNpcId(), location); this.player = player; this.set = set; @@ -84,14 +82,14 @@ public final class AnimatedArmour extends NPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (entity != player) { if (entity instanceof Player) { ((Player) entity).getPacketDispatch().sendMessage("This isn't your armour to attack."); } return false; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override @@ -119,6 +117,7 @@ public final class AnimatedArmour extends NPC { int amount = set.getTokenAmount(); GroundItemManager.create(new Item(8851, amount), location, player); } + player.logoutListeners.remove("animation-room"); player.removeAttribute("animated_set"); } } diff --git a/Server/src/main/java/core/game/content/activity/wguild/animator/AnimationRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimationRoom.java similarity index 89% rename from Server/src/main/java/core/game/content/activity/wguild/animator/AnimationRoom.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimationRoom.java index 67fbb13fe..746fd9368 100644 --- a/Server/src/main/java/core/game/content/activity/wguild/animator/AnimationRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimationRoom.java @@ -1,4 +1,4 @@ -package core.game.content.activity.wguild.animator; +package content.region.asgarnia.burthorpe.handlers.wguild.animator; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -8,10 +8,9 @@ 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.game.system.task.ItemLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import kotlin.Unit; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; @@ -19,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 @@ -112,7 +114,7 @@ public final class AnimationRoom extends MapZone implements Plugin { */ private void animateArmour(final Player player, final Scenery object, final ArmourSet set) { if (!player.getInventory().containItems(set.getPieces())) { - player.getDialogueInterpreter().sendDialogue("You need a plate body, playe legs and full helm of the same type to", "activate the armour animator."); + player.getDialogueInterpreter().sendDialogue("You need a plate body, plate legs and full helm of the same type to", "activate the armour animator."); return; } if (player.getAttribute("animated_set") != null) { @@ -133,8 +135,11 @@ public final class AnimationRoom extends MapZone implements Plugin { return true; } } - player.getAudioManager().send(1909); - player.addExtension(LogoutTask.class, new ItemLogoutTask(5, new Item(set.getPieces()[0]), new Item(set.getPieces()[1]), new Item(set.getPieces()[2]))); + 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; + }); player.getDialogueInterpreter().sendPlainMessage(true, "The animator hums, something appears to be working.", "You stand back..."); spawn = true; super.setDelay(4); @@ -142,11 +147,10 @@ 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; } - player.removeExtension(LogoutTask.class); player.getInterfaceManager().closeChatbox(); NPC npc = new AnimatedArmour(player, object.getLocation(), set); player.setAttribute("animated_set", npc); diff --git a/Server/src/main/java/core/game/content/activity/wguild/barrel/BarrelRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/barrel/BarrelRoom.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/wguild/barrel/BarrelRoom.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/barrel/BarrelRoom.java index 50b3eaa64..e01b9b698 100644 --- a/Server/src/main/java/core/game/content/activity/wguild/barrel/BarrelRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/barrel/BarrelRoom.java @@ -1,4 +1,4 @@ -package core.game.content.activity.wguild.barrel; +package content.region.asgarnia.burthorpe.handlers.wguild.barrel; import java.util.ArrayList; import java.util.Iterator; @@ -15,7 +15,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBuilder; @@ -23,9 +23,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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Handles the Barrel room. * @author Emperor @@ -86,7 +88,7 @@ public final class BarrelRoom extends MapZone implements Plugin { public Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); pulse.stop(); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.forId(15668).getHandlers().put("option:pick-up", this); @@ -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/java/core/game/content/activity/wguild/catapult/CatapultRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/catapult/CatapultRoom.java similarity index 93% rename from Server/src/main/java/core/game/content/activity/wguild/catapult/CatapultRoom.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/catapult/CatapultRoom.java index c19811a5b..a5ae501b3 100644 --- a/Server/src/main/java/core/game/content/activity/wguild/catapult/CatapultRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/catapult/CatapultRoom.java @@ -1,4 +1,4 @@ -package core.game.content.activity.wguild.catapult; +package content.region.asgarnia.burthorpe.handlers.wguild.catapult; import java.util.ArrayList; import java.util.List; @@ -9,7 +9,7 @@ import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.game.container.impl.EquipmentContainer; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -22,7 +22,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.zone.MapZone; @@ -30,9 +30,13 @@ 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.Plugin; -import rs09.plugin.PluginManager; +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(); } @@ -164,7 +167,7 @@ public final class CatapultRoom extends MapZone implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { ItemDefinition.forId(SHIELD_ID).getHandlers().put("option:wield", this); @@ -185,7 +188,7 @@ public final class CatapultRoom extends MapZone implements Plugin { } ItemDefinition.getOptionHandlers().get("wield").handle(player, node, option); if (player.getEquipment().getNew(EquipmentContainer.SLOT_SHIELD).getId() == SHIELD_ID) { - player.getInterfaceManager().hideTabs(2, 3, 5, 6, 7, 11, 12); + player.getInterfaceManager().removeTabs(2, 3, 5, 6, 7, 11, 12); player.getInterfaceManager().openTab(4, new Component(411)); player.getInterfaceManager().setViewedTab(4); } @@ -195,7 +198,7 @@ public final class CatapultRoom extends MapZone implements Plugin { return true; } }); - PluginManager.definePlugin(new ComponentPlugin() { + ClassScanner.definePlugin(new ComponentPlugin() { @Override public Plugin newInstance(Object arg) throws Throwable { ComponentDefinition.put(411, this); @@ -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/java/core/game/content/activity/wguild/cyclopes/CyclopesRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java similarity index 89% rename from Server/src/main/java/core/game/content/activity/wguild/cyclopes/CyclopesRoom.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java index 4ef808d96..98111f7c1 100644 --- a/Server/src/main/java/core/game/content/activity/wguild/cyclopes/CyclopesRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java @@ -1,4 +1,4 @@ -package core.game.content.activity.wguild.cyclopes; +package content.region.asgarnia.burthorpe.handlers.wguild.cyclopes; import java.util.ArrayList; import java.util.Iterator; @@ -7,9 +7,9 @@ import java.util.List; import core.cache.def.impl.SceneryDefinition; import core.game.container.impl.EquipmentContainer; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.global.action.DoorActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; @@ -19,7 +19,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.path.Pathfinder; @@ -28,8 +28,13 @@ import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBuilder; import core.game.world.map.zone.ZoneRestriction; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +import static core.api.ContentAPIKt.clearLogoutListener; +import static core.api.ContentAPIKt.registerLogoutListener; /** * The cyclopes room. @@ -97,12 +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"); } return super.leave(e, logout); } @@ -111,7 +117,7 @@ public final class CyclopesRoom extends MapZone implements Plugin { public boolean death(Entity e, Entity killer) { if (killer instanceof Player && e instanceof NPC && (e.getId() == 4292 || e.getId() == 4291)) { int defenderId = getDefenderIndex((Player) killer); - if (RandomFunction.randomize(32) == 10) { + if (RandomFunction.randomize(50) == 10) { if (++defenderId == DEFENDERS.length) { defenderId--; } @@ -123,15 +129,15 @@ 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 public Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); PULSE.stop(); - PluginManager.definePlugin(new KamfreenaDial()); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new KamfreenaDial()); + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { @@ -199,6 +205,10 @@ public final class CyclopesRoom extends MapZone implements Plugin { PULSE.start(); GameWorld.getPulser().submit(PULSE); } + registerLogoutListener(player, "cyclopes", player1 -> { + player1.setLocation(Location.create(2844, 3540, 2)); + return Unit.INSTANCE; + }); } /** diff --git a/Server/src/main/java/core/game/content/activity/wguild/dummy/DummyRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/dummy/DummyRoom.java similarity index 98% rename from Server/src/main/java/core/game/content/activity/wguild/dummy/DummyRoom.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/dummy/DummyRoom.java index 060e58927..bc1ad6f5a 100644 --- a/Server/src/main/java/core/game/content/activity/wguild/dummy/DummyRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/dummy/DummyRoom.java @@ -1,4 +1,4 @@ -package core.game.content.activity.wguild.dummy; +package content.region.asgarnia.burthorpe.handlers.wguild.dummy; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; @@ -11,7 +11,7 @@ import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag; 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/java/core/game/content/activity/wguild/shot/ShotPutRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/shot/ShotPutRoom.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/wguild/shot/ShotPutRoom.java rename to Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/shot/ShotPutRoom.java index 8504dc572..7e075e351 100644 --- a/Server/src/main/java/core/game/content/activity/wguild/shot/ShotPutRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/shot/ShotPutRoom.java @@ -1,12 +1,12 @@ -package core.game.content.activity.wguild.shot; +package content.region.asgarnia.burthorpe.handlers.wguild.shot; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.container.impl.EquipmentContainer; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -18,11 +18,11 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; /** @@ -33,7 +33,7 @@ import core.tools.RandomFunction; public final class ShotPutRoom extends DialoguePlugin { static { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public boolean handle(final Player player, Node node, String option) { 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/java/core/game/content/dialogue/CuffsDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/CuffsDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/dialogue/CuffsDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/CuffsDialogue.java index 238059b19..261d85682 100644 --- a/Server/src/main/java/core/game/content/dialogue/CuffsDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/CuffsDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.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; @@ -8,6 +10,7 @@ import core.game.node.entity.player.Player; * Represents the dialogue plugin used for the cuffs npc. * @author 'Vexia * @version 1.0 + * Fixed a typo -Nuggles */ @Initializable public final class CuffsDialogue extends DialoguePlugin { @@ -54,7 +57,7 @@ public final class CuffsDialogue extends DialoguePlugin { stage = 2; break; case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Nope, we'd never do anuthing like that.", "Just a band of innocent walkers, that's us."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Nope, we'd never do anything like that.", "Just a band of innocent walkers, that's us."); stage = 3; break; case 3: diff --git a/Server/src/main/java/core/game/content/dialogue/GrimgnashDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/GrimgnashDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/GrimgnashDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/GrimgnashDialogue.java index 401828907..4d19b4e8f 100644 --- a/Server/src/main/java/core/game/content/dialogue/GrimgnashDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/GrimgnashDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.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; diff --git a/Server/src/main/java/core/game/content/dialogue/IcyCavernDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/IcyCavernDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/IcyCavernDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/IcyCavernDialogue.java index f364c0dad..cdf4bf630 100644 --- a/Server/src/main/java/core/game/content/dialogue/IcyCavernDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/IcyCavernDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.asgarnia.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/content/dialogue/JeffDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/JeffDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/JeffDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/JeffDialogue.java index 959729dd2..22d40009b 100644 --- a/Server/src/main/java/core/game/content/dialogue/JeffDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/JeffDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.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; diff --git a/Server/src/main/java/core/game/content/dialogue/LakkiDwarfDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/LakkiDwarfDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/LakkiDwarfDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/LakkiDwarfDialogue.java index 0b607d66b..96332b5f0 100644 --- a/Server/src/main/java/core/game/content/dialogue/LakkiDwarfDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/LakkiDwarfDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.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; diff --git a/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt b/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt new file mode 100644 index 000000000..054dbf209 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt @@ -0,0 +1,111 @@ +package content.region.asgarnia.dialogue + +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.global.Skillcape +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 +import core.tools.END_DIALOGUE + +/** + * @author bushtail + */ + +@Initializable +class MasterCrafterDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player): DialoguePlugin { + return MasterCrafterDialogue(player) + } + + 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++ } + + 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() + } + } + + 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 } + } + } + + 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 } + } + } + } + + 111 -> npcl( + FacialExpression.FRIENDLY, + "Well, come back and see me when you do." + ).also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MASTER_CRAFTER_805) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/NarfsDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/NarfsDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/NarfsDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/NarfsDialogue.java index 7a3f4258c..30659ab3d 100644 --- a/Server/src/main/java/core/game/content/dialogue/NarfsDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/NarfsDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.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; diff --git a/Server/src/main/java/core/game/content/dialogue/OracleDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/OracleDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/OracleDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/OracleDialogue.java index 95d4748f9..b811dbc74 100644 --- a/Server/src/main/java/core/game/content/dialogue/OracleDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/OracleDialogue.java @@ -1,9 +1,11 @@ -package core.game.content.dialogue; +package content.region.asgarnia.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.game.node.entity.player.link.quest.Quest; +import content.data.Quests; /** * Represents the oracle dialogue plugin related to dragon slayer. @@ -42,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/java/core/game/content/dialogue/RustyDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/RustyDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/RustyDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/RustyDialogue.java index 8d9bbdf37..82d8d60c1 100644 --- a/Server/src/main/java/core/game/content/dialogue/RustyDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/RustyDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.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; diff --git a/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt b/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt new file mode 100644 index 000000000..b4d949075 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt @@ -0,0 +1,81 @@ +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.player.Player +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.tools.END_DIALOGUE + +/** + * Handles the Crafting Guild Tanner's dialogue. + * @author bushtail + */ + +@Initializable +class TannerDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player): DialoguePlugin { + return TannerDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.NEUTRAL, "Greetings friend. I am a manufacturer of leather.") + stage = 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> { + 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.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 { + return intArrayOf(NPCs.TANNER_804) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/dialogue/TheDoorDialogues.kt b/Server/src/main/content/region/asgarnia/dialogue/TheDoorDialogues.kt new file mode 100644 index 000000000..fd7f6bae0 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dialogue/TheDoorDialogues.kt @@ -0,0 +1,29 @@ +package content.region.asgarnia.dialogue + +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE + +/** + * @author bushtail + */ + +class TheDoorDialogues(val it: Int) : DialogueFile() { + override fun handle(interfaceId: Int, buttonId: Int) { + npc = NPC(NPCs.MASTER_CRAFTER_805) + when(it) { + 0 -> when(stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "Welcome to the Guild of Master Craftsmen.").also{ stage = END_DIALOGUE } + } + 1 -> when(stage) { + 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(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/java/core/game/content/dialogue/ThuroDialogue.java b/Server/src/main/content/region/asgarnia/dialogue/ThuroDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/dialogue/ThuroDialogue.java rename to Server/src/main/content/region/asgarnia/dialogue/ThuroDialogue.java index 1af14ae17..b11f3845c 100644 --- a/Server/src/main/java/core/game/content/dialogue/ThuroDialogue.java +++ b/Server/src/main/content/region/asgarnia/dialogue/ThuroDialogue.java @@ -1,11 +1,14 @@ -package core.game.content.dialogue; +package content.region.asgarnia.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for thurgo. @@ -75,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: @@ -152,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; @@ -485,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 new file mode 100644 index 000000000..191f7237e --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dialogue/ZandarHorfyreDialogue.kt @@ -0,0 +1,53 @@ +package content.region.asgarnia.dialogue + +import core.api.teleport +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.TeleportManager +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.END_DIALOGUE + +/** + * @author bushtail + */ +@Initializable +class ZandarHorfyreDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player) : DialoguePlugin { + return ZandarHorfyreDialogue(player) + } + + override fun open(vararg args: Any?) : Boolean { + npc = args[0] as NPC + player(FacialExpression.HALF_THINKING,"Who are you?") + stage = -1 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + when (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(FacialExpression.NEUTRAL,"Good! And don't forget to close the door behind you!").also{ stage++ } + 11 -> stage = END_DIALOGUE + + 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 + } + + override fun getIds() : IntArray { + return intArrayOf(3308) + } +} diff --git a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrogoDialogue.kt b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrogoDialogue.kt new file mode 100644 index 000000000..e5c2343c3 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrogoDialogue.kt @@ -0,0 +1,48 @@ +package content.region.asgarnia.dwarfmine.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 DrogoDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_DEFAULT,"'Ello. Welcome to my Mining shop, friend.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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(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(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?): DialoguePlugin { + return DrogoDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DROGO_DWARF_579) + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/DrokarDialogue.java b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrokarDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/DrokarDialogue.java rename to Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrokarDialogue.java index 19349d3ea..9bb56732b 100644 --- a/Server/src/main/java/core/game/content/dialogue/DrokarDialogue.java +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrokarDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.dwarfmine.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; diff --git a/Server/src/main/java/core/game/content/dialogue/DwarfShopDialogue.java b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarfShopDialogue.java similarity index 80% rename from Server/src/main/java/core/game/content/dialogue/DwarfShopDialogue.java rename to Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarfShopDialogue.java index 96c24987c..dbcbf2c69 100644 --- a/Server/src/main/java/core/game/content/dialogue/DwarfShopDialogue.java +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarfShopDialogue.java @@ -1,11 +1,13 @@ -package core.game.content.dialogue; +package content.region.asgarnia.dwarfmine.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 dialouge plugin used for the dward shop. + * Represents the dialouge plugin used for the dwarf shop. * @author 'Vexia * @version 1.0 */ @@ -37,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 new file mode 100644 index 000000000..e5d7c85a3 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarvenMineGuardDialogue.kt @@ -0,0 +1,44 @@ +package content.region.asgarnia.dwarfmine.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 DwarvenMineGuardDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return DwarvenMineGuardDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUARD_206) + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/HuraDialogue.java b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/HuraDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/HuraDialogue.java rename to Server/src/main/content/region/asgarnia/dwarfmine/dialogue/HuraDialogue.java index 09ffe1f16..41e01ecd1 100644 --- a/Server/src/main/java/core/game/content/dialogue/HuraDialogue.java +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/HuraDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.dwarfmine.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; diff --git a/Server/src/main/java/core/game/content/dialogue/MiningSkillCapeDwarfDialogue.java b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/MiningSkillCapeDwarfDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/MiningSkillCapeDwarfDialogue.java rename to Server/src/main/content/region/asgarnia/dwarfmine/dialogue/MiningSkillCapeDwarfDialogue.java index 779bc5ab1..b82f8f618 100644 --- a/Server/src/main/java/core/game/content/dialogue/MiningSkillCapeDwarfDialogue.java +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/MiningSkillCapeDwarfDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.dwarfmine.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.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 new file mode 100644 index 000000000..366bbfaee --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/NurmofDialogue.kt @@ -0,0 +1,59 @@ +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 +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +/** + * @author qmqz + */ + +@Initializable +class NurmofDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("Yes, please.", "No, thank you.", "Are your pickaxes better than other pickaxes, then?").also { stage++ } + + 1 -> when (buttonId) { + 1 -> end().also { npc.openShop(player) } + 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(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?): DialoguePlugin { + return NurmofDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.NURMOF_594) + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/RoladDialogue.java b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/RoladDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/dialogue/RoladDialogue.java rename to Server/src/main/content/region/asgarnia/dwarfmine/dialogue/RoladDialogue.java index 55bb9a641..b59a9b30a 100644 --- a/Server/src/main/java/core/game/content/dialogue/RoladDialogue.java +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/RoladDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.dwarfmine.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; diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/AmbassadorSpanfippleDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/AmbassadorSpanfippleDialogue.kt new file mode 100644 index 000000000..3d94098c1 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/AmbassadorSpanfippleDialogue.kt @@ -0,0 +1,62 @@ +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 org.rs09.consts.NPCs + + +/** + * @author qmqz + */ + +@Initializable +class AmbassadorSpanfippleDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(FacialExpression.THINKING, "Well, it is the White Knights' Castle.").also { stage++ } + } + + 1 -> { + 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(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(FacialExpression.OLD_DEFAULT, "You're probably right. Maybe brown, then.").also { stage++ } + } + + 4 -> { + player(FacialExpression.HALF_THINKING, "I think that may be worse...").also { stage++ } + } + + 5 -> { + npc(FacialExpression.OLD_ANGRY1, "Bah, humans have no sense of style...").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return AmbassadorSpanfippleDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.AMBASSADOR_SPANFIPPLE_4581) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/ApprenticeWorkmanDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/ApprenticeWorkmanDialogue.kt new file mode 100644 index 000000000..b3aa28213 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/ApprenticeWorkmanDialogue.kt @@ -0,0 +1,41 @@ +package content.region.asgarnia.falador.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 + +/** + * @author qmqz + * https://youtu.be/DwQgAQqaXos + */ + +@Initializable +class ApprenticeWorkmanDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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?): DialoguePlugin { + return ApprenticeWorkmanDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.APPRENTICE_WORKMAN_3235) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/CassieDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/CassieDialogue.java similarity index 79% rename from Server/src/main/java/core/game/content/dialogue/CassieDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/CassieDialogue.java index 664f01b98..d509b21a2 100644 --- a/Server/src/main/java/core/game/content/dialogue/CassieDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/CassieDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -37,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; } @@ -56,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/java/core/game/content/dialogue/ChemistDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/ChemistDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/ChemistDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/ChemistDialogue.java index bf8c99c57..8c8255719 100644 --- a/Server/src/main/java/core/game/content/dialogue/ChemistDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/ChemistDialogue.java @@ -1,9 +1,10 @@ -package core.game.content.dialogue; +package content.region.asgarnia.falador.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.diary.AchievementDiary; import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.GroundItemManager; import core.plugin.Initializable; import core.game.node.item.Item; @@ -15,7 +16,6 @@ import core.game.node.item.Item; @Initializable public final class ChemistDialogue extends DialoguePlugin { private boolean replacementReward = false; - private AchievementDiary diary; private int level = 1; /** @@ -45,10 +45,7 @@ public final class ChemistDialogue extends DialoguePlugin { public boolean open(Object... args) { interpreter.sendOptions("Do you want to talk about lamps?", "Yes.", "No.", "No, I'm more interested in impling jars.", "Falador Achievement Diary"); stage = 0; - diary = player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR); - replacementReward = diary.isLevelRewarded(level) - && diary.isComplete(level, true) - && !player.hasItem(diary.getType().getRewards(level)[0]); + replacementReward = AchievementDiary.canReplaceReward(player, DiaryType.FALADOR, level); return true; } @@ -211,7 +208,7 @@ public final class ChemistDialogue extends DialoguePlugin { } break; case 80: - player.getInventory().add(diary.getType().getRewards(level)[0], player); + AchievementDiary.grantReplacement(player, DiaryType.FALADOR, level); npc("Here's your replacement. Please be more careful."); stage = 999; break; @@ -238,7 +235,7 @@ public final class ChemistDialogue extends DialoguePlugin { break; // https://www.youtube.com/watch?v=ZW9k1922Ggk case 105: - if (!diary.isLevelRewarded(1)) { + if (!AchievementDiary.hasClaimedLevelRewards(player, DiaryType.FALADOR, level)) { options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); stage = 106; } else { @@ -293,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: @@ -349,10 +346,10 @@ public final class ChemistDialogue extends DialoguePlugin { break; case 200: - if (diary.isLevelRewarded(level)) { + if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.FALADOR, level)) { npc("But you've already gotten yours!"); stage = 105; - } else if (diary.isComplete(level, true)) { + } else if (AchievementDiary.hasCompletedLevel(player, DiaryType.FALADOR, level)) { npc("So, you've finished. Well done! I believe congratulations", "are in order."); stage = 201; } else { @@ -370,13 +367,8 @@ public final class ChemistDialogue extends DialoguePlugin { break; case 203: npc("This is the second stage of the Falador shield: a kite", "shield. It grants you all the benefits fo the buckler, but", "with increased Prayer restore, and Farming experience", "when using the patches near Falador."); - if (!diary.isLevelRewarded(level)) { - for (Item i : diary.getType().getRewards(level)) { - if (!player.getInventory().add(i, player)) { - GroundItemManager.create(i, player); - } - } - diary.setLevelRewarded(level); + if (!AchievementDiary.hasClaimedLevelRewards(player, DiaryType.FALADOR, level)) { + AchievementDiary.flagRewarded(player, DiaryType.FALADOR, level); } stage = 204; break; 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 new file mode 100644 index 000000000..8d3372d3a --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/DrunkenManDialogue.kt @@ -0,0 +1,66 @@ +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 org.rs09.consts.NPCs + +/** + * @author qmqz + */ + +@Initializable +class DrunkenManDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + npc(FacialExpression.DRUNK, "... whassup?").also { stage++ } + } + + 1 -> { + player(FacialExpression.ASKING, "Are you alright?").also { stage++ } + } + + 2 -> { + npc(FacialExpression.DRUNK, "... see... two of you... why there two of you?").also { stage++ } + } + + 3 -> { + player(FacialExpression.FRIENDLY, "There's only one of me, friend.").also { stage++ } + } + + 4 -> { + npc(FacialExpression.DRUNK, "... no, two of you... you can't count...", + "... maybe you drunk too much...").also { stage++ } + } + + 5 -> { + player(FacialExpression.FRIENDLY, "Whatever you say, friend.").also { stage++ } + } + + 6 -> { + npc(FacialExpression.DRUNK, "... giant hairy cabbages...").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DrunkenManDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DRUNKEN_MAN_3222) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/FaladorGardenerDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorGardenerDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/FaladorGardenerDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/FaladorGardenerDialogue.java index 1f215517d..b2fb3cf0b 100644 --- a/Server/src/main/java/core/game/content/dialogue/FaladorGardenerDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorGardenerDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/FaladorManHouseDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorManHouseDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/FaladorManHouseDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/FaladorManHouseDialogue.java index 59ecfe94c..799e25060 100644 --- a/Server/src/main/java/core/game/content/dialogue/FaladorManHouseDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorManHouseDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/FaladorShopKeeperDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorShopKeeperDialogue.java similarity index 77% rename from Server/src/main/java/core/game/content/dialogue/FaladorShopKeeperDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/FaladorShopKeeperDialogue.java index d975af5b6..051c55474 100644 --- a/Server/src/main/java/core/game/content/dialogue/FaladorShopKeeperDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorShopKeeperDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -37,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; } @@ -56,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/java/core/game/content/dialogue/FaladorSquireDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorSquireDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/FaladorSquireDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/FaladorSquireDialogue.java index d5d97c6b8..8fe25153e 100644 --- a/Server/src/main/java/core/game/content/dialogue/FaladorSquireDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorSquireDialogue.java @@ -1,14 +1,16 @@ -package core.game.content.dialogue; +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.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 core.game.node.item.Item; import core.plugin.Initializable; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; +import content.data.Quests; /** * Represents the falador squire dialogue plugin. @@ -20,7 +22,6 @@ import rs09.game.world.GameWorld; public final class FaladorSquireDialogue extends DialoguePlugin { private boolean useDiaryDialogueTree = false; private boolean replacementReward = false; - private AchievementDiary diary; private int level = 2; /** @@ -59,13 +60,10 @@ 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; - diary = player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR); - replacementReward = diary.isLevelRewarded(level) - && diary.isComplete(level, true) - && !player.hasItem(diary.getType().getRewards(level)[0]); + replacementReward = AchievementDiary.canReplaceReward(player, DiaryType.FALADOR, level); return true; } @@ -84,7 +82,7 @@ public final class FaladorSquireDialogue extends DialoguePlugin { } break; case 80: - player.getInventory().add(diary.getType().getRewards(level)[0], player); + AchievementDiary.grantReplacement(player, DiaryType.FALADOR, level); npc("Here's your replacement. Please be more careful."); stage = 999; break; @@ -109,7 +107,7 @@ public final class FaladorSquireDialogue extends DialoguePlugin { } break; case 105: - if (!diary.isLevelRewarded(level)) { + if (!AchievementDiary.hasClaimedLevelRewards(player, DiaryType.FALADOR, level)) { options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); stage = 106; } else { @@ -164,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: @@ -198,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; @@ -228,10 +226,10 @@ public final class FaladorSquireDialogue extends DialoguePlugin { break; case 200: - if (diary.isLevelRewarded(level)) { + if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.FALADOR, level)) { npc("But you've already gotten yours!"); stage = 105; - } else if (diary.isComplete(level, true)) { + } else if (AchievementDiary.hasCompletedLevel(player, DiaryType.FALADOR, level)) { npc("So, you've finished. Well done! I believe congratulations", "are in order."); stage = 201; } else { @@ -249,14 +247,7 @@ public final class FaladorSquireDialogue extends DialoguePlugin { break; case 203: npc("This is the final stage of the Falador shield: a tower", "shield. It grants you all the benefits fo the buckler", "and kiteshield did, full Prayer restore, and access to", "some interesting new seeds that my friend Wyson has"); - if (!diary.isLevelRewarded(level)) { - for (Item i : diary.getType().getRewards(level)) { - if (!player.getInventory().add(i, player)) { - GroundItemManager.create(i, player); - } - } - diary.setLevelRewarded(level); - } + AchievementDiary.flagRewarded(player, DiaryType.FALADOR, level); stage = 204; break; case 204: @@ -383,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/java/core/game/content/dialogue/FaladorWomenParkDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorWomenParkDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/FaladorWomenParkDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/FaladorWomenParkDialogue.java index 3fd187da8..d65092532 100644 --- a/Server/src/main/java/core/game/content/dialogue/FaladorWomenParkDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorWomenParkDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/FlynnDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FlynnDialogue.java similarity index 79% rename from Server/src/main/java/core/game/content/dialogue/FlynnDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/FlynnDialogue.java index 6ae2ed85e..eed86be01 100644 --- a/Server/src/main/java/core/game/content/dialogue/FlynnDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FlynnDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -37,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; } @@ -52,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/java/core/game/content/dialogue/HairdresserDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/HairdresserDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/HairdresserDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/HairdresserDialogue.java index 4d9d3e948..362b12403 100644 --- a/Server/src/main/java/core/game/content/dialogue/HairdresserDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/HairdresserDialogue.java @@ -1,7 +1,9 @@ -package core.game.content.dialogue; +package content.region.asgarnia.falador.dialogue; import core.game.component.Component; import core.game.container.impl.EquipmentContainer; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/HerquinDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/HerquinDialogue.java similarity index 76% rename from Server/src/main/java/core/game/content/dialogue/HerquinDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/HerquinDialogue.java index 5a5e6c98f..2ea67f374 100644 --- a/Server/src/main/java/core/game/content/dialogue/HerquinDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/HerquinDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -46,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/java/core/game/content/dialogue/LucyPartyRoomDialoguePlugin.java b/Server/src/main/content/region/asgarnia/falador/dialogue/LucyPartyRoomDialoguePlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/LucyPartyRoomDialoguePlugin.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/LucyPartyRoomDialoguePlugin.java index f0fc88d0a..663f0de78 100644 --- a/Server/src/main/java/core/game/content/dialogue/LucyPartyRoomDialoguePlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/LucyPartyRoomDialoguePlugin.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/MakeOverMageDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/MakeOverMageDialogue.java new file mode 100644 index 000000000..2d3a191eb --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/MakeOverMageDialogue.java @@ -0,0 +1,229 @@ +package content.region.asgarnia.falador.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.plugin.Initializable; +import core.game.node.item.Item; + +/** + * Handles the MakeOverMageDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class MakeOverMageDialogue extends DialoguePlugin { + + public MakeOverMageDialogue() { + + } + + public MakeOverMageDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new MakeOverMageDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + if (args.length == 2) { + + 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.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; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + 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: + if (!player.getInventory().contains(7803, 1) && !player.getBank().contains(7803, 1) && !player.getEquipment().contains(7803, 1)) { + interpreter.sendOptions("Select an Option", "Tell me more about this 'make-over'.", "Sure. Do it.", "No thanks.", "Cool amulet! Can I have one?"); + } else { + interpreter.sendOptions("Select an Option", "Tell me more about this 'make-over'.", "Sure. Do it.", "No thanks."); + } + stage = 2; + break; + case 2: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Tell me more about this 'make-over'."); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Sure. Do it."); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "No thanks. I'm happy as Saradomin made me."); + stage = 19; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Cool amulet! Can I have one?"); + stage = 40; + break; + } + break; + case 20: + 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.ANGRY, "Well, go get it then. No freebies here!"); + stage = 22; + break; + case 22: + end(); + break; + case 25: + if(!player.getEquipment().isEmpty()){ + interpreter.sendDialogue("You need to take off all your equipment before the mage", "can change your appearance."); + stage = 900; + } else { + end(); + player.getInterfaceManager().open(new Component(205)); + } + break; + case 10: + 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.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.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.FRIENDLY, "Or, you know, somewhere vaguely close enough", "anyway."); + stage = 14; + break; + case 14: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh... that doesn't sound particularly safe to me..."); + stage = 15; + break; + case 15: + 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.ASKING, "So what do you say? Feel like a change?"); + stage = 17; + break; + case 17: + interpreter.sendOptions("Select an Option", "Sure. Do it.", "No thanks."); + stage = 18; + break; + case 18: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Sure. Do it."); + stage = 20; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "No thanks. I'm happy as Saradomin made me."); + stage = 19; + break; + } + break; + case 19: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ehhh... suit yourself."); + stage = 900; + break; + case 900: + end(); + break; + case 40: + 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: + interpreter.sendOptions("Select an Option", "Sure, here you go.", "No way! that's too expensive."); + stage = 42; + break; + case 42: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Sure, here you go."); + stage = 100; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.AMAZED, "No way! That's too expensive."); + stage = 400; + break; + + } + break; + case 400: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That's fair enough, my jewellery is not to everyone's", "taste."); + stage = 401; + break; + case 401: + end(); + break; + case 600: + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "Uh... No...?"); + stage = 601; + break; + case 601: + interpreter.sendDialogues(npc, FacialExpression.WORRIED, "Good, good, I was worried for a second there!"); + stage = 602; + break; + case 602: + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Uh... Thanks, I guess."); + stage = 603; + break; + case 603: + end(); + break; + case 100: + if (player.getInventory().freeSlots() == 0) { + end(); + player.getPacketDispatch().sendMessage("You don't have enough inventory space."); + } + if (!player.getInventory().contains(995, 100)) { + end(); + player.getPacketDispatch().sendMessage("You need 100 coins."); + } else { + // you recieve an amulet in exchange for 100 coins. + // 7803 + Item remove = new Item(995, 100); + if (!player.getInventory().containsItem(remove)) { + end(); + return true; + } + if (player.getInventory().remove(remove)) { + interpreter.sendItemMessage(7803, "You receive an amulet in exchange for 100 coins."); + player.getInventory().add(new Item(7803, 1)); + stage = 101; + } + } + break; + case 101: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 2676, 599 }; + } +} diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/MiningGuildDwarf.java b/Server/src/main/content/region/asgarnia/falador/dialogue/MiningGuildDwarf.java new file mode 100644 index 000000000..83db1cc57 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/MiningGuildDwarf.java @@ -0,0 +1,135 @@ +package content.region.asgarnia.falador.dialogue; + +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +import core.game.node.entity.skill.Skills; +import core.game.node.entity.npc.NPC; +import core.plugin.Initializable; +import core.game.node.entity.player.Player; + +/** + * Represents the mining guild dwarf. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class MiningGuildDwarf extends DialoguePlugin { + + /** + * Constructs a new {@code MiningGuildDwarf} {@code Object}. + */ + public MiningGuildDwarf() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code MiningGuildDwarf} {@code Object}. + * @param player the player. + */ + public MiningGuildDwarf(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new MiningGuildDwarf(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + if (args.length == 2) { + if (player.getSkills().getStaticLevel(Skills.MINING) < 60) { + interpreter.sendDialogues(npc, null, "Sorry, but you need level 60 Mining to go in there."); + stage = 69; + return true; + } + } + interpreter.sendDialogues(npc, FacialExpression.ASKING, "Welcome to the Mining Guild.", "Can I help you with anything?"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 69: + end(); + break; + case 0: + 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.ASKING, "What have you got in the Guild?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "What do you dwarves do with the ore you mine?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No thanks, I'm fine."); + stage = 30; + break; + + } + break; + case 10: + 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_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.NEUTRAL, "Sorry, but the rules are rules. Do some more training", "first."); + stage = 13; + } else { + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Yes, you can enter if you wish."); + stage = 14; + } + break; + case 13: + end(); + break; + case 14: + end(); + break; + case 20: + 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_THINKING, "I don't see many dwarves here."); + stage = 22; + break; + case 22: + 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.NEUTRAL, "smelting the ore so much easier. There are plenty of", "dwarven traders working in Keldagrim."); + stage = 24; + break; + case 24: + end(); + break; + case 30: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 382 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/PartyPeteDialoguePlugin.java b/Server/src/main/content/region/asgarnia/falador/dialogue/PartyPeteDialoguePlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/PartyPeteDialoguePlugin.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/PartyPeteDialoguePlugin.java index c42a8e77a..02790abad 100644 --- a/Server/src/main/java/core/game/content/dialogue/PartyPeteDialoguePlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/PartyPeteDialoguePlugin.java @@ -1,8 +1,10 @@ -package core.game.content.dialogue; +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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.plugin.Initializable; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/RisingSunInnBartenderDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/RisingSunInnBartenderDialogue.kt new file mode 100644 index 000000000..199a545a0 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/RisingSunInnBartenderDialogue.kt @@ -0,0 +1,156 @@ +package content.region.asgarnia.falador.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.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +/** + * Handles dialogue for Kaylee and Emily + * of the Rising Sun Inn + * + * @author vddCore + */ +@Initializable +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( + FacialExpression.HAPPY, + "Hi! What can I get you?" + ) + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> if (hasAnyBeerGlasses()) { + showTopics( + Topic(FacialExpression.ASKING, "What ales are you serving?", 10), + Topic(FacialExpression.HAPPY, "I've got some beer glasses...", 20) + ) + } else { + playerl(FacialExpression.ASKING, "What ales are you serving?") + .also { stage = 10 } + } + + 10 -> npcl( + 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(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()) { + purchaseBrew(Items.ASGARNIAN_ALE_1905) + } + + 13 -> if (ensureHasMoney()) { + purchaseBrew(Items.WIZARDS_MIND_BOMB_1907) + } + + 14 -> if (ensureHasMoney()) { + purchaseBrew(Items.DWARVEN_STOUT_1913) + } + + 20 -> { + npcl( + 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(FacialExpression.HAPPY, "Yes, please!", 22), + Topic(FacialExpression.NEUTRAL, "No thanks, I like my empty beer glasses.", END_DIALOGUE) + ) + + 22 -> { + trySellAllBeerGlasses() + + npcl( + FacialExpression.FRIENDLY, + "There you go!" + ).also { stage = END_DIALOGUE } + } + + 30 -> { + playerl(FacialExpression.FRIENDLY, "Thanks, ${npc.name}.") + .also { stage = END_DIALOGUE } + } + } + + return true + } + + override fun getIds(): IntArray = intArrayOf( + NPCs.EMILY_736, + NPCs.KAYLEE_3217 + ) + + private fun ensureHasMoney(): Boolean { + if (!inInventory(player, Items.COINS_995, 3)) { + npcl(FacialExpression.ANGRY, "No freeloaders!") + .also { + sendMessage(player, "You don't have enough money to buy that.") + stage = END_DIALOGUE + } + return false + } + + return true + } + + private fun purchaseBrew(brewId: Int) { + if (removeItem(player, Item(Items.COINS_995, 3))) { + addItemOrDrop(player, brewId) + + sendItemDialogue( + player, + Item(brewId), + "You hand 3 coins over to ${npc.name}. She gives you ${when (brewId) { + Items.WIZARDS_MIND_BOMB_1907 -> "a Wizard's Mind Bomb" + Items.DWARVEN_STOUT_1913 -> "a Dwarven Stout" + Items.ASGARNIAN_ALE_1905 -> "an Asgarnian Ale" + else -> "a broken fucking dialogue" + }}." + ).also { stage = 30 } + } + } + + private fun hasAnyBeerGlasses() + = inInventory(player, Items.BEER_GLASS_1919) + || inInventory(player, Items.BEER_GLASS_1920) + + private fun trySellAllBeerGlasses() { + val regularGlassAmount = amountInInventory(player, Items.BEER_GLASS_1919) + val notedGlassAmount = amountInInventory(player, Items.BEER_GLASS_1920) + + if (removeItem(player, Item(Items.BEER_GLASS_1919, regularGlassAmount))) { + addItem(player, Items.COINS_995, regularGlassAmount * 2) + } + + if (removeItem(player, Item(Items.BEER_GLASS_1920, notedGlassAmount))) { + addItem(player, Items.COINS_995, notedGlassAmount * 2) + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/SarahFarmingPlugin.java b/Server/src/main/content/region/asgarnia/falador/dialogue/SarahFarmingPlugin.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/SarahFarmingPlugin.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/SarahFarmingPlugin.java index b657694e9..e3c33b19f 100644 --- a/Server/src/main/java/core/game/content/dialogue/SarahFarmingPlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/SarahFarmingPlugin.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/SirReniteeDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/dialogue/SirReniteeDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.java index e8fb7db64..f8f9695c9 100644 --- a/Server/src/main/java/core/game/content/dialogue/SirReniteeDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.java @@ -1,6 +1,9 @@ -package core.game.content.dialogue; +package content.region.asgarnia.falador.dialogue; +import core.ServerConstants; import core.Util; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import org.rs09.consts.Items; import core.game.node.entity.npc.NPC; @@ -9,7 +12,7 @@ import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.item.Item; import core.tools.RandomFunction; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.construction.CrestType; +import content.global.skill.construction.CrestType; /** * Represents the dialogue to handle Sir Renitee @@ -63,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.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/java/core/game/content/dialogue/SirVyvinDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/SirVyvinDialogue.java similarity index 87% rename from Server/src/main/java/core/game/content/dialogue/SirVyvinDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/SirVyvinDialogue.java index 8e0695de9..b593c1259 100644 --- a/Server/src/main/java/core/game/content/dialogue/SirVyvinDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/SirVyvinDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/WayneDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/WayneDialogue.java similarity index 81% rename from Server/src/main/java/core/game/content/dialogue/WayneDialogue.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/WayneDialogue.java index 857b414f1..85a9824be 100644 --- a/Server/src/main/java/core/game/content/dialogue/WayneDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WayneDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -37,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/java/core/game/content/dialogue/WhiteKnightDialoguePlugin.java b/Server/src/main/content/region/asgarnia/falador/dialogue/WhiteKnightDialoguePlugin.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/WhiteKnightDialoguePlugin.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/WhiteKnightDialoguePlugin.java index ed4653eac..300edab67 100644 --- a/Server/src/main/java/core/game/content/dialogue/WhiteKnightDialoguePlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WhiteKnightDialoguePlugin.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.asgarnia.falador.dialogue; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/WorkmanDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/WorkmanDialogue.kt new file mode 100644 index 000000000..81bd9e2f0 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WorkmanDialogue.kt @@ -0,0 +1,37 @@ +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 WorkmanDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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?): DialoguePlugin { + return WorkmanDialogue(player) + } + + override fun getIds(): IntArray { + 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 new file mode 100644 index 000000000..b26643bba --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt @@ -0,0 +1,285 @@ +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.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.link.diary.* + +/** + * Represents the Wyson the gardener dialogue. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +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) {} + + 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 + 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 + } + 1 -> when (buttonId) { + 1 -> { + player("Yes please, I need woad leaves.") + stage = 10 + } + 2 -> { + player("Sorry, but I'm not interested.") + stage = 200 + } + } + 10 -> { + npc("How much are you willing to pay?") + stage = 11 + } + 11 -> { + options("How about 5 coins?", "How about 10 coins?", "How about 15 coins?", "How about 20 coins?") + stage = 12 + } + 12 -> when (buttonId) { + 1 -> { + player("How about 5 coins?") + stage = 110 + } + 2 -> { + player("How about 10 coins?") + stage = 110 + } + 3 -> { + player("How about 15 coins?") + stage = 130 + } + 4 -> { + player("How about 20 coins?") + stage = 140 + } + } + 110 -> { + npc("No no, that's far too little. Woad leaves are hard to get. I", "used to have plenty but someone kept stealing them off", "me.") + stage = 111 + } + 111 -> end() + 130 -> { + npc("Mmmm... ok, that sounds fair.") + stage = 131 + } + 131 -> if (removeItem(player,Item(Items.COINS_995, 15) ,Container.INVENTORY)) { + addItemOrDrop(player, Items.WOAD_LEAF_1793, 1) + + player("Thanks.") + sendMessage(player, "You buy a woad leaf from Wyson.") + stage = 132 + } else { + end() + 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.") + stage = 133 + } + 133 -> end() + 140 -> { + npc("Thanks for being generous", "here's an extra woad leaf.") + stage = 141 + } + 141 -> if (removeItem(player,Item(Items.COINS_995, 20) ,Container.INVENTORY)) { + addItemOrDrop(player, Items.WOAD_LEAF_1793, 2) + player("Thanks.") + sendMessage(player, "You buy two woad leaves from Wyson.") + stage = 132 + } else { + end() + 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("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("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 -> { + player("Okay, I will trade the mole skin.") + stage = 920 + } + 3 -> { + player("Alright, I'll trade the claws and skin.") + stage = 930 + } + 4 -> { + player("Yes please, I need woad leaves.") + stage = 10 + } + 5 -> { + player("Sorry, but I'm not interested.") + stage = 200 + } + } + 910 -> { + 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 + } + } + 920 -> { + 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 + } + } + 930 -> { + 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 + } + } + 999 -> end() + } + return true + } + + /** + * Adds nests. + */ + 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) + } + } + } + + 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) + } + } + } + } + + override fun getIds(): IntArray { + return intArrayOf(36) + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/npc/city/falador/gardensupplier.java b/Server/src/main/content/region/asgarnia/falador/dialogue/gardensupplier.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/npc/city/falador/gardensupplier.java rename to Server/src/main/content/region/asgarnia/falador/dialogue/gardensupplier.java index 4162fe4ff..e3dfa2795 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/falador/gardensupplier.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/gardensupplier.java @@ -1,6 +1,6 @@ -package core.game.node.entity.npc.city.falador; +package content.region.asgarnia.falador.dialogue; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; diff --git a/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt b/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt new file mode 100644 index 000000000..aac922c9a --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt @@ -0,0 +1,300 @@ +package content.region.asgarnia.falador.diary + +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 { + private const val ATTRIBUTE_BLACK_CHAINBODY_PURCHASED = "diary:falador:black-chain-bought" + + private val MINING_GUILD_AREA = ZoneBorders(3016, 9731, 3055, 9756) + private val DARK_WIZARDS_TOWER_ROOF_AREA = ZoneBorders(2904, 3331, 2911, 3338, 2) + private val WHITE_KNIGHTS_CASTLE_ROOF_AREA = ZoneBorders(2955, 3333, 2996, 3354, 3) + private val PARK_TREE_PATCH_AREA = ZoneBorders(3002, 3371, 3006, 3375) + private val PARK_POND_AREA = ZoneBorders(2987, 3381, 2994, 3386) + 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( + Items.PROSELYTE_SALLET_9672, + Items.PROSELYTE_HAUBERK_9674, + Items.PROSELYTE_CUISSE_9676 + ) + + private val PROSELYTE_FULL_ARMOR_FEMALE = intArrayOf( + Items.PROSELYTE_SALLET_9672, + Items.PROSELYTE_HAUBERK_9674, + Items.PROSELYTE_TASSET_9678 + ) + + private val PARTY_BALLOONS = intArrayOf( + Scenery.PARTY_BALLOON_115, Scenery.PARTY_BALLOON_116, Scenery.PARTY_BALLOON_117, + Scenery.PARTY_BALLOON_118, Scenery.PARTY_BALLOON_119, Scenery.PARTY_BALLOON_120, + Scenery.PARTY_BALLOON_121, Scenery.PARTY_BALLOON_122 + ) + + private val PARK_DUCKS = intArrayOf( + NPCs.DUCK_46, NPCs.DUCK_2693 + ) + + private val FALADOR_GUARD = intArrayOf( + NPCs.GUARD_9, // Guard - City Gates + NPCs.GUARD_3230, // Guard - Greataxe + NPCs.GUARD_3228, // Guard - Fountain Longsword + NPCs.GUARD_3229 // Guard - Fountain Crossbow + ) + + private val SKELETAL_WYVERNS = intArrayOf( + NPCs.SKELETAL_WYVERN_3068, NPCs.SKELETAL_WYVERN_3069, + NPCs.SKELETAL_WYVERN_3070, NPCs.SKELETAL_WYVERN_3071 + ) + + object EasyTasks { + const val PORT_SARIM_SARAH_BUY_FARMING_AMULET = 0 + const val RISING_SUN_BUY_A_STATBOOST = 1 + const val WAYNE_BUY_AND_WEAR_BLACK_CHAINBODY = 2 + const val WHITE_KNIGHTS_CASTLE_CLIMB_TO_TOP = 3 + const val SIR_RENITEE_FAMILY_CREST_DISCOVER = 4 + const val PARK_ENTER_MOLE_LAIR = 5 + const val FEED_RIDGELEY_AT_HAIRDRESSERS = 6 + const val FILL_BUCKET_FROM_NORTHERN_PUMP = 7 + const val HEAL_ELEMENTAL_WIZARD_WITH_SPELL = 8 + const val PARK_KILL_A_DUCK = 9 + const val SOUTHERN_ROAD_KILL_HIGHWAYMAN = 10 + const val MAKE_AIR_TIARA = 11 + const val POP_PARTY_BALLOON = 12 + const val PORT_SARIM_RECHARGE_PRAYER_POINTS = 13 + const val TAKE_BOAT_TO_ENTRANA = 14 + } + + object MediumTasks { + const val PORT_SARIM_CRAFT_FRUIT_BASKET = 0 + const val CRAWL_UNDER_SOUTHERN_WALL = 1 + const val GRAPPLE_AND_JUMP_OFF_NORTHERN_WALL = 2 + const val INCREASE_WHITE_KNIGHT_REPUTATION = 3 + const val ICE_DUNGEON_KILL_ICE_GIANT = 4 + const val CHEMISTS_LIGHT_BULLSEYE_LANTERN = 5 + const val PICKPOCKET_GUARD = 6 + const val PORT_SARIM_NORTHERN_PATCH_PLACE_SCARECROW = 7 + const val SALUTE_SIR_TIFFY_CASHIEN_IN_INITIATE_ARMOR = 8 + const val SMITH_BLURITE_CROSSBOW_LIMBS_ON_THURGOS_ANVIL = 9 + const val PORT_SARIM_CHAROS_TRAVEL_TO_MUSA_POINT = 10 + const val PORT_SARIM_VISIT_RAT_PITS = 11 + } + + object HardTasks { + const val DARK_WIZARDS_TOWER_ASCEND_IN_FULL_PROSELYTE_ARMOR = 0 + const val CHANGE_FAMILY_CREST_TO_SARADOMIN = 1 + const val CRAFT_196_AIR_RUNES_AT_ONCE = 2 + const val CUT_DOWN_GROWN_YEW_OR_MAGIC_TREE = 3 + const val DIAL_FAIRY_RING_MUDSKIPPER_POINT = 4 + const val PORT_SARIM_BETTY_DYE_CAPE_PINK = 5 + const val ENTER_MINING_GUILD = 6 + const val MUDSKIPPER_POINT_KILL_MOGRE = 7 + const val ICE_DUNGEON_KILL_SKELETAL_WYVERN = 8 + const val PORT_SARIM_FISH_STORE_SUMMON_IBIS = 9 + } + } + + override val areaTasks + get() = arrayOf( + AreaDiaryTask( + WHITE_KNIGHTS_CASTLE_ROOF_AREA, + DiaryLevel.EASY, + EasyTasks.WHITE_KNIGHTS_CASTLE_CLIMB_TO_TOP + ), + + AreaDiaryTask( + MINING_GUILD_AREA, + DiaryLevel.HARD, + HardTasks.ENTER_MINING_GUILD + ), + + AreaDiaryTask( + DARK_WIZARDS_TOWER_ROOF_AREA, + DiaryLevel.HARD, + HardTasks.DARK_WIZARDS_TOWER_ASCEND_IN_FULL_PROSELYTE_ARMOR, + ) { player -> + allInEquipment(player, *PROSELYTE_FULL_ARMOR_MALE) + || allInEquipment(player, *PROSELYTE_FULL_ARMOR_FEMALE) + } + ) + + override fun onInteracted(player: Player, event: InteractionEvent) { + when (player.viewport.region.id) { + 12084 -> { + if (event.option == "burst" && event.target.id in PARTY_BALLOONS) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.POP_PARTY_BALLOON + ) + } + } + } + + if (event.option == "pickpocket" && (event.target.id in FALADOR_GUARD && inBorders(player, FALADOR_GENERAL_AREA) && getStatLevel(player, Skills.THIEVING) >= 40)) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.PICKPOCKET_GUARD + ) + } + } + + override fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) { + when (event.dialogue) { + is RisingSunInnBartenderDialogue -> { + if (event.currentStage in 12..14) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.RISING_SUN_BUY_A_STATBOOST + ) + } + } + } + } + + override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { + when (player.viewport.region.id) { + 11828 -> when (event.itemId) { + Items.YEW_LOGS_1515, Items.MAGIC_LOGS_1513 -> { + if (inBorders(player, PARK_TREE_PATCH_AREA)) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.CUT_DOWN_GROWN_YEW_OR_MAGIC_TREE + ) + } + } + } + } + } + + override fun onNpcKilled(player: Player, event: NPCKillEvent) { + when (player.viewport.region.id) { + 11828 -> if (event.npc.id in PARK_DUCKS && inBorders(event.npc, PARK_POND_AREA)) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.PARK_KILL_A_DUCK + ) + } + + 12181 -> if (event.npc.id in SKELETAL_WYVERNS) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.ICE_DUNGEON_KILL_SKELETAL_WYVERN + ) + } + } + if (event.npc.id == NPCs.MOGRE_114){ + finishTask(player, DiaryLevel.HARD, HardTasks.MUDSKIPPER_POINT_KILL_MOGRE) + } + } + + override fun onItemPurchasedFromShop(player: Player, event: ItemShopPurchaseEvent) { + when { + inBorders(player, WAYNES_CHAINS_AREA) && (event.itemId == Items.BLACK_CHAINBODY_1107) -> { + fulfillTaskRequirement( + player, + DiaryLevel.EASY, + EasyTasks.WAYNE_BUY_AND_WEAR_BLACK_CHAINBODY, + ATTRIBUTE_BLACK_CHAINBODY_PURCHASED + ) + } + + inBorders(player, SARAHS_FARMING_SHOP_AREA) && (event.itemId == Items.AMULET_OF_FARMING8_12622) -> { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.PORT_SARIM_SARAH_BUY_FARMING_AMULET + ) + } + } + } + + override fun onItemEquipped(player: Player, event: ItemEquipEvent) { + when { + inBorders(player, WAYNES_CHAINS_AREA) && (event.itemId == Items.BLACK_CHAINBODY_1107) -> { + whenTaskRequirementFulfilled(player, ATTRIBUTE_BLACK_CHAINBODY_PURCHASED) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.WAYNE_BUY_AND_WEAR_BLACK_CHAINBODY + ) + } + } + } + } + + override fun onFairyRingDialed(player: Player, event: FairyRingDialEvent) { + if (event.fairyRing == FairyRing.AIQ) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.DIAL_FAIRY_RING_MUDSKIPPER_POINT + ) + } + } + + 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/java/core/game/interaction/city/FaladorNodePlugin.java b/Server/src/main/content/region/asgarnia/falador/handlers/FaladorNodePlugin.java similarity index 94% rename from Server/src/main/java/core/game/interaction/city/FaladorNodePlugin.java rename to Server/src/main/content/region/asgarnia/falador/handlers/FaladorNodePlugin.java index 9e2bd9c48..3bb521e84 100644 --- a/Server/src/main/java/core/game/interaction/city/FaladorNodePlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/FaladorNodePlugin.java @@ -1,9 +1,8 @@ -package core.game.interaction.city; +package content.region.asgarnia.falador.handlers; -import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; @@ -16,7 +15,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; @@ -61,7 +60,6 @@ public final class FaladorNodePlugin extends OptionHandler { // dwarven mine SceneryDefinition.forId(30868).getHandlers().put("option:squeeze-through", this); SceneryDefinition.forId(5020).getHandlers().put("option:ride", this); - ItemDefinition.forId(245).getHandlers().put("option:take", this); // fally park. NPCDefinition.forId(2290).getHandlers().put("option:talk-to", this); SceneryDefinition.forId(11708).getHandlers().put("option:close", this); diff --git a/Server/src/main/java/core/game/node/entity/npc/bosses/GiantMoleNPC.java b/Server/src/main/content/region/asgarnia/falador/handlers/GiantMoleNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/bosses/GiantMoleNPC.java rename to Server/src/main/content/region/asgarnia/falador/handlers/GiantMoleNPC.java index 42f63420d..20edbb3ac 100644 --- a/Server/src/main/java/core/game/node/entity/npc/bosses/GiantMoleNPC.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/GiantMoleNPC.java @@ -1,13 +1,14 @@ -package core.game.node.entity.npc.bosses; +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; import core.game.component.ComponentPlugin; -import core.game.content.global.BossKillCounter; -import core.game.content.global.LightSource; -import core.game.content.global.action.DigAction; -import core.game.content.global.action.DigSpadeHandler; +import content.data.BossKillCounter; +import content.data.LightSource; +import core.game.global.action.DigAction; +import core.game.global.action.DigSpadeHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; @@ -20,7 +21,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.RegionManager; @@ -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. @@ -211,11 +215,7 @@ public final class GiantMoleNPC extends AbstractNPC { @Override public void onImpact(final Entity entity, BattleState state) { if (!getLocks().isInteractionLocked()) { - int chance = 15; - if (getSkills().getLifepoints() < getSkills().getMaximumLifepoints() >> 1) { - chance *= 3; - } - if (RandomFunction.randomize(100) < chance && inCombat()) { + if (RandomFunction.randomize(100) < 24 && inCombat() && getSkills().getLifepoints() < 100 && getSkills().getLifepoints() > 5){ dig(); return; } @@ -224,11 +224,11 @@ public final class GiantMoleNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (digging) { return false; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override @@ -243,6 +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)); + 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)) { @@ -257,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; } @@ -298,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/java/core/game/content/activity/guild/MiningGuildPlugin.java b/Server/src/main/content/region/asgarnia/falador/handlers/MiningGuildPlugin.java similarity index 76% rename from Server/src/main/java/core/game/content/activity/guild/MiningGuildPlugin.java rename to Server/src/main/content/region/asgarnia/falador/handlers/MiningGuildPlugin.java index d77056288..1409c3aca 100644 --- a/Server/src/main/java/core/game/content/activity/guild/MiningGuildPlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/MiningGuildPlugin.java @@ -1,8 +1,9 @@ -package core.game.content.activity.guild; +package content.region.asgarnia.falador.handlers; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -14,6 +15,8 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.getDynLevel; + /** * Represents the plugin used for the mining guild. * @author 'Vexia @@ -27,14 +30,19 @@ 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 (player.getSkills().getStaticLevel(Skills.MINING) < 60) { + if (getDynLevel(player, Skills.MINING) < 60) { player.getDialogueInterpreter().open(382, NPC.create(382, Location.create(0, 0, 0)), 1); return true; } @@ -45,7 +53,7 @@ public final class MiningGuildPlugin extends OptionHandler { return true; } if (option.equals("open")) { - if (player.getSkills().getStaticLevel(Skills.MINING) < 60) { + if (getDynLevel(player, Skills.MINING) < 60) { player.getDialogueInterpreter().open(382, NPC.create(382, Location.create(0, 0, 0)), 1); return true; } diff --git a/Server/src/main/java/core/game/interaction/object/WantedPoster.java b/Server/src/main/content/region/asgarnia/falador/handlers/WantedPoster.java similarity index 93% rename from Server/src/main/java/core/game/interaction/object/WantedPoster.java rename to Server/src/main/content/region/asgarnia/falador/handlers/WantedPoster.java index 4325eebfa..67bd9780d 100644 --- a/Server/src/main/java/core/game/interaction/object/WantedPoster.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/WantedPoster.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.asgarnia.falador.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/content/activity/partyroom/BalloonManager.java b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/BalloonManager.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/partyroom/BalloonManager.java rename to Server/src/main/content/region/asgarnia/falador/handlers/partyroom/BalloonManager.java index 8bbd52077..79d70ae79 100644 --- a/Server/src/main/java/core/game/content/activity/partyroom/BalloonManager.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/BalloonManager.java @@ -1,4 +1,4 @@ -package core.game.content.activity.partyroom; +package content.region.asgarnia.falador.handlers.partyroom; import java.util.ArrayList; import java.util.List; @@ -8,20 +8,22 @@ 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.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.system.task.Pulse; -import rs09.game.world.GameWorld; +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.Plugin; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + + /** * Manages the droped party balloons. * @author Vexia @@ -84,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(); @@ -244,9 +246,6 @@ public final class BalloonManager extends OptionHandler { getBalloons().remove(object); player.animate(Animation.create(10017)); - // Pop a party balloon - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 0, 12); - GameWorld.getPulser().submit(new Pulse(1) { int counter; diff --git a/Server/src/main/java/core/game/content/activity/partyroom/ChestViewer.java b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/ChestViewer.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/partyroom/ChestViewer.java rename to Server/src/main/content/region/asgarnia/falador/handlers/partyroom/ChestViewer.java index d00ba8dd6..c1b2302a0 100644 --- a/Server/src/main/java/core/game/content/activity/partyroom/ChestViewer.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/ChestViewer.java @@ -1,4 +1,4 @@ -package core.game.content.activity.partyroom; +package content.region.asgarnia.falador.handlers.partyroom; import core.game.component.CloseEvent; import core.game.component.Component; @@ -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; @@ -82,9 +85,9 @@ public final class ChestViewer { */ public void update(int type, ContainerEvent event) { if (event == null) { - player.getPacketDispatch().sendAccessMask(1278, 27, 647, 0, 10); - player.getPacketDispatch().sendAccessMask(1278, 28, 647, 0, 10); - player.getPacketDispatch().sendAccessMask(1278, 29, 647, 0, 10); + player.getPacketDispatch().sendIfaceSettings(1278, 27, 647, 0, 10); + player.getPacketDispatch().sendIfaceSettings(1278, 28, 647, 0, 10); + player.getPacketDispatch().sendIfaceSettings(1278, 29, 647, 0, 10); switch (type) { case 0: player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", BEING_DROPPED); @@ -310,14 +313,14 @@ public final class ChestViewer { player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", INV_OPTIONS); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 90, c.toArray(), 10, false)); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 94, player.getInventory(), false)); - player.getPacketDispatch().sendAccessMask(1278, 0, 648, 0, 28); + player.getPacketDispatch().sendIfaceSettings(1278, 0, 648, 0, 28); } } @Override public void refresh(Container c) { if (c instanceof DepositContainer) { - player.getPacketDispatch().sendAccessMask(1278, 0, 648, 0, 28); + player.getPacketDispatch().sendIfaceSettings(1278, 0, 648, 0, 28); player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", ACCEPT); player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", INV_OPTIONS); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 94, player.getInventory(), false)); diff --git a/Server/src/main/java/core/game/interaction/object/DropPartyLeverOptionPlugin.java b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/DropPartyLeverOptionPlugin.java similarity index 93% rename from Server/src/main/java/core/game/interaction/object/DropPartyLeverOptionPlugin.java rename to Server/src/main/content/region/asgarnia/falador/handlers/partyroom/DropPartyLeverOptionPlugin.java index f8431a5e5..fb0e9d992 100644 --- a/Server/src/main/java/core/game/interaction/object/DropPartyLeverOptionPlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/DropPartyLeverOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.asgarnia.falador.handlers.partyroom; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; @@ -6,7 +6,7 @@ import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; diff --git a/Server/src/main/java/core/game/content/activity/partyroom/PartyRoomPlugin.java b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/PartyRoomPlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/partyroom/PartyRoomPlugin.java rename to Server/src/main/content/region/asgarnia/falador/handlers/partyroom/PartyRoomPlugin.java index a6ae4b2ee..16ef90a5f 100644 --- a/Server/src/main/java/core/game/content/activity/partyroom/PartyRoomPlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/PartyRoomPlugin.java @@ -1,18 +1,18 @@ -package core.game.content.activity.partyroom; +package content.region.asgarnia.falador.handlers.partyroom; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.SceneryDefinition; 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.content.dialogue.DialogueAction; +import core.game.dialogue.DialogueAction; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; @@ -22,12 +22,12 @@ import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; import kotlin.Unit; -import rs09.game.world.GameWorld; +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.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles the party room. @@ -72,8 +72,8 @@ public final class PartyRoomPlugin extends OptionHandler { SceneryDefinition.forId(OPEN_CHEST).getHandlers().put("option:deposit", this); SceneryDefinition.forId(OPEN_CHEST).getHandlers().put("option:shut", this); SceneryDefinition.forId(LEVER).getHandlers().put("option:pull", this); - PluginManager.definePlugin(new DepositInterfaceHandler()); - PluginManager.definePlugin(new BalloonManager()); + ClassScanner.definePlugin(new DepositInterfaceHandler()); + ClassScanner.definePlugin(new BalloonManager()); return this; } @@ -316,7 +316,7 @@ public final class PartyRoomPlugin extends OptionHandler { viewer.getContainer().addItem(slot, ammount); break; case 234: - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { viewer.getContainer().addItem(slot, (int) value); return Unit.INSTANCE; }); @@ -347,7 +347,7 @@ public final class PartyRoomPlugin extends OptionHandler { viewer.getContainer().takeItem(slot, ammount); break; case 234: - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { viewer.getContainer().takeItem(slot, (int) value); return Unit.INSTANCE; }); diff --git a/Server/src/main/java/core/game/content/quest/free/TheKnightsSword.java b/Server/src/main/content/region/asgarnia/falador/quest/TheKnightsSword.java similarity index 98% rename from Server/src/main/java/core/game/content/quest/free/TheKnightsSword.java rename to Server/src/main/content/region/asgarnia/falador/quest/TheKnightsSword.java index ff19f2d26..70ca79515 100644 --- a/Server/src/main/java/core/game/content/quest/free/TheKnightsSword.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/TheKnightsSword.java @@ -1,10 +1,11 @@ -package core.game.content.quest.free; +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.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/java/core/game/content/quest/free/blackknightsfortress/BKCabbagePlugin.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKCabbagePlugin.java similarity index 86% rename from Server/src/main/java/core/game/content/quest/free/blackknightsfortress/BKCabbagePlugin.java rename to Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKCabbagePlugin.java index 8faa8ee2d..1fdcd3565 100644 --- a/Server/src/main/java/core/game/content/quest/free/blackknightsfortress/BKCabbagePlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKCabbagePlugin.java @@ -1,10 +1,11 @@ -package core.game.content.quest.free.blackknightsfortress; +package content.region.asgarnia.falador.quest.blackknightsfortress; import core.game.interaction.NodeUsageEvent; 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/java/core/game/content/quest/free/blackknightsfortress/BKFortressPlugin.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKFortressPlugin.java similarity index 83% rename from Server/src/main/java/core/game/content/quest/free/blackknightsfortress/BKFortressPlugin.java rename to Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKFortressPlugin.java index 3311d3adb..9f3c3ad6d 100644 --- a/Server/src/main/java/core/game/content/quest/free/blackknightsfortress/BKFortressPlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKFortressPlugin.java @@ -1,21 +1,24 @@ -package core.game.content.quest.free.blackknightsfortress; +package content.region.asgarnia.falador.quest.blackknightsfortress; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +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; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; -import rs09.game.world.repository.Repository; +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 new file mode 100644 index 000000000..b8d4dd509 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKListenDialogue.java @@ -0,0 +1,162 @@ +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' + * fortress quest. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class BKListenDialogue extends DialoguePlugin { + + /** + * Represents the cabbage item. + */ + private static final Item CABBAGE = new Item(1965); + + /** + * Represents the animation to use. + */ + private static final Animation ANIMATION = new Animation(832); + + /** + * Represents the captain npc. + */ + private final int captain = 610; + + /** + * Represents the witch npc. + */ + private final int witch = 611; + + /** + * Represents the goblin greldo npc. + */ + private final int greldo = 612; + + /** + * Represents the cat npc. + */ + private final int cat = 4607; + + /** + * Constructs a new {@code BKListenDialogue} {@code Object}. + */ + public BKListenDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code BKListenDialogue} {@code Object}. + * @param player the player. + */ + public BKListenDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new BKListenDialogue(player); + } + + @Override + public boolean open(Object... args) { + if (args != null && args.length == 2) { + interpreter.sendDialogues(witch, FacialExpression.NEUTRAL, "Where has Greldo got to with that magic cabbage!"); + stage = 10; + player.animate(ANIMATION); + return true; + } + interpreter.sendDialogues(captain, FacialExpression.ASKING, "So... how's the secret weapon coming along?"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendDialogues(witch, FacialExpression.HAPPY, "The invincibility potion is almost ready..."); + stage = 1; + break; + case 1: + interpreter.sendDialogues(witch, FacialExpression.HAPPY, "It's taken me FIVE YEARS, but it's almost ready."); + stage = 2; + break; + case 2: + 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, 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, FacialExpression.NEUTRAL, "The soil there is slightly magical and it gives the", "cabbages slight magical properties...."); + stage = 5; + break; + case 5: + interpreter.sendDialogues(witch, FacialExpression.AMAZED, "...not to mention the trees!"); + stage = 6; + break; + case 6: + 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, FacialExpression.OLD_NORMAL, "Yeth, Mithreth."); + stage = 8; + break; + case 8: + player.getQuestRepository().getQuest(Quests.BLACK_KNIGHTS_FORTRESS).setStage(player, 20); + end(); + break; + case 10: + interpreter.sendDialogues(captain, FacialExpression.ASKING, "What's that noise?"); + stage = 11; + break; + case 11: + interpreter.sendDialogues(witch, FacialExpression.NEUTRAL, "Hopefully Greldo with the cabbage... yes, look her it", "co....NOOOOOoooo!"); + stage = 12; + break; + case 12: + interpreter.sendDialogues(witch, FacialExpression.EXTREMELY_SHOCKED, "My potion!"); + stage = 13; + break; + case 13: + interpreter.sendDialogues(captain, FacialExpression.WORRIED, "Oh boy, this doesn't look good!"); + stage = 14; + break; + case 14: + interpreter.sendDialogues(cat, FacialExpression.EXTREMELY_SHOCKED, "Meow!"); + stage = 15; + break; + case 15: + if (player.getInventory().remove(CABBAGE)) { + 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; + case 16: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 992752973 }; + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/BlackKnightNPC.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightNPC.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/other/BlackKnightNPC.java rename to Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightNPC.java index 3df7c8395..1b38a40f8 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/BlackKnightNPC.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.asgarnia.falador.quest.blackknightsfortress; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/java/core/game/content/quest/free/blackknightsfortress/BlackKnightsFortress.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightsFortress.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/free/blackknightsfortress/BlackKnightsFortress.java rename to Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightsFortress.java index fca3763b8..a3a1bf405 100644 --- a/Server/src/main/java/core/game/content/quest/free/blackknightsfortress/BlackKnightsFortress.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BlackKnightsFortress.java @@ -1,10 +1,11 @@ -package core.game.content.quest.free.blackknightsfortress; +package content.region.asgarnia.falador.quest.blackknightsfortress; 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.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the black knights fortress quest. @@ -23,12 +24,12 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugins(new BKCabbagePlugin(), new BKFortressPlugin(), new SirAmikVarzeDialogue()); + ClassScanner.definePlugins(new BKCabbagePlugin(), new BKFortressPlugin(), new SirAmikVarzeDialogue()); return this; } 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 new file mode 100644 index 000000000..51fa14058 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/FortressGuardDialogue.java @@ -0,0 +1,235 @@ +package content.region.asgarnia.falador.quest.blackknightsfortress; + +import java.util.List; + +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +import core.game.global.action.DoorActionHandler; +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.game.world.map.RegionManager; + +/** + * Represents the fortress guard dialogue. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public class FortressGuardDialogue extends DialoguePlugin { + + /** + * Represents the uniform item. + */ + private static final Item[] UNIFORM = new Item[] { new Item(1139), new Item(1101) }; + + /** + * Constructs a new {@code FortressGuardDialogue} {@code Object}. + */ + public FortressGuardDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code FortressGuardDialogue} {@code Object}. + * @param player the player. + */ + public FortressGuardDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new FortressGuardDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + if (args.length == 2) { + 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, 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.ANGRY, "Get lost. This is private property."); + stage = 0; + } else { + interpreter.sendDialogues(npc, FacialExpression.FURIOUS, "Hey! Get back on duty!"); + stage = 30; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, but I work here!"); + stage = 1; + break; + case 1: + 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.ASKING, "How can you tell?"); + stage = 3; + break; + case 3: + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "You're not even wearing proper guards uniform!"); + stage = 4; + break; + case 4: + interpreter.sendOptions("Select an Option", "Oh pleeeaase let me in!", "So what is this uniform?"); + stage = 5; + break; + case 5: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh pleeeaaase let me in!"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "So what is this uniform?"); + stage = 20; + break; + } + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.ANNOYED, "Go away. You're getting annoying."); + stage = 11; + break; + case 11: + end(); + break; + case 20: + 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_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_ASKING, "What was that you muttered?"); + stage = 23; + break; + case 23: + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Oh, nothing important!"); + stage = 24; + break; + case 24: + end(); + break; + case 30: + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Uh..."); + stage = 31; + break; + case 31: + end(); + break; + case 40: + interpreter.sendOptions("Select an Option", "Yes, but I work here!", "Oh, sorry.", "So who does it belong to?"); + stage = 41; + break; + case 41: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, but I work here!"); + stage = 1; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, sorry."); + stage = 42; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.ASKING, "So who does it belong to?"); + stage = 44; + break; + } + break; + case 42: + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Don't let it happen again."); + stage = 43; + break; + case 43: + end(); + break; + case 44: + 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.FRIENDLY, "Oh. Okay, thanks."); + stage = 46; + break; + case 46: + end(); + break; + case 50: + interpreter.sendOptions("Select an Option", "Okay, I won't.", "I don't care. I'm going in anyway."); + stage = 51; + break; + case 51: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay, I won't."); + stage = 52; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I don't care. I'm going in anyway."); + stage = 54; + break; + } + break; + case 52: + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Wise move."); + stage = 53; + break; + case 53: + end(); + break; + case 54: + end(); + DoorActionHandler.handleAutowalkDoor(player, RegionManager.getObject(0, 3020, 3515)); + List npcs = RegionManager.getLocalNpcs(player); + for (NPC npc : npcs) { + if (npc.getId() == 179) { + npc.getProperties().getCombatPulse().attack(player); + npc.sendChat("Die, intruder!"); + return true; + } + } + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 609, 4603, 4604, 4605, 4606 }; + } + + /** + * Method used to check if the player is in the inventory. + * @param player the player. + * @return True if so. + */ + public static boolean inUniform(final Player player) { + for (Item i : UNIFORM) { + if (!player.getEquipment().containsItem(i)) { + return false; + } + } + return true; + } +} 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 new file mode 100644 index 000000000..93901c19e --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/SirAmikVarzeDialogue.java @@ -0,0 +1,331 @@ +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 + * + */ +public class SirAmikVarzeDialogue extends DialoguePlugin { + + /** + * Represents the instanced quest. + */ + private Quest quest; + + /** + * Constructs a new {@code SirAmikVarzeDialogue} {@code Object}. + */ + public SirAmikVarzeDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code SirAmikVarzeDialogue} {@code Object}. + * @param player the player. + */ + public SirAmikVarzeDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new SirAmikVarzeDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + quest = player.getQuestRepository().getQuest(Quests.BLACK_KNIGHTS_FORTRESS); + switch (quest.getStage(player)) { + case 30: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "I have ruined the Black Knights' invincibility potion."); + stage = 0; + break; + case 10: + case 20: + interpreter.sendDialogues(npc, FacialExpression.ASKING, "How's the mission going?"); + stage = 0; + break; + case 100: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Hello Sir Amik."); + stage = 0; + break; + default: + interpreter.sendDialogues(npc, FacialExpression.ASKING, "I am the leader of the White Knights of Falador. Why", "do you seek my audience?"); + stage = 0; + break; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (quest.getStage(player)) { + case 100: + openDialogue(player, new SirAmikVarzeDialogueFile(), npc); + break; + case 30: + switch (stage) { + case 0: + 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, FacialExpression.HALF_ASKING, "Now I believe there was some talk of a cash reward..."); + stage = 2; + break; + case 2: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Absolutely right. Please accept this reward."); + stage = 3; + break; + case 3: + interpreter.sendDialogue("Sir Amik hands you 2500 coins."); + stage = 4; + break; + case 4: + if (!player.getInventory().add(new Item(995, 2500))) { + GroundItemManager.create(new Item(995, 2500), player); + } + quest.finish(player); + player.getQuestRepository().syncronizeTab(player); + end(); + break; + } + break; + case 20: + switch (stage) { + case 0: + 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: + end(); + break; + } + break; + case 10: + switch (stage) { + case 0: + 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, 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, FacialExpression.NEUTRAL, "Here's the dossier on the case."); + stage = 3; + } else { + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Don't forget to read that dossier of information I gave", "you."); + stage = 4; + } + break; + case 3: + player.getInventory().add(BlackKnightsFortress.DOSSIER); + end(); + break; + case 4: + end(); + break; + } + break; + case 0: + switch (stage) { + case 0: + if (player.getQuestRepository().getPoints() < 12) { + 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."); + stage = 1; + } + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I seek a quest."); + stage = 5; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I don't I'm just looking around."); + stage = 2; + break; + } + break; + case 2: + end(); + break; + case 3: + interpreter.sendDialogues(npc, FacialExpression.HALF_WORRIED, "Ok. Please don't break anything."); + stage = 4; + break; + case 4: + end(); + break; + case 5: + 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: + interpreter.sendOptions("Select an Option", "I laugh in the face of danger!", "I go and cower in the corner at the first sign of danger!"); + stage = 7; + break; + case 7: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "I laugh in the face of danger!"); + stage = 15; + break; + case 2: + 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, FacialExpression.NEUTRAL, "Err...."); + stage = 9; + break; + case 9: + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well."); + stage = 10; + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "I... suppose spy work DOES involve a little hiding in", "corners."); + stage = 11; + break; + case 11: + interpreter.sendOptions("Select an Option", "Oh. I suppose I'll give it a go then.", "No, I'm not ready to do that."); + stage = 12; + break; + case 12: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Oh. I suppose I'll give it a go then."); + stage = 17; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I'm not ready to do that."); + stage = 13; + break; + } + break; + case 13: + 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, FacialExpression.NEUTRAL, "Well that's good. Don't get too overconfident though."); + stage = 16; + break; + case 16: + 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, FacialExpression.HALF_GUILTY, "Subtlety isn't exactly our strong point."); + stage = 18; + break; + case 18: + 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, 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, FacialExpression.HALF_ASKING, "Temple Knights? Who are they?"); + stage = 21; + break; + case 21: + 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, FacialExpression.HALF_ASKING, "So... What do you need doing?"); + stage = 23; + break; + case 23: + 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, FacialExpression.NEUTRAL, "Now, NORMALLY this wouldn't be a problem..."); + stage = 25; + break; + case 25: + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "But they claim to have a powerful new secret weapon."); + stage = 26; + break; + case 26: + 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: + interpreter.sendOptions("Select an Option", "Ok, I'll do my best.", "No, I'm not ready to do that."); + stage = 28; + break; + case 28: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Ok, I'll do my best."); + stage = 31; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, I'm not ready to do that."); + stage = 29; + break; + } + break; + case 29: + 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, 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: + if (player.getInventory().add(BlackKnightsFortress.DOSSIER)) { + quest.start(player); + } else { + player.getPacketDispatch().sendMessage("You don't have enough inventory space."); + } + end(); + break; + } + break; + default: + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 608 }; + } +} \ No newline at end of file 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/java/core/game/content/dialogue/GoblinVillageDialogue.java b/Server/src/main/content/region/asgarnia/goblinvillage/dialogue/GoblinVillageDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/GoblinVillageDialogue.java rename to Server/src/main/content/region/asgarnia/goblinvillage/dialogue/GoblinVillageDialogue.java index a69bab074..e28c34fd8 100644 --- a/Server/src/main/java/core/game/content/dialogue/GoblinVillageDialogue.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/dialogue/GoblinVillageDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.goblinvillage.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; diff --git a/Server/src/main/java/core/game/content/dialogue/GoblinVillageGuardDialogue.java b/Server/src/main/content/region/asgarnia/goblinvillage/dialogue/GoblinVillageGuardDialogue.java similarity index 79% rename from Server/src/main/java/core/game/content/dialogue/GoblinVillageGuardDialogue.java rename to Server/src/main/content/region/asgarnia/goblinvillage/dialogue/GoblinVillageGuardDialogue.java index 77c1707dc..0926dc69d 100644 --- a/Server/src/main/java/core/game/content/dialogue/GoblinVillageGuardDialogue.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/dialogue/GoblinVillageGuardDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.asgarnia.goblinvillage.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; @@ -7,6 +8,7 @@ import core.plugin.Initializable; /** * Represents the dialogue plugin used for the goblin village guard npc. * @author jamix77 + * Fixed a typo -Nuggles */ @Initializable public final class GoblinVillageGuardDialogue extends DialoguePlugin { @@ -47,7 +49,7 @@ public final class GoblinVillageGuardDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - npc("I know. We guards uaully stay by banks and shops,", "but I got sent all the way out here to keep an eye on","the brigands loitering just south of here."); + npc("I know. We guards usually stay by banks and shops,", "but I got sent all the way out here to keep an eye on","the brigands loitering just south of here."); stage = 1; break; case 1: @@ -59,7 +61,7 @@ public final class GoblinVillageGuardDialogue extends DialoguePlugin { stage++; break; case 3: - player("Honestly people these days just don't know how to behave!"); + player("Honestly, people these days just don't know how to behave!"); stage++; break; case 4: diff --git a/Server/src/main/java/core/game/node/entity/npc/other/GoblinVillageNPC.java b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillageNPC.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/npc/other/GoblinVillageNPC.java rename to Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillageNPC.java index 357f6aacf..0c34e38d0 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/GoblinVillageNPC.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillageNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.asgarnia.goblinvillage.handlers; import java.util.List; 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/java/core/game/content/quest/free/goblindiplomacy/GDiplomacyCutscene.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GDiplomacyCutscene.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GDiplomacyCutscene.java rename to Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GDiplomacyCutscene.java index 14975a07c..3743a5c2c 100644 --- a/Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GDiplomacyCutscene.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GDiplomacyCutscene.java @@ -1,23 +1,24 @@ -package core.game.content.quest.free.goblindiplomacy; +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.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialoguePlugin; 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.quest.Quest; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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.build.DynamicRegion; import core.game.world.map.path.Pathfinder; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; import core.game.world.update.flag.context.Animation; import core.net.packet.PacketRepository; import core.net.packet.context.CameraContext; @@ -25,7 +26,10 @@ import core.net.packet.context.CameraContext.CameraType; import core.net.packet.out.CameraViewPacket; import core.tools.RandomFunction; import core.tools.StringUtils; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.FacialExpression; + +import static core.api.ContentAPIKt.*; + /** * Represents the goblin diplomacy cutscene plugin. @@ -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/java/core/game/content/quest/free/goblindiplomacy/GoblinDiplomacy.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GoblinDiplomacy.java rename to Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java index 59c70f6f8..ad3d9c910 100644 --- a/Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GoblinDiplomacy.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacy.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.goblindiplomacy; +package content.region.asgarnia.goblinvillage.quest.goblindiplomacy; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; @@ -6,7 +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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the demon slayer quest. @@ -15,12 +16,6 @@ import rs09.plugin.PluginManager; */ @Initializable public class GoblinDiplomacy extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "Goblin Diplomacy"; - /** * Represents the orange goblin mail. */ @@ -45,12 +40,12 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugins(new GDiplomacyCutscene(), new GoblinDiplomacyPlugin(), new GrubfootDialogue()); + ClassScanner.definePlugins(new GDiplomacyCutscene(), new GoblinDiplomacyPlugin(), new GrubfootDialogue()); return this; } @@ -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/java/core/game/content/quest/free/goblindiplomacy/GoblinDiplomacyPlugin.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacyPlugin.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GoblinDiplomacyPlugin.java rename to Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacyPlugin.java index 807c401af..a412b1839 100644 --- a/Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GoblinDiplomacyPlugin.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GoblinDiplomacyPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.goblindiplomacy; +package content.region.asgarnia.goblinvillage.quest.goblindiplomacy; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; @@ -10,9 +10,9 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.StringUtils; /** @@ -42,7 +42,7 @@ public final class GoblinDiplomacyPlugin extends OptionHandler { for (GoblinMailPlugin.GoblinMail mail : GoblinMailPlugin.GoblinMail.values()) { ItemDefinition.forId(mail.getProduct().getId()).getHandlers().put("option:wear", this); } - PluginManager.definePlugin(new GoblinMailPlugin()); + ClassScanner.definePlugin(new GoblinMailPlugin()); return this; } diff --git a/Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GrubFoot.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubFoot.java similarity index 87% rename from Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GrubFoot.java rename to Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubFoot.java index 3b110f9d1..af5178b10 100644 --- a/Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GrubFoot.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubFoot.java @@ -1,8 +1,10 @@ -package core.game.content.quest.free.goblindiplomacy; +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/java/core/game/content/quest/free/goblindiplomacy/GrubfootDialogue.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubfootDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GrubfootDialogue.java rename to Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubfootDialogue.java index 021702cb2..ca562e9c6 100644 --- a/Server/src/main/java/core/game/content/quest/free/goblindiplomacy/GrubfootDialogue.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubfootDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.free.goblindiplomacy; +package content.region.asgarnia.goblinvillage.quest.goblindiplomacy; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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 new file mode 100644 index 000000000..32059ef91 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/handlers/CraftingGuildListeners.kt @@ -0,0 +1,46 @@ +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 + +/** + * @author bushtail, Player Name + */ + +class CraftingGuildListeners : InteractionListener { + override fun defineListeners() { + on(Scenery.GUILD_DOOR_2647, IntType.SCENERY, "open") { player, door -> + if (player.location == Location.create(2933, 3289, 0)) { + if (hasLevelStat(player, Skills.CRAFTING, 40)) { + if (anyInEquipment(player, 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 + } else { + openDialogue(player, TheDoorDialogues(1)) + return@on false + } + } else { + openDialogue(player, TheDoorDialogues(2)) + return@on false + } + } else { + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door.asScenery()) + 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/handlers/WineOfZamorakInteraction.kt b/Server/src/main/content/region/asgarnia/handlers/WineOfZamorakInteraction.kt new file mode 100644 index 000000000..d908028b4 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/handlers/WineOfZamorakInteraction.kt @@ -0,0 +1,28 @@ +package content.region.asgarnia.handlers + +import core.game.node.item.GroundItem +import core.game.world.map.RegionManager +import org.rs09.consts.Items +import core.game.global.action.PickupHandler +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class WineOfZamorakInteraction : InteractionListener { + + override fun defineListeners() { + on(Items.WINE_OF_ZAMORAK_245, IntType.GROUNDITEM,"take"){ player, wine -> + if(player.location.regionId != 11574){ + PickupHandler.take(player, wine as GroundItem) + return@on true + } + val npcs = RegionManager.getLocalNpcs(player) + for (n in npcs) { + if (n.id == 188) { + n.sendChat("Hands off zamorak's wine!") + n.properties.combatPulse.attack(player) + } + } + 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/java/core/game/content/dialogue/AhabDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/AhabDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/AhabDialogue.java rename to Server/src/main/content/region/asgarnia/portsarim/dialogue/AhabDialogue.java index 6d0406266..5f5446083 100644 --- a/Server/src/main/java/core/game/content/dialogue/AhabDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/AhabDialogue.java @@ -1,8 +1,11 @@ -package core.game.content.dialogue; +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; +import content.data.Quests; /** * Represents the dialogue plugin used for the npc Ahav. @@ -115,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/java/core/game/content/dialogue/BettyDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/BettyDialogue.java similarity index 79% rename from Server/src/main/java/core/game/content/dialogue/BettyDialogue.java rename to Server/src/main/content/region/asgarnia/portsarim/dialogue/BettyDialogue.java index 00632d34e..df1214c82 100644 --- a/Server/src/main/java/core/game/content/dialogue/BettyDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/BettyDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -37,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; } @@ -62,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/java/core/game/content/dialogue/GrumDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/GrumDialogue.java similarity index 78% rename from Server/src/main/java/core/game/content/dialogue/GrumDialogue.java rename to Server/src/main/content/region/asgarnia/portsarim/dialogue/GrumDialogue.java index 8b3ee9e95..14d964cc2 100644 --- a/Server/src/main/java/core/game/content/dialogue/GrumDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/GrumDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -37,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; } @@ -63,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/java/core/game/content/dialogue/JackSeagullDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/JackSeagullDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/JackSeagullDialogue.java rename to Server/src/main/content/region/asgarnia/portsarim/dialogue/JackSeagullDialogue.java index c1bdfb4d1..671e7f482 100644 --- a/Server/src/main/java/core/game/content/dialogue/JackSeagullDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/JackSeagullDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/KlarenseDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/KlarenseDialogue.java new file mode 100644 index 000000000..6c5e14b1e --- /dev/null +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/KlarenseDialogue.java @@ -0,0 +1,241 @@ +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.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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class KlarenseDialogue extends DialoguePlugin { + + /** + * Represents the amount of coins. + */ + private static final Item COINS = new Item(995, 2000); + + /** + * Represents the quest instance. + */ + private Quest quest; + + /** + * Constructs a new {@code KlarenseDialogue} {@code Object}. + */ + public KlarenseDialogue() { + /** + * emptpy. + */ + } + + /** + * Constructs a new {@code KlarenseDialogue} {@code Object}. + * @param player the player. + */ + public KlarenseDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new KlarenseDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + quest = player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER); + if (args.length > 1) { + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "Hey, stay off my ship! That's private property!"); + stage = 0; + return true; + } + switch (quest.getStage(player)) { + case 100: + player("Hey, that's my ship!"); + stage = -1; + break; + default: + if (player.getSavedData().getQuestData().getDragonSlayerAttribute("ship")) { + 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.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; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + if (stage == 400) { + end(); + return true; + } + switch (quest.getStage(player)) { + case 100: + switch (stage) { + case 0: + end(); + break; + case -1: + 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: + interpreter.sendDialogues(player, FacialExpression.ANNOYED,"It has Lady Lumbridge pained out and 'Klarense's", "Cruiser' painted over it!"); + stage = 2; + break; + case 2: + interpreter.sendDialogues(npc, FacialExpression.SUSPICIOUS,"Nope, you're mistaken. It's my new boat."); + stage = 3; + break; + case 3: + end(); + break; + } + break; + case 20: + switch (stage) { + case 0: + end(); + break; + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Would you take me to Crandor when she's ready?"); + stage = 2; + break; + case 2: + interpreter.sendDialogues(npc, FacialExpression.EXTREMELY_SHOCKED, "Crandor? You're joking, right?"); + stage = 3; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No. I want to go to Crandor."); + stage = 4; + break; + case 4: + interpreter.sendDialogues(npc, FacialExpression.WORRIED, "Then you must be crazy."); + stage = 5; + break; + case 5: + 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: + 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: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL,"I'd like to buy her."); + stage = 8; + break; + case 8: + 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: + 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: + interpreter.sendDialogues(player, FacialExpression.HAPPY,"Yep, sounds good."); + stage = 11; + break; + case 11: + if (!player.getInventory().containsItem(COINS)) { + 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)) { + end(); + return false; + } + if (player.getInventory().remove(COINS)) { + interpreter.sendDialogues(npc, FacialExpression.HAPPY,"Okey dokey, she's all yours!"); + player.getSavedData().getQuestData().setDragonSlayerAttribute("ship", true); + stage = 12; + } + } + break; + case 12: + end(); + break; + } + break; + default: + switch (stage) { + case 0: + end(); + break; + case 1: + interpreter.sendOptions("Select an Option", "Do you know when she will be seaworthy?", "Why is she damaged?", "Ah, well, never mind."); + stage = 2; + break; + case 2: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you know when she will be seaworthy?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Why is she damaged?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ah, well, never mind."); + stage = 30; + break; + } + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No, not really. Port Sarim's shipbuilders aren't very", "efficient so it could be quite a while."); + stage = 11; + break; + case 11: + end(); + break; + case 20: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh, there was no particular accident. It's just years of", "wear and tear."); + stage = 21; + break; + case 21: + 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.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.NEUTRAL, "but once she's patched up, she'll be good as new!"); + stage = 24; + break; + case 24: + end(); + break; + case 30: + end(); + break; + case 400: + end(); + break; + } + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 744 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/LongbowBenDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/LongbowBenDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/LongbowBenDialogue.java rename to Server/src/main/content/region/asgarnia/portsarim/dialogue/LongbowBenDialogue.java index 0740dd674..46cf18c27 100644 --- a/Server/src/main/java/core/game/content/dialogue/LongbowBenDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/LongbowBenDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -52,7 +54,7 @@ public final class LongbowBenDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Why are you called Longbow Ben?"); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why are you called Longbow Ben?"); stage = 100; break; case 2: diff --git a/Server/src/main/java/core/game/content/dialogue/MonkOfEntranaDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java similarity index 85% rename from Server/src/main/java/core/game/content/dialogue/MonkOfEntranaDialogue.java rename to Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java index 9d94aead0..e8c921159 100644 --- a/Server/src/main/java/core/game/content/dialogue/MonkOfEntranaDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java @@ -1,12 +1,16 @@ -package core.game.content.dialogue; +package content.region.asgarnia.portsarim.dialogue; import core.cache.def.impl.ItemDefinition; -import core.game.content.global.travel.ship.Ships; +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.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 @@ -37,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; } @@ -94,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: @@ -102,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); } @@ -128,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 new file mode 100644 index 000000000..a771cf3d1 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBartenderDialogue.java @@ -0,0 +1,101 @@ +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.game.node.entity.player.Player; +import core.plugin.Initializable; +import core.game.node.item.Item; + +/** + * Handles the PortSarimBartenderDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class PortSarimBartenderDialogue extends DialoguePlugin { + + public PortSarimBartenderDialogue() { + + } + + public PortSarimBartenderDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 734 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + + switch (stage) { + case 0: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello there!"); + stage = 1; + break; + case 1: + interpreter.sendOptions("Choose an option:", "Could I buy a beer, please.", "Bye, then."); + stage = 2; + break; + case 2: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Could I buy a beer, please?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Bye, then."); + stage = 20; + break; + } + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Sure, that will be two gold coins, please."); + stage = 11; + break; + case 11: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Okay, here you go."); + stage = 12; + break; + case 12: + if (player.getInventory().contains(995, 2)) { + interpreter.sendDialogue("You buy a pint of beer."); + player.getInventory().remove(new Item(995, 2)); + player.getInventory().add(new Item(1917, 1)); + stage = 13; + } else { + player.getPacketDispatch().sendMessage("You need 2 gold coins to buy beer."); + end(); + } + break; + case 13: + end(); + break; + case 20: + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Come back soon!"); + stage = 21; + break; + case 21: + end(); + break; + } + + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new PortSarimBartenderDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + 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 new file mode 100644 index 000000000..3065f02aa --- /dev/null +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBrianDialogue.java @@ -0,0 +1,76 @@ +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; + +/** + * Handles the PortSarimBrianDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class PortSarimBrianDialogue extends DialoguePlugin { + + public PortSarimBrianDialogue() { + + } + + public PortSarimBrianDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 559 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "So, are you selling something?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "'Ello."); + stage = 20; + break; + } + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Yep, take a look at these great axes."); + stage = 11; + break; + case 11: + end(); + npc.openShop(player); + break; + case 20: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "'Ello."); + stage = 21; + break; + case 21: + end(); + break; + } + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new PortSarimBrianDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendOptions("Select an Option", "So, are you selling something?", "'Ello."); + stage = 0; + return true; + } +} diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/RedbeardFrankDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/RedbeardFrankDialogue.java new file mode 100644 index 000000000..e3a46166c --- /dev/null +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/RedbeardFrankDialogue.java @@ -0,0 +1,516 @@ +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.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.game.node.item.GroundItemManager; +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. + * + * @author afaroutdude + */ +@Initializable +public class RedbeardFrankDialogue extends DialoguePlugin { + private boolean replacementReward = false; + private int level = 0; + + /** + * Represents the karamjan rum item. + */ + private static final Item KARAMJAN_RUM = new Item(431); + + /** + * Represents the chest key item. + */ + private static final Item KEY = new Item(432); + + /** + * Represents this quest instance. + */ + private Quest quest; + + /** + * Constructs a new {@code RedbeardFrankDialogue} {@code Object}. + */ + public RedbeardFrankDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code RedbeardFrankDialogue} {@code Object}. + * + * @param player the player. + */ + public RedbeardFrankDialogue(Player player) { + super(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + quest = player.getQuestRepository().getQuest(Quests.PIRATES_TREASURE); + npc("Arr, Matey!"); + stage = 0; + replacementReward = AchievementDiary.canReplaceReward(player, DiaryType.FALADOR, level); + return true; + } + + // https://www.youtube.com/watch?v=U_sNcqQ2dtQ - achievement diary branch + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + if (quest.getStage(player) == 20 && !player.getInventory().containsItem(KEY) && !player.getBank().containsItem(KEY)) { + player("I seem to have lost my chest key..."); + stage = 700; + return true; + } + if (quest.getStage(player) == 10) { + npc("Have ye brought some rum for yer ol' mate Frank?"); + stage = 20; + break; + } + if (quest.getStage(player) == 0) { + options("I'm in search of treasure.", "Arr!", "Do you have anything for trade?", "Tell me about the Falador Achievement Diary."); + stage = 11; + break; + } + options("Arr!", "Do you have anything for trade?", "Tell me about the Falador Achievement Diary."); + stage = 10; + break; + case 10: + switch (buttonId) { + case 1: + player("Arr!"); + stage = 12; + break; + case 2: + player("Do you have anything for trade?"); + stage = 13; + break; + case 3: + player("Tell me about the Falador Achievement Diary."); + stage = 80; + break; + } + break; + case 11: + switch (buttonId) { + case 1: + player("I'm in search of treasure."); + stage = 40; + break; + case 2: + player("Arr!"); + stage = 12; + break; + case 3: + player("Do you have anything for trade?"); + stage = 13; + break; + case 4: + player("Tell me about the Falador Achievement Diary."); + stage = 80; + break; + } + break; + case 12: + npc("Arr!"); + stage = 0; + break; + case 13: + npc("Nothin' at the moment, but then again the Customs", "Agents are on the warpath right now."); + stage = 999; + break; + case 20: + if (!player.getInventory().containsItem(KARAMJAN_RUM)) { + player("No, not yet."); + stage = 21; + } else { + player("Yes, I've got some."); + stage = 31; + } + break; + case 21: + npc("Not suprising, tis no easy task to get it off Karamja."); + stage = 22; + break; + case 22: + player("What do you mean?"); + stage = 23; + break; + case 23: + npc("The Customs office has been clampin' down on the", "export of spirits. You seem like a resourceful young lad,", "I'm sure ye'll be able to find a way to slip the stuff past", "them."); + stage = 24; + break; + case 24: + player("Well I'll give it another shot."); + stage = 25; + break; + case 999: + end(); + break; + case 31: + npc("Now a deal's a deal, I'll tell ye about the treasure. I", "used to serve under a pirate captain called One-Eyed", "Hector."); + stage = 32; + break; + case 32: + npc("Hector were very successful and became very rich.", "But about a year ago we were boarded by the Customs", "and Excise Agents."); + stage = 33; + break; + case 33: + npc("Hector were killed along with many of the crew, I were", "one of the few to escape and I escaped with this."); + stage = 34; + break; + case 34: + if (player.getInventory().remove(KARAMJAN_RUM)) { + if (!player.getInventory().add(KEY)) { + GroundItemManager.create(KEY, player); + } + quest.setStage(player, 20); + interpreter.sendItemMessage(KEY.getId(), "Frank happily takes the rum... ... and hands you a key"); + stage = 35; + } + break; + case 35: + npc("This be Hector's key. I believe it opens his chest in his", "old room in the Blue Moon Inn in Varrock."); + stage = 36; + break; + case 36: + npc("With any luck his treasure will be in there."); + stage = 37; + break; + case 37: + player("Ok thanks, I'll go and get it."); + stage = 999; + break; + case 40: + npc("Arr, treasure you be after eh? Well I might be able to", "tell you where to find some... For a price..."); + stage = 41; + break; + case 41: + player("What sort of price?"); + stage = 42; + break; + case 42: + npc("Well for example if you go and get me a bottle of rum...", "Not just any rum mind..."); + stage = 43; + break; + case 43: + npc("I'd like some rum made on Karamja Island. There's no", "rum like Karamaja Rum!"); + stage = 44; + break; + case 44: + options("Ok, I will bring you some rum.", "Not right now."); + stage = 45; + break; + case 45: + switch (buttonId) { + case 1: + quest.start(player); + player("Ok, I will bring you some rum."); + stage = 47; + break; + case 2: + player("Not right now."); + stage = 46; + break; + } + break; + case 46: + npc("Fair enough. I'll still be here and thirsty whenever you", "feel like helpin' out."); + stage = 999; + break; + case 47: + npc("Yer a saint, although it'll take a miracle to get it off", "Karamja."); + stage = 48; + break; + case 48: + player("What do you mean?"); + stage = 49; + break; + case 49: + npc("The Customs office has been clampin' down on the", "export of spirits. You seem like a resourceful young lad,", "I'm sure ye'll be able to find a way to slip the stuff past", "them."); + stage = 50; + break; + case 50: + player("Well I'll give it a shot."); + stage = 51; + break; + case 51: + npc("Arr, that's the spirit!"); + stage = 999; + break; + + + case 700: + npc("Arr, silly you. Fortunately I took the precaution to have", "another one made."); + stage = 701; + break; + case 701: + interpreter.sendItemMessage(KEY.getId(), "Frank hands you a chest key."); + stage = 702; + break; + case 702: + end(); + if (!player.getInventory().add(KEY)) { + GroundItemManager.create(KEY, player); + } + break; + + + case 80: + if (player.getAttribute("falador-diary-talk-first-time", false)) { + npc("So you're interested in exploring Falador and it's", "surrounding areas, eh?"); + player.setAttribute("/save:falador-diary-talk-first-time", true); + stage = 100; + } else { + npc("How are you getting on with the Achievement Diary?"); + stage = 90; + } + break; + + case 90: + options("I've come for my reward.", "I'm doing good.", "I have a question."); + stage = 91; + break; + case 91: + switch (buttonId) { + case 1: + player("I've come for my reward."); + stage = 200; + break; + case 2: + player("I'm doing good."); + stage = 220; + break; + case 3: + player("I have a question."); + stage = 105; + break; + } + break; + + case 100: + player("Er... I guess."); + stage = 101; + break; + case 101: + npc("Arrr! That's the spirit! Soon ye'll be exploring", "underground caverns, sailin' the high seas and", "plundering booty!"); + stage = 102; + break; + case 102: + interpreter.sendDialogues(player, FacialExpression.AMAZED, "Plundering booty?"); + stage = 103; + break; + case 103: + npc("Arrr! Nay, that be a lie, I be getting carried away."); + stage = 104; + break; + case 104: + npc("Have you got any questions?"); + stage = 105; + break; + case 105: + if (!AchievementDiary.hasClaimedLevelRewards(player, DiaryType.FALADOR, level)) { + options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); + stage = 106; + } else { + options("Can you remind me what my Falador shield does, please?", "What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); + stage = 107; + } + break; + case 106: + 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 = 999; + break; + } + break; + + case 107: + switch (buttonId) { + case 1: + player("Can you remind me what my Falador shield does, please?"); + stage = 150; + break; + case 2: + player("What is the Achievement Diary?"); + stage = 110; + break; + case 3: + player("What are the rewards?"); + stage = 120; + break; + case 4: + player("How do I claim the rewards?"); + stage = 130; + break; + case 5: + player("See you later."); + stage = 999; + break; + } + break; + + case 110: + 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: + npc("If you manage to complete a particular set of tasks,", "you will be rewarded for your explorative efforts."); + stage = 112; + break; + case 112: + npc("You can access your Achievement Diary by going to", "the Quest Journal, then clicking on the green star icon", "in the top-right hand corner."); + stage = 105; + break; + + case 120: + npc("Ah, well there are different rewards for each", "Achievement Diary. For completing each stage of the", "Falador diary, you are presented with a Falador shield."); + stage = 121; + break; + case 121: + npc("There are three shields available, one for each difficulty", "level."); + stage = 122; + break; + case 122: + npc("When you are presented with your rewards, you will", "be told of their uses."); + stage = 105; + break; + + case 130: + npc("You need to complete all of the tasks in a particular", "difficulty, then you can claim your reward."); + stage = 131; + break; + case 131: + npc("Some of Falador's tasks are simple, some will require", "certain skill levels, and some might require quests to be", "started or completed."); + stage = 132; + break; + case 132: + npc("To claim your Falador Achievement Diary rewards,", "speak to the chemist in Rimmington, Sir Vyvin's squire", "in the White Knight's Castle, or myself."); + stage = 105; + break; + + case 150: + npc("This is the first stage fo the Falador shield: a buckler. It", "grants you access to a Prayer restore ability and an", "emote."); + stage = 151; + break; + case 151: + npc("Each of these features can be triggered while wielding", "the shield by selecting the 'Operate' option. The Prayer", "restore can also be activated from your inventory."); + stage = 152; + break; + case 152: + npc("The Prayer restore ability can only be used once per", "day, and gives you back a quarter of your Prayer", "points."); + stage = 153; + break; + case 153: + npc("As well as all of these features, the shield is pretty", "handy in combat, and gives you a small Prayer boost."); + stage = 105; + break; + + case 200: + if (AchievementDiary.canReplaceReward(player, DiaryType.FALADOR, level)) { + playerl(FacialExpression.HALF_GUILTY, "I seem to have lost my shield..."); + stage = 250; + } else if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.FALADOR, level)) { + npc("But you've already gotten yours!"); + stage = 105; + } else if (AchievementDiary.hasCompletedLevel(player, DiaryType.FALADOR, level)) { + npc("So, you've finished. Well done! I believe congratulations", "are in order."); + stage = 201; + } else { + npc("But you haven't finished!"); + stage = 105; + } + break; + case 201: + player("I believe rewards are in order."); + stage = 202; + break; + case 202: + npc("Right you are."); + stage = 203; + break; + case 203: + npc("This is the first stage of the Falador shield: a buckler. It", "grants you access to a Prayer restore ability and an", "emote."); + AchievementDiary.flagRewarded(player, DiaryType.FALADOR, level); + stage = 204; + break; + case 204: + npc("Each of these features can be triggered while wielding", "the shield by selecting the 'Operate' option. The Prayer", "restore can also be activated from your inventory."); + stage = 205; + break; + case 205: + npc("The Prayer restore ability can only be used once per", "day, and gives you back a quarter of your Prayer", "points."); + stage = 206; + break; + case 206: + npc("As well as all of these features, the shield is pretty", "handy in combat, and gives you a small Prayer boost."); + stage = 207; + break; + case 207: + npc("I've even thrown in a bit of booty I found on my", "travels."); + stage = 208; + break; + case 208: + interpreter.sendDialogues(player, FacialExpression.AMAZED, "Wow, thanks!"); + stage = 209; + break; + case 209: + npc("If you should lose your shield, come back and see me", "for another one."); + stage = 105; + break; + case 220: + npc("Keep it up!"); + stage = 105; + break; + case 250: + npcl(FacialExpression.LAUGH, "Alright, matey, I'll give ye a new one."); + stage++; + break; + case 251: + AchievementDiary.grantReplacement(player, DiaryType.FALADOR, level); + npcl(FacialExpression.FRIENDLY, "Be more careful this time, aye?"); + stage = END_DIALOGUE; + break; + } + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new RedbeardFrankDialogue(player); + } + + @Override + public int[] getIds() { + return new int[]{375}; + } + +} diff --git a/Server/src/main/java/core/game/content/dialogue/TakiDwarfDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/TakiDwarfDialogue.java similarity index 76% rename from Server/src/main/java/core/game/content/dialogue/TakiDwarfDialogue.java rename to Server/src/main/content/region/asgarnia/portsarim/dialogue/TakiDwarfDialogue.java index 509cb5b5c..7df140e35 100644 --- a/Server/src/main/java/core/game/content/dialogue/TakiDwarfDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/TakiDwarfDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -29,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: @@ -37,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 new file mode 100644 index 000000000..50b38cc95 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/WydinDialogue.java @@ -0,0 +1,234 @@ +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 that handles the wydin npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class WydinDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code WydinDialogue} {@code Object}. + */ + public WydinDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code WydinDialogue} {@code Object}. + * @param player the player. + */ + public WydinDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new WydinDialogue(player); + } + + @Override + public boolean open(Object... args) { + boolean door = false; + if (args.length == 2) + door = (boolean) args[1]; + if (door) { + if (player.getSavedData().getGlobalData().isWydinEmployee()) { + interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "Can you put your white apron on before going in there", "please."); + stage = 100; + } else { + 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; + } else { + npc = (NPC) args[0]; + } + if (player.getSavedData().getGlobalData().isWydinEmployee()) { + interpreter.sendDialogues(557, FacialExpression.ASKING, "Is it nice and tidy round the back now?"); + stage = 0; + } else { + interpreter.sendDialogues(557, FacialExpression.HAPPY, "Welcome to my food store! Would you like to buy", "anything?"); + stage = 0; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + if (player.getSavedData().getGlobalData().isWydinEmployee()) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "Yes, can I work out front now?", "Yes, are you going to pay me yet?", "No, it's a complete mess.", "Can I buy something please?"); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Yes, can I work out front now?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Yes, are you going to pay me yet?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, it's a complete mess."); + stage = 30; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I buy something please?"); + stage = 40; + break; + + } + break; + case 10: + 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.THINKING, "Umm... No, not yet."); + stage = 21; + break; + case 21: + end(); + break; + case 30: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, it's a complete mess."); + stage = 31; + break; + case 31: + 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.HAPPY, "Yes, ofcourse."); + stage = 41; + break; + case 41: + end(); + npc.openShop(player); + break; + case 100: + end(); + break; + } + } else { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "Yes please.", "No, thank you.", "What can you recommend?", "Can I get a job here?"); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "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.ASKING, "What can you recommend?"); + stage = 30; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Can I get a job here?"); + stage = 40; + break; + } + break; + case 10: + end(); + npc.openShop(player); + break; + case 20: + end(); + break; + case 30: + 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: + interpreter.sendOptions("Select an Option", "Hmm, I think I'll try one.", "I don't like the sound of that."); + stage = 32; + break; + case 32: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hmm, I think I'll try one."); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't like the sound of that."); + stage = 100; + break; + } + break; + case 40: + 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, FacialExpression.SAD, "No, I haven't."); + stage = 42; + } else { + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes, I have one."); + stage = 50; + } + break; + case 42: + 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, FacialExpression.ASKING, "Where can I get one of those?"); + stage = 44; + break; + case 44: + 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, 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: + end(); + break; + case 50: + player.getSavedData().getGlobalData().setWydinEmployee(true); + 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: + end(); + break; + } + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 557 }; + } +} diff --git a/Server/src/main/content/region/asgarnia/portsarim/handlers/AhabBeerInteraction.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/AhabBeerInteraction.java new file mode 100644 index 000000000..57bcdcea1 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/AhabBeerInteraction.java @@ -0,0 +1,32 @@ +package content.region.asgarnia.portsarim.handlers; + +import core.game.interaction.Option; +import core.game.interaction.SpecialGroundInteraction; +import core.game.interaction.SpecialGroundItems; +import core.game.node.entity.npc.NPC; +import core.game.node.entity.player.Player; +import core.game.world.map.RegionManager; +import org.rs09.consts.NPCs; + +import static core.api.ContentAPIKt.findLocalNPC; + +/** + * Handles Ahab's beer in port sarim + * @author ceik + */ + +public class AhabBeerInteraction extends SpecialGroundInteraction { + //The dialogue key + public static final int DIALOGUE_KEY = 2692; + + @Override + public void handle(final Player player, final Option option){ + configure(); + if(option.getName() == "take"){ + player.faceLocation(SpecialGroundItems.AHAB_BEER.getLocation()); + player.getDialogueInterpreter().open(DIALOGUE_KEY, findLocalNPC(player, NPCs.AHAB_2692), false); + } else { + player.debug("Unhandled option: " + option.getName()); + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/ElementalWizardNPC.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/ElementalWizardNPC.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/other/ElementalWizardNPC.java rename to Server/src/main/content/region/asgarnia/portsarim/handlers/ElementalWizardNPC.java index 1440c25df..c00c9dcc9 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/ElementalWizardNPC.java +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/ElementalWizardNPC.java @@ -1,20 +1,21 @@ -package core.game.node.entity.npc.other; +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.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.MagicSpell; +import core.game.node.entity.combat.spell.MagicSpell; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; 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.node.entity.player.link.SpellBookManager.SpellBook; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; @@ -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/java/core/game/interaction/city/PortSarimPlugin.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/PortSarimPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/city/PortSarimPlugin.java rename to Server/src/main/content/region/asgarnia/portsarim/handlers/PortSarimPlugin.java index 4bf051ea8..8746b7e93 100644 --- a/Server/src/main/java/core/game/interaction/city/PortSarimPlugin.java +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/PortSarimPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.city; +package content.region.asgarnia.portsarim.handlers; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; @@ -9,12 +9,13 @@ 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 rs09.game.world.GameWorld; +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.plugin.Initializable; import core.tools.RandomFunction; +import content.data.Quests; /** * Represents the port sarim plugin. @@ -72,10 +73,9 @@ 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.getPulseManager().clear("interaction:attack:" + node.hashCode()); player.getProperties().getCombatPulse().attack(node); } break; diff --git a/Server/src/main/java/core/game/interaction/object/PortsObjectPlugin.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/PortsObjectPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/PortsObjectPlugin.java rename to Server/src/main/content/region/asgarnia/portsarim/handlers/PortsObjectPlugin.java index 54530ad20..7f45a94f4 100644 --- a/Server/src/main/java/core/game/interaction/object/PortsObjectPlugin.java +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/PortsObjectPlugin.java @@ -1,16 +1,17 @@ -package core.game.interaction.object; +package content.region.asgarnia.portsarim.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; +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 rs09.game.world.repository.Repository; +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/java/core/game/interaction/object/WydinDoorPlugin.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/WydinDoorPlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/object/WydinDoorPlugin.java rename to Server/src/main/content/region/asgarnia/portsarim/handlers/WydinDoorPlugin.java index 00939ab60..05ea0a1d3 100644 --- a/Server/src/main/java/core/game/interaction/object/WydinDoorPlugin.java +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/WydinDoorPlugin.java @@ -1,7 +1,7 @@ -package core.game.interaction.object; +package content.region.asgarnia.portsarim.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; diff --git a/Server/src/main/java/core/game/node/entity/npc/quest/pirates_treasure/GardenerNPC.java b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/GardenerNPC.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/npc/quest/pirates_treasure/GardenerNPC.java rename to Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/GardenerNPC.java index bca136edc..f0926dfed 100644 --- a/Server/src/main/java/core/game/node/entity/npc/quest/pirates_treasure/GardenerNPC.java +++ b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/GardenerNPC.java @@ -1,11 +1,11 @@ -package core.game.node.entity.npc.quest.pirates_treasure; +package content.region.asgarnia.portsarim.quest.piratestreasure; 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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.plugin.Initializable; import core.game.world.map.Location; @@ -64,12 +64,12 @@ public final class GardenerNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { final Player target = getAttribute("target", null); if (target != entity) { return false; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override diff --git a/Server/src/main/java/core/game/content/quest/free/piratestreasure/PiratesTreasure.java b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasure.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/free/piratestreasure/PiratesTreasure.java rename to Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasure.java index d9bc2ed00..65ab84c0e 100644 --- a/Server/src/main/java/core/game/content/quest/free/piratestreasure/PiratesTreasure.java +++ b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasure.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.piratestreasure; +package content.region.asgarnia.portsarim.quest.piratestreasure; import core.game.component.Component; import core.game.node.entity.player.Player; @@ -6,7 +6,8 @@ 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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the pirates treasure quest. @@ -50,12 +51,12 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugin(new PiratesTreasurePlugin()); + ClassScanner.definePlugin(new PiratesTreasurePlugin()); return this; } diff --git a/Server/src/main/java/core/game/content/quest/free/piratestreasure/PiratesTreasurePlugin.java b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasurePlugin.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/free/piratestreasure/PiratesTreasurePlugin.java rename to Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasurePlugin.java index c609db1e3..ce41de603 100644 --- a/Server/src/main/java/core/game/content/quest/free/piratestreasure/PiratesTreasurePlugin.java +++ b/Server/src/main/content/region/asgarnia/portsarim/quest/piratestreasure/PiratesTreasurePlugin.java @@ -1,9 +1,9 @@ -package core.game.content.quest.free.piratestreasure; +package content.region.asgarnia.portsarim.quest.piratestreasure; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DigAction; -import core.game.content.global.action.DigSpadeHandler; +import core.game.global.action.DigAction; +import core.game.global.action.DigSpadeHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; @@ -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/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/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/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.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/java/core/game/content/dialogue/BrianORichardDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/BrianORichardDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/BrianORichardDialogue.java rename to Server/src/main/content/region/asgarnia/taverley/dialogue/BrianORichardDialogue.java index e708c621a..1737820fc 100644 --- a/Server/src/main/java/core/game/content/dialogue/BrianORichardDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/BrianORichardDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.taverley.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/JatixDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/JatixDialogue.java new file mode 100644 index 000000000..27702f723 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/JatixDialogue.java @@ -0,0 +1,70 @@ +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; + +/** + * Represents the dialogue plugin used for the jatix npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class JatixDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code JatixDialogue} {@code Object}. + */ + public JatixDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code JatixDialogue} {@code Object}. + * @param player the player. + */ + public JatixDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new JatixDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello, adventurer."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hello."); + stage = 1; + break; + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "What are you selling?"); + stage = 2; + break; + case 2: + end(); + npc.openShop(player); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 587 }; + } +} diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/KaqemeexDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/KaqemeexDialogue.java new file mode 100644 index 000000000..487939a33 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/KaqemeexDialogue.java @@ -0,0 +1,326 @@ +package content.region.asgarnia.taverley.dialogue; + +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +import core.game.global.Skillcape; +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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class KaqemeexDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code KaqemeexDialogue} {@code Object}. + */ + public KaqemeexDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code KaqemeexDialogue} {@code Object}. + * @param player the player. + */ + public KaqemeexDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new KaqemeexDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + if (args.length >= 2) { + interpreter.sendDialogues(npc, null, "I will now explain the fundamentals of Herblore:"); + stage = 1000; + return true; + } + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hello there."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + 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(Quests.DRUIDIC_RITUAL).getStage(player) == 10) { + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Hello again."); + stage = 40; + break; + } + 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.FRIENDLY, "What brings you to our holy monument?"); + stage = 1; + break; + case 1: + 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; + } else { + interpreter.sendOptions("Select an Option", "Who are you?", "Did you build this?"); + stage = 500; + } + break; + } else { + interpreter.sendOptions("Select an Option", "Who are you?", "I'm in search of a quest.", "Did you build this?"); + stage = 2; + } + break; + case 2: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Who are you?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "I'm in search of a quest."); + stage = 20; + break; + case 3: + 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", "throughout these lands."); + stage = 11; + break; + case 11: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Well, I'll be on my way now."); + stage = 12; + break; + case 12: + 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.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 usable by us anymore."); + stage = 32; + break; + case 32: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Well, I'll be on my way now."); + stage = 12; + break; + case 20: + 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: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That used to be our stone circle. Unfortunately,", "many years ago, dark wizards cast a wicked spell", "upon it so that they could corrupt its power for their", "own evil ends."); + 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", "adventurer to go on a quest for us to help purify the", "circle of Varrock."); + stage = 23; + break; + case 23: + interpreter.sendOptions("Select an Option", "Ok, I will try and help.", "No, that doesn't sound very interesting."); + stage = 24; + break; + case 24: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok, I will try and help."); + stage = 26; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, that doesn't sound very interesting."); + stage = 25; + break; + } + break; + case 25: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I will not try and change your mind adventurer. Some", "day when you have matured you may reconsider your", "position. We will wait until then."); + stage = 13; + break; + case 26: + 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.HAPPY, "Will do."); + stage = 28; + break; + case 28: + end(); + break; + case 40: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You will need to speak to my fellow druid Sanfew in", "the village south of here to continue in your quest", "adventurer."); + stage = 41; + break; + case 41: + end(); + break; + case 200: + end(); + player.getQuestRepository().getQuest(Quests.DRUIDIC_RITUAL).finish(player); + break; + case 500: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Who are you?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Did you build this?"); + stage = 30; + break; + } + break; + case 501: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Can I buy a Skillcape of Herblore?"); + stage = 800; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Who are you?"); + stage = 10; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Did you build this?"); + stage = 30; + break; + } + break; + case 600: + interpreter.sendDialogues(player, null, "Good good!"); + stage = 601; + break; + case 601: + 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 = 501; + } else { + interpreter.sendOptions("Select an Option", "Who are you?", "Did you build this?"); + stage = 500; + } + break; + case 1000: + interpreter.sendDialogues(npc, null, "Herblore is the skill of working with herbs and other", "ingredients, to make useful potions and poison."); + stage = 1001; + break; + case 1001: + interpreter.sendDialogues(npc, null, "First you will need a vial, which can be found or made", "with the crafting skill."); + stage = 1002; + break; + case 1002: + interpreter.sendDialogues(npc, null, "Then you must gather the herbs needed to make the", "potion you want."); + stage = 1003; + break; + case 1003: + interpreter.sendDialogues(npc, null, "Refer to the Council's instructions in the Skills section", "of the website for the items needed to make a particular", "kind of potion."); + stage = 1004; + break; + case 1004: + interpreter.sendDialogues(npc, null, "You must fill your vial with water and add the", "ingredients you need. There are normally 2 ingredients", "to each type of potion."); + stage = 1005; + break; + case 1005: + interpreter.sendDialogues(npc, null, "Bear in mind you must first identify each herb, to see", "what it is."); + stage = 1006; + break; + case 1006: + interpreter.sendDialogues(npc, null, "You may also have to grind some herbs before you can", "use them. You will need a pestle and mortar in order", "to do this."); + stage = 1007; + break; + case 1007: + interpreter.sendDialogues(npc, null, "Herbs can be found on the ground, and are also", "dropped by some monsters when you kill them."); + stage = 1008; + break; + case 1008: + interpreter.sendDialogues(npc, null, "Mix these in your water-filled vial, and you will produce", "an Attack potion."); + stage = 1009; + break; + case 1009: + interpreter.sendDialogues(npc, null, "Drink this poition to increase your Attack level."); + stage = 1010; + break; + case 1010: + interpreter.sendDialogues(npc, null, "Different potions also require different Herblore levels", "before you can make them."); + stage = 1011; + break; + case 1011: + interpreter.sendDialogues(npc, null, "Once again, check the instructions found on the", "Council's website for the levels needed to make a", "particulur potion."); + stage = 1012; + break; + case 1012: + interpreter.sendDialogues(npc, null, "Good luck with your Herblore practices, Good day", "Adventurer."); + stage = 1013; + break; + case 1013: + interpreter.sendDialogues(player, null, "Thanks for your help."); + stage = 1014; + break; + case 1014: + end(); + break; + case 800: + npc("Certainly! Right when you give me 99000 coins."); + stage = 801; + break; + case 801: + options("Yes, here you go,", "No, thanks."); + stage = 802; + break; + case 802: + switch (buttonId) { + case 1: + player("Yes, here you go."); + stage = 803; + break; + case 2: + end(); + break; + } + break; + case 803: + if (Skillcape.purchase(player, Skills.HERBLORE)) { + npc("There you go! Enjoy."); + } + stage = 804; + break; + case 804: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 455 }; + } +} diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java new file mode 100644 index 000000000..39f24867c --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java @@ -0,0 +1,209 @@ +package content.region.asgarnia.taverley.dialogue; + +import java.util.List; + +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.plugin.Initializable; +import core.game.world.map.RegionManager; + +/** + * Represents the dialogue for the taverly pet shop owner. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class PetshopOwnerTaverly extends DialoguePlugin { + + /** + * Represents the dog name. + */ + private String dogName; + + /** + * Represents the puppy. + */ + private Item puppy; + + /** + * Constructs a new {@code PetshopOwnerTaverly} {@code Object}. + */ + public PetshopOwnerTaverly() { + /** + * emppty. + */ + } + + /** + * Constructs a new {@code PetshopOwnerTaverly} {@code Object}. + * @param player the player. + */ + public PetshopOwnerTaverly(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new PetshopOwnerTaverly(player); + } + + @Override + public boolean open(Object... args) { + if (args.length > 1) { + List npcs = RegionManager.getLocalNpcs(player); + for (NPC n : npcs) { + if (n.getId() == 6893) { + npc = n; + break; + } + } + dogName = (String) args[0]; + puppy = (Item) args[1]; + player("No, the " + dogName + "."); + stage = 699; + return true; + } + npc = (NPC) args[0]; + interpreter.sendOptions("Choose an option:", "Can I see your shop, please?", "How much is that puppy in the window?", "So, what sorts of pets are available?"); + stage = 1; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Can I see your shop please?"); + stage = 100; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "How much is that puppy in the window?"); + stage = 200; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "So, what sorts of pets are available?"); + stage = 300; + break; + } + break; + case 100: + end(); + npc.openShop(player); + break; + case 200: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "The one with the waggly tail?"); + stage = 201; + break; + case 201: + end(); + player.getInterfaceManager().openChatbox(668); + stage = 202; + break; + case 202: + break; + case 300: + 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_ASKING, "Such as?"); + stage = 302; + break; + case 302: + 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.FRIENDLY, "I'll bear that in mind."); + stage = 304; + break; + case 304: + 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.HAPPY, "Thank's alot! You've been very helpfull."); + stage = 306; + break; + case 306: + 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: + end(); + break; + case 700: + player("Isn't that a little steep?"); + stage = 701; + break; + case 701: + npc("Well, if we gave them away for free then people would", "just buy them and dump them without a care."); + stage = 702; + break; + case 702: + npc("Dogs are a big responsibility and should be cared for."); + stage = 703; + break; + case 703: + npc("If a person in unwilling to invest 500 coins, then they", "don't deserve to have the puppy in the first place."); + stage = 704; + break; + case 704: + npc("So, do you still want one?"); + stage = 705; + break; + case 705: + options("Okay, I'll take the " + dogName + ".", "No thanks."); + stage = 706; + break; + case 706: + switch (buttonId) { + case 1: + player("Okay, I'll take the " + dogName + "."); + stage = 707; + break; + case 2: + end(); + break; + } + break; + case 707: + if (player.getInventory().freeSlots() == 0) { + npc("You don't have enough inventory space."); + stage = 708; + return true; + } + if (!player.getInventory().containsItem(new Item(995, 500))) { + end(); + return true; + } + if (player.getInventory().remove(new Item(995, 500))) { + player.getInventory().add(puppy); + npc("There you go! I hope you two get on."); + stage = 708; + } else { + player.getPacketDispatch().sendMessage("You don't the required coins in order to do this."); + end(); + } + break; + case 708: + end(); + break; + case 699: + npc("500 gold."); + stage = 700; + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 6893 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/PikkupstixDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/PikkupstixDialogue.java rename to Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java index 5a11ddea7..399daecc7 100644 --- a/Server/src/main/java/core/game/content/dialogue/PikkupstixDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java @@ -1,5 +1,8 @@ -package core.game.content.dialogue; +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; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -7,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 @@ -15,6 +21,7 @@ import core.game.node.item.Item; * @notice Modified March 2nd, 2015 to allow the bulk buying of shards. * @notice The previous notice is dubious * @notice Spirit shard packs were released 9 September 2009, outside of 530. Option removed. + * Fixed multiple typos -Nuggles */ @Initializable public final class PikkupstixDialogue extends DialoguePlugin { @@ -79,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?"); @@ -122,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); @@ -141,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; @@ -438,7 +449,7 @@ public final class PikkupstixDialogue extends DialoguePlugin { stage = 6; break; case 6: - npc("Well, for a start, it has the ability to perform a", "Summoning scroll that forces the oppnents to flee. That is", "what it will use on the giant wolpertinger, with the", "difference being that the giant wolpertinger will be so"); + npc("Well, for a start, it has the ability to perform a", "Summoning scroll that forces the opponents to flee. That is", "what it will use on the giant wolpertinger, with the", "difference being that the giant wolpertinger will be so"); stage = 7; break; case 7: @@ -624,10 +635,10 @@ public final class PikkupstixDialogue extends DialoguePlugin { switch (stage) { case 0: if (player.getSkills().getStaticLevel(Skills.SUMMONING) == 99) { - options("So, what's Summmoning 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 Summmoning all about, then?", "Can I buy some Summoning supplies?", "Please tell me about skillcapes."); + options("So, what's Summoning all about, then?", "Can I buy some Summoning supplies?", "Please tell me about skillcapes."); stage = 1; } break; @@ -685,7 +696,7 @@ public final class PikkupstixDialogue extends DialoguePlugin { stage = 102; break; case 102: - npc("These animals can also perform a special move, wich is", "specific to the species. For example, a spirit wolf can", "perform the Howl special move if you are holding the", "correct Howl scroll."); + npc("These animals can also perform a special move, which is", "specific to the species. For example, a spirit wolf can", "perform the Howl special move if you are holding the", "correct Howl scroll."); stage = 103; break; case 103: @@ -693,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: @@ -743,41 +754,46 @@ public final class PikkupstixDialogue extends DialoguePlugin { stage = 401; break; case 401: - interpreter.sendOptions("Choose an option:", "So, what's Summmoning all about, then?", "Can I buy some Summoning supplies?", "Please tell me about skillcapes."); + interpreter.sendOptions("Choose an option:", "So, what's Summoning all about, then?", "Can I buy some Summoning supplies?", "Please tell me about skillcapes."); stage = 1; break; 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; @@ -797,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/java/core/game/content/dialogue/SanfewDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/SanfewDialogue.java similarity index 80% rename from Server/src/main/java/core/game/content/dialogue/SanfewDialogue.java rename to Server/src/main/content/region/asgarnia/taverley/dialogue/SanfewDialogue.java index 0cb3a771f..0c5d09444 100644 --- a/Server/src/main/java/core/game/content/dialogue/SanfewDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/SanfewDialogue.java @@ -1,9 +1,12 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Handles the SanfewDialogue dialogue. @@ -38,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; @@ -61,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: @@ -82,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: @@ -118,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: @@ -127,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/java/core/game/content/dialogue/TeegidDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/TeegidDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/TeegidDialogue.java rename to Server/src/main/content/region/asgarnia/taverley/dialogue/TeegidDialogue.java index 22c21ed57..d6b8d3abc 100644 --- a/Server/src/main/java/core/game/content/dialogue/TeegidDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/TeegidDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; import core.game.world.map.Direction; diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/dungeon/taverley/VelrakDialogue.kt b/Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt similarity index 81% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/dungeon/taverley/VelrakDialogue.kt rename to Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt index 319cd2089..90063d64c 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/dungeon/taverley/VelrakDialogue.kt +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt @@ -1,14 +1,14 @@ -package rs09.game.content.dialogue.region.dungeon.taverley +package content.region.asgarnia.taverley.dialogue -import api.Container -import api.ContentAPI -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +import core.api.Container +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 org.rs09.consts.Items import org.rs09.consts.NPCs -import rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE @Initializable class VelrakDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -17,11 +17,10 @@ class VelrakDialogue(player: Player? = null) : DialoguePlugin(player) { } override fun open(vararg args: Any?): Boolean { - if(ContentAPI.inInventory(player, Items.DUSTY_KEY_1590)){ + if(inInventory(player, Items.DUSTY_KEY_1590)){ playerl(FacialExpression.HALF_THINKING, "Are you still here?").also { stage = 100 } } else { npcl(FacialExpression.FRIENDLY, "Thank you for rescuing me! It isn't very comfy in this cell.") - ContentAPI.removeItem(player, Items.JAIL_KEY_1591, Container.INVENTORY) } return true } @@ -42,7 +41,7 @@ class VelrakDialogue(player: Player? = null) : DialoguePlugin(player) { 2 -> playerl(FacialExpression.NEUTRAL, "No, it's too dangerous for me too.").also { stage = 15 } } - 14 -> ContentAPI.sendItemDialogue(player, Items.DUSTY_KEY_1590, "Velrak reaches somewhere mysterious and passes you a key.").also { ContentAPI.addItem(player, Items.DUSTY_KEY_1590, 1); stage = END_DIALOGUE } + 14 -> sendItemDialogue(player, Items.DUSTY_KEY_1590, "Velrak reaches somewhere mysterious and passes you a key.").also { addItemOrDrop(player, Items.DUSTY_KEY_1590, 1); stage = END_DIALOGUE } 15 -> npcl(FacialExpression.FRIENDLY, "I don't blame you!").also { stage = END_DIALOGUE } diff --git a/Server/src/main/java/core/game/node/entity/npc/other/ArmourSuitNPC.java b/Server/src/main/content/region/asgarnia/taverley/handlers/ArmourSuitNPC.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/npc/other/ArmourSuitNPC.java rename to Server/src/main/content/region/asgarnia/taverley/handlers/ArmourSuitNPC.java index 013071479..2a85c2bf1 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/ArmourSuitNPC.java +++ b/Server/src/main/content/region/asgarnia/taverley/handlers/ArmourSuitNPC.java @@ -1,10 +1,10 @@ -package core.game.node.entity.npc.other; +package content.region.asgarnia.taverley.handlers; import core.game.node.entity.npc.AbstractNPC; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.plugin.Initializable; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/interaction/object/CrystalChestPlugin.java b/Server/src/main/content/region/asgarnia/taverley/handlers/CrystalChestPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/object/CrystalChestPlugin.java rename to Server/src/main/content/region/asgarnia/taverley/handlers/CrystalChestPlugin.java index 5684a18b5..59fd7beb8 100644 --- a/Server/src/main/java/core/game/interaction/object/CrystalChestPlugin.java +++ b/Server/src/main/content/region/asgarnia/taverley/handlers/CrystalChestPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.asgarnia.taverley.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.NodeUsageEvent; @@ -8,7 +8,7 @@ import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.plugin.Initializable; import core.tools.RandomFunction; @@ -35,7 +35,7 @@ public final class CrystalChestPlugin extends UseWithHandler { @Override public Plugin newInstance(Object arg) throws Throwable { addHandler(172, OBJECT_TYPE, this); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { @@ -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/java/core/game/interaction/object/CrystalKeyObjectPlugin.java b/Server/src/main/content/region/asgarnia/taverley/handlers/CrystalKeyObjectPlugin.java similarity index 90% rename from Server/src/main/java/core/game/interaction/object/CrystalKeyObjectPlugin.java rename to Server/src/main/content/region/asgarnia/taverley/handlers/CrystalKeyObjectPlugin.java index 99dc9a38b..5bc49c4cb 100644 --- a/Server/src/main/java/core/game/interaction/object/CrystalKeyObjectPlugin.java +++ b/Server/src/main/content/region/asgarnia/taverley/handlers/CrystalKeyObjectPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.asgarnia.taverley.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/region/asgarnia/taverley/handlers/TaverleyDungeonListeners.kt b/Server/src/main/content/region/asgarnia/taverley/handlers/TaverleyDungeonListeners.kt new file mode 100644 index 000000000..fd9c45bec --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/handlers/TaverleyDungeonListeners.kt @@ -0,0 +1,42 @@ +package content.region.asgarnia.taverley.handlers + +import core.api.* +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class TaverleyDungeonListeners : InteractionListener { + + val BD_GATE = Scenery.GATE_2623 + val JAIL_DOOR = Scenery.DOOR_31838 + + override fun defineListeners() { + + on(BD_GATE, IntType.SCENERY, "open"){ player, node -> + if(!inInventory(player, Items.DUSTY_KEY_1590)){ + sendMessage(player, "This gate seems to be locked.") + } else { + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + return@on true + } + + + on(JAIL_DOOR, IntType.SCENERY, "open"){ player, node -> + when(player.location.y){ + 9689 -> core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) //inside the cell going out + 9690 -> { //outside the cell going in + if(!inInventory(player, Items.JAIL_KEY_1591)){ + sendMessage(player, "This door is locked.") + } else { + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + } + } + + return@on true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/city/TaverlyDungeonPlugin.java b/Server/src/main/content/region/asgarnia/taverley/handlers/TaverlyDungeonPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/city/TaverlyDungeonPlugin.java rename to Server/src/main/content/region/asgarnia/taverley/handlers/TaverlyDungeonPlugin.java index 68dafd82e..4dc86deb4 100644 --- a/Server/src/main/java/core/game/interaction/city/TaverlyDungeonPlugin.java +++ b/Server/src/main/content/region/asgarnia/taverley/handlers/TaverlyDungeonPlugin.java @@ -1,7 +1,7 @@ -package core.game.interaction.city; +package content.region.asgarnia.taverley.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; 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 new file mode 100644 index 000000000..29160561d --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/quest/WolfWhistle.java @@ -0,0 +1,198 @@ +package content.region.asgarnia.taverley.quest; + +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.Item; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; + + +/** + * Represents the demon slayer quest. + * @author 'Vexia + */ +@Initializable +public class WolfWhistle extends Quest { + + /** + * The wolf bones item. + */ + 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(Quests.WOLF_WHISTLE, 146, 145, 1); + } + + @Override + public void drawJournal(Player player, int stage) { + super.drawJournal(player, stage); + 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); + } + } + + @Override + public void finish(Player player) { + super.finish(player); + player.getPacketDispatch().sendString("1 Quest Points", 277, 8+ 2); + player.getPacketDispatch().sendString("276 Summoning XP", 277, 9+ 2); + player.getPacketDispatch().sendString("Access to the Summoning", 277, 10+ 2); + player.getPacketDispatch().sendString("skill.", 277, 11+ 2); + player.getPacketDispatch().sendString("275 gold charms.", 277, 12+ 2); + player.getPacketDispatch().sendItemZoomOnInterface(12047, 230, 277, 3+ 2); + 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 = getVarp(player, 1178); + boolean open = val >= 4096; + boolean closed = val == 2048; + if (stage == 100) { + if (val == 5 || val == 0) { + return new int[] { 1178, 32989 }; + } else if (val == 4101) { + return new int[] { 1178, 28893 }; + } + return new int[] { 1178, val }; + } + return new int[] { 1178, stage > 0 ? 5 + (open ? 4096 : 0) : stage >= 100 ? !closed ? 28893 : 32989 : 0 }; + } + + @Override + public Quest newInstance(Object object) { + return this; + } + +} diff --git a/Server/src/main/java/core/game/content/quest/members/witchshouse/BallInteraction.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/BallInteraction.java similarity index 90% rename from Server/src/main/java/core/game/content/quest/members/witchshouse/BallInteraction.java rename to Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/BallInteraction.java index 337af2bd2..df28abdb3 100644 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/BallInteraction.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/BallInteraction.java @@ -1,16 +1,17 @@ -package core.game.content.quest.members.witchshouse; +package content.region.asgarnia.taverley.quest.witchshouse; import core.game.interaction.MovementPulse; import core.game.interaction.Option; +import core.game.node.entity.impl.PulseType; 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.plugin.Plugin; import core.game.node.entity.combat.spell.SpellBlocks; -import core.game.content.activity.mta.TelekineticGrabSpell; -import core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import content.minigame.mta.TelekineticGrabSpell; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; import core.game.node.entity.skill.Skills; @Initializable @@ -35,7 +36,7 @@ public class BallInteraction extends PluginInteraction { public boolean pulse() { return true; } - }, "movement"); + }, PulseType.STANDARD); handleBall(player); } return handled; 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 new file mode 100644 index 000000000..2574498c4 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/BoyDialoguePlugin.java @@ -0,0 +1,151 @@ +package content.region.asgarnia.taverley.quest.witchshouse; + +import core.plugin.Initializable; +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.data.Quests; + +/** + * Created for 2009Scape + * User: Ethan Kyle Millard + * Date: March 15, 2020 + * Time: 11:49 AM + */ +@Initializable +public class BoyDialoguePlugin extends DialoguePlugin { + + private static final Item BALL = new Item(2407); + + public BoyDialoguePlugin() { + } + + public BoyDialoguePlugin(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new BoyDialoguePlugin(player); + } + + @Override + public boolean open(Object... args) { + 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(FacialExpression.FRIENDLY, "Hello young man."); + setStage(1); + return true; + } + if (quest.isCompleted(player) || quest.getStage(player) == 100) { + sendDialogue("The boy is too busy playing with his ball to talk."); + finish(); + return true; + } + if (!player.getInventory().containsItem(BALL)) { + npc( FacialExpression.CHILD_THINKING, "Have you gotten my ball back yet?"); + } else { + player(FacialExpression.NEUTRAL, "Hi, I have got your ball back. It was MUCH harder", "than I thought it would be."); + } + setStage(11); + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + final Quest quest = player.getQuestRepository().getQuest(Quests.WITCHS_HOUSE); + switch(stage) { + case -1: + end(); + break; + case 1: + sendDialogue("The boy sobs."); + next(); + break; + case 2: + options("What's the matter?", "Well if you're not going to answer then I'll go."); + next(); + break; + case 3: + switch(buttonId) { + case 1: + player(FacialExpression.THINKING, "What's the matter?"); + setStage(5); + break; + case 2: + player(FacialExpression.NEUTRAL, "Well if you're not going to answer then I'll go."); + next(); + break; + } + break; + case 4: + sendDialogue("The boy sniffs slightly."); + finish(); + break; + case 5: + 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: + options("Ok, I'll see what I can do.", "Get it back yourself."); + next(); + break; + case 7: + switch(buttonId) { + case 1: + player(FacialExpression.NEUTRAL, "Ok, I'll see what I can do."); + setStage(10); + break; + case 2: + player(FacialExpression.NEUTRAL, "Get it back yourself."); + next(); + break; + } + break; + case 8: + npc(FacialExpression.CHILD_SAD, "You're a meany."); + next(); + break; + case 9: + sendDialogue("The boy starts crying again."); + finish(); + break; + case 10: + npc(FacialExpression.CHILD_FRIENDLY, "Thanks mister!"); + finish(); + quest.start(player); + break; + case 11: + if (!player.getInventory().containsItem(BALL)) { + player(FacialExpression.NEUTRAL, "Not yet."); + next(); + } else { + if (player.getInventory().remove(BALL)) + sendDialogue("You give the ball back."); + setStage(13); + } + break; + case 12: + npc(FacialExpression.CHILD_ANGRY, "Well it's in the shed in that garden."); + finish(); + break; + case 13: + npc(FacialExpression.CHILD_FRIENDLY, "Thank you so much!"); + next(); + break; + case 14: + quest.finish(player); + finish(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 895 }; + } +} diff --git a/Server/src/main/java/core/game/content/quest/members/witchshouse/ExperimentSession.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/ExperimentSession.java similarity index 98% rename from Server/src/main/java/core/game/content/quest/members/witchshouse/ExperimentSession.java rename to Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/ExperimentSession.java index 24d11b697..016372446 100644 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/ExperimentSession.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/ExperimentSession.java @@ -3,7 +3,7 @@ package core.game.content.quest.witchs_house; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; public final class ExperimentSession { diff --git a/Server/src/main/java/core/game/content/quest/members/witchshouse/MouseNPC.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/MouseNPC.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/members/witchshouse/MouseNPC.java rename to Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/MouseNPC.java index 3bd8fdee2..db5b3d621 100644 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/MouseNPC.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/MouseNPC.java @@ -1,8 +1,8 @@ -package core.game.content.quest.members.witchshouse; +package content.region.asgarnia.taverley.quest.witchshouse; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; /** diff --git a/Server/src/main/java/core/game/content/quest/members/witchshouse/NoraTHaggNPC.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/NoraTHaggNPC.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/members/witchshouse/NoraTHaggNPC.java rename to Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/NoraTHaggNPC.java index b9395c572..2192bf840 100644 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/NoraTHaggNPC.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/NoraTHaggNPC.java @@ -1,12 +1,12 @@ -package core.game.content.quest.members.witchshouse; +package content.region.asgarnia.taverley.quest.witchshouse; import core.game.node.entity.Entity; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.DeathTask; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; 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/java/core/game/content/quest/members/witchshouse/WitchsExperimentNPC.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsExperimentNPC.java rename to Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java index e2c6de9cf..5f1e46e06 100644 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsExperimentNPC.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java @@ -1,11 +1,11 @@ -package core.game.content.quest.members.witchshouse; +package content.region.asgarnia.taverley.quest.witchshouse; 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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.plugin.Initializable; @@ -57,7 +57,7 @@ public class WitchsExperimentNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { return p == entity; } @@ -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 new file mode 100644 index 000000000..ab2b32e00 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHouse.java @@ -0,0 +1,64 @@ +package content.region.asgarnia.taverley.quest.witchshouse; + +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 + */ +@Initializable +public class WitchsHouse extends Quest { + + /** + * Constructs a new {@code WitchsHouse} {@code Object}. + */ + public WitchsHouse() { + super(Quests.WITCHS_HOUSE, 124, 123, 4, 226, 0, 1, 7); + } + + @Override + public void drawJournal(Player player, int stage) { + super.drawJournal(player, stage); + 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.getPacketDispatch().sendString("4 Quest Points", 277, 8 + 2); + player.getPacketDispatch().sendString("6325 Hitpoints XP", 277, 9 + 2); + player.getSkills().addExperience(Skills.HITPOINTS, 6325); + player.getInterfaceManager().closeChatbox(); + player.getPacketDispatch().sendItemZoomOnInterface(2407, 240, 277, 3 + 2); + } + + @Override + public Quest newInstance(Object object) { + return this; + } +} diff --git a/Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsHousePlugin.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHousePlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsHousePlugin.java rename to Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHousePlugin.java index e781d6403..5e8ccb1d9 100644 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsHousePlugin.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsHousePlugin.java @@ -1,8 +1,8 @@ -package core.game.content.quest.members.witchshouse; +package content.region.asgarnia.taverley.quest.witchshouse; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -16,8 +16,9 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +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,18 +160,14 @@ 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; } @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new WitchsHouseUseWithHandler()); - PluginManager.definePlugin(new MouseNPC()); + ClassScanner.definePlugin(new WitchsHouseUseWithHandler()); + ClassScanner.definePlugin(new MouseNPC()); SceneryDefinition.forId(2867).getHandlers().put("option:look-under", this); SceneryDefinition.forId(2861).getHandlers().put("option:open", this); SceneryDefinition.forId(2865).getHandlers().put("option:open", this); diff --git a/Server/src/main/java/core/game/content/dialogue/GWDKnightDialogue.java b/Server/src/main/content/region/asgarnia/trollheim/dialogue/GWDKnightDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/GWDKnightDialogue.java rename to Server/src/main/content/region/asgarnia/trollheim/dialogue/GWDKnightDialogue.java index 862fe1d43..750447d19 100644 --- a/Server/src/main/java/core/game/content/dialogue/GWDKnightDialogue.java +++ b/Server/src/main/content/region/asgarnia/trollheim/dialogue/GWDKnightDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.dialogue; +package content.region.asgarnia.trollheim.dialogue; 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; @@ -10,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 @@ -63,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; } @@ -90,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/java/core/game/node/entity/npc/other/ThrowerTrollNPC.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/ThrowerTrollNPC.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/npc/other/ThrowerTrollNPC.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/ThrowerTrollNPC.java index 1d852be82..e8fbf5642 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/ThrowerTrollNPC.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/ThrowerTrollNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.asgarnia.trollheim.handlers; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatStyle; diff --git a/Server/src/main/java/core/game/interaction/city/TrollheimPlugin.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/TrollheimPlugin.java similarity index 80% rename from Server/src/main/java/core/game/interaction/city/TrollheimPlugin.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/TrollheimPlugin.java index 1e74ef3fc..d6a2c1fc6 100644 --- a/Server/src/main/java/core/game/interaction/city/TrollheimPlugin.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/TrollheimPlugin.java @@ -1,14 +1,14 @@ -package core.game.interaction.city; +package content.region.asgarnia.trollheim.handlers; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialoguePlugin; +import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -19,13 +19,13 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; 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 rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; import core.game.world.update.flag.context.Animation; import core.net.packet.PacketRepository; import core.net.packet.context.CameraContext; @@ -33,7 +33,7 @@ import core.net.packet.context.CameraContext.CameraType; import core.net.packet.out.CameraViewPacket; import core.plugin.Plugin; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the plugin used to handle all trollheim node interations. @@ -156,12 +156,10 @@ public final class TrollheimPlugin extends OptionHandler { // ladder SceneryDefinition.forId(18833).getHandlers().put("option:climb-down", this);// stronghold // ladder - PluginManager.definePlugin(new SabaDialogue()); - PluginManager.definePlugin(new WoundedSoldier()); - PluginManager.definePlugin(new WarningZone()); + ClassScanner.definePlugin(new WoundedSoldier()); + ClassScanner.definePlugin(new WarningZone()); ActivityManager.register(new WarningCutscene()); - PluginManager.definePlugin(new TenzingDialogue()); - PluginManager.definePlugin(new TrollNPC()); + 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/java/core/game/content/activity/gwd/EcumenicalKeyHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/EcumenicalKeyHandler.java similarity index 100% rename from Server/src/main/java/core/game/content/activity/gwd/EcumenicalKeyHandler.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/EcumenicalKeyHandler.java diff --git a/Server/src/main/java/core/game/content/activity/gwd/GWDGraardorSwingHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDGraardorSwingHandler.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/gwd/GWDGraardorSwingHandler.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDGraardorSwingHandler.java index 8d5fbf21b..540d48f02 100644 --- a/Server/src/main/java/core/game/content/activity/gwd/GWDGraardorSwingHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDGraardorSwingHandler.java @@ -1,4 +1,4 @@ -package core.game.content.activity.gwd; +package content.region.asgarnia.trollheim.handlers.gwd; import java.util.ArrayList; import java.util.List; @@ -6,8 +6,7 @@ import java.util.List; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import org.jetbrains.annotations.NotNull; -import rs09.game.node.entity.combat.CombatSwingHandler; +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; @@ -71,7 +70,7 @@ public final class GWDGraardorSwingHandler extends CombatSwingHandler { if (!CHAMBER.insideBorder(t.getLocation())) { continue; } - if (t.isAttackable(npc, CombatStyle.RANGE)) { + if (t.isAttackable(npc, CombatStyle.RANGE, false)) { list.add(new BattleState(entity, t)); } } @@ -180,4 +179,4 @@ public final class GWDGraardorSwingHandler extends CombatSwingHandler { else return -1; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/content/activity/gwd/GWDKreeArraSwingHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDKreeArraSwingHandler.java similarity index 97% rename from Server/src/main/java/core/game/content/activity/gwd/GWDKreeArraSwingHandler.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDKreeArraSwingHandler.java index 77d0a50a8..925f917fd 100644 --- a/Server/src/main/java/core/game/content/activity/gwd/GWDKreeArraSwingHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDKreeArraSwingHandler.java @@ -1,4 +1,4 @@ -package core.game.content.activity.gwd; +package content.region.asgarnia.trollheim.handlers.gwd; import java.util.ArrayList; import java.util.List; @@ -6,7 +6,7 @@ import java.util.List; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; +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; @@ -87,7 +87,7 @@ public final class GWDKreeArraSwingHandler extends CombatSwingHandler { if (!CHAMBER.insideBorder(t.getLocation())) { continue; } - if (t.isAttackable(npc, CombatStyle.RANGE)) { + if (t.isAttackable(npc, CombatStyle.RANGE, false)) { list.add(new BattleState(entity, t)); } } @@ -214,4 +214,4 @@ public final class GWDKreeArraSwingHandler extends CombatSwingHandler { else return -1; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/content/activity/gwd/GWDTsutsarothSwingHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDTsutsarothSwingHandler.java similarity index 87% rename from Server/src/main/java/core/game/content/activity/gwd/GWDTsutsarothSwingHandler.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDTsutsarothSwingHandler.java index 57032f817..2e18d36e2 100644 --- a/Server/src/main/java/core/game/content/activity/gwd/GWDTsutsarothSwingHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDTsutsarothSwingHandler.java @@ -1,19 +1,20 @@ -package core.game.content.activity.gwd; +package content.region.asgarnia.trollheim.handlers.gwd; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; +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/java/core/game/content/activity/gwd/GWDZilyanaSwingHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDZilyanaSwingHandler.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/gwd/GWDZilyanaSwingHandler.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDZilyanaSwingHandler.java index 1dec4fb03..a1f060d9a 100644 --- a/Server/src/main/java/core/game/content/activity/gwd/GWDZilyanaSwingHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GWDZilyanaSwingHandler.java @@ -1,4 +1,4 @@ -package core.game.content.activity.gwd; +package content.region.asgarnia.trollheim.handlers.gwd; import java.util.ArrayList; import java.util.List; @@ -6,7 +6,7 @@ import java.util.List; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; +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.Animator.Priority; @@ -60,7 +60,7 @@ public class GWDZilyanaSwingHandler extends CombatSwingHandler { NPC npc = (NPC) entity; List list = new ArrayList<>(20); for (Entity t : RegionManager.getLocalPlayers(npc.getCenterLocation(), (npc.size() >> 1) + 2)) { - if (t.getLocation().getX() < 2908 && t.isAttackable(npc, CombatStyle.MAGIC)) { + if (t.getLocation().getX() < 2908 && t.isAttackable(npc, CombatStyle.MAGIC, false)) { list.add(new BattleState(entity, t)); } } @@ -157,4 +157,4 @@ public class GWDZilyanaSwingHandler extends CombatSwingHandler { state.getStyle().getSwingHandler().visualizeImpact(entity, victim, state); } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsFaction.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsFaction.java new file mode 100644 index 000000000..2c653dee3 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsFaction.java @@ -0,0 +1,84 @@ +package content.region.asgarnia.trollheim.handlers.gwd; + +import core.api.God; +import core.game.node.entity.player.Player; + +import static core.api.ContentAPIKt.hasGodItem; + +/** + * The god wars factions. + * @author Emperor + */ +public enum GodWarsFaction { + ARMADYL(6222, 6246, God.ARMADYL), + BANDOS(6260, 6283, God.BANDOS), + SARADOMIN(6247, 6259, God.SARADOMIN), + ZAMORAK(6203, 6221, God.ZAMORAK); + + /** + * The start NPC id. + */ + private final int startId; + + /** + * The end NPC id. + */ + private final int endId; + + /** + * The god this faction represents + */ + private final God god; + + /** + * Constructs a new {@code GodWarsFaction} {@code Object}. + * @param startId The start NPC id. + * @param endId The end NPC id. + * @param god The god this faction represents. + */ + private GodWarsFaction(int startId, int endId, God god) { + this.startId = startId; + this.endId = endId; + this.god = god; + } + + /** + * Gets the god wars faction for the given NPC id. + * @param npcId The NPC id. + * @return The faction for this NPC. + */ + public static GodWarsFaction forId(int npcId) { + for (GodWarsFaction faction : values()) { + if (npcId >= faction.getStartId() && npcId <= faction.getEndId()) { + return faction; + } + } + return null; + } + + /** + * Checks if the player is protected from this faction. + * @param player The player. + * @return {@code True} if no NPCs of this faction should attack the + * player. + */ + public boolean isProtected(Player player) { + return hasGodItem(player, god); + } + + /** + * Gets the startId. + * @return The startId. + */ + public int getStartId() { + return startId; + } + + /** + * Gets the endId. + * @return The endId. + */ + public int getEndId() { + return endId; + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/gwd/GodWarsMinionNPC.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsMinionNPC.java similarity index 83% rename from Server/src/main/java/core/game/content/activity/gwd/GodWarsMinionNPC.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsMinionNPC.java index ca2e69964..b6841b41e 100644 --- a/Server/src/main/java/core/game/content/activity/gwd/GodWarsMinionNPC.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsMinionNPC.java @@ -1,4 +1,4 @@ -package core.game.content.activity.gwd; +package content.region.asgarnia.trollheim.handlers.gwd; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatStyle; @@ -6,7 +6,7 @@ import core.game.node.entity.combat.DeathTask; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.plugin.Initializable; import core.game.world.map.Location; @@ -41,6 +41,7 @@ public final class GodWarsMinionNPC extends AbstractNPC { @Override public void init() { super.init(); + getAggressiveHandler().setAllowTolerance(false); } @Override @@ -84,12 +85,14 @@ public final class GodWarsMinionNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (boss != null && boss.getId() == 6222 && style == CombatStyle.MELEE && entity instanceof Player) { - ((Player) entity).getPacketDispatch().sendMessage("The aviansie is flying too high for you to attack using melee."); + if(message) { + ((Player) entity).getPacketDispatch().sendMessage("The aviansie is flying too high for you to attack using melee."); + } return false; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsNPC.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsNPC.java new file mode 100644 index 000000000..215de9f11 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodWarsNPC.java @@ -0,0 +1,169 @@ +package content.region.asgarnia.trollheim.handlers.gwd; + +import java.util.ArrayList; +import java.util.List; + +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.DeathTask; +import core.game.node.entity.npc.AbstractNPC; +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.entity.player.link.TeleportManager; +import core.game.world.map.Location; +import core.plugin.Initializable; +import core.game.world.map.RegionManager; +import core.tools.RandomFunction; +import org.rs09.consts.NPCs; + +import static core.api.ContentAPIKt.getPathableRandomLocalCoordinate; +import static core.api.ContentAPIKt.sendGraphics; + +/** + * Handles a god wars NPC. + * @author Emperor + */ +@Initializable +public final class GodWarsNPC extends AbstractNPC { + + private static final int IMP_TELEPORT_CHANCE_ON_HIT = 10; // 1/10 + private static final int IMP_TELEPORT_CHANCE_ON_TICK = 75; // 1/75 + + /** + * The aggressive behavior. + */ + private static final AggressiveBehavior AGGRO_BEHAVIOR = new AggressiveBehavior() { + + @Override + public boolean canSelectTarget(Entity entity, Entity target) { + if (!target.isActive() || DeathTask.isDead(target) || DeathTask.isDead(entity)) { + return false; + } + if (!target.getProperties().isMultiZone() && target.inCombat()) { + return false; + } + if (target instanceof GodWarsNPC) { + if (((GodWarsNPC) target).faction != ((GodWarsNPC) entity).faction) { + return true; + } + } else if (target instanceof Player) { + if (!((GodWarsNPC) entity).faction.isProtected((Player) target)) { + return true; + } + } + return false; + } + + @Override + public List getPossibleTargets(Entity entity, int radius) { + List targets = new ArrayList<>(20); + for (Player player : RegionManager.getLocalPlayers(entity, radius)) { + if (canSelectTarget(entity, player)) { + targets.add(player); + } + } + if (!targets.isEmpty()) { + return targets; + } + for (NPC npc : RegionManager.getLocalNpcs(entity, radius)) { + if (canSelectTarget(entity, npc)) { + targets.add(npc); + } + } + return targets; + } + }; + + /** + * The god wars faction (0=armadyl, 1=bandos, 2=saradomin, 3=zamorak). + */ + private GodWarsFaction faction; + + /** + * Constructs a new {@code GodWarsNPC} {@code Object}. + */ + public GodWarsNPC() { + super(-1, null); + } + + /** + * Constructs a new {@code GodWarsNPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + public GodWarsNPC(int id, Location location) { + super(id, location); + } + + @Override + public void init() { + super.init(); + setWalks(true); + faction = GodWarsFaction.forId(getId()); + } + + @Override + public void setDefaultBehavior() { + setAggressive(true); + aggressiveHandler = new AggressiveHandler(this, AGGRO_BEHAVIOR); + } + + @Override + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { + if (style == CombatStyle.MELEE && faction == GodWarsFaction.ARMADYL && entity instanceof Player) { + if(message) { + ((Player) entity).getPacketDispatch().sendMessage("The aviansie is flying too high for you to attack using melee."); + } + return false; + } + return super.isAttackable(entity, style, message); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new GodWarsNPC(id, location); + } + + @Override + public int[] getIds() { + return new int[] { 6210, 6211, 6212, 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, 6221, 6229, 6230, 6231, 6232, 6233, 6234, 6235, 6236, 6237, 6238, 6239, 6240, 6241, 6242, 6243, 6244, 6245, 6246, 6254, 6255, 6256, 6257, 6258, 6259, 6267, 6268, 6269, 6270, 6271, 6272, 6273, 6274, 6275, 6276, 6277, 6278, 6279, 6280, 6281, 6282, 6283 }; + } + + @Override + public void checkImpact(BattleState state) { + super.checkImpact(state); + + if (getId() == NPCs.IMP_6211) { + if (RandomFunction.roll(IMP_TELEPORT_CHANCE_ON_HIT)) { + getRandomLocAndTeleport(); + } + } + } + + @Override + public void tick() { + super.tick(); + + if (getId() == NPCs.IMP_6211) { + if (RandomFunction.roll(IMP_TELEPORT_CHANCE_ON_TICK)) { + getRandomLocAndTeleport(); + } + } + } + + /** + * Used to teleport imps + */ + private void getRandomLocAndTeleport() { + Location teleportLocation = getPathableRandomLocalCoordinate(this, walkRadius, getProperties().getSpawnLocation(), 3); + + if (getLocation() != teleportLocation) { + sendGraphics(1119, getLocation()); + ContentAPIKt.teleport(this, teleportLocation, TeleportManager.TeleportType.INSTANT); + } + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/GodswordDialogue.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodswordDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/GodswordDialogue.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodswordDialogue.java index 358a37873..d6cc049a0 100644 --- a/Server/src/main/java/core/game/content/dialogue/GodswordDialogue.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodswordDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.asgarnia.trollheim.handlers.gwd; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/activity/gwd/GodwarsBossNPC.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsBossNPC.java similarity index 93% rename from Server/src/main/java/core/game/content/activity/gwd/GodwarsBossNPC.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsBossNPC.java index 99d92a8a9..ad04fa730 100644 --- a/Server/src/main/java/core/game/content/activity/gwd/GodwarsBossNPC.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsBossNPC.java @@ -1,6 +1,6 @@ -package core.game.content.activity.gwd; +package content.region.asgarnia.trollheim.handlers.gwd; -import core.game.content.global.BossKillCounter; +import content.data.BossKillCounter; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -15,9 +15,9 @@ import core.game.world.map.Location; import core.game.world.map.zone.ZoneBorders; import core.plugin.Initializable; import core.tools.RandomFunction; -import rs09.game.node.entity.combat.CombatPulse; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.world.GameWorld; +import core.game.node.entity.combat.CombatPulse; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.world.GameWorld; /** * Handles a god wars boss NPC. @@ -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; @@ -244,12 +244,14 @@ public final class GodwarsBossNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (getId() == 6222 && style == CombatStyle.MELEE && entity instanceof Player) { - ((Player) entity).getPacketDispatch().sendMessage("The aviansie is flying too high for you to attack using melee."); + if(message) { + ((Player) entity).getPacketDispatch().sendMessage("The aviansie is flying too high for you to attack using melee."); + } return false; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } /** diff --git a/Server/src/main/java/core/game/content/activity/gwd/GodwarsEntranceHandler.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java similarity index 89% rename from Server/src/main/java/core/game/content/activity/gwd/GodwarsEntranceHandler.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java index 41534ca4d..8803d9d88 100644 --- a/Server/src/main/java/core/game/content/activity/gwd/GodwarsEntranceHandler.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsEntranceHandler.java @@ -1,8 +1,8 @@ -package core.game.content.activity.gwd; +package content.region.asgarnia.trollheim.handlers.gwd; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -11,12 +11,15 @@ 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 rs09.game.world.GameWorld; +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.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); + 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) == null) { + 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/java/core/game/content/activity/gwd/GodwarsMapzone.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsMapzone.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/gwd/GodwarsMapzone.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsMapzone.java index 4ab906656..b2dd1fd25 100644 --- a/Server/src/main/java/core/game/content/activity/gwd/GodwarsMapzone.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsMapzone.java @@ -1,9 +1,9 @@ -package core.game.content.activity.gwd; +package content.region.asgarnia.trollheim.handlers.gwd; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; import core.game.container.impl.EquipmentContainer; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.MovementPulse; import core.game.interaction.Option; import core.game.node.Node; @@ -16,7 +16,9 @@ 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 rs09.game.world.GameWorld; +import core.tools.Log; +import core.tools.SystemLogger; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.zone.MapZone; @@ -33,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 @@ -85,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); } /** @@ -365,8 +370,11 @@ public final class GodwarsMapzone extends MapZone implements Plugin { player.getPacketDispatch().sendMessage("You need " + required + " " + StringUtils.formatDisplayName(name) + " kills to enter this."); return true; } - increaseKillcount(player, faction, -required); - DoorActionHandler.handleAutowalkDoor(player, object); + + if (DoorActionHandler.handleAutowalkDoor(player, object)) { + 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/java/core/game/content/zone/IcePathZone.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/IcePathZone.java similarity index 96% rename from Server/src/main/java/core/game/content/zone/IcePathZone.java rename to Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/IcePathZone.java index 3d8e1ad20..5f399c7e1 100644 --- a/Server/src/main/java/core/game/content/zone/IcePathZone.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/IcePathZone.java @@ -1,11 +1,11 @@ -package core.game.content.zone; +package content.region.asgarnia.trollheim.handlers.gwd; import core.game.component.Component; import core.game.node.entity.Entity; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBuilder; 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 new file mode 100644 index 000000000..0330f7f7e --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AablaDialogue.kt @@ -0,0 +1,48 @@ +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 core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * @author bushtail + */ + +@Initializable +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 + playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + 0 -> showTopics( + Topic("Can you heal me?", AlKharidHealDialogue(true)), + Topic("Do you see a lot of injured fighters?", 101), + Topic("Do you come here often?", 201) + ) + + 101 -> npcl(FacialExpression.FRIENDLY, "I work here, so yes!").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 + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.AABLA_959) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidHealListener.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidHealListener.kt new file mode 100644 index 000000000..b7c0356df --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidHealListener.kt @@ -0,0 +1,53 @@ +package content.region.desert.alkharid.dialogue + +import core.api.* +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 + +/** + * @author bushtail + */ + +class AlKharidHealListener : InteractionListener { + override fun defineListeners() { + on(getIds(), IntType.NPC, "heal") { player, node -> + openDialogue(player, AlKharidHealDialogue(false), node.asNpc()) + return@on true + } + } + + fun getIds() : IntArray { + return intArrayOf( + NPCs.AABLA_959, + NPCs.SABREEN_960, + NPCs.SURGEON_GENERAL_TAFANI_961, + NPCs.JARAAH_962 + ) + } +} + +class AlKharidHealDialogue(val skipFirst: Boolean) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + if (stage == 0 && skipFirst) stage++ + when(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(FacialExpression.FRIENDLY, "There you go!") + } else { + npcl(FacialExpression.FRIENDLY, "You look healthy to me!") + } + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidShopKeeperDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidShopKeeperDialogue.kt new file mode 100644 index 000000000..803581271 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidShopKeeperDialogue.kt @@ -0,0 +1,48 @@ +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 core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * @author bushtail + * "Henceforth, you shall be called... Craig." + */ + +@Initializable +class AlKharidShopKeeperDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return AlKharidShopKeeperDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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++ } + 101 -> when(buttonId) { + 1 -> { + stage = END_DIALOGUE + npc.openShop(player) + } + 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(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) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AlTheCamelDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/AlTheCamelDialogue.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/city/alkharid/AlTheCamelDialogue.java rename to Server/src/main/content/region/desert/alkharid/dialogue/AlTheCamelDialogue.java index 5f50b90d9..d8e83e252 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AlTheCamelDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AlTheCamelDialogue.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.alkharid; +package content.region.desert.alkharid.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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; 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 new file mode 100644 index 000000000..46a0e020a --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheFarmerDialogue.kt @@ -0,0 +1,64 @@ +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 + +/** + * @author qmqz + */ + +@Initializable +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 + playerl(FacialExpression.FRIENDLY,"Hello there!") + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(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 -> 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() + } + } + return true + } + + override fun getIds(): IntArray { + 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 new file mode 100644 index 000000000..7704e0a30 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheGuard.kt @@ -0,0 +1,64 @@ +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 + +/** + * @author qmqz + */ + +@Initializable +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 + playerl(FacialExpression.FRIENDLY,"Hello there!") + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(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 -> 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() + } + } + return true + } + + override fun getIds(): IntArray { + 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 new file mode 100644 index 000000000..3ff317b3c --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperDialogue.kt @@ -0,0 +1,71 @@ +package content.region.desert.alkharid.dialogue + +import core.api.addItem +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 + +/** + * @author bushtail + * Converts AliTheLeafletDropper.java with a Kotlin implementation + */ + +@Initializable +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(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++ } + 101 -> when(buttonId) { + 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(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(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(FacialExpression.CHILD_FRIENDLY, "Here! Take one and let me get back to work.").also { stage = END_DIALOGUE } + } else { + end() + } + } + } + 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 + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ALI_THE_LEAFLET_DROPPER_3680) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperListener.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperListener.kt new file mode 100644 index 000000000..0f662d917 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperListener.kt @@ -0,0 +1,47 @@ +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 + +/** + * @author bushtail + */ + +class AliTheLeafletDropperListener : InteractionListener { + override fun defineListeners() { + on(NPCs.ALI_THE_LEAFLET_DROPPER_3680, IntType.NPC, "Take-flyer") { player, node -> + if(player.inventory.containItems(Items.AL_KHARID_FLYER_7922)) { + openDialogue(player, DropperDialogue(2), node as NPC) + } else { + if(addItem(player, Items.AL_KHARID_FLYER_7922)) { + openDialogue(player, DropperDialogue(1), node as NPC) + } else { + return@on false + } + } + return@on true + } + } + +} + +class DropperDialogue(val it : Int) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(it) { + 1 -> when(stage) { + 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(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 } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheSmithDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheSmithDialogue.kt new file mode 100644 index 000000000..ff79c3deb --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheSmithDialogue.kt @@ -0,0 +1,67 @@ +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 + +/** + * @author qmqz + */ + +@Initializable +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 + playerl(FacialExpression.FRIENDLY,"Hello there!") + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(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 -> 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 ->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 ->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 -> 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 -> 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() + } + + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ALI_THE_SMITH_2820) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/BorderGuardDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/BorderGuardDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/BorderGuardDialogue.java rename to Server/src/main/content/region/desert/alkharid/dialogue/BorderGuardDialogue.java index 3a15338f1..4331b9370 100644 --- a/Server/src/main/java/core/game/content/dialogue/BorderGuardDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/BorderGuardDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.dialogue; +package content.region.desert.alkharid.dialogue; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialoguePlugin; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -8,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. @@ -79,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.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/java/core/game/content/dialogue/EllyTheCamelDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/EllyTheCamelDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/EllyTheCamelDialogue.java rename to Server/src/main/content/region/desert/alkharid/dialogue/EllyTheCamelDialogue.java index c5f381b3b..747a6c57f 100644 --- a/Server/src/main/java/core/game/content/dialogue/EllyTheCamelDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/EllyTheCamelDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/FadliDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/FadliDialogue.kt new file mode 100644 index 000000000..c82704891 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/FadliDialogue.kt @@ -0,0 +1,72 @@ +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 core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * @author bushtail + */ + +@Initializable +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(FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + 0 -> npcl(FacialExpression.HALF_ASKING, "What?").also { stage++ } + 1 -> showTopics( + Topic("What do you do?", 101), + Topic("What is this place?", 201), + Topic(FacialExpression.FRIENDLY,"I'd like to store some items, please.", 301), + Topic("Do you watch any matches?", 401) + ) + + 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(FacialExpression.LAUGH, "Isn't it obvious?").also { stage++ } + 202 -> npcl(FacialExpression.ANNOYED, "This is the Duel Arena...duh!").also { stage = END_DIALOGUE } + + 301 -> npcl(FacialExpression.FRIENDLY, "Sure.").also { + end() + player.bank.open() + } + + 401 -> npcl(FacialExpression.LAUGH, "Most aren't any good so I throw rotten fruit at them!").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FADLI_958) + } +} + +class FadliListener : InteractionListener { + override fun defineListeners() { + on(NPCs.FADLI_958, IntType.NPC, "buy") { player, node -> + if(node.asNpc().openShop(player)) { + return@on true + } + return@on false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt new file mode 100644 index 000000000..cd6bd5d5e --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt @@ -0,0 +1,83 @@ +package content.region.desert.alkharid.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 org.rs09.consts.NPCs +import content.data.Quests +import core.api.getQuestStage +import core.api.setQuestStage + +/** + * Represents the gem trader Dialogue plugin + * @author plex + * @version 2.0 + */ + +@Initializable +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) + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 1 -> options("Yes, please.", "No, thank you.", "I'm in search of a man named Avan Fitzharmon").also{ + stage = 10 + } + + 2 -> options("Yes, please.", "No, thank you.").also{ + stage = 20 + } + + 10 -> when(buttonId){ + 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} + } + + 100 -> npc("You know, I HAVE seen someone of that" , + " persuasion around here recently... " , + "wearing a 'poncey' yellow cape, he was.").also{stage++} + + 101 -> npc("Came in here all la-di-dah, high and mighty,", + "asking for jewellery made from 'perfect gold' - " , + "whatever that is - like 'normal' gold just isn't " , + "good enough for 'little lord fancy pants' there!").also{stage ++} + + 102 -> npc("I told him to head to the desert 'cos " , + "I know there's gold out there, in them there sand dunes. " , + "And if it's not up to his lordship's " , + "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 + setQuestStage(player, Quests.FAMILY_CREST, 13) + } + + 1000 -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GEM_TRADER_540) + } + + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/HassanDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/HassanDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/HassanDialogue.java rename to Server/src/main/content/region/desert/alkharid/dialogue/HassanDialogue.java index 6b6a08dea..7c711edab 100644 --- a/Server/src/main/java/core/game/content/dialogue/HassanDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/HassanDialogue.java @@ -1,11 +1,14 @@ -package core.game.content.dialogue; +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.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. @@ -50,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 new file mode 100644 index 000000000..d114990c4 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/JaraahDialogue.kt @@ -0,0 +1,66 @@ +package content.region.desert.alkharid.dialogue + +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 + +/** + * @author bushtail + */ + +@Initializable +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(FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(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), + Topic("Why do they call you 'The Butcher'?", 301) + ) + + // Jaraah has to be special and have different dialogue for the heal listener so I am repeating it here. + 101 -> { + animate(npc!!, Animations.HUMAN_PICKPOCKETING_881) + if(player!!.skills.lifepoints < getStatLevel(player!!, Skills.HITPOINTS)) { + player!!.skills.heal(21) + npcl(FacialExpression.FRIENDLY, "There you go!") + } else { + npcl(FacialExpression.FRIENDLY, "Okay, this will hurt you more than it will me.") + } + 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(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 + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.JARAAH_962) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/KarimDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/KarimDialogue.java similarity index 86% rename from Server/src/main/java/core/game/content/dialogue/KarimDialogue.java rename to Server/src/main/content/region/desert/alkharid/dialogue/KarimDialogue.java index e796c679a..41c010f1a 100644 --- a/Server/src/main/java/core/game/content/dialogue/KarimDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/KarimDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -48,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; } @@ -67,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 new file mode 100644 index 000000000..f20999304 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/LouiLegsDialogue.java @@ -0,0 +1,86 @@ +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; + +/** + * Represents the plugin used for loui legs. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class LouiLegsDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code LouiLegsDialogue} {@code Object}. + */ + public LouiLegsDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code LouiLegsDialogue} {@code Object}. + * @param player the player. + */ + public LouiLegsDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new LouiLegsDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hey, wanna buy some armour?"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "What have you got?", "No, thank you."); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.THINKING, "What have you got?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No, thank you."); + stage = 20; + break; + + } + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "I provide items to help you keep your legs!"); + stage = 11; + break; + case 11: + end(); + npc.openShop(player); + break; + case 20: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 542 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/OllieTheCamelDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/OllieTheCamelDialogue.java similarity index 88% rename from Server/src/main/java/core/game/content/dialogue/OllieTheCamelDialogue.java rename to Server/src/main/content/region/desert/alkharid/dialogue/OllieTheCamelDialogue.java index a480f139f..bfff7de4a 100644 --- a/Server/src/main/java/core/game/content/dialogue/OllieTheCamelDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/OllieTheCamelDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/RanaelDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/RanaelDialogue.java similarity index 80% rename from Server/src/main/java/core/game/content/dialogue/RanaelDialogue.java rename to Server/src/main/content/region/desert/alkharid/dialogue/RanaelDialogue.java index bbf86648c..cb52690ce 100644 --- a/Server/src/main/java/core/game/content/dialogue/RanaelDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/RanaelDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -60,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 new file mode 100644 index 000000000..7bfeb1651 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/SabreenDialogue.kt @@ -0,0 +1,48 @@ +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 core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +/** + * @author bushtail + */ + +@Initializable +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(FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + 0 -> showTopics( + Topic("Can you heal me?", AlKharidHealDialogue(true)), + Topic("Do you see a lot of injured fighters?", 101), + Topic("Do you come here often?", 201) + ) + + 101 -> npcl(FacialExpression.FRIENDLY, "I work here, so yes!").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 + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SABREEN_960) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/SurgeonGeneralTafaniDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/SurgeonGeneralTafaniDialogue.kt new file mode 100644 index 000000000..eb6428519 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/SurgeonGeneralTafaniDialogue.kt @@ -0,0 +1,70 @@ +package content.region.desert.alkharid.dialogue + +import core.api.hasLevelStat +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic +import core.game.global.Skillcape +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.NPCs + +/** + * @author bushtail + */ + +@Initializable +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(FacialExpression.FRIENDLY, "Hi. How can I help?").also { stage = 100 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + 100 -> showTopics( + Topic("Can you heal me?", AlKharidHealDialogue(true)), + Topic("Do you see a lot of injured fighters?", 201), + Topic("Do you come here often?", 301), + IfTopic("Can I buy a Skillcape of Hitpoints from you?", 401, hasLevelStat(player, Skills.HITPOINTS, 99)), + IfTopic("Where can I get a cape like yours?", 501, !hasLevelStat(player, Skills.HITPOINTS, 99)) + ) + + 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(FacialExpression.LAUGH, "I work here, so yes!").also { stage = END_DIALOGUE } + + 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(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(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(FacialExpression.HALF_GUILTY, "Sorry, you can't buy that right now.").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 + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SURGEON_GENERAL_TAFANI_961) + } +} + diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/ZekeDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/ZekeDialogue.java new file mode 100644 index 000000000..0ffb354f0 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/ZekeDialogue.java @@ -0,0 +1,137 @@ +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; + +/** + * Represents the dialogue plugin used for zeke. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class ZekeDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code ZekeDialogue} {@code Object}. + */ + public ZekeDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code ZekeDialogue} {@code Object}. + * @param player the player. + */ + public ZekeDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new ZekeDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "A thousand greetings, sir."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "Do you want to trade?", "Nice cloak.", "Could you sell me a dragon scimitar?"); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Do you want to trade?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Nice cloak."); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Could you sell me a dragon scimitar?"); + stage = 30; + break; + } + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Yes, certainly. I deal in scimitars."); + stage = 11; + break; + case 11: + npc.openShop(player); + end(); + break; + case 20: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Thank you."); + stage = 21; + break; + case 21: + end(); + break; + case 30: + interpreter.sendDialogues(npc, FacialExpression.EXTREMELY_SHOCKED, "A dragon scimitar? A DRAGON scimitar?"); + stage = 31; + break; + case 31: + interpreter.sendDialogues(npc, FacialExpression.EXTREMELY_SHOCKED, "No way, man!"); + stage = 32; + break; + case 32: + 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.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.SUSPICIOUS, "Hmmm, funny you should say that..."); + stage = 35; + break; + case 35: + interpreter.sendDialogues(npc, FacialExpression.ASKING, "Perhaps you'd like to take a look at my stock?"); + stage = 36; + break; + case 36: + interpreter.sendOptions("Select an Option", "Yes, please, Zeke.", "Not today, thank you."); + stage = 37; + break; + case 37: + switch (buttonId) { + case 1: + npc.openShop(player); + end(); + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No today, thank you."); + stage = 2000; + break; + } + break; + case 2000: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 541 }; + } + +} diff --git a/Server/src/main/kotlin/rs09/game/content/zone/AlKharidStairsPlugin.kt b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidStairsPlugin.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/content/zone/AlKharidStairsPlugin.kt rename to Server/src/main/content/region/desert/alkharid/handlers/AlKharidStairsPlugin.kt index 4c7525f09..cec65c1a5 100644 --- a/Server/src/main/kotlin/rs09/game/content/zone/AlKharidStairsPlugin.kt +++ b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidStairsPlugin.kt @@ -1,8 +1,8 @@ -package rs09.game.content.zone; +package content.region.desert.alkharid.handlers -import api.ContentAPI +import core.api.* import core.cache.def.impl.SceneryDefinition -import core.game.content.global.action.DoorActionHandler +import core.game.global.action.DoorActionHandler import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.player.Player @@ -28,7 +28,7 @@ class AlKharidStairsPlugin : OptionHandler() { node ?: return false option ?: return false if(node.location == zekeDoorOpened.location || node.location == craftingDoorOpened.location){ - ContentAPI.sendMessage(player,"This door appears to be stuck open.") + sendMessage(player,"This door appears to be stuck open.") } else{ DoorActionHandler.handleDoor(player,node.asScenery()) } diff --git a/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt new file mode 100644 index 000000000..ba1b4662d --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt @@ -0,0 +1,73 @@ +package content.region.desert.alkharid.handlers + +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 org.rs09.consts.NPCs + +/** + * Handles the Al-Kharid Warrior + * @author Ceikry + */ +@Initializable +class AlKharidWarriorNPC : AbstractNPC { + var target: Player? = null + private val supportRange: Int = 5 + + //Constructor spaghetti because Arios I guess + constructor() : super(18, null, true) {} + private constructor(id: Int, location: Location) : super(id, location) {} + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return AlKharidWarriorNPC(id, location) + } + + //Maintains target combat (resolves issue where they would just give up/walk away) + override fun tick() { + if(target != null && !inCombat() && skills.lifepoints > 0){ + attack(target) + } + super.tick() + } + + //Clear target on death + override fun finalizeDeath(killer: Entity?) { + 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 + } + + companion object { + /** + * The NPC ids of NPCs using this plugin. + */ + private val ID = intArrayOf(NPCs.AL_KHARID_WARRIOR_18) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/princealirescue/LadyKeliDialogue.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/free/princealirescue/LadyKeliDialogue.java rename to Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java index 0284cc9a6..038567099 100644 --- a/Server/src/main/java/core/game/content/quest/free/princealirescue/LadyKeliDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliDialogue.java @@ -1,15 +1,16 @@ -package core.game.content.quest.free.princealirescue; +package content.region.desert.alkharid.quest.princealirescue; import java.util.List; -import core.game.content.dialogue.DialoguePlugin; +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.GroundItemManager; import core.game.node.item.Item; -import rs09.game.world.GameWorld; +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/java/core/game/content/quest/free/princealirescue/LadyKeliNPC.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliNPC.java similarity index 91% rename from Server/src/main/java/core/game/content/quest/free/princealirescue/LadyKeliNPC.java rename to Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliNPC.java index 47f0cb6a0..2e9c31fe6 100644 --- a/Server/src/main/java/core/game/content/quest/free/princealirescue/LadyKeliNPC.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/LadyKeliNPC.java @@ -1,8 +1,8 @@ -package core.game.content.quest.free.princealirescue; +package content.region.desert.alkharid.quest.princealirescue; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; /** diff --git a/Server/src/main/kotlin/rs09/game/content/quest/free/princealirescue/NedPARDialogue.kt b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/NedPARDialogue.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/content/quest/free/princealirescue/NedPARDialogue.kt rename to Server/src/main/content/region/desert/alkharid/quest/princealirescue/NedPARDialogue.kt index 2455eda3d..3994545f6 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/princealirescue/NedPARDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/NedPARDialogue.kt @@ -1,15 +1,14 @@ -package rs09.game.content.quest.free.princealirescue +package content.region.desert.alkharid.quest.princealirescue +import core.game.dialogue.DialogueFile import core.game.node.item.GroundItemManager import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE private const val STAGE_KNIT_SWEATER = 10 private const val STAGE_KNIT_WIG = 20 private const val STAGE_REPAIR_HOLES = 30 - class NedPARDialogue(val questStage: Int) : DialogueFile() { private val WIG_WOOL = Item(1759, 3) @@ -20,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){ @@ -98,4 +97,4 @@ class NedPARDialogue(val questStage: Int) : DialogueFile() { } } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/content/quest/free/princealirescue/PrinceAliRescue.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescue.java similarity index 84% rename from Server/src/main/java/core/game/content/quest/free/princealirescue/PrinceAliRescue.java rename to Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescue.java index c161997c0..1c37aebab 100644 --- a/Server/src/main/java/core/game/content/quest/free/princealirescue/PrinceAliRescue.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescue.java @@ -1,11 +1,12 @@ -package core.game.content.quest.free.princealirescue; +package content.region.desert.alkharid.quest.princealirescue; 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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the prince ali rescue quest. @@ -44,12 +45,12 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugins(new LadyKeliDialogue(), new LadyKeliNPC(), new PrinceAliRescuePlugin(), new WigDyePlugin()); + ClassScanner.definePlugins(new LadyKeliDialogue(), new LadyKeliNPC(), new PrinceAliRescuePlugin(), new WigDyePlugin()); return this; } @@ -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/java/core/game/content/quest/free/princealirescue/PrinceAliRescuePlugin.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescuePlugin.java similarity index 91% rename from Server/src/main/java/core/game/content/quest/free/princealirescue/PrinceAliRescuePlugin.java rename to Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescuePlugin.java index 146b02a1a..3f38c3489 100644 --- a/Server/src/main/java/core/game/content/quest/free/princealirescue/PrinceAliRescuePlugin.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/PrinceAliRescuePlugin.java @@ -1,18 +1,19 @@ -package core.game.content.quest.free.princealirescue; +package content.region.desert.alkharid.quest.princealirescue; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +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.quest.Quest; import core.game.node.scenery.Scenery; -import rs09.game.world.GameWorld; +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/java/core/game/content/quest/free/princealirescue/WigDyePlugin.java b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/WigDyePlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/free/princealirescue/WigDyePlugin.java rename to Server/src/main/content/region/desert/alkharid/quest/princealirescue/WigDyePlugin.java index 5cf2bebfe..4d61582cd 100644 --- a/Server/src/main/java/core/game/content/quest/free/princealirescue/WigDyePlugin.java +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/WigDyePlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.princealirescue; +package content.region.desert.alkharid.quest.princealirescue; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; 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/java/core/game/interaction/city/BedabinPlugin.java b/Server/src/main/content/region/desert/bedabin/handlers/BedabinPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/city/BedabinPlugin.java rename to Server/src/main/content/region/desert/bedabin/handlers/BedabinPlugin.java index 22b9eb696..a0a2870a9 100644 --- a/Server/src/main/java/core/game/interaction/city/BedabinPlugin.java +++ b/Server/src/main/content/region/desert/bedabin/handlers/BedabinPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.city; +package content.region.desert.bedabin.handlers; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; diff --git a/Server/src/main/java/core/game/content/dialogue/CamelDialoguePlugin.java b/Server/src/main/content/region/desert/dialogue/CamelDialoguePlugin.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/CamelDialoguePlugin.java rename to Server/src/main/content/region/desert/dialogue/CamelDialoguePlugin.java index f3b5fc846..e0b4e027f 100644 --- a/Server/src/main/java/core/game/content/dialogue/CamelDialoguePlugin.java +++ b/Server/src/main/content/region/desert/dialogue/CamelDialoguePlugin.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.desert.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; diff --git a/Server/src/main/java/core/game/content/dialogue/RugMerchantDialogue.java b/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java similarity index 88% rename from Server/src/main/java/core/game/content/dialogue/RugMerchantDialogue.java rename to Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java index 23ce43b07..a4a3c626a 100644 --- a/Server/src/main/java/core/game/content/dialogue/RugMerchantDialogue.java +++ b/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java @@ -1,11 +1,14 @@ -package core.game.content.dialogue; +package content.region.desert.dialogue; + +import core.api.Container; +import static core.api.ContentAPIKt.*; +import static core.tools.GlobalsKt.colorize; -import api.Container; -import api.ContentAPI; import core.cache.def.impl.NPCDefinition; import core.game.container.impl.EquipmentContainer; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; @@ -13,14 +16,14 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.system.task.Pulse; import kotlin.Unit; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import org.rs09.consts.Items; - -import static rs09.tools.stringtools.GlobalsKt.colorize; +import org.rs09.consts.Sounds; +import content.data.Quests; /** @@ -65,7 +68,7 @@ public final class RugMerchantDialogue extends DialoguePlugin { @Override public void init() { super.init(); - PluginManager.definePlugin(new RugMerchantPlugin()); + ClassScanner.definePlugin(new RugMerchantPlugin()); } /** @@ -154,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 { @@ -162,7 +172,6 @@ public final class RugMerchantDialogue extends DialoguePlugin { destination.travel(current, player); } } - end(); break; case 20: end(); @@ -282,14 +291,16 @@ 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().hideTabs(0,1,2,3,4,5,6,7,8,9,10,11,12,13); + 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); - ContentAPI.registerLogoutListener(player, "magic-carpet", (pl) -> { - ContentAPI.removeItem(pl, Items.MAGIC_CARPET_5614, Container.EQUIPMENT); + registerLogoutListener(player, "magic-carpet", (pl) -> { + removeItem(pl, Items.MAGIC_CARPET_5614, Container.EQUIPMENT); return Unit.INSTANCE; }); @@ -320,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; @@ -331,9 +342,10 @@ 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); - ContentAPI.clearLogoutListener(player, "magic-carpet"); + clearLogoutListener(player, "magic-carpet"); break; case 902: diff --git a/Server/src/main/content/region/desert/dialogue/ShantayDialogue.java b/Server/src/main/content/region/desert/dialogue/ShantayDialogue.java new file mode 100644 index 000000000..fcbf3077f --- /dev/null +++ b/Server/src/main/content/region/desert/dialogue/ShantayDialogue.java @@ -0,0 +1,296 @@ +package content.region.desert.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.system.task.Pulse; +import core.game.world.GameWorld; +import core.plugin.Initializable; +import core.game.world.map.Location; + +/** + * Represents the dialogue plugin used for shantay. + * @author Vexia + * @version 1.0 + */ +@Initializable +public final class ShantayDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code ShantayDialogue} {@code Object}. + */ + public ShantayDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code ShantayDialogue} {@code Object}. + * @param player the player. + */ + public ShantayDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new ShantayDialogue(player); + } + + @Override + public boolean open(Object... args) { + if (args[0] != null && args[0] instanceof NPC) { + npc = (NPC) args[0]; + } + if (args.length == 2) { + player.getPacketDispatch().sendMessage("Shantay saunters over to talk with you."); + 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.NEUTRAL, "Hello effendi, I am Shantay."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + 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.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: + interpreter.sendOptions("Select an Option", "What is this place?", "Can I see what you have to sell please?", "I must be going.", "I want to buy a Shantay pass for 5 gold coins."); + stage = 3; + break; + case 3: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "What is this place?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Can I see what you have to sell please?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I must be going."); + stage = 30; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I want to buy a Shantay pass for 5 gold coins."); + stage = 40; + break; + + } + break; + case 40: + if (!player.getInventory().contains(995, 5)) { + end(); + return true; + } + if (player.getInventory().remove(new Item(995, 5))) { + player.getInventory().add(new Item(1854)); + interpreter.sendItemMessage(1854, "You purchase a Shantay Pass."); + } else { + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Sorry, I don't seem to have enough money."); + } + stage = 41; + break; + case 41: + end(); + break; + case 30: + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "So long..."); + stage = 31; + break; + case 31: + end(); + break; + case 10: + 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.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: + interpreter.sendOptions("Select an Option", "I am definitely an outlaw, prepare to die!", "I am a little inexperienced.", "Er, neither, I'm an adventurer."); + stage = 13; + break; + case 13: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I am definitely an outlaw, prepare to die."); + stage = 700; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I am a little inexperienced."); + stage = 710; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Er, neither, I'm an adventurer."); + stage = 720; + break; + } + break; + case 20: + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Absolutely Effendi!"); + stage = 21; + break; + case 21: + end(); + npc.openShop(player); + break; + case 700: + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Ha, very funny....."); + stage = 701; + break; + case 701: + interpreter.sendDialogues(836, FacialExpression.NEUTRAL, "Guards arrest him!"); + stage = 702; + break; + case 702: + player.getPacketDispatch().sendMessage("The guards arrest you and place you in the jail."); + close(); + player.lock(10); + GameWorld.getPulser().submit(new Pulse(3, player) { + @Override + public boolean pulse() { + player.setAttribute("/save:shantay-jail", true); + player.getProperties().setTeleportLocation(Location.create(3298, 3123, 0)); + 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; + } + }); + break; + case 703: + interpreter.sendOptions("Select an Option", "Yes, okay.", "No thanks, you're not having my money."); + stage = 704; + break; + case 704: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Yes, okay."); + stage = 705; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No thanks, you're not having my money."); + stage = 800; + break; + } + break; + case 705: + 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, FacialExpression.NEUTRAL, "Great, Effendi, now please try to keep the peace."); + player.removeAttribute("shantay-jail"); + stage = 822; + } else { + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Sorry, I don't seem to have enough money."); + stage = 825; + } + break; + case 800: + 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, FacialExpression.NEUTRAL, "Will you pay the five gold pieces?"); + stage = 802; + break; + case 802: + interpreter.sendOptions("Select an Option", "Yes, okay.", "No, do your worst!"); + stage = 803; + break; + case 803: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Yes, okay."); + stage = 820; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, do your worst!"); + stage = 804; + break; + } + break; + case 804: + 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: + player.getPacketDispatch().sendMessage("You find yourself in a prison."); + player.removeAttribute("shantay-jail"); + player.getProperties().setTeleportLocation(Location.create(3018, 3188, 0)); + end(); + break; + case 820: + 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, FacialExpression.NEUTRAL, "Great, Effendi, now please try to keep the peace."); + stage = 822; + } else { + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Sorry, I don't seem to have enough money."); + stage = 825; + } + break; + case 822: + end(); + break; + case 825: + end(); + break; + case 710: + 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, 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, 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, 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, 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: + end(); + break; + case 730: + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 836 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/ShantayGuard.java b/Server/src/main/content/region/desert/dialogue/ShantayGuard.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/ShantayGuard.java rename to Server/src/main/content/region/desert/dialogue/ShantayGuard.java index 1e35e76ab..f4d631071 100644 --- a/Server/src/main/java/core/game/content/dialogue/ShantayGuard.java +++ b/Server/src/main/content/region/desert/dialogue/ShantayGuard.java @@ -1,15 +1,17 @@ -package core.game.content.dialogue; +package content.region.desert.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.path.Pathfinder; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; /** * Represents the dialogue used for a shantay guard. diff --git a/Server/src/main/java/core/game/content/dialogue/SimonTempleton.java b/Server/src/main/content/region/desert/dialogue/SimonTempleton.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/SimonTempleton.java rename to Server/src/main/content/region/desert/dialogue/SimonTempleton.java index 47ea1a4c4..fe695172a 100644 --- a/Server/src/main/java/core/game/content/dialogue/SimonTempleton.java +++ b/Server/src/main/content/region/desert/dialogue/SimonTempleton.java @@ -1,16 +1,22 @@ -package core.game.content.dialogue; +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 * @author ceik */ @Initializable -public final class SimonTempleton extends DialoguePlugin{ +public final class SimonTempleton extends DialoguePlugin { public final Item[][] ARTIFACTS = { {new Item(9032),new Item(9036), new Item(9026)}, {new Item(9042), new Item(9030), new Item(9038)}, {new Item(9040), new Item(9028), new Item(9034)} }; public SimonTempleton(){ /** @@ -38,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; } @@ -46,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++){ @@ -91,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: @@ -101,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/java/core/game/content/zone/DesertZone.java b/Server/src/main/content/region/desert/handlers/DesertZone.java similarity index 90% rename from Server/src/main/java/core/game/content/zone/DesertZone.java rename to Server/src/main/content/region/desert/handlers/DesertZone.java index 6e0f6b1de..3110c7233 100644 --- a/Server/src/main/java/core/game/content/zone/DesertZone.java +++ b/Server/src/main/content/region/desert/handlers/DesertZone.java @@ -1,16 +1,15 @@ -package core.game.content.zone; +package content.region.desert.handlers; import java.util.ArrayList; import java.util.List; import core.cache.def.impl.ItemDefinition; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; import core.game.node.entity.Entity; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBuilder; @@ -18,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. @@ -36,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. @@ -55,7 +58,7 @@ public final class DesertZone extends MapZone implements Plugin { @Override public boolean pulse() { for (Player player : PLAYERS) { - if (player.getInterfaceManager().isOpened() || player.getInterfaceManager().hasChatbox() || player.getLocks().isMovementLocked()) { + if (!player.getAttribute("tutorial:complete", false) || player.getInterfaceManager().isOpened() || player.getInterfaceManager().hasChatbox() || player.getLocks().isMovementLocked()) { continue; } if (player.getAttribute("desert-delay", -1) < GameWorld.getTicks()) { @@ -76,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."); } @@ -105,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; } } @@ -178,7 +182,7 @@ public final class DesertZone extends MapZone implements Plugin { public boolean enter(Entity e) { if (e instanceof Player) { Player p = (Player) e; - if (TutorialSession.getExtension(p).getStage() < TutorialSession.MAX_STAGE) { + if (p.getAttribute("tutorial:stage", 0) > 71) { return true; } p.setAttribute("desert-delay", GameWorld.getTicks() + getDelay(p)); diff --git a/Server/src/main/java/core/game/interaction/object/KalphiteEntranceHandler.java b/Server/src/main/content/region/desert/handlers/KalphiteEntranceHandler.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/KalphiteEntranceHandler.java rename to Server/src/main/content/region/desert/handlers/KalphiteEntranceHandler.java index 15b7c22cd..0172a0622 100644 --- a/Server/src/main/java/core/game/interaction/object/KalphiteEntranceHandler.java +++ b/Server/src/main/content/region/desert/handlers/KalphiteEntranceHandler.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.desert.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.NodeUsageEvent; @@ -9,7 +9,7 @@ import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; 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/java/core/game/node/entity/npc/bosses/KalphiteQueenNPC.java b/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/bosses/KalphiteQueenNPC.java rename to Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java index e94be4859..39dbc9399 100644 --- a/Server/src/main/java/core/game/node/entity/npc/bosses/KalphiteQueenNPC.java +++ b/Server/src/main/content/region/desert/handlers/KalphiteQueenNPC.java @@ -1,13 +1,13 @@ -package core.game.node.entity.npc.bosses; +package content.region.desert.handlers; import java.util.ArrayList; import java.util.List; -import core.game.content.global.BossKillCounter; +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 rs09.game.node.entity.combat.CombatSwingHandler; +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; @@ -16,7 +16,7 @@ import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; @@ -25,7 +25,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; -import core.game.interaction.object.dmc.DMCHandler; +import content.region.kandarin.quest.dwarfcannon.dmc.DMCHandler; /** * Handles the Kalphite Queen. @@ -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); @@ -212,7 +212,7 @@ public final class KalphiteQueenNPC extends AbstractNPC { private BattleState[] getRangeTargets(Entity e, Entity victim) { List list = new ArrayList<>(20); for (Entity t : RegionManager.getLocalPlayers(victim, -1 + (int) e.getCenterLocation().getDistance(victim.getLocation()))) { - if (t.isAttackable(e, CombatStyle.RANGE)) { + if (t.isAttackable(e, CombatStyle.RANGE, false)) { list.add(new BattleState(e, t)); } } @@ -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/java/core/game/interaction/object/KhardianInteractionPlugin.java b/Server/src/main/content/region/desert/handlers/KhardianInteractionPlugin.java similarity index 98% rename from Server/src/main/java/core/game/interaction/object/KhardianInteractionPlugin.java rename to Server/src/main/content/region/desert/handlers/KhardianInteractionPlugin.java index 86562b787..c2dbc4200 100644 --- a/Server/src/main/java/core/game/interaction/object/KhardianInteractionPlugin.java +++ b/Server/src/main/content/region/desert/handlers/KhardianInteractionPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.desert.handlers; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/interaction/npc/ShantayOptionPlugin.java b/Server/src/main/content/region/desert/handlers/ShantayOptionPlugin.java similarity index 91% rename from Server/src/main/java/core/game/interaction/npc/ShantayOptionPlugin.java rename to Server/src/main/content/region/desert/handlers/ShantayOptionPlugin.java index 7287e50bf..cfda8287c 100644 --- a/Server/src/main/java/core/game/interaction/npc/ShantayOptionPlugin.java +++ b/Server/src/main/content/region/desert/handlers/ShantayOptionPlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.npc; +package content.region.desert.handlers; import core.cache.def.impl.NPCDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; +import core.game.dialogue.DialogueInterpreter; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/interaction/object/ShantayPassPlugin.java b/Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java similarity index 83% rename from Server/src/main/java/core/game/interaction/object/ShantayPassPlugin.java rename to Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java index e308a0fd2..74ae051e1 100644 --- a/Server/src/main/java/core/game/interaction/object/ShantayPassPlugin.java +++ b/Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java @@ -1,12 +1,14 @@ -package core.game.interaction.object; +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.content.global.action.DoorActionHandler; -import core.game.node.entity.skill.agility.AgilityHandler; +import core.game.dialogue.FacialExpression; +import core.game.global.action.DoorActionHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; @@ -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/java/core/game/interaction/object/TollGateOptionPlugin.java b/Server/src/main/content/region/desert/handlers/TollGateOptionPlugin.java similarity index 80% rename from Server/src/main/java/core/game/interaction/object/TollGateOptionPlugin.java rename to Server/src/main/content/region/desert/handlers/TollGateOptionPlugin.java index a3fd99706..2c9f338a9 100644 --- a/Server/src/main/java/core/game/interaction/object/TollGateOptionPlugin.java +++ b/Server/src/main/content/region/desert/handlers/TollGateOptionPlugin.java @@ -1,18 +1,19 @@ -package core.game.interaction.object; +package content.region.desert.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +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.item.Item; import core.game.node.scenery.Scenery; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; import core.plugin.Initializable; import core.plugin.Plugin; -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_ALKHARID_GATE; -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_BASE; +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/java/core/game/node/entity/npc/city/nardah/AliTheCarter.java b/Server/src/main/content/region/desert/nardah/dialogue/AliTheCarter.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/city/nardah/AliTheCarter.java rename to Server/src/main/content/region/desert/nardah/dialogue/AliTheCarter.java index d0dca16ae..aabc56ca1 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/nardah/AliTheCarter.java +++ b/Server/src/main/content/region/desert/nardah/dialogue/AliTheCarter.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.nardah; +package content.region.desert.nardah.dialogue; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/nardah/ArtimeusDialogue.java b/Server/src/main/content/region/desert/nardah/dialogue/ArtimeusDialogue.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/city/nardah/ArtimeusDialogue.java rename to Server/src/main/content/region/desert/nardah/dialogue/ArtimeusDialogue.java index 9ec0867d7..94ee6643f 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/nardah/ArtimeusDialogue.java +++ b/Server/src/main/content/region/desert/nardah/dialogue/ArtimeusDialogue.java @@ -1,6 +1,6 @@ -package core.game.node.entity.npc.city.nardah; +package content.region.desert.nardah.dialogue; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Awusah.java b/Server/src/main/content/region/desert/nardah/dialogue/Awusah.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/city/nardah/Awusah.java rename to Server/src/main/content/region/desert/nardah/dialogue/Awusah.java index 91611802d..2d2f4717d 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Awusah.java +++ b/Server/src/main/content/region/desert/nardah/dialogue/Awusah.java @@ -1,6 +1,6 @@ -package core.game.node.entity.npc.city.nardah; +package content.region.desert.nardah.dialogue; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Ghaslor.java b/Server/src/main/content/region/desert/nardah/dialogue/Ghaslor.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/city/nardah/Ghaslor.java rename to Server/src/main/content/region/desert/nardah/dialogue/Ghaslor.java index 4c6fec5d2..c8f1601a2 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Ghaslor.java +++ b/Server/src/main/content/region/desert/nardah/dialogue/Ghaslor.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.nardah; +package content.region.desert.nardah.dialogue; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Kazemde.java b/Server/src/main/content/region/desert/nardah/dialogue/Kazemde.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/city/nardah/Kazemde.java rename to Server/src/main/content/region/desert/nardah/dialogue/Kazemde.java index 0429925dd..bd293d43c 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Kazemde.java +++ b/Server/src/main/content/region/desert/nardah/dialogue/Kazemde.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.nardah; +package content.region.desert.nardah.dialogue; import core.cache.def.impl.NPCDefinition; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/content/region/desert/nardah/dialogue/Nkuku.kt b/Server/src/main/content/region/desert/nardah/dialogue/Nkuku.kt new file mode 100644 index 000000000..11ceafab5 --- /dev/null +++ b/Server/src/main/content/region/desert/nardah/dialogue/Nkuku.kt @@ -0,0 +1,41 @@ +package content.region.desert.nardah.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 Nkuku(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Good day to you.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + npc(FacialExpression.FRIENDLY, "May Saradomin be with you.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return Nkuku(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.NKUKU_3032) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Rokuh.java b/Server/src/main/content/region/desert/nardah/dialogue/Rokuh.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/npc/city/nardah/Rokuh.java rename to Server/src/main/content/region/desert/nardah/dialogue/Rokuh.java index b2eedb851..b7e4e4936 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Rokuh.java +++ b/Server/src/main/content/region/desert/nardah/dialogue/Rokuh.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.nardah; +package content.region.desert.nardah.dialogue; import core.cache.def.impl.NPCDefinition; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Seddu.java b/Server/src/main/content/region/desert/nardah/dialogue/Seddu.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/npc/city/nardah/Seddu.java rename to Server/src/main/content/region/desert/nardah/dialogue/Seddu.java index 789027727..977d4a2f9 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/nardah/Seddu.java +++ b/Server/src/main/content/region/desert/nardah/dialogue/Seddu.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.nardah; +package content.region.desert.nardah.dialogue; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.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/java/core/game/node/entity/npc/city/pollnivneach/AliCamelMan.java b/Server/src/main/content/region/desert/pollniv/dialogue/AliCamelMan.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/AliCamelMan.java rename to Server/src/main/content/region/desert/pollniv/dialogue/AliCamelMan.java index a17a270cb..d7e15062b 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/AliCamelMan.java +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliCamelMan.java @@ -1,8 +1,8 @@ -package core.game.node.entity.npc.city.pollnivneach; +package content.region.desert.pollniv.dialogue; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/AliTheCamelHandler.java b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheCamelHandler.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/AliTheCamelHandler.java rename to Server/src/main/content/region/desert/pollniv/dialogue/AliTheCamelHandler.java index f8a43b7cf..653789938 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/AliTheCamelHandler.java +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheCamelHandler.java @@ -1,15 +1,15 @@ -package core.game.node.entity.npc.city.pollnivneach; +package content.region.desert.pollniv.dialogue; import core.cache.def.impl.NPCDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.tools.RandomFunction; diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheHagDialogue.kt b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheHagDialogue.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheHagDialogue.kt rename to Server/src/main/content/region/desert/pollniv/dialogue/AliTheHagDialogue.kt index 9076eb97a..24bafe58e 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheHagDialogue.kt +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheHagDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.node.entity.npc.city.pollnivneach +package content.region.desert.pollniv.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/AliTheKebabSeller.java b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheKebabSeller.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/AliTheKebabSeller.java rename to Server/src/main/content/region/desert/pollniv/dialogue/AliTheKebabSeller.java index 29de8fad9..5e1fe2989 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/AliTheKebabSeller.java +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheKebabSeller.java @@ -1,7 +1,8 @@ -package core.game.node.entity.npc.city.pollnivneach; +package content.region.desert.pollniv.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.ServerConstants; +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; @@ -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/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheMayorDialogue.kt b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheMayorDialogue.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheMayorDialogue.kt rename to Server/src/main/content/region/desert/pollniv/dialogue/AliTheMayorDialogue.kt index de24c099f..b1108b757 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheMayorDialogue.kt +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheMayorDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.node.entity.npc.city.pollnivneach +package content.region.desert.pollniv.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheOperator.kt b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheOperator.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheOperator.kt rename to Server/src/main/content/region/desert/pollniv/dialogue/AliTheOperator.kt index 425778bd5..d2c6ff057 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/pollnivneach/AliTheOperator.kt +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheOperator.kt @@ -1,7 +1,7 @@ -package rs09.game.node.entity.npc.city.pollnivneach +package content.region.desert.pollniv.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/BlackJackSeller.java b/Server/src/main/content/region/desert/pollniv/dialogue/BlackJackSeller.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/BlackJackSeller.java rename to Server/src/main/content/region/desert/pollniv/dialogue/BlackJackSeller.java index fcb004294..bab4965dc 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/BlackJackSeller.java +++ b/Server/src/main/content/region/desert/pollniv/dialogue/BlackJackSeller.java @@ -1,6 +1,6 @@ -package core.game.node.entity.npc.city.pollnivneach; +package content.region.desert.pollniv.dialogue; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/DesertBandit.java b/Server/src/main/content/region/desert/pollniv/dialogue/DesertBandit.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/DesertBandit.java rename to Server/src/main/content/region/desert/pollniv/dialogue/DesertBandit.java index ebbe9fd35..6eaaa9f81 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/DesertBandit.java +++ b/Server/src/main/content/region/desert/pollniv/dialogue/DesertBandit.java @@ -1,8 +1,8 @@ -package core.game.node.entity.npc.city.pollnivneach; +package content.region.desert.pollniv.dialogue; import core.cache.def.impl.NPCDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/StreetUrchinDialogue.java b/Server/src/main/content/region/desert/pollniv/dialogue/StreetUrchinDialogue.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/StreetUrchinDialogue.java rename to Server/src/main/content/region/desert/pollniv/dialogue/StreetUrchinDialogue.java index d452edcdc..ff307b62f 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/StreetUrchinDialogue.java +++ b/Server/src/main/content/region/desert/pollniv/dialogue/StreetUrchinDialogue.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.pollnivneach; +package content.region.desert.pollniv.dialogue; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/SnakeCharmerBasket.java b/Server/src/main/content/region/desert/pollniv/handlers/SnakeCharmerBasket.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/SnakeCharmerBasket.java rename to Server/src/main/content/region/desert/pollniv/handlers/SnakeCharmerBasket.java index 9e9609e84..49b940a85 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/pollnivneach/SnakeCharmerBasket.java +++ b/Server/src/main/content/region/desert/pollniv/handlers/SnakeCharmerBasket.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.pollnivneach; +package content.region.desert.pollniv.handlers; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.player.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 new file mode 100644 index 000000000..397cd052f --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/ArchaeologistDialogue.kt @@ -0,0 +1,211 @@ +package content.region.desert.quest.deserttreasure + +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.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +/** Known in RS3 as Asgarnia Smith */ +class ArchaeologistDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, ArchaeologistDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return ArchaeologistDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ARCHAEOLOGIST_1918) + } +} + +class ArchaeologistDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0) + .player(FacialExpression.FRIENDLY,"Hello there.") + .branch { player -> + return@branch if (DesertTreasure.hasRequirements(player)) { 1 } else { 0 } + }.let{ branch -> + // Failure branch + branch.onValue(0) + .line("He seems to be lost in his own thoughts...") + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) // Success branch + .npc(FacialExpression.FRIENDLY, "Howdy stranger. What brings you out to these parts?") + .let { path -> + val originalPath = b.placeholder() + path.goto(originalPath) + originalPath.builder().options().let { optionBuilder -> + optionBuilder.option("What are you doing here?") + .player("Nothing much - What are you doing here?", "It doesn't seem like there's much to do out here in the", "desert!") + .npc("Well, that's where you'd be wrong.", "I work for the Archaeological Society of Varrock, and", "have been excavating around here recently.") + .npcl("Was there something you specifically wanted?") + .goto(originalPath) + optionBuilder.option("Do you have any quests?") + .player(FacialExpression.FRIENDLY, "Do you have any quests?", "Call it a hunch, but you look like the type of man who", "might...") + .npc(FacialExpression.HALF_THINKING, "Well, it's funny you should say that.", "I'm not sure if I would really call it a quest as such,", "but I found this ancient stone tablet in one of my", "excavations, and it would really help me out if you") + .npc(FacialExpression.FRIENDLY, "could go and take it back to the digsite for me and get", "it examined.") + .npc(FacialExpression.NEUTRAL, "It's very old, and I don't recognise and of the", "inscriptions on it.") + .options().let { optionBuilder2 -> + optionBuilder2.option("Yes, I'll help you.") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.ETCHINGS_4654) + } + .player("Sure, I was heading that way anyway.", "Any particular person at the digsite you want me to", "talk to?") + .npc(FacialExpression.NEUTRAL, "His name's Terry Balando. Give it to nobody but him.", "I'm sorry, I can't entrust you with the actual tablet I", "found, but it is far too valuable to give away, but I took", "these etchings - they should be enough for him to make") + .npc(FacialExpression.NEUTRAL, "a preliminary translation on.", "Come back and let me know what he says, I would hate", "to waste my time excavating anything that isn't worth", "my time as a world famous archaeologist!") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 0) { + setQuestStage(player, DesertTreasure.questName, 1) + } + } + optionBuilder2.option("No thanks, I don't want to help.") + .playerl("No thanks. Playing delivery boy doesn't really sound like much fun to me. ") + .npcl(" Well, okay. Can't say as I blame you for thinking that. I'll go take it there myself later I guess.") + .end() + } + optionBuilder.option("Who are you?") + .playerl(FacialExpression.THINKING, "Who are you, anyway?") + .npc(FacialExpression.EXTREMELY_SHOCKED, "You don't recognize me???", "I am the world famous Asgarnia Smith, Archaeologist", "extraordinaire!") + .npc(FacialExpression.HALF_THINKING, "I am the one who discovered the long forgotten", "Temple of Ikov!", "The one who unearthed the strange trap filled arena", "in Brimhaven!") + .npc(FacialExpression.HALF_THINKING, "I'm the leading archaeological expert of our time!", "I was voted archaeologist of the year four years", "running by the Varrock Herald! Are you REALLY", "sure you've never heard of me?") + .playerl(FacialExpression.THINKING, "No, I really haven't.") + .npc(FacialExpression.EXTREMELY_SHOCKED, "Well then, I'm confused.", "Why did you come over and speak to me if you don't", "know who I am? What do you want?") + .goto(originalPath) + optionBuilder.option("Nothing really.") + .playerl("Nothing really. I'm just wandering around for no good reason.") + .npcl("Uh-huh. Well, if you'll excuse me, I have a lot more work to do before I can bed down for the night.") + .end() + } + } + + + b.onQuestStages(DesertTreasure.questName, 1,2) + .npcl("So what did Terry Balando say about those etchings? Did he give you a translation for me?") + .playerl("Um...yeah...about that... I kind of didn't go and speak to him yet...") + .npcl("Well what are you waiting for? A written invitation? All I want you to do is take those etchings up to the digsite for me!") + .playerl("Okay, I'll do that then.") + .end() + + b.onQuestStages(DesertTreasure.questName, 3) + .playerl("Hello there.") + .npcl("So what did Terry Balando say about those etchings? Did he give you a translation for me?") + .branch { player -> + return@branch if (inInventory(player, Items.TRANSLATION_4655)) { 1 } else { 0 } + }.let{ branch -> + // Failure branch + branch.onValue(0) + .playerl("Yeah, he did. But I don't have it with me.") + .npcl("...") + .npcl("I see.") + .npcl("Well could you go and get me that translation? It could be vital!") + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) // Success branch + .playerl("Yeah, he did. I have it here in this book.") + .npcl("Did you take a read of this? It will do you good to understand how this profession works. Here, have a read.") + .options().let { optionBuilder -> + optionBuilder.option("Read book") + .endWith { _, player -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, TranslationBook.Companion::display) + } + return@let optionBuilder + } + .option("Don't read book") + .playerl("Yeah, I did. Kind of boring really.") + .npcl("Excellent. Just give me a moment to read this, and talk to me again in a second.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 3) { + removeItem(player, Items.TRANSLATION_4655) // remove if exists + setQuestStage(player, DesertTreasure.questName, 4) + } + } + + b.onQuestStages(DesertTreasure.questName, 4) + .playerl("Hello there.") + .npcl("Hmmm. Interesting. It seems to me like there's some kind of treasure hidden out in the desert.") + .npcl("So what do you say? Fancy being a treasure hunter like me?") + .playerl("Uh... don't you mean archaeologist?") + .npcl("Yeeees... An archaeologist...") + .npcl("It's all this hot sun getting to me I think.") + .npcl("Well anyway, let's just assume there is a treasure hidden in the desert somewhere, and let's just say for the sake of argument that maybe if I found a big stash of gold and treasure I wouldn't necessarily just hand it") + .npcl("all over to the Museum of Varrock.") + .npcl("Let's also say, purely hypothetically, that if there were such a big stash of treasure and someone were to help me find it, then that hypothetical personal might be entitled to, oh, let's say a purely for the sake of") + .npcl("argument thirty percent split...") + .playerl("Fifty percent.") + .npcl("That's right!") + .npcl("A purely for the sake of argument fifty-fifty split on this hypothetical treasure, should it exist...") + .npcl("Do you see where I'm going with this?") + .playerl("You want me to help you find some treasure for a fifty percent share, as long as I don't tell anybody, and your reputation as an esteemed archaeologist with the Museum of Varrock remains intact, and nobody") + .playerl("discovers you're actually just a treasure hunter?") + .npcl("Uh... yes, but the way you said it makes it sound like I'm doing something wrong...") + .npcl("So what do you say? Partners?") + .options() + .let { optionBuilder -> + optionBuilder.option("Help him") + .playerl("Well... I guess nobody is really going to lose out on anything, and we don't even know if there is any treasure...") + .playerl("Aw, go on then. Count me in.") + .npcl("Good @g[lad,lass]! Well, if we split up we'll be able to find treasure quicker.") + .npcl("I'll continue searching around here in this Bedabin Camp, you head due South to the Bandit Village. If either of us find anything, we'll come find each other and say what, okay?") + .npcl("You head due South, see what you can find out about this tablet.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 4) { + setQuestStage(player, DesertTreasure.questName, 5) + } + } + optionBuilder.option("Don't help him") + .playerl("This all sounds very immoral, and I don't want any part of your seedy little schemes.") + .npcl("Aw, c'mon, cut me a break here man! Look, nobody is really getting hurt, right? This treasure is either going to lie around in some underground ruin, or lie around in some museum for") + .npcl("people to look at!") + .npcl("It makes sense for the people to go to all the risk to get a little payback for it, right? Besides, anything of real historical value we can hand over to the museum - we'll just keep the cash for") + .npcl("ourselves!") + .npcl("C'mon... what do you say? Partners?") + .options() + .let { optionBuilder -> + optionBuilder.option("Help him") + .playerl("Well... I guess nobody is really going to lose out on anything, and we don't even know if there is any treasure...") + .playerl("Aw, go on then. Count me in.") + .npcl("Good @g[lad,lass]! Well, if we split up we'll be able to find treasure quicker.") + .npcl("I'll continue searching around here in this Bedabin Camp, you head due South to the Bandit Village. If either of us find anything, we'll come find each other and say what, okay?") + .npcl("You head due South, see what you can find out about this tablet.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 4) { + setQuestStage(player, DesertTreasure.questName, 5) + } + } + optionBuilder.option("Don't help him") + .playerl("I don't trust anything about your little scheme, including you! I want nothing to do with this!") + .npcl("Eh, you'll come running back when you realize just how much money we could be talking here.") + .npcl("I'll see you again when you come crawling back, begging to be cut in on the deal.") + .end() + } + } + + b.onQuestStages(DesertTreasure.questName, 5) + .playerl("Hello there.") + .npcl("Hello again. You find any signs of that treasure yet?") + .playerl("Not yet...") + .npcl("Well, head south to that bandit camp. I bet if there's anything out here, they'll know about it. Don't let on it might be valuable though! Those thieves will steal it as soon as look at you!") + + b.onQuestStages(DesertTreasure.questName, 6,7,8) + .playerl("Hello there.") + .npcl("Hello again. You find any signs of that treasure yet?") + .playerl("Not as such... Although I think I'm onto something of a promising lead...") + .playerl("Did you ever hear of something called the Diamonds of Azzanadra?") + .npcl("Diamonds of Azzanadra? That sounds very promising indeed!") + .playerl("So you have heard of them?") + .npcl("Nope, never heard of them before, but you said diamonds! Diamonds are always promising!") + + + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/AzzanadraDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/AzzanadraDialogue.kt new file mode 100644 index 000000000..c8401ac87 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/AzzanadraDialogue.kt @@ -0,0 +1,59 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.SpellBookManager +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class AzzanadraDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, AzzanadraDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return AzzanadraDialogue(player) + } + override fun getIds(): IntArray { + /* This is wrong SCARABS_1970 should be AZZANADRA_1970 */ + return intArrayOf(NPCs.SCARABS_1970, NPCs.AZZANADRA_1971) + } +} + +class AzzanadraDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 10) + .npcl(FacialExpression.OLD_DEFAULT, "I knew they could not trap me here for long!") + .npcl(FacialExpression.OLD_DEFAULT, "Well done, soldier, tell me, how goes the battle?") + .playerl(FacialExpression.THINKING, "Battle?") + .npcl(FacialExpression.OLD_DEFAULT, "You do not know of the battle?") + .npcl(FacialExpression.OLD_DEFAULT, "More time must have passed than I had thought...") + .npcl(FacialExpression.OLD_DEFAULT, "Tell me, what news of great Paddewwa? Do the shining spires of Lassar still stand? And what of glorious Annakarl? The fortress is still intact?") + .player("Uh...", "Sorry, I've never heard of them...") + .npcl(FacialExpression.OLD_SAD, "No!") + .npcl(FacialExpression.OLD_SAD, "My lord... What has become of you? I cannot hear your voice in my mind anymore!") + .npcl(FacialExpression.OLD_DEFAULT, "My thanks to you brave warrior for your help in freeing me from this accursed tomb, but it seems I have much to do to make amends.") + .npcl(FacialExpression.OLD_DEFAULT, "If the shining cities no longer stand, then it means that we must have failed my lord...") + .npcl(FacialExpression.OLD_DEFAULT, "How long have I been trapped here? Master... Have you truly been dispatched from this world?") + .npcl(FacialExpression.OLD_DEFAULT, "Warrior, for your efforts in freeing me, I offer you the gift of knowledge.") + .npcl(FacialExpression.OLD_DEFAULT, "I bestow upon you the ancient magicks, taught me by my Lord before his disappearance, may you use them well in battle for our people!") + .npcl(FacialExpression.OLD_DEFAULT, "They will replace the knowledge you previously had, but you may switch between them by praying at the altar in this room at any time.") + .npcl(FacialExpression.OLD_DEFAULT, "I trust that we shall meet again adventurer, I offer you the blessings of myself and my master in all of your endeavours!") + .npcl(FacialExpression.OLD_DEFAULT, "Now, I must leave you, for there must be some trace of my master's power left somewhere. Feel free to use the portal I shall create to return here easily in the future!") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 10) { + sendMessage(player,"A strange wisdom has filled your mind...") + finishQuest(player, DesertTreasure.questName) + player.spellBookManager.setSpellBook(SpellBookManager.SpellBook.ANCIENT) + player.spellBookManager.update(player) + } + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/BartenderDialogue.kt b/Server/src/main/content/region/desert/quest/deserttreasure/BartenderDialogue.kt new file mode 100644 index 000000000..2e3e78954 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/BartenderDialogue.kt @@ -0,0 +1,155 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class BartenderDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player!!, BartenderDialogueFile(), npc) + return false + } + override fun newInstance(player: Player?): DialoguePlugin { + return BartenderDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.BARTENDER_1921) + } +} +class BartenderDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + b.onQuestStages(DesertTreasure.questName, 0,1,2,3,4) + .npcl(FacialExpression.ANNOYED, "Get out of here. I have nothing to say to the likes of you.") + .end() + + b.onQuestStages(DesertTreasure.questName, 5) + .branch { player -> + return@branch if (getAttribute(player, DesertTreasure.attributeBoughtBeer, false)) { 1 } else { 0 } + }.let{ branch -> + branch.onValue(1) + .npcl("You've had your drink, now get out of here. I have nothing to say to the-") + .playerl("No, Wait! Look, I'm here on an archaeological expedition for the Museum of Varrock.") + .playerl("I am only here looking for artefacts...") + .npcl("Oh really? Our inheritance is only sand and death. What do you expect to find out here in this desert forsaken by the gods?") + .options() + .let { optionBuilder -> + optionBuilder.option("I heard about treasure...") + .playerl("As I understand it there's some kind of hidden treasure in these parts...") + .npcl("Look around @g[pal,lady]. Does it looks like there's any treasure near here?") + .npcl("If I were you I'd get lost before someone takes a dislike to your face and removes it for you.") + .end() + + optionBuilder.option("I heard about four diamonds...") + .playerl("I heard a rumour about four diamonds or crystals...") + .npcl("The four diamonds of Azzanadra??? How came you to know of this?") + .playerl("You've heard of them then?") + .npcl("It's just a fairy tale for children. Maybe one of the village elders might know more, but it's not really something I care about.") + .npcl("Now get out of here, your sort isn't welcome in my bar.") + .endWith { _, player -> + if(getQuestStage(player, DesertTreasure.questName) == 5) { + setQuestStage(player, DesertTreasure.questName, 6) + } + } + + optionBuilder.option("I heard about a fortress...") + .playerl("Certain things have led me to believe there may be some kind of ruined fortress around here...") + .npcl("Doubt it. What in the world would someone need to guard against in the middle of this desert?") + .npcl("A bad attack of sand? I think you're on the wrong track, mister so-called archaeologist.") + .end() + } + + branch.onValue(0) + .npcl("If you're not buying anything, I have nothing to say to you.") + .options() + .let { optionBuilder -> + optionBuilder.option("Ask about Desert Treasure") + .playerl("As I understand it there's some kind of hidden treasure in these parts...") + .npcl("Look around @g[pal,lady]. Does it looks like there's any treasure near here?") + .npcl("If I were you I'd get lost before someone takes a dislike to your face and removes it for you.") + .end() + optionBuilder.option("Buy a drink") + .branch { player -> + return@branch if (inInventory(player, Items.COINS_995, 650)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("What's that? You wanna buy a beer? It'll cost ya 650 coins.") + .options() + .let { optionBuilder -> + optionBuilder.option("Buy a beer") + .betweenStage { _, player, _, _ -> + if (removeItem(player, Item(Items.COINS_995, 650))) { + addItemOrDrop(player, Items.BANDITS_BREW_4627) + setAttribute(player, DesertTreasure.attributeBoughtBeer, true) + } + } + .npcl("There you go. Now get out, we don't like your sort around here.") + .end() + optionBuilder.option("Don't buy anything") + .npcl("Get out of my bar then! We don't like your sort round here!") + .end() + } + + branch.onValue(0) + .npcl("You ain't got the 650 coins it costs to buy it, and I'm glad 'cos I didn't want to serve you anyway.") + .end() + } + + } + } + + + b.onQuestStages(DesertTreasure.questName, 6,7,8,9,10,11) + .branch { player -> + return@branch if (getAttribute(player, DesertTreasure.attributeBoughtBeer, false)) { 1 } else { 0 } + }.let { branch -> + branch.onValue(1) + .npcl("You've had your drink, now get out of here. I have nothing to say to the-") + .playerl("No, Wait! Look, I'm here on an archaeological expedition for the Museum of Varrock.") + .playerl("I am only here looking for artefacts...") + .npcl("Oh really? Our inheritance is only sand and death. What do you expect to find out here in this desert forsaken by the gods?") + .options() + .let { optionBuilder -> + optionBuilder.option("I heard about treasure...") + .playerl("As I understand it there's some kind of hidden treasure in these parts...") + .npcl("Look around @g[pal,lady]. Does it looks like there's any treasure near here?") + .npcl("If I were you I'd get lost before someone takes a dislike to your face and removes it for you.") + .end() + + optionBuilder.option("I heard about four diamonds...") + .playerl("I heard a rumour about four diamonds or crystals...") + .npcl("The four diamonds of Azzanadra??? How came you to know of this?") + .playerl("You've heard of them then?") + .npcl("It's just a fairy tale for children. Maybe one of the village elders might know more, but it's not really something I care about.") + .npcl("Now get out of here, your sort isn't welcome in my bar.") + .end() + + optionBuilder.option("I heard about a fortress...") + .playerl("Certain things have led me to believe there may be some kind of ruined fortress around here...") + .npcl("Doubt it. What in the world would someone need to guard against in the middle of this desert?") + .npcl("A bad attack of sand? I think you're on the wrong track, mister so-called archaeologist.") + .end() + + optionBuilder.option("I heard of the Diamonds of Azzanadra.") + .playerl("Tell me all you know about the Diamonds of Azzanadra.") + .npcl("Not that I think it's any of your business, but when I was a child I remember hearing the legend.") + .npcl("I don't recall it particularly well, other than they are said to contain an incredible power.") + .npcl("If you really want to hear more about it you'd be best off finding someone who cares about the past, and the history of this area, and stop bothering me.") + .end() + } + } + + b.onQuestStages(DesertTreasure.questName, 100) + .npcl("So you're the @g[fella,lass] that freed Azzanadra, huh? Fair play to ya. What will you have?") + .end() + + } +} diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DamisBehavior.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DamisBehavior.kt new file mode 100644 index 000000000..1b996dafc --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DamisBehavior.kt @@ -0,0 +1,81 @@ +package content.region.desert.quest.deserttreasure + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class DamisBehavior : NPCBehavior(NPCs.DAMIS_1974, NPCs.DAMIS_1975) { + private var disappearing = false + + override fun tick(self: NPC): Boolean { + if (disappearing) { + return true + } + val player: Player? = getAttribute(self, "target", null) + if (player == null || !self.location.withinDistance(self.properties.spawnLocation, self.walkRadius)) { + if (player != null && !disappearing) { + disappearing = true + sendMessage(player, "Damis has vanished once more into the shadows...") + removeAttribute(player, DesertTreasure.attributeDamisInstance) + } + poofClear(self) + } + return true + } + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + if (attacker is Player) { + if (attacker == getAttribute(self, "target", null)) { + return true + } + sendMessage(attacker, "It's not after you...") + } + return false + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (state.estimatedHit + Integer.max(state.secondaryHit, 0) >= self.skills.lifepoints && self.id == NPCs.DAMIS_1974) { + state.estimatedHit = self.skills.lifepoints + 1 + state.secondaryHit = -1 + + transformNpc(self, NPCs.DAMIS_1975, 500) + self.skills.lifepoints = self.skills.maximumLifepoints + sendChat(self, "Armour... is for restraint, not... protection...") + queueScript(self, 2, QueueStrength.NORMAL) { stage: Int -> + sendChat(self, "Now I show... you... my true power!") + self.properties.attackSpeed = 3 + return@queueScript stopExecuting(self) + } + } + } + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + // In second form, drain prayer by 5. + if (self.id == NPCs.DAMIS_1975) { + victim.skills.decrementPrayerPoints(5.0) + } + } + + override fun onDeathFinished(self: NPC, killer: Entity) { + if (killer is Player) { + if (self.id == NPCs.DAMIS_1975) { + if (DesertTreasure.getSubStage(killer, DesertTreasure.attributeShadowStage) == 3) { + GroundItemManager.create(Item(Items.SHADOW_DIAMOND_4673), self.location, killer) + DesertTreasure.setSubStage(killer, DesertTreasure.attributeShadowStage, 100) + removeAttribute(killer, DesertTreasure.attributeFareedInstance) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt new file mode 100644 index 000000000..f23f6e024 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DesertTreasure.kt @@ -0,0 +1,544 @@ +package content.region.desert.quest.deserttreasure + +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 + +/** + * 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){ + + 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. + + // 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. + + // Blood Diamond + const val attributeBloodStage = "/save:quest:deserttreasure-bloodstage" + const val attributeDessousInstance = "quest:deserttreasure-dessousinstance" + + // 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 + + // 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 + + // 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. + + // 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 + + fun completedAllSubstages(player: Player): Boolean { + return getSubStage(player, attributeBloodStage) == 100 && + getSubStage(player, attributeSmokeStage) == 100 && + getSubStage(player, attributeIceStage) == 100 && + getSubStage(player, attributeShadowStage) == 100 + } + + fun getSubStage(player: Player, attributeName: String): Int { + return getAttribute(player, attributeName, 0) + } + + 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++ + } + + + // 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) + + 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) + + 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) + } + + 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/java/core/game/content/quest/members/icthlarinslittlehelper/WandererDialogue.java b/Server/src/main/content/region/desert/quest/icthlarinslittlehelper/WandererDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/members/icthlarinslittlehelper/WandererDialogue.java rename to Server/src/main/content/region/desert/quest/icthlarinslittlehelper/WandererDialogue.java index 660e1a777..956ba2537 100644 --- a/Server/src/main/java/core/game/content/quest/members/icthlarinslittlehelper/WandererDialogue.java +++ b/Server/src/main/content/region/desert/quest/icthlarinslittlehelper/WandererDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.quest.members.icthlarinslittlehelper; +package content.region.desert.quest.icthlarinslittlehelper; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** 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 new file mode 100644 index 000000000..19af2735d --- /dev/null +++ b/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt @@ -0,0 +1,134 @@ +package content.region.desert.quest.thegolem + +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.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) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return ClayGolemDialoguePlugin(player) + } + override fun open(vararg objects: Any?): Boolean { + npc = objects[0] as NPC + player.dialogueInterpreter.open(ClayGolemDialogueFile(), npc) + return true + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + return false + } + override fun getIds(): IntArray { + return intArrayOf(1907, NPCs.BROKEN_CLAY_GOLEM_1908, NPCs.DAMAGED_CLAY_GOLEM_1909, NPCs.CLAY_GOLEM_1910) + } +} + +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(Quests.THE_GOLEM).hasRequirements(player) } + .playerl("Shall I try to repair you?") + .npcl("Repairs... needed...") + .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(Quests.THE_GOLEM, 1) + .npcl("Repairs... needed...") + .end() + 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.") + .playerl("What enemy?") + .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, 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(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, 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, Quests.THE_GOLEM, 6) } + b.onQuestStages(Quests.THE_GOLEM, 6, 7) + .npcl("My task is incomplete. You must open the portal so I can defeat the great demon.") + .playerl("I already told you, he's dead!") + .npcl("Task incomplete.") + .playerl("Oh, how am I going to convince you?") + .endWith() { df, player -> if(player.questRepository.getStage(Quests.THE_GOLEM) < 7) { setQuestStage(player, Quests.THE_GOLEM, 7) } } + } +} + +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, Quests.THE_GOLEM) } + } +} + +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.") + .npcl("What do you want it for?") + .options() + opt1 + .option("I want to open a portal to the lair of an elder-demon.") + .playerl("I want to open a portal to the lair of an elder-demon.") + .npcl("Good heavens! I'd never let you do such a dangerous thing.") + .end() + opt1 + .option("Well, I, er, just want it.") + .playerl("Well, I, er, just want it.") + .end() + } +} + +// https://youtu.be/owPSLUB9E8k?t=261 +val LETTER_LINES = arrayOf( + 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 DISPLAY_CASE_TEXT = arrayOf("3rd age - yr 3000-4000", + "", + "This statuette was found in an underground", + "temple in the ruined city of Uzer, which was", + "destroyed late in the 3rd Age, suddenly, due to", + "causes unknown. It probably represents one of", + "the clay golems that the craftsmen of the city", + "built as warriors and servants. The statuette", + "was originally part of a mechanism whose", + "purpose is unknown.") diff --git a/Server/src/main/content/region/desert/quest/thegolem/TheGolemQuest.kt b/Server/src/main/content/region/desert/quest/thegolem/TheGolemQuest.kt new file mode 100644 index 000000000..4d637b890 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/thegolem/TheGolemQuest.kt @@ -0,0 +1,434 @@ +package content.region.desert.quest.thegolem + +import core.api.* +import core.game.node.Node +import core.game.node.entity.npc.AbstractNPC +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.node.scenery.Scenery +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +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(Quests.THE_GOLEM, 70, 69, 1, 437, 0, 1, 10) { + 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 talking to the golem who is in the:", ln++, false) + line(player, "Ruined city of !!Uzer??, which is in the desert to the east of", ln++, false) + line(player, "the !!Shantay Pass.", ln++, false) + line(player, "I will need to have !!level 20 crafting?? and !!level 25 thieving", ln++, false) + } + if(stage >= 1) { + line(player, "I've found the golem, and offered to repair it.", ln++, stage > 1) + } + if(stage >= 2) { + line(player, "I've repaired the golem with some soft clay.", ln++, stage > 2) + } + if(stage >= 3) { + line(player, "The golem wants me to open a portal to help it defeat", ln++, stage > 3) + line(player, "the demon that attacked its city.", ln++, stage > 3) + } + val readLetter = player.getAttribute("the-golem:read-elissa-letter", false) + val readBook = player.getAttribute("the-golem:varmen-notes-read", false) + if(readLetter) { + line(player, "I've found a letter that mentions !!The Digsite", ln++, readBook) + } + if(readBook) { + line(player, "I've found a book that mentions that golems are programmed by", ln++, stage > 7) + line(player, "writing instructions on papyrus with a phoenix quill pen.", ln++, stage > 7) + } + val hasStatuette = TheGolemListeners.hasStatuette(player) + val doorOpen = player.getAttribute("the-golem:door-open", false) + if(hasStatuette) { + line(player, "I've acquired a statuette that fits a mechanism in the ruins", ln++, doorOpen) + line(player, " of !!Uzer?? from the Varrock museum.", ln++, doorOpen) + } + val seenDemon = player.getAttribute("the-golem:seen-demon", false) + if(doorOpen) { + line(player, "I've opened the portal in the ruins of Uzer.", ln++, seenDemon) + } + if(seenDemon) { + line(player, "It turns out that the demon is already dead!", ln++, stage > 4) + line(player, "I should tell the golem the good news.", ln++, stage > 4) + } + if(stage > 4) { + line(player, "The demon doesn't think its task is complete.", ln++, stage > 7) + } + if(stage >= 100) { + line(player, "%%QUEST COMPLETE!&&", ln++, false) + } + } + + override fun hasRequirements(player: Player): Boolean { + return player.skills.getStaticLevel(Skills.CRAFTING) >= 20 && player.skills.getStaticLevel(Skills.THIEVING) >= 25 + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.STATUETTE_4618,230,277,5) + drawReward(player, "1 quest point", ln++) + drawReward(player, "1,000 Crafting XP",ln++) + drawReward(player, "1,000 Theiving XP", ln++) + rewardXP(player, Skills.CRAFTING, 3000.0) + rewardXP(player, Skills.THIEVING, 2000.0) + } + + override fun updateVarps(player: Player) { + TheGolemListeners.updateVarps(player) + } +} + +@Initializable +class ClayGolemNPC : AbstractNPC { + constructor() : super(NPCs.BROKEN_CLAY_GOLEM_1908, null, true) {} + private constructor(id: Int, location: Location) : super(id, location) {} + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + return ClayGolemNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(1907, NPCs.BROKEN_CLAY_GOLEM_1908, NPCs.DAMAGED_CLAY_GOLEM_1909, NPCs.CLAY_GOLEM_1910) + } +} + +class DisplayCaseListener : InterfaceListener { + override fun defineInterfaceListeners() { + onOpen(534) { player, component -> + //player.packetDispatch.sendModelOnInterface(25576, 534, 5, 650) + val model = player.getAttribute("ifaces:534:model", 0) + player.packetDispatch.sendModelOnInterface(model, 534, 4, 461) + //player.packetDispatch.sendModelOnInterface(25576, 534, 12, 973) + setInterfaceText(player, DISPLAY_CASE_TEXT.joinToString("
"), 534, 2) + setInterfaceText(player, "30", 534, 85) + return@onOpen true + } + } +} + +class TheGolemListeners : InteractionListener { + fun repairGolem(player: Player): Boolean { + 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." + 1 -> "You fix the golem's legs." + 2 -> "The golem is nearly whole." + 3 -> "You repair the golem with a final piece of clay." + else -> null + } + if(removeItem(player, Item(Items.SOFT_CLAY_1761, 1))) { + if(msg != null) { + sendItemDialogue(player, Items.SOFT_CLAY_1761, msg) + } + clayUsed = Math.min(clayUsed + 1, 4) + player.setAttribute("/save:the-golem:clay-used", clayUsed) + updateVarps(player) + if(clayUsed == 4) { + setQuestStage(player, Quests.THE_GOLEM, 2) + } + } + } + return true + } + + fun takeThroneGems(player: Player) { + if(player.getAttribute("the-golem:gems-taken", false)) { + return + } + if(!player.inventory.containsAtLeastOneItem(Items.HAMMER_2347) || !player.inventory.containsAtLeastOneItem(Items.CHISEL_1755)) { + player.sendMessage("You need a hammer and chisel to remove the gems.") + return + } + if(!(player.inventory.freeSlots() >= 6)) { + player.sendMessage("You don't have enough free space to remove all six gems.") + return + } + player.setAttribute("/save:the-golem:gems-taken", true) + updateVarps(player) + for(item in arrayOf(Items.SAPPHIRE_1607, Items.EMERALD_1605, Items.RUBY_1603)) { + player.inventory.add(Item(item, 2)) + } + sendItemDialogue(player, Items.RUBY_1603, "You prize the gems from the demon's throne.") + } + + companion object { + @JvmStatic + fun hasStatuette(player: Player): Boolean { + return player.inventory.containsAtLeastOneItem(4618) || player.bank.containsAtLeastOneItem(4618) || player.getAttribute("the-golem:placed-statuette", false) + } + + @JvmStatic + 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:initialized", true) + } + } + + @JvmStatic + fun updateVarps(player: Player) { + val clayUsed = player.getAttribute("the-golem:clay-used", 0) + val gemsTaken = if(player.getAttribute("the-golem:gems-taken", false)) { 1 } else { 0 } + 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 doorOpen = player.getAttribute("the-golem:door-open", false) + var clientStage = 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(Quests.THE_GOLEM) >= 100) { + clientStage = Math.max(clientStage, 10) + } + 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)) + } + } + + fun pickpocketCurator(player: Player, node: Node): Boolean { + if(player.inventory.containsAtLeastOneItem(4617) || player.bank.containsAtLeastOneItem(4617)) { + player.sendMessage("You have no reason to do that.") + return true + } + sendItemDialogue(player, 4617, "You steal a tiny key.") + addItemOrDrop(player, 4617, 1) + return true + } + + fun displayCase(player: Player, node: Scenery): Boolean { + val model = node.definition.modelIds[0] + player.setAttribute("ifaces:534:model", model) + openInterface(player, 534) + return true + } + + fun openDisplayCase(player: Player, node: Node): Boolean { + if(!player.inventory.containsAtLeastOneItem(4617)) { + player.sendMessage("You can't open the display case without the key.") + return true + } + if(hasStatuette(player)) { + return true + } + addItemOrDrop(player, 4618, 1) + updateVarps(player) + return true + } + + fun placeStatuette(player: Player): Boolean { + + if(player.inventory.remove(Item(4618))) { + player.sendMessage("You insert the statuette into the alcove.") + player.setAttribute("/save:the-golem:placed-statuette", true) + updateVarps(player) + } + return true + } + + fun turnStatuette(player: Player, node: Scenery): Boolean { + if(player.getAttribute("the-golem:door-open", false)) { + player.sendMessage("You've already opened the door.") + return true + } + val index = when(node.wrapper.id) { + 6303 -> 0 + 6304 -> 1 + 6305 -> 2 + 6306 -> 3 + else -> return true + } + initializeStatuettes(player) + val rotation = 1 - player.getAttribute("the-golem:statuette-rotation:${index}", 0) + val dir = if(rotation == 0) { "right" } else { "left" } + player.sendMessage("You turn the statuette to the ${dir}.") + player.setAttribute("the-golem:statuette-rotation:${index}", rotation) + checkDoor(player) + updateVarps(player) + return true + } + + 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 placed = player.getAttribute("the-golem:placed-statuette", false) + if(rotation0 == 1 && rotation1 == 1 && rotation2 == 0 && rotation3 == 0 && placed) { + player.sendMessage("The door grinds open.") + player.setAttribute("/save:the-golem:door-open", true) + } + } + } + + fun mortarOnMushroom(player: Player): Boolean { + if(!player.inventory.containsAtLeastOneItem(Items.VIAL_229)) { + player.sendMessage("You need a vial to do that.") + return true + } + if(player.inventory.remove(Item(Items.BLACK_MUSHROOM_4620, 1)) && player.inventory.remove(Item(Items.VIAL_229, 1))) { + sendItemDialogue(player, Items.BLACK_MUSHROOM_INK_4622, "You crush the mushroom and pour the juice into a vial.") + player.inventory.add(Item(Items.BLACK_MUSHROOM_INK_4622, 1)) + } + return true + } + + fun featherOnInk(player: Player): Boolean { + if(player.inventory.remove(Item(Items.PHOENIX_FEATHER_4621, 1))) { + sendItemDialogue(player, Items.PHOENIX_QUILL_PEN_4623, "You dip the phoenix feather into the ink.") + player.inventory.add(Item(Items.PHOENIX_QUILL_PEN_4623, 1)) + } + return true + } + + fun penOnPapyrus(player: Player): Boolean { + if(!player.getAttribute("the-golem:varmen-notes-read", false)) { + player.sendMessage("You don't know what to write.") + return true + } + if(player.inventory.remove(Item(Items.PAPYRUS_970, 1))) { + sendItemDialogue(player, Items.GOLEM_PROGRAM_4624, "You write on the papyrus:
YOUR TASK IS DONE") + player.inventory.add(Item(Items.GOLEM_PROGRAM_4624, 1)) + } + return true + } + + fun implementOnGolem(player: Player): Boolean { + if(!player.getAttribute("the-golem:varmen-notes-read", false)) { + player.sendMessage("You don't know what that would do.") + return true + } + if(player.questRepository.getStage(Quests.THE_GOLEM) == 7) { + player.sendMessage("You insert the key and the golem's skull hinges open.") + setQuestStage(player, Quests.THE_GOLEM, 8) + } + return true + } + + fun programOnGolem(player: Player, used: Node, with: Node): Boolean { + player.dialogueInterpreter.open(ClayGolemProgramDialogueFile(), with) + return true + } + + + override fun defineDestinationOverrides() { + addClimbDest(Location.create(3492, 3089, 0), Location.create(2722, 4886, 0)) + addClimbDest(Location.create(2721, 4884, 0), Location.create(3491, 3090, 0)) + setDest(IntType.SCENERY, intArrayOf(34978), "climb-down") { _, _ -> return@setDest Location.create(3491, 3090, 0) } + setDest(IntType.SCENERY, intArrayOf(6372), "climb-up") { _, _ -> return@setDest Location.create(2722, 4886, 0) } + } + + override fun defineListeners() { + onUseWith(IntType.NPC, Items.SOFT_CLAY_1761, 1907) { player, used, with -> return@onUseWith repairGolem(player) } + 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("/save:the-golem:read-elissa-letter", true) + ScrollInterface.scrollSetup(player, Components.MESSAGESCROLL_220, LETTER_LINES) + return@on true + } + on(35226, IntType.SCENERY, "search") { player, node -> + player.packetDispatch.sendMessage("You search the bookcase.") + val readLetter = player.getAttribute("the-golem:read-elissa-letter", false) + if(!player.inventory.containsAtLeastOneItem(4616) && !player.bank.containsAtLeastOneItem(4616) && readLetter) { + sendItemDialogue(player, 4616, "You find Varmen's expedition notes.") + addItemOrDrop(player, 4616, 1) + } else { + player.packetDispatch.sendMessage("You find nothing of interest.") + } + return@on true + } + on(6363, IntType.SCENERY, "open") { player, node -> player.sendMessage("The door doesn't open."); return@on true } + on(6364, IntType.SCENERY, "enter") { player, node -> + player.sendMessage("You step into the portal.") + 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, Quests.THE_GOLEM, 4) + } + teleport(player, Location.create(3552, 4948, 0)) + return@on true + } + on(6282, IntType.SCENERY, "enter") { player, node -> + player.sendMessage("You step into the portal.") + teleport(player, Location.create(2722, 4911, 0)) + return@on true + } + onUseWith(IntType.SCENERY, Items.HAMMER_2347, 6301) { player, _, _ -> takeThroneGems(player); return@onUseWith true } + onUseWith(IntType.SCENERY, Items.CHISEL_1755, 6301) { player, _, _ -> takeThroneGems(player); return@onUseWith true } + on(646, IntType.NPC, "pickpocket") { player, node -> return@on pickpocketCurator(player, node) } + on(intArrayOf(24627, 24550), IntType.SCENERY, "study") { player, node -> return@on displayCase(player, node as Scenery) } + on(24627, IntType.SCENERY, "open") { player, node -> return@on openDisplayCase(player, node) } + onUseWith(IntType.SCENERY, 4618, 6309) { player, used, with -> return@onUseWith placeStatuette(player) } + on(intArrayOf(6307, 6308), IntType.SCENERY, "turn") { player, node -> turnStatuette(player, node as Scenery) } + onUseWith(IntType.ITEM, 233, 4620) { player, used, with -> return@onUseWith mortarOnMushroom(player) } + onUseWith(IntType.ITEM, 4621, 4622) { player, used, with -> return@onUseWith featherOnInk(player) } + onUseWith(IntType.ITEM, 970, 4623) { player, used, with -> return@onUseWith penOnPapyrus(player) } + onUseWith(IntType.NPC, 4619, 1907) { player, used, with -> return@onUseWith implementOnGolem(player) } + onUseWith(IntType.NPC, 4624, 1907) { player, used, with -> return@onUseWith programOnGolem(player, used, with) } + on(1911, IntType.NPC, "grab-feather") { player, node -> + if(player.getAttribute("the-golem:varmen-notes-read", false)) { + player.lock() + GameWorld.Pulser.submit(object : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> { + player.sendMessage("You attempt to grab the pheonix's tail-feather.") + player.animator.animate(Animation(881)) + } + 3 -> { + player.sendMessage("You grab a tail-feather.") + player.inventory.add(Item(Items.PHOENIX_FEATHER_4621)) + player.unlock() + return true + } + } + return false + } + }) + } else { + player.sendMessage("You have no reason to take the phoenix's feathers.") + } + 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/java/core/game/content/quest/members/thetouristrap/AlShabimDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/AlShabimDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/AlShabimDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/AlShabimDialogue.java index febde8bdf..4afd56007 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/AlShabimDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/AlShabimDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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/java/core/game/content/quest/members/thetouristrap/AnaDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/AnaDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java index ec03928ef..efb949796 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/AnaDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/AnaDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +import content.data.Quests; +import core.game.dialogue.DialoguePlugin; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.npc.AbstractNPC; @@ -8,11 +9,11 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * The dialogue plugin used to handle the ana npc. @@ -51,13 +52,13 @@ public final class AnaDialogue extends DialoguePlugin { @Override public void init() { super.init(); - PluginManager.definePlugin(new AnaNPC()); - PluginManager.definePlugin(new AnaBarrelHandler()); + ClassScanner.definePlugin(new AnaNPC()); + ClassScanner.definePlugin(new AnaBarrelHandler()); } @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/java/core/game/content/quest/members/thetouristrap/BedabinNomadDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/BedabinNomadDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/BedabinNomadDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/BedabinNomadDialogue.java index 20ea36f0d..420714f24 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/BedabinNomadDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/BedabinNomadDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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/java/core/game/content/quest/members/thetouristrap/CaptainSiadDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/CaptainSiadDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/CaptainSiadDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/CaptainSiadDialogue.java index 6839eba09..9de225a64 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/CaptainSiadDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/CaptainSiadDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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/java/core/game/content/quest/members/thetouristrap/DesertGuardDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/DesertGuardDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java index 927f2ef5d..9c9a08367 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/DesertGuardDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/DesertGuardDialogue.java @@ -1,17 +1,18 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; import java.util.List; -import core.game.content.dialogue.DialoguePlugin; +import content.data.Quests; +import core.game.dialogue.DialoguePlugin; 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.quest.Quest; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; /** @@ -51,13 +52,13 @@ public final class DesertGuardDialogue extends DialoguePlugin { @Override public void init() { super.init(); - PluginManager.definePlugin(new DesertGuardNPC()); + ClassScanner.definePlugin(new DesertGuardNPC()); } @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/java/core/game/content/quest/members/thetouristrap/IrenaDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/IrenaDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/IrenaDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/IrenaDialogue.java index 6ec1cc6d0..8788371b8 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/IrenaDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/IrenaDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +import content.data.Quests; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -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/java/core/game/content/quest/members/thetouristrap/MaleSlaveDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/MaleSlaveDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/MaleSlaveDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/MaleSlaveDialogue.java index 3aa5ab409..269f4185a 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/MaleSlaveDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MaleSlaveDialogue.java @@ -1,12 +1,13 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +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 core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.tools.RandomFunction; @@ -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/java/core/game/content/quest/members/thetouristrap/MercenaryCaptainDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryCaptainDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/MercenaryCaptainDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/MercenaryCaptainDialogue.java index 43f74baf4..e020f6793 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/MercenaryCaptainDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryCaptainDialogue.java @@ -1,13 +1,14 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +import content.data.Quests; +import core.game.dialogue.DialoguePlugin; 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.quest.Quest; import core.game.world.map.Location; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the mercenary captain dialogue plugin. @@ -46,13 +47,13 @@ public final class MercenaryCaptainDialogue extends DialoguePlugin { @Override public void init() { super.init(); - PluginManager.definePlugin(new MercenaryCaptain()); + ClassScanner.definePlugin(new MercenaryCaptain()); } @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/java/core/game/content/quest/members/thetouristrap/MercenaryDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/MercenaryDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/MercenaryDialogue.java index d97a2263a..4f063203c 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/MercenaryDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MercenaryDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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/java/core/game/content/quest/members/thetouristrap/MineSlaveNPC.java b/Server/src/main/content/region/desert/quest/thetouristrap/MineSlaveNPC.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/MineSlaveNPC.java rename to Server/src/main/content/region/desert/quest/thetouristrap/MineSlaveNPC.java index e9cbaf1b4..c0e77e062 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/MineSlaveNPC.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MineSlaveNPC.java @@ -1,7 +1,7 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; import core.game.node.entity.npc.AbstractNPC; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.tools.RandomFunction; diff --git a/Server/src/main/java/core/game/content/quest/members/thetouristrap/MinecartDriverDialogue.java b/Server/src/main/content/region/desert/quest/thetouristrap/MinecartDriverDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/MinecartDriverDialogue.java rename to Server/src/main/content/region/desert/quest/thetouristrap/MinecartDriverDialogue.java index e2c2ff30a..a748227fd 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/MinecartDriverDialogue.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MinecartDriverDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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/java/core/game/content/quest/members/thetouristrap/MiningCampZone.java b/Server/src/main/content/region/desert/quest/thetouristrap/MiningCampZone.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/MiningCampZone.java rename to Server/src/main/content/region/desert/quest/thetouristrap/MiningCampZone.java index b569aeceb..ae06c8e0a 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/MiningCampZone.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/MiningCampZone.java @@ -1,12 +1,13 @@ -package core.game.content.quest.members.thetouristrap; +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; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; @@ -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/java/core/game/content/quest/members/thetouristrap/RowdySlaveNPC.java b/Server/src/main/content/region/desert/quest/thetouristrap/RowdySlaveNPC.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/RowdySlaveNPC.java rename to Server/src/main/content/region/desert/quest/thetouristrap/RowdySlaveNPC.java index 60f0ac084..0146e4f54 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/RowdySlaveNPC.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/RowdySlaveNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; @@ -11,7 +11,7 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.world.map.Location; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; /** @@ -65,7 +65,7 @@ public final class RowdySlaveNPC extends AbstractNPC { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { diff --git a/Server/src/main/java/core/game/content/quest/members/thetouristrap/TouristTrap.java b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/TouristTrap.java rename to Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java index 6c68c59e2..75f4d6134 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/TouristTrap.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.thetouristrap; +package content.region.desert.quest.thetouristrap; import core.game.component.Component; import core.game.container.impl.EquipmentContainer; @@ -7,11 +7,14 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.zone.ZoneBorders; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * The main type for the tourist trap quest. @@ -21,12 +24,6 @@ import rs09.plugin.PluginManager; */ @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,12 +108,12 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugins(new TouristTrapPlugin(), new AnaDialogue(), new CaptainSiadDialogue(), new DesertGuardDialogue(), new IrenaDialogue(), new MaleSlaveDialogue(), new MercenaryCaptainDialogue(), new MercenaryDialogue(), new MinecartDriverDialogue(), new MineSlaveNPC(), new MiningCampZone(), new RowdySlaveNPC(), new AlShabimDialogue(), new BedabinNomadDialogue()); + ClassScanner.definePlugins(new TouristTrapPlugin(), new AnaDialogue(), new CaptainSiadDialogue(), new DesertGuardDialogue(), new IrenaDialogue(), new MaleSlaveDialogue(), new MercenaryCaptainDialogue(), new MercenaryDialogue(), new MinecartDriverDialogue(), new MineSlaveNPC(), new MiningCampZone(), new RowdySlaveNPC(), new AlShabimDialogue(), new BedabinNomadDialogue()); return this; } @@ -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/java/core/game/content/quest/members/thetouristrap/TouristTrapPlugin.java b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/thetouristrap/TouristTrapPlugin.java rename to Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java index a87caed01..463d61272 100644 --- a/Server/src/main/java/core/game/content/quest/members/thetouristrap/TouristTrapPlugin.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrapPlugin.java @@ -1,19 +1,20 @@ -package core.game.content.quest.members.thetouristrap; +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; import core.game.component.Component; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.smithing.smelting.Bar; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.smithing.smelting.Bar; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -26,17 +27,19 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.build.DynamicRegion; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; -import core.game.content.quest.members.thetouristrap.TouristTrapPlugin.AnnaCartHandler.AnnaCartCutscene; -import core.game.content.quest.members.thetouristrap.TouristTrapPlugin.BedabinAnvilHandler.AnnaWinchHandler; +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); @@ -117,22 +121,22 @@ public final class TouristTrapPlugin extends OptionHandler { TouristTrap.TECHNICAL_PLANS.getDefinition().getHandlers().put("option:read", this); TouristTrap.ANNA_BARREL.getDefinition().getHandlers().put("option:look", this); TouristTrap.ANNA_BARREL.getDefinition().getHandlers().put("option:drop", this); - PluginManager.definePlugin(new BedabinKeyHandler()); - PluginManager.definePlugin(new BedabinAnvilHandler()); - PluginManager.definePlugin(new BarrelDialogue()); - PluginManager.definePlugin(new WinchDialogue()); - PluginManager.definePlugin(new MineCartDialogue()); - PluginManager.definePlugin(new AnnaCartHandler()); - PluginManager.definePlugin(new AnnaCartCutscene()); - PluginManager.definePlugin(new AnnaWinchHandler()); - PluginManager.definePlugin(new WinchCutscene()); - PluginManager.definePlugin(new CartDialogue()); + ClassScanner.definePlugin(new BedabinKeyHandler()); + ClassScanner.definePlugin(new BedabinAnvilHandler()); + ClassScanner.definePlugin(new BarrelDialogue()); + ClassScanner.definePlugin(new WinchDialogue()); + ClassScanner.definePlugin(new MineCartDialogue()); + ClassScanner.definePlugin(new AnnaCartHandler()); + ClassScanner.definePlugin(new AnnaCartCutscene()); + ClassScanner.definePlugin(new AnnaWinchHandler()); + ClassScanner.definePlugin(new WinchCutscene()); + ClassScanner.definePlugin(new CartDialogue()); return this; } @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); @@ -963,7 +972,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public void init() { super.init(); - PluginManager.definePlugin(new MiningCartCutscene()); + ClassScanner.definePlugin(new MiningCartCutscene()); } @Override @@ -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; @@ -1386,15 +1395,15 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) { addHandler(2672, OBJECT_TYPE, this); - PluginManager.definePlugin(new PrototypeDartHandler()); - PluginManager.definePlugin(new BedabinAnvilDialogue()); + ClassScanner.definePlugin(new PrototypeDartHandler()); + ClassScanner.definePlugin(new BedabinAnvilDialogue()); return this; } @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; } @@ -1459,7 +1468,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) { addHandler(TouristTrap.PROTOTYPE_DART_TIP.getId(), ITEM_TYPE, this); - PluginManager.definePlugin(new ProtoTypeDialogue()); + ClassScanner.definePlugin(new ProtoTypeDialogue()); return this; } diff --git a/Server/src/main/content/region/desert/sophanem/dialogue/EmbalmerDialogue.kt b/Server/src/main/content/region/desert/sophanem/dialogue/EmbalmerDialogue.kt new file mode 100644 index 000000000..99ee9e3db --- /dev/null +++ b/Server/src/main/content/region/desert/sophanem/dialogue/EmbalmerDialogue.kt @@ -0,0 +1,41 @@ +package content.region.desert.sophanem.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 EmbalmerDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.SUSPICIOUS,"What are you doing?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return EmbalmerDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EMBALMER_1980) + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/sophanem/KlenterNPC.java b/Server/src/main/content/region/desert/sophanem/dialogue/KlenterNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/city/sophanem/KlenterNPC.java rename to Server/src/main/content/region/desert/sophanem/dialogue/KlenterNPC.java index c13299fe6..70e046b16 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/sophanem/KlenterNPC.java +++ b/Server/src/main/content/region/desert/sophanem/dialogue/KlenterNPC.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.sophanem; +package content.region.desert.sophanem.dialogue; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/node/entity/npc/city/sophanem/SphinxDialogue.java b/Server/src/main/content/region/desert/sophanem/dialogue/SphinxDialogue.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/city/sophanem/SphinxDialogue.java rename to Server/src/main/content/region/desert/sophanem/dialogue/SphinxDialogue.java index 3ae8d15cb..0d94a4972 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/sophanem/SphinxDialogue.java +++ b/Server/src/main/content/region/desert/sophanem/dialogue/SphinxDialogue.java @@ -1,8 +1,8 @@ -package core.game.node.entity.npc.city.sophanem; +package content.region.desert.sophanem.dialogue; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.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 new file mode 100644 index 000000000..ac6aaa061 --- /dev/null +++ b/Server/src/main/content/region/desert/sophanem/dialogue/TarikDialogue.kt @@ -0,0 +1,83 @@ +package content.region.desert.sophanem.dialogue + +import core.api.sendDialogue +import core.api.setAttribute +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 + +@Initializable +class TarikDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + npcl(FacialExpression.WORRIED, "Ouch!") + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) + { + 0 -> playerl(FacialExpression.HALF_WORRIED, "Are you alright?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "I'm fine, I'm fine. Just a scratch.").also { stage++ } + 2 -> options("Who are you then?","How did you injure yourself?","Do you know anything about this pyramid?").also { stage++ } + 3 -> when(buttonId) { + 1 -> playerl(FacialExpression.FRIENDLY, "Who are you then?").also { stage = 10 } + 2 -> playerl(FacialExpression.HALF_WORRIED, "How did you injure yourself?").also { stage = 20 } + 3 -> playerl(FacialExpression.HALF_ASKING, "So what's in the pyramid?").also { stage = 30 } + } + + //Who are you + 10 -> npcl(FacialExpression.FRIENDLY, "Me? I'm Tarik.").also { stage++ } + 11 -> playerl(FacialExpression.HALF_ASKING, "That hat you're wearing doesn't look like it comes from around here?").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "It was a present, my employer thought it would suit me. I'm not sure what it does, though it does keep the sun off very well.").also { stage++ } + 13 -> playerl(FacialExpression.FRIENDLY, "Who is your employer then?").also { stage++ } + 14 -> npcl(FacialExpression.FRIENDLY, "Simon Templeton, though I haven't seen him since the town gates were closed off.").also { stage++ } + 15 -> playerl(FacialExpression.HALF_THINKING, "Simon Templeton ... that name seems familiar?").also { stage++ } + 16 -> npcl(FacialExpression.FRIENDLY, "He's an archaeologist, I worked as an assistant for him. Though I work for myself now.").also { stage++ } + 17 -> playerl(FacialExpression.FRIENDLY, "So what have you been up to?").also { stage++ } + 18 -> npcl(FacialExpression.FRIENDLY, "Simon suggested that there might be riches to be found in that pyramid over there.").also { stage = 30 } + + //How did you injure yourself + 20 -> npcl(FacialExpression.NEUTRAL, "I've been investigating that pyramid over there.").also { stage = 30 } + + //Do you know anything about this pyramid? + 30 -> npcl(FacialExpression.HALF_THINKING, "Well, I'm not sure. First of all there's something odd about the doors on the place. There's four of them - but three of them lead to an empty tomb. The other one is guarded by something.").also { stage++ } + 31 -> npcl(FacialExpression.THINKING, "If I make it through the door there's a Mummy waiting. But which door the right one is seems to change, it's all quite confusing.").also { stage++ } + 32 -> playerl(FacialExpression.HALF_ASKING, "What about this Mummy?").also { stage++ } + 33 -> npcl(FacialExpression.FRIENDLY, "I don't think he likes people. However he does allow you to enter some of the rooms in the tomb. They are dangerous though.").also { stage++ } + 34 -> playerl(FacialExpression.HAPPY, "Bah, I laugh in the face of danger!").also { stage++ } + 35 -> npcl(FacialExpression.FRIENDLY, "Well, if you go into that pyramid then you'll be laughing a lot then. It's full of poisonous snakes and scarabs, and rather nasty Mummies as well. You could die in there.").also { stage++ } + 36 -> npcl(FacialExpression.FRIENDLY, "I managed to get into one room, but the next room was harder. My lockpicking skills weren't good enough, maybe I should have brought a lockpick.").also { stage++ } + 37 -> sendDialogue(player, "The first room in the pyramid requires a thieving level of 21. Each subsequent room requires an extra 10 levels to enter.").also { stage++ } + 38 -> npcl(FacialExpression.NEUTRAL, "There are also lots of poisonous snakes in the urns. They'll bite you if they can. You might be able to charm them if you know how. I'd bring some antipoison anyway if I were you.").also { stage++ } + 39 -> playerl(FacialExpression.FRIENDLY, "It all sounds like fun to me. So is there anything valuable in there?").also { stage++ } + 40 -> npcl(FacialExpression.NEUTRAL, "There are lots of artefacts, you should be able to sell them on the black market, I mean, to a legitimate trader, for some money.").also { stage++ } + 41 -> playerl(FacialExpression.NEUTRAL, "You mean Simon Templeton?").also { stage++ } + 42 -> npcl(FacialExpression.NEUTRAL, "Well, he's a 'legitimate' as you can get, if you can get out of this city and get to him.").also { stage++ } + 43 -> playerl(FacialExpression.FRIENDLY, "I'll find a way. Is there anything else of value in there?").also { stage++ } + 44 -> npcl(FacialExpression.SUSPICIOUS, "I have heard a rumour that there is a valuable magic sceptre in there as well.").also { stage++ } + 45 -> playerl(FacialExpression.HALF_THINKING, "Ah, now you have piqued my interest. What do you know about this sceptre?").also { stage++ } + 46 -> npcl(FacialExpression.SUSPICIOUS, "Not a lot. It is apparently made of gold and covered in jewels, and used to be owned by one of Tumeken's sons.").also { stage++ } + 47 -> playerl(FacialExpression.HALF_ASKING, "Tumeken?").also { setAttribute(player, "/save:tarik-spoken-to", true); stage++ } + 48 -> npcl(FacialExpression.NEUTRAL, "Tumeken, the sun god and head of the gods. His sons were the rulers, chosen by him to rule in his name.").also { stage++ } + 49 -> playerl(FacialExpression.HALF_THINKING, "So these sons were rich and powerful then? This sceptre should be pretty good then.").also { stage++ } + 50 -> npcl(FacialExpression.NEUTRAL, "Hmmm. Well it is supposed to have some magical powers.").also { stage++ } + 51 -> playerl(FacialExpression.HALF_ASKING, "Magical powers? This sounds good. What are they?").also { stage++ } + 52 -> npcl(FacialExpression.LAUGH, "I'll let you know when I find it!").also { stage++ } + 53 -> playerl(FacialExpression.ANNOYED, "Not if I find it first!").also { stage++ } + 54 -> npcl(FacialExpression.FRIENDLY, "Hey! If you find it I deserve a share of the profits! You wouldn't have known it existed without my help.").also { stage++ } + 55 -> playerl(FacialExpression.NEUTRAL, "I'll think about it... Right, I think I'd better investigate this place.").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TARIK_4478) + } + + 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 new file mode 100644 index 000000000..60cfad2e1 --- /dev/null +++ b/Server/src/main/content/region/desert/sophanem/handlers/SophanemPlugin.java @@ -0,0 +1,62 @@ +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; +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.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, Player Name + * + */ +@Initializable +public class SophanemPlugin extends OptionHandler { + + @Override + 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; + } + + @Override + public boolean handle(Player player, Node node, String option) { + 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/java/core/game/node/entity/npc/city/sophanem/WallShortcut.java b/Server/src/main/content/region/desert/sophanem/handlers/WallShortcut.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/city/sophanem/WallShortcut.java rename to Server/src/main/content/region/desert/sophanem/handlers/WallShortcut.java index 3a394d3ea..4e335a061 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/sophanem/WallShortcut.java +++ b/Server/src/main/content/region/desert/sophanem/handlers/WallShortcut.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.city.sophanem; +package content.region.desert.sophanem.handlers; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; @@ -9,7 +9,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; 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/java/core/game/content/dialogue/CaptainBentleyDialogue.java b/Server/src/main/content/region/fremennik/dialogue/CaptainBentleyDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/CaptainBentleyDialogue.java rename to Server/src/main/content/region/fremennik/dialogue/CaptainBentleyDialogue.java index 9ba748def..b74d3fc88 100644 --- a/Server/src/main/java/core/game/content/dialogue/CaptainBentleyDialogue.java +++ b/Server/src/main/content/region/fremennik/dialogue/CaptainBentleyDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.dialogue; +package content.region.fremennik.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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.net.packet.PacketRepository; import core.net.packet.context.MinimapStateContext; diff --git a/Server/src/main/java/core/game/content/dialogue/JossikDialogue.java b/Server/src/main/content/region/fremennik/dialogue/JossikDialogue.java similarity index 77% rename from Server/src/main/java/core/game/content/dialogue/JossikDialogue.java rename to Server/src/main/content/region/fremennik/dialogue/JossikDialogue.java index b58954d2d..2af173c7d 100644 --- a/Server/src/main/java/core/game/content/dialogue/JossikDialogue.java +++ b/Server/src/main/content/region/fremennik/dialogue/JossikDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.dialogue; +package content.region.fremennik.dialogue; -import core.game.content.global.GodBook; +import core.game.dialogue.DialoguePlugin; +import content.data.GodBook; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -9,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 @@ -70,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); @@ -94,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/java/core/game/content/dialogue/LokarSearunnerDialogue.java b/Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java similarity index 86% rename from Server/src/main/java/core/game/content/dialogue/LokarSearunnerDialogue.java rename to Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java index 0a1271b41..19e639678 100644 --- a/Server/src/main/java/core/game/content/dialogue/LokarSearunnerDialogue.java +++ b/Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java @@ -1,15 +1,18 @@ -package core.game.content.dialogue; +package content.region.fremennik.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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; 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. @@ -19,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); @@ -57,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; @@ -108,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 new file mode 100644 index 000000000..51d160974 --- /dev/null +++ b/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt @@ -0,0 +1,358 @@ +package content.region.fremennik.diary + +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 { + private const val ATTRIBUTE_SEAWEED_PICKED = "diary:fremennik:seaweed-picked" + 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, + NPCs.FISHING_SPOT_324, NPCs.FISHING_SPOT_322 + ) + + private val ROCK_CRABS = arrayOf( + NPCs.ROCK_CRAB_1265, NPCs.ROCK_CRAB_1267 + ) + + private val CAVE_CRAWLERS = arrayOf( + NPCs.CAVE_CRAWLER_1600, NPCs.CAVE_CRAWLER_1601, + 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 + const val MAINLAND_FIND_HIGHEST_TREE = 2 + const val BARBARIAN_ASSAULT_VIEW_REWARDS = 3 + const val OTTO_GODBLESSED_LEARN_BARBARIAN_FISHING = 4 + const val PICK_3_SEAWEED = 5 + const val FIND_HUNTING_EXPERT = 6 + const val PIER_CATCH_FISH = 7 + const val GATE_OBELISK_RECHARGE_POINTS = 8 + const val KILL_ADULT_BLACK_UNICORN = 9 + } + + object MediumTasks { + const val LEARN_HISTORY_FROM_CHIEFTAIN_BRUNDT = 0 + const val TOWER_GUARDS_WATCH_SHOUTING_MATCH = 1 + const val INTERACT_WITH_PET_ROCK = 2 + const val MAKE_3_VIALS = 3 + const val CHARM_FOSSEGRIMEN_RAW_BASS = 4 + const val KILL_ICE_TROLL_WHILE_WEARING_YAK_ARMOR = 5 + const val MAKE_CHEESE_WITH_CHURN = 6 + const val DIAL_FAIRY_RING_MOUNTAINTOP = 7 + const val BROWSE_BOOT_COLORS_YRSA = 8 + const val HUNT_SABRE_TOOTHED_KYATT = 9 + const val STEAL_FISH_FROM_STALL = 10 + } + + object HardTasks { + const val KILL_3_DAGANNOTHS = 0 + const val FREMENNIK_HONORIFIC_ARMOR = 1 + const val COMPLETE_BARBARIAN_OUTPOST_AGILITY_COURSE = 2 + const val LUNAR_ISLE_MINE_PURE_ESSENCE = 3 + const val MAKE_BARBARIAN_PYRE_SHIP_ARCTIC_PINE = 4 + const val CATCH_TUNE_WITHOUT_HARPOON = 5 + const val BAKE_PIE_WITH_MAGIC = 6 + const val KILL_MITHRIL_DRAGON = 7 + const val GET_MAHOGANY_FROM_ETCETERIA = 8 + } + } + + override val areaTasks get() = arrayOf( + AreaDiaryTask( + WINDSWEPT_TREE_AREA, + DiaryLevel.EASY, + EasyTasks.MAINLAND_FIND_HIGHEST_TREE + ) + ) + + override fun onAttributeSet(player: Player, event: AttributeSetEvent) { + when (event.attribute) { + "/save:$ATTRIBUTE_BARBARIAN_FISHING_TRAINING" -> { + if (event.value !is Boolean) return + + if (event.value) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.OTTO_GODBLESSED_LEARN_BARBARIAN_FISHING + ) + } + } + } + } + + override fun onDialogueOpened(player: Player, event: DialogueOpenEvent) { + when (event.dialogue) { + is CaptainCainDialogue -> { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.BARBARIAN_ASSAULT_VIEW_REWARDS + ) + } + + is HuntingExpertRellekkaDialogue -> { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.FIND_HUNTING_EXPERT + ) + } + } + } + + 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, + 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 + ) + } + } + }*/ + } + } + + override fun onNpcKilled(player: Player, event: NPCKillEvent) { + when (player.viewport.region.id) { + 10042, 10554 -> if (event.npc.id in ROCK_CRABS) { + progressIncrementalTask( + player, + DiaryLevel.EASY, + EasyTasks.KILL_5_ROCK_CRABS, + ATTRIBUTE_ROCK_CRAB_KILLCOUNT, + 5 + ) + } + + 11164 -> if (event.npc.id in CAVE_CRAWLERS) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.SLAYER_CAVE_KILL_CAVE_CRAWLER + ) + } + + 10808, 10809 -> if (event.npc.id == NPCs.BLACK_UNICORN_133) { + finishTask( + player, + DiaryLevel.EASY, + 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 + ) + } + } + } + + override fun onPickedUp(player: Player, event: PickUpEvent) { + when (player.viewport.region.id) { + 10810 -> if (event.itemId == Items.SEAWEED_401) { + progressIncrementalTask( + player, + DiaryLevel.EASY, + EasyTasks.PICK_3_SEAWEED, + ATTRIBUTE_SEAWEED_PICKED, + 3 + ) + } + } + } + + override fun onFairyRingDialed(player: Player, event: FairyRingDialEvent) { + if (event.fairyRing == FairyRing.DKS) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.DIAL_FAIRY_RING_MOUNTAINTOP + ) + } + } + + override fun onSummoningPointsRecharged(player: Player, event: SummoningPointsRechargeEvent) { + when (player.viewport.region.id) { + 10552 -> if (event.obelisk.id == Scenery.SMALL_OBELISK_29944) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.GATE_OBELISK_RECHARGE_POINTS + ) + } + } + } + + 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/java/core/game/content/zone/fremmenik/FremmenikPlugin.java b/Server/src/main/content/region/fremennik/handlers/FremmenikPlugin.java similarity index 93% rename from Server/src/main/java/core/game/content/zone/fremmenik/FremmenikPlugin.java rename to Server/src/main/content/region/fremennik/handlers/FremmenikPlugin.java index ff42e1fdb..ccfb066aa 100644 --- a/Server/src/main/java/core/game/content/zone/fremmenik/FremmenikPlugin.java +++ b/Server/src/main/content/region/fremennik/handlers/FremmenikPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.zone.fremmenik; +package content.region.fremennik.handlers; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/HonourGuardGFI.kt b/Server/src/main/content/region/fremennik/handlers/HonourGuardGFI.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/node/entity/npc/other/HonourGuardGFI.kt rename to Server/src/main/content/region/fremennik/handlers/HonourGuardGFI.kt index deba6ad9e..d82806261 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/HonourGuardGFI.kt +++ b/Server/src/main/content/region/fremennik/handlers/HonourGuardGFI.kt @@ -1,6 +1,6 @@ -package rs09.game.node.entity.npc.other +package content.region.fremennik.handlers -import api.ContentAPI +import core.api.* import core.game.node.entity.npc.AbstractNPC import core.game.world.map.Location import core.plugin.Initializable @@ -23,7 +23,7 @@ class HonourGuardGFI : AbstractNPC { override fun tick() { if(isActive && !inCombat() && RandomFunction.roll(10)){ - val localTrolls = ContentAPI.findLocalNPCs(this, intArrayOf(NPCs.ICE_TROLL_FEMALE_5523, NPCs.ICE_TROLL_MALE_5522, NPCs.ICE_TROLL_RUNT_5521, NPCs.ICE_TROLL_GRUNT_5524)) + val localTrolls = findLocalNPCs(this, intArrayOf(NPCs.ICE_TROLL_FEMALE_5523, NPCs.ICE_TROLL_MALE_5522, NPCs.ICE_TROLL_RUNT_5521, NPCs.ICE_TROLL_GRUNT_5524)) localTrolls.forEach{troll -> if(troll.location.withinDistance(location,6)) { attack(troll) diff --git a/Server/src/main/java/core/game/content/zone/rellekka/LightHousePlugin.java b/Server/src/main/content/region/fremennik/handlers/LightHousePlugin.java similarity index 91% rename from Server/src/main/java/core/game/content/zone/rellekka/LightHousePlugin.java rename to Server/src/main/content/region/fremennik/handlers/LightHousePlugin.java index 009e8dca8..b704a4b04 100644 --- a/Server/src/main/java/core/game/content/zone/rellekka/LightHousePlugin.java +++ b/Server/src/main/content/region/fremennik/handlers/LightHousePlugin.java @@ -1,7 +1,7 @@ -package core.game.content.zone.rellekka; +package content.region.fremennik.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/TarBarrelListener.kt b/Server/src/main/content/region/fremennik/handlers/TarBarrelListener.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/interaction/object/TarBarrelListener.kt rename to Server/src/main/content/region/fremennik/handlers/TarBarrelListener.kt index 27f95711b..18abaf1f6 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/object/TarBarrelListener.kt +++ b/Server/src/main/content/region/fremennik/handlers/TarBarrelListener.kt @@ -1,10 +1,11 @@ -package rs09.game.interaction.`object` +package content.region.fremennik.handlers import core.game.node.scenery.SceneryBuilder import core.game.node.item.Item import core.tools.RandomFunction import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener +import core.game.interaction.InteractionListener +import core.game.interaction.IntType /** * @author Woah, with love @@ -15,7 +16,7 @@ import rs09.game.interaction.InteractionListener * Option(s): * "Take-from" */ -class TarBarrelListener : InteractionListener() { +class TarBarrelListener : InteractionListener { val FULL_TAR_BARREL_16860 = 16860 val EMPTY_TAR_BARREL_16688 = 16688 @@ -23,7 +24,7 @@ class TarBarrelListener : InteractionListener() { override fun defineListeners() { - on(FULL_TAR_BARREL_16860,SCENERY,"take-from") { player, node -> + on(FULL_TAR_BARREL_16860, IntType.SCENERY, "take-from") { player, node -> val incrementAmount = RandomFunction.random(83, 1000) if (player.inventory.isFull) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/CitizenDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/CitizenDialogue.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/CitizenDialogue.kt rename to Server/src/main/content/region/fremennik/jatizso/dialogue/CitizenDialogue.kt index 03ad3458d..a964ae552 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/CitizenDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/CitizenDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.jatizso +package content.region.fremennik.jatizso.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE @Initializable class CitizenDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/EricDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/EricDialogue.kt similarity index 79% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/EricDialogue.kt rename to Server/src/main/content/region/fremennik/jatizso/dialogue/EricDialogue.kt index 354555430..de6d0b84f 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/EricDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/EricDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.jatizso +package content.region.fremennik.jatizso.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 -import rs09.tools.END_DIALOGUE @Initializable class EricDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/GruvaPatrullDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/GruvaPatrullDialogue.kt similarity index 86% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/GruvaPatrullDialogue.kt rename to Server/src/main/content/region/fremennik/jatizso/dialogue/GruvaPatrullDialogue.kt index b8c641f4c..cdaec8ac9 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/GruvaPatrullDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/GruvaPatrullDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.jatizso +package content.region.fremennik.jatizso.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE @Initializable class GruvaPatrullDialogue(player: Player? = null) : DialoguePlugin(player) { 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/kotlin/rs09/game/content/dialogue/region/jatizso/LeftieRightieDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/LeftieRightieDialogue.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/LeftieRightieDialogue.kt rename to Server/src/main/content/region/fremennik/jatizso/dialogue/LeftieRightieDialogue.kt index 27d961b7b..b2c3b14ee 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/LeftieRightieDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/LeftieRightieDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.jatizso +package content.region.fremennik.jatizso.dialogue -import api.DialUtils -import core.game.content.dialogue.FacialExpression +import core.api.* +import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import org.rs09.consts.NPCs -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE class LeftieRightieDialogue() : DialogueFile() { val rightie = NPCs.GUARD_5491 @@ -27,6 +27,6 @@ class LeftieRightieDialogue() : DialogueFile() { } fun npc2(messages: String){ - sendNormalDialogue(leftie, FacialExpression.NEUTRAL, *DialUtils.splitLines(messages)) + sendNormalDialogue(leftie, FacialExpression.NEUTRAL, *splitLines(messages)) } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/MinerDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/MinerDialogue.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/MinerDialogue.kt rename to Server/src/main/content/region/fremennik/jatizso/dialogue/MinerDialogue.kt index fd2861969..8a9212353 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/MinerDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/MinerDialogue.kt @@ -1,12 +1,11 @@ -package rs09.game.content.dialogue.region.jatizso +package content.region.fremennik.jatizso.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE @Initializable class MinerDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt new file mode 100644 index 000000000..e4ed6b691 --- /dev/null +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt @@ -0,0 +1,51 @@ +package content.region.fremennik.jatizso.dialogue + +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.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) { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return MordGunnarsDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if(npc.id == NPCs.MORD_GUNNARS_5481){ + npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Would you like to sail to Jatizso?") + } else { + npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Would you like to sail back to Rellekka?") + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("Yes, please.", "No, thanks.").also { stage++ } + 1 -> when(buttonId){ + 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, please!").also { stage++ } + 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "No, thank you.").also { stage = END_DIALOGUE } + } + + 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) + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MORD_GUNNARS_5482, NPCs.MORD_GUNNARS_5481) + } + +} diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/TowerGuardDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/TowerGuardDialogue.kt new file mode 100644 index 000000000..184051315 --- /dev/null +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/TowerGuardDialogue.kt @@ -0,0 +1,50 @@ +package content.region.fremennik.jatizso.dialogue + +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.interaction.IntType +import core.game.interaction.InteractionListeners +import core.tools.END_DIALOGUE + +@Initializable +class TowerGuardDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return TowerGuardDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "What are you doing here?") + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "I'M ON SHOUTING DUTY!").also { stage++ } + 1 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "No need to shout.").also { stage++ } + 2 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "I'M SORRY I'VE BEEN SHOUTING","INSULTS SO LONG I CAN'T HELP IT!").also { stage++ } + 3 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "Who are you insulting?").also { stage++ } + 4 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "THE TOWER IN ${if(npc.id == NPCs.GUARD_5489) "NEITIZNOT" else "JATIZSO"}.","THEY SHOUT INSULTS BACK.").also { stage++ } + 5 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Err, why?" ).also { stage++ } + 6 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "THE ${if(npc.id == NPCs.GUARD_5489) "KING" else "BURGHER"} TELLS US TO.").also { stage++ } + 7 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "Your ${if(npc.id == NPCs.GUARD_5489) "King" else "Burgher"} is a strange person.").also { stage++ } + 8 -> options("Can I watch? I'm curious.", "Oh well, I'd better get going.").also { stage++ } + 9 -> when(buttonId){ + 1 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Can I watch? I'm curious.").also { stage++ } + 2 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "Oh well, I'd better get going.").also { stage = END_DIALOGUE } + } + 10 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "IF YOU LIKE!").also { + stage = END_DIALOGUE + InteractionListeners.run(NPCs.GUARD_5489, IntType.NPC, "watch-shouting", player, npc) + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUARD_5489, NPCs.GUARD_5490) + } + +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/IceTrollJatizsoCaves.kt b/Server/src/main/content/region/fremennik/jatizso/handlers/IceTrollJatizsoCaves.kt similarity index 79% rename from Server/src/main/kotlin/rs09/game/node/entity/npc/other/IceTrollJatizsoCaves.kt rename to Server/src/main/content/region/fremennik/jatizso/handlers/IceTrollJatizsoCaves.kt index 3cbd154a5..99820f64c 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/IceTrollJatizsoCaves.kt +++ b/Server/src/main/content/region/fremennik/jatizso/handlers/IceTrollJatizsoCaves.kt @@ -1,12 +1,10 @@ -package rs09.game.node.entity.npc.other +package content.region.fremennik.jatizso.handlers -import api.ContentAPI +import core.api.* 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 -import rs09.game.system.SystemLogger @Initializable class IceTrollJatizsoCaves : AbstractNPC { @@ -23,7 +21,7 @@ class IceTrollJatizsoCaves : AbstractNPC { } override fun tick() { - val nearbyMiner = ContentAPI.findLocalNPC(this, NPCs.MINER_5497) ?: return super.tick() + val nearbyMiner = findLocalNPC(this, NPCs.MINER_5497) ?: return super.tick() if(!inCombat() && nearbyMiner.location.withinDistance(location, 8)){ attack(nearbyMiner) } diff --git a/Server/src/main/content/region/fremennik/jatizso/handlers/JatizsoListeners.kt b/Server/src/main/content/region/fremennik/jatizso/handlers/JatizsoListeners.kt new file mode 100644 index 000000000..65824ab40 --- /dev/null +++ b/Server/src/main/content/region/fremennik/jatizso/handlers/JatizsoListeners.kt @@ -0,0 +1,153 @@ +package content.region.fremennik.jatizso.handlers + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.system.task.Pulse +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +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) + val NORTH_GATE_ZONE = ZoneBorders(2414,3822,2417,3825) + val WEST_GATE_ZONE = ZoneBorders(2386,3797,2390,3801) + val SOUTH_GAE_ZONE = ZoneBorders(2411,3795,2414,3799) + val BELL = 21394 + val TOWER_GUARDS = intArrayOf(NPCs.GUARD_5490, NPCs.GUARD_5489) + val GUARDS = intArrayOf(NPCs.GUARD_5491, NPCs.GUARD_5492) + val KING_CHEST = intArrayOf(21299,21300) + val LINES = arrayOf( + arrayOf( + "YOU WOULDN'T KNOW HOW TO FIGHT A TROLL IF IT CAME UP AND BIT YER ARM OFF", + "YAK LOVERS", + "CALL THAT A TOWN? I'VE SEEN BETTER HAMLETS!", + "AND YOUR FATHER SMELLED OF WINTERBERRIES!", + "WOODEN BRIDGES ARE RUBBISH!", + "OUR KING'S BETTER THAN YOUR BURGHER!", + ), + arrayOf( + "YOU WOULDN'T KNOW HOW TO SHAVE A YAK IF YOUR LIFE DEPENDED ON IT", + "MISERABLE LOSERS", + "YOUR MOTHER WAS A HAMSTER!", + "AT LEAST WE HAVE SOMETHING OTHER THAN SMELLY FISH!", + "AT LEAST WE CAN COOK!", + ) + ) + override fun defineListeners() { + on(GATES_CLOSED, IntType.SCENERY, "open"){ player, node -> + if(NORTH_GATE_ZONE.insideBorder(player)){ + if(node.id == GATES_CLOSED.first()){ + val other = getScenery(node.location.transform(1, 0, 0)) ?: return@on true + replaceScenery(node.asScenery(), node.id + 1, -1, Direction.EAST) + replaceScenery(other.asScenery(), other.id - 1, -1, Direction.NORTH_EAST) + } else { + val other = getScenery(node.location.transform(-1, 0, 0)) ?: return@on true + replaceScenery(node.asScenery(), node.id + 1, -1, Direction.NORTH_EAST) + replaceScenery(other.asScenery(), other.id, -1, Direction.EAST) + } + } else if(WEST_GATE_ZONE.insideBorder(player)){ + if(node.id == GATES_CLOSED.first()){ + val other = getScenery(node.location.transform(0, 1, 0)) ?: return@on true + replaceScenery(node.asScenery(), node.id + 1, -1, Direction.WEST) + replaceScenery(other.asScenery(), other.id + 1, -1, Direction.NORTH) + } else { + val other = getScenery(node.location.transform(0, -1, 0)) ?: return@on true + replaceScenery(node.asScenery(), node.id + 1, -1, Direction.NORTH) + replaceScenery(other.asScenery(), other.id + 1, -1, Direction.WEST) + } + } else if(SOUTH_GAE_ZONE.insideBorder(player)){ + if(node.id == GATES_CLOSED.first()){ + val other = getScenery(node.location.transform(-1, 0, 0)) ?: return@on true + replaceScenery(node.asScenery(), node.id + 1, -1, Direction.SOUTH) + replaceScenery(other.asScenery(), other.id - 1, -1, Direction.EAST) + } else { + val other = getScenery(node.location.transform(1, 0, 0)) ?: return@on true + replaceScenery(node.asScenery(), node.id + 1, -1, Direction.EAST) + replaceScenery(other.asScenery(), other.id, -1, Direction.SOUTH) + } + } + playAudio(player, Sounds.NICEDOOR_OPEN_81) + return@on true + } + + on(TOWER_GUARDS, IntType.NPC, "watch-shouting"){ player, _ -> + val local = findLocalNPC(player, NPCs.GUARD_5489) + lock(player, 200) + face(local!!, Location.create(2371, 3801, 2)) + local.asNpc().isRespawn = false + submitIndividualPulse(player, object : Pulse(4){ + var id = NPCs.GUARD_5489 + var counter = 0 + val other = findLocalNPC(player, getOther(NPCs.GUARD_5489)) + + override fun start() { + other?.isRespawn = false + } + + override fun pulse(): Boolean { + val npc = findLocalNPC(player, id) ?: return false + val index = when(id){ + NPCs.GUARD_5489 -> 0 + else -> 1 + } + if(index == 1 && counter == 5) return true + sendChat(npc, LINES[index][counter]) + if(npc.id != NPCs.GUARD_5489) counter++ + id = getOther(id) + return false + } + + override fun stop() { + unlock(player) + other?.isRespawn = true + local.asNpc().isRespawn = true + super.stop() + } + + fun getOther(npc: Int): Int{ + return when(npc){ + NPCs.GUARD_5489 -> NPCs.GUARD_5490 + NPCs.GUARD_5490 -> NPCs.GUARD_5489 + else -> NPCs.GUARD_5489 + } + } + }) + return@on true + } + + on(BELL, IntType.SCENERY, "ring-bell"){ player, _ -> + playAudio(player, Sounds.STEEL_15) + sendMessage(player, "You ring the warning bell, but everyone ignores it!") + return@on true + } + + on(GUARDS, IntType.NPC, "talk-to"){ player, node -> + player.dialogueInterpreter.open(LeftieRightieDialogue(), NPC(NPCs.GUARD_5491)) + return@on true + } + + on(KING_CHEST, IntType.SCENERY, "open"){ player, node -> + sendPlayerDialogue(player, "I probably shouldn't mess with that.", core.game.dialogue.FacialExpression.HALF_THINKING) + return@on true + } + + setDest(IntType.NPC, NPCs.MAGNUS_GRAM_5488){ _, _ -> + return@setDest Location.create(2416, 3801, 0) + } + + //Climb handling for the ladders in the towers around the city walls. + addClimbDest(Location.create(2388, 3804, 0),Location.create(2387, 3804, 2)) + addClimbDest(Location.create(2388, 3804, 2),Location.create(2387, 3804, 0)) + addClimbDest(Location.create(2388, 3793, 0),Location.create(2387, 3793, 2)) + addClimbDest(Location.create(2388, 3793, 2),Location.create(2387, 3793, 0)) + addClimbDest(Location.create(2410, 3823, 0),Location.create(2410, 3824, 2)) + addClimbDest(Location.create(2410, 3823, 2),Location.create(2410, 3824, 0)) + addClimbDest(Location.create(2421, 3823, 0),Location.create(2421, 3824, 2)) + addClimbDest(Location.create(2421, 3823, 2),Location.create(2421, 3824, 0)) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/BabaYagaDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/BabaYagaDialogue.kt new file mode 100644 index 000000000..b597ce416 --- /dev/null +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/BabaYagaDialogue.kt @@ -0,0 +1,55 @@ +package content.region.fremennik.lunarisle.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 + * Does not include Lunar Diplomacy dialogue + */ + +@Initializable +class BabaYagaDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npc(FacialExpression.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(FacialExpression.ASKING, "It's a very interesting house you have here. Does he have a name?").also { stage = 10 } + 3 -> end() + } + + 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() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BabaYagaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BABA_YAGA_4513) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/BouquetMacHyacinthDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/BouquetMacHyacinthDialogue.kt new file mode 100644 index 000000000..29f063219 --- /dev/null +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/BouquetMacHyacinthDialogue.kt @@ -0,0 +1,77 @@ +package content.region.fremennik.lunarisle.dialogue + +import core.api.teleport +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.TeleportManager +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + +/** + * @author qmqz + */ + +@Initializable +class BouquetMacHyacinthDialogue(player: Player? = null) : DialoguePlugin(player){ + + private val Rellekka = Location.create(2663, 3644, 0) + private var teled = false + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + if (!teled) { + if (player.inventory.contains(Items.SEAL_OF_PASSAGE_9083, 1) || player.equipment.contains(Items.SEAL_OF_PASSAGE_9083, 1)) { + player(FacialExpression.FRIENDLY, "Hi! What are you up to?").also { stage = 0 } + } else { + player(FacialExpression.FRIENDLY, "Hi, I...").also { stage = 5 } + } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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() + } + return true + } + + private fun wait1() { + Executors.newSingleThreadScheduledExecutor().schedule({ + if (player.location.isInRegion(Rellekka.regionId)) { + 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?): DialoguePlugin { + return BouquetMacHyacinthDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BOUQUET_MAC_HYACINTH_4526) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/CabinBoyDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/CabinBoyDialogue.kt new file mode 100644 index 000000000..4ce80aecf --- /dev/null +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/CabinBoyDialogue.kt @@ -0,0 +1,72 @@ +package content.region.fremennik.lunarisle.dialogue + +import core.api.addItemOrDrop +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 + +/** + * @author qmqz + * Must have Lunar Diplomacy completed for the emerald lens + */ + +@Initializable +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(FacialExpression.FRIENDLY, "So you've plucked up the courage to come and confront that girl!").also { stage = 0 } + } else { + player(FacialExpression.FRIENDLY, "Hi.").also { stage = 20 } + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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(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(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(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?): DialoguePlugin { + return CabinBoyDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CABIN_BOY_4539) + } +} diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/MelenaMoonlanderDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/MelenaMoonlanderDialogue.kt new file mode 100644 index 000000000..4992dbf2e --- /dev/null +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/MelenaMoonlanderDialogue.kt @@ -0,0 +1,64 @@ +package content.region.fremennik.lunarisle.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 MelenaMoonlanderDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> 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(FacialExpression.HALF_ASKING, "I have a question...").also { stage = 11 } + 3 -> player(FacialExpression.FRIENDLY, "I'm good thanks, bye.").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?): DialoguePlugin { + return MelenaMoonlanderDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MELANA_MOONLANDER_4516) + } +} diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/PaulinePolarisDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/PaulinePolarisDialogue.kt new file mode 100644 index 000000000..a0ede3811 --- /dev/null +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/PaulinePolarisDialogue.kt @@ -0,0 +1,48 @@ +package content.region.fremennik.lunarisle.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 + * Does not include Lunar Diplomacy dialogue + */ + +@Initializable +class PaulinePolarisDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npc(FacialExpression.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?): DialoguePlugin { + return PaulinePolarisDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.PAULINE_POLARIS_4514) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/RimaeSirsalisDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/RimaeSirsalisDialogue.kt new file mode 100644 index 000000000..6046a7931 --- /dev/null +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/RimaeSirsalisDialogue.kt @@ -0,0 +1,62 @@ +package content.region.fremennik.lunarisle.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 RimaeSirsalisDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("What can you sell me?", "Do you have any use for suqah teeth or hides?", "It's a very interesting island you have here.", "I'm good thanks, bye.").also { stage++ } + + 1 -> when (buttonId) { + 1 -> end().also { npc.openShop(player) } + 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(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(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?): DialoguePlugin { + return RimaeSirsalisDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.RIMAE_SIRSALIS_4518) + } +} diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/SeleneDialog.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/SeleneDialog.kt new file mode 100644 index 000000000..8e0f00f75 --- /dev/null +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/SeleneDialog.kt @@ -0,0 +1,79 @@ +package content.region.fremennik.lunarisle.dialogue + +import core.api.teleport +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.TeleportManager +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + +/** + * @author qmqz + */ + +@Initializable +class SeleneDialog(player: Player? = null) : DialoguePlugin(player){ + + private val Rellekka = Location.create(2663, 3644, 0) + private var teled = false + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + if (!teled) { + if (player.inventory.contains(Items.SEAL_OF_PASSAGE_9083, 1) || player.equipment.contains(Items.SEAL_OF_PASSAGE_9083, 1)) { + player(FacialExpression.FRIENDLY, "Can you tell me a bit about your people?").also { stage = 0; } + } else { + player(FacialExpression.FRIENDLY, "Hi, I...").also { stage = 10 } + } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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() + } + return true + } + + private fun wait1() { + Executors.newSingleThreadScheduledExecutor().schedule({ + if (player.location.isInRegion(Rellekka.regionId)) { + 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?): DialoguePlugin { + return SeleneDialog(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SELENE_4517) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/city/LunarIslePlugin.java b/Server/src/main/content/region/fremennik/lunarisle/handlers/LunarIslePlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/city/LunarIslePlugin.java rename to Server/src/main/content/region/fremennik/lunarisle/handlers/LunarIslePlugin.java index 0931271a5..42f28bc4b 100644 --- a/Server/src/main/java/core/game/interaction/city/LunarIslePlugin.java +++ b/Server/src/main/content/region/fremennik/lunarisle/handlers/LunarIslePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.city; +package content.region.fremennik.lunarisle.handlers; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; diff --git a/Server/src/main/java/core/game/content/dialogue/AnnaIsaakson.java b/Server/src/main/content/region/fremennik/neitiznot/dialogue/AnnaIsaakson.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/AnnaIsaakson.java rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/AnnaIsaakson.java index 5e5ffe51d..2585f7d63 100644 --- a/Server/src/main/java/core/game/content/dialogue/AnnaIsaakson.java +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/AnnaIsaakson.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.fremennik.neitiznot.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.plugin.Initializable; diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/FridleifShieldsonDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/FridleifShieldsonDialogue.kt similarity index 80% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/FridleifShieldsonDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/FridleifShieldsonDialogue.kt index c3e46ded2..c94e5c096 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/FridleifShieldsonDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/FridleifShieldsonDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 -import rs09.tools.END_DIALOGUE @Initializable class FridleifShieldsonDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/GunnarHoldstromDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/GunnarHoldstromDialogue.kt similarity index 86% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/GunnarHoldstromDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/GunnarHoldstromDialogue.kt index a8a241124..5a2e9f1c2 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/GunnarHoldstromDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/GunnarHoldstromDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE @Initializable class GunnarHoldstromDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/JofridrMordstatterDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/JofridrMordstatterDialogue.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/JofridrMordstatterDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/JofridrMordstatterDialogue.kt index ea658b0cd..3188fd345 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/JofridrMordstatterDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/JofridrMordstatterDialogue.kt @@ -1,12 +1,12 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE @Initializable class JofridrMordstatterDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/KjedeligUppsenDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/KjedeligUppsenDialogue.kt similarity index 81% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/KjedeligUppsenDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/KjedeligUppsenDialogue.kt index f396415aa..e0e0b6416 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/KjedeligUppsenDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/KjedeligUppsenDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 -import rs09.tools.END_DIALOGUE @Initializable class KjedeligUppsenDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/LisseIsaaksonDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/LisseIsaaksonDialogue.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/LisseIsaaksonDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/LisseIsaaksonDialogue.kt index dd24b0e1b..b99892d90 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/LisseIsaaksonDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/LisseIsaaksonDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE @Initializable class LisseIsaaksonDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/java/core/game/content/zone/neitiznot/MawnisBurowgarDialogue.java b/Server/src/main/content/region/fremennik/neitiznot/dialogue/MawnisBurowgarDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/zone/neitiznot/MawnisBurowgarDialogue.java rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/MawnisBurowgarDialogue.java index d341a0c1d..29ae5a1b1 100644 --- a/Server/src/main/java/core/game/content/zone/neitiznot/MawnisBurowgarDialogue.java +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/MawnisBurowgarDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.zone.neitiznot; +package content.region.fremennik.neitiznot.dialogue; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/MortenHoldstromDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/MortenHoldstromDialogue.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/MortenHoldstromDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/MortenHoldstromDialogue.kt index c1dd5c240..5d44c591c 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/MortenHoldstromDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/MortenHoldstromDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE @Initializable class MortenHoldstromDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/SlugHemliggsenDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/SlugHemliggsenDialogue.kt similarity index 80% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/SlugHemliggsenDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/SlugHemliggsenDialogue.kt index 418b55d14..5959779a3 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/SlugHemliggsenDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/SlugHemliggsenDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 -import rs09.tools.END_DIALOGUE @Initializable class SlugHemliggsenDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/ThakkradSigmundsonDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradSigmundsonDialogue.kt similarity index 81% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/ThakkradSigmundsonDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradSigmundsonDialogue.kt index 37eaa75dd..6ecb15192 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/ThakkradSigmundsonDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradSigmundsonDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 -import rs09.tools.END_DIALOGUE @Initializable class ThakkradSigmundsonDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/java/core/game/content/zone/neitiznot/ThakkradYakDialogue.java b/Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradYakDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/zone/neitiznot/ThakkradYakDialogue.java rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradYakDialogue.java index 09a556bd5..340cb2b6c 100644 --- a/Server/src/main/java/core/game/content/zone/neitiznot/ThakkradYakDialogue.java +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradYakDialogue.java @@ -1,7 +1,7 @@ -package core.game.content.zone.neitiznot; +package content.region.fremennik.neitiznot.dialogue; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/TrogenKonungardeDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/TrogenKonungardeDialogue.kt similarity index 81% rename from Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/TrogenKonungardeDialogue.kt rename to Server/src/main/content/region/fremennik/neitiznot/dialogue/TrogenKonungardeDialogue.kt index 244e9f499..83eec864d 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/neitiznot/TrogenKonungardeDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/TrogenKonungardeDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue.region.neitiznot +package content.region.fremennik.neitiznot.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 -import rs09.tools.END_DIALOGUE @Initializable class TrogenKonungardeDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/content/region/fremennik/neitiznot/handlers/NeitiznotListeners.kt b/Server/src/main/content/region/fremennik/neitiznot/handlers/NeitiznotListeners.kt new file mode 100644 index 000000000..26fc6f658 --- /dev/null +++ b/Server/src/main/content/region/fremennik/neitiznot/handlers/NeitiznotListeners.kt @@ -0,0 +1,39 @@ +package content.region.fremennik.neitiznot.handlers + +import core.api.* +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.map.Location +import core.game.world.map.zone.MapZone +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class NeitiznotListeners : InteractionListener { + val STUMP = 21305 + + override fun defineListeners() { + + on(STUMP, IntType.SCENERY, "cut-wood"){ player, _ -> + sendPlayerDialogue(player, "I should probably leave this alone.", core.game.dialogue.FacialExpression.HALF_THINKING) + return@on true + } + + val zone = object : MapZone("Yakzone", true){ + override fun handleUseWith(player: Player, used: Item?, with: Node?): Boolean { + if(with is NPC && with.id == NPCs.YAK_5529){ + sendMessage(player, "The cow doesn't want that.") + return true + } + return false + } + } + + registerMapZone(zone, ZoneBorders(2313,3786,2331,3802)) + addClimbDest(Location.create(2363, 3799, 0), Location.create(2364, 3799, 2)) + addClimbDest(Location.create(2363, 3799, 2), Location.create(2362, 3799, 0)) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/zone/neitiznot/NeitiznotZone.java b/Server/src/main/content/region/fremennik/neitiznot/handlers/NeitiznotZone.java similarity index 78% rename from Server/src/main/java/core/game/content/zone/neitiznot/NeitiznotZone.java rename to Server/src/main/content/region/fremennik/neitiznot/handlers/NeitiznotZone.java index 577471667..a609cbb20 100644 --- a/Server/src/main/java/core/game/content/zone/neitiznot/NeitiznotZone.java +++ b/Server/src/main/content/region/fremennik/neitiznot/handlers/NeitiznotZone.java @@ -1,5 +1,7 @@ -package core.game.content.zone.neitiznot; +package content.region.fremennik.neitiznot.handlers; +import content.region.fremennik.neitiznot.dialogue.MawnisBurowgarDialogue; +import content.region.fremennik.neitiznot.dialogue.ThakkradYakDialogue; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -8,7 +10,7 @@ import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBuilder; import core.plugin.Plugin; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles the neitiznot zone. @@ -27,7 +29,7 @@ public class NeitiznotZone extends MapZone implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); - PluginManager.definePlugins(new MawnisBurowgarDialogue(), new ThakkradYakDialogue(), new YakArmourPlugin(), new YakArmourPlugin()); + ClassScanner.definePlugins(new MawnisBurowgarDialogue(), new ThakkradYakDialogue(), new YakArmourPlugin(), new YakArmourPlugin()); return this; } diff --git a/Server/src/main/java/core/game/content/zone/neitiznot/YakArmourPlugin.java b/Server/src/main/content/region/fremennik/neitiznot/handlers/YakArmourPlugin.java similarity index 88% rename from Server/src/main/java/core/game/content/zone/neitiznot/YakArmourPlugin.java rename to Server/src/main/content/region/fremennik/neitiznot/handlers/YakArmourPlugin.java index 8ac202b67..135470838 100644 --- a/Server/src/main/java/core/game/content/zone/neitiznot/YakArmourPlugin.java +++ b/Server/src/main/content/region/fremennik/neitiznot/handlers/YakArmourPlugin.java @@ -1,10 +1,10 @@ -package core.game.content.zone.neitiznot; +package content.region.fremennik.neitiznot.handlers; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; +import core.game.dialogue.SkillDialogueHandler; +import core.game.dialogue.SkillDialogueHandler.SkillDialogue; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.crafting.armour.LeatherCrafting; +import content.global.skill.crafting.armour.LeatherCrafting; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; import core.game.node.entity.player.Player; @@ -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/kotlin/rs09/game/node/entity/npc/other/YakNPC.kt b/Server/src/main/content/region/fremennik/neitiznot/handlers/YakNPC.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/node/entity/npc/other/YakNPC.kt rename to Server/src/main/content/region/fremennik/neitiznot/handlers/YakNPC.kt index c26ba2803..d7ab41342 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/YakNPC.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/handlers/YakNPC.kt @@ -1,4 +1,4 @@ -package rs09.game.node.entity.npc.other +package content.region.fremennik.neitiznot.handlers import core.game.node.entity.npc.AbstractNPC import core.game.world.map.Location diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/BjornAndEldgrimDialogues.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/BjornAndEldgrimDialogues.kt new file mode 100644 index 000000000..47b7a42e5 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/BjornAndEldgrimDialogues.kt @@ -0,0 +1,53 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class BjornAndEldgrimDialogues(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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(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?): DialoguePlugin { + return BjornAndEldgrimDialogues(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BJORN_1284, NPCs.ELDGRIM_1285) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/BlaninDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/BlaninDialogue.kt new file mode 100644 index 000000000..52a931e01 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/BlaninDialogue.kt @@ -0,0 +1,45 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class BlaninDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + player(FacialExpression.FRIENDLY, "Good day.").also { stage = 0 } + } else { + player(FacialExpression.FRIENDLY, "That's one less thing to worry about.").also { stage = 10 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return BlaninDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BLANIN_2940) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt new file mode 100644 index 000000000..c54085080 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt @@ -0,0 +1,44 @@ +package content.region.fremennik.rellekka.dialogue + +import content.region.fremennik.rellekka.quest.thefremenniktrials.CouncilWorkerFTDialogue +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(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) in 1..99){ + player.dialogueInterpreter.open((CouncilWorkerFTDialogue(1))) + } + else if(player.achievementDiaryManager.getDiary(DiaryType.FREMENNIK).isComplete(0, true)){ + player.dialogueInterpreter.open((CouncilWorkerDiaryDialogue())) + } + else{ + player(FacialExpression.FRIENDLY,"Hello.") + npc(FacialExpression.FRIENDLY,"How do. You planning on crossing this here bridge and heading up to Rellekka then?").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> end() + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return CouncilWorkerDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1287) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDiaryDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDiaryDialogue.kt new file mode 100644 index 000000000..a4600a34b --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDiaryDialogue.kt @@ -0,0 +1,56 @@ +package content.region.fremennik.rellekka.dialogue + +import core.api.* +import core.game.dialogue.FacialExpression +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(FacialExpression.FRIENDLY, "About my achievement diary...");stage++ + } + 1 -> { + 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 { + sendItemDialogue( + it, + Items.FREMENNIK_SEA_BOOTS_1_14571, + "The worker hands you some old sea boots." + );stage++ + } + } + 3 -> { + npc( + COUNCIL_WORKER, + "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 + } + } + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/DronDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/DronDialogue.kt new file mode 100644 index 000000000..d2f20660e --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/DronDialogue.kt @@ -0,0 +1,45 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class DronDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, Quests.MAKING_HISTORY)) { + player(FacialExpression.FRIENDLY, "Excuse me.").also { stage = 0 } + } else { + player(FacialExpression.FRIENDLY, "Excuse me.").also { stage = 10 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return DronDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DRON_2939) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/FishmongerRellekkaDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/FishmongerRellekkaDialogue.kt new file mode 100644 index 000000000..ae896659c --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/FishmongerRellekkaDialogue.kt @@ -0,0 +1,44 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class FishmongerRellekkaDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } + } else { + npcl(FacialExpression.FRIENDLY,"Hello there, ${player.getAttribute("fremennikname","fremmyname")}. Looking for fresh fish?").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> end().also { npc.openShop(player) } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FishmongerRellekkaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FISH_MONGER_1315) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/FurTraderDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/FurTraderDialogue.kt new file mode 100644 index 000000000..a117d29ed --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/FurTraderDialogue.kt @@ -0,0 +1,44 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class FurTraderDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } + } else { + npcl(FacialExpression.FRIENDLY,"Welcome back, ${player.getAttribute("fremennikname","fremmyname")}. Have you seen the furs I have today?").also { stage = 10 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 10 -> end().also { npc.openShop(player) } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FurTraderDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FUR_TRADER_1316) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/HuntingExpertRellekkaDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/HuntingExpertRellekkaDialogue.kt new file mode 100644 index 000000000..baa10c58f --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/HuntingExpertRellekkaDialogue.kt @@ -0,0 +1,89 @@ +package content.region.fremennik.rellekka.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.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +/** + * Represents the dialogue plugin used for the Hunting Expert in the Rellekkan Hunter area + * @author vddcore + */ +@Initializable +class HuntingExpertRellekkaDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player): DialoguePlugin { + return HuntingExpertRellekkaDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + npcl( + FacialExpression.HAPPY, + "Good day, you seem to have a keen eye. " + + "Maybe even some hunter's blood in that body of yours?" + ) + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> showTopics( + Topic(FacialExpression.ASKING, "Is there anything you can teach me?", 1), + Topic(FacialExpression.NEUTRAL, "Nevermind.", END_DIALOGUE) + ) + + 1 -> npcl( + FacialExpression.FRIENDLY, + "I can teach you how to hunt." + ).also { stage++ } + + 2 -> playerl( + FacialExpression.THINKING, + "What kind of creatures can I hunt?" + ).also { stage++ } + + 3 -> npcl( + FacialExpression.FRIENDLY, + "Many creatures in many ways. You need to make some traps " + + "and catch birds!" + ).also { stage++ } + + 4 -> playerl( + FacialExpression.HALF_ASKING, + "Birds?" + ).also { stage++ } + + 5 -> npcl( + FacialExpression.ANNOYED, + "Yes, birds! Like the ones here!" + ).also { stage++ } + + 6 -> npcl( + FacialExpression.ANNOYED, + "Look. Just... Get some hunting gear and go set up some traps." + ).also { stage++ } + + 7 -> playerl( + FacialExpression.HALF_ROLLING_EYES, + "Is that it?" + ).also { stage++ } + + 8 -> npcl( + FacialExpression.FURIOUS, + "JUST GO DO IT!" + ).also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray + = intArrayOf(NPCs.HUNTING_EXPERT_5112) +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/IngridHradsonDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/IngridHradsonDialogue.kt new file mode 100644 index 000000000..ba86e5be8 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/IngridHradsonDialogue.kt @@ -0,0 +1,50 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class IngridHradsonDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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(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?): DialoguePlugin { + return IngridHradsonDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.INGRID_HRADSON_3696) + } +} 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 new file mode 100644 index 000000000..296b0a7fc --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/LonghallBouncerDialogue.kt @@ -0,0 +1,45 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class LonghallBouncerDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + npcl(FacialExpression.ANNOYED, "Hey, outerlander. You can't go through there. Talent only, backstage.").also { stage = END_DIALOGUE } + } else{ + npcl(FacialExpression.ANNOYED, "You can't go through there. Talent only, backstage.").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return LonghallBouncerDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LONGHALL_BOUNCER_1278) + } +} diff --git a/Server/src/main/java/core/game/content/zone/rellekka/MariaGunnarsDialogue.java b/Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java similarity index 80% rename from Server/src/main/java/core/game/content/zone/rellekka/MariaGunnarsDialogue.java rename to Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java index 6c96cdf01..b998fa8af 100644 --- a/Server/src/main/java/core/game/content/zone/rellekka/MariaGunnarsDialogue.java +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java @@ -1,11 +1,13 @@ -package core.game.content.zone.rellekka; +package content.region.fremennik.rellekka.dialogue; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.world.map.Location; -import rs09.game.util.region.rellekka.RellekkaDestination; -import rs09.game.util.region.rellekka.RellekkaUtils; +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. @@ -63,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 new file mode 100644 index 000000000..9636f23ae --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/ReesoDialogue.kt @@ -0,0 +1,42 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class ReesoDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + npcl(FacialExpression.ANNOYED, "Please do not disturb me, outerlander. I have much to do.").also { stage = END_DIALOGUE } + } else { + npcl(FacialExpression.STRUGGLE, "Sorry, ${player.getAttribute("fremennikname","fremmyname")}, I must get on with my work.").also { stage = END_DIALOGUE } + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ReesoDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.REESO_3116) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/TalkToChiefDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/TalkToChiefDialogue.kt new file mode 100644 index 000000000..6c371733a --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/TalkToChiefDialogue.kt @@ -0,0 +1,48 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + * 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) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + npcl(FacialExpression.ANNOYED, "I cannot speak to you outerlander! Talk to Brundt, the Chieftain!").also { stage = END_DIALOGUE } + } else { + player(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npc(FacialExpression.FRIENDLY, "Hello to you, too!").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return TalkToChiefDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BORROKAR_1307, NPCs.FREIDIR_1306, NPCs.INGA_1314, + NPCs.JENNELLA_1312, NPCs.LANZIG_1308, NPCs.PONTAK_1309, + NPCs.SASSILIK_1313) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/VolfOlasfsonDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/VolfOlasfsonDialogue.kt new file mode 100644 index 000000000..315312236 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/VolfOlasfsonDialogue.kt @@ -0,0 +1,49 @@ +package content.region.fremennik.rellekka.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 org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class VolfOlasfsonDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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(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?): DialoguePlugin { + return VolfOlasfsonDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.VOLF_OLAFSON_3695) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/WaitingOnTheShowDialogues.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/WaitingOnTheShowDialogues.kt new file mode 100644 index 000000000..d9b9fd99d --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/WaitingOnTheShowDialogues.kt @@ -0,0 +1,35 @@ +package content.region.fremennik.rellekka.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 + +/** + * @author qmqz + */ + +@Initializable +class WaitingOnTheShowDialogues(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.ANNOYED, "Shhh! I'm waiting for the show!").also { stage = END_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return WaitingOnTheShowDialogues(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FRIDGEIR_1277, NPCs.OSPAK_1274, NPCs.STYRMIR_1275) + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/handlers/FremennikGuards.kt b/Server/src/main/content/region/fremennik/rellekka/handlers/FremennikGuards.kt new file mode 100644 index 000000000..5e56fa013 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/handlers/FremennikGuards.kt @@ -0,0 +1,32 @@ +package content.region.fremennik.rellekka.handlers + +import core.api.* +import core.game.node.entity.npc.AbstractNPC +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class FremennikGuards : AbstractNPC { + //Constructor spaghetti because Arios I guess + constructor() : super(NPCs.GUARD_5489, null, true) {} + private constructor(id: Int, location: Location) : super(id, location) {} + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + return FremennikGuards(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUARD_5489, NPCs.GUARD_5490) + } + + override fun tick() { + if(this.isRespawn) { + when (id) { + NPCs.GUARD_5489 -> if (getWorldTicks() % 5 == 0) sendChat(this, "JATIZSO!") + NPCs.GUARD_5490 -> if (getWorldTicks() % 8 == 0) sendChat(this, "NEITIZNOT!") + } + } + super.tick() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt new file mode 100644 index 000000000..cac9e67e8 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt @@ -0,0 +1,79 @@ +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 content.data.Quests + +/** + * File to be used for anything Rellekka related. + * Handles the snow kebbit stairs up and down. + * @author Ceikry + */ + +class RellekkaListeners : InteractionListener { + + val UP1A = Location.create(2715, 3798, 0) + val UP1B = Location.create(2716, 3798, 0) + val UP2A = Location.create(2726, 3801, 0) + val UP2B = Location.create(2727, 3801, 0) + + val DOWN1A = Location.create(2715, 3802, 1) + val DOWN1B = Location.create(2716, 3802, 1) + val DOWN2A = Location.create(2726, 3805, 1) + val DOWN2B = Location.create(2727, 3805, 1) + val STAIRS = intArrayOf(19690,19691) + + override fun defineListeners() { + on(STAIRS, IntType.SCENERY, "ascend","descend"){ player, node -> + if(player.location.y < 3802){ + player.properties.teleportLocation = when(player.location.x){ + 2715 -> DOWN1A + 2716 -> DOWN1B + 2726 -> DOWN2A + 2727 -> DOWN2B + else -> player.location + } + } else { + player.properties.teleportLocation = when(player.location.x) { + 2715 -> UP1A + 2716 -> UP1B + 2726 -> UP2A + 2727 -> UP2B + else -> player.location + } + } + return@on true + } + + 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 + } + } +} diff --git a/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaUtils.kt b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaUtils.kt new file mode 100644 index 000000000..9f8da9ca1 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaUtils.kt @@ -0,0 +1,35 @@ +package content.region.fremennik.rellekka.handlers + +import core.api.* +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.map.Location +import org.rs09.consts.Components + +object RellekkaUtils { + @JvmStatic + fun sail(player: Player, destination: RellekkaDestination){ + lock(player, 100) + openOverlay(player, 115) + openInterface(player, Components.MISC_SHIPJOURNEY_224) + animateInterface(player, Components.MISC_SHIPJOURNEY_224, 7, destination.shipAnim) + + val animDuration = animationDuration(getAnimation(destination.shipAnim)) + submitWorldPulse(object : Pulse(animDuration){ + override fun pulse(): Boolean { + teleport(player, destination.destLoc) + closeInterface(player) + closeOverlay(player) + unlock(player) + return true + } + }) + } +} + +enum class RellekkaDestination(val destName: String, val destLoc: Location, val shipAnim: Int){ + RELLEKKA_TO_JATIZSO("Jatizso", Location.create(2421, 3781, 0), 5766), + JATIZSO_TO_RELLEKKA("Rellekka", Location.create(2644, 3710, 0), 5767), + RELLEKKA_TO_NEITIZNOT("Neitiznot",Location(2310, 3782, 0), 5764), + NEITIZNOT_TO_RELLEKKA("Rellekka", Location(2644, 3710, 0), 5765) +} \ 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 new file mode 100644 index 000000000..4698885bb --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaZone.java @@ -0,0 +1,209 @@ +package content.region.fremennik.rellekka.handlers; + +import content.region.fremennik.rellekka.dialogue.MariaGunnarsDialogue; +import core.cache.def.impl.SceneryDefinition; +import core.game.system.task.Pulse; +import core.plugin.Initializable; +import content.global.skill.agility.AgilityHandler; +import core.game.interaction.Option; +import core.game.interaction.OptionHandler; +import core.game.component.Component; +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.scenery.Scenery; +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.Plugin; +import kotlin.Unit; +import core.game.world.GameWorld; +import core.plugin.ClassScanner; + +/** + * Handles the rellekka zone. + * @author Vexia + */ +@Initializable +public final class RellekkaZone extends MapZone implements Plugin { + + /** + * Constructs a new {@code RellekkaZone} {@code Object}. + */ + public RellekkaZone() { + super("rellekka", true); + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ZoneBuilder.configure(this); + ClassScanner.definePlugins(new RellekaOptionHandler(), new MariaGunnarsDialogue()); + ClassScanner.definePlugin(new OptionHandler() { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + return true; + } + + }); + return this; + } + + @Override + public boolean interact(Entity e, Node target, Option option) { + if (e instanceof Player) { + final Player player = (Player) e; + switch (target.getId()) { + case 4306: + case 4310: + case 4309: + case 4304: + case 4308: + player.sendMessage("Only Fremenniks may use this " + target.getName().toLowerCase() + "."); + return true; + /* + * case 1301: player.sendMessage("Only Fremenniks may " + + * (option.equals("Trade") ? "buy clothes here" : + * "change their shoes here") + "."); return true; + */ + //case 4148: + // player.getDialogueInterpreter().sendDialogues(1278, null, "Hey, outerlander. You can't go through there. Talent", "only, backstage."); + // return true; + case 100: + player.getDialogueInterpreter().sendDialogue("You try to open the trapdoor but it won't budge! It looks like the", "trapdoor can only be opened from the other side."); + return true; + case 2435: + case 2436: + case 2437: + case 2438: + if (option.getName().equals("Travel")) { + player.getDialogueInterpreter().open(target.getId(), target, true); + return true; + } + break; + } + } + return super.interact(e, target, option); + } + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public void configure() { + register(new ZoneBorders(2602, 3639, 2739, 3741)); + } + + /** + * Sails a player using the relleka ships. + * @param player the player. + * @param name the name. + * @param destination the destination. + */ + public static void sail(final Player player, final String name, final Location destination) { + player.lock(); + player.getInterfaceManager().open(new Component(224)); + player.logoutListeners.put("rellekka-sail", player1 -> { + player1.setLocation(destination); + 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(destination); + player.getDialogueInterpreter().sendDialogue("The ship arrives at " + name + "."); + player.logoutListeners.remove("rellekka-sail"); + return true; + } + return false; + } + + }); + } + + /** + * Handles options related to relleka. + * @author Vexia + */ + public static final class RellekaOptionHandler extends OptionHandler { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + SceneryDefinition.forId(4616).getHandlers().put("option:cross", this); + SceneryDefinition.forId(4615).getHandlers().put("option:cross", this); + SceneryDefinition.forId(5847).getHandlers().put("option:climb-over", this); + SceneryDefinition.forId(5008).getHandlers().put("option:enter",this); + SceneryDefinition.forId(15116).getHandlers().put("option:climb-down", this); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + switch (option) { + case "cross": + switch (node.getId()) { + case 4616: + case 4615: + crossRellekaBridge(player, (Scenery) node); + break; + } + break; + case "climb-over": + switch (node.getId()) { + case 5847: + AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(0, player.getLocation().getY() <= 3657 ? 3 : -3, 0), Animation.create(839), 20, 1, null, 0); + break; + } + break; + case "enter": + switch(node.getId()){ + case 5008: + player.getProperties().setTeleportLocation(Location.create(2773, 10162, 0)); + break; + } + break; + case "climb-down": + switch(node.getId()){ + case 15116: + player.getProperties().setTeleportLocation(Location.create(2509, 10245, 0)); + break; + } + break; + } + return true; + } + + /** + * Crosses the relleka bridge. + * @param player the player. + * @param node the node. + */ + private void crossRellekaBridge(Player player, Scenery node) { + boolean east = node.getId() == 4616; + player.lock(2); + if (player.getLocation().equals(node.getLocation())) { + AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(east ? -2 : 2, 0, 0), Animation.create(1115), 20, 1.0, null, 1); + return; + } + AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(east ? -1 : 1, 0, 0), ForceMovement.WALK_ANIMATION, 10, 0.0, null); + AgilityHandler.forceWalk(player, -1, player.getLocation().transform(east ? -1 : 1, 0, 0), player.getLocation().transform(east ? -3 : 3, 0, 0), Animation.create(1115), 20, 1.0, null, 1); + } + + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/RockCrabNPC.java b/Server/src/main/content/region/fremennik/rellekka/handlers/RockCrabNPC.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/other/RockCrabNPC.java rename to Server/src/main/content/region/fremennik/rellekka/handlers/RockCrabNPC.java index 4641439cc..0c3efc873 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/RockCrabNPC.java +++ b/Server/src/main/content/region/fremennik/rellekka/handlers/RockCrabNPC.java @@ -1,13 +1,11 @@ -package core.game.node.entity.npc.other; +package content.region.fremennik.rellekka.handlers; 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.agg.AggressiveBehavior; import core.game.node.entity.npc.agg.AggressiveHandler; -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.RandomFunction; 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 new file mode 100644 index 000000000..526732374 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/AskeladdenDialogue.kt @@ -0,0 +1,146 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.game.node.entity.player.Player +import core.game.dialogue.DialoguePlugin +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) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + player?.let { + if(player.inventory.contains(3709,1)){ + playerl(FacialExpression.ASKING,"I thought you really liked the long hall?") + stage = 27 + return true + } + else if(player?.getAttribute("sigmund-steps", 0) == 13){ + 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(FacialExpression.ASKING,"I've lost one of the items I was supposed to be trading.") + stage = 35 + return true + } + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + playerl(FacialExpression.HAPPY,"Hello again Askeladden.") + stage = 40 + return true + } + else if (it.questRepository.getStage(Quests.THE_FREMENNIK_TRIALS) > 0) { + player("Hello there.") + stage = 0 + return true + } + else if(it.getAttribute("fremtrials:lalli-talkedto",false)!!){ + player("Hello there. I understand you managed to get some", "golden wool from Lalli?") + stage = 0 + return true + } + else{ + playerl(FacialExpression.HAPPY,"Hello there.") + stage = 55 + return true + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return AskeladdenDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (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(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) + stage = 1000} + + 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(FacialExpression.HAPPY,"That's all you want in return? Sure thing. Here you go.") + stage = 22 + }else stage = 25 + 2 -> stage = 25 + } + 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(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(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(FacialExpression.CHILD_NORMAL,"That's a shame. You should speak to the merchant.").also { stage = 1000 } + + 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(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(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(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(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() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(2439) + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..ac6cc0c24 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ChieftanBrundtDialogue.kt @@ -0,0 +1,351 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +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 +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) : 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(FacialExpression.HAPPY,"I got Sigli's hunting map for you.") + stage = 515 + return true + } + else if(player?.inventory?.contains(3708,1) == true){ + 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(FacialExpression.ASKING,"I've got this trade item. Is it for you?") + stage = 525 + return true + } + if(player?.getAttribute("sigmund-steps",0) == 5){ + 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(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 (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 (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 (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 (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(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(Quests.THE_FREMENNIK_TRIALS)!! == 0) { + npc("Greetings outerlander!") + stage = 0 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + //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(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(FacialExpression.HAPPY,"Nice hat!").also { stage = 15 } + } + 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(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(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 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){ + 1 -> {player("Actually, I would be very interested to hear what you","have to offer."); stage = 310 } + 2 -> {player("No, I'm not interested."); stage = END_DIALOGUE } + } + + //Yes, I am interested in what you have to say. + 310 -> {npc("You would? These are unusual sentiments to hear from","an outerlander! My suggestion was going to be that if","you crave adventure and battle,"); stage++} + 311 -> {npc("and your heart sings for glory, then perhaps you would","be interested in joining our clan, and becoming a","Fremennik yourself?");stage++} + 312 -> {player("What would that involve exactly?");stage++} + 313 -> {npc("Well, there are two ways to become a member of our","clan and call yourself a Fremennik: be born a","Fremennik, or be voted in by our council of elders.");stage++} + 314 -> {player("Well, I think I've missed the first way, but how can I","get the council of elders to vote to let me join your","clan?");stage++} + 315 -> {npc("Well, that I cannot answer myself. You will need to","speak to each of them and see what they require of you","as proof of your dedication.");stage++} + 316 -> {npc("There are twelve council members around this village;","you will need to gain a majority vote of at least seven","councillors in your favor.");stage++} + 317 -> {npc("So, what say you? Give me the word and I will tell all","of my tribe of your intentions, be they yes or nay.");stage++} + 318 -> {options("I want to become a Fremennik!","I don't want to become a Fremennik.");stage++} + 319 -> when(buttonId){ + 1 -> {player("I think I would enjoy the challenge of becoming an","honorary Fremennik. Where and how do I start?");stage = 320} + 2 -> {player("That sounds too complicated to me.");stage = 322} + } + + //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(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} + + //No, I'm not interested + 340 -> TODO("Not implemented yet") + + 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(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(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(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(FacialExpression.ANNOYED,"Not unless it's a map of the hunting grounds.").also { stage = 1000 } + + 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(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(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(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 + } + + 550 -> npcl(FacialExpression.HAPPY,"If you need any help with your trials, I suggest you speak to Askeladden. He is currently doing his own trials of manhood to become a true Fremennik.").also { stage = END_DIALOGUE } + + 560 -> npcl(FacialExpression.HAPPY,"From this day onward, you are outerlander no more! In honour of your acceptance into the Fremennik, you gain a new name: ${player.getAttribute("fremennikname","how did u break this")}.").also { + cleanupAttributes(player) + player.questRepository.getQuest(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?", "Can I have a seal of passage?").also { stage++ } + 601 -> when(buttonId){ + 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(FacialExpression.HAPPY,"Well met, $gender $fName. it is always good to see your face in glorious Rellekka once more!").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(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(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(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(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(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(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(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() + } + return true + } + + + override fun newInstance(player: Player?): DialoguePlugin { + return ChieftanBrundt(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1294) + } + +} + +fun GenerateFremennikName(): String { + val namePrefixes = arrayOf( + "Bal", + "Bar", + "Dal", + "Dar", + "Den", + "Dok", + "Jar", + "Jik", + "Lar", + "Rak", + "Ral", + "Ril", + "Sig", + "Tal", + "Thor", + "Ton" + ) + val nameSuffixes = arrayOf( + "dar", + "dor", + "dur", + "kal", + "kar", + "kir", + "kur", + "lah", + "lak", + "lim", + "lor", + "rak", + "rar", + "tin", + "ton", + "tor", + "vald" + ) + val randomPrefix = Random.nextInt(0, 15) + val randomSuffix = Random.nextInt(0, 16) + + return "${namePrefixes[randomPrefix]}${nameSuffixes[randomSuffix]}" +} + +fun cleanupAttributes(player: Player){ + player.removeAttribute("PeerStarted") + player.removeAttribute("housepuzzlesolved") + player.removeAttribute("fremtrials:peer-vote") + player.removeAttribute("fremtrials:votes") + player.removeAttribute("fremtrials:sigmund-vote") + player.removeAttribute("fremtrials:manni-vote") + player.removeAttribute("fremtrials:swensen-vote") + player.removeAttribute("fremtrials:olaf-accepted") + player.removeAttribute("fremtrials:lalli-talkedto") + player.removeAttribute("fremtrials:askeladden-talkedto") + player.removeAttribute("hasWool") + player.removeAttribute("lyreConcertPlayed") + player.removeAttribute("fremtrials:olaf-vote") + player.removeAttribute("fremtrials:sigli-accepted") + player.removeAttribute("fremtrials:thorvald-vote") + player.removeAttribute("fremtrials:sigli-vote") + player.removeAttribute("riddlesolved") +} 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 new file mode 100644 index 000000000..f9b6223af --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/CouncilWorkerFTDialogue.kt @@ -0,0 +1,53 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.* +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 + +class CouncilWorkerFTDialogue(val questStage: Int, var isBeerInteraction: Boolean = false, val beerId: Int? = null) : DialogueFile(){ + + override fun handle(componentID: Int, buttonID: Int) { + + if(isBeerInteraction){ + when(stage){ + START_DIALOGUE -> {npc(COUNCIL_WORKER,"Oh, thank you much ${if(player!!.isMale) "sir" else "miss"}");stage++} + 1 -> { + npc(COUNCIL_WORKER,"Ta very much like. That'll hit the spot nicely.. Here,","You can have this. I picked it up as a souvenir on me","last holz.") + if(beerId != null){ + if(removeItem(player!!, beerId)){ + addItem(player!!, Items.STRANGE_OBJECT_3713) + } + } else if(removeItem(player!!, Items.BEER_3803) || removeItem(player!!, Items.BEER_1917)) { + addItem(player!!, Items.STRANGE_OBJECT_3713) + } + stage = END_DIALOGUE + } + } + } + + else if(questStage in 1..99){ + when(stage){ + START_DIALOGUE -> + 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() + } + 1 -> {npc(COUNCIL_WORKER,"'fraid not, ${if(player!!.isMale) "sir" else "miss"}."); stage++} + 2 -> {npc(COUNCIL_WORKER,"Say, would you do me a favor? I'm quite parched.","If you bring me a beer, I'll make it worthwhile.");stage++} + 3 -> if(inInventory(player!!,Items.BEER_3803) || inInventory(player!!,Items.BEER_1917)) { + player("Oh, I have one here! Take it.") + stage = 0 + isBeerInteraction = true + } else { + end() + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/Draugen.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/Draugen.kt new file mode 100644 index 000000000..d8bc9b6a9 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/Draugen.kt @@ -0,0 +1,34 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +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.node.item.Item + +class Draugen(val player: Player) : NPC(1279,player.location?.transform(1,0,0)){ + + override fun init() { + super.init() + isRespawn = false + } + + override fun handleTickActions() { + super.handleTickActions() + if(!properties.combatPulse.isAttacking){ + properties.combatPulse.attack(player) + } + if (!player.isActive) { + removeAttribute(player, "fremtrials:draugen-spawned") + this.clear() + } + } + + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + setAttribute(player, "/save:fremtrials:draugen-killed",true) + removeAttribute(player, "fremtrials:draugen-loc") + removeItem(player, Item(3696), Container.INVENTORY) + addItem(player, 3697, 1, Container.INVENTORY) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..c0c3f4f3f --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FishermanDialogue.kt @@ -0,0 +1,88 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.addItem +import core.api.isQuestComplete +import core.api.removeItem +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 content.data.Quests + +@Initializable +class FishermanDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + if(player?.inventory?.contains(3704,1) == true){ + playerl(FacialExpression.HAPPY,"Here. I got you your map.") + stage = 15 + return true + } + else if(player?.inventory?.contains(3703,1) == true){ + 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(FacialExpression.ASKING,"Is this trade item for you?") + stage = 17 + return true + } + if(player?.getAttribute("sigmund-steps", 0) == 8){ + 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(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, 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(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(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(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(FacialExpression.ANNOYED,"Me neither, outerlander. That is why I gave it to you.").also { stage = 1000 } + + 17 -> npcl(FacialExpression.ANNOYED,"I don't think so.").also { stage = 1000 } + + 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?): DialoguePlugin { + return FishermanDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1302) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..84f926395 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikSailorDialogue.kt @@ -0,0 +1,80 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.addItem +import core.api.removeItem +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable + +@Initializable +class SailorDialogue (player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + if(player?.inventory?.contains(3699,1) == true){ + 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(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(FacialExpression.ASKING,"I've got an item to trade.") + stage = 25 + return true + } + else if(player?.getAttribute("sigmund-steps",0) == 2){ + 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(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 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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(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(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(FacialExpression.HAPPY,"Remember, it's not for me unless it's a love ballad.") + + 1000 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return SailorDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1385) + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..ec693af62 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikTrials.kt @@ -0,0 +1,99 @@ +package core.game.content.quest.fremtrials + +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 FremennikTrials : Quest(Quests.THE_FREMENNIK_TRIALS,64,63,3,347,0,1,10){ + + class SkillRequirement(val skill: Int?, val level: Int?) + + val requirements = arrayListOf() + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 11 + val started = player?.questRepository?.getStage(Quests.THE_FREMENNIK_TRIALS)!! > 0 + + if(!started){ + line(player,"Requirements to complete quest:",line++) + line += 1 + line(player,"Level 40 Woodcutting",line++, player.skills.getStaticLevel(Skills.WOODCUTTING) >= 40) + line(player,"Level 40 Crafting",line++,player.skills.getStaticLevel(Skills.CRAFTING) >= 40) + line(player,"Level 25 Fletching",line++,player.skills.getStaticLevel(Skills.FLETCHING) >= 25) + line(player,"I must also be able to defeat a !!level 69 enemy?? and must",line++) + line(player,"not be afraid of !!combat without any weapons or armour.",line++) + line += 1 + line(player,"I can start this quest by speaking to !!Chieftan Brundt?? on",line++) + line(player,"the !!Fremennik Longhall,?? which is in the town of !!Rellekka?? to",line++) + line(player,"the north of !!Sinclair Mansion??.",line) + } + else if(started && stage != 100){ + line(player,"In order to join the Fremenniks, I need to",line++) + line(player,"!!earn the approval?? of !!7 members?? of the elder council.",line++) + line(player,"I've written down the members who I can try to help:",line++) + line(player,"Manni the Reveller",line++,player.getAttribute("fremtrials:manni-vote",false)) + line(player,"Swensen the Navigator",line++,player.getAttribute("fremtrials:swensen-vote",false)) + line(player,"Sigli the Huntsman",line++,player.getAttribute("fremtrials:sigli-vote",false)) + line(player,"Olaf the Bard",line++,player.getAttribute("fremtrials:olaf-vote",false)) + 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" + if (voteCount === 1) { + voteText = "1 vote" + } + line(player,"So far I have gotten ${voteText}.",line++) + } + else if(stage == 100){ + line(player,"I made my way to the far north of !!Kandarin?? and found",line++) + line(player,"the Barbarian hometown of !!Rellekka??. The tribe that live",line++) + line(player,"there call themselves the !!Fremennik??, and offerred me the",line++) + line(player,"chance to join them if I could pass their trials.",line++) + line += 1 + line(player,"I managed to persuade !!seven?? of the !!twelve?? council of",line++) + line(player,"elders to vote for me at their next meeting. and become an",line++) + line(player,"honorary member of the !!Fremennik??.",line++) + line += 1 + line(player,"---QUEST COMPLETE---",line++) + line(player,"They also gave me a new name:",line++) + line(player,player.getAttribute("fremennikname","hingerdinger lmao"),line++) + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.FREMENNIK_HELM_3748, 235, 277, 5) + drawReward(player, "3 Quest points, 2.8k XP in:", ln++) + drawReward(player, "Strength, Defence, Attack,",ln++) + drawReward(player, "Hitpoints, Fishing, Thieving,", ln++) + drawReward(player, "Agility,Crafting, Fletching,", ln++) + drawReward(player, "Woodcutting",ln++) + player.skills.addExperience(Skills.STRENGTH, 2812.4) + player.skills.addExperience(Skills.DEFENCE, 2812.4) + player.skills.addExperience(Skills.ATTACK, 2812.4) + player.skills.addExperience(Skills.HITPOINTS, 2812.4) + player.skills.addExperience(Skills.FISHING, 2812.4) + player.skills.addExperience(Skills.THIEVING, 2812.4) + player.skills.addExperience(Skills.AGILITY, 2812.4) + player.skills.addExperience(Skills.CRAFTING, 2812.4) + player.skills.addExperience(Skills.FLETCHING, 2812.4) + player.skills.addExperience(Skills.WOODCUTTING, 2812.4) + player.questRepository.syncronizeTab(player) + } + + override fun newInstance(`object`: Any?): Quest { + requirements.add(SkillRequirement(Skills.FLETCHING,25)) + requirements.add(SkillRequirement(Skills.CRAFTING,40)) + requirements.add(SkillRequirement(Skills.WOODCUTTING,40)) + return this + } + +} diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/HunterTalismanListener.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/HunterTalismanListener.kt new file mode 100644 index 000000000..8cab68f23 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/HunterTalismanListener.kt @@ -0,0 +1,108 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.map.Location +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.GameWorld.Pulser +import kotlin.math.abs +import kotlin.math.atan2 + +class HunterTalismanListener : InteractionListener { + + val TALISMAN = Items.HUNTERS_TALISMAN_3696 + + override fun defineListeners() { + + on(TALISMAN, IntType.ITEM, "locate"){ player, _ -> + var locationString = getAttribute(player,"fremtrials:draugen-loc","none") + if(locationString == "none"){ + val newLoc = possibleLocations.random() + setAttribute(player, "/save:fremtrials:draugen-loc","${newLoc.x},${newLoc.y}") + locationString = "${newLoc.x},${newLoc.y}" + } + val locationComponents = locationString?.split(",") + val draugenLoc = Location(Integer.parseInt(locationComponents?.get(0)),Integer.parseInt(locationComponents?.get(1))) + + if(player.location?.withinDistance(draugenLoc,5)!!){ + sendDialogue(player, "The Draugen is nearby, be careful!") + Pulser.submit(DraugenPulse(player)) + } else { + val neededDirection = draugenLoc.getDirection(player as Entity) + sendMessage(player, "The talisman pulls you to the $neededDirection") + } + return@on true + } + + } + + class DraugenPulse(val player: Player) : Pulse(){ + var count = 0 + override fun pulse(): Boolean { + when(count++){ + 3 -> { + if(getAttribute(player, "fremtrials:draugen-spawned", false)) return true + Draugen(player).init() + setAttribute(player, "fremtrials:draugen-spawned", true) + return true + } + } + return false + } + } + + val possibleLocations = listOf(Location(2625,3608), + Location(2602,3628), + Location(2668,3714), + Location(2711,3602), + Location(2664,3592)) + + + fun Location.getDirection(entity: Entity): String{ + val loc: Location = this + val difX: Double = (loc.x - entity.location.x).toDouble() + val difY: Double = (loc.y - entity.location.y).toDouble() + val angle = Math.toDegrees(atan2(difX,difY)) + val NORTH = 0.toDouble() + val SOUTH = 180.toDouble() + val EAST = (-90).toDouble() + val WEST = 90.toDouble() + val NORTHEAST = (-135).toDouble() + val NORTHWEST = 135.toDouble() + val SOUTHEAST = (-45).toDouble() + val SOUTHWEST = 45.toDouble() + if(diff(angle,NORTH) < 3){ + return "north" + } + if(diff(angle,SOUTH) < 3){ + return "south" + } + if(diff(angle,EAST) < 3){ + return "west" + } + if(diff(angle,WEST) < 3){ + return "east" + } + if(diff(angle,SOUTHEAST) < 45){ + return "north-west" + } + if(diff(angle,SOUTHWEST) < 45){ + return "north-east" + } + if(diff(angle,NORTHEAST) < 45){ + return "south-west" + } + if(diff(angle,NORTHWEST) < 45){ + return "south-east" + } + return "Dunno. ${angle}" + } + + fun diff(x: Double,y: Double): Double{ + return abs(x - y) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..1a936e792 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/KoscheiNPC.kt @@ -0,0 +1,210 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +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.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.NPCs +import core.game.world.GameWorld.Pulser +import core.game.world.repository.Repository + +/** + * Handles the Koschei npc. + * @author NixWigton + */ +class KoscheiNPC constructor(id: Int = 0, location: Location? = null, session: KoscheiSession? = null) : + AbstractNPC(id, location) { + + /** + * The session. + */ + val session: KoscheiSession? + + /** + * The Koschei type. + */ + var type: KoscheiType? + + /** + * If the fight has commenced. + */ + var isCommenced = false + + /** + * Constructs a new `KoscheiNPC` `Object`. + */ + init { + this.isWalks = true + this.session = session + this.isRespawn = false + type = KoscheiType.forId(id) + } + + override fun init() { + super.init() + + /** + * Ensure the player is in the arena then spawn. + */ + if (session?.player?.location?.regionId == 10653) + Pulser.submit(KoscheiSpawnPulse(session.player, this)) + else session?.close() + } + + override fun handleTickActions() { + super.handleTickActions() + if (session == null) { + return + } + if (!session.player.isActive) { + clear() + return + } + if (isCommenced && !properties.combatPulse.isAttacking) { + properties.combatPulse.attack(session.player) + } + } + + override fun startDeath(killer: Entity) { + if (killer === session!!.player) { + if (type !== KoscheiType.FOURTH_FORM) { + type!!.transform(this, session!!.player) + } 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?.removeAttribute("fremtrials:warrior-accepted") + addItemOrDrop(session?.player!!, Items.FREMENNIK_BLADE_3757, 1) + session.close() + } + return + } + super.startDeath(killer) + } + + override fun sendImpact(state: BattleState?) { + if (type == KoscheiType.FOURTH_FORM) { + if (session?.player?.skills?.lifepoints!! < 2) { + session.player.fullRestore() + properties.combatPulse.stop() + Pulser.submit(FightEndPulse(session.player, this)) + return + } else { + session.player.skills?.decrementPrayerPoints(session.player.skills?.prayerPoints!!) + } + } + super.sendImpact(state) + } + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return KoscheiNPC(id, location, null) + } + + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { + if (session == null) { + return false + } + return session.player == entity + } + + override fun canSelectTarget(target: Entity): Boolean { + if (target is Player) { + if (target != session!!.player) { + return false + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KOSCHEI_THE_DEATHLESS_1290, NPCs.KOSCHEI_THE_DEATHLESS_1291, NPCs.KOSCHEI_THE_DEATHLESS_1292, NPCs.KOSCHEI_THE_DEATHLESS_1293) + } + + /** + * Represents a Koschei type. + * @author NixWigton + */ + enum class KoscheiType(var npcId: Int, var appearMessage: String?, vararg var appearDialogues: String?) { + FIRST_FORM(NPCs.KOSCHEI_THE_DEATHLESS_1290, "You must prove yourself... now!"), + SECOND_FORM(NPCs.KOSCHEI_THE_DEATHLESS_1291, "This is only the beginning; you can't beat me!", "It seems you have some idea of combat after all,", "outerlander! I will not hold back so much this time!"), + THIRD_FORM(NPCs.KOSCHEI_THE_DEATHLESS_1292, "Foolish mortal; I am unstoppable.", "Impressive start... But now we fight for real!"), + FOURTH_FORM(NPCs.KOSCHEI_THE_DEATHLESS_1293, "Aaaaaaaarrgghhhh! The power!", "You show some skill at combat... I will hold back no", "longer! This time you lose your prayer however, and", "fight like a warrior!"); + + /** + * Transforms the new npc. + */ + fun transform(koschei: KoscheiNPC, player: Player) { + val newType = next() + koschei.lock() + player.properties.combatPulse.stop() + koschei.properties.combatPulse.stop() + koschei.walkingQueue.queue.clear() + koschei.animate(Animation(1057)) + koschei.type = newType + koschei.transform(newType.npcId) + Pulser.submit(KoscheiSpawnPulse(player, koschei)) + } + + /** + * Gets the next type. + * @return the type. + */ + operator fun next(): KoscheiType { + return values()[ordinal + 1] + } + + companion object { + /** + * Gets the Koschei type for the id. + * @param id the id. + * @return the Koschei type. + */ + fun forId(id: Int): KoscheiType? { + for (type in values()) { + if (type.npcId == id) { + return type + } + } + return null + } + } + } + + class KoscheiSpawnPulse(val player: Player?, val koschei: KoscheiNPC) : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when(counter++) { + 0 -> koschei.face(player).also { koschei.unlock(); player?.face(koschei) } + 1 -> if (koschei.type?.appearDialogues?.size!! > 0) { + player?.dialogueInterpreter?.sendDialogues(NPCs.KOSCHEI_THE_DEATHLESS_1291, core.game.dialogue.FacialExpression.NEUTRAL, *koschei.type!!.appearDialogues) + } else { counter = 4 } + 4 -> koschei.attack(player).also { if (koschei.type?.appearMessage?.isNotEmpty() == true) { koschei.sendChat(koschei.type?.appearMessage) }} + } + return false + } + } + + class FightEndPulse(val player: Player?, val koschei: KoscheiNPC) : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + 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?.removeAttribute("fremtrials:warrior-accepted") + } + 3 -> player?.teleport(Location.create(2666,3694,1)).also { koschei.session?.close() } + 4 -> player?.sendMessage("...still alive somehow?") + 6 -> player?.dialogueInterpreter?.open(NPCs.THORVALD_THE_WARRIOR_1289, Repository.findNPC(NPCs.THORVALD_THE_WARRIOR_1289), this) + 7 -> player?.unlock().also { player?.sendMessage("Congratulations! You have passed the warrior's trial!") } + } + return false + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/KoscheiSession.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/KoscheiSession.kt new file mode 100644 index 000000000..6e2b44e47 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/KoscheiSession.kt @@ -0,0 +1,71 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.game.node.entity.player.Player +import org.rs09.consts.NPCs + +/** + * Represents a session with Koschei. + * @author NixWigton + */ +class KoscheiSession( + /** + * The player. + */ + val player: Player +) { + /** + * The Koschei npc. + */ + private val koschei: KoscheiNPC = KoscheiNPC( + NPCs.KOSCHEI_THE_DEATHLESS_1290, + player.location?.transform(1,0,0), + this + ) + + /** + * Constructs a new `KoscheiSession` `Object`. + * @param player the player. + */ + init { + if (player.getExtension(KoscheiSession::class.java) != null) { + player.removeExtension(KoscheiSession::class.java) + } + player.addExtension(KoscheiSession::class.java, this) + } + + /** + * Starts the session. + */ + fun start() { + koschei.init() + player.unlock() + } + + /** + * Closes the session. + */ + fun close() { + koschei.clear() + player.removeExtension(KoscheiSession::class.java) + } + + companion object { + /** + * Creates the Koschei session. + * @param player the player. + * @return the session. + */ + fun create(player: Player): KoscheiSession { + return KoscheiSession(player) + } + + /** + * Gets the Koschei session. + * @param player the player. + * @return the session. + */ + fun getSession(player: Player): KoscheiSession { + return player.getExtension(KoscheiSession::class.java) + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..a6d1f37de --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/LalliDialogue.kt @@ -0,0 +1,167 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.addItem +import core.api.inInventory +import core.api.removeItem +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.item.Item +import org.rs09.consts.Items +import content.data.Quests + +@Initializable +class LalliDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun open(vararg args: Any?): Boolean { + player?.let { + println(it.getAttribute("lalliEatStew", false)) + if (it.questRepository.isComplete(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(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(FacialExpression.OLD_NORMAL,"It am ready now?") + stage = 58 + return true + } + if (it.getAttribute("lalliEatStew", false)!!){ + playerl(FacialExpression.NEUTRAL,"Hello there.") + stage = 65 + return true + } + if (it.getAttribute("hasWool", false)!!){ + playerl(FacialExpression.NEUTRAL,"Hello there.") + stage = 75 + return true + } + if (it.getAttribute("fremtrials:askeladden-talkedto", false)!!) { + player("Hello there.") + stage = 50 + return true + } + if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + playerl(FacialExpression.HAPPY,"Hello there.") + stage = 100 + return true + } + if (it.questRepository.getStage(Quests.THE_FREMENNIK_TRIALS) > 0) { + player("Hello there.").also { stage = 0; return true } + } + } + return true + } + + + + override fun newInstance(player: Player?): DialoguePlugin { + return LalliDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(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(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 } + 2 -> player("No, honest, you're REALLY clever!").also { stage = 20 } + 3 -> player("Can I give you a present.").also { stage = 30 } + } + + //Other human? + 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(FacialExpression.OLD_SNEAKY,"Me no believe you tell truth. Go away.").also { stage = 1000 } + + //Can I give you a present + 30 -> npc(FacialExpression.OLD_SNEAKY,"Me think about that.").also { stage = 1000 } + + 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(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(FacialExpression.NEUTRAL,"Not just yet...").also { stage = 1000 } + + 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") + player.removeAttribute("lalliStewCabbageAdded") + player.removeAttribute("lalliStewRockAdded") + stage = 1000 + } + + 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(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(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(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 + } + else 115 + } + 105 -> options("Pay","Don't pay").also { stage++ } + 106 -> when(buttonId){ + 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(FacialExpression.OLD_HAPPY,"You no pay, then you go! You no get any golden apples!").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(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() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(1270) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/LonghallStageZone.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/LonghallStageZone.kt new file mode 100644 index 000000000..0f9425439 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/LonghallStageZone.kt @@ -0,0 +1,40 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.game.node.entity.Entity +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 + +@Initializable +class LonghallStageZone : MapZone("LonghallStageZone",true), Plugin { + + override fun newInstance(arg: Any?): LonghallStageZone { + ZoneBuilder.configure(this) + return this + } + + override fun configure() { + super.register(ZoneBorders(2655,3682,2662,3685)) + } + + override fun enter(e: Entity?): Boolean { + if (e != null && e.isPlayer) { + e.setAttribute("onStage",true) + } + return super.enter(e) + } + + override fun leave(e: Entity?, logout: Boolean): Boolean { + if (e != null && e.isPlayer) { + e.removeAttribute("onStage") + } + return super.leave(e, logout) + } + + override fun fireEvent(identifier: String?, vararg args: Any?): Any { + return Unit + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..425c617cf --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ManniDialogue.kt @@ -0,0 +1,203 @@ +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 +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.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(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 + return true + } + else if(player?.inventory?.contains(3706,1) == true){ + playerl(core.game.dialogue.FacialExpression.ASKING,"So it doesn't bother you at all that you just gave up your place here for one drink?") + stage = 180 + return true + } + else if(player?.getAttribute("sigmundreturning",false) == true){ + playerl(core.game.dialogue.FacialExpression.ASKING,"Is this trade item for you?") + stage = 165 + return true + } + 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?") + stage = 162 + return true + } + else if(player?.getAttribute("sigmund-steps", 0) == 11){ + playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a token to allow a seat at the champions table, do you?") + stage = 150 + return true + } + else if(player?.getAttribute("fremtrials:manni-accepted",false) == true) { + if (player?.inventory?.containsItem(Item(3712)) == true || player?.inventory?.containsItem(Item(3711)) == true) { + npc("Ah, I see you have your keg of beer. Are ye ready to", "drink against each other?") + stage = 101 + return true + } else { + npc("Come back when you're ready to begin", "the contest.") + stage = 1000 + return true + } + } + 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(Quests.THE_FREMENNIK_TRIALS)){ + playerl(core.game.dialogue.FacialExpression.HAPPY,"Howdy!") + stage = 190 + return true + } + player("Hello there!") + stage = 0 + return true + } + else { + playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there!") + stage = 200 + return true + } + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> {npc("Hello outerlander. I overheard your conversation with","Brundt just now. You wish to become a member of the","Fremennik?");stage++} + 1 -> {player("That's right! Why, are you on the counsel?");stage++} + 2 -> {npc("Do not let my drink-ssussed appearance fool you, I","earnt my place on the council many years past.I am","always glad to see new blood enter our tribe, and will","happily vote for you.");stage++} + 3 -> {player("Great!");stage++} + 4 -> {npc("Providing you can pass a little test for me. As a","Fremennik, you will need to show cunning, stamina,","fastitude, and an iron constitution. I know of only one","way to test all of these.");stage++} + 5 -> {player("And what's that?");stage++} + 6 -> {npc("Why, a drinking contest!");stage++} + 7 -> {npc("The task is simple enough! You versus me, a stiff drink","each, last man standing wins the trial. So what say you?");stage++} + 8 -> {options("Yes","No");stage++} + 9 -> when(buttonId){ + 1 -> {player("A drinking contest? Easy. Set them up, and I'll knock","them back.");stage++} + 2 -> {player("I don't like the sound of that.");stage = 12} + } + + //Yes + 10 -> {npc("When you are ready to begin, go and pick up a keg","from that table over there, and come back here.");stage++} + 11 -> {npc("We start when you have your keg of beer with you","and finish when one of us can drink no more and","yields.");stage = 1000;player?.setAttribute("/save:fremtrials:manni-accepted",true)} + + //No + 12 -> {npc("That's a shame."); stage = 1000} + + + //Begin drinking competition + 101 -> {options("Yes","No");stage++} + 102 -> when(buttonId){ + 1 -> {player("Yes, let's start this drinking contest!");stage++} + 2 -> {player("No, I don't think I am.");stage = 1000} + } + 103 -> {npc("As you wish outerlander; I will drink first, then you will","drink.");stage++} + 104 -> { + GameWorld.submit(DrinkingPulse(player,curNPC,player?.getAttribute("fremtrials:keg-mixed",false))); end()} + + //Sigmund stuff + 150 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As a matter of fact, I do. I have one right here. I earnt my place here at the longhall for surviving over 5000 battles and raiding parties.").also { stage++ } + 151 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Due to my contribution to the tribe, I am now permitted to spend my days here in the longhall listening to the epic tales of the bard, and drinking beer.").also { stage++ } + 152 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Cool. That sounds pretty sweet! So I guess you don't want to give it away?").also { stage++ } + 153 -> npcl(core.game.dialogue.FacialExpression.SAD,"I think it sounds better than it actually is outerlander. I miss my glory days of combat on the battlefield.").also { stage++ } + 154 -> npcl(core.game.dialogue.FacialExpression.SAD,"And to tell you the truth, the beer here isn't great, and the bards' music is lousy. I would happily give up my token if it were not for the one thing that keeps me here.").also { stage++ } + 155 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Our barkeep is one of the best in the world, and has worked in taverns across the land. When she was younger, she experimented a lot with her drinks").also { stage++ } + 156 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"and invented a cocktail so alcoholic and tasty that it has become something of a legend to all who enjoy a drink.").also { stage++ } + 157 -> npcl(core.game.dialogue.FacialExpression.SAD,"Unfortunately, she decided that cocktails were not a suitable drink for Fremennik warriors, and vowed to never again make it.").also { stage++ } + 158 -> npcl(core.game.dialogue.FacialExpression.SAD,"I have been here every day since she returned, hoping that someday she might change her mind and I might try this legendary cocktail for myself. Alas, it has never come to pass...").also { stage++ } + 159 -> npcl(core.game.dialogue.FacialExpression.SAD,"If you can persuade her to make me her legendary cocktail, I will be happy to never let another drop of alcohol pass my lips, and will give you my champions token.").also { stage++ } + 160 -> playerl(core.game.dialogue.FacialExpression.THINKING,"That's all?").also { stage++ } + 161 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"That is all.").also { + player?.incrementAttribute("sigmund-steps",1) + stage = 1000 + } + 162 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Uh... yes, the longhall barkeep has it. So could you get me my drink now please?").also { stage = 1000 } + + 165 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Not me, no.").also { stage = 1000 } + 170 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"...It is true! The legendary cocktail! I have waited for this day ever since I first started drinking!").also { + removeItem(player,3707) + addItem(player,3706) + stage++ + } + 171 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here outerlander, you may take my token. I will happily give up my place at the longhalls table of champions just for a taste of this exquisite beverage!").also { stage++ } + 172 -> playerl(core.game.dialogue.FacialExpression.ASKING,"It's just a drink...").also { stage++ } + 173 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"No, it is an artform. A drink such as this should be appreciated, and admired.").also { stage++ } + 174 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"It is like a fine painting, or a tasteful sculpture. If what I hear is true, then all other drinks become like unpalatable water in comparison to this!").also { stage++ } + 175 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I guess you're happy with the trade then!").also { stage = 1000 } + + 180 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah, but it was not just any drink... It was the finest cocktail ever created! Now that I have tasted it, I need never drink again, for my tastebuds will never be so excited!").also { stage++ } + 181 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So it was nice?").also { stage++ } + 182 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"It was... exquisite!").also { stage++ } + 183 -> playerl(core.game.dialogue.FacialExpression.ASKING,"What did it taste of, then?").also { stage++ } + 184 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Mostly tomato juice.").also { stage = 1000 } + + 190 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hey! It's ${player.getAttribute("fremennikname","clyde smilers")}! Let me buy you a drink!").also { + addItem(player, Items.BEER_1917) + stage++ } + 191 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"There ya go! Anyone who can drink like you earns my respect!").also { stage = 1000 } + + 200 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Do not think me rude outerlander, but our customs forbid me talking to you. All contact with outerlanders must be vetted by our chieftain, Brundt.").also { stage++ } + 201 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Where is this Brundt?").also { stage++ } + 202 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"He is standing just over there. He will speak for the tribe.").also { stage = 1000 } + + + 1000 -> end() + } + return true + } + + class DrinkingPulse(val player: Player?,val npc: NPC?, val lowAlcohol: Boolean? = false) : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + if(!lowAlcohol!!){ + when(counter++){ + 0 -> {player?.lock();npc?.lock(); npc?.isNeverWalks = true} + 1 -> {player?.face(npc); npc?.face(player)} + 3 -> npc?.animator?.animate(Animation(1330,Animator.Priority.HIGH)) + 5 -> {player?.animator?.animate(Animation(1330,Animator.Priority.HIGH)); player?.inventory?.remove(Item(3711))} + 7 -> player?.dialogueInterpreter?.sendDialogues(player, core.game.dialogue.FacialExpression.DRUNK,"Ish no fair!! I canna drink another drop! I alsho","feel veddy, veddy ill...") + 15 -> player?.dialogueInterpreter?.sendDialogues(npc, core.game.dialogue.FacialExpression.DRUNK,"I guessh I win then ouddaladder! (hic) Niche try,","anyway!") + 16 -> {player?.unlock(); npc?.unlock(); player?.face(player);npc?.face(npc); npc?.isNeverWalks = false; return true} + } + } else { + when(counter++){ + 0 -> {player?.lock();npc?.lock(); npc?.isNeverWalks = true} + 1 -> {player?.face(npc); npc?.face(player)} + 3 -> npc?.animator?.animate(Animation(1330,Animator.Priority.HIGH)) + 5 -> {player?.animator?.animate(Animation(1330,Animator.Priority.HIGH)); player?.inventory?.remove(Item(3711))} + 7 -> player?.dialogueInterpreter?.sendDialogues(player, core.game.dialogue.FacialExpression.HAPPY,"Aaaah, lovely stuff. So you want to get the next round","in, or shall I? You don't look so good there!") + 15 -> player?.dialogueInterpreter?.sendDialogues(npc, core.game.dialogue.FacialExpression.DRUNK,"Wassha? Guh? You drank that whole keg! But it dinnna","affect you at all! I conshede! You can probably","outdrink me!") + 21 -> player?.dialogueInterpreter?.sendDialogues(npc, core.game.dialogue.FacialExpression.DRUNK,"I jusht can't (hic) believe it! Thatsh shome might fine","drinking legs you got! Anyone who can drink like","THAT getsh my vote atta somsh.... coumah... gets my","vote!") + 22 -> {player?.unlock(); npc?.unlock(); player?.face(player);npc?.face(npc); npc?.isNeverWalks = false;player?.removeAttribute("fremtrials:cherrybomb");player?.removeAttribute("fremtrials:manni-accepted");player?.removeAttribute("fremtrials:keg-mixed"); player?.setAttribute("/save:fremtrials:manni-vote",true); player?.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1); return true} + } + } + return false + } + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return ManniDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1286) + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..baffb1143 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/OlafTheBard.kt @@ -0,0 +1,166 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +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 content.data.Quests + +@Initializable +class OlafTheBard(player: Player? = null) : DialoguePlugin(player){ + override fun open(vararg args: Any?): Boolean { + if(player.inventory.contains(3700,1)){ + 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(FacialExpression.ASKING,"So you think this song is pretty good then?") + stage = 70 + return true + } + else if(player?.getAttribute("sigmundreturning",false) == true){ + 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(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(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(FacialExpression.HAPPY,"So can I rely on your vote with the council of elders in my favour?") + stage = 40 + return true + } + else if(player?.getAttribute("fremtrials:olaf-accepted",false)!!){ + npc("I can't wait to see your performance.") + stage = 1000 + return true + } + 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(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0){ + npc("Hello? Yes? You want something outerlander?") + stage = 0 + return true + } + else{ + playerl(FacialExpression.HAPPY,"Hello there. So you're a bard?") + stage = 150 + return true + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> player("Are you a member of the council?").also { stage++ } + 1 -> npc("Why, indeed I am, outerlander! My talents as an","exemplary musician made it difficult for them not to","accept me! Why do you wish to know this?").also { stage++ } + 2 -> player("Well, I ask because I am currently doing the","Fremennik trials so as to join you clan. I need seven","of the twelve council of elders to vote for me.").also { stage++ } + 3 -> npc("Ahhh... and you wish to earn my vote? I will gladly","accept you as a Fremennik should you be able to prove","yourself to have a little musical ability!").also { stage++ } + 4 -> player("So how would I do that?").also { stage++ } + 5 -> npc("Why, by playing in our longhall! All you need to do is","impress the revellers there with a few verses of an epic","of your own creation!").also { stage++ } + 6 -> npc("So what say you outerlander? Are you up for the","challenge?").also { stage++ } + 7 -> options("Yes","No").also { stage++ } + 8 -> when(buttonId){ + 1 -> player("Sure! This certainly sounds pretty easy to accomplish.","I'll have your vote in no time!").also { stage++ } + 2 -> player("No, that sounds like too much work.").also { stage = 1000 } + } + + //Yes + 9 -> npc("That is great news outerlander! We always need more","music lovers here!").also { stage++;player?.setAttribute("/save:fremtrials:olaf-accepted",true) } + 10 -> player("So how would I go about writing this epic?").also { stage++ } + 11 -> npc("Well, first of all you are going to need an instrument.","Like all true bards you are going to have to make this","yourself.").also { stage++ } + 12 -> player("How do I make an instrument?").also { stage++ } + 13 -> npc("Well, it is a long and drawn-out process. Just east of","this village there is an unusually musical tree that can","be used to make very high quality instruments.").also { stage++ } + 14 -> npc("Cut a piece from it, and then carve it into a special","shape that will allow you to string it. Using a knife as","you would craft any other wooden object would be best","for this.").also { stage++ } + 15 -> player("Then what do I need to do?").also { stage++ } + 16 -> npc("Next you will need to string your lyre. There is a troll","to the South-east who has some golden wool. I would","not recommend using anything else to string your lyre","with.").also { stage++ } + 17 -> player("Anything else?").also { stage++ } + 18 -> npc("Well, when you have crafted your lyre you will need","the blessing of the Fossegrimen to tune your lyre to","perfection before you even consider a public","performance.").also { stage++ } + 19 -> player("Who or what is Fossegrimen?").also { stage++ } + 20 -> npc("Fossegrimen is a lake spirit that lives just a little way","South-west of this village. Make her an offering of fish,","and you will then be ready for your performance.").also { stage++ } + 21 -> npc("Make sure you give her a suitable offering however. If","the offering is found to be unworthy, then you may","find yourself unable to play your lyre with any skill at","all!").also { stage++ } + 22 -> player("So what would be a worthy offering?").also { stage++ } + 23 -> npc("A raw shark, manta ray, or sea turtle should be","sufficient as an offering.").also { stage++ } + 24 -> player("Ok, what else do I need to do?").also { stage++ } + 25 -> npc("When you have crafted your lyre and been blessed by","Fossegrimen, then you will finally be ready to make","your performance to the revellers at the longhall.").also { stage++ } + 26 -> npc("Head past the bouncers and onto the stage, and then","begin to play. If all goes well, you should find the music","spring to your mind and sing your own epic on the","spot").also { stage++ } + 27 -> npc("I will observe both you and the audience, and if you","show enough talent, I will happily vote in your favour at","the council of elders.").also { stage++ } + 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(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 + } + + + //Sigmund bullshit + 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(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(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(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(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(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(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(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(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?): DialoguePlugin { + return OlafTheBard(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1269) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..c30737ab4 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PeerTheSeerDialogue.kt @@ -0,0 +1,261 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.game.node.entity.player.Player +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) : 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") + val predictionFour = arrayOf("Al Kharid","Ardougne","Burthorpe","Canifis","Catherby","Falador","Karamja","Varrock","The Wilderness","Yanille") + val predictionFive = arrayOf("battleaxe","crossbow","dagger","javelin","long sword","mace","scimitar","spear","warhammer") + val predictionSix = arrayOf("Agility","Cooking","Crafting","Fishing","Fletching","Herblore","Mining","Runecrafting","Thieving") + val PREDICTIONS = arrayOf( + "You will find luck today with the number ${predictionOne[RandomFunction.getRandom(8)]}.", + "The colour ${predictionTwo[RandomFunction.getRandom(8)]} will bring you luck this day.", + "The enemy called ${predictionThree[RandomFunction.getRandom(9)]} is your lucky totem this day.", + "The place called ${predictionFour[RandomFunction.getRandom(9)]} will be worth your time to visit.", + "The stars tell me that you should use a ${predictionFive[RandomFunction.getRandom(8)]} in combat today.", + "You would be wise to train the skill ${predictionSix[RandomFunction.getRandom(8)]}") + var prediction = RandomFunction.getRandom(5) + + override fun open(vararg args: Any?): Boolean { + if(player.inventory.contains(3710,1)){ + playerl(FacialExpression.HAPPY,"Can I have a weather forecast now please?") + stage = 15 + return true + } + else if(player.inventory.contains(3705,1)){ + playerl(FacialExpression.ASKING,"So, about this forecast...") + stage = 20 + return true + } + else if(player.getAttribute("sigmundreturning",false) == true){ + 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(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(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(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(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + stage = 110 + return true + } + 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(Quests.THE_FREMENNIK_TRIALS)){ + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + stage = 150 + return true + } + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0){ + npcl(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") + stage = 50 + return true + } + if (getQuestStage(player, 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(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(FacialExpression.ANNOYED,"If I did, then I would simply have asked them myself now, wouldn't I, outerlander?").also { stage = 1000 } + + 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(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(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(FacialExpression.ANNOYED,"Not me, I'm afraid.").also { stage++ } + + //The Seer's Trial + 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(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(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(FacialExpression.HAPPY,"As you wish, outerlander.").also { stage = 1000 } + + //Yes to challenge + 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() && 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(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(FacialExpression.HAPPY,"No thanks. Nobody touches my stuff but me!").also { stage++ } + } + + //No to banking + 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(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(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(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(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 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(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(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(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(FacialExpression.HAPPY,"Here is your prediction: ${PREDICTIONS[prediction]}").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(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(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(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?): DialoguePlugin { + return PeerTheSeerDialogue(player) + } + + override fun getIds(): IntArray { + 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 new file mode 100644 index 000000000..2e0fd8671 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/PoisonSalesman.kt @@ -0,0 +1,153 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import content.region.kandarin.quest.murdermystery.MurderMystery.Companion.attributePoisonClue +import core.api.inInventory +import core.api.setAttribute +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import content.data.Quests + +@Initializable +class PoisonSalesman(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + options("Talk about the Murder Mystery Quest","Talk about the The Fremennik Trials") + stage = START_DIALOGUE + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + val murderMysteryStage = player.questRepository.getStage(Quests.MURDER_MYSTERY) + val fremennikTrialsStage = player.questRepository.getStage(Quests.THE_FREMENNIK_TRIALS) + + when (stage) { + START_DIALOGUE -> when (buttonId) { + 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 } + } + + //The Fremennik Trials + 10 -> { + /**when (fremennikTrialsStage) { + 0 -> { npc("Come see me if you ever need low-alcohol beer!"); stage = END_DIALOGUE } + }*/ + if(fremennikTrialsStage == 0){ + npc("Come see me if you ever need low-alcohol beer!"); stage = END_DIALOGUE + } + else if(fremennikTrialsStage > 30){ + npc("Thanks for buying out all that low-alcohol beer!"); stage = END_DIALOGUE + } else if(fremennikTrialsStage > 0){ + npc("Howdy! You seem like someone with discerning taste!","Howsabout you try my brand new range of alcohol?"); stage++ + } + } + 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 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++ } + 16 -> { npc("Well I would, but I ran out of stock. Business wasn't","helped with that stuff that happened up at the Sinclair","Mansion much either, I'll be honest."); stage++ } + 17 -> { npc("So, being the man of enterprise that I am I decided to","branch out a little bit!"); stage++ } + 18 -> { player("Into alcohol?"); stage++ } + 19 -> { npc("Absolutely! The basic premise between alcohol and poison","is pretty much the same, after all! The difference is that","my alcohol has a unique property others do not!"); stage++ } + 20 -> { player("And what is that?"); stage++ } + 21 -> { sendDialogue("The salesman takes a deep breath."); stage++ } + 22 -> { npc("Ever been too drunk to find your own home? Ever","wished that you could party away all night long, and","still wake up fresh as a daisy the next morning?"); stage++ } + 23 -> { npc("Thanks to the miracles of modern magic we have come","up with just the solution you need! Peter Potter's","Patented Party Potions!"); stage++ } + 24 -> { npc("It looks just like beer! It tastes just like beer! It smells","just like beer! But... it's not beer!"); stage++ } + 25 -> { npc("Our mages have mused for many moments to bring","you this miracle of modern magic! It has all the great","tastes you'd expect, but contains absolutely no alcohol!"); stage++ } + 26 -> { npc("That's right! You can drink Peter Potter's Patented","Party Potion as much as you want, and suffer","absolutely no ill effects whatsoever!"); stage++ } + 27 -> { npc("The clean fresh taste you know you can trust, from","the people who brought you: Peter Potters Patented","multipurpose poison, Pete Potters peculiar paint packs"); stage++ } + 28 -> { npc("and Peter Potters paralyzing panic pins. Available now","from all good stockists! Ask your local bartender now,","and experience the taste revolution of the century!"); stage++ } + 29 -> { sendDialogue("He seems to have finished for the time being."); stage++ } + 30 -> { player("So.. when you say 'all good stockists'..."); stage++ } + 31 -> { npc("Yes?"); stage++ } + 32 -> { player("How many inns actually sell this stuff?"); stage++ } + 33 -> { npc("Well.. nobody has actually bought any yet. Everyone I","try and sell it to always asks me what exactly the point","of beer that has absolutely no effect on you is."); stage++ } + 34 -> { player("So what is the point?"); stage++ } + 35 -> { npc("Well... Um... Er... Hmmm. You, er, don't get drunk."); stage++ } + 36 -> { player("I see..."); stage++ } + 37 -> { npc("Aw man.. you don't want any now do you? I've really","tried to push this product, but I just don't think the","world is ready for beer that doesn't get you drunk."); stage++ } + 38 -> { npc("I'm a man ahead of my time I tell you! It's not that","my products are bad, it's that they're too good for the","market!"); stage++ } + 39 -> { player("Actually, I would like some. How much do you want for it?"); stage++ } + 40 -> { npc("Y-you would??? Um, okay! I knew I still had the old","salesmans skills going on!"); stage++ } + 41 -> { npc("I will sell you a keg of it for only 250 gold pieces! So","What do you say?"); stage++ } + 42 -> { options("Yes","No"); stage++ } + 43 -> when(buttonId){ + 1 -> { player("Yes please!"); stage++ } + 2 -> { player("No, thanks."); stage = END_DIALOGUE } + } + //Yes + 44 -> if(player?.inventory?.containsItem(Item(995,250)) == true){ + player?.inventory?.remove(Item(995,250)) + player?.inventory?.add(Item(3712)) + npc("Here you go.") + stage = END_DIALOGUE + } else { + player("I don't seem to have enough.") + 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?): DialoguePlugin { + return PoisonSalesman(player) + } + + override fun getIds(): IntArray { + return intArrayOf(820) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SeerLockInterfaceListener.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SeerLockInterfaceListener.kt new file mode 100644 index 000000000..6be622d9d --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SeerLockInterfaceListener.kt @@ -0,0 +1,165 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.sendMessage +import core.game.interaction.InterfaceListener + +class SeerLockInterfaceListener : InterfaceListener { + + override fun defineInterfaceListeners() { + val LETTERONEBACK = 39 + val LETTERONEFORWARD = 40 + val LETTERTWOBACK = 35 + val LETTERTWOFORWARD = 36 + val LETTERTHREEBACK = 31 + val LETTERTHREEFORWARD = 32 + val LETTERFOURBACK = 27 + val LETTERFOURFORWARD = 28 + val ENTER = 1 + val EXIT = 2 + val DOORLOCKINTERFACE = 298 + 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.packetDispatch.sendVarcUpdate(618, 0) + player.packetDispatch.sendVarcUpdate(619, 0) + player.packetDispatch.sendVarcUpdate(620, 0) + player.packetDispatch.sendVarcUpdate(621, 0) + player.packetDispatch.sendIfaceSettings(0, 2, 298, 0, 1) + player.setAttribute("riddle-letter-one",0) + player.setAttribute("riddle-letter-two",0) + player.setAttribute("riddle-letter-three",0) + player.setAttribute("riddle-letter-four",0) + return@onOpen true + } +// + onClose(DOORLOCKINTERFACE){player, component -> + player.removeAttribute("riddle-letter-one") + player.removeAttribute("riddle-letter-two") + player.removeAttribute("riddle-letter-three") + player.removeAttribute("riddle-letter-four") + return@onClose true + } +// + on(DOORLOCKINTERFACE){player, component, opcode, buttonID, slot, itemID -> + when(buttonID){ + LETTERONEBACK ->{ + if(player.getAttribute("riddle-letter-one",0) == 0){ + player.setAttribute("riddle-letter-one",25) + } + else{ + (player.incrementAttribute("riddle-letter-one",-1)) + } + } + LETTERONEFORWARD ->{ + if(player.getAttribute("riddle-letter-one",0) == 25){ + player.setAttribute("riddle-letter-one",0) + } + else{ + (player.incrementAttribute("riddle-letter-one",1)) + } + } + LETTERTWOBACK ->{ + if(player.getAttribute("riddle-letter-two",0) == 0){ + player.setAttribute("riddle-letter-two",25) + } + else{ + (player.incrementAttribute("riddle-letter-two",-1)) + } + } + LETTERTWOFORWARD ->{ + if(player.getAttribute("riddle-letter-two",0) == 25){ + player.setAttribute("riddle-letter-two",0) + } + else{ + (player.incrementAttribute("riddle-letter-two",1)) + } + } + LETTERTHREEBACK ->{ + if(player.getAttribute("riddle-letter-three",0) == 0){ + player.setAttribute("riddle-letter-three",25) + } + else{ + (player.incrementAttribute("riddle-letter-three",-1)) + } + } + LETTERTHREEFORWARD ->{ + if(player.getAttribute("riddle-letter-three",0) == 25){ + player.setAttribute("riddle-letter-three",0) + } + else{ + (player.incrementAttribute("riddle-letter-three",1)) + } + } + LETTERFOURBACK ->{ + if(player.getAttribute("riddle-letter-four",0) == 0){ + player.setAttribute("riddle-letter-four",25) + } + else{ + (player.incrementAttribute("riddle-letter-four",-1)) + } + } + LETTERFOURFORWARD ->{ + if(player.getAttribute("riddle-letter-four",0) == 25){ + player.setAttribute("riddle-letter-four",0) + } + else{ + (player.incrementAttribute("riddle-letter-four",1)) + } + } + ENTER -> { + val letterOne = LETTERS[player.getAttribute("riddle-letter-one", 0)] + val letterTwo = LETTERS[player.getAttribute("riddle-letter-two", 0)] + val letterThree = LETTERS[player.getAttribute("riddle-letter-three", 0)] + val letterFour = LETTERS[player.getAttribute("riddle-letter-four", 0)] + when (player.getAttribute("PeerRiddle", 0)) { + 0 -> { + if (letterOne == "L" && letterTwo == "I" && letterThree == "F" && letterFour == "E") { + sendMessage(player, "You have solved the riddle!") + player.removeAttribute("PeerRiddle") + player.setAttribute("/save:riddlesolved", true) + player.interfaceManager.close() + } else { + sendMessage(player, "You have failed to solve the riddle.") + player.interfaceManager.close() + } + } + 1 -> { + if (letterOne == "M" && letterTwo == "I" && letterThree == "N" && letterFour == "D") { + sendMessage(player, "You have solved the riddle!") + player.removeAttribute("PeerRiddle") + player.setAttribute("/save:riddlesolved", true) + player.interfaceManager.close() + } else { + sendMessage(player, "You have failed to solve the riddle.") + player.interfaceManager.close() + } + } + 2 -> { + if (letterOne == "T" && letterTwo == "I" && letterThree == "M" && letterFour == "E") { + sendMessage(player, "You have solved the riddle!") + player.removeAttribute("PeerRiddle") + player.setAttribute("/save:riddlesolved", true) + player.interfaceManager.close() + } else { + sendMessage(player, "You have failed to solve the riddle.") + player.interfaceManager.close() + } + } + 3 -> { + if (letterOne == "W" && letterTwo == "I" && letterThree == "N" && letterFour == "D") { + sendMessage(player, "You have solved the riddle!") + player.removeAttribute("PeerRiddle") + player.setAttribute("/save:riddlesolved", true) + player.interfaceManager.close() + } else { + sendMessage(player, "You have failed to solve the riddle.") + player.interfaceManager.close() + } + } + } + } + } + return@on true + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..684eece92 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SeersHouseListeners.kt @@ -0,0 +1,1076 @@ +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 +import core.game.node.entity.player.link.audio.Audio +import core.game.node.scenery.Scenery +import core.game.node.scenery.SceneryBuilder +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 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 { + + val WESTDOOR = 4165 + val EASTDOOR = 4166 + val WESTLADDER = 4163 + val EASTLADDER = 4164 + val WESTTRAPDOOR = getScenery(2631, 3663, 2)!! + val EASTTRAPDOOR = getScenery(2636, 3663, 2)!! + val TAP = 4176 + val COOKINGRANGE = 4172 + val DRAIN = 4175 + val CUPBOARD = 4178 + val BALANCECHEST = 4170 + val UNICORNHEAD = 4181 + val SOUTHBOXES = 4183 + val CHEST = intArrayOf (4167,4168) + val SOUTHCRATES = 4186 + val EASTCRATES = 4185 + val EASTBOXES = 4184 + val FROZENTABLE = 4169 + val BOOKCASE = 4171 + val BULLSHEAD = 4182 + val MURAL = 4179 + + val OLDREDDISK = Items.OLD_RED_DISK_9947 + val WOODENDISK = Items.WOODEN_DISK_3744 + val REDHERRING = Items.RED_HERRING_3742 + val BLUETHREAD = Items.THREAD_3719 + val PICK = Items.PICK_3720 + val SHIPTOY = Items.SHIP_TOY_3721 + val MAGNET = Items.MAGNET_3718 + val REDGOOP = Items.STICKY_RED_GOOP_3746 + val REDDISK = Items.RED_DISK_3743 + val VASELID = Items.VASE_LID_3737 + val VASE = Items.VASE_3734 + val FULLVASE = Items.VASE_OF_WATER_3735 + val FROZENVASE = Items.FROZEN_VASE_3736 + val SEALEDEMPTYVASE = Items.SEALED_VASE_3738 + val SEALEDFULLVASE = Items.SEALED_VASE_3739 + val FROZENKEY = Items.FROZEN_KEY_3741 + val SEERSKEY = Items.SEERS_KEY_3745 + val EMPTYBUCKET = Items.EMPTY_BUCKET_3727 + val ONEFIFTHBUCKET = Items.ONE_5THS_FULL_BUCKET_3726 + val TWOFIFTHBUCKET = Items.TWO_5THS_FULL_BUCKET_3725 + val THREEFIFTHBUCKET = Items.THREE_5THS_FULL_BUCKET_3724 + val FOURFIFTHBUCKET = Items.FOUR_5THS_FULL_BUCKET_3723 + val FULLBUCKET = Items.FULL_BUCKET_3722 + val FROZENBUCKET = Items.FROZEN_BUCKET_3728 + val EMPTYJUG = Items.EMPTY_JUG_3732 + val ONETHIRDJUG = Items.ONE_THIRDRDS_FULL_JUG_3731 + val TWOTHIRDJUG = Items.TWO_THIRDSRDS_FULL_JUG_3730 + val FULLJUG = Items.FULL_JUG_3729 + val FROZENJUG = Items.FROZEN_JUG_3733 + + val JUGS = intArrayOf( + Items.EMPTY_JUG_3732, + Items.ONE_THIRDRDS_FULL_JUG_3731, + Items.TWO_THIRDSRDS_FULL_JUG_3730, + Items.FULL_JUG_3729 + ) + + val BUCKETS = intArrayOf( + Items.EMPTY_BUCKET_3727, + Items.ONE_5THS_FULL_BUCKET_3726, + Items.TWO_5THS_FULL_BUCKET_3725, + Items.THREE_5THS_FULL_BUCKET_3724, + Items.FOUR_5THS_FULL_BUCKET_3723, + Items.FULL_BUCKET_3722 + ) + + val DISKS = intArrayOf( + Items.OLD_RED_DISK_9947, + Items.RED_DISK_3743 + ) + + val EASTZONE = ZoneBorders(2635, 3662, 2637, 3664, 2) + + val WESTZONE = ZoneBorders(2630, 3662, 2632, 3664, 2) + + override fun defineListeners() { + + on(WESTDOOR, IntType.SCENERY, "open"){ player, node -> + if(!player.getAttribute("PeerStarted",false)){ + sendDialogue(player,"You should probably talk to the owner of this home.") + } + 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 + } + else if(player.getAttribute("PeerRiddle",5) < 5){ + player.dialogueInterpreter.open(DoorRiddleDialogue(player), Scenery(WESTDOOR,node.location)) + } + 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() + } 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)!!) + } + } + return@on true + } + + on(WESTLADDER, IntType.SCENERY, "Climb-up") { player, node -> + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2631, 3664, 2)) + return@on true + } + + on(WESTTRAPDOOR.id, IntType.SCENERY, "Climb-down") { player, node -> + if(player.location.x < 2634){ + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2631, 3664, 0)) + } + else if(player.location.x > 2634){ + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2636, 3664, 0)) + } + return@on true + } + + on(WESTTRAPDOOR.id, IntType.SCENERY, "Close") { player, node -> + SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(4174)) + return@on true + } + + on(WESTTRAPDOOR.id, IntType.SCENERY, "Open") { player, node -> + SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(4173)) + return@on true + } + + on(EASTTRAPDOOR.id, IntType.SCENERY, "Open") { player, node -> + SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(4173)) + return@on true + } + + on(EASTTRAPDOOR.id, IntType.SCENERY, "Close") { player, node -> + SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(4174)) + return@on true + } + + on(EASTTRAPDOOR.id, IntType.SCENERY, "Climb-down") { player, node -> + if(player.location.x > 2634){ + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2636, 3664, 0)) + } + return@on true + } + + on(EASTLADDER, IntType.SCENERY, "Climb-Up") { player, node -> + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2636, 3664, 2)) + return@on true + } + + on(CUPBOARD, IntType.SCENERY, "Search") { player, node -> + sendMessage(player,"You search the cupboard...") + if(player.inventory.contains(EMPTYBUCKET,1)){ + sendMessage(player,"You find nothing of interest.") + } + else{ + addItem(player,EMPTYBUCKET) + sendMessage(player,"You find a bucket with a number five painted on it.") + } + return@on true + } + + on(BALANCECHEST, IntType.SCENERY, "Open") { player, node -> + sendDialogue(player,"This chest is securely locked shut. There is some kind of balance attached to the lock, and a number four is painted just above it.") + return@on true + } + + on(BOOKCASE, IntType.SCENERY, "Search") { player, node -> + sendMessage(player,"You search the bookcase...") + if(player.inventory.contains(REDHERRING,1)){ + sendMessage(player,"You find nothing of interest.") + } + else{ + addItem(player,REDHERRING) + sendMessage(player,"Hidden behind some old books, you find a red herring.") + } + return@on true + } + + on(SOUTHBOXES, IntType.SCENERY, "Search"){ player, node -> + sendMessage(player,"You search the boxes...") + if(player.inventory.contains(BLUETHREAD,1)){ + sendMessage(player,"You find nothing of interest.") + } + else{ + addItem(player,BLUETHREAD) + sendMessage(player,"You find some thread hidden inside.") + } + return@on true + } + + on(CHEST, IntType.SCENERY, "Open"){ player, node -> + SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(4168)) + return@on true + } + + on(CHEST, IntType.SCENERY, "Close"){ player, node -> + SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(4167)) + return@on true + } + + on(CHEST, IntType.SCENERY, "Search"){ player, node -> + sendMessage(player,"You search the chest...") + if(player.inventory.contains(EMPTYJUG,1)){ + sendMessage(player,"You find nothing of interest.") + } + else{ + addItem(player,EMPTYJUG) + sendMessage(player,"You find a jug with a number three painted on it") + } + return@on true + } + + on(EASTCRATES, IntType.SCENERY, "Search") { player, node -> + sendMessage(player,"You search the crates...") + if(player.inventory.contains(PICK,1)){ + sendMessage(player,"You find nothing of interest.") + } + else{ + addItem(player,PICK) + sendMessage(player,"You find a small pick hidden inside.") + } + return@on true + } + + on(SOUTHCRATES, IntType.SCENERY, "Search") { player, node -> + sendMessage(player,"You search the crates...") + if(player.inventory.contains(SHIPTOY,1)){ + sendMessage(player,"You find nothing of interest.") + } + else{ + addItem(player,SHIPTOY) + sendMessage(player,"You findd a toy ship hidden inside") + } + return@on true + } + + on(BULLSHEAD, IntType.SCENERY, "Study") { player, node -> + player.dialogueInterpreter.open(BullHeadDialogue(player)) + return@on true + } + + on(UNICORNHEAD, IntType.SCENERY, "Study") { player, node -> + player.dialogueInterpreter.open(UnicornHeadDialogue(player)) + return@on true + } + + onUseWith(IntType.SCENERY,REDHERRING,COOKINGRANGE) { player, used, with -> + player.animate(Animation(883,Animator.Priority.HIGH)) + 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...") + return@onUseWith true + } + + onUseWith(IntType.ITEM,REDGOOP,WOODENDISK) { player, used, with -> + removeItem(player, WOODENDISK) + removeItem(player, REDGOOP) + addItem(player,REDDISK) + sendMessage(player,"You coat the wooden coin with the sticky red goop.") + return@onUseWith true + } + + on(MURAL, IntType.SCENERY, "Study") { player, node -> + sendMessage(player,"The mural feels like something is missing.") + return@on true + } + + onUseWith(IntType.SCENERY, BUCKETS, TAP) { player, bucket, _ -> + when(bucket.id){ + 3727 ->{ + removeItem(player,EMPTYBUCKET) + addItem(player,FULLBUCKET) + sendMessage(player,"You fill the bucket from the tap.") + return@onUseWith true + } + ONEFIFTHBUCKET ->{ + removeItem(player,ONEFIFTHBUCKET) + addItem(player,FULLBUCKET) + sendMessage(player,"You fill the bucket from the tap.") + } + TWOFIFTHBUCKET ->{ + removeItem(player,TWOFIFTHBUCKET) + addItem(player,FULLBUCKET) + sendMessage(player,"You fill the bucket from the tap.") + } + THREEFIFTHBUCKET ->{ + removeItem(player,THREEFIFTHBUCKET) + addItem(player,FULLBUCKET) + sendMessage(player,"You fill the bucket from the tap.") + } + FOURFIFTHBUCKET ->{ + removeItem(player,FOURFIFTHBUCKET) + addItem(player,FULLBUCKET) + sendMessage(player,"You fill the bucket from the tap.") + } + FULLBUCKET ->{ + sendMessage(player,"The bucket is already full!") + } + else -> sendMessage(player,"FUCKFUCKFUCKFUCK") + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, JUGS, TAP) { player, used, with -> + when(used.id){ + EMPTYJUG ->{ + removeItem(player,EMPTYJUG) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug from the tap.") + } + ONETHIRDJUG ->{ + removeItem(player,ONETHIRDJUG) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug from the tap.") + } + TWOTHIRDJUG ->{ + removeItem(player,TWOTHIRDJUG) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug from the tap.") + } + FULLJUG ->{ + sendMessage(player,"The jug is already full!") + } + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, BUCKETS, DRAIN) { player, used, with -> + when(used.id){ + EMPTYBUCKET ->{ + sendMessage(player,"The bucket is already empty!") + } + ONEFIFTHBUCKET ->{ + removeItem(player,ONEFIFTHBUCKET) + addItem(player,EMPTYBUCKET) + sendMessage(player,"You empty the bucket down the drain.") + } + TWOFIFTHBUCKET ->{ + removeItem(player,TWOFIFTHBUCKET) + addItem(player,EMPTYBUCKET) + sendMessage(player,"You empty the bucket down the drain.") + } + THREEFIFTHBUCKET ->{ + removeItem(player,THREEFIFTHBUCKET) + addItem(player,EMPTYBUCKET) + sendMessage(player,"You empty the bucket down the drain.") + } + FOURFIFTHBUCKET ->{ + removeItem(player,FOURFIFTHBUCKET) + addItem(player,EMPTYBUCKET) + sendMessage(player,"You empty the bucket down the drain.") + } + FULLBUCKET ->{ + removeItem(player,FULLBUCKET) + addItem(player,EMPTYBUCKET) + sendMessage(player,"You empty the bucket down the drain.") + } + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, JUGS, DRAIN) { player, used, with -> + when(used.id){ + EMPTYJUG ->{ + sendMessage(player,"The jug is already empty!") + } + ONETHIRDJUG ->{ + removeItem(player,ONETHIRDJUG) + addItem(player,EMPTYJUG) + sendMessage(player,"You empty the jug down the drain.") + } + TWOTHIRDJUG ->{ + removeItem(player,TWOTHIRDJUG) + addItem(player,EMPTYJUG) + sendMessage(player,"You empty the jug down the drain.") + } + FULLJUG ->{ + removeItem(player,FULLJUG) + addItem(player,EMPTYJUG) + sendMessage(player,"You empty the jug down the drain.") + } + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, FOURFIFTHBUCKET, BALANCECHEST) { player, used, with -> + removeItem(player,FOURFIFTHBUCKET) + addItem(player,VASE) + sendMessage(player,"You place the bucket on the scale.") + sendMessage(player,"It is a perfect counterweight and balances precisely.") + sendMessage(player,"You take a strange looking vase out of the chest.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, DISKS, MURAL) { player, used, with -> + when(used.id){ + REDDISK ->{ + if(player.getAttribute("olddiskplaced",false)){ + removeItem(player,REDDISK) + addItem(player,VASELID) + sendDialogue(player,"You put the red disk into the empty hole on the mural. It is a perfect fit! The centre of the mural appears to have become loose.") + } + else if(player.getAttribute("reddiskplaced",false)){ + sendMessage(player,"You already have a disk in that spot") + } + else{ + removeItem(player, REDDISK) + sendMessage(player,"You put the red disk into the empty hold on the mural.") + sendMessage(player, "It's a perfect fit!") + player.setAttribute("reddiskplaced",true) + } + } + OLDREDDISK -> { + if(player.getAttribute("reddiskplaced", false)) { + removeItem(player, OLDREDDISK) + addItem(player, VASELID) + sendDialogue(player, "You put the red disk into the empty hole on the mural. It is a perfect fit! The centre of the mural appears to have become loose.") + } + else if(player.getAttribute("olddiskplaced", false)) { + sendMessage(player, "You already have a disk in that spot") + } + else{ + removeItem(player, OLDREDDISK) + sendMessage(player, "You put the red disk into the empty hold on the mural.") + sendMessage(player, "It's a perfect fit!") + player.setAttribute("olddiskplaced", true) + } + } + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, BUCKETS, BALANCECHEST) { player, used, with -> + when(used.id){ + EMPTYBUCKET ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the bucket on the scale") + sendMessage(player,"It is too light to balance it properly") + } + ONEFIFTHBUCKET ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the bucket on the scale") + sendMessage(player,"It is too light to balance it properly") + } + TWOFIFTHBUCKET ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the bucket on the scale") + sendMessage(player,"It is too light to balance it properly") + } + THREEFIFTHBUCKET ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the bucket on the scale") + sendMessage(player,"It is too light to balance it properly") + } + FOURFIFTHBUCKET ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the bucket on the scale") + sendMessage(player,"It is a perfect counterweight and balances precisely.") + sendMessage(player,"You take a strange looking vase out of the chest.") + addItem(player,VASE) + } + FULLBUCKET ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the bucket on the scale") + sendMessage(player,"It is too heavy to balance it properly") + } + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, JUGS, BALANCECHEST) { player, used, with -> + when(used.id){ + EMPTYJUG ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the jug on the scale") + sendMessage(player,"It is too light to balance it properly") + } + ONETHIRDJUG ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the jug on the scale") + sendMessage(player,"It is too light to balance it properly") + } + TWOTHIRDJUG ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the jug on the scale") + sendMessage(player,"It is too light to balance it properly") + } + FULLJUG ->{ + player.animate(Animation(883,Animator.Priority.HIGH)) + sendMessage(player,"You place the jug on the scale") + sendMessage(player,"It is too light to balance it properly") + } + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, BUCKETS, FROZENTABLE) { player, used, with -> + when(used.id){ + EMPTYBUCKET -> sendMessage(player,"Your empty bucket gets very cold on the icy table.") + FULLBUCKET -> { + player.animate(Animation(883,Animator.Priority.HIGH)) + removeItem(player,FULLBUCKET) + addItem(player,FROZENBUCKET) + sendMessage(player,"They icy table immediately freezes the water in your bucket.") + } + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, JUGS, FROZENTABLE) { player, used, with -> + when(used.id){ + EMPTYJUG -> sendMessage(player,"Your empty jug gets very cold on the icy table.") + FULLJUG -> { + player.animate(Animation(883,Animator.Priority.HIGH)) + removeItem(player,FULLJUG) + addItem(player,FROZENJUG) + sendMessage(player,"The icy table immediately freezes the water in your jug.") + } + } + return@onUseWith true + } + + onUseWith(IntType.ITEM,BUCKETS,*JUGS) { player, used, with -> + when(used.id){ + ONEFIFTHBUCKET -> { + when(with.id){ + EMPTYJUG ->{ + removeItem(player,ONEFIFTHBUCKET) + removeItem(player,EMPTYJUG) + addItem(player,EMPTYBUCKET) + addItem(player,ONETHIRDJUG) + sendMessage(player,"You empty the bucket into the jug") + } + ONETHIRDJUG ->{ + removeItem(player,ONEFIFTHBUCKET) + removeItem(player,ONETHIRDJUG) + addItem(player,EMPTYBUCKET) + addItem(player,TWOTHIRDJUG) + sendMessage(player,"You empty the bucket into the jug") + } + TWOTHIRDJUG ->{ + removeItem(player,ONEFIFTHBUCKET) + removeItem(player,TWOTHIRDJUG) + addItem(player,EMPTYBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + FULLJUG -> sendMessage(player,"The jug is already full!") + } + } + TWOFIFTHBUCKET -> { + when(with.id){ + EMPTYJUG ->{ + removeItem(player,TWOFIFTHBUCKET) + removeItem(player,EMPTYJUG) + addItem(player,EMPTYBUCKET) + addItem(player,TWOTHIRDJUG) + sendMessage(player,"You empty the bucket into the jug") + } + ONETHIRDJUG ->{ + removeItem(player,TWOFIFTHBUCKET) + removeItem(player,ONETHIRDJUG) + addItem(player,EMPTYBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + TWOTHIRDJUG ->{ + removeItem(player,TWOFIFTHBUCKET) + removeItem(player,TWOTHIRDJUG) + addItem(player,ONEFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + FULLJUG ->sendMessage(player,"The jug is already full!") + } + } + THREEFIFTHBUCKET -> { + when(with.id){ + EMPTYJUG ->{ + removeItem(player,THREEFIFTHBUCKET) + removeItem(player,EMPTYJUG) + addItem(player,EMPTYBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + ONETHIRDJUG ->{ + removeItem(player,THREEFIFTHBUCKET) + removeItem(player,ONETHIRDJUG) + addItem(player,ONEFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + TWOTHIRDJUG ->{ + removeItem(player,THREEFIFTHBUCKET) + removeItem(player,TWOTHIRDJUG) + addItem(player,TWOFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + FULLJUG ->sendMessage(player,"The jug is already full!") + } + } + FOURFIFTHBUCKET -> { + when(with.id){ + EMPTYJUG ->{ + removeItem(player,FOURFIFTHBUCKET) + removeItem(player,EMPTYJUG) + addItem(player,ONEFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + ONETHIRDJUG ->{ + removeItem(player,FOURFIFTHBUCKET) + removeItem(player,ONETHIRDJUG) + addItem(player,TWOFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + TWOTHIRDJUG ->{ + removeItem(player,FOURFIFTHBUCKET) + removeItem(player,TWOTHIRDJUG) + addItem(player,THREEFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + FULLJUG ->sendMessage(player,"The jug is already full!") + } + } + FULLBUCKET -> { + when(with.id){ + EMPTYJUG ->{ + removeItem(player,FULLBUCKET) + removeItem(player,EMPTYJUG) + addItem(player,TWOFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + ONETHIRDJUG ->{ + removeItem(player,FULLBUCKET) + removeItem(player,ONETHIRDJUG) + addItem(player,THREEFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + TWOTHIRDJUG ->{ + removeItem(player,FULLBUCKET) + removeItem(player,TWOTHIRDJUG) + addItem(player,FOURFIFTHBUCKET) + addItem(player,FULLJUG) + sendMessage(player,"You fill the jug to the brim.") + } + FULLJUG ->sendMessage(player,"The jug is already full!") + } + } + } + return@onUseWith true + } + + onUseWith(IntType.ITEM,JUGS,*BUCKETS) { player, used, with -> + when(used.id){ + ONETHIRDJUG->{ + when(with.id){ + EMPTYBUCKET ->{ + removeItem(player,ONETHIRDJUG) + removeItem(player,EMPTYBUCKET) + addItem(player,EMPTYJUG) + addItem(player,ONEFIFTHBUCKET) + sendMessage(player,"You empty the jug into the bucket.") + } + ONEFIFTHBUCKET ->{ + removeItem(player,ONETHIRDJUG) + removeItem(player,ONEFIFTHBUCKET) + addItem(player,EMPTYJUG) + addItem(player,TWOFIFTHBUCKET) + sendMessage(player,"You empty the jug into the bucket.") + } + TWOFIFTHBUCKET ->{ + removeItem(player,ONETHIRDJUG) + removeItem(player,TWOFIFTHBUCKET) + addItem(player,EMPTYJUG) + addItem(player,THREEFIFTHBUCKET) + sendMessage(player,"You empty the jug into the bucket.") + } + THREEFIFTHBUCKET ->{ + removeItem(player,ONETHIRDJUG) + removeItem(player,THREEFIFTHBUCKET) + addItem(player,EMPTYJUG) + addItem(player,FOURFIFTHBUCKET) + sendMessage(player,"You empty the jug into the bucket.") + } + FOURFIFTHBUCKET ->{ + removeItem(player,ONETHIRDJUG) + removeItem(player,FOURFIFTHBUCKET) + addItem(player,EMPTYJUG) + addItem(player,FULLBUCKET) + sendMessage(player,"You fill the bucket to the brim.") + } + FULLBUCKET -> sendMessage(player,"The bucket is already full!") + } + } + TWOTHIRDJUG->{ + when(with.id){ + EMPTYBUCKET ->{ + removeItem(player,TWOTHIRDJUG) + removeItem(player,EMPTYBUCKET) + addItem(player,TWOFIFTHBUCKET) + addItem(player,EMPTYJUG) + sendMessage(player,"You empty the jug into the bucket.") + } + ONEFIFTHBUCKET ->{ + removeItem(player,TWOTHIRDJUG) + removeItem(player,ONEFIFTHBUCKET) + addItem(player,THREEFIFTHBUCKET) + addItem(player,EMPTYJUG) + sendMessage(player,"You empty the jug into the bucket.") + } + TWOFIFTHBUCKET ->{ + removeItem(player,TWOTHIRDJUG) + removeItem(player,TWOFIFTHBUCKET) + addItem(player,FOURFIFTHBUCKET) + addItem(player,EMPTYJUG) + sendMessage(player,"You empty the jug into the bucket.") + } + THREEFIFTHBUCKET ->{ + removeItem(player,TWOTHIRDJUG) + removeItem(player,THREEFIFTHBUCKET) + addItem(player,FULLBUCKET) + addItem(player,EMPTYJUG ) + sendMessage(player,"You fill the bucket to the brim.") + } + FOURFIFTHBUCKET ->{ + removeItem(player,TWOTHIRDJUG) + removeItem(player,FOURFIFTHBUCKET) + addItem(player,FULLBUCKET) + addItem(player,ONETHIRDJUG) + sendMessage(player,"You fill the bucket to the brim.") + } + FULLBUCKET -> sendMessage(player,"The bucket is already full!") + } + } + FULLJUG->{ + when(with.id){ + EMPTYBUCKET ->{ + removeItem(player,FULLJUG) + removeItem(player,EMPTYBUCKET) + addItem(player,THREEFIFTHBUCKET) + addItem(player,EMPTYJUG) + sendMessage(player,"You empty the jug into the bucket.") + } + ONEFIFTHBUCKET ->{ + removeItem(player,FULLJUG) + removeItem(player,ONEFIFTHBUCKET) + addItem(player,FOURFIFTHBUCKET) + addItem(player,EMPTYJUG) + sendMessage(player,"You empty the jug into the bucket.") + } + TWOFIFTHBUCKET ->{ + removeItem(player,FULLJUG) + removeItem(player,TWOFIFTHBUCKET) + addItem(player,FULLBUCKET) + addItem(player,EMPTYJUG) + sendMessage(player,"You fill the bucket to the brim.") + } + THREEFIFTHBUCKET ->{ + removeItem(player,FULLJUG) + removeItem(player,THREEFIFTHBUCKET) + addItem(player,FULLBUCKET) + addItem(player,ONETHIRDJUG) + sendMessage(player,"You fill the bucket to the brim.") + } + FOURFIFTHBUCKET ->{ + removeItem(player,FULLJUG) + removeItem(player,FOURFIFTHBUCKET) + addItem(player,FULLBUCKET) + addItem(player,TWOTHIRDJUG) + sendMessage(player,"You fill the bucket to the brim.") + } + FULLBUCKET -> sendMessage(player,"The bucket is already full!") + } + } + } + return@onUseWith true + } + + on(VASE, IntType.ITEM, "Shake") { player, node -> + sendDialogue(player,"You shake the strangely shaped Vase. From the sound of it there is something metallic inside, but the neck of th vase is too narrow for it to come out.") + return@on true + } + + onUseWith(IntType.ITEM,PICK,VASE) { player, used, with -> + sendMessage(player,"The pick wouldn't be strong enough to break the vase open.") + return@onUseWith true + } + + onUseWith(IntType.ITEM,MAGNET,VASE) { player, used, with -> + sendMessage(player,"You use the magnet on the vase. The metallic object inside moves.") + sendMessage(player,"The neck of the vase is too thin for thee object to come out of the vase.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, VASE, TAP) { player, used, with -> + removeItem(player,VASE) + addItem(player,FULLVASE) + sendMessage(player,"You fill the strange looking vase with water.") + return@onUseWith true + } + + onUseWith(IntType.ITEM,FULLJUG,VASE) { player, used, with -> + removeItem(player,FULLJUG) + removeItem(player,VASE) + addItem(player,EMPTYJUG) + addItem(player,FULLVASE) + sendMessage(player,"You fill the vase with water.") + return@onUseWith true + } + + on(FULLVASE, IntType.ITEM, "Shake") { player, node -> + sendDialogue(player,"You shake the strangely shaped vase. The water inside it sloshes a little. Some spills out of the neck of the vase.") + return@on true + } + + onUseWith(IntType.ITEM,VASE,VASELID) { player, used, with -> + removeItem(player,VASE) + removeItem(player,VASELID) + addItem(player,SEALEDEMPTYVASE) + sendMessage(player,"You screw the lid on tightly.") + return@onUseWith true + } + + onUseWith(IntType.ITEM,FULLVASE,VASELID) { player, used, with -> + removeItem(player,FULLVASE) + removeItem(player,VASELID) + addItem(player,SEALEDFULLVASE) + sendMessage(player,"You screw the lid on tightly.") + return@onUseWith true + } + + on(SEALEDEMPTYVASE, IntType.ITEM, "Remove-lid") { player, node -> + removeItem(player,SEALEDEMPTYVASE) + addItem(player,VASE) + addItem(player,VASELID) + sendMessage(player,"You unscrew the lid from the vase.") + return@on true + } + + on(SEALEDFULLVASE, IntType.ITEM, "Remove-lid") { player, node -> + removeItem(player,SEALEDFULLVASE) + addItem(player,VASE) + addItem(player,VASELID) + sendMessage(player,"You unscrew the lid from the vase.") + return@on true + } + + onUseWith(IntType.SCENERY, FULLVASE, FROZENTABLE) { player, used, with -> + player.animate(Animation(883,Animator.Priority.HIGH)) + removeItem(player,FULLVASE) + addItem(player,FROZENVASE) + sendMessage(player,"The icy table immediately freezes the water in your vase.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, SEALEDFULLVASE, FROZENTABLE) { player, used, with -> + player.animate(Animation(883,Animator.Priority.HIGH)) + removeItem(player,SEALEDFULLVASE) + addItem(player,FROZENKEY) + sendMessage(player,"The water expands as it freezes, and shatters the vase.") + sendMessage(player,"You are left with a key encased in ice.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, FROZENBUCKET, COOKINGRANGE) { player, used, with -> + player.animate(Animation(883,Animator.Priority.HIGH)) + 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.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, FROZENJUG, COOKINGRANGE) { player, used, with -> + player.animate(Animation(883,Animator.Priority.HIGH)) + 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.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, FROZENVASE, COOKINGRANGE) { player, used, with -> + player.animate(Animation(883,Animator.Priority.HIGH)) + 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.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, FROZENKEY, COOKINGRANGE) { player, used, with -> + player.animate(Animation(883,Animator.Priority.HIGH)) + playAudio(player, Sounds.FRY_2577) + removeItem(player,FROZENKEY) + addItem(player,SEERSKEY) + sendMessage(player,"The heat of the range melts the ice around the key.") + return@onUseWith true + } + + on(EASTDOOR, IntType.SCENERY, "Open") { player, node -> + if(player.inventory.contains(SEERSKEY,1)){ + player.setAttribute("/save:housepuzzlesolved",true) + player.inventory.clear() + 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!") + } + else sendMessage(player,"This door is locked tightly shut.") + return@on true + } + + } +} + +class BullHeadDialogue(player:Player) : DialogueFile() { + + private val WOODENDISK = Items.WOODEN_DISK_3744 + + val p = player + + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> sendDialogue(p,"You notice there is something unusual about the right eye of this bulls' head...").also { + if(p.inventory.contains(WOODENDISK,1)){ + stage = 10 + }else stage = 1 + } + 1 -> sendDialogue(p,"It is not an eye at all, but some kind of disk made of wood. You take it from the head").also { + addItem(p,WOODENDISK) + stage = 1000 + } + 10 -> sendMessage(p,"It's unusual because you've already taken it!").also { stage = 1000 } + 1000 -> end() + } + } +} + +class UnicornHeadDialogue(player:Player) : DialogueFile() { + + private val OLDREDDISK = Items.OLD_RED_DISK_9947 + + val p = player + + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> sendDialogue(p,"You notice there is something unusual about the left eye of this unicorn head...").also { + if(p.inventory.contains(OLDREDDISK,1)){ + stage = 10 + }else stage = 1 + } + 1 -> sendDialogue(p,"It is not an eye at all, but some kind of redd coloured disk. You take it from the head").also { + addItem(p,OLDREDDISK) + stage = 1000 + } + 10 -> sendMessage(p,"It's unusual because you've already taken it!").also { stage = 1000 } + 1000 -> end() + } + } +} + +class DoorRiddleDialogue(player: Player) : DialogueFile() { + + private val RIDDLEONE = arrayOf( + "My first is in the well, but not at sea.", + "My second in 'I', but not in 'me'.", + "My third is in flies, but insects not found.", + "My last is in earth, but not in the ground.", + "My whole when stolen from you, caused you death.", + "What am I?" + ) + + private val RIDDLETWO = arrayOf( + "My first is in mage, but not in wizard.", + "My second in goblin, and also in lizard.", + "My third is in night, but not in the day.", + "My last is in fields, but not in the hay.", + "My whole is the most powerful tool you will ever possess.", + "What am I?" + ) + + private val RIDDLETHREE = arrayOf( + "My first is in water, and also in tea.", + "My second in fish, but not in the sea.", + "My third in mountains, but not underground.", + "My last is in strike, but not in pound.", + "My whole crushes mountains, drains rivers, and destroys civilisations.", + "All that live fear my passing.", + "What am I?" + ) + + private val RIDDLEFOUR = arrayOf( + "My first is in wizard, but not in a mage.", + "My second in jail, but not in a cage.", + "My third is in anger, but not in a rage.", + "My last in a drawing, but not on a page.", + "My whole helps to make bread, let birds fly and boats sail.", + "What am I?" + ) + + private val RIDDLEANSWER = arrayOf("LIFE","MIND","TIME","WIND") + + val p = player + + val riddle = when(p.getAttribute("PeerRiddle",0)){ + 0 -> RIDDLEONE + 1 -> RIDDLETWO + 2 -> RIDDLETHREE + 3 -> RIDDLEFOUR + else -> RIDDLEONE + } + + var init = true + + override fun handle(componentID: Int, buttonID: Int) { + if(init){ + stage = 1 + init = false + } + when(stage){ + 1 -> sendDialogue(player!!,"There is a combination lock on this door. Above the lock you can see that there is a metal plaque with a riddle on it.").also { stage = 5 } + 5 -> options("Read the riddle","Solve the riddle","Forget it").also { stage = 10 } + 10 -> when(buttonID){ + 1 -> dialogue(riddle[0],riddle[1],riddle[2],riddle[3]).also { stage = 20 } + 2 -> { + openInterface(p,298) + end() + } + 3 -> end() + } + 15 ->{ + dialogue(riddle[0],riddle[1],riddle[2],riddle[3]) + stage = 20 + } + 20 -> if(riddle.contentEquals(RIDDLETHREE)){ + dialogue(riddle[4],riddle[5],riddle[6]).also { stage = 1000 } + }else{ + dialogue(riddle[4],riddle[5]).also { stage = 1000 } + } + 1000 -> end() + } + } +} 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 new file mode 100644 index 000000000..8fd16e3fc --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigliTheHuntsman.kt @@ -0,0 +1,162 @@ +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.item.Item +import core.plugin.Initializable +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import content.data.Quests +import org.rs09.consts.Items + +@Initializable +class SigliTheHuntsman(player: Player? = null) : DialoguePlugin(player){ + override fun open(vararg args: Any?): Boolean { + if(player?.inventory?.contains(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(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(FacialExpression.ASKING,"I have an item to trade.") + stage = 175 + return true + } + if(player?.getAttribute("sigmund-steps", 0) == 6){ + npcl(FacialExpression.HAPPY,"Greetings outerlander.") + stage = 160 + return true + } + else if(player?.getAttribute("sigmund-steps", 0) == 5){ + npcl(FacialExpression.HAPPY,"Greetings outerlander.") + stage = 150 + return true + } + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player?.getAttribute("fremtrials:sigli-vote",false)!!) { + npc("You have my vote!") + stage = 1000 + return true + } + if(player?.getAttribute("fremtrials:draugen-killed",false)!!){ + npc("I saw the entire hunt. Let me take that talisman from","you, I would be honored to speak out for you to our","council of elders after such a hunt, outerlander.") + stage = 100 + return true + } + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + playerl(FacialExpression.HAPPY,"Hello again Sigli.") + stage = 180 + return true + } + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0){ + npc("What do you want outerlander?") + stage = 0 + return true + } + else{ + 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 + } + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> player("Are you a member of the council?").also{stage++} + 1 -> npc("The Fremennik council of elders? I am pleased to say","that I am. My value as a huntsman is recognized by","my position there.").also { stage++ } + 2 -> player("I was wondering if I could persuade you to vouch for","me as a member of your clan?").also { stage++ } + 3 -> npc("You? ... well... I am not totally against the idea","outerlander. If you can demonstrate some hunting skills","then perhaps I may offer my vote.").also { stage++ } + 4 -> player("How can I prove my hunting skills to you? I can go","kill, like, a hundred chickens for you right now!").also { stage++ } + 5 -> npc("Chickens? You think that would impress me?").also { stage++ } + 6 -> player("Cows then? I can kill cows until, er, the cows come","home.").also { stage++ } + 7 -> npc("No. The prey I have in mind for you to prove your","worth to me is something far more dangerous. Far","more difficult. Far more deadly.").also { stage++ } + 8 -> player("Not... Giant Rats?!?!").also { stage++ } + 9 -> npc("I suspect you are mocking me outerlander. You will","need to prove your skill as a hunter to me by tracking","and defeating... The Draugen.").also { stage++ } + 10 -> options("What's a Draugen?","Forget it.").also { stage++ } + 11 -> when(buttonId){ + 1 -> player("What's a Draugen? Some kind of cheap rip-","off of a dragon?").also { stage++ } + 2 -> player("Forget it.").also { stage = 1000 } + } + + //What's a Draugen + 12 -> npc("Hmmm.. No, the words are slightly similar I suppose,","but they are very different creatures.").also { stage++ } + 13 -> npc("The Draugen is an evil ghost from Fremennik","mythology, that devours souls of those brave","warriors who meet their ends at sea.").also { stage++ } + 14 -> npc("It stalks the coastlines, invisible to all. It brings us bad","fortunes, and curses our journeys across the seas. It is","also unkillable by normal means.").also { stage++ } + 15 -> player("...Let me get this straight; You want me to hunt an","unkillable, invincible, and invisible enemy? How am I","supposed to do that?").also { stage++ } + 16 -> npc("Well outerlander, should you accept my challenge I will","show you a special hunter's trick that will help you. Do","you accept the challenge?").also { stage++ } + 17 -> options("Yes","No").also { stage++ } + 18 -> when(buttonId){ + 1 -> player("Well, I need every vote I can get in the council of","elders, but this certainly sounds impossible to do...").also { stage++ } + 2 -> player("No, I don't think I will.").also { stage = 1000 } + } + + //Yes + 19 -> npc("Not at all outerlander. The Draugen is indeed","impossible to kill but that is not the same as being","impossible to fight against.").also { stage++ } + 20 -> npc("Every time he takes a Fremennik life, he gains in","power, so to keep it from becoming too powerful we","hunters hunt it and steal its life force.").also { stage++ } + 21 -> npc("We do this with a special talisman. Here, take it, it will","let you track the Draugen while it's invisible, and when","you defeat it will absorb its essence.").also { stage++; player?.inventory?.add(Item(3696)) } + 22 -> npc("I want you to track the Draugen, defeat it, and store","its essence in that talisman for me. If you can do this","important task for my clan, I will vote for you.").also { stage++ } + 23 -> npc("Take care of the talisman, and see me when you have","completed this task.").also{stage = 1000; player?.setAttribute("/save:fremtrials:sigli-accepted",true)} + + //Draugen killed + 100 -> player("Thanks!").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(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(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(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(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(FacialExpression.ANNOYED,"Not the one I want, outerlander.").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?): DialoguePlugin { + return SigliTheHuntsman(player) + } + + + + override fun getIds(): IntArray { + return intArrayOf(1281) + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..947ffee6c --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SigmundDialogue.kt @@ -0,0 +1,150 @@ +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 +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests +import core.api.addItemOrDrop + +@Initializable +class SigmundDialogue (player: Player? = null) : DialoguePlugin(player) { + + private val QUEST_ITEMS = intArrayOf(3709,3707,3706,3710,3705,3704,3703,3702,3701,3708,3700,3699,3698) + + val gender = if(player?.isMale == true){ + "brother" + }else "sister" + + val fName = player?.getAttribute("fremennikname","uhhh fucking uhhh geoff lol") + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + playerl(FacialExpression.HAPPY,"Hello there!") + stage = 50 + return true + } + 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(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(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(FacialExpression.HAPPY,"Hello there!") + stage = 25 + return true + } + 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(FacialExpression.HAPPY,"Hello there!") + stage = 40 + return true + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(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(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(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(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(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(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(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") + player?.removeAttribute("sigmundreturning") + player?.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1) + player?.setAttribute("/save:fremtrials:sigmund-vote",true) + 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(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(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(FacialExpression.HAPPY,"Can I see your wares?").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"Nothing thanks").also { stage = 55 } + } + 53 -> npcl(FacialExpression.HAPPY,"Certainly, $fName.").also { + npc.openShop(player) + 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(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?): DialoguePlugin { + return SigmundDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1282) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..4ca7edd4f --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SkulgrimenDialogue.kt @@ -0,0 +1,105 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.addItem +import core.api.removeItem +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 content.data.Quests +import org.rs09.consts.Items + +@Initializable +class SkulgrimenDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if(player?.inventory?.contains(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(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(FacialExpression.ASKING,"Is this trade item for you?") + stage = 26 + return true + } + if(player?.getAttribute("sigmund-steps", 0) == 7){ + 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(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(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(FacialExpression.HAPPY,"Hello!") + stage = 100 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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(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(FacialExpression.HAPPY,"Thanks!").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(FacialExpression.ANNOYED,"Not for me, I'm afraid.").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 ->{ + end() + npc.openShop(player) + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return SkulgrimenDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1303) + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..0f7762d8e --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/SwensenTheNavigator.kt @@ -0,0 +1,155 @@ +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 core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import content.data.Quests +import org.rs09.consts.Items + +@Initializable +class SwensenTheNavigator(player: Player? = null) : DialoguePlugin(player){ + val gender = if (player?.isMale == true){"brother"} else "sister" + val fName = player?.getAttribute("fremennikname","doug hug'em") + override fun open(vararg args: Any?): Boolean { + if(player?.inventory?.contains(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(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(FacialExpression.ASKING,"I have a trade item here.") + stage = 130 + return true + } + if(player?.getAttribute("sigmund-steps", 0) == 9){ + npcl(FacialExpression.HAPPY,"Greetings outerlander.") + stage = 115 + return true + } + else if(player?.getAttribute("sigmund-steps", 0) == 8){ + npcl(FacialExpression.HAPPY,"Greetings outerlander.") + stage = 105 + return true + } + if(player?.getAttribute("fremtrials:maze-complete",false) == true){ + npc("Outerlander! You have finished my maze!","I am genuinely impressed!") + stage = 100 + return true + } else if(player?.getAttribute("fremtrials:swensen-vote",false) == true){ + npc("You have my vote!") + stage = 1000 + return true + } + else if (player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + playerl(FacialExpression.HAPPY,"Hello!") + stage = 140 + return true + } + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0) { + player("Hello!") + stage = 0 + return true + } + else{ + playerl(FacialExpression.HAPPY,"Hello!") + stage = 145 + return true + } + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> {player("I am trying to become a member of the Fremennik","clan! The Chieftan told me that I may be able to gain","your vote at the council of elders?");stage++} + 1 -> {npc("You wish to stop being an outerlander? I can","understand that! I have no reason why I would prevent","you becoming a Fremennik...");stage++} + 2 -> {npc("...but you must first pass a little test for me to prove","you are worthy.");stage++} + 3 -> {player("What kind of test?");stage++} + 4 -> {npc("Well, I serve our clan as a navigator. The seas can be","a fearful place when you know not where you are","heading.");stage++} + 5 -> {npc("Should something happen to me, all members of our","tribe have some basic sense of direction so that they","may always return safely home.");stage++} + 6 -> {npc("If you are able to demonstrate to me that you too have","a good sense of direction then I will recommend you to","the rest of the council of elders immediately.");stage++} + 7 -> {player("Well, how would I go about showing that?");stage++} + 8 -> {npc("Ah, a simple task! Below this building I have constructed","a maze; should you be able to walk from one side to the","other that will be proof to me.");stage++} + 9 -> {npc("You wish to try my challenge?");stage++} + 10 -> {options("Yes","No");stage++} + 11 -> when(buttonId){ + 1 -> {player("A maze? Is that all? Sure, it sounds simple enough.");stage++} + 2 -> {player("No, that sounds too hard.");stage = 1000} + } + + //Yes + 12 -> {npc("I will warn you outerlander, this maze was designed by","myself, and is of the most fiendish complexity!");stage++} + 13 -> {player("Oh really? Watch and learn...");stage = 1000;player?.setAttribute("/save:fremtrials:swensen-accepted",true)} + + //After maze + 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?.removeAttribute("fremtrials:swensen-accepted") + player?.setAttribute("/save:fremtrials:swensen-vote",true) + player?.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1) + stage = 1000 + } + + //Sigmund subquest + 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(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(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(FacialExpression.HAPPY,"Here, outerlander; you may take my map of local fishing patterns with my gratitude!").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(FacialExpression.ANNOYED,"It isn't for me, I'm afraid.").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(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?): DialoguePlugin { + return SwensenTheNavigator(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1283) + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..e7b48f9d3 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/TFTInteractionListeners.kt @@ -0,0 +1,491 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.* +import core.game.container.impl.EquipmentContainer.SLOT_WEAPON +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.diary.DiaryType +import core.game.node.entity.player.link.music.MusicEntry +import core.game.node.entity.skill.Skills +import content.global.skill.gather.woodcutting.WoodcuttingSkillPulse +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.game.world.update.flag.context.Graphics +import org.rs09.consts.Items +import org.rs09.consts.NPCs +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 { + + private val BEER = intArrayOf(Items.BEER_3803,Items.BEER_1917) + private val WORKER = NPCs.COUNCIL_WORKMAN_1287 + private val FISH_ALTAR = 4141 + private val FISH = intArrayOf(Items.RAW_SHARK_383,Items.RAW_SEA_TURTLE_395,Items.RAW_MANTA_RAY_389) + private val LOW_ALC_KEG = Items.LOW_ALCOHOL_KEG_3712 + private val KEG = Items.KEG_OF_BEER_3711 + private val TINDERBOX = Items.TINDERBOX_590 + private val CHERRY_BOMB = Items.STRANGE_OBJECT_3713 + private val LIT_BOMB = Items.LIT_STRANGE_OBJECT_3714 + private val PIPE = 4162 + private val PORTALIDs = intArrayOf(2273,2274,2506,2507,2505,2503,2504,5138) + private val SWENSEN_LADDER = 4158 + private val SWAYING_TREE = 4142 + private val KNIFE = Items.KNIFE_946 + private val TREE_BRANCH = Items.BRANCH_3692 + private val LYRE_IDs = intArrayOf(14591,14590,6127,6126,6125,3691,3690) + private val THORVALD_LADDER = 34286 + private val THORVALD_LADDER_LOWER = 4188 + private val LALLIS_STEW = 4149 + private val UNSTRUNG_LYRE = Items.UNSTRUNG_LYRE_3688 + private val GOLDEN_FLEECE = Items.GOLDEN_FLEECE_3693 + private val GOLDEN_WOOL = Items.GOLDEN_WOOL_3694 + private val LONGHALL_BACKDOOR = 4148 + private val SHOPNPCS = intArrayOf(NPCs.YRSA_1301,NPCs.SKULGRIMEN_1303,NPCs.THORA_THE_BARKEEP_1300,NPCs.SIGMUND_THE_MERCHANT_1282,NPCs.FISH_MONGER_1315) + private val SPINNING_WHEEL_IDS = intArrayOf(2644,4309,8748,20365,21304,25824,26143,34497,36970,37476) + private val STEW_INGREDIENT_IDS = intArrayOf(Items.POTATO_1942,Items.ONION_1957,Items.CABBAGE_1965,Items.PET_ROCK_3695) + + override fun defineListeners() { + onUseWith(IntType.NPC,BEER,WORKER){ player, beer, _ -> + player.dialogueInterpreter.open(CouncilWorkerFTDialogue(0,true,beer.id), NPC(WORKER)) + return@onUseWith true + } + + onUseWith(IntType.SCENERY, FISH, FISH_ALTAR){ player, fish, _ -> + if(inInventory(player,Items.LYRE_3689) || inInventory(player,Items.ENCHANTED_LYRE_3690)) { + Pulser.submit(SpiritPulse(player, fish.id)) + } else { + sendMessage(player,"I should probably have my lyre with me.") + } + return@onUseWith true + } + + onUseWith(IntType.ITEM,LOW_ALC_KEG,KEG){ player, _, _ -> + if(!getAttribute(player,"fremtrials:keg-mixed",false)){ + if(getAttribute(player,"fremtrials:cherrybomb",false)) { + removeItem(player,LOW_ALC_KEG) + setAttribute(player,"/save:fremtrials:keg-mixed", true) + sendMessage(player,"The cherry bomb in the pipe goes off.") + RegionManager.getLocalEntitys(player).stream().forEach { e -> e.sendChat("What was THAT??") } + sendMessage(player,"You mix the kegs together.") + } else { + player.dialogueInterpreter?.sendDialogue("I can't do this right now. I should create","a distraction.") + } + } else return@onUseWith false + return@onUseWith true + } + + onUseWith(IntType.ITEM,TINDERBOX,CHERRY_BOMB){ player, _, _ -> + if (removeItem(player,CHERRY_BOMB) && addItem(player,LIT_BOMB)) { + sendMessage(player,"You light the strange object.") + } + return@onUseWith true + } + + onUseWith(IntType.ITEM,KNIFE,TREE_BRANCH){ player, _, _ -> + if (!player.skills.hasLevel(Skills.CRAFTING,40)) { + sendDialogue(player, "You need 40 crafting to do this!") + return@onUseWith true + } + if (inInventory(player,KNIFE)) + Pulser.submit(BranchFletchingPulse(player)) + else + sendMessage(player,"You need a knife to do this.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, STEW_INGREDIENT_IDS, LALLIS_STEW){ player, stewIngredient, _ -> + when(stewIngredient.id){ + Items.ONION_1957 -> { + sendDialogue(player,"You added an onion to the stew") + setAttribute(player,"/save:lalliStewOnionAdded",true) + removeItem(player,stewIngredient) + } + Items.POTATO_1942 -> { + sendDialogue(player,"You added a potato to the stew") + setAttribute(player,"/save:lalliStewPotatoAdded",true) + removeItem(player,stewIngredient) + } + Items.CABBAGE_1965 -> { + sendDialogue(player,"You added a cabbage to the stew") + setAttribute(player,"/save:lalliStewCabbageAdded",true) + removeItem(player,stewIngredient) + } + Items.PET_ROCK_3695 -> { + sendDialogue(player,"You added your dear pet rock to the stew") + setAttribute(player,"/save:lalliStewRockAdded",true) + removeItem(player,stewIngredient) + } + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY,GOLDEN_FLEECE,*SPINNING_WHEEL_IDS){ player, _, _ -> + if(removeItem(player,GOLDEN_FLEECE)){ + addItem(player,GOLDEN_WOOL) + animate(player,896) + sendDialogue(player,"You spin the Golden Fleece into a ball of Golden Wool") + } + return@onUseWith true + } + + onUseWith(IntType.ITEM,UNSTRUNG_LYRE,GOLDEN_WOOL){ player, _, _ -> + if(player.getSkills().getLevel(Skills.FLETCHING) >= 25){ + if(removeItem(player,GOLDEN_WOOL) && + removeItem(player,Items.UNSTRUNG_LYRE_3688)){ + animate(player,1248) + addItem(player,Items.LYRE_3689) + sendDialogue(player,"You string the Lyre with the Golden Wool.") + } + } else sendDialogue(player,"You need 25 fletching to do this!") + return@onUseWith true + } + + on(LONGHALL_BACKDOOR, IntType.SCENERY, "open"){ player, door -> + when { + getAttribute(player,"LyreEnchanted",false) -> { + 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) || isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS) -> { + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,door.asScenery()) + } + else -> { + sendNPCDialogue(player,1278,"I didn't give you permission to go backstage!") + } + } + return@on true + } + + on(LYRE_IDs, IntType.ITEM, "play"){ player, lyre -> + if(getAttribute(player,"onStage",false) && !getAttribute(player,"lyreConcertPlayed",false)){ + 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 (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 + } + + on(PIPE, IntType.SCENERY, "put-inside"){ player, _ -> + if(inInventory(player,LIT_BOMB)){ + sendMessage(player,"You stuff the lit object into the pipe.") + setAttribute(player,"/save:fremtrials:cherrybomb",true) + removeItem(player,LIT_BOMB) + } else { + sendMessage(player,"What am I supposed to put in there? A shoe?") + } + return@on true + } + + on(PORTALIDs, IntType.SCENERY, "use"){ player, portal -> + player.properties?.teleportLocation = when(portal.id){ + 2273 -> DestRoom(2639, 10012, 2645, 10018).getCenter() + 2274 -> DestRoom(2650, 10034, 2656, 10040).getCenter() + 2506 -> DestRoom(2662, 10023, 2669, 10029).getCenter() + 2507 -> DestRoom(2626, 10023, 2633, 10029).getCenter() + 2505 -> DestRoom(2650, 10001, 2656, 10007).getCenter() + 2503 -> DestRoom(2662, 10012, 2668, 10018).getCenter() + 2504 -> { + setAttribute(player,"/save:fremtrials:maze-complete",true) + DestRoom(2662, 10034, 2668, 10039).getCenter() + } + else -> getRandomLocation(player) + } + return@on true + } + + 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, 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)) { + player.dialogueInterpreter?.sendDialogues(NPCs.THORVALD_THE_WARRIOR_1289, core.game.dialogue.FacialExpression.ANGRY, + "Outerlander... do not test my patience. I do not take", + "kindly to people wandering in here and acting as though", + "they own the place." + ) + return@on true + } else if (hasEquippableItems(player)) { + player.dialogueInterpreter?.sendDialogues(NPCs.THORVALD_THE_WARRIOR_1289, core.game.dialogue.FacialExpression.ANGRY, + "You may not enter the battleground with any armour", + "or weaponry of any kind." + ) + player.dialogueInterpreter.addAction { _, _ -> + player.dialogueInterpreter?.sendDialogues(NPCs.THORVALD_THE_WARRIOR_1289, core.game.dialogue.FacialExpression.ANGRY, + "If you need to place your equipment into your bank", + "account, I recommend that you speak to the seer. He", + "knows a spell that will do that for you." + ) + } + return@on true + } + + if (player.getExtension(KoscheiSession::class.java) != null) { + KoscheiSession.getSession(player).close() + } + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2671, 10099, 2)) + Pulser.submit(KoscheiPulse(player)) + return@on true + } + + on(THORVALD_LADDER_LOWER, IntType.SCENERY, "climb-up") { player, _ -> + if (player.getExtension(KoscheiSession::class.java) != null) { + KoscheiSession.getSession(player).close() + } + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2666, 3694, 0)) + return@on true + } + + on(SHOPNPCS, IntType.NPC, "Trade") { player, npc -> + 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.") + NPCs.SKULGRIMEN_1303 -> sendDialogue(player,"Only Fremenniks may purchase weapons and armour here.") + NPCs.SIGMUND_THE_MERCHANT_1282 -> sendDialogue(player,"Only Fremenniks may trade with this merchant.") + NPCs.YRSA_1301 -> sendDialogue(player,"Only Fremenniks may buy clothes here.") + NPCs.FISH_MONGER_1315 -> sendDialogue(player,"Only Fremenniks may purchase fish here.") + } + return@on true + } + } + + class DestRoom(val swx: Int, val swy: Int, val nex: Int, val ney: Int) + + fun DestRoom.getCenter(): Location { + return Location((swx + nex) / 2, (swy + ney) / 2).transform(1,0,0) + } + + fun getRandomLocation(player: Player?): Location{ + var obj: Scenery? = null + + while(obj?.id != 5138) { + val objects = player?.viewport?.chunks?.random()?.random()?.objects + obj = objects?.random()?.random() + if(obj == null || obj.location?.equals(Location(0,0,0))!!){ + continue + } + } + return obj.location + } + + fun hasEquippableItems(player: Player?): Boolean { + val container = arrayOf(player!!.inventory, player.equipment) + for (c in container) { + for (i in c.toArray()) { + if (i == null) { + continue + } + if (i.name.lowercase().contains(" rune")) { + return true + } + var slot: Int = i.definition.getConfiguration(ItemConfigParser.EQUIP_SLOT, -1) + if (slot == -1 && i.definition.getConfiguration(ItemConfigParser.WEAPON_INTERFACE, -1) != -1) { + slot = SLOT_WEAPON + } + if (slot >= 0) { + return true + } + } + } + return false + } + + 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 -> { + npc.init() + player.lock() + removeItem(player,fish) + } + 1 -> npc.moveStep() + 2 -> npc.face(player).also { player.face(npc) } + 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 -> 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 { + setAttribute(player,"/save:LyreEnchanted",true) + return true } + } + return false + } + } + + fun playLyreConcert(player: Player, lyre: Int) { + val GENERIC_LYRICS = arrayOf( + "${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." + ) + val CHAMPS_LYRICS = arrayOf( + "The thought of lots of questing,", + "Leaves some people unfulfilled,", + "But I have done my simple best, in", + "Entering the Champions Guild." + ) + val HEROES_LYRICS = arrayOf( + "When it comes to fighting", + "I hit my share of zeroes", + "But I'm well respected at", + "the Guild reserved for Heroes," + ) + val LEGENDS_LYRICS = arrayOf( + "I cannot even start to list", + "The amount of foes I've killed.", + "I will simply tell you this:", + "I've joined the Legends' Guild!" + ) + val questPoints = getQuestPoints(player) + val champGuild = player.achievementDiaryManager?.hasCompletedTask(DiaryType.VARROCK, 1, 1)?: false + val legGuild = questPoints >= 111 + val heroGuild = questPoints >= 5 + val masteredAmount = player.getSkills()?.masteredSkills!! > 0 + var SKILLNAME = getMasteredSkillNames(player) + + val LYRICS = when { + masteredAmount -> { + arrayOf( + "When people speak of training,", + "Some people think they're fine.", + "But they just all seem jealous that", + "My ${SKILLNAME.random()}'s ninety-nine!" + ) + } + legGuild -> LEGENDS_LYRICS + heroGuild -> HEROES_LYRICS + champGuild -> CHAMPS_LYRICS + else -> GENERIC_LYRICS + } + + 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) + player.musicPlayer.play(MusicEntry.forId(164)) + sendChat(player,LYRICS[0]) + } + 6 -> { + animate(player,1320,true) + player.musicPlayer.play(MusicEntry.forId(164)) + sendChat(player,LYRICS[1]) + } + 8 -> { + animate(player,1320,true) + player.musicPlayer.play(MusicEntry.forId(164)) + sendChat(player,LYRICS[2]) + } + 10 ->{ + animate(player,1320,true) + player.musicPlayer.play(MusicEntry.forId(164)) + sendChat(player,LYRICS[3]) + } + 14 ->{ + setAttribute(player,"/save:lyreConcertPlayed",true) + removeAttribute(player, "/save:LyreEnchanted") + if (removeItem(player,lyre)) { + addItem(player, Items.ENCHANTED_LYRE_3690) + } + player.unlock() + return@queueScript stopExecuting(player) + } + } + return@queueScript delayScript(player, 1) + } + } + + class BranchFletchingPulse(val player: Player) : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> player.animator?.animate(Animation(1248)).also { player.lock() } + 3 -> { + if(removeItem(player,Items.BRANCH_3692)) + addItem(player,Items.UNSTRUNG_LYRE_3688) + player.unlock() + return true + } + } + return false + } + } + + class TelePulse(val player: Player) : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> { + player.lock() + player.animator.animate(Animation(9600, Animator.Priority.VERY_HIGH), Graphics(1682)) + } + 6 -> player.properties.teleportLocation = Location(2661, 3646, 0) + 7 -> player.unlock().also { return true } + } + return false + } + } + + class KoscheiPulse(val player: Player) : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when(counter++) { + 0 -> sendMessage(player,"Explore this battleground and find your foe...") + 20 -> { + if (player.getExtension(KoscheiSession::class.java) != null) + return true + KoscheiSession.create(player).start().also { return true } + } + } + return false + } + } +} 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 new file mode 100644 index 000000000..51159d5d6 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThoraDialogue.kt @@ -0,0 +1,130 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +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.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +@Initializable +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(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(FacialExpression.HAPPY,"Hi! Can I please have one of your legendary cocktails now?") + stage = 25 + } + else if(player?.getAttribute("sigmundreturning",false) == true){ + 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(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(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(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(FacialExpression.HAPPY,"Hello there.") + stage = 60 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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(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(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(FacialExpression.NEUTRAL,"If it's not the note from Askeladden it isn't me, I'm afraid.").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(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(FacialExpression.HAPPY,"Yes please").also { + npc.openShop(player) + stage = 1000 + } + 2 -> playerl(FacialExpression.HAPPY,"No thanks.").also { stage++ } + } + 54 -> npcl(FacialExpression.HAPPY,"Okay then.").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?): DialoguePlugin { + return ThoraDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1300) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..e64080988 --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/ThorvaldDialogue.kt @@ -0,0 +1,209 @@ +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(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(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 + } + else if(player?.getAttribute("sigmundreturning",false) == true){ + playerl(core.game.dialogue.FacialExpression.ASKING,"Is this item for you?") + stage = 214 + return true + } + else if(player?.getAttribute("sigmund-steps", 0) == 11){ + playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find the token to allow your seat at the champions table?") + stage = 211 + 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?") + stage = 200 + return true + } + else if (args.size > 1) { + npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Hahaha! Well fought outerlander! Now come down from there, you have passed my trial with flying colours!") + stage = 150 + return true + } + else if (player?.questRepository?.isComplete(Quests.THE_FREMENNIK_TRIALS)!!) { + playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Howdy Thorvald!") + stage = 0 + return true + } + 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(Quests.THE_FREMENNIK_TRIALS)){ + playerl(core.game.dialogue.FacialExpression.HAPPY,"Howdy Thorvald!") + stage = 250 + return true + } + 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 (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 + return true + } else { + playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello!") + stage = 60 + return true + } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + //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 + 60 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hello yourself, outerlander. What brings you to dare speak to a mighty Fremennik warrior such as myself?").also { stage++ } + 61 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Erm... are you a member of the council?").also { stage++ } + 62 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "The Fremennik council of elders? Why, of course I am. I am recognised as one the clans mightiest warriors.").also { stage++ } + 63 -> npcl(core.game.dialogue.FacialExpression.HALF_ASKING, "What is it to you outerlander?").also { stage++ } + 64 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Well... I was wondering if you could vote for me to become a Fremennik.").also { stage++ } + 65 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "An outerlander wishes to become a Fremennik!?!? Ha! That is priceless!").also { stage++ } + 66 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, let us say that I am not totally against this concept. As a warrior, I appreciate the value of brave and powerful warriors to our clan, and even though you may be an outerlander,").also { stage++ } + 67 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I will not hold this against you if you can prove yourself to be fierce of heart in a combat situation to me.").also { stage++ } + 68 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "So how can I prove that? You want to fight me? Come on then, bring it on! Right here, right now, buddy!").also { stage++ } + 69 -> npcl(core.game.dialogue.FacialExpression.LOUDLY_LAUGHING, "Hahahahaha! You certainly show some spirit for an outerlander!").also { stage++ } + 70 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "But spirit does not always make a good warrior. It takes both skill and spirit to be so. I have a test that I give all Fremenniks on their path to be a member of the clan.").also { stage++ } + 71 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "My test will challenge both your combat prowess and your bravery equally. Should you pass it you will earn my vote at the council, and more importantly my respect for you as a warrior.").also { stage++ } + 72 -> npcl(core.game.dialogue.FacialExpression.HALF_ASKING, "So what say you, outerlander? Are you prepared for the battle?").also { stage++ } + 73 -> options("Yes", "No").also { stage++ } + 74 -> when(buttonId){ + 1 -> playerl(core.game.dialogue.FacialExpression.EVIL_LAUGH, "Am I prepared? I'll show you what combat's all about, you big sissy barbarian type guy!").also { player.setAttribute("/save:fremtrials:warrior-accepted",true); stage = 80 } + 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "No thanks, I'm pretty sure that I can find someone else to vote for me.").also { stage = 90 } + } + //Yes + 80 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Hahahahaha! I'm beginning to like you already, outerlander!").also { stage++ } + 81 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Then allow me to present you with my challenge; This ladder here will take you to a place of combat. I have placed a special warrior down there to challenge you.").also { stage++ } + 82 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Battle him to the death, and you will pass my challenge. If at any point you wish to leave combat, simply climb back up the ladder, to leave that place. If you leave you will of course fail the test.").also { stage++ } + 83 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "You may retry my test in the future if you fail, but you must stay down there until the death if you wish for my vote at the council. You must defeat him three times to prove that you are worthy.").also { stage++ } + 84 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "The fourth time that you fight him will be to the death, so do not show cowardice.").also { stage++ } + 85 -> playerl(core.game.dialogue.FacialExpression.EVIL_LAUGH, "Is that all? It will be easy!").also { stage++ } + 86 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "No, there is one more important rule; You may not enter the battleground with any armour or weaponry of any kind.").also { stage++ } + 87 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "If you need to place your equipment into your bank account, I recommend that you speak to the seer, who knows a spell that will do that for you.").also { stage = 1000 } + + //No thanks + 90 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hmm, not so brave after all, outerlander? Perhaps it is for the best. I doubt you have what it takes to pass my challenge.").also { stage = 1000 } + + //Warrior Trial Accepted Not Completed + 100 -> when(buttonId){ + 1 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "So what do I have to do to earn your vote at the council again?").also { stage = 110 } + 2 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "So, who is my opponent?").also { stage = 120 } + 3 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "I don't really like fighting that much... Isn't there something else I can do to earn your vote at the council of elders?").also { stage = 130 } + } + //What do I have to do again? + 110 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I will not offer my vote to anybody whose bravery in combat I do not trust completely. You must go down that ladder and fight your foe to the death.").also { stage = 1000 } + + //Who is my opponent? + 120 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Ah, a wise question before entering combat. His name is Koschei the deathless. He is something of a mystery, even to us.").also { stage++ } + 121 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "On one of our regular raiding parties, our longship discovered a man in the frozen waters far north-east of here. We took him aboard our ship, thinking he must be dead.").also { stage++ } + 122 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "To our amazement he was perfectly healthy, even though he must have been in those deadly icey waters for many weeks.").also { stage++ } + 123 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "He has no memory of who he is, or how he came to be there, except for his own name: Koschei. We named him 'The Deathless' because he is seemingly unkillable!").also { stage++ } + 124 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Any combat technique used against him, he learns instantly! He also apparently can heal himself from any wound at will!").also { stage++ } + 125 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "When he attacks, his weapon moves like a whirlwind! He can hide his combat level from his opponents at will as well!").also { stage++ } + 126 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "He is truly a horribly fierce opponent to face! I am only glad that he has chosen to stay here with us!").also { stage++ } + 127 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "The daylight makes him feel weak, so we have built him his own battleground beneath this building, where he can train his fiercesome skills without being disturbed.").also { stage++ } + 128 -> playerl(core.game.dialogue.FacialExpression.LAUGH, "But he can't REALLY be unkillable...").also { stage++ } + 129 -> playerl(core.game.dialogue.FacialExpression.SUSPICIOUS, "...can he?").also { stage++ } + 130 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Some say he cannot die, for he has hidden his heart outside of his body to be kept forever safe in a duck egg.").also { stage++ } + 131 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Others say he has been cursed by the gods to wander this land forever, never knowing any peace in his life, but only combat.").also { stage++ } + 132 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Some claim that the sword he carries is the source of all his power, and if he should lose it, then exactly one minute later, he will turn back into his true form; A weakened, lame, old man.").also { stage++ } + 133 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "And what do you believe?").also { stage++ } + 134 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I believe you shouldn't look a gift horse in the mouth. He is a fearfully powerful warrior, but more importantly; He is on OUR side, not against us.").also { stage++ } + 135 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "He is content testing the battle skills of anyone taking their Fremennik trials of manhood, and I am content knowing that should an enemy ever invade").also { stage++ } + 136 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "our town, while our warriors are out on a raiding party, Koschei will be able to hold off ANY invader long enough for us to make our return.").also { stage = 1000} + + //Can't I do something else? + 140 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes of course outerlander! If you bring me five raw sardines then I will vote for you instead!").also { stage++ } + 141 -> playerl(core.game.dialogue.FacialExpression.EXTREMELY_SHOCKED, "REALLY?!?!?").also { stage++ } + 142 -> npcl(core.game.dialogue.FacialExpression.LOUDLY_LAUGHING, "HAHAHAHAHAHA! No, of course not! You are stupid, even by outerlander standards!").also { stage++ } + 143 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "If you want my vote, you must pass my trial. It is as simple as that.").also { stage = 1000 } + + //Warrior Trial Completed - after the fight + 150 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "But... I don't understand... I did not manage to beat Koschei...").also { stage++ } + 151 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I did not say you had to, outerlander! All I asked was that you fought to the death! And you did! The death was your own!").also { stage++ } + 152 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I was not interested in how strong you are! I was interested in how BRAVE you are!").also { stage++ } + 153 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "You fought a superior enemy to your very last breath - THAT is bravery.").also { stage++ } + 154 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I would be honoured to represent you to the council as worthy of being a Fremennik after watching that superb battle!").also { stage = 1000 } + + //Warrior Trial Completed + 160 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Absolutely! I watched the entire battle, and was extremely impressed with your bravery in combat!").also { stage = 1000 } + + //Sigmund stuff + 200 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Know you not who I am outerlander? There are none more brave or powerful than me amongst all the Fremennik!").also { stage++ } + 201 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"The role of bodyguard is below me, as a noble warrior. You might as well ask me to babysit the children!").also { stage++ } + 202 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Is there no way you would do this for me?").also { stage++ } + 203 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"There is but one way outerlander. Since I was steeled in battle, I have dreamt of earning my place at the Champions Table in the Long Hall.").also { stage++ } + 204 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"It is a tradition amongst us that the bravest and strongest are honoured with a table of champions to drink and feast all that they can in our Long Hall.").also { stage++ } + 205 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Unfortunately, there are only a fixed number of places available at the table, and these places were all filled many moons ago by others.").also { stage++ } + 206 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Although my worthiness is undeniable, the only way I may take my place is if one of those already there die, or give up their place to me voluntarily.").also { stage++ } + 207 -> playerl(core.game.dialogue.FacialExpression.THINKING,"So you want me to go kill one of them off for you? Make it look like an accident?").also { stage++ } + 208 -> npcl(core.game.dialogue.FacialExpression.EXTREMELY_SHOCKED,"WHAT? No, no, not at all! I am shocked you would suggest such a thing!").also { stage++ } + 209 -> npcl(core.game.dialogue.FacialExpression.THINKING,"If you can persuade one of the Revellers to give up their Champions' Token to you so that I might take their place, you may have my contract as a bodyguard.").also { stage++ } + 210 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Okay, I'll see what I can do.").also { + player?.incrementAttribute("sigmund-steps",1) + stage = 1000 + } + 211 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You will need to persuade one of the revellers in the Long Hall to give up their token, and their place, in deference to my own worthiness somehow.").also { stage = 1000 } + + 214 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I'm afraid not, outerlander.").also { stage = 1000 } + + 215 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"Oh you would, would you outerlander? I have already told you, I will not demean myself with such a baby sitting job until I can sit in the Longhall with pride.").also { stage++ } + 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 { + 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 } + + 220 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You misunderstand, outerlander. Normally I will only battle for a noble cause, but have never been recognised as a true champion here.").also { stage++ } + 221 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"With this Champion's token, I can stand alongside my warrior brethren in the Long Hall, and revel in the glories of past victories together!").also { stage = 1000 } + + 250 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"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 } + + + 1000 -> end() + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return ThorvaldDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.THORVALD_THE_WARRIOR_1289) + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..3114af8ab --- /dev/null +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/YrsaDialogue.kt @@ -0,0 +1,107 @@ +package content.region.fremennik.rellekka.quest.thefremenniktrials + +import core.api.addItem +import core.api.removeItem +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 content.data.Quests +import org.rs09.consts.Items + +@Initializable +class YrsaDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if(player?.inventory?.contains(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(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(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(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(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(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(FacialExpression.HAPPY,"Hello!") + stage = 35 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(FacialExpression.NEUTRAL,"Yes I do outerlander. Only the Chieftain may permit such a thing. Talk to him.").also { stage = 1000 } + + 15 -> npcl(FacialExpression.HAPPY,"Certainly! Let me have a look at what he has written here, just give me a moment...").also { + if (removeItem(player, Items.FISCAL_STATEMENT_3708) && addItem(player, Items.STURDY_BOOTS_3700)) { + stage++ + } + } + 16 -> npcl(FacialExpression.HAPPY,"Yes, that all appears in order. Tell Olaf to come to me next time for shoes!").also { stage = 1000 } + + 20 -> npcl(FacialExpression.HAPPY,"Maybe if you pass your trial and become a full fledged member of the Fremennik...").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(FacialExpression.HAPPY,"I'd like to buy some clothes").also { + npc.openShop(player) + stage = 1000 + } + 2 -> playerl(FacialExpression.HAPPY,"Nothing, thanks.").also { stage++ } + } + 32 -> npcl(FacialExpression.HAPPY,"As you wish.").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?): DialoguePlugin { + return YrsaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(1301) + } + + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/zone/wbisland/DagannothKingNPC.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java similarity index 96% rename from Server/src/main/java/core/game/content/zone/wbisland/DagannothKingNPC.java rename to Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java index 36dabfe40..65f5d4e43 100644 --- a/Server/src/main/java/core/game/content/zone/wbisland/DagannothKingNPC.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java @@ -1,8 +1,8 @@ -package core.game.content.zone.wbisland; +package content.region.fremennik.waterbirth.handlers; import java.util.List; -import core.game.content.global.BossKillCounter; +import content.data.BossKillCounter; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -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/java/core/game/content/zone/wbisland/SpinolypNPC.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java similarity index 84% rename from Server/src/main/java/core/game/content/zone/wbisland/SpinolypNPC.java rename to Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java index e374379c4..e887f5263 100644 --- a/Server/src/main/java/core/game/content/zone/wbisland/SpinolypNPC.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java @@ -1,18 +1,19 @@ -package core.game.content.zone.wbisland; +package content.region.fremennik.waterbirth.handlers; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; 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 rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MultiSwingHandler; + +import static core.api.ContentAPIKt.*; /** * Represents a spinolyp npc. @@ -122,6 +123,12 @@ public final class SpinolypNPC extends AbstractNPC { return 12; } + @Override + public int calculateDefence(Entity v, Entity e) { + // Spinolyps' attack always targets ranged defence + return CombatStyle.RANGE.getSwingHandler().calculateDefence(v, e); + } + @Override public void visualize(Entity entity, Entity victim, BattleState state) { super.visualize(entity, victim, state); @@ -137,11 +144,11 @@ public final class SpinolypNPC extends AbstractNPC { @Override public void impact(Entity entity, Entity victim, BattleState state) { super.impact(entity, victim, state); - if (super.getType() == CombatStyle.MAGIC) { + 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/java/core/game/content/zone/wbisland/WaterBirthDungeonZone.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/WaterBirthDungeonZone.java similarity index 88% rename from Server/src/main/java/core/game/content/zone/wbisland/WaterBirthDungeonZone.java rename to Server/src/main/content/region/fremennik/waterbirth/handlers/WaterBirthDungeonZone.java index eeea49007..1a5a35235 100644 --- a/Server/src/main/java/core/game/content/zone/wbisland/WaterBirthDungeonZone.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/WaterBirthDungeonZone.java @@ -1,12 +1,12 @@ -package core.game.content.zone.wbisland; +package content.region.fremennik.waterbirth.handlers; import java.util.ArrayList; import java.util.List; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueAction; -import core.game.content.global.action.ClimbActionHandler; +import core.game.dialogue.DialogueAction; +import core.game.global.action.ClimbActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.Option; import core.game.interaction.OptionHandler; @@ -19,18 +19,19 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; 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.ZoneRestriction; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import org.rs09.consts.Items; /** * Handles the waterbirth dungeon zone. @@ -48,11 +49,11 @@ public final class WaterBirthDungeonZone extends MapZone implements Plugin 0) { + return true; + } + if (RegionManager.getRegionPlane(location).getItem(Items.PET_ROCK_3695,location,player) != null) { + return true; + } + return false; + } @Override public boolean handle(Player player, Node node, String option) { @@ -245,7 +260,7 @@ public final class WaterBirthDungeonZone extends MapZone implements Plugin= 2492; if (!behind) { - if (RegionManager.getLocalPlayers(node.getLocation().transform(-1, 0, 0), 0).size() == 0 || RegionManager.getLocalPlayers(node.getLocation().getLocation().transform(-1, 2, 0), 0).size() == 0) { + if (!pressurePadActivated(player,node.getLocation().transform(-1, 0, 0)) || !pressurePadActivated(player,node.getLocation().transform(-1, 2, 0))) { player.sendMessage("You cannot see a way to open this door..."); break; } @@ -389,14 +404,16 @@ public final class WaterBirthDungeonZone extends MapZone implements Plugin 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/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/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 new file mode 100644 index 000000000..118e67f4b --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/ChadwellDialogue.kt @@ -0,0 +1,47 @@ +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.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +/** + * @author qmqz + */ + +@Initializable +class ChadwellDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Good day. What can I get you?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("Let's see what you've got.", "Nothing thanks.").also { stage++ } + 1 -> when (buttonId) { + 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(FacialExpression.FRIENDLY, "Okay then.").also { stage = 99 } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ChadwellDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CHADWELL_971) + } +} 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.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 new file mode 100644 index 000000000..4aa655a47 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/MonkeyChildThingZooDialogue.kt @@ -0,0 +1,71 @@ +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.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * @author qmqz + */ + +@Initializable +class MonkeyChildThingZooDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"").also { stage = 0 } + if (!player.equipment.contains(Items.MSPEAK_AMULET_4021, 1)) { + npc(FacialExpression.OLD_LAUGH1, "Eeekeek ookeek!").also { stage = 99 } + } else { + var a = 1..5 + when(a.random()) { + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + + 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(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(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(FacialExpression.ASKING, "What?").also { stage = 99 } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return MonkeyChildThingZooDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MONKEY_4363) + } +} 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 new file mode 100644 index 000000000..616a942cc --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkMerchantPlugin.java @@ -0,0 +1,220 @@ +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.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.game.system.task.Pulse; +import core.game.world.GameWorld; +import core.plugin.Initializable; +import core.game.world.map.RegionManager; + +/** + * Handles the SilkMerchantPlugin dialogue. + * @author 'Vexia + */ +@Initializable +public class SilkMerchantPlugin extends DialoguePlugin { + + /** + * Represents the silk item. + */ + private final Item SILK = new Item(950); + + /** + * Represents the noted silk item. + */ + private final Item NOTED_SILK = new Item(951); + + public SilkMerchantPlugin() { + + } + + public SilkMerchantPlugin(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 574 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + if (player.getInventory().containsItem(SILK)) { + 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, FacialExpression.ASKING, "I've got some silk here. Will you buy it?"); + stage = 100; + } else { + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Sorry, I don't have any silk."); + stage = 200; + } + break; + case 200: + end(); + break; + case 201: + end(); + break; + case 1: + 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: + interpreter.sendOptions("Select an Option", "20 coins.", "30 coins.", "120 coins.", "200 coins."); + stage = 3; + break; + case 3: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "20 coins."); + stage = 1000; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "30 coins."); + stage = 2000; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "120 coins."); + stage = 300; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "200 coins."); + stage = 400; + break; + } + break; + case 1000: + case 2000: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "That price is fine by me! Hand over your silk."); + stage = stage == 1000 ? 1010 : 1011; + break; + case 1010: + buy(20); + break; + case 1011: + buy(30); + break; + case 300: + case 400: + 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. + interpreter.sendOptions("Select an Option", "Ok, I guess 50 will do.", "I'll give it to you for 60.", "No, that is not enough."); + stage = 501; + break; + case 501: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Ok, I guess 50 will do."); + stage = 510; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "I'll give it to you for 60."); + stage = 520; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, that is not enough."); + stage = 530; + break; + } + break; + case 510: + buy(50); + break; + case 520: + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "You drive a hard bargain, but", "I guess that will have to do."); + stage = 521; + break; + case 521: + buy(60); + break; + case 530: + end(); + break; + case 100: + interpreter.sendDialogues(npc, null, "Silk? You're not carrying any silk."); + stage = 101; + break; + case 101: + interpreter.sendDialogues(player, null, "Yes I am."); + stage = 102; + break; + case 102: + interpreter.sendDialogues(npc, null, "No, you're carrying " + player.getInventory().getAmount(NOTED_SILK) + " bits of paper that say they", "can be exchanged for silk at a bank or general store.", "I'm not buying those. Fetch me some real silk, then", "we'll trade."); + stage = 103; + break; + case 103: + interpreter.sendDialogues(player, null, "Oh, alright."); + stage = 104; + break; + case 104: + end(); + break; + } + return true; + } + + /** + * Method used to buy silk. + * @param price + */ + public final void buy(int price) { + end(); + int amt = player.getInventory().getAmount(SILK); + Item remove = new Item(SILK.getId(), amt); + if (!player.getInventory().containsItem(remove)) { + return; + } + if (player.getInventory().remove(remove)) { + player.getInventory().add(new Item(995, price * amt)); + } + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new SilkMerchantPlugin(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + if (player.getSavedData().getGlobalData().getSilkSteal() > System.currentTimeMillis()) { + end(); + for (NPC npc : RegionManager.getLocalNpcs(player.getLocation(), 8)) { + if (!npc.getProperties().getCombatPulse().isAttacking() && npc.getId() == 32) { + npc.sendChat("Hey! Get your hands off there!"); + npc.getProperties().getCombatPulse().attack(player); + break; + } + } + GameWorld.getPulser().submit(new Pulse(1) { + int count = 0; + + @Override + public boolean pulse() { + if (count == 0) + npc.sendChat("You're the one who stole something from me!"); + if (count == 2) { + npc.sendChat("Guards guards!"); + return true; + } + count++; + return false; + } + + }); + return false; + } + 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/java/core/game/content/dialogue/SilkTradeDialogue.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkTradeDialogue.java similarity index 83% rename from Server/src/main/java/core/game/content/dialogue/SilkTradeDialogue.java rename to Server/src/main/content/region/kandarin/ardougne/dialogue/SilkTradeDialogue.java index dfa0f3af1..dc2a5b1ca 100644 --- a/Server/src/main/java/core/game/content/dialogue/SilkTradeDialogue.java +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkTradeDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; import core.plugin.Initializable; @@ -29,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: @@ -47,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: @@ -72,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: @@ -82,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: @@ -138,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 new file mode 100644 index 000000000..045a51c3f --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilverMechantPlugin.java @@ -0,0 +1,68 @@ +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; + +/** + * Handles the SilverMechantPlugin dialogue. + * @author 'Vexia + */ +@Initializable +public class SilverMechantPlugin extends DialoguePlugin { + + public SilverMechantPlugin() { + + } + + public SilverMechantPlugin(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 569 }; + } + + @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.NEUTRAL, "No, thank you."); + stage = 20; + break; + } + break; + case 20: + end(); + break; + } + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new SilverMechantPlugin(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + 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/java/core/game/content/dialogue/SpiceSellerPlugin.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/SpiceSellerPlugin.java similarity index 81% rename from Server/src/main/java/core/game/content/dialogue/SpiceSellerPlugin.java rename to Server/src/main/content/region/kandarin/ardougne/dialogue/SpiceSellerPlugin.java index 8fdb90097..593d0ac04 100644 --- a/Server/src/main/java/core/game/content/dialogue/SpiceSellerPlugin.java +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/SpiceSellerPlugin.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -60,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 new file mode 100644 index 000000000..c4ff1a89e --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/ZookeeperDialogue.kt @@ -0,0 +1,82 @@ +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.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +/** + * @author qmqz + * Does not include treasure trails dialogue + */ + +@Initializable +class ZookeeperDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hi!").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + 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(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(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(FacialExpression.FRIENDLY, "Ooh, I will!").also { stage = 99 } + } + + 20 -> { + 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(FacialExpression.HALF_ASKING, "Where on Gielinor did you get that scary", + "looking Cyclops?!").also { stage++ } + } + + 22 -> { + npc(FacialExpression.LAUGH, "Yes he is scary looking!").also { stage++ } + } + + 23 -> { + 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 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ZookeeperDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ZOO_KEEPER_28) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/object/WildernessLeverPlugin.java b/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java similarity index 87% rename from Server/src/main/java/core/game/interaction/object/WildernessLeverPlugin.java rename to Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java index bb7330025..117dbf5da 100644 --- a/Server/src/main/java/core/game/interaction/object/WildernessLeverPlugin.java +++ b/Server/src/main/content/region/kandarin/ardougne/handlers/WildernessLeverPlugin.java @@ -1,26 +1,30 @@ -package core.game.interaction.object; +package content.region.kandarin.ardougne.handlers; +import core.ServerConstants; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +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.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; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +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 @@ -38,7 +42,7 @@ public final class WildernessLeverPlugin extends OptionHandler { SceneryDefinition.forId(id).getHandlers().put("option:pull", this); } } - PluginManager.definePlugin(new LeverDialogue()); + ClassScanner.definePlugin(new LeverDialogue()); return this; } @@ -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 new file mode 100644 index 000000000..64d4a7fa9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherCedricNPC.kt @@ -0,0 +1,122 @@ +package content.region.kandarin.ardougne.quest.monksfriend + +import core.api.getQuestStage +import core.api.sendItemDialogue +import core.api.setQuestStage +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.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.tools.END_DIALOGUE +import content.data.Quests + +/** + * Handles BrotherCedricDialogue Dialogue + * @author Kya + */ +class BrotherCedricDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.MONKS_FRIEND) + when { + questStage < 30 -> { + when(stage) { + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Hello.").also{stage++} + 1 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "Honey, money, woman and wine!").also{stage++} + 2 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Are you ok?").also{stage++ } + 3 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "Yesshh...hic up...beautiful!").also{stage++} + 4 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Take care old monk.").also{ stage++ } + 5 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "La..di..da..hic..up!").also{stage = END_DIALOGUE } + } + } + questStage == 30 -> { + when(stage){ + 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!!, Quests.MONKS_FRIEND, 40) } + } + } + questStage == 40 -> { + when(stage){ + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Are you okay?").also{stage++} + 1 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "Hic up! Oh my head! I need a jug of water.").also{stage++} + 2 -> if (player!!.inventory.containItems(Items.JUG_OF_WATER_1937)){ + playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Cedric! Here, drink! I have some water.").also{stage=10} + } else { + playerl(core.game.dialogue.FacialExpression.NEUTRAL, "I'll see if I can get some.").also{stage = 3} + } + 3 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "Thanks! *hic*").also{stage = END_DIALOGUE } + 10 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "Good stuff, my head's spinning!").also {stage++} + 11 -> { + 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!!, Quests.MONKS_FRIEND, 41) + } + } + } + questStage == 41 -> { + when(stage){ + 0 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Aah! That's better!").also{stage++} + 1 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Now I just need to fix this cart and we can go party.").also {stage++} + 2 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Could you help?").also{stage++} + 3 -> options( + "No, I've helped enough monks today!", + "Yes, I'd be happy to!").also{stage++} + 4 -> when(buttonID){ + 1 -> playerl(core.game.dialogue.FacialExpression.ANGRY, "No, I've helped enough monks today!").also{stage++} + 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, I'd be happy to!").also{stage=10} + } + 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!!, Quests.MONKS_FRIEND, 42)} + } + } + questStage == 42 -> { + when(stage){ + 0 -> npcl(core.game.dialogue.FacialExpression.HALF_ASKING, "Did you manage to get some wood?").also{stage++} + 1 -> if (player!!.inventory.containItems(Items.LOGS_1511)){ + sendItemDialogue(player!!, Items.LOGS_1511, "You hand Cedric some logs.") + stage=2 + } else { + playerl(core.game.dialogue.FacialExpression.SAD, "Not yet, I'm afraid.").also{stage = END_DIALOGUE } + } + 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Here you go!").also{stage++} + 3 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Well done! Now I'll fix this cart. You head back to Brother Omad and tell him I'll be there soon.").also{stage++} + 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!!, Quests.MONKS_FRIEND, 50) + } + } + } + questStage == 50 -> { + when(stage){ + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Hello Cedric.").also{stage++} + 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hi, I'm almost done here. Could you tell Omad that I'll be back soon?").also{stage = END_DIALOGUE } + } + } + questStage == 100 -> { + when(stage){ + 0 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Brother Omad sends you his thanks! He won't be in a fit state to thank you in person.").also{stage = END_DIALOGUE } + } + } + } + } +} + +/** + * Handles BrotherCedricListener to launch the dialogue + * @author Kya + */ +class BrotherCedricListener : InteractionListener { + override fun defineListeners() { + on(NPCs.BROTHER_CEDRIC_280, IntType.NPC, "talk-to"){ player, _ -> + player.dialogueInterpreter.open(BrotherCedricDialogue(), NPC(NPCs.BROTHER_CEDRIC_280)) + return@on true + } + } +} 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 new file mode 100644 index 000000000..bea3883cc --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/BrotherOmadNPC.kt @@ -0,0 +1,221 @@ +package content.region.kandarin.ardougne.quest.monksfriend + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.node.item.Item +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 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.game.world.GameWorld.Pulser +import core.tools.END_DIALOGUE +import content.data.Quests + + +/** +* Handles BrotherOmadDialogue Dialogue +* @author Kya +*/ +class BrotherOmadDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.MONKS_FRIEND) + when (questStage) { + 0 -> { + when(stage) { + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Hello there. What's wrong?").also { stage++ } + 1 -> npcl(core.game.dialogue.FacialExpression.SAD, "*yawn*...oh, hello...*yawn* I'm sorry! I'm just so tired! I haven't slept in a week!").also { stage++ } + 2 -> options( + "Why can't you sleep, what's wrong?", + "Sorry! I'm too busy to hear your problems!").also { stage++ } + 3 -> when (buttonID) { + 1 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Why can't you sleep, what's wrong?").also { stage++ } + 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Sorry! I'm too busy to hear your problems!").also { stage = END_DIALOGUE } + } + 4 -> npcl(core.game.dialogue.FacialExpression.SAD, "It's brother Androe's son! With his constant: Waaaaaah! Waaaaaaaaah! Androe said it's natural, but it's so annoying!").also { stage++ } + + 5 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "I suppose that's what kids do.").also { stage++ } + 6 -> npcl(core.game.dialogue.FacialExpression.SAD, "He was fine, up until last week! Thieves broke in! They stole his favourite sleeping blanket!").also { stage++ } + 7 -> npcl(core.game.dialogue.FacialExpression.SAD, "Now he won't rest until it's returned... ...and that, means neither can I!").also { stage++ } + 8 -> options( + "Can I help at all?", + "I'm sorry to hear that! I hope you find his blanket.").also { stage++ } + 9 -> when (buttonID) { + 1 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Can I help at all?").also { stage++ } + 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm sorry to hear that! I hope you find his blanket.").also { stage = END_DIALOGUE } + } + 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(Quests.MONKS_FRIEND).start(player) }.also { player!!.questRepository.syncronizeTab(player) } + } + } + 10 -> { + when(stage) { + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Hello.").also{stage = 20 } + 20 -> npcl(core.game.dialogue.FacialExpression.SAD, "*yawn*...oh, hello again...*yawn*").also{stage++ } + 21 -> npcl(core.game.dialogue.FacialExpression.SAD, "Please tell me you have the blanket.").also{stage++} + 22 -> if(player!!.inventory.containItems(Items.CHILDS_BLANKET_90)){ + playerl(core.game.dialogue.FacialExpression.HAPPY, "Yes! I've recovered it from the clutches of the evil thieves!").also{stage = 30} + } else { + playerl(core.game.dialogue.FacialExpression.SAD, "I'm afraid not.").also{stage++} + } + 23 -> npcl(core.game.dialogue.FacialExpression.SAD, "I need some sleep!").also{stage = END_DIALOGUE } + 30 -> { + sendItemDialogue(player!!, Items.CHILDS_BLANKET_90, "You hand the monk the childs blanket.") + stage=31 + } + 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!!, Quests.MONKS_FRIEND, 20); stage = END_DIALOGUE + } + } + } + 20 -> { + when(stage) { + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Hello, how are you?").also{stage = 30 } + 30 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Much better now I'm sleeping well! Now I can organise the party.").also{stage++} + 31 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Ooh! What party?").also{stage++} + 32 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "The son of Brother Androe's birthday party. He's going to be one year old!").also {stage++} + 33 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "That's sweet!").also{stage++ } + 34 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "It's also a great excuse for a drink!").also{stage++} + 35 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "We just need Brother Cedric to return with the wine.").also{stage++} + 36 -> options( + "Who's Brother Cedric?", + "Enjoy it! I'll see you soon!").also { stage++} + 37 -> when(buttonID){ + 1 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Who's Brother Cedric?").also{stage++} + 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Enjoy it! I'll see you soon!").also{stage = 998} + } + 998 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Take care traveller.").also{stage = END_DIALOGUE } + 38 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Cedric is a member of the order too. We sent him out three days ago to collect wine. But he didn't return!").also{stage++} + 39 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "He most probably got drunk and lost in the forest!").also{stage++} + 40 -> options( + "I've no time for that, sorry.", + "Where should I look?", + "Can I come to the party?").also { stage++} + 41 -> when(buttonID){ + 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "I've no time for that, sorry.").also {stage = 996} + 2 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Where should I look?").also {stage++} + 3 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Can I come to the party?").also{stage = 997} + } + 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!!, + Quests.MONKS_FRIEND, + 30 + )} + } + } + 30 -> { + when(stage) { + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Hello brother Omad.").also{stage = 50} + 50 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hello adventurer, have you found Brother Cedric?").also{stage++} + 51 -> playerl(core.game.dialogue.FacialExpression.SAD, "Not yet.").also{stage++} + 52 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, keep looking, we need that wine!").also{stage = END_DIALOGUE } + } + } + 40 -> { + when(stage) { + 0 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Hello adventurer, have you found Brother Cedric?").also{stage = 60} + 60 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes I've seen him, he's a bit drunk!").also{stage++} + 61 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, try your best to get him back here!").also{stage = END_DIALOGUE } + } + } + 41, 42 -> { + when(stage) { + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Hello again brother Omad.").also{stage = 70} + 70 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hello adventurer, where's Brother Cedric?").also{stage++} + 71 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "He's having a bit of trouble with his cart.").also{stage++} + 72 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hmmm! Maybe you could help?").also{stage = END_DIALOGUE } + } + } + 50 -> { + when(stage) { + 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Hi Omad, Brother Cedric is on his way!").also{stage = 80} + 80 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Good! Good! Now we can party!").also{stage++} + 81 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "I have little to repay you with, but I'd like to offer you some rune stones. But first, let's party!").also{stage++} + 82 -> sendDialogue(player!!, "Brother Omad gives you 8 Law Runes").also{stage++} + 83 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "Thanks Brother Omad!").also{stage++} + 84 -> { + commenceMonkParty(true) + end() + } + } + } + 100 -> { + commenceMonkParty(false) + } + } + } + + private fun commenceMonkParty(questComplete : Boolean) { + val brotherOmad: NPC? = RegionManager.getNpc(Location(2604, 3209, 0), 279, 6) + val monk: NPC? = RegionManager.getNpc(Location(2609, 3207, 0), 281, 6) + + // Spawn balloons when PartyRoom code is fixed. + + Pulser.submit(object : Pulse(1) { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 1 -> { + brotherOmad!!.sendChat("Party!") + brotherOmad.animator.animate(Animation(866)) // Dance + + } + 3 -> { + player!!.sendChat("Woop!") + player!!.animator.animate(Animation(866)) // Dance + } + 5 -> { + monk!!.sendChat("Yeah!") + monk.animator.animate(Animation(2106)) // Jig + } + 7 -> brotherOmad!!.sendChat("Let's boogie!") + 9 -> player!!.sendChat("Oh, baby!") + 11 -> monk!!.sendChat("GO!") + 13 -> { + brotherOmad!!.sendChat("Get down!") + brotherOmad.animator.animate(Animation(2108)) // Headbang + } + 15 -> player!!.sendChat("Feel the rhythm!") + 17 -> monk!!.sendChat("Dance!") + 19 -> brotherOmad!!.sendChat("Oh my!") + 21 -> { + player!!.sendChat("Watch me go!") + player!!.animator.animate(Animation(861)) // Laugh + } + 23 -> { + monk!!.sendChat("You go!") + monk.animator.animate(Animation(2109)) // Jump for joy + } + 25 -> if (questComplete) { + player!!.questRepository.getQuest(Quests.MONKS_FRIEND).finish(player) + } + } + count++ + return false + } + }) + } +} + +/** + * Handles BrotherCedricListener to launch the dialogue + * @author Kya + */ +class BrotherOmadListener : InteractionListener { + override fun defineListeners() { + on(NPCs.BROTHER_OMAD_279, IntType.NPC, "talk-to"){ player, _ -> + player.dialogueInterpreter.open(BrotherOmadDialogue(), NPC(NPCs.BROTHER_OMAD_279)) + return@on true + } + } +} 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 new file mode 100644 index 000000000..cfe48d058 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonasteryMonkNPC.kt @@ -0,0 +1,45 @@ +package content.region.kandarin.ardougne.quest.monksfriend + +import core.game.node.entity.npc.NPC +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 +import content.data.Quests + +/** +* Handles MonasteryMonkDialogue Dialogue +* @author Kya +*/ +class MonasteryMonkDialogue : DialogueFile() { + override fun handle(interfaceId: Int, buttonId: Int) { + 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 } + } + } else if (questStage < 100) { + when(stage) { + 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY,"*yawn*").also{stage = END_DIALOGUE } + } + } else { + when(stage) { + 0 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Can't wait for the party!").also{stage = END_DIALOGUE } + } + } + } +} + +/** + * Handles BrotherCedricListener to launch the dialogue + * @author Kya + */ +class MonasteryMonkListener : InteractionListener { + override fun defineListeners() { + on(NPCs.MONK_281, IntType.NPC, "talk-to"){ player, _ -> + player.dialogueInterpreter.open(MonasteryMonkDialogue(), NPC(NPCs.MONK_281)) + return@on true + } + } +} 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 new file mode 100644 index 000000000..c559c6a85 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/monksfriend/MonksFriend.kt @@ -0,0 +1,69 @@ +package content.region.kandarin.ardougne.quest.monksfriend + + +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 content.data.Quests + +/** +* Represents the "Monk's Friend" quest. +* @author Kya +*/ + +@Initializable +class MonksFriend: Quest(Quests.MONKS_FRIEND, 89, 88, 1, 30, 0, 1, 80) { + + 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 !!Brother Omad?? in the", line++) + line(player, "!!Monastery?? south of !!Ardougne??.", line++) + line++ + } + if(stage == 10){ + line(player, "Brother Omad asked me to recover a child's blanket.", line++) + } + if(stage >= 20){ + line(player, "Brother Omad asked me to recover a child's blanket, I ", line++, true) + line(player, "found the secret cave and gave back the blanket.", line++, true) + + } + if(stage ==30){ + line(player, "I agreed to find !!Brother Cedric.?? he is somewhere in the", line++) + line(player, "!!forest?? south of !!Ardougne??", line++) + } + if(stage >= 50){ + line(player, "I found Brother Cedric in the forest south of Ardougne.", line++, true) + line(player, "I sobered him up and I helped him fix his cart.", line++, true) + } + 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++) + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.LAW_RUNE_563, 230, 277, 5) + drawReward(player,"1 Quest Point", ln++) + drawReward(player,"8 Law Runes", ln++) + drawReward(player,"2000 Woodcutting XP", ln++) + + rewardXP(player, Skills.WOODCUTTING, 2000.0) + player.inventory.add(Item(563, 8)) + } +} 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/java/core/game/content/quest/members/sheepherder/GateInteractionHandler.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/GateInteractionHandler.java similarity index 84% rename from Server/src/main/java/core/game/content/quest/members/sheepherder/GateInteractionHandler.java rename to Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/GateInteractionHandler.java index d9e718bbf..af008a0d6 100644 --- a/Server/src/main/java/core/game/content/quest/members/sheepherder/GateInteractionHandler.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/GateInteractionHandler.java @@ -1,15 +1,15 @@ -package core.game.content.quest.members.sheepherder; +package content.region.kandarin.ardougne.quest.sheepherder; import core.game.interaction.DestinationFlag; import core.game.interaction.MovementPulse; import core.game.node.Node; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; import core.plugin.Initializable; import core.plugin.Plugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.dialogue.FacialExpression; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; @Initializable @@ -40,7 +40,7 @@ public class GateInteractionHandler extends PluginInteraction { player.getDialogueInterpreter().sendDialogues(SheepHerder.FARMER_BRUMTY, FacialExpression.SUSPICIOUS, "You can't enter without your protective gear!", "Can't have you spreading the plague!"); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } return false; diff --git a/Server/src/main/java/core/game/content/quest/members/sheepherder/HalgriveDialogue.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HalgriveDialogue.java similarity index 86% rename from Server/src/main/java/core/game/content/quest/members/sheepherder/HalgriveDialogue.java rename to Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HalgriveDialogue.java index 930a9e693..4f3dee3c2 100644 --- a/Server/src/main/java/core/game/content/quest/members/sheepherder/HalgriveDialogue.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HalgriveDialogue.java @@ -1,8 +1,11 @@ -package core.game.content.quest.members.sheepherder; +package content.region.kandarin.ardougne.quest.sheepherder; import core.game.node.entity.player.Player; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/quest/members/sheepherder/HerderSheepNPC.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HerderSheepNPC.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/members/sheepherder/HerderSheepNPC.java rename to Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HerderSheepNPC.java index ddd2368ab..8576e3fcb 100644 --- a/Server/src/main/java/core/game/content/quest/members/sheepherder/HerderSheepNPC.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/HerderSheepNPC.java @@ -1,9 +1,9 @@ -package core.game.content.quest.members.sheepherder; +package content.region.kandarin.ardougne.quest.sheepherder; import core.game.interaction.MovementPulse; import core.game.node.entity.Entity; import core.game.node.entity.npc.NPC; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.path.Pathfinder; diff --git a/Server/src/main/java/core/game/content/quest/members/sheepherder/IncineratorHandler.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/IncineratorHandler.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/sheepherder/IncineratorHandler.java rename to Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/IncineratorHandler.java index acca54416..dd6a0448e 100644 --- a/Server/src/main/java/core/game/content/quest/members/sheepherder/IncineratorHandler.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/IncineratorHandler.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.sheepherder; +package content.region.kandarin.ardougne.quest.sheepherder; import core.game.interaction.DestinationFlag; import core.game.interaction.MovementPulse; @@ -9,8 +9,8 @@ import core.game.node.scenery.Scenery; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; import java.util.Objects; diff --git a/Server/src/main/java/core/game/content/quest/members/sheepherder/OrbonDialogue.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/OrbonDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/sheepherder/OrbonDialogue.java rename to Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/OrbonDialogue.java index 07a6476a9..55efedcf1 100644 --- a/Server/src/main/java/core/game/content/quest/members/sheepherder/OrbonDialogue.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/OrbonDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.quest.members.sheepherder; +package content.region.kandarin.ardougne.quest.sheepherder; import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/quest/members/sheepherder/ProdActionHandler.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/ProdActionHandler.java similarity index 90% rename from Server/src/main/java/core/game/content/quest/members/sheepherder/ProdActionHandler.java rename to Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/ProdActionHandler.java index 24e27750e..a9385fa2b 100644 --- a/Server/src/main/java/core/game/content/quest/members/sheepherder/ProdActionHandler.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/ProdActionHandler.java @@ -1,21 +1,21 @@ -package core.game.content.quest.members.sheepherder; +package content.region.kandarin.ardougne.quest.sheepherder; import core.game.interaction.DestinationFlag; import core.game.interaction.MovementPulse; import core.game.interaction.Option; 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 rs09.game.system.SystemLogger; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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 core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; @Initializable public class ProdActionHandler extends PluginInteraction { @@ -30,7 +30,7 @@ public class ProdActionHandler extends PluginInteraction { handleProd(player,n); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } return false; 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 new file mode 100644 index 000000000..75ed87dbf --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/SheepHerder.java @@ -0,0 +1,148 @@ +package content.region.kandarin.ardougne.quest.sheepherder; + +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.world.map.Direction; +import core.game.world.map.Location; +import core.plugin.Initializable; +import org.rs09.consts.Items; + +import java.util.HashMap; +import content.data.Quests; + +@Initializable +public class SheepHerder extends Quest { + public static Item CATTLE_PROD = new Item(Items.CATTLEPROD_278); + public static Item POISON = new Item(279); + public static Item PLAGUE_TOP = new Item(284); + public static Item PLAGUE_BOTTOM = new Item(285); + public static Item RED_SHEEP_BONES = new Item(Items.SHEEP_BONES_1_280); + public static Item GREEN_SHEEP_BONES = new Item(Items.SHEEP_BONES_2_281); + public static Item BLUE_SHEEP_BONES = new Item(Items.SHEEP_BONES_3_282); + public static Item YELLOW_SHEEP_BONES = new Item(Items.SHEEP_BONES_4_283); + public static int RED_SHEEP = 2345; + public static int GREEN_SHEEP = 2346; + public static int BLUE_SHEEP = 2347; + public static int YELLOW_SHEEP = 2348; + public static int FARMER_BRUMTY = 291; + + public static HashMap boneMap = new HashMap<>(); + static{ + boneMap.put(RED_SHEEP,RED_SHEEP_BONES); + boneMap.put(GREEN_SHEEP,GREEN_SHEEP_BONES); + boneMap.put(YELLOW_SHEEP,YELLOW_SHEEP_BONES); + boneMap.put(BLUE_SHEEP,BLUE_SHEEP_BONES); + } + + 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 = 12; + boolean sheepDead = player.getAttribute("sheep_herder:all_dead",false); + super.drawJournal(player, stage); + 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 { + 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 must find the first sheep and herd it to the special pen.", line++); + } + 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); + } + } + } + + @Override + public void finish(Player player) { + int ln = 10; + super.finish(player); + player.getPacketDispatch().sendItemZoomOnInterface(995, 230, 277, 5); + drawReward(player,"4 Quest Points",ln++); + drawReward(player,"3100 coins",ln++); + player.removeAttribute("sheep_herder:red_dead"); + player.removeAttribute("sheep_herder:blue_dead"); + player.removeAttribute("sheep_herder:green_dead"); + player.removeAttribute("sheep_herder:yellow_dead"); + player.removeAttribute("sheep_herder:all_dead"); + player.getInventory().add(new Item(995,3100)); + } + + @Override + public Quest newInstance(Object object) { + new HerderSheepNPC(RED_SHEEP, Location.create(2609, 3343, 0)).init(); + new HerderSheepNPC(RED_SHEEP,Location.create(2610, 3344, 0)).init(); + new HerderSheepNPC(RED_SHEEP,Location.create(2609, 3345, 0)).init(); + new HerderSheepNPC(RED_SHEEP,Location.create(2615, 3343, 0)).init(); + new HerderSheepNPC(GREEN_SHEEP,Location.create(2622, 3366, 0)).init(); + new HerderSheepNPC(GREEN_SHEEP,Location.create(2622, 3366, 0)).init(); + new HerderSheepNPC(GREEN_SHEEP,Location.create(2619, 3371, 0)).init(); + new HerderSheepNPC(GREEN_SHEEP,Location.create(2617, 3365, 0)).init(); + new HerderSheepNPC(YELLOW_SHEEP,Location.create(2610, 3390, 0)).init(); + new HerderSheepNPC(YELLOW_SHEEP,Location.create(2613, 3389, 0)).init(); + new HerderSheepNPC(YELLOW_SHEEP,Location.create(2606, 3391, 0)).init(); + new HerderSheepNPC(YELLOW_SHEEP,Location.create(2608, 3387, 0)).init(); + new HerderSheepNPC(BLUE_SHEEP,Location.create(2559, 3388, 0)).init(); + new HerderSheepNPC(BLUE_SHEEP,Location.create(2562, 3388, 0)).init(); + new HerderSheepNPC(BLUE_SHEEP,Location.create(2563, 3383, 0)).init(); + new HerderSheepNPC(BLUE_SHEEP,Location.create(2570, 3381, 0)).init(); + NPC Brumty = new NPC(FARMER_BRUMTY,Location.create(2594,3357,0)); + Brumty.setWalks(false); + Brumty.setRespawn(true); + Brumty.init(); + Brumty.setDirection(Direction.NORTH); + return this; + } +} diff --git a/Server/src/main/java/core/game/content/quest/members/sheepherder/SheepPoisonHandler.java b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/SheepPoisonHandler.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/sheepherder/SheepPoisonHandler.java rename to Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/SheepPoisonHandler.java index 147f16a92..585210c92 100644 --- a/Server/src/main/java/core/game/content/quest/members/sheepherder/SheepPoisonHandler.java +++ b/Server/src/main/content/region/kandarin/ardougne/quest/sheepherder/SheepPoisonHandler.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.sheepherder; +package content.region.kandarin.ardougne.quest.sheepherder; import core.game.interaction.DestinationFlag; import core.game.interaction.MovementPulse; @@ -6,15 +6,14 @@ import core.game.interaction.NodeUsageEvent; import core.game.node.Node; 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 rs09.game.world.GameWorld; +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.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; @Initializable public class SheepPoisonHandler extends PluginInteraction { 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/java/core/game/content/quest/miniquest/barcrawl/BarcrawlDialogue.java b/Server/src/main/content/region/kandarin/barcrawl/BarcrawlDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/miniquest/barcrawl/BarcrawlDialogue.java rename to Server/src/main/content/region/kandarin/barcrawl/BarcrawlDialogue.java index 891d7e2e8..26f913ee9 100644 --- a/Server/src/main/java/core/game/content/quest/miniquest/barcrawl/BarcrawlDialogue.java +++ b/Server/src/main/content/region/kandarin/barcrawl/BarcrawlDialogue.java @@ -1,11 +1,11 @@ -package core.game.content.quest.miniquest.barcrawl; +package content.region.kandarin.barcrawl; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; import core.plugin.Initializable; -import rs09.game.world.GameWorld; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +import core.game.world.GameWorld; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; /** * The barcrawl diaogue plugin. @@ -73,7 +73,7 @@ public final class BarcrawlDialogue extends DialoguePlugin { } type.message(player, true); player.getInventory().remove(type.getCoins()); - player.getBarcrawlManager().complete(type.ordinal()); + BarcrawlManager.getInstance(player).complete(type.ordinal()); player.lock(6); GameWorld.getPulser().submit(new Pulse(6, player) { @Override diff --git a/Server/src/main/content/region/kandarin/barcrawl/BarcrawlManager.java b/Server/src/main/content/region/kandarin/barcrawl/BarcrawlManager.java new file mode 100644 index 000000000..95f1a4150 --- /dev/null +++ b/Server/src/main/content/region/kandarin/barcrawl/BarcrawlManager.java @@ -0,0 +1,186 @@ +package content.region.kandarin.barcrawl; + +import core.api.LoginListener; +import core.api.PersistPlayer; +import core.game.component.Component; +import core.game.node.entity.player.Player; + +import core.game.node.item.Item; +import org.jetbrains.annotations.NotNull; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +/** + * Manages the players barcrawl quest. + * @author 'Vexia + */ +public final class BarcrawlManager implements LoginListener, PersistPlayer { + + /** + * The barcrawl card. + */ + public static final Item BARCRAWL_CARD = new Item(455); + + /** + * The name of bars. + */ + public static final String[] NAMES = new String[] { "BlueMoon Inn", "Blueberry's Bar", "Dead Man's Chest", "Dragon Inn", "Flying Horse Inn", "Foresters Arms", "Jolly Boar Inn", "Karamja Spirits bar", "Rising Sun Inn", "Rusty Anchor Inn" }; + + /** + * The component used to draw the completed bars on. + */ + public static final Component COMPONENT = new Component(220); + + /** + * The player. + */ + private final Player player; + + /** + * The bars completed. + */ + private final boolean[] bars = new boolean[10]; + + /** + * If the quest has been started. + */ + private boolean started; + + /** + * Constructs a new {@code BarcrawlManager} {@code Object}. + * @param player the player. + */ + public BarcrawlManager(final Player player) { + this.player = player; + } + + public BarcrawlManager() {this.player = null;} + + @Override + public void login(@NotNull Player player) { + BarcrawlManager instance = new BarcrawlManager(player); + player.setAttribute("barcrawl-inst", instance); + } + + @Override + public void parsePlayer(@NotNull Player player, @NotNull JSONObject data) { + JSONObject bcData = (JSONObject) data.get("barCrawl"); + if(bcData == null) return; + JSONArray barsVisisted = (JSONArray) bcData.get("bars"); + BarcrawlManager instance = getInstance(player); + instance.started = (boolean) bcData.get("started"); + for(int i = 0; i < barsVisisted.size(); i++){ + instance.bars[i] = (boolean) barsVisisted.get(i); + } + } + + @Override + public void savePlayer(@NotNull Player player, @NotNull JSONObject save) { + BarcrawlManager instance = getInstance(player); + JSONObject barCrawl = new JSONObject(); + barCrawl.put("started", instance.started); + JSONArray barsVisited = new JSONArray(); + for(boolean visited : instance.bars) + barsVisited.add(visited); + barCrawl.put("bars",barsVisited); + save.put("barCrawl",barCrawl); + } + + /** + * Method used to read the card. + */ + public void read() { + if (isFinished()) { + player.getPacketDispatch().sendMessage("You are too drunk to be able to read the barcrawl card."); + ; + return; + } + player.getInterfaceManager().open(COMPONENT); + drawCompletions(); + } + + /** + * Draws the completed bars on the interface. + */ + private void drawCompletions() { + player.getPacketDispatch().sendString("The Official Alfred Grimhand Barcrawl!", 220, 1); + boolean complete; + for (int i = 0; i < bars.length; i++) { + complete = bars[i]; + player.getPacketDispatch().sendString((complete ? "" : "") + NAMES[i] + " - " + (complete ? "Complete!" : "Not Completed..."), 220, 3 + i); + } + } + + /** + * Completes a bar challenge. + * @param index the index. + */ + public void complete(int index) { + bars[index] = true; + } + + /** + * Checks if the barcrawl quest is completed. + * @return {@code True} if so. + */ + public boolean isFinished() { + for (int i = 0; i < bars.length; i++) { + if (!isCompleted(i)) { + return false; + } + } + return true; + } + + /** + * Resets the bars. + */ + public void reset() { + started = false; + for (int i = 0; i < bars.length; i++) { + bars[i] = false; + } + } + + /** + * Checks if a bar is completed. + * @param index the index. + * @return {@code True} if completed. + */ + public boolean isCompleted(int index) { + return bars[index]; + } + + /** + * Checks if the player has the card. + * @return the card. + */ + public boolean hasCard() { + return player.getInventory().containsItem(BARCRAWL_CARD) || player.getBank().containsItem(BARCRAWL_CARD); + } + + /** + * Gets the started. + * @return The started. + */ + public boolean isStarted() { + return started; + } + + /** + * Sets the started. + * @param started The started to set. + */ + public void setStarted(boolean started) { + this.started = started; + } + + public boolean[] getBars() { + return bars; + } + + public static BarcrawlManager getInstance(Player player) + { + return player.getAttribute("barcrawl-inst", new BarcrawlManager()); + } +} diff --git a/Server/src/main/java/core/game/content/quest/miniquest/barcrawl/BarcrawlType.java b/Server/src/main/content/region/kandarin/barcrawl/BarcrawlType.java similarity index 99% rename from Server/src/main/java/core/game/content/quest/miniquest/barcrawl/BarcrawlType.java rename to Server/src/main/content/region/kandarin/barcrawl/BarcrawlType.java index 8573d9b54..0ae18f671 100644 --- a/Server/src/main/java/core/game/content/quest/miniquest/barcrawl/BarcrawlType.java +++ b/Server/src/main/content/region/kandarin/barcrawl/BarcrawlType.java @@ -1,4 +1,4 @@ -package core.game.content.quest.miniquest.barcrawl; +package content.region.kandarin.barcrawl; import core.game.node.entity.combat.ImpactHandler; import core.game.node.entity.player.Player; @@ -9,7 +9,7 @@ import core.net.packet.PacketRepository; import core.net.packet.context.CameraContext; import core.net.packet.out.CameraViewPacket; import core.tools.StringUtils; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * A barcrawl type npc. diff --git a/Server/src/main/content/region/kandarin/catherby/dialogue/ArheinDialogue.kt b/Server/src/main/content/region/kandarin/catherby/dialogue/ArheinDialogue.kt new file mode 100644 index 000000000..3300e43d5 --- /dev/null +++ b/Server/src/main/content/region/kandarin/catherby/dialogue/ArheinDialogue.kt @@ -0,0 +1,188 @@ +package content.region.kandarin.catherby.dialogue +import core.api.* +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.StringUtils +import org.rs09.consts.NPCs +import core.ServerStore +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 + + +/** + * @author lila + */ + +/** + * Arhein dialogue + * This class handles all dialogue from the Catherby dock NPC Arhein. + * + * Arhein is relevant to: + * Merlin's Crystal quest + * crafting/glassmaking - via selling seaweed + * farming/supercompost - via selling pineapples + */ + +@Initializable +class ArheinDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + + val limits = mapOf( + Items.PINEAPPLE_2114 to 40, + Items.SEAWEED_401 to 80 + ) + val period = "daily" + + // this function handles Arhein's specialty stock (pineapple and seaweed) transactions. + fun getGoods(requestedItem: Int, requestedAmount: Int): Int { + val price = 2 + val afford = player.getInventory().getAmount(Items.COINS_995) / price + var realamount = minOf(requestedAmount, afford) + val exactafford = (realamount == afford) && (afford == freeSlots(player) + 1) + realamount = minOf(realamount,if(exactafford) realamount else freeSlots(player)) + realamount = ServerStore.getNPCItemAmount(NPCs.ARHEIN_563, requestedItem, limits.getOrDefault(requestedItem,0), player, realamount, period) + if (removeItem(player, Item(Items.COINS_995, realamount * price), Container.INVENTORY)) { + if (addItem(player, requestedItem, realamount, Container.INVENTORY)) { + ServerStore.addNPCItemAmount(NPCs.ARHEIN_563, requestedItem, limits.getOrDefault(requestedItem,0), player, realamount, period) + return realamount + } + } + return 0 + } + // this function handles selecting which of the specialty stock items (pineapple or seaweed) to use. + fun selectGoods(requestedItem: Int) { + this.goods = requestedItem + this.stock = ServerStore.getNPCItemStock(NPCs.ARHEIN_563,this.goods,limits.getOrDefault(this.goods,0),player,period) + } + + // some class variables that handle the special stock (pineapples and seaweed) + private var goods = Items.PINEAPPLE_2114 + private var goodsMessage = "" + private var goodsName = "" + private var stock = 0 + + /** Arhein Dialogue Opener + * If the opener is called with nontrivial arguments, then this indicates that the player tried to board the ship without permission. + * In that case, Arhein tells the player to bugger off. + * Otherwise, start the normal dialogue. + * Before all this, we check and initialize some important information for Arhein's specialty stock. + */ + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + if (args.size > 1) { + npcl(core.game.dialogue.FacialExpression.ANGRY, "Hey buddy! Get away from my ship alright?") + stage = 701 + } else { + npcl(core.game.dialogue.FacialExpression.HAPPY, "Hello! Would you like to trade? I've a variety of wares for sale!") + stage = 1 + } + return true + } + + /** Arhein Dialogue Handler + * Handler for the dialogue. the handler determines dialogue content and flow, + * as well as some specialty operations to handle special things like quest progress and specialty stock. + */ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + 1 -> showTopics( + Topic("Let's Trade.", 7), + Topic("I hear that you sell pineapples.",800), + Topic("I hear that you sell seaweed.",900), + Topic("No thank you.", END_DIALOGUE), + Topic("Is that your ship?", 100) + ) + 7 -> npc.openShop(player).also { stage = END_DIALOGUE } + 100 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, I use it to make deliveries to my customers up and down the coast. These crates here are all ready for my next trip.").also { stage++ } + 101 -> showTopics( + Topic("Where do you deliver to?",120), + Topic("Are you rich then?", 110), + Topic("Do you deliver to the fort just down the coast?",500) + ) + 110 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Business is going reasonably well... I wouldn't say I was the richest of merchants ever, but I'm doing fairly well all things considered.").also { stage = END_DIALOGUE } + 120 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Various places up and down the coast. Mostly Karamja and Port Sarim.").also { stage++ } + 121 -> showTopics( + Topic("I don't suppose I could get a lift anywhere?",140), + Topic("Well, good luck with your business.",130) + ) + 130 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Thanks buddy!").also { stage = END_DIALOGUE } + 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(Quests.MERLINS_CRYSTAL) + if(queststage == 30 || queststage == 40) { + loadFile(ArheinMCDialogue(queststage)) + } else { + stage = END_DIALOGUE + } + } + 701 -> playerl(core.game.dialogue.FacialExpression.GUILTY,"Yeah... uh... sorry...").also{ stage = END_DIALOGUE } + 800 -> { + // Pineapples + selectGoods(Items.PINEAPPLE_2114) + // and now the conditional block to handle cases + if(stock==0) { + npcl(core.game.dialogue.FacialExpression.SAD,"Actually, I've run out. Come back tomorrow and I should have some more.") + stage = END_DIALOGUE + } else { + // approximately authentic dialogue prompt for buying pineapples from arhein. (closest known dialogues do not make grammatical sense when spliced.) + this.goodsMessage = "I certainly do! I've got ${stock} in stock, going for 2 coins each. How many would you like?" + npcl(core.game.dialogue.FacialExpression.HAPPY,this.goodsMessage) + this.goodsName = if(stock==1) getItemName(this.goods) else StringUtils.plusS(getItemName(this.goods)) + stage = 1200 + } + } + 900 -> { + // Seaweed + selectGoods(Items.SEAWEED_401) + // and now the conditional block to handle cases + if(stock==0) { + npcl(core.game.dialogue.FacialExpression.SAD,"Actually, I've run out. Come back tomorrow and I should have some more.") + stage = END_DIALOGUE + } else { + // approximately authentic dialogue prompt for buying seaweed from arhein. (closest known dialogues do not make grammatical sense when spliced.) + this.goodsMessage = "I certainly do! I've ${stock} at the moment and they cost 2 coins each. How many would you like?" + npcl(core.game.dialogue.FacialExpression.HAPPY,this.goodsMessage) + this.goodsName = getItemName(this.goods) + stage = 1200 + } + } + 1200 -> { + // arhein specialty items sales + val goodsPrompt = "Arhein has ${this.stock} ${this.goodsName}. How many would you like to buy?" + sendInputDialogue(player,InputType.AMOUNT, goodsPrompt) { value -> + val amountReceived = getGoods(this.goods,Integer.parseInt(value.toString())) + var exitMsg = "" + if(amountReceived==this.stock) { + exitMsg = "Here you go! I've run out for now. Come again tomorrow and I should have more." + } else if (amountReceived>0) { + // inauthentic exit dialogue for arhein's pineapple/seaweed sales. documented dialogue that i found indicates that there is no exit message. + // i kept the spirit of arhein's personality, using "buddy!" that is known from another line. + exitMsg = "Here you go, buddy!" + } else { + // same deal here, inauthentic exit dialogue based on arhein's authentic personality. + exitMsg = "Take care, buddy!" + } + npcl(core.game.dialogue.FacialExpression.HAPPY,exitMsg) + } + stage = END_DIALOGUE + } + } + return true + } + + + // finale funz - newInstance, getIds + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return ArheinDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ARHEIN_563) + } +} + diff --git a/Server/src/main/java/core/game/content/dialogue/HarryDialogue.java b/Server/src/main/content/region/kandarin/catherby/dialogue/HarryDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/HarryDialogue.java rename to Server/src/main/content/region/kandarin/catherby/dialogue/HarryDialogue.java index 8e1d65163..1ecf54b21 100644 --- a/Server/src/main/java/core/game/content/dialogue/HarryDialogue.java +++ b/Server/src/main/content/region/kandarin/catherby/dialogue/HarryDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.kandarin.catherby.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import org.rs09.consts.Items; import core.game.node.entity.npc.NPC; import core.game.node.item.Item; @@ -59,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/java/core/game/content/dialogue/HicktonDialogue.java b/Server/src/main/content/region/kandarin/catherby/dialogue/HicktonDialogue.java similarity index 81% rename from Server/src/main/java/core/game/content/dialogue/HicktonDialogue.java rename to Server/src/main/content/region/kandarin/catherby/dialogue/HicktonDialogue.java index febb0b3bc..b0c0103c4 100644 --- a/Server/src/main/java/core/game/content/dialogue/HicktonDialogue.java +++ b/Server/src/main/content/region/kandarin/catherby/dialogue/HicktonDialogue.java @@ -1,6 +1,8 @@ -package core.game.content.dialogue; +package content.region.kandarin.catherby.dialogue; -import core.game.content.global.Skillcape; +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.plugin.Initializable; @@ -39,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; } @@ -63,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; } @@ -82,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 new file mode 100644 index 000000000..f1ed25b7d --- /dev/null +++ b/Server/src/main/content/region/kandarin/catherby/dialogue/VanessaDialogue.java @@ -0,0 +1,77 @@ +package content.region.kandarin.catherby.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 VanessaDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class VanessaDialogue extends DialoguePlugin { + + public VanessaDialogue() { + + } + + public VanessaDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 2305 }; + } + + @Override + 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 = 10; + break; + case 10: + switch (buttonId) { + case 1: + end(); + npc.openShop(player); + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Can you give me any Farming advice?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "I'm okay, thank you."); + stage = 30; + break; + + } + break; + case 20: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes - ask a gardener."); + stage = 30; + break; + case 30: + end(); + break; + } + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new VanessaDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + 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 new file mode 100644 index 000000000..fd294e309 --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt @@ -0,0 +1,179 @@ +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 +import org.rs09.consts.Components +import org.rs09.consts.NPCs +import core.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +@Initializable +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( + FacialExpression.NEUTRAL, + "My apologies, dear ${if (player.isMale) "sir" else "madam"}, " + + "our services are not available for Ultimate ${if (player.isMale) "Ironmen" else "Ironwomen"}." + ).also { stage = END_DIALOGUE } + } + else { + npcl( + FacialExpression.NEUTRAL, + "Good day, how may I help you?" + ).also { + if (hasAwaitingGrandExchangeCollections(player)) { + stage++ + } else { + stage += 2 + } + } + } + + 1 -> npcl( + FacialExpression.NEUTRAL, + "Before we go any further, I should inform you that you " + + "have items ready for collection from the Grand Exchange." + ).also { stage++ } + + 2 -> playerl(FacialExpression.ASKING, "Who are you?") + .also { stage++ } + + 3 -> npcl( + FacialExpression.NEUTRAL, + "How 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(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( + 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( + FacialExpression.ASKING, + "So... What are you doing here?" + ).also { stage++ } + + 12 -> npcl( + 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( + 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( + FacialExpression.ASKING, + "Can I access my bank account by speaking to you?" + ).also { stage++ } + + 15 -> npcl( + 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( + 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( + FacialExpression.NEUTRAL, + "We know you will be busy creating runes and do not wish " + + "to carry money with you." + ).also { stage++ } + + 18 -> npcl( + 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( + FacialExpression.HALF_ASKING, + "Would you like to pay the price of twenty runes to open " + + "your bank account?" + ).also { stage++ } + + 20 -> showTopics( + 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( + 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( + FacialExpression.NEUTRAL, + "I'm sorry to hear that, dear ${if (player.isMale) "sir" else "madam"}." + ).also { stage++ } + + 23 -> npcl( + 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 } + + 30 -> { + setAttribute(player, "zmi:bankaction", "open") + openInterface(player, Components.BANK_CHARGE_ZMI_619) + end() + } + + 31 -> { + openBankPinSettings(player) + end() + } + + 32 -> { + setAttribute(player, "zmi:bankaction", "collect") + 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) +} diff --git a/Server/src/main/content/region/kandarin/dialogue/EniolaListener.kt b/Server/src/main/content/region/kandarin/dialogue/EniolaListener.kt new file mode 100644 index 000000000..9a7656d25 --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/EniolaListener.kt @@ -0,0 +1,37 @@ +package content.region.kandarin.dialogue + +import core.api.openInterface +import core.api.restrictForIronman +import core.api.setAttribute +import core.game.node.entity.player.link.IronmanMode +import org.rs09.consts.Components +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Handles special banking interactions for Eniola at ZMI altar area. + * + * @author vddCore + */ +class EniolaListener : InteractionListener { + override fun defineListeners() { + on(NPCs.ENIOLA_6362, IntType.NPC, "bank") { player, _ -> + restrictForIronman(player, IronmanMode.ULTIMATE) { + setAttribute(player, "zmi:bankaction", "open") + openInterface(player, Components.BANK_CHARGE_ZMI_619) + } + + return@on true + } + + on(NPCs.ENIOLA_6362, IntType.NPC, "collect") { player, _ -> + restrictForIronman(player, IronmanMode.ULTIMATE) { + setAttribute(player, "zmi:bankaction", "collect") + openInterface(player, Components.BANK_CHARGE_ZMI_619) + } + + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/GalahadDialogue.java b/Server/src/main/content/region/kandarin/dialogue/GalahadDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/GalahadDialogue.java rename to Server/src/main/content/region/kandarin/dialogue/GalahadDialogue.java index 814b8f6d5..f7ab01f90 100644 --- a/Server/src/main/java/core/game/content/dialogue/GalahadDialogue.java +++ b/Server/src/main/content/region/kandarin/dialogue/GalahadDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.kandarin.dialogue; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; import org.rs09.consts.Items; import core.game.node.entity.npc.NPC; 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 new file mode 100644 index 000000000..dd0865501 --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/OttoGodblessedDialogue.kt @@ -0,0 +1,91 @@ +package content.region.kandarin.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.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +/** + * Represents the dialogue plugin used for Otto + * @author vddCore + */ +@Initializable +class OttoGodblessedDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player): DialoguePlugin { + return OttoGodblessedDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + npcl( + FacialExpression.HAPPY, + "Good day, you seem a hearty warrior. " + + "Maybe even some barbarian blood in that body of yours?" + ) + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> showTopics( + Topic(FacialExpression.ASKING, "Is there anything you can teach me?", 1), + Topic(FacialExpression.NEUTRAL, "Nevermind.", END_DIALOGUE) + ) + + 1 -> npcl( + FacialExpression.FRIENDLY, + "I can teach you how to fish." + ).also { stage++ } + + 2 -> playerl( + FacialExpression.HALF_ROLLING_EYES, + "Oh, that's pretty underwhelming. But uhhh, okay!" + ).also { stage++ } + + 3 -> npcl( + 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( + FacialExpression.HALF_ASKING, + "The whole... Pole?" + ).also { stage++ } + + 5 -> npcl( + FacialExpression.ANNOYED, + "No, not the whole pole!" + ).also { stage++ } + + 6 -> npcl( + FacialExpression.ANNOYED, + "Look. Just... Grab the pole from under my bed and go click on that fishing spot." + ).also { stage++ } + + 7 -> playerl( + FacialExpression.STRUGGLE, + "...click?" + ).also { stage++ } + + 8 -> npcl( + FacialExpression.FURIOUS, + "JUST GO DO IT!" + ).also { + stage = END_DIALOGUE + player.setAttribute("/save:barbtraining:fishing", true) + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.OTTO_GODBLESSED_2725) +} \ No newline at end of file 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/java/core/game/content/dialogue/StankersDialogue.java b/Server/src/main/content/region/kandarin/dialogue/StankersDialogue.java similarity index 88% rename from Server/src/main/java/core/game/content/dialogue/StankersDialogue.java rename to Server/src/main/content/region/kandarin/dialogue/StankersDialogue.java index ea022144d..5e419f60a 100644 --- a/Server/src/main/java/core/game/content/dialogue/StankersDialogue.java +++ b/Server/src/main/content/region/kandarin/dialogue/StankersDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.kandarin.dialogue; +import core.game.dialogue.DialoguePlugin; import org.rs09.consts.Items; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -40,7 +41,6 @@ public class StankersDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - AchievementDiary diary = player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE); int level = 1; switch (stage) { @@ -62,22 +62,17 @@ public class StankersDialogue extends DialoguePlugin { stage = 20; break; case 3: - if (!diary.isStarted(level)) { - player("Do you have an Achievement Diary for me?"); - stage = 100; - break; - } - else if (diary.isLevelRewarded(level) && !player.hasItem(diary.getType().getRewards()[level][0])) { + if (AchievementDiary.canReplaceReward(player, DiaryType.SEERS_VILLAGE, level)) { player("I seem to have lost my seers' headband..."); stage = 80; break; } - else if (diary.isLevelRewarded(level)) { + else if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.SEERS_VILLAGE, level)) { player("Can you remind me what my headband does?"); stage = 90; break; } - else if (diary.isComplete(level, true)) { + else if (AchievementDiary.canClaimLevelRewards(player, DiaryType.SEERS_VILLAGE, level)) { player("Hi. I've completed the Medium tasks in my Achievement", "Diary. Can I have a reward?"); stage = 200; break; @@ -151,7 +146,7 @@ public class StankersDialogue extends DialoguePlugin { break; case 80: - player.getInventory().add(diary.getType().getRewards(level)[0], player); + AchievementDiary.grantReplacement(player, DiaryType.SEERS_VILLAGE, level); npc("Here's your replacement. Please be more careful."); stage = 999; break; @@ -176,12 +171,11 @@ public class StankersDialogue extends DialoguePlugin { stage++; break; case 201: - if (!player.hasItem(diary.getType().getRewards()[level][0])) { + if (!player.hasItem(AchievementDiary.getRewards(DiaryType.SEERS_VILLAGE, level)[0])) { npc("I need your headband to anoint it! Come back when", "you have it."); stage = 999; } else { - diary.setLevelRewarded(level); - player.getInventory().add(diary.getType().getRewards()[level][1]); + AchievementDiary.flagRewarded(player, DiaryType.SEERS_VILLAGE, level); sendDialogue("Stankers produces a chalice containing a vile-looking concoction that", "he pours all over your headband."); stage++; } 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/java/core/game/content/dialogue/HuntingExpert.java b/Server/src/main/content/region/kandarin/feldip/dialogue/HuntingExpert.java similarity index 98% rename from Server/src/main/java/core/game/content/dialogue/HuntingExpert.java rename to Server/src/main/content/region/kandarin/feldip/dialogue/HuntingExpert.java index 94cafcbf0..e69ff6954 100644 --- a/Server/src/main/java/core/game/content/dialogue/HuntingExpert.java +++ b/Server/src/main/content/region/kandarin/feldip/dialogue/HuntingExpert.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.kandarin.feldip.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; @@ -270,6 +272,6 @@ public final class HuntingExpert extends DialoguePlugin { @Override public int[] getIds() { - return new int[] { 5112, 5113 }; + return new int[] { 5113 }; } } diff --git a/Server/src/main/java/core/game/interaction/npc/BogrogPlugin.java b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPlugin.java similarity index 84% rename from Server/src/main/java/core/game/interaction/npc/BogrogPlugin.java rename to Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPlugin.java index bc6d8a378..c66f8e3ae 100644 --- a/Server/src/main/java/core/game/interaction/npc/BogrogPlugin.java +++ b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPlugin.java @@ -1,17 +1,20 @@ -package core.game.interaction.npc; +package content.region.kandarin.feldip.gutanoth.handlers; import core.cache.def.impl.NPCDefinition; import core.game.component.Component; import core.game.container.access.InterfaceContainer; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; 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; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import kotlin.Unit; + +import static core.api.ContentAPIKt.*; /** * Handles the bogrog npc. @@ -23,7 +26,7 @@ public final class BogrogPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { NPCDefinition.forId(4472).getHandlers().put("option:swap", this); - PluginManager.definePlugin(new BogrogDialogue()); + ClassScanner.definePlugin(new BogrogDialogue()); return this; } @@ -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 new file mode 100644 index 000000000..90dbfbea5 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPouchSwapper.kt @@ -0,0 +1,82 @@ +package content.region.kandarin.feldip.gutanoth.handlers + +import core.api.* +import core.game.component.Component +import core.game.node.entity.player.Player +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 + +/** + * Handles swapping pouches/scrolls for shards. + * We decided to use the GE set exchange interface for this because it's the only one we could find + * that allowed us to do what we needed. Jagex did the same thing as far as we can tell. + * @author Ceikry + */ +object BogrogPouchSwapper { + //Opcodes for item options + 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 + + //GE Zone borders because shitty hack lol + private val GEBorders = ZoneBorders(3151,3501,3175,3477) + + @JvmStatic + 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, InputType.AMOUNT, "Enter the amount:"){value -> + swap(player, value as Int, item.id) + } + } + else -> false + } + } + + private fun swap(player: Player, amount: Int, itemID: Int): Boolean{ + var amt = amount + val value = getValue(itemID) + if(value == 0.0){ + return false + } + val inInventory = player.inventory.getAmount(itemID) + if(amount > inInventory) + amt = inInventory + player.inventory.remove(Item(itemID,amt)) + player.inventory.add(Item(SPIRIT_SHARD, floor(value * amt).toInt())) + return true + } + + private fun sendValue(itemID: Int, player: Player): Boolean{ + val value = getValue(itemID) + if(value == 0.0){ + return false + } + + player.sendMessage("Bogrog will give you ${floor(value).toInt()} shards for that.") + return true + } + + private fun getValue(itemID: Int): Double{ + var item = SummoningPouch.get(itemID) + var isScroll = false + if(item == null) item = SummoningPouch.get(Item(itemID).noteChange) + 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 + return if(isScroll) shardQuantity / 20.0 else ceil(shardQuantity) + } +} diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/GutanothChestPlugin.kt b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/GutanothChestPlugin.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/interaction/object/GutanothChestPlugin.kt rename to Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/GutanothChestPlugin.kt index e26c41232..c98daee42 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/object/GutanothChestPlugin.kt +++ b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/GutanothChestPlugin.kt @@ -1,4 +1,4 @@ -package rs09.game.interaction.`object` +package content.region.kandarin.feldip.gutanoth.handlers import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder @@ -9,16 +9,17 @@ 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 rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.GameWorld import java.util.concurrent.TimeUnit private const val CHEST = 2827 -class GutanothChestInteractionHandler : InteractionListener(){ +class GutanothChestInteractionHandler : InteractionListener { override fun defineListeners() { - on(CHEST,SCENERY,"open"){ player, node -> + on(CHEST, IntType.SCENERY, "open"){ player, node -> val delay = player.getAttribute("gutanoth-chest-delay", 0L) GameWorld.Pulser.submit(ChestPulse(player,System.currentTimeMillis() > delay, node as Scenery)) return@on true @@ -64,7 +65,7 @@ class GutanothChestInteractionHandler : InteractionListener(){ } } - enum class Rewards(val id: Int,val type: Type,val message: String){ + enum class Rewards(val id: Int, val type: Type, val message: String){ BONES(Items.BONES_2530, Type.ITEM, "Oh! Some bones. Delightful."), EMERALD(Items.EMERALD_1605, Type.ITEM, "Ooh! A lovely emerald!"), ROTTEN_APPLE(Items.ROTTEN_APPLE_1984, Type.ITEM, "Oh, joy, spoiled fruit! My favorite!"), 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 new file mode 100644 index 000000000..2df29fe88 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/BalneaDialogue.kt @@ -0,0 +1,52 @@ +package content.region.kandarin.feldip.ooglog.dialogue + +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 core.tools.START_DIALOGUE + +/** + * Provides dialogue tree for Balnea NPC involved in the + * "As a First Resort..." quest. + * + * @author vddCore + */ +@Initializable +class BalneaDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> playerl( + FacialExpression.FRIENDLY, + "Hi there!" + ).also { stage++ } + + 1 -> npcl( + FacialExpression.NEUTRAL, + "I'm ever so busy at the moment; please come back after the grand opening." + ).also { stage++ } + + 2 -> playerl( + FacialExpression.HALF_ASKING, + "What grand reopening?" + ).also { stage++ } + + 3 -> npcl( + FacialExpression.ANNOYED, + "I'm sorry, I really can't spare the time to talk to you." + ).also { stage++ } + + 4 -> playerl( + FacialExpression.THINKING, + "Uh, sure." + ).also { stage = END_DIALOGUE } + } + + /* TODO: "As a First Resort" quest dialogue file is required here. */ + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.BALNEA_7047) +} \ No newline at end of file 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 new file mode 100644 index 000000000..045e01862 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/OgressBankerDialogue.kt @@ -0,0 +1,78 @@ +package content.region.kandarin.feldip.ooglog.dialogue + +import core.api.sendNPCDialogue +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 core.tools.START_DIALOGUE + +/** + * Provides dialogue tree for the Ogress Bankers in the city of Oo'glog. + * + * @author vddCore + */ +@Initializable +class OgressBankerDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun getIds(): IntArray = intArrayOf( + NPCs.OGRESS_BANKER_7049, + NPCs.OGRESS_BANKER_7050 + ) + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> npcl( + FacialExpression.ANNOYED, + "..." + ).also { stage++ } + + 1 -> playerl( + FacialExpression.ANNOYED, + "Excuse me, can I get some service here, please?" + ).also { stage++ } + + 2 -> npcl( + FacialExpression.ANGRY, + "GRAAAAAH! You go away, human! Me too busy with training to talk to puny thing like you." + ).also { stage++ } + + 3 -> sendNPCDialogue( + player, + NPCs.BALNEA_7047, + "I do apologise, sir. We're temporarily unable to meet your banking needs.", + FacialExpression.NEUTRAL, + ).also { stage++ } + + 4 -> sendNPCDialogue( + player, + NPCs.BALNEA_7047, + "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.", + FacialExpression.NEUTRAL + ).also { stage++ } + + 5 -> playerl( + FacialExpression.THINKING, + "What did you just say to me?" + ).also { stage++ } + + 6 -> sendNPCDialogue( + player, + NPCs.BALNEA_7047, + "We're closed until I can teach these wretched creatures some manners.", + FacialExpression.ANNOYED + ).also { stage++ } + + 7 -> playerl( + FacialExpression.NEUTRAL, + "Ah, right. Good luck with that." + ).also { stage = END_DIALOGUE } + } + + /* TODO: "As a First Resort..." quest dialogue file is required here. */ + return true + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..49a074680 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/BloatedToadNPC.kt @@ -0,0 +1,168 @@ +package content.region.kandarin.feldip.quest.chompybird + +import core.api.* + +import org.rs09.consts.NPCs +import org.rs09.consts.Items +import org.rs09.consts.Graphics + +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.system.command.Privilege +import core.plugin.Initializable +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.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 { + constructor() : super(NPCs.BLOATED_TOAD_1014, null, true) {} + private constructor(id: Int, location: Location) : super(id, location) {} + + var ticksToLive = 100 + var chompySpawned = false + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + val npc = BloatedToadNPC(id, location) + npc.isWalks = false + npc.isNeverWalks = true + npc.isRespawn = false + return npc + } + + override fun getIds() : IntArray { + return intArrayOf(NPCs.BLOATED_TOAD_1014) + } + + override fun handleTickActions() { + super.handleTickActions() + + if (ticksToLive-- <= 0) { + val toDamage = RegionManager.getLocalEntitys(this.location, 2) + + for (entity in toDamage) { + if (entity == this) continue + impact(entity, RandomFunction.random(1, 3)) + } + + sendGraphics(Graphics.TOAD_DETONATION_240, this.location) + + clear() + } + + if (!chompySpawned && RandomFunction.random(20) == 5) { //Arbitrary random number matching (ryan: updated this from 1/5 to 1/20 because 1/5 was debug stuff.) + val chompy = NPC.create(NPCs.CHOMPY_BIRD_1550, this.location) + val spawn = getPathableRandomLocalCoordinate(chompy, 4, this.location, 3) + if (spawn == null || spawn == this.location) return + + val owner = getAttribute("owner", null) ?: return + + chompy.isRespawn = false + chompy.location = spawn + chompy.setAttribute("owner", owner) + chompy.setAttribute("toad", this) + chompy.walkingQueue.update() + + chompy.init() + sendChat(chompy, "Squawk!") + clearHintIcon(owner) + registerHintIcon(owner, chompy) + chompySpawned = true + ticksToLive = 100 + } + } + +} + + +class BloatedToadListeners : InteractionListener, StartupListener, Commands { + lateinit var borders: ZoneBorders + val extraBorders = ArrayList() + + override fun startup() { + borders = ZoneBorders(2368, 2944, 2687, 3071) + borders.addException(ZoneBorders.forRegion(10287)) + borders.addException(ZoneBorders.forRegion(10031)) + borders.addException(ZoneBorders.forRegion(9775)) + } + + override fun defineCommands() { + define("toadzone", Privilege.MODERATOR, "", "Toad inflation.") {player, _ -> + val swloc = player.location.transform(-15,-15,0) + val neloc = player.location.transform(15,15,0) + val newBorders = ZoneBorders(swloc.x, swloc.y, neloc.x, neloc.y) + extraBorders.add(newBorders) + for (i in 0 until 10) { + val npc = NPC(NPCs.SWAMP_TOAD_1013, newBorders.randomLoc) + npc.isRespawn = true + npc.isWalks = true + npc.isNeverWalks = false + npc.init() + } + } + + define("toadbomb", Privilege.ADMIN, "", "Toad detoadation.") {player, _ -> + val swloc = player.location.transform(-5,-5,0) + val neloc = player.location.transform(5,5,0) + for (x in swloc.x until neloc.x) { + for (y in swloc.y until neloc.y) { + val npc = BloatedToadNPC().construct(NPCs.BLOATED_TOAD_1014, Location.create(x, y, swloc.z)) + npc.isNeverWalks = true + npc.isWalks = false + (npc as BloatedToadNPC).ticksToLive = 5 + npc.init() + } + } + } + } + + override fun defineListeners() { + on(Items.BLOATED_TOAD_2875, IntType.ITEM, "drop") { player, used -> + val quest = player.questRepository.getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING) + val inExtraBorder = extraBorders.filter { it.insideBorder(player) }.count() > 0 + + if (!borders.insideBorder(player) && !inExtraBorder) { + sendPlayerDialogue(player, "I probably wouldn't catch many chompies here.") + return@on true + } + + if (quest.getStage(player) < 30) { + sendPlayerDialogue(player, "I don't know what you want from me.") + return@on true + } + + if (quest.getStage(player) in 30..40) { + if (player.location == Location.create(2635, 2966, 0) || player.location == Location.create(2636, 2966, 0)){ + quest.setStage(player, 40) + } else { + sendPlayerDialogue(player, "I should probably put this where Rantz told me.") + return@on true + } + } + + if (removeItem(player, used.asItem())) { + val toad = core.game.node.entity.npc.NPC.create(NPCs.BLOATED_TOAD_1014, player.location) + setAttribute(toad, "owner", player) + toad.init() + } + return@on true + } + + on(Items.BLOATED_TOAD_2875, IntType.ITEM, "release") { player, used -> + removeItem(player, used.asItem()) + sendPlayerDialogue(player, "Free the fat toadsies!") + return@on true + } + + on(Items.BLOATED_TOAD_2875, IntType.ITEM, "release all") { player, used -> + removeAll(player, used.asItem()) + sendPlayerDialogue(player, "Free the fat toadsies!") + return@on true + } + } +} 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 new file mode 100644 index 000000000..2ca5b5079 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBird.kt @@ -0,0 +1,327 @@ +package content.region.kandarin.feldip.quest.chompybird + +import core.api.* + +import org.rs09.consts.Vars +import org.rs09.consts.Items +import org.rs09.consts.Animations +import org.rs09.consts.Graphics +import org.rs09.consts.NPCs + +import core.plugin.Initializable +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.node.item.Item +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.world.update.flag.context.Graphics as Gfx + +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.system.config.NPCConfigParser +import core.game.world.GameWorld + +import kotlin.math.min +import java.util.Random +import content.data.Quests + +@Initializable +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) + val TOAD_LOCATION = Location.create(2636, 2966, 0) + val ATTR_ING_RANTZ = "/save:chompybird:rantz-ingredient" + val ATTR_ING_BUGS = "/save:chompybird:bugs-ingredient" + val ATTR_ING_FYCIE = "/save:chompybird:fycie-ingredient" + val ATTR_BUGS_ASKED = "/save:chompybird:bugs-asked" + val ATTR_FYCIE_ASKED = "/save:chompybird:fycie-asked" + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + + var ln = 11 + + if (stage == 0) { + line(player, "To start this quest I will need:", ln++, false) + line(player, "Level 5 !!Fletching??", ln++, getStatLevel(player, Skills.FLETCHING) >= 5) + line(player, "Level 30 !!Cooking??", ln++, getStatLevel(player, Skills.COOKING) >= 30) + line(player, "Level 30 !!Ranged??", ln++, getStatLevel(player, Skills.RANGE) >= 30) + line(player, "Ability to defend against !!level 64 wolves?? and !!level 70 ogres??for short periods of time.", ln++, false) + } else { + if (stage == 10) { + line(player, "Rantz needs me to make 'stabbers'. To do this I need:", ln++, false) + line(player, "- !!Achey Logs??", ln++, false) + line(player, "- !!Wolf Bones??", ln++, false) + line(player, "- !!Feathers??", ln++, false) + line(player, "I then must turn the !!achey logs?? into !!ogre shafts??,", ln++, false) + line(player, "attach !!feathers?? to these !!shafts??, and then tip them", ln++, false) + line(player, "with !!wolf bones?? chiseled into !!tips??.", ln++, false) + line(player, "At least, that's what I think he was getting at.", ln++, false) + } else if (stage > 10) { + line(player, "I created 'stabbers' for Rantz.", ln++, true) + } + + if (stage == 20) { + line(player, "Rantz needs me to obtain a bloated swamp toad.", ln++, false) + line(player, "To do this, I need to take !!billows?? from the !!locked", ln++, false) + line(player, "!!chest?? in his cave, and then head to the !!swamp to", ln++, false) + line(player, "!!the south??. There, I should !!use the billows?? on the", ln++, false) + line(player, "!!swamp bubbles?? to fill them with swamp gas. Then I can", ln++, false) + line(player, "use the !!billows?? to fill the !!swamp toads?? with gas.", ln++, false) + } else if (stage > 20) { + line(player, "I learned how to collect swamp gas and conduct toad inflation.", ln++, true) + } + + if (stage == 30) { + line(player, "Rantz needs me to place the swamp toad to bait out a 'chompy'.", ln++, false) + } else if (stage > 30) { + line(player, "I learned how to use the toads to bait chompies.", ln++, true) + } + + if (stage == 40) { + line(player, "I should return to Rantz and let him know.", ln++, false) + } + + if (stage == 50) { + line(player, "Rantz keeps missing the birds. Perhaps I should try.", ln++, false) + } else if (stage > 50) { + line(player, "Rantz gave me his bow so that I could try to catch a chompy.", ln++, true) + } + + if (stage == 60) { + line(player, "I should use what I've learned to try to bait and kill a chompy bird.", ln++, false) + } + + if (stage == 70) { + line(player, "I managed to hunt and kill a chompy bird, and now Rantz wants", ln++, false) + line(player, "me to cook the bird for him! And to make it even worse, he and", ln++, false) + line(player, "his children want special ingredients! Those are listed below:", ln++, false) + line(player, "- Rantz wants: !!${getItemName(getAttribute(player, ATTR_ING_RANTZ, -1))}??", ln++, false) + line(player, "- ${if(getAttribute(player, ATTR_BUGS_ASKED, false)) "Bugs wants: !!${getItemName(getAttribute(player, ATTR_ING_BUGS, -1))}??" else "I still need to ask !!Bugs??."}", ln++, false) + 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) + } + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.OGRE_BOW_2883, 230, 277, 5) + drawReward(player, "2 Quest Points, 262 Fletching", ln++) + drawReward(player, "XP, 1470 Cooking XP, 735", ln++) + drawReward(player, "Ranged XP", ln++) + drawReward(player, "Ogre Bow", ln++) + drawReward(player, "Ability to make Ogre Arrows", ln++) + rewardXP(player, Skills.FLETCHING, 262.0) + rewardXP(player, Skills.COOKING, 1470.0) + rewardXP(player, Skills.RANGE, 735.0) + removeItem(player, Items.SEASONED_CHOMPY_2882) + removeAttribute(player, ATTR_ING_BUGS) + removeAttribute(player, ATTR_BUGS_ASKED) + removeAttribute(player, ATTR_ING_RANTZ) + removeAttribute(player, ATTR_ING_FYCIE) + removeAttribute(player, ATTR_FYCIE_ASKED) + } + + override fun newInstance(`object`: Any?): Quest { return this } + + override fun defineListeners() { + val filledBellows = intArrayOf(Items.OGRE_BELLOWS_3_2872, Items.OGRE_BELLOWS_2_2873, Items.OGRE_BELLOWS_1_2874) + + on(3379, IntType.SCENERY, "enter") { player, _ -> + teleport(player, CAVE_ENTRANCE) + sendMessage(player, "You walk through the cave entrance into a dimly lit cave.") + return@on true + } + + on(intArrayOf(32068, 32069), IntType.SCENERY, "pass-through") { player, _ -> + teleport(player, CAVE_EXIT) + sendMessage(player, "You walk back out of the darkness of the cave into daylight.") + return@on true + } + + on(3377, IntType.SCENERY, "unlock") { player, node -> + if (freeSlots(player) == 0) { + sendMessage(player, "You don't have enough space to do that.") + return@on true + } + if (amountInInventory(player, Items.OGRE_BELLOWS_2871) > 0) { + sendDialogue(player, "I don't need another set of bellows.") + return@on true + } + sendMessage(player, "You strain to lift the rock off the chest.") + sendChat(player, "Humph!") + runTask(player, 2) { + if (Random().nextBoolean()) { + sendMessage(player, "But it's just too heavy for you.") + sendMessage(player, "The experience has left you feeling temporarily weakened.") + sendChat(player, "ARRRGH") + adjustLevel(player, Skills.STRENGTH, -1) + } else { + replaceScenery(node.asScenery(), node.id + 1, 5) + sendChat(player, "I guess this is what an ogre would call a locked chest.") + sendItemDialogue(player, Items.OGRE_BELLOWS_2871, "You take the bellows from the chest.") + addItem(player, Items.OGRE_BELLOWS_2871) + } + } + + return@on true + } + + onUseWith(IntType.SCENERY, Items.RAW_CHOMPY_2876, 3375) { player, used, _ -> + val rantzIngredient = getAttribute(player, ATTR_ING_RANTZ, -1) + val bugsIngredient = getAttribute(player, ATTR_ING_BUGS, -1) + val fycieIngredient = getAttribute(player, ATTR_ING_FYCIE, -1) + + if (rantzIngredient == -1) { + sendDialogue(player, "I don't have a reason to do this yet.") + return@onUseWith true + } + + if ( + amountInInventory(player, rantzIngredient) > 0 + && amountInInventory(player, bugsIngredient) > 0 + && amountInInventory(player, fycieIngredient) > 0 + ) { + lock(player, 5) + setVarbit(player, Vars.VARBIT_QUEST_CHOMPY_SPITROAST, 1) + animate(player, Animations.HUMAN_COOKING_RANGE_896) + sendMessage(player, "You carefully place the chompy bird on the spit-roast.") + sendMessage(player, "You add the other ingredients and cook the food.") + runTask(player, 4) { + setVarbit(player, Vars.VARBIT_QUEST_CHOMPY_SPITROAST, 0) + sendItemDialogue( + player, + Items.SEASONED_CHOMPY_2882, + "You use the ${getItemName(rantzIngredient).lowercase()}, ${getItemName(bugsIngredient).lowercase()} and the ${getItemName(fycieIngredient)} with the chompy bird to make a seasoned chompy." + ) + if ( + removeItem(player, used.asItem()) + && removeItem(player, rantzIngredient) + && removeItem(player, bugsIngredient) + && removeItem(player, fycieIngredient) + ) addItem(player, Items.SEASONED_CHOMPY_2882) + sendMessage(player, "Eventually the chompy is cooked") + sendMessage(player, "It has been deliciously seasoned to taste wonderful for ogres.") + } + } else { + sendDialogue(player, "I don't have all the ingredients I need yet.") + player.debug("Required Items: $rantzIngredient, $bugsIngredient, $fycieIngredient") + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.OGRE_BELLOWS_2871, 684) { player, used, _ -> + if (removeItem(player, used.asItem())) { + lock(player, 2) + visualize(player, Animations.HUMAN_USING_BELLOWS_1026, Gfx(Graphics.USING_BELLOWS, 80)) + addItem(player, Items.OGRE_BELLOWS_3_2872) + sendMessage(player, "You fill the bellows with swamp gas.") + } + return@onUseWith true + } + + onUseWith(IntType.NPC, filledBellows, NPCs.SWAMP_TOAD_1013) { player, used, with -> + val hasTooManyToads = amountInInventory(player, Items.BLOATED_TOAD_2875) >= 3 + + if (hasTooManyToads) { + sendMessage(player, "One of your toads manages to escape.") + removeItem(player, Items.BLOATED_TOAD_2875) + } + + sendChat(player, "Come here toady!") + sendMessage(player, "You manage to catch the toad and inflate it with swamp gas.") + visualize(player, Animations.HUMAN_USING_BELLOWS_1026, Gfx(Graphics.USING_BELLOWS, 80)) + animate(with.asNpc(), Animations.TOAD_INFLATION_1019) + runTask(player, 2) { + if (removeItem(player, used.asItem())) { + if (filledBellows.isLast(used.id)) { + addItem(player, Items.OGRE_BELLOWS_2871) + } else { + addItem(player, filledBellows.getNext(used.id)) + } + addItem(player, Items.BLOATED_TOAD_2875) + with.asNpc().respawnTick = GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + } + return@onUseWith true + } + + onUseWith(IntType.ITEM, Items.WOLF_BONES_2859, Items.CHISEL_1755) { player, used, with -> + val maxAmount = amountInInventory(player, used.id) + + if (getStage(player) == 0) { + sendMessage(player, "You must have started Big Chompy Bird Hunting to make these.") + return@onUseWith true + } + + if (getStatLevel(player, Skills.FLETCHING) < 5) { + sendMessage(player, "You need a Fletching level of 5 to make these.") + return@onUseWith true + } + + fun process() { + if (removeItem(player, Item(used.id))) { + addItem(player, Items.WOLFBONE_ARROWTIPS_2861, 4) + rewardXP(player, Skills.FLETCHING, 2.5) + } + } + + sendSkillDialogue(player) { + create { id, amount -> + var actualAmount = min(amount, maxAmount) + runTask( + player, + delay = 2, + repeatTimes = actualAmount, + task = ::process + ) + } + withItems(Item(Items.WOLFBONE_ARROWTIPS_2861, 5)) + } + + return@onUseWith true + } + + onUseWith(IntType.ITEM, Items.WOLFBONE_ARROWTIPS_2861, Items.FLIGHTED_OGRE_ARROW_2865) { player, used, with -> + fun getMaxAmount(_unused: Int = 0): Int { + val tips = amountInInventory(player, Items.WOLFBONE_ARROWTIPS_2861) + val shafts = amountInInventory(player, Items.FLIGHTED_OGRE_ARROW_2865) + 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, Items.OGRE_ARROW_2866, amountThisIter) + sendMessage(player, "You make $amountThisIter ogre arrows.") + rewardXP(player, Skills.FLETCHING, 6.0) + } + } + + sendSkillDialogue(player) { + create { id, amount -> + runTask( + player, + delay = 2, + repeatTimes = min(amount, getMaxAmount() / 6 + 1), + task = ::process + ) + } + calculateMaxAmount(::getMaxAmount) + withItems(Item(Items.OGRE_ARROW_2866, 5)) + } + return@onUseWith true + } + + } +} 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 new file mode 100644 index 000000000..054159fd3 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdDialogues.kt @@ -0,0 +1,462 @@ +package content.region.kandarin.feldip.quest.chompybird + +import core.api.* + +import org.rs09.consts.NPCs +import org.rs09.consts.Items + +import core.tools.END_DIALOGUE +import core.game.dialogue.DialogueFile +import core.game.dialogue.Topic +import core.game.dialogue.IfTopic +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression + +import core.tools.RandomFunction +import core.plugin.Initializable +import core.game.node.item.Item +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import content.data.Quests + +@Initializable +class RantzDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun getIds() : IntArray { + return intArrayOf(NPCs.RANTZ_1010) + } + + 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(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) + + if (stage in 60 until 100 && !hasOgreBow) { + stage = 201 + player.dialogueInterpreter.handle(0,0) + return true + } + + when (chompyStage) { + in 0 until 100 -> loadFile(RantzChompyBirdDialogue(chompyBird)) + } + + player.dialogueInterpreter.handle(0,0) + + return true + } + + override fun handle(componentId: Int, buttonId: Int) : Boolean { + when(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(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(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(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(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(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(FacialExpression.OLD_NORMAL, "There all hats, creature. Come back when kill more chompy. Unless you kill all chompy already.") + } + } + stage = END_DIALOGUE + } + + 200 -> showTopics( + IfTopic("About that...", 201, !inBank(player, Items.OGRE_BOW_2883)), + Topic("Okay.", END_DIALOGUE) + ) + + 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(FacialExpression.OLD_NORMAL, "Me think $cost shiny coins be ok.") + stage++ + } + + 207 -> showTopics( + IfTopic( + "Okay.", 208, + amountInInventory(player, Items.COINS_995) >= getAttribute(player, "chompybird:new-bow-cost", 500) + ), + Topic("No thank you...", END_DIALOGUE) + ) + + 208 -> { + if (removeItem(player, Item(Items.COINS_995, getAttribute(player, "chompybird:new-bow-cost", 500)))) { + addItemOrDrop(player, Items.OGRE_BOW_2883) + npcl(FacialExpression.OLD_NORMAL, "There you go, creature, now hunt chompy.") + } else { + playerl(FacialExpression.FRIENDLY, "Actually, I can't afford it right now.") + } + stage = END_DIALOGUE + } + } + return true + } +} + +@Initializable +class BugsDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun getIds() : IntArray { + return intArrayOf(NPCs.BUGS_1012) + } + + 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(Quests.BIG_CHOMPY_BIRD_HUNTING) + val chompyStage = chompyBird.getStage(player) + + when (chompyStage) { + in 0 until 100 -> loadFile(BugsChompyBirdDialogue(chompyBird)) + } + + player.dialogueInterpreter.handle(0,0) + + return true + } + + override fun handle(componentId: Int, buttonId: Int) : Boolean { + return true + } +} + +@Initializable +class FycieDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun getIds() : IntArray { + return intArrayOf(NPCs.FYCIE_1011) + } + + 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(Quests.BIG_CHOMPY_BIRD_HUNTING) + val chompyStage = chompyBird.getStage(player) + + when (chompyStage) { + in 0 until 100 -> loadFile(FycieChompyBirdDialogue(chompyBird)) + } + + player.dialogueInterpreter.handle(0,0) + + return true + } + + override fun handle(componentId: Int, buttonId: Int) : Boolean { + return true + } +} + +class BugsChompyBirdDialogue(val quest: Quest) : DialogueFile() { + override fun handle(componentId: Int, buttonId: Int) { + when (quest.getStage(player)) { + 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) + } + } + + 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(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(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) + } +} + +class FycieChompyBirdDialogue(val quest: Quest) : DialogueFile() { + override fun handle(componentId: Int, buttonId: Int) { + when (quest.getStage(player)) { + 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) + } + } + + private fun handleIngredientDialogue(player: Player?, buttonId: Int) { + val fycieIngredient = getAttribute(player!!, ChompyBird.ATTR_ING_FYCIE, -1) + when(stage) { + 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) + } +} + +class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { + override fun handle(componentId: Int, buttonId: Int) { + when (quest.getStage(player)) { + 0 -> handleQuestStartDialogue(player, buttonId) + in 20 until 30 -> handleToadsiesDialogue(player, buttonId) + in 30 until 40 -> handlePlaceToadsiesDialogue(player, buttonId) + in 40 until 50 -> handlePlacedBaitDialogue(player, buttonId) + in 50 until 60 -> handleRantzSucksDialogue(player, buttonId) + in 60 until 70 -> handleWaitingForChompyDialogue(player, buttonId) + in 70 until 80 -> handleWaitingForCookedChompy(player, buttonId) + else -> handleImpatienceDialogue(player, buttonId) + } + } + + private fun handleQuestStartDialogue(player: Player?, buttonId: Int) { + when(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(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(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(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) } + } + } + + private fun handleImpatienceDialogue(player: Player?, buttonId: Int) { + val hasArrows = amountInInventory(player!!, Items.OGRE_ARROW_2866) > 0 + + when(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(FacialExpression.ANNOYED, "Well, yes actually, as you asked so nicely. Here you go! Here's your 'stabbers'.") + stage = 100 + } + } + + 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(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(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.") + removeItem(player!!, Item(Items.OGRE_ARROW_2866, 6)) + stage++ + } + + 101 -> { + 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) + } + } + } + + private fun handleToadsiesDialogue(player: Player?, buttonId: Int) { + val hasToads = amountInInventory(player!!, Items.BLOATED_TOAD_2875) > 0 + + when(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!") + stage = 100 + } else { + playerl("No I haven't got the 'fatsy toady' yet!").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 } + 2 -> playerl("What are 'fatsy toadies'?").also { stage = 8 } + 3 -> playerl("Where do we put the fatsy toadies?").also { stage = 9 } + 4 -> playerl("What do you mean 'sneaky..sneaky.. stick da chompy?'").also { stage = 12 } + 5 -> playerl("Ok, thanks.").also { stage = END_DIALOGUE } + } + 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(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) + stage = 0 + } + } + } + + private fun handlePlaceToadsiesDialogue(player: Player?, buttonId: Int) { + 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 + } + + private fun handlePlacedBaitDialogue(player: Player?, buttonId: Int) { + when(stage) { + 0 -> playerl("There you go, I've placed the bait.").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(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 } + } + } + + private fun handleRantzSucksDialogue(player: Player?, buttonId: Int) { + when(stage) { + 0 -> playerl("Hey there, you keep missing the chompy bird.").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(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(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) + stage = END_DIALOGUE + quest.setStage(player, 60) + } + } + } + + private fun handleWaitingForChompyDialogue(player: Player?, buttonId: Int) { + val hasChompyBird = amountInInventory(player!!, Items.RAW_CHOMPY_2876) > 0 + + assignRandomIngredients(player ?: return) + val rantzIngredient = getAttribute(player, ChompyBird.ATTR_ING_RANTZ, -1) + + when(stage) { + 0 -> npcl(FacialExpression.OLD_NORMAL, "Hey You! Got da chompy yet?").also { stage++ } + 1 -> { + if (hasChompyBird) { + playerl("Yep, here's your chompy Bird!") + stage = 100 + } else { + playerl("Not yet!").also { stage++ } + } + } + 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(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(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) + } + } + } + + private fun handleWaitingForCookedChompy(player: Player?, buttonId: Int) { + val hasCookedChompy = amountInInventory(player!!, Items.SEASONED_CHOMPY_2882) > 0 + + when (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.") + stage = 100 + } else { + playerl("Not yet!").also { stage++ } + } + } + 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(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) } + } + } + + private fun assignRandomIngredients(player: Player) { + val possibleIngredients = arrayListOf( + Items.ONION_1957, + Items.DOOGLE_LEAVES_1573, + Items.EQUA_LEAVES_2128, + Items.TOMATO_1982, + Items.POTATO_1942, + Items.CABBAGE_1965 + ) + + setAttribute(player, ChompyBird.ATTR_ING_RANTZ, possibleIngredients.random().also { possibleIngredients.remove(it) }) + setAttribute(player, ChompyBird.ATTR_ING_BUGS, possibleIngredients.random().also { possibleIngredients.remove(it) }) + setAttribute(player, ChompyBird.ATTR_ING_FYCIE, possibleIngredients.random().also { possibleIngredients.remove(it) }) + } +} 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 new file mode 100644 index 000000000..efff701ef --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyBirdNPC.kt @@ -0,0 +1,173 @@ +package content.region.kandarin.feldip.quest.chompybird + +import core.api.* + +import org.rs09.consts.NPCs +import org.rs09.consts.Items +import org.rs09.consts.Animations + +import core.plugin.Initializable +import core.tools.RandomFunction +import core.game.system.task.Pulse + +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.entity.npc.AbstractNPC +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatStyle + +import core.game.world.map.Location +import core.game.world.map.path.Pathfinder +import core.game.interaction.MovementPulse +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.update.flag.context.Animation + +@Initializable +class ChompyBirdNPC : AbstractNPC, InteractionListener { + constructor() : super(NPCs.CHOMPY_BIRD_1550, null, true) + private constructor(id: Int, location: Location) : super(id, location) {} + + var timeToLive = 100 + + override fun init() { + super.init() + val toad = getAttribute("toad", null) + animate(Animation(Animations.CHOMPY_SPAWN_6766)) + + if (toad != null) { + submitIndividualPulse(this, object : MovementPulse(this, toad, Pathfinder.SMART) { + var bufferTicks = 7 + override fun pulse() : Boolean { + if (bufferTicks-- == 0) { + setAttribute("toad_eaten", true) + toad.clear() + return true + } + return false + } + }) + } + } + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + val npc = ChompyBirdNPC(id, location) + npc.isRespawn = false + return npc + } + + override fun getIds() : IntArray { + return intArrayOf(NPCs.CHOMPY_BIRD_1550) + } + + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean) : Boolean { + if (entity !is Player) return true + + var attackable = super.isAttackable(entity, style, message) + val hasOgreBow = inEquipment(entity, Items.OGRE_BOW_2883) + + if (!hasOgreBow) { + sendMessage(entity, "You need an ogre bow to hunt chompy birds.") + } + + return attackable && hasOgreBow + } + + override fun attack(node: Node) {} + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + if (getAttribute("toad_eaten", false) || state.attacker.id == NPCs.RANTZ_1010) { + impactHandler.disabledTicks = 10 + locks.lockInteractions(10) + sendChat("Squawk!") + submitWorldPulse(object : Pulse(2) { + var counter = 0 + override fun pulse() : Boolean { + when (counter++) { + 1 -> animate(Animation(Animations.CHOMPY_FLY_AWAY_6767)) + 2 -> clear() + 3 -> return true + } + return false + } + }) + } else { + sendChat("Screech!") + } + } + + override fun tick() { + if (this.inCombat() && this.skills.lifepoints > 0) { + val newLoc = this.location.transform( + RandomFunction.random(-2,2), + RandomFunction.random(-2,2), + 0 + ) + submitIndividualPulse(this, object : MovementPulse(this, newLoc, Pathfinder.DUMB) { + override fun pulse() : Boolean { return true } + }) + } + super.tick() + } + + override fun handleTickActions() { + if (id == NPCs.CHOMPY_BIRD_1550) { + if (getAttribute("toad_eaten", false)) { + super.handleTickActions() + } + if (timeToLive-- <= 0) { + animate(Animation(Animations.CHOMPY_FLY_AWAY_6767)) + runTask(this, 2) { clear() } + timeToLive = 100 + } + } else { + if (timeToLive-- <= 0) + clear() + } + } + + override fun finalizeDeath(killer: Entity) { + properties.teleportLocation = null + val newSelf = transform(NPCs.CHOMPY_BIRD_1016) + newSelf.isNeverWalks = true + newSelf.isWalks = false + newSelf.walkRadius = 0 + timeToLive = 200 + + this.pulseManager.clear() + this.walkingQueue.reset() + + if (killer is Player) { + sendMessage(killer, "You scratch a notch on your bow for the chompy bird kill.") + val old = killer.getAttribute("chompy-kills", 0) + killer.setAttribute("/save:chompy-kills", old + 1) + ChompyHat.checkForNewRank(killer) + } + } + + override fun defineListeners() { + on(NPCs.CHOMPY_BIRD_1016, IntType.NPC, "pluck") { player, node -> + val bird = node.asNpc() + + if (!bird.getAttribute("plucked", false)) { + 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() + } + + bird.setAttribute("plucked", true) + + return@on true + } + + 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/ChompyHat.kt b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyHat.kt new file mode 100644 index 000000000..7e09f0025 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/ChompyHat.kt @@ -0,0 +1,73 @@ +package content.region.kandarin.feldip.quest.chompybird + +import core.api.* +import core.tools.colorize + +import org.rs09.consts.Items + +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player + +import java.util.* + +enum class ChompyHat(val id: Int, val kills: Int, val rankName: String) { + O_BOWMAN(Items.CHOMPY_BIRD_HAT_2978, 30, "an Ogre Bowman"), + BOWMAN(Items.CHOMPY_BIRD_HAT_2979, 40, "a Bowman"), + O_YEOMAN(Items.CHOMPY_BIRD_HAT_2980, 50, "an Ogre Yeoman"), + YEOMAN(Items.CHOMPY_BIRD_HAT_2981, 70, "a Yeoman"), + O_MARKSMAN(Items.CHOMPY_BIRD_HAT_2982, 95, "an Ogre Marksman"), + MARKSMAN(Items.CHOMPY_BIRD_HAT_2983, 125, "a Marksman"), + O_WOODSMAN(Items.CHOMPY_BIRD_HAT_2984, 170, "an Ogre Woodsman"), + WOODSMAN(Items.CHOMPY_BIRD_HAT_2985, 225, "a Woodsman"), + O_FORESTER(Items.CHOMPY_BIRD_HAT_2986, 300, "an Ogre Forester"), + FORESTER(Items.CHOMPY_BIRD_HAT_2987, 400, "a Forester"), + O_BOWMASTER(Items.CHOMPY_BIRD_HAT_2988, 550, "an Ogre Bowmaster"), + BOWMASTER(Items.CHOMPY_BIRD_HAT_2989, 700, "a Bowmaster"), + O_EXPERT(Items.CHOMPY_BIRD_HAT_2990, 1000, "an Ogre Expert"), + EXPERT(Items.CHOMPY_BIRD_HAT_2991, 1300, "an Expert"), + O_DA(Items.CHOMPY_BIRD_HAT_2992, 1700, "an Ogre Dragon Archer"), + DA(Items.CHOMPY_BIRD_HAT_2993, 2250, "a Dragon Archer"), + EO_DA(Items.CHOMPY_BIRD_HAT_2994, 3000, "an Expert Ogre Dragon Archer"), + E_DA(Items.CHOMPY_BIRD_HAT_2995, 4000, "an Expert Dragon Archer"); + + companion object { + val killMap = values().map { it.kills to it }.toMap() + + fun checkForNewRank(player: Player) { + val kills = getAttribute(player, "chompy-kills", 0) + val newRank = killMap[kills] ?: return + + sendDialogueLines(player, + colorize("%B*** Congratulations! $kills Chompies! ***"), + colorize("%R~ You're ${newRank.rankName} ~") + ) + } + + fun getApplicableHats(player: Player) : List { + val kills = getAttribute(player, "chompy-kills", 0) + val hats = ArrayList() + for (hat in values()) { + if (hat.kills > kills) break + if (inInventory(player, hat.id) || inEquipment(player, hat.id) || inBank(player, hat.id)) continue + hats.add(hat.id) + } + return hats + } + } +} + +class ChompyEquipListener : InteractionListener { + override fun defineListeners() { + ChompyHat.values().forEach { hat -> + onEquip(hat.id) {player, node -> + val kills = getAttribute(player, "chompy-kills", 0) + if (kills < hat.kills) { + sendItemDialogue(player, node.id, "You haven't earned this!") + removeItem(player, node.asItem()) + return@onEquip false + } + return@onEquip 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 new file mode 100644 index 000000000..f0f1ca4ff --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/chompybird/RantzNPC.kt @@ -0,0 +1,44 @@ +package content.region.kandarin.feldip.quest.chompybird + +import core.api.* + +import org.rs09.consts.NPCs + +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 { + constructor() : super(NPCs.RANTZ_1010, null, true) + private constructor(id: Int, location: Location) : super(id, location) + + override fun construct(id: Int, location: Location, vararg objects: Any?) : AbstractNPC { + return RantzNPC(id, location) + } + + override fun getIds() : IntArray { + return intArrayOf(NPCs.RANTZ_1010) + } + + override fun handleTickActions() { + super.handleTickActions() + val chompy = findLocalNPC(this, NPCs.CHOMPY_BIRD_1550) as? ChompyBirdNPC ?: return + + val owner = getAttribute(chompy, "owner", null) ?: return + val quest = owner.questRepository.getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING) + + if (quest.getStage(owner) !in 40..50 || chompy.getAttribute("attacked", false)) return + + sendChat("Hey, dere's da chompy, I's gonna shoot it.") + sendMessage(owner, "Rantz: Hey, dere's da chompy, I's gonna shoot it.") + sendMessage(owner, "Rantz keeps missing the chompy bird.") + sendMessage(owner, "Rantz: Grrr...de'ese arrows are rubbish.") + + attack(chompy) + chompy.setAttribute("attacked", true) + quest.setStage(owner, 50) + } +} 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/kotlin/rs09/game/content/dialogue/AluftGianneJrDialogue.kt b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneJrDialogue.kt similarity index 84% rename from Server/src/main/kotlin/rs09/game/content/dialogue/AluftGianneJrDialogue.kt rename to Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneJrDialogue.kt index 27eb8a084..685abce87 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AluftGianneJrDialogue.kt +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneJrDialogue.kt @@ -1,12 +1,12 @@ -package rs09.game.content.dialogue +package content.region.kandarin.gnomestronghold.dialogue +import content.minigame.gnomecooking.GC_BASE_ATTRIBUTE +import content.minigame.gnomecooking.GC_TUT_PROG import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable -import rs09.game.content.activity.gnomecooking.GC_BASE_ATTRIBUTE -import rs09.game.content.activity.gnomecooking.GC_TUT_PROG @Initializable class AluftGianneJrDialogue(player: Player? = null) : DialoguePlugin(player) { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/AluftGianneSnrDialogue.kt b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneSnrDialogue.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/content/dialogue/AluftGianneSnrDialogue.kt rename to Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneSnrDialogue.kt index 6d57baf8c..8d0c10ecf 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AluftGianneSnrDialogue.kt +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneSnrDialogue.kt @@ -1,8 +1,7 @@ -package rs09.game.content.dialogue +package content.region.kandarin.gnomestronghold.dialogue +import content.minigame.gnomecooking.* import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.item.Item @@ -11,9 +10,8 @@ import core.plugin.Initializable import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.rs09.consts.Items -import rs09.game.content.activity.gnomecooking.* -import rs09.game.world.GameWorld -import rs09.tools.stringtools.colorize +import core.game.world.GameWorld +import core.tools.colorize import java.util.concurrent.TimeUnit val gnomeItems = arrayOf( @@ -24,16 +22,16 @@ Items.SHORT_GREEN_GUY_9510,Items.BLURBERRY_SPECIAL_9520,Items.PINEAPPLE_PUNCH_95 val ALUFT_ALOFT_BOX = Item(Items.ALUFT_ALOFT_BOX_9477) @Initializable -class AluftGianneSnrDialogue(player: Player? = null) : DialoguePlugin(player) { +class AluftGianneSnrDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { var tutorialProgress = -1 var tutorialComplete = false - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return AluftGianneSnrDialogue(player) } override fun npc(vararg messages: String?): Component { - return super.npc(FacialExpression.OLD_NORMAL,*messages) + return super.npc(core.game.dialogue.FacialExpression.OLD_NORMAL,*messages) } override fun open(vararg args: Any?): Boolean { @@ -66,9 +64,9 @@ class AluftGianneSnrDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> player(FacialExpression.HAPPY,"Yes, how do I get started?").also { stage++ } + 0 -> player(core.game.dialogue.FacialExpression.HAPPY,"Yes, how do I get started?").also { stage++ } 1 -> npc("Well first thing's first I need to teach","you how to cook!").also { stage++ } - 2 -> player(FacialExpression.THINKING,"But I already-").also { stage++ } + 2 -> player(core.game.dialogue.FacialExpression.THINKING,"But I already-").also { stage++ } 3 -> npc("Stop whatever it is you're saying, no one knows","how to cook gnome food except gnomes!").also { stage++ } 4 -> player("Alright, go on...").also{stage++} 5 -> npc("Alright, first thing I want you to do is","make me a toad batta. Here's all the","ingredients, now get to work!").also { stage++ } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/BlurberryDialogue.kt b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/BlurberryDialogue.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/content/dialogue/BlurberryDialogue.kt rename to Server/src/main/content/region/kandarin/gnomestronghold/dialogue/BlurberryDialogue.kt index c4125ca62..83c544994 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/BlurberryDialogue.kt +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/BlurberryDialogue.kt @@ -1,13 +1,13 @@ -package rs09.game.content.dialogue +package content.region.kandarin.gnomestronghold.dialogue -import core.game.content.dialogue.DialoguePlugin +import content.minigame.gnomecooking.GC_BASE_ATTRIBUTE +import content.minigame.gnomecooking.GC_TUT_FIN +import content.minigame.gnomecooking.GC_TUT_PROG +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 rs09.game.content.activity.gnomecooking.GC_BASE_ATTRIBUTE -import rs09.game.content.activity.gnomecooking.GC_TUT_FIN -import rs09.game.content.activity.gnomecooking.GC_TUT_PROG @Initializable class BlurberryDialogue(player: Player? = null): DialoguePlugin(player) { 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/java/core/game/content/dialogue/GnomeTrainerPlugin.java b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeTrainerPlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/GnomeTrainerPlugin.java rename to Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeTrainerPlugin.java index 1cc5945e7..8549ba948 100644 --- a/Server/src/main/java/core/game/content/dialogue/GnomeTrainerPlugin.java +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/GnomeTrainerPlugin.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.kandarin.gnomestronghold.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; 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/java/core/game/interaction/city/GnomeStrongholdPlugin.java b/Server/src/main/content/region/kandarin/gnomestronghold/handlers/GnomeStrongholdPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/city/GnomeStrongholdPlugin.java rename to Server/src/main/content/region/kandarin/gnomestronghold/handlers/GnomeStrongholdPlugin.java index 88116c5b6..3356cca1f 100644 --- a/Server/src/main/java/core/game/interaction/city/GnomeStrongholdPlugin.java +++ b/Server/src/main/content/region/kandarin/gnomestronghold/handlers/GnomeStrongholdPlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.city; +package content.region.kandarin.gnomestronghold.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.impl.ForceMovement; @@ -11,7 +11,7 @@ import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; import core.game.world.map.path.Pathfinder; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/content/activity/guild/FishingGuild.java b/Server/src/main/content/region/kandarin/guilds/FishingGuild.java similarity index 90% rename from Server/src/main/java/core/game/content/activity/guild/FishingGuild.java rename to Server/src/main/content/region/kandarin/guilds/FishingGuild.java index bff6904d3..673f6158d 100644 --- a/Server/src/main/java/core/game/content/activity/guild/FishingGuild.java +++ b/Server/src/main/content/region/kandarin/guilds/FishingGuild.java @@ -1,10 +1,10 @@ -package core.game.content.activity.guild; +package content.region.kandarin.guilds; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.Skillcape; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialoguePlugin; +import core.game.global.Skillcape; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -30,12 +30,11 @@ public final class FishingGuild extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - final int id = ((Scenery) node).getId(); switch (option) { case "open": - switch (id) { + switch (node.getId()) { case 2025: - if (player.getSkills().getStaticLevel(Skills.FISHING) < 68 && player.getLocation().withinDistance(new Location(2611, 3394, 0))) { + if (player.getSkills().getDynamicLevels()[Skills.FISHING] < 68 && player.getLocation().withinDistance(new Location(2611, 3394, 0))) { player.getDialogueInterpreter().sendDialogues(308, null, "Hello, I'm afraid only the top fishers are allowed to use", "our premier fishing facilities."); return true; } diff --git a/Server/src/main/java/core/game/content/activity/guild/WizardGuildPlugin.java b/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java similarity index 81% rename from Server/src/main/java/core/game/content/activity/guild/WizardGuildPlugin.java rename to Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java index 132a399c3..1322bf19c 100644 --- a/Server/src/main/java/core/game/content/activity/guild/WizardGuildPlugin.java +++ b/Server/src/main/content/region/kandarin/guilds/WizardGuildPlugin.java @@ -1,12 +1,12 @@ -package core.game.content.activity.guild; +package content.region.kandarin.guilds; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.Skillcape; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialoguePlugin; +import core.game.global.Skillcape; +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; @@ -16,7 +16,8 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.content.global.travel.EssenceTeleport; +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(); @@ -61,7 +61,7 @@ public final class WizardGuildPlugin extends OptionHandler { switch (id) { case 1600: case 1601: - if (ContentAPI.getDynLevel(player, Skills.MAGIC) < 66) { + if (getDynLevel(player, Skills.MAGIC) < 66) { player.getDialogueInterpreter().sendDialogue("You need a Magic level of at least 66 to enter."); return true; } @@ -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/java/core/game/interaction/object/BeehivePlugin.java b/Server/src/main/content/region/kandarin/handlers/BeehivePlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/BeehivePlugin.java rename to Server/src/main/content/region/kandarin/handlers/BeehivePlugin.java index b2edb87a7..3308b3e2a 100644 --- a/Server/src/main/java/core/game/interaction/object/BeehivePlugin.java +++ b/Server/src/main/content/region/kandarin/handlers/BeehivePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.kandarin.handlers; import core.cache.def.impl.SceneryDefinition; import org.rs09.consts.Items; @@ -8,7 +8,7 @@ import core.game.node.entity.combat.ImpactHandler; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.plugin.Initializable; 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/java/core/game/interaction/object/SinclairFlourBarrelPlugin.java b/Server/src/main/content/region/kandarin/handlers/SinclairFlourBarrelPlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/object/SinclairFlourBarrelPlugin.java rename to Server/src/main/content/region/kandarin/handlers/SinclairFlourBarrelPlugin.java index d272cb9c0..a3b5ecd46 100644 --- a/Server/src/main/java/core/game/interaction/object/SinclairFlourBarrelPlugin.java +++ b/Server/src/main/content/region/kandarin/handlers/SinclairFlourBarrelPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.kandarin.handlers; import core.cache.def.impl.SceneryDefinition; import org.rs09.consts.Items; @@ -12,7 +12,7 @@ import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** @@ -59,7 +59,7 @@ public final class SinclairFlourBarrelPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.forId(26122).getHandlers().put("option:take from", this); - PluginManager.definePlugin(new FlourHandler()); + ClassScanner.definePlugin(new FlourHandler()); return this; } diff --git a/Server/src/main/content/region/kandarin/handlers/StoneCircleZone.kt b/Server/src/main/content/region/kandarin/handlers/StoneCircleZone.kt new file mode 100644 index 000000000..c276acc83 --- /dev/null +++ b/Server/src/main/content/region/kandarin/handlers/StoneCircleZone.kt @@ -0,0 +1,48 @@ +package content.region.kandarin.handlers + +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.plugin.Initializable +import core.plugin.Plugin +import core.api.* + +/** + * Handles the stone circle zone south of Ardougne. + * @author Kya + */ +@Initializable +class StoneCircleZone : MapZone("stone circle", true), Plugin { + /** + * Constructs a new `StoneCircleZone` `Object`. + */ + override fun configure() { + register(ZoneBorders(2558, 3219, 2559, 3225)) + register(ZoneBorders(2560, 3219, 2564, 3225)) + } + + override fun fireEvent(identifier: String?, vararg args: Any?): Any? { + return null + } + + override fun newInstance(arg: Any?): Plugin { + ZoneBuilder.configure(this) + return this + } + + override fun enter(e: Entity): Boolean { + if (e is Player) { + setVarbit(e, 4833, 1) + } + return super.enter(e) + } + + override fun leave(e: Entity, logout: Boolean): Boolean { + if (!logout && e is Player) { + setVarbit(e, 4833, 0) + } + return super.leave(e, logout) + } +} diff --git a/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt b/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt new file mode 100644 index 000000000..b1233b698 --- /dev/null +++ b/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt @@ -0,0 +1,101 @@ +package content.region.kandarin.pisc.dialogue + +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.game.node.item.Item +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +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 + } + } + + 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 new file mode 100644 index 000000000..b926d7fde --- /dev/null +++ b/Server/src/main/content/region/kandarin/pisc/dialogue/MathiasFlaconryDialogue.java @@ -0,0 +1,294 @@ +package content.region.kandarin.pisc.dialogue; + +import core.game.container.impl.EquipmentContainer; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +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; +import org.rs09.consts.Items; + +/** + * Handles the MathiasFlaconryDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class MathiasFlaconryDialogue extends DialoguePlugin { + + public MathiasFlaconryDialogue() { + } + + public MathiasFlaconryDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 5093 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 90: + interpreter.sendOptions("Select an Option", "Ok, that seems reasonable.", "I'm not interested then, thanks."); + stage = 91; + break; + case 91: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok, that seems reasonable."); + stage = 95; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm not interested then, thanks."); + stage = 323; + break; + + } + break; + case 323: + 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)) { + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "You already have a falcon!"); + stage = 99; + 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, 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; + } + if (player.getInventory().contains(995, 500)) { + player.getInventory().remove(new Item(995, 500)); + player.getEquipment().add(new Item(10024), true, false); + interpreter.sendDialogue("The falconer gives you a large leather glove and brings one of the", "smaller birds over to land on it."); + stage = 97; + } else { + end(); + player.getPacketDispatch().sendMessage("You need 500 gold coins."); + } + break; + case 97: + 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: + end(); + break; + case 500: + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Greetings. Can I help you at all?"); + stage = 501; + break; + case 501: + 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 = 801; + break; + 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; + break; + case 901: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, null, "Yes, please."); + stage = 920; + break; + case 2: + end(); + break; + } + break; + case 920: + player.getBank().remove(FALCON, new Item(10023)); + player.getInventory().remove(new Item(10023), FALCON); + player.getEquipment().add(FALCON, true, false); + interpreter.sendDialogue("The falconer gives you a large leather glove and brings one of the", "smaller birds over to land on it."); + stage = 97; + break; + 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; + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new MathiasFlaconryDialogue(player); + } + + private final Item FALCON = new Item(10024); + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + boolean quick = false; + if (player.getEquipment().contains(10023, 1)) { + interpreter.sendDialogues(5093, null, "Oh, it looks like you've lost your falcon.", "Would you like a new one?"); + 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) { + interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "If you wish to try falconry, I request a small fee. How", "does 500 gold coins sound?"); + stage = 90; + return true; + } + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there."); + stage = 500; + return true; + } +} diff --git a/Server/src/main/content/region/kandarin/pisc/handlers/SeaweedNetHandler.kt b/Server/src/main/content/region/kandarin/pisc/handlers/SeaweedNetHandler.kt new file mode 100644 index 000000000..4ae4df149 --- /dev/null +++ b/Server/src/main/content/region/kandarin/pisc/handlers/SeaweedNetHandler.kt @@ -0,0 +1,52 @@ +package content.region.kandarin.pisc.handlers + +import core.api.* +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 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, Quests.SWAN_SONG)) + { + sendMessage(player, "You must complete Swan Song first.") + } + else if (!hasSpaceFor(player, Item(Items.SEAWEED_401, 1))) { + sendMessage(player, "You do not have space in your inventory.") + } + else { + submitIndividualPulse(player, object : Pulse() { + private var tick = 0 + override fun pulse(): Boolean { + when(tick++){ + 0 -> { + animate(player, Animations.HUMAN_BURYING_BONES_827) // no idea what animation is supposed to be used, can't even find a video of this content + } + 1 -> { + if (addItem(player, Items.SEAWEED_401)) { + SceneryBuilder.replace(node.asScenery(), Scenery(EMPTY_NET, node.location, node.asScenery().rotation),5) // osrs wiki says 3 second respawn timer + } + return true + } + } + return false + } + }) + } + return@on true + } + } + + companion object { + private const val NET = 14973 + private const val EMPTY_NET = 14972 + } +} diff --git a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/CaptainLawgofDialogue.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/members/dwarfcannon/CaptainLawgofDialogue.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofDialogue.java index d1be4ca8f..ae586a3f3 100644 --- a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/CaptainLawgofDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofDialogue.java @@ -1,10 +1,13 @@ -package core.game.content.quest.members.dwarfcannon; +package content.region.kandarin.quest.dwarfcannon; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/quest/members/dwarfcannon/CaptainLawgofNPC.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofNPC.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/members/dwarfcannon/CaptainLawgofNPC.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofNPC.java index ea462387d..36268288f 100644 --- a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/CaptainLawgofNPC.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/CaptainLawgofNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.dwarfcannon; +package content.region.kandarin.quest.dwarfcannon; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/DwarfCannon.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java similarity index 81% rename from Server/src/main/java/core/game/content/quest/members/dwarfcannon/DwarfCannon.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java index 20da23932..8268ced31 100644 --- a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/DwarfCannon.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java @@ -1,24 +1,21 @@ -package core.game.content.quest.members.dwarfcannon; +package content.region.kandarin.quest.dwarfcannon; 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.Initializable; -import rs09.plugin.PluginManager; +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,22 +35,22 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugin(new LollkDialogue()); - PluginManager.definePlugin(new NulodionDialogue()); - PluginManager.definePlugin(new CaptainLawgofNPC()); - PluginManager.definePlugin(new CannonBallPlugin()); - PluginManager.definePlugin(new CaptainLawgofDialogue()); - PluginManager.definePlugin(new DwarfCannonPlugin()); + ClassScanner.definePlugin(new LollkDialogue()); + ClassScanner.definePlugin(new NulodionDialogue()); + ClassScanner.definePlugin(new CaptainLawgofNPC()); + ClassScanner.definePlugin(new CaptainLawgofDialogue()); + ClassScanner.definePlugin(new DwarfCannonPlugin()); return this; } @@ -65,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); @@ -97,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 @@ -118,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/java/core/game/content/quest/members/dwarfcannon/DwarfCannonPlugin.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannonPlugin.java similarity index 86% rename from Server/src/main/java/core/game/content/quest/members/dwarfcannon/DwarfCannonPlugin.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannonPlugin.java index 6a9646129..876f9cfda 100644 --- a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/DwarfCannonPlugin.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannonPlugin.java @@ -1,12 +1,13 @@ -package core.game.content.quest.members.dwarfcannon; +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; import core.game.component.ComponentPlugin; -import core.game.content.dialogue.DialogueAction; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialogueAction; +import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -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 rs09.game.system.SystemLogger; +import core.tools.Log; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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("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/java/core/game/content/quest/members/dwarfcannon/LollkDialogue.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/LollkDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/members/dwarfcannon/LollkDialogue.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/LollkDialogue.java index c1c00777d..08cedded9 100644 --- a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/LollkDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/LollkDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.dwarfcannon; +package content.region.kandarin.quest.dwarfcannon; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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/java/core/game/content/quest/members/dwarfcannon/NulodionDialogue.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/dwarfcannon/NulodionDialogue.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionDialogue.java index a2c1accd9..05ac29b60 100644 --- a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/NulodionDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionDialogue.java @@ -1,11 +1,14 @@ -package core.game.content.quest.members.dwarfcannon; +package content.region.kandarin.quest.dwarfcannon; -import core.game.content.dialogue.DialoguePlugin; +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 new file mode 100644 index 000000000..9b7d2363c --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java @@ -0,0 +1,288 @@ +package content.region.kandarin.quest.dwarfcannon.dmc; + +import core.api.LogoutListener; +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.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.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.RegionManager; +import core.game.world.map.zone.ZoneRestriction; +import core.game.world.update.flag.context.Animation; +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. + * @author Emperor + */ +public final class DMCHandler implements LogoutListener { + + /** + * The player. + */ + private final Player player; + + /** + * The cannon object. + */ + private Scenery cannon; + + /** + * The amount of cannonballs loaded. + */ + private int cannonballs; + + /** + * The current direction. + */ + private DMCRevolution direction = DMCRevolution.NORTH; + + private CannonTimer timer; + + public DMCHandler() { + this.player = null; + } + + /** + * Constructs a new {@code DMCHandler} {@code Object} + * @param player The player owning the cannon. + */ + public DMCHandler(final Player player) { + this.player = player; + } + + /** + * Rotates the cannon. + * @return {@code True} if the cannon should stop rotating. + */ + public boolean rotate() { + if (cannonballs < 1) { + player.getPacketDispatch().sendMessage("Your cannon has run out of ammo!"); + return false; + } + player.getPacketDispatch().sendSceneryAnimation(cannon, Animation.create(direction.getAnimationId())); + Location l = cannon.getLocation().transform(1, 1, 0); + 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(); + playGlobalAudio(l, Sounds.MCANNON_FIRE_1667); + cannonballs--; + int hit = 0; + if (player.getSwingHandler(false).isAccurateImpact(player, npc, CombatStyle.RANGE, 1.2, 1.0)) { + hit = RandomFunction.getRandom(30); + } + player.getSkills().addExperience(Skills.RANGE, hit * 2); + npc.getImpactHandler().manualHit(player, hit, HitsplatType.NORMAL, (int) Math.ceil(l.getDistance(npc.getLocation()) * 0.3)); + npc.attack(player); + break; + } + } + return true; + } + + /** + * Starts rotating the cannon. + */ + public void startFiring() { + if (cannon == null || !cannon.isActive()) { + player.getPacketDispatch().sendMessage("You don't have a cannon active."); + return; + } + if (timer.isFiring()) { + timer.setFiring(false); + return; + } + if (cannonballs < 1) { + int amount = player.getInventory().getAmount(new Item(2)); + if (amount < 1) { + player.getPacketDispatch().sendMessage("Your cannon is out of cannonballs."); + return; + } + int toUse = 30 - cannonballs; + if(toUse > amount){ + toUse = amount; + } + if(toUse > 0){ + cannonballs = toUse; + player.getPacketDispatch().sendMessage("You load the cannon with " + toUse + " cannonballs."); + player.getInventory().remove(new Item(2, toUse)); + } else { + player.sendMessage("Your cannon is already fully loaded."); + } + } + timer.setFiring(true); + } + + /** + * Makes the cannon explode. + */ + public void explode(boolean decay) { + if (!cannon.isActive()) { + return; + } + player.sendMessage("Your cannon has " + (decay ? "decayed" : "been destroyed") + "!"); + for (Player p : RegionManager.getLocalPlayers(player)) { + p.getPacketDispatch().sendPositionedGraphic(189, 0, 1, cannon.getLocation()); + } + clear(false); + } + + /** + * Constructs the cannon. + */ + public static void construct(final Player player) { + final Location spawn = RegionManager.getSpawnLocation(player, new Scenery(6, player.getLocation())); + if (spawn == null) { + player.getPacketDispatch().sendMessage("There's not enough room for your cannon."); + return; + } + if (player.getZoneMonitor().isRestricted(ZoneRestriction.CANNON)) { + player.getPacketDispatch().sendMessage("You can't set up a cannon here."); + return; + } + final DMCHandler handler = new DMCHandler(player); + player.setAttribute("dmc", handler); + player.getPulseManager().clear(); + player.getWalkingQueue().reset(); + player.lock(9); + player.faceLocation(spawn.transform(Direction.NORTH_EAST)); + GameWorld.getPulser().submit(new Pulse(2, player) { + int count = 0; + Scenery object = null; + + @Override + public boolean pulse() { + player.animate(Animation.create(827)); + if (!player.getInventory().remove(new Item(6 + (count * 2)))) { + for (int i = count - 1; i >= 0; i--) { + player.getInventory().add(new Item(6 + (i * 2))); + } + if (object != null) { + SceneryBuilder.remove(object); + } + return true; + } + switch (count) { + case 0: + object = SceneryBuilder.add(new Scenery(7, spawn)); + player.getPacketDispatch().sendMessage("You place the cannon base on the ground."); + break; + case 1: + player.getPacketDispatch().sendMessage("You add the stand."); + break; + case 2: + player.getPacketDispatch().sendMessage("You add the barrels."); + break; + case 3: + 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; + } + playGlobalAudio(player.getLocation(), Sounds.MCANNON_SETUP_2876); + if(count != 0) { + SceneryBuilder.remove(object); + SceneryBuilder.add(object = object.transform(object.getId() + 1)); + } + return ++count == 4; + } + }); + } + + /** + * Configures the cannon. + * @param cannon The cannon. + */ + public void configure(Scenery cannon) { + this.cannon = cannon; + } + + @Override + public void logout(@NotNull Player player) { + if(player.getAttribute("dmc") != null) + { + DMCHandler handler = player.getAttribute("dmc"); + handler.clear(false); + } + } + + /** + * Clears the cannon. + * @param pickup If the cannon is getting picked up. + */ + public void clear(boolean pickup) { + SceneryBuilder.remove(cannon); + player.removeAttribute("dmc"); + if (!pickup) { + player.getSavedData().getActivityData().setLostCannon(true); + return; + } + for (int i = 0; i < 4; i++) { + player.getInventory().add(new Item(12 - (i * 2))); + } + if (cannonballs > 0) { + player.getInventory().add(new Item(2, cannonballs)); + cannonballs = 0; + } + } + + /** + * Gets the player. + * @return the player + */ + public Player getPlayer() { + return player; + } + + /** + * Gets the cannon. + * @return the cannon + */ + public Scenery getCannon() { + return cannon; + } + + /** + * Sets the cannon. + * @param cannon the cannon to set. + */ + public void setCannon(Scenery cannon) { + this.cannon = cannon; + } + + /** + * Gets the cannonballs. + * @return the cannonballs + */ + public int getCannonballs() { + return cannonballs; + } + + /** + * Sets the bacannonballs. + * @param cannonballs the cannonballs to set. + */ + public void setCannonballs(int cannonballs) { + this.cannonballs = cannonballs; + } + +} 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/java/core/game/interaction/object/dmc/DMCRevolution.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCRevolution.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/dmc/DMCRevolution.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCRevolution.java index 57af5b3de..ff9f8ceee 100644 --- a/Server/src/main/java/core/game/interaction/object/dmc/DMCRevolution.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCRevolution.java @@ -1,4 +1,4 @@ -package core.game.interaction.object.dmc; +package content.region.kandarin.quest.dwarfcannon.dmc; /** * The DMC aiming directions. diff --git a/Server/src/main/java/core/game/interaction/object/dmc/DMCZone.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCZone.java similarity index 95% rename from Server/src/main/java/core/game/interaction/object/dmc/DMCZone.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCZone.java index 193d31d84..7f1327e24 100644 --- a/Server/src/main/java/core/game/interaction/object/dmc/DMCZone.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCZone.java @@ -1,4 +1,4 @@ -package core.game.interaction.object.dmc; +package content.region.kandarin.quest.dwarfcannon.dmc; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; diff --git a/Server/src/main/java/core/game/interaction/object/dmc/DwarfMultiCannonPlugin.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DwarfMultiCannonPlugin.java similarity index 93% rename from Server/src/main/java/core/game/interaction/object/dmc/DwarfMultiCannonPlugin.java rename to Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DwarfMultiCannonPlugin.java index 9b7e1c4e6..0fbe40125 100644 --- a/Server/src/main/java/core/game/interaction/object/dmc/DwarfMultiCannonPlugin.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DwarfMultiCannonPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object.dmc; +package content.region.kandarin.quest.dwarfcannon.dmc; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; @@ -11,7 +11,8 @@ import core.game.node.entity.player.info.Rights; import core.game.node.item.Item; import core.plugin.Plugin; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Handles the Dwarf multi-cannon. @@ -62,7 +63,7 @@ public final class DwarfMultiCannonPlugin extends OptionHandler { } }); - PluginManager.definePlugin(new DMCZone()); + ClassScanner.definePlugin(new DMCZone()); return this; } @@ -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/java/core/game/content/quest/members/fishingcontest/BigDaveDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/BigDaveDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/BigDaveDialogue.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/BigDaveDialogue.java index 9623d5585..d677eb5b1 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/BigDaveDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/BigDaveDialogue.java @@ -1,9 +1,9 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; /** diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/BonzoDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/BonzoDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/BonzoDialogue.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/BonzoDialogue.java index f6d6665a9..8ed55ae9e 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/BonzoDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/BonzoDialogue.java @@ -1,11 +1,12 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.node.entity.player.link.quest.QuestRepository; import core.game.node.item.Item; import core.plugin.Initializable; import core.game.node.entity.player.Player; -import core.game.content.activity.ActivityManager; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/quest/members/fishingcontest/DwarfDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/DwarfDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/DwarfDialogue.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/DwarfDialogue.java index 1f3b66bc0..6e56d58e5 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/DwarfDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/DwarfDialogue.java @@ -1,11 +1,12 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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/java/core/game/content/quest/members/fishingcontest/FenceInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/FenceInteraction.java similarity index 90% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/FenceInteraction.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/FenceInteraction.java index 649a16186..bc6f158d8 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/FenceInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/FenceInteraction.java @@ -1,16 +1,17 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.interaction.MovementPulse; import core.game.node.Node; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; @Initializable public class FenceInteraction extends PluginInteraction { @@ -29,7 +30,7 @@ public class FenceInteraction extends PluginInteraction { GameWorld.getPulser().submit(new SqueezePulse(player)); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContest.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContest.java new file mode 100644 index 000000000..9e01f552e --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContest.java @@ -0,0 +1,88 @@ +package content.region.kandarin.quest.fishingcontest; + +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.Initializable; +import core.game.node.entity.skill.Skills; +import content.data.Quests; + +@Initializable +public class FishingContest extends Quest { + 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); + public static final Item RAW_GIANT_CARP = new Item(338); + public static final Item GIANT_CARP = new Item(337); + public static final Item FISHING_TROPHY = new Item(26); + 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 = 12; + super.drawJournal(player, stage); + + 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); + } + } + + @Override + public void finish(Player player) { + 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 Tunnel shortcut",ln); + player.removeAttribute("fishing_contest:garlic"); + player.removeAttribute("fishing_contest:won"); + player.removeAttribute("fishing_contest:pass-shown"); + player.getSkills().addExperience(Skills.FISHING,2437); + } + + @Override + public Quest newInstance(Object object) { + return this; + } +} diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingContestCutscene.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContestCutscene.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingContestCutscene.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContestCutscene.java index e85409c21..b5ea21118 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingContestCutscene.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingContestCutscene.java @@ -1,19 +1,20 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.interaction.MovementPulse; import core.game.node.entity.Entity; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; import core.game.world.map.zone.ZoneBorders; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; @Initializable public class FishingContestCutscene extends CutscenePlugin { @@ -140,7 +141,7 @@ public class FishingContestCutscene extends CutscenePlugin { player.getWalkingQueue().setRunDisabled(true); return true; } - }, "movement"); + }, PulseType.STANDARD); break; case 12: stranger.sendChat("You're switching with me. Go."); @@ -152,7 +153,7 @@ public class FishingContestCutscene extends CutscenePlugin { player.getDialogueInterpreter().sendDialogue("Your spot is now by the pipes."); return true; } - }, "movement"); + }, PulseType.STANDARD); break; case 22: stranger.getPulseManager().run(new MovementPulse(stranger, base.getLocation().transform(7, 43, 0)) { @@ -160,7 +161,7 @@ public class FishingContestCutscene extends CutscenePlugin { public boolean pulse() { return true; } - }, "movement"); + },PulseType.STANDARD); GameWorld.getPulser().submit(new FishingPulse()); return true; } diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingSpotInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingSpotInteraction.java similarity index 87% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingSpotInteraction.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/FishingSpotInteraction.java index c425e1971..c9449afdb 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingSpotInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/FishingSpotInteraction.java @@ -1,15 +1,16 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.interaction.MovementPulse; import core.game.interaction.Option; +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.plugin.Initializable; import core.plugin.Plugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.dialogue.FacialExpression; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; @Initializable public class FishingSpotInteraction extends PluginInteraction { @@ -24,7 +25,7 @@ public class FishingSpotInteraction extends PluginInteraction { player.getDialogueInterpreter().sendDialogues(3677, FacialExpression.NEUTRAL, "I think you will find that is","my spot."); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } else if (npc_loc.equals(2630, 3435, 0)){ player.getPulseManager().run(new MovementPulse(player, npc.getLocation().transform(1, 0, 0)) { @@ -33,7 +34,7 @@ public class FishingSpotInteraction extends PluginInteraction { player.getDialogueInterpreter().sendDialogues(225, FacialExpression.NEUTRAL, "Hey, you need to pay to enter the", "competition first! Only 5gp entrance fee!"); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } else if (npc_loc.equals(Location.create(2632, 3427, 0))){ player.getPulseManager().run(new MovementPulse(player, npc.getLocation().transform(1, 0, 0)) { @@ -42,7 +43,7 @@ public class FishingSpotInteraction extends PluginInteraction { player.getDialogueInterpreter().sendDialogues(228, FacialExpression.NEUTRAL, "I think you will find that is my spot."); return true; } - }, "movement"); + },PulseType.STANDARD); return true; } else if(npc_loc.equals(Location.create(2627,3415,0))) { player.getPulseManager().run(new MovementPulse(player, npc.getLocation().transform(1, 0, 0)) { @@ -51,7 +52,7 @@ public class FishingSpotInteraction extends PluginInteraction { player.getDialogueInterpreter().sendDialogues(228, FacialExpression.NEUTRAL, "I think you will find that is my spot."); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } } diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/ForesterDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/ForesterDialogue.java similarity index 88% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/ForesterDialogue.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/ForesterDialogue.java index ae49b472e..0815774f0 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/ForesterDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/ForesterDialogue.java @@ -1,9 +1,9 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; /** diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/GarlicPipeInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/GarlicPipeInteraction.java similarity index 87% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/GarlicPipeInteraction.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/GarlicPipeInteraction.java index 164c0a7bd..5771f2dab 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/GarlicPipeInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/GarlicPipeInteraction.java @@ -1,8 +1,9 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.interaction.MovementPulse; import core.game.interaction.NodeUsageEvent; import core.game.node.Node; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.node.scenery.Scenery; @@ -10,8 +11,9 @@ import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; import org.rs09.consts.Items; -import core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; +import content.data.Quests; @Initializable public class GarlicPipeInteraction extends PluginInteraction { @@ -25,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() { @@ -39,7 +41,7 @@ public class GarlicPipeInteraction extends PluginInteraction { player.setAttribute("fishing_contest:garlic",true); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } } @@ -57,7 +59,7 @@ public class GarlicPipeInteraction extends PluginInteraction { player.getDialogueInterpreter().sendDialogue("This is the pipe I stuffed that garlic into."); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } } diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/GateInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/GateInteraction.java similarity index 83% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/GateInteraction.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/GateInteraction.java index 08f706856..7a79ca976 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/GateInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/GateInteraction.java @@ -1,13 +1,15 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.interaction.MovementPulse; import core.game.node.Node; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.player.Player; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; -import core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; +import content.data.Quests; @Initializable public class GateInteraction extends PluginInteraction { @@ -26,18 +28,18 @@ 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."); } return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } else { if(!player.getInventory().containsItem(FishingContest.FISHING_ROD)){ @@ -55,7 +57,7 @@ public class GateInteraction extends PluginInteraction { player.getDialogueInterpreter().sendDialogue("This gate is locked."); return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } return false; diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/JoshuaDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/JoshuaDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/JoshuaDialogue.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/JoshuaDialogue.java index 1bdf638a6..40a26dd25 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/JoshuaDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/JoshuaDialogue.java @@ -1,9 +1,9 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; /** diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/MorrisDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/MorrisDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/MorrisDialogue.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/MorrisDialogue.java index af0684b99..1c145b1c3 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/MorrisDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/MorrisDialogue.java @@ -1,10 +1,10 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; @Initializable public final class MorrisDialogue extends DialoguePlugin { diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/SinisterStrangerDialogue.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/SinisterStrangerDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/SinisterStrangerDialogue.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/SinisterStrangerDialogue.java index ef09b148a..bd3e90e63 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/SinisterStrangerDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/SinisterStrangerDialogue.java @@ -1,9 +1,9 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; /** * Handles the SinisterStrangerDialogue dialogue. diff --git a/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java new file mode 100644 index 000000000..97daa72b1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java @@ -0,0 +1,60 @@ +package content.region.kandarin.quest.fishingcontest; + +import core.game.interaction.MovementPulse; +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.node.scenery.Scenery; +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(Quests.FISHING_CONTEST)) { + Scenery object = node.asScenery(); + switch (object.getId()) { + case 57: + handleStairs(player,232,object); + return true; + case 55: + handleStairs(player,3679,object); + return true; + } + } + return false; + } + + private void handleStairs(Player player, int npc_id, Scenery object){ + player.getPulseManager().run(new MovementPulse(player,object.getLocation().transform(0,2,0)) { + @Override + public boolean pulse() { + 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); + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + setIds(new int[]{57,55}); + PluginInteractionManager.register(this, PluginInteractionManager.InteractionType.OBJECT); + return this; + } + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } +} diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/VineInteraction.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/VineInteraction.java similarity index 80% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/VineInteraction.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/VineInteraction.java index f64cddca9..17ceb597a 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/VineInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/VineInteraction.java @@ -1,14 +1,16 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; import core.game.interaction.MovementPulse; import core.game.node.Node; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; -import core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; +import core.game.interaction.PluginInteraction; +import core.game.interaction.PluginInteractionManager; +import content.data.Quests; @Initializable public class VineInteraction extends PluginInteraction { @@ -23,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() { @@ -36,7 +38,7 @@ public class VineInteraction extends PluginInteraction { } return true; } - }, "movement"); + }, PulseType.STANDARD); return true; } } diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/grandpaJack.java b/Server/src/main/content/region/kandarin/quest/fishingcontest/grandpaJack.java similarity index 98% rename from Server/src/main/java/core/game/content/quest/members/fishingcontest/grandpaJack.java rename to Server/src/main/content/region/kandarin/quest/fishingcontest/grandpaJack.java index cb2198ea6..2a89313ac 100644 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/grandpaJack.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/grandpaJack.java @@ -1,10 +1,10 @@ -package core.game.content.quest.members.fishingcontest; +package content.region.kandarin.quest.fishingcontest; 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.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; /** * Represents the dialogue plugin used for the grandpaJack npc. 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 new file mode 100644 index 000000000..afc028e55 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/BallistaDialogue.kt @@ -0,0 +1,47 @@ +package content.region.kandarin.quest.tree + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE + +class BallistaDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) + if (questStage > 30) { + when (stage) { + 0 -> sendDialogue(player!!, "The Khazard stronghold has already been breached.").also { stage = END_DIALOGUE } + } + } else if (questStage != 30) return + + val tracker1 = getAttribute(player!!, "/save:treegnome:tracker1", false) + val tracker2 = getAttribute(player!!, "/save:treegnome:tracker2", false) + val tracker3 = getAttribute(player!!, "/save:treegnome:tracker3", false) + + if(tracker1 && tracker2 && tracker3) { + when(stage){ + 0 -> playerl("That tracker gnome was a bit vague about the x coordinate! What could it be?").also { stage++ } + 1 -> interpreter!!.sendOptions("Enter the x-coordinate of the stronghold", + "0001","0002","0003","0004").also { stage++ } + 2 -> { + sendDialogue(player!!, "You entered the height and y coordinates you got from the tracker gnomes.") + val answer = getAttribute(player!!,"treegnome:xcoord",1) + 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!!, Quests.TREE_GNOME_VILLAGE, 31) + } + else -> sendDialogue(player!!, "The huge spear completely misses the Khazard stronghold!") + } + stage = END_DIALOGUE + } + } + } else { + when(stage){ + 0 -> sendDialogue(player!!,"You enter some random coordinates.").also { stage++ } + 1 -> sendDialogue(player!!,"The huge spear completely misses the Khazard stronghold!").also { stage++ } + 2 -> playerl("I've got no hope of hitting the stronghold without knowing any of the coordinates. Maybe I should ask Montai's tracker gnomes for more coordinates.").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/CommanderMontaiDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/CommanderMontaiDialogue.kt new file mode 100644 index 000000000..e758479a2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/CommanderMontaiDialogue.kt @@ -0,0 +1,108 @@ +package content.region.kandarin.quest.tree + +import content.data.Quests +import core.api.* +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE + +class CommanderMontaiDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) + if (questStage == 10) { + when(stage) { + 0 -> playerl("Hello.").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(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(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(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 + } + } + } else if (questStage == 20) { + if(inInventory(player!!, Items.LOGS_1511,6)){ + when(stage) { + 0 -> playerl("Hello.").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!!, 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(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(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(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(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(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(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(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(FacialExpression.OLD_HAPPY, "Incredible, for a human you really are something.").also { stage++ } + 2 -> playerl("Thanks... I think!").also { stage++ } + 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(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(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 } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/ElkoyDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/ElkoyDialogue.kt new file mode 100644 index 000000000..708033a0d --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/ElkoyDialogue.kt @@ -0,0 +1,171 @@ +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 +import core.game.system.task.Pulse +import core.game.world.map.Location +import org.rs09.consts.Components +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import content.region.kandarin.quest.tree.TreeGnomeVillage.Companion.mazeEntrance +import content.region.kandarin.quest.tree.TreeGnomeVillage.Companion.mazeVillage +import core.game.dialogue.FacialExpression +import core.game.world.GameWorld.Pulser +import core.tools.END_DIALOGUE + +class ElkoyDialogue : DialogueFile(){ + fun travelCutscene(player: Player, location: Location){ + sendDialogue(player,"Elkoy escorts you through the maze...") + 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,location) + player.interfaceManager.closeOverlay() + player.interfaceManager.openOverlay(Component(Components.FADE_FROM_BLACK_170)) + return true + } + } + count++ + return false + } + }) + } + override fun handle(componentID: Int, buttonID: Int) { + 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(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(FacialExpression.OLD_NORMAL, "If you like, but please take the orb to King Bolren soon.").also { stage++ } + 5 -> { + travelCutscene(player!!, mazeEntrance) + stage++ + } + 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(FacialExpression.OLD_NORMAL, "You're back! And the orb?").also { stage++ } + 2 -> playerl("I have it here.").also { stage++ } + 3 -> { + if(locY > 3161){ + 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(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++ } + 5 -> when(buttonID) { + 1 -> playerl("Yes please.").also { stage = 7 } + 2 -> playerl("No thanks Elkoy.").also { stage = 6 } + } + 6 -> npcl(FacialExpression.OLD_NORMAL, "Ok then, take care.").also { stage = END_DIALOGUE } + 7 -> travelCutscene(player!!, mazeVillage).also { stage++ } + 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(FacialExpression.OLD_HAPPY, "You truly are a hero.").also { stage++ } + 2 -> playerl("Thanks.").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(FacialExpression.OLD_NORMAL, "Ok then, take care.").also { stage = END_DIALOGUE } + 8 -> { + if(followLocation == "village") { + travelCutscene(player!!, mazeVillage) + stage = 10 + } + else { + travelCutscene(player!!, mazeEntrance) + stage++ + } + } + 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(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(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(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(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(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(FacialExpression.OLD_NORMAL, "Ok then, take care.").also { stage = END_DIALOGUE } + 6 -> { + if(followLocation == "village") + travelCutscene(player!!, mazeVillage) + else + travelCutscene(player!!, mazeEntrance) + stage++ + } + 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(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(FacialExpression.OLD_NORMAL, "Ok then, take care.").also { stage = END_DIALOGUE } + 6 -> { + if(followLocation == "village") { + travelCutscene(player!!, mazeVillage) + stage = 8 + } else { + travelCutscene(player!!, mazeEntrance) + stage++ + } + } + 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 } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordDialogue.kt new file mode 100644 index 000000000..b70b6449b --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordDialogue.kt @@ -0,0 +1,32 @@ +package content.region.kandarin.quest.tree + +import content.data.Quests +import core.api.getQuestStage +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE + +class KhazardWarlordDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) + if(questStage == 31){ + when(stage) { + 0 -> playerl("Hello there.").also { stage++ } + 1 -> npcl("You think you're so clever. You know nothing!").also { stage++ } + 2 -> playerl("What?").also { stage++ } + 3 -> npcl("I'll crush you and those pesky little green men!").also { stage = END_DIALOGUE } + } + } else if (questStage == 40) { + when(stage) { + 0 -> playerl("You there, stop!").also { stage++ } + 1 -> npcl("Go back to your pesky little green friends.").also { stage++ } + 2 -> playerl("I've come for the orbs.").also { stage++ } + 3 -> npcl("You're out of your depth traveller. These orbs are part of a much larger picture.").also { stage++ } + 4 -> playerl("They're stolen goods, now give them here!").also { stage++ } + 5 -> npcl("Ha, you really think you stand a chance? I'll crush you.").also { + npc!!.attack(player!!) + stage = END_DIALOGUE + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordNPC.kt b/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordNPC.kt new file mode 100644 index 000000000..736612730 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/KhazardWarlordNPC.kt @@ -0,0 +1,37 @@ +package content.region.kandarin.quest.tree + +import content.data.Quests +import core.api.addItemOrDrop +import core.api.getQuestStage +import core.api.sendDialogue +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 + +@Initializable +class KhazardWarlordNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,location), InteractionListener { + + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return KhazardWarlordNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KHAZARD_WARLORD_477) + } + + override fun defineListeners() { + } + + override fun finalizeDeath(killer: Entity?) { + 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) + } + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/KingBolrenDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/KingBolrenDialogue.kt new file mode 100644 index 000000000..89f6d5c11 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/KingBolrenDialogue.kt @@ -0,0 +1,202 @@ +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 +import core.game.world.map.RegionManager +import core.game.world.update.flag.context.Animation +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 core.game.dialogue.FacialExpression +import core.game.world.GameWorld +import core.tools.END_DIALOGUE + +class KingBolrenDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) + when { + questStage < 10 -> { + when (stage) { + 0 -> playerl("Hello.").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(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(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(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(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.", FacialExpression.OLD_NORMAL) + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 10) + stage = END_DIALOGUE + } + } + } + questStage < 31 -> { + when (stage) { + 0 -> playerl("Hello Bolren.").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 } + } + } + questStage == 31 -> { + if(inInventory(player!!,Items.ORB_OF_PROTECTION_587)){ + when(stage) { + 0 -> playerl("I have the orb.").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(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(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(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(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(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(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!!, Quests.TREE_GNOME_VILLAGE, 40) + sendNPCDialogue(player!!, NPCs.ELKOY_5179, "Good luck friend.", FacialExpression.OLD_NORMAL) + } + stage = END_DIALOGUE + } + } + } else { + when(stage) { + 0 -> playerl("Hello Bolren.").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(FacialExpression.OLD_NORMAL, "Please, we must have the orb if we are to survive.").also { stage = END_DIALOGUE } + } + } + } + questStage == 40 -> { + if(inInventory(player!!,Items.ORBS_OF_PROTECTION_588)){ + when(stage) { + 0 -> playerl("Bolren, I have returned.").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(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(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 + GameWorld.Pulser.submit(object : Pulse(0) { + var count = 0 + val orbsOfProtection = 79 + val spiritTreeWithOrbs = Animation(333) + val gnomeSpots = listOf(Location(2542, 3172, 0),Location(2541, 3171, 0),Location(2542, 3167, 0),Location(2541, 3168, 0)) + val spiritTree = getScenery(2543,3168,0) + override fun pulse(): Boolean { + when (count) { + 0 -> { + forceWalk(player!!,Location(2542, 3170, 0),"DUMB") + } + 2 -> { + player!!.faceLocation(Location(2543,3170,0)) + for((i, spot) in gnomeSpots.withIndex()){ + val gnome = RegionManager.getNpc(spot, NPCs.LOCAL_GNOME_484, 1) + gnome!!.faceLocation(Location(2543,3169,0)) + gnome.animate(Animation(197)) + if(i < 2) + gnome.sendChat("En tanai.") + else + gnome.sendChat("Su tana.") + } + + } + 3 -> { + player!!.packetDispatch.sendGraphic(orbsOfProtection) + } + 5 -> { + player!!.packetDispatch.sendSceneryAnimation(spiritTree, spiritTreeWithOrbs, false) + sendDialogue(player!!,"The orbs of protection come to rest gently in the branches of the ancient spirit tree.") + return true + } + } + count++ + return false + } + }) + // This loops back to the start of the handle.. + if(removeItem(player!!,Items.ORBS_OF_PROTECTION_588)){ + setQuestStage(player!!, Quests.TREE_GNOME_VILLAGE, 99) + } + stage = 0 + } + } + } else { + when(stage) { + 0 -> playerl("Bolren, I have returned.").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(FacialExpression.OLD_NORMAL, "Please, we must have the orb if we are to survive.").also { stage = END_DIALOGUE } + } + } + } + questStage == 99 -> { + when(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(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(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!!, Quests.TREE_GNOME_VILLAGE) -> { + when(stage) { + 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 + } + } + } + } + } +} diff --git a/Server/src/main/content/region/kandarin/quest/tree/LieutenantSchepburDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/LieutenantSchepburDialogue.kt new file mode 100644 index 000000000..2571454a9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/LieutenantSchepburDialogue.kt @@ -0,0 +1,19 @@ +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(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(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(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 } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/LocalGnomeDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/LocalGnomeDialogue.kt new file mode 100644 index 000000000..b6a0ff54c --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/LocalGnomeDialogue.kt @@ -0,0 +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(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 new file mode 100644 index 000000000..3038fed58 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/RemsaiDialogue.kt @@ -0,0 +1,51 @@ +package content.region.kandarin.quest.tree + +import content.data.Quests +import core.api.inInventory +import core.api.getQuestStage +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE + +class RemsaiDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) + when { + inInventory(player!!,Items.ORBS_OF_PROTECTION_588) -> { + when(stage) { + 0 -> playerl("I've returned.").also { stage++ } + 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(FacialExpression.OLD_NORMAL, "Hello, did you find the orb?").also { stage++ } + 2 -> playerl("I have it here.").also { stage++ } + 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(FacialExpression.OLD_NORMAL, "Hello, did you find the orb?").also { stage++ } + 2 -> playerl("No, I'm afraid not.").also { stage++ } + 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(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(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 } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeOneDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeOneDialogue.kt new file mode 100644 index 000000000..d4a4919fb --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeOneDialogue.kt @@ -0,0 +1,48 @@ +package content.region.kandarin.quest.tree + +import content.data.Quests +import core.api.* +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE + +class TrackerGnomeOneDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) + when { + questStage >= 40 -> { + when (stage) { + 0 -> playerl("Hello").also { stage++ } + 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(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(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(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(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 } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeThreeDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeThreeDialogue.kt new file mode 100644 index 000000000..de00880e0 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeThreeDialogue.kt @@ -0,0 +1,69 @@ +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(){ + + private val xcoordMap = mapOf( + 1 to "Less than my hands.", + 2 to "More than my head, less than my fingers.", + 3 to "More than we but less than our feet.", + 4 to "My legs and your legs.") + + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) + when { + questStage == 30 -> { + when(stage) { + 0 -> playerl("Are you OK?").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(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(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(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(FacialExpression.OLD_NORMAL, xcoordMap[answer]) + setAttribute(player!!,"/save:treegnome:xcoord",answer) + } else { + npcl(FacialExpression.OLD_NORMAL, xcoordMap[getAttribute(player!!,"treegnome:xcoord",1)]) + } + stage++ + } + 11 -> playerl("You're mad").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(FacialExpression.OLD_LAUGH1, "All day we pray in the hay, hee hee.").also { + setAttribute(player!!, "/save:treegnome:tracker3", true) + stage = END_DIALOGUE + } + } + } + questStage == 31 -> { + when(stage) { + 0 -> playerl("Hello again.").also { stage++ } + 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(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 } + } + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeTwoDialogue.kt b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeTwoDialogue.kt new file mode 100644 index 000000000..d4bbd63c0 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/TrackerGnomeTwoDialogue.kt @@ -0,0 +1,52 @@ +package content.region.kandarin.quest.tree + +import content.data.Quests +import core.api.* +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE + +class TrackerGnomeTwoDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + val questStage = getQuestStage(player!!, Quests.TREE_GNOME_VILLAGE) + when { + questStage == 30 -> { + when (stage) { + 0 -> playerl("Are you OK?").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(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(FacialExpression.OLD_NORMAL, "Now leave before they find you and all is lost.").also { stage++ } + 8 -> playerl("Hang in there.").also { stage++ } + 9 -> npcl(FacialExpression.OLD_NORMAL, "Go!").also { stage = END_DIALOGUE } + } + } + questStage >= 40 -> { + when(stage) { + 0 -> playerl("Hello").also { stage++ } + 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(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(FacialExpression.OLD_NORMAL, "Well done, you've broken down their defences. This battle must be ours.").also { stage = END_DIALOGUE } + } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillage.kt b/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillage.kt new file mode 100644 index 000000000..14f04896f --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillage.kt @@ -0,0 +1,96 @@ +package content.region.kandarin.quest.tree + +import core.api.addItemOrDrop +import core.api.inInventory +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +@Initializable +class TreeGnomeVillage: Quest(Quests.TREE_GNOME_VILLAGE, 125, 124, 2, 111, 0, 1, 9) { + 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.GNOME_AMULET_589, 230, 277, 5) + drawReward(player,"2 Quest Points", ln++) + drawReward(player,"11,450 Attack XP", ln++) + drawReward(player,"Gnome Amulet of Protection", ln) + player.skills.addExperience(Skills.ATTACK, 11450.0) + addItemOrDrop(player,Items.GNOME_AMULET_589) + player.questRepository.syncronizeTab(player) + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + player?: return + if(stage == 0){ + line(player, "You can start this quest by speaking to !!King Bolren?? in the", line++, stage > 0) + line(player, "!!Tree Gnome Village??", line++, stage > 0) + line++ + } + if(stage >= 10){ + line(player, "King Bolren said that one of their orbs of", line++, stage > 10) + line(player, "protection has been stolen by Khazard troops.", line++, stage > 10) + line(player, "Commander Montai, North !!of the maze, can help you get?? it", line++, stage > 10) + line++ + } + if (stage >= 20) { + line(player, "Commander Montai has asked you to gather !!6 normal logs??", line++, stage > 20) + line(player, "to make more battlements.", line++, stage > 20) + line++ + } + if (stage == 25) { + line(player, "You should ask Commander Montai for further instruction.", line++) + line++ + } + if (stage >= 30) { + line(player, "Commander Montai needs help locating his !!tracker gnomes?? to", line++,stage > 30) + line(player, "gather the coordinates to strike the Khazard stronghold", line++, stage > 30) + line(player, "You must speak with:", line++, stage > 30) + line(player, "Tracker Gnome 1", line++, player.getAttribute("treegnome:tracker1",false)) + line(player, "Tracker Gnome 2", line++, player.getAttribute("treegnome:tracker2",false)) + line(player, "Tracker Gnome 3", line++, player.getAttribute("treegnome:tracker3",false)) + line++ + } + if (stage >= 31) { + line(player, "The ballista opened a hole in the !!Khazard stronghold??.", line++, stage > 31) + line(player, "Commander Montai says the !!Orb of Protection?? should be in there", line++, stage > 31) + line++ + } + if (inInventory(player,Items.ORB_OF_PROTECTION_587)) { + line(player, "You've retrieved the orb! You should return it to !!King Bolren??", line++) + } + if (stage >= 40) { + line(player, "Khazard troops took the other !!Orbs of Protection?? ", line++, stage > 40) + line(player, "They were headed north of the stronghold.", line++, stage > 40) + line(player,"A warlord carries the orbs.",line++, stage > 40) + line++ + } + if (inInventory(player,Items.ORBS_OF_PROTECTION_588)) { + line(player, "You've retrieved the orbs!", line++) + line(player, "You should return them it to !!King Bolren??",line++) + } + if (stage >= 99) { + line(player,"You have returned the orbs!", line++, stage > 99) + line(player, "Speak with King Bolren for a reward!", line++, stage > 99) + line++ + } + if (stage == 100) { + line(player,"%%QUEST COMPLETE!&&", line, stage > 99) + } + } + companion object { + val mazeVillage = Location(2515,3159,0) + val mazeEntrance = Location(2504,3192,0) + } +} \ 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 new file mode 100644 index 000000000..71471bf43 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/tree/TreeGnomeVillageListeners.kt @@ -0,0 +1,177 @@ +package content.region.kandarin.quest.tree + +import content.data.Quests +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import content.global.skill.agility.AgilityHandler +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.RegionManager +import core.game.world.update.flag.context.Animation +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.mazeVillage +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.GameWorld + +class TreeGnomeVillageListeners : InteractionListener { + private val ballista = 2181 + private val crumbledWall = 12762 + private val closedChest = 2183 + private val looseRailing = 2186 + private val openedChest = 2182 + private val strongholdDoor = 2184 + + override fun defineDestinationOverrides() { + setDest(IntType.NPC, intArrayOf(NPCs.TRACKER_GNOME_2_482),"talk-to"){ _, _ -> + return@setDest Location.create(2524, 3256, 0) + } + } + + override fun defineListeners() { + on(NPCs.KING_BOLREN_469, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, KingBolrenDialogue(), npc) + return@on true + } + on(NPCs.ELKOY_5179, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, ElkoyDialogue(), npc) + return@on true + } + on(NPCs.ELKOY_5182, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, ElkoyDialogue(), npc) + return@on true + } + on(NPCs.ELKOY_5182, IntType.NPC, "follow"){ player, _ -> + ElkoyDialogue().travelCutscene(player, if (player.location.y > 3161) mazeVillage else mazeEntrance) + return@on true + } + on(NPCs.COMMANDER_MONTAI_470, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, CommanderMontaiDialogue(), npc) + return@on true + } + on(NPCs.TRACKER_GNOME_1_481, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, TrackerGnomeOneDialogue(), npc) + return@on true + } + on(NPCs.TRACKER_GNOME_2_482, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, TrackerGnomeTwoDialogue(), npc) + return@on true + } + on(NPCs.TRACKER_GNOME_3_483, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, TrackerGnomeThreeDialogue(), npc) + return@on true + } + on(NPCs.KHAZARD_WARLORD_477, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, KhazardWarlordDialogue(), npc) + return@on true + } + on(NPCs.REMSAI_472, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, RemsaiDialogue(), npc) + return@on true + } + on(NPCs.LOCAL_GNOME_484, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, LocalGnomeDialogue(), npc) + return@on true + } + on(ballista, IntType.SCENERY, "fire"){ player, _ -> + openDialogue(player, BallistaDialogue()) + return@on true + } + on(looseRailing, IntType.SCENERY, "squeeze-through"){ player, _ -> + squeezeThrough(player) + return@on true + } + on(crumbledWall, IntType.SCENERY, "climb-over"){ player, _ -> + openDialogue(player, ClimbWall()) + return@on true + } + on(closedChest, IntType.SCENERY, "open"){ player, node -> + 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 (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 -> + if(player.location.y >= 3251){ + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node as Scenery) + } + return@on true + } + on(NPCs.LIEUTENANT_SCHEPBUR_3817, IntType.NPC, "talk-to"){ player, node -> + openDialogue(player, LieutenantSchepburDialogue(),node) + return@on true + } + } + + 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(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 + } + }) + } + } + } + } + } +} diff --git a/Server/src/main/java/core/game/content/quest/members/waterfallquest/AlmeraDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/AlmeraDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/quest/members/waterfallquest/AlmeraDialogue.java rename to Server/src/main/content/region/kandarin/quest/waterfall/AlmeraDialogue.java index 739f73716..6d3f2bee6 100644 --- a/Server/src/main/java/core/game/content/quest/members/waterfallquest/AlmeraDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/AlmeraDialogue.java @@ -1,8 +1,9 @@ -package core.game.content.quest.members.waterfallquest; +package content.region.kandarin.quest.waterfall; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import content.data.Quests; +import core.game.dialogue.DialogueInterpreter; +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; @@ -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/java/core/game/content/quest/members/waterfallquest/GolrieDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/GolrieDialogue.java similarity index 84% rename from Server/src/main/java/core/game/content/quest/members/waterfallquest/GolrieDialogue.java rename to Server/src/main/content/region/kandarin/quest/waterfall/GolrieDialogue.java index d78137290..f16af0ea8 100644 --- a/Server/src/main/java/core/game/content/quest/members/waterfallquest/GolrieDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/GolrieDialogue.java @@ -1,11 +1,13 @@ -package core.game.content.quest.members.waterfallquest; +package content.region.kandarin.quest.waterfall; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import static core.api.ContentAPIKt.*; + /** * Handles Golrie's Dialogue for the Waterfall quest * @author Splinter @@ -48,7 +50,11 @@ public class GolrieDialogue extends DialoguePlugin { case 3: //TODO: Possibly change this to chat message interpreter.sendDialogue("You look amongst the junk on the floor."); - stage = 4; + if (hasAnItem(player, 294).getContainer() != null) { + stage = 50; + } else { + stage = 4; + } break; case 4: //TODO: Possibly change this to chat message @@ -79,6 +85,11 @@ public class GolrieDialogue extends DialoguePlugin { interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "OK... Take care Golrie."); stage = 100; break; + case 50: + //TODO: Possibly change this to chat message + interpreter.sendDialogue("You find nothing of interest."); + stage = 100; + break; } return true; } diff --git a/Server/src/main/java/core/game/content/quest/members/waterfallquest/HadleyDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/HadleyDialogue.java similarity index 81% rename from Server/src/main/java/core/game/content/quest/members/waterfallquest/HadleyDialogue.java rename to Server/src/main/content/region/kandarin/quest/waterfall/HadleyDialogue.java index d3a51e5e3..6ac9e47f9 100644 --- a/Server/src/main/java/core/game/content/quest/members/waterfallquest/HadleyDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/HadleyDialogue.java @@ -1,10 +1,10 @@ -package core.game.content.quest.members.waterfallquest; +package content.region.kandarin.quest.waterfall; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * Handles Almera's Dialogue for the Waterfall Quest. @@ -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/java/core/game/content/quest/members/waterfallquest/HudonDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/HudonDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/members/waterfallquest/HudonDialogue.java rename to Server/src/main/content/region/kandarin/quest/waterfall/HudonDialogue.java index cebc27e7c..e8c3262ad 100644 --- a/Server/src/main/java/core/game/content/quest/members/waterfallquest/HudonDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/HudonDialogue.java @@ -1,8 +1,9 @@ -package core.game.content.quest.members.waterfallquest; +package content.region.kandarin.quest.waterfall; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import content.data.Quests; +import core.game.dialogue.DialogueInterpreter; +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; @@ -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/java/core/game/content/dialogue/MithrilSeedsDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/MithrilSeedsDialogue.java rename to Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsDialogue.java index 5e5a95020..4a6c28e70 100644 --- a/Server/src/main/java/core/game/content/dialogue/MithrilSeedsDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsDialogue.java @@ -1,14 +1,14 @@ -package core.game.content.dialogue; +package content.region.kandarin.quest.waterfall; +import core.game.dialogue.DialoguePlugin; 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 rs09.game.world.GameWorld; +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. @@ -63,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/java/core/game/content/dialogue/MithrilSeedsPlugin.java b/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsPlugin.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/MithrilSeedsPlugin.java rename to Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsPlugin.java index a9a300f21..b085e42e2 100644 --- a/Server/src/main/java/core/game/content/dialogue/MithrilSeedsPlugin.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.dialogue; +package content.region.kandarin.quest.waterfall; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; @@ -8,10 +8,9 @@ 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 rs09.game.world.GameWorld; +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/java/core/game/content/quest/members/waterfallquest/WaterFall.java b/Server/src/main/content/region/kandarin/quest/waterfall/WaterFall.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/waterfallquest/WaterFall.java rename to Server/src/main/content/region/kandarin/quest/waterfall/WaterFall.java index 0b96c06d1..33030e29a 100644 --- a/Server/src/main/java/core/game/content/quest/members/waterfallquest/WaterFall.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/WaterFall.java @@ -1,11 +1,12 @@ -package core.game.content.quest.members.waterfallquest; +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; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * The main type for the waterfall quest. @@ -13,17 +14,11 @@ import rs09.plugin.PluginManager; */ @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) { - PluginManager.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/WaterfallListeners.kt b/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallListeners.kt new file mode 100644 index 000000000..eb99eef08 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallListeners.kt @@ -0,0 +1,23 @@ +package content.region.kandarin.quest.waterfall + +import core.game.world.map.Location +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class WaterfallListeners : InteractionListener { + + val HUDON = NPCs.HUDON_305 + override fun defineListeners() { + on(HUDON, IntType.NPC, "talk-to"){ player, node -> + player.dialogueInterpreter.open(HUDON,node.asNpc()) + return@on true + } + } + + override fun defineDestinationOverrides() { + setDest(IntType.NPC,HUDON){ _, _ -> + return@setDest Location.create(2512, 3481, 0) + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/members/waterfallquest/WaterfallPlugin.java b/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallPlugin.java similarity index 92% rename from Server/src/main/java/core/game/content/quest/members/waterfallquest/WaterfallPlugin.java rename to Server/src/main/content/region/kandarin/quest/waterfall/WaterfallPlugin.java index 3af50ab50..2a10ebe5c 100644 --- a/Server/src/main/java/core/game/content/quest/members/waterfallquest/WaterfallPlugin.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallPlugin.java @@ -1,15 +1,16 @@ -package core.game.content.quest.members.waterfallquest; +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; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; -import core.game.node.entity.skill.agility.AgilityHandler; +import core.game.dialogue.FacialExpression; +import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -21,13 +22,14 @@ 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.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.Pulse; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import kotlin.Unit; +import core.plugin.ClassScanner; + +import static core.api.ContentAPIKt.*; /** * Master plugin file for the Waterfall quest. @@ -101,7 +103,7 @@ public final class WaterfallPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new WaterfallUseWithHandler()); + ClassScanner.definePlugin(new WaterfallUseWithHandler()); NPCDefinition.forId(305).getHandlers().put("option:talk-to", this); SceneryDefinition.forId(1987).getHandlers().put("option:board", this); SceneryDefinition.forId(2020).getHandlers().put("option:climb", this); @@ -129,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; @@ -140,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 { @@ -178,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:// @@ -248,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: @@ -276,7 +278,10 @@ public final class WaterfallPlugin extends OptionHandler { break; case 10283: case 1996: - player.addExtension(LogoutTask.class, new LocationLogoutTask(10, player.getLocation())); + player.logoutListeners.put("waterfall", player1 -> { + player1.setLocation(player.getLocation().transform(0,0,0)); + return Unit.INSTANCE; + }); player.getPacketDispatch().sendGraphic(68); player.lock(6); AgilityHandler.walk(player, -1, player.getLocation(), new Location(2512, 3471, 0), Animation.create(164), 0, null); @@ -421,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) { @@ -431,7 +436,10 @@ public final class WaterfallPlugin extends OptionHandler { if (SWIMMERS.size() == 0 || ROPES.size() == 0) { handleObjects(true, player); } - player.addExtension(LogoutTask.class, new LocationLogoutTask(13, player.getLocation())); + player.logoutListeners.put("waterfall", player1 -> { + player1.setLocation(player.getLocation().transform(0,0,0)); + return Unit.INSTANCE; + }); player.getPulseManager().run(new Pulse(2, player) { @Override public boolean pulse() { @@ -443,6 +451,7 @@ public final class WaterfallPlugin extends OptionHandler { player.teleport(new Location(2513, 3468)); player.faceLocation(new Location(2512, 3468, 0)); player.animate(Animation.create(780)); + player.logoutListeners.remove("waterfall"); return true; } @@ -476,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/java/core/game/content/quest/members/waterfallquest/WaterfallTreeDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallTreeDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/quest/members/waterfallquest/WaterfallTreeDialogue.java rename to Server/src/main/content/region/kandarin/quest/waterfall/WaterfallTreeDialogue.java index 1b7bd1271..54b39a043 100644 --- a/Server/src/main/java/core/game/content/quest/members/waterfallquest/WaterfallTreeDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/WaterfallTreeDialogue.java @@ -1,7 +1,7 @@ -package core.game.content.quest.members.waterfallquest; +package content.region.kandarin.quest.waterfall; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; 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/java/core/game/content/dialogue/CamelotGuardDialogue.java b/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java similarity index 86% rename from Server/src/main/java/core/game/content/dialogue/CamelotGuardDialogue.java rename to Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java index 2cab50764..5cc97311a 100644 --- a/Server/src/main/java/core/game/content/dialogue/CamelotGuardDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/dialogue/CamelotGuardDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.kandarin.seers.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; @@ -50,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 new file mode 100644 index 000000000..ea78bf714 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/dialogue/FionellaDialogue.kt @@ -0,0 +1,56 @@ +package content.region.kandarin.seers.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 + +/** + * Dialogue for + * @author qmqz + */ + +@Initializable +class FionellaDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"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?", "No thanks.").also { stage++ } + } + + 1 -> when(buttonId){ + 1 -> { + player(FacialExpression.FRIENDLY, "Yes please. What are you selling?").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } + } + } + + 10 -> npc(FacialExpression.FRIENDLY, "Take a look.").also { stage = 100 } + + 99 -> end() + 100 -> end().also { npc.openShop(player) } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FionellaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FIONELLA_932) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt b/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt new file mode 100644 index 000000000..323e82d85 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt @@ -0,0 +1,86 @@ +package content.region.kandarin.seers.dialogue + +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import core.ServerStore +import core.ServerStore.Companion.getBoolean +import org.json.simple.JSONObject + +@Initializable +class GeoffreyDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + //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.") + //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?): DialoguePlugin { + return GeoffreyDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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++ } + 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++ } + 5 -> player("Erm, okay. Maybe I can have some of your spare flax?").also { stage++ } + 6 -> npc("No. I don't trust outsiders. Who knows what depraved", "things you would do with the GLORIOUS flax? Only", "locals know how to treat it right.").also { stage++ } + 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("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 + 102 -> {rewardFlax(120, "Yes. Sir Kay has instructed me to give you an", "allowance of 120 GLORIOUS flax a day. I'm not going", "to argue with a knight, so here you go.")} + } + return true + } + + fun rewardFlax(n: Int, vararg messages: String): Unit { + npc(*messages) + player.inventory.add(Item(Items.FLAX_1780, n)) + getStoreFile()[player.username.toLowerCase()] = true + stage = 999 + } + + override fun getIds(): IntArray { + return intArrayOf(8590) + } + + fun getStoreFile(): JSONObject { + return ServerStore.getArchive("daily-seers-flax") + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/IgnatiusVulcanDialogue.java b/Server/src/main/content/region/kandarin/seers/dialogue/IgnatiusVulcanDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/IgnatiusVulcanDialogue.java rename to Server/src/main/content/region/kandarin/seers/dialogue/IgnatiusVulcanDialogue.java index aec85e33c..2d5a75762 100644 --- a/Server/src/main/java/core/game/content/dialogue/IgnatiusVulcanDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/dialogue/IgnatiusVulcanDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.dialogue; +package content.region.kandarin.seers.dialogue; -import core.game.content.global.Skillcape; +import core.game.dialogue.DialoguePlugin; +import core.game.global.Skillcape; import core.game.node.entity.skill.Skills; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.AbstractNPC; @@ -8,7 +9,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/LegendsGuardDialogue.kt b/Server/src/main/content/region/kandarin/seers/dialogue/LegendsGuardDialogue.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/dialogue/LegendsGuardDialogue.kt rename to Server/src/main/content/region/kandarin/seers/dialogue/LegendsGuardDialogue.kt index a529844fb..8cacba296 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/LegendsGuardDialogue.kt +++ b/Server/src/main/content/region/kandarin/seers/dialogue/LegendsGuardDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.content.dialogue +package content.region.kandarin.seers.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/PhantuwtiFantstuwiFarSight.java b/Server/src/main/content/region/kandarin/seers/dialogue/PhantuwtiFantstuwiFarSight.java new file mode 100644 index 000000000..aaaa62e66 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/dialogue/PhantuwtiFantstuwiFarSight.java @@ -0,0 +1,103 @@ +package content.region.kandarin.seers.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 PhantuwtiFantstuwiFarSight dialogue. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class PhantuwtiFantstuwiFarSight extends DialoguePlugin { + + /** + * Constructs a new {@code PhantuwtiFantstuwiFarSight} {@code Object}. + */ + public PhantuwtiFantstuwiFarSight() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code PhantuwtiFantstuwiFarSight} {@code Object}. + * @param player the player. + */ + public PhantuwtiFantstuwiFarSight(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new PhantuwtiFantstuwiFarSight(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendOptions("Select an Option", "Hello, what is this place?", "What do you do here?", "Do you have any quests?", "Ok, thanks."); + stage = 0; + return true; + } + + @Override + public int[] getIds() { + return new int[] { 1798 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Hello, what is this place?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "What do you do here?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Do you have any quests?"); + stage = 30; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Ok, thanks."); + stage = 40; + break; + + } + break; + case 10: + 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: + interpreter.sendOptions("Select an Option", "Hello, what is this place?", "What do you do here?", "Do you have any quests?", "Ok, thanks."); + stage = 0; + break; + case 20: + 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: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Unfortunately no, sorry, but if adventure is what you", "seek, try checking through your quest list!"); + stage = 31; + break; + case 31: + interpreter.sendOptions("Select an Option", "Hello, what is this place?", "What do you do here?", "Do you have any quests?", "Ok, thanks."); + stage = 0; + break; + case 40: + end(); + break; + } + return true; + } + +} diff --git a/Server/src/main/java/core/game/content/dialogue/SeerBartenderDialogue.java b/Server/src/main/content/region/kandarin/seers/dialogue/SeerBartenderDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/SeerBartenderDialogue.java rename to Server/src/main/content/region/kandarin/seers/dialogue/SeerBartenderDialogue.java index 492bd93f2..d55659f43 100644 --- a/Server/src/main/java/core/game/content/dialogue/SeerBartenderDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/dialogue/SeerBartenderDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.kandarin.seers.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; 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/diary/SeerDiaryDialogue.kt b/Server/src/main/content/region/kandarin/seers/diary/SeerDiaryDialogue.kt new file mode 100644 index 000000000..e0eb61789 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/diary/SeerDiaryDialogue.kt @@ -0,0 +1,87 @@ +package content.region.kandarin.seers.diary + +import core.api.sendItemDialogue +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +class SeerDiaryDialogue : DialogueFile() { + + companion object { + + const val LOST_HEAD_BAND = 20 + const val HEAD_BAND_HELP = 30 + const val CLAIM_HEAD_BAND = 40 + const val ASK_FOR_HELP = 50 + } + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE ->{ + if (AchievementDiary.canReplaceReward(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl(FacialExpression.SAD, "I seem to have lost my seers' headband...").also { + stage = LOST_HEAD_BAND + } + } else if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl(FacialExpression.ASKING, "Can you remind me what my headband does?").also { + stage = HEAD_BAND_HELP + } + } else if (AchievementDiary.canClaimLevelRewards(player, DiaryType.SEERS_VILLAGE, 0)) { + playerl( + FacialExpression.HAPPY, + "Hi. I've completed the Easy tasks in my Achievement Diary." + ).also { + stage = CLAIM_HEAD_BAND + } + } else { + playerl(FacialExpression.ASKING, "Do you have an Achievement Diary for me?").also { + stage = ASK_FOR_HELP + } + } + } + + + LOST_HEAD_BAND -> { + if (AchievementDiary.grantReplacement(player, DiaryType.SEERS_VILLAGE, 0)) + npcl(FacialExpression.ANNOYED, "Here's your replacement. Please be more careful.").also { + stage = END_DIALOGUE + } + else + // This line is just guessed + npcl(FacialExpression.HALF_GUILTY, "It seems your inventory is full").also { stage = END_DIALOGUE } + } + HEAD_BAND_HELP -> npcl(FacialExpression.NEUTRAL, "Your headband marks you as an honourary seer. Geoffrey - who works in the field to the south - will give you free flax every day.").also { + stage = END_DIALOGUE + } + + // This has to be npc otherwise wordwrap goes wrong and extends to 5 lines + ASK_FOR_HELP -> npc(FacialExpression.HAPPY, "I certainly do - we have a set of tasks spanning Seers'", "Village, Catherby, Hemenster and the Sinclair Mansion.", + "Just complete the tasks listed in the Achievement Diary", "and they will be ticked off automatically.").also { stage++ } + ASK_FOR_HELP + 1 -> playerl(FacialExpression.ASKING, "Can you help me out with the Achievement Diary tasks?").also { stage++ } + ASK_FOR_HELP + 2 -> npcl(FacialExpression.SAD, + "I'm afraid not. It is important that adventurers complete the tasks unaided. That way, only the truly worthy collect the spoils.").also { + stage = END_DIALOGUE + } + + CLAIM_HEAD_BAND -> npcl(FacialExpression.HAPPY, "Well done, adventurer. You are clearly a "+(if (player!!.isMale) "man" else "woman")+" of great wisdom. I have a gift for you.").also { stage++ } + CLAIM_HEAD_BAND + 1 -> { + if (!AchievementDiary.flagRewarded(player, DiaryType.SEERS_VILLAGE, 0)) { + npcl(FacialExpression.NEUTRAL, "Come back when you have two free inventory slots.").also { + stage = END_DIALOGUE + } + } else { + sendItemDialogue(player!!, AchievementDiary.getRewards(DiaryType.SEERS_VILLAGE, 0)[0], + "The seer hands you a strange-looking headband and a rusty lamp.").also { stage++ } + } + } + CLAIM_HEAD_BAND + 2 -> npcl(FacialExpression.HAPPY, "You are now an honourary seer and Geoffrey - who works in the field to the south - will give you free flax every day. Don't call him 'Geoffrey' though: he prefers to be known as 'Flax'.").also { stage++ } + CLAIM_HEAD_BAND + 3 -> playerl(FacialExpression.ASKING, "Flax? What sort of name is that for a person?").also { stage++ } + CLAIM_HEAD_BAND + 4 -> npcl(FacialExpression.NEUTRAL, "I know, I know. The poor boy is a simple soul - he just really loves picking flax. A little too much, I fear.").also { + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt new file mode 100644 index 000000000..bd9e90188 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt @@ -0,0 +1,367 @@ +package content.region.kandarin.seers.diary + +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 org.rs09.consts.Scenery + +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" + private const val ATTRIBUTE_SHARK_CAUGHT_COUNT = "diary:seers:shark-caught" + private const val ATTRIBUTE_SHARK_COOKED_COUNT = "diary:seers:shark-cooked" + 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) + + private val COMBAT_BRACELETS = arrayOf( + Items.COMBAT_BRACELET_11126, Items.COMBAT_BRACELET4_11118, + Items.COMBAT_BRACELET3_11120, Items.COMBAT_BRACELET2_11122, + Items.COMBAT_BRACELET1_11124 + ) + + private val RANGING_GUILD_ARCHERS = arrayOf( + NPCs.TOWER_ARCHER_688, NPCs.TOWER_ARCHER_689, + NPCs.TOWER_ARCHER_690, NPCs.TOWER_ARCHER_691 + ) + + private val WORKSHOP_ELEMENTALS = arrayOf( + NPCs.FIRE_ELEMENTAL_1019, NPCs.EARTH_ELEMENTAL_1020, + 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 + const val SIR_GALAHAD_MAKE_TEA = 2 + const val TAKE_POISON_TO_KING_ARTHUR = 3 + const val SPIN_5_BOW_STRINGS = 4 + const val SINCLAIR_MANSION_FILL_5_POTS_WITH_FLOUR = 5 + const val FORESTERS_ARMS_GIVE_5_LOCALS_GLASS_OF_CIDER = 6 + const val PLANT_JUTE = 7 + const val SINCLAIR_MANSION_USE_CHURN = 8 + const val BUY_CANDLE = 9 + const val PRAY_AT_ALTAR = 10 + const val CATCH_MACKEREL = 11 + } + + object MediumTasks { + const val SINCLAIR_MANSION_USE_FREMENNIK_SHORTCUT = 0 + const val THORMAC_SORCERER_TALK_ABOUT_MYSTIC_STAVES = 1 + const val TRANSPORT_FULL_LOAD_OF_COAL = 2 + const val FIND_HIGHEST_POINT = 3 + const val DEFEAT_EACH_ELEMENTAL_TYPE = 4 + const val TELEPORT_TO_CAMELOT = 5 + const val RANGING_GUILD_KILL_EACH_TOWER_GUARD = 6 + const val RANGING_GUILD_JUDGE_1000_ARCHERY_TICKETS = 7 + const val RANGING_GUILD_BUY_SOMETHING_FOR_TICKETS = 8 + const val USE_FAMILIAR_TO_LIGHT_MAPLE_LOGS = 9 + const val HARRY_GET_PET_FISH = 10 + const val CATHERBY_CATCH_AND_COOK_BASS = 11 + } + + object HardTasks { + const val RANGING_GUILD_TELEPORT = 0 + const val CUT_5_YEW_LOGS = 1 + const val FLETCH_MAGIC_SHORTBOW_INSIDE_BANK = 2 + const val ENTER_SEERS_COURTHOUSE_WITH_PIETY = 3 + const val DIAL_FAIRY_RING_MCGRUBORS_WOOD = 4 + const val LIGHT_MAGIC_LOG = 5 + const val HIGH_ALCH_MAGIC_SHORTBOW_INSIDE_BANK = 6 + const val CATHERBY_CATCH_5_SHARKS = 7 + const val CATHERBY_COOK_5_SHARKS_WITH_COOKING_GAUNTLETS = 8 + const val CHARGE_5_WATER_ORBS_AT_ONCE = 9 + const val GRAPPLE_FROM_WATER_OBELISK_TO_CATHERBY_SHORE = 10 + } + } + + 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, + DiaryLevel.HARD, + HardTasks.CUT_5_YEW_LOGS, + ATTRIBUTE_CUT_YEW_COUNT, + 5 + ) + } + + 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( + player, + DiaryLevel.EASY, + EasyTasks.CATCH_MACKEREL + ) + } + + Items.RAW_BASS_363 -> { + fulfillTaskRequirement( + player, + DiaryLevel.MEDIUM, + MediumTasks.CATHERBY_CATCH_AND_COOK_BASS, + ATTRIBUTE_BASS_CAUGHT + ) + } + + Items.BASS_365 -> { + whenTaskRequirementFulfilled(player, ATTRIBUTE_BASS_CAUGHT) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CATHERBY_CATCH_AND_COOK_BASS + ) + } + } + + Items.RAW_SHARK_383 -> { + progressIncrementalTask( + player, + DiaryLevel.HARD, + HardTasks.CATHERBY_CATCH_5_SHARKS, + ATTRIBUTE_SHARK_CAUGHT_COUNT, + 5 + ) + } + + Items.SHARK_385 -> { + if (inEquipment(player, Items.COOKING_GAUNTLETS_775)) { + progressIncrementalTask( + player, + DiaryLevel.HARD, + HardTasks.CATHERBY_COOK_5_SHARKS_WITH_COOKING_GAUNTLETS, + ATTRIBUTE_SHARK_COOKED_COUNT, + 5 + ) + } + } + } + } + } + + override fun onNpcKilled(player: Player, event: NPCKillEvent) { + when (player.viewport.region.id) { + 10906 -> if (event.npc.id in WORKSHOP_ELEMENTALS) { + progressFlaggedTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.DEFEAT_EACH_ELEMENTAL_TYPE, + ATTRIBUTE_ELEMENTAL_KILL_FLAGS, + 1 shl (event.npc.id - NPCs.FIRE_ELEMENTAL_1019), + 0xF + ) + } + + 10549 -> if (event.npc.id in RANGING_GUILD_ARCHERS) { + progressFlaggedTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.RANGING_GUILD_KILL_EACH_TOWER_GUARD, + ATTRIBUTE_ARCHER_KILL_FLAGS, + /* Thanks for sequential NPC IDs, Jagex! */ + 1 shl (event.npc.id - NPCs.TOWER_ARCHER_688), + 0xF + ) + } + } + } + + override fun onTeleported(player: Player, event: TeleportEvent) { + when (event.source) { + is Item -> if (event.source.id in COMBAT_BRACELETS) { + if (event.location == RANGING_GUILD_LOCATION) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.RANGING_GUILD_TELEPORT + ) + } + } + } + } + + override fun onFireLit(player: Player, event: LitFireEvent) { + when { + inBorders(player, SEERS_VILLAGE_AREA) -> { + if (event.logId == Items.MAGIC_LOGS_1513) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.LIGHT_MAGIC_LOG + ) + } + } + } + } + + override fun onInteracted(player: Player, event: InteractionEvent) { + when { + inBorders(player, SEERS_COAL_TRUCKS_AREA) -> { + whenTaskRequirementFulfilled(player, ATTRIBUTE_COAL_TRUCK_FULL) { + if (event.option == "remove-coal") { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.TRANSPORT_FULL_LOAD_OF_COAL + ) + } + } + } + } + } + + override fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) { + when (event.dialogue) { + is PoisonChaliceOnKingArthurDialogue -> { + if (event.currentStage == 4) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.TAKE_POISON_TO_KING_ARTHUR + ) + } + } + } + } + + override fun onAttributeSet(player: Player, event: AttributeSetEvent) { + when (event.attribute) { + "/save:coal-truck-inventory" -> { + if (event.value !is Int) return + + if (event.value >= 120) { + fulfillTaskRequirement( + player, + DiaryLevel.MEDIUM, + MediumTasks.TRANSPORT_FULL_LOAD_OF_COAL, + ATTRIBUTE_COAL_TRUCK_FULL + ) + } + } + } + } + + override fun onItemAlchemized(player: Player, event: ItemAlchemizationEvent) { + 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 + ) + } + } + } + + 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.ENTER_SEERS_COURTHOUSE_WITH_PIETY + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/object/BuyCrateOptionPlugin.java b/Server/src/main/content/region/kandarin/seers/handlers/BuyCrateOptionPlugin.java similarity index 82% rename from Server/src/main/java/core/game/interaction/object/BuyCrateOptionPlugin.java rename to Server/src/main/content/region/kandarin/seers/handlers/BuyCrateOptionPlugin.java index b5535e752..0c9f81ea3 100644 --- a/Server/src/main/java/core/game/interaction/object/BuyCrateOptionPlugin.java +++ b/Server/src/main/content/region/kandarin/seers/handlers/BuyCrateOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.kandarin.seers.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; @@ -6,7 +6,8 @@ import core.game.node.Node; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.system.config.ShopParser; +import core.game.shops.Shops; +import core.game.system.config.ShopParser; /** * Represents the buy crate option plugin for the seers village city. @@ -18,7 +19,7 @@ public final class BuyCrateOptionPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - ShopParser.Companion.openUid(player, 93); + Shops.openId(player, 93); return true; } diff --git a/Server/src/main/java/core/game/interaction/city/CamelotNodePlugin.java b/Server/src/main/content/region/kandarin/seers/handlers/CamelotNodePlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/city/CamelotNodePlugin.java rename to Server/src/main/content/region/kandarin/seers/handlers/CamelotNodePlugin.java index 8503dc735..cdf3aff78 100644 --- a/Server/src/main/java/core/game/interaction/city/CamelotNodePlugin.java +++ b/Server/src/main/content/region/kandarin/seers/handlers/CamelotNodePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.city; +package content.region.kandarin.seers.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/content/region/kandarin/seers/handlers/MysteriousStatueZone.kt b/Server/src/main/content/region/kandarin/seers/handlers/MysteriousStatueZone.kt new file mode 100644 index 000000000..6d62923fe --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/handlers/MysteriousStatueZone.kt @@ -0,0 +1,106 @@ +package content.region.kandarin.seers.handlers + +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.game.world.map.zone.MapZone +import core.game.node.entity.player.link.diary.DiaryType +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneBuilder +import core.plugin.Plugin + +@Initializable +class MysteriousStatueZone : MapZone("mysterious-statue", true), Plugin { + + val northWest = Location.create(2739, 3492, 0) + val northEast = Location.create(2742, 3492, 0) + val southEast = Location.create(2742, 3489, 0) + val southWest = Location.create(2739, 3489, 0) + + + override fun newInstance(arg: Any?): MysteriousStatueZone { + ZoneBuilder.configure(this) + return this + } + + override fun configure() { + super.register(ZoneBorders(2739,3492,2742,3489)) + } + + override fun enter(e: Entity?): Boolean { + return super.enter(e) + } + + override fun fireEvent(identifier: String?, vararg args: Any?): Any { + return Unit + } + + override fun locationUpdate(e: Entity?, last: Location?) { + if (e is Player && !e.isArtificial) { + val player = e.asPlayer() + if(!player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE,0,1 ) && player.getAttribute("seersStatueProgress",-1) !in 0..3 ){ + player.setAttribute("seersStatueProgress",0) + println("statue started") + }else{ + when(player.location){ + northWest ->{ + if(player.getAttribute("seersStatueProgress",-1) == 0){ + player.setAttribute("seersStatueProgress",1) + println("location 1 of 4 finished") + println(player.getAttribute("seersStatueProgress",-1)) + }else if(player.getAttribute("seersStatueProgress",-1) in 2..3){ + player.setAttribute("seersStatueProgress",0) + println("location progress reset") + } + } + northEast ->{ + if(player.getAttribute("seersStatueProgress",0) == 1){ + player.setAttribute("seersStatueProgress",2) + println("location 2 of 4 finished") + }else if(player.getAttribute("seersStatueProgress",0) != 1){ + player.setAttribute("seersStatueProgress",0) + println("location progress reset") + } + } + southEast ->{ + if(player.getAttribute("seersStatueProgress",0) == 2){ + player.setAttribute("seersStatueProgress",3) + println("location 3 of 4 finished") + }else if(player.getAttribute("seersStatueProgress",0) != 2){ + player.setAttribute("seersStatueProgress",0) + println("location progress reset") + } + } + southWest ->{ + if(player.getAttribute("seersStatueProgress",0) == 3){ + player.setAttribute("seersStatueProgress",4) + println("location 4 of 4 finished") + }else if(player.getAttribute("seersStatueProgress",0) != 3){ + player.setAttribute("seersStatueProgress",0) + println("location progress reset") + } + } + } + } + if(player.getAttribute("seersStatueProgress",0) == 4){ + player.achievementDiaryManager.finishTask(player,DiaryType.SEERS_VILLAGE,0,1) + player.setAttribute("seersStatueComplete",true) + player.removeAttribute("seersStatueProgress") + } + } + super.locationUpdate(e, last) + } + + override fun leave(e: Entity?, logout: Boolean): Boolean { + if(e is Player && !e.isArtificial){ + val player = e.asPlayer() + if(!player.getAttribute("seersStatueComplete",false) && player.getAttribute("seersStatueProgress",-0) in 0..3){ + player.removeAttribute("seersStatueProgress") + super.leave(e, logout) + }else return super.leave(e, logout) + } + return super.leave(e, logout) + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/guild/RangingGuildPlugin.java b/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/guild/RangingGuildPlugin.java rename to Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java index 8e445cd0b..5589a4d69 100644 --- a/Server/src/main/java/core/game/content/activity/guild/RangingGuildPlugin.java +++ b/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.activity.guild; +package content.region.kandarin.seers.handlers; import java.util.List; @@ -9,18 +9,17 @@ import core.game.component.ComponentPlugin; import core.game.container.impl.EquipmentContainer; import core.plugin.Initializable; import org.rs09.consts.Items; -import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.link.diary.DiaryType; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.Skillcape; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +import core.game.global.Skillcape; +import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.crafting.TanningProduct; +import content.global.skill.crafting.TanningProduct; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.combat.equipment.Ammunition; @@ -35,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. * @@ -58,7 +59,7 @@ public final class RangingGuildPlugin extends OptionHandler { new BowArrowSalesman().init(); new WarningInterface().newInstance(arg); new CompetitionJudge().init(); - PluginManager.definePlugin(new TowerArcher()); + ClassScanner.definePlugin(new TowerArcher()); return this; } @@ -1073,21 +1074,6 @@ public final class RangingGuildPlugin extends OptionHandler { super(id, location); } - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - Player player = (Player) killer; - if (!player.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 1, 6)) { - player.setAttribute("/save:diary:seers:tower-archers", - player.getAttribute("diary:seers:tower-archers", 0) - | 1 << getId() - 688); - if (player.getAttribute("diary:seers:tower-archers", 0) == 15) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 6); - } - } - } - } @Override public AbstractNPC construct(int id, Location location, Object... objects) { @@ -1131,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/SeersCourthouseZone.kt b/Server/src/main/content/region/kandarin/seers/handlers/SeersCourthouseZone.kt new file mode 100644 index 000000000..9fd5ce160 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/handlers/SeersCourthouseZone.kt @@ -0,0 +1,26 @@ +package content.region.kandarin.seers.handlers + +import core.api.MapArea +import core.game.node.entity.Entity +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.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 + +class SeersCourthouseZone : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2735,3471,2736,3471)) + } + + override fun areaEnter(entity: Entity) { + if(entity is Player && !entity.isArtificial){ + if(!entity.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE,2,3) && entity.prayer.active.contains(PrayerType.PIETY)){ + entity.achievementDiaryManager.finishTask(entity,DiaryType.SEERS_VILLAGE,2,3) + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..8d25adaf1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/BatteredBookHandler.kt @@ -0,0 +1,41 @@ +package content.region.kandarin.seers.quest.elementalworkshop + +import content.global.handlers.iface.BookInterface +import core.api.setQuestStage +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items +import content.data.Quests +import core.api.* + +/** + * Battered book handler for the Elemental Workshop I quest + * + * @author Woah, with love + */ +class BatteredBookHandler : InteractionListener { + + companion object { + private val TITLE = "Book of the elemental shield" + private val CONTENTS = EWUtils.PAGES + + 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 + } + } + + 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 new file mode 100644 index 000000000..f43ac6bed --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt @@ -0,0 +1,607 @@ +package content.region.kandarin.seers.quest.elementalworkshop + +import core.api.* +import core.game.node.entity.skill.Skills +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.* +import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.BELLOWS_STATE +import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.FURNACE_STATE +import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.LEFT_WATER_CONTROL_STATE +import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.RIGHT_WATER_CONTROL_STATE +import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.WATER_WHEEL_STATE +import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.currentStage +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.tools.Log +import content.data.Quests + +/** + * Listeners for the Elemental Workshop I quest + * + * @author Woah, with love + */ +class EWListeners : InteractionListener { + + /* Items */ + private val batteredBook = Item(Items.BATTERED_BOOK_2886) + private val batteredKey = Item(Items.BATTERED_KEY_2887) + private val slashedBook = Item(Items.SLASHED_BOOK_9715) + private val emptyStoneBowl = Item(Items.A_STONE_BOWL_2888) + private val fullStoneBowl = Item(Items.A_STONE_BOWL_2889) + private val elementalOre = Item(Items.ELEMENTAL_ORE_2892) + private val elementalMetal = Item(Items.ELEMENTAL_METAL_2893) + private val elementalShield = Item(Items.ELEMENTAL_SHIELD_2890) + private val bellowFixReqItems = intArrayOf( + Items.NEEDLE_1733, + Items.THREAD_1734, + Items.LEATHER_1741 + ) + private val elementalShieldReqItems = intArrayOf( + Items.SLASHED_BOOK_9715, + Items.HAMMER_2347, + Items.ELEMENTAL_METAL_2893 + ) + + /* Scenery */ + private val bookcase = Scenery.BOOKCASE_26113 + private val lavaTrough = intArrayOf( + Scenery.LAVA_TROUGH_18519, Scenery.LAVA_TROUGH_18520, + Scenery.LAVA_TROUGH_18521, Scenery.LAVA_TROUGH_18522, + Scenery.LAVA_TROUGH_18523 + ) + // Handling for on/off state (one obj id for both left and right) + private val waterControls = intArrayOf( + Scenery.WATER_CONTROLS_18509, Scenery.WATER_CONTROLS_18510 + ) + + /* Animations */ + private val turnWaterControlAnimation = Animation(Animations.HUMAN_TURN_LARGE_VALVE_4861) + private val fillStoneBowlAnimation = Animation(Animations.HUMAN_FILL_STONE_BOWL_4862) + private val fixBellowsAnimation = Animation(Animations.HUMAN_SEW_LARGE_SCENERY_4862) + private val smeltElementalBar = Animation(Animations.HUMAN_FURNACE_SMELTING_3243) + private val smithElementalShield = Animation(Animations.HUMAN_COOKING_RANGE_896) + + /* Sound effects */ + 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) + private val rightWaterControlsLocation = Location.create(2726, 9907, 0) + + /* Special interactions */ + private val furnaceIDS = intArrayOf(4304, 6189, 11010, 11666, 12100, 12809, 14921, 18497, 26814, 30021, 30510, 36956, 37651) + + private val DISABLED = 0 + private val ENABLED = 1 + + override fun defineListeners() { + /* * * * * * * * * * * * * * * * * * * + * Seers Village House listeners * + * * * * * * * * * * * * * * * * * */ + // Bookcase (quest start) + on(Scenery.BOOKCASE_26113, IntType.SCENERY, "search") { player, _ -> + val stage = currentStage(player) + + if (stage < 3) { + // Player already has battered book in inventory + if (player.inventory.containsItem(batteredBook)) { + sendItemDialogue( + player, Item(Items.BATTERED_BOOK_2886), "There is a book here titled 'The Elemental Shield'. " + + "It can stay here, as you have a copy in your backpack." + ) + return@on true + } + // Player needs to receive a battered book + sendItemDialogue(player, Item(Items.BATTERED_BOOK_2886), "You find a book titled 'The Elemental Shield'.") + addItemOrDrop(player, batteredBook.id) + return@on true + } + + // --- AFTER QUEST START --- + if (player.inventory.containsItem(slashedBook)) { + sendItemDialogue(player, Item(Items.SLASHED_BOOK_9715), + "There is a book here titled 'The Elemental Shield'. " + + "It can stay here, as you have a copy in your backpack." + ) + if (player.inventory.addIfDoesntHave(batteredKey)) { + sendItemDialogue(player, Item(Items.BATTERED_KEY_2887),"You also find a key.") + } + return@on true + } + + sendItemDialogue(player, Item(Items.SLASHED_BOOK_9715), "You find a book titled 'The Elemental Shield'.") + addItemOrDrop(player, slashedBook.id) + if (player.inventory.addIfDoesntHave(batteredKey)) { + sendItemDialogue(player, Item(Items.BATTERED_KEY_2887),"You also find a key.") + } + return@on true + } + + // Knife on Battered book -> Slashed book + Battered key + onUseWith(IntType.ITEM, Items.KNIFE_946, Items.BATTERED_BOOK_2886) { player, _, with -> + val stage = currentStage(player) + if (stage >= 1) { + lock(player, 2) + player.pulseManager.run(object : Pulse() { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> sendMessage(player, "You make a small cut in the spine of the book.") + 1 -> { + sendMessage(player, "Inside you find a small, old, battered key.") + replaceSlot(player, with.asItem().slot, slashedBook) + addItemOrDrop(player, Items.BATTERED_KEY_2887) + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_I, 3) + return true + } + } + count++ + return false + } + }) + return@onUseWith true + } else { + sendMessage(player, "Nothing interesting happens.") + return@onUseWith true + } + } + + + /* * * * * * * * * * * * * * * * * * * + * Seers Village Smithy listeners * + * * * * * * * * * * * * * * * * * */ + // Odd looking wall handler + on(intArrayOf(Scenery.ODD_LOOKING_WALL_26114, Scenery.ODD_LOOKING_WALL_26115), IntType.SCENERY, "open") { player, wall -> + // Nothing interesting happens if quest wasn't started. + if (currentStage(player) < 1) { + sendMessage(player, "Nothing interesting happens.") + return@on true + } + // Player is allowed to exit without key + if (player.location == Location.create(2710, 3496, 0) || player.location == Location.create(2709, 3496, 0)) { + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, wall.asScenery()) + return@on true + } + // Player does not have battered key in inventory + if (!inInventory(player, Items.BATTERED_KEY_2887)) { + sendMessage(player, "You see a small hole in the wall but no way to open it.") + return@on true + } + // Increment quest stage + 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.") + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, wall.asScenery()) + return@on true + } + + on(Scenery.STAIRCASE_3415, IntType.SCENERY, "climb-down") { player, _ -> + val stage = currentStage(player) + // Prevent player from somehow skipping beginning quest stages + if (stage < 1) { + sendMessage(player, "Nothing interesting happens.") + return@on true + } + // Move player to downstairs location + teleport(player, Location.create(2716, 9888, 0)) + // If it is the players first time in the workshop + if (stage < 6) { + sendPlayerDialogue(player, + "Now to explore this area thoroughly, to find what " + + "forgotten secrets it contains.", core.game.dialogue.FacialExpression.NEUTRAL) + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_I, 7) + } + return@on true + } + + on(Scenery.STAIRCASE_3416, IntType.SCENERY, "climb-up") { player, _ -> + teleport(player, Location.create(2709, 3498, 0)) + return@on true + } + + + /* * * * * * * * * * * * * * * * + * Workshop Area listeners * + * * * * * * * * * * * * * * */ + // Center hatch, inaccessible + // NOTE: REMOVE ME/ADD CORRECT CHECKS FOR ELEMENTAL WORKSHOP II + on(Scenery.HATCH_18595, IntType.SCENERY, "open") { player, _ -> + sendDialogue(player, "You're unable to open the locked hatch.") + return@on true + } + + // Stone bowl box, player can get more at any time + on(Scenery.BOXES_3397, IntType.SCENERY, "search") { player, _ -> + // If the player doesn't have a stone bowl in their inventory + if (player.inventory.addIfDoesntHave(emptyStoneBowl)) { + sendMessage(player, "You find a stone bowl.") + } else { + sendMessage(player, "It's empty.") + } + return@on true + } + + // Needle crate, player can only receive once + on(Scenery.CRATE_3400, IntType.SCENERY, "search") { player, _ -> + if (!getAttribute(player, "/save:ew1:got_needle", false)) { + setAttribute(player, "/save:ew1:got_needle", true) + addItemOrDrop(player, Items.NEEDLE_1733) + sendMessage(player, "You find a needle.") + } else { + sendMessage(player, "You search the crate but find nothing.") + } + return@on true + } + + // Leather crate, player can only receive once + on(Scenery.CRATE_3394, IntType.SCENERY, "search") { player, _ -> + if (!getAttribute(player, "/save:ew1:got_leather", false)) { + setAttribute(player, "/save:ew1:got_leather", true) + addItemOrDrop(player, Items.LEATHER_1741) + sendMessage(player, "You find some leather.") + } else { + sendMessage(player, "You search the crate but find nothing.") + } + return@on true + } + + // Workbenches/Anvil + onUseWith(IntType.SCENERY, Items.ELEMENTAL_METAL_2893, Scenery.WORKBENCH_3402) { player, used, workbench -> + // Warn player their smithing level is too low to make the shield + if (player.skills.getLevel(Skills.SMITHING) < 20) { + sendMessage(player, "You need a smithing level of 20 to create an elemental shield.") + return@onUseWith true + } + // Warn player they don't have the required book in their inventory + if (!player.inventory.containsAtLeastOneItem(intArrayOf(Items.SLASHED_BOOK_9715, Items.BATTERED_BOOK_2886))) { + sendDialogue(player, "This workbench is too complicated. You need instructions to follow.") + return@onUseWith true + } + // Warn player they don't have a hammer in their inventory + if (!inInventory(player, Items.HAMMER_2347)) { + sendDialogue(player, "You don't have a tool available to shape the metal.") + return@onUseWith true + } + // Sanity error check (Should never get thrown) + if (!player.inventory.containsAll(*elementalShieldReqItems)) { + 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 + lock(player, (animationDuration(smithElementalShield) + 2)) + runTask(player) { + face(player, workbench) + animate(player, smithElementalShield) + playAudio(player, smithElementalShieldSFX) + replaceSlot(player, used.asItem().slot, elementalShield) + rewardXP(player, Skills.SMITHING, 20.0) + 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(Quests.ELEMENTAL_WORKSHOP_I).isCompleted(player)) { + player.questRepository.getQuest(Quests.ELEMENTAL_WORKSHOP_I).finish(player) + } + return@onUseWith true + } + + + /* * * * * * * * * * * * * * + * Fire room listeners * + * * * * * * * * * * * * */ + // Lava trough stone bowl (empty) + onUseWith(IntType.SCENERY, Items.A_STONE_BOWL_2888, *lavaTrough) { player, used, trough -> + lock(player, (animationDuration(fillStoneBowlAnimation) + 1)) + runTask(player) { + face(player, trough) + playAudio(player, fillStoneBowlSFX) + animate(player, fillStoneBowlAnimation) + replaceSlot(player, used.asItem().slot, fullStoneBowl) + sendMessage(player, "You fill the bowl with hot lava.") + } + return@onUseWith true + } + + // Lava trough stone bowl (filled with lava) + onUseWith(IntType.SCENERY, fullStoneBowl.id, *lavaTrough) { player, _, _ -> + sendMessage(player, "The bowl is already full of lava.") + return@onUseWith true + } + + // Pour lava into unlit furnace + onUseWith(IntType.SCENERY, fullStoneBowl.id, Scenery.FURNACE_18525) { player, used, _ -> + player.faceLocation(Location.create(2726, 9875, 0)) + lock(player, 3) + submitIndividualPulse(player, object : Pulse() { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> { + replaceSlot(player, used.asItem().slot, emptyStoneBowl) + playAudio(player, fillFurnaceWithLavaSFX) + sendMessage(player, "You empty the lava into the furnace.") + } + 2 -> { + setVarbit(player, FURNACE_STATE, ENABLED, true) + sendMessage(player, "The furnace bursts to life.") + return true + } + } + count++ + return false + } + }) + return@onUseWith true + } + + // Pour lava into lit furnace + onUseWith(IntType.SCENERY, fullStoneBowl.id, Scenery.FURNACE_18526) { player, used, _ -> + player.faceLocation(Location.create(2726, 9875, 0)) + runTask(player) { + playAudio(player, fillFurnaceWithLavaSFX) + replaceSlot(player, used.asItem().slot, emptyStoneBowl) + sendMessage(player, "The lava makes little difference to the furnace's searing heat.") + } + return@onUseWith true + } + + // Use elemental ore on lit furnace + onUseWith(IntType.SCENERY, Items.ELEMENTAL_ORE_2892, Scenery.FURNACE_18526) { player, _, _ -> + player.faceLocation(Location.create(2726, 9875, 0)) + // Warn player their smithing level is too low to smelt the ore + if (player.skills.getLevel(Skills.SMITHING) < 20) { + sendMessage(player, "You need a smithing level of 20 to smelt elemental ore.") + return@onUseWith true + } + // Warn player the bellows must be on so the furnace is properly heated + if (!EWUtils.bellowsEnabled(player)) { + sendMessage(player, "The furnace needs to be hotter to be of any use.") + return@onUseWith true + } + // Warn player they don't have enough coal to smelt the bar + if (amountInInventory(player, Items.COAL_453) < 4) { + sendMessage(player, "You need four heaps of coal to smelt elemental ore.") + return@onUseWith true + } + + val barSmeltingDuration = animationDuration(smeltElementalBar) + lock(player, barSmeltingDuration + 2) + // Run the "smelting" pulse + submitIndividualPulse(player, object : Pulse() { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> { + removeItem(player, Item(Items.COAL_453, 4)) + removeItem(player, elementalOre) + animate(player, Animations.HUMAN_FURNACE_SMELTING_3243) + sendMessage(player, "You place the elemental ore and four heaps of coal into the furnace.") + } + barSmeltingDuration -> { + addItem(player, Items.ELEMENTAL_METAL_2893) + sendMessage(player, "You retrieve a bar of elemental metal.") + rewardXP(player, Skills.SMITHING, 7.0) + return true + } + } + count++ + return false + } + }) + return@onUseWith true + } + + /* Special Interactions: + * onUseWith FURNACE scenery (this handler) + * onUseWith BLAST FURNACE BELT (this handler) + * onCast SUPERHEAT spell (ModernListeners.kt) + * SKIPPED DUE TO SPAGHETTI: on BLAST FURNACE BELT "put-ore-on" (BlastfurnaceListeners.kt) + */ + onUseWith(IntType.SCENERY, Items.ELEMENTAL_ORE_2892, *furnaceIDS) { player, _, _ -> + sendMessage(player, "This furnace is not hot enough to smelt elemental ore.") + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.ELEMENTAL_ORE_2892, Scenery.CONVEYOR_BELT_9100) { player, _, _ -> + sendMessage(player, "There would be no point in smelting that.") + return@onUseWith true + } + + /* * * * * * * * * * * * * * + * Water room listeners * + * * * * * * * * * * * * */ + // Water controls + on(waterControls, IntType.SCENERY, "turn") { player, _ -> + // Notify player they can't change water control values while water wheel is running + if (EWUtils.waterWheelEnabled(player)) { + sendMessage(player, "Now that the water wheel is running, the valve seems locked off.") + return@on true + } + + // Varbit offset + val offset: Int + // Whether it is enabled or disabled (used for xor toggle) + val enabled: Int + when (player.location) { + // Check left control + leftWaterControlsLocation -> { + offset = LEFT_WATER_CONTROL_STATE + enabled = EWUtils.leftWaterControlBit(player) xor 0x1 + } + // Check right control + rightWaterControlsLocation -> { + offset = RIGHT_WATER_CONTROL_STATE + if (EWUtils.leftWaterControlEnabled(player)) { + enabled = 0 + } else { + enabled = EWUtils.rightWaterControlBit(player) xor 0x1 + } + } + // Sanity control check + else -> { + offset = -1 + enabled = 0 + log(this::class.java, Log.ERR, "Unhandled location when determining enabled water controls! ${player.location}") + return@on false + } + } + + // Run the turn valve pulse + lock(player, (animationDuration(turnWaterControlAnimation) + 1)) + submitIndividualPulse(player, object : Pulse() { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> { + playAudio(player, turnWaterControlsSFX) + animate(player, turnWaterControlAnimation) + sendMessage(player, "You turn the handle.") + } + 3 -> { + setVarbit(player, offset, enabled, true) + return true + } + } + count++ + return false + } + }) + return@on true + } + + // Water wheel lever handler + on(Scenery.LEVER_3406, IntType.SCENERY, "pull") { player, lever -> + // Default will happen no matter what + lock(player, 2) + sendMessage(player, "You pull the lever") + replaceScenery(lever.asScenery(), Scenery.LEVER_3417, 2) + + // Check to see if the water wheel is running + if (EWUtils.waterWheelEnabled(player)) { + playAudio(player, pullLeverDisabledSFX) + sendMessage(player, "You hear the sound of a water wheel coming to a standstill.") + setVarbit(player, WATER_WHEEL_STATE, DISABLED, true) + setVarbit(player, BELLOWS_STATE, DISABLED, true) + return@on true + } + + // Check to reset both valves to the "off" position if both aren't green + if (!EWUtils.leftWaterControlEnabled(player) || !EWUtils.rightWaterControlEnabled(player)) { + playAudio(player, pullLeverResetGatesSFX) + sendMessage(player, "You hear the sound of the flow gates resetting.") + 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, WATER_WHEEL_STATE, ENABLED, true) + return@on true + } + + + /* * * * * * * * * * * * * * + * Air room listeners * + * * * * * * * * * * * * */ + on(Scenery.BELLOWS_18516, IntType.SCENERY, "fix") { player, bellows -> + player.faceLocation(Location.create(2736, 9883, 0)) + // Warn player bellows are already fixed + if (getAttribute(player, "/save:ew1:bellows_fixed", false)) { + sendMessage(player, "The bellows already look fixed to you.") + return@on true + } + // Warn player they don't have a high enough crafting level + if (player.skills.getLevel(Skills.CRAFTING) < 20) { + sendMessage(player, "You need a crafting level of 20 to fix the bellows.") + return@on true + } + // Warn player they don't have the required items + bellowFixReqItems.forEach { reqItem -> + if (!player.inventory.contains(reqItem, 1)) { + val missingItemMsg = when (reqItem) { + Items.NEEDLE_1733 -> "a needle" + Items.THREAD_1734 -> "some thread" + Items.LEATHER_1741 -> "a piece of leather" + else -> "NULL" // Should never be called; no sense in making an enum + } + sendMessage(player, "You need $missingItemMsg to fix the bellows.") + return@on true + } + } + // If the player has all the requirements to fix the bellows + lock(player, (animationDuration(fixBellowsAnimation) + 1)) + runTask(player) { + removeItem(player, Items.LEATHER_1741) + removeItem(player, Item(Items.THREAD_1734, 1)) + animate(player, fixBellowsAnimation) + sendMessage(player, "You stitch the leather over the hole in the bellows.") + setAttribute(player, "/save:ew1:bellows_fixed", true) + } + return@on true + } + + on(Scenery.LEVER_3409, IntType.SCENERY, "pull") { player, lever -> + // Warn player they have to fix the bellows before pulling the lever + if (!getAttribute(player, "/save:ew1:bellows_fixed", false)) { + sendPlayerDialogue(player, "I shouldn't risk damaging the bellows any further.") + return@on true + } + // Pull the lever checks + lock(player, 2) + sendMessage(player, "You pull the lever") + replaceScenery(lever.asScenery(), Scenery.LEVER_3417, 2) + playAudio(player, bellowLeverSFX) + + // Warn player the water wheel is not running + if (!EWUtils.waterWheelEnabled(player)) { + sendMessage(player, "Nothing happens; the lever resets itself.") + return@on true + } + // Bellows lever "OFF" state + if (EWUtils.bellowsEnabled(player)) { + sendMessage(player, "The bellows stop pumping air.") + setVarbit(player, BELLOWS_STATE, DISABLED, true) + return@on true + } + + // Bellows lever "ON" state + sendMessage(player, "The bellows pump air down the pipe.") + setVarbit(player, BELLOWS_STATE, ENABLED, true) + return@on true + } + } + override fun defineDestinationOverrides() { + setDest(IntType.SCENERY, intArrayOf(Scenery.FURNACE_18525, Scenery.FURNACE_18526), "use") { _, _ -> + return@setDest Location.create(2724,9875) + } + setDest(IntType.SCENERY, intArrayOf(Scenery.BELLOWS_18516), "fix") { _, _ -> + return@setDest Location.create(2733, 9884, 0) + } + setDest(IntType.SCENERY, intArrayOf(Scenery.ODD_LOOKING_WALL_26115), "open") { player, _ -> + if (inBorders(player, 2709, 3496, 2711, 3498)) { + return@setDest Location.create(2709, 3496, 0) + } else { + return@setDest Location.create(2709, 3495, 0) + } + } + setDest(IntType.SCENERY, intArrayOf(Scenery.ODD_LOOKING_WALL_26114), "open") { player, _ -> + if (inBorders(player, 2709, 3496, 2711, 3498)) { + return@setDest Location.create(2710, 3496, 0) + } else { + return@setDest Location.create(2710, 3495, 0) + } + } + } +} 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 new file mode 100644 index 000000000..a2629fcb1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWUtils.kt @@ -0,0 +1,107 @@ +package content.region.kandarin.seers.quest.elementalworkshop + +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 core.api.* +import content.data.Quests + +/** + * Utils for the Elemental Workshop I quest + * Book/Varp/Method Utils + * + * @author Woah, with love + */ +object EWUtils { + /** + * Represents the array of pages for both the BATTERED_BOOK and SLASHED_BOOK. + */ + val PAGES = arrayOf( + 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), + ), + 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) + ) + ), + 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) + ), + 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 = 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 getVarbit(player, LEFT_WATER_CONTROL_STATE) + } + + fun rightWaterControlBit(player: Player): Int { + return getVarbit(player, RIGHT_WATER_CONTROL_STATE) + } + + fun leftWaterControlEnabled(player: Player): Boolean { + return getVarbit(player, LEFT_WATER_CONTROL_STATE) == 1 + } + + fun rightWaterControlEnabled(player: Player): Boolean { + return getVarbit(player, RIGHT_WATER_CONTROL_STATE) == 1 + } + + fun waterWheelEnabled(player: Player): Boolean { + return getVarbit(player, WATER_WHEEL_STATE) == 1 + } + + fun bellowsEnabled(player: Player): Boolean { + return getVarbit(player, BELLOWS_STATE) == 1 + } + + fun currentStage(player: Player): Int { + return player.questRepository.getStage(Quests.ELEMENTAL_WORKSHOP_I) + } +} diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/ElementalRockNPC.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/ElementalRockNPC.kt new file mode 100644 index 000000000..a1a759eb0 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/ElementalRockNPC.kt @@ -0,0 +1,97 @@ +package content.region.kandarin.seers.quest.elementalworkshop + +import core.api.getTool +import core.api.sendDialogue +import core.game.node.entity.Entity +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.skill.Skills +import content.data.skill.SkillingTool +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 +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Npc(s): + * Earth elemental - ID: 4910 (Attack-able form) + * Elemental rock - ID: 4911 (Sleeping "mine-able" form) + * Option(s): + * "Mine" + * + * @author Woah, with love + */ +@Initializable +class ElementalRockNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,location), InteractionListener { + + private val ELEMENTAL_ROCK_TRANSFORMATION_4865 = Animation(4865) + + // Sanity transform back to elemental rock + override fun init() { + isWalks = false + isNeverWalks = true + transform(NPCs.ELEMENTAL_ROCK_4911) + super.init() + } + + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return ElementalRockNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EARTH_ELEMENTAL_4910, NPCs.ELEMENTAL_ROCK_4911) + } + + // The NPC stays in their upright form when the player + // stops attacking and can walk around + override fun defineListeners() { + on(ids, IntType.NPC, "Mine") { player, node -> + val tool: SkillingTool? = getTool(player, true) + // Check to see if the player can proc the NPC + if (tool == null) { + sendDialogue(player, "You do not have a pickaxe which you have the Mining level to use.") + return@on true + } + // Warn player they don't have a high enough mining level + if (player.skills.getStaticLevel(Skills.MINING) < 20) { + sendDialogue(player, "You need a mining level of at least 20 to mine elemental ore.") + return@on true + } + // Transform the current NPC to 4910 (monster form) + pulseManager.run(object : Pulse() { + var count = 0 + override fun pulse(): Boolean { + when (count) { + // Play the transformation Animation + 0 -> node.asNpc().animate(ELEMENTAL_ROCK_TRANSFORMATION_4865) + // Yell at the player and transform into the combat NPC 4910 + 1 -> { + node.asNpc().sendChat("Grr... Ge'roff us!") + node.asNpc().transform(NPCs.EARTH_ELEMENTAL_4910) + } + // Start attacking the player + 3 -> { + node.asNpc().attack(player) + node.asNpc().isNeverWalks = false + node.asNpc().isWalks = true + return true + } + } + count++ + return false + } + }) + return@on true + } + } + + // Reset back to rock NPC + override fun finalizeDeath(killer: Entity?) { + isWalks = false + isNeverWalks = true + transform(NPCs.ELEMENTAL_ROCK_4911) + super.finalizeDeath(killer) + } +} \ 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 new file mode 100644 index 000000000..097b61613 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/ElementalWorkshopQuest.kt @@ -0,0 +1,145 @@ +package content.region.kandarin.seers.quest.elementalworkshop + +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.world.map.Location +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 + * + * Original Development Team: + * Developer: Dylan C + * Graphics: Tom W + * Quality Assurance: Andrew C + * Audio: Ian T + * + * RS2 Rework Team: + * Developer: Dylan C + * Graphics: Tom W + * Quality Assurance: Andrew C + * QuestHelp: Rob M + * + * 2009Scape adaptation: + * @author Woah, with love + */ +@Initializable +class ElementalWorkshopQuest : Quest(Quests.ELEMENTAL_WORKSHOP_I, 52, 51, 1), Commands { + + override fun newInstance(`object`: Any?): Quest { + return this + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + player ?: return + var line = 11 + if (stage == 0) { + line(player, "I can start this quest by reading a", line++) + line(player, "!!book?? found in !!Seers village??.", line++) + line++ + line(player, "Minimum requirements:", line++) + line(player, if (player.skills.getStaticLevel(Skills.MINING) >= 20) "---Level 20 Mining/--" else "!!Level 20 Mining??", line++) + line(player, if (player.skills.getStaticLevel(Skills.SMITHING) >= 20) "---Level 20 Smithing/--" else "!!Level 20 Smithing??", line++) + line(player, if (player.skills.getStaticLevel(Skills.CRAFTING) >= 20) "---Level 20 Crafting/--" else "!!Level 20 Crafting??", line++) + } else { + // Player read through book on bookshelf + if (stage < 100) { + if (stage >= 1) { + line(player, "---I have found a battered book in a house in Seers Village./--", line++) + line(player, "---It tells of magic ore and a workshop created to fashion it./--", line++) + line++ + if (stage <= 2) { + line(player, "Where is the workshop and how do I get in?", line++) + } + } + + if (stage >= 3) { + line(player, "---Cutting open the spine of the book with a knife,/--", line++) + line(player, "---I found a key hidden under the leather binding./--", line++) + line++ + if (stage <= 4) { + line(player, "Where is the workshop and how do I get in?", line++) + } + } + + if (stage >= 5) { + line(player, "---I have found a secret door in the Seers Village smithy/--", line++) + line++ + line(player, "---Where is the workshop and how do I get in?/--", line++) + line++ + } + + // Player climbed down staircase + if (stage == 7) { + line(player, "There is obviously lots to do here.", line++) + } + } else { + line(player, "---I have found a battered book in a house in Seers Village./--", line++) + line(player, "---It tells of magic ore and a workshop created to fashion it./--", line++) + line++ + 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++) + } + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + player.packetDispatch.sendString("1 Quest Point,", 277, 8 + 2) + player.packetDispatch.sendString("5,000 Crafting XP", 277, 9 + 2) + player.packetDispatch.sendString("5,000 Smithing XP", 277, 10 + 2) + player.packetDispatch.sendString("The ability to make", 277, 11 + 2) + player.packetDispatch.sendString("elemental shields.", 277, 12 + 2) + player.packetDispatch.sendItemZoomOnInterface(Items.ELEMENTAL_SHIELD_2890, 235, 277, 3 + 2) + player.skills.addExperience(Skills.CRAFTING, 5000.0) + player.skills.addExperience(Skills.SMITHING, 5000.0) + player.questRepository.syncronizeTab(player) + } + + override fun getConfig(player: Player?, stage: Int): IntArray { + if (stage >= 100) return intArrayOf(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, 1048576) + if (stage > 0) return intArrayOf(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, 3) + else return intArrayOf(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, 0) + } + + override fun defineCommands() { + 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) + setVarp(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, 0) + player.teleport(Location.create(2715, 3481, 0)) + player.inventory.clear() + addItem(player, Items.KNIFE_946) + addItem(player, Items.BRONZE_PICKAXE_1265) + addItem(player, Items.NEEDLE_1733) + addItem(player, Items.THREAD_1734) + addItem(player, Items.LEATHER_1741) + addItem(player, Items.HAMMER_2347) + addItem(player, Items.COAL_453, 4) + } + 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, 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) + } + } +} 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 new file mode 100644 index 000000000..01f24b0e6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/SlashedBookHandler.kt @@ -0,0 +1,31 @@ +package content.region.kandarin.seers.quest.elementalworkshop + +import content.global.handlers.iface.BookInterface +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +/** + * Slashed book handler for the Elemental Workshop I quest + * + * @author Woah, with love + */ +class SlashedBookHandler : InteractionListener { + companion object { + private val TITLE = "Book of the elemental shield" + private val CONTENTS = EWUtils.PAGES + + 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.SLASHED_BOOK_9715, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ArheinMCDialogue.kt b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ArheinMCDialogue.kt new file mode 100644 index 000000000..1309b550f --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ArheinMCDialogue.kt @@ -0,0 +1,30 @@ +package content.region.kandarin.seers.quest.merlinsquest + +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 + */ + +/** + * This class handles dialogue for Arhein for the Merlin's Crystal quest + */ + +class ArheinMCDialogue (val questStage: Int) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.NEUTRAL, "Can you drop me off on the way down please?").also { stage++ } + 1 -> { + npcl(FacialExpression.ANNOYED,"I don't think Sir Mordred would like that. He wants as few outsiders visiting as possible. I wouldn't want to lose his business.") + val quest = player!!.questRepository.getQuest(Quests.MERLINS_CRYSTAL) + player!!.questRepository.setStage(quest, 40) + stage = END_DIALOGUE + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/BeggarDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/BeggarDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/BeggarDialogue.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/BeggarDialogue.java index fafb2f487..286b845f1 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/BeggarDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/BeggarDialogue.java @@ -1,11 +1,12 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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.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/java/core/game/content/quest/members/merlinscrystal/CandleMakerDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CandleMakerDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/CandleMakerDialogue.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CandleMakerDialogue.java index ee078e04e..46b402ed3 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/CandleMakerDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CandleMakerDialogue.java @@ -1,7 +1,7 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; import core.cache.def.impl.NPCDefinition; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.npc.NPC; @@ -9,8 +9,9 @@ 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.Plugin; -import rs09.game.system.config.ShopParser; -import rs09.plugin.PluginManager; +import core.game.shops.Shops; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the dialogue plugin used to handle the candle maker npc. @@ -45,7 +46,7 @@ public final class CandleMakerDialogue extends DialoguePlugin { @Override public void init() { super.init(); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { @@ -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) { - ShopParser.Companion.openUid(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) { - ShopParser.Companion.openUid(player, 198); + Shops.openId(player, 56); } else { npc.openShop(player); } diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/CrateCutscenePlugin.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CrateCutscenePlugin.java similarity index 92% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/CrateCutscenePlugin.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CrateCutscenePlugin.java index b026b7195..aab24ce71 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/CrateCutscenePlugin.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/CrateCutscenePlugin.java @@ -1,8 +1,8 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/KingArthurDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/KingArthurDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/KingArthurDialogue.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/KingArthurDialogue.java index 95944860c..ea84952e5 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/KingArthurDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/KingArthurDialogue.java @@ -1,13 +1,14 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; import org.rs09.consts.Items; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.item.Item; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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 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/java/core/game/content/quest/members/merlinscrystal/MerlinCrystal.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystal.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/MerlinCrystal.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystal.java index 7d1cbd416..15a6c5ce4 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/MerlinCrystal.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystal.java @@ -1,9 +1,10 @@ -package core.game.content.quest.members.merlinscrystal; +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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the merlin's crystal quest. @@ -16,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 @@ -76,9 +77,8 @@ public final class MerlinCrystal extends Quest { @Override public Quest newInstance(Object object) { - PluginManager.definePlugins(new CrateCutscenePlugin(), + ClassScanner.definePlugins(new CrateCutscenePlugin(), new MerlinCrystalPlugin(), - new ArheinShopDialogue(), new BeggarDialogue(), new CandleMakerDialogue(), new KingArthurDialogue(), diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/MerlinCrystalOptionPlugin.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalOptionPlugin.java similarity index 88% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/MerlinCrystalOptionPlugin.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalOptionPlugin.java index 431d99244..82a53c48c 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/MerlinCrystalOptionPlugin.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; @@ -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/java/core/game/content/quest/members/merlinscrystal/MerlinCrystalPlugin.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalPlugin.java similarity index 81% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/MerlinCrystalPlugin.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalPlugin.java index af194ccbc..e90ecbfd1 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/MerlinCrystalPlugin.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinCrystalPlugin.java @@ -1,21 +1,17 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; -import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; -import core.game.content.global.action.DropItemHandler; +import core.game.activity.ActivityManager; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialogueInterpreter; +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.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; @@ -23,13 +19,13 @@ 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 rs09.game.world.GameWorld; -import core.game.world.map.Direction; +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.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Handles the Merlin's Crystal Dialogue/Interactions. @@ -59,22 +55,21 @@ public final class MerlinCrystalPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new MerlinCrystalDialogue()); - PluginManager.definePlugin(new MerlinCrystalItemHandler()); + ClassScanner.definePlugin(new MerlinCrystalDialogue()); + ClassScanner.definePlugin(new MerlinCrystalItemHandler()); SceneryDefinition.forId(63).getHandlers().put("option:hide-in", this); SceneryDefinition.forId(40026).getHandlers().put("option:climb-up", this); SceneryDefinition.forId(72).getHandlers().put("option:open", this); 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/java/core/game/content/quest/members/merlinscrystal/RenegadeKnightDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/RenegadeKnightDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/RenegadeKnightDialogue.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/RenegadeKnightDialogue.java index c66a97092..1750d17a9 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/RenegadeKnightDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/RenegadeKnightDialogue.java @@ -1,9 +1,9 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; public class RenegadeKnightDialogue extends DialoguePlugin { public RenegadeKnightDialogue() {} diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirGawainDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirGawainDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirGawainDialogue.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirGawainDialogue.java index 65e78b594..0f3d5b858 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirGawainDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirGawainDialogue.java @@ -1,9 +1,10 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; import core.game.node.entity.player.link.quest.Quest; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/quest/members/merlinscrystal/SirKayDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirKayDialogue.java similarity index 86% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirKayDialogue.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirKayDialogue.java index 9492f6580..594645e0d 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirKayDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirKayDialogue.java @@ -1,11 +1,12 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; 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.content.dialogue.DialoguePlugin; +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; @@ -52,7 +53,6 @@ public final class SirKayDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - AchievementDiary diary = player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE); int level = 2; switch (stage) { @@ -66,22 +66,17 @@ public final class SirKayDialogue extends DialoguePlugin { stage = 1; break; case 2: - if (!diary.isStarted(level)) { - player("Do you have an Achievement Diary for me?"); - stage = 100; - break; - } - else if (diary.isLevelRewarded(level) && !player.hasItem(diary.getType().getRewards()[level][0])) { + if (AchievementDiary.canReplaceReward(player, DiaryType.SEERS_VILLAGE, level)) { player("I seem to have lost my seers' headband..."); stage = 80; break; } - else if (diary.isLevelRewarded(level)) { + else if (AchievementDiary.hasClaimedLevelRewards(player, DiaryType.SEERS_VILLAGE, level)) { player("Can you remind me what my headband does?"); stage = 90; break; } - else if (diary.isComplete(level, true)) { + else if (AchievementDiary.canClaimLevelRewards(player, DiaryType.SEERS_VILLAGE, level)) { player("Greetings, Sir Kay. I have completed all of the Hard", "tasks in my Achievement Diary. May I have a reward?"); stage = 200; break; @@ -154,7 +149,7 @@ public final class SirKayDialogue extends DialoguePlugin { break; case 80: - player.getInventory().add(diary.getType().getRewards(level)[0], player); + AchievementDiary.grantReplacement(player, DiaryType.SEERS_VILLAGE, level); npc("Here's your replacement. Please be more careful."); stage = 999; break; @@ -179,12 +174,11 @@ public final class SirKayDialogue extends DialoguePlugin { stage++; break; case 201: - if (!player.hasItem(diary.getType().getRewards()[level][0])) { + if (!player.hasItem(AchievementDiary.getRewards(DiaryType.SEERS_VILLAGE, level)[0])) { npc("I need your headband. Come back when", "you have it."); stage = 999; } else { - diary.setLevelRewarded(level); - player.getInventory().add(diary.getType().getRewards()[level][1]); + AchievementDiary.flagRewarded(player, DiaryType.SEERS_VILLAGE, level); sendDialogue("You hand Sir Kay your headband and he concentrates for a", "moment. Some mysterious knightly energy passes through his hands", "and he gives the headband back to you, along with an old lamp."); stage++; } diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirLancelotDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLancelotDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirLancelotDialogue.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLancelotDialogue.java index 310f7b2c1..b8d5b8d8a 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirLancelotDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLancelotDialogue.java @@ -1,9 +1,10 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; import core.game.node.entity.player.link.quest.Quest; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/quest/members/merlinscrystal/SirLucan.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLucan.java similarity index 90% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirLucan.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLucan.java index a78534880..2732d5667 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirLucan.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirLucan.java @@ -1,10 +1,11 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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 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/java/core/game/content/quest/members/merlinscrystal/SirMordredNPC.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirMordredNPC.java similarity index 91% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirMordredNPC.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirMordredNPC.java index d49803fa0..6065a1b2e 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirMordredNPC.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirMordredNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; @@ -6,9 +6,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.system.task.Pulse; -import rs09.game.world.GameWorld; +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/java/core/game/content/quest/members/merlinscrystal/SirPalomedes.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirPalomedes.java similarity index 87% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirPalomedes.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirPalomedes.java index ff3d47c3d..50b4e2de2 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/SirPalomedes.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/SirPalomedes.java @@ -1,10 +1,11 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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 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 new file mode 100644 index 000000000..e1ed69c03 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/TheLadyOfTheLake.kt @@ -0,0 +1,161 @@ +package content.region.kandarin.seers.quest.merlinsquest + +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.node.item.Item +import org.rs09.consts.Items +import content.data.Quests + +/** + * Handles the LadyOfTheLake dialogue. + * + * @author Vexia + * @author Splinter + * @author pain + */ +class TheLadyOfTheLake(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + stage = if (!player.hasItem(Item(35, 1))) { + npc("Good day to you, " + (if (player.isMale) "sir" else "madam") + ".") + 0 + } else if(player.achievementDiaryManager.getDiary(DiaryType.SEERS_VILLAGE).isComplete(2) + && player.equipment.contains(14631,1) && player.equipment.contains(35,1)){ + npcl(FacialExpression.HAPPY,"I am the Lady of the Lake.") + 110 + } + else { + npc("Good day to you, " + (if (player.isMale) "sir" else "madam") + ".") + 699 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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) { + 1 -> player("Who are you?").also { stage = 100 } + 2 -> player("I seek the sword Excalibur.").also { + if (quest.getStage(player) < 50) { + stage = 250 + } else { + stage = 161 + } + } + 3 -> player("Good day.").also { stage = 10000 } + } + 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.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++ } + 113 -> npc("You shall be rewarded handsomely!").also { stage++ } + 114 -> { + interpreter.sendItemMessage( + Items.EXCALIBUR_35, + "The Lady of the Lake reaches out and touches the", + "blade Excalibur which seems to vibrate with new power." + ).also { + if (player.inventory.containsAtLeastOneItem(Items.EXCALIBUR_35)) { + player.inventory.remove(Item(Items.EXCALIBUR_35)) + player.inventory.add(Item(14632)) + } + else if (player.equipment.containsAtLeastOneItem(Items.EXCALIBUR_35)) { + player.equipment.remove(Item(Items.EXCALIBUR_35)) + player.equipment.add(Item(14632), true, false) + } + stage++ + } + } + 115 -> player("What does this do then?").also { stage++ } + 116 -> npc( + "I made the blade more powerful, and also gave it a", + "rather healthy effect when you use the special." + ).also { stage++ } + 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.") + 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 + npc("Aye, I have that artefact in my possession.").also { stage = 300 } + } else if (quest.getStage(player) >= 70) { + npc( + "... But you have already proved thyself to be worthy", + "of wielding it once already. I shall return it to you", + "if you can prove yourself to still be worthy.").also { stage = 162 } + } + 162 -> player("... And how can I do that?").also { stage = 163 } + 163 -> npc("Why, by proving yourself to be above material goods.").also { stage = 164 } + + 164 -> npc("500 coins ought to do it.").also { stage = 166 } + 166 -> if (player.inventory.contains(995, 500)) { + player("Ok, here you go.").also { stage = 168 } + } else { + player("I don't have that kind of money...").also { stage = 167 } + } + 167 -> npc("Well, come back when you do.").also { stage = 10000 } + 168 -> if (player.inventory.freeSlots() == 0) { + player("Sorry, I don't seem to have enough inventory space.").also { stage = 10000 } + } else if (player.inventory.contains(995, 500)) { + player.inventory.remove(Item(995, 500)) + player.inventory.add(Item(35, 1)) + npc( + "You are still worthy to wield Excalibur! And thanks", + "for the cash! I felt like getting a new haircut!" + ) + stage = 170 + } else { + player("I don't have that kind of money...").also { stage = 167 } + } + 170 -> interpreter.sendDialogue("The lady of the Lake hands you Excalibur.").also { stage = 10000 } + + 250 -> npc("I am afraid I do not know what you are talking about.").also { stage = 10000 } + + 300 -> npc("'Tis very valuable, and not an artefact to be given", "away lightly.").also { stage = 301 } + 301 -> npc("I would want to give it away only to one who is worthy", "and good.").also { stage = 302 } + 302 -> player("And how am I meant to prove that?").also { stage = 303 } + 303 -> npc("I shall set a test for you.").also { stage = 304 } + 304 -> npc( + "First I need you to travel to Port Sarim. Then go to", + "the upstairs room of the jeweller's shop there." + ).also { stage = 305 } + 305 -> player("Ok. That seems easy enough.").also { + quest.setStage(player, 60) + stage = 10000 + } + 699 -> options("Who are you?", "Good day.").also { stage = 700 } + 700 -> when (buttonId) { + 1 -> player("Who are you?").also { stage = 720 } + 2 -> player("Good day.").also { stage = 10000 } + } + 720 -> npc("I am the Lady of the Lake.").also { stage = 10000 } + 10000 -> end() + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return TheLadyOfTheLake(player) + } + + override fun getIds(): IntArray { + return intArrayOf(250) + } +} diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/ThrantaxDialogue.java b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/merlinscrystal/ThrantaxDialogue.java rename to Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxDialogue.java index 1737c9d5b..d30d76d33 100644 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/ThrantaxDialogue.java +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxDialogue.java @@ -1,11 +1,12 @@ -package core.game.content.quest.members.merlinscrystal; +package content.region.kandarin.seers.quest.merlinsquest; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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.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 new file mode 100644 index 000000000..72e26afca --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/BrotherMaledictDialogue.kt @@ -0,0 +1,38 @@ +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 + * Does not take quests into consideration + */ + +@Initializable +class BrotherMaledictDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"The blessings of Saradomin be with you child.").also { stage = 99 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BrotherMaledictDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BROTHER_MALEDICT_4878) + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/CarolineDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/CarolineDialogue.kt new file mode 100644 index 000000000..cc3b0ad95 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/CarolineDialogue.kt @@ -0,0 +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 + +@Initializable +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 new file mode 100644 index 000000000..5dddfe275 --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/ColONiallDialogue.kt @@ -0,0 +1,56 @@ +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 + * Does not take quest into consideration + */ + +@Initializable +class ColONiallDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npc(FacialExpression.FRIENDLY, "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?): DialoguePlugin { + return ColONiallDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.COL_ONIALL_4872) + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/EzekialLovecraftDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/EzekialLovecraftDialogue.kt new file mode 100644 index 000000000..4f9c68fbe --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/EzekialLovecraftDialogue.kt @@ -0,0 +1,64 @@ +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 + * Does not take quests into consideration + */ + +@Initializable +class EzekialLovecraftDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.FRIENDLY,"Well hello there; welcome to our little village. Pray, stay awhile.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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(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(FacialExpression.FRIENDLY, "Sniff. Yes, everyone says that.").also { stage = 99 } + + + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return EzekialLovecraftDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EZEKIAL_LOVECRAFT_4856) + } +} diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartDialogue.kt new file mode 100644 index 000000000..5f7432efa --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/HolgartDialogue.kt @@ -0,0 +1,26 @@ +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 +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +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_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 new file mode 100644 index 000000000..970d61f4d --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/JebDialogue.kt @@ -0,0 +1,38 @@ +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 + * Does not take quest into consideration + */ + +@Initializable +class JebDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Hello there.").also { stage = 99 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return JebDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.JEB_4895) + } +} 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 new file mode 100644 index 000000000..dafb2f88f --- /dev/null +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/MayorHobbDialogue.kt @@ -0,0 +1,38 @@ +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 + * Does not take quest into consideration + */ + +@Initializable +class MayorHobbDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.FRIENDLY,"Well hello there; welcome to our little village. Pray, stay awhile.").also { stage = 99 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return MayorHobbDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MAYOR_HOBB_4874) + } +} 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/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/java/core/game/content/dialogue/AleckDialogue.java b/Server/src/main/content/region/kandarin/yanille/dialogue/AleckDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/AleckDialogue.java rename to Server/src/main/content/region/kandarin/yanille/dialogue/AleckDialogue.java index 1111f51c3..9eb3ebeeb 100644 --- a/Server/src/main/java/core/game/content/dialogue/AleckDialogue.java +++ b/Server/src/main/content/region/kandarin/yanille/dialogue/AleckDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.kandarin.yanille.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/dialogue/FenitasDialogue.java b/Server/src/main/content/region/kandarin/yanille/dialogue/FenitasDialogue.java similarity index 76% rename from Server/src/main/java/core/game/content/dialogue/FenitasDialogue.java rename to Server/src/main/content/region/kandarin/yanille/dialogue/FenitasDialogue.java index 19cac707b..a562fe43c 100644 --- a/Server/src/main/java/core/game/content/dialogue/FenitasDialogue.java +++ b/Server/src/main/content/region/kandarin/yanille/dialogue/FenitasDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -37,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; } @@ -46,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.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/java/core/game/content/dialogue/MagicStoreDialogue.java b/Server/src/main/content/region/kandarin/yanille/dialogue/MagicStoreDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/MagicStoreDialogue.java rename to Server/src/main/content/region/kandarin/yanille/dialogue/MagicStoreDialogue.java index 911400983..ba13e7c3d 100644 --- a/Server/src/main/java/core/game/content/dialogue/MagicStoreDialogue.java +++ b/Server/src/main/content/region/kandarin/yanille/dialogue/MagicStoreDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.kandarin.yanille.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; 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 new file mode 100644 index 000000000..509ea4e58 --- /dev/null +++ b/Server/src/main/content/region/kandarin/yanille/handlers/YanilleAgilityDungeon.kt @@ -0,0 +1,189 @@ +package content.region.kandarin.yanille.handlers + +import core.api.* +import org.rs09.consts.Items +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.node.entity.skill.Skills +import content.global.skill.agility.AgilityHandler +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.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.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.Initializable +import core.tools.RandomFunction + +/** + * The herb items. + */ +val SINISTER_CHEST_HERBS = arrayOf(Item(205, 2), Item(207, 3), Item(209), Item(211), Item(213), Item(219)) + +public class YanilleAgilityDungeonListeners : InteractionListener { + override fun defineListeners() { + ZoneBuilder.configure(YanilleAgilityDungeon()) + + on(1728, IntType.SCENERY, "climb-down") { player, _ -> + player.teleport(Location(2620, 9565, 0)) + return@on true + } + on(1729, IntType.SCENERY, "climb-up") { player, _ -> + player.teleport(Location(2620, 9496, 0)) + return@on true + } + on(2316, IntType.SCENERY, "climb-up") { player, _ -> + player.teleport(Location.create(2569, 9525, 0)) + return@on true + } + on(intArrayOf(2318, 2317), IntType.SCENERY, "climb-up", "climb-down") { player, target -> + if (player.skills.getLevel(Skills.AGILITY) < 67) { + player.dialogueInterpreter.sendDialogue("You need an Agility level of at least 67 in order to do this.") + return@on true + } + val loc = if (target.id == 2317) { Location(2615, 9503, 0) } else { Location(2617, 9572, 0) } + core.game.global.action.ClimbActionHandler.climb(player, if(target.id == 2317) { core.game.global.action.ClimbActionHandler.CLIMB_UP } else { core.game.global.action.ClimbActionHandler.CLIMB_DOWN }, loc) + player.sendMessage("You climb " + if(target.id == 2317) { "up" } else { "down" } + " the pile of rubble...") + player.skills.addExperience(Skills.AGILITY, 5.5, true) + return@on true + } + on(intArrayOf(35969, 2303), IntType.SCENERY, "walk-across") { player, target -> + handleBalancingLedge(player, target.asScenery()) + return@on true + } + on(377, IntType.SCENERY, "open") { player, target -> + if (!player.inventory.contains(Items.SINISTER_KEY_993, 1)) { + player.sendMessage("The chest is locked.") + } else { + if (player.getInventory().freeSlots() == 0) { + 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") + 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) + } + } + return@on true + } + on(378, IntType.SCENERY, "search") { player, _ -> + player.sendMessage("The chest is empty.") + return@on true + } + on(378, IntType.SCENERY, "shut") { player, _ -> + return@on true + } + + } + + /** + * Handles the balancing ledge. + * @param player the player. + * @param object the object. + */ + 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 + } + 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)) { + player.lock(3) + GameWorld.Pulser.submit(object : Pulse(2, player) { + override public fun pulse(): Boolean { + AgilityHandler.fail(player, 1, Location(2572, 9570, 0), Animation.create(761 - diff), RandomFunction.random(1, 3), "You lost your balance!") + return true + } + }) + } 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) + } + +} + +/** + * Handles the salarin twisted npc. + * @author Vexia + */ +@Initializable +public class SalarinTwistedNPC : AbstractNPC { + + /** + * The spell ids. + */ + val SPELL_IDS = intArrayOf( 1, 4, 6, 8 ) + + /** + * Constructs a new {@Code SalarinTwistedNPC} {@Code + * Object} + */ + public constructor(): super(-1, null) {} + + /** + * Constructs a new {@Code SalarinTwistedNPC} {@Code + * Object} + * @param id the id. + * @param location the location. + */ + public constructor(id: Int, location: Location) : super(id, location) { + super.setAggressive(true) + } + + override public fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + return SalarinTwistedNPC(id, location) + } + + override fun checkImpact(state: BattleState) { + if (state.style != CombatStyle.MAGIC) { + state.neutralizeHits() + return + } + if (state.spell == null) { + state.neutralizeHits() + return + } + val spell = state.spell + for (id in SPELL_IDS) { + if (id == spell.spellId) { + state.estimatedHit = state.maximumHit + return + } + } + state.neutralizeHits() + } + + override public fun getIds(): IntArray { + return intArrayOf(205) + } + +} + +/** + * Handles the yanille agility dungeon. + * @author Vexia + */ +public class YanilleAgilityDungeon : MapZone("Yanille agility", true) { + override public fun configure() { + register(ZoneBorders(2544, 9481, 2631, 9587)) + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/BrimhavenBartenderDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenBartenderDialogue.java similarity index 79% rename from Server/src/main/java/core/game/content/dialogue/BrimhavenBartenderDialogue.java rename to Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenBartenderDialogue.java index eec906e9e..231610533 100644 --- a/Server/src/main/java/core/game/content/dialogue/BrimhavenBartenderDialogue.java +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenBartenderDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; import core.plugin.Initializable; @@ -58,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; } @@ -73,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; } @@ -90,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: @@ -113,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/java/core/game/content/dialogue/BrimhavenPirateDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenPirateDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/BrimhavenPirateDialogue.java rename to Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenPirateDialogue.java index 28c7eb27f..0c1c3cd9d 100644 --- a/Server/src/main/java/core/game/content/dialogue/BrimhavenPirateDialogue.java +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenPirateDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; 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 new file mode 100644 index 000000000..e202dd5e9 --- /dev/null +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/DavonDialogue.java @@ -0,0 +1,96 @@ +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 used for the davon npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class DavonDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code DavonDialogue} {@code Object}. + */ + public DavonDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code DavonDialogue} {@code Object}. + * @param player the player. + */ + public DavonDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new DavonDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.SUSPICIOUS, "Pssst! Come here if you want to do some amulet", "trading."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "What are you selling?", "What do you mean pssst?", "Why don't you ever restock some types of amulets?"); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "What are you selling?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "What do you mean pssst?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Why don't you ever restock some types of amulets?"); + stage = 30; + break; + } + break; + case 10: + end(); + npc.openShop(player); + break; + case 20: + interpreter.sendDialogues(npc, FacialExpression.SUSPICIOUS, "Errr, I was...I was clearing my throat."); + stage = 21; + break; + case 21: + end(); + break; + case 30: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Some of these amulets are very hard to get. I have to", "wait until an adventurer supplies me."); + stage = 31; + break; + case 31: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 588 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/LubufuDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/LubufuDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/LubufuDialogue.java rename to Server/src/main/content/region/karamja/brimhaven/dialogue/LubufuDialogue.java index dd96e353e..0ad34449c 100644 --- a/Server/src/main/java/core/game/content/dialogue/LubufuDialogue.java +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/LubufuDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; 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/java/core/game/content/dialogue/SandyDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/SandyDialogue.java similarity index 85% rename from Server/src/main/java/core/game/content/dialogue/SandyDialogue.java rename to Server/src/main/content/region/karamja/brimhaven/dialogue/SandyDialogue.java index be3217aee..736424e92 100644 --- a/Server/src/main/java/core/game/content/dialogue/SandyDialogue.java +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/SandyDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/SanibochDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/SanibochDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/SanibochDialogue.java rename to Server/src/main/content/region/karamja/brimhaven/dialogue/SanibochDialogue.java index 0288317eb..0ed5006ad 100644 --- a/Server/src/main/java/core/game/content/dialogue/SanibochDialogue.java +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/SanibochDialogue.java @@ -1,9 +1,12 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.item.Item; +import org.rs09.consts.Items; /** * Handles the SanibochDialogue dialogue. @@ -127,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 new file mode 100644 index 000000000..2e39db2ad --- /dev/null +++ b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenDungeonListeners.kt @@ -0,0 +1,94 @@ +package content.region.karamja.brimhaven.handlers + +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 org.rs09.consts.Scenery + +class BrimhavenDungeonListeners : InteractionListener { + + 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) + val STAIRS = intArrayOf(5094,5096,5097,5098) + 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 + } + + on(STAIRS, IntType.SCENERY, "walk-up","walk-down"){ player, node -> + BrimhavenUtils.handleStairs(node.asScenery(), player) + return@on true + } + + on(STEPPING_STONES, IntType.SCENERY, "jump-from"){ player, node -> + BrimhavenUtils.handleSteppingStones(player, node.asScenery()) + return@on true + } + + on(VINES, IntType.SCENERY, "chop-down"){ player, node -> + BrimhavenUtils.handleVines(player, node.asScenery()) + return@on true + } + + on(SANIBOCH, IntType.NPC, "pay"){ player, node -> + player.dialogueInterpreter.open(SANIBOCH,node.asNpc(),10) + return@on true + } + + on(LOGS, IntType.SCENERY, "walk-across"){ player, node -> + + if (player.skills.getLevel(Skills.AGILITY) < 30) { + player.packetDispatch.sendMessage("You need an agility level of 30 to cross this.") + return@on true + } + + if(node.id == 5088){ + AgilityHandler.walk( + player, + -1, + player.location, + Location.create(2687, 9506, 0), + Animation.create(155), + 0.0, + null + ) + } else { + AgilityHandler.walk( + player, + -1, + player.location, + Location.create(2682, 9506, 0), + Animation.create(155), + 0.0, + null + ) + } + + return@on true + } + } +} \ No newline at end of file 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/kotlin/rs09/game/interaction/region/dungeons/brimhaven/BrimhavenUtils.kt b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenUtils.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/interaction/region/dungeons/brimhaven/BrimhavenUtils.kt rename to Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenUtils.kt index 3131a58a0..2aaefa255 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/region/dungeons/brimhaven/BrimhavenUtils.kt +++ b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenUtils.kt @@ -1,19 +1,18 @@ -package rs09.game.interaction.region.dungeons.brimhaven +package content.region.karamja.brimhaven.handlers -import api.ContentAPI -import core.game.content.global.action.ClimbActionHandler +import core.api.* import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder 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.entity.skill.agility.AgilityHandler -import core.game.node.entity.skill.gather.SkillingTool +import content.global.skill.agility.AgilityHandler +import content.data.skill.SkillingTool 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 rs09.game.world.GameWorld +import core.game.world.GameWorld object BrimhavenUtils { fun getVineDestination(player: Player, node: Scenery): Location { @@ -29,14 +28,14 @@ object BrimhavenUtils { fun handleStairs(node: Scenery, player: Player){ when(node.id){ - 5094 -> ClimbActionHandler.climb(player, null, Location.create(2643, 9594, 2)) - 5096 -> ClimbActionHandler.climb(player, null, Location.create(2649, 9591, 0)) + 5094 -> core.game.global.action.ClimbActionHandler.climb(player, null, Location.create(2643, 9594, 2)) + 5096 -> core.game.global.action.ClimbActionHandler.climb(player, null, Location.create(2649, 9591, 0)) 5097 -> { // Climb the stairs within Brimhaven Dungeon player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 16) - ClimbActionHandler.climb(player, null, Location.create(2636, 9510, 2)) + core.game.global.action.ClimbActionHandler.climb(player, null, Location.create(2636, 9510, 2)) } - 5098 -> ClimbActionHandler.climb(player, null, Location.create(2636, 9517, 0)) + 5098 -> core.game.global.action.ClimbActionHandler.climb(player, null, Location.create(2636, 9517, 0)) } } @@ -49,8 +48,8 @@ object BrimhavenUtils { val dir = AgilityHandler.forceWalk(player, -1, player.location, node.location, Animation.create(769), 10, 0.0, null).direction val loc = player.location - ContentAPI.registerLogoutListener(player, "steppingstone"){p -> - ContentAPI.teleport(p, loc) + registerLogoutListener(player, "steppingstone"){p -> + teleport(p, loc) } GameWorld.Pulser.submit(object : Pulse(3, player) { @@ -73,7 +72,7 @@ object BrimhavenUtils { } override fun stop() { - ContentAPI.clearLogoutListener(player, "steppingstone") + clearLogoutListener(player, "steppingstone") super.stop() } }) diff --git a/Server/src/main/java/core/game/interaction/npc/SanibochPlugin.java b/Server/src/main/content/region/karamja/brimhaven/handlers/SanibochPlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/npc/SanibochPlugin.java rename to Server/src/main/content/region/karamja/brimhaven/handlers/SanibochPlugin.java index a012c9e25..2c2e0496b 100644 --- a/Server/src/main/java/core/game/interaction/npc/SanibochPlugin.java +++ b/Server/src/main/content/region/karamja/brimhaven/handlers/SanibochPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.region.karamja.brimhaven.handlers; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/content/dialogue/CaptainShanksDialogue.java b/Server/src/main/content/region/karamja/dialogue/CaptainShanksDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/CaptainShanksDialogue.java rename to Server/src/main/content/region/karamja/dialogue/CaptainShanksDialogue.java index 015ac50e3..ddf3f18e5 100644 --- a/Server/src/main/java/core/game/content/dialogue/CaptainShanksDialogue.java +++ b/Server/src/main/content/region/karamja/dialogue/CaptainShanksDialogue.java @@ -1,8 +1,9 @@ -package core.game.content.dialogue; +package content.region.karamja.dialogue; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; import org.rs09.consts.Items; -import core.game.content.global.travel.ship.Ships; +import content.global.travel.ship.Ships; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -20,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() { /** @@ -29,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/java/core/game/content/dialogue/CustomsOfficerDialogue.java b/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/CustomsOfficerDialogue.java rename to Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java index 37ab988b8..b32c1bfa0 100644 --- a/Server/src/main/java/core/game/content/dialogue/CustomsOfficerDialogue.java +++ b/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java @@ -1,12 +1,17 @@ -package core.game.content.dialogue; +package content.region.karamja.dialogue; -import core.game.content.global.travel.ship.Ships; +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.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 @@ -50,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; @@ -182,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/java/core/game/content/dialogue/JungleForesterDialogue.java b/Server/src/main/content/region/karamja/dialogue/JungleForesterDialogue.java similarity index 84% rename from Server/src/main/java/core/game/content/dialogue/JungleForesterDialogue.java rename to Server/src/main/content/region/karamja/dialogue/JungleForesterDialogue.java index d92570368..23946f84d 100644 --- a/Server/src/main/java/core/game/content/dialogue/JungleForesterDialogue.java +++ b/Server/src/main/content/region/karamja/dialogue/JungleForesterDialogue.java @@ -1,10 +1,10 @@ -package core.game.content.dialogue; +package content.region.karamja.dialogue; +import core.game.dialogue.DialoguePlugin; 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; -import core.game.node.item.Item; /** * Represents the dialoge used for a jungle forester. @@ -12,12 +12,6 @@ import core.game.node.item.Item; */ @Initializable public class JungleForesterDialogue extends DialoguePlugin { - - /** - * The achievement diary. - */ - private AchievementDiary diary; - /** * Constructs a new {@code KalebParamaya} {@code Object} */ @@ -51,15 +45,12 @@ public class JungleForesterDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 41: - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.KARAMJA); - } - if (diary.isComplete(2) && !diary.isLevelRewarded(2)) { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.KARAMJA, 2)) { player("I've done all the hard tasks in my Karamja", "Achievement Diary."); stage = 440; break; } - if (diary.isLevelRewarded(2) && diary.isComplete(2) && !player.hasItem(diary.getType().getRewards(2)[0])) { + if (AchievementDiary.canReplaceReward(player, DiaryType.KARAMJA, 2)) { player("I've seemed to have lost my gloves.."); stage = 450; break; @@ -68,9 +59,6 @@ public class JungleForesterDialogue extends DialoguePlugin { stage++; break; case 42: - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.KARAMJA); - } switch (buttonId) { case 1: player("What is the Achievement Diary?"); @@ -98,10 +86,7 @@ public class JungleForesterDialogue extends DialoguePlugin { stage++; break; case 442: - diary.setLevelRewarded(2); - for (Item i : diary.getType().getRewards(2)) { - player.getInventory().add(i, player); - } + AchievementDiary.flagRewarded(player, DiaryType.KARAMJA, 2); npc("These Karamja gloves are a symbol of your explorin'", "on the island. All the merchants will recognise them", "and mabe give you a discount. I'll", "have a word with some of the seafarin' folk who sail to"); stage += 2; case 443: @@ -113,7 +98,7 @@ public class JungleForesterDialogue extends DialoguePlugin { stage = 41; break; case 450: - player.getInventory().add(diary.getType().getRewards(2)[0], player); + AchievementDiary.grantReplacement(player, DiaryType.KARAMJA, 2); npc("You better be more careful this time."); stage = 41; break; 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 new file mode 100644 index 000000000..8692e3a63 --- /dev/null +++ b/Server/src/main/content/region/karamja/dialogue/LuthasDialogue.java @@ -0,0 +1,158 @@ +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.game.node.item.GroundItem; +import core.game.node.item.GroundItemManager; +import core.plugin.Initializable; +import core.game.node.item.Item; + +/** + * Represents the dialogue plugin used for the luthas npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class LuthasDialogue extends DialoguePlugin { + + /** + * Represents the coins item. + */ + private static final Item COINS = new Item(995, 30); + + /** + * Constructs a new {@code LuthasDialogue} {@code Object}. + */ + public LuthasDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code LuthasDialogue} {@code Object}. + * @param player the player. + */ + public LuthasDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new LuthasDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + if (player.getSavedData().getGlobalData().isLuthasTask()) { + final int current = player.getSavedData().getGlobalData().getKaramjaBananas(); + if (current >= 10) { + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "I've filled a crate with bananas."); + stage = 905; + return true; + } + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Have you completed your task yet?"); + stage = 900; + return true; + } + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello I'm Luthas, I run the banana plantation here."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "Could you offer me employment on your plantation?", "That customs officer is annoying isn't he?"); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Could you offer me employment on your plantation?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "That customs officer is annoying isn't she?"); + stage = 20; + break; + } + break; + case 10: + 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.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.HAPPY, "If you could go fill it up with bananas, I'll pay you 30", "gold."); + stage = 13; + player.getSavedData().getGlobalData().setLuthasTask(true); + break; + case 13: + end(); + break; + case 20: + 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, FacialExpression.HALF_GUILTY, "No, the crate isn't full yet."); + stage = 901; + } else { + + } + break; + case 901: + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well come back when it is."); + stage = 21; + break; + case 905: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Well done, here's your payment."); + stage = 906; + break; + case 906: + end(); + player.getPacketDispatch().sendMessage("Luthas hands you 30 coins."); + player.getSavedData().getGlobalData().setKaramjaBannanas(0); + player.getSavedData().getGlobalData().setLuthasTask(false); + if (player.getAttribute("stashed-rum", false)) { + player.removeAttribute("stashed-rum"); + player.setAttribute("/save:wydin-rum", true); + } + if (!player.getInventory().add(COINS)) { + GroundItemManager.create(new GroundItem(COINS, player.getLocation(), player)); + } + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 379 }; + } +} 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.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 new file mode 100644 index 000000000..cda31648c --- /dev/null +++ b/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt @@ -0,0 +1,204 @@ +package content.region.karamja.diary + +import core.game.event.NPCKillEvent +import core.game.event.PickUpEvent +import core.game.event.ResourceProducedEvent +import core.api.inBorders +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 core.game.diary.DiaryEventHookBase +import core.game.diary.DiaryLevel + +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) + private val TAI_BWO_WANNAI_WOODCUTTING_AREA = ZoneBorders(2817, 3076, 2829, 3090) + + private val KET_ZEKS = arrayOf( + NPCs.KET_ZEK_2743, + NPCs.KET_ZEK_2744 + ) + + private val METAL_DRAGONS = arrayOf( + NPCs.BRONZE_DRAGON_1590, + NPCs.IRON_DRAGON_1591, + NPCs.STEEL_DRAGON_1592 + ) + + object EasyTasks { + const val PICK_5_BANANAS = 0 + const val TRAVEL_TO_MOSS_GIANTS_VIA_ROPESWING = 1 + const val BRIMHAVEN_MINE_GOLD = 2 + const val MUSA_POINT_CHARTER_SHIP_TO_PORT_SARIM = 3 + const val BRIMHAVEN_CHARTER_SHIP_TO_ARDOUGNE = 4 + const val CAIRN_ISLE_VISIT = 5 + const val USE_FISHING_SPOTS_BANANA_PLANTATION = 6 + const val PICK_5_SEAWEED = 7 + const val ATTEMPT_TZHAAR_FIGHT_PITS_OR_FIGHT_CAVE = 8 + const val POTHOLE_DUNGEON_KILL_JOGRE = 9 + } + + object MediumTasks { + const val BRIMHAVEN_CLAIM_AGILITY_ARENA_TICKET = 0 + const val FIND_HIDDEN_WALL_BELOW_VOLCANO = 1 + const val CRANDOR_ISLAND_VISIT = 2 + const val USE_VIGROY_HAJEDY_CARTS = 3 + const val TAI_BWO_WANNAI_EARN_FULL_FAVOR = 4 + const val COOK_SPIDER_ON_STICK = 5 + const val CAIRN_ISLE_CHARTER_LADY_OF_THE_WAVES = 6 + const val CUT_TEAK_TREE = 7 + const val CUT_MAHOGANY_TREE = 8 + const val CATCH_KARAMBWAN = 9 + const val EXCHANGE_GEMS_TUBER_TRADING_STICKS_FOR_MACHETE = 10 + const val GNOME_GLIDE_TO_KARAMJA = 11 + const val BRIMHAVEN_GROW_HEALTHY_FRUIT_TREE = 12 + const val TRAP_HORNED_GRAAHK = 13 + const val BRIMHAVEN_DUNGEON_CHOP_VINES = 14 + const val BRIMHAVEN_DUNGEON_CROSS_LAVA_STEPPING_STONES = 15 + const val BRIMHAVEN_DUNGEON_CLIMB_STAIRS = 16 + const val CHARTER_SHIP_EAST = 17 + const val MINE_RED_TOPAZ = 18 + } + + object HardTasks { + const val FIGHT_PITS_BECOME_CHAMPION = 0 + const val FIGHT_CAVE_KILL_KET_ZEK = 1 + const val EAT_OOMLIE_WRAP = 2 + const val CRAFT_NATURE_RUNES = 3 + const val COOK_KARAMBWAN_PROPERLY = 4 + const val KHARAZI_JUNGLE_KILL_DEATHWING = 5 + const val VOLCANO_USE_CROSSBOW_SHORTCUT_SOUTH = 6 + const val PICK_5_PALM_LEAVES = 7 + const val DURADEL_LAPALOK_GET_SLAYER_TASK = 8 + const val BRIMHAVEN_DUNGEON_KILL_METAL_DRAGON = 9 + } + } + + 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, + DiaryLevel.MEDIUM, + MediumTasks.MINE_RED_TOPAZ + ) + } + + 11569 -> if (event.itemId == Items.BANANA_1963) { + progressIncrementalTask( + player, + DiaryLevel.EASY, + EasyTasks.PICK_5_BANANAS, + ATTRIBUTE_BANANA_PICKED, + 5 + ) + } + } + + when { + inBorders(player, BANANA_FISHING_SPOT_AREA) -> { + when (event.source.id) { + NPCs.FISHING_SPOT_323, + NPCs.FISHING_SPOT_333 -> { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.USE_FISHING_SPOTS_BANANA_PLANTATION + ) + } + } + } + + inBorders(player, TAI_BWO_WANNAI_WOODCUTTING_AREA) -> { + when (event.itemId) { + Items.MAHOGANY_LOGS_6332 -> { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CUT_MAHOGANY_TREE + ) + } + + Items.TEAK_LOGS_6333 -> { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CUT_TEAK_TREE + ) + } + } + } + } + } + + override fun onNpcKilled(player: Player, event: NPCKillEvent) { + when (player.viewport.region.id) { + 10899, 10900 -> if (event.npc.id in METAL_DRAGONS) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.BRIMHAVEN_DUNGEON_KILL_METAL_DRAGON + ) + } + + 11412 -> if (event.npc.id == NPCs.JOGRE_113) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.POTHOLE_DUNGEON_KILL_JOGRE + ) + } + + /* Ket-Zek only appears in an instanced Fight Cave area. */ + else -> if (event.npc.id in KET_ZEKS) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.FIGHT_CAVE_KILL_KET_ZEK + ) + } + } + } + + override fun onPickedUp(player: Player, event: PickUpEvent) { + when { + inBorders(player, MAIN_ISLAND_AREA) -> { + if (event.itemId == Items.SEAWEED_401) { + progressIncrementalTask( + player, + DiaryLevel.EASY, + EasyTasks.PICK_5_SEAWEED, + ATTRIBUTE_SEAWEED_PICKED, + 5 + ) + } + + if (event.itemId == Items.PALM_LEAF_2339) { + progressIncrementalTask( + player, + DiaryLevel.HARD, + HardTasks.PICK_5_PALM_LEAVES, + ATTRIBUTE_PALM_LEAF_PICKED, + 5 + ) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/BananaCrateDialogue.java b/Server/src/main/content/region/karamja/handlers/BananaCrateDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/BananaCrateDialogue.java rename to Server/src/main/content/region/karamja/handlers/BananaCrateDialogue.java index e6b7dbb3c..a7eaeb46a 100644 --- a/Server/src/main/java/core/game/content/dialogue/BananaCrateDialogue.java +++ b/Server/src/main/content/region/karamja/handlers/BananaCrateDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.karamja.handlers; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Initializable; diff --git a/Server/src/main/java/core/game/content/zone/CairnIslandZone.java b/Server/src/main/content/region/karamja/handlers/CairnIslandZone.java similarity index 96% rename from Server/src/main/java/core/game/content/zone/CairnIslandZone.java rename to Server/src/main/content/region/karamja/handlers/CairnIslandZone.java index e6ce81b89..b56d6ab6b 100644 --- a/Server/src/main/java/core/game/content/zone/CairnIslandZone.java +++ b/Server/src/main/content/region/karamja/handlers/CairnIslandZone.java @@ -1,4 +1,4 @@ -package core.game.content.zone; +package content.region.karamja.handlers; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/interaction/npc/CustomsOfficerPlugin.java b/Server/src/main/content/region/karamja/handlers/CustomsOfficerPlugin.java similarity index 88% rename from Server/src/main/java/core/game/interaction/npc/CustomsOfficerPlugin.java rename to Server/src/main/content/region/karamja/handlers/CustomsOfficerPlugin.java index 068de18ce..a29ad6b17 100644 --- a/Server/src/main/java/core/game/interaction/npc/CustomsOfficerPlugin.java +++ b/Server/src/main/content/region/karamja/handlers/CustomsOfficerPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.region.karamja.handlers; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; @@ -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 new file mode 100644 index 000000000..8e4462110 --- /dev/null +++ b/Server/src/main/content/region/karamja/handlers/JungleBushHandler.kt @@ -0,0 +1,59 @@ +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 +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 + +/** + * Handles the chopping down of dense jungle, mainly to grant access to the Kharazi Jungle. + * @author Ceikry + */ +class JungleBushHandler : InteractionListener { + val chopped_bush = 2895 + val chop_a = Animation(910) + val chop_b = Animation(2382) + val ids = intArrayOf(2892,2893) + + override fun defineListeners() { + + on(ids, IntType.SCENERY, "chop-down"){ player, node -> + val toChop = node.asScenery() + if(checkRequirement(player)){ + GameWorld.Pulser.submit(object : Pulse(0){ + var ticks = 0 + override fun pulse(): Boolean { + when(ticks++){ + 0 -> player.animator.animate(chop_a).also { player.lock() } + 1 -> player.animator.animate(chop_b) + 2 -> SceneryBuilder.replace(toChop, Scenery(chopped_bush, toChop.location, toChop.rotation),20) + 3 -> {player.walkingQueue.reset(); player.walkingQueue.addPath(toChop.location.x, toChop.location.y,true)} + 4 -> player.unlock().also { return true } + } + return false + } + }) + } else { + player.sendMessage("You need a machete to get through this dense jungle.") + } + return@on true + } + + } + + 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 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/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/java/core/game/interaction/object/MossGiantRopePlugin.java b/Server/src/main/content/region/karamja/handlers/MossGiantRopePlugin.java similarity index 94% rename from Server/src/main/java/core/game/interaction/object/MossGiantRopePlugin.java rename to Server/src/main/content/region/karamja/handlers/MossGiantRopePlugin.java index 86b6faf0b..ed17bbc7f 100644 --- a/Server/src/main/java/core/game/interaction/object/MossGiantRopePlugin.java +++ b/Server/src/main/content/region/karamja/handlers/MossGiantRopePlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.object; +package content.region.karamja.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/quest/members/junglepotion/JunglePotion.java b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/members/junglepotion/JunglePotion.java rename to Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java index 92a012675..5639c3b20 100644 --- a/Server/src/main/java/core/game/content/quest/members/junglepotion/JunglePotion.java +++ b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java @@ -1,19 +1,20 @@ -package core.game.content.quest.members.junglepotion; +package content.region.karamja.quest.junglepotion; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.herblore.Herbs; +import content.global.skill.herblore.Herbs; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; import core.game.world.update.flag.context.Animation; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; import core.tools.StringUtils; -import core.game.content.quest.members.junglepotion.JunglePotionPlugin.JogreCavernDialogue; +import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * The main type or the jungle potion quest. @@ -23,23 +24,18 @@ import core.game.content.quest.members.junglepotion.JunglePotionPlugin.JogreCave @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 public Quest newInstance(Object object) { - PluginManager.definePlugin(new TrufitusDialogue()); - PluginManager.definePlugin(new JogreCavernDialogue()); - PluginManager.definePlugin(new JunglePotionPlugin()); + ClassScanner.definePlugin(new TrufitusDialogue()); + ClassScanner.definePlugin(new JunglePotionPlugin.JogreCavernDialogue()); + ClassScanner.definePlugin(new JunglePotionPlugin()); return this; } @@ -79,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); + setVarbit(player, 897, 2); + setVarbit(player, 898, 2); + setVarbit(player, 899, 2); + setVarbit(player, 900, 2); + setVarbit(player, 896, 2, true); } @Override @@ -116,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/java/core/game/content/quest/members/junglepotion/JunglePotionPlugin.java b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotionPlugin.java similarity index 92% rename from Server/src/main/java/core/game/content/quest/members/junglepotion/JunglePotionPlugin.java rename to Server/src/main/content/region/karamja/quest/junglepotion/JunglePotionPlugin.java index d8d4bf7a5..5828060ba 100644 --- a/Server/src/main/java/core/game/content/quest/members/junglepotion/JunglePotionPlugin.java +++ b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotionPlugin.java @@ -1,8 +1,9 @@ -package core.game.content.quest.members.junglepotion; +package content.region.karamja.quest.junglepotion; +import content.data.Quests; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -12,7 +13,7 @@ import core.game.node.scenery.SceneryBuilder; import core.game.world.map.Location; import core.plugin.Plugin; -import core.game.content.quest.members.junglepotion.JunglePotion.JungleObjective; +import content.region.karamja.quest.junglepotion.JunglePotion.JungleObjective; /** * Handles any interactions of the jungle potion quest. @@ -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/java/core/game/content/quest/members/junglepotion/TrufitusDialogue.java b/Server/src/main/content/region/karamja/quest/junglepotion/TrufitusDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/quest/members/junglepotion/TrufitusDialogue.java rename to Server/src/main/content/region/karamja/quest/junglepotion/TrufitusDialogue.java index 05864a255..267bef03c 100644 --- a/Server/src/main/java/core/game/content/quest/members/junglepotion/TrufitusDialogue.java +++ b/Server/src/main/content/region/karamja/quest/junglepotion/TrufitusDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.quest.members.junglepotion; +package content.region.karamja.quest.junglepotion; -import core.game.content.dialogue.DialoguePlugin; +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.content.quest.members.junglepotion.JunglePotion.JungleObjective; +import content.region.karamja.quest.junglepotion.JunglePotion.JungleObjective; /** * Handles the trufitus dialogue. @@ -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 new file mode 100644 index 000000000..a8d2da26e --- /dev/null +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/ArdougneGuideBook.kt @@ -0,0 +1,126 @@ +package content.region.karamja.quest.tribaltotem + +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 + +/** + * Ardougne Guide Book + * https://www.youtube.com/watch?v=-Qburz3T00o + * @author ovenbreado + * @author Splinter + * @author Phil + */ +class ArdougneGuideBook : InteractionListener { + companion object { + 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), + ), + 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 new file mode 100644 index 000000000..54ec527f3 --- /dev/null +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/CrompertyDialogue.kt @@ -0,0 +1,135 @@ +package content.region.karamja.quest.tribaltotem + +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.update.flag.context.Animation +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.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 + } + + 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 -> 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(Quests.RUNE_MYSTERIES)){ + EssenceTeleport.teleport(npc,player) + end() + } + else stage = 2 + } + } + 2 -> npcl(core.game.dialogue.FacialExpression.THINKING,"I have no idea what you're talking about.") + 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Not when you combine them it isn't! Invent MAGIC things!").also { stage++ } + 6 -> options("So what have you invented?","Well I shall leave you to your inventing").also { stage++ } + 7 -> when(buttonId){ + 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So, what have you invented?").also { stage = 10 } + 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Well I shall leave you to your inventing").also { stage++ } + } + 8 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Thanks for dropping by! Stop again anytime!").also { stage = 1000 } + + 10 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah! My latest invention is my patent pending teleportation block! It emits a low level magical signal,").also { stage++ } + 11 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"that will allow me to locate it anywhere in the world, and teleport anything directly to it! I hope to revolutionize the entire teleportation system!").also { stage++ } + 12 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Don't you think I'm great? Uh, I mean it's great?").also { stage++ } + 13 -> options("So where is the other block?","Can I be teleported please?").also { stage++ } + 14 -> when(buttonId){ + 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So where is the other block?").also { stage = 15 } + 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++ } + 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 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++ } + 26 -> options("Yes, that sounds good. Teleport me!","That sounds dangerous. Leave me here.").also { stage++ } + 27 -> when(buttonId){ + 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Yes, that sounds good. Teleport me!").also { stage = 30 } + 2 -> playerl(core.game.dialogue.FacialExpression.THINKING,"That sounds dangerous. Leave me here.").also { stage++ } + } + 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(Quests.TRIBAL_TOTEM) && player.questRepository.getStage(Quests.TRIBAL_TOTEM) < 50) { + 35 + } + else 31 + } + 31 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Hmmm... that's odd... I can't seem to get a signal...").also { stage++ } + 32 -> playerl(core.game.dialogue.FacialExpression.SAD,"Oh well, never mind.").also { stage = 1000 } + + 35 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Okay, I got a signal. Get ready!").also { + TribalTotemTeleport(player, npc) + end() + } + + 1000 -> end() + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return CrompertyDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(844,NPCs.WIZARD_CROMPERTY_2328) + } + + fun TribalTotemTeleport(player : Player, npc: NPC) { + val LOCATIONS = arrayOf(Location.create(2649, 3272, 0),Location.create(2642, 3321, 0)) + npc.animate(Animation(437)) + npc.faceTemporary(player, 1) + npc.graphics(Graphics(108)) + player.lock() + 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(Quests.TRIBAL_TOTEM) >= 25 + override fun pulse(): Boolean { + when(counter++){ + 2 -> { + if(delivered) { + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player,30) + player.properties.teleportLocation = LOCATIONS[1] + } + else player.properties.teleportLocation = LOCATIONS[0] + } + 3 ->{ + player.graphics(Graphics(110)) + player.unlock() + return true + } + } + return false + } + }) + } +} + diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/HoracioDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/HoracioDialogue.kt new file mode 100644 index 000000000..09cc8aea8 --- /dev/null +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/HoracioDialogue.kt @@ -0,0 +1,59 @@ +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 content.data.Quests + +@Initializable +class HoracioDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + 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(FacialExpression.HAPPY,"It's a fine day to be out in a garden, isn't it? ") + stage = 0 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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(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(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(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?): DialoguePlugin { + return HoracioDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(845) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/KangaiMauDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/KangaiMauDialogue.kt new file mode 100644 index 000000000..1e72fbd22 --- /dev/null +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/KangaiMauDialogue.kt @@ -0,0 +1,92 @@ +package content.region.karamja.quest.tribaltotem + +import core.api.isQuestComplete +import core.api.removeItem +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 content.data.Quests + +@Initializable +class KangaiMauDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + 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, Quests.TRIBAL_TOTEM)) { + npcl(FacialExpression.HAPPY, "Many greetings esteemed thief.") + stage = 40 + } + else if(player.inventory.containsAtLeastOneItem(Items.TOTEM_1857)){ + npcl(FacialExpression.ASKING,"Have you got our totem back?") + stage = 35 + } + else if(player.questRepository.hasStarted(Quests.TRIBAL_TOTEM)){ + npcl(FacialExpression.ASKING,"Have you got our totem back?") + stage = 30 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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(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(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(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(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(FacialExpression.HAPPY,"Best of luck with that adventurer").also { stage = 1000 } + + 30 -> playerl(FacialExpression.SAD,"Not yet, sorry.").also { stage = 1000 } + + 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, Quests.TRIBAL_TOTEM) && removeItem(player, Items.TOTEM_1857)) { + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).finish(player) + stage = 1000 + } else { + stage = 1000 + } + } + + 40 -> player(FacialExpression.NEUTRAL, "Hey.").also { stage = 1000 } + + 1000 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return KangaiMauDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(846) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..372a51f0c --- /dev/null +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/TTDoorCodeInterfaceListener.kt @@ -0,0 +1,146 @@ +package content.region.karamja.quest.tribaltotem + +import core.api.closeInterface +import core.api.sendMessage +import core.api.setInterfaceText +import core.game.world.map.Location +import core.game.interaction.InterfaceListener + +class TTDoorCodeInterfaceListener : InterfaceListener { + + override fun defineInterfaceListeners() { + 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) + player.setAttribute("tt-letter-two", 0) + player.setAttribute("tt-letter-three", 0) + player.setAttribute("tt-letter-four", 0) + return@onOpen true + } + + onClose(DOORLOCKINTERFACE) { player, component -> + player.removeAttribute("tt-letter-one") + player.removeAttribute("tt-letter-two") + player.removeAttribute("tt-letter-three") + 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, 13) + } else { + (player.incrementAttribute("tt-letter-one", -1)) + 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, 13) + } else { + (player.incrementAttribute("tt-letter-one", 1)) + 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, 14) + } else { + (player.incrementAttribute("tt-letter-two", -1)) + 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, 14) + } else { + (player.incrementAttribute("tt-letter-two", 1)) + 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, 15) + } else { + (player.incrementAttribute("tt-letter-three", -1)) + 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, 15) + } else { + (player.incrementAttribute("tt-letter-three", 1)) + 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, 16) + } else { + (player.incrementAttribute("tt-letter-four", -1)) + 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, 16) + } else { + (player.incrementAttribute("tt-letter-four", 1)) + 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)] + val letterThree = LETTERS[player.getAttribute("tt-letter-three", 0)] + val letterFour = LETTERS[player.getAttribute("tt-letter-four", 0)] + + if (letterOne == "K" && letterTwo == "U" && letterThree == "R" && letterFour == "T") { + player.setAttribute("/save:TT:DoorUnlocked",true) + sendMessage(player, "You hear a satisfying click, signifying that the door has been unlocked") + closeInterface(player) + } + else { + sendMessage(player,"You hear a satisfying click, and then a worrying thunk.") + sendMessage(player,"The floor opens up beneath you sending you plummeting down") + sendMessage(player,"to the sewers.") + player.teleport(Location.create(2641, 9721, 0)) + closeInterface(player) + } + } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt new file mode 100644 index 000000000..97a16f7d9 --- /dev/null +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemListeners.kt @@ -0,0 +1,111 @@ +package content.region.karamja.quest.tribaltotem + +import core.api.* +import core.game.node.entity.skill.Skills +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 core.game.interaction.InteractionListener +import core.game.interaction.IntType +import content.data.Quests + +class TribalTotemListeners : InteractionListener { + + val frontDoor = 2706 + val wizCrate = 2707 + val realCrate = 2708 + val label = Items.ADDRESS_LABEL_1858 + val lockedDoor = 2705 + val stairs = 2711 + val closedChest = 2709 + val openChest = 2710 + + override fun defineListeners() { + on(frontDoor, IntType.SCENERY, "Open"){ player, door -> + if(player.questRepository.getStage(Quests.TRIBAL_TOTEM) >= 35){ + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,door.asScenery()) + } + else { + sendMessage(player,"The door is locked shut.") + } + return@on true + } + + on(realCrate, IntType.SCENERY, "Investigate"){ player, node -> + 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(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 + } + + on(wizCrate, IntType.SCENERY, "Investigate"){ player, node -> + sendDialogue(player,"There is a label on this crate: Senior Patents Clerk, Chamber of Invention, The Wizards' Tower, Misthalin. The crate is securely fastened shut and ready for delivery.") + return@on true + } + + 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(Quests.TRIBAL_TOTEM).setStage(player,20) + return@onUseWith true + } + + on(lockedDoor, IntType.SCENERY, "Open"){ player, node -> + if(player.getAttribute("TT:DoorUnlocked",false) == true){ + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,node.asScenery()) + }else{ + openInterface(player,369) + } + return@on true + } + + on(stairs, IntType.SCENERY, "Climb-up"){ player, node -> + if(player.getAttribute("TT:StairsChecked",false)){ + core.game.global.action.ClimbActionHandler.climb(player, Animation(828), Location.create(2629, 3324, 1)) + } + else{ + sendMessage(player,"You set off a trap and the stairs give way under you, dropping you into the sewers.") + player.teleport(Location.create(2641, 9721, 0)) + } + return@on true + } + + on(stairs, IntType.SCENERY, "Investigate"){ player, node -> + if(player.getSkills().getStaticLevel(Skills.THIEVING) >= 21){ + sendDialogue(player,"Your trained senses as a thief enable you to see that there is a trap in these stairs. You make a note of its location for future reference when using these stairs") + player.setAttribute("/save:TT:StairsChecked",true) + }else{ + sendDialogue(player,"You don't see anything out of place on these stairs.") + } + return@on true + } + + on(closedChest, IntType.SCENERY, "Open"){ player, node -> + SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(2710)) + return@on true + } + + on(openChest, IntType.SCENERY, "Close"){ player, node -> + SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(2709)) + return@on true + } + + on(openChest, IntType.SCENERY, "Search"){ player, node -> + if(!player.inventory.containsAtLeastOneItem(Items.TOTEM_1857)){ + sendDialogue(player,"Inside the chest you find the tribal totem.") + 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!") + } + return@on true + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt new file mode 100644 index 000000000..c89baea9e --- /dev/null +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/TribalTotemQuest.kt @@ -0,0 +1,83 @@ +package content.region.karamja.quest.tribaltotem + +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.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(Quests.TRIBAL_TOTEM,126,125,1,200,0,1,5){ + class SkillRequirement(val skill: Int?, val level: Int?) + + val requirements = arrayListOf() + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 11 + val started = player?.questRepository?.getStage(Quests.TRIBAL_TOTEM)!! > 0 + + if(!started){ + 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 Thieving??",line++, player?.skills?.getStaticLevel(Skills.THIEVING)!! >= 21) + } + else if(started && stage != 100){ + if(stage >= 10){ + line(player,"I agreed to help !!Kangai Mau?? on Brimhaven recover",line++,stage>15) + line(player,"the tribal totem stolen from his village by",line++,stage>15) + line(player,"!!Lord Handelmort??.",line++,stage>10) + } + if(stage >= 20){ + line(player,"I found a package due for delivery to !!Lord Handelmort??",line++,stage>25) + line(player,"at the !!G.P.D.T. Depot??, and swapped the label for the",line++,stage>25) + line(player,"!!Wizard Cromperty's?? experimental teleport block.",line++,stage>25) + } + if(stage >= 30){ + line(player,"I got the !!G.P.D.T.?? men to deliver the teleport block to",line++,stage>35) + line(player,"!!Lord Handelmort?? and teleported myself inside.",line++,stage>35) + } + } + else if(stage == 100){ + if(stage == 100){ + line(player,"After bypassing the traps and security inside the mansion I was able",line++) + line(player,"to reclaim the totem, and take it back to !!Kangai Mau??, who rewarded",line++) + line(player,"me for all of my help.",line++) + line += 1 + line(player,"QUEST COMPLETE!",line++ +1) + } + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.TOTEM_1857,230,277,5) + drawReward(player,"1 Quest point",ln++) + drawReward(player,"1,775 Thieving XP",ln++) + drawReward(player,"5 Swordfish",ln++) + rewardXP(player,Skills.THIEVING,1775.0) + if(!player.inventory.add(Item(Items.SWORDFISH_373,5))){ + GroundItemManager.create(Item(Items.SWORDFISH_373,5),player) + } + cleanTTAttributes(player) + } + + override fun newInstance(`object`: Any?): Quest { + requirements.add(SkillRequirement(Skills.THIEVING, 21)) + return this + } + + private fun cleanTTAttributes(player: Player){ + player.removeAttribute("TT:StairsChecked") + player.removeAttribute("TT:DoorUnlocked") + } + +} 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 new file mode 100644 index 000000000..3a2655ac4 --- /dev/null +++ b/Server/src/main/content/region/karamja/shilo/dialogue/FernaheiDialogue.kt @@ -0,0 +1,55 @@ +package content.region.karamja.shilo.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 FernaheiDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Welcome to Fernahei's Fishing Shop Bwana!", + "Would you like to see my items?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + options("Yes please!", "No, but thanks for the offer.").also { stage++ } + } + + 1 -> when (buttonId) { + 1 -> player(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "No, but thanks for the offer.").also { stage = 20 } + } + + 10 -> { + end().also { npc.openShop(player) } + } + + 20 -> { + npc(FacialExpression.FRIENDLY, "That's fine, and thanks for your interest.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FernaheiDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FERNAHEI_517) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/KalebParamaya.java b/Server/src/main/content/region/karamja/shilo/dialogue/KalebParamaya.java similarity index 84% rename from Server/src/main/java/core/game/content/dialogue/KalebParamaya.java rename to Server/src/main/content/region/karamja/shilo/dialogue/KalebParamaya.java index e23607abb..977ee1000 100644 --- a/Server/src/main/java/core/game/content/dialogue/KalebParamaya.java +++ b/Server/src/main/content/region/karamja/shilo/dialogue/KalebParamaya.java @@ -1,10 +1,10 @@ -package core.game.content.dialogue; +package content.region.karamja.shilo.dialogue; +import core.game.dialogue.DialoguePlugin; 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; -import core.game.node.item.Item; /** * Represents the dialoge used for kaleb paramaya. @@ -12,12 +12,6 @@ import core.game.node.item.Item; */ @Initializable public class KalebParamaya extends DialoguePlugin { - - /** - * The achievement diary. - */ - private AchievementDiary diary; - /** * Constructs a new {@code KalebParamaya} {@code Object} */ @@ -51,15 +45,12 @@ public class KalebParamaya extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 41: - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.KARAMJA); - } - if (diary.isComplete(1) && !diary.isLevelRewarded(1)) { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.KARAMJA, 1)) { player("I've done all the medium tasks in my Karamja", "Achievement Diary."); stage = 440; break; } - if (diary.isLevelRewarded(1) && diary.isComplete(1) && !player.hasItem(diary.getType().getRewards(1)[0])) { + if (AchievementDiary.canReplaceReward(player, DiaryType.KARAMJA, 1)) { player("I've seemed to have lost my gloves.."); stage = 450; break; @@ -68,9 +59,6 @@ public class KalebParamaya extends DialoguePlugin { stage++; break; case 42: - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.KARAMJA); - } switch (buttonId) { case 1: player("What is the Achievement Diary?"); @@ -98,10 +86,7 @@ public class KalebParamaya extends DialoguePlugin { stage++; break; case 442: - diary.setLevelRewarded(1); - for (Item i : diary.getType().getRewards(1)) { - player.getInventory().add(i, player); - } + AchievementDiary.flagRewarded(player, DiaryType.KARAMJA, 1); npc("These Karamja gloves are a symbol of your explorin'", "on the island. All the merchants will recognise them", "and mabe give you a discount. I'll", "have a word with some of the seafarin' folk who sail to"); stage++; break; @@ -114,7 +99,7 @@ public class KalebParamaya extends DialoguePlugin { stage = 41; break; case 450: - player.getInventory().add(diary.getType().getRewards(1)[0], player); + AchievementDiary.grantReplacement(player, DiaryType.KARAMJA, 1); npc("You better be more careful this time."); stage = 41; break; diff --git a/Server/src/main/content/region/karamja/shilo/dialogue/ObliDialogue.kt b/Server/src/main/content/region/karamja/shilo/dialogue/ObliDialogue.kt new file mode 100644 index 000000000..6299c94ee --- /dev/null +++ b/Server/src/main/content/region/karamja/shilo/dialogue/ObliDialogue.kt @@ -0,0 +1,51 @@ +package content.region.karamja.shilo.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 ObliDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Welcome to Obli's General Store Bwana!", + "Would you like to see my items?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + options("Yes please!", "No, but thanks for the offer.").also { stage++ } + } + + 1 -> when (buttonId) { + 1 -> player(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "No, but thanks for the offer.").also { stage = 99 } + } + + 10 -> { + end().also { npc.openShop(player) } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ObliDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.OBLI_516) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/SeravelDialogue.java b/Server/src/main/content/region/karamja/shilo/dialogue/SeravelDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/SeravelDialogue.java rename to Server/src/main/content/region/karamja/shilo/dialogue/SeravelDialogue.java index 41102970a..2f3009766 100644 --- a/Server/src/main/java/core/game/content/dialogue/SeravelDialogue.java +++ b/Server/src/main/content/region/karamja/shilo/dialogue/SeravelDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.karamja.shilo.dialogue; +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; 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/java/core/game/interaction/object/BrokenCartBypass.java b/Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java similarity index 88% rename from Server/src/main/java/core/game/interaction/object/BrokenCartBypass.java rename to Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java index 127b32b70..e06e1ad09 100644 --- a/Server/src/main/java/core/game/interaction/object/BrokenCartBypass.java +++ b/Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java @@ -1,16 +1,19 @@ -package core.game.interaction.object; +package content.region.karamja.shilo.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.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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 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/kotlin/rs09/game/content/dialogue/GabootyDialogue.kt b/Server/src/main/content/region/karamja/taibwo/dialogue/GabootyDialogue.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/content/dialogue/GabootyDialogue.kt rename to Server/src/main/content/region/karamja/taibwo/dialogue/GabootyDialogue.kt index 1f77413a7..a590492c6 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/GabootyDialogue.kt +++ b/Server/src/main/content/region/karamja/taibwo/dialogue/GabootyDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.dialogue +package content.region.karamja.taibwo.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.game.system.config.ShopParser +import core.game.shops.Shops /** * Dialogue for Gabooty @@ -116,8 +116,8 @@ class GabootyDialogue(player: Player? = null) : DialoguePlugin(player){ "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 } - 90 -> end().also { ShopParser.Companion.openUid(player,226) } - 91 -> end().also { ShopParser.Companion.openUid(player,227) } + 90 -> end().also { Shops.openId(player,226) } + 91 -> end().also { Shops.openId(player,227) } 99 -> end() } diff --git a/Server/src/main/java/core/game/content/dialogue/JiminuaDialogue.java b/Server/src/main/content/region/karamja/taibwo/dialogue/JiminuaDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/JiminuaDialogue.java rename to Server/src/main/content/region/karamja/taibwo/dialogue/JiminuaDialogue.java index edc7b50e2..d2bf7aae6 100644 --- a/Server/src/main/java/core/game/content/dialogue/JiminuaDialogue.java +++ b/Server/src/main/content/region/karamja/taibwo/dialogue/JiminuaDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.karamja.taibwo.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import org.rs09.consts.Items; import core.game.interaction.NodeUsageEvent; @@ -8,7 +10,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.item.Item; import core.game.node.entity.player.Player; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the jiminua dialogue plugin. @@ -38,7 +40,7 @@ public final class JiminuaDialogue extends DialoguePlugin { @Override public DialoguePlugin newInstance(Player player) { - PluginManager.definePlugin(new JiminuaUnnoteHandler()); + ClassScanner.definePlugin(new JiminuaUnnoteHandler()); return new JiminuaDialogue(player); } diff --git a/Server/src/main/java/core/game/interaction/npc/GabootyTrade.java b/Server/src/main/content/region/karamja/taibwo/handlers/GabootyTrade.java similarity index 86% rename from Server/src/main/java/core/game/interaction/npc/GabootyTrade.java rename to Server/src/main/content/region/karamja/taibwo/handlers/GabootyTrade.java index 08d5dc4ae..135a78f0c 100644 --- a/Server/src/main/java/core/game/interaction/npc/GabootyTrade.java +++ b/Server/src/main/content/region/karamja/taibwo/handlers/GabootyTrade.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.region.karamja.taibwo.handlers; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; @@ -7,7 +7,8 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.game.system.config.ShopParser; +import core.game.shops.Shops; +import core.game.system.config.ShopParser; /** * Represents the plugin used for trading with Gabooty @@ -39,11 +40,11 @@ public final class GabootyTrade extends OptionHandler { return true; case "trade-co-op": - ShopParser.Companion.openUid(player, 226); + Shops.openId(player, 226); return true; case "trade-drinks": - ShopParser.Companion.openUid(player, 227); + Shops.openId(player, 254); return true; } break; 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/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/java/core/game/node/entity/npc/city/tzhaar/TzHaarMejKah.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejKah.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/city/tzhaar/TzHaarMejKah.java rename to Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejKah.java index cf221138c..97a62c9a1 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/tzhaar/TzHaarMejKah.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzHaarMejKah.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.tzhaar; +package content.region.karamja.tzhaar.handlers; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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; diff --git a/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightCaveNPC.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCaveNPC.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightCaveNPC.java rename to Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCaveNPC.java index abd27d066..1fd8b7f9d 100644 --- a/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightCaveNPC.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCaveNPC.java @@ -1,9 +1,9 @@ -package core.game.content.activity.tzhaar; +package content.region.karamja.tzhaar.handlers; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.InteractionType; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.combat.equipment.ArmourSet; @@ -11,8 +11,6 @@ import core.game.node.entity.impl.Projectile; 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.node.entity.player.link.diary.DiaryType; import core.game.system.task.Pulse; import core.game.world.map.Location; import core.game.world.map.MapDistance; @@ -103,18 +101,6 @@ public final class TzhaarFightCaveNPC extends AbstractNPC { return mover instanceof TzhaarFightCaveNPC; } - @Override - public void finalizeDeath(Entity killer) { - if (killer.isPlayer()) { - Player player = killer.asPlayer(); - // Successfully kill a Ket-Zek in the Fight Caves - if ((getId() == 2743 || getId() == 2744)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 1); - } - } - super.finalizeDeath(killer); - } - /** * Heals the NPC. * @param amount The amount to heal. diff --git a/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightCavesPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java similarity index 77% rename from Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightCavesPlugin.java rename to Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java index 55d058535..5a4cdfd11 100644 --- a/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightCavesPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java @@ -1,15 +1,14 @@ -package core.game.content.activity.tzhaar; +package content.region.karamja.tzhaar.handlers; -import core.game.content.activity.ActivityPlugin; -import core.game.content.global.BossKillCounter; +import core.game.event.NPCKillEvent; +import core.game.activity.ActivityPlugin; +import content.data.BossKillCounter; import core.game.interaction.Option; 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.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.slayer.Tasks; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.node.scenery.Scenery; @@ -19,8 +18,9 @@ import core.game.world.map.build.DynamicRegion; import core.game.world.map.zone.ZoneRestriction; import core.plugin.Initializable; import core.tools.RandomFunction; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; +import core.game.world.GameWorld; +import core.game.world.repository.Repository; +import org.rs09.consts.NPCs; import java.util.ArrayList; import java.util.List; @@ -60,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; } @@ -95,7 +95,8 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { player.removeAttribute("fc_offset"); player.getProperties().setTeleportLocation(getBase().transform(offsetX, offsetY, 0)); Pulse pulse; - if (!login) { + boolean practice = player.getAttribute("fc_practice_jad", false); + if (!login && !practice) { final int wave = 0; player.setAttribute("fc_wave", wave); player.getWalkingQueue().reset(); @@ -117,6 +118,9 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { } }; } else { + if (practice) { + player.setAttribute("fc_wave", 62); + } if (player.getAttribute("fc_wave", 0) == 62) { player.getDialogueInterpreter().sendDialogues(2617, null, "Look out, here comes TzTok-Jad!"); } @@ -141,29 +145,44 @@ 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 (!player.getInventory().add(new Item(6570))) { - GroundItemManager.create(new Item(6570), getSpawnLocation(), player); + if (!practice) { + if (!player.getInventory().add(new Item(6570))) { + GroundItemManager.create(new Item(6570), getSpawnLocation(), player); + } + } else { + // give player the appearance fee back + int amount = 8000; + if (!player.getInventory().add(new Item(6529, amount))) { + GroundItemManager.create(new Item(6529, amount), getSpawnLocation(), player); + } } player.getPacketDispatch().sendMessage("You were victorious!"); - BossKillCounter.addtoKillcount(player, 2745); - if (player.getSlayer().getTask() == Tasks.JAD) { - player.getSkills().addExperience(Skills.SLAYER, 25000); - player.getSlayer().clear(); - player.sendMessage("You receive 25,000 slayer experience for defeating TzTok-Jad."); + if (!practice) { + 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 { + player.getDialogueInterpreter().sendDialogues(2617, null, "You defeated TzTok-Jad. I am most impressed!", "Here is you TokKul back.", "Maybe next time you do all training, and get real reward..."); } - 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 if (wave <= 1) { player.getDialogueInterpreter().sendDialogues(2617, null, "Well I suppose you tried... better luck next time."); } else { - player.getDialogueInterpreter().sendDialogues(2617, null, "Well done in the cave, here, take TokKul as reward."); + if (!practice) { + player.getDialogueInterpreter().sendDialogues(2617, null, "Well done in the cave, here, take TokKul as reward."); + } else { + player.getDialogueInterpreter().sendDialogues(2617, null, "You both impatient and also failure.", "Better luck next time."); + } } - int amount = wave << 7; - if (amount > 0 && !player.getInventory().add(new Item(6529, amount))) { - GroundItemManager.create(new Item(6529, amount), getSpawnLocation(), player); + + if (!practice) { + int amount = wave << 7; + if (amount > 0 && !player.getInventory().add(new Item(6529, amount))) { + GroundItemManager.create(new Item(6529, amount), getSpawnLocation(), player); + } } + } @Override @@ -181,6 +200,7 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { } else { player.removeAttribute("fc_offset"); player.removeAttribute("fc_wave"); + player.removeAttribute("fc_practice_jad"); player.removeAttribute("fc_safe_logout"); Pulse pulse = player.getAttribute("fc:pulse"); if (pulse != null) { @@ -230,6 +250,11 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { } else { leave((Player) e, e.getAttribute("fc_wave", 0)); } + + /* Kludge to fix this plugin overriding the base NPC death finalization + * resulting in not dispatching the proper NPC kill event. */ + if (e instanceof NPC) + killer.dispatch(new NPCKillEvent(e.asNpc())); return true; } @@ -272,6 +297,7 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { leave((Player) e, e.getAttribute("fc_wave", 0)); player.removeAttribute("fc_wave"); player.removeAttribute("fc_offset"); + player.removeAttribute("fc_practice_jad"); return true; } return false; diff --git a/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightPitsPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightPitsPlugin.java rename to Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java index 06f651c79..5070b76ef 100644 --- a/Server/src/main/java/core/game/content/activity/tzhaar/TzhaarFightPitsPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.activity.tzhaar; +package content.region.karamja.tzhaar.handlers; import java.util.ArrayList; import java.util.Iterator; @@ -7,9 +7,9 @@ import java.util.Random; import core.game.component.Component; import core.game.container.impl.EquipmentContainer; -import core.game.content.activity.ActivityPlugin; +import core.game.activity.ActivityPlugin; import core.plugin.Initializable; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import core.game.interaction.Option; import core.game.node.Node; @@ -24,14 +24,15 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; 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(); @@ -392,7 +393,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { } } } - int skill = lastVictor.getSkills().getHighestCombatSkill(); + int skill = lastVictor.getSkills().getHighestCombatSkillId(); switch (skill) { case Skills.ATTACK: case Skills.STRENGTH: @@ -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/java/core/game/content/zone/TzhaarZone.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarZone.java similarity index 94% rename from Server/src/main/java/core/game/content/zone/TzhaarZone.java rename to Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarZone.java index 2e56f6dea..dc3b2e1cb 100644 --- a/Server/src/main/java/core/game/content/zone/TzhaarZone.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarZone.java @@ -1,4 +1,4 @@ -package core.game.content.zone; +package content.region.karamja.tzhaar.handlers; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; 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 new file mode 100644 index 000000000..5eee207d0 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/bananaplantation/BonzaraDialogue.kt @@ -0,0 +1,54 @@ +package content.region.misc.apeatoll.dialogue.bananaplantation + +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 BonzaraDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.OLD_DEFAULT,"It looks like you're trying to escape. Would you like some help?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("Yes", "No").also { stage++ } + 1 -> when (buttonId) { + 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(FacialExpression.OLD_DEFAULT, "Right you are.").also { stage++ } + 11 -> { + end() + //teleport to ape atoll + } + + 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?): DialoguePlugin { + return BonzaraDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BONZARA_1468) + } +} 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 new file mode 100644 index 000000000..c84225bd8 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/dungeon/ZooknockDialogue.kt @@ -0,0 +1,145 @@ +package content.region.misc.apeatoll.dialogue.dungeon + +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 + * using temporary variables until quest is actually added + */ + +@Initializable +class ZooknockDialogue(player: Player? = null) : DialoguePlugin(player){ + + + 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 + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!hasSpokenToZook) { + player(ask, "Hello?").also { stage = 1 } + } else { + options("What do we need for the monkey amulet?", "What do we need for the monkey talisman?").also { stage = 57 } + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 1 -> npc(ntalk1, "A human ... here? What business have you on Ape Atoll?").also { stage++ } + 2 -> playerl(neu, "I am on a mission for King Narode Shareen of the Gnomes.").also { stage++ } + 3 -> playerl(neu, "I have been sent to investigate the whereabouts of the 10th squad of his Royal Guard, which went missing during a recent mission to Karamja.").also { stage++ } + 4 -> npc(ntalk1, "Well you've found us - what's left of us, that is.").also { stage++ } + 5 -> npcl(ntalk2, "I am Zooknock, the 10th squad mage. These are Bunkwicker and Waymottin, two of our finest sappers. I assme you will want to know how we got here?").also { stage++ } + 6 -> player(neu, "Of course.").also { stage++ } + 7 -> npcl(ntalk1, "Your story first, human. What possessed you to travel to this forsaken island?").also { stage++ } + 8 -> playerl(neu, "I am currently in the service of your King. I was the human who exposed Glough's warfaring plans and defeated his demon.").also { stage++ } + 9 -> playerl(neu, "As far as I understand, the 10th squad were sent to oversee the decomission of Glough's shipyard in east Karamja.").also { stage++ } + 10 -> playerl(neu, "Rumour has it you were blown off course. The King worried as to your fate, and sent me to investigate.").also { stage++ } + 11 -> npc(ntalk1, "You were sent alone?").also { stage++ } + 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(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++ } + 19 -> npcl(ntalk2, "The rumours are correct. We were indeed blown off course. Fortunately, we managed to find a small island to steer our gliders towards, else we surely would have drowned.").also { stage++ } + 20 -> playerl(neu, "Then you gathered enough wood to fashion two boats. Your Sergeant and the rest of the 10th squad took the larger boat to this island, leaving Lumdo to guard the gliders and the other boat...").also { stage++ } + 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(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++ } + 28 -> npcl(ntalk1, "Myself, the Sergeant, Bunwicker and Waymottin here. Karam, our assassin, probably managed to escape - he usually does.").also { stage++ } + 29 -> player(ask, "And of the rest?").also { stage++ } + 30 -> npcl(ntalk1, "Lumo, Bunkdo and Carado were captured, as I said. We believe they are being held in the jail. We are working on a way to release them. I have sensed there lies a cavern to the north.").also { stage++ } + 31 -> npcl(ntalk2, "We are attempting to tunnel to this northern cavern and then move upwards from there.").also { stage++ } + 32 -> player(ask, "Why don't you just go overground?").also { stage++ } + 33 -> npcl(ntalk2, "We have considered this, but every entrance seems to be excessively guarded.").also { stage++ } + 34 -> player(ask, "I see.").also { stage++ } + 35 -> playerl(ask, "I have spoken to your Sergeant. He believes that the best way to rescue the rest of your squad with the minimum of casualties is to have an insider - a monkey working for us.").also { stage++ } + 36 -> npc(ntalk1, "Aha. He wants me to turn you into a monkey.").also { stage++ } + 37 -> player(ask, "Actually, it was more along the lines of a disguise...").also { stage++ } + 38 -> npcl(ntalk1, "I think you misunderstand, human. Do you know why you were sent here?").also { stage++ } + 39 -> player(ask, "King Narode Shareen asked me to...").also { stage++ } + 40 -> npcl(ntalk1, "Indeed. However, King Narode Shareen is still in contact with Garkor! You were sent here because Garkot specifically asked Narode for you!").also { stage++ } + 41 -> player(ask, "Why wasn't I told?").also { stage++ } + 42 -> npcl(ntalk2, "Before you arrived on this island, that information would have endangered both yourself and the mission.").also { stage++ } + 43 -> player(ask, "But why a human? Why me?").also { stage++ } + 44 -> npcl(ntalk1, "Garkor had long decided that we need a monkey insider. I have the necessary magic to perform the shapeshifting spell, but we needed a human to transform.").also { stage++ } + 45 -> player(ask, "Why don't you just transform a gnome?").also { stage++ } + 46 -> npcl(ntalk2, "It has been tried in the past, but the results were far from ... satisfactory. Although we, like you, are related to the monkeys, the link is too weak for a successful transformation.").also { stage++ } + 47 -> npc(ntalk1, "That is why we need you.").also { stage++ } + 48 -> player(ask, "Right. What do I have to do?").also { stage++ } + 49 -> npcl(ntalk1, "There will be two aspects to your transformation. We must first arrange it so that you are able to understand and communicate with the monkeys.").also { stage++ } + 50 -> npcl(ntalk2, "We must also transform your body so that you may pass amongst them unnoticed.").also { stage++ } + 51 -> npcl(ntalk1, "So that the effects of my spells are not permanent, I will invest their power into magical items which you must find. You can then use them at your will.").also { stage++ } + 52 -> player(ask, "What kind of items?").also { stage++ } + 53 -> npcl(ntalk2, "For the spells to take full effect, they will have to be in some way related to the monkeys.").also { stage++ } + 54 -> npcl(ntalk1, "I suggest that I invest the ability to communicate with the monkeys in an authentic monkey amulet.").also { stage++ } + 55 -> { + hasSpokenToZook = true + npcl(ntalk1, "Similarily, the transformation spell should be stored in a monkey talisman of some kind.").also { stage++ } + } + 56 -> options("What do we need for the monkey amulet?", "What do we need for the monkey talisman?").also { stage++ } + 57 -> when (buttonId) { + 1 -> player(ask, "What do we need for the monkey amulet?").also { stage = 60 } + 2 -> player(ask, "What do we need for the monkey talisman?").also { stage = 100 } + } + + 60 -> npcl(ntalk1, "We need a gold bar, a monkey amulet mould and something to do monkey speech.").also { stage++ } + 61 -> options("Where do I find the gold bar?", "Where do I find a monkey amulet mould?", "Where do I find something to do with monkey speech?", "I'll be back later.").also { stage++ } + 62 -> when (buttonId) { + 1 -> player(ask, "Where do I find the gold bar?").also { stage = 70 } + 2 -> player(ask, "Where do I find a monkey amulet mould?").also { stage = 80 } + 3 -> player(ask, "Where do I find something to do with monkey speech?").also { stage = 90 } + 4 -> player(neu, "I'll be back later.").also { stage = 99 } + } + + 70 -> npcl(ntalk1, "I am not sure. You look wealthy enough to know the answer to that question anyway.").also { stage = 61 } + 80 -> npcl(ntalk1, "They ought to be for sale in the village, but be careful: you probably will not be able to walk straight in and buy one.").also { stage = 61 } + 90 -> npc(ntalk2, "I don't know. I'm sure you'll think of something.").also { stage = 61 } + + 100 -> npcl(nor, "We need some kind of monkey remains as well as an authentic magical monkey talisman.").also { stage++ } + 101 -> options("Where do I find monkey remains?", "Where do I find a magical monkey talisman?", "I'll be back later.").also { stage++ } + 102 -> when (buttonId) { + 1 -> player(ask, "Where do I find monkey remains?").also { stage = 110 } + 2 -> player(ask, "Where do I find a magical monkey talisman?").also { stage = 120 } + 3 -> player(neu, "I'll be back later.").also { stage = 99 } + } + + 110 -> npcl(nor, "I'll leave that to your better judgement... However, bear in mind the type of remain might affect the type of monkey you become...").also { stage++ } + 111 -> player(ask, "What if I need to be another type of monkey?").also { stage++ } + 112 -> npcl(ntalk1, "Then bring me different monkey remains and another talisman.").also { stage = 101} + + 120 -> npcl(ntalk2, "There ought to be something in the village. I cannot be sure, as I have not spent much time there.").also { stage = 101 } + + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ZooknockDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ZOOKNOCK_1425) + } +} diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/dungeon/ZooknockDialogueFile.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/dungeon/ZooknockDialogueFile.kt new file mode 100644 index 000000000..7fa68c04f --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/dungeon/ZooknockDialogueFile.kt @@ -0,0 +1,93 @@ +package content.region.misc.apeatoll.dialogue.dungeon + +import content.global.handlers.item.withnpc.ZooknockListener +import core.api.addItemOrDrop +import core.api.sendDoubleItemDialogue +import core.api.sendItemDialogue +import core.game.node.entity.npc.NPC +import org.rs09.consts.Items +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression + +/** + * @author qmqz + * todo will need to check quest stages for what's been handed over and whatnot + */ + +class ZooknockDialogueFile(val it: Int) : DialogueFile() { + + var i = ZooknockListener() + var n = NPC(i.zooknock) + var itemUsed = it + var hasGivenGoldBar = false + var hasGivenDentures = false + var hasGivenMould = false + + override fun handle(interfaceId: Int, buttonId: Int) { + npc = n + + when (stage) { + 0 -> when (itemUsed) { + i.goldBar -> { + if (!hasGivenGoldBar) { + player(FacialExpression.ASKING, "What do you think of this?").also { stage = 10 } + hasGivenGoldBar = true + } + } + + i.monkeyAmuletMould -> { + if (!hasGivenMould) { + player(FacialExpression.ASKING, "What do you think of this?").also { stage = 20 } + hasGivenMould = true + } + } + + i.monkeyDentures -> { + if (!hasGivenDentures) { + player(FacialExpression.ASKING, "What do you think of this?").also { stage = 30 } + } + } + } + + 10 -> sendItemDialogue(player!!, Items.GOLD_BAR_2357, "You hand Zooknock the gold bar.").also { stage++ } + 11 -> npc(FacialExpression.OLD_CALM_TALK1, "Nicely done.").also { stage++ } + 12 -> { + if (!hasGivenDentures && !hasGivenMould && hasGivenGoldBar) { + npcl(FacialExpression.OLD_CALM_TALK1, "We still need the monkey amulet mould and something to do with monkey speech.").also { stage = 99 } + } + } + + 20 -> sendItemDialogue(player!!, Items.MAMULET_MOULD_4020, "You hand Zooknock the monkey amulet mould.").also { stage++ } + 21 -> npc(FacialExpression.OLD_CALM_TALK1, "Good work.").also { stage++ } + 22 -> { + if (!hasGivenDentures && hasGivenMould && hasGivenGoldBar) { + npcl(FacialExpression.OLD_CALM_TALK1, "We still need something to do with monkey speech.").also { stage = 99 } + } + } + + 30 -> sendItemDialogue(player!!, Items.MSPEAK_AMULET_4021, "You hand Zooknock the magical monkey dentures.").also { stage++ } + 31 -> npc(FacialExpression.OLD_CALM_TALK1, "Good work.").also { stage++ } + 32 -> { + if (hasGivenDentures && hasGivenMould && hasGivenGoldBar) { + npcl(FacialExpression.OLD_CALM_TALK1, "Now listen closely, human. I will cast a spell to enchant this gold bar with the power contained in these monkey dentures.").also { stage = 40 } + } + } + + 40 -> npcl(FacialExpression.OLD_CALM_TALK1, "You must then smith the gold using the monkey amulet mould. However, unless you do this in a place of religious significance to the monkeys, the spirits").also { stage++ } + 41 -> npcl(FacialExpression.OLD_CALM_TALK2, "contained within will likely depart.").also { stage++ } + 42 -> playerl(FacialExpression.FRIENDLY, "Where do I find a place of religious significance to monkeys?").also { stage++ } + 43 -> npcl(FacialExpression.OLD_CALM_TALK1, "Somewhere in the village. It out to be obvious. Now give me a moment.").also { stage++ } + 44 -> { + //zooknock does animation, bla bla bla, waits a few seconds, opens up new next dialog + } + 45 -> { + addItemOrDrop(player!!, Items.ENCHANTED_BAR_4007, 1) + addItemOrDrop(player!!, Items.MAMULET_MOULD_4020, 1) + sendDoubleItemDialogue(player!!, Items.ENCHANTED_BAR_4007, Items.MAMULET_MOULD_4020, "Zooknock hands you back the gold bar and the monkey amulet mould.").also { stage++ } + } + + + 99 -> end() + } + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/AberabDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/AberabDialogue.kt similarity index 76% rename from Server/src/main/kotlin/rs09/game/content/dialogue/AberabDialogue.kt rename to Server/src/main/content/region/misc/apeatoll/dialogue/marim/AberabDialogue.kt index 5e6f42c9b..5d84c1aae 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AberabDialogue.kt +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/AberabDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.content.dialogue +package content.region.misc.apeatoll.dialogue.marim -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 @@ -16,7 +16,7 @@ class AberabDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(FacialExpression.CHILD_SUSPICIOUS,"Grr ... Get out of my way...") + npc(FacialExpression.OLD_ANGRY1,"Grr ... Get out of my way...") stage = 99 return true } 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 new file mode 100644 index 000000000..5be5285bf --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/DagaDialogue.kt @@ -0,0 +1,62 @@ +package content.region.misc.apeatoll.dialogue.marim + +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.tools.END_DIALOGUE + +/** + * @author qmqz + * Does not include treasure trails dialogue + */ + +@Initializable +class DagaDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_DEFAULT,"Would you like to buy or sell some scimitars?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("Yes, please.", "No, thanks.", "Do you have any Dragon Scimitars in stock?").also { stage++ } + + 1 -> when (buttonId) { + 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(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(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(FacialExpression.FRIENDLY, "No thanks.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DagaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DAGA_1434) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..6902cbaa6 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/ElderGuardDialogue.kt @@ -0,0 +1,63 @@ +package content.region.misc.apeatoll.dialogue.marim + +import core.api.toIntArray +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 ElderGuardDialogue(player: Player? = null) : DialoguePlugin(player){ + + val ids = 4025..4031 + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + //todo check player pos, if outside, player needs to be unable from entering without speaking, but currently player can clip through monke + var outside = true + //todo check if monke is in the corner + var cornerGuard = false + + if (player.equipment.containsAtLeastOneItem(ids.toIntArray())) { + if (outside) { + npc(FacialExpression.OLD_ANGRY1, "Grrr ... What do you want?").also { stage = 10 } + } else if(cornerGuard){ + npc(FacialExpression.OLD_ANGRY1, "Grrr ... What do you want?").also { stage = 20 } + } else { + 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 + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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?): DialoguePlugin { + return ElderGuardDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ELDER_GUARD_1461, NPCs.ELDER_GUARD_1462) + } +} 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 new file mode 100644 index 000000000..fd621df95 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/MuruwoiDialogue.kt @@ -0,0 +1,39 @@ +package content.region.misc.apeatoll.dialogue.marim + +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 + * quest dialogue is missing on rs3 and osrs, someone will need to youtube it + */ + +@Initializable +class MuruwoiDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_ANGRY1,"Grr ... Get out of my way...") + stage = 99 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return MuruwoiDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MURUWOI_1450) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/PadulahDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/PadulahDialogue.kt new file mode 100644 index 000000000..2eeed97c8 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/PadulahDialogue.kt @@ -0,0 +1,66 @@ +package content.region.misc.apeatoll.dialogue.marim + +import core.api.openDialogue +import core.game.dialogue.DialogueBuilder +import core.game.dialogue.DialogueBuilderFile +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class PadulahDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PadulahDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return PadulahDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.PADULAH_1447) + } +} + +class PadulahDialogueFile: DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.defaultDialogue().npcl( + "What do you want?" + ).playerl( + FacialExpression.NEUTRAL, + "Oh, nothing in particular really." + ).npcl( + "Well stop distracting me then. I'm meant to be guarding this sacred statue from the temple of Marimbo." + ).options().let { optionsBuilder -> optionsBuilder.option("Where is the temple of Marimbo?") + .npcl( + "You're not from around here are you?" + ).playerl( + FacialExpression.NEUTRAL, + "Actually I'm not. I'm a visitor from foreign lands." + ).npcl( + "Very well, the temple is to the east of the village." + ).end() + optionsBuilder.option("What is the statue of? ") + .npcl( + "It's of Marimbo, you cretin!" + ).playerl( + FacialExpression.NEUTRAL, + "Ah yes. How stupid of me not to see the likeness." + ).end() + optionsBuilder.option("I'll be back later.") + .npcl( + "I wouldn't count on it." + ).playerl( + FacialExpression.WORRIED, + "What?!" + ).npcl( + "Oh, nothing." + ).end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/SolihibDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/SolihibDialogue.kt new file mode 100644 index 000000000..381e3e856 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/SolihibDialogue.kt @@ -0,0 +1,42 @@ +package content.region.misc.apeatoll.dialogue.marim + +import content.data.Quests +import core.api.hasRequirement +import core.api.openNpcShop +import core.game.dialogue.ChatAnim +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +class SolihibDialogue : InteractionListener { + override fun defineListeners() { + on(NPCs.SOLIHIB_1433, IntType.NPC, "talk-to") { player, node -> + if (!hasRequirement(player, Quests.MONKEY_MADNESS)) return@on true + DialogueLabeller.open(player, SolihibDialogueLabellerFile(), node as NPC) + return@on true + } + on(NPCs.SOLIHIB_1433, IntType.NPC, "trade") { player, _ -> + if (!hasRequirement(player, Quests.MONKEY_MADNESS)) return@on true + openNpcShop(player, NPCs.SOLIHIB_1433) + return@on true + } + } + + class SolihibDialogueLabellerFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SOLIHIB_1433) + + npc(ChatAnim.FRIENDLY, "Would you like to buy or sell some food?") + options( + DialogueOption("trade", "Yes, please."), + DialogueOption("nowhere", "No, thanks.") + ) + label("trade") + exec { player, _ -> openNpcShop(player, NPCs.SOLIHIB_1433) } + goto("nowhere") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/apeatoll/quest/monkeymadness/CrateDialogues.kt b/Server/src/main/content/region/misc/apeatoll/quest/monkeymadness/CrateDialogues.kt new file mode 100644 index 000000000..573a36171 --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/quest/monkeymadness/CrateDialogues.kt @@ -0,0 +1,163 @@ +package content.region.misc.apeatoll.quest.monkeymadness + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.player.link.TeleportManager +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.tools.RandomFunction +import org.rs09.consts.Items +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + +/** + * @author qmqz + */ + +class CrateDialogues(val it: Int) : DialogueFile() { + + private val MMDungeon2 = Location.create(2804, 9168, 0) + + override fun handle(interfaceId: Int, buttonId: Int) { + when (it) { + 0 -> when (stage) { + 0 -> sendItemDialogue(player!!, Items.MAMULET_MOULD_4020, "The crate is full of ... monkey amulet moulds!").also { stage++ } + 1 -> player!!.dialogueInterpreter.sendOptions("Do you wish to take one?", "Yes", "No").also { stage++ } + 2 -> when (buttonId) { + 1 -> { + addItemOrDrop(player!!, Items.MAMULET_MOULD_4020) + end() + } + 2 -> end() + } + } + + 1 -> when (stage) { + 0 -> sendItemDialogue(player!!, Items.THREAD_1734, "The crate is full of ... crafting thread.").also { stage++ } + 1 -> player!!.dialogueInterpreter.sendOptions("Do you wish to take one?", "Yes", "No").also { stage++ } + 2 -> when (buttonId) { + 1 -> { + addItemOrDrop(player!!, Items.THREAD_1734) + end() + } + 2 -> end() + } + } + + 2 -> when (stage) { + 0 -> sendItemDialogue(player!!, Items.MONKEY_DENTURES_4006, "The crate is full of ... magical monkey talking dentures!").also { stage++ } + 1 -> player!!.dialogueInterpreter.sendOptions("Do you wish to take one?", "Yes", "No").also { stage++ } + 2 -> when (buttonId) { + 1 -> { + addItemOrDrop(player!!, Items.MONKEY_DENTURES_4006) + end() + } + 2 -> end() + } + } + + 3 -> when (stage) { + 0 -> sendItemDialogue(player!!, Items.BANANA_1963, "The crate is full of ... bananas.").also { stage++ } + 1 -> player!!.dialogueInterpreter.sendOptions("Do you wish to take one?", "Yes", "No").also { stage++ } + 2 -> when (buttonId) { + 1 -> { + addItemOrDrop(player!!, Items.BANANA_1963) + end() + } + 2 -> end() + } + } + + 4 -> when (stage) { + 0 -> sendDialogue( + player!!, + "You find a hole in the floor under the crate! All you can see is the faint glimmer of light from extremely far below." + ).also { stage++ } + 1 -> player!!.dialogueInterpreter.sendOptions( + "Would you like to go down?", + "Yes, I'm sure.", + "No, not yet." + ).also { stage++ } + 2 -> when (buttonId) { + 1 -> sendDialogue(player!!, "You begin to lower yourself into the hole...").also { stage = 10 } + 2 -> end() + } + 10 -> { + end() + openInterface(player!!, 120) + player!!.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 3 -> teleport(player!!, MMDungeon2, TeleportManager.TeleportType.INSTANT) + } + return false + } + }) + monkeyMadnessGoIntoBasementDamage() + } + } + + 5 -> when (stage) { + 0 -> sendItemDialogue(player!!, Items.TINDERBOX_590, "The crate is full of ... tinderboxes.").also { stage++ } + 1 -> player!!.dialogueInterpreter.sendOptions("Do you wish to take one?", "Yes", "No").also { stage++ } + 2 -> when (buttonId) { + 1 -> { + addItemOrDrop(player!!, Items.TINDERBOX_590) + end() + } + 2 -> end() + } + } + + 6 -> when (stage) { + 0 -> sendItemDialogue(player!!, Items.EYE_OF_GNOME_4008, "The crate is full of ... slimy gnome eyes!").also { stage++ } + 1 -> player!!.dialogueInterpreter.sendOptions("Do you wish to take one?", "Yes", "No").also { stage++ } + 2 -> when (buttonId) { + 1 -> { + addItemOrDrop(player!!, Items.EYE_OF_GNOME_4008) + end() + } + 2 -> end() + } + } + + 7 -> when (stage) { + 0 -> sendItemDialogue(player!!, Items.HAMMER_2347, "The crate is full of ... hammers.").also { stage++ } + 1 -> player!!.dialogueInterpreter.sendOptions("Do you wish to take one?", "Yes", "No").also { stage++ } + 2 -> when (buttonId) { + 1 -> { + addItemOrDrop(player!!, Items.HAMMER_2347) + end() + } + 2 -> end() + } + } + + + + } + } + + private fun monkeyMadnessGoIntoBasementDamage() { + Executors.newSingleThreadScheduledExecutor().schedule({ + if (player!!.location.isInRegion(MMDungeon2.regionId)) { + player!!.interfaceManager.close() + openInterface(player!!, 170) + + player!!.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 3 -> player!!.impactHandler.manualHit(player, RandomFunction.random(0, player!!.skills.lifepoints / 2), ImpactHandler.HitsplatType.NORMAL) + } + return false + } + }) + } else { + monkeyMadnessGoIntoBasementDamage() + } + }, 1, TimeUnit.SECONDS) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/apeatoll/quest/monkeymadness/CrateHandler.kt b/Server/src/main/content/region/misc/apeatoll/quest/monkeymadness/CrateHandler.kt new file mode 100644 index 000000000..f6b557a6c --- /dev/null +++ b/Server/src/main/content/region/misc/apeatoll/quest/monkeymadness/CrateHandler.kt @@ -0,0 +1,125 @@ +package content.region.misc.apeatoll.quest.monkeymadness + +import core.api.openDialogue +import core.api.sendMessage +import core.game.system.task.Pulse +import core.game.world.update.flag.context.Animation +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * @author qmqz + */ + +class CrateHandler : InteractionListener { + + private val monkeyAmuletMouldCrate = 4724 + private val threadCrate = 4718 + private val monkeyTalkingDentures = 4715 + private val bananaCrate = 4723 + private val monkeyMadnessEntranceDown = 4714 + private val tinderboxCrate = 4719 + private val slimyGnomeEyesCrate = 4716 + private val hammersCrate = 4726 + + override fun defineListeners() { + //searching crates with monkey amulet moulds + on(monkeyAmuletMouldCrate, IntType.SCENERY, "search") { player, node -> + player.animator.animate(Animation(536)) + sendMessage(player, "You search the crate.") + openDialogue(player!!, CrateDialogues(0)) + return@on true + } + + on(threadCrate, IntType.SCENERY, "search") { player, node -> + sendMessage(player, "You search the crate.") + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 3 -> openDialogue(player!!, CrateDialogues(1)) + } + return false + } + }) + return@on true + } + + on(monkeyTalkingDentures, IntType.SCENERY, "search") { player, node -> + sendMessage(player, "You search the crate.") + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 3 -> openDialogue(player!!, CrateDialogues(2)) + } + return false + } + }) + return@on true + } + + on(bananaCrate, IntType.SCENERY, "search") { player, node -> + sendMessage(player, "You search the crate.") + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 3 -> openDialogue(player!!, CrateDialogues(3)) + } + return false + } + }) + return@on true + } + + on(monkeyMadnessEntranceDown, IntType.SCENERY, "search") { player, node -> + sendMessage(player, "You search the crate.") + openDialogue(player!!, CrateDialogues(4)) + return@on true + } + + on(tinderboxCrate, IntType.SCENERY, "search") { player, node -> + sendMessage(player, "You search the crate.") + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 3 -> openDialogue(player!!, CrateDialogues(5)) + } + return false + } + }) + return@on true + } + + on(slimyGnomeEyesCrate, IntType.SCENERY, "search") { player, node -> + sendMessage(player, "You search the crate.") + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 3 -> openDialogue(player!!, CrateDialogues(6)) + } + return false + } + }) + return@on true + } + + on(hammersCrate, IntType.SCENERY, "search") { player, node -> + sendMessage(player, "You search the crate.") + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 3 -> openDialogue(player!!, CrateDialogues(7)) + } + return false + } + }) + return@on true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/zone/CrandorZone.java b/Server/src/main/content/region/misc/crandor/handlers/CrandorZone.java similarity index 95% rename from Server/src/main/java/core/game/content/zone/CrandorZone.java rename to Server/src/main/content/region/misc/crandor/handlers/CrandorZone.java index 70c3fc020..aeaead05b 100644 --- a/Server/src/main/java/core/game/content/zone/CrandorZone.java +++ b/Server/src/main/content/region/misc/crandor/handlers/CrandorZone.java @@ -1,4 +1,4 @@ -package core.game.content.zone; +package content.region.misc.crandor.handlers; import core.game.node.entity.Entity; import core.game.world.map.zone.MapZone; diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/EnchantedValleyListeners.kt b/Server/src/main/content/region/misc/enchvalley/handlers/EnchantedValleyListeners.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/interaction/object/EnchantedValleyListeners.kt rename to Server/src/main/content/region/misc/enchvalley/handlers/EnchantedValleyListeners.kt index 41beb3236..ddcff97b7 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/object/EnchantedValleyListeners.kt +++ b/Server/src/main/content/region/misc/enchvalley/handlers/EnchantedValleyListeners.kt @@ -1,18 +1,19 @@ -package rs09.game.interaction.`object` +package content.region.misc.enchvalley.handlers import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.skill.gather.SkillingTool +import content.data.skill.SkillingTool import core.game.system.task.Pulse import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener +import core.game.interaction.InteractionListener +import core.game.interaction.IntType -class EnchantedValleyListeners : InteractionListener() { +class EnchantedValleyListeners : InteractionListener { val ENCHANTED_V_TREE = 16265 val TREE_SPIRIT_IDS = intArrayOf(NPCs.TREE_SPIRIT_438,NPCs.TREE_SPIRIT_439,NPCs.TREE_SPIRIT_440,NPCs.TREE_SPIRIT_441,NPCs.TREE_SPIRIT_442,NPCs.TREE_SPIRIT_443) override fun defineListeners() { - on(ENCHANTED_V_TREE,SCENERY,"chop-down"){ player, node -> + on(ENCHANTED_V_TREE, IntType.SCENERY, "chop-down"){ player, node -> val tool: SkillingTool? = SkillingTool.getHatchet(player) tool ?: player.sendMessage("You lack an axe which you have the Woodcutting level to use.").also { return@on true } diff --git a/Server/src/main/java/core/game/content/dialogue/CaveMonk.java b/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/CaveMonk.java rename to Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java index 1a9d79e95..15e7f8803 100644 --- a/Server/src/main/java/core/game/content/dialogue/CaveMonk.java +++ b/Server/src/main/content/region/misc/entrana/dialogue/CaveMonk.java @@ -1,11 +1,14 @@ -package core.game.content.dialogue; +package content.region.misc.entrana.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; 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 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. @@ -50,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/java/core/game/content/dialogue/ConeDialogue.java b/Server/src/main/content/region/misc/entrana/dialogue/ConeDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/dialogue/ConeDialogue.java rename to Server/src/main/content/region/misc/entrana/dialogue/ConeDialogue.java index cb74607d8..e2ba278aa 100644 --- a/Server/src/main/java/core/game/content/dialogue/ConeDialogue.java +++ b/Server/src/main/content/region/misc/entrana/dialogue/ConeDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misc.entrana.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; diff --git a/Server/src/main/java/core/game/content/dialogue/EntranaChurchMonk.java b/Server/src/main/content/region/misc/entrana/dialogue/EntranaChurchMonk.java similarity index 89% rename from Server/src/main/java/core/game/content/dialogue/EntranaChurchMonk.java rename to Server/src/main/content/region/misc/entrana/dialogue/EntranaChurchMonk.java index 99906b51e..4086b195f 100644 --- a/Server/src/main/java/core/game/content/dialogue/EntranaChurchMonk.java +++ b/Server/src/main/content/region/misc/entrana/dialogue/EntranaChurchMonk.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misc.entrana.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; diff --git a/Server/src/main/java/core/game/content/dialogue/FrincosDialogue.java b/Server/src/main/content/region/misc/entrana/dialogue/FrincosDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/FrincosDialogue.java rename to Server/src/main/content/region/misc/entrana/dialogue/FrincosDialogue.java index 708c9a3db..cc49de3b6 100644 --- a/Server/src/main/java/core/game/content/dialogue/FrincosDialogue.java +++ b/Server/src/main/content/region/misc/entrana/dialogue/FrincosDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misc.entrana.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; diff --git a/Server/src/main/java/core/game/content/dialogue/FritzGlassBlowerDialogue.java b/Server/src/main/content/region/misc/entrana/dialogue/FritzGlassBlowerDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/dialogue/FritzGlassBlowerDialogue.java rename to Server/src/main/content/region/misc/entrana/dialogue/FritzGlassBlowerDialogue.java index cbdf0dd21..0e2840222 100644 --- a/Server/src/main/java/core/game/content/dialogue/FritzGlassBlowerDialogue.java +++ b/Server/src/main/content/region/misc/entrana/dialogue/FritzGlassBlowerDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misc.entrana.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.GroundItem; diff --git a/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt b/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt new file mode 100644 index 000000000..4f177c848 --- /dev/null +++ b/Server/src/main/content/region/misc/entrana/dialogue/HighPriestEntranaDialogue.kt @@ -0,0 +1,72 @@ +package content.region.misc.entrana.dialogue + +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.Items +import org.rs09.consts.NPCs + +@Initializable +class HighPriestEntranaDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + // 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 } + + 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 + } + } + return true + } + + 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 new file mode 100644 index 000000000..60368b2cc --- /dev/null +++ b/Server/src/main/content/region/misc/entrana/dialogue/MazionDialogue.kt @@ -0,0 +1,41 @@ +package content.region.misc.entrana.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 MazionDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + when ((1..3).random()) { + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return MazionDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MAZION_3114) + } +} 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/java/core/game/interaction/city/EntranaObjectPlugin.java b/Server/src/main/content/region/misc/entrana/handlers/EntranaObjectPlugin.java similarity index 93% rename from Server/src/main/java/core/game/interaction/city/EntranaObjectPlugin.java rename to Server/src/main/content/region/misc/entrana/handlers/EntranaObjectPlugin.java index a61706255..d81a1be5c 100644 --- a/Server/src/main/java/core/game/interaction/city/EntranaObjectPlugin.java +++ b/Server/src/main/content/region/misc/entrana/handlers/EntranaObjectPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.city; +package content.region.misc.entrana.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; @@ -6,7 +6,7 @@ import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.TeleportManager; import core.game.world.map.Location; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; import core.plugin.Initializable; import core.plugin.Plugin; diff --git a/Server/src/main/content/region/misc/etceteria/dialogue/FishmongerEtcDialogue.kt b/Server/src/main/content/region/misc/etceteria/dialogue/FishmongerEtcDialogue.kt new file mode 100644 index 000000000..39f349294 --- /dev/null +++ b/Server/src/main/content/region/misc/etceteria/dialogue/FishmongerEtcDialogue.kt @@ -0,0 +1,37 @@ +package content.region.misc.etceteria.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 FishmongerEtcDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.FRIENDLY,"Welcome, ${player.getAttribute("fremennikname","fremmyname")}. My fish is fresher than any in Miscellania.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> end().also { npc.openShop(player) } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FishmongerEtcDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FISHMONGER_1369) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/AgmundiDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/AgmundiDialogue.kt new file mode 100644 index 000000000..e67ea194b --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/AgmundiDialogue.kt @@ -0,0 +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.dialogue.Topic +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class AgmundiDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return AgmundiDialogue(player) + } + + override fun getIds(): IntArray { + 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 new file mode 100644 index 000000000..22d229fc6 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/BlackGuardDialogue.kt @@ -0,0 +1,37 @@ +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 BlackGuardDialogue(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_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?): 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, 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/kotlin/rs09/game/content/zone/keldagrim/CartConductorDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/CartConductorDialogue.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/zone/keldagrim/CartConductorDialogue.kt rename to Server/src/main/content/region/misc/keldagrim/dialogue/CartConductorDialogue.kt index 3d6e04c2c..d88d0841c 100644 --- a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/CartConductorDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/CartConductorDialogue.kt @@ -1,13 +1,14 @@ -package rs09.game.content.zone.keldagrim +package content.region.misc.keldagrim.dialogue import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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.Location import core.plugin.Initializable +import content.region.misc.keldagrim.handlers.KeldagrimCartMethods private const val ICE_MOUNTAIN_CONDUCTOR = 2180 private const val WHITE_WOLF_CONDUCTOR = 2181 @@ -41,8 +42,8 @@ class CartConductorDialogue(player: Player? = null) : DialoguePlugin(player) { 301 -> options("Grand Exchange","White Wolf Mountain","Ice Mountain").also { stage++ } 302 -> when(buttonId){ 1 -> KeldagrimCartMethods.leaveKeldagrimTo(player, Location.create(3140, 3507, 0)).also{ end() } - 2 -> KeldagrimCartMethods.leaveKeldagrimTo(player,Location.create(2875, 9871, 0)).also{ end() } - 3 -> KeldagrimCartMethods.leaveKeldagrimTo(player,Location.create(2997, 9837, 0)).also{ end() } + 2 -> KeldagrimCartMethods.leaveKeldagrimTo(player, Location.create(2875, 9871, 0)).also{ end() } + 3 -> KeldagrimCartMethods.leaveKeldagrimTo(player, Location.create(2997, 9837, 0)).also{ end() } } 1000 -> end() } 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 new file mode 100644 index 000000000..c25041dc4 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/DwarvenBoatmanDialogue.kt @@ -0,0 +1,152 @@ +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.game.world.map.Location +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 DwarvenBoatmanForthDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // 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 newInstance(player: Player?): DialoguePlugin { + return DwarvenBoatmanForthDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DWARVEN_BOATMAN_2205) //1844,1843 + } +} + +class TravelForthPulse(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(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 + } + } + return false + } +} + + +@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 new file mode 100644 index 000000000..f341e6d53 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/InnKeeperDialogue.kt @@ -0,0 +1,57 @@ +package content.region.misc.keldagrim.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 InnKeeperDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + npc(FacialExpression.CHILD_NEUTRAL, "Welcome to the King's Axe inn!", "What can I help you with?").also { stage++ } + } + + 1 -> { + player(FacialExpression.ASKING, "Can I have some beer please?").also { stage++ } + } + + 2 -> { + npc(FacialExpression.CHILD_NORMAL, "Go to the bar downstairs.", "I only deal with residents.").also { stage++ } + } + + 3 -> { + player(FacialExpression.THINKING, "Residents? People live here?").also { stage++ } + } + + 4 -> { + npc(FacialExpression.CHILD_LOUDLY_LAUGHING, "No, just guests that stay the night.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return InnKeeperDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.INN_KEEPER_2177) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/JorzikDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/JorzikDialogue.kt new file mode 100644 index 000000000..8844f3758 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/JorzikDialogue.kt @@ -0,0 +1,46 @@ +package content.region.misc.keldagrim.dialogue + +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +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 + +/** + * Jorzik dialogue. + * @author phil lips + */ + +@Initializable +class JorzikDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return JorzikDialogue(player) + } + + override fun getIds(): IntArray { + 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 new file mode 100644 index 000000000..9b8eb4a32 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/NolarDialogue.kt @@ -0,0 +1,35 @@ +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 NolarDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return NolarDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.NOLAR_2158) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/OrdanDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/OrdanDialogue.kt new file mode 100644 index 000000000..701ed5aec --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/OrdanDialogue.kt @@ -0,0 +1,140 @@ +package content.region.misc.keldagrim.dialogue + +import core.game.node.entity.player.Player +import core.plugin.Initializable +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.item.Item +import org.rs09.consts.NPCs +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items + +@Initializable +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){ + 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 + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return OrdanDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ORDAN_2564) + } + + 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 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, + ) + } + + // 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() + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/ReinaldDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/ReinaldDialogue.kt new file mode 100644 index 000000000..6e746f33b --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/ReinaldDialogue.kt @@ -0,0 +1,53 @@ +package content.region.misc.keldagrim.dialogue + +import core.api.openInterface +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.player.Player +import core.plugin.Initializable +import core.plugin.Plugin +import org.rs09.consts.NPCs + +/** + * File for handling Reinald's dialogue and right-click option + * @author Ceikry + */ +@Initializable +class ReinaldDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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 newInstance(player: Player?): DialoguePlugin { + return ReinaldDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.REINALD_2194) + } + +} + +@Initializable +class ReinaldOptionHandler : OptionHandler(){ + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + openInterface(player!!,593) + return true + } + + override fun newInstance(arg: Any?): Plugin { + NPCDefinition.forId(NPCs.REINALD_2194).handlers["option:change-armguards"] = this + return this + } + +} \ No newline at end of file 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/kotlin/rs09/game/content/zone/keldagrim/KeldagrimCartMethods.kt b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/zone/keldagrim/KeldagrimCartMethods.kt rename to Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt index 31797319d..20d4d5771 100644 --- a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/KeldagrimCartMethods.kt +++ b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt @@ -1,4 +1,4 @@ -package rs09.game.content.zone.keldagrim +package content.region.misc.keldagrim.handlers import core.game.component.Component import core.game.node.entity.npc.NPC @@ -7,16 +7,22 @@ import core.game.system.task.Pulse import core.game.world.map.Direction import core.game.world.map.Location import org.rs09.consts.Components -import rs09.game.world.GameWorld +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 new file mode 100644 index 000000000..24a7a333b --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimPlugin.kt @@ -0,0 +1,126 @@ +package content.region.misc.keldagrim.handlers + +import content.minigame.blastfurnace.BlastFurnace +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.item.Item +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, + * most notably the KeldagrimOptionHandlers and GETrapdoorDialogue + * Anything that wasn't significant enough for its own individual file, tbh. + * @author Ceikry + */ + +const val GETrapdoorDialogueID = 22381232 + +/** + * Handles various options around keldagrim that weren't significant enough for a separate file + */ +@Initializable +class KeldagrimOptionHandlers : OptionHandler() { + + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + player ?: return false + node ?: return false + option ?: return false + when(option){ + "go-through" -> { + when (node.id) { + 5973 -> player.properties.teleportLocation = Location.create(2838, 10125) + 5998 -> player.properties.teleportLocation = Location.create(2780, 10161) + } + } + "open" -> { + when(node.id){ + 28094 -> player.dialogueInterpreter.open(GETrapdoorDialogueID) + } + } + "enter" -> { + when(node.id){ + 5014 -> player.properties.teleportLocation = Location.create(2730, 3713, 0) + } + } + } + return true + } + + override fun newInstance(arg: Any?): Plugin { + SceneryDefinition.forId(5973).handlers["option:go-through"] = this + SceneryDefinition.forId(5998).handlers["option:go-through"] = this + SceneryDefinition.forId(9084).handlers["option:climb-down"] = this + SceneryDefinition.forId(9138).handlers["option:climb-up"] = this + SceneryDefinition.forId(28094).handlers["option:open"] = this + SceneryDefinition.forId(5014).handlers["option:enter"] = this + return this + } +} + +/** + * Dialogue used for the trapdoor in the grand exchange. + */ +@Initializable +class GETrapdoorDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> end() + 10 -> when(buttonId){ + 1 -> KeldagrimCartMethods.goToKeldagrim(player).also { end() } + 2 -> end() + } + } + return true + } + + override fun open(vararg args: Any?): Boolean { + val keldagrimVisited = player.getAttribute("keldagrim-visited",false) + if(keldagrimVisited){ + options("Travel to Keldagrim","Nevermind.") + stage = 10 + } else { + player.dialogueInterpreter.sendDialogue("Perhaps I should visit Keldagrim first.") + stage = 0 + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return GETrapdoorDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(GETrapdoorDialogueID) + } +} + + +class BlastFurnaceDoorDialogue(val fee: Int) : DialogueFile(){ + var init = true + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 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 (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 = END_DIALOGUE } + } + } + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/ReinaldSmithingEmporiumInterface.kt b/Server/src/main/content/region/misc/keldagrim/handlers/ReinaldSmithingEmporiumInterface.kt similarity index 99% rename from Server/src/main/kotlin/rs09/game/content/zone/keldagrim/ReinaldSmithingEmporiumInterface.kt rename to Server/src/main/content/region/misc/keldagrim/handlers/ReinaldSmithingEmporiumInterface.kt index 378e813d4..c6f0a0f9b 100644 --- a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/ReinaldSmithingEmporiumInterface.kt +++ b/Server/src/main/content/region/misc/keldagrim/handlers/ReinaldSmithingEmporiumInterface.kt @@ -1,4 +1,4 @@ -package rs09.game.content.zone.keldagrim +package content.region.misc.keldagrim.handlers import core.game.component.Component import core.game.component.ComponentDefinition diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/AlvissDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/AlvissDialogue.kt new file mode 100644 index 000000000..b3f4f7004 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/AlvissDialogue.kt @@ -0,0 +1,65 @@ +package content.region.misc.miscellania.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 AlvissDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + options("What are you doing down here?", "Good day.").also { stage++ } + } + + 1 -> when(buttonId){ + 1 -> { + player(FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } + } + } + + 10 -> { + 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(FacialExpression.OLD_DEFAULT," I'm also researching the links between the ", + "Fremenniks and the Dwarves.").also { stage++ } + } + + 12 -> { + npc(FacialExpression.OLD_DEFAULT,"I've found that we have some mythology in common.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return AlvissDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ALVISS_3933) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/DerrikDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/DerrikDialogue.kt new file mode 100644 index 000000000..4e55e6f08 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/DerrikDialogue.kt @@ -0,0 +1,56 @@ +package content.region.misc.miscellania.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 + +/** + * Dialogue for Derrik in Misc. + * @author qmqz + */ + +@Initializable +class DerrikDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Good day, Sir. Can I help you with anything?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + + 0 -> { + options("Can I use your anvil?", "Nothing, thanks.").also { stage++ } + } + + 1 -> when(buttonId){ + 1 -> { + player(FacialExpression.ASKING, "Can I use your anvil?").also { stage = 5 } + } + 2 -> { + player(FacialExpression.NEUTRAL, "Nothing, thanks.").also { stage = 99 } + } + } + + 5 -> { + npc(FacialExpression.NEUTRAL, "You may.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DerrikDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DERRIK_1376) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/DonalDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/DonalDialogue.kt new file mode 100644 index 000000000..a7aafe588 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/DonalDialogue.kt @@ -0,0 +1,52 @@ +package content.region.misc.miscellania.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 DonalDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(FacialExpression.THINKING, "Just wondering if you were still here.").also { stage++ } + } + + 1 -> { + npc(FacialExpression.OLD_DEFAULT, "Of course I'm still here.").also { stage++ } + } + + 2 -> { + npc(FacialExpression.OLD_DISTRESSED, "I'm not going near that crack in the wall again.").also { stage++ } + } + + 3 -> { + 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?): DialoguePlugin { + return DonalDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DONAL_3938) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FerdDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FerdDialogue.kt new file mode 100644 index 000000000..92d018524 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FerdDialogue.kt @@ -0,0 +1,66 @@ +package content.region.misc.miscellania.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 FerdDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + player(FacialExpression.THINKING, "What are you doing down here?.").also { stage++ } + } + + 1 -> { + npc(FacialExpression.OLD_DEFAULT, "Shoring up the walls.").also { stage++ } + } + + 2 -> { + player(FacialExpression.ASKING, "What does that do?").also { stage++ } + } + + 3 -> { + npc(FacialExpression.OLD_DEFAULT, "Stops them falling down.").also { stage = 99 } + } + + 4 -> { + player(FacialExpression.ASKING, "Oh, I see.").also { stage++ } + } + + 5 -> { + 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(FacialExpression.ASKING, "Okay then.").also { stage++ } + } + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FerdDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FERD_3937) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FinnDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FinnDialogue.kt new file mode 100644 index 000000000..f9c813a53 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FinnDialogue.kt @@ -0,0 +1,68 @@ +package content.region.misc.miscellania.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 + +/** + * Dialogue for Finn in Misc. + * @author qmqz + */ + +@Initializable +class FinnDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Can I help you, your Royal Highness?").also { stage = 0 } + stage = 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + options("Yes please. What are you selling?", "No thanks.", "What's it like living down here?").also { stage++ } + } + + 1 -> when(buttonId){ + 1 -> { + player(FacialExpression.ASKING, "Yes please. What are you selling?").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.NEUTRAL, "No thanks.").also { stage = 99 } + } + + 3 -> { + player(FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } + } + } + + 10 -> { + end().also { npc.openShop(player) } + } + + 20 -> { + npc(FacialExpression.HALF_WORRIED, "A lot drier in the winter than it is above ground.").also { stage = 99 } + } + + 99 -> { + end() + } + + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FinnDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FINN_3922) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FishmongerMiscDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FishmongerMiscDialogue.kt new file mode 100644 index 000000000..e2ece679c --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FishmongerMiscDialogue.kt @@ -0,0 +1,43 @@ +package content.region.misc.miscellania.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 org.rs09.consts.NPCs +import content.data.Quests + +/** + * @author qmqz + */ + +@Initializable +class FishmongerMiscDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, Quests.THRONE_OF_MISCELLANIA)) { + npcl(FacialExpression.FRIENDLY,"Greetings, Sir. Get your fresh fish here! I've heard that the Etceterian fish is stored in a cow shed.").also { stage = 0 } + } else { + npcl(FacialExpression.FRIENDLY,"Greetings, Your Highness. Have some fresh fish! I've heard that the Etceterian fish is stored in a cow shed.").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> end().also { npc.openShop(player) } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FishmongerMiscDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FISHMONGER_1393) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt new file mode 100644 index 000000000..6320d5ad1 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt @@ -0,0 +1,81 @@ +package content.region.misc.miscellania.dialogue + +import core.api.addItemOrDrop +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.plugin.Initializable +import org.rs09.consts.NPCs + +/** + * Dialogue for the Flower Girl in Misc. + * @author qmqz + */ + +@Initializable +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(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(FacialExpression.NEUTRAL, "Hello.").also { stage = 1 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 1 -> { + player(FacialExpression.ASKING, "Good day. What are you doing?").also { stage++ } + } + + 2 -> { + /* + 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(FacialExpression.NEUTRAL, "I'm selling flowers, 15gp for three. Would you like some?").also { stage++ } + } + + 3 -> { + options("Yes, please.", "No, thank you.").also { stage++ } + } + + 4 -> when(buttonId){ + 1 -> { + if (player.inventory.contains(995,15)) { + npc(FacialExpression.HAPPY, "Thank you! Here you go.").also { stage = 99 } + player.inventory.remove(Item(995, 15)) + addItemOrDrop(player, 2460, 1) + } else { + player(FacialExpression.HALF_THINKING, "I'm sorry, but I don't have 15gp.").also { stage = 99 } + } + } + 2 -> { + player(FacialExpression.NEUTRAL, "No, thank you.").also { stage = 99 } + } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FlowerGirlDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FLOWER_GIRL_1378) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FullangrDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FullangrDialogue.kt new file mode 100644 index 000000000..c8e0530e8 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FullangrDialogue.kt @@ -0,0 +1,57 @@ +package content.region.misc.miscellania.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 FullangrDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + options("What are you doing down here?", "Good day.").also { stage++ } + } + + 1 -> when(buttonId){ + 1 -> { + player(FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } + } + } + + 10 -> { + 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 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FullangrDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FULLANGR_3934) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/GreengrocerMiscDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/GreengrocerMiscDialogue.kt new file mode 100644 index 000000000..789d09154 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/GreengrocerMiscDialogue.kt @@ -0,0 +1,41 @@ +package content.region.misc.miscellania.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 + +/** + * Dialogue for the Fishmonger in Misc. + * @author qmqz + */ + +@Initializable +class GreengrocerMiscDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Welcome, Sir.", + "I sell only the finest and freshest vegetables!").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage){ + 0 -> { + end().also { npc.openShop(player) } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return GreengrocerMiscDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GREENGROCER_1394) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/HallaDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/HallaDialogue.kt new file mode 100644 index 000000000..ca55da312 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/HallaDialogue.kt @@ -0,0 +1,70 @@ +package content.region.misc.miscellania.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 HallaDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(FacialExpression.ASKING, "Can I help you with anything, your Royal Highness?").also { stage++ } + } + + 1 -> { + options("I'd like to look at what you have for sale.", "No thank you, I'm fine.", "What's it like living down here?").also { stage++ } + } + + 2 -> when(buttonId){ + 1 -> { + player(FacialExpression.FRIENDLY, "I'd like to look at what you have for sale.").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.NEUTRAL, "No thank you, I'm fine.").also { stage = 99 } + } + + 3 -> { + player(FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } + } + } + + 10 -> { + end().also { npc.openShop(player) } + } + + 20 -> { + 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(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() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return HallaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HALLA_3921) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/JariDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/JariDialogue.kt new file mode 100644 index 000000000..94e6c2def --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/JariDialogue.kt @@ -0,0 +1,60 @@ +package content.region.misc.miscellania.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 JariDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + options("What are you doing down here?", "Good day.").also { stage++ } + } + + 1 -> when(buttonId){ + 1 -> { + player(FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } + } + } + + 10 -> { + npc(FacialExpression.OLD_DEFAULT,"I'm waiting to work on the digging.").also { stage++ } + } + + 11 -> { + npc(FacialExpression.OLD_HAPPY,"It's the first excavation I've worked on, ", + "and I'm looking forward to it.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return JariDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.JARI_3935) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/KjallakOnChopDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/KjallakOnChopDialogue.kt new file mode 100644 index 000000000..a8e674228 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/KjallakOnChopDialogue.kt @@ -0,0 +1,13 @@ +package content.region.misc.miscellania.dialogue + +import core.tools.END_DIALOGUE +import core.game.dialogue.DialogueFile + +class KjallakOnChopDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npc("Hey! You're not allowed to chop those!").also { stage++ } + 1 -> player("Oh, ok...").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/MiscCitizenGoodDayDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/MiscCitizenGoodDayDialogue.kt new file mode 100644 index 000000000..72247144d --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/MiscCitizenGoodDayDialogue.kt @@ -0,0 +1,45 @@ +package content.region.misc.miscellania.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 MiscCitizenGoodDayDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + npc(FacialExpression.FRIENDLY, "Good day, Your Royal Highness.").also { stage = 99 } + } + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return MiscCitizenGoodDayDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ALRIK_1381, NPCs.RUNOLF_3924, + NPCs.BRODDI_1390, NPCs.EINAR_1380, NPCs.INGRID_3926, + NPCs.RAGNAR_1379, NPCs.RAGNVALD_1392, NPCs.RANNVEIG_1386, + NPCs.THORA_3927, NPCs.THORA_1387, NPCs.THORHILD_1382, + NPCs.VALGERD_1388, NPCs.TJORVI_3925 + ) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/OsvaldDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/OsvaldDialogue.kt new file mode 100644 index 000000000..ad21fb7ea --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/OsvaldDialogue.kt @@ -0,0 +1,73 @@ +package content.region.misc.miscellania.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 OsvaldDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(FacialExpression.NEUTRAL, "Would you like to buy anything,", + "your Royal Highness?").also { stage++ } + } + + 1 -> { + options("Could you show me what you have for sale?", + "No thank you, I don't need food just now.", + "What's it like living down here?").also { stage++ } + } + + 2 -> when(buttonId){ + 1 -> { + player(FacialExpression.ASKING, "Could you show me what you have for sale?").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.NEUTRAL, "No thank you, I don't need food just now.").also { end() } + } + + 3 -> { + player(FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } + } + } + + 10 -> { + end().also { npc.openShop(player) } + } + + 20 -> { + npc(FacialExpression.FRIENDLY, "The town's thriving.", + "I'm sure it'll soon be as busy as Rellekka!").also { stage = 99 } + } + + 99 -> { + end() + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return OsvaldDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.OSVALD_3923) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/RunaDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/RunaDialogue.kt new file mode 100644 index 000000000..8a13bdabe --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/RunaDialogue.kt @@ -0,0 +1,77 @@ +package content.region.misc.miscellania.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 RunaDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(FacialExpression.ASKING, "Well I say Miscellanian, but it's actually brewed", "on the mainland.").also { stage++ } + } + + 1 -> { + npc(FacialExpression.FRIENDLY, "Would you like to try some anyway?").also { stage++ } + } + + 2 -> { + options("Yes, please.", "No, thank you.", "What's it like living down here?").also { stage++ } + } + + 3 -> when(buttonId){ + 1 -> { + player(FacialExpression.ASKING, "Yes please.").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.NEUTRAL, "No thank you.").also { stage = 99 } + } + + 3 -> { + player(FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } + } + } + + 10 -> { + end().also { npc.openShop(player) } + } + + 20 -> { + npc(FacialExpression.HALF_WORRIED, "Business is booming!").also { stage++ } + } + + 21 -> { + npc(FacialExpression.HALF_WORRIED, "Now, if only I hadn't taken a loss to the beer I sold", "to those teenagers.").also { end() } + } + + 99 -> { + end() + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return RunaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.RUNA_3920) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/ThorodinDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/ThorodinDialogue.kt new file mode 100644 index 000000000..6cf33d5d5 --- /dev/null +++ b/Server/src/main/content/region/misc/miscellania/dialogue/ThorodinDialogue.kt @@ -0,0 +1,75 @@ +package content.region.misc.miscellania.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 ThorodinDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + options("What are you doing down here?", "Good day.").also { stage++ } + } + + 1 -> when(buttonId){ + 1 -> { + player(FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } + } + + 2 -> { + player(FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } + } + } + + 10 -> { + 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(FacialExpression.ASKING,"...such as?").also { stage++ } + } + + 12 -> { + 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(FacialExpression.FRIENDLY,"It's been taken care of.").also { stage++ } + } + + 14 -> { + npc(FacialExpression.OLD_HAPPY,"Glad to hear it.", + "Now we can get on with excavating.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ThorodinDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.THORODIN_3936) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/mosle/dialogue/CharleyDialogue.kt b/Server/src/main/content/region/misc/mosle/dialogue/CharleyDialogue.kt new file mode 100644 index 000000000..9461b96a2 --- /dev/null +++ b/Server/src/main/content/region/misc/mosle/dialogue/CharleyDialogue.kt @@ -0,0 +1,77 @@ +package content.region.misc.mosle.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.Items +import org.rs09.consts.NPCs + +/** + * @author qmqz + */ + +@Initializable +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(FacialExpression.FRIENDLY, "I got fish, you got gold?").also { stage = 10 } + } else { + player(FacialExpression.FRIENDLY, "Hello!").also { stage = 0 } + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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(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(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() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return CharleyDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CHARLEY_3161) + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/MamaDialogue.java b/Server/src/main/content/region/misc/mosle/dialogue/MamaDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/MamaDialogue.java rename to Server/src/main/content/region/misc/mosle/dialogue/MamaDialogue.java index 3f7d8e79e..91c1f1591 100644 --- a/Server/src/main/java/core/game/content/dialogue/MamaDialogue.java +++ b/Server/src/main/content/region/misc/mosle/dialogue/MamaDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misc.mosle.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; diff --git a/Server/src/main/java/core/game/content/dialogue/PatchyDialogue.java b/Server/src/main/content/region/misc/mosle/dialogue/PatchyDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/PatchyDialogue.java rename to Server/src/main/content/region/misc/mosle/dialogue/PatchyDialogue.java index aaab1bcfc..36c74c651 100644 --- a/Server/src/main/java/core/game/content/dialogue/PatchyDialogue.java +++ b/Server/src/main/content/region/misc/mosle/dialogue/PatchyDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misc.mosle.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; diff --git a/Server/src/main/content/region/misc/piratecove/dialogue/BeefyBurnsDialogue.kt b/Server/src/main/content/region/misc/piratecove/dialogue/BeefyBurnsDialogue.kt new file mode 100644 index 000000000..9ee5b708c --- /dev/null +++ b/Server/src/main/content/region/misc/piratecove/dialogue/BeefyBurnsDialogue.kt @@ -0,0 +1,43 @@ +package content.region.misc.piratecove.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 BeefyBurnsDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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?): DialoguePlugin { + return BeefyBurnsDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BEEFY_BURNS_4541) + } +} diff --git a/Server/src/main/content/region/misc/piratecove/dialogue/DaveyBoyDialogue.kt b/Server/src/main/content/region/misc/piratecove/dialogue/DaveyBoyDialogue.kt new file mode 100644 index 000000000..756915ac2 --- /dev/null +++ b/Server/src/main/content/region/misc/piratecove/dialogue/DaveyBoyDialogue.kt @@ -0,0 +1,51 @@ +package content.region.misc.piratecove.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 DaveyBoyDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + when ((1..2).random()) { + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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?): DialoguePlugin { + return DaveyBoyDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FIRST_MATE_DAVEY_BOY_4543) + } +} diff --git a/Server/src/main/content/region/misc/piratecove/dialogue/EagleEyeShultzDialogue.kt b/Server/src/main/content/region/misc/piratecove/dialogue/EagleEyeShultzDialogue.kt new file mode 100644 index 000000000..ab7083b0b --- /dev/null +++ b/Server/src/main/content/region/misc/piratecove/dialogue/EagleEyeShultzDialogue.kt @@ -0,0 +1,42 @@ +package content.region.misc.piratecove.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 EagleEyeShultzDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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?): DialoguePlugin { + return EagleEyeShultzDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EAGLE_EYE_SHULTZ_4542) + } +} diff --git a/Server/src/main/content/region/misc/piratecove/dialogue/LecherousLeeDialogue.kt b/Server/src/main/content/region/misc/piratecove/dialogue/LecherousLeeDialogue.kt new file mode 100644 index 000000000..566450942 --- /dev/null +++ b/Server/src/main/content/region/misc/piratecove/dialogue/LecherousLeeDialogue.kt @@ -0,0 +1,107 @@ +package content.region.misc.piratecove.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 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(FacialExpression.FRIENDLY, "Hello.").also { stage = conversations.random() } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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(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(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(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(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(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(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(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(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(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() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return LecherousLeeDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LECHEROUS_LEE_4556) + } +} 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 new file mode 100644 index 000000000..f19f67a64 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt @@ -0,0 +1,49 @@ +package content.region.misc.tutisland.dialogue + +import core.api.setAttribute +import core.api.teleport +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 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, Player Name + */ +@Initializable +class SkipTutorialDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return SkipTutorialDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + 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++ } + 1 -> when(buttonId) + { + 1 -> { + end() + setAttribute(player, "/save:tutorial:stage", 71) + TutorialStage.load(player, 71) + teleport(player, Location.create(3141, 3089, 0)) + } + 2 -> end() + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SKIPPY_2796) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt new file mode 100644 index 000000000..4e0385178 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt @@ -0,0 +1,108 @@ +package content.region.misc.tutisland.dialogue + +import core.api.addItemOrDrop +import core.api.inInventory +import core.api.setAttribute +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 + +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 + } + } +} + +class SurvivalExpertDialogueFile : DialogueLabeller() { + override fun addConversation() { + assignToIds(NPCs.SURVIVAL_EXPERT_943) + + 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") + } + 11 -> loadLabel(player, "fishing") + else -> loadLabel(player, "nowhere") + } + } + + 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") + } + val stage = getAttribute(player, "/save:tutorial:stage", 0) + if (stage >= 11 && !inInventory(player, Items.SMALL_FISHING_NET_303)) { + loadLabel(player, "spare net") + } + } + goto("nowhere") //closes the dialogue, letting the hook reopen the tutorial stage dialog as appropriate + + 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") + + 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 new file mode 100644 index 000000000..1ab335e26 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt @@ -0,0 +1,106 @@ +package content.region.misc.tutisland.dialogue + +import core.game.node.entity.npc.NPC +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 + +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 addConversation() { + assignToIds(NPCs.COMBAT_INSTRUCTOR_944) + + exec { player, _ -> + when (getAttribute(player, "tutorial:stage", 0)) { + 44 -> loadLabel(player, "hello") + 47 -> loadLabel(player, "butter") + 48, 49, 50, 51, 52 -> loadLabel(player, if (lostWeapon(player, Items.BRONZE_SWORD_1277)) "lost sword" else if (lostWeapon(player, Items.WOODEN_SHIELD_1171)) "lost shield" else "nowhere") + 53 -> loadLabel(player, "killed rat") + 54 -> loadLabel(player, if (lostWeapon(player, Items.SHORTBOW_841)) "lost bow" else if (lostWeapon(player, Items.BRONZE_ARROW_882)) "lost arrows" else "nowhere") + else -> loadLabel(player, "nowhere") + } + } + + label("hello") + player(ChatAnim.FRIENDLY, "Hi! My name's ${player?.username}.", unclosable = true) + npc(ChatAnim.ANGRY, "Do I look like I care? To me you're just another newcomer who thinks they're ready to fight.", unclosable = true) + npc(ChatAnim.FRIENDLY, "I'm Vannaka, the greatest swordsman alive.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Let's get started by teaching you to wield a weapon.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 45) + TutorialStage.load(player, 45) + } + + label("butter") + npc(ChatAnim.FRIENDLY, "Very good, but that little butter knife isn't going to protect you much. Here, take these.", unclosable = true) + exec { player, _ -> + addItemOrDrop(player, Items.BRONZE_SWORD_1277) + addItemOrDrop(player, Items.WOODEN_SHIELD_1171) + } + item(Item(Items.BRONZE_SWORD_1277), Item(Items.WOODEN_SHIELD_1171), "The Combat Guide gives you a bronze sword and a", "wooden shield!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 48) + TutorialStage.load(player, 48) + } + goto("nowhere") + + label("lost sword") + exec { player, _ -> addItemOrDrop(player, Items.BRONZE_SWORD_1277) } + item(Item(Items.BRONZE_SWORD_1277), "The Combat Guide gives you a spare sword.", unclosable = true) + exec { player, _ -> if (lostWeapon(player, Items.WOODEN_SHIELD_1171)) loadLabel(player, "lost shield") } + goto("nowhere") + + label("lost shield") + exec { player, _ -> addItemOrDrop(player, Items.WOODEN_SHIELD_1171) } + item(Item(Items.WOODEN_SHIELD_1171), "The Combat Guide gives you a spare shield.", unclosable = true) + goto("nowhere") + + label("killed rat") + player(ChatAnim.FRIENDLY, "I did it! I killed a giant rat!", unclosable = true) + npc(ChatAnim.FRIENDLY, "I saw, ${player?.username}. You seem better at this than I thought. Now that you have grasped basic swordplay, let's move on.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Let's try some ranged attacking, with this you can kill foes from a distance. Also, foes unable to reach you are as good as dead. You'll be able to attack the rats, without entering the pit.", unclosable = true) + exec { player, _ -> + addItemOrDrop(player, Items.SHORTBOW_841) + addItemOrDrop(player, Items.BRONZE_ARROW_882, 30) + } + item(Item(Items.SHORTBOW_841), Item(Items.BRONZE_ARROW_882), "The Combat Guide gives you some bronze arrows and", "a shortbow!", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 54) + TutorialStage.load(player, 54) + } + goto("nowhere") + + label("lost bow") + exec { player, _ -> addItemOrDrop(player, Items.SHORTBOW_841) } + item(Item(Items.SHORTBOW_841), "The Combat Guide gives you a spare bow.", unclosable = true) + goto("nowhere") + + label("lost arrows") + exec { player, _ -> addItemOrDrop(player, Items.BRONZE_ARROW_882, 10) } + item(Item(Items.BRONZE_ARROW_882), "You receive some spare arrows.", unclosable = true) + goto("nowhere") + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } + } +} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt new file mode 100644 index 000000000..d94bcd922 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialFinanceAdvisorDialogue.kt @@ -0,0 +1,59 @@ +package content.region.misc.tutisland.dialogue + +import core.api.getAttribute +import core.api.setAttribute +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.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, Player Name + */ +@Initializable +class TutorialFinanceAdvisorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return TutorialFinanceAdvisorDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + when(getAttribute(player, "tutorial:stage", 0)) + { + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(getAttribute(player, "tutorial:stage", 0)){ + 58 -> when(stage++){ + 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 + } + + 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 new file mode 100644 index 000000000..68707766d --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt @@ -0,0 +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.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 + +@Initializable +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 + } + openDialogue(player, TutorialMagicTutorDialogueFile(), NPC(NPCs.MAGIC_INSTRUCTOR_946)) + return@on true + } + } +} + +class TutorialMagicTutorDialogueFile : DialogueLabeller() { + override fun addConversation() { + val STARTER_PACK = arrayOf( + Item(Items.BRONZE_AXE_1351), + Item(Items.TINDERBOX_590), + Item(Items.SMALL_FISHING_NET_303), + Item(Items.SHRIMPS_315), + Item(Items.BUCKET_1925), + Item(Items.EMPTY_POT_1931), + Item(Items.BREAD_2309), + Item(Items.BRONZE_PICKAXE_1265), + Item(Items.BRONZE_DAGGER_1205), + Item(Items.BRONZE_SWORD_1277), + Item(Items.WOODEN_SHIELD_1171), + Item(Items.SHORTBOW_841), + Item(Items.BRONZE_ARROW_882, 25), + Item(Items.AIR_RUNE_556, 25), + Item(Items.MIND_RUNE_558, 15), + Item(Items.WATER_RUNE_555, 6), + Item(Items.EARTH_RUNE_557, 4), + Item(Items.BODY_RUNE_559, 2) + ) + val STARTER_BANK = arrayOf(Item(Items.COINS_995, 25)) + + exec { player, _ -> + when (getAttribute(player, "tutorial:stage", 0)) { + 67 -> loadLabel(player, "hello") + 69 -> loadLabel(player, "spelllist") + 70 -> { + if (!inInventory(player, Items.AIR_RUNE_556, 1) || !inInventory(player, Items.MIND_RUNE_558, 1)) { + loadLabel(player, "givemorerunes") + } else { + goto("nowhere") + } + } + 71 -> loadLabel(player, "finishedtutorial") + else -> goto("nowhere") + } + } + + label("hello") + player(ChatAnim.FRIENDLY, "Hello.", unclosable = true) + npc(ChatAnim.FRIENDLY, "Good day, newcomer. My name is Terrova. I'm here to tell you about Magic. Let's start by opening your spell list.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 68) + TutorialStage.load(player, 68) + } + + label("spelllist") + npc(ChatAnim.FRIENDLY, "Good. This is a list of your spells. Currently you can only cast one offensive spell called Wind Strike. Let's try it out on one of those chickens.", unclosable = true) + exec { player, _ -> + setAttribute(player, "tutorial:stage", 70) + addItemOrDrop(player, Items.AIR_RUNE_556, 15) + addItemOrDrop(player, Items.MIND_RUNE_558, 15) + } + item(Item(Items.AIR_RUNE_556), Item(Items.MIND_RUNE_558), "Terrova gives you 15 air runes and 15 mind runes!", unclosable = true) + exec { player, _ -> TutorialStage.load(player, 70) } + + label("givemorerunes") + exec { player, _ -> + addItemOrDrop(player, Items.AIR_RUNE_556, 5) + addItemOrDrop(player, Items.MIND_RUNE_558, 5) + } + item(Item(Items.AIR_RUNE_556), Item(Items.MIND_RUNE_558), "You receive some spare runes.", unclosable = true) + exec { player, _ -> TutorialStage.load(player, 70) } + + label("finishedtutorial") + exec { player, _ -> + if (ServerConstants.XP_RATES || ServerConstants.IRONMAN) { + loadLabel(player, "talk about inauthentic") + } else { + loadLabel(player, "leave") + } + } + + label("talk about inauthentic") + npc(ChatAnim.FRIENDLY, "Alright, last thing. Are you interested in our inauthentic ${ServerConstants.SERVER_NAME} features?", unclosable = true) + goto("inauthentic") + + label("inauthentic") + options( + DialogueOption("xprate","Change XP rate (current: ${player?.skills?.experienceMultiplier}x)", skipPlayer = true) { _, _ -> + return@DialogueOption ServerConstants.XP_RATES + }, + DialogueOption("ironman","Set ironman mode (current: ${player?.ironmanManager?.mode?.name?.toLowerCase()})", skipPlayer = true) { _, _ -> + return@DialogueOption ServerConstants.IRONMAN + }, + DialogueOption("leave","I'm ready now."), + unclosable = true) + + label("xprate") + options( + DialogueOption("1.0x","1.0x (default)", skipPlayer = true), + DialogueOption("2.5x","2.5x", skipPlayer = true), + DialogueOption("5.0x","5.0x", skipPlayer = true), + title = "Change XP rate (current: ${player?.skills?.experienceMultiplier}x)", + unclosable = true + ) + for (rate in doubleArrayOf(1.0, 2.5, 5.0)) { + label("${rate}x") + exec { player, _ -> player.skills.experienceMultiplier = rate } + manual(unclosable = true) { player, _ -> player.dialogueInterpreter.sendDialogue("You set your XP rate to: ${rate}x.") } + goto("inauthentic") + } + + label("ironman") + options( + DialogueOption("NONE","None (default)", skipPlayer = true), + DialogueOption("STANDARD","Standard", skipPlayer = true), + DialogueOption("ULTIMATE","Ultimate (no bank)", skipPlayer = true), + title = "Change ironman mode (current: ${player?.ironmanManager?.mode?.name?.toLowerCase()}x)" + ) + for (mode in arrayOf(IronmanMode.NONE, IronmanMode.STANDARD, IronmanMode.ULTIMATE)) { + label(mode.name) + exec { player, _ -> player.ironmanManager.mode = mode } + manual(unclosable = true){ player, _ -> return@manual player.dialogueInterpreter.sendDialogue("You set your ironman mode to: ${mode.name.toLowerCase()}.") } + exec { player, _ -> loadLabel(player, if (player.ironmanManager.mode == IronmanMode.NONE) "inauthentic" else "ironwarning") } + } + + label("ironwarning") + manual(unclosable = true) { player, _ -> player.dialogueInterpreter.sendDialogue(*splitLines("WARNING: You have selected an ironman mode. This is an uncompromising mode that WILL completely restrict your ability to trade. This MAY leave you unable to complete certain content, including quests.")) } + goto("inauthentic") + + label("leave") + npc(ChatAnim.FRIENDLY, "Well, you're all finished here now. I'll give you a reasonable number of starting items when you leave.", unclosable = true) + options( + DialogueOption("leave:yes","Yes, I'm ready.","I'm ready to go now, thank you.", ChatAnim.FRIENDLY), + DialogueOption("nowhere","No, not yet.","I'm not quite ready to go yet, thank you.", ChatAnim.FRIENDLY), + title = "Leave Tutorial Island?", + unclosable = true + ) + + label("leave:yes") + manual { player, _ -> + setAttribute(player, "/save:tutorial:complete", true) + setVarbit(player, 3756, 0) + setVarp(player, 281, 1000, true) + teleport(player, Location.create(3233, 3230), TeleportManager.TeleportType.NORMAL) + closeOverlay(player) + + player.inventory.clear() + player.bank.clear() + player.equipment.clear() + player.interfaceManager.restoreTabs() + player.interfaceManager.setViewedTab(3) + player.inventory.add(*STARTER_PACK) + player.bank.add(*STARTER_BANK) + + TutorialStage.removeHintIcon(player) + player.unhook(TutorialKillReceiver) + player.unhook(TutorialFireReceiver) + player.unhook(TutorialResourceReceiver) + player.unhook(TutorialUseWithReceiver) + player.unhook(TutorialInteractionReceiver) + player.unhook(TutorialButtonReceiver) + player.unhook(TutorialDialogPreserver) + + if (GameWorld.settings!!.enable_default_clan) { + player.communication.currentClan = ServerConstants.SERVER_NAME.toLowerCase() + + val clanJoin = JoinClanRequest.newBuilder() + clanJoin.clanName = ServerConstants.SERVER_NAME.toLowerCase() + clanJoin.username = player.name + + ManagementEvents.publish(clanJoin.build()) + } + + // This shows the actual dialog, which is what this manual stage is for. + // Dialog is from 2007 or thereabouts. + // Original is five lines, but if the same is done here it will break. Need to find another way of showing all this information. + player.dialogueInterpreter.sendDialogue( + "Welcome to Lumbridge! To get more help, simply click on the", + "Lumbridge Guide or one of the Tutors - these can be found by looking", + "for the question mark icon on your mini-map. If you find you are lost", + "at any time, look for a signpost or use the Lumbridge Home Port Spell." + ) + if (ServerConstants.RULES_AND_INFO_ENABLED) { + RulesAndInfo.openFor(player) + // The teleport finishing will release the player, so we need to relock them here + queueScript(player, 4, QueueStrength.SOFT) { _ -> + player.lock() + return@queueScript stopExecuting(player) + } + } + return@manual null + } + + label("nowhere") + exec { player, _ -> sendStageDialog(player) } + } +} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt new file mode 100644 index 000000000..89d985f29 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt @@ -0,0 +1,75 @@ +package content.region.misc.tutisland.dialogue + +import core.api.* +import core.game.component.Component.setUnclosable +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 + +/** + * Handles the cooking tutor's dialogue + * @author Ceikry + */ +@Initializable +class TutorialMasterChefDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return TutorialMasterChefDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + when(getAttribute(player, "tutorial:stage", 0)) + { + 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)) { + 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 + } + } + return false + } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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 -> { + 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() + TutorialStage.load(player, 19) + } + } + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MASTER_CHEF_942) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt new file mode 100644 index 000000000..fc1f96c81 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt @@ -0,0 +1,95 @@ +package content.region.misc.tutisland.dialogue + +import core.game.node.entity.npc.NPC +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 + +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 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") + } + } + + 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) + } + + 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") + + 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 new file mode 100644 index 000000000..3fb71bd16 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt @@ -0,0 +1,70 @@ +package content.region.misc.tutisland.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 +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) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return TutorialPrayerDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + when(getAttribute(player, "tutorial:stage", 0)) + { + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(getAttribute(player, "tutorial:stage", 0)) + { + 60 -> when(stage++){ + 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) + TutorialStage.load(player, 61) + } + } + + 62 -> when(stage++){ + 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) + TutorialStage.load(player, 63) + } + } + + 65 -> { + end() + setAttribute(player, "tutorial:stage", 66) + TutorialStage.load(player, 66) + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BROTHER_BRACE_954) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt new file mode 100644 index 000000000..716bc2cf4 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt @@ -0,0 +1,122 @@ +package content.region.misc.tutisland.dialogue + +import core.api.getAttribute +import core.api.setAttribute +import core.api.setVarbit +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.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) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return TutorialQuestGuideDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + when(getAttribute(player, "tutorial:stage", 0)) + { + 27 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + FacialExpression.FRIENDLY, + "Ah. Welcome, adventurer. I'm here to tell you all about", + "quests. Lets start by opening the Quest List." + ) + ) + + 28 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + 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", + "means you have not started them yet." + ) + ) + + else -> return false + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(getAttribute(player, "tutorial:stage", 0)) + { + 27 -> { + setUnclosable( + player, + interpreter.sendPlaneMessageWithBlueTitle( + "Open the Quest Journal.", + "", + "Click on the flashing icon next to your inventory.", + "", + "" + ) + ) + setVarbit(player, 3756, 3) + player.interfaceManager.openTab(Component(Components.QUESTJOURNAL_V2_274)) + } + 28 -> when(stage) + { + 0 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + 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 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + 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 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + 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 adventure in the caves", + "under my house." + ) + ).also { stage++ } + 3 -> { + end() + setAttribute(player, "tutorial:stage", 29) + TutorialStage.load(player, 29) + } + } + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.QUEST_GUIDE_949) + } +} diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt new file mode 100644 index 000000000..b3611f89e --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt @@ -0,0 +1,123 @@ +package content.region.misc.tutisland.dialogue + +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.NPCs +import content.region.misc.tutisland.handlers.TutorialStage +import core.game.component.Component.setUnclosable + +/** + * Handles the 2009scape guide's dialogue + * @author Ceikry + * @author Player Name + */ +@Initializable +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) { + 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, + 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 + } 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 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + FacialExpression.FRIENDLY, + "You have already learned the first thing needed to", + "succeed in this world: talking to other people!" + ) + ).also { stage++ } + + 1 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + 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 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + 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", + "additional information you're ever likely to need. they also" + ) + ).also { stage++ } + + 3 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + FacialExpression.FRIENDLY, + "contain helpful tips to help you on your", + "journey." + ) + ).also { stage++ } + + 4 -> setUnclosable( + player, + interpreter.sendDialogues( + npc, + FacialExpression.FRIENDLY, + "To continue the tutorial go through that door over", + "there and speak to your first instructor!" + ) + ).also { stage++ } + + 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 + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.RUNESCAPE_GUIDE_945) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/npc/other/RatTutorialNPC.java b/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java similarity index 78% rename from Server/src/main/java/core/game/node/entity/npc/other/RatTutorialNPC.java rename to Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java index f5decd4b3..56845ac8b 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/RatTutorialNPC.java +++ b/Server/src/main/content/region/misc/tutisland/handlers/RatTutorialNPC.java @@ -1,8 +1,6 @@ -package core.game.node.entity.npc.other; +package content.region.misc.tutisland.handlers; import core.plugin.Initializable; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; import core.game.node.entity.skill.Skills; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; @@ -10,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. @@ -66,14 +65,8 @@ public class RatTutorialNPC extends AbstractNPC { return; } final Player p = ((Player) killer); - if (TutorialSession.getExtension(p).getStage() == 52) { - TutorialStage.load(p, 53, false); - } - if (TutorialSession.getExtension(p).getStage() == 54) { - TutorialStage.load(p, 55, false); - } 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/java/core/game/node/entity/npc/other/SkippyNPC.java b/Server/src/main/content/region/misc/tutisland/handlers/SkippyNPC.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/other/SkippyNPC.java rename to Server/src/main/content/region/misc/tutisland/handlers/SkippyNPC.java index 88bdfbc2c..828e2606e 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/SkippyNPC.java +++ b/Server/src/main/content/region/misc/tutisland/handlers/SkippyNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.misc.tutisland.handlers; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; 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 new file mode 100644 index 000000000..c65191bb1 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt @@ -0,0 +1,313 @@ +package content.region.misc.tutisland.handlers + +import core.api.getAttribute +import core.api.setAttribute +import core.game.node.entity.Entity +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, Player Name + */ +object TutorialButtonReceiver : EventHook +{ + override fun process(entity: Entity, event: ButtonClickEvent) { + if(entity !is Player) return + + when(getAttribute(entity, "tutorial:stage", 0)) + { + //character design interface, confirm button + 0 -> if(event.iface == 771 && event.buttonId == 362) { + setAttribute(entity, "/save:tutorial:stage", 1) + TutorialStage.load(entity, 1) + } + + //click settings tab SD: 548,24 HD: 746,52 + 1 -> if((event.iface == 548 && event.buttonId == 24) || (event.iface == 746 && event.buttonId == 52)) { + setAttribute(entity, "/save:tutorial:stage", 2) + TutorialStage.load(entity, 2) + } + + //click inventory tab SD: 548,41 HD: 746,44 + 5 -> if((event.iface == 548 && event.buttonId == 41) || (event.iface == 746 && event.buttonId == 44)) { + setAttribute(entity, "tutorial:stage", 6) + TutorialStage.load(entity, 6) + } + + //Click skills tab SD: 548,39 HD: 746,42 + 10 -> if((event.iface == 548 && event.buttonId == 39) || (event.iface == 746 && event.buttonId == 42)) { + setAttribute(entity, "tutorial:stage", 11) + TutorialStage.load(entity, 11) + } + + //Click music tab SD: 548,26 HD: 746,54 + 21 -> if((event.iface == 548 && event.buttonId == 26) || (event.iface == 746 && event.buttonId == 54)) { + setAttribute(entity, "tutorial:stage", 22) + TutorialStage.load(entity, 22) + } + + //Click emote tab SD: 548,25 HD: 746,53 + 23 -> if((event.iface == 548 && event.buttonId == 25) || (event.iface == 746 && event.buttonId == 53)){ + setAttribute(entity, "tutorial:stage", 24) + TutorialStage.load(entity, 24) + } + + //Click any emote + 24 -> if(event.iface == 464){ + setAttribute(entity, "tutorial:stage", 25) + TutorialStage.load(entity, 25) + } + + //click run button + 25 -> if(event.iface == 261 && event.buttonId == 3 || event.iface == 750 && event.buttonId == 1){ + setAttribute(entity, "tutorial:stage", 26) + TutorialStage.load(entity, 26) + } + + //Open quest journal tab SD:548,40 HD:746,43 + 27 -> if((event.iface == 548 && event.buttonId == 40) || (event.iface == 746 && event.buttonId == 43)){ + setAttribute(entity, "tutorial:stage", 28) + TutorialStage.load(entity, 28) + } + + //Open equipment tab SD:548,42 HD:746,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) + } + + //Open weapon interface SD:548,38 HD:746,41 + 49 -> if((event.iface == 548 && event.buttonId == 38) || (event.iface == 746 && event.buttonId == 41)) { + setAttribute(entity, "tutorial:stage", 50) + TutorialStage.load(entity, 50) + } + + //Open prayer interface SD:548,43 HD:746,46 + 61 -> if((event.iface == 548 && event.buttonId == 43) || (event.iface == 746 && event.buttonId == 46)){ + setAttribute(entity, "tutorial:stage", 62) + TutorialStage.load(entity, 62) + } + + //Open friends tab SD:548,21 HD:746,49 + 63 -> if((event.iface == 548 && event.buttonId == 21) || (event.iface == 746 && event.buttonId == 49)){ + setAttribute(entity, "tutorial:stage", 64) + TutorialStage.load(entity, 64) + } + + //Open ignore list tab SD:548,22 HD:746,50 + 64 -> if((event.iface == 548 && event.buttonId == 22) || (event.iface == 746 && event.buttonId == 50)){ + setAttribute(entity, "tutorial:stage", 65) + TutorialStage.load(entity, 65) + } + + //Open magic tab SD:548,44 HD:746,47 + 68 -> if((event.iface == 548 && event.buttonId == 44) || (event.iface == 746 && event.buttonId == 47)){ + setAttribute(entity, "tutorial:stage", 69) + TutorialStage.load(entity, 69) + } + } + } +} + +object TutorialInteractionReceiver : EventHook +{ + override fun process(entity: Entity, event: InteractionEvent) { + if(entity !is Player) return + + when(getAttribute(entity, "tutorial:stage", 0)) + { + //Click on tree and start chopping + 6 -> if((WoodcuttingNode.forId(event.target.id)?.identifier ?: -1) == 1.toByte()) + { + setAttribute(entity, "tutorial:stage", 7) + TutorialStage.load(entity, 7) + } + + //Click on fishing spot to start fishing + 12 -> if(FishingSpot.forId(event.target.id) != null) + { + setAttribute(entity, "tutorial:stage", 13) + TutorialStage.load(entity, 13) + } + + //Mine rock - Tin + 35 -> if(MiningNode.forId(event.target.id)?.identifier?.equals(2.toByte()) == true && event.option == "mine"){ + setAttribute(entity, "tutorial:stage", 36) + TutorialStage.load(entity, 36) + } + + //Equip bronze dagger + 46 -> if(event.target.id == Items.BRONZE_DAGGER_1205 && event.option == "equip"){ + setAttribute(entity, "tutorial:stage", 47) + TutorialStage.load(entity, 47) + } + + //Equip sword and shield + 48 -> { + if(event.target.id == Items.BRONZE_SWORD_1277 && event.option == "equip"){ + setAttribute(entity, "/save:tutorial:sword", true) + } + else if(event.target.id == Items.WOODEN_SHIELD_1171 && event.option == "equip"){ + setAttribute(entity, "/save:tutorial:shield", true) + } + if(getAttribute(entity, "tutorial:shield",false) && getAttribute(entity, "tutorial:sword",false)) + { + setAttribute(entity, "tutorial:stage", 49) + TutorialStage.load(entity, 49) + } + } + + //Attack rat + 51 -> if(event.target.id == NPCs.GIANT_RAT_86 && event.option == "attack") { + setAttribute(entity, "tutorial:stage", 52) + TutorialStage.load(entity, 52) + } + + //Open bank + 56 -> if(event.target.name.contains("booth", true) && event.option == "use"){ + setAttribute(entity, "tutorial:stage", 57) + TutorialStage.load(entity, 57) + } + } + } +} + +object TutorialResourceReceiver : EventHook +{ + override fun process(entity: Entity, event: ResourceProducedEvent) { + if(entity !is Player) return + + when(getAttribute(entity, "tutorial:stage", 0)) + { + //Gather some logs + 7 -> if(event.itemId == Items.LOGS_1511) + { + setAttribute(entity, "tutorial:stage", 8) + TutorialStage.load(entity, 8) + } + + //Catch some raw shrimp + 13 -> if(event.itemId == Items.RAW_SHRIMPS_317) + { + setAttribute(entity, "tutorial:stage", 14) + TutorialStage.load(entity, 14) + } + + //Make some bread dough + 19 -> if(event.itemId == Items.BREAD_DOUGH_2307) { + setAttribute(entity, "tutorial:stage", 20) + TutorialStage.load(entity, 20) + } + + //Mine some tin ore + 36 -> if(event.itemId == Items.TIN_ORE_438){ + setAttribute(entity, "tutorial:stage", 37) + TutorialStage.load(entity, 37) + } + + //Mine some copper ore + 37 -> if(event.itemId == Items.COPPER_ORE_436){ + setAttribute(entity, "tutorial:stage", 38) + TutorialStage.load(entity, 38) + } + + //Make a bronze bar + 38 -> if(event.itemId == Items.BRONZE_BAR_2349){ + setAttribute(entity, "tutorial:stage", 40) + TutorialStage.load(entity, 40) + } + + //Make a bronze dagger + 42 -> if(event.itemId == Items.BRONZE_DAGGER_1205){ + setAttribute(entity, "tutorial:stage", 43) + TutorialStage.load(entity, 43) + } + } + } +} + +object TutorialFireReceiver : EventHook +{ + override fun process(entity: Entity, event: LitFireEvent) { + if(entity !is Player) return + + when(getAttribute(entity, "tutorial:stage", 0)) + { + 9 -> { + setAttribute(entity, "tutorial:stage", 10) + TutorialStage.load(entity, 10) + } + } + } +} + +object TutorialUseWithReceiver : EventHook +{ + override fun process(entity: Entity, event: UseWithEvent) { + if(entity !is Player) return + + + when(getAttribute(entity, "tutorial:stage", 0)) + { + //Start lighting a fire + 8 -> if(event.used == Items.TINDERBOX_590 && event.with == Items.LOGS_1511) { + setAttribute(entity, "tutorial:stage", 9) + TutorialStage.load(entity, 9) + } + + //Use bar on anvil + 41 -> if(event.used == Items.BRONZE_BAR_2349 && event.with == 2783) { + setAttribute(entity, "tutorial:stage", 42) + TutorialStage.load(entity, 42) + } + } + } +} + +object TutorialKillReceiver : EventHook +{ + override fun process(entity: Entity, event: NPCKillEvent) { + if(entity !is Player) return + + when(getAttribute(entity, "tutorial:stage", 0)) + { + 52 -> if(event.npc.id == NPCs.GIANT_RAT_86){ + setAttribute(entity, "tutorial:stage", 53) + TutorialStage.load(entity, 53) + } + + 54 -> if(event.npc.id == NPCs.GIANT_RAT_86){ + setAttribute(entity, "tutorial:stage", 55) + TutorialStage.load(entity, 55) + } + + 70 -> if(event.npc.id == NPCs.CHICKEN_41){ + setAttribute(entity, "tutorial:stage", 71) + TutorialStage.load(entity, 71) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt new file mode 100644 index 000000000..5a143acfb --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialListeners.kt @@ -0,0 +1,259 @@ +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 + * @author Ceikry + */ +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 + val CHURCH_EXIT = 3026 + 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 -> + if(getAttribute(player, "tutorial:stage", 0) != 3) + return@on true + + + setAttribute(player, "tutorial:stage", 4) + TutorialStage.load(player, 4) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door as Scenery, Location.create(3098, 3107, 0)) + return@on true + } + + on(FIRST_GATE, IntType.SCENERY, "open"){ player, gate -> + if(getAttribute(player, "tutorial:stage", 0) != 16) + return@on true + + setAttribute(player, "tutorial:stage", 17) + TutorialStage.load(player, 17) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, gate as Scenery) + return@on true + } + + on(COOKS_DOOR, IntType.SCENERY, "open"){ player, door -> + if(getAttribute(player, "tutorial:stage", 0) != 17) + return@on true + + setAttribute(player, "tutorial:stage", 18) + TutorialStage.load(player, 18) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door as Scenery) + 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 + + setAttribute(player, "tutorial:stage", 23) + TutorialStage.load(player, 23) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door as Scenery) + return@on true + } + + on(QUEST_ENTER, IntType.SCENERY, "open") { player, door -> + if(getAttribute(player, "tutorial:stage", 0) != 26) + return@on true + + setAttribute(player, "tutorial:stage", 27) + TutorialStage.load(player, 27) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door as Scenery) + return@on true + } + + on(QUEST_LADDER, IntType.SCENERY, "climb-down") { player, ladder -> + if(getAttribute(player, "tutorial:stage", 0) < 29) + return@on true + + if (getAttribute(player, "tutorial:stage", 0) == 29) { + setAttribute(player, "tutorial:stage", 30) + TutorialStage.load(player, 30) + } + core.game.global.action.ClimbActionHandler.climbLadder(player, ladder.asScenery(), "climb-down") + } + + on(QUEST_EXIT_LADDER, IntType.SCENERY, "climb-up") { player, ladder -> + core.game.global.action.ClimbActionHandler.climbLadder(player, ladder.asScenery(), "climb-up") + + submitWorldPulse(object : Pulse(2) { + override fun pulse(): Boolean { + val questTutor = Repository.findNPC(NPCs.QUEST_GUIDE_949) ?: return true + sendChat(questTutor, "What are you doing, ${player.username}? Get back down the ladder.") + return true + } + }) + + return@on true + } + + 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 + } + + 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) { + openDialogue(player, RatPenDialogue(), NPC(NPCs.COMBAT_INSTRUCTOR_944)) + return@on true + } + if (stage == 50) { + setAttribute(player, "tutorial:stage", 51) + TutorialStage.load(player, 51) + } + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, gate as Scenery) + return@on true + } + + on(COMBAT_EXIT, IntType.SCENERY, "climb-up") { player, ladder -> + val stage = getAttribute(player, "tutorial:stage", 0) + if (stage < 55) { + return@on true + } + if (stage == 55) { + setAttribute(player, "tutorial:stage", 56) + TutorialStage.load(player, 56) + } + core.game.global.action.ClimbActionHandler.climbLadder(player, ladder.asScenery(), "climb-up") + } + + on(BANK_EXIT, IntType.SCENERY, "open") { player, door -> + if(getAttribute(player, "tutorial:stage", 0) != 57) + return@on true + + setAttribute(player, "tutorial:stage", 58) + TutorialStage.load(player, 58) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door as Scenery) + } + + on(FINANCE_EXIT, IntType.SCENERY, "open") { player, door -> + if(getAttribute(player, "tutorial:stage", 0) != 59) + return@on true + + setAttribute(player, "tutorial:stage", 60) + TutorialStage.load(player, 60) + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door as Scenery) + } + + on(CHURCH_EXIT, IntType.SCENERY, "open") { player, door -> + if(getAttribute(player, "tutorial:stage", 0) != 66) + return@on true + + setAttribute(player, "tutorial:stage", 67) + TutorialStage.load(player, 67) + 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) } + } + } +} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/TutorialLoginCheck.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialLoginCheck.kt new file mode 100644 index 000000000..a123a793a --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialLoginCheck.kt @@ -0,0 +1,28 @@ +package content.region.misc.tutisland.handlers + +import core.api.* +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.GameWorld + +/** + * Login listener that checks for tutorial completion and loads the current tutorial stage + * @author Ceikry + */ +class TutorialLoginCheck : LoginListener { + override fun login(player: Player) { + if(!getAttribute(player, "tutorial:complete", false)) { + //Disable need to do tutorial for anyone who has already started their account (gained levels or using it as a mule) + if(getAttribute(player, "tutorial:stage", 0) == 0 && (player.skills.totalLevel > 33 || player.bank.itemCount() > 0 || player.inventory.itemCount() > 0)){ + setAttribute(player, "/save:tutorial:complete", true) + return + } + GameWorld.Pulser.submit(object : Pulse(2) { + override fun pulse(): Boolean { + TutorialStage.load(player, getAttribute(player, "tutorial:stage", 0), true) + return true + } + }) + } + } +} \ 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 new file mode 100644 index 000000000..50eeddb00 --- /dev/null +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt @@ -0,0 +1,581 @@ +package content.region.misc.tutisland.handlers + +import core.api.* +import core.game.component.Component +import content.region.misc.tutisland.handlers.iface.CharacterDesign +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.HintIconManager +import core.game.system.task.Pulse +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.repository.Repository +import org.rs09.consts.NPCs + +/** + * Loads stage-relevant tutorial data + * @author Ceikry + * @author Player Name + */ +object TutorialStage { + /** + * Performs stage actions for the player + * @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) { + 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) { + 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, 3756, 12) + removeHintIcon(player) + sendStageDialog(player, stage) + } + 2 -> { + setVarbit(player, 3756, 0) + hideTabs(player, login) + 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) + sendStageDialog(player, stage) + } + 4 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.SURVIVAL_EXPERT_943)!!) + sendStageDialog(player, stage) + } + 5 -> { + hideTabs(player, login) + player.interfaceManager.openTab(Component(Components.INVENTORY_149)) + setVarbit(player, 3756, 4) + removeHintIcon(player) + sendStageDialog(player, stage) + } + 6 -> { + hideTabs(player, login) + setVarbit(player, 3756, 4) + sendStageDialog(player, stage) + } + 7 -> { + hideTabs(player, login) + removeHintIcon(player) + sendStageDialog(player, stage) + } + 8 -> { + hideTabs(player, login) + sendStageDialog(player, stage) + } + 9 -> { + hideTabs(player, login) + sendStageDialog(player, stage) + } + 10 -> { + hideTabs(player, login) + player.interfaceManager.openTab(Component(Components.STATS_320)) + setVarbit(player, 3756, 2) + sendStageDialog(player, stage) + } + 11 -> { + hideTabs(player, login) + setVarbit(player, 3756, 2) + registerHintIcon(player, Repository.findNPC(NPCs.SURVIVAL_EXPERT_943)!!) + sendStageDialog(player, stage) + } + 12 -> { + hideTabs(player, login) + setVarp(player, 406, 2) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.FISHING_SPOT_952)!!) + sendStageDialog(player, stage) + } + 13 -> { + hideTabs(player, login) + removeHintIcon(player) + sendStageDialog(player, stage) + } + 14 -> { + hideTabs(player, login) + sendStageDialog(player, stage) + } + 15 -> { + hideTabs(player, login) + sendStageDialog(player, stage) + } + 16 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3089, 3091, 0), 75) + sendStageDialog(player, stage) + } + 17 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3079, 3084, 0), 125) + sendStageDialog(player, stage) + } + 18 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.MASTER_CHEF_942)!!) + sendStageDialog(player, stage) + } + 19 -> { + hideTabs(player, login) + removeHintIcon(player) + sendStageDialog(player, stage) + } + 20 -> { + hideTabs(player, login) + registerHintIcon(player, Location.create(3076, 3081, 0), 75) + sendStageDialog(player, stage) + } + 21 -> { + hideTabs(player, login) + removeHintIcon(player) + player.interfaceManager.openTab(Component(Components.MUSIC_V3_187)) + setVarbit(player, 3756, 14) + sendStageDialog(player, stage) + } + 22 -> { + hideTabs(player, login) + setVarbit(player, 3756, 0) + registerHintIcon(player, Location.create(3073, 3090, 0), 125) + sendStageDialog(player, stage) + } + 23 -> { + hideTabs(player, login) + setVarbit(player, 3756, 13) + removeHintIcon(player) + player.interfaceManager.openTab(Component(Components.EMOTES_464)) + stopWalk(player) + player.locks.lockMovement(100000) + sendStageDialog(player, stage) + } + 24 -> { + hideTabs(player, login) + setVarbit(player, 3756, 0) + player.locks.lockMovement(100000) + sendStageDialog(player, stage) + } + 25 -> { + hideTabs(player, login) + sendStageDialog(player, stage) + } + 26 -> { + hideTabs(player, login) + registerHintIcon(player, Repository.findNPC(NPCs.QUEST_GUIDE_949)!!) + player.locks.unlockMovement() + sendStageDialog(player, stage) + } + 27 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.QUEST_GUIDE_949)!!) + sendStageDialog(player, stage) + } + 28 -> { + hideTabs(player, login) + setVarbit(player, 3756, 0) + sendStageDialog(player, stage) + } + 29 -> { + hideTabs(player, login) + removeHintIcon(player) + setVarbit(player, 3756, 0) + registerHintIcon(player, Location.create(3088, 3119, 0), 15) + sendStageDialog(player, stage) + } + 30 -> { + hideTabs(player, login) + removeHintIcon(player) + 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) + sendStageDialog(player, stage) + } + 32 -> { + hideTabs(player, login) + removeHintIcon(player) + sendStageDialog(player, stage) + Pulser.submit(object : Pulse(3) { + override fun pulse(): Boolean { + setAttribute(player, "tutorial:stage", 33) + load(player, 33) + return true + } + }) + } + 33 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3086, 9501, 0), 50) + sendStageDialog(player, stage) + } + 34 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.MINING_INSTRUCTOR_948)!!) + sendStageDialog(player, stage) + } + 35 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3076, 9504), 50) + sendStageDialog(player, stage) + } + 36 -> { + hideTabs(player, login) + removeHintIcon(player) + sendStageDialog(player, stage) + } + 37 -> { + hideTabs(player, login) + registerHintIcon(player, Location.create(3086, 9501), 50) + sendStageDialog(player, stage) + } + 38 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3079, 9496), 75) + sendStageDialog(player, stage) + } + 39 -> { + sendStageDialog(player, stage) + } + 40 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.MINING_INSTRUCTOR_948)!!) + sendStageDialog(player, stage) + } + 41 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3083, 9499), 50) + sendStageDialog(player, stage) + } + 42 -> { + hideTabs(player, login) + removeHintIcon(player) + sendStageDialog(player, stage) + } + 43 -> { + hideTabs(player, login) + registerHintIcon(player, Location.create(3095, 9502), 75) + sendStageDialog(player, stage) + } + 44 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) + } + 45 -> { + hideTabs(player, login) + removeHintIcon(player) + 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, 3756, 0) + sendStageDialog(player, stage) + } + 47 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) + } + 48 -> { + hideTabs(player, login) + removeHintIcon(player) + sendStageDialog(player, stage) + } + 49 -> { + hideTabs(player, login) + setVarbit(player, 3756, 1) + var wepInter = player.getExtension(WeaponInterface::class.java) + if (wepInter == null) { + wepInter = WeaponInterface(player) + player.addExtension(WeaponInterface::class.java, wepInter) + } + player.interfaceManager.openTab(wepInter) + sendStageDialog(player, stage) + } + 50 -> { + hideTabs(player, login) + setVarbit(player, 3756, 0) + registerHintIcon(player, Location.create(3111,9518,0), 75) + sendStageDialog(player, stage) + } + 51 -> { + hideTabs(player, login) + removeHintIcon(player) + //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) + //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) + registerHintIcon(player, Repository.findNPC(NPCs.COMBAT_INSTRUCTOR_944)!!) + sendStageDialog(player, stage) + } + 54 -> { + hideTabs(player, login) + sendStageDialog(player, stage) + } + 55 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3111,9526), 50) + sendStageDialog(player, stage) + } + 56 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3122,3124), 50) + sendStageDialog(player, stage) + } + 57 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Location.create(3125, 3124), 75) + sendStageDialog(player, stage) + } + 58 -> { + hideTabs(player, login) + removeHintIcon(player) + 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) + sendStageDialog(player, stage) + } + 60 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.BROTHER_BRACE_954)!!) + sendStageDialog(player, stage) + } + 61 -> { + hideTabs(player, login) + removeHintIcon(player) + player.interfaceManager.openTab(Component(Components.PRAYER_271)) + setVarbit(player, 3756, 6) + sendStageDialog(player, stage) + } + 62 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.BROTHER_BRACE_954)!!) + sendStageDialog(player, stage) + } + 63 -> { + hideTabs(player, login) + removeHintIcon(player) + player.interfaceManager.openTab(Component(Components.FRIENDS2_550)) + setVarbit(player, 3756, 9) + sendStageDialog(player, stage) + } + 64 -> { + hideTabs(player, login) + setVarbit(player, 3756, 10) + player.interfaceManager.openTab(Component(Components.IGNORE2_551)) + player.interfaceManager.openTab(Component(Components.CLANJOIN_589)) + sendStageDialog(player, stage) + } + 65 -> { + hideTabs(player, login) + 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) + sendStageDialog(player, stage) + } + 67 -> { + hideTabs(player, login) + removeHintIcon(player) + registerHintIcon(player, Repository.findNPC(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) + } + 68 -> { + hideTabs(player, login) + removeHintIcon(player) + player.interfaceManager.openTab(Component(player.spellBookManager.spellBook)) + setVarbit(player, 3756, 7) + sendStageDialog(player, stage) + } + 69 -> { + hideTabs(player, login) + removeHintIcon(player) + setVarbit(player, 3756, 0) + registerHintIcon(player, Repository.findNPC(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) + } + 70 -> { + hideTabs(player, login) + 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(NPCs.MAGIC_INSTRUCTOR_946)!!) + sendStageDialog(player, stage) + } + } + } + + @JvmStatic + fun hideTabs(player: Player, login: Boolean) + { + val stage = getAttribute(player, "tutorial:stage", 0) + if(login && player.interfaceManager.tabs.isNotEmpty()) + player.interfaceManager.removeTabs(*(0..13).toIntArray()) + + if(stage > 2) + player.interfaceManager.openTab(Component(Components.OPTIONS_261)) + if(stage > 5) + player.interfaceManager.openTab(Component(Components.INVENTORY_149)) + if(stage > 10) + player.interfaceManager.openTab(Component(Components.STATS_320)) + if(stage > 21) + player.interfaceManager.openTab(Component(Components.MUSIC_V3_187)) + if(stage > 23) + player.interfaceManager.openTab(Component(Components.EMOTES_464)) + if(stage > 28) + player.interfaceManager.openTab(Component(Components.QUESTJOURNAL_V2_274)) + if(stage > 45) + player.interfaceManager.openTab(Component(Components.WORNITEMS_387)) + if(stage > 46){ + var wepInter = player.getExtension(WeaponInterface::class.java) + if (wepInter == null) { + wepInter = WeaponInterface(player) + player.addExtension(WeaponInterface::class.java, wepInter) + } + } + if(stage > 61) + player.interfaceManager.openTab(Component(Components.PRAYER_271)) + if(stage > 63) + player.interfaceManager.openTab(Component(Components.FRIENDS2_550)) + if(stage > 64){ + player.interfaceManager.openTab(Component(Components.IGNORE2_551)) + player.interfaceManager.openTab(Component(Components.CLANJOIN_589)) + } + if(stage > 68) + player.interfaceManager.openTab(Component(player.spellBookManager.spellBook)) + } + + private fun updateProgressBar(player: Player) + { + 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) + setVarp(player, 406, barPercent) + setInterfaceText(player, "$percent% Done", 371, 1) + } + + fun removeHintIcon(player: Player) { + val slot = player.getAttribute("tutorial:hinticon", -1) + if (slot < 0 || slot >= HintIconManager.MAXIMUM_SIZE) { + return + } + player.removeAttribute("tutorial:hinticon") + HintIconManager.removeHintIcon(player, slot) + } + + private fun registerHintIcon(player: Player, node: Node) + { + setAttribute(player, "tutorial:hinticon", HintIconManager.registerHintIcon(player, node)) + } + + private fun registerHintIcon(player: Player, location: Location, height: Int) + { + setAttribute(player, "tutorial:hinticon", HintIconManager.registerHintIcon(player, location, 1, -1, player.hintIconManager.freeSlot(), height, 3)) + } +} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/CharacterDesign.java b/Server/src/main/content/region/misc/tutisland/handlers/iface/CharacterDesign.java similarity index 80% rename from Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/CharacterDesign.java rename to Server/src/main/content/region/misc/tutisland/handlers/iface/CharacterDesign.java index 0220dd4f0..c20c65f72 100644 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/CharacterDesign.java +++ b/Server/src/main/content/region/misc/tutisland/handlers/iface/CharacterDesign.java @@ -1,17 +1,14 @@ -package core.game.content.quest.tutorials.tutorialisland; +package content.region.misc.tutisland.handlers.iface; import core.game.component.CloseEvent; import core.game.component.Component; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.appearance.Gender; -import core.game.node.entity.player.link.music.MusicEntry; import core.game.node.item.Item; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Represents the class used to handle the character design. * @author Emperor @@ -152,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. */ @@ -164,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: @@ -231,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); } @@ -304,43 +326,6 @@ public final class CharacterDesign { if (close) { player.setAttribute("char-design:accepted", true); player.getInterfaceManager().close(); - //Removing Tutorial Island properties on the account (?) and sending the Player to Lumbridge - player.getMusicPlayer().play(MusicEntry.forId(62)); - player.removeAttribute("tut-island"); - player.getConfigManager().set(1021, 0); - TutorialSession.getExtension(player).setStage(72); - player.getInterfaceManager().closeOverlay(); - - //Clears and Resets the Player's account and focuses the default interface to their Inventory -// player.getInventory().clear(); -// player.getEquipment().clear(); -// player.getBank().clear(); - player.getInterfaceManager().restoreTabs(); //This still hides the Attack (double swords) in the player menu until Player wields a weapon. - player.getInterfaceManager().setViewedTab(3); -// player.getInventory().add(STARTER_PACK); -// player.getBank().add(STARTER_BANK); - - player.getPulseManager().run(new Pulse(1){ - @Override - public boolean pulse() { - player.unlock(); - if (player.getIronmanManager().isIronman() && player.getSettings().isAcceptAid()) { - player.getSettings().toggleAcceptAid(); - } - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendInfoUpdate(player); - } - //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. - //player.getDialogueInterpreter().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."); - player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Welcome to " + GameWorld.getSettings().getName() + "!","Hans at the castle in Lumbridge can","enable ironman mode, modify xp rate settings,","toggle random events, and more!","Use the ladder to leave."); - - //Appending the welcome message and some other stuff - player.getPacketDispatch().sendMessage("Welcome to " + GameWorld.getSettings().getName() + "."); - return true; - } - }); } player.getAppearance().setGender(player.getAttribute("male", player.getAppearance().isMale()) ? Gender.MALE : Gender.FEMALE); for (int i = 0; i < player.getAppearance().getAppearanceCache().length; i++) { @@ -363,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 new file mode 100644 index 000000000..ce6c7f48b --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/dialogue/BlaecDialogue.kt @@ -0,0 +1,42 @@ +package content.region.misc.zanaris.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 BlaecDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + when ((1..3).random()) { + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BlaecDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BLAEC_3115) + } +} diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/CoOrdinatorDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/CoOrdinatorDialogue.kt new file mode 100644 index 000000000..2d190847d --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/dialogue/CoOrdinatorDialogue.kt @@ -0,0 +1,47 @@ +package content.region.misc.zanaris.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 CoOrdinatorDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY, "Hello, what are you doing?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + when ((1..5).random()) { + 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 } + } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return CoOrdinatorDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CO_ORDINATOR_3302) + } +} diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyChefDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyChefDialogue.kt new file mode 100644 index 000000000..8db8031b0 --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyChefDialogue.kt @@ -0,0 +1,38 @@ +package content.region.misc.zanaris.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 FairyChefDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FairyChefDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FAIRY_CHEF_3322, NPCs.FAIRY_CHEF_3323) + } +} diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyDialogue.kt new file mode 100644 index 000000000..ff10ef193 --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyDialogue.kt @@ -0,0 +1,53 @@ +package content.region.misc.zanaris.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 FairyDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + when ((1..7).random()) { + 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(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(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?): DialoguePlugin { + return FairyDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FAIRY_4443, NPCs.FAIRY_4444, NPCs.FAIRY_4445, NPCs.FAIRY_4446, NPCs.FAIRY_567, NPCs.FAIRY_57) + } +} diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyFixit.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyFixit.kt new file mode 100644 index 000000000..f0e851ce1 --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyFixit.kt @@ -0,0 +1,72 @@ +package content.region.misc.zanaris.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 FairyFixit(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + //tempvar + //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(FacialExpression.OLD_CALM_TALK1, "Pssst! Human! I've got something for you.").also { stage = 20 } + } else { + npc(FacialExpression.OLD_DISTRESSED, "What is it, human? Busy busy busy!").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("Why are you carrying that toolbox?", "I'm okay, thanks.").also { stage++ } + 1 -> when (buttonId){ + 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(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(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(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) + } + + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FairyFixit(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FAIRY_FIXIT_4455) + } +} 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 new file mode 100644 index 000000000..ea0d18c04 --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyShopAssistantDialogue.kt @@ -0,0 +1,44 @@ +package content.region.misc.zanaris.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 FairyShopAssistantDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_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?", "No thanks.").also { stage++ } + + 1 -> when (buttonId) { + 1 -> end().also { npc.openShop(player) } + 2 -> player(FacialExpression.NEUTRAL, "No thanks.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FairyShopAssistantDialogue(player) + } + + override fun getIds(): IntArray { + 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 new file mode 100644 index 000000000..72a003a7b --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/handlers/FairyRingPlugin.kt @@ -0,0 +1,68 @@ +package content.region.misc.zanaris.handlers + +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 content.data.Quests + +/** + * Handles interactions with fairy rings + * @author Ceikry + */ + +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 { + + override fun defineListeners() { + + on(RINGS, IntType.SCENERY, "use"){ player, _ -> + if (!fairyMagic(player)) return@on true + val mainRingLocation = Location.create(2412, 4434, 0) + player.teleporter.send(mainRingLocation, TeleportType.FAIRY_RING) + return@on true + } + on(MAIN_RING, IntType.SCENERY, "use"){ player, _ -> + if (!fairyMagic(player)) return@on true + openFairyRing(player) + return@on true + } + on(ENTRY_RING, IntType.SCENERY, "use") { player, _ -> + val lumbridgeSwampShed = Location.create(3203, 3168, 0) + player.teleporter.send(lumbridgeSwampShed, TeleportType.FAIRY_RING) + return@on true + } + on(MARKETPLACE_RING, IntType.SCENERY, "use"){ player, _ -> + val alKharidBank = Location.create(3260, 3156, 0) + player.teleporter.send(alKharidBank, TeleportType.FAIRY_RING) + return@on true + } + + } + + private fun fairyMagic(player: Player) : Boolean { + if (!hasRequirement(player, 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 (!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 openFairyRing(player: Player) { + openInterface(player, FairyRingInterface.RINGS_IFACE) + } +} diff --git a/Server/src/main/java/core/game/interaction/city/ZanarisPlugin.java b/Server/src/main/content/region/misc/zanaris/handlers/ZanarisPlugin.java similarity index 93% rename from Server/src/main/java/core/game/interaction/city/ZanarisPlugin.java rename to Server/src/main/content/region/misc/zanaris/handlers/ZanarisPlugin.java index c9c40f062..4179ab857 100644 --- a/Server/src/main/java/core/game/interaction/city/ZanarisPlugin.java +++ b/Server/src/main/content/region/misc/zanaris/handlers/ZanarisPlugin.java @@ -1,11 +1,11 @@ -package core.game.interaction.city; +package content.region.misc.zanaris.handlers; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.action.DoorActionHandler; -import core.game.node.entity.skill.crafting.gem.Gems; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.global.action.DoorActionHandler; +import content.global.skill.crafting.gem.Gems; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -13,7 +13,7 @@ import core.game.node.entity.player.link.TeleportManager.TeleportType; import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Handles the zanaris city plugin. @@ -24,7 +24,7 @@ public final class ZanarisPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new MagicDoorDialogue()); + ClassScanner.definePlugin(new MagicDoorDialogue()); SceneryDefinition.forId(12094).getHandlers().put("option:use", this); SceneryDefinition.forId(12045).getHandlers().put("option:open", this); SceneryDefinition.forId(12047).getHandlers().put("option:open", this); diff --git a/Server/src/main/java/core/game/content/dialogue/BarbarianDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/dialogue/BarbarianDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/BarbarianDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/dialogue/BarbarianDialogue.java index 9bd76f415..a8573dfd6 100644 --- a/Server/src/main/java/core/game/content/dialogue/BarbarianDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/dialogue/BarbarianDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/GunthorBraveDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/dialogue/GunthorBraveDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/GunthorBraveDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/dialogue/GunthorBraveDialogue.java index 66c7dbf6b..79e14b967 100644 --- a/Server/src/main/java/core/game/content/dialogue/GunthorBraveDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/dialogue/GunthorBraveDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.misthalin.barbvillage.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/dialogue/LitaraDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/dialogue/LitaraDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/LitaraDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/dialogue/LitaraDialogue.java index a23319235..692272475 100644 --- a/Server/src/main/java/core/game/content/dialogue/LitaraDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/dialogue/LitaraDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; 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/java/core/game/content/dialogue/SigurdDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/dialogue/SigurdDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/SigurdDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/dialogue/SigurdDialogue.java index f64b80363..29677bb1b 100644 --- a/Server/src/main/java/core/game/content/dialogue/SigurdDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/dialogue/SigurdDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -113,7 +115,7 @@ public class SigurdDialogue extends DialoguePlugin { break; case 24: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Then take your hatchet to it and shape it how you", "like!"); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Then take your axe to it and shape it how you", "like!"); stage = 26; break; case 26: diff --git a/Server/src/main/java/core/game/node/entity/npc/other/BarbarianNPC.java b/Server/src/main/content/region/misthalin/barbvillage/handlers/BarbarianNPC.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/other/BarbarianNPC.java rename to Server/src/main/content/region/misthalin/barbvillage/handlers/BarbarianNPC.java index a2d17429e..def8e11ac 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/BarbarianNPC.java +++ b/Server/src/main/content/region/misthalin/barbvillage/handlers/BarbarianNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.misthalin.barbvillage.handlers; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; diff --git a/Server/src/main/java/core/game/content/activity/stronghold/BoxOfHealthDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/BoxOfHealthDialogue.java similarity index 82% rename from Server/src/main/java/core/game/content/activity/stronghold/BoxOfHealthDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/stronghold/BoxOfHealthDialogue.java index 73f33c179..3647f087d 100644 --- a/Server/src/main/java/core/game/content/activity/stronghold/BoxOfHealthDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/BoxOfHealthDialogue.java @@ -1,9 +1,12 @@ -package core.game.content.activity.stronghold; +package content.region.misthalin.barbvillage.stronghold; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/node/entity/npc/other/CatableponNPC.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/CatableponNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/other/CatableponNPC.java rename to Server/src/main/content/region/misthalin/barbvillage/stronghold/CatableponNPC.java index 284652414..776f5d12b 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/CatableponNPC.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/CatableponNPC.java @@ -1,6 +1,6 @@ -package core.game.node.entity.npc.other; +package content.region.misthalin.barbvillage.stronghold; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.npc.AbstractNPC; @@ -8,8 +8,8 @@ import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MultiSwingHandler; /** * Represents the catablepon npc type. diff --git a/Server/src/main/java/core/game/content/activity/stronghold/CradleOfLifeDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/CradleOfLifeDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/activity/stronghold/CradleOfLifeDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/stronghold/CradleOfLifeDialogue.java index e634eb8ee..9ff2f0255 100644 --- a/Server/src/main/java/core/game/content/activity/stronghold/CradleOfLifeDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/CradleOfLifeDialogue.java @@ -1,8 +1,8 @@ -package core.game.content.activity.stronghold; +package content.region.misthalin.barbvillage.stronghold; import core.game.container.Container; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.emote.Emotes; import core.game.node.item.Item; @@ -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/java/core/game/content/activity/stronghold/GiftOfPeaceDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java similarity index 82% rename from Server/src/main/java/core/game/content/activity/stronghold/GiftOfPeaceDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java index 6a4eadb6c..0608d0fea 100644 --- a/Server/src/main/java/core/game/content/activity/stronghold/GiftOfPeaceDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java @@ -1,9 +1,12 @@ -package core.game.content.activity.stronghold; +package content.region.misthalin.barbvillage.stronghold; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/activity/stronghold/GrainOfPlentyDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java similarity index 80% rename from Server/src/main/java/core/game/content/activity/stronghold/GrainOfPlentyDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java index 94746edd5..1135cefd5 100644 --- a/Server/src/main/java/core/game/content/activity/stronghold/GrainOfPlentyDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java @@ -1,9 +1,13 @@ -package core.game.content.activity.stronghold; +package content.region.misthalin.barbvillage.stronghold; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/dialogue/SkullSceptreDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/SkullSceptreDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/SkullSceptreDialogue.java rename to Server/src/main/content/region/misthalin/barbvillage/stronghold/SkullSceptreDialogue.java index 9661657ee..a2926a5d1 100644 --- a/Server/src/main/java/core/game/content/dialogue/SkullSceptreDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/SkullSceptreDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.misthalin.barbvillage.stronghold; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/activity/stronghold/StrongHoldSecurityPlugin.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/StrongHoldSecurityPlugin.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/stronghold/StrongHoldSecurityPlugin.java rename to Server/src/main/content/region/misthalin/barbvillage/stronghold/StrongHoldSecurityPlugin.java index 50227a414..c6de2b356 100644 --- a/Server/src/main/java/core/game/content/activity/stronghold/StrongHoldSecurityPlugin.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/StrongHoldSecurityPlugin.java @@ -1,32 +1,38 @@ -package core.game.content.activity.stronghold; +package content.region.misthalin.barbvillage.stronghold; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; import core.game.component.ComponentPlugin; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialogueInterpreter; +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.interaction.Option; 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.diary.DiaryType; +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; -import rs09.game.world.GameWorld; +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.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 rs09.plugin.PluginManager; +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. @@ -50,13 +56,13 @@ public final class StrongHoldSecurityPlugin extends MapZone implements Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); - PluginManager.definePlugin(new ExplorerDialogue()); - PluginManager.definePlugin(new StrongholdDialogue()); - PluginManager.definePlugin(new GrainOfPlentyDialogue()); - PluginManager.definePlugin(new GiftOfPeaceDialogue()); - PluginManager.definePlugin(new CradleOfLifeDialogue()); - PluginManager.definePlugin(new BoxOfHealthDialogue()); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new ExplorerDialogue()); + ClassScanner.definePlugin(new StrongholdDialogue()); + ClassScanner.definePlugin(new GrainOfPlentyDialogue()); + ClassScanner.definePlugin(new GiftOfPeaceDialogue()); + ClassScanner.definePlugin(new CradleOfLifeDialogue()); + ClassScanner.definePlugin(new BoxOfHealthDialogue()); + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.forId(16154).getHandlers().put("option:climb-down", this); @@ -121,25 +127,40 @@ 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/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.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/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/java/core/game/content/dialogue/HoodedMonkDialogue.java b/Server/src/main/content/region/misthalin/dialogue/HoodedMonkDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/HoodedMonkDialogue.java rename to Server/src/main/content/region/misthalin/dialogue/HoodedMonkDialogue.java index 889036451..b1dc3b675 100644 --- a/Server/src/main/java/core/game/content/dialogue/HoodedMonkDialogue.java +++ b/Server/src/main/content/region/misthalin/dialogue/HoodedMonkDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.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; diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/ArchaeologistcalExpertUsedOnDialogueFile.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/ArchaeologistcalExpertUsedOnDialogueFile.kt new file mode 100644 index 000000000..c16b62b2d --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ArchaeologistcalExpertUsedOnDialogueFile.kt @@ -0,0 +1,126 @@ +package content.region.misthalin.digsite.dialogue + +import content.region.misthalin.digsite.quest.thedigsite.ArchaeologicalExpertListener +import core.api.addItemOrDrop +import core.api.sendDialogue +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 + +/** + * @author qmqz + */ + +class ArchaeologistcalExpertUsedOnDialogueFile(val it: Int) : DialogueFile() { + + var i = ArchaeologicalExpertListener() + var n = NPC(i.archy) + var itemUsed = it + + override fun handle(interfaceId: Int, buttonId: Int) { + + npc = n + + when (stage) { + + 0 -> when (itemUsed) { + i.staff -> player(FacialExpression.ASKING, "What do you think of this?").also { stage = 100 } + + i.unidentifiedLiquid -> { + replaceAll(i.unidentifiedLiquid, i.nitroglycerin) + 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!").also { stage = 99 } + } + + i.nitroglycerin -> player(FacialExpression.ASKING, "Can you tell me any more about this?").also { stage = 110} + i.ammoniumNitrate -> player(FacialExpression.ASKING, "Have a look at this.").also { stage = 120 } + i.nuggets -> player(FacialExpression.ASKING, "I have these gold nuggets...").also { stage = 130 } + i.needle -> player(FacialExpression.NEUTRAL, "I found a needle.").also { stage = 140 } + i.rottenApple -> player(FacialExpression.SAD, "I found these...").also { stage = 150 } + i.brokenGlass -> player(FacialExpression.NEUTRAL, "Have a look at this glass.").also { stage = 160 } + i.brokenArrow -> player(FacialExpression.FRIENDLY, "Have a look at this arrow.").also { stage = 170 } + i.panningTray -> { + if (player!!.inventory.contains(Items.PANNING_TRAY_677,1)) { + npc(FacialExpression.NEUTRAL, "I have no need for panning trays!").also { stage = 99 } + } + if (player!!.inventory.contains(Items.PANNING_TRAY_678,1) || player!!.inventory.contains(Items.PANNING_TRAY_679,1)) { + npc(FacialExpression.NEUTRAL, "Have you searched this tray yet?").also { stage = 180 } + } + } + i.bones -> player(FacialExpression.FRIENDLY, "Have a look at these bones.").also { stage = 190 } + i.buttons -> player(FacialExpression.FRIENDLY, "I found these buttons.").also { stage = 200 } + i.crackedSample -> player(FacialExpression.WORRIED, "I found this rock...").also { stage = 210 } + i.oldTooth -> player(FacialExpression.FRIENDLY, "Hey look at this.").also { stage = 220 } + i.rustySword -> player(FacialExpression.FRIENDLY, "I found an old sword.").also { stage = 230 } + i.brokenStaff -> player(FacialExpression.ASKING, "Have a look at this staff.").also { stage = 240 } + i.brokenArmour -> player(FacialExpression.ASKING, "I found some armour.").also { stage = 250 } + i.damagedArmour -> player(FacialExpression.THINKING, "I found some old armour.").also { stage = 260 } + i.ceramicRemains -> player(FacialExpression.FRIENDLY, "I found some pottery pieces.").also { stage = 270 } + i.beltBuckle -> player(FacialExpression.ASKING, "Have a look at this unusual item...").also { stage = 280 } + i.animalSkull -> player(FacialExpression.FRIENDLY, "Have a look at this.").also { stage = 290 } + i.specialCup -> player(FacialExpression.FRIENDLY, "Have a look at this.").also { stage = 300 } + i.teddy -> player(FacialExpression.FRIENDLY, "Have a look at this.").also { stage = 310 } + i.stoneTablet -> player(FacialExpression.FRIENDLY, "Have a look at this.").also { stage = 320 } + else -> npcl(FacialExpression.FRIENDLY, "I don't think that has any archaeological significance").also { stage = 99 } + } + + + 100 -> npcl(FacialExpression.AMAZED, "That staff is incredible! It's the same symbol that was on that talisman you found here. Does this mean you've found out more about Zaros?").also { stage++ } + + 101 -> playerl( + FacialExpression.FRIENDLY, "I found out that he was banished, and that the people's hero was trapped in a pyramid and...").also { stage++ } + 102 -> npcl(FacialExpression.THINKING, "So you're the one who found out about that. I've heard the story from my friends in the museum. Well done on being able to wield such an impressive symbol.").also { stage++ } + 103 -> npc(FacialExpression.FRIENDLY, "Anyway....").also { stage = 99 } // 500 will be digsite dialog + + 110 -> npcl(FacialExpression.WORRIED, "Nitroglycerin! This is a dangerous substance. This is normally mixed with other chemicals to produce a potent compound.").also { stage++ } + 111 -> npcl(FacialExpression.WORRIED, "Be sure not to drop it! That stuff is highly volatile...").also { stage = 99 } + + 120 -> 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...").also { stage = 99 } + 130 -> { + if (player!!.inventory.getAmount(i.nuggets) >= 3) { + player!!.inventory.remove(Item(i.nuggets, 3)) + addItemOrDrop(player!!, Items.GOLD_ORE_444, 1) + npcl(FacialExpression.FRIENDLY, "Good – that's three; I can exchange them for normal gold now. You can get this refined and make a profit!").also { stage = 131 } + } else if (player!!.inventory.getAmount(i.nuggets) < 3) { + npcl(FacialExpression.FRIENDLY, "I can't do much with these nuggets yet. Come back when you have 3 and I will exchange them for you.").also { stage = 99 } + } + } + 131 -> player(FacialExpression.FRIENDLY, "Excellent!").also { stage = 99 } + 140 -> npcl(FacialExpression.LAUGH, "Hmm, yes; I wondered why this race were so well dressed! It looks like they had mastery of needlework.").also { stage = 99 } + 150 -> npc(FacialExpression.DISGUSTED, "Ew! Throw them away this instant!").also { stage = 99 } + 160 -> npcl(FacialExpression.NEUTRAL, "Hey you should be careful of that. It might cut your fingers, throw it away!").also { stage = 99 } + 170 -> npcl(FacialExpression.AMAZED, "No doubt this arrow was shot by a strong warrior – it's split in half! It is not a valuable object though.").also { stage = 99 } + 180 -> player(FacialExpression.THINKING, "Not that I remember...").also { stage++ } + 181 -> npcl(FacialExpression.DISGUSTED, "It may contain something; I don't want to get my hands dirty.").also { stage++ } + 182 -> sendDialogue(player!!,"The expert hands the tray back to you.").also { stage = 99 } + 190 -> npcl(FacialExpression.FRIENDLY, "Ah, yes – a fine bone example... no noticeable fractures... and in good condition. These are common cow bones, however; they have no archaeological value.").also { stage = 99 } + 200 -> npcl(FacialExpression.THINKING, "Let's have a look. Ah, I think these are from the nobility, perhaps a royal servant?").also { stage = 99 } + 210 -> npcl(FacialExpression.THINKING, "What a shame it's cracked; this looks like it would have been a good sample.").also { stage = 99 } + 220 -> npcl(FacialExpression.LAUGH, "Oh, an old tooth. It looks like it has come from a mighty being. Pity there are no tooth fairies around here!").also { stage = 99 } + 230 -> npcl(FacialExpression.THINKING, "Oh, it's very rusty isn't it? I'm not sure this sword belongs here, it looks very out of place.").also { stage = 99 } + 240 -> npcl(FacialExpression.HALF_THINKING, "Look at this... Interesting... This appears to belong to a cleric of some kind; certainly not a follower of Saradomin, however.").also { stage++ } + 241 -> npcl(FacialExpression.THINKING, "I wonder if there was another civilization here before the Saradominists?").also { stage = 99 } + 250 -> npcl(FacialExpression.AMAZED, "It looks like the wearer of this fought a mighty battle.").also { stage = 99 } + 260 -> npcl(FacialExpression.THINKING, "How unusual. This armour doesn't seem to match with the other finds. Keep looking.").also { stage = 99 } + 270 -> npcl(FacialExpression.FRIENDLY, "Yes, many parts are discovered. The inhabitants of these parts were great potters.").also { stage++ } + 271 -> player(FacialExpression.ASKING, "You mean they were good at using potions?").also { stage++ } + 272 -> npcl(FacialExpression.LAUGH, "No, no, silly. They were known for their skill with clay.").also { stage = 99 } + 280 -> npcl(FacialExpression.THINKING, "Let me see. This is a belt buckle. Not so unusual - I should imagine it came from a guard.").also { stage = 99 } + 290 -> npcl(FacialExpression.THINKING, "Hmm, an interesting find; an animal skull for sure. Another student found one just like this today.").also { stage = 99 } + 300 -> npcl(FacialExpression.THINKING, "Looks like an award cup for some small find. Perhaps it belongs to one of the students?").also { stage = 99 } + 310 -> npcl(FacialExpression.LAUGH, "Why, it looks like a teddy bear to me. Perhaps someone's lucky mascot!").also { stage = 99 } + 320 -> npcl(FacialExpression.NEUTRAL, "I don't need another tablet! One is enough, thank you.").also { stage = 99 } + + 99 -> end() + } + } + + fun replaceAll(originalItem: Int, newItem: Int) { + for (a in 0..player!!.inventory.getAmount(originalItem)) { + if (player!!.inventory.contains(originalItem, 1)) { + player!!.inventory.replace(Item(newItem), player!!.inventory.getSlot(Item(originalItem))) + } + } + } +} 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 new file mode 100644 index 000000000..d41dc2d87 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ElissaDialogue.kt @@ -0,0 +1,101 @@ +package content.region.misthalin.digsite.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 ElissaDialogue(player: Player? = null) : DialoguePlugin(player){ + + 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(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + //three quest branches + //normal dialogue + 0 -> (options("What do you do here?", "What is this place?")).also { stage++ } + 1 -> { + when(qb) { + 1 -> player(ask, "What do you do here?").also { stage = 10 } + 2 -> player(ask, "What is this place?").also { stage = 20 } + } + } + + 10 -> npcl(fr, "I am helping with the dig. I am an expert on Third Age architecture.").also { stage = 99 } + 20 -> npcl(ann, "In the Third Age, this was a great city, which would have put Varrock to shame!").also { stage++ } + 21 -> options("I don't know, Varrock is pretty impressive.", "What happened to the city?").also { stage++ } + 22 -> when(buttonId) { + 1 -> player(fr, "I don't know, Varrock is pretty impressive.").also { stage = 30 } + 2 -> player(ask, "What happened to the city?").also { stage = 40 } + } + + 30 -> npcl(ann, "Hmph. I don't think it will look this good when it's been buried in the ground for three thousand years!").also { stage = 99 } + 40 -> npc(neu, "No one knows for sure.").also { stage++ } + 41 -> npcl(neu, "But the The Third Age was a time of destruction, when the gods were violently at war. Many great civilizations were destroyed then.").also { stage = 99 } + + //the golem quest dialogue first time talking + 50 -> (options("What do you do here?", "What is this place?", "I found a letter in the desert with your name on.")).also { stage++ } + 51 -> when (buttonId) { + 1 -> player(ask, "What do you do here?").also { stage = 10 } + 2 -> player(ask, "What is this place?").also { stage = 20 } + 3 -> playerl(fr, "I found a letter in the desert with your name on.").also { stage = 60 } + } + 60 -> npc(ama, "Ah, so you've found the ruins of Uzer.").also { stage++ } + 61 -> npcl(ama, "I wrote that letter to my late husband when he was exploring there.").also { stage++ } + 62 -> npcl(ama, "That was a great city as well, but the museum could only fund one excavation and this one was closer to home.").also { stage++ } + 63 -> npcl(ama, "If you're interested in his expedition, the notes he made are in the library in the Exam Centre.").also { stage = 99 } + + //talking to her again after the initial golem quest dialogue + 70 -> (options("What do you do here?", "What is this place?", "Where did you say the notes were?")).also { stage++ } + 71 -> when (buttonId) { + 1 -> player(ask, "What do you do here?").also { stage = 10 } + 2 -> player(ask, "What is this place?").also { stage = 20 } + 3 -> playerl(fr, "Where did you say the notes were?").also { stage = 80 } + } + + 80 -> npc(fr, "They're on a bookcase in the Exam Centre.").also { stage = 99 } + + //after reading varmen's notes + 90 -> (options("What do you do here?", "What is this place?", "Where is the statuette that Varmen took back from Uzer?")).also { stage++ } + 91 -> when (buttonId) { + 1 -> player(ask, "What do you do here?").also { stage = 10 } + 2 -> player(ask, "What is this place?").also { stage = 20 } + 3 -> playerl(fr, "Where is the statuette that Varmen took back from Uzer?").also { stage = 100 } + } + + 100 -> npc(ask, "The statuette? Oh, yes...").also { stage++ } + 101 -> npcl(neu, "That statuette was the only thing we had to show from that expedition.").also { stage++ } + 102 -> npcl(ask, "It was very worn, but you can still make out a lot of detail. The Uzerians were expert sculptors. It's a pity we only have that small example.").also { stage++ } + 103 -> npc(ask, "Now it's on display in the Varrock museum.").also { stage = 99 } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ElissaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ELISSA_1912) + } +} 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 new file mode 100644 index 000000000..9d375798c --- /dev/null +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BarlakDialogue.kt @@ -0,0 +1,247 @@ +package content.region.misthalin.dorgeshuun.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.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 + +/** + * @author qmqz + */ + +@Initializable +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 + + val sets = arrayOf( + intArrayOf(Items.LONG_BONE_10976, 1000, 1500, Skills.CONSTRUCTION), + intArrayOf(Items.CURVED_BONE_10977, 2000, 2250, Skills.CONSTRUCTION), + intArrayOf(Items.SNAIL_SHELL_7800, 600, 0, Skills.CONSTRUCTION), + intArrayOf(Items.PERFECT_SNAIL_SHELL_10996, 600, 500, Skills.CRAFTING), + intArrayOf(Items.TORTOISE_SHELL_7939, 600, 0, Skills.CRAFTING), + intArrayOf(Items.PERFECT_SHELL_10995, 600, 500, Skills.CRAFTING), + ) + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + val playerMeetsBoneReqs = getDynLevel(player, Skills.CONSTRUCTION) >= 30 && hasHouse(player) + if (!playerMeetsBoneReqs) { + sendMessage(player, "You require at least level 30 construction and a house to speak to Barlak about bones.") + } + if (playerMeetsBoneReqs && inInventory(player, Items.LONG_BONE_10976, 1)) { + curItem = 0 + npcl(nhap, "Those bones! Those are exactly the sort of thing I need! Will you sell them?").also { stage = 0 } + } else if (playerMeetsBoneReqs && inInventory(player, Items.CURVED_BONE_10977, 1)) { + curItem = 1 + npcl(nhap, "Those bones! Those are exactly the sort of thing I need! Will you sell them?").also { stage = 0 } + } else if (inInventory(player, Items.PERFECT_SHELL_10995, 1)) { + curItem = 5 + npc(ntalk2, "That giant shell... what is it?").also { stage = 120 } + } else if (inInventory(player, Items.TORTOISE_SHELL_7939, 1)) { + curItem = 4 + npc(ntalk2, "That giant shell... what is it?").also { stage = 100 } + } else if (inInventory(player, Items.PERFECT_SNAIL_SHELL_10996, 1)) { + curItem = 3 + npc(ntalk1, "That giant shell... what is it?").also { stage = 60 } + } else if (inInventory(player, Items.SNAIL_SHELL_7800, 1)) { + curItem = 2 + npc(ntalk1, "That giant shell... what is it?").also { stage = 50 } + } else if (playerMeetsBoneReqs) { + npc(nhap, "Bones!").also { stage = 150 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npcl(ntalk1, "I'll give you ${getTotalCoinsForCurItem()} gp for the bones you're carrying and for any you have in your bank.").also { stage++ } + 1 -> npcl(ntalk2, "I'll try to teach you something about Construction as well, but it's highly technical so you won't understand if you don't already have level 30 Construction.").also { stage++ } + 2 -> options("Okay.", "No, I'll keep the bones.").also { stage++ } + 3 -> when (buttonId) { + 1 -> player(pfr, "Okay.").also { stage = 10 } + 2 -> player(pneu, "No thanks.").also { stage = 99 } + } + + 10 -> { + exchange() + npc(ntalk1, "Thanks! Now, let me explain...").also { stage = 999 } + } + + 50 -> player(pfr, "It's a giant snail shell!").also { stage++ } + 51 -> npcl(ntalk1, "Hmm... I think I might be able to make something out of them.").also { stage++ } + 52 -> npcl(ntalk2, "I'll give you ${getTotalCoinsForCurItem()} gp for the snail shells you're carrying.").also { stage++ } + 53 -> options("Okay.", "No. I'll keep the bones.").also { stage++ } + 54 -> when (buttonId) { + 1 -> player(pfr, "Okay.").also { stage = 55 } + 2 -> player(pneu, "No thanks.").also { stage = 99 } + } + 55 -> { + exchange() + npcl(ntalk1, "Thanks. If you find any more shells like these, please bring them to me!").also { stage = 99 } + } + 60 -> player(pfr, "It's a giant snail shell!").also { stage++ } + 61 -> { + when (amountInInventory(player, Items.PERFECT_SNAIL_SHELL_10996)) { + 1 -> npcl(ntalk2, "Hmm... I think I might be able to make something out of them, especially that perfect one.").also { stage++ } + else -> npcl(ntalk1, "Hmm... I think I might be able to make something out of them, especially those perfect ones.").also { stage = 70 } + } + } + + 62 -> npcl(ntalk2, "I'll give you ${getTotalCoinsForCurItem()} gp for the snail shell you're carrying, and I'll try to give you some advice on Crafting while I'm at it.").also { stage++ } + 63 -> options("Okay.", "No. I'll keep the bones.").also { stage++ } + 64 -> when (buttonId) { + 1 -> player(pfr, "Okay.").also { stage = 65 } + 2 -> player(pneu, "No thanks.").also { stage = 99 } + } + 65 -> { + npcl(ntalk2, "Thanks! Now, I don't think I could use that for Construction, but maybe I could make something...").also { stage = 1000 } + exchange() + } + + 70 -> npcl(ntalk2, "I'll give you ${getTotalCoinsForCurItem()} gp for the snail shells you're carrying, and I'll try to give you some advice on Crafting while I'm at it.").also { stage++ } + 71 -> options("Okay.", "No. I'll keep the bones.").also { stage++ } + 72 -> when (buttonId) { + 1 -> player(pfr, "Okay.").also { stage = 75 } + 2 -> player(pneu, "No thanks.").also { stage = 99 } + } + 75 -> { + npcl(ntalk2, "Thanks! Now, I don't think I could use that for Construction, but maybe I could make something...").also { stage = 1000 } + exchange() + } + + 100 -> player(pfr, "It's a giant battle tortoise shell!").also { stage++ } + 101 -> npcl(ntalk1, "Hmm... I think I might be able to make something out of them.").also { stage++ } + 102 -> npcl(ntalk2, "I'll give you ${getTotalCoinsForCurItem()} gp for the tortoise shells you're carrying.").also { stage++ } + 103 -> options("Okay.", "No. I'll keep the bones.").also { stage++ } + 104 -> when (buttonId) { + 1 -> player(pfr, "Okay.").also { stage = 105 } + 2 -> player(pneu, "No thanks.").also { stage = 99 } + } + 105 -> { + npcl(ntalk2, "Thanks. If you find any more shells like these, please bring them to me!").also { stage = 130 } + exchange() + } + + 120 -> player(pfr, "It's a perfect shell!").also { stage++ } + 121 -> { + when (amountInInventory(player, Items.PERFECT_SHELL_10995)) { + 1 -> npcl(ntalk2, "Hmm... I think I might be able to make something out of them, especially that perfect one.").also { stage++ } + else -> npcl(ntalk1, "Hmm... I think I might be able to make something out of them, especially those perfect ones.").also { stage++ } + } + } + 122 -> npcl(ntalk2, "I'll give you ${getTotalCoinsForCurItem()} gp for the perfect shell you're carrying, and I'll try to give you some advice on Crafting while I'm at it.").also { stage++ } + 123 -> options("Okay.", "No. I'll keep the bones.").also { stage++ } + 124 -> when (buttonId) { + 1 -> player(pfr, "Okay.").also { stage = 125 } + 2 -> player(pneu, "No thanks.").also { stage = 99 } + } + 125 -> { + npcl(ntalk2, "Thanks! Now, I don't think I could use that for Construction, but maybe I could make something...").also { stage = 1001 } + exchange() + } + + 130 -> { + if (inInventory(player, Items.SNAIL_SHELL_7800, 1)) { + curItem = 2 + npc(ntalk1, "That giant shell... what is it?").also { stage = 50 } + } else if (inInventory(player, Items.PERFECT_SNAIL_SHELL_10996, 1)) { + curItem = 3 + npc(ntalk1, "That giant shell... what is it?").also { stage = 60 } + } else { + end() + } + } + + 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(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(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(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++ } + 242 -> npcl(ntalk2, "I'll pay double for them and give you a few tips about Crafting too.").also { stage = 152 } + + 260 -> npcl(ntalk1, "Enormous bones, as big as you can get. Not just any big bones will do. Sometimes you might find a particularly long, straight bone. That's the kind of thing I need; I'll give you 1,000gp for one of them.").also { stage++ } + 261 -> npcl(ntalk2, "Occasionally, you might find a long curved bone - these are especially valuable - I'll give you 2,000gp for them. I'll also teach you a bit about how we use the bones in Construction, if you like.").also { stage = 152 } + + 280 -> npc(ntalk2, "Goodbye.").also { stage = 99 } + + 999 -> sendDialogue("Barlak gives you a short lecture and you learn more about Construction.").also { stage = 99 } + 1000 -> sendDialogue("Barlak gives you a short lecture and you learn more about Crafting.").also { stage = 99 } + 1001 -> sendDialogue("Barlak gives you a short lecture and you learn more about Crafting.").also { stage = 130 } + 99 -> end() + } + return true + } + + + private fun exchange() { + val item = getItemForCurItem() + val numToExchange = getNumToExchange() + removeAll(player, item, Container.INVENTORY) + removeAll(player, item, Container.BANK) + addItemOrDrop(player, Items.COINS_995, getCoinsForCurItem() * numToExchange) + rewardXP(player, getSkillForCurItem(), (getXpForCurItem() * numToExchange).toDouble()) + } + + private fun getNumToExchange(): Int { + val itemId = getItemIdForCurItem() + return amountInInventory(player, itemId) + amountInBank(player, itemId) + } + + private fun getTotalCoinsForCurItem(): Int { + return getCoinsForCurItem() * getNumToExchange() + } + + private fun getItemForCurItem(): Item { + return Item(getItemIdForCurItem()) + } + + private fun getItemIdForCurItem(): Int { + return sets[curItem][0] + } + + private fun getCoinsForCurItem(): Int { + return sets[curItem][1] + } + + private fun getXpForCurItem(): Int { + return sets[curItem][2] + } + + private fun getSkillForCurItem(): Int { + return sets[curItem][3] + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BarlakDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BARLAK_5828) + } +} diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BartakDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BartakDialogue.kt new file mode 100644 index 000000000..3de1e2e86 --- /dev/null +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BartakDialogue.kt @@ -0,0 +1,44 @@ +package content.region.misthalin.dorgeshuun.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 BartakDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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?): DialoguePlugin { + return BartakDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BARTAK_5778) + } +} diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/CrateGoblinDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/CrateGoblinDialogue.kt new file mode 100644 index 000000000..614c33949 --- /dev/null +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/CrateGoblinDialogue.kt @@ -0,0 +1,38 @@ +package content.region.misthalin.dorgeshuun.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 CrateGoblinDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_NORMAL,"Excuse me, I need to deliver this.").also { stage = 99 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return CrateGoblinDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CRATE_GOBLIN_5784) + } +} diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DartogDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DartogDialogue.kt new file mode 100644 index 000000000..dbf286bc9 --- /dev/null +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DartogDialogue.kt @@ -0,0 +1,72 @@ +package content.region.misthalin.dorgeshuun.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 + * todo add teleporting when areas are complete + */ + +@Initializable +class DartogDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.OLD_NORMAL,"Hello, surface-dweller.").also { stage = 0 } + return true + } + + 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) { + 0 -> options("Who are you?", "Can you show me the way to the mine?", "Can you show me the way to Lumbridge Castle cellar?").also { stage++ } + 1 -> when (buttonId) { + 1 -> player(ask, "Who are you?").also { stage = 10} + 2 -> npcl(nor, "Of course! You're always welcome in our mines!").also { stage = 20 } + 3 -> player(ask, "Can you show me the way to Lumbridge Castle cellar?").also { stage = 50 } + } + + 10 -> npcl(nor, "The council posted me here to guard this new tunnel. I can also give you directions through the tunnels. A hero like you is always welcome in our mines!").also { stage++ } + 11 -> options("Can you show me the way to the mine?", "Can you show me the way to Lumbridge Castle cellar?", "Maybe some other time").also { stage++ } + 12 -> when (buttonId) { + 1 -> player(ask, "Can you show me the way to the mine?").also { stage = 40 } + 2 -> player(ask, "Can you show me the way to Lumbridge Castle cellar?").also { stage = 50 } + 3 -> player(fr, "Maybe some other time.").also { stage = 99 } + } + + 20 -> { + end() + //move player to mine + } + + 40 -> { + npcl(nor, "Of course! You're always welcome in our mines!").also { stage = 99 } + //move player to mine + } + + 50 -> { + npc(nor, "Of course!").also { stage = 99 } + //move to lumby castle celler + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DartogDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DARTOG_4314) + } +} diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DurgokDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DurgokDialogue.kt new file mode 100644 index 000000000..81d78acb9 --- /dev/null +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DurgokDialogue.kt @@ -0,0 +1,54 @@ +package content.region.misthalin.dorgeshuun.dialogue + +import core.api.addItemOrDrop +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.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * @author qmqz + */ + +@Initializable +class DurgokDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("Yes, please.", "No, thanks.").also { stage++ } + 1 -> when(buttonId) { + 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(FacialExpression.OLD_NORMAL, "There you go.").also { stage = 99 } + } else { + npc(FacialExpression.OLD_NORMAL, "I'm sorry, but you need 10gp for that.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DurgokDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DURGOK_5794) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/MistagDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/MistagDialogue.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/content/dialogue/MistagDialogue.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/dialogue/MistagDialogue.kt index 68be71adb..a05bfeb93 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/MistagDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/MistagDialogue.kt @@ -1,15 +1,16 @@ -package rs09.game.content.dialogue +package content.region.misthalin.dorgeshuun.dialogue +import content.data.Quests import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 rs09.game.content.quest.members.thelosttribe.GoblinFollower -import rs09.game.content.quest.members.thelosttribe.MistagLTDialogue -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +import content.region.misthalin.dorgeshuun.quest.thelosttribe.GoblinFollower +import content.region.misthalin.dorgeshuun.quest.thelosttribe.MistagLTDialogue +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE @Initializable class MistagDialogue (player: Player? = null) : DialoguePlugin(player){ @@ -23,7 +24,7 @@ class MistagDialogue (player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - val ltStage = player.questRepository.getStage("Lost Tribe") + val ltStage = player.questRepository.getStage(Quests.THE_LOST_TRIBE) if(args.size > 1 && args[1] == "greeting"){ npc("A human knows ancient greeting?") diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/randomChildrenDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/randomChildrenDialogue.kt new file mode 100644 index 000000000..9063efe67 --- /dev/null +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/randomChildrenDialogue.kt @@ -0,0 +1,59 @@ +package content.region.misthalin.dorgeshuun.dialogue + +import core.api.toIntArray +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 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(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 } + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> player(b, "Yes...").also { stage++ } + 1 -> npcl(a, "Haha! You look funny! All tall and skinny with tiny eyes!").also { stage = 99 } + + 10 -> player(b, "Yes, that was me!").also { stage++ } + 11 -> npcl(a, "When I'm older I'm going to be an adventurer, just like Zanik!").also { stage = 99 } + + 20 -> player(b, "Don't tell anyone what?").also { stage++ } + 21 -> npc(a, "SHHH!").also { stage = 99 } + + 30 -> player(b, "It's alright, I'm friendly!").also { stage = 99 } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return randomChildrenDialogue(player) + } + + val ids = 5807..5822 + + override fun getIds(): IntArray { + return ids.toIntArray() + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/CaveGoblinMinerNPC.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveGoblinMinerNPC.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/CaveGoblinMinerNPC.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveGoblinMinerNPC.kt index bb144e6fb..4d6feec03 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/CaveGoblinMinerNPC.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveGoblinMinerNPC.kt @@ -1,4 +1,4 @@ -package rs09.game.content.quest.members.thelosttribe +package content.region.misthalin.dorgeshuun.quest.thelosttribe import core.game.node.entity.Entity import core.game.node.entity.npc.AbstractNPC @@ -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/kotlin/rs09/game/content/quest/members/thelosttribe/CaveRockHandler.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveRockHandler.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/CaveRockHandler.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveRockHandler.kt index deb567a76..bad59c4fe 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/CaveRockHandler.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveRockHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.content.quest.members.thelosttribe +package content.region.misthalin.dorgeshuun.quest.thelosttribe import core.cache.def.impl.SceneryDefinition import core.game.component.Component diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/CaveZone.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveZone.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/CaveZone.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveZone.kt index 79ca033a7..68744f6ca 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/CaveZone.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/CaveZone.kt @@ -1,10 +1,10 @@ -package rs09.game.content.quest.members.thelosttribe +package content.region.misthalin.dorgeshuun.quest.thelosttribe import core.game.node.entity.Entity import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.player.Player import core.game.system.task.Pulse -import rs09.game.world.GameWorld +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 diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/DukeHoracioTLTDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DukeHoracioTLTDialogue.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/DukeHoracioTLTDialogue.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DukeHoracioTLTDialogue.kt index ca1fecb5c..c71192c37 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/DukeHoracioTLTDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DukeHoracioTLTDialogue.kt @@ -1,12 +1,14 @@ -package rs09.game.content.quest.members.thelosttribe +package content.region.misthalin.dorgeshuun.quest.thelosttribe -import core.game.content.dialogue.FacialExpression +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 import org.rs09.consts.Items -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { private val Sigmund = NPC(2082) @@ -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 } } @@ -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 } } @@ -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 } } @@ -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 } } @@ -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/kotlin/rs09/game/content/quest/members/thelosttribe/GoblinFollower.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/GoblinFollower.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/GoblinFollower.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/GoblinFollower.kt index 699e11768..987f73012 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/GoblinFollower.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/GoblinFollower.kt @@ -1,11 +1,11 @@ -package rs09.game.content.quest.members.thelosttribe +package content.region.misthalin.dorgeshuun.quest.thelosttribe import core.game.component.Component import core.game.node.entity.player.Player import core.game.system.task.Pulse import core.game.world.map.Location import org.rs09.consts.Components -import rs09.game.world.GameWorld +import core.game.world.GameWorld /** * Small object for the goblin follow options diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/HistoryOfTheGoblinRace.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/HistoryOfTheGoblinRace.kt similarity index 86% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/HistoryOfTheGoblinRace.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/HistoryOfTheGoblinRace.kt index d7db3d751..d6d5ed9ab 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/HistoryOfTheGoblinRace.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/HistoryOfTheGoblinRace.kt @@ -1,9 +1,10 @@ -package rs09.game.content.quest.members.thelosttribe +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 -import core.game.content.dialogue.FacialExpression +import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable import core.plugin.Plugin @@ -23,11 +24,11 @@ class HistoryOfTheGoblinRace : ComponentPlugin() { player ?: return super.open(player, component) player.packetDispatch.sendInterfaceConfig(183,17,true) - val qstage = player.questRepository.getQuest("Lost Tribe").getStage(player) + val qstage = player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) component?.setCloseEvent { player, c -> if(qstage == 42 || qstage == 41 ) { player.dialogueInterpreter.sendDialogues(player, FacialExpression.THINKING, "Hey... The symbol of the 'Dorgeshuun' tribe looks just", "like the symbol on the brooch I found.") - player.questRepository.getQuest("Lost Tribe").setStage(player, 43) + player.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player, 43) } player.removeAttribute("hgr-index") true diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/KazgarDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/KazgarDialogue.kt similarity index 88% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/KazgarDialogue.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/KazgarDialogue.kt index c206fc67c..86d056d40 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/KazgarDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/KazgarDialogue.kt @@ -1,6 +1,6 @@ -package rs09.game.content.quest.members.thelosttribe +package content.region.misthalin.dorgeshuun.quest.thelosttribe -import core.game.content.dialogue.DialoguePlugin +import core.game.dialogue.DialoguePlugin import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable @@ -31,7 +31,7 @@ class KazgarDialogue(player: Player? = null) : DialoguePlugin(player) { 10 -> npc("I'm Kazgar, I guide people through the mines.").also { stage = 1000 } 20 -> npc("Certainly!").also { stage++ } 21 -> end().also { - GoblinFollower.sendToMines(player) + GoblinFollower.sendToMines(player) } 1000 -> end() diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribe.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribe.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribe.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribe.kt index 161ecc35d..2bbcda87a 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribe.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribe.kt @@ -1,4 +1,4 @@ -package rs09.game.content.quest.members.thelosttribe +package content.region.misthalin.dorgeshuun.quest.thelosttribe import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest @@ -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 new file mode 100644 index 000000000..f155df3f5 --- /dev/null +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeCutscene.kt @@ -0,0 +1,122 @@ +package content.region.misthalin.dorgeshuun.quest.thelosttribe + +import content.data.Quests +import core.api.animate +import core.api.face +import core.game.activity.Cutscene +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.emote.Emotes +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.global.action.DoorActionHandler +import core.game.dialogue.FacialExpression + +class LostTribeCutscene(player: Player) : Cutscene(player) { + override fun setup() { + setExit(Location.create(3207, 3221, 0)) + if(player.settings.isRunToggled){ + player.settings.toggleRun() + } + loadRegion(12850) + addNPC(DUKE, 6, 23, Direction.SOUTH) + addNPC(MISTAG, 8, 17, Direction.NORTH) + addNPC(URTAG, 8, 15, Direction.NORTH) + addNPC(SIGMUND, 13, 22, Direction.NORTH_WEST) + } + + override fun runStage(stage: Int) { + when(stage) + { + 0 -> { + fadeToBlack() + timedUpdate(6) + } + 1 -> { + teleport(player, 7, 17) + timedUpdate(2) + } + 2 -> { + fadeFromBlack() + moveCamera(8, 26) + rotateCamera(5, 18) + timedUpdate(6) + } + 3 -> { + DoorActionHandler.handleDoor(player, getObject(7, 17)) + timedUpdate(3) + } + 4 -> { + move(player, 7, 22) + move(getNPC(MISTAG)!!, 7, 19) + move(getNPC(URTAG)!!, 7, 18) + timedUpdate(5) + } + 5 -> { + move(getNPC(MISTAG)!!, 7, 20) + move(getNPC(URTAG)!!, 6, 21) + timedUpdate(3) + } + 6 -> { + player.faceLocation(getNPC(DUKE)!!.location) + playerDialogueUpdate(FacialExpression.FRIENDLY, "Your grace, I present Ur-tag, headman of the Dorgeshuun.") + Emotes.BOW.play(player) + } + 7 -> { + move(player, 7, 24) + timedUpdate(5) + } + 8 -> { + animate(getNPC(DUKE)!!, Emotes.BOW.animation) + animate(getNPC(URTAG)!!, URTAG_BOW_ANIM) + dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "Welcome, Ur-tag. I am sorry that your race came under suspicion.") + } + 9 -> { + dialogueUpdate(DUKE, FacialExpression.ANGRY, "I assure you that the warmongering element has been dealt with.") + moveCamera(9, 22) + rotateCamera(6, 22) + } + 10 -> dialogueUpdate(URTAG, FacialExpression.OLD_NORMAL, "I apologize for the damage to your cellar. I will send workers to repair the hole.") + 11 -> dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "No, let it stay. It can be a route of commerce between our lands.") + 12 -> { + val duke = getNPC(DUKE)!! + face(duke, player.location) + face(player, duke.location) + rotateCamera(6, 22, 300, 3) + moveCamera(11, 22, 325, 3) + dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "${player.username}, Lumbridge is in your debt. Please accept this ring as a token of my thanks.") + } + 13 -> dialogueUpdate(DUKE, FacialExpression.FRIENDLY, "It is enchanted to save you in your hour of need.") + 14 -> { + move(getNPC(URTAG)!!, 7, 23) + dialogueUpdate(URTAG, FacialExpression.OLD_NORMAL,"I too thank you. Accept the freedom of the Dorgeshuun mines." ) + } + 15 -> { + dialogueUpdate(URTAG, FacialExpression.OLD_NORMAL, "These are strange times. I never dreamed that I would see the surface, still less that I would be on friendly terms with its people." ) + moveCamera(16, 21, 300, 3) + rotateCamera(6, 22, 300, 2) + } + 16 -> dialogueUpdate(SIGMUND, FacialExpression.ANGRY, "Prattle on, goblin.") + 17 -> { + animate(getNPC(SIGMUND)!!, Emotes.LAUGH.animation) + dialogueUpdate(SIGMUND, FacialExpression.EVIL_LAUGH, "Soon you will be destroyed!") + } + 18 -> { + move(getNPC(SIGMUND)!!, 16, 17) + timedUpdate(4) + } + 19 -> { + end { + player.questRepository.getQuest(Quests.THE_LOST_TRIBE).finish(player) + } + } + } + } + + companion object { + private const val DUKE = 2088 + private const val MISTAG = 2089 + private const val SIGMUND = 2090 + private const val URTAG = 5858 + private const val URTAG_BOW_ANIM = 6000 + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribeOptionHandler.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeOptionHandler.kt similarity index 79% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribeOptionHandler.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeOptionHandler.kt index 9975fcf24..de266e188 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribeOptionHandler.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/LostTribeOptionHandler.kt @@ -1,10 +1,11 @@ -package rs09.game.content.quest.members.thelosttribe +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.content.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,16 +50,16 @@ 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.") } } 32952,32953 -> { - player.dialogueInterpreter.sendDialogues(player,FacialExpression.THINKING,"I don't think I have permission to go in there.") + player.dialogueInterpreter.sendDialogues(player, core.game.dialogue.FacialExpression.THINKING,"I don't think I have permission to go in there.") } } diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/MistagLTDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/MistagLTDialogue.kt similarity index 84% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/MistagLTDialogue.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/MistagLTDialogue.kt index 8ae776b8d..8c637a839 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/MistagLTDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/MistagLTDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.quest.members.thelosttribe +package content.region.misthalin.dorgeshuun.quest.thelosttribe +import content.data.Quests import core.game.component.Component -import core.game.content.activity.ActivityManager -import core.game.content.dialogue.FacialExpression -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +import core.game.dialogue.FacialExpression +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE class MistagLTDialogue(val isGreeting: Boolean, val questStage: Int) : DialogueFile() { @@ -21,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) } } } @@ -33,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) } } } @@ -45,7 +46,8 @@ class MistagLTDialogue(val isGreeting: Boolean, val questStage: Int) : DialogueF 3 -> npc("I will summon Ur-tag, our headman, at once.").also { stage++ } 4 -> { end() - ActivityManager.start(player,"Lost Tribe Cutscene",false) + LostTribeCutscene(player!!).start() + //ActivityManager.start(player,"Lost Tribe Cutscene",false) } } } diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/PickaxeOnRubble.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickaxeOnRubble.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/PickaxeOnRubble.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickaxeOnRubble.kt index c86aa3361..a1a871135 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/PickaxeOnRubble.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickaxeOnRubble.kt @@ -1,13 +1,15 @@ -package rs09.game.content.quest.members.thelosttribe +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 -import core.game.node.entity.skill.gather.SkillingTool +import content.data.skill.SkillingTool import core.game.system.task.Pulse -import rs09.game.world.GameWorld +import core.game.world.GameWorld import core.plugin.Initializable import core.plugin.Plugin +import core.api.* @Initializable /** @@ -16,13 +18,15 @@ import core.plugin.Plugin */ class PickaxeOnRubble : UseWithHandler(1265,1267,1269,1271,1273,1275){ override fun newInstance(arg: Any?): Plugin { - addHandler(6898, OBJECT_TYPE,this) + for(id in arrayOf(6898, 6903, 6904, 6905)) { + addHandler(id, OBJECT_TYPE,this) + } return this } 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 @@ -55,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 } @@ -66,4 +70,4 @@ class PickaxeOnRubble : UseWithHandler(1265,1267,1269,1271,1273,1275){ return true } -} \ No newline at end of file +} 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 new file mode 100644 index 000000000..a4e519efa --- /dev/null +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/PickpocketSigmund.kt @@ -0,0 +1,45 @@ +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 +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.GameWorld + +/** + * handles pickpocketing sigmund during the lost tribe quest + * @author Ceikry + */ +class PickpocketSigmund : InteractionListener { + val SIGMUND = NPCs.SIGMUND_2082 + + override fun defineListeners() { + on(SIGMUND, IntType.NPC, "pickpocket"){ player, node -> + player.lock() + GameWorld.Pulser.submit(object : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> player.animator.animate(Animation(881)) + 3 -> { + 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 { + player.dialogueInterpreter.sendDialogues(2082, core.game.dialogue.FacialExpression.ANGRY,"What do you think you're doing?!") + } + player.unlock() + return true + } + } + return false + } + }) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/SigmundChestHandler.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/SigmundChestHandler.kt similarity index 78% rename from Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/SigmundChestHandler.kt rename to Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/SigmundChestHandler.kt index 95cfc4e0f..ef26dc557 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/SigmundChestHandler.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/SigmundChestHandler.kt @@ -1,5 +1,6 @@ -package rs09.game.content.quest.members.thelosttribe +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/java/core/game/content/dialogue/AggieDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/AggieDialogue.java similarity index 83% rename from Server/src/main/java/core/game/content/dialogue/AggieDialogue.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/AggieDialogue.java index 5a97e4a79..fe7fb4830 100644 --- a/Server/src/main/java/core/game/content/dialogue/AggieDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/AggieDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; @@ -7,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. @@ -24,7 +27,14 @@ public final class AggieDialogue extends DialoguePlugin { /** * Represents the ingredients needed to make paste. */ - private static final Item[] PASTE_INGREDIENTS = new Item[] { new Item(592), new Item(1951), new Item(1929), new Item(1933) }; + + private static final Item ASHES = new Item(592); + private static final Item POT_OF_FLOUR = new Item(1933); + private static final Item REDBERRIES_SINGLE= new Item(1951); // specifying single because 3 redberries are needed for the red dye + private static final Item[] PASTE_SOLID_INGREDIENTS = new Item[] { ASHES, REDBERRIES_SINGLE, POT_OF_FLOUR }; + + private static final Item BUCKET_OF_WATER = new Item(1929); + private static final Item JUG_OF_WATER = new Item(1937); // Jug of Water is meant to be substitutable with the Bucket of Water /** * Represents the cauldron location @@ -106,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; @@ -143,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; } @@ -200,7 +210,8 @@ public final class AggieDialogue extends DialoguePlugin { stage = 732; break; case 732: - if (player.getInventory().remove(PASTE_INGREDIENTS)) { + // remove the solid ingredients and one of the water ingredients + if (player.getInventory().remove(PASTE_SOLID_INGREDIENTS) && (player.getInventory().remove(BUCKET_OF_WATER) || player.getInventory().remove(JUG_OF_WATER))) { interpreter.sendDialogue("You hand the ash, flour, water and redberries to Aggie.", "Aggie tips the ingredients into a cauldron", "and mutters some words."); stage = 733; } @@ -237,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: @@ -253,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; } @@ -271,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: @@ -294,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: @@ -317,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: @@ -389,11 +400,16 @@ public final class AggieDialogue extends DialoguePlugin { * @return {@code True} if so. */ private final boolean hasIngredients(final Player player) { - for (Item i : PASTE_INGREDIENTS) { + // check if the player has all the solid ingredients + for (Item i : PASTE_SOLID_INGREDIENTS) { if (!player.getInventory().containsItem(i)) { return false; } } + // check if the player has a bucket or jug of water + if (!(player.getInventory().containsItem(BUCKET_OF_WATER) || player.getInventory().containsItem(JUG_OF_WATER))) { + return false; + } return true; } 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 new file mode 100644 index 000000000..754d79dce --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/DiangoDialogue.java @@ -0,0 +1,109 @@ +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 by diango. + * @author 'Vexia + * @version 1.1 + */ +@Initializable +public final class DiangoDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code DiangoDialogue {@code Object}. + */ + public DiangoDialogue() { + /* + * empty. + */ + } + + /** + * Constructs a new {@code DiangoDialogue} {@code Object}. + * @param player the player. + */ + public DiangoDialogue(Player player) { + super(player); + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + options("Spinning plates?", "I'd like to check holiday items please!", "I'd like to claim purchased cosmetics."); + stage++; + break; + case 1: + switch (buttonId) { + case 1: + player("Spinning plates?"); + stage = 10; + break; + case 2: + player("I'd like to check holiday items please?"); + stage = 20; + break; + case 3: + player("I'd like to claim purchased cosmetics."); + stage = 30; + break; + } + break; + case 10: + 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.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: + end(); + npc.openShop(player); + break; + case 20: + npc("Sure thing, let me just see what you're missing."); + stage++; + break; + case 21: + openItemReturn(player); + end(); + break; + case 30: + end(); + break; + } + return true; + } + + /** + * Opens the item return. + * @param player the player. + */ + private void openItemReturn(Player player) { + + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new DiangoDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + 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; + } + + @Override + public int[] getIds() { + return new int[] { 970 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/DraynorBankGuard.java b/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/DraynorBankGuard.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java index 12ac334f5..13275ac76 100644 --- a/Server/src/main/java/core/game/content/dialogue/DraynorBankGuard.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java @@ -1,6 +1,8 @@ -package core.game.content.dialogue; +package content.region.misthalin.draynor.dialogue; -import core.game.content.activity.ActivityManager; +import core.game.activity.ActivityManager; +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; @@ -229,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.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/java/core/game/content/dialogue/JoeGuardDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/JoeGuardDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/JoeGuardDialogue.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/JoeGuardDialogue.java index f7d1018e7..b7b91c6d8 100644 --- a/Server/src/main/java/core/game/content/dialogue/JoeGuardDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/JoeGuardDialogue.java @@ -1,10 +1,13 @@ -package core.game.content.dialogue; +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.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. @@ -47,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/java/core/game/content/dialogue/LeelaDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/LeelaDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/LeelaDialogue.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/LeelaDialogue.java index b5c35101c..133cebfb1 100644 --- a/Server/src/main/java/core/game/content/dialogue/LeelaDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/LeelaDialogue.java @@ -1,11 +1,13 @@ -package core.game.content.dialogue; +package content.region.misthalin.draynor.dialogue; +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.GroundItemManager; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represnets the dialogue used to handle the Leela npc. @@ -75,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: @@ -137,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: @@ -181,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: @@ -214,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/java/core/game/content/dialogue/MartinTheMasterGardener.java b/Server/src/main/content/region/misthalin/draynor/dialogue/MartinTheMasterGardener.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/MartinTheMasterGardener.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/MartinTheMasterGardener.java index afc6cecd8..b3646c209 100644 --- a/Server/src/main/java/core/game/content/dialogue/MartinTheMasterGardener.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/MartinTheMasterGardener.java @@ -1,6 +1,8 @@ -package core.game.content.dialogue; +package content.region.misthalin.draynor.dialogue; -import core.game.content.global.Skillcape; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +import core.game.global.Skillcape; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/java/core/game/content/dialogue/MissSchismDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/MissSchismDialogue.java similarity index 87% rename from Server/src/main/java/core/game/content/dialogue/MissSchismDialogue.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/MissSchismDialogue.java index 92008a86a..afa313cd1 100644 --- a/Server/src/main/java/core/game/content/dialogue/MissSchismDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/MissSchismDialogue.java @@ -1,8 +1,11 @@ -package core.game.content.dialogue; +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; +import content.data.Quests; /** * Represents the miss schism dialogue plugin. @@ -88,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 { @@ -101,7 +104,7 @@ public final class MissSchismDialogue extends DialoguePlugin { stage = 112; break; case 112: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm sure I could, but I don't see why. The vampie wasn't", "bothering me."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm sure I could, but I don't see why. The vampire wasn't", "bothering me."); stage = 113; break; case 113: @@ -127,7 +130,7 @@ public final class MissSchismDialogue extends DialoguePlugin { stage = 212; break; case 212: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "And who'd have ever thought such a sweet old genleman", "would do such a thing?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "And who'd have ever thought such a sweet old gentleman", "would do such a thing?"); stage = 213; break; case 213: @@ -150,12 +153,12 @@ public final class MissSchismDialogue extends DialoguePlugin { stage = 22; break; case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I also do voluntary work for the Draynor Manor", "Restoration Fund. We're campagning to have", "Draynor manor turned into a museum before the wet-rot", "destroys it completely."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I also do voluntary work for the Draynor Manor", "Restoration Fund. We're campaigning to have", "Draynor manor turned into a museum before the wet-rot", "destroys it completely."); stage = 23; break; case 23: - if(player.getQuestRepository().isComplete("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 to mutch trouble turning it into a", "museum."); + 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 { end(); @@ -185,3 +188,6 @@ public final class MissSchismDialogue extends DialoguePlugin { return new int[] { 2634 }; } } +/** + * Fixed Vexia's crappy English in some sentences + */ \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/MorganDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/MorganDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/MorganDialogue.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/MorganDialogue.java index 1bbbae1ff..e7d150cf7 100644 --- a/Server/src/main/java/core/game/content/dialogue/MorganDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/MorganDialogue.java @@ -1,9 +1,12 @@ -package core.game.content.dialogue; +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.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. @@ -37,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 new file mode 100644 index 000000000..4f53b99b9 --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/OliviaDialogue.java @@ -0,0 +1,80 @@ +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; + +/** + * Handles the OliviaDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class OliviaDialogue extends DialoguePlugin { + + public OliviaDialogue() { + + } + + public OliviaDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 2233, 2572 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "Yes", "No", "Where do I get rarer seeds from?"); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + end(); + npc.openShop(player); + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, thanks."); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Where do I get rarer seeds from?"); + stage = 40; + break; + + } + break; + case 20: + end(); + break; + case 40: + 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: + end(); + break; + } + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new OliviaDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Would you like to trade in seeds?"); + stage = 0; + return true; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/PrinceAliDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/PrinceAliDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/PrinceAliDialogue.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/PrinceAliDialogue.java index 4d6fdfcba..cb13ff920 100644 --- a/Server/src/main/java/core/game/content/dialogue/PrinceAliDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/PrinceAliDialogue.java @@ -1,12 +1,14 @@ -package core.game.content.dialogue; +package content.region.misthalin.draynor.dialogue; +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.plugin.Initializable; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; +import content.data.Quests; /** * Represents the dialogue used to handle the Pricne Ali NPC. @@ -51,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."); @@ -80,7 +82,7 @@ public class PrinceAliDialogue extends DialoguePlugin { stage = 1; break; case 1: - interpreter.sendDialogues(player, null, "With a dusiguise. 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/java/core/game/content/dialogue/ProfessorOddensteinPlugin.java b/Server/src/main/content/region/misthalin/draynor/dialogue/ProfessorOddensteinPlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/ProfessorOddensteinPlugin.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/ProfessorOddensteinPlugin.java index 55f3866b8..b90bdc349 100644 --- a/Server/src/main/java/core/game/content/dialogue/ProfessorOddensteinPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/ProfessorOddensteinPlugin.java @@ -1,17 +1,20 @@ -package core.game.content.dialogue; +package content.region.misthalin.draynor.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; 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.quest.Quest; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; -import rs09.game.world.repository.Repository; +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 @@ -79,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: @@ -100,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/java/core/game/content/dialogue/VeronicaDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/VeronicaDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/VeronicaDialogue.java rename to Server/src/main/content/region/misthalin/draynor/dialogue/VeronicaDialogue.java index 942982b2e..3480ca1b4 100644 --- a/Server/src/main/java/core/game/content/dialogue/VeronicaDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/VeronicaDialogue.java @@ -1,9 +1,12 @@ -package core.game.content.dialogue; +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.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. @@ -37,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) { @@ -167,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 new file mode 100644 index 000000000..d6c82d58f --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/WiseOldManDialogue.java @@ -0,0 +1,241 @@ +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. + * + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class WiseOldManDialogue extends DialoguePlugin { + + /** + * Represents the items to use. + */ + private static final Item[] ITEMS = new Item[]{new Item(9813), new Item(9814)}; + + /** + * Represents the coins item. + */ + 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}. + */ + public WiseOldManDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code WiseOldManDialogue} {@code Object}. + * + * @param player the player. + */ + public WiseOldManDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new WiseOldManDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + npc("Greetings, " + player.getUsername() + ","); + stage = 0; + return true; + } + + @Override + 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; + return true; + } + options("Is there anything I can do for you?", "Could you check my things for junk, please?", "I've got something I'd like you to look at."); + stage = 1; + break; + case 500: + switch (buttonId) { + case 1: + player("I believe you are the person to talk to if I want to buy", "a Quest Point Cape?"); + stage = 501; + break; + case 2: + options("Is there anything I can do for you?", "Could you check my things for junk, please?", "I've got something I'd like you to look at."); + stage = 1; + break; + } + break; + case 501: + npc("Indeed you believe rightly, " + player.getUsername() + ", and if you know that", "then you'll also know that they cost 99000 coins."); + stage = 502; + break; + case 502: + options("No, I hadn't heard that!", "Yes, so I was lead to believe."); + stage = 503; + break; + case 503: + switch (buttonId) { + case 1: + player("No, I hadn't heard that!"); + stage = 504; + break; + case 2: + player("Yes, so I was lead to believe."); + stage = 506; + break; + } + break; + case 504: + npc("Well that's the cost, and it's not changing."); + stage = 505; + break; + case 505: + end(); + break; + case 506: + if (player.getInventory().freeSlots() < 2) { + player("I don't seem to have enough inventory space."); + stage = 507; + return true; + } + if (!player.getInventory().containsItem(COINS)) { + player("I don't seem to have enough coins with", "me at this time."); + stage = 507; + return true; + } + if (player.getInventory().remove(COINS) && player.getInventory().add(ITEMS)) { + npc("Have fun with it."); + stage = 507; + } else { + player("I don't seem to have enough coins with", "me at this time."); + stage = 507; + } + break; + case 507: + end(); + break; + case 1: + switch (buttonId) { + case 1: + player("Is there anything I can do for you?"); + stage = 10; + break; + case 2: + options("Could you check my bank for junk, please?", "Could you check my inventory for junk, please?"); + stage = 20; + break; + case 3: + player("I've got something I'd like you to look at."); + stage = 30; + break; + } + + break; + case 10: + npc("Thanks, but I don't have anything I need."); + stage = 11; + break; + case 11: + end(); + break; + case 20: + npc("Certainly, but I should warn you that I don't know about", "all items."); + switch (buttonId) { + case 1: + stage = 100; + break; + case 2: + stage = 102; + break; + } + break; + case 100: + 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: + 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."); + stage = 31; + break; + case 31: + end(); + break; + } + + return true; + } + + @Override + public int[] getIds() { + return new int[]{2253, 3820}; + } +} diff --git a/Server/src/main/java/core/game/content/cutscene/DBRCutscenePlugin.java b/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java similarity index 97% rename from Server/src/main/java/core/game/content/cutscene/DBRCutscenePlugin.java rename to Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java index df29bf950..78c30fa3e 100644 --- a/Server/src/main/java/core/game/content/cutscene/DBRCutscenePlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java @@ -1,9 +1,9 @@ -package core.game.content.cutscene; +package content.region.misthalin.draynor.handlers; import core.game.component.Component; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.global.action.DoorActionHandler; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.global.action.DoorActionHandler; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.Node; @@ -22,7 +22,7 @@ import core.game.node.item.ItemPlugin; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.RegionManager; @@ -35,7 +35,7 @@ import core.net.packet.context.CameraContext; import core.net.packet.context.CameraContext.CameraType; import core.net.packet.out.CameraViewPacket; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the draynor bank robbery cutscene plugin. @@ -228,7 +228,7 @@ public final class DBRCutscenePlugin extends CutscenePlugin { } catch (Throwable e) { e.printStackTrace(); } - PluginManager.definePlugin(new BluePhatItem()); + ClassScanner.definePlugin(new BluePhatItem()); } @Override @@ -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)); } /** @@ -867,7 +867,7 @@ public final class DBRCutscenePlugin extends CutscenePlugin { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { return true; } 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/java/core/game/interaction/city/DraynorManorPlugin.java b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorPlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/city/DraynorManorPlugin.java rename to Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorPlugin.java index 5ce18a965..27ba1d922 100644 --- a/Server/src/main/java/core/game/interaction/city/DraynorManorPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorPlugin.java @@ -1,10 +1,10 @@ -package core.game.interaction.city; +package content.region.misthalin.draynor.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; -import core.game.node.entity.skill.agility.AgilityHandler; +import core.game.dialogue.FacialExpression; +import core.game.global.action.ClimbActionHandler; +import core.game.global.action.DoorActionHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -13,7 +13,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.path.Pathfinder; 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/java/core/game/content/zone/DraynorMansionCourtyardZone.java b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorMansionCourtyardZone.java similarity index 96% rename from Server/src/main/java/core/game/content/zone/DraynorMansionCourtyardZone.java rename to Server/src/main/content/region/misthalin/draynor/handlers/DraynorMansionCourtyardZone.java index e75b30b8c..3a286e242 100644 --- a/Server/src/main/java/core/game/content/zone/DraynorMansionCourtyardZone.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorMansionCourtyardZone.java @@ -1,4 +1,4 @@ -package core.game.content.zone; +package content.region.misthalin.draynor.handlers; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/zone/DraynorMarketZone.java b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorMarketZone.java similarity index 95% rename from Server/src/main/java/core/game/content/zone/DraynorMarketZone.java rename to Server/src/main/content/region/misthalin/draynor/handlers/DraynorMarketZone.java index 8861f10c4..6e64da158 100644 --- a/Server/src/main/java/core/game/content/zone/DraynorMarketZone.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorMarketZone.java @@ -1,4 +1,4 @@ -package core.game.content.zone; +package content.region.misthalin.draynor.handlers; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/interaction/city/DraynorNodePlugin.java b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorNodePlugin.java similarity index 95% rename from Server/src/main/java/core/game/interaction/city/DraynorNodePlugin.java rename to Server/src/main/content/region/misthalin/draynor/handlers/DraynorNodePlugin.java index 5408317e9..474822f01 100644 --- a/Server/src/main/java/core/game/interaction/city/DraynorNodePlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorNodePlugin.java @@ -1,22 +1,22 @@ -package core.game.interaction.city; +package content.region.misthalin.draynor.handlers; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.scenery.SceneryBuilder; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; +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.system.task.Pulse; -import rs09.game.world.GameWorld; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/other/DraynorTreeNPC.java b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/npc/other/DraynorTreeNPC.java rename to Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java index f136479ef..2bc5f6852 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/DraynorTreeNPC.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.misthalin.draynor.handlers; import java.util.List; @@ -6,12 +6,16 @@ import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +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.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/java/core/game/content/dialogue/ManorFountainDialogue.java b/Server/src/main/content/region/misthalin/draynor/handlers/ManorFountainDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/ManorFountainDialogue.java rename to Server/src/main/content/region/misthalin/draynor/handlers/ManorFountainDialogue.java index 773004070..2ef389491 100644 --- a/Server/src/main/java/core/game/content/dialogue/ManorFountainDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/ManorFountainDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.misthalin.draynor.handlers; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.player.Player; import core.game.node.item.GroundItemManager; 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/java/core/game/content/quest/members/animalmagnetism/AliceDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/animalmagnetism/AliceDialogue.java rename to Server/src/main/content/region/misthalin/draynor/quest/anma/AliceDialogue.java index 296a6e3df..0d81ec43b 100644 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AliceDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.animalmagnetism; +package content.region.misthalin.draynor.quest.anma; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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 new file mode 100644 index 000000000..b584565ff --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AliceHusbandDialogue.java @@ -0,0 +1,427 @@ +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. + * @author Vexia + */ +public final class AliceHusbandDialogue extends DialoguePlugin { + + /** + * The quest. + */ + private Quest quest; + + /** + * Constructs a new {@code AliceHusbandDialogue} {@code Object}. + */ + public AliceHusbandDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code AliceHusbandDialogue} {@code Object}. + * @param player the player. + */ + public AliceHusbandDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new AliceHusbandDialogue(player); + } + + @Override + public void init() { + super.init(); + } + + @Override + public boolean open(Object... args) { + 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."); + break; + case 10: + player("Your animals don't look too healthy."); + break; + case 11: + npc("'Ave you talked to the wife for me?"); + break; + case 12: + player("Your wife says she needs the family cash and wants to", "know what you did with it."); + stage++; + break; + case 13: + npc("Any luck wiv me wife?"); + break; + case 14: + case 15: + npc("'Ave you talked to 'er?"); + break; + case 16: + case 17: + case 18: + player("I talked to your wife and thought that if you had a", "special amulet, you could speak to her and sort out the", "bank situation without me being involved."); + break; + case 19: + npc("Ahh, many thanks. Now what was it you were wanting", "again?"); + break; + default: + npc("Hello, how can I help you? I'm sellin' if you have ecto-", "tokens."); + break; + } + return true; + } + + @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) { + case 0: + options("Could I buy those chickens now, then?", "Your animals don't look too healthy.", "I'm okay, thank you.", "Where can I get these ecto-tokens?"); + stage++; + break; + case 1: + switch (buttonId) { + case 1: + player("Could I buy those chickens now, then?"); + stage = 10; + break; + case 2: + player("Your animals don't look too healthy."); + stage = 20; + break; + case 3: + player("I'm okay, thank you."); + stage = 30; + break; + case 4: + player("Where can I get these ecto-tokens?"); + stage = 40; + break; + } + break; + case 10: + npc("I can hand over a chicken if you give me 10 of them", "ecto-token thingies per bird."); + stage++; + break; + case 11: + options("Could I buy 1 chicken now?", "Could I buy 2 chickens now?", "Your animals don't look too healthy; I'll buy elsewhere."); + stage++; + break; + case 12: + switch (buttonId) { + case 1: + player("Could I buy 1 chicken now?"); + stage = 14; + break; + case 2: + player("Could I buy 2 chickens now?"); + stage = 15; + break; + case 3: + player("Your animals don't look too healthy; I'll buy elsewhere."); + stage++; + break; + } + break; + case 13: + end(); + break; + case 14: + case 15: + buy(stage == 14 ? 1 : 2); + stage = 16; + break; + case 16: + end(); + break; + case 20: + npc("It's that fountain thingy in the temple to the east. It's", "turned them all into zombies."); + stage++; + break; + case 21: + player("What use are zombie animals?"); + stage++; + break; + case 22: + npc("None at all, mate, except that those worshippers at that", "temple keep comin' and killin' em all for their bones.", "Don't ask me why."); + stage++; + break; + case 23: + player("But you're a ghost - surely you know", "something about it."); + stage++; + break; + case 24: + npc("I don't know nuthin' about nuthin'. Oim a simple ghost", "with simple needs. All I know is, years ago, that temple", "started glowing green and, a few months later, I woke", "up dead. That's all there is to it."); + stage++; + break; + case 25: + end(); + break; + case 30: + end(); + break; + case 40: + npc("The ghosts I talk to say that the tokens have something", "to do with the tower just east of here. If you need to", "collect some I'd try there."); + stage++; + break; + case 41: + end(); + break; + } + break; + case 0: + switch (stage) { + default: + end(); + break; + } + break; + case 10: + switch (stage) { + case 0: + npc("It's that fountain thingy in the temple to the east. It's", "turned them all into zombies."); + stage++; + break; + case 1: + player("What use are zombie animals?"); + stage++; + break; + case 2: + npc("None at all, mate, except that those worshippers at that", "temple keep comin' and killin' em all for their bones.", "Don't ask me why."); + stage++; + break; + case 3: + player("But you're a ghost - surely you know", "something about it."); + stage++; + break; + case 4: + npc("I don't know nuthin' about nuthin'. Oim a simple ghost", "with simple needs. All I know is, years ago, that temple", "started glowing green and, a few months later, I woke", "up dead. That's all there is to it."); + stage++; + break; + case 5: + npc("I do miss the wife though; tell 'er I still loves her."); + stage++; + break; + case 6: + player("Would I be able to buy some of your chickens?"); + stage++; + break; + case 7: + npc("Talk to my wife and I'll think about it."); + stage++; + break; + case 8: + if (quest.getStage(player) == 10) { + quest.setStage(player, 11); + } + end(); + break; + } + break; + case 11: + switch (stage) { + case 0: + player("Not yet; I've been distracted by the thought of undead", "cow milk."); + stage++; + break; + case 1: + end(); + break; + } + break; + case 12: + switch (stage) { + case 1: + npc("Tell 'er I spent it on cheap spirits, har har."); + stage++; + break; + case 2: + player("Your sense of humour died too, it seems..."); + stage++; + break; + case 3: + npc("Hah, just trying to lift your spirits."); + stage++; + break; + case 4: + player("I rest my case."); + stage++; + break; + case 5: + npc("Suit yerself, stick-in-the-mud. Anyway, Oim not one o'", "them yokels. Tell 'er I putted the cash in the bank like", "she always told me to."); + stage++; + break; + case 6: + npc("A warning to ya, too; annoy her and I'll haunt ya till", "yer hair turns white."); + stage++; + break; + case 7: + quest.setStage(player, 13); + end(); + break; + } + break; + case 13: + switch (stage) { + case 0: + player("Nothing new, no."); + stage++; + break; + case 1: + end(); + break; + } + break; + case 14: + switch (stage) { + case 0: + player("You may not believe me, but she wants me to find", "your bank pass now."); + stage++; + break; + case 1: + npc("Maybe she said that, maybe she didn't. I think you're", "just after me savings. Tell 'er that no one but a fool", "gives away their pass numbers."); + stage++; + break; + case 2: + npc("Go tell 'er now, if you're not a double-dealing' scammer,", "that is."); + stage++; + break; + case 3: + quest.setStage(player, 15); + end(); + break; + } + break; + case 15: + switch (stage) { + case 0: + player("Not since we last spoke."); + stage++; + break; + case 1: + end(); + break; + } + break; + case 16: + case 17: + case 18: + switch (stage) { + case 0: + npc("Arr, that makes far more sense than I was expecting", "from a muscle-head like you. My wife's a clever one."); + stage++; + break; + case 1: + if (player.getInventory().containsItem(AnimalMagnetism.CRONE_AMULET)) { + player("Well... oh, never mind. I'm desperate enough for those", "chickens to let that pass."); + stage += 2; + break; + } + player("Well...oh, never mind. I'm working on getting the", "amulet anyway."); + stage++; + break; + case 2: + end(); + break; + case 3: + npc("Give me that amulet, then, and we'll be seeing about", "your unnatural desire for chickens."); + stage++; + break; + case 4: + player("Okay, you need it more than I do, I suppose."); + stage++; + break; + case 5: + npc("Ta, mate."); + stage++; + break; + case 6: + player("Lucky we had such a brilliant idea."); + stage++; + break; + case 7: + if (player.getInventory().remove(AnimalMagnetism.CRONE_AMULET)) { + quest.setStage(player, 19); + end(); + } + break; + } + break; + case 19: + switch (stage) { + case 0: + player("I need a couple of your chickens."); + stage++; + break; + case 1: + npc("Chickens is tricksy, 'specially dead 'uns. I'll have to catch", "'em for ye."); + stage++; + break; + case 2: + player("They look preety pathetic, how hard can it be?"); + stage++; + break; + case 3: + npc("Stand back while I catches 'em, ya city slicker."); + stage++; + break; + case 4: + end(); + new AnmaCutscene(player).start(); + break; + } + break; + } + return true; + } + + /** + * Buys an undead chicken(s). + * @param amount the amount. + */ + private void buy(int amount) { + final Item tokens = new Item(4278, amount * 10); + if (!player.getInventory().containsItem(tokens)) { + npc("I'm not a charity here, ya know. Bad enough all you", "cow-killing folks are a'slaughterin' me beasts. Come back", "when ya have enough tokens."); + return; + } + if (player.getInventory().freeSlots() < amount) { + player("Sorry, I don't have enough inventory space."); + return; + } + if (player.getInventory().remove(tokens)) { + player.getInventory().add(new Item(10487, amount), player); + npc("Great! I'm laying away me tokens for some killer cows.", "That'll learn them bones rustlers."); + } + } + + @Override + public int[] getIds() { + return new int[] { 5202 }; + } + +} diff --git a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetism.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetism.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetism.java rename to Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetism.java index 12a39c6b2..a04a6c9de 100644 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetism.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetism.java @@ -1,16 +1,17 @@ -package core.game.content.quest.members.animalmagnetism; +package content.region.misthalin.draynor.quest.anma; 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 rs09.plugin.PluginManager; +import core.plugin.ClassScanner; -import core.game.content.quest.members.animalmagnetism.AnimalMagnetismPlugin.ContainerHandler; -import core.game.content.quest.members.animalmagnetism.AnimalMagnetismPlugin.HammerMagnetPlugin; -import core.game.content.quest.members.animalmagnetism.AnimalMagnetismPlugin.ResearchNoteHandler; +import content.region.misthalin.draynor.quest.anma.AnimalMagnetismPlugin.ContainerHandler; +import content.region.misthalin.draynor.quest.anma.AnimalMagnetismPlugin.HammerMagnetPlugin; +import content.region.misthalin.draynor.quest.anma.AnimalMagnetismPlugin.ResearchNoteHandler; import core.plugin.Initializable; -import core.game.content.quest.members.animalmagnetism.AnimalMagnetismPlugin.UndeadTreePlugin; +import content.region.misthalin.draynor.quest.anma.AnimalMagnetismPlugin.UndeadTreePlugin; +import content.data.Quests; /** * Handles the animal magnetism quest. @@ -18,12 +19,6 @@ import core.game.content.quest.members.animalmagnetism.AnimalMagnetismPlugin.Und */ @Initializable public final class AnimalMagnetism extends Quest { - - /** - * The name of this quest. - */ - public static String NAME = "Animal Magnetism"; - /** * The crone made amulet item. */ @@ -103,21 +98,20 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugin(new AvaDialogue()); - PluginManager.definePlugin(new AliceDialogue()); - PluginManager.definePlugin(new WitchDialogue()); - PluginManager.definePlugin(new ContainerHandler()); - PluginManager.definePlugin(new UndeadTreePlugin()); - PluginManager.definePlugin(new AvasDevicePlugin()); - PluginManager.definePlugin(new HammerMagnetPlugin()); - PluginManager.definePlugin(new ResearchNoteHandler()); - PluginManager.definePlugin(new AliceHusbandDialogue()); - PluginManager.definePlugin(new AnimalMagnetismPlugin()); + ClassScanner.definePlugin(new AvaDialogue()); + ClassScanner.definePlugin(new AliceDialogue()); + ClassScanner.definePlugin(new WitchDialogue()); + ClassScanner.definePlugin(new ContainerHandler()); + ClassScanner.definePlugin(new UndeadTreePlugin()); + ClassScanner.definePlugin(new HammerMagnetPlugin()); + ClassScanner.definePlugin(new ResearchNoteHandler()); + ClassScanner.definePlugin(new AliceHusbandDialogue()); + ClassScanner.definePlugin(new AnimalMagnetismPlugin()); return this; } @@ -220,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/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetismPlugin.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetismPlugin.java similarity index 87% rename from Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetismPlugin.java rename to Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetismPlugin.java index 83750c71f..6156fe9bd 100644 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AnimalMagnetismPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnimalMagnetismPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.animalmagnetism; +package content.region.misthalin.draynor.quest.anma; import java.util.HashMap; import java.util.Map; @@ -11,7 +11,7 @@ import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.gather.SkillingTool; +import content.data.skill.SkillingTool; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -19,11 +19,10 @@ 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; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.zone.MapZone; @@ -31,9 +30,12 @@ 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.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * Handles the animal magnetism plugin. * @author Vexia @@ -59,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; } @@ -72,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; @@ -116,33 +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) { - player.getInventory().replace(new Item(4252), item.getSlot()); - player.sendMessage("...and the world changes around you.", 4); - player.getTeleporter().send(Location.create(3658, 3517, 0), TeleportType.ECTOPHIAL); - 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 @@ -219,7 +188,7 @@ public final class AnimalMagnetismPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { NPCDefinition.forId(5208).getHandlers().put("option:chop", this); @@ -228,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) { @@ -273,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); @@ -323,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 new file mode 100644 index 000000000..da0a40472 --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AnmaCutscene.kt @@ -0,0 +1,204 @@ +package content.region.misthalin.draynor.quest.anma + +import core.api.* +import core.game.activity.Cutscene +import core.game.dialogue.FacialExpression +import core.game.node.entity.combat.ImpactHandler +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() { + setExit(player.location.transform(0,0,0)) //create copy of player's location + loadRegion(14391) + addNPC(FARMER, 36, 9, Direction.EAST, 0) + addNPC(CHICKEN, 44, 10, Direction.WEST, 0) + addNPC(ALICE, 43, 5, Direction.WEST) + addNPC(COWKILLER, 52, 8, Direction.WEST) + addNPC(COW, 37, 8, Direction.WEST) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(5) + } + 1 -> { + teleport(player, 46, 13) + face(player, getNPC(CHICKEN)!!.location) + face(getNPC(FARMER)!!, getNPC(CHICKEN)!!) + move(getNPC(FARMER)!!, 43, 10) + timedUpdate(5) + } + 2 -> { + fadeFromBlack() + moveCamera(48,16, 500) + rotateCamera(44, 0) + dialogueUpdate(FARMER, FacialExpression.NEUTRAL, "Here, chicky chicky!") + } + 3 -> { + move(getNPC(FARMER)!!, 44, 10) + animate(getNPC(FARMER)!!, ATTEMPT_CATCH) + move(getNPC(CHICKEN)!!, 45, 9) + timedUpdate(2) + } + 4 -> { + move(getNPC(FARMER)!!, 53, 8) + move(getNPC(CHICKEN)!!, 53, 8) + timedUpdate(10) + } + 5 -> { + move(getNPC(CHICKEN)!!, 47, 8) + timedUpdate(1) + } + 6 -> { + move(getNPC(FARMER)!!, 48, 8) + timedUpdate(8) + } + 7 -> { + move(getNPC(CHICKEN)!!, 45, 10) + animate(getNPC(FARMER)!!, ATTEMPT_CATCH) + timedUpdate(1) + } + 8 -> { + move(getNPC(FARMER)!!, 45, 10) + move(getNPC(CHICKEN)!!, 38, 10) + animate(getNPC(CHICKEN)!!, CHICKEN_JUMP) + timedUpdate(1) + } + 9 -> { + move(getNPC(FARMER)!!, 38, 10) + timedUpdate(9) + } + 10 -> { + face(player, getNPC(FARMER)!!.location) + dialogueUpdate(FARMER, FacialExpression.NEUTRAL, "Git 'ere yer pesky bird!") + } + 11 -> { + move(getNPC(FARMER)!!, 44, 10) + move(getNPC(CHICKEN)!!, 45, 10) + timedUpdate(8) + } + 12 -> { + animate(getNPC(FARMER)!!, ATTEMPT_CATCH) + animate(getNPC(CHICKEN)!!, CHICKEN_JUMP) + face(player, getNPC(CHICKEN)!!.location) + move(getNPC(CHICKEN)!!, 47, 7) + timedUpdate(3) + } + 13 -> { + move(getNPC(FARMER)!!, 52, 8) + timedUpdate(6) + } + 14 -> { + face(getNPC(CHICKEN)!!, getNPC(CHICKEN)!!.location.transform(0, 1, 0)) + timedUpdate(6) + } + 15 -> { + move(getNPC(CHICKEN)!!, 47, 11) + face(getNPC(COW)!!, getNPC(COW)!!.location.transform(Direction.EAST)) + timedUpdate(8) + } + 16 -> { + face(getNPC(CHICKEN)!!, getNPC(FARMER)!!.location) + teleport(getNPC(COW)!!, 41, 10) + dialogueUpdate(FARMER, FacialExpression.NEUTRAL, "Where'd she go?") + } + 17 -> { + setAttribute(getNPC(COWKILLER)!!, "1hko", true) + move(getNPC(COWKILLER)!!, 46, 9) + move(getNPC(COW)!!, 46, 10) + move(getNPC(FARMER)!!, 44, 10) + move(getNPC(ALICE)!!, 48, 8) + timedUpdate(11) + } + 18 -> { + face(getNPC(FARMER)!!, player.location) + face(player, getNPC(FARMER)!!.location) + move(getNPC(CHICKEN)!!, 45, 9) + dialogueUpdate(FARMER, FacialExpression.ANNOYED, "Git oof my laaaaaand!") + } + 19 -> { + face(getNPC(FARMER)!!, getNPC(ALICE)!!.location) + face(getNPC(ALICE)!!, getNPC(COWKILLER)!!.location) + dialogueUpdate(ALICE, FacialExpression.ANNOYED, "You heard my husband: leave now!") + } + 20 -> { + face(getNPC(ALICE)!!, getNPC(COWKILLER)!!.location) + face(getNPC(COWKILLER)!!, getNPC(ALICE)!!.location) + dialogueUpdate(COWKILLER, FacialExpression.HALF_THINKING, "Always the same, I can never get these animals to myself.") + } + 21 -> { + face(getNPC(COWKILLER)!!, getNPC(COW)!!.location) + animate(getNPC(COWKILLER)!!, ATTACK_ANIM) + impact(getNPC(COW)!!, 8, ImpactHandler.HitsplatType.NORMAL) + timedUpdate(3) + } + 22 -> { + dialogueUpdate(ALICE, FacialExpression.SAD, "You killed Bessie!") + } + 23 -> { + dialogueUpdate(COWKILLER, FacialExpression.FRIENDLY, "Buying cowhides and feathers - ahh, that chicken is next, feathers for me!") + } + 24 -> { + move(getNPC(COWKILLER)!!, 46, 10) + timedUpdate(3) + } + 25 -> { + animate(getNPC(COWKILLER)!!, Animations.HUMAN_BURYING_BONES_827) + timedUpdate(1) + } + 26 -> { + face(getNPC(COWKILLER)!!, getNPC(COWKILLER)!!.location.transform(-1,0,0)) + timedUpdate(2) + } + 27 -> { + face(getNPC(CHICKEN)!!, getNPC(CHICKEN)!!.location.transform(0,1,0)) + sendChat(getNPC(CHICKEN)!!, "Woo woo!") + timedUpdate(2) + } + 28 -> { + move(getNPC(CHICKEN)!!, 45, 10) + timedUpdate(2) + } + 29 -> { + animate(getNPC(COWKILLER)!!, ATTACK_ANIM) + animate(getNPC(CHICKEN)!!, CHICKEN_JUMP) + impact(getNPC(CHICKEN)!!, 0, ImpactHandler.HitsplatType.MISS) + timedUpdate(2) + } + 30 -> { + face(getNPC(CHICKEN)!!, getNPC(FARMER)!!) + face(getNPC(FARMER)!!, getNPC(CHICKEN)!!) + visualize(getNPC(FARMER)!!, GRAB_CHICKEN_ANIM, GRAB_CHICKEN_GFX) + timedUpdate(6) + } + 31 -> { + getNPC(CHICKEN)!!.clear() + playerDialogueUpdate(FacialExpression.FRIENDLY, "Well, that's one way to catch a chicken, I suppose.") + } + 32 -> { + end { + setQuestStage(player, Quests.ANIMAL_MAGNETISM, 20) + } + } + } + } + + companion object { + val FARMER = NPCs.ALICES_HUSBAND_5205 + val ALICE = NPCs.ALICE_5212 + val CHICKEN = NPCs.UNDEAD_CHICKEN_1692 + val COWKILLER = NPCs.COW1337KILLR_5210 + val COW = NPCs.UNDEAD_COW_5211 + val ATTEMPT_CATCH = 5377 + val ATTACK_ANIM = 2066 + val GRAB_CHICKEN_ANIM = 5376 + val GRAB_CHICKEN_GFX = 973 + val CHICKEN_JUMP = 5380 + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvaDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/AvaDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvaDialogue.java rename to Server/src/main/content/region/misthalin/draynor/quest/anma/AvaDialogue.java index a5023701d..bae10eb6d 100644 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvaDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AvaDialogue.java @@ -1,16 +1,19 @@ -package core.game.content.quest.members.animalmagnetism; +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.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; + +import static core.api.ContentAPIKt.*; /** * Handles the ava npc dialogue. @@ -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 new file mode 100644 index 000000000..9607a29ca --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/AvasDevice.kt @@ -0,0 +1,119 @@ +package content.region.misthalin.draynor.quest.anma + +import core.api.* +import core.game.event.EventHook +import core.game.event.TickEvent +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import org.rs09.consts.Items +import core.api.Event +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 + * @source https://runescape.wiki/w/Ava%27s_accumulator?oldid=2097350 + */ +class AvasDevice : InteractionListener, EventHook { + override fun defineListeners() { + onEquip(devices) { player, _ -> + if (!isQuestComplete(player, Quests.ANIMAL_MAGNETISM)) { + sendMessage(player, "You need to complete Animal Magnetism to equip this.") + return@onEquip false + } + + if (attractEnabled(player)) + player.hook(Event.Tick, this) + + setAttribute(player, LAST_TICK, getWorldTicks()) //set this on equip so can't be spam-re-equipped to spawn infinite items. + return@onEquip true + } + onUnequip(devices) { player, _ -> + if (attractEnabled(player)) + player.unhook(this) + return@onUnequip true + } + on(devices, IntType.ITEM, "operate") { player, _ -> + val attract = !attractEnabled(player) + setAttribute(player, ATTRACT_ENABLED, attract) + sendMessage( + player, + colorize( + "Ava's device will ${if (attract) "now" else "no longer"} randomly collect loot for you.", + "990000" + ) + ) + if (attract) { + player.hook(Event.Tick, this) + } else { + player.unhook(this) + } + return@on true + } + } + + override fun process(entity: Entity, event: TickEvent) { + if (entity !is Player) { + entity.unhook(this) + return + } + + if (getWorldTicks() - getLastTick(entity) < attractDelay) + return + else + setAttribute(entity, LAST_TICK, getWorldTicks()) + + if (isInterfered(entity)) { + sendMessage(entity, "Your armour interferes with Ava's device.") + return + } + + val wornId = getItemFromEquipment(entity, EquipmentSlot.CAPE)?.id ?: -1 + + val reward = when (wornId) { + Items.AVAS_ACCUMULATOR_10499 -> ACCUMULATOR_REWARDS + Items.AVAS_ATTRACTOR_10498 -> ATTRACTOR_REWARDS + else -> { + entity.unhook(this) + return + } + }.random() + + if (equipSlot(reward) == EquipmentSlot.AMMO) { + val equippedId = getItemFromEquipment(entity, EquipmentSlot.AMMO)?.id ?: -1 + if (reward == equippedId || equippedId == -1) { + entity.equipment.add(reward.asItem(), true, false) + return + } + } + + addItemOrDrop(entity, reward) + } + + private fun attractEnabled(entity: Entity) : Boolean { + return getAttribute(entity, ATTRACT_ENABLED, true) //defaults to enabled + } + + private fun getLastTick(entity: Entity) : Int { + return getAttribute(entity, LAST_TICK, 0) + } + + private fun isInterfered(player: Player) : Boolean { + val chestPiece = getItemFromEquipment(player, EquipmentSlot.CHEST) + val modelId = chestPiece?.definition?.maleWornModelId1 ?: -1 + return modelId != -1 && modelId in metalBodies + } + + companion object { + const val ATTRACT_ENABLED = "/save:avadevice:attract" + const val LAST_TICK = "avadevice:tick" + val devices = intArrayOf(Items.AVAS_ACCUMULATOR_10499, Items.AVAS_ATTRACTOR_10498) + val metalBodies = intArrayOf(301, 306, 3379) + val attractDelay = secondsToTicks(180) + val ATTRACTOR_REWARDS = arrayOf(Items.IRON_BAR_2351, Items.IRON_KNIFE_863,Items.IRON_DART_807,Items.IRON_DAGGER_1203,Items.IRON_BOLTS_9140,Items.IRON_ARROW_884,Items.IRON_ORE_440,Items.COPPER_ORE_436,Items.IRON_FULL_HELM_1153,Items.IRON_2H_SWORD_1309,Items.STEEL_BAR_2353) + val ACCUMULATOR_REWARDS = arrayOf(Items.STEEL_BAR_2353,Items.STEEL_2H_SWORD_1311,Items.STEEL_KNIFE_865,Items.STEEL_DAGGER_1207,Items.STEEL_MED_HELM_1141,Items.STEEL_DART_808,Items.STEEL_BOLTS_9141,Items.STEEL_ARROW_886,Items.IRON_BAR_2351) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/OldCronDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/animalmagnetism/OldCronDialogue.java rename to Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java index 741330b0a..eb76716f4 100644 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/OldCronDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/OldCronDialogue.java @@ -1,12 +1,13 @@ -package core.game.content.quest.members.animalmagnetism; +package content.region.misthalin.draynor.quest.anma; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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.Item; -import core.game.content.quest.free.therestlessghost.RestlessGhost; +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/java/core/game/content/quest/members/animalmagnetism/WitchDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/anma/WitchDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/animalmagnetism/WitchDialogue.java rename to Server/src/main/content/region/misthalin/draynor/quest/anma/WitchDialogue.java index cd893002f..7c23e06c0 100644 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/WitchDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/anma/WitchDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.animalmagnetism; +package content.region.misthalin.draynor.quest.anma; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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/java/core/game/content/dialogue/ErnestDialogue.java b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestDialogue.java similarity index 83% rename from Server/src/main/java/core/game/content/dialogue/ErnestDialogue.java rename to Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestDialogue.java index 459875009..8fbf7d11f 100644 --- a/Server/src/main/java/core/game/content/dialogue/ErnestDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestDialogue.java @@ -1,8 +1,11 @@ -package core.game.content.dialogue; +package content.region.misthalin.draynor.quest.ernest; +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; +import content.data.Quests; /** * Represents the dialogue which handles the interaction with ernest. @@ -71,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/java/core/game/content/quest/free/ErnestTheChicken.java b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChicken.java similarity index 91% rename from Server/src/main/java/core/game/content/quest/free/ErnestTheChicken.java rename to Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChicken.java index d9eb30bc5..58a9fecef 100644 --- a/Server/src/main/java/core/game/content/quest/free/ErnestTheChicken.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChicken.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free; +package content.region.misthalin.draynor.quest.ernest; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; @@ -7,7 +7,8 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.world.map.Location; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the ernest the chicken quest. @@ -40,12 +41,12 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugins(new ErnestNPC(), new ErnestChickenNPC()); + ClassScanner.definePlugins(new ErnestNPC(), new ErnestChickenNPC()); return this; } @@ -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/java/core/game/interaction/object/ErnestTheChickenPlugin.java b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChickenPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/ErnestTheChickenPlugin.java rename to Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChickenPlugin.java index 63d6eb349..fb009f9c8 100644 --- a/Server/src/main/java/core/game/interaction/object/ErnestTheChickenPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChickenPlugin.java @@ -1,11 +1,11 @@ -package core.game.interaction.object; +package content.region.misthalin.draynor.quest.ernest; import java.awt.Point; import java.util.Arrays; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.DoorActionHandler; -import core.game.node.entity.skill.agility.AgilityHandler; +import core.game.global.action.DoorActionHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; @@ -13,7 +13,7 @@ import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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; @@ -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/java/core/game/content/quest/free/VampireSlayer.java b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayer.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/free/VampireSlayer.java rename to Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayer.java index 72104e3f0..94e7f13cb 100644 --- a/Server/src/main/java/core/game/content/quest/free/VampireSlayer.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayer.java @@ -1,9 +1,10 @@ -package core.game.content.quest.free; +package content.region.misthalin.draynor.quest.vampire; 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/java/core/game/node/entity/npc/other/VampireSlayerNPC.java b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerNPC.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/other/VampireSlayerNPC.java rename to Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerNPC.java index cc4f30b36..17c72b2f2 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/VampireSlayerNPC.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.misthalin.draynor.quest.vampire; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; @@ -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); @@ -155,7 +156,7 @@ public class VampireSlayerNPC extends AbstractNPC { } @Override - public boolean isAttackable(final Entity entity, final CombatStyle style) { + public boolean isAttackable(final Entity entity, final CombatStyle style, boolean message) { final Player player = ((Player) entity); final Player pl = getAttribute("player", null); return pl == null ? false : pl == player ? true : false; diff --git a/Server/src/main/java/core/game/interaction/object/VampireSlayerPlugin.java b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerPlugin.java similarity index 94% rename from Server/src/main/java/core/game/interaction/object/VampireSlayerPlugin.java rename to Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerPlugin.java index e9fe41fb3..f8f582536 100644 --- a/Server/src/main/java/core/game/interaction/object/VampireSlayerPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/vampire/VampireSlayerPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.misthalin.draynor.quest.vampire; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; @@ -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/kotlin/rs09/game/content/dialogue/BrotherBordissDialogue.kt b/Server/src/main/content/region/misthalin/edgeville/dialogue/BrotherBordissDialogue.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/content/dialogue/BrotherBordissDialogue.kt rename to Server/src/main/content/region/misthalin/edgeville/dialogue/BrotherBordissDialogue.kt index 4e15312f4..1a14231ff 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/BrotherBordissDialogue.kt +++ b/Server/src/main/content/region/misthalin/edgeville/dialogue/BrotherBordissDialogue.kt @@ -1,8 +1,8 @@ -package rs09.game.content.dialogue +package content.region.misthalin.edgeville.dialogue import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/java/core/game/content/dialogue/EdgevilleMonkDialogue.java b/Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleMonkDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/EdgevilleMonkDialogue.java rename to Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleMonkDialogue.java index 3e6cf3538..665ab07b2 100644 --- a/Server/src/main/java/core/game/content/dialogue/EdgevilleMonkDialogue.java +++ b/Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleMonkDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.edgeville.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/dialogue/EdgevilleShopKeeperDialogue.java b/Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleShopKeeperDialogue.java similarity index 77% rename from Server/src/main/java/core/game/content/dialogue/EdgevilleShopKeeperDialogue.java rename to Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleShopKeeperDialogue.java index 613ba47e0..94927f4a2 100644 --- a/Server/src/main/java/core/game/content/dialogue/EdgevilleShopKeeperDialogue.java +++ b/Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleShopKeeperDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.edgeville.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; @@ -37,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; } @@ -56,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/java/core/game/content/dialogue/HariDialogue.java b/Server/src/main/content/region/misthalin/edgeville/dialogue/HariDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/HariDialogue.java rename to Server/src/main/content/region/misthalin/edgeville/dialogue/HariDialogue.java index f278a17ec..be922970d 100644 --- a/Server/src/main/java/core/game/content/dialogue/HariDialogue.java +++ b/Server/src/main/content/region/misthalin/edgeville/dialogue/HariDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.edgeville.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; @@ -102,7 +104,7 @@ public final class HariDialogue extends DialoguePlugin { } break; case 18: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "When you have done that you can shape the log further", "with your hatchet to make a canoe."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "When you have done that you can shape the log further", "with your axe to make a canoe."); stage = 19; break; case 19: diff --git a/Server/src/main/java/core/game/interaction/city/EdgevilleNodePlugin.java b/Server/src/main/content/region/misthalin/edgeville/handlers/EdgevilleNodePlugin.java similarity index 75% rename from Server/src/main/java/core/game/interaction/city/EdgevilleNodePlugin.java rename to Server/src/main/content/region/misthalin/edgeville/handlers/EdgevilleNodePlugin.java index 532c83e23..34264e29a 100644 --- a/Server/src/main/java/core/game/interaction/city/EdgevilleNodePlugin.java +++ b/Server/src/main/content/region/misthalin/edgeville/handlers/EdgevilleNodePlugin.java @@ -1,10 +1,10 @@ -package core.game.interaction.city; +package content.region.misthalin.edgeville.handlers; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +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; @@ -45,51 +45,47 @@ public final class EdgevilleNodePlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - int id = ((Scenery) node).getId(); - switch (id) { + switch (node.getId()) { case 9262: case 9261: case 30806: - ContentAPI.sendMessage(player, "There doesn't seem to be any seeds on this rosebush."); + sendMessage(player, "There doesn't seem to be any seeds on this rosebush."); break; case 12265: ClimbActionHandler.climb(player, null, Location.create(3078, 3493, 0)); 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) if (option.equalsIgnoreCase("open")) { player.animate(Animation.create(536)); - ContentAPI.sendMessage(player, "The trapdoor opens..."); + sendMessage(player, "The trapdoor opens..."); SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(26934), 500); } break; case 26934: // Edgeville Dungeon trapdoor (when open) if (option.equalsIgnoreCase("close")) { player.animate(Animation.create(535)); - ContentAPI.sendMessage(player, "You close the trapdoor."); + sendMessage(player, "You close the trapdoor."); SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(26933)); } else if (option.equalsIgnoreCase("climb-down")) { - ContentAPI.sendMessage(player, "You climb down through the trapdoor..."); + sendMessage(player, "You climb down through the trapdoor..."); ClimbActionHandler.climbLadder(player, (Scenery) node, option); } break; case 29319: case 29320: // Edgeville Dungeon wilderness entrance if (option.equalsIgnoreCase("open") && player.getLocation().getY() < 9918) { - Location endLocation = new Location(player.getLocation().getX(),player.getLocation().getY()+1); - player.getInterfaceManager().open(new Component(382)); + player.getInterfaceManager().openComponent(382); player.setAttribute("wildy_gate", node); - player.setAttribute("wildy_gate_loc",endLocation); } else{ // Leaving the wilderness - Location endLocation = new Location(player.getLocation().getX(),player.getLocation().getY()-1); - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node,endLocation); + DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); } } return true; diff --git a/Server/src/main/java/core/game/content/dialogue/BarfyBill.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BarfyBill.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/BarfyBill.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/BarfyBill.java index 7420dbcb3..1207ee094 100644 --- a/Server/src/main/java/core/game/content/dialogue/BarfyBill.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BarfyBill.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.misthalin.lumbridge.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; @@ -66,7 +67,7 @@ public final class BarfyBill extends DialoguePlugin { } break; case 24: - npc("Then take your hatchet to it and shape it how you like!"); + npc("Then take your axe to it and shape it how you like!"); stage = 26; break; case 26: diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt new file mode 100644 index 000000000..c759ef0b2 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.kt @@ -0,0 +1,266 @@ +package content.region.misthalin.lumbridge.dialogue + +import content.data.RepairItem +import content.global.handlers.item.equipment.BarrowsEquipment +import core.cache.def.impl.ItemDefinition +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.item.Item +import core.plugin.Initializable + +/** + * Represents the dialogue plugin used for the bob npc who repairs items. + * @author 'Vexia + * @author Damighty - Kotlin conversion + * @version 1.0 + */ +@Initializable +class BobDialogue(player: Player? = null) : DialoguePlugin(player) { + + /** + * Represents the item id being repaired. + */ + private var itemId = 0 + + /** + * Represents the item being repaired. + */ + private lateinit var item: Item + + /** + * Represents the item repairing. + */ + private var repairItem: RepairItem? = null + + /** + * The achievement diary. + */ + private val level = 1 + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 754 -> { + options("Yes, please.", "No, thank you.") + stage = 755 + } + 755 -> when (buttonId) { + 1 -> { + player("Yes, please.") + stage = 757 + } + 2 -> { + player("No, thank you.") + stage = 756 + } + } + 756 -> end() + 757 -> { + end() + val repairItemDef = RepairItem.forId(itemId) + + if (repairItemDef != null) { + // Standard repairable items + if (!player.inventory.contains(995, repairItemDef.cost)) { + player.packetDispatch.sendMessage("You don't have enough to pay him.") + return true + } + if (player.inventory.remove(Item(itemId, 1))) { + player.inventory.remove(Item(995, repairItemDef.cost)) + player.inventory.add(repairItemDef.product) + val costText = if (repairItemDef.cost > 0) "${repairItemDef.cost}gp" else "free" + player.packetDispatch.sendMessage("Bob fixes your ${item.name.lowercase().replace("broken", "").trim()} for $costText.") + } + } else { + // Barrows items + val barrowsDef = BarrowsEquipment.getDefinition(itemId) ?: return true + val repairCost = BarrowsEquipment.getRepairCost(item) + + if (!player.inventory.contains(995, repairCost)) { + player.packetDispatch.sendMessage("You don't have enough to pay him.") + return true + } + if (player.inventory.remove(Item(itemId, 1))) { + player.inventory.remove(Item(995, repairCost)) + player.inventory.add(Item(barrowsDef.repairedId)) + val costText = if (repairCost > 0) "${repairCost}gp" else "free" + player.packetDispatch.sendMessage("Bob fixes your ${barrowsDef.itemName.lowercase()} for $costText.") + } + } + return true + } + 678 -> end() + 0 -> when (buttonId) { + 1 -> { + player("Give me a quest!") + stage = -5 + } + 2 -> { + player("Have you anything to sell?") + stage = 10 + } + 3 -> { + player("Can you repair my items for me?") + stage = 20 + } + 4 -> { + player("I'd like to talk about Achievement Diaries.") + stage = 30 + } + } + -5 -> { + interpreter.sendDialogues(npc, FacialExpression.FURIOUS, "Get yer own!") + stage = -4 + } + -4 -> end() + 10 -> { + npc("Yes! I buy and sell axes! Take your pick (or axe)!") + stage = 11 + } + 11 -> { + end() + npc.openShop(player) + } + 20 -> { + npc("Of course I'll repair it, though the materials may cost", "you. Just hand me the item and I'll have a look.") + stage = 21 + } + 21 -> end() + 30 -> { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.LUMBRIDGE, level)) { + player("I've done all the medium tasks in my Lumbridge", "Achievement Diary.") + stage = 150 + } else if (AchievementDiary.canReplaceReward(player, DiaryType.LUMBRIDGE, level)) { + player("I've seemed to have lost my explorer's ring...") + stage = 160 + } else { + options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later.") + stage = 31 + } + } + 31 -> when (buttonId) { + 1 -> { + player("What is the Achievement Diary?") + stage = 110 + } + 2 -> { + player("What are the rewards?") + stage = 120 + } + 3 -> { + player("How do I claim the rewards?") + stage = 130 + } + 4 -> { + player("See you later!") + stage = 140 + } + } + 110 -> { + npc("Ah, well, it's a diary that helps you keep track of", "particular achievements you've made in the world of", "Gielinor. In Lumbridge and Draynor I can help you", "discover some very useful things indeed.") + stage++ + } + 111 -> { + npc("Eventually with enough exploration you will be", "rewarded for your explorative efforts.") + stage++ + } + 112 -> { + npc("You can access your Achievement Diary by going to", "the Quest Journal. When you've opened the Quest", "Journal click on the green star icon on the top right", "hand corner. This will open the diary.") + stage = 30 + } + 120 -> { + npc("Ah, well there are different rewards for each", "Achievement Diary. For completing the Lumbridge and", "Draynor diary you are presented with an explorer's", "ring.") + stage++ + } + 121 -> { + npc("This ring will become increasingly useful with each", "section of the diary that you complete.") + stage = 30 + } + 130 -> { + npc("You need to complete the tasks so that they're all ticked", "off, then you can claim your reward. Most of them are", "straightforward although you might find some required", "quests to be started, if not finished.") + stage++ + } + 131 -> { + npc("To claim the explorer's ring speak to Explorer Jack", " in Lumbridge, Ned in Draynor Village or myself.") + stage = 30 + } + 140 -> end() + 150 -> { + npc("Yes I see that, you'll be wanting your", "reward then I assume?") + stage++ + } + 151 -> { + player("Yes please.") + stage++ + } + 152 -> { + AchievementDiary.flagRewarded(player, DiaryType.LUMBRIDGE, level) + npc("This ring is a representation of the adventures you", "went on to complete your tasks.") + stage++ + } + 153 -> { + player("Wow, thanks!") + stage = 30 + } + 160 -> { + AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level) + npc("You better be more careful this time.") + stage = -1 + } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return BobDialogue(player) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + var repair = false + var wrong = false + + if (npc.id == 3797 && args.size == 1) { + player("Can you repair my items for me?") + stage = 20 + return true + } + + if (args.size == 1) { + options("Give me a quest!", "Have you anything to sell?", "Can you repair my items for me?", "Talk about Achievement Diaries") + stage = 0 + return true + } + + if (args.size > 1) repair = args[1] as Boolean + if (args.size > 2) wrong = args[2] as Boolean + if (args.size > 3) { + repairItem = RepairItem.forId(args[3] as Int) + itemId = args[3] as Int + } + if (args.size > 4) item = args[4] as Item + + if (repair && !wrong) { + val cost = RepairItem.forId(itemId)?.cost ?: BarrowsEquipment.getRepairCost(item) + val costText = if (cost > 0) "${cost}gp" else "free" + npc("Quite badly damaged, but easy to repair. Would you", "like me to repair it for $costText?") + stage = 754 + return true + } + + if (repair && wrong) { + npc("Sorry friend, but I can't do anything with that.") + stage = 678 + return true + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(519, 3797) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/CandleSellerDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/CandleSellerDialogue.java new file mode 100644 index 000000000..f2497754b --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/CandleSellerDialogue.java @@ -0,0 +1,231 @@ +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.node.item.Item; + +/** + * Represents the dialogue plugin used for the candle seller. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class CandleSellerDialogue extends DialoguePlugin { + + /** + * Represents the coins item. + */ + private static final Item COINS = new Item(995, 1000); + + /** + * Represents the candle item. + */ + private static final Item CANDLE = new Item(33, 1); + + /** + * Constructs a new {@code CandleSellerPlugin} {@code Object}. + */ + public CandleSellerDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code CandleSellerPlugin} {@code Object}. + * @param player the player. + */ + public CandleSellerDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new CandleSellerDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Do you want a lit candle for 1000 gold?"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "Yes please.", "One thousand gold?!", "No thanks, I'd rather curse the darkness."); + stage = 1; + break; + case 800: + if (player.getInventory().freeSlots() == 0) { + end(); + player.getPacketDispatch().sendMessage("You don't have enough inventory space to buy a candle."); + break; + } + if (!player.getInventory().contains(995, 1000)) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't seem to have enough coins."); + stage = 30; + break; + } + if (player.getInventory().contains(995, 1000)) { + player.getInventory().remove(COINS); + player.getInventory().add(new Item(33, 1)); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Here you go then."); + stage = 400; + } + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please."); + stage = 800; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "One thousand gold?!"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "No thanks, I'd rather curse the darkness."); + stage = 30; + break; + } + break; + case 20: + 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.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.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: + interpreter.sendOptions("Select an Option", "All right, you win, I'll buy a candle.", "No way.", "How do you make lanterns?"); + stage = 240; + break; + case 30: + end(); + break; + case 240: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "All right, you win, I'll buy a candle."); + stage = 350; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ANNOYED, "No way."); + stage = 30; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "How do you make lanterns?"); + stage = 230; + break; + } + break; + case 230: + 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.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.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.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.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.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.HAPPY, "But if all that's to complicated, you can buy a candle", "right here for 1000 gold!"); + stage = 237; + break; + case 237: + interpreter.sendOptions("Select an Option", "All right, you win, I'll buy a candle.", "No thanks, I'd rather curse the darkness."); + stage = 290; + break; + case 350: + if (player.getInventory().freeSlots() == 0) { + end(); + player.getPacketDispatch().sendMessage("You don't have enough inventory space to buy a candle."); + break; + } + if (!player.getInventory().contains(995, 1000)) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't seem to have enough coins."); + stage = 30; + break; + } + + if (player.getInventory().remove(COINS)) { + player.getInventory().add(CANDLE); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Here you go then."); + stage = 400; + } + break; + case 400: + 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.FRIENDLY, "Okay, thanks."); + stage = 402; + break; + case 402: + end(); + break; + case 290: + switch (buttonId) { + case 1: + if (player.getInventory().freeSlots() == 0) { + end(); + player.getPacketDispatch().sendMessage("You don't have enough inventory space to buy a candle."); + break; + } + if (!player.getInventory().contains(995, 1000)) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't seem to have enough coins."); + stage = 30; + break; + } + if (player.getInventory().remove(COINS)) { + player.getInventory().add(CANDLE); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Here you go then."); + stage = 400; + } + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "No thanks, I'd rather curse the darkness."); + stage = 291; + break; + } + break; + case 291: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 1834 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/CookingTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/CookingTutorDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/CookingTutorDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/CookingTutorDialogue.java index 2e5568425..c7c2f7197 100644 --- a/Server/src/main/java/core/game/content/dialogue/CookingTutorDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/CookingTutorDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/CraftingTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/CraftingTutorDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/CraftingTutorDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/CraftingTutorDialogue.java index f7fee7485..d51457996 100644 --- a/Server/src/main/java/core/game/content/dialogue/CraftingTutorDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/CraftingTutorDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; 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/java/core/game/content/dialogue/DoomsayerDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DoomsayerDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/DoomsayerDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/DoomsayerDialogue.java index 69b53d9c0..76f0161af 100644 --- a/Server/src/main/java/core/game/content/dialogue/DoomsayerDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DoomsayerDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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.link.diary.DiaryType; import core.plugin.Initializable; diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/DukeHoracioDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DukeHoracioDialogue.kt new file mode 100644 index 000000000..da9ead1cc --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/DukeHoracioDialogue.kt @@ -0,0 +1,87 @@ +package content.region.misthalin.lumbridge.dialogue + +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import content.region.misthalin.varrock.quest.dragonslayer.DragonSlayer +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import content.region.misthalin.varrock.quest.dragonslayer.DukeHoracioDSDialogue +import content.region.misthalin.lumbridge.quest.runemysteries.DukeHoracioRMDialogue +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) : DialoguePlugin(player) { + + override fun getIds(): IntArray { + return intArrayOf(741) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + 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(Quests.THE_LOST_TRIBE) && player.questRepository.getQuest(Quests.THE_LOST_TRIBE).isStarted(player)) { + addOption("Lost Tribe", DukeHoracioTLTDialogue(player.questRepository.getStage(Quests.THE_LOST_TRIBE))) + } + if (!sendChoices()) { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings. Welcome to my castle.") + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + + DIALOGUE_INITIAL_OPTIONS_HANDLE -> { + npc("Greetings. Welcome to my castle.") + loadFile(optionFiles[buttonId - 1]) + } + + 0 -> { + interpreter.sendOptions("Select an Option", "Have you any quests for me?", "Where can I find money?") + stage = 1 + } + 1 -> when (buttonId) { + 1 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Have any quests for me?") + stage = 20 + } + 2 -> { + interpreter.sendDialogues( + npc, + 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." + ) + stage = END_DIALOGUE + } + } + 20 -> { + npc("Let me see...") + if (!player.questRepository.isComplete(Quests.RUNE_MYSTERIES)) { + loadFile(DukeHoracioRMDialogue(player.questRepository.getStage(Quests.RUNE_MYSTERIES))) + } else { + stage++ + } + } + 21 -> { + npc("Nope, I've got everything under control", "in the castle at the moment.") + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return DukeHoracioDialogue(player) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/ExplorerJackDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/ExplorerJackDialogue.java similarity index 75% rename from Server/src/main/java/core/game/content/dialogue/ExplorerJackDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/ExplorerJackDialogue.java index 6cc533d82..52339c8e2 100644 --- a/Server/src/main/java/core/game/content/dialogue/ExplorerJackDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/ExplorerJackDialogue.java @@ -1,11 +1,11 @@ -package core.game.content.dialogue; +package content.region.misthalin.lumbridge.dialogue; +import core.game.dialogue.DialoguePlugin; 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.emote.Emotes; import core.plugin.Initializable; -import core.game.node.item.Item; /** * Handles the explorer jack dialogue. @@ -18,7 +18,6 @@ public class ExplorerJackDialogue extends DialoguePlugin { /** * The achievement diary. */ - private AchievementDiary diary; private final int level = 0; /** @@ -52,17 +51,14 @@ public class ExplorerJackDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE); - } switch (stage) { case -1: - if (diary.isComplete(level, true) && !diary.isLevelRewarded(level)) { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.LUMBRIDGE, level)) { player("I've done all the beginner tasks in my Lumbridge", "Achievement Diary."); stage = 50; break; } - if (diary.isLevelRewarded(level) && diary.isComplete(level, true) && !player.hasItem(diary.getType().getRewards(level)[0])) { + if (AchievementDiary.canReplaceReward(player, DiaryType.LUMBRIDGE, level)) { player("I've seemed to have lost my explorer's ring..."); stage = 60; break; @@ -91,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: @@ -99,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: @@ -127,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: @@ -146,10 +142,7 @@ public class ExplorerJackDialogue extends DialoguePlugin { stage++; break; case 52: - diary.setLevelRewarded(level); - for (Item i : diary.getType().getRewards(level)) { - player.getInventory().add(i, player); - } + AchievementDiary.flagRewarded(player, DiaryType.LUMBRIDGE, level); player.getEmoteManager().unlock(Emotes.EXPLORE); npc("This ring is a representation of the adventures you", "went on to complete your tasks."); stage ++; @@ -159,7 +152,7 @@ public class ExplorerJackDialogue extends DialoguePlugin { stage = -1; break; case 60: - player.getInventory().add(diary.getType().getRewards(level)[0], player); + AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level); npc("You better be more careful this time."); stage = -1; break; diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FishingTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FishingTutorDialogue.java new file mode 100644 index 000000000..5f0cb3a81 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FishingTutorDialogue.java @@ -0,0 +1,161 @@ +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 fishing tutor dialogue plugin. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class FishingTutorDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code FishingTutorDialogue} {@code Object}. + */ + public FishingTutorDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code FishingTutorDialogue} {@code Object}. + * @param player the player. + */ + public FishingTutorDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new FishingTutorDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendOptions("Select an Option", "Can you teach me the basics of fishing please?", "Tell me about different fish.", "Where and what should I fish?", "Goodbye."); + stage = 1; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Can you teach me the basics of fishing please?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Tell me about different fish."); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Where and what should I fish?"); + stage = 30; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Goodbye."); + stage = 40; + break; + } + break; + case 10: + 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.THINKING, "I see.. is that it?"); + stage = 12; + break; + case 12: + 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.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.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: + interpreter.sendOptions("Select an Option", "Can you teach me the basics of fishing please?", "Tell me about different fish.", "Where and what should I fish?", "Goodbye."); + stage = 1; + break; + case 20: + interpreter.sendOptions("Select an Option", "Small Net Fish", "Big Net Fish", "Rod and Fly Fishing", "Tell me about..."); + stage = 21; + break; + case 21: + switch (buttonId) { + case 1: + interpreter.sendItemMessage(303, "Ahoy, small net fishin' you can do just south of Draynor Village and in these very spots here. Aye."); + stage = 210; + break; + case 2: + interpreter.sendItemMessage(305, "Aye, you can net yourself some big fish in Catherby, which is a good place to fish for most things, Gar!"); + stage = 220; + break; + case 3: + interpreter.sendItemMessage(307, "Aye, rod fishin' can be practiced here at these spots, as well as south of Draynor Village and in the Lumbridge river, depending upon your experience. You can get bait at any fishin' shop, there be one in Port Sarim."); + stage = 230; + break; + case 4: + interpreter.sendOptions("Select an Option", "Can you teach me the basics of fishing please?", "Tell me about different fish.", "Where and what should I fish?", "Goodbye."); + stage = 1; + break; + } + break; + case 210: + interpreter.sendItemMessage(315, "Shrimps and anchovies can be caught with your small fishin' net"); + stage = 20; + break; + case 220: + interpreter.sendItemMessage(355, "Mackerel and Cod will form the backbone of your catch when big net fishin'.. except for the added extras..."); + stage = 221; + break; + case 221: + interpreter.sendDialogues(npc, null, "Some rich rewards for big net fishin', make sure you be", "using a big net fishing spot though..."); + stage = 20; + break; + case 230: + interpreter.sendItemMessage(351, "With a rod you can catch pike, sardines and herring. Good eating on them."); + stage = 231; + break; + case 231: + interpreter.sendItemMessage(309, "The art of fly fishin' can be done in rivers, so the Lumbridge river here would suffice."); + stage = 232; + break; + case 232: + interpreter.sendItemMessage(329, "Aye, you can catch yourself a delicious trout or salmon."); + stage = 20; + break; + case 30: + 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: + interpreter.sendOptions("Select an Option", "Can you teach me the basics of fishing please?", "Tell me about different fish.", "Where and what should I fish?", "Goodbye."); + stage = 1; + break; + case 40: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 4901 }; + } +} 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/java/core/game/content/dialogue/GeeDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/GeeDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/GeeDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/GeeDialogue.java index 74dc447e1..5fdc5bcfb 100644 --- a/Server/src/main/java/core/game/content/dialogue/GeeDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/GeeDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/HansDialoguePlugin.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/HansDialoguePlugin.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/HansDialoguePlugin.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/HansDialoguePlugin.java index 2b2010c97..97610d36f 100644 --- a/Server/src/main/java/core/game/content/dialogue/HansDialoguePlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/HansDialoguePlugin.java @@ -1,12 +1,14 @@ -package core.game.content.dialogue; +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.link.IronmanMode; import core.game.world.map.zone.ZoneBorders; import core.plugin.Initializable; import core.game.node.entity.player.Player; -import static rs09.tools.DialogueConstKt.END_DIALOGUE; +import static core.tools.DialogueConstKt.END_DIALOGUE; /** @@ -62,7 +64,7 @@ public final class HansDialoguePlugin extends DialoguePlugin { switch (stage) { case 0: - interpreter.sendOptions("Select an Option", "I'm looking for whoever is in charge of this place.", "I have come to kill everyone in this castle!", "I don't know. I'm lost. Where am I?", "More Options..."); + interpreter.sendOptions("Select an Option", "I'm looking for whoever is in charge of this place.", "I have come to kill everyone in this castle!", "I don't know. I'm lost. Where am I?", "Have you been here as long as me?"); stage++; break; case 1: @@ -83,12 +85,8 @@ public final class HansDialoguePlugin extends DialoguePlugin { stage = 50; break; case 4: - if (!inStartDungeon) { - interpreter.sendOptions("Select an Option", "Have you been here as long as me?", "About my xp rate...", "About Iron Man mode...","About random events...", "Go Back..."); - } else { - interpreter.sendOptions("Select an Option", "Have you been here as long as me?", "About my xp rate...", "About Iron Man mode...","About random events..."); - } - stage = 10; + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "I've been patrolling this castle for years!"); + stage = 41; break; } break; @@ -100,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: @@ -125,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++; @@ -137,7 +135,11 @@ public final class HansDialoguePlugin extends DialoguePlugin { case 12: switch(buttonId){ case 1: - options("1.0x","2.5x","Stay 5.0x"); + if(player.getAttributes().containsKey("permadeath")){ + options("1.0x", "2.5x", "Stay 5.0x", "(HCIM Only) 10x"); + } else { + options("1.0x", "2.5x", "Stay 5.0x"); + } stage++; break; case 2: @@ -150,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; @@ -159,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; @@ -169,11 +171,19 @@ public final class HansDialoguePlugin extends DialoguePlugin { playerl(FacialExpression.FRIENDLY, "I'd rather stay 5x, thank you."); stage = END_DIALOGUE; return true; + case 4: + if (player.newPlayer) { + player.getSkills().experienceMultiplier = 10.0; + stage = 14; + } else { + stage = 15; + } + break; } 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: @@ -325,6 +335,9 @@ public final class HansDialoguePlugin extends DialoguePlugin { interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"I have changed your Iron Man mode to: ","" + (buttonId == 1 ? "Standard" : "Hardcore" + " Ironman mode.")); player.getSettings().toggleAcceptAid(); player.getIronmanManager().setMode(IronmanMode.values()[buttonId]); + if(buttonId == 2) { + player.setAttribute("/save:permadeath",true); + } player.sendMessage("Your Iron Man status has been changed."); stage = 50; break; @@ -363,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/java/core/game/content/dialogue/HarlanDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/HarlanDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/HarlanDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/HarlanDialogue.java index f14365e00..d1dd299e3 100644 --- a/Server/src/main/java/core/game/content/dialogue/HarlanDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/HarlanDialogue.java @@ -1,7 +1,9 @@ -package core.game.content.dialogue; +package content.region.misthalin.lumbridge.dialogue; import core.game.container.Container; -import core.game.content.global.Skillcape; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +import core.game.global.Skillcape; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/JimmyTheChiselDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/JimmyTheChiselDialogue.kt new file mode 100644 index 000000000..be0088503 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/JimmyTheChiselDialogue.kt @@ -0,0 +1,38 @@ +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 org.rs09.consts.NPCs + +/** + * @author qmqz + * todo char quest description osrs and rs3 doesn't match, but there isn't any for rs3 yet + */ + +@Initializable +class JimmyTheChiselDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Hello mate!").also { stage = 99 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return JimmyTheChiselDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.JIMMY_THE_CHISEL_1718) + } +} diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeGuideDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeGuideDialogue.kt new file mode 100644 index 000000000..d01bdf614 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeGuideDialogue.kt @@ -0,0 +1,128 @@ +package content.region.misthalin.lumbridge.dialogue + +import core.api.isQuestComplete +import core.api.sendGraphics +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.game.node.entity.player.link.diary.DiaryType +import core.game.world.map.zone.impl.ModeratorZone +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 content.data.Quests + +@Initializable +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, Quests.SHEEP_SHEARER) + val cooksAssistantComplete = isQuestComplete(player, Quests.COOKS_ASSISTANT) + + when (stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "Greetings, adventurer. I am Phileas, the Lumbridge Guide. I am here to give information and directions to new players. Do you require any help?").also { stage++ } + 1 -> showTopics( + Topic("Where can I find a quest to go on?", 10), + Topic("What monsters should I fight?", 20), + Topic("Where can I make money?", 30), + Topic("I'd like to know more about security.", 40), + IfTopic("Where can I find a bank?", 50, !staff && !ironman), + IfTopic("More Options...", 100, staff || ironman, skipPlayer = true), + ) + + //More Options... + 100 -> showTopics( + Topic("Where can I find a bank?", 50), + IfTopic("I would like to access the P-Mod room.", 200, staff), + IfTopic("I would like to de-iron.", 300, ironman), + Topic("Go back...", 1, skipPlayer = true) + ) + + //Where can I find a quest? + 10 -> { + if (!cooksAssistantComplete) + 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(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(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(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(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(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(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(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(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(FacialExpression.FRIENDLY, "You'll find a bank upstairs in Lumbridge Castle - go right to the top!").also { stage = 1 } + + //visit pmod room + 200 -> npcl(FacialExpression.FRIENDLY, "Yes, of course.").also { stage++ } + 201 -> { + end() + if (player.isStaff) + ModeratorZone.teleport(player) + } + + //deiron + 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(FacialExpression.FRIENDLY, "Yes, I'm sure.", 310), + Topic(FacialExpression.FRIENDLY, "No, I've changed my mind.", END_DIALOGUE) + ) + + //yes - deiron + 310 -> npcl(FacialExpression.FRIENDLY, "Very well, let me just check one thing...").also { stage++ } + 311 -> if (player.ironmanManager.mode == IronmanMode.HARDCORE) { + 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(FacialExpression.FRIENDLY, "Oh, wonderful. It appears everything is in order. Sit still for a moment...").also { stage++ } + } + 312 -> { + sendGraphics(342, player.location) + sendDialogue("------------------", "The wise old wizard casts a strange spell.", "------------------") + stage++ + } + 313 -> { + player.ironmanManager.mode = IronmanMode.NONE + npcl(FacialExpression.HALF_ASKING, "There, I believe it is done. You should no longer be restricted from the wider world.") + stage = END_DIALOGUE + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LUMBRIDGE_GUIDE_2244) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/LumbridgeJailGuard.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeJailGuard.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/LumbridgeJailGuard.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeJailGuard.java index 8f1060749..4e6ae256d 100644 --- a/Server/src/main/java/core/game/content/dialogue/LumbridgeJailGuard.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeJailGuard.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; 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/java/core/game/content/dialogue/LumbridgeSwampArcher.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampArcher.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/LumbridgeSwampArcher.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampArcher.java index 6b22afdc3..cdc54f615 100644 --- a/Server/src/main/java/core/game/content/dialogue/LumbridgeSwampArcher.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampArcher.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/LumbridgeSwampMonk.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampMonk.java similarity index 89% rename from Server/src/main/java/core/game/content/dialogue/LumbridgeSwampMonk.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampMonk.java index 1867cb940..5ad40d929 100644 --- a/Server/src/main/java/core/game/content/dialogue/LumbridgeSwampMonk.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampMonk.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/LumbridgeSwampWizard.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampWizard.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/LumbridgeSwampWizard.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampWizard.java index 4de3879aa..aea5dc02d 100644 --- a/Server/src/main/java/core/game/content/dialogue/LumbridgeSwampWizard.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeSwampWizard.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/MagicTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/MagicTutorDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/dialogue/MagicTutorDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/MagicTutorDialogue.java index ed19f0d0f..db1e45435 100644 --- a/Server/src/main/java/core/game/content/dialogue/MagicTutorDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/MagicTutorDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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.item.GroundItem; diff --git a/Server/src/main/java/core/game/content/dialogue/MiningtutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/MiningtutorDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/MiningtutorDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/MiningtutorDialogue.java index 8f6415694..73f483494 100644 --- a/Server/src/main/java/core/game/content/dialogue/MiningtutorDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/MiningtutorDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/PrayerTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/PrayerTutorDialogue.java new file mode 100644 index 000000000..687a6b889 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/PrayerTutorDialogue.java @@ -0,0 +1,119 @@ +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; + +/** + * Handles the PrayerTutorDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class PrayerTutorDialogue extends DialoguePlugin { + + public PrayerTutorDialogue() { + + } + + public PrayerTutorDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new PrayerTutorDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendOptions("Select an Option", "I already know about the basic prayers, got any tips?", "Tell me about different bones.", "Goodbye."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + switch (buttonId) { + case 1: + 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_ASKING, "Tell me about different bones."); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Goodbye."); + stage = 30; + break; + } + break; + case 10: + 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.HAPPY, "useful prayers."); + stage = 12; + break; + case 12: + 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.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: + interpreter.sendOptions("Select an Option", "I already know about the basic prayers, got any tips?", "Tell me about different bones.", "Goodbye."); + stage = 0; + break; + case 20: + interpreter.sendOptions("Select an Option", "Basic Bones", "Big Bones", "Babydragon Bones", "Goodbye."); + stage = 21; + break; + case 21: + switch (buttonId) { + case 1: + interpreter.sendItemMessage(526, "Basic bones are left by many creatures such as goblins, monkeys and that sort of thing. They won't get you much when you burry them, but if you do it every time you come across them, it all adds up!"); + stage = 20; + break; + case 2: + interpreter.sendItemMessage(532, "Big bones you can get by killing things like ogres and giants, them being big things and all. They're quite a good boost for your prayer if you are up to fighting the big boys."); + stage = 220; + break; + case 3: + interpreter.sendItemMessage(536, "If you're feeling adventurous and up to tackling baby dragons, you can get these Baby Dragon bones which are even better than big bones."); + stage = 20; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Goodbye."); + stage = 40; + break; + } + break; + case 220: + interpreter.sendItemMessage(532, "You can probably find them in caves and such dark dank places."); + stage = 20; + break; + case 40: + end(); + break; + case 30: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 4903 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/RangedTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/RangedTutorDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/dialogue/RangedTutorDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/RangedTutorDialogue.java index 95175dc15..8e322cc48 100644 --- a/Server/src/main/java/core/game/content/dialogue/RangedTutorDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/RangedTutorDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/SethGroatsDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/SethGroatsDialogue.java similarity index 85% rename from Server/src/main/java/core/game/content/dialogue/SethGroatsDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/SethGroatsDialogue.java index 2606158d1..3bbb3be36 100644 --- a/Server/src/main/java/core/game/content/dialogue/SethGroatsDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/SethGroatsDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/SigmundDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/SigmundDialogue.java similarity index 78% rename from Server/src/main/java/core/game/content/dialogue/SigmundDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/SigmundDialogue.java index 238aab13c..e4e521668 100644 --- a/Server/src/main/java/core/game/content/dialogue/SigmundDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/SigmundDialogue.java @@ -1,10 +1,13 @@ -package core.game.content.dialogue; +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; -import static rs09.tools.DialogueConstKt.END_DIALOGUE; +import static core.tools.DialogueConstKt.END_DIALOGUE; +import content.data.Quests; /** @@ -33,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; @@ -78,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; @@ -105,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/java/core/game/content/dialogue/WoodsmanTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/WoodsmanTutorDialogue.java similarity index 98% rename from Server/src/main/java/core/game/content/dialogue/WoodsmanTutorDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/WoodsmanTutorDialogue.java index ae0cc228b..03ad3d400 100644 --- a/Server/src/main/java/core/game/content/dialogue/WoodsmanTutorDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/WoodsmanTutorDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.lumbridge.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt b/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt new file mode 100644 index 000000000..965dcb157 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt @@ -0,0 +1,437 @@ +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.getQuestStage +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.world.map.zone.ZoneBorders +import org.rs09.consts.Components +import org.rs09.consts.Items +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) { + + companion object { + private val DEAD_TREES = arrayOf( + Scenery.DEAD_TREE_1282, + Scenery.DEAD_TREE_1286, + Scenery.DEAD_TREE_1365 + ) + + private val ZOMBIES = arrayOf( + NPCs.ZOMBIE_73, + NPCs.ZOMBIE_74 + ) + + private val CASTLE_ROOF_AREA = ZoneBorders(3207, 3215, 3210, 3222, 3) + private val CASTLE_COURTYARD_AREA = ZoneBorders(3226, 3229, 3217, 3208) + private val COW_PEN_AREA_1 = ZoneBorders(3253, 3255, 3265, 3297) + private val COW_PEN_AREA_2 = ZoneBorders(3245, 3278, 3253, 3298) + private val WIZARDS_TOWER_TOP_FLOOR_AREA = ZoneBorders(3103, 3155, 3115, 3165, 2) + + object BeginnerTasks { + const val CASTLE_CLIMB_TO_HIGHEST_POINT = 0 + const val CASTLE_RAISE_FLAG_ON_ROOF = 1 + const val CASTLE_SPEAK_TO_DUKE_HORACIO = 2 + const val SPEAK_TO_DOOMSAYER = 3 + const val AL_KHARID_PASS_THROUGH_GATE = 4 + const val CHAMPIONS_GUILD_MINE_CLAY = 5 + const val BARBARIAN_VILLAGE_MAKE_SOFT_CLAY = 6 + const val BARBARIAN_VILLAGE_FIRE_A_POT = 7 + const val DRAYNOR_ENTER_SPOOKY_MANSION_COURTYARD = 8 + const val DRAYNOR_VISIT_MARKET = 9 + const val DRAYNOR_TALK_TO_TOWNCRIER_ABOUT_RULES = 10 + const val WIZARDS_TOWER_CLIMB_TO_TOP = 11 + const val SWAMP_MINE_COPPER_ORE = 12 + const val SWAMP_CATCH_SHRIMPS = 13 + const val SWAMP_FISHING_TUTOR_GET_A_JOB = 14 + const val BROWSE_FATHER_AERECK_GRAVES = 15 + const val CHURCH_PLAY_ORGAN = 16 + const val LUMBRIDGE_GUIDE_TALK_ABOUT_SOS = 17 + const val BROWSE_GENERAL_STORE = 18 + const val VISIT_FRED_THE_FARMER = 19 + const val WINDMILL_MAKE_FLOUR = 20 + } + + object EasyTasks { + const val AL_KHARID_MINE_IRON = 0 + const val COWFIELD_OBTAIN_COW_HIDE = 1 + const val AL_KHARID_TAN_COW_HIDE = 2 + const val CRAFT_LEATHER_GLOVES = 3 + const val RIVER_CATCH_PIKE = 4 + const val SMELT_STEEL_BAR = 5 + const val SWAMP_SEARCH_SHED = 6 + const val SWAMP_KILL_GIANT_RAT = 7 + const val SWAMP_CUT_DEAD_TREE = 8 + const val SWAMP_LIGHT_NORMAL_LOGS = 9 + const val SWAMP_COOK_RAT_MEAT_ON_CAMPFIRE = 10 + const val SWAMP_WATER_ALTAR_CRAFT_RUNE = 11 + const val SWAMP_REPLACE_GHOSTSPEAK_AMULET = 12 + const val WIZARDS_TOWER_TAUNT_DEMON = 13 + const val WIZARDS_TOWER_TELEPORT_ESSENCE_MINE = 14 + const val DRAYNOR_ACCESS_BANK = 15 + const val DRAYNOR_WISEOLDMAN_CHECK_JUNK = 16 + const val DRAYNOR_WISEOLDMAN_PEEK_TELESCOPE = 17 + const val DRAYNOR_JAIL_SEWER_KILL_ZOMBIE = 18 + } + + object MediumTasks { + const val DRAYNOR_JAIL_SEWER_SMITH_STEEL_LONGSWORD = 0 + const val RIDE_GNOMECOPTER = 1 + const val CAST_LUMBRIDGE_TELEPORT = 2 + const val CASTLE_LIGHT_WILLOW_LOGS = 3 + const val CASTLE_COOK_LOBSTER_ON_RANGE = 4 + const val CASTLE_OBTAIN_ANTIDRAGON_SHIELD = 5 + const val RIVER_GATHER_WILLOW_LOGS = 6 + const val SMELT_SILVER_BAR = 7 + const val CRAFT_HOLY_SYMBOL = 8 + const val RIVER_CATCH_SALMON = 9 + const val AL_KHARID_MINE_SILVER = 10 + const val SWAMP_MINE_COAL = 11 + } + } + + override val areaTasks + get() = arrayOf( + AreaDiaryTask( + CASTLE_ROOF_AREA, + DiaryLevel.BEGINNER, + BeginnerTasks.CASTLE_CLIMB_TO_HIGHEST_POINT + ), + + AreaDiaryTask( + WIZARDS_TOWER_TOP_FLOOR_AREA, + DiaryLevel.BEGINNER, + BeginnerTasks.WIZARDS_TOWER_CLIMB_TO_TOP + ) + ) + + override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { + when (player.viewport.region.id) { + 12596 -> if (event.itemId == Items.CLAY_434) { + finishTask( + player, + DiaryLevel.BEGINNER, + BeginnerTasks.CHAMPIONS_GUILD_MINE_CLAY + ) + } + + 12593, 12849 -> { + when (event.itemId) { + Items.RAW_SHRIMPS_317 -> { + finishTask( + player, + DiaryLevel.BEGINNER, + BeginnerTasks.SWAMP_CATCH_SHRIMPS + ) + } + + Items.COPPER_ORE_436 -> { + finishTask( + player, + DiaryLevel.BEGINNER, + BeginnerTasks.SWAMP_MINE_COPPER_ORE + ) + } + + Items.COAL_453 -> { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.SWAMP_MINE_COAL + ) + } + + Items.LOGS_1511 -> { + if (event.source.id in DEAD_TREES) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.SWAMP_CUT_DEAD_TREE + ) + } + } + + Items.COOKED_MEAT_2142 -> { + if (event.original == Items.RAW_RAT_MEAT_2134) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.SWAMP_COOK_RAT_MEAT_ON_CAMPFIRE + ) + } + } + } + } + + 12850 -> when (event.itemId) { + Items.WILLOW_LOGS_1519 -> { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.RIVER_GATHER_WILLOW_LOGS + ) + } + + Items.RAW_PIKE_349 -> { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.RIVER_CATCH_PIKE + ) + } + + Items.RAW_SALMON_331 -> { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.RIVER_CATCH_SALMON + ) + } + + Items.LOBSTER_379 -> { + if (event.original == Items.RAW_LOBSTER_377 + && event.source.id == Scenery.COOKING_RANGE_114 + ) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CASTLE_COOK_LOBSTER_ON_RANGE + ) + } + } + + Items.UNSTRUNG_SYMBOL_1714 -> { + if (event.original == Items.SILVER_BAR_2355 + && event.source.id == Scenery.FURNACE_36956 + ) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CRAFT_HOLY_SYMBOL + ) + } + } + } + + 13107 -> when (event.itemId) { + Items.IRON_ORE_440 -> { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.AL_KHARID_MINE_IRON + ) + } + + Items.SILVER_ORE_442 -> { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.AL_KHARID_MINE_SILVER + ) + } + } + } + } + + override fun onNpcKilled(player: Player, event: NPCKillEvent) { + when (player.viewport.region.id) { + 12593, 12849 -> if (event.npc.id == NPCs.GIANT_RAT_86) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.SWAMP_KILL_GIANT_RAT + ) + } + + 12438, 12439 -> if (event.npc.id in ZOMBIES) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.DRAYNOR_JAIL_SEWER_KILL_ZOMBIE + ) + } + } + } + + override fun onTeleported(player: Player, event: TeleportEvent) { + when (event.source) { + is NPC -> if (event.method == TeleportMethod.NPC && event.source.id == NPCs.SEDRIDOR_300) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.WIZARDS_TOWER_TELEPORT_ESSENCE_MINE + ) + } + } + } + + override fun onFireLit(player: Player, event: LitFireEvent) { + when (player.viewport.region.id) { + 12593, 12849 -> if (event.logId == Items.LOGS_1511) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.SWAMP_LIGHT_NORMAL_LOGS + ) + } + + 12850 -> if (event.logId == Items.WILLOW_LOGS_1519) { + if (inBorders(player, CASTLE_COURTYARD_AREA)) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CASTLE_LIGHT_WILLOW_LOGS + ) + } + } + } + } + + override fun onInteracted(player: Player, event: InteractionEvent) { + when (player.viewport.region.id) { + 12337 -> if (event.target.id == Scenery.RAILING_37668 + && event.option == "taunt-through" + ) { + finishTask( + player, + DiaryLevel.EASY, + 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 + ) + } + } + } + + override fun onAttributeRemoved(player: Player, event: AttributeRemoveEvent) { + when (event.attribute) { + "gc:flying" -> { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.RIDE_GNOMECOPTER + ) + } + } + } + + override fun onDialogueOpened(player: Player, event: DialogueOpenEvent) { + if (event.dialogue is DukeHoracioDialogue) { + finishTask( + player, + DiaryLevel.BEGINNER, + BeginnerTasks.CASTLE_SPEAK_TO_DUKE_HORACIO + ) + } + } + + override fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) { + when (event.dialogue) { + is DukeHoracioDSDialogue -> { + val dragonSlayerStage = getQuestStage(player, Quests.DRAGON_SLAYER) + + if ((dragonSlayerStage == 100 && event.currentStage == 4) + || event.currentStage == 12) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CASTLE_OBTAIN_ANTIDRAGON_SHIELD + ) + } + } + } + } + + + override fun onPickedUp(player: Player, event: PickUpEvent) { + when (player.viewport.region.id) { + 12850, 12851 -> { + if (event.itemId == Items.COWHIDE_1739) { + if (inBorders(player, COW_PEN_AREA_1) + || inBorders(player, COW_PEN_AREA_2) + ) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.COWFIELD_OBTAIN_COW_HIDE + ) + } + } + } + } + } + + override fun onInterfaceOpened(player: Player, event: InterfaceOpenEvent) { + when (player.viewport.region.id) { + 12338 -> if (event.component.id == Components.BANK_V2_MAIN_762) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.DRAYNOR_ACCESS_BANK + ) + } + + 12850 -> if (event.component.id == Components.SHOP_TEMPLATE_620) { + finishTask( + player, + DiaryLevel.BEGINNER, + BeginnerTasks.BROWSE_GENERAL_STORE + ) + } + } + } + + override fun onSpellCast(player: Player, event: SpellCastEvent) { + if (event.spellBook == SpellBookManager.SpellBook.MODERN && event.spellId == Modern.LUMBRIDGE_TELEPORT) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CAST_LUMBRIDGE_TELEPORT + ) + } + } + + override fun onJobAssigned(player: Player, event: JobAssignmentEvent) { + when (player.viewport.region.id) { + 12849 -> if (event.employerNpc.id == NPCs.FISHING_TUTOR_4901) { + finishTask( + player, + DiaryLevel.BEGINNER, + BeginnerTasks.SWAMP_FISHING_TUTOR_GET_A_JOB + ) + } + } + } + + 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 new file mode 100644 index 000000000..5e52e236c --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/diary/NedDiaryDialogue.kt @@ -0,0 +1,124 @@ +package content.region.misthalin.lumbridge.diary + +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType +import core.game.dialogue.DialogueFile + +class NedDiaryDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + val level = 2 + when(stage) { + 0 -> { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.LUMBRIDGE, level)) { + player("I've done all the medium tasks in my Lumbridge", "Achievement Diary.") + stage = 50 + } + else if (AchievementDiary.canReplaceReward(player, DiaryType.LUMBRIDGE, level)) { + player("I've seemed to have lost my explorer's ring...") + stage = 60 + } + else { + options( + "What is the Achievement Diary?", + "What are the rewards?", + "How do I claim the rewards?", + "See you later." + ) + stage = 1 + } + } + 1 -> when (buttonID) { + 1 -> { + player("What is the Achievement Diary?") + stage = 10 + } + 2 -> { + player("What are the rewards?") + stage = 20 + } + 3 -> { + player("How do I claim the rewards?") + stage = 30 + } + 4 -> { + player("See you later!") + stage = 40 + } + } + 10 -> { + 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++ + } + 11 -> { + npc("Eventually with enough exploration you will be", "rewarded for your explorative efforts.") + stage++ + } + 12 -> { + 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 = 0 + } + 20 -> { + 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++ + } + 21 -> { + npc("This ring will become increasingly useful with each", "section of the diary that you complete.") + stage = 0 + } + 30 -> { + npc( + "You need to complete the task 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++ + } + 31 -> { + npc( + "To claim the explorer's ring speak to Explorer Jack in ", + "Lumbridge, Bob in Bob's Axes in Lumbridge, or myself." + ) + stage = 0 + } + 40 -> end() + 50 -> { + npc("Yes I see that, you'll be wanting your", "reward then I assume?") + stage++ + } + 51 -> { + player("Yes please.") + stage++ + } + 52 -> { + AchievementDiary.flagRewarded(player, DiaryType.LUMBRIDGE, level) + npc("This ring is a representation of the adventures you", "went on to complete your tasks.") + stage++ + } + 53 -> { + player("Wow, thanks!") + stage = 0 + } + 60 -> { + AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level) + npc("You better be more careful this time.") + stage = 0 + } + } + } +} 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 new file mode 100644 index 000000000..ed6d21643 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/ChurchSignListener.kt @@ -0,0 +1,26 @@ +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 + +class ChurchSignListener : InteractionListener { + + val CHURCH_SIGN = Scenery.SIGNPOST_31299 + + 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 $servername and been sent to their respawn location. Be careful out there.") + } else { + 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 + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/CowFieldSignListener.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/CowFieldSignListener.kt new file mode 100644 index 000000000..fbad9dc8b --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/CowFieldSignListener.kt @@ -0,0 +1,28 @@ +package content.region.misthalin.lumbridge.handlers + +import core.api.* +import org.rs09.consts.Scenery +import core.GlobalStats +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * @author bushtail + */ + +class CowFieldSignListener : InteractionListener { + + val SIGN = Scenery.SIGNPOST_31297 + + override fun defineListeners() { + on(SIGN, IntType.SCENERY, "read") { player, _ -> + val COW_DEATHS = GlobalStats.getDailyCowDeaths() + if(COW_DEATHS > 0) { + sendDialogue(player, "Local cowherders have reported that $COW_DEATHS cows have been slain in this field today by passing adventurers. Farmers throughout the land fear this may be an epidemic.") + } else { + sendDialogue(player,"The Lumbridge cow population has been thriving today, without a single cow death to worry about!") + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/city/lumbridge/CowPenZone.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/CowPenZone.java similarity index 93% rename from Server/src/main/java/core/game/interaction/city/lumbridge/CowPenZone.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/CowPenZone.java index 6c14c1eea..ffccf94f0 100644 --- a/Server/src/main/java/core/game/interaction/city/lumbridge/CowPenZone.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/CowPenZone.java @@ -1,4 +1,4 @@ -package core.game.interaction.city.lumbridge; +package content.region.misthalin.lumbridge.handlers; import core.game.node.entity.Entity; import core.game.node.entity.npc.NPC; @@ -8,7 +8,7 @@ import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneBuilder; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.GlobalStats; +import core.GlobalStats; /** * Zone for the lumbridge cow pen diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinoChestListener.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinoChestListener.kt new file mode 100644 index 000000000..9b5aefc13 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinoChestListener.kt @@ -0,0 +1,29 @@ +package content.region.misthalin.lumbridge.handlers + +import core.api.getUsedOption +import core.api.openBankAccount +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Handles the culino chest options. + * @author Ceikry + */ +class CulinoChestListener : InteractionListener { + companion object { + private const val CULINO_CHEST = Scenery.CHEST_12309 + } + + override fun defineListeners() { + on(CULINO_CHEST, IntType.SCENERY, "buy-items", "buy-food"){ player, _ -> + CulinomancerShop.openShop(player, food = getUsedOption(player).lowercase() == "buy-food") + return@on true + } + + on(CULINO_CHEST, IntType.SCENERY, "bank"){ player, _ -> + openBankAccount(player) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinomancerShop.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinomancerShop.kt new file mode 100644 index 000000000..e82bcce41 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinomancerShop.kt @@ -0,0 +1,144 @@ +package content.region.misthalin.lumbridge.handlers + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.system.task.Pulse +import org.rs09.consts.Items +import core.game.shops.Shop +import core.game.shops.ShopItem +import core.game.world.GameWorld +import java.lang.Integer.min +import kotlin.collections.HashMap + +class CulinomancerShop : LoginListener { + //Enable the chest if the player has 18 quest points or more + override fun login(player: Player) { + if(player.questRepository.points >= 18){ + 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.) + val restockPulse = object : Pulse(100){ //Run once a minute + override fun pulse(): Boolean { + getShop(player, false).restock() + getShop(player, true).restock() + return false + } + } + GameWorld.Pulser.submit(restockPulse) + //Stop the pulse if the player logs out (easy way to avoid a player having multiple restock pulses by relogging a bunch) + //Stopped pulses are cleared from the list on the next tick cycle + player.logoutListeners["culino-restock"] = {restockPulse.stop()} + } + } + + companion object { + // Our shop mappings - shops are individualized due to the differing items based on player's QP. + // Maps player UID -> shop + private val foodShops = HashMap() + private val gearShops = HashMap() + + //Open methods for the shops - should check player's QP and whether they already have a container generated + @JvmStatic + fun openShop(player: Player, food: Boolean) { + getShop(player, food).openFor(player) + } + + //Retrieve a player's shop - should generate the shop if it does not exist. + fun getShop(player: Player, food: Boolean): Shop { + val uid = player.details.uid + val points = player.questRepository.points + val tier = (points / 18) + if (tier != getAttribute(player, "culino-tier", 0)) //If player tier has changed + { + foodShops.remove(uid) //Clear the previous shops, so they can regenerate with the new tier + gearShops.remove(uid) + } + return if (food) { + val shop = foodShops[uid] ?: Shop("Culinomancer's Chest Tier $tier", generateFoodStock(points), false) + foodShops[uid] = shop + shop + } else { + val shop = gearShops[uid] ?: Shop("Culinomancer's Chest Tier $tier", generateGearStock(points), false) + gearShops[uid] = shop + shop + } + } + + //Generate default food stock based on an amount of total QP. + private fun generateFoodStock(points: Int): Array { + val stock = Array(foodStock.size) { ShopItem(0, 0) } + val maxQty = when (val qpTier = (points / 18) - 1) { + 0, 1, 2, 3, 4 -> 1 + qpTier + else -> qpTier + (qpTier + (qpTier - 5)) //5 = 10, 6 = 13, 7 = 15, etc + } + for ((index, item) in foodStock.withIndex()) { + stock[index].itemId = item.id + stock[index].amount = if (item.id == Items.PIZZA_BASE_2283) 1 else maxQty + } + return stock + } + + //Generate default gear stock based on an amount of total QP. + private fun generateGearStock(points: Int): Array { + val stock = Array(gearStock.size) { ShopItem(0, 0) } + val qpTier = (points / 18) + for ((index, item) in stock.withIndex()) item.itemId = gearStock[index] + + for (i in 0 until min(qpTier, 10)) { + stock[i].amount = 30 + stock[i + 10].amount = 5 + } + stock[9].amount = 1 + return stock + } + + //Default gear shop stock + private val gearStock = arrayOf( + Items.GLOVES_7453, + Items.GLOVES_7454, + Items.GLOVES_7455, + Items.GLOVES_7456, + Items.GLOVES_7457, + Items.GLOVES_7458, + Items.GLOVES_7459, + Items.GLOVES_7460, + Items.GLOVES_7461, + Items.GLOVES_7462, + Items.WOODEN_SPOON_7433, + Items.EGG_WHISK_7435, + Items.SPORK_7437, + Items.SPATULA_7439, + Items.FRYING_PAN_7441, + Items.SKEWER_7443, + Items.ROLLING_PIN_7445, + Items.KITCHEN_KNIFE_7447, + Items.MEAT_TENDERISER_7449, + Items.CLEAVER_7451 + ) + + //Default food shop stock + private val foodStock = arrayOf( + Item(Items.CHOCOLATE_BAR_1973, 1), + Item(Items.CHEESE_1985, 1), + Item(Items.TOMATO_1982, 1), + Item(Items.COOKING_APPLE_1955, 1), + Item(Items.GRAPES_1987, 1), + Item(Items.POT_OF_FLOUR_1933, 1), + Item(Items.PIZZA_BASE_2283, 1), + Item(Items.EGG_1944, 1), + Item(Items.BUCKET_OF_MILK_1927, 1), + Item(Items.POT_OF_CREAM_2130, 1), + Item(Items.PAT_OF_BUTTER_6697, 1), + Item(Items.SPICE_2007, 1), + Item(Items.PIE_DISH_2313, 1), + Item(Items.CAKE_TIN_1887, 1), + Item(Items.BOWL_1923, 1), + Item(Items.JUG_1935, 1), + Item(Items.EMPTY_POT_1931, 1), + Item(Items.EMPTY_CUP_1980, 1), + Item(Items.BUCKET_1925, 1) + ) + } +} diff --git a/Server/src/main/java/core/game/interaction/npc/DoomsayerTogglePlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/DoomsayerTogglePlugin.java similarity index 96% rename from Server/src/main/java/core/game/interaction/npc/DoomsayerTogglePlugin.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/DoomsayerTogglePlugin.java index ccc856fd3..5d7c2722c 100644 --- a/Server/src/main/java/core/game/interaction/npc/DoomsayerTogglePlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/DoomsayerTogglePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.region.misthalin.lumbridge.handlers; import core.cache.def.impl.NPCDefinition; import core.game.component.Component; diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/FredChestListener.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/FredChestListener.kt new file mode 100644 index 000000000..0157925ba --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/FredChestListener.kt @@ -0,0 +1,30 @@ +package content.region.misthalin.lumbridge.handlers + +import core.api.* +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * @author bushtail + */ + +class FredChestListener : InteractionListener { + val SHUT = Scenery.CLOSED_CHEST_37009 + val OPEN = Scenery.OPEN_CHEST_37010 + + override fun defineListeners() { + on(SHUT, IntType.SCENERY, "open") { _, node -> + replaceScenery(node.asScenery(), OPEN, -1, node.location) + return@on true + } + on(OPEN, IntType.SCENERY, "shut") { _, node -> + replaceScenery(node.asScenery(), SHUT, -1, node.location) + return@on true + } + on(OPEN, IntType.SCENERY, "search") { player, _ -> + sendMessage(player, "You search the chest but find nothing.") + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/zone/FredsFarmHouseZone.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/FredsFarmHouseZone.java similarity index 95% rename from Server/src/main/java/core/game/content/zone/FredsFarmHouseZone.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/FredsFarmHouseZone.java index 81a6d9213..d5b059254 100644 --- a/Server/src/main/java/core/game/content/zone/FredsFarmHouseZone.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/FredsFarmHouseZone.java @@ -1,4 +1,4 @@ -package core.game.content.zone; +package content.region.misthalin.lumbridge.handlers; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/interaction/npc/GameInstructorPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/GameInstructorPlugin.java similarity index 89% rename from Server/src/main/java/core/game/interaction/npc/GameInstructorPlugin.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/GameInstructorPlugin.java index 7872c6d6d..2bd8a13d0 100644 --- a/Server/src/main/java/core/game/interaction/npc/GameInstructorPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/GameInstructorPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.region.misthalin.lumbridge.handlers; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; @@ -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/GnomeCopterSignListener.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/GnomeCopterSignListener.kt new file mode 100644 index 000000000..668e13113 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/GnomeCopterSignListener.kt @@ -0,0 +1,20 @@ +package content.region.misthalin.lumbridge.handlers + +import core.api.sendDialogue +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * @author bushtail + */ + +class GnomeCopterSignListener : InteractionListener { + val SIGN = Scenery.ADVERTISEMENT_30037 + override fun defineListeners() { + on(SIGN, IntType.SCENERY, "read") { player, _ -> + sendDialogue(player, "Come check our gnome copters up north! Disclaimer: EXTREMELY WIP") + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/city/HamHideoutPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/HamHideoutPlugin.java similarity index 83% rename from Server/src/main/java/core/game/interaction/city/HamHideoutPlugin.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/HamHideoutPlugin.java index 8c095a871..f0b482a63 100644 --- a/Server/src/main/java/core/game/interaction/city/HamHideoutPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/HamHideoutPlugin.java @@ -1,19 +1,22 @@ -package core.game.interaction.city; +package content.region.misthalin.lumbridge.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; +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.system.task.Pulse; -import rs09.game.world.GameWorld; +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.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/java/core/game/node/entity/npc/other/HansNPC.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/HansNPC.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/other/HansNPC.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/HansNPC.java index 92c574ced..7cae51688 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/HansNPC.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/HansNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.misthalin.lumbridge.handlers; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java new file mode 100644 index 000000000..7a810e667 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java @@ -0,0 +1,173 @@ +package content.region.misthalin.lumbridge.handlers; + +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.impl.ForceMovement; +import core.game.node.entity.npc.AbstractNPC; +import core.game.node.entity.player.Player; +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.path.Pathfinder; +import core.game.world.update.flag.context.Animation; +import core.plugin.Plugin; +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 + * + */ +@Initializable +public class LumbridgeBasementPlugin extends OptionHandler { + + /** + * The animation to use for the shortcut. + */ + private static final Animation ANIMATION = new Animation(2240); + + /** + * The jumping animation for stepping stones. + */ + private static final Animation JUMP_ANIMATION = new Animation(741); + + @Override + public Plugin newInstance(Object arg) throws Throwable { + SceneryDefinition.forId(6899).getHandlers().put("option:squeeze-through", this); + SceneryDefinition.forId(6898).getHandlers().put("option:squeeze-through", this); + SceneryDefinition.forId(6905).getHandlers().put("option:squeeze-through", this); + SceneryDefinition.forId(6912).getHandlers().put("option:squeeze-through", this); + SceneryDefinition.forId(5949).getHandlers().put("option:jump-across", this); + SceneryDefinition.forId(6658).getHandlers().put("option:enter", this); + SceneryDefinition.forId(32944).getHandlers().put("option:enter", this); + SceneryDefinition.forId(40261).getHandlers().put("option:climb-up", this); + SceneryDefinition.forId(40262).getHandlers().put("option:climb-up", this); + 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); + SceneryBuilder.add(new Scenery(40260, Location.create(2526, 5828, 2), 2)); + return this; + } + + @Override + public boolean handle(final Player player, Node node, String option) { + switch (option) { + case "squeeze-through": + Direction dir = null; + Location to = null; + switch (node.getId()) { + case 6912: + to = node.getLocation().getY() == 9603 ? Location.create(3224, 9601, 0) : Location.create(3224, 9603, 0); + dir = node.getLocation().getY() == 9603 ? Direction.SOUTH : Direction.NORTH; + break; + default: + to = player.getLocation().getX() >= 3221 ? Location.create(3219, 9618, 0) : Location.create(3222, 9618, 0); + dir = player.getLocation().getX() >= 3221 ? Direction.WEST : Direction.EAST; + break; + } + player.sendMessage("You squeeze through the hole."); + ForceMovement.run(player, player.getLocation(), to, ANIMATION, ANIMATION, dir, 20).setEndAnimation(Animation.RESET); + return true; + case "jump-across": + Location f = null; + Location s = null; + switch (node.getId()) { + case 5949: + f = Location.create(3221, 9554, 0); + s = player.getLocation().getY() >= 9556 ? Location.create(3221, 9552, 0) : Location.create(3221, 9556, 0); + break; + } + final Location first = f; + final Location second = s; + player.lock(); + GameWorld.getPulser().submit(new Pulse(2, player) { + int counter = 1; + + @Override + public boolean pulse() { + if (counter == 3) { + player.unlock(); + ForceMovement.run(player, player.getLocation(), second, JUMP_ANIMATION, 20); + player.sendMessage("You leap across with a mighty leap!"); + return true; + } else if (counter == 1){ + ForceMovement.run(player, player.getLocation(), first, JUMP_ANIMATION, 20); + } + counter++; + return false; + } + }); + break; + case "enter": + switch (node.getId()) { + case 32944: + //Location.create(3353,3951,0) + player.teleport(Location.create(3219, 9532, 2)); + break; + case 6658: + //Location.create(3226, 9542, 0) + player.teleport(Location.create(3226, 9542, 0)); + break; + } + break; + case "climb-up": + switch (node.getId()) { + case 40261: + player.teleport(player.getLocation().transform(0, -1, 1)); + break; + case 40262: + player.teleport(player.getLocation().transform(0, -1, 1)); + break; + } + break; + case "jump-down": + switch (node.getId()) { + case 40849: + player.teleport(player.getLocation().transform(0, 1, -1)); + break; + } + break; + case "climb-through": + switch (node.getId()) { + case 40260: + player.teleport(Location.create(2525, 5810, 0)); + break; + } + break; + case "crawl-through": + switch (node.getId()) { + case 41077: + player.teleport(Location.create(2527, 5830, 2)); + break; + } + break; + } + return true; + } + + @Override + public Location getDestination(Node node, Node n) { + if (n instanceof Scenery) { + if (n.getId() == 5949) { + return node.getLocation().getY() >= 9555 ? Location.create(3221, 9556, 0) : Location.create(3221, 9552, 0); + } else if (n.getId() == 40262) { + return n.getLocation().transform(0, 1, 0); + } else if (n.getId() == 40261) { + return n.getLocation().transform(0, 1, 0); + } + } + return null; + } + +} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/LumbridgeInstructorNPC.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeInstructorNPC.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/other/LumbridgeInstructorNPC.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeInstructorNPC.java index 385b55050..53f19c653 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/LumbridgeInstructorNPC.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeInstructorNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.other; +package content.region.misthalin.lumbridge.handlers; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/interaction/city/LumbridgeNodePlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeNodePlugin.java similarity index 90% rename from Server/src/main/java/core/game/interaction/city/LumbridgeNodePlugin.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeNodePlugin.java index c705175af..0a0d61107 100644 --- a/Server/src/main/java/core/game/interaction/city/LumbridgeNodePlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeNodePlugin.java @@ -1,10 +1,11 @@ -package core.game.interaction.city; +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; -import core.game.content.activity.ActivityManager; -import core.game.content.global.action.ClimbActionHandler; +import core.game.activity.ActivityManager; +import core.game.global.action.ClimbActionHandler; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -16,11 +17,14 @@ 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 rs09.game.world.GameWorld; +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 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/java/core/game/content/cutscene/OrganCutScene.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/OrganCutScene.java similarity index 90% rename from Server/src/main/java/core/game/content/cutscene/OrganCutScene.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/OrganCutScene.java index 61427a5c5..26ab44f6b 100644 --- a/Server/src/main/java/core/game/content/cutscene/OrganCutScene.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/OrganCutScene.java @@ -1,13 +1,13 @@ -package core.game.content.cutscene; +package content.region.misthalin.lumbridge.handlers; import core.game.node.entity.player.link.diary.DiaryType; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.build.DynamicRegion; @@ -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/java/core/game/node/entity/npc/bosses/TormentedDemonNPC.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/npc/bosses/TormentedDemonNPC.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java index 30fee5e5e..17942de67 100644 --- a/Server/src/main/java/core/game/node/entity/npc/bosses/TormentedDemonNPC.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java @@ -1,8 +1,8 @@ -package core.game.node.entity.npc.bosses; +package content.region.misthalin.lumbridge.handlers; import java.util.concurrent.TimeUnit; -import core.game.content.global.BossKillCounter; +import content.data.BossKillCounter; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -16,7 +16,7 @@ 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 rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; /** * Handles the Tormented Demon NPC. @@ -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/java/core/game/content/activity/gnomecopter/GCInformationSign.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/gnomecopter/GCInformationSign.java similarity index 95% rename from Server/src/main/java/core/game/content/activity/gnomecopter/GCInformationSign.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/gnomecopter/GCInformationSign.java index 728ed6a2e..1d1284ed6 100644 --- a/Server/src/main/java/core/game/content/activity/gnomecopter/GCInformationSign.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/gnomecopter/GCInformationSign.java @@ -1,4 +1,4 @@ -package core.game.content.activity.gnomecopter; +package content.region.misthalin.lumbridge.handlers.gnomecopter; import core.game.component.Component; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/activity/gnomecopter/GnomeCopterActivity.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/gnomecopter/GnomeCopterActivity.java similarity index 94% rename from Server/src/main/java/core/game/content/activity/gnomecopter/GnomeCopterActivity.java rename to Server/src/main/content/region/misthalin/lumbridge/handlers/gnomecopter/GnomeCopterActivity.java index f942472d5..021e8efaa 100644 --- a/Server/src/main/java/core/game/content/activity/gnomecopter/GnomeCopterActivity.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/gnomecopter/GnomeCopterActivity.java @@ -1,8 +1,7 @@ -package core.game.content.activity.gnomecopter; +package content.region.misthalin.lumbridge.handlers.gnomecopter; import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.content.activity.ActivityPlugin; +import core.game.activity.ActivityPlugin; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -12,7 +11,7 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.zone.ZoneBorders; @@ -112,7 +111,7 @@ public final class GnomeCopterActivity extends ActivityPlugin { @Override public boolean pulse() { if (++stage == 1) { - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 11); + player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 7, 11); ForceMovement.run(player, player.getLocation(), object.getLocation(), ForceMovement.WALK_ANIMATION, new Animation(8955), Direction.NORTH, 8); player.lock(); } else if (stage == 3) { @@ -184,7 +183,6 @@ public final class GnomeCopterActivity extends ActivityPlugin { player.getInterfaceManager().restoreTabs(); usedLandingPads[pad] = false; player.removeAttribute("gc:flying"); - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 2, 1); return true; } return false; diff --git a/Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/CooksAssistant.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/CooksAssistant.kt rename to Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt index 83a2e429e..54d38f426 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/CooksAssistant.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/CooksAssistant.kt @@ -1,9 +1,10 @@ -package rs09.game.content.quest.free.cooksassistant +package content.region.misthalin.lumbridge.quest.cooksassistant 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/kotlin/rs09/game/content/quest/free/cooksassistant/GillieGroatsDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/GillieGroatsDialogue.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/GillieGroatsDialogue.kt rename to Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/GillieGroatsDialogue.kt index a56b755c1..a951bcaba 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/GillieGroatsDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/GillieGroatsDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.content.quest.free.cooksassistant +package content.region.misthalin.lumbridge.quest.cooksassistant -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable diff --git a/Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/LumbridgeCookDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/LumbridgeCookDialogue.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/LumbridgeCookDialogue.kt rename to Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/LumbridgeCookDialogue.kt index 5e99af1c1..ae7b6439f 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/LumbridgeCookDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/LumbridgeCookDialogue.kt @@ -1,11 +1,12 @@ -package rs09.game.content.quest.free.cooksassistant +package content.region.misthalin.lumbridge.quest.cooksassistant -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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.plugin.Initializable +import content.data.Quests /** * Dialogue for the Lumbridge Cook. @@ -29,16 +30,16 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if(player?.questRepository?.getQuest("Lost Tribe")?.getStage(player) == 10){ + if (player?.questRepository?.getQuest(Quests.THE_LOST_TRIBE)?.getStage(player) == 10) { player("Did you see what happened in the cellar?") stage = 0 return true } - if (player?.questRepository?.getQuest("Cook's Assistant")!!.getStage(player) <= 0) { //If the player has ot started cook's assistant + if (player?.questRepository?.getQuest(Quests.COOKS_ASSISTANT)!!.getStage(player) <= 0) { //If the player has ot started cook's assistant npc(FacialExpression.SAD, "What am I to do?") stage = 0 return true - } else if (player?.questRepository?.getQuest("Cook's Assistant")!!.getStage(player) in 10..99) { //During the Cook's Assistant Quest + } else if (player?.questRepository?.getQuest(Quests.COOKS_ASSISTANT)!!.getStage(player) in 10..99) { //During the Cook's Assistant Quest if (player.getAttribute("cooks_assistant:all_submitted", false) || (player.getAttribute("cooks_assistant:milk_submitted", false) && player.getAttribute("cooks_assistant:flour_submitted", false) && player.getAttribute("cooks_assistant:egg_submitted", false))){ //If the player has handed all the ingredients to the chef but did not continue the dialogue npc(FacialExpression.HAPPY, "You've brought me everything I need! I am saved!", "Thank you!") stage = 200 @@ -57,14 +58,14 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - if(player.questRepository.getQuest("Lost Tribe").getStage(player) == 10){ + if (player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 10) { when(stage){ //Lost Tribe 0 -> npc("Last night I was in the kitchen and I heard a noise","from the cellar. I opened the trapdoor and saw a","creature dart into a hole in the wall.").also { stage++ } 1 -> npc("It looked a bit like a goblin, but it had big bulging eyes.","It wasn't wearing armour, but it had this odd helmet","with a light on it.").also { stage++ } 2 -> npc("The tunnel was too dark for me to follow it, so I went","to tell the Duke. But when we went down to the cellar","the hole had been blocked up, and no one believes me.").also { stage++ } 3 -> player("I believe you.").also { stage++ } - 4 -> npc("Thank you, ${player.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 @@ -117,7 +118,7 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ 44 -> npc(FacialExpression.ANGRY, "I AM a real cook! I haven't got time to be chatting", "about culinary fashion. I'm in desperate need of help!").also { stage = 21 } //Yes, I'll help you - 50 -> npc(FacialExpression.HAPPY, "Oh thank you, thank you. I need milk, an egg and", "flour. I'd be very grateful if you can get them for me.").also{ player?.questRepository?.getQuest("Cook's Assistant")?.start(player!!); stage++ } + 50 -> npc(FacialExpression.HAPPY, "Oh thank you, thank you. I need milk, an egg and", "flour. I'd be very grateful if you can get them for me.").also{ player?.questRepository?.getQuest(Quests.COOKS_ASSISTANT)?.start(player!!); stage++ } 51 -> player(FacialExpression.NEUTRAL, "So where do I find these ingredients then?").also { stage = 60 } //Where do I find these ingredients? @@ -241,10 +242,10 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ 203 -> npc(FacialExpression.NEUTRAL, "Maybe, but I won't be holding my breath.").also { stage++ } //Activate the Cook's Assistant Quest Complete Certificate - 204 -> end().also { player?.questRepository?.getQuest("Cook's Assistant")?.finish(player!!) } + 204 -> end().also { player?.questRepository?.getQuest(Quests.COOKS_ASSISTANT)?.finish(player!!) } //Dialogue after Cook's Assistant Completion - 300 -> if(player.questRepository.getQuest("Lost Tribe").getStage(player) == 10) { + 300 -> if(player.questRepository.getQuest(Quests.THE_LOST_TRIBE).getStage(player) == 10) { player("Do you know what happened in the castle cellar?").also { stage = 600 } } else { options("I am getting strong and mighty.", "I keep on dying.", "Can I use your range?").also { stage++ } @@ -280,4 +281,4 @@ class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(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/cooksassistant/MillieMillerDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/MillieMillerDialogue.kt new file mode 100644 index 000000000..ea7b4f5ea --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/MillieMillerDialogue.kt @@ -0,0 +1,68 @@ + package content.region.misthalin.lumbridge.quest.cooksassistant + + import core.ServerConstants + import core.game.node.entity.player.Player + import core.plugin.Initializable + + /** + * Dialogue for Millie Miller. + * @author Qweqker + */ + + @Initializable + class MillieMillerDialogue (player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + + npc(core.game.dialogue.FacialExpression.HAPPY, "Hello Adventurer. Welcome to Mill Lane Mill. Can I", "help you?") + stage = 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + + //Continuation of Millie's greeting + 0 -> options("Who are you?", "What is this place?", "How do I mill flour?", "I'm fine, thanks.").also { stage++ } + 1 -> when (buttonId) { + 1 -> player(core.game.dialogue.FacialExpression.ASKING, "Who are you?").also { stage = 10 } + 2 -> player(core.game.dialogue.FacialExpression.ASKING, "What is this place?").also { stage = 20 } + 3 -> player(core.game.dialogue.FacialExpression.ASKING, "How do I mill flour?").also { stage = 30 } + 4 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "I'm fine, thanks.").also { stage = 1000 } + } + + //Who are you? + 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "I'm Miss Millicent Miller the Miller of Mill Lane Mill.", "Our family have been milling flour for generations.").also { stage++ } + 11 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "It's a good business to be in. People will always need", "flour.").also { stage++ } + 12 -> player(core.game.dialogue.FacialExpression.ASKING, "How do I mill flour?").also { stage = 30 } + + //What is this place? + 20 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS, "This is Mill Lane Mill. Millers of the finest flour in", ServerConstants.SERVER_NAME + ", and home to the Miller family for many", "generations").also { stage++ } + 21 -> npc(core.game.dialogue.FacialExpression.HAPPY, "We take grain from the field nearby and mill into flour.").also { stage++ } + 22 -> player(core.game.dialogue.FacialExpression.ASKING, "How do I mill flour?").also { stage = 30 } + + //How do I mill flour? + 30 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Making flour is pretty easy. First of all you need to", "get some grain. You can pick some from wheat fields.", "There is one just outside the Mill, but there are many", "others scattered across " + ServerConstants.SERVER_NAME + ". Feel free to pick wheat").also { stage++ } + 31 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "from our field! There always seems to be plenty of", "wheat there.").also { stage++ } + 32 -> player(core.game.dialogue.FacialExpression.ASKING, "Then I bring my wheat here?").also { stage++ } + 33 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, or one of the other mills in " + ServerConstants.SERVER_NAME + ". They all work", "the same way. Just take your grain to the top floor of", "the mill (up two ladders, there are three floors including", "this one) and then place some grain in to the hopper.").also { stage++ } + 34 -> npc(core.game.dialogue.FacialExpression.HAPPY, "Then you need to start the grinding process by pulling", "the hopper lever. You can add more grain, but each", "time you add grain you have to pul the hopper lever", "again.").also { stage++ } + 35 -> player(core.game.dialogue.FacialExpression.ASKING, "So where does the flour go then?").also { stage++ } + 36 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS, "The flour appears in this room here, you'll need a pot", "to put the flour into. One pot will hold the flour made", "by one load of grain").also { stage++ } + 37 -> npc(core.game.dialogue.FacialExpression.HAPPY, "And that's it! You now have some pots of finely ground", "flour of the highest quality. Ideal for making tasty cakes", "or delicous bread. I'm not a cook so you'll have to ask a", "cook to ind out how to bake things.").also { stage++ } + 38 -> player(core.game.dialogue.FacialExpression.HAPPY, "Great! Thanks for your help.").also { stage = 1000 } + + //Conversation Endpoint + 1000 -> end() + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return MillieMillerDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(3806) + } + } \ No newline at end of file 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 new file mode 100644 index 000000000..89f48fe42 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCity.kt @@ -0,0 +1,118 @@ +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, Vexia, Aero, Player Name + */ +@Initializable +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() + + override fun finish(player: Player?) { + player ?: return + super.finish(player) + var line = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.DRAMEN_STAFF_772, 235, 277, 3 + 2) + drawReward(player,"3 Quest points",line++) + drawReward(player,"Access to Zanaris",line++) + player.questRepository.syncronizeTab(player) + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + when (stage) { + 0 -> { + line(player, BLUE + "I can start this quest by speaking to the Adventurers in", 4 + 7) + line(player, BLUE + "the Swamp just south of Lumbridge.", 5 + 7) + drawQuestRequirements(player) + } + 10 -> line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

Apparently there is a leprechaun hiding in a tree nearby

who can tell me how to enter the Lost City of Zanaris", 4 + 7) + 20 -> line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

I found the Leprechaun hiding in a nearby tree.

He told me that the entrance to Zanaris is in the shed in

Lumbridge swamp but only if I am carrying a Dramen Staff

I can find a Dramen Tree in a cave on Entrana somewhere", 4 + 7) + 21 -> if (player.hasItem(Item(Items.DRAMEN_STAFF_772,1))) { + line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

I found the Leprechaun hiding in a nearby tree.

He told me that the entrance to Zanaris is in the shed in

Lumbridge swamp but only if I am carrying a Dramen Staff.

The Dramen Tree was guarded by a powerful Tree Spirit.

I cut a branch from the tree and crafted a Dramen Staff.

I should enter Zanaris by going to the shed in Lumbridge

Swamp while keeping the Dramen staff with me", 4 + 7) + } else { + line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

I found the Leprechaun hiding in a nearby tree.

He told me that the entrance to Zanaris is in the shed in

Lumbridge swamp but only if I am carrying a Dramen Staff.

The Dramen Tree was guarded by a powerful Tree Spirit.

With the Spirit defeated I can cut a branch from the tree", 4 + 7) + } + 100 -> line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

I found the Leprechaun hiding in a nearby tree.

He told me that the entrance to Zanaris is in the shed in

Lumbridge swamp but only if I am carrying a Dramen Staff.

The Dramen Tree was guarded by a powerful Tree Spirit.

I cut a branch from the tree and crafted a Dramen Staff.

With the mystical Dramen Staff in my possession I was

able to enter Zanaris through the shed in Lumbridge

swamp.


QUEST COMPLETE!", 4 + 7) + } + } + + private fun drawQuestRequirements(player: Player) { + var line = 7 + 7 + val questRequirements = arrayOf( + "Level 31 Crafting", + "Level 36 Woodcutting" + ) + val hasRequirements = booleanArrayOf( + player.getSkills().hasLevel(Skills.CRAFTING, 31), + player.getSkills().hasLevel(Skills.WOODCUTTING,35) + ) + line(player, BLUE + "To complete this quest I will need:", 6) + for (i in 0..1) { + line(player, questRequirements[i], line++, hasRequirements[i]) + } + 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 + } +} 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 new file mode 100644 index 000000000..32bef2049 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/LostCityListeners.kt @@ -0,0 +1,74 @@ +package content.region.misthalin.lumbridge.quest.lostcity + +import core.api.* +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.system.task.Pulse +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +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 { + + override fun defineListeners() { + + // 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 isOutsideShed = player.location.x < node.location.x + 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 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 -> { + 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, Quests.LOST_CITY) + 2 -> { + finishQuest(player, Quests.LOST_CITY) + return true + } + } + return false + } + }) + } + return@on true + } + // creating the dramen staff via using knife on dramen branch + onUseWith(IntType.ITEM,Items.KNIFE_946,Items.DRAMEN_BRANCH_771){ player, _, _ -> + if(!player.skills.hasLevel(Skills.CRAFTING,31)) { + sendDialogue(player,"You need a crafting level of 31 to do this.") + return@onUseWith false + } + runTask(player, 2) { + 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 new file mode 100644 index 000000000..15800e2f7 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/ShamusDialogue.kt @@ -0,0 +1,66 @@ +package content.region.misthalin.lumbridge.quest.lostcity + +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs +import core.api.getQuestStage +import core.api.setQuestStage +import content.data.Quests + +/** + * ShamusDialogue, to handle the dialogue of Shamus the Leprechaun from the Lost City quest + * @author lila + * @author Vexia + */ +@Initializable +class ShamusDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + 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 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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!") + 2 -> end().also { ShamusTreeListener.disappearShamus() } + } + 10 -> when(stage++) { + 0 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"I want to find Zanaris.") + 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Zanaris is it now? Well well well... Yer'll be needing to be going to that funny little shed out there in the swamp, so you will.") + 2 -> playerl(core.game.dialogue.FacialExpression.THINKING,"...but... I thought... Zanaris was a city?") + 3 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Aye that it is!") + 4 -> playerl(core.game.dialogue.FacialExpression.THINKING,"...How does it fit in a shed then?") + 5 -> npcl(core.game.dialogue.FacialExpression.LAUGH,"Ah yer stupid elephant! The city isn't IN the shed! The doorway to the shed is being a portal to Zanaris, so it is.") + 6 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING,"So I just walk into the shed and end up in Zanaris then?") + 7 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Oh, was I fergetting to say? Yer need to be carrying a Dramenwood staff to be getting there! Otherwise Yer'll just be ending up in the shed.") + 8 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So where would I get a staff?") + 9 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Dramenwood staffs are crafted from branches of the Dramen tree, so they are. I hear there's a Dramen tree over on the island of Entrana in a cave.") + 10 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING,"or some such. There would be probably be a good place for an elephant like yer to be starting looking I reckon.") + 11 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"The monks are running a ship from Port Sarim to Entrana, I hear too. Now leave me alone yer elephant!") + 12 -> end().also { + ShamusTreeListener.disappearShamus() + sendDialogue("The leprechaun magically disappears.") + setQuestStage(player, Quests.LOST_CITY, 20) + } + } + else -> when(stage++) { + 0 -> playerl(core.game.dialogue.FacialExpression.THINKING, "I'm not sure.") + 1 -> { + val pronoun = if(player.isMale) "he" else "she" + npcl(core.game.dialogue.FacialExpression.ANNOYED,"HA! Look at yer! Look at the stupid elephant who tries to go catching a leprechaun when $pronoun don't even be knowing what $pronoun wants!") + } + 2 -> end().also { ShamusTreeListener.disappearShamus() } + } + + } + return true + } + + override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + return ShamusDialogue(player) + } + override fun getIds(): IntArray = intArrayOf(NPCs.SHAMUS_654) +} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/ShamusTreeListener.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/ShamusTreeListener.kt new file mode 100644 index 000000000..a35fc5bcb --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/ShamusTreeListener.kt @@ -0,0 +1,72 @@ +package content.region.misthalin.lumbridge.quest.lostcity + +import core.api.sendMessage +import core.api.sendNPCDialogue +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import content.data.skill.SkillingTool +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.GameWorld + + +/** + * Shamus tree listener, to handle when a player chops Shamus's home tree, + * and to handle some details about the Shamus NPC + * @author lila + * @author Vexia + */ +@Initializable +class ShamusTreeListener : InteractionListener { + + init { + SHAMUS.init() + SHAMUS.isWalks = true + SHAMUS.isInvisible = true + } + + companion object { + val SHAMUS = NPC(NPCs.SHAMUS_654, Location(3138, 3211, 0)) + fun disappearShamus() { + SHAMUS.isInvisible = true + } + } + + private fun handleShamusTree(player: Player): Boolean { + if (SkillingTool.getHatchet(player) == null) { + sendMessage(player,"You do not have an axe which you have the level to use.") + return true + } + showShamus(player) + return true + } + + private fun showShamus(player: Player) { + if(SHAMUS.isInvisible) { + SHAMUS.isInvisible = false + SHAMUS.properties.teleportLocation = SHAMUS.properties.spawnLocation + } + sendNPCDialogue(player,NPCs.SHAMUS_654,"Hey! Yer big elephant! Don't go choppin' down me house, now!", core.game.dialogue.FacialExpression.FURIOUS) + GameWorld.Pulser.submit(object : Pulse(100) { + override fun pulse(): Boolean { + if (SHAMUS.dialoguePlayer == null) { + SHAMUS.isInvisible = true + return true + } + return false + } + }) + } + + override fun defineListeners() { + on(Scenery.TREE_2409, IntType.SCENERY, "chop") { player, _ -> + handleShamusTree(player) + return@on true + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..754a82d69 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/TreeSpiritNPC.kt @@ -0,0 +1,56 @@ +package content.region.misthalin.lumbridge.quest.lostcity + +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 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 + */ +@Initializable +class TreeSpiritNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var target: Player? = null + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return TreeSpiritNPC(id, location) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.TREE_SPIRIT_655) + } + init { + isWalks = true + isRespawn = false + } + + override fun handleTickActions() { + if(target==null) { + clear() + return + } + super.handleTickActions() + if (!inCombat()) { + attack(target) + } + if (!target!!.isActive() || target!!.getLocation().getDistance(getLocation()) > 15) { + clear() + target!!.removeAttribute("treeSpawned") + } + } + + override fun finalizeDeath(killer: Entity) { + super.finalizeDeath(killer) + if (killer is Player) { + if (getQuestStage(killer, Quests.LOST_CITY) == 20) { + setQuestStage(killer, Quests.LOST_CITY,21) + sendDialogue(killer, "With the Tree Spirit defeated you can now chop the tree.") + } + } + } +} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/WarriorDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/WarriorDialogue.kt new file mode 100644 index 000000000..9281c9153 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/WarriorDialogue.kt @@ -0,0 +1,99 @@ +package content.region.misthalin.lumbridge.quest.lostcity + +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.getQuestStage +import core.api.startQuest +import content.data.Quests + +/** + * WarriorDialogue, to handle the dialogue for the Warrior in the Lost City quest + * @author lila + * @author Vexia + */ +@Initializable +class WarriorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + 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 } + else -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Hello there traveller.").also { stage = 1 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + 1 -> showTopics( + Topic(core.game.dialogue.FacialExpression.THINKING,"What are you camped out here for?",100), + Topic(core.game.dialogue.FacialExpression.HALF_THINKING,"Do you know any good adventures I can go on?",101) + ) + 2 -> showTopics( + Topic(core.game.dialogue.FacialExpression.ASKING,"Please tell me.",200), + Topic(core.game.dialogue.FacialExpression.ANGRY,"I don't think you've found a good adventure at all!",250) + ) + 3 -> showTopics( + Topic(core.game.dialogue.FacialExpression.ASKING,"Who's Zanaris?",301), + Topic(core.game.dialogue.FacialExpression.ASKING,"What's Zanaris?",302), + Topic(core.game.dialogue.FacialExpression.ASKING,"What makes you think it's out here?",300) + ) + 4 -> showTopics( + Topic(core.game.dialogue.FacialExpression.ASKING,"If it's hidden how are you planning to find it?",400), + Topic(core.game.dialogue.FacialExpression.LAUGH,"There's no such thing!",450) + ) + 5 -> options("Please tell me.", "Looks like you don't know either.").also { stage = 500 } + 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,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 } + 200 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"No.").also { stage++ } + 201 -> playerl(core.game.dialogue.FacialExpression.SAD,"Please?").also { stage++ } + 202 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"No!").also { stage++ } + 203 -> playerl(core.game.dialogue.FacialExpression.SAD,"PLEEEEEEEEEEEEEEEEEEEEEEEEASE???").also { stage++ } + 204 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"NO!").also { stage = END_DIALOGUE } + 250 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"Hah! Adventurers of our calibre don't just hang around in forests for fun, whelp!").also { stage++ } + 251 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Oh Really?").also { stage++ } + 252 -> playerl(core.game.dialogue.FacialExpression.THINKING,"What are you camped out here for?").also{ stage = 100 } + 300 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Don't you know of the legends that tell of the magical city, hidden in the swamp... Uh, no, you're right, we're wasting our time here.").also { stage = 4 } + 301 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Ahahahaha! Zanaris isn't a person! It's a magical hidden city filled with treasure and rich... uh, nothing. It's nothing.").also { stage = 4 } + 302 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING,"I don't think we want other people competing with us to find it. Forget I said anything.").also { stage = 5 } + 400 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Well, we don't want to tell anyone else about that, because we don't want anyone else sharing in all that glory and treasure.").also { stage = 5 } + 450 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"When we've found Zanaris you'll... GAH! I mean, we're not here for any particular reason at all.").also { stage = 3 } + 500 -> when(buttonId) { + 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Please tell me.").also{ stage = 200 } + 2 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Well, it looks to ME like YOU don't know EITHER, seeing as you're all just sat around here.").also { stage++ } + } + 501 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"Of course we know! We just haven't found which tree the stupid leprechaun's hiding in yet!").also { stage++ } + 502 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"GAH! I didn't mean to tell you that! Look, just forget I said anything okay?").also { stage = 6 } + 600 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Ye.. uh, no. No, not at all. And even if he did - which he doesn't - he DEFINITELY ISN'T hiding in some tree around here. Nope, definitely not. Honestly.").also { stage = 7 } + 700 -> npcl(core.game.dialogue.FacialExpression.WORRIED,"Help? What help? I didn't help! Please don't say I did, I'll get in trouble!").also { stage = 8 } + 1000 -> npcl(core.game.dialogue.FacialExpression.WORRIED,"What? How did you know that? Uh... I mean, no, no you're very wrong. Very wrong, and not right at all, and I definitely didn't tell you about that at all.").also { stage = END_DIALOGUE } + 2000 -> npcl(core.game.dialogue.FacialExpression.SAD,"We're still searching for Zanaris...GAH! I mean we're not doing anything here at all.").also { stage++ } + 2001 -> playerl(core.game.dialogue.FacialExpression.SAD,"I haven't found it yet either.").also { stage = END_DIALOGUE } + 3000 -> npcl(core.game.dialogue.FacialExpression.SAD,"Oh please don't say that anymore! If the rest of my party knew I'd helped you they'd probably throw me out and make me walk home by myself!").also { stage++ } + 3001 -> npcl(core.game.dialogue.FacialExpression.ASKING,"So anyway, what have you found out? Where is the fabled Zanaris? Is it all the legends say it is?").also { stage++ } + 3002 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"You know.... I think I'll keep that to myself.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + return WarriorDialogue(player) + } + override fun getIds(): IntArray = intArrayOf(NPCs.WARRIOR_650) +} + +/* +Some evidence from early/mid 2009 to help determine facial expressions, and contains some lines not previously included in this warrior's dialogue +this also functions as evidence for most of the warrior's dialogue +https://www.youtube.com/watch?v=nFDifUB8dxQ +https://www.youtube.com/watch?v=4BKRG4yw16o + */ diff --git a/Server/src/main/kotlin/rs09/game/content/quest/free/runemysteries/DukeHoracioRMDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/DukeHoracioRMDialogue.kt similarity index 90% rename from Server/src/main/kotlin/rs09/game/content/quest/free/runemysteries/DukeHoracioRMDialogue.kt rename to Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/DukeHoracioRMDialogue.kt index 7d6fdd976..42ad4fa36 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/runemysteries/DukeHoracioRMDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/DukeHoracioRMDialogue.kt @@ -1,11 +1,12 @@ -package rs09.game.content.quest.free.runemysteries +package content.region.misthalin.lumbridge.quest.runemysteries import core.game.node.entity.player.link.quest.Quest import core.game.node.item.GroundItemManager import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +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/java/core/game/content/quest/free/runemysteries/RuneMysteries.java b/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/RuneMysteries.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/free/runemysteries/RuneMysteries.java rename to Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/RuneMysteries.java index 1e994fa22..85e329907 100644 --- a/Server/src/main/java/core/game/content/quest/free/runemysteries/RuneMysteries.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/runemysteries/RuneMysteries.java @@ -1,8 +1,9 @@ -package core.game.content.quest.free.runemysteries; +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/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/java/core/game/content/quest/free/therestlessghost/FatherAereckDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/free/therestlessghost/FatherAereckDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java index 34083b405..acf0ca8c2 100644 --- a/Server/src/main/java/core/game/content/quest/free/therestlessghost/FatherAereckDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherAereckDialogue.java @@ -1,12 +1,12 @@ -package core.game.content.quest.free.therestlessghost; +package content.region.misthalin.lumbridge.quest.therestlessghost; import core.game.component.Component; -import core.game.container.access.BitregisterAssembler; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.link.diary.DiaryType; import core.plugin.Initializable; import core.game.node.entity.player.Player; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; +import content.data.Quests; /** @@ -43,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; @@ -73,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 { @@ -129,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; @@ -237,8 +237,6 @@ public final class FatherAereckDialogue extends DialoguePlugin { case 10: end(); player.getInterfaceManager().open(new Component(652)); - BitregisterAssembler.send(player, 652, 34, 0, 13, new BitregisterAssembler(0)); - player.getConfigManager().set(1146, player.getGraveManager().getType().ordinal() | 262112); player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 15); break; case 20: 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 new file mode 100644 index 000000000..219c0c562 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/FatherUhrneyDialogue.java @@ -0,0 +1,285 @@ +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; +import core.plugin.Initializable; +import core.game.node.item.Item; +import core.game.dialogue.DialoguePlugin; + +/** + * Represents the father urgney dialogue plugin. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class FatherUhrneyDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code FatherUhrneyDialogue} {@code Object}. + */ + public FatherUhrneyDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code FatherUhrneyDialogue} {@code Object}. + * @param player the player. + */ + public FatherUhrneyDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new FatherUhrneyDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + npc(FacialExpression.ANGRY, "Go away! I'm meditating!"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + 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(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(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; + case 500: + switch (buttonId) { + case 1: + player("Well, that's friendly."); + stage = 10; + break; + case 2: + 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(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: + player("He's got a ghost haunting his graveyard."); + stage = 503; + break; + case 503: + npc(FacialExpression.ANGRY, "Oh, the silly fool."); + stage = 504; + break; + case 504: + npc(FacialExpression.ANGRY, "I leave town for just five months, and ALREADY he", "can't manage."); + stage = 505; + break; + case 505: + npc(FacialExpression.SAD, "(sigh)"); + stage = 506; + break; + case 506: + 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(FacialExpression.NEUTRAL, "Tell you what I can do though; take this amulet."); + stage = 508; + break; + case 508: + if (player.getInventory().freeSlots() == 0) { + end(); + player.getPacketDispatch().sendMessage("You don't have enough inventory space to accept this amulet."); + break; + } + interpreter.sendItemMessage(552, "Father Urhney hands you an amulet."); + player.getInventory().add(new Item(552, 1)); + player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 20); + player.getGameAttributes().setAttribute("/save:restless-ghost:urhney", true); + stage = 509; + break; + case 509: + npc(FacialExpression.NEUTRAL, "It is an Amulet of Ghostspeak."); + stage = 510; + break; + case 510: + 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(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: + player("Thank you. I'll give it a try!"); + stage = 513; + break; + case 513: + end(); + break; + case 514: + switch (buttonId) { + case 1: + player("Well, that's friendly."); + stage = 10; + break; + case 2: + player(FacialExpression.NEUTRAL, "I've lost the Amulet of Ghostspeak."); + stage = 515; + break; + case 3: + player("I've come to repossess your house."); + stage = 20; + break; + } + break; + case 515: + if (player.getInventory().contains(552, 1) || player.getEquipment().contains(552, 1)) { + interpreter.sendDialogue("Father Urhney sighs."); + stage = 516; + break; + } + if (player.getBank().contains(552, 1)) { + interpreter.sendDialogue("Father Urhney sighs."); + stage = 517; + break; + } + interpreter.sendDialogue("Father Urhney sighs."); + stage = 519; + break; + case 516: + npc("What are you talking about? I can see you've got it", "with you!"); + stage = 518; + break; + case 517: + 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(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: + player.getInventory().add(new Item(552)); + interpreter.sendItemMessage(552, "Father Urhney hands you an amulet."); + player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 12); + stage = 521; + break; + case 521: + npc(FacialExpression.ANGRY, "Be more careful this time."); + stage = 522; + break; + case 522: + player("Ok, I'll try to be."); + stage = 523; + break; + case 523: + end(); + break; + case 1: + switch (buttonId) { + case 1: + player("Well, that's friendly."); + stage = 10; + break; + case 2: + player("I've come to repossess your house."); + stage = 20; + break; + + } + break; + case 10: + npc("I SAID go AWAY."); + stage = 11; + break; + case 11: + player("Ok, ok... sheesh, what a grouch."); + stage = 12; + break; + case 12: + end(); + break; + case 20: + npc("Under what grounds???"); + stage = 21; + break; + case 21: + options("Repeated failure on mortgage repayments.", "I don't know, I just wanted this house."); + stage = 22; + break; + case 22: + switch (buttonId) { + case 1: + player("Repeated failure on mortgage repayments."); + stage = 100; + break; + case 2: + player("I don't know. I just wanted this house..."); + stage = 200; + break; + + } + break; + case 100: + npc("What?"); + stage = 101; + break; + case 101: + npc("I don't have a mortgage! I built this house."); + stage = 102; + break; + case 102: + player("Sorry. I must have got the wrong address. All the", "houses look the same around here."); + stage = 103; + break; + case 103: + npc("What? What houses? What ARE you talking about???"); + stage = 104; + break; + case 104: + player("Never mind."); + stage = 105; + break; + case 105: + end(); + break; + case 200: + npc("Oh... go away and stop wasting my time!"); + stage = 201; + break; + case 201: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 458 }; + } +} diff --git a/Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhost.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhost.java rename to Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java index 0108fadf3..11fd553b1 100644 --- a/Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhost.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhost.java @@ -1,12 +1,16 @@ -package core.game.content.quest.free.therestlessghost; +package content.region.misthalin.lumbridge.quest.therestlessghost; -import core.game.content.quest.members.animalmagnetism.OldCronDialogue; +import content.region.misthalin.draynor.quest.anma.OldCronDialogue; 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 core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * Represents the restless ghost quest. @@ -15,27 +19,21 @@ import rs09.plugin.PluginManager; */ @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 public Quest newInstance(Object object) { - PluginManager.definePlugin(new OldCronDialogue()); + ClassScanner.definePlugin(new OldCronDialogue()); return this; } @@ -107,8 +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); - player.getPacketDispatch().sendMessage("Congratulations! Quest complete!"); + setVarp(player, 728, 31, true); player.getGameAttributes().removeAttribute("restless-ghost:urhney"); } diff --git a/Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhostDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhostDialogue.java rename to Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java index 1e30d2b14..41b9f046b 100644 --- a/Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhostDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.quest.free.therestlessghost; +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; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; /** * Handles the RestlessGhostDialogue dialogue. @@ -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/java/core/game/content/quest/free/therestlessghost/RestlessGhostPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java similarity index 87% rename from Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhostPlugin.java rename to Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java index 61f9b54de..d19f260a1 100644 --- a/Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhostPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostPlugin.java @@ -1,5 +1,6 @@ -package core.game.content.quest.free.therestlessghost; +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; @@ -13,12 +14,14 @@ 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 rs09.game.world.GameWorld; +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 static core.api.ContentAPIKt.*; + /** * Represents the restless ghost plugin. * @author 'Vexia @@ -104,10 +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(Quests.THE_RESTLESS_GHOST).setStage(player, 40); break; case 2145: toggleCoffin(player, object); @@ -129,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(); } } @@ -158,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; } @@ -166,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); } @@ -238,7 +242,7 @@ public final class RestlessGhostPlugin extends OptionHandler { } @Override - public boolean isAttackable(final Entity entity, final CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { final Player player = ((Player) entity); final Player pl = getAttribute("player", null); return pl == null ? false : pl == player ? true : false; @@ -256,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 new file mode 100644 index 000000000..4a899c64a --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/RestlessGhostSkull.java @@ -0,0 +1,51 @@ +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.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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class RestlessGhostSkull extends UseWithHandler { + + /** + * Constructs a new {@code RestlessGhostSkull} {@code Object}. + */ + public RestlessGhostSkull() { + super(964); + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + addHandler(2145, OBJECT_TYPE, this); + addHandler(15052, OBJECT_TYPE, this); + addHandler(15061, OBJECT_TYPE, this); + return this; + } + + @Override + public boolean handle(NodeUsageEvent event) { + final Scenery object = (Scenery) event.getUsedWith(); + if (object.getId() == 2145) { + event.getPlayer().getDialogueInterpreter().sendDialogue("Maybe I should open it first."); + return true; + } + if (removeItem(event.getPlayer(), Items.SKULL_964, Container.INVENTORY)) { + event.getPlayer().getPacketDispatch().sendMessage("You put the skull in the coffin."); + event.getPlayer().getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).finish(event.getPlayer()); + } + return true; + } + +} diff --git a/Server/src/main/java/core/game/content/quest/free/therestlessghost/SkullDropPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/SkullDropPlugin.java similarity index 91% rename from Server/src/main/java/core/game/content/quest/free/therestlessghost/SkullDropPlugin.java rename to Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/SkullDropPlugin.java index cb782206c..0fdaf0b68 100644 --- a/Server/src/main/java/core/game/content/quest/free/therestlessghost/SkullDropPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/therestlessghost/SkullDropPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.therestlessghost; +package content.region.misthalin.lumbridge.quest.therestlessghost; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/content/dialogue/AbbotLangleyDialogue.java b/Server/src/main/content/region/misthalin/monastery/dialogue/AbbotLangleyDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/AbbotLangleyDialogue.java rename to Server/src/main/content/region/misthalin/monastery/dialogue/AbbotLangleyDialogue.java index 31004b87e..5c80acfce 100644 --- a/Server/src/main/java/core/game/content/dialogue/AbbotLangleyDialogue.java +++ b/Server/src/main/content/region/misthalin/monastery/dialogue/AbbotLangleyDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.misthalin.monastery.dialogue; +import core.game.dialogue.DialoguePlugin; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/java/core/game/content/dialogue/BrotherJeredDialogue.java b/Server/src/main/content/region/misthalin/monastery/dialogue/BrotherJeredDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/BrotherJeredDialogue.java rename to Server/src/main/content/region/misthalin/monastery/dialogue/BrotherJeredDialogue.java index fe2f91298..e6103d2f7 100644 --- a/Server/src/main/java/core/game/content/dialogue/BrotherJeredDialogue.java +++ b/Server/src/main/content/region/misthalin/monastery/dialogue/BrotherJeredDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.monastery.dialogue; -import core.game.content.global.Skillcape; +import core.game.dialogue.DialoguePlugin; +import core.game.global.Skillcape; import core.game.node.item.Item; import org.rs09.consts.Items; import core.game.node.entity.skill.Skills; diff --git a/Server/src/main/java/core/game/interaction/city/MonasteryPlugin.java b/Server/src/main/content/region/misthalin/monastery/handlers/MonasteryPlugin.java similarity index 91% rename from Server/src/main/java/core/game/interaction/city/MonasteryPlugin.java rename to Server/src/main/content/region/misthalin/monastery/handlers/MonasteryPlugin.java index 299dcb2a7..f430630cc 100644 --- a/Server/src/main/java/core/game/interaction/city/MonasteryPlugin.java +++ b/Server/src/main/content/region/misthalin/monastery/handlers/MonasteryPlugin.java @@ -1,7 +1,7 @@ -package core.game.interaction.city; +package content.region.misthalin.monastery.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; +import core.game.global.action.ClimbActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/quest/members/asoulsbane/ASoulsBane.java b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBane.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/asoulsbane/ASoulsBane.java rename to Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBane.java index 0d729ec88..91f1f33ef 100644 --- a/Server/src/main/java/core/game/content/quest/members/asoulsbane/ASoulsBane.java +++ b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBane.java @@ -4,8 +4,9 @@ package core.game.content.quest.members.asoulsbane; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; -import rs09.game.content.quest.members.asoulsbane.SoulsBaneLaunaDialogue; -import rs09.plugin.PluginManager; +import core.game.content.quest.members.asoulsbane.SoulsBaneLaunaDialogue; +import core.plugin.PluginManager; +import content.data.Quests; */ /** @@ -14,10 +15,8 @@ import rs09.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/java/core/game/content/quest/members/asoulsbane/ASoulsBanePlugin.java b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBanePlugin.java similarity index 98% rename from Server/src/main/java/core/game/content/quest/members/asoulsbane/ASoulsBanePlugin.java rename to Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBanePlugin.java index 51e230d6b..98391ae92 100644 --- a/Server/src/main/java/core/game/content/quest/members/asoulsbane/ASoulsBanePlugin.java +++ b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBanePlugin.java @@ -14,11 +14,11 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.game.node.object.GameObject; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.PluginManager; */ /** diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/asoulsbane/SoulsBaneLaunaDialogue.kt b/Server/src/main/content/region/misthalin/quest/asoulsbane/SoulsBaneLaunaDialogue.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/content/quest/members/asoulsbane/SoulsBaneLaunaDialogue.kt rename to Server/src/main/content/region/misthalin/quest/asoulsbane/SoulsBaneLaunaDialogue.kt index d63935faa..94525db9f 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/asoulsbane/SoulsBaneLaunaDialogue.kt +++ b/Server/src/main/content/region/misthalin/quest/asoulsbane/SoulsBaneLaunaDialogue.kt @@ -1,8 +1,8 @@ /* package rs09.game.content.quest.members.asoulsbane -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression import core.game.content.quest.members.asoulsbane.ASoulsBane import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest diff --git a/Server/src/main/java/core/game/content/dialogue/DoorPerilDialogue.java b/Server/src/main/content/region/misthalin/quest/priestinperil/DoorPerilDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/DoorPerilDialogue.java rename to Server/src/main/content/region/misthalin/quest/priestinperil/DoorPerilDialogue.java index bb18fd294..c21b288a8 100644 --- a/Server/src/main/java/core/game/content/dialogue/DoorPerilDialogue.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/DoorPerilDialogue.java @@ -1,10 +1,13 @@ -package core.game.content.dialogue; +package content.region.misthalin.quest.priestinperil; -import core.game.content.global.action.DoorActionHandler; +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.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. @@ -44,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; @@ -104,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/java/core/game/content/dialogue/DrezelDialogue.java b/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/DrezelDialogue.java rename to Server/src/main/content/region/misthalin/quest/priestinperil/DrezelDialogue.java index 715e83e49..4d06a70d8 100644 --- a/Server/src/main/java/core/game/content/dialogue/DrezelDialogue.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelDialogue.java @@ -1,11 +1,14 @@ -package core.game.content.dialogue; +package content.region.misthalin.quest.priestinperil; import core.cache.def.impl.NPCDefinition; +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 core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for the drezel npc. @@ -47,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; @@ -66,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?"); @@ -109,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: @@ -141,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: @@ -367,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/java/core/game/content/dialogue/DrezelMonumentDialogue.java b/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelMonumentDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/DrezelMonumentDialogue.java rename to Server/src/main/content/region/misthalin/quest/priestinperil/DrezelMonumentDialogue.java index aeb536891..06c77b5cc 100644 --- a/Server/src/main/java/core/game/content/dialogue/DrezelMonumentDialogue.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/DrezelMonumentDialogue.java @@ -1,16 +1,19 @@ -package core.game.content.dialogue; +package content.region.misthalin.quest.priestinperil; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; + +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 core.game.node.item.Item; import org.rs09.consts.NPCs; -import rs09.game.content.quest.members.naturespirit.NSDrezelDialogue; -import rs09.game.system.SystemLogger; +import content.region.morytania.quest.naturespirit.NSDrezelDialogue; -import static rs09.tools.DialogueConstKt.END_DIALOGUE; +import static core.tools.DialogueConstKt.END_DIALOGUE; +import content.data.Quests; /** * Represents the dialogue plugin used for the drezel monument. @@ -50,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; @@ -77,13 +80,13 @@ 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?"); stage = 420; } else if (quest.getStage(player) < 100){ - ContentAPI.openDialogue(player, new NSDrezelDialogue(), npc); + openDialogue(player, new NSDrezelDialogue(), npc); } else { npcl(FacialExpression.NEUTRAL, "I heard you finished your quest with Filliman! Great work!"); stage = END_DIALOGUE; @@ -93,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."); @@ -112,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: @@ -188,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/kotlin/rs09/game/content/quest/members/priestinperil/KingRoaldPIPDialogue.kt b/Server/src/main/content/region/misthalin/quest/priestinperil/KingRoaldPIPDialogue.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/quest/members/priestinperil/KingRoaldPIPDialogue.kt rename to Server/src/main/content/region/misthalin/quest/priestinperil/KingRoaldPIPDialogue.kt index e95c640c9..d8f8f35d8 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/priestinperil/KingRoaldPIPDialogue.kt +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/KingRoaldPIPDialogue.kt @@ -1,8 +1,9 @@ -package rs09.game.content.quest.members.priestinperil +package content.region.misthalin.quest.priestinperil -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +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/java/core/game/node/entity/npc/quest/priest_in_peril/MonkOfZamorakNPC.java b/Server/src/main/content/region/misthalin/quest/priestinperil/MonkOfZamorakNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/quest/priest_in_peril/MonkOfZamorakNPC.java rename to Server/src/main/content/region/misthalin/quest/priestinperil/MonkOfZamorakNPC.java index 59faeea66..9ca6c99be 100644 --- a/Server/src/main/java/core/game/node/entity/npc/quest/priest_in_peril/MonkOfZamorakNPC.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/MonkOfZamorakNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.quest.priest_in_peril; +package content.region.misthalin.quest.priestinperil; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; @@ -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/java/core/game/content/quest/members/priestinperil/PriestInPeril.java b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPeril.java similarity index 98% rename from Server/src/main/java/core/game/content/quest/members/priestinperil/PriestInPeril.java rename to Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPeril.java index 36fcb0a30..c14d08d0d 100644 --- a/Server/src/main/java/core/game/content/quest/members/priestinperil/PriestInPeril.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPeril.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.priestinperil; +package content.region.misthalin.quest.priestinperil; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; @@ -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/java/core/game/interaction/object/PriestInPerilOptionPlugin.java b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilOptionPlugin.java similarity index 85% rename from Server/src/main/java/core/game/interaction/object/PriestInPerilOptionPlugin.java rename to Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilOptionPlugin.java index 03e4f027d..538afabc8 100644 --- a/Server/src/main/java/core/game/interaction/object/PriestInPerilOptionPlugin.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilOptionPlugin.java @@ -1,9 +1,9 @@ -package core.game.interaction.object; +package content.region.misthalin.quest.priestinperil; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.combat.CombatStyle; @@ -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 new file mode 100644 index 000000000..61a36a6c8 --- /dev/null +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/PriestInPerilUseListener.kt @@ -0,0 +1,132 @@ +package content.region.misthalin.quest.priestinperil + +import core.api.* +import core.game.node.item.Item +import org.rs09.consts.Items +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 + * @author Byte + */ +class PriestInPerilUseListener : InteractionListener { + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.MONUMENT_3493) { player, used, _ -> + if (!getAttribute(player, "priest_in_peril:tinderbox", false) && removeItem(player, used)) { + addItem(player, Items.GOLDEN_TINDERBOX_2946) + sendMessage(player, "You swap the tinderbox for the golden tinderbox.") + setAttribute(player, "/save:priest_in_peril:tinderbox", true) + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.CANDLE_36, Scenery.MONUMENT_3494) { player, used, _ -> + if (!getAttribute(player, "priest_in_peril:candle", false) && removeItem(player, used)) { + addItem(player, Items.GOLDEN_CANDLE_2947) + sendMessage(player, "You swap the candle for the golden candle.") + setAttribute(player, "/save:priest_in_peril:candle", true) + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.EMPTY_POT_1931, Scenery.MONUMENT_3495) { player, used, _ -> + if (!getAttribute(player, "priest_in_peril:pot", false) && removeItem(player, used)) { + addItem(player, Items.GOLDEN_POT_2948) + sendMessage(player, "You swap the pot for the golden pot.") + setAttribute(player, "/save:priest_in_peril:pot", true) + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.HAMMER_2347, Scenery.MONUMENT_3496) { player, used, _ -> + if (!getAttribute(player, "priest_in_peril:hammer", false) && removeItem(player, used)) { + addItem(player, Items.GOLDEN_HAMMER_2949) + sendMessage(player, "You swap the hammer for the golden hammer.") + setAttribute(player, "/save:priest_in_peril:hammer", true) + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.FEATHER_314, Scenery.MONUMENT_3497) { player, used, _ -> + if (!getAttribute(player, "priest_in_peril:feather", false) && removeItem(player, Item(used.id, 1))) { + addItem(player, Items.GOLDEN_FEATHER_2950) + sendMessage(player, "You swap the feather for the golden feather.") + setAttribute(player, "/save:priest_in_peril:feather", true) + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.NEEDLE_1733, Scenery.MONUMENT_3498) { player, used, _ -> + if (!getAttribute(player, "priest_in_peril:needle", false) && removeItem(player, Item(used.id, 1))) { + addItem(player, Items.GOLDEN_NEEDLE_2951) + sendMessage(player, "You swap the needle for the golden needle.") + setAttribute(player, "/save:priest_in_peril:needle", true) + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.GOLDEN_KEY_2944, Scenery.MONUMENT_3499) { 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) + } + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.BUCKET_1925, Scenery.WELL_3485) { player, used, _ -> + if (!removeItem(player, used)) { + return@onUseWith false + } + + addItem(player, Items.BUCKET_OF_WATER_2953) + sendMessage(player, "You fill the bucket from the well.") + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.IRON_KEY_2945, Scenery.CELL_DOOR_3463) { player, used, _ -> + if (!removeItem(player, used)) { + return@onUseWith false + } + + 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) + npc.name = "Drezel" + sendNPCDialogue(player, npc.id, "Oh! Thank you! You have found the key!", core.game.dialogue.FacialExpression.HALF_GUILTY) + + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.BUCKET_OF_WATER_2954, Scenery.MORYTANIA_COFFIN_30728) { player, used, _ -> + if (!removeItem(player, used)) { + return@onUseWith false + } + + addItem(player, Items.BUCKET_1925) + 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/java/core/game/node/entity/npc/quest/priest_in_peril/TempleGuardianNPC.java b/Server/src/main/content/region/misthalin/quest/priestinperil/TempleGuardianNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/quest/priest_in_peril/TempleGuardianNPC.java rename to Server/src/main/content/region/misthalin/quest/priestinperil/TempleGuardianNPC.java index 2a05f58fd..23dcd52c7 100644 --- a/Server/src/main/java/core/game/node/entity/npc/quest/priest_in_peril/TempleGuardianNPC.java +++ b/Server/src/main/content/region/misthalin/quest/priestinperil/TempleGuardianNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.quest.priest_in_peril; +package content.region.misthalin.quest.priestinperil; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; @@ -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/java/core/game/content/dialogue/AeonisigRaispher.java b/Server/src/main/content/region/misthalin/varrock/dialogue/AeonisigRaispher.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/AeonisigRaispher.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/AeonisigRaispher.java index 6309ba4ab..b461bbbb8 100644 --- a/Server/src/main/java/core/game/content/dialogue/AeonisigRaispher.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/AeonisigRaispher.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/AmbassadorFernook.java b/Server/src/main/content/region/misthalin/varrock/dialogue/AmbassadorFernook.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/AmbassadorFernook.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/AmbassadorFernook.java index 4cef10e39..4748e933b 100644 --- a/Server/src/main/java/core/game/content/dialogue/AmbassadorFernook.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/AmbassadorFernook.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/ApothecaryDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/ApothecaryDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/ApothecaryDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/ApothecaryDialogue.java index 9277c8322..d9ca490aa 100644 --- a/Server/src/main/java/core/game/content/dialogue/ApothecaryDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ApothecaryDialogue.java @@ -1,276 +1,278 @@ -package core.game.content.dialogue; - -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.GroundItem; -import core.game.node.item.GroundItemManager; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the dialogue plugin used for the apothecary npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ApothecaryDialogue extends DialoguePlugin { - - /** - * Represents the potion requirements. - */ - private static final Item[] POTION_ITEMS = new Item[] { new Item(223), new Item(225), new Item(995, 5) }; - - /** - * Represents the potion item. - */ - private static final Item POTION = new Item(115); - - /** - * Represents the unkown potion. - */ - private static final Item UNKNOWN_POTION = new Item(195, 1); - - /** - * Represents the cadava berries item. - */ - private static final Item CADAVA_BERRIES = new Item(753); - - /** - * Represents the cadava potion item. - */ - private static final Item CADAVA_POTION = new Item(756); - - /** - * Constructs a new {@code ApothecaryDialogue} {@code Object}. - */ - public ApothecaryDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ApothecaryDialogue} {@code Object}. - * @param player the player. - */ - public ApothecaryDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ApothecaryDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (player.getQuestRepository().getQuest("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.getInventory().contains(753, 1)) { - npc("Keep searching for those Cadava berries. They're needed", "for the potion."); - stage = 507; - return true; - } else { - npc("Well done. You have the berries."); - stage = 637; - return true; - } - } - if (player.getQuestRepository().getQuest("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."); - stage = 637; - return true; - } else { - npc("Keep searching for those Cadava berries. They're needed", "for the potion."); - stage = 507; - return true; - } - } else { - npc("I am the Apothecary. I brew potions.", "Do you need anything specific?"); - } - } - npc("I am the Apothecary. I brew potions.", "Do you need anything specific?"); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - options("Can you make a strength potion?", "Do you know a potion to make hair fall out?", "Have you got any good potions to give away?", "Can you make a potion that makes it seem like I'm dead?"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - player("Can you make a strength potion?"); - stage = 10; - break; - case 2: - player("Do you know a potion to make hair fall out?"); - stage = 20; - break; - case 3: - player("Have you got any good potions to give away?"); - stage = 140; - break; - case 4: - player("Can you make a potion that makes it seems like I'm dead?"); - stage = 40; - break; - } - - break; - case 10: - if (player.getInventory().containItems(223, 225)) { - npc("Certainly, just hand over the ingredients and 5 coins."); - stage = 50; - return true; - } - npc("Yes, but the ingredients are a little hard to find. If you", "ever get them I will make it for you, for a fee."); - stage = 11; - break; - case 50: - if (!player.getInventory().contains(995, 5)) { - end(); - player.getPacketDispatch().sendMessage("You need 5 gold coins to do that."); - return true; - } - interpreter.sendDialogue("You hand over the ingredients and money."); - stage = 51; - break; - case 51: - if (player.getInventory().remove(POTION_ITEMS)) { - player.getInventory().add(POTION); - end(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 0); - } - break; - case 11: - player("So what are the ingredients?"); - stage = 12; - break; - case 12: - npc("You'll need to find the eggs of the deadly red spider and a", "limpwurt root."); - stage = 13; - break; - case 13: - npc("Oh and you'll have to pay me 5 coins."); - stage = 14; - break; - case 14: - player("Ok, I'll look out for them."); - stage = 15; - break; - case 15: - end(); - break; - case 20: - npc("I do indeed. I gave it to my mother. That's why I now live", "alone."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - npc("Ok then. Try this potion."); - player.getInventory().add(UNKNOWN_POTION); - stage = 31; - break; - case 31: - end(); - break; - case 40: - npc("What a strange and morbid request! I can as it happens.", "The berry of the cadava bush, prepared properly, will", "induce a coma so deep that you will seem to be dead. It's", "very dangerous."); - stage = 41; - break; - case 41: - npc("I have the other ingredients, but I'll need you to bring me", "one bunch of cadava berries."); - stage = 42; - break; - case 42: - player("I'll bear that in mind."); - stage = 43; - break; - case 43: - end(); - break; - case 140: - npc("Sorry, charity is not my strong point."); - stage = 141; - break; - case 141: - end(); - break; - case 500: - interpreter.sendDialogues(player, null, "I need a Cadava potion to help Romeo and Juliet."); - stage = 501; - break; - case 501: - interpreter.sendDialogues(npc, null, "Cadava potion. It's pretty nasty. And hard to make."); - stage = 502; - break; - case 502: - interpreter.sendDialogues(npc, null, "Wing of rat, tail of frog.", "Ear of snake and horn of dog."); - stage = 503; - break; - case 503: - interpreter.sendDialogues(npc, null, "I have all that, but I need some Cadava berries."); - stage = 504; - break; - case 504: - interpreter.sendDialogues(npc, null, "You will have to find them while I get the rest ready."); - stage = 505; - break; - case 505: - interpreter.sendDialogues(npc, null, "Bring them here when you have them. But be careful.", "They are nasty."); - stage = 506; - break; - case 506: - player.getQuestRepository().getQuest("Romeo & Juliet").setStage(player, 50); - interpreter.sendDialogues(player, null, "Ok, thanks."); - stage = 507; - break; - case 507: - end(); - break; - case 637: - interpreter.sendItemMessage(753, "You hand over the berries."); - stage = 638; - break; - case 638: - if (player.getInventory().remove(CADAVA_BERRIES)) { - npc("Phew! Here is what you need."); - } - stage = 639; - break; - case 639: - if (!player.getInventory().add(CADAVA_POTION)) { - GroundItemManager.create(new GroundItem(CADAVA_POTION, player.getLocation(), player)); - } - interpreter.sendItemMessage(756, "The Apothecary gives you a Cadava potion."); - stage = 640; - break; - case 640: - player.getQuestRepository().getQuest("Romeo & Juliet").setStage(player, 60); - end(); - break; - } - - return true; - } - - @Override - public int[] getIds() { - return new int[] { 638 }; - } -} +package content.region.misthalin.varrock.dialogue; + +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.diary.DiaryType; +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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class ApothecaryDialogue extends DialoguePlugin { + + /** + * Represents the potion requirements. + */ + private static final Item[] POTION_ITEMS = new Item[] { new Item(223), new Item(225), new Item(995, 5) }; + + /** + * Represents the potion item. + */ + private static final Item POTION = new Item(115); + + /** + * Represents the unkown potion. + */ + private static final Item UNKNOWN_POTION = new Item(195, 1); + + /** + * Represents the cadava berries item. + */ + private static final Item CADAVA_BERRIES = new Item(753); + + /** + * Represents the cadava potion item. + */ + private static final Item CADAVA_POTION = new Item(756); + + /** + * Constructs a new {@code ApothecaryDialogue} {@code Object}. + */ + public ApothecaryDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code ApothecaryDialogue} {@code Object}. + * @param player the player. + */ + public ApothecaryDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new ApothecaryDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + 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(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; + return true; + } else { + npc("Well done. You have the berries."); + stage = 637; + return true; + } + } + 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."); + stage = 637; + return true; + } else { + npc("Keep searching for those Cadava berries. They're needed", "for the potion."); + stage = 507; + return true; + } + } else { + npc("I am the Apothecary. I brew potions.", "Do you need anything specific?"); + } + } + npc("I am the Apothecary. I brew potions.", "Do you need anything specific?"); + stage = 1; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 1: + options("Can you make a strength potion?", "Do you know a potion to make hair fall out?", "Have you got any good potions to give away?", "Can you make a potion that makes it seem like I'm dead?"); + stage = 2; + break; + case 2: + switch (buttonId) { + case 1: + player("Can you make a strength potion?"); + stage = 10; + break; + case 2: + player("Do you know a potion to make hair fall out?"); + stage = 20; + break; + case 3: + player("Have you got any good potions to give away?"); + stage = 140; + break; + case 4: + player("Can you make a potion that makes it seems like I'm dead?"); + stage = 40; + break; + } + + break; + case 10: + if (player.getInventory().containItems(223, 225)) { + npc("Certainly, just hand over the ingredients and 5 coins."); + stage = 50; + return true; + } + npc("Yes, but the ingredients are a little hard to find. If you", "ever get them I will make it for you, for a fee."); + stage = 11; + break; + case 50: + if (!player.getInventory().contains(995, 5)) { + end(); + player.getPacketDispatch().sendMessage("You need 5 gold coins to do that."); + return true; + } + interpreter.sendDialogue("You hand over the ingredients and money."); + stage = 51; + break; + case 51: + if (player.getInventory().remove(POTION_ITEMS)) { + player.getInventory().add(POTION); + end(); + player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 0); + } + break; + case 11: + player("So what are the ingredients?"); + stage = 12; + break; + case 12: + npc("You'll need to find the eggs of the deadly red spider and a", "limpwurt root."); + stage = 13; + break; + case 13: + npc("Oh and you'll have to pay me 5 coins."); + stage = 14; + break; + case 14: + player("Ok, I'll look out for them."); + stage = 15; + break; + case 15: + end(); + break; + case 20: + npc("I do indeed. I gave it to my mother. That's why I now live", "alone."); + stage = 21; + break; + case 21: + end(); + break; + case 30: + npc("Ok then. Try this potion."); + player.getInventory().add(UNKNOWN_POTION); + stage = 31; + break; + case 31: + end(); + break; + case 40: + npc("What a strange and morbid request! I can as it happens.", "The berry of the cadava bush, prepared properly, will", "induce a coma so deep that you will seem to be dead. It's", "very dangerous."); + stage = 41; + break; + case 41: + npc("I have the other ingredients, but I'll need you to bring me", "one bunch of cadava berries."); + stage = 42; + break; + case 42: + player("I'll bear that in mind."); + stage = 43; + break; + case 43: + end(); + break; + case 140: + npc("Sorry, charity is not my strong point."); + stage = 141; + break; + case 141: + end(); + break; + case 500: + interpreter.sendDialogues(player, null, "I need a Cadava potion to help Romeo and Juliet."); + stage = 501; + break; + case 501: + interpreter.sendDialogues(npc, null, "Cadava potion. It's pretty nasty. And hard to make."); + stage = 502; + break; + case 502: + interpreter.sendDialogues(npc, null, "Wing of rat, tail of frog.", "Ear of snake and horn of dog."); + stage = 503; + break; + case 503: + interpreter.sendDialogues(npc, null, "I have all that, but I need some Cadava berries."); + stage = 504; + break; + case 504: + interpreter.sendDialogues(npc, null, "You will have to find them while I get the rest ready."); + stage = 505; + break; + case 505: + interpreter.sendDialogues(npc, null, "Bring them here when you have them. But be careful.", "They are nasty."); + stage = 506; + break; + case 506: + player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).setStage(player, 50); + interpreter.sendDialogues(player, null, "Ok, thanks."); + stage = 507; + break; + case 507: + end(); + break; + case 637: + interpreter.sendItemMessage(753, "You hand over the berries."); + stage = 638; + break; + case 638: + if (player.getInventory().remove(CADAVA_BERRIES)) { + npc("Phew! Here is what you need."); + } + stage = 639; + break; + case 639: + if (!player.getInventory().add(CADAVA_POTION)) { + GroundItemManager.create(new GroundItem(CADAVA_POTION, player.getLocation(), player)); + } + interpreter.sendItemMessage(756, "The Apothecary gives you a Cadava potion."); + stage = 640; + break; + case 640: + player.getQuestRepository().getQuest(Quests.ROMEO_JULIET).setStage(player, 60); + end(); + break; + } + + return true; + } + + @Override + public int[] getIds() { + return new int[] { 638 }; + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/AsyffDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/AsyffDialogue.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/content/dialogue/AsyffDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/dialogue/AsyffDialogue.kt index 214231252..0ab285bf6 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AsyffDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/AsyffDialogue.kt @@ -1,8 +1,8 @@ -package rs09.game.content.dialogue +package content.region.misthalin.varrock.dialogue import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/java/core/game/content/dialogue/BaraekDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/BaraekDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/BaraekDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/BaraekDialogue.java index d1ce7f83c..ab062f4b4 100644 --- a/Server/src/main/java/core/game/content/dialogue/BaraekDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/BaraekDialogue.java @@ -1,11 +1,13 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +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.GroundItemManager; import core.plugin.Initializable; import core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue plugin used for the baraek npc. @@ -60,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/java/core/game/content/dialogue/BennyDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/BennyDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/BennyDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/BennyDialogue.java index a1852acf8..9b039425b 100644 --- a/Server/src/main/java/core/game/content/dialogue/BennyDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/BennyDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +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.diary.DiaryType; diff --git a/Server/src/main/java/core/game/content/dialogue/BobBarterDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/BobBarterDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/BobBarterDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/BobBarterDialogue.java index 1178c0166..140f4023d 100644 --- a/Server/src/main/java/core/game/content/dialogue/BobBarterDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/BobBarterDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.ge.GEGuidePrice; import core.game.ge.GEGuidePrice.GuideType; import core.game.node.entity.npc.NPC; diff --git a/Server/src/main/java/core/game/content/dialogue/DrHarlowDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/DrHarlowDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/dialogue/DrHarlowDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/DrHarlowDialogue.java index 1ba632726..0e14e004f 100644 --- a/Server/src/main/java/core/game/content/dialogue/DrHarlowDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/DrHarlowDialogue.java @@ -1,11 +1,14 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; 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. @@ -54,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; @@ -65,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); @@ -120,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: @@ -129,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/java/core/game/content/dialogue/DraulLeptoc.java b/Server/src/main/content/region/misthalin/varrock/dialogue/DraulLeptoc.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/DraulLeptoc.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/DraulLeptoc.java index 9cb1c352f..2b9875c62 100644 --- a/Server/src/main/java/core/game/content/dialogue/DraulLeptoc.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/DraulLeptoc.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/EllamariaDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/EllamariaDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/EllamariaDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/EllamariaDialogue.java index d6a47fd94..eda264a1a 100644 --- a/Server/src/main/java/core/game/content/dialogue/EllamariaDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/EllamariaDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/ElsieDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/ElsieDialogue.kt new file mode 100644 index 000000000..cef36f068 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ElsieDialogue.kt @@ -0,0 +1,212 @@ +package content.region.misthalin.varrock.dialogue + +import core.api.inInventory +import core.api.removeItem +import core.api.sendItemDialogue +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 core.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +@Initializable +class ElsieDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return ElsieDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + if (hasTea()) { + npcl( + FacialExpression.FRIENDLY, + "Ooh - that looks like a lovely cup of tea, dearie. Is it for me?" + ).also { stage = 10 } + } else { + npcl( + FacialExpression.FRIENDLY, + "Hello dearie! What can old Elsie do for you?" + ) + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> showTopics( + 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( + FacialExpression.FRIENDLY, + "I'm knitting a new stole for Father Lawrence downstairs." + ).also { stage++ } + + 2 -> npcl( + FacialExpression.FRIENDLY, + "He could do with something to keep his neck warm, standing in " + + "that draughty old church all day." + ).also { stage = START_DIALOGUE } + + 3 -> { + if (hasTea()) { + npcl( + FacialExpression.HALF_THINKING, + "Perhaps... Can I have that lovely cup of tea you have over there?" + ).also { stage = 10 } + } else { + npcl( + FacialExpression.NEUTRAL, + "Maybe I could tell you a story if you'd fetch me a nice cup of tea." + ).also { stage++ } + } + } + + 4 -> playerl( + FacialExpression.ROLLING_EYES, + "I'll think about it." + ).also { stage = START_DIALOGUE } + + 5 -> npcl( + 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( + 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( + 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( + 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( + FacialExpression.ROLLING_EYES, + "Thanks, old woman." + ).also { stage = START_DIALOGUE } + + 10 -> showTopics( + Topic(FacialExpression.HAPPY, "Yes, you can have it.", 12), + Topic(FacialExpression.ANNOYED, "No, keep your hands off my tea.", 11) + ) + + 11 -> npcl( + FacialExpression.SAD, + "Aww. Maybe another time, then... Anyway, what can old Elsie do for you?" + ).also { stage = START_DIALOGUE } + + 12 -> sendItemDialogue(player, Items.CUP_OF_TEA_712, "Elsie takes a sip from the cup of tea.") + .also { stage++; removeItem(player, Items.CUP_OF_TEA_712) } + + 13 -> npcl( + 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( + 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( + 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( + FacialExpression.NEUTRAL, + "But young Dissy - that was the young man's nickname - he was a wild young thing." + ).also { stage++ } + + 17 -> npcl( + 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( + 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( + FacialExpression.LAUGH, + "The guards told us that someone had painted 'Saradomin pwns' on the wall of Zamorakian temple!" + ).also { stage++ } + + 20 -> npcl( + FacialExpression.HALF_THINKING, + "Now, we'd always been taught to keep well away from that dreadful place..." + ).also { stage++ } + + 21 -> npcl( + 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( + 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( + FacialExpression.HALF_GUILTY, + "It was such a shame, he had the most handsome... Shoulders..." + ).also { stage++ } + + 24 -> npcl( + 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( + 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( + FacialExpression.HAPPY, + "Well, until I met my Freddie and we got married. But that's another story." + ).also { stage++ } + + 27 -> playerl( + FacialExpression.FRIENDLY, + "Thank you. I'll leave you to your knitting now." + ).also { stage = END_DIALOGUE } + } + + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.ELSIE_5915) + + private fun hasTea(): Boolean { + return inInventory(player, Items.CUP_OF_TEA_712) + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/FaridMorrisaneDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/FaridMorrisaneDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/FaridMorrisaneDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/FaridMorrisaneDialogue.java index ce470c3e3..d7774b747 100644 --- a/Server/src/main/java/core/game/content/dialogue/FaridMorrisaneDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/FaridMorrisaneDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.ge.GEGuidePrice; import core.game.ge.GEGuidePrice.GuideType; diff --git a/Server/src/main/java/core/game/content/dialogue/FatherLawrenceDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/FatherLawrenceDialogue.java similarity index 86% rename from Server/src/main/java/core/game/content/dialogue/FatherLawrenceDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/FatherLawrenceDialogue.java index 192b9f662..db0f198ad 100644 --- a/Server/src/main/java/core/game/content/dialogue/FatherLawrenceDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/FatherLawrenceDialogue.java @@ -1,181 +1,184 @@ -package core.game.content.dialogue; - -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; - -/** - * Represents the father lawrence dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FatherLawrenceDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code FatherLawrenceDialogue} {@code Object}. - */ - public FatherLawrenceDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FatherLawrenceDialogu} {@code Object}. - * @param player the player. - */ - public FatherLawrenceDialogue(Player player) { - super(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("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; - } - switch (quest.getStage(player)) { - case 30: - interpreter.sendDialogues(npc, null, "\"...and let Saradomin light the way for you... \"", "Urgh!"); - stage = 41; - break; - case 40: - interpreter.sendDialogues(npc, null, "Ah, have you found the Apothecary yet? Remember,", "Cadava potion, for Juliet."); - stage = 30; - break; - case 50: - interpreter.sendDialogues(npc, null, "Did you find the Apothecary?"); - stage = 820; - break; - case 60: - case 70: - interpreter.sendDialogues(npc, null, "Did you find the Apothecary?"); - stage = 820; - break; - case 100: - player("Hi again!"); - stage = 0; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Romeo & Juliet"); - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I sing and I drink and I wake up in gutters."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Top of the morning to you."); - stage = 2; - break; - case 2: - end(); - break; - case 41: - interpreter.sendDialogues(npc, null, "Can't you see that I'm in the middle of a sermon?!"); - stage = 42; - break; - case 42: - interpreter.sendDialogues(player, null, "But Romeo sent me!"); - stage = 43; - break; - case 43: - interpreter.sendDialogues(npc, null, "But I'm busy delivering a sermon to my congregation!"); - stage = 44; - break; - case 44: - interpreter.sendDialogues(player, null, "Yes well it certainly seems like you have a captive", "audience!"); - stage = 45; - break; - case 45: - interpreter.sendDialogues(npc, null, "Ok, ok...what do you want so I can get rid of you and", "continue with my sermon?"); - stage = 46; - break; - case 46: - interpreter.sendDialogues(player, null, "Romeo sent me. He says you may be able to help."); - stage = 47; - break; - case 47: - interpreter.sendDialogues(npc, null, "Ah Romeo, yes. A fine lad, but a little bit confused."); - 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!"); - stage = 49; - break; - case 49: - interpreter.sendDialogues(npc, null, "I agree, and I think I have an idea! A potion to make", "her appear dead..."); - stage = 50; - break; - case 50: - interpreter.sendDialogues(player, null, "Dead! Sounds a bit creepy to me...but please, continue."); - stage = 51; - break; - case 51: - interpreter.sendDialogues(npc, null, "The potion will only make Juliet 'appear' dead...then", "she'll be taken to the crypt..."); - stage = 52; - break; - case 52: - interpreter.sendDialogues(player, null, "Crypt! Again...very creepy! You must have some", "strange hobbies."); - 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."); - stage = 54; - break; - case 54: - interpreter.sendDialogues(player, null, "Apart from the strong overtones of death, this is", "turning out to be a real love story."); - stage = 55; - break; - case 55: - quest.setStage(player, 40); - end(); - break; - case 30: - end(); - break; - case 820: - interpreter.sendDialogues(player, null, "Yes I did. He's told me I must find some Cadava", "berries."); - stage = 821; - break; - case 821: - interpreter.sendDialogues(npc, null, "Well, good luck with that...they're quite tricky to find."); - stage = 822; - break; - case 822: - interpreter.sendDialogues(player, null, "Any clues where I can start to look?"); - stage = 823; - break; - case 823: - interpreter.sendDialogues(npc, null, "I heard some kids saying they saw some the other day.", "They were visiting the mining place to the south east", "Varrock."); - stage = 824; - break; - case 824: - interpreter.sendDialogues(player, null, "Ok, that's as good a place to start looking as any."); - stage = 825; - break; - case 825: - end(); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FatherLawrenceDialogue(player); - } - - @Override - public int[] getIds() { - return new int[] { 640 }; - } -} +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.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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class FatherLawrenceDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code FatherLawrenceDialogue} {@code Object}. + */ + public FatherLawrenceDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code FatherLawrenceDialogu} {@code Object}. + * @param player the player. + */ + public FatherLawrenceDialogue(Player player) { + super(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + 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; + } + switch (quest.getStage(player)) { + case 30: + interpreter.sendDialogues(npc, null, "\"...and let Saradomin light the way for you... \"", "Urgh!"); + stage = 41; + break; + case 40: + interpreter.sendDialogues(npc, null, "Ah, have you found the Apothecary yet? Remember,", "Cadava potion, for Juliet."); + stage = 30; + break; + case 50: + interpreter.sendDialogues(npc, null, "Did you find the Apothecary?"); + stage = 820; + break; + case 60: + case 70: + interpreter.sendDialogues(npc, null, "Did you find the Apothecary?"); + stage = 820; + break; + case 100: + player("Hi again!"); + stage = 0; + break; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + 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."); + stage = 1; + break; + case 1: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Top of the morning to you."); + stage = 2; + break; + case 2: + end(); + break; + case 41: + interpreter.sendDialogues(npc, null, "Can't you see that I'm in the middle of a Sermon?!"); + stage = 42; + break; + case 42: + interpreter.sendDialogues(player, null, "But Romeo sent me!"); + stage = 43; + break; + case 43: + interpreter.sendDialogues(npc, null, "But I'm busy delivering a sermon to my congregation!"); + stage = 44; + break; + case 44: + interpreter.sendDialogues(player, null, "Yes, well, it certainly seems like you have a captive", "audience!"); + stage = 45; + break; + case 45: + interpreter.sendDialogues(npc, null, "Ok, ok...what do you want so I can get rid of you and", "continue with my sermon?"); + stage = 46; + break; + case 46: + interpreter.sendDialogues(player, null, "Romeo sent me. He says you may be able to help."); + stage = 47; + break; + case 47: + interpreter.sendDialogues(npc, null, "Ah Romeo, yes. A fine lad, but a little bit confused."); + 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!"); + stage = 49; + break; + case 49: + interpreter.sendDialogues(npc, null, "I agree, and I think I have an idea! A potion to make", "her appear dead..."); + stage = 50; + break; + case 50: + interpreter.sendDialogues(player, null, "Dead! Sounds a bit creepy to me...but please, continue."); + stage = 51; + break; + case 51: + interpreter.sendDialogues(npc, null, "The potion will only make Juliet 'appear' dead...then", "she'll be taken to the crypt..."); + stage = 52; + break; + case 52: + interpreter.sendDialogues(player, null, "Crypt! Again...very creepy! You must have some", "strange hobbies."); + stage = 53; + break; + case 53: + 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: + interpreter.sendDialogues(player, null, "Apart from the strong overtones of death, this is", "turning out to be a real love story."); + stage = 55; + break; + case 55: + quest.setStage(player, 40); + end(); + break; + case 30: + end(); + break; + case 820: + interpreter.sendDialogues(player, null, "Yes I did. He's told me I must find some Cadava", "berries."); + stage = 821; + break; + case 821: + interpreter.sendDialogues(npc, null, "Well, good luck with that...they're quite tricky to find."); + stage = 822; + break; + case 822: + interpreter.sendDialogues(player, null, "Any clues where I can start to look?"); + stage = 823; + break; + case 823: + interpreter.sendDialogues(npc, null, "I heard some kids saying they saw some the other day.", "They were visiting the mining place to the south east", "Varrock."); + stage = 824; + break; + case 824: + interpreter.sendDialogues(player, null, "Ok, that's as good a place to start looking as any."); + stage = 825; + break; + case 825: + end(); + break; + } + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new FatherLawrenceDialogue(player); + } + + @Override + public int[] getIds() { + return new int[] { 640 }; + } +} diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudeDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudeDialogue.java new file mode 100644 index 000000000..e9a4ea493 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudeDialogue.java @@ -0,0 +1,430 @@ +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; +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.Initializable; +import core.tools.RandomFunction; +import org.rs09.consts.Items; + +import content.data.Quests; + +import static core.api.ContentAPIKt.*; + +/** + * Represents the gertrude dialogue plugin. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class GertrudeDialogue extends DialoguePlugin { + + /** + * Represents the coins item. + */ + private static final Item COINS = new Item(995, 100); + + /** + * Constructs a new {@code GertrudeDialogue} {@code Object}. + */ + public GertrudeDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code GertrudeDialogue} {@code Object}. + * @param player the player. + */ + public GertrudeDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new GertrudeDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + switch (getQuestStage(player, Quests.GERTRUDES_CAT)) { + case 0: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, are you OK?"); + break; + case 10: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, Gertrude."); + stage = 210; + break; + case 20: + 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, FacialExpression.HALF_WORRIED, "Please bring me my cat back!"); + stage = 235; + break; + case 40: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Gertrude."); + stage = 300; + break; + case 60: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Gertrude. Fluffs ran off with her kitten."); + stage = 320; + break; + case 100: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); + stage = 500; + break; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + 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."); + stage = 1; + break; + case 1: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm sorry. It's just that I've lost her."); + stage = 2; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Lost who?"); + stage = 3; + break; + case 3: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Fluffs, poor Fluffs. She never hurt anyone."); + stage = 4; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who's Fluffs?"); + stage = 5; + break; + case 5: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "My beloved feline friends Fluffs. She's been purring by", "my side for almost a decade. Please, could you go", "search for her while I look over the kids?"); + stage = 6; + break; + case 6: + interpreter.sendOptions("Select an Option", "Well, I suppose I could.", "Sorry, I'm too busy to play pet rescue."); + stage = 7; + break; + case 7: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, I suppose I could."); + stage = 100; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I'm to busy too play pet rescue."); + stage = 200; + break; + } + break; + case 100: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Really? Thank you so much! I really have no idea", "where she could be!"); + stage = 101; + break; + case 101: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I think my sons, Shilop and Wilough, saw the cat last.", "They'll be out in the market place."); + stage = 102; + break; + case 102: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Alright then, I'll see what I can do."); + stage = 103; + break; + case 103: + quest.start(player); + player.getQuestRepository().syncronizeTab(player); + end(); + break; + case 200: + end(); + break; + case 210:// start of stage 10 + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Have you seen my poor Fluffs?"); + stage = 211; + break; + case 211: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm afraid not."); + stage = 212; + break; + case 212: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "What about Shilop?"); + stage = 213; + break; + case 213: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No sign of him either."); + stage = 214; + break; + case 214: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmmm...strange, he should be at the market."); + stage = 215; + break; + case 215: + end(); + break; + case 230: + 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, FacialExpression.HALF_GUILTY, "He does seem quite a handful."); + stage = 232; + break; + case 232: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You have no idea! Did he help at all?"); + stage = 233; + break; + case 233: + interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "I think so, I'm just going to look now."); + stage = 234; + break; + case 234: + 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, FacialExpression.HALF_GUILTY, "Hi! Did you find fluffs?"); + stage = 301; + break; + case 301: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes! But she won't follow me."); + stage = 302; + break; + case 302: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You should try tempting her with a", "seasoned sardine! Those are her favourite snacks."); + stage = 303; + break; + case 303: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks for the advice!"); + stage = 304; + break; + case 304: + end(); + break; + case 320: + 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: + interpreter.sendDialogue("Gertrude gives you a hug."); + npc.face(player); + stage = 322; + break; + case 322: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "If you hadn't found her kitten it would have died out", "there!"); + stage = 323; + break; + case 323: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's OK, I like to do my bit."); + stage = 324; + break; + case 324: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I don't know how to thank you. I have no real material", "possesions. I do have kittens! I can only really look", "after one."); + stage = 325; + break; + case 325: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, if it needs a home."); + stage = 326; + break; + case 326: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I would sell it to my cousin in West Ardougne. I hear", "there's a rat epidemic there. But it's too far."); + stage = 327; + break; + case 327: + if (player.getInventory().freeSlots() == 0) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't seem to have enough inventory space."); + stage = 1000; + break; + } else { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here you go, look after her and thank you again!"); + stage = 328; + } + break; + case 328: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh by the way, the kitten can live in your backpack,", "but to make it grow you must take it out and feed and", "stroke it often."); + stage = 329; + break; + case 329: + interpreter.sendDialogue("Gertrude gives you a kitten."); + stage = 330; + break; + case 330: + player.getPacketDispatch().sendMessage("...and some food!"); + end(); + quest.finish(player); + player.getQuestRepository().syncronizeTab(player); + break; + case 331: + break; + case 1000: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Good good, See you again."); + stage = 1001; + break; + case 1001: + end(); + break; + case 500: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Hello my dear. How's things?"); + stage = 501; + break; + case 501: + interpreter.sendOptions("Select an Option", "I'm fine, thanks.", "Do you have any more kittens?"); + stage = 502; + break; + case 502: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm fine, thanks."); + stage = 1000; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Do you have any more kittens?"); + stage = 503; + break; + } + break; + case 503: + boolean has = false; + int[] kittens = new int[] { 1555, 1556, 1557, 1558, 1559, 1560, 7583 }; + 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; + } + } + if (has) { + interpreter.sendDialogues(npc, null, "Aren't you still raising that other kitten? Only once it's", "fully grown and it no longer needs your attention will", "I let you have another kitten."); + stage = 505; + } else { + interpreter.sendDialogues(npc, null, "Indeed I have. They are 100 coins each, do you want", "one?"); + stage = 900; + } + break; + case 900: + interpreter.sendOptions("Select an Option", "Yes please.", "No thanks."); + stage = 901; + break; + case 901: + switch (buttonId) { + case 1:// yes + boolean hasMoney = inInventory(player, Items.COINS_995, 100); + if (!hasMoney) { + player.getPacketDispatch().sendMessage("You need 100 coins to buy a kitten."); + end(); + break; + } else { + 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; + } + interpreter.sendDialogues(player, null, "Yes please."); + stage = 903; + } + break; + case 2:// no + end(); + break; + } + break; + case 903: + interpreter.sendDialogues(npc, null, "OK then, here you go."); + stage = 904; + break; + case 904: + interpreter.sendDialogues(player, null, "Thanks."); + stage = 905; + break; + case 905: + if (player.getInventory().remove(COINS)) { + interpreter.sendDialogue("Gertrude gives you another kitten."); + stage = 906; + final Item kitten = getKitten(); + if (player.getFamiliarManager().hasFamiliar()) { + player.getInventory().add(kitten); + } else { + player.getFamiliarManager().summon(kitten, true, false); + } + } + break; + case 906: + end(); + break; + case 505: + end(); + break; + } + return true; + } + + /** + * Method used to get a random kitten. + * @return the item. + */ + public Item getKitten() { + return new Item(RandomFunction.random(1555, 1561)); + } + + @Override + public int[] getIds() { + return new int[] { 780 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/GertrudesCatDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudesCatDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/GertrudesCatDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/GertrudesCatDialogue.java index f9a8f35d0..93d5a003e 100644 --- a/Server/src/main/java/core/game/content/dialogue/GertrudesCatDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/GertrudesCatDialogue.java @@ -1,12 +1,14 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +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.system.task.Pulse; -import rs09.game.world.GameWorld; +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. @@ -52,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 new file mode 100644 index 000000000..6c00a6bae --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/GrandExchangeClerk.java @@ -0,0 +1,136 @@ +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; +import core.game.ge.GrandExchangeRecords; +import content.global.handlers.iface.ge.ExchangeItemSets; +import content.global.handlers.iface.ge.StockMarket; + +/** + * Handles the GrandExchangeClerk dialogue. + * @author 'Vexia + * @author Emperor + * @version 1.0 + */ +@Initializable +public final class GrandExchangeClerk extends DialoguePlugin { + + /** + * Constructs a new {@code GrandExchangeClerk} {@code Object}. + */ + public GrandExchangeClerk() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code GrandExchangeClerk} {@code Object}. + * @param player The player. + */ + public GrandExchangeClerk(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new GrandExchangeClerk(player); + } + + @Override + public boolean open(Object... args) { + if (args.length > 0 && args[0] instanceof NPC) { + npc = (NPC) args[0]; + } + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi there."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + String salutation = player.isMale() ? "sir" : "madam"; + switch (stage) { + case 0: + npc("Good day to you, " + salutation + ", how can I help?"); + stage = 1; + break; + case 1: + interpreter.sendOptions("Select an Option", "I want to access the Grand Exchange, please.", "I want to collect my items.", "Can I see a history of my offers?", "Can you help me with item sets?", "I'm fine, actually."); + stage = 2; + break; + case 2: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I want to access the Grand Exchange, please."); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I want to collect my items."); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I see history of my offers?"); + stage = 30; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you help me with item sets?"); + stage = 40; + break; + case 5: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm fine, actually."); + stage = 50; + break; + } + break; + case 10: + npc("Only too happy to help you, " + salutation + "."); + stage = 11; + break; + case 11: + end(); + StockMarket.openFor(player); + break; + case 20: + npc("As you wish, " + salutation + "."); + stage = 21; + break; + case 21: + end(); + GrandExchangeRecords.getInstance(player).openCollectionBox(); + break; + case 30: + npc("If that is your wish."); + stage = 31; + break; + case 31: + end(); + GrandExchangeRecords.getInstance(player).openHistoryLog(player); + break; + case 40: + npc("It would be my pleasure, " + salutation + "."); + stage = 41; + break; + case 41: + end(); + ExchangeItemSets.openFor(player); + break; + case 50: + npc("If you say so, " + salutation + "."); + stage = 51; + break; + case 51: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 6528, 6529, 6530, 6531 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/GrandExchangeTutor.java b/Server/src/main/content/region/misthalin/varrock/dialogue/GrandExchangeTutor.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/GrandExchangeTutor.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/GrandExchangeTutor.java index 63ff47cf7..0c5369107 100644 --- a/Server/src/main/java/core/game/content/dialogue/GrandExchangeTutor.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/GrandExchangeTutor.java @@ -1,9 +1,11 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; import core.plugin.Initializable; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * Represents the dialogue plugin used for the grand exchange tutor. diff --git a/Server/src/main/java/core/game/content/dialogue/GuidorsWifeDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/GuidorsWifeDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/dialogue/GuidorsWifeDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/GuidorsWifeDialogue.java index 0c3a8a0fb..5c6666631 100644 --- a/Server/src/main/java/core/game/content/dialogue/GuidorsWifeDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/GuidorsWifeDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/HeadChefDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/HeadChefDialogue.java new file mode 100644 index 000000000..2e0527b0a --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/HeadChefDialogue.java @@ -0,0 +1,169 @@ +package content.region.misthalin.varrock.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.plugin.Initializable; +import core.game.node.entity.player.Player; + +/** + * Represents the dialogue plugin used for the head chef. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class HeadChefDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code HeadChefDialogue} {@code Object}. + */ + public HeadChefDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code HeadChefDialogue} {@code Object}. + * @param player the player. + */ + public HeadChefDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new HeadChefDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + boolean door = false; + if (args.length == 2) + door = (boolean) args[1]; + if (door) { + 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.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.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; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + + switch (stage) { + case 0: + end(); + break; + case 1: + interpreter.sendOptions("Select an Option", "Nice cape you're wearing!", "Thanks, bye."); + stage = 2; + break; + case 2: + + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Nice cape, you're wearing!"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Thanks, bye."); + stage = 3; + break; + } + + break; + case 3: + end(); + break; + case 10: + 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: + end(); + break; + case 100: + interpreter.sendOptions("Select an Option", "No thanks.", "Yes please."); + stage = 101; + break; + case 101: + + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No thanks."); + stage = 110; + break; + case 2: + player("Yes, please."); + stage = 150; + break; + } + break; + + case 110: + interpreter.sendDialogues(847, FacialExpression.HAPPY, "Okay, come back to me if you change your mind."); + stage = 111; + break; + case 111: + end(); + break; + case 150: + interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Most certainly, just as soon as you give me 99000 gold", "coins."); + stage = 151; + break; + case 151: + interpreter.sendOptions("Select an Option", "That's much too expensive.", "Sure."); + stage = 152; + break; + case 152: + + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "That's much too expensive."); + stage = 160; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Sure."); + stage = 200; + break; + } + + break; + case 160: + interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "I'm sorry you feel that way."); + stage = 161; + break; + case 161: + end(); + break; + case 200: + if (Skillcape.purchase(player, Skills.COOKING)) { + interpreter.sendDialogues(847, FacialExpression.HAPPY, "Now you can use the title Master Chef."); + } + stage = 202; + break; + case 202: + end(); + break; + } + + return true; + } + + @Override + public int[] getIds() { + return new int[] { 847 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/HofutThandDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/HofutThandDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/HofutThandDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/HofutThandDialogue.java index e6379a4b2..2edc88ece 100644 --- a/Server/src/main/java/core/game/content/dialogue/HofutThandDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/HofutThandDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.ge.GEGuidePrice; import core.game.ge.GEGuidePrice.GuideType; diff --git a/Server/src/main/java/core/game/content/dialogue/HooknosedJackDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/HooknosedJackDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/dialogue/HooknosedJackDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/HooknosedJackDialogue.java index e7839a768..48504a7c7 100644 --- a/Server/src/main/java/core/game/content/dialogue/HooknosedJackDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/HooknosedJackDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/HorvikDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/HorvikDialogue.java new file mode 100644 index 000000000..d9125024a --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/HorvikDialogue.java @@ -0,0 +1,84 @@ +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; + +/** + * Represents the horvik dialogue plugin. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class HorvikDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code HorvikDialogue} {@code Object}. + */ + public HorvikDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code HorvikDialogue} {@code Object}. + * @param player the player. + */ + public HorvikDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new HorvikDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello, do you need any help?"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "No, thanks. I'm just looking around.", "Do you want to trade?"); + stage = 1; + break; + case 1: + + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No, thanks. I'm just looking around."); + stage = 10; + break; + case 2: + end(); + npc.openShop(player); + break; + } + + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Well, come and see me if you're ever in need of armour!"); + stage = 11; + break; + case 11: + end(); + break; + } + + return true; + } + + @Override + public int[] getIds() { + return new int[] { 549 }; + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/IffieDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/IffieDialogue.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/content/dialogue/IffieDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/dialogue/IffieDialogue.kt index 8cb3b2f4d..2ebebdf69 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/IffieDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/IffieDialogue.kt @@ -1,6 +1,6 @@ -package rs09.game.content.dialogue +package content.region.misthalin.varrock.dialogue -import core.game.content.dialogue.DialoguePlugin +import core.game.dialogue.DialoguePlugin import core.game.node.entity.player.Player import core.plugin.Initializable @@ -12,7 +12,7 @@ import core.plugin.Initializable 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 } diff --git a/Server/src/main/java/core/game/content/dialogue/InformationclerkMuseumDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/InformationclerkMuseumDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/InformationclerkMuseumDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/InformationclerkMuseumDialogue.java index 8ec9f2258..f0ca68e0b 100644 --- a/Server/src/main/java/core/game/content/dialogue/InformationclerkMuseumDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/InformationclerkMuseumDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +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; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import org.rs09.consts.Items; /** @@ -86,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: @@ -154,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 new file mode 100644 index 000000000..4cf81e311 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/KanelDialogue.java @@ -0,0 +1,72 @@ +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; + +/** + * 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}. + */ + public KanelDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code KanelDialogue} {@code Object}. + * @param player the player. + */ + public KanelDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new KanelDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hello there."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + + switch (stage) { + case 0: + interpreter.sendDialogues(npc, FacialExpression.CHILD_THINKING, "Hel-lo?"); + stage = 1; + break; + case 1: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Right. Goodbye."); + stage = 2; + break; + case 2: + interpreter.sendDialogues(npc, FacialExpression.CHILD_THINKING, "Bye?"); + stage = 3; + break; + case 3: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 784 }; + } +} diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/KingRoaldDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/KingRoaldDialogue.kt new file mode 100644 index 000000000..1f775acc6 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/KingRoaldDialogue.kt @@ -0,0 +1,89 @@ +package content.region.misthalin.varrock.dialogue + +import content.minigame.allfiredup.KingRoaldAFUMiniDialogue +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 content.region.misthalin.varrock.quest.shieldofarrav.KingRoaldArravDialogue +import content.region.misthalin.varrock.quest.allfiredup.KingRoaldAFUDialogue +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 + * @author Ceikry + * @version 1.0 + */ +class KingRoaldDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player): DialoguePlugin { + return KingRoaldDialogue(player) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + + if (player.getAttribute("afu-mini:ring", false) || player.getAttribute("afu-mini:gloves", false) || player.getAttribute("afu-mini:adze", false)) { + player("Your Majesty! I did it!") + loadFile(KingRoaldAFUMiniDialogue()) + return true + } + + 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(Quests.PRIEST_IN_PERIL))) + } + + if (player.questRepository.getQuest(Quests.SHIELD_OF_ARRAV).isStarted(player) && !player.questRepository.getQuest(Quests.SHIELD_OF_ARRAV).isCompleted(player)) { + addOption("Shield of Arrav", KingRoaldArravDialogue()) + } + + if(!sendChoices()){ + player("Greetings, your Majesty.") + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + //Default (non-specific) dialogue + when (stage) { + + DIALOGUE_INITIAL_OPTIONS_HANDLE -> { + player("Greetings, your Majesty.") + loadFile(optionFiles[buttonId - 1]) + } + + START_DIALOGUE -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you have anything of importance to say?") + stage = 1 + } + 1 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "...Not really.") + stage = 2 + } + 2 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "You will have to excuse me, then. I am very busy as I", + "have a kingdom to run!" + ) + stage = END_DIALOGUE + } + + END_DIALOGUE -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(648, 2590, 5838) + } +} \ No newline at end of file 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/LoweDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/LoweDialogue.java new file mode 100644 index 000000000..97c0da485 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/LoweDialogue.java @@ -0,0 +1,82 @@ +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; + +/** + * Represents the dialogue plugin used for lowe. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class LoweDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code LoweDialogue} {@code Object}. + */ + public LoweDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code LoweDialogue} {@code Object}. + * @param player the player. + */ + public LoweDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new LoweDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Welcome to Lowe's Archery Emporium. Do you want", "to see my wares?"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "Yes, please.", "No, I prefer to bash things close up."); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + npc.openShop(player); + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.EVIL_LAUGH, "No, I prefer to bash things close up."); + stage = 3; + break; + } + + break; + case 3: + interpreter.sendDialogues(npc, FacialExpression.ANNOYED, "Humph, philistine."); + stage = 4; + break; + case 4: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 550 }; + } + +} diff --git a/Server/src/main/java/core/game/content/dialogue/MurkyMattDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/MurkyMattDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/MurkyMattDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/MurkyMattDialogue.java index f036114e8..9354bb592 100644 --- a/Server/src/main/java/core/game/content/dialogue/MurkyMattDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/MurkyMattDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.ge.GEGuidePrice; import core.game.ge.GEGuidePrice.GuideType; import core.game.node.entity.npc.NPC; 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/java/core/game/node/entity/npc/city/varrock/MuseumHistorianDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumHistorianDialogue.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/city/varrock/MuseumHistorianDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/MuseumHistorianDialogue.java index ef86afd12..91fe6293c 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/MuseumHistorianDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/MuseumHistorianDialogue.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.varrock; +package content.region.misthalin.varrock.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/PhillipaDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/PhillipaDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/PhillipaDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/PhillipaDialogue.java index 0d95fa1b7..2b6881dd5 100644 --- a/Server/src/main/java/core/game/content/dialogue/PhillipaDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/PhillipaDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/PhilopDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/PhilopDialogue.java new file mode 100644 index 000000000..9cf02f497 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/PhilopDialogue.java @@ -0,0 +1,73 @@ +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; + +/** + * Philop - Child in Gertrude's House + */ +@Initializable +public class PhilopDialogue extends DialoguePlugin { + // https://www.youtube.com/watch?v=ANI7DaRVEbg + public PhilopDialogue() { + + } + + public PhilopDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 782 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + + switch (stage) { + case 0: + interpreter.sendDialogues(npc, FacialExpression.CHILD_ANGRY, "Gwwrr!"); + stage = 1; + break; + case 1: + interpreter.sendDialogues(player, FacialExpression.THINKING, "Err, hello there. What's that you have there?"); + stage = 2; + break; + case 2: + interpreter.sendDialogues(npc, FacialExpression.CHILD_ANGRY, "Gwwwrrr! Dwa-gon Gwwwwrrrr!"); + stage = 3; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Enjoy playing with your dragon, then."); + stage = 4; + break; + case 4: + interpreter.sendDialogues(npc, FacialExpression.CHILD_ANGRY, "Gwwwrrr!"); + stage = 5; + break; + case 5: + end(); + break; + } + + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new PhilopDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, what's your name?"); + stage = 0; + return true; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/PoxDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/PoxDialogue.java similarity index 87% rename from Server/src/main/java/core/game/content/dialogue/PoxDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/PoxDialogue.java index 2d88120ec..c5f341754 100644 --- a/Server/src/main/java/core/game/content/dialogue/PoxDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/PoxDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/ReloboBlinyoDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/ReloboBlinyoDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/ReloboBlinyoDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/ReloboBlinyoDialogue.java index 094ed398e..e71d8c344 100644 --- a/Server/src/main/java/core/game/content/dialogue/ReloboBlinyoDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ReloboBlinyoDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.ge.GEGuidePrice; import core.game.ge.GEGuidePrice.GuideType; diff --git a/Server/src/main/java/core/game/content/dialogue/RomilyWeaklaxDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/RomilyWeaklaxDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/dialogue/RomilyWeaklaxDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/RomilyWeaklaxDialogue.java index a2c41c81e..26eac9af2 100644 --- a/Server/src/main/java/core/game/content/dialogue/RomilyWeaklaxDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/RomilyWeaklaxDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +import core.game.dialogue.DialoguePlugin; import org.rs09.consts.Items; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -9,7 +10,7 @@ import core.game.node.item.Item; import core.plugin.Initializable; import core.game.node.entity.player.Player; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; /** @@ -67,7 +68,7 @@ public class RomilyWeaklaxDialogue extends DialoguePlugin { @Override public void init() { super.init(); - PluginManager.definePlugin(new RomilyWildPieHandler()); + ClassScanner.definePlugin(new RomilyWildPieHandler()); } @Override @@ -246,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/java/core/game/content/dialogue/SaniDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/SaniDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/SaniDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/SaniDialogue.java index 50b26e74c..040f59ed7 100644 --- a/Server/src/main/java/core/game/content/dialogue/SaniDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SaniDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.dialogue; +import core.game.dialogue.DialoguePlugin; 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 rs09.game.system.config.ShopParser; +import core.game.shops.Shops; /** * Handles the SaniDialogue dialogue. @@ -79,7 +80,6 @@ public class SaniDialogue extends DialoguePlugin { break; case 2: end(); - ShopParser.Companion.getSHOPS().get(4905).open(player); break; } break; @@ -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: @@ -136,7 +136,7 @@ public class SaniDialogue extends DialoguePlugin { * @param uid The uid. */ private void openWeaponShop(Player player, int uid) { - ShopParser.Companion.openUid(player, uid); + Shops.openId(player, uid); } @Override diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/SawmillOperator.java b/Server/src/main/content/region/misthalin/varrock/dialogue/SawmillOperator.java new file mode 100644 index 000000000..505fbb3d1 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SawmillOperator.java @@ -0,0 +1,97 @@ +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; + +/** + * Handles the SawmillOperator dialogue. + * @author Vexia + */ +@Initializable +public class SawmillOperator extends DialoguePlugin { + + public SawmillOperator() { + + } + + public SawmillOperator(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new SawmillOperator(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + 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; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + options("Planks please!", "What kind of planks can you make?", "Can I buy some housing supplies?", "Nothing, thanks."); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY,"Planks please!"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING,"What kind of planks can you make?"); + stage = 20; + break; + case 3: + end(); + npc.openShop(player); + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY,"Nothing, thanks."); + stage = 40; + break; + } + break; + case 40: + 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: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING,"What kind of planks do you want?"); + stage = 11; + break; + case 11: + end(); + player.getInterfaceManager().open(new Component(403)); + break; + case 20: + 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: + 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: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 4250 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/ScavvoDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/ScavvoDialogue.java similarity index 88% rename from Server/src/main/java/core/game/content/dialogue/ScavvoDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/ScavvoDialogue.java index c9987c2c5..c097b2834 100644 --- a/Server/src/main/java/core/game/content/dialogue/ScavvoDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ScavvoDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/varrock/SchoolchildMuseumDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/SchoolchildMuseumDialogue.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/city/varrock/SchoolchildMuseumDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/SchoolchildMuseumDialogue.java index 147d3fff0..ff51da763 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/SchoolchildMuseumDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SchoolchildMuseumDialogue.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.varrock; +package content.region.misthalin.varrock.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/ShilopDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/ShilopDialogue.java new file mode 100644 index 000000000..d17b6b329 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ShilopDialogue.java @@ -0,0 +1,243 @@ +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.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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class ShilopDialogue extends DialoguePlugin { + + /** + * Represents the coins item. + */ + private static final Item COINS = new Item(995, 100); + + /** + * Represents the id. + */ + private int id; + + /** + * Constructs a new {@code ShilopDialogue} {@code Object} + */ + public ShilopDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code ShilopDialogue} {@code Object}. + * @param player the player. + */ + public ShilopDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new ShilopDialogue(player); + } + + @Override + public boolean open(Object... args) { + if (args[0] instanceof NPC) { + id = ((NPC) args[0]).getId(); + } else if (args[0] instanceof Integer) { + id = (Integer) args[0]; + } + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); + switch (quest.getStage(player)) { + case 0: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); + stage = 0; + break; + case 10: + 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, FacialExpression.THINKING, "Where did you say you saw Fluffs?"); + stage = 130; + break; + default: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); + stage = 0; + break; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); + switch (stage) { + case 0: + interpreter.sendDialogues(id, FacialExpression.CHILD_ANGRY, "You think you're tough do you?"); + stage = 1; + break; + case 1: + interpreter.sendDialogues(player, FacialExpression.THINKING, "Pardon?"); + stage = 2; + break; + case 2: + interpreter.sendDialogues(id, FacialExpression.CHILD_ANGRY, "I can beat anyone up!"); + stage = 3; + break; + case 3: + interpreter.sendDialogues(783, FacialExpression.CHILD_ANGRY, "He can you know!"); + stage = 4; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.THINKING, "Really?"); + stage = 5; + break; + case 5: + interpreter.sendDialogue("The boy begins to jump around with his fists up.", "You wonder what sort of desperado he'll grow up to be."); + stage = 6; + break; + case 6: + end(); + break; + case 100:// stage 10 + 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.THINKING, "What? I'm trying to help your mum find Fluffs."); + stage = 102; + break; + case 102: + 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.THINKING, "Where is this play area?"); + stage = 104; + break; + case 104: + 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.NEUTRAL, "What will make you tell me?"); + stage = 106; + break; + case 106: + 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.NEUTRAL, "How much?"); + stage = 108; + break; + case 108: + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SIDE_EYE, "10 coins."); + stage = 109; + break; + case 109: + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_SURPRISED, "10 coins?!"); + stage = 110; + break; + case 110: + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_NORMAL, "I'll handle this."); + stage = 111; + break; + case 111: + interpreter.sendDialogues(id == 781 ? 783 : 781, FacialExpression.CHILD_FRIENDLY, "100 coins should cover it."); + stage = 112; + break; + case 112: + interpreter.sendDialogues(player, FacialExpression.ANGRY, "100 coins! Why should I pay you?"); + stage = 113; + break; + case 113: + 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: + interpreter.sendOptions("Select an Option", "I'm not paying you a penny.", "Okay then, I'll pay."); + stage = 115; + break; + case 115: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ANGRY, "I'm not paying you a penny."); + stage = 116; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay then, I'll pay."); + stage = 118; + break; + } + break; + case 116: + 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: + end(); + break; + case 118: + if (!player.getInventory().containsItem(COINS)) { + interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough coins."); + stage = 117; + return true; + } + if (player.getInventory().remove(COINS)) { + interpreter.sendItemMessage(COINS, "You give the lad 100 coins."); + stage = 119; + } else { + interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough coins."); + stage = 117; + } + quest.setStage(player, 20); + break; + case 119: + 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, 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, FacialExpression.NEUTRAL, "Anything else?"); + stage = 122; + break; + case 122: + 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, 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: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 781 }; + } +} 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/java/core/game/content/dialogue/SmithingApparenticeDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/SmithingApparenticeDialogue.java similarity index 89% rename from Server/src/main/java/core/game/content/dialogue/SmithingApparenticeDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/SmithingApparenticeDialogue.java index 98b77c525..2e50d202b 100644 --- a/Server/src/main/java/core/game/content/dialogue/SmithingApparenticeDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SmithingApparenticeDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/SurokMagis.java b/Server/src/main/content/region/misthalin/varrock/dialogue/SurokMagis.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/SurokMagis.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/SurokMagis.java index 3d8fb3c01..588f8b1cd 100644 --- a/Server/src/main/java/core/game/content/dialogue/SurokMagis.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SurokMagis.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/dialogue/TarquinDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/TarquinDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/TarquinDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/TarquinDialogue.java index 24cf9d4fa..0139ce2f9 100644 --- a/Server/src/main/java/core/game/content/dialogue/TarquinDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/TarquinDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; @@ -105,7 +107,7 @@ public class TarquinDialogue extends DialoguePlugin { } break; case 24: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Then take your hatchet to it and shape it how you like!"); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Then take your axe to it and shape it how you like!"); stage = 26; break; case 26: diff --git a/Server/src/main/java/core/game/content/dialogue/TeaSellerDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/TeaSellerDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/TeaSellerDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/TeaSellerDialogue.java index ec41e9fea..48693f203 100644 --- a/Server/src/main/java/core/game/content/dialogue/TeaSellerDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/TeaSellerDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/varrock/TeacherandPupilMuseumDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/TeacherandPupilMuseumDialogue.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/city/varrock/TeacherandPupilMuseumDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/TeacherandPupilMuseumDialogue.java index bba65b3ae..66dc0c133 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/TeacherandPupilMuseumDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/TeacherandPupilMuseumDialogue.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.varrock; +package content.region.misthalin.varrock.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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; diff --git a/Server/src/main/java/core/game/node/entity/npc/city/varrock/TeacherandPupilMuseumMaleDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/TeacherandPupilMuseumMaleDialogue.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/city/varrock/TeacherandPupilMuseumMaleDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/TeacherandPupilMuseumMaleDialogue.java index 9c5639322..f663c2881 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/TeacherandPupilMuseumMaleDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/TeacherandPupilMuseumMaleDialogue.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.varrock; +package content.region.misthalin.varrock.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/ThessaliaDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/ThessaliaDialogue.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/content/dialogue/ThessaliaDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/dialogue/ThessaliaDialogue.kt index eccde821c..21772e6a8 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/ThessaliaDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ThessaliaDialogue.kt @@ -1,12 +1,13 @@ -package rs09.game.content.dialogue +package content.region.misthalin.varrock.dialogue +import core.api.playJingle import core.game.component.Component import core.plugin.Initializable -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.link.diary.DiaryType -import core.game.content.dialogue.FacialExpression +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 org.rs09.consts.Components @Initializable class ThessaliaDialogue(player: Player? = null): DialoguePlugin(player) { @@ -15,14 +16,14 @@ class ThessaliaDialogue(player: Player? = null): DialoguePlugin(player) { //The trade argument is handled elsewhere if (args.size == 3) { //Right-Click 'Change-Clothes' Option if (player.equipment.isEmpty) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 0) if (player.isMale) { end() - player.interfaceManager.open(Component(591)) + player.interfaceManager.open(Component(Components.THESSALIA_CLOTHES_MALE_591)) } else { end() - player.interfaceManager.open(Component(594)) + player.interfaceManager.open(Component(Components.THESSALIA_CLOTHES_FEMALE_594)) } + playJingle(player, 273) } else { //Has some armour equipped interpreter.sendDialogues(548, FacialExpression.WORRIED, "You can't try them on while wearing armour. Take", "it off and speak to me again.") stage = 52 @@ -55,7 +56,7 @@ class ThessaliaDialogue(player: Player? = null): DialoguePlugin(player) { } } 10 -> { - interpreter.sendDialogues(npc, 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 -> { @@ -81,11 +82,11 @@ class ThessaliaDialogue(player: Player? = null): DialoguePlugin(player) { stage++ } 22 -> { - interpreter.sendDialogues(npc, 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, 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 -> { @@ -98,7 +99,7 @@ class ThessaliaDialogue(player: Player? = null): DialoguePlugin(player) { stage = 30 } 2 -> { - interpreter.sendDialogues(player, 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 } } @@ -110,13 +111,12 @@ class ThessaliaDialogue(player: Player? = null): DialoguePlugin(player) { stage = 52 } 31 -> if (player.equipment.isEmpty) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 0) if (player.isMale) { end() - player.interfaceManager.open(Component(591)) + player.interfaceManager.open(Component(Components.THESSALIA_CLOTHES_MALE_591)) } else { end() - player.interfaceManager.open(Component(594)) + player.interfaceManager.open(Component(Components.THESSALIA_CLOTHES_FEMALE_594)) } } 49 -> { diff --git a/Server/src/main/java/core/game/content/dialogue/TrampDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/TrampDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/TrampDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/TrampDialogue.java index 8a084ccea..703d86712 100644 --- a/Server/src/main/java/core/game/content/dialogue/TrampDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/TrampDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/java/core/game/content/dialogue/ValaineDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/ValaineDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/ValaineDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/ValaineDialogue.java index b601e2aa5..970913e5c 100644 --- a/Server/src/main/java/core/game/content/dialogue/ValaineDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ValaineDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockBartenderDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockBartenderDialogue.java new file mode 100644 index 000000000..67c0f28bb --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockBartenderDialogue.java @@ -0,0 +1,138 @@ +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.game.node.entity.player.Player; +import core.plugin.Initializable; +import core.game.node.item.Item; + +/** + * The plugin used to handle the dialogue for the varrock bartender. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class VarrockBartenderDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code VarrockBartenderDialogue} {@code Object}. + */ + public VarrockBartenderDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code VarrockBartenderDialogue} {@code Object}. + * @param player the player. + */ + public VarrockBartenderDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 732, 731 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Good day to you, brave adventurer. Can I get you a", "refreshing beer?"); + stage = 1; + break; + case 1: + interpreter.sendOptions("Select an Option", "Yes please!", "No thanks.", "How much?"); + stage = 2; + break; + case 2: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please!"); + stage = 10; + break; + + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No thanks."); + stage = 20; + break; + + case 3: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "How much?"); + stage = 30; + break; + } + + break; + case 10: + 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.HAPPY, "Cheers!"); + stage = 12; + } else { + end(); + player.getPacketDispatch().sendMessage("You need two gold coins to buy a beer."); + } + break; + case 12: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Cheers!"); + stage = 13; + break; + case 13: + end(); + break; + case 20: + 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.FRIENDLY, "Two gold pieces a pint. So, what do you say?"); + stage = 31; + break; + case 31: + interpreter.sendOptions("Select an Option", "Yes please!", "No thanks."); + stage = 32; + break; + case 32: + + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please!"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No thanks."); + stage = 20; + break; + } + + break; + } + + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new VarrockBartenderDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello."); + stage = 0; + return true; + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockCookDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockCookDialogue.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockCookDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/VarrockCookDialogue.java index 403df82d9..e174548ff 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockCookDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockCookDialogue.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.varrock; +package content.region.misthalin.varrock.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockEastBartenderDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockEastBartenderDialogue.java new file mode 100644 index 000000000..6c57f4385 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockEastBartenderDialogue.java @@ -0,0 +1,154 @@ +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.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.plugin.Initializable; +import core.game.world.GameWorld; + +/** + * Handles the VarrockEastBartenderDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class VarrockEastBartenderDialogue extends DialoguePlugin { + + public VarrockEastBartenderDialogue() { + + } + + public VarrockEastBartenderDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 733 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "A glass of your finest ale please.", "Can you recommend where an adventurer might make his fortune?", "Do you know where I can get some good equipment?"); + stage = 1; + break; + case 1: + + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "A glass of your finest ale please."); + stage = 10; + break; + + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Can you recommend where an adventurer might make", "his fortune?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you know where I can get some good equipment?"); + stage = 30; + break; + } + + break; + + case 10: + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "No problemo. That'll be 2 coins."); + 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)); + end(); + player.getPacketDispatch().sendMessage("You buy a pint of beer."); + } else { + end(); + player.getPacketDispatch().sendMessage("You need 2 coins to buy ale."); + } + break; + case 20: + 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: + interpreter.sendOptions("Select an Option", "Oh ah well...", "Computer game? What are you talking about?", "Just a small clue?"); + stage = 22; + break; + case 22: + + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh ah well..."); + stage = 150; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Computer game? What are you talking about?"); + stage = 160; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.THINKING, "Just a small clue?"); + stage = 170; + break; + } + + break; + case 160: + 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_THINKING, "Nope, still don't understand what you are talking about.", "What's a computer?"); + stage = 162; + break; + case 162: + 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.WORRIED, "I give up. You're obviously completely mad."); + stage = 164; + break; + case 164: + end(); + break; + case 150: + end(); + break; + case 30: + 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.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: + end(); + break; + } + + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new VarrockEastBartenderDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "What can I do yer for?"); + stage = 0; + return true; + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockGateGuardDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockGateGuardDialogue.java similarity index 86% rename from Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockGateGuardDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/VarrockGateGuardDialogue.java index 296cf7443..4fba68d0b 100644 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockGateGuardDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockGateGuardDialogue.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.city.varrock; +package content.region.misthalin.varrock.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockShopKeeperDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockShopKeeperDialogue.java new file mode 100644 index 000000000..3597cce51 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockShopKeeperDialogue.java @@ -0,0 +1,78 @@ +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; + +/** + * Represents the Varrock shop keeper dialogue. + */ +@Initializable +public final class VarrockShopKeeperDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code VarrockShopKeeperDialogue} {@code Object}. + */ + public VarrockShopKeeperDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code VarrockShopKeeperDialogue} {@code Object}. + * @param player the player. + */ + public VarrockShopKeeperDialogue(Player player) { + super(player); + } + + @Override + 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 + 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[] { 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 new file mode 100644 index 000000000..d0deef1e4 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockSwordShopDialogue.java @@ -0,0 +1,81 @@ +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.game.node.entity.player.Player; +import core.plugin.Initializable; + +/** + * Handles the VarrockSwordShopDialogue dialogue. + * @author 'Vexia + */ + +@Initializable +public class VarrockSwordShopDialogue extends DialoguePlugin { + + public VarrockSwordShopDialogue() { + + } + + public VarrockSwordShopDialogue(Player player) { + super(player); + } + + @Override + public int[] getIds() { + return new int[] { 551, 552 }; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "Yes, please.", "No, I'm okay for swords right now."); + stage = 1; + break; + case 1: + + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes, please."); + stage = 2; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No, I'm okay for swords right now."); + stage = 10; + break; + } + + break; + case 2: + end(); + npc.openShop(player); + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Come back if you need any."); + stage = 11; + break; + case 11: + end(); + break; + } + + return true; + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new VarrockSwordShopDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + 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 new file mode 100644 index 000000000..02deb3337 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/WiloughDialogue.java @@ -0,0 +1,242 @@ +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.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. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class WiloughDialogue extends DialoguePlugin { + + /** + * Represents the coins item. + */ + private static final Item COINS = new Item(995, 100); + + /** + * Represents the id. + */ + private int id; + + /** + * Constructs a new {@code WiloughDialogue} {@code Object}. + */ + public WiloughDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code WiloughDialogue} {@code Object}. + * @param player the player. + */ + public WiloughDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new WiloughDialogue(player); + } + + @Override + public boolean open(Object... args) { + if (args[0] instanceof NPC) { + id = ((NPC) args[0]).getId(); + } else if (args[0] instanceof Integer) { + id = (int) args[0]; + } + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); + switch (quest.getStage(player)) { + case 0: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); + stage = 0; + break; + case 10: + 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, FacialExpression.THINKING, "Where did you say you saw Fluffs?"); + stage = 130; + break; + default: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); + stage = 0; + break; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + final Quest quest = player.getQuestRepository().getQuest(Quests.GERTRUDES_CAT); + switch (stage) { + case 0: + interpreter.sendDialogues(id, FacialExpression.CHILD_ANGRY, "You think you're tough do you?"); + stage = 1; + break; + case 1: + interpreter.sendDialogues(player, FacialExpression.THINKING, "Pardon?"); + stage = 2; + break; + case 2: + interpreter.sendDialogues(id, FacialExpression.CHILD_ANGRY, "I can beat anyone up!"); + stage = 3; + break; + case 3: + interpreter.sendDialogues(781, FacialExpression.CHILD_ANGRY, "He can you know!"); + stage = 4; + break; + case 4: + interpreter.sendDialogues(player, FacialExpression.THINKING, "Really?"); + stage = 5; + break; + case 5: + interpreter.sendDialogue("The boy begins to jump around with his fists up.", "You wonder what sort of desperado he'll grow up to be."); + stage = 6; + break; + case 6: + end(); + break; + case 100:// stage 10 + 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.THINKING, "What? I'm trying to help your mum find Fluffs."); + stage = 102; + break; + case 102: + 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.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, FacialExpression.CHILD_SIDE_EYE, "If I told you that, it wouldn't be a secret."); + stage = 105; + break; + case 105: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "What will make you tell me?"); + stage = 106; + break; + case 106: + 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.NEUTRAL, "How much?"); + stage = 108; + break; + case 108: + interpreter.sendDialogues(id == 783 ? 781 : 783, FacialExpression.CHILD_SIDE_EYE, "10 coins."); + stage = 109; + break; + case 109: + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_SURPRISED, "10 coins?!"); + stage = 110; + break; + case 110: + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_NORMAL, "I'll handle this."); + stage = 111; + break; + case 111: + interpreter.sendDialogues(id == 783 ? 783 : id, FacialExpression.CHILD_FRIENDLY, "100 coins should cover it."); + stage = 112; + break; + case 112: + interpreter.sendDialogues(player, FacialExpression.ANGRY, "100 coins! Why should I pay you?"); + stage = 113; + break; + case 113: + 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: + interpreter.sendOptions("Select an Option", "I'm not paying you a penny.", "Okay then, I'll pay."); + stage = 115; + break; + case 115: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ANGRY, "I'm not paying you a penny."); + stage = 116; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay then, I'll pay."); + stage = 118; + break; + } + break; + case 116: + 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: + end(); + break; + case 118: + if (!player.getInventory().containsItem(COINS)) { + end(); + return true; + } + if (player.getInventory().remove(COINS)) { + interpreter.sendItemMessage(COINS, "You give the lad 100 coins."); + quest.setStage(player, 20); + stage = 119; + } else { + interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough coins."); + stage = 117; + } + break; + case 119: + 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, 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, FacialExpression.NEUTRAL, "Anything else?"); + stage = 122; + break; + case 122: + 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, 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: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 783 }; + } +} 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/java/core/game/content/quest/miniquest/surok/DakhThoulanAegisDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/DakhThoulanAegisDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/miniquest/surok/DakhThoulanAegisDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/surok/DakhThoulanAegisDialogue.java index 1beead186..fdb6a579a 100644 --- a/Server/src/main/java/core/game/content/quest/miniquest/surok/DakhThoulanAegisDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/DakhThoulanAegisDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.quest.miniquest.surok; +package content.region.misthalin.varrock.dialogue.surok; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/surok/HuntForSurokPlugin.java b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/HuntForSurokPlugin.java new file mode 100644 index 000000000..b9b424f58 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/HuntForSurokPlugin.java @@ -0,0 +1,42 @@ +package content.region.misthalin.varrock.dialogue.surok; + +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.world.map.Location; +import core.plugin.Plugin; +import core.plugin.ClassScanner; + +/** + * Handles the hunt for surok mini quest. + * @author Vexia + */ + +public class HuntForSurokPlugin extends OptionHandler { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ClassScanner.definePlugin(new DakhThoulanAegisDialogue()); + ClassScanner.definePlugin(new MishkalunDornDialogue()); + ClassScanner.definePlugin(new SilasDahcsnuDialogue()); + ClassScanner.definePlugin(new SurokMagisDialogue()); + SceneryDefinition.forId(28780).getHandlers().put("option:use", this); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + switch (option) { + case "use": + switch (node.getId()) { + case 28780: + player.teleport(new Location(3326, 5469, 0)); + break; + } + break; + } + return true; + } + +} diff --git a/Server/src/main/java/core/game/content/quest/miniquest/surok/MishkalunDornDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/MishkalunDornDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/miniquest/surok/MishkalunDornDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/surok/MishkalunDornDialogue.java index 5c0b83772..f12277626 100644 --- a/Server/src/main/java/core/game/content/quest/miniquest/surok/MishkalunDornDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/MishkalunDornDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.quest.miniquest.surok; +package content.region.misthalin.varrock.dialogue.surok; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/content/quest/miniquest/surok/SilasDahcsnuDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/SilasDahcsnuDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/miniquest/surok/SilasDahcsnuDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/surok/SilasDahcsnuDialogue.java index ae7079bc1..b4e3023b0 100644 --- a/Server/src/main/java/core/game/content/quest/miniquest/surok/SilasDahcsnuDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/SilasDahcsnuDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.quest.miniquest.surok; +package content.region.misthalin.varrock.dialogue.surok; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/surok/SurokMagisDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/SurokMagisDialogue.java new file mode 100644 index 000000000..85b9fa6af --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/SurokMagisDialogue.java @@ -0,0 +1,201 @@ +package content.region.misthalin.varrock.dialogue.surok; + +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialoguePlugin; +import core.game.node.entity.player.Player; +import core.game.world.map.Location; +import core.game.world.map.build.DynamicRegion; + +/** + * Handles the surok magis dialogue. + * @author Vexia + */ +public class SurokMagisDialogue extends DialoguePlugin { + + /** + * Constructs a new {@Code SurokMagisDialogue} {@Code Object} + * @param player the player. + */ + public SurokMagisDialogue(Player player) { + super(player); + } + + /** + * Constructs a new {@Code SurokMagisDialogue} {@Code Object} + */ + public SurokMagisDialogue() { + /** + * empty. + */ + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new SurokMagisDialogue(player); + } + + @Override + public boolean open(Object... args) { + player(player.getUsername() + "! The meddling adventurer."); + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + player("Surok! What are you doing here?", "How did you-"); + stage++; + break; + case 1: + npc("Escape from Varrock Palace Library? That cruel", "imprisonment you left me in?"); + stage++; + break; + case 2: + player("Well...er..yes."); + stage++; + break; + case 3: + npc("Bah! A mere trifle for a powerful mage such as myself.", "There were plenty of other foolish people to help with", "my plans, you would do well to stay out of my way."); + stage++; + break; + case 4: + player("Stop, Surok! As a member of the Varrock Palace Secret", "Guard, I arrest you! Again!"); + stage++; + break; + case 5: + npc("Ha! I tire of this meaningless drivel. Catch me if you can."); + stage++; + break; + case 6: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 7002, 7136 }; + } + + /** + * Handles a surok cutscene. + * @author Vexia + */ + public static class SurokCutscene extends CutscenePlugin { + + /** + * The surok scene. + */ + private SurokScene scene; + + /** + * Constructs a new {@Code SurokCutscene} {@Code Object} + */ + public SurokCutscene() { + super("Surok Cutscene"); + } + + /** + * Constructs a new {@Code SurokCutscene} {@Code Object} + * @param player the player. + */ + public SurokCutscene(Player player) { + this(); + this.player = player; + } + + @Override + public boolean start(Player player, boolean login, Object... args) { + scene = (SurokScene) args[0]; + region = DynamicRegion.create(scene.getRegionId()); + setRegionBase(); + registerRegion(region.getId()); + return super.start(player, login, args); + } + + @Override + public ActivityPlugin newInstance(Player p) throws Throwable { + return new SurokCutscene(p); + } + + @Override + public Location getStartLocation() { + return base.transform(scene.getStartData()[0], scene.getStartData()[1], 0); + } + + @Override + public Location getSpawnLocation() { + return null; + } + + @Override + public void configure() { + + } + + /** + * Gets the scene. + * @return the scene + */ + public SurokScene getScene() { + return scene; + } + + /** + * Sets the bascene. + * @param scene the scene to set. + */ + public void setScene(SurokScene scene) { + this.scene = scene; + } + + /** + * The surok scene. + * @author Vexia + */ + public static enum SurokScene { + ESCAPE(-1, new int[] {}); + + /** + * The region id. + */ + private final int regionId; + + /** + * The start data. + */ + private final int[] startData; + + /** + * Constructs a new {@Code SurokScene} {@Code Object} + * @param regionId the region id. + * @param startData the start data. + */ + private SurokScene(int regionId, int[] startData) { + this.regionId = regionId; + this.startData = startData; + } + + /** + * Gets the regionId. + * @return the regionId + */ + public int getRegionId() { + return regionId; + } + + /** + * Gets the startData. + * @return the startData + */ + public int[] getStartData() { + return startData; + } + + } + + } +} diff --git a/Server/src/main/content/region/misthalin/varrock/diary/RatBurgissDiaryDialogue.kt b/Server/src/main/content/region/misthalin/varrock/diary/RatBurgissDiaryDialogue.kt new file mode 100644 index 000000000..010a3fb19 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/diary/RatBurgissDiaryDialogue.kt @@ -0,0 +1,86 @@ +package content.region.misthalin.varrock.diary + +import core.api.getAttribute +import core.api.setAttribute +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.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType +import core.tools.END_DIALOGUE + +class RatBurgissDiaryDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + val easyDiaryComplete = AchievementDiary.hasCompletedLevel(player, DiaryType.VARROCK, 0) + val alternateTeleport = getAttribute(player!!, "diaries:varrock:alttele", false) + when(stage) { + 0 -> { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.VARROCK, 0)) { + playerl(FacialExpression.FRIENDLY, "I think I've finished all of the tasks in my Varrock Achievement Diary.") + stage = 40 + return + } + else if (AchievementDiary.canReplaceReward(player, DiaryType.VARROCK, 0)) { + playerl(FacialExpression.ANNOYED, "I seem to have lost my armor.") + stage = 50 + return + } + + showTopics( + Topic("What is the Achievement Diary?", 10), + IfTopic("Can I change my Varrock Teleport point?", 100, easyDiaryComplete), + Topic("What are the rewards?", 20), + Topic("How do I claim the rewards?", 30), + Topic(FacialExpression.NEUTRAL, "See you later.", END_DIALOGUE) + ) + } + + 10 -> npcl(FacialExpression.FRIENDLY, "It's a diary that helps you keep track of particular achievements. Here in Varrock it can help you discover some quite useful things. Eventually, with enough exploration, the people of Varrock will reward").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "you. You can see what tasks you have listed by clicking on the green button in the Quest List.").also { stage = 0 } + + 20 -> npcl(FacialExpression.FRIENDLY, "Well, there's three different levels of Varrock Armour, 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").also { stage++ } + 21 -> npcl(FacialExpression.FRIENDLY, "won't spoil your surprise. Rest assured, the people of Varrock are happy to see you visiting the land.").also { stage = 0 } + + 30 -> npcl(FacialExpression.FRIENDLY, "Just complete the tasks so they're all ticked off, then you can claim your reward. Most of them are straightforward; you might find some require quests to be started, if not finished.").also { stage++ } + 31 -> npcl(FacialExpression.FRIENDLY, "To claim the different Varrock Armour, speak to Vannaka, Reldo, and myself.").also { stage = 0 } + + 40 -> npcl(FacialExpression.FRIENDLY, "You have? Excellent! Well done.").also { stage++ } + 41 -> playerl(FacialExpression.FRIENDLY, "Thank you. Uh... can I have the reward?").also { stage++ } + 42 -> npcl(FacialExpression.FRIENDLY, "Reward? Ah yes! Of course. Your reward, it's right here.").also { stage++ } + 43 -> { + AchievementDiary.flagRewarded(player, DiaryType.VARROCK, 0) + npcl(FacialExpression.FRIENDLY, "Now, this body armour is magically enhanced to help you with your Smithing and Mining. There is a furnace, not far from here, in Edgeville. Use this armour there and, when smelting ores up to and") + stage++ + } + 44 -> npcl(FacialExpression.FRIENDLY, "including steel, you will have a chance of making an extra bar every time. Also, when you mine with this armour on, you will have a chance of Mining extra ores from rocks up to and including coal.").also { stage++ } + 45 -> npcl(FacialExpression.FRIENDLY, "Bear in mind that you will need to be wearing the armour for either of these to work. I will speak to the shopkeepers around Varrock who sell armour and weapons to get you better prices when you are").also { stage++ } + 46 -> npcl(FacialExpression.FRIENDLY, "wearing it. I can also change your Varrock Teleport spell so that it takes you to the Grand Exchange, if you'd find that more convenient.").also { stage++ } + 47 -> npcl(FacialExpression.FRIENDLY, "As an extra reward, you can also have this old magical lamp to help you with your skills. I was going to use it myself, but I don't really need it.").also { stage++ } + 48 -> playerl(FacialExpression.FRIENDLY, "Wow, thanks!").also { stage++ } + 49 -> npcl(FacialExpression.FRIENDLY, "If you should lose this armour, come back and see me for another set.").also { stage = 0 } + + 50 -> { + AchievementDiary.grantReplacement(player, DiaryType.VARROCK, 0) + npcl(FacialExpression.ANNOYED, "You better be more careful this time.").also { stage = END_DIALOGUE } + } + + 100 -> showTopics( + IfTopic("I'd like to teleport to the Grand Exchange.", 101, easyDiaryComplete && !alternateTeleport), + IfTopic("I'd like to teleport to the city square.", 102, easyDiaryComplete && alternateTeleport), + Topic("Nevermind.", END_DIALOGUE) + ) + + 101 -> { + npcl(FacialExpression.FRIENDLY, "There you are, your Varrock teleport will now take you to the Grand Exchange.") + setAttribute(player!!, "/save:diaries:varrock:alttele", true) + stage = 0 + } + 102 -> { + npcl(FacialExpression.FRIENDLY, "There you are, your Varrock teleport will now take you to the city square.") + setAttribute(player!!, "/save:diaries:varrock:alttele", false) + stage = 0 + } + } + } +} \ 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 new file mode 100644 index 000000000..f49899210 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt @@ -0,0 +1,292 @@ +package content.region.misthalin.varrock.diary + +import content.global.skill.prayer.Bones +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.world.map.zone.ZoneBorders +import org.rs09.consts.Components +import org.rs09.consts.Items +import org.rs09.consts.NPCs +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.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, + NPCs.STRAY_DOG_5917, NPCs.STRAY_DOG_5918 + ) + + object EasyTasks { + const val THESSALIA_BROWSE_CLOTHES = 0 + const val AUBURY_TELEPORT_ESSENCE_MINE = 1 + const val MINE_IRON_SOUTHEAST = 2 + const val MAKE_PLANK_SAWMILL = 3 + const val VISIT_SOS_LEVEL2 = 4 + const val JUMP_OVER_FENCE_SOUTH = 5 + const val LUMBERYARD_CHOP_DYING_TREE = 6 + const val BUY_VARROCK_HERALD = 7 + const val GIVE_STRAY_DOG_A_BONE = 8 + const val BARBARIAN_VILLAGE_SPIN_A_BOWL = 9 + const val EDGEVILLE_ENTER_DUNGEON_SOUTH = 10 + const val MOVE_POH_TO_VARROCK = 11 + const val SPEAK_TO_HAIG_HALEN_50QP = 12 + const val ENTER_EARTH_ALTAR = 13 + const val ELSIE_TELL_A_STORY = 14 + const val PATERDOMUS_MINE_LIMESTONE = 15 + const val BARBARIAN_VILLAGE_CATCH_TROUT = 16 + const val SEWERS_CUT_COBWEB = 17 + const val FIND_HIGHEST_POINT = 18 + } + + object MediumTasks { + const val APOTHECARY_MAKE_STRENGTH_POTION = 0 + const val CHAMPIONS_GUILD_VISIT = 1 + const val TAKE_DAGONHAI_CHAOS_PORTAL_SHORTCUT = 2 + const val RAT_POLE_FULL_RAT_COMPLEMENT = 3 + const val SEWER_GATHER_RED_SPIDERS_EGGS = 4 + const val USE_SPIRIT_TREE_NORTH = 5 + const val PERFORM_ALL_SOS_EMOTES = 6 + const val SELECT_KITTEN_COLOR = 7 + const val USE_GE_UNDER_WALL_SHORTCUT = 8 + const val ENTER_SOULBANE_RIFT = 9 + const val DIGSITE_PENDANT_TELEPORT = 10 + const val CRAFT_EARTH_TIARA = 11 + const val PALACE_PICKPOCKET_GUARD = 12 + const val CAST_VARROCK_TELEPORT_SPELL = 13 + const val VANNAKA_GET_SLAYER_TASK = 14 + const val SAWMILL_BUY_20_MAHOGANY_PLANKS = 15 + const val PICK_FROM_WHITE_TREE = 16 + const val HOTAIR_BALLOON_TRAVEL_SOMEWHERE = 17 + const val GERTRUDE_GET_CAT_TRAINING_MEDAL = 18 + const val DIAL_FAIRY_RING_WEST = 19 + const val OZIACH_BROWSE_STORE = 20 + } + + object HardTasks { + const val PICK_POISON_IVY_FARMING_PATCH = 0 + const val USE_MOSS_GIANT_PIPE_SHORTCUT = 1 + const val FANCY_DRESS_SELLER_TRADE_FURS = 2 + const val SMITH_ADAMANT_MED_HELM_SOUTHEAST = 3 + const val SPEAK_TO_ORLANDO_SMITH_153_KUDOS = 4 + const val GIVE_WEAKLAX_A_PIE = 5 + const val CRAFT_AIR_BATTLESTAFF = 6 + const val GIVE_POH_TROPICAL_WOOD_OR_FANCY_STONE_FINISH = 7 + const val MAKE_VARROCK_TELEPORT_TABLET_OR_MAHOGANY_LECTERN = 8 + const val OBTAIN_NEW_SET_OF_FAMILY_CREST_GAUNTLETS = 9 + const val EDGEVILLE_MAKE_WAKA_CANOE = 10 + const val EDGEVILLE_TELEPORT_USING_ANCIENT_MAGICKS = 11 + const val BARBARIAN_VILLAGE_TELEPORT_USING_SKULL_SCEPTRE = 12 + } + } + + override val areaTasks get() = arrayOf( + AreaDiaryTask( + VARROCK_ROOF_AREA, + DiaryLevel.EASY, + EasyTasks.FIND_HIGHEST_POINT + ), + + AreaDiaryTask( + SOS_LEVEL_2_AREA, + DiaryLevel.EASY, + EasyTasks.VISIT_SOS_LEVEL2 + ), + + AreaDiaryTask( + CHAMPIONS_GUILD, + DiaryLevel.MEDIUM, + MediumTasks.CHAMPIONS_GUILD_VISIT + ) + ) + + override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { + when (player.viewport.region.id) { + 12341 -> if (event.itemId == Items.RAW_TROUT_335) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.BARBARIAN_VILLAGE_CATCH_TROUT + ) + } + + 13108 -> if (event.itemId == Items.IRON_ORE_440) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.MINE_IRON_SOUTHEAST + ) + } + + 13110 -> { + if (event.itemId == Items.LOGS_1511 + && event.source.id == Scenery.DYING_TREE_24168 + ) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.LUMBERYARD_CHOP_DYING_TREE + ) + } + } + + 13366 -> if (event.itemId == Items.LIMESTONE_3211) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.PATERDOMUS_MINE_LIMESTONE + ) + } + } + } + + override fun onTeleported(player: Player, event: TeleportEvent) { + when (event.source) { + is NPC -> if (event.method == TeleportMethod.NPC && event.source.id == NPCs.AUBURY_553) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.AUBURY_TELEPORT_ESSENCE_MINE + ) + } + } + } + + override fun onInteracted(player: Player, event: InteractionEvent) { + when (player.viewport.region.id) { + 12342 -> if (event.target.id == 26934) { + finishTask( + player, + DiaryLevel.EASY, + 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 + ) + } + } + } + + override fun onButtonClicked(player: Player, event: ButtonClickEvent) { + /* This gets fired even on the login screen, and we don't have a region there, so... */ + player.viewport.region?.let { + when (it.id) { + 12342 -> { + if (event.iface == CanoeListener.CANOE_SHAPING_INTERFACE + && event.buttonId == CanoeListener.CANOE_SHAPING_BUTTONS[CanoeListener.Companion.Canoes.WAKA.ordinal]) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.EDGEVILLE_MAKE_WAKA_CANOE + ) + } + } + } + } + } + + override fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) { + when (event.dialogue) { + is ElsieDialogue -> if (event.currentStage == 12) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.ELSIE_TELL_A_STORY + ) + } + } + } + + override fun onUsedWith(player: Player, event: UseWithEvent) { + when (event.used) { + in Bones.array -> if (event.with in STRAY_DOGS) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.GIVE_STRAY_DOG_A_BONE + ) + } + } + } + + override fun onInterfaceOpened(player: Player, event: InterfaceOpenEvent) { + when (event.component.id) { + Components.THESSALIA_CLOTHES_MALE_591, + Components.THESSALIA_CLOTHES_FEMALE_594 -> { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.THESSALIA_BROWSE_CLOTHES + ) + } + } + } + + override fun onSpellCast(player: Player, event: SpellCastEvent) { + if (event.spellBook == SpellBookManager.SpellBook.MODERN && event.spellId == 15) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CAST_VARROCK_TELEPORT_SPELL + ) + } + } + + override fun onFairyRingDialed(player: Player, event: FairyRingDialEvent) { + if (event.fairyRing == FairyRing.DKR) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.DIAL_FAIRY_RING_WEST + ) + } + } +} \ No newline at end of file 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/ChampionsGuildDoor.kt b/Server/src/main/content/region/misthalin/varrock/handlers/ChampionsGuildDoor.kt new file mode 100644 index 000000000..4d6187d7c --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/ChampionsGuildDoor.kt @@ -0,0 +1,32 @@ +package content.region.misthalin.varrock.handlers + +import core.plugin.Initializable +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.game.interaction.InteractionListener + +/** + * Represents the plugin used to handle the interaction with the champions guild + * door. + * @author 'Vexia + * @author dginovker + * @version 2.0 + */ +@Initializable +class ChampionsGuildDoor : InteractionListener { + override fun defineListeners() { + on(Scenery.DOOR_1805, IntType.SCENERY, "open") { player, node -> + if (player.location.y > 3362 && player.questRepository.points < 32) { + player.dialogueInterpreter.open(70099, "You have not proved yourself worthy to enter here yet.") + 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.") + } + core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node as core.game.node.scenery.Scenery) + } + return@on true + } + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/object/ChefGuildDoorPlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/ChefGuildDoorPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/ChefGuildDoorPlugin.java rename to Server/src/main/content/region/misthalin/varrock/handlers/ChefGuildDoorPlugin.java index d3793bd6b..4058a2606 100644 --- a/Server/src/main/java/core/game/interaction/object/ChefGuildDoorPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/ChefGuildDoorPlugin.java @@ -1,8 +1,8 @@ -package core.game.interaction.object; +package content.region.misthalin.varrock.handlers; import core.cache.def.impl.SceneryDefinition; import org.rs09.consts.Items; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.node.item.Item; import core.game.node.entity.skill.Skills; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/content/cutscene/GECutscenePlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/GECutscenePlugin.java similarity index 98% rename from Server/src/main/java/core/game/content/cutscene/GECutscenePlugin.java rename to Server/src/main/content/region/misthalin/varrock/handlers/GECutscenePlugin.java index 0106ab8f3..4630d1f57 100644 --- a/Server/src/main/java/core/game/content/cutscene/GECutscenePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/GECutscenePlugin.java @@ -1,17 +1,17 @@ -package core.game.content.cutscene; +package content.region.misthalin.varrock.handlers; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.ge.GEGuidePrice; import core.game.ge.GEGuidePrice.GuideType; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.net.packet.PacketRepository; import core.net.packet.context.CameraContext; diff --git a/Server/src/main/java/core/game/interaction/object/GrandExchangePlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/GrandExchangePlugin.java similarity index 87% rename from Server/src/main/java/core/game/interaction/object/GrandExchangePlugin.java rename to Server/src/main/content/region/misthalin/varrock/handlers/GrandExchangePlugin.java index f1fa68c4d..a4be16751 100644 --- a/Server/src/main/java/core/game/interaction/object/GrandExchangePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/GrandExchangePlugin.java @@ -1,10 +1,9 @@ -package core.game.interaction.object; +package content.region.misthalin.varrock.handlers; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; import core.plugin.Initializable; import core.game.ge.GEGuidePrice; -import core.game.ge.GrandExchangeDatabase; import core.game.ge.GEGuidePrice.GuideType; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -12,6 +11,9 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; import core.plugin.Plugin; +import core.game.ge.GrandExchangeRecords; +import content.global.handlers.iface.ge.ExchangeItemSets; +import content.global.handlers.iface.ge.StockMarket; /** * Represents the plugin used for grand exchange npc and object options. @@ -44,6 +46,7 @@ public final class GrandExchangePlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { + GrandExchangeRecords records = GrandExchangeRecords.getInstance(player); if (player.getIronmanManager().checkRestriction() && !option.equals("sets")) { return true; } @@ -52,21 +55,13 @@ public final class GrandExchangePlugin extends OptionHandler { player.getDialogueInterpreter().open(6528, NPC.create(6528, player.getLocation())); break; case "exchange": - if (!GrandExchangeDatabase.hasInitialized()) { - player.getDialogueInterpreter().sendDialogue("The Grand Exchange desk seems to be closed..."); - break; - } - player.getPlayerGrandExchange().open(); + StockMarket.openFor(player); break; case "history": - player.getPlayerGrandExchange().openHistoryLog(player); + records.openHistoryLog(player); break; case "collect": - if (!GrandExchangeDatabase.hasInitialized()) { - player.getDialogueInterpreter().sendDialogue("The Grand Exchange desk seems to be closed..."); - break; - } - player.getPlayerGrandExchange().openCollectionBox(); + records.openCollectionBox(); break; case "info-logs": GEGuidePrice.open(player, GuideType.LOGS); @@ -84,7 +79,7 @@ public final class GrandExchangePlugin extends OptionHandler { GEGuidePrice.open(player, GuideType.WEAPONS_AND_ARMOUR); break; case "sets": - player.getPlayerGrandExchange().openItemSets(); + ExchangeItemSets.openFor(player); break; } return true; @@ -137,7 +132,7 @@ public final class GrandExchangePlugin extends OptionHandler { player.getBank().open(); break; case "collect": - player.getPlayerGrandExchange().openCollectionBox(); + GrandExchangeRecords.getInstance(player).openCollectionBox(); break; } return 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 new file mode 100644 index 000000000..45994537a --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/GrandExchangeShortcut.kt @@ -0,0 +1,104 @@ +package content.region.misthalin.varrock.handlers + +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.skill.Skills +import core.game.node.scenery.Scenery +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import core.game.interaction.IntType +import core.game.interaction.InteractionListener + +/** + * Handles the grand exchange shortcut. + * @author Emperor + * @author dginovker + * @version 2.0 + */ +@Initializable +class GrandExchangeShortcut : InteractionListener { + companion object { + /** + * The shortcut configs + */ + val SHORTCUTS = mapOf( + 9311 to listOf( + Location.create(3138, 3516, 0), // Run to loc + Location.create(3143, 3514, 0), // Crawl through tele loc + Location.create(3144, 3514, 0), // End loc + ), + 9312 to listOf( + Location.create(3144, 3514, 0), // Run to loc + Location.create(3139, 3516, 0), // Crawl through tele loc + Location.create(3138, 3516, 0), // End loc + ) + ) + /** + * The climbing down animation. + */ + private val CLIMB_DOWN = Animation.create(2589) + + /** + * The crawling through animation. + */ + private val CRAWL_THROUGH = Animation.create(2590) + + /** + * The climbing up animation. + */ + private val CLIMB_UP = Animation.create(2591) + } + + override fun defineListeners() { + on(SHORTCUTS.keys.toIntArray(), IntType.SCENERY, "climb-into") { player, node -> + if (!hasLevelDyn(player, Skills.AGILITY, 21)) { + sendDialogue(player, "You need an Agility level of at least 21 to do this.") + return@on true + } + 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) + runCrawlPulse(player, path) + return@on true + } + } + + private fun runCrawlPulse(player: Player, path: List) { + submitIndividualPulse(player, object : Pulse(1, player) { + var count = 0 + var reachedStart = false + override fun pulse(): Boolean { + // If the player hasn't reached path[0], don't do anything + if (!reachedStart && player.location != path[0]) { + return false + } + reachedStart = true + + when (++count) { + 2 -> { + teleport(player, path[1]) + visualize(player, CRAWL_THROUGH, -1) + } + + 3 -> { + ForceMovement.run( + player, + path[1], + path[2], + CLIMB_UP + ) + unlock(player) + return true + } + } + return false + } + }) + } + +} diff --git a/Server/src/main/java/core/game/interaction/object/LumberYardCratePlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/LumberYardCratePlugin.java similarity index 94% rename from Server/src/main/java/core/game/interaction/object/LumberYardCratePlugin.java rename to Server/src/main/content/region/misthalin/varrock/handlers/LumberYardCratePlugin.java index 83d5a1ad3..277d7d634 100644 --- a/Server/src/main/java/core/game/interaction/object/LumberYardCratePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/LumberYardCratePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.misthalin.varrock.handlers; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; @@ -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/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/java/core/game/interaction/object/RedberryBushPlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/RedberryBushPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/RedberryBushPlugin.java rename to Server/src/main/content/region/misthalin/varrock/handlers/RedberryBushPlugin.java index ce7b031da..af25650e7 100644 --- a/Server/src/main/java/core/game/interaction/object/RedberryBushPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/RedberryBushPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.misthalin.varrock.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; 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/java/core/game/node/entity/npc/other/StrayDogNPC.java b/Server/src/main/content/region/misthalin/varrock/handlers/StrayDogNPC.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/npc/other/StrayDogNPC.java rename to Server/src/main/content/region/misthalin/varrock/handlers/StrayDogNPC.java index 1151d1cb9..e2ce0867e 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/StrayDogNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/StrayDogNPC.java @@ -1,10 +1,11 @@ -package core.game.node.entity.npc.other; +package content.region.misthalin.varrock.handlers; import java.util.ArrayList; import java.util.List; import core.game.interaction.DestinationFlag; 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; @@ -71,7 +72,7 @@ public class StrayDogNPC extends AbstractNPC { players = RegionManager.getLocalPlayers(this, 7); if (players.size() != 0) { target = players.get(RandomFunction.random(players.size())); - getPulseManager().run(getFollowPulse(target), "movement"); + getPulseManager().run(getFollowPulse(target), PulseType.STANDARD); delay = System.currentTimeMillis() + 150000; } } diff --git a/Server/src/main/java/core/game/interaction/npc/ThessaliaPlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/ThessaliaPlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/npc/ThessaliaPlugin.java rename to Server/src/main/content/region/misthalin/varrock/handlers/ThessaliaPlugin.java index 4b17a7799..3ef52ee6d 100644 --- a/Server/src/main/java/core/game/interaction/npc/ThessaliaPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/handlers/ThessaliaPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.region.misthalin.varrock.handlers; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; 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 new file mode 100644 index 000000000..29a4526a7 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockGuardSignpost.kt @@ -0,0 +1,43 @@ +package content.region.misthalin.varrock.handlers + +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.tools.Log + +class VarrockGuardSignpost : InteractionListener { + override fun defineListeners() { + val zone = object : MapZone("Varrock Guards", true){ + override fun interact(e: Entity?, target: Node?, option: Option?): Boolean { + if(option != null && option.name.toLowerCase().contains("pickpocket") && target != null && target.name.toLowerCase().contains("guard")){ + GlobalStats.incrementGuardPickpockets() + } + return false + } + } + + registerMapZone(zone, ZoneBorders(3225,3445,3198,3471)) + registerMapZone(zone, ZoneBorders(3222,3375,3199,3387)) + registerMapZone(zone, ZoneBorders(3180,3420,3165,3435)) + registerMapZone(zone, ZoneBorders(3280,3422,3266,3435)) + + on(31298, IntType.SCENERY, "read"){ player, _ -> + val pickpocketCount = GlobalStats.getDailyGuardPickpockets() + 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." ) + else -> sendDialogue(player, "Guards in the Varrock Palace are on full alert due to increasing levels of pickpocketing. So far today, $pickpocketCount guards have had their money pickpocketed in the palace or at the city gates.") + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/VarrockInteractionListener.kt b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockInteractionListener.kt new file mode 100644 index 000000000..1610dafb6 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockInteractionListener.kt @@ -0,0 +1,91 @@ +package content.region.misthalin.varrock.handlers + +import content.region.misthalin.varrock.dialogue.KnockAtBankDoor +import core.api.* +import core.game.global.action.DoorActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +class VarrockInteractionListener : InteractionListener { + override fun defineListeners() { + // Varrock Sewer Manhole + on(VARROCK_MANHOLE, IntType.SCENERY, "open", "close") { player, node -> + if (getUsedOption(player) == "open") { + playAudio(player, Sounds.MANHOLE_OPEN_75) + replaceScenery(node.asScenery(), Scenery.VARROCK_MANHOLE_OPEN_882, 100) + } else { + playAudio(player, Sounds.MANHOLE_CLOSE_74) + replaceScenery(node.asScenery(), Scenery.VARROCK_MANHOLE_CLOSED_881, -1) + } + return@on true + } + + // Phoenix Gang Hideout Plaque + on(Scenery.PLAQUE_23636, IntType.SCENERY, "read") { player, _ -> + openInterface(player, VTAM_IFACE) + return@on true + } + + // Varrock Census in the palace Library + on(Scenery.VARROCK_CENSUS_37209, IntType.SCENERY, "read") { player, _ -> + sendPlayerDialogue(player, "Hmm. The Varrock Census - year 160. That means it's nine years out of date.") + addDialogueAction(player) { _, buttonID -> + if (buttonID == 6) { + openInterface(player, VARROCK_CENSUS_IFACE) + } + } + return@on true + } + + // Broken Cart next to Rat Burgiss + on(Scenery.BROKEN_CART_23055, IntType.SCENERY, "search") { player, node -> + sendDialogue(player, "You search the cart but are surprised to find very little there. " + + "It's a little odd for a travelling trader not to have anything to trade.") + return@on true + } + + on(openOptionNodes, IntType.SCENERY, "open") { player, node -> + when (node.id) { + // Guidor's Bedroom Door + Scenery.BEDROOM_DOOR_2032 -> { + openDialogue(player, NPCs.GUIDORS_WIFE_342, true, true) + } + + // Guidor's Drawers + Scenery.DRAWERS_17466 -> { + sendMessage(player, "The drawers are locked shut.") + } + + // Brass Key Door to Edgeville Dungeon + Scenery.DOOR_1804 -> { + if (inInventory(player, Items.BRASS_KEY_983)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "This door is locked.") + } + } + } + return@on true + } + + // Varrock West Bank Door + on(Scenery.DOOR_24389, IntType.SCENERY, "knock-at") { player, node -> + openDialogue(player, KnockAtBankDoor()) + return@on true + } + + // TODO: Cooking Guild + // TODO: Fix Achievements + } + companion object { + private val VARROCK_MANHOLE = intArrayOf(Scenery.VARROCK_MANHOLE_CLOSED_881, Scenery.VARROCK_MANHOLE_OPEN_882) + private val openOptionNodes = intArrayOf(Scenery.BEDROOM_DOOR_2032, Scenery.DRAWERS_17466, Scenery.DOOR_1804) + private const val VTAM_IFACE = 531 + private const val VARROCK_CENSUS_IFACE = 794 + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/npc/ZaffPlugin.kt b/Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt similarity index 82% rename from Server/src/main/java/core/game/interaction/npc/ZaffPlugin.kt rename to Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt index 01693be2f..22243ca0b 100644 --- a/Server/src/main/java/core/game/interaction/npc/ZaffPlugin.kt +++ b/Server/src/main/content/region/misthalin/varrock/handlers/ZaffPlugin.kt @@ -1,18 +1,13 @@ -package core.game.interaction.npc +package content.region.misthalin.varrock.handlers -import api.Container -import api.ContentAPI -import api.InputType +import core.api.Container +import core.api.* +import core.api.InputType import core.cache.def.impl.NPCDefinition import core.plugin.Initializable import core.game.interaction.OptionHandler import core.plugin.Plugin -import core.game.interaction.npc.ZaffPlugin.ZaffDialogue -import core.game.interaction.npc.ZaffPlugin.ZaffStaveDialogue -import core.game.content.dialogue.DialoguePlugin import core.game.node.entity.player.link.quest.Quest -import core.game.content.dialogue.FacialExpression -import core.game.interaction.npc.ZaffPlugin import core.game.node.Node import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player @@ -20,9 +15,9 @@ import core.game.node.entity.player.link.diary.DiaryType import core.game.node.item.Item import org.json.simple.JSONObject import org.rs09.consts.Items -import rs09.ServerStore -import rs09.ServerStore.getInt -import rs09.game.system.SystemLogger +import core.ServerStore +import core.ServerStore.Companion.getInt +import content.data.Quests /** * Represents the plugin used for buying a battle staff from zeke. @@ -49,7 +44,7 @@ class ZaffPlugin : OptionHandler() { * @author 'Vexia * @version 1.0 */ - class ZaffDialogue : DialoguePlugin { + class ZaffDialogue : core.game.dialogue.DialoguePlugin { /** * The quest. */ @@ -70,16 +65,16 @@ class ZaffPlugin : OptionHandler() { */ constructor(player: Player?) : super(player) {} - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return ZaffDialogue(player) } 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, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "Would you like to buy or sell some staves or is there", "something else you need?" ) @@ -121,11 +116,11 @@ class ZaffPlugin : OptionHandler() { } 1 -> when (buttonId) { 1 -> { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, please.") + interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.HALF_GUILTY, "Yes, please.") stage = 10 } 2 -> { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thank you.") + interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.HALF_GUILTY, "No, thank you.") stage = 20 } 3 -> { @@ -136,14 +131,14 @@ class ZaffPlugin : OptionHandler() { player("We did it! We beat Surok!") stage = 200 } else { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I have another ring?") + interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.HALF_GUILTY, "Can I have another ring?") stage = 50 } } } 10 -> { if(player.achievementDiaryManager.getDiary(DiaryType.VARROCK).levelRewarded.contains(true)){ - npcl(FacialExpression.FRIENDLY, "Would you like to hear about my battlestaves?") + npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Would you like to hear about my battlestaves?") stage = 1000 } else { end() @@ -153,7 +148,7 @@ class ZaffPlugin : OptionHandler() { 20 -> { interpreter.sendDialogues( npc, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "Well, 'stick' your head in again if you change your mind." ) stage = 21 @@ -161,7 +156,7 @@ class ZaffPlugin : OptionHandler() { 21 -> { interpreter.sendDialogues( player, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "Huh, terrible pun. You just can't get the 'staff' these", "days!" ) @@ -171,20 +166,20 @@ class ZaffPlugin : OptionHandler() { 50 -> { if (player.inventory.contains(11014, 1)) interpreter.sendDialogues( npc, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "Go and get the one that's in your inventory " + player.username + "!" ) else if (player.bank.contains(11014, 1)) interpreter.sendDialogues( npc, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "Go and get the one that's in your bank" + player.username + "!" ) else if (player.equipment.contains(11014, 1)) interpreter.sendDialogues( npc, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "Go and get the one that's on your finger " + player.username + "!" ) else { interpreter.sendDialogues( npc, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "Of course you can! Here you go " + player.username + "!" ) player.inventory.add(BEACON_RING) @@ -311,7 +306,7 @@ class ZaffPlugin : OptionHandler() { 1001 -> when(buttonId){ 1 -> { end() - ContentAPI.openDialogue(player, 9679, npc) + openDialogue(player, 9679, npc) } 2 -> { end() @@ -339,7 +334,7 @@ class ZaffPlugin : OptionHandler() { * @author 'Vexia * @version 1.0 */ - inner class ZaffStaveDialogue : DialoguePlugin { + inner class ZaffStaveDialogue : core.game.dialogue.DialoguePlugin { /** * The ammount of battlestaves. */ @@ -360,13 +355,13 @@ class ZaffPlugin : OptionHandler() { */ constructor(player: Player?) : super(player) {} - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return ZaffStaveDialogue(player) } override fun open(vararg args: Any): Boolean { ammount = getStoreFile().getInt(player.username.toLowerCase()) - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you have any battlestaves?") + interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.HALF_GUILTY, "Do you have any battlestaves?") stage = 0 return true } @@ -377,7 +372,7 @@ class ZaffPlugin : OptionHandler() { if (ammount >= maxStaffs) { interpreter.sendDialogues( 546, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "I'm very sorry! I seem to be out of battlestaves at the", "moment! I expect I'll get some more in by tomorrow,", "though." @@ -387,35 +382,37 @@ class ZaffPlugin : OptionHandler() { } interpreter.sendDialogues( 546, - FacialExpression.HALF_GUILTY, - "Battlestaves cost 8,000 gold pieces each. I have ${maxStaffs - ammount} left.", + core.game.dialogue.FacialExpression.HALF_GUILTY, + "Battlestaves cost 7,000 gold pieces each. I have ${maxStaffs - ammount} left.", "How many would you like to buy?" ) stage = 1 } - 1 -> end().also { ContentAPI.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 - val coinage = amt * 8000 - if(!ContentAPI.inInventory(player, Items.COINS_995, coinage)){ - ContentAPI.sendDialogue(player, "You can't afford that many.") - return@sendInputDialogue - } - if(amt == 0){ return@sendInputDialogue } - - if(ContentAPI.removeItem(player, Item(Items.COINS_995, coinage), Container.INVENTORY)){ - ContentAPI.addItem(player, Items.BATTLESTAFF_1392, amt) - ZaffPlugin.getStoreFile()[player.username.toLowerCase()] = amt + ammount + val coinage = amt * 7000 + if(!inInventory(player, Items.COINS_995, coinage)){ + sendDialogue(player, "You can't afford that many.") + return@sendInputDialogue + } + if(!hasSpaceFor(player, Item(Items.BATTLESTAFF_1392, amt)) && amountInInventory(player, Items.COINS_995) > coinage){ + sendDialogue(player, "You don't have enough inventory space.") + return@sendInputDialogue + } + if(removeItem(player, Item(Items.COINS_995, coinage), Container.INVENTORY)){ + addItem(player, Items.BATTLESTAFF_1392, amt) + getStoreFile()[player.username.toLowerCase()] = amt + ammount } } } 2 -> { interpreter.sendDialogues( player, - FacialExpression.HALF_GUILTY, + core.game.dialogue.FacialExpression.HALF_GUILTY, "Oh, okay then. I'll try again another time." ) stage = 3 @@ -436,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 new file mode 100644 index 000000000..56e81b3bb --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/AllFiredUp.kt @@ -0,0 +1,158 @@ +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 + +/** + * Represents the "All Fired Up" quest. + * @author Ceikry + */ +@Initializable +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 + } + + 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 !!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(Quests.PRIEST_IN_PERIL)) + limitScrolling(player, line, true) + } else { + 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) { + // 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'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) { + // 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) { + // 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, "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) { + // 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, "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) { + // 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 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) + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + player.packetDispatch.sendString("20,000gp,", 277, 8 + 2) + player.packetDispatch.sendString("5,500 Firemaking XP", 277, 9 + 2) + player.packetDispatch.sendString("Full access to the beacon network", 277, 10 + 2) + player.packetDispatch.sendString("1 Quest Point", 277, 11 + 2) + player.packetDispatch.sendItemZoomOnInterface(Items.TINDERBOX_590, 235, 277, 3 + 2) + player.skills.addExperience(Skills.FIREMAKING, 5500.0) + player.inventory.add(Item(995,20000)) + AFUBeacon.resetAllBeacons(player) + player.questRepository.syncronizeTab(player) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/BlazeSharpeyeDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/BlazeSharpeyeDialogue.kt similarity index 88% rename from Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/BlazeSharpeyeDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/quest/allfiredup/BlazeSharpeyeDialogue.kt index 0e4fb2461..9f85e6603 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/BlazeSharpeyeDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/BlazeSharpeyeDialogue.kt @@ -1,10 +1,11 @@ -package rs09.game.content.quest.members.allfiredup +package content.region.misthalin.varrock.quest.allfiredup -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 content.data.Quests @Initializable class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -14,7 +15,7 @@ class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player?.questRepository?.getStage("All Fired Up") ?: -1 + val qstage = player?.questRepository?.getStage(Quests.ALL_FIRED_UP) ?: -1 when(qstage){ 0 -> player.dialogueInterpreter.sendDialogue("He seems uninterested in talking.").also { stage = 1000 } 10 -> player("So, what's going on?").also { stage = 100 } @@ -38,7 +39,7 @@ class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { 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) : DialoguePlugin(player) { 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(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) : DialoguePlugin(player) { 201 -> npc("Well, apparently, not for someone of your Firemaking","calibre and expertise. Now that you've got the hang of","things, we can get this show on the road.").also { stage++ } 202 -> npc("If you'd be so kind as to light the beacon to the west","and report back to me, I can make sure I can clearly","see its glow on the horizon.").also { stage++ } 203 -> npc("It's near the limestone quarry, north-east of Varrock,","west of the Rag and Bone Man's hovel.").also { stage++ } - 204 -> npc("My colleague, Squire Fyre, is tending that beacon.","She'll help you out if you run into any trouble.").also { stage = 116; player.questRepository.getQuest("All Fired Up").setStage(player,40) } + 204 -> npc("My colleague, Squire Fyre, is tending that beacon.","She'll help you out if you run into any trouble.").also { stage = 116; player.questRepository.getQuest(Quests.ALL_FIRED_UP).setStage(player,40) } //Stage = 60 300 -> player("Can you really see it from this far away?").also { stage++ } 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) : DialoguePlugin(player) { 410 -> npc("Imminently, I'm sure - we're just waiting for the word","from King Roald. Speaking of which, have you reported","back to him about the progress we've made?").also { stage++ } 411 -> player("Not yet, I'm afraid.").also { stage++ } 412 -> npc("Well, what are you waiting for? This is a serious","matter! I'm sure King Roald is on the edge of his","throne, waiting for the news.").also { stage++ } - 413 -> player("I'll get right on that.").also { stage = 1000; player.questRepository.getQuest("All Fired Up").setStage(player,90) } - 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/kotlin/rs09/game/content/quest/members/allfiredup/KingRoaldAFUDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/KingRoaldAFUDialogue.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/KingRoaldAFUDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/quest/allfiredup/KingRoaldAFUDialogue.kt index ef7a4fce6..4c5d4480a 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/KingRoaldAFUDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/KingRoaldAFUDialogue.kt @@ -1,9 +1,10 @@ -package rs09.game.content.quest.members.allfiredup +package content.region.misthalin.varrock.quest.allfiredup import core.game.node.entity.skill.Skills -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +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/kotlin/rs09/game/content/quest/members/allfiredup/SquireFyreDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/SquireFyreDialogue.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/SquireFyreDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/quest/allfiredup/SquireFyreDialogue.kt index e0a572a66..606e784dc 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/SquireFyreDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/allfiredup/SquireFyreDialogue.kt @@ -1,10 +1,12 @@ -package rs09.game.content.quest.members.allfiredup +package content.region.misthalin.varrock.quest.allfiredup -import core.game.content.dialogue.DialoguePlugin +import core.game.dialogue.DialoguePlugin import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable -import rs09.game.content.activity.allfiredup.BeaconState +import content.minigame.allfiredup.BeaconState +import core.api.* +import content.data.Quests @Initializable class SquireFyreDialogue(player: Player? = null) : DialoguePlugin(player){ @@ -14,7 +16,7 @@ class SquireFyreDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = (args[0] as NPC).getShownNPC(player) - val qstage = player.questRepository.getQuest("All Fired Up").getStage(player) + val qstage = player.questRepository.getQuest(Quests.ALL_FIRED_UP).getStage(player) when(qstage){ 40 -> player("Hi there. I'm helping Blaze and King Roald test the","beacon network. Can you see it from here? Blaze said","you have pretty sharp eyes.").also { stage = 100 } else -> npc("Carry on, friend.").also { stage = 1000 } @@ -26,8 +28,7 @@ class SquireFyreDialogue(player: Player? = null) : DialoguePlugin(player){ when(stage){ 100 -> npc("Of course I can see it. I haven't spent my entire life","practising my seeing skills for nothing! I'm happy to","report that the fire near Blaze is burning brightly.").also { stage++ } 101 -> player("Terrific! Blaze has asked me to light this fire as well, so","he can see how things look from his vantage point.").also { stage++ } - 102 -> npc("Be my guest!").also { stage++; player.questRepository.getQuest("All Fired Up").setStage(player,50); player.varpManager.get(1283).setVarbit(0, - 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) : DialoguePlugin(player){ 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 new file mode 100644 index 000000000..d498fd07a --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSCutsceneTrigger.kt @@ -0,0 +1,29 @@ +package content.region.misthalin.varrock.quest.demonslayer + +import core.api.* +import core.game.world.map.zone.ZoneBorders +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 { + + override fun defineAreaBorders() : Array { + return arrayOf(ZoneBorders(3222, 3364, 3234, 3375)) + } + + override fun areaEnter(entity: Entity) { + if (entity !is Player) return + + 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) + + if (quest.getStage(entity) == 30 && !alreadyInCutscene && hasSilverlight) { + ActivityManager.start(entity, "Demon Slayer Cutscene", false) + setAttribute(entity, "demon-slayer:cutscene", true) + } + } +} diff --git a/Server/src/main/java/core/game/content/quest/free/demonslayer/DSlayerDrainPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSlayerDrainPlugin.java similarity index 88% rename from Server/src/main/java/core/game/content/quest/free/demonslayer/DSlayerDrainPlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSlayerDrainPlugin.java index 1d349e2d0..253484745 100644 --- a/Server/src/main/java/core/game/content/quest/free/demonslayer/DSlayerDrainPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DSlayerDrainPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.demonslayer; +package content.region.misthalin.varrock.quest.demonslayer; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -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/java/core/game/content/quest/free/demonslayer/DemonSlayer.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayer.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/free/demonslayer/DemonSlayer.java rename to Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayer.java index 262ec28b8..0492bb2a9 100644 --- a/Server/src/main/java/core/game/content/quest/free/demonslayer/DemonSlayer.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayer.java @@ -1,17 +1,18 @@ -package core.game.content.quest.free.demonslayer; +package content.region.misthalin.varrock.quest.demonslayer; import java.util.ArrayList; import java.util.Collections; import java.util.List; import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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.Item; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import content.data.Quests; /** * Represents the demon slayer quest. @@ -50,12 +51,12 @@ 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 public Quest newInstance(Object object) { - PluginManager.definePlugins(new DemonSlayerPlugin(), new DSlayerDrainPlugin(), new DemonSlayerCutscene(), new WallyCutscenePlugin(), new GypsyArisDialogue(), new SirPyrsinDialogue(), new TraibornDialogue(), new CaptainRovinDialogue()); + ClassScanner.definePlugins(new DemonSlayerPlugin(), new DSlayerDrainPlugin(), new DemonSlayerCutscene(), new WallyCutscenePlugin(), new GypsyArisDialogue(), new SirPyrsinDialogue(), new TraibornDialogue(), new CaptainRovinDialogue()); return this; } @@ -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/java/core/game/content/quest/free/demonslayer/DemonSlayerCutscene.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerCutscene.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/free/demonslayer/DemonSlayerCutscene.java rename to Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerCutscene.java index 6de56ef71..7c46309f0 100644 --- a/Server/src/main/java/core/game/content/quest/free/demonslayer/DemonSlayerCutscene.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerCutscene.java @@ -1,12 +1,12 @@ -package core.game.content.quest.free.demonslayer; +package content.region.misthalin.varrock.quest.demonslayer; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; @@ -16,12 +16,11 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; -import core.game.world.map.zone.ZoneBorders; import core.game.world.update.flag.context.Animation; import core.net.packet.PacketRepository; import core.net.packet.context.CameraContext; @@ -30,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 @@ -120,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); @@ -177,7 +179,7 @@ public final class DemonSlayerCutscene extends CutscenePlugin { @Override public void register() { - register(new ZoneBorders(3222, 3364, 3234, 3375)); + //register(new ZoneBorders(3222, 3364, 3234, 3375)); try { new DarkWizardNPC().newInstance(null); new DelrithNPC().newInstance(null); @@ -420,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; } @@ -518,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 new file mode 100644 index 000000000..87740d6df --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/DemonSlayerPlugin.java @@ -0,0 +1,97 @@ +package content.region.misthalin.varrock.quest.demonslayer; + +import core.cache.def.impl.NPCDefinition; +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.entity.player.link.quest.Quest; +import core.game.node.item.Item; +import core.game.node.scenery.Scenery; +import core.game.world.map.Location; +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 + * @date 3/1/14 + */ +public final class DemonSlayerPlugin extends OptionHandler { + + /** + * Represents the object id of the drain. + */ + public static final int DRAIN_ID = 17424; + + /** + * Represents the location of the lumbridge sewers. + */ + private static final Location SEWER_LOCATION = new Location(3237, 9858, 0); + + @Override + public Plugin newInstance(Object arg) throws Throwable { + 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); + NPCDefinition.forId(DemonSlayerCutscene.WEAKENED_DELRITH).getHandlers().put("option:banish", this); + return this; + } + + @Override + public boolean handle(Player player, Node node, String option) { + 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: + player.getDialogueInterpreter().open(8427322); + break; + case 879: + if (!player.getEquipment().containsItem(DemonSlayer.SILVERLIGHT)) { + player.getPacketDispatch().sendMessage("I'd better wield Silverlight first."); + return true; + } + player.face(((NPC) node)); + player.getProperties().getCombatPulse().attack(node); + return true; + case DRAIN_ID: + if (quest.getStage(player) == 20) { + player.getDialogueInterpreter().open(883, 883, "key"); + return true; + } else { + player.sendMessage("You search the castle drain and find nothing of value."); + } + return true; + case 17429: + if (quest.getStage(player) == 20 && player.getInventory().add(DemonSlayer.FIRST_KEY)) { + 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."); + } else { + if (player.getInventory().freeSlots() == 0) { + player.getPacketDispatch().sendMessage("Not enough inventory space."); + return true; + } + } + break; + } + return true; + } + + @Override + public Location getDestination(final Node node, final Node n) { + if (n instanceof NPC) { + return n.getLocation().transform(2, 0, 0); + } + if (n.getId() == DRAIN_ID) { + return Location.create(3226, 3496, 0); + } + return null; + } + +} diff --git a/Server/src/main/java/core/game/content/quest/free/demonslayer/GypsyArisDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/GypsyArisDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/free/demonslayer/GypsyArisDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/demonslayer/GypsyArisDialogue.java index 5c4d4aa4a..7d0c85262 100644 --- a/Server/src/main/java/core/game/content/quest/free/demonslayer/GypsyArisDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/GypsyArisDialogue.java @@ -1,22 +1,23 @@ -package core.game.content.quest.free.demonslayer; +package content.region.misthalin.varrock.quest.demonslayer; import core.game.component.Component; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.activity.ActivityManager; +import core.game.activity.CutscenePlugin; +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.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.update.flag.context.Animation; 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/java/core/game/content/quest/free/demonslayer/SirPyrsinDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/SirPyrsinDialogue.java similarity index 88% rename from Server/src/main/java/core/game/content/quest/free/demonslayer/SirPyrsinDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/demonslayer/SirPyrsinDialogue.java index 059c66caf..8be176a64 100644 --- a/Server/src/main/java/core/game/content/quest/free/demonslayer/SirPyrsinDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/SirPyrsinDialogue.java @@ -1,13 +1,21 @@ -package core.game.content.quest.free.demonslayer; +package content.region.misthalin.varrock.quest.demonslayer; -import core.game.content.dialogue.DialoguePlugin; +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 rs09.game.world.GameWorld; +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 new file mode 100644 index 000000000..5ea8e3622 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/TraibornDialogue.java @@ -0,0 +1,393 @@ +package content.region.misthalin.varrock.quest.demonslayer; + +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.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 content.data.Quests; + +/** + * Represents the dialogue used to handle the Traiborn NPC. + * @author 'Vexia + * @date 3/1/14 + */ +public class TraibornDialogue extends DialoguePlugin { + + /** + * Represents the bones item. + */ + private static final Item[] BONES = {new Item(526, 25), new Item(2530, 25)}; + + /** + * Represents the animation of giving the key. + */ + private static final Animation ANIMATION = new Animation(536); + + /** + * Represents the quest instance. + */ + private Quest quest; + + /** + * Constructs a new {@code TraibornDialogue} {@code Object}. + */ + public TraibornDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code TraibornDialogue} {@code Object}. + * @param player the player. + */ + public TraibornDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new TraibornDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); + switch (quest.getStage(player)) { + case 20: + if (player.getAttribute("demon-slayer:traiborn", false)) { + npc("How are you doing finding bones?"); + stage = 380; + } else { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ello young thingummywut."); + stage = 0; + } + break; + default: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ello young thingummywut."); + stage = 0; + break; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (quest.getStage(player)) { + case 20: + switch (stage) { + case 0: + if (player.getInventory().containsItem(DemonSlayer.THIRD_KEY) && player.getBank().containsItem(DemonSlayer.THIRD_KEY)) { + options("What's a thingummywut?", "Teach me to be a mighty powerful wizard."); + stage = 1; + } else { + options("What's a thingummywut?", "Teach me to be a mighty powerful wizard.", "I need to get a key given to you by Sir Prysin."); + stage = 1; + } + break; + case 1: + switch (buttonId) { + case 1: + case 2: + handleDefault(buttonId); + break; + case 3: + player("I need to get a key given to you by Sir Prysin."); + stage = 300; + break; + } + break; + case 300: + npc("Sir Prysin? Who's that? What would I want his key", "for?"); + stage = 301; + break; + case 301: + player("Well, have you got any keys knocking around?"); + stage = 302; + break; + case 302: + npc("Now you come to mention it, yes I do have a key. It's", "in my special closet of valuable stuff. Now how do I get", "into that?"); + stage = 303; + break; + case 303: + npc("I sealed it using one of my magic rituals. So it would", "makes sense that another ritual would open it again."); + stage = 304; + break; + case 304: + player("So do you know what ritual to use?"); + stage = 305; + break; + case 305: + npc("Let me think a second."); + stage = 306; + break; + case 306: + npc("Yes a simple drazier style ritual should suffice. Hmm,", "main problem with that is I'll need 25 sets of bones.", "Now where am I going to get hold of something like", "that?"); + stage = 307; + break; + case 307: + options("Hmm, that's too bad. I really need that key.", "I'll get the bones for you."); + stage = 308; + break; + case 308: + switch (buttonId) { + case 1: + player("Hmm, that's too bad. I really need that key."); + stage = 330; + break; + case 2: + player("I'll get the bones for you."); + stage = 340; + break; + } + break; + case 330: + npc("Ah well, sorry I couldn't be any more help."); + stage = 331; + break; + case 331: + end(); + break; + case 340: + npc("Ooh that would be very good of you."); + stage = 341; + break; + case 341: + player("Ok I'll speak to you when I've got some bones."); + stage = 342; + break; + case 342: + player.setAttribute("/save:demon-slayer:traiborn", true); + end(); + break; + case 380: + if (player.getInventory().containsItem(BONES[0]) || player.getInventory().containsItem(BONES[1])) { + player("I have some bones."); + stage = 382; + } else { + player("I don't have all the bones yet."); + stage = 381; + } + break; + case 382: + npc("Give 'em here then."); + stage = 383; + break; + case 383: + interpreter.sendDialogue("You give Traiborn 25 sets of bones."); + stage = 384; + break; + case 384: + npc("Hurrah! That's all 25 sets of bones."); + stage = 385; + break; + case 385: + npc.animate(new Animation(4602)); + npc("Wings of dark and colour too,", "Spreading in the morning dew;", "Locked away I have a key;", "Return it now, please, unto me."); + stage = 386; + break; + case 386: + final Scenery object = new Scenery(17434, Location.create(3113, 3161, 1), 11, 1); + SceneryBuilder.add(object); + npc.faceLocation(object.getLocation()); + npc.animate(ANIMATION); + if (!player.getInventory().containsItem(BONES[0]) && !player.getInventory().containsItem(BONES[1])) { + end(); + return true; + } + if (player.getInventory().remove(BONES[0]) || player.getInventory().remove(BONES[1])) { + player.removeAttribute("demon-slayer:traiborn"); + player.getInventory().add(DemonSlayer.THIRD_KEY); + interpreter.sendItemMessage(DemonSlayer.THIRD_KEY.getId(), "Traiborn hands you a key."); + stage = 387; + } + GameWorld.getPulser().submit(new Pulse(1) { + int counter = 0; + + @Override + public boolean pulse() { + switch (counter++) { + case 5: + npc.face(player); + break; + case 7: + SceneryBuilder.remove(object); + return true; + } + return false; + } + }); + break; + case 387: + player("Thank you very much."); + stage = 388; + break; + case 388: + npc("Not a problem for a friend of Sir What's-his-face."); + stage = 389; + break; + case 389: + end(); + break; + case 381: + end(); + break; + } + break; + case 0: + switch (stage) { + default: + handleDefault(buttonId); + break; + } + break; + } + return true; + } + + /** + * Method used to handle the default button id. + * @param buttonId the button id. + */ + private final void handleDefault(int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "What's a thingummywut?", "Teach me to be a mighty and powerful wizard."); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's a thingummywut?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Teach me to be a mighty and powerful wizard."); + stage = 20; + break; + } + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A thingummywut? Where? Where?"); + stage = 11; + break; + case 11: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Those pesky thingummywuts. They get everywhere.", "They leave a terrible mess too."); + stage = 12; + break; + case 12: + interpreter.sendOptions("Select an Option", "Err you just called me a thingummywut.", "Tell me what they look like and I'll mask 'em."); + stage = 13; + break; + case 13: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err you just called me thingummywut."); + stage = 100; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me what they look like and I'll mash 'em."); + stage = 120; + break; + } + break; + case 120: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't be ridiculous. No-one has ever seen one."); + stage = 121; + break; + case 121: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "They're invisible, or a myth, or a figment of my", "imagination. Can't remember which right now."); + stage = 122; + break; + case 122: + end(); + break; + case 100: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're a thingummywut? I've never seen one up close", "before. They said I was mad!"); + stage = 101; + break; + case 101: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Now you are my proof! There ARE thingummywuts in", "this tower. Now where can I find a cage big enough to", "keep you?"); + stage = 102; + break; + case 102: + interpreter.sendOptions("Select an Option", "Err I'd better be off really.", "They're right, you are mad."); + stage = 103; + break; + case 103: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err I'd better be off really."); + stage = 110; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "They're right, you are mad."); + stage = 130; + break; + } + break; + case 130: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That's a pity. I thought maybe they were winding me", "up."); + stage = 131; + break; + case 131: + end(); + break; + case 110: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh ok, have a good time, and watch out for sheep!", "They're more cunning than they look."); + stage = 111; + break; + case 111: + end(); + break; + case 20: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wizard eh? You don't want any truck with that sort.", "They're not to be trusted. That's what I've heard", "anyways."); + stage = 21; + break; + case 21: + interpreter.sendOptions("Select an Option", "So aren't you a wizard?", "Oh I'd better stop talking to you then."); + stage = 22; + break; + case 22: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So aren't you a wizard?"); + stage = 40; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh I'd better stop talking to you then."); + stage = 60; + break; + } + break; + case 60: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Cheerio then. It was nice chatting to you."); + stage = 61; + break; + case 61: + end(); + break; + case 40: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How dare you? Of course I'm a wizard. Now don't be", "so cheeky or I'll turn you into a frog."); + stage = 41; + break; + case 41: + end(); + break; + } + } + + @Override + public int[] getIds() { + return new int[] { 881 }; + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/demonslayer/WallyCutscenePlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/WallyCutscenePlugin.java similarity index 86% rename from Server/src/main/java/core/game/content/quest/free/demonslayer/WallyCutscenePlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/demonslayer/WallyCutscenePlugin.java index b5bddd7dd..190989d52 100644 --- a/Server/src/main/java/core/game/content/quest/free/demonslayer/WallyCutscenePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/demonslayer/WallyCutscenePlugin.java @@ -1,15 +1,16 @@ -package core.game.content.quest.free.demonslayer; +package content.region.misthalin.varrock.quest.demonslayer; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; import core.game.node.entity.player.Player; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; 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/java/core/game/content/dialogue/CabinBoyJenkins.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CabinBoyJenkins.java similarity index 86% rename from Server/src/main/java/core/game/content/dialogue/CabinBoyJenkins.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CabinBoyJenkins.java index bbace5ed8..096bbba01 100644 --- a/Server/src/main/java/core/game/content/dialogue/CabinBoyJenkins.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CabinBoyJenkins.java @@ -1,9 +1,13 @@ -package core.game.content.dialogue; +package content.region.misthalin.varrock.quest.dragonslayer; +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.entity.player.link.quest.Quest; +import content.data.Quests; + +import static core.api.ContentAPIKt.getQuestStage; /** * Represents the cabin boy jenkins dialogue. @@ -11,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}. */ @@ -42,10 +40,9 @@ 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?"); + npc("Ahoy! Whay d'ye think of yer ship then?"); stage = 0; break; case 40: @@ -59,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/java/core/game/content/quest/free/dragonslayer/CrandorMapPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CrandorMapPlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/CrandorMapPlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CrandorMapPlugin.java index 454bf72e3..a01c96817 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/CrandorMapPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/CrandorMapPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; 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 new file mode 100644 index 000000000..604bfb0c8 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSChestDialogue.java @@ -0,0 +1,76 @@ +package content.region.misthalin.varrock.quest.dragonslayer; + +import core.game.dialogue.DialoguePlugin; +import core.game.node.entity.player.Player; +import core.game.node.item.GroundItemManager; + +/** + * Represents the dialogue plugin used for the dragon slayer chest. + * @author 'Vexia + * @version 1.0 + */ +public final class DSChestDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code DSChestDialogue} {@code Object}. + */ + public DSChestDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code DSChestDialogue} {@code Object}. + * @param player the player. + */ + public DSChestDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new DSChestDialogue(player); + } + + @Override + public boolean open(Object... args) { + interpreter.sendDialogue("As you open the chest, you notice an inscription on the lid:"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + interpreter.sendDialogue("Here I rest the map to my beloved home. To whoever finds it, I beg", "of you, let it be. I was honour-bound not to destroy the map piece,", "but I have used all my magical skill to keep it from being recovered."); + 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", "bringing its wrath down upon another land."); + stage = 2; + break; + case 2: + interpreter.sendDialogue("I cannot stop you from taking this map piece now, but think on this:", "if you can slay the Dragon of Crandor, you are a greater hero than", "my land ever produced. There is no shame in backing out now."); + stage = 3; + break; + case 3: + interpreter.sendItemMessage(DragonSlayer.MAGIC_PIECE.getId(), "You find a map piece in the chest."); + stage = 4; + break; + case 4: + if (!player.getInventory().add(DragonSlayer.MAGIC_PIECE)) { + GroundItemManager.create(DragonSlayer.MAGIC_PIECE, player); + } + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 3802875 }; + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..20efdab26 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSListeners.kt @@ -0,0 +1,29 @@ +package content.region.misthalin.varrock.quest.dragonslayer + +import core.api.sendMessage +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() { + on(Scenery.CELL_DOOR_40184, IntType.SCENERY, "open") { player, _ -> + 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/java/core/game/content/quest/free/dragonslayer/DSMagicDoorPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSMagicDoorPlugin.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/DSMagicDoorPlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSMagicDoorPlugin.java index 4ac5ea792..0501b06f0 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/DSMagicDoorPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSMagicDoorPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -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/java/core/game/content/quest/free/dragonslayer/DSNedNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSNedNPC.java similarity index 77% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/DSNedNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSNedNPC.java index 056195966..4040b3190 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/DSNedNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSNedNPC.java @@ -1,8 +1,9 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; +import content.data.Quests; /** * Represents the dragon slayer npc. @@ -39,7 +40,7 @@ public final class DSNedNPC extends AbstractNPC { @Override public boolean isHidden(final Player player) { - return player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) != 30 && player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) != 40; + return player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 30 && player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) != 40; } @Override diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.kt b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.kt 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/java/core/game/content/quest/free/dragonslayer/DragonSlayerCutscene.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerCutscene.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/DragonSlayerCutscene.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerCutscene.java index 0c86708bf..b1a4fbc34 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/DragonSlayerCutscene.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerCutscene.java @@ -1,20 +1,20 @@ - package core.game.content.quest.free.dragonslayer; + package content.region.misthalin.varrock.quest.dragonslayer; import java.util.ArrayList; import java.util.List; import core.game.component.Component; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.travel.ship.Ships; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialoguePlugin; +import content.global.travel.ship.Ships; 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.quest.Quest; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; import core.game.world.update.flag.context.Animation; @@ -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)); @@ -196,7 +200,7 @@ public final class DragonSlayerCutscene extends CutscenePlugin { @Override public Pulse getStartPulse() { PacketRepository.send(MinimapState.class, new MinimapStateContext(player, getMapState())); - player.getInterfaceManager().hideTabs(getRemovedTabs()); + player.getInterfaceManager().removeTabs(getRemovedTabs()); return new Pulse(1) { int counter = 0; @@ -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/java/core/game/content/quest/free/dragonslayer/DragonSlayerPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java similarity index 90% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/DragonSlayerPlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java index 415cbfb56..f16658df0 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/DragonSlayerPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayerPlugin.java @@ -1,10 +1,10 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +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; @@ -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,24 +118,16 @@ 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; } - player.getPulseManager().clear("interaction:attack:" + node.hashCode()); player.getProperties().getCombatPulse().attack(node); player.face((Entity) node); break; @@ -151,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) { @@ -179,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")) { @@ -220,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."); } } @@ -230,7 +216,6 @@ public final class DragonSlayerPlugin extends OptionHandler { break; case 745: if (option.equals("attack")) { - player.getPulseManager().clear("interaction:attack:" + node.hashCode()); player.getProperties().getCombatPulse().attack(node); return true; } diff --git a/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/DukeHoracioDSDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DukeHoracioDSDialogue.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/DukeHoracioDSDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DukeHoracioDSDialogue.kt index e68fcf4ae..e61630a3b 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/DukeHoracioDSDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DukeHoracioDSDialogue.kt @@ -1,12 +1,11 @@ -package rs09.game.content.quest.free.dragonslayer +package content.region.misthalin.varrock.quest.dragonslayer -import core.game.content.quest.free.dragonslayer.DragonSlayer +import content.region.misthalin.varrock.quest.dragonslayer.DragonSlayer import core.game.node.entity.player.Player -import core.game.node.entity.player.link.diary.DiaryType import core.game.node.item.GroundItemManager -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +import core.game.dialogue.DialogueFile +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE class DukeHoracioDSDialogue(val questStage: Int) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { @@ -24,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( @@ -51,8 +50,6 @@ class DukeHoracioDSDialogue(val questStage: Int) : DialogueFile() { GroundItemManager.create(DragonSlayer.SHIELD, player) } interpreter!!.sendItemMessage(DragonSlayer.SHIELD, "The Duke hands you a heavy orange shield.") - // Obtain an Anti-dragonbreath shield from Duke Horacio - player!!.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 5) stage = END_DIALOGUE } } @@ -80,8 +77,6 @@ class DukeHoracioDSDialogue(val questStage: Int) : DialogueFile() { GroundItemManager.create(DragonSlayer.SHIELD, player) } interpreter!!.sendItemMessage(DragonSlayer.SHIELD, "The Duke hands you the shield.") - // Obtain an Anti-dragonbreath shield from Duke Horacio - player!!.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 5) stage = END_DIALOGUE } } 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 new file mode 100644 index 000000000..fa25a9fdb --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ElvargNPC.java @@ -0,0 +1,330 @@ +package content.region.misthalin.varrock.quest.dragonslayer; + +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.combat.equipment.ArmourSet; +import core.game.node.entity.combat.equipment.FireType; +import core.game.node.entity.impl.Projectile; +import core.game.node.entity.impl.Animator.Priority; +import core.game.node.entity.npc.AbstractNPC; +import core.game.node.entity.player.Player; +import core.game.node.item.GroundItemManager; +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.update.flag.context.Animation; +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. + * @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 }; + + /** + * 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(); + + /** + * Constructs a new {@code ElvargNPC} {@code Object}. + */ + public ElvargNPC() { + super(0, null); + } + + /** + * 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 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 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 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; + } + + @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(Quests.DRAGON_SLAYER).getStage(player) > 40) { + if(message) { + player.getPacketDispatch().sendMessage("You have already slain Elvarg."); + } + return false; + } + return super.isAttackable(entity, style, message); + } + + @Override + public int getDragonfireProtection(boolean fire) { + return 0x2 | 0x4 | 0x8; + } + + /** + * Handles the Elvarg combat swing handler. + * @author Emperor + */ + static class ElvargCombatSwingHandler extends CombatSwingHandler { + + /** + * The style. + */ + private CombatStyle style = CombatStyle.RANGE; + + /** + * 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 DragonFire Attack */ + private static final DragonfireSwingHandler DRAGONFIRE = new DragonfireSwingHandler(false, 60, null, true); + + /** + * Constructs a new {@Code ElvargCombatSwingHandler} {@Code CombatStyle}. + * The Combat style. + */ + public ElvargCombatSwingHandler() { + super(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); + } + + @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 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 int calculateHit(Entity entity, Entity victim, double modifier) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateHit(entity, victim, modifier); + } + + return calculateDragonfireMaxHit(victim, 60, false, 3, true); + } + + @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 ArmourSet getArmourSet(Entity e) { + return style.getSwingHandler().getArmourSet(e); + } + + @Override + public double getSetMultiplier(Entity e, int skillId) { + return style.getSwingHandler().getSetMultiplier(e, skillId); + } + + @Override + public void impact(Entity entity, Entity victim, BattleState state) { + style.getSwingHandler().impact(entity, victim, state); + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + style = CombatStyle.RANGE; + int hit = 0; + int ticks = 1; + + if (victim.getCenterLocation().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/java/core/game/content/quest/free/dragonslayer/GuildmasterDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/GuildmasterDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/GuildmasterDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/GuildmasterDialogue.java index 548ddf8d4..a94d7ba5e 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/GuildmasterDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/GuildmasterDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; -import core.game.content.dialogue.DialoguePlugin; +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.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/java/core/game/content/quest/free/dragonslayer/MazeDemonNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeDemonNPC.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeDemonNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeDemonNPC.java index 05545cf9f..3e4a92af1 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeDemonNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeDemonNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeGhostNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeGhostNPC.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeGhostNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeGhostNPC.java index af2934b63..497cad990 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeGhostNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeGhostNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeSkeletonNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeSkeletonNPC.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeSkeletonNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeSkeletonNPC.java index 2039b4603..55afb35e2 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeSkeletonNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeSkeletonNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeZombieNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeZombieNPC.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeZombieNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeZombieNPC.java index 68927b4a2..950927e65 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MazeZombieNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MazeZombieNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MeldarMadNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MeldarMadNPC.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/MeldarMadNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MeldarMadNPC.java index cd1d91d91..2b731c0cc 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/MeldarMadNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/MeldarMadNPC.java @@ -1,11 +1,11 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; 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.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.DeathTask; import core.game.node.entity.combat.InteractionType; import core.game.node.entity.combat.equipment.ArmourSet; diff --git a/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/NedDSDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDSDialogue.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/NedDSDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDSDialogue.kt index 127f96292..7b59f9c70 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/NedDSDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDSDialogue.kt @@ -1,9 +1,9 @@ -package rs09.game.content.quest.free.dragonslayer +package content.region.misthalin.varrock.quest.dragonslayer -import core.game.content.quest.free.dragonslayer.DragonSlayer -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +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 new file mode 100644 index 000000000..dcc331bf6 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/NedDialogue.kt @@ -0,0 +1,214 @@ +package content.region.misthalin.varrock.quest.dragonslayer + +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.item.GroundItemManager +import core.game.node.item.Item +import core.game.dialogue.IfTopic +import core.game.dialogue.Topic +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 + + +/** + * Core dialogue plugin for Ned. + * @author Ceikry + */ +class NedDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + /** + * The achievement diary. + */ + private var diary: AchievementDiary? = null + private val level = 2 + + override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + return NedDialogue(player) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npc( + "Why, hello there, lad. Me friends call me Ned. I was a", + "man of the sea, but it's past me now. Could I be", + "making or selling you some rope?" + ) + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> { + 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), + Topic("Yes, I would like some rope.", 10), + Topic("No thanks Ned, I don't need any.", END_DIALOGUE), + Topic("I'd like to ask about my Achievement Diary.", NedDiaryDialogue()) + ) + } + + 10 -> { + npc( + "Well, I can sell you some rope for 15 coins. Or I can", + "be making you some if you gets me 4 balls of wool. I", + "strands them together I does, makes em strong." + ) + stage = 11 + } + 11 -> { + player("You make rope from wool?") + stage = 12 + } + 12 -> { + npc("Of course you can!") + stage = 13 + } + 13 -> { + player("I thought you needed hemp or jute.") + stage = 14 + } + 14 -> { + npc("Do you want some rope or not?") + stage = 15 + } + 15 -> stage = if (!player.inventory.containsItem(WOOL)) { + options( + "Okay, please sell me some rope.", + "That's a little more than I want to pay.", + "I will go and get some wool." + ) + 16 + } else { + options( + "Okay, please sell me some rope.", + "I have some balls of wool. Could you make me some rope?", + "That's a little more than I want to pay." + ) + 17 + } + 17 -> when (buttonId) { + 1 -> { + player("Okay, please sell me some rope.") + stage = 100 + } + 2 -> { + player("I have some balls of wool.", "Could you make me some rope?") + stage = 120 + } + 3 -> { + player("That's a little more than I want to pay.") + stage = 200 + } + } + 16 -> when (buttonId) { + 1 -> { + player("Okay, please sell me some rope.") + stage = 100 + } + 2 -> { + player("That's a little more than I want to pay.") + stage = 200 + } + 3 -> { + player("I will go and get some wool.") + stage = 300 + } + } + 40 -> when (buttonId) { + 1 -> { + player("Ned could you make other things from wool?") + stage = 41 + } + 2 -> { + player("Yes, I would like some rope.") + stage = 10 + } + 3 -> { + player("No thanks Ned, I don't need any.") + stage = 20 + } + } + 100 -> { + npc("There you go, finest rope in " + settings!!.name + ".") + stage = 101 + } + 101 -> { + interpreter.sendDialogue("You hand Ned 15 coins. Ned gives you a coil of rope.") + stage = 102 + } + 102 -> { + if (player.inventory.remove(COINS)) { + if (!player.inventory.add(ROPE)) { + GroundItemManager.create(ROPE, player) + } + } else { + player.packetDispatch.sendMessage("You don't have enough coins to pay for rope.") + } + end() + } + 120 -> { + interpreter.sendDialogues(npc, null, "Sure I can.") + stage = 121 + } + 121 -> { + interpreter.sendDialogue("You hand over 4 balls of wool. Ned gives you a coil of rope.") + stage = 122 + } + 122 -> { + if (player.inventory.remove(WOOL)) { + player.inventory.add(ROPE) + } + end() + } + 200 -> { + npc( + "Well, if you ever need rope that's the price. Sorry.", + "An old sailor needs money for a little drop o' rum." + ) + stage = 201 + } + 201 -> end() + 300 -> { + npc("Aye, you do that. Remember, it takes 4 balls of wool to", "make strong rope.") + stage = 301 + } + 301 -> end() + 20 -> { + npc( + "Well, old Neddy is always here if you do. Tell your", + "friends. I can always be using the business." + ) + stage = 21 + } + 21 -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(743) + } + + companion object { + /** + * Represents the coins item to remove. + */ + private val COINS = Item(995, 15) + + /** + * Represents the rope item to add. + */ + private val ROPE = Item(954) + + /** + * Represents the ball of wool item. + */ + private val WOOL = Item(1759, 4) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/OziachDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/OziachDialogue.java similarity index 97% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/OziachDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/OziachDialogue.java index fb0a5b758..faebde0bd 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/OziachDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/OziachDialogue.java @@ -1,10 +1,11 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; -import core.game.content.dialogue.DialoguePlugin; +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; /** * 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/java/core/game/content/quest/free/dragonslayer/WormbrainDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainDialogue.java similarity index 90% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/WormbrainDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainDialogue.java index 723a7503f..84feb6bfc 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/WormbrainDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainDialogue.java @@ -1,11 +1,13 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; -import core.game.content.dialogue.DialoguePlugin; +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.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 new file mode 100644 index 000000000..f755bd88f --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/WormbrainNPC.java @@ -0,0 +1,74 @@ +package content.region.misthalin.varrock.quest.dragonslayer; + +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.node.item.GroundItemManager; +import core.game.world.map.Location; +import content.data.Quests; + +/** + * Represents the worm brain npc. + * @author 'Vexia + * @versin 1.0 + */ +public final class WormbrainNPC extends AbstractNPC { + + /** + * The NPC ids of NPCs using this plugin. + */ + private static final int[] ID = { 745 }; + + /** + * Constructs a new {@code WormBrainPlugin} {@code Object}. + */ + public WormbrainNPC() { + super(0, null); + } + + /** + * Constructs a new {@code AlKharidWarriorPlugin} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + private WormbrainNPC(int id, Location location) { + super(id, location); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new WormbrainNPC(id, location); + } + + @Override + public void finalizeDeath(final Entity killer) { + super.finalizeDeath(killer); + if (killer instanceof Player) { + 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."); + } + } + } + + @Override + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { + if (entity instanceof Player) { + final Player player = (Player) entity; + 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."); + } + return false; + } + } + return super.isAttackable(entity, style, message); + } + + @Override + public int[] getIds() { + return ID; + } + +} diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/ZombieRatNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ZombieRatNPC.java similarity index 86% rename from Server/src/main/java/core/game/content/quest/free/dragonslayer/ZombieRatNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ZombieRatNPC.java index d56ae4205..65a602673 100644 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/ZombieRatNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/ZombieRatNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.dragonslayer; +package content.region.misthalin.varrock.quest.dragonslayer; import core.game.node.entity.Entity; import core.game.node.entity.npc.AbstractNPC; @@ -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 new file mode 100644 index 000000000..1961c4586 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/AvanDialogue.kt @@ -0,0 +1,212 @@ +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.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +@Initializable +class AvanDialogue (player: Player? = null): DialoguePlugin(player) { + + val CREST_PIECE_AVAN: Item = Item(779) + + 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(Quests.FAMILY_CREST) ?: -1 + + if (qstage == 100) { + options("Can you change my gauntlets for me?", "Nevermind") + stage = 6000 + return true + } + + if(qstage < 13){ + npc("What? Can't you see I'm busy?").also { stage = 1000 } + }else{ + when(qstage){ + 13 -> options("Why are you lurking around a scorpion pit?", "I'm looking for a man... his name is Avan Fitzharmon.").also{stage = 2} + 14 -> npc("So how are you doing getting me my perfect gold jewelry?").also{stage = 100} + 15 -> npc("So how are you doing getting me my perfect gold jewelry?").also{stage = 200} + 16 -> player("Where did you say I could find your brother Johnathon again?").also{stage = 304} + 17 -> npc("Greetings again, adventurer. How are you doing on retrieving the crest pieces?").also{stage = 400} + 18 -> npc("Greetings again, adventurer. How are you doing on retrieving the crest pieces?").also{stage = 400} + 19 -> npc("Greetings again, adventurer. How are you doing on retrieving the crest pieces?").also{stage = 400} + } + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + + when(stage){ + 1 -> player("Well, sooooorry...").also{stage = 1000} + + 2->when(buttonId){ + 1 -> npc("It's a good place to find gold...").also{stage = 1000} + 2 -> npc("Then you have found him. My name is Avan Fitzharmon.").also{stage++} + } + + 3 -> player("You have a part of your family crest. I am on a quest ", + "to retrieve all of the fragmented pieces ", + "and restore the crest.").also{stage++} + + 4 -> npc("Ha! I suppose one of my worthless brothers ", + "has sent you on this quest then?").also{stage++} + + 5 -> player("No, it was your father who has asked me to do this for him.").also{stage++} + + 6 -> npc("My... my father wishes this? Then that is a different matter. ", + "I will let you have my crest shard, adventurer, ", + "but you must first do something for me.").also{stage++} + + 7 -> npc("There is a certain lady I am trying to impress. ", + "As a man of noble birth, I can not give her just ", + "any gold trinket to show my devotion. ").also{stage++} + + 8 -> npc("What I intend to give her, is a golden ring, " , + "embedded with the finest precious red stone available, " , + "and a necklace to match this ring. ").also { stage++ } + + 9 -> npc("The problem however for me, is that ", + "not just any old gold will be suitable. ", + "I seek only the purest, the most high quality of gold ", + "what I seek, if you will, is perfect gold.").also{stage++} + 10 -> npc("None of the gold around here is even " , + "remotely suitable in terms of quality. ", + "I have searched far and wide for the perfect gold I desire, ", + "but have had no success so in finding it I am afraid. ").also { stage++ } + + 11 -> npc("If you can find me my perfect gold, " , + "make a ring and necklace from it, and add rubies to them, ", + "I will gladly hand over my fragment of " , + "the family crest to you.").also{stage++} + + 12 -> player("Can you give me any help on finding this 'perfect gold'?").also{stage++} + + 13 -> npc("I thought I had found a solid lead on its whereabouts ", + "when I heard of a dwarf who is an expert " , + "on gold who goes by the name of 'Boot'. ").also { stage++ } + + 14 -> npc("Unfortunately he has apparently returned to this home,", + "somewhere in the mountains, " , + "and I have no idea how to find him.").also { stage++} + + 15 -> player("Well, I'll see what I can do.").also{ + stage = 1000 + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 14) + } + + 100 -> player("I'm still after that 'perfect gold'.").also { stage++ } + + 101 -> npc("I know how you feel... for such a long time " , + "I have searched and searched for the elusive perfect gold... " , + "I thought I had gotten a good lead on finding it ").also{stage++} + + 102 -> npc("when I heard talk of a dwarven expert on gold named Boot " , + "some time back, but unfortunately for me," , + " he has returned to his mountain home, which I cannot find.").also { stage = 1000} + + 200 -> if(!player.inventory.containItems(774,773)){ + npc("I have spoken to Boot the dwarf about the location " , + "of 'perfect gold', " , + "but haven't managed to make you your jewelry yet.").also { stage++ } + } + else{ + player("I have the ring and necklace right here.") + 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(Quests.FAMILY_CREST).setStage(player, 16) + player.inventory.remove(Item(774), Item(773)) + player.inventory.add(CREST_PIECE_AVAN) + stage++ + } + 301 -> npc("These... these are exquisite! E" , + "XACTLY what I was searching for all of this time! " , + "Please, take my crest fragment!").also { stage++ } + + 302 -> npc("Now, I suppose you will be wanting to find my brother " , + "Johnathon who is in possession of the " , + "final piece of the family's crest?").also { stage++ } + + 303 -> player("That's correct.").also{stage++} + + 304 -> npc("Well, the last I heard of my brother Johnathon," , + " he was studying the magical arts, " , + "and trying to hunt some demon or other out in The Wilderness.").also{stage++} + 305 -> npc("Unsurprisingly, I do not believe he is doing a particularly good job of things, ", + "and spends most of his time recovering from his injuries " , + "in some tavern or other near the eastern edge of The Wilderness. " , + "You'll probably find him still there.").also{stage++} + + 306 -> player("Thanks Avan.").also { stage = 1000 } + + 400 -> player("I am still working on it.").also{stage++} + + 401 -> npc("I hope you succeed for my father's sake.").also{ + if(player.inventory.containItems(CREST_PIECE_AVAN.id, 782) || player.bank.containItems(CREST_PIECE_AVAN.id, 782)){ + stage = 1000 + } + else{ + stage++ + } + } + + 402 -> player("I have lost the fragment you gave me...").also{stage++} + 403 -> npc("I have a confession myself adventurer... " , + "I did not fully trust you with the actual part ", + "of my family's crest before, and gave you a " , + "worthless replica before... ").also{stage++} + 404-> npc("In hindsight, it seems I was right. " , + "I will give you the real piece now, ", + "but please try not to lose it; " , + "it is a priceless family heirloom.").also{stage = 1000 + player.inventory.add(CREST_PIECE_AVAN)} + + 6000 -> when (buttonId) { + 1 -> { + var freeThisTime = getAttribute(player, "family-crest:gauntlets", Items.FAMILY_GAUNTLETS_778) == Items.FAMILY_GAUNTLETS_778 + npc("Yes certainly, though it will cost you 25,000 coins" + if (freeThisTime) " next time." else ".").also { stage = 6001 } + } + 2 -> player("Never mind").also{ stage = 1000 } + } + 6001 -> options("Great thanks!", "No that's okay thanks.").also{ stage++ } + 6002 -> when (buttonId) { + 1 -> { + stage = 1000 + val givingGauntletsId = Items.GOLDSMITH_GAUNTLETS_776 + val npcString = SwapGauntletsHelper.swapGauntlets(player, givingGauntletsId) + if (npcString == "") + { + end() + } + else { + npc(npcString) + } + } + 2 -> end() + } + + 1000 -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(663) + } + +} 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 new file mode 100644 index 000000000..89dd53a2f --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/BootDialogue.kt @@ -0,0 +1,76 @@ +package content.region.misthalin.varrock.quest.familycrest + +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 content.data.Quests + + +@Initializable +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(Quests.FAMILY_CREST) ?: -1 + + if(qstage < 14 || qstage > 14){ + npc(FacialExpression.OLD_NORMAL,"Hello tall person.") + stage = 1 + } + else{ + npc(FacialExpression.OLD_NORMAL,"Hello tall person.") + stage = 2 + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + + 1-> options("Hello short person.", "Why are you called Boot?").also { stage = 10 } + + 2 -> options("Hello. I'm in search of very high quality gold.", "Hello short person.", "Why are you called Boot?").also { stage= 20} + + 10 ->when(buttonId){ + 1-> player("Hello short person.").also { stage = 1000 } + 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++} + + } + + 11 -> player("Yeah, great, I didn't want your life story.").also { stage = 1000} + + 20 -> when (buttonId){ + 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( + 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(Quests.FAMILY_CREST).setStage(player, 15) + } + 1000 -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(665) + } + +} 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 new file mode 100644 index 000000000..618bf40ec --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/CalebDialogue.kt @@ -0,0 +1,242 @@ +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.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + + +@Initializable +class CalebDialogue (player: Player? = null): DialoguePlugin(player) { + + val CREST_PIECE: Item = Item(780) + + 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(Quests.FAMILY_CREST) ?: -1 + + if (qstage == 100) { + options("Can you change my gauntlets for me?", "Nevermind") + stage = 6000 + return true + } + + when(qstage){ + 0-> npc("Who are you? What are you after?").also{stage = 2} + + 10 -> npc("Who are you? What are you after?").also{stage = 1} + + 11 -> npc("How is the fish collecting going?").also{stage = 300} + + 12 -> player("Where did you say I could find Avan again?").also{stage = 400} + + 13 -> player("Where did you say I could find Avan again?").also{stage = 400} + + 14 -> player("Where did you say I could find Avan again?").also{stage = 400} + + 15 -> player("Where did you say I could find Avan again?").also{stage = 400} + 16 -> player("How are you doing getting the crest pieces?").also{stage = 402} + 17 -> player("How are you doing getting the crest pieces?").also{stage = 402} + 18 -> player("How are you doing getting the crest pieces?").also{stage = 402} + 19 -> player("How are you doing getting the crest pieces?").also{stage = 402} + + + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 1 -> options("Are you Caleb Fitzharmon", "Nothing I will be on my way.", "I see you are a chef... could you cook me anything?").also{stage = 3} + 2-> options( "Nothing I will be on my way.", "I see you are a chef... could you cook me anything?").also{stage = 4} + + 3 -> when(buttonId){ + 1 -> npc("Why... yes I am, but I don't believe I know you... " , + "how did you know my name?").also{stage = 5} + 2 -> player("Nothing I will be on my way.").also{stage = 1000} + + 3 -> npc("I would, but I am very busy. I am trying to increase ", + "my renown as one of the world's leading chefs " , + "by preparing a special and unique fish salad.").also{stage = 1000} + } + + 4 -> when(buttonId){ + 1 -> player("Nothing I will be on my way.").also{stage = 1000} + + 2 -> npc("I would, but I am very busy. I am trying to increase ", + "my renown as one of the world's leading chefs " , + "by preparing a special and unique fish salad.").also{stage = 1000} + } + + 5 -> player("I have been sent by your father. ", + "He wishes the Fitzharmon Crest to be restored.").also{stage++} + + 6 -> npc("Ah... well... hmmm... yes... ", + "I do have a piece of it anyway...").also{stage++} + + 7 -> options("Uh... what happened to the rest of it?", "So can I have your bit?").also{stage++} + + 8 -> when(buttonId){ + 1 -> npc("Well... my brothers and I ", + "had a slight disagreement about it...", + " we all wanted to be heir to my fathers' lands, ", + "and we each ended up with a piece of the crest.").also{stage = 100} + 2 -> npc("Well, I am the oldest son, so by the rules of chivalry, ", + "I am most entitled to be the rightful bearer of the crest.").also{stage = 200} + } + + 100 -> npc("None of us wanted to give up our rights to our brothers, ", + "so we didn't want to give up our pieces of the crest, " , + "but none of us wanted to face our father by returning to " , + "him with an incomplete crest... ").also{stage ++} + + 101 -> npc("We each went our separate ways many years past, " , + "none of us seeing our father or willing " , + "to give up our fragments.").also{stage = 7} + + 200 -> player("It's not really much use without " , + "the other fragments is it though?").also{stage++} + + 201 -> npc("Well that is true... " + + "perhaps it is time to put my pride aside... ").also{stage++} + + 202 -> npc( "I'll tell you what: ", + "I'm struggling to complete this fish salad of mine, ").also{stage++} + + 203 -> npc( "so if you will assist me in my search for the ingredients, " , + "then I will let you take my", + "piece as reward for your assistance.").also{stage++} + + 204 -> player("So what ingredients are you missing?").also{stage++} + + 205 -> npc("I require the following cooked fish: " , + "Swordfish, Bass, Tuna, Salmon and Shrimp.").also{stage++} + 206 -> options("Ok, I will get those.", "Why don't you just give me the crest?").also{stage++} + + 207 -> when(buttonId){ + 1 -> npc("You will? It would help me a lot!").also{stage = 1000}.also{ + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 11) + } + + 2 -> npc("It's a valuable family heirloom. " , + "I think the least you can do is prove you're worthy " , + "of it before I hand it over.").also{stage = 206} + + } + + 300 -> if(player.inventory.containItems(315, 329, 361, 365, 373)){ + player("Got them all with me.").also{stage++} + + }else{ + player("I didn't manage to get them all yet...").also{stage = 320} + } + + 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(Quests.FAMILY_CREST).setStage(player, 12) + } + + 302 -> options("Uh... what happened to the rest of it?" , "Thank you very much!").also{stage++} + 303 -> when(buttonId){ + 1 -> npc("Well... my brothers and I had a slight disagreement about it... ", + "we all wanted to be the heir of my father's lands ", + "and we each ended up with a piece of the crest.").also{stage++} + 2 -> npc("You're welcome.").also{stage = 1000} + } + + 304 -> npc("None of us wanted to give up our rights to our brothers," , + "so we didn't want to give up our pieces of the crest, " , + "but none of us wanted to face our father " , + "by returning to him with an incomplete crest.").also{stage ++} + + 305 -> npc("We each went our separate ways many years past,", + "none of us seeing our father or willing to", + "give up our fragments.").also{stage++} + + 306 -> player("So do you know where I could find any of your brothers?").also{stage++} + + 307 -> npc("Well, we haven't really kept in touch... ", + "what with the dispute over the crest and all...", + "I did hear from my brother Avan a while ago though..").also{stage++} + + 308 -> npc("He said he was on some kind of search for treasure,", + "or gold, or something out near Al Kharid somewhere. ", + "Avan always had expensive tastes, so you might try", + "asking the gem trader for his wherebouts.").also{stage ++} + + 309 -> npc("Be warned though. Avan is quite greedy, ", + "and you may find he is not prepared to hand over " , + "his crest piece to you as easily as I have.").also{stage = 1000} + + 320 -> npc("Remember, I want the following cooked fish: ", + "Swordfish, Bass, Tuna, Salmon and Shrimp.").also{stage = 1000} + + 400 -> npc("Last I heard he was on some " , + "stupid treasure hunt out in the desert somewhere. " , + "Your best bet is asking around there.").also{stage++} + 401 -> npc("How are you doing getting the crest pieces?").also{stage++} + + 402 -> player("I am still working on it.").also{stage++} + + 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 + } + else{ + player("I have lost the fragment that you gave me...") + 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++} + 405 -> sendDialogue("Caleb hands over his crest piece to you again.").also{ + stage++ + player.inventory.add(CREST_PIECE) + } + 406 -> npc("I suggest you be less careless in the future. ", + "The crest is extremely valuable, and utterly irreplaceable.").also{stage = 1000} + + 6000 -> when (buttonId) { + 1 -> { + var freeThisTime = getAttribute(player, "family-crest:gauntlets", Items.FAMILY_GAUNTLETS_778) == Items.FAMILY_GAUNTLETS_778 + npc("Yes certainly, though it will cost you 25,000 coins" + if (freeThisTime) " next time." else ".").also { stage = 6001 } + } + 2 -> player("Never mind").also{ stage = 1000 } + } + 6001 -> options("Great thanks!", "No that's okay thanks.").also{ stage++ } + 6002 -> when (buttonId) { + 1 -> { + stage = 1000 + val givingGauntletsId = Items.COOKING_GAUNTLETS_775 + val npcString = SwapGauntletsHelper.swapGauntlets(player, givingGauntletsId) + if (npcString == "") + { + end() + } + else { + npc(npcString) + } + } + 2 -> end() + } + + 1000 -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(666) + } + +} 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 new file mode 100644 index 000000000..1ca3002f0 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonCaveZone.kt @@ -0,0 +1,77 @@ +package content.region.misthalin.varrock.quest.familycrest + + +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.RegionManager +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.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 spawnLoc = Location(3086, 9936, 0) + var chronozon = ChronozonNPC(NPCs.CHRONOZON_667, spawnLoc) + + override fun configure() { + register(ZoneBorders(3079, 9927, 3095, 9944)) + } + + override fun move(e: Entity?, from: Location?, to: Location?): Boolean { + return super.move(e, from, to) + } + + override fun enter(e: Entity?): Boolean { + if (e != null) { + if (e.isPlayer) { + 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 + } + + override fun fireEvent(identifier: String?, vararg args: Any?): Any { + return UInt + } + + +} 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 new file mode 100644 index 000000000..954e5b02e --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/ChronozonNPC.kt @@ -0,0 +1,102 @@ +package content.region.misthalin.varrock.quest.familycrest + +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 +import content.data.Quests + + +class ChronozonNPC(id: Int, location: Location?) : AbstractNPC(NPCs.CHRONOZON_667, Location(3086, 9936, 0)){ + + private lateinit var targetplayer: Player + + private var amountOfFireDamageTaken: Int = 0 + + private var amountOfAirDamageTaken: Int = 0 + + private var amountOfWaterDamageTaken: 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(NPCs.CHRONOZON_667) + } + + override fun checkImpact(state: BattleState?) { + if (state != null) { + if(amountOfAirDamageTaken == 0 || amountOfWaterDamageTaken == 0 || + amountOfEarthDamageTaken == 0 || amountOfFireDamageTaken == 0) { + if(state.style != CombatStyle.MAGIC || state.totalDamage >= skills.lifepoints) { + state.neutralizeHits() + } + } + + if(state.spell != null) { + if(state.spell.spellId == 24) { + if(state.totalDamage > 0 && amountOfAirDamageTaken == 0) { + targetplayer.sendMessage("Chronozon weakens...") + } + amountOfAirDamageTaken += state.totalDamage + } + + if(state.spell.spellId == 27) { + if(state.totalDamage > 0 && amountOfWaterDamageTaken == 0) { + targetplayer.sendMessage("Chronozon weakens...") + } + amountOfWaterDamageTaken += state.totalDamage + } + + if(state.spell.spellId == 33) { + if(state.totalDamage > 0 && amountOfEarthDamageTaken == 0) { + targetplayer.sendMessage("Chronozon weakens...") + } + amountOfEarthDamageTaken += state.totalDamage + } + + if(state.spell.spellId == 38) { + if(state.totalDamage > 0 && amountOfFireDamageTaken == 0) { + targetplayer.sendMessage("Chronozon weakens...") + } + amountOfFireDamageTaken += state.totalDamage + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + 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){ + targetplayer = player + } + + override fun init() { + // Make sure to reset damage taken when spawning in + amountOfFireDamageTaken = 0 + amountOfAirDamageTaken= 0 + amountOfWaterDamageTaken= 0 + amountOfEarthDamageTaken= 0 + super.init() + + } + +} + diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/CrestCombiningInteration.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/CrestCombiningInteration.kt new file mode 100644 index 000000000..8a6e40c49 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/CrestCombiningInteration.kt @@ -0,0 +1,53 @@ +package content.region.misthalin.varrock.quest.familycrest + +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 + +@Initializable +class CrestCombiningInteraction: UseWithHandler(779, 780,781) { + + val CREST_AVAN: Item = Item(779) + val CREST_CALEB: Item = Item(780) + val CREST_JOHNATHON: Item = Item(781) + val CREST_FULL: Item = Item(782) + + + + override fun newInstance(arg: Any?): Plugin { + addHandler(779, ITEM_TYPE, this) + addHandler(780, ITEM_TYPE, this) + addHandler(781, ITEM_TYPE, this) + return this + } + + override fun handle(event: NodeUsageEvent?): Boolean { + event ?: return false + val used = event.used + return when(used.id){ + 779 -> CraftCrest(event.player, event) + 780 -> CraftCrest(event.player, event) + 781 -> CraftCrest(event.player, event) + else -> false + } + } + + private fun CraftCrest(player: Player, event: NodeUsageEvent): Boolean{ + return when(event.usedWith.id){ + 779,780,781 ->{ + if(player.inventory.containItems(779, 780, 781)) { + player.inventory.remove(CREST_AVAN) + player.inventory.remove(CREST_CALEB) + player.inventory.remove(CREST_JOHNATHON) + player.inventory.add(CREST_FULL) + true + } + false + } + else -> false + } + } +} 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 new file mode 100644 index 000000000..fc857c7e6 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/DimintheisDialogue.kt @@ -0,0 +1,189 @@ +package content.region.misthalin.varrock.quest.familycrest + + +import core.api.* +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 +/** +* Handles DimintheisDialogue Dialogue +* @author Plex +*/ +class DimintheisDialogue(player: Player? = null): core.game.dialogue.DialoguePlugin(player) { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return DimintheisDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = (args[0] as NPC).getShownNPC(player) + val questStage = getQuestStage(player, Quests.FAMILY_CREST) + val questComplete = isQuestComplete(player, Quests.FAMILY_CREST) + + if (questStage == 20 && inInventory(player, Items.FAMILY_CREST_782)) { + player("I have retrieved your crest.").also{ stage = 5000 } + return true + } + + val hasGauntlets = hasAnItem(player, 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, ", + "We will never forget you") + stage = 1000 + return true + } + + if (questComplete && !hasGauntlets) { + player("I've lost the gauntlets you gave me") + stage = 6000 + return true + } + + when(questStage) { + 0 -> npc("Hello. My name is Dimintheis, ", + "of the noble family Fitzharmon.").also { stage = 1 } + 10 -> player("Where did you say I could find your son Caleb again?").also { stage = 3000 } + 11 -> player("Where did you say I could find your son Caleb again?").also { stage = 3000 } + 12 -> npc("Have you found my crest yet?").also{ stage = 4000 } + 13 -> npc("Have you found my crest yet?").also{ stage = 4000 } + 14 -> npc("Have you found my crest yet?").also{ stage = 4000 } + 15 -> npc("Have you found my crest yet?").also{ stage = 4000 } + 16 -> npc("Have you found my crest yet?").also{ stage = 4000 } + 17 -> npc("Have you found my crest yet?").also{ stage = 4000 } + 18 -> npc("Have you found my crest yet?").also{ stage = 4000 } + 19 -> npc("Have you found my crest yet?").also{ stage = 4000 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 1 -> options( + "Why would a nobleman live in a dump like this?", + "You're rich then? Can i have some money?", + "Hi, i am a bold adventurer.").also { stage++ } + 2 -> when(buttonId){ + 1 -> npc("The King has taken my estate from me ", + "until such time as I can show my family crest to him.").also { stage = 3 } + 2 -> npc("Gah! Lousy beggar! " , + "Your sort is what's ruining this great land! ", + "Why don't you just go and get a " , + "job if you need money so badly?").also { stage = 1000} + 3 -> npc("An adventurer hmmm? How lucky. I may have an adventure for you. " , + "I desperately need my family crest returning to me. " , + "It is of utmost importance.").also { stage = 4 } + } + 3 -> options("Why would he do that?", + "So where is this crest?").also { stage = 5 } + + 4 -> options("Why are you so desperate for it?", + "So where is this crest?", + "I'm not interested in that adventure right now").also { stage = 6 } + 5 -> when(buttonId){ + 1-> npc("Well, there is a long standing rule of chivalry " , + "amongst the Varrockian aristocracy,").also{ stage = 2000 } + 2-> npc("Well, my three sons took it with them " , + "many years ago when they rode ").also { stage = 2007 } + } + 6 -> when(buttonId){ + 1 -> npc("Well, there is a long standing rule of chivalry " , + "amongst the Varrockian aristocracy,").also{ stage = 2000 } + 2 -> npc("Well, my three sons took it with them " , + "many years ago when they rode ").also { stage = 2007 } + 3 -> npc("I realise it was a lot to ask of a stranger.").also { stage = 1000} + + } + 2000 -> npc( + "where each noble family is in possession of a unique crest, ", + "which signifies the honour and lineage of the family. ").also { stage++} + 2001 -> npc("More than this however, it also represents the ", + "lawful rights of each family to prove their ownership of ", + "their wealth and lands. If the family crest is lost,").also { stage++ } + + 2002 -> npc( "then the family's estate is handed over to the ", + "current monarch until the crest is restored.").also{ stage++ } + 2003 -> npc("This dates back to the times when there was much in-fighting " , + "amongst the noble families and their clans, and ", + "was introduced as a way of reducing the bloodshed that was ", + "devastating the ranks of the ruling classes at that time.").also { stage++ } + 2004 -> npc("When you captured a rival family's clan, " , + "you also captured their lands and wealth.").also{ stage++} + + 2005 -> options("So where is this crest?", "I'm not interested in an adventure right now").also{stage++} + + 2006 -> when(buttonId){ + 1-> npc("Well, my three sons took it ", + "with them many years ago when they rode out ").also { stage++ } + 2 -> npc("I realise it was a lot to ask of a stranger.").also { stage = 1000 } + + } + 2007 -> npc("to fight in the war against the undead necromancer " , + "and his army in the battle to save Varrock.").also { stage++ } + 2008 -> npc("For many years I had assumed them all dead, " , + "as I had heard no word from them.").also { stage++ } + 2009 -> npc("Recently I heard that my son Caleb is alive and well, " , + "trying to earn his fortune as a great fish chef in Catherby.").also { stage++ } + 2010 -> options("Ok, I will help you", "I'm not interested in an adventure right now").also { stage++ } + 2011 -> when(buttonId){ + 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, 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 { + npc("But im sorry, but you cannot help me right now").also{ stage = 1000 } + } + + 3000 ->npc("The only thing I have heard of my son Caleb ", + "is that he is trying to earn his fortune as a great fish chef.").also{ stage++ } + 3001 ->npc("I believe he is staying with a friend ", + "who lives just outside the west gates of Varrock.").also{ stage = 1000 } + + 4000 -> player("I'm still looking for it").also{ stage = 1000 } + + 5000 -> npc("Adventurer... I can only thank you for your kindness, " , + "although the words are insufficient " , + "to express the gratitude I feel!").also{ stage++ } + 5001 -> npc("You are truly a hero in every sense, " , + "and perhaps your efforts can begin to " , + "patch the wounds that have torn this family apart...").also{ stage++ } + 5002 -> npc("I know not how I can adequately reward you for your efforts... " , + "although I do have these mystical gauntlets, " , + "a family heirloom that through some power unknown to me, " , + "have always returned to the head of the family whenever lost,").also{ stage++ } + 5003 -> npc(" or if the owner has died. " , + "I will pledge these to you, " , + "and if you should lose them return to me, " , + "and they will be here.").also{ stage++ } + 5004 -> npc("They can also be granted extra powers. " , + "Take them to one of my sons, " , + "they should be able to imbue them with a skill for you.").also { + stage = 1000 + if (removeItem(player, Items.FAMILY_CREST_782)) { + 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 + } + + override fun getIds(): IntArray { + return intArrayOf(8171) + } +} 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 new file mode 100644 index 000000000..2b8f1aef9 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/FamilyCrest.kt @@ -0,0 +1,127 @@ +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 content.data.Quests + +/** +* Represents the "Family Crest" quest. +* @author Plex +*/ + +@Initializable +class FamilyCrest: Quest(Quests.FAMILY_CREST, 59, 58, 1, 148, 0, 1, 11) { + + 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 !!Dimintheis??", line++) + line(player, "in east Varrock", line++) + line++ + line(player, "To start this quest I require:", line++) + line(player, "!!40 Crafting??", line++, player.skills.getLevel(Skills.CRAFTING) >= 40) + line(player, "!!40 Smithing??", line++, player.skills.getLevel(Skills.SMITHING) >= 40) + line(player, "!!40 Mining??", line++, player.skills.getLevel(Skills.MINING) >= 40) + line(player, "!!59 Magic??", line++, player.skills.getLevel(Skills.MAGIC) >= 59) + } + if(stage >= 10){ + line(player, "I have agreed to restore !!Dimintheis'?? family crest to him.", line++, stage >10) + line(player, "He has asked me to find his son Caleb for him", line++, stage >10) + } + + if(stage >= 11){ + line(player, "I found !!Caleb?? at his house?? in !!Catherby??", line++, stage >11) + line(player, "and told him of my Quest for his father to restore his Family Crest.", line++, stage >11) + line(player, "I gave !!Caleb?? the Swordfish, Bass, Tuna, Salmon and Shrimp he needed for his salad in return for his crest piece", line++, stage >11) + + } + if(stage >=12){ + line(player, "!!Caleb?? has told me to speak to the !!Gem trader??", line++, stage >12) + line(player, " in !!Al-Kharid?? to find his brother.", line++, stage >12) + } + if(stage >=13){ + line(player, "I found !!Avan?? by some gold rocks North of !!Al Kharid??.", line++, stage >13) + } + if(stage >= 14){ + line(player, "!!Avan?? has asked me to find the perfect gold, he has heard of a !!dwarf??", line++, stage >14) + line(player, "who might know where to get some", line++, stage >14) + } + if(stage >=15){ + line(player, "I have spoken to !!Boot?? and he told me that ", line++, stage >15) + line(player, "i can find perfect gold in the !!Witchaven dungeons??", line++, stage >15) + } + if(stage >=16){ + line(player, "!!Avan?? gave me his crest piece in return for a ruby ring ", line++, stage >16) + line(player, "and ruby necklace made of high quality 'perfect gold'.", line++, stage >16) + } + if(stage >= 17){ + line(player, "I found !!Johnathon?? looking very ill at the !!Jolly Boar Inn??.", line++, stage >17) + } + if(stage >= 18){ + line(player, "He soon recovered when I used an antipoison potion on him.", line++, stage >18) + } + if(stage >= 19){ + line(player, "He has told me about the Demon !!Chronozon?? located in the !!Edgeville dungeon??", line++, stage > 19) + } + if(stage >= 20){ + line(player, "I defeated the Demon !!Chronozon?? and obtained !!Johnathon??'s crest piece", line++, stage > 20) + } + if(stage == 100){ + line(player, "I took all three pieces of the crest back to !!Dimintheis?? in !!Varrock??", line++) + line(player, "As a reward !!Dimintheis?? gave me some !!magical gauntlets?? ", line++) + line(player, "that could be enchanted by his sons", line++) + line(player, "to give them bonuses in specific skillss", line++) + 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++) + } + + + } + + override fun hasRequirements(player: Player?): Boolean { + if (player != null) { + if (player.skills.getLevel(Skills.CRAFTING) < 40) + return false + if (player.skills.getLevel(Skills.SMITHING) < 40) + return false + if(player.skills.getLevel(Skills.MINING) < 40) + return false + if( player.skills.getLevel(Skills.MAGIC) < 59) + return false + return true + } + return false + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.COOKING_GAUNTLETS_775,230,277,5) + drawReward(player,"1 Quest Point",ln++) + drawReward(player,"Family Gauntlets",ln++) + drawReward(player,"A choice of special abilities for the gauntlets",ln++) + drawReward(player,"for the gauntlets",ln++) + + if (!addItem(player, Items.FAMILY_GAUNTLETS_778)) { + log(this::class.java, Log.ERR, "Failed to give gauntlets to ${player.username} at end of quest, this should not occur due to crest item removal needed to finish quest.") + } + setAttribute(player, "/save:family-crest:gauntlets", Items.FAMILY_GAUNTLETS_778) + } +} diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonAntiPoisonInteraction.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonAntiPoisonInteraction.kt new file mode 100644 index 000000000..9c12e517a --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonAntiPoisonInteraction.kt @@ -0,0 +1,34 @@ +package content.region.misthalin.varrock.quest.familycrest + + +import core.api.* +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() { + val poisons = intArrayOf(Items.ANTIPOISON4_2446, Items.ANTIPOISON3_175, Items.ANTIPOISON2_177, Items.ANTIPOISON1_179) + + onUseWith(IntType.NPC, poisons, NPCs.JOHNATHON_668){ player, used, with -> + val npc = with.asNpc() + val antip = used.asItem() + 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, Quests.FAMILY_CREST, 18) + openDialogue(player, NPCs.JOHNATHON_668, npc) + } else { + sendMessage(player, "Nothing interesting happens.") + } + + return@onUseWith true + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..c0e6d6f35 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/JohnathonDialogue.kt @@ -0,0 +1,128 @@ +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.game.node.item.Item +import core.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +@Initializable +class JohnathonDialogue(player: Player? = null): DialoguePlugin(player) { + val CREST_PIECE: Item = Item(781) + 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(Quests.FAMILY_CREST) ?: -1 + + if (qstage == 100) { + options("Can you change my gauntlets for me?", "Nevermind") + stage = 6000 + return true + } + + if(qstage < 16){ + npc("I don't feel so well... maybe we can talk later") + stage = 1000 + } + else{ + when(qstage){ + 16 -> player("Greetings. Would you happen to be Johnathon Fitzharmon?").also { stage = 1} + 17 -> npc("What... what did that spider... DO to me? " , + "I... I feel so weak... " , + "I can hardly... think at all...").also { stage = 1000 } + 18 -> sendDialogue("You use the potion on Johnathon").also { stage = 100 } + 19 -> player("I'm trying to kill this demon Chronozon that you mentioned...").also { stage = 200 } + } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + + 1 -> npc("That... I am...").also { stage++ } + 2 -> player("I am here to retrieve your fragment " , + "of the Fitzharmon family crest.").also { stage++ } + 3 -> npc("The... poison... it is all... " , + "too much... My head... " , + "will not... stop spinning...").also { stage++ } + 4 -> sendDialogue("Sweat is pouring down Jonathons' face.").also { stage = 1000 + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 17) + } + + 100 -> npc("Ooooh... thank you... Wow! " , + "I'm feeling a lot better now! " , + "That potion really seems to have done the trick!").also{stage++} + 101 -> npc("How can I reward you?").also { stage++ } + + 102 -> player("I've come here for your piece of the Fitzharmon family crest.").also { stage++} + + 103 -> npc("You have? Unfortunately I don't have it any more... " , + "in my attempts to slay the fiendish Chronozon, the blood demon, " , + "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(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++} + 201 -> when(buttonId){ + 1-> npc("Well... you will have to be a skilled Mage to defeat him, " , + "and my powers are not good enough yet. " , + "You will need to hit him once with each of the four " , + "elemental spells of death before he will be defeated.").also{stage = 1000} + + 2->npc("The fiend has made his lair in Edgeville Dungeon. " , + "When you come in down the ladder in Edgeville, follow" , + "the corridor north until you reach a room with skeletons. " , + "That passageway to the left will lead you to him.").also{stage = 1000} + + 3 -> npc("Those accursed poison spiders that surround " , + "the entrance to Chronozon's lair... " , + "I must have taken a nip from one of them " , + "as I attempted to make my escape.").also{stage = 1000} + + 4 -> npc("My thanks for the assistance adventure").also{stage = 1000} + + } + 6000 -> when (buttonId) { + 1 -> { + var freeThisTime = getAttribute(player, "family-crest:gauntlets", Items.FAMILY_GAUNTLETS_778) == Items.FAMILY_GAUNTLETS_778 + npc("Yes certainly, though it will cost you 25,000 coins" + if (freeThisTime) " next time." else ".").also { stage = 6001 } + } + 2 -> player("Never mind").also{ stage = 1000 } + } + 6001 -> options("Great thanks!", "No that's okay thanks.").also{ stage++ } + 6002 -> when (buttonId) { + 1 -> { + stage = 1000 + val givingGauntletsId = Items.CHAOS_GAUNTLETS_777 + val npcString = SwapGauntletsHelper.swapGauntlets(player, givingGauntletsId) + if (npcString == "") + { + end() + } + else { + npc(npcString) + } + } + 2 -> end() + } + + 1000 -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(668) + } + +} 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 new file mode 100644 index 000000000..cec2f6520 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectGoldSmeltingHandler.kt @@ -0,0 +1,48 @@ +package content.region.misthalin.varrock.quest.familycrest + + +import core.api.addItem +import core.api.animate +import core.api.removeItem +import core.game.interaction.NodeUsageEvent +import core.game.interaction.UseWithHandler +import core.game.node.entity.skill.Skills +import content.global.skill.smithing.FurnaceOptionPlugin +import core.game.system.task.Pulse +import core.plugin.Initializable +import core.plugin.Plugin +import org.rs09.consts.Items +import core.game.world.GameWorld.Pulser + + +@Initializable +class PerfectGoldSmeltingHandler : UseWithHandler(Items.PERFECT_GOLD_ORE_446){ + + + private val furnaceIDs: IntArray = FurnaceOptionPlugin.SmeltUseWithHandler.furnaceIDS + + override fun newInstance(arg: Any?): Plugin { + for(furnace in furnaceIDs){ + addHandler(furnace, OBJECT_TYPE, this) + } + return this + } + + override fun handle(event: NodeUsageEvent?): Boolean { + + event ?: return false + val player = event.player + + Pulser.submit(object : Pulse(2, player) { + override fun pulse(): Boolean { + if(removeItem(player,Items.PERFECT_GOLD_ORE_446)){ + animate(player,3243) + addItem(player,Items.PERFECT_GOLD_BAR_2365) + player.skills.addExperience(Skills.SMITHING,22.5) + } + return true + } + }) + return true + } +} 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 new file mode 100644 index 000000000..ff615e0e1 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryHandler.kt @@ -0,0 +1,63 @@ +package content.region.misthalin.varrock.quest.familycrest + +import core.game.dialogue.DialogueInterpreter +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.plugin.Initializable + + +@Initializable +class PerfectJewelryHandler (player: Player? = null): DialoguePlugin(player){ + + override fun newInstance(player: Player?): DialoguePlugin { + return PerfectJewelryHandler(player) + } + + override fun open(vararg args: Any?): Boolean { + if(player.inventory.containItems(2365, 1603)){ + options("Craft perfect ruby ring", "Craft perfect ruby necklace") + stage = 1 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + + when(stage){ + 1-> when(buttonId){ + + 1-> if(player.inventory.containItems(2365, 1603,1592)) { + player.inventory.remove(Item(2365), Item(1603)) + player.inventory.add(Item(773)) + sendDialogue("You made a perfect gold ring.") + stage = 1000 + } + else{ + sendDialogue("You do not have everything to make this item.") + stage = 1000 + } + + 2-> if(player.inventory.containItems(2365, 1603) && player.inventory.containItems(1597)){ + player.inventory.remove(Item(2365), Item(1603)) + player.inventory.add(Item(774)) + sendDialogue("You made a perfect gold necklace.") + stage = 1000 + } + else{ + sendDialogue("You do not have everything to make this item.") + stage = 1000 + } + } + + 1000 -> end() + } + + return true + } + + override fun getIds(): IntArray { + 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 new file mode 100644 index 000000000..4d258b5db --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryOnUseHandler.kt @@ -0,0 +1,28 @@ +package content.region.misthalin.varrock.quest.familycrest + +import org.rs09.consts.Items + +import core.game.interaction.NodeUsageEvent +import core.game.interaction.UseWithHandler +import core.plugin.Initializable +import core.plugin.Plugin + +@Initializable +class PerfectJewelryOnUseHandler : UseWithHandler(Items.PERFECT_GOLD_BAR_2365){ + val furnaceIDs = listOf(2349, 2351, 2353, 2359, 2361, 2363, 2366, 2368, 9467, 11286, 1540, 11710, 11712, 11714, 11666, 11686, 11688, 11692) + override fun newInstance(arg: Any?): Plugin { + for(furnaces in furnaceIDs){ + addHandler(furnaces, OBJECT_TYPE, this) + } + return this + } + + override fun handle(event: NodeUsageEvent?): Boolean { + event ?: return false + event.player.dialogueInterpreter.open("perfect-jewelry") + return true + } + +} + +//event.getPlayer().getDialogueInterpreter().open("perfect-jewelry"); diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/SwapGauntletsHelper.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/SwapGauntletsHelper.kt new file mode 100644 index 000000000..ebe4a8802 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/SwapGauntletsHelper.kt @@ -0,0 +1,45 @@ +package content.region.misthalin.varrock.quest.familycrest + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.item.Item +import org.rs09.consts.Items + +class SwapGauntletsHelper { + companion object { + private val legalGauntlets = setOf(Items.COOKING_GAUNTLETS_775, Items.GOLDSMITH_GAUNTLETS_776, Items.CHAOS_GAUNTLETS_777, Items.FAMILY_GAUNTLETS_778) + + @JvmStatic + fun swapGauntlets(player: Player, givingGauntletsId: Int): String { + if (givingGauntletsId !in legalGauntlets) { + throw IllegalArgumentException("givingGauntletsId not in list of legal gauntlets.") + } + if (inInventory(player, givingGauntletsId)) { + val gauntletString = Item(givingGauntletsId).name + return "You already have the $gauntletString." + } + var otherGauntlets = -1 + val otherPossibleGauntlets = legalGauntlets.toMutableSet() + otherPossibleGauntlets.remove(givingGauntletsId) + for (gauntletId in otherPossibleGauntlets) { + if (inInventory(player, gauntletId)) + { + otherGauntlets = gauntletId + } + } + if (otherGauntlets == -1) { + return "You do not have the gauntlets with you in your inventory." + } + val fee = 25000 + val shouldBeFree = getAttribute(player, "family-crest:gauntlets", Items.FAMILY_GAUNTLETS_778) == Items.FAMILY_GAUNTLETS_778 + if (!shouldBeFree && !inInventory(player, Items.COINS_995, fee)) { + return "You do not have enough coins." + } + if ((shouldBeFree || removeItem(player, Item(Items.COINS_995, fee))) && removeItem(player, otherGauntlets)) { + addItem(player, givingGauntletsId) + setAttribute(player, "/save:family-crest:gauntlets", givingGauntletsId) + } + return "" + } + } +} 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 new file mode 100644 index 000000000..af534f4d8 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/WitchavenLeverInteraction.kt @@ -0,0 +1,102 @@ +package content.region.misthalin.varrock.quest.familycrest + +import core.game.node.entity.impl.ForceMovement +import core.game.node.entity.player.Player +import core.game.node.scenery.Scenery +import core.game.world.map.Direction +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.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 } + var direction = Direction.NORTH + if(scenery.rotation % 2 == 0) { + direction = if(player.location.x <= scenery.location.x + d) { Direction.EAST } else { Direction.WEST } + } else { + direction = if(player.location.y <= scenery.location.y + d) { Direction.NORTH } else { Direction.SOUTH } + } + ForceMovement.run(player, + player.location, + player.location.transform(direction.stepX, direction.stepY, 0), + ForceMovement.WALK_ANIMATION, + ForceMovement.WALK_ANIMATION, + direction, + 8) + +} + +class WitchavenLeverInteraction : InteractionListener { + val DOWN_ANIMATION = Animation(2140) + val UP_ANIMATION = Animation(2139) + + // TODO: It seems like the even numbers are used for the "up" positions, but there's no parent object with varps for them. + // Currently, we just send a `ConstructScenery` packet to the specific player. Is there a cleaner way to do this? + val NORTH_LEVER_A = 2421 + val NORTH_LEVER_B = 2425 + val SOUTH_LEVER = 2423 + val LEVERS = intArrayOf(NORTH_LEVER_A, NORTH_LEVER_A+1, NORTH_LEVER_B, NORTH_LEVER_B+1, SOUTH_LEVER, SOUTH_LEVER+1) + + val NORTH_DOOR = 2431 // 92 in RSC + val HELLHOUND_DOOR = 2430 // 91 in RSC + val SOUTHWEST_DOOR = 2427 // 90 in RSC + val SOUTHEAST_DOOR = 2429 // 88 in RSC + val DOORS = intArrayOf(NORTH_DOOR, HELLHOUND_DOOR, SOUTHWEST_DOOR, SOUTHEAST_DOOR) + + 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(Quests.FAMILY_CREST).getStage(player) == 0) { + player.sendMessage("Nothing interesting happens.") + } + val old = player.getAttribute("family-crest:witchaven-lever:${baseId}", false) + player.setAttribute("family-crest:witchaven-lever:${baseId}", !old) + val direction = if(old) { "down" } else { "up" } + player.sendMessage("You pull the lever ${direction}.") + player.sendMessage("You hear a clunk.") + player.animate(if(old) { DOWN_ANIMATION } else { UP_ANIMATION }) + val downLever = (node as Scenery).transform(baseId) + val upLever = (node as Scenery).transform(baseId + 1) + player.debug("lever: ${downLever.id} ${upLever.id}") + val buffer = UpdateAreaPosition.getChunkUpdateBuffer(player, RegionManager.getRegionChunk(player.location).currentBase) + if(old) { + // TODO: This doesn't seem to properly make the lever return to the "down" position visually. + ClearScenery.write(buffer, upLever) + ConstructScenery.write(buffer, downLever) + //PacketRepository.send(ClearScenery::class.java, BuildSceneryContext(player, downLever)) + } else { + ClearScenery.write(buffer, downLever) + ConstructScenery.write(buffer, upLever) + //PacketRepository.send(ConstructScenery::class.java, BuildSceneryContext(player, upLever)) + } + player.session.write(buffer) + return@on true + } + on(DOORS, IntType.SCENERY, "open") { player, node -> + 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(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) + HELLHOUND_DOOR -> questComplete || (northA && northB && !south) + SOUTHWEST_DOOR -> (northA && !south) || (northA && northB && !south) + SOUTHEAST_DOOR -> (northA && south) || (northA && northB && south) + else -> false + } + if(canPass) { + player.sendMessage("The door swings open. You go through the door.") + doDoor(player, node as Scenery) + } else { + player.sendMessage("The door is locked.") + } + return@on true + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/quest/gertrudes_cat/FluffNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/FluffNPC.java similarity index 85% rename from Server/src/main/java/core/game/node/entity/npc/quest/gertrudes_cat/FluffNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/gertrude/FluffNPC.java index a5309ab90..fd90e43a0 100644 --- a/Server/src/main/java/core/game/node/entity/npc/quest/gertrudes_cat/FluffNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/FluffNPC.java @@ -1,9 +1,10 @@ -package core.game.node.entity.npc.quest.gertrudes_cat; +package content.region.misthalin.varrock.quest.gertrude; 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/java/core/game/content/quest/members/GertrudesCat.java b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/GertrudesCat.java similarity index 91% rename from Server/src/main/java/core/game/content/quest/members/GertrudesCat.java rename to Server/src/main/content/region/misthalin/varrock/quest/gertrude/GertrudesCat.java index fbc2cfa11..c4eb02518 100644 --- a/Server/src/main/java/core/game/content/quest/members/GertrudesCat.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/GertrudesCat.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members; +package content.region.misthalin.varrock.quest.gertrude; import core.plugin.Initializable; import core.game.node.entity.skill.Skills; @@ -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/java/core/game/node/entity/npc/other/LumberKittenNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/LumberKittenNPC.java similarity index 90% rename from Server/src/main/java/core/game/node/entity/npc/other/LumberKittenNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/gertrude/LumberKittenNPC.java index 5d8d9d99e..51ab6f1b5 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/LumberKittenNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/gertrude/LumberKittenNPC.java @@ -1,12 +1,13 @@ -package core.game.node.entity.npc.other; +package content.region.misthalin.varrock.quest.gertrude; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; -import rs09.game.world.GameWorld; +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/java/core/game/content/cutscene/JulietCutscenePlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietCutscenePlugin.java similarity index 93% rename from Server/src/main/java/core/game/content/cutscene/JulietCutscenePlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietCutscenePlugin.java index b1adc0f89..da943faf8 100644 --- a/Server/src/main/java/core/game/content/cutscene/JulietCutscenePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietCutscenePlugin.java @@ -1,7 +1,7 @@ -package core.game.content.cutscene; +package content.region.misthalin.varrock.quest.romeo; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; @@ -14,7 +14,7 @@ import core.net.packet.context.CameraContext; import core.net.packet.context.CameraContext.CameraType; import core.net.packet.out.CameraViewPacket; import core.plugin.Initializable; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the romeo and juliet cutscene plugin. @@ -35,7 +35,7 @@ public final class JulietCutscenePlugin extends CutscenePlugin { */ public JulietCutscenePlugin() { this(null); - PluginManager.definePlugin(new JulietDialogue()); + ClassScanner.definePlugin(new JulietDialogue()); } /** diff --git a/Server/src/main/java/core/game/content/cutscene/JulietDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/cutscene/JulietDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietDialogue.java index e78cd5112..7d6b190e4 100644 --- a/Server/src/main/java/core/game/content/cutscene/JulietDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietDialogue.java @@ -1,8 +1,8 @@ -package core.game.content.cutscene; +package content.region.misthalin.varrock.quest.romeo; -import core.game.content.activity.ActivityManager; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.activity.ActivityManager; +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; @@ -10,11 +10,12 @@ 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; -import rs09.game.world.repository.Repository; +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/java/core/game/node/entity/npc/quest/romeo__juliet/JulietNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietNPC.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/npc/quest/romeo__juliet/JulietNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietNPC.java index 55d9d1867..d87164ef7 100644 --- a/Server/src/main/java/core/game/node/entity/npc/quest/romeo__juliet/JulietNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/JulietNPC.java @@ -1,9 +1,10 @@ -package core.game.node.entity.npc.quest.romeo__juliet; +package content.region.misthalin.varrock.quest.romeo; 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/java/core/game/content/cutscene/RJCutscenePlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RJCutscenePlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/cutscene/RJCutscenePlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/romeo/RJCutscenePlugin.java index ed94f6b8a..17a33f20b 100644 --- a/Server/src/main/java/core/game/content/cutscene/RJCutscenePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RJCutscenePlugin.java @@ -1,16 +1,16 @@ -package core.game.content.cutscene; +package content.region.misthalin.varrock.quest.romeo; -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.activity.ActivityManager; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +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.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; @@ -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/java/core/game/content/quest/free/RomeoJuliet.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoJuliet.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/free/RomeoJuliet.java rename to Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoJuliet.java index 55d13d11c..2e5945316 100644 --- a/Server/src/main/java/core/game/content/quest/free/RomeoJuliet.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoJuliet.java @@ -1,8 +1,9 @@ -package core.game.content.quest.free; +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/java/core/game/node/entity/npc/quest/romeo__juliet/RomeoNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoNPC.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/npc/quest/romeo__juliet/RomeoNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoNPC.java index bcf29ec6d..0034cddca 100644 --- a/Server/src/main/java/core/game/node/entity/npc/quest/romeo__juliet/RomeoNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.quest.romeo__juliet; +package content.region.misthalin.varrock.quest.romeo; import core.game.node.entity.npc.AbstractNPC; import core.game.world.map.Location; @@ -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/java/core/game/interaction/object/RomeoQuestPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoQuestPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/object/RomeoQuestPlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoQuestPlugin.java index 8740ef9a3..a95cc82e8 100644 --- a/Server/src/main/java/core/game/interaction/object/RomeoQuestPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/romeo/RomeoQuestPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.misthalin.varrock.quest.romeo; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/CertificatePlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CertificatePlugin.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/free/shieldofarrav/CertificatePlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CertificatePlugin.java index 8579c012c..98c11bfda 100644 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/CertificatePlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CertificatePlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free.shieldofarrav; +package content.region.misthalin.varrock.quest.shieldofarrav; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; 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 new file mode 100644 index 000000000..3ba076b35 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CharlieTheTrampDialogue.kt @@ -0,0 +1,114 @@ +package content.region.misthalin.varrock.quest.shieldofarrav + +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 + +/** + * @author qmqz + */ + +@Initializable +class CharlieTheTrampDialogue(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,"Spare some change guv?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> options("Who are you?", "Sorry, I haven't got any.", "Go get a job!", "Ok. Here you go.", "Is there anything down this alleyway?").also { stage++ } + + 1 -> when (buttonId) { + 1 -> player(core.game.dialogue.FacialExpression.HALF_THINKING, "Who are you?").also { stage = 50 } + 2 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Sorry, I haven't got any.").also { stage = 100 } + 3 -> player(core.game.dialogue.FacialExpression.ANNOYED, "Go get a job!").also { stage = 150 } + 4 -> player(core.game.dialogue.FacialExpression.ANNOYED, "Ok. Here you go.").also { stage = 200 } + 5 -> player(core.game.dialogue.FacialExpression.ANNOYED, "Is there anything down this alleyway?").also { stage = 250 } + } + + 50 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Charles. Charles E. Trampin' at your service. Now, about that change you were going to give me...").also { stage = 0 } + 100 -> npc(core.game.dialogue.FacialExpression.SAD, "Thanks anyways!").also { stage = 99 } + 150 -> npc(core.game.dialogue.FacialExpression.ANNOYED, "You startin? I hope your nose falls off!").also { stage = 99 } + 200 -> { + if (player.inventory.contains(Items.COINS_995, 1)) { + player.inventory.remove(Item(Items.COINS_995, 1)).also { end() } + } else { + sendDialogue("You need one coin to give away.").also { stage = 99 } + } + } + 201 -> npc(core.game.dialogue.FacialExpression.HAPPY, "Hey, thanks a lot!").also { stage++ } + 202 -> options("No problem.", "Don't I get some sort of quest hint or something now?").also { stage++ } + 203 -> when(buttonId) { + 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No problem.").also { stage = 99 } + 2 -> player(core.game.dialogue.FacialExpression.HALF_THINKING, "So... don't I get some sort of quest hint or something now?").also { stage = 220 } + } + 220 -> npcl(core.game.dialogue.FacialExpression.ANNOYED, "Huh? What do you mean? That wasn't why I asked you for money.").also { stage++ } + 221 -> npcl(core.game.dialogue.FacialExpression.SAD, "I just need to eat...").also { stage = 99 } + 250 -> npcl(core.game.dialogue.FacialExpression.AFRAID,"The ruthless and notorious criminal gang known as the Black Arm Gang have their headquarters down there.").also { stage++ } + 251 -> options("Thank you for the warning!", "Do you think they would let me join?").also { stage++ } + 252 -> when (buttonId) { + 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"Thanks for the warning!").also { stage = 270 } + 2 -> player(core.game.dialogue.FacialExpression.ASKING, "Do you think they would let me join?").also { stage = 280 } + } + 270 -> npc(core.game.dialogue.FacialExpression.HAPPY, "Don't worry about it.").also { stage = 99 } + + 280 -> if (!ShieldofArrav.isBlackArm(player) && !ShieldofArrav.isPhoenix(player)) { + npcl(core.game.dialogue.FacialExpression.SUSPICIOUS, "You never know. You'll find a lady down there called Katrine. Speak to her.").also { stage = 282 } + } else if (ShieldofArrav.isBlackArm(player)) { + npcl(core.game.dialogue.FacialExpression.SUSPICIOUS, "I was under the impression you were already a member...").also { stage = 99 } + } else { + npcl(core.game.dialogue.FacialExpression.ANNOYED, "No. You're a collaborator with the Phoenix Gang. There's no way they'll let you join now.").also { stage++ } + } + + 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(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(Quests.SHIELD_OF_ARRAV).setStage(player, 50) + } + end() + } + + 291 -> options("How did you know I was in the Phoenix Gang?", "Any ideas how I could get in there then?").also { stage++ } + 292 -> when(buttonId) { + 1 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "How did you know I was in the Phoenix Gang?").also { stage = 300 } + 2 -> stage = 290 + } + + 300 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "In my current profession I spend a lot of time on the streets and you hear these sorta things sometimes.").also { stage++ } + 301 -> player(core.game.dialogue.FacialExpression.ASKING, "Any ideas how I could get in there then?").also { stage++ } + 302 -> npc(core.game.dialogue.FacialExpression.THINKING, "Hmmm. I dunno.").also { stage++ } + 303 -> npcl(core.game.dialogue.FacialExpression.THINKING, "Your best bet would probably be to find someone else... Someone who ISN'T a member of the Phoenix Gang, and get them to infiltrate the ranks of the Black Arm Gang for you.").also { stage++ } + 304 -> npc(core.game.dialogue.FacialExpression.THINKING, "If you find someone like that, tell 'em to come to me first.").also { stage++ } + + 305 -> options("Ok. Good plan!", "Like who?").also { stage++ } + 306 -> when (buttonId) { + 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Ok. Good plan!").also { stage = 310 } + 2 -> player(core.game.dialogue.FacialExpression.ASKING, "Like who?").also { stage = 320 } + } + + 310 -> npc(core.game.dialogue.FacialExpression.LAUGH, "I'm not just a pretty face!").also { stage = 99 } + 320 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "There's plenty of other adventurers about besides yourself. I'm sure if you asked one of them nicely they would help you.").also { stage = 99 } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return CharlieTheTrampDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CHARLIE_THE_TRAMP_641) + } +} 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/CuratorHaigHalenSOADialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenSOADialogue.java new file mode 100644 index 000000000..a13fb96f7 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenSOADialogue.java @@ -0,0 +1,93 @@ +package content.region.misthalin.varrock.quest.shieldofarrav; + +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.game.dialogue.DialogueFile; + +public final class CuratorHaigHalenSOADialogue extends DialogueFile { + @Override + public void handle(int componentID, int buttonID) { + Player player = getPlayer(); + if (player.getInventory().containsItem(ShieldofArrav.PHOENIX_SHIELD) || player.getInventory().containsItem(ShieldofArrav.BLACKARM_SHIELD)) { + switch (getStage()) { + case 0: + player("I have half the shield of Arrav here. Can I get a", "reward?"); + setStage(1); + break; + case 1: + npc("The Shield of Arrav! Goodness, the Museum has been", "searching for that for years! The late King Roald II", "offered a reward for it years ago!"); + setStage(2); + break; + case 2: + player("Well, I'm here to claim it."); + setStage(3); + break; + case 3: + npc("Let me have a look at it first."); + setStage(4); + break; + case 4: + getInterpreter().sendItemMessage(ShieldofArrav.getShield(player), "The curator peers at the shield."); + setStage(5); + break; + case 5: + npc("This is incredible!"); + setStage(6); + break; + case 6: + npc("That shield has been missing for over twenty-five years!"); + setStage(7); + break; + case 7: + npc("Leave the shield here with me and I'll write you out a", "certificate saying that you have returned the shield, so", "that you can claim your reward from the King."); + setStage(8); + break; + case 8: + player("Can I have two certificates please?"); + setStage(9); + break; + case 9: + npc("Yes, certainly. Please hand over the shield."); + setStage(10); + break; + case 10: + getInterpreter().sendItemMessage(ShieldofArrav.getShield(player), "You hand over the shield half."); + setStage(11); + break; + case 11: + final Item shield = ShieldofArrav.getShield(player); + final Item certificate = shield == ShieldofArrav.BLACKARM_SHIELD ? ShieldofArrav.BLACKARM_CERTIFICATE : ShieldofArrav.PHOENIX_CERTIFICATE; + if (player.getInventory().remove(shield)) { + player.getInventory().add(certificate); + getInterpreter().sendItemMessage(certificate, "The curator writes out two half-certificates."); + setStage(12); + } + break; + } + return; + } + switch (getStage()) { + case 0: + case 12: + npc("Of course you won't actually be able to claim the", "reward with only half the reward certificate..."); + setStage(13); + break; + case 13: + player("What? I went through a lot of trouble to get that shield", "piece and now you tell me it was for nothing? That's", "not very fair!"); + setStage(14); + break; + case 14: + npc("Well, if you were to get me the other half of the shield,", "I could give you the other half of the reward certificate.", "It's rumoured to be in the possession of the infamous", "Blackarm Gang, beyond that I can't help you."); + setStage(15); + break; + case 15: + player("Okay, I'll see what I can do."); + setStage(16); + break; + case 16: + end(); + break; + } + } +} + 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 new file mode 100644 index 000000000..f5be4a4b0 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/JohnnyBeardNPC.java @@ -0,0 +1,61 @@ +package content.region.misthalin.varrock.quest.shieldofarrav; + +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.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. + * @author 'Vexia + * @version 1.0 + */ +public final class JohnnyBeardNPC extends AbstractNPC { + + /** + * The NPC ids of NPCs using this plugin. + */ + private static final int[] ID = { 645 }; + + /** + * Constructs a new {@code JohnnyBeardNPC} {@code Object}. + */ + public JohnnyBeardNPC() { + super(0, null); + } + + /** + * Constructs a new {@code JohnnyBeardNPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + private JohnnyBeardNPC(int id, Location location) { + super(id, location); + } + + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new JohnnyBeardNPC(id, location); + } + + @Override + public void finalizeDeath(final Entity killer) { + super.finalizeDeath(killer); + 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); + } + } + } + + @Override + public int[] getIds() { + return ID; + } + +} diff --git a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/JonnytheBeardPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/JonnytheBeardPlugin.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/free/shieldofarrav/JonnytheBeardPlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/JonnytheBeardPlugin.java index 4a8ff5ec0..41f238218 100644 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/JonnytheBeardPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/JonnytheBeardPlugin.java @@ -1,7 +1,7 @@ -package core.game.content.quest.free.shieldofarrav; +package content.region.misthalin.varrock.quest.shieldofarrav; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/KatrineDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KatrineDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/free/shieldofarrav/KatrineDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KatrineDialogue.java index 250b17f1d..53f90edcd 100644 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/KatrineDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KatrineDialogue.java @@ -1,10 +1,14 @@ -package core.game.content.quest.free.shieldofarrav; +package content.region.misthalin.varrock.quest.shieldofarrav; -import core.game.content.dialogue.DialoguePlugin; +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/kotlin/rs09/game/content/quest/free/shieldofarrav/KingRoaldArravDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KingRoaldArravDialogue.kt similarity index 91% rename from Server/src/main/kotlin/rs09/game/content/quest/free/shieldofarrav/KingRoaldArravDialogue.kt rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KingRoaldArravDialogue.kt index ce7f43e4c..26238d76d 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/shieldofarrav/KingRoaldArravDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/KingRoaldArravDialogue.kt @@ -1,11 +1,11 @@ -package rs09.game.content.quest.free.shieldofarrav +package content.region.misthalin.varrock.quest.shieldofarrav -import core.game.content.quest.free.shieldofarrav.ShieldofArrav import core.game.node.item.GroundItemManager import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE +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/java/core/game/content/quest/free/shieldofarrav/ReldoDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ReldoDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/free/shieldofarrav/ReldoDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ReldoDialogue.java index 4d92b87d0..91bee2817 100644 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/ReldoDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ReldoDialogue.java @@ -1,14 +1,14 @@ -package core.game.content.quest.free.shieldofarrav; +package content.region.misthalin.varrock.quest.shieldofarrav; import org.rs09.consts.Items; -import core.game.content.dialogue.DialoguePlugin; +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.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 core.game.node.item.Item; +import content.data.Quests; /** * Represents the dialogue to handle reldo. @@ -28,11 +28,6 @@ public class ReldoDialogue extends DialoguePlugin { */ private Quest shieldArrav; - /** - * The achievement diary. - */ - private AchievementDiary diary; - /** * If w'ere chatting about our diary. */ @@ -61,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."); @@ -91,9 +86,6 @@ public class ReldoDialogue extends DialoguePlugin { stage++; break; case 0: - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.VARROCK); - } switch (buttonId) { case 1: player("What is the Achievement Diary?"); @@ -126,10 +118,7 @@ public class ReldoDialogue extends DialoguePlugin { stage = 444; break; case 444: - diary.setLevelRewarded(level); - for (Item i : diary.getType().getRewards(level)) { - player.getInventory().add(i, player); - } + AchievementDiary.flagRewarded(player, DiaryType.VARROCK, level); sendDialogue("Reldo takes the Varrock armour and attaches some more plate metal", "to it, filling it out to look like a proper suit of armour. He etches", "some words into the armour, which glows slightly before fading."); stage++; break; @@ -167,7 +156,7 @@ public class ReldoDialogue extends DialoguePlugin { break; case 460: - player.getInventory().add(diary.getType().getRewards(level)[0], player); + AchievementDiary.grantReplacement(player, DiaryType.VARROCK, level); npc("You better be more careful this time."); stage = 999; break; @@ -249,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; @@ -588,15 +579,12 @@ public class ReldoDialogue extends DialoguePlugin { */ private void sendDiaryDialogue() { isDiary = true; - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.VARROCK); - } - if (diary.isComplete(level) && !diary.isLevelRewarded(level)) { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.VARROCK, level)) { player("I've finished all the medium tasks in my Varrock", "Achievement Diary."); stage = 440; return; } - if (diary.isLevelRewarded(level) && diary.isComplete(level) && !player.hasItem(diary.getType().getRewards(level)[0])) { + if (AchievementDiary.canReplaceReward(player, DiaryType.VARROCK, level)) { player("I've seemed to have lost my armour..."); stage = 460; return; diff --git a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldArravPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java similarity index 84% rename from Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldArravPlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java index 388ebc9cb..80a581a22 100644 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldArravPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldArravPlugin.java @@ -1,10 +1,10 @@ -package core.game.content.quest.free.shieldofarrav; +package content.region.misthalin.varrock.quest.shieldofarrav; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +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; @@ -19,10 +19,11 @@ import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; -import rs09.game.content.global.action.PickupHandler; -import rs09.game.world.repository.Repository; +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,20 +60,20 @@ 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: player.getInterfaceManager().open(new Component(526)); - player.getPacketDispatch().sendString("You can use this certificate in order to claim your reward.", 526, 2); + player.getPacketDispatch().sendString("The bearer of this certificate has brought both halves of the legendary Shield of Arrav to me, Halg Halen, Curator of the Varrock Museum. I have examined the shield and am satisfied as to its authenticity. I recommend to His Majesty, King Roald III, that the bearer is rewarded as per Proclamation 262 of the year 143 of the 5th Age, by King Roald II.", 526, 2); break; case 11173: player.getInterfaceManager().open(new Component(525)); - player.getPacketDispatch().sendString("Attach this certificate to another.", 525, 2); + player.getPacketDispatch().sendString("The bearer of this certificate has brought both halves of the legendary Shield of Arrav to me, Halg Halen, Curator of the Varrock Museum. I have examined the shield and am satisfied as to its authenticity. I recommend to His Majesty, King Roald III, that the bearer is rewarded as per Proclamation 262 of the year 143 of the 5th Age, by King Roald II.", 525, 2); break; case 11174: player.getInterfaceManager().open(new Component(529)); - player.getPacketDispatch().sendString("Attach this certificate to another.", 529, 2); + player.getPacketDispatch().sendString("The bearer of this certificate has brought both halves of the legendary Shield of Arrav to me, Halg Halen, Curator of the Varrock Museum. I have examined the shield and am satisfied as to its authenticity. I recommend to His Majesty, King Roald III, that the bearer is rewarded as per Proclamation 262 of the year 143 of the 5th Age, by King Roald II.", 529, 2); break; case 2400: SceneryBuilder.replace(((Scenery) node), ((Scenery) node).transform(2401)); diff --git a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldofArrav.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArrav.java similarity index 91% rename from Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldofArrav.java rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArrav.java index 957d98c71..4b427035d 100644 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldofArrav.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArrav.java @@ -1,11 +1,12 @@ -package core.game.content.quest.free.shieldofarrav; +package content.region.misthalin.varrock.quest.shieldofarrav; 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.Initializable; -import rs09.game.content.dialogue.KingRoaldDialogue; -import rs09.plugin.PluginManager; +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) { - PluginManager.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.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/java/core/game/content/quest/free/shieldofarrav/StravenDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/StravenDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/free/shieldofarrav/StravenDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/StravenDialogue.java index 1c583672a..f12d9cef4 100644 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/StravenDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/StravenDialogue.java @@ -1,10 +1,14 @@ -package core.game.content.quest.free.shieldofarrav; +package content.region.misthalin.varrock.quest.shieldofarrav; -import core.game.content.dialogue.DialoguePlugin; +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.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/java/core/game/content/quest/free/shieldofarrav/WeaponsMasterDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/WeaponsMasterDialogue.java similarity index 92% rename from Server/src/main/java/core/game/content/quest/free/shieldofarrav/WeaponsMasterDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/WeaponsMasterDialogue.java index 24d0b11fd..e0f0f5fc6 100644 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/WeaponsMasterDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/WeaponsMasterDialogue.java @@ -1,9 +1,10 @@ -package core.game.content.quest.free.shieldofarrav; +package content.region.misthalin.varrock.quest.shieldofarrav; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/quest/members/whatliesbelow/AnnaJonesDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/AnnaJonesDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/whatliesbelow/AnnaJonesDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/AnnaJonesDialogue.java index e0537c678..645995cdb 100644 --- a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/AnnaJonesDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/AnnaJonesDialogue.java @@ -1,6 +1,7 @@ -package core.game.content.quest.members.whatliesbelow; +package content.region.misthalin.varrock.quest.whatliesbelow; -import core.game.content.dialogue.DialoguePlugin; +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; @@ -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/java/core/game/content/quest/members/whatliesbelow/KingRoaldNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/KingRoaldNPC.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/whatliesbelow/KingRoaldNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/KingRoaldNPC.java index 41b20c1ee..437ec836b 100644 --- a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/KingRoaldNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/KingRoaldNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.whatliesbelow; +package content.region.misthalin.varrock.quest.whatliesbelow; import core.game.node.entity.combat.BattleState; import core.game.node.entity.npc.AbstractNPC; diff --git a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/OutlawNPC.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/OutlawNPC.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/members/whatliesbelow/OutlawNPC.java rename to Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/OutlawNPC.java index 8fda7be09..9649f5fa3 100644 --- a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/OutlawNPC.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/OutlawNPC.java @@ -1,5 +1,6 @@ -package core.game.content.quest.members.whatliesbelow; +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/java/core/game/content/quest/members/whatliesbelow/RatBurgissDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/RatBurgissDialogue.java similarity index 80% rename from Server/src/main/java/core/game/content/quest/members/whatliesbelow/RatBurgissDialogue.java rename to Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/RatBurgissDialogue.java index e5e3d5ad5..0a8e97bc6 100644 --- a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/RatBurgissDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/RatBurgissDialogue.java @@ -1,12 +1,13 @@ -package core.game.content.quest.members.whatliesbelow; +package content.region.misthalin.varrock.quest.whatliesbelow; -import core.game.content.dialogue.DialoguePlugin; +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.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.Item; +import content.region.misthalin.varrock.diary.RatBurgissDiaryDialogue; /** * The rat burgiss dialogue. @@ -14,12 +15,6 @@ import core.game.node.item.Item; * @author Vexia */ public class RatBurgissDialogue extends DialoguePlugin { - - /** - * The achievement diary. - */ - private AchievementDiary diary; - /** * The quest. */ @@ -58,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; @@ -66,126 +61,6 @@ public class RatBurgissDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - if (isDiary) { - // https://www.youtube.com/watch?v=j-wvq79hUgU&t=309s - switch (stage) { - case 999: - end(); - break; - case -1: - // when available, "Can I change my Varrock teleport point?" is option 2 - options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); - stage++; - break; - case 0: - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.VARROCK); - } - 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: - player("See you later."); - stage = 999; - break; - } - break; - case 440: - npc("You have? Excellent! Well done."); - stage++; - break; - case 441: - player("Thank you. Uh... can I have the reward?"); - stage++; - break; - case 442: - npc("Reward? Ah yes! Of course. Your reward, it's right", "here."); - stage = 444; - break; - case 444: - diary.setLevelRewarded(level); - for (Item i : diary.getType().getRewards(level)) { - player.getInventory().add(i, player); - } - npc("Now, this body armour is magically enhanced to help", "you with your Smithing and Mining. There is a", "furnace, not far from here, in Edgeville. Use this", "armour there and, when smelting ores up to and"); - stage++; - break; - case 445: - npc("including steel, you will have a chance of making an", "extra bar every time. Also, when you mine with this", "armour on, you will have a chance of Mining extra", "ores from rocks up to and including coal."); - stage++; - break; - case 446: - npc("Bear in mind that you will need to be wearing the", "armour for either of these to work. I will speak to the", "shopkeepers around Varrock who sell armour and", "weapons to get you better prices when you are wearing"); - stage++; - break; - case 447: - npc("the Varrock armour."); - stage = 449; - break; - case 449: - npc("I can also change your Varrock Teleport spell so that it", "takes you to the Grand Exchange, if you'd find that more convenient."); - stage++; - break; - case 450: - npc("As an extra reward, you can also have this old magical", "lamp to help you with your skills. I was going to use it", "myself, but I don't really need it."); - stage++; - break; - case 451: - player("Wow, thanks!"); - stage++; - break; - case 452: - npc("If you should lose this armour, come back and see me", "for another set."); - stage = -1; - break; - - case 460: - player.getInventory().add(diary.getType().getRewards(level)[0], player); - npc("You better be more careful this time."); - stage = 999; - break; - - case 410: - npc("It's a diary that helps you keep track of particular", "achievements. Here in Varrock it can help you", "discover some quite useful things. Eventually, with", "enough exploration, the people of Varrock 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 = 999; - break; - case 420: - npc("Well, there's three different levels of Varrock Armour,", "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 Varrock are happy to see", "you visiting the land."); - stage = 999; - break; - case 430: - npc("Just complete the tasks so they're all ticked off, then", "you can claim your 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 Varrock Armour, speak to Vannaka", "Reldo, and myself."); - stage = 999; - break; - } - return true; - } if (stage == -1) { switch (buttonId) { case 1: @@ -194,7 +69,7 @@ public class RatBurgissDialogue extends DialoguePlugin { break; case 2: player("I have a question about my Achievement Diary."); - stage = 900; + loadFile(new RatBurgissDiaryDialogue()); break; } return true; @@ -571,15 +446,12 @@ public class RatBurgissDialogue extends DialoguePlugin { */ private void sendDiaryDialogue() { isDiary = true; - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.VARROCK); - } - if (diary.isComplete(level) && !diary.isLevelRewarded(level)) { + if (AchievementDiary.canClaimLevelRewards(player, DiaryType.VARROCK, level)) { player("I think I've finished all of the tasks in my Varrock", "Achievement Diary."); stage = 440; return; } - if (diary.isLevelRewarded(0) && diary.isComplete(level) && !player.hasItem(diary.getType().getRewards(level)[0])) { + if (AchievementDiary.canReplaceReward(player, DiaryType.VARROCK, level)) { player("I've seemed to have lost my armour..."); stage = 460; return; 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 new file mode 100644 index 000000000..838900d75 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/SurokMagisDialogue.java @@ -0,0 +1,531 @@ +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; +import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.quest.Quest; +import core.game.world.update.flag.context.Animation; + +/** + * Handles the surok magis dialogue. + * @author Vexia + * @editor Nuggles + * fixed the default dialogue, the NPC and Player tags were swapped. + */ +public class SurokMagisDialogue extends DialoguePlugin { + + /** + * The quest instance. + */ + private Quest quest; + + /** + * The cutscene. + */ + private WLBelowCutscene cutscene; + + /** + * Constructs a new {@Code SurokMagisDialogue} {@Code Object} + */ + public SurokMagisDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@Code SurokMagisDialogue} {@Code Object} + * @param player the player. + */ + public SurokMagisDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new SurokMagisDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + quest = player.getQuestRepository().getQuest(Quests.WHAT_LIES_BELOW); + switch (quest.getStage(player)) { + default: + npc("Excuse me?"); + break; + case 20: + player("Hello."); + break; + case 30: + case 40: + npc("Ah! You're back. Have you found the things I need yet?"); + break; + case 50: + if (!player.hasItem(WhatLiesBelow.SUROKS_LETTER)) { + player("That letter was treasonous so I destroyed it!"); + stage = 10; + break; + } + player("This letter is treasonous! I'm going to report you to the", "king!"); + break; + case 60: + npc("Hi, " + player.getUsername() + "!"); + break; + case 70: + if (!player.getInventory().containsItem(WhatLiesBelow.BEACON_RING) && !player.getEquipment().containsItem(WhatLiesBelow.BEACON_RING)) { + player("I should probably get my beacon ring first..."); + stage = -1; + break; + } + if (args.length >= 2) { + cutscene = player.getAttribute("cutscene", null); + if (args.length == 3) { + player.getDialogueInterpreter().sendDialogues(cutscene.getZaff(), null, "Your teleport spell has been corrupted, Surok! I have", "placed a magic block on this room. You will remain here,", "under guard, in the library from now on."); + stage = 12; + return true; + } + interpreter.sendPlainMessage(true, "The room grows dark and you sense objects moving..."); + stage = 10; + break; + } + player("Surok!! Your plans have been uncovered! You are hereby", "under arrest on the authority of the Varrock Palace", "Secret Guard!"); + break; + case 80: + case 90: + case 100: + npc("You have foiled my plans, " + player.getUsername() + "... I obviously", "underestimated you."); + break; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (quest.getStage(player)) { + default: + switch (stage) { + case 0: + npc("What do you want? ...Oh, wait. I know! You're", "probably just like all the others, aren't you? After some", "fancy spell or potion from me, I bet!"); + stage = 1; + break; + case 1: + player("No! at least, I don't think so. What sort of spells", "do you have?"); + stage = 2; + break; + case 2: + npc("Hah! I knew it! I expect you want my Aphro-Dizzy-", "Yak spell! Want someone to fall madly in love with you,", "eh?"); + stage = 3; + break; + case 3: + player("That spell sounds very interesting, but I didn't mean to", "disturb you!"); + stage = 4; + break; + case 4: + npc("Well, I see that you do have some manners. I'm glad", "to see that you use them."); + stage = 5; + break; + case 5: + npc("Now, if it's all the same, I am very busy at the", "moment. Come back another time", "please and thank you."); + stage = 6; + break; + case 6: + player("Yes, of course!"); + stage = 7; + break; + case 7: + end(); + break; + } + break; + case 20: + switch (stage) { + case 0: + npc("Hah! Come for my Aphro-Dizzy-Yak spell! Want", "someone to fall madly in love with you eh? Not", "surprised with a face like that, to be honest!"); + stage++; + break; + case 1: + player("I didn't come here to be insulted!"); + stage++; + break; + case 2: + npc("Really? Well, with ears like that, you do surprise me!"); + stage++; + break; + case 3: + if (!player.getInventory().containsItem(WhatLiesBelow.RATS_LETTER)) { + player("Nevermind!"); + stage = 99; + break; + } + player("No, look. I have a letter for you."); + stage++; + break; + case 4: + npc("Really? Well then, let me see it!"); + stage++; + break; + case 5: + player("Here it is!"); + stage++; + break; + case 6: + npc.animate(Animation.create(6096)); + npc("Of all the luck!"); + stage++; + break; + case 7: + player("Why did you destroy the letter?"); + stage++; + break; + case 8: + npc("None of your business! It's a secret!"); + stage++; + break; + case 9: + player("Yes, there seems to be a lot of them going around at", "the moment."); + stage++; + break; + case 10: + npc("Of course. Hmmm. However, I could let you in on", "another secret, if you like?"); + stage++; + break; + case 11: + player("Go on, then!"); + stage++; + break; + case 12: + npc("My secret is this. I have been spending time here in", "the palace library trying to discover some ancient spells", "and magics."); + stage++; + break; + case 13: + npc("In my research, I have uncovered the most astounding", "spell that will allow me to transform simple clay into solid", "gold bars!"); + stage++; + break; + case 14: + npc("Now I am ready to use the spell to create all the gold", "I...uh...the city wants. I would gladly share this gold", "with you; I simply need a few more things."); + stage++; + break; + case 15: + player("Okay, what do you need?"); + stage++; + break; + case 16: + npc("I will only need a couple of items. The first is very", "simple: an ordinary bowl to use as a casting vessel."); + stage++; + break; + case 17: + npc("You should be able to find one of these at any local", "store here in Varrock. I would go myself but I", "am...uh...busy with my research."); + stage++; + break; + case 18: + npc("The other item is much harder. I need a metal wand", "infused with chaos magic."); + stage++; + break; + case 19: + player("How would I get something like that?"); + stage++; + break; + case 20: + npc("Take this metal wand. You will also need 15 chaos", "runes. When you get to the Chaos Altar, use the wand", "on the altar itself. This would infuse the runes into the", "wand."); + stage++; + break; + case 21: + player("How on earth do you know about Runecrafting? I", "thought only a few people knew of it."); + stage++; + break; + case 22: + npc("Hah! Don't presume to think that those wizards in their", "fancy towers are the only people to have heard of", "Runecrafting! Now pay attention!"); + stage++; + break; + case 23: + npc("You will need to have the 15 chaos runes in your", "inventory. Make sure you also have either a chaos", "talisman or chaos tiara to complete the infusion."); + stage++; + break; + case 24: + player("Where can I get a talisman or a tiara?"); + stage++; + break; + case 25: + npc("I'm afraid I don't know. You will need to research for", "one."); + stage++; + break; + case 26: + npc("Bring the infused wand and a bowl back to me and I", "will make us both rich!"); + stage++; + break; + case 27: + npc("One more thing. I have uncovered information here in", "the library which may be of use to you. It tells of a", "safe route to the Chaos Altar that avoids the Wilderness."); + stage++; + break; + case 28: + player("Great! What is it?"); + stage++; + break; + case 29: + npc("It is an old tome...a history book of sorts. It's", "somewhere here in the library. I forget where I left it,", "but it should be easy enough for you to find."); + stage++; + break; + case 30: + npc("I have also given you a copy of a diary", "I...uh...acquired. It may also help you to find that which", "you seek."); + stage++; + break; + case 31: + player.getInventory().remove(WhatLiesBelow.RATS_LETTER); + player.getInventory().add(WhatLiesBelow.WAND, player); + player.getInventory().add(WhatLiesBelow.SIN_KETH_DIARY, player); + quest.setStage(player, 30); + end(); + break; + case 99: + end(); + break; + } + break; + case 30: + case 40: + switch (stage) { + case 0: + if (player.getInventory().containsItem(WhatLiesBelow.INFUSED_WAND)) { + if (!player.getInventory().containsItem(WhatLiesBelow.BOWL)) { + player("No, I still need to get you a bowl."); + stage = 1; + break; + } + player("I have the things you wanted."); + stage = 6; + break; + } + if (!player.hasItem(WhatLiesBelow.WAND)) { + player("I lost the wand!"); + stage = 2; + break; + } + if (!player.hasItem(WhatLiesBelow.SIN_KETH_DIARY)) { + player("I lost the diary!"); + stage = 4; + break; + } + player("No not yet."); + stage++; + break; + case 1: + end(); + break; + case 2: + npc("Somehow, I knew that would happen so I have made a", "few spares for just such an occasion!"); + stage++; + break; + case 3: + npc("Here you are. Try not to lose this one!"); + player.getInventory().add(WhatLiesBelow.WAND, player); + stage = 1; + break; + case 4: + player.getInventory().add(WhatLiesBelow.SIN_KETH_DIARY, player); + npc("Here you are, Try not to lose this one!"); + stage = 5; + break; + case 5: + end(); + break; + case 6: + npc("Excellent! Well done! I knew that you would not let", "me down."); + stage++; + break; + case 7: + player("So...about this gold that you're going to", "give me?"); + stage++; + break; + case 8: + npc("All in good time. I must prepare the spell first", ", and that will take a little time. While I am", "doing that, please take this letter to Rat, the trader", "outside the city who sent you here."); + stage++; + break; + case 9: + player("Okay, but I'll be back for my gold."); + stage++; + break; + case 10: + npc("Yes, yes, yes. Now off you go!"); + stage++; + break; + case 11: + quest.setStage(player, 50); + player.getInventory().add(WhatLiesBelow.SUROKS_LETTER, player); + end(); + break; + } + break; + case 50: + switch (stage) { + case 0: + npc("Hah! Have fun with that!"); + stage++; + break; + case 1: + end(); + break; + case 10: + npc("Really? You destroyed it?"); + stage++; + break; + case 11: + player("Yes!"); + stage++; + break; + case 12: + npc("You want another one, don't you?"); + stage++; + break; + case 13: + player("Ah..uh..yes..yes, I do."); + stage++; + break; + case 14: + player.getInventory().add(WhatLiesBelow.SUROKS_LETTER, player); + npc("Fine. Here you are. And stop all this complaining; it's", "getting me down!"); + stage++; + break; + case 15: + end(); + break; + } + break; + case 60: + switch (stage) { + case 0: + player("You are a bad man!"); + stage++; + break; + case 1: + end(); + break; + } + break; + case 70: + switch (stage) { + case -1: + end(); + break; + case 0: + npc("So! You're with the Secret Guard, eh? I should have", "known! I knew you had ugly ears from the start...", "and your nose is too short!"); + stage++; + break; + case 1: + player("Give yourself up, Surok!"); + stage++; + break; + case 2: + npc("Never! I am Surok Magis, descendant of the High Elder", "Sin'keth Magis, rightful heir of the Dagon'hai Order! I will", "have my revenge on those who destroyed my people!"); + stage++; + break; + case 3: + player("The place is surrounded. There is nowhere to run!"); + stage++; + break; + case 4: + npc("Do you really wish to die so readily? Are you prepared", "to face your death?"); + stage++; + break; + case 5: + player("Bring it on!"); + stage++; + break; + case 6: + npc("I am Dagon'hai! I run from nothing. My spell has", "been completed and it is time for you to meet your", "end, " + player.getUsername() + "! The king is now under my control!"); + stage++; + break; + case 7: + end(); + ActivityManager.start(player, "What Lies below", false); + break; + case 10: + close(); + player.unlock(); + cutscene.getKing().unlock(); + cutscene.reset(); + player.getInterfaceManager().restoreTabs(); + cutscene.getKing().getProperties().getCombatPulse().attack(player); + stage = 11; + break; + case 11: + close(); + stage = 12; + break; + case 12: + player.lock(); + npc("No! All is lost! I must escape!"); + stage++; + break; + case 13: + interpreter.sendDialogues(cutscene.getZaff(), null, "You will not escape justice this time, Surok!"); + stage = 14; + break; + case 14: + npc("No! My plans have been ruined! I was so close to", "success!"); + stage = 16; + break; + case 16: + interpreter.sendDialogues(cutscene.getZaff(), null, "Thank you for your help, " + player.getUsername() + ". I will put the", "room back in order and then I must leave. Surok is", "defeated and will be no more trouble for us. We will", "guard him more closely from now on!"); + stage++; + break; + case 17: + quest.setStage(player, 80); + interpreter.sendPlainMessage(true, "The room grows dark and you sense objects moving..."); + cutscene.stop(true); + break; + } + break; + case 80: + case 90: + case 100: + switch (stage) { + case 0: + player("Yes. Let this be a lesson to you."); + stage++; + break; + case 1: + npc("..."); + stage++; + break; + case 2: + end(); + break; + } + break; + } + return true; + } + + @Override + public void end() { + if (cutscene != null) { + return; + } + super.end(); + } + + @Override + public int[] getIds() { + return new int[] { 5834, 5835 }; + } + + /** + * Gets the cutscene. + * @return the cutscene + */ + public WLBelowCutscene getCutscene() { + return cutscene; + } + + /** + * Sets the cutscene. + * @param cutscene the cutscene to set. + */ + public void setCutscene(WLBelowCutscene cutscene) { + this.cutscene = cutscene; + } + +} diff --git a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/WLBelowCutscene.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowCutscene.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/members/whatliesbelow/WLBelowCutscene.java rename to Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowCutscene.java index e26b29980..d1c154c5d 100644 --- a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/WLBelowCutscene.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowCutscene.java @@ -1,8 +1,8 @@ -package core.game.content.quest.members.whatliesbelow; +package content.region.misthalin.varrock.quest.whatliesbelow; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.FacialExpression; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -12,7 +12,7 @@ import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.build.DynamicRegion; @@ -191,7 +191,7 @@ public class WLBelowCutscene extends CutscenePlugin { king.lock(); king.getProperties().getCombatPulse().stop(); player.getProperties().getCombatPulse().stop(); - player.getInterfaceManager().hideTabs(getRemovedTabs()); + player.getInterfaceManager().removeTabs(getRemovedTabs()); zaff = NPC.create(5836, player.getLocation()); Location loc = RegionManager.getSpawnLocation(player, zaff); if (loc != null) { @@ -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/java/core/game/content/quest/members/whatliesbelow/WLBelowPlugin.java b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowPlugin.java similarity index 90% rename from Server/src/main/java/core/game/content/quest/members/whatliesbelow/WLBelowPlugin.java rename to Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowPlugin.java index 664683215..09c27bcd9 100644 --- a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/WLBelowPlugin.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WLBelowPlugin.java @@ -1,12 +1,13 @@ -package core.game.content.quest.members.whatliesbelow; +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; -import core.game.content.activity.ActivityManager; +import core.game.activity.ActivityManager; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.gather.SkillingTool; -import core.game.node.entity.skill.runecrafting.Altar; +import content.data.skill.SkillingTool; +import content.global.skill.runecrafting.Altar; import core.game.interaction.NodeUsageEvent; import core.game.interaction.OptionHandler; import core.game.interaction.UseWithHandler; @@ -15,11 +16,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.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.*; + /** * Handles the what lies below options. @@ -30,14 +33,14 @@ public class WLBelowPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new OutlawNPC()); - PluginManager.definePlugin(new KingRoaldNPC()); + ClassScanner.definePlugin(new OutlawNPC()); + ClassScanner.definePlugin(new KingRoaldNPC()); ActivityManager.register(new WLBelowCutscene()); - PluginManager.definePlugin(new FolderHandler()); - PluginManager.definePlugin(new MetalWandHandler()); - PluginManager.definePlugin(new AnnaJonesDialogue()); - PluginManager.definePlugin(new SurokMagisDialogue()); - PluginManager.definePlugin(new RatBurgissDialogue()); + ClassScanner.definePlugin(new FolderHandler()); + ClassScanner.definePlugin(new MetalWandHandler()); + ClassScanner.definePlugin(new AnnaJonesDialogue()); + ClassScanner.definePlugin(new SurokMagisDialogue()); + ClassScanner.definePlugin(new RatBurgissDialogue()); SceneryDefinition.forId(23095).getHandlers().put("option:use", this); SceneryDefinition.forId(23058).getHandlers().put("option:enter", this); SceneryDefinition.forId(23057).getHandlers().put("option:excavate", this); @@ -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 new file mode 100644 index 000000000..0b0238179 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/whatliesbelow/WhatLiesBelow.java @@ -0,0 +1,219 @@ +package content.region.misthalin.varrock.quest.whatliesbelow; + +import core.plugin.Initializable; +import org.rs09.consts.Items; +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 core.plugin.ClassScanner; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; + +/** + * The what lies below quest. + * @author Vexa + * + */ +@Initializable +public class WhatLiesBelow extends Quest { + /** + * The bowl item. + */ + public static final Item BOWL = new Item(Items.BOWL_1923); + + /** + * The sin keth diary. + */ + public static final Item SIN_KETH_DIARY = new Item(Items.SINKETHS_DIARY_11002); + + /** + * The empty folder item. + */ + public static final Item EMPTY_FOLDER = new Item(Items.AN_EMPTY_FOLDER_11003); + + /** + * The used folder. + */ + public static final Item USED_FOLDER = new Item(Items.USED_FOLDER_11006); + + /** + * The full folder. + */ + public static final Item FULL_FOLDER = new Item(Items.FULL_FOLDER_11007); + + /** + * The rats paper item. + */ + public static final Item RATS_PAPER = new Item(Items.RATS_PAPER_11008); + + /** + * The rats letter. + */ + public static final Item RATS_LETTER = new Item(Items.RATS_LETTER_11009); + + /** + * The suroks letter. + */ + public static final Item SUROKS_LETTER = new Item(Items.SUROKS_LETTER_11010); + + /** + * The wand item. + */ + public static final Item WAND = new Item(Items.WAND_11012); + + /** + * The infused item. + */ + public static final Item INFUSED_WAND = new Item(Items.INFUSED_WAND_11013); + + /** + * The bacon ring. + */ + public static final Item BEACON_RING = new Item(Items.BEACON_RING_11014); + + /** + * The requirements. + */ + private final boolean[] requirements = new boolean[4]; + + /** + * Constructs a new {@Code WhatLiesBelow} {@Code Object} + */ + public WhatLiesBelow() { + super(Quests.WHAT_LIES_BELOW, 136, 135, 1); + } + + @Override + public Quest newInstance(Object object) { + ClassScanner.definePlugin(new WLBelowPlugin()); + return this; + } + + @Override + public void drawJournal(Player player, int stage) { + super.drawJournal(player, stage); + 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++); + } + } + } + + @Override + public void start(Player player) { + super.start(player); + player.getInventory().add(EMPTY_FOLDER, player); + } + + @Override + public void finish(Player player) { + super.finish(player); + player.getPacketDispatch().sendString("8,000 Runecrafting XP", 277, 8+ 2); + player.getPacketDispatch().sendString("2,000 Defence XP", 277, 9+ 2); + player.getPacketDispatch().sendString("Beacon Ring", 277, 10+ 2); + player.getPacketDispatch().sendString("Knowledge of Chaos Tunnel", 277, 11+ 2); + player.getPacketDispatch().sendItemZoomOnInterface(BEACON_RING.getId(), 235, 277, 3+ 2); + player.getSkills().addExperience(Skills.RUNECRAFTING, 8000); + player.getSkills().addExperience(Skills.DEFENCE, 2000); + player.getQuestRepository().syncronizeTab(player); + } + + + @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(Quests.RUNE_MYSTERIES); + return requirements[0] && requirements[2] && requirements[3]; + } + + @Override + public int[] getConfig(Player player, int stage) { + int id = 992; + if (stage >= 40 && stage != 100) { + return new int[] { id, (1 << 8) + 1 }; + } + if (stage == 0) { + return new int[] { id, 0 }; + } else if (stage > 0 && stage < 100) { + return new int[] { id, 1 }; + } + setVarp(player, 1181, (1 << 8) + (1 << 9), true); + return new int[] { id, 502 }; + } + +} diff --git a/Server/src/main/java/core/game/content/dialogue/DrOnglewipDialogue.java b/Server/src/main/content/region/misthalin/wiztower/dialogue/DrOnglewipDialogue.java similarity index 79% rename from Server/src/main/java/core/game/content/dialogue/DrOnglewipDialogue.java rename to Server/src/main/content/region/misthalin/wiztower/dialogue/DrOnglewipDialogue.java index 9c8943794..75b08b020 100644 --- a/Server/src/main/java/core/game/content/dialogue/DrOnglewipDialogue.java +++ b/Server/src/main/content/region/misthalin/wiztower/dialogue/DrOnglewipDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.misthalin.wiztower.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; @@ -37,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; } @@ -50,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: @@ -58,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 new file mode 100644 index 000000000..4f4200c9e --- /dev/null +++ b/Server/src/main/content/region/misthalin/wiztower/dialogue/TraibornDialogue.java @@ -0,0 +1,401 @@ +package content.region.misthalin.wiztower.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.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 content.region.misthalin.varrock.quest.demonslayer.DemonSlayer; +import content.data.Quests; + +/** + * Represents the dialogue used to handle the Traiborn NPC. + * @author 'Vexia + * @date 3/1/14 + */ +public class TraibornDialogue extends DialoguePlugin { + + /** + * Represents the bones item. + */ + private static final Item BONES = new Item(526, 25); + + /** + * Represents the object animation. + */ + @SuppressWarnings("unused") + private static final Animation OBJECT_ANIM = new Animation(4599); + + /** + * Represents the animation of giving the key. + */ + private static final Animation ANIMATION = new Animation(536); + + /** + * Represents the quest instance. + */ + private Quest quest; + + /** + * Constructs a new {@code TraibornDialogue} {@code Object}. + */ + public TraibornDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code TraibornDialogue} {@code Object}. + * @param player the player. + */ + public TraibornDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new TraibornDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + quest = player.getQuestRepository().getQuest(Quests.DEMON_SLAYER); + switch (quest.getStage(player)) { + case 20: + if (player.getAttribute("demon-slayer:traiborn", false)) { + npc("How are you doing finding bones?"); + stage = 380; + } else { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ello young thingummywut."); + stage = 0; + } + break; + default: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ello young thingummywut."); + stage = 0; + break; + } + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (quest.getStage(player)) { + case 20: + switch (stage) { + case 0: + if (player.getInventory().containsItem(DemonSlayer.THIRD_KEY) && player.getBank().containsItem(DemonSlayer.THIRD_KEY)) { + options("What's a thingummywut?", "Teach me to be a mighty powerful wizard."); + stage = 1; + } else { + options("What's a thingummywut?", "Teach me to be a mighty powerful wizard.", "I need to get a key given to you by Sir Prysin."); + stage = 1; + } + break; + case 1: + switch (buttonId) { + case 1: + case 2: + handleDefault(buttonId); + break; + case 3: + player("I need to get a key given to you by Sir Prysin."); + stage = 300; + break; + } + break; + case 300: + npc("Sir Prysin? Who's that? What would I want his key", "for?"); + stage = 301; + break; + case 301: + player("Well, have you got any keys knocking around?"); + stage = 302; + break; + case 302: + npc("Now you come to mention it, yes I do have a key. It's", "in my special closet of valuable stuff. Now how do I get", "into that?"); + stage = 303; + break; + case 303: + npc("I sealed it using one of my magic rituals. So it would", "makes sense that another ritual would open it again."); + stage = 304; + break; + case 304: + player("So do you know what ritual to use?"); + stage = 305; + break; + case 305: + npc("Let me think a second."); + stage = 306; + break; + case 306: + npc("Yes a simple drazier style ritual should suffice. Hmm,", "main problem with that is I'll need 25 sets of bones.", "Now where am I going to get hold of something like", "that?"); + stage = 307; + break; + case 307: + options("Hmm, that's too bad. I really need that key.", "I'll get the bones for you."); + stage = 308; + break; + case 308: + switch (buttonId) { + case 1: + player("Hmm, that's too bad. I really need that key."); + stage = 330; + break; + case 2: + player("I'll get the bones for you."); + stage = 340; + break; + } + break; + case 330: + npc("Ah well, sorry I couldn't be any more help."); + stage = 331; + break; + case 331: + end(); + break; + case 340: + npc("Ooh that would be very good of you."); + stage = 341; + break; + case 341: + player("Ok I'll speak to you when I've got some bones."); + stage = 342; + break; + case 342: + player.setAttribute("/save:demon-slayer:traiborn", true); + end(); + break; + case 380: + if (player.getInventory().containsItem(BONES)) { + player("I have some bones."); + stage = 382; + } else { + player("I don't have all the bones yet."); + stage = 381; + } + break; + case 382: + npc("Give 'em here then."); + stage = 383; + break; + case 383: + interpreter.sendDialogue("You give Traiborn 25 sets of bones."); + stage = 384; + break; + case 384: + npc("Hurrah! That's all 25 sets of bones."); + stage = 385; + break; + case 385: + npc.animate(new Animation(4602)); + npc("Wings of dark and colour too,", "Spreading in the morning dew;", "Locked away I have a key;", "Return it now, please, unto me."); + stage = 386; + break; + case 386: + final Scenery object = new Scenery(17434, Location.create(3113, 3161, 1), 11, 1); + SceneryBuilder.add(object); + npc.faceLocation(object.getLocation()); + npc.animate(ANIMATION); + if (!player.getInventory().containsItem(BONES)) { + end(); + return true; + } + if (player.getInventory().remove(BONES)) { + player.removeAttribute("demon-slayer:traiborn"); + player.getInventory().add(DemonSlayer.THIRD_KEY); + interpreter.sendItemMessage(DemonSlayer.THIRD_KEY.getId(), "Traiborn hands you a key."); + stage = 387; + } + GameWorld.getPulser().submit(new Pulse(1) { + int counter = 0; + + @Override + public boolean pulse() { + switch (counter++) { + case 5: + npc.face(player); + break; + case 7: + SceneryBuilder.remove(object); + return true; + } + return false; + } + }); + break; + case 387: + player("Thank you very much."); + stage = 388; + break; + case 388: + npc("Not a problem for a friend of Sir What's-his-face."); + stage = 389; + break; + case 389: + end(); + break; + case 381: + end(); + break; + } + break; + case 0: + switch (stage) { + default: + handleDefault(buttonId); + break; + } + break; + } + return true; + } + + /** + * Method used to handle the default button id. + * @param buttonId the buttton id. + */ + private final void handleDefault(int buttonId) { + switch (stage) { + case 0: + interpreter.sendOptions("Select an Option", "What's a thingummywut?", "Teach me to be a mighty and powerful wizard."); + stage = 1; + break; + case 1: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's a thingumywut?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Teach me to be a mighty and powerful wizard."); + stage = 20; + break; + } + break; + case 10: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A thingummywut? Where? Where?"); + stage = 11; + break; + case 11: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Those pesky thingummywuts. They get everywhere.", "They leave a terrible mess too."); + stage = 12; + break; + case 12: + interpreter.sendOptions("Select an Option", "Err you just called me a thingymummywut.", "Tell me what they look like and I'll mask 'em."); + stage = 13; + break; + case 13: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err you just called me thingummywut."); + stage = 100; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me what they look like and I'll mash 'em."); + stage = 120; + break; + } + break; + case 120: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't be ridiculous. No-one has ever seen one."); + stage = 121; + break; + case 121: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "They're invisible, or a myth, or a figment of my", "imagination. Can't remember which right now."); + stage = 122; + break; + case 122: + end(); + break; + case 100: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're a thingummywut? I've never seen one up close", "before. They said I was mad!"); + stage = 101; + break; + case 101: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Now you are my proof! There ARE thingummywuts in", "this tower. Now where can I find a cage big enough to", "keep you?"); + stage = 102; + break; + case 102: + interpreter.sendOptions("Select an Option", "Err I'd better be off really.", "They're right, you are mad."); + stage = 103; + break; + case 103: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err I'd better be off really."); + stage = 110; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "They're right, you are mad."); + stage = 130; + break; + } + break; + case 130: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That's a pity. I thought maybe they were winding me", "up."); + stage = 131; + break; + case 131: + end(); + break; + case 110: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh ok, have a good time, and watch out for sheep!", "They're more cunning than they look."); + stage = 111; + break; + case 111: + end(); + break; + case 20: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wizard eh? You don't want any truck with that sort.", "They're not to be trusted. That's what I've heard", "anyways."); + stage = 21; + break; + case 21: + interpreter.sendOptions("Select an Option", "So aren't you a wizard?", "Oh I'd better stop talking to you then."); + stage = 22; + break; + case 22: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So aren't you a wizard?"); + stage = 40; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh I'd better stop talking to you then."); + stage = 60; + break; + } + break; + case 60: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Cheerio then. It was nice chatting to you."); + stage = 61; + break; + case 61: + end(); + break; + case 40: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How dare you? Of course I'm a wizard. Now don't be", "so cheeky or I'll turn you into a frog."); + stage = 41; + break; + case 41: + end(); + break; + } + } + + @Override + public int[] getIds() { + return new int[] { 881 }; + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/wiztower/handlers/DemonTauntHandler.kt b/Server/src/main/content/region/misthalin/wiztower/handlers/DemonTauntHandler.kt new file mode 100644 index 000000000..6b7406406 --- /dev/null +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/DemonTauntHandler.kt @@ -0,0 +1,29 @@ +package content.region.misthalin.wiztower.handlers + +import core.api.* +import core.game.node.entity.player.link.emote.Emotes +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * Handles taunting of the demon in the wizard's tower + * @author Ceikry + * @author vddCore + */ +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, "Graaaagh!") + face(demon, player, 3) + emote(player, Emotes.RASPBERRY) + return@on true + } + } +} \ No newline at end of file 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/WizardGuildPortals.kt b/Server/src/main/content/region/misthalin/wiztower/handlers/WizardGuildPortals.kt new file mode 100644 index 000000000..e6c5a0b70 --- /dev/null +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/WizardGuildPortals.kt @@ -0,0 +1,36 @@ +package content.region.misthalin.wiztower.handlers + +import core.api.* +import core.game.world.map.Location +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class WizardGuildPortals : InteractionListener { + + val WTOWER_PORTAL = 2156 + val DWTOWER_PORTAL = 2157 + val SORC_TOWER_PORTAL = 2158 + + override fun defineListeners() { + on(WTOWER_PORTAL, IntType.SCENERY, "enter"){ player, _ -> + teleport(player, Location.create(3109, 3159, 0)) + sendMessage(player, "You enter the magic portal...") + sendMessage(player, "You teleport to the Wizards' tower.") + return@on true + } + + on(DWTOWER_PORTAL, IntType.SCENERY, "enter"){ player, _ -> + teleport(player, Location.create(2907, 3333, 0)) + sendMessage(player, "You enter the magic portal...") + sendMessage(player, "You teleport to the Dark Wizards' tower.") + return@on true + } + + on(SORC_TOWER_PORTAL, IntType.SCENERY, "enter"){ player, _ -> + teleport(player, Location.create(2703, 3406, 0)) + sendMessage(player, "You enter the magic portal...") + sendMessage(player, "You teleport to Thormac the Sorceror's house.") + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/city/WizardTowerPlugin.java b/Server/src/main/content/region/misthalin/wiztower/handlers/WizardTowerPlugin.java similarity index 97% rename from Server/src/main/java/core/game/interaction/city/WizardTowerPlugin.java rename to Server/src/main/content/region/misthalin/wiztower/handlers/WizardTowerPlugin.java index c4093c1a5..01b970fd8 100644 --- a/Server/src/main/java/core/game/interaction/city/WizardTowerPlugin.java +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/WizardTowerPlugin.java @@ -1,17 +1,16 @@ -package core.game.interaction.city; +package content.region.misthalin.wiztower.handlers; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.Skillcape; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +import core.game.dialogue.DialoguePlugin; +import core.game.global.Skillcape; +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.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; @@ -36,9 +35,10 @@ import core.plugin.Plugin; import core.tools.RandomFunction; import core.tools.StringUtils; import kotlin.Unit; -import rs09.game.content.global.travel.EssenceTeleport; -import rs09.game.world.GameWorld; -import rs09.plugin.PluginManager; +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. @@ -67,13 +67,13 @@ public final class WizardTowerPlugin extends OptionHandler { SceneryDefinition.forId(32015).getHandlers().put("option:climb-up", this); NPCDefinition.forId(300).getHandlers().put("option:teleport", this); SceneryDefinition.forId(11993).getHandlers().put("option:open", this); - PluginManager.definePlugin(new WizardtowerWizardNPC()); - PluginManager.definePlugin(new WizardTowerDialogue()); - PluginManager.definePlugin(new WizardMisgogDialogue()); - PluginManager.definePlugin(new WizardGrayzagDialogue()); - PluginManager.definePlugin(new WizardDialogue()); - PluginManager.definePlugin(new SedridorDialogue()); - PluginManager.definePlugin(new AuburyDialoguePlugin()); + ClassScanner.definePlugin(new WizardtowerWizardNPC()); + ClassScanner.definePlugin(new WizardTowerDialogue()); + ClassScanner.definePlugin(new WizardMizgogDialogue()); + ClassScanner.definePlugin(new WizardGrayzagDialogue()); + ClassScanner.definePlugin(new WizardDialogue()); + ClassScanner.definePlugin(new SedridorDialogue()); + ClassScanner.definePlugin(new AuburyDialoguePlugin()); return this; } @@ -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; @@ -259,12 +253,12 @@ public final class WizardTowerPlugin extends OptionHandler { } /** - * Handles the WizardMisgogDialogue dialogue. + * Handles the WizardMizgogDialogue dialogue. * * @author 'Vexia * @version 1.0 */ - public static final class WizardMisgogDialogue extends DialoguePlugin { + public static final class WizardMizgogDialogue extends DialoguePlugin { /** * Represents the black bead item. @@ -292,32 +286,32 @@ public final class WizardTowerPlugin extends OptionHandler { private static final Animation ANIMATION = new Animation(4285); /** - * Constructs a new {@code WizardMisgogDialogue} {@code Object}. + * Constructs a new {@code WizardMizgogDialogue} {@code Object}. */ - public WizardMisgogDialogue() { + public WizardMizgogDialogue() { /** * empty. */ } /** - * Constructs a new {@code WizardMisgogDialogue} {@code Object}. + * Constructs a new {@code WizardMizgogDialogue} {@code Object}. * * @param player the player. */ - public WizardMisgogDialogue(Player player) { + public WizardMizgogDialogue(Player player) { super(player); } @Override public DialoguePlugin newInstance(Player player) { - return new WizardMisgogDialogue(player); + return new WizardMizgogDialogue(player); } @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) { @@ -687,7 +681,7 @@ public final class WizardTowerPlugin extends OptionHandler { } final int amount = getAmt(buttonId); if (amount == -1) {// rscript. - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, true, "Enter the amount:", (value) -> { make(bark, (int) value); return Unit.INSTANCE; }); @@ -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/java/core/game/interaction/city/RCGuildMap.java b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildMap.java similarity index 77% rename from Server/src/main/java/core/game/interaction/city/RCGuildMap.java rename to Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildMap.java index d2b9270f2..1044a72f6 100644 --- a/Server/src/main/java/core/game/interaction/city/RCGuildMap.java +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildMap.java @@ -1,5 +1,6 @@ -package core.game.interaction.city; +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/java/core/game/interaction/city/RCGuildPortal.java b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildPortal.java similarity index 94% rename from Server/src/main/java/core/game/interaction/city/RCGuildPortal.java rename to Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildPortal.java index b24822c32..75296322c 100644 --- a/Server/src/main/java/core/game/interaction/city/RCGuildPortal.java +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildPortal.java @@ -1,4 +1,4 @@ -package core.game.interaction.city; +package content.region.misthalin.wiztower.handlers.rcguild; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/interaction/object/RuneCraftingGuildObjects.java b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RuneCraftingGuildObjects.java similarity index 95% rename from Server/src/main/java/core/game/interaction/object/RuneCraftingGuildObjects.java rename to Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RuneCraftingGuildObjects.java index 68511467e..600525b1a 100644 --- a/Server/src/main/java/core/game/interaction/object/RuneCraftingGuildObjects.java +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RuneCraftingGuildObjects.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.misthalin.wiztower.handlers.rcguild; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; diff --git a/Server/src/main/java/core/game/content/quest/free/ImpCatcher.java b/Server/src/main/content/region/misthalin/wiztower/quest/ImpCatcher.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/free/ImpCatcher.java rename to Server/src/main/content/region/misthalin/wiztower/quest/ImpCatcher.java index 8bc3e9b55..d077a59f1 100644 --- a/Server/src/main/java/core/game/content/quest/free/ImpCatcher.java +++ b/Server/src/main/content/region/misthalin/wiztower/quest/ImpCatcher.java @@ -1,4 +1,4 @@ -package core.game.content.quest.free; +package content.region.misthalin.wiztower.quest; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; @@ -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/java/core/game/content/dialogue/HiylikMynaDialogue.java b/Server/src/main/content/region/morytania/burghderott/dialogue/HiylikMynaDialogue.java similarity index 96% rename from Server/src/main/java/core/game/content/dialogue/HiylikMynaDialogue.java rename to Server/src/main/content/region/morytania/burghderott/dialogue/HiylikMynaDialogue.java index 1baecca1d..40ae0743d 100644 --- a/Server/src/main/java/core/game/content/dialogue/HiylikMynaDialogue.java +++ b/Server/src/main/content/region/morytania/burghderott/dialogue/HiylikMynaDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.morytania.burghderott.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; diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/AgilityBossDialogue.kt b/Server/src/main/content/region/morytania/canifis/dialogue/AgilityBossDialogue.kt similarity index 93% rename from Server/src/main/kotlin/rs09/game/content/dialogue/AgilityBossDialogue.kt rename to Server/src/main/content/region/morytania/canifis/dialogue/AgilityBossDialogue.kt index 0b62b2825..5b476b620 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AgilityBossDialogue.kt +++ b/Server/src/main/content/region/morytania/canifis/dialogue/AgilityBossDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.content.dialogue +package content.region.morytania.canifis.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/BarkerDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/BarkerDialogue.java new file mode 100644 index 000000000..810c0d3bb --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/dialogue/BarkerDialogue.java @@ -0,0 +1,90 @@ +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 barker npc dialogue plugin. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +public final class BarkerDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code BarkerDialogue} {@code Object}. + */ + public BarkerDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code BarkerDialogue} {@code Object}. + * @param player the player. + */ + public BarkerDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new BarkerDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Hello."); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + 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: + interpreter.sendOptions("Select an Option", "Yes, please.", "No, thanks."); + stage = 2; + break; + case 2: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Yes, please."); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No thanks."); + stage = 15; + break; + } + break; + + case 15: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Unfortunate for you, yes?", "Many bargains, won't find elsewhere!"); + stage = 20; + break; + case 20: + end(); + break; + case 10: + end(); + npc.openShop(player); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 1039 }; + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/FilelioDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/FilelioDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/FilelioDialogue.java rename to Server/src/main/content/region/morytania/canifis/dialogue/FilelioDialogue.java index 41bc12aea..07c21876e 100644 --- a/Server/src/main/java/core/game/content/dialogue/FilelioDialogue.java +++ b/Server/src/main/content/region/morytania/canifis/dialogue/FilelioDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +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; 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/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 new file mode 100644 index 000000000..abe17c032 --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/dialogue/RufusDialogue.java @@ -0,0 +1,107 @@ +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 RufusDialogue dialogue. + * @author 'Vexia + */ +@Initializable +public class RufusDialogue extends DialoguePlugin { + + public RufusDialogue() { + + } + + public RufusDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + + return new RufusDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Hi!"); + stage = 0; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 0: + 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: + interpreter.sendOptions("Select an Option", "Why do you only sell meats?", "Do you sell cooked food?", "Can I buy some food?"); + stage = 2; + break; + case 2: + switch (buttonId) { + case 1: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Why do you only sell meats?"); + stage = 10; + break; + case 2: + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you sell cooked food?"); + stage = 20; + break; + case 3: + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Can I buy some food?"); + stage = 30; + break; + } + break; + case 10: + 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: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "...A vegetarian one?"); + stage = 12; + break; + case 12: + interpreter.sendDialogues(npc, FacialExpression.EXTREMELY_SHOCKED, "Vegetarrrian...?"); + stage = 13; + break; + case 13: + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Never mind."); + stage = 14; + break; + case 14: + end(); + break; + case 20: + 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.HAPPY, "Cerrrtainly!"); + stage = 31; + break; + case 31: + end(); + npc.openShop(player); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 1038 }; + } +} 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/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/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/java/core/game/interaction/npc/RoavarOptionPlugin.java b/Server/src/main/content/region/morytania/canifis/handlers/RoavarOptionPlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/npc/RoavarOptionPlugin.java rename to Server/src/main/content/region/morytania/canifis/handlers/RoavarOptionPlugin.java index 772f752dc..1a7152799 100644 --- a/Server/src/main/java/core/game/interaction/npc/RoavarOptionPlugin.java +++ b/Server/src/main/content/region/morytania/canifis/handlers/RoavarOptionPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.npc; +package content.region.morytania.canifis.handlers; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; 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/kotlin/rs09/game/content/dialogue/AfflictedDialogue.kt b/Server/src/main/content/region/morytania/dialogue/AfflictedDialogue.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/content/dialogue/AfflictedDialogue.kt rename to Server/src/main/content/region/morytania/dialogue/AfflictedDialogue.kt index 98f92ef5c..0f3cfd062 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AfflictedDialogue.kt +++ b/Server/src/main/content/region/morytania/dialogue/AfflictedDialogue.kt @@ -1,7 +1,7 @@ -package rs09.game.content.dialogue +package content.region.morytania.dialogue -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +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 diff --git a/Server/src/main/java/core/game/content/dialogue/UliziusDialogue.java b/Server/src/main/content/region/morytania/dialogue/UliziusDialogue.java similarity index 93% rename from Server/src/main/java/core/game/content/dialogue/UliziusDialogue.java rename to Server/src/main/content/region/morytania/dialogue/UliziusDialogue.java index 441c75ff1..6b83303c5 100644 --- a/Server/src/main/java/core/game/content/dialogue/UliziusDialogue.java +++ b/Server/src/main/content/region/morytania/dialogue/UliziusDialogue.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.morytania.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; diff --git a/Server/src/main/java/core/game/content/dialogue/VanstromKlause.java b/Server/src/main/content/region/morytania/dialogue/VanstromKlause.java similarity index 90% rename from Server/src/main/java/core/game/content/dialogue/VanstromKlause.java rename to Server/src/main/content/region/morytania/dialogue/VanstromKlause.java index 4f4cdec88..f9e8e8227 100644 --- a/Server/src/main/java/core/game/content/dialogue/VanstromKlause.java +++ b/Server/src/main/content/region/morytania/dialogue/VanstromKlause.java @@ -1,5 +1,7 @@ -package core.game.content.dialogue; +package content.region.morytania.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; diff --git a/Server/src/main/java/core/game/interaction/object/BarrowsBoatPlugin.java b/Server/src/main/content/region/morytania/handlers/BarrowsBoatPlugin.java similarity index 94% rename from Server/src/main/java/core/game/interaction/object/BarrowsBoatPlugin.java rename to Server/src/main/content/region/morytania/handlers/BarrowsBoatPlugin.java index 16656b4d4..befd516b6 100644 --- a/Server/src/main/java/core/game/interaction/object/BarrowsBoatPlugin.java +++ b/Server/src/main/content/region/morytania/handlers/BarrowsBoatPlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.morytania.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; @@ -6,7 +6,7 @@ import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; diff --git a/Server/src/main/java/core/game/interaction/object/BarrowsTunnelShortcut.java b/Server/src/main/content/region/morytania/handlers/BarrowsTunnelShortcut.java similarity index 94% rename from Server/src/main/java/core/game/interaction/object/BarrowsTunnelShortcut.java rename to Server/src/main/content/region/morytania/handlers/BarrowsTunnelShortcut.java index 210bc20ab..65f72d74c 100644 --- a/Server/src/main/java/core/game/interaction/object/BarrowsTunnelShortcut.java +++ b/Server/src/main/content/region/morytania/handlers/BarrowsTunnelShortcut.java @@ -1,8 +1,8 @@ -package core.game.interaction.object; +package content.region.morytania.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; +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; diff --git a/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt b/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt new file mode 100644 index 000000000..cb80df540 --- /dev/null +++ b/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt @@ -0,0 +1,106 @@ +package content.region.morytania.handlers + +import core.api.Container +import core.api.* +import content.data.consumables.Consumables +import core.game.consumable.Food +import core.game.interaction.MovementPulse +import core.game.node.entity.Entity +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import core.tools.RandomFunction +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import content.region.morytania.quest.naturespirit.NSUtils +import org.rs09.consts.Sounds + +@Initializable +class MortMyreGhastNPC : AbstractNPC { + //Constructor spaghetti because Arios I guess + constructor() : super(NPCs.GHAST_1052, null, true) {} + private constructor(id: Int, location: Location) : super(id, location) {} + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + isAggressive = id != ids[0] + return MortMyreGhastNPC(id, location) + } + + override fun handleTickActions() { + super.handleTickActions() + if(id == ids[0] && RandomFunction.roll(35)){ + val players = RegionManager.getLocalPlayers(this, 5).filter { !it.inCombat() } + if(players.isNotEmpty()){ + val player = players.random() + submitIndividualPulse(this, object : MovementPulse(this, player){ + override fun pulse(): Boolean { + animate(Animation(1093)) + attemptLifeSiphon(player) + return true + } + }) + } + } else { + val ticksTransformed = getWorldTicks() - getAttribute(this, "woke", 0) + if(!inCombat() && ticksTransformed > 10){ + reTransform() + } + } + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GHAST_1052, NPCs.GHAST_1053) + } + + fun attemptLifeSiphon(player: Player){ + var hasFood = false + + if(NSUtils.activatePouch(player, this)) return + + GlobalScope.launch { + for(i in player.inventory.toArray()){ + if(i == null) continue + val consumable = Consumables.getConsumableById(i.id) + if(consumable != null && consumable.consumable is Food) { + hasFood = true + 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.") + } else if(!hasFood){ + impact(player, RandomFunction.random(3,6), ImpactHandler.HitsplatType.NORMAL) + sendMessage(player, "A supernatural force draws energy from you.") + } + } + } + + override fun commenceDeath(killer: Entity?) { + super.commenceDeath(killer) + } + + override fun finalizeDeath(killer: Entity?) { + super.finalizeDeath(killer) + if(id == ids[1]) { + reTransform() + if(killer is Player){ + NSUtils.incrementGhastKC(killer) + rewardXP(killer, Skills.PRAYER, 30.0) + removeAttribute("woke") + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt b/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt new file mode 100644 index 000000000..5edfa55f7 --- /dev/null +++ b/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt @@ -0,0 +1,81 @@ +package content.region.morytania.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.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), //Morytania overworld + ZoneBorders(3520, 9856, 3583, 9919) //Werewolf agility course + ) + } + + override fun areaEnter(entity: Entity) { + 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) + } + } + + private fun kickThemOut(entity: Player) { + val watchdog = NPC(NPCs.ABIDOR_CRANK_3635) + watchdog.isNeverWalks = true + watchdog.isWalks = false + watchdog.location = entity.location + watchdog.init() + entity.lock() + + runTask(watchdog, 1) { + watchdog.moveStep() + watchdog.face(entity) + openDialogue(entity, FuckOffDialogue(), 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, FuckOffDialogue(), watchdog) + return !watchdog.isActive || !entity.isActive + } + }) + } + } + + class FuckOffDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage) { + 0 -> npcl(core.game.dialogue.FacialExpression.WORRIED, "Oh this is no good, you surely will not survive here. Let me take you back.").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(3402, 3485, 0)) + unlock(player!!) + removeAttribute(player!!, "teleporting-away") + } + } + + } + } + } +} + + diff --git a/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt b/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt new file mode 100644 index 000000000..254bbd4a0 --- /dev/null +++ b/Server/src/main/content/region/morytania/handlers/MorytaniaListeners.kt @@ -0,0 +1,125 @@ +package content.region.morytania.handlers + +import core.api.* +import content.global.skill.agility.AgilityHandler +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.rs09.consts.NPCs +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. + * Handles the summoning/altar cave enter and exit in Morytania. + * @author Sir Kermit + */ + +class MorytaniaListeners : InteractionListener { + + val SWAMP_GATES = intArrayOf(Scenery.GATE_3506, Scenery.GATE_3507) + val GROTTO_EXIT = intArrayOf(3525, 3526) + val GROTTO_BRIDGE = 3522 + val outside = Location.create(3439, 3337, 0) + val inside = Location.create(3442, 9734, 1) + + private val swimAnim = Animation(6988) + private val jumpAnim = Animation(1603) + private val failWater = Location(3439,3330) + private val failMessage = "You nearly drown in the disgusting swamp." + private val splashGFX = Graphics(68) + + override fun defineListeners() { + + 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(Quests.NATURE_SPIRIT)) { + setAttribute(player, "swampgate", node) + openInterface(player, Components.CWS_WARNING_20_580) + } else { + sendNPCDialogue( + player, + NPCs.ULIZIUS_1054, + "I'm sorry, but I'm afraid it's too dangerous to let you through this gate right now." + ) + } + } + return@on true + } + + on(GROTTO_EXIT, IntType.SCENERY, "exit"){ player, node -> + player.teleport(outside) + return@on true + } + + on(GROTTO_BRIDGE, IntType.SCENERY, "jump"){ player, node -> + val start = node.location + var failLand = Location(3438,3331) + var failAnim = Animation(770) + var fromGrotto = false + + lock(player,10) + + // Switch to south facing animations if jumping from Grotto + if (start.y == 3331) { + fromGrotto = true + failAnim = Animation(771) + failLand = Location(3438,3328) + } + if (AgilityHandler.hasFailed(player, 1, 0.1)) { + val end = if (fromGrotto) failWater else start + AgilityHandler.forceWalk(player, -1, start, end, failAnim, 15, 0.0, null,0).endAnimation = swimAnim + AgilityHandler.forceWalk(player, -1, failWater, failLand, swimAnim, 15, 2.0, null,3) + submitIndividualPulse(player, object : Pulse(2){ + override fun pulse(): Boolean { + visualize(player,failAnim,splashGFX) + teleport(player,failWater) + // Deal 1-6 damage but wait until the player is back on land + AgilityHandler.fail(player,0,failLand,swimAnim,Random.nextInt(1,7),failMessage) + return true + } + }) + } + else{ + val end = if (fromGrotto) start.transform(0,-3,0) else start.transform(0,3,0) + AgilityHandler.forceWalk(player, -1, start, end, jumpAnim, 15, 15.0, null,0) + } + 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/java/core/game/interaction/object/PyreSitePlugin.java b/Server/src/main/content/region/morytania/handlers/PyreSitePlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/object/PyreSitePlugin.java rename to Server/src/main/content/region/morytania/handlers/PyreSitePlugin.java index d00c330dc..d21b400da 100644 --- a/Server/src/main/java/core/game/interaction/object/PyreSitePlugin.java +++ b/Server/src/main/content/region/morytania/handlers/PyreSitePlugin.java @@ -1,4 +1,4 @@ -package core.game.interaction.object; +package content.region.morytania.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; @@ -10,8 +10,8 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.npc.drop.DropFrequency; import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.firemaking.Log; -import core.game.node.entity.skill.gather.SkillingTool; +import content.global.skill.firemaking.Log; +import content.data.skill.SkillingTool; import core.game.node.item.ChanceItem; import core.game.node.item.Item; import core.game.node.scenery.Scenery; @@ -22,9 +22,10 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import rs09.plugin.PluginManager; +import org.rs09.consts.Items; +import core.game.world.GameWorld; +import core.game.world.repository.Repository; +import core.plugin.ClassScanner; import java.util.ArrayList; import java.util.List; @@ -44,17 +45,17 @@ public final class PyreSitePlugin extends OptionHandler { /** * The chewed bones item. */ - private static final Item CHEWED_BONES = new Item(11338); + private static final Item CHEWED_BONES = new Item(Items.CHEWED_BONES_11338); /** * The mangled bones item. */ - private static final Item MANGLED_BONES = new Item(11337); + private static final Item MANGLED_BONES = new Item(Items.MANGLED_BONES_11337); /** * The dragon full helm item (1/250) chance. */ - private static final Item DFH = new Item(11335); + private static final Item DFH = new Item(Items.DRAGON_FULL_HELM_11335); /** * The list of used pyre ship locations. @@ -64,7 +65,7 @@ public final class PyreSitePlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.forId(25286).getHandlers().put("option:construct", this); - PluginManager.definePlugin(new FerociousBarbarianNPC()); + ClassScanner.definePlugin(new FerociousBarbarianNPC()); return this; } @@ -205,7 +206,7 @@ public final class PyreSitePlugin extends OptionHandler { * @return the random item. */ private Item getRandomItem(Player player) { - if (RandomFunction.random(2) == 1 && RandomFunction.random(250) == 10) { + if (RandomFunction.random(250) == 10) { Repository.sendNews(player.getUsername() + " has just received a Dragon Full Helm from the Pyre Ship."); return DFH; } @@ -391,12 +392,14 @@ public final class PyreSitePlugin extends OptionHandler { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (entity instanceof Player && entity != target) { - ((Player) entity).getPacketDispatch().sendMessage("It's not after you."); + if(message) { + ((Player) entity).getPacketDispatch().sendMessage("It's not after you."); + } return false; } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override 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/java/core/game/content/zone/phasmatys/GhostDiscipleDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/zone/phasmatys/GhostDiscipleDialogue.java rename to Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java index a471e3fcc..40f395cf4 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/GhostDiscipleDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java @@ -1,14 +1,16 @@ -package core.game.content.zone.phasmatys; +package content.region.morytania.phas.dialogue; -import core.game.content.dialogue.DialoguePlugin; +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/java/core/game/content/zone/phasmatys/GhostInkeeperDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostInkeeperDialogue.java similarity index 91% rename from Server/src/main/java/core/game/content/zone/phasmatys/GhostInkeeperDialogue.java rename to Server/src/main/content/region/morytania/phas/dialogue/GhostInkeeperDialogue.java index 4adbbfe04..107cc9800 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/GhostInkeeperDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostInkeeperDialogue.java @@ -1,12 +1,15 @@ -package core.game.content.zone.phasmatys; +package content.region.morytania.phas.dialogue; -import core.game.content.dialogue.DialoguePlugin; +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 { /** @@ -104,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/java/core/game/content/zone/phasmatys/GhostSailorDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostSailorDialogue.java similarity index 85% rename from Server/src/main/java/core/game/content/zone/phasmatys/GhostSailorDialogue.java rename to Server/src/main/content/region/morytania/phas/dialogue/GhostSailorDialogue.java index 033c67148..55a1f6e59 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/GhostSailorDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostSailorDialogue.java @@ -1,15 +1,18 @@ -package core.game.content.zone.phasmatys; +package content.region.morytania.phas.dialogue; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialoguePlugin; +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 { /** @@ -39,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; } @@ -50,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/java/core/game/content/zone/phasmatys/GhostVillagerDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostVillagerDialogue.java similarity index 86% rename from Server/src/main/java/core/game/content/zone/phasmatys/GhostVillagerDialogue.java rename to Server/src/main/content/region/morytania/phas/dialogue/GhostVillagerDialogue.java index b8678aa16..759c66f1b 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/GhostVillagerDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostVillagerDialogue.java @@ -1,13 +1,17 @@ -package core.game.content.zone.phasmatys; +package content.region.morytania.phas.dialogue; -import core.game.content.dialogue.DialoguePlugin; +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 { /** @@ -45,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/java/core/game/content/zone/phasmatys/NecrovarusDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/NecrovarusDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/zone/phasmatys/NecrovarusDialogue.java rename to Server/src/main/content/region/morytania/phas/dialogue/NecrovarusDialogue.java index ad1dd2324..3283cd80c 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/NecrovarusDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/NecrovarusDialogue.java @@ -1,6 +1,6 @@ -package core.game.content.zone.phasmatys; +package content.region.morytania.phas.dialogue; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; /** diff --git a/Server/src/main/java/core/game/content/dialogue/VelorinaDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/VelorinaDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/dialogue/VelorinaDialogue.java rename to Server/src/main/content/region/morytania/phas/dialogue/VelorinaDialogue.java index f0a420acd..2407c17a2 100644 --- a/Server/src/main/java/core/game/content/dialogue/VelorinaDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/VelorinaDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.morytania.phas.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.game.node.item.Item; diff --git a/Server/src/main/content/region/morytania/phas/handlers/BoneGrinderListener.kt b/Server/src/main/content/region/morytania/phas/handlers/BoneGrinderListener.kt new file mode 100644 index 000000000..9f333e5b2 --- /dev/null +++ b/Server/src/main/content/region/morytania/phas/handlers/BoneGrinderListener.kt @@ -0,0 +1,264 @@ +package content.region.morytania.phas.handlers + +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 org.rs09.consts.Scenery +import org.rs09.consts.Sounds + +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" +private const val BONE_BIN_KEY = "/save:bonegrinder-bin" + +private val FILL_ANIM = Animation(1649) +private val WIND_ANIM = Animation(1648) +private val SCOOP_ANIM = Animation(1650) + +class BoneGrinderListener : InteractionListener { + + private val boneIDs = Bones.values().map { it.itemId }.toIntArray() + + override fun defineListeners() { + + /** + * Handle the bone loader/hopper fill option + */ + on(LOADER, IntType.SCENERY, "fill"){ player, _ -> + handleFill(player) + } + + /** + * Handle the wheel's wind option + */ + on(BONE_GRINDER, IntType.SCENERY, "wind"){ player, _ -> + handleWind(player) + } + + /** + * Handle the wheel's status option + */ + on(BONE_GRINDER, IntType.SCENERY, "status"){ player, _ -> + handleStatus(player) + } + + /** + * Handle the bin's empty option + */ + on(BIN, IntType.SCENERY, "empty"){ player, _ -> + handleEmpty(player) + } + + /** + * Handle Bone -> Hopper + */ + onUseWith(IntType.SCENERY, LOADER,*boneIDs){ player, _, _ -> + handleFill(player) + return@onUseWith true + } + + } + + fun handleFill(player: Player): Boolean{ + val bone = getBone(player) + 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)){ + sendMessage(player,"You already have some bones in the hopper.") + return true + } + if(getAttribute(player, BONE_BIN_KEY,false)){ + sendMessage(player,"You already have some bonemeal that needs to be collected.") + return true + } + + val fillPulse = object: Pulse(){ + var stage = 0 + override fun pulse(): Boolean { + when(stage++){ + 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.") + removeItem(player,Item(bone.itemId),Container.INVENTORY) + setAttribute(player, LOADED_BONE_KEY,bone.ordinal) + setAttribute(player, BONE_HOPPER_KEY,true) + return true + } + } + return false + } + } + + if(inInventory(player,bone.itemId)){ + player.pulseManager.run(object : Pulse(){ + var stage = 0 + override fun pulse(): Boolean { + when(stage++){ + 0 -> Pulser.submit(fillPulse).also { delay = FILL_ANIM.duration + 1} + 1 -> { + stopWalk(player) + forceWalk(player,Location(3659,3524),"smart") + delay = 2 + } + 2 -> { + handleWind(player) + delay = WIND_ANIM.duration + 1 + } + 3 -> { + stopWalk(player) + forceWalk(player,Location(3658,3524),"smart") + delay = 2 + } + 4 -> { + if(!inInventory(player,Items.EMPTY_POT_1931,1)){ + return handleEmpty(player) + } else { + handleEmpty(player) + delay = SCOOP_ANIM.duration + 1 + } + } + 5 -> { + stopWalk(player) + forceWalk(player,Location(3660,3524),"smart") + delay = 4 + } + 6 -> { + face(player,Location(3660,3526)) + handleFill(player) + return true + } + } + return false + } + }) + } else { + player.pulseManager.run(fillPulse, PulseType.CUSTOM_1) + } + return true + } + + fun handleWind(player: Player): Boolean{ + if(!getAttribute(player, BONE_HOPPER_KEY,false)){ + sendMessage(player,"You have no bones loaded to grind.") + return true + } + + if(getAttribute(player, BONE_BIN_KEY,false)){ + sendMessage(player,"You already have some bonemeal which you need to collect.") + return true + } + + player.pulseManager.run(object : Pulse(){ + var stage = 0 + override fun pulse(): Boolean { + when(stage++){ + 0 -> { + face(player,Location(3659, 3526, 1)) + 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.") + setAttribute(player, BONE_HOPPER_KEY,false) + setAttribute(player, BONE_BIN_KEY,true) + return true + } + } + return false + } + }, PulseType.CUSTOM_1) + return true + } + + private fun handleStatus(player: Player): Boolean{ + val bonesLoaded = getAttribute(player, BONE_HOPPER_KEY,false) + val boneMealReady = getAttribute(player, BONE_BIN_KEY,false) + + if(bonesLoaded) sendMessage(player,"There are bones waiting in the hopper.") + if(boneMealReady) sendMessage(player,"There is bonemeal waiting in the bin to be collected.") + + if(!bonesLoaded && !boneMealReady){ + sendMessage(player,"There is nothing loaded into the machine.") + } + + return true + } + + fun handleEmpty(player: Player): Boolean { + val inHopper = getAttribute(player, BONE_HOPPER_KEY, false) + val boneType = getAttribute(player, LOADED_BONE_KEY, -1) + val hasMeal = getAttribute(player, BONE_BIN_KEY, false) && boneType != -1 + + if(!hasMeal){ + if (inHopper) + sendMessage(player,"You need to wind the wheel to grind the bones.") + else if (boneType == -1) + sendMessage(player, "You need to load some bones in the hopper first.") + else + sendMessage(player,"You have no bonemeal to collect.") + return true + } + + if(!inInventory(player,Items.EMPTY_POT_1931,1)){ + sendMessage(player,"You don't have any pots to take the bonemeal with.") + return true + } + + val bone = Bones.values()[boneType] + + removeAttributes(player, BONE_HOPPER_KEY, BONE_BIN_KEY, LOADED_BONE_KEY) + lock(player, SCOOP_ANIM.duration + 1) + + player.pulseManager.run(object : Pulse(){ + var stage = 0 + override fun pulse(): Boolean { + when(stage++){ + 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.bonemealId!!) + } + return true + } + } + return false + } + }, PulseType.CUSTOM_1) + return true + } + + fun getBone(player: Player): Bones? { + for(bone in Bones.values()){ + if(inInventory(player,bone.itemId)) return bone + } + return null + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/zone/phasmatys/EctoplasmFillPlugin.java b/Server/src/main/content/region/morytania/phas/handlers/EctoplasmFillPlugin.java similarity index 85% rename from Server/src/main/java/core/game/content/zone/phasmatys/EctoplasmFillPlugin.java rename to Server/src/main/content/region/morytania/phas/handlers/EctoplasmFillPlugin.java index cf4774f40..46000fc0d 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/EctoplasmFillPlugin.java +++ b/Server/src/main/content/region/morytania/phas/handlers/EctoplasmFillPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.zone.phasmatys; +package content.region.morytania.phas.handlers; import core.game.interaction.NodeUsageEvent; import core.game.interaction.UseWithHandler; @@ -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/java/core/game/content/zone/phasmatys/GravingasNPC.java b/Server/src/main/content/region/morytania/phas/handlers/GravingasNPC.java similarity index 94% rename from Server/src/main/java/core/game/content/zone/phasmatys/GravingasNPC.java rename to Server/src/main/content/region/morytania/phas/handlers/GravingasNPC.java index cd9960696..171ce930e 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/GravingasNPC.java +++ b/Server/src/main/content/region/morytania/phas/handlers/GravingasNPC.java @@ -1,11 +1,11 @@ -package core.game.content.zone.phasmatys; +package content.region.morytania.phas.handlers; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; /** @@ -50,7 +50,7 @@ public final class GravingasNPC extends AbstractNPC { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new GravingasDialogue()); + ClassScanner.definePlugin(new GravingasDialogue()); return super.newInstance(arg); } diff --git a/Server/src/main/content/region/morytania/phas/handlers/PhasmatysZone.java b/Server/src/main/content/region/morytania/phas/handlers/PhasmatysZone.java new file mode 100644 index 000000000..70131a4ba --- /dev/null +++ b/Server/src/main/content/region/morytania/phas/handlers/PhasmatysZone.java @@ -0,0 +1,218 @@ +package content.region.morytania.phas.handlers; + +import content.global.skill.agility.AgilityHandler; +import content.global.skill.prayer.Bones; +import content.region.morytania.phas.dialogue.NecrovarusDialogue; +import core.game.global.action.ClimbActionHandler; +import core.game.interaction.Option; +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.entity.skill.Skills; +import core.game.node.item.Item; +import core.game.node.scenery.Scenery; +import core.game.node.scenery.SceneryBuilder; +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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; +import static core.api.ContentAPIKt.sendMessage; + +/** + * Handles the phasmatys zone area. + * + * @author Vexia + */ +@Initializable +public final class PhasmatysZone extends MapZone implements Plugin { + int b; + Bones[] bones; + Player player; + Bones bone; + + /** + * Constructs a new {@code PhasmatysZone} {@code Object}. + */ + public PhasmatysZone() { + super("Port phasmatys", true); + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ZoneBuilder.configure(this); + ClassScanner.definePlugin(new GravingasNPC()); + ClassScanner.definePlugin(new NecrovarusDialogue()); + ClassScanner.definePlugin(new EctoplasmFillPlugin()); + return this; + } + + @Override + public boolean interact(Entity e, Node target, Option option) { + if (e.isPlayer()) { + player = e.asPlayer(); + if (target instanceof NPC) { + NPC npc = (NPC) target; + + if (npc.getId() == NPCs.GHOST_BANKER_1702) { + // Kludge to prevent this module from overriding NPC + // handling until I rip this piece of Vexia shit out. + return false; + } + + if ((npc.getName().toLowerCase().contains("ghost") || npc.getName().equalsIgnoreCase("velorina") || npc.getName().contains("husband")) && !hasAmulet(player)) { + //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; + } + } + switch (target.getId()) { + case 5259: + handleEnergyBarrier(player, (Scenery) target); + return true; + case 5267: + player.animate(Animation.create(536)); + sendMessage(player, "The trapdoor opens..."); + SceneryBuilder.replace((Scenery) target, ((Scenery) target).transform(5268)); + return true; + case 5268: + if (option.getName().equals("Close")) { + player.animate(Animation.create(535)); + sendMessage(player, "You close the trapdoor."); + SceneryBuilder.replace((Scenery) target, ((Scenery) target).transform(5267)); + } else { + sendMessage(player, "You climb down through the trapdoor..."); + player.getProperties().setTeleportLocation(Location.create(3669, 9888, 3)); + } + return true; + case 7434: // Trapdoors in bar + if (option.getName().equalsIgnoreCase("open")) { + SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(7435)); + } + break; + case 7435: // open trapdoor in bar + if (option.getName().equalsIgnoreCase("close")) { + SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(7434)); + } + break; + case 9308: + if (player.getSkills().getStaticLevel(Skills.AGILITY) < 58) { + player.sendMessage("You need an agility level of at least 58 to climb down this wall."); + return true; + } + player.getProperties().setTeleportLocation(Location.create(3671, 9888, 2)); + return true; + case 9307: + if (player.getSkills().getStaticLevel(Skills.AGILITY) < 58) { + player.sendMessage("You need an agility level of at least 58 to climb up this wall."); + return true; + } + player.getProperties().setTeleportLocation(Location.create(3670, 9888, 3)); + return true; + case 5264: + ClimbActionHandler.climb(player, Animation.create(828), Location.create(3654, 3519, 0)); + return true; + case 5282: + worship(player); + return true; + } + } + return super.interact(e, target, option); + } + + /** + * Checks if the player has the amulet equipped. + * + * @param player the player. + * @return {@code True} if so. + */ + public static boolean hasAmulet(Player player) { + return player.getEquipment().contains(552, 1); + } + + /** + * Worships the ectofuntus. + * + * @param player the player. + */ + private void worship(Player player) { + Bones bone = null; + for (Item i : player.getInventory().toArray()) { + if (i == null) { + continue; + } + Bones b = Bones.forBoneMeal(i.getId()); + if (b != null) { + bone = b; + break; + } + } + if (!player.getInventory().contains(4286, 1) && bone == null) { + player.getDialogueInterpreter().sendDialogue("You don't have any ectoplasm or crushed bones to put into the", "Ectofuntus."); + return; + } + if (bone == null) { + player.getDialogueInterpreter().sendDialogue("You need a pot of crushed bones to put into the Ectofuntus."); + return; + } + if (!player.getInventory().contains(4286, 1)) { + player.getDialogueInterpreter().sendDialogue("You need ectoplasm to put into the Ectofuntus."); + return; + } + 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."); + player.getSavedData().getGlobalData().setEctoCharges(player.getSavedData().getGlobalData().getEctoCharges() + 1); + } + } + + /** + * Handles the passing of the energy barrier. + * + * @param player the player. + * @param object the object. + */ + private void handleEnergyBarrier(Player player, Scenery object) { + boolean force = object.getLocation().equals(3659, 3508, 0) && player.getLocation().getY() < 3508 || object.getLocation().equals(3652, 3485, 0) && player.getLocation().getX() > 3652; + if (!force && !player.getInventory().contains(4278, 2)) { + player.getDialogueInterpreter().open(1706); + return; + } + if (force || player.getInventory().remove(new Item(4278, 2))) { + final Direction direction = Direction.getLogicalDirection(player.getLocation(), object.getLocation()); + Location end = player.getLocation().transform(direction, 2); + if (player.getLocation().getY() >= 3508) { + end = object.getLocation().transform(0, -2, 0); + } + if (object.getLocation().equals(new Location(3652, 3485, 0))) { + end = object.getLocation().transform(player.getLocation().getX() >= 3653 ? -1 : 2, 0, 0); + } + AgilityHandler.walk(player, -1, player.getLocation(), end, null, 0.0, null); + } + } + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public void configure() { + register(new ZoneBorders(3583, 3456, 3701, 3552)); + register(new ZoneBorders(3667, 9873, 3699, 9914)); + } + +} diff --git a/Server/src/main/java/core/game/content/zone/phasmatys/TokenCollectOption.java b/Server/src/main/content/region/morytania/phas/handlers/TokenCollectOption.java similarity index 86% rename from Server/src/main/java/core/game/content/zone/phasmatys/TokenCollectOption.java rename to Server/src/main/content/region/morytania/phas/handlers/TokenCollectOption.java index 497f65a2f..608cc5bc5 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/TokenCollectOption.java +++ b/Server/src/main/content/region/morytania/phas/handlers/TokenCollectOption.java @@ -1,4 +1,4 @@ -package core.game.content.zone.phasmatys; +package content.region.morytania.phas.handlers; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; @@ -14,7 +14,7 @@ public class TokenCollectOption extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { if(option.equals("collect")){ - player.getDialogueInterpreter().open(1686,new NPC(1686),true); + player.getDialogueInterpreter().open(1686,node.asNpc() ,true); } return true; } diff --git a/Server/src/main/java/core/game/node/entity/npc/other/akharanu.java b/Server/src/main/content/region/morytania/phas/handlers/akharanu.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/npc/other/akharanu.java rename to Server/src/main/content/region/morytania/phas/handlers/akharanu.java index b838f5ece..798ff0937 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/akharanu.java +++ b/Server/src/main/content/region/morytania/phas/handlers/akharanu.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.other; +package content.region.morytania.phas.handlers; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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; 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/kotlin/rs09/game/content/quest/members/naturespirit/FillimanTarlockNPC.kt b/Server/src/main/content/region/morytania/quest/naturespirit/FillimanTarlockNPC.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/FillimanTarlockNPC.kt rename to Server/src/main/content/region/morytania/quest/naturespirit/FillimanTarlockNPC.kt index 6e6cc1475..3fc36b22a 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/FillimanTarlockNPC.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/FillimanTarlockNPC.kt @@ -1,6 +1,6 @@ -package rs09.game.content.quest.members.naturespirit +package content.region.morytania.quest.naturespirit -import api.ContentAPI +import core.api.* import core.game.node.entity.npc.AbstractNPC import core.game.world.map.Location import core.plugin.Initializable @@ -23,7 +23,7 @@ class FillimanTarlockNPC : AbstractNPC { override fun handleTickActions() { super.handleTickActions() - if(spawnedTicks++ > 100) ContentAPI.poofClear(this) + if(spawnedTicks++ > 100) poofClear(this) } override fun getIds(): IntArray { diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSDrezelDialogue.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt similarity index 81% rename from Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSDrezelDialogue.kt rename to Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt index 26a0872a3..8bfa2330f 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSDrezelDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt @@ -1,22 +1,22 @@ -package rs09.game.content.quest.members.naturespirit +package content.region.morytania.quest.naturespirit -import api.ContentAPI -import core.game.content.dialogue.FacialExpression +import core.api.* import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio import core.game.system.task.Pulse import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import org.rs09.consts.Items -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.stringtools.colorize +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){ @@ -45,19 +45,19 @@ class NSDrezelDialogue : DialogueFile() { 2 -> playerl(FacialExpression.NEUTRAL, "Sorry, I don't think I can help.").also { stage = END_DIALOGUE } } - 20 -> npcl(FacialExpression.NEUTRAL, "That's great! Many Thanks! Now then, please be aware of the Ghasts, you cannot attack them, only Filliman knew how to take them on.").also { stage++ } + 20 -> npcl(FacialExpression.NEUTRAL, "That's great! Many thanks! Now then, please be aware of the Ghasts, you cannot attack them, only Filliman knew how to take them on.").also { stage++ } 21 -> npcl(FacialExpression.NEUTRAL, "Just run from them if you can. If you start to get lost, try to make your way back to the temple.").also { stage++ } 22 -> { - ContentAPI.sendDoubleItemDialogue(player!!, Items.MEAT_PIE_2327, Items.APPLE_PIE_2323, "The cleric hands you some food.") + sendDoubleItemDialogue(player!!, Items.MEAT_PIE_2327, Items.APPLE_PIE_2323, "The cleric hands you some food.") if(questStage == 0){ - repeat(3) { ContentAPI.addItemOrDrop(player!!, Items.MEAT_PIE_2327, 1) } - repeat(3) { ContentAPI.addItemOrDrop(player!!, Items.APPLE_PIE_2323, 1) } - player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, 5) + repeat(3) { addItemOrDrop(player!!, Items.MEAT_PIE_2327, 1) } + repeat(3) { addItemOrDrop(player!!, Items.APPLE_PIE_2323, 1) } + player!!.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player!!, 5) } stage++ } 23 -> npcl(FacialExpression.NEUTRAL, "Please take this food to Filliman, he'll probably appreciate a bit of cooked food. Now, he's never revealed where he lives in the swamps but I guess he'd be to the south, search for him won't you?").also { stage++ } - 24 -> playerl(FacialExpression.FRIENDLY, "I'll do my very best, don't worry, if he's in there and he's still alive I'll definitely find him.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest("Nature Spirit").start(player!!) } + 24 -> playerl(FacialExpression.FRIENDLY, "I'll do my very best, don't worry, if he's in there and he's still alive I'll definitely find him.").also { stage = END_DIALOGUE; player!!.questRepository.getQuest(Quests.NATURE_SPIRIT).start(player!!) } } } @@ -79,14 +79,14 @@ class NSDrezelDialogue : DialogueFile() { 3 -> npcl(FacialExpression.NEUTRAL, "Very well my friend, prepare yourself for the blessings of Saradomin. Here we go!").also { stage++ } 4 -> { end() - player!!.lock(); - ContentAPI.submitIndividualPulse(player!!, BlessingPulse(npc!!, player!!)) + player!!.lock() + submitIndividualPulse(player!!, BlessingPulse(npc!!, player!!)) } } } else if(questStage == 40){ - npcl(FacialExpression.NEUTRAL, "There you go my friend, you're now blessed. It's funny, now I look at you, there seems to be something of the faith about you. Anyway, good luck with your quest!").also { stage = END_DIALOGUE; player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, 45) } + 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 { @@ -105,9 +105,9 @@ private class BlessingPulse(val drezel: NPC, val player: Player) : Pulse(){ override fun pulse(): Boolean { when(ticks){ - 0 -> ContentAPI.animate(drezel, 1162).also { ContentAPI.spawnProjectile(drezel, player, 268); ContentAPI.playAudio(player, Audio(2674)) } - 2 -> ContentAPI.visualize(player, Animation(645), Graphics(267, 100)) - 4 -> ContentAPI.unlock(player).also { player.questRepository.getQuest("Nature Spirit").setStage(player, 40); return true } + 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(Quests.NATURE_SPIRIT).setStage(player, 40); return true } } ticks++ return false @@ -115,6 +115,6 @@ private class BlessingPulse(val drezel: NPC, val player: Player) : Pulse(){ override fun stop() { super.stop() - ContentAPI.openDialogue(player, NSDrezelDialogue(), drezel) + openDialogue(player, NSDrezelDialogue(), drezel) } } \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt new file mode 100644 index 000000000..b580aa4f3 --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt @@ -0,0 +1,261 @@ +package content.region.morytania.quest.naturespirit + +import core.api.Container +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.GroundItem +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.world.map.Location +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.dialogue.DialogueFile +import core.game.global.action.PickupHandler +import core.game.shops.Shops +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import content.region.morytania.handlers.MortMyreGhastNPC +import core.tools.END_DIALOGUE +import core.tools.Log +import content.data.Quests + +class NSListeners : InteractionListener { + + val GROTTO_TREE = 3517 + val GROTTO_ENTRANCE = 3516 + val GROTTO_ALTAR = 3520 + val NATURE_ALTAR = 3521 + val JOURNAL = Items.JOURNAL_2967 + val NATURE_STONE = 3527 + val FAITH_STONE = 3528 + val FREELY_GIVEN_STONE = 3529 + val WASHING_BOWL = Items.WASHING_BOWL_2964 + val MIRROR = Items.MIRROR_2966 + val SPELLCARD = Items.DRUIDIC_SPELL_2968 + val USED_SPELLCARD = Items.A_USED_SPELL_2969 + val FUNGUS = Items.MORT_MYRE_FUNGUS_2970 + val STEM = Items.MORT_MYRE_STEM_2972 + val PEAR = Items.MORT_MYRE_PEAR_2974 + val MIRROR_TAKEN = "/save:ns:mirror_taken" + val GROTTO_SEARCHED = "/save:ns:grotto_searched" + val WISHING_WELL = 28715 + val DRUID_POUCH = Items.DRUID_POUCH_2958 + val DRUID_POUCH_EMPTY = Items.DRUID_POUCH_2957 + val stones = intArrayOf(NATURE_STONE, FAITH_STONE, FREELY_GIVEN_STONE) + val items = intArrayOf(USED_SPELLCARD, FUNGUS) + + override fun defineListeners() { + on(GROTTO_TREE, IntType.SCENERY, "look-at"){ player, _ -> + sendMessage(player, "It looks like a tree on a large rock with roots trailing down to the ground.") + return@on true + } + + on(GROTTO_TREE, IntType.SCENERY, "search"){ player, _ -> + if(!getAttribute(player, GROTTO_SEARCHED, false) || !(inInventory(player, JOURNAL) || inBank(player, JOURNAL))){ + sendItemDialogue(player, JOURNAL, "You search the strange rock. You find a knot and inside of it you discover a small tome. The words on the front are a bit vague, but you can make out the words 'Tarlock' and 'journal'.") + addItemOrDrop(player, JOURNAL, 1) + setAttribute(player, GROTTO_SEARCHED, true) + return@on true + } + return@on false + } + + on(GROTTO_ENTRANCE, IntType.SCENERY, "enter"){ player, node -> + 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() + } else if(questStage < 60) { + player.teleport(Location.create(3442, 9734, 0)) + } else if (questStage >= 60){ + player.teleport(Location.create(3442, 9734, 1)) + } + return@on true + } + + on(GROTTO_ALTAR, IntType.SCENERY, "search"){ player, node -> + val stage = player.questRepository.getStage(Quests.NATURE_SPIRIT) + if(stage == 55){ + openDialogue(player, FillimanCompletionDialogue(), NPC(NPCs.FILLIMAN_TARLOCK_1050)) + return@on true + } + + return@on false + } + + on(NATURE_STONE, IntType.SCENERY, "search"){ player, _ -> + sendDialogue(player, "You search the stone and find that it has some sort of nature symbol scratched into it.") + return@on true + } + + on(FAITH_STONE, IntType.SCENERY, "search"){ player, _ -> + sendDialogue(player, "You search the stone and find that it has some sort of faith symbol scratched into it.") + return@on true + } + + on(FREELY_GIVEN_STONE, IntType.SCENERY, "search"){ player, _ -> + sendDialogue(player, "You search the stone and find it has some sort of spirit symbol scratched into it.") + return@on true + } + + on(WISHING_WELL, IntType.SCENERY, "make-wish"){ player, node -> + 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.") + + return@on true + } + + on(JOURNAL, IntType.ITEM, "read"){ player, _ -> + player.dialogueInterpreter.open(NSJournalDialogue()) + return@on true + } + + on(WASHING_BOWL, IntType.GROUNDITEM, "take"){ player, node -> + log(this::class.java, Log.FINE, "Running listener") + GroundItemManager.create(Item(MIRROR), node.location, player) + PickupHandler.take(player, node as GroundItem) + return@on true + } + + on(MIRROR, IntType.GROUNDITEM, "take"){ player, node -> + if(getAttribute(player, MIRROR_TAKEN, false) && (inInventory(player, MIRROR) || inBank(player, MIRROR))){ + sendDialogue(player, "I don't need another one of these.") + return@on true + } + setAttribute(player, MIRROR_TAKEN, true) + PickupHandler.take(player, node as GroundItem) + return@on true + } + + on(SPELLCARD, IntType.ITEM, "cast"){ player, node -> + if (NSUtils.castBloom(player) && removeItem(player, node.asItem(), Container.INVENTORY)) { + addItem(player, Items.A_USED_SPELL_2969) + } + return@on true + } + + on(intArrayOf(DRUID_POUCH, DRUID_POUCH_EMPTY), IntType.ITEM, "fill"){ player, node -> + + 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) + } + removeItem(player, Item(PEAR, 3), Container.INVENTORY) + addItem(player, Items.DRUID_POUCH_2958, 9 ) + } else if (amountInInventory(player, STEM) >= 3) { + if (node.id != Items.DRUID_POUCH_2958) { + removeItem(player, node, Container.INVENTORY) + } + removeItem(player, Item(STEM, 3), Container.INVENTORY) + addItem(player, Items.DRUID_POUCH_2958, 6) + } else if (amountInInventory(player, FUNGUS) >= 3) { + if (node.id != Items.DRUID_POUCH_2958) { + removeItem(player, node, Container.INVENTORY) + } + removeItem(player, Item(FUNGUS, 3), Container.INVENTORY) + addItem(player, Items.DRUID_POUCH_2958, 3) + } else { + sendDialogue(player, "You need 3 fungus before you can do that.") + } + } else { + sendDialogue(player, "I don't know how to use that yet.") + } + + return@on true + } + + onUseWith(IntType.SCENERY, Items.SILVER_SICKLE_2961, NATURE_ALTAR){ player, used, with -> + sendItemDialogue(player, Items.SILVER_SICKLEB_2963, "You dump the sickle into the waters.") + if(removeItem(player, Items.SILVER_SICKLE_2961, Container.INVENTORY)){ + addItem(player, Items.SILVER_SICKLEB_2963, 1) + } + return@onUseWith true + } + + onUseWith(IntType.NPC, DRUID_POUCH, NPCs.GHAST_1052){ player, used, with -> + 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 -> { + if (with.id == FREELY_GIVEN_STONE) { + if(removeItem(player, used, Container.INVENTORY)){ + sendNPCDialogue(player, NPCs.FILLIMAN_TARLOCK_1050, "Aha, yes, that seems right well done!") + sendMessage(player, "The stone seems to absorb the used spell scroll.") + NSUtils.flagCardPlaced(player) + } + } else sendMessage(player, "You try to put the item on the stone, but it just moves off.") + } + + FUNGUS -> { + if (with.id == NATURE_STONE) { + if(removeItem(player, used, Container.INVENTORY)){ + sendNPCDialogue(player, NPCs.FILLIMAN_TARLOCK_1050, "Aha, yes, that seems right well done!") + sendMessage(player, "The stone seems to absorb the used fungus.") + NSUtils.flagFungusPlaced(player) + } + } else sendMessage(player, "You try to put the item on the stone, but it just moves off.") + } + } + return@onUseWith true + } + } +} + +class NSJournalDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> dialogue(*splitLines("Most of the writing is pretty uninteresting, but something inside refers to a nature spirit. The requirements for which are,")).also { stage++ } + 1 -> dialogue(*splitLines("'Something from nature', 'something with faith' and 'something of the spirit-to-become freely given'. It's all pretty vague.")).also { stage = END_DIALOGUE } + } + } +} + +class FillimanCompletionDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, hello there again. I was just enjoying the grotto. Many thanks for your help, I couldn't have become a Spirit of nature without you.").also { stage++ } + 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I must complete the transformation now. Just stand there and watch the show, apparently it's quite good!").also { stage++ } + 2 -> { + end() + lock(player!!, 10) + submitWorldPulse(CompleteSpellPulse(player!!)) + } + } + } +} + +class CompleteSpellPulse(val player: Player) : Pulse(2){ + var counter = 0 + val locations = arrayOf(Location.create(3444, 9740, 0), Location.create(3439, 9740, 0), Location.create(3439, 9737, 0), Location.create(3444, 9737, 0), Location.create(3444, 9735, 0), Location.create(3438, 9735, 0)) + val dest = Location.create(3441, 9738, 0) + override fun pulse(): Boolean { + when(counter++){ + 0 -> repeat(6) { spawnProjectile(locations[it], dest, 268, 0, 1000, 0, 40, 20) } + 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 } + } + return false + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSTarlockDialogue.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSTarlockDialogue.kt rename to Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt index 31f0b8260..7559ff085 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSTarlockDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSTarlockDialogue.kt @@ -1,20 +1,19 @@ -package rs09.game.content.quest.members.naturespirit +package content.region.morytania.quest.naturespirit -import api.Container -import api.ContentAPI -import api.DialUtils -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +import core.api.Container +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.player.link.quest.Quest import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.update.flag.context.Graphics import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs -import rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE +import content.data.Quests @Initializable class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -26,10 +25,10 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - val quest = player.questRepository.getQuest("Nature Spirit") + val quest = player.questRepository.getQuest(Quests.NATURE_SPIRIT) questStage = quest.getStage(player) - if(questStage > 10 && !ContentAPI.inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ + if(questStage > 10 && !inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ npcl(FacialExpression.HALF_GUILTY, "OooOOOOOOoooOoOOoOOOo") setQuest(15) return false @@ -42,7 +41,7 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { 30 -> npcl(FacialExpression.HALF_GUILTY, "Thanks for the journal, I've been reading it. It looks like I came to a violent and bitter end but that's not really important. I just have to figure out what I am going to do now?").also { stage = 14 } 35 -> npcl(FacialExpression.NEUTRAL, "Hello there, have you been blessed yet?").also { stage = 60 } 45 -> { - if(ContentAPI.inInventory(player, Items.MORT_MYRE_FUNGUS_2970)){ + if(inInventory(player, Items.MORT_MYRE_FUNGUS_2970)){ npcl(FacialExpression.NEUTRAL, "Did you manage to get something from nature?").also { stage = 80 } } else { playerl( @@ -59,7 +58,7 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> if(ContentAPI.inInventory(player, Items.MIRROR_2966)){ + 0 -> if(inInventory(player, Items.MIRROR_2966)){ sendDialogue("You use the mirror on the spirit","of the dead Filliman Tarlock.").also { stage++ } } else { playerl(FacialExpression.NEUTRAL, "Yes, I do think you're dead and I'll prove it somehow.").also { stage = 1002 } @@ -71,9 +70,9 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { 4 -> playerl(FacialExpression.NEUTRAL, "That's because you're dead! Dead as a door nail... Deader in fact... You bear a remarkable resemblance to wormbait! Err... No offence...").also { stage++ } 5 -> npcl(FacialExpression.HALF_GUILTY, "I think you might be right my friend, though I still feel very much alive. It is strange how I still come to be here and yet I've not turned into a Ghast.").also { stage++ } 6 -> npcl(FacialExpression.HALF_GUILTY, " It must be a sign... Yes a sign... I must try to find out what it means. Now, where did I put my journal?").also { stage++ } - 7 -> if(!ContentAPI.inInventory(player, Items.JOURNAL_2967)){ + 7 -> if(!inInventory(player, Items.JOURNAL_2967)){ playerl(FacialExpression.NEUTRAL, "Where did you put it?").also { stage++; setQuest(25) } - } else sendDialogue("You give the journal to Filliman Tarlock").also { ContentAPI.removeItem(player, Items.JOURNAL_2967, Container.INVENTORY); stage = 10; setQuest(30) } + } 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(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 } @@ -107,7 +106,7 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { 52 -> npcl(FacialExpression.HALF_GUILTY, "Well, the book says, that I need, and I quote:- 'Something with faith', 'something from nature' and the 'spirit-to-become' freely given'. Hmm, I know how to get something from nature.").also { stage++ } 53 -> playerl(FacialExpression.NEUTRAL, "Well, that does seem a bit vague.").also { stage++ } 54 -> npcl(FacialExpression.HALF_GUILTY, "Hmm, it does and I could understand if you didn't want to help. However, if you could perhaps at least get the item from nature, that would be a start. Perhaps we can figure out the rest as we go along.").also { stage++ } - 55 -> sendDialogue(*DialUtils.splitLines("The druid produces a small sheet of papyrus with some writing on it.")).also { ContentAPI.addItemOrDrop(player, Items.DRUIDIC_SPELL_2968); setQuest(35); stage++ } + 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(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 } @@ -115,20 +114,20 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { //have you been blessed yet 60 -> playerl(FacialExpression.NEUTRAL, "No, not yet.").also { stage++ } 61 -> npcl(FacialExpression.NEUTRAL, "Well, hurry up!").also { stage++ } - 62 -> if(ContentAPI.inInventory(player, Items.DRUIDIC_SPELL_2968) || ContentAPI.inBank(player, Items.DRUIDIC_SPELL_2968)) end() + 62 -> if(inInventory(player, Items.DRUIDIC_SPELL_2968) || inBank(player, Items.DRUIDIC_SPELL_2968)) end() else playerl(FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } 63 -> npcl(FacialExpression.NEUTRAL, "Sure, but please look after this one.").also { stage++ } - 64 -> sendDialogue("The spirit of Filliman Tarlock gives you another bloom spell.").also { ContentAPI.addItemOrDrop(player, Items.DRUIDIC_SPELL_2968); stage = END_DIALOGUE } + 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(FacialExpression.NEUTRAL, "Well, you need to bring 'something from nature', 'something with faith' and 'something of the spirit-to- become freely given.'").also { stage++ } 71 -> playerl(FacialExpression.NEUTRAL, "Yeah, but what does that mean?").also { stage++ } 72 -> npcl(FacialExpression.NEUTRAL, "Hmm, it is a conundrum, however, if you use that spell I gave you, you should be able to get from nature. Once you have that, we may be puzzle the rest out.").also { stage++ } - 73 -> if(!ContentAPI.inInventory(player, Items.DRUIDIC_SPELL_2968) && !ContentAPI.inBank(player, Items.DRUIDIC_SPELL_2968)){ + 73 -> if(!inInventory(player, Items.DRUIDIC_SPELL_2968) && !inBank(player, Items.DRUIDIC_SPELL_2968)){ playerl(FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } } else end() 74 -> npcl(FacialExpression.NEUTRAL, "Sure, but please look after this one.").also { stage++ } - 75 -> sendDialogue("The spirit of Filliman Tarlock gives you","another bloom spell.").also { ContentAPI.addItem(player, Items.DRUIDIC_SPELL_2968); stage = END_DIALOGUE } + 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++ } @@ -161,22 +160,22 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { 120 -> npcl(FacialExpression.NEUTRAL, "Oh really.. Have you placed all the items on the stones? Ok, well, let's try!").also { stage++ } 121 -> sendDialogue("~ The druid attempts to cast a spell. ~").also { stage++ } 122 -> { - ContentAPI.animate(npc, 812) + animate(npc, 812) if(NSUtils.hasPlacedCard(player) && NSUtils.hasPlacedFungus(player) && NSUtils.onStone(player)){ end() player.lock() val locations = arrayOf(Location.create(3439, 3336, 0), Location.create(3441, 3336, 0), Location.create(3440, 3335, 0)) - repeat(3) {i -> ContentAPI.spawnProjectile(locations[i], Location.create(3440, 3336, 0), 268, 0, 35, 0, 100, 20) } - ContentAPI.submitIndividualPulse(player, object : Pulse(4){ + repeat(3) {i -> spawnProjectile(locations[i], Location.create(3440, 3336, 0), 268, 0, 35, 0, 100, 20) } + submitIndividualPulse(player, object : Pulse(4){ override fun pulse(): Boolean { - ContentAPI.sendNPCDialogue(player, npc.originalId, "Aha, everything seems to be in place! You can come through now into the grotto for the final section of my transformation.") + sendNPCDialogue(player, npc.originalId, "Aha, everything seems to be in place! You can come through now into the grotto for the final section of my transformation.") setQuest(55) - ContentAPI.unlock(player) + unlock(player) return true } override fun stop() { - ContentAPI.visualize(npc, -1, Graphics(266, 80)) + visualize(npc, -1, Graphics(266, 80)) super.stop() } }) @@ -187,17 +186,17 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { stage = END_DIALOGUE } - 130 -> if(ContentAPI.inInventory(player, Items.DRUIDIC_SPELL_2968) || ContentAPI.inBank(player, Items.DRUIDIC_SPELL_2968)){ + 130 -> if(inInventory(player, Items.DRUIDIC_SPELL_2968) || inBank(player, Items.DRUIDIC_SPELL_2968)){ npcl(FacialExpression.NEUTRAL, "No, you've already got one!").also { stage = END_DIALOGUE } } else { npcl(FacialExpression.NEUTRAL, "Sure, but look after this one.") - ContentAPI.addItem(player, Items.DRUIDIC_SPELL_2968) + addItemOrDrop(player, Items.DRUIDIC_SPELL_2968) stage = END_DIALOGUE } //Initial dialogue 1000 -> playerl(FacialExpression.HALF_ASKING, "Hello?").also { stage++ } - 1001 -> if(ContentAPI.inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ + 1001 -> if(inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ npcl(FacialExpression.EXTREMELY_SHOCKED, "Oh, I understand you! At last, someone who doesn't just mumble. I understand what you're saying!").also { stage++ } } else npcl(FacialExpression.HALF_GUILTY, "OooOOoOOoOOOOo.") @@ -235,7 +234,7 @@ class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { } fun setQuest(stage: Int){ - player.questRepository.getQuest("Nature Spirit").setStage(player, stage) + player.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player, stage) } } \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt new file mode 100644 index 000000000..1a59aa8df --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt @@ -0,0 +1,134 @@ +package content.region.morytania.quest.naturespirit + +import core.api.Container +import core.api.* +import core.game.node.scenery.SceneryBuilder +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import core.game.world.map.RegionManager +import core.game.world.map.RegionManager.forId +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) { + setAttribute(player, "/save:ns:fungus_placed", true) + } + + fun flagCardPlaced(player: Player){ + setAttribute(player, "/save:ns:card_placed", true) + } + + fun hasPlacedFungus(player: Player): Boolean { + return getAttribute(player, "ns:fungus_placed", false) + } + + fun hasPlacedCard(player: Player): Boolean { + return getAttribute(player, "ns:card_placed", false) + } + + fun onStone(player: Player): Boolean { + return player.location.equals(3440, 3335, 0) + } + + fun getGhastKC(player: Player): Int { + return getAttribute(player,"ns:ghasts_killed", 0) as Int + } + + fun incrementGhastKC(player: Player){ + setAttribute(player, "/save:ns:ghasts_killed", getGhastKC(player) + 1) + val msg = when(getGhastKC(player)) { + 1 -> "That's one down, two more to go." + 2 -> "Two down, only one more to go." + 3 -> "That's it! I've killed all 3 Ghasts!" + else -> "" + } + + if(!msg.isEmpty()){ + sendMessage(player, msg) + } + } + + fun activatePouch(player: Player, attacker: MortMyreGhastNPC): Boolean { + var shouldAddEmptyPouch = false + val pouchAmt = amountInInventory(player, Items.DRUID_POUCH_2958) + if(pouchAmt == 1) shouldAddEmptyPouch = true + if(pouchAmt > 0 && removeItem(player, Items.DRUID_POUCH_2958, Container.INVENTORY)){ + if(shouldAddEmptyPouch){ + addItemOrDrop(player, Items.DRUID_POUCH_2957) + } + spawnProjectile(player, attacker, 268) + submitWorldPulse(object : Pulse(){ + var ticks = 0 + override fun pulse(): Boolean { + when(ticks++){ + 2 -> visualize(attacker, -1, Graphics(269, 125)) + 3 -> attacker.transform(attacker.id + 1).also { attacker.attack(player); attacker.setAttribute("woke", getWorldTicks()); return true } + } + return false + } + }) + return true + } + return false + } + + fun cleanupAttributes(player: Player){ + player.removeAttribute("ns:fungus_placed") + player.removeAttribute("ns:card_placed") + } + + @JvmStatic + fun castBloom(player: Player): Boolean{ + var success = false + val region = forId(player.location.regionId) + if (player.skills.prayerPoints < 1) { + player.packetDispatch.sendMessage("You don't have enough prayer points to do this.") + return false + } + handleVisuals(player) + val locs = player.location.surroundingTiles + for (o in locs) { + val obj = RegionManager.getObject(o) + if (obj != null) { + if (obj.name.equals("Rotting log", ignoreCase = true) && player.skills.prayerPoints >= 1) { + if (player.location.withinDistance(obj.location, 2)) { + SceneryBuilder.replace(obj, obj.transform(3509)) + success = true + } + } + if (obj.name.equals("Rotting branch", ignoreCase = true) && player.skills.prayerPoints >= 1) { + if (player.location.withinDistance(obj.location, 2)) { + SceneryBuilder.replace(obj, obj.transform(3511)) + success = true + } + } + if (obj.name.equals("A small bush", ignoreCase = true) && player.skills.prayerPoints >= 1) { + if (player.location.withinDistance(obj.location, 2)) { + SceneryBuilder.replace(obj, obj.transform(3513)) + success = true + } + } + } + } + return success + } + + /** + * Handles the draining of prayer points and physical graphics and + * animation. + */ + private fun handleVisuals(player: Player) { + player.skills.decrementPrayerPoints(RandomFunction.random(1, 3).toDouble()) + 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 + // including the tile you are on. + player.packetDispatch.sendGlobalPositionGraphic(263, location) + } + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritDialogue.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritDialogue.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritDialogue.kt rename to Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritDialogue.kt index 052b699d5..bdf92adea 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritDialogue.kt @@ -1,9 +1,9 @@ -package rs09.game.content.quest.members.naturespirit +package content.region.morytania.quest.naturespirit -import api.Container -import api.ContentAPI -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression +import core.api.Container +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.system.task.Pulse @@ -11,13 +11,13 @@ import core.game.world.map.Location import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE +import core.tools.END_DIALOGUE +import content.data.Quests @Initializable class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ - val questStage = player?.questRepository?.getStage("Nature Spirit") ?: 0 + val questStage = player?.questRepository?.getStage(Quests.NATURE_SPIRIT) ?: 0 override fun newInstance(player: Player?): DialoguePlugin { return NatureSpiritDialogue(player) } @@ -57,7 +57,7 @@ class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ 30 -> npcl(FacialExpression.NEUTRAL, "My blessings will entice nature to bloom in Mort Myre! And then with nature's harvest you can fill a druids pouch and release the Ghasts from their torment.").also { stage = 2 } //this dude kinda weird //have you brought me a sickle yet bro - 100 -> if(ContentAPI.inInventory(player, Items.SILVER_SICKLE_2961)){ + 100 -> if(inInventory(player, Items.SILVER_SICKLE_2961)){ playerl(FacialExpression.FRIENDLY, "Yes, here it is. What are you going to do with it?").also { stage = 110 } } else { playerl(FacialExpression.NEUTRAL, "No sorry, not yet!").also { stage++ } @@ -76,11 +76,11 @@ class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ /** * Here we go uoooh */ - 112 -> end().also { ContentAPI.lock(player, 10); ContentAPI.submitWorldPulse(SickleBlessPulse(player, npc)) } + 112 -> end().also { lock(player, 10); submitWorldPulse(SickleBlessPulse(player, npc)) } //go kill some ghasts bro 200 -> npcl(FacialExpression.NEUTRAL, "Go forth into Mort Myre and slay three Ghasts. You'll be releasing their souls from Mort Myre.").also { stage++ } - 201 -> ContentAPI.sendItemDialogue(player, Items.DRUID_POUCH_2957, "The nature spirit gives you an empty pouch.").also { stage++; setQuest(75) } + 201 -> sendItemDialogue(player, Items.DRUID_POUCH_2957, "The nature spirit gives you an empty pouch.").also { stage++; setQuest(75) } 202 -> npcl(FacialExpression.NEUTRAL, "You'll need this in order to collect together nature's bounty. It will bind the Ghast to you until you flee or it is defeated.").also { stage = END_DIALOGUE } //Have you killed the ghasts yet bro @@ -115,7 +115,7 @@ class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ //killed all dem buggers bruv 350 -> npcl(FacialExpression.NEUTRAL, "Many thanks my friend, you have completed your quest!").also { stage++ } - 351 -> end().also { player.questRepository.getQuest("Nature Spirit").finish(player) } + 351 -> end().also { player.questRepository.getQuest(Quests.NATURE_SPIRIT).finish(player) } } return true @@ -135,21 +135,21 @@ class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ override fun pulse(): Boolean { when(ticks++){ - 0 -> ContentAPI.animate(spirit, 812) + 0 -> animate(spirit, 812) 1 -> repeat(4) { val loc = locs.random() locs.remove(loc) - ContentAPI.spawnProjectile(loc, player.location, 268, 0, 400, 0, 125, 180) - ContentAPI.animate(player, 9021) + spawnProjectile(loc, player.location, 268, 0, 400, 0, 125, 180) + animate(player, 9021) } 4 -> { - if(ContentAPI.removeItem(player, Items.SILVER_SICKLE_2961, Container.INVENTORY)){ - ContentAPI.addItem(player, Items.SILVER_SICKLEB_2963) - ContentAPI.unlock(player) - player.questRepository.getQuest("Nature Spirit").setStage(player, 70) - ContentAPI.openDialogue(player, NPCs.NATURE_SPIRIT_1051, ContentAPI.findLocalNPC(player, NPCs.NATURE_SPIRIT_1051) as NPC) - ContentAPI.sendMessage(player, "Your sickle has been blessed! You can bless a new sickle by dipping it into the grotto waters.") + if(removeItem(player, Items.SILVER_SICKLE_2961, Container.INVENTORY)){ + addItem(player, Items.SILVER_SICKLEB_2963) + unlock(player) + 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.") } } 6 -> return true @@ -159,6 +159,6 @@ class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ } fun setQuest(stage: Int){ - player!!.questRepository.getQuest("Nature Spirit").setStage(player!!, stage) + player!!.questRepository.getQuest(Quests.NATURE_SPIRIT).setStage(player!!, stage) } } \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritQuest.kt b/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritQuest.kt new file mode 100644 index 000000000..87d7107b0 --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NatureSpiritQuest.kt @@ -0,0 +1,210 @@ +package content.region.morytania.quest.naturespirit + +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 + +@Initializable +class NatureSpiritQuest : Quest(Quests.NATURE_SPIRIT, 95, 94, 2, 307, 0, 1, 110 ) { + override fun newInstance(`object`: Any?): Quest { + return this + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + player ?: return + var line = 12 + if(stage == 0){ + 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 >= 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 >= 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 >= 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 >= 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 >= 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 >= 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 >= 45) { + // Disappears. + } else if (stage >= 40) { + line(player, "I should return to !!Filliman?? to see what I need to do.", line++) + } + + 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 >= 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 >= 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 >= 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++) + } + // 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++) + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.SILVER_SICKLEB_2963,230,277,5) + drawReward(player, "2 Quest Points", ln++) + drawReward(player, "3,000 Crafting XP",ln++) + drawReward(player, "2,000 Hitpoints XP", ln++) + drawReward(player, "2,000 Defence XP", ln++) + rewardXP(player, Skills.CRAFTING, 3000.0) + rewardXP(player, Skills.HITPOINTS, 2000.0) + rewardXP(player, Skills.DEFENCE, 2000.0) + NSUtils.cleanupAttributes(player) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/ArvelDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/ArvelDialogue.kt new file mode 100644 index 000000000..221c75a57 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/ArvelDialogue.kt @@ -0,0 +1,47 @@ +package content.region.tirranwn.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 + * Not including quest-related dialogue + */ + +@Initializable +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(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(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?): DialoguePlugin { + return ArvelDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ARVEL_2365) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/DalldavDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/DalldavDialogue.kt new file mode 100644 index 000000000..1b89b65c9 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/DalldavDialogue.kt @@ -0,0 +1,48 @@ +package content.region.tirranwn.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 DalldavDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"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?", "Why do you sell this stuff?", "No thanks.").also { stage++ } + 1 -> when (buttonId) { + 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(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?): DialoguePlugin { + return DalldavDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DALLDAV_2356) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/ElfTrackerDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/ElfTrackerDialogue.kt new file mode 100644 index 000000000..d11634cee --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/ElfTrackerDialogue.kt @@ -0,0 +1,89 @@ +package content.region.tirranwn.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.Items +import org.rs09.consts.NPCs + +/** + * @author qmqz + */ + +@Initializable +class ElfTrackerDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + //todo check if found tracks or not + //temporary booly boi + var tracksFound = false + if (!tracksFound) { + player(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } + } else { + 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 + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(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(FacialExpression.HALF_GUILTY, "Well... Err... No.").also { stage = 10 } + } + } + 10 -> npcl(FacialExpression.ANNOYED, "As I was saying... I have no time for brigands or outlaws.").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(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(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(FacialExpression.FRIENDLY, "Getting there.").also { stage = 99 } + } else { + player(FacialExpression.NEUTRAL, "I found it a short distance west of here.").also { stage++ } + } + } + 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?): DialoguePlugin { + return ElfTrackerDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ELF_TRACKER_1199) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/EoinDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/EoinDialogue.kt new file mode 100644 index 000000000..a8bfbe949 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/EoinDialogue.kt @@ -0,0 +1,39 @@ +package content.region.tirranwn.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 EoinDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return EoinDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EOIN_2368) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/EudavGethinDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/EudavGethinDialogue.kt new file mode 100644 index 000000000..1d2c93a3e --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/EudavGethinDialogue.kt @@ -0,0 +1,44 @@ +package content.region.tirranwn.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 EudavGethinDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"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?", "No thanks.").also { stage++ } + + 1 -> when(buttonId) { + 1 -> end().also { npc.openShop(player) } + 2 -> player(FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } + } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return EudavGethinDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EUDAV_2352, NPCs.GETHIN_2357) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/GoreuDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/GoreuDialogue.kt new file mode 100644 index 000000000..3152265e1 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/GoreuDialogue.kt @@ -0,0 +1,42 @@ +package content.region.tirranwn.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 GoreuDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return GoreuDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GOREU_2363) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/IlfeenDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/IlfeenDialogue.kt new file mode 100644 index 000000000..7f5f121d0 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/IlfeenDialogue.kt @@ -0,0 +1,78 @@ +package content.region.tirranwn.dialogue + +import core.api.addItemOrDrop +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 + +/** + * @author qmqz + */ + +@Initializable +class IlfeenDialogue(player: Player? = null) : DialoguePlugin(player){ + + //temp var + var queststage = 0 + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + //todo choose dialogue tree based off of quest stage + when(queststage) { + 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(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(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(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(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(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?): DialoguePlugin { + return IlfeenDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ILFEEN_1777) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/IonaDialouge.kt b/Server/src/main/content/region/tirranwn/dialogue/IonaDialouge.kt new file mode 100644 index 000000000..9ab6bdc25 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/IonaDialouge.kt @@ -0,0 +1,39 @@ +package content.region.tirranwn.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 IonaDialouge(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return IonaDialouge(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.IONA_2369) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/KelynDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/KelynDialogue.kt new file mode 100644 index 000000000..5b19b0be4 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/KelynDialogue.kt @@ -0,0 +1,43 @@ +package content.region.tirranwn.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 KelynDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return KelynDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KELYN_2367) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/MawrthDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/MawrthDialogue.kt new file mode 100644 index 000000000..db5e0f9d3 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/MawrthDialogue.kt @@ -0,0 +1,44 @@ +package content.region.tirranwn.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 MawrthDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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?): DialoguePlugin { + return MawrthDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MAWRTH_2366) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/OronwenDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/OronwenDialogue.kt new file mode 100644 index 000000000..11729482f --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/OronwenDialogue.kt @@ -0,0 +1,46 @@ +package content.region.tirranwn.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 OronwenDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"Hello, can I help?").also { stage = 0 } + 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 -> 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) } + + 99 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return OronwenDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ORONWEN_2353) + } +} diff --git a/Server/src/main/java/core/game/content/dialogue/QuarterMasterDialogue.java b/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java similarity index 78% rename from Server/src/main/java/core/game/content/dialogue/QuarterMasterDialogue.java rename to Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java index 799b40248..b8d7628f1 100644 --- a/Server/src/main/java/core/game/content/dialogue/QuarterMasterDialogue.java +++ b/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java @@ -1,9 +1,13 @@ -package core.game.content.dialogue; +package content.region.tirranwn.dialogue; +import core.game.dialogue.DialoguePlugin; 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 @@ -39,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 new file mode 100644 index 000000000..076a7eb21 --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/TyrasGuard.kt @@ -0,0 +1,57 @@ +package content.region.tirranwn.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 TyrasGuard(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.FRIENDLY,"What is it?").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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(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(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(FacialExpression.ASKING, "Do you know what's south of here?").also { stage = 20 } + 2 -> player(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = 99 } + } + + 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?): DialoguePlugin { + return TyrasGuard(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TYRAS_GUARD_1203) + } +} diff --git a/Server/src/main/content/region/tirranwn/dialogue/TyrasGuardTent.kt b/Server/src/main/content/region/tirranwn/dialogue/TyrasGuardTent.kt new file mode 100644 index 000000000..35f8ab51f --- /dev/null +++ b/Server/src/main/content/region/tirranwn/dialogue/TyrasGuardTent.kt @@ -0,0 +1,39 @@ +package content.region.tirranwn.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 TyrasGuardTent(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + player(FacialExpression.FRIENDLY,"Hello").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 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?): DialoguePlugin { + return TyrasGuardTent(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TYRAS_GUARD_1206) + } +} diff --git a/Server/src/main/content/region/tirranwn/handlers/IsafdarListeners.kt b/Server/src/main/content/region/tirranwn/handlers/IsafdarListeners.kt new file mode 100644 index 000000000..15d837ecb --- /dev/null +++ b/Server/src/main/content/region/tirranwn/handlers/IsafdarListeners.kt @@ -0,0 +1,31 @@ +package content.region.tirranwn.handlers + +import core.game.world.map.Location +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +/** + * File to be used for anything Isafdar related. + * Handles the summoning/altar cave enter and exit in Isafdar. + * @author Sir Kermit + */ + +class IsafdarListeners : InteractionListener { + + val CAVE_ENTRANCE = 4006 + val CAVE_EXIT = 4007 + val outside = Location.create(2312, 3217, 0) + val inside = Location.create(2314, 9624, 0) + + override fun defineListeners() { + on(CAVE_ENTRANCE, IntType.SCENERY, "enter"){ player, node -> + player.teleport(inside) + return@on true + } + + on(CAVE_EXIT, IntType.SCENERY, "exit"){ player, node -> + player.teleport(outside) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/members/rovingelves/ElunedDialogue.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/ElunedDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/members/rovingelves/ElunedDialogue.java rename to Server/src/main/content/region/tirranwn/quest/rovingelves/ElunedDialogue.java index 5f7739d85..67b9ddad7 100644 --- a/Server/src/main/java/core/game/content/quest/members/rovingelves/ElunedDialogue.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/ElunedDialogue.java @@ -1,11 +1,12 @@ -package core.game.content.quest.members.rovingelves; +package content.region.tirranwn.quest.rovingelves; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +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 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/java/core/game/content/quest/members/rovingelves/IsafdarZone.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/IsafdarZone.java similarity index 80% rename from Server/src/main/java/core/game/content/quest/members/rovingelves/IsafdarZone.java rename to Server/src/main/content/region/tirranwn/quest/rovingelves/IsafdarZone.java index 8923f07e2..11c0d1397 100644 --- a/Server/src/main/java/core/game/content/quest/members/rovingelves/IsafdarZone.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/IsafdarZone.java @@ -1,6 +1,6 @@ -package core.game.content.quest.members.rovingelves; +package content.region.tirranwn.quest.rovingelves; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.node.entity.Entity; import core.game.node.entity.combat.ImpactHandler; import core.game.node.entity.player.Player; @@ -56,15 +56,15 @@ public final class IsafdarZone extends MapZone implements Plugin { if (e instanceof Player) { Player player = (Player) e; if(LEAF_TRAPS.contains(player.getLocation())){ - ContentAPI.sendMessage(player,LEAF_FAIL_MSG); - ContentAPI.impact(player,1, ImpactHandler.HitsplatType.NORMAL); + sendMessage(player,LEAF_FAIL_MSG); + impact(player,1, ImpactHandler.HitsplatType.NORMAL); player.teleport(LEAF_TRAP_PIT); } else if(STICK_TRAPS.contains(player.getLocation())) { - ContentAPI.sendMessage(player,STICK_FAIL_MSG); - ContentAPI.impact(player,1, ImpactHandler.HitsplatType.NORMAL); + sendMessage(player,STICK_FAIL_MSG); + impact(player,1, ImpactHandler.HitsplatType.NORMAL); } else if (WIRE_TRAPS.contains(player.getLocation())){ - ContentAPI.sendMessage(player,WIRE_FAIL_MSG); - ContentAPI.impact(player,1, ImpactHandler.HitsplatType.NORMAL); + sendMessage(player,WIRE_FAIL_MSG); + impact(player,1, ImpactHandler.HitsplatType.NORMAL); } } } diff --git a/Server/src/main/java/core/game/content/quest/members/rovingelves/IslwynDialogue.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/rovingelves/IslwynDialogue.java rename to Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java index c87050fd9..2d111e7cd 100644 --- a/Server/src/main/java/core/game/content/quest/members/rovingelves/IslwynDialogue.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/IslwynDialogue.java @@ -1,12 +1,13 @@ -package core.game.content.quest.members.rovingelves; +package content.region.tirranwn.quest.rovingelves; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.DialogueInterpreter; +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.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: @@ -211,8 +212,6 @@ public class IslwynDialogue extends DialoguePlugin { if (!player.getInventory().add(new Item(4222, 1))) { GroundItemManager.create(new Item(4222, 1), player); } - end(); - quest.setStage(player, 100); quest.finish(player); } end(); @@ -222,8 +221,6 @@ public class IslwynDialogue extends DialoguePlugin { if (!player.getInventory().add(new Item(4233, 1))) { GroundItemManager.create(new Item(4233, 1), player); } - end(); - quest.setStage(player, 100); quest.finish(player); } end(); @@ -326,8 +323,8 @@ public class IslwynDialogue extends DialoguePlugin { } if (player.getInventory().contains(995, price)) { if (player.getInventory().remove(new Item(995, price))) { - if (!player.getInventory().add(new Item(4214, 1))) { - GroundItemManager.create(new Item(4214, 1), player); + if (!player.getInventory().add(new Item(4212, 1))) { + GroundItemManager.create(new Item(4212, 1), player); } end(); } @@ -341,8 +338,8 @@ public class IslwynDialogue extends DialoguePlugin { } if (player.getInventory().contains(995, price)) { if (player.getInventory().remove(new Item(995, price))) { - if (!player.getInventory().add(new Item(4225, 1))) { - GroundItemManager.create(new Item(4225, 1), player); + if (!player.getInventory().add(new Item(4224, 1))) { + GroundItemManager.create(new Item(4224, 1), player); } end(); } @@ -359,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/java/core/game/content/quest/members/rovingelves/MossGiantGuardianNPC.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java similarity index 89% rename from Server/src/main/java/core/game/content/quest/members/rovingelves/MossGiantGuardianNPC.java rename to Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java index 63cab6fdf..82685174f 100644 --- a/Server/src/main/java/core/game/content/quest/members/rovingelves/MossGiantGuardianNPC.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/MossGiantGuardianNPC.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.rovingelves; +package content.region.tirranwn.quest.rovingelves; import core.cache.def.impl.NPCDefinition; import core.game.interaction.OptionHandler; @@ -12,7 +12,8 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.world.map.Location; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +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); @@ -57,7 +58,7 @@ public final class MossGiantGuardianNPC extends AbstractNPC { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { diff --git a/Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElves.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java similarity index 94% rename from Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElves.java rename to Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java index 0971d62d1..91d68130e 100644 --- a/Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElves.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElves.java @@ -1,11 +1,12 @@ -package core.game.content.quest.members.rovingelves; +package content.region.tirranwn.quest.rovingelves; 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 core.plugin.Initializable; -import rs09.plugin.PluginManager; +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 @@ -94,11 +95,11 @@ public class RovingElves extends Quest { @Override public Quest newInstance(Object object) { - PluginManager.definePlugin(new RovingElvesPlugin()); - PluginManager.definePlugin(new RovingElvesObstacles()); - PluginManager.definePlugin(new ElunedDialogue()); - PluginManager.definePlugin(new IslwynDialogue()); - PluginManager.definePlugin(new MossGiantGuardianNPC()); + ClassScanner.definePlugin(new RovingElvesPlugin()); + ClassScanner.definePlugin(new RovingElvesObstacles()); + ClassScanner.definePlugin(new ElunedDialogue()); + ClassScanner.definePlugin(new IslwynDialogue()); + ClassScanner.definePlugin(new MossGiantGuardianNPC()); return this; } diff --git a/Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElvesObstacles.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java similarity index 95% rename from Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElvesObstacles.java rename to Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java index 92d572443..144b624c1 100644 --- a/Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElvesObstacles.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java @@ -1,8 +1,8 @@ -package core.game.content.quest.members.rovingelves; +package content.region.tirranwn.quest.rovingelves; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.cache.def.impl.SceneryDefinition; -import core.game.node.entity.skill.agility.AgilityHandler; +import content.global.skill.agility.AgilityHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; @@ -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: @@ -200,7 +205,7 @@ public final class RovingElvesObstacles extends OptionHandler { break; case 3927: player.teleport(LEAF_TRAP_CLIMB); - ContentAPI.sendMessage(player,LEAF_LADDER_MSG); + sendMessage(player,LEAF_LADDER_MSG); break; } diff --git a/Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElvesPlugin.java b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesPlugin.java similarity index 93% rename from Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElvesPlugin.java rename to Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesPlugin.java index c04388c8c..38616e8d4 100644 --- a/Server/src/main/java/core/game/content/quest/members/rovingelves/RovingElvesPlugin.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesPlugin.java @@ -1,4 +1,4 @@ -package core.game.content.quest.members.rovingelves; +package content.region.tirranwn.quest.rovingelves; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; @@ -8,10 +8,11 @@ import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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/java/core/game/content/dialogue/CapeMerchantDialogue.java b/Server/src/main/content/region/wilderness/dialogue/CapeMerchantDialogue.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/CapeMerchantDialogue.java rename to Server/src/main/content/region/wilderness/dialogue/CapeMerchantDialogue.java index 20e772742..ebf5d9593 100644 --- a/Server/src/main/java/core/game/content/dialogue/CapeMerchantDialogue.java +++ b/Server/src/main/content/region/wilderness/dialogue/CapeMerchantDialogue.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package content.region.wilderness.dialogue; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/npc/other/BorkNPC.java b/Server/src/main/content/region/wilderness/handlers/BorkNPC.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/npc/other/BorkNPC.java rename to Server/src/main/content/region/wilderness/handlers/BorkNPC.java index c464ec4c0..53bd862e5 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/BorkNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/BorkNPC.java @@ -1,7 +1,7 @@ -package core.game.node.entity.npc.other; +package content.region.wilderness.handlers; import core.game.component.Component; -import core.game.content.global.BossKillCounter; +import content.data.BossKillCounter; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -14,7 +14,7 @@ import core.game.node.item.ChanceItem; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; import core.game.world.update.flag.context.Animation; @@ -27,13 +27,13 @@ import core.net.packet.out.CameraViewPacket; import core.net.packet.out.MinimapState; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; +import core.game.activity.ActivityPlugin; +import core.game.activity.CutscenePlugin; +import core.game.dialogue.DialogueInterpreter; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; import java.util.ArrayList; @@ -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 @@ -130,7 +130,7 @@ public class BorkNPC extends AbstractNPC { player.lock(); cutscene.wizard.clear(); cutscene.wizard.lock(); - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); + player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); player.getInterfaceManager().open(new Component(693)); if (player.getDialogueInterpreter().getDialogue() != null) { @@ -183,7 +183,7 @@ public class BorkNPC extends AbstractNPC { * Spawns the legion. */ private void spawnLegion() { - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); + player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); spawnedLegion = true; player.lock(); @@ -255,10 +255,10 @@ public class BorkNPC extends AbstractNPC { @Override public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new BorkCutscene()); - PluginManager.definePlugin(new DagonDialogue()); - PluginManager.definePlugin(new OrkLegion()); - PluginManager.definePlugin(new DagonElite()); + ClassScanner.definePlugin(new BorkCutscene()); + ClassScanner.definePlugin(new DagonDialogue()); + ClassScanner.definePlugin(new OrkLegion()); + ClassScanner.definePlugin(new DagonElite()); return super.newInstance(arg); } @@ -373,7 +373,7 @@ public class BorkNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity e, CombatStyle style) { + public boolean isAttackable(Entity e, CombatStyle style, boolean message) { return false; } @@ -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/java/core/game/node/entity/npc/bosses/wilderness/ChaosElementalNPC.java b/Server/src/main/content/region/wilderness/handlers/ChaosElementalNPC.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/npc/bosses/wilderness/ChaosElementalNPC.java rename to Server/src/main/content/region/wilderness/handlers/ChaosElementalNPC.java index 0660e4477..9e4f6f758 100644 --- a/Server/src/main/java/core/game/node/entity/npc/bosses/wilderness/ChaosElementalNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/ChaosElementalNPC.java @@ -1,6 +1,6 @@ -package core.game.node.entity.npc.bosses.wilderness; +package content.region.wilderness.handlers; -import core.game.content.global.BossKillCounter; +import content.data.BossKillCounter; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -10,13 +10,15 @@ import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; 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.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.tools.RandomFunction; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.MultiSwingHandler; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the chaos elemental npc. @@ -62,7 +64,7 @@ public class ChaosElementalNPC extends AbstractNPC { @Override public void sendImpact(BattleState state) { if (state.getEstimatedHit() > 28) { - state.setEstimatedHit(RandomFunction.random(20, 28)); + state.setEstimatedHit(RandomFunction.random(20, 28)); //possibly absolutely mental "haha random" damage adjustment. not sure. - crash } super.sendImpact(state); } @@ -80,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()); } /** @@ -135,16 +137,15 @@ public class ChaosElementalNPC extends AbstractNPC { if (player == null) { return; } - if (attack.getProjectile().getProjectileId() == 554) { - Location loc = getRandomLoc(entity); - while (!RegionManager.isTeleportPermitted(loc) || RegionManager.getObject(loc) != null) { - loc = getRandomLoc(entity); - } - if (loc.equals(player.getLocation())) { - loc = entity.getLocation(); - } + if (attack.getProjectile().getProjectileId() == 557) { + playGlobalAudio(player.getLocation(), Sounds.CHAOS_ELEMENTAL_HIT_351); // C. Elemental Discord Impact SFX + } + else if (attack.getProjectile().getProjectileId() == 554) { + 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) { + 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/java/core/game/content/zone/ChaosTunnelZone.java b/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java similarity index 94% rename from Server/src/main/java/core/game/content/zone/ChaosTunnelZone.java rename to Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java index db69d4241..6d1f06cfc 100644 --- a/Server/src/main/java/core/game/content/zone/ChaosTunnelZone.java +++ b/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java @@ -1,11 +1,11 @@ -package core.game.content.zone; +package content.region.wilderness.handlers; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import core.cache.def.impl.SceneryDefinition; -import core.game.content.activity.ActivityManager; +import core.game.activity.ActivityManager; import core.game.interaction.Option; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -14,8 +14,8 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import org.json.simple.JSONObject; -import rs09.ServerStore; -import rs09.game.world.GameWorld; +import core.ServerStore; +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; @@ -24,9 +24,12 @@ import core.game.world.map.zone.ZoneRestriction; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Handles the chaos tunnels. * @author Vexia @@ -59,7 +62,7 @@ public final class ChaosTunnelZone extends MapZone implements Plugin { @Override public Plugin newInstance(Object arg) throws Throwable { ZoneBuilder.configure(this); - PluginManager.definePlugin(new OptionHandler() { + ClassScanner.definePlugin(new OptionHandler() { @Override public Plugin newInstance(Object arg) throws Throwable { for (int i = 0; i < ENTRANCE_DATA.length; i++) { @@ -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 new file mode 100644 index 000000000..265a60c9b --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/CorpAreaController.kt @@ -0,0 +1,74 @@ +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 +import core.game.world.map.zone.ZoneRestriction +import core.tools.secondsToTicks + +class CorpAreaController : MapArea, TickListener { + companion object { + var activePlayers = ArrayList() + var corpBeast: NPC? = null + var borders = ZoneBorders(2974, 4369, 3007, 4400) + } + + override fun defineAreaBorders(): Array { + return arrayOf(borders) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.GRAVES, ZoneRestriction.RANDOM_EVENTS) + } + + 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 NPC && entity.behavior is CorporealBeastNPC) { + corpBeast = entity + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + activePlayers.remove(entity) + } + } + + override fun tick() { + if (activePlayers.size == 0) { + corpBeast?.let { + it.skills.lifepoints = it.skills.maximumLifepoints + val behavior = it.behavior as CorporealBeastNPC + if (behavior.darkEnergyCore != null) { + behavior.darkEnergyCore.clear() + behavior.darkEnergyCore = null + } + } + } + + if (corpBeast?.isActive == true && activePlayers.isNotEmpty()) { + for (player in activePlayers.toTypedArray()) { + val familiar = player.familiarManager.familiar ?: continue + val timeRemaining = getAttribute(familiar, "corp-time-remaining", -1) + if (timeRemaining == 0 && borders.insideBorder(familiar)) { + val healBy = familiar.skills.lifepoints / 4 + player.familiarManager.dismiss() + corpBeast?.skills?.heal(healBy) + sendMessage(familiar.owner, "The Beast devoured your familiar!") + } + setAttribute(familiar, "corp-time-remaining", timeRemaining - 1) + } + } + } +} diff --git a/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java b/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java new file mode 100644 index 000000000..0b71e76e6 --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java @@ -0,0 +1,302 @@ +package content.region.wilderness.handlers; + +import content.data.BossKillCounter; +import core.game.node.entity.Entity; +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.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.tools.RandomFunction; +import org.rs09.consts.NPCs; + +import java.util.ArrayList; +import java.util.List; + +/** + * Handles the Corporeal beast NPC. + * @author Emperor + */ +@Initializable +public final class CorporealBeastNPC extends NPCBehavior { + + /** + * The combat handler. + */ + private final MultiSwingHandler combatHandler = new CombatHandler(); + + /** + * The dark energy core NPC. + */ + public NPC darkEnergyCore; + + /** + * Whether to force a dark core spawn roll on our next swing (only done if we just got hit >= 32 damage). + */ + public boolean forceCoreRoll = false; + + /** + * Constructs a new {@code CorporealBeastNPC} {@code Object}. + */ + public CorporealBeastNPC() { + super(new int[] { NPCs.CORPOREAL_BEAST_8133 }); + } + + @Override + public void onCreation(NPC self) { + self.configureBossData(); + } + + @Override + public CombatSwingHandler getSwingHandlerOverride(NPC self, CombatSwingHandler original) { + return combatHandler; + } + + @Override + public void beforeDamageReceived(NPC self, Entity attacker, BattleState state) { + if (state.getStyle() == CombatStyle.MELEE || state.getStyle() == CombatStyle.RANGE) { + Weapon w = state.getWeapon(); + String name = w != null ? w.getName() : ""; + if (w == null || name.toLowerCase().indexOf("spear") == -1) { + if (state.getEstimatedHit() > 0) { + state.setEstimatedHit(state.getEstimatedHit() / 2); + } + if (state.getSecondaryHit() > 0) { + state.setSecondaryHit(state.getSecondaryHit() / 2); + } + } + } + if (state.getEstimatedHit() >= 32) { + CorporealBeastNPC corp = (CorporealBeastNPC) self.behavior; + corp.forceCoreRoll = true; + } + if (state.getEstimatedHit() > 100) { + state.setEstimatedHit(100); + } + if (state.getSecondaryHit() > 100) { + state.setSecondaryHit(100); + } + } + + @Override + public void onDeathFinished(NPC self, Entity killer) { + BossKillCounter.addToBossKillCount((Player) killer, NPCs.CORPOREAL_BEAST_8133); + if (darkEnergyCore != null) { + darkEnergyCore.clear(); + darkEnergyCore = null; + } + } + + /** + * Handles the Corporeal beast's combat. + * @author Emperor + */ + static class CombatHandler extends MultiSwingHandler { + + /** + * Constructs a new {@code CombatHandler} {@code Object}. + */ + public CombatHandler() { + super( + //Melee (crush) + new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10057)).setMaximumHit(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) { + // 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. + if (super.getNext().getProjectile() != null && super.getNext().getProjectile().getProjectileId() == 1824) { + setCurrent(getNext()); + CombatStyle style = getCurrent().getStyle(); + setType(style); + int index = RandomFunction.randomize(super.getAttacks().length); + SwitchAttack pick = getNext(entity, victim, state, index); + setNext(pick); + fireLocationBasedAttack(entity, victim.getLocation()); + entity.getProperties().getCombatPulse().setNextAttack(entity.getProperties().getAttackSpeed()); + return -1; + } + return super.swing(entity, victim, state); + } + + /** + * Rolls a 1/8 chance to spawn a dark core. + * @param npc The corporeal beast NPC. + * @param victim The victim. + */ + private void rollDarkCore(Entity corp, final CorporealBeastNPC npc, Entity victim) { + if (npc.darkEnergyCore != null && npc.darkEnergyCore.isActive() && !DeathTask.isDead(npc.darkEnergyCore)) { + return; + } + if (!RandomFunction.roll(8)) { + return; + } + Location l = RegionManager.getTeleportLocation(victim.getLocation(), 3); + npc.darkEnergyCore = NPC.create(8127, l, corp); + npc.darkEnergyCore.setActive(true); + 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; + } + }); + } + + /** + * Fires the location based magic attack. + * @param entity The corporeal beast. + * @param location The location. + */ + private void fireLocationBasedAttack(final Entity entity, final Location location) { + entity.animate(getCurrent().getAnimation()); + Projectile.create(entity, null, 1824, 60, 0, 41, 0).transform(entity, location, true, 46, 10).send(); + int ticks = 1 + (int) Math.ceil(entity.getLocation().getDistance(location) * 0.5); + GameWorld.getPulser().submit(new Pulse(ticks) { + boolean secondStage = false; + List players = RegionManager.getLocalPlayers(entity); + Location[] locations = null; + + @Override + public boolean pulse() { + if (!secondStage) { + for (Player p : players) { + if (p.getLocation().equals(location)) { + hit(p); + } + } + locations = new Location[4 + RandomFunction.random(3)]; + for (int i = 0; i < locations.length; i++) { + locations[i] = location.transform(-2 + RandomFunction.random(5), -2 + RandomFunction.random(5), 0); + Projectile.create(location, locations[i], 1824, 60, 0, 25, 56, 0, 0).send(); + } + setDelay(2); + secondStage = true; + return false; + } + for (int i = 0; i < locations.length; i++) { + Location l = locations[i]; + Graphics.send(Graphics.create(1806), l.transform(-1, -1, 0)); + for (Player p : players) { + if (p.getLocation().equals(l)) { + hit(p); + } + } + } + players.clear(); + players = null; + locations = null; + return true; + } + + private void hit(Player p) { + int hit = 0; + if (isAccurateImpact(entity, p)) { + hit = RandomFunction.random(42); + if (p.hasProtectionPrayer(CombatStyle.MAGIC)) { + hit = (int) (hit * 0.6); + } + } + p.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC); + } + }); + } + + /** + * Attempts to do a stomp attack if needed. + * @param entity The corporeal beast. + * @return {@code True} if a stomp attack is being done. + */ + public boolean doStompAttack(Entity entity) { + Location l = entity.getLocation(); + List stompTargets = null; + for (Player player : RegionManager.getLocalPlayers(entity, 5)) { + Location p = player.getLocation(); + if (p.getX() >= l.getX() && p.getY() >= l.getY() && p.getX() < l.getX() + entity.size() && p.getY() < l.getY() + entity.size()) { + if (stompTargets == null) { + stompTargets = new ArrayList<>(20); + } + stompTargets.add(player); + } + } + if (stompTargets != null) { + entity.visualize(Animation.create(10496), Graphics.create(1834)); + for (Player p : stompTargets) { + p.getImpactHandler().manualHit(entity, RandomFunction.random(52), HitsplatType.NORMAL, 1); + } + return true; + } + return false; + } + + @Override + public void adjustBattleState(Entity entity, Entity victim, BattleState state) { + super.adjustBattleState(entity, victim, state); + if (getCurrent().getProjectile() != null && getCurrent().getProjectile().getProjectileId() == 1823) { + if (state.getEstimatedHit() > 0) { + int random = RandomFunction.random(3); + 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); + ((Player) victim).getPacketDispatch().sendMessage("Your Hitpoints have been slightly drained!"); + } else { + if (skill == Skills.PRAYER) { + victim.getSkills().decrementPrayerPoints(drain); + } else { + victim.getSkills().updateLevel(skill, -drain, 0); + } + if (victim instanceof Player) { + ((Player) victim).getPacketDispatch().sendMessage("Your " + Skills.SKILL_NAME[skill] + " has been slightly drained!"); + } + } + } + } + } + + @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 new file mode 100644 index 000000000..5568d5460 --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/CorporealBeastWarningInterface.kt @@ -0,0 +1,40 @@ +package content.region.wilderness.handlers + +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 + * @author Ceikry + */ +class CorporealBeastWarningInterface : InterfaceListener { + + val COMPONENT_ID = 650 + + 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) + } else { + close(player,component) + } + return@on true + } + + on(COMPONENT_ID){player, component, _, _, _, _ -> + close(player,component) + return@on true + } + } + + fun close(player: Player,component: Component){ + player.interfaceManager.close(component) + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/bosses/corp/DarkEnergyCoreNPC.java b/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/npc/bosses/corp/DarkEnergyCoreNPC.java rename to Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java index 0da933b84..0695955ba 100644 --- a/Server/src/main/java/core/game/node/entity/npc/bosses/corp/DarkEnergyCoreNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.bosses.corp; +package content.region.wilderness.handlers; import core.game.node.entity.Entity; import core.game.node.entity.combat.ImpactHandler.HitsplatType; @@ -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 rs09.game.world.GameWorld; +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 new file mode 100644 index 000000000..75f8a94de --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java @@ -0,0 +1,248 @@ +package content.region.wilderness.handlers; + +import content.data.BossKillCounter; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.InteractionType; +import core.game.node.entity.combat.equipment.ArmourSet; +import core.game.node.entity.combat.equipment.FireType; +import content.global.handlers.item.equipment.special.DragonfireSwingHandler; +import core.game.node.entity.impl.Projectile; +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 + * @version 1.0 + */ +@Initializable +public final class KingBlackDragonNPC extends AbstractNPC { + + /** + * The default spawn location. + */ + private static final Location DEFAULT_SPAWN = Location.create(2273, 4698, 0); + + /** + * The combat swing handler. + */ + private CombatSwingHandler combatHandler = new KBDCombatSwingHandler(); + + + public KingBlackDragonNPC() { + super(-1,null); + } + + @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); + } + + @Override + public void init() { + super.init(); + configureBossData(); + } + + @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[] getIds() { + return new int[] { 50 }; + } + + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + return combatHandler; + } + + @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 { + + /** + * 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}. + */ + public KBDCombatSwingHandler() { + super(CombatStyle.RANGE); + } + + @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 int calculateAccuracy(Entity entity) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateAccuracy(entity); + } + return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); + } + + @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 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 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 ArmourSet getArmourSet(Entity e) { + return style.getSwingHandler().getArmourSet(e); + } + + @Override + public double getSetMultiplier(Entity e, int skillId) { + return style.getSwingHandler().getSetMultiplier(e, skillId); + } + + @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/java/core/game/interaction/object/LavaMazePlugin.java b/Server/src/main/content/region/wilderness/handlers/LavaMazePlugin.java similarity index 92% rename from Server/src/main/java/core/game/interaction/object/LavaMazePlugin.java rename to Server/src/main/content/region/wilderness/handlers/LavaMazePlugin.java index 0282bca1d..4af2240f4 100644 --- a/Server/src/main/java/core/game/interaction/object/LavaMazePlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/LavaMazePlugin.java @@ -1,7 +1,7 @@ -package core.game.interaction.object; +package content.region.wilderness.handlers; import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; +import core.game.global.action.ClimbActionHandler; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/MuddyChestHandler.kt b/Server/src/main/content/region/wilderness/handlers/MuddyChestHandler.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/interaction/object/MuddyChestHandler.kt rename to Server/src/main/content/region/wilderness/handlers/MuddyChestHandler.kt index 28f3e7f09..48000d5f9 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/object/MuddyChestHandler.kt +++ b/Server/src/main/content/region/wilderness/handlers/MuddyChestHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.interaction.`object` +package content.region.wilderness.handlers import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder @@ -6,19 +6,20 @@ import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.game.world.update.flag.context.Animation import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener +import core.game.interaction.InteractionListener +import core.game.interaction.IntType /** * Handles the muddy chest * @author Ceikry */ -class MuddyChestHandler : InteractionListener() { +class MuddyChestHandler : InteractionListener { private val CHEST = 170 override fun defineListeners() { - on(CHEST,SCENERY,"open"){ player, node -> + on(CHEST, IntType.SCENERY, "open"){ player, node -> val key = Item(Items.MUDDY_KEY_991) if(player.inventory.containsItem(key)){ player.inventory.remove(key) diff --git a/Server/src/main/content/region/wilderness/handlers/RoguesCastleListeners.kt b/Server/src/main/content/region/wilderness/handlers/RoguesCastleListeners.kt new file mode 100644 index 000000000..f2e1a50f2 --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/RoguesCastleListeners.kt @@ -0,0 +1,144 @@ +package content.region.wilderness.handlers + +import core.api.* +import core.game.node.scenery.Scenery +import core.game.node.entity.combat.ImpactHandler +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.tools.RandomFunction +import org.rs09.consts.Items +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +import core.game.interaction.InteractionListener +import core.game.interaction.IntType + +class RoguesCastleListeners : InteractionListener { + + val CHEST_ANIM = getAnimation(536) + val FLOOR_1_CHESTS = intArrayOf(14773, 14774) + val FLOOR_2_CHESTS = intArrayOf(38834, 38835) + + override fun defineListeners() { + on(FLOOR_1_CHESTS, IntType.SCENERY, "open"){ player, node -> + val scenery = node.asScenery() + openChest(player, scenery) + return@on true + } + + on(FLOOR_1_CHESTS, IntType.SCENERY, "search"){ player, node -> + val scenery = node.asScenery() + if(getCharge(scenery) == 0){ + sendMessage(player, "This chest has already been looted.") + return@on true + } + + if(freeSlots(player) == 0){ + sendMessage(player, "You don't have enough space to do that.") + return@on true + } + + val item = FLOOR_1_LOOT.roll()[0] + addLoot(player, item) + setCharge(scenery, 0) + return@on true + } + + on(FLOOR_2_CHESTS, IntType.SCENERY, "open"){ player, node -> + sendMessage(player, "This chest appears to be locked.") + return@on true + } + + on(FLOOR_2_CHESTS, IntType.SCENERY, "pick-lock"){ player, node -> + val scenery = node.asScenery() + if(!inInventory(player, Items.LOCKPICK_1523)){ + sendMessage(player, "You need a lockpick in order to attempt this.") + return@on true + } + + if(!hasLevelDyn(player, Skills.THIEVING, 13)){ + sendMessage(player, "You need a Thieving level of 13 to attempt this.") + return@on true + } + + sendMessage(player, "You attempt to pick the lock on the chest...") + submitIndividualPulse(player, object : Pulse(2){ + override fun pulse(): Boolean { + val success = RandomFunction.roll(10) // 1/10 chance to succeed + if(success){ + replaceScenery(scenery, scenery.id + 1, 20) + rewardXP(player, Skills.THIEVING, 300.0) + } else { + val dealsDamage = RandomFunction.roll(10) // 1/10 chance to deal damage on a fail + if(dealsDamage) { + impact(player, RandomFunction.random(1, 3), ImpactHandler.HitsplatType.NORMAL) + sendMessage(player, "You activated a trap on the chest!") + } + } + + sendMessage(player, "You ${if(success) "manage" else "fail"} to pick the lock on the chest.") + return true + } + }) + + return@on true + } + + on(FLOOR_2_CHESTS, IntType.SCENERY, "search"){ player, node -> + val scenery = node.asScenery() + + if(getCharge(scenery) == 0){ + sendMessage(player, "This chest has already been looted.") + return@on true + } + + val loot = FLOOR_2_LOOT.roll()[0] + if(freeSlots(player) > 0){ + addItemOrDrop(player, loot.id, loot.amount) + sendMessage(player, "In the chest you find some ${loot.name.toLowerCase() + if(!loot.name.endsWith("s")) "s" else ""}!") + setCharge(scenery, 0) + rewardXP(player, Skills.THIEVING, 60.0) + } + + return@on true + } + } + + fun openChest(player: Player, scenery: Scenery){ + animate(player, CHEST_ANIM) + submitIndividualPulse(player, object : Pulse(animationDuration(CHEST_ANIM)){ + override fun pulse(): Boolean { + return true.also { replaceScenery(scenery, scenery.id + 1, 20) } + } + }) + } + + fun addLoot(player: Player, item: Item){ + sendMessage(player, "You search the chest...") + submitIndividualPulse(player, object : Pulse(){ + override fun pulse(): Boolean { + sendMessage(player, "... and find some ${item.name.toLowerCase() + if(!item.name.endsWith("s")) "s" else ""}!") + addItemOrDrop(player, item.id, item.amount) + return true + } + }) + } + + val FLOOR_1_LOOT = WeightBasedTable.create( + WeightedItem(Items.COINS_995, 8, 25, 70.0), + WeightedItem(Items.NATURE_RUNE_561, 2, 3, 10.0), + WeightedItem(Items.BLOOD_RUNE_565, 2, 3, 10.0), + WeightedItem(Items.DEATH_RUNE_560, 3, 5, 10.0) + ) + + val FLOOR_2_LOOT = WeightBasedTable.create( + WeightedItem(Items.COINS_995, 4, 57, 75.0), + WeightedItem(Items.COINS_995, 107, 243, 5.0), + WeightedItem(Items.BLOOD_RUNE_565, 2, 5, 5.0), + WeightedItem(Items.GOLD_ORE_445, 1, 1, 5.0), + WeightedItem(Items.STEEL_MED_HELM_1141, 1, 1, 5.0), + WeightedItem(Items.STEEL_PLATELEGS_1069, 1, 1, 5.0) + ) + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/object/wildyditch/WildernessDitchPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java similarity index 84% rename from Server/src/main/java/core/game/interaction/object/wildyditch/WildernessDitchPlugin.java rename to Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java index e31f7604f..20b1c8719 100644 --- a/Server/src/main/java/core/game/interaction/object/wildyditch/WildernessDitchPlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessDitchPlugin.java @@ -1,15 +1,16 @@ -package core.game.interaction.object.wildyditch; +package content.region.wilderness.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; import core.game.interaction.MovementPulse; import core.game.interaction.OptionHandler; import core.game.node.Node; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; -import rs09.plugin.PluginManager; +import core.plugin.ClassScanner; /** * Represents the plugin to handle the crossing. @@ -22,7 +23,7 @@ public final class WildernessDitchPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.forId(23271).getHandlers().put("option:cross", this); - PluginManager.definePlugin(new WildernessInterfacePlugin()); + ClassScanner.definePlugin(new WildernessInterfacePlugin()); return this; } @@ -37,7 +38,7 @@ public final class WildernessDitchPlugin extends OptionHandler { handleDitch(player, node); return true; } - }, "movement"); + }, PulseType.STANDARD); } return true; } @@ -51,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()) { @@ -64,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 new file mode 100644 index 000000000..6500ccfef --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/WildernessInterfacePlugin.java @@ -0,0 +1,111 @@ +package content.region.wilderness.handlers; + +import core.game.component.Component; +import core.game.component.ComponentDefinition; +import core.game.component.ComponentPlugin; +import core.game.global.action.DoorActionHandler; +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.audio.Audio; +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. + * @author Emperor + * @version 1.0 + */ +public final class WildernessInterfacePlugin extends ComponentPlugin { + + /** + * The animation. + */ + private static final Animation ANIMATION = Animation.create(6132); + + @Override + public Plugin newInstance(Object arg) throws Throwable { + ComponentDefinition.put(382, this); + return this; + } + + @Override + public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { + player.getInterfaceManager().close(); + if (button != 18) { + return true; + } + if (player.getAttribute("wildy_ditch") != null) + handleDitch(player); + else if(player.getAttribute("wildy_gate") != null) + handleGate(player); + return true; + } + + /** + * Method used to handle the ditch interface. + * @param player the player. + */ + public static void handleDitch(final Player player) { + Scenery ditch = player.getAttribute("wildy_ditch"); + player.removeAttribute("wildy_ditch"); + Location l = ditch.getLocation(); + int x = player.getLocation().getX(); + int y = player.getLocation().getY(); + if (ditch.getRotation() % 2 == 0) { + if (y <= l.getY()) { + ForceMovement.run(player, Location.create(x, l.getY() - 1, 0), Location.create(x, l.getY() + 2, 0), ANIMATION, 20).setEndAnimation(null); + } else { + ForceMovement.run(player, Location.create(x, l.getY() + 2, 0), Location.create(x, l.getY() - 1, 0), ANIMATION, 20).setEndAnimation(null); + } + } else { + if (x > l.getX()) { + ForceMovement.run(player, Location.create(l.getX() + 2, y, 0), Location.create(l.getX() - 1, y, 0), ANIMATION, 20).setEndAnimation(null); + } else { + ForceMovement.run(player, Location.create(l.getX() - 1, y, 0), Location.create(l.getX() + 2, y, 0), ANIMATION, 20).setEndAnimation(null); + } + } + playAudio(player, Sounds.JUMP2_2462, 30); + /*if (GameWorld.getSettings().isPvp()) { + ActivityManager.register(new BountyHunterActivity(CraterType.MID_LEVEL)); + ActivityManager.register(new BountyHunterActivity(CraterType.HIGH_LEVEL)); + }*/ + } + public static void handleBotDitch(final Player player, Node node) { + Scenery ditch = node.asScenery(); + player.removeAttribute("wildy_ditch"); + Location l = ditch.getLocation(); + int x = player.getLocation().getX(); + int y = player.getLocation().getY(); + if (ditch.getRotation() % 2 == 0) { + if (y <= l.getY()) { + ForceMovement.run(player, Location.create(x, l.getY() - 1, 0), Location.create(x, l.getY() + 2, 0), ANIMATION, 20).setEndAnimation(null); + } else { + ForceMovement.run(player, Location.create(x, l.getY() + 2, 0), Location.create(x, l.getY() - 1, 0), ANIMATION, 20).setEndAnimation(null); + } + } else { + if (x > l.getX()) { + ForceMovement.run(player, Location.create(l.getX() + 2, y, 0), Location.create(l.getX() - 1, y, 0), ANIMATION, 20).setEndAnimation(null); + } else { + ForceMovement.run(player, Location.create(l.getX() - 1, y, 0), Location.create(l.getX() + 2, y, 0), ANIMATION, 20).setEndAnimation(null); + } + } + playAudio(player, Sounds.JUMP2_2462, 30); + /*if (GameWorld.getSettings().isPvp()) { + ActivityManager.register(new BountyHunterActivity(CraterType.MID_LEVEL)); + ActivityManager.register(new BountyHunterActivity(CraterType.HIGH_LEVEL)); + }*/ + } + public static void handleGate(final Player player){ + Scenery gate = player.getAttribute("wildy_gate"); + // Cleanup + player.removeAttribute("wildy_gate"); + // Move player through gate/door + DoorActionHandler.handleAutowalkDoor(player,gate); + } +} diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java new file mode 100644 index 000000000..267a5b8cb --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java @@ -0,0 +1,168 @@ +package content.region.wilderness.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.game.node.entity.player.link.TeleportManager.TeleportType; +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.chunk.GraphicUpdateFlag; +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 + * @author Player Name + * @version 1.2 + */ +@Initializable +public final class WildernessObeliskPlugin extends OptionHandler { + + @Override + public Plugin newInstance(Object arg) throws Throwable { + SceneryDefinition.forId(14829).getHandlers().put("option:activate", this); + SceneryDefinition.forId(14826).getHandlers().put("option:activate", this); + SceneryDefinition.forId(14827).getHandlers().put("option:activate", this); + SceneryDefinition.forId(14828).getHandlers().put("option:activate", this); + SceneryDefinition.forId(14830).getHandlers().put("option:activate", this); + SceneryDefinition.forId(14831).getHandlers().put("option:activate", this); + return this; + } + + @Override + public boolean handle(final Player player, final Node node, String option) { + final Scenery nodeObject = (Scenery) node; + final Obelisk stationObelisk = Obelisk.forLocation(player.getLocation()); + 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(); + int z = stationObelisk.getLocation().getZ(); + switch (i) { + case 0: + x = x + 2; + y = y + 2; + SceneryBuilder.replace(new Scenery(nodeObject.getId(), Location.create(x, y, z)), new Scenery(14825, Location.create(x, y, 0)), 6); + break; + case 1: + x = x - 2; + y = y + 2; + SceneryBuilder.replace(new Scenery(nodeObject.getId(), Location.create(x, y, z)), new Scenery(14825, Location.create(x, y, 0)), 6); + break; + case 2: + x = x - 2; + y = y - 2; + SceneryBuilder.replace(new Scenery(nodeObject.getId(), Location.create(x, y, z)), new Scenery(14825, Location.create(x, y, 0)), 6); + break; + case 3: + x = x + 2; + y = y - 2; + SceneryBuilder.replace(new Scenery(nodeObject.getId(), Location.create(x, y, z)), new Scenery(14825, Location.create(x, y, 0)), 6); + break; + } + } + playAudio(player, Sounds.WILDERNESS_TELEPORT_204); + GameWorld.getPulser().submit(new Pulse(6, player) { + @Override + public boolean pulse() { + final Location center = stationObelisk.getLocation(); + if (getDelay() == 1) { + for (int x = center.getX() - 1; x <= center.getX() + 1; x++) { + for (int y = center.getY() - 1; y <= center.getY() + 1; y++) { + Location l = Location.create(x, y, 0); + RegionManager.getRegionChunk(l).flag(new GraphicUpdateFlag(Graphics.create(342), l)); + } + } + return true; + } + // 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; + } + } + 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.getPacketDispatch().sendMessage("A magical force has stopped you from teleporting."); + } + } + super.setDelay(1); + return false; + } + + }); + return true; + } + + /** + * Represents an obelisk type. + * @author 'Vexia + * @version 1.0 + */ + public enum Obelisk { + LEVEL_13(new Location(3156, 3620, 0)), LEVEL_19(new Location(3219, 3656, 0)), LEVEL_27(new Location(3035, 3732, 0)), LEVEL_35(new Location(3106, 3794, 0)), LEVEL_44(new Location(2980, 3866, 0)), LEVEL_50(new Location(3307, 3916, 0)); + + /** + * Represents the location to teleport to. + */ + private Location location; + + /** + * Constructs a new {@code Obelisk} {@code Object}. + * @param location the location. + */ + Obelisk(Location location) { + this.location = location; + } + + /** + * Gets the location. + * @return the location + */ + public Location getLocation() { + return location; + } + + /** + * Gets the obelisk by location. + * @param location the location. + * @return the obelisk. + */ + public static Obelisk forLocation(Location location) { + for (Obelisk obelisk : Obelisk.values()) + if (obelisk.getLocation().getDistance(location) <= 20) + return obelisk; + return null; + } + } +} diff --git a/Server/src/main/java/core/game/interaction/city/WildernessPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java similarity index 89% rename from Server/src/main/java/core/game/interaction/city/WildernessPlugin.java rename to Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java index fe057d388..26bb09c1a 100644 --- a/Server/src/main/java/core/game/interaction/city/WildernessPlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java @@ -1,17 +1,22 @@ -package core.game.interaction.city; +package content.region.wilderness.handlers; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.content.global.action.ClimbActionHandler; +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 new file mode 100644 index 000000000..0c509175f --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantCombatHandler.java @@ -0,0 +1,108 @@ +package content.region.wilderness.handlers.revenants; + +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.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.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. + * @author Vexia + */ +public class RevenantCombatHandler extends MultiSwingHandler { + + /** + * The magic graphics. + */ + private static final Graphics MAGIC_GRAPHIC = Graphics.create(1276); + + /** + * The range graphics. + */ + private static final Graphics RANGE_GRAPHIC = Graphics.create(1278); + + /** + * Constructs a new {@code RevenantCombatHandler} {@code Object} + */ + public RevenantCombatHandler(Animation meleeAnimation, Animation magicAnimation, Animation rangeAnimation) { + super(true, new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), meleeAnimation), new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), rangeAnimation, createProjectile(RANGE_GRAPHIC)), new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), magicAnimation, createProjectile(MAGIC_GRAPHIC))); + } + + @Override + public void visualizeImpact(Entity entity, Entity victim, BattleState state) { + if (victim instanceof Player) { + SwitchAttack attack = getCurrent(); + if (attack != null) { + if (attack.getStyle() == CombatStyle.RANGE) { + playGlobalAudio(victim.getLocation(), 4061, 0, 1, 10); + } + } + } + super.visualizeImpact(entity, victim, state); + } + + @Override + public void impact(Entity entity, Entity victim, BattleState state) { + if (victim instanceof Player) { + SwitchAttack attack = getCurrent(); + if (attack != null) { + 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 (hasTimerActive(victim, "teleblock")) { + playGlobalAudio(victim.getLocation(), 4064, 0, 1, 10); + } else { + registerTimer (victim, spawnTimer("teleblock", ticks)); + } + } + } + } + if (entity.getId() == 6998) { + applyPoison(victim, entity, 40); + } else if (WildernessZone.getWilderness(entity) >= 50) { + applyPoison(victim, entity, 30); + } + super.impact(entity, victim, state); + } + + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + super.visualize(entity, victim, state); + if (victim.isPlayer()) { + SwitchAttack attack = getCurrent(); + if (attack != null) { + if (attack.getStyle() == CombatStyle.MAGIC) { + playGlobalAudio(entity.getLocation(), Sounds.TELEPORTBLOCK_CAST_202, 0, 1, 10); + } else if (attack.getStyle() == CombatStyle.RANGE) { + playGlobalAudio(entity.getLocation(), 4062, 0, 1, 10); + } + } + } + } + + /** + * Creates a projectile with a graphic id. + * @param graphic the graphic. + * @return the projectile. + */ + public static Projectile createProjectile(Graphics graphic) { + return Projectile.create(null, null, graphic.getId(), 48, 36, 34, 20); + } +} diff --git a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt new file mode 100644 index 000000000..fed2f91ec --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt @@ -0,0 +1,415 @@ +package content.region.wilderness.handlers.revenants + +import core.game.interaction.MovementPulse +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.link.TeleportManager +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import core.ServerConstants +import core.api.* +import core.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 + +class RevenantController : TickListener, Commands { + + companion object { + private val trackedRevenants = ArrayList() + private val taskTimeRemaining = HashMap() + private val currentTask = HashMap() + 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, removeRender: Boolean = true) { + trackedRevenants.remove(revenantNPC) + taskTimeRemaining.remove(revenantNPC) + currentTask.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( + 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, + ), + ) + + // 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() { + taskTimeRemaining.replaceAll { _, t -> t - 1 } + currentTask.entries.forEach { entry -> + if (entry.value == RevenantTask.NONE) { + entry.setValue(assignRandomTask(entry.key)) + } else { + entry.value.execute(entry.key) + } + } + spawnMissingRevenants() + } + + private fun spawnMissingRevenants() { + val amountToSpawn = min(5, expectedRevAmount - trackedRevenants.size) //only spawn 5 at a time + + if (amountToSpawn <= 0) return + + for (i in 0 until amountToSpawn) { + val type = RevenantType.values().random() + val npc = NPC.create(type.ids[0], getRandomSpawnLocation()) + npc.init() + } + } + + private fun getRandomSpawnLocation(): Location { + return spawnLocations.random() + } + + private fun assignRandomTask(npc: RevenantNPC): RevenantTask { + return RevenantTask.values().random().also { it.assign(npc) } + } + + override fun defineCommands() { + 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, description = "Logs the IDs and locations of all tracked revenants.") {player, strings -> + for (rev in trackedRevenants) { + log(this::class.java, Log.FINE, "REV ${rev.id}-${rev.name} @ ${rev.location.toString()}") + } + log(this::class.java, Log.FINE, "Total of ${trackedRevenants.size} revenants spawned.") + } + + define("clearrevs", Privilege.ADMIN, description = "Despawns all revenants.") {_, _ -> + for (rev in trackedRevenants.toTypedArray()) rev.clear() + } + } + + //The current task for any given revenant - execute is called every tick. + enum class RevenantTask { + NONE { + override fun execute(revenantNPC: RevenantNPC) {} + }, + RANDOM_ROAM { + private val MAX_ROAM_TICKS: Int = 250 + + override fun execute(revenantNPC: 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 + } + }) + } + private fun canMoveBasic(revenantNPC: RevenantNPC): Boolean { + return !revenantNPC.walkingQueue.isMoving + && !revenantNPC.pulseManager.hasPulseRunning() + && !revenantNPC.properties.combatPulse.isAttacking + && !revenantNPC.properties.combatPulse.isInCombat + } + + override fun assign(revenantNPC: RevenantNPC) { + taskTimeRemaining[revenantNPC] = RandomFunction.random(MAX_ROAM_TICKS) + } + }, + PATROLLING_ROUTE { + private val MAXIMUM_GROUP_PATROL_LEVEL = 105 + + override fun assign(revenantNPC: RevenantNPC) { + if (canGroup(revenantNPC)) { + addToPatrolGroup(revenantNPC) + } else { + revenantNPC.setAttribute("route", getSuitableRoute(revenantNPC)) + revenantNPC.setAttribute("routeidx", -1) + } + } + + private fun addToPatrolGroup(revenantNPC: RevenantNPC) { + revenantNPC.setAttribute("group", true) + groupPatrolQueue.add(revenantNPC) + + if (groupPatrolQueue.size == 3) { + // 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) + } + groupPatrolQueue.clear() + } + } + + private fun canGroup(revenantNPC: RevenantNPC) = + revenantNPC.properties.currentCombatLevel <= MAXIMUM_GROUP_PATROL_LEVEL && RandomFunction.nextBool() + + override fun execute(revenantNPC: RevenantNPC) { + val isGroup = revenantNPC.getAttribute("group", false) + val route = revenantNPC.getAttribute>("route", null) + val routeIdx = revenantNPC.getAttribute("routeidx", -1) + + 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... + RANDOM_ROAM.execute(revenantNPC) + return + } + + if (routeIdx == -1) { + GameWorld.Pulser.submit(object : Pulse() { + override fun pulse(): Boolean { + Graphics.send(Graphics(86), revenantNPC.location) + return true + } + }) + teleport(revenantNPC, route[0], TeleportManager.TeleportType.INSTANT) + revenantNPC.setAttribute("routeidx", 1) + } else { + if (routeIdx == route.size) { + poofClear(revenantNPC) + revenantNPC.setAttribute("done", true) + return + } + 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, Pathfinder.SMART) { + override fun pulse(): Boolean { + return true + } + }) + revenantNPC.setAttribute("routeidx", routeIdx + 1) + } + } + private fun canMoveAdvanced(revenantNPC: RevenantNPC) : Boolean { + return !revenantNPC.walkingQueue.isMoving + && !revenantNPC.pulseManager.hasPulseRunning() + && !revenantNPC.properties.combatPulse.isAttacking + && !revenantNPC.properties.combatPulse.isInCombat + && revenantNPC.properties.teleportLocation == null + && !revenantNPC.getAttribute("done", false) + } + } + ; + + abstract fun execute(revenantNPC: RevenantNPC) + open fun assign(revenantNPC: RevenantNPC) {} + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantNPC.java b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/npc/revenant/RevenantNPC.java rename to Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java index 2f03bcff0..6e0d0e6fb 100644 --- a/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java @@ -1,4 +1,4 @@ -package core.game.node.entity.npc.revenant; +package content.region.wilderness.handlers.revenants; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -8,22 +8,27 @@ 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.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; +import content.global.skill.summoning.familiar.Familiar; import core.game.world.map.Location; import core.game.world.map.RegionManager; -import core.game.world.map.path.Pathfinder; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.impl.WildernessZone; import core.game.world.update.flag.context.Animation; +import core.plugin.Initializable; import core.tools.RandomFunction; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.system.config.NPCConfigParser; -import rs09.game.world.GameWorld; +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 Vexia + * @author Ceikry-ish (mostly Vexia code still), Player Name */ +@Initializable public class RevenantNPC extends AbstractNPC { /** @@ -74,21 +79,22 @@ public class RevenantNPC extends AbstractNPC { setWalks(true); setRespawn(false); setAggressive(true); + setRenderable(true); setDefaultBehavior(); getAggressiveHandler().setRadius(64 * 2); getAggressiveHandler().setChanceRatio(9); getAggressiveHandler().setAllowTolerance(false); getProperties().setCombatTimeOut(120); - configureRoute(); - super.configure(); configureBonuses(); + super.configure(); this.swingHandler = new RevenantCombatHandler(getProperties().getAttackAnimation(), getProperties().getMagicAnimation(), getProperties().getRangeAnimation()); + setAttribute("food-items", 20); } @Override public void init() { super.init(); - RevenantPlugin.getRevenants().add(this); + RevenantController.registerRevenant(this); int spawnAnim = getDefinition().getConfiguration(NPCConfigParser.SPAWN_ANIMATION, -1); if (spawnAnim != -1) { animate(new Animation(spawnAnim)); @@ -98,38 +104,38 @@ public class RevenantNPC extends AbstractNPC { @Override public void clear() { super.clear(); - RevenantPlugin.getRevenants().remove(this); - RevenantPlugin.spawn(); + 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 handleTickActions() { - 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); + public void tick() { + skills.pulse(); + getWalkingQueue().update(); + if (this.getViewport().getRegion().isActive()) { + getUpdateMasks().prepare(this); } - if (!getLocks().isMovementLocked()) { - if (!getPulseManager().hasPulseRunning() && !getProperties().getCombatPulse().isAttacking() && !getProperties().getCombatPulse().isInCombat() && nextWalk < GameWorld.getTicks()) { - setNextWalk(); - Location l = getMovementDestination(); - if (canMove(l)) { - Pathfinder.find(this, l, true, Pathfinder.SMART).walk(this); + 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; } @@ -190,18 +196,17 @@ public class RevenantNPC extends AbstractNPC { } @Override - public boolean continueAttack(Entity target, CombatStyle style) { - return target instanceof Player ? checkCombatLevel(target.asPlayer()) : true; + public boolean continueAttack(Entity target, CombatStyle style, boolean message) { + return target instanceof Player ? hasAcceptableCombatLevel(target.asPlayer()) : true; } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if (entity.asPlayer().isArtificial()) { - return false; - } + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (entity instanceof Player) { - if (!checkCombatLevel(entity.asPlayer()) && !entity.asPlayer().isAdmin()) { - entity.asPlayer().sendMessage("The level difference between you and your opponent is too great."); + if (!hasAcceptableCombatLevel(entity.asPlayer()) && !entity.asPlayer().isAdmin()) { + if (message) { + entity.asPlayer().sendMessage("The level difference between you and your opponent is too great."); + } return false; } } @@ -210,11 +215,11 @@ public class RevenantNPC extends AbstractNPC { if (owner == null) { return false; } - if (!checkCombatLevel(owner)) { + if (!hasAcceptableCombatLevel(owner)) { return false; } } - return super.isAttackable(entity, style); + return super.isAttackable(entity, style, message); } @Override @@ -222,7 +227,7 @@ public class RevenantNPC extends AbstractNPC { if (!(target instanceof Player)) { return false; } - return checkCombatLevel(target.asPlayer()); + return hasAcceptableCombatLevel(target.asPlayer()); } @Override @@ -256,17 +261,14 @@ public class RevenantNPC extends AbstractNPC { * @param player the player. * @return {@code True} if so. */ - private boolean checkCombatLevel(Player player) { + private boolean hasAcceptableCombatLevel(Player player) { int level = WildernessZone.getWilderness(this); if (player.getSkullManager().getLevel() < level) { level = player.getSkullManager().getLevel(); } int combat = getProperties().getCurrentCombatLevel(); int targetCombat = player.getProperties().getCurrentCombatLevel(); - if (combat - level > targetCombat || combat + level < targetCombat) { - return false; - } - return true; + return Math.abs(combat - targetCombat) <= level; } /** diff --git a/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantType.java b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/npc/revenant/RevenantType.java rename to Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java index 2b3322560..0e7bfb000 100644 --- a/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantType.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java @@ -1,4 +1,9 @@ -package core.game.node.entity.npc.revenant; +package content.region.wilderness.handlers.revenants; + +import core.cache.def.impl.NPCDefinition; + +import java.util.ArrayList; +import java.util.List; /** * A revenant type. @@ -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/kotlin/rs09/GlobalStats.kt b/Server/src/main/core/GlobalStats.kt similarity index 95% rename from Server/src/main/kotlin/rs09/GlobalStats.kt rename to Server/src/main/core/GlobalStats.kt index 8817ae3b3..f055e32ff 100644 --- a/Server/src/main/kotlin/rs09/GlobalStats.kt +++ b/Server/src/main/core/GlobalStats.kt @@ -1,7 +1,7 @@ -package rs09 +package core import org.json.simple.JSONObject -import rs09.ServerStore.getInt +import core.ServerStore.Companion.getInt object GlobalStats { diff --git a/Server/src/main/kotlin/rs09/JSONUtils.kt b/Server/src/main/core/JSONUtils.kt similarity index 99% rename from Server/src/main/kotlin/rs09/JSONUtils.kt rename to Server/src/main/core/JSONUtils.kt index df375170a..bafec26a5 100644 --- a/Server/src/main/kotlin/rs09/JSONUtils.kt +++ b/Server/src/main/core/JSONUtils.kt @@ -1,4 +1,4 @@ -package rs09 +package core import core.game.world.map.Location import java.io.File diff --git a/Server/src/main/core/Server.kt b/Server/src/main/core/Server.kt new file mode 100644 index 000000000..cf46e2823 --- /dev/null +++ b/Server/src/main/core/Server.kt @@ -0,0 +1,212 @@ +package core + +import core.api.log +import core.game.system.SystemManager +import core.game.system.SystemState +import core.game.system.config.ServerConfigParser +import core.game.world.GameWorld +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 + + +/** + * The main class, for those that are unable to read the class' name. + * @author Emperor + * @author Ceikry + */ +object Server { + /** + * The time stamp of when the server started running. + */ + @JvmField + var startTime: Long = 0 + + var lastHeartbeat = System.currentTimeMillis() + + @JvmStatic + var running = false + + /** + * The NIO reactor. + */ + @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. + * @param args The arguments cast on runtime. + * @throws Throwable When an exception occurs. + */ + @Throws(Throwable::class) + @JvmStatic + fun main(args: Array) { + if (args.isNotEmpty()) { + log(this::class.java, Log.INFO, "Using config file: ${args[0]}") + ServerConfigParser.parse(args[0]) + } else { + 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) + 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) { + log(this::class.java, Log.ERR, "Port " + (43594 + GameWorld.settings?.worldId!!) + " is already in use!") + throw e + } + //WorldCommunicator.connect() + 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 + GlobalScope.launch { + while(scanner.hasNextLine()){ + val command = scanner.nextLine() + when(command){ + "stop" -> exitProcess(0) + + "update" -> SystemManager.flag(SystemState.UPDATING) + "help","commands" -> printCommands() + "restartworker" -> SystemManager.flag(SystemState.ACTIVE) + + } + } + } + + if (ServerConstants.WATCHDOG_ENABLED) { + GlobalScope.launch { + delay(20000) + while (running) { + val timeStart = System.currentTimeMillis() + if (!checkConnectivity()) + networkReachability = NetworkReachability.Unreachable + else + networkReachability = NetworkReachability.Reachable + if (System.currentTimeMillis() - lastHeartbeat > 7200 && running) { + log(this::class.java, Log.ERR, "Triggering reboot due to heartbeat timeout") + log(this::class.java, Log.ERR, "Creating thread dump...") + val dump = threadDump(true, true) + + withContext(Dispatchers.IO) { + FileWriter("latestdump.txt").use { + + if (dump != null) { + it.write(dump) + } + + it.flush() + it.close() + } + } + + if (!SystemManager.isTerminated()) + exitProcess(0) + } + 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() + } + + fun printCommands(){ + println("stop - stop the server (saves all accounts and such)") + println("players - show online player count") + println("update - initiate an update with a countdown visible to players") + println("help, commands - show this") + println("restartworker - Reboots the major update worker in case of a travesty.") + } + + fun autoReconnect() { + /*SystemLogger.log("Attempting autoreconnect of server") + WorldCommunicator.connect()*/ + } + /** + * Gets the startTime. + * @return the startTime + */ + fun getStartTime(): Long { + return startTime + } + + private fun threadDump(lockedMonitors: Boolean, lockedSynchronizers: Boolean): String? { + val threadDump = StringBuffer(System.lineSeparator()) + val threadMXBean: ThreadMXBean = ManagementFactory.getThreadMXBean() + for (threadInfo in threadMXBean.dumpAllThreads(lockedMonitors, lockedSynchronizers)) { + threadDump.append(threadInfo.toString()) + } + return threadDump.toString() + } + + /** + * Sets the bastartTime.ZZ + * @param startTime the startTime to set. + */ + fun setStartTime(startTime: Long) { + Server.startTime = startTime + } +} diff --git a/Server/src/main/core/ServerConstants.kt b/Server/src/main/core/ServerConstants.kt new file mode 100644 index 000000000..8c5f493a4 --- /dev/null +++ b/Server/src/main/core/ServerConstants.kt @@ -0,0 +1,393 @@ +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 + + @JvmField + var CURRENT_SAVEFILE_VERSION = 4 + + @JvmField + var DAILY_ACCOUNT_LIMIT = 3 + + @JvmField + var REVENANT_POPULATION: Int = 30 + + @JvmField + var BOTS_INFLUENCE_PRICE_INDEX = true + + @JvmField + var SHUTDOWN_HOOK: Thread = Thread(SystemShutdownHook()) + + @JvmField + var ALLOW_GUI: Boolean = false + + @JvmField + var DATA_PATH: String? = null + + //path to the cache + @JvmField + var CACHE_PATH: String? = null + + //path for the server store + @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 + + @JvmField + var CELEDT_DATA_PATH: String? = null + + @JvmField + var USDT_DATA_PATH: String? = null + + @JvmField + var HDT_DATA_PATH: String? = null + + @JvmField + var GDT_DATA_PATH: String? = null + + @JvmField + var RSDT_DATA_PATH: String? = null + + @JvmField + var ASDT_DATA_PATH: String? = null + + @JvmField + var GRAFANA_PATH: String? = null + + //the max number of players. + @JvmField + var MAX_PLAYERS = 2000 + + //the max number of NPCs + @JvmField + var MAX_NPCS = 32000 + + //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 BANK_BOOTH_QUICK_OPEN: Boolean = false + + @JvmField + var BANK_BOOTH_NOTE_ENABLED: Boolean = true + + @JvmField + var BANK_BOOTH_NOTE_UIM: Boolean = true + + @JvmField + var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour + + @JvmField + var GE_AUTOSTOCK_ENABLED = false + + @JvmField + var MS_SECRET_KEY = "" + + @JvmField + var LOG_CUTSCENE = true + + @JvmField + var RULES_AND_INFO_ENABLED = false + + @JvmField + var WATCHDOG_ENABLED = true + + @JvmField + var I_AM_A_CHEATER = false + + @JvmField + var LOG_LEVEL = LogLevel.VERBOSE + + @JvmField + var JAVA_VERSION = 8 + + @JvmField + var GRAFANA_LOGGING = true + + @JvmField + var GRAFANA_TTL_DAYS = 7 + + //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 DATABASE: Database? = null + + //if SQL is enabled + @JvmField + var MYSQL = true + + //the server name + @JvmField + var SERVER_NAME: String = "" + + //the server's grand exchange name + @JvmField + var SERVER_GE_NAME: String = "" + + //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 new file mode 100644 index 000000000..52df7fb52 --- /dev/null +++ b/Server/src/main/core/ServerStore.kt @@ -0,0 +1,216 @@ +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 +import core.tools.SystemLogger +import core.tools.SystemLogger.logShutdown +import core.tools.SystemLogger.logStartup +import java.io.File +import java.io.FileReader +import java.io.FileWriter +import javax.script.ScriptEngineManager + +class ServerStore : PersistWorld { + override fun parse() { + logStartup("Parsing server store...") + val dir = File(ServerConstants.STORE_PATH!!) + if(!dir.exists()){ + dir.mkdirs() + return + } + + var parser: JSONParser + var reader: FileReader + + dir.listFiles()?.forEach { storeFile -> + val key = storeFile.nameWithoutExtension + + reader = FileReader(storeFile) + parser = JSONParser() + + try { + val data = parser.parse(reader) as JSONObject + fileMap[key] = data + counter++ + } catch (e: Exception){ + log(this::class.java, Log.ERR, "Failed parsing ${storeFile.name} - stack trace below.") + e.printStackTrace() + return@forEach + } + } + + logStartup("Initialized $counter store files.") + } + + override fun save() { + logShutdown("Saving server store...") + val dir = File(ServerConstants.DATA_PATH + File.separator + "serverstore") + if(!dir.exists()){ + dir.mkdirs() + return + } + + val manager = ScriptEngineManager() + val scriptEngine = manager.getEngineByName("JavaScript") + + fileMap.forEach { (name, data) -> + val path = dir.absolutePath + File.separator + name + ".json" + + scriptEngine.put("jsonString", data.toJSONString()) + scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") + val prettyPrintedJson = scriptEngine["result"] as String + + FileWriter(path).use { it.write(prettyPrintedJson); it.flush(); it.close() } + } + } + + companion object { + val fileMap = HashMap() + var counter = 0 + + @JvmStatic + fun getArchive(name: String): JSONObject { + if(fileMap[name] == null){ + fileMap[name] = JSONObject() + } + + return fileMap[name]!! + } + + fun setArchive(name: String, data: JSONObject){ + fileMap[name] = data + } + + fun clearDailyEntries() { + fileMap.keys.toTypedArray().forEach { + if(it.toLowerCase().contains("daily")) fileMap[it]?.clear() + } + } + + fun clearWeeklyEntries() { + fileMap.keys.toTypedArray().forEach { + if(it.toLowerCase().contains("weekly")) fileMap[it]?.clear() + } + } + + @JvmStatic + 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 -> default + else -> default + } + } + + @JvmStatic + fun JSONObject.getString(key: String, default: String = "nothing"): String { + return this[key] as? String ?: default + } + + @JvmStatic + fun JSONObject.getLong(key: String, default: Long = 0L): Long { + return this[key] as? Long ?: default + } + + @JvmStatic + fun JSONObject.getBoolean(key: String, default: Boolean = false): Boolean { + return this[key] as? Boolean ?: default + } + + fun List.toJSONArray(): JSONArray{ + val jArray = JSONArray() + for(i in this){ + jArray.add(i) + } + return jArray + } + + inline fun JSONObject.getList(key: String) : List { + val array = this[key] as? JSONArray ?: JSONArray() + val list = ArrayList() + for(element in array) list.add(element as T) + return list + } + + fun JSONObject.addToList(key: String, value: Any) { + val array = this.getOrPut(key) {JSONArray()} as JSONArray + array.add(value) + } + + /** NPCItemMemory + * These next methods handle the NPCItemMemory database. these are server-stored JSON objects, + * which are based on an npc and an item. + * each NPC can have a JSON object for itself for any item. + * the NPC Item object can store integer values for individual players, using their names as keys. + * the first methods handle the serverstore filename and accessing the JSON object. + * these methods are wrapped by more convenient ones that allow access for a particular player, see below. + */ + fun NPCItemFilename(npc: Int, item: Int, period: String = "daily"): String { + val itemName = getItemName(item).lowercase().replace(" ","-") + val npcName = NPC(npc).name.lowercase() + return "$period-$npcName-$itemName" + } + fun NPCItemMemory(npc: Int, item: Int, period: String = "daily"): JSONObject { + return getArchive(NPCItemFilename(npc,item,period)) + } + + /** NPCItemMemory Player Access + * These next functions handle individual player access to the NPC Item Memory database. + * each of the functions has at least 3 mandatory arguments: npc, item, player. + * These functions can be used to allow players to access a daily- or weekly-reset stock of a particular item overseen by a particular NPC. + * + * note that none of these functions perform tangible actions on the player. + * whoever calls these functions is responsible for handling tangible actions like putting items in an inventory. + * these functions simply return and update the NPCItemMemory database numbers in useful ways. + */ + + /** getNPCItemStock: + * gets the available stock of a particular item at a particular NPC for a particular player. + */ + fun getNPCItemStock(npc: Int, item: Int, limit: Int, player: Player, period: String = "daily"): Int { + val itemMemory = NPCItemMemory(npc,item) + val key = player.name + var stock = limit-itemMemory.getInt(key) + stock = maxOf(stock,0) + return stock + } + /** getNPCItemAmount: + * gets the usable amount of a particular item from a particular npc for a particular player. + * this is essentially just a requested amount cut to conform to stock and nonnegative requirements. + * what this function does is it takes a requested amount, cuts it down to be equal to available stock if + * available stock is less than the requested amount, and then additionally sets a minimum of 0. + */ + fun getNPCItemAmount(npc: Int, item: Int, limit: Int, player: Player, amount: Int, period: String = "daily"): Int { + val stock = getNPCItemStock(npc,item,limit,player,period) + var realamount = minOf(amount,stock) + realamount = maxOf(realamount,0) + return realamount + } + /** addNPCItemAmount: + * this function updates the NPC Item Memory database entry for a particular player. + * it is intended to be called after all the actions that use the number have been successfully completed. + * this function will *add* the amount argument to the current npc item memory entry for that player. this means that + * the amount argument is the amount to add, not the absolute final amount. + * this function will handle correctly setting the limit and any other required semantics. + */ + fun addNPCItemAmount(npc: Int, item: Int, limit: Int, player: Player, amount: Int, period: String = "daily") { + val itemMemory = NPCItemMemory(npc,item,period) + val key = player.name + var realamount = itemMemory.getInt(key) + amount + realamount = minOf(realamount,limit) + realamount = maxOf(realamount,0) + itemMemory[key] = realamount + } + } +} + diff --git a/Server/src/main/java/core/Util.java b/Server/src/main/core/Util.java similarity index 92% rename from Server/src/main/java/core/Util.java rename to Server/src/main/core/Util.java index 861df7c69..5bb17aaac 100644 --- a/Server/src/main/java/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 new file mode 100644 index 000000000..fc8efda5a --- /dev/null +++ b/Server/src/main/core/api/ApiExtensions.kt @@ -0,0 +1,66 @@ +package core.api + +import core.game.node.item.Item +import java.util.* +import kotlin.collections.ArrayList + +fun IntProgression.toIntArray(): IntArray { + val result = IntArray((last - first) / step + 1) + forEachIndexed { index, i -> result[index] = i } + return result +} + +fun Int.asItem() : Item { + return Item(this) +} + +fun Collection.toIntArray() : IntArray { + val list = ArrayList() + this.forEach { arr -> arr.forEach { list.add(it) } } + return list.toIntArray() +} + +inline fun Collection.isLast(element: T) : Boolean { + return this.indexOf(element) == this.size - 1 +} + +inline fun Collection.getNext(element: T) : T { + val idx = this.indexOf(element) + return if (idx < this.size - 1) + this.elementAt(idx + 1) + else + element +} + +inline fun Collection.isNextLast(element: T) : Boolean { + return this.isLast(this.getNext(element)) +} + +fun IntArray.isLast(element: Int) : Boolean { + return this.indexOf(element) == this.size - 1 +} + +fun IntArray.getNext(element: Int) : Int { + val idx = this.indexOf(element) + return if (idx < this.size - 1) + this.elementAt(idx + 1) + else + element +} + +fun IntArray.isNextLast(element: Int) : Boolean { + return this.isLast(this.getNext(element)) +} + +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 new file mode 100644 index 000000000..231adc129 --- /dev/null +++ b/Server/src/main/core/api/Commands.kt @@ -0,0 +1,52 @@ +package core.api + +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 + +/** + * An interface for writing content that allows the class to define commands. + * + * Includes methods [reject] and [notify] for notifying the player of a command rejection or some output text respectively. + * + * Includes the method [define] to define individual commands + * + * Commands should ideally be defined in the required [defineCommands] method. + */ +interface Commands : ContentInterface { + /** + * Glorified player.sendMessage with red text coloring. Please use this + * rather than just player.sendMessage for anything that involves informing the player + * of proper usage or invalid syntax. Throws an IllegalStateException and ends command execution immediately. + */ + fun reject(player: Player, vararg message: String){ + for(msg in message) { + player.sendMessage(colorize("-->%R$msg")) + } + throw IllegalStateException() + } + + /** + * 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, logToConsole: Boolean = false){ + if (logToConsole) + log (this::class.java, Log.DEBUG, message) + player.sendMessage(colorize("-->$message")) + } + + /** + * Used to define commands. define("name",(optional) privilege override){ handle } + * @param name the name of the command. Example: ::example would be just "example" + * @param privilege the rights level needed to execute the command. Options are [Privilege.STANDARD], [Privilege.MODERATOR], [Privilege.ADMIN]. Defaults to [Privilege.STANDARD] + */ + fun define(name: String, privilege: Privilege = Privilege.ADMIN, usage: String = "", description: String = "", handle: (Player, Array) -> Unit){ + CommandMapping.register(Command(name, privilege, usage, description, handle)) + } + + fun defineCommands() +} diff --git a/Server/src/main/core/api/Container.kt b/Server/src/main/core/api/Container.kt new file mode 100644 index 000000000..bdcfd4900 --- /dev/null +++ b/Server/src/main/core/api/Container.kt @@ -0,0 +1,8 @@ +package core.api + +enum class Container { + INVENTORY, + BANK, + 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 new file mode 100644 index 000000000..7423518a7 --- /dev/null +++ b/Server/src/main/core/api/ContentAPI.kt @@ -0,0 +1,3388 @@ +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 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 +import core.game.world.map.RegionManager.getRegionChunk +import core.game.world.map.path.Pathfinder +import core.game.world.map.zone.MapZone +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneBuilder +import core.game.world.repository.Repository +import core.game.world.update.flag.* +import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag +import core.game.world.update.flag.context.* +import core.net.packet.PacketRepository +import core.net.packet.context.DefaultContext +import core.net.packet.context.MusicContext +import core.net.packet.out.AudioPacket +import core.net.packet.out.MusicPacket +import core.tools.* +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import 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. + * @param player the player to get the tool for + * @param pickaxe whether or not we are trying to get a pickaxe. + * @return the tool which meets the requirements or null if none. + */ +fun getTool(player: Player, pickaxe: Boolean): SkillingTool? { + return if (pickaxe) SkillingTool.getPickaxe(player) else SkillingTool.getHatchet(player) +} + +/** + * Check if a player has a given level (buffable/debuffable) + * @param player the player to check the stat for + * @param skill the Skill to check. There is an enum for this called Skills. Example: Skills.STRENGTH + * @param level the level to check against + * @return true if the check succeeds, false otherwise + */ +fun hasLevelDyn(player: Player, skill: Int, level: Int): Boolean { + return player.skills.getLevel(skill) >= level +} + +/** + * Check if a player's static skill level meets a certain value + * @param player the player to check the stat for + * @param skill the Skill to check. There is an enum for this called Skills. Example: Skills.STRENGTH + * @param level the level to check against + * @return true if the check succeeds, false otherwise + */ +fun hasLevelStat(player: Player, skill: Int, level: Int): Boolean { + return player.skills.getStaticLevel(skill) >= level +} + +/** + * Check the amount of a given item in the player's inventory + * @param player the player whose inventory to check + * @param id the ID of the item to check for the amount of + * @return the amount of the given ID in the player's inventory + */ +fun amountInInventory(player: Player, id: Int): Int { + return player.inventory.getAmount(id) +} + +/** + * 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 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 +} + +/** + * Check the amount of a given item in the player's equipment slots + * @param player the player to check + * @param id the ID of the item to check for + * @return the amount of the ID in the player's equipment. + */ +fun amountInEquipment(player: Player, id: Int): Int { + 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 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 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 allInEquipment(player: Player, vararg ids: Int): Boolean { + return ids.all { id -> + inEquipment(player, id) + } +} + +/** + * Check that at least one item from a set of items is equipped by the given player + * @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 equipped, false if none are equipped + */ +fun anyInEquipment(player: Player, vararg ids: Int): Boolean { + return ids.any { id -> + 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 + } + fun exists() : Boolean { + return this.container != null && this.itemId > -1 + } +} + +/** + * 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, 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 (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) +} + +/** + * Check if a player has an item equipped which corresponds to the given God + * @param player the player to check + * @param god the God whose equipment we are checking for + * @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 (inEquipment(player, it)) return true } + return false +} + +/** + * Check if the given player should have the "remove nothing" RoW effect active. + * @param player the player we are checking + * @return whether we should ignore nothings + */ +fun shouldRemoveNothings(player: Player) : Boolean { + val ring = getItemFromEquipment(player, EquipmentSlot.RING) + return ring != null && ring.id in Items.RING_OF_WEALTH_14638..Items.RING_OF_WEALTH4_14646 +} + +/** + * Remove an item from a player's inventory + * @param player the player whose inventory to remove the item from + * @param item the ID or Item object to remove from the player's inventory + * @param container the Container to remove the items from. An enum exists for this in the api package called Container. Ex: api.Container.BANK + */ +fun removeItem(player: Player, item: T, container: Container = Container.INVENTORY): Boolean { + item ?: return false + val it = when (item) { + is Item -> item + is Int -> Item(item) + else -> throw IllegalStateException("Invalid value passed for item") + } + + return when (container) { + 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 + * @param id the ID of the item to add + * @param amount the amount of the item to add + * @param container the Container to modify + * @return true if the item was successfully added + */ +fun addItem(player: Player, id: Int, amount: Int = 1, container: Container = Container.INVENTORY): Boolean { + val cont = when (container) { + 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)) +} + +/** + * Replaces the item in the given slot in the given container with the given item. + * @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. + */ +fun replaceSlot(player: Player, slot: Int, item: Item, currentItem: Item? = null, container: Container = Container.INVENTORY): Item? { + val cont = when (container) { + 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, 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) ?: 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 + * @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 addItemOrDrop(player: Player, id: Int, amount: Int = 1) { + val item = Item(id, amount) + 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!)")) + } + } +} + +/** + * Clears an NPC with the "poof" smoke graphics commonly seen with random event NPCs. + * @param npc the NPC object to initialize + */ +fun poofClear(npc: NPC) { + submitWorldPulse(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 2 -> { + npc.isInvisible = true; Graphics.send(Graphics(86), npc.location) + } + 3 -> npc.clear().also { return true } + } + return false + } + }) +} + +/** + * Get number of free slots in a player's inventory + * @param player the player to check + * @return the number of free slots in the player's inventory + */ +fun freeSlots(player: Player): Int { + return player.inventory.freeSlots() +} + +/** + * Get an animation by ID. + * @param id the ID of the animation to use + * @return an Animation object with the given ID. + */ +fun getAnimation(id: Int): Animation { + return Animation(id) +} + +/** + * Get an animation by ID with priority + * @param id the ID of the animation to get + * @param priority the Animator.Priority enum instance to represent the desired priority + * @return an Animation object with the given ID and priority + */ +fun getAnimationWithPriority(id: Int, priority: Animator.Priority): Animation { + return Animation(id, Animator.Priority.values()[priority.ordinal]) +} + +/** + * Reset a player's animator + * @param player the player whose animator to reset + */ +fun resetAnimator(player: Player) { + player.animator.animate(Animation(-1, Animator.Priority.VERY_HIGH)) +} + +/** + * Get the number of ticks an animation lasts + * @param animation the Animation object to check the duration of + * @return the number of ticks the given animation lasts for + */ +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 + * @param skill the Skill ID to reward XP for. There is a Skills enum you can use for this. Example: Skills.STRENGTH + * @param amount the amount, including decimal place, of experience to award + */ +fun rewardXP(player: Player, skill: Int, amount: Double) { + player.skills.addExperience(skill, amount) +} + +/** + * Replace an object with the given revert timer + * @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, forTicks: Int, loc: Location? = null) { + val newLoc = when (loc) { + null -> toReplace.location + else -> loc + } + if (forTicks == -1) { + SceneryBuilder.replace(toReplace, toReplace.transform(with, toReplace.rotation, newLoc)) + } else { + 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 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, forTicks: Int, rotation: Direction, loc: Location? = null) { + val newLoc = when (loc) { + null -> toReplace.location + else -> loc + } + val rot = when (rotation) { + Direction.NORTH_WEST -> 0 + Direction.NORTH -> 1 + Direction.NORTH_EAST -> 2 + Direction.EAST -> 4 + Direction.SOUTH_EAST -> 7 + Direction.SOUTH -> 6 + Direction.SOUTH_WEST -> 5 + Direction.WEST -> 3 + } + if (forTicks == -1) { + SceneryBuilder.replace(toReplace, toReplace.transform(with, rot, newLoc)) + } else { + SceneryBuilder.replace(toReplace, toReplace.transform(with, rot, newLoc), forTicks) + } + toReplace.isActive = false +} + +/** + * Gets the name of an item. + * @param id the ID of the item to get the name of + * @return the name of the item + */ +fun getItemName(id: Int): String { + return ItemDefinition.forId(id).name +} + +/** + * Removes a ground item + * @param node the GroundItem object to remove + */ +fun removeGroundItem(node: GroundItem) { + GroundItemManager.destroy(node) +} + +/** + * Checks if a ground item is valid/still exists/should exist + * @param node the GroundItem object to check the validity of + * @return true if the node is valid, false otherwise + */ +fun isValidGroundItem(node: GroundItem): Boolean { + return GroundItemManager.getItems().contains(node) +} + +/** + * Checks if a player has space for an item + * @param player the player whose inventory to check + * @param item the Item to check against + * @return true if the player's inventory has space for the item + */ +fun hasSpaceFor(player: Player, item: Item): Boolean { + return player.inventory.hasSpaceFor(item) +} + +/** + * Get the number of ticks passed since server startup + */ +fun getWorldTicks(): Int { + return GameWorld.ticks +} + +/** + * Plays a jingle by id + * @param player the player to play the jingle for + * @param jingleId the jingle to play + */ +fun playJingle(player: Player, jingleId: Int) { + PacketRepository.send(MusicPacket::class.java, MusicContext(player, jingleId, true)) +} + +/** + * Impact an enemy with the given amount of damage and the given hitsplat type + * @param entity the entity to damage + * @param amount the amount of damage to deal + * @param type the type of hit splat to use, ImpactHandler.HitsplatType is an enum containing these options. + */ +fun impact(entity: Entity, amount: Int, type: ImpactHandler.HitsplatType = ImpactHandler.HitsplatType.NORMAL) { + entity.impactHandler.manualHit(entity, amount, type) + if (entity is Player) playHurtAudio(entity) +} + +/** + * Get an item's definition + * @param id the ID of the item to get the definition of + * @return the ItemDefinition for the given ID. + */ +fun itemDefinition(id: Int): ItemDefinition { + return ItemDefinition.forId(id) +} + +/** + * Check whether a node contains an interaction option. + * + * @param node: An NPC, a scenery object or an item to inspect. + * @param option: The name of the interaction option to check for existence of. + * + * @return A Boolean value indicating the presence of the specified option in the given node. + * + * @author vddCore + */ +fun hasOption(node: Node, option: String): Boolean { + return when (node) { + is NPC -> node.definition.hasAction(option) + is Scenery -> node.definition.hasAction(option) + is Item -> node.definition.hasAction(option) + else -> throw IllegalArgumentException("Expected an NPC, Scenery or an Item, got ${node.javaClass.simpleName}.") + } +} + +/** + * Send an object animation + */ +fun animateScenery(player: Player, obj: Scenery, animationId: Int, global: Boolean = false) { + player.packetDispatch.sendSceneryAnimation(obj, getAnimation(animationId), global) +} + +/** + * Send an object animation independent of a player + */ +fun animateScenery(obj: Scenery, animationId: Int) { + val animation = Animation(animationId) + animation.setObject(obj) + getRegionChunk(obj.location).flag(AnimateObjectUpdateFlag(animation)) +} + +/** + * Produce a ground item owned by the player + */ +fun produceGroundItem(player: Player, item: Int) { + GroundItemManager.create(Item(item), player) +} + +/** + * Produces a ground item with the given ID and amount at the given location + * @param owner the owner of the ground item, use null for none. + * @param id the id of the item. + * @param amount the amount of the item. + * @param location the location of the item. + * @return the created ground item. + */ +fun produceGroundItem(owner: Player?, id: Int, amount: Int, location: Location) : GroundItem { + return GroundItemManager.create(Item(id, amount), location, owner) +} + +/** + * Spawns a projectile + */ +fun spawnProjectile(source: Entity, dest: Entity, projectileId: Int) { + Projectile.create(source, dest, projectileId).send() +} + +/** + * Spawns a projectile with more advanced parameters + * @param source the initial Location of the projectile + * @param dest the final Location of the projectile + * @param projectile the ID of the gfx used for the projectile + * @param startHeight the height the projectile spawns at + * @param endHeight the height the projectile ends at + * @param delay the delay before the projectile spawns + * @param speed the speed the projectile travels at + * @param angle the angle the projectile travels at + */ +fun spawnProjectile( + source: Location, + dest: Location, + projectile: Int, + startHeight: Int, + endHeight: Int, + delay: Int, + speed: Int, + angle: Int +) { + Projectile.create( + source, + dest, + projectile, + startHeight, + endHeight, + delay, + speed, + angle, + source.getDistance(dest).toInt() + ).send() +} + +/** + * Causes the given entity to face the given toFace + * @param entity the entity you wish to face something + * @param toFace the thing to face + * @param duration how long you wish to face the thing for + */ +fun face(entity: Entity, toFace: Node, duration: Int = -1) { + if (duration == -1) { + when (toFace) { + is Location -> entity.faceLocation(toFace) + is Entity -> entity.face(toFace) + } + } else { + when (toFace) { + is Location -> entity.faceTemporary(toFace.asNpc(), duration) + else -> entity.faceTemporary(toFace as Entity, duration) + } + } +} + +/** + * 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 + * @param id the ID of the interface to open + */ +fun openInterface(player: Player, id: Int) { + player.interfaceManager.open(Component(id)) +} + +/** + * Opens the given interface as an overlay for the given player + * @param player the player to open the interface for + * @param id the ID of the interface to open + */ +fun openOverlay(player: Player, id: Int) { + player.interfaceManager.openOverlay(Component(id)) +} + +/** + * Closes any open overlays for the given player + * @param player the player to close for + */ +fun closeOverlay(player: Player) { + player.interfaceManager.closeOverlay() +} + +/** + * Runs the given Emote for the given Entity + * @param entity the entity to run the emote on + * @param emote the Emotes enum entry to run + */ +fun emote(entity: Entity, emote: Emotes) { + entity.animate(emote.animation) +} + +/** + * 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.sendMessages(*splitLines(message, 86)) +} + +/** + * Forces an above-head chat message for the given entity + * @param entity the entity to send the chat for + * @param message the message to display + */ +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) +} + +/** + * Sends a message to a player's dialogue box + * @param player the player to send the dialogue to + * @param message the message to send, lines are split automatically. + */ +fun sendDialogue(player: Player, message: String) { + player.dialogueInterpreter.sendDialogue(*splitLines(message)) +} + +/** + * Sends a message to the player's dialogue box + * @param player the player to send the dialogue to + * @param lines the lines of dialogue to send. No automatic splitting. + */ +fun sendDialogueLines(player: Player, vararg message: String) { + player.dialogueInterpreter.sendDialogue(*message) +} + +/** + * Sends options dialogue to the given player. + * @param player the player to send the options to. + * @param title the title of the dialogue options + * @param options the options to present to the player + */ +fun sendDialogueOptions(player: Player, title: String, vararg options: String) { + player.dialogueInterpreter.sendOptions(title, *options) +} + +/** + * Plays an animation on the entity + * @param entity the entity to animate + * @param anim the animation to play, can be an ID or an Animation object. + * @param forced whether or not to force the animation (usually not necessary) + */ +fun animate(entity: Entity, anim: T, forced: Boolean = false) { + val animation = when (anim) { + is Int -> Animation(anim) + is Animation -> anim + else -> throw IllegalStateException("Invalid value passed for anim") + } + + if (forced) { + entity.animator.forceAnimation(animation) + } else { + entity.animator.animate(animation) + } +} + +/** + * 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) + */ +@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) + } +} + +/** + * Opens a dialogue with the given dialogue key or dialogue file, depending which is passed. + * @param player the player to open the dialogue for + * @param dialogue either the dialogue key or an instance of a DialogueFile + * @param args various args to pass to the opened dialogue + */ +fun openDialogue(player: Player, dialogue: Any, vararg args: Any) { + player.dialogueInterpreter.close() + when (dialogue) { + is Int -> player.dialogueInterpreter.open(dialogue, *args) + is DialogueFile -> player.dialogueInterpreter.open(dialogue, *args) + is SkillDialogueHandler -> dialogue.open() + 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 + * @returns an NPC instance matching the ID if it finds one, null otherwise + */ +fun findNPC(id: Int): NPC? { + return Repository.findNPC(id) +} + +/** + * Gets the spawned scenery from the world map using the given coordinates + * @param x the X coordinate to use + * @param y the Y coordinate to use + * @param z the Z coordinate to use + */ +fun getScenery(x: Int, y: Int, z: Int): Scenery? { + return RegionManager.getObject(z, x, y) +} + +/** + * Gets the spawned scenery from the world map using the given Location object. + * @param loc the Location object to use. + */ +fun getScenery(loc: Location): Scenery? { + return RegionManager.getObject(loc) +} + +/** + * Gets an NPC within render distance of the refLoc that matches the given ID + * @param refLoc the Location to find the closes NPC to + * @param id the ID of the NPC to locate + * @returns an NPC instance matching the ID if it finds one, null otherwise + */ +fun findNPC(refLoc: Location, id: Int): NPC? { + return Repository.npcs.firstOrNull { it.id == id && it.location.withinDistance(refLoc) } +} + +/** + * Gets an NPC with the given ID in the same general area as the given Entity + * @param entity the entity to search around + * @param id the ID of the NPC to locate + * @returns an NPC matching the given ID or null if none is found + */ +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 + * @param ids the IDs of the NPCs to look for + */ +fun findLocalNPCs(entity: Entity, ids: IntArray): List { + return RegionManager.getLocalNpcs(entity).filter { it.id in ids }.toList() +} + +/** + * Gets a list of nearby NPCs that match the given IDs. + * @param entity the entity to check around + * @param ids the IDs of the NPCs to look for + * @param distance The maximum distance to the entity. + */ +fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List { + return RegionManager.getLocalNpcs(entity, distance).filter { it.id in ids }.toList() +} + +/** + * @param regionId the ID of the region + * @return a [ZoneBorders] encapsulating the entire region indicated by the provided regionId + */ +fun getRegionBorders(regionId: Int): ZoneBorders { + return ZoneBorders.forRegion(regionId) +} + +/** + * Gets the value of an attribute key from the Entity's attributes store + * @param entity the entity to get the attribute from + * @param attribute the attribute key to use + * @param default the default value to return if the attribute does not exist + */ +fun getAttribute(entity: Entity, attribute: String, default: T): T { + return entity.getAttribute(attribute, default) +} + +/** + * Sets an attribute key to the given value in an Entity's attribute store + * @param entity the entity to set the attribute for + * @param attribute the attribute key to use + * @param value the value to set the attribute to + */ +fun setAttribute(entity: Entity, attribute: String, value: T) { + entity.setAttribute(attribute, value) +} + +fun removeAttribute(entity: Entity, attribute: String) { + entity.removeAttribute(attribute.replace("/save:","")) +} + +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 + * @param duration the number of ticks to lock for + */ +fun lock(entity: Entity, duration: Int) { + entity.lock(duration) +} + +/** + * Locks specifically an entity's interactions, allowing movement still + * @param entity the entity to lock + * @param duration the duration in ticks to lock for + */ +fun lockInteractions(entity: Entity, duration: Int) { + entity.locks.lockInteractions(duration) +} + +/** + * Unlocks the given entity + * @param entity the entity to unlock + */ +fun unlock(entity: Entity) { + entity.unlock() +} + +/** + * Transforms an NPC for the given number of ticks + * @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 transformNpc(npc: NPC, transformTo: Int, restoreTicks: Int) { + npc.transform(transformTo) + Pulser.submit(object : Pulse(restoreTicks) { + override fun pulse(): Boolean { + npc.reTransform() + return true + } + }) +} + +/** + * 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 + */ +fun location(x: Int, y: Int, z: Int): Location { + return Location.create(x, y, z) +} + +/** + * Checks if the given entity is within the given ZoneBorders + */ +fun inBorders(entity: Entity, borders: ZoneBorders): Boolean { + return borders.insideBorder(entity) +} + +/** + * Checks if the given entity is within the given borders + */ +fun inBorders(entity: Entity, swX: Int, swY: Int, neX: Int, neY: Int): Boolean { + return ZoneBorders(swX, swY, neX, neY).insideBorder(entity) +} + +/** + * AHeals the given entity for the given number of hitpoints + */ +fun heal(entity: Entity, amount: Int) { + entity.skills.heal(amount) +} + +fun getVarp (player: Player, varpIndex: Int) : Int { + return player.varpMap[varpIndex] ?: 0 +} + +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) +} + +fun setVarc (player: Player, varc: Int, value: Int) +{ + player.packetDispatch.sendVarcUpdate(varc.toShort(), value) +} + +fun reinitVarps (player: Player) { + for ((index, value) in player.varpMap) { + setVarp(player, index, value, player.saveVarp[index] ?: false) + } +} + +/** + * Force an entity to walk to a given destination. + * @param entity the entity to forcewalk + * @param dest the Location object to walk to + * @param type the type of pathfinder to use. "smart" for the SMART pathfinder, "clip" for the noclip pathfinder, anything else for DUMB. + */ +fun forceWalk(entity: Entity, dest: Location, type: String) { + if (type == "clip") { + ForceMovement(entity, dest, 10, 10).run() + return + } + val pathfinder = when (type) { + "smart" -> Pathfinder.SMART + else -> Pathfinder.DUMB + } + val path = Pathfinder.find(entity, dest, true, pathfinder) + 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 + */ +fun stopWalk(entity: Entity) { + entity.walkingQueue.reset() +} + +/** + * Returns a list of all valid children IDs for a given scenery ID + */ +fun getChildren(scenery: Int): IntArray { + return SceneryDefinition.forId(scenery).getChildrenIds().filter { it != -1 }.toIntArray() +} + +/** + * Adjusts the charge for the given node. + * @param node the node to adjust the charge of + * @param amount the amount to adjust by + */ +fun adjustCharge(node: Node, amount: Int) { + when (node) { + is Item -> node.charge += amount + is Scenery -> node.charge += amount + else -> log(ContentAPI::class.java, Log.ERR, "Attempt to adjust the charge of invalid type: ${node.javaClass.simpleName}") + } +} + +/** + * Get the current charge of the given node + * @param node the node whose charge to check + * @return amount of charges the node has, or -1 if the node does not accept charges. + */ +fun getCharge(node: Node): Int { + when (node) { + is Item -> return node.charge + is Scenery -> return node.charge + else -> log(ContentAPI::class.java, Log.ERR, "Attempt to get charge of invalid type: ${node.javaClass.simpleName}") + .also { return -1 } + } +} + +/** + * Set the charge of the given node to the given amount. + * @param node the node to set the charge for + * @param charge the amount to set the node's charge to (default is 1000) + */ +fun setCharge(node: Node, charge: Int) { + when (node) { + is Item -> node.charge = charge + is Scenery -> node.charge = charge + else -> log(ContentAPI::class.java, Log.ERR, "Attempt to set the charge of invalid type: ${node.javaClass.simpleName}") + } +} + +/** + * Gets the used option in the context of an interaction. + * @param player the player to get the used option for. + * @return the option the player used + */ +fun getUsedOption(player: Player): String { + return player.getAttribute("interact:option", "INVALID") +} + +/** + * Used to play both an Animation and Graphics object simultaneously. + * Use -1 in place of anim or graphics if not needed. + * @param entity the entity to perform this on + * @param anim the Animation object to use, can also be an ID. + * @param gfx the Graphics object to use, can also be an ID. + */ +fun visualize(entity: Entity, anim: A, gfx: G) { + val animation = when (anim) { + is Int -> Animation(anim) + is Animation -> anim + else -> throw IllegalStateException("Invalid parameter passed for animation.") + } + + val graphics = when (gfx) { + is Int -> Graphics(gfx) + is Graphics -> gfx + else -> throw IllegalStateException("Invalid parameter passed for graphics.") + } + + entity.visualize(animation, graphics) +} + +/** + * Used to submit a pulse to the GameWorld's Pulser. + * @param pulse the Pulse object to submit + */ +fun submitWorldPulse(pulse: Pulse) { + Pulser.submit(pulse) +} + +/** + * Used to submit a pulse to the GameWorld's Pulser, albeit with a cleaner syntax. + * @param task an anonymous function that will be run in the Pulse + * @return the newly submitted Pulse + */ +fun runWorldTask(task: () -> Unit): Pulse { + val pulse = object : Pulse() { + override fun pulse(): Boolean { + task.invoke() + return true + } + } + + submitWorldPulse(pulse) + return pulse +} + +/** + * Teleports or "instantly moves" an entity to a given Location object. + * @param entity the entity to move + * @param loc the Location object to move them to + * @param type the teleport type to use (defaults to instant). An enum exists as TeleportManager.TeleportType. + */ +fun teleport(entity: Entity, loc: Location, type: TeleportManager.TeleportType = TeleportManager.TeleportType.INSTANT) : Boolean { + if (type == TeleportManager.TeleportType.INSTANT) { + entity.properties.teleportLocation = loc + return true + } + else return entity.teleporter.send(loc, type) +} + +/** + * Sets the dynamic or "temporary" (restores) level of a skill. + * @param entity the entity to set the level for + * @param skill the Skill to set. A Skills enum exists that can be used. Ex: Skills.STRENGTH + * @param level the level to set the skill to + */ +fun setTempLevel(entity: Entity, skill: Int, level: Int) { + entity.skills.setLevel(skill, level) +} + +/** + * Gets the static (unchanging/max) level of an entity's skill + * @param entity the entity to get the level for + * @param skill the Skill to get the level of. A Skills enum exists that can be used. Ex: Skills.STRENGTH + * @return the static level of the skill + */ +fun getStatLevel(entity: Entity, skill: Int): Int { + return entity.skills.getStaticLevel(skill) +} + +/** + * Gets the dynamic (boostable/debuffable/restoring) level of an entity's skill + * @param entity the entity to get the level for + * @param skill the Skill to get the level of. A Skills enum exists that can be used. Ex: Skills.STRENGTH + * @return the dynamic level of the skill + */ +fun getDynLevel(entity: Entity, skill: Int): Int { + return entity.skills.getLevel(skill) +} + +/** + * Adjusts (buffs/debuffs) the given Skill by the amount given. + * @param entity the entity to adjust the skill for + * @param skill the Skill to adjust. A Skills enum exists that can be used. Ex: Skills.STRENGTH + * @param amount the amount to adjust the skill by. Ex-Buff: 5, Ex-Debuff: -5 + */ +fun adjustLevel(entity: Entity, skill: Int, amount: Int) { + entity.skills.setLevel(skill, entity.skills.getStaticLevel(skill) + amount) +} + +/** + * Remove all of a given item from the given container + * @param player the player to remove the item from + * @param item the item to remove. Can be an Item object or an ID. + * @param container the Container to remove the item from. An enum exists for this called Container. Ex: Container.BANK + */ +fun removeAll(player: Player, item: T, container: Container = Container.INVENTORY): Boolean { + item ?: return false + val it = when (item) { + is Item -> item.id + is Int -> item + else -> throw IllegalStateException("Invalid value passed as item") + } + + return when (container) { + Container.EQUIPMENT -> player.equipment.remove(Item(it, amountInEquipment(player, it))) + Container.BANK -> { + val amountInPrimary = amountInBank(player, it, false) + val amountInSecondary = amountInBank(player, it, true) - amountInPrimary + 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 + } + } +} + +/** + * Sends a string to a specific interface child + * @param player the player to send the packet to + * @param string the string to send to the child + * @param iface the ID of the interface to use + * @param child the index of the child to send the string to + */ +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 + */ +fun closeInterface(player: Player) { + player.interfaceManager.close() +} + +/** + * Closes any opened tab interfaces for the player + * @param player the player to close the tab for + */ +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, hide: Boolean = false) { + player.dialogueInterpreter.sendDialogues(player, expr, hide, *splitLines(msg)) +} + +/** + * Sends a player model to a specific interface child + * @param player the player to send the packet to and whose model to use + * @param iface the ID of the interface to send it to + * @param child the index of the child on the interface to send the model to + */ +fun sendPlayerOnInterface(player: Player, iface: Int, child: Int) { + player.packetDispatch.sendPlayerOnInterface(iface, child) +} + +/** + * 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, + 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) +} + +/** + * Sends an animation to a specific interface child + * @param player the player to send the packet to + * @param anim the ID of the animation to send to the interface + * @param iface the ID of the interface to send the animation to + * @param child the index of the child on the interface to send the model to + */ +fun sendAnimationOnInterface(player: Player, anim: Int, iface: Int, child: Int) { + player.packetDispatch.sendAnimationInterface(anim, iface, child) +} + +/** + * Register a logout listener to a player. Logout listeners are methods that run when a player logs out. + * @param player the player to register the listener for + * @param handler the method to run when the listener is invoked (when the player logs out) + */ +fun registerLogoutListener(player: Player, key: String, handler: (p: Player) -> Unit) { + player.logoutListeners[key] = handler +} + +/** + * Removes a logout listener based on the key from a player + * @param player the player to remove the logout listner from + * @param key the key of the logout listener to remove. + */ +fun clearLogoutListener(player: Player, key: String) { + player.logoutListeners.remove(key) +} + +/** + * Sends an 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 amount the amount of the item to send - defaults to 1 + */ +fun sendItemOnInterface(player: Player, iface: Int, child: Int, item: Int, amount: Int = 1) { + 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 + * @param item the ID of the item to show + * @param message the text to display + */ +fun sendItemDialogue(player: Player, item: Any, message: String) { + val dialogueItem = when (item) { + is Item -> item + is Int -> Item(item) + else -> { + throw java.lang.IllegalArgumentException("Expected an Item or an Int, got ${item::class.java.simpleName}.") + } + } + + player.dialogueInterpreter.sendItemMessage(dialogueItem, *splitLines(message)) +} + +/** + * Sends a dialogue box with two items and some text + * @param player the player to send it to + * @param item1 the ID of the first item to show + * @param item2 the ID of the second item to show + * @param message the text to display + */ +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)) + } +} + +/** + * Send an input dialogue to retrieve a specified value from the player + * @param player the player to send the input dialogue to + * @param numeric whether or not the input is numeric + * @param prompt what to prompt the player + * @param handler the method that handles the value gained from the input dialogue + */ +fun sendInputDialogue(player: Player, numeric: Boolean, prompt: String, handler: (value: Any) -> Unit) { + if (numeric) sendInputDialogue(player, InputType.NUMERIC, prompt, handler) + else sendInputDialogue(player, InputType.STRING_SHORT, prompt, handler) +} + +/** + * Send input dialogues based on type. Some dialogues are special and can't be covered by the other sendInputDialogue method + * @param player the player to send the input dialogue to + * @param type the input type to send - an enum is available for this called InputType + * @param prompt what to prompt the player + * @param handler the method that handles the value from the input dialogue + */ +fun sendInputDialogue(player: Player, type: InputType, prompt: String, handler: (value: Any) -> Unit) { + when (type) { + InputType.AMOUNT -> { + player.setAttribute("parseamount", true) + player.dialogueInterpreter.sendInput(true, prompt) + } + + InputType.NUMERIC, InputType.STRING_SHORT -> player.dialogueInterpreter.sendInput( + type != InputType.NUMERIC, + prompt + ) + + InputType.STRING_LONG -> player.dialogueInterpreter.sendLongInput(prompt) + InputType.MESSAGE -> player.dialogueInterpreter.sendMessageInput(prompt) + } + + player.setAttribute("runscript", handler) + player.setAttribute("input-type", type) +} + +/** + * Forces an NPC to "flee" from a player or other entity + * @param entity the entity to make flee + * @param from the entity to flee from + */ +fun flee(entity: Entity, from: Entity) { + lock(entity, 5) + face(entity, from, 5) + + val diffX = entity.location.x - from.location.x + val diffY = entity.location.y - from.location.y + + forceWalk(entity, entity.location.transform(diffX, diffY, 0), "DUMB") +} + +/** + * Submits an individual or "weak" pulse to a specific entity's pulse manager. Pulses submitted this way can be overridden by other pulses. + * @param entity the entity to submit the pulse to + * @param pulse the pulse to submit + */ +fun submitIndividualPulse(entity: Entity, pulse: Pulse) { + entity.pulseManager.run(pulse) +} + +/** + * Similar to submitIndividualPulse, but for non-repeating tasks, with a cleaner syntax. + */ +fun runTask(entity: Entity, delay: Int = 0, repeatTimes: Int = 1, task: () -> Unit) { + var cycles = repeatTimes + entity.pulseManager.run(object : Pulse(delay) { + override fun pulse(): Boolean { + task.invoke() + return --cycles <= 0 + } + }) +} + +/** + * Gets the number of QP a player has + * @param player the player to get the QP for + * @return the number of QP the player has + */ +fun getQuestPoints(player: Player): Int { + return player.questRepository.points +} + +/** + * Gets the stage for the given quest for the given player + */ +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: 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: 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: Quests): Boolean { + val quest = player.questRepository.getQuest(quest) + val canStart = quest.hasRequirements(player) + if (!canStart) return false + quest.start(player) + return true +} + +/** + * Finishes a quest, gives rewards, marks as completed, etc + */ +fun finishQuest(player: Player, quest: Quests) { + player.questRepository.getQuest(quest).finish(player) +} + +/** + * Gets a scenery definition from the given ID + * @param id the ID of the scenery to get the definition for. + * @return the scenery definition + */ +fun sceneryDefinition(id: Int): SceneryDefinition { + return SceneryDefinition.forId(id) +} + +/** + * Register a map zone + * @param zone the zone to register + * @param borders the ZoneBorders that compose the zone + */ +fun registerMapZone(zone: MapZone, borders: ZoneBorders) { + ZoneBuilder.configure(zone) + zone.register(borders) +} + +/** + * Animates a component of an interface. + * @param player the player to animate the interface for. + * @param iface the ID of the interface to animate. + * @param child the child on the interface to animate. + * @param anim the ID of the animation to use. + */ +fun animateInterface(player: Player, iface: Int, child: Int, anim: Int) { + player.packetDispatch.sendAnimationInterface(anim, iface, child) +} + +/** + * Adds a climb destination to the ladder handler. + * @param ladderLoc the location of the ladder/stairs object you want to climb. + * @param dest the destination for the climb. + */ +fun addClimbDest(ladderLoc: Location, dest: Location) { + core.game.global.action.SpecialLadders.add(ladderLoc, dest) +} + +/** + * Sends a news announcement in game chat. + * @param message the message to announce + */ +fun sendNews(message: String) { + Repository.sendNews(message, 12, "CC6600") +} + +/** + * Sends a given Graphics object, or graphics ID, to the given location. + * @param gfx the Graphics object, or the Integer ID of the graphics, to send. Either works. + * @param location the location to send it to + */ +fun sendGraphics(gfx: G, location: Location) { + when (gfx) { + is Int -> Graphics.send(Graphics(gfx), location) + is Graphics -> Graphics.send(gfx, 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}.") + 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 + * @return a List of skill names + */ +fun getMasteredSkillNames(player: Player): List { + val hasMastered = player.getSkills().masteredSkills > 0 + val masteredSkills = ArrayList() + + if (hasMastered) { + for ((skillId, skillName) in Skills.SKILL_NAME.withIndex()) { + //phil you were looping every skill and performing a string comparison (another loop) for every one here. + //that's some real shit code bro. + //you were also performing a dynamic level check rather than a static. + if (hasLevelStat(player, skillId, 99)) { + masteredSkills.add(skillName) + } + } + } + return masteredSkills +} + +/** + * Empties the provided container into the player's bank. + * @param player the player + * @param container the container to be emptied. + * @return The amount of items which were successfully transferred to the bank. + * + * @author ceik + * @author James Triantafylos + * @author vddCore + */ +fun dumpContainer(player: Player, container: core.game.container.Container): Int { + val bank = player.bank + 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.") + return@beginDepositing + } + + if (!bank.canAdd(item)) { + sendMessage(player, "A magical force prevents you from banking the ${item.name}.") + return@forEach + } else { + if (container is EquipmentContainer) { + if (!InteractionListeners.run(item.id, player, item, false)) { + 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), false) + dumpedCount++ + } + } + } + + container.update() + bank.update() + + return dumpedCount +} + +/** + * Attempts to empty the player's Beast of Burden's inventory + * into the player's bank. + * + * @param player The player whose Beast of Burden's inventory to empty. + * + * @author vddCore + */ +fun dumpBeastOfBurden(player: Player) { + val famMan = player.familiarManager + + if (!famMan.hasFamiliar()) { + sendMessage(player, "You don't have a familiar.") + return + } + + if (famMan.familiar !is BurdenBeast) { + sendMessage(player, "Your familiar is not a Beast of Burden.") + return + } + + val beast: BurdenBeast = (famMan.familiar as BurdenBeast) + + if (beast.container.isEmpty) { + sendMessage(player, "Your familiar's inventory is empty.") + return + } + + val itemCount = beast.container.itemCount() + val dumpedCount = dumpContainer(player, beast.container) + + + when { + dumpedCount == itemCount -> sendMessage(player, "Your familiar's inventory was deposited into your bank.") + dumpedCount > 0 -> { + val remainPhrase = when { + (itemCount - dumpedCount == 1) -> "item remains" + else -> "items remain" + } + + sendMessage(player, "${itemCount - dumpedCount} $remainPhrase in your familiar's inventory.") + } + } +} + +/** + * Gets the player's familiar boost in the given skill + * + * @param player The player who owns the familiar. + * @param skill The skill to check boost of. + * @return The amount of skill boost gained from the player's familiar. + * + * @author bushtail + */ +fun getFamiliarBoost(player: Player, skill: Int): Int { + return player.familiarManager.getBoost(skill) +} + +/** + * Converts an item into its noted representation. + * + * @param item The item to convert. + * @return Noted form of the item or the item itself if it's already, or cannot be, noted. + * + * @author vddCore + */ +fun note(item: Item): Item { + if (!item.definition.isUnnoted) + return item + + if (item.definition.noteId < 0) + return item + + return Item(item.definition.noteId, item.amount, item.charge) +} + +/** + * Converts a noted item into its unnoted representation. + * + * @param item The item to convert. + * @return Unnoted form of the item, or the item itself if it's already unnoted. + * + * @author vddCore + */ +fun unnote(item: Item): Item { + if (item.definition.isUnnoted) + return item + + return Item(item.noteChange, item.amount, item.charge) +} + +/** + * Checks if the player has the Seal of Passage equipped or if it's present in the inventory. + * + * @param player The player to inspect for item's presence. + * @return True if Seal of Passage present, false otherwise. + */ +fun hasSealOfPassage(player: Player): Boolean { + return inEquipmentOrInventory(player, Items.SEAL_OF_PASSAGE_9083) +} + +/** + * Returns a boolean indicating if the player has a house. + * @param player the player. + * @return boolean indicating presence of house. + */ +fun hasHouse(player: Player): Boolean { + return player.houseManager.hasHouse() +} + +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) +} + +fun getServerConfig(): Toml { + return ServerConfigParser.tomlData ?: Toml() +} + +fun getPathableRandomLocalCoordinate(target: Entity, radius: Int, center: Location, maxAttempts: Int = 3): Location { + 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(center, dest, target.size()) + success = path.isSuccessful && !path.isMoveNear + if (success) return dest + } + + 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 + * @param player the player whose task we are checking. + * @return the slayer task. + */ +fun getSlayerTask(player: Player): Tasks? { + return SlayerManager.getInstance(player).activeTask +} + +/** + * Returns the name of the player's active slayer task. + * @author bushtail + * @param player the player whose task we are checking. + * @return the name of the slayer task. + */ +fun getSlayerTaskName(player: Player): String { + return SlayerManager.getInstance(player).taskName +} + +/** + * Returns the player's remaining kills for their active slayer task. + * @author bushtail + * @param player the player whose task we are checking. + * @return the remaining kills of the slayer task. + */ +fun getSlayerTaskKillsRemaining(player: Player): Int { + return SlayerManager.getInstance(player).amount +} + +/** + * Returns the player's slayer master as npc. + * @author bushtail + * @param player the player whose master we are checking. + * @return the slayer master as NPC. + */ +fun getSlayerMaster(player: Player): NPC { + return findNPC(SlayerManager.getInstance(player).master?.npc as Int) as NPC +} + +/** + * Returns the player's slayer master location as string. + * @author bushtail + * @param player the player whose master we are checking. + * @return the slayer master location as String. + */ +fun getSlayerMasterLocation(player: Player): String { + return when (getSlayerMaster(player).id) { + NPCs.CHAELDAR_1598 -> "Zanaris" + NPCs.DURADEL_8275 -> "Shilo Village" + NPCs.MAZCHNA_8274 -> "Canifis" + NPCs.TURAEL_8273 -> "Taverley" + NPCs.VANNAKA_1597 -> "Edgeville Dungeon" + else -> "The Backrooms" + } +} + +/** + * Returns the player's slayer task tip as String. + * @author bushtail + * @param player the player whose task tip we are checking. + * @return the task tip as String. + */ +fun getSlayerTip(player: Player): Array { + return if (hasSlayerTask(player)) { + getSlayerTask(player)?.tip!! + } else { + arrayOf("You need something new to hunt.") + } +} + +/** + * Returns the player's slayer task flags as Int. + * @author bushtail + * @param player the player whose task flags we are checking. + * @return the task flags as Int. + */ +fun getSlayerTaskFlags(player: Player): Int { + return getSlayerFlags(player).taskFlags +} + +fun getSlayerFlags(player: Player): SlayerFlags { + return SlayerManager.getInstance(player).flags +} + +/** + * Returns whether or not the player has a task. + * @author bushtail + * @param player the player whose task we are checking. + * @return has task as Boolean. + */ +fun hasSlayerTask(player: Player): Boolean { + return SlayerManager.getInstance(player).hasTask() +} + +/** + * Checks whether a player plays in a specific Ironman mode. + * + * @param player Player whose Ironman status to check. + * @param restriction The Ironman restriction level to check the player against. + * @return Whether the player is restricted to the provided Ironman mode. + */ +fun hasIronmanRestriction(player: Player, restriction: IronmanMode): Boolean { + return player.ironmanManager.isIronman + && player.ironmanManager.mode.ordinal >= restriction.ordinal +} + +/** + * Conditionally executes an action based on the player's Ironman status. + * + * @param player Player whose action to restrict. + * @param restriction Ironman mode that will be used as the restriction criterion. + * @param action The action to be restricted. + */ +fun restrictForIronman(player: Player, restriction: IronmanMode, action: () -> Unit) { + if (!player.ironmanManager.checkRestriction(restriction)) { + action() + } +} + +/** + * Opens the given player's Grand Exchange interface. + * + * @author vddCore + * @param player The player whose Grand Exchange interface to open. + */ +fun openGrandExchange(player: Player) { + restrictForIronman(player, IronmanMode.ULTIMATE) { + StockMarket.openFor(player) + } +} + +/** + * Checks if the player has any items to collect from the Grand Exchange + * + * @author vddCore + * @param player The player whose Grand Exchange collection box to inspect. + */ +fun hasAwaitingGrandExchangeCollections(player: Player): Boolean { + val records = GrandExchangeRecords.getInstance(player) + + for (record in records.offerRecords) { + val offer = records.getOffer(record) + + return offer != null + && offer.withdraw[0] != null + } + + return false +} + +/** + * Opens the given player's Grand Exchange collection box. + * + * It will send a prohibition message to Ultimate Ironmen. + * + * @author vddCore + * @param player The player whose collection box to open. + */ +fun openGrandExchangeCollectionBox(player: Player) { + restrictForIronman(player, IronmanMode.ULTIMATE) { + GrandExchangeRecords.getInstance(player).openCollectionBox() + } +} + +/** + * Opens the given player's bank account. If the player has a PIN set, + * the PIN interface will be shown first. + * + * It will send a prohibition message to Ultimate Ironmen. + * + * @author vddCore + * @param player The player whose bank account to open. + */ +fun openBankAccount(player: Player) { + restrictForIronman(player, IronmanMode.ULTIMATE) { + player.bank.open() + } +} + +/** + * Opens the given player's bank deposit box interface. + * + * It will send a prohibition message to Ultimate Ironmen. + * + * @author vddCore + * @param player The player whose bank account to open. + */ +fun openDepositBox(player: Player) { + restrictForIronman(player, IronmanMode.ULTIMATE) { + player.bank.openDepositBox() + } +} + +/** + * Opens the given player's bank PIN settings interface. + * + * It will send a prohibition message to Ultimate Ironmen. + * + * @author vddCore + * @param player The player whose PIN settings to open. + */ +fun openBankPinSettings(player: Player) { + restrictForIronman(player, IronmanMode.ULTIMATE) { + player.bankPinManager.openSettings() + } +} + +enum class SecondaryBankAccountActivationResult { + SUCCESS, + ALREADY_ACTIVE, + NOT_ENOUGH_MONEY, + INTERNAL_FAILURE +} +/** + * Activates the secondary bank account and handles all the fees required. + * + * @author vddCore + * @param player The player whose secondary bank account to activate. + * @returns Whether the operation was successful or not. + */ +fun activateSecondaryBankAccount(player: Player): SecondaryBankAccountActivationResult { + if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { + return SecondaryBankAccountActivationResult.INTERNAL_FAILURE + } + + if (hasActivatedSecondaryBankAccount(player)) { + return SecondaryBankAccountActivationResult.ALREADY_ACTIVE + } + + val cost = 5000000 + val coinsInInventory = amountInInventory(player, Items.COINS_995) + val coinsInBank = amountInBank(player, Items.COINS_995) + val coinsTotal = coinsInInventory + coinsInBank + + if (cost > coinsTotal) { + return SecondaryBankAccountActivationResult.NOT_ENOUGH_MONEY + } + + val operationResult = if (cost > coinsInInventory) { + val amountToTakeFromBank = cost - coinsInInventory + + removeItem(player, Item(Items.COINS_995, coinsInInventory), Container.INVENTORY) + && removeItem(player, Item(Items.COINS_995, amountToTakeFromBank), Container.BANK) + } else { + removeItem(player, Item(Items.COINS_995, cost)) + } + + return if (operationResult) { + setAttribute(player, "/save:UnlockedSecondaryBank", true) + SecondaryBankAccountActivationResult.SUCCESS + } else { + sendMessage(player, "$cost;$coinsInInventory;$coinsInBank;$coinsTotal") + SecondaryBankAccountActivationResult.INTERNAL_FAILURE + } +} + +/** + * Checks if the player has unlocked their secondary bank account. + * + * @author vddCore + * @param player The player whose secondary bank activation status to inspect. + * @return Secondary bank activation status. + */ +fun hasActivatedSecondaryBankAccount(player: Player): Boolean { + return getAttribute(player, "UnlockedSecondaryBank", false) +} + +/** + * Toggles between the player's primary and/or secondary bank account. + * Has no effect if the secondary bank account hasn't been activated. + * + * It will send a prohibition message to Ultimate Ironmen. + * + * @author vddCore + * @param player The player whose bank accounts to toggle. + */ +fun toggleBankAccount(player: Player) { + restrictForIronman(player, IronmanMode.ULTIMATE) { + if (!hasActivatedSecondaryBankAccount(player)) { + return@restrictForIronman + } + + player.useSecondaryBank = !player.useSecondaryBank + } +} + +/** + * Checks if a player is currentl using their secondary bank account. + * + * @author vddCore + * @param player The player whose bank account toggle state to inspect. + */ +fun isUsingSecondaryBankAccount(player: Player): Boolean { + return player.useSecondaryBank +} + +/** + * Returns 'primary' or 'secondary' strings based on which + * bank account is activated for the given user. + * + * @author vddCore + * @param player The player whose bank account name to retrieve. + * @param invert Whether to invert the return value. + * @return Bank account name according to the given criteria. + */ +fun getBankAccountName(player: Player, invert: Boolean = false): String { + return if (isUsingSecondaryBankAccount(player)) { + if (invert) "primary" else "secondary" + } else { + if (invert) "secondary" else "primary" + } +} + +/** + * Opens a shop for the given NPC in the provided player's context. + * + * @author vddCore + * @param player The player serving as the shop context. + * @param npc The NPC ID whose shop to open. + */ +fun openNpcShop(player: Player, npc: Int): Boolean { + val shop = Shops.shopsByNpc[npc] + + if (shop != null) { + shop.openFor(player) + return true + } + + return false +} + +/** + * Skill Dialogue builder. + * @param player the player to send the dialogue for. + * Use like: + * sendSkillDialogue(player) { + * withItems(Items.EXAMPLE_0, Items.EXAMPLE_1) + * create { id, amount -> + * doSomethingWith(id, amount) + * } + * calculateMaxAmount { id -> + * return someAmount + * } + * } + */ +fun sendSkillDialogue(player: Player, init: SkillDialogueBuilder.() -> Unit) { + val builder = SkillDialogueBuilder() + builder.player = player + builder.init() + + if (builder.items.size !in 1..5) { + throw IllegalStateException("Invalid number of items passed to skill dialogue (min 1, max 5): ${builder.items.size}") + } + + val type = when (builder.items.size) { + 1 -> SkillDialogueHandler.SkillDialogue.ONE_OPTION + 2 -> SkillDialogueHandler.SkillDialogue.TWO_OPTION + 3 -> SkillDialogueHandler.SkillDialogue.THREE_OPTION + 4 -> SkillDialogueHandler.SkillDialogue.FOUR_OPTION + 5 -> SkillDialogueHandler.SkillDialogue.FIVE_OPTION + else -> null + } + + object : SkillDialogueHandler(player, type, *builder.items) { + override fun create(amount: Int, index: Int) { + builder.creationCallback(builder.items[index].id, amount) + } + + override fun getAll(index: Int): Int { + return builder.totalAmountCallback(builder.items[index].id) + } + }.open() +} + +class SkillDialogueBuilder { + internal lateinit var player: Player + internal var items: Array = arrayOf() + internal var creationCallback: (itemId: Int, amount: Int) -> Unit = {_,_ -> } + internal var totalAmountCallback: (itemId: Int) -> Int = {id -> amountInInventory(player, id)} + + fun withItems(vararg item: Item) { + items = arrayOf(*item) + } + + fun withItems(vararg item: Int) { + items = item.map { Item(it) }.toTypedArray() + } + + fun create(method: (itemId: Int, amount: Int) -> Unit) { + creationCallback = method + } + + fun calculateMaxAmount(method: (itemId: Int) -> Int) { + totalAmountCallback = method + } +} + +/** + * Registers a hint icon with the given height at the given location + * @param player the player to register the hint icon for + * @param height the height of the hint icon + * @param location the location of the hint icon + */ +fun registerHintIcon(player: Player, location: Location, height: Int) { + setAttribute(player, "hinticon", HintIconManager.registerHintIcon(player, location, 1, -1, player.hintIconManager.freeSlot(), height, 3)) +} + +/** + * Registers a hint icon for the given Node. + * @param player the player to show a hint icon to. + * @param node the Node to register a hint icon for. + */ +fun registerHintIcon(player: Player, node: Node) { + if (getAttribute(player, "hinticon", null) != null) + return + setAttribute(player, "hinticon", HintIconManager.registerHintIcon(player, node)) +} + +/** + * Clears the active ContentAPI-originated hint icon + * @param player the player to clear the active hint icon for + */ +fun clearHintIcon(player: Player) { + val slot = getAttribute(player, "hinticon", -1) + player.removeAttribute("hinticon") + HintIconManager.removeHintIcon(player, slot) +} + +/** + * Gets the equipment slot the item belongs to + * @param item the Id of the item to check + * @return the EquipmentSlot, or null if the item cannot be equipped, or has no slot defined. + */ +fun equipSlot(item: Int) : EquipmentSlot? { + return EquipmentSlot + .values() + .getOrNull(itemDefinition(item).getConfiguration(ItemConfigParser.EQUIP_SLOT, -1)) +} + +/** + * Adjusts the user's credits by the given amount + * @param player the player whose credits to adjust + * @param amt the amount to adjust by - add with positive, remove with negative. + * @return true if successful. Success is defined as always true when adding, but can be false if we are trying to remove, and it would put the amount below 0. + */ +fun updateCredits(player: Player, amount: Int) : Boolean { + val creds = getCredits(player) + amount + + if (creds < 0) + return false + else + player.details.accountInfo.credits = creds + + return true +} + +/** + * Gets the number of credits a user has. + * @param player the player to check + * @return the number of credits they have + */ +fun getCredits(player: Player) : Int { + return player.details.accountInfo.credits +} + +/** + * 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 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, 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 specified node is a player + * @param entity the node whom we are checking + * @return whether or not the entity is a player + */ +fun isPlayer(node: Node) : Boolean { + return (node is Player) +} + +/** + * Adds a dialogue action to the player's dialogue interpreter + * @param player the player to add the dialogue action to + * @param action the dialogue action to add + */ +fun addDialogueAction(player: Player, action: core.game.dialogue.DialogueAction) { + player.dialogueInterpreter.addAction(action) +} + +/** + * 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/ContentInterface.kt b/Server/src/main/core/api/ContentInterface.kt new file mode 100644 index 000000000..1c48fe94b --- /dev/null +++ b/Server/src/main/core/api/ContentInterface.kt @@ -0,0 +1,7 @@ +package core.api + +interface ContentInterface { + /** + * A dummy/shell interface that allows us to more easily load content via class scanning + */ +} \ No newline at end of file diff --git a/Server/src/main/core/api/DialUtils.kt b/Server/src/main/core/api/DialUtils.kt new file mode 100644 index 000000000..1149906cd --- /dev/null +++ b/Server/src/main/core/api/DialUtils.kt @@ -0,0 +1,90 @@ +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(DialUtils.removeMatches(message, DialUtils.tagRegex).length / perLineLimit.toFloat()).toInt()) { "" } + + //short circuit when possible because it's cheaper. + if (lines.size == 1) { + lines[0] = message + return lines + } + + val tokenQueue = LinkedList(message.split(" ")) + var index = 0 + 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()) + else + lines[index] = line.toString() + 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 = DialUtils.removeMatches(line.toString(), DialUtils.tagRegex).isNotEmpty() + accumulator += DialUtils.removeMatches(tokenQueue.peek(), DialUtils.tagRegex).length + + if (shouldSpace) accumulator += 1 + if (accumulator > perLineLimit) { + pushLine() + continue + } + + if (shouldSpace) line.append(" ") + line.append(tokenQueue.pop()) + } + + 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 +} diff --git a/Server/src/main/core/api/EquipmentSlot.kt b/Server/src/main/core/api/EquipmentSlot.kt new file mode 100644 index 000000000..0b09e3751 --- /dev/null +++ b/Server/src/main/core/api/EquipmentSlot.kt @@ -0,0 +1,43 @@ +package core.api + +enum class EquipmentSlot { + HEAD, + CAPE, + NECK, + WEAPON, + CHEST, + SHIELD, + HIDDEN_1, + LEGS, + HIDDEN_2, + HANDS, + FEET, + HIDDEN_3, + RING, + 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 new file mode 100644 index 000000000..a572dc4bb --- /dev/null +++ b/Server/src/main/core/api/Event.kt @@ -0,0 +1,41 @@ +package core.api + +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 + @JvmStatic val DialogueOptionSelected = DialogueOptionSelectionEvent::class.java + @JvmStatic val DialogueClosed = DialogueCloseEvent::class.java + @JvmStatic val UsedWith = UseWithEvent::class.java + @JvmStatic val SelfDeath = SelfDeathEvent::class.java + @JvmStatic val Tick = TickEvent::class.java + @JvmStatic val PickedUp = PickUpEvent::class.java + @JvmStatic val InterfaceOpened = InterfaceOpenEvent::class.java + @JvmStatic val InterfaceClosed = InterfaceCloseEvent::class.java + @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 + @JvmStatic val ItemPurchased = ItemShopPurchaseEvent::class.java + @JvmStatic val ItemSold = ItemShopSellEvent::class.java + @JvmStatic val JobAssigned = JobAssignmentEvent::class.java + @JvmStatic val FairyRingDialed = FairyRingDialEvent::class.java + @JvmStatic val VarbitUpdated = VarbitUpdateEvent::class.java + @JvmStatic val DynamicSkillLevelChanged = DynamicSkillLevelChangeEvent::class.java + @JvmStatic val SummoningPointsRecharged = SummoningPointsRechargeEvent::class.java + @JvmStatic val PrayerPointsRecharged = PrayerPointsRechargeEvent::class.java + @JvmStatic val XpGained = XPGainEvent::class.java + @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 new file mode 100644 index 000000000..70eb2b0fd --- /dev/null +++ b/Server/src/main/core/api/God.kt @@ -0,0 +1,104 @@ +package core.api + +import org.rs09.consts.Items + +enum class God(vararg val validItems: Int) { + 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/IfaceSettingsBuilder.kt b/Server/src/main/core/api/IfaceSettingsBuilder.kt new file mode 100644 index 000000000..d3e5774d0 --- /dev/null +++ b/Server/src/main/core/api/IfaceSettingsBuilder.kt @@ -0,0 +1,151 @@ +package core.api + +/** + * Used to generate interface settings hashes. + * Deprecates [core.game.container.access.BitregisterAssembler] + * @author Ceikry + */ +class IfaceSettingsBuilder { + /** + * Contains the value which should be sent in access mask packet. + */ + private var value = 0 + + /** + * Sets right click option settings. If specified option is not allowed, it + * might not appear in the context menu, and if it does, it will throw an error when clicked. + * @param optionId The option index. + */ + fun enableOption(optionId: Int): IfaceSettingsBuilder { + require(!(optionId < 0 || optionId > 9)) { "Option index must be 0-9." } + value = value or (0x1 shl optionId + 1) + return this + } + + fun enableOptions(vararg ids: Int): IfaceSettingsBuilder { + for (i in ids.indices) { + enableOption(ids[i]) + } + return this + } + + fun enableOptions(ids: IntRange): IfaceSettingsBuilder { + for (i in ids.start..ids.endInclusive) { + enableOption(i) + } + return this + } + + fun enableAllOptions(): IfaceSettingsBuilder { + for (i in 0..9) { + enableOption(i) + } + return this + } + + fun enableOptions(vararg options: String?): IfaceSettingsBuilder { + for (i in options.indices) { + enableOption(i) + } + return this + } + + /** + * Sets use on option settings. If nothing is allowed then 'use' option will + * not appear in right click menu. + */ + fun setUseOnSettings( + groundItems: Boolean, + npcs: Boolean, + objects: Boolean, + otherPlayer: Boolean, + selfPlayer: Boolean, + component: Boolean + ): IfaceSettingsBuilder { + var useFlag = 0 + if (groundItems) { + useFlag = useFlag or 0x1 + } + if (npcs) { + useFlag = useFlag or 0x2 + } + if (objects) { + useFlag = useFlag or 0x4 + } + if (otherPlayer) { + useFlag = useFlag or 0x8 + } + if (selfPlayer) { + useFlag = useFlag or 0x10 + } + if (component) { + useFlag = useFlag or 0x20 + } + value = value or (useFlag shl 11) + return this + } + + /** + * Sets interface events depth. For example, we have inventory interface + * which is opened on gameframe interface (548) If depth is 1, then the + * clicks in inventory will also invoke click event handler scripts on + * gameframe interface. Setting depth to 2 also allows dragged items to + * leave the bounds of their container, useful for things such as bank tabs. + * @param depth The depth value. + */ + fun setInterfaceEventsDepth(depth: Int): IfaceSettingsBuilder { + require(!(depth < 0 || depth > 7)) { "depth must be 0-7." } + value = value and (0x7 shl 18).inv() + value = value or (depth shl 18) + return this + } + + /** + * Allows items in this interface container to be switched around + * @return this builder + */ + fun enableSlotSwitch(): IfaceSettingsBuilder { + value = value or (1 shl 21) + return this + } + + /** + * Allows items in this interface container to have a "Use" option + * @return this builder + */ + fun enableUseOption(): IfaceSettingsBuilder { + value = value or (1 shl 17) + return this + } + + fun enableExamine(): IfaceSettingsBuilder { + value = value or (1 shl 9) + return this + } + + /** + * Allows this component to have items used on it + * @return this builder + */ + fun enableUseOnSelf(): IfaceSettingsBuilder { + value = value or (1 shl 22) + return this + } + + /** + * Allows this component to switch item slots with a slot that contains a null (empty slot) + * @return this builder + */ + fun enableNullSlotSwitch(): IfaceSettingsBuilder { + value = value or (1 shl 23) + return this + } + + /** + * Gets the current value. + * @return The value. + */ + fun build(): Int { + return value + } +} \ No newline at end of file diff --git a/Server/src/main/core/api/InputType.kt b/Server/src/main/core/api/InputType.kt new file mode 100644 index 000000000..bdbf2b2c9 --- /dev/null +++ b/Server/src/main/core/api/InputType.kt @@ -0,0 +1,9 @@ +package core.api + +enum class InputType { + AMOUNT, + NUMERIC, + STRING_SHORT, + STRING_LONG, + MESSAGE +} \ No newline at end of file diff --git a/Server/src/main/core/api/LoginListener.kt b/Server/src/main/core/api/LoginListener.kt new file mode 100644 index 000000000..e76164008 --- /dev/null +++ b/Server/src/main/core/api/LoginListener.kt @@ -0,0 +1,17 @@ +package core.api + +import core.game.node.entity.player.Player + +/** + * An interface for writing content that allows the class to execute some code when a player logs in. + * + * Login listeners are called *before* [PersistPlayer] data is parsed. + */ +interface LoginListener : ContentInterface { + /** + * NOTE: This should NOT reference any non-static class-local variables. + * If you need to access a player's specific instance, use an attribute. + * Alternatively, consider using an [api.events.EventHook] if applicable. + */ + fun login(player: Player) +} \ No newline at end of file diff --git a/Server/src/main/core/api/LogoutListener.kt b/Server/src/main/core/api/LogoutListener.kt new file mode 100644 index 000000000..bd845913c --- /dev/null +++ b/Server/src/main/core/api/LogoutListener.kt @@ -0,0 +1,16 @@ +package core.api + +import core.game.node.entity.player.Player + +/** + * An interface for writing content that allows code to be executed by the class when a player logs out. + * + * Logout listeners are called *before* [PersistPlayer] data is saved. + */ +interface LogoutListener : ContentInterface { + /** + * NOTE: This should NOT reference any non-static class-local variables. + * If you need to access a player's specific instance, use an attribute. + */ + fun logout(player: Player) +} \ No newline at end of file diff --git a/Server/src/main/core/api/MapArea.kt b/Server/src/main/core/api/MapArea.kt new file mode 100644 index 000000000..28631f54d --- /dev/null +++ b/Server/src/main/core/api/MapArea.kt @@ -0,0 +1,32 @@ +package core.api + +import core.game.node.entity.Entity +import core.game.world.map.Location +import core.game.world.map.zone.MapZone +import core.game.world.map.zone.RegionZone +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction + +/** + * Interface that allows a class to define a map area. + * Optionally-overridable methods include [getRestrictions], [areaEnter], [areaLeave] and [entityStep] + */ +interface MapArea : ContentInterface { + var zone: MapZone + get(){ + return zoneMaps[this::class.java.simpleName + "MapArea"]!! + } + set(value) { + zoneMaps[this::class.java.simpleName + "MapArea"] = value + } + + fun defineAreaBorders() : Array + fun getRestrictions() : Array {return arrayOf()} + fun areaEnter(entity: Entity) {} + fun areaLeave(entity: Entity, logout: Boolean) {} + fun entityStep(entity: Entity, location: Location, lastLocation: Location) {} + + companion object { + val zoneMaps = HashMap() + } +} diff --git a/Server/src/main/core/api/PersistPlayer.kt b/Server/src/main/core/api/PersistPlayer.kt new file mode 100644 index 000000000..76c6db1e4 --- /dev/null +++ b/Server/src/main/core/api/PersistPlayer.kt @@ -0,0 +1,26 @@ +package core.api + +import core.game.node.entity.player.Player +import org.json.simple.JSONObject + +/** + * An interface for writing content that allows data to be saved and loaded from player saves. + * + * Parsing is called *after* any [LoginListener] is executed. + * + * Saving is called *after* any [LogoutListener] is executed. + */ +interface PersistPlayer : ContentInterface { + /** + * NOTE: This should NOT reference nonstatic class-local variables. + * You need to fetch a player's specific instance of the data and save from that. + * For reference, see [rs09.game.node.entity.skill.slayer.SlayerManager] + */ + fun savePlayer(player: Player, save: JSONObject) + /** + * NOTE: This should NOT reference nonstatic class-local variables. + * You need to fetch a player's specific instance of the data and parse to that. + * For reference, see [rs09.game.node.entity.skill.slayer.SlayerManager] + */ + fun parsePlayer(player: Player, data: JSONObject) +} \ No newline at end of file diff --git a/Server/src/main/core/api/PersistWorld.kt b/Server/src/main/core/api/PersistWorld.kt new file mode 100644 index 000000000..06571fe4e --- /dev/null +++ b/Server/src/main/core/api/PersistWorld.kt @@ -0,0 +1,6 @@ +package core.api + +interface PersistWorld : ContentInterface { + fun save() + fun parse() +} diff --git a/Server/src/main/core/api/ShutdownListener.kt b/Server/src/main/core/api/ShutdownListener.kt new file mode 100644 index 000000000..21e20e269 --- /dev/null +++ b/Server/src/main/core/api/ShutdownListener.kt @@ -0,0 +1,11 @@ +package core.api + +/** + * An interface for writing content that allows the class to execute code as the server is shutting down + */ +interface ShutdownListener : ContentInterface { + /** + * NOTE: This should NOT reference nonstatic class-local variables. + */ + fun shutdown() +} \ No newline at end of file diff --git a/Server/src/main/core/api/StartupListener.kt b/Server/src/main/core/api/StartupListener.kt new file mode 100644 index 000000000..d3735f50d --- /dev/null +++ b/Server/src/main/core/api/StartupListener.kt @@ -0,0 +1,11 @@ +package core.api + +/** + * An interface for writing content that allows the class to execute code when the server is started. + */ +interface StartupListener : ContentInterface { + /** + * NOTE: This should NOT reference nonstatic class-local variables. + */ + fun startup() +} \ No newline at end of file diff --git a/Server/src/main/core/api/TickListener.kt b/Server/src/main/core/api/TickListener.kt new file mode 100644 index 000000000..f006d593c --- /dev/null +++ b/Server/src/main/core/api/TickListener.kt @@ -0,0 +1,14 @@ +package core.api + +/** + * An interface for writing content that allows the class to be updated each tick. + */ +interface TickListener : ContentInterface { + /** + * NOTE: This should NOT reference nonstatic class-local variables. + * TickListeners are generally for NON-player, WORLD tick events. + * Examples: Fishing spot rotation, grand exchange updates, puro puro randomization, etc. + * If you need something (player/entity)-specific, use an [api.events.EventHook] with the [api.events.TickEvent] + */ + fun tick() +} \ No newline at end of file diff --git a/Server/src/main/core/api/regionspec/RegionSpecification.kt b/Server/src/main/core/api/regionspec/RegionSpecification.kt new file mode 100644 index 000000000..863c98024 --- /dev/null +++ b/Server/src/main/core/api/regionspec/RegionSpecification.kt @@ -0,0 +1,33 @@ +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.build.DynamicRegion + +class RegionSpecification(val regionContract: RegionSpecContract = EmptyRegionContract(), vararg val chunkContracts: ChunkSpecContract = arrayOf(EmptyChunkContract())) { + constructor(vararg chunkContracts: ChunkSpecContract) : this(EmptyRegionContract(), *chunkContracts) + + fun build(): DynamicRegion { + val dyn = regionContract.instantiateRegion() + Region.load(dyn) + chunkContracts.forEach { it.populateChunks(dyn) } + return dyn + } +} + +fun fillWith(chunk: RegionChunk?): FillChunkContract { + return FillChunkContract(chunk) +} + +fun fillWith(delegate: (Int, Int, Int, Region) -> RegionChunk?) : FillChunkContract { + return FillChunkContract(delegate) +} + +fun copyOf(regionId: Int): RegionSpecContract { + return CloneRegionContract(regionId) +} + +fun using(region: DynamicRegion) : UseExistingRegionContract { + return UseExistingRegionContract(region) +} diff --git a/Server/src/main/core/api/regionspec/contracts/ChunkSpecContract.kt b/Server/src/main/core/api/regionspec/contracts/ChunkSpecContract.kt new file mode 100644 index 000000000..ea4bd936f --- /dev/null +++ b/Server/src/main/core/api/regionspec/contracts/ChunkSpecContract.kt @@ -0,0 +1,7 @@ +package core.api.regionspec.contracts + +import core.game.world.map.build.DynamicRegion + +interface ChunkSpecContract { + fun populateChunks(dyn: DynamicRegion) +} diff --git a/Server/src/main/core/api/regionspec/contracts/CloneRegionContract.kt b/Server/src/main/core/api/regionspec/contracts/CloneRegionContract.kt new file mode 100644 index 000000000..63b3279c1 --- /dev/null +++ b/Server/src/main/core/api/regionspec/contracts/CloneRegionContract.kt @@ -0,0 +1,9 @@ +package core.api.regionspec.contracts + +import core.game.world.map.build.DynamicRegion + +class CloneRegionContract(val regionId: Int) : RegionSpecContract { + override fun instantiateRegion(): DynamicRegion { + return DynamicRegion.create(regionId) + } +} diff --git a/Server/src/main/core/api/regionspec/contracts/EmptyChunkContract.kt b/Server/src/main/core/api/regionspec/contracts/EmptyChunkContract.kt new file mode 100644 index 000000000..3abd07f61 --- /dev/null +++ b/Server/src/main/core/api/regionspec/contracts/EmptyChunkContract.kt @@ -0,0 +1,7 @@ +package core.api.regionspec.contracts + +import core.game.world.map.build.DynamicRegion + +class EmptyChunkContract : ChunkSpecContract { + override fun populateChunks(dyn: DynamicRegion) {} +} diff --git a/Server/src/main/core/api/regionspec/contracts/EmptyRegionContract.kt b/Server/src/main/core/api/regionspec/contracts/EmptyRegionContract.kt new file mode 100644 index 000000000..a8f8725a3 --- /dev/null +++ b/Server/src/main/core/api/regionspec/contracts/EmptyRegionContract.kt @@ -0,0 +1,11 @@ +package core.api.regionspec.contracts + +import core.game.world.map.build.DynamicRegion + +class EmptyRegionContract : RegionSpecContract { + override fun instantiateRegion(): DynamicRegion { + val borders = DynamicRegion.reserveArea(8,8) + val dyn = DynamicRegion(borders) + return dyn + } +} diff --git a/Server/src/main/core/api/regionspec/contracts/FillChunkContract.kt b/Server/src/main/core/api/regionspec/contracts/FillChunkContract.kt new file mode 100644 index 000000000..a26d33b92 --- /dev/null +++ b/Server/src/main/core/api/regionspec/contracts/FillChunkContract.kt @@ -0,0 +1,54 @@ +package core.api.regionspec.contracts + +import core.game.world.map.BuildRegionChunk +import core.game.world.map.Region +import core.game.world.map.RegionChunk +import core.game.world.map.build.DynamicRegion + +open class FillChunkContract(var chunk: RegionChunk? = null) : ChunkSpecContract { + constructor(chunk: (Int, Int, Int, Region) -> RegionChunk?) : this(null) {this.chunkDelegate = chunk} + + lateinit var sourceRegion: Region + var planes: IntArray = intArrayOf(0) + var replaceCondition: (Int,Int,Int) -> Boolean = {_,_,_ -> true} + var chunkDelegate: (Int, Int, Int, Region) -> RegionChunk? = {_,_,_,_ -> chunk} + + override fun populateChunks(dyn: DynamicRegion) { + for(plane in planes) { + for(x in 0 until 8) + for(y in 0 until 8) + if(replaceCondition.invoke(x,y,plane)) { + val chunk = getChunk(x,y,plane,dyn) + dyn.replaceChunk( + plane, + x, + y, + chunk, + sourceRegion + ) + afterSetting(chunk, x, y, plane, dyn) + } + } + } + + open fun getChunk(x: Int, y: Int, plane: Int, dyn: DynamicRegion) : BuildRegionChunk? { + return chunkDelegate.invoke(x, y, plane, sourceRegion)?.copy(dyn.planes[plane]) + } + + open fun afterSetting(chunk: BuildRegionChunk?, x: Int, y: Int, plane: Int, dyn: DynamicRegion) {} + + fun from(region: Region): FillChunkContract { + this.sourceRegion = region + return this + } + + fun onPlanes(vararg planes: Int) : FillChunkContract { + this.planes = planes + return this + } + + fun onCondition(cond: (Int,Int,Int) -> Boolean): FillChunkContract { + this.replaceCondition = cond + return this + } +} diff --git a/Server/src/main/core/api/regionspec/contracts/RegionSpecContract.kt b/Server/src/main/core/api/regionspec/contracts/RegionSpecContract.kt new file mode 100644 index 000000000..3136bd48c --- /dev/null +++ b/Server/src/main/core/api/regionspec/contracts/RegionSpecContract.kt @@ -0,0 +1,7 @@ +package core.api.regionspec.contracts + +import core.game.world.map.build.DynamicRegion + +interface RegionSpecContract { + fun instantiateRegion(): DynamicRegion +} diff --git a/Server/src/main/core/api/regionspec/contracts/UseExistingRegionContract.kt b/Server/src/main/core/api/regionspec/contracts/UseExistingRegionContract.kt new file mode 100644 index 000000000..24e4bded4 --- /dev/null +++ b/Server/src/main/core/api/regionspec/contracts/UseExistingRegionContract.kt @@ -0,0 +1,9 @@ +package core.api.regionspec.contracts + +import core.game.world.map.build.DynamicRegion + +class UseExistingRegionContract(val region: DynamicRegion) : RegionSpecContract { + override fun instantiateRegion(): DynamicRegion { + 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 new file mode 100644 index 000000000..d2d95f672 --- /dev/null +++ b/Server/src/main/core/api/utils/CameraUtils.kt @@ -0,0 +1,23 @@ +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/NPCDropTable.kt b/Server/src/main/core/api/utils/NPCDropTable.kt new file mode 100644 index 000000000..defe06ea6 --- /dev/null +++ b/Server/src/main/core/api/utils/NPCDropTable.kt @@ -0,0 +1,28 @@ +package core.api.utils + +import core.game.node.entity.Entity +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) + } + + 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(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 new file mode 100644 index 000000000..8be7bed91 --- /dev/null +++ b/Server/src/main/core/api/utils/PlayerCamera.kt @@ -0,0 +1,52 @@ +package core.api.utils + +import core.game.node.entity.player.Player +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 + + fun setPosition(x: Int, y: Int, height: Int){ + player ?: return + ctx = CameraContext(player,CameraContext.CameraType.SET,x,y,height,0,0) + PacketRepository.send(CameraViewPacket::class.java,ctx) + } + fun rotateTo(x: Int, y: Int, height: Int, speed: Int){ + player ?: return + ctx = CameraContext(player,CameraContext.CameraType.ROTATION,x,y,height,speed,1) + PacketRepository.send(CameraViewPacket::class.java,ctx) + } + fun rotateBy(diffX: Int, diffY: Int, diffHeight: Int, speed: Int){ + player ?: return + ctx ?: return + ctx = CameraContext(player,CameraContext.CameraType.ROTATION, ctx!!.x + diffX, ctx!!.y + diffY, ctx!!.height + diffHeight,speed,1) + PacketRepository.send(CameraViewPacket::class.java,ctx) + } + fun panTo(x: Int, y: Int, height: Int, speed: Int){ + player ?: return + 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 new file mode 100644 index 000000000..39505c241 --- /dev/null +++ b/Server/src/main/core/api/utils/WeightBasedTable.kt @@ -0,0 +1,233 @@ +package core.api.utils + +import core.cache.def.impl.ItemDefinition +import content.global.activity.ttrail.ClueLevel +import content.global.activity.ttrail.ClueScrollPlugin +import content.data.tables.RareDropTable +import content.data.tables.CELEMinorTable +import content.data.tables.UncommonSeedDropTable +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 +import core.tools.RandomFunction +import org.rs09.consts.Items + +open class WeightBasedTable : ArrayList() { + var totalWeight = 0.0 + val guaranteedItems = ArrayList() + + override fun add(element: WeightedItem): Boolean { + return if (element.guaranteed) { + 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 { + return roll(receiver, 1) + } + + 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(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 { + val safeItems = ArrayList() + for (e in weightedItems) { + val safeItem = when (e.id) { + SLOT_CLUE_EASY -> ClueScrollPlugin.getClue(ClueLevel.EASY) + SLOT_CLUE_MEDIUM -> ClueScrollPlugin.getClue(ClueLevel.MEDIUM) + SLOT_CLUE_HARD -> ClueScrollPlugin.getClue(ClueLevel.HARD) + SLOT_RDT -> RareDropTable.retrieve(receiver) + SLOT_CELEDT -> CELEMinorTable.retrieve() + SLOT_USDT -> UncommonSeedDropTable.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() + Items.DWARF_REMAINS_0 -> continue + else -> e.getItem() + } + safeItems.add(safeItem ?: continue) + } + return safeItems + } + + open fun canRoll(player: Player): Boolean{ + val guaranteed = guaranteedItems.map { it.getItem() }.toTypedArray() + return (guaranteed.isNotEmpty() && player.inventory.hasSpaceFor(*guaranteed)) || !player.inventory.isFull + } + + fun insertEasyClue(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_CLUE_EASY,1,1,weight,false)) + return this + } + + fun insertMediumClue(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_CLUE_MEDIUM,1,1,weight,false)) + return this + } + + fun insertHardClue(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_CLUE_HARD,1,1,weight,false)) + return this + } + + fun insertRDTRoll(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_RDT,1,1,weight,false)) + return this + } + + fun insertCELEDTRoll(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_CELEDT,1,1,weight,false)) + return this + } + + fun insertSEEDDTRoll(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_USDT,1,1,weight,false)) + return this + } + + fun insertHERBDTRoll(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_HDT,1,1,weight,false)) + return this + } + + fun insertGDTRoll(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_GDT,1,1,weight,false)) + return this + } + + fun insertRSDTRoll(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_RSDT,1,1,weight,false)) + return this + } + + fun insertASDTRoll(weight: Double): WeightBasedTable { + this.add(WeightedItem(SLOT_ASDT,1,1,weight,false)) + 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 { + val table = WeightBasedTable() + items.forEach { + table.add(it) + } + return table + } + + @JvmField + val SLOT_RDT = Items.TINDERBOX_31 + 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 + val SLOT_GDT = Items.SACRED_CLAY_POUCH_CLASS_3_14426 + val SLOT_RSDT = Items.SACRED_CLAY_POUCH_CLASS_4_14428 + val SLOT_ASDT = Items.SACRED_CLAY_POUCH_CLASS_5_14430 + } + + override fun toString(): String { + val builder = StringBuilder() + for(item in this){ + builder.append("${ItemDefinition.forId(item.id).name} || Weight: ${item.weight} || MinAmt: ${item.minAmt} || maxAmt: ${item.maxAmt}") + builder.appendLine() + } + return builder.toString() + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/global/WeightedItem.kt b/Server/src/main/core/api/utils/WeightedItem.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/content/global/WeightedItem.kt rename to Server/src/main/core/api/utils/WeightedItem.kt index c2c7cff3b..adba25ebb 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/WeightedItem.kt +++ b/Server/src/main/core/api/utils/WeightedItem.kt @@ -1,4 +1,4 @@ -package rs09.game.content.global +package core.api.utils import core.game.node.item.Item import core.tools.RandomFunction 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/auth/Auth.kt b/Server/src/main/core/auth/Auth.kt new file mode 100644 index 000000000..8d55aff71 --- /dev/null +++ b/Server/src/main/core/auth/Auth.kt @@ -0,0 +1,23 @@ +package core.auth + +import core.ServerConstants +import core.storage.AccountStorageProvider +import core.storage.InMemoryStorageProvider +import core.storage.SQLStorageProvider + +object Auth { + lateinit var authenticator: AuthProvider<*> + lateinit var storageProvider: AccountStorageProvider + + fun configure() { + storageProvider = if (ServerConstants.PERSIST_ACCOUNTS) + SQLStorageProvider() + else + InMemoryStorageProvider() + + authenticator = if (ServerConstants.USE_AUTH) + ProductionAuthenticator().also { it.configureFor(storageProvider) } + else + DevelopmentAuthenticator().also { it.configureFor(storageProvider) } + } +} \ No newline at end of file diff --git a/Server/src/main/core/auth/AuthProvider.kt b/Server/src/main/core/auth/AuthProvider.kt new file mode 100644 index 000000000..69db563c7 --- /dev/null +++ b/Server/src/main/core/auth/AuthProvider.kt @@ -0,0 +1,22 @@ +package core.auth + +import core.game.node.entity.player.Player +import core.storage.AccountStorageProvider + +abstract class AuthProvider { + lateinit var storageProvider: T + + abstract fun configureFor(provider: T) + + fun canCreateAccountWith(info: UserAccountInfo) : Boolean { + return !storageProvider.checkUsernameTaken(info.username) + } + + abstract fun createAccountWith(info: UserAccountInfo) : Boolean + + abstract fun checkLogin(username: String, password: String) : Pair + + abstract fun checkPassword(player: Player, password: String) : Boolean + + abstract fun updatePassword(username: String, newPassword: String) +} diff --git a/Server/src/main/core/auth/AuthResponse.kt b/Server/src/main/core/auth/AuthResponse.kt new file mode 100644 index 000000000..379d927ff --- /dev/null +++ b/Server/src/main/core/auth/AuthResponse.kt @@ -0,0 +1,27 @@ +package core.auth + +enum class AuthResponse { + UnexpectedError, + CouldNotAd, + Success, + InvalidCredentials, + AccountDisabled, + AlreadyOnline, + Updated, + FullWorld, + LoginServerOffline, + LoginLimitExceeded, + BadSessionID, + WeakPassword, + MembersWorld, + CouldNotLogin, + Updating, + TooManyIncorrectLogins, + StandingInMembersArea, + AccountLocked, + ClosedBeta, + InvalidLoginServer, + MovingWorld, + ErrorLoadingProfile, + BannedUser +} diff --git a/Server/src/main/core/auth/DevelopmentAuthenticator.kt b/Server/src/main/core/auth/DevelopmentAuthenticator.kt new file mode 100644 index 000000000..eb26aa483 --- /dev/null +++ b/Server/src/main/core/auth/DevelopmentAuthenticator.kt @@ -0,0 +1,41 @@ +package core.auth + +import core.game.node.entity.player.Player +import core.ServerConstants +import core.storage.AccountStorageProvider + +class DevelopmentAuthenticator : AuthProvider() { + override fun configureFor(provider: AccountStorageProvider) { + storageProvider = provider + } + + override fun checkLogin(username: String, password: String): Pair { + val info: UserAccountInfo + if(!storageProvider.checkUsernameTaken(username.toLowerCase())) { + info = UserAccountInfo.createDefault() + info.username = username + createAccountWith(info) + } else { + info = storageProvider.getAccountInfo(username.toLowerCase()) + } + return Pair(AuthResponse.Success, storageProvider.getAccountInfo(username)) + } + + override fun createAccountWith(info: UserAccountInfo): Boolean { + info.username = info.username.toLowerCase() + if (ServerConstants.NOAUTH_DEFAULT_ADMIN) + info.rights = 2 + storageProvider.store(info) + return true + } + + override fun checkPassword(player: Player, password: String): Boolean { + return password == player.details.password + } + + override fun updatePassword(username: String, newPassword: String) { + val info = storageProvider.getAccountInfo(username) + info.password = newPassword + storageProvider.update(info) + } +} diff --git a/Server/src/main/core/auth/ProductionAuthenticator.kt b/Server/src/main/core/auth/ProductionAuthenticator.kt new file mode 100644 index 000000000..b18592bca --- /dev/null +++ b/Server/src/main/core/auth/ProductionAuthenticator.kt @@ -0,0 +1,64 @@ +package core.auth + +import core.game.node.entity.player.Player +import core.game.system.SystemManager +import core.ServerConstants +import core.game.world.repository.Repository +import core.storage.AccountStorageProvider +import core.storage.SQLStorageProvider +import java.sql.SQLDataException +import java.sql.Timestamp + +class ProductionAuthenticator : AuthProvider() { + override fun configureFor(provider: AccountStorageProvider) { + storageProvider = provider + if (provider is SQLStorageProvider) { + provider.configure(ServerConstants.DATABASE_ADDRESS!!, ServerConstants.DATABASE_NAME!!, ServerConstants.DATABASE_USER!!, ServerConstants.DATABASE_PASS!!) + } + } + + override fun createAccountWith(info: UserAccountInfo): Boolean { + try { + info.password = SystemManager.getEncryption().hashPassword(info.password) + info.joinDate = Timestamp(System.currentTimeMillis()) + storageProvider.store(info) + } catch (e: SQLDataException) { + return false + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + override fun checkLogin(username: String, password: String): Pair { + val info: UserAccountInfo + try { + if (!storageProvider.checkUsernameTaken(username.toLowerCase())) { + return Pair(AuthResponse.InvalidCredentials, null) + } + info = storageProvider.getAccountInfo(username.toLowerCase()) + val passCorrect = SystemManager.getEncryption().checkPassword(password, info.password) + if(!passCorrect || info.password.isEmpty()) + return Pair(AuthResponse.InvalidCredentials, null) + if(info.banEndTime > System.currentTimeMillis()) + return Pair(AuthResponse.AccountDisabled, null) + if(info.online) + return Pair(AuthResponse.AlreadyOnline, null) + } catch (e: Exception) { + e.printStackTrace() + return Pair(AuthResponse.CouldNotLogin, null) + } + return Pair(AuthResponse.Success, info) + } + + override fun checkPassword(player: Player, password: String): Boolean { + return SystemManager.getEncryption().checkPassword(password, player.details.password) + } + + override fun updatePassword(username: String, newPassword: String) { + val info = storageProvider.getAccountInfo(username) + info.password = SystemManager.getEncryption().hashPassword(newPassword) + storageProvider.update(info) + } +} \ No newline at end of file diff --git a/Server/src/main/core/auth/UserAccountInfo.kt b/Server/src/main/core/auth/UserAccountInfo.kt new file mode 100644 index 000000000..58f8e37e6 --- /dev/null +++ b/Server/src/main/core/auth/UserAccountInfo.kt @@ -0,0 +1,108 @@ +package core.auth + +import java.sql.Timestamp + +class UserAccountInfo( + var username: String, + var password: String, + var uid: Int, + var rights: Int, + var credits: Int, + var ip: String, + var lastUsedIp: String, + var muteEndTime: Long, + var banEndTime: Long, + var contacts: String, + var blocked: String, + var clanName: String, + var currentClan: String, + var clanReqs: String, + var timePlayed: Long, + var lastLogin: Long, + var online: Boolean, + var joinDate: Timestamp +) { + companion object { + val default = createDefault() + @JvmStatic fun createDefault() : UserAccountInfo { + return UserAccountInfo("", "", 0, 0, 0, "", "", 0L, 0L, "", "", "", "", "1,0,8,9", 0L, 0L, false, joinDate = Timestamp(System.currentTimeMillis())).also { it.setInitialReferenceValues() } + } + } + + lateinit var initialValues: Array + + fun setInitialReferenceValues() { + initialValues = toArray() + } + + fun getChangedFields(): Pair, Array> { + val current = toArray() + val changed = ArrayList() + + for(i in current.indices) { + if (current[i] != initialValues[i]) changed.add(i) + } + + return Pair(changed, current) + } + + fun toArray(): Array { + return arrayOf(username, password, uid, rights, credits, ip, lastUsedIp, muteEndTime, banEndTime, contacts, blocked, clanName, currentClan, clanReqs, timePlayed, lastLogin, online, joinDate) + } + + override fun toString(): String { + return "USER:$username,PASS:$password,UID:$uid,RIGHTS:$rights,CREDITS:$credits,IP:$ip,LASTIP:$lastUsedIp" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as UserAccountInfo + + if (username != other.username) return false + if (password != other.password) return false + if (uid != other.uid) return false + if (rights != other.rights) return false + if (credits != other.credits) return false + if (ip != other.ip) return false + if (lastUsedIp != other.lastUsedIp) return false + if (muteEndTime != other.muteEndTime) return false + if (banEndTime != other.banEndTime) return false + if (contacts != other.contacts) return false + if (blocked != other.blocked) return false + if (clanName != other.clanName) return false + if (currentClan != other.currentClan) return false + if (clanReqs != other.clanReqs) return false + if (timePlayed != other.timePlayed) return false + if (lastLogin != other.lastLogin) return false + if (online != other.online) return false + + return true + } + + override fun hashCode(): Int { + var result = username.hashCode() + result = 31 * result + password.hashCode() + result = 31 * result + uid + result = 31 * result + rights + result = 31 * result + credits + result = 31 * result + ip.hashCode() + result = 31 * result + lastUsedIp.hashCode() + result = 31 * result + muteEndTime.hashCode() + result = 31 * result + banEndTime.hashCode() + result = 31 * result + contacts.hashCode() + result = 31 * result + blocked.hashCode() + result = 31 * result + clanName.hashCode() + result = 31 * result + currentClan.hashCode() + result = 31 * result + clanReqs.hashCode() + result = 31 * result + timePlayed.hashCode() + result = 31 * result + lastLogin.hashCode() + result = 31 * result + online.hashCode() + return result + } + + fun isDefault() : Boolean { + return this == default + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/cache/Cache.java b/Server/src/main/core/cache/Cache.java similarity index 93% rename from Server/src/main/java/core/cache/Cache.java rename to Server/src/main/core/cache/Cache.java index 0194eb874..3459d58b9 100644 --- a/Server/src/main/java/core/cache/Cache.java +++ b/Server/src/main/core/cache/Cache.java @@ -4,13 +4,16 @@ import java.io.File; import java.io.RandomAccessFile; import java.nio.ByteBuffer; -import rs09.ServerConstants; +import core.ServerConstants; import core.cache.def.impl.AnimationDefinition; 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 rs09.game.system.SystemLogger; +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("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("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("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/java/core/cache/CacheFile.java b/Server/src/main/core/cache/CacheFile.java similarity index 97% rename from Server/src/main/java/core/cache/CacheFile.java rename to Server/src/main/core/cache/CacheFile.java index a03642895..bd21a8a0d 100644 --- a/Server/src/main/java/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/java/core/cache/CacheFileManager.java b/Server/src/main/core/cache/CacheFileManager.java similarity index 100% rename from Server/src/main/java/core/cache/CacheFileManager.java rename to Server/src/main/core/cache/CacheFileManager.java diff --git a/Server/src/main/java/core/cache/bzip2/BZip2BlockEntry.java b/Server/src/main/core/cache/bzip2/BZip2BlockEntry.java similarity index 100% rename from Server/src/main/java/core/cache/bzip2/BZip2BlockEntry.java rename to Server/src/main/core/cache/bzip2/BZip2BlockEntry.java diff --git a/Server/src/main/java/core/cache/bzip2/BZip2Decompressor.java b/Server/src/main/core/cache/bzip2/BZip2Decompressor.java similarity index 100% rename from Server/src/main/java/core/cache/bzip2/BZip2Decompressor.java rename to Server/src/main/core/cache/bzip2/BZip2Decompressor.java 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.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.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/java/core/cache/def/Definition.java b/Server/src/main/core/cache/def/Definition.java similarity index 100% rename from Server/src/main/java/core/cache/def/Definition.java rename to Server/src/main/core/cache/def/Definition.java diff --git a/Server/src/main/java/core/cache/def/impl/AnimationDefinition.java b/Server/src/main/core/cache/def/impl/AnimationDefinition.java similarity index 95% rename from Server/src/main/java/core/cache/def/impl/AnimationDefinition.java rename to Server/src/main/core/cache/def/impl/AnimationDefinition.java index 7eb25798f..1f56d91cd 100644 --- a/Server/src/main/java/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. @@ -141,7 +149,7 @@ public final class AnimationDefinition { int i_22_ = buffer.get() & 0xFF; if ((i_22_ ^ 0xffffffff) < -1) { handledSounds[i_21_] = new int[i_22_]; - handledSounds[i_21_][0] = ByteBufferUtils.getTriByte(buffer); + handledSounds[i_21_][0] = ByteBufferUtils.getMedium(buffer); for (int i_23_ = 1; ((i_22_ ^ 0xffffffff) < (i_23_ ^ 0xffffffff)); i_23_++) { handledSounds[i_21_][i_23_] = buffer.getShort() & 0xFFFF; } @@ -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/java/core/cache/def/impl/CS2Mapping.java b/Server/src/main/core/cache/def/impl/CS2Mapping.java similarity index 98% rename from Server/src/main/java/core/cache/def/impl/CS2Mapping.java rename to Server/src/main/core/cache/def/impl/CS2Mapping.java index cc1914b50..440c06c67 100644 --- a/Server/src/main/java/core/cache/def/impl/CS2Mapping.java +++ b/Server/src/main/core/cache/def/impl/CS2Mapping.java @@ -1,7 +1,6 @@ package core.cache.def.impl; import java.io.BufferedWriter; -import java.io.FileWriter; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Paths; @@ -10,7 +9,7 @@ import java.util.Map; import core.cache.Cache; import core.cache.misc.buffer.ByteBufferUtils; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * The CS2 mapping. diff --git a/Server/src/main/java/core/cache/def/impl/ClothDefinition.java b/Server/src/main/core/cache/def/impl/ClothDefinition.java similarity index 92% rename from Server/src/main/java/core/cache/def/impl/ClothDefinition.java rename to Server/src/main/core/cache/def/impl/ClothDefinition.java index fb34666fa..c100c16ef 100644 --- a/Server/src/main/java/core/cache/def/impl/ClothDefinition.java +++ b/Server/src/main/core/cache/def/impl/ClothDefinition.java @@ -1,9 +1,8 @@ package core.cache.def.impl; import java.nio.ByteBuffer; -import java.util.Arrays; -import rs09.ServerConstants; +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 new file mode 100644 index 000000000..bdf4c82cc --- /dev/null +++ b/Server/src/main/core/cache/def/impl/DataMap.java @@ -0,0 +1,117 @@ +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 { + + /** + * The config definitions mapping. + */ + private static final Map definitions = new HashMap<>(); + + /** + * The enum id. + */ + private final int id; + + public char keyType = '?'; + + public char valueType = '?'; + + public String defaultString; + + public int defaultInt; + + public HashMap dataStore = new HashMap<>(); + + public DataMap(int id) { + this.id = id; + } + + public int getInt(int key){ + if(!dataStore.containsKey(key)){ + log(this.getClass(), Log.ERR, "Invalid value passed for key: " + key + " map: " + id); + return -1; + } + return (int) dataStore.get(key); + } + + public String getString(int key){ + return (String) dataStore.get(key); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + return "DataMapDefinition{" + + "id=" + id + + ", keyType=" + keyType + + ", valueType=" + (valueType == 'K' ? "Normal" : valueType == 'J' ? "Struct Pointer" : "Unknown") + + ", defaultString='" + defaultString + '\'' + + ", defaultInt=" + defaultInt + + ", dataStore=" + dataStore + + '}' + "\n"; + } + + public static DataMap get(int id){ + core.cache.def.impl.DataMap def = definitions.get(id); + if (def != null) { + return def; + } + byte[] data = Cache.getIndexes()[17].getFileData(id >>> 8, id & 0xFF); + def = parse(id, data); + definitions.put(id, def); + return def; + } + + public static DataMap parse(int id, byte[] data) + { + DataMap def = new DataMap(id); + if (data != null) { + ByteBuffer buffer = ByteBuffer.wrap(data); + int opcode; + + while ((opcode = buffer.get() & 0xFF) != 0) { + + if (opcode == 1) { + def.keyType = CP1252.getFromByte(buffer.get()); + } else if (opcode == 2) { + def.valueType = CP1252.getFromByte(buffer.get()); + } else if (opcode == 3) { + def.defaultString = ByteBufferUtils.getString(buffer); + } else if (opcode == 4) { + def.defaultInt = buffer.getInt(); + } else if (opcode == 5 || opcode == 6) { + int size = buffer.getShort() & 0xFFFF; + + for (int i = 0; i < size; i++) { + int key = buffer.getInt(); + + Object value; + if (opcode == 5) { + value = ByteBufferUtils.getString(buffer); + } else { + value = buffer.getInt(); + } + def.dataStore.put(key, value); + } + } + } + } + return def; + } + + public int getId() { + return id; + } +} diff --git a/Server/src/main/core/cache/def/impl/GraphicDefinition.java b/Server/src/main/core/cache/def/impl/GraphicDefinition.java new file mode 100644 index 000000000..866aa8050 --- /dev/null +++ b/Server/src/main/core/cache/def/impl/GraphicDefinition.java @@ -0,0 +1,189 @@ +package core.cache.def.impl; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import core.ServerConstants; +import core.cache.Cache; + +/** + * Represents a Graphic's definition. + * @author Jagex + */ +public class GraphicDefinition { + + public short[] aShortArray1435; + public short[] aShortArray1438; + public int anInt1440; + public boolean aBoolean1442; + public int defaultModel; + public int anInt1446; + public boolean aBoolean1448 = false; + public int anInt1449; + public int animationId; + public int anInt1451; + public int graphicsId; + public int anInt1454; + public short[] aShortArray1455; + public short[] aShortArray1456; + + // added + public byte byteValue; + // added + public int intValue; + + /** + * The definitions mapping. + */ + private static final Map graphicDefinitions = new HashMap<>(); + + /** + * Gets the graphic definition for the given graphic id. + * @param gfxId The graphic id. + * @return The definition. + */ + public static final GraphicDefinition forId(int gfxId) { + GraphicDefinition def = graphicDefinitions.get(gfxId); + if (def != null) { + return def; + } + byte[] data = Cache.getIndexes()[21].getFileData(gfxId >>> 8, gfxId & 0xff); + def = new GraphicDefinition(); + def.graphicsId = gfxId; + if (data != null) { + def.readValueLoop(ByteBuffer.wrap(data)); + } + graphicDefinitions.put(gfxId, def); + return def; + } + + /** + * The main method, used for running a graphic definition search. + * @param s The arguments cast on runtime. + */ + public static final void main(String... s) { + try { + Cache.init(ServerConstants.CACHE_PATH); + } catch (Throwable e) { + e.printStackTrace(); + } + // 5046 - 5050 are related anims & 2148 + GraphicDefinition d = GraphicDefinition.forId(803); + + 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)) { + + } + } + } + + /** + * Reads and handles all data from the input stream. + * @param buffer The input stream. + */ + private void readValueLoop(ByteBuffer buffer) { + for (;;) { + int opcode = buffer.get() & 0xFF; + if (opcode == 0) { + break; + } + readValues(buffer, opcode); + } + } + + /** + * Reads the opcode values from the input stream. + * @param buffer The input stream. + * @param opcode The opcode to handle. + */ + public void readValues(ByteBuffer buffer, int opcode) { + if (opcode != 1) { + if (opcode == 2) + animationId = buffer.getShort(); + else if (opcode == 4) + anInt1446 = buffer.getShort() & 0xFFFF; + else if (opcode != 5) { + if ((opcode ^ 0xffffffff) != -7) { + if (opcode == 7) + anInt1440 = buffer.get() & 0xFF; + else if ((opcode ^ 0xffffffff) == -9) + anInt1451 = buffer.get() & 0xFF; + else if (opcode != 9) { + if (opcode != 10) { + if (opcode == 11) { // added opcode + // aBoolean1442 = true; + byteValue = (byte) 1; + } else if (opcode == 12) { // added opcode + // aBoolean1442 = true; + byteValue = (byte) 4; + } else if (opcode == 13) { // added opcode + // aBoolean1442 = true; + byteValue = (byte) 5; + } else if (opcode == 14) { // added opcode + // aBoolean1442 = true; + // aByte2856 = 2; + byteValue = (byte) 2; + intValue = (buffer.get() & 0xFF) * 256; + } else if (opcode == 15) { + // aByte2856 = 3; + byteValue = (byte) 3; + intValue = buffer.getShort() & 0xFFFF; + } else if (opcode == 16) { + // aByte2856 = 3; + byteValue = (byte) 3; + intValue = buffer.getInt(); + } else if (opcode != 40) { + if ((opcode ^ 0xffffffff) == -42) { + int i = buffer.get() & 0xFF; + aShortArray1455 = new short[i]; + aShortArray1435 = new short[i]; + for (int i_0_ = 0; i > i_0_; i_0_++) { + aShortArray1455[i_0_] = (short) (buffer.getShort() & 0xFFFF); + aShortArray1435[i_0_] = (short) (buffer.getShort() & 0xFFFF); + } + } + } else { + int i = buffer.get() & 0xFF; + aShortArray1438 = new short[i]; + aShortArray1456 = new short[i]; + for (int i_1_ = 0; ((i ^ 0xffffffff) < (i_1_ ^ 0xffffffff)); i_1_++) { + aShortArray1438[i_1_] = (short) (buffer.getShort() & 0xFFFF); + aShortArray1456[i_1_] = (short) (buffer.getShort() & 0xFFFF); + } + } + } else + aBoolean1448 = true; + } else { + // aBoolean1442 = true; + byteValue = (byte) 3; + intValue = 8224; + } + } else + anInt1454 = buffer.getShort() & 0xFFFF; + } else + anInt1449 = buffer.getShort() & 0xFFFF; + } else + defaultModel = buffer.getShort(); + } + + /** + * Constructs a new {@code GraphicDefinition} {@code Object}. + */ + public GraphicDefinition() { + byteValue = 0; + intValue = -1; + anInt1446 = 128; + aBoolean1442 = false; + anInt1449 = 128; + anInt1451 = 0; + animationId = -1; + anInt1454 = 0; + anInt1440 = 0; + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..b7c17e5ec --- /dev/null +++ b/Server/src/main/core/cache/def/impl/ItemDefinition.java @@ -0,0 +1,1699 @@ +package core.cache.def.impl; + +import core.api.EquipmentSlot; +import core.cache.Cache; +import core.cache.def.Definition; +import core.cache.misc.buffer.ByteBufferUtils; +import core.game.container.Container; +import core.game.interaction.OptionHandler; +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.game.world.GameWorld; +import core.tools.Log; +import core.tools.StringUtils; +import core.game.system.config.ItemConfigParser; +import org.rs09.consts.Items; + +import java.nio.ByteBuffer; +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 Emperor + */ +public class ItemDefinition extends Definition { + + /** + * The item definitions mapping. + */ + private static final Map DEFINITIONS = new HashMap<>(); + + /** + * The default option handlers. + */ + private static final Map OPTION_HANDLERS = new HashMap<>(); + + /** + * The interface model id. + */ + private int interfaceModelId; + + /** + * The model zoom. + */ + private int modelZoom; + + /** + * The model rotation. + */ + private int modelRotationX; + + /** + * The model rotation. + */ + private int modelRotationY; + + /** + * The model offset. + */ + private int modelOffsetX; + + /** + * The model offset. + */ + private int modelOffsetY; + + /** + * If item is stackable. + */ + private boolean stackable; + + /** + * The item value. + */ + private int value = 1; + + /** + * If item is members only. + */ + private boolean membersOnly; + + /** + * The male model wear id. + */ + private int maleWornModelId1 = -1; + + /** + * The female model wear id. + */ + private int femaleWornModelId1 = -1; + + /** + * The male model wear id. + */ + private int maleWornModelId2 = -1; + + /** + * The female wear model id. + */ + private int femaleWornModelId2 = -1; + + /** + * The male model wear id. + */ + private int maleWornModelId3 = -1; + + /** + * The female model wear id. + */ + private int femaleWornModelId3 = -1; + + /** + * The male model wear id. + */ + private int maleWornModelId4 = -1; + + /** + * The female wear model id. + */ + private int femaleWornModelId4 = -1; + + /** + * The ground actions. + */ + private String[] groundActions; + + /** + * The original model colors. + */ + private short[] originalModelColors; + + /** + * The modified model colors. + */ + private short[] modifiedModelColors; + + /** + * The texture color 1. + */ + private short[] textureColour1; + + /** + * The texture color 2. + */ + private short[] textureColour2; + + /** + * A unknown byte array. + */ + private byte[] unknownArray1; + + /** + * A unknown integer array. + */ + private int[] unknownArray2; + + /** + * A unknown integer array. + */ + private int[][] unknownArray3; + + /** + * If item is noted. + */ + private boolean unnoted = true; + + /** + * The colour equipment. + */ + private int colourEquip1 = -1; + + /** + * The colour equipment. + */ + private int colourEquip2; + + /** + * The note item id if un-noted. The un-noted item id if noted. -1 if no noted counterpart. + */ + private int noteId = -1; + + /** + * The note template id. + */ + private int noteTemplateId = -1; + + /** + * The stackable ids. + */ + private int[] stackIds; + + /** + * The stackable amounts. + */ + private int[] stackAmounts; + + /** + * The team id. + */ + private int teamId; + + /** + * The lend id. + */ + private int lendId = -1; + + /** + * The lend template id. + */ + private int lendTemplateId = -1; + + /** + * The recolour id. + */ + private int recolourId = -1; + + /** + * The recolour template id. + */ + private int recolourTemplateId = -1; + + /** + * The equip id. + */ + private int equipId; + + /** + * The item requirements + */ + private HashMap itemRequirements; + + /** + * The clientscript data. + */ + private HashMap clientScriptData; + + /** + * The item type. + */ + private int itemType; + + /** + * Constructs a new {@code ItemDefinition} {@code Object}. + */ + public ItemDefinition() { + groundActions = new String[] { null, null, "take", null, null }; + options = new String[] { null, null, null, null, "drop" }; + } + + /** + * Parses the item definitions. + */ + public static void parse() { + for (int itemId = 0; itemId < Cache.getItemDefinitionsSize(); itemId++) { + byte[] data = Cache.getIndexes()[19].getFileData(itemId >>> 8, itemId & 0xFF); + if (data == null) { + ItemDefinition.getDefinitions().put(itemId, new ItemDefinition()); + continue; + } + ItemDefinition def = ItemDefinition.parseDefinition(itemId, ByteBuffer.wrap(data)); + if (def == null) { + 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(); + } + + /** + * Gets an item definition. + * @param itemId The item's id. + * @return The item definition. + */ + public static ItemDefinition forId(int itemId) { + ItemDefinition def = DEFINITIONS.get(itemId); + if (def != null) { + return def; + } + return new ItemDefinition(); + } + + /** + * Parses an item's definitions. + * @param itemId The item id. + * @param buffer The buffer. + * @return The item definition. + */ + public static ItemDefinition parseDefinition(int itemId, ByteBuffer buffer) { + ItemDefinition def = new ItemDefinition(); + def.id = itemId; + while (true) { + int opcode = buffer.get() & 0xFF; + if (opcode == 0) { + break; + } else if (opcode == 1) { + def.interfaceModelId = buffer.getShort() & 0xFFFF; + } else if (opcode == 2) { + def.name = ByteBufferUtils.getString(buffer); + } else if (opcode == 3) { + def.handlers.put("examine", ByteBufferUtils.getString(buffer)); // Examine + // info. + } else if (opcode == 4) { + def.modelZoom = buffer.getShort() & 0xFFFF; + } else if (opcode == 5) { + 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) + def.modelOffsetX -= 65536; + } else if (opcode == 8) { + def.modelOffsetY = buffer.getShort() & 0xFFFF; + if (def.modelOffsetY > 32767) { + def.modelOffsetY -= 65536; + } + } else if (opcode == 10) { + // buffer.getShort(); //10 is unused opcode. + } else if (opcode == 11) { + def.stackable = true; + } else if (opcode == 12) { + def.value = ((buffer.get() & 0xFF) << 24) + ((buffer.get() & 0xFF) << 16) + ((buffer.get() & 0xFF) << 8) + (buffer.get() & 0xFF); + } else if (opcode == 16) { + def.membersOnly = true; + } else if (opcode == 23) { + def.maleWornModelId1 = buffer.getShort() & 0xFFFF; + // buffer.get(); + } else if (opcode == 24) { + def.femaleWornModelId1 = buffer.getShort() & 0xFFFF; + } else if (opcode == 25) { + def.maleWornModelId2 = buffer.getShort() & 0xFFFF; + // buffer.get(); + } else if (opcode == 26) { + def.femaleWornModelId2 = buffer.getShort() & 0xFFFF; + } else if (opcode >= 30 && opcode < 35) { + def.groundActions[opcode - 30] = ByteBufferUtils.getString(buffer); + } else if (opcode >= 35 && opcode < 40) { + def.options[opcode - 35] = ByteBufferUtils.getString(buffer); + } else if (opcode == 40) { + int length = buffer.get() & 0xFF; + def.originalModelColors = new short[length]; + def.modifiedModelColors = new short[length]; + for (int index = 0; index < length; index++) { + def.originalModelColors[index] = buffer.getShort(); + def.modifiedModelColors[index] = buffer.getShort(); + } + } else if (opcode == 41) { + int length = buffer.get() & 0xFF; + def.textureColour1 = new short[length]; + def.textureColour2 = new short[length]; + for (int index = 0; index < length; index++) { + def.textureColour1[index] = buffer.getShort(); + def.textureColour2[index] = buffer.getShort(); + } + } else if (opcode == 42) { + int length = buffer.get() & 0xFF; + def.unknownArray1 = new byte[length]; + for (int index = 0; index < length; index++) + def.unknownArray1[index] = buffer.get(); + } else if (opcode == 65) { + def.unnoted = true; + } else if (opcode == 78) { + def.colourEquip1 = buffer.getShort() & 0xFFFF; + } else if (opcode == 79) { + def.colourEquip2 = buffer.getShort() & 0xFFFF; + } else if (opcode == 90) { + def.setMaleWornModelId3(buffer.getShort()); + } else if (opcode == 91) { + def.setFemaleWornModelId3(buffer.getShort()); + } else if (opcode == 92) { + def.setMaleWornModelId4(buffer.getShort()); + } else if (opcode == 93) { + def.setFemaleWornModelId4(buffer.getShort()); + } else if (opcode == 95) { + buffer.getShort(); + } else if (opcode == 96) { + def.itemType = buffer.get(); + } else if (opcode == 97) { + def.noteId = buffer.getShort() & 0xFFFF; + } else if (opcode == 98) { + def.noteTemplateId = buffer.getShort() & 0xFFFF; + } else if (opcode >= 100 && opcode < 110) { + if (def.stackIds == null) { + def.stackIds = new int[10]; + def.stackAmounts = new int[10]; + } + def.stackIds[opcode - 100] = buffer.getShort() & 0xFFFF; + def.stackAmounts[opcode - 100] = buffer.getShort() & 0xFFFF; + } else if (opcode == 110) { + buffer.getShort(); + } else if (opcode == 111) { + buffer.getShort(); + } else if (opcode == 112) { + buffer.getShort(); + } else if (opcode == 113) { + buffer.get(); + } else if (opcode == 114) { + buffer.get(); + } else if (opcode == 115) { + def.teamId = buffer.get(); + } else if (opcode == 121) { + def.lendId = buffer.getShort() & 0xFFFF; + } else if (opcode == 122) { + def.lendTemplateId = buffer.getShort() & 0xFFFF; + } else if (opcode == 125) { + buffer.get(); + buffer.get(); + buffer.get(); + } else if (opcode == 126) { + buffer.get(); + buffer.get(); + buffer.get(); + } else if (opcode == 127) { + buffer.get(); + buffer.getShort(); + } else if (opcode == 128) { + buffer.get(); + buffer.getShort(); + } else if (opcode == 129) { + buffer.get(); + buffer.getShort(); + } else if (opcode == 130) { + buffer.get(); + buffer.getShort(); + } else if (opcode == 249) { + int length = buffer.get() & 0xFF; + if (def.clientScriptData == null) { + def.clientScriptData = new HashMap(); + } + for (int index = 0; index < length; index++) { + boolean string = (buffer.get() & 0xFF) == 1; + int key = ByteBufferUtils.getMedium(buffer); + Object value = string ? ByteBufferUtils.getString(buffer) : buffer.getInt(); + def.clientScriptData.put(key, value); + } + } else { + + break; + } + } + return def; + } + + /** + * Defines the definitions for noted, lending and recolored items. + */ + public static void defineTemplates() { + int equipId = 0; + for (int i = 0; i < Cache.getItemDefinitionsSize(); i++) { + ItemDefinition def = forId(i); + if (def.noteTemplateId != -1) { + def.transferNoteDefinition(forId(def.noteId), forId(def.noteTemplateId)); + } + if (def.lendTemplateId != -1) { + def.transferLendDefinition(forId(def.lendId), forId(def.lendTemplateId)); + } + if (def.recolourTemplateId != -1) { + def.transferRecolourDefinition(forId(def.recolourId), forId(def.recolourTemplateId)); + } + if (def != null && (def.maleWornModelId1 >= 0 || def.maleWornModelId2 >= 0)) { + def.equipId = equipId++; + } + } + forId(2513).equipId = forId(3140).equipId; + } + + /** + * Transfers definitions for noted items. + * @param reference The reference definitions. + * @param templateReference The template definitions. + */ + public void transferNoteDefinition(ItemDefinition reference, ItemDefinition templateReference) { + membersOnly = reference.membersOnly; + interfaceModelId = templateReference.interfaceModelId; + originalModelColors = templateReference.originalModelColors; + name = reference.name; + modelOffsetY = templateReference.modelOffsetY; + textureColour1 = templateReference.textureColour1; + value = reference.value; + modelRotationY = templateReference.modelRotationY; + stackable = true; + unnoted = false; + modifiedModelColors = templateReference.modifiedModelColors; + modelRotationX = templateReference.modelRotationX; + modelZoom = templateReference.modelZoom; + textureColour1 = templateReference.textureColour1; + handlers.put(ItemConfigParser.TRADEABLE, true); + } + + /** + * Transfers definitions for lending items. + * @param reference The reference definitions. + * @param templateReference The template definitions. + */ + public void transferLendDefinition(ItemDefinition reference, ItemDefinition templateReference) { + femaleWornModelId1 = reference.femaleWornModelId1; + maleWornModelId2 = reference.maleWornModelId2; + membersOnly = reference.membersOnly; + interfaceModelId = templateReference.interfaceModelId; + textureColour2 = reference.textureColour2; + groundActions = reference.groundActions; + unknownArray1 = reference.unknownArray1; + modelRotationX = templateReference.modelRotationX; + modelRotationY = templateReference.modelRotationY; + originalModelColors = reference.originalModelColors; + name = reference.name; + maleWornModelId1 = reference.maleWornModelId1; + colourEquip1 = reference.colourEquip1; + teamId = reference.teamId; + modelOffsetY = templateReference.modelOffsetY; + clientScriptData = reference.clientScriptData; + modifiedModelColors = reference.modifiedModelColors; + colourEquip2 = reference.colourEquip2; + modelOffsetX = templateReference.modelOffsetX; + textureColour1 = reference.textureColour1; + value = 0; + modelZoom = templateReference.modelZoom; + options = new String[5]; + femaleWornModelId2 = reference.femaleWornModelId2; + if (reference.options != null) { + options = reference.options.clone(); + } + } + + /** + * Transfers definitions for recolored items. + * @param reference The reference definitions. + * @param templateReference The template definitions. + */ + public void transferRecolourDefinition(ItemDefinition reference, ItemDefinition templateReference) { + femaleWornModelId2 = reference.femaleWornModelId2; + options = new String[5]; + modelRotationY = templateReference.modelRotationY; + name = reference.name; + maleWornModelId1 = reference.maleWornModelId1; + modelOffsetY = templateReference.modelOffsetY; + femaleWornModelId1 = reference.femaleWornModelId1; + maleWornModelId2 = reference.maleWornModelId2; + modelOffsetX = templateReference.modelOffsetX; + unknownArray1 = reference.unknownArray1; + stackable = reference.stackable; + modelRotationX = templateReference.modelRotationX; + textureColour1 = reference.textureColour1; + colourEquip1 = reference.colourEquip1; + textureColour2 = reference.textureColour2; + modifiedModelColors = reference.modifiedModelColors; + modelZoom = templateReference.modelZoom; + colourEquip2 = reference.colourEquip2; + teamId = reference.teamId; + value = 0; + groundActions = reference.groundActions; + originalModelColors = reference.originalModelColors; + membersOnly = reference.membersOnly; + clientScriptData = reference.clientScriptData; + interfaceModelId = templateReference.interfaceModelId; + if (reference.options != null) { + options = reference.options.clone(); + } + } + + /** + * Checks if the player has the needed requirements to use this item. + * @param player The player. + * @param wield If requirements are checked for wearing the item. + * @param message If a message should be sent when the player does not meet + * a requirement. + * @return {@code True} if so. + */ + public boolean hasRequirement(Player player, boolean wield, boolean message) { + Map requirements = getConfiguration(ItemConfigParser.REQUIREMENTS); + if (requirements == null) { + return true; + } + for (int skill : requirements.keySet()) { + if (skill < 0 || skill >= Skills.SKILL_NAME.length) { + continue; + } + int level = requirements.get(skill); + if (player.getSkills().getStaticLevel(skill) < level) { + if (message) { + String name = Skills.SKILL_NAME[skill]; + player.getPacketDispatch().sendMessage("You need a" + (StringUtils.isPlusN(name) ? "n " : " ") + name + " level of " + level + " to " + (wield ? "wear " : "use ") + "this."); + } + return false; + } + } + return true; + } + + /** + * Checks if the player can enter entrana. + * @param player the player. + * @return {@code True} if so. + */ + public static boolean canEnterEntrana(Player player) { + 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; + } + } + } + return true; + } + + /** + * The allowed ids. + */ + private static final HashSet entranaAllowedItems = new HashSet(Arrays.asList( + Items.PENANCE_GLOVES_10553, + Items.ICE_GLOVES_1580, + Items.BOOTS_OF_LIGHTNESS_88, + Items.CLIMBING_BOOTS_3105, + Items.SPOTTED_CAPE_10069, + Items.SPOTTIER_CAPE_10071, + Items.SARADOMIN_CAPE_2412, + Items.ZAMORAK_CAPE_2414, + Items.GUTHIX_CAPE_2413, + Items.SARADOMIN_CLOAK_10446, + Items.ZAMORAK_CLOAK_10450, + Items.GUTHIX_CLOAK_10448, + Items.HOLY_BOOK_3840, + Items.DAMAGED_BOOK_3839, + Items.UNHOLY_BOOK_3842, + Items.DAMAGED_BOOK_3841, + 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, + Items.COMBAT_BRACELET4_11118, + Items.REGEN_BRACELET_11133, + Items.WARLOCK_CLOAK_14081, + Items.WARLOCK_LEGS_14077, + 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**/ + Items.DWARF_CANNON_SET_11967, + Items.CANNON_BARRELS_10, + Items.CANNON_BASE_6, + Items.CANNON_STAND_8, + Items.CANNON_FURNACE_12, + Items.ZAMORAK_STOLE_10474, + Items.EXPLORERS_RING_1_13560, + Items.EXPLORERS_RING_2_13561, + Items.EXPLORERS_RING_3_13562 + )); + + + /** + * Checks if the item is allowed on entrana. + * @return {@code True} if so. + */ + public boolean isAllowedOnEntrana() { + if (entranaAllowedItems.contains(getId())) { + return true; + } + if (entranaBannedItems.contains(getId())) { + return false; + } + if (equipSlot(getId()) == EquipmentSlot.AMMO || equipSlot(getId()) == EquipmentSlot.NECK || equipSlot(getId()) == EquipmentSlot.RING) { + return true; + } + if (hasAction("summon")) { + return false; + } + int[] bonuses = getConfiguration(ItemConfigParser.BONUS); + return bonuses == null || Arrays.stream(bonuses).allMatch(x -> x == 0); + } + + /** + * Gets the level requirement for this item. + * @param skillId The skill id. + * @return The level required. + */ + public int getRequirement(int skillId) { + Map requirements = getConfiguration(ItemConfigParser.REQUIREMENTS); + if (requirements == null) { + return 0; + } + Integer level = requirements.get(skillId); + return level == null ? 0 : level; + } + + /** + * Gets the wielding animation id (render animation id). + * @return The wield animation id. + */ + public int getRenderAnimationId() { + return getConfiguration(ItemConfigParser.RENDER_ANIM_ID, 1426); + } + + @Override + public int getId() { + return id; + } + + @Override + public void setId(int id) { + this.id = id; + } + + /** + * Gets the interfaceModelId. + * @return The interfaceModelId. + */ + public int getInterfaceModelId() { + return interfaceModelId; + } + + /** + * Sets the interfaceModelId. + * @param interfaceModelId The interfaceModelId to set. + */ + public void setInterfaceModelId(int interfaceModelId) { + this.interfaceModelId = interfaceModelId; + } + + /** + * Gets the name. + * @return The name. + */ + @Override + public String getName() { + return name; + } + + /** + * Sets the name. + * @param name The name to set. + */ + @Override + public void setName(String name) { + this.name = name; + } + + /** + * Checks if the item type is for player usage. + * @return {@code True}. + */ + public boolean isPlayerType() { + return itemType == 0; + } + + /** + * Gets the modelZoom. + * @return The modelZoom. + */ + public int getModelZoom() { + return modelZoom; + } + + /** + * Sets the modelZoom. + * @param modelZoom The modelZoom to set. + */ + public void setModelZoom(int modelZoom) { + this.modelZoom = modelZoom; + } + + /** + * Gets the modelRotation1. + * @return The modelRotation1. + */ + public int getModelRotationX() { + return modelRotationX; + } + + /** + * Sets the modelRotation1. + * @param modelRotation1 The modelRotation1 to set. + */ + public void setModelRotationX(int modelRotation1) { + this.modelRotationX = modelRotation1; + } + + /** + * Gets the modelRotation2. + * @return The modelRotation2. + */ + public int getModelRotationY() { + return modelRotationY; + } + + /** + * Sets the modelRotation2. + * @param modelRotation2 The modelRotation2 to set. + */ + public void setModelRotationY(int modelRotation2) { + this.modelRotationY = modelRotation2; + } + + /** + * Gets the modelOffset1. + * @return The modelOffset1. + */ + public int getModelOffset1() { + return modelOffsetX; + } + + /** + * Sets the modelOffset1. + * @param modelOffset1 The modelOffset1 to set. + */ + public void setModelOffset1(int modelOffset1) { + this.modelOffsetX = modelOffset1; + } + + /** + * Gets the modelOffset2. + * @return The modelOffset2. + */ + public int getModelOffset2() { + return modelOffsetY; + } + + /** + * Sets the modelOffset2. + * @param modelOffset2 The modelOffset2 to set. + */ + public void setModelOffset2(int modelOffset2) { + this.modelOffsetY = modelOffset2; + } + + /** + * Gets the stackable. + * @return The stackable. + */ + public boolean isStackable() { + return stackable || !this.unnoted; + } + + /** + * Sets the stackable. + * @param stackable The stackable to set. + */ + public void setStackable(boolean stackable) { + this.stackable = stackable; + } + + /** + * Gets the value. + * @return The value. + */ + public int getValue() { + 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. + */ + public int getMaxValue() { + if ((int) (value * 1.05) <= 0) { + return 1; + } + return (int) (value * 1.05); + } + + /** + * @return The value. + */ + public int getMinValue() { + if ((int) (value * .95) <= 0) { + return 1; + } + return (int) (value * .95); + } + + /** + * Sets the value. + * @param value The value to set. + */ + public void setValue(int value) { + this.value = value; + } + + /** + * Gets the membersOnly. + * @return The membersOnly. + */ + public boolean isMembersOnly() { + return membersOnly; + } + + /** + * Sets the membersOnly. + * @param membersOnly The membersOnly to set. + */ + public void setMembersOnly(boolean membersOnly) { + this.membersOnly = membersOnly; + } + + /** + * Gets the maleWornModelId1. + * @return The maleWornModelId1. + */ + public int getMaleWornModelId1() { + return maleWornModelId1; + } + + /** + * Sets the maleWornModelId1. + * @param maleWornModelId1 The maleWornModelId1 to set. + */ + public void setMaleWornModelId1(int maleWornModelId1) { + this.maleWornModelId1 = maleWornModelId1; + } + + /** + * Gets the femaleWornModelId1. + * @return The femaleWornModelId1. + */ + public int getFemaleWornModelId1() { + return femaleWornModelId1; + } + + /** + * Sets the femaleWornModelId1. + * @param femaleWornModelId1 The femaleWornModelId1 to set. + */ + public void setFemaleWornModelId1(int femaleWornModelId1) { + this.femaleWornModelId1 = femaleWornModelId1; + } + + /** + * Gets the maleWornModelId2. + * @return The maleWornModelId2. + */ + public int getMaleWornModelId2() { + return maleWornModelId2; + } + + /** + * Sets the maleWornModelId2. + * @param maleWornModelId2 The maleWornModelId2 to set. + */ + public void setMaleWornModelId2(int maleWornModelId2) { + this.maleWornModelId2 = maleWornModelId2; + } + + /** + * Gets the femaleWornModelId2. + * @return The femaleWornModelId2. + */ + public int getFemaleWornModelId2() { + return femaleWornModelId2; + } + + /** + * Sets the femaleWornModelId2. + * @param femaleWornModelId2 The femaleWornModelId2 to set. + */ + public void setFemaleWornModelId2(int femaleWornModelId2) { + this.femaleWornModelId2 = femaleWornModelId2; + } + + /** + * Gets the groundOptions. + * @return The groundOptions. + */ + public String[] getGroundOptions() { + return groundActions; + } + + /** + * Sets the groundOptions. + * @param groundOptions The groundOptions to set. + */ + public void setGroundOptions(String[] groundOptions) { + this.groundActions = groundOptions; + } + + /** + * Gets the inventoryOptions. + * @return The inventoryOptions. + */ + public String[] getInventoryOptions() { + return options; + } + + /** + * Sets the inventoryOptions. + * @param inventoryOptions The inventoryOptions to set. + */ + public void setInventoryOptions(String[] inventoryOptions) { + this.options = inventoryOptions; + } + + /** + * Gets the originalModelColors. + * @return The originalModelColors. + */ + public short[] getOriginalModelColors() { + return originalModelColors; + } + + /** + * Sets the originalModelColors. + * @param originalModelColors The originalModelColors to set. + */ + public void setOriginalModelColors(short[] originalModelColors) { + this.originalModelColors = originalModelColors; + } + + /** + * Gets the modifiedModelColors. + * @return The modifiedModelColors. + */ + public short[] getModifiedModelColors() { + return modifiedModelColors; + } + + /** + * Sets the modifiedModelColors. + * @param modifiedModelColors The modifiedModelColors to set. + */ + public void setModifiedModelColors(short[] modifiedModelColors) { + this.modifiedModelColors = modifiedModelColors; + } + + /** + * Gets the textureColour1. + * @return The textureColour1. + */ + public short[] getTextureColour1() { + return textureColour1; + } + + /** + * Sets the textureColour1. + * @param textureColour1 The textureColour1 to set. + */ + public void setTextureColour1(short[] textureColour1) { + this.textureColour1 = textureColour1; + } + + /** + * Gets the textureColour2. + * @return The textureColour2. + */ + public short[] getTextureColour2() { + return textureColour2; + } + + /** + * Sets the textureColour2. + * @param textureColour2 The textureColour2 to set. + */ + public void setTextureColour2(short[] textureColour2) { + this.textureColour2 = textureColour2; + } + + /** + * Gets the unknownArray1. + * @return The unknownArray1. + */ + public byte[] getUnknownArray1() { + return unknownArray1; + } + + /** + * Sets the unknownArray1. + * @param unknownArray1 The unknownArray1 to set. + */ + public void setUnknownArray1(byte[] unknownArray1) { + this.unknownArray1 = unknownArray1; + } + + /** + * Gets the unknownArray2. + * @return The unknownArray2. + */ + public int[] getUnknownArray2() { + return unknownArray2; + } + + /** + * Sets the unknownArray2. + * @param unknownArray2 The unknownArray2 to set. + */ + public void setUnknownArray2(int[] unknownArray2) { + this.unknownArray2 = unknownArray2; + } + + /** + * Gets the unnoted. + * @return The unnoted. + */ + public boolean isUnnoted() { + return unnoted; + } + + /** + * Sets the unnoted. + * @param unnoted The unnoted to set. + */ + public void setUnnoted(boolean unnoted) { + this.unnoted = unnoted; + } + + /** + * Gets the colourEquip1. + * @return The colourEquip1. + */ + public int getColourEquip1() { + return colourEquip1; + } + + /** + * Sets the colourEquip1. + * @param colourEquip1 The colourEquip1 to set. + */ + public void setColourEquip1(int colourEquip1) { + this.colourEquip1 = colourEquip1; + } + + /** + * Gets the colourEquip2. + * @return The colourEquip2. + */ + public int getColourEquip2() { + return colourEquip2; + } + + /** + * Sets the colourEquip2. + * @param colourEquip2 The colourEquip2 to set. + */ + public void setColourEquip2(int colourEquip2) { + this.colourEquip2 = colourEquip2; + } + + /** + * Gets the noteId. + * @return The noteId. + */ + public int getNoteId() { + return noteId; + } + + /** + * Sets the noteId. + * @param noteId The noteId to set. + */ + public void setNoteId(int noteId) { + this.noteId = noteId; + } + + /** + * Gets the noteTemplateId. + * @return The noteTemplateId. + */ + public int getNoteTemplateId() { + return noteTemplateId; + } + + /** + * Sets the noteTemplateId. + * @param noteTemplateId The noteTemplateId to set. + */ + public void setNoteTemplateId(int noteTemplateId) { + this.noteTemplateId = noteTemplateId; + } + + /** + * Gets the stackIds. + * @return The stackIds. + */ + public int[] getStackIds() { + return stackIds; + } + + /** + * Sets the stackIds. + * @param stackIds The stackIds to set. + */ + public void setStackIds(int[] stackIds) { + this.stackIds = stackIds; + } + + /** + * Gets the stackAmounts. + * @return The stackAmounts. + */ + public int[] getStackAmounts() { + return stackAmounts; + } + + /** + * Sets the stackAmounts. + * @param stackAmounts The stackAmounts to set. + */ + public void setStackAmounts(int[] stackAmounts) { + this.stackAmounts = stackAmounts; + } + + /** + * Gets the teamId. + * @return The teamId. + */ + public int getTeamId() { + return teamId; + } + + /** + * Sets the teamId. + * @param teamId The teamId to set. + */ + public void setTeamId(int teamId) { + this.teamId = teamId; + } + + /** + * Gets the lendId. + * @return The lendId. + */ + public int getLendId() { + return lendId; + } + + /** + * Sets the lendId. + * @param lendId The lendId to set. + */ + public void setLendId(int lendId) { + this.lendId = lendId; + } + + /** + * Gets the lendTemplateId. + * @return The lendTemplateId. + */ + public int getLendTemplateId() { + return lendTemplateId; + } + + /** + * Sets the lendTemplateId. + * @param lendTemplateId The lendTemplateId to set. + */ + public void setLendTemplateId(int lendTemplateId) { + this.lendTemplateId = lendTemplateId; + } + + /** + * Gets the recolourId. + * @return The recolourId. + */ + public int getRecolourId() { + return recolourId; + } + + /** + * Sets the recolourId. + * @param recolourId The recolourId to set. + */ + public void setRecolourId(int recolourId) { + this.recolourId = recolourId; + } + + /** + * Gets the recolourTemplateId. + * @return The recolourTemplateId. + */ + public int getRecolourTemplateId() { + return recolourTemplateId; + } + + /** + * Sets the recolourTemplateId. + * @param recolourTemplateId The recolourTemplateId to set. + */ + public void setRecolourTemplateId(int recolourTemplateId) { + this.recolourTemplateId = recolourTemplateId; + } + + /** + * Gets the equipId. + * @return The equipId. + */ + public int getEquipId() { + return equipId; + } + + /** + * Sets the equipId. + * @param equipId The equipId to set. + */ + public void setEquipId(int equipId) { + this.equipId = equipId; + } + + /** + * Gets the clientScriptData. + * @return The clientScriptData. + */ + public HashMap getClientScriptData() { + return clientScriptData; + } + + /** + * Sets the clientScriptData. + * @param clientScriptData The clientScriptData to set. + */ + public void setClientScriptData(HashMap clientScriptData) { + this.clientScriptData = clientScriptData; + } + + /** + * Gets the alchemy value. + * @param highAlchemy If the value is for high alchemy (instead of low). + * @return The alchemy value. + */ + public int getAlchemyValue(boolean highAlchemy) { + if (!unnoted && noteId > -1) { + return forId(noteId).getAlchemyValue(highAlchemy); + } + if (highAlchemy) { + return getConfiguration(ItemConfigParser.HIGH_ALCHEMY, (int)Math.rint(value * 0.6)); + } + 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. + */ + public boolean isTradeable() { + if (hasDestroyAction() && !getName().contains("impling jar")) { + return false; + } + if (!getConfiguration(ItemConfigParser.TRADEABLE, false)) { + return false; + } + return true; + } + + /** + * If the item has the specified item. + * @param optionName The reward. + * @return If the item has the specified reward {@code true}. + */ + public boolean hasAction(String optionName) { + if (options == null) { + return false; + } + for (String action : options) { + if (action == null) { + continue; + } + if (action.equalsIgnoreCase(optionName)) { + return true; + } + } + return false; + } + + /** + * If the item has the destroy reward. + * @return If the item has the destroy reward {@code true}. + */ + public boolean hasDestroyAction() { + return hasAction("destroy") || hasAction("dissolve"); + } + + /** + * If the item has the wear reward. + * @return If the item has the wear reward {@code true}. + */ + public boolean hasWearAction() { + if (options == null) { + return false; + } + for (String action : options) { + if (action == null) { + continue; + } + if (action.equalsIgnoreCase("wield") || action.equalsIgnoreCase("wear") || action.equalsIgnoreCase("equip")) { + return true; + } + } + return false; + } + + /** + * If the item has a special bar. + * @return If the item has a special bar {@code true}. + */ + public boolean hasSpecialBar() { + if (clientScriptData == null) { + return false; + } + Object specialBar = clientScriptData.get(686); + if (specialBar != null && specialBar instanceof Integer) { + return (Integer) specialBar == 1; + } + return false; + } + + /** + * Get the quest id for the item. + * @return The quest id. + */ + public int getQuestId() { + if (clientScriptData == null) { + return -1; + } + Object questId = clientScriptData.get(861); + if (questId != null && questId instanceof Integer) { + return (Integer) questId; + } + return -1; + } + + /** + * Get the archive id. + * @return The archive id. + */ + public int getArchiveId() { + return id >>> 8; + } + + /** + * Get the file id. + * @return The file id. + */ + public int getFileId() { + return 0xff & id; + } + + /** + * Gets the definitions. + * @return The definitions. + */ + public static Map getDefinitions() { + return DEFINITIONS; + } + + /** + * Gets the option handler for the given option name. + * @param nodeId + * @param name The name. + * @return The option handler, or {@code null} if there was no default + * option handler. + */ + public static OptionHandler getOptionHandler(int nodeId, String name) { + ItemDefinition def = forId(nodeId); + if (def == null) { + if (nodeId == 22937) + log(ItemDefinition.class, Log.ERR, "[ItemDefinition] No definition for item id " + nodeId + "!"); + return null; + } + OptionHandler handler = def.getConfiguration("option:" + name); + if (handler != null) { + return handler; + } + return OPTION_HANDLERS.get(name); + } + + /** + * The bonus names. + */ + private static final String[] BONUS_NAMES = { "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Summoning: ", "Strength: ", "Prayer: " }; + + /** + * Checks if it has a plugin. + * @return {@code True} if so. + */ + public boolean hasPlugin() { + return getItemPlugin() != null; + } + + /** + * Gets the item plugin. + * @return the plugin. + */ + public ItemPlugin getItemPlugin() { + return getConfiguration("wrapper", null); + } + + /** + * Sets the item plugin. + * @param plugin the plugin. + */ + public void setItemPlugin(ItemPlugin plugin) { + getHandlers().put("wrapper", plugin); + } + + /** + * Sets the default option handler for an option. + * @param name The option name. + * @param handler The default option handler. + * @return {@code True} if there was a previous default handler mapped. + */ + public static boolean setOptionHandler(String name, OptionHandler handler) { + return OPTION_HANDLERS.put(name, handler) != null; + } + + /** + * @return the optionHandlers + */ + public static Map getOptionHandlers() { + return OPTION_HANDLERS; + } + + /** + * @return the itemRequirements. + */ + public HashMap getItemRequirements() { + return itemRequirements; + } + + /** + * @param itemRequirements the itemRequirements to set. + */ + public void setItemRequirements(HashMap itemRequirements) { + this.itemRequirements = itemRequirements; + } + + /** + * Gets the itemType. + * @return The itemType. + */ + public int getItemType() { + return itemType; + } + + /** + * Sets the itemType. + * @param itemType The itemType to set. + */ + public void setItemType(int itemType) { + this.itemType = itemType; + } + + /** + * Gets the femaleWornModelId3 value. + * @return The femaleWornModelId3. + */ + public int getFemaleWornModelId3() { + return femaleWornModelId3; + } + /** + * Sets the femaleWornModelId3 value. + * @param femaleWornModelId3 The femaleWornModelId3 to set. + */ + public void setFemaleWornModelId3(int femaleWornModelId3) { + this.femaleWornModelId3 = femaleWornModelId3; + } + /** + * Gets the femaleWornModelId4 value. + * @return The femaleWornModelId4. + */ + public int getFemaleWornModelId4() { + return femaleWornModelId4; + } + /** + * Sets the femaleWornModelId4 value. + * @param femaleWornModelId4 The femaleWornModelId4 to set. + */ + public void setFemaleWornModelId4(int femaleWornModelId4) { + this.femaleWornModelId4 = femaleWornModelId4; + } + /** + * Gets the maleWornModelId3 value. + * @return The maleWornModelId3. + */ + public int getMaleWornModelId3() { + return maleWornModelId3; + } + /** + * Sets the maleWornModelId3 value. + * @param maleWornModelId3 The maleWornModelId3 to set. + */ + public void setMaleWornModelId3(int maleWornModelId3) { + this.maleWornModelId3 = maleWornModelId3; + } + /** + * Gets the maleWornModelId4 value. + * @return The maleWornModelId4. + */ + public int getMaleWornModelId4() { + return maleWornModelId4; + } + /** + * Sets the maleWornModelId4 value. + * @param maleWornModelId4 The maleWornModelId4 to set. + */ + public void setMaleWornModelId4(int maleWornModelId4) { + this.maleWornModelId4 = maleWornModelId4; + } + + /** + * Gets the examine. + * @return The examine. + */ + @Override + public String getExamine() { + examine = super.getExamine(); + if (!isUnnoted()) { + examine = "Swap this note at any bank for the equivalent item."; + } + return examine; + } +} 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 new file mode 100644 index 000000000..dbf3bdf1d --- /dev/null +++ b/Server/src/main/core/cache/def/impl/NPCDefinition.java @@ -0,0 +1,1119 @@ +package core.cache.def.impl; + +import core.cache.Cache; +import core.cache.def.Definition; +import core.cache.misc.buffer.ByteBufferUtils; +import core.game.interaction.OptionHandler; +import core.game.node.entity.npc.NPC; +import core.game.node.entity.npc.drop.NPCDropTables; +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.tools.StringUtils; +import core.game.system.config.NPCConfigParser; +import core.game.world.GameWorld; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import static core.api.ContentAPIKt.*; + +/** + * Debug NPC Opcodes Line 483, Uncomment + */ + +/** + * Represents an NPC's definitions. + * @author Emperor + */ +public final class NPCDefinition extends Definition { + + /** + * The definitions. + */ + private static final Map DEFINITIONS = new HashMap<>(); + + /** + * The default option handlers. + */ + private static final Map OPTION_HANDLERS = new HashMap<>(); + + /** + * The size. + */ + public int size = 1; + + /** + * The combat level. + */ + private int combatLevel; + + /** + * The head icons. + */ + public int headIcons; + + /** + * If the NPC can be seen on the minimap. + */ + public boolean isVisibleOnMap; + + +/* public String examine;*/ + + /** + * The drop tables. + */ + private NPCDropTables dropTables = new NPCDropTables(this); + + /** + * Unidentified variables. + */ + public int anInt833; + public int anInt836; + public int anInt837; + public boolean aBoolean841; + public int anInt842; + public int configFileId; + public int[] childNPCIds; + public int anInt846; + public int anInt850; + public byte aByte851; + public boolean aBoolean852; + public int anInt853; + public byte aByte854; + public boolean aBoolean856; + public boolean aBoolean857; + public short[] aShortArray859; + public byte[] aByteArray861; + public short aShort862; + public boolean aBoolean863; + public int anInt864; + public short[] aShortArray866; + public int[] anIntArray868; + public int anInt869; + public int anInt870; + public int anInt871; + public int anInt872; + public int anInt874; + public int anInt875; + public int anInt876; + public int anInt879; + public short[] aShortArray880; + public int anInt884; + public int configId; + public int anInt889; + public int[] anIntArray892; + public short aShort894; + public short[] aShortArray896; + public int anInt897; + public int anInt899; + public int anInt901; + public int standAnimation; + public int walkAnimation; + public int renderAnimationId; + + /** + * The minimum combat distance (0 uses default distances). + */ + private int combatDistance; + + /** + * The combat graphics. + */ + private Graphics[] combatGraphics = new Graphics[3]; + + /** + * The turning animation. + */ + private int turnAnimation; + + /** + * The turn 180� animation. + */ + private int turn180Animation; + + /** + * The turn clock-wise animation. + */ + private int turnCWAnimation; + + /** + * The turn counter clock-wise animation. + */ + private int turnCCWAnimation; + + /** + * Constructs a new {@code NPCDefinition} {@code Object}. + * @param id The NPC id. + */ + public NPCDefinition(int id) { + this.id = id; + anInt842 = -1; + configFileId = -1; + anInt837 = -1; + anInt846 = -1; + anInt853 = 32; + standAnimation = -1; + walkAnimation = -1; + combatLevel = 0; + anInt836 = -1; + name = "null"; + anInt869 = 0; + anInt850 = 255; + anInt871 = -1; + aBoolean852 = true; + aShort862 = (short) 0; + anInt876 = -1; + aByte851 = (byte) -96; + anInt875 = 0; + anInt872 = -1; + aBoolean857 = true; + anInt870 = -1; + anInt874 = -1; + anInt833 = -1; + anInt864 = 128; + headIcons = -1; + aBoolean856 = false; + configId = -1; + aByte854 = (byte) -16; + aBoolean863 = false; + isVisibleOnMap = true; + anInt889 = -1; + anInt884 = -1; + aBoolean841 = true; + anInt879 = -1; + anInt899 = 128; + aShort894 = (short) 0; + options = new String[5]; + anInt897 = 0; + anInt901 = -1; + anIntArray868 = new int[0]; + } + + /** + * Gets the NPC definition for this id. + * @param id The NPC id. + * @return The NPC definition object. + */ + public static NPCDefinition forId(int id) { + NPCDefinition def = DEFINITIONS.get(id); + if (def == null) { + def = new NPCDefinition(id); + byte[] data = Cache.getIndexes()[18].getFileData(id >>> 7, id & 0x7f); + if (data == null) { + if (id != -1) { + + } + } else { + def.parse(ByteBuffer.wrap(data)); + } + DEFINITIONS.put(id, def); + } + return def; + } + + public static void main(String... args) throws Throwable { + GameWorld.prompt(false); + + // for (int i = 0; i < 11000; i++) { + // ItemDefinition def = ItemDefinition.forId(i); + // if (def.getMaleWornModelId1() >= 1250 && def.getMaleWornModelId1() <= + // 1550) { + + // def.getMaleWornModelId1()); + // } + // } + } + + /** + * Gets the child object definitions. + * @param player The player to get it for. + * @return The object definition. + */ + public NPCDefinition getChildNPC(Player player) { + if (childNPCIds == null || childNPCIds.length < 1) { + return this; + } + int configValue = -1; + if (player != null) { + if (configFileId != -1) { + configValue = VarbitDefinition.forNPCID(configFileId).getValue(player); + } else if (configId != -1) { + configValue = getVarp(player, configId); + } + } else { + configValue = 0; + } + if (configValue < 0 || configValue >= childNPCIds.length - 1 || childNPCIds[configValue] == -1) { + int objectId = childNPCIds[childNPCIds.length - 1]; + if (objectId != -1) { + return forId(objectId); + } + return this; + } + return forId(childNPCIds[configValue]); + } + + /** + * Parses the data. + * @param buffer The data input stream. + */ + private void parse(ByteBuffer buffer) { + while (true) { + int opcode = buffer.get() & 0xFF; + if (opcode == 0) { + break; + } + parseOpcode(buffer, opcode); + } + } + + /** + * Parses an opcode. + * @param buffer The data input stream. + * @param opcode The opcode to parse. + */ + private void parseOpcode(ByteBuffer buffer, int opcode) { + switch (opcode) { + case 1: + int length = buffer.get() & 0xFF; + anIntArray868 = new int[length]; + for (int i_66_ = 0; i_66_ < length; i_66_++) { + anIntArray868[i_66_] = buffer.getShort() & 0xFFFF; + if ((anIntArray868[i_66_] ^ 0xffffffff) == -65536) + anIntArray868[i_66_] = -1; + } + break; + case 2: + name = ByteBufferUtils.getString(buffer); + break; + case 12: + size = buffer.get() & 0xFF; + break; + case 13: + standAnimation = buffer.getShort(); + break; + case 14: + walkAnimation = buffer.getShort(); + break; + case 15: + turnAnimation = buffer.getShort(); + break; + case 16: + buffer.getShort(); // Another turn animation + break; + case 17: + walkAnimation = buffer.getShort(); + turn180Animation = buffer.getShort(); + turnCWAnimation = buffer.getShort(); + turnCCWAnimation = buffer.getShort(); + break; + case 30: + case 31: + case 32: + case 33: + case 34: + options[opcode - 30] = ByteBufferUtils.getString(buffer); + break; + case 40: + length = buffer.get() & 0xFF; + aShortArray859 = new short[length]; + aShortArray896 = new short[length]; + for (int i_65_ = 0; (length ^ 0xffffffff) < (i_65_ ^ 0xffffffff); i_65_++) { + aShortArray896[i_65_] = (short) (buffer.getShort() & 0xFFFF); + aShortArray859[i_65_] = (short) (buffer.getShort() & 0xFFFF); + } + break; + case 41: + length = buffer.get() & 0xFF; + aShortArray880 = new short[length]; + aShortArray866 = new short[length]; + for (int i_54_ = 0; (i_54_ ^ 0xffffffff) > (length ^ 0xffffffff); i_54_++) { + aShortArray880[i_54_] = (short) (buffer.getShort() & 0xFFFF); + aShortArray866[i_54_] = (short) (buffer.getShort() & 0xFFFF); + } + break; + case 42: + length = buffer.get() & 0xFF; + aByteArray861 = new byte[length]; + for (int i_55_ = 0; length > i_55_; i_55_++) { + aByteArray861[i_55_] = (byte) buffer.get(); + } + break; + case 60: + length = buffer.get() & 0xFF; + anIntArray892 = new int[length]; + for (int i_64_ = 0; (i_64_ ^ 0xffffffff) > (length ^ 0xffffffff); i_64_++) { + anIntArray892[i_64_] = buffer.getShort() & 0xFFFF; + } + break; + case 93: + isVisibleOnMap = false; + break; + case 95: + setCombatLevel(buffer.getShort() & 0xFFFF); + break; + case 97: + anInt864 = buffer.getShort() & 0xFFFF; + break; + case 98: + anInt899 = buffer.getShort() & 0xFFFF; + break; + case 99: + aBoolean863 = true; + break; + case 100: + anInt869 = buffer.get(); + break; + case 101: + anInt897 = buffer.get() * 5; + break; + case 102: + headIcons = buffer.getShort() & 0xFFFF; + break; + case 103: + anInt853 = buffer.getShort() & 0xFFFF; + break; + case 106: + case 118: + configFileId = buffer.getShort() & 0xFFFF; + if (configFileId == 65535) { + configFileId = -1; + } + configId = buffer.getShort() & 0xFFFF; + if (configId == 65535) { + configId = -1; + } + int defaultValue = -1; + if ((opcode ^ 0xffffffff) == -119) { + defaultValue = buffer.getShort() & 0xFFFF; + if (defaultValue == 65535) { + defaultValue = -1; + } + } + length = buffer.get() & 0xFF; + childNPCIds = new int[2 + length]; + for (int i = 0; length >= i; i++) { + childNPCIds[i] = buffer.getShort() & 0xFFFF; + if (childNPCIds[i] == 65535) { + childNPCIds[i] = -1; + } + } + childNPCIds[length + 1] = defaultValue; + break; + case 107: + aBoolean841 = false; + break; + case 109: + aBoolean852 = false; + break; + case 111: + aBoolean857 = false; + break; + case 113: + aShort862 = (short) (buffer.getShort() & 0xFFFF); + aShort894 = (short) (buffer.getShort() & 0xFFFF); + break; + case 114: + aByte851 = (byte) (buffer.get()); + aByte854 = (byte) (buffer.get()); + break; + case 115: + buffer.get();// & 0xFF; + buffer.get();// & 0xFF; + break; + case 119: + buffer.get(); + break; + case 121: + length = buffer.get() & 0xFF; + for (int i = 0; i < length; i++) { + buffer.get(); + buffer.get(); + buffer.get(); + buffer.get(); + } + break; + case 122: + buffer.getShort(); + break; + case 123: + buffer.getShort(); + break; + case 125: + buffer.get(); + break; + case 126: + buffer.getShort(); + case 127: + renderAnimationId = buffer.getShort(); + break; + case 128: + buffer.get(); + break; + case 134: + buffer.getShort(); + buffer.getShort(); + buffer.getShort(); + buffer.getShort(); + buffer.get(); + break; + case 135: + buffer.get(); + buffer.getShort(); + break; + case 136: + buffer.get(); + buffer.getShort(); + break; + case 137: + buffer.getShort(); + break; + case 249: + length = buffer.get() & 0xFF; + for (int i = 0; i < length; i++) { + boolean string = buffer.get() == 1; + ByteBufferUtils.getMedium(buffer); // script id + if (!string) { + buffer.getInt(); // Value + } else { + ByteBufferUtils.getString(buffer); // value + } + } + break; + default: + //SystemLogger.logErr("Unhandled NPC definition opcode: " + opcode); + } + } + + /** + * Checks if this NPC has an attack option. + * @return {@code True} if so. + */ + public boolean hasAttackOption() { + for (String option : options) { + if (option != null && option.equalsIgnoreCase("attack")) { + return true; + } + } + return false; + } + + /** + * Gets the option handler for the given option name. + * @param nodeId The node id. + * @param name The name. + * @return The option handler, or {@code null} if there was no default + * option handler. + */ + public static OptionHandler getOptionHandler(int nodeId, String name) { + NPCDefinition def = forId(nodeId); + OptionHandler handler = def.getConfiguration("option:" + name); + if (handler != null) { + return handler; + } + return OPTION_HANDLERS.get(name); + } + + /** + * Checks if an npc has an reward. + * @param optionName The option name. + * @return {@code True} if so. + */ + public boolean hasAction(String optionName) { + if (options == null) { + return false; + } + for (String action : options) { + if (action == null) { + continue; + } + if (action.equalsIgnoreCase(optionName)) { + return true; + } + } + return false; + } + + /** + * Sets the default option handler for an option. + * @param name The option name. + * @param handler The default option handler. + * @return {@code True} if there was a previous default handler mapped. + */ + public static boolean setOptionHandler(String name, OptionHandler handler) { + return OPTION_HANDLERS.put(name, handler) != null; + } + + /** + * Gets the option handlers. + * @return The option handlers. + */ + public static Map getOptionHandlers() { + return OPTION_HANDLERS; + } + + /** + * Gets the definitions mapping. + * @return The mapping. + */ + public static final Map getDefinitions() { + return DEFINITIONS; + } + + /** + * Method returns the value for 'examine' + * @return the examine. + */ + public final String getExamine() { + String string = getConfiguration(NPCConfigParser.EXAMINE, examine); + if (string != null) { + return string; + } + if (name.length() <= 1) { + return string; + } + return "It's a" + (StringUtils.isPlusN(name) ? "n " : " ") + name + "."; + } + + /** + * Method sets the value for 'examine' + * @param examine the examine to set. + */ + public final void setExamine(String examine) { + this.examine = examine; + } + + /** + * Initializes the combat graphics. + * @param config The configurations. + */ + public void initCombatGraphics(Map config) { + if (config.containsKey(NPCConfigParser.START_GRAPHIC)) { + combatGraphics[0] = new Graphics((Integer) config.get(NPCConfigParser.START_GRAPHIC), getConfiguration(NPCConfigParser.START_HEIGHT, 0)); + } + if (config.containsKey(NPCConfigParser.PROJECTILE)) { + combatGraphics[1] = new Graphics((Integer) config.get(NPCConfigParser.PROJECTILE), getConfiguration(NPCConfigParser.PROJECTILE_HEIGHT, 42)); + } + if (config.containsKey(NPCConfigParser.END_GRAPHIC)) { + combatGraphics[2] = new Graphics((Integer) config.get(NPCConfigParser.END_GRAPHIC), getConfiguration(NPCConfigParser.END_HEIGHT, 96)); + } + } + + /** + * Gets the combat animation. + * @param index The index. + * @return The Animation. + */ + public Animation getCombatAnimation(int index) { + String name = ""; + switch (index) { + case 0: + name = NPCConfigParser.MELEE_ANIMATION; + break; + case 1: + name = NPCConfigParser.MAGIC_ANIMATION; + break; + case 2: + name = NPCConfigParser.RANGE_ANIMATION; + break; + case 3: + name = NPCConfigParser.DEFENCE_ANIMATION; + break; + case 4: + name = NPCConfigParser.DEATH_ANIMATION; + break; + default: + break; + } + return getConfiguration(name, null); + } + + /** + * Gets the size. + * @return The size. + */ + public int getSize() { + return size; + } + + /** + * Gets the headIcons. + * @return The headIcons. + */ + public int getHeadIcons() { + return headIcons; + } + + /** + * Gets the isVisibleOnMap. + * @return The isVisibleOnMap. + */ + public boolean isVisibleOnMap() { + return isVisibleOnMap; + } + + /** + * Gets the anInt833. + * @return The anInt833. + */ + public int getAnInt833() { + return anInt833; + } + + /** + * Gets the anInt836. + * @return The anInt836. + */ + public int getAnInt836() { + return anInt836; + } + + /** + * Gets the anInt837. + * @return The anInt837. + */ + public int getAnInt837() { + return anInt837; + } + + /** + * Gets the aBoolean841. + * @return The aBoolean841. + */ + public boolean isaBoolean841() { + return aBoolean841; + } + + /** + * Gets the anInt842. + * @return The anInt842. + */ + public int getAnInt842() { + return anInt842; + } + + /** + * Gets the configFileId. + * @return The configFileId. + */ + public int getConfigFileId() { + return configFileId; + } + + /** + * Gets the childNPCIds. + * @return The childNPCIds. + */ + public int[] getChildNPCIds() { + return childNPCIds; + } + + /** + * Gets the anInt846. + * @return The anInt846. + */ + public int getAnInt846() { + return anInt846; + } + + /** + * Gets the anInt850. + * @return The anInt850. + */ + public int getAnInt850() { + return anInt850; + } + + /** + * Gets the aByte851. + * @return The aByte851. + */ + public byte getaByte851() { + return aByte851; + } + + /** + * Gets the aBoolean852. + * @return The aBoolean852. + */ + public boolean isaBoolean852() { + return aBoolean852; + } + + /** + * Gets the anInt853. + * @return The anInt853. + */ + public int getAnInt853() { + return anInt853; + } + + /** + * Gets the aByte854. + * @return The aByte854. + */ + public byte getaByte854() { + return aByte854; + } + + /** + * Gets the aBoolean856. + * @return The aBoolean856. + */ + public boolean isaBoolean856() { + return aBoolean856; + } + + /** + * Gets the aBoolean857. + * @return The aBoolean857. + */ + public boolean isaBoolean857() { + return aBoolean857; + } + + /** + * Gets the aShortArray859. + * @return The aShortArray859. + */ + public short[] getaShortArray859() { + return aShortArray859; + } + + /** + * Gets the aByteArray861. + * @return The aByteArray861. + */ + public byte[] getaByteArray861() { + return aByteArray861; + } + + /** + * Gets the aShort862. + * @return The aShort862. + */ + public short getaShort862() { + return aShort862; + } + + /** + * Gets the aBoolean863. + * @return The aBoolean863. + */ + public boolean isaBoolean863() { + return aBoolean863; + } + + /** + * Gets the anInt864. + * @return The anInt864. + */ + public int getAnInt864() { + return anInt864; + } + + /** + * Gets the aShortArray866. + * @return The aShortArray866. + */ + public short[] getaShortArray866() { + return aShortArray866; + } + + /** + * Gets the anIntArray868. + * @return The anIntArray868. + */ + public int[] getAnIntArray868() { + return anIntArray868; + } + + /** + * Gets the anInt869. + * @return The anInt869. + */ + public int getAnInt869() { + return anInt869; + } + + /** + * Gets the anInt870. + * @return The anInt870. + */ + public int getAnInt870() { + return anInt870; + } + + /** + * Gets the anInt871. + * @return The anInt871. + */ + public int getAnInt871() { + return anInt871; + } + + /** + * Gets the anInt872. + * @return The anInt872. + */ + public int getAnInt872() { + return anInt872; + } + + /** + * Gets the anInt874. + * @return The anInt874. + */ + public int getAnInt874() { + return anInt874; + } + + /** + * Gets the anInt875. + * @return The anInt875. + */ + public int getAnInt875() { + return anInt875; + } + + /** + * Gets the anInt876. + * @return The anInt876. + */ + public int getAnInt876() { + return anInt876; + } + + /** + * Gets the anInt879. + * @return The anInt879. + */ + public int getAnInt879() { + return anInt879; + } + + /** + * Gets the aShortArray880. + * @return The aShortArray880. + */ + public short[] getaShortArray880() { + return aShortArray880; + } + + /** + * Gets the anInt884. + * @return The anInt884. + */ + public int getAnInt884() { + return anInt884; + } + + /** + * Gets the configId. + * @return The configId. + */ + public int getConfigId() { + if(configFileId != -1) { + return VarbitDefinition.forNPCID(configFileId).getVarpId(); + } else return configFileId; + } + + public int getVarbitOffset() { + if(configFileId != -1){ + return VarbitDefinition.forNPCID(configFileId).getStartBit(); + } + return -1; + } + + public int getVarbitSize() { + if(configFileId != -1){ + return VarbitDefinition.forNPCID(configFileId).getEndBit() - VarbitDefinition.forNPCID(configFileId).getStartBit(); + } + return -1; + } + + /** + * Gets the anInt889. + * @return The anInt889. + */ + public int getAnInt889() { + return anInt889; + } + + /** + * Gets the anIntArray892. + * @return The anIntArray892. + */ + public int[] getAnIntArray892() { + return anIntArray892; + } + + /** + * Gets the aShort894. + * @return The aShort894. + */ + public short getaShort894() { + return aShort894; + } + + /** + * Gets the aShortArray896. + * @return The aShortArray896. + */ + public short[] getaShortArray896() { + return aShortArray896; + } + + /** + * Gets the anInt897. + * @return The anInt897. + */ + public int getAnInt897() { + return anInt897; + } + + /** + * Gets the anInt899. + * @return The anInt899. + */ + public int getAnInt899() { + return anInt899; + } + + /** + * Gets the anInt901. + * @return The anInt901. + */ + public int getAnInt901() { + return anInt901; + } + + /** + * Gets the standAnimation. + * @return The standAnimation. + */ + public int getStandAnimation() { + return standAnimation; + } + + /** + * Gets the walkAnimation. + * @return The walkAnimation. + */ + public int getWalkAnimation() { + return walkAnimation; + } + + /** + * Gets the turnAnimation. + * @return The turnAnimation. + */ + public int getTurnAnimation() { + return turnAnimation; + } + + /** + * Gets the turn180Animation. + * @return The turn180Animation. + */ + public int getTurn180Animation() { + return turn180Animation; + } + + /** + * Gets the turnCWAnimation. + * @return The turnCWAnimation. + */ + public int getTurnCWAnimation() { + return turnCWAnimation; + } + + /** + * Gets the turnCCWAnimation. + * @return The turnCCWAnimation. + */ + public int getTurnCCWAnimation() { + return turnCCWAnimation; + } + + /** + * @return the dropTables. + */ + public NPCDropTables getDropTables() { + return dropTables; + } + + /** + * @param dropTables the dropTables to set. + */ + public void setDropTables(NPCDropTables dropTables) { + this.dropTables = dropTables; + } + + /** + * Gets the combatLevel. + * @return The combatLevel. + */ + public int getCombatLevel() { + return combatLevel; + } + + /** + * Sets the combatLevel. + * @param combatLevel The combatLevel to set. + */ + public void setCombatLevel(int combatLevel) { + this.combatLevel = combatLevel; + } + + /** + * Gets the combatDistance. + * @return The combatDistance. + */ + public int getCombatDistance() { + return combatDistance; + } + + /** + * Sets the combatDistance. + * @param combatDistance The combatDistance to set. + */ + public void setCombatDistance(int combatDistance) { + this.combatDistance = combatDistance; + } + + /** + * Gets the combatGraphics. + * @return The combatGraphics. + */ + public Graphics[] getCombatGraphics() { + return combatGraphics; + } + + /** + * Sets the combatGraphics. + * @param combatGraphics The combatGraphics to set. + */ + public void setCombatGraphics(Graphics[] combatGraphics) { + this.combatGraphics = combatGraphics; + } + + /** + * Gets the renderAnimationId value. + * @return The renderAnimationId. + */ + public int getRenderAnimationId() { + return renderAnimationId; + } + + /** + * Sets the renderAnimationId value. + * @param renderAnimationId The renderAnimationId to set. + */ + public void setRenderAnimationId(int renderAnimationId) { + this.renderAnimationId = renderAnimationId; + } +} diff --git a/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java b/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java new file mode 100644 index 000000000..881e4040d --- /dev/null +++ b/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java @@ -0,0 +1,330 @@ +package core.cache.def.impl; + +import core.cache.Cache; +import core.tools.Log; +import core.game.world.GameWorld; + +import java.lang.reflect.Array; +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 + * @author Emperor + * + */ +public class RenderAnimationDefinition { + + public int turn180Animation; + public int anInt951 = -1; + public int anInt952; + public int turnCWAnimation = -1; + public int anInt954; + public int anInt955; + public int anInt956; + public int anInt957; + public int anInt958; + public int[] anIntArray959 = null; + public int anInt960; + public int anInt961 = 0; + public int anInt962; + public int walkAnimationId; + public int anInt964; + public int anInt965; + public int anInt966; + public int[] standAnimationIds; + public int anInt969; + public int[] anIntArray971; + public int standAnimationId; + public int anInt973; + public int anInt974; + public int anInt975; + public int runAnimationId; + public int anInt977; + public boolean aBoolean978; + public int[][] anIntArrayArray979; + public int anInt980; + public int turnCCWAnimation; + public int anInt983; + public int anInt985; + public int anInt986; + public int anInt987; + public int anInt988; + public int anInt989; + public int anInt990; + public int anInt992; + public int anInt993; + public int anInt994; + + /** + * Gets the render animation definitions for the given id. + * @param animId The render animation id. + * @return The render animation definitions. + */ + public static RenderAnimationDefinition forId(int animId) { + if (animId == -1) { + return null; + } + byte[] data = Cache.getIndexes()[2].getFileData(32, animId); + RenderAnimationDefinition defs = new RenderAnimationDefinition(); + if (data != null) { + defs.parse(ByteBuffer.wrap(data)); + } else { + log(RenderAnimationDefinition.class, Log.ERR, "No definitions found for render animation " + animId + ", size=" + Cache.getIndexes()[2].getFilesSize(32) + "!"); + } + return defs; + } + + private void parse(ByteBuffer buffer) { + for (;;) { + int opcode = buffer.get() & 0xFF; + if (opcode == 0) { + break; + } + parseOpcode(buffer, opcode); + } + } + + private void parseOpcode(ByteBuffer buffer, int opcode) { + if (opcode == 54) { + @SuppressWarnings("unused") + int anInt1260 = (buffer.get() & 0xFF) << 6; + @SuppressWarnings("unused") + int anInt1227 = (buffer.get() & 0xFF) << 6; + } else if (opcode == 55) { + int[] anIntArray1246 = new int[12]; + int i_14_ = buffer.get() & 0xFF; + anIntArray1246[i_14_] = buffer.getShort() & 0xFFFF; + } else if (opcode == 56) { + int[][] anIntArrayArray1217 = new int[12][]; + int i_12_ = buffer.get() & 0xFF; + anIntArrayArray1217[i_12_] = new int[3]; + for (int i_13_ = 0; i_13_ < 3; i_13_++) + anIntArrayArray1217[i_12_][i_13_] = buffer.getShort(); + } else if ((opcode ^ 0xffffffff) != -2) { + if ((opcode ^ 0xffffffff) != -3) { + if (opcode != 3) { + if ((opcode ^ 0xffffffff) != -5) { + if (opcode == 5) + anInt977 = buffer.getShort() & 0xFFFF; + else if ((opcode ^ 0xffffffff) != -7) { + if (opcode == 7) + anInt960 = buffer.getShort() & 0xFFFF; + else if ((opcode ^ 0xffffffff) == -9) + anInt985 = buffer.getShort() & 0xFFFF; + else if (opcode == 9) + anInt957 = buffer.getShort() & 0xFFFF; + else if (opcode == 26) { + anInt973 = (short) (4 * buffer + .get() & 0xFF); + anInt975 = (short) (buffer.get() & 0xFF * 4); + } else if ((opcode ^ 0xffffffff) == -28) { + if (anIntArrayArray979 == null) + anIntArrayArray979 = new int[12][]; + int i = buffer.get() & 0xFF; + anIntArrayArray979[i] = new int[6]; + for (int i_1_ = 0; (i_1_ ^ 0xffffffff) > -7; i_1_++) + anIntArrayArray979[i][i_1_] = buffer + .getShort(); + } else if ((opcode ^ 0xffffffff) == -29) { + anIntArray971 = new int[12]; + for (int i = 0; i < 12; i++) { + anIntArray971[i] = buffer + .get() & 0xFF; + if (anIntArray971[i] == 255) + anIntArray971[i] = -1; + } + } else if (opcode != 29) { + if (opcode != 30) { + if ((opcode ^ 0xffffffff) != -32) { + if (opcode != 32) { + if ((opcode ^ 0xffffffff) != -34) { + if (opcode != 34) { + if (opcode != 35) { + if ((opcode ^ 0xffffffff) != -37) { + if ((opcode ^ 0xffffffff) != -38) { + if (opcode != 38) { + if ((opcode ^ 0xffffffff) != -40) { + if ((opcode ^ 0xffffffff) != -41) { + if ((opcode ^ 0xffffffff) == -42) + turnCWAnimation = buffer + .getShort() & 0xFFFF; + else if (opcode != 42) { + if ((opcode ^ 0xffffffff) == -44) + buffer.getShort(); + else if ((opcode ^ 0xffffffff) != -45) { + if ((opcode ^ 0xffffffff) == -46) + anInt964 = buffer + .getShort() & 0xFFFF; + else if ((opcode ^ 0xffffffff) != -47) { + if (opcode == 47) + anInt966 = buffer + .getShort() & 0xFFFF; + else if (opcode == 48) + anInt989 = buffer + .getShort() & 0xFFFF; + else if (opcode != 49) { + if ((opcode ^ 0xffffffff) != -51) { + if (opcode != 51) { + if (opcode == 52) { + int i = buffer + .get() & 0xFF; + anIntArray959 = new int[i]; + standAnimationIds = new int[i]; + for (int i_2_ = 0; i_2_ < i; i_2_++) { + standAnimationIds[i_2_] = buffer + .getShort() & 0xFFFF; + int i_3_ = buffer + .get() & 0xFF; + anIntArray959[i_2_] = i_3_; + anInt994 += i_3_; + } + } else if (opcode == 53) + aBoolean978 = false; + } else + anInt962 = buffer + .getShort() & 0xFFFF; + } else + anInt990 = buffer + .getShort() & 0xFFFF; + } else + anInt952 = buffer + .getShort() & 0xFFFF; + } else + anInt983 = buffer + .getShort() & 0xFFFF; + } else + anInt955 = buffer + .getShort() & 0xFFFF; + } else + turnCCWAnimation = buffer + .getShort() & 0xFFFF; + } else + turn180Animation = buffer + .getShort() & 0xFFFF; + } else + anInt954 = buffer + .getShort() & 0xFFFF; + } else + anInt958 = (buffer + .getShort() & 0xFFFF); + } else + anInt951 = (buffer + .get() & 0xFF); + } else + anInt965 = (buffer + .getShort()); + } else + anInt969 = (buffer + .getShort() & 0xFFFF); + } else + anInt993 = buffer + .get() & 0xFF; + } else + anInt956 = (buffer.getShort()); + } else + anInt961 = buffer + .getShort() & 0xFFFF; + } else + anInt988 = buffer.get() & 0xFF; + } else + anInt980 = buffer.getShort() & 0xFFFF; + } else + anInt992 = buffer.get() & 0xFF; + } else + runAnimationId = buffer.getShort() & 0xFFFF; + } else + anInt986 = buffer.getShort() & 0xFFFF; + } else + anInt987 = buffer.getShort() & 0xFFFF; + } else + anInt974 = buffer.getShort() & 0xFFFF; + } else { + standAnimationId = buffer.getShort() & 0xFFFF; + walkAnimationId = buffer.getShort() & 0xFFFF; + if ((standAnimationId ^ 0xffffffff) == -65536) + standAnimationId = -1; + if ((walkAnimationId ^ 0xffffffff) == -65536) + walkAnimationId = -1; + } + } + + public RenderAnimationDefinition() { + anInt957 = -1; + anInt954 = -1; + anInt960 = -1; + anInt958 = -1; + anInt965 = 0; + anInt973 = 0; + turn180Animation = -1; + anInt956 = 0; + standAnimationId = -1; + standAnimationIds = null; + anInt952 = -1; + anInt983 = -1; + anInt985 = -1; + anInt962 = -1; + anInt966 = -1; + anInt977 = -1; + anInt975 = 0; + runAnimationId = -1; + anInt988 = 0; + turnCCWAnimation = -1; + anInt987 = -1; + anInt980 = 0; + anInt964 = -1; + walkAnimationId = -1; + anInt986 = -1; + aBoolean978 = true; + anInt992 = 0; + anInt955 = -1; + anInt989 = -1; + anInt974 = -1; + anInt969 = 0; + anInt994 = 0; + anInt990 = -1; + anInt993 = 0; + } + + public static void main(String...args) throws Throwable { + GameWorld.prompt(false); + RenderAnimationDefinition def = RenderAnimationDefinition.forId(1426); + + for (Field f : def.getClass().getDeclaredFields()) { + if (!Modifier.isStatic(f.getModifiers())) { + if (f.getType().isArray()) { + Object object = f.get(def); + if (object != null) { + int length = Array.getLength(object); + System.out.print(f.getName() + ", ["); + for (int i = 0; i < length; i++) { + System.out.print(Array.get(object, i) + (i < (length - 1) ? ", " : "]")); + } + continue; + } + } + + } + } + for (Field f : def.getClass().getSuperclass().getDeclaredFields()) { + if (!Modifier.isStatic(f.getModifiers())) { + if (f.getType().isArray()) { + Object object = f.get(def); + if (object != null) { + int length = Array.getLength(object); + System.out.print(f.getName() + ", ["); + for (int i = 0; i < length; i++) { + System.out.print(Array.get(object, i) + (i < (length - 1) ? ", " : "]")); + } + continue; + } + } + + } + } + } +} diff --git a/Server/src/main/java/core/cache/def/impl/SceneryDefinition.java b/Server/src/main/core/cache/def/impl/SceneryDefinition.java similarity index 95% rename from Server/src/main/java/core/cache/def/impl/SceneryDefinition.java rename to Server/src/main/core/cache/def/impl/SceneryDefinition.java index 155a22803..6172d7828 100644 --- a/Server/src/main/java/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 rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; +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 @@ -312,7 +315,7 @@ public class SceneryDefinition extends Definition { /** * The second integer. */ - public int secondInt; + public int interactable; /** * A unknown boolean. @@ -493,7 +496,7 @@ public class SceneryDefinition extends Definition { aByte3914 = (byte) 0; anInt3915 = 0; anInt3900 = 0; - secondInt = -1; + interactable = -1; aBoolean3894 = false; aByte3912 = (byte) 0; anInt3921 = 0; @@ -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("[ObjectDefinition] Buffer empty for " + objectId); + log(SceneryDefinition.class, Log.ERR, "Buffer empty for " + objectId); break; } int opcode = buffer.get() & 0xFF; @@ -648,7 +651,7 @@ public class SceneryDefinition extends Definition { } else if (opcode == 18) { def.projectileClipped = false; } else if (opcode == 19) { - def.secondInt = buffer.get() & 0xFF; + def.interactable = buffer.get() & 0xFF; } else if (opcode == 21) { def.aByte3912 = (byte) 1; } else if (opcode == 22) { @@ -792,7 +795,7 @@ public class SceneryDefinition extends Definition { int length = buffer.get() & 0xFF; for (int i = 0; i < length; i++) { boolean string = buffer.get() == 1; - ByteBufferUtils.getTriByte(buffer); // script id + ByteBufferUtils.getMedium(buffer); // script id if (!string) { buffer.getInt(); // Value } else { @@ -801,7 +804,7 @@ public class SceneryDefinition extends Definition { } } else { if (opcode != 0) { - SystemLogger.logErr("Unhandled object definition opcode: " + opcode); + log(SceneryDefinition.class, Log.ERR, "Unhandled object definition opcode: " + opcode); } break; } @@ -818,18 +821,24 @@ public class SceneryDefinition extends Definition { * Configures the object definitions. */ final void configureObject() { - if (secondInt == -1) { - secondInt = 0; + if (interactable == -1) { + interactable = 0; if (modelIds != null && (getModelConfiguration() == null || getModelConfiguration()[0] == 10)) { - secondInt = 1; + interactable = 1; } for (int i = 0; i < 5; i++) { if (options[i] != null) { - secondInt = 1; + interactable = 1; break; } } } + if(childrenIds != null) { + for (int i = 0; i < childrenIds.length; ++i) { + SceneryDefinition def = forId(childrenIds[i]); + def.configFileId = configFileId; + } + } if (anInt3855 == -1) { anInt3855 = clipType == 0 ? 0 : 1; } @@ -847,6 +856,9 @@ public class SceneryDefinition extends Definition { * @return {@code True} if so. */ public boolean hasActions() { + if(interactable > 0) { + return true; + } if (childrenIds == null) { return hasOptions(false); } @@ -878,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]); } /** @@ -1354,11 +1374,11 @@ public class SceneryDefinition extends Definition { } /** - * Get the secondInt. - * @return the secondInt + * Get the interactable. + * @return the interactable */ - public int getSecondInt() { - return secondInt; + public int getInteractable() { + return interactable; } /** @@ -1672,4 +1692,4 @@ public class SceneryDefinition extends Definition { public static int getContainerId(int id) { return id >>> 8; } -} \ No newline at end of file +} 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 new file mode 100644 index 000000000..92081ade0 --- /dev/null +++ b/Server/src/main/core/cache/def/impl/Struct.java @@ -0,0 +1,100 @@ +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 { + + /** + * The struct definitions mapping. + */ + private static final Map definitions = new HashMap<>(); + + /** + * The enum id. + */ + private final int id; + + public HashMap dataStore = new HashMap<>(); + + public Struct(int id) { + this.id = id; + } + + public int getInt(int key){ + if(!dataStore.containsKey(key)){ + log(this.getClass(), Log.ERR, "Invalid value passed for key: " + key + " struct: " + id); + return -1; + } + return (int) dataStore.get(key); + } + + public String getString(int key){ + return (String) dataStore.get(key); + } + + @Override + public String toString() { + return "Struct{" + + "id=" + id + + ", dataStore=" + dataStore + + '}'; + } + + public static Struct get(int id){ + Struct def = definitions.get(id); + if (def != null) { + return def; + } + byte[] data = Cache.getIndexes()[2].getFileData(26, id); + def = parse(id, data); + + definitions.put(id, def); + return def; + } + + public static Struct parse(int id, byte[] data) + { + Struct def = new Struct(id); + if (data != null) { + ByteBuffer buffer = ByteBuffer.wrap(data); + int opcode; + + while ((opcode = buffer.get() & 0xFF) != 0) { + + if (opcode == 249) { + int size = buffer.get() & 0xFF; + + for (int i = 0; size > i; i++) { + boolean bool = (buffer.get() & 0xFF) == 1; + int key = ByteBufferUtils.getMedium(buffer); + Object value; + if (bool) { + value = ByteBufferUtils.getString(buffer); + } else { + value = buffer.getInt(); + } + def.dataStore.put(key, value); + } + } + } + } + return def; + } + + public static int getCount() { + return Cache.getIndexes()[2].getFilesSize(26); + } + + public int getId() { + return id; + } +} diff --git a/Server/src/main/core/cache/def/impl/VarbitDefinition.java b/Server/src/main/core/cache/def/impl/VarbitDefinition.java new file mode 100644 index 000000000..9fb4f6b15 --- /dev/null +++ b/Server/src/main/core/cache/def/impl/VarbitDefinition.java @@ -0,0 +1,173 @@ +package core.cache.def.impl; + +import core.cache.Cache; +import core.game.node.entity.player.Player; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import static core.api.ContentAPIKt.*; + +/** + * Handles config definition reading. + * @author Emperor + */ +public final class VarbitDefinition { + + /** + * The config definitions mapping. + */ + private static final Map MAPPING = new HashMap<>(); + + /** + * The bit size flags. + */ + private static final int[] BITS = new int[32]; + + /** + * The file id. + */ + private final int id; + + /** + * The config id. + */ + private int varpId; + + /** + * The bit shift amount. + */ + private int startBit; + + /** + * The bit amount. + */ + private int endBit; + + /** + * Constructs a new {@code ConfigFileDefinition} {@code Object}. + * @param id The file id. + */ + public VarbitDefinition(int id) { + this.id = id; + } + + public VarbitDefinition(int varpId, int id, int startBit, int endBit) + { + this.varpId = varpId; + this.id = id; + this.startBit = startBit; + this.endBit = endBit; + } + + /** + * Initializes the bit flags. + */ + static { + int flag = 2; + for (int i = 0; i < 32; i++) { + BITS[i] = flag - 1; + flag += flag; + } + } + + /** + * Gets the config file definitions for the given file id. + * @param id The file id. + * @return The definition. + */ + public static VarbitDefinition forObjectID(int id) { + return forId(id); + } + + public static VarbitDefinition forNPCID(int id){ + return forId(id); + } + + public static VarbitDefinition forItemID(int id){ + return forId(id); + } + + public static VarbitDefinition forId(int id){ + VarbitDefinition def = MAPPING.get(id); + if (def != null) { + return def; + } + + def = new VarbitDefinition(id); + byte[] bs = Cache.getIndexes()[22].getFileData(id >>> 10, id & 0x3ff); + if (bs != null) { + ByteBuffer buffer = ByteBuffer.wrap(bs); + int opcode = 0; + while ((opcode = buffer.get() & 0xFF) != 0) { + if (opcode == 1) { + def.varpId = buffer.getShort() & 0xFFFF; + def.startBit = buffer.get() & 0xFF; + def.endBit = buffer.get() & 0xFF; + } + } + } + MAPPING.put(id, def); + return def; + } + + public static void create(int varpId, int varbitId, int startBit, int endBit){ + VarbitDefinition def = new VarbitDefinition( + varpId, + varbitId, + startBit, + endBit + ); + MAPPING.put(varbitId, def); + } + + /** + * Gets the current config value for this file. + * @param player The player. + * @return The config value. + */ + public int getValue(Player player) { + return getVarbit(player, id); + } + + /** + * Gets the mapping. + * @return The mapping. + */ + public static Map getMapping() { + return MAPPING; + } + + /** + * Gets the id. + * @return The id. + */ + public int getId() { + return id; + } + + public int getVarpId() { + return varpId; + } + + public int getStartBit() { + return startBit; + } + + public int getEndBit() { + 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 + "]"; + } +} diff --git a/Server/src/main/java/core/cache/def/impl/test.txt b/Server/src/main/core/cache/def/impl/test.txt similarity index 100% rename from Server/src/main/java/core/cache/def/impl/test.txt rename to Server/src/main/core/cache/def/impl/test.txt diff --git a/Server/src/main/java/core/cache/gzip/GZipDecompressor.java b/Server/src/main/core/cache/gzip/GZipDecompressor.java similarity index 100% rename from Server/src/main/java/core/cache/gzip/GZipDecompressor.java rename to Server/src/main/core/cache/gzip/GZipDecompressor.java 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.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.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/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/java/core/game/content/activity/ActivityManager.java b/Server/src/main/core/game/activity/ActivityManager.java similarity index 87% rename from Server/src/main/java/core/game/content/activity/ActivityManager.java rename to Server/src/main/core/game/activity/ActivityManager.java index 700a369d8..15ab8ff39 100644 --- a/Server/src/main/java/core/game/content/activity/ActivityManager.java +++ b/Server/src/main/core/game/activity/ActivityManager.java @@ -1,12 +1,15 @@ -package core.game.content.activity; +package core.game.activity; import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; +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("Unhandled activity - " + name + "!"); + log(ActivityManager.class, Log.ERR, "Unhandled activity - " + name + "!"); } return false; } diff --git a/Server/src/main/java/core/game/content/activity/ActivityPlugin.java b/Server/src/main/core/game/activity/ActivityPlugin.java similarity index 93% rename from Server/src/main/java/core/game/content/activity/ActivityPlugin.java rename to Server/src/main/core/game/activity/ActivityPlugin.java index 1b9657e63..50ea847f5 100644 --- a/Server/src/main/java/core/game/content/activity/ActivityPlugin.java +++ b/Server/src/main/core/game/activity/ActivityPlugin.java @@ -1,6 +1,6 @@ -package core.game.content.activity; +package core.game.activity; -import rs09.ServerConstants; +import core.ServerConstants; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; import core.game.world.map.Location; @@ -44,6 +44,12 @@ public abstract class ActivityPlugin extends MapZone implements Plugin { */ protected Location base; + /** + * A location which the player is teleported to in the case of death, if this activity is safe. + * Defaults to the home location of the server. + */ + protected Location safeRespawn = ServerConstants.HOME_LOCATION; + /** * The player. */ @@ -134,6 +140,7 @@ public abstract class ActivityPlugin extends MapZone implements Plugin { e.getProperties().setSpawnLocation(l); } e.getProperties().setSafeZone(safe); + e.getProperties().safeRespawn = this.safeRespawn; e.setAttribute("activity", this); return super.enter(e); } @@ -148,6 +155,7 @@ public abstract class ActivityPlugin extends MapZone implements Plugin { e.setLocation(l); } e.getProperties().setSafeZone(false); + e.getProperties().safeRespawn = ServerConstants.HOME_LOCATION; e.removeAttribute("activity"); return super.leave(e, logout); } diff --git a/Server/src/main/core/game/activity/Cutscene.kt b/Server/src/main/core/game/activity/Cutscene.kt new file mode 100644 index 000000000..a75dcb4b5 --- /dev/null +++ b/Server/src/main/core/game/activity/Cutscene.kt @@ -0,0 +1,509 @@ +package core.game.activity + +import core.api.* +import core.game.event.EventHook +import core.game.event.SelfDeathEvent +import core.game.component.Component +import core.game.interaction.MovementPulse +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.system.task.Pulse +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.Region +import core.game.world.map.RegionManager +import core.game.world.map.build.DynamicRegion +import core.game.world.map.path.Pathfinder +import core.net.packet.PacketRepository +import core.net.packet.context.MinimapStateContext +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.game.system.timer.impl.AntiMacro +import core.game.world.GameWorld +import core.tools.Log + +/** + * A utility class for making cutscenes. + * @author Ceikry + */ +abstract class Cutscene(val player: Player) { + lateinit var region: Region + lateinit var base: Location + var exitLocation: Location = player.location.transform(0,0,0) + var ended = false + + val camera = PlayerCamera(player) + private val addedNPCs = HashMap>() + + abstract fun setup() + abstract fun runStage(stage: Int) + + /** + * Creates a new dynamic copy of the region identified by regionId, sets this cutscene's region as this new copy, and clears any cutscene-spawned NPCs from the previous region. + * @param regionId the region ID to duplicate. + */ + fun loadRegion(regionId: Int) + { + clearNPCs() + logCutscene("Creating new instance of region $regionId for ${player.username}.") + region = DynamicRegion.create(regionId) + logCutscene("Dynamic region instantiated for ${player.username}. Global coordinates: ${region.baseLocation}.") + 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. + */ + fun fadeToBlack() + { + logCutscene("Fading ${player.username}'s screen to black.") + player.interfaceManager.closeOverlay() + player.interfaceManager.openOverlay(Component(Components.FADE_TO_BLACK_120)) + } + + /** + * Fade the player's view from black back to normal. This process can be safely assumed to take about 6 ticks to complete. + */ + fun fadeFromBlack() + { + logCutscene("Fading ${player.username}'s screen from black to normal.") + player.interfaceManager.closeOverlay() + player.interfaceManager.openOverlay(Component(Components.FADE_FROM_BLACK_170)) + } + + /** + * Teleports an entity to a pair of coordinates within the currently active cutscene region + * @param entity the entity to teleport + * @param regionX the region X coordinate to teleport the entity to (0-63) + * @param regionY the region Y coordinate to teleport the entity to (0-63) + * @param plane (optional) the plane to teleport to (0-3) + */ + fun teleport(entity: Entity, regionX: Int, regionY: Int, plane: Int = 0) + { + val newLoc = base.transform(regionX, regionY, plane) + logCutscene("Teleporting ${entity.username} to coordinates: LOCAL[$regionX,$regionY,$plane] GLOBAL[${newLoc.x},${newLoc.y},$plane].") + entity.properties.teleportLocation = newLoc + } + + /** + * Moves an entity to the given coordinates within the currently active cutscene region + * @param entity the entity to move + * @param regionX the region X coordinate to move the entity to (0-63) + * @param regionY the region Y coordinate to move the entity to (0-63) + */ + fun move(entity: Entity, regionX: Int, regionY: Int) + { + logCutscene("Moving ${entity.username} to LOCAL[$regionX,$regionY].") + entity.pulseManager.run(object : MovementPulse(entity, base.transform(regionX,regionY,0), Pathfinder.SMART) { + override fun pulse(): Boolean { + return true + } + }) + } + + /** + * 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. + * @param hide Should the continue button be hidden? + */ + 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, 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 + * @param onContinue (optional) a method that runs when the dialogue is "continued." Increments the cutscene stage by default. + */ + fun dialogueUpdate(message: String, onContinue: () -> Unit = {incrementStage()}) + { + logCutscene("Sending standard dialogue update.") + sendDialogue(player, message) + 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 + * @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 playerDialogueUpdate(expression: core.game.dialogue.FacialExpression, message: String, onContinue: () -> Unit = {incrementStage()}) + { + logCutscene("Sending player dialogue update") + sendPlayerDialogue(player, message, expression) + player.dialogueInterpreter.addAction{_,_ -> onContinue.invoke()} + } + + /** + * Updates the stage after a set number of ticks. + * @param ticks the number of ticks to wait + * @param newStage (optional) the new stage to update to. If not passed, stage is incremented instead. + */ + fun timedUpdate(ticks: Int, newStage: Int = -1) + { + logCutscene("Executing timed updated for $ticks ticks.") + GameWorld.Pulser.submit(object : Pulse(ticks) + { + override fun pulse(): Boolean { + if(newStage == -1) + incrementStage() + else + updateStage(newStage) + return true + } + }) + } + + /** + * Retrieves the first NPC from the added-to-cutscene NPCs with a matching ID. + * @param id the ID to grab for. + * @return the first NPC, or null if there are no matching NPCs. + */ + fun getNPC(id: Int): NPC? + { + return addedNPCs[id]?.firstOrNull() + } + + /** + * Retrieves all NPCs from the added-to-cutscene NPCs with a matching ID. + * @param id the ID to grab for. + * @return an ArrayList of the matching NPCs, which will be empty if there were no matches. + */ + fun getNPCs(id: Int): ArrayList + { + return addedNPCs[id] ?: ArrayList() + } + + /** + * Retrieves the object in the region at the given regionX and regionY + * @param regionX the region-local X coordinate to grab the object from (0-63) + * @param regionY the region-local Y coordinate to grab the object from (0-63) + * @param plane the plane to grab the object from (0-3) + */ + fun getObject(regionX: Int, regionY: Int, plane: Int = 0): Scenery? + { + val obj = RegionManager.getObject(base.transform(regionX,regionY,plane)) + logCutscene("Retrieving object at LOCAL[$regionX,$regionY], GOT: ${obj?.definition?.name ?: "null"}.") + return obj + } + + /** + * Adds an NPC to the cutscene with the given ID, at the region-local X and Y coordinates. + * @param id the ID of the NPC to add. + * @param regionX the region-local X coordinate to place the NPC at + * @param regionY the region-local Y coordinate to place the NPC at + * @param direction the direction the NPC faces when it is initially placed. + * @param plane the plane to place te NPC on. Default is 0. + */ + fun addNPC(id: Int, regionX: Int, regionY: Int, direction: Direction, plane: Int = 0) + { + val npc = NPC(id) + npc.isRespawn = false + npc.isAggressive = false + npc.isWalks = false + npc.location = base.transform(regionX, regionY, plane) + npc.init() + npc.faceLocation(npc.location.transform(direction)) + + val npcs = addedNPCs[id] ?: ArrayList() + npcs.add(npc) + addedNPCs[id] = npcs + logCutscene("Added NPC $id at location LOCAL[$regionX,$regionY,$plane] GLOBAL[${npc.location.x},${npc.location.y},$plane]") + } + + 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() + if (hideMiniMap) { + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + } + runStage(player.getCutsceneStage()) + setAttribute(player, ATTRIBUTE_CUTSCENE, this) + setAttribute(player, ATTRIBUTE_CUTSCENE_STAGE, 0) + player.interfaceManager.removeTabs(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14) + player.properties.isSafeZone = true + player.properties.safeRespawn = player.location + player.lock() + player.hook(Event.SelfDeath, CUTSCENE_DEATH_HOOK) + player.logoutListeners["cutscene"] = {player -> player.location = exitLocation; player.getCutscene()?.end() } + AntiMacro.pause(player) + } + + + /** + * 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 endWithoutFade(endActions: (() -> Unit)? = null) + { + ended = true + GameWorld.Pulser.submit(object : Pulse(){ + var tick: Int = 0 + override fun pulse(): Boolean { + when(tick++) + { + 0 -> player.properties.teleportLocation = exitLocation + 1 -> { + return true + } + } + return false + } + + 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() + } + } + }) + } + + /** + * Ends this cutscene, fading the screen to black, teleporting the player to the exit location, and then fading it back in and executing the endActions passed to this method. + * @param fade (optional) should the cutscene fade to black? + * @param endActions (optional) a method that executes when the cutscene fully completes + */ + fun end(fade: Boolean = true, endActions: (() -> Unit)? = null) + { + ended = true + if (fade) fadeToBlack() + GameWorld.Pulser.submit(object : Pulse(){ + var tick: Int = 0 + override fun pulse(): Boolean { + if (fade) { + when (tick++) { + 8 -> player.properties.teleportLocation = exitLocation + 9 -> fadeFromBlack() + 16 -> return true + else -> return false + } + } + else player.properties.teleportLocation = exitLocation + return true + } + + override fun stop() { + super.stop() + player ?: return + player.removeAttribute(ATTRIBUTE_CUTSCENE) + player.removeAttribute(ATTRIBUTE_CUTSCENE_STAGE) + player.properties.isSafeZone = false + player.properties.safeRespawn = ServerConstants.HOME_LOCATION + player.interfaceManager.restoreTabs() + player.unlock() + clearNPCs() + player.unhook(CUTSCENE_DEATH_HOOK) + player.logoutListeners.remove("cutscene") + AntiMacro.unpause(player) + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + try { + endActions?.invoke() + } catch (e: Exception) { + log(this::class.java, Log.ERR, "There's some bad nasty code in ${this::class.java.simpleName} end actions!") + e.printStackTrace() + } + } + }) + } + + /** + * Moves the camera to the specified regionX and regionY + * @param regionX the region-local X coordinate to move the camera to (0-63) + * @param regionY the region-local Y coordinate to move 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 moveCamera(regionX: Int, regionY: Int, height: Int = 300, speed: Int = 100) + { + val globalLoc = base.transform(regionX, regionY, 0) + camera.panTo(globalLoc.x, globalLoc.y, height, speed) + } + + /** + * Rotates the camera to face the given region-local X and Y coordinates + * @param regionX the region-local X coordinate to rotate the camera to (0-63) + * @param regionY the region-local Y coordinate 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 rotateCamera(regionX: Int, regionY: Int, height: Int = 300, speed: Int = 100) + { + val globalLoc = base.transform(regionX, regionY, 0) + 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. + */ + fun setExit(location: Location) + { + exitLocation = location + } + + private fun loadCurrentStage() + { + if(ended) return + runStage(player.getCutsceneStage()) + } + + /** + * Increments the player's stage attribute by 1 + */ + fun incrementStage() + { + setAttribute(player, ATTRIBUTE_CUTSCENE_STAGE, player.getCutsceneStage() + 1) + loadCurrentStage() + } + + /** + * Sets the player's stage attribute to a new stage + */ + fun updateStage(newStage: Int) + { + setAttribute(player, ATTRIBUTE_CUTSCENE_STAGE, newStage) + loadCurrentStage() + } + + fun logCutscene(message: String) + { + if(ServerConstants.LOG_CUTSCENE) + log(this::class.java, Log.FINE, "$message") + } + + fun clearNPCs() { + for(entry in addedNPCs.entries) + { + logCutscene("Clearing ${entry.value.size} NPCs with ID ${entry.key} for ${player.username}.") + for(npc in entry.value) npc.clear() + } + addedNPCs.clear() + } + + companion object { + const val ATTRIBUTE_CUTSCENE = "cutscene" + const val ATTRIBUTE_CUTSCENE_STAGE = "cutscene:stage" + object CUTSCENE_DEATH_HOOK : EventHook + { + override fun process(entity: Entity, event: SelfDeathEvent) { + if(entity !is Player) return + entity.getCutscene()?.end() ?: entity.unhook(this) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/CutscenePlugin.java b/Server/src/main/core/game/activity/CutscenePlugin.java similarity index 96% rename from Server/src/main/java/core/game/content/activity/CutscenePlugin.java rename to Server/src/main/core/game/activity/CutscenePlugin.java index bc9a214a1..043b7c179 100644 --- a/Server/src/main/java/core/game/content/activity/CutscenePlugin.java +++ b/Server/src/main/core/game/activity/CutscenePlugin.java @@ -1,11 +1,11 @@ -package core.game.content.activity; +package core.game.activity; import core.game.component.Component; 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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.build.DynamicRegion; import core.net.packet.PacketRepository; @@ -77,7 +77,7 @@ public abstract class CutscenePlugin extends ActivityPlugin { GameWorld.getPulser().submit(getStartPulse()); } else { PacketRepository.send(MinimapState.class, new MinimapStateContext(player, getMapState())); - player.getInterfaceManager().hideTabs(getRemovedTabs()); + player.getInterfaceManager().removeTabs(getRemovedTabs()); player.getProperties().setTeleportLocation(getStartLocation()); player.unlock(); player.getWalkingQueue().reset(); @@ -174,7 +174,7 @@ public abstract class CutscenePlugin extends ActivityPlugin { break; case 3: PacketRepository.send(MinimapState.class, new MinimapStateContext(player, getMapState())); - player.getInterfaceManager().hideTabs(getRemovedTabs()); + player.getInterfaceManager().removeTabs(getRemovedTabs()); break; case 4: player.getProperties().setTeleportLocation(getStartLocation()); @@ -222,7 +222,7 @@ public abstract class CutscenePlugin extends ActivityPlugin { break; case 3: PacketRepository.send(MinimapState.class, new MinimapStateContext(player, getMapState())); - player.getInterfaceManager().hideTabs(getRemovedTabs()); + player.getInterfaceManager().removeTabs(getRemovedTabs()); break; case 4: Location loc = (Location) (player.getAttribute("real-end", player.getAttribute("cutscene:original-loc", player.getLocation()))); diff --git a/Server/src/main/java/rs09/game/ai/AIPBuilder.java b/Server/src/main/core/game/bots/AIPBuilder.java similarity index 99% rename from Server/src/main/java/rs09/game/ai/AIPBuilder.java rename to Server/src/main/core/game/bots/AIPBuilder.java index 244dc5aa9..fb619f730 100644 --- a/Server/src/main/java/rs09/game/ai/AIPBuilder.java +++ b/Server/src/main/core/game/bots/AIPBuilder.java @@ -1,4 +1,4 @@ -package rs09.game.ai; +package core.game.bots; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; @@ -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/java/rs09/game/ai/AIPlayer.java b/Server/src/main/core/game/bots/AIPlayer.java similarity index 88% rename from Server/src/main/java/rs09/game/ai/AIPlayer.java rename to Server/src/main/core/game/bots/AIPlayer.java index ffa0c3e06..e54fe732a 100644 --- a/Server/src/main/java/rs09/game/ai/AIPlayer.java +++ b/Server/src/main/core/game/bots/AIPlayer.java @@ -1,11 +1,10 @@ -package rs09.game.ai; +package core.game.bots; -import rs09.ServerConstants; +import core.game.node.entity.impl.PulseType; +import core.ServerConstants; import core.game.container.impl.EquipmentContainer; import core.game.interaction.DestinationFlag; import core.game.interaction.MovementPulse; -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.npc.NPC; @@ -18,15 +17,12 @@ import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.path.Pathfinder; import core.game.world.map.zone.impl.WildernessZone; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; import core.net.packet.context.MessageContext; -import core.net.packet.in.InteractionPacket; -import core.plugin.Plugin; import core.tools.RandomFunction; import core.tools.StringUtils; -import core.game.content.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; -import core.game.content.quest.tutorials.tutorialisland.CharacterDesign; +import content.region.misc.tutisland.handlers.iface.CharacterDesign; import java.io.File; import java.io.FileNotFoundException; @@ -44,20 +40,13 @@ public class AIPlayer extends Player { */ private static int currentUID = 0x1; - private static List botNames = new ArrayList(); - - private static List usedNames = new ArrayList(); + private static final List botNames = new ArrayList(); /** * The active Artificial intelligent players mapping. */ private static final Map botMapping = new HashMap<>(); - /** - * The aip control dialogue. - */ - private static final AIPControlDialogue CONTROL_DIAL = new AIPControlDialogue(); - /** * A line of data from namesandarmor.txt that will be used to generate the appearance * Data in format: @@ -120,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 @@ -194,8 +183,8 @@ public class AIPlayer extends Player { public static void loadNames(String fileName){ try { Scanner sc = new Scanner(new File(ServerConstants.BOT_DATA_PATH + fileName)); - while(sc.hasNextLine()){ - botNames.add(sc.next()); + while (sc.hasNextLine()) { + botNames.add(sc.nextLine()); } } catch (Exception e){ e.printStackTrace(); @@ -205,11 +194,7 @@ public class AIPlayer extends Player { public static String getRandomName(){ int index = (RandomFunction.random(botNames.size())); String name = botNames.get(index); - while(usedNames.contains(name)){ - index = (RandomFunction.random(botNames.size())); - name = botNames.get(index); - } - usedNames.add(name); + botNames.remove(index); return name; } @@ -232,7 +217,7 @@ public class AIPlayer extends Player { } } } catch (FileNotFoundException e) { - System.out.println("Missing " + fileName); + e.printStackTrace(); } } @@ -257,27 +242,6 @@ public class AIPlayer extends Player { super.init(); getSettings().setRunToggled(true); CharacterDesign.randomize(this, false); - getInteraction().set(new Option("Control", 7).setHandler(new OptionHandler() { - @Override - public Plugin newInstance(Object arg) throws Throwable { - return null; - } - - @Override - public boolean handle(Player p, Node node, String option) { - DialoguePlugin dial = CONTROL_DIAL.newInstance(p); - if (dial != null && dial.open(AIPlayer.this)) { - p.getDialogueInterpreter().setDialogue(dial); - } - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - })); getPlayerFlags().setLastSceneGraph(location); } @@ -293,7 +257,7 @@ public class AIPlayer extends Player { face(e); return false; } - }, "movement"); + }, PulseType.STANDARD); } public void randomWalkAroundPoint(Location point, int radius) { @@ -385,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())) @@ -417,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); @@ -427,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); @@ -437,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); @@ -489,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; } @@ -507,7 +471,7 @@ public class AIPlayer extends Player { private Node getClosestNodeinNodeList(ArrayList nodes) { if (nodes.isEmpty()) { - //System.out.println("nodelist empty"); + return null; } @@ -526,7 +490,7 @@ public class AIPlayer extends Player { @Override public void clear() { botMapping.remove(uid); - super.clear(true); + super.clear(); } @Override @@ -564,7 +528,7 @@ public class AIPlayer extends Player { Repository.getPlayers().remove(player); return; } - //SystemLogger.logErr("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 @@ -609,6 +573,6 @@ public class AIPlayer extends Player { public void interact(Node n) { - InteractionPacket.handleObjectInteraction(this, 0, n.getLocation(), n.getId()); + // InteractionPacket.handleObjectInteraction(this, 0, n.getLocation(), n.getId()); } } \ No newline at end of file diff --git a/Server/src/main/core/game/bots/AIRepository.kt b/Server/src/main/core/game/bots/AIRepository.kt new file mode 100644 index 000000000..3faef5166 --- /dev/null +++ b/Server/src/main/core/game/bots/AIRepository.kt @@ -0,0 +1,69 @@ +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. + * @author Ceikry + */ +class AIRepository { + companion object { + val groundItems = HashMap>() + val GEOffers = HashMap() + + @JvmStatic + val PulseRepository = HashMap() // Lowercase username, pulse + + @JvmStatic + fun addItem(item: GroundItem){ + if(groundItems[item.dropper] == null){ + val list = ArrayList() + list.add(item) + groundItems[item.dropper] = list + return + } + groundItems[item.dropper]!!.add(item) + } + + @JvmStatic + fun getItems(player: Player): ArrayList?{ + return groundItems[player] + } + + @JvmStatic + fun addOffer(player: Player, offer: GrandExchangeOffer){ + GEOffers[player] = offer + } + + @JvmStatic + fun getOffer(player: Player): GrandExchangeOffer? { + return GEOffers[player] + } + + @JvmStatic fun clearAllBots() { + 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/java/rs09/game/ai/ArtificialSession.java b/Server/src/main/core/game/bots/ArtificialSession.java similarity index 97% rename from Server/src/main/java/rs09/game/ai/ArtificialSession.java rename to Server/src/main/core/game/bots/ArtificialSession.java index 0a6222de8..81ef7dd0c 100644 --- a/Server/src/main/java/rs09/game/ai/ArtificialSession.java +++ b/Server/src/main/core/game/bots/ArtificialSession.java @@ -1,4 +1,4 @@ -package rs09.game.ai; +package core.game.bots; import java.nio.ByteBuffer; diff --git a/Server/src/main/kotlin/rs09/game/ai/pvmbots/CombatBot.kt b/Server/src/main/core/game/bots/CombatBot.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/ai/pvmbots/CombatBot.kt rename to Server/src/main/core/game/bots/CombatBot.kt index 7890787ed..a5b5c6780 100644 --- a/Server/src/main/kotlin/rs09/game/ai/pvmbots/CombatBot.kt +++ b/Server/src/main/core/game/bots/CombatBot.kt @@ -1,4 +1,4 @@ -package rs09.game.ai.pvmbots +package core.game.bots import core.game.node.entity.player.link.appearance.Gender import core.game.node.entity.player.link.prayer.PrayerType @@ -6,11 +6,10 @@ import core.game.node.item.Item import core.game.world.map.Direction import core.game.world.map.Location import core.tools.RandomFunction -import rs09.game.ai.AIPlayer -import core.game.content.consumable.Consumable -import core.game.content.consumable.Consumables -import core.game.content.consumable.Food -import core.game.content.consumable.effects.HealingEffect +import core.game.consumable.Consumable +import content.data.consumables.Consumables +import core.game.consumable.Food +import content.data.consumables.effects.HealingEffect import core.game.node.entity.skill.Skills import java.util.* @@ -44,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/kotlin/rs09/game/ai/pvmbots/CombatBotAssembler.kt b/Server/src/main/core/game/bots/CombatBotAssembler.kt similarity index 96% rename from Server/src/main/kotlin/rs09/game/ai/pvmbots/CombatBotAssembler.kt rename to Server/src/main/core/game/bots/CombatBotAssembler.kt index e161c7d89..ebbed1402 100644 --- a/Server/src/main/kotlin/rs09/game/ai/pvmbots/CombatBotAssembler.kt +++ b/Server/src/main/core/game/bots/CombatBotAssembler.kt @@ -1,11 +1,10 @@ -package rs09.game.ai.pvmbots +package core.game.bots import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.world.map.Location import core.tools.RandomFunction import org.rs09.consts.Items -import rs09.game.ai.AIPlayer import java.util.* import kotlin.collections.ArrayList @@ -54,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 } @@ -80,10 +79,10 @@ class CombatBotAssembler { * @return a CombatBot suited for killing dragons. * @author Kermit */ - fun MeleeAdventurer(tier: Tier, location: Location): CombatBot{ + 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) @@ -112,10 +111,10 @@ class CombatBotAssembler { * @return a CombatBot suited for killing dragons. * @author Kermit */ - fun RangeAdventurer(tier: Tier, location: Location): CombatBot{ + 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) @@ -146,7 +145,7 @@ class CombatBotAssembler { * @return a CombatBot suited for killing dragons. * @author Ceikry */ - fun assembleMeleeDragonBot(tier: Tier, location: Location): CombatBot{ + fun assembleMeleeDragonBot(tier: Tier, location: Location): CombatBot { val bot = CombatBot(location) generateStats(bot,tier,Skills.ATTACK, Skills.STRENGTH, Skills.DEFENCE) equipHighest(bot, MELEE_HELMS, 50) @@ -167,7 +166,7 @@ class CombatBotAssembler { * @return a CombatBot suited for killing dragons. * @author Ceikry */ - fun assembleRangeDragonBot(tier: Tier, location: Location): CombatBot{ + fun assembleRangeDragonBot(tier: Tier, location: Location): CombatBot { val bot = CombatBot(location) bot.fullRestore() generateStats(bot,tier,Skills.RANGE,Skills.DEFENCE) @@ -391,7 +390,7 @@ class CombatBotAssembler { * @param skills the skills that we should generate. * @author Ceikry and Eli */ - fun generateStats(bot: AIPlayer, tier: CombatBotAssembler.Tier, vararg skills: Int) { + fun generateStats(bot: AIPlayer, tier: Tier, vararg skills: Int) { var totalXPAdd = 0.0 var skillAmt = 0.0 val variance = 0.5 @@ -415,12 +414,15 @@ class CombatBotAssembler { skillAmt++ } when (tier){ - Tier.PURE -> bot.skills.setStaticLevel(Skills.DEFENCE,10) - Tier.PURE -> bot.skills.setStaticLevel(Skills.STRENGTH,99) - Tier.PURE -> bot.skills.setStaticLevel(Skills.ATTACK,90) - Tier.PURE -> bot.skills.setStaticLevel(Skills.PRAYER,43) - Tier.PURE -> bot.skills.setStaticLevel(Skills.RANGE,1) - Tier.PURE -> bot.skills.setStaticLevel(Skills.MAGIC,1) + Tier.PURE -> { + bot.skills.setStaticLevel(Skills.DEFENCE,10) + bot.skills.setStaticLevel(Skills.STRENGTH,99) + bot.skills.setStaticLevel(Skills.ATTACK,90) + bot.skills.setStaticLevel(Skills.PRAYER,43) + bot.skills.setStaticLevel(Skills.RANGE,1) + bot.skills.setStaticLevel(Skills.MAGIC,1) + } + else -> {} } bot.skills.addExperience(Skills.HITPOINTS, (totalXPAdd / skillAmt) * 0.2) @@ -528,4 +530,4 @@ class CombatBotAssembler { val RING_ARCH = arrayOf(6733) val RICH_MELEE_HELMS = arrayOf(2587,2595,2605,2613,2619,2627,2657,2665,2673,3486,1149,10828,4716,4724,4753) -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/bots/GeneralBotCreator.kt b/Server/src/main/core/game/bots/GeneralBotCreator.kt new file mode 100644 index 000000000..7e3cb5478 --- /dev/null +++ b/Server/src/main/core/game/bots/GeneralBotCreator.kt @@ -0,0 +1,129 @@ +package core.game.bots + +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.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 + constructor(botScript: Script, loc: Location?) { + botScript.bot = AIPBuilder.create(loc) + GameWorld.Pulser.submit(BotScriptPulse(botScript)) + } + + constructor(botScript: Script, bot: AIPlayer?) { + botScript.bot = bot + GameWorld.Pulser.submit(BotScriptPulse(botScript).also { AIRepository.PulseRepository[it.botScript.bot.username.lowercase()] = it }) + } + + constructor(botScript: Script, player: Player, isPlayer: Boolean){ + botScript.bot = player + val pulse = BotScriptPulse(botScript,isPlayer) + GameWorld.Pulser.submit(pulse) + player.setAttribute("/save:not_on_highscores",true) + player.setAttribute("botting:script",pulse) + } + + 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.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) + ticks = 0 + SystemLogger.log("Submitting transition pulse from ticks") + GameWorld.Pulser.submit(TransitionPulse(botScript)) + return true + }*/ + 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(10) + if(idleRoll == 2 && botScript !is Idler){ + randomDelay += RandomFunction.random(20,50) + return false + } + botScript.tick() + } + return false + } + + override fun stop() { + ticks = Integer.MAX_VALUE - 20 //Sets the ticks as high as they can go (safely) and then runs pulse again + pulse() //to trigger the transition pulse to be submitted. + super.stop() + if (Server.running) AIRepository.PulseRepository.remove(this.botScript.bot.username.lowercase()) + } + } + + inner class TransitionPulse(val script: Script) : Pulse(RandomFunction.random(60,200)){ + override fun pulse(): Boolean { + // This does not get called and should be removed + GameWorld.Pulser.submit(BotScriptPulse(script.newInstance()).also { AIRepository.PulseRepository[it.botScript.bot.username.lowercase()] = it }) + return true + } + } +} diff --git a/Server/src/main/core/game/bots/PlayerCompatible.kt b/Server/src/main/core/game/bots/PlayerCompatible.kt new file mode 100644 index 000000000..870efe574 --- /dev/null +++ b/Server/src/main/core/game/bots/PlayerCompatible.kt @@ -0,0 +1,3 @@ +package core.game.bots + +annotation class PlayerCompatible diff --git a/Server/src/main/core/game/bots/PlayerScripts.kt b/Server/src/main/core/game/bots/PlayerScripts.kt new file mode 100644 index 000000000..f4e018502 --- /dev/null +++ b/Server/src/main/core/game/bots/PlayerScripts.kt @@ -0,0 +1,6 @@ +package core.game.bots + +object PlayerScripts { + class PlayerScript(val identifier: String, val description: Array, val name: String, val clazz: Class<*>) + val identifierMap = HashMap() +} \ No newline at end of file diff --git a/Server/src/main/java/rs09/game/ai/pvmbots/PvMBots.java b/Server/src/main/core/game/bots/PvMBots.java similarity index 92% rename from Server/src/main/java/rs09/game/ai/pvmbots/PvMBots.java rename to Server/src/main/core/game/bots/PvMBots.java index a63635bc8..6aef6e01e 100644 --- a/Server/src/main/java/rs09/game/ai/pvmbots/PvMBots.java +++ b/Server/src/main/core/game/bots/PvMBots.java @@ -1,17 +1,16 @@ -package rs09.game.ai.pvmbots; +package core.game.bots; import java.util.ArrayList; import java.util.List; -import core.game.content.consumable.Consumable; -import core.game.content.consumable.Consumables; -import core.game.content.consumable.Food; -import core.game.content.consumable.effects.HealingEffect; +import core.game.consumable.Consumable; +import content.data.consumables.Consumables; +import core.game.consumable.Food; +import content.data.consumables.effects.HealingEffect; 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 rs09.game.ai.AIPlayer; import core.game.node.entity.player.link.prayer.PrayerType; import core.game.node.item.Item; import core.game.world.map.Location; @@ -134,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/PvMBotsBuilder.kt b/Server/src/main/core/game/bots/PvMBotsBuilder.kt new file mode 100644 index 000000000..bfe79d674 --- /dev/null +++ b/Server/src/main/core/game/bots/PvMBotsBuilder.kt @@ -0,0 +1,30 @@ +package core.game.bots + +import core.game.world.map.Location +import content.minigame.pestcontrol.bots.PestControlTestBot +import content.minigame.pestcontrol.bots.PestControlTestBot2 + +// import sun.util.resources.CalendarData; +class PvMBotsBuilder { + + companion object { + var botsSpawned = 0 + + fun create(l: Location?): PvMBots { + botsSpawned++ + return PvMBots(l) + } + + @JvmStatic + fun createPestControlTestBot2(l: Location?): PestControlTestBot2 { + botsSpawned++ + return PestControlTestBot2(l!!) + } + @JvmStatic + fun createPestControlTestBot(l: Location?): PestControlTestBot { + botsSpawned++ + return PestControlTestBot(l!!) + } + + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/bots/Script.java b/Server/src/main/core/game/bots/Script.java new file mode 100644 index 000000000..9c5da330a --- /dev/null +++ b/Server/src/main/core/game/bots/Script.java @@ -0,0 +1,64 @@ +package core.game.bots; + +import content.data.Quests; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public abstract class Script { + + public ScriptAPI scriptAPI; + 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) + { + //bot.init(); + 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); + } + bot.getInventory().clear(); + for (Item i : inventory) { + bot.getInventory().add(i); + } + } + } + + @Override + public String toString() { + return bot.getName() + " is a " + this.getClass().getSimpleName() + " at location " + bot.getLocation().toString() + " Current pulse: " + bot.getPulseManager().getCurrent(); + } + + public abstract void tick(); + + public void setLevel(int skill, int level) { + bot.getSkills().setLevel(skill, level); + bot.getSkills().setStaticLevel(skill, level); + bot.getSkills().updateCombatLevel(); + bot.getAppearance().sync(); + } + + // This does not get called and all implementations should be removed + public abstract Script newInstance(); +} diff --git a/Server/src/main/core/game/bots/ScriptAPI.kt b/Server/src/main/core/game/bots/ScriptAPI.kt new file mode 100644 index 000000000..c2ac69649 --- /dev/null +++ b/Server/src/main/core/game/bots/ScriptAPI.kt @@ -0,0 +1,882 @@ +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 +import content.data.consumables.Consumables +import core.game.consumable.Food +import content.data.consumables.effects.HealingEffect +import core.game.interaction.DestinationFlag +import core.game.interaction.MovementPulse +import core.game.interaction.Option +import core.game.node.Node +import core.game.node.scenery.Scenery +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.GroundItem +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.map.RegionManager +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.* +import core.tools.RandomFunction +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.rs09.consts.Items +import core.ServerConstants.Companion.SERVER_GE_NAME +import core.game.ge.GrandExchange +import core.game.ge.GrandExchangeOffer +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners +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) + val ANIMATIONUP = Animation(8939) + val GRAPHICSDOWN = Graphics(1577) + val ANIMATIONDOWN = Animation(8941) + + /** + * Gets the distance between two nodes + * @param n1 the first node + * @param n2 the second node + * @author Ceikry + */ + fun distance(n1: Node, n2: Node): Double { + return sqrt((n1.location.x - n2.location.x.toDouble()).pow(2.0) + (n2.location.y - n1.location.y.toDouble()).pow(2.0)) + } + + fun interact(bot: Player, node: Node?, option: String){ + + if(node == null) return + + val type = when(node){ + is Scenery -> IntType.SCENERY + is NPC -> IntType.NPC + is Item -> IntType.ITEM + else -> null + } ?: return + val opt: Option? = node.interaction.options.filter {it != null && it.name.equals(option, true) }.firstOrNull() + + if(opt == null){ + 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) + } + + 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 + } + 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(EntityFlag.Chat, ChatMessage(bot, message, 0, 0)) + } + + /** + * Gets the nearest node with a name contained in the list of acceptable names + * @param acceptedNames the list of accepted npc/object names + * @return the nearest node with a matching name or null + * @author Ceikry + */ + fun getNearestNodeFromList(acceptedNames: List, isObject: Boolean): Node? { + 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) + } + + /** + * Gets the nearest node with matching id. + * @param id the id to look for + * @param object whether or not the node we are looking for is an object. + * @return the closest node with matching id or null. + * @author Ceikry + */ + 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)) + } + + /** + * Gets the nearest node with a matching name. + * @param name the name to look for. + * @param object whether or not the node we are looking for is an object. + * @return the nearest matching node or null. + * @author Ceikry + */ + 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 { + 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. + * @return the nearest GroundItem with a matching id or null + * @author Ceikry + */ + private fun getNearestGroundItem(id: Int): GroundItem? { + var distance = 11.0 + var closest: GroundItem? = null + if(AIRepository.getItems(bot) != null) { + for (item in AIRepository.getItems(bot)!!.filter { it: GroundItem -> it.distance(bot.location) < 10 }) { + if (item.id == id) { + //distance = item.distance(bot.location) + closest = item + } + } + if (!GroundItemManager.getItems().contains(closest)){ + AIRepository.getItems(bot)?.remove(closest) + return null + } + } else { + val items: ArrayList? = bot.getAttribute("botting:drops",null) + if(items != null){ + for(item in items.filter { it.distance(bot.location) < 10 }){ + if(item.id == id) return item.also { items.remove(item); bot.setAttribute("botting:drops",items) } + } + } + } + return closest + } + + /** + * Takes the nearest ground item with a matching id if it exists. + * @param id the id to look for + * @author Ceikry + */ + fun takeNearestGroundItem(id: Int) : Boolean{ + val item = getNearestGroundItem(id) + if(item != null){ + item.interaction?.handle(bot, item.interaction[2]) + return true + } + else return false + } + + /** + * Gets the nearest GameObject to loc with matching objectId + * @param loc the location we are checking around + * @param objectId the id of the object we are looking for + * @return the nearest matching object or null. + * @author Ceikry + */ + fun getNearestGameObject(loc: Location, objectId: Int): Scenery? { + var nearestObject: Scenery? = null + val minDistance = Double.MAX_VALUE + for (o in RegionManager.forId(loc.regionId).planes[0].objects) { + for (obj in o) { + if (obj != null) { + if (distance(loc, obj) < minDistance && obj.id == objectId) { + nearestObject = obj + } + } + } + } + return nearestObject + } + + /** + * Gets a list of NPCs that are attackable around the entity. + * @param entity the entity to search around. + * @param radius the radius around entity to search in. + * @param name the name of the NPC to look for. + * @return an Array of the nearest NPCs with matching name, or null. + * @author Ceikry + */ + private fun findTargets(entity: Entity, radius: Int, name: String? = null): List? { + val targets: MutableList = ArrayList() + val localNPCs: Array = RegionManager.getLocalNpcs(entity, radius).toTypedArray() + var length = localNPCs.size + if (length > 5) { + length = 5 + } + for (i in 0 until length) { + val npc = localNPCs[i] as NPC + run { if (checkValidTargets(npc, name)) targets.add(npc) } + } + return if (targets.size == 0) null else targets + } + + /** + * Checks if the given target is a valid target for a combat bot. + * @param target the NPC that we are checking + * @param name the expected name of the NPC. + * @return true if the target is valid, false if not. + * @author Ceikry + */ + private fun checkValidTargets(target: NPC, name: String?): Boolean { + if (!target.isActive) { + return false + } + if (!target.properties.isMultiZone && target.inCombat()) { + return false + } + if (name != null){ + if(target.name != name) + return false + } + return target.definition.hasAction("attack") + } + + /** + * Attacks npcs in the given radius of the bot. + * @param bot the bot that is attacking + * @param radius the radius to attack in + * @return true if successfully attacking an NPC within that radius, false if not. + * @author Ceikry + */ + fun attackNpcsInRadius(bot: Player, radius: Int): Boolean { + if (bot.inCombat()) return true + var creatures: List? = findTargets(bot, radius) ?: return false + bot.attack(creatures!![RandomFunction.getRandom(creatures.size - 1)]) + return if (creatures.isNotEmpty()) { + true + } else { + creatures = findTargets(bot, radius) + if (!creatures!!.isEmpty()) { + bot.attack(creatures[RandomFunction.getRandom(creatures.size - 1)]) + return true + } + false + } + } + + /** + * Function to iteratively walk a bot to a faraway location. Limited by doors and large obstacles like mountains. + * @param loc the location to walk to. + * @author Ceikry + */ + fun walkTo(loc: Location){ + if(!bot.walkingQueue.isMoving) { + walkToIterator(loc) + } + } + + /** + * Function to iteratively walk an array of Locations to get over complex obstacles. + * @param steps the array of locations to walk to. + * @author dginovker + */ + fun walkArray(steps: Array){ + bot.pulseManager.run(object : Pulse(){ + var stepIndex = 0 + override fun pulse(): Boolean { + // If the stepIndex is out of bounds, we're done + if(stepIndex >= steps.size) return true + // If we're near the last step, we're done + if (bot.location.withinDistance(steps[steps.size - 1], 2)) { + return true + } + // If we're not near the next step, walk to it + if (!bot.location.withinDistance(steps[stepIndex], 5)) { + walkTo(steps[stepIndex]) + return false + } + // If we're near the next step, increment the step index + stepIndex++ + + return false + } + }) + } + + /** + * @param loc the location to walk to. + * @param radius tiles around the location the bot could walk to. + * @author Kermit + */ + fun randomWalkTo(loc: Location, radius: Int) { + if(!bot.walkingQueue.isMoving) { + 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. + * @param loc the location to find a path to. + * @author Ceikry + */ + private fun walkToIterator(loc: Location){ + var diffX = loc.x - bot.location.x + var diffY = loc.y - bot.location.y + + 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 } }) + } + + /** + * Attacks npcs in the given radius of the bot. + * @param bot the bot that is attacking + * @param radius the radius to attack in + * @param name the name of the NPC to target. + * @return true if successfully attacking an NPC within that radius, false if not. + * @author Ceikry + */ + fun attackNpcInRadius(bot: Player, name: String, radius: Int): Boolean { + if (bot.inCombat()) return true + var creatures: List? = findTargets(bot, radius, name) ?: return false + bot.attack(creatures!![RandomFunction.getRandom(creatures.size - 1)]) + return if (creatures.isNotEmpty()) { + true + } else { + creatures = findTargets(bot, radius, name) + if (!creatures!!.isEmpty()) { + bot.attack(creatures.random()) + return true + } + false + } + } + + /** + * Extension function to find the distance between a location and a ground item. + * @param loc the location to check the distance from. + * @return a Double representing the distance. + * @author Ceikry + */ + fun GroundItem.distance(loc: Location): Double{ + return location.getDistance(loc) + } + + /** + * A function for teleporting the bot to the GE + * @author Ceikry + */ + fun teleportToGE() : Boolean{ + if (bot.isTeleBlocked) { + return false + } + bot.lock() + bot.visualize(ANIMATIONUP, GRAPHICSUP) + bot.impactHandler.disabledTicks = 4 + val location = Location.create(3165, 3482, 0) + bot.pulseManager.run(object : Pulse(4, bot) { + override fun pulse(): Boolean { + bot.unlock() + bot.properties.teleportLocation = location + bot.pulseManager.clear() + bot.animator.reset() + return true + } + }) + return true + } + + /** + * A function for selling a given item on the GE. + * @param id the ID of the item to sell on the GE. Pulls from the bot's bank. + * @author Ceikry + * @author Angle + */ + fun sellOnGE(id: Int){ + class toCounterPulse : MovementPulse(bot, Location.create(3165, 3487, 0)){ + override fun pulse(): Boolean { + var actualId = id + val itemAmt = bot.bank.getAmount(id) + if (ItemDefinition.forId(id).noteId == id){ + actualId = Item(id).noteChange + } + val canSell = GrandExchange.addBotOffer(actualId, itemAmt) + if (canSell && saleIsBigNews(actualId, itemAmt)) { + 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() + return true + } + } + bot.pulseManager.run(toCounterPulse()) + } + + /** + * Function to sell all items in a bot's bank on the Grand Exchange, if they are tradable. + * @author Ceikry + */ + fun sellAllOnGe(){ + class toCounterPulseAll : MovementPulse(bot, Location.create(3165, 3487, 0)){ + override fun pulse(): Boolean { + for(item in bot.bank.toArray()) { + item ?: continue + if (item.id == Items.LOBSTER_379) continue + if (item.id == Items.SWORDFISH_373) continue + if (item.id == Items.SHARK_385) continue + if(!item.definition.isTradeable) {continue} + val itemAmt = item.amount + var actualId = item.id + if (ItemDefinition.forId(actualId).noteId == actualId){ + actualId = Item(actualId).noteChange + } + val canSell = GrandExchange.addBotOffer(actualId, itemAmt) + if (canSell && saleIsBigNews(actualId, itemAmt)) { + Repository.sendGrandExchangeNews( + SERVER_GE_NAME + " just offered " + itemAmt + " " + + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE." + ) + } + bot.bank.remove(item) + bot.bank.refresh() + } + return true + } + } + bot.pulseManager.run(toCounterPulseAll()) + } + + /** + * Function to sell all items in a bot's bank on the Grand Exchange, if they are tradable. + * @author Ceikry & Kermit + */ + fun sellAllOnGeAdv(){ + val ge: Scenery? = getNearestNode("Desk", true) as Scenery? + class toCounterPulseAll : MovementPulse(bot, ge, DestinationFlag.OBJECT) { + override fun pulse(): Boolean { + for(item in bot.bank.toArray()) { + item ?: continue + if(!item.definition.isTradeable) {continue} + val itemAmt = item.amount + var actualId = item.id + if (ItemDefinition.forId(actualId).noteId == actualId){ + actualId = Item(actualId).noteChange + } + val canSell = GrandExchange.addBotOffer(actualId, itemAmt) + if (canSell && saleIsBigNews(actualId, itemAmt)) { + when (actualId){ + 1511 -> continue + 1513 -> continue + 1515 -> continue + 1517 -> continue + 1519 -> continue + 1521 -> continue + else -> Repository.sendGrandExchangeNews( + SERVER_GE_NAME + " just offered " + itemAmt + " " + + ItemDefinition.forId(actualId).name.lowercase() + " on the GE." + ) + } + } + bot.bank.remove(item) + bot.bank.refresh() + } + return true + } + } + 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()) + } + } + + /** + * Function to determine whether or not to bother everyone on the server + * with the big news that a bot is selling something to the GE, based on item value + * @param itemID + * @param amount + * @author Gexja + */ + fun saleIsBigNews(itemID: Int, amount: Int): Boolean { + return ItemDefinition.forId(itemID).getAlchemyValue(true) * amount >= (GameWorld.settings?.ge_announcement_limit + ?: 500) + } + + /** + * Function to teleport a bot to the given location. + * @param loc the location to teleport to + * @author Ceikry + */ + fun teleport(loc: Location) : Boolean { + if (bot.isTeleBlocked) { + return false + } + bot.lock() + bot.visualize(ANIMATIONUP, GRAPHICSUP) + bot.impactHandler.disabledTicks = 4 + val location = loc + GameWorld.Pulser.submit(object : Pulse(4, bot) { + override fun pulse(): Boolean { + bot.unlock() + bot.properties.teleportLocation = location + bot.pulseManager.clear() + bot.animator.reset() + return true + } + }) + return true + } + + /** + * Takes the given item out of the bot's inventory and places it into the bank. Banks all items with matching ID. + * @param item the ID of the item to bank + * @author Ceikry + */ + fun bankItem(item: Int){ + class BankingPulse() : Pulse(20){ + override fun pulse(): Boolean { + val logs = bot.inventory.getAmount(item) + bot.inventory.remove(Item(item, logs)) + bot.bank.add(Item(item, logs)) + return true + } + } + bot.pulseManager.run(BankingPulse()) + } + + /** + * Takes every item out of the bots inventory and places it into the bank. + * @param none + * @author cfunnyman joe + */ + fun bankAll(onComplete: (() -> Unit)? = null){ + class BankingPulse() : Pulse(20){ + override fun pulse(): Boolean { + for(item in bot.inventory.toArray()){ + if(item != null) { + var itemAmount = bot.inventory.getAmount(item) + + if(bot.inventory.remove(item)) { + bot.bank.add(item) + } + } + } + if(onComplete != null) { + onComplete?.invoke() + } + return true + } + } + bot.pulseManager.run(BankingPulse()) + } + + /** + * Function that makes the bot eat the food item in their inventory if their HP is below 2/3. + * @author Ceikry + * @param foodId the ID of the food item to eat + */ + fun eat(foodId: Int) { + val foodItem = Item(foodId) + if (bot.skills.getStaticLevel(Skills.HITPOINTS) * RandomFunction.random(0.5, 0.75) >= bot.skills.lifepoints && bot.inventory.containsItem(foodItem)) { + bot.lock(3) + //this.animate(new Animation(829)); + val food = bot.inventory.getItem(foodItem) + var consumable: Consumable? = Consumables.getConsumableById(foodId)?.consumable + if (consumable == null) { + consumable = Food(intArrayOf(food.id), HealingEffect(1)) + } + consumable.consume(food, bot) + bot.properties.combatPulse.delayNextAttack(3) + } + } + + /** + * Same as the eat function, except that it forces the bot to eat regardless of HP. + * @author Ceikry + * @param foodId the ID of the food item to eat. + */ + fun forceEat(foodId: Int) { + val foodItem = Item(foodId) + if (bot.inventory.containsItem(foodItem)) { + bot.lock(3) + //this.animate(new Animation(829)); + val food = bot.inventory.getItem(foodItem) + var consumable: Consumable? = Consumables.getConsumableById(foodId)?.consumable + if (consumable == null) { + consumable = Food(intArrayOf(foodId), HealingEffect(1)) + } + consumable.consume(food, bot) + bot.properties.combatPulse.delayNextAttack(3) + } + } + + /** + * Function for buying items off the GE. + * @param itemID the id of the item to buy off the GE. + * @param amount the amount to buy. + * @return true if item was successfully bought, false if not. + */ + fun buyFromGE(bot: Player, itemID: Int, amount: Int){ + GlobalScope.launch { + val offer = GrandExchangeOffer() + offer.itemID = itemID + offer.sell = false + offer.offeredValue = checkPriceOverrides(itemID) ?: ItemDefinition.forId(itemID).value + offer.amount = amount + offer.player = bot + //GrandExchange.dispatch(bot, offer) + AIRepository.addOffer(bot, offer) + var bought: Boolean = false + val latch = CountDownLatch(1) + bot.pulseManager.run(object : Pulse(5) { + override fun pulse(): Boolean { + bought = offer.completedAmount == offer.amount + latch.countDown() + return true + } + }) + latch.await() + if(bought){ + bot.bank.add(Item(offer.itemID, offer.completedAmount)) + bot.bank.refresh() + } + } + } + + /** + * Method to allow a bot to withdraw items from its bank. + * @param itemID the item to withdraw. + * @param amount the amount to withdraw. + * @author Ceikry + */ + fun withdraw(itemID: Int, amount: Int){ + var item: Item? = null + if(bot.bank.containsItem(Item(itemID, amount))){ + item = Item(itemID, amount) + } else { + item = Item(itemID, bot.bank.getAmount(itemID)) + } + if(item.amount == 0) return + if(!bot.inventory.hasSpaceFor(item)){ + item.amount = bot.inventory.getMaximumAdd(item) + } + bot.bank.remove(item) + bot.inventory.add(item) + } + + /** + * Method to equip list of items and set stats + * Useful for starting a bot up + * @param items the list of items to equip + * @author dginovker + */ + fun equipAndSetStats(items: List?){ + if (items == null) return + for(item in items){ + equipAndSetStats(item) + } + } + + /** + * Method to equip a single item and set stats + * Useful for starting a bot up + * @param item the item to equip + * @author dginovker + */ + fun equipAndSetStats(item: Item){ + val configs = item.definition.handlers + val slot = configs["equipment_slot"] ?: return + bot.equipment.add(item, slot as Int, + false,false) + val reqs = configs["requirements"] + if(reqs != null) + for(req in configs["requirements"] as HashMap) + bot.skills.setStaticLevel(req.key, req.value) + bot.skills.updateCombatLevel() + } + + /** + * Method to load appearance and equipment from JSON + * Useful for starting a bot up + * @param json the JSON object to load from (dumped via ::dumpappearance) + * @author dginovker + */ + fun loadAppearanceAndEquipment(json: JSONObject?) { + if (json == null) return + bot.equipment.clear() + bot.appearance.parse(json["appearance"] as JSONObject) + val equipment = json["equipment"] as JSONArray + bot.equipment.parse(equipment) + bot.appearance.sync() + for (i in 0 until bot.equipment.capacity()) { + val item = bot.equipment.get(i) + if (item != null) { + equipAndSetStats(item) + } + } + // Set all combat stats to a function of the highest combat stat(otherwise you end up with lopsided stats) + val highestCombatSkill = bot.skills.getStaticLevel(bot.skills.highestCombatSkillId) + for (i in 0 until 7) { + bot.skills.setStaticLevel(i, max((highestCombatSkill * 0.75).toInt(), bot.skills.getStaticLevel(i))) + } + bot.skills.updateCombatLevel() + } + + fun getOverlay(): BottingOverlay { + return BottingOverlay(bot) + } + + /** + * Function to check for price overrides. + * @param id the id to check for overrides for. + * @author Ceikry + */ + fun checkPriceOverrides(id: Int): Int?{ + return when(id){ + else -> itemDefinition(id).getConfiguration(ItemConfigParser.GE_PRICE) + } + } + + class BottingOverlay(val player: Player){ + fun init(){ + player.interfaceManager.openOverlay(Component(195)) + player.packetDispatch.sendInterfaceConfig(195,5,true) + } + fun setTitle(title: String){ + player.packetDispatch.sendString(colorize("%B$title"),195,7) + } + fun setTaskLabel(label: String){ + player.packetDispatch.sendString(colorize("%B$label"),195,8) + } + fun setAmount(amount: Int){ + player.packetDispatch.sendString(colorize("%B$amount"),195,9) + } + } +} diff --git a/Server/src/main/core/game/bots/ScriptDescription.kt b/Server/src/main/core/game/bots/ScriptDescription.kt new file mode 100644 index 000000000..95ded9ca1 --- /dev/null +++ b/Server/src/main/core/game/bots/ScriptDescription.kt @@ -0,0 +1,3 @@ +package core.game.bots + +annotation class ScriptDescription(vararg val value: String) diff --git a/Server/src/main/core/game/bots/ScriptIdentifier.kt b/Server/src/main/core/game/bots/ScriptIdentifier.kt new file mode 100644 index 000000000..a2a8cb56b --- /dev/null +++ b/Server/src/main/core/game/bots/ScriptIdentifier.kt @@ -0,0 +1,3 @@ +package core.game.bots + +annotation class ScriptIdentifier(val value: String) diff --git a/Server/src/main/core/game/bots/ScriptName.kt b/Server/src/main/core/game/bots/ScriptName.kt new file mode 100644 index 000000000..cf99dffc5 --- /dev/null +++ b/Server/src/main/core/game/bots/ScriptName.kt @@ -0,0 +1,3 @@ +package core.game.bots + +annotation class ScriptName(val value: String) diff --git a/Server/src/main/core/game/bots/SkillingBotAssembler.kt b/Server/src/main/core/game/bots/SkillingBotAssembler.kt new file mode 100644 index 000000000..decb90a85 --- /dev/null +++ b/Server/src/main/core/game/bots/SkillingBotAssembler.kt @@ -0,0 +1,200 @@ +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 { + return assembleBot(AIPlayer(loc),type) + } + + fun assembleBot(bot: AIPlayer, type: Wealth): AIPlayer { + return when(type){ + Wealth.POOR -> equipSet(bot,POORSETS.random()) + Wealth.AVERAGE -> equipSet(bot,AVGSETS.random()) + Wealth.RICH -> equipSet(bot,RICHSETS.random()) + } + } + + fun equipSet(bot: AIPlayer, set: List): AIPlayer { + for(i in set){ + val item = Item(i) + val configs = item.definition.handlers + val slot = configs["equipment_slot"] ?: continue + 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) + bot.skills.setStaticLevel(req.key, req.value) + } + bot.skills.updateCombatLevel() + return bot + } + + enum class Wealth { + POOR, + AVERAGE, + RICH + } + + 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/java/core/game/component/CloseEvent.java b/Server/src/main/core/game/component/CloseEvent.java similarity index 100% rename from Server/src/main/java/core/game/component/CloseEvent.java rename to Server/src/main/core/game/component/CloseEvent.java diff --git a/Server/src/main/java/core/game/component/Component.java b/Server/src/main/core/game/component/Component.java similarity index 91% rename from Server/src/main/java/core/game/component/Component.java rename to Server/src/main/core/game/component/Component.java index 9ce427f4c..412ca8df8 100644 --- a/Server/src/main/java/core/game/component/Component.java +++ b/Server/src/main/core/game/component/Component.java @@ -5,7 +5,7 @@ import core.game.node.entity.player.link.InterfaceManager; import core.net.packet.PacketRepository; import core.net.packet.context.InterfaceContext; import core.net.packet.out.Interface; -import rs09.game.interaction.InterfaceListeners; +import core.game.interaction.InterfaceListeners; /** * Represents a component. @@ -84,11 +84,7 @@ public class Component { * @return {@code True} if the component can be closed. */ public boolean close(Player player) { - InterfaceListeners.runClose(player,this); - if (closeEvent != null && !closeEvent.close(player, this)) { - return false; - } - return true; + return (closeEvent == null || closeEvent.close(player, this)) && InterfaceListeners.runClose(player, this); } /** @@ -129,14 +125,11 @@ public class Component { * @param c The component. */ public static void setUnclosable(Player p, Component c) { - p.setAttribute("close_c_", false); + p.setAttribute("close_c_", true); c.setCloseEvent(new CloseEvent() { @Override public boolean close(Player player, Component c) { - if (!player.getAttribute("close_c_", false)) { - return false; - } - return true; + return !player.getAttribute("close_c_", false); } }); } diff --git a/Server/src/main/java/core/game/component/ComponentDefinition.java b/Server/src/main/core/game/component/ComponentDefinition.java similarity index 100% rename from Server/src/main/java/core/game/component/ComponentDefinition.java rename to Server/src/main/core/game/component/ComponentDefinition.java diff --git a/Server/src/main/java/core/game/component/ComponentPlugin.java b/Server/src/main/core/game/component/ComponentPlugin.java similarity index 100% rename from Server/src/main/java/core/game/component/ComponentPlugin.java rename to Server/src/main/core/game/component/ComponentPlugin.java diff --git a/Server/src/main/java/core/game/component/InterfaceType.java b/Server/src/main/core/game/component/InterfaceType.java similarity index 96% rename from Server/src/main/java/core/game/component/InterfaceType.java rename to Server/src/main/core/game/component/InterfaceType.java index 937012ec6..59dd77e2a 100644 --- a/Server/src/main/java/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/java/core/game/content/consumable/BarbarianMix.java b/Server/src/main/core/game/consumable/BarbarianMix.java similarity index 91% rename from Server/src/main/java/core/game/content/consumable/BarbarianMix.java rename to Server/src/main/core/game/consumable/BarbarianMix.java index 2ba4c4556..6cfe96c43 100644 --- a/Server/src/main/java/core/game/content/consumable/BarbarianMix.java +++ b/Server/src/main/core/game/consumable/BarbarianMix.java @@ -1,4 +1,4 @@ -package core.game.content.consumable; +package core.game.consumable; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/content/consumable/Cake.java b/Server/src/main/core/game/consumable/Cake.java similarity index 89% rename from Server/src/main/java/core/game/content/consumable/Cake.java rename to Server/src/main/core/game/consumable/Cake.java index 249c5bd41..bd7286240 100644 --- a/Server/src/main/java/core/game/content/consumable/Cake.java +++ b/Server/src/main/core/game/consumable/Cake.java @@ -1,5 +1,6 @@ -package core.game.content.consumable; +package core.game.consumable; +import content.data.consumables.Consumables; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -22,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/java/core/game/content/consumable/Consumable.java b/Server/src/main/core/game/consumable/Consumable.java similarity index 81% rename from Server/src/main/java/core/game/content/consumable/Consumable.java rename to Server/src/main/core/game/consumable/Consumable.java index a3e82ff3f..f30084740 100644 --- a/Server/src/main/java/core/game/content/consumable/Consumable.java +++ b/Server/src/main/core/game/consumable/Consumable.java @@ -1,7 +1,8 @@ -package core.game.content.consumable; +package core.game.consumable; -import api.Container; -import api.ContentAPI; +import content.data.consumables.Consumables; +import core.api.Container; +import static core.api.ContentAPIKt.*; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; @@ -10,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. @@ -49,15 +50,16 @@ public abstract class Consumable implements Plugin { executeConsumptionActions(player); final int nextItemId = getNextItemId(item.getId()); - if(item.getAmount() > 1){ - ContentAPI.removeItem(player, item.getId(), Container.INVENTORY); - } else ContentAPI.removeItem(player, item, Container.INVENTORY); - - if (nextItemId != -1) { - ContentAPI.addItem(player, nextItemId, 1); + // 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); } @@ -99,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/java/core/game/content/consumable/ConsumableEffect.java b/Server/src/main/core/game/consumable/ConsumableEffect.java similarity index 83% rename from Server/src/main/java/core/game/content/consumable/ConsumableEffect.java rename to Server/src/main/core/game/consumable/ConsumableEffect.java index e7f975463..6b827abd3 100644 --- a/Server/src/main/java/core/game/content/consumable/ConsumableEffect.java +++ b/Server/src/main/core/game/consumable/ConsumableEffect.java @@ -1,4 +1,4 @@ -package core.game.content.consumable; +package core.game.consumable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/consumable/Drink.java b/Server/src/main/core/game/consumable/Drink.java similarity index 84% rename from Server/src/main/java/core/game/content/consumable/Drink.java rename to Server/src/main/core/game/consumable/Drink.java index 6a72e6459..e5b8611ab 100644 --- a/Server/src/main/java/core/game/content/consumable/Drink.java +++ b/Server/src/main/core/game/consumable/Drink.java @@ -1,13 +1,12 @@ -package core.game.content.consumable; +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/java/core/game/content/consumable/FakeConsumable.java b/Server/src/main/core/game/consumable/FakeConsumable.java similarity index 95% rename from Server/src/main/java/core/game/content/consumable/FakeConsumable.java rename to Server/src/main/core/game/consumable/FakeConsumable.java index 019ff1757..94ff20c6e 100644 --- a/Server/src/main/java/core/game/content/consumable/FakeConsumable.java +++ b/Server/src/main/core/game/consumable/FakeConsumable.java @@ -1,4 +1,4 @@ -package core.game.content.consumable; +package core.game.consumable; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/content/consumable/Food.java b/Server/src/main/core/game/consumable/Food.java similarity index 81% rename from Server/src/main/java/core/game/content/consumable/Food.java rename to Server/src/main/core/game/consumable/Food.java index db0c6cff3..d5b55bb82 100644 --- a/Server/src/main/java/core/game/content/consumable/Food.java +++ b/Server/src/main/core/game/consumable/Food.java @@ -1,14 +1,14 @@ -package core.game.content.consumable; +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/java/core/game/content/consumable/HalfableFood.java b/Server/src/main/core/game/consumable/HalfableFood.java similarity index 96% rename from Server/src/main/java/core/game/content/consumable/HalfableFood.java rename to Server/src/main/core/game/consumable/HalfableFood.java index 46c299c91..89e18f2ff 100644 --- a/Server/src/main/java/core/game/content/consumable/HalfableFood.java +++ b/Server/src/main/core/game/consumable/HalfableFood.java @@ -1,4 +1,4 @@ -package core.game.content.consumable; +package core.game.consumable; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/core/game/consumable/Potion.java b/Server/src/main/core/game/consumable/Potion.java new file mode 100644 index 000000000..89912d9f1 --- /dev/null +++ b/Server/src/main/core/game/consumable/Potion.java @@ -0,0 +1,88 @@ +package core.game.consumable; + +import content.data.consumables.Consumables; +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 { + + private static final int VIAL = 229; + + private static final Audio SOUND = new Audio(2401, 1, 1); + + public Potion(final int[] ids, final ConsumableEffect effect, final String... messages) { + super(ids, effect, new Animation(829), messages); + } + + @Override + public void consume(Item item, Player player) { + executeConsumptionActions(player); + final int nextItemId = getNextItemId(item.getId()); + if (nextItemId != -1) { + player.getInventory().replace(new Item(nextItemId), item.getSlot()); + } + else { + player.getInventory().replace(new Item(VIAL), item.getSlot()); + } + + final int initialLifePoints = player.getSkills().getLifepoints(); + Consumables.getConsumableById(item.getId()).getConsumable().effect.activate(player); + if (messages.length == 0) { + sendDefaultMessages(player, item); + } else { + sendCustomMessages(player, messages); + } + sendHealingMessage(player, initialLifePoints); + } + + @Override + protected void executeConsumptionActions(Player player) { + player.animate(animation); + playAudio(player, Sounds.LIQUID_2401); + } + + @Override + protected void sendDefaultMessages(Player player, Item item) { + int consumedDoses = 1; + int i = 0; + while (ids[i] != item.getId()) { + consumedDoses++; + i++; + } + final int dosesLeft = ids.length - consumedDoses; + player.getPacketDispatch().sendMessage("You drink some of your " + getFormattedName(item) + "."); + // 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 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.]","")); + } + + public int[] getIds() { + return ids; + } + + public ConsumableEffect getEffect() { + return effect; + } +} diff --git a/Server/src/main/core/game/container/Container.java b/Server/src/main/core/game/container/Container.java new file mode 100644 index 000000000..5cecb6905 --- /dev/null +++ b/Server/src/main/core/game/container/Container.java @@ -0,0 +1,1078 @@ +package core.game.container; + +import core.cache.def.impl.ItemDefinition; +import core.game.node.entity.player.Player; +import core.game.node.item.GroundItemManager; +import core.game.node.item.Item; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.rs09.consts.Items; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +import com.google.errorprone.annotations.CheckReturnValue; + + +/** + * Represents a container which contains items. + * + * @author Emperor + */ +public class Container { + + /** + * The item array. A crystalline + */ + private Item[] items; + + /** + * The capacity. + */ + private final int capacity; + + /** + * The current sort type. + */ + private SortType sortType; + + /** + * The current container type. + */ + private final ContainerType type; + + /** + * The current container event. + */ + private ContainerEvent event; + + /** + * The container listeners. + */ + private final List listeners = new ArrayList<>(20); + + /** + * Constructs a new {@code Container} {@code Object}. + * + * @param capacity The capacity. + */ + public Container(int capacity) { + this(capacity, ContainerType.DEFAULT); + } + + /** + * Constructs a new {@code Container.java} {@code Object}. + * + * @param capacity the capacity. + * @param items the items to add. + */ + public Container(int capacity, Item... items) { + this(capacity); + add(items); + } + + /** + * Constructs a new {@code Container} {@code Object}. + * + * @param capacity The capacity. + * @param type The container type. + */ + public Container(int capacity, ContainerType type) { + this(capacity, type, SortType.ID); + } + + /** + * Constructs a new {@code Container} {@code Object}. + * + * @param capacity The capacity. + * @param type The container type. + * @param sortType The sort type. + */ + public Container(int capacity, ContainerType type, SortType sortType) { + this.capacity = capacity; + this.type = type; + this.items = new Item[capacity]; + this.sortType = sortType; + this.event = new ContainerEvent(capacity); + } + + /** + * Registers a container listener. + * + * @param listener The container listener. + * @return This container instance, for chaining. + */ + public Container register(ContainerListener listener) { + listeners.add(listener); + return this; + } + + /** + * Adds the items. + * + * @param items The items to add. + * @return {@code True} if successfully added all items. + */ + public boolean add(Item... items) { + boolean addedAll = true; + for (Item item : items) { + if (item == null) { + continue; + } + if (!add(item, false)) { + addedAll = false; + break; + } + } + update(); + return addedAll; + } + + public void addList(List items){ + items.stream().filter(Objects::nonNull).forEach(this::add); + update(); + } + + /** + * Inserts an item into a specific slot. + * + * @param fromSlot The original slot of the item. + * @param toSlot The slot to insert into. + */ + public void insert(int fromSlot, int toSlot) { + insert(fromSlot, toSlot, true); + } + + /** + * Inserts an item into a specific slot. + * + * @param fromSlot The original slot of the item. + * @param toSlot The slot to insert into. + * @param update If the container packets should be sent. + */ + public void insert(int fromSlot, int toSlot, boolean update) { + Item temp = items[fromSlot]; + if (toSlot > fromSlot) { + for (int i = fromSlot; i < toSlot; i++) { + replace(get(i + 1), i, false); + } + } else if (fromSlot > toSlot) { + for (int i = fromSlot; i > toSlot; i--) { + replace(get(i - 1), i, false); + } + } + replace(temp, toSlot, update); + } + + /** + * Adds an item to this container if full it goes to ground. + * + * @param item the item. + * @param player the player. + * @return {@code True} if added. + */ + public boolean add(final Item item, final Player player) { + if (!add(item, true, -1)) { + GroundItemManager.create(item, player); + return false; + } + return true; + } + + public boolean addIfDoesntHave(final Item item) { + if (containsItem(item)) { + return false; + } else { + return add(item); + + } + } + + /** + * Adds an item to this container. + * + * @param item The item. + * @return {@code True} if the item got added. + */ + public boolean add(Item item) { + return add(item, true, -1); + } + + /** + * Adds an item to this container. + * + * @param item The item to add. + * @param fireListener If we should update. + * @return {@code True} if the item got added. + */ + public boolean add(Item item, boolean fireListener) { + return add(item, fireListener, -1); + } + + /** + * Adds an item to this container. + * + * @param item The item to add. + * @param fireListener If we should update. + * @param preferredSlot The slot to add the item in, when possible. + * @return {@code True} if the item got added. + */ + public boolean add(Item item, boolean fireListener, int preferredSlot) { + item = item.copy(); + int maximum = getMaximumAdd(item); + if (maximum == 0) { + return false; + } + if (preferredSlot > -1 && items[preferredSlot] != null) { + preferredSlot = -1; + } + if (item.getAmount() > maximum) { + item.setAmount(maximum); + } + if (type != ContainerType.NEVER_STACK && (item.getDefinition().isStackable() || type == ContainerType.ALWAYS_STACK || type == ContainerType.SHOP)) { + boolean hashBased = sortType == SortType.HASH; + for (int i = 0; i < items.length; i++) { + if (items[i] != null) { + if ((hashBased && items[i].getIdHash() == item.getIdHash()) || (!hashBased && items[i].getId() == item.getId())) { + int totalCount = item.getAmount() + items[i].getAmount(); + items[i] = new Item(items[i].getId(), totalCount, item.getCharge()); + items[i].setIndex(i); + event.flag(i, items[i]); + if (fireListener) { + update(); + } + return true; + } + } + } + int slot = preferredSlot > -1 ? preferredSlot : freeSlot(); + if (slot == -1) { + return false; + } + items[slot] = item; + item.setIndex(slot); + event.flag(slot, item); + if (fireListener) { + update(); + } + return true; + } + int slots = freeSlots(); + if (slots >= item.getAmount()) { + for (int i = 0; i < item.getAmount(); i++) { + int slot = i == 0 && preferredSlot > -1 ? preferredSlot : freeSlot(); + items[slot] = new Item(item.getId(), 1, item.getCharge()); + items[slot].setIndex(slot); + event.flag(slot, items[slot]); + } + if (fireListener) { + update(); + } + return true; + } + return false; + } + + /** + * Removes a set of items. + * + * @param items The set of items. + * @return {@code True} if all items got successfully removed. + */ + @CheckReturnValue + public boolean remove(Item... items) { + boolean removedAll = true; + for (Item item : items) { + if (!remove(item, false)) { + removedAll = false; + } + } + update(); + return removedAll; + } + + /** + * Removes an item. + * + * @param item The item. + * @return {@code True} if the item got removed, {@code false} if not. + */ + @CheckReturnValue + public boolean remove(Item item) { + return remove(item, true); + } + + /** + * Removes an item. + * + * @param item The item to remove. + * @param fireListener If the fire listener should be "notified". + * @return {@code True} if the item got removed,
{@code false} if not. + */ + @CheckReturnValue + public boolean remove(Item item, boolean fireListener) { + int slot = getSlot(item); + if (slot != -1) { + return remove(item, slot, fireListener); + } + return false; + } + + /** + * Removes an item from this container. + * + * @param item The item. + * @param slot The item slot. + * @param fireListener If the fire listener should be "notified". + * @return {@code True} if the item got removed,
{@code false} if the + * item on the slot was null or the ids didn't match. + */ + @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; + } + if (item.getAmount() < 1) { + return true; + } + if (oldItem.getDefinition().isStackable() || type.equals(ContainerType.ALWAYS_STACK) || type == ContainerType.SHOP) { + if (item.getAmount() >= oldItem.getAmount()) { + items[slot] = null; + event.flagNull(slot); + if (fireListener) { + update(); + } + return true; + } + items[slot] = new Item(item.getId(), oldItem.getAmount() - item.getAmount(), item.getCharge()); + items[slot].setIndex(slot); + event.flag(slot, items[slot]); + if (fireListener) { + update(); + } + return true; + } + items[slot] = null; + event.flagNull(slot); + int removed = 1; + for (int i = removed; i < item.getAmount(); i++) { + slot = getSlot(item); + if (slot != -1) { + items[slot] = null; + event.flagNull(slot); + } else { + break; + } + } + if (fireListener) { + update(); + } + 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. + * + * @param item The item. + * @param slot The slot. + * @return The old item. + */ + public Item replace(Item item, int slot) { + return replace(item, slot, true); + } + + /** + * Replaces the item on the given slot with the argued item. + * + * @param item The item. + * @param slot The slot. + * @param fireListener If the listener should be "notified". + * @return The old item. + */ + public Item replace(Item item, int slot, boolean fireListener) { + if (item != null) { + if (item.getAmount() < 1 && type != ContainerType.SHOP) { + item = null; + } else { + item = item.copy(); + } + } + Item oldItem = items[slot]; + items[slot] = item; + if (item == null) { + event.flagNull(slot); + } else { + item.setIndex(slot); + event.flag(slot, item); + } + if (fireListener) { + update(); + } + return oldItem; + } + + /** + * Updates the container. + */ + public void update() { + if (event.getChangeCount() < 1 && !event.isClear()) { + return; + } + for (ContainerListener listener : listeners) { + listener.update(this, event); + } + event.setClear(false); + event = new ContainerEvent(capacity); + } + + /** + * Updates the container. + */ + public void update(boolean force) { + if (event.getChangeCount() < 1 && !force) { + return; + } + for (ContainerListener listener : listeners) { + listener.update(this, event); + } + event = new ContainerEvent(capacity); + } + + /** + * Refreshes the entire container. + */ + public void refresh() { + for (ContainerListener listener : listeners) { + listener.refresh(this); + } + event = new ContainerEvent(capacity); + } + + public void refresh(ContainerListener listener) { + listener.refresh(this); + event = new ContainerEvent(capacity); + } + + /** + * Gets the item on the given slot. + * + * @param slot The slot. + * @return The id of the item on the slot, or 0 if the item wasn't there. + */ + public int getAsId(int slot) { + if (slot < 0 || slot >= items.length || items[slot] == null) { + return 0; + } + return items[slot].getId(); + } + + + /** + * Gets the item on the given slot. + * + * @param slot The slot. + * @return The item on the slot, or {@code null} if the item wasn't there. + */ + public Item get(int slot) { + if (slot < 0 || slot >= items.length) { + return null; + } + return items[slot]; + } + + /** + * Gets the item on the given slot. + * + * @param slot The slot. + * @return The item on the slot, or a new constructed item with id 0 if the + * item wasn't there. + */ + public Item getNew(int slot) { + Item item = items[slot]; + if (item != null) { + return item; + } + return new Item(0); + } + + /** + * Gets the item id on the given slot. + * + * @param slot The slot. + * @return The id of the item on the slot. + */ + public int getId(int slot) { + if (slot >= items.length) { + return -1; + } + Item item = items[slot]; + if (item != null) { + return item.getId(); + } + return -1; + } + + public void parse(JSONArray itemArray){ + AtomicInteger total = new AtomicInteger(0); + itemArray.forEach(item -> { + JSONObject i = (JSONObject) item; + int slot = Integer.parseInt(i.get("slot").toString()); + int id = Integer.parseInt(i.get("id").toString()); + int amount = Integer.parseInt(i.get("amount").toString()); + int charge = Integer.parseInt(i.get("charge").toString()); + if (id >= ItemDefinition.getDefinitions().size() || id < 0 || slot >= items.length || id == Items.MAGIC_CARPET_5614) { + } else { + Item it = items[slot] = new Item(id,amount,charge); + it.setIndex(slot); + total.set(total.get() + (int)it.getValue()); + } + }); + } + + /** + * Copies the container to this container. + * + * @param c The container to copy. + */ + public void copy(Container c) { + items = new Item[c.items.length]; + for (int i = 0; i < items.length; i++) { + Item it = c.items[i]; + if (it == null) { + continue; + } + items[i] = new Item(it.getId(), it.getAmount(), it.getCharge()); + items[i].setIndex(i); + } + } + + /** + * Formats a container for the SQL database. + * + * @return the string. + */ + public String format() { + String log = ""; + Map map = new HashMap<>(); + Integer old = null; + for (Item item : items) { + if (item != null) { + old = map.get(item.getId()); + map.put(item.getId(), old == null ? item.getAmount() : old + item.getAmount()); + + } + } + for (int i : map.keySet()) { + log += i + "," + map.get(i) + "|"; + } + if (log.length() > 0 && log.charAt(log.length() - 1) == '|') { + log = log.substring(0, log.length() - 1); + } + return log; + } + + /** + * Checks if the container contains an item. + * + * @param item the Item + * @return {@code True} if so. + */ + public boolean containsItem(Item item) { + return contains(item.getId(), item.getAmount()); + } + + /** + * Checks if the containers contains these items. + * + * @param items the items. + * @return {@code True} if so. + */ + public boolean containsItems(Item... items) { + for (Item i : items) { + if (!containsItem(i)) { + return false; + } + } + return true; + } + + /** + * Checks if the container contains an item. + * + * @param itemId The item id. + * @param amount The amount. + * @return {@code True} if so. + */ + public boolean contains(int itemId, int amount) { + int count = 0; + for (Item item : items) { + if (item != null && item.getId() == itemId) { + if ((count += item.getAmount()) >= amount) { + return true; + } + } + } + return false; + } + + /** + * Checks if the containers contains at least ONE item. + * + * @param itemId + * @return + */ + public boolean containsAtLeastOneItem(int itemId) { + for (Item item : items) { + if (item != null && item.getId() == itemId && item.getAmount() > 0) { + return true; + } + } + return false; + } + + /** + * Checks if the containers contains AT LEAST ONE item from a list of items. + * @param itemIds + * @return true if at least one item from list of IDs is in the container + */ + public boolean containsAtLeastOneItem(int[] itemIds) { + for (int id : itemIds) { + if (getAmount(id) > 0) + return true; + } + return false; + } + + public boolean containsAtLeastOneItem(Item... items) { + for (Item item : items) { + if (containsItem(item)) { + return true; + } + } + return false; + } + + /** + * Checks if the container contains all items. + * + * @param itemIds to check + * @return {@code True} if so. + */ + public boolean containsAll(int... itemIds) { + for (int i : itemIds) { + if (!containsAtLeastOneItem(i)) { + return false; + } + } + return true; + } + + /** + * Adds a container to this container. + * + * @param container The container. + */ + public void addAll(Container container) { + add(container.items); + } + + /** + * Checks the maximum amount of this item we can add. + * + * @param item The item. + * @return The maximum amount we can add. + */ + public int getMaximumAdd(Item item) { + if (type != ContainerType.NEVER_STACK) { + if (item.getDefinition().isStackable() || type == ContainerType.ALWAYS_STACK || type == ContainerType.SHOP) { + if (contains(item.getId(), 1)) { + return Integer.MAX_VALUE - getAmount(item); + } + return freeSlots() > 0 ? Integer.MAX_VALUE : 0; + } + } + return freeSlots(); + } + + /** + * Checks if the container has space for the item. + * + * @param item The item to check. + * @return {@code True} if so. + */ + public boolean hasSpaceFor(Item item) { + return item.getAmount() <= getMaximumAdd(item); + } + + public boolean hasSpaceFor(Item... items) { + Container c = new Container(28, ContainerType.DEFAULT); + c.add(items); + return this.hasSpaceFor(c); + } + + /** + * Checks if this container has space to add the other container. + * + * @param c The other container. + * @return {@code True} if so. + */ + public boolean hasSpaceFor(Container c) { + if (c == null) { + return false; + } + Container check = new Container(capacity, type); + check.addAll(this); + for (Item item : c.items) { + if (item != null) { + if (!check.add(item, false)) { + return false; + } + } + } + return true; + } + + /** + * Gets the item slot. + * + * @param item The item. + * @return The slot of the item in this container. + */ + public int getSlot(Item item) { + if (item == null) { + return -1; + } + int id = item.getId(); + for (int i = 0; i < items.length; i++) { + Item it = items[i]; + if (it != null && it.getId() == id) { + return i; + } + } + 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. + * + * @param item the item. + * @return the item. + */ + public Item getItem(Item item) { + return get(getSlot(item)); + } + + public Item get(Item item){ + for(Item i : items){ + if(i == null) continue; + if(item.getId() == i.getId()) return i; + } + 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. + * + * @return The slot, or -1 if there are no available slots. + */ + public int freeSlot() { + for (int i = 0; i < items.length; i++) { + if (items[i] == null) { + return i; + } + } + return -1; + } + + /** + * Gets the slot of where to add the item. + * + * @param item The item to add. + * @return The slot where the item will go. + */ + public int getAddSlot(Item item) { + if (type != ContainerType.NEVER_STACK && (item.getDefinition().isStackable() || type.equals(ContainerType.ALWAYS_STACK) || type == ContainerType.SHOP)) { + boolean hashBased = sortType == SortType.HASH; + for (int i = 0; i < items.length; i++) { + if (items[i] != null) { + if ((hashBased && items[i].getIdHash() == item.getIdHash()) || (!hashBased && items[i].getId() == item.getId())) { + return i; + } + } + } + } + return freeSlot(); + } + + /** + * Gets the number of free slots. + * + * @return The number of free slots. + */ + public int freeSlots() { + return capacity - itemCount(); + } + + /** + * Gets the size of this container. + * + * @return The size of this container. + */ + public int itemCount() { + int size = 0; + for (int i = 0; i < items.length; i++) { + if (items[i] != null) { + size++; + } + } + return size; + } + + /** + * Checks if the player has all the item ids in the inventory. + * + * @param itemIds The item ids. + * @return {@code True} if so. + */ + public boolean containItems(int... itemIds) { + for (int i = 0; i < itemIds.length; i++) { + if (!contains(itemIds[i], 1)) { + return false; + } + } + return true; + } + + /** + * Gets the amount of an item. + * + * @param item The item. + * @return The amount of this item in this container. + */ + public int getAmount(Item item) { + if (item == null) { + return 0; + } + int count = 0; + for (Item i : items) { + if (i != null && i.getId() == item.getId()) { + count += i.getAmount(); + } + } + return count; + } + + /** + * Gets the amount. + * + * @param id the id. + * @return the amount. + */ + public int getAmount(int id) { + return getAmount(new Item(id)); + } + + /** + * Shifts the elements in the Container to the appropriate position. + */ + public void shift() { + final Item itemss[] = items; + clear(false); + for (Item item : itemss) { + if (item == null) { + continue; + } + add(item, false); + } + refresh(); + } + + /** + * Checks if the container is empty. + * + * @return {@code True} if so. + */ + public boolean isEmpty() { + for (Item item : items) { + if (item != null) { + return false; + } + } + return true; + } + + /** + * Checks if the container is full. + * + * @return {@code True} if so. + */ + public boolean isFull() { + return freeSlots() < 1; + } + + /** + * Clears and updates the container. + */ + public void clear() { + clear(true); + } + + /** + * Clears the container. + * + * @param update If the container should be updated. + */ + public void clear(boolean update) { + items = new Item[capacity]; + event.flagEmpty(); + if (update) { + refresh(); + } + } + + /** + * Gets the wealth. + * + * @return the wealth. + */ + public int getWealth() { + int wealth = 0; + for (Item i : items) { + if (i == null) { + continue; + } + wealth += i.getDefinition().getValue() * i.getAmount(); + } + return wealth; + } + + /** + * Returns an array representing this container. + * + * @return The array. + */ + public Item[] toArray() { + return items; + } + + /** + * Gets the listeners. + * + * @return The listeners. + */ + public List getListeners() { + return listeners; + } + + /** + * Gets the capacity. + * + * @return The capacity of this container. + */ + public int capacity() { + return capacity; + } + + /** + * Gets the event. + * + * @return the event. + */ + public ContainerEvent getEvent() { + return event; + } + + @Override + public String toString() { + return "Container{" + + "items=" + Arrays.toString(items) + + ", capacity=" + capacity + + ", sortType=" + sortType + + ", type=" + type + + ", event=" + event + + ", listeners=" + listeners + + '}'; + } +} diff --git a/Server/src/main/java/core/game/container/ContainerEvent.java b/Server/src/main/core/game/container/ContainerEvent.java similarity index 100% rename from Server/src/main/java/core/game/container/ContainerEvent.java rename to Server/src/main/core/game/container/ContainerEvent.java diff --git a/Server/src/main/java/core/game/container/ContainerListener.java b/Server/src/main/core/game/container/ContainerListener.java similarity index 100% rename from Server/src/main/java/core/game/container/ContainerListener.java rename to Server/src/main/core/game/container/ContainerListener.java diff --git a/Server/src/main/java/core/game/container/ContainerType.java b/Server/src/main/core/game/container/ContainerType.java similarity index 100% rename from Server/src/main/java/core/game/container/ContainerType.java rename to Server/src/main/core/game/container/ContainerType.java diff --git a/Server/src/main/java/core/game/container/SortType.java b/Server/src/main/core/game/container/SortType.java similarity index 100% rename from Server/src/main/java/core/game/container/SortType.java rename to Server/src/main/core/game/container/SortType.java diff --git a/Server/src/main/java/core/game/container/access/InterfaceContainer.java b/Server/src/main/core/game/container/access/InterfaceContainer.java similarity index 91% rename from Server/src/main/java/core/game/container/access/InterfaceContainer.java rename to Server/src/main/core/game/container/access/InterfaceContainer.java index 713b72eda..2fde47411 100644 --- a/Server/src/main/java/core/game/container/access/InterfaceContainer.java +++ b/Server/src/main/core/game/container/access/InterfaceContainer.java @@ -1,5 +1,6 @@ package core.game.container.access; +import core.api.IfaceSettingsBuilder; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.net.packet.PacketRepository; @@ -37,14 +38,16 @@ public class InterfaceContainer { private static int generate(Player player, Item[] itemArray, String[] options, int interfaceIndex, int childIndex, int x, int y, int key) { Object[] clientScript = new Object[options.length + 7]; player.getPacketDispatch().sendRunScript(CLIENT_SCRIPT_INDEX, generateScriptArguments(options.length), populateScript(clientScript, options, interfaceIndex << 16 | childIndex, x, y, key)); - BitregisterAssembler.send(player, interfaceIndex, childIndex, 0, itemArray.length, new BitregisterAssembler(options)); + int settings = new IfaceSettingsBuilder().enableAllOptions().build(); + player.getPacketDispatch().sendIfaceSettings(settings, childIndex, interfaceIndex, 0, itemArray.length); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, key, itemArray, itemArray.length, false)); return increment(); } public static int generateOptions(Player player, String[] options, int interfaceIndex, int childIndex, int x, int y, int key){ player.getPacketDispatch().sendRunScript(CLIENT_SCRIPT_INDEX, generateScriptArguments(options.length), populateScript(new Object[options.length + 7],options,interfaceIndex << 16 | childIndex, x, y, key)); - BitregisterAssembler.send(player,interfaceIndex,childIndex,0,28,new BitregisterAssembler(options)); + int settings = new IfaceSettingsBuilder().enableAllOptions().build(); + player.getPacketDispatch().sendIfaceSettings(settings, childIndex, interfaceIndex, 0, 28); return increment(); } @@ -76,7 +79,8 @@ public class InterfaceContainer { int key = increment(); Object[] clientScript = new Object[options.length + 7]; player.getPacketDispatch().sendRunScript(CLIENT_SCRIPT_INDEX, generateScriptArguments(options.length), populateScript(clientScript, options, interfaceId << 16 | childId, x, y, key)); - BitregisterAssembler.send(player, interfaceId, childId, 0, itemLength, new BitregisterAssembler(options)); + int settings = new IfaceSettingsBuilder().enableAllOptions().build(); + player.getPacketDispatch().sendIfaceSettings(settings, childId, interfaceId, 0, itemLength); return key; } @@ -125,7 +129,6 @@ public class InterfaceContainer { /** * Default method to generate and send an item array for the client. * @return The container key. - * @see {@link InterfaceContainer.generate} */ public static int generateItems(Player player, Item[] itemArray, String[] options, int interfaceIndex, int childIndex) { return generateItems(player, itemArray, options, interfaceIndex, childIndex, 7, 3, increment()); @@ -134,7 +137,6 @@ public class InterfaceContainer { /** * Default method to generate and send an item array for the client. * @return The container key. - * @see {@link InterfaceContainer.generate} */ public static int generateItems(Player player, Item[] itemArray, String[] options, int interfaceIndex, int childIndex, int key) { return generateItems(player, itemArray, options, interfaceIndex, childIndex, 7, 3, key); diff --git a/Server/src/main/core/game/container/impl/BankContainer.java b/Server/src/main/core/game/container/impl/BankContainer.java new file mode 100644 index 000000000..b27ecf700 --- /dev/null +++ b/Server/src/main/core/game/container/impl/BankContainer.java @@ -0,0 +1,515 @@ +package core.game.container.impl; + +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; +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 kotlin.ranges.IntRange; +import org.rs09.consts.Vars; + +import static core.api.ContentAPIKt.*; + + +/** + * Represents the bank container. + * @author Emperor + */ +public final class BankContainer extends Container { + + /** + * The bank container size. + */ + public static final int SIZE = ServerConstants.BANK_SIZE; + + /** + * The maximum amount of bank tabs + */ + public static final int TAB_SIZE = 11; + + /** + * The player reference. + */ + private Player player; + + /** + * The bank listener. + */ + private final BankListener listener; + + /** + * If the bank is open. + */ + private boolean open; + + /** + * The last x-amount entered. + */ + private int lastAmountX = 50; + + /** + * The current tab index. + */ + private int tabIndex = 10; + + /** + * The tab start indexes. + */ + private final int[] tabStartSlot = new int[TAB_SIZE]; + + /** + * Construct a new {@code BankContainer} {@code Object}. + * @param player The player reference. + */ + public BankContainer(Player player) { + super(SIZE, ContainerType.ALWAYS_STACK, SortType.HASH); + super.register(listener = new BankListener(player)); + this.player = player; + } + + /** + * Method used to open the deposit box. + */ + public void openDepositBox() { + player.getInterfaceManager().open(new Component(11)).setCloseEvent((player, c) -> { + player.getInterfaceManager().openDefaultTabs(); + return true; + }); + player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6); + refreshDepositBoxInterface(); + } + + /** + * Invalidates the visual state of deposit box interface + * forcing the client to re-draw the items + */ + public void refreshDepositBoxInterface() + { + InterfaceContainer.generateItems( + player, + player.getInventory().toArray(), + new String[] { + "Examine", + "Deposit-X", + "Deposit-All", + "Deposit-10", + "Deposit-5", + "Deposit-1" + }, 11, 15, 5, 7 + ); + } + + /** + * Open the bank. + */ + public void open() { + if (open) { + return; + } + if (player.getIronmanManager().checkRestriction(IronmanMode.ULTIMATE)) { + return; + } + if (!player.getBankPinManager().isUnlocked() && !GameWorld.getSettings().isDevMode()) { + player.getBankPinManager().openType(1); + return; + } + player.getInterfaceManager().openComponent(762).setCloseEvent((player, c) -> { + BankContainer.this.close(); + return true; + }); + player.getInterfaceManager().openSingleTab(new Component(763)); + super.refresh(); + player.getInventory().refresh(); + 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); + open = true; + } + + public void open(Player player) { + if (open) { + return; + } + if (player.getIronmanManager().checkRestriction(IronmanMode.ULTIMATE)) { + return; + } + if (!player.getBankPinManager().isUnlocked() && !GameWorld.getSettings().isDevMode()) { + player.getBankPinManager().openType(1); + return; + } + player.getInterfaceManager().openComponent(762).setCloseEvent((player1, c) -> { + BankContainer.this.close(); + return true; + }); + player.getInterfaceManager().openSingleTab(new Component(763)); + refresh(listener); + player.getInventory().refresh(); + 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; + } + + /** + * Closes the bank. + */ + public void close() { + open = false; + player.getInventory().getListeners().remove(listener); + player.getInterfaceManager().closeSingleTab(); + player.removeAttribute("search"); + player.getPacketDispatch().sendRunScript(571, ""); + } + + /** + * Adds an item to the bank container. + * @param slot The item slot. + * @param amount The amount. + */ + public void addItem(int slot, int amount) { + if (slot < 0 || slot > player.getInventory().capacity() || amount < 1) { + return; + } + Item item = player.getInventory().get(slot); + if (item == null) { + return; + } + + if (!item.getDefinition().getConfiguration(ItemConfigParser.BANKABLE, true)) { + player.sendMessage("A magical force prevents you from banking this item"); + return; + } + + int maximum = player.getInventory().getAmount(item); + if (amount > maximum) { + amount = maximum; + } + + item = new Item(item.getId(), amount, item.getCharge()); + Item add = item.toUnnotedItem(); + + int maxCount = getMaximumAdd(add); + if (amount > maxCount) { + add.setAmount(maxCount); + item.setAmount(maxCount); + if (maxCount < 1) { + player.getPacketDispatch().sendMessage("There is not enough space left in your bank."); + return; + } + } + + 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); + insert(freeSlot(), preferredSlot, false); + increaseTabStartSlots(tabIndex); + } + super.add(add, true, preferredSlot); + player.getInventory().update(); + } + } + + /** + * Takes a item from the bank container and adds one to the inventory + * container. + * @param slot The slot. + * @param amount The amount. + */ + public void takeItem(int slot, int amount) { + if (slot < 0 || slot > super.capacity() || amount <= 0) { + return; + } + Item item = get(slot); + if (item == null) { + return; + } + if (amount > item.getAmount()) { + amount = item.getAmount(); // It always stacks in the bank. + } + item = new Item(item.getId(), amount, item.getCharge()); + int noteId = item.getDefinition().getNoteId(); + Item add = isNoteItems() && noteId > 0 ? new Item(noteId, amount, item.getCharge()) : item; + int maxCount = player.getInventory().getMaximumAdd(add); + if (amount > maxCount) { + item.setAmount(maxCount); + add.setAmount(maxCount); + if (maxCount < 1) { + player.getPacketDispatch().sendMessage("Not enough space in your inventory."); + return; + } + } + if (isNoteItems() && noteId < 0) { + player.getPacketDispatch().sendMessage("This item can't be withdrawn as a note."); + add = item; + } + if (super.remove(item, slot, false)) { + player.getInventory().add(add, false); + } + if (get(slot) == null) { + int tabId = getTabByItemSlot(slot); + decreaseTabStartSlots(tabId); + shift(); + } else update(); + player.getInventory().update(); + } + + /** + * Updates the last x-amount entered. + * @param amount The amount to set. + */ + public void updateLastAmountX(int amount) { + this.lastAmountX = amount; + setVarp(player, 1249, amount); + } + + /** + * Gets the tab the item slot is in. + * @param itemSlot The item slot. + * @return The tab index. + */ + public int getTabByItemSlot(int itemSlot) { + int tabId = 0; + for (int i = 0; i < tabStartSlot.length; i++) { + if (itemSlot >= tabStartSlot[i]) { + tabId = i; + } + } + return tabId; + } + + /** + * Increases a tab's start slot. + * @param startId The start id. + */ + public void increaseTabStartSlots(int startId) { + for (int i = startId + 1; i < tabStartSlot.length; i++) { + tabStartSlot[i]++; + } + } + + /** + * Decreases a tab's start slot. + * @param startId The start id. + */ + public void decreaseTabStartSlots(int startId) { + if (startId == 10) { + return; + } + for (int i = startId + 1; i < tabStartSlot.length; i++) { + tabStartSlot[i]--; + } + if (getItemsInTab(startId) == 0) { + collapseTab(startId); + } + } + + /** + * Gets the array index for a tab. + * @param tabId The tab id. + * @return The array index. + */ + public static int getArrayIndex(int tabId) { + if (tabId == 41 || tabId == 74) { + return 10; + } + int base = 39; + for (int i = 1; i < 10; i++) { + if (tabId == base) { + return i; + } + base -= 2; + } + return -1; + } + + /** + * Sends the bank space values on the interface. + */ + public void sendBankSpace() { + setVarc(player, 192, capacity() - freeSlots()); + } + + /** + * Collapses a tab. + * @param tabId The tab index. + */ + public void collapseTab(int tabId) { + int size = getItemsInTab(tabId); + Item[] tempTabItems = new Item[size]; + for (int i = 0; i < size; i++) { + tempTabItems[i] = get(tabStartSlot[tabId] + i); + replace(null, tabStartSlot[tabId] + i, false); + } + shift(); + for (int i = tabId; i < tabStartSlot.length - 1; i++) { + tabStartSlot[i] = tabStartSlot[i + 1] - size; + } + tabStartSlot[10] = tabStartSlot[10] - size; + for (int i = 0; i < size; i++) { + int slot = freeSlot(); + replace(tempTabItems[i], slot, false); + } + refresh(); //We only refresh once. + } + + /** + * Sets the tab configs. + */ + public void setTabConfigurations() { + for (int i = 0; i < 8; i++) { + setVarbit(player, 4885 + i, getItemsInTab(i + 1)); + } + } + + /** + * Gets the amount of items in one tab. + * @param tabId The tab index. + * @return The amount of items in this tab. + */ + public int getItemsInTab(int tabId) { + return tabStartSlot[tabId + 1] - tabStartSlot[tabId]; + } + + /** + * Checks if the item can be added. + * @param item the item. + * @return {@code True} if so. + */ + public boolean canAdd(Item item) { + return item.getDefinition().getConfiguration(ItemConfigParser.BANKABLE, true); + } + + /** + * Gets the last x-amount. + * @return The last x-amount. + */ + public int getLastAmountX() { + return lastAmountX; + } + + /** + * If items have to be noted. + * @return If items have to be noted {@code true}. + */ + public boolean isNoteItems() { + return getVarbit(player, Vars.VARBIT_IFACE_BANK_NOTE_MODE) == 1; + } + + /** + * Set if items have to be noted. + * @param noteItems If items have to be noted {@code true}. + */ + public void setNoteItems(boolean noteItems) { + setVarbit(player, Vars.VARBIT_IFACE_BANK_NOTE_MODE, noteItems ? 1 : 0, true); + } + + /** + * Gets the tabStartSlot value. + * @return The tabStartSlot. + */ + public int[] getTabStartSlot() { + return tabStartSlot; + } + + /** + * Gets the tabIndex value. + * @return The tabIndex. + */ + public int getTabIndex() { + return tabIndex; + } + + /** + * Sets the tabIndex value. + * @param tabIndex The tabIndex to set. + */ + public void setTabIndex(int tabIndex) { + this.tabIndex = tabIndex == 0 ? 10 : tabIndex; + setVarbit(player, 4893, tabIndex + 1); + setAttribute(player, "bank:lasttab", tabIndex); + } + + /** + * Sets the insert items value. + * @param insertItems The insert items value. + */ + public void setInsertItems(boolean insertItems) { + setVarbit(player, Vars.VARBIT_IFACE_BANK_INSERT_MODE, insertItems ? 1 : 0, true); + } + + /** + * Gets the insert items value. + * @return {@code True} if inserting items mode is enabled. + */ + public boolean isInsertItems() { + return getVarbit(player, Vars.VARBIT_IFACE_BANK_INSERT_MODE) == 1; + } + + /** + * Checks if the bank is opened. + * @return {@code True} if so. + */ + public boolean isOpen() { + return open; + } + + @Override + public int getMaximumAdd(Item item) { + return super.getMaximumAdd(item.toUnnotedItem()); + } + + /** + * Listens to the bank container. + * @author Emperor + */ + private static class BankListener implements ContainerListener { + + /** + * The player reference. + */ + private Player player; + + /** + * Construct a new {@code BankListener} {@code Object}. + * @param player The player reference. + */ + public BankListener(Player player) { + this.player = player; + } + + @Override + 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())); + } + } + + @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)); + } + } + } +} diff --git a/Server/src/main/core/game/container/impl/EquipmentContainer.java b/Server/src/main/core/game/container/impl/EquipmentContainer.java new file mode 100644 index 000000000..d398d9f3b --- /dev/null +++ b/Server/src/main/core/game/container/impl/EquipmentContainer.java @@ -0,0 +1,379 @@ +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; +import core.game.container.ContainerListener; +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.net.packet.PacketRepository; +import core.net.packet.context.ContainerContext; +import core.net.packet.out.ContainerPacket; +import core.net.packet.out.WeightUpdate; +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 + */ +public final class EquipmentContainer extends Container { + /** + * The equipment slots. + */ + public static final int SLOT_HAT = 0, SLOT_CAPE = 1, SLOT_AMULET = 2, SLOT_WEAPON = 3, SLOT_CHEST = 4, SLOT_SHIELD = 5, SLOT_LEGS = 7, SLOT_HANDS = 9, SLOT_FEET = 10, SLOT_RING = 12, SLOT_ARROWS = 13; + + /** + * The bonus names. + */ + private static final String[] BONUS_NAMES = { "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Summoning: ", "Strength: ", "Prayer: " }; + + /** + * The player. + */ + private final Player player; + + /** + * Constructs a new {@code EquipmentContainer} {@code Object}. + * @param player The player. + */ + public EquipmentContainer(Player player) { + super(14); + this.player = player; + register(new EquipmentListener(player)); + } + + @Override + public boolean add(Item item, boolean fire) { + return add(item, fire, true); + } + + /** + * Adds an item to the equipment container. + * @param item The item to add. + * @param fire If we should refresh. + * @param fromInventory If the item is being equipped from the inventory. + * @return {@code True} if succesful, {@code false} if not. + */ + public boolean add(Item item, boolean fire, boolean fromInventory) { + return add(item, player.getInventory().getSlot(item), fire, fromInventory); + } + + /** + * Adds an item to the equipment container. + * @param newItem The item to add. + * @param inventorySlot The inventory slot of the item to add. + * @param fire If we should refresh. + * @param fromInventory If the item is being equipped from the inventory. + * @return {@code True} if succesful, {@code false} if not. + */ + public boolean add(Item newItem, int inventorySlot, boolean fire, boolean fromInventory) { + int equipmentSlot = newItem.getDefinition().getConfiguration(ItemConfigParser.EQUIP_SLOT, -1); + if (!isEquippable(newItem, equipmentSlot)) return false; + + ArrayList itemsToRemove = new ArrayList<>(); + + Item currentItem = super.get(equipmentSlot); + if(currentItem != null) itemsToRemove.add(currentItem); + + Item secondaryEquip = getSecondaryEquipIfApplicable(newItem, equipmentSlot); + if(secondaryEquip != null) itemsToRemove.add(secondaryEquip); + + if(fromInventory && !player.getInventory().remove(newItem, inventorySlot, true)) { + return false; + } + + if (!itemsToRemove.isEmpty()) { + ArrayList invalidatedEntries = new ArrayList<>(); + for(Item current : itemsToRemove) { + if(current.getId() == newItem.getId() && current.getDefinition().isStackable()) { + addStackableItemToExistingStack(newItem, fromInventory, equipmentSlot, current); + invalidatedEntries.add(current); + } + } + itemsToRemove.removeAll(invalidatedEntries); + + if(itemsToRemove.isEmpty()) { + return true; + } + + boolean successfullyRemovedAll = tryUnequipCurrent(itemsToRemove, newItem, inventorySlot); + + if(!successfullyRemovedAll) { + if (fromInventory) player.getInventory().add(newItem); //add the item back in case we weren't able to remove the currently equipped item(s) + return false; + } + } + + super.replace(newItem, equipmentSlot, fire); + setWeaponInterfaceWeaponName(newItem); + return true; + } + + private boolean isEquippable(Item newItem, int slot) { + if (slot < 0) { + return false; + } + return newItem.getDefinition().hasRequirement(player, true, true); + } + + private void setWeaponInterfaceWeaponName(Item newItem) { + if (newItem.getSlot() == SLOT_WEAPON) { + player.getPacketDispatch().sendString(newItem.getName(), 92, 0); + } + } + + private boolean tryUnequipCurrent(ArrayList current, Item newItem, int preferredSlot) { + if(current.isEmpty()) return true; + int freeSlots = player.getInventory().freeSlots(); + int neededSlots = getNeededSlotsToUnequip(current); + + boolean hasSpaceForUnequippedItems = freeSlots >= neededSlots; + + if(!hasSpaceForUnequippedItems) { + player.getPacketDispatch().sendMessage("Not enough space in your inventory!"); + return false; + } + + boolean listenersSayWeCanUnequip = runUnequipHooks(current, newItem); + + boolean allRemoved = true; + for(Item item : current) { + if(!remove(item)) { + allRemoved = false; + break; + } + } + + boolean allAdded = allRemoved; + if(allRemoved) { + for (Item item : current) { + if (!player.getInventory().add(item, true, preferredSlot)) { + allAdded = false; + break; + } + } + } + + if (listenersSayWeCanUnequip && allRemoved && allAdded) return true; + else { + //put things back if we couldn't remove everything + for(Item item : current) { + if(!containsItem(item)) { + add(item); + } + } + } + + return false; + } + + private int getNeededSlotsToUnequip(ArrayList current) { + int neededSlots = 0; + + for(Item item : current) { + if(!item.getDefinition().isStackable()) { + neededSlots++; + } else { + if(player.getInventory().getAmount(item.getId()) == 0) { + neededSlots++; + } + } + } + return neededSlots; + } + + @Nullable + private Item getSecondaryEquipIfApplicable(Item newItem, int equipmentSlot) { + Item secondaryEquipItem = null; + if (newItem.getDefinition().getConfiguration(ItemConfigParser.TWO_HANDED, false)) { + secondaryEquipItem = get(SLOT_SHIELD); + } else if (equipmentSlot == SLOT_SHIELD) { + Item inSlot = player.getEquipment().get(SLOT_WEAPON); + if(inSlot != null && inSlot.getDefinition().getConfiguration(ItemConfigParser.TWO_HANDED, false)) + secondaryEquipItem = get(SLOT_WEAPON); + } + return secondaryEquipItem; + } + + private boolean runUnequipHooks(ArrayList currentItems, Item newItem) { + boolean canContinue = true; + + for(Item currentItem : currentItems) { + Plugin plugin = currentItem.getDefinition().getConfiguration("equipment", null); + if (plugin != null) { + Object object = plugin.fireEvent("unequip", player, currentItem); + if (object != null && !((Boolean) object)) { + canContinue = false; + break; + } + } + + canContinue = InteractionListeners.run(currentItem.getId(), player, currentItem, false); + + if(!canContinue) break; + } + + return canContinue; + } + + private void addStackableItemToExistingStack(Item item, boolean fromInventory, int slot, Item current) { + int amount = getMaximumAdd(item); + if (item.getAmount() > amount) { + amount += current.getAmount(); + } else { + amount = current.getAmount() + item.getAmount(); + } + Item transferItem = new Item(current.getId(), amount); + if (fromInventory) { + player.getInventory().remove(transferItem); + } + replace(transferItem, slot); + } + + /** + * Listens to the equipment container. + * @author Emperor + */ + private static class EquipmentListener implements ContainerListener { + + /** + * The player. + */ + private final Player player; + + /** + * Constructs a new {@code EquipmentContainer} {@code Object}. + * @param player The player. + */ + public EquipmentListener(Player player) { + this.player = player; + } + + @Override + public void update(Container c, ContainerEvent event) { + int[] slots = event.getSlots(); + PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 387, 28, 94, event.getItems(), false, slots)); + update(c); + boolean updateDefenceAnimation = false; + for (int slot : slots) { + if (slot == EquipmentContainer.SLOT_WEAPON) { + player.getProperties().setAttackSpeed(c.getNew(slot).getDefinition().getConfiguration(ItemConfigParser.ATTACK_SPEED, 4)); + WeaponInterface inter = player.getExtension(WeaponInterface.class); + if (inter == null) { + break; + } + inter.updateInterface(); + updateDefenceAnimation = true; + } else if (slot == EquipmentContainer.SLOT_SHIELD) { + updateDefenceAnimation = true; + } else if (slot == EquipmentContainer.SLOT_CAPE) { + SkillcapeEquipmentPlugin.updateCapePerks(player, c, slot); + } + } + if (updateDefenceAnimation) { + player.getProperties().updateDefenceAnimation(); + } + } + + @Override + public void refresh(Container c) { + player.getProperties().setAttackSpeed(c.getNew(3).getDefinition().getConfiguration(ItemConfigParser.ATTACK_SPEED, 4)); + WeaponInterface inter = player.getExtension(WeaponInterface.class); + if (inter != null) { + inter.updateInterface(); + } + PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 387, 28, 94, c.toArray(), 14, false)); + update(c); + player.getProperties().updateDefenceAnimation(); + } + + /** + * Updates the bonuses, weight, animations, ... + * @param c The container. + */ + public void update(Container c) { + if (c.getNew(SLOT_SHIELD).getId() != 11283 && player.getAttribute("dfs_spec", false)) { + player.removeAttribute("dfs_spec"); + player.getProperties().getCombatPulse().setHandler(null); + if (!player.getSettings().isSpecialToggled()) { + setVarp(player, 301, 0); + } + } + player.getAppearance().setAnimations(); + player.updateAppearance(); + player.getSettings().updateWeight(); + updateBonuses(player); + } + } + + /** + * Updates the bonuses. + * @param player The player. + */ + public static void updateBonuses(Player player) { + int[] bonuses = new int[15]; + for (Item item : player.getEquipment().toArray()) { + if (item != null) { + int[] bonus = item.getDefinition().getConfiguration(ItemConfigParser.BONUS, new int[15]); + for (int i = 0; i < bonus.length; i++) { + bonuses[i] += bonus[i]; + } + } + } + Item weapon = player.getEquipment().get(SLOT_WEAPON); + if(weapon != null && weapon.getDefinition().getRequirement(Skills.STRENGTH) > 0 && SkillcapePerks.isActive(SkillcapePerks.FINE_ATTUNEMENT,player)){ + int[] bonus = weapon.getDefinition().getConfiguration(ItemConfigParser.BONUS, new int[15]); + bonuses[11] += Math.ceil(bonus[11] * 0.20); + } + Item shield = player.getEquipment().get(SLOT_SHIELD); + if(shield != null && SkillcapePerks.isActive(SkillcapePerks.GRAND_BULLWARK,player)){ + bonuses[5] += Math.ceil(bonuses[5] * 0.20); + bonuses[6] += Math.ceil(bonuses[6] * 0.20); + bonuses[7] += Math.ceil(bonuses[7] * 0.20); + bonuses[9] += Math.ceil(bonuses[9] * 0.20); + bonuses[10] += Math.ceil(bonuses[10] * 0.20); + } + if (shield != null && shield.getId() == 11283) { + int increase = shield.getCharge() / 20; + bonuses[5] += increase; + bonuses[6] += increase; + bonuses[7] += increase; + bonuses[9] += increase; + bonuses[10] += increase; + } + player.getProperties().setBonuses(bonuses); + if (player.getInterfaceManager().hasMainComponent(Components.EQUIP_SCREEN2_667)) { + update(player); + } + } + + /** + * Updates the equipment stats interface. + * @param player The player to update for. + */ + public static void update(Player player) { + 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.getSettings().updateWeight(); + player.getPacketDispatch().sendString("Attack bonus", 667, 34); + } +} diff --git a/Server/src/main/java/core/game/container/impl/InventoryListener.java b/Server/src/main/core/game/container/impl/InventoryListener.java similarity index 92% rename from Server/src/main/java/core/game/container/impl/InventoryListener.java rename to Server/src/main/core/game/container/impl/InventoryListener.java index 727ad6f20..4ef874f4a 100644 --- a/Server/src/main/java/core/game/container/impl/InventoryListener.java +++ b/Server/src/main/core/game/container/impl/InventoryListener.java @@ -3,7 +3,7 @@ package core.game.container.impl; import core.game.container.Container; import core.game.container.ContainerEvent; import core.game.container.ContainerListener; -import core.game.node.entity.skill.summoning.SummoningPouch; +import content.global.skill.summoning.SummoningPouch; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.net.packet.PacketRepository; @@ -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/java/core/game/content/dialogue/ActivateEnchantedGem.java b/Server/src/main/core/game/dialogue/ActivateEnchantedGem.java similarity index 88% rename from Server/src/main/java/core/game/content/dialogue/ActivateEnchantedGem.java rename to Server/src/main/core/game/dialogue/ActivateEnchantedGem.java index a3858653d..7865f4ddd 100644 --- a/Server/src/main/java/core/game/content/dialogue/ActivateEnchantedGem.java +++ b/Server/src/main/core/game/dialogue/ActivateEnchantedGem.java @@ -1,5 +1,6 @@ -package core.game.content.dialogue; +package core.game.dialogue; +import content.global.skill.slayer.SlayerManager; import core.cache.def.impl.ItemDefinition; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -26,7 +27,7 @@ public final class ActivateEnchantedGem extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - if (!player.getSlayer().hasStarted()) { + if (!SlayerManager.getInstance(player).hasStarted()) { player.getPacketDispatch().sendMessage("You try to activate the gem..."); return true; } diff --git a/Server/src/main/java/core/game/content/dialogue/DialogueAction.java b/Server/src/main/core/game/dialogue/DialogueAction.java similarity index 88% rename from Server/src/main/java/core/game/content/dialogue/DialogueAction.java rename to Server/src/main/core/game/dialogue/DialogueAction.java index 8f263e7e0..4ed1a4102 100644 --- a/Server/src/main/java/core/game/content/dialogue/DialogueAction.java +++ b/Server/src/main/core/game/dialogue/DialogueAction.java @@ -1,4 +1,4 @@ -package core.game.content.dialogue; +package core.game.dialogue; import core.game.node.entity.player.Player; diff --git a/Server/src/main/core/game/dialogue/DialogueBuilder.kt b/Server/src/main/core/game/dialogue/DialogueBuilder.kt new file mode 100644 index 000000000..5b1f542c5 --- /dev/null +++ b/Server/src/main/core/game/dialogue/DialogueBuilder.kt @@ -0,0 +1,428 @@ +package core.game.dialogue + +import content.data.Quests +import core.api.splitLines +import core.game.node.entity.player.Player +import core.tools.END_DIALOGUE +import java.util.regex.Pattern + +val DEBUG_DIALOGUE = 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: DialogueBuilder) + init { + 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()) { + if(clause.predicate(player!!)) { + stage = clause.handle(this, componentID, buttonID, stage) + if(stage == END_DIALOGUE) { + end() + } + return + } + } + } +} + +interface DialogueNode { + fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int +} + +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 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 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 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: 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 + } + 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(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.entries.asSequence().filter({ it.predicate(df.player!!) }).toList() + df.stage = tmp[0].nextStage + df.handle(componentID, 0) + return df.stage + } else { + return END_DIALOGUE + } + } +} +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: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + if(stage < nodes.size) { + return nodes[stage].handle(df, componentID, buttonID, stage) + } else { + return END_DIALOGUE + } + } +} + +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): 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 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 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): DialogueBuilder { + target.data[clauseIndex].nodes.add(PlayerLNode(FacialExpression.NEUTRAL, value)) + return this + } + fun playerl(expression: FacialExpression, value: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(PlayerLNode(expression, value)) + return this + } + fun player(vararg values: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(PlayerNode(FacialExpression.NEUTRAL, values as Array)) + return this + } + 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(EndWithNode(::endWithNoop)) + } + /** 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 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 new file mode 100644 index 000000000..c73e3dbab --- /dev/null +++ b/Server/src/main/core/game/dialogue/DialogueFile.kt @@ -0,0 +1,179 @@ +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 +import core.game.node.entity.player.Player +import core.tools.START_DIALOGUE + +abstract class DialogueFile { + var player: Player? = null + var npc: NPC? = null + var interpreter: DialogueInterpreter? = null + open var stage = START_DIALOGUE + var dialoguePlugin: DialoguePlugin? = null + + abstract fun handle(componentID: Int, buttonID: Int) + + fun load(player: Player, npc: NPC?, interpreter: DialogueInterpreter): DialogueFile { + this.player = player + this.npc = npc + this.interpreter = interpreter + interpreter.activeTopics.clear() + + return this + } + + open fun npc(vararg messages: String?): Component? { + if (npc != null) { + return interpreter!!.sendDialogues( + npc, + if (npc!!.id > 8591) FacialExpression.OLD_NORMAL else FacialExpression.FRIENDLY, + *messages + ) + } + return null + } + + open fun npc(id: Int, vararg messages: String?): Component? { + return interpreter!!.sendDialogues(id, FacialExpression.FRIENDLY, *messages) + } + + 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: FacialExpression?, vararg messages: String?): Component? { + return interpreter!!.sendDialogues(player, expression, *messages) + } + + /** + * Use the automatic linesplitting feature in DialUtils to produce npc dialogues + * @param expr the FacialExpression to use, located in the FacialExpression enum. + * @param msg the message for the NPC to say + */ + 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!!)) + } + + /** + * Use the automatic linesplitting feature in DialUtils to produce player dialogues + * @param expr the FacialExpression to use, located in the FacialExpression enum. + * @param msg the message for the player to say + */ + open fun playerl(expr: FacialExpression?, msg: String?): Component? { + return player(expr, *splitLines(msg!!)) + } + + open fun playerl(msg: String?): Component? { + return player(*splitLines(msg!!)) + } + + open fun end(){ + if(interpreter != null) interpreter!!.close() + } + + open fun sendNormalDialogue(entity: Entity?, expression: FacialExpression?, vararg messages: String?) { + interpreter!!.sendDialogues(entity, expression, *messages) + } + + open fun options(vararg options: String?, title: String = "Select an Option") { + interpreter!!.sendOptions(title, *options) + } + + /** + * Use in place of setting the stage to END_DIALOGUE when you want to return to the default dialogue plugin at START_DIALOGUE + */ + fun endFile(){ + interpreter!!.dialogue.file = null + + } + + /** + * Return to the default dialogue plugin at the given stage. + * Should be used in place of setting a new stage. I.E. instead of "stage = END_DIALOGUE" use "returnAtStage(whatever stage)" + * @param stage The stage to return to. + */ + fun returnAtStage(toStage: Int){ + dialoguePlugin!!.file = null + dialoguePlugin!!.stage = toStage + } + + /** + * Use when you've entered a DialogueFile but current state does not match any possible conditionals. + * Sort-of a fail-safe in a sense. + */ + fun abandonFile(){ + interpreter!!.dialogue.file = null + player("Huh. Nevermind.") + } + + open fun getCurrentStage(): Int{ + return stage + } + + fun Int.substage(stage: Int): Int{ + return this + stage + } + + fun dialogue(vararg messages: String){ + player?.dialogueInterpreter?.sendDialogue(*messages) + } + + 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 == 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) + } else if(topic.toStage is Int) { + stage = topic.toStage as Int + } + player(topic.text) + interpreter!!.activeTopics.clear() + return false + } + else { options(*validTopics.toTypedArray(), title = title) + return false + } + } +} diff --git a/Server/src/main/core/game/dialogue/DialogueInterpreter.java b/Server/src/main/core/game/dialogue/DialogueInterpreter.java new file mode 100644 index 000000000..ccc4a94c0 --- /dev/null +++ b/Server/src/main/core/game/dialogue/DialogueInterpreter.java @@ -0,0 +1,635 @@ +package core.game.dialogue; + +import core.game.event.DialogueCloseEvent; +import core.game.event.DialogueOpenEvent; +import core.game.event.DialogueOptionSelectionEvent; +import core.cache.def.impl.ItemDefinition; +import core.cache.def.impl.NPCDefinition; +import core.game.component.Component; +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; + +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; + + +/** + * Handles the dialogues. + * @author Emperor + */ +@PluginManifest(type = PluginType.DIALOGUE) +public final class DialogueInterpreter { + public ArrayList> activeTopics = new ArrayList<>(); + + /** + * The dialogue plugins. + */ + private static final Map PLUGINS = new HashMap<>(); + + /** + * a List of dialogue actions. + */ + private final List actions = new ArrayList<>(10); + + /** + * The currently opened dialogue. + */ + private DialoguePlugin dialogue; + + /** + * The current dialogue key. + */ + private int key; + + /** + * The player. + */ + private final Player player; + + /** + * Constructs a new {@code DialogueInterpreter} {@code Object}. + * @param player The player. + */ + public DialogueInterpreter(Player player) { + this.player = player; + } + + /** + * @param dialogue the dialogue to set. + */ + public void setDialogue(DialoguePlugin dialogue) { + this.dialogue = dialogue; + } + + /** + * Opens the dialogue for the given dialogue type. + * @param dialogueType The dialogue type. + * @param args the args. + * @return {@code True} if successful. + */ + public boolean open(String dialogueType, Object... args) { + return open(getDialogueKey(dialogueType), args); + } + + /** + * Opens the dialogue for the given NPC id. + * @param dialogueKey The dialogue key (usually NPC id). + * @param args The arguments. + * @return {@code True} if successful. + */ + public boolean open(int dialogueKey, Object... args) { + key = dialogueKey; + if (args.length > 0 && args[0] instanceof NPC) { + NPC npc = (NPC) args[0]; + npc.setDialoguePlayer(player); + try { //this prevents the dialogue interpreter from dying when you try opening an NPC's dialogue without passing the NPC itself as an argument + npc.getWalkingQueue().reset(); + npc.getPulseManager().clear(); + } catch(Exception e){ + e.printStackTrace(); + } + } else if (args.length < 1) { + args = new Object[] { dialogueKey }; + } + DialoguePlugin plugin = PLUGINS.get(dialogueKey); + if (plugin == null) { + return false; + } + if (player.isDebug()) { + player.sendMessage("Dialogue opening - " + plugin.getClass().getSimpleName() + ", key=" + dialogueKey + ""); + } + this.dialogue = plugin.newInstance(player); + if (dialogue == null || !dialogue.open(args)) { + dialogue = null; + return false; + } + + if (dialogue != null) { + player.dispatch(new DialogueOpenEvent(dialogue)); + } + + return true; + } + + public void open(DialogueFile file, Object... args){ + this.dialogue = new EmptyPlugin(player,file); + this.dialogue.open(args); + } + + /** + * Handles an dialogue input. + * @param componentId The id of the chatbox component. + * @param buttonId The button id. + */ + public void handle(int componentId, int buttonId) { + player.setAttribute("chatbox-buttonid",buttonId); + if((player.getDialogueInterpreter().getDialogue().file != null && player.getDialogueInterpreter().getDialogue().file.getCurrentStage() == END_DIALOGUE) || player.getDialogueInterpreter().getDialogue().stage == END_DIALOGUE){ + player.getInterfaceManager().closeChatbox(); + player.getInterfaceManager().openChatbox(137); + player.getDialogueInterpreter().dialogue.finished = true; + close(); + return; + } + + DialogueFile file = dialogue.file; + if (!activeTopics.isEmpty() && buttonId >= 2) { + Topic topic = activeTopics.get(buttonId - 2); + + if (!topic.getSkipPlayer()) + sendDialogues(player, topic.getExpr(), topic.getText()); + + if(topic.getToStage() instanceof DialogueFile) { + DialogueFile topicFile = (DialogueFile) topic.getToStage(); + dialogue.loadFile(topicFile); + } else if(topic.getToStage() instanceof Integer) { + int stage = (Integer) topic.getToStage(); + if (file == null) dialogue.stage = stage; + else file.setStage(stage); + } + activeTopics.clear(); + + if (topic.getSkipPlayer()) + handle(componentId, buttonId); + + return; + } + + if(file != null){ + player.dispatch( + new DialogueOptionSelectionEvent( + file, + file.getStage(), + buttonId - 1 + ) + ); + + file.handle(componentId,buttonId - 1); + } else { + player.dispatch( + new DialogueOptionSelectionEvent( + dialogue, + dialogue.stage, + buttonId - 1 + ) + ); + + dialogue.handle(componentId, buttonId - 1);//here + } + } + + /** + * Closes the current dialogue. + * @return {@code True} if successful. + */ + public boolean close() { + if (dialogue != null) { + actions.clear(); + + if (player.getInterfaceManager().getChatbox() != null && player.getInterfaceManager().getChatbox().getCloseEvent() != null) { + return true; + } + if (dialogue != null) { + DialoguePlugin d = dialogue; + dialogue = null; + d.close(); + player.dispatch(new DialogueCloseEvent(d)); + } + } + activeTopics.clear(); + return dialogue == null; + } + + /** + * Puts a dialogue plugin on the mapping. + * @param id The NPC id (or {@code 1 << 16 | dialogueId} when the dialogue + * isn't for an NPC). + * @param plugin The plugin. + */ + public static void add(int id, DialoguePlugin plugin) { + if (PLUGINS.containsKey(id)) { + throw new IllegalArgumentException("Dialogue " + (id & 0xFFFF) + " is already in use - [old=" + PLUGINS.get(id).getClass().getSimpleName() + ", new=" + plugin.getClass().getSimpleName() + "]!"); + } + PLUGINS.put(id, plugin); + } + + /** + * Send plain messages based on the amount of specified messages. + * @param messages The messages. + * @return The chatbox component. + */ + public Component sendDialogue(String... messages) { + if (messages.length < 1 || messages.length > 4) { + return null; + } + int interfaceId = 209 + messages.length; + for (int i = 0; i < messages.length; i++) { + player.getPacketDispatch().sendString(messages[i], interfaceId, i + 1); + } + player.getInterfaceManager().openChatbox(interfaceId); + player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), 1, false); + return player.getInterfaceManager().getChatbox(); + } + + /** + * Sends a plane message and hides the continue button. + * @param hideContinue if we should hide it or not. + * @param messages the messages. + * @return the component. + */ + public Component sendPlainMessage(final boolean hideContinue, String... messages) { + sendDialogue(messages); + player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), (messages.length + 1), hideContinue); + return player.getInterfaceManager().getChatbox(); + } + + /** + * Opens the destroy item chatbox interface. + * @param id The item id. + * @param message The message to display. + * @return The component. + */ + public Component sendDestroyItem(int id, String message) { + 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(" "); + StringBuilder sb = new StringBuilder(words[0]); + for (int i = 1; i < words.length; i++) { + if (i == (words.length / 2)) { + sb.append("
"); + } else { + sb.append(" "); + } + sb.append(words[i]); + } + text = sb.toString(); + } + 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(); + } + + /** + * Send plane messages with a blue title. + * @param title The title. + * @param messages The messages. + * @return The chatbox component. + */ + public Component sendPlaneMessageWithBlueTitle(String title, String... messages) { + player.getPacketDispatch().sendString(title, 372, 0); + for (int i = 0; i < messages.length; i++) { + player.getPacketDispatch().sendString(messages[i], 372, i + 1); + } + player.getInterfaceManager().openChatbox(372); + return player.getInterfaceManager().getChatbox(); + } + + /** + * Send plane messages with scroll and a blue title. + * @param title The title. + * @param messages The messages. + * @return The chatbox component. + */ + public Component sendScrollMessageWithBlueTitle(String title, String... messages) { + for (int i = 0; i < 11; i++) { + player.getPacketDispatch().sendString(" ", 421, i + 2); + } + player.getPacketDispatch().sendString(title, 421, 1); + for (int i = 0; i < messages.length; i++) { + player.getPacketDispatch().sendString(messages[i], 421, i + 2); + } + player.getInterfaceManager().openChatbox(421); + return player.getInterfaceManager().getChatbox(); + } + + /** + * Send a message with an item next to it. + * Accepts item IDs. + */ + public Component sendItemMessage(int itemId, String... messages) { + 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) { + // Select interface based on number of messages - 241 (1 line) to 244 (4 lines) + int interfaceId = 240 + messages.length; + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 1, false); + player.getPacketDispatch().sendInterfaceConfig(interfaceId, 2, false); + // Hide or empty the title, since double item messages do not have them. (Child 3) + player.getPacketDispatch().sendString("", interfaceId, 3); + // Loop and print messages on Child 4,5,6,7 based on messages count. + for(int i = 0; i < messages.length; i++) { + player.getPacketDispatch().sendString(messages[i], interfaceId, 4+i); + } + // Set the first Item (child 1) + ItemDefinition itemDef = ItemDefinition.forId(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 two items next to it. + * Accepts item IDs. + */ + public Component sendDoubleItemMessage(int first, int second, String... message) { + return sendDoubleItemMessage(new Item(first), new Item(second), 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... 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(); + } + + /** + * Send dialogues based on the amount of specified messages. + * @param entity The entity. + * @param expression The entity's facial expression. + * @param messages The messages. + * @return The chatbox component. + */ + public Component sendDialogues(Entity entity, FacialExpression expression, String... messages) { + return sendDialogues(entity, expression == null ? -1 : expression.getAnimationId(), messages); + } + + /** + * Send dialogues based on the amount of specified messages. + * @param entity The entity. + * @param expression The entity's facial expression. + * @param messages The messages. + * @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, 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, FacialExpression expression, boolean hide, String... messages) { + return sendDialogues(npcId, 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. + * @return The chatbox component. + */ + public Component sendDialogues(Entity entity, FacialExpression expression, boolean hide, String... messages) { + 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 should the continue button be hidden? + * @return The chatbox component. + */ + public Component sendDialogues(Entity entity, int expression, boolean hide, String... messages) { + return sendDialogues(entity.getId(), expression, hide, 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. + * @return The chatbox component. + */ + public Component sendDialogues(int npcId, FacialExpression expression, String... 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. + * @param expression The entity's facial expression. + * @param messages The messages. + * @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(doSubstitutions(player, messages[i]), interfaceId, (i + 4)); + } + 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(); + } + + /** + * Send options based on the amount of specified options. + * @param title The title. + * @param options The options. + */ + public Component sendOptions(Object title, String... options) { + int interfaceId = 224 + (2 * options.length); + if (options.length < 2 || options.length > 5) { + return null; + } + if (title != null) { + player.getPacketDispatch().sendString(title.toString(), interfaceId, 1); + } + 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(); + } + + /** + * Send a input run script. + * @param string The strings. + * @param objects The arguments. + */ + public void sendInput(boolean string, Object... objects) { + player.getPacketDispatch().sendRunScript(string ? 109 : 108, "s", objects); + } + + /** + * Sends a long input. + * @param objects the objects. + */ + public void sendLongInput(Object... objects) { + player.getPacketDispatch().sendRunScript(110, "s", objects); + } + + /** + * Sends the private message input. + * @param reciever The receiver. + */ + public void sendMessageInput(String reciever) { + player.getPacketDispatch().sendRunScript(107, "s", reciever); + } + + /** + * Checks if the dialogue for the given id is added. + * @param id The NPC id/dialogue id. + * @return {@code True} if so. + */ + public static boolean contains(int id) { + return PLUGINS.containsKey(id); + } + + /** + * Gets the currently opened dialogue. + * @return The dialogue plugin. + */ + public DialoguePlugin getDialogue() { + return dialogue; + } + + /** + * Reserves a key for the name. + * @param name The name. + * @return The key. + */ + public static int getDialogueKey(String name) { + return 1 << 16 | name.hashCode(); + } + + /** + * Adds a dialogue reward. + * @param action the reward. + */ + public void addAction(DialogueAction action) { + actions.add(action); + } + + /** + * Gets the actions. + * @return The actions. + */ + public List getActions() { + return actions; + } +} 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/java/core/game/content/dialogue/DialoguePlugin.java b/Server/src/main/core/game/dialogue/DialoguePlugin.java similarity index 75% rename from Server/src/main/java/core/game/content/dialogue/DialoguePlugin.java rename to Server/src/main/core/game/dialogue/DialoguePlugin.java index b814afc94..18d17dd37 100644 --- a/Server/src/main/java/core/game/content/dialogue/DialoguePlugin.java +++ b/Server/src/main/core/game/dialogue/DialoguePlugin.java @@ -1,4 +1,4 @@ -package core.game.content.dialogue; +package core.game.dialogue; import core.game.component.Component; import core.game.node.entity.Entity; @@ -7,12 +7,16 @@ import core.game.node.entity.player.Player; import core.plugin.Plugin; import core.plugin.PluginManifest; import core.plugin.PluginType; -import rs09.game.content.dialogue.DialogueFile; +import core.tools.Log; +import core.tools.SystemLogger; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; -import static rs09.tools.DialogueConstKt.DIALOGUE_INITIAL_OPTIONS_HANDLE; -import static rs09.tools.DialogueConstKt.START_DIALOGUE; +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; /** * Represents a dialogue plugin. @@ -166,14 +170,45 @@ public abstract class DialoguePlugin implements Plugin { } @Override - public abstract DialoguePlugin newInstance(Player player); + public DialoguePlugin newInstance(Player player) { + try { + Class classReference = Class.forName(this.getClass().getCanonicalName()); + + return (DialoguePlugin)classReference + .getDeclaredConstructor(Player.class) + .newInstance(player); + } catch (ClassNotFoundException + | IllegalAccessException + | IllegalArgumentException + | NoSuchMethodException + | InvocationTargetException + | InstantiationException e) { + e.printStackTrace(); + return null; + } + } /** * Opens the dialogue. * @param args The arguments. * @return {@code True} if the dialogue plugin succesfully opened. */ - public abstract boolean open(Object... args); + public boolean open(Object... args) { + player.getDialogueInterpreter().activeTopics.clear(); + if (args.length > 0 && args[0] instanceof NPC) { + npc = (NPC)args[0]; + } + + if (npc == null) { + log(this.getClass(), Log.WARN, + args[0].getClass().getSimpleName() + + "Is not assigning an NPC. Whoever did that should fix it." + ); + } + + player.getDialogueInterpreter().handle(0, 0); + return true; + } /** * Handles the progress of this dialogue.. @@ -241,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); } /** @@ -318,7 +353,7 @@ public abstract class DialoguePlugin implements Plugin { * @param msg the message for the NPC to say */ public Component npcl(FacialExpression expr, String msg){ - return npc(expr, api.DialUtils.splitLines(msg)); + return npc(expr, splitLines(msg, 54)); } /** @@ -327,7 +362,36 @@ public abstract class DialoguePlugin implements Plugin { * @param msg the message for the player to say */ public Component playerl(FacialExpression expr, String msg){ - return player(expr, api.DialUtils.splitLines(msg)); + return player(expr, splitLines(msg, 54)); } -} \ No newline at end of file + public boolean showTopics(Topic... topics) { + ArrayList validTopics = new ArrayList<>(); + for(Topic topic : topics) + { + if(topic instanceof IfTopic && !((IfTopic) topic).getShowCondition()) continue; + interpreter.activeTopics.add(topic); + validTopics.add(topic.getText()); + } + 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; + } + } +} diff --git a/Server/src/main/core/game/dialogue/DialogueTopic.kt b/Server/src/main/core/game/dialogue/DialogueTopic.kt new file mode 100644 index 000000000..3ef290a0b --- /dev/null +++ b/Server/src/main/core/game/dialogue/DialogueTopic.kt @@ -0,0 +1,14 @@ +package core.game.dialogue + +/** + * Topic/IfTopic system backported from my personal project + * @author Ceikry + */ +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 @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) +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/EmptyPlugin.kt b/Server/src/main/core/game/dialogue/EmptyPlugin.kt similarity index 86% rename from Server/src/main/kotlin/rs09/game/content/dialogue/EmptyPlugin.kt rename to Server/src/main/core/game/dialogue/EmptyPlugin.kt index 91a35127c..79857654e 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/EmptyPlugin.kt +++ b/Server/src/main/core/game/dialogue/EmptyPlugin.kt @@ -1,9 +1,8 @@ -package rs09.game.content.dialogue +package core.game.dialogue -import core.game.content.dialogue.DialoguePlugin import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import rs09.tools.START_DIALOGUE +import core.tools.START_DIALOGUE class EmptyPlugin(player: Player? = null,val file: DialogueFile?) : DialoguePlugin(player) { override fun newInstance(player: Player?): DialoguePlugin { diff --git a/Server/src/main/core/game/dialogue/FacialExpression.java b/Server/src/main/core/game/dialogue/FacialExpression.java new file mode 100644 index 000000000..8b4f2eb8c --- /dev/null +++ b/Server/src/main/core/game/dialogue/FacialExpression.java @@ -0,0 +1,158 @@ +package core.game.dialogue; + +/** + * Represents the facial expressions (the animations the entity does when + * talking). + * + * @author Emperor + * @author Empathy + */ +public enum FacialExpression { + + /** + * The normal talking expression. + */ + + //Names based off https://2009scape.wiki/w/Chathead/Animations + + //Chat heads from oldschool 2009scape? + //Maybe for gnomes or dwarves? Chat heads are frozen when used on Human NPCs + OLD_HAPPY(588), + OLD_CALM_TALK1(589), + OLD_CALM_TALK2(590), + OLD_DEFAULT(591), + OLD_EVIL1(592), + OLD_EVIL2(593), + OLD_NORMAL(594), + OLD_SNEAKY(595), + OLD_DISTRESSED(596), + OLD_DISTRESSED2(597), + OLD_ALMOST_CRYING(598), + OLD_BOWS_HEAD_SAD(599), + OLD_DRUNK_LEFT(600), + OLD_DRUNK_RIGHT(601), + OLD_NOT_INTERESTED(602), + OLD_SLEEPY(603), + OLD_PLAIN_EVIL(604), + OLD_LAUGH1(605), + OLD_LAUGH2(606), + OLD_LAUGH3(607), + OLD_LAUGH4(608), + OLD_EVIL_LAUGH(609), + OLD_SAD(610), + OLD_MORE_SAD(611), + OLD_ON_ONE_HAND(612), + OLD_NEARLY_CRYING(613), + OLD_ANGRY1(614), + OLD_ANGRY2(615), + OLD_ANGRY3(616), + OLD_ANGRY4(617), + + //Chatheads from 2009? + NOD_YES(9741), + WORRIED(9743), + HALF_WORRIED(9745), //Not on the wiki, first half of worried + AMAZED(9746), + EXTREMELY_SHOCKED(9750), + GUILTY(9758), + HALF_GUILTY(9760), //Not on the wiki, first half of guilty + SAD(9761), + CRYING(9765), + HALF_CRYING(9768), //Not on the wiki, but plays the first half of the crying animation + AFRAID(9772), + SCARED(9776), + PANICKED(9780), + ANNOYED(9784), + ANGRY(9785), + FURIOUS(9792), + ANGRY_WITH_SMILE(9796), // Not on the wiki + ANGRY_WITH_SMILE_AND_EVIL_EYE(9798), //Not on the Wiki + SLEEPING(9802), + SILENT(9804), + NEUTRAL(9808), + THINKING(9812), + HALF_THINKING(9814), + DISGUSTED(9816), + DISGUSTED_HEAD_SHAKE(9823), //Not on the wiki + ASKING(9827), + HALF_ASKING(9830), //Not on wiki, first half of Asking + ROLLING_EYES(9831), //9832, 9833 are the same + HALF_ROLLING_EYES(9834), //Not on Wiki, first half of Rolling eyes + DRUNK(9835), + SUSPICIOUS(9836), + LAUGH(9840), + LOUDLY_LAUGHING(9841), + EVIL_LAUGH(9842), + FRIENDLY(9844), + HAPPY(9847), + JOLLY(9851), + STRUGGLE(9865), //TODO: More? + //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), + CHILD_NEUTRAL(7174), + CHILD_LOUDLY_LAUGHING(7175), + CHILD_THINKING(7176), + CHILD_SAD(7177), + CHILD_GUILTY(7178), + 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. + */ + private final int animationId; + + /** + * Constructs a new {@code FacialExpression} {@code Object}. + * + * @param animationId The animation id. + */ + FacialExpression(int animationId) { + this.animationId = animationId; + } + + /** + * Gets the animation id. + * + * @return The animation id. + */ + public int getAnimationId() { + return animationId; + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/SimpleDialoguePlugin.java b/Server/src/main/core/game/dialogue/SimpleDialoguePlugin.java similarity index 95% rename from Server/src/main/java/core/game/content/dialogue/SimpleDialoguePlugin.java rename to Server/src/main/core/game/dialogue/SimpleDialoguePlugin.java index f39bc2daf..d930c3d61 100644 --- a/Server/src/main/java/core/game/content/dialogue/SimpleDialoguePlugin.java +++ b/Server/src/main/core/game/dialogue/SimpleDialoguePlugin.java @@ -1,4 +1,4 @@ -package core.game.content.dialogue; +package core.game.dialogue; import core.plugin.Initializable; import core.game.node.entity.player.Player; diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/SkillDialogueHandler.kt b/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt similarity index 79% rename from Server/src/main/kotlin/rs09/game/content/dialogue/SkillDialogueHandler.kt rename to Server/src/main/core/game/dialogue/SkillDialogueHandler.kt index cbdffd428..acc07905c 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/SkillDialogueHandler.kt +++ b/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.content.dialogue +package core.game.dialogue import core.game.node.entity.player.Player import core.game.node.item.Item @@ -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/java/core/game/content/dialogue/SkillDialoguePlugin.java b/Server/src/main/core/game/dialogue/SkillDialoguePlugin.java similarity index 83% rename from Server/src/main/java/core/game/content/dialogue/SkillDialoguePlugin.java rename to Server/src/main/core/game/dialogue/SkillDialoguePlugin.java index 4b6d4ff2b..f33a3a00c 100644 --- a/Server/src/main/java/core/game/content/dialogue/SkillDialoguePlugin.java +++ b/Server/src/main/core/game/dialogue/SkillDialoguePlugin.java @@ -1,11 +1,9 @@ -package core.game.content.dialogue; +package core.game.dialogue; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; import core.plugin.Initializable; import kotlin.Unit; -import rs09.game.content.dialogue.SkillDialogueHandler; /** * Represents the dialogue plugin used to automatically handle skill dialogues with creation amounts. @@ -58,8 +56,12 @@ public class SkillDialoguePlugin extends DialoguePlugin { if (amount != -1) { handler.create(amount, index); } else { - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { - handler.create((int) value, index); + sendInputDialogue(player, true, "Enter the amount:", (value) -> { + if (value instanceof String) { + handler.create(Integer.parseInt((String) value), index); + } else { + handler.create((int) value, index); + } return Unit.INSTANCE; }); return true; diff --git a/Server/src/main/core/game/diary/AreaDiaryTask.kt b/Server/src/main/core/game/diary/AreaDiaryTask.kt new file mode 100644 index 000000000..7041ad411 --- /dev/null +++ b/Server/src/main/core/game/diary/AreaDiaryTask.kt @@ -0,0 +1,22 @@ +package core.game.diary + +import core.api.inBorders +import core.game.node.entity.player.Player +import core.game.world.map.zone.ZoneBorders + +class AreaDiaryTask( + val zoneBorders: ZoneBorders, + val diaryLevel: DiaryLevel, + val taskId: Int, + private val condition: ((player: Player) -> Boolean)? = null +) { + fun whenSatisfied(player: Player, then: () -> Unit) { + var result = inBorders(player, zoneBorders) + + condition?.let { + result = it(player) + } + + if (result) then() + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/diary/DiaryEventHookBase.kt b/Server/src/main/core/game/diary/DiaryEventHookBase.kt new file mode 100644 index 000000000..692759fd9 --- /dev/null +++ b/Server/src/main/core/game/diary/DiaryEventHookBase.kt @@ -0,0 +1,223 @@ +package core.game.diary + +import core.api.* +import core.game.event.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.world.map.zone.ZoneBorders +import core.api.Event + +abstract class DiaryEventHookBase(private val diaryType: DiaryType) : MapArea, 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: DiaryEventHookBase, + private val handler: (Player, T) -> Unit + ) : EventHook { + override fun process(entity: Entity, event: T) { + forEligibleEntityDo(entity, event, handler) + } + } + + open val areaTasks get() = arrayOf() + + final override fun defineAreaBorders(): Array + = areaTasks.map { task -> task.zoneBorders }.toTypedArray() + + final override fun areaEnter(entity: Entity) { + if (entity !is Player) return + if (entity.isArtificial) return + + onAreaVisited(entity) + } + + final override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity !is Player) return + if (entity.isArtificial) return + + onAreaLeft(entity) + } + + final override fun login(player: Player) { + player.hook(Event.ResourceProduced, EventHandler(this, ::onResourceProduced)) + 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)) + player.hook(Event.DialogueOptionSelected, EventHandler(this, ::onDialogueOptionSelected)) + player.hook(Event.DialogueClosed, EventHandler(this, ::onDialogueClosed)) + player.hook(Event.UsedWith, EventHandler(this, ::onUsedWith)) + player.hook(Event.PickedUp, EventHandler(this, ::onPickedUp)) + player.hook(Event.InterfaceOpened, EventHandler(this, ::onInterfaceOpened)) + player.hook(Event.AttributeSet, EventHandler(this, ::onAttributeSet)) + player.hook(Event.AttributeRemoved, EventHandler(this, ::onAttributeRemoved)) + player.hook(Event.SpellCast, EventHandler(this, ::onSpellCast)) + player.hook(Event.ItemAlchemized, EventHandler(this, ::onItemAlchemized)) + player.hook(Event.ItemEquipped, EventHandler(this, ::onItemEquipped)) + player.hook(Event.ItemUnequipped, EventHandler(this, ::onItemUnequipped)) + player.hook(Event.ItemPurchased, EventHandler(this, ::onItemPurchasedFromShop)) + player.hook(Event.ItemSold, EventHandler(this, ::onItemSoldToShop)) + player.hook(Event.JobAssigned, EventHandler(this, ::onJobAssigned)) + player.hook(Event.FairyRingDialed, EventHandler(this, ::onFairyRingDialed)) + player.hook(Event.SummoningPointsRecharged, EventHandler(this, ::onSummoningPointsRecharged)) + player.hook(Event.PrayerPointsRecharged, EventHandler(this, ::onPrayerPointsRecharged)) + } + + protected fun fulfillTaskRequirement(player: Player, level: DiaryLevel, task: Int, attribute: String) { + if (getAttribute(player, attribute, false)) return + + player.achievementDiaryManager.updateTask( + player, + diaryType, + findIndexFor(level), + task, + false + ) + + setAttribute(player, "/save:$attribute", true) + } + + protected fun whenTaskRequirementFulfilled(player: Player, attribute: String, then: () -> Unit) { + if (getAttribute(player, attribute, false)) { + then() + removeAttribute(player, attribute) + } + } + + protected fun progressIncrementalTask( + player: Player, + level: DiaryLevel, + task: Int, + attribute: String, + maxProgress: Int + ) { + if (isTaskCompleted(player, level, task)) return + + val newValue = getAttribute(player, attribute, 0) + 1 + + setAttribute( + player, + "/save:${attribute}", + newValue + ) + + if (newValue < maxProgress) { + player.achievementDiaryManager.updateTask( + player, + diaryType, + findIndexFor(level), + task, + false + ) + } else { + finishTask(player, level, task) + removeAttribute(player, attribute) + } + } + + protected fun progressFlaggedTask( + player: Player, + level: DiaryLevel, + task: Int, + attribute: String, + bit: Int, + targetValue: Int + ) { + if (isTaskCompleted(player, level, task)) { + return + } + + val oldValue = getAttribute(player, attribute, 0) + val newValue = oldValue or bit + + if (newValue != targetValue) { + if ((oldValue and bit) != 0) return + + setAttribute( + player, + "/save:${attribute}", + newValue + ) + + player.achievementDiaryManager.updateTask( + player, + diaryType, + findIndexFor(level), + task, + false + ) + } else { + finishTask(player, level, task) + removeAttribute(player, attribute) + } + } + + protected fun finishTask(player: Player, level: DiaryLevel, task: Int) { + player.achievementDiaryManager.finishTask( + player, + diaryType, + findIndexFor(level), + task + ) + } + + private fun isTaskCompleted(player: Player, level: DiaryLevel, task: Int): Boolean { + return player.achievementDiaryManager.hasCompletedTask( + diaryType, + findIndexFor(level), + task + ) + } + + private fun findIndexFor(level: DiaryLevel): Int = getDiaryLevelIndex(diaryType, level) + + protected open fun onAreaVisited(player: Player) { + areaTasks.forEach { + it.whenSatisfied(player) { + finishTask( + player, + it.diaryLevel, + it.taskId + ) + } + } + } + + protected open fun onAreaLeft(player: Player) {} + protected open fun onResourceProduced(player: Player, event: ResourceProducedEvent) {} + 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) {} + protected open fun onDialogueClosed(player: Player, event: DialogueCloseEvent) {} + protected open fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) {} + protected open fun onUsedWith(player: Player, event: UseWithEvent) {} + protected open fun onPickedUp(player: Player, event: PickUpEvent) {} + protected open fun onInterfaceOpened(player: Player, event: InterfaceOpenEvent) {} + protected open fun onAttributeSet(player: Player, event: AttributeSetEvent) {} + protected open fun onAttributeRemoved(player: Player, event: AttributeRemoveEvent) {} + protected open fun onSpellCast(player: Player, event: SpellCastEvent) {} + protected open fun onItemAlchemized(player: Player, event: ItemAlchemizationEvent) {} + protected open fun onItemEquipped(player: Player, event: ItemEquipEvent) {} + protected open fun onItemUnequipped(player: Player, event: ItemUnequipEvent) {} + protected open fun onItemPurchasedFromShop(player: Player, event: ItemShopPurchaseEvent) {} + protected open fun onItemSoldToShop(player: Player, event: ItemShopSellEvent) {} + protected open fun onJobAssigned(player: Player, event: JobAssignmentEvent) {} + protected open fun onFairyRingDialed(player: Player, event: FairyRingDialEvent) {} + protected open fun onSummoningPointsRecharged(player: Player, event: SummoningPointsRechargeEvent) {} + protected open fun onPrayerPointsRecharged(player: Player, event: PrayerPointsRechargeEvent) {} +} \ No newline at end of file diff --git a/Server/src/main/core/game/diary/DiaryLevel.kt b/Server/src/main/core/game/diary/DiaryLevel.kt new file mode 100644 index 000000000..f983cc0c0 --- /dev/null +++ b/Server/src/main/core/game/diary/DiaryLevel.kt @@ -0,0 +1,8 @@ +package core.game.diary + +enum class DiaryLevel { + BEGINNER, + EASY, + MEDIUM, + HARD +} \ No newline at end of file diff --git a/Server/src/main/core/game/event/Event.kt b/Server/src/main/core/game/event/Event.kt new file mode 100644 index 000000000..9dfb6c767 --- /dev/null +++ b/Server/src/main/core/game/event/Event.kt @@ -0,0 +1,3 @@ +package core.game.event + +interface Event {} \ No newline at end of file diff --git a/Server/src/main/core/game/event/EventHook.kt b/Server/src/main/core/game/event/EventHook.kt new file mode 100644 index 000000000..728fa46b8 --- /dev/null +++ b/Server/src/main/core/game/event/EventHook.kt @@ -0,0 +1,7 @@ +package core.game.event + +import core.game.node.entity.Entity + +interface EventHook { + fun process(entity: Entity, event: T) +} diff --git a/Server/src/main/core/game/event/Events.kt b/Server/src/main/core/game/event/Events.kt new file mode 100644 index 000000000..b11d6241b --- /dev/null +++ b/Server/src/main/core/game/event/Events.kt @@ -0,0 +1,52 @@ +package core.game.event + +import core.game.component.Component +import core.game.dialogue.DialoguePlugin +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 +import content.global.handlers.iface.FairyRing +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 +data class DialogueOptionSelectionEvent(val dialogue: Any, val currentStage: Int, val optionId: Int) : Event +data class DialogueCloseEvent(val dialogue: DialoguePlugin) : Event +data class UseWithEvent(val used: Int, val with: Int) : Event +data class SelfDeathEvent(val killer: Entity) : Event +data class TickEvent(val worldTicks: Int) : Event +data class PickUpEvent(val itemId: Int) : Event +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, 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 +data class ItemShopPurchaseEvent(val itemId: Int, val amount: Int, val currency: Item) : Event +data class ItemShopSellEvent(val itemId: Int, val amount: Int, val currency: Item) : Event +data class JobAssignmentEvent(val jobType: JobType, val employerNpc: NPC) : Event +data class FairyRingDialEvent(val fairyRing: FairyRing) : Event +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 +data class PrayerActivatedEvent (val type: PrayerType) : Event +data class PrayerDeactivatedEvent (val type: PrayerType) : Event diff --git a/Server/src/main/kotlin/rs09/game/ge/BotPrices.kt b/Server/src/main/core/game/ge/BotPrices.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/ge/BotPrices.kt rename to Server/src/main/core/game/ge/BotPrices.kt index 44190357d..3d180d81a 100644 --- a/Server/src/main/kotlin/rs09/game/ge/BotPrices.kt +++ b/Server/src/main/core/game/ge/BotPrices.kt @@ -1,4 +1,4 @@ -package rs09.game.ge +package core.game.ge import core.cache.def.impl.ItemDefinition import org.rs09.consts.Items diff --git a/Server/src/main/kotlin/rs09/game/ge/GEAutoStock.kt b/Server/src/main/core/game/ge/GEAutoStock.kt similarity index 75% rename from Server/src/main/kotlin/rs09/game/ge/GEAutoStock.kt rename to Server/src/main/core/game/ge/GEAutoStock.kt index 113e57fa9..db0467e79 100644 --- a/Server/src/main/kotlin/rs09/game/ge/GEAutoStock.kt +++ b/Server/src/main/core/game/ge/GEAutoStock.kt @@ -1,13 +1,18 @@ -package rs09.game.ge +package core.game.ge +import core.api.StartupListener import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser -import rs09.ServerConstants +import core.ServerConstants import java.io.File import java.io.FileReader -class GEAutoStock { +class GEAutoStock : StartupListener { + override fun startup() { + autostock() + } + companion object { // autostock format should be identical to the botoffers json format. private val DB_PATH = "data" + File.separator + "eco" + File.separator + "autostock.json" @@ -21,7 +26,7 @@ class GEAutoStock { val offers = botSave["offers"] as JSONArray for (offer in offers) { val o = offer as JSONObject - OfferManager.addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt()) + GrandExchange.addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt()) } } } diff --git a/Server/src/main/core/game/ge/GEDB.kt b/Server/src/main/core/game/ge/GEDB.kt new file mode 100644 index 000000000..3fedd1e2d --- /dev/null +++ b/Server/src/main/core/game/ge/GEDB.kt @@ -0,0 +1,94 @@ +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 java.sql.Connection + +/** + * Collection of methods for interacting with the grand exchange databases + * @author Ceikry + */ +object GEDB { + lateinit var db: SQLiteProvider + + fun init() { + init (ServerConstants.GRAND_EXCHANGE_DATA_PATH + "grandexchange.db") + } + + fun init (path: String) { + db = SQLiteProvider(path, expectedTables) + db.initTables { createdTable: String -> + if (createdTable == "price_index") + populateInitialPriceIndex() + } + } + + @JvmStatic fun run(closure: (conn: Connection) -> Unit) { + db.run(closure) + } + + private fun convertJsonArray(arr: JSONArray): String + { + val sb = StringBuilder() + for ((index, data) in arr.withIndex()) + { + val item = data as JSONObject + sb.append("$index") + sb.append(",") + sb.append(item["id"]) + sb.append(",") + sb.append(item["amount"]) + if(index + 1 < arr.size) + sb.append(":") + } + return sb.toString() + } + + 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() + { + //price index isn't worth transferring, so we're just going to make a new one. + 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/java/core/game/ge/GEGuidePrice.java b/Server/src/main/core/game/ge/GEGuidePrice.java similarity index 96% rename from Server/src/main/java/core/game/ge/GEGuidePrice.java rename to Server/src/main/core/game/ge/GEGuidePrice.java index b077d94ce..f5cff84a3 100644 --- a/Server/src/main/java/core/game/ge/GEGuidePrice.java +++ b/Server/src/main/core/game/ge/GEGuidePrice.java @@ -3,7 +3,6 @@ package core.game.ge; import core.game.component.Component; import core.game.node.entity.player.Player; import core.tools.StringUtils; -import rs09.game.ge.OfferManager; /** * Represents the glass used to open the guide prices for the different type of @@ -140,7 +139,7 @@ public final class GEGuidePrice { player.getPacketDispatch().sendInterfaceConfig(642, i, false); } for (GuideItem item : getItems()) { - player.getPacketDispatch().sendString("" + OfferManager.getRecommendedPrice(item.item, false) + " gp", COMPONENT.getId(), item.getChildData()[0]); + player.getPacketDispatch().sendString("" + GrandExchange.getRecommendedPrice(item.item, false) + " gp", COMPONENT.getId(), item.getChildData()[0]); } } } diff --git a/Server/src/main/java/core/game/ge/GEItemSet.java b/Server/src/main/core/game/ge/GEItemSet.java similarity index 100% rename from Server/src/main/java/core/game/ge/GEItemSet.java rename to Server/src/main/core/game/ge/GEItemSet.java diff --git a/Server/src/main/core/game/ge/GrandExchange.kt b/Server/src/main/core/game/ge/GrandExchange.kt new file mode 100644 index 000000000..4472ed2f1 --- /dev/null +++ b/Server/src/main/core/game/ge/GrandExchange.kt @@ -0,0 +1,367 @@ +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.system.command.Privilege +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.repository.Repository +import core.tools.Log +import core.tools.SystemLogger +import java.lang.Integer.max +import java.util.concurrent.LinkedBlockingDeque + +/** + * Handles the exchanging of offers, offer update thread, etc. + * @author Ceikry + */ +class GrandExchange : StartupListener, Commands { + + /** + * Fallback safety check to make sure we don't start the GE twice under any circumstance + */ + var isRunning = false + + /** + * Initializes the offer manager and spawns an update thread. + * @param local whether or not the GE should be the local in-code server rather than some hypothetical remote implementation. + */ + fun boot(){ + if(isRunning) return + + SystemLogger.logGE("Initializing GE Update Worker") + + Thread { + Thread.currentThread().name = "GE Update Worker" + while(true) { + var offer = pendingOffers.takeFirst() + offer.writeNew() + offer = getOfferByUid(offer.uid) ?: continue + selectPotentialMatches(offer).asSequence() + .sortedBy { if (offer.sell) -it.offeredValue else it.offeredValue } + .filter { if (offer.sell) it.offeredValue >= offer.offeredValue else it.offeredValue <= offer.offeredValue } + .forEach { match -> exchange(offer, match) } + if (!offer.isBot && offer.amountLeft > 0 && !offer.sell) + tryExchangeWithBots(offer) + } + }.start() + + isRunning = true + } + + + private fun tryExchangeWithBots(offer: GrandExchangeOffer) { + GEDB.run { conn -> + val query = conn.prepareStatement(GET_MATCH_FROM_BOT_OFFERS) + query.setInt(1, offer.itemID) + val res = query.executeQuery() + + if (res.next()) { + exchange(offer, GrandExchangeOffer.fromBotQuery(res).also { it.timeStamp = offer.timeStamp - 1L }) + } + } + } + + private fun selectPotentialMatches(offer: GrandExchangeOffer): List { + val matches = ArrayList() + GEDB.run { conn -> + val query = conn.prepareStatement(GET_MATCHES_FROM_PLAYER_OFFERS) + query.setInt(1, offer.itemID) + query.setBoolean(2, !offer.sell) + val res = query.executeQuery() + while (res.next()) { + matches.add(GrandExchangeOffer.fromQuery(res)) + } + } + return matches + } + + override fun defineCommands() { + 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, "::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, "::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.") + } + } + + companion object { + val pendingOffers = LinkedBlockingDeque() + private val GET_SPECIFIC_OFFER_BY_UID = "SELECT * FROM player_offers WHERE uid = ?;" + private val GET_MATCHES_FROM_PLAYER_OFFERS = "SELECT * FROM player_offers WHERE item_id = ? AND is_sale = ? AND offer_state < 4 AND NOT offer_state = 2;" + private val GET_MATCH_FROM_BOT_OFFERS = "SELECT * FROM bot_offers WHERE item_id = ?;" + + private fun getOfferByUid(uid: Long): GrandExchangeOffer? { + var offer: GrandExchangeOffer? = null + GEDB.run { conn -> + val query = conn.prepareStatement(GET_SPECIFIC_OFFER_BY_UID) + query.setLong(1, uid) + val res = query.executeQuery() + + if (res.next()) + offer = GrandExchangeOffer.fromQuery(res) + } + return offer + } + + @JvmStatic + fun getRecommendedPrice(itemID: Int, from_bot: Boolean = false): Int { + var base = PriceIndex.getValue(itemID) + if (from_bot) base = (max(BotPrices.getPrice(itemID), base) * 1.10).toInt() + return base + } + + @JvmStatic + fun getOfferStats(itemID: Int, sale: Boolean) : String + { + val sb = StringBuilder() + + GEDB.run { conn -> + var foundOffers = 0 + var totalAmount = 0 + var bestPrice = 0 + var stmt = conn.createStatement() + + if (!sale) { + var botAmt = 0 + var botPrice = 0 + val player_offers = stmt.executeQuery("SELECT * from player_offers where item_id = $itemID AND is_sale = 1 AND offer_state < 4 AND NOT offer_state = 2") + + while (player_offers.next()) { + val o = GrandExchangeOffer.fromQuery(player_offers) + ++foundOffers + totalAmount += o.amountLeft + if (o.offeredValue < bestPrice || bestPrice == 0) + bestPrice = o.offeredValue + } + + stmt.close() + stmt = conn.createStatement() + val bot_offers = stmt.executeQuery("SELECT * from bot_offers where item_id = $itemID") + if (bot_offers.next()) { + val o = GrandExchangeOffer.fromBotQuery(bot_offers) + botAmt = o.amount + botPrice = getRecommendedPrice(itemID, true) + } + + sb.append("Player Stock: $totalAmount ") + sb.append(" Lowest Price: $bestPrice
") + sb.append("-".repeat(50)) + sb.append("
Bot Stock: $botAmt ") + sb.append(" Bot Price: $botPrice") + } else { + val buy_offers = stmt.executeQuery("SELECT * from player_offers where item_id = $itemID AND is_sale = 0 AND offer_state < 4 AND NOT offer_state = 2") + + while (buy_offers.next()) { + val o = GrandExchangeOffer.fromQuery(buy_offers) + ++foundOffers + totalAmount += o.amountLeft + if (o.offeredValue > bestPrice) + bestPrice = o.offeredValue + } + + sb.append("Buy Offers: $totalAmount ") + sb.append("Highest Offer: $bestPrice") + } + + stmt.close() + } + + return sb.toString() + } + + fun addBotOffer(itemID: Int, amount: Int): Boolean + { + if (!PriceIndex.canTrade(itemID)) + return false + + // 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 + } + + fun dispatch(player: Player, offer: GrandExchangeOffer) : Boolean + { + if ( offer.amount < 1 ) + sendMessage(player, "You must choose the quantity you wish to buy!").also { return false } + + if ( offer.offeredValue < 1 ) + sendMessage(player, "You must choose the price you wish to buy for!").also { return false } + + if ( offer.offerState != OfferState.PENDING || offer.uid != 0L ) + { + log(this::class.java, Log.WARN, "[GE] DISPATCH FAILURE: ${offer.offerState.name}, UID: ${offer.uid}") + return false + } + + if ( player.isArtificial ) + offer.playerUID = PlayerDetails.getDetails("2009scape").uid.also { offer.isBot = true } + else + offer.playerUID = player.details.uid + + offer.offerState = OfferState.REGISTERED + //GrandExchangeRecords.getInstance(player).update(offer) + + if (offer.sell && !player.isArtificial) { + Repository.sendGrandExchangeNews( + player.username + " just offered " + offer.amount + " " + getItemName(offer.itemID) + " on the GE." + ) + } + + if (ServerConstants.I_AM_A_CHEATER) { + val otherO = GrandExchangeOffer() + otherO.itemID = offer.itemID + otherO.amount = offer.amount + otherO.sell = !offer.sell + otherO.offeredValue = offer.offeredValue + offer.writeNew() + GameWorld.Pulser.submit(object : Pulse(5) { + override fun pulse(): Boolean { + val offer2 = getOfferByUid(offer.uid) ?: return false + exchange(offer2, otherO) + return true + } + }) + return true + } + + pendingOffers.add(offer) + return true + } + + fun exchange(offer: GrandExchangeOffer, other: GrandExchangeOffer) + { + if(offer.sell == other.sell) return //Don't exchange if they are both buy/sell offers + val amount = Integer.min(offer.amount - offer.completedAmount, other.amount - other.completedAmount) + + if (amount == 0) return + + val seller = if(offer.sell) offer else other + val buyer = if(offer == seller) other else offer + + val sellerBias = seller.timeStamp > buyer.timeStamp + + //If the buyer is buying for less than the seller is selling for, don't exchange + if(seller.offeredValue > buyer.offeredValue) return + + seller.completedAmount += amount + buyer.completedAmount += amount + + seller.addWithdrawItem(995, amount * if(sellerBias) buyer.offeredValue else seller.offeredValue) + buyer.addWithdrawItem(seller.itemID, amount) + + if(!sellerBias) + buyer.addWithdrawItem(995, amount * (buyer.offeredValue - seller.offeredValue)) + + if(seller.amountLeft < 1) + seller.offerState = OfferState.COMPLETED + if(buyer.amountLeft < 1) + buyer.offerState = OfferState.COMPLETED + + val totalCoinXC = (if(sellerBias) buyer.offeredValue else seller.offeredValue) * amount + + seller.totalCoinExchange += totalCoinXC + buyer.totalCoinExchange += totalCoinXC + + if(canUpdatePriceIndex(seller, buyer)) + PriceIndex.addTrade(offer.itemID, amount, (totalCoinXC / amount)) + +/* + if (seller.amountLeft > 0) { + Discord.postOfferUpdate(true, seller.itemID, seller.offeredValue, seller.amountLeft) + } + + if (buyer.amountLeft > 0) { + Discord.postOfferUpdate(false, buyer.itemID, buyer.offeredValue, buyer.amountLeft) + } +*/ + + 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 + return true + } + + fun getValidOffers(): List + { + val offers = ArrayList() + + GEDB.run { conn -> + val stmt = conn.createStatement() + + val results = + stmt.executeQuery("SELECT * FROM player_offers WHERE offer_state < 4 AND NOT offer_state = 2") + while (results.next()) { + val o = GrandExchangeOffer.fromQuery(results) + offers.add(o) + } + stmt.close() + } + return offers + } + + fun getBotOffers(): List + { + val offers = ArrayList() + + GEDB.run { conn -> + val stmt = conn.createStatement() + + val results = stmt.executeQuery("SELECT item_id,amount FROM bot_offers WHERE amount > 0") + while (results.next()) { + val o = GrandExchangeOffer.fromBotQuery(results) + offers.add(o) + } + stmt.close() + } + 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() + } +} diff --git a/Server/src/main/core/game/ge/GrandExchangeOffer.kt b/Server/src/main/core/game/ge/GrandExchangeOffer.kt new file mode 100644 index 000000000..eee8594dd --- /dev/null +++ b/Server/src/main/core/game/ge/GrandExchangeOffer.kt @@ -0,0 +1,258 @@ +package core.game.ge + +import core.api.getAttribute +import core.cache.def.impl.ItemDefinition +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.context.GrandExchangeContext +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 + + +/** + * A struct holding all the data for grand exchange offers. + * + * @author Ceikry + */ + +class GrandExchangeOffer() { + var itemID = 0 + var completedAmount = 0 + var offeredValue = 0 + var index = 0 + var sell = false + var offerState = OfferState.PENDING + var uid: Long = 0 + var timeStamp: Long = 0 + var withdraw = arrayOfNulls(2) + var totalCoinExchange = 0 + var player: Player? = null + var playerUID = 0 + 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. + */ + val totalValue: Int + get() = offeredValue * amount + + /** + * Gets the amount of this item left to buy. + * @return The amount. + */ + val amountLeft: Int + get() = if (isBot) min(ServerConstants.BOTSTOCK_LIMIT, amount - completedAmount) else amount - completedAmount + + /** + * Checks if this offer is still active for dispatching. + * @return `True` if so. + */ + val isActive: Boolean + get() = offerState != OfferState.ABORTED && offerState != OfferState.PENDING && offerState != OfferState.COMPLETED && offerState != OfferState.REMOVED + + fun addWithdrawItem(id: Int, amount: Int) + { + if(amount == 0) return + //loop checking if the item is already present first + for(item in withdraw) + if(item != null && item.id == id) + { + item.amount += amount + return + } + + //if we make it to this point, the item was not present. Loop to find first null slot and stick item there. + for((index,item) in withdraw.withIndex()) + if(item == null) + { + withdraw[index] = Item(id, amount) + return + } + + //send container update packet to player if they exist (are online) + if ( player != null ) + visualize(player) + } + + fun visualize(player: Player?) + { + player ?: return + PacketRepository.send( + GrandExchangePacket::class.java, + GrandExchangeContext(player, index.toByte(), offerState.ordinal.toByte(), itemID.toShort(), + sell, offeredValue, amount, completedAmount, totalCoinExchange) + ) + PacketRepository.send(ContainerPacket::class.java, ContainerContext(player, -1, -1757, 523 + index, withdraw, false)) + } + + fun update() + { + GEDB.run { conn -> + if (isBot) { + val stmt = conn.prepareStatement("UPDATE bot_offers SET amount = ? WHERE item_id = ?") + stmt.setInt(1, amountLeft) + stmt.setInt(2, itemID) + stmt.executeUpdate() + stmt.close() + } else { + val stmt = conn.prepareStatement("UPDATE player_offers SET amount_complete = ?, offer_state = ?, total_coin_xc = ?, withdraw_items = ?, slot_index = ? WHERE uid = ?") + stmt.setInt(1, completedAmount) + stmt.setInt(2, offerState.ordinal) + stmt.setInt(3, totalCoinExchange) + stmt.setString(4, encodeWithdraw()) + stmt.setInt(5, index) + stmt.setLong(6, uid) + stmt.executeUpdate() + stmt.close() + } + } + } + + /** Called when writing a brand new offer to the database. Should not be used under any other circumstance **/ + fun writeNew() + { + GEDB.run { conn -> + if (isBot) { + val stmt = conn.createStatement() + val result = stmt.executeQuery("SELECT * from bot_offers where item_id = $itemID") + val isExists = result.next() + + if (isExists) { + val oldAmount = result.getInt("amount") + stmt.executeUpdate("UPDATE bot_offers set amount = ${oldAmount + amount} where item_id = $itemID") + } else + stmt.executeUpdate("INSERT INTO bot_offers(item_id,amount) values($itemID,$amount)") + stmt.close() + } else { + val stmt = conn.createStatement() + stmt.executeUpdate( + "INSERT INTO player_offers(player_uid, item_id, amount_total, offered_value, time_stamp, offer_state, is_sale, slot_index) " + + "values($playerUID,$itemID,$amount,$offeredValue,${System.currentTimeMillis()},${offerState.ordinal},${if (sell) 1 else 0}, $index)" + ) + val nowuid = stmt.executeQuery("SELECT last_insert_rowid()") + uid = nowuid.getLong(1) + GrandExchangeRecords.getInstance(player).offerRecords[index] = GrandExchangeRecords.OfferRecord(uid, index) + visualize(player) + stmt.close() + + Discord.postNewOffer(sell, itemID, offeredValue, amount, player?.username ?: return@run) + } + } + } + + private fun encodeWithdraw() : String + { + val sb = StringBuilder() + for((index, item) in withdraw.withIndex()) + { + sb.append(index) + sb.append(",") + if(item == null) + sb.append("null") + else + sb.append(item.id) + sb.append(",") + if(item == null) + sb.append("null") + else + sb.append(item.amount) + + if(index + 1 < withdraw.size) + sb.append(":") + } + + return sb.toString() + } + + override fun toString(): String { + return "[name=" + ItemDefinition.forId(itemID).name + ", itemId=" + itemID + ", amount=" + amount + ", completedAmount=" + completedAmount + ", offeredValue=" + offeredValue + ", index=" + index + ", sell=" + sell + ", state=" + offerState + ", withdraw=" + withdraw.contentToString() + ", totalCoinExchange=" + totalCoinExchange + ", playerUID=" + playerUID + "]" + } + + companion object { + fun fromQuery(result: ResultSet): GrandExchangeOffer + { + val o = GrandExchangeOffer() + o.itemID = result.getInt("item_id") + o.amount = result.getInt("amount_total") + o.completedAmount = result.getInt("amount_complete") + o.offeredValue = result.getInt("offered_value") + o.sell = result.getInt("is_sale") == 1 + o.offerState = OfferState.values()[result.getInt("offer_state")] + o.uid = result.getLong("uid") + o.timeStamp = result.getLong("time_stamp") + + val itemString = result.getString("withdraw_items") + if(itemString != null && itemString.isNotEmpty()) { + val items = itemString.split(":") + for (item in items) { + if(item.isEmpty()) continue + val tokens = item.split(",") + val index = tokens[0].toInt() + if (tokens[1] == "null") continue //Skip null slots + o.withdraw[index] = Item(tokens[1].toInt(), tokens[2].toInt()) + } + } + + o.totalCoinExchange = result.getInt("total_coin_xc") + o.playerUID = result.getInt("player_uid") + o.index = result.getInt("slot_index") + o.player = Repository.players.firstOrNull { it.details.accountInfo.uid == o.playerUID } + + return o + } + + fun fromBotQuery(result: ResultSet): GrandExchangeOffer + { + val o = GrandExchangeOffer() + o.sell = true + o.amount = result.getInt("amount") + o.offerState = OfferState.REGISTERED + o.itemID = result.getInt("item_id") + o.offeredValue = GrandExchange.getRecommendedPrice(o.itemID, true) + o.isBot = true + o.timeStamp = System.currentTimeMillis() + return o + } + + fun createBotOffer(itemId: Int, amount: Int, sale: Boolean = true) : GrandExchangeOffer + { + val o = GrandExchangeOffer() + o.sell = sale + o.itemID = itemId + o.amount = amount + o.offeredValue = GrandExchange.getRecommendedPrice(itemId, true) + o.offerState = OfferState.REGISTERED + o.isBot = true + 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 new file mode 100644 index 000000000..e970c01bb --- /dev/null +++ b/Server/src/main/core/game/ge/GrandExchangeRecords.kt @@ -0,0 +1,256 @@ +package core.game.ge + +import core.api.* +import core.cache.def.impl.ItemDefinition +import core.game.component.Component +import core.game.node.entity.player.Player +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.rs09.consts.Components +import core.tools.SystemLogger +import java.text.NumberFormat +import java.util.* + + +/** + * Handles the grand exchange interfaces for the player. + * + * @author Emperor + * @author Angle + */ + +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) + player.setAttribute("ge-records", instance) + } + + override fun parsePlayer(player: Player, data: JSONObject) { + /** + * Parse history from JSON + */ + val historyRaw = data["ge-history"] + if(historyRaw != null){ + val history = historyRaw as JSONArray + for (i in history.indices) { + val offer = history[i] as JSONObject + val o = GrandExchangeOffer() + o.itemID = offer["itemId"].toString().toInt() + o.sell = offer["isSell"] as Boolean + o.totalCoinExchange = (offer["totalCoinExchange"].toString().toInt()) + o.completedAmount = (offer["completedAmount"].toString().toInt()) + getInstance(player).history[i] = o + } + } + + /** + * Read offers from the database + */ + val needsIndex = ArrayDeque() + val instance = getInstance(player) + + GEDB.run { conn -> + val stmt = conn.createStatement() + val offer_records = stmt.executeQuery("SELECT * from player_offers where player_uid = ${player.details.uid} AND offer_state < 6") + + while (offer_records.next()) { + val offer = GrandExchangeOffer.fromQuery(offer_records) + if (offer.index == -1) //used to index old (converted from JSON) offers + needsIndex.push(offer) + else + instance.offerRecords[offer.index] = OfferRecord(offer.uid, offer.index) + } + stmt.close() + } + + if (needsIndex.isNotEmpty()) { + for ((index, offer) in offerRecords.withIndex()) { + if (offer == null) { + val o = needsIndex.pop() + o.index = index + instance.offerRecords[o.index] = OfferRecord(o.uid, o.index) + o.update() //write the new index to the database + } + } + + while(needsIndex.isNotEmpty()) //If we enter this loop, there were more offers than can fit inside the 6 slots. This should never happen - at least, not anymore. Can't speak for JSON offers. + { + val o = needsIndex.pop() + SystemLogger.logGE("[WARN] PLAYER HAD EXTRA OFFER - RECOMMEND IMMEDIATE REFUND OF CONTENTS -> OFFER UID: ${o.uid}") + SystemLogger.logGE("[WARN] AS PER ABOVE MESSAGE, REFUND CONTENTS OF OFFER AND MANUALLY SET offer_state = 6") + } + } + + instance.init() + } + + override fun savePlayer(player: Player, save: JSONObject) { + /** + * Save history to JSON + */ + val history = JSONArray() + getInstance(player).history.map { + if(it != null){ + val historyEntry = JSONObject() + historyEntry["isSell"] = it.sell + historyEntry["itemId"] = it.itemID.toString() + historyEntry["totalCoinExchange"] = it.totalCoinExchange.toString() + historyEntry["completedAmount"] = it.completedAmount.toString() + history.add(historyEntry) + } + } + save["ge-history"] = history + } + + /** + * Opens the collection box. + */ + fun openCollectionBox() { + if (!player!!.bankPinManager.isUnlocked) { + player.bankPinManager.openType(3) + return + } + player.interfaceManager.openComponent(Components.STOCKCOLLECT_109) + player.packetDispatch.sendIfaceSettings(6, 18, 109, 0, 2) + player.packetDispatch.sendIfaceSettings(6, 23, 109, 0, 2) + player.packetDispatch.sendIfaceSettings(6, 28, 109, 0, 2) + player.packetDispatch.sendIfaceSettings(6, 36, 109, 0, 2) + player.packetDispatch.sendIfaceSettings(6, 44, 109, 0, 2) + player.packetDispatch.sendIfaceSettings(6, 52, 109, 0, 2) + + visualizeRecords() + } + + fun visualizeRecords() + { + GEDB.run { conn -> + val stmt = conn.createStatement() + + for (record in offerRecords) { + if (record != null) { + val offer_raw = stmt.executeQuery("select * from player_offers where uid = ${record.uid}") + if (offer_raw.next()) { + val offer = GrandExchangeOffer.fromQuery(offer_raw) + if (offer.offerState == OfferState.REMOVED) continue + offer.index = record.slot + offer.visualize(player) + } + } + } + stmt.close() + } + } + + fun getOffer(index: Int) : GrandExchangeOffer? + { + if(index == -1) return getOffer(null) + return getOffer(offerRecords[index]) + } + + fun getOffer(record: OfferRecord?) : GrandExchangeOffer? + { + record ?: return null + var offerToReturn: GrandExchangeOffer? = null + GEDB.run { conn -> + val stmt = conn.createStatement() + val offer_raw = stmt.executeQuery("select * from player_offers where uid = ${record.uid}") + if (offer_raw.next()) { + val offer = GrandExchangeOffer.fromQuery(offer_raw) + offer.index = record.slot + stmt.close() + offerToReturn = offer + } + stmt.close() + } + return offerToReturn + } + + /** + * Opens the history log. + * + * @param p The player to open it for. + */ + fun openHistoryLog(p: Player) { + p.interfaceManager.open(Component(643)) + for (i in history.indices) { + val o: GrandExchangeOffer? = history[i] + if (o == null) { + for (j in 0..3) { + p.packetDispatch.sendString("-", 643, 25 + i + j * 5) + } + continue + } + p.packetDispatch.sendString(if (o.sell) "You sold" else "You bought", 643, 25 + i) + p.packetDispatch.sendString( + NumberFormat.getNumberInstance(Locale.US).format(o.completedAmount.toLong()), + 643, + 30 + i + ) + p.packetDispatch.sendString(ItemDefinition.forId(o.itemID).name, 643, 35 + i) + p.packetDispatch.sendString( + NumberFormat.getNumberInstance(Locale.US).format(o.totalCoinExchange.toLong()) + " gp", 643, 40 + i + ) + } + } + + /** + * Initializes the grand exchange. + */ + fun init() { + // Were trades made while gone? + var updated = false + for (record in offerRecords) { + if (record != null) { + val offer = getOffer(record) ?: continue + if (!updated && (offer.withdraw[0] != null || offer.withdraw[1] != null)) { + updated = true + } + offer.visualize(player) + } + } + if (updated) { + sendMessage(player!!, "You have items waiting in your Grand Exchange collection box!") + playJingle(player, 284) + } + } + + fun hasActiveOffer(): Boolean { + for (i in offerRecords) { + if (i != null) + return true + } + return false + } + + /** + * Formats the grand exchange. + * + * @return the formatted offer for the SQL database. + */ + fun format(): String? { + var log = "" + for (record in offerRecords) { + if (record != null) { + val offer = getOffer(record) ?: continue + log += offer.itemID.toString() + "," + offer.amount + "," + offer.sell + "|" + } + } + if (log.isNotEmpty() && log[log.length - 1] == '|') { + log = log.substring(0, log.length - 1) + } + return log + } + + data class OfferRecord(val uid: Long, val slot: Int) + + companion object { + @JvmStatic fun getInstance(player: Player? = null): GrandExchangeRecords + { + return player?.getAttribute("ge-records", GrandExchangeRecords()) ?: GrandExchangeRecords() + } + } +} 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/java/core/game/ge/OfferState.java b/Server/src/main/core/game/ge/OfferState.java similarity index 100% rename from Server/src/main/java/core/game/ge/OfferState.java rename to Server/src/main/core/game/ge/OfferState.java diff --git a/Server/src/main/core/game/ge/PriceIndex.kt b/Server/src/main/core/game/ge/PriceIndex.kt new file mode 100644 index 000000000..5acf81384 --- /dev/null +++ b/Server/src/main/core/game/ge/PriceIndex.kt @@ -0,0 +1,137 @@ +package core.game.ge + +import core.api.itemDefinition +import java.sql.ResultSet + +object PriceIndex { + @JvmStatic fun canTrade(id: Int): Boolean { + var canTrade = false + + GEDB.run { conn -> + val stmt = conn.prepareStatement(EXISTS_QUERY) + stmt.setInt(1, id) + val res = stmt.executeQuery() + if (res.next()) { + canTrade = res.getInt(1) == 1 + } + } + + return canTrade + } + + fun banItem(id: Int) { + GEDB.run { conn -> + val stmt = conn.prepareStatement(REMOVE_QUERY) + stmt.setInt(1, id) + stmt.execute() + } + } + + fun allowItem(id: Int) { + if(canTrade(id)) return + + GEDB.run { conn -> + val stmt = conn.prepareStatement(INSERT_QUERY) + stmt.setInt(1, id) + stmt.setInt(2, itemDefinition(id).getAlchemyValue(true)) + stmt.setInt(3, itemDefinition(id).getAlchemyValue(true)) + stmt.setInt(4, 0) + stmt.setInt(5, 0) + stmt.execute() + } + } + + fun getValue(id: Int): Int { + var value = itemDefinition(id).getAlchemyValue(true) + GEDB.run { conn -> + val stmt = conn.prepareStatement(GET_VALUE_QUERY) + stmt.setInt(1, id) + val res = stmt.executeQuery() + if (res.next()) { + value = res.getInt(1) + } + } + return value + } + + fun addTrade(id: Int, amount: Int, pricePerUnit: Int) { + val oldInfo = getInfo(id) ?: return + val newInfo = oldInfo.copy() + + val volumeResetThreshold = + if (pricePerUnit >= 1000000) 500 + else if (pricePerUnit >= 100000) 1000 + else if (pricePerUnit >= 25000) 2500 + else 10000 + + newInfo.totalValue += (amount * pricePerUnit) + newInfo.uniqueTrades += amount + newInfo.lastUpdate = System.currentTimeMillis() + newInfo.currentValue = (newInfo.totalValue / newInfo.uniqueTrades).toInt() + + if (newInfo.uniqueTrades > volumeResetThreshold) { + val newAmt = (0.1 * volumeResetThreshold).toInt() + newInfo.uniqueTrades = newAmt + newInfo.totalValue = newAmt.toLong() * newInfo.currentValue + } + + updateInfo(newInfo) + } + + private fun updateInfo(newInfo: PriceInfo) { + GEDB.run { conn -> + val stmt = conn.prepareStatement(UPDATE_QUERY) + stmt.setInt(1, newInfo.currentValue) + stmt.setLong(2, newInfo.totalValue) + stmt.setInt(3, newInfo.uniqueTrades) + stmt.setLong(4, newInfo.lastUpdate) + stmt.setInt(5, newInfo.itemId) + stmt.execute() + } + } + + private fun getInfo(id: Int): PriceInfo? { + var priceInfo: PriceInfo? = null + + GEDB.run { conn -> + val stmt = conn.prepareStatement(SELECT_QUERY) + stmt.setInt(1, id) + val res = stmt.executeQuery() + if (res.next()) { + priceInfo = PriceInfo.fromQuery(res) + } + } + + return priceInfo + } + + const val SELECT_QUERY = "SELECT * FROM price_index WHERE item_id = ?;" + const val UPDATE_QUERY = "UPDATE price_index SET value = ?, total_value = ?, unique_trades = ?, last_update = ? WHERE item_id = ?;" + const val EXISTS_QUERY = "SELECT EXISTS(SELECT 1 FROM price_index WHERE item_id = ?);" + const val REMOVE_QUERY = "DELETE FROM price_index WHERE item_id = ?;" + const val INSERT_QUERY = "INSERT INTO price_index (item_id, value, total_value, unique_trades, last_update) VALUES (?,?,?,?,?);" + const val GET_VALUE_QUERY = "SELECT value FROM price_index WHERE item_id = ?;" +} + +data class PriceInfo( + var itemId: Int, + var currentValue: Int, + var totalValue: Long, + var uniqueTrades: Int, + var lastUpdate: Long +) { + fun copy() : PriceInfo { + return PriceInfo(itemId, currentValue, totalValue, uniqueTrades, lastUpdate) + } + companion object { + fun fromQuery(result: ResultSet) : PriceInfo { + return PriceInfo( + result.getInt(1), + result.getInt(2), + result.getLong(3), + result.getInt(4), + result.getLong(5) + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/Skillcape.java b/Server/src/main/core/game/global/Skillcape.java similarity index 75% rename from Server/src/main/java/core/game/content/global/Skillcape.java rename to Server/src/main/core/game/global/Skillcape.java index 0b335eeec..4ae5ef8a5 100644 --- a/Server/src/main/java/core/game/content/global/Skillcape.java +++ b/Server/src/main/core/game/global/Skillcape.java @@ -1,7 +1,9 @@ -package core.game.content.global; +package core.game.global; +import content.global.skill.construction.decoration.pohstorage.StorableFamily; import core.game.container.Container; -import core.game.content.dialogue.FacialExpression; +import core.game.dialogue.FacialExpression; +import core.game.dialogue.FacialExpression; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -65,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/java/core/game/content/global/action/ClimbActionHandler.java b/Server/src/main/core/game/global/action/ClimbActionHandler.java similarity index 81% rename from Server/src/main/java/core/game/content/global/action/ClimbActionHandler.java rename to Server/src/main/core/game/global/action/ClimbActionHandler.java index 8e2d403fa..1914c2440 100644 --- a/Server/src/main/java/core/game/content/global/action/ClimbActionHandler.java +++ b/Server/src/main/core/game/global/action/ClimbActionHandler.java @@ -1,13 +1,10 @@ -package core.game.content.global.action; +package core.game.global.action; -import core.game.container.impl.EquipmentContainer; -import org.rs09.consts.Items; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.content.dialogue.DialoguePlugin; +import core.game.dialogue.DialoguePlugin; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.RegionManager; @@ -80,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; } @@ -198,45 +197,6 @@ public final class ClimbActionHandler { for (String message : messages) { player.getPacketDispatch().sendMessage(message); } - - // Achievement Diary Handlers - // falador climb to top of white knight's castle - player.debug(destination.toString()); - if ((destination.equals(2959, 3338, 3) || destination.equals(2983, 3350, 3) || destination.equals(2995, 3341, 3))) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 0, 3); - } - // falador dark wizards tower proselyte - if (destination.equals(2908, 3336, 2) - && player.getEquipment().get(EquipmentContainer.SLOT_HAT).getId() == Items.PROSELYTE_SALLET_9672 - && player.getEquipment().get(EquipmentContainer.SLOT_CHEST).getId() == Items.PROSELYTE_HAUBERK_9674 - && player.getEquipment().get(EquipmentContainer.SLOT_LEGS).getId() == Items.PROSELYTE_CUISSE_9676) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 0); - } - // falador enter the mining guild - if (destination.equals(3021, 9739, 0)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 6); - } - - // Climb to the highest point in Lumbridge - if (destination.equals(3207, 3222, 3)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 0); - } - - // Climb to the top of the Wizards' Tower - if (destination.equals(3104, 3161, 2)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 11); - } - - // Enter Edgeville Dungeon using the entrance to the south of

Edgeville - if (destination.equals(3096, 9867, 0)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 10); - } - - // Find the highest point in Varrock - if (destination.equals(3206, 3497, 3) || destination.equals(3223, 3472, 3) || destination.equals(3258, 3486, 3)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 18); - } - return true; } }); diff --git a/Server/src/main/java/core/game/content/global/action/DigAction.java b/Server/src/main/core/game/global/action/DigAction.java similarity index 84% rename from Server/src/main/java/core/game/content/global/action/DigAction.java rename to Server/src/main/core/game/global/action/DigAction.java index 7a49b68f7..3c47ef614 100644 --- a/Server/src/main/java/core/game/content/global/action/DigAction.java +++ b/Server/src/main/core/game/global/action/DigAction.java @@ -1,4 +1,4 @@ -package core.game.content.global.action; +package core.game.global.action; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/content/global/action/DigSpadeHandler.java b/Server/src/main/core/game/global/action/DigSpadeHandler.java similarity index 79% rename from Server/src/main/java/core/game/content/global/action/DigSpadeHandler.java rename to Server/src/main/core/game/global/action/DigSpadeHandler.java index 4ea41bdd3..31735e452 100644 --- a/Server/src/main/java/core/game/content/global/action/DigSpadeHandler.java +++ b/Server/src/main/core/game/global/action/DigSpadeHandler.java @@ -1,16 +1,20 @@ -package core.game.content.global.action; +package core.game.global.action; import core.game.node.entity.player.Player; -import rs09.game.interaction.SpadeDigListener; -import rs09.game.system.SystemLogger; +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 rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; 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 @@ -62,7 +66,7 @@ public final class DigSpadeHandler { */ public static boolean register(Location location, DigAction action) { if (ACTIONS.containsKey(location)) { - SystemLogger.logErr("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/java/core/game/content/global/action/DoorActionHandler.java b/Server/src/main/core/game/global/action/DoorActionHandler.java similarity index 85% rename from Server/src/main/java/core/game/content/global/action/DoorActionHandler.java rename to Server/src/main/core/game/global/action/DoorActionHandler.java index 47202eb02..02234e136 100644 --- a/Server/src/main/java/core/game/content/global/action/DoorActionHandler.java +++ b/Server/src/main/core/game/global/action/DoorActionHandler.java @@ -1,21 +1,24 @@ -package core.game.content.global.action; +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; import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; import core.game.system.task.Pulse; 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 rs09.game.system.config.DoorConfigLoader; -import rs09.game.world.GameWorld; +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.*; @@ -41,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); @@ -52,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; @@ -66,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()); @@ -94,20 +115,30 @@ 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(); - entity.addExtension(LogoutTask.class, new LocationLogoutTask(4, loc)); - object.setCharge(IN_USE_CHARGE); - if (second != null) { - second.setCharge(IN_USE_CHARGE); - } 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; + }); } GameWorld.getPulser().submit(new Pulse(1) { boolean opened = false; - @Override public boolean pulse() { if (!opened) { @@ -115,12 +146,14 @@ public final class DoorActionHandler { entity.getWalkingQueue().reset(); entity.getWalkingQueue().addPath(endLocation.getX(), endLocation.getY()); opened = true; + + // Set the door in_use + object.setCharge(IN_USE_CHARGE); + if (second != null) { + second.setCharge(IN_USE_CHARGE); + } return false; } - object.setCharge(1000); - if (second != null) { - second.setCharge(1000); - } if (entity instanceof Player) { Player player = entity.asPlayer(); if (object.getId() == 2112 && player.getLocation().withinDistance(new Location(3046, 9756, 0), 10)) { @@ -136,6 +169,14 @@ public final class DoorActionHandler { if (object.getId() == 2406 && player.getLocation().withinDistance(Location.create(3202,3169,0))) { player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 6); } + + entity.asPlayer().logoutListeners.remove("autowalk"); + } + + // Reset door to inactive + object.setCharge(1000); + if (second != null) { + second.setCharge(1000); } return true; } @@ -375,7 +416,13 @@ public final class DoorActionHandler { } entity.lock(4); final Location loc = entity.getLocation(); - entity.addExtension(LogoutTask.class, new LocationLogoutTask(4, loc)); + if(entity instanceof Player) + { + entity.asPlayer().logoutListeners.put("autowalk", player -> { + player.setLocation(loc); + return Unit.INSTANCE; + }); + } object.setCharge(IN_USE_CHARGE); second.setCharge(IN_USE_CHARGE); GameWorld.getPulser().submit(new Pulse(1) { @@ -391,6 +438,10 @@ public final class DoorActionHandler { opened = true; return false; } + if(entity instanceof Player) + { + entity.asPlayer().logoutListeners.remove("autowalk"); + } object.setCharge(1000); if (second != null) { second.setCharge(1000); @@ -495,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/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 new file mode 100644 index 000000000..29299e238 --- /dev/null +++ b/Server/src/main/core/game/global/action/EquipHandler.kt @@ -0,0 +1,149 @@ +package core.game.global.action + +import core.game.event.ItemEquipEvent +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 +import core.game.node.entity.player.link.audio.Audio +import core.plugin.Plugin +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. + * @author Ceikry + * @author Woah + */ +class EquipHandler : InteractionListener { + + override fun defineListeners() { + on(IntType.ITEM, "equip", "wield", "wear") { player, node -> + handleEquip(player, node) + return@on true + } + } + + fun handleEquip(player: Player, node: Node) { + val item = node.asItem() + val itemEquipmentSlot = item.definition.getConfiguration(ItemConfigParser.EQUIP_SLOT, -1) + + 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) + if (bool != true) { + return + } + } + if (!InteractionListeners.run(node.id, player, item, true)) { + return + } + + val lock = player.locks.equipmentLock + if (lock != null && lock.isLocked) { + if (lock.message != null) { + player.packetDispatch.sendMessage(lock.message) + } + return + } + + if (player.equipment.add(item, item.slot, true, true)) { + //check if a brawling glove is being equipped and register it + if (item.id in 13845..13857) { + player.debug("Registering gloves... ID: " + item.id) + BrawlingGlovesManager.getInstance(player).registerGlove(item.id) + } + + player.dialogueInterpreter.close() + + /* TODO: Send different equip sound based on what is being equip.*/ + playAudio(player, item.definition.getConfiguration(ItemConfigParser.EQUIP_AUDIO, 2244)) + + if (player.properties.autocastSpell != null) { + + 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)) { + SlayerEquipmentFlags.updateFlags(player) + } + + player.dispatch(ItemEquipEvent(item.id, item.slot)) + } + } + + companion object { + /** + * Unequips an item. + * @param player the player. + * @param slot the slot. + * @param itemId the item id. + */ + @JvmStatic + fun unequip(player: Player, slot: Int, itemId: Int) { + if (slot < 0 || slot > 13) { + return + } + val item = player.equipment[slot] ?: return + val lock = player.locks.equipmentLock + if (lock != null && lock.isLocked) { + if (lock.message != null) { + player.packetDispatch.sendMessage(lock.message) + } + return + } + if (slot == EquipmentContainer.SLOT_WEAPON) { + player.packetDispatch.sendString("", 92, 0) + } + val maximumAdd = player.inventory.getMaximumAdd(item) + if (maximumAdd < item.amount) { + player.packetDispatch.sendMessage("Not enough free space in your inventory.") + return + } + val plugin = item.definition.getConfiguration>("equipment", null) + if (plugin != null) { + if (!(plugin.fireEvent("unequip", player, item) as Boolean)) { + return + } + } + if (!InteractionListeners.run(itemId, player, item, false)) { + return + } + if (player.equipment.remove(item)) { + /* TODO: Send different unequip sound based on what is being unequipped.*/ + playAudio(player, Sounds.EQUIP_FUN_2238) + player.dialogueInterpreter.close() + player.inventory.add(item) + + player.dispatch(ItemUnequipEvent(itemId, slot)) + } + + if (SlayerEquipmentFlags.isSlayerEq(item.id)) { + SlayerEquipmentFlags.updateFlags(player) + } + } + } +} diff --git a/Server/src/main/java/core/game/content/global/action/LadderAchievementCheck.java b/Server/src/main/core/game/global/action/LadderAchievementCheck.java similarity index 79% rename from Server/src/main/java/core/game/content/global/action/LadderAchievementCheck.java rename to Server/src/main/core/game/global/action/LadderAchievementCheck.java index 30d893514..9f8e6b57a 100644 --- a/Server/src/main/java/core/game/content/global/action/LadderAchievementCheck.java +++ b/Server/src/main/core/game/global/action/LadderAchievementCheck.java @@ -1,4 +1,4 @@ -package core.game.content.global.action; +package core.game.global.action; import core.game.node.entity.player.Player; diff --git a/Server/src/main/core/game/global/action/PickupHandler.kt b/Server/src/main/core/game/global/action/PickupHandler.kt new file mode 100644 index 000000000..ec42b1858 --- /dev/null +++ b/Server/src/main/core/game/global/action/PickupHandler.kt @@ -0,0 +1,107 @@ +package core.game.global.action + +import core.game.event.PickUpEvent +import core.api.getItemName +import content.data.GodType +import core.game.node.entity.player.Player +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 +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.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. + * @author 'Vexia + */ +object PickupHandler { + /** + * Method used to take a ground item. + * @param player the player. + * @param item the item. + * @return `True` if taken. + */ + @JvmStatic + fun take(player: Player, item: GroundItem): Boolean { + if (item.location == null) { + player.packetDispatch.sendMessage("Invalid ground item!") + return true + } + if (!GroundItemManager.getItems().contains(item)) { + player.packetDispatch.sendMessage("Too late!") + return true + } + if (player.getAttribute("droppedItem:" + item.id, 0) > GameWorld.ticks) { //Splinter + return true + } + if (item !is GroundSpawnLoader.GroundSpawn && item.isRemainPrivate && !item.droppedBy(player)) { + player.sendMessage("You can't take that item!") + return true + } + val add = Item(item.id, item.amount, item.charge) + if (!player.inventory.hasSpaceFor(add)) { + player.packetDispatch.sendMessage("You don't have enough inventory space to hold that item.") + return true + } + if (!canTake(player, item, 0)) { + return true + } + if (item.isActive && player.inventory.add(add)) { + if (item.dropper is Player && item.dropper.details.uid != player.details.uid){ + PlayerMonitor.log(item.dropper, LogType.DROP_TRADE, "${getItemName(item.id)} x${item.amount} picked up by ${player.name}.") + } + if (!RegionManager.isTeleportPermitted(item.location)) { + player.animate(Animation.create(535)) + } + GroundItemManager.destroy(item) +/* if (item.dropper?.isArtificial == true) { + getItems(item.dropper)?.remove(item) + } + */ + + playAudio(player, Sounds.PICK2_2582) + player.dispatch(PickUpEvent(item.id)) + } + return true + } + + /** + * Checks if the player can take an item. + * @param player the player. + * @param item the item. + * @param type the type (1= ground, 2=telegrab) + * @return `True` if so. + */ + @JvmStatic + fun canTake(player: Player, item: GroundItem, type: Int): Boolean { + if (item.dropper != null && !item.droppedBy(player) && player.ironmanManager.checkRestriction()) { + return false + } + if (item.id == 8858 || item.id == 8859) { + player.dialogueInterpreter.sendDialogues(4300, core.game.dialogue.FacialExpression.FURIOUS, "Hey! You can't take that, it's guild property. Take one", "from the pile.") + return false + } + if (GodType.forCape(item) != null) { + if (GodType.hasAny(player)) { + player.sendMessages("You may only possess one sacred cape at a time.", "The conflicting powers of the capes drive them apart.") + return false + } + } + if (RunePouch.forItem(item) != null) { + if (player.hasItem(item)) { + player.sendMessage("A mystical force prevents you from picking up the pouch.") + return false + } + } + return if (item.hasItemPlugin()) { + item.plugin.canPickUp(player, item, type) + } else true + } +} diff --git a/Server/src/main/core/game/global/action/SpecialLadders.java b/Server/src/main/core/game/global/action/SpecialLadders.java new file mode 100644 index 000000000..532bd896a --- /dev/null +++ b/Server/src/main/core/game/global/action/SpecialLadders.java @@ -0,0 +1,130 @@ +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; + +public enum SpecialLadders implements LadderAchievementCheck { + HAM_STORAGE_UP(new Location(2567,5185,0), new Location(3166,9623,0)), + GEM_MINE(new Location(2821,2996,0), new Location(2838,9387,0)), + GEM_MINE_UP(new Location(2838,9388,0), new Location(2820, 2996, 0)), + BEAR_CAGE_UP(Location.create(3230, 9904, 0),Location.create(3231, 3504, 0)), + BEAR_CAGE_DOWN(Location.create(3230,3508,0),Location.create(3229, 9904, 0)), + GLARIAL_EXIT(Location.create(2556,9844,0),Location.create(2557,3444,0)), + SWENSEN_DOWN(Location.create(2644, 3657, 0), Location.create(2631, 10006, 0)), + 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_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) { + player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR,1,11); + } + }, + 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) { + player.getAchievementDiaryManager().finishTask(player,DiaryType.SEERS_VILLAGE,1,3); + } + }, + SEERS_VILLAGE_SPINNING_HOUSE_ROOFTOP_DOWN(new Location(2715,3472,3), new Location(2714,3472,1)); + + private static HashMap destinationMap = new HashMap<>(); + private static HashMap ladderMap = new HashMap<>(); + static { + Arrays.stream(SpecialLadders.values()).forEach(entry -> { + destinationMap.putIfAbsent(entry.ladderLoc,entry.destLoc); + }); + Arrays.stream(SpecialLadders.values()).forEach(entry -> { + ladderMap.putIfAbsent(entry.ladderLoc, entry); + }); + } + + private Location ladderLoc,destLoc; + SpecialLadders(Location ladderLoc, Location destLoc){ + this.ladderLoc = ladderLoc; + this.destLoc = destLoc; + } + + public static void add(Location from, Location to){ + destinationMap.put(from,to); + } + + public static Location getDestination(Location loc){ + return destinationMap.get(loc); + } + public static SpecialLadders getSpecialLadder(Location loc) { + return ladderMap.get(loc); + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/presets/Preset.java b/Server/src/main/core/game/global/presets/Preset.java similarity index 93% rename from Server/src/main/java/core/game/content/global/presets/Preset.java rename to Server/src/main/core/game/global/presets/Preset.java index b9806bc1a..4be95485e 100644 --- a/Server/src/main/java/core/game/content/global/presets/Preset.java +++ b/Server/src/main/core/game/global/presets/Preset.java @@ -1,4 +1,4 @@ -package core.game.content.global.presets; +package core.game.global.presets; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/content/global/presets/PresetManager.java b/Server/src/main/core/game/global/presets/PresetManager.java similarity index 96% rename from Server/src/main/java/core/game/content/global/presets/PresetManager.java rename to Server/src/main/core/game/global/presets/PresetManager.java index 59726bce3..a5fa057ac 100644 --- a/Server/src/main/java/core/game/content/global/presets/PresetManager.java +++ b/Server/src/main/core/game/global/presets/PresetManager.java @@ -1,4 +1,4 @@ -package core.game.content.global.presets; +package core.game.global.presets; import core.game.node.entity.player.Player; import core.game.node.item.Item; diff --git a/Server/src/main/java/core/game/content/global/report/AbuseReport.java b/Server/src/main/core/game/global/report/AbuseReport.java similarity index 82% rename from Server/src/main/java/core/game/content/global/report/AbuseReport.java rename to Server/src/main/core/game/global/report/AbuseReport.java index 2a6c2f50b..dee08a06f 100644 --- a/Server/src/main/java/core/game/content/global/report/AbuseReport.java +++ b/Server/src/main/core/game/global/report/AbuseReport.java @@ -1,6 +1,8 @@ -package core.game.content.global.report; +package core.game.global.report; import core.game.node.entity.player.Player; +import core.integrations.discord.Discord; +import core.game.system.command.CommandMapping; /** * Represents an abuse report to file. @@ -45,7 +47,11 @@ public final class AbuseReport { * @param player the player. */ public void construct(Player player, boolean mute) { + if (mute) { + CommandMapping.INSTANCE.get("mute").attemptHandling(player, new String[] {"mute", victim, "48h"}); + } player.getPacketDispatch().sendMessage("Thank-you, your abuse report has been received."); + Discord.postPlayerAlert(victim, "Abuse Report - " + rule.name()); } /** diff --git a/Server/src/main/java/core/game/content/global/report/Rule.java b/Server/src/main/core/game/global/report/Rule.java similarity index 96% rename from Server/src/main/java/core/game/content/global/report/Rule.java rename to Server/src/main/core/game/global/report/Rule.java index e340e92df..b11c3a54a 100644 --- a/Server/src/main/java/core/game/content/global/report/Rule.java +++ b/Server/src/main/core/game/global/report/Rule.java @@ -1,4 +1,4 @@ -package core.game.content.global.report; +package core.game.global.report; import core.game.node.entity.player.Player; 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/java/core/game/interaction/DestinationFlag.java b/Server/src/main/core/game/interaction/DestinationFlag.java similarity index 98% rename from Server/src/main/java/core/game/interaction/DestinationFlag.java rename to Server/src/main/core/game/interaction/DestinationFlag.java index cbd44f039..a5d9a0165 100644 --- a/Server/src/main/java/core/game/interaction/DestinationFlag.java +++ b/Server/src/main/core/game/interaction/DestinationFlag.java @@ -1,6 +1,6 @@ package core.game.interaction; -import core.game.content.global.action.DoorActionHandler; +import core.game.global.action.DoorActionHandler; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.scenery.Scenery; @@ -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/IntType.kt b/Server/src/main/core/game/interaction/IntType.kt new file mode 100644 index 000000000..6fbee0041 --- /dev/null +++ b/Server/src/main/core/game/interaction/IntType.kt @@ -0,0 +1,9 @@ +package core.game.interaction + +enum class IntType { + ITEM, + SCENERY, + NPC, + GROUNDITEM, + PLAYER +} \ No newline at end of file diff --git a/Server/src/main/core/game/interaction/InteractPlugin.java b/Server/src/main/core/game/interaction/InteractPlugin.java new file mode 100644 index 000000000..d5057980e --- /dev/null +++ b/Server/src/main/core/game/interaction/InteractPlugin.java @@ -0,0 +1,346 @@ +package core.game.interaction; + +import core.game.event.InteractionEvent; +import core.game.container.Container; +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.node.item.Item; +import core.game.node.scenery.Scenery; +import core.game.system.task.Pulse; +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 InteractPlugin { + + /** + * The current options. + */ + private Option[] options = new Option[8]; + + /** + * The player. + */ + private final Node node; + + /** + * If the interaction option handlers have been initialized. + */ + private boolean initialized; + + /** + * Constructs a new {@code Interaction} {@code Object}. + * @param node The node reference. + */ + public InteractPlugin(Node node) { + this.node = node; + } + + /** + * Sends the current option. + * @param node The node. + * @param index The index. + * @param name The option name. + */ + public static void sendOption(Node node, int index, String name) { + if (!(node instanceof Player)) { + return; + } + PacketRepository.send(InteractionOption.class, new InteractionOptionContext((Player) node, index, name)); + } + + /** + * Handles an interaction option being clicked. + * @param player The player using the option. + * @param option The option used. + */ + public void handle(final Player player, final Option option) { + try { + if (player.getLocks().isInteractionLocked() || option == null) { + return; + } + player.debug("Received interaction request " + option.getName()); + boolean hasHandler = option.getHandler() != null; + boolean walk = hasHandler && option.getHandler().isWalk(); + if (!walk && hasHandler && option.getHandler().isWalk(player, node)) { + walk = true; + } + if (!hasHandler || walk) { + handleWalkOption(player, option, PulseType.STANDARD); + } else if (hasHandler) { + player.debug("Option handler being used=" + option.getHandler().getClass().getSimpleName()); + handleDefaultOption(player, option, PulseType.STANDARD); + } else { + player.getPulseManager().runUnhandledAction(player, PulseType.STANDARD); + } + player.dispatch(new InteractionEvent(node, option.getName().toLowerCase())); + } catch (Exception e){ + e.printStackTrace(); + log(this.getClass(), Log.ERR, this.getClass().getName() + e.getMessage()); + } + } + + /** + * Handles an item option. + * @param player The player. + * @param option The option. + * @param container The container the item is in. + */ + public void handleItemOption(final Player player, final Option option, final Container container) { + if (player.getLocks().isInteractionLocked()) { + return; + } + player.getPulseManager().clear(PulseType.STANDARD); + GameWorld.getPulser().submit(new Pulse(1, player) { + @Override + public boolean pulse() { + try { + if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { + return true; + } + if(InteractionListeners.run(node.getId(), IntType.ITEM, option.getName(),player,node)){ + return true; + } + if (option.getHandler() == null || !option.getHandler().handle(player, node, option.getName().toLowerCase())) { + player.getPacketDispatch().sendMessage("Nothing interesting happens."); + } + if (option.getHandler() != null) { + player.debug("Using item handler " + option.getHandler().getClass().getSimpleName()); + } + } catch (Exception e){ + e.printStackTrace(); + log(this.getClass(), Log.ERR, this.getClass().getName() + e.getMessage()); + } + return true; + } + }); + } + + /** + * Handles an invalid interaction. + * @param player The player. + * @param node The node to interact with. + */ + public static void handleInvalidInteraction(final Player player, final Node node, final Option option) { + if (node == null) { + return; + } + if (node.getLocation() != null) { + if (player.getLocks().isMovementLocked()) { + return; + } + player.getPulseManager().run(new MovementPulse(player, node) { + @Override + public boolean pulse() { + try { + 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(); + log(this.getClass(), Log.ERR, this.getClass().getName() + e.getMessage()); + } + return true; + } + }, PulseType.STANDARD); + } else { + player.getPulseManager().runUnhandledAction(player, PulseType.STANDARD); + } + } + + /** + * Handles an option requiring the player to walk to the node. + * @param player The player. + * @param option The option. + * @param pulseType The pulse type. + */ + private void handleWalkOption(final Player player, final Option option, PulseType pulseType) { + if (node.getLocation() == null) { + player.getPulseManager().runUnhandledAction(player, pulseType); + return; + } + if (player.getLocks().isMovementLocked()) { + player.getPulseManager().clear(pulseType); + return; + } + player.getPulseManager().run(new MovementPulse(player, node, option.getHandler()) { + @Override + public boolean pulse() { + try { + player.faceLocation(node.getFaceLocation(player.getLocation())); + if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { + return true; + } + if (option == null || option.getHandler() == null || !option.getHandler().handle(player, node, option.getName().toLowerCase())) { + player.getPacketDispatch().sendMessage("Nothing interesting happens."); + } + } catch (Exception e){ + e.printStackTrace(); + log(this.getClass(), Log.ERR, this.getClass().getName() + e.getMessage()); + } + return true; + } + }, pulseType); + } + + /** + * Handles a default option. + * @param player The player. + * @param option The option. + * @param pulseType The pulse type. + */ + private void handleDefaultOption(final Player player, final Option option, PulseType pulseType) { + if (!option.getHandler().isDelayed(player)) { + if (player.getZoneMonitor().interact(node, option)) { + return; + } + player.getProperties().getCombatPulse().stop(); + if (!option.getHandler().handle(player, node, option.getName().toLowerCase())) { + player.getPulseManager().runUnhandledAction(player, pulseType); + } + return; + } + player.getPulseManager().run(new Pulse(1, player, node) { + @Override + public boolean pulse() { + if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { + return true; + } + if (!option.getHandler().handle(player, node, option.getName().toLowerCase())) { + player.getPacketDispatch().sendMessage("Nothing interesting happens."); + } + return true; + } + }, pulseType); + } + + /** + * Initializes the interaction options. + * @param nodeId The node id. + * @param names The option names. + */ + public void init(int nodeId, String... names) { + options = new Option[names.length]; + for (int i = 0; i < options.length; i++) { + String name = names[i]; + if (name != null && !name.equals("null")) { + set(new Option(name, i).setHandler(Option.defaultHandler(node, nodeId, name))); + } + continue; + } + } + + /** + * Sets the default option handlers (if not yet initialized). + */ + public void setDefault() { + if (initialized) { + return; + } + if (node instanceof Player) { + for (int i = 0; i < options.length; i++) { + remove(i); + } + set(Option._P_FOLLOW); + set(Option._P_TRADE); + set(Option._P_ASSIST); + } else if (node instanceof NPC) { + NPC npc = (NPC) node; + init(npc.getId(), npc.getDefinition().getOptions()); + } else if (node instanceof Scenery) { + Scenery object = (Scenery) node; + init(object.getId(), object.getDefinition().getOptions()); + } else if (node instanceof Item) { + Item item = (Item) node; + if (item.getLocation() != null) { + init(item.getId(), item.getDefinition().getGroundOptions()); + } else { + init(item.getId(), item.getDefinition().getInventoryOptions()); + } + } else { + throw new IllegalStateException("Unsupported node type - " + node); + } + initialized = true; + } + + /** + * Sets a new option. + * @param option The option. + */ + public void set(Option option) { + options[option.getIndex()] = option; + sendOption(node, option.getIndex(), option.getName()); + } + + /** + * Removes an option. + * @return {@code True} if the option got removed, {@code false} if the + * option wasn't set. + */ + public boolean remove(Option option) { + if (options[option.getIndex()] == option) { + remove(option.getIndex()); + return true; + } + return false; + } + + /** + * Removes an option. + * @param index The index. + */ + public void remove(int index) { + if (options[index] == null) { + return; + } + options[index] = null; + sendOption(node, index, "null"); + } + + /** + * Gets the option on the requested slot. + * @param index The slot index. + * @return The option on that slot, or {@code null} if there was no option. + */ + public Option get(int index) { + return options[index]; + } + + /** + * Gets the options. + * @return The options. + */ + public Option[] getOptions() { + return options; + } + + /** + * Gets the initialized value. + * @return The initialized. + */ + public boolean isInitialized() { + return initialized; + } + + /** + * Sets the initialized value. + * @param initialized The initialized to set. + */ + public void setInitialized(boolean initialized) { + this.initialized = initialized; + } + +} diff --git a/Server/src/main/core/game/interaction/InteractionListener.kt b/Server/src/main/core/game/interaction/InteractionListener.kt new file mode 100644 index 000000000..fff396633 --- /dev/null +++ b/Server/src/main/core/game/interaction/InteractionListener.kt @@ -0,0 +1,97 @@ +package core.game.interaction + +import core.api.ContentInterface +import core.game.node.Node +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.map.Location +import content.global.handlers.item.SpadeDigListener + +interface InteractionListener : ContentInterface{ + val ITEM + get() = IntType.ITEM + val GROUNDITEM + get() = IntType.GROUNDITEM + val NPC + get() = IntType.NPC + val SCENERY + get() = IntType.SCENERY + + fun on(id: Int, type: IntType, vararg option: String, handler: (player: Player, node: Node) -> Boolean){ + InteractionListeners.add(id, type.ordinal, option, handler) + } + fun on(ids: IntArray, type: IntType, vararg option: String, handler: (player: Player, node: Node) -> Boolean){ + InteractionListeners.add(ids, type.ordinal, option, handler) + } + @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){ + InteractionListeners.add(option, type.ordinal, handler) + } + fun onUseWith(type: IntType, used: Int, vararg with: Int, handler: (player: Player, used: Node, with: Node) -> Boolean){ + InteractionListeners.add(type.ordinal, used, with, handler) + } + fun onUseWith(type: IntType, used: IntArray, vararg with: Int, handler: (player: Player, used: Node, with: Node) -> Boolean){ + InteractionListeners.add(type.ordinal, used, with, handler) + } + fun onUseAnyWith(type: IntType, vararg with: Int, handler: (player: Player, used: Node, with: Node) -> Boolean) { + InteractionListeners.add(type.ordinal, with, handler) + } + fun onUseWithPlayer(vararg used: Int, handler: (player: Player, used: Node, with: Node) -> Boolean) { + InteractionListeners.add(IntType.PLAYER.ordinal, used, handler) + } + // Note: wildcard listeners incur overhead on every use-with interaction, only use them as a space-time tradeoff when something + // is actually supposed to have a response to every item used with it (e.g. imp boxes, certain quest npcs) + fun onUseWithWildcard(type: IntType, predicate: (used: Int, with: Int) -> Boolean, handler: (player: Player, used: Node, with: Node) -> Boolean) { + InteractionListeners.addWildcard(type.ordinal, predicate, handler) + } + fun onEquip(id: Int, handler: (player: Player, node: Node) -> Boolean){ + InteractionListeners.addEquip(id, handler) + } + fun onUnequip(id:Int, handler: (player: Player, node: Node) -> Boolean){ + InteractionListeners.addUnequip(id, handler) + } + + fun onEquip(ids: IntArray, handler: (player: Player, node: Node) -> Boolean){ + ids.forEach { id -> InteractionListeners.addEquip(id, handler) } + } + fun onUnequip(ids:IntArray, handler: (player: Player, node: Node) -> Boolean){ + ids.forEach{ id -> InteractionListeners.addUnequip(id, handler) } + } + + fun defineDestinationOverrides(){} + + fun setDest(type: IntType, id: Int, handler: (Entity, Node) -> Location){ + InteractionListeners.addDestOverride(type.ordinal, id, handler) + } + fun setDest(type: IntType, vararg options: String, handler: (Entity, Node) -> Location){ + InteractionListeners.addDestOverrides(type.ordinal, options, handler) + } + + fun setDest(type: IntType, ids: IntArray, vararg options: String, handler: (Entity, Node) -> Location){ + InteractionListeners.addDestOverrides(type.ordinal, ids, options, handler) + } + + fun onDig(location: Location,method: (player: Player) -> Unit){ + SpadeDigListener.registerListener(location, method) + } + + fun flagInstant() { + val name = this::class.java.name + 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 new file mode 100644 index 000000000..e3cab3e34 --- /dev/null +++ b/Server/src/main/core/game/interaction/InteractionListeners.kt @@ -0,0 +1,324 @@ +package core.game.interaction + +import core.game.event.InteractionEvent +import core.game.event.UseWithEvent +import core.game.node.Node +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.map.Location + +object InteractionListeners { + private val listeners = HashMap Boolean>(1000) + private val useWithListeners = HashMap Boolean>(1000) + private val useAnyWithListeners = HashMap Boolean>(10) + 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 + fun add(id: Int, type: Int, option: Array, method: (Player,Node) -> Boolean){ + for(opt in option) { + val key = "$id:$type:${opt.toLowerCase()}" + if (!validate(key)) { + throw IllegalStateException("$opt for $id with type ${IntType.values()[type].name} already defined! Existing use: [${listeners[key]!!::class.toString()}]") + } + listeners[key] = method + } + } + + private fun validate(key: String): Boolean { + return !listeners.containsKey(key) && !useWithListeners.containsKey(key) + } + + @JvmStatic + fun add(ids: IntArray, type: Int, option: Array, method: (Player,Node) -> Boolean){ + for(id in ids){ + add(id,type,option,method) + } + } + + @JvmStatic + fun add(option: String,type: Int, method: (Player,Node) -> Boolean){ + val key = "$type:${option.toLowerCase()}" + if (!validate(key)) { + throw IllegalStateException("Catchall listener for $option on type ${IntType.values()[type].name} already in use: ${listeners[key]!!::class}") + } + listeners[key] = method + } + + @JvmStatic + fun add(options: Array,type: Int,method: (Player, Node) -> Boolean){ + for(opt in options){ + add(opt.toLowerCase(),type,method) + } + } + + @JvmStatic + fun add(used: Int, with: Int, type: Int, method: (Player,Node,Node) -> Boolean){ + val key = "$used:$with:$type" + val altKey = "$with:$used:$type" + if (!validate(key) || !validate(altKey)) { + throw IllegalStateException("Usewith using $used with $with for type ${IntType.values()[type]} already in use: [${(useWithListeners[key] ?: useWithListeners[altKey])!!::class}") + } + useWithListeners[key] = method + } + + @JvmStatic + fun add(type: Int, used: Int, with: IntArray, method: (Player, Node, Node) -> Boolean){ + for(id in with){ + add(used = used, with = id, type = type, method = method) + } + } + + @JvmStatic + fun addWildcard(type: Int, predicate: (used: Int, with: Int) -> Boolean, handler: (player: Player, used: Node, with: Node) -> Boolean) { + if(!useWithWildcardListeners.containsKey(type)) { + useWithWildcardListeners.put(type, ArrayList()) + } + useWithWildcardListeners[type]!!.add(Pair(predicate, handler)) + } + + @JvmStatic + fun addEquip(id: Int,method: (Player, Node) -> Boolean){ + equipListeners["equip:$id"] = method + } + + @JvmStatic + fun addUnequip(id: Int, method: (Player,Node) -> Boolean){ + equipListeners["unequip:$id"] = method + } + + @JvmStatic + fun getEquip(id: Int): ((Player,Node) -> Boolean)? { + return equipListeners["equip:$id"] + } + + @JvmStatic + fun getUnequip(id: Int): ((Player,Node) -> Boolean)? { + return equipListeners["unequip:$id"] + } + + @JvmStatic + fun get(used: Int, with: Int, type: Int): ((Player,Node,Node) -> Boolean)? { + val method = useWithListeners["$used:$with:$type"] ?: useAnyWithListeners["$with:$type"] + if(method != null) { + return method + } + val handlers = useWithWildcardListeners[type] ?: return null + for(pair in handlers) { + if(pair.first(used, with)) { + return pair.second + } + } + return null + } + + @JvmStatic + fun get(id: Int, type: Int, option: String): ((Player,Node) -> Boolean)?{ + return listeners["$id:$type:${option.toLowerCase()}"] + } + + @JvmStatic + fun get(option: String,type: Int): ((Player,Node) -> Boolean)?{ + return listeners["$type:${option.toLowerCase()}"] + } + + @JvmStatic + fun addDestOverride(type: Int, id: Int, method: (Entity,Node) -> Location){ + destinationOverrides["$type:$id"] = method + } + + @JvmStatic + fun addDestOverrides(type: Int,options: Array, method: (Entity,Node) -> Location){ + for(opt in options){ + destinationOverrides["$type:${opt.toLowerCase()}"] = method + } + } + + @JvmStatic + fun addDestOverrides(type: Int, ids: IntArray,options: Array, method: (Entity,Node) -> Location){ + for(id in ids){ + for(opt in options){ + destinationOverrides["$type:$id:${opt.toLowerCase()}"] = method + } + } + } + + @JvmStatic + fun getOverride(type: Int, id:Int, option: String): ((Entity, Node) -> Location)?{ + return destinationOverrides["$type:$id:${option.toLowerCase()}"] + } + + @JvmStatic + fun getOverride(type: Int,id: Int): ((Entity,Node) -> Location)?{ + return destinationOverrides["$type:$id"] + } + + @JvmStatic + fun getOverride(type: Int,option: String): ((Entity,Node) -> Location)?{ + return destinationOverrides["$type:$option"] + } + + @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 + } else { + return equipListeners["unequip:$id"]?.invoke(player,node) ?: true + } + } + + @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 + IntType.GROUNDITEM -> DestinationFlag.ITEM + else -> DestinationFlag.OBJECT + } + + if(player.locks.isInteractionLocked) return false + + var flipped = false + + val method = if (with is Player) get(-1, used.id, 4) ?: return false + else get(used.id,with.id,type.ordinal) ?: + if (type == IntType.ITEM) + get(with.id,used.id,type.ordinal).also { flipped = true } ?: return false + else return false + + val destOverride = if(flipped) { + getOverride(type.ordinal, used.id, "use") ?: getOverride(type.ordinal, with.id) ?: getOverride(type.ordinal, "use") + } else { + getOverride(type.ordinal, with.id, "use") ?: getOverride(type.ordinal, used.id) ?: getOverride(type.ordinal, "use") + } + + + if(type != IntType.ITEM && !isUseWithInstant(method)) { + if(player.locks.isMovementLocked) return false + player.pulseManager.run(object : MovementPulse(player, with, flag, destOverride) { + override fun pulse(): Boolean { + if (player.zoneMonitor.useWith(used.asItem(), with)) { + return true + } + player.faceLocation(with.location) + 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) + return true + } + }) + } else { + if(flipped) player.dispatch(UseWithEvent(with.id, used.id)) + else player.dispatch(UseWithEvent(used.id, with.id)) + 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 + IntType.NPC -> DestinationFlag.ENTITY + IntType.SCENERY -> null + else -> DestinationFlag.OBJECT + } + + if(player.locks.isInteractionLocked) return false + + val method = get(id,type.ordinal,option) ?: get(option,type.ordinal) + + player.setAttribute("interact:option", option.lowercase()) + player.dispatch(InteractionEvent(node, option.toLowerCase())) + + 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) + method.invoke(player,node) + return true + } + }) + } else { + method.invoke(player,node) + } + return true + } + + fun add(type: Int, used: IntArray, with: IntArray, handler: (Player, Node, Node) -> Boolean) { + for(u in used){ + for (w in with){ + useWithListeners["$u:$w:$type"] = handler + } + } + } + + fun add(type: Int, with: IntArray, handler: (Player, Node, Node) -> Boolean) { + for (w in with) { + useAnyWithListeners["$w:$type"] = handler + } + } + + fun isInstant(handler: (Player, Node) -> Boolean): Boolean { + val className = handler.javaClass.name.substringBefore("$") + return instantClasses.contains(className) + } + + fun isUseWithInstant(handler: (player: Player, used: Node, with: Node) -> Boolean): Boolean { + 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/InterfaceListener.kt b/Server/src/main/core/game/interaction/InterfaceListener.kt new file mode 100644 index 000000000..b28c5e9a5 --- /dev/null +++ b/Server/src/main/core/game/interaction/InterfaceListener.kt @@ -0,0 +1,27 @@ +package core.game.interaction + +import core.api.ContentInterface +import core.game.component.Component +import core.game.node.entity.player.Player + +/** + * An interface for writing content that allows the class to handle game interface interactions + * + * Interactions should be defined in the required [defineInterfaceListeners] method. + */ +interface InterfaceListener : ContentInterface { + fun defineInterfaceListeners() + + fun on(componentID: Int, buttonID: Int, handler: (player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int) -> Boolean){ + InterfaceListeners.add(componentID, buttonID, handler) + } + fun on(componentID: Int, handler: (player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int) -> Boolean){ + InterfaceListeners.add(componentID, handler) + } + fun onOpen(componentID: Int,handler: (player: Player, component: Component) -> Boolean){ + InterfaceListeners.addOpenListener(componentID, handler) + } + fun onClose(componentID: Int,handler: (player: Player, component: Component) -> Boolean){ + InterfaceListeners.addCloseListener(componentID, handler) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/InterfaceListeners.kt b/Server/src/main/core/game/interaction/InterfaceListeners.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/interaction/InterfaceListeners.kt rename to Server/src/main/core/game/interaction/InterfaceListeners.kt index 7145a3fa9..2fded1e88 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InterfaceListeners.kt +++ b/Server/src/main/core/game/interaction/InterfaceListeners.kt @@ -1,4 +1,4 @@ -package rs09.game.interaction +package core.game.interaction import core.game.component.Component import core.game.node.entity.player.Player @@ -55,7 +55,7 @@ object InterfaceListeners { @JvmStatic fun runClose(player: Player,component: Component): Boolean{ - val method = getCloseListener(component.id) ?: return false + val method = getCloseListener(component.id) ?: return true return method.invoke(player,component) } diff --git a/Server/src/main/core/game/interaction/Listener.kt b/Server/src/main/core/game/interaction/Listener.kt new file mode 100644 index 000000000..dc4b5c4e1 --- /dev/null +++ b/Server/src/main/core/game/interaction/Listener.kt @@ -0,0 +1,11 @@ +package core.game.interaction + +import core.api.StartupListener + +interface Listener : StartupListener { + override fun startup() { + defineListeners() + } + + fun defineListeners() +} \ No newline at end of file diff --git a/Server/src/main/core/game/interaction/MovementPulse.java b/Server/src/main/core/game/interaction/MovementPulse.java new file mode 100644 index 000000000..b378a8a1f --- /dev/null +++ b/Server/src/main/core/game/interaction/MovementPulse.java @@ -0,0 +1,645 @@ +package core.game.interaction; + +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; + +/** + * Handles a movement task. + * + * @author Emperor + */ +public abstract class MovementPulse extends Pulse { + + /** + * The moving entity. + */ + protected Entity mover; + + /** + * The destination node. + */ + protected Node destination; + + /** + * The destination's last location. + */ + private Location last; + + /** + * The pathfinder. + */ + private Pathfinder pathfinder; + + /** + * If running should be forced. + */ + private boolean forceRun; + + /** + * The option handler. + */ + private OptionHandler optionHandler; + + /** + * The use with handler. + */ + private UseWithHandler useHandler; + + /** + * The destination flag. + */ + private DestinationFlag destinationFlag; + + /** + * The location to interact from. + */ + private Location interactLocation; + + /** + * If the path couldn't be fully found. + */ + private boolean near; + + private Function2 overrideMethod; + + private Location previousLoc; + + /** + * Constructs a new {@code MovementPulse} {@code Object}. + * + * @param mover The moving entity. + * @param destination The destination node. + */ + public MovementPulse(Entity mover, Node destination) { + this(mover, destination, null, false); + } + + /** + * Constructs a new {@code MovementPulse} {@code Object}. + * + * @param mover The moving entity. + * @param destination The destination node. + * @param forceRun If the entity is forced to run. + */ + public MovementPulse(Entity mover, Node destination, boolean forceRun) { + this(mover, destination, null, forceRun); + } + + /** + * Constructs a new {@code MovementPulse} {@code Object}. + * + * @param mover The moving entity. + * @param destination The destination node. + * @param pathfinder The pathfinder to use. + */ + public MovementPulse(Entity mover, Node destination, Pathfinder pathfinder) { + this(mover, destination, pathfinder, false); + } + + /** + * Constructs a new {@code MovementPulse} {@code Object}. + * + * @param mover The moving entity. + * @param destination The destination node. + * @param optionHandler The option handler used. + */ + public MovementPulse(Entity mover, Node destination, OptionHandler optionHandler) { + this(mover, destination, null, false); + this.optionHandler = optionHandler; + } + + /** + * Constructs a new {@code MovementPulse} {@code Object}. + * + * @param mover The moving entity. + * @param destination The destination node. + * @param useHandler The use with handler used. + */ + public MovementPulse(Entity mover, Node destination, UseWithHandler useHandler) { + this(mover, destination, null, false); + this.useHandler = useHandler; + } + + /** + * Constructs a new {@code MovementPulse} {@code Object}. + * + * @param mover The moving entity. + * @param destination The destination node. + * @param destinationFlag The destination flag. + */ + public MovementPulse(Entity mover, Node destination, DestinationFlag destinationFlag) { + this(mover, destination, null, false); + this.destinationFlag = destinationFlag; + } + + public MovementPulse(Entity mover, Node destination, DestinationFlag destinationFlag, Function2 method){ + this(mover,destination,null,false); + this.destinationFlag = destinationFlag; + this.overrideMethod = method; + } + + /** + * Constructs a new {@code MovementPulse} {@code Object}. + * + * @param mover The moving entity. + * @param destination The destination node. + * @param pathfinder The pathfinder to use. + * @param forceRun If the entity is forced to run. + */ + public MovementPulse(Entity mover, Node destination, Pathfinder pathfinder, boolean forceRun) { + super(1, mover, destination); + this.mover = mover; + this.destination = destination; + 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; + } + } else { + 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() { + if (!mover.getViewport().getRegion().isActive()) + return false; + + if (!isRunning()) return true; + + if (!validate()) { + stop(); + return true; + } + + clearInferiorScripts(); + + mover.face(null); + if (canInteractWithoutMoving()) { + if (interactImmediately()) { + stop(); + return true; + } + } + updatePath(); + + if (tryInteract()) { + stop(); + return true; + } + + return false; + } + + private boolean tryInteract() { + Location ml = mover.getLocation(); + // Allow being within 1 square of moving entities to interact with them. + int radius = destination instanceof Entity && ((Entity)destination).getWalkingQueue().hasPath() ? 1 : 0; + if (interactLocation == null) + return false; + 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) { + if (near) { + ((Player) mover).getPacketDispatch().sendMessage("I can't reach that."); + } + PacketRepository.send(ClearMinimapFlag.class, new PlayerContext((Player) mover)); + } + stop(); + return true; + } + } catch (Exception e){ + e.printStackTrace(); + stop(); + } + } + 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(); + if (destination instanceof Entity) { + mover.face(null); + } + last = null; + } + + /** + * Finds a path to the destination, if necessary. + */ + private boolean usingTruncatedPath = false; + private boolean isMoveNearSet = false; + public void updatePath() { + if (mover instanceof NPC && mover.asNpc().isNeverWalks()) { + return; + } + if(destination == null || destination.getLocation() == null){ + return; + } + + Location loc = 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(loc == null && overrideMethod != null){ + loc = overrideMethod.invoke(mover,destination); + if(loc == destination.getLocation() && destinationFlag != null) loc = destinationFlag.getDestination(mover,destination); + else if (loc == destination.getLocation()) loc = null; + } + + if (destination instanceof NPC && mover.getProperties().getCombatPulse().getVictim() != destination) + loc = checkForEntityPathInterrupt(loc != null ? loc : destination.getLocation()); + + if (interactLocation == null) + interactLocation = loc; + + if (destination instanceof Entity || interactLocation == null || (mover.getWalkingQueue().getQueue().size() <= 1 && interactLocation.getDistance(mover.getLocation()) > 0) || (usingTruncatedPath && destination.getLocation().getDistance(mover.getLocation()) < 14)) { + if (!checkAllowMovement()) + return; + if (destination instanceof Entity && previousLoc != null && previousLoc.equals(loc) && mover.getWalkingQueue().hasPath()) + 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); + } + + if (i == size - 1 && interactLocation == null) + interactLocation = Location.create(point.getX(), point.getY(), mover.getLocation().getZ()); + } + } + previousLoc = loc; + } + 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 Entity) { + Entity e = (Entity) destination; + Location l = e.getLocation(); + Deque npcPath = e.getWalkingQueue().getQueue(); + if (e.getWalkingQueue().hasPath() && e.getProperties().getCombatPulse().isRunning() && e.getProperties().getCombatPulse().getVictim() == mover) + canMove = false; + if (!canMove) { //If we normally shouldn't move, but the NPC's pathfinding is not letting them move, then move. + if (npcPath.size() == 1) { + Point pathElement = npcPath.peek(); + if (pathElement.getX() == l.getX() && pathElement.getY() == l.getY()) + canMove = true; + } + } + } + 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()); + } + + /** + * Finds the closest location next to the node. + * + * @return The location to walk to. + */ + private Location findBorderLocation(Location centerDestLoc) { + int size = destination.size(); + 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(centerDestLoc, mover.getLocation()); + main: + for (int i = 0; i < 4; i++) { + int amount = 0; + switch (direction) { + case NORTH: + amount = size - delta.getY(); + break; + case EAST: + amount = size - delta.getX(); + break; + case SOUTH: + amount = mover.size() + delta.getY(); + break; + case WEST: + amount = mover.size() + delta.getX(); + break; + default: + return null; + } + for (int j = 0; j < amount; j++) { + for (int s = 0; s < mover.size(); s++) { + switch (direction) { + case NORTH: + if (!direction.canMove(mover.getLocation().transform(s, j + mover.size(), 0))) { + direction = Direction.get((direction.toInteger() + 1) & 3); + continue main; + } + break; + case EAST: + if (!direction.canMove(mover.getLocation().transform(j + mover.size(), s, 0))) { + direction = Direction.get((direction.toInteger() + 1) & 3); + continue main; + } + break; + case SOUTH: + if (!direction.canMove(mover.getLocation().transform(s, -(j + 1), 0))) { + direction = Direction.get((direction.toInteger() + 1) & 3); + continue main; + } + break; + case WEST: + if (!direction.canMove(mover.getLocation().transform(-(j + 1), s, 0))) { + direction = Direction.get((direction.toInteger() + 1) & 3); + continue main; + } + break; + default: + return null; + } + } + } + Location location = mover.getLocation().transform(direction, amount); + return location; + } + return null; + } + + /** + * Checks if the mover is standing on an invalid position. + * + * @param l The location. + * @return {@code True} if so. + */ + private boolean isInsideEntity(Location l) { + if (!(destination instanceof Entity)) { + return false; + } + if (((Entity) destination).getWalkingQueue().isMoving()) { + return false; + } + Location loc = destination.getLocation(); + int size = destination.size(); + return Pathfinder.isStandingIn(l.getX(), l.getY(), mover.size(), mover.size(), loc.getX(), loc.getY(), size, size); + } + + /** + * Gets the forceRun. + * + * @return The forceRun. + */ + public boolean isForceRun() { + return forceRun; + } + + /** + * Sets the forceRun. + * + * @param forceRun The forceRun to set. + */ + public void setForceRun(boolean forceRun) { + this.forceRun = forceRun; + } + + /** + * Sets the current destination. + * + * @param destination The destination. + */ + public void setDestination(Node destination) { + this.destination = destination; + } + + /** + * Sets the last location. + * + * @param last The last location. + */ + public void setLast(Location last) { + this.last = last; + } + +} diff --git a/Server/src/main/java/core/game/interaction/NodeUsageEvent.java b/Server/src/main/core/game/interaction/NodeUsageEvent.java similarity index 100% rename from Server/src/main/java/core/game/interaction/NodeUsageEvent.java rename to Server/src/main/core/game/interaction/NodeUsageEvent.java diff --git a/Server/src/main/java/core/game/interaction/Option.java b/Server/src/main/core/game/interaction/Option.java similarity index 94% rename from Server/src/main/java/core/game/interaction/Option.java rename to Server/src/main/core/game/interaction/Option.java index 37b85c9ba..8c99a1b57 100644 --- a/Server/src/main/java/core/game/interaction/Option.java +++ b/Server/src/main/core/game/interaction/Option.java @@ -28,6 +28,9 @@ public final class Option { * The player trade option. */ public static final Option _P_TRADE = new Option("Trade with", 3); + public static final Option _P_GIVETO = new Option("Give-to", 3); + + public static final Option _P_PICKPOCKET = new Option("Pickpocket", 4); public static final Option _P_EXAMINE = new Option("Examine", 7); @@ -85,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; } @@ -122,4 +125,4 @@ public final class Option { this.handler = handler; return this; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/interaction/OptionHandler.java b/Server/src/main/core/game/interaction/OptionHandler.java similarity index 91% rename from Server/src/main/java/core/game/interaction/OptionHandler.java rename to Server/src/main/core/game/interaction/OptionHandler.java index 467464958..057bdacdb 100644 --- a/Server/src/main/java/core/game/interaction/OptionHandler.java +++ b/Server/src/main/core/game/interaction/OptionHandler.java @@ -3,13 +3,16 @@ package core.game.interaction; import core.cache.def.impl.SceneryDefinition; import core.game.node.Node; import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; +import core.tools.Log; +import core.tools.SystemLogger; import core.game.world.map.Location; 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("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/java/core/game/content/quest/PluginInteraction.java b/Server/src/main/core/game/interaction/PluginInteraction.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/PluginInteraction.java rename to Server/src/main/core/game/interaction/PluginInteraction.java index c7d1e672e..ecb8708b7 100644 --- a/Server/src/main/java/core/game/content/quest/PluginInteraction.java +++ b/Server/src/main/core/game/interaction/PluginInteraction.java @@ -1,4 +1,4 @@ -package core.game.content.quest; +package core.game.interaction; import core.game.interaction.NodeUsageEvent; import core.game.interaction.Option; diff --git a/Server/src/main/java/core/game/content/quest/PluginInteractionManager.java b/Server/src/main/core/game/interaction/PluginInteractionManager.java similarity index 96% rename from Server/src/main/java/core/game/content/quest/PluginInteractionManager.java rename to Server/src/main/core/game/interaction/PluginInteractionManager.java index 727363b11..1f2eadbfa 100644 --- a/Server/src/main/java/core/game/content/quest/PluginInteractionManager.java +++ b/Server/src/main/core/game/interaction/PluginInteractionManager.java @@ -1,7 +1,5 @@ -package core.game.content.quest; +package core.game.interaction; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.Option; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; 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/java/core/game/interaction/SpecialGroundInteraction.java b/Server/src/main/core/game/interaction/SpecialGroundInteraction.java similarity index 100% rename from Server/src/main/java/core/game/interaction/SpecialGroundInteraction.java rename to Server/src/main/core/game/interaction/SpecialGroundInteraction.java diff --git a/Server/src/main/java/core/game/interaction/SpecialGroundItems.java b/Server/src/main/core/game/interaction/SpecialGroundItems.java similarity index 92% rename from Server/src/main/java/core/game/interaction/SpecialGroundItems.java rename to Server/src/main/core/game/interaction/SpecialGroundItems.java index 1357f3c40..9e7324282 100644 --- a/Server/src/main/java/core/game/interaction/SpecialGroundItems.java +++ b/Server/src/main/core/game/interaction/SpecialGroundItems.java @@ -1,6 +1,6 @@ package core.game.interaction; -import core.game.interaction.city.portsarim.AhabBeerInteraction; +import content.region.asgarnia.portsarim.handlers.AhabBeerInteraction; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.game.world.map.Location; diff --git a/Server/src/main/java/core/game/interaction/UseWithHandler.java b/Server/src/main/core/game/interaction/UseWithHandler.java similarity index 90% rename from Server/src/main/java/core/game/interaction/UseWithHandler.java rename to Server/src/main/core/game/interaction/UseWithHandler.java index 0af44418f..4c5a6f414 100644 --- a/Server/src/main/java/core/game/interaction/UseWithHandler.java +++ b/Server/src/main/core/game/interaction/UseWithHandler.java @@ -1,15 +1,17 @@ package core.game.interaction; +import core.game.event.UseWithEvent; import core.cache.def.impl.SceneryDefinition; 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.node.item.Item; import core.game.node.scenery.Scenery; -import rs09.game.system.SystemLogger; +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; @@ -17,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. * @@ -125,16 +129,18 @@ public abstract class UseWithHandler implements Plugin { } if (handler == null) { if (n instanceof Item && !(event.getUsed() instanceof Player)) { - event.getPlayer().getPulseManager().runUnhandledAction(event.getPlayer()); + event.getPlayer().getPulseManager().runUnhandledAction(event.getPlayer(), PulseType.STANDARD); } else { 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; } - }, "movement"); + }, PulseType.STANDARD); } return; } @@ -143,6 +149,7 @@ public abstract class UseWithHandler implements Plugin { event.getPlayer().getPulseManager().run(new Pulse(1, event.getPlayer(), event.getUsed(), event.getUsedWith()) { @Override public boolean pulse() { + event.getPlayer().dispatch(new UseWithEvent(event.getUsed().getId(), event.getUsedWith().getId())); boolean handled = false; if (event.getPlayer() != null) { event.getPlayer().getInterfaceManager().close(); @@ -162,13 +169,14 @@ public abstract class UseWithHandler implements Plugin { } return true; } - }); + }, PulseType.STANDARD); return; } event.getPlayer().getPulseManager().run(new MovementPulse(event.getPlayer(), event.getUsedWith(), handler.get(0)) { @Override public boolean pulse() { - event.getPlayer().faceLocation(FaceLocationFlag.getFaceLocation(event.getPlayer(), event.getUsedWith())); + event.getPlayer().dispatch(new UseWithEvent(event.getUsed().getId(), event.getUsedWith().getId())); + event.getPlayer().faceLocation(event.getUsedWith().getFaceLocation(event.getPlayer().getLocation())); boolean handled = false; Item used = (Item) event.getUsed(); for (UseWithHandler h : handlers) { @@ -185,7 +193,7 @@ public abstract class UseWithHandler implements Plugin { } return true; } - }, "movement"); + }, PulseType.STANDARD); } catch (Exception e){ e.printStackTrace(); } @@ -206,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("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()) { @@ -266,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 new file mode 100644 index 000000000..2e93e6049 --- /dev/null +++ b/Server/src/main/core/game/node/Node.java @@ -0,0 +1,295 @@ +package core.game.node; + +import core.game.interaction.DestinationFlag; +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; +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 2009Scape. + * @author Emperor + */ +public abstract class Node { + + /** + * The name of the node; + */ + protected String name; + + /** + * The location. + */ + protected Location location; + + /** + * The index of the node. + */ + protected int index; + + /** + * The node's direction. + */ + protected Direction direction; + + /** + * The node's size. + */ + protected int size = 1; + + /** + * If the node is active. + */ + protected boolean active = true; + + /** + * The interaction instance. + */ + protected InteractPlugin interactPlugin; + + /** + * The destination flag. + */ + protected DestinationFlag destinationFlag; + + /** + * If the node is renderable. + */ + protected boolean renderable = true; + + /** + * Constructs a new {@code Node} {@code Object}. + * @param name The name. + * @param location The location. + */ + public Node(String name, Location location) { + this.name = name; + this.location = location; + } + + /** + * Casts the npc to a player. + * @return the npc. + */ + public NPC asNpc() { + return (NPC) this; + } + + /** + * Casts the player. + * @return the player. + */ + public Player asPlayer() { + return (Player) this; + } + + /** + * Casts the scenery. + * @return the object. + */ + public Scenery asScenery() { + return (Scenery) this; + } + + /** + * Casts the item. + * @return the item. + */ + public Item asItem() { + return (Item) this; + } + + /** + * Gets the node id. + * @return the id. + */ + public int getId() { + 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. + */ + public Location getCenterLocation() { + int offset = size >> 1; + 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. + */ + public String getName() { + return name; + } + + /** + * Get a formated username. + * @return The username. + */ + public String getUsername() { + return StringUtils.formatDisplayName(name); + } + + /** + * 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 location. + * @return The location. + */ + public Location getLocation() { + return location; + } + + /** + * Sets the location. + * @param location The location to set. + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * Gets the direction. + * @return The direction. + */ + public Direction getDirection() { + return direction; + } + + /** + * Sets the direction. + * @param direction The direction to set. + */ + public void setDirection(Direction direction) { + if (direction == null) + return; + this.direction = direction; + } + + /** + * Gets the size. + * @return The size. + */ + public int size() { + return size; + } + + /** + * Sets the size. + * @param size The size to set. + */ + public void setSize(int size) { + this.size = size; + } + + /** + * Gets the active. + * @return The active. + */ + public boolean isActive() { + return active; + } + + /** + * Sets the active. + * @param active The active to set. + */ + public void setActive(boolean active) { + this.active = active; + } + + /** + * Gets the interaction. + * @return The interaction. + */ + public InteractPlugin getInteraction() { + if (interactPlugin != null && !interactPlugin.isInitialized()) { + interactPlugin.setDefault(); + } + return interactPlugin; + } + + /** + * Sets the interaction. + * @param interactPlugin The interaction to set. + */ + public void setInteraction(InteractPlugin interactPlugin) { + this.interactPlugin = interactPlugin; + } + + /** + * Gets the destinationFlag. + * @return The destinationFlag. + */ + public DestinationFlag getDestinationFlag() { + return destinationFlag; + } + + /** + * Sets the destinationFlag. + * @param destinationFlag The destinationFlag to set. + */ + public void setDestinationFlag(DestinationFlag destinationFlag) { + this.destinationFlag = destinationFlag; + } + + /** + * Gets the renderable. + * @return The renderable. + */ + public boolean isRenderable() { + return renderable; + } + + /** + * Sets the renderable. + * @param renderable The renderable to set. + */ + public void setRenderable(boolean renderable) { + this.renderable = renderable; + } +} diff --git a/Server/src/main/core/game/node/entity/Entity.java b/Server/src/main/core/game/node/entity/Entity.java new file mode 100644 index 000000000..ffaa8f461 --- /dev/null +++ b/Server/src/main/core/game/node/entity/Entity.java @@ -0,0 +1,1007 @@ +package core.game.node.entity; + +import core.game.event.*; +import core.game.interaction.*; +import core.game.node.Node; +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.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.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; +import core.game.world.map.Viewport; +import core.game.world.map.path.Path; +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.*; + +/** + * An entity is a movable node, such as players and NPCs. + * @author Emperor + */ +public abstract class Entity extends Node { + + /** + * The entity's properties. + */ + private final Properties properties = new Properties(this); + + /** + * The entity's update masks. + */ + private final UpdateMasks updateMasks = new UpdateMasks(this); + + /** + * The walking queue. + */ + private final WalkingQueue walkingQueue = new WalkingQueue(this); + + /** + * The entity's skills. + */ + public Skills skills = new Skills(this); + + /** + * The entity's extension classes. + */ + private final Map, Object> extensions = new HashMap, Object>(); + + /** + * The entity's attributes. + */ + private final GameAttributes attributes = new GameAttributes(); + + /** + * The entity's viewport. + */ + private final Viewport viewport = new Viewport(); + + /** + * The pulse manager. + */ + private final PulseManager pulseManager = new PulseManager(this); + + /** + * The impact handler. + */ + private final ImpactHandler impactHandler = new ImpactHandler(this); + + /** + * The animator. + */ + private final Animator animator = new Animator(this); + + /** + * The teleporter. + */ + private final TeleportManager teleporter = new TeleportManager(this); + + /** + * The zone monitor. + */ + private final ZoneMonitor zoneMonitor = new ZoneMonitor(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. + * @param location The location. + */ + public Entity(String name, Location location) { + super(name, location); + super.destinationFlag = DestinationFlag.ENTITY; + } + + /** + * Attempts to make the entity move one step (priority order: west, south, + * east, north). + */ + public void moveStep() { + if (locks.isMovementLocked()) { + return; + } + Path path; + if (!(path = Pathfinder.find(this, getLocation().transform(-1, 0, 0), false, Pathfinder.DUMB)).isSuccessful()) { + if (!(path = Pathfinder.find(this, getLocation().transform(0, -1, 0), false, Pathfinder.DUMB)).isSuccessful()) { + if (!(path = Pathfinder.find(this, getLocation().transform(1, 0, 0), false, Pathfinder.DUMB)).isSuccessful()) { + if (!(path = Pathfinder.find(this, getLocation().transform(0, 1, 0), false, Pathfinder.DUMB)).isSuccessful()) { + path = null; + } + } + } + } + if (path != null) { + path.walk(this); + } + } + + /** + * Dispatches an event to this entity's event hooks + */ + public void dispatch(Event event) + { + if(this.hooks.containsKey(event.getClass())) + { + ArrayList processList = new ArrayList(this.hooks.get(event.getClass())); + processList.forEach((hook) -> { hook.process(this, event); }); + } + } + + /** + * Unhooks an eventhook from this entity + */ + public void unhook(EventHook hook) + { + for(ArrayList s : hooks.values()) s.remove(hook); + } + + /** + * Hooks an eventhook to this entity + */ + public void hook(Event event, EventHook hook) + { + hook(event.getClass(), hook); + } + + public void hook(Class event, EventHook hook) + { + ArrayList hookList; + if(hooks.get(event) != null) + { + hookList = hooks.get(event); + } + else + { + hookList = new ArrayList(); + } + if (!hookList.contains(hook)) + hookList.add(hook); + hooks.put(event, hookList); + } + + /** + * Initializes the entity. + */ + 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); + } + + /** + * Updates the entity, this gets called in a thread pool. + */ + public void update() { + } + + /** + * Resets the entity's update flags, ...
Gets called after + * {@link #update()}. + */ + public void reset() { + updateMasks.reset(); + properties.setTeleporting(false); + } + + /** + * Removes the entity from the world. + */ + public void clear() { + active = false; + viewport.remove(this); + pulseManager.clear(); + } + + /** + * Checks if the entity is in combat. + * @return {@code True} if so. + */ + public boolean inCombat() { + return getAttribute("combat-time", 0L) > System.currentTimeMillis(); + } + + /** + * Fully restores the entity. + */ + public void fullRestore() { + skills.restore(); + timers.removeTimer("poison"); + timers.removeTimer("poison:immunity"); + } + + /** + * Called when the death task gets submitted. + * @param killer The killer of this entity. + */ + public void commenceDeath(Entity killer) { + } + + /** + * Finalizes the death task. + * @param killer The killer of this entity. + */ + public void finalizeDeath(Entity killer) { + skills.restore(); + skills.rechargePrayerPoints(); + impactHandler.getImpactQueue().clear(); + impactHandler.setDisabledTicks(10); + timers.onEntityDeath(); + removeAttribute("combat-time"); + face(null); + //Check if it's a Loar shade and transform back into the shadow version. + if(this.getId() == 1240 || this.getId() == 1241){ + this.asNpc().transform(1240); + } + } + + /** + * Updates the location of an entity. + * @param last the last location. + */ + public void updateLocation(Location last) { + + } + + /** + * Checks if multiway combat zone rules should be ignored. + * @param victim The victim. + * @return {@code True} if this entity can attack regardless of multiway + * combat zone. + */ + public boolean isIgnoreMultiBoundaries(Entity victim) { + if (this instanceof NPC) { + return ((NPC) this).behavior.shouldIgnoreMultiRestrictions((NPC) this, victim); + } + return false; + } + + /** + * Should this entity prevent the mover from moving through it? + */ + public boolean shouldPreventStacking(Entity mover) { + return false; + } + + /** + * Checks an impact before receiving it. + */ + public void checkImpact(BattleState state) { + getProperties().getCombatPulse().setLastReceivedAttack(GameWorld.getTicks()); + int ticks = GameWorld.getTicks() - getProperties().getCombatPulse().getLastSentAttack(); + if (ticks > 10 && this instanceof NPC && ((NPC) this).getDefinition().getConfiguration("safespot", false)) { + Pathfinder.find(state.getAttacker(), getLocation()).walk(state.getAttacker()); + Pathfinder.find(state.getVictim(), state.getAttacker().getLocation()).walk(state.getVictim()); + if (ticks > 40) { + state.getAttacker().moveStep(); + state.getAttacker().getProperties().getCombatPulse().stop(); + state.getVictim().moveStep(); + state.getVictim().getProperties().getCombatPulse().stop(); + } + } + } + + /** + * Handles an impact. + * @param entity The entity. + * @param state The battle state. + */ + 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); + } + } + } + + /** + * Handles the first attack. + * @param target the target. + */ + public void onAttack(final Entity target) { + + } + + /** + * Method used to attack a node. + * @param node the node. + */ + public void attack(final Node node) { + getProperties().getCombatPulse().attack(node); + } + + /** + * Checks if the entity can move to the destination. + * @param destination the destination. + * @return {@code True} if so. + */ + public boolean canMove(Location destination) { + return true; + } + + /** + * Teleports the entity to a location. + * @param location the location. + */ + public void teleport(Location location) { + getProperties().setTeleportLocation(location); + } + + /** + * Teleports the entity. + * @param location the location. + * @param ticks the ticks. + */ + public void teleport(final Location location, int ticks) { + GameWorld.getPulser().submit(new Pulse(ticks, this) { + @Override + public boolean pulse() { + teleport(location); + return true; + } + }); + } + + /** + * Locks the entity. + */ + public void lock() { + locks.lock(); + } + + /** + * Locks the entity from using any actions. + * @param time The time (in game ticks) to lock. + */ + public void lock(int time) { + locks.lock(time); + } + + /** + * Unlocks the default reward locks. + */ + public void unlock() { + locks.unlock(); + } + + /** + * Checks if the entity is using the protection prayer for the given style. + * @param style The combat style. + * @return {@code True} if so. + */ + public abstract boolean hasProtectionPrayer(CombatStyle style); + + /** + * Gets the dragonfire protection value. + * @param fire if a fire attack. + * @return The value. + */ + public abstract int getDragonfireProtection(boolean fire); + + /** + * Checks if this entity is attackable by the attacking entity. + * @param entity The attacking entity. + * @param style The combat style used. + * @param message Whether to send the player a message indicating why the entity isn't attackable. + * @return {@code True} if the attacking entity can attack this entity. + */ + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { + if (DeathTask.isDead(this)) { + return false; + } + if (!entity.getZoneMonitor().continueAttack(this, style, message)) { + return false; + } + if (!this.getZoneMonitor().continueAttack(entity, style, message)) { + return false; + } + return true; + } + + /** + * Registers a new graphics update flag to the update masks. + * @param graphics The graphics. + * @return {@code True} if succesful. + */ + public boolean graphics(Graphics graphics) { + return animator.graphics(graphics); + } + + /** + * Registers a new animation update flag to the update masks. + * @param animation The animation. + * @return {@code True} if succesful. + */ + public boolean animate(Animation animation) { + return animator.animate(animation); + } + + /** + * Registers a new graphics update flag. + * @param graphics the graphics. + * @param delay the delay. + * @return {@code True} if so. + */ + public boolean graphics(final Graphics graphics, int delay) { + GameWorld.getPulser().submit(new Pulse(delay, this) { + @Override + public boolean pulse() { + graphics(graphics); + return true; + } + }); + return true; + } + + /** + * 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; + } + + /** + * Registers a new animation update flag to the update masks. + * @param animation The animation. + * @param delay the delay + * @return {@code True} if succesful. + */ + public boolean animate(final Animation animation, int delay) { + GameWorld.getPulser().submit(new Pulse(delay, this) { + @Override + public boolean pulse() { + animate(animation); + return true; + } + }); + return true; + } + + /** + * Sends the impact. + * @param state the state. + */ + public void sendImpact(BattleState state) { + getProperties().getCombatPulse().setLastSentAttack(GameWorld.getTicks()); + } + + /** + * Checks if the target can be selected. + * @param target the target. + * @return {@code True} if so. + */ + public boolean canSelectTarget(Entity target) { + return true; + } + + /** + * Registers a new animation & graphic update flag to the update masks. + * @param animation The animation. + * @param graphics The graphics. + * @return {@code True} if successful. + */ + public boolean visualize(Animation animation, Graphics graphics) { + return animator.animate(animation, graphics); + } + + /** + * Temporarily faces an entity. + * @param entity The entity to face. + * @param ticks The ticks to face the entity. + * @return {@code True} if successful. + */ + public boolean faceTemporary(Entity entity, int ticks) { + return faceTemporary(entity, null, ticks); + } + + /** + * Temporarily faces an entity. + * @param entity The entity to face. + * @param reset The entity to face when the pulse has passed. + * @param ticks The ticks to face the entity. + * @return {@code True} if successful. + */ + public boolean faceTemporary(Entity entity, final Entity reset, int ticks) { + if (face(entity)) { + GameWorld.getPulser().submit(new Pulse(ticks + 1, this) { + @Override + public boolean pulse() { + face(reset); + return true; + } + }); + return true; + } + return false; + } + + /** + * Gets the formatted hit. + * @param state the state. + * @param hit the hit. + * @return {@code True} if so. + */ + public double getFormattedHit(BattleState state, int hit) { + if (state.getAttacker() == null || state.getVictim() == null || state.getStyle() == null) { + return hit; + } + Entity entity = state.getAttacker(); + Entity victim = state.getVictim(); + CombatStyle type = state.getStyle(); + if (state.getArmourEffect() != ArmourSet.VERAC && !entity.isIgnoreProtection(type) && victim.hasProtectionPrayer(type)) { + return hit *= (entity instanceof Player && victim instanceof Player) ? 0.6 : 0; + } + return hit; + } + + /** + * Checks if this entity ignores protection prayers for the given combat style. + * @param style The combat style used. + * @return {@code True} if the entity can hit through protection prayers. + */ + public boolean isIgnoreProtection(CombatStyle style) { + return false; + } + + /** + * Starts the death for an npc. + * @param killer the killer. + */ + public void startDeath(Entity killer) { + if (zoneMonitor.startDeath(this, killer)) { + DeathTask.startDeath(this, killer); + } + } + + /** + * Casts the player type. + * @return the player. + */ + public Player asPlayer() { + return (Player) this; + } + + /** + * Checks if the entity is instance of a player. + * @return {@code True} if so. + */ + public boolean isPlayer() { + return this instanceof Player; + } + + /** + * Registers a new face entity update flag to the update masks. + * @param entity The entity to face. + * @return {@code True} if succesful. + */ + 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 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 successful. + */ + public boolean sendChat(String string) { + return getUpdateMasks().register(EntityFlag.ForceChat, string); + } + + /** + * Gets the current combat swing handler. + * @param swing If this method is called when actually performing a combat + * swing. + * @return The current combat swing handler to use. + */ + public abstract CombatSwingHandler getSwingHandler(boolean swing); + + /** + * Checks if the entity is immune to poison. + * @return {@code True} if the entity is immune to poison. + */ + public abstract boolean isPoisonImmune(); + + /** + * Sends the chat on a tick. + * @param string the string. + * @param ticks the ticks. + */ + public void sendChat(final String string, int ticks) { + GameWorld.getPulser().submit(new Pulse(ticks, this) { + @Override + public boolean pulse() { + sendChat(string); + return true; + } + }); + } + + /** + * Gets the level mod. + * @param entity the entity. + * @param victim the victim. + * @return the levelMod. + */ + public double getLevelMod(Entity entity, Entity victim) { + return 0; + } + + /** + * Gets the reward locks. + * @return The reward locks. + */ + public ActionLocks getLocks() { + return locks; + } + + /** + * Gets the client index of the entity. + * @return The client index. + */ + public int getClientIndex() { + return index; + } + + /** + * Gets the properties. + * @return The properties. + */ + public Properties getProperties() { + return properties; + } + /** + * Gets the updateMasks. + * @return The updateMasks. + */ + public UpdateMasks getUpdateMasks() { + return updateMasks; + } + + /** + * Adds an extension. + * @param clazz The class type. + * @param object The object. + */ + public void addExtension(Class clazz, Object object) { + extensions.put(clazz, object); + } + + /** + * Gets an extension. + * @param clazz The class type. + * @return The object. + */ + @SuppressWarnings("unchecked") + public T getExtension(Class clazz) { + return (T) extensions.get(clazz); + } + + /** + * Gets an extension. + * @param clazz The class type. + * @param fail The object to return if the extension wasn't added. + * @return The object. + */ + @SuppressWarnings("unchecked") + public T getExtension(Class clazz, T fail) { + T extension = (T) extensions.get(clazz); + if (extension == null) { + return fail; + } + return extension; + } + + /** + * Removes the extension. + * @param clazz The class type. + */ + public void removeExtension(Class clazz) { + extensions.remove(clazz); + } + + /** + * Gets the players attributes. + * @return the attributes. + */ + public Map getAttributes() { + return attributes.getAttributes(); + } + + public void clearAttributes() { + this.attributes.getAttributes().clear(); + this.attributes.getSavedAttributes().clear(); + } + + /** + * Sets an attribute value. + * @param key The attribute name. + * @param value The attribute value. + */ + public void setAttribute(String key, Object value) { + attributes.setAttribute(key, value); + dispatch(new AttributeSetEvent(this, key, value)); + } + + /** + * Gets an attribute. + * @param key The attribute name. + * @return The attribute value. + */ + public T getAttribute(String key) { + return attributes.getAttribute(key); + } + + /** + * Increments an attribute + * @param key The attribute name. + */ + public void incrementAttribute(String key) { + incrementAttribute(key, 1); + } + + /** + * Increments an attribute + * @param key The attribute name. + */ + public void incrementAttribute(String key, int amount) { + attributes.setAttribute(key, attributes.getAttribute(key, 0) + amount); + } + + /** + * Gets an attribute. + * @param string The attribute name. + * @param fail The value to return if the attribute is null. + * @return The attribute value, or the fail argument when null. + */ + public T getAttribute(String string, T fail) { + return attributes.getAttribute(string, fail); + } + + /** + * Removes an attribute. + * @param string The attribute name. + */ + public void removeAttribute(String string) { + attributes.removeAttribute(string); + dispatch(new AttributeRemoveEvent(this, string)); + } + + /** + * Gets the game attributes instance. + * @return The game attributes. + */ + public GameAttributes getGameAttributes() { + return attributes; + } + + /** + * Gets the walkingQueue. + * @return The walkingQueue. + */ + public WalkingQueue getWalkingQueue() { + return walkingQueue; + } + + /** + * Gets the viewport. + * @return The viewport. + */ + public Viewport getViewport() { + return viewport; + } + + /** + * Gets the skills. + * @return The skills. + */ + public Skills getSkills() { + return skills; + } + + /** + * Gets the pulseManager. + * @return The pulseManager. + */ + public PulseManager getPulseManager() { + return pulseManager; + } + + /** + * Gets the impactHandler. + * @return The impactHandler. + */ + public ImpactHandler getImpactHandler() { + return impactHandler; + } + + /** + * @return the animator. + */ + public Animator getAnimator() { + return animator; + } + + /** + * Gets the Teleporter + * @return the Teleporter + */ + public TeleportManager getTeleporter() { + return teleporter; + } + + /** + * Gets the zoneMonitor. + * @return The zoneMonitor. + */ + public ZoneMonitor getZoneMonitor() { + return zoneMonitor; + } + + /** + * Checks if we have fire resistance. + * @return {@code True} if so. + */ + public boolean hasFireResistance() { + return getAttribute("fire:immune",0) >= GameWorld.getTicks(); + } + + /** + * Checks if the entity is teleblocked. + * @return {@code True} if so. + */ + public boolean isTeleBlocked() { + return timers.getTimer("teleblock") != null || getLocks().isTeleportLocked() || getZoneMonitor().isRestricted(ZoneRestriction.TELEPORT); + } + + /** + * Gets the invisible value. + * @return The invisible. + */ + public boolean isInvisible() { + return invisible; + } + + /** + * Sets the invisible value. + * @param invisible The invisible to set. + */ + public void setInvisible(boolean invisible) { + this.invisible = invisible; + } + + public Location getClosestOccupiedTile(@NotNull Location other) { + List occupied = getOccupiedTiles(); + + Location closest = location; + if (occupied.size() > 1) { + double lowest = 9999; + for (Location tile : occupied) { + double dist = tile.getDistance(other); + if (dist < lowest) { + lowest = dist; + closest = tile; + } + } + } + + return closest; + } + + public List getOccupiedTiles() { + ArrayList occupied = new ArrayList<>(); + + Location northEast = location.transform(size, size, 0); + + for (int x = location.getX(); x < northEast.getX(); x++) { + for (int y = location.getY(); y < northEast.getY(); y++) { + occupied.add(Location.create(x, y, location.getZ())); + } + } + 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/java/core/game/node/entity/combat/BattleState.java b/Server/src/main/core/game/node/entity/combat/BattleState.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/combat/BattleState.java rename to Server/src/main/core/game/node/entity/combat/BattleState.java index 17871ede9..4a12b5966 100644 --- a/Server/src/main/java/core/game/node/entity/combat/BattleState.java +++ b/Server/src/main/core/game/node/entity/combat/BattleState.java @@ -5,9 +5,11 @@ import core.game.node.entity.combat.equipment.Ammunition; import core.game.node.entity.combat.equipment.ArmourSet; import core.game.node.entity.combat.equipment.RangeWeapon; import core.game.node.entity.combat.equipment.Weapon; +import core.game.node.entity.combat.spell.CombatSpell; /** * Represents an entity's current battle state. + * * @author Emperor */ public final class BattleState { @@ -98,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) { @@ -133,6 +136,7 @@ public final class BattleState { /** * Gets the estimatedHit. + * * @return The estimatedHit. */ public int getEstimatedHit() { @@ -141,6 +145,7 @@ public final class BattleState { /** * Sets the estimated hit. + * * @param estimatedHit The estimated hit to set. */ public void setEstimatedHit(int estimatedHit) { @@ -163,6 +168,7 @@ public final class BattleState { /** * Gets the recoilDamage. + * * @return The recoilDamage. */ public int getRecoilDamage() { @@ -171,6 +177,7 @@ public final class BattleState { /** * Sets the recoilDamage. + * * @param recoilDamage The recoilDamage to set. */ public void setRecoilDamage(int recoilDamage) { @@ -235,6 +242,7 @@ public final class BattleState { /** * Gets the maximumHit. + * * @return The maximumHit. */ public int getMaximumHit() { @@ -243,6 +251,7 @@ public final class BattleState { /** * Sets the maximumHit. + * * @param maximumHit The maximumHit to set. */ public void setMaximumHit(int maximumHit) { @@ -251,6 +260,7 @@ public final class BattleState { /** * Gets the rangeWeapon. + * * @return The rangeWeapon. */ public RangeWeapon getRangeWeapon() { @@ -259,6 +269,7 @@ public final class BattleState { /** * Sets the rangeWeapon. + * * @param rangeWeapon The rangeWeapon to set. */ public void setRangeWeapon(RangeWeapon rangeWeapon) { @@ -267,6 +278,7 @@ public final class BattleState { /** * Gets the ammunition. + * * @return The ammunition. */ public Ammunition getAmmunition() { @@ -275,6 +287,7 @@ public final class BattleState { /** * Sets the ammunition. + * * @param ammunition The ammunition to set. */ public void setAmmunition(Ammunition ammunition) { @@ -283,6 +296,7 @@ public final class BattleState { /** * Gets the frozen. + * * @return The frozen. */ public boolean isFrozen() { @@ -291,6 +305,7 @@ public final class BattleState { /** * Sets the frozen. + * * @param frozen The frozen to set. */ public void setFrozen(boolean frozen) { @@ -299,6 +314,7 @@ public final class BattleState { /** * Gets the style. + * * @return The style. */ public CombatStyle getStyle() { @@ -307,6 +323,7 @@ public final class BattleState { /** * Sets the style. + * * @param style The style to set. */ public void setStyle(CombatStyle style) { @@ -315,6 +332,7 @@ public final class BattleState { /** * Gets the armourEffect. + * * @return The armourEffect. */ public ArmourSet getArmourEffect() { @@ -323,6 +341,7 @@ public final class BattleState { /** * Sets the armourEffect. + * * @param armourEffect The armourEffect to set. */ public void setArmourEffect(ArmourSet armourEffect) { @@ -331,6 +350,7 @@ public final class BattleState { /** * Gets the attacking entity. + * * @return The entity. */ public Entity getAttacker() { @@ -339,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/kotlin/rs09/game/node/entity/combat/CombatPulse.kt b/Server/src/main/core/game/node/entity/combat/CombatPulse.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt rename to Server/src/main/core/game/node/entity/combat/CombatPulse.kt index 5525626e2..074a656e9 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatPulse.kt @@ -1,25 +1,24 @@ -package rs09.game.node.entity.combat +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 import core.game.node.Node import core.game.node.entity.Entity -import core.game.node.entity.combat.BattleState -import core.game.node.entity.combat.CombatStyle -import core.game.node.entity.combat.DeathTask -import core.game.node.entity.combat.InteractionType 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 rs09.game.world.GameWorld +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. @@ -29,7 +28,7 @@ class CombatPulse( /** * The entity. */ - private val entity: Entity?) : Pulse(1, entity, null) { + val entity: Entity?) : Pulse(1, entity, null) { /** * The victim. @@ -108,6 +107,11 @@ class CombatPulse( * The last attack recieved. */ var lastReceivedAttack = 0 + + init { + running = false + } + override fun pulse(): Boolean { if (victim == null || DeathTask.isDead(entity) || DeathTask.isDead(victim)) { return true @@ -129,7 +133,7 @@ class CombatPulse( if (handler == null) { handler = entity.getSwingHandler(true) } - if (!v.isAttackable(entity, handler!!.type)) { + if (!v.isAttackable(entity, handler!!.type, true) && entity != getAttribute(v, AntiMacro.EVENT_NPC, null)) { return true } if (!swing(entity, victim, handler)) { @@ -139,12 +143,13 @@ class CombatPulse( } var speed = entity.properties.attackSpeed val magic = handler!!.type == CombatStyle.MAGIC + val salamander = handler!! is SalamanderSwingHandler if (entity is Player && magic) { speed = 5 - } else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_RAPID) { + } 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) @@ -179,7 +184,7 @@ class CombatPulse( } } if (!victim.pulseManager.isMovingPulse) { - victim.pulseManager.clear("combat") + victim.pulseManager.clear() } victim.setAttribute("combat-time", System.currentTimeMillis() + 10000) victim.setAttribute("combat-attacker", entity) @@ -212,7 +217,7 @@ class CombatPulse( if (entity == null || victim == null || entity.locks.isMovementLocked) { return false } - movement.findPath() + movement.updatePath() return type == InteractionType.MOVE_INTERACT } @@ -286,11 +291,18 @@ class CombatPulse( victim.skills.updateLevel(Skills.DEFENCE, -drain, victim.skills.getStaticLevel(Skills.DEFENCE) - drain) } } - victim.walkingQueue.reset() + if (!victim.locks.isMovementLocked) + victim.walkingQueue.reset() } setVictim(victim) entity.onAttack(victim as Entity?) - entity.pulseManager.run(this) + if (isAttacking) { + victim.scripts.removeWeakScripts() + } + + if (!isAttacking) { + entity.pulseManager.run(this) + } } /** @@ -345,6 +357,7 @@ class CombatPulse( override fun start() { super.start() entity!!.face(victim) + entity.walkingQueue.reset() } override fun stop() { @@ -418,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) @@ -429,20 +443,20 @@ class CombatPulse( GameWorld.Pulser.submit(object : Pulse(delay - 1, entity, victim) { var impact = false override fun pulse(): Boolean { - if (DeathTask.isDead(victim)) { + 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/java/core/game/node/entity/combat/CombatStyle.java b/Server/src/main/core/game/node/entity/combat/CombatStyle.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/combat/CombatStyle.java rename to Server/src/main/core/game/node/entity/combat/CombatStyle.java index cf751001a..eaa4c3140 100644 --- a/Server/src/main/java/core/game/node/entity/combat/CombatStyle.java +++ b/Server/src/main/core/game/node/entity/combat/CombatStyle.java @@ -1,10 +1,6 @@ package core.game.node.entity.combat; import core.game.node.entity.player.link.prayer.PrayerType; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MagicSwingHandler; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; -import rs09.game.node.entity.combat.handlers.RangeSwingHandler; /** * Represents the different styles of combat. diff --git a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt new file mode 100644 index 000000000..d4324b233 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt @@ -0,0 +1,685 @@ +package core.game.node.entity.combat + +import core.game.component.Component +import core.game.container.impl.EquipmentContainer +import core.game.node.Node +import core.game.node.entity.Entity +import core.game.node.entity.combat.equipment.* +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.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 +import core.game.world.map.RegionManager.getClippingFlag +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.game.system.config.ItemConfigParser +import core.tools.Log +import org.rs09.consts.Sounds +import java.util.* +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. + */ + private var specialHandlers: MutableMap? = null + + /** + * Starts the combat swing. + * @param entity The attacking entity. + * @param victim The victim. + * @param state The battle state instance. + * @return The amount of ticks before impact of the attack. + */ + abstract fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int + + /** + * Handles the impact of the combat swing (victim getting hit). + * @param entity The attacking entity. + * @param victim The victim. + * @param state The battle state instance. + */ + abstract fun impact(entity: Entity?, victim: Entity?, state: BattleState?) + + /** + * Visualizes the impact itself (end animation, end GFX, ...) + * @param entity The attacking entity. + * @param victim The victim. + * @param state The battle state instance. + */ + abstract fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) + + /** + * Calculates the maximum accuracy of the entity. + * @param entity The entity. + * @return The maximum accuracy value. + */ + abstract fun calculateAccuracy(entity: Entity?): Int + + /** + * Calculates the maximum strength of the entity. + * @param entity The entity. + * @param victim The victim. + * @param modifier The modifier. + * @return The maximum strength value. + */ + abstract fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int + + /** + * Calculates the maximum defence of the entity. + * @param victim The entity. + * @param attacker The entity to defend against. + * @return The maximum defence value. + */ + abstract fun calculateDefence(victim: Entity?, attacker: Entity?): Int + + /** + * Gets the void set multiplier. + * @param e The entity. + * @param skillId The skill id. + * @return The multiplier. + */ + abstract fun getSetMultiplier(e: Entity?, skillId: Int): Double + + /** + * Visualizes the combat swing (start animation, GFX, projectile, ...) + * @param entity The attacking entity. + * @param victim The victim. + * @param state The battle state instance. + */ + open fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { + entity.animate(getAttackAnimation(entity, type)) + } + + /** + * Method called when the impact method got called. + * @param entity The attacking entity. + * @param victim The victim. + * @param state The battle state. + */ + fun onImpact(entity: Entity?, victim: Entity?, state: BattleState?) { + if (entity is Player && victim != null) { + DegradableEquipment.degrade(entity as Player?, victim, true) + } + if (state == null) { + return + } + if (state.targets != null && state.targets.isNotEmpty()) { + if (!(state.targets.size == 1 && state.targets[0] == state)) { + for (s in state.targets) { + if (s != null && s != state) { + onImpact(entity, s.victim, s) + } + } + return + } + } + victim!!.onImpact(entity, state) + } + + /** + * Gets the currently worn armour set, if any. + * @param e The entity. + * @return The armour set worn. + */ + open fun getArmourSet(e: Entity?): ArmourSet? { + return null + } + + /** + * Checks if the hit will be accurate. + * @param entity The entity. + * @param victim The victim. + * @return `True` if the hit is accurate. + */ + fun isAccurateImpact(entity: Entity?, victim: Entity?): Boolean { + return isAccurateImpact(entity, victim, type, 1.0, 1.0) + } + + /** + * Checks if the hit will be accurate. + * @param entity The entity. + * @param victim The victim. + * @param style The combat style used. + * @return `True` if the hit is accurate. + */ + fun isAccurateImpact(entity: Entity?, victim: Entity?, style: CombatStyle?): Boolean { + return isAccurateImpact(entity, victim, style, 1.0, 1.0) + } + + /** + * Checks if the hit will be accurate. + * @param entity The entity. + * @param victim The victim. + * @param style The combat style (null to ignore prayers). + * @param accuracyMod The accuracy modifier. + * @param defenceMod The defence modifier. + * @return `True` if the hit is accurate. + */ + fun isAccurateImpact(entity: Entity?, victim: Entity?, style: CombatStyle?, accuracyMod: Double, defenceMod: Double): Boolean { + 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 + val defence = calculateDefence(victim, entity) * defenceMod + val chance: Double = if (attack > defence) { + 1 - ((defence + 2) / (2 * (attack + 1))) + } else { + attack / (2 * (defence + 1)) + } + return Math.random() < chance + } + + /** + * Checks if the entity can execute a combat swing at the victim. + * @param entity The entity. + * @param victim The victim. + * @return `True` if so. + */ + open fun canSwing(entity: Entity, victim: Entity): InteractionType? { + return isAttackable(entity, victim) + } + + /** + * Checks if the victim can be attacked by the entity. + * @param entity The attacking entity. + * @param victim The entity being attacked. + * @return `True` if so. + */ + open fun isAttackable(entity: Entity, victim: Entity): InteractionType? { + if (victim.getAttribute("return-to-spawn", false)) { + return InteractionType.NO_INTERACT + } + + if (type == CombatStyle.MELEE) { + val stepType = canStepTowards(entity, victim) + if (stepType != InteractionType.STILL_INTERACT) return stepType + } + + val comp = entity.getAttribute("autocast_component",null) as Component? + if((comp != null || type == CombatStyle.MAGIC) && (entity.properties.autocastSpell == null || entity.properties.autocastSpell.spellId == 0) && entity is Player){ + val weapEx = entity.getExtension(WeaponInterface::class.java) as WeaponInterface? + if(comp != null){ + entity.interfaceManager.close(comp) + entity.interfaceManager.openTab(weapEx) + entity.properties.combatPulse.stop() + entity.attack(victim) + entity.removeAttribute("autocast_component") + } + weapEx?.updateInterface() + entity.debug("Adjusting attack style") + } + if (entity.location == victim.location) { + return if (entity is Player && victim is Player && entity.clientIndex < victim.clientIndex && victim.properties.combatPulse.getVictim() === entity) { + InteractionType.STILL_INTERACT + } else InteractionType.NO_INTERACT + } + val el = entity.location + val vl = victim.location + val evl = vl.transform(victim.size(), victim.size(), 0) + if (el.x >= vl.x && el.x < evl.x && el.y >= vl.y && el.y < evl.y || el.z != vl.z) { + return InteractionType.NO_INTERACT + } + return InteractionType.STILL_INTERACT + } + + 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 + + //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() + for (i in 0 until maxIterations) { //step towards the target tile, checking if anything would obstruct us on the way, and immediately breaking + returning if it does. + next = next.transform(dir) + result = checkStepInterval(dir, next) + if (result == InteractionType.NO_INTERACT) break + } + + + return result + } + + private fun checkStepInterval( + dir: Direction, + next: Location + ): InteractionType { + val components = next.getStepComponents(dir) + + when (dir) { + Direction.NORTH -> if (getClippingFlag(next) and PREVENT_NORTH != 0) return InteractionType.NO_INTERACT + Direction.EAST -> if (getClippingFlag(next) and PREVENT_EAST != 0) return InteractionType.NO_INTERACT + Direction.SOUTH -> if (getClippingFlag(next) and PREVENT_SOUTH != 0) return InteractionType.NO_INTERACT + Direction.WEST -> if (getClippingFlag(next) and PREVENT_WEST != 0) return InteractionType.NO_INTERACT + + Direction.NORTH_EAST -> { + if (getClippingFlag(components[0]) and PREVENT_EAST != 0 + || getClippingFlag(components[1]) and PREVENT_NORTH != 0 + || getClippingFlag(next) and PREVENT_NORTHEAST != 0 + ) return InteractionType.NO_INTERACT + } + + Direction.NORTH_WEST -> { + if (getClippingFlag(components[0]) and PREVENT_WEST != 0 + || getClippingFlag(components[1]) and PREVENT_NORTH != 0 + || getClippingFlag(next) and PREVENT_NORTHWEST != 0 + ) return InteractionType.NO_INTERACT + } + + Direction.SOUTH_EAST -> { + if (getClippingFlag(components[0]) and PREVENT_EAST != 0 + || getClippingFlag(components[1]) and PREVENT_SOUTH != 0 + || getClippingFlag(next) and PREVENT_SOUTHEAST != 0 + ) return InteractionType.NO_INTERACT + } + + Direction.SOUTH_WEST -> { + if (getClippingFlag(components[0]) and PREVENT_WEST != 0 + || getClippingFlag(components[1]) and PREVENT_SOUTH != 0 + || getClippingFlag(next) and PREVENT_SOUTHWEST != 0 + ) return InteractionType.NO_INTERACT + } + + } + + return InteractionType.STILL_INTERACT + } + + /** + * Gets the dragonfire message. + * @param protection The protection value. + * @param fireName The fire breath name. + * @return The message to send. + */ + fun getDragonfireMessage(protection: Int, fireName: String): String { + if (protection and 0x4 != 0) { + if (protection and 0x2 != 0) { + return "Your potion and shield fully protects you from the dragon's $fireName." + } + return "Your shield absorbs most of the dragon's $fireName." + } + if (protection and 0x2 != 0) { + return "Your antifire potion helps you defend the against 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." + } + + /** + * Visualizes the audio. + * @param entity the entity. + * @param victim the victim. + * @param state the state. + */ + fun visualizeAudio(entity: Entity, victim: Entity, state: BattleState) { + if (entity is Player) { + val styleIndex = entity.settings.attackStyleIndex + if (state.weapon != null && state.weapon.item != null) { + val weapon = state.weapon.item + val audios = weapon.definition.getConfiguration>(ItemConfigParser.ATTACK_AUDIO, null) + if (audios != null) { + var audio: Audio? = null + if (styleIndex < audios.size) { + audio = audios[styleIndex] + } + if (audio == null || audio.id == 0) { + audio = audios[0] + } + playGlobalAudio(entity.location, audio.id) + } + } 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) { + val npc = entity.asNpc() + val audio = npc.getAudio(0) + if (audio != null) playGlobalAudio(entity.location, audio.id) + } + } + + /** + * Gets the combat distance. + * @param e The entity. + * @param v The victim. + * @param rawDistance The distance. + * @return The actual distance used for combat. + */ + open fun getCombatDistance(e: Entity, v: Entity, rawDistance: Int): Int { + var distance = rawDistance + if (e is NPC) { + if (e.definition.combatDistance > 0) { + distance = e.definition.combatDistance + } + } + return (e.size() shr 1) + (v.size() shr 1) + distance + } + + /** + * Formats the hit for the victim. (called as + * victim.getSwingHandler(false).formatHit(victim, hit)) + * @param victim The entity receiving the hit. + * @param rawHit The hit to format. + * @return The formatted hit. + */ + fun formatHit(victim: Entity, rawHit: Int): Int { + var hit = rawHit + if (hit < 1) { + return hit + } + if (hit > victim.skills.lifepoints) { + hit = victim.skills.lifepoints + } + return hit + } + + /** + * Adjusts the battle state object for this combat swing. + * @param entity The attacking entity. + * @param victim The victim. + * @param state The battle state. + */ + open fun adjustBattleState(entity: Entity, victim: Entity, state: BattleState) { + EXPERIENCE_MOD = 4.0 + var totalHit = 0 + if (entity is Player) { + entity.familiarManager.adjustBattleState(state) + } + entity.sendImpact(state) + victim.checkImpact(state) + //Prevents lumbridge dummies from dying (true to how rs3 / 2009scape in 2009 does it) + if (victim.id == 4474 && type == CombatStyle.MAGIC || victim.id == 7891 && type == CombatStyle.MELEE) { + EXPERIENCE_MOD = 0.1 + victim.fullRestore() + if (state.estimatedHit >= 15) { + state.estimatedHit = 14 + } + if (state.secondaryHit >= 15) { + state.secondaryHit = 14 + } + } + if (victim.id == 757) { + EXPERIENCE_MOD = 0.01 + } + // Recursively adjustBattleState targets so that multi-target attacks have protection prayers applied. + if (state.targets != null && state.targets.isNotEmpty()) { + if (!(state.targets.size == 1 && state.targets[0] == state)) { + for (s in state.targets) { + if (s != null && s != state) { + adjustBattleState(entity, victim, s) + } + } + } + } + if (state.estimatedHit > 0) { + state.estimatedHit = getFormattedHit(entity, victim, state, state.estimatedHit) + totalHit += state.estimatedHit + } + if (state.secondaryHit > 0) { + state.secondaryHit = getFormattedHit(entity, victim, state, state.secondaryHit) + totalHit += state.secondaryHit + } + if (entity is Player) { + entity.degrader.checkWeaponDegrades(entity) + if (totalHit > 0 && entity.prayer[PrayerType.SMITE] && victim.skills.prayerPoints > 0) { + victim.skills.decrementPrayerPoints(totalHit * 0.25) + } + if (entity.getAttribute("1hko", false)) { + state.estimatedHit = victim.skills.lifepoints + } + } + if (victim is NPC) { + if (victim.properties.protectStyle != null && state.style == victim.properties.protectStyle) { + state.neutralizeHits() + } + } + } + + /** + * Adds the experience for the current combat swing. + * @param entity The attacking entity. + * @param victim The victim. + * @param state The battle state. + */ + open fun addExperience(entity: Entity?, victim: Entity?, state: BattleState?) { + if (entity == null || (victim is Player && entity is Player && entity.asPlayer().ironmanManager.isIronman)) { + return + } + var player: Player + var attStyle: Int + + when(entity) + { + is Familiar -> {player = entity.owner; attStyle = entity.attackStyle} + 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) { + 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 + when (attStyle) { + WeaponInterface.STYLE_DEFENSIVE -> skill = Skills.DEFENCE + WeaponInterface.STYLE_ACCURATE -> skill = Skills.ATTACK + WeaponInterface.STYLE_AGGRESSIVE -> skill = Skills.STRENGTH + WeaponInterface.STYLE_CONTROLLED -> { + var experience = hit * EXPERIENCE_MOD + experience /= 3.0 + player.skills.addExperience(Skills.ATTACK, experience, true) + player.skills.addExperience(Skills.STRENGTH, experience, true) + player.skills.addExperience(Skills.DEFENCE, experience, true) + return + } + + WeaponInterface.STYLE_RANGE_ACCURATE -> skill = Skills.RANGE + WeaponInterface.STYLE_RAPID -> skill = Skills.RANGE + WeaponInterface.STYLE_LONG_RANGE -> { + player.skills.addExperience(Skills.RANGE, hit * (EXPERIENCE_MOD / 2), true) + player.skills.addExperience(Skills.DEFENCE, hit * (EXPERIENCE_MOD / 2), true) + return + } + + WeaponInterface.STYLE_CAST -> skill = Skills.MAGIC + WeaponInterface.STYLE_DEFENSIVE_CAST -> { + 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 + } + } + if (skill < 0) return + player.skills.addExperience(skill, hit * EXPERIENCE_MOD, true) + } + } + + /** + * 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. + * @param victim The victim. + * @param state The battle state. + * @param rawHit The hit to format. + * @return The formated hit. + */ + protected open fun getFormattedHit(attacker: Entity, victim: Entity, state: BattleState, rawHit: Int): Int { + var hit = rawHit + hit = attacker.getFormattedHit(state, hit).toInt() + if (victim is Player) { + val player = victim.asPlayer() + val shield = player.equipment[EquipmentContainer.SLOT_SHIELD] + if (shield != null && shield.id == 13742) { + if (RandomFunction.random(100) < 71) { + hit -= (hit.toDouble() * 0.25).toInt() + if (hit < 1) { + hit = 0 + } + } + } + if (shield != null && shield.id == 13740) { + val reduce = hit.toDouble() * 0.30 + var drain = hit * 0.15 + if (player.skills.prayerPoints > drain && drain > 0) { + if (drain < 1) { + drain = 1.0 + } + hit -= reduce.toInt() + if (hit < 1) { + hit = 0 + } + player.skills.decrementPrayerPoints(drain) + } + } + } + if (attacker is Player) { + val player = attacker.asPlayer() + if (player.equipment[3] != null && player.equipment[3].id == 14726 && state.style == CombatStyle.MAGIC) { + hit += (hit.toDouble() * 0.15).toInt() + } + } + if (attacker is Familiar && victim is Player) { + if (victim.prayer[PrayerType.PROTECT_FROM_SUMMONING]) { + hit = 0 + } + } + return formatHit(victim, hit) + } + + /** + * Gets the default animation of the entity. + * @param e The entity. + * @param style The combat style. + * @return The attack animation. + */ + fun getAttackAnimation(e: Entity, style: CombatStyle?): Animation { + var anim: Animation? = null + if (type != null && e is NPC) { + anim = e.properties.getCombatAnimation(style!!.ordinal % 3) + } + return anim ?: e.properties.attackAnimation + } + + /** + * Registers a special attack handler. + * @param itemId The item id. + * @param handler The combat swing handler. + * @return `True` if succesful. + */ + fun register(itemId: Int, handler: CombatSwingHandler): Boolean { + if (specialHandlers == null) { + specialHandlers = HashMap() + } + if (specialHandlers!!.containsKey(itemId)) { + 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 + } + + /** + * Gets the special attack handler for the given item id. + * @param itemId The item id. + * @return The special attack handler, or `null` if this item has no + * special attack handler. + */ + fun getSpecial(itemId: Int): CombatSwingHandler? { + if (specialHandlers == null) { + specialHandlers = HashMap() + } + return specialHandlers!![itemId] + } + + companion object { + /** + * The amount of experience to get per hit. + */ + @JvmField + var EXPERIENCE_MOD = 4.0 + + /** + * Checks if a projectile can be fired from the node location to the victim + * location. + * @param entity The node. + * @param victim The victim. + * @param checkClose If we are checking for a melee attack rather than a + * projectile. + * @return `True` if so. + */ + @JvmStatic + fun isProjectileClipped(entity: Node, victim: Node?, checkClose: Boolean): Boolean { + for(x1 in 0 until entity.size()) { + for(y1 in 0 until entity.size()) { + val src = entity.location.transform(x1, y1, 0) + for(x2 in 0 until victim!!.size()) { + for(y2 in 0 until victim!!.size()) { + val dst = victim!!.location.transform(x2, y2, 0) + 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 + } + } + } + } + } + return false + } + } + +} + +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/java/core/game/node/entity/combat/DeathTask.java b/Server/src/main/core/game/node/entity/combat/DeathTask.java similarity index 82% rename from Server/src/main/java/core/game/node/entity/combat/DeathTask.java rename to Server/src/main/core/game/node/entity/combat/DeathTask.java index 83922c552..da308856d 100644 --- a/Server/src/main/java/core/game/node/entity/combat/DeathTask.java +++ b/Server/src/main/core/game/node/entity/combat/DeathTask.java @@ -1,12 +1,12 @@ package core.game.node.entity.combat; +import core.game.event.SelfDeathEvent; import core.game.container.Container; import core.game.container.ContainerType; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.impl.Animator; +import core.game.node.entity.impl.PulseType; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.IronmanMode; @@ -15,9 +15,11 @@ import core.game.node.entity.player.link.prayer.PrayerType; import core.game.node.item.Item; import core.game.system.task.NodeTask; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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); @@ -69,14 +68,6 @@ public final class DeathTask extends NodeTask { if (ticks < 1 || ticks > 30) { ticks = 6; } - if (node instanceof Player) { - if (TutorialSession.getExtension(((Player) node)).getStage() == 52) { - TutorialStage.load(((Player) node), 53, false); - } - if (TutorialSession.getExtension(((Player) node)).getStage() == 54) { - TutorialStage.load(((Player) node), 55, false); - } - } return e.getAttribute("tick:death", -1) <= GameWorld.getTicks() - ticks; } @@ -86,14 +77,15 @@ public final class DeathTask extends NodeTask { Entity killer = n.length > 0 ? (Entity) n[0] : e; e.removeAttribute("state:death"); e.removeAttribute("tick:death"); - Location spawn = e.getProperties().getSpawnLocation(); + Location spawn = e.getProperties().isSafeZone() ? e.getProperties().safeRespawn : e.getProperties().getSpawnLocation(); e.getAnimator().forceAnimation(Animator.RESET_A); 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)); } @Override @@ -160,13 +152,11 @@ public final class DeathTask extends NodeTask { @SuppressWarnings("deprecation") public static void startDeath(Entity entity, Entity killer) { if (!isDead(entity)) { - entity.getPulseManager().clear(); if (killer == null) { killer = entity; } - Pulse pulse = SINGLETON.schedule(entity, killer); - pulse.start(); - entity.getPulseManager().set(pulse); + Pulse pulse = new DeathTask().schedule(entity, killer); + entity.getPulseManager().run(pulse, PulseType.STRONG); } } @@ -176,7 +166,8 @@ public final class DeathTask extends NodeTask { * @return {@code True} if so. */ public static boolean isDead(Entity e) { - return e.getAttribute("state:death", false); + if (e instanceof NPC) return ((NPC) e).getRespawnTick() > GameWorld.getTicks() || e.getAttribute("state:death", false); + else return e.getAttribute("state:death", false); } /** @@ -187,4 +178,4 @@ public final class DeathTask extends NodeTask { return SINGLETON; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/node/entity/combat/ImpactHandler.java b/Server/src/main/core/game/node/entity/combat/ImpactHandler.java similarity index 79% rename from Server/src/main/java/core/game/node/entity/combat/ImpactHandler.java rename to Server/src/main/core/game/node/entity/combat/ImpactHandler.java index 46fe57c21..b3a6a6bec 100644 --- a/Server/src/main/java/core/game/node/entity/combat/ImpactHandler.java +++ b/Server/src/main/core/game/node/entity/combat/ImpactHandler.java @@ -1,25 +1,25 @@ package core.game.node.entity.combat; -import rs09.ServerConstants; +import content.data.EnchantedJewellery; import core.game.container.impl.EquipmentContainer; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.pet.Pet; +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; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +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. @@ -138,6 +143,11 @@ public final class ImpactHandler { * @return The impact object created. */ public Impact handleImpact(Entity source, int hit, final CombatStyle style, final BattleState state, HitsplatType type, boolean secondary) { + if (DeathTask.isDead(this.entity)) { + this.entity.getProperties().getCombatPulse().setVictim(null); + this.entity.getProperties().getCombatPulse().stop(); + return null; + } boolean fam = source instanceof Familiar; if (fam) { source = ((Familiar) source).getOwner(); @@ -145,7 +155,7 @@ public final class ImpactHandler { if (disabledTicks > GameWorld.getTicks()) { return null; } - if (entity instanceof Player && TutorialSession.getExtension((Player) entity).getStage() < TutorialSession.MAX_STAGE) { + if (entity instanceof Player && !(entity instanceof AIPlayer) && !(entity.getAttribute("tutorial:complete",false))) { Impact impact = new Impact(source, 0, style, HitsplatType.MISS); impactQueue.add(impact); return impact; @@ -161,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(); @@ -195,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."); + } } } } @@ -241,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; } /** @@ -383,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/java/core/game/node/entity/combat/InteractionType.java b/Server/src/main/core/game/node/entity/combat/InteractionType.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/combat/InteractionType.java rename to Server/src/main/core/game/node/entity/combat/InteractionType.java diff --git a/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt new file mode 100644 index 000000000..0062c20d9 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/MagicSwingHandler.kt @@ -0,0 +1,254 @@ +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 +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.tools.RandomFunction +import kotlin.math.floor + +/** + * Handles the magic combat swings. + * @author Emperor + * @author Ceikry, Kotlin conversion + cleanup + */ +open class MagicSwingHandler (vararg flags: SwingHandlerFlag) + : CombatSwingHandler(CombatStyle.MAGIC, *flags) { + + override fun canSwing(entity: Entity, victim: Entity): InteractionType? { + if (!isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_INTERACT + } + var distance = 10 + var type = InteractionType.STILL_INTERACT + var goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) + if (victim.walkingQueue.isMoving && !goodRange) { + goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) + type = InteractionType.MOVE_INTERACT + } + if (goodRange && isAttackable(entity, victim) != 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 spell = entity!!.properties.spell + if (spell == null) { + if (entity.properties.autocastSpell.also { spell = it } == null) { + return -1 + } + } + state!!.style = CombatStyle.MAGIC + if (!spell!!.meetsRequirements(entity, true, true)) { + entity.properties.spell = null + val inter = entity.getExtension(WeaponInterface::class.java) + if (inter != null) { + inter.selectAutoSpell(-1, true) + entity.properties.combatPulse.updateStyle() + } + return -1 + } + val max = calculateHit(entity, victim, spell!!.getMaximumImpact(entity, victim, state).toDouble()) + state.targets = spell!!.getTargets(entity, victim) + state.spell = spell + for (s in state.targets) { + var hit = -1 + s.spell = spell + if (isAccurateImpact(entity, s.victim, CombatStyle.MAGIC)) { + s.maximumHit = max + hit = RandomFunction.random(max) + } + s.style = CombatStyle.MAGIC + s.estimatedHit = hit + } + if (spell === entity.properties.spell) { + entity.properties.spell = null + if (entity.properties.autocastSpell == null) { + entity.properties.combatPulse.stop() + } + } + var ticks = 2 + floor(entity.location.getDistance(victim!!.location) * 0.5).toInt() + if (spell!!.type === SpellType.BLITZ) { + ticks++ + } + 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) + return ticks + } + + override fun adjustBattleState(entity: Entity, victim: Entity, state: BattleState) { + if (state.targets == null) { + super.adjustBattleState(entity, victim, state) + if (state.spell != null) { + state.spell.fireEffect(entity, victim, state) + } + return + } + for (s in state.targets) { + if (s != null) { + super.adjustBattleState(entity, s.victim, s) + if (s.spell != null) { + s.spell.fireEffect(entity, s.victim, s) + } + } + } + } + + override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { + state!!.spell.visualize(entity, victim) + } + + override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { + if (state!!.targets == null) { + if (state.estimatedHit > -1) { + victim!!.impactHandler.handleImpact(entity, state.estimatedHit, CombatStyle.MAGIC, state) + } + return + } + for (s in state.targets) { + if (s == null || s.estimatedHit < 0) { + continue + } + val hit = s.estimatedHit + s.victim.impactHandler.handleImpact(entity, hit, CombatStyle.MAGIC, s) + } + } + + override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { + if (state!!.targets == null) { + if (state.spell != null) { + state.spell.visualizeImpact(entity, victim, state) + } + return + } + for (s in state.targets) { + if (s != null) { + state.spell.visualizeImpact(entity, s.victim, s) + } + } + } + + override fun calculateAccuracy(entity: Entity?): Int { + 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 + } + } + + return 0 + } + + override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { + var baseDamage = modifier + if (baseDamage < 2.0 || entity is NPC) { + if (baseDamage > 2.0) { + baseDamage = 1.0 + } + val level = entity!!.skills.getLevel(Skills.MAGIC, true) + val bonus = entity.properties.bonuses[if (entity is Player) 14 else 13] + val cumulativeStr = level.toDouble() + return 1 + ((14 + cumulativeStr + bonus.toDouble() / 8 + cumulativeStr * bonus * 0.016865) * baseDamage).toInt() / 10 + } + var levelMod = 1.0 + val entityMod = entity!!.getLevelMod(entity, victim) + if (entityMod != 0.0) { + levelMod += entityMod + } + return (baseDamage * levelMod).toInt() + 1 + } + + override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { + 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 + } + } + + return 0 + } + + override fun getSetMultiplier(e: Entity?, skillId: Int): Double { + if(skillId == Skills.MAGIC) { + if(e is Player && e.isWearingVoid(CombatStyle.MAGIC)) { + return 1.3 + } + } + 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) + return + } + + super.addExperience(entity, victim, state) + } + + + override fun getArmourSet(e: Entity?): ArmourSet? { + return if (ArmourSet.AHRIM.isUsing(e)) { + ArmourSet.AHRIM + } else super.getArmourSet(e) + } +} diff --git a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt new file mode 100644 index 000000000..7f9b668d8 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt @@ -0,0 +1,371 @@ +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 +import core.game.node.entity.Entity +import core.game.node.entity.combat.equipment.ArmourSet +import core.game.node.entity.combat.equipment.Weapon +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.world.map.path.Pathfinder +import core.tools.RandomFunction +import org.rs09.consts.Items +import kotlin.math.ceil +import kotlin.math.floor + +/** + * Handles a melee combat swing. + * @author Emperor + * @author Ceikry, Kotlin conversion + cleanup + */ +open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) +/** + * Constructs a new `MeleeSwingHandler` {@Code Object}. + */ + : 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 + state!!.style = CombatStyle.MELEE + if (entity is Player) { + state.weapon = Weapon(entity.equipment[3]) + } + if (entity!!.properties.armourSet == ArmourSet.VERAC && RandomFunction.roll(4)) { + state.armourEffect = ArmourSet.VERAC + } + 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) + } + state.estimatedHit = hit + if(victim != null) { + 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) + } + return 1 + } + + override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { + entity.animate(entity.properties.attackAnimation) + } + + override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { + var targets = state!!.targets + if (targets == null) { + targets = arrayOf(state) + } + for (s in targets) { + var hit = s!!.estimatedHit + if (hit > -1) { + victim!!.impactHandler.handleImpact(entity, hit, CombatStyle.MELEE, s) + } + hit = s.secondaryHit + if (hit > -1) { + victim!!.impactHandler.handleImpact(entity, hit, CombatStyle.MELEE, s) + } + } + } + + override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { + victim!!.animate(victim.properties.defenceAnimation) + } + + override fun adjustBattleState(entity: Entity, victim: Entity, state: BattleState) { + if (entity is Player) { + if (state.estimatedHit > 0) { + val name = entity.equipment.getNew(3).name + var damage = -1 + if (name.contains("(p++") || name.contains("(s)") || name.contains("(kp)")) { + damage = 30 + } else if (name.contains("(p+)")) { + damage = 25 + } else if (name.contains("(p)")) { + damage = 20 + } + if (damage > -1 && RandomFunction.random(10) < 4) { + applyPoison (victim, entity, damage) + } + } + } else if (entity is NPC) { + val poisonous = entity.isPoisonous + val damage = entity.poisonSeverity() + + if (poisonous && damage > -1 && RandomFunction.random(10) < 4) { + applyPoison (victim, entity, damage) + } + } + super.adjustBattleState(entity, victim, state) + } + + 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 + + val styleAttackBonus = entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64 + when (entity) { + is Player -> { + var effectiveAttackLevel = entity.skills.getLevel(Skills.ATTACK).toDouble() + if(!flags.contains(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY)) + effectiveAttackLevel = floor(effectiveAttackLevel + (entity.prayer.getSkillBonus(Skills.ATTACK) * effectiveAttackLevel)) + if(entity.properties.attackStyle.style == WeaponInterface.STYLE_ACCURATE) effectiveAttackLevel += 3 + else if(entity.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveAttackLevel += 1 + effectiveAttackLevel += 8 + if(SkillcapePerks.isActive(SkillcapePerks.PRECISION_STRIKES, entity)){ //Attack skillcape perk + effectiveAttackLevel += 6 + } + effectiveAttackLevel *= getSetMultiplier(entity, Skills.ATTACK) + effectiveAttackLevel = floor(effectiveAttackLevel) + if (!flags.contains(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY)) + effectiveAttackLevel *= styleAttackBonus + else effectiveAttackLevel *= 64 + + val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none" + + // 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 0 + + + } + + override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { + 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() + } + } + + return 0 + } + + override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { + //authentic formula, taken from OSRS wiki: https://oldschool.runescape.wiki/w/Damage_per_second/Melee#Step_five:_Calculate_the_Defence_roll + victim ?: return 0 + attacker ?: return 0 + + 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 || victim.properties.attackStyle.style == WeaponInterface.STYLE_LONG_RANGE) effectiveDefenceLevel += 3 + else if (victim.properties.attackStyle.style == WeaponInterface.STYLE_CONTROLLED) effectiveDefenceLevel += 1 + effectiveDefenceLevel += 8 + effectiveDefenceLevel *= getSetMultiplier(victim, Skills.DEFENCE) + effectiveDefenceLevel *= familiarDefenceBonus(victim) + return effectiveDefenceLevel.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 (e!!.properties.armourSet === ArmourSet.DHAROK && skillId == Skills.STRENGTH) { + + 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)) { + multiplier += 0.1 + } + 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? { + if (ArmourSet.DHAROK.isUsing(e)) { + return ArmourSet.DHAROK + } + if (ArmourSet.GUTHAN.isUsing(e)) { + return ArmourSet.GUTHAN + } + 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) + } + + /** + * Check to see whether an NPC is classified as undead. + * @param name + * @return true if so + */ + private fun checkUndead(name: String): Boolean { + return (name == "Zombie" || name.contains("rmoured") || name == "Ankou" || name == "Crawling Hand" || name == "Banshee" || name == "Ghost" || name == "Ghast" || name == "Mummy" || name.contains("Revenant") + || 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. + * @param entity The entity. + * @return `True` if so. + */ + private fun usingHalberd(entity: Entity): Boolean { + if (entity is Player) { + val weapon = entity.equipment[EquipmentContainer.SLOT_WEAPON] + if (weapon != null) { + return weapon.id in 3190..3204 || weapon.id == 6599 + } + } else if (entity is NPC) { + return entity.id == 8612 + } + return false + } + + /** + * Checks if the entity can execute a melee swing from its current location. + * @param entity The attacking entity. + * @param victim The victim. + * @return `True` if so. + */ + fun canMelee(entity: Entity, victim: Entity?, distance: Int): Boolean { + val e = entity.location + if (victim == null) { + return false + } + if (entity.id == 7135 && entity.location.withinDistance(victim.location, 2)) { + return true + } + val x = victim.location.x + val y = victim.location.y + val size = entity.size() + if (distance == 1) { + for (i in 0 until size) { + if (Pathfinder.isStandingIn(e.x - 1, e.y + i, 1, 1, x, y, victim.size(), victim.size())) { + return true + } + if (Pathfinder.isStandingIn(e.x + size, e.y + i, 1, 1, x, y, victim.size(), victim.size())) { + return true + } + if (Pathfinder.isStandingIn(e.x + i, e.y - 1, 1, 1, x, y, victim.size(), victim.size())) { + return true + } + if (Pathfinder.isStandingIn(e.x + i, e.y + size, 1, 1, x, y, victim.size(), victim.size())) { + return true + } + } + if (e == victim.location) { + return true + } + return victim.getSwingHandler(false).type == CombatStyle.MELEE && e.withinDistance(victim.location, 1) && victim.properties.combatPulse.getVictim() === entity && entity.index < victim.index + } + return entity.centerLocation.withinDistance(victim.centerLocation, distance + (size shr 1) + (victim.size() shr 1)) + } + } +} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MultiSwingHandler.kt b/Server/src/main/core/game/node/entity/combat/MultiSwingHandler.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MultiSwingHandler.kt rename to Server/src/main/core/game/node/entity/combat/MultiSwingHandler.kt index ebfcf75f0..a3d6d5fec 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MultiSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MultiSwingHandler.kt @@ -1,10 +1,6 @@ -package rs09.game.node.entity.combat.handlers +package core.game.node.entity.combat import core.game.node.entity.Entity -import core.game.node.entity.combat.BattleState -import core.game.node.entity.combat.CombatStyle -import rs09.game.node.entity.combat.CombatSwingHandler -import core.game.node.entity.combat.InteractionType import core.game.node.entity.combat.equipment.SwitchAttack import core.game.node.entity.npc.NPC import core.tools.RandomFunction @@ -12,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) { /** @@ -31,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 @@ -123,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 new file mode 100644 index 000000000..5eeed5f21 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt @@ -0,0 +1,434 @@ +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.* +import core.game.node.entity.combat.equipment.Weapon.WeaponType +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.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 java.util.* +import kotlin.math.ceil +import kotlin.math.floor + +/** + * Handles the range combat swings. + * @author Emperor + * @author Ceikry, conversion to Kotlin + cleanup + */ +open class RangeSwingHandler (vararg flags: SwingHandlerFlag) : CombatSwingHandler(CombatStyle.RANGE, *flags) { +/** + * Constructs a new `RangeSwingHandler` {@Code Object}. + */ + override fun canSwing(entity: Entity, victim: Entity): InteractionType? { + if (!isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_INTERACT + } + var distance = 7 + if (entity is Player && (entity.getExtension(WeaponInterface::class.java) as WeaponInterface).weaponInterface.interfaceId == 91) { + distance -= 2 + } + if (entity.properties.attackStyle.style == WeaponInterface.STYLE_LONG_RANGE) { + distance += 2 + } + if (entity is Player) { + val rw = RangeWeapon.get(entity.getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId()) + if(rw != null && (rw.weaponType == WeaponType.DOUBLE_SHOT || rw.weaponType == WeaponType.DEGRADING)) { + // Dark bow and crystal bow have a 10-square range, independent of whether longrange stance is used + distance = 10 + } + } + var goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) + var type = InteractionType.STILL_INTERACT + if (victim.walkingQueue.isMoving && !goodRange) { + goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) + type = InteractionType.MOVE_INTERACT + } + if (goodRange && super.canSwing(entity, victim) != 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 { + configureRangeData(entity, state) + if (state!!.weapon == null || !hasAmmo(entity, state)) { + entity!!.properties.combatPulse.stop() + return -1 + } + var hit = 0 + 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) + 1) + } + state.secondaryHit = hit + } + if (entity == null || victim!!.location == null) { + return -1 + } + 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) + 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. + * @param state The battle state. + */ + fun configureRangeData(entity: Entity?, state: BattleState?) { + state!!.style = CombatStyle.RANGE + val w: Weapon? + if (entity is Player) { + val rw = RangeWeapon.get(entity.equipment.getNew(3).id) + if (rw == null) { + 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)) + w.type = rw.weaponType + state.rangeWeapon = rw + state.ammunition = Ammunition.get(w.ammunition.id) + } else { + w = Weapon(null) + } + state.weapon = w + } + + override fun adjustBattleState(entity: Entity, victim: Entity, state: BattleState) { + if (state.ammunition != null && entity is Player) { + val damage = state.ammunition.poisonDamage + if (state.estimatedHit > 0 && damage > 8 && RandomFunction.random(10) < 4) { + applyPoison(victim, entity, damage) + } + } else if (entity is NPC) { + val poisonous = entity.isPoisonous + val damage = entity.poisonSeverity() + + if (poisonous && damage > -1 && RandomFunction.random(10) < 4) { + applyPoison (victim, entity, damage) + } + } + super.adjustBattleState(entity, victim, state) + } + + override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { + var start: Graphics? = null + if (state!!.ammunition != null) { + start = state.ammunition.startGraphics + state.ammunition.projectile.copy(entity, victim, 5.0).send() + if (state.weapon.type == WeaponType.DOUBLE_SHOT && state.ammunition.darkBowGraphics != null) { + start = state.ammunition.darkBowGraphics + val speed = (55 + entity.location.getDistance(victim!!.location) * 10).toInt() + Projectile.create(entity, victim, state.ammunition.projectile.projectileId, 40, 36, 41, speed, 25).send() + } + } else if (entity is NPC) { + if (entity.definition.combatGraphics[0] != null) { + start = entity.definition.combatGraphics[0] + } + val g = entity.definition.combatGraphics[1] + if (g != null) { + Projectile.ranged(entity, victim, g.id, g.height, 36, 41, 5).send() + } + } + val weapon: RangeWeapon? = state.weapon?.let { RangeWeapon.get(it.id) } + val anim = entity.properties.attackAnimation.id + weapon?.let { + if ((anim == 422 || anim == 423)) { + entity.visualize(it.animation, start) + return + } + } + entity.visualize(entity.properties.attackAnimation, start) + } + + override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { + 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 + victim!!.impactHandler.handleImpact(entity, hit, CombatStyle.RANGE, state) + if (state.secondaryHit > -1) { + val hitt = state.secondaryHit + GameWorld.Pulser.submit(object : Pulse(1, victim) { + override fun pulse(): Boolean { + victim.impactHandler.handleImpact(entity, hitt, CombatStyle.RANGE, state) + return true + } + }) + } + } + + override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { + victim!!.animate(victim.properties.defenceAnimation) + } + + override fun calculateAccuracy(entity: Entity?): Int { + entity ?: return 0 + + 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 + + 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 { + 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() + } + } + + return 0 + } + + override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { + victim ?: return 0 + attacker ?: return 0 + + val styleDefenceBonus = victim.properties.bonuses[attacker.properties.attackStyle.bonusType + 5] + 64 + 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.2 + } + } + return 1.0 + } + + override fun getArmourSet(e: Entity?): ArmourSet? { + return if (ArmourSet.KARIL.isUsing(e)) { + ArmourSet.KARIL + } 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. + * @param e The entity. + * @param state The battle state. + * @return `True` if so. + */ + fun hasAmmo(e: Entity?, state: BattleState?): Boolean { + if (e !is Player) { + return true + } + val type = state!!.weapon.type + val amount = if (type == WeaponType.DOUBLE_SHOT) 2 else 1 + if (type == WeaponType.DEGRADING) { + return true + } + val item = e.equipment[state.weapon.ammunitionSlot] + if (item != null && item.amount >= amount) { + if (!state.rangeWeapon.ammunition.contains(item.id)) { + e.packetDispatch.sendMessage("You can't use this type of ammunition with this bow.") + return false + } + return true + } + if (type == WeaponType.DOUBLE_SHOT) { + state.weapon.type = WeaponType.DEFAULT + return hasAmmo(e, state) + } + e.packetDispatch.sendMessage("You do not have enough ammo left.") + return false + } + + /** + * Uses the ammunition for the range weapon. + * @param e The entity. + * @param state The battle state. + * @param location The drop location. + */ + fun useAmmo(e: Entity, state: BattleState, location: Location?) { + var dropLocation = location + if (e !is Player) { + return + } + val type = state.weapon.type + val amount = if (type == WeaponType.DOUBLE_SHOT) 2 else 1 + if (type == WeaponType.DEGRADING) { + return + } + val ammo = state.weapon.ammunition + if (state.weapon.ammunitionSlot == -1 || ammo == null) { + return + } + val dropRate = getDropRate(e) + if (dropRate == -1.0) { + return + } + e.equipment.replace(Item(ammo.id, ammo.amount - amount, ammo.charge), state.weapon.ammunitionSlot) + if (dropLocation == null) { + return + } + val flag = RegionManager.getClippingFlag(dropLocation) + if (flag and 0x200000 != 0) { //Water + dropLocation = null + } + if (dropLocation != null && state.rangeWeapon.isDropAmmo) { + val rate = 5 * (1.0 + e.skills.getLevel(Skills.RANGE) * 0.01) * dropRate + if (RandomFunction.randomize(rate.toInt()) != 0) { + val drop = GroundItemManager.increase(Item(ammo.id, amount), dropLocation, e) + if (drop != null) { + if (e.getAttribute("duel:ammo", null) != null) { + (e.getAttribute("duel:ammo") as ArrayList).add(drop) + } + } + } + } + if (e.equipment[state.rangeWeapon.ammunitionSlot] == null) { + e.packetDispatch.sendMessage("You have no ammo left in your quiver!") + } + } + + /** + * Checks if ammunition should be saved. + * @param e The entity. + * @return `True` if so. + */ + private fun getDropRate(e: Entity?): Double { + if (e is Player) { + val cape = e.equipment[EquipmentContainer.SLOT_CAPE] + val weapon = e.equipment[EquipmentContainer.SLOT_WEAPON] + if (cape != null && (cape.id == 10498 || cape.id == 10499) && weapon != null && weapon.id != 10034 && weapon.id != 10033) { + val rate = 80 + if (RandomFunction.random(100) < rate) { + val torso = e.equipment[EquipmentContainer.SLOT_CHEST] + val modelId = torso?.definition?.maleWornModelId1 ?: -1 + if (modelId == 301 || modelId == 306 || modelId == 3379) { + e.packetDispatch.sendMessage("Your armour interferes with Ava's device.") + return 1.0 + } + return (-1).toDouble() + } + return 0.33 + } + } + return 1.0 + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/combat/equipment/Ammunition.java b/Server/src/main/core/game/node/entity/combat/equipment/Ammunition.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/combat/equipment/Ammunition.java rename to Server/src/main/core/game/node/entity/combat/equipment/Ammunition.java index 33461f024..a7b895166 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/combat/equipment/ArmourSet.java b/Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/combat/equipment/ArmourSet.java rename to Server/src/main/core/game/node/entity/combat/equipment/ArmourSet.java index 16097c032..5b7a7e3af 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/combat/equipment/BoltEffect.java b/Server/src/main/core/game/node/entity/combat/equipment/BoltEffect.java similarity index 80% rename from Server/src/main/java/core/game/node/entity/combat/equipment/BoltEffect.java rename to Server/src/main/core/game/node/entity/combat/equipment/BoltEffect.java index 7fe5fb221..71d26d8b6 100644 --- a/Server/src/main/java/core/game/node/entity/combat/equipment/BoltEffect.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/BoltEffect.java @@ -7,10 +7,12 @@ 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 rs09.game.world.GameWorld; +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. @@ -19,7 +21,6 @@ import core.tools.RandomFunction; */ 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)); @@ -31,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) { @@ -57,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)); @@ -106,24 +105,19 @@ 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(2915)) { - + 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) { + 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); int playerPoints = (int) (state.getAttacker().getSkills().getLifepoints() * 0.10); - if (victimPoints >= 35) { - victimPoints = 35; + if (victimPoints >= 100 && state.getVictim().getId() == NPCs.CORPOREAL_BEAST_8133) { + victimPoints = 100; } - int total = state.getEstimatedHit() + victimPoints; - if (total >= 35) { - total = 35; - } - state.setEstimatedHit(total); + state.setEstimatedHit(victimPoints); state.getAttacker().getImpactHandler().manualHit(state.getVictim(), playerPoints, HitsplatType.NORMAL); super.impact(state); } @@ -137,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)); - 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)); @@ -167,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); @@ -216,43 +201,47 @@ public enum BoltEffect { * @param graphics the graphics. * @param sound the sound. */ - private BoltEffect(int itemId, Graphics graphics, Audio sound) { + BoltEffect(int itemId, Graphics graphics, Audio sound) { this.itemId = itemId; this.graphics = graphics; this.sound = sound; } - /** - * Constructs a new {@Code BoltEffect} {@Code Object} - * @param itemId the id. - * @param graphics the graphics. - */ - private BoltEffect(int itemId, Graphics graphics) { - this(itemId, graphics, null); - } - /** * Handles the impact. * @param state the battle state. */ - @SuppressWarnings("unused") public void impact(BattleState state) { Entity victim = state.getVictim(); - if (sound != null && victim instanceof Player) { - sound.send(victim.asPlayer(), true); + if (sound != null) { + Entity attacker = state.getAttacker(); + if (attacker instanceof Player) { + playGlobalAudio(attacker.getLocation(), sound.id); + } + if (victim instanceof Player) { + 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; } /** @@ -276,5 +265,4 @@ public enum BoltEffect { public int getItemId() { return itemId; } - } diff --git a/Server/src/main/java/core/game/node/entity/combat/equipment/DegradableEquipment.java b/Server/src/main/core/game/node/entity/combat/equipment/DegradableEquipment.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/combat/equipment/DegradableEquipment.java rename to Server/src/main/core/game/node/entity/combat/equipment/DegradableEquipment.java diff --git a/Server/src/main/java/core/game/node/entity/combat/equipment/FireType.java b/Server/src/main/core/game/node/entity/combat/equipment/FireType.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/combat/equipment/FireType.java rename to Server/src/main/core/game/node/entity/combat/equipment/FireType.java index d4d18d910..4e006d648 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/combat/equipment/RangeWeapon.java b/Server/src/main/core/game/node/entity/combat/equipment/RangeWeapon.java similarity index 97% rename from Server/src/main/java/core/game/node/entity/combat/equipment/RangeWeapon.java rename to Server/src/main/core/game/node/entity/combat/equipment/RangeWeapon.java index e82601410..13265303a 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/combat/equipment/SwitchAttack.java b/Server/src/main/core/game/node/entity/combat/equipment/SwitchAttack.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/combat/equipment/SwitchAttack.java rename to Server/src/main/core/game/node/entity/combat/equipment/SwitchAttack.java index c707e343b..7a11c7eb3 100644 --- a/Server/src/main/java/core/game/node/entity/combat/equipment/SwitchAttack.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/SwitchAttack.java @@ -3,7 +3,7 @@ package core.game.node.entity.combat.equipment; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; +import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.impl.Projectile; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; diff --git a/Server/src/main/java/core/game/node/entity/combat/equipment/Weapon.java b/Server/src/main/core/game/node/entity/combat/equipment/Weapon.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/combat/equipment/Weapon.java rename to Server/src/main/core/game/node/entity/combat/equipment/Weapon.java diff --git a/Server/src/main/java/core/game/node/entity/combat/equipment/WeaponInterface.java b/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java similarity index 99% rename from Server/src/main/java/core/game/node/entity/combat/equipment/WeaponInterface.java rename to Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java index 78c7bdf86..cb74f3fa6 100644 --- a/Server/src/main/java/core/game/node/entity/combat/equipment/WeaponInterface.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java @@ -4,7 +4,7 @@ import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.InterfaceType; import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.SpellBookManager; @@ -18,7 +18,10 @@ import core.net.packet.out.Interface; import core.net.packet.out.InterfaceConfig; import core.net.packet.out.StringPacket; import org.rs09.consts.Components; -import rs09.game.system.config.ItemConfigParser; +import core.game.system.config.ItemConfigParser; + +import static core.api.ContentAPIKt.*; + /** @@ -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 new file mode 100644 index 000000000..e330489a9 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/graves/Grave.kt @@ -0,0 +1,178 @@ +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 +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.item.GroundItem +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.NPCs +import core.game.world.GameWorld +import core.game.world.repository.Repository +import core.tools.secondsToTicks +import core.tools.colorize +import core.tools.ticksToSeconds + +@Initializable +class Grave : AbstractNPC { + lateinit var type: GraveType + private val items = ArrayList() + var ownerUsername: String = "" + var ownerUid: Int = -1 + + var ticksRemaining = -1 + + constructor() : super(NPCs.GRAVESTONE_6571, Location.create(0,0,0), false) + private constructor(id: Int, location: Location) : super(id, location) + + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return Grave(id, location) + } + + override fun getIds(): IntArray { + return GraveType.ids + } + + fun configureType(type: GraveType) { + this.type = type + this.transform(type.npcId) + this.ticksRemaining = secondsToTicks(type.durationMinutes * 60) + } + + fun initialize(player: Player, location: Location, inventory: Array) { + if (!GraveController.allowGenerate(player)) + return + + this.ownerUid = player.details.uid + this.ownerUsername = player.username + this.location = player.getAttribute("/save:original-loc",location) + this.isRespawn = false + this.isWalks = false + this.isNeverWalks = true + + for (item in inventory) { + if (GraveController.shouldRelease(item.id)) { + sendMessage(player, "Your ${item.name.lowercase().replace("jar", "")} has escaped.") + continue + } + + if (GraveController.shouldCrumble(item.id)) { + sendMessage(player, "Your ${item.name.lowercase()} has crumbled to dust.") + continue + } + + 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 + gi.decayTime = secondsToTicks(type.durationMinutes * 60) + this.items.add(gi) + } + + if (items.isEmpty()) { + clear() + return + } + + this.init() + + if (GraveController.activeGraves[ownerUid] != null) { + val oldGrave = GraveController.activeGraves[ownerUid] + oldGrave?.collapse() + } + + GraveController.activeGraves[ownerUid] = this + sendMessage(player, colorize("%RBecause of your current gravestone, you have ${type.durationMinutes} minutes to get your items back.")) + } + + fun setupFromJsonParams(playerUid: Int, ticks: Int, location: Location, items: Array, username: String) { + this.ownerUid = playerUid + this.ticksRemaining = ticks + this.location = location + this.isRespawn = false + this.isWalks = false + this.isNeverWalks = true + this.ownerUsername = username + + for (item in items) { + val gi = GroundItemManager.create(item, location, playerUid, GameWorld.ticks + ticksRemaining) + gi.isRemainPrivate = true + this.items.add(gi) + } + + this.transform(type.npcId) + this.init() + } + + override fun tick() { + //Grave should not do anything else on tick, that is all handled by GraveController. + if (Repository.uid_map[ownerUid] != null) { + val p = Repository.uid_map[ownerUid] ?: return + registerHintIcon(p, this) + } + } + + fun addTime(ticks: Int) { + ticksRemaining += ticks + for (gi in items) { + gi.decayTime = ticksRemaining + } + if (ticksRemaining < 30) + transform(type.npcId + 2) + else if (ticksRemaining < 90) + transform(type.npcId + 1) + else + transform(type.npcId) + } + + fun collapse() { + for (item in items) { + GroundItemManager.destroy(item) + } + clear() + GraveController.activeGraves.remove(ownerUid) + if (Repository.uid_map[ownerUid] != null) { + val p = Repository.uid_map[ownerUid] ?: return + clearHintIcon(p) + } + } + + fun demolish() { + val owner = Repository.uid_map[ownerUid] ?: return + for (item in items) { + if (!item.isRemoved) + item.decayTime = secondsToTicks(45) + } + clear() + sendMessage(owner, "It looks like it'll last another ${getFormattedTimeRemaining()}.") + sendMessage(owner, "You demolish it anyway.") + GraveController.activeGraves.remove(ownerUid) + clearHintIcon(owner) + } + + fun getItems() : Array { + return this.items.toTypedArray() + } + + fun retrieveFormattedText(): String { + return type.text + .replace("@name", ownerUsername) + .replace("@mins", getFormattedTimeRemaining()) + } + + fun getFormattedTimeRemaining() : String { + val seconds = ticksToSeconds(ticksRemaining) + val timeQty = if (seconds / 60 > 0) seconds / 60 else seconds + val timeUnit = (if (seconds / 60 > 0) "minute" else "second") + if (timeQty > 1) "s" else "" + return "$timeQty $timeUnit" + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..00e0e0b1b --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/graves/GraveController.kt @@ -0,0 +1,297 @@ +package core.game.node.entity.combat.graves + +import core.api.* +import core.game.node.Node +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.skill.Skills +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.map.zone.ZoneRestriction +import org.json.simple.JSONArray +import org.json.simple.JSONObject +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 + +class GraveController : PersistWorld, TickListener, InteractionListener, Commands { + override fun defineListeners() { + on(GraveType.ids, IntType.NPC, "read", handler = this::onGraveReadOption) + on(GraveType.ids, IntType.NPC, "bless", handler = this::onGraveBlessed) + on(GraveType.ids, IntType.NPC, "repair", handler = this::onGraveRepaired) + on(GraveType.ids, IntType.NPC, "demolish", handler = this::onGraveDemolished) + } + + override fun defineCommands() { + 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.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) + } + } + } + + + override fun tick() { + for (grave in activeGraves.values.toTypedArray()) { + if (grave.ticksRemaining == -1) return + + if (grave.ticksRemaining == secondsToTicks(30) || grave.ticksRemaining == secondsToTicks(90)) { + grave.transform(grave.id + 1) + } + + if (grave.ticksRemaining == 0) { + grave.collapse() + } + + grave.ticksRemaining-- + } + } + + private fun onGraveReadOption(player: Player, node: Node) : Boolean { + val grave = node as? Grave ?: return false + + var isGraniteBackground = false + + when (grave.type) { + in GraveType.SMALL_GS..GraveType.ANGEL_DEATH -> isGraniteBackground = true + else -> {} + } + + if (isGraniteBackground) + setVarbit(player, 4191, 1) + else + setVarbit(player, 4191, 0) + + openInterface(player, 266) + setInterfaceText(player, grave.retrieveFormattedText(), 266, 23) + + sendMessage(player, "It looks like it'll survive another ${grave.getFormattedTimeRemaining()}.") + if (player.details.uid == grave.ownerUid) { + sendMessage(player, "Isn't there something a bit odd about reading your own gravestone?") + } + return true + } + + private fun onGraveBlessed(player: Player, node: Node) : Boolean { + val g = node as? Grave ?: return false + + if (getAttribute(g, "blessed", false)) { + sendMessage(player, "This grave has already been blessed.") + return true + } + + if (player.details.uid == g.ownerUid) { + sendMessage(player, "The gods don't seem to approve of people attempting to bless their own gravestones.") + 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 + } + + val blessAmount = min(60, player.skills.prayerPoints.toInt() - 10) + + if (blessAmount <= 0) { + sendMessage(player, "You do not have enough prayer points to do that.") + return true + } + + g.addTime(secondsToTicks(blessAmount * 60)) + player.skills.prayerPoints -= blessAmount + setAttribute(g, "blessed", true) + + playAudio(player, Sounds.PRAYER_RECHARGE_2674) + animate(player, 645) + + if (gOwner != null) { + sendMessage(gOwner, colorize("%RYour grave has been blessed.")) + } + return true + } + + private fun onGraveRepaired(player: Player, node: Node) : Boolean { + val g = node as? Grave ?: return false + + if (getAttribute(g, "repaired", false)) { + sendMessage(player, "This grave has already been repaired.") + return true + } + + if (getStatLevel(player, Skills.PRAYER) < 2) { + sendMessage(player, "You need a Prayer level of 2 to bless a grave.") + return true + } + + if (player.skills.prayerPoints < 1.0) { + sendMessage(player, "You do not have enough prayer points to do that.") + return true + } + + val restoreAmount = min(5, player.skills.prayerPoints.toInt()) + g.addTime(secondsToTicks(restoreAmount * 60)) + player.skills.prayerPoints -= restoreAmount + setAttribute(g, "repaired", true) + + playAudio(player, Sounds.PRAYER_RECHARGE_2674) + animate(player, 645) + return true + } + + private fun onGraveDemolished(player: Player, node: Node) : Boolean { + val g = node as? Grave ?: return false + + if (player.details.uid != g.ownerUid) { + sendMessage(player, "You cannot demolish someone else's gravestone!") + return true + } + + g.demolish() + return true + } + + override fun save() { + serializeToServerStore() + } + + override fun parse() { + deserializeFromServerStore() + } + + companion object { + val activeGraves = HashMap() + var childCounter = 0 + val ATTR_GTYPE = "/save:gravetype" + + @JvmStatic fun produceGrave(type: GraveType): Grave { + val g = Grave() + g.configureType(type) + return g + } + + @JvmStatic fun shouldCrumble(item: Int) : Boolean { + when (item) { + Items.ECTOPHIAL_4251 -> return true + in Items.SMALL_POUCH_5509..Items.GIANT_POUCH_5515 -> return true + } + + return itemDefinition(item).hasAction("destroy") + } + + @JvmStatic fun shouldRelease(item: Int) : Boolean { + when (item) { + Items.CHINCHOMPA_9976 -> return true + Items.CHINCHOMPA_10033 -> return true + in Items.BABY_IMPLING_JAR_11238..Items.DRAGON_IMPLING_JAR_11257 -> return itemDefinition(item).isUnnoted + } + + return false + } + + @JvmStatic fun checkTransform(item: Item) : Item { + if (item.hasItemPlugin()) + return item.plugin.getDeathItem(item) + return item + } + + @JvmStatic fun allowGenerate(player: Player) : Boolean { + if (player.skullManager.isSkulled) + return false + if (player.skullManager.isWilderness) + return false + if (WildernessZone.isInZone(player)) + return false + if (player.ironmanManager.mode == IronmanMode.HARDCORE) + return false + if (player.zoneMonitor.isRestricted(ZoneRestriction.GRAVES)) + return false + return true + } + + @JvmStatic fun getGraveType(player: Player) : GraveType { + return GraveType.values()[getAttribute(player, ATTR_GTYPE, 0)] + } + + @JvmStatic fun updateGraveType(player: Player, type: GraveType) { + setAttribute(player, ATTR_GTYPE, type.ordinal) + } + + @JvmStatic fun hasGraveAt(loc: Location) : Boolean { + return activeGraves.values.toTypedArray().any { it.location == loc } + } + + fun serializeToServerStore() { + val archive = ServerStore.getArchive("active-graves") + archive.clear() + for ((uid,grave) in activeGraves) { + val g = JSONObject() + g["ticksRemaining"] = grave.ticksRemaining + g["location"] = grave.location.toString() + g["type"] = grave.type.ordinal + g["username"] = grave.ownerUsername + val items = JSONArray() + for (item in grave.getItems()) { + val i = JSONObject() + i["id"] = item.id + i["amount"] = item.amount + i["charge"] = item.charge + items.add(i) + } + g["items"] = items + archive["$uid"] = g + } + } + + fun deserializeFromServerStore() { + val archive = ServerStore.getArchive("active-graves") + for (entry in archive.entries as Set>) { + val g = entry.value as JSONObject + val uid = (entry.key as String).toInt() + val type = g["type"].toString().toInt() + val ticks = g["ticksRemaining"].toString().toInt() + val location = Location.fromString(g["location"].toString()) + val username = g["username"].toString() + + val items = ArrayList() + val itemsRaw = g["items"] as JSONArray + for (itemRaw in itemsRaw) { + val item = itemRaw as JSONObject + val id = item["id"].toString().toInt() + val amount = item["amount"].toString().toInt() + val charge = item["charge"].toString().toInt() + items.add(Item(id, amount, charge)) + } + + val grave = produceGrave(GraveType.values()[type]) + grave.setupFromJsonParams(uid, ticks, location, items.toTypedArray(), username) + activeGraves[uid] = grave + } + } + } +} 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 new file mode 100644 index 000000000..86a589f17 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/graves/GravePurchaseInterface.kt @@ -0,0 +1,65 @@ +package core.game.node.entity.combat.graves + +import core.api.* +import core.game.node.item.Item +import org.rs09.consts.Components +import org.rs09.consts.Items +import core.game.interaction.InterfaceListener + +class GravePurchaseInterface : InterfaceListener { + val BUTTON_CONFIRM = 34 + val AVAILABLE_GRAVES_BITFIELD = 0xFFF //Enable all graves, authentically the quest-locked ones should be excluded from the bitfield, but y'know. + val AVAILABLE_GRAVES_VARBIT = 4191 + val CURRENT_GRAVE_VARBIT = 4190 + + override fun defineInterfaceListeners() { + onOpen (Components.GRAVESTONE_SHOP_652) {player, _ -> + val userType = GraveController.getGraveType(player).ordinal + setVarbit(player, AVAILABLE_GRAVES_VARBIT, AVAILABLE_GRAVES_BITFIELD) + setVarbit(player, CURRENT_GRAVE_VARBIT, userType) + + val settings = IfaceSettingsBuilder() + .enableAllOptions() + .build() + player.packetDispatch.sendIfaceSettings(settings, 34, Components.GRAVESTONE_SHOP_652, 0, 13) + return@onOpen true + } + + on (Components.GRAVESTONE_SHOP_652, BUTTON_CONFIRM) { player, _, _, _, slot, _ -> + val selectedType = GraveType.values()[slot] + val userType = GraveController.getGraveType(player) + val activeGrave = GraveController.activeGraves[player.details.uid] + + if (activeGrave != null){ + sendDialogue(player, "You cannot change graves while you have a grave active.") + return@on true + } + + if (selectedType == userType) { + sendDialogue(player, "You already have that gravestone!") + return@on true + } + + val cost = selectedType.cost + val requirement = selectedType.requiredQuest + + if (requirement != null && !isQuestComplete(player, requirement)) { + sendDialogue(player, "That gravestone requires completion of $requirement.") + return@on true + } + + if (selectedType != GraveType.MEM_PLAQUE && amountInInventory(player, Items.COINS_995) < cost) { + sendDialogue(player, "You do not have enough coins to afford that gravestone.") + return@on true + } + + if (selectedType == GraveType.MEM_PLAQUE || removeItem(player, Item(995, cost))) { + GraveController.updateGraveType(player, selectedType) + sendDialogue(player, "Your grave has been updated.") + } + + closeInterface(player) + return@on true + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..1a9105873 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/graves/GraveType.kt @@ -0,0 +1,29 @@ +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: 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."), + ORNATE_GS(NPCs.GRAVESTONE_6574, 5000, 3, false, text = SMALL_GS.text), + FONT_OF_LIFE(NPCs.GRAVESTONE_6577, 50000, 4, true, text = "In your travels,
pause awhile to remember @name,
who passed away at this spot."), + STELE(NPCs.STELE_6580, 50000, 4, true, text = FONT_OF_LIFE.text), + SARA_SYMBOL(NPCs.SARADOMIN_SYMBOL_6583, 50000, 4, true, text = "@name,
an enlightened servant of Saradomin,
perished in this place."), + ZAM_SYMBOL(NPCs.ZAMORAK_SYMBOL_6586, 50000, 4, true, text = "@name,
a most bloodthirsty follower of Zamorak,
perished in this place."), + GUTH_SYMBOL(NPCs.GUTHIX_SYMBOL_6589, 50000, 4, true, text = "@name,
who walked with the Balance of Guthix,
perished in this place."), + BAND_SYMBOL(NPCs.BANDOS_SYMBOL_6592, 50000, 4, true, requiredQuest = 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 { + val ids = values().fold(ArrayList()) {list, type -> + list.add(type.npcId) + list.add(type.npcId + 1) + list.add(type.npcId + 2) + list + }.toIntArray() + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/combat/CombatSpell.java b/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java similarity index 75% rename from Server/src/main/java/core/game/node/entity/combat/CombatSpell.java rename to Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java index 3761ffaf6..30cf2f238 100644 --- a/Server/src/main/java/core/game/node/entity/combat/CombatSpell.java +++ b/Server/src/main/core/game/node/entity/combat/spell/CombatSpell.java @@ -1,9 +1,11 @@ -package core.game.node.entity.combat; +package core.game.node.entity.combat.spell; -import core.game.node.entity.skill.magic.MagicSpell; +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; import core.game.node.Node; import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.impl.Projectile; import core.game.node.entity.npc.NPC; @@ -12,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 @@ -102,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. @@ -131,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 @@ -174,7 +190,7 @@ public abstract class CombatSpell extends MagicSpell { entity.animate(animation); } } - sendAudio(entity); + playGlobalAudio(entity.getLocation(), audio.id, 20); } @Override @@ -236,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/CombinationRune.java b/Server/src/main/core/game/node/entity/combat/spell/CombinationRune.java new file mode 100644 index 000000000..08e475767 --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/spell/CombinationRune.java @@ -0,0 +1,34 @@ +package core.game.node.entity.combat.spell; + +import java.util.ArrayList; +import java.util.List; + +public enum CombinationRune { + LAVA_RUNE(4699, Runes.FIRE_RUNE,Runes.EARTH_RUNE), + STEAM_RUNE(4694,Runes.FIRE_RUNE,Runes.WATER_RUNE), + MIST_RUNE(4695,Runes.WATER_RUNE,Runes.AIR_RUNE), + DUST_RUNE(4696, Runes.AIR_RUNE,Runes.EARTH_RUNE), + SMOKE_RUNE(4697,Runes.FIRE_RUNE,Runes.AIR_RUNE), + MUD_RUNE(4698,Runes.EARTH_RUNE,Runes.WATER_RUNE), + ELEMENTAL_RUNE(12850, Runes.AIR_RUNE, Runes.WATER_RUNE, Runes.EARTH_RUNE, Runes.FIRE_RUNE), + CATALYTIC_RUNE(12851, Runes.MIND_RUNE, Runes.CHAOS_RUNE, Runes.DEATH_RUNE, Runes.BLOOD_RUNE, Runes.SOUL_RUNE, Runes.ASTRAL_RUNE); + + public Runes[] types; + public int id; + CombinationRune(int id, Runes... types){ + this.id = id; + this.types = types; + } + + public static List eligibleFor(Runes rune){ + List runes = new ArrayList<>(20); + for(CombinationRune r : CombinationRune.values()){ + for(Runes ru : r.types){ + if(ru == rune){ + runes.add(r); + } + } + } + return runes; + } +} diff --git a/Server/src/main/java/core/game/node/entity/combat/equipment/DefaultCombatSpell.java b/Server/src/main/core/game/node/entity/combat/spell/DefaultCombatSpell.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/combat/equipment/DefaultCombatSpell.java rename to Server/src/main/core/game/node/entity/combat/spell/DefaultCombatSpell.java index fcbfa6133..3dc776bcf 100644 --- a/Server/src/main/java/core/game/node/entity/combat/equipment/DefaultCombatSpell.java +++ b/Server/src/main/core/game/node/entity/combat/spell/DefaultCombatSpell.java @@ -1,10 +1,9 @@ -package core.game.node.entity.combat.equipment; +package core.game.node.entity.combat.spell; import core.game.node.entity.skill.Skills; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; import core.game.node.entity.impl.Projectile; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.link.SpellBookManager; 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 new file mode 100644 index 000000000..544372bba --- /dev/null +++ b/Server/src/main/core/game/node/entity/combat/spell/MagicSpell.java @@ -0,0 +1,357 @@ +package core.game.node.entity.combat.spell; + +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; +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.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 static core.api.ContentAPIKt.playGlobalAudio; + +/** + * Represents a magic spell. + * @author Emperor + */ +public abstract class MagicSpell implements Plugin { + + /** + * The spell book. + */ + protected final SpellBook book; + + /** + * The level requirement. + */ + protected final int level; + + /** + * The animation. + */ + protected final Animation animation; + + /** + * The casting graphics. + */ + protected final Graphics graphic; + + /** + * The casting Audio. + */ + protected final Audio audio; + + /** + * The item-array containing the runes required. + */ + protected final Item[] runes; + + /** + * The spell id. + */ + protected int spellId; + + /** + * The experience gained. + */ + private final double experience; + + /** + * Constructs a new {@code MagicSpell} {@code Object}. + */ + public MagicSpell() { + this(SpellBook.MODERN, 0, 0, null, null, null, new Item[0]); + } + + /** + * Constructs a new {@code MagicSpell} {@code Object}. + * @param book The spell book this spell is from. + * @param level The level requirement. + * @param animation the cast animation. + * @param graphic The cast graphic. + * @param Audio The casting Audio. + * @param runes The runes required to cast the spell. + */ + public MagicSpell(SpellBook book, int level, final double experience, Animation animation, Graphics graphic, Audio Audio, Item[] runes) { + this.book = book; + this.level = level; + this.experience = experience; + this.animation = animation; + this.graphic = graphic; + this.audio = Audio; + this.runes = runes; + } + + /** + * Casts a spell. + * @param p The player casting the spell. + * @param target The target. + */ + public static boolean castSpell(final Player p, SpellBook book, int spellId, Node target) { + if (p.getAttribute("magic-delay", 0) > GameWorld.getTicks()) { + return false; + } + MagicSpell spell = book.getSpell(spellId); + if (spell == null) { + return false; + } + if (spell.book != book || p.getSpellBookManager().getSpellBook() != book.getInterfaceId()) { + return false; + } + if (target.getLocation() != null && target != p) { + if (!target.getLocation().withinDistance(p.getLocation(), 15)) { + return false; + } + p.faceLocation(target.getLocation()); + } + boolean combatSpell = spell instanceof CombatSpell; + if (!combatSpell && target instanceof Entity) { + p.faceTemporary((Entity) target, 1); + } + if (spell.cast(p, target)) { + 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; + } + + /** + * Gets the delay. + * @return the delay. + */ + public int getDelay() { + return 3; + } + + /** + * Starts the effect of this spell (if any). + * @param entity The entity. + */ + public abstract boolean cast(Entity entity, Node target); + + /** + * Visualizes the spell. + * @param entity The entity. + * @param target The target. + */ + public void visualize(Entity entity, Node target) { + entity.graphics(graphic); + entity.animate(animation); + playGlobalAudio(entity.getLocation(), audio.id, 20); + } + + /** + * Checks if the player is holding the staff that will be used instead of + * the rune. + * @param p The player. + * @param rune The rune item id. + * @return {@code True} if the player is wearing the correct staff for the + * rune. + */ + public boolean usingStaff(Player p, int rune) { + Item weapon = p.getEquipment().get(3); + if (weapon == null) { + return false; + } + MagicStaff staff = MagicStaff.forId(rune); + if (staff == null) { + return false; + } + int[] staves = staff.getStaves(); + for (int id : staves) { + if (weapon.getId() == id) { + return true; + } + } + return false; + } + + /** + * Checks if the casting entity meets the requirements to cast this spell. + * @param caster The caster. + * @param message If a message should be send to the caster if it doesn't + * meet the requirements. + * @param remove If we should remove the runes from the player's inventory. + * @return {@code True} if so. + */ + public boolean meetsRequirements(Entity caster, boolean message, boolean remove) { + if (!checkLevelRequirement(caster, message)) { + return false; + } + if (caster instanceof Player) { + CombatSpell spell = ((Player) caster).getProperties().getAutocastSpell(); + if (spell != null) { + boolean slayer = ((Player) caster).getEquipment().get(3).getName().contains("layer's staff"); + boolean voidKnight = ((Player) caster).getEquipment().get(3).getName().contains("knight mace"); + if ((spell.getSpellId() == 31 && !slayer) || (spell.getSpellId() == 42 && !voidKnight)) { + ((Player) caster).getPacketDispatch().sendMessage("You need the proper staff to autocast this spell."); + return false; + } + } + } + if((spellId == 12 || spellId == 30 || spellId == 56) && caster instanceof Player){ + if (caster.getAttribute("entangleDelay", 0) > GameWorld.getTicks()) { + caster.asPlayer().sendMessage("You have recently cast a binding spell."); + return false; + } + } + if (caster instanceof Player) { + Player p = (Player) caster; + if(p.getEquipment().get(3) != null && p.getEquipment().get(3).getId() == 14726){ + if(RandomFunction.getRandom(100) < 13){ + p.sendMessage("Your staff negates the rune requirement of the spell."); + return true; + } + } + if (runes == null) { + return true; + } + 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) { + ArrayList toRemove = p.getAttribute("spell:runes", new ArrayList<>()); + toRemove.forEach(i -> p.getInventory().remove(i)); + p.removeAttribute("spell:runes"); + } + return true; + } + return true; + } + + /** + * Checks the level requirement. + * @param caster The caster. + * @param message If we display the message. + * @return {@code True} if passed. + */ + public boolean checkLevelRequirement(Entity caster, boolean message) { + if (caster instanceof Player && caster.getSkills().getLevel(Skills.MAGIC, this instanceof CombatSpell ? true : false) < levelRequirement()) { + if (message && caster instanceof Player) { + ((Player) caster).getPacketDispatch().sendMessage("You need a Magic level of " + levelRequirement() + " to cast this spell."); + } + return false; + } + return true; + } + + /** + * Adds the experience for casting this spell. + * @param entity The entity to reward with experience. + * @param hit The hit. + */ + public void addExperience(Entity entity, int hit) { + entity.getSkills().addExperience(Skills.MAGIC, experience, true); + if (!(entity instanceof Player) || hit < 1) { + return; + } + entity.getSkills().addExperience(Skills.HITPOINTS, hit * 1.33, true); + if (entity.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_DEFENSIVE_CAST) { + 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), true); + } + + /** + * Gets the level requirement to cast this spell. + * @return The level requirement. + */ + public int levelRequirement() { + return level; + } + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + /** + * Gets the audio. + * @return the audio. + */ + public Audio getAudio() { + return audio; + } + + /** + * Gets the spell book for this spell. + * @return The spell book. + */ + public SpellBook getBook() { + return book; + } + + /** + * Gets the item array of runes required to cast this spell. + * @return The item array of runes, or {@code null} if the spell doesn't need + * any runes. + */ + public Item[] getCastRunes() { + return runes; + } + + /** + * Gets the spellId. + * @return The spellId. + */ + public int getSpellId() { + return spellId; + } + + /** + * Sets the spellId. + * @param spellId The spellId to set. + */ + public void setSpellId(int spellId) { + this.spellId = spellId; + } + + /** + * Gets the experience. + * @return The experience. + */ + public double getExperience() { + return experience; + } + + /** + * Gets the experience. + * @param player the player. + * @return the experience. + */ + public double getExperience(Player player) { + return experience; + } + + /** + * Gets the level requirement. + * @return The level requirement. + */ + public int getLevel() { + return level; + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/MagicStaff.java b/Server/src/main/core/game/node/entity/combat/spell/MagicStaff.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/skill/magic/MagicStaff.java rename to Server/src/main/core/game/node/entity/combat/spell/MagicStaff.java index bae873e7f..6664e721b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/MagicStaff.java +++ b/Server/src/main/core/game/node/entity/combat/spell/MagicStaff.java @@ -1,4 +1,4 @@ -package core.game.node.entity.skill.magic; +package core.game.node.entity.combat.spell; import java.util.HashMap; import java.util.Map; @@ -12,7 +12,7 @@ public enum MagicStaff { /** * Represents the fire rune staves. */ - FIRE_RUNE(554, 1387, 1393, 1401, 3053, 3055, 3056, 11736, 11738), + FIRE_RUNE(554, 1387, 1393, 1401, 3053, 3054, 3055, 3056, 11736, 11738), /** * Represents the water rune staves. @@ -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/java/core/game/node/entity/skill/magic/Runes.java b/Server/src/main/core/game/node/entity/combat/spell/Runes.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/skill/magic/Runes.java rename to Server/src/main/core/game/node/entity/combat/spell/Runes.java index 15e4e8c4d..dacb28c27 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/Runes.java +++ b/Server/src/main/core/game/node/entity/combat/spell/Runes.java @@ -1,6 +1,5 @@ -package core.game.node.entity.skill.magic; +package core.game.node.entity.combat.spell; -import core.game.node.entity.combat.equipment.SpellType; import core.game.node.item.Item; import org.rs09.consts.Items; diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/SpellBlocks.java b/Server/src/main/core/game/node/entity/combat/spell/SpellBlocks.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/combat/spell/SpellBlocks.java rename to Server/src/main/core/game/node/entity/combat/spell/SpellBlocks.java index b1a37c197..e6fc5a781 100644 --- a/Server/src/main/java/core/game/node/entity/combat/spell/SpellBlocks.java +++ b/Server/src/main/core/game/node/entity/combat/spell/SpellBlocks.java @@ -1,7 +1,6 @@ package core.game.node.entity.combat.spell; import core.game.node.Node; -import rs09.game.system.SystemLogger; import java.util.ArrayList; import java.util.HashMap; diff --git a/Server/src/main/java/core/game/node/entity/combat/equipment/SpellType.java b/Server/src/main/core/game/node/entity/combat/spell/SpellType.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/combat/equipment/SpellType.java rename to Server/src/main/core/game/node/entity/combat/spell/SpellType.java index 135dcf8c5..828a88804 100644 --- a/Server/src/main/java/core/game/node/entity/combat/equipment/SpellType.java +++ b/Server/src/main/core/game/node/entity/combat/spell/SpellType.java @@ -1,13 +1,14 @@ -package core.game.node.entity.combat.equipment; +package core.game.node.entity.combat.spell; import core.game.container.impl.EquipmentContainer; 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/java/core/game/node/entity/impl/Animator.java b/Server/src/main/core/game/node/entity/impl/Animator.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/impl/Animator.java rename to Server/src/main/core/game/node/entity/impl/Animator.java index 8bc30f93f..430c4d551 100644 --- a/Server/src/main/java/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 rs09.game.world.GameWorld; +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 new file mode 100644 index 000000000..8cfc7efd6 --- /dev/null +++ b/Server/src/main/core/game/node/entity/impl/ForceMovement.java @@ -0,0 +1,532 @@ +package core.game.node.entity.impl; + +import core.game.node.entity.Entity; +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.update.flag.context.*; +import core.game.world.update.flag.*; + +/** + * The force movement handler. + * @author Emperor + */ +public class ForceMovement extends Pulse { + + /** + * The walking speed. + */ + public static final int WALKING_SPEED = 10; + + /** + * The running speed. + */ + public static final int RUNNING_SPEED = 20; + + /** + * The walking animation. + */ + public static final Animation WALK_ANIMATION = Animation.create(819); + + /** + * The entity. + */ + protected Entity entity; + + /** + * The location to start the force movement from. + */ + private Location start; + + /** + * The destination. + */ + private Location destination; + + /** + * The animation. + */ + private Animation startAnim; + + /** + * The animation. + */ + protected Animation animation; + + /** + * The ending animation. + */ + private Animation endAnimation = null; + + /** + * The direction. + */ + protected Direction direction; + + /** + * The commencing speed. + */ + private int commenceSpeed; + + /** + * The path speed. + */ + private int pathSpeed; + + /** + * Whether to unlock the entity after the ForceMovement completes + */ + private boolean unlockAfter; + + /** + * Constructs a new {@code ForceMovement} {@code Object}. + * @param e The entity. + * @param start The start location. + * @param destination The destination. + * @param startAnim The start animation. + * @param animation The animation + * @param direction The direction. + * @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; + this.start = start; + this.destination = destination; + this.startAnim = startAnim; + this.animation = animation; + this.direction = direction; + this.commenceSpeed = commenceSpeed; + this.pathSpeed = pathSpeed; + this.unlockAfter = unlockAfter; + } + + /** + * Constructs a new {@code ForceMovement} {@code Object}. + * @param e the entity. + * @param start the start location. + * @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); + } + + /** + * Constructs a new {@code ForceMovement} {@code Object}. + * @param e the entity. + * @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); + } + + /** + * Constructs a new {@code ForceMovement} {@code Object}. + * @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); + } + + /** + * Creates and runs a new force movement 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); + } + + /** + * Creates and runs a new force movement pulse. + * @param e The entity. + * @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); + } + + /** + * Creates and runs a new force movement pulse. + * @param e The entity. + * @param start The start location. + * @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); + } + + /** + * Creates and runs a new force movement pulse. + * @param e The entity. + * @param start The start location. + * @param destination The destination location. + * @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); + } + + /** + * Creates and runs a new force movement pulse. + * @param e The entity. + * @param start The start location. + * @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); + } + + /** + * Creates and runs a new force movement pulse. + * @param e The entity. + * @param start The start location. + * @param destination The destination location. + * @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); + } + + /** + * Creates and runs a new force movement pulse. + * @param e The entity. + * @param start The start location. + * @param destination The destination location. + * @param animation The animation. + * @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); + } + + /** + * Creates and runs a new force movement pulse. + * @param e The entity. + * @param start The start location. + * @param destination The destination location. + * @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); + } + if (animation != null) { + animation.setPriority(Animator.Priority.VERY_HIGH); + } + ForceMovement fm = new ForceMovement(e, start, destination, startAnim, animation, direction, commenceSpeed, pathSpeed, unlockAfter); + fm.start(); + e.lock(); + 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); + } + + /** + * 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()); + if (commence != 0 && commenceSpeed != 0) { + commence = (int) (1 + (commence / (commenceSpeed * 0.1))); + } + int path = 1 + (int) Math.ceil(start.getDistance(destination) / (pathSpeed * 0.1)); + this.pathSpeed = pathSpeed == 0 ? path : speed; + this.commenceSpeed = commence; + start(); + e.lock(); + GameWorld.getPulser().submit(this); + } + + /** + * Method used to run the movement. + * @param e the entity. + */ + public void run(final Entity e) { + run(e, 0); + } + + /** + * Method used to run the movement. + */ + public void run() { + run(entity); + } + + /** + * Gets the direction value. + * @param s The start location. + * @param d The destination. + * @return The direction object. + */ + public static Direction direction(Location s, Location d) { + Location delta = Location.getDelta(s, d); + int x = Math.abs(delta.getX()); + int y = Math.abs(delta.getY()); + if (x > y) { + return Direction.getDirection(delta.getX(), 0); + } + return Direction.getDirection(0, delta.getY()); + } + + @Override + public void start() { + commenceSpeed = (int) Math.ceil(start.getDistance(entity.getLocation()) / (commenceSpeed * 0.1)); + pathSpeed = (int) Math.ceil(start.getDistance(destination) / (pathSpeed * 0.1)); + if (commenceSpeed != 0) { + entity.animate(startAnim); + super.setDelay(commenceSpeed); + } else { + entity.animate(animation); + super.setDelay(pathSpeed); + } + int ticks = 1 + commenceSpeed + pathSpeed; + entity.getImpactHandler().setDisabledTicks(ticks); + entity.getUpdateMasks().register(EntityFlag.ForceMove, new ForceMoveCtx(start, destination, commenceSpeed * 30, pathSpeed * 30, direction)); + if(entity instanceof Player) { + entity.getWalkingQueue().updateRegion(destination, false); + } + super.start(); + } + + @Override + public boolean pulse() { + if (commenceSpeed != 0) { + entity.animate(animation); + setDelay(pathSpeed); + commenceSpeed = 0; + entity.getProperties().setTeleportLocation(start); + return false; + } + return true; + } + + @Override + public void stop() { + super.stop(); + entity.getProperties().setTeleportLocation(destination); + if (endAnimation != null) { + entity.animate(endAnimation); + } + if (unlockAfter) entity.unlock(); + } + + /** + * Gets the start. + * @return The start. + */ + public Location getStart() { + return start; + } + + /** + * Sets the start. + * @param start The start to set. + */ + public void setStart(Location start) { + this.start = start; + } + + /** + * Gets the destination. + * @return The destination. + */ + public Location getDestination() { + return destination; + } + + /** + * Sets the destination. + * @param destination The destination to set. + */ + public void setDestination(Location destination) { + this.destination = destination; + } + + /** + * Gets the direction. + * @return The direction. + */ + public Direction getDirection() { + return direction; + } + + /** + * Sets the direction. + * @param direction The direction to set. + */ + public void setDirection(Direction direction) { + this.direction = direction; + } + + /** + * Gets the commenceSpeed. + * @return The commenceSpeed. + */ + public int getCommenceSpeed() { + return commenceSpeed; + } + + /** + * Sets the commenceSpeed. + * @param commenceSpeed The commenceSpeed to set. + */ + public void setCommenceSpeed(int commenceSpeed) { + this.commenceSpeed = commenceSpeed; + } + + /** + * Sets the entity. + * @param entity the entity. + */ + public void setEntity(Entity entity) { + this.entity = entity; + } + + /** + * Gets the pathSpeed. + * @return The pathSpeed. + */ + public int getPathSpeed() { + return pathSpeed; + } + + /** + * Sets the pathSpeed. + * @param pathSpeed The pathSpeed to set. + */ + public void setPathSpeed(int pathSpeed) { + this.pathSpeed = pathSpeed; + } + + /** + * Gets the entity. + * @return The entity. + */ + public Entity getEntity() { + return entity; + } + + /** + * Gets the startAnim. + * @return The startAnim. + */ + public Animation getStartAnim() { + return startAnim; + } + + /** + * Sets the startAnim. + * @param startAnim The startAnim to set. + */ + public void setStartAnim(Animation startAnim) { + this.startAnim = startAnim; + } + + /** + * Gets the animation. + * @return The animation. + */ + public Animation getAnimation() { + return animation; + } + + /** + * Sets the animation. + * @param animation The animation to set. + */ + public void setAnimation(Animation animation) { + this.animation = animation; + } + + /** + * Gets the endAnimation. + * @return The endAnimation. + */ + public Animation getEndAnimation() { + return endAnimation; + } + + /** + * Sets the endAnimation. + * @param endAnimation The endAnimation to set. + */ + public void setEndAnimation(Animation endAnimation) { + this.endAnimation = endAnimation; + } +} diff --git a/Server/src/main/java/core/game/node/entity/impl/GameAttributes.java b/Server/src/main/core/game/node/entity/impl/GameAttributes.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/impl/GameAttributes.java rename to Server/src/main/core/game/node/entity/impl/GameAttributes.java index df82a0e9b..4596b76d8 100644 --- a/Server/src/main/java/core/game/node/entity/impl/GameAttributes.java +++ b/Server/src/main/core/game/node/entity/impl/GameAttributes.java @@ -1,6 +1,6 @@ package core.game.node.entity.impl; -import rs09.ServerConstants; +import core.ServerConstants; import org.w3c.dom.*; import javax.xml.parsers.DocumentBuilder; @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; /** * Handles an entity's game attributes. @@ -136,7 +135,7 @@ public final class GameAttributes { */ public void setAttribute(String key, Object value) { if (key.startsWith("/save:")) { - key = key.substring(6, key.length()); + key = key.substring(6); if (!savedAttributes.contains(key)) { savedAttributes.add(key); } @@ -144,17 +143,6 @@ public final class GameAttributes { attributes.put(key, value); } - /** - * Sets an inherently temporary (but saved cross-session) key. - * @param key the key to set - * @param value the value to assign to the key - * @param timeToLive the time (in milliseconds) that the key will be valid for - */ - public void setExpirableAttribute(String key, Object value, Long timeToLive){ - setAttribute(key,value); - keyExpirations.put(key,System.currentTimeMillis() + timeToLive); - } - /** * Gets an attribute. * @param key The attribute name. @@ -163,9 +151,6 @@ public final class GameAttributes { @SuppressWarnings("unchecked") public T getAttribute(String key) { key = key.replace("/save:",""); - if (!attributes.containsKey(key)) { - return null; - } return (T) attributes.get(key); } @@ -182,9 +167,6 @@ public final class GameAttributes { if (object != null) { return (T) object; } - if(keyExpirations.containsKey(string) && keyExpirations.get(string) < System.currentTimeMillis()){ - return fail; - } return fail; } diff --git a/Server/src/main/java/core/game/node/entity/impl/Projectile.java b/Server/src/main/core/game/node/entity/impl/Projectile.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/impl/Projectile.java rename to Server/src/main/core/game/node/entity/impl/Projectile.java diff --git a/Server/src/main/java/core/game/node/entity/impl/Properties.java b/Server/src/main/core/game/node/entity/impl/Properties.java similarity index 91% rename from Server/src/main/java/core/game/node/entity/impl/Properties.java rename to Server/src/main/core/game/node/entity/impl/Properties.java index 081ca3c4f..78e4f8807 100644 --- a/Server/src/main/java/core/game/node/entity/impl/Properties.java +++ b/Server/src/main/core/game/node/entity/impl/Properties.java @@ -3,19 +3,21 @@ package core.game.node.entity.impl; import core.game.container.Container; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatSpell; +import core.game.node.entity.combat.spell.CombatSpell; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.equipment.ArmourSet; 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 rs09.game.node.entity.combat.CombatPulse; -import rs09.game.system.config.ItemConfigParser; -import rs09.game.system.config.NPCConfigParser; +import core.game.node.entity.combat.CombatPulse; +import core.game.system.config.ItemConfigParser; +import core.game.system.config.NPCConfigParser; /** * Holds an entity's properties. @@ -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. @@ -130,6 +132,11 @@ public final class Properties { */ private boolean safeZone; + /** + * The location which the player respawns at the event of death while safeZone = true + */ + public Location safeRespawn; + /** * The combat time out ticks. */ @@ -226,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; @@ -321,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. @@ -560,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 new file mode 100644 index 000000000..6f93e1250 --- /dev/null +++ b/Server/src/main/core/game/node/entity/impl/PulseManager.java @@ -0,0 +1,159 @@ +package core.game.node.entity.impl; + +import core.game.interaction.MovementPulse; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.DeathTask; +import core.game.node.entity.player.Player; +import core.game.system.task.Pulse; +import core.game.node.entity.combat.CombatPulse; +import core.game.world.GameWorld; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Represents an entity's pulse manager. + * @author Emperor + */ +public final class PulseManager { + + private final Entity entity; + + public PulseManager(Entity entity) { + this.entity = entity; + } + + /** + * The movement pulse. + */ + private final HashMap currentPulses = new HashMap<>(); + + public void run(Pulse pulse) { + run(pulse, PulseType.STANDARD); + } + + /** + * Runs a pulse. + * @param pulse The pulse. + * @param pulseType The pulse type we're trying to run. + */ + public void run(Pulse pulse, PulseType pulseType) { + ArrayList toRemove = new ArrayList<>(currentPulses.size()); + currentPulses.forEach((key, value) -> { + if (value != null && !value.isRunning()) { + toRemove.add(key); + } + }); + for (PulseType t : toRemove) currentPulses.remove(t); + + if (currentPulses.get(PulseType.STRONG) != null) { + //strong pulses cannot be interrupted or ran alongside anything else. They are the ONLY pulse type when they are present. + return; + } + + if (!clear(pulseType)) { + return; + } + + if (pulseType == PulseType.STRONG) { + clear(); + } + + currentPulses.put(pulseType, pulse); + pulse.start(); + if (pulse.isRunning()) { + GameWorld.getPulser().submit(pulse); + } + } + + public void clear() { + entity.scripts.removeWeakScripts(); + entity.scripts.removeNormalScripts(); + + currentPulses.forEach((type, pulse) -> { + if (type != PulseType.STRONG && pulse != null) pulse.stop(); + }); + } + + /** + * Clears the pulses. + */ + public boolean clear(PulseType pulseType) { + entity.scripts.removeWeakScripts(); + entity.scripts.removeNormalScripts(); + + Pulse pulse = currentPulses.get(pulseType); + + if (pulse != null) { + pulse.stop(); + currentPulses.remove(pulseType); + } + return true; + } + + /** + * Runs the unhandled reward pulse ("Nothing interesting happens.") + * @param player The player. + * @return The pulse. + * @param pulseType The pulse type. + */ + public Pulse runUnhandledAction(final Player player, PulseType pulseType) { + Pulse pulse = new Pulse(1, player) { + @Override + public boolean pulse() { + player.getPacketDispatch().sendMessage("Nothing interesting happens."); + return true; + } + }; + run(pulse, pulseType); + return pulse; + } + + /** + * Checks if the current pulse moves the entity. + * @return {@code True} if so. + */ + public boolean isMovingPulse() { + if (!hasPulseRunning()) { + return false; + } + + Pulse current = getCurrent(); + return current instanceof MovementPulse || current instanceof CombatPulse; + } + + /** + * Checks if a pulse is running. + * @return {@code True} if so. + */ + public boolean hasPulseRunning() { + return getCurrent() != null && getCurrent().isRunning(); + } + + /** + * Cancels the death task, if any. + * @param e The entity. + */ + public static void cancelDeathTask(Entity e) { + if (!DeathTask.isDead(e) || e.getPulseManager().getCurrent() == null) { + return; + } + e.getPulseManager().getCurrent().stop(); + } + + /** + * Gets the current. + * @return The current. + */ + public Pulse getCurrent() { + PulseType[] types = PulseType.values(); + for (PulseType type : types) { + if (currentPulses.get(type) != null) { + return currentPulses.get(type); + } + } + return null; + } + +} + diff --git a/Server/src/main/core/game/node/entity/impl/PulseType.java b/Server/src/main/core/game/node/entity/impl/PulseType.java new file mode 100644 index 000000000..315069557 --- /dev/null +++ b/Server/src/main/core/game/node/entity/impl/PulseType.java @@ -0,0 +1,11 @@ +package core.game.node.entity.impl; + +//facilitating pseudo-entity-queues in a clean manner with minimal disruption to existing code. +//this is NOT perfect. +//bit shit, really. +public enum PulseType { + STANDARD, //standard pulse slot, should be interrupted/replaced by most things + STRONG, //enforces itself as the only that can run + CUSTOM_1, //custom slots for extra tasks that should run alongside standard tasks. + CUSTOM_2 +} diff --git a/Server/src/main/java/core/game/node/entity/impl/WalkingQueue.java b/Server/src/main/core/game/node/entity/impl/WalkingQueue.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/impl/WalkingQueue.java rename to Server/src/main/core/game/node/entity/impl/WalkingQueue.java index 49fd4d737..fbd9d2e36 100644 --- a/Server/src/main/java/core/game/node/entity/impl/WalkingQueue.java +++ b/Server/src/main/core/game/node/entity/impl/WalkingQueue.java @@ -1,16 +1,24 @@ package core.game.node.entity.impl; +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 rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; +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. @@ -70,15 +80,29 @@ public final class WalkingQueue { boolean isPlayer = entity instanceof Player; this.walkDir = -1; this.runDir = -1; + if(entity.getLocation() == null) { + return; + } if (updateTeleport()) { return; } 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) { @@ -166,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; } @@ -293,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++) { @@ -334,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; } @@ -345,11 +380,7 @@ public final class WalkingQueue { * @return {@code True} if so. */ public boolean hasPath() { - if (!walkingQueue.isEmpty()) { - Point p = walkingQueue.peek(); - return p.getDirection() != null; - } - return false; + return !walkingQueue.isEmpty(); } /** @@ -372,8 +403,17 @@ public final class WalkingQueue { * @param running The running flag (ctrl + click reward). */ public void reset(boolean running) { + Location loc = entity.getLocation(); + + if (loc == null) { + 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?" + ); + } + walkingQueue.clear(); - walkingQueue.add(new Point(entity.getLocation().getX(), entity.getLocation().getY())); + walkingQueue.add(new Point(loc.getX(), loc.getY())); this.running = running; } diff --git a/Server/src/main/java/core/game/node/entity/lock/ActionLocks.java b/Server/src/main/core/game/node/entity/lock/ActionLocks.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/lock/ActionLocks.java rename to Server/src/main/core/game/node/entity/lock/ActionLocks.java index f24ea9a9a..e8a816a88 100644 --- a/Server/src/main/java/core/game/node/entity/lock/ActionLocks.java +++ b/Server/src/main/core/game/node/entity/lock/ActionLocks.java @@ -1,7 +1,7 @@ package core.game.node.entity.lock; import core.game.node.Node; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import java.util.HashMap; import java.util.Map; @@ -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/java/core/game/node/entity/lock/Lock.java b/Server/src/main/core/game/node/entity/lock/Lock.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/lock/Lock.java rename to Server/src/main/core/game/node/entity/lock/Lock.java index 005e55e64..efa739c03 100644 --- a/Server/src/main/java/core/game/node/entity/lock/Lock.java +++ b/Server/src/main/core/game/node/entity/lock/Lock.java @@ -1,6 +1,6 @@ package core.game.node.entity.lock; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * Represents a lock. diff --git a/Server/src/main/java/core/game/node/entity/lock/LockElapse.java b/Server/src/main/core/game/node/entity/lock/LockElapse.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/lock/LockElapse.java rename to Server/src/main/core/game/node/entity/lock/LockElapse.java diff --git a/Server/src/main/core/game/node/entity/npc/AbstractNPC.java b/Server/src/main/core/game/node/entity/npc/AbstractNPC.java new file mode 100644 index 000000000..b98656677 --- /dev/null +++ b/Server/src/main/core/game/node/entity/npc/AbstractNPC.java @@ -0,0 +1,88 @@ +package core.game.node.entity.npc; + +import core.tools.Log; +import core.tools.SystemLogger; +import core.game.world.map.Location; +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 + */ +public abstract class AbstractNPC extends NPC implements Plugin { + + /** + * The abstract NPC mapping. + */ + protected static Map mapping = new HashMap<>(); + + /** + * Constructs a new {@code AbstractNPC} {@Code Object}. + * @param id The id. + * @param location The location. + */ + public AbstractNPC(int id, Location location) { + this(id, location, true); + } + + /** + * Constructs a new {@code AbstractNPC} {@Code Object}. + * @param id The id. + * @param location The location. + * @param autowalk If the NPC should move around. + */ + public AbstractNPC(int id, Location location, boolean autowalk) { + super(id, location); + super.setWalks(autowalk); + } + + @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()) { + log(this.getClass(), Log.ERR, "[" + getClass().getSimpleName() + "] - Warning: Mapping already contained NPC id " + id + "! (" + name + ")"); + continue; + } + } + mapping.put(id, this); + } + return this; + } + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + /** + * Constructs a new instance of this abstract NPC. + * @param id The npc id. + * @param location The location. + * @param objects TODO + * @return The abstract npc instance. + */ + public abstract AbstractNPC construct(int id, Location location, Object... objects); + + /** + * Gets the NPC ids using this abstract NPC handler. + * @return The NPC ids. + */ + public abstract int[] getIds(); + + /** + * Gets the abstract NPC object for this NPC id. + * @param npcId The NPC id. + * @return The abstract NPC "loader" object for the given NPC id. + */ + public static AbstractNPC forId(int npcId) { + return mapping.get(npcId); + } + +} diff --git a/Server/src/main/java/core/game/node/entity/npc/IdleAbstractNPC.java b/Server/src/main/core/game/node/entity/npc/IdleAbstractNPC.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/IdleAbstractNPC.java rename to Server/src/main/core/game/node/entity/npc/IdleAbstractNPC.java index 6c96cda72..a2047b8b5 100644 --- a/Server/src/main/java/core/game/node/entity/npc/IdleAbstractNPC.java +++ b/Server/src/main/core/game/node/entity/npc/IdleAbstractNPC.java @@ -57,17 +57,19 @@ public abstract class IdleAbstractNPC extends AbstractNPC { } @Override - public boolean isAttackable(Entity entity, CombatStyle style) { + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { if (isInvisible()) { return false; } - if (getTask() != null && entity instanceof Player && getTask().levelReq > entity.getSkills().getStaticLevel(Skills.SLAYER)) { - ((Player) entity).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster."); + 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."); + } } if (DeathTask.isDead(this)) { return false; } - if (!entity.getZoneMonitor().continueAttack(this, style)) { + if (!entity.getZoneMonitor().continueAttack(this, style, message)) { return false; } return true; @@ -136,4 +138,4 @@ public abstract class IdleAbstractNPC extends AbstractNPC { this.idle = idle; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/npc/NPC.java b/Server/src/main/core/game/node/entity/npc/NPC.java new file mode 100644 index 000000000..18334251e --- /dev/null +++ b/Server/src/main/core/game/node/entity/npc/NPC.java @@ -0,0 +1,1063 @@ +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.InteractPlugin; +import core.game.interaction.MovementPulse; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.spell.CombatSpell; +import core.game.node.entity.combat.CombatStyle; +import core.game.node.entity.combat.spell.DefaultCombatSpell; +import core.game.node.entity.combat.equipment.WeaponInterface; +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.link.SpellBookManager; +import core.game.node.entity.player.link.audio.Audio; +import core.game.node.entity.skill.Skills; +import content.global.skill.slayer.Tasks; +import content.global.skill.summoning.familiar.Familiar; +import core.game.world.map.Direction; +import core.game.world.map.Location; +import core.game.world.map.RegionManager; +import core.game.world.map.build.DynamicRegion; +import core.game.world.map.path.Pathfinder; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; +import core.game.world.update.flag.*; +import core.tools.RandomFunction; +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; +import core.game.world.GameWorld; +import core.game.world.repository.Repository; + +import static core.game.system.command.sets.StatAttributeKeysKt.STATS_BASE; +import static core.game.system.command.sets.StatAttributeKeysKt.STATS_ENEMIES_KILLED; + +/** + * Represents a non-player character. + * @author Emperor + */ +public class NPC extends Entity { + + /** + * The non-player character's id. + */ + private int id; + + /** + * The original NPC id. + */ + private final int originalId; + + /** + * The NPC definitions. + */ + private NPCDefinition definition; + + /** + * If the NPC walks; + */ + private boolean walks; + + /** + * If the NPC is aggressive. + */ + private boolean aggressive; + + /** + * Represents if the NPC will respawn. + */ + private boolean respawn = true; + + /** + * The movement path of the NPC. + */ + protected Location[] movementPath; + + /** + * The walking radius. + */ + protected int walkRadius = 11; + + /** + * The movement index. + */ + protected int movementIndex; + + /** + * The respawn tick. + */ + private int respawnTick = -1; + + /** + * If the NPC walks bound to a path. + */ + protected boolean pathBoundMovement; + + /** + * The current dialogue. + */ + protected Player dialoguePlayer; + + /** + * The aggressive handler of the NPC. + */ + protected AggressiveHandler aggressiveHandler; + + /** + * The walk back radius. + */ + protected int nextWalk; + + /** + * The children NPCs. + */ + private NPC[] children; + + /** + * The amount of slayer experience received when killing this NPC. + */ + private double slayerExperience; + + /** + * The slayer task. + */ + private Tasks task; + + /** + * If the npc can never walk. + */ + private boolean neverWalks; + + /** + * The force talk string. + */ + private String forceTalk; + + public NPCBehavior behavior; + + public boolean isRespawning = false; + + /** + * Constructs a new {@code NPC} {@code Object}. + * @param id The NPC id. + */ + public NPC(int id) { + this(id, null); + } + + /** + * Constructs a new {@code NPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + protected NPC(int id, Location location, Direction direction) { + super(NPCDefinition.forId(id).getName(), location); + this.id = id; + this.originalId = id; + this.definition = NPCDefinition.forId(id); + super.size = definition.size; + super.direction = direction; + super.interactPlugin = new InteractPlugin(this); + this.behavior = NPCBehavior.forId(id); + } + + /** + * Constructs a new {@code NPC} {@code Object}. + * @param id the id. + * @param location the location. + */ + public NPC(int id, Location location) { + this(id, location, Direction.SOUTH); + } + + /** + * Creates a new NPC object. + * @param id The NPC id. + * @param location The location. + * @param direction the dir. + * @param objects the objects. + * @return The NPC object. + */ + 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); + } + if (n == null) { + n = new NPC(id, location, direction); + } + return n; + } + + /** + * Creates a new NPC object. + * @param id the id. + * @param location the location. + * @return the npc object. + */ + public static NPC create(int id, Location location, Object... objects) { + return create(id, location, Direction.SOUTH, objects); + } + + @Override + public void init() { + super.init(); + getProperties().setSpawnLocation(getLocation()); + initConfig(); + Repository.getNpcs().add(this); + RegionManager.move(this); + if (getViewport().getRegion().isActive()) { + Repository.addRenderableNPC(this); + } + 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.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 + public void clear() { + super.clear(); + 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. + */ + public void initConfig() { + int defaultLevel = definition.getCombatLevel() / 2; + getProperties().setCombatLevel(definition.getCombatLevel()); + getSkills().setStaticLevel(Skills.ATTACK, definition.getConfiguration(NPCConfigParser.ATTACK_LEVEL, defaultLevel)); + getSkills().setStaticLevel(Skills.STRENGTH, definition.getConfiguration(NPCConfigParser.STRENGTH_LEVEL, defaultLevel)); + getSkills().setStaticLevel(Skills.DEFENCE, definition.getConfiguration(NPCConfigParser.DEFENCE_LEVEL, defaultLevel)); + getSkills().setStaticLevel(Skills.RANGE, definition.getConfiguration(NPCConfigParser.RANGE_LEVEL, defaultLevel)); + getSkills().setStaticLevel(Skills.MAGIC, definition.getConfiguration(NPCConfigParser.MAGIC_LEVEL, defaultLevel)); + getSkills().setStaticLevel(Skills.HITPOINTS, definition.getConfiguration(NPCConfigParser.LIFEPOINTS, defaultLevel)); + int lp = getSkills().getMaximumLifepoints(); + if (this instanceof Familiar) { + lp = getAttribute("hp", lp); + } + getSkills().setLevel(Skills.HITPOINTS, lp); + aggressive = definition.getConfiguration(NPCConfigParser.AGGRESSIVE, aggressive); + Animation anim = null; + for (int i = 0; i < 3; i++) { + if (definition.getCombatAnimation(i) != null) { + getProperties().setAttackAnimation(anim = definition.getCombatAnimation(i)); + break; + } + } + if (definition.getCombatAnimation(3) != null) { + getProperties().setDefenceAnimation(definition.getCombatAnimation(3)); + } + if (definition.getCombatAnimation(4) != null) { + getProperties().setDeathAnimation(definition.getCombatAnimation(4)); + } + if (definition.getCombatAnimation(1) != null) { + getProperties().setMagicAnimation(definition.getCombatAnimation(1)); + } + if (definition.getCombatAnimation(2) != null) { + getProperties().setRangeAnimation(definition.getCombatAnimation(2)); + } + if (getProperties().getAttackAnimation() == null && anim != null) { + getProperties().setAttackAnimation(anim); + } + if (getProperties().getMagicAnimation() == null && anim != null) { + getProperties().setMagicAnimation(anim); + } + if (getProperties().getRangeAnimation() == null && anim != null) { + getProperties().setRangeAnimation(anim); + } + definition.initCombatGraphics(definition.getHandlers()); + getProperties().setBonuses(definition.getConfiguration(NPCConfigParser.BONUSES, new int[15])); + getProperties().setAttackSpeed(definition.getConfiguration(NPCConfigParser.ATTACK_SPEED, 5)); + forceTalk = definition.getConfiguration("force_talk", null); + if (definition.getConfiguration("movement_radius") != null) { + this.setWalkRadius(definition.getConfiguration("movement_radius")); + } + if(definition.getConfiguration("death_gfx") != null) { + getProperties().deathGfx = new Graphics(definition.getConfiguration("death_gfx")); + } + } + + /** + * Opens the shop for a player. + * @param player the player. + * @return {@code True} if so. + */ + public boolean openShop(Player player) { + if (getName().contains("assistant")) { + NPC n = RegionManager.getNpc(this, getId() - 1); + if (n != null) { + return n.openShop(player); + } + } + + if (Shops.getShopsByNpc().get(id) == null) { + return false; + } + Shops.getShopsByNpc().get(id).openFor(player); + + //Fix for issue #11 for shops keeping dialogue open. + DialoguePlugin dialogue = player.getDialogueInterpreter().getDialogue(); + if (dialogue != null) + dialogue.end(); + return true; + } + + @Override + public boolean isInvisible() { + if (!isActive() || getRespawnTick() > GameWorld.getTicks()) { + return true; + } + return super.isInvisible(); + } + + /** + * Checks if the NPC is hidden for the player. + * @param player The player. + * @return {@code True} if so. + */ + public boolean isHidden(Player player) { + return isInvisible(); + } + + /** + * Sets the default aggressive behavior of the NPC. + */ + public void setDefaultBehavior() { + if (aggressive && definition.getCombatLevel() > 0) { + aggressiveHandler = new AggressiveHandler(this, AggressiveBehavior.DEFAULT); + aggressiveHandler.setRadius(definition.getConfiguration(NPCConfigParser.AGGRESSIVE_RADIUS, 4)); + } + } + + /** + * Configures the movement path. + * @param movementPath The movement path. + */ + public void configureMovementPath(Location... movementPath) { + this.movementPath = movementPath; + this.movementIndex = 0; + this.pathBoundMovement = true; + } + + @Override + public void checkImpact(BattleState state) { + super.checkImpact(state); + Entity entity = state.getAttacker(); + 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 continueAttack(Entity target, CombatStyle style, boolean message) { + if (isInvisible()) { + return false; + } + return true; + } + + @Override + public int getDragonfireProtection(boolean fire) { + return 0; + } + + @Override + public void tick() { + if (!getViewport().getRegion().isActive()) { + onRegionInactivity(); + return; + } + if (respawnTick > GameWorld.getTicks()) { + if (respawnTick - GameWorld.getTicks() == 2) + teleport(getProperties().getSpawnLocation()); + return; + } + if (isRespawning && respawnTick <= GameWorld.getTicks()) { + behavior.onRespawn(this); + onRespawn(); + fullRestore(); + isRespawning = false; + } + handleTickActions(); + super.tick(); + } + + /** + * Called when the NPC respawns. + */ + protected void onRespawn() {} + + /** + * 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; + } + if (aggressiveHandler != null) { + aggressiveHandler.setPauseTicks(walkRadius + 1); + } + nextWalk = GameWorld.getTicks() + walkRadius + 1; + getLocks().lockMovement(100); + getImpactHandler().setDisabledTicks(100); + setAttribute("return-to-spawn", true); + + MovementPulse returnPulse = new MovementPulse(this, getProperties().getSpawnLocation(), Pathfinder.SMART) { + @Override + public boolean pulse() { + getProperties().getCombatPulse().stop(); + getLocks().unlockMovement(); + 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()) { + dialoguePlayer = null; + if (walks && !getPulseManager().hasPulseRunning() && !getProperties().getCombatPulse().isAttacking() && !getProperties().getCombatPulse().isInCombat() && nextWalk < GameWorld.getTicks()) { + if (walkToNextDest()) return; + } + } + } + if (aggressive && aggressiveHandler != null && aggressiveHandler.selectTarget()) { + return; + } + if (getProperties().getCombatPulse().isAttacking()) { + return; + } + } + if (forceTalk != null && getAttribute("lastForceTalk", 0) < GameWorld.getTicks()) { + sendChat(forceTalk); + setAttribute("lastForceTalk", GameWorld.getTicks() + RandomFunction.random(15, 30)); + } + } + + private boolean walkToNextDest() { + if (RandomFunction.nextBool()) return true; + setNextWalk(); + Location l = getMovementDestination(); + if (canMove(l)) { + if((Boolean) definition.getHandlers().getOrDefault("water_npc",false)){ + Pathfinder.findWater(this,l,true,Pathfinder.DUMB).walk(this); + } else { + Pathfinder.find(this, l, true, Pathfinder.DUMB).walk(this); + } + } + return false; + } + + public int getNextWalk() { + return nextWalk; + } + /** + * Sets the next walk. + */ + public void setNextWalk() { + nextWalk = GameWorld.getTicks() + 5 + RandomFunction.randomize(10); + } + + public void resetWalk() { + nextWalk = GameWorld.getTicks() - 1; + getWalkingQueue().reset(); + } + + /** + * Called when the region goes inactive. + */ + public void onRegionInactivity() { + 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(); + } + } + + @Override + public boolean isPoisonImmune() { + 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; + } + Player p = !(killer instanceof Player) ? null : (Player) killer; + if (p != null) { + p.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_ENEMIES_KILLED); + 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); + } + + /** + * Handles the drops of the npc. + * @param p the p. + * @param killer the killer. + */ + public void handleDrops(Player p, Entity killer) { + if (getAttribute("disable:drop", false)) { + return; + } + if (killer instanceof Player && p != null && p.getIronmanManager().isIronman() && getImpactHandler().getPlayerImpactLog().size() > 1) { + return; + } + if (definition == null || definition.getDropTables() == null) { + return; + } + definition.getDropTables().drop(this, killer); + } + + @Override + public void update() { + super.update(); + } + + @Override + public void reset() { + super.reset(); + } + + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + CombatSwingHandler original = getProperties().getCombatPulse().getStyle().getSwingHandler(); + return behavior.getSwingHandlerOverride(this, original); + } + + /** + * Gets an audio piece. + * @param index the index. + * @return the audio. + */ + public Audio getAudio(int index) { + int[] audios = getDefinition().getConfiguration(NPCConfigParser.COMBAT_AUDIO, null); + if (audios != null) { + Audio audio = new Audio(audios[index]); + if (audio != null && audio.id != 0) { + return audio; + } + } + return null; + } + + /** + * Configures the NPC.
Override this instead of {@link #init()} when + * creating a sub class. + */ + public void configure() { + int[] bonus = definition.getConfiguration(NPCConfigParser.BONUSES, new int[3]); + int highest = 0; + int index = 0; + for (int i = 0; i < 3; i++) { + if (bonus[i] > highest) { + highest = bonus[i]; + index = i; + } + } + CombatStyle protectStyle = definition.getConfiguration(NPCConfigParser.PROTECT_STYLE, null); + if (protectStyle != null) { + getProperties().setProtectStyle(protectStyle); + } + getProperties().setAttackStyle(new WeaponInterface.AttackStyle(WeaponInterface.STYLE_CONTROLLED, index)); + CombatStyle style = getDefinition().getConfiguration(NPCConfigParser.COMBAT_STYLE); + if (style != null) { + getProperties().getCombatPulse().setStyle(style); + } + if (style == CombatStyle.MAGIC) { + getProperties().setAutocastSpell(new DefaultCombatSpell(this)); + int spell = definition.getConfiguration("spell_id", -1); + if (spell != -1) { + getProperties().setSpell((CombatSpell) SpellBookManager.SpellBook.MODERN.getSpell(spell)); + getProperties().setAutocastSpell((CombatSpell) SpellBookManager.SpellBook.MODERN.getSpell(spell)); + } + } + task = Tasks.forId(getId()); + } + + /** + * Method used to check if the NPC can attack the victim. This method is + * used for aggressive behavior. + * @return True if so. + */ + public boolean canAttack(final Entity victim) { + return true; + } + + @Override + public boolean hasProtectionPrayer(CombatStyle style) { + return getProperties().getProtectStyle() == style; + } + + /** + * Checks if the npc should ignore victim attack restrictions(i.e You can't + * attack this npc.") + * @param victim the victim. + * @return {@code True} if ignore. + */ + public boolean isIgnoreAttackRestrictions(Entity victim) { + return false; + } + + @Override + public String toString() { + return "NPC " + id + ", " + getLocation() + ", " + getIndex(); + } + + /** + * Transforms this NPC. + * @param id The new NPC id. + */ + public NPC transform(int id) { + this.id = id; + this.definition = NPCDefinition.forId(id); + super.name = definition.getName(); + super.size = definition.size; + super.interactPlugin = new InteractPlugin(this); + initConfig(); + configure(); + interactPlugin.setDefault(); + if (id == originalId) { + int ordinal = EntityFlags.getOrdinal (EFlagType.NPC, EntityFlag.TypeSwap); + getUpdateMasks().unregisterSynced(ordinal); + } + 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; + } + + /** + * Transforms this NPC back to original. + */ + public void reTransform() { + if (originalId == this.id) { + return; + } + transform(originalId); + } + + /** + * Gets the movement destination of the NPC. + * @return The movement destination. + */ + protected Location getMovementDestination() { + if (!pathBoundMovement || movementPath == null || movementPath.length < 1) { + 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 = 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++]; + if (movementIndex == movementPath.length) { + movementIndex = 0; + } + return l; + } + + /** + * Gets the drop location. + * @return The drop location. + */ + public Location getDropLocation() { + return getLocation(); + } + + /** + * Checks if the npc can start combat. + * @param victim the victim. + * @return {@code True} if so. + */ + public boolean canStartCombat(Entity victim) { + return true; + } + + /** + * Gets the definition. + * @return The definition. + */ + public NPCDefinition getDefinition() { + return definition; + } + + /** + * Sets the definition. + * @param definition The definition to set. + */ + public void setDefinition(NPCDefinition definition) { + this.definition = definition; + } + + /** + * Get the id. + * @return The non-player character's id. + */ + public int getId() { + return id; + } + + /** + * Set the id. + * @param id The non-player character's id. + */ + public void setId(int id) { + this.id = id; + } + + /** + * Gets the walks. + * @return The walks. + */ + public boolean isWalks() { + return walks; + } + + /** + * Sets the walks. + * @param walks The walks to set. + */ + public void setWalks(boolean walks) { + this.walks = walks; + } + + /** + * Gets the aggressive. + * @return The aggressive. + */ + public boolean isAggressive() { + return aggressive; + } + + /** + * Sets the aggressive. + * @param aggressive The aggressive to set. + */ + public void setAggressive(boolean aggressive) { + this.aggressive = aggressive; + } + + /** + * Sets the name. + * @param @name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets the movementPath. + * @return The movementPath. + */ + public Location[] getMovementPath() { + return movementPath; + } + + /** + * @return the respawn. + */ + public boolean isRespawn() { + return respawn; + } + + /** + * @param respawn the respawn to set. + */ + public void setRespawn(boolean respawn) { + this.respawn = respawn; + } + + /** + * Gets the pathBoundMovement. + * @return The pathBoundMovement. + */ + public boolean isPathBoundMovement() { + return pathBoundMovement; + } + + /** + * Sets the pathBoundMovement. + * @param pathBoundMovement The pathBoundMovement. + */ + public void setPathBoundMovement(boolean pathBoundMovement) { + this.pathBoundMovement = pathBoundMovement; + } + + /** + * Gets the dialoguePlayer. + * @return The dialoguePlayer. + */ + public Player getDialoguePlayer() { + return dialoguePlayer; + } + + /** + * Sets the dialoguePlayer. + */ + public void setDialoguePlayer(Player dialoguePlayer) { + this.dialoguePlayer = dialoguePlayer; + } + + /** + * Gets the aggressive handler. + * @return The aggressive handler. + */ + public AggressiveHandler getAggressiveHandler() { + return aggressiveHandler; + } + + /** + * Sets the aggressive handler. + * @param handler The handler. + */ + public void setAggressiveHandler(AggressiveHandler handler) { + this.aggressiveHandler = handler; + } + + /** + * Gets the respawnTick. + * @return The respawnTick. + */ + public int getRespawnTick() { + return respawnTick; + } + + /** + * Sets the walking radius. + * @param radius The walking radius. + */ + public void setWalkRadius(int radius) { + this.walkRadius = radius; + } + + /** + * Gets the walking radius. + * @return the walking radius. + */ + public int getWalkRadius() { + return walkRadius; + } + + /** + * Sets the respawnTick. + * @param respawnTick The respawnTick to set. + */ + public void setRespawnTick(int respawnTick) { + this.respawnTick = respawnTick; + } + + /** + * Gets the originalId. + * @return The originalId. + */ + public int getOriginalId() { + return originalId; + } + + /** + * Gets the currently shown NPC. + * @param player The player to check for. + * @return The shown NPC. + */ + public NPC getShownNPC(Player player) { + if (children == null) { + return this; + } + int npcId = definition.getChildNPC(player).getId(); + for (NPC npc : children) { + if (npc.getId() == npcId) { + return npc; + } + } + return this; + } + + /** + * Gets the slayerExperience. + * @return The slayerExperience. + */ + public double getSlayerExperience() { + return slayerExperience; + } + + /** + * Sets the slayerExperience. + * @param slayerExperience The slayerExperience to set. + */ + public void setSlayerExperience(double slayerExperience) { + this.slayerExperience = slayerExperience; + } + + /** + * Gets the task. + * @return The task. + */ + public Tasks getTask() { + return task; + } + + /** + * Sets the task. + * @param task The task to set. + */ + public void setTask(Tasks task) { + this.task = task; + } + + /** + * Gets the neverWalks. + * @return the neverWalks + */ + public boolean isNeverWalks() { + return neverWalks; + } + + /** + * Sets the baneverWalks. + * @param neverWalks the neverWalks to set. + */ + public void setNeverWalks(boolean neverWalks) { + this.neverWalks = neverWalks; + } + +} 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/java/core/game/node/entity/npc/agg/AggressiveBehavior.java b/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/npc/agg/AggressiveBehavior.java rename to Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java index 254926ad9..50f93be3f 100644 --- a/Server/src/main/java/core/game/node/entity/npc/agg/AggressiveBehavior.java +++ b/Server/src/main/core/game/node/entity/npc/agg/AggressiveBehavior.java @@ -4,7 +4,7 @@ import core.game.node.entity.Entity; import core.game.node.entity.combat.DeathTask; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.map.zone.impl.WildernessZone; import core.game.world.map.RegionManager; import java.util.ArrayList; @@ -71,14 +71,8 @@ public class AggressiveBehavior { } if (entity instanceof NPC && target instanceof Player) { NPC npc = (NPC) entity; - if (npc.getAggressiveHandler() != null && npc.getAggressiveHandler().isAllowTolerance()) { - if (RegionManager.forId(regionId).isTolerated(target.asPlayer())) { - return false; - } - int ticks = GameWorld.getTicks() - npc.getAggressiveHandler().getPlayerTolerance()[target.getIndex()]; - if (ticks > 3000) { - npc.getAggressiveHandler().getPlayerTolerance()[target.getIndex()] = GameWorld.getTicks(); - } else if (ticks > 1500) { + if (npc.getAggressiveHandler() != null && npc.getAggressiveHandler().isAllowTolerance() && !WildernessZone.isInZone(npc)) { + if (RegionManager.forId(regionId).isTolerated(target.asPlayer())) { return false; } } @@ -90,9 +84,9 @@ public class AggressiveBehavior { return true; } - public boolean ignoreCombatLevelDifference() { - return false; - } + public boolean ignoreCombatLevelDifference() { + return false; + } /** * Gets the priority flag. @@ -138,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/java/core/game/node/entity/npc/agg/AggressiveHandler.java b/Server/src/main/core/game/node/entity/npc/agg/AggressiveHandler.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/npc/agg/AggressiveHandler.java rename to Server/src/main/core/game/node/entity/npc/agg/AggressiveHandler.java index 3d960d549..c2a49842d 100644 --- a/Server/src/main/java/core/game/node/entity/npc/agg/AggressiveHandler.java +++ b/Server/src/main/core/game/node/entity/npc/agg/AggressiveHandler.java @@ -1,11 +1,12 @@ package core.game.node.entity.npc.agg; -import rs09.ServerConstants; +import core.game.node.entity.npc.NPC; +import core.ServerConstants; import core.game.node.entity.Entity; import core.game.node.entity.combat.DeathTask; import core.game.node.entity.player.Player; import core.game.node.entity.player.info.Rights; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.tools.RandomFunction; /** @@ -89,15 +90,13 @@ public final class AggressiveHandler { } if (target != null) { target.setAttribute("aggressor", entity); - if (target != entity.getProperties().getCombatPulse().getVictim()) { - if (entity.getProperties().getCombatPulse().isAttacking()) { - entity.getProperties().getCombatPulse().setVictim(target); - entity.face(target); - } else { - entity.getProperties().getCombatPulse().attack(target); - } - return true; + if (entity.getProperties().getCombatPulse().isAttacking()) { + entity.getProperties().getCombatPulse().setVictim(target); + entity.face(target); + } else { + entity.getProperties().getCombatPulse().attack(target); } + return true; } return entity.getProperties().getCombatPulse().isAttacking(); } @@ -187,7 +186,11 @@ public final class AggressiveHandler { * @return The allowTolerance. */ public boolean isAllowTolerance() { - return allowTolerance; + boolean configSetting = true; + if(entity instanceof NPC){ + configSetting = ((NPC) entity).getDefinition().getConfiguration("can_tolerate", true); + } + return allowTolerance && configSetting; } /** diff --git a/Server/src/main/java/core/game/node/entity/npc/drop/DropFrequency.java b/Server/src/main/core/game/node/entity/npc/drop/DropFrequency.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/npc/drop/DropFrequency.java rename to Server/src/main/core/game/node/entity/npc/drop/DropFrequency.java diff --git a/Server/src/main/java/core/game/node/entity/npc/drop/NPCDropTables.java b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java similarity index 80% rename from Server/src/main/java/core/game/node/entity/npc/drop/NPCDropTables.java rename to Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java index 9f6177596..baacc117b 100644 --- a/Server/src/main/java/core/game/node/entity/npc/drop/NPCDropTables.java +++ b/Server/src/main/core/game/node/entity/npc/drop/NPCDropTables.java @@ -1,9 +1,10 @@ package core.game.node.entity.npc.drop; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; + +import content.data.tables.*; import core.cache.def.impl.NPCDefinition; -import core.game.content.global.Bones; -import core.game.ge.GrandExchangeDatabase; +import content.global.skill.prayer.Bones; import core.game.node.entity.Entity; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -15,14 +16,12 @@ import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.tools.RandomFunction; import core.tools.StringUtils; -import rs09.game.ai.AIPlayer; -import rs09.game.ai.AIRepository; -import rs09.game.ai.general.GeneralBotCreator; -import rs09.game.content.global.GlobalKillCounter; -import rs09.game.content.global.NPCDropTable; -import rs09.game.ge.OfferManager; -import rs09.game.system.config.ItemConfigParser; -import rs09.game.world.repository.Repository; +import core.game.bots.AIPlayer; +import core.game.bots.AIRepository; +import core.game.bots.GeneralBotCreator; +import core.api.utils.NPCDropTable; +import core.game.ge.GrandExchange; +import core.game.world.repository.Repository; import java.util.ArrayList; import java.util.List; @@ -43,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. @@ -75,7 +74,15 @@ public final class NPCDropTables { */ public void drop(NPC npc, Entity looter) { Player p = looter instanceof Player ? (Player) looter : null; - table.roll().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; } /** @@ -92,15 +99,6 @@ public final class NPCDropTables { if (handleBoneCrusher(player, item)) { return; } - if (item.getId() == RareDropTable.SLOT_ITEM_ID){ - item = RareDropTable.retrieve(); - } - if (item.getId() == 995 && player.getBank().hasSpaceFor(item) && ( player.getGlobalData().isEnableCoinMachine() )) { - item = new Item(995, (int) (item.getAmount() + (item.getAmount() * 0.25))); - player.getBank().add(item); - player.sendMessage(" " + item.getAmount() + " coins were sent to your bank."); - return; - } if (item.hasItemPlugin() && player != null) { if (!item.getPlugin().createDrop(item, player, npc, l)) { return; @@ -113,7 +111,7 @@ public final class NPCDropTables { } return; } - ContentAPI.announceIfRare(player, item); + announceIfRare(player, item); if(item.getId() == 6199 && player instanceof Player){ player.sendMessage("A mystery box has fallen on the ground."); } @@ -144,12 +142,12 @@ public final class NPCDropTables { */ public Player getLooter(Player player, NPC npc, Item item) { int itemId = item.getDefinition().isUnnoted() ? item.getId() : item.getNoteChange(); - if (player != null && npc.getProperties().isMultiZone() && (GrandExchangeDatabase.getDatabase().get(itemId) != null || item.getName().endsWith("charm")) && player.getCommunication().getClan() != null && player.getCommunication().isLootShare() && player.getCommunication().getLootRequirement().ordinal() >= player.getCommunication().getClan().getLootRequirement().ordinal() && !player.getIronmanManager().isIronman()) { + if (player != null && npc.getProperties().isMultiZone() && (item.getDefinition().isTradeable() || item.getName().endsWith("charm")) && player.getCommunication().getClan() != null && player.getCommunication().isLootShare() && player.getCommunication().getLootRequirement().ordinal() >= player.getCommunication().getClan().getLootRequirement().ordinal() && !player.getIronmanManager().isIronman()) { Player looter = player; 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); } } @@ -162,7 +160,7 @@ public final class NPCDropTables { } } player.sendMessage(player.getInterfaceManager().isResizable()+""); - int price = item.getName().endsWith("charm") ? 100 : OfferManager.getRecommendedPrice(itemId, false); + int price = item.getName().endsWith("charm") ? 100 : GrandExchange.getRecommendedPrice(itemId, false); looter.getGlobalData().setLootSharePoints(looter.getGlobalData().getLootSharePoints() - (price) + ((price / looters.size()))); looter.sendMessage((player.getInterfaceManager().isResizable() ? "" : "") + "You received: " + item.getAmount() + " " + item.getName()); for (Player p : looters) { diff --git a/Server/src/main/core/game/node/entity/player/Player.java b/Server/src/main/core/game/node/entity/player/Player.java new file mode 100644 index 000000000..d21c591fc --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/Player.java @@ -0,0 +1,1436 @@ +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.impl.BankContainer; +import core.game.container.impl.EquipmentContainer; +import core.game.container.impl.InventoryListener; +import core.game.dialogue.DialogueInterpreter; +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; +import core.game.node.entity.combat.DeathTask; +import core.game.node.entity.combat.ImpactHandler.HitsplatType; +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.diary.AchievementDiaryManager; +import core.game.node.entity.player.link.emote.EmoteManager; +import core.game.node.entity.player.link.music.MusicPlayer; +import core.game.node.entity.player.link.prayer.Prayer; +import core.game.node.entity.player.link.prayer.PrayerType; +import core.game.node.entity.player.link.quest.QuestRepository; +import core.game.node.entity.player.link.request.RequestManager; +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; +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.*; +import core.net.IoSession; +import core.net.packet.PacketRepository; +import core.net.packet.context.DynamicSceneContext; +import core.net.packet.context.SceneGraphContext; +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.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; +import core.ServerConstants; +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.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. + * @author Emperor + * @author Vexia + */ +public class Player extends Entity { + + /** + * The details of the player. + */ + private PlayerDetails details; + + public boolean inWardrobe = false; + + public Location startLocation = null; + + private final Graphics wardrobe_hold_graphics = new Graphics(1182,0,0); + + public boolean newPlayer = getSkills().getTotalLevel() < 50; + + public BankContainer dropLog = new BankContainer(this); + + public EquipmentDegrader degrader = new EquipmentDegrader(); + + public PouchManager pouchManager = new PouchManager(this); + + public VarpManager varpManager = new VarpManager(this); + + 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)); + + /** + * The equipment. + */ + private final EquipmentContainer equipment = new EquipmentContainer(this); + + /** + * The bank container. + */ + private final BankContainer bank = new BankContainer(this); + + /** + * The secondary bank container. + */ + private final BankContainer bankSecondary = new BankContainer(this); + + /** + * Is secondary bank in use or not + */ + public boolean useSecondaryBank = false; + + /** + * The packet dispatcher. + */ + private final PacketDispatch packetDispatch = new PacketDispatch(this); + + /** + * The spell book manager. + */ + private final SpellBookManager spellBookManager = new SpellBookManager(); + + /** + * The rendering info. + */ + private final RenderInfo renderInfo = new RenderInfo(this); + + /** + * The interface manager. + */ + private final InterfaceManager interfaceManager = new InterfaceManager(this); + + /** + * The emote manager. + */ + private final EmoteManager emoteManager = new EmoteManager(this); + + /** + * The player flags. + */ + private final PlayerFlags playerFlags = new PlayerFlags(); + + /** + * The appearance. + */ + private final Appearance appearance = new Appearance(this); + + /** + * The settings of the player. + */ + private final Settings settings = new Settings(this); + + /** + * The dialogue interpreter. + */ + private final DialogueInterpreter dialogueInterpreter = new DialogueInterpreter(this); + + /** + * The hint icon manager. + */ + private final HintIconManager hintIconManager = new HintIconManager(); + + /** + * The quest repository. + */ + public QuestRepository questRepository = new QuestRepository(this); + + /** + * The prayer manager. + */ + private final Prayer prayer = new Prayer(this); + + /** + * The skull manager. + */ + private final SkullManager skullManager = new SkullManager(this); + + /** + * The familiar manager. + */ + private final FamiliarManager familiarManager = new FamiliarManager(this); + + /** + * The saved data. + */ + public SavedData savedData = new SavedData(this); + + /** + * The request manager. + */ + private final RequestManager requestManager = new RequestManager(this); + + /** + * The farming manager. + */ + + /** + * Represents the players warning messages. + */ + private final WarningMessages warningMessages = new WarningMessages(); + + /** + * The music player instance. + */ + private final MusicPlayer musicPlayer = new MusicPlayer(this); + + /** + * The house manager. + */ + private final HouseManager houseManager = new HouseManager(); + + /** + * The bank pin manager. + */ + private final BankPinManager bankPinManager = new BankPinManager(this); + + /** + * The achievement diary manager. + */ + private final AchievementDiaryManager achievementDiaryManager = new AchievementDiaryManager(this); + + /** + * The Ironman manager. + */ + private final IronmanManager ironmanManager = new IronmanManager(this); + + /** + * The boolean for the player playing. + */ + private boolean playing; + + /** + * If the player is invisible. + */ + private boolean invisible; + + /** + * If the player is artificial. + */ + protected boolean artificial; + + /** + * The skiller tasks. + */ + protected SkillerTasks skillTasks = new SkillerTasks(); + + /** + * A custom state for bot debugging + */ + private String customState = ""; + + /** + * 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. + */ + public Player(PlayerDetails details) { + super(details.getUsername(), ServerConstants.START_LOCATION); + super.active = false; + super.interactPlugin = new InteractPlugin(this); + this.details = details; + this.direction = Direction.SOUTH; + } + + @Override + public void init() { + if (!artificial) { + 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() { + if (isArtificial()) { + finishClear(); + return; + } + Repository.getDisconnectionQueue().remove(getName()); + Repository.getDisconnectionQueue().add(this, true); + details.save(); + } + + /** + * Clears the player from the game. + * 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 finishClear() { + if (!isArtificial()) + GameWorld.getLogoutListeners().forEach((it) -> it.logout(this)); + setPlaying(false); + getWalkingQueue().reset(); + if(!logoutListeners.isEmpty()){ + logoutListeners.forEach((key,method) -> method.invoke(this)); + } + if (familiarManager.hasFamiliar()) { + familiarManager.getFamiliar().clear(); + } + interfaceManager.close(); + interfaceManager.closeSingleTab(); + super.clear(); + getZoneMonitor().clear(); + HouseManager.leave(this); + UpdateSequence.getRenderablePlayers().remove(this); + sendLogoutEvents(); + checkForWealthUpdate(true); + } + + private void sendLogoutEvents() { + PlayerStatusUpdate.Builder statusBuilder = PlayerStatusUpdate.newBuilder(); + statusBuilder.setUsername(this.name); + statusBuilder.setWorld(0); //offline + statusBuilder.setNotifyFriendsOnly(false); + ManagementEvents.publish(statusBuilder.build()); + + if (getCommunication().getClan() != null) { + ClanLeaveNotification.Builder event = ClanLeaveNotification.newBuilder(); + event.setUsername(getName()); + event.setWorld(GameWorld.getSettings().getWorldId()); + event.setClanName(getCommunication().getClan().getOwner()); + ManagementEvents.publish(event.build()); + } + } + + public void toggleWardrobe(boolean intoWardrobe){ + class wardrobePulse extends Pulse{ + final Player player; + boolean first = true; + wardrobePulse(Player player){ + this.player = player; + } + @Override + public boolean pulse() { + if(first){ + player.visualize(new Animation(1241), new Graphics(1181,0,0)); + first = false; + return !player.inWardrobe; + } + if(player.inWardrobe) { + player.visualize(new Animation(1241),wardrobe_hold_graphics); + } else { + player.visualize(new Animation(1241), new Graphics(1183,0,0)); + player.getPulseManager().run(new Pulse(1){ + @Override + public boolean pulse() { + player.getAnimator().reset(); + player.packetDispatch.sendInterfaceConfig(548,69,false); + return true; + } + }); + } + return !player.inWardrobe; + } + } + if(intoWardrobe){ + packetDispatch.sendInterfaceConfig(548,69,true); + GameWorld.getPulser().submit(new wardrobePulse(this)); + inWardrobe = true; + } else { + inWardrobe = false; + } + } + + @Override + public void tick() { + super.tick(); + musicPlayer.tick(); + if(getAttribute("fire:immune",0) > 0){ + int time = getAttribute("fire:immune",0) - GameWorld.getTicks(); + if(time == TickUtilsKt.secondsToTicks(30)){ + sendMessage(colorize("%RYou have 30 seconds remaining on your antifire potion.")); + playAudio(this, Sounds.CLOCK_TICK_1_3120, 0, 3); + } + if(time == 0){ + sendMessage(colorize("%RYour antifire potion has expired.")); + removeAttribute("fire:immune"); + playAudio(this, Sounds.DRAGON_POTION_FINISHED_2607); + } + } + if(getAttribute("poison:immunity",0) > 0){ + int time = getAttribute("poison:immunity",0) - GameWorld.getTicks(); + debug(time + ""); + if(time == TickUtilsKt.secondsToTicks(30)){ + sendMessage(colorize("%RYou have 30 seconds remaining on your antipoison potion.")); + playAudio(this, Sounds.CLOCK_TICK_1_3120, 0, 3); + } + if(time == 0){ + sendMessage(colorize("%RYour antipoison potion has expired.")); + removeAttribute("poison:immunity"); + playAudio(this, Sounds.DRAGON_POTION_FINISHED_2607); + } + } + if(getAttribute("infinite-special", false)) { + settings.setSpecialEnergy(100); + } + + // Decrement prayer points + getPrayer().tick(); + + // 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) { + if (isArtificial()) return; + long previousWealth = getAttribute("last-wealth", -1L); + long lastWealthCheck = getAttribute("last-wealth-check", -1L); + + long nowTime = System.currentTimeMillis(); + if (force || nowTime - lastWealthCheck >= TimeUnit.MINUTES.toMillis(5)) { + long totalWealth = 0L; + for (Item i : inventory.toArray()) { + if (i == null) continue; + totalWealth += (long) i.getDefinition().getValue() * i.getAmount(); + } + for (Item i : bank.toArray()) { + if (i == null) break; + totalWealth += (long) i.getDefinition().getValue() * i.getAmount(); + } + for (Item i : bankSecondary.toArray()) { + 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); + setAttribute("/save:last-wealth-check", nowTime); + + if (diff != 0) + PlayerMonitor.logWealthChange(this, totalWealth, diff); + } + } + + @Override + public void update() { + super.update(); + if (playerFlags.isUpdateSceneGraph()) { + updateSceneGraph(false); + } + PlayerRenderer.render(this); + NPCRenderer.render(this); + MapChunkRenderer.render(this); + } + + @Override + public void reset() { + super.reset(); + playerFlags.setUpdateSceneGraph(false); + renderInfo.updateInformation(); + if (getSkills().isLifepointsUpdate()) { + PacketRepository.send(SkillLevel.class, new SkillContext(this, Skills.HITPOINTS)); + getSkills().setLifepointsUpdate(false); + } + if (getAttribute("flagged-for-save", false)) { + PlayerParser.saveImmediately(this); + removeAttribute("flagged-for-save"); + } + Arrays.fill(opCounts, (byte) 0); + } + + @Override + public int getClientIndex() { + 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(); + int weaponId = equipment.getNew(3).getId(); + if (swing) { + if (getProperties().getSpell() != null || getProperties().getAutocastSpell() != null) { + return CombatStyle.MAGIC.getSwingHandler(); + } + if (settings.isSpecialToggled()) { + CombatSwingHandler handler; + if ((handler = style.getSwingHandler().getSpecial(weaponId)) != null) { + return handler; + } + packetDispatch.sendMessage("Unhandled special attack for item " + weaponId + "!"); + } + } + if (style == CombatStyle.RANGE && weaponId == 10033 || weaponId == 10034) { + return ChinchompaSwingHandler.getInstance(); + } + if (weaponId >= 10146 && weaponId <= 10149) { + return SalamanderSwingHandler.Companion.getINSTANCE(); + } + return style.getSwingHandler(); + } + + @Override + public void commenceDeath(Entity killer) { + if (!isPlaying()) return; + super.commenceDeath(killer); + if (prayer.get(PrayerType.RETRIBUTION)) { + prayer.startRetribution(killer); + } + } + + @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); + Player k = killer instanceof Player ? (Player) killer : this; + if (!k.isActive()) { + k = this; + } + if (this.isArtificial() && killer instanceof Player){ + setAttribute("dead", true); + } + 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 + 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 + permadeath(this); + return; + } + } + 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; + + 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); + sendMessage(colorize("%RDue to the circumstances of your death, you do not have a grave.")); + } + + equipment.clear(); + SlayerEquipmentFlags.updateFlags(this); + inventory.clear(); + inventory.addAll(c[0]); + familiarManager.dismiss(); + } + 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()) { + getInterfaceManager().open(new Component(153)); + } + } + + @Override + public boolean hasProtectionPrayer(CombatStyle style) { + if (style == null) { + return false; + } + return prayer.get(style.getProtectionPrayer()); + } + + @Override + public int getDragonfireProtection(boolean fire) { + int value = 0; + if (fire) { + if (hasFireResistance()) { + value |= 0x2; + } + } + Item item = equipment.get(EquipmentContainer.SLOT_SHIELD); + if (item != null && (item.getId() == 11283 || item.getId() == 11284 || (fire && (item.getId() == 1540) || (!fire && (item.getId() == 2890 || item.getId() == 9731))))) { + value |= 0x4; + } + if (prayer.get(PrayerType.PROTECT_FROM_MAGIC)) { + value |= 0x8; + } + setAttribute("fire_resistance", value); + return value; + } + + @Deprecated + @Override + public void setLocation(Location location) { + super.setLocation(location); + } + + @Override + public void fullRestore() { + prayer.reset(); + settings.setSpecialEnergy(100); + settings.updateRunEnergy(-100); + super.fullRestore(); + } + + @Override + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { + 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 timers.getTimer("poison:immunity") != null; + } + + @Override + public void onImpact(final Entity entity, BattleState state) { + super.onImpact(entity, state); + boolean recoil = getEquipment().getNew(EquipmentContainer.SLOT_RING).getId() == 2550; + if (state.getEstimatedHit() > 0) { + if (getAttribute("vengeance", false)) { + removeAttribute("vengeance"); + final int hit = (int) (state.getEstimatedHit() * 0.75); + sendChat("Taste vengeance!"); + if (hit > -1) { + entity.getImpactHandler().manualHit(Player.this, hit, HitsplatType.NORMAL); + } + } + if (recoil) { + getImpactHandler().handleRecoilEffect(entity, state.getEstimatedHit()); + } + } + if (state.getSecondaryHit() > 0) { + if (recoil) { + getImpactHandler().handleRecoilEffect(entity, state.getSecondaryHit()); + } + } + degrader.checkArmourDegrades(this); + } + + public void randomWalk(int radiusX, int radiusY) { + Pathfinder.find(this, this.getLocation().transform(RandomFunction.random(radiusX, (radiusX * -1)), RandomFunction.random(radiusY, (radiusY * -1)), 0), false, Pathfinder.SMART).walk(this); + } + + /** + * Initializes the player for reconnection. + */ + public void initReconnect() { + getInterfaceManager().setChatbox(null); + getPulseManager().clear(); + getZoneMonitor().getZones().clear(); + getViewport().setCurrentPlane(RegionManager.forId(66666).getPlanes()[3]); + playerFlags.setLastSceneGraph(null); + playerFlags.setUpdateSceneGraph(false); + playerFlags.setLastViewport(new RegionChunk[Viewport.CHUNK_SIZE][Viewport.CHUNK_SIZE]); + renderInfo.getLocalNpcs().clear(); + renderInfo.getLocalPlayers().clear(); + renderInfo.setLastLocation(null); + renderInfo.setOnFirstCycle(true); + Arrays.fill(renderInfo.getAppearanceStamps(),0); + } + + /** + * Checks if the player is wearing void. + */ + public boolean isWearingVoid(CombatStyle style) { + int helm; + if(style == CombatStyle.MELEE) { + helm = Items.VOID_MELEE_HELM_11665; + } else if(style == CombatStyle.RANGE) { + helm = Items.VOID_RANGER_HELM_11664; + } else if(style == CombatStyle.MAGIC) { + helm = Items.VOID_MAGE_HELM_11663; + } else { + return false; + } + boolean legs = inEquipment(this, Items.VOID_KNIGHT_ROBE_8840, 1); + boolean top = inEquipment(this, Items.VOID_KNIGHT_TOP_8839, 1) + || inEquipment(this, Items.VOID_KNIGHT_TOP_10611, 1); + boolean gloves = inEquipment(this, Items.VOID_KNIGHT_GLOVES_8842, 1); + return inEquipment(this, helm, 1) && legs && top && gloves; + } + + /** + * Updates the player's scene graph. + * @param login If the player is logging in. + */ + public void updateSceneGraph(boolean login) { + Region region = getViewport().getRegion(); + if (region instanceof DynamicRegion || region == null && (region = RegionManager.forId(location.getRegionId())) instanceof DynamicRegion) { + PacketRepository.send(BuildDynamicScene.class, new DynamicSceneContext(this, login)); + } else { + PacketRepository.send(UpdateSceneGraph.class, new SceneGraphContext(this, login)); + } + } + + /** + * Toggles the debug mode. + */ + public void toggleDebug() { + boolean debug = getAttribute("debug", false); + setAttribute("debug", !debug); + getPacketDispatch().sendMessage("Your debug mode is toggled to " + !debug + "."); + } + + /** + * Wrapper method for sending a message. + * @param messages the messages. + */ + public void sendMessages(String... messages) { + packetDispatch.sendMessages(messages); + } + + /** + * Wrapper method for sending a message. + * @param message the message. + */ + public void sendMessage(String message) { + sendMessages(message); + } + + /** + * Sends a notification message. + * @param message The message. + */ + public void sendNotificationMessage(String message) { + sendMessages("" + message + ""); + } + + /** + * Checks if the player can spawn. & Location + * @return {@code True} if so. + */ + public boolean spawnZone() { + return (getLocation().getX() > 3090 && getLocation().getY() < 3500 + && getLocation().getX() < 3099 && getLocation().getY() > 3487); + } + + public boolean canSpawn() { + if (!spawnZone()) { + sendMessage("You can only spawn items inside the edgeville bank."); + return true; + } + if (inCombat() || getLocks().isInteractionLocked() || getSkullManager().isWilderness() || getAttribute("activity", null) != null) { + sendMessage("You can't spawn items at the moment."); + return true; + } + return false; + } + + /** + * Sends a message on a tick. + * @param message the message. + * @param ticks the ticks. + */ + public void sendMessage(String message, int ticks) { + packetDispatch.sendMessage(message, ticks); + } + + /** + * Sends a message to the player if it's an administrator. + * @param string The message. + */ + public void debug(String string) { + if (getAttribute("debug",false)) { + packetDispatch.sendMessage(string); + } + } + + /** + * Grabs a players gender, using shorter amount of code + */ + + public boolean isMale() { + return this.getAppearance().getGender().ordinal() == 0; + } + + /** + * Sets the player details. + * @param details The player details. + */ + @SuppressWarnings("deprecation") + public void updateDetails(PlayerDetails details) { + if (this.details != null) { + details.setBanTime(this.details.getBanTime()); + details.setMuteTime(this.details.getMuteTime()); + } + details.getSession().setObject(this); + this.details = details; + } + + /** + * Checks if the player is allowed to be removed from the game. + * @return {@code True} if so. + */ + public boolean allowRemoval() { + return !(inCombat() || getSkills().getLifepoints() < 1 || DeathTask.isDead(this) || isTeleporting() || scripts.hasTypeInQueue(QueueStrength.SOFT)); + } + + /** + * Checks if the containers have this item. + * @param item the item. + * @return {@code True} if so. + */ + public boolean hasItem(Item item) { + return getInventory().containsItem(item) || getBank().containsItem(item) || getEquipment().containsItem(item); + } + + /** + * Gets the player extra experience mod. + * @return the mod. + */ + public double getExperienceMod() { + return getSavedData().getGlobalData().hasDoubleExp() ? 2 : 1; + } + + /** + * Checks if the player is a staff member. + * @return {@code True} if so. + */ + public boolean isStaff() { + return getDetails().getRights() != Rights.REGULAR_PLAYER; + } + + /** + * Checks if the player is an admin. + * @return true if so. + */ + public boolean isAdmin() { + return getDetails().getRights() == Rights.ADMINISTRATOR; + } + + /** + * Checks if the player is debugging. + * @return {@code True} if so. + */ + public boolean isDebug() { + return details.getRights() == Rights.ADMINISTRATOR && getAttribute("debug", false); + } + + /** + * Gets the uid info. + * @return the info. + */ + public UIDInfo getUidInfo() { + return details.getInfo(); + } + + /** + * Gets the {@code PlayerDetails}. + * @return the details. + */ + public PlayerDetails getDetails() { + return details; + } + + /** + * Gets the name. + * @return the name. + */ + public String getName() { +// return display ? details.getDisplayName() : super.getName(); + return super.getName(); + } + + /** + * Gets the players {@code Session}. + * @return the {@code PlayerDetails} {@code Object} session. + */ + public IoSession getSession() { + return details.getSession(); + } + + /** + * Gets the {@code Equipment} + * @return {@code Equipment}. + */ + public EquipmentContainer getEquipment() { + return equipment; + } + + /** + * Gets the current active bank. + * @return Current active bank. + */ + public BankContainer getBank() { + return useSecondaryBank ? bankSecondary : bank; + } + + /** + * Gets the primary bank. + * @return Primary bank + */ + public BankContainer getBankPrimary() { + return bank; + } + + /** + * Gets the Secondary bank. + * @return Secondary bank + */ + public BankContainer getBankSecondary() { + return bankSecondary; + } + + public BankContainer getDropLog() {return dropLog;} + + /** + * @return the inventory + */ + public Container getInventory() { + return inventory; + } + + /** + * Sets the playing flag. + * @param playing the flag to set. + */ + public void setPlaying(boolean playing) { + this.playing = playing; + } + + /** + * Checks if the player is playing. + * @return {@code True} if so. + */ + public boolean isPlaying() { + return playing; + } + + /** + * Gets the rights of the player. + * @return the rights. + */ + public Rights getRights() { + return details.getRights(); + } + + /** + * Gets the renderInfo. + * @return The renderInfo. + */ + public RenderInfo getRenderInfo() { + return renderInfo; + } + + /** + * Gets the appearance. + * @return The appearance. + */ + public Appearance getAppearance() { + return appearance; + } + + /** + * Gets the playerFlags. + * @return The playerFlags. + */ + public PlayerFlags getPlayerFlags() { + return playerFlags; + } + + /** + * Gets the {@code PacketDispatch}. + * @return the packet dispatch. + */ + public PacketDispatch getPacketDispatch() { + return packetDispatch; + } + + + /** + * @return the spellBookManager + */ + public SpellBookManager getSpellBookManager() { + return spellBookManager; + } + + /** + * Gets the settings. + * @return The settings. + */ + public Settings getSettings() { + return settings; + } + + /** + * @return the interface manager. + */ + public InterfaceManager getInterfaceManager() { + 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. + */ + public DialogueInterpreter getDialogueInterpreter() { + return dialogueInterpreter; + } + + /** + * @return the hintIconManager + */ + public HintIconManager getHintIconManager() { + return hintIconManager; + } + + /** + * Checks if the player is artifical (AIPlayer). + * @return {@code True} if so. + */ + public boolean isArtificial() { + return artificial; + } + + /** + * @return the questRepository. + */ + public QuestRepository getQuestRepository() { + return questRepository; + } + + /** + * @return the prayer. + */ + public Prayer getPrayer() { + return prayer; + } + + /** + * @return the skullManager. + */ + public SkullManager getSkullManager() { + return skullManager; + } + + /** + * @return the familiarManager. + */ + public FamiliarManager getFamiliarManager() { + return familiarManager; + } + + /** + * Gets the communication. + * @return The communication. + */ + public CommunicationInfo getCommunication() { + return details.getCommunication(); + } + + /** + * Gets the requestManager. + * @return The requestManager. + */ + public RequestManager getRequestManager() { + return requestManager; + } + + /** + * Gets the savedData. + * @return The savedData. + */ + public SavedData getSavedData() { + return savedData; + } + + /** + * Gets the global data. + * @return the global data. + */ + public GlobalData getGlobalData() { + return savedData.getGlobalData(); + } + + /** + * Gets the farmingManager. + * @return The farmingManager. + */ + + + /** + * Gets the warningMessages. + * @return The warningMessages. + */ + public WarningMessages getWarningMessages() { + return warningMessages; + } + + /** + * Gets the musicPlayer. + * @return The musicPlayer. + */ + public MusicPlayer getMusicPlayer() { + return musicPlayer; + } + + /** + * Gets the houseManager. + * @return The houseManager. + */ + public HouseManager getHouseManager() { + return houseManager; + } + + /** + * Gets the bankPinManager. + * @return the bankPinManager + */ + public BankPinManager getBankPinManager() { + return bankPinManager; + } + + /** + * Gets the achievementDiaryManager. + * @return the achievementDiaryManager + */ + public AchievementDiaryManager getAchievementDiaryManager() { + return achievementDiaryManager; + } + + /** + * Gets the ironmanManager. + * @return the ironmanManager + */ + public IronmanManager getIronmanManager() { + return ironmanManager; + } + + /** + * Gets the emoteManager. + * @return the emoteManager. + */ + public EmoteManager getEmoteManager() { + return emoteManager; + } + + /** + * Gets the invisible. + * @return the invisible + */ + public boolean isInvisible() { + return invisible; + } + + /** + * Sets the invisible. + * @param invisible the invisible to set. + */ + public void setInvisible(boolean invisible) { + this.invisible = invisible; + } + + @Override + public String getUsername() { + return StringUtils.formatDisplayName(getName()); + } + + public SkillerTasks getSkillTasks() { + return skillTasks; + } + + public void setSkillTasks(SkillerTasks skillTasks) { + this.skillTasks = skillTasks; + } + + @Override + public String toString() { + return "Player [name=" + name + ", getRights()=" + getRights() + "]"; + } + + public String getCustomState() { + return customState; + } + + public void setCustomState(String state) + { + this.customState = state; + } + + public int getArcheryTargets() { + return archeryTargets; + } + + public void setArcheryTargets(int archeryTargets) { + this.archeryTargets = archeryTargets; + } + + public int getArcheryTotal() { + return archeryTotal; + } + + public void setArcheryTotal(int archeryTotal) { + this.archeryTotal = archeryTotal; + } + + public void updateAppearance() { + getUpdateMasks().register(EntityFlag.Appearance, this); + } + + public void incrementInvalidPacketCount() { + invalidPacketCount++; + if (invalidPacketCount >= 5) { + clear(); + log(this.getClass(), Log.ERR, "Disconnecting " + getName() + " for having a high rate of invalid packets. Potential packet bot misbehaving, or simply really bad connection."); + } + } +} diff --git a/Server/src/main/core/game/node/entity/player/Varbit.kt b/Server/src/main/core/game/node/entity/player/Varbit.kt new file mode 100644 index 000000000..fff714c06 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/Varbit.kt @@ -0,0 +1,5 @@ +package core.game.node.entity.player + +class Varbit(var value: Int, val offset: Int){ + +} \ 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 new file mode 100644 index 000000000..96067b1c7 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/VarpManager.kt @@ -0,0 +1,34 @@ +package core.game.node.entity.player + +import content.global.skill.farming.FarmingPatch +import core.game.event.VarbitUpdateEvent +import core.cache.def.impl.VarbitDefinition +import org.json.simple.JSONArray +import org.json.simple.JSONObject + +/** + * Manages the collection of a player's varps. + * Also handles saving and loading of saved varps. + * @author Ceikry + */ +class VarpManager(val player: Player) { + fun save(root: JSONObject){ + } + + fun parse(data: JSONArray){ + for(varpobj in data){ + val vobj = varpobj as JSONObject + val index = vobj["index"].toString().toInt() + 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() + total = total or (value shl offset) + } + player.varpMap[index] = total + player.saveVarp[index] = true + } + } +} diff --git a/Server/src/main/java/core/game/node/entity/player/info/ClientInfo.java b/Server/src/main/core/game/node/entity/player/info/ClientInfo.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/info/ClientInfo.java rename to Server/src/main/core/game/node/entity/player/info/ClientInfo.java diff --git a/Server/src/main/core/game/node/entity/player/info/PlayerDetails.java b/Server/src/main/core/game/node/entity/player/info/PlayerDetails.java new file mode 100644 index 000000000..7a8775972 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/info/PlayerDetails.java @@ -0,0 +1,273 @@ +package core.game.node.entity.player.info; + +import core.game.system.communication.CommunicationInfo; +import core.net.IoSession; +import org.jetbrains.annotations.NotNull; +import core.auth.UserAccountInfo; +import core.game.world.GameWorld; + +import java.util.concurrent.TimeUnit; + +/** + * Stores the details of a player's account. + * @author Vexia + * + */ +public class PlayerDetails { + public UserAccountInfo accountInfo = UserAccountInfo.createDefault(); + + /** + * The communication info. + */ + private final CommunicationInfo communicationInfo = new CommunicationInfo(); + + public int getCredits() { + return accountInfo.getCredits(); + } + + public void setCredits(int amount) { + accountInfo.setCredits(amount); + } + + + /** + * The unique id info. + */ + private final UIDInfo info = new UIDInfo(); + + /** + * Represents the session. + */ + private IoSession session; + + public boolean saveParsed = false; + + /** + * Constructs a new {@code PlayerDetails}. + * @param username the username to set. + */ + public PlayerDetails(String username) { + accountInfo.setUsername(username); + } + + /** + * Checks if the player is muted. + * @return {@code True} if so. + */ + public boolean isBanned() { + return accountInfo.getBanEndTime() > System.currentTimeMillis(); + } + + /** + * Checks if the mute is permanent. + * @return {@code True} if so. + */ + public boolean isPermMute() { + return TimeUnit.MILLISECONDS.toDays(accountInfo.getMuteEndTime() - System.currentTimeMillis()) > 1000; + } + + /** + * Checks if the player is muted. + * @return {@code True} if so. + */ + public boolean isMuted() { + return accountInfo.getMuteEndTime() > System.currentTimeMillis(); + } + + /** + * Gets the rights. + * @return The rights. + */ + public Rights getRights() { + return Rights.values()[accountInfo.getRights()]; + } + + /** + * Sets the credentials. + * @param rights The credentials to set. + */ + public void setRights(Rights rights) { + this.accountInfo.setRights(rights.ordinal()); + } + + /** + * Gets the session. + * @return The session. + */ + public IoSession getSession() { + return session; + } + + /** + * Sets the session. + * @param session The session to set. + */ + public void setSession(IoSession session) { + this.session = session; + } + + /** + * Sets the password. + * @param password the password. + */ + public void setPassword(final String password) { + this.accountInfo.setPassword(password); + } + + /** + * Gets the username. + * @return The username. + */ + + public String getUsername() { + return this.accountInfo.getUsername(); + } + + /** + * Gets the uid. + * @return the uid. + */ + public int getUid() { + return accountInfo.getUid(); + } + + /** + * Gets the password. + * @return The password. + */ + public String getPassword() { + return this.accountInfo.getPassword(); + } + + /** + * Gets the mac address. + * @return the address. + */ + public String getMacAddress() { + return info.getMac(); + } + + /** + * Gets the computer name. + * @return the name. + */ + public String getCompName() { + return info.getCompName(); + } + + /** + * Gets the ip address. + * @return the ip. + */ + public String getIpAddress() { + if (session == null) { + return info.getIp(); + } + return session.getAddress(); + } + + /** + * Gets the serial. + * @return the serial. + */ + public String getSerial() { + return info.getSerial(); + } + + /** + * Gets the info. + * @return the info + */ + public UIDInfo getInfo() { + return info; + } + + /** + * Gets the communicationInfo. + * @return the communicationInfo + */ + public CommunicationInfo getCommunication() { + return communicationInfo; + } + + /** + * Gets the lastLogin. + * @return the lastLogin. + */ + public long getLastLogin() { + return this.accountInfo.getLastLogin(); + } + + /** + * Sets the lastLogin. + * @param lastLogin the lastLogin to set + */ + public void setLastLogin(long lastLogin) { + this.accountInfo.setLastLogin(lastLogin); + } + + /** + * Gets the timePlayed. + * @return the timePlayed. + */ + public long getTimePlayed() { + return this.accountInfo.getTimePlayed(); + } + + /** + * Sets the timePlayed. + * @param timePlayed the timePlayed to set + */ + public void setTimePlayed(long timePlayed) { + this.accountInfo.setTimePlayed(timePlayed); + } + + /** + * Sets the mute time. + * @param muteTime the mute time. + */ + public void setMuteTime(long muteTime) { + this.accountInfo.setMuteEndTime(muteTime); + } + + /** + * Gets the mute time. + * @return The mute time. + */ + public long getMuteTime() { + return this.accountInfo.getMuteEndTime(); + } + + /** + * Gets the banTime. + * @return the banTime. + */ + public long getBanTime() { + return this.accountInfo.getBanEndTime(); + } + + /** + * Sets the banTime. + * @param banTime the banTime to set + */ + public void setBanTime(long banTime) { + this.accountInfo.setBanEndTime(banTime); + } + + public void save() { + if(!saveParsed) return; + if(isBanned()) return; + try { + accountInfo.setContacts(communicationInfo.getContactString()); + accountInfo.setBlocked(communicationInfo.getBlockedString()); + accountInfo.setClanName(communicationInfo.getClanName()); + accountInfo.setClanReqs(communicationInfo.getClanReqString()); + accountInfo.setCurrentClan(communicationInfo.getCurrentClan()); + GameWorld.getAccountStorage().update(accountInfo); + } catch (IllegalStateException ignored) {} + } + + public static PlayerDetails getDetails(@NotNull String username) { + return new PlayerDetails(username); + } +} \ 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 new file mode 100644 index 000000000..112d51a3c --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/info/PlayerMonitor.kt @@ -0,0 +1,246 @@ +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 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 + +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( + player.name, + player.details.uid, + totalWealth, + diff, + System.currentTimeMillis() + ) + dispatch(event) + if (abs(diff) >= 500_000L) + Discord.postPlayerAlert(player.name, "Dubious change in wealth: $diff, total wealth: $totalWealth") + } + + @JvmStatic fun logChat(player: Player, type: String, message: String) { + val event = LogEvent.ChatLog( + player.name, + player.details.uid, + type, + message, + System.currentTimeMillis() + ) + checkForFlaggedText(player.name, message) + dispatch(event) + } + + @JvmStatic fun logTrade(player1: Player, player2: Player, container1: Container, container2: Container) { + val container1String = StringBuilder() + val container2String = StringBuilder() + + for (item in container1.toArray()) { + item ?: continue + container1String.append(getItemName(item.id) + "(${item.amount}), ") + } + + for (item in container2.toArray()) { + item ?: continue + container2String.append(getItemName(item.id) + "(${item.amount}), ") + } + + val event = LogEvent.TradeLog( + player1.name, + player1.details.uid, + player2.name, + player2.details.uid, + container1String.toString(), + container2String.toString(), + System.currentTimeMillis() + ) + dispatch(event) + } + + @JvmStatic fun logPrivateChat(sender: Player, receiver: String, message: String) { + val event = LogEvent.ChatLog( + sender.name, + sender.details.uid, + "private", + "=> $receiver: $message", + System.currentTimeMillis() + ) + checkForFlaggedText(sender.name, message) + dispatch(event) + } + + @JvmStatic fun log(player: Player, type: LogType, details: String) { + val event = LogEvent.MiscLog( + player.name, + player.details.uid, + type.token, + details, + System.currentTimeMillis() + ) + dispatch(event) + } + + @JvmStatic fun logXpGains(player: Player, xpDiff: List>) { + if (player.isArtificial) return + if (xpDiff.isEmpty()) return + val query = StringBuilder("INSERT INTO xp_gains(player,uid,") + val xpNames = StringBuilder() + val xpAmounts = StringBuilder() + for ((skillId, amount) in xpDiff) { + xpNames.append(Skills.SKILL_NAME[skillId].lowercase() + ",") + xpAmounts.append("$amount,") + } + query.append(xpNames) + query.append("timestamp) VALUES(") + query.append("'" + player.name + "',") + query.append(player.details.uid.toString() + ",") + query.append(xpAmounts) + query.append(System.currentTimeMillis().toString()) + query.append(");") + dispatch(LogEvent.XpLog(query.toString())) + } + + private fun dispatch(event: LogEvent) { + eventQueue.put(event) + if (eventQueue.size >= 50) { + processQueuedEvents() + } + } + + fun init () { + if (!this::db.isInitialized) { + var path = ServerConstants.LOGS_PATH + "playerlogs.db" + db = SQLiteProvider(path, expectedTables) + db.initTables() + } + } + + fun processQueuedEvents() { + init() + if (activeTask?.isActive == true) + return + + activeTask = db.runAsync { + while (eventQueue.isNotEmpty()) + process(eventQueue.take(), it) + } + } + + @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) { + when (event) { + is LogEvent.ChatLog -> { + val stmt = conn.prepareStatement(CHAT_LOG_INSERT) + stmt.setString(1, event.player) + stmt.setInt(2, event.uid) + stmt.setString(3, event.type) + stmt.setString(4, event.message) + stmt.setLong(5, event.timestamp) + stmt.execute() + } + is LogEvent.TradeLog -> { + val stmt = conn.prepareStatement(TRADE_LOG_INSERT) + stmt.setString(1, event.player1) + stmt.setString(2, event.player2) + stmt.setInt(3, event.uid1) + stmt.setInt(4, event.uid2) + stmt.setString(5, event.items1) + stmt.setString(6, event.items2) + stmt.setLong(7, event.timestamp) + stmt.execute() + } + is LogEvent.MiscLog -> { + val stmt = conn.prepareStatement(MISC_LOG_INSERT) + stmt.setString(1, event.player) + stmt.setInt(2, event.uid) + stmt.setString(3, event.type) + stmt.setString(4, event.details) + stmt.setLong(5, event.timestamp) + stmt.execute() + } + is LogEvent.XpLog -> { + val stmt = conn.createStatement() + stmt.execute(event.query) + } + is LogEvent.WealthLog -> { + val stmt = conn.prepareStatement(WEALTH_LOG_INSERT) + stmt.setString(1, event.player) + stmt.setInt(2, event.uid) + stmt.setLong(3, event.total) + stmt.setLong(4, event.diff) + stmt.setLong(5, event.timeStamp) + stmt.execute() + } + } + } + + fun checkForFlaggedText(username: String, message: String) { + if (message.contains("dupe") || message.contains("bug") || message.contains("exploit") || message.contains("glitch")) { + Discord.postPlayerAlert(username, "Flagged Chat - $message") + } + } + + private sealed class LogEvent { + data class ChatLog(val player: String, val uid: Int, val type: String, val message: String, val timestamp: Long) : LogEvent() + data class TradeLog(val player1: String, val uid1: Int, val player2 : String, val uid2: Int, val items1: String, val items2: String, val timestamp: Long) : LogEvent() + data class MiscLog(val player: String, val uid: Int, val type: String, val details: String, val timestamp: Long) : LogEvent() + data class XpLog(val query: String) : LogEvent() + data class WealthLog(val player: String, val uid: Int, val total: Long, val diff: Long, val timeStamp: Long) : LogEvent() + } + + private const val CHAT_LOG_INSERT = "INSERT INTO chat_logs(player,uid,type,message,timestamp) VALUES (?,?,?,?,?);" + private const val TRADE_LOG_INSERT = "INSERT INTO trade_logs(player_a,player_b,uid_a,uid_b,items_a,items_b,timestamp) VALUES (?,?,?,?,?,?,?);" + private const val MISC_LOG_INSERT = "INSERT INTO misc_logs(player,uid,type,details,timestamp) VALUES (?,?,?,?,?);" + private const val WEALTH_LOG_INSERT = "INSERT INTO wealth_logs(player,uid,total,diff,timestamp) VALUES (?,?,?,?,?);" + +} + +enum class LogType(val token: String) { + DUPE_ALERT("dupe_warning"), + DUEL_INFO("Duel"), + PK("PK"), + DROP_TRADE("DropTrade"), + COMMAND("CommandUsed"), + IP_LOG("login_ip") +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/info/RenderInfo.java b/Server/src/main/core/game/node/entity/player/info/RenderInfo.java similarity index 99% rename from Server/src/main/java/core/game/node/entity/player/info/RenderInfo.java rename to Server/src/main/core/game/node/entity/player/info/RenderInfo.java index ff2749ea4..3aa6a7a07 100644 --- a/Server/src/main/java/core/game/node/entity/player/info/RenderInfo.java +++ b/Server/src/main/core/game/node/entity/player/info/RenderInfo.java @@ -1,6 +1,6 @@ package core.game.node.entity.player.info; -import rs09.ServerConstants; +import core.ServerConstants; import core.game.node.entity.Entity; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/player/info/Rights.java b/Server/src/main/core/game/node/entity/player/info/Rights.java similarity index 89% rename from Server/src/main/java/core/game/node/entity/player/info/Rights.java rename to Server/src/main/core/game/node/entity/player/info/Rights.java index 498cb32a5..95009c23a 100644 --- a/Server/src/main/java/core/game/node/entity/player/info/Rights.java +++ b/Server/src/main/core/game/node/entity/player/info/Rights.java @@ -1,7 +1,7 @@ package core.game.node.entity.player.info; import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; +import core.ServerConstants; /** * Represent the rights of a player. @@ -25,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/java/core/game/node/entity/player/info/UIDInfo.java b/Server/src/main/core/game/node/entity/player/info/UIDInfo.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/info/UIDInfo.java rename to Server/src/main/core/game/node/entity/player/info/UIDInfo.java 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 new file mode 100644 index 000000000..fb50ee051 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/info/login/LoginConfiguration.java @@ -0,0 +1,306 @@ +package core.game.node.entity.player.info.login; + +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.net.packet.PacketRepository; +import core.net.packet.context.InterfaceContext; +import core.net.packet.out.Interface; +import core.plugin.Plugin; +import core.ServerConstants; +import core.game.interaction.InteractionListeners; +import content.global.handlers.iface.RulesAndInfo; +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; +import java.util.List; +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. + * + * @author Emperor + */ +public final class LoginConfiguration { + + /** + * The login plugins. + */ + private static final List> LOGIN_PLUGINS = new ArrayList<>(20); + + /** + * The lobby pane component. + */ + 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; + + /** + * Constructs a new {@Code LoginConfiguration} {@Code Object} + */ + public LoginConfiguration() { + /* + * empty. + */ + } + + /** + * Configures the lobby login. + * + * @param player The player. + */ + public static void configureLobby(Player player) { + player.updateSceneGraph(true); + if (!player.isArtificial() && player.getAttribute("tutorial:complete",false) && player.getAttribute("login_type", LoginType.NORMAL_LOGIN) != LoginType.RECONNECT_TYPE) { + sendLobbyScreen(player); + } else { + configureGameWorld(player); + } + } + + /** + * Sends the lobby interface-related packets. + * + * @param player The player. + */ + public static void sendLobbyScreen(Player player) { + messModel = autoSelect(); + for(Player p : Repository.getLobbyPlayers()){ + if(p.getName().equals(player.getName())){ + p.clear(); + Repository.getLobbyPlayers().remove(p); + break; + } + } + Repository.getLobbyPlayers().add(player); + 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, 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()); + } + + /** + * Configures the game world. + * + * @param player The player. + */ + public static void configureGameWorld(final Player player) { + sendGameConfiguration(player); + Repository.getLobbyPlayers().remove(player); + player.setPlaying(true); + UpdateSequence.getRenderablePlayers().add(player); + RegionManager.move(player); + player.getMusicPlayer().init(); + player.updateAppearance(); + player.getPlayerFlags().setUpdateSceneGraph(true); + player.getPacketDispatch().sendInterfaceConfig(226, 1, true); + + if(player.getGlobalData().getTestStage() == 3 && !player.getEmoteManager().isUnlocked(Emotes.SAFETY_FIRST)){ + player.getEmoteManager().unlock(Emotes.SAFETY_FIRST); + } + + for (Item item : player.getEquipment().toArray()) { + //Run equip hooks for all items equipped on login. + //We should have already been doing this. + //Frankly, I don't even want to imagine the number of bugs us *not* doing this has caused. + if (item == null) continue; + 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 (TutorialSession.getExtension(player).getStage() != 73) { + TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), true); + }*/ + } + + /** + * Sends the game configuration packets. + * + * @param player The player to send to. + */ + public static void sendGameConfiguration(final Player player) { + player.getInterfaceManager().openWindowsPane(new Component(player.getInterfaceManager().isResizable() ? 746 : 548)); + player.getInterfaceManager().openChatbox(137); + player.getInterfaceManager().openDefaultTabs(); + welcome(player); + config(player); + for (Plugin plugin : LOGIN_PLUGINS) { + try { + plugin.newInstance(player); + } catch (Throwable e) { + e.printStackTrace(); + } + } + player.getAppearance().sync(); + } + + /** + * Method used to welcome the player. + * + * @param player the player. Fullscreen mode Object id: + */ + public static final void welcome(final Player player) { + if (player.isArtificial()) { + return; + } + 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."); + } + } + + /** + * Method used to configure all possible settings for the player. + * + * @param player the player. + */ + public static final void config(final Player player) { + if(!player.isArtificial()) + log(LoginConfiguration.class, Log.INFO, "configuring player " + player.getUsername()); + player.getInventory().refresh(); + player.getEquipment().refresh(); + player.getSkills().refresh(); + player.getSkills().configure(); + player.getSettings().update(); + player.getInteraction().setDefault(); + player.getPacketDispatch().sendRunEnergy(); + player.getFamiliarManager().login(); + player.getInterfaceManager().openDefaultTabs(); + 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()) + log(LoginConfiguration.class, Log.INFO, "finished configuring player " + player.getUsername()); + } + + /** + * Gets the message child for the inter id. + * @notice GameSettings.kt contains the list of what these are + * @param interfaceId The interface id. + * @return The child id. + */ + public static int getMessageChild(int interfaceId) { + if (interfaceId == 622) { + return 8; + } else if (interfaceId == 16) { + return 6; + } else if (interfaceId == 20 || interfaceId == 623) { + return 5; + } 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; + } else if (interfaceId == 679) { + return 1; + } + return 0; + } + + /** + * Sets a random interface id for the "message of the week" models + */ + private final static int autoSelect() { + boolean contains = IntStream.of(MESSAGE_MODEL).anyMatch(x -> x == GameWorld.getSettings().getMessage_model()); + return contains ? GameWorld.getSettings().getMessage_model():MESSAGE_MODEL[new Random().nextInt(MESSAGE_MODEL.length)]; + } + + /** + * Gets the last login string for the lobby. + * + * @param player the player. + * @return the last login. + */ + public static String getLastLogin(Player player) { + 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(); + long diff = current.getTime() - lastLogin.getTime(); + int days = (int) TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS); + if (days < 1) { + string = string.replace("@timeAgo", "earlier today"); + } else if (days == 1) { + string = string.replace("@timeAgo", "yesterday"); + } else { + string = string.replace("@timeAgo", days + " days ago"); + } + return string; + } + + + /** + * Gets the loginPlugins. + * + * @return The loginPlugins. + */ + public static List> getLoginPlugins() { + return LOGIN_PLUGINS; + } + +} 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 new file mode 100644 index 000000000..649db34a5 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/info/login/LoginParser.kt @@ -0,0 +1,105 @@ +package core.game.node.entity.player.info.login + +import core.ServerConstants +import core.api.* +import core.auth.AuthResponse +import core.game.node.entity.player.Player +import core.game.node.entity.player.info.PlayerDetails +import core.game.system.SystemManager +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.GameWorld.loginListeners +import core.game.world.repository.Repository +import java.util.function.Consumer + +/** + * Parses the login of a player. + */ +class LoginParser(val details: PlayerDetails) { + /** + * Initializes the player. + * @param player The player. + * @param reconnect If the player data should be parsed. + */ + fun initialize(player: Player, reconnect: Boolean) { + if(!validateRequest()) return + lateinit var parser: PlayerSaveParser + try { + parser = PlayerParser.parse(player) + ?: throw IllegalStateException("Failed parsing save for: " + player.username) //Parse core + } + catch (e: Exception) { + e.printStackTrace() + Repository.removePlayer(player) + flag(AuthResponse.ErrorLoadingProfile) + } + GameWorld.Pulser.submit(object : Pulse(1) { + 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) + } + } catch (t: Throwable) { + t.printStackTrace() + Repository.removePlayer(player) + flag(AuthResponse.ErrorLoadingProfile) + } + return true + } + }) + } + + /** + * Initializes a reconnecting player. + * @param player The player. + * @param type The login type. + */ + private fun reconnect(player: Player) { + Repository.disconnectionQueue.remove(details.username) + player.initReconnect() + player.isActive = true + flag(AuthResponse.Success) + player.updateSceneGraph(true) + reinitVarps(player) + LoginConfiguration.configureGameWorld(player) + } + + /** + * Checks if the login request is valid. + * @return `True` if the request is valid. + */ + private fun validateRequest(): Boolean { + if (!details.session.isActive) { + return false + } + if (SystemManager.isUpdating()) { + return flag(AuthResponse.Updating) + } + return if (details.isBanned) { + flag(AuthResponse.AccountDisabled) + } else true + } + + /** + * Flags a response. + * @param response the [Response]. + * @return `True` if successfully logged in. + */ + fun flag(response: AuthResponse): Boolean { + details.session.write(response, true) + return response == AuthResponse.Success + } +} diff --git a/Server/src/main/java/core/game/node/entity/player/info/login/LoginType.java b/Server/src/main/core/game/node/entity/player/info/login/LoginType.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/info/login/LoginType.java rename to Server/src/main/core/game/node/entity/player/info/login/LoginType.java 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 new file mode 100644 index 000000000..6eb56ff4c --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerParser.java @@ -0,0 +1,72 @@ +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 + */ +public final class PlayerParser { + /** + * Parses or creates the player's save file depending on whether or not it exists. + * @param player The player. + */ + public static PlayerSaveParser parse(Player player) { + PlayerSaveParser parser = new PlayerSaveParser(player); + + try { + parser.parse(); + return parser; + } catch (Exception e){ + e.printStackTrace(); + return null; + } + } + /** + * Saves the player's details to the character file at data/players/player_name.json + * @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(); + } + + /** + * Copies the template at data/players/template/template.json to data/players/player_name.json + * @param player the player to copy the template for. + */ + public static void makeFromTemplate(Player player){ + InputStream is = null; + OutputStream os = null; + try { + is = new FileInputStream(ServerConstants.PLAYER_SAVE_PATH + "template/template.json"); + os = new FileOutputStream(ServerConstants.PLAYER_SAVE_PATH + player.getName() + ".json"); + byte[] buffer = new byte[1024]; + int length; + while ((length = is.read(buffer)) > 0) { + os.write(buffer, 0, length); + } + } catch (Exception e){ e.printStackTrace(); + } finally { + try { + assert is != null; + is.close(); + assert os != null; + os.close(); + } catch (Exception f){ + log(PlayerParser.class, Log.ERR, "Unable to close file copiers in PlayerParser.java line 216."); + f.printStackTrace(); + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..74d82e646 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaveParser.kt @@ -0,0 +1,364 @@ +package core.game.node.entity.player.info.login + +import core.JSONUtils +import core.api.PersistPlayer +import core.game.node.entity.combat.spell.CombatSpell +import core.game.node.entity.player.Player +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.Log +import java.io.File +import java.io.FileReader +import java.util.* + +/** + * Class used for parsing JSON player saves. + * @author Ceikry + * @param player: The player we are parsing. + */ +class PlayerSaveParser(val player: Player) { + companion object { + val contentHooks = ArrayList() + } + var parser = JSONParser() + var reader: FileReader? = null + var saveFile: JSONObject? = null + var read = true + + fun parse() { + val JSON = File(ServerConstants.PLAYER_SAVE_PATH + player.name + ".json") + if(JSON.exists()) + { + reader = FileReader(JSON) + } + 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 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() + { + if(read) + contentHooks.forEach{it.parsePlayer(player, saveFile!!)} + player.details.saveParsed = true + } + + fun parseVarps(){ + if(saveFile!!.containsKey("varps")) + player.varpManager.parse(saveFile!!["varps"] as JSONArray) + } + + fun parsePouches() { + if (saveFile!!.containsKey("pouches")) + 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 + for (a in attrData) { + val attr = a as JSONObject + val key = attr["key"].toString() + val type = attr["type"].toString() + val isExpirable = attr.getOrDefault("expirable",false) as Boolean + if(isExpirable){ + val expireTime = attr["expiration-time"].toString().toLong() + if(expireTime < System.currentTimeMillis()) continue + + player.gameAttributes.keyExpirations[key] = expireTime + } + player.gameAttributes.savedAttributes.add(key) + player.gameAttributes.attributes.put(key, when (type) { + "int" -> attr["value"].toString().toInt() + "str" -> attr["value"].toString() + "short" -> attr["value"].toString().toShort() + "long" -> attr["value"].toString().toLong() + "bool" -> attr["value"] as Boolean + "byte" -> Base64.getDecoder().decode(attr["value"].toString())[0] + "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 { + player.gameAttributes.parse(player.name + ".xml") + } + } + + fun parseStatistics() { + if (saveFile!!.containsKey("statistics")) { + val stats: JSONArray = saveFile!!["statistics"] as JSONArray + for (stat in stats) { + val s = stat as JSONObject + val index = (s.get("index") as String).toInt() + val value = (s.get("value") as String).toInt() + } + } + } + + fun parseEmoteManager() { + if (saveFile!!.containsKey("emoteData")) { + val emoteData: JSONArray = saveFile!!["emoteData"] as JSONArray + for (emote in emoteData) { + val e = Emotes.values()[(emote as String).toInt()] + if (!player.emoteManager.emotes.contains(e)) { + player.emoteManager.emotes.add(e) + } + } + } + } + + fun parseIronman() { + if (saveFile!!.containsKey("ironManMode")) { + val ironmanMode = (saveFile!!["ironManMode"] as String).toInt() + player.ironmanManager.mode = IronmanMode.values()[ironmanMode] + } + } + + fun parseAchievements() { + if (saveFile!!.containsKey("achievementDiaries")) { + val achvData = saveFile!!["achievementDiaries"] as JSONArray + player.achievementDiaryManager.parse(achvData) + } else { + player.achievementDiaryManager.resetRewards() + } + } + + fun parseHouse() { + val houseData = saveFile!!["houseData"] as JSONObject + player.houseManager.parse(houseData) + } + + fun parseBankPin() { + val bpData = saveFile!!["bankPinManager"] as JSONObject + player.bankPinManager.parse(bpData) + } + + fun parseFamiliars() { + val familiarData = saveFile!!["familiarManager"] as JSONObject + player.familiarManager.parse(familiarData) + } + + fun parseMusic() { + val unlockedSongs = saveFile!!["unlockedMusic"] as JSONArray + for (song in unlockedSongs) { + val s = (song as String).toInt() + val entry = MusicEntry.forId(s) + player.musicPlayer.unlocked.put(entry.index, entry) + } + } + + fun parseMonitor() { + } + + fun parseConfigs() { + 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 + val value = (c.get("value") as String).toInt() + player.varpMap[index] = value + } + } + + fun parseFarming() { + /*val farmingData = saveFile!!["farming"] as JSONObject + + if (farmingData.containsKey("equipment")) { + val equipmentData: JSONArray? = farmingData.get("equipment") as JSONArray + player.farmingManager.equipment.container.parse(equipmentData) + } + if (farmingData.containsKey("bins")) { + val compostData: JSONArray? = farmingData.get("bins") as JSONArray + player.farmingManager.compostManager.parse(compostData) + } + if (farmingData.containsKey("wrappers")) { + val wrapperData: JSONArray? = farmingData.get("wrappers") as JSONArray + player.farmingManager.parseWrappers(wrapperData) + } + if(farmingData.containsKey("seedlings")){ + val seedlingData = farmingData.get("seedlings") as JSONArray + player.farmingManager.seedlingManager.parse(seedlingData) + } + if(farmingData.containsKey("farmingAmuletWrapperID")){ + player.farmingManager.amuletBoundWrapper = player.farmingManager.getPatchWrapper(farmingData.get("farmingAmuletWrapperID").toString().toInt()) + }*/ + } + + fun parseAutocastSpell() { + val autocastRaw = saveFile!!["autocastSpell"] + autocastRaw ?: return + val autocast = autocastRaw as JSONObject + val book = (autocast.get("book") as String).toInt() + val spellId = (autocast.get("spellId") as String).toInt() + player.properties.autocastSpell = SpellBookManager.SpellBook.values()[book].getSpell(spellId) as CombatSpell + } + + fun parseSavedData() { + val activityData = saveFile!!["activityData"] as JSONObject + val questData = saveFile!!["questData"] as JSONObject + val globalData = saveFile!!["globalData"] as JSONObject + player.savedData.activityData.parse(activityData) + player.savedData.questData.parse(questData) + player.savedData.globalData.parse(globalData) + } + + fun parseSpellbook() { + val spellbookData = (saveFile!!["spellbook"] as String).toInt() + player.spellBookManager.setSpellBook(SpellBookManager.SpellBook.forInterface(spellbookData)) + } + + fun parseGrave() { + saveFile ?: return + val graveData = (saveFile!!["grave_type"] as? String)?.toInt() ?: return + val type = GraveType.values()[graveData] + GraveController.updateGraveType(player, type) + } + + fun parseAppearance() { + saveFile ?: return + val appearanceData = saveFile!!["appearance"] as JSONObject + player.appearance.parse(appearanceData) + } + + fun parseQuests() { + saveFile ?: return + val questData = saveFile!!["quests"] as JSONObject + player.questRepository.parse(questData) + } + + fun parseCore() { + saveFile ?: return + val coreData = saveFile!!["core_data"] as JSONObject + val inventory = coreData["inventory"] as JSONArray + val bank = coreData["bank"] as JSONArray + val bankSecondary = coreData.getOrDefault("bankSecondary", JSONArray()) as JSONArray + val equipment = coreData["equipment"] as JSONArray + 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) { + val tabData = bankTabData as JSONArray + for (i in tabData) { + i ?: continue + val tab = i as JSONObject + val index = tab["index"].toString().toInt() + val startSlot = tab["startSlot"].toString().toInt() + player.bankPrimary.tabStartSlot[index] = startSlot + } + } + val bankTabSecondaryData = coreData["bankTabsSecondary"] + if (bankTabSecondaryData != null) { + val tabData = bankTabSecondaryData as JSONArray + for (i in tabData) { + i ?: continue + val tab = i as JSONObject + val index = tab["index"].toString().toInt() + val startSlot = tab["startSlot"].toString().toInt() + player.bankSecondary.tabStartSlot[index] = startSlot + } + } + player.useSecondaryBank = coreData.getOrDefault("useSecondaryBank", false) as Boolean + player.inventory.parse(inventory) + player.bankPrimary.parse(bank) + player.bankSecondary.parse(bankSecondary) + player.equipment.parse(equipment) + 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() { + saveFile ?: return + val skillData = saveFile!!["skills"] as JSONArray + player.skills.parse(skillData) + player.skills.experienceGained = saveFile!!["totalEXP"].toString().toDouble() + player.skills.experienceMultiplier = saveFile!!["exp_multiplier"].toString().toDouble() + val divisor: Double + if(player.skills.experienceMultiplier >= 10 && !player.attributes.containsKey("permadeath")){ //exclude permadeath HCIMs from XP squish + divisor = player.skills.experienceMultiplier / 5.0 + player.skills.correct(divisor) + } + if (saveFile!!.containsKey("milestone")) { + val milestone: JSONObject = saveFile!!["milestone"] as JSONObject + player.skills.combatMilestone = (milestone.get("combatMilestone")).toString().toInt() + player.skills.skillMilestone = (milestone.get("skillMilestone")).toString().toInt() + } + } + + fun parseSettings() { + saveFile ?: return + val settingsData = saveFile!!["settings"] as JSONObject + player.settings.parse(settingsData) + } + + fun parseVersion() { + saveFile ?: return + player.version = 0 + if (saveFile!!.containsKey("version")) { + player.version = saveFile!!["version"].toString().toInt() + } + } +} diff --git a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt new file mode 100644 index 000000000..351c8dc34 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt @@ -0,0 +1,650 @@ +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 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 java.io.File +import java.io.FileWriter +import java.io.IOException +import javax.script.ScriptEngineManager +import java.util.* + + +/** + * Class used for saving the player's data in JSON format. + * Files are saved in the directory defined in ServerConstants.PLAYER_SAVE_PATH + * @param player: the player to save for. + * @author Ceikry + */ +class PlayerSaver (val player: Player){ + companion object { + val contentHooks = ArrayList() + } + fun populate(): JSONObject { + val saveFile = JSONObject() + saveVersion(saveFile) + saveCoreData(saveFile) + saveSkills(saveFile) + saveSettings(saveFile) + saveQuests(saveFile) + saveAppearance(saveFile) + saveSpellbook(saveFile) + saveSavedData(saveFile) + saveAutocast(saveFile) + savePlayerMonitor(saveFile) + saveMusicPlayer(saveFile) + saveFamiliarManager(saveFile) + saveBankPinData(saveFile) + saveHouseData(saveFile) + saveAchievementData(saveFile) + saveIronManData(saveFile) + saveEmoteData(saveFile) + 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 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() + withContext(Dispatchers.IO) { + File("${ServerConstants.PLAYER_SAVE_PATH}${player.name}.json").createNewFile() + } + } + withContext(Dispatchers.IO) { + FileWriter("${ServerConstants.PLAYER_SAVE_PATH}${player.name}.json").use { file -> + file.write(json) + file.flush() + } + } + } catch (e: IOException) { + e.printStackTrace() + } + } + + 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 attr = JSONObject() + val type = when(value){ + is Int -> "int" + is Boolean -> "bool" + is Long -> "long" + is Short -> "short" + is String -> "str" + is Byte -> "byte" + 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) + if(value is Byte){ + val asString = Base64.getEncoder().encodeToString(byteArrayOf(value)) + attr.put("value",asString) + } else { + attr.put("value", if (value is Boolean) value else value.toString()) + } + if(isExpirable){ + attr.put("expirable",true) + attr.put("expiration-time",player.gameAttributes.keyExpirations[key].toString()) + } + attrs.add(attr) + } + root.put("attributes",attrs) + } + } + + fun saveStatManager(root: JSONObject){ + val statistics = JSONArray() + var index = 0 + root.put("statistics",statistics) + } + + fun saveEmoteData(root: JSONObject){ + if(player.emoteManager.isSaveRequired) { + val emoteData = JSONArray() + player.emoteManager.emotes.map { + emoteData.add(it.ordinal.toString()) + } + root.put("emoteData",emoteData) + } + } + + fun saveIronManData(root: JSONObject){ + if(player.ironmanManager.mode != IronmanMode.NONE){ + root.put("ironManMode",player.ironmanManager.mode.ordinal.toString()) + } + } + + fun saveAchievementData(root: JSONObject){ + val achievementData = JSONArray() + player.achievementDiaryManager.diarys.map { + val diary = JSONObject() + val startedLevels = JSONArray() + it.levelStarted.map { + startedLevels.add(it) + } + diary.put("startedLevels",startedLevels) + val completedLevels = JSONArray() + it.taskCompleted.map { + val level = JSONArray() + it.map { + level.add(it) + } + completedLevels.add(level) + } + diary.put("completedLevels",completedLevels) + val rewardedLevels = JSONArray() + it.levelRewarded.map { + rewardedLevels.add(it) + } + diary.put("rewardedLevels",rewardedLevels) + val diaryCollector = JSONObject() + diaryCollector.put(it.type.name, diary) + achievementData.add(diaryCollector) + } + root.put("achievementDiaries",achievementData) + } + + fun saveHouseData(root: JSONObject){ + val manager = player.houseManager + val houseData = JSONObject() + houseData.put("location",manager.location.ordinal.toString()) + houseData.put("style",manager.style.ordinal.toString()) + if(manager.hasServant()){ + val servant = JSONObject() + servant.put("type",manager.servant.type.ordinal.toString()) + servant.put("uses",manager.servant.uses.toString()) + if(manager.servant.item != null){ + val item = JSONObject() + item.put("id",manager.servant.item.id.toString()) + item.put("amount",manager.servant.item.amount.toString()) + servant.put("item",item) + } + servant.put("greet",manager.servant.isGreet) + houseData.put("servant",servant) + } + val rooms = JSONArray() + var z = 0 + for(room in player.houseManager.rooms){ + var x = 0 + for(xr in room){ + var y = 0 + for(yr in xr){ + if(yr != null) { + val r = JSONObject() + r.put("z", z.toString()) + r.put("x", x.toString()) + r.put("y", y.toString()) + r.put("properties", yr.properties.ordinal.toString()) + r.put("rotation", yr.rotation.toInteger().toString()) + val hotspots = JSONArray() + var hotspotIndex = 0 + yr.hotspots.map { + if (it.decorationIndex > -1) { + val hotspot = JSONObject() + hotspot.put("hotspotIndex", hotspotIndex.toString()) + hotspot.put("decorationIndex", it.decorationIndex.toString()) + hotspots.add(hotspot) + } + hotspotIndex++ + } + r.put("hotspots", hotspots) + rooms.add(r) + } + y++ + } + x++ + } + z++ + } + houseData.put("rooms",rooms) + root.put("houseData",houseData) + } + + fun saveBankPinData(root: JSONObject){ + val bankPinManager = JSONObject() + if(player.bankPinManager.hasPin()){ + bankPinManager.put("pin",player.bankPinManager.pin.toString()) + } + bankPinManager.put("longRecovery",player.bankPinManager.isLongRecovery) + if(player.bankPinManager.status.ordinal != 0){ + bankPinManager.put("status",player.bankPinManager.status.ordinal.toString()) + } + if(player.bankPinManager.pendingDelay != -1L && player.bankPinManager.pendingDelay > System.currentTimeMillis()){ + bankPinManager.put("pendingDelay",player.bankPinManager.pendingDelay.toString()) + } + if(player.bankPinManager.tryDelay > System.currentTimeMillis()){ + bankPinManager.put("tryDelay",player.bankPinManager.tryDelay.toString()) + } + root.put("bankPinManager",bankPinManager) + } + + fun saveFamiliarManager(root: JSONObject){ + val familiarManager = JSONObject() + val petDetails = JSONObject() + player.familiarManager.petDetails.map { + 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).getItemId().toString()) + } else if (player.familiarManager.hasFamiliar()){ + val familiar = JSONObject() + familiar.put("originalId",player.familiarManager.familiar.originalId.toString()) + familiar.put("ticks",player.familiarManager.familiar.ticks.toString()) + familiar.put("specialPoints",player.familiarManager.familiar.specialPoints.toString()) + if(player.familiarManager.familiar.isBurdenBeast && !(player.familiarManager.familiar as BurdenBeast).container.isEmpty){ + val familiarInventory = saveContainer((player.familiarManager.familiar as BurdenBeast).container) + familiar.put("inventory",familiarInventory) + } + familiar.put("lifepoints",player.familiarManager.familiar.skills.lifepoints) + familiarManager.put("familiar",familiar) + + } + root.put("familiarManager",familiarManager) + } + + fun saveMusicPlayer(root: JSONObject){ + val unlockedMusic = JSONArray() + player.musicPlayer.unlocked.values.map { + unlockedMusic.add(it.id.toString()) + } + root.put("unlockedMusic",unlockedMusic) + } + + fun savePlayerMonitor(root: JSONObject){ + + } + + fun saveAutocast(root: JSONObject){ + player.properties.autocastSpell ?: return + val spell = JSONObject() + spell.put("book",player.properties.autocastSpell.book.ordinal.toString()) + spell.put("spellId",player.properties.autocastSpell.spellId.toString()) + root.put("autocastSpell",spell) + } + + fun saveSavedData(root: JSONObject) { + saveActivityData(root) + saveQuestData(root) + saveGlobalData(root) + } + + fun saveGlobalData(root: JSONObject){ + val globalData = JSONObject() + globalData.put("homeTeleportDelay",player.savedData.globalData.homeTeleportDelay.toString()) + globalData.put("lumbridgeRope",player.savedData.globalData.hasTiedLumbridgeRope()) + globalData.put("apprentice",player.savedData.globalData.hasSpokenToApprentice()) + globalData.put("assistTime",player.savedData.globalData.assistTime.toString()) + val assistExperience = JSONArray() + player.savedData.globalData.assistExperience.map { + assistExperience.add(it.toString()) + } + globalData.put("assistExperience",assistExperience) + val strongholdRewards = JSONArray() + player.savedData.globalData.strongHoldRewards.map { + strongholdRewards.add(it) + } + globalData.put("strongHoldRewards",strongholdRewards) + globalData.put("chatPing",player.savedData.globalData.chatPing.toString()) + globalData.put("tutorClaim",player.savedData.globalData.tutorClaim.toString()) + globalData.put("luthasTask",player.savedData.globalData.isLuthasTask) + globalData.put("karamjaBananas",player.savedData.globalData.karamjaBananas.toString()) + globalData.put("silkSteal",player.savedData.globalData.silkSteal.toString()) + globalData.put("zafAmount",player.savedData.globalData.zaffAmount.toString()) + globalData.put("zafTime",player.savedData.globalData.zafTime.toString()) + globalData.put("fritzGlass",player.savedData.globalData.isFritzGlass) + globalData.put("wydinEmployee",player.savedData.globalData.isWydinEmployee) + globalData.put("draynorRecording",player.savedData.globalData.isDraynorRecording) + globalData.put("geTutorial",player.savedData.globalData.isGeTutorial) + globalData.put("essenceTeleporter",player.savedData.globalData.essenceTeleporter.toString()) + globalData.put("recoilDamage",player.savedData.globalData.recoilDamage.toString()) + globalData.put("doubleExpDelay",player.savedData.globalData.doubleExpDelay.toString()) + globalData.put("joinedMonastery",player.savedData.globalData.isJoinedMonastery) + val readPlaques = JSONArray() + player.savedData.globalData.readPlaques.map { + readPlaques.add(it) + } + globalData.put("readPlaques",readPlaques) + globalData.put("forgingUses",player.savedData.globalData.forgingUses.toString()) + globalData.put("ectoCharges",player.savedData.globalData.ectoCharges.toString()) + globalData.put("dropDelay",player.savedData.globalData.dropDelay.toString()) + val abyssData = JSONArray() + player.savedData.globalData.abyssData.map { + abyssData.add(it) + } + globalData.put("abyssData",abyssData) + val rcDecays = JSONArray() + player.savedData.globalData.rcDecays.map { + rcDecays.add(it.toString()) + } + globalData.put("rcDecays",rcDecays) + globalData.put("disableDeathScreen",player.savedData.globalData.isDeathScreenDisabled) + globalData.put("playerTestStage",player.savedData.globalData.playerTestStage.toString()) + globalData.put("charmingDelay",player.savedData.globalData.charmingDelay.toString()) + val travelLogs = JSONArray() + player.savedData.globalData.travelLogs.map { + travelLogs.add(it) + } + globalData.put("travelLogs",travelLogs) + val godBooks = JSONArray() + player.savedData.globalData.godBooks.map { + godBooks.add(it) + } + globalData.put("godBooks",godBooks) + globalData.put("disableNews",player.savedData.globalData.isDisableNews) + val godPages = JSONArray() + player.savedData.globalData.godPages.map { + godPages.add(it) + } + globalData.put("godPages",godPages) + globalData.put("overChargeDelay",player.savedData.globalData.overChargeDelay.toString()) + val bossCounters = JSONArray() + player.savedData.globalData.bossCounters.map { + bossCounters.add(it.toString()) + } + globalData.put("bossCounters",bossCounters) + globalData.put("barrowsLoots",player.savedData.globalData.barrowsLoots.toString()) + globalData.put("lootSharePoints",player.savedData.globalData.lootSharePoints.toString()) + globalData.put("lootShareDelay",player.savedData.globalData.lootShareDelay.toString()) + globalData.put("doubleExp",player.savedData.globalData.doubleExp.toString()) + globalData.put("globalTeleporterDelay",player.savedData.globalData.globalTeleporterDelay.toString()) + globalData.put("starSpriteDelay",player.savedData.globalData.starSpriteDelay.toString()) + globalData.put("runReplenishDelay",player.savedData.globalData.runReplenishDelay.toString()) + globalData.put("runReplenishCharges",player.savedData.globalData.runReplenishCharges.toString()) + globalData.put("lowAlchemyCharges",player.savedData.globalData.lowAlchemyCharges.toString()) + globalData.put("lowAlchemyDelay",player.savedData.globalData.lowAlchemyDelay.toString()) + globalData.put("magicSkillCapeDelay",player.savedData.globalData.magicSkillCapeDelay.toString()) + globalData.put("hunterCapeDelay",player.savedData.globalData.hunterCapeDelay.toString()) + globalData.put("hunterCapeCharges",player.savedData.globalData.hunterCapeCharges.toString()) + globalData.put("taskAmount",player.savedData.globalData.taskAmount.toString()) + globalData.put("taskPoints",player.savedData.globalData.taskPoints.toString()) + globalData.put("macroDisabled",player.savedData.globalData.macroDisabled) + root.put("globalData",globalData) + } + + fun saveQuestData(root: JSONObject){ + val questData = JSONObject() + val draynorLever = JSONArray() + player.savedData.questData.draynorLever.map { + draynorLever.add(it) + } + questData.put("draynorLever",draynorLever) + val dslayer = JSONArray() + player.savedData.questData.dragonSlayer.map { + dslayer.add(it) + } + questData.put("dragonSlayer",dslayer) + questData.put("dragonSlayerPlanks",player.savedData.questData.dragonSlayerPlanks.toString()) + val demonSlayer = JSONArray() + player.savedData.questData.demonSlayer.map { + demonSlayer.add(it) + } + questData.put("demonSlayer",demonSlayer) + val cooksAssistant = JSONArray() + player.savedData.questData.cooksAssistant.map { + cooksAssistant.add(it) + } + questData.put("cooksAssistant",cooksAssistant) + questData.put("gardenerAttack",player.savedData.questData.isGardenerAttack) + questData.put("talkedDrezel",player.savedData.questData.isTalkedDrezel) + val desertTreasureNode = JSONArray() + player.savedData.questData.desertTreasure.map { + val item = JSONObject() + item.put("id",it.id.toString()) + item.put("amount",it.amount.toString()) + desertTreasureNode.add(item) + } + questData.put("desertTreasureNode",desertTreasureNode) + questData.put("witchsExperimentStage",player.savedData.questData.witchsExperimentStage.toString()) + questData.put("witchsExperimentKilled",player.savedData.questData.isWitchsExperimentKilled) + root.put("questData",questData) + } + + fun saveActivityData(root: JSONObject){ + val activityData = JSONObject() + activityData.put("pestPoints",player.savedData.activityData.pestPoints.toString()) + activityData.put("warriorGuildTokens",player.savedData.activityData.warriorGuildTokens.toString()) + activityData.put("bountyHunterRate",player.savedData.activityData.bountyHunterRate.toString()) + activityData.put("bountyRogueRate",player.savedData.activityData.bountyRogueRate.toString()) + activityData.put("barrowKills",player.savedData.activityData.barrowKills.toString()) + val barrowBrothers = JSONArray() + player.savedData.activityData.barrowBrothers.map { + barrowBrothers.add(it) + } + activityData.put("barrowBrothers",barrowBrothers) + activityData.put("barrowTunnelIndex",player.savedData.activityData.barrowTunnelIndex.toString()) + activityData.put("kolodionStage",player.savedData.activityData.kolodionStage.toString()) + val godCasts = JSONArray() + player.savedData.activityData.godCasts.map { + godCasts.add(it.toString()) + } + activityData.put("godCasts",godCasts) + activityData.put("kolodionBoss",player.savedData.activityData.kolodionBoss.toString()) + activityData.put("elnockSupplies",player.savedData.activityData.isElnockSupplies) + activityData.put("lastBorkBattle",player.savedData.activityData.lastBorkBattle.toString()) + activityData.put("startedMta",player.savedData.activityData.isStartedMta) + activityData.put("lostCannon",player.savedData.activityData.isLostCannon) + val pizazzPoints = JSONArray() + player.savedData.activityData.pizazzPoints.map { + pizazzPoints.add(it.toString()) + } + activityData.put("pizazzPoints",pizazzPoints) + activityData.put("bonesToPeaches",player.savedData.activityData.isBonesToPeaches) + activityData.put("solvedMazes",player.savedData.activityData.solvedMazes.toString()) + activityData.put("fogRating",player.savedData.activityData.fogRating.toString()) + activityData.put("borkKills",player.savedData.activityData.borkKills.toString()) + activityData.put("hardcoreDeath",player.savedData.activityData.hardcoreDeath) + activityData.put("topGrabbed",player.savedData.activityData.isTopGrabbed) + root.put("activityData",activityData) + } + + fun saveSpellbook(root: JSONObject){ + root.put("spellbook",player.spellBookManager.spellBook.toString()) + } + + fun saveAppearance(root: JSONObject){ + val appearance = JSONObject() + appearance.put("gender",player.appearance.gender.toByte().toString()) + val appearanceCache = JSONArray() + player.appearance.appearanceCache.map { + val bodyPart = JSONObject() + bodyPart.put("look",it.look.toString()) + bodyPart.put("color",it.color.toString()) + appearanceCache.add(bodyPart) + } + appearance.put("appearance_cache", appearanceCache) + root.put("appearance",appearance) + } + + fun saveQuests(root: JSONObject){ + val quests = JSONObject() + quests.put("points",player.questRepository.points.toString()) + val questStages = JSONArray() + player.questRepository.questList.map { + val quest = JSONObject() + quest.put("questId",it.key.toString()) + quest.put("questStage",it.value.toString()) + questStages.add(quest) + } + quests.put("questStages",questStages) + root.put("quests",quests) + } + + fun saveSettings(root: JSONObject){ + val settings = JSONObject() + settings.put("brightness",player.settings.brightness.toString()) + settings.put("musicVolume",player.settings.musicVolume.toString()) + settings.put("soundEffectVolume",player.settings.soundEffectVolume.toString()) + settings.put("areaSoundVolume",player.settings.areaSoundVolume.toString()) + settings.put("publicChatSetting",player.settings.publicChatSetting.toString()) + settings.put("privateChatSetting",player.settings.privateChatSetting.toString()) + settings.put("clanChatSetting",player.settings.clanChatSetting.toString()) + settings.put("tradeSetting",player.settings.tradeSetting.toString()) + settings.put("assistSetting",player.settings.assistSetting.toString()) + settings.put("runEnergy",player.settings.runEnergy.toString()) + settings.put("specialEnergy",player.settings.specialEnergy.toString()) + settings.put("attackStyle",player.settings.attackStyleIndex.toString()) + settings.put("singleMouse",player.settings.isSingleMouseButton) + settings.put("disableChatEffects",player.settings.isDisableChatEffects) + settings.put("splitPrivate",player.settings.isSplitPrivateChat) + settings.put("acceptAid",player.settings.isAcceptAid) + settings.put("runToggled",player.settings.isRunToggled) + settings.put("retaliation",player.properties.isRetaliating) + root.put("settings",settings) + } + + fun saveSkills(root: JSONObject){ + val skills = JSONArray() + for(i in 0 until 24){ + val skill = JSONObject() + skill.put("id",i.toString()) + skill.put("static",player.skills.staticLevels[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.experienceMultiplier.toString()) + if(player.skills.combatMilestone > 0 || player.skills.skillMilestone > 0){ + val milestone = JSONObject() + milestone.put("combatMilestone",player.skills.combatMilestone.toString()) + milestone.put("skillMilestone",player.skills.skillMilestone.toString()) + root.put("milestone",milestone) + } + } + + fun saveContainer(container: Container): JSONArray { + val json = JSONArray() + container.toArray().map{ + if (it != null) { + val item = JSONObject() + item.put("slot", it.slot.toString()) + item.put("id", it.id.toString()) + item.put("amount", it.amount.toString()) + item.put("charge", it.charge.toString()) + json.add(item) + } + } + return json + } + + fun saveCoreData(root: JSONObject){ + val coreData = JSONObject() + val inventory = saveContainer(player.inventory) + coreData.put("inventory",inventory) + + val bank = saveContainer(player.bankPrimary) + coreData.put("bank",bank) + + val bankSecondary = saveContainer(player.bankSecondary) + coreData.put("bankSecondary",bankSecondary) + + val bankTabs = JSONArray() + for(i in player.bankPrimary.tabStartSlot.indices){ + val tab = JSONObject() + tab.put("index",i.toString()) + tab.put("startSlot",player.bankPrimary.tabStartSlot[i].toString()) + bankTabs.add(tab) + } + coreData.put("bankTabs",bankTabs) + + val bankTabsSecondary = JSONArray() + for(i in player.bankSecondary.tabStartSlot.indices){ + val tab = JSONObject() + tab.put("index",i.toString()) + tab.put("startSlot",player.bankSecondary.tabStartSlot[i].toString()) + bankTabsSecondary.add(tab) + } + coreData.put("bankTabsSecondary",bankTabsSecondary) + + coreData.put("useSecondaryBank",player.useSecondaryBank) + + val equipment = saveContainer(player.equipment) + coreData.put("equipment",equipment) + + val loctemp = player.location + 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/java/core/game/node/entity/player/info/login/Response.java b/Server/src/main/core/game/node/entity/player/info/login/Response.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/info/login/Response.java rename to Server/src/main/core/game/node/entity/player/info/login/Response.java 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/java/core/game/node/entity/player/info/portal/Icon.java b/Server/src/main/core/game/node/entity/player/info/portal/Icon.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/info/portal/Icon.java rename to Server/src/main/core/game/node/entity/player/info/portal/Icon.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/ActivityData.java b/Server/src/main/core/game/node/entity/player/link/ActivityData.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/ActivityData.java rename to Server/src/main/core/game/node/entity/player/link/ActivityData.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/BankPinManager.java b/Server/src/main/core/game/node/entity/player/link/BankPinManager.java similarity index 92% rename from Server/src/main/java/core/game/node/entity/player/link/BankPinManager.java rename to Server/src/main/core/game/node/entity/player/link/BankPinManager.java index 8457e8f5f..321b5f8bf 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/BankPinManager.java +++ b/Server/src/main/core/game/node/entity/player/link/BankPinManager.java @@ -1,11 +1,12 @@ package core.game.node.entity.player.link; -import core.cache.misc.buffer.ByteBufferUtils; import core.game.component.CloseEvent; import core.game.component.Component; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.ge.GrandExchangeRecords; +import content.global.handlers.iface.ge.StockMarket; +import core.game.world.GameWorld; import core.net.packet.PacketRepository; import core.net.packet.context.ChildPositionContext; import core.net.packet.context.StringContext; @@ -13,14 +14,17 @@ 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.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; 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)); } @@ -203,9 +207,9 @@ public class BankPinManager { player.getBank().open(); } } else if (buttonId == 3) { - player.getPlayerGrandExchange().openCollectionBox(); + GrandExchangeRecords.getInstance(player).openCollectionBox(); } else if (buttonId == 4) { - player.getPlayerGrandExchange().open(); + StockMarket.openFor(player); } } @@ -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/java/core/game/node/entity/player/link/GlobalData.java b/Server/src/main/core/game/node/entity/player/link/GlobalData.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/player/link/GlobalData.java rename to Server/src/main/core/game/node/entity/player/link/GlobalData.java index f851a51e0..62873b245 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/GlobalData.java +++ b/Server/src/main/core/game/node/entity/player/link/GlobalData.java @@ -1,23 +1,15 @@ package core.game.node.entity.player.link; -import core.game.content.global.GodBook; +import content.data.GodBook; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import java.nio.ByteBuffer; - /** * Represents the quest data to save. * @author 'Vexia */ public final class GlobalData { - - /** - * Represents the tutorial stage. - */ - private int tutorialStage; - /** * Represents the home teleport delay. */ @@ -152,11 +144,6 @@ public final class GlobalData { */ private int ectoCharges; - /** - * The bracelet of clay uses. - */ - private int braceletClayUses; - /** * The abbyss data. */ @@ -236,12 +223,7 @@ public final class GlobalData { * The double exp time. */ private long doubleExp; - - /** - * If Ava's device should randomly collect loot. - */ - private boolean avasDevice; - + /** * The delay of the global teleporter system. */ @@ -310,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"); @@ -344,7 +325,6 @@ public final class GlobalData { } forgingUses = Integer.parseInt( data.get("forgingUses").toString()); ectoCharges = Integer.parseInt( data.get("ectoCharges").toString()); - braceletClayUses = Integer.parseInt( data.get("braceletClayUses").toString()); dropDelay = Long.parseLong(data.get("dropDelay").toString()); JSONArray ad = (JSONArray) data.get("abyssData"); for(int i = 0; i < ad.size(); i++){ @@ -379,7 +359,6 @@ public final class GlobalData { lootShareDelay = Long.parseLong(data.get("lootShareDelay").toString()); lootSharePoints = Integer.parseInt( data.get("lootSharePoints").toString()); doubleExp = Long.parseLong(data.get("doubleExp").toString()); - avasDevice = (boolean) data.get("avasDevice"); globalTeleporterDelay = Long.parseLong(data.get("globalTeleporterDelay").toString()); starSpriteDelay = Long.parseLong(data.get("starSpriteDelay").toString()); runReplenishDelay = Long.parseLong(data.get("runReplenishDelay").toString()); @@ -481,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. @@ -835,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; @@ -904,21 +867,6 @@ public final class GlobalData { this.ectoCharges = ectoCharges; } - /** - * Gets the braceletClayUses. - * @return The braceletClayUses. - */ - public int getBraceletClayUses() { - return braceletClayUses; - } - - /** - * Increments the bracelet of clay uses. - */ - public void incrementBraceletOfClay() { - this.braceletClayUses += 1; - } - /** * Resets the abyss data. */ @@ -945,14 +893,6 @@ public final class GlobalData { return abyssData[ordinal]; } - /** - * Sets the braceletClayUses. - * @param braceletClayUses The braceletClayUses to set. - */ - public void setBraceletClayUses(int braceletClayUses) { - this.braceletClayUses = braceletClayUses; - } - /** * Gets the dropDelay. * @return The dropDelay. @@ -1172,22 +1112,6 @@ public final class GlobalData { public boolean hasDoubleExp() { return doubleExp > System.currentTimeMillis(); } - - /** - * Gets the avasDevice - * @return the avasDevice - */ - public boolean isAvasDisabled() { - return avasDevice; - } - - /** - * Sets the avasDevice - * @param avasDevice - */ - public void setAvasDisabled(boolean avasDevice) { - this.avasDevice = avasDevice; - } /** * Gets the globalTeleporterDelay. diff --git a/Server/src/main/java/core/game/node/entity/player/link/HintIconManager.java b/Server/src/main/core/game/node/entity/player/link/HintIconManager.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/HintIconManager.java rename to Server/src/main/core/game/node/entity/player/link/HintIconManager.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/InterfaceManager.java b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/player/link/InterfaceManager.java rename to Server/src/main/core/game/node/entity/player/link/InterfaceManager.java index d56b34537..4a0fb00ae 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/InterfaceManager.java +++ b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java @@ -1,8 +1,9 @@ package core.game.node.entity.player.link; +import core.game.event.InterfaceCloseEvent; +import core.game.event.InterfaceOpenEvent; import core.game.component.Component; import core.game.component.InterfaceType; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.net.packet.PacketRepository; @@ -11,9 +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 rs09.game.system.SystemLogger; +import content.region.misc.tutisland.handlers.TutorialStage; +import static core.api.ContentAPIKt.log; +import static core.api.ContentAPIKt.*; /** @@ -122,22 +126,31 @@ public final class InterfaceManager { public Component openWindowsPane(Component windowsPane, boolean overlap) { this.windowsPane = windowsPane; if (windowsPane.getDefinition().getType() != InterfaceType.WINDOW_PANE) { - SystemLogger.logErr("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); } + PacketRepository.send(WindowsPane.class, new WindowsPaneContext(player, windowsPane.getId(), overlap ? 1 : 0)); windowsPane.open(player); + + if (opened != null) + player.dispatch(new InterfaceOpenEvent(opened)); + return windowsPane; } public void openWindowsPane(Component windowsPane, int type) { this.windowsPane = windowsPane; if (windowsPane.getDefinition().getType() != InterfaceType.WINDOW_PANE) { - SystemLogger.logErr("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); } + PacketRepository.send(WindowsPane.class, new WindowsPaneContext(player, windowsPane.getId(), type)); windowsPane.open(player); + + if (opened != null) + player.dispatch(new InterfaceOpenEvent(opened)); } /** @@ -161,7 +174,11 @@ public final class InterfaceManager { return null; } component.open(player); - return opened = component; + + opened = component; + player.dispatch(new InterfaceOpenEvent(opened)); + + return opened; } /** @@ -188,9 +205,14 @@ 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())); + player.dispatch(new InterfaceCloseEvent(opened)); } opened = null; } @@ -252,7 +274,7 @@ public final class InterfaceManager { */ public Component openSingleTab(Component component) { if (component.getDefinition().getType() != InterfaceType.SINGLE_TAB) { - SystemLogger.logErr("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); @@ -282,54 +304,11 @@ public final class InterfaceManager { public Component getSingleTab() { return singleTab; } - - /** - * Hides the tabs. - * @param tabs The tab indexes. - */ - public void hideTabs(int... tabs) { - if (true) { - removeTabs(tabs); - return; - } - @SuppressWarnings("unused") - boolean changeViewedTab = false; - for (int slot : tabs) { - if (slot == currentTabIndex) { - changeViewedTab = true; - } - Component tab = this.tabs[slot]; - if (tab != null && !tab.isHidden()) { - int child = (slot < 7 ? 38 : 13) + slot; - boolean resize = isResizable(); - player.getPacketDispatch().sendInterfaceConfig(getWindowPaneId(), child, true); - player.getPacketDispatch().sendInterfaceConfig(getWindowPaneId(), child + 7, true); - tab.setHidden(true); - } - } - if (changeViewedTab) { - int currentIndex = -1; - if (this.tabs[3] == null) { - for (int i = 0; i < this.tabs.length; i++) { - if (this.tabs[i] != null) { - currentIndex = i; - break; - } - } - } else { - currentIndex = 3; - } - if (currentIndex > -1) { - setViewedTab(currentIndex); - } - } - } /** * Removes the tabs. * @param tabs The tab indexes. */ - @Deprecated public void removeTabs(int... tabs) { boolean changeViewedTab = false; for (int slot : tabs) { @@ -480,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("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("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); @@ -497,7 +476,7 @@ public final class InterfaceManager { */ public void openTab(Component component) { if (component.getDefinition().getTabIndex() < 0) { - SystemLogger.logErr("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); @@ -523,18 +502,15 @@ 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("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); } - if (TutorialSession.getExtension(player).getStage() < TutorialSession.MAX_STAGE) { - Component.setUnclosable(player, getChatbox()); - } } /** @@ -545,7 +521,13 @@ public final class InterfaceManager { if (windowMode != player.getSession().getClientInfo().getWindowMode()) { player.getSession().getClientInfo().setWindowMode(windowMode); openWindowsPane(new Component(isResizable() ? Components.TOPLEVEL_FULLSCREEN_746 : Components.TOPLEVEL_548)); - openDefaultTabs(); + if(!player.getAttribute("tutorial:complete", false)) { + TutorialStage.hideTabs(player, false); + } + else + { + openDefaultTabs(); + } openInfoBars(); PacketRepository.send(Interface.class, new InterfaceContext(player, getWindowPaneId(), isResizable() ? 23 : 14, Components.FILTERBUTTONS_751, true)); PacketRepository.send(Interface.class, new InterfaceContext(player, getWindowPaneId(), isResizable() ? 70 : 75, Components.CHATTOP_752, true)); @@ -591,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) { @@ -629,31 +611,14 @@ public final class InterfaceManager { return; } overlay = component; - if (overlay.getDefinition().getType() != InterfaceType.OVERLAY) { - SystemLogger.logErr("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("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. */ @@ -807,4 +772,4 @@ public final class InterfaceManager { packetCount += increment; return count; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/node/entity/player/link/IronmanManager.java b/Server/src/main/core/game/node/entity/player/link/IronmanManager.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/IronmanManager.java rename to Server/src/main/core/game/node/entity/player/link/IronmanManager.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/IronmanMode.java b/Server/src/main/core/game/node/entity/player/link/IronmanMode.java similarity index 88% rename from Server/src/main/java/core/game/node/entity/player/link/IronmanMode.java rename to Server/src/main/core/game/node/entity/player/link/IronmanMode.java index 2573b3fe4..3aa5e2a7a 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/player/link/PacketDispatch.java b/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/player/link/PacketDispatch.java rename to Server/src/main/core/game/node/entity/player/link/PacketDispatch.java index ec8e26a23..de6bb8233 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/PacketDispatch.java +++ b/Server/src/main/core/game/node/entity/player/link/PacketDispatch.java @@ -1,24 +1,25 @@ package core.game.node.entity.player.link; -import rs09.game.Varp; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; -import rs09.game.system.SystemLogger; +import core.tools.Log; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; 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 @@ -45,23 +46,27 @@ 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) { + PacketRepository.send(VarcUpdate.class, new VarcUpdateContext(player, index, value)); + } + /** * Send a game message. * @param message The game message. */ public void sendMessage(String message) { - if (message == null || TutorialSession.getExtension(player).getStage() < TutorialSession.MAX_STAGE) { + if (message == null) { return; } if (player.getAttribute("chat_filter") != null && !message.contains("") && !message.contains("")) { return; } if (message.length() > 255) { - SystemLogger.logErr("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)); @@ -94,14 +99,14 @@ public final class PacketDispatch { /** * Send a access mask. - * @param id The access mask id. + * @param settingsHash The access mask settingsHash. * @param childId The access mask child id. * @param interfaceId The access mask interface Id. * @param offset The access mask off set. * @param length The access mask length. */ - public void sendAccessMask(int id, int childId, int interfaceId, int offset, int length) { - PacketRepository.send(AccessMask.class, new AccessMaskContext(player, id, childId, interfaceId, offset, length)); + public void sendIfaceSettings(int settingsHash, int childId, int interfaceId, int offset, int length) { + PacketRepository.send(AccessMask.class, new AccessMaskContext(player, settingsHash, childId, interfaceId, offset, length)); } /** @@ -196,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); } /** @@ -213,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. @@ -294,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)); } /** @@ -303,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)); } /** @@ -311,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)); } /** @@ -377,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)); } @@ -411,4 +429,12 @@ public final class PacketDispatch { public void sendScriptConfigs(int id, int value, String type, Object... params) { PacketRepository.send(CSConfigPacket.class, new CSConfigContext(player, id, value, type, params)); } + + 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/java/core/game/node/entity/player/link/QuestData.java b/Server/src/main/core/game/node/entity/player/link/QuestData.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/player/link/QuestData.java rename to Server/src/main/core/game/node/entity/player/link/QuestData.java index aa58c395f..110c89e01 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/player/link/RunScript.java b/Server/src/main/core/game/node/entity/player/link/RunScript.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/RunScript.java rename to Server/src/main/core/game/node/entity/player/link/RunScript.java 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 new file mode 100644 index 000000000..a0e005592 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/SavedData.java @@ -0,0 +1,80 @@ +package core.game.node.entity.player.link; + +import core.game.node.entity.player.Player; + +/** + * Represents a managing class of saved data related to ingame interactions, + * such as questing data, npc talking data, etc. + * @author 'Vexia + */ +public class SavedData { + + /** + * Represents the global data to save. + */ + private final GlobalData globalData = new GlobalData(); + + /** + * Represents the activity data to save. + */ + private final ActivityData activityData = new ActivityData(); + + /** + * Represents the quest data to save. + */ + private final QuestData questData = new QuestData(); + + /** + * The player. + */ + private final Player player; + + /** + * Constructs a new {@Code SavedData} {@Code Object} + * @param player the player. + */ + public SavedData(Player player) { + this.player = player; + } + + /** + * Gets the boolean value. + * @param value the value. + * @return the value. + */ + public static boolean getBoolean(byte value) { + return value == 1; + } + + /** + * Gets the activityData. + * @return The activityData. + */ + public ActivityData getActivityData() { + return activityData; + } + + /** + * Gets the questData. + * @return The questData. + */ + public QuestData getQuestData() { + return questData; + } + + /** + * Gets the globalData. + * @return The globalData. + */ + public GlobalData getGlobalData() { + return globalData; + } + + /** + * Gets the player. + * @return the player + */ + public Player getPlayer() { + return player; + } +} 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 new file mode 100644 index 000000000..920a65629 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/Settings.java @@ -0,0 +1,612 @@ +package core.game.node.entity.player.link; + +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.net.packet.IoBuffer; + +import static core.api.ContentAPIKt.*; + + +/** + * Holds a player's settings. + * @author Emperor + */ +public final class Settings { + + /** + * The player. + */ + private final Player player; + + /** + * The run energy. + */ + private double runEnergy = 100.0; + + /** + * The player's weight. + */ + private double weight; + + /** + * The brightness setting. + */ + private int brightness = 2; + + /** + * The music volume. + */ + private int musicVolume; + + /** + * The sound effects volume. + */ + private int soundEffectVolume; + + /** + * The area sounds volume. + */ + private int areaSoundVolume; + + /** + * If the player has the single mouse button setting enabled. + */ + private boolean singleMouseButton; + + /** + * If the chat effects should be disabled. + */ + private boolean disableChatEffects; + + /** + * If the private chat should be split from public chat. + */ + private boolean splitPrivateChat; + + /** + * If the player has the accept aid setting enabled. + */ + private boolean acceptAid; + + /** + * If the player's run button is toggled. + */ + private boolean runToggled; + + /** + * The public chat setting. + */ + private int publicChatSetting = 0; + + /** + * The private chat setting. + */ + private int privateChatSetting = 0; + + /** + * The clan chat setting. + */ + private int clanChatSetting = 0; + + /** + * The trade setting. + */ + private int tradeSetting = 0; + + /** + * The assist setting. + */ + private int assistSetting = 0; + + /** + * If the special attack is toggled. + */ + private boolean specialToggled; + + /** + * The current special energy the player has left. + */ + private int specialEnergy = 100; + + /** + * The current attack style index. + */ + private int attackStyleIndex = 0; + + /** + * Constructs a new {@code Settings} {@code Object}. + * @param player The player. + */ + public Settings(Player player) { + this.player = player; + } + + /** + * Updates the settings. + */ + public void update() { + 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(); + } + + /** + * Toggles the attack style index. + * @param index The index. + */ + public void toggleAttackStyleIndex(int index) { + this.attackStyleIndex = index; + setVarp(player, 43, attackStyleIndex); + } + + /** + * Updates the chat settings. + */ + public void updateChatSettings() { + player.getSession().write(new IoBuffer(232).put(publicChatSetting).put(privateChatSetting).put(tradeSetting)); + } + + /** + * Sets the chat settings. + * @param pub The public chat setting. + * @param priv The private chat setting. + * @param trade The trade setting. + */ + public void updateChatSettings(int pub, int priv, int trade) { + boolean update = false; + if (publicChatSetting != pub) { + publicChatSetting = pub; + update = true; + } + if (tradeSetting != trade) { + tradeSetting = trade; + update = true; + } + if (update) { + updateChatSettings(); + } + } + + /** + * Sets the chat settings. + * @param pub The public chat setting. + * @param priv The private chat setting. + * @param trade The trade setting. + */ + public void setChatSettings(int pub, int priv, int trade) { + publicChatSetting = pub; + privateChatSetting = priv; + tradeSetting = trade; + } + + /** + * Parses the settings from the save file. + * @param settingsData The JSON object. + */ + public void parse(JSONObject settingsData){ + brightness = Integer.parseInt( settingsData.get("brightness").toString()); + musicVolume = Integer.parseInt( settingsData.get("musicVolume").toString()); + soundEffectVolume = Integer.parseInt( settingsData.get("soundEffectVolume").toString()); + areaSoundVolume = Integer.parseInt( settingsData.get("areaSoundVolume").toString()); + singleMouseButton = (boolean) settingsData.get("singleMouse"); + disableChatEffects = (boolean) settingsData.get("disableChatEffects"); + splitPrivateChat = (boolean) settingsData.get("splitPrivate"); + acceptAid = (boolean) settingsData.get("acceptAid"); + runToggled = (boolean) settingsData.get("runToggled"); + publicChatSetting = Integer.parseInt( settingsData.get("publicChatSetting").toString()); + privateChatSetting = Integer.parseInt( settingsData.get("privateChatSetting").toString()); + clanChatSetting = Integer.parseInt( settingsData.get("clanChatSetting").toString()); + tradeSetting = Integer.parseInt( settingsData.get("tradeSetting").toString()); + assistSetting = Integer.parseInt( settingsData.get("assistSetting").toString()); + runEnergy = Double.parseDouble( settingsData.get("runEnergy").toString()); + specialEnergy = Integer.parseInt( settingsData.get("specialEnergy").toString()); + attackStyleIndex = Integer.parseInt( settingsData.get("attackStyle").toString()); + player.getProperties().setRetaliating((boolean) settingsData.get("retaliation")); + } + + /** + * Toggles the special attack bar. + */ + public void toggleSpecialBar() { + setSpecialToggled(!specialToggled); + } + + /** + * Toggles the special attack bar. + * @param enable If the special attack should be enabled. + */ + public void setSpecialToggled(boolean enable) { + specialToggled = !specialToggled; + setVarp(player, 301, specialToggled ? 1 : 0); + } + + /** + * Checks if the special attack bar is toggled. + * @return {@code True} if so. + */ + public boolean isSpecialToggled() { + return specialToggled; + } + + /** + * Drains an amount of special attack energy. + * @param amount The amount to drain. + * @return {@code True} if succesful, {@code false} if the special attack + * energy amount hasn't changed after calling this method. + */ + public boolean drainSpecial(int amount) { + if (!specialToggled) { + return false; + } + setSpecialToggled(false); + if (amount > specialEnergy) { + player.getPacketDispatch().sendMessage("You do not have enough special attack energy left."); + return false; + } + setSpecialEnergy(specialEnergy - amount); + return true; + } + + /** + * Sets the special energy amount. + * @param value The amount to set. + */ + public void setSpecialEnergy(int value) { + specialEnergy = value; + setVarp(player, 300, specialEnergy * 10); + } + + /** + * Gets the amount of special energy left. + * @return The amount of energy. + */ + public int getSpecialEnergy() { + return specialEnergy; + } + + /** + * Toggles the retaliating button. + */ + public void toggleRetaliating() { + player.getProperties().setRetaliating(!player.getProperties().isRetaliating()); + setVarp(player, 172, !player.getProperties().isRetaliating() ? 1 : 0); + } + + /** + * Toggles the singleMouseButton. + */ + public void toggleMouseButton() { + singleMouseButton = !singleMouseButton; + setVarp(player, 170, singleMouseButton ? 1 : 0); + } + + /** + * Toggles the disableChatEffects. + */ + public void toggleChatEffects() { + disableChatEffects = !disableChatEffects; + setVarp(player, 171, disableChatEffects ? 1 : 0); + } + + /** + * Toggles the splitPrivateChat. + */ + public void toggleSplitPrivateChat() { + splitPrivateChat = !splitPrivateChat; + setVarp(player, 287, splitPrivateChat ? 1 : 0); + } + + /** + * Toggles the acceptAid. + */ + public void toggleAcceptAid() { + acceptAid = !acceptAid; + setVarp(player, 427, acceptAid ? 1 : 0); + } + + /** + * Toggles the run button. + */ + public void toggleRun() { + setRunToggled(!runToggled); + } + + /** + * Toggles the run button. + * If the run button should be enabled. + */ + public void setRunToggled(boolean enabled) { + runToggled = enabled; + setVarp(player, 173, runToggled ? 1 : 0); + } + + /** + * Decreases the run energy with the given amount (drain parameter).
To + * increase, use a negative drain value. + * @param drain The drain amount. + */ + public void updateRunEnergy(double drain) { + runEnergy -= drain; + if (runEnergy < 0) { + runEnergy = 0.0; + } else if (runEnergy > 100) { + runEnergy = 100.0; + } + player.getPacketDispatch().sendRunEnergy(); + } + + /** + * Updates the weight. + */ + public void updateWeight() { + weight = 0.0; + for (int i = 0; i < 28; i++) { + Item item = player.getInventory().get(i); + if (item == null) { + continue; + } + double value = item.getDefinition().getConfiguration(ItemConfigParser.WEIGHT, 0.0); + if (value > 0) { + weight += value; + } + } + for (int i = 0; i < 11; i++) { + Item item = player.getEquipment().get(i); + if (item == null) { + continue; + } + weight += item.getDefinition().getConfiguration(ItemConfigParser.WEIGHT, 0.0); + } + player.getPacketDispatch().sendString((int) weight + " kg", 667, 32); + } + + /** + * Gets the weight. + * @return The weight. + */ + public double getWeight() { + return weight; + } + + /** + * Gets the brightness. + * @return The brightness. + */ + public int getBrightness() { + return brightness; + } + + /** + * Sets the brightness. + * @param brightness The brightness to set. + */ + public void setBrightness(int brightness) { + this.brightness = brightness; + setVarp(player, 166, brightness + 1); + } + + /** + * Gets the musicVolume. + * @return The musicVolume. + */ + public int getMusicVolume() { + return musicVolume; + } + + /** + * Sets the musicVolume. + * @param musicVolume The musicVolume to set. + */ + public void setMusicVolume(int musicVolume) { + this.musicVolume = musicVolume; + setVarp(player, 168, musicVolume); + } + + /** + * Gets the soundEffectVolume. + * @return The soundEffectVolume. + */ + public int getSoundEffectVolume() { + return soundEffectVolume; + } + + /** + * Sets the soundEffectVolume. + * @param soundEffectVolume The soundEffectVolume to set. + */ + public void setSoundEffectVolume(int soundEffectVolume) { + this.soundEffectVolume = soundEffectVolume; + setVarp(player, 169, soundEffectVolume); + } + + /** + * Gets the areaSoundVolume. + * @return The areaSoundVolume. + */ + public int getAreaSoundVolume() { + return areaSoundVolume; + } + + /** + * Sets the areaSoundVolume. + * @param areaSoundVolume The areaSoundVolume to set. + */ + public void setAreaSoundVolume(int areaSoundVolume) { + this.areaSoundVolume = areaSoundVolume; + setVarp(player, 872, areaSoundVolume); + } + + /** + * Gets the singleMouseButton. + * @return The singleMouseButton. + */ + public boolean isSingleMouseButton() { + return singleMouseButton; + } + + /** + * Gets the disableChatEffects. + * @return The disableChatEffects. + */ + public boolean isDisableChatEffects() { + return disableChatEffects; + } + + /** + * Gets the splitPrivateChat. + * @return The splitPrivateChat. + */ + public boolean isSplitPrivateChat() { + return splitPrivateChat; + } + + /** + * Gets the acceptAid. + * @return The acceptAid. + */ + public boolean isAcceptAid() { + if (player.getIronmanManager().isIronman()) { + return false; + } + return acceptAid; + } + + /** + * Gets the runToggled. + * @return The runToggled. + */ + public boolean isRunToggled() { + return runToggled; + } + + /** + * Gets the publicChatSetting. + * @return The publicChatSetting. + */ + public int getPublicChatSetting() { + return publicChatSetting; + } + + /** + * Sets the publicChatSetting. + * @param publicChatSetting The publicChatSetting to set. + */ + public void setPublicChatSetting(int publicChatSetting) { + this.publicChatSetting = publicChatSetting; + updateChatSettings(); + } + + /** + * Gets the privateChatSetting. + * @return The privateChatSetting. + */ + public int getPrivateChatSetting() { + return privateChatSetting; + } + + /** + * Sets the privateChatSetting. + * @param privateChatSetting The privateChatSetting to set. + */ + public void setPrivateChatSetting(int privateChatSetting) { + this.privateChatSetting = privateChatSetting; + updateChatSettings(); + } + + /** + * Gets the clanChatSetting. + * @return The clanChatSetting. + */ + public int getClanChatSetting() { + return clanChatSetting; + } + + /** + * Sets the clanChatSetting. + * @param clanChatSetting The clanChatSetting to set. + */ + public void setClanChatSetting(int clanChatSetting) { + this.clanChatSetting = clanChatSetting; + setVarp(player, 1054, clanChatSetting); + } + + /** + * Gets the tradeSetting. + * @return The tradeSetting. + */ + public int getTradeSetting() { + return tradeSetting; + } + + /** + * Sets the tradeSetting. + * @param tradeSetting The tradeSetting to set. + */ + public void setTradeSetting(int tradeSetting) { + this.tradeSetting = tradeSetting; + updateChatSettings(); + } + + /** + * Gets the assistSetting. + * @return The assistSetting. + */ + public int getAssistSetting() { + return assistSetting; + } + + /** + * Sets the assistSetting. + * @param assistSetting The assistSetting to set. + */ + public void setAssistSetting(int assistSetting) { + this.assistSetting = assistSetting; + setVarp(player, 1055, assistSetting); + } + + /** + * @return the runEnergy + */ + public double getRunEnergy() { + return runEnergy; + } + + /** + * @param runEnergy the runEnergy to set + */ + public void setRunEnergy(double runEnergy) { + this.runEnergy = runEnergy; + } + + /** + * Gets the attackStyleIndex. + * @return The attackStyleIndex. + */ + public int getAttackStyleIndex() { + return attackStyleIndex; + } + + /** + * Sets the attackStyleIndex. + * @param attackStyleIndex The attackStyleIndex to set. + */ + public void setAttackStyleIndex(int attackStyleIndex) { + this.attackStyleIndex = attackStyleIndex; + } + +} 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 new file mode 100644 index 000000000..7b3f97c33 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/SkullManager.java @@ -0,0 +1,310 @@ +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.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. + */ + private final Player player; + + /** + * Represents if the player is in the wilderness. + */ + private boolean wilderness = false; + + /** + * If the wilderness zone is currently disabled. + */ + private boolean wildernessDisabled = false; + + /** + * Represents the current wilderness level. + */ + private int level; + + /** + * The players this player has skulled on. + */ + private final List skullCauses = new ArrayList(); + + /** + * If the player is skulled. + */ + private boolean skulled; + + /** + * If the skull check is disabled. + */ + private boolean skullCheckDisabled; + + private boolean deepWilderness; + + /** + * Constructs a new {@code SkullManager} {@code Object}. + * @param player the player. + */ + public SkullManager(Player player) { + this.player = player; + } + + /** + * Checks if we should skull on this entity. + * @param other The entity to check. + */ + public void checkSkull(Entity other) { + if (!(other instanceof Player) || !wilderness || skullCheckDisabled) { + return; + } + Player o = (Player) other; + for (Player p : o.getSkullManager().skullCauses) { + if (p == player) { + return; + } + } + if (skullCauses.contains(o)) { + return; + } + skullCauses.add(o); + removeTimer (player, "skulled"); + registerTimer (player, spawnTimer("skulled", 2000)); + } + + /** + * Sets the skull icon. + * @param skullIcon The skull icon. + */ + public void setSkullIcon(int skullIcon) { + player.getAppearance().setSkullIcon(skullIcon); + player.updateAppearance(); + } + + /** + * Resets the skull causes cache. + */ + public void reset() { + skullCauses.clear(); + setSkullIcon(-1); + setSkulled(false); + player.getAppearance().sync(); + } + + /** + * Gets the player. + * @return the player. + */ + public Player getPlayer() { + return player; + } + + /** + * Gets the level. + * @return the level. + */ + public int getLevel() { + return level; + } + + /** + * Sets the level. + * @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; + } + + /** + * Gets the value. + * @return the wilderness. + */ + public boolean isWilderness() { + return wilderness; + } + + /** + * Sets the value of this boolean. + * @param wilderness the wilderness to set. + */ + public void setWilderness(boolean wilderness) { + this.wilderness = wilderness; + } + + /** + * Gets the skullCheckDisabled. + * @return The skullCheckDisabled. + */ + public boolean isSkullCheckDisabled() { + return skullCheckDisabled; + } + + /** + * Sets the skullCheckDisabled. + * @param skullCheckDisabled The skullCheckDisabled to set. + */ + public void setSkullCheckDisabled(boolean skullCheckDisabled) { + this.skullCheckDisabled = skullCheckDisabled; + } + + /** + * Gets the wildernessDisabled. + * @return The wildernessDisabled. + */ + public boolean isWildernessDisabled() { + return wildernessDisabled; + } + + public boolean hasWildernessProtection() { + return level < 49; + } + + /** + * Sets the wildernessDisabled. + * @param wildernessDisabled The wildernessDisabled to set. + */ + public void setWildernessDisabled(boolean wildernessDisabled) { + this.wildernessDisabled = wildernessDisabled; + } + + /** + * Gets the skulled. + * @return The skulled. + */ + public boolean isSkulled() { + 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. + */ + public void setSkulled(boolean skulled) { + this.skulled = skulled; + } +} diff --git a/Server/src/main/java/core/game/node/entity/player/link/SpellBookManager.java b/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java similarity index 76% rename from Server/src/main/java/core/game/node/entity/player/link/SpellBookManager.java rename to Server/src/main/core/game/node/entity/player/link/SpellBookManager.java index 010a90a29..72fd3005a 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/SpellBookManager.java +++ b/Server/src/main/core/game/node/entity/player/link/SpellBookManager.java @@ -1,13 +1,14 @@ package core.game.node.entity.player.link; import core.game.component.Component; -import core.game.node.entity.skill.magic.MagicSpell; +import core.game.node.entity.combat.spell.MagicSpell; import core.game.node.entity.player.Player; - -import java.nio.ByteBuffer; 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. @@ -16,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(); @@ -24,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()); } /** @@ -54,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 */ @@ -132,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 new file mode 100644 index 000000000..723b62b48 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/TeleportManager.java @@ -0,0 +1,717 @@ +package core.game.node.entity.player.link; + +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.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. + * @author SonicForce41 + * @author Woah + */ +public class TeleportManager { + + /** + * The wildy teleport type. + */ + public static final int WILDY_TELEPORT = 1 << 16 | 8; + + /** + * The entity being handled. + */ + private final Entity entity; + + /** + * The last teleport of this Entity + */ + private Pulse lastTeleport; + + /** + * The current teleport of this Entity + */ + private Pulse currentTeleport; + + /** + * The current teleport type. + */ + private int teleportType; + + /** + * Constructs a new {@code Teleporter.java} {@Code Object}. + * @param entity the Entity + */ + public TeleportManager(Entity entity) { + this.entity = entity; + } + + /** + * Sends the teleport. + * @param location the Location. + * @return {@code True} if the player successfully started teleporting. + */ + public boolean send(Location location) { + return send(location, entity instanceof Player ? getType((Player) entity) : TeleportType.NORMAL, 0); + } + + /** + * Sends the teleport. + * @param location the Location. + * @param type the NodeType. + * @return {@code True} if the player successfully started teleporting. + */ + public boolean send(Location location, TeleportType type) { + return send(location, type, 0); + } + + /** + * Sends the teleport. + * @param location the Location. + * @param type the NodeType. + * @param teleportType The teleporting type. (0=spell, 1=item, 2=object, + * 3=npc -1= force) + * @return {@code True} if the player successfully started teleporting. + */ + public boolean send(Location location, TeleportType type, int teleportType) { + 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; + } + } + if (teleportType != -1) { + if (entity instanceof Player) { + Player p = (Player) entity; + p.scripts.setDelay(GameWorld.getTicks() + 12); + p.getDialogueInterpreter().close(); + } + } + if(entity.getAttribute("tablet-spell",false)){ + type = TeleportType.TELETABS; + } + this.teleportType = teleportType; + entity.getWalkingQueue().reset(); + currentTeleport = type.getPulse(entity, location); + entity.getPulseManager().clear(); + entity.lock(12); + entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12); + GameWorld.getPulser().submit(currentTeleport); + if (entity instanceof Player) { + ((Player) entity).getInterfaceManager().close(); + } + return true; + } + + /** + * Fires a random event. + * @param entity The entity teleporting. + * @param location The destination location. + */ + public static void fireRandom(Entity entity, Location location) { + if (entity instanceof Player && entity.getTeleporter().getTeleportType() == 0) { + Player p = (Player) entity; + } + } + + /** + * Gets the entity. + * @return the Entity + */ + public final Entity getEntity() { + return entity; + } + + /** + * Gets the current teleport pulse. + * @return the Pulse + */ + public final Pulse getCurrentTeleport() { + return currentTeleport; + } + + /** + * Represents a NodeType for Teleporter + * @author SonicForce41 + */ + public enum TeleportType { + + /** + * The value types + */ + NORMAL(new TeleportSettings(8939, 8941, 1576, 1577)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + 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) { + playGlobalAudio(entity.getLocation(), Sounds. TELEPORT_REVERSE_201); + entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); + entity.graphics(new Graphics(getSettings().getEndGfx())); + return true; + } + delay++; + return false; + } + + @Override + public void stop() { + super.stop(); + entity.unlock(); + entity.lock(4); + } + }; + } + }, + ANCIENT(new TeleportSettings(1979, -1, 392, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + 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) { + entity.getProperties().setTeleportLocation(Location.create(location)); + fireRandom(entity, location); + } else if (delay == 5) { + entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); + entity.graphics(new Graphics(getSettings().getEndGfx())); + return true; + } + delay++; + return false; + } + + @Override + public void stop() { + super.stop(); + entity.getAnimator().forceAnimation(new Animation(-1)); + entity.graphics(new Graphics(-1)); + entity.unlock(); + } + }; + } + }, + LUNAR(new TeleportSettings(1816, -1, 747, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + entity.graphics(new Graphics(getSettings().getStartGfx(), 120)); + entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); + } else if (delay == 3) { + entity.getProperties().setTeleportLocation(Location.create(location)); + fireRandom(entity, location); + } else if (delay == 4) { + entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); + entity.graphics(new Graphics(getSettings().getEndGfx())); + return true; + } + delay++; + return false; + } + + @Override + public void stop() { + super.stop(); + entity.getAnimator().forceAnimation(new Animation(-1)); + entity.graphics(new Graphics(-1)); + entity.unlock(); + } + }; + } + }, + TELETABS(new TeleportSettings(4731, -1, 678, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + 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)); + entity.graphics(new Graphics(getSettings().getStartGfx())); + } else if (delay == 3) { + entity.getProperties().setTeleportLocation(Location.create(location)); + } else if (delay == 4) { + entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote())); + entity.graphics(new Graphics(getSettings().getEndGfx())); + return true; + } + delay++; + return false; + } + + @Override + public void stop() { + super.stop(); + entity.getAnimator().forceAnimation(new Animation(-1)); + entity.graphics(new Graphics(-1)); + entity.unlock(); + entity.lock(2); + } + }; + } + }, + OBELISK(new TeleportSettings(8939, 8941, 661, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + entity.lock(); + entity.getAnimator().forceAnimation(new Animation(1816)); + } else if (delay == 3) { + entity.getProperties().setTeleportLocation(Location.create(location)); + } else if (delay == 4) { + entity.getAnimator().forceAnimation(Animation.RESET); + entity.unlock(); + return true; + } + delay++; + return false; + } + }; + } + }, + TELE_OTHER(new TeleportSettings(1816, -1, 342, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + 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) { + entity.getProperties().setTeleportLocation(Location.create(location)); + } else if (delay == 4) { + entity.getAnimator().forceAnimation(new Animation(-1)); + entity.unlock(); + return true; + } + delay++; + return false; + } + }; + } + }, + FAIRY_RING(new TeleportSettings(-1, -1, -1, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + entity.graphics(Graphics.create(569)); + return new TeleportPulse(entity) { + int delay; + + @Override + public boolean pulse() { + switch (++delay) { + case 2: + entity.animate(Animation.create(3265)); + if(entity instanceof Player) { + playAudio(entity.asPlayer(), Sounds.FT_FAIRY_TELEPORT_1098); + } + break; + case 4: + entity.animate(Animation.create(3266)); + entity.getProperties().setTeleportLocation(location); + entity.unlock(); + entity.lock(2); + return true; + } + return false; + } + + }; + } + }, + PURO_PURO(new TeleportSettings(6601, 1118, -1, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); + entity.graphics(new Graphics(1118)); + } else if (delay == 9) { + entity.getProperties().setTeleportLocation(Location.create(location)); + entity.getAnimator().forceAnimation(new Animation(-1)); + entity.unlock(); + return true; + } + delay++; + return false; + } + }; + } + }, + ECTOPHIAL(new TeleportSettings(8939, 8941, 1587, 1588)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + 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) { + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_REVERSE_201); + entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); + entity.graphics(new Graphics(getSettings().getEndGfx())); + return true; + } + delay++; + return false; + } + }; + } + }, + CHRISTMAS(new TeleportSettings(7534, -1, 1292, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + 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) { + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_REVERSE_201); + entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); + entity.graphics(new Graphics(getSettings().getEndGfx())); + return true; + } + delay++; + return false; + } + + @Override + public void stop() { + super.stop(); + entity.unlock(); + entity.lock(4); + } + }; + } + }, + CABBAGE(new TeleportSettings(9984, 9986, 1731, 1732)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + if (entity instanceof Player) { + playAudio(entity.asPlayer(), 5036); + } + entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); + entity.graphics(new Graphics(getSettings().getStartGfx())); + } else if (delay == 5) { + entity.getProperties().setTeleportLocation(Location.create(location)); + fireRandom(entity, location); + if (entity instanceof Player) { + playAudio(entity.asPlayer(), 5034); + } + entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); + entity.graphics(new Graphics(getSettings().getEndGfx())); + return true; + } + delay++; + return false; + } + + @Override + public void stop() { + super.stop(); + entity.unlock(); + entity.lock(4); + } + }; + } + }, + ENTRANA_MAGIC_DOOR(new TeleportSettings(10100, 9013, 1745, 1747)) { // + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + 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) { + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_REVERSE_201); + entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); + entity.graphics(new Graphics(getSettings().getEndGfx())); + return true; + } + delay++; + return false; + } + + @Override + public void stop() { + super.stop(); + entity.unlock(); + entity.lock(4); + } + }; + } + }, + MINIGAME(new TeleportSettings(6601, 1118, -1, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + Player player; + + @Override + public boolean pulse() { + if (delay == 0) { + entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); + entity.graphics(new Graphics(1118)); + } else if (delay == 9) { + entity.getProperties().setTeleportLocation(Location.create(location)); + entity.getAnimator().forceAnimation(new Animation(-1)); + entity.unlock(); + return true; + } + delay++; + return false; + } + + @Override + public void start() { + + super.start(); + } + + @Override + public void stop() { + super.stop(); + entity.getAnimator().forceAnimation(new Animation(-1)); + entity.graphics(new Graphics(-1)); + } + }; + } + }, + INSTANT(new TeleportSettings(-1, -1, -1, -1)) { + @Override + public Pulse getPulse(final Entity entity, final Location location) { + return new TeleportPulse(entity) { + int delay = 0; + + @Override + public boolean pulse() { + if (delay == 0) { + entity.lock(); + } else if (delay == 3) { + entity.getProperties().setTeleportLocation(Location.create(location)); + } else if (delay == 4) { + entity.getAnimator().forceAnimation(Animation.RESET); + entity.unlock(); + return true; + } + delay++; + return false; + } + }; + } + }; + + /** + * The NodeSettings + */ + private TeleportSettings settings; + + /** + * @param entity + * @param location + * @return the Pulse + */ + public abstract Pulse getPulse(final Entity entity, final Location location); + + /** + * Constructs a new {@code Teleporter.java} {@Code Object}. + * @param settings the NodeSettings + */ + TeleportType(TeleportSettings settings) { + this.settings = settings; + } + + /** + * Gets the NodeSettings + * @return the NodeSettings + */ + public final TeleportSettings getSettings() { + return settings; + } + } + + /** + * Represents teleport node settings + * @author SonicForce41 + */ + static class TeleportSettings { + + /** + * The start animation. + */ + private int startAnim; + + /** + * The end animation. + */ + private int endAnim; + + /** + * The start graphics. + */ + private int startGFX; + + /** + * The end gfx. + */ + private int endGFX; + + /** + * Constructs a new {@code Teleporter.java} {@code Object}. + * @param startAnim the start animation. + * @param endAnim the end animation. + * @param startGfx the start graphics. + * @param endGfx the end graphiics. + */ + public TeleportSettings(int startAnim, int endAnim, int startGfx, int endGfx) { + this.startAnim = startAnim; + this.endAnim = endAnim; + this.startGFX = startGfx; + this.endGFX = endGfx; + } + + /** + * @return the anim. + */ + public final int getStartEmote() { + return startAnim; + } + + /** + * @return the anim. + */ + public final int getEndEmote() { + return endAnim; + } + + /** + * @return the start graphics. + */ + public final int getStartGfx() { + return startGFX; + } + + /** + * @return the end gfx. + */ + public final int getEndGfx() { + return endGFX; + } + } + + /** + * Gets the teleporting type for the player depending on his/her current + * spellbook. + * @param player The player. + * @return The teleport type. + */ + public static TeleportType getType(Player player) { + switch (player.getSpellBookManager().getSpellBook()) { + case 193: + return TeleportType.ANCIENT; + case 430: + return TeleportType.LUNAR; + } + return TeleportType.NORMAL; + } + + /** + * Gets the teleportType. + * @return The teleportType. + */ + public int getTeleportType() { + return teleportType; + } + + /** + * Sets the teleportType. + * @param teleportType The teleportType to set. + */ + public void setTeleportType(int teleportType) { + 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/java/core/game/node/entity/player/link/WarningMessages.java b/Server/src/main/core/game/node/entity/player/link/WarningMessages.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/player/link/WarningMessages.java rename to Server/src/main/core/game/node/entity/player/link/WarningMessages.java index fad53a42b..11731ff1d 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/player/link/appearance/Appearance.java b/Server/src/main/core/game/node/entity/player/link/appearance/Appearance.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/player/link/appearance/Appearance.java rename to Server/src/main/core/game/node/entity/player/link/appearance/Appearance.java index 9b37e4356..e4c5ecfb4 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/appearance/Appearance.java +++ b/Server/src/main/core/game/node/entity/player/link/appearance/Appearance.java @@ -8,10 +8,9 @@ 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 rs09.game.system.config.ItemConfigParser; +import core.game.system.config.ItemConfigParser; /** * Represents an appearance managing class of a player. @@ -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/java/core/game/node/entity/player/link/appearance/BodyPart.java b/Server/src/main/core/game/node/entity/player/link/appearance/BodyPart.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/appearance/BodyPart.java rename to Server/src/main/core/game/node/entity/player/link/appearance/BodyPart.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/appearance/Gender.java b/Server/src/main/core/game/node/entity/player/link/appearance/Gender.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/appearance/Gender.java rename to Server/src/main/core/game/node/entity/player/link/appearance/Gender.java 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/diary/AchievementDiary.java b/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiary.java new file mode 100644 index 000000000..875798165 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiary.java @@ -0,0 +1,556 @@ +package core.game.node.entity.player.link.diary; + +import core.cache.def.impl.NPCDefinition; +import core.game.component.Component; +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import core.game.diary.DiaryLevel; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Represents an achievement diary. + * @author Vexia + */ +public class AchievementDiary { + + /** + * The component id of the diary. + */ + public static final int DIARY_COMPONENT = 275; + + /** + * Completed levels for this diary + */ + public static final ArrayList completedLevels = new ArrayList(); + + /** + * Represents the red color code string. + */ + private static final String RED = ""; + + /** + * Represents the blue color code string. + */ + private static final String BLUE = ""; + + /** + * Represents the yellow color code string. + */ + private static final String YELLOW = ""; + + /** + * Represets the green color code string. + */ + private static final String GREEN = ""; + + /** + * The diary type. + */ + private final DiaryType type; + + /** + * The task types started. + */ + private final boolean[] levelStarted = new boolean[3]; + + /** + * If the rewards have been given. + */ + private final boolean[] levelRewarded = new boolean[3]; + + /** + * The completed achievements. + */ + private final boolean[][] taskCompleted; + + /** + * Constructs a new {@code AchievementDiary} {@code Object} + * @param type the diary type. + */ + public AchievementDiary(DiaryType type) { + this.type = type; + this.taskCompleted = new boolean[type.getAchievements().length][25]; + } + + /** + * Open the achievement diary. + * @param player the player. + */ + public void open(Player player) { + clear(player); + sendString(player, "Achievement Diary - " + type.getName(), 2); + int child = 12; + + sendString(player, (isComplete() ? GREEN : isStarted() ? YELLOW : "") + type.getName() + " Area Tasks", child++); + //child++; + + if (!type.getInfo().isEmpty() && !this.isStarted()) { + sendString(player, type.getInfo(), child++); + child += type.getInfo().split("

").length; + } + child++; + + boolean complete; + String line; + for (int level = 0; level < type.getAchievements().length; level++) { + sendString(player, getStatus(level) + getLevel(level) + "", child++); + child++; + for (int i = 0; i < type.getAchievements(level).length; i++) { + complete = isComplete(level, i); + line = type.getAchievements(level)[i]; + if (line.contains("

")) { + String[] lines = line.split("

"); + for (String l : lines) { + sendString(player, complete ? "" + l : l, child++); + } + } else { + sendString(player, complete ? "" + line : line, child++); + } + sendString(player, "*", child++); + } + child++; + } + 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); + } + + /** + * Clears the diary screen. + * @param player the player. + */ + private void clear(Player player) { + for (int i = 0; i < 311; i++) { + player.getPacketDispatch().sendString("", DIARY_COMPONENT, i); + } + } + + public void parse(JSONObject data){ + JSONArray startedArray = (JSONArray) data.get("startedLevels"); + for(int i = 0; i < startedArray.size(); i++){ + levelStarted[i] = (boolean) startedArray.get(i); + } + JSONArray completedArray = (JSONArray) data.get("completedLevels"); + for(int i = 0; i < completedArray.size(); i++){ + JSONArray level = (JSONArray) completedArray.get(i); + boolean completed = true; + for(int j = 0; j < level.size(); j++){ + taskCompleted[i][j] = (boolean) level.get(j); + if(!taskCompleted[i][j]){completed = !completed;} + completedLevels.add(i); + } + } + JSONArray rewardedArray = (JSONArray) data.get("rewardedLevels"); + for(int i = 0; i < rewardedArray.size(); i++){ + levelRewarded[i] = (boolean) rewardedArray.get(i); + } + } + + /** + * Draws the status of the diary. + * @param player the player. + */ + public void drawStatus(Player player) { + if (isStarted()) { + player.getPacketDispatch().sendString((isComplete() ? GREEN : YELLOW) + type.getName(), 259, type.getChild()); + for (int i = 0; i < 3; i++) { + player.getPacketDispatch().sendString((isComplete(i) ? GREEN : isStarted(i) ? YELLOW : "") + getLevel(i), 259, type.getChild() + (i + 1)); + } + } + } + + /** + * Induces a task update. + * @param player the player. + * @param level the level. + * @param index the index of the task. + * @param complete if it's completed. + */ + public void updateTask(Player player, int level, int index, boolean complete) { + if (!levelStarted[level]) { + levelStarted[level] = true; + } + if (!complete) { + player.sendMessage("Well done! A " + type.getName() + " task has been updated."); + } else { + taskCompleted[level][index] = true; + int tempLevel = this.type == DiaryType.LUMBRIDGE ? level - 1 : level; + player.sendMessages("Well done! You have completed " + + (tempLevel == -1 ? "a beginner" : tempLevel == 0 ? "an easy" : tempLevel == 1 ? "a medium" : "a hard") + + " task in the " + type.getName() + " area. Your", "Achievement Diary has been updated."); + } + if (isComplete(level)) { + player.sendMessages("Congratulations! You have completed all of the " + getLevel(level).toLowerCase() + + " tasks in the " + type.getName() + " area.", "Speak to " + + NPCDefinition.forId(type.getNpc(level)).getName() + " to claim your reward."); + } + drawStatus(player); + } + + public void finishTask(Player player, int level, int index) { + if (!this.isComplete(level, index)) { + this.updateTask(player, level, index, true); + boolean complete = true; + for(int i = 0; i < taskCompleted[level].length; i++){ + if(!taskCompleted[level][i]) { + complete = false; + break; + } + } + if(complete){ + completedLevels.add(level); + } else if(completedLevels.contains(level)) completedLevels.remove((Object) level); + } + } + + /** + * Resets a task to un-start + */ + public void resetTask(Player player, int level, int index) { + taskCompleted[level][index] = false; + if (!isStarted(level)) { + this.levelStarted[level] = false; + } + if (!isComplete(level)) { + this.levelRewarded[level] = false; + } + drawStatus(player); + } + + /** + * Sends a string on the diary interface. + * @param player the player. + * @param string the string. + * @param child the child. + */ + private void sendString(Player player, String string, int child) { + player.getPacketDispatch().sendString(string.replace("", BLUE).replace("", RED), DIARY_COMPONENT, child); + } + + /** + * Sets the diary for the level as started. + * @param level the level. + */ + public void setLevelStarted(int level) { + this.levelStarted[level] = true; + } + + /** + * Sets an achievement as completed. + * @param level the level. + * @param index the index. + */ + public void setCompleted(int level, int index) { + this.taskCompleted[level][index] = true; + } + + /** + * Checks if the achievement level is started. + * @param level the level. + * @return {@code True} if so. + */ + public boolean isStarted(int level) { + return this.levelStarted[level]; + } + + /** + * Checks if the diary is started. + * @return {@code True} if so. + */ + public boolean isStarted() { + for (int j = 0; j < type.getLevelNames().length; j++) { + if (isStarted(j)) { + return true; + } + } + return false; + } + + /** + * Checks if an achievement task is completed. + * @param level the level. + * @param index the index. + * @return {@code True} if an achievement is completed. + */ + public boolean isComplete(int level, int index) { + return taskCompleted[level][index]; + } + + /** + * Checks if the achievement level is completed. + * @param level the level. + * @return {@code True} if so. + */ + public boolean isComplete(int level) { + for (int i = 0; i < type.getAchievements(level).length; i++) { + if (!taskCompleted[level][i]) { + return false; + } + } + return true; + } + + public boolean isComplete(int level, boolean cumulative) { + if (isComplete(level)) { + return !cumulative || level <= 0 || isComplete(level - 1, true); + } else { + return false; + } + } + + /** + * Checks if an achievement diary is complete. + * @return {@code True} if completed. + */ + public boolean isComplete() { + for (int i = 0; i < taskCompleted.length; i++) { + for (int x = 0; x < type.getAchievements(i).length; x++) { + if (!taskCompleted[i][x]) { + return false; + } + } + } + return true; + } + + /** + * Gets the level of completion. + * @return the level. + */ + public int getLevel() { + return isComplete(2) ? 2 : isComplete(1) ? 1 : isComplete(0) ? 0 : -1; + } + + /** + * Gets the level of reward. + * @return the level. + */ + public int getReward() { + return isLevelRewarded(2) ? 2 : isLevelRewarded(1) ? 1 : isLevelRewarded(0) ? 0 : -1; + } + + /** + * Gets the level string. + * @param level the level. + * @return the string format. + */ + public String getLevel(int level) { + return type.getLevelNames()[level]; + } + + /** + * Gets the status for a level of completion of the achievement. + * @param level the level. + * @return the string color status. + */ + public String getStatus(int level) { + return !isStarted(level) ? RED : isComplete(level) ? GREEN : YELLOW; + } + + /** + * Sets the level as rewarded. + * @param level the level. + */ + public void setLevelRewarded(int level) { + this.levelRewarded[level] = true; + } + + /** + * Checks if the reward has been given. + * @param level the level. + * @return {@code True} if so. + */ + public boolean isLevelRewarded(int level) { + return levelRewarded[level]; + } + + /** + * Gets the completed. + * @return the completed + */ + public boolean[][] getTaskCompleted() { + return taskCompleted; + } + + /** + * Gets the type. + * @return the type + */ + public DiaryType getType() { + return type; + } + + /** + * Gets the started. + * @return the started + */ + public boolean[] getLevelStarted() { + return levelStarted; + } + + /** + * Gets the rewarded. + * @return the rewarded + */ + public boolean[] getLevelRewarded() { + return levelRewarded; + } + + /** + * Removes the non-lamp reward item for the given level of the given type + * @param player the player to remove the reward from + * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. + * @param level the diary level. 0-indexed. + * @return whether or not a reward was removed + */ + public static boolean removeRewardsFor(Player player, DiaryType type, int level) { + Item[] rewards = type.getRewards(level); + //lamps are always the 2nd reward for a level, don't remove lamps + boolean hasRemoved = + player.getInventory().remove(rewards[0]) + ||player.getBank().remove(rewards[0]) + ||player.getEquipment().remove(rewards[0]); + + if (hasRemoved) { + player.debug("Removed previous reward"); + } + + return hasRemoved; + } + + /** + * Adds all rewards for the given level of the given type (including lamps) + * Will return false if the player can't fit all the items in their inventory. + * @param player the player to grant the items to + * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. + * @param level the diary level. 0-indexed. + * @return whether or not we successfully added the reward items to the player's inventory. + */ + public static boolean addRewardsFor(Player player, DiaryType type, int level) { + Item[] rewards = type.getRewards(level); + + int freeSlots = player.getInventory().freeSlots(); + if (freeSlots < rewards.length) + return false; + + boolean allRewarded = true; + for (Item reward : rewards) { + allRewarded &= player.getInventory().add(reward); + } + + if (!allRewarded) { + Arrays.stream(rewards).forEach((item) -> { + boolean _ignored = player.getInventory().remove(item); + }); + } + + return allRewarded; + } + + /** + * Convenience method. Flags a level as complete, removes previous level's reward item, then adds new reward items. + * @param player the player to flag completion for + * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. + * @param level the diary level. 0-indexed. + */ + public static boolean flagRewarded(Player player, DiaryType type, int level) { + if (level > 0) { + removeRewardsFor(player, type, level - 1); + } + if (addRewardsFor(player, type, level)) + player.getAchievementDiaryManager().getDiary(type).setLevelRewarded(level); + else { + player.sendMessage("You do not have enough space in your inventory to claim these rewards."); + return false; + } + + return true; + } + + /** + * 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 claimed + * the next diary level. + * @param player the player to check + * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. + * @param level the diary level. 0-indexed. + * @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]; + boolean claimed = hasCompletedLevel(player, type, level) + && hasClaimedLevelRewards(player, type, level) + && !player.hasItem(reward); + return level == 2 ? claimed : claimed && !hasClaimedLevelRewards(player, type, level+1); + } + + /** + * Grants the replacement for the given level provided the player is eligible. + * Does not refund lamps. + * @param player the player to grant the reward to + * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. + * @param level the diary level, 0-indexed + * @return whether or not the player was granted the replacement + */ + public static boolean grantReplacement(Player player, DiaryType type, int level) { + Item reward = type.getRewards(level)[0]; //Can only replace non-lamp reward + return canReplaceReward(player, type, level) && player.getInventory().add(reward); + } + + /** + * Checks if a player has completed the given level of the given diary. + * @param player the player to check + * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. + * @param level the level to check, 0-indexed + * @return whether or not the player has completed the level. + */ + public static boolean hasCompletedLevel(Player player, DiaryType type, int level) { + if (level > type.getLevelNames().length - 1) + return false; + return player.getAchievementDiaryManager().getDiary(type).isComplete(level, true); + } + + /** + * Checks if a player has claimed the rewards for the given level of the given diary + * @param player the player to check + * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. + * @param level the level te check + * @return whether or not the player has claimed the rewards + */ + public static boolean hasClaimedLevelRewards(Player player, DiaryType type, int level) { + return player.getAchievementDiaryManager().getDiary(type).isLevelRewarded(level); + } + + /** + * Checks if a player can claim the rewards for the given level of the given diary + * 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) { + 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); + } + + /** + * Fetches the rewards for a given diary at the given level. + * @param type the DiaryType: LUMBRIDGE, FALADOR, etc. + * @param level the level, 0-indexed. + * @return an array of the reward items for that level. Includes lamps. The non-lamp reward is always index 0. + */ + public static Item[] getRewards(DiaryType type, int level) { + return type.getRewards(level); + } +} diff --git a/Server/src/main/java/core/game/node/entity/player/link/diary/AchievementDiaryManager.java b/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiaryManager.java similarity index 99% rename from Server/src/main/java/core/game/node/entity/player/link/diary/AchievementDiaryManager.java rename to Server/src/main/core/game/node/entity/player/link/diary/AchievementDiaryManager.java index 2f51e4caa..fd693fb13 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/diary/AchievementDiaryManager.java +++ b/Server/src/main/core/game/node/entity/player/link/diary/AchievementDiaryManager.java @@ -3,7 +3,7 @@ package core.game.node.entity.player.link.diary; import core.game.component.Component; import core.game.container.impl.EquipmentContainer; import core.game.node.entity.player.Player; -import core.game.node.entity.skill.smithing.smelting.Bar; +import content.global.skill.smithing.smelting.Bar; import core.game.node.item.Item; import org.json.simple.JSONArray; import org.json.simple.JSONObject; diff --git a/Server/src/main/java/core/game/node/entity/player/link/diary/DiaryType.java b/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/player/link/diary/DiaryType.java rename to Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java index dbda3a106..dbbb06e81 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/diary/DiaryType.java +++ b/Server/src/main/core/game/node/entity/player/link/diary/DiaryType.java @@ -1,7 +1,8 @@ package core.game.node.entity.player.link.diary; -import org.rs09.consts.Items; +import core.game.node.entity.player.Player; import core.game.node.item.Item; +import org.rs09.consts.Items; /** * An achievement diary type. @@ -33,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", @@ -117,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", @@ -139,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" @@ -343,16 +344,16 @@ public enum DiaryType { new String[]{"Easy", "Medium", "Hard"}, new String[][]{ { - "Kill a cave crawler in the Fremennik Slayer Dungeon", // TODO - "Kill five rock crabs on the shore near Rellekka or on

Waterbirth Island", // TODO - "Find the highest tree on the Fremennik mainland", // TODO - "View the rewards in the Barbarian Assault tutorial", // TODO - "Speak to Otto Godblessed about barbarian training", // TODO - "Collect three seaweed from the shore north-east of Rellekka", // TODO - "Find the Hunting Expert on the northern ice plains", // TODO - "Catch a fish off one of Rellekka's piers", // TODO - "Recharge your Summoning points near Rellekka's gate", // TODO - "Kill an adult black unicorn" // TODO + "Kill a cave crawler in the Fremennik Slayer Dungeon", + "Kill five rock crabs on the shore near Rellekka or on

Waterbirth Island", + "Find the highest tree on the Fremennik mainland", + "View the rewards in the Barbarian Assault tutorial", // TODO: Move this to appropriate dialogue once Barbarian Assault is implemented + "Speak to Otto Godblessed about barbarian training", // TODO: Move this to an appropriate point once the entirety of Barbarian training is implemented + "Collect three seaweed from the shore north-east of Rellekka", + "Find the Hunting Expert on the northern ice plains", // TODO: Transcribe the appropriate authentic dialogue for this NPC. Mockup dialogue implemented as a bandaid. + "Catch a fish off one of Rellekka's piers", // TODO: Make this check for any Fishing Spots within the Rellekkan region ID list. Currently only checks the "Cage/Harpoon" Fishing spots at the northeastern most pier. + "Recharge your Summoning points near Rellekka's gate", + "Kill an adult black unicorn" }, { "Learn of the history of the Fremennik and the Outerlanders

from Chieftain Brundt", // TODO @@ -544,6 +545,15 @@ public enum DiaryType { return npcs[level]; } + public boolean hasRewardEquipment(Player player) { + for(Item[] tier : getRewards()) { + for(Item item : tier) { + if(!item.getName().toLowerCase().contains("lamp") && player.hasItem(item)) return true; + } + } + return false; + } + /** * Gets the rewards for the task level. * diff --git a/Server/src/main/java/core/game/node/entity/player/link/emote/EmoteManager.java b/Server/src/main/core/game/node/entity/player/link/emote/EmoteManager.java similarity index 93% rename from Server/src/main/java/core/game/node/entity/player/link/emote/EmoteManager.java rename to Server/src/main/core/game/node/entity/player/link/emote/EmoteManager.java index 7f2e691f9..e9c991d80 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/player/link/emote/Emotes.java b/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/player/link/emote/Emotes.java rename to Server/src/main/core/game/node/entity/player/link/emote/Emotes.java index 2406f1925..2bda2ca57 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/emote/Emotes.java +++ b/Server/src/main/core/game/node/entity/player/link/emote/Emotes.java @@ -1,21 +1,21 @@ 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; import core.game.world.map.Location; import core.game.world.map.RegionManager; import org.rs09.consts.Items; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; 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 rs09.game.world.GameWorld; +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. * @@ -52,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) { @@ -107,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); } @@ -168,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."), @@ -176,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 @@ -293,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. @@ -339,9 +344,6 @@ public enum Emotes { } player.getAchievementDiaryManager().getDiary(DiaryType.VARROCK).updateTask(player, 1, 6, good); } - if (TutorialSession.getExtension(player).getStage() == 24) { - TutorialStage.load(player, 25, false); - } player.getPulseManager().clear(); emote.play(player); } @@ -420,4 +422,4 @@ public enum Emotes { return lockedMessage; } -} +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/music/MusicEntry.java b/Server/src/main/core/game/node/entity/player/link/music/MusicEntry.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/music/MusicEntry.java rename to Server/src/main/core/game/node/entity/player/link/music/MusicEntry.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/music/MusicPlayer.java b/Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java similarity index 83% rename from Server/src/main/java/core/game/node/entity/player/link/music/MusicPlayer.java rename to Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java index 54a3cdae8..163cdadc5 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/music/MusicPlayer.java +++ b/Server/src/main/core/game/node/entity/player/link/music/MusicPlayer.java @@ -3,17 +3,17 @@ package core.game.node.entity.player.link.music; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.emote.Emotes; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.net.packet.PacketRepository; import core.net.packet.context.MusicContext; import core.net.packet.context.StringContext; import core.net.packet.out.MusicPacket; import core.net.packet.out.StringPacket; -import java.nio.ByteBuffer; -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. @@ -34,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. @@ -75,12 +75,12 @@ 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; } - player.getPacketDispatch().sendAccessMask(value, 1, 187, 0, CONFIG_IDS.length * 64); + player.getPacketDispatch().sendIfaceSettings(value, 1, 187, 0, CONFIG_IDS.length * 64); if (!unlocked.containsKey(TUTORIAL_MUSIC)) { unlock(TUTORIAL_MUSIC, false); } @@ -93,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. @@ -130,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]); } } @@ -167,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(), 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; + } } /** @@ -189,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(); @@ -214,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){} } } @@ -225,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); } /** @@ -290,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/java/core/game/node/entity/player/link/music/MusicZone.java b/Server/src/main/core/game/node/entity/player/link/music/MusicZone.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/music/MusicZone.java rename to Server/src/main/core/game/node/entity/player/link/music/MusicZone.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java b/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java rename to Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java index b2718b75d..8dcdf8265 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/Prayer.java +++ b/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java @@ -1,5 +1,6 @@ package core.game.node.entity.player.link.prayer; +import content.global.skill.skillcapeperks.SkillcapePerks; import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; import core.game.node.entity.Entity; @@ -13,12 +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 rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; -import rs09.game.world.GameWorld; +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)) { @@ -119,7 +121,7 @@ public final class Prayer { } for (Object o : targets) { Entity entity = (Entity) o; - if (entity.isAttackable(player, CombatStyle.MAGIC)) { + if (entity.isAttackable(player, CombatStyle.MAGIC, false)) { entity.getImpactHandler().manualHit(player, 1 + RandomFunction.randomize(maximum), HitsplatType.NORMAL); } } @@ -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/java/core/game/node/entity/player/link/prayer/PrayerCategory.java b/Server/src/main/core/game/node/entity/player/link/prayer/PrayerCategory.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/prayer/PrayerCategory.java rename to Server/src/main/core/game/node/entity/player/link/prayer/PrayerCategory.java 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 new file mode 100644 index 000000000..41f7dfbf2 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/prayer/PrayerType.java @@ -0,0 +1,444 @@ +package core.game.node.entity.player.link.prayer; + +import content.data.Quests; +import core.game.node.entity.player.link.diary.DiaryType; +import core.game.node.entity.skill.SkillBonus; +import core.game.node.entity.skill.Skills; +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 + * @author Emperor + * @author jamix77 + */ +public enum PrayerType { + 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, 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 + * what head icon to display. + */ + private final static Object[][] ICON_CACHE = new Object[][] { { REDEMPTION, 5 }, { RETRIBUTION, 3 }, { SMITE, 4 }, { PROTECT_FROM_MAGIC, 2, 10 }, { PROTECT_FROM_MELEE, 0, 8 }, { PROTECT_FROM_MISSILES, 1, 9 }, { PROTECT_FROM_SUMMONING, 7, PROTECT_FROM_MELEE, 8, PROTECT_FROM_MISSILES, 9, PROTECT_FROM_MAGIC, 10 } }; + + /** + * The level required. + */ + private final int level; + + /** + * The drain rate. + */ + private final int drain; + + /** + * The configuration id of the prayer. + */ + private final int config; + + /** + * The button id. + */ + private final int button; + + /** + * The restriction. + */ + private final PrayerCategory restriction; + + /** + * Represents the second restriction. + */ + private final PrayerCategory secondRestriction; + + /** + * The sound. + */ + private Audio sound; + + /** + * The required level of defence to use this prayer. + */ + private int defenceReq; + + /** + * The skill bonuses for this type. + */ + private final SkillBonus[] bonuses; + + /** + * Constructs a new {@code PrayerType} {@code Object}. + * @param level the level. + * @param drain the drain, represents the seconds until a drain. + * @param config the config value to represent on and off. + * @param button the button value to turn the prayer off and on. + * @param bonuses the skill bonuses for this type. + */ + 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 + * @param config + * @param button + * @param restriction + * @param soundId + * @param defenceReq + * @param bonuses + */ + PrayerType(int level, int drain, int config, int button, PrayerCategory restriction, int soundId, int defenceReq, SkillBonus... bonuses) { + this(level, drain, config, button, restriction, null, new Audio(soundId), defenceReq,bonuses); + } + + /** + * Constructs a new {@code PrayerType} {@code Object}. + * @param level the level. + * @param drain the drain, represents the seconds until a drain. + * @param config the config value to represent on and off. + * @param button the button value to turn the prayer off and on. + * @param bonuses the skill bonuses for this type. + */ + 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 + * @param config + * @param button + * @param restriction + * @param secondRestriction + * @param sound + * @param defenceReq + * @param bonuses + */ + PrayerType(int level, int drain, int config, int button, PrayerCategory restriction, PrayerCategory secondRestriction, Audio sound, int defenceReq, SkillBonus... bonuses) { + this.level = level; + this.drain = drain; + this.config = config; + this.button = button; + this.restriction = restriction; + this.secondRestriction = secondRestriction; + this.sound = sound; + this.bonuses = bonuses; + this.defenceReq = defenceReq; + } + + /** + * Gets the level. + * @return The level. + */ + public int getLevel() { + return level; + } + + /** + * Gets the drain. + * @return The drain. + */ + public int getDrain() { + return drain; + } + + /** + * Gets the config. + * @return The config. + */ + public int getConfig() { + return config; + } + + /** + * Gets the button. + * @return The button. + */ + public int getButton() { + return button; + } + + /** + * Gets the restriction. + * @return The restriction. + */ + public PrayerCategory getRestriction() { + return restriction; + } + + /** + * Gets the bonuses. + * @return The bonuses. + */ + public SkillBonus[] getBonuses() { + return bonuses; + } + + /** + * Method used to check if the player has the required level to toggle this + * type. + * @param player the player. + * @return True if it is permitted. + */ + public boolean permitted(final Player player) { + 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; + } + + /** + * Method used to check if we need to toggle a prayer on or off. + * @param player the player. + * @return True if toggled. + */ + public boolean toggle(final Player player, final boolean on) { + setVarp(player, getConfig(), on ? 1 : 0); + if (on) { + flag(player, this); + player.getPrayer().getActive().add(this); + iconify(player, getIcon(player, this)); + 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; + } + + /** + * Method used to flag others prayers that cannot be toggled together. + * @param player the player. + */ + public void flag(final Player player, final PrayerType type) { + final List active = player.getPrayer().getActive(); + final PrayerType[] remove = new PrayerType[active.size() + 10]; + int index = 0; + for (int i = 0; i < active.size(); i++) { + if (active.get(i).getRestriction() == type.getRestriction() || active.get(i).getSecondRestriction() != null && type.getSecondRestriction() != null && active.get(i).getSecondRestriction() == type.getSecondRestriction()) { + remove[index++] = active.get(i); + continue; + } + for (SkillBonus b : active.get(i).getBonuses()) { + for (SkillBonus bb : type.getBonuses()) { + if ((bb.getSkillId() == b.getSkillId()) || (b.getSkillId() == Skills.STRENGTH || b.getSkillId() == Skills.ATTACK) && (bb.getSkillId() == Skills.MAGIC || bb.getSkillId() == Skills.RANGE) || (b.getSkillId() == Skills.RANGE || b.getSkillId() == Skills.MAGIC) && (bb.getSkillId() == Skills.ATTACK || bb.getSkillId() == Skills.STRENGTH) || (b.getSkillId() == Skills.DEFENCE && bb.getSkillId() == Skills.DEFENCE)) { + remove[index++] = active.get(i); + } + } + } + } + for (int i = 0; i < index; i++) { + remove[i].toggle(player, false); + } + } + + /** + * Method used to iconify the player. + */ + public boolean iconify(final Player player, final int icon) { + if (icon == -1) { + return false; + } + player.getAppearance().setHeadIcon(icon); + player.getAppearance().sync(); + return false; + } + + /** + * Method used to find the next icon in place. + * @param player the player. + */ + public void findNextIcon(final Player player) { + if (!hasIcon(player)) { + player.getAppearance().setHeadIcon(-1); + player.getAppearance().sync(); + } + if ((this == PROTECT_FROM_MELEE || this == PROTECT_FROM_MISSILES || this == PROTECT_FROM_MAGIC) && player.getPrayer().get(PROTECT_FROM_SUMMONING)) { + iconify(player, 7); + } else if (this == PROTECT_FROM_SUMMONING) { + for (PrayerType t : player.getPrayer().getActive()) { + iconify(player, getIcon(player, t)); + } + if (player.getAppearance().getHeadIcon() == 7) { + player.getAppearance().setHeadIcon(-1); + player.getAppearance().sync(); + } + } + } + + /** + * Method used to get the icon value. + * @return the icon. + */ + public int getIcon(final Player player, final PrayerType type) { + List active = player.getPrayer().getActive(); + for (int i = 0; i < ICON_CACHE.length; i++) { + if (ICON_CACHE[i].length == 2 && type == ((PrayerType) ICON_CACHE[i][0])) { + return (int) ICON_CACHE[i][1]; + } else if (ICON_CACHE[i].length == 3 && type == ((PrayerType) ICON_CACHE[i][0])) { + if (active.contains(PROTECT_FROM_SUMMONING)) { + return (int) ICON_CACHE[i][2]; + } + return (int) ICON_CACHE[i][1]; + } else if (ICON_CACHE[i].length == 8 && type == ((PrayerType) ICON_CACHE[i][0])) { + for (int k = 2; k < ICON_CACHE[i].length; k++) { + if (active.contains(ICON_CACHE[i][k])) { + return (int) ICON_CACHE[i][k + 1]; + } + } + return (int) ICON_CACHE[i][1]; + } + } + return -1; + } + + /** + * Method used to check if theres an icon present. + * @param player the player. + * @return True if theres an icon present. + */ + public boolean hasIcon(final Player player) { + int count = 0; + for (PrayerType type : player.getPrayer().getActive()) { + if (getIcon(player, type) != -1) { + count++; + } + } + return count != 0; + } + + /** + * Method used to return the type by the button. + * @param button the button. + * @return the type. + */ + public static PrayerType get(int button) { + for (PrayerType type : PrayerType.values()) { + if (type.getButton() == button) { + return type; + } + } + return null; + } + + /** + * Method used to get the melee types. + * @return the types. + */ + public static PrayerType[] getMeleeTypes() { + return getByBonus(Skills.ATTACK, Skills.STRENGTH); + } + + /** + * Method used to get the rage types. + * @return the types. + */ + public static PrayerType[] getRangeTypes() { + return getByBonus(Skills.RANGE); + } + + /** + * Method used to get the magic types. + * @return the types. + */ + public static PrayerType[] getMagicTypes() { + return getByBonus(Skills.MAGIC); + } + + /** + * Method used to get the prayer types by the bonuses. + * @param ids the ids. + * @return the type. + */ + public static PrayerType[] getByBonus(int... ids) { + PrayerType[] types = new PrayerType[values().length]; + int count = 0; + for (PrayerType type : values()) { + for (SkillBonus b : type.getBonuses()) { + for (int i : ids) { + if (i == b.getSkillId()) { + types[count] = type; + count++; + } + } + } + } + return types; + } + + /** + * Gets the secondRestriction. + * @return The secondRestriction. + */ + public PrayerCategory getSecondRestriction() { + return secondRestriction; + } + + /** + * Gets the sound. + * @return The sound. + */ + public Audio getSound() { + return sound; + } + + public int getDefenceReq() { + return defenceReq; + } + +} 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 new file mode 100644 index 000000000..e210f04c4 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/quest/Quest.java @@ -0,0 +1,345 @@ +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; +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. + * @author Vexia + * + */ +@PluginManifest(type = PluginType.QUEST) +public abstract class Quest implements Plugin { + + /** + * Represents the red string. + */ + public static final String RED = ""; + + /** + * Represents the bright red string. + */ + public static final String BRIGHT_RED = ""; + + /** + * Represents the blue string. + */ + public static final String BLUE = ""; + + /** + * Represents the black string. + */ + public static final String BLACK = ""; + + /** + * The constant representing the journal component. + */ + public static final int JOURNAL_COMPONENT = 275; + + /** + * The constant representing the quest reward component. + */ + public static final int REWARD_COMPONENT = 277; + + /** + * The quest as a Quests item (currently only used for the quest's name). + */ + private final Quests quest; + + /** + * The index id of the quest. + */ + private final int index; + + /** + * The button id of the quest. + */ + private final int buttonId; + + /** + * The rewarded quest points. + */ + private final int questPoints; + + /** + * The config values based on stage. + */ + private final int[] 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(Quests quest, int index, int buttonId, int questPoints, int...configs) { + this.quest = quest; + this.index = index; + this.buttonId = buttonId; + this.questPoints = questPoints; + this.configs = configs; + } + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public abstract Quest newInstance(Object object); + + /** + * Starts this quest. + * @param player The player. + */ + public void start(Player player) { + player.getQuestRepository().setStage(this, 10); + player.getQuestRepository().syncronizeTab(player); + } + + /** + * Draws the text on the quest journal. + * @param player The player. + * @param stage The stage to draw. + */ + public void drawJournal(Player player, int stage) { + for (int i = 0; i < 311; i++) { + player.getPacketDispatch().sendString("" , JOURNAL_COMPONENT, i); + } + player.getPacketDispatch().sendString("" + getQuest() + "", JOURNAL_COMPONENT, 2); + + } + + /** + * Finishes the quest. + * @param player The player. + */ + public void finish(Player player) { + 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) { + continue; + } + player.getPacketDispatch().sendString("", 277, i); + } + player.getQuestRepository().setStage(this, 100); + player.getQuestRepository().incrementPoints(getQuestPoints()); + player.getQuestRepository().syncronizeTab(player); + 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 " + getQuest() + " Quest!", 277, 4); + player.getPacketDispatch().sendMessage("Congratulations! Quest complete!"); + 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. + * @param message The message. + * @param line The line. + */ + public void line(Player player, String message, int line) { + String send = BLUE + "" + message.replace("", "

").replace("", BLUE).replace("", RED); + if (send.contains("

") || send.contains("")) { + String[] lines = send.split(send.contains("

") ? "

" : ""); + for (int i = 0; i < lines.length; i++) { + line(player, lines[i].replace("

", "").replace("", ""), line, false); + line++; + } + } else { + send = send.replace("!!", RED).replace("??", BLUE).replace("---", BLACK + "").replace("/--", BLUE).replace("%%", BRIGHT_RED).replace("&&", BLUE); + line(player, send, line, false); + } + } + + /** + * Draws a line on the quest journal component. + * @param player The player. + * @param message The message. + * @param line The line number. + * @param crossed True if the message should be crossed out. + */ + public void line(Player player, String message, int line, final boolean crossed) { + String send; + if(!crossed){ + send = BLUE + "" + message.replace("", "

").replace("", BLUE).replace("", RED); + send = send.replace("!!", RED).replace("??", BLUE).replace("%%", BRIGHT_RED).replace("&&", BLUE); + } else { + 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. + * @param string The string to draw. + * @param line The line number to draw on. + */ + public void drawReward(Player player, final String string, final int line) { + player.getPacketDispatch().sendString(string, REWARD_COMPONENT, line); + } + + /** + * Sets the player instanced stage. + * @param player The player. + * @param stage The stage to set. + */ + public void setStage(Player player, int stage) { + player.getQuestRepository().setStage(this, stage); + } + + /** + * Gets the config id based on the stage. + * @param player The player. + * @param stage The stage. + * @return The config data. + */ + public int[] getConfig(Player player, int stage) { + if (configs.length < 4) { + 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]}; + } + + public void updateVarps(Player player) { + } + + /** + * Checks if the quest is in progress. + * @param player The player. + * @return {@code True} if so. + */ + public boolean isStarted(Player player) { + return getStage(player) > 0 && getStage(player) < 100; + } + + /** + * Checks if the quest is completed. + * @param player The player. + * @return {@code True} if so. + */ + public boolean isCompleted(Player player) { + return getStage(player) >= 100; + } + + /** + * Gets the player instanced stage of this quest. + * @param player The player. + * @return The stage. + */ + public int getStage(Player player) { + return player.getQuestRepository().getStage(this); + } + + /** + * Checks the requirements for the quest. + * @param player The player + * @return {@code True} if so. + */ + public boolean hasRequirements(Player player) { + return true; + } + + /** + * Gets the name. + * @return the name. + */ + public Quests getQuest() { + return quest; + } + + /** + * Gets the index. + * @return the index. + */ + public int getIndex() { + return index; + } + + /** + * Gets the buttonId. + * @return the buttonId. + */ + public int getButtonId() { + return buttonId; + } + + /** + * Gets the questPoints. + * @return the questPoints. + */ + public int getQuestPoints() { + return questPoints; + } + + @Override + public String toString() { + 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 new file mode 100644 index 000000000..02ccf6b29 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/quest/QuestRepository.java @@ -0,0 +1,298 @@ +package core.game.node.entity.player.link.quest; + +import content.data.Quests; +import core.game.node.entity.player.Player; + +import core.tools.Log; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +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. + * + * @author Vexia + */ +public final class QuestRepository { + + /** + * The static mapping of instanced quests. + */ + private static final Map QUESTS = new TreeMap<>(); + + /** + * The mapping of quest indexes with related stages. + */ + private final Map quests = new HashMap<>(); + + /** + * The player instance for this manager. + */ + private final Player player; + + /** + * The current syncronized accumulated quest points. + */ + private int points; + + /** + * Constructs a new {@code QuestRepository} {@code Object}. + * + * @param player the player. + */ + public QuestRepository(final Player player) { + this.player = player; + for (Quest quest : QUESTS.values()) { + quests.put(quest.getIndex(), 0); + } + } + + public void parse(JSONObject questData){ + points = Integer.parseInt( questData.get("points").toString()); + JSONArray questArray = (JSONArray) questData.get("questStages"); + questArray.forEach(quest -> { + JSONObject q = (JSONObject) quest; + quests.put(Integer.parseInt( q.get("questId").toString()),Integer.parseInt(q.get("questStage").toString())); + }); + syncPoints(); + } + + /** + * Synchronizes the quest tab. + * + * @param player The player. + */ + public void syncronizeTab(Player player) { + setVarp(player, 101, points); + int[] config = null; + 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); + } + } + + /** + * Sets the stage of a quest. + * + * @param quest The quest. + * @param stage The stage. + */ + public void setStage(Quest quest, int stage) { + int oldStage = getStage(quest); + if(oldStage < stage) { + quests.put(quest.getIndex(), stage); + } else { + log(this.getClass(), Log.WARN, String.format("Nonmonotonic QuestRepository.setStage call for player \"%s\", quest \"%s\", old stage %d, new stage %d", player.getName(), quest.getQuest(), oldStage, stage)); + } + } + + /** + * Sets the stage of a quest, permitting non-monotonic updates. + * + * @param quest The quest. + * @param stage The stage. + */ + public void setStageNonmonotonic(Quest quest, int stage) { + quests.put(quest.getIndex(), stage); + } + + /** + * Increments the obtained points by the value. + * + * @param value the value. + */ + public void incrementPoints(int value) { + points += value; + } + + /** + * Decrease the points by the value. + * + * @param value the value. + */ + public void dockPoints(int value) { points -= value; } + + /** + * Syncronizes the quest points. + */ + public void syncPoints() { + int points = 0; + for (Quest quest : QUESTS.values()) { + if (getStage(quest) >= 100) { + points += quest.getQuestPoints(); + } + } + this.points = points; + } + + /** + * Gets the available quest points. + * + * @return The availble quest points. + */ + public int getAvailablePoints() { + int points = 0; + for (Quest quest : QUESTS.values()) { + points += quest.getQuestPoints(); + } + return points; + } + + /** + * Gets the quest for the button id. + * + * @param buttonId The button id. + * @return The quest. + */ + public Quest forButtonId(int buttonId) { + for (Quest q : QUESTS.values()) { + if (q.getButtonId() == buttonId) { + return q; + } + } + return null; + } + + /** + * Gets the quest for the index. + * + * @param index The index. + * @return The quest. + */ + public Quest forIndex(int index) { + for (Quest q : QUESTS.values()) { + if (q.getIndex() == index) { + return q; + } + } + return null; + } + + /** + * Checks if all quests are completed. + * + * @return {@code True} if so. + */ + public boolean hasCompletedAll() { + return getPoints() >= getAvailablePoints(); + } + + /** + * Checks if the quest is complete. + * + * @param quest The quest. + * @return {@code True} if so. + */ + 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 theQuest.getStage(player) >= 100; + } + + /** + * Checks if the quest has at least started. + * + * @param quest The quest by id. + * @return {@code True} if so. + */ + public boolean hasStarted(Quests quest) { + Quest theQuest = getQuest(quest); + if (quest == null) { + log(this.getClass(), Log.ERR, "Error can't check if quest is complete for " + quest); + return false; + } + return theQuest.getStage(player) > 0; + } + + + /** + * Gets the stage of quest by id. + * @param quest The quest. + * @return The stage. + */ + public int getStage(Quests quest) { + var theQuest = QUESTS.get(quest); + if (theQuest == null) { + return 0; + } + return getStage(theQuest); + } + + /** + * Gets the stage of a quest. + * @param quest The quest. + * @return The stage. + */ + public int getStage(Quest quest) { + return quests.get(quest.getIndex()); + } + + /** + * Gets the quest by id. + * @param quest The quest. + * @return The quest. + */ + public Quest getQuest(Quests quest) { + return QUESTS.get(quest); + } + + /** + * Gets the points. + * + * @return the points. + */ + public int getPoints() { + return points; + } + + /** + * Gets the player. + * + * @return The player. + */ + public Player getPlayer() { + return player; + } + + /** + * Registers the quest to the repository. + * + * @param quest The quest. + */ + public static void register(Quest quest) { + QUESTS.put(quest.getQuest(), quest); + } + + /** + * Gets the quest repository. + * + * @return the quests. + */ + public static Map getQuests() { + return QUESTS; + } + + public Map getQuestList() {return quests;} + +} diff --git a/Server/src/main/core/game/node/entity/player/link/request/RequestManager.kt b/Server/src/main/core/game/node/entity/player/link/request/RequestManager.kt new file mode 100644 index 000000000..73e958863 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/request/RequestManager.kt @@ -0,0 +1,113 @@ +package core.game.node.entity.player.link.request + +import core.game.node.entity.player.Player +import core.game.bots.AIRepository +import content.global.bots.DoublingMoney +import core.game.world.GameWorld.ticks + +/** + * Represents a managing class for requests of a player. + * @author Vexia + * @author dginovker + * @version 2.0 + */ +class RequestManager(val player: Player) { + /** + * Represents the target being requested. + */ + var target: Player? = null + private set + + /** + * Method used to send a request type to a target. + * @param target the target. + * @param type the type of request. + * @return `True` if successful. + */ + fun request(target: Player, type: RequestType): Boolean { + if (!canRequest(type, target)) { + return false + } + if (acceptExisting(target, type)) { + return true + } + player.packetDispatch.sendMessage(type.message) + target.packetDispatch.sendMessage(type.getRequestMessage(player)) + player.setAttribute("lastRequest", type) + this.target = target + + // If the target is a bot, get their bot manager to handle the request. + if (target.isArtificial) { + val bot = AIRepository.PulseRepository[target.username.lowercase()] + if (bot != null && bot.botScript is DoublingMoney) { + bot.botScript.tradeReceived(player) + } + } + return true + } + + /** + * Method used to check if a player can continue with a request. + * @param type the type. + * @param target the target. + * @return `True` if so. + */ + private fun canRequest(type: RequestType, target: Player): Boolean { + if (target === player) { + return false + } + if (!target.location.withinDistance(player.location, 15)) { + player.packetDispatch.sendMessage("Unable to find " + target.username + ".") + return false + } + if (!target.isActive || target.interfaceManager.isOpened) { + player.packetDispatch.sendMessage("Other player is busy at the moment.") + return false + } + if (target.getAttribute("busy", 0) > ticks || player.getAttribute("busy", 0) > ticks) { + player.packetDispatch.sendMessage("Other player is busy at the moment.") + return false + } + return if (!player.zoneMonitor.canRequest(type, target)) { + false + } else type.canRequest(player, target) + } + + /** + * Method used to check if we can accept an existing request. + * @param target the target. + * @param type the type. + * @return `True` if so. + */ + private fun acceptExisting(target: Player, type: RequestType): Boolean { + val lastType = target.getAttribute("lastRequest", null) + if (lastType === type && player === target.requestManager.target) { + close(player) + clear() + target.requestManager.clear() + player.setAttribute("busy", ticks + 2) + target.setAttribute("busy", ticks + 2) + type.module.open(player, target) + return true + } + close(player) + return false + } + + /** + * Closes the components for the player. + * @param player the player. + */ + private fun close(player: Player) { + player.dialogueInterpreter.close() + player.interfaceManager.close() + player.interfaceManager.closeChatbox() + } + + /** + * Method used to clear the cached target. + */ + fun clear() { + target = null + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/player/link/request/RequestModule.kt b/Server/src/main/core/game/node/entity/player/link/request/RequestModule.kt new file mode 100644 index 000000000..4e1705157 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/request/RequestModule.kt @@ -0,0 +1,18 @@ +package core.game.node.entity.player.link.request + +import core.game.node.entity.player.Player + +/** + * Represents the module used for all request types. + * @author 'Vexia + * @author dginovker + * @version 2.0 + */ +interface RequestModule { + /** + * Method invoked when the targeting player accepts a request. + * @param player the player. + * @param target the target. + */ + fun open(player: Player?, target: Player?) +} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/player/link/request/RequestType.kt b/Server/src/main/core/game/node/entity/player/link/request/RequestType.kt new file mode 100644 index 000000000..14f7189e3 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/request/RequestType.kt @@ -0,0 +1,63 @@ +package core.game.node.entity.player.link.request + +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.request.assist.AssistSession +import core.game.node.entity.player.link.request.trade.TradeModule + +/** + * Represents a request type. + * @author 'Vexia + * @author Emperor + * @author dginovker + * @version 2.0 + */ +open class RequestType +/** + * Constructs a new `RequestManager { Object}. + * message the message. + * requestMessage the requesting message.` + */( + /** + * Represents the message to send for the player when requesting. + */ + val message: String, + /** + * Represents the requesting message type. + */ + val requestMessage: String, + /** + * Represents the module used for this type of requesting. + */ + val module: RequestModule +) { + /** + * Checks if the request can be made. + * @param player The player. + * @param target The target. + * @return `True` if so. + */ + open fun canRequest(player: Player?, target: Player?): Boolean { + return true + } + + /** + * Gets the requesting message formated with the targets name. + * @param target the target. + * @return the message to send. + */ + fun getRequestMessage(target: Player): String { + return target.username + requestMessage + } + + companion object { + /** + * The trade request type. + */ + val TRADE = RequestType("Sending a trade offer...", ":tradereq:", TradeModule(null, null)) + + /** + * The assist request type. + */ + val ASSIST = RequestType("Sending assistance request...", ":assistreq:", AssistSession()) + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/assist/AssistSession.java b/Server/src/main/core/game/node/entity/player/link/request/assist/AssistSession.java similarity index 98% rename from Server/src/main/java/core/game/node/entity/player/link/request/assist/AssistSession.java rename to Server/src/main/core/game/node/entity/player/link/request/assist/AssistSession.java index e596895ad..bf82b6548 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/request/assist/AssistSession.java +++ b/Server/src/main/core/game/node/entity/player/link/request/assist/AssistSession.java @@ -6,13 +6,16 @@ import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.request.RequestModule; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; 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/java/core/game/node/entity/player/link/request/trade/TradeCloseEvent.java b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeCloseEvent.java similarity index 95% rename from Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeCloseEvent.java rename to Server/src/main/core/game/node/entity/player/link/request/trade/TradeCloseEvent.java index 78b1aa8b0..e2eb3a095 100644 --- a/Server/src/main/java/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/java/core/game/node/entity/player/link/request/trade/TradeContainer.java b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java similarity index 96% rename from Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeContainer.java rename to Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java index 0a62d001b..22fab5af3 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeContainer.java +++ b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeContainer.java @@ -1,17 +1,18 @@ package core.game.node.entity.player.link.request.trade; -import api.ContentAPI; +import static core.api.ContentAPIKt.*; +import static core.tools.GlobalsKt.colorize; + import core.cache.def.impl.ItemDefinition; import core.game.container.*; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; import core.net.packet.out.ContainerPacket; -import static rs09.tools.stringtools.GlobalsKt.colorize; /** * Represents the container during a trade session. @@ -160,14 +161,15 @@ public final class TradeContainer extends Container { String targetMac = target.getDetails().getMacAddress(); String playerHost = player.getDetails().getCompName(); String targetHost = target.getDetails().getCompName(); - if (item.getId() == 11174 || item.getId() == 11173 || item.getId() == 759) { + // Ironman trading exceptions for Shield of Arrav and Heroes Quest + if (item.getId() == 11174 || item.getId() == 11173 || item.getId() == 759 || item.getId() == 1586 || item.getId() == 1577) { return true; } if (player.getIronmanManager().isIronman() || target != null && target.getIronmanManager().isIronman()) { return false; } if ((playerIsBot || targetIsBot) && (playerIP.equals(targetIP) || playerMac.equals(targetMac) || playerHost.equals(targetHost))){ - ContentAPI.sendMessage(player, colorize("%RYou can not trade items with your own bot accounts.")); + sendMessage(player, colorize("%RYou can not trade items with your own bot accounts.")); return false; } if (item.getName().equals("Coins") && item.getId() != 995) { 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 new file mode 100644 index 000000000..3d0c84483 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeModule.kt @@ -0,0 +1,515 @@ +package core.game.node.entity.player.link.request.trade + +import core.game.component.Component +import core.game.container.Container +import core.game.container.ContainerType +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.request.RequestModule +import core.game.node.item.GroundItemManager +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.* + +/** + * Represents the module use to handle a request between trading of two players. + * @author Vexia + * @author dginovker + * @version 2.0 + */ +class TradeModule +/** + * Constructs a new `TradeModule` `Object`. + * @param player the player. + * @param target the target. + */(player: Player?, target: Player?) : RequestModule { + /** + * If the container has been retained. + */ + var isRetained = false + /** + * Represents the player instance. + */ + var player: Player? = player + private set + /** + * Represents the target instance. + */ + var target: Player? = target + private set + /** + * Represents the container of this session. + */ + var container: TradeContainer? = null + private set + /** + * Represents if this session has accepted. + */ + var isAccepted = false + private set + /** + * Represents if the trade is modified. + */ + var isModified = false + /** + * Represents the stage of the trade(0=started, 1=second accept) + */ + var stage = 0 + private set + + override fun open(player: Player?, target: Player?) { + extend(player, target) + if (getExtension(target) == null || getExtension(player) == null) { + return + } + getExtension(player)!!.openInterface(getInterface(stage)).display(stage) + if (getExtension(target) == null || getExtension(player) == null) { + return + } + getExtension(target)!!.openInterface(getInterface(stage)).display(stage) + player!!.dialogueInterpreter.close() + target!!.dialogueInterpreter.close() + } + + /** + * Method used to update the trade interfaces. + */ + fun update() { + display(stage) + getExtension(target)!!.display(stage) + } + + /** + * Method used to open an interface at a stage. + * @param component the component. + * @return the module instance for chaining. + */ + private fun openInterface(component: Component): TradeModule { + return if (component === MAIN_INTERFACE) openMain() else openSecond() + } + + /** + * Gets the accepting message to display. + * @return the message. + */ + private val acceptMessage: String + get() { + val otherAccept = getExtension(target)!!.isAccepted + return if (!otherAccept && !isAccepted) "" else if (otherAccept) "Other player has accepted." else "Waiting for other player..." + } + + /** + * Method used to display the interface display depending on stage. + * @param stage the stage. + * @return module the module instance for chaining. + */ + private fun display(stage: Int): TradeModule { + when (stage) { + 0 -> { + for (i in HIDDEN_CHILDS) { + player!!.packetDispatch.sendString("", MAIN_INTERFACE.id, i) + } + player!!.packetDispatch.sendString("Trading With: " + target!!.username, 335, 15) + player!!.packetDispatch.sendString( + target!!.username + " has " + (if (target!!.inventory.freeSlots() == 0) "no" else target!!.inventory.freeSlots()) + " free inventory slots.", + 335, + 21 + ) + player!!.packetDispatch.sendString(acceptMessage, 335, 36) + } + + 1 -> { + player!!.packetDispatch.sendString( + "Trading with:
" + "" + target!!.username.substring( + 0, + 1 + ).uppercase(Locale.getDefault()) + target!!.username.substring(1), 334, 2 + ) + val acceptMessage = acceptMessage + if (acceptMessage !== "") { + player!!.packetDispatch.sendString(acceptMessage, 334, 33) + } + player!!.interfaceManager.restoreTabs() + } + } + displayModified(stage) + container!!.update(true) + return this + } + + /** + * Gets the interface for the current stage. + * @param stage the stage, defaults to this TradeModule's stage. + * @return the component. + */ + fun getInterface(stage: Int = this.stage): Component { + return if (stage == 0) MAIN_INTERFACE else ACCEPT_INTERFACE + } + + /** + * Gets the interface for the TradeModule stage + * @return the component + */ + fun getInterface(): Component { + return getInterface(stage) + } + + /** + * Method used to display red text at the top of the trade window when the trade has been modified + * (Red text appears at the bottom in second trade screen) + * @param stage the sytage. + */ + private fun displayModified(stage: Int) { + val otherModified = getExtension(target)!!.isModified + if (stage == 0) { + // Main interface + if (otherModified) { + setVarp(player!!, 1043, 1) + } + if (isModified) { + setVarp(player!!, 1042, 1) + } + } else { + // Accept interface + player!!.packetDispatch.sendInterfaceConfig(334, 45, !isModified) + player!!.packetDispatch.sendInterfaceConfig(334, 46, !otherModified) + } + } + + /** + * Method used to display flashing alert if the trade has been modified. + * @param slot the slot. + */ + fun alert(slot: Int) { + player!!.packetDispatch.sendRunScript(143, "Iiii", *arrayOf(slot, 7, 4, 21954594)) + target!!.packetDispatch.sendRunScript(143, "Iiii", *arrayOf(slot, 7, 4, 21954593)) + } + + + /** + * Method used to decline this offer. + */ + fun decline() { + player!!.interfaceManager.close() + target!!.packetDispatch.sendMessage("Other player has declined trade!") + } + + /** + * Method used to check if the next stage can proceed. + */ + private fun nextStage() { + if (isAccepted && getExtension(target)!!.isAccepted) { + if (stage == 0) { + if (!hasSpace()) { + return + } + incrementStage() + openInterface(getInterface(stage)) + getExtension(target)!!.incrementStage() + getExtension(target)!!.openInterface(getInterface(stage)) + getExtension(target)!!.setAccepted(false, false) + setAccepted(false, false) + } else { + giveContainers(this) + incrementStage() + getExtension(target)!!.incrementStage() + getExtension(target)!!.setAccepted(false, false) + setAccepted(false, false) + player!!.interfaceManager.close() + return + } + } + update() + } + + /** + * Method used to open the main interface. + * @return the module instance for chaining. + */ + private fun openMain(): TradeModule { + player!!.interfaceManager.closeDefaultTabs() + player!!.interfaceManager.open(MAIN_INTERFACE) + player!!.interfaceManager.openSingleTab(OVERLAY_INTERFACE) + player!!.inventory.refresh() + player!!.packetDispatch.sendIfaceSettings(1278, 30, 335, 0, 27) + player!!.packetDispatch.sendIfaceSettings(1026, 32, 335, 0, 27) + player!!.packetDispatch.sendIfaceSettings(1278, 0, 336, 0, 27) + player!!.packetDispatch.sendIfaceSettings(2360446, 0, 335, 0, 27) + player!!.packetDispatch.sendRunScript(150, "IviiiIsssssssss", *INVENTORY_PARAMS) + player!!.packetDispatch.sendRunScript(150, "IviiiIsssssssss", *TRADE_PARAMS) + player!!.packetDispatch.sendRunScript(695, "IviiiIsssssssss", *PARTENER_PARAMS) + return this + } + + /** + * Method used to open the second interface. + * @return the module instance for chaining. + */ + private fun openSecond(): TradeModule { + player!!.interfaceManager.open(ACCEPT_INTERFACE) + player!!.interfaceManager.closeSingleTab() + displayOffer(container, 37) + displayOffer(getExtension(target)!!.container, 41) + return this + } + + /** + * Method used to set the value of accepted. + * @param accept whether the trade was accepted or declined + */ + fun setAccepted(accept: Boolean, update: Boolean) { + isAccepted = accept + if (update) { + nextStage() + } + } + + /** + * Method used to display an offer. + * @param container the container. + */ + private fun displayOffer(container: Container?, child: Int) { + val split = container!!.itemCount() > 14 + if (split) { + player!!.packetDispatch.sendInterfaceConfig(334, child + 1, false) + player!!.packetDispatch.sendInterfaceConfig(334, child + 2, false) + val messages = arrayOf( + getDisplayMessage(splitList(container.toArray(), 0, 14)), getDisplayMessage( + splitList( + container.toArray(), 14, container.toArray().size + ) + ) + ) + player!!.packetDispatch.sendString(messages[0], 334, child + 1) + player!!.packetDispatch.sendString(messages[1], 334, child + 2) + } else { + player!!.packetDispatch.sendInterfaceConfig(334, child, false) + player!!.packetDispatch.sendString(getDisplayMessage(container.toArray()), 334, child) + } + } + + /** + * Method used to get the display message. + * @param items the items. + * @return the message. + */ + private fun getDisplayMessage(items: Array): String { + var message = "Absolutely nothing!" + if (items.isNotEmpty()) { + message = "" + for (i in items.indices) { + if (items[i] == null) { + continue + } + message = message + "" + items[i]!!.name + if (items[i]!!.amount > 1) { + message = "$message x " + message = message + "" + getFormattedNumber(items[i]!!.amount) + "
" + } else { + message = "$message
" + } + } + } + return message + } + + /** + * Method used to check if the players & targets have enough space. + * @return `True` if so. + */ + private fun hasSpace(): Boolean { + var hasSpace = true + if (!player!!.inventory.hasSpaceFor(getExtension(target)!!.container)) { + target!!.packetDispatch.sendMessage("Other player doesn't have enough space in their inventory for this trade.") + player!!.packetDispatch.sendMessage("You don't have enough inventory space for this.") + hasSpace = false + } else if (!target!!.inventory.hasSpaceFor(container)) { + player!!.packetDispatch.sendMessage("Other player doesn't have enough space in their inventory for this trade.") + target!!.packetDispatch.sendMessage("You don't have enough inventory space for this.") + hasSpace = false + } + if (!hasSpace) { + setAccepted(false, true) + getExtension(target)!!.setAccepted(false, true) + } + return hasSpace + } + + /** + * Gets the formatted number. + * @param amount the amount. + * @return the formatted number. + */ + private fun getFormattedNumber(amount: Int): String { + return DecimalFormat("#,###,##0").format(amount.toLong()).toString() + } + + /** + * Method used to give the containers to each participants. + * @param module the module. + */ + private fun giveContainers(module: TradeModule) { + val pContainer: Container = module.container ?: return + val oContainer: Container = getExtension(module.target)!!.container ?: return + + PlayerMonitor.logTrade(module.player!!, module.target!!, pContainer, oContainer) + + (AIRepository.PulseRepository[module.player!!.username.lowercase()]?.botScript as DoublingMoney?)?.itemsReceived(module.target!!, oContainer) + (AIRepository.PulseRepository[module.target!!.username.lowercase()]?.botScript as DoublingMoney?)?.itemsReceived(module.player!!, pContainer) + + addContainer(module.player, oContainer) + addContainer(module.target, pContainer) + module.target!!.packetDispatch.sendMessage("Accepted trade.") + module.player!!.packetDispatch.sendMessage("Accepted trade.") + } + + /** + * Method used to add a container for a player. + * @param player the player who loses the items. + * @param target the target who gains the items. + * @param container the container. + */ + private fun addContainer(player: Player?, container: Container?) { + val c = Container(container!!.itemCount(), ContainerType.ALWAYS_STACK) + c.addAll(container) + for (i in container.toArray()) { + if (i == null) { + continue + } + if (i.amount > player!!.inventory.getMaximumAdd(i)) { + i.amount = player.inventory.getMaximumAdd(i) + } + if (!player.inventory.add(i)) { + GroundItemManager.create(i, player) + } + } + } + + /** + * Gets the split list as an array. + * @param items the items. + * @param min the min. + * @param max the max. + * @return the split item array. + */ + private fun splitList(items: Array, min: Int, max: Int): Array { + val list: MutableList = ArrayList(20) + for (i in min until max) { + if (items[i] == null) { + continue + } + list.add(items[i]) + } + val array = arrayOfNulls(list.size) + for (i in list.indices) { + if (list[i] == null) { + continue + } + array[i] = list[i] + } + return array + } + + /** + * Method used to increment the stage. + */ + fun incrementStage() { + stage++ + } + + companion object { + /** + * Represents the overlay interface component. + */ + val OVERLAY_INTERFACE = Component(336) + + /** + * Represents the main interface component. + */ + val MAIN_INTERFACE: Component = Component(335).setCloseEvent(TradeCloseEvent()) + + /** + * Represents the "accept" interface component. + */ + val ACCEPT_INTERFACE: Component = Component(334).setCloseEvent(TradeCloseEvent()) + + /** + * The inventory params for the runscript. + */ + val INVENTORY_PARAMS = arrayOf( + "", + "", + "", + "Lend", + "Offer-X", + "Offer-All", + "Offer-10", + "Offer-5", + "Offer", + -1, + 0, + 7, + 4, + 93, + 336 shl 16 + ) + + /** + * The trade params for the run script. + */ + val TRADE_PARAMS = arrayOf( + "", + "", + "", + "", + "Remove-X", + "Remove-All", + "Remove-10", + "Remove-5", + "Remove", + -1, + 0, + 7, + 4, + 90, + 335 shl 16 or 30 + ) + + /** + * The partener params for the run script. + */ + val PARTENER_PARAMS = arrayOf("", "", "", "", "", "", "", "", "", -1, 0, 7, 4, 91, 335 shl 16 or 32) + + /** + * Represents the childs that should be hidden. + */ + val HIDDEN_CHILDS = intArrayOf(42, 43, 44, 42, 44, 40, 41) + + /** + * Method used to extend the module for each participant. + * @param player the player. + * @param target the target. + */ + fun extend(player: Player?, target: Player?) { + player!!.addExtension(TradeModule::class.java, TradeModule(player, target)) + target!!.addExtension(TradeModule::class.java, TradeModule(target, player)) + } + + /** + * Method used to get the extension from the player. + * @param player the player. + * @return the module instance. + */ + @JvmStatic + fun getExtension(player: Player?): TradeModule? { + return player!!.getExtension(TradeModule::class.java) + } + } + + init { + container = TradeContainer(player) + } +} diff --git a/Server/src/main/java/core/game/node/entity/player/link/skillertasks/Difficulty.java b/Server/src/main/core/game/node/entity/player/link/skillertasks/Difficulty.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/skillertasks/Difficulty.java rename to Server/src/main/core/game/node/entity/player/link/skillertasks/Difficulty.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/skillertasks/SkillTasks.java b/Server/src/main/core/game/node/entity/player/link/skillertasks/SkillTasks.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/skillertasks/SkillTasks.java rename to Server/src/main/core/game/node/entity/player/link/skillertasks/SkillTasks.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/skillertasks/SkillerTasks.java b/Server/src/main/core/game/node/entity/player/link/skillertasks/SkillerTasks.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/skillertasks/SkillerTasks.java rename to Server/src/main/core/game/node/entity/player/link/skillertasks/SkillerTasks.java diff --git a/Server/src/main/java/core/game/node/entity/player/link/spawn/PKPackage.java b/Server/src/main/core/game/node/entity/player/link/spawn/PKPackage.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/player/link/spawn/PKPackage.java rename to Server/src/main/core/game/node/entity/player/link/spawn/PKPackage.java diff --git a/Server/src/main/java/core/game/node/entity/skill/LevelUp.java b/Server/src/main/core/game/node/entity/skill/LevelUp.java similarity index 87% rename from Server/src/main/java/core/game/node/entity/skill/LevelUp.java rename to Server/src/main/core/game/node/entity/skill/LevelUp.java index 3d72294c4..c9e23d8ea 100644 --- a/Server/src/main/java/core/game/node/entity/skill/LevelUp.java +++ b/Server/src/main/core/game/node/entity/skill/LevelUp.java @@ -1,26 +1,24 @@ package core.game.node.entity.skill; import core.game.component.Component; -import core.game.content.global.Skillcape; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; +import core.game.global.Skillcape; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.emote.Emotes; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; 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. */ @@ -113,16 +111,10 @@ public final class LevelUp { * @param amount The amount of levels gained. */ public static void levelup(Player player, int slot, int amount) { - if (player.getAttribute("tut-island") != null) { + if (!player.getAttribute("tutorial:complete", false)) { return; } - if (TutorialSession.getExtension(player).getStage() < TutorialSession.MAX_STAGE) { - 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); @@ -132,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 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); @@ -169,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); } /** @@ -188,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); } /** @@ -197,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/java/core/game/node/entity/skill/SkillBonus.java b/Server/src/main/core/game/node/entity/skill/SkillBonus.java similarity index 100% rename from Server/src/main/java/core/game/node/entity/skill/SkillBonus.java rename to Server/src/main/core/game/node/entity/skill/SkillBonus.java diff --git a/Server/src/main/java/core/game/node/entity/skill/SkillPulse.java b/Server/src/main/core/game/node/entity/skill/SkillPulse.java similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/SkillPulse.java rename to Server/src/main/core/game/node/entity/skill/SkillPulse.java index 2d3dc2922..4f349b92d 100644 --- a/Server/src/main/java/core/game/node/entity/skill/SkillPulse.java +++ b/Server/src/main/core/game/node/entity/skill/SkillPulse.java @@ -1,7 +1,7 @@ package core.game.node.entity.skill; -import core.game.node.entity.skill.gather.SkillingResource; -import core.game.node.entity.skill.gather.SkillingTool; +import content.global.skill.gather.SkillingResource; +import content.data.skill.SkillingTool; import core.game.node.Node; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; diff --git a/Server/src/main/java/core/game/node/entity/skill/Skills.java b/Server/src/main/core/game/node/entity/skill/Skills.java similarity index 84% rename from Server/src/main/java/core/game/node/entity/skill/Skills.java rename to Server/src/main/core/game/node/entity/skill/Skills.java index 6835a1f11..06a978589 100644 --- a/Server/src/main/java/core/game/node/entity/skill/Skills.java +++ b/Server/src/main/core/game/node/entity/skill/Skills.java @@ -1,27 +1,32 @@ package core.game.node.entity.skill; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.interaction.item.brawling_gloves.BrawlingGloves; +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 rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import rs09.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; @@ -34,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. @@ -66,6 +71,10 @@ public final class Skills { */ private final double[] experience; + private double[] lastUpdateXp = null; + + private int lastUpdate = GameWorld.getTicks(); + /** * An array containing all the maximum levels. */ @@ -96,11 +105,6 @@ public final class Skills { */ private double experienceGained = 0; - /** - * The restoration pulses. - */ - private final SkillRestoration[] restoration; - /** * If a lifepoints update should occur. */ @@ -116,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. @@ -125,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; @@ -155,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); - } - } } /** @@ -173,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); } /** @@ -210,6 +194,8 @@ public final class Skills { * @param experience The experience. */ public void addExperience(int slot, double experience, boolean playerMod) { + if (lastUpdateXp == null) + lastUpdateXp = this.experience.clone(); double mod = getExperienceMod(slot, experience, playerMod, true); final Player player = entity instanceof Player ? ((Player) entity) : null; final AssistSession assist = entity.getExtension(AssistSession.class); @@ -219,14 +205,15 @@ public final class Skills { boolean already200m = this.experience[slot] == 200000000; double experienceAdd = (experience * mod); //check if a player has brawling gloves and, if equipped, modify xp - if(!player.getBrawlingGlovesManager().GloveCharges.isEmpty()){ + BrawlingGlovesManager bgManager = BrawlingGlovesManager.getInstance(player); + if(!bgManager.GloveCharges.isEmpty()){ Item gloves = BrawlingGloves.forSkill(slot) == null ? null : new Item(BrawlingGloves.forSkill(slot).getId()); if(gloves == null && (slot == Skills.STRENGTH || slot == Skills.DEFENCE)){ gloves = new Item (BrawlingGloves.forSkill(Skills.ATTACK).getId()); } if(gloves != null && player.getEquipment().containsItem(gloves)){ - experienceAdd += experienceAdd * player.getBrawlingGlovesManager().getExperienceBonus(); - player.getBrawlingGlovesManager().updateCharges(gloves.getId(),1); + experienceAdd += experienceAdd * bgManager.getExperienceBonus(); + bgManager.updateCharges(gloves.getId(),1); } } //Check for Flame Gloves and Ring of Fire @@ -247,7 +234,7 @@ public final class Skills { this.experience[slot] = 200000000; } if (entity instanceof Player && this.experience[slot] > 175) { - if (player.getSavedData().getGlobalData().getTutorialStage() < TutorialSession.MAX_STAGE && slot != HITPOINTS) { + if (!player.getAttribute("tutorial:complete",false) && slot != HITPOINTS) { this.experience[slot] = 175; } } @@ -263,19 +250,31 @@ public final class Skills { lifepoints += amount; } staticLevels[slot] = newLevel; - if(newLevel == 99 && !player.isArtificial()){ - Repository.sendNews(entity.asPlayer().getUsername() + " has just achieved level 99 " + SKILL_NAME[slot]); - } + 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) { PacketRepository.send(SkillLevel.class, new SkillContext((Player) entity, slot)); + entity.dispatch(new XPGainEvent(slot, experienceAdd)); } + if (GameWorld.getTicks() - lastUpdate >= 200) { + ArrayList> diffs = new ArrayList<>(); + for (int i = 0; i < this.experience.length; i++) { + double diff = this.experience[i] - lastUpdateXp[i]; + if (diff != 0.0) { + diffs.add(new Pair<>(i, diff)); + } + } + PlayerMonitor.logXpGains(player, diffs); + lastUpdateXp = this.experience.clone(); + lastUpdate = GameWorld.getTicks(); + } + lastTrainedSkill = slot; + lastXpGain = getWorldTicks(); } /** @@ -288,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; } @@ -342,7 +341,7 @@ public final class Skills { * Gets the highest combat skill id. * @return The id of the highest combat skill. */ - public int getHighestCombatSkill() { + public int getHighestCombatSkillId() { int id = 0; int last = 0; for (int i = 0; i < 5; i++) { @@ -362,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); @@ -413,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. */ @@ -545,8 +506,8 @@ public final class Skills { double base = 0.25 * (staticLevels[DEFENCE] + staticLevels[HITPOINTS] + floor(0.5 * staticLevels[PRAYER])); double meleeBase = 0.325 * (staticLevels[ATTACK] + staticLevels[STRENGTH]); - double rangeBase = 0.325 * (floor(staticLevels[RANGE] / 2.0) * 1.5); - double magicBase = 0.325 * (floor(staticLevels[MAGIC] / 2.0) * 1.5); + double rangeBase = 0.325 * (floor(staticLevels[RANGE] / 2.0) * 3.0); + double magicBase = 0.325 * (floor(staticLevels[MAGIC] / 2.0) * 3.0); return (int) (base + max(meleeBase, max(rangeBase, magicBase))); } @@ -603,13 +564,13 @@ public final class Skills { } else if (slot == PRAYER) { prayerPoints = level; } + + int previousLevel = dynamicLevels[slot]; dynamicLevels[slot] = level; - if (restoration[slot] != null) { - restoration[slot].restartHpSummPray(false); - restoration[slot].restartStat(false); - } + if (entity instanceof Player) { PacketRepository.send(SkillLevel.class, new SkillContext((Player) entity, slot)); + entity.dispatch(new DynamicSkillLevelChangeEvent(slot, previousLevel, level)); } } @@ -627,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); @@ -818,10 +779,6 @@ public final class Skills { } else if (amount > 0 && level > maximum) { dynamicLevels[skill] = maximum; } - if (restoration[skill] != null) { - restoration[skill].restartHpSummPray(false); - restoration[skill].restartStat(false); - } if (entity instanceof Player) { PacketRepository.send(SkillLevel.class, new SkillContext((Player) entity, skill)); } @@ -863,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++; } @@ -992,4 +941,4 @@ public final class Skills { public int[] getDynamicLevels() { return dynamicLevels; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/node/item/ChanceItem.java b/Server/src/main/core/game/node/item/ChanceItem.java similarity index 100% rename from Server/src/main/java/core/game/node/item/ChanceItem.java rename to Server/src/main/core/game/node/item/ChanceItem.java diff --git a/Server/src/main/java/core/game/node/item/GroundItem.java b/Server/src/main/core/game/node/item/GroundItem.java similarity index 86% rename from Server/src/main/java/core/game/node/item/GroundItem.java rename to Server/src/main/core/game/node/item/GroundItem.java index c67501267..ba5c4e272 100644 --- a/Server/src/main/java/core/game/node/item/GroundItem.java +++ b/Server/src/main/core/game/node/item/GroundItem.java @@ -1,7 +1,7 @@ package core.game.node.item; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; /** @@ -15,6 +15,8 @@ public class GroundItem extends Item { */ private Player dropper; + private int dropperUid; + /** * The amount of ticks. */ @@ -35,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 @@ -62,6 +66,12 @@ public class GroundItem extends Item { this(item, location, 200, player); } + public GroundItem(Item item, Location location, int playerUid, int ticks) { + this(item, location); + this.dropperUid = playerUid; + this.decayTime = ticks; + } + /** * Constructs a new {@code GroundItem} {@code Object}. * @param item The item. @@ -72,8 +82,9 @@ 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(); this.decayTime = ticks + decay; } @@ -90,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 @@ -104,7 +115,7 @@ public class GroundItem extends Item { * @return {@code True} if so. */ public boolean droppedBy(Player p) { - if (dropper != null && p.getDetails().getUid() == dropper.getDetails().getUid()) { + if (p.getDetails().getUid() == dropperUid) { dropper = p; return true; } @@ -191,6 +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/java/core/game/node/item/GroundItemManager.java b/Server/src/main/core/game/node/item/GroundItemManager.java similarity index 95% rename from Server/src/main/java/core/game/node/item/GroundItemManager.java rename to Server/src/main/core/game/node/item/GroundItemManager.java index fc5d797ad..464dbbe25 100644 --- a/Server/src/main/java/core/game/node/item/GroundItemManager.java +++ b/Server/src/main/core/game/node/item/GroundItemManager.java @@ -1,14 +1,14 @@ package core.game.node.item; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.update.flag.chunk.ItemUpdateFlag; import core.net.packet.PacketRepository; import core.net.packet.context.BuildItemContext; import core.net.packet.out.UpdateGroundItemAmount; -import rs09.game.ai.AIRepository; +import core.game.bots.AIRepository; import java.util.ArrayList; import java.util.List; @@ -32,6 +32,10 @@ public final class GroundItemManager { return create(new GroundItem(item, location, null)); } + public static GroundItem create (Item item, Location location, int playerUid, int ticks) { + return create(new GroundItem(item, location, playerUid, ticks)); + } + /** * Creates a ground item. * @param item the item. diff --git a/Server/src/main/java/core/game/node/item/Item.java b/Server/src/main/core/game/node/item/Item.java similarity index 87% rename from Server/src/main/java/core/game/node/item/Item.java rename to Server/src/main/core/game/node/item/Item.java index 59bbcf8ea..c95077106 100644 --- a/Server/src/main/java/core/game/node/item/Item.java +++ b/Server/src/main/core/game/node/item/Item.java @@ -1,14 +1,11 @@ package core.game.node.item; import core.cache.def.impl.ItemDefinition; -import core.game.ge.GrandExchangeDatabase; -import core.game.ge.GrandExchangeEntry; 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; -import rs09.game.ge.OfferManager; /** * Represents an item. @@ -37,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; } @@ -68,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); @@ -109,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. @@ -130,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 */ @@ -191,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. @@ -248,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/java/core/game/node/item/ItemPlugin.java b/Server/src/main/core/game/node/item/ItemPlugin.java similarity index 100% rename from Server/src/main/java/core/game/node/item/ItemPlugin.java rename to Server/src/main/core/game/node/item/ItemPlugin.java diff --git a/Server/src/main/java/core/game/node/item/WeightedChanceItem.java b/Server/src/main/core/game/node/item/WeightedChanceItem.java similarity index 100% rename from Server/src/main/java/core/game/node/item/WeightedChanceItem.java rename to Server/src/main/core/game/node/item/WeightedChanceItem.java diff --git a/Server/src/main/java/core/game/node/scenery/Constructed.java b/Server/src/main/core/game/node/scenery/Constructed.java similarity index 100% rename from Server/src/main/java/core/game/node/scenery/Constructed.java rename to Server/src/main/core/game/node/scenery/Constructed.java diff --git a/Server/src/main/java/core/game/node/scenery/Scenery.java b/Server/src/main/core/game/node/scenery/Scenery.java similarity index 87% rename from Server/src/main/java/core/game/node/scenery/Scenery.java rename to Server/src/main/core/game/node/scenery/Scenery.java index 4493620ff..9594a7f26 100644 --- a/Server/src/main/java/core/game/node/scenery/Scenery.java +++ b/Server/src/main/core/game/node/scenery/Scenery.java @@ -3,13 +3,18 @@ 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; import core.game.system.task.Pulse; import core.game.world.map.Direction; import core.game.world.map.Location; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import static core.api.ContentAPIKt.*; /** * Represents a scenery. @@ -30,7 +35,7 @@ public class Scenery extends Node { /** * The rotation. */ - private final int rotation; + private int rotation; /** * The object's definition. @@ -141,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; @@ -158,6 +163,10 @@ public class Scenery extends Node { childs = null; } } + + public Scenery(Scenery other) { + this(other.getId(), other.getLocation(), other.getType(), other.getRotation()); + } /** * Called when an object is removed. @@ -224,10 +233,10 @@ public class Scenery extends Node { if (def.getVarbitID() > -1) { VarbitDefinition config = def.getConfigFile(); if (config != null) { - player.varpManager.get(config.getConfigId()).setVarbit(config.getBitShift(),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); } } @@ -309,6 +318,10 @@ public class Scenery extends Node { return rotation; } + public void setRotation(int rot) { + rotation = rot; + } + /** * @return the location */ @@ -426,4 +439,37 @@ public class Scenery extends Node { this.wrapper = wrapper; } -} \ No newline at end of file + @SuppressWarnings("SuspiciousNameCombination") + @NotNull + public List getOccupiedTiles() { + List occupied = new ArrayList<>(); + occupied.add(location); + + int sizeX = getSizeX(); + int sizeY = getSizeY(); + + if (rotation % 2 == 1) { + int tmp = sizeX; + sizeX = sizeY; + sizeY = tmp; + } + + boolean sub = rotation >= 2; + + if (sizeX > 1) { + for (int i = 1; i < sizeX; i++) { + int modifier = sub ? -i : i; + occupied.add(location.transform(modifier, 0, 0)); + } + } + + if (sizeY > 1) { + for (int i = 1; i < sizeY; i++) { + int modifier = sub ? -i : i; + occupied.add(location.transform(0, modifier, 0)); + } + } + + return occupied; + } +} diff --git a/Server/src/main/java/core/game/node/scenery/SceneryBuilder.java b/Server/src/main/core/game/node/scenery/SceneryBuilder.java similarity index 95% rename from Server/src/main/java/core/game/node/scenery/SceneryBuilder.java rename to Server/src/main/core/game/node/scenery/SceneryBuilder.java index 4db5c619a..3dac14bf0 100644 --- a/Server/src/main/java/core/game/node/scenery/SceneryBuilder.java +++ b/Server/src/main/core/game/node/scenery/SceneryBuilder.java @@ -2,14 +2,17 @@ package core.game.node.scenery; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; -import rs09.game.system.SystemLogger; +import core.tools.Log; +import core.tools.SystemLogger; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.game.world.map.Location; 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("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("Object could not be replaced - object to remove is invalid."); - } return false; } if (current.getRestorePulse() != null) { @@ -270,4 +267,4 @@ public final class SceneryBuilder { RegionManager.getRegionChunk(o.getLocation()).flag(new ObjectUpdateFlag(o, !o.isActive())); } } -} \ No newline at end of file +} 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 new file mode 100644 index 000000000..9d8425993 --- /dev/null +++ b/Server/src/main/core/game/shops/Shop.kt @@ -0,0 +1,484 @@ +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.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 java.lang.Integer.max +import java.lang.Integer.min +import kotlin.math.ceil +import kotlin.math.roundToInt + +data class ShopItem(var itemId: Int, var amount: Int, val restockRate: Int = 100) + +class ShopListener(val player: Player) : ContainerListener +{ + var enabled = false + override fun update(c: Container?, event: ContainerEvent?) { + PacketRepository.send(ContainerPacket::class.java, ContainerContext(player, -1, -1, 92, event!!.items, false, *event.slots)) + } + + override fun refresh(c: Container?) { + PacketRepository.send(ContainerPacket::class.java, ContainerContext(player, -1, -1, 92, c!!.toArray(), c.capacity(), 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) + val needsUpdate = HashMap() + val restockRates = HashMap() + + init { + if(!getServerConfig().getBoolean(Shops.personalizedShops, false) || forceShared) + stockInstances[ServerConstants.SERVER_NAME.hashCode()] = generateStockContainer() + } + + fun openFor(player: Player) + { + val cont = getContainer(player) + setInterfaceText(player, title, 620, 22) + setAttribute(player, "shop", this) + setAttribute(player, "shop-cont", cont) + openInterface(player, Components.SHOP_TEMPLATE_620) + player.interfaceManager.openSingleTab(Component(Components.SHOP_TEMPLATE_SIDE_621)) + showTab(player, true) + logShop("Opening shop [Title: $title, Player: ${player.username}]") + } + + fun showTab(player: Player, main: Boolean) + { + val mainCont = getAttribute(player, "shop-cont", null) ?: return + val cont = if (main) mainCont else playerStock + + if(!main) + { + mainCont.listeners.remove(listenerInstances[player.details.uid]) + playerStock.listeners.add(listenerInstances[player.details.uid]) + } + else + { + playerStock.listeners.remove(listenerInstances[player.details.uid]) + mainCont.listeners.add(listenerInstances[player.details.uid]) + } + + val settings = IfaceSettingsBuilder() + .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) + player.packetDispatch.sendInterfaceConfig(620, 23, !main) + player.packetDispatch.sendInterfaceConfig(620, 24, main) + player.packetDispatch.sendInterfaceConfig(620, 29, !main) + player.packetDispatch.sendInterfaceConfig(620, 25, main) + player.packetDispatch.sendInterfaceConfig(620, 27, main) + player.packetDispatch.sendInterfaceConfig(620, 26, false) + + if (!main) playerStock.refresh() + else cont.refresh() + + setAttribute(player, "shop-main", main) + } + + public fun getContainer(player: Player) : Container + { + 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()] ?: generateStockContainer().also { stockInstances[ServerConstants.SERVER_NAME.hashCode()] = it } + + val listener = listenerInstances[player.details.uid] + + if(listener != null && listener.player != player) + { + container.listeners.remove(listener) + } + + if(listener == null || listener.player != player) + { + listenerInstances[player.details.uid] = ShopListener(player) + } + + return container + } + + private fun generateStockContainer(): Container + { + val container = Container(40, ContainerType.SHOP) + for(item in stock) { + container.add(Item(item.itemId,item.amount)) + restockRates[item.itemId] = item.restockRate + } + + return container + } + + fun restock() + { + stockInstances.filter { needsUpdate[it.key] == true }.forEach{ (player,cont) -> + for(i in 0 until cont.capacity()) + { + 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){ + cont[i].amount++ + cont.event.flag(i, cont[i]) + } + else if(cont[i].amount > stock[i].amount){ + cont[i].amount-- + cont.event.flag(i, cont[i]) + } + if(cont[i].amount != stock[i].amount) needsUpdate[player] = true + } + cont.update() + } + } + + fun getBuyPrice(player: Player, slot: Int): Item + { + 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(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) + } + + fun getSellPrice(player: Player, slot: Int): Pair + { + 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 + } + else + { + item.noteChange + } + 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 + 0 + } + } + + 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) + { + return Pair(null, Item(-1,-1)) + } + + return Pair(if (isPlayerStock) playerStock else shopCont, Item(currency, price)) + } + + private fun getGPCost(item: Item, stockAmount: Int, currentAmt: Int): Int + { + var mod: Int + mod = if(stockAmount == 0) 100 + else if(currentAmt == 0) 130 + else if(currentAmt >= stockAmount) 100 + else 130 - (130 - 100) * currentAmt / stockAmount + if(mod < 1) mod = 1 + mod = max(100, min(130, mod)) + + + val price: Int = ceil(item.definition.value * mod.toDouble() / 100.0).toInt() + /* if(player.getVarp(532) == 6529){ + price = 3 * price / 2 + }*/ + return max(price, 1) + } + + private fun getGPSell(item: Item, stockAmount: Int, currentAmt: Int): Int + { + val base = item.definition.getAlchemyValue(highAlch) + var overstock = currentAmt - stockAmount + if (overstock < 0) { + return base + } + if (overstock > 10) { + overstock = 10 + } + val price = (base - (item.definition.value * 0.03 * overstock)).roundToInt() + if (price < 1) { + return 1 + } + return price + } + + fun buy(player: Player, slot: Int, amount: Int) : TransactionStatus + { + if(amount !in 1..Integer.MAX_VALUE) return TransactionStatus.Failure("Invalid amount: $amount") + val isMainStock = getAttribute(player, "shop-main", false) + if(!isMainStock && player.ironmanManager.isIronman) + { + sendDialogue(player, "As an ironman, you cannot buy from player stock in shops.") + return TransactionStatus.Failure("Ironman buying from player stock") + } + val cont = if (isMainStock) (getAttribute(player, "shop-cont", null) ?: return TransactionStatus.Failure("Invalid shop-cont attr")) else playerStock + val inStock = cont[slot] + val item = Item(inStock.id, amount) + if(inStock.amount < amount) + item.amount = inStock.amount + if (item.amount > player.inventory.getMaximumAdd(item)) + item.amount = player.inventory.getMaximumAdd(item) + + if(inStock.amount == 0) { + sendMessage(player, "This item is out of stock.") + return TransactionStatus.Failure("Shop item out of stock.") + } + + 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") + } + + val cost = getBuyPrice(player, slot) + 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){ + 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 + } + + if(inInventory(player, cost.id, cost.amount)) + { + if(removeItem(player, cost)) + { + if (item.amount == 0) { + item.amount = 1 + } + if(!hasSpaceFor(player, item)) { + addItem(player, cost.id, cost.amount) + sendMessage(player, "You don't have enough inventory space to buy that many.") + return TransactionStatus.Failure("Not enough inventory space") + } + + if(!isMainStock && cont[slot].amount - item.amount == 0) + { + cont.remove(cont[slot], false) + cont.refresh() + } + else { + cont[slot].amount -= item.amount + cont.event.flag(slot, cont[slot]) + cont.update() + } + + addItem(player, item.id, item.amount) + + if(getServerConfig().getBoolean(Shops.personalizedShops, false)){ + needsUpdate[player.details.uid] = true + } else { + needsUpdate[ServerConstants.SERVER_NAME.hashCode()] = true + } + } + } + else + { + 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)) + return TransactionStatus.Success() + } + + fun sell(player: Player, slot: Int, amount: Int) : TransactionStatus + { + if(amount !in 1..Integer.MAX_VALUE) return TransactionStatus.Failure("Invalid amount: $amount") + val playerInventory = player.inventory[slot] + if(playerInventory.id in intArrayOf(Items.COINS_995, Items.TOKKUL_6529, Items.ARCHERY_TICKET_1464)) + { + sendMessage(player, "You can't sell currency to a shop.") + return TransactionStatus.Failure("Tried to sell currency - ${playerInventory.id}") + } + val item = Item(playerInventory.id, amount) + val def = itemDefinition(item.id) + + if (def.hasDestroyAction()) { + sendMessage(player, "You can't sell this item.") + return TransactionStatus.Failure("Attempt to sell a destroyable - ${playerInventory.id}.") + } + + if (!def.isTradeable) { + sendMessage(player, "You can't sell this item.") + return TransactionStatus.Failure("Attempt to sell an untradeable - ${playerInventory.id}.") + } + + val (container,profit) = getSellPrice(player, slot) + if(profit.amount == -1) sendMessage(player, "This item can't be sold to this shop.").also { return TransactionStatus.Failure("Can't sell this item to this shop - ${playerInventory.id}, general: $general, price: $profit") } + if(amount > player.inventory.getAmount(item.id)) + item.amount = player.inventory.getAmount(item.id) + + val id = if(!item.definition.isUnnoted) item.noteChange else item.id + val (isPlayerStock, shopSlot) = getStockSlot(id) + + if(isPlayerStock && shopSlot == -1 && generalPlayerStock.freeSlots() == 0) { + sendMessage(player, "The shop is too full to buy any more items") + return TransactionStatus.Failure("Attempt to sell to full shop.") + } + + 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) + profit.amount += getGPSell(Item(item.id, 1), if (isPlayerStock) 0 else stock[shopSlot].amount, ++inStockAmt) + } else { + profit.amount = profit.amount * item.amount + } + + if(removeItem(player, item)) + { + if(!hasSpaceFor(player, profit)){ + sendMessage(player, "You don't have enough space to do that.") + addItem(player, item.id, item.amount) + return TransactionStatus.Failure("Did not have enough inventory space") + } + if(container == playerStock && getAttribute(player, "shop-main", false)){ + showTab(player, false) + } + else if(!getAttribute(player, "shop-main", false) && container != playerStock) + { + showTab(player, true) + } + addItem(player, profit.id, profit.amount) + if(!item.definition.isUnnoted) + { + item.id = item.noteChange + } + container?.add(item) + container?.refresh() + if(getServerConfig().getBoolean(Shops.personalizedShops, false)){ + needsUpdate[player.details.uid] = true + } else { + needsUpdate[ServerConstants.SERVER_NAME.hashCode()] = true + } + } + + player.dispatch(ItemShopSellEvent(item.id, item.amount, profit)) + return TransactionStatus.Success() + } + + fun getStockSlot(itemId: Int): Pair + { + var shopSlot: Int = -1 + var isPlayerStock = false + val notechange = itemDefinition(itemId).noteId + for((stockSlot, shopItem) in stock.withIndex()) + { + if(shopItem.itemId == itemId || shopItem.itemId == notechange) + shopSlot = stockSlot + } + if(shopSlot == -1) + { + for((stockSlot, playerStockItem) in playerStock.toArray().withIndex()) + { + if(playerStockItem == null) continue + if(playerStockItem.id == itemId || playerStockItem.id == notechange) { + shopSlot = stockSlot + isPlayerStock = true + } + } + } + + if(shopSlot == -1) isPlayerStock = true + return Pair(isPlayerStock, shopSlot) + } + + 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 new file mode 100644 index 000000000..b79b354f2 --- /dev/null +++ b/Server/src/main/core/game/shops/Shops.kt @@ -0,0 +1,298 @@ +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 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 java.io.FileReader +import content.data.Quests + +/** + * The "controller" class for shops. Handles opening shops from various NPC interactions and updating stock, etc. + * Note: If you wish to enable personalized shops, add and set the personalized_shops entry to true in the world section of the server config. + * ex: + * ```toml + * [world] + * personalized_shops = true + * ``` + */ +class Shops : StartupListener, TickListener, InteractionListener, InterfaceListener, Commands { + companion object { + @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) + { + shopsById[id]?.openFor(player) + } + + fun logShop(msg: String) + { + log(this::class.java, Log.FINE, "[SHOPS] $msg") + } + + fun parseStock(stock: String, id: Int): ArrayList{ + val items = ArrayList() + val idsInStock = HashMap() + if(stock.isEmpty()){ + return items + } + stock.split('-').map { + 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 + } + } + + override fun startup() { + val path = ServerConstants.CONFIG_PATH + "shops.json" + var shopCount = 0 + logShop("Using JSON path: $path") + + val reader = FileReader(path) + val data = JSONParser().parse(reader) as JSONArray + + for(rawShop in data) + { + var shop: Shop? + val shopData = rawShop as JSONObject + val id = shopData["id"].toString().toInt() + val title = shopData["title"].toString() + val general = shopData["general_store"].toString().toBoolean() + val stock = parseStock(shopData["stock"].toString(), id).toTypedArray() + 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" + 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 + ++shopCount + } + + logShop("Parsed $shopCount shops.") + } + + 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 || npc.id == 1041 || npc.id == 804) { + TanningProduct.open(player, npc.id) + return@on true + } + if (npc.id == 7601) { + openInterface(player, 732) + return@on true + } + shopsByNpc[npc.id]?.openFor(player) ?: return@on false + return@on true + } + + on(NPCs.SIEGFRIED_ERKLE_933, IntType.NPC, "trade"){ player, node -> + 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 + } + shopsByNpc[node.id]?.openFor(player) + return@on true + } + + on(NPCs.FUR_TRADER_1316, IntType.NPC, "trade") { player, node -> + 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() { + on(Components.SHOP_TEMPLATE_620){player, _, opcode, buttonID, slot, _ -> + val OP_VALUE = 155 + val OP_BUY_1 = 196 + val OP_BUY_5 = 124 + val OP_BUY_10 = 199 + val OP_BUY_X = 234 + val OP_EXAMINE = 9 + + val shop = getAttribute(player, "shop", null) ?: return@on false + val isMainStock = getAttribute(player, "shop-main", true) + + when(buttonID) + { + 26 -> shop.showTab(player, false).also { return@on true } + 25 -> shop.showTab(player, true).also { return@on true } + 27,29 -> return@on true + } + + val price = shop.getBuyPrice(player, slot) + + 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.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, InputType.AMOUNT, "Enter the amount to buy:"){value -> + val amt = value as Int + shop.buy(player, slot, amt) + } + OP_EXAMINE -> sendMessage(player, itemDefinition(if (isMainStock) shop.stock[slot].itemId else shop.playerStock[slot].id).examine) + } + + return@on true + } + + onOpen(Components.SHOP_TEMPLATE_SIDE_621) {player, _ -> + val settings = IfaceSettingsBuilder() + .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) + return@onOpen true + } + + onClose(Components.SHOP_TEMPLATE_620) { player, _ -> + val shop = getAttribute(player, "shop", null) ?: return@onClose true + val listener = Shop.listenerInstances[player.details.uid] ?: return@onClose true + + if(getServerConfig().getBoolean(personalizedShops, false)) + shop.stockInstances[player.details.uid]?.listeners?.remove(listener) + else + shop.stockInstances[ServerConstants.SERVER_NAME.hashCode()]!!.listeners.remove(listener) + + shop.playerStock.listeners.remove(listener) + player.interfaceManager.closeSingleTab() + return@onClose true + } + + on(Components.SHOP_TEMPLATE_SIDE_621){player, component, opcode, buttonID, slot, itemID -> + val OP_VALUE = 155 + val OP_SELL_1 = 196 + val OP_SELL_5 = 124 + val OP_SELL_10 = 199 + val OP_SELL_X = 234 + + 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.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()}." + } + + when(opcode) + { + OP_VALUE -> sendMessage(player, valueMsg) + 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, InputType.AMOUNT, "Enter the amount to sell:"){value -> + val amt = value as Int + shop.sell(player, slot, amt) + } + } + + return@on true + } + } + + override fun defineDestinationOverrides() { + setDest(IntType.NPC,"trade","shop"){ _, node -> + val npc = node as NPC + if (npc.getAttribute("facing_booth", false)) { + val offsetX = npc.direction.stepX shl 1 + val offsetY = npc.direction.stepY shl 1 + return@setDest npc.location.transform(offsetX, offsetY, 0) + } + return@setDest node.location + } + } + + override fun defineCommands() { + 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", 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) + } + } +} diff --git a/Server/src/main/java/core/game/system/SystemConfig.java b/Server/src/main/core/game/system/SystemConfig.java similarity index 100% rename from Server/src/main/java/core/game/system/SystemConfig.java rename to Server/src/main/core/game/system/SystemConfig.java diff --git a/Server/src/main/java/core/game/system/SystemManager.java b/Server/src/main/core/game/system/SystemManager.java similarity index 98% rename from Server/src/main/java/core/game/system/SystemManager.java rename to Server/src/main/core/game/system/SystemManager.java index 48685fdb3..5c1ba185a 100644 --- a/Server/src/main/java/core/game/system/SystemManager.java +++ b/Server/src/main/core/game/system/SystemManager.java @@ -1,7 +1,7 @@ package core.game.system; import core.game.system.security.EncryptionManager; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * Manages the "game system" states, such as updating or terminating. diff --git a/Server/src/main/core/game/system/SystemShutdownHook.java b/Server/src/main/core/game/system/SystemShutdownHook.java new file mode 100644 index 000000000..850de35ed --- /dev/null +++ b/Server/src/main/core/game/system/SystemShutdownHook.java @@ -0,0 +1,22 @@ +package core.game.system; + +import core.tools.Log; +import core.tools.SystemLogger; + +import static core.api.ContentAPIKt.log; + +/** + * Handles the shutdown hook. + * @author Emperor + */ +public final class SystemShutdownHook implements Runnable { + + @Override + public void run() { + if (SystemManager.isTerminated()) { + return; + } + log(this.getClass(), Log.INFO, "Terminating..."); + SystemManager.flag(SystemState.TERMINATED); + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/SystemState.java b/Server/src/main/core/game/system/SystemState.java similarity index 100% rename from Server/src/main/java/core/game/system/SystemState.java rename to Server/src/main/core/game/system/SystemState.java diff --git a/Server/src/main/core/game/system/SystemTermination.java b/Server/src/main/core/game/system/SystemTermination.java new file mode 100644 index 000000000..813b16fe3 --- /dev/null +++ b/Server/src/main/core/game/system/SystemTermination.java @@ -0,0 +1,106 @@ +package core.game.system; + +import core.api.PersistWorld; +import core.api.ShutdownListener; +import core.game.node.entity.player.Player; +import core.Server; +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; + +import java.io.File; +import java.util.Iterator; + +import static core.api.ContentAPIKt.log; + +/** + * Handles the terminating of the system. + * @author Emperor + * + */ +public final class SystemTermination { + + /** + * Constructs a new {@code SystemTermination} {@code Object}. + */ + protected SystemTermination() { + /* + * empty. + */ + } + + /** + * Terminates the system safely. + */ + public void terminate() { + log(this.getClass(), Log.INFO, "Initializing termination sequence - do not shutdown!"); + try { + log(this.getClass(), Log.INFO, "Shutting down networking..."); + Server.setRunning(false); + log(this.getClass(), Log.INFO, "Stopping all bots..."); + AIRepository.clearAllBots(); + Server.getReactor().terminate(); + 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 { + Player p = it.next(); + if (p != null && !p.isArtificial()) { // Should never be null. + p.getDetails().save(); + p.clear(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + GameWorld.getShutdownListeners().forEach(ShutdownListener::shutdown); + PlayerMonitor.flushRemainingEventsImmediately(); + ServerStore s = null; + for (PersistWorld wld : GameWorld.getWorldPersists()) { + if (wld instanceof ServerStore) + s = (ServerStore) wld; + else + wld.save(); + } + //ServerStore should ***always*** save last. Fudging a race condition here :) + if (s != null) + s.save(); + if(ServerConstants.DATA_PATH != null) + save(ServerConstants.DATA_PATH); + } catch (Throwable e) { + e.printStackTrace(); + } + log(this.getClass(), Log.INFO, "Server successfully terminated!"); + } + + /** + * Saves all system data on the directory. + * @param directory The base directory. + */ + public void save(String directory) { + File file = new File(directory); + log(this.getClass(), Log.INFO, "Saving data [dir="+ file.getAbsolutePath() + "]..."); + if (!file.isDirectory()) { + file.mkdirs(); + } + Server.getReactor().terminate(); + long start = System.currentTimeMillis(); + while(!Repository.getDisconnectionQueue().isEmpty() && System.currentTimeMillis() - start < 5000L){ + Repository.getDisconnectionQueue().update(); + try { + Thread.sleep(100); + } catch (Exception ignored) {} + } + Repository.getDisconnectionQueue().update(); + Repository.getDisconnectionQueue().clear(); + } +} diff --git a/Server/src/main/java/core/game/system/SystemUpdate.java b/Server/src/main/core/game/system/SystemUpdate.java similarity index 92% rename from Server/src/main/java/core/game/system/SystemUpdate.java rename to Server/src/main/core/game/system/SystemUpdate.java index 9df42b872..e28fe342f 100644 --- a/Server/src/main/java/core/game/system/SystemUpdate.java +++ b/Server/src/main/core/game/system/SystemUpdate.java @@ -1,10 +1,10 @@ package core.game.system; -import rs09.ServerConstants; +import core.ServerConstants; import core.game.node.entity.player.Player; import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; +import core.game.world.GameWorld; +import core.game.world.repository.Repository; import java.util.Iterator; import java.util.concurrent.Executors; @@ -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 new file mode 100644 index 000000000..c76e42a61 --- /dev/null +++ b/Server/src/main/core/game/system/command/Command.kt @@ -0,0 +1,69 @@ +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, 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 + 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!") + } + } +} + +object CommandMapping { + private val mapping = hashMapOf() + + fun get(name: String): Command?{ + return mapping[name] + } + + fun register(command: Command){ + mapping[command.name] = command + } + + fun getCommands(): Array { + return mapping.values.toTypedArray() + } + + fun getNames(): Array { + return mapping.keys.toTypedArray() + } + + fun getPageIndices(rights: Int): IntArray { + val list = ArrayList() + list.add(0) + + var lineCounter = 0 + for ((index, command) in getCommands().filter { it.privilege.ordinal <= rights }.withIndex()) { + + lineCounter += 2 + if (command.usage.isNotEmpty()) lineCounter++ + if (command.description.isNotEmpty()) lineCounter++ + + if (lineCounter > 306) { + list.add(index) + lineCounter = 0 + } + } + + return list.toIntArray() + } +} diff --git a/Server/src/main/kotlin/rs09/game/system/command/CommandPlugin.kt b/Server/src/main/core/game/system/command/CommandPlugin.kt similarity index 95% rename from Server/src/main/kotlin/rs09/game/system/command/CommandPlugin.kt rename to Server/src/main/core/game/system/command/CommandPlugin.kt index debb49580..45cd441a7 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/CommandPlugin.kt +++ b/Server/src/main/core/game/system/command/CommandPlugin.kt @@ -1,9 +1,8 @@ -package rs09.game.system.command +package core.game.system.command import core.game.node.entity.player.Player -import core.game.system.command.CommandSet import core.plugin.Plugin -import rs09.game.world.repository.Repository +import core.game.world.repository.Repository /** * Represents a command plugin that can be linked to a command set. diff --git a/Server/src/main/java/core/game/system/command/CommandSet.java b/Server/src/main/core/game/system/command/CommandSet.java similarity index 96% rename from Server/src/main/java/core/game/system/command/CommandSet.java rename to Server/src/main/core/game/system/command/CommandSet.java index 98cf63265..56daa2317 100644 --- a/Server/src/main/java/core/game/system/command/CommandSet.java +++ b/Server/src/main/core/game/system/command/CommandSet.java @@ -2,8 +2,8 @@ package core.game.system.command; import core.game.node.entity.player.Player; import core.game.node.entity.player.info.Rights; -import rs09.game.system.command.CommandPlugin; -import rs09.game.world.GameWorld; +import core.game.system.command.CommandPlugin; +import core.game.world.GameWorld; import java.util.ArrayList; import java.util.List; diff --git a/Server/src/main/kotlin/rs09/game/system/command/CommandSystem.kt b/Server/src/main/core/game/system/command/CommandSystem.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/system/command/CommandSystem.kt rename to Server/src/main/core/game/system/command/CommandSystem.kt index a1a4b5a38..3601182b0 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/CommandSystem.kt +++ b/Server/src/main/core/game/system/command/CommandSystem.kt @@ -1,7 +1,7 @@ -package rs09.game.system.command +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/kotlin/rs09/game/system/command/MapDumpCommand.kt b/Server/src/main/core/game/system/command/MapDumpCommand.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/system/command/MapDumpCommand.kt rename to Server/src/main/core/game/system/command/MapDumpCommand.kt index 5a0be4fcb..2743af899 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/MapDumpCommand.kt +++ b/Server/src/main/core/game/system/command/MapDumpCommand.kt @@ -1,4 +1,4 @@ -package rs09.game.system.command +package core.game.system.command import core.game.node.entity.player.Player import core.game.system.command.CommandSet @@ -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/Privilege.kt b/Server/src/main/core/game/system/command/Privilege.kt new file mode 100644 index 000000000..7c4fc9354 --- /dev/null +++ b/Server/src/main/core/game/system/command/Privilege.kt @@ -0,0 +1,7 @@ +package core.game.system.command + +enum class Privilege{ + STANDARD, + MODERATOR, + ADMIN +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/oldsys/SimpleDumpingCommands.kt b/Server/src/main/core/game/system/command/oldsys/SimpleDumpingCommands.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/system/command/oldsys/SimpleDumpingCommands.kt rename to Server/src/main/core/game/system/command/oldsys/SimpleDumpingCommands.kt index 5f5df5cfe..c4cee6e38 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/oldsys/SimpleDumpingCommands.kt +++ b/Server/src/main/core/game/system/command/oldsys/SimpleDumpingCommands.kt @@ -1,4 +1,4 @@ -package rs09.game.system.command.oldsys +package core.game.system.command.oldsys import core.cache.def.impl.ItemDefinition import core.cache.def.impl.NPCDefinition @@ -7,7 +7,7 @@ import core.game.node.entity.player.Player import core.game.system.command.CommandSet import core.plugin.Initializable import core.plugin.Plugin -import rs09.game.system.command.CommandPlugin +import core.game.system.command.CommandPlugin import java.io.BufferedWriter import java.io.File diff --git a/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt b/Server/src/main/core/game/system/command/oldsys/VisualCommand.kt similarity index 90% rename from Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt rename to Server/src/main/core/game/system/command/oldsys/VisualCommand.kt index ec0814583..17d98c883 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt +++ b/Server/src/main/core/game/system/command/oldsys/VisualCommand.kt @@ -1,9 +1,9 @@ -package rs09.game.system.command.oldsys +package core.game.system.command.oldsys -import api.ContentAPI +import core.api.* import core.cache.Cache import core.game.container.access.InterfaceContainer -import core.game.content.quest.tutorials.tutorialisland.CharacterDesign +import content.region.misc.tutisland.handlers.iface.CharacterDesign import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder import core.game.node.entity.combat.ImpactHandler.HitsplatType @@ -23,8 +23,8 @@ import core.net.packet.context.VarbitContext import core.net.packet.out.Varbit import core.plugin.Initializable import core.plugin.Plugin -import rs09.game.system.command.CommandPlugin -import rs09.game.world.GameWorld +import core.game.system.command.CommandPlugin +import core.game.world.GameWorld import java.awt.Toolkit import java.awt.datatransfer.StringSelection @@ -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) @@ -188,15 +196,15 @@ class VisualCommand : CommandPlugin() { player!!.debug("syntax error: x y id") return true } - location = if (args.size > 2) Location.create(args[1]!!.toInt(), args[2]!!.toInt(), 3) else player!!.location - `object` = RegionManager.getObject(location) + location = if (args.size > 2) Location.create(args[1]!!.toInt(), args[2]!!.toInt(), player!!.location.z) else player!!.location + `object` = RegionManager.getObject(location!!) if (`object` == null) { player!!.debug("error: object not found in region cache.") return true } player!!.packetDispatch.sendSceneryAnimation(`object`, Animation(toInteger(args[args.size - 1]!!))) - //ContentAPI.sendMessage(player, `object`.definition.modelIds.map { it.toString() }.toString()) - ContentAPI.sendMessage(player, `object`.definition.animationId.toString()) + //sendMessage(player, `object`.definition.modelIds.map { it.toString() }.toString()) + sendMessage(player, `object`.definition.animationId.toString()) return true } "inter", "component", "interface" -> { @@ -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") @@ -227,12 +238,12 @@ class VisualCommand : CommandPlugin() { val value = (args!![1]!!.toString().toInt()) ?: 0 val cfg_index = (args.getOrNull(2)?.toString()?.toInt() ?: -1) if(cfg_index == -1){ - ContentAPI.submitWorldPulse(object : Pulse(3, player){ + submitWorldPulse(object : Pulse(3, player){ var pos = 32 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){ @@ -243,52 +254,34 @@ class VisualCommand : CommandPlugin() { } }) } else { - ContentAPI.submitWorldPulse(object : Pulse(3, player) { + 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]!!) - ContentAPI.submitWorldPulse(object : Pulse(3){ + submitWorldPulse(object : Pulse(3){ override fun pulse(): Boolean { player!!.packetDispatch.sendAnimationInterface(anim++, 224, 7) - ContentAPI.sendMessage(player, "${anim - 1}") + sendMessage(player, "${anim - 1}") 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/kotlin/rs09/game/system/command/rottenpotato/RPUseWithNPCDialogue.kt b/Server/src/main/core/game/system/command/rottenpotato/RPUseWithNPCDialogue.kt similarity index 86% rename from Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RPUseWithNPCDialogue.kt rename to Server/src/main/core/game/system/command/rottenpotato/RPUseWithNPCDialogue.kt index deb209cfb..fa689367a 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RPUseWithNPCDialogue.kt +++ b/Server/src/main/core/game/system/command/rottenpotato/RPUseWithNPCDialogue.kt @@ -1,21 +1,20 @@ -package rs09.game.system.command.rottenpotato +package core.game.system.command.rottenpotato -import core.game.content.dialogue.DialoguePlugin import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable -import rs09.tools.stringtools.colorize +import core.tools.colorize /** * Rotten potato -> npc interaction menu * @author Ceikry */ @Initializable -class RPUseWithNPCDialogue(player: Player? = null) : DialoguePlugin(player) { +class RPUseWithNPCDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { var npc: NPC? = null val ID = 38575795 - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return RPUseWithNPCDialogue(player) } diff --git a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt b/Server/src/main/core/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt rename to Server/src/main/core/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt index 6183f2f92..f79fdfcd9 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt +++ b/Server/src/main/core/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt @@ -1,6 +1,6 @@ -package rs09.game.system.command.rottenpotato +package core.game.system.command.rottenpotato -import core.game.content.dialogue.DialoguePlugin +import core.game.dialogue.DialoguePlugin import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.player.Player import core.plugin.Initializable diff --git a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt similarity index 78% rename from Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt rename to Server/src/main/core/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt index 4898a0869..5528ecf20 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt +++ b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoExtraDialogue.kt @@ -1,28 +1,26 @@ -package rs09.game.system.command.rottenpotato +package core.game.system.command.rottenpotato -import api.ContentAPI -import api.InputType -import core.game.content.dialogue.DialoguePlugin +import core.api.* +import core.api.InputType import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.player.link.RunScript import core.game.world.map.RegionManager import core.plugin.Initializable -import rs09.game.world.repository.Repository -import rs09.tools.stringtools.colorize +import core.game.world.repository.Repository +import core.tools.colorize @Initializable /** * Rotten Potato Right click -> Extra dialogue menu * @author Ceikry */ -class RottenPotatoExtraDialogue(player: Player? = null) : DialoguePlugin(player) { +class RottenPotatoExtraDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { val ID = 38575794 val AMEs = arrayOf("chicken","Sandwich Lady","tree spirit","rick turpentine","Genie") val BossIDs = arrayOf(50,8350,8133,2745) val BossNames = arrayOf("KBD","Tormented Demon","Corporeal Beast","Jad") - override fun newInstance(player: Player?): DialoguePlugin { + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { return RottenPotatoExtraDialogue(player) } @@ -39,7 +37,7 @@ class RottenPotatoExtraDialogue(player: Player? = null) : DialoguePlugin(player) //Send Player Notification 1 -> { end() - ContentAPI.sendInputDialogue(player, InputType.STRING_LONG, "Enter the notification message:"){ value -> + sendInputDialogue(player, InputType.STRING_LONG, "Enter the notification message:"){ value -> val message = value as String for (p in Repository.players) { p ?: continue @@ -62,7 +60,7 @@ class RottenPotatoExtraDialogue(player: Player? = null) : DialoguePlugin(player) //Force Area NPC Chat 4 -> { end() - ContentAPI.sendInputDialogue(player, InputType.STRING_LONG,"Enter the chat message:"){ value -> + sendInputDialogue(player, InputType.STRING_LONG,"Enter the chat message:"){ value -> val msg = value as String RegionManager.getLocalNpcs(player).forEach { it.sendChat(msg) @@ -82,7 +80,7 @@ class RottenPotatoExtraDialogue(player: Player? = null) : DialoguePlugin(player) //AME Spawning 100 -> { end() - ContentAPI.sendInputDialogue(player, InputType.STRING_SHORT, "Enter player name:"){ value -> + sendInputDialogue(player, InputType.STRING_SHORT, "Enter player name:"){ value -> val other = Repository.getPlayerByName(value.toString().toLowerCase().replace(" ", "_")) if (other == null) { player.sendMessage(colorize("%RInvalid player name.")) diff --git a/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt new file mode 100644 index 000000000..3d825cac4 --- /dev/null +++ b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt @@ -0,0 +1,53 @@ +package core.game.system.command.rottenpotato + +import core.api.removeItem +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.info.Rights +import core.plugin.Initializable +import core.plugin.Plugin +import org.rs09.consts.Items +import core.tools.SystemLogger +import core.game.system.command.CommandSystem +import core.tools.colorize + +/** + * Handles the Rotten Potato options + * @author Ceikry + */ +private val ROTTEN_POTATO_OPTIONS = arrayOf("RS HD","Heal","Extra","Commands") +@Initializable +class RottenPotatoOptionHandler : OptionHandler() { + override fun newInstance(arg: Any?): Plugin { + for(option in ROTTEN_POTATO_OPTIONS) { + ItemDefinition.forId(Items.ROTTEN_POTATO_5733).handlers["option:${option.toLowerCase()}"] = this + } + return this + } + + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + player ?: return false + node ?: return false + option ?: return false + + // re-add the fucking check because some fucking moron removed it at some point + if(player.rights != Rights.ADMINISTRATOR) + { + removeItem(player, Items.ROTTEN_POTATO_5733) + return false + } + + when(option){ + "rs hd" -> player.dialogueInterpreter.open(RottenPotatoRSHDDialogue().ID) + "heal" -> player.fullRestore().also { player.sendMessage(colorize("%RAll healed!")) } + "extra" -> player.dialogueInterpreter.open(RottenPotatoExtraDialogue().ID) + "commands" -> { + CommandSystem.commandSystem.parse(player,"commands") + } + } + return true + } + +} \ No newline at end of file diff --git a/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoRSHDDialogue.kt b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoRSHDDialogue.kt new file mode 100644 index 000000000..6321d5f07 --- /dev/null +++ b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoRSHDDialogue.kt @@ -0,0 +1,87 @@ +package core.game.system.command.rottenpotato + +import core.api.* +import core.api.InputType +import core.game.node.entity.player.Player +import core.game.node.entity.player.info.login.PlayerParser +import core.game.world.ImmerseWorld +import core.game.world.repository.Repository +import core.plugin.Initializable +import core.tools.colorize +import core.game.bots.AIRepository + +@Initializable +/** + * Rotten Potato "RS HD" option menu + * @author Ceikry + */ +class RottenPotatoRSHDDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + val ID = 38575793 + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return RottenPotatoRSHDDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + options("Wipe Bots", "Spawn Bots", "Force Log Players", "View Bank", "Copy Inventory") + stage = 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> { + when (buttonId) { + //Wipe Bots + 1 -> AIRepository.clearAllBots().also { player.sendMessage(colorize("%RBots wiped.")); end() } + //Spawn Bots + 2 -> ImmerseWorld.spawnBots().also { player.sendMessage(colorize("%RBots Respawning...")); end() } + //Force Log All Online Players + 3 -> { + Repository.disconnectionQueue.clear().also { end() } + Repository.players.toArray().forEach { + val p = it.asPlayer() + if (p != null && !p.isArtificial()) { // Should never be null. + p.removeAttribute("combat-time") + p.clear() + PlayerParser.save(p) + p.getDetails().save() + } + } + } + //View Bank + 4 -> { + end() + sendInputDialogue(player, InputType.STRING_SHORT, "Enter player name:"){ value -> + val other = Repository.getPlayerByName(value.toString().toLowerCase().replace(" ","_")) + if(other == null){ + player.sendMessage(colorize("%RInvalid player name.")) + return@sendInputDialogue + } + + other.bank.open(player) + } + } + //View Inventory + 5 -> { + end() + sendInputDialogue(player, InputType.STRING_SHORT, "Enter player name:"){ value -> + val other = Repository.getPlayerByName(value.toString().toLowerCase().replace(" ","_")) + if(other == null){ + player.sendMessage(colorize("%RInvalid player name.")) + return@sendInputDialogue + } + + player.inventory.clear() + player.inventory.addAll(other.inventory) + } + } + } + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(ID) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoUseWithHandler.kt b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoUseWithHandler.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoUseWithHandler.kt rename to Server/src/main/core/game/system/command/rottenpotato/RottenPotatoUseWithHandler.kt index 53a42225e..c5a97fef9 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoUseWithHandler.kt +++ b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoUseWithHandler.kt @@ -1,4 +1,4 @@ -package rs09.game.system.command.rottenpotato +package core.game.system.command.rottenpotato import core.game.node.Node import core.game.node.scenery.Scenery diff --git a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt new file mode 100644 index 000000000..d06258333 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt @@ -0,0 +1,117 @@ +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 +import core.plugin.Initializable +import core.game.system.command.CommandPlugin.Companion.toInteger +import core.game.system.command.Privilege +import core.game.world.GameWorld +import java.util.* + +@Initializable +class AnimationCommandSet : CommandSet(Privilege.ADMIN) { + + private var npcs: List = ArrayList() + + override fun defineCommands() { + + /** + * Force the player to play animation + */ + define("anim", Privilege.ADMIN, "::anim Animation ID", "Plays the animation with the given ID."){ player, args -> + if (args.size < 2) { + reject(player, "Syntax error: ::anim ") + } + val animation = Animation(args[1].toInt()) + 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 + */ + define("loopanim", Privilege.ADMIN, "::loopanim Animation ID Times", "Plays the animation with the given ID the given number of times"){ player, args -> + if (args.size < 2) { + reject(player, "Syntax error: ::loopanim ") + } + val start = toInteger(args[1]) + var end = toInteger((args[2])) + if (end > 25) { + notify(player, "Really...? $end times...? Looping 25 times instead.") + end = 25 + } + GameWorld.Pulser.submit(object : Pulse(3, player) { + var id = start + override fun pulse(): Boolean { + player.animate(Animation.create(id)) + return ++id >= end + } + }) + } + + /** + * Change the player's render animation to + */ + define("ranim", Privilege.ADMIN, "::ranim Render Anim ID", "Sets the player's render (walk/idle) animation."){ player, args -> + if (args.size < 2) { + 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 ") + } + } + } + + + /** + * Reset the player's render animation to default + */ + define("ranimreset", Privilege.ADMIN, "", "Resets the player's render (walk/idle) animation to default."){ player, _ -> + player.appearance.prepareBodyData(player) + player.appearance.setDefaultAnimations() + player.appearance.setAnimations() + player.appearance.sync() + } + } +} 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/kotlin/rs09/game/system/command/sets/BottingCommandSet.kt b/Server/src/main/core/game/system/command/sets/BottingCommandSet.kt similarity index 87% rename from Server/src/main/kotlin/rs09/game/system/command/sets/BottingCommandSet.kt rename to Server/src/main/core/game/system/command/sets/BottingCommandSet.kt index 8131ecb3a..d3ddcdd70 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/BottingCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/BottingCommandSet.kt @@ -1,25 +1,30 @@ -package rs09.game.system.command.sets +package core.game.system.command.sets import core.game.component.Component import core.plugin.Initializable import org.rs09.consts.Components -import rs09.game.ai.general.GeneralBotCreator -import rs09.game.ai.general.scriptrepository.PlayerScripts -import rs09.game.ai.general.scriptrepository.Script -import rs09.game.system.command.Command -import rs09.game.world.GameWorld -import rs09.tools.stringtools.colorize +import core.game.bots.GeneralBotCreator +import core.game.bots.PlayerScripts +import core.game.bots.Script +import core.game.system.command.Privilege +import core.game.world.GameWorld +import core.tools.colorize @Initializable -class BottingCommandSet : CommandSet(Command.Privilege.STANDARD) { +class BottingCommandSet : CommandSet(Privilege.STANDARD) { override fun defineCommands() { + + if (GameWorld.settings?.enabled_botting != true) { + return + } + define("scripts"){player, _ -> if(GameWorld.settings?.enabled_botting != true){ player.sendChat("I just tried to do something silly!") return@define } if(!player.getAttribute("botting:warning_shown",false)){ - player.dialogueInterpreter.sendDialogue(colorize("%RWARNING: Running a bot script will permanently remove you"),colorize("%Rfrom the highscores.")) + player.dialogueInterpreter.sendDialogue(colorize("%RWARNING: Running a bot script will permanently remove you"), colorize("%Rfrom the highscores.")) player.dialogueInterpreter.addAction { player, buttonId -> player.setAttribute("/save:botting:warning_shown",true) } return@define } @@ -68,4 +73,4 @@ class BottingCommandSet : CommandSet(Command.Privilege.STANDARD) { player.interfaceManager.closeOverlay() } } -} \ No newline at end of file +} 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/kotlin/rs09/game/system/command/sets/CommandSet.kt b/Server/src/main/core/game/system/command/sets/CommandSet.kt similarity index 77% rename from Server/src/main/kotlin/rs09/game/system/command/sets/CommandSet.kt rename to Server/src/main/core/game/system/command/sets/CommandSet.kt index d8992d412..ba15f4134 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/CommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/CommandSet.kt @@ -1,10 +1,11 @@ -package rs09.game.system.command.sets +package core.game.system.command.sets import core.game.node.entity.player.Player -import rs09.game.system.command.Command -import rs09.game.system.command.CommandMapping +import core.game.system.command.Command +import core.game.system.command.CommandMapping import core.plugin.Plugin -import rs09.tools.stringtools.colorize +import core.game.system.command.Privilege +import core.tools.colorize /** * Command sets allow you to organize sets of commands by category/type. It also provides @@ -13,7 +14,7 @@ import rs09.tools.stringtools.colorize * @author Ceikry * @param defaultPrivilege the default privilege level for all commands in this set. */ -abstract class CommandSet(val defaultPrivilege: Command.Privilege) : Plugin { +abstract class CommandSet(val defaultPrivilege: Privilege) : Plugin { override fun newInstance(arg: Any?): Plugin { defineCommands() return this @@ -50,7 +51,7 @@ abstract class CommandSet(val defaultPrivilege: Command.Privilege) : Plugin) -> Unit){ - CommandMapping.register(Command(name, privilege, handle)) + fun define(name: String, privilege: Privilege = defaultPrivilege, usage: String = "", description: String = "", handle: (Player, Array) -> Unit){ + CommandMapping.register(Command(name, privilege, usage, description, handle)) } -} \ 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 new file mode 100644 index 000000000..825dd326d --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/ConfigCommandSet.kt @@ -0,0 +1,77 @@ +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){ + override fun defineCommands() { + + /** + * Sets a range of configs to a maximum value + */ + 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)) { + setVarp(player, idsend, Integer.MAX_VALUE) + notify(player,"Config: $idsend value: " + Integer.MAX_VALUE) + } + } + + /** + * Sets a range of configs to 0 + */ + 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)) { + setVarp(player, idsend, 0) + notify(player,"Config: $idsend value: 0") + } + } + + /** + * Opens an interface component + */ + define("iface", Privilege.ADMIN, "::iface Interface ID", "Opens the interface with the given ID."){player, args -> + if (args.size < 2) { + 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 new file mode 100644 index 000000000..25ef32fef --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/DevelopmentCommandSet.kt @@ -0,0 +1,512 @@ +package core.game.system.command.sets + +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 +import core.game.node.item.Item +import core.net.packet.context.PlayerContext +import core.net.packet.out.ResetInterface +import core.plugin.Initializable +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.rs09.consts.Items +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,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() { + define("farmkit", Privilege.ADMIN, "", "Provides a kit of various farming equipment."){player,_ -> + for(item in farmKitItems){ + player.inventory.add(Item(item)) + } + } + + /** + * 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) { + for (task in 0 until diary.taskCompleted[level].size) { + diary.resetTask(player, level, task) + } + } + } + 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}") + } + + define("spellbook", Privilege.ADMIN, "::spellbook book ID (0 = MODERN, 1 = ANCIENTS, 2 = LUNARS)", "Swaps your spellbook to the given book ID."){player, args -> + if(args.size < 2){ + reject(player,"Usage: ::spellbook [int]. 0 = MODERN, 1 = ANCIENTS, 2 = LUNARS") + } + val spellBook = SpellBookManager.SpellBook.values()[args[1].toInt()] + player.spellBookManager.setSpellBook(spellBook) + player.spellBookManager.update(player) + } + + define("killme", Privilege.ADMIN, description = "Literally kills you in-game even") { player, _ -> + player.impactHandler.manualHit(player, player.skills.lifepoints, HitsplatType.NORMAL) + } + + 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) + log(this::class.java, Log.FINE, def.toString()) + } + + 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) + 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 + 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() + writer.close() + } + + define("dumpdatamaps", Privilege.ADMIN, "", "Dumps all the cache data maps to datamaps.txt") {player, _ -> + val index = Cache.getIndexes()[17] + val containers = index.information.containersIndexes + + val dump = File("datamaps.txt") + val writer = BufferedWriter(FileWriter(dump)) + + for(cID in containers) + { + 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() + } + } + } + } + writer.flush() + writer.close() + } + + 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 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 + } + + else -> { + reject(player, "Usage: ::rolldrops npcid amount [true|false]") + return@define + } + + } + + val drops = NPCDefinition.forId(npcId).dropTables.table.roll(player, amount) + + for (drop in drops) container.add(drop, false) + container.open(player) + } + + define("varbits", Privilege.ADMIN, "::varbits Varp ID", "Lists all the varbits assigned to the given varp.") { player, args -> + if(args.size < 2) + reject(player, "Usage: ::varbits varpIndex") + + val varp = args[1].toIntOrNull() ?: reject(player, "Please use a valid int for the varpIndex.") + GlobalScope.launch { + sendMessage(player, "========== Found Varbits for Varp $varp ==========") + for(id in 0 until 10000) + { + val def = VarbitDefinition.forId(id) + if(def.varpId == varp) + { + sendMessage(player, "${def.id} -> [offset: ${def.startBit}, upperBound: ${def.endBit}]") + } + } + sendMessage(player, "=========================================") + } + } + + define("testpacket", description = "Sends the reset-interface packet to the client.") { player, _ -> + PacketWriteQueue.write(ResetInterface(), PlayerContext(player)) + } + + define("npcsearch", Privilege.STANDARD, "::npcsearch name", "Searches for NPCs that match the name either in main or children.") {player, strings -> + val name = strings.slice(1 until strings.size).joinToString(" ").lowercase() + for (id in 0 until 9000) { + val def = NPCDefinition.forId(id) + if (def.name.isNotBlank() && (def.name.lowercase().contains(name) || name.contains(def.name.lowercase()))) { + notify(player, "$id - ${def.name}") + } + else { + for ((childId,index) in def.childNPCIds?.withIndex() ?: continue) { + val childDef = NPCDefinition.forId(childId) + if (childDef.name.lowercase().contains(name) || name.contains(childDef.name.lowercase())) { + notify(player, "$childId child($id) index $index - ${childDef.name}") + } + } + } + } + } + + define("itemsearch", Privilege.STANDARD, "::itemsearch name", "Searches for items that match the name.") {player, args -> + val itemName = args.copyOfRange(1, args.size).joinToString(" ").lowercase() + for (i in 0 until 15000) { + val name = getItemName(i).lowercase() + if (name.contains(itemName) || itemName.contains(name)) + notify(player, "$i: $name") + } + } + + define("runekit", Privilege.ADMIN, "", "Gives 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 new file mode 100644 index 000000000..05b59c153 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/FunCommandSet.kt @@ -0,0 +1,506 @@ +package core.game.system.command.sets + +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 org.rs09.consts.Sounds +import java.awt.HeadlessException +import java.awt.Toolkit +import java.awt.datatransfer.StringSelection +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("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.lock(6) + n.faceTemporary(player, 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) + } + } + } + } + + + /** + * Transform a player's appearance into that of an NPC. + */ + define("pnpc", Privilege.MODERATOR, "::pnpc NPC ID", "Transforms the player into the given NPC."){ player, args -> + if(args.size < 2){ + reject(player, "Usage: ::pnpc ") + return@define + } + + val pnpc_id = args[1].toIntOrNull() + if(pnpc_id == null){ + reject(player, " must be a valid integer.") + } + + player.appearance.transformNPC(pnpc_id!!) + notify(player,"Transformed into NPC $pnpc_id") + } + + + /** + * Open bank + */ + define("bank", Privilege.ADMIN, "", "Opens your bank."){ player, _ -> + player.getBank().open() + } + + /** + * Toggle invisibility + */ + define("invis", Privilege.ADMIN, "", "Makes you invisible to others."){ player, _ -> + player.isInvisible = !player.isInvisible + notify(player,"You are now ${if (player.isInvisible) "invisible" else "visible"} to others.") + } + + + /** + * Toggle 1-hit kills + */ + define("1hit", Privilege.ADMIN, "", "Makes you kill things in 1 hit."){ player, _ -> + player.setAttribute("1hko", !player.getAttribute("1hko", false)) + notify(player,"1-hit KO mode " + if (player.getAttribute("1hko", false)) "on." else "off.") + } + + + /** + * Toggle god mode + */ + define("god", Privilege.ADMIN, "", "Makes you invulnerable to damage."){ player, _ -> + player.setAttribute("godMode", !player.getAttribute("godMode", false)) + notify(player,"God mode ${if (player.getAttribute("godMode", false)) "enabled." else "disabled."}") + } + + + /** + * Go on Mr Bones' Wild Ride + */ + 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) p.sendChat("I want to get off Mr. Bones Wild Ride.") + p.moveStep() + return !p.appearance.isRidingMinecart + } + }) + } + } + + + /** + * Opens up the makeover interface + */ + 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, description = "Copies your appearance and equipment as JSON to the clipboard."){ player, _ -> + val json = JSONObject() + PlayerSaver(player).saveAppearance(json) + val equipJson = PlayerSaver(player).saveContainer(player.equipment) + json["equipment"] = equipJson + val jsonString = json.toJSONString() + try { + val clpbrd = Toolkit.getDefaultToolkit().systemClipboard + clpbrd.setContents(StringSelection(jsonString), null) + notify(player, "Appearance and equipment copied to clipboard.") + } catch (e: HeadlessException) { + reject(player, "NOTE: Paste will not be available due to remote server.") + } + } + + /** + * Bury inventory at current location + */ + 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 at this spot.","Are you sure?").also { stage++ } + 1 -> options("Yes","No").also { stage++ } + 2 -> when(buttonID){ + 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, hideName: Boolean = false, customMessage: String? = null){ + + val loc = Location.create(player.location) + 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 new file mode 100644 index 000000000..0727f65db --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt @@ -0,0 +1,1142 @@ +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.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 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() { + + /** + * Toggles debug mode + */ + define("debug", Privilege.STANDARD, "", "Toggles debug mode."){ player, _ -> + player.toggleDebug() + } + + 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}") + val accuracy = handler.calculateAccuracy(player) + player.sendMessage("calculateAccuracy: ${accuracy}") + + if (args.size > 1) + { + val npcId: Int = args[1].toInt() + val npc = NPC(npcId) + npc.initConfig() + player.sendMessage("npc: ${npc.name}. npc defence: ${npc.skills.getLevel(Skills.DEFENCE)}") + 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("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 + */ + define("loc", Privilege.STANDARD, "", "Prints quite a lot of information about your current location."){ player, _-> + val l = player.location + val r = player.viewport.region + var obj: Scenery? = null + notify(player,"Absolute: " + l + ", regional: [" + l.localX + ", " + l.localY + "], chunk: [" + l.chunkOffsetX + ", " + l.chunkOffsetY + "], flag: [" + RegionManager.isTeleportPermitted(l) + ", " + RegionManager.getClippingFlag(l) + ", " + RegionManager.isLandscape(l) + "].") + notify(player,"Region: [id=" + l.regionId + ", active=" + r.isActive + ", instanced=" + (r is DynamicRegion) + "], obj=" + RegionManager.getObject(l) + ".") + 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!!))) + 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 + ")" + log(this::class.java, Log.FINE, loc + "; " + player.playerFlags.lastSceneGraph + ", " + l.localX + ", " + l.localY) + try { + val stringSelection = StringSelection(loc) + val clpbrd = Toolkit.getDefaultToolkit().systemClipboard + clpbrd.setContents(stringSelection, null) + notify(player, "Coordinates copied to clipboard.") + } catch (e: HeadlessException) { + reject(player, "NOTE: Paste will not be available due to remote server.") + } + } + + /** + * Tells the player to use loc + */ + define("pos", Privilege.STANDARD){ player, _-> + notify(player, "Do you mean ::loc?") + } + + /** + * Tells the player to use loc + */ + define("coords", Privilege.STANDARD){ player, _-> + notify(player, "Do you mean ::loc?") + } + + 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: ${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/clears your inventory."){player,_-> + player.inventory.clear() + player.inventory.refresh() + } + + /** + * Announces a message in chat (NEWS) + */ + define("announce", Privilege.ADMIN, "::announce String", "Sends the given string as a News message."){_,args -> + val message = args.slice(1 until args.size).joinToString(" ") + Repository.sendNews(message) + } + + /** + * Lists the players currently online + */ + define("players", Privilege.MODERATOR, "", "Lists the online players."){ player, _ -> + val rights = player.rights.ordinal + if (player!!.interfaceManager.isOpened && player.interfaceManager.opened.id != Components.QUESTJOURNAL_SCROLL_275 || player.locks.isMovementLocked || player.locks.isTeleportLocked) { + reject(player, "Please finish what you're doing first.") + } + player.interfaceManager.open(Component(Components.QUESTJOURNAL_SCROLL_275)) + var i = 0 + while (i < 257) { + player.packetDispatch.sendString("", 275, i) + i++ + } + val red = "" + player.packetDispatch.sendString("" + "Players" + "", 275, 2) + var lineStart = 11 + for(p in Repository.players){ + if(!p.isArtificial) + player.packetDispatch.sendString(red + "" + p.username + if(rights > 0) " [ip=" + p.details.ipAddress + ", name=" + p.details.compName + "]" else "",275,lineStart++) + } + } + + /** + * Lists information about a bot + */ + define("botinfo", Privilege.STANDARD, "::botinfo botname", "Prints debug information about a bot"){ player, args -> + val scriptInstances = AIRepository.PulseRepository + + // Find the bot with the given name (non-case sensitive, concat args by space) + val botName = args.slice(1 until args.size).joinToString(" ").lowercase() + val bot = scriptInstances[botName] + if (bot == null) { + reject(player, "No bot with that name found.") + return@define + } + val botInfo = bot.botScript.toString() + // Print the bot's information, max 80chars per line + botInfo.chunked(80).forEach { notify(player, it) } + } + + /** + * Opens the credit/voting shop + */ + define("shop", Privilege.STANDARD, "", "Opens the credit shop."){ player, _ -> + 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 or toggles muting sell offer News announcements. + */ + 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) { + "buying" -> showGeBuy(player) + "selling" -> showGeSell(player) + "search" -> showGeInputDialogue(player, args, ::showOffers) + "bots" -> showGeBots(player) + "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, bots, botsearch, mute" + ) + } + } + /** + * ================================================================================== + */ + + + /** + * List all commands + */ + 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 + 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") + } + + if (page > pages.size) { + reject(player, "That page number is too high, you don't have access to that many.") + } + + val title = "Commands" + if (pages.size > 1) " (${page + 1}/${pages.size})" else "" + var lineId = setupScrollInterface(player, title) + + for(i in pages[page] until end) { + val command = CommandMapping.getCommands()[i] + lineId = displayCommandInScroll(player, command, player.rights.ordinal, 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)) + } + + /** + * Reply to PMs (also enables tab-to-reply) + */ + define("reply", Privilege.STANDARD, "", "Opens a reply prompt to your last DM. Same as pressing tab."){ player, _ -> + if(player.interfaceManager.isOpened){ + reject(player, "Please finish what you're doing first.") + } + if (player.attributes.containsKey("replyTo")) { + player.setAttribute("keepDialogueAlive", true) + val replyTo = player.getAttribute("replyTo", "").replace("_".toRegex(), " ") + sendInputDialogue(player, InputType.MESSAGE ,StringUtils.formatDisplayName(replyTo)){ value -> + CommunicationInfo.sendMessage(player, replyTo.toLowerCase(), value as String) + player.removeAttribute("keepDialogueAlive") + } + } else { + reject(player, "You have not recieved any recent messages to which you can reply.") + } + } + + /** + * Enables client to safety close the currently opened interface or dialogue (esc-to-close plugin) + */ + define("xface", Privilege.STANDARD, "", "Closes the currently opened interface/dialogue."){player, _ -> + player.interfaceManager.close() + player.dialogueInterpreter.close() + } + + define("completediaries", Privilege.ADMIN, "", "Completes all diaries."){player,_ -> + player.achievementDiaryManager.diarys.forEach { + for(level in it.taskCompleted.indices){ + for(task in it.taskCompleted[level].indices){ + it.finishTask(player,level,task) + } + } + } + } + + /** + * 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}" + log.add(locString) + player.setAttribute("loc-log",log) + } + + 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 + + val sb = StringBuilder() + var first = true + for(entry in log){ + if(!first) sb.append("-") + sb.append(entry) + first = false + } + + val stringSelection = StringSelection(sb.toString()) + val clpbrd = Toolkit.getDefaultToolkit().systemClipboard + clpbrd.setContents(stringSelection, null) + + log.clear() + player.setAttribute("loc-log",log) + } + + define("rolltrawlerloot", Privilege.ADMIN, "::rolltrawlerloot ROLL COUNT", "Rolls some trawler loot."){player,args -> + val rolls = if(args.size < 2){ + 100 + } else { + args[1].toString().toInt() + } + TrawlerLoot.addLootAndMessage(player, player.skills.getLevel(Skills.FISHING), rolls, false) + } + + 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, description = "Clears your bank contents."){player,_ -> + player.bank.clear() + player.bank.update() + } + + 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() + setVarp(player, configID, configValue) + } + + 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") + } + val objectID = args[1].toInt() + notify(player, "${VarbitDefinition.forObjectID(SceneryDefinition.forId(objectID).varbitID).varpId}") + } + + 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("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 ") + } + val index = args[1].toIntOrNull() + val value = args[2].toIntOrNull() + + if(index == null || value == null){ + reject(player,"Usage ::setvarbit ") + } + + 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."){ + player,args -> + if(args.size < 4){ + reject(player,"Usage: ::setvarp index offset value") + } + val index = args[1].toIntOrNull() + val offset = args[2].toIntOrNull() + val value = args[3].toIntOrNull() + + if(index == null || offset == null || value == null){ + reject(player,"Usage ::setvarp index offset value") + } + + setVarp(player, index!!, value!! shl offset!!) + } + + define("setvarc", Privilege.ADMIN, "::setvarc VARC ID VALUE") { player, args -> + if(args.size < 3){ + reject(player,"Usage: ::setvarc index value") + } + val index = args[1].toShortOrNull() + val value = args[2].toIntOrNull() + + if(index == null || value == null) { + reject(player,"Usage ::setvarc index value") + } + + player.packetDispatch.sendVarcUpdate(index!!, value!!) + } + + define("grow", Privilege.ADMIN, "", "Grows all planted crops by 1 stage."){ player, _ -> + val state = getOrStartTimer (player)!! + + for(patch in state.getPatches()){ + patch.nextGrowth = System.currentTimeMillis() - 1 + } + + state.run (player) + } + + define("finishbins", Privilege.ADMIN, "", "Finishes any in-progress compost bins."){ player, _ -> + val bins = getOrStartTimer(player).getBins() + for (bin in bins) { + if (!bin.isFinished && bin.isClosed) bin.finish() + } + } + + define("resetbins", Privilege.ADMIN, "", "Resets the player's compost bins to their initial states."){ player, _ -> + val bins = getOrStartTimer(player).getBins() + for (bin in bins) bin.reset() + } + + define("diseasecrops", Privilege.ADMIN, "", "Disease all crops"){ player, _ -> + val state = getOrStartTimer(player) + for (patch in state.getPatches()){ + patch.diseaseMod = -128 + patch.nextGrowth = System.currentTimeMillis() + 1 + } + state.run(player) + } + + define("addcredits", Privilege.ADMIN, description = "Adds 100 ::shop credits to your account."){ player, _ -> + player.details.credits += 100 + } + + 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") + } + + val npcid = args[1].toIntOrNull() ?: reject(player,"Invalid NPC ID.") + + GlobalScope.launch { + for(def in NPCDefinition.getDefinitions().values){ + def ?: continue + def.childNPCIds ?: continue + for(id in def.childNPCIds){ + if(id == npcid){ + notify(player,"Parent NPC: ${def.id}") + return@launch + } + } + } + notify(player,"No parent NPC found.") + } + } + 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( + "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 + } + 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) + player.packetDispatch.sendInterfaceConfig(384, 17, true) + openInterface(player, 384) + } + + define("confirmrules", Privilege.STANDARD) { player, args -> + if(getAttribute(player,"rules:confirmed", false)) + reject(player, "You have already confirmed the rules.") + if(args.size < 2) + reject(player, "Usage: ::confirmrules PIN") + val pin = args[1].toIntOrNull() ?: (-1).also{ reject(player, "Please enter a valid number.") } + if(pin == getAttribute(player, "rules:pin", -1)) + { + player.setAttribute("/save:rules:confirmed", true) + player.interfaceManager.close() + 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) + { + val offerAmounts = HashMap() + val offerPrice = HashMap() + + val offers = GrandExchange.getBotOffers().filter { getItemName(it.itemID).contains(searchTerm, true) || getItemName(it.itemID).equals(searchTerm, true) } + + for(offer in offers) + { + offerAmounts[offer.itemID] = offer.amount + offerPrice[offer.itemID] = offer.offeredValue + } + + 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") + } + showGeBook(player, "Bot Stock - \"$searchTerm\"", leftLines, rightLines) + } + + fun showGeBots(player: Player) + { + val offerAmounts = HashMap() + val offerPrice = HashMap() + + val offers = GrandExchange.getBotOffers() + + for(offer in offers) + { + offerAmounts[offer.itemID] = offer.amount + offerPrice[offer.itemID] = offer.offeredValue + } + + 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") + } + showGeBook(player, "Bot Stock", leftLines, rightLines) + } + + fun showGeSell(player: Player){ + val offerAmounts = HashMap() + val lowestPrice = HashMap() + + val offers = GrandExchange.getValidOffers() + + for(offer in offers) + { + if(!offer.sell) continue + var amount = offerAmounts[offer.itemID] ?: 0 + amount += offer.amountLeft + + var price = lowestPrice[offer.itemID] ?: Integer.MAX_VALUE + if(offer.offeredValue < price) price = offer.offeredValue + + offerAmounts[offer.itemID] = amount + lowestPrice[offer.itemID] = price + } + + 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") + } + showGeBook(player, "Active Sell Offers", leftLines, rightLines) + } + + fun showGeBuy(player: Player){ + val offerAmounts = HashMap() + val highestPrice = HashMap() + + val offers = GrandExchange.getValidOffers() + + for(offer in offers) + { + if(offer.sell) continue + var amount = offerAmounts[offer.itemID] ?: 0 + amount += offer.amountLeft + + var price = highestPrice[offer.itemID] ?: 0 + if(offer.offeredValue > price) price = offer.offeredValue + + offerAmounts[offer.itemID] = amount + highestPrice[offer.itemID] = price + } + + 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") + } + 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) } + 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) + { + var price = sellingLowest[offer.itemID] ?: Int.MAX_VALUE + if(offer.offeredValue < price) price = offer.offeredValue + + var amount = sellingAmount[offer.itemID] ?: 0 + amount += offer.amountLeft + + sellingAmount[offer.itemID] = amount + sellingLowest[offer.itemID] = price + } + else + { + var price = buyingHighest[offer.itemID] ?: 0 + if(offer.offeredValue > price) price = offer.offeredValue + + var amount = buyingAmount[offer.itemID] ?: 0 + amount += offer.amountLeft + + buyingAmount[offer.itemID] = amount + buyingHighest[offer.itemID] = price + } + } + + 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") + } + } + showGeBook(player, "Results for \"$searchTerm\"", leftLines, rightLines) + } + + 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 new file mode 100644 index 000000000..8fd7550ef --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/ModerationCommandSet.kt @@ -0,0 +1,333 @@ +package core.game.system.command.sets + +import core.api.sendMessage +import core.game.node.entity.player.Player +import core.game.node.entity.player.info.Rights +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.plugin.Initializable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import core.ServerConstants +import core.ServerStore +import core.ServerStore.Companion.addToList +import core.auth.UserAccountInfo +import core.game.system.command.CommandMapping +import core.game.system.command.Privilege +import core.game.world.GameWorld +import core.game.world.repository.Repository +import java.io.File +import java.io.FileReader +import java.util.concurrent.TimeUnit +import kotlin.math.abs + +@Initializable +/** + * Moderation commands + * @author Ceikry + */ +class ModerationCommandSet : CommandSet(Privilege.MODERATOR){ + override fun defineCommands() { + val MAX_JAIL_TIME = 1800 //Max jail time (in seconds) + + + /** + * Kick a player + * ============================================================================================================= + */ + 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() + notify(player, "Player ${playerToKick.username} was kicked.") + } else { + reject(player, "ERROR REMOVING PLAYER.") + } + } + /** + * ============================================================================================================= + */ + + /** + * Ban a player + * ============================================================================================================= + */ + define("ban", Privilege.ADMIN, "::ban USERNAME TIME", "Bans the user. Time format: INTd/s/m/h ex: 30d for 30 days."){ player, args -> + val name = args[1] + if(!GameWorld.accountStorage.checkUsernameTaken(name)) { + reject(player, "Invalid username: $name") + } + val playerToKick: Player? = Repository.getPlayerByName(name) + val durationString = args[2] + val durationTokens = durationString.toCharArray() + var intToken = "" + var durationMillis = 0L + var durationUnit: TimeUnit = TimeUnit.NANOSECONDS + for(token in durationTokens){ + if(token.toString().toIntOrNull() != null) intToken += token + else { + val durationInt: Int = (intToken.toIntOrNull() ?: -1).also { if(it == -1) reject(player, "Invalid duration: $intToken") } + durationUnit = when(token) { + 'd' -> TimeUnit.DAYS + 's' -> TimeUnit.SECONDS + 'm' -> TimeUnit.MINUTES + 'h' -> TimeUnit.HOURS + else -> TimeUnit.SECONDS + } + durationMillis = durationUnit.toMillis(durationInt.toLong()) + } + } + + playerToKick?.details?.accountInfo?.banEndTime = System.currentTimeMillis() + durationMillis + playerToKick?.clear() + GameWorld.Pulser.submit(object : Pulse(2) { + override fun pulse(): Boolean { + val info = GameWorld.accountStorage.getAccountInfo(name) + info.banEndTime = System.currentTimeMillis() + durationMillis + GameWorld.accountStorage.update(info) + return true + } + }) + + notify(player, "Banned user $name for $intToken ${durationUnit.name.toLowerCase()}.") + } + /** + * ============================================================================================================= + */ + + /** + * Ban all players on a given IP + * ============================================================================================================= + */ + define("ipban", Privilege.ADMIN, "::ipban IP TIME", "Bans all players on the given ip. Time format: INTd/s/m/h ex: 30d for 30 days."){ player, args -> + val ip = args[1] + val durationString = args[2] + val durationTokens = durationString.toCharArray() + var intToken = "" + var durationMillis = 0L + var durationUnit: TimeUnit = TimeUnit.NANOSECONDS + for(token in durationTokens){ + if(token.toString().toIntOrNull() != null) intToken += token + else { + val durationInt: Int = (intToken.toIntOrNull() ?: -1).also { if(it == -1) reject(player, "Invalid duration: $intToken") } + durationUnit = when(token) { + 'd' -> TimeUnit.DAYS + 's' -> TimeUnit.SECONDS + 'm' -> TimeUnit.MINUTES + 'h' -> TimeUnit.HOURS + else -> TimeUnit.SECONDS + } + durationMillis = durationUnit.toMillis(durationInt.toLong()) + } + } + + val playersToBan = GameWorld.accountStorage.getUsernamesWithIP(ip) + if (playersToBan.isEmpty()) { + reject(player, "No accounts found on IP $ip") + } + + for (p in playersToBan) { + val playerToKick = Repository.getPlayerByName(p) + playerToKick?.details?.accountInfo?.banEndTime = System.currentTimeMillis() + durationMillis + playerToKick?.clear() + GameWorld.Pulser.submit(object : Pulse(2) { + override fun pulse(): Boolean { + val info = GameWorld.accountStorage.getAccountInfo(p) + info.banEndTime = System.currentTimeMillis() + durationMillis + GameWorld.accountStorage.update(info) + return true + } + }) + } + + ServerStore.getArchive("flagged-ips").addToList("ips", ip) + + notify(player, "Banned all accounts on $ip for $intToken ${durationUnit.name.toLowerCase()}.") + } + /** + * ============================================================================================================= + */ + + /** + * Mute a player + * ============================================================================================================= + */ + define("mute", Privilege.MODERATOR, "::mute USERNAME TIME", "Mutes the user. Time format: INTd/s/m/h ex: 30d for 30 days."){ player, args -> + val name = args[1] + if(!GameWorld.accountStorage.checkUsernameTaken(name)) { + reject(player, "Invalid username: $name") + } + val playerToMute: Player? = Repository.getPlayerByName(name) + val durationString = args[2] + val durationTokens = durationString.toCharArray() + var intToken = "" + var durationMillis = 0L + var durationUnit: TimeUnit = TimeUnit.NANOSECONDS + for(token in durationTokens){ + if(token.toString().toIntOrNull() != null) intToken += token + else { + val durationInt: Int = (intToken.toIntOrNull() ?: -1).also { if(it == -1) reject(player, "Invalid duration: $intToken") } + durationUnit = when(token) { + 'd' -> TimeUnit.DAYS + 's' -> TimeUnit.SECONDS + 'm' -> TimeUnit.MINUTES + 'h' -> TimeUnit.HOURS + else -> TimeUnit.SECONDS + } + durationMillis = durationUnit.toMillis(durationInt.toLong()) + } + } + + playerToMute?.details?.accountInfo?.muteEndTime = System.currentTimeMillis() + durationMillis + if(playerToMute == null) { //Player was offline at the time + GameWorld.Pulser.submit(object : Pulse(2) { + override fun pulse(): Boolean { + val info = GameWorld.accountStorage.getAccountInfo(name) + info.muteEndTime = System.currentTimeMillis() + durationMillis + GameWorld.accountStorage.update(info) + return true + } + }) + } + + notify(player, "Muted user $name for $intToken ${durationUnit.name.toLowerCase()}.") + } + /** + * ============================================================================================================= + */ + + /** + * Jail a player + * ============================================================================================================= + */ + define("jail", Privilege.MODERATOR, "::jail SECONDS USERNAME", "Sends the player to the jail cells in Varrock."){player,args -> + if(args.size < 3) { + reject(player,"Usage: ::jail ") + } + + val timeSeconds = args[1].toIntOrNull() + if(timeSeconds == null){ + reject(player, " Must be a valid integer!") + } + if(timeSeconds!! > MAX_JAIL_TIME){ + reject(player, "Maximum jail time is $MAX_JAIL_TIME seconds.") + } + val name = args.slice(2 until args.size).joinToString("_") + val otherPlayer = Repository.getPlayerByName(name) + if(otherPlayer == null){ + reject(player, "Can not find $name in the player list!") + } + + if (otherPlayer?.rights == Rights.ADMINISTRATOR){ + reject(player, "You cannot jail $name, they are a god. Nice try though ${player.username}!") + } + + notify(player, "Jailing ${otherPlayer!!.username} for $timeSeconds seconds.") + notify(otherPlayer, "${player.username} has jailed you for $timeSeconds seconds.") + GameWorld.Pulser.submit(object : Pulse(3){ + val originalLoc = otherPlayer.location + val releaseTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(timeSeconds.toLong()) + override fun pulse(): Boolean { + val done = System.currentTimeMillis() >= releaseTime + if(otherPlayer.location != Location.create(3228, 3407, 0)){ + otherPlayer.properties.teleportLocation = Location.create(3228, 3407, 0) + } + if(done){ + otherPlayer.properties.teleportLocation = originalLoc + } + return done + } + }) + } + /** + * ============================================================================================================= + */ + + define("modcr", Privilege.MODERATOR, "::modcr user_name amount", "Modifies user_name's credits by the given amount.") {player, args -> + val username = (args.getOrNull(1) ?: "").lowercase() + val amount = args.getOrNull(2)?.toIntOrNull() ?: Integer.MIN_VALUE + + if (username.isEmpty()) { + reject(player, "Usage: ::modcr user_name amount") + return@define + } + + if (amount == Integer.MIN_VALUE) { + reject(player, "Usage ::modcr user_name amount") + return@define + } + + val p = Repository.getPlayerByName(username) + val info: UserAccountInfo = if (p == null) + GameWorld.accountStorage.getAccountInfo(username) + else + p.details.accountInfo + + if (info.isDefault()) { + reject(player, "The user you specified ($username) does not exist.") + return@define + } + + info.credits += amount + if (p == null) + GameWorld.accountStorage.update(info) + else + 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)}.") + } + + define("csvmodcr", Privilege.ADMIN, "::csvmodcr filename", "Awards credits based on a csv list from a file. Relative to data dir.") {player, args -> + val filename = args.getOrNull(1) ?: "" + + if (filename.isEmpty()) { + reject(player, "Usage: ::csvmodcr filename") + return@define + } + + GlobalScope.launch { + val f = File(ServerConstants.DATA_PATH + File.separator + filename) + if (!f.exists()) { + notify(player, "Unable to locate file $filename.") + notify(player, "Note: The file must be in ${ServerConstants.DATA_PATH}") + return@launch + } + val lines: List + withContext(Dispatchers.IO) { + FileReader(f).use { + lines = f.readLines() + } + } + for (line in lines) { + if (line.startsWith("#")) continue + val tokens = line.split(",") + val username = tokens[0].lowercase().trim().replace(" ", "_") + val amount = tokens[1].trim() + CommandMapping.get("modcr")?.handle?.invoke(player, arrayOf("", username, amount)) + } + } + } + + define("getattribute", Privilege.ADMIN, "::getattribute username attribute", "Gets the value of an attribute for a player.") {player, args -> + val username = args.getOrNull(1) ?: "" + val attribute = args.getOrNull(2) ?: "" + if (username.isEmpty() || attribute.isEmpty()) { + reject(player, "Usage: ::getattribute username attribute") + return@define + } + val p = Repository.getPlayerByName(username) + if (p == null) { + reject(player, "Unable to find player $username.") + return@define + } + val value = p.getAttribute(attribute) + if (value == null) { + reject(player, "Unable to find attribute $attribute for player $username.") + return@define + } + notify(player, "Attribute $attribute for player $username is $value.") + } + } +} 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 new file mode 100644 index 000000000..c8c414c38 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt @@ -0,0 +1,163 @@ +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.plugin.Initializable +import core.game.system.command.Privilege +import core.game.world.repository.Repository +import core.tools.Log + +@Initializable +class QuestCommandSet : CommandSet(Privilege.ADMIN){ + override fun defineCommands() { + /** + * Completes all quests + */ + define("allquest", description = "Instantly completes every quest for your account."){player,_-> + for (quest in QuestRepository.getQuests().values) { + 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", + 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", 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") + 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, description = "Opens the quests interface listing current implementations."){ player, _ -> + sendQuests(player) + } + } + + /** + * Sends the list of quests. + * @param player the player. + */ + private fun sendQuests(player: Player?) { + player!!.interfaceManager.open(Component(275)) + for (i in 0..310) { + player.packetDispatch.sendString("", 275, i) + } + var lineId = 11 + 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.quest + " ", 275, lineId++) + } + } + + /** + * Sends the list of quests with debug information + * @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(admin: Player?, lookupUser: Player?, questFilter: String? = null) { + admin!!.interfaceManager.open(Component(275)) + + // clear interface lines + for (i in 0..310) { + admin.packetDispatch.sendString("", 275, i) + } + + var lineId = 11 + + // 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 new file mode 100644 index 000000000..0f2e32426 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/SlayerCommandSet.kt @@ -0,0 +1,58 @@ +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.game.system.command.Privilege +import core.plugin.Initializable +import core.api.* + +@Initializable +class SlayerCommandSet : CommandSet(Privilege.ADMIN){ + override fun defineCommands() { + /** + * Finishes a player's slayer task (the correct way) + */ + 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) + finisher.isRespawn = false + finisher.init() + } + + /** + * Set slayer points + */ + define("setslayerpoints", usage = "::setslayerpoints amount", description = "Sets your Slayer points total to amount."){player,args -> + if(args.size < 2){ + reject(player,"Usage: ::setslayerpoints amount") + } + + val amount = args[1].toIntOrNull() + if(amount == null){ + reject(player,"Amount needs to be a valid integer!") + } + + 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) + } + } +} diff --git a/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt b/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt new file mode 100644 index 000000000..468bdaf96 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt @@ -0,0 +1,146 @@ +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.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 + +@Initializable +class SpawnCommandSet : CommandSet(Privilege.ADMIN){ + override fun defineCommands() { + /** + * Spawns an npc with the given ID + */ + 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: id (required) amount (optional) isWalks (optional)") + return@define + } + 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) + } + + /** + * Spawns an item with the given ID + */ + 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 + } + val id = args[1].toIntOrNull() ?: return@define + var amount = (args.getOrNull(2) ?: "1").toInt() + 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) + } + + + /** + * 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", 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`) + log(this::class.java, Log.FINE, "object = $`object`") + } + + 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 + } + val beginId = args[1].toIntOrNull() ?: return@define + val endId = args[2].toIntOrNull() ?: return@define + val type = args[3].toIntOrNull() ?: return@define + val rotation = args[4].toIntOrNull() ?: return@define + for(i in 0..10) { + SceneryBuilder.add(Scenery(29447 + i, player.location.transform(i, -1, 0))) + } + for(i in beginId..endId) { + val j = i - beginId + val scenery = Scenery(i, player.location.transform(j % 10, j / 10, 0), type, rotation) + SceneryBuilder.add(scenery) + if(j % 10 == 0) { + SceneryBuilder.add(Scenery(29447 + (j / 10) % 10, player.location.transform(-1, j/10, 0))) + } + } + } + } +} diff --git a/Server/src/main/core/game/system/command/sets/StatAttributeKeys.kt b/Server/src/main/core/game/system/command/sets/StatAttributeKeys.kt new file mode 100644 index 000000000..431cb84be --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/StatAttributeKeys.kt @@ -0,0 +1,17 @@ +package core.game.system.command.sets + +const val STATS_BASE = "stats_manager" +const val STATS_ENEMIES_KILLED = "enemies_killed" +const val STATS_DEATHS = "deaths" +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" +const val FISHING_TRAWLER_SHIPS_SANK = "fishing-trawler-sank" diff --git a/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt b/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt new file mode 100644 index 000000000..ad8cf75da --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt @@ -0,0 +1,198 @@ +package core.game.system.command.sets + +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.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) +val GARGOYLE_IDS = intArrayOf(NPCs.GARGOYLE_1610, NPCs.GARGOYLE_1827, NPCs.GARGOYLE_6389) +val SPIRITUAL_MAGE_IDS = intArrayOf(NPCs.SPIRITUAL_MAGE_6221, NPCs.SPIRITUAL_MAGE_6231, NPCs.SPIRITUAL_MAGE_6257, NPCs.SPIRITUAL_MAGE_6278) +val GREEN_DRAGON_IDS = intArrayOf(NPCs.GREEN_DRAGON_941, NPCs.GREEN_DRAGON_4677, NPCs.GREEN_DRAGON_4678, NPCs.GREEN_DRAGON_4679, NPCs.GREEN_DRAGON_4680, NPCs.BRUTAL_GREEN_DRAGON_5362) +val BLUE_DRAGON_IDS = intArrayOf(NPCs.BLUE_DRAGON_55, NPCs.BLUE_DRAGON_4681, NPCs.BLUE_DRAGON_4682, NPCs.BLUE_DRAGON_4683, NPCs.BLUE_DRAGON_4684, NPCs.BLUE_DRAGON_5178) +val RED_DRAGON_IDS = intArrayOf(NPCs.RED_DRAGON_53, NPCs.RED_DRAGON_4669, NPCs.RED_DRAGON_4670, NPCs.RED_DRAGON_4671, NPCs.RED_DRAGON_4672) +val BLACK_DRAGON_IDS = intArrayOf(NPCs.BLACK_DRAGON_54, NPCs.BLACK_DRAGON_4673, NPCs.BLACK_DRAGON_4674, NPCs.BLACK_DRAGON_4675, NPCs.BLACK_DRAGON_4676) +val BRONZE_DRAGON_IDS = intArrayOf(NPCs.BRONZE_DRAGON_1590) +val IRON_DRAGON_IDS = intArrayOf(NPCs.IRON_DRAGON_1591) +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 FAKE_CONTENT = arrayOf( + PageSet(Page(), Page()), + PageSet(Page(), Page()), + PageSet(Page(), Page()), +) + +@Initializable +class StatsCommandSet : CommandSet(Privilege.STANDARD) { + 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 + } + + // 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 + } + + 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 new file mode 100644 index 000000000..d4e48ca9c --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/SystemCommandSet.kt @@ -0,0 +1,308 @@ +package core.game.system.command.sets + +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.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 +class SystemCommandSet : CommandSet(Privilege.ADMIN) { + override fun defineCommands() { + /** + * Start an update countdown + */ + 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()) + } + SystemManager.flag(SystemState.UPDATING) + } + + /** + * Cancel an update countdown + */ + define("cancelupdate", description = "Cancels any active update countdown.") { player, _ -> + SystemManager.getUpdater().cancel() + } + + + /** + * Allows a player to reset their password + */ + 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() + runTask(player) { + if (GameWorld.authenticator.checkPassword(player, pass)) { + sendInputDialogue(player, InputType.STRING_SHORT, "Enter New Password:") { value2 -> + val newPass = value2.toString() + if (pass == newPass) { + sendDialogue(player, "Failed: Passwords Match") + } else if (newPass.length !in 5..20) { + sendDialogue(player, "Failed: Password Too Long Or Too Short") + } else if (newPass == player.details.accountInfo.username) { + sendDialogue(player, "Failed: Password Is Username") + } else { + GameWorld.authenticator.updatePassword(player.details.accountInfo.username, newPass) + sendDialogue(player, "Success: Password Updated!") + } + } + } else { + sendDialogue(player, "Fail: Wrong Password.") + } + } + } + } + + /** + * Allows an Administrator to reset a password + */ + define("setpasswordother", Privilege.ADMIN, "::resetpasswordother USERNAME NEW", "Gives the username password NEW.") { player, args -> + if (args.size != 3) { + reject(player, "Usage: ::resetpasswordother user new", "WARNING: THIS IS PERMANENT.", "WARNING: PASSWORD CAN NOT CONTAIN SPACES.") + } + val otherUser = args[1] + val newPass = args[2] + + if (GameWorld.accountStorage.checkUsernameTaken(otherUser)) { + + if (newPass.length < 5 || newPass.length > 20) { + reject(player, "NEW PASSWORD MUST BE BETWEEN 5 AND 20 CHARACTERS") + } + + if (newPass == otherUser) { + reject(player, "PASSWORD CAN NOT BE SAME AS USERNAME.") + } + + GameWorld.authenticator.updatePassword(otherUser, newPass) + notify(player, "Password updated successfully.") + + } else { + reject(player, "USER DOES NOT EXIST!") + } + } + + define("giveitem", Privilege.ADMIN, "::giveitem USERNAME ITEM ID AMOUNT", "Gives the user the amount of the given item.") { player, args -> + if (args.size == 3 || args.size == 4) { + val victim = Repository.getPlayerByName(args[1]) + val itemID = args[2].toIntOrNull() + val amount = args.getOrNull(3)?.toIntOrNull() ?: 1 + + if (victim == null) { + reject(player, "INVALID TARGET USERNAME.") + } + + if (itemID == null || itemID <= 0 || itemID > ItemDefinition.getDefinitions().size) { + reject(player, "INVALID ITEM ID ENTERED.") + } + + if (amount > Int.MAX_VALUE || amount <= 0) { + reject(player, "INVALID ITEM ID ENTERED.") + } + + val item = Item(itemID!!, amount) + val invFull = victim!!.inventory.isFull + val syntax = if (amount > 1) "items" else "item" + + if (invFull) { + victim.bank.add(item) + } else { + victim.inventory.add(item) + } + + notify(player, "Successfully gave ${victim.username} $amount ${item.name}. ${if (invFull) "The $syntax were sent to their bank." else ""}") + notify(victim, "You received $amount ${item.name} from ${player.username}. ${if (invFull) "The $syntax were placed in your bank." else ""}") + + } else { + reject(player, "WRONG USAGE. USE giveitem target itemID || giveitem target itemID amt") + } + } + + define("removeitem", Privilege.ADMIN, "::removeitem LOC USERNAME ITEM ID AMOUNT", "LOC = bank,inventory,equipment") { player, args -> + if (args.size == 4 || args.size == 5) { + val itemLoc = args[1].toLowerCase() + val victim = Repository.getPlayerByName(args[2]) + val itemID = args[3].toIntOrNull() + var amount = args.getOrNull(4)?.toIntOrNull() ?: 1 + + if (victim == null) { + reject(player, "INVALID TARGET USERNAME.") + } + + if (itemID == null || itemID <= 0 || itemID > ItemDefinition.getDefinitions().size) { + reject(player, "INVALID ITEM ID ENTERED.") + } + + if (amount > Int.MAX_VALUE || amount <= 0) { + reject(player, "INVALID ITEM AMOUNT ENTERED.") + } + + val item = Item(itemID!!, amount) + var totalItemAmount = 0 + + when (itemLoc) { + "i", "inv", "inventory" -> { + totalItemAmount = victim!!.inventory.getItem(item).amount + victim.inventory.remove(item) + } + "b", "bk", "bank" -> { + totalItemAmount = victim!!.bank.getItem(item).amount + victim.bank.remove(item) + } + "e", "equip", "equipment" -> { + totalItemAmount = victim!!.equipment.getItem(item).amount + victim.equipment.remove(item) + } + else -> reject(player, "INVALID ITEM LOCATION ENTERED. USE: ", "i, inv, inventory | b, bk, bank | e, equip, equipment") + } + + if (amount > totalItemAmount) { + amount = totalItemAmount + } + + notify(player, "Successfully removed $amount ${item.name} from ${victim!!.username}.") + notify(victim, "${player.username} removed $amount ${item.name} from your inventory.") + + } else { + reject(player, "WRONG USAGE. USE removeitem itemLoc target itemID || removeitem itemLoc target itemID amt", + "ItemLoc: inv = inventory | equip = equipment | bank |") + } + } + + define("removeitemall", Privilege.ADMIN, "::removeitemall USERNAME ITEM ID", "Removes ALL of a given item from the player.") { player, args -> + if (args.size == 3) { + val victim = Repository.getPlayerByName(args[1]) + val itemID = args[2].toIntOrNull() + + if (victim == null) { + reject(player, "INVALID TARGET USERNAME.") + } + + if (itemID == null || itemID <= 0 || itemID > ItemDefinition.getDefinitions().size) { + reject(player, "INVALID ITEM ID ENTERED.") + } + + /* Handles removing the non noted version */ + val itemInv = Item(itemID!!) + /* Handles removing the noted version */ + val itemNote = Item(itemInv.noteChange) + + val untotal = victim!!.inventory.getAmount(itemID) + victim.bank.getAmount(itemID) + + victim.equipment.getAmount(itemID) + + val nototal = victim.inventory.getAmount(itemNote) + victim.bank.getAmount(itemNote) + + victim.equipment.getAmount(itemNote) + + val eqtotal = victim.equipment.getAmount(itemID) + victim.equipment.getAmount(itemNote) + + val total = untotal + nototal + eqtotal + + if (total == 0) { + reject(player, "USER HAS NONE OF THOSE ITEMS.") + } + + victim.inventory.remove(Item(itemID, victim.inventory.getAmount(itemID))) + victim.bank.remove(Item(itemID, victim.bank.getAmount(itemID))) + victim.equipment.remove(Item(itemID, victim.equipment.getAmount(itemID))) + + victim.inventory.remove(Item(itemInv.noteChange, victim.inventory.getAmount(itemNote))) + victim.bank.remove(Item(itemInv.noteChange, victim.bank.getAmount(itemNote))) + victim.equipment.remove(Item(itemInv.noteChange, victim.equipment.getAmount(itemNote))) + + notify(player, "Successfully removed $total ${itemInv.name} from ${victim.username}.") + notify(victim, "${player.username} removed $total ${itemInv.name} from your account.") + + } else { + reject(player, "WRONG USAGE. USE removeitemall target itemID") + } + } + + define("potato", Privilege.ADMIN, "", "Gives you a rotten potato.") { player, _ -> + player.inventory.add(Item(Items.ROTTEN_POTATO_5733)) + } + + 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 new file mode 100644 index 000000000..30e4107c9 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt @@ -0,0 +1,211 @@ +package core.game.system.command.sets + +import core.game.node.entity.player.link.TeleportManager +import core.game.world.map.Location +import core.game.world.map.RegionManager +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){ + override fun defineCommands() { + /** + * Allows teleporting by location name + */ + define("to", Privilege.ADMIN, "::to String", "See ServerConstants.TELEPORT_DESTINATIONS"){player,args -> + var destination: Location? = null + val place = args.slice(1 until args.size).joinToString(" ") + for (destinations in ServerConstants.TELEPORT_DESTINATIONS) { + var i = 1 + while (i < destinations.size) { + if (place == destinations[i]) { + destination = destinations[0] as Location + break + } + i++ + } + } + if (destination != null) { + player.teleporter.send(destination, TeleportManager.TeleportType.NORMAL) + } else { + reject(player,"Could not locate teleport destination [name=$place]!") + } + } + + /** + * Teleport to location using coordinates + */ + define("tele", Privilege.ADMIN, "::tele X Y Z OR JAGCOORD", "JAGCOORD is Z_REGIONX_REGIONY_LOCALX_LOCALY"){player,args -> + if (args.size == 2 && args[1].contains(",")) { + val args2 = args[1].split(",".toRegex()).toTypedArray() + val x = args2[1].toInt() shl 6 or args2[3].toInt() + val y = args2[2].toInt() shl 6 or args2[4].toInt() + val z = args2[0].toInt() + player.properties.teleportLocation = Location.create(x, y, z) + return@define + } + if (args.size == 2 && args[1].contains("_")) + { + val tokens = args[1].split("_") + when (tokens.size) { + 2 -> { + val regionX = tokens[0].toInt() + val regionY = tokens[1].toInt() + + player.properties.teleportLocation = Location.create((regionX shl 6) or 15, (regionY shl 6) or 15, 0) + } + 3 -> { + val plane = tokens[0].toInt() + val regionX = tokens[1].toInt() + val regionY = tokens[2].toInt() + + player.properties.teleportLocation = Location.create((regionX shl 6) or 15, (regionY shl 6) or 15, plane) + } + 4 -> { + val regionX = tokens[0].toInt() + val regionY = tokens[1].toInt() + val offsetX = tokens[2].toInt() + val offsetY = tokens[3].toInt() + + val xCoord = (regionX shl 6) or offsetX + val yCoord = (regionY shl 6) or offsetY + + player.properties.teleportLocation = Location.create(xCoord, yCoord, 0) + } + 5 -> { + val plane = tokens[0].toInt() + val regionX = tokens[1].toInt() + val regionY = tokens[2].toInt() + val offsetX = tokens[3].toInt() + val offsetY = tokens[4].toInt() + + val xCoord = (regionX shl 6) or offsetX + val yCoord = (regionY shl 6) or offsetY + + player.properties.teleportLocation = Location.create(xCoord, yCoord, plane) + } + else -> reject(player, "Usage: regionX_regionY OR regionX_regionY_offsetX_offsetY") + } + return@define + } + if (args.size < 2) { + reject(player,"Usage: x,y,(optional)z OR regionX_regionY OR regionX_regionY_offsetX_offsetY") + } + player.properties.teleportLocation = Location.create(args[1].toInt(), args[2].toInt(), if (args.size > 3) args[3].toInt() else 0) + } + + /** + * Teleport to the first object with the given name in the given regionX regionY + */ + define("teleobj", Privilege.ADMIN, "::teleobj RX_RY OBJ NAME", "Teleports to the first object with the given name."){player, args -> + if(args.size < 3) reject(player, "Usage: regionX_regionY Object Name") + var objName = "" + for(i in 2 until args.size) objName += (args[i] + if(i + 1 == args.size) "" else " ") + + val tokens = args[1].split("_") + if(tokens.size != 2) reject(player, "Usage: regionX_regionY Object Name") + val regionX = tokens[0].toInt() + val regionY = tokens[1].toInt() + + val regionId = (regionX shl 8) or regionY + 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.childs != null) { + for (obj in parent.childs.filterNotNull()) { + if (obj.name.equals(objName, ignoreCase = true)) { + player.properties.teleportLocation = obj.location + return@launch + } + } + } else { + if (parent.name.equals(objName, ignoreCase = true)) { + player.properties.teleportLocation = parent.location + return@launch + } + } + } + } + } + } + } + + /** + * 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 + */ + define("teleto", Privilege.ADMIN, "::teleto USERNAME", "Teleports to the named player."){player,args -> + if (args.size < 1) { + reject(player,"syntax error: name") + } + val n = args.slice(1 until args.size).joinToString("_") + val target = Repository.getPlayerByName(n) + if (target == null) { + reject(player,"syntax error: name") + } + if (target!!.getAttribute("fc_wave") != null) { + reject(player,"You cannot teleport to a player who is in the Fight Caves.") + } + player.properties.teleportLocation = target.location + } + + + /** + * Teleport a specific player to you + */ + define("teletome", Privilege.ADMIN, "::teletome USERNAME", "Teleports the given user to you."){player,args -> + if (args.size < 1) { + reject(player,"syntax error: name") + } + val n = args.slice(1 until args.size).joinToString("_") + val target = Repository.getPlayerByName(n) + if (target == null) { + reject(player,"syntax error: name") + } + if (target!!.getAttribute("fc_wave") != null) { + reject(player,"You cannot teleport to a player who is in the Fight Caves.") + } + target.properties.teleportLocation = player.location + } + + + /** + * Teleports to the server's home location + */ + define("home", Privilege.ADMIN, "", "Teleports to ServerConstants.HOME_LOCATION"){player,_ -> + player.properties.teleportLocation = ServerConstants.HOME_LOCATION + } + } +} diff --git a/Server/src/main/java/core/game/system/communication/ClanEntry.java b/Server/src/main/core/game/system/communication/ClanEntry.java similarity index 97% rename from Server/src/main/java/core/game/system/communication/ClanEntry.java rename to Server/src/main/core/game/system/communication/ClanEntry.java index 55d2280cc..135638d84 100644 --- a/Server/src/main/java/core/game/system/communication/ClanEntry.java +++ b/Server/src/main/core/game/system/communication/ClanEntry.java @@ -1,7 +1,7 @@ package core.game.system.communication; import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; /** * Represents a player in a clan chat. diff --git a/Server/src/main/java/core/game/system/communication/ClanMember.java b/Server/src/main/core/game/system/communication/ClanMember.java similarity index 100% rename from Server/src/main/java/core/game/system/communication/ClanMember.java rename to Server/src/main/core/game/system/communication/ClanMember.java diff --git a/Server/src/main/java/core/game/system/communication/ClanRank.java b/Server/src/main/core/game/system/communication/ClanRank.java similarity index 100% rename from Server/src/main/java/core/game/system/communication/ClanRank.java rename to Server/src/main/core/game/system/communication/ClanRank.java diff --git a/Server/src/main/core/game/system/communication/ClanRepository.java b/Server/src/main/core/game/system/communication/ClanRepository.java new file mode 100644 index 000000000..58ec6708c --- /dev/null +++ b/Server/src/main/core/game/system/communication/ClanRepository.java @@ -0,0 +1,577 @@ +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; +import core.game.node.entity.player.info.PlayerDetails; +import core.game.node.entity.player.info.Rights; +import proto.management.ClanJoinNotification; +import proto.management.ClanLeaveNotification; +import core.game.node.entity.player.info.PlayerMonitor; +import core.game.world.GameWorld; +import core.game.world.repository.Repository; +import core.net.amsc.WorldCommunicator; +import core.net.packet.PacketRepository; +import core.net.packet.context.ClanContext; +import core.net.packet.context.MessageContext; +import core.net.packet.out.CommunicationMessage; +import core.net.packet.out.UpdateClanChat; +import core.worker.ManagementEvents; + +import java.util.*; + +/** + * Handles clan communication. + * @author Emperor + */ +public final class ClanRepository { + + /** + * The maximum amount of members to be in a clan chat. + */ + private static final int MAX_MEMBERS = 100; + + /** + * The clan repository. + */ + private static final Map CLAN_REPOSITORY = new HashMap<>(); + + /** + * The name of the clan owner. + */ + private final String owner; + + /** + * The clan name. + */ + private String name = "Chat disabled"; + + /** + * The rank required for joining. + */ + private ClanRank joinRequirement = ClanRank.FRIEND; + + /** + * The rank required for messaging. + */ + private ClanRank messageRequirement = ClanRank.NONE; + + /** + * The rank required for kicking members. + */ + private ClanRank kickRequirement = ClanRank.OWNER; + + /** + * The rank required for loot-share. + */ + private ClanRank lootRequirement = ClanRank.ADMINISTRATOR; + + /** + * The members mapping. + */ + private final Map ranks = new HashMap<>(); + + /** + * The banned players. + */ + private final Map banned = new HashMap<>(); + + /** + * The players who are currently in the friends chat. + */ + private List players = new ArrayList<>(MAX_MEMBERS); + + /** + * The current clan wars activity. + */ + private ActivityPlugin clanWar; + + /** + * Constructs a new {@code ClanRepository} {@code Object}. + * @param owner The owner of the clan. + */ + public ClanRepository(String owner) { + this.owner = owner; + } + + /** + * Enters the clan chat. + * @param player The player. + * @return {@code True} if the player successfully entered the clan chat. + */ + public boolean enter(Player player) { + 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; + } + if (!player.getName().equals(owner) && player.getDetails().getRights() != Rights.ADMINISTRATOR) { + if (isBanned(player.getName())) { + player.getPacketDispatch().sendMessage("You are temporarily banned from this clan channel.:clan:"); + return false; + } + Player o = Repository.getPlayerByName(owner); + if (o != null) { + if (o.getCommunication().getBlocked().contains(player.getName())) { + player.getPacketDispatch().sendMessage("You do not have a high enough rank to join this clan channel.:clan:"); + return false; + } + } + ClanRank rank = getRank(player); + if (rank.ordinal() < joinRequirement.ordinal()) { + player.getPacketDispatch().sendMessage("You do not have a high enough rank to join this clan channel.:clan:"); + return false; + } + } + ClanEntry entry = new ClanEntry(player); + if (!players.contains(entry)) { + players.add(entry); + } + + ClanJoinNotification.Builder event = ClanJoinNotification.newBuilder(); + event.setUsername(player.getName()); + event.setWorld(GameWorld.getSettings().getWorldId()); + event.setClanName(owner); + ManagementEvents.publish(event.build()); + + player.getPacketDispatch().sendMessage("Now talking in clan channel " + name + ":clan:"); + player.getPacketDispatch().sendMessage("To talk, start each line of chat with the / symbol.:clan:"); + update(); + return true; + } + + /** + * Cleans the chat from all players that shouldn't be in it. + */ + public void clean(boolean disable) { + if (WorldCommunicator.isEnabled()) { + return; + } + for (Iterator it = players.iterator(); it.hasNext();) { + ClanEntry entry = it.next(); + Player player = entry.getPlayer(); + boolean remove = disable; + if (!remove) { + remove = getRank(player).ordinal() < joinRequirement.ordinal(); + } + if (remove) { + leave(player, false); + player.getCommunication().setClan(null); + it.remove(); + } + } + if (players.isEmpty()) { + banned.clear(); + } + update(); + } + + /** + * Checks if a player is banned. + * @param name The player's name. + * @return {@code True} if so. + */ + public boolean isBanned(String name) { + if (banned.containsKey(name)) { + long time = banned.get(name); + if (time > System.currentTimeMillis()) { + return true; + } + banned.remove(name); + } + return false; + } + + /** + * Sends a message to all players in the chat. + * @param player The player sending the message. + * @param message The message to send. + */ + public void message(Player player, String message) { + if (player.getLocks().isLocked("cc_message") || isBanned(player.getName())) { + return; + } + player.getLocks().lock("cc_message", 1); + if (!player.getName().equals(owner) && player.getDetails().getRights() != Rights.ADMINISTRATOR) { + ClanRank rank = getRank(player); + if (rank.ordinal() < messageRequirement.ordinal()) { + player.getPacketDispatch().sendMessage("You do not have a high enough rank to talk in this clan channel.:clan:"); + return; + } + } + PlayerMonitor.logChat(player, "clan", message); + for (Iterator it = players.iterator(); it.hasNext();) { + ClanEntry entry = it.next(); + Player p = entry.getPlayer(); + if (p != null) { + PacketRepository.send(CommunicationMessage.class, new MessageContext(p, player, MessageContext.CLAN_MESSAGE, message)); + } + } + } + + /** + * Kicks a player from this chat. + * @param player the player. + * @param target the victim. + */ + public void kick(Player player, Player target) { + ClanRank rank = getRank(player); + if (target.getDetails().getRights() == Rights.ADMINISTRATOR) { + player.sendMessage("You can't kick an administrator.:clan:"); + return; + } + if (target.getName().equals(player.getName())) { + player.sendMessage("You can't kick yourself.:clan:"); + return; + } + if (player.getDetails().getRights() != Rights.ADMINISTRATOR && rank.ordinal() < kickRequirement.ordinal()) { + player.getPacketDispatch().sendMessage("You do not have a high enough rank to kick in this clan channel.:clan:"); + return; + } + if (target.getName().equals(owner)) { + player.getPacketDispatch().sendMessage("You can't kick the owner of this clan channel.:clan:"); + return; + } + for (ClanEntry e : players) { + PacketRepository.send(CommunicationMessage.class, new MessageContext(e.getPlayer(), player, MessageContext.CLAN_MESSAGE, "[Attempting to kick/ban " + target.getUsername() + " from this Clan Chat.]")); + } + leave(target, true, "You have been kicked from the channel.:clan:"); + target.getCommunication().setClan(null); + banned.put(target.getName(), System.currentTimeMillis() + (3_600_000)); + } + + /** + * Represents the method to leave a clan. + * @param player the player. + * @param remove If the player should be removed from the list. + */ + public void leave(Player player, boolean remove) { + leave(player, remove, "You have left the channel.:clan:"); + } + + /** + * Represents the method to leave a clan. + * @param player the player. + * @param remove If the player should be removed from the list. + */ + public void leave(Player player, boolean remove, String message) { + if (remove) { + players.remove(new ClanEntry(player)); + update(); + if (players.size() < 1) { + banned.clear(); + } + } + PacketRepository.send(UpdateClanChat.class, new ClanContext(player, this, true)); + player.getPacketDispatch().sendMessage(message); + if (clanWar != null && !isDefault()) { + clanWar.fireEvent("leavefc", player); + } + + ClanLeaveNotification.Builder event = ClanLeaveNotification.newBuilder(); + event.setClanName(owner); + event.setUsername(player.getName()); + event.setWorld(GameWorld.getSettings().getWorldId()); + ManagementEvents.publish(event.build()); + } + + /** + * Ranks a member of this chat. + * @param name The name of the member. + * @param rank The rank to set. + */ + public void rank(String name, ClanRank rank) { + boolean update; + if (rank == ClanRank.NONE) { + update = ranks.remove(name) != null; + } else { + update = ranks.put(name, rank) != rank; + } + if (update) { + clean(false); + } + } + + /** + * Updates the clan chat for all players in this clan. + */ + public void update() { + for (Iterator it = players.iterator(); it.hasNext();) { + ClanEntry e = it.next(); + if (e.getWorldId() == GameWorld.getSettings().getWorldId() && e.getPlayer() != null) { + PacketRepository.send(UpdateClanChat.class, new ClanContext(e.getPlayer(), this, false)); + } + } + } + + /** + * Gets the clan rank for the given clan entry. + * @param entry The clan entry. + * @return The rank. + */ + public ClanRank getRank(ClanEntry entry) { + if (entry.getPlayer() != null) { + return getRank(entry.getPlayer()); + } + ClanRank rank = ranks.get(entry.getName()); + if (rank == null) { + return ClanRank.NONE; + } + return rank; + } + + /** + * Gets the rank of the player. + * @param player The player. + * @return The clan rank. + */ + public ClanRank getRank(Player player) { + ClanRank rank = ranks.get(player.getName()); + if (player.getDetails().getRights() == Rights.ADMINISTRATOR && !player.getName().equals(owner)) { + return ClanRank.ADMINISTRATOR; + } + if (rank == null) { + if (player.getName().equals(owner)) { + return ClanRank.OWNER; + } + return ClanRank.NONE; + } + return rank; + } + + /** + * Opens the clan settings for the player. + * @param player The player. + */ + public static void openSettings(Player player) { + player.getInterfaceManager().open(new Component(590)); + ClanRepository c = get(player.getName()); + if (c != null) { + c.updateSettings(player); + } + } + + /** + * Updates the clan settings for the player. + * @param player The player. + */ + public void updateSettings(Player player) { + player.getPacketDispatch().sendString(name, 590, 22); + // player.getPacketDispatch().sendConfig(1083, (isCoinshare() ? 1 : 0) + // << 18 | (isLootshare() ? 0 : 1)); + player.getPacketDispatch().sendString(joinRequirement.getInfo(), 590, 23); + player.getPacketDispatch().sendString(messageRequirement.getInfo(), 590, 24); + player.getPacketDispatch().sendString(kickRequirement.getInfo(), 590, 25); + player.getPacketDispatch().sendString(lootRequirement.getInfo(), 590, 26); + } + + /** + * Loads the clan data. + * @param owner The owner of the clan to load. + * @return The clan data. + */ + public static ClanRepository get(String owner) { + return get(owner, false); + } + + /** + * Loads the clan data. + * @param owner The owner of the clan to load. + * @param create If the clan should be created if it doesn't exist. + * @return The clan data. + */ + public static ClanRepository get(String owner, boolean create) { + ClanRepository clan = CLAN_REPOSITORY.get(owner); + if (clan != null) { + return clan; + } + Player player = Repository.getPlayerByName(owner); + PlayerDetails details = player != null ? player.getDetails() : null; + if (details == null) { + details = PlayerDetails.getDetails(owner); + if (details == null) { + return null; + } + } + String name = details.getCommunication().getClanName(); + if (name.length() < 1) { + if (!create) { + return null; + } + name = "Chat disabled"; + } + CLAN_REPOSITORY.put(owner, clan = new ClanRepository(owner)); + for (Contact c : details.getCommunication().getContacts().values()) { + clan.ranks.put(c.getUsername(), c.getRank()); + } + clan.name = name; + clan.joinRequirement = details.getCommunication().getJoinRequirement(); + clan.messageRequirement = details.getCommunication().getMessageRequirement(); + clan.kickRequirement = details.getCommunication().getKickRequirement(); + clan.lootRequirement = details.getCommunication().getLootRequirement(); + return clan; + } + + /** + * Checks if this clan chat is the default clan chat. + * @return {@code True} if so. + */ + public boolean isDefault() { + return owner.equals(GameWorld.getSettings().getName().toLowerCase()); + } + + /** + * Gets the default clan chat. + * @return The default clan chat. + */ + public static ClanRepository getDefault() { + return get(GameWorld.getSettings().getName().toLowerCase()); + } + + /** + * Deletes a clan. + */ + public void delete() { + CLAN_REPOSITORY.remove(owner); + clean(true); + } + + /** + * Gets the currently loaded clans. + * @return The clans. + */ + public static Map getClans() { + return CLAN_REPOSITORY; + } + + /** + * Gets the list of players currently in the clan. + * @return The list of players. + */ + public List getPlayers() { + return players; + } + + /** + * Gets the joinRequirement. + * @return The joinRequirement. + */ + public ClanRank getJoinRequirement() { + return joinRequirement; + } + + /** + * Sets the joinRequirement. + * @param joinRequirement The joinRequirement to set. + */ + public void setJoinRequirement(ClanRank joinRequirement) { + this.joinRequirement = joinRequirement; + clean(false); + } + + /** + * Gets the messageRequirement. + * @return The messageRequirement. + */ + public ClanRank getMessageRequirement() { + return messageRequirement; + } + + /** + * Sets the messageRequirement. + * @param messageRequirement The messageRequirement to set. + */ + public void setMessageRequirement(ClanRank messageRequirement) { + this.messageRequirement = messageRequirement; + } + + /** + * Gets the kickRequirement. + * @return The kickRequirement. + */ + public ClanRank getKickRequirement() { + return kickRequirement; + } + + /** + * Sets the kickRequirement. + * @param kickRequirement The kickRequirement to set. + */ + public void setKickRequirement(ClanRank kickRequirement) { + this.kickRequirement = kickRequirement; + update(); + } + + /** + * Gets the lootRequirement. + * @return The lootRequirement. + */ + public ClanRank getLootRequirement() { + return lootRequirement; + } + + /** + * Gets the banned. + * @return the banned + */ + public Map getBanned() { + return banned; + } + + /** + * Sets the lootRequirement. + * @param lootRequirement The lootRequirement to set. + */ + public void setLootRequirement(ClanRank lootRequirement) { + this.lootRequirement = lootRequirement; + } + + /** + * Gets the owner. + * @return The owner. + */ + public String getOwner() { + return owner; + } + + /** + * Gets the name. + * @return The name. + */ + public String getName() { + return name; + } + + /** + * Sets the name. + * @param name The name to set. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets the members. + * @return The members. + */ + public Map getRanks() { + return ranks; + } + + /** + * Gets the clanWar. + * @return The clanWar. + */ + public ActivityPlugin getClanWar() { + return clanWar; + } + + /** + * Sets the clanWar. + * @param clanWar The clanWar to set. + */ + public void setClanWar(ActivityPlugin clanWar) { + this.clanWar = clanWar; + } + +} \ No newline at end of file diff --git a/Server/src/main/core/game/system/communication/CommunicationInfo.java b/Server/src/main/core/game/system/communication/CommunicationInfo.java new file mode 100644 index 000000000..8135e7315 --- /dev/null +++ b/Server/src/main/core/game/system/communication/CommunicationInfo.java @@ -0,0 +1,663 @@ +package core.game.system.communication; + +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.game.system.mysql.SQLTable; +import core.game.system.task.Pulse; +import core.game.world.GameWorld; +import core.game.world.repository.Repository; +import core.net.amsc.MSPacketRepository; +import core.net.amsc.WorldCommunicator; +import core.net.packet.PacketRepository; +import core.net.packet.context.ContactContext; +import core.net.packet.out.ContactPackets; +import core.tools.StringUtils; +import core.worker.ManagementEvents; + +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 + */ +public final class CommunicationInfo { + + /** + * The maximum list size. + */ + public static final int MAX_LIST_SIZE = 200; + + /** + * The clan ranks. + */ + private Map contacts = new HashMap<>(); + + /** + * The list of blocked players. + */ + private final List blocked = new ArrayList<>(20); + + /** + * The player's clan name. + */ + private String clanName = ""; + + /** + * The current clan this player is in. + */ + private String currentClan = ""; + + /** + * The rank required for joining. + */ + private ClanRank joinRequirement = ClanRank.FRIEND; + + /** + * The rank required for messaging. + */ + private ClanRank messageRequirement = ClanRank.NONE; + + /** + * The rank required for kicking members. + */ + private ClanRank kickRequirement = ClanRank.OWNER; + + /** + * The rank required for loot-share. + */ + private ClanRank lootRequirement = ClanRank.ADMINISTRATOR; + + /** + * If lootshare is enabled. + */ + private boolean lootShare; + + /** + * The current clan joined. + */ + private ClanRepository clan = null; + + /** + * The loot-share pulse. + */ + private Pulse lootSharePulse; + + /** + * Constructs a new {@code CommunicationInfo} {@code Object}. + */ + public CommunicationInfo() { + /* + * empty. + */ + } + + /** + * Saves the communication info. + * @param table The SQL table to update. + */ + public void save(SQLTable table) { + String contacts = ""; + String blocked = ""; + for (int i = 0; i < this.blocked.size(); i++) { + blocked += (i == 0 ? "" : ",") + this.blocked.get(i); + } + int count = 0; + for (Entry entry : this.contacts.entrySet()) { + contacts += "{" + entry.getKey() + "," + entry.getValue().getRank().ordinal() + "}" + (count == this.contacts.size() - 1 ? "" : "~"); + count++; + } + table.getColumn("blocked").updateValue(blocked); + table.getColumn("contacts").updateValue(contacts); + table.getColumn("clanName").updateValue(clanName); + table.getColumn("currentClan").updateValue(currentClan); + table.getColumn("clanReqs").updateValue(joinRequirement.ordinal() + "," + messageRequirement.ordinal() + "," + kickRequirement.ordinal() + "," + lootRequirement.ordinal()); + } + + /** + * Parses the communication info from the database. + * @param table The sql table to parse. + */ + public void parse(SQLTable table) { + String[] tokens = null; + if (table.getColumn("contacts").getValue() != null ) { + String contacts = (String) table.getColumn("contacts").getValue(); + if (!contacts.isEmpty()) { + String[] datas = contacts.split("~"); + Contact contact = null; + for (String d : datas) { + tokens = d.replace("{", "").replace("}", "").split(","); + if (tokens.length == 0) { + continue; + } + contact = new Contact(tokens[0]); + contact.setRank(ClanRank.values()[Integer.valueOf(tokens[1])]); + this.contacts.put(tokens[0], contact); + } + } + } + if (table.getColumn("blocked").getValue() != null ) { + String blocked = (String) table.getColumn("blocked").getValue(); + if (!blocked.isEmpty()) { + tokens = blocked.split(","); + for (String name : tokens) { + this.blocked.add(name); + } + } + } + clanName = ((String) table.getColumn("clanName").getValue()); + currentClan = (String) table.getColumn("currentClan").getValue(); + String clanReqs = (String) table.getColumn("clanReqs").getValue(); + if (clanReqs == "") { + return; + } + tokens = clanReqs.split(","); + ClanRank rank = null; + int ordinal = 0; + for (int i = 0; i < tokens.length; i++) { + ordinal = Integer.parseInt(tokens[i]); + if (ordinal < 0 || ordinal > ClanRank.values().length -1) { + continue; + } + rank = ClanRank.values()[ordinal]; + switch (i) { + case 0: + joinRequirement = rank; + break; + case 1: + messageRequirement = rank; + break; + case 2: + if (ordinal < 3 || ordinal > 8) { + break; + } + kickRequirement = rank; + break; + case 3: + lootRequirement = rank; + break; + } + } + } + + /** + * Toggles the loot-share. + */ + public void toggleLootshare(final Player player) { + if (lootShare) { + lootShare = false; + setVarp(player, 1083, 0); + } else if (lootSharePulse != null) { + lootSharePulse.stop(); + lootSharePulse = null; + setVarp(player, 1083, 0); + } else if (!lootShare) { + setVarp(player, 1083, 2); + lootSharePulse = new Pulse(GameWorld.getSettings().isDevMode() ? 5 : 200, player) { + @Override + public boolean pulse() { + lootShare = true; + lootSharePulse = null; + setVarp(player, 1083, 1); + return true; + } + }; + GameWorld.getPulser().submit(lootSharePulse); + } + } + + /** + * Sends a message to the target. + * @param player The player sending the message. + * @param target The target. + * @param message The message to send. + */ + public static void sendMessage(Player player, String target, String message) { + PrivateMessage.Builder builder = PrivateMessage.newBuilder(); + builder.setSender(player.getName()); + builder.setRank(player.getDetails().getRights().ordinal()); + builder.setMessage(message); + builder.setReceiver(target); + ManagementEvents.publish(builder.build()); + } + + /** + * Adds a contact. + * @param contact The contact to add. + */ + 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; + } + if (info.contacts.size() >= MAX_LIST_SIZE) { + player.getPacketDispatch().sendMessage("Your friend list is full."); + return; + } + if (info.contacts.containsKey(contact)) { + player.getPacketDispatch().sendMessage(StringUtils.formatDisplayName(contact) + " is already on your friend list."); + return; + } + ClanRepository clan = ClanRepository.get(player.getName(), false); + if (clan != null) { + clan.rank(contact, ClanRank.FRIEND); + } + info.contacts.put(contact, new Contact(contact)); + Player target = Repository.getPlayerByName(contact); + if (target != null) { + if (showActive(player, target)) { + PacketRepository.send(ContactPackets.class, new ContactContext(player, contact, GameWorld.getSettings().getWorldId())); + } + if (player.getSettings().getPrivateChatSetting() == 1 && showActive(target, player)) { + PacketRepository.send(ContactPackets.class, new ContactContext(target, player.getName(), GameWorld.getSettings().getWorldId())); + } + } + } + + /** + * Removes a contact. + * @param contact The contact to remove. + * @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; + } + CommunicationInfo info = player.getDetails().getCommunication(); + if (block) { + info.blocked.remove(contact); + Player target = Repository.getPlayerByName(contact); + if (target != null && hasContact(target, player.getName())) { + int worldId = 0; + if (showActive(target, player)) { + worldId = GameWorld.getSettings().getWorldId(); + } + PacketRepository.send(ContactPackets.class, new ContactContext(target, player.getName(), worldId)); + } + } else { + info.contacts.remove(contact); + ClanRepository clan = ClanRepository.get(player.getName(), false); + if (clan != null) { + clan.rank(contact, ClanRank.NONE); + } + if (player.getSettings().getPrivateChatSetting() == 1) { + Player target = Repository.getPlayerByName(contact); + if (target != null) { + PacketRepository.send(ContactPackets.class, new ContactContext(target, player.getName(), 0)); + } + } + } + } + + /** + * Adds a blocked contact. + * @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; + } + CommunicationInfo info = player.getDetails().getCommunication(); + if (info.blocked.size() >= MAX_LIST_SIZE) { + player.getPacketDispatch().sendMessage("Your ignore list is full."); + return; + } + if (info.blocked.contains(contact)) { + player.getPacketDispatch().sendMessage(StringUtils.formatDisplayName(contact) + " is already on your ignore list."); + return; + } + info.blocked.add(contact); + Player target = Repository.getPlayerByName(contact); + if (target != null && hasContact(target, player.getName())) { + PacketRepository.send(ContactPackets.class, new ContactContext(target, player.getName(), 0)); + } + } + + /** + * Updates the clan rank of a certain contact. + * @param contact The contact. + * @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) { + log(CommunicationInfo.class, Log.ERR, "Could not find contact " + contact + " to update clan rank!"); + return; + } + c.setRank(clanRank); + ClanRepository clan = ClanRepository.get(player.getName()); + if (clan != null) { + clan.rank(contact, clanRank); + } + int worldId = 0; + if (CommunicationInfo.showActive(player, contact)) { + worldId = c.getWorldId(); + } + PacketRepository.send(ContactPackets.class, new ContactContext(player, contact, worldId)); + } + + /** + * Checks if the player has the contact added. + * @param player The player. + * @param contact The contact. + * @return {@code True} if so. + */ + public static boolean hasContact(Player player, String contact) { + return player.getDetails().getCommunication().contacts.containsKey(contact); + } + + /** + * Checks if the target should be shown as online. + * @param name The target's name. + * @return {@code True} if so. + */ + public static boolean showActive(Player player, String name) { + Player p = Repository.getPlayerByName(name); + if (p != null) { + return showActive(player, p); + } + return false; + } + + /** + * Checks if the target should be shown as online. + * @param target The target. + * @return {@True} if so. + */ + public static boolean showActive(Player player, Player target) { + if (target.getName().equals(player.getName())) { + return false; + } + if (target.getCommunication().getBlocked().contains(player.getName())) { + return false; + } + switch (target.getSettings().getPrivateChatSetting()) { + case 1: + if (!hasContact(target, player.getName())) { + return false; + } + return true; + case 2: + return false; + } + return true; + } + + /** + * Gets the contacts value. + * @return The contacts. + */ + public Map getContacts() { + return contacts; + } + + /** + * Gets the blocked value. + * @return The blocked. + */ + public List getBlocked() { + return blocked; + } + + /** + * Gets the clanName value. + * @return The clanName. + */ + public String getClanName() { + return clanName; + } + + /** + * Sets the clanName value. + * @param clanName The clanName to set. + */ + public void setClanName(String clanName) { + this.clanName = clanName; + } + + /** + * Gets the currentClan value. + * @return The currentClan. + */ + public String getCurrentClan() { + return currentClan == null ? "" : currentClan; + } + + /** + * Sets the currentClan value. + * @param currentClan The currentClan to set. + */ + @Deprecated + public void setCurrentClan(String currentClan) { + this.currentClan = currentClan; + } + + /** + * Gets the joinRequirement value. + * @return The joinRequirement. + */ + public ClanRank getJoinRequirement() { + return joinRequirement; + } + + /** + * Sets the joinRequirement value. + * @param joinRequirement The joinRequirement to set. + */ + public void setJoinRequirement(ClanRank joinRequirement) { + this.joinRequirement = joinRequirement; + } + + /** + * Gets the messageRequirement value. + * @return The messageRequirement. + */ + public ClanRank getMessageRequirement() { + return messageRequirement; + } + + /** + * Sets the messageRequirement value. + * @param messageRequirement The messageRequirement to set. + */ + public void setMessageRequirement(ClanRank messageRequirement) { + this.messageRequirement = messageRequirement; + } + + /** + * Gets the kickRequirement value. + * @return The kickRequirement. + */ + public ClanRank getKickRequirement() { + return kickRequirement; + } + + /** + * Sets the kickRequirement value. + * @param kickRequirement The kickRequirement to set. + */ + public void setKickRequirement(ClanRank kickRequirement) { + this.kickRequirement = kickRequirement; + } + + /** + * Gets the lootRequirement value. + * @return The lootRequirement. + */ + public ClanRank getLootRequirement() { + return lootRequirement; + } + + /** + * Sets the lootRequirement value. + * @param lootRequirement The lootRequirement to set. + */ + public void setLootRequirement(ClanRank lootRequirement) { + this.lootRequirement = lootRequirement; + } + + /** + * Gets the clan. + * @return the clan + */ + public ClanRepository getClan() { + return clan; + } + + /** + * Sets the clan. + * @param clan the clan to set. + */ + public void setClan(ClanRepository clan) { + this.clan = clan; + this.currentClan = clan == null ? null : clan.getOwner(); + } + + /** + * Gets the lootShare. + * @return the lootShare + */ + public boolean isLootShare() { + return lootShare; + } + + /** + * Sets the lootShare. + * @param lootShare the lootShare to set. + */ + public void setLootShare(boolean lootShare) { + this.lootShare = lootShare; + } + + public void parse(@NotNull UserAccountInfo accountInfo) { + blocked.addAll(Arrays.asList(accountInfo.getBlocked().split(","))); + clanName = accountInfo.getClanName(); + currentClan = accountInfo.getCurrentClan(); + + String clanReqs = accountInfo.getClanReqs(); + ClanRank[] reqs = parseClanRequirements(clanReqs); + joinRequirement = reqs[0]; + messageRequirement = reqs[1]; + kickRequirement = reqs[2]; + lootRequirement = reqs[3]; + + String contacts = accountInfo.getContacts(); + this.contacts = parseContacts(contacts); + } + + public static ClanRank[] parseClanRequirements(String clanReqs) { + ClanRank[] requirements = new ClanRank[4]; + String[] tokens = clanReqs.split(","); + ClanRank rank; + int ordinal; + for (int i = 0; i < tokens.length; i++) { + ordinal = Integer.parseInt(tokens[i]); + if (ordinal < 0 || ordinal > ClanRank.values().length -1) { + continue; + } + rank = ClanRank.values()[ordinal]; + switch (i) { + case 0: + requirements[0] = rank; + break; + case 1: + requirements[1] = rank; + break; + case 2: + if (ordinal < 3 || ordinal > 8) { + requirements[2] = ClanRank.ADMINISTRATOR; + break; + } + requirements[2] = rank; + break; + case 3: + requirements[3] = rank; + break; + } + } + + return requirements; + } + + public static HashMap parseContacts(String contacts) { + HashMap theseContacts = new HashMap<>(); + String[] tokens; + if (!contacts.isEmpty()) { + String[] datas = contacts.split("~"); + Contact contact; + for (String d : datas) { + tokens = d.replace("{", "").replace("}", "").split(","); + if (tokens.length == 0) { + continue; + } + contact = new Contact(tokens[0]); + contact.setRank(ClanRank.values()[Integer.parseInt(tokens[1])]); + theseContacts.put(tokens[0], contact); + } + } + + return theseContacts; + } + + public String getContactString() { + StringBuilder sb = new StringBuilder(); + + int index = 0; + for (Contact contact : contacts.values()) { + sb.append("{"); + sb.append(contact.getUsername()); + sb.append(","); + sb.append(contact.getRank().ordinal()); + sb.append("}"); + if (index++ < contacts.size() - 1) sb.append("~"); + } + + return sb.toString(); + } + + public String getBlockedString() { + StringBuilder sb = new StringBuilder(); + + int index = 0; + for (String block : blocked) { + sb.append(block); + if (index++ < blocked.size() - 1) sb.append(","); + } + + return sb.toString(); + } + + public String getClanReqString() { + return + joinRequirement.ordinal() + + "," + messageRequirement.ordinal() + + "," + kickRequirement.ordinal() + + "," + lootRequirement.ordinal(); + } +} diff --git a/Server/src/main/java/core/game/system/communication/Contact.java b/Server/src/main/core/game/system/communication/Contact.java similarity index 100% rename from Server/src/main/java/core/game/system/communication/Contact.java rename to Server/src/main/core/game/system/communication/Contact.java 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/ClueRewardParser.kt b/Server/src/main/core/game/system/config/ClueRewardParser.kt new file mode 100644 index 000000000..664de1bde --- /dev/null +++ b/Server/src/main/core/game/system/config/ClueRewardParser.kt @@ -0,0 +1,53 @@ +package core.game.system.config + +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser + +import core.ServerConstants +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem + +import java.io.FileReader + +class ClueRewardParser { + companion object { + @JvmStatic var easyTable = WeightBasedTable() + @JvmStatic var medTable = WeightBasedTable() + @JvmStatic var hardTable = WeightBasedTable() + @JvmStatic var rareTable = WeightBasedTable() + } + + + val parser = JSONParser() + var reader: FileReader? = null + + fun load() { + reader = FileReader(ServerConstants.CONFIG_PATH + "clue_rewards.json") + + val rawData = parser.parse(reader) as JSONObject + easyTable = parseClueTable(rawData["easy"] as JSONArray) + medTable = parseClueTable(rawData["medium"] as JSONArray) + hardTable = parseClueTable(rawData["hard"] as JSONArray) + rareTable = parseClueTable(rawData["rare"] as JSONArray) + } + + private fun parseClueTable(data: JSONArray) : WeightBasedTable { + val table = WeightBasedTable() + + for (element in data) { + val itemData = element as JSONObject + + table.add(WeightedItem( + itemData["id"].toString().toInt(), + itemData["minAmount"].toString().toInt(), + itemData["maxAmount"].toString().toInt(), + itemData["weight"].toString().toDouble(), + false + )) + + } + + return table + } +} diff --git a/Server/src/main/core/game/system/config/ConfigParser.kt b/Server/src/main/core/game/system/config/ConfigParser.kt new file mode 100644 index 000000000..db7e1b0f3 --- /dev/null +++ b/Server/src/main/core/game/system/config/ConfigParser.kt @@ -0,0 +1,60 @@ +package core.game.system.config + +import core.api.* + +import core.game.world.repository.Repository +import core.game.system.command.Privilege +import core.game.world.map.RegionManager +import core.game.node.item.GroundItemManager + +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +class ConfigParser : Commands { + fun prePlugin() { + NPCConfigParser().load() + ItemConfigParser().load() + ObjectConfigParser().load() + XteaParser().load() + InterfaceConfigParser().load() + } + fun postPlugin() { + ShopParser().load() + DropTableParser().load() + NPCSpawner().load() + DoorConfigLoader().load() + GroundSpawnLoader().load() + MusicConfigLoader().load() + RangedConfigLoader().load() + CustomVarbitParser().load() + ClueRewardParser().load() + } + + fun reloadConfigs(callback: () -> Unit) { + GlobalScope.launch { + Repository.npcs.toTypedArray().forEach { npc -> + npc.isRespawn = false + npc.clear() + Repository.npcs.remove(npc) + Repository.removeRenderableNPC(npc) + } + + GroundItemManager.getItems().toTypedArray().forEach {gi -> + GroundItemManager.getItems().remove(gi) + RegionManager.getRegionPlane(gi.location).remove(gi) + } + + prePlugin() + postPlugin() + + callback.invoke() + } + } + + override fun defineCommands() { + define("reloadjson", Privilege.ADMIN, "", "Reloads all the JSON configs.") { player, _ -> + notify(player, "Reloading JSON...") + reloadConfigs() { notify(player, "JSON reloaded.") } + } + } +} diff --git a/Server/src/main/core/game/system/config/CustomVarbitParser.kt b/Server/src/main/core/game/system/config/CustomVarbitParser.kt new file mode 100644 index 000000000..bf4647479 --- /dev/null +++ b/Server/src/main/core/game/system/config/CustomVarbitParser.kt @@ -0,0 +1,37 @@ +package core.game.system.config + +import core.cache.def.impl.VarbitDefinition +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 + +class CustomVarbitParser { + val parser = JSONParser() + var reader: FileReader? = null + + fun load() { + var count = 0 + + reader = FileReader(ServerConstants.CONFIG_PATH + "varbit_definitions.json") + val array = parser.parse(reader) as JSONArray + + for (jObject in array) { + val varbitDef = jObject as JSONObject + + val varpId = varbitDef["varpId"].toString().toInt() + val varbitId = varbitDef["varbitId"].toString().toInt() + val startBit = varbitDef["startBit"].toString().toInt() + val endBit = varbitDef["endBit"].toString().toInt() + + VarbitDefinition.create(varpId, varbitId, startBit, endBit) + count++ + } + + log(this::class.java, Log.FINE, "Parsed $count custom varbit definitions.") + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/DoorConfigLoader.kt b/Server/src/main/core/game/system/config/DoorConfigLoader.kt similarity index 77% rename from Server/src/main/kotlin/rs09/game/system/config/DoorConfigLoader.kt rename to Server/src/main/core/game/system/config/DoorConfigLoader.kt index b8957ca36..04d23c978 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/DoorConfigLoader.kt +++ b/Server/src/main/core/game/system/config/DoorConfigLoader.kt @@ -1,7 +1,9 @@ -package rs09.game.system.config +package core.game.system.config -import rs09.ServerConstants -import rs09.game.system.SystemLogger +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 import org.json.simple.parser.JSONParser @@ -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("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 new file mode 100644 index 000000000..465a1c6ee --- /dev/null +++ b/Server/src/main/core/game/system/config/DropTableParser.kt @@ -0,0 +1,64 @@ +package core.game.system.config + +import core.cache.def.impl.NPCDefinition +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import core.ServerConstants +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 + fun load(){ + var count = 0 + reader = FileReader(ServerConstants.CONFIG_PATH + "drop_tables.json") + val obj = parser.parse(reader) as JSONArray + for(i in obj){ + val tab = i as JSONObject + val ids = tab["ids"].toString().split(",") + + 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)}") + } + } + log(this::class.java, Log.FINE, "Parsed $count drop tables.") + } + + 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) + } + } +} diff --git a/Server/src/main/core/game/system/config/GroundSpawnLoader.kt b/Server/src/main/core/game/system/config/GroundSpawnLoader.kt new file mode 100644 index 000000000..d57ac9031 --- /dev/null +++ b/Server/src/main/core/game/system/config/GroundSpawnLoader.kt @@ -0,0 +1,117 @@ +package core.game.system.config + +import core.game.node.item.GroundItem +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.game.system.task.Pulse +import core.game.world.map.Location +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import core.ServerConstants +import core.api.log +import core.game.world.GameWorld +import core.game.world.repository.Repository +import core.tools.Log +import java.io.* + +class GroundSpawnLoader { + val parser = JSONParser() + var reader: FileReader? = null + + fun load() { + var count = 0 + reader = FileReader(ServerConstants.CONFIG_PATH + "ground_spawns.json") + var configs = parser.parse(reader) as JSONArray + for(config in configs){ + 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") + } + } + log(this::class.java, Log.FINE, "Initialized $count ground items.") + } + class GroundSpawn + /** + * Constructs a new `GroundItemParser` `Object`. + * @param item the item. + * @param location the location. + */( + /** + * Represents the rate at which a ground item will respawn. + */ + var respawnRate: Int, item: Item?, location: Location?) : GroundItem(item, location) { + /** + * Gets the respawn rate. + * @return the rate. + */ + + override fun toString(): String { + return "GroundSpawn [name=" + getName() + ", respawnRate=" + respawnRate + ", loc=" + getLocation() + "]" + } + + /** + * Method used to initialize this spawn. + */ + fun init(): GroundItem { + if (respawnRate shr 16 == 0) respawnRate = respawnRate or (respawnRate shl 16) + return GroundItemManager.create(this) + } + + override fun isActive(): Boolean { + return true + } + + override fun isPrivate(): Boolean { + return false + } + + override fun isAutoSpawn(): Boolean { + return true + } + + override fun respawn() { + GameWorld.Pulser.submit(object : Pulse(respawnDuration) { + override fun pulse(): Boolean { + GroundItemManager.create(this@GroundSpawn) + return true + } + }) + } + + /** + * Method used to set the respawn rate. + * @param min the min. + * @param max the max. + */ + fun setRespawnRate(min: Int, max: Int) { + respawnRate = min or max shl 16 + } + + /** + * Gets the current respawn duration (in ticks). + * @return The respawn duration. + */ + val respawnDuration: Int + get() { + val minimum = respawnRate and 0xFFFF + val maximum = respawnRate shr 16 and 0xFFFF + val playerRatio = ServerConstants.MAX_PLAYERS / Repository.players.size.toDouble() + return (minimum + (maximum - minimum) / playerRatio).toInt() + } + + } +} diff --git a/Server/src/main/kotlin/rs09/game/system/config/InterfaceConfigParser.kt b/Server/src/main/core/game/system/config/InterfaceConfigParser.kt similarity index 82% rename from Server/src/main/kotlin/rs09/game/system/config/InterfaceConfigParser.kt rename to Server/src/main/core/game/system/config/InterfaceConfigParser.kt index f4573ce18..d691bd167 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/InterfaceConfigParser.kt +++ b/Server/src/main/core/game/system/config/InterfaceConfigParser.kt @@ -1,8 +1,10 @@ -package rs09.game.system.config +package core.game.system.config -import rs09.ServerConstants +import core.ServerConstants +import core.api.log import core.game.component.ComponentDefinition -import rs09.game.system.SystemLogger +import core.tools.Log +import core.tools.SystemLogger import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser @@ -24,6 +26,6 @@ class InterfaceConfigParser{ ComponentDefinition.getDefinitions()[id] = ComponentDefinition().parse(e["interfaceType"].toString(),e["walkable"].toString(),e["tabIndex"].toString()) count++ } - SystemLogger.logInfo("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/kotlin/rs09/game/system/config/ItemConfigParser.kt b/Server/src/main/core/game/system/config/ItemConfigParser.kt similarity index 92% rename from Server/src/main/kotlin/rs09/game/system/config/ItemConfigParser.kt rename to Server/src/main/core/game/system/config/ItemConfigParser.kt index 4fcd1e381..39645961d 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/ItemConfigParser.kt +++ b/Server/src/main/core/game/system/config/ItemConfigParser.kt @@ -1,4 +1,4 @@ -package rs09.game.system.config +package core.game.system.config import core.cache.def.impl.ItemDefinition import core.game.node.entity.impl.Animator @@ -7,8 +7,10 @@ import core.game.world.update.flag.context.Animation import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser -import rs09.ServerConstants -import rs09.game.system.SystemLogger +import core.ServerConstants +import core.api.log +import core.tools.Log +import core.tools.SystemLogger import java.io.FileReader class ItemConfigParser { @@ -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() @@ -233,7 +245,6 @@ class ItemConfigParser { when (it.key.toString()) { //Special cases "defence_anim" -> configs.put(it.key.toString(), Animation(it.value.toString().toInt(), Animator.Priority.HIGH)) - "shop_price" -> {def.value = (it.value.toString().toInt()); configs.put(it.key.toString(),it.value.toString().toInt())} "requirements" -> { configs.put(it.key.toString(),requirements) it.value.toString() .split("-") @@ -262,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()) @@ -269,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", @@ -296,6 +309,6 @@ class ItemConfigParser { } count++ } - SystemLogger.logInfo("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 new file mode 100644 index 000000000..3f5cc60cf --- /dev/null +++ b/Server/src/main/core/game/system/config/MusicConfigLoader.kt @@ -0,0 +1,83 @@ +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 +import java.io.FileReader + +class MusicConfigLoader { + //1351 -> buttonID:songID + //1345 -> buttonID:songName (capitalized) + //1347 -> buttonID:songName (lowercase) + + val parser = JSONParser() + var reader: FileReader? = null + fun load(){ + // Populate the server data map + val songs = DataMap.get(1351) + val names = DataMap.get(1345) + + for((index, songId) in songs.dataStore) + { + val entry = MusicEntry(songId as Int, names.getString(index as Int), index) + MusicEntry.getSongs().putIfAbsent(songId, entry) + } + + // 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() + val borderArray = string.split("-") + var tokens: Array? = null + var borders: ZoneBorders? = null + for (border in borderArray) { + 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 + var exceptions: String? = "" + for (i in 4 until tokens.size) { + exceptions += tokens[i] + if (!tokens[i].contains("]~")) "," else if (tokens[i].contains("[")) "," else "" + } + tokens = exceptions!!.split("~".toRegex()).toTypedArray() + var e: Array? = null + for (exception in tokens) { + e = exception.replace("[", "").replace("]", "").split(",".toRegex()).toTypedArray() + borders.addException(ZoneBorders(e[0].toInt(), e[1].toInt(), e[2].toInt(), e[3].toInt())) + } + } + val zone = MusicZone(musicId, borders) + for (id in borders.getRegionIds()) { + RegionManager.forId(id!!).musicZones.add(zone) + } + } + count++ + } + log(this::class.java, Log.FINE, "Parsed $count tile music configs.") + } +} diff --git a/Server/src/main/kotlin/rs09/game/system/config/NPCConfigParser.kt b/Server/src/main/core/game/system/config/NPCConfigParser.kt similarity index 94% rename from Server/src/main/kotlin/rs09/game/system/config/NPCConfigParser.kt rename to Server/src/main/core/game/system/config/NPCConfigParser.kt index 7200d3c7d..292c682e7 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/NPCConfigParser.kt +++ b/Server/src/main/core/game/system/config/NPCConfigParser.kt @@ -1,12 +1,14 @@ -package rs09.game.system.config +package core.game.system.config -import rs09.ServerConstants +import core.ServerConstants import core.cache.def.impl.NPCDefinition -import core.game.content.ttrail.ClueLevel +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 rs09.game.system.SystemLogger +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 @@ -257,13 +259,14 @@ NPCConfigParser { "poisonous", "poison_immune", "facing_booth", + "can_tolerate", "water_npc"-> configs.put(it.key.toString(), it.value.toString().toBoolean()) - else -> SystemLogger.logWarn("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("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/kotlin/rs09/game/system/config/NPCSpawner.kt b/Server/src/main/core/game/system/config/NPCSpawner.kt similarity index 89% rename from Server/src/main/kotlin/rs09/game/system/config/NPCSpawner.kt rename to Server/src/main/core/game/system/config/NPCSpawner.kt index 69a11a2c3..958b3e2d6 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/NPCSpawner.kt +++ b/Server/src/main/core/game/system/config/NPCSpawner.kt @@ -1,10 +1,12 @@ -package rs09.game.system.config +package core.game.system.config -import rs09.ServerConstants +import core.ServerConstants +import core.api.log import core.game.node.entity.npc.NPC -import rs09.game.system.SystemLogger +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("Spawned $count NPCs") + log(this::class.java, Log.FINE, "Spawned $count NPCs") } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/ObjectConfigParser.kt b/Server/src/main/core/game/system/config/ObjectConfigParser.kt similarity index 86% rename from Server/src/main/kotlin/rs09/game/system/config/ObjectConfigParser.kt rename to Server/src/main/core/game/system/config/ObjectConfigParser.kt index 77a7a9a33..0445909c2 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/ObjectConfigParser.kt +++ b/Server/src/main/core/game/system/config/ObjectConfigParser.kt @@ -1,8 +1,10 @@ -package rs09.game.system.config +package core.game.system.config -import rs09.ServerConstants +import core.ServerConstants +import core.api.log import core.cache.def.impl.SceneryDefinition -import rs09.game.system.SystemLogger +import core.tools.Log +import core.tools.SystemLogger import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser @@ -35,7 +37,7 @@ class ObjectConfigParser { count++ } } - SystemLogger.logInfo("Parsed $count object configs.") + log(this::class.java, Log.FINE, "Parsed $count object configs.") } } diff --git a/Server/src/main/kotlin/rs09/game/system/config/RangedConfigLoader.kt b/Server/src/main/core/game/system/config/RangedConfigLoader.kt similarity index 90% rename from Server/src/main/kotlin/rs09/game/system/config/RangedConfigLoader.kt rename to Server/src/main/core/game/system/config/RangedConfigLoader.kt index ac8476a2a..6c54cbd55 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/RangedConfigLoader.kt +++ b/Server/src/main/core/game/system/config/RangedConfigLoader.kt @@ -1,16 +1,17 @@ -package rs09.game.system.config +package core.game.system.config -import rs09.ServerConstants +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 rs09.game.system.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 @@ -52,10 +53,10 @@ class RangedConfigLoader { if (effect != null) { ammo.effect = effect } - Ammunition.getAmmunition().putIfAbsent(Integer.parseInt(e["itemId"].toString()),ammo) + Ammunition.getAmmunition().put(Integer.parseInt(e["itemId"].toString()),ammo) count++ } - SystemLogger.logWarn("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("Parsed $count ranged weapon configs...") + log(this::class.java, Log.FINE, "Parsed $count ranged weapon configs...") } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/config/ServerConfigParser.kt b/Server/src/main/core/game/system/config/ServerConfigParser.kt new file mode 100644 index 000000000..58e3e6ebf --- /dev/null +++ b/Server/src/main/core/game/system/config/ServerConfigParser.kt @@ -0,0 +1,232 @@ +package core.game.system.config + +import com.moandjiezana.toml.Toml +import core.ServerConstants +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 + +/** + * Class for parsing the server config, I.E default.toml + * @param path the path to the TOML file to parse. + * @author Ceikry + */ +object ServerConfigParser { + var confFile: File? = null + var tomlData: Toml? = null + + fun parse(path: String){ + confFile = File(parsePath(path)) + parseFromFile(confFile) + } + + fun parse(path: URL?) { + confFile = File(path!!.toURI()) + parseFromFile(confFile) + } + + private fun parseFromFile(confFile: File?) { + if(!confFile!!.canonicalFile.exists()){ + 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) { + 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) + } + } + } + + private fun parseGameSettings(){ + tomlData ?: return + val data = tomlData!! + + GameWorld.settings = GameSettings( + name = ServerConstants.SERVER_NAME, + isBeta = data.getBoolean("world.debug"), + isDevMode = data.getBoolean("world.dev"), + isGui = data.getBoolean("world.start_gui"), + worldId = data.getString("world.world_id").toInt(), + countryIndex = data.getString("world.country_id").toInt(), + activity = data.getString("world.activity"), + isMembers = data.getBoolean("world.members"), + isPvp = data.getBoolean("world.pvp"), + isQuickChat = false, + isLootshare = false, + msAddress = data.getString("server.msip"), + enable_default_clan = data.getBoolean("world.enable_default_clan"), + enable_bots = data.getBoolean("world.enable_bots"), + autostock_ge = data.getBoolean("world.autostock_ge"), + allow_token_purchase = data.getBoolean("world.allow_token_purchase"), + skillcape_perks = data.getBoolean("world.skillcape_perks"), + increased_door_time = data.getBoolean("world.increased_door_time"), + enabled_botting = data.getBoolean("world.enable_botting"), + max_adv_bots = data.getLong("world.max_adv_bots").toInt(), + 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) + ) + } + + private fun parseServerSettings(){ + tomlData ?: return + val data = tomlData!! + + ServerConstants.DATA_PATH = data.getString("paths.data_path") + ServerConstants.WRITE_LOGS = data.getBoolean("server.write_logs") + ServerConstants.DATABASE_NAME = data.getString("database.database_name") + ServerConstants.DATABASE_USER = data.getString("database.database_username") + ServerConstants.DATABASE_PASS = data.getString("database.database_password") + ServerConstants.DATABASE_ADDRESS = data.getString("database.database_address") + ServerConstants.DATABASE_PORT = data.getString("database.database_port") + ServerConstants.DATABASE = Database(ServerConstants.DATABASE_ADDRESS + ":" + ServerConstants.DATABASE_PORT, ServerConstants.DATABASE_NAME, ServerConstants.DATABASE_USER, ServerConstants.DATABASE_PASS) + ServerConstants.CACHE_PATH = data.getPath("paths.cache_path") + ServerConstants.CONFIG_PATH = data.getPath("paths.configs_path") + ServerConstants.PLAYER_SAVE_PATH = data.getPath("paths.save_path") + ServerConstants.STORE_PATH = data.getPath("paths.store_path") + ServerConstants.RDT_DATA_PATH = data.getPath("paths.rare_drop_table_path") + ServerConstants.OBJECT_PARSER_PATH = data.getPath("paths.object_parser_path") + ServerConstants.LOGS_PATH = data.getPath("paths.logs_path") + ServerConstants.SERVER_NAME = data.getPath("world.name") + ServerConstants.BOT_DATA_PATH = data.getPath("paths.bot_data") + ServerConstants.MS_SECRET_KEY = data.getString("server.secret_key") + ServerConstants.HOME_LOCATION = parseLocation(data.getString("world.home_location")) + ServerConstants.START_LOCATION = parseLocation(data.getString("world.new_player_location")) + ServerConstants.DAILY_RESTART = data.getBoolean("world.daily_restart") + ServerConstants.LOG_CUTSCENE = data.getBoolean("world.verbose_cutscene", false) + ServerConstants.GRAND_EXCHANGE_DATA_PATH = data.getPath("paths.eco_data") + ServerConstants.CELEDT_DATA_PATH = data.getPath("paths.cele_drop_table_path") + ServerConstants.USDT_DATA_PATH = data.getPath("paths.uncommon_seed_drop_table_path") + ServerConstants.HDT_DATA_PATH = data.getPath("paths.herb_drop_table_path") + ServerConstants.GDT_DATA_PATH = data.getPath("paths.gem_drop_table_path") + ServerConstants.RSDT_DATA_PATH = data.getPath("paths.rare_seed_drop_table_path") + ServerConstants.ASDT_DATA_PATH = data.getPath("paths.allotment_seed_drop_table_path") + ServerConstants.SERVER_GE_NAME = data.getString("world.name_ge") ?: ServerConstants.SERVER_NAME + ServerConstants.RULES_AND_INFO_ENABLED = data.getBoolean("world.show_rules", true) + ServerConstants.BOTS_INFLUENCE_PRICE_INDEX = data.getBoolean("world.bots_influence_ge_price", true) + ServerConstants.PRELOAD_MAP = data.getBoolean("server.preload_map", false) + ServerConstants.REVENANT_POPULATION = data.getLong("world.revenant_population", 30L).toInt() + 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("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("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, "@data/").replace("@data", ServerConstants.DATA_PATH!!)) + } catch (e: Exception){ + log(this::class.java, Log.ERR, "Error parsing key: $key") + exitProcess(0) + } + } + + /** + * Parses a location from the format "x,y,z" + * @author Ceikry + * @param locString The string to parse + * @return Location + */ + fun parseLocation(locString: String): Location { + val locTokens = locString.split(",").map { it.toInt() } + return Location(locTokens[0], locTokens[1], locTokens[2]) + } + + /** + * Parses a path string + * @author Ceikry + * @param pathString The string to parse + * @return a String with the proper file separators for the current OS. + */ + fun parsePath(pathString: String): String { + var pathTokens: List? = null + if(pathString.contains("/")) + pathTokens = pathString.split("/") + else if(pathString.contains("\\")) + pathTokens = pathString.split("\\") + + pathTokens ?: return pathString //return the initial pathString if path does not contain file separators. + var pathProduct = "" + for(token in pathTokens){ + if(token != "") + pathProduct += "$token${File.separator}" + } + + return pathProduct + } +} diff --git a/Server/src/main/core/game/system/config/ShopParser.kt b/Server/src/main/core/game/system/config/ShopParser.kt new file mode 100644 index 000000000..1546e3733 --- /dev/null +++ b/Server/src/main/core/game/system/config/ShopParser.kt @@ -0,0 +1,36 @@ +package core.game.system.config + +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 + +class ShopParser{ + val parser = JSONParser() + var reader: FileReader? = null + + companion object { +/* val SHOPS = HashMap() + val UID_SHOPS = HashMap()*/ + fun openUid(player: Player,uid: Int): Boolean { +/* val shop = UID_SHOPS[uid] ?: return false; + shop.open(player);*/ + return true +} + } + + fun load(){ + var count = 0 + reader = FileReader(ServerConstants.CONFIG_PATH + "shops.json") + val configlist = parser.parse(reader) as JSONArray + for(config in configlist){ + + count++ + } + 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/TableDump.kt b/Server/src/main/core/game/system/config/TableDump.kt new file mode 100644 index 000000000..e69de29bb diff --git a/Server/src/main/core/game/system/config/XteaParser.kt b/Server/src/main/core/game/system/config/XteaParser.kt new file mode 100644 index 000000000..9eb4f569c --- /dev/null +++ b/Server/src/main/core/game/system/config/XteaParser.kt @@ -0,0 +1,39 @@ +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 +import org.json.simple.parser.JSONParser +import java.io.FileReader +import kotlin.collections.HashMap + +class XteaParser { + companion object{ + val REGION_XTEA = HashMap() + 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() + var reader: FileReader? = null + + + fun load() { + var count = 0 + reader = FileReader(ServerConstants.CONFIG_PATH + "xteas.json") + val obj = parser.parse(reader) as JSONObject + val configlist = obj["xteas"] as JSONArray + for(config in configlist){ + val e = config as JSONObject + val id = e["regionId"].toString().toInt() + val keys = e["keys"].toString().split(",").map(String::toInt) + REGION_XTEA[id] = intArrayOf(keys[0],keys[1],keys[2],keys[3]) + count++ + } + log(this::class.java, Log.FINE, "Parsed $count region keys.") + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/monitor/DuplicationLog.java b/Server/src/main/core/game/system/monitor/DuplicationLog.java similarity index 98% rename from Server/src/main/java/core/game/system/monitor/DuplicationLog.java rename to Server/src/main/core/game/system/monitor/DuplicationLog.java index 9ac4969f4..de1d380ba 100644 --- a/Server/src/main/java/core/game/system/monitor/DuplicationLog.java +++ b/Server/src/main/core/game/system/monitor/DuplicationLog.java @@ -1,6 +1,6 @@ package core.game.system.monitor; -import rs09.ServerConstants; +import core.ServerConstants; /** * Handles duplication-related logging. diff --git a/Server/src/main/java/core/game/system/monitor/MessageLog.java b/Server/src/main/core/game/system/monitor/MessageLog.java similarity index 100% rename from Server/src/main/java/core/game/system/monitor/MessageLog.java rename to Server/src/main/core/game/system/monitor/MessageLog.java diff --git a/Server/src/main/java/core/game/system/mysql/SQLColumn.java b/Server/src/main/core/game/system/mysql/SQLColumn.java similarity index 100% rename from Server/src/main/java/core/game/system/mysql/SQLColumn.java rename to Server/src/main/core/game/system/mysql/SQLColumn.java diff --git a/Server/src/main/java/core/game/system/mysql/SQLTable.java b/Server/src/main/core/game/system/mysql/SQLTable.java similarity index 100% rename from Server/src/main/java/core/game/system/mysql/SQLTable.java rename to Server/src/main/core/game/system/mysql/SQLTable.java diff --git a/Server/src/main/core/game/system/security/EncryptionManager.java b/Server/src/main/core/game/system/security/EncryptionManager.java new file mode 100644 index 000000000..617daa0fc --- /dev/null +++ b/Server/src/main/core/game/system/security/EncryptionManager.java @@ -0,0 +1,513 @@ +package core.game.system.security; + +import core.game.system.SystemManager; + +import java.io.UnsupportedEncodingException; +import java.security.SecureRandom; + +/** + * Represents the class of simple encryption functions. + * @author Unknown + */ +public class EncryptionManager { + + // Define the BCrypt workload to use when generating password hashes. 10-31 + // is a valid value. + private static int workload = 12; + + public static void main(String... args) { + + String pass = "lugi"; + String salt = BCrypt.gensalt(workload); + + + + // "$2a$12$N3jCPMPEZTisbMf7PG9O7ePgTQEJjhNXGHOJ9zvovT8ibY3Uk.Xlm")); + String hash = "$2a$12$N3jCPMPEZTisbMf7PG9O7ePgTQEJjhNXGHOJ9zvovT8ibY3Uk.Xlm"; + salt = hash.substring(0, 29); + + String hsh = hash.substring(29, hash.length()); + + } + + /** + * This method can be used to generate a string representing an account + * password suitable for storing in a database. It will be an OpenBSD-style + * crypt(3) formatted hash string of length=60 The bcrypt workload is + * specified in the above static variable, a value from 10 to 31. A workload + * of 12 is a very reasonable safe default as of 2013. This automatically + * handles secure 128-bit salt generation and storage within the hash. + * @param password_plaintext The account's plaintext password as provided + * during account creation, or when changing an account's password. + * @return String - a string of length 60 that is the bcrypt hashed password + * in crypt(3) format. + */ + public String hashPassword(String password_plaintext) { + String salt = BCrypt.gensalt(workload); + String hashed_password = BCrypt.hashpw(password_plaintext, salt); + return (hashed_password); + } + + /** + * Hash pass. + * @param password_plaintext + * @param salt + * @return + */ + public String hashPassword(String password_plaintext, String salt) { + String hashed_password = BCrypt.hashpw(password_plaintext, salt); + return (hashed_password); + } + + /** + * This method can be used to verify a computed hash from a plaintext (e.g. + * during a login request) with that of a stored hash from a database. The + * password hash from the database must be passed as the second variable. + * @param password_plaintext The account's plaintext password, as provided + * during a login request + * @param stored_hash The account's stored password hash, retrieved from the + * authorization database + * @return boolean - true if the password matches the password of the stored + * hash, false otherwise + */ + public boolean checkPassword(String password_plaintext, String stored_hash) { + boolean password_verified = false; + if (null == stored_hash || !stored_hash.startsWith("$2a$")) + throw new java.lang.IllegalArgumentException("Invalid hash provided for comparison"); + password_verified = BCrypt.checkpw(password_plaintext, stored_hash); + return (password_verified); + } + + // Copyright (c) 2006 Damien Miller + // + // Permission to use, copy, modify, and distribute this software for any + // purpose with or without fee is hereby granted, provided that the above + // copyright notice and this permission notice appear in all copies. + // + // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + /** + * BCrypt implements OpenBSD-style Blowfish password hashing using the + * scheme described in "A Future-Adaptable Password Scheme" by Niels Provos + * and David Mazieres.

This password hashing system tries to thwart + * off-line password cracking using a computationally-intensive hashing + * algorithm, based on Bruce Schneier's Blowfish cipher. The work factor of + * the algorithm is parameterised, so it can be increased as computers get + * faster.

Usage is really simple. To hash a password for the first + * time, call the hashpw method with a random salt, like this:

+ * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
+ *

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

if + * (BCrypt.checkpw(candidate_password, stored_hash))
+ + + *

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 = + * BCrypt.gensalt(12)

The amount of work increases + * exponentially (2**log_rounds), so each increment is twice as much work. + * The default log_rounds is 10, and the valid range is 4 to 31. + * @author Damien Miller + * @version 0.2 + */ + public static class BCrypt { + // BCrypt parameters + private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; + private static final int BCRYPT_SALT_LEN = 16; + + // Blowfish parameters + private static final int BLOWFISH_NUM_ROUNDS = 16; + + // Initial contents of key schedule + private static final int P_orig[] = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b }; + private static final int S_orig[] = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, + 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 }; + + // bcrypt IV: "OrpheanBeholderScryDoubt" + static private final int bf_crypt_ciphertext[] = { 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 }; + + // Table for Base64 encoding + static private final char base64_code[] = { '.', '/', '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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + + // Table for Base64 decoding + static private final byte index_64[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1 }; + + // Expanded Blowfish key + private int P[]; + private int S[]; + + /** + * Encode a byte array using bcrypt's slightly-modified base64 encoding + * scheme. Note that this is *not* compatible with the standard + * MIME-base64 encoding. + * @param d the byte array to encode + * @param len the number of bytes to encode + * @return base64-encoded string + * @exception IllegalArgumentException if the length is invalid + */ + private static String encode_base64(byte d[], int len) throws IllegalArgumentException { + int off = 0; + StringBuffer rs = new StringBuffer(); + int c1, c2; + + if (len <= 0 || len > d.length) + throw new IllegalArgumentException("Invalid len"); + + while (off < len) { + c1 = d[off++] & 0xff; + rs.append(base64_code[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.append(base64_code[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.append(base64_code[c1 & 0x3f]); + rs.append(base64_code[c2 & 0x3f]); + } + return rs.toString(); + } + + /** + * Look up the 3 bits base64-encoded by the specified character, + * range-checking againt conversion table + * @param x the base64-encoded value + * @return the decoded value of x + */ + private static byte char64(char x) { + if ((int) x > index_64.length) + return -1; + return index_64[(int) x]; + } + + /** + * Decode a string encoded using bcrypt's base64 scheme to a byte array. + * Note that this is *not* compatible with the standard MIME-base64 + * encoding. + * @param s the string to decode + * @param maxolen the maximum number of bytes to decode + * @return an array containing the decoded bytes + * @throws IllegalArgumentException if maxolen is invalid + */ + private static byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException { + StringBuffer rs = new StringBuffer(); + int off = 0, slen = s.length(), olen = 0; + byte ret[]; + byte c1, c2, c3, c4, o; + + if (maxolen <= 0) + throw new IllegalArgumentException("Invalid maxolen"); + + while (off < slen - 1 && olen < maxolen) { + c1 = char64(s.charAt(off++)); + c2 = char64(s.charAt(off++)); + if (c1 == -1 || c2 == -1) + break; + o = (byte) (c1 << 2); + o |= (c2 & 0x30) >> 4; + rs.append((char) o); + if (++olen >= maxolen || off >= slen) + break; + c3 = char64(s.charAt(off++)); + if (c3 == -1) + break; + o = (byte) ((c2 & 0x0f) << 4); + o |= (c3 & 0x3c) >> 2; + rs.append((char) o); + if (++olen >= maxolen || off >= slen) + break; + c4 = char64(s.charAt(off++)); + o = (byte) ((c3 & 0x03) << 6); + o |= c4; + rs.append((char) o); + ++olen; + } + + ret = new byte[olen]; + for (off = 0; off < olen; off++) + ret[off] = (byte) rs.charAt(off); + return ret; + } + + /** + * Blowfish encipher a single 64-bit block encoded as two 32-bit halves + * @param lr an array containing the two 32-bit half blocks + * @param off the position in the array of the blocks + */ + private final void encipher(int lr[], int off) { + int i, n, l = lr[off], r = lr[off + 1]; + + l ^= P[0]; + for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { + // Feistel substitution on left word + n = S[(l >> 24) & 0xff]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[++i]; + + // Feistel substitution on right word + n = S[(r >> 24) & 0xff]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[++i]; + } + lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; + lr[off + 1] = l; + } + + /** + * Cycically extract a word of key material + * @param data the string to extract the data from + * @param offp a "pointer" (as a one-entry array) to the current offset + * into data + * @return the next word of material from data + */ + private static int streamtoword(byte data[], int offp[]) { + int i; + int word = 0; + int off = offp[0]; + + for (i = 0; i < 4; i++) { + word = (word << 8) | (data[off] & 0xff); + off = (off + 1) % data.length; + } + + offp[0] = off; + return word; + } + + /** + * Initialise the Blowfish key schedule + */ + private void init_key() { + P = (int[]) P_orig.clone(); + S = (int[]) S_orig.clone(); + } + + /** + * Key the Blowfish cipher + * @param key an array containing the key + */ + private void key(byte key[]) { + int i; + int koffp[] = { 0 }; + int lr[] = { 0, 0 }; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) + P[i] = P[i] ^ streamtoword(key, koffp); + + for (i = 0; i < plen; i += 2) { + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + /** + * Perform the "enhanced key schedule" step described by Provos and + * Mazieres in "A Future-Adaptable Password Scheme" + * http://www.openbsd.org/papers/bcrypt-paper.ps + * @param data salt information + * @param key password information + */ + private void ekskey(byte data[], byte key[]) { + int i; + int koffp[] = { 0 }, doffp[] = { 0 }; + int lr[] = { 0, 0 }; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) + P[i] = P[i] ^ streamtoword(key, koffp); + + for (i = 0; i < plen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + /** + * Perform the central password hashing step in the bcrypt scheme + * @param password the password to hash + * @param salt the binary salt to hash with the password + * @param log_rounds the binary logarithm of the number of rounds of + * hashing to apply + * @return an array containing the binary hashed password + */ + private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) { + int rounds, i, j; + int cdata[] = (int[]) bf_crypt_ciphertext.clone(); + int clen = cdata.length; + byte ret[]; + + if (log_rounds < 4 || log_rounds > 31) + throw new IllegalArgumentException("Bad number of rounds"); + rounds = 1 << log_rounds; + if (salt.length != BCRYPT_SALT_LEN) + throw new IllegalArgumentException("Bad salt length"); + + init_key(); + ekskey(salt, password); + for (i = 0; i < rounds; i++) { + key(password); + key(salt); + } + + for (i = 0; i < 64; i++) { + for (j = 0; j < (clen >> 1); j++) + encipher(cdata, j << 1); + } + + ret = new byte[clen * 4]; + for (i = 0, j = 0; i < clen; i++) { + ret[j++] = (byte) ((cdata[i] >> 24) & 0xff); + ret[j++] = (byte) ((cdata[i] >> 16) & 0xff); + ret[j++] = (byte) ((cdata[i] >> 8) & 0xff); + ret[j++] = (byte) (cdata[i] & 0xff); + } + return ret; + } + + /** + * Hash a password using the OpenBSD bcrypt scheme + * @param password the password to hash + * @param salt the salt to hash with (perhaps generated using + * BCrypt.gensalt) + * @return the hashed password + */ + public static String hashpw(String password, String salt) { + BCrypt B; + String real_salt; + byte passwordb[], saltb[], hashed[]; + char minor = (char) 0; + int rounds, off = 0; + StringBuffer rs = new StringBuffer(); + + if (salt.charAt(0) != '$' || salt.charAt(1) != '2') + throw new IllegalArgumentException("Invalid salt version"); + if (salt.charAt(2) == '$') + off = 3; + else { + minor = salt.charAt(2); + if (minor != 'a' || salt.charAt(3) != '$') + throw new IllegalArgumentException("Invalid salt revision"); + off = 4; + } + + // Extract number of rounds + if (salt.charAt(off + 2) > '$') + throw new IllegalArgumentException("Missing salt rounds"); + rounds = Integer.parseInt(salt.substring(off, off + 2)); + + real_salt = salt.substring(off + 3, off + 25); + try { + passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8"); + } catch (UnsupportedEncodingException uee) { + throw new AssertionError("UTF-8 is not supported"); + } + + saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); + + B = new BCrypt(); + hashed = B.crypt_raw(passwordb, saltb, rounds); + + rs.append("$2"); + if (minor >= 'a') + rs.append(minor); + rs.append("$"); + if (rounds < 10) + rs.append("0"); + rs.append(Integer.toString(rounds)); + rs.append("$"); + rs.append(encode_base64(saltb, saltb.length)); + rs.append(encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1)); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * @param log_rounds the log2 of the number of rounds of hashing to apply + * - the work factor therefore increases as 2**log_rounds. + * @param random an instance of SecureRandom to use + * @return an encoded salt value + */ + public static String gensalt(int log_rounds, SecureRandom random) { + StringBuffer rs = new StringBuffer(); + byte rnd[] = new byte[BCRYPT_SALT_LEN]; + + random.nextBytes(rnd); + + rs.append("$2a$"); + if (log_rounds < 10) + rs.append("0"); + rs.append(Integer.toString(log_rounds)); + rs.append("$"); + rs.append(encode_base64(rnd, rnd.length)); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * @param log_rounds the log2 of the number of rounds of hashing to apply + * - the work factor therefore increases as 2**log_rounds. + * @return an encoded salt value + */ + public static String gensalt(int log_rounds) { + return gensalt(log_rounds, new SecureRandom()); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method, selecting a + * reasonable default for the number of hashing rounds to apply + * @return an encoded salt value + */ + public static String gensalt() { + return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS); + } + + /** + * Check that a plaintext password matches a previously hashed one + * @param plaintext the plaintext password to verify + * @param hashed the previously-hashed password + * @return true if the passwords match, false otherwise + */ + public static boolean checkpw(String plaintext, String hashed) { + return (hashed.compareTo(hashpw(plaintext, hashed)) == 0); + } + } + +} diff --git a/Server/src/main/java/core/game/system/task/MovementHook.java b/Server/src/main/core/game/system/task/MovementHook.java similarity index 100% rename from Server/src/main/java/core/game/system/task/MovementHook.java rename to Server/src/main/core/game/system/task/MovementHook.java diff --git a/Server/src/main/java/core/game/system/task/NodeTask.java b/Server/src/main/core/game/system/task/NodeTask.java similarity index 95% rename from Server/src/main/java/core/game/system/task/NodeTask.java rename to Server/src/main/core/game/system/task/NodeTask.java index e827aa696..0e3e9afb8 100644 --- a/Server/src/main/java/core/game/system/task/NodeTask.java +++ b/Server/src/main/core/game/system/task/NodeTask.java @@ -1,7 +1,6 @@ package core.game.system.task; import core.game.node.Node; -import rs09.game.world.GameWorld; /** * Represents "Node pulse", which is used to execute methods with node @@ -79,7 +78,7 @@ public abstract class NodeTask { * @return The pulse used for this task. */ public Pulse schedule(final Node node, final Node... n) { - GameWorld.getPulser().submit(pulse = new Pulse(ticks, node) { + pulse = new Pulse(ticks, node) { @Override public void start() { @@ -102,7 +101,8 @@ public abstract class NodeTask { public boolean removeFor(String s) { return NodeTask.this.removeFor(s, node, n); } - }); + }; + pulse.start(); return pulse; } diff --git a/Server/src/main/java/core/game/system/task/Pulse.java b/Server/src/main/core/game/system/task/Pulse.java similarity index 99% rename from Server/src/main/java/core/game/system/task/Pulse.java rename to Server/src/main/core/game/system/task/Pulse.java index 827bca3aa..dc81708c3 100644 --- a/Server/src/main/java/core/game/system/task/Pulse.java +++ b/Server/src/main/core/game/system/task/Pulse.java @@ -12,7 +12,7 @@ public abstract class Pulse implements Runnable { /** * If the task is still running. */ - private boolean running = true; + public boolean running = true; /** * The amount of game-ticks to wait before execution. diff --git a/Server/src/main/java/core/game/system/task/TaskExecutor.kt b/Server/src/main/core/game/system/task/TaskExecutor.kt similarity index 100% rename from Server/src/main/java/core/game/system/task/TaskExecutor.kt rename to Server/src/main/core/game/system/task/TaskExecutor.kt 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 new file mode 100644 index 000000000..f64d97ec6 --- /dev/null +++ b/Server/src/main/core/game/world/DeadlockDetector.kt @@ -0,0 +1,38 @@ +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 deadLockedThreads = mbean.findDeadlockedThreads() + + + + + fun `(┛◉Д◉)┛彡┻â”â”»`() { + val infos = mbean.getThreadInfo(deadLockedThreads) + infos.forEach { threadInfo -> + if (threadInfo != null) { + for (thread in Thread.getAllStackTraces().keys) { + if (thread.id == threadInfo.threadId) { + log(this::class.java, Log.ERR, threadInfo.toString().trim()) + for (ste in thread.stackTrace) { + log(this::class.java, Log.ERR, "\t" + ste.toString().trim { it <= ' ' }) + } + } + } + } + } + } + + + if(deadLockedThreads != null){ + `(┛◉Д◉)┛彡┻â”â”»`() + } + + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/GameSettings.kt b/Server/src/main/core/game/world/GameSettings.kt similarity index 78% rename from Server/src/main/kotlin/rs09/game/world/GameSettings.kt rename to Server/src/main/core/game/world/GameSettings.kt index b3c2d9b82..e113dbbe3 100644 --- a/Server/src/main/kotlin/rs09/game/world/GameSettings.kt +++ b/Server/src/main/core/game/world/GameSettings.kt @@ -1,7 +1,7 @@ -package rs09.game.world +package core.game.world import org.json.simple.JSONObject -import rs09.ServerConstants +import core.ServerConstants import java.io.FileInputStream import java.io.IOException import java.util.* @@ -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, @@ -79,21 +77,25 @@ class GameSettings var increased_door_time: Boolean, var enabled_botting : Boolean, var max_adv_bots: Int, + 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 @@ -122,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 @@ -131,7 +131,12 @@ class GameSettings val increased_door_time = if(data.containsKey("increased_door_time")) data["increased_door_time"] as Boolean else false val enable_botting = if(data.containsKey("botting_enabled")) data["botting_enabled"] as Boolean else false val max_adv_bots = if(data.containsKey("max_adv_bots")) data["max_adv_bots"].toString().toInt() else 100 + 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() @@ -148,8 +153,6 @@ class GameSettings false, false, msip, - default_xp_rate, - allow_slayer_reroll, enable_default_clan, enable_bots, autostock_ge, @@ -158,7 +161,12 @@ class GameSettings increased_door_time, enable_botting, max_adv_bots, + 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 ) @@ -182,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 new file mode 100644 index 000000000..2c0b55e2c --- /dev/null +++ b/Server/src/main/core/game/world/GameWorld.kt @@ -0,0 +1,220 @@ +package core.game.world + +import core.api.* +import core.cache.Cache +import core.cache.def.impl.SceneryDefinition +import core.game.node.entity.player.Player +import core.game.system.SystemManager +import core.game.system.SystemState +import core.game.system.task.Pulse +import core.game.system.task.TaskExecutor +import core.game.world.map.Location +import core.game.world.map.Region +import core.game.world.map.RegionManager +import core.plugin.CorePluginTypes.StartupPlugin +import core.tools.RandomFunction +import core.ServerConstants +import core.ServerStore +import core.auth.AuthProvider +import core.auth.Auth +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.* +import java.util.function.Consumer +import kotlin.collections.ArrayList + +/** + * Represents the game world. + * @author Ceikry + */ +object GameWorld { + @JvmStatic + val worldPersists = ArrayList() + + /** + * The major update worker. + */ + @JvmStatic + val majorUpdateWorker = MajorUpdateWorker() + + /** + * Login listeners + */ + @JvmStatic + val loginListeners = ArrayList() + + /** + * Logout listeners + */ + @JvmStatic + val logoutListeners = ArrayList() + + /** + * Tick listeners + */ + @JvmStatic + val tickListeners = ArrayList() + + /** + * Startup Listeners + */ + @JvmStatic + val startupListeners = ArrayList() + + /** + * Shutdown Listeners + */ + @JvmStatic + val shutdownListeners = ArrayList() + + @JvmStatic + val STARTUP_PLUGINS: List = ArrayList() + private val configParser = ConfigParser() + @JvmStatic + var PCBotsSpawned = false + @JvmStatic + var PCnBotsSpawned = false + @JvmStatic + var PCiBotsSpawned = false + /** + * The game settings to use. + */ + @JvmStatic + var settings: GameSettings? = null + @JvmStatic + val authenticator: AuthProvider<*> + get() = Auth.authenticator + @JvmStatic + val accountStorage: AccountStorageProvider + get() = Auth.storageProvider + /** + * The current amount of (600ms) cycles elapsed. + */ + @JvmStatic + var ticks = 0 + + @JvmStatic + var Pulser = PulseRunner() + + /** + * Submits a pulse. + * + * @param pulse the pulse. + */ + @Deprecated("", ReplaceWith("Pulser.submit(pulse!!)", "core.game.world.GameWorld.Pulser")) + fun submit(pulse: Pulse?) { + Pulser.submit(pulse!!) + } + + fun pulse() { + ticks++ + if (ticks % 50 == 0) { + TaskExecutor.execute { + val player = Repository.players + try { + player.stream().filter { obj: Player? -> Objects.nonNull(obj) }.filter { p: Player -> !p.isArtificial && p.isPlaying }.forEach { p: Player? -> Repository.disconnectionQueue.save(p!!, false) } + } catch (t: Throwable) { + t.printStackTrace() + } + } + } + } + + fun checkDay(): Int { + val weeklySdf = SimpleDateFormat("u") + return weeklySdf.format(Date()).toInt() + } + + /** + * Prompts the [GameWorld] to begin it's initialization. + * + * @param directory the directory to the properties. + * @throws Throwable when the exception occurs. + */ + @Throws(Throwable::class) + fun prompt(directory: String?) { + prompt(true, directory) + } + + /** + * Prompts the game world. + * + * @param running if running. + * @throws Throwable the throwable. + */ + @Throws(Throwable::class) + @JvmStatic + fun prompt(running: Boolean) { + prompt(running, "server.properties") + } + + /** + * Prompts the [GameWorld] to begin its initialization. + * + * @param run If the server should be running. + * @param directory the path to the dir. + * @throws Throwable When an exception occurs. + */ + @Throws(Throwable::class) + fun prompt(run: Boolean, directory: String?){ + 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() } + ClassScanner.loadSideEffectfulPlugins() + configParser.postPlugin() + startupListeners.forEach { it.startup() } + if (run) { + SystemManager.flag(if (settings?.isDevMode == true) SystemState.PRIVATE else SystemState.ACTIVE) + } + SceneryDefinition.getDefinitions().values.forEach(Consumer { obj: SceneryDefinition -> obj.examine }) + + if (ServerConstants.PRELOAD_MAP) { + //force early loading of all commonly accessed regions to improve performance at the cost of memory usage + (7483..15420).forEach { id -> RegionManager.forId(id).also { Region.load(it) } } + } + + System.gc() + } + + /** + * Called when the server shuts down. + * + * @throws Throwable When an exception occurs. + */ + @Throws(Throwable::class) + fun shutdown() { + SystemManager.flag(SystemState.TERMINATED) + } + + /** + * Checks if its the economy world. + * + * @return `True` if so. + */ + @JvmStatic + val isEconomyWorld: Boolean + get() = false + + private fun generateLocation(): Location { + val random_location = Location(3075 + RandomFunction.random(-15, 15), 3954 + RandomFunction.random(-15, 15), 0) + if (!RegionManager.isTeleportPermitted(random_location)) { + return generateLocation() + } + return if (RegionManager.getObject(random_location) != null) { + generateLocation() + } else random_location + } +} diff --git a/Server/src/main/core/game/world/ImmerseWorld.kt b/Server/src/main/core/game/world/ImmerseWorld.kt new file mode 100644 index 000000000..2adee483f --- /dev/null +++ b/Server/src/main/core/game/world/ImmerseWorld.kt @@ -0,0 +1,257 @@ +package core.game.world + +import content.global.bots.* +import core.api.StartupListener +import core.game.node.entity.combat.CombatStyle +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.bots.GeneralBotCreator +import core.game.bots.CombatBotAssembler +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() { + 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) + { + Executors.newSingleThreadExecutor().execute { + Thread.currentThread().name = "BotSpawner" + immerseSeersAndCatherby() + immerseLumbridgeDraynor() + immerseVarrock() + immerseWilderness() + immerseFishingGuild() + immerseAdventurer() + immerseFalador() + // immerseSlayer() + immerseGE() + } + } + } + + fun immerseAdventurer(){ + for (i in 0..(GameWorld.settings?.max_adv_bots ?: 50)){ + var random = Random.nextInt(20000, 400000).toLong() + Timer().schedule(random){ + spawn_adventurers() + } + } + } + + fun spawn_adventurers() { + val lumbridge = Location.create(3221, 3219, 0) + val tiers = listOf(CombatBotAssembler.Tier.LOW, CombatBotAssembler.Tier.MED) + 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() { + val fishingGuild = Location.create(2604, 3421, 0) + for (i in (0..4)) { + GeneralBotCreator(SharkCatcher(), fishingGuild) + } + } + + fun immerseSeersAndCatherby() { + GeneralBotCreator( + SeersMagicTrees(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(2702, 3397, 0)) + ) + GeneralBotCreator( + SeersFlax(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(2738, 3444, 0)) + ) + GeneralBotCreator( + FletchingBankstander(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(2722, 3493, 0)) + ) + GeneralBotCreator( + GlassBlowingBankstander(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(2807, 3441, 0)) + ) + GeneralBotCreator(LobsterCatcher(), Location.create(2805, 3435, 0)) + } + + fun immerseLumbridgeDraynor() { + GeneralBotCreator( + CowKiller(), + assembler.produce( + CombatBotAssembler.Type.RANGE, + CombatBotAssembler.Tier.MED, + Location.create(3261, 3269, 0) + ) + ) + GeneralBotCreator( + CowKiller(), + assembler.produce( + CombatBotAssembler.Type.MELEE, + CombatBotAssembler.Tier.LOW, + Location.create(3261, 3269, 0) + ) + ) + GeneralBotCreator( + CowKiller(), + assembler.produce( + CombatBotAssembler.Type.MELEE, + CombatBotAssembler.Tier.MED, + Location.create(3257, 3267, 0) + ) + ) + GeneralBotCreator( + ManThiever(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3235, 3213, 0)) + ) + GeneralBotCreator( + FarmerThiever(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3094, 3243, 0)) + ) + GeneralBotCreator( + FarmerThiever(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3094, 3243, 0)) + ) + GeneralBotCreator( + DraynorWillows(), + skillingBotAssembler.produce( + SkillingBotAssembler.Wealth.values().random(), + Location.create(3094, 3245, 0) + ) + ) + GeneralBotCreator( + DraynorWillows(), + skillingBotAssembler.produce( + SkillingBotAssembler.Wealth.values().random(), + Location.create(3094, 3245, 0) + ) + ) + GeneralBotCreator( + DraynorWillows(), + skillingBotAssembler.produce( + SkillingBotAssembler.Wealth.values().random(), + Location.create(3094, 3245, 0) + ) + ) + GeneralBotCreator( + DraynorFisher(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3095, 3246, 0)) + ) + GeneralBotCreator( + DraynorFisher(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3095, 3246, 0)) + ) + GeneralBotCreator( + DraynorFisher(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3095, 3246, 0)) + ) + } + + fun immerseVarrock() { + val WestBankIdlerBorders = ZoneBorders(3184, 3435, 3187, 3444) + GeneralBotCreator( + GlassBlowingBankstander(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.RICH, Location.create(3189, 3435, 0)) + ) + GeneralBotCreator( + FletchingBankstander(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(3189, 3439, 0)) + ) + GeneralBotCreator( + Idler(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.RICH, WestBankIdlerBorders.randomLoc) + ) + GeneralBotCreator( + GlassBlowingBankstander(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3256, 3420, 0)) + ) + GeneralBotCreator( + VarrockEssenceMiner(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3253, 3420, 0)) + ) + GeneralBotCreator( + VarrockSmither(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.RICH, Location.create(3189, 3436, 0)) + ) + GeneralBotCreator( + NonBankingMiner(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3182, 3374, 0)) + ) + } + + fun immerseWilderness() { + val wilderness = Location.create(3092, 3493, 0) + + repeat(6) { + GeneralBotCreator ( + GreenDragonKiller(CombatStyle.MELEE), + assembler.assembleMeleeDragonBot(CombatBotAssembler.Tier.MED, wilderness) + ) + } + } + + fun immerseFalador() { + GeneralBotCreator( + 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() { + GeneralBotCreator( + GenericSlayerBot(), + assembler.produce( + CombatBotAssembler.Type.MELEE, + CombatBotAssembler.Tier.HIGH, + Location.create(2673, 3635, 0) + ) + ) + } + + private fun immerseGE() { + spawnDoubleMoneyBot(false) + } + + fun spawnDoubleMoneyBot(delay: Boolean) { + if (GameWorld.settings?.enable_doubling_money_scammers != true) return + val random: Long = (10_000..7_200_000).random().toLong() + Timer().schedule(if (delay) random else 0) { + GeneralBotCreator ( + DoublingMoney(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, DoublingMoney.startingLocs.random()) + ) + } + } + } +} diff --git a/Server/src/main/core/game/world/PulseRunner.kt b/Server/src/main/core/game/world/PulseRunner.kt new file mode 100644 index 000000000..e320cd7c9 --- /dev/null +++ b/Server/src/main/core/game/world/PulseRunner.kt @@ -0,0 +1,83 @@ +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() + + val currentPulses: Array get() = pulses.toTypedArray() + + fun submit(pulse: Pulse) { + pulses.add(pulse) + } + + 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() && pulse.isRunning) { + pulses.add(pulse) + } + } catch (e: Exception) { + 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 { + val startTime = System.currentTimeMillis() + + logic() + + return System.currentTimeMillis() - startTime + } + + private fun notifyIfTooLong(pulse: Pulse, elapsedTime: Long) { + if (elapsedTime >= 100) { + if (pulse is GeneralBotCreator.BotScriptPulse) { + log(this::class.java, Log.WARN, "CRITICALLY long bot-script tick - ${pulse.botScript.javaClass.name} took $elapsedTime ms") + } else { + 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) { + log(this::class.java, Log.WARN, "Long bot-script tick - ${pulse.botScript.javaClass.name} took $elapsedTime ms") + } else { + log(this::class.java, Log.WARN, "Long running pulse - ${pulse.javaClass.name} took $elapsedTime ms") + } + } + } +} diff --git a/Server/src/main/core/game/world/map/BuildRegionChunk.java b/Server/src/main/core/game/world/map/BuildRegionChunk.java new file mode 100644 index 000000000..008937301 --- /dev/null +++ b/Server/src/main/core/game/world/map/BuildRegionChunk.java @@ -0,0 +1,336 @@ +package core.game.world.map; + + +import core.game.node.entity.player.Player; +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; +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 + * + */ +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 = 10; + + /** + * The list of changes made. + */ + private final Scenery[][][] objects; + + /** + * Constructs a new {@code BuildRegionChunk} {@code Object} + * @param base The base location. + * @param rotation The rotation. + * @param plane The region plane. + */ + public BuildRegionChunk(Location base, int rotation, RegionPlane plane) { + super(base, rotation, plane); + this.objects = new Scenery[ARRAY_SIZE][8][8]; + } + + 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 + protected boolean appendUpdate(Player player, IoBuffer buffer) { + boolean updated = false;//super.appendUpdate(player, buffer); + for (int i = 0; i < objects.length; i++) { + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + Scenery o = objects[i][x][y]; + if (o instanceof Constructed) { + ConstructScenery.write(buffer, o); + updated = true; + } + else if (o != null && !o.isRenderable()) { + ClearScenery.write(buffer, o); + 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; + } + + @Override + public void rotate(Direction direction) { + if (rotation != 0) { + log(this.getClass(), Log.ERR, "Region chunk was already rotated!"); + return; + } + Scenery[][][] copy = new Scenery[ARRAY_SIZE][SIZE][SIZE]; + int baseX = currentBase.getLocalX(); + int baseY = currentBase.getLocalY(); + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + for (int i = 0; i < objects.length; i++) { + Scenery object = copy[i][x][y] = objects[i][x][y]; + if (object != null) { + SceneryBuilder.remove(object); + this.remove(object); + } + } + } + } + clear(); + 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++) { + Scenery object = copy[i][x][y]; + if (object != null) { + int[] pos = getRotatedPosition(x, y, object.getDefinition().getSizeX(), object.getDefinition().getSizeY(), object.getRotation(), rotation); + Scenery obj = object.transform(object.getId(), (object.getRotation() + rotation) % 4, object.getLocation().transform(pos[0] - x, pos[1] - y, 0)); + if (object instanceof Constructed) { + obj = obj.asConstructed(); + } + obj.setActive(object.isActive()); + obj.setRenderable(object.isRenderable()); + LandscapeParser.flagScenery(plane, baseX + pos[0], baseY + pos[1], obj, true, true); + } + } + } + } + } + + @Override + public BuildRegionChunk copy(RegionPlane plane) { + BuildRegionChunk chunk = new BuildRegionChunk(base, rotation, plane); + for (int i = 0; i < chunk.objects.length; i++) { + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + Scenery o = objects[i][x][y]; + if (o instanceof Constructed) { + chunk.objects[i][x][y] = o.transform(o.getId(), o.getRotation()).asConstructed(); + } + else if (o != null) { + chunk.objects[i][x][y] = o.transform(o.getId()); + chunk.objects[i][x][y].setActive(o.isActive()); + chunk.objects[i][x][y].setRenderable(o.isRenderable()); + } + } + } + } + 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(); + 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++) { + objects[i][x][y] = null; + } + } + } + } + + /** + * Removes the scenery. + * @param object The object to remove. + */ + public void remove(Scenery object) { + int chunkX = object.getLocation().getChunkOffsetX(); + int chunkY = object.getLocation().getChunkOffsetY(); + Scenery current = null; + int index = -1; + int i = 0; + while ((current == null || current.getId() != object.getId()) && i < objects.length) { + current = objects[i++][chunkX][chunkY]; + if ((current == null || current.getId() < 1) && index == -1) { + index = i - 1; + } + } + if (current != null && current.equals(object)) { + current.setActive(false); + object.setRenderable(false); + } + else { + objects[index][chunkX][chunkY] = object; + } + object.setActive(false); + object.setRenderable(false); + } + + /** + * Adds the scenery. + * @param object The object to add. + */ + public void add(Scenery object) { + int chunkX = object.getLocation().getChunkOffsetX(); + int chunkY = object.getLocation().getChunkOffsetY(); + Scenery current = null; + int index = -1; + int i = 0; + while ((current == null || current.getId() != object.getId()) && i < objects.length) { + current = objects[i++][chunkX][chunkY]; + if ((current == null || current.getId() < 1) && index == -1) { + index = i - 1; + } + } + if (current != null && current.equals(object)) { + current.setActive(true); + current.setRenderable(true); + } + else if (index == -1) { + throw new IllegalStateException("Insufficient array length for storing object!"); + } + else { + objects[index][chunkX][chunkY] = object = object.asConstructed(); + } + object.setActive(true); + object.setRenderable(true); + } + + /** + * Stores an object on the region chunk. + * @param object The object. + */ + public void store(Scenery object) { + if (object == null) { + return; + } + int chunkX = object.getLocation().getChunkOffsetX(); + int chunkY = object.getLocation().getChunkOffsetY(); + for (int i = 0; i < objects.length; i++) { + Scenery stat = objects[i][chunkX][chunkY]; + if (stat == null || stat.getId() < 1) { + objects[i][chunkX][chunkY] = object; + object.setActive(true); + object.setRenderable(true); + return; + } + } + System.err.print("Objects - ["); + for (int i = 0; i < objects.length; i++) { + System.err.print(objects[i][chunkX][chunkY]); + if (i < objects.length - 1) { + System.err.print(", "); + } + } + log(this.getClass(), Log.ERR, "]!"); + throw new IllegalStateException("Insufficient array length for storing all objects! "); + } + + /** + * Gets the objects index for the given object id. + * @param x The x-coordinate on the region chunk. + * @param y The y-coordinate on the region chunk. + * @param objectId The object id. + */ + public int getIndex(int x, int y, int objectId) { + for (int i = 0; i < objects.length; i++) { + Scenery o = get(x, y, i); + if (o != null && ((objectId > -1 && o.getId() == objectId) || (objectId == -1 && o.getDefinition().hasOptions(false)))) { + return i; + } + } + return 0; + } + + /** + * Gets a scenery. + * @param x The chunk x-coordinate. + * @param y The chunk y-coordinate. + * @param index The index (0 = default). + * @return The object. + */ + public Scenery get(int x, int y, int index) { + return objects[index][x][y]; + } + + @Override + public Scenery[] getObjects(int chunkX, int chunkY) { + Scenery[] objects = new Scenery[ARRAY_SIZE]; + for (int i = 0; i < ARRAY_SIZE; i++) { + objects[i] = this.objects[i][chunkX][chunkY]; + } + return objects; + } + + /** + * Gets the objects. + * @param index The index. + * @return The objects array. + */ + 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/java/core/game/world/map/Direction.java b/Server/src/main/core/game/world/map/Direction.java similarity index 84% rename from Server/src/main/java/core/game/world/map/Direction.java rename to Server/src/main/core/game/world/map/Direction.java index 5b34c2f50..4555f931b 100644 --- a/Server/src/main/java/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.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/java/core/game/world/map/MapDistance.java b/Server/src/main/core/game/world/map/MapDistance.java similarity index 100% rename from Server/src/main/java/core/game/world/map/MapDistance.java rename to Server/src/main/core/game/world/map/MapDistance.java diff --git a/Server/src/main/java/core/game/world/map/Point.java b/Server/src/main/core/game/world/map/Point.java similarity index 100% rename from Server/src/main/java/core/game/world/map/Point.java rename to Server/src/main/core/game/world/map/Point.java diff --git a/Server/src/main/java/core/game/world/map/Region.java b/Server/src/main/core/game/world/map/Region.java similarity index 85% rename from Server/src/main/java/core/game/world/map/Region.java rename to Server/src/main/core/game/world/map/Region.java index 78b580d7c..ff87d8e6b 100644 --- a/Server/src/main/java/core/game/world/map/Region.java +++ b/Server/src/main/core/game/world/map/Region.java @@ -4,15 +4,16 @@ import core.cache.Cache; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.music.MusicZone; +import core.game.system.communication.CommunicationInfo; import core.game.system.task.Pulse; 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 rs09.game.system.SystemLogger; -import rs09.game.system.config.XteaParser; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; +import core.tools.Log; +import core.game.system.config.XteaParser; +import core.game.world.GameWorld; +import core.game.world.repository.Repository; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -20,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. * @@ -58,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); @@ -147,6 +155,18 @@ public class Region { } } + public void remove(RegionZone zone) { + regionZones.remove(zone); + for (RegionPlane plane : planes) { + for (NPC npc : plane.getNpcs()) { + npc.getZoneMonitor().updateLocation(npc.getLocation()); + } + for (Player p : plane.getPlayers()) { + p.getZoneMonitor().updateLocation(p.getLocation()); + } + } + } + /** * Adds a player to this region. * @param player The player. @@ -252,12 +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 void flagInactive() { - unload(this); - active = false; + public boolean flagInactive() { + return flagInactive(false); } /** @@ -284,11 +312,17 @@ public class Region { int regionX = regionId >> 8 & 0xFF; int regionY = regionId & 0xFF; int mapscapeId = Cache.getIndexes()[5].getArchiveId("m" + regionX + "_"+ regionY); + + if (mapscapeId < 0 && !dynamic) { + r.setLoaded(true); + return; + } + byte[][][] mapscapeData = new byte[4][SIZE][SIZE]; for (RegionPlane plane : r.planes) { plane.getFlags().setLandscape(new boolean[SIZE][SIZE]); - plane.getFlags().setClippingFlags(new int[SIZE][SIZE]); - plane.getProjectileFlags().setClippingFlags(new int[SIZE][SIZE]); + //plane.getFlags().setClippingFlags(new int[SIZE][SIZE]); + //plane.getProjectileFlags().setClippingFlags(new int[SIZE][SIZE]); } if (mapscapeId > -1) { ByteBuffer mapscape = ByteBuffer.wrap(Cache.getIndexes()[5].getCacheFile().getContainerUnpackedData(mapscapeId)); @@ -315,21 +349,25 @@ public class Region { } } + public static boolean unload(Region r) { + return unload(r, false); + } + /** * Unloads a region. * @param r The region. */ - private static void unload(Region r) { - if (r.isViewed()) { - SystemLogger.logErr("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; + return false; } for (RegionPlane p : r.planes) { - if (!p.getPlayers().isEmpty()) { - SystemLogger.logErr("Players still in region!"); + if (!force && !p.getPlayers().isEmpty()) { + log(CommunicationInfo.class, Log.ERR, "Players still in region!"); r.flagActive(); - return; + return false; } } for (RegionPlane p : r.planes) { @@ -340,6 +378,10 @@ public class Region { } } } + if (r.isBuild()) + r.setLoaded(false); + r.activityPulse.stop(); + return true; } /** @@ -369,7 +411,7 @@ public class Region { public int decrementViewAmount() { synchronized (this) { if (viewAmount < 1) { - //SystemLogger.logErr("View amount is " + (viewAmount - 1)); + //log(this.getClass(), Log.ERR, "View amount is " + (viewAmount - 1)); viewAmount++; } return --viewAmount; @@ -435,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. @@ -546,4 +603,4 @@ public class Region { public void setUpdateAllPlanes(boolean updateAllPlanes) { this.updateAllPlanes = updateAllPlanes; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/RegionChunk.java b/Server/src/main/core/game/world/map/RegionChunk.java new file mode 100644 index 000000000..7b3fb71c8 --- /dev/null +++ b/Server/src/main/core/game/world/map/RegionChunk.java @@ -0,0 +1,441 @@ +package core.game.world.map; + +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; +import core.game.world.update.flag.UpdateFlag; +import core.net.packet.IoBuffer; +import core.net.packet.out.ClearScenery; +import core.net.packet.out.ConstructGroundItem; +import core.net.packet.out.ConstructScenery; +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. + */ + protected Scenery[][] objects; + + /** + * 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. + * @param rotation The rotation. + */ + public RegionChunk(Location base, int rotation, RegionPlane plane) { + this.base = base; + this.currentBase = base; + this.rotation = rotation; + this.plane = plane; + this.objects = new Scenery[SIZE][SIZE]; + } + + /** + * Copies the region chunk. + * @param plane The region plane. + * @return The region chunk. + */ + public BuildRegionChunk copy(RegionPlane plane) { + return new BuildRegionChunk(base, rotation, plane, this.objects); + } + + /** + * Registers an update flag. + * @param flag The flag. + */ + public void flag(UpdateFlag flag) { + flags.add(flag); + } + + /** + * Clears the region chunk. + */ + public void clear() { + flags.clear(); + if (items != null && plane.getRegion() instanceof DynamicRegion) { + items.clear(); + items = null; + } + } + + /** + * Updates the region chunk. + * @param player The player. + */ + public void synchronize(Player player) { + IoBuffer buffer = UpdateAreaPosition.getChunkUpdateBuffer(player, currentBase); + if (appendUpdate(player, buffer)) { + player.getSession().write(buffer); + } + } + + /** + * Writes the region chunk update data on the buffer. + * @param player The player we're updating for. + * @param buffer The buffer to write on. + * @return {@code True} if an update occured. + */ + protected boolean appendUpdate(Player player, IoBuffer buffer) { + boolean updated = false; + int baseX = currentBase.getLocalX(); + int baseY = currentBase.getLocalY(); + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + Scenery dyn = objects[x][y]; + if (dyn == null || plane.getObjects() == null) { + continue; + } + Scenery stat = plane.getObjects()[baseX + x][baseY + y]; + if (!dyn.isRenderable() && stat != null) { + ClearScenery.write(buffer, stat); + updated = true; + } + else if (dyn != stat) { + if (stat != null) { + ClearScenery.write(buffer, stat); + } + ConstructScenery.write(buffer, dyn); + 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. + */ + public void update(Player player) { + if (isUpdated()) { + IoBuffer buffer = UpdateAreaPosition.getChunkUpdateBuffer(player, currentBase); + Object[] flagsArray = flags.toArray(); + int size = flagsArray.length; + for (int i = 0; i < size; i++) { + UpdateFlag flag = (UpdateFlag) flagsArray[i]; + flag.writeDynamic(buffer, player); + } + player.getSession().write(buffer); + } + } + + /** + * Rotates the chunk. + * @param direction The direction. + */ + public void rotate(Direction direction) { + if (rotation != 0) { + log(this.getClass(), Log.ERR, "Region chunk was already rotated!"); + return; + } + Scenery[][] copy = new Scenery[SIZE][SIZE]; + Scenery[][] staticCopy = new Scenery[SIZE][SIZE]; + int baseX = currentBase.getLocalX(); + int baseY = currentBase.getLocalY(); + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + copy[x][y] = objects[x][y]; + staticCopy[x][y] = plane.getObjects()[baseX + x][baseY + y]; + objects[x][y] = plane.getObjects()[baseX + x][baseY + y] = null; + plane.getFlags().clearFlag(baseX + x, baseY + y); + } + } + rotation = direction.toInteger(); + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + Scenery object = copy[x][y]; + Scenery stat = staticCopy[x][y]; + boolean match = object == stat; + if (stat == null) { + continue; + } + int[] pos = getRotatedPosition(x, y, stat.getDefinition().getSizeX(), stat.getDefinition().getSizeY(), stat.getRotation(), rotation); + if (object != null) { + object = object.transform(object.getId(), (object.getRotation() + rotation) % 4, object.getLocation().transform(pos[0] - x, pos[1] - y, 0)); + LandscapeParser.flagScenery(plane, baseX + pos[0], baseY + pos[1], object, true, true); + } + if (match) { + stat = object; + } else { + stat = stat.transform(stat.getId(), (stat.getRotation() + rotation) % 4, stat.getLocation().transform(pos[0] - x, pos[1] - y, 0)); + } + plane.getObjects()[baseX + pos[0]][baseY + pos[1]] = stat; + } + } + } + + /** + * Gets the new coordinates for an object/chunk tile when rotating. + * @param x The current x-coordinate. + * @param y The current y-coordinate. + * @param sizeX The x-size of the object. + * @param sizeY The y-size of the object. + * @param rotation The object rotation. + * @param chunkRotation The chunk rotation. + * @return The new x-coordinate. + */ + public static int[] getRotatedPosition(int x, int y, int sizeX, int sizeY, int rotation, int chunkRotation) { + if ((rotation & 0x1) == 1) { + int s = sizeX; + sizeX = sizeY; + sizeY = s; + } + if (chunkRotation == 0) { + return new int[] { x, y }; + } + if (chunkRotation == 1) { + return new int[] { y, 7 - x - (sizeX - 1) }; + } + if (chunkRotation == 2) { + return new int[] { 7 - x - (sizeX - 1), 7 - y - (sizeY - 1) }; + } + return new int[] { 7 - y - (sizeY - 1), x }; + } + + /** + * Gets the items. + * @return The items. + */ + public List getItems() { + if (items == null) { + items = new ArrayList(); + } + return items; + } + + /** + * Sets the items. + * @param items The items to set. + */ + public void setItems(List items) { + this.items = items; + } + + /** + * Gets the scenerys located on the coordinates in this chunk. + * @param chunkX The chunk x-coordinate (0-7). + * @param chunkY The chunk y-coordinate (0-7). + * @return The objects. + */ + public Scenery[] getObjects(int chunkX, int chunkY) { + return new Scenery[] { objects[chunkX][chunkY] }; + } + + /** + * Gets the objects. + * @return The objects. + */ + public Scenery[][] getObjects() { + return objects; + } + + /** + * Sets the objects. + * @param objects The objects to set. + */ + public void setObjects(Scenery[][] objects) { + this.objects = objects; + } + + /** + * Gets the base. + * @return The base. + */ + public Location getBase() { + return base; + } + + /** + * Sets the base location of the region to copy. + * @param base The base location. + */ + public void setBase(Location base) { + this.base = base; + } + + /** + * Gets the rotation. + * @return The rotation. + */ + public int getRotation() { + return rotation; + } + + /** + * Sets the rotation of the region chunk. + * @param rotation The rotation + */ + public void setRotation(int rotation) { + this.rotation = rotation; + } + + /** + * Gets the updated. + * @return The updated. + */ + public boolean isUpdated() { + return !flags.isEmpty(); + } + + /** + * Resets the flags. + */ + public void resetFlags() { + flags.clear(); + } + + /** + * Gets the region plane. + * @return The plane. + */ + public RegionPlane getPlane() { + return plane; + } + + /** + * Gets the currentBase. + * @return The currentBase. + */ + public Location getCurrentBase() { + return currentBase; + } + + /** + * Sets the currentBase. + * @param currentBase The currentBase to set. + */ + public void setCurrentBase(Location currentBase) { + this.currentBase = currentBase; + } + + 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/java/core/game/world/map/RegionManager.kt b/Server/src/main/core/game/world/map/RegionManager.kt similarity index 80% rename from Server/src/main/java/core/game/world/map/RegionManager.kt rename to Server/src/main/core/game/world/map/RegionManager.kt index 9b1ec02f4..9fb7b848b 100644 --- a/Server/src/main/java/core/game/world/map/RegionManager.kt +++ b/Server/src/main/core/game/world/map/RegionManager.kt @@ -1,18 +1,19 @@ package core.game.world.map -import core.game.world.map.RegionManager -import core.game.world.map.RegionPlane -import core.game.node.scenery.Scenery +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 rs09.game.system.SystemLogger +import core.tools.SystemLogger import java.util.* import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantLock +import kotlin.collections.HashMap /** * Manages the regions. @@ -23,6 +24,8 @@ object RegionManager { * The region cache mapping. */ private val REGION_CACHE: MutableMap = HashMap() + @JvmStatic val CLIPPING_FLAGS = HashMap>() + @JvmStatic val PROJECTILE_FLAGS = HashMap>() public val LOCK = ReentrantLock() @@ -42,7 +45,7 @@ object RegionManager { LOCK.unlock() return REGION_CACHE[regionId]!! } - SystemLogger.logErr("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) } @@ -82,16 +85,56 @@ object RegionManager { */ @JvmStatic fun getClippingFlag(z: Int, x: Int, y: Int): Int { - var x = x - var y = y - val region = forId(((x shr 6) shl 8) or (y shr 6)) - Region.load(region) - if (!region.isHasFlags) { - return -1 + val regionX = x shr 6 + val regionY = y shr 6 + val localX = x and 63 + val localY = y and 63 + return getClippingFlag(z, regionX, regionY, localX, localY) + } + + /** + * Gets the clipping flags using Jagex-style coords + * e.g 0_50_50_13_13 gets plane 0, region 50-50 (12850), (13, 13) which is in lumbridge. + */ + @JvmStatic + fun getClippingFlag(z: Int, regionX: Int, regionY: Int, localX: Int, localY: Int, projectile: Boolean = false) : Int { + val (region, index) = getFlagIndex(z, regionX, regionY, localX, localY) + var flag = getFlags(region, projectile)[index] + + if (flag == -1) { + val r = forId((regionX shr 8) or regionY) + if (!r.isLoaded) + Region.load(r) + if (!r.isHasFlags) + return -1 + flag = getFlags(region, projectile)[index] } - x -= (x shr 6) shl 6 - y -= (y shr 6) shl 6 - return region.planes[z].flags.clippingFlags[x][y] + + return flag + } + + private fun getFlagIndex(z: Int, regionX: Int, regionY: Int, localX: Int, localY: Int) : Pair { + return Pair((regionX shl 8) or regionY, (z * 64 * 64) + (localX * 64) + localY) + } + + @JvmStatic + fun getFlags(regionX: Int, regionY: Int, projectile: Boolean) : Array { + val region = (regionX shl 8) or regionY + return getFlags(region, projectile) + } + + @JvmStatic + fun getFlags(regionId: Int, projectile: Boolean) : Array { + return if (projectile) + PROJECTILE_FLAGS.getOrPut (regionId) {Array(16384){0}} + else + CLIPPING_FLAGS.getOrPut (regionId) {Array(16384){-1}} + } + + @JvmStatic + fun resetFlags(regionId: Int) { + PROJECTILE_FLAGS.put (regionId, Array(16384){0}) + CLIPPING_FLAGS.put (regionId, Array(16384){-1}) } /** @@ -138,26 +181,6 @@ object RegionManager { return region.planes[z].flags.landscape[x][y] } - /** - * Sets the clipping flag on the given location. - * @param l The location. - * @param flag The flag to set. - */ - @JvmStatic - fun setClippingFlag(l: Location, flag: Int) { - var x = l.x - var y = l.y - val z = l.z - val region = forId(((x shr 6) shl 8) or (y shr 6)) - Region.load(region) - if (!region.isHasFlags) { - return - } - x -= x shr 6 shl 6 - y -= y shr 6 shl 6 - region.planes[z].flags.clippingFlags[x][y] = flag - } - /** * Adds a clipping flag. * @param z The plane. @@ -219,16 +242,11 @@ object RegionManager { */ @JvmStatic fun getProjectileFlag(z: Int, x: Int, y: Int): Int { - var x = x - var y = y - val region = forId(((x shr 6) shl 8) or (y shr 6)) - Region.load(region) - if (!region.isHasFlags) { - return -1 - } - x -= (x shr 6) shl 6 - y -= (y shr 6) shl 6 - return region.planes[z].projectileFlags.clippingFlags[x][y] + val regionX = x shr 6 + val regionY = y shr 6 + val localX = x and 63 + val localY = y and 63 + return getClippingFlag(z, regionX, regionY, localX, localY, true) } /** @@ -294,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 } /** @@ -587,8 +619,17 @@ object RegionManager { val it = npcs.iterator() while (it.hasNext()) { val p = it.next() + if(p.properties.teleportLocation != null && !p.properties.teleportLocation.withinMaxnormDistance(n.location, 1)) { + it.remove() + continue + } + if(p.getAttribute("state:death", false)) { + it.remove() + continue + } if(p.isInvisible()) { it.remove() + continue } if(!p.location.withinMaxnormDistance(n.location, 1)) { it.remove() @@ -729,6 +770,52 @@ 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. + * @param distance The distance to that location. + * @return The list of players. + */ + @JvmStatic + fun getLocalPlayersMaxNorm(l: Location, distance: 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.withinMaxnormDistance(l, distance)) { + players.add(player) + } + } + } + } + return players + } + /** * Gets a list of local players within 16 tile distance of the location. * @param l The location. @@ -807,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/java/core/game/world/map/RegionPlane.java b/Server/src/main/core/game/world/map/RegionPlane.java similarity index 89% rename from Server/src/main/java/core/game/world/map/RegionPlane.java rename to Server/src/main/core/game/world/map/RegionPlane.java index 15b68bc03..f83724ec0 100644 --- a/Server/src/main/java/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/java/core/game/world/map/Viewport.java b/Server/src/main/core/game/world/map/Viewport.java similarity index 99% rename from Server/src/main/java/core/game/world/map/Viewport.java rename to Server/src/main/core/game/world/map/Viewport.java index 8489b40c0..d563b0357 100644 --- a/Server/src/main/java/core/game/world/map/Viewport.java +++ b/Server/src/main/core/game/world/map/Viewport.java @@ -152,4 +152,4 @@ public final class Viewport { this.currentPlane = currentPlane; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/world/map/build/DynamicRegion.java b/Server/src/main/core/game/world/map/build/DynamicRegion.java similarity index 87% rename from Server/src/main/java/core/game/world/map/build/DynamicRegion.java rename to Server/src/main/core/game/world/map/build/DynamicRegion.java index bbfef5458..2ccbabc69 100644 --- a/Server/src/main/java/core/game/world/map/build/DynamicRegion.java +++ b/Server/src/main/core/game/world/map/build/DynamicRegion.java @@ -10,6 +10,7 @@ import core.game.world.map.*; import core.game.world.map.zone.RegionZone; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.impl.MultiwayCombatZone; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Arrays; @@ -76,6 +77,14 @@ public final class DynamicRegion extends Region { super(x, y); this.regionId = regionId; this.chunks = new RegionChunk[4][SIZE >> 3][SIZE >> 3]; + RegionManager.resetFlags(getId()); + } + + public DynamicRegion(@NotNull ZoneBorders borders) { + this(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6); + setBorders(borders); + setUpdateAllPlanes(true); + RegionManager.addRegion(getId(), this); } /** @@ -206,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)); @@ -310,8 +323,8 @@ public final class DynamicRegion extends Region { if (chunk == null) { for (int i = x << 3; i < (x + 1) << 3; i++) { for (int j = y << 3; j < (y + 1) << 3; j++) { - p.getFlags().getClippingFlags()[i][j] = -1; - p.getProjectileFlags().getClippingFlags()[i][j] = -1; + p.getFlags().invalidateFlag(i, j); + p.getProjectileFlags().invalidateFlag(i, j); Scenery object = p.getObjects()[i][j]; if (object != null) { LandscapeParser.removeScenery(object); @@ -321,59 +334,31 @@ 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().getClippingFlags()[toX][toY] = rp.getFlags().getClippingFlags()[fromX][fromY]; - p.getProjectileFlags().getClippingFlags()[toX][toY] = rp.getProjectileFlags().getClippingFlags()[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 void flagInactive() { + public boolean flagInactive(boolean force) { if (!permanent) { if (parentRegion != null && parentRegion.isActive()) { parentRegion.checkInactive(); - return; + return false; } if (linked != null) { for (DynamicRegion r : linked) { if (!r.isInactive(false)) { - return; + return false; } } } - super.flagInactive(); + if(!super.flagInactive(force)) { + return false; + } for (RegionPlane plane : getPlanes()) { for (int i = 0; i < plane.getNpcs().size(); i++) { NPC npc = plane.getNpcs().get(0); @@ -393,12 +378,16 @@ public final class DynamicRegion extends Region { if (multicombat) { toggleMulticombat(); } + boolean allLinkedInactive = true; if (linked != null) { for (DynamicRegion r : linked) { - r.flagInactive(); + allLinkedInactive &= r.flagInactive(force); } } - } + return allLinkedInactive; + } else { + return true; + } } @Override @@ -476,4 +465,4 @@ public final class DynamicRegion extends Region { } NPCs.clear(); } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/world/map/build/LandscapeParser.java b/Server/src/main/core/game/world/map/build/LandscapeParser.java similarity index 93% rename from Server/src/main/java/core/game/world/map/build/LandscapeParser.java rename to Server/src/main/core/game/world/map/build/LandscapeParser.java index 703e3907b..81025e7d6 100644 --- a/Server/src/main/java/core/game/world/map/build/LandscapeParser.java +++ b/Server/src/main/core/game/world/map/build/LandscapeParser.java @@ -7,6 +7,7 @@ import core.game.world.map.Location; import core.game.world.map.Region; import core.game.world.map.RegionManager; import core.game.world.map.RegionPlane; +import core.game.system.config.ObjectConfigParser; import java.nio.ByteBuffer; @@ -46,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--; @@ -87,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; @@ -97,19 +114,10 @@ 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; - if (def.secondInt != 0 || def.clipType == 1 || def.secondBool) { + if (def.interactable != 0 || def.clipType == 1 || def.secondBool) { if (def.clipType == 1) { plane.getFlags().flagTileObject(localX, localY); if (def.isProjectileClipped()) { @@ -132,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; } /** @@ -189,7 +195,7 @@ public final class LandscapeParser { } int type = object.getType(); if (type == 22) { //Tile - if (def.secondInt != 0 || def.clipType == 1 || def.secondBool) { + if (def.interactable != 0 || def.clipType == 1 || def.secondBool) { if (def.clipType == 1) { plane.getFlags().unflagTileObject(localX, localY); if (def.isProjectileClipped()) { @@ -214,4 +220,4 @@ public final class LandscapeParser { } return current; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/world/map/build/MapscapeParser.java b/Server/src/main/core/game/world/map/build/MapscapeParser.java similarity index 96% rename from Server/src/main/java/core/game/world/map/build/MapscapeParser.java rename to Server/src/main/core/game/world/map/build/MapscapeParser.java index 896a464ee..7ecc4c81c 100644 --- a/Server/src/main/java/core/game/world/map/build/MapscapeParser.java +++ b/Server/src/main/core/game/world/map/build/MapscapeParser.java @@ -60,6 +60,7 @@ public final class MapscapeParser { for (int z = 0; z < 4; z++) { for (int x = 0; x < 64; x++) { for (int y = 0; y < 64; y++) { + r.getPlanes()[z].getFlags().flagEmptyTile(x,y); if ((mapscape[z][x][y] & 0x1) == 1) { int plane = z; if ((mapscape[1][x][y] & 0x2) == 2) { diff --git a/Server/src/main/java/core/game/world/map/build/RegionFlags.java b/Server/src/main/core/game/world/map/build/RegionFlags.java similarity index 83% rename from Server/src/main/java/core/game/world/map/build/RegionFlags.java rename to Server/src/main/core/game/world/map/build/RegionFlags.java index 131407bca..177da33c9 100644 --- a/Server/src/main/java/core/game/world/map/build/RegionFlags.java +++ b/Server/src/main/core/game/world/map/build/RegionFlags.java @@ -1,6 +1,9 @@ package core.game.world.map.build; import core.game.world.map.RegionManager; +import kotlin.Pair; + +import static java.lang.Math.max; /** * Holds a region's flags like clipping flags, members, ... @@ -9,6 +12,11 @@ import core.game.world.map.RegionManager; */ public final class RegionFlags { + public static final int TILE_OBJECT = 0x40000; + public static final int EMPTY_TILE = 0; + public static final int SOLID_TILE = 0x200000; + public static final int OBJ_10_PROJECTILE = 0x20000; + public static final int OBJ_10 = 0x100; /** * The plane. */ @@ -28,11 +36,6 @@ public final class RegionFlags { * The base y-coordinate. */ private final int baseY; - - /** - * The clipping flags. - */ - private int[][] clippingFlags; /** * The landscape data. @@ -42,7 +45,7 @@ public final class RegionFlags { /** * If the flags are set for projectile clipping */ - private boolean projectile; + private final boolean projectile; /** * Constructs a new {@code RegionFlags} {@code Object}. @@ -71,7 +74,11 @@ public final class RegionFlags { * @param y The y-coordinate. */ public void flagSolidTile(int x, int y) { - clippingFlags[x][y] |= 0x200000; + flag(x, y, SOLID_TILE); + } + + public void flagEmptyTile(int x, int y) { + flag(x, y, EMPTY_TILE); } /** @@ -80,7 +87,7 @@ public final class RegionFlags { * @param y The y-coordinate. */ public void flagTileObject(int x, int y) { - clippingFlags[x][y] |= 0x40000; + flag(x, y, TILE_OBJECT); } /** @@ -89,12 +96,7 @@ public final class RegionFlags { * @param y The y-coordinate. */ public void unflagTileObject(int x, int y) { - if (clippingFlags == null) { - return; - } - if ((clippingFlags[x][y] & 0x40000) != 0) { - clippingFlags[x][y] &= ~0x40000; - } + unflag(x, y, TILE_OBJECT); } /** @@ -106,9 +108,9 @@ public final class RegionFlags { * @param projectileClipped If the object is solid. */ public void flagSolidObject(int x, int y, int sizeX, int sizeY, boolean projectileClipped) { - int clipdata = 0x100; + int clipdata = OBJ_10; if (projectileClipped) { - clipdata += 0x20000; + clipdata += OBJ_10_PROJECTILE; } for (int i = x; i < x + sizeX; i++) { for (int j = y; j < y + sizeY; j++) { @@ -125,12 +127,11 @@ public final class RegionFlags { */ public void flag(int x, int y, int clipdata) { if (x > -1 && x < 64 && y > -1 && y < 64) { - clippingFlags[x][y] |= clipdata; + addFlag(x, y, clipdata); } else { RegionManager.addClippingFlag(plane, baseX + x, baseY + y, projectile, clipdata); } } - /** * Unflags a solid object (type 10/11). @@ -141,9 +142,9 @@ public final class RegionFlags { * @param projectileClipped If the object is solid. */ public void unflagSolidObject(int x, int y, int sizeX, int sizeY, boolean projectileClipped) { - int clipdata = 0x100; + int clipdata = OBJ_10; if (projectileClipped) { - clipdata += 0x20000; + clipdata += OBJ_10_PROJECTILE; } for (int i = x; i < x + sizeX; i++) { for (int j = y; j < y + sizeY; j++) { @@ -159,18 +160,47 @@ public final class RegionFlags { * @param clipdata The clip data. */ public void unflag(int x, int y, int clipdata) { - if (clippingFlags == null) { - return; - } if (x > -1 && x < 64 && y > -1 && y < 64) { - if ((clippingFlags[x][y] & clipdata) != 0) { - clippingFlags[x][y] &= ~clipdata; - } + removeFlag(x, y, clipdata); } else { RegionManager.removeClippingFlag(plane, baseX + x, baseY + y, projectile, clipdata); } } + private Pair getFlagIndex(int x, int y) { + return new Pair<>(((baseX >> 6) << 8) | (baseY >> 6), (plane * 64 * 64) + (x * 64) + y); + } + + public int getFlag(int x, int y) { + Pair indices = getFlagIndex(x, y); + return RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()]; + } + + public void addFlag(int x, int y, int clipdata) { + int current = getFlag(x, y); + Pair indices = getFlagIndex(x, y); + RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = max(0, current) | clipdata; + } + + public void removeFlag(int x, int y, int clipdata) { + int current = getFlag(x, y); + Pair indices = getFlagIndex(x, y); + if ((current & clipdata) == 0) return; + current = max(0, current) & ~clipdata; + + RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = current; + } + + public void clearFlag(int x, int y) { + Pair indices = getFlagIndex(x, y); + RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = 0; + } + + public void invalidateFlag(int x, int y) { + Pair indices = getFlagIndex(x, y); + RegionManager.getFlags(indices.getFirst(), projectile)[indices.getSecond()] = -1; + } + /** * Flags a door object (type 0-3). * @param x The x-coordinate @@ -465,13 +495,6 @@ public final class RegionFlags { } } - /** - * Unloads the clipping flags. - */ - public void unload() { - clippingFlags = null; - } - /** * Gets the members. * @return The members. @@ -488,22 +511,6 @@ public final class RegionFlags { this.members = members; } - /** - * Gets the clippingFlags. - * @return The clippingFlags. - */ - public int[][] getClippingFlags() { - return clippingFlags; - } - - /** - * Sets the clippingFlags. - * @param clippingFlags The clippingFlags to set. - */ - public void setClippingFlags(int[][] clippingFlags) { - this.clippingFlags = clippingFlags; - } - /** * Gets the plane. * @return The plane. diff --git a/Server/src/main/java/core/game/world/map/path/ClipMaskSupplier.java b/Server/src/main/core/game/world/map/path/ClipMaskSupplier.java similarity index 100% rename from Server/src/main/java/core/game/world/map/path/ClipMaskSupplier.java rename to Server/src/main/core/game/world/map/path/ClipMaskSupplier.java diff --git a/Server/src/main/java/core/game/world/map/path/DumbPathfinder.java b/Server/src/main/core/game/world/map/path/DumbPathfinder.java similarity index 90% rename from Server/src/main/java/core/game/world/map/path/DumbPathfinder.java rename to Server/src/main/core/game/world/map/path/DumbPathfinder.java index 0080f3480..8bcfb32e0 100644 --- a/Server/src/main/java/core/game/world/map/path/DumbPathfinder.java +++ b/Server/src/main/core/game/world/map/path/DumbPathfinder.java @@ -14,7 +14,6 @@ import java.util.List; * @author Emperor */ public final class DumbPathfinder extends Pathfinder { - /** * If a path can be found. */ @@ -84,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)); } @@ -103,7 +100,7 @@ public final class DumbPathfinder extends Pathfinder { found = true; switch (dir) { case NORTH: - if ((clipMaskSupplier.getClippingFlag(z, x, y + 1) & 0x12c0120) != 0) { + if ((clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0) { found = false; break; } @@ -111,7 +108,7 @@ public final class DumbPathfinder extends Pathfinder { y++; break; case NORTH_EAST: - if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & 0x12c0180) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & 0x12c0120) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y + 1) & 0x12c01e0) != 0) { + if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y + 1) & PREVENT_NORTHEAST) != 0) { found = false; break; } @@ -120,7 +117,7 @@ public final class DumbPathfinder extends Pathfinder { y++; break; case EAST: - if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & 0x12c0180) != 0) { + if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0) { found = false; break; } @@ -128,7 +125,7 @@ public final class DumbPathfinder extends Pathfinder { x++; break; case SOUTH_EAST: - if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & 0x12c0180) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c0102) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y - 1) & 0x12c0183) != 0) { + if ((clipMaskSupplier.getClippingFlag(z, x + 1, y) & PREVENT_EAST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x + 1, y - 1) & PREVENT_SOUTHEAST) != 0) { found = false; break; } @@ -137,7 +134,7 @@ public final class DumbPathfinder extends Pathfinder { y--; break; case SOUTH: - if ((clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c0102) != 0) { + if ((clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0) { found = false; break; } @@ -145,7 +142,7 @@ public final class DumbPathfinder extends Pathfinder { y--; break; case SOUTH_WEST: - if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c0108) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & 0x12c0102) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y - 1) & 0x12c010e) != 0) { + if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y - 1) & PREVENT_SOUTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y - 1) & PREVENT_SOUTHWEST) != 0) { found = false; break; } @@ -154,7 +151,7 @@ public final class DumbPathfinder extends Pathfinder { y--; break; case WEST: - if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c0108) != 0) { + if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0) { found = false; break; } @@ -162,7 +159,7 @@ public final class DumbPathfinder extends Pathfinder { x--; break; case NORTH_WEST: - if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & 0x12c0108) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & 0x12c0120) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + 1) & 0x12c0138) != 0) { + if ((clipMaskSupplier.getClippingFlag(z, x - 1, y) & PREVENT_WEST) != 0 || (clipMaskSupplier.getClippingFlag(z, x, y + 1) & PREVENT_NORTH) != 0 || (clipMaskSupplier.getClippingFlag(z, x - 1, y + 1) & PREVENT_NORTHWEST) != 0) { found = false; break; } @@ -427,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/java/core/game/world/map/path/Path.java b/Server/src/main/core/game/world/map/path/Path.java similarity index 100% rename from Server/src/main/java/core/game/world/map/path/Path.java rename to Server/src/main/core/game/world/map/path/Path.java diff --git a/Server/src/main/java/core/game/world/map/path/Pathfinder.java b/Server/src/main/core/game/world/map/path/Pathfinder.java similarity index 94% rename from Server/src/main/java/core/game/world/map/path/Pathfinder.java rename to Server/src/main/core/game/world/map/path/Pathfinder.java index 7d0a81d23..9fce3a782 100644 --- a/Server/src/main/java/core/game/world/map/path/Pathfinder.java +++ b/Server/src/main/core/game/world/map/path/Pathfinder.java @@ -2,13 +2,23 @@ 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; -import rs09.game.world.map.path.SmartPathfinder; public abstract class Pathfinder { + public static final int PREVENT_NORTH = 0x12c0120; + public static final int PREVENT_EAST = 0x12c0180; + public static final int PREVENT_NORTHEAST = 0x12c01e0; + public static final int PREVENT_SOUTH = 0x12c0102; + public static final int PREVENT_SOUTHEAST = 0x12c0183; + public static final int PREVENT_WEST = 0x12c0108; + public static final int PREVENT_SOUTHWEST = 0x12c010e; + public static final int PREVENT_NORTHWEST = 0x12c0138; + /** * The smart path finder. @@ -65,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. @@ -101,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){ @@ -121,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. @@ -681,4 +715,4 @@ public abstract class Pathfinder { } return false; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/game/world/map/path/ProjectilePathfinder.java b/Server/src/main/core/game/world/map/path/ProjectilePathfinder.java similarity index 100% rename from Server/src/main/java/core/game/world/map/path/ProjectilePathfinder.java rename to Server/src/main/core/game/world/map/path/ProjectilePathfinder.java diff --git a/Server/src/main/core/game/world/map/path/SmartPathfinder.kt b/Server/src/main/core/game/world/map/path/SmartPathfinder.kt new file mode 100644 index 000000000..2dcae4a9c --- /dev/null +++ b/Server/src/main/core/game/world/map/path/SmartPathfinder.kt @@ -0,0 +1,591 @@ +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 +/** + * Constructs a new `SmartPathfinder` `Object`. + */ +internal constructor() : Pathfinder() { + /** + * The x-queue. + */ + private var queueX: IntArray = intArrayOf(0) + + /** + * The y-queue. + */ + private var queueY: IntArray = intArrayOf(0) + + /** + * The "via" array. + */ + private var via: Array = Array(104) { IntArray(104) } + + /** + * The cost array. + */ + private var cost: Array = Array(104) { IntArray(104) } + + /** + * The current writing position. + */ + private var writePathPosition = 0 + + /** + * The current x-coordinate. + */ + private var curX = 0 + + /** + * The current y-coordinate. + */ + private var curY = 0 + + /** + * The destination x-coordinate. + */ + private var dstX = 0 + + /** + * The destination y-coordinate. + */ + private var dstY = 0 + + /** + * If a path was found. + */ + private var foundPath = false + + /** + * Resets the pathfinder. + */ + fun reset() { + queueX = IntArray(4096) + queueY = IntArray(4096) + via = Array(104) { IntArray(104) } + cost = Array(104) { IntArray(104) } + writePathPosition = 0 + } + + /** + * Checks a tile. + * @param x The x-coordinate. + * @param y The y-coordinate. + * @param dir The direction. + * @param currentCost The current cost. + */ + 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, dest: Location?, sizeX: Int, sizeY: Int, rotation: Int, type: Int, walkingFlag: Int, near: Boolean, clipMaskSupplier: ClipMaskSupplier?): Path { + reset() + 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) { + for (y in 0..103) { + via[x][y] = 0 + cost[x][y] = 99999999 + } + } + val z = start!!.z + val location = Location.create(start.regionX - 6 shl 3, start.regionY - 6 shl 3, z) + curX = start.sceneX + curY = start.sceneY + dstX = end!!.getSceneX(start) + dstY = end.getSceneY(start) + var attempts: Int + var readPosition: Int + check(curX, curY, 99, 0) + try { + if (moverSize < 2) { + 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 + var thisCost = 100 + val depth = 10 + for (x in dstX - depth..dstX + depth) { + for (y in dstY - depth..dstY + depth) { + if (x >= 0 && y >= 0 && x < 104 && y < 104 && cost[x][y] < 100) { + var diffX = 0 + if (x < dstX) { + diffX = dstX - x + } else if (x > dstX + sizeX - 1) { + diffX = x - (dstX + sizeX - 1) + } + var diffY = 0 + if (y < dstY) { + diffY = dstY - y + } else if (y > dstY + sizeY - 1) { + diffY = y - (dstY + sizeY - 1) + } + val totalCost = diffX * diffX + diffY * diffY + if (totalCost < fullCost || totalCost == fullCost && cost[x][y] < thisCost) { + fullCost = totalCost + thisCost = cost[x][y] + curX = x + curY = y + } + } + } + } + if (fullCost == 1000) { + return path + } + path.isMoveNear = true + } + } + readPosition = 0 + queueX[readPosition] = curX + queueY[readPosition++] = curY + var previousDirection: Int + attempts = 0 + var directionFlag = via[curX][curY].also { previousDirection = it } + while (curX != start.sceneX || curY != start.sceneY) { + if (++attempts > queueX.size) { + return path + } + previousDirection = directionFlag + queueX[readPosition] = curX + queueY[readPosition++] = curY + if (directionFlag and WEST_FLAG != 0) { + curX++ + } else if (directionFlag and EAST_FLAG != 0) { + curX-- + } + if (directionFlag and SOUTH_FLAG != 0) { + curY++ + } 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] + path.points.add(Point(absX, absY)) + for (i in 1 until size) { + readPosition-- + absX = location.x + queueX[readPosition] + absY = location.y + queueY[readPosition] + 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. + * @param sizeX The x-size of the destination. + * @param sizeY The y-size of the destination. + * @param type The object type. + * @param rotation The object rotation. + * @param walkingFlag The walking flag. + * @param location The viewport location. + */ + private fun checkSingleTraversal(end: Location, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) { + var readPosition = 0 + val z = location.z + while (writePathPosition != readPosition) { + curX = queueX[readPosition] + curY = queueY[readPosition] + readPosition = readPosition + 1 and 0xfff + if (curX == dstX && curY == dstY) { + foundPath = true + break + } + try { + val absX = location.x + curX + val absY = location.y + curY + 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 thisCost = cost[curX][curY] + 1 + if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) { + check(curX, curY - 1, SOUTH_FLAG, thisCost) + } + if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0) { + check(curX - 1, curY, WEST_FLAG, thisCost) + } + if (curY < 103 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) { + check(curX, curY + 1, NORTH_FLAG, thisCost) + } + if (curX < 103 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0) { + check(curX + 1, curY, EAST_FLAG, thisCost) + } + if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) { + check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost) + } + if (curX > 0 && curY < 103 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) { + check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost) + } + if (curX < 103 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) { + check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost) + } + if (curX < 103 && curY < 103 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 1) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) { + check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost) + } + } catch (e: Exception) { + // e.printStackTrace()println("curX " + curX + " curY" + curY + " via " + via[curX + 1] + via[curY + 1]) + } + } + } + + /** + * Checks possible traversal for a size 2 entity. + * @param end The destination location. + * @param sizeX The x-size of the destination. + * @param sizeY The y-size of the destination. + * @param type The object type. + * @param rotation The object rotation. + * @param walkingFlag The walking flag. + * @param location The viewport location. + */ + private fun checkDoubleTraversal(end: Location, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) { + var readPosition = 0 + val z = location.z + while (writePathPosition != readPosition) { + curX = queueX[readPosition] + curY = queueY[readPosition] + readPosition = readPosition + 1 and 0xfff + if (curX == dstX && curY == dstY) { + foundPath = true + break + } + val absX = location.x + curX + val absY = location.y + curY + if (type != 0) { + if ((type < 5 || type == 10) && canDoorInteract(absX, absY, 2, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { + foundPath = true + break + } + if (type < 10 && canDecorationInteract(absX, absY, 2, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { + foundPath = true + break + } + } + if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, 2, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) { + foundPath = true + break + } + val thisCost = cost[curX][curY] + 1 + if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c0183 == 0) { + check(curX, curY - 1, SOUTH_FLAG, thisCost) + } + if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c0138 == 0) { + check(curX - 1, curY, WEST_FLAG, thisCost) + } + if (curY < 102 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 2) and 0x12c01e0 == 0) { + check(curX, curY + 1, NORTH_FLAG, thisCost) + } + if (curX < 102 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 1) and 0x12c01e0 == 0) { + check(curX + 1, curY, EAST_FLAG, thisCost) + } + if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0183 == 0) { + check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost) + } + if (curX > 0 && curY < 102 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 2) and 0x12c01e0 == 0) { + check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost) + } + if (curX < 102 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY - 1) and 0x12c0183 == 0) { + check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost) + } + if (curX < 102 && curY < 102 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 2) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 1) and 0x12c0183 == 0) { + check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost) + } + } + } + + /** + * Checks possible traversal for any sized entity. + * @param end The destination location. + * @param size The mover size. + * @param sizeX The x-size of the destination. + * @param sizeY The y-size of the destination. + * @param type The object type. + * @param rotation The object rotation. + * @param walkingFlag The walking flag. + * @param location The viewport location. + */ + private fun checkVariableTraversal(end: Location, size: Int, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) { + var readPosition = 0 + val z = location.z + main@ while (writePathPosition != readPosition) { + curX = queueX[readPosition] + curY = queueY[readPosition] + readPosition = readPosition + 1 and 0xfff + if (curX == dstX && curY == dstY) { + foundPath = true + break + } + val absX = location.x + curX + val absY = location.y + curY + if (type != 0) { + if ((type < 5 || type == 10) && canDoorInteract(absX, absY, size, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { + foundPath = true + break + } + if (type < 10 && canDecorationInteract(absX, absY, size, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { + foundPath = true + break + } + } + if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, size, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) { + foundPath = true + break + } + val thisCost = cost[curX][curY] + 1 + south@ do { + if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 1), absY - 1) and 0x12c0183 == 0) { + for (i in 1 until size - 1) { + if (clipMaskSupplier.getClippingFlag(z, absX + i, absY - 1) and 0x12c018f != 0) { + break@south + } + } + check(curX, curY - 1, SOUTH_FLAG, thisCost) + } + } while (false) + west@ do { + if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (size - 1)) and 0x12c0138 == 0) { + for (i in 1 until size - 1) { + if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + i) and 0x12c013e != 0) { + break@west + } + } + check(curX - 1, curY, WEST_FLAG, thisCost) + } + } while (false) + north@ do { + if (curY < 102 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 1), absY + size) and 0x12c01e0 == 0) { + for (i in 1 until size - 1) { + if (clipMaskSupplier.getClippingFlag(z, absX + i, absY + size) and 0x12c01f8 != 0) { + break@north + } + } + check(curX, curY + 1, NORTH_FLAG, thisCost) + } + } while (false) + east@ do { + if (curX < 102 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + (size - 1)) and 0x12c01e0 == 0) { + for (i in 1 until size - 1) { + if (clipMaskSupplier.getClippingFlag(z, absX + size, absY + i) and 0x12c01e3 != 0) { + break@east + } + } + check(curX + 1, curY, EAST_FLAG, thisCost) + } + } while (false) + southWest@ do { + if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (size - 2)) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 2), absY - 1) and 0x12c0183 == 0) { + for (i in 1 until size - 1) { + if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (i - 1)) and 0x12c013e != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i - 1), absY - 1) and 0x12c018f != 0) { + break@southWest + } + } + check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost) + } + } while (false) + northWest@ do { + if (curX > 0 && curY < 102 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + size) and 0x12c01e0 == 0) { + for (i in 1 until size - 1) { + if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (i + 1)) and 0x12c013e != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i - 1), absY + size) and 0x12c01f8 != 0) { + break@northWest + } + } + check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost) + } + } while (false) + southEast@ do { + if (curX < 102 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY - 1) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + (size - 2)) and 0x12c01e0 == 0) { + for (i in 1 until size - 1) { + if (clipMaskSupplier.getClippingFlag(z, absX + size, absY + (i - 1)) and 0x12c01e3 != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i + 1), absY - 1) and 0x12c018f != 0) { + break@southEast + } + } + check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost) + } + } while (false) + if (curX < 102 && curY < 102 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + size) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + 1) and 0x12c0183 == 0) { + for (i in 1 until size - 1) { + if (clipMaskSupplier.getClippingFlag(z, absX + (i + 1), absY + size) and 0x12c01f8 != 0 || clipMaskSupplier.getClippingFlag(z, absX + size, absY + (i + 1)) and 0x12c01e3 != 0) { + continue@main + } + } + check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost) + } + } + } +} diff --git a/Server/src/main/java/core/game/world/map/zone/MapZone.java b/Server/src/main/core/game/world/map/zone/MapZone.java similarity index 81% rename from Server/src/main/java/core/game/world/map/zone/MapZone.java rename to Server/src/main/core/game/world/map/zone/MapZone.java index 48d93b0fb..7adb16dfc 100644 --- a/Server/src/main/java/core/game/world/map/zone/MapZone.java +++ b/Server/src/main/core/game/world/map/zone/MapZone.java @@ -1,6 +1,6 @@ package core.game.world.map.zone; -import core.game.node.entity.skill.summoning.familiar.Familiar; +import content.global.skill.summoning.familiar.Familiar; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; @@ -14,6 +14,7 @@ import core.game.world.map.Region; import core.game.world.map.RegionManager; import java.util.Iterator; +import java.util.Objects; /** * Represents a map zone. @@ -129,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; @@ -163,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; } @@ -299,6 +322,15 @@ public abstract class MapZone implements Zone { } } + public void unregister(ZoneBorders borders) { + for (Integer id : borders.getRegionIds()) { + Region r = RegionManager.forId(id); + if (r != null) { + r.remove(new RegionZone(this, borders)); + } + } + } + /** * Registers this zone in the region for the given id. * @param regionId The region id. @@ -378,7 +410,7 @@ public abstract class MapZone implements Zone { * @return The uid. */ public int getUid() { - return uid; + return getName().hashCode(); } /** @@ -455,4 +487,17 @@ public abstract class MapZone implements Zone { this.zoneType = type; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MapZone mapZone = (MapZone) o; + return getUid() == mapZone.getUid(); + } + + @Override + public int hashCode() { + return Objects.hash(uid, name, overlappable, fireRandomEvents, restriction, zoneType); + } } \ No newline at end of file diff --git a/Server/src/main/core/game/world/map/zone/RegionZone.java b/Server/src/main/core/game/world/map/zone/RegionZone.java new file mode 100644 index 000000000..4c4c112e5 --- /dev/null +++ b/Server/src/main/core/game/world/map/zone/RegionZone.java @@ -0,0 +1,59 @@ +package core.game.world.map.zone; + +import java.util.Objects; + +/** + * Represents a zone inside a single region of the world map. + * @author Emperor + */ +public final class RegionZone { + + /** + * The map zone. + */ + private final MapZone zone; + + /** + * The borders. + */ + private final ZoneBorders borders; + + /** + * Constructs a new {@code RegionZone} {@code Object}. + * @param zone The map zone. + * @param borders The borders. + */ + public RegionZone(MapZone zone, ZoneBorders borders) { + this.zone = zone; + this.borders = borders; + } + + /** + * Gets the borders. + * @return The borders. + */ + public ZoneBorders getBorders() { + return borders; + } + + /** + * Gets the zone. + * @return The zone. + */ + public MapZone getZone() { + return zone; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RegionZone that = (RegionZone) o; + return Objects.equals(zone, that.zone) && Objects.equals(borders, that.borders); + } + + @Override + public int hashCode() { + return Objects.hash(zone, borders); + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/map/zone/Zone.java b/Server/src/main/core/game/world/map/zone/Zone.java similarity index 100% rename from Server/src/main/java/core/game/world/map/zone/Zone.java rename to Server/src/main/core/game/world/map/zone/Zone.java diff --git a/Server/src/main/java/core/game/world/map/zone/ZoneBorders.java b/Server/src/main/core/game/world/map/zone/ZoneBorders.java similarity index 86% rename from Server/src/main/java/core/game/world/map/zone/ZoneBorders.java rename to Server/src/main/core/game/world/map/zone/ZoneBorders.java index cb1bd8917..b43fa5d5f 100644 --- a/Server/src/main/java/core/game/world/map/zone/ZoneBorders.java +++ b/Server/src/main/core/game/world/map/zone/ZoneBorders.java @@ -2,10 +2,12 @@ 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; import java.util.List; +import java.util.Objects; import java.util.Random; /** @@ -37,7 +39,7 @@ public final class ZoneBorders { /** * The plane required to be on. */ - private int plane; + private int plane = 0; /** * The list of exceptions. @@ -96,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. @@ -230,7 +236,17 @@ public final class ZoneBorders { public Location getRandomLoc() { int x = northEastX - southWestX == 0 ? southWestX : new Random().nextInt(northEastX - southWestX + 1) + southWestX; int y = northEastY - southWestY == 0 ? southWestY : new Random().nextInt(northEastY - southWestY + 1) + southWestY; - return new Location(x, y); + 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; } /** @@ -268,4 +284,17 @@ public final class ZoneBorders { public boolean insideRegion(Node n) { return insideBorder(n.getLocation().getRegionX(), n.getLocation().getRegionY()); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ZoneBorders that = (ZoneBorders) o; + return southWestX == that.southWestX && southWestY == that.southWestY && northEastX == that.northEastX && northEastY == that.northEastY && plane == that.plane && zeroPlaneCheck == that.zeroPlaneCheck && Objects.equals(exceptions, that.exceptions); + } + + @Override + public int hashCode() { + return Objects.hash(southWestX, southWestY, northEastX, northEastY, plane, exceptions, zeroPlaneCheck); + } } \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/map/zone/ZoneBuilder.java b/Server/src/main/core/game/world/map/zone/ZoneBuilder.java similarity index 80% rename from Server/src/main/java/core/game/world/map/zone/ZoneBuilder.java rename to Server/src/main/core/game/world/map/zone/ZoneBuilder.java index b5e69f641..0fd373e54 100644 --- a/Server/src/main/java/core/game/world/map/zone/ZoneBuilder.java +++ b/Server/src/main/core/game/world/map/zone/ZoneBuilder.java @@ -1,6 +1,6 @@ package core.game.world.map.zone; -import core.game.world.callback.CallBack; +import core.api.StartupListener; import core.game.world.map.zone.impl.*; @@ -8,17 +8,16 @@ import core.game.world.map.zone.impl.*; * Loads all the default zones. * @author Emperor */ -public class ZoneBuilder implements CallBack { +public class ZoneBuilder implements StartupListener { @Override - public boolean call() { + public void startup() { configure(WildernessZone.getInstance()); configure(MultiwayCombatZone.getInstance()); configure(new ModeratorZone()); configure(new DarkZone()); configure(new KaramjaZone()); configure(new BankZone()); - return true; } /** diff --git a/Server/src/main/java/core/game/world/map/zone/ZoneMonitor.java b/Server/src/main/core/game/world/map/zone/ZoneMonitor.java similarity index 76% rename from Server/src/main/java/core/game/world/map/zone/ZoneMonitor.java rename to Server/src/main/core/game/world/map/zone/ZoneMonitor.java index e9fdd394e..65b4a0322 100644 --- a/Server/src/main/java/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. */ @@ -112,24 +147,26 @@ public final class ZoneMonitor { * @param style The combat style used. * @return {@code True} if so. */ - public boolean continueAttack(Node target, CombatStyle style) { + public boolean continueAttack(Node target, CombatStyle style, boolean message) { if (target instanceof Entity) { - if (!entity.continueAttack((Entity) target, style)) { + if (!entity.continueAttack((Entity) target, style, message)) { return false; } } for (RegionZone z : zones) { - if (!z.getZone().continueAttack(entity, target, style, true)) { + if (!z.getZone().continueAttack(entity, target, style, message)) { return false; } } if (entity instanceof Player && target instanceof Player) { if (!((Player) entity).getSkullManager().isWilderness() || !((Player) target).getSkullManager().isWilderness()) { - ((Player) entity).getPacketDispatch().sendMessage("You can only attack other players in the wilderness."); + if(message) { + ((Player) entity).getPacketDispatch().sendMessage("You can only attack other players in the wilderness."); + } return false; } } - if (target instanceof Entity && !MapZone.checkMulti(entity, (Entity) target, true)) { + if (target instanceof Entity && !MapZone.checkMulti(entity, (Entity) target, message)) { return false; } return true; @@ -201,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."); } @@ -215,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. @@ -334,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 new file mode 100644 index 000000000..64dd971db --- /dev/null +++ b/Server/src/main/core/game/world/map/zone/ZoneRestriction.java @@ -0,0 +1,59 @@ +package core.game.world.map.zone; + +/** + * The zone restrictions. + * @author Emperor + */ +public enum ZoneRestriction { + + /** + * No followers allowed in this zone. + */ + FOLLOWERS, + + /** + * No random events allowed. + */ + RANDOM_EVENTS, + + /** + * No fires allowed. + */ + FIRES, + + /** + * Members only. + */ + MEMBERS, + + /** + * No cannons allowed. + */ + CANNON, + /** + * 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, + ; + + /** + * Gets the restriction flag. + * @return The flag. + */ + public int getFlag() { + return 1 << ordinal(); + } +} diff --git a/Server/src/main/java/core/game/world/map/zone/ZoneType.java b/Server/src/main/core/game/world/map/zone/ZoneType.java similarity index 100% rename from Server/src/main/java/core/game/world/map/zone/ZoneType.java rename to Server/src/main/core/game/world/map/zone/ZoneType.java diff --git a/Server/src/main/java/core/game/world/map/zone/impl/BankZone.java b/Server/src/main/core/game/world/map/zone/impl/BankZone.java similarity index 100% rename from Server/src/main/java/core/game/world/map/zone/impl/BankZone.java rename to Server/src/main/core/game/world/map/zone/impl/BankZone.java diff --git a/Server/src/main/java/core/game/world/map/zone/impl/DarkZone.java b/Server/src/main/core/game/world/map/zone/impl/DarkZone.java similarity index 81% rename from Server/src/main/java/core/game/world/map/zone/impl/DarkZone.java rename to Server/src/main/core/game/world/map/zone/impl/DarkZone.java index 8b525d2e6..5d955031a 100644 --- a/Server/src/main/java/core/game/world/map/zone/impl/DarkZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/DarkZone.java @@ -1,8 +1,12 @@ package core.game.world.map.zone.impl; +import content.global.skill.skillcapeperks.SkillcapePerks; +import core.game.event.EventHook; +import core.game.event.UseWithEvent; import core.game.component.Component; -import core.game.content.global.LightSource; +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; @@ -13,14 +17,18 @@ import core.game.system.task.Pulse; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.RegionZone; import core.game.world.map.zone.ZoneBorders; -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; -import rs09.game.world.GameWorld; +import kotlin.Unit; +import org.jetbrains.annotations.NotNull; +import core.api.Event; +import core.game.world.GameWorld; + +import static core.api.ContentAPIKt.*; /** * Handles a dark area. * @author Emperor */ -public final class DarkZone extends MapZone { +public final class DarkZone extends MapZone implements EventHook{ /** * The darkness overlay. @@ -77,6 +85,7 @@ public final class DarkZone extends MapZone { 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)); @@ -128,6 +137,7 @@ public final class DarkZone extends MapZone { player.getInterfaceManager().openOverlay(new Component(source.getInterfaceId())); } } + e.hook(Event.getUsedWith(), this); return true; } @@ -136,6 +146,7 @@ public final class DarkZone extends MapZone { if (e instanceof Player) { ((Player) e).getInterfaceManager().closeOverlay(); } + e.unhook(this); return true; } @@ -146,9 +157,11 @@ public final class DarkZone extends MapZone { 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) { @@ -187,4 +200,14 @@ public final class DarkZone extends MapZone { } return false; } -} \ No newline at end of file + + @Override + public void process(@NotNull Entity entity, @NotNull UseWithEvent event) { + boolean isTinderbox = getItemName(event.getUsed()).equals("Tinderbox") || getItemName(event.getWith()).equals("Tinderbox"); + + if (isTinderbox && entity instanceof Player) runTask(entity, 2, 1, () -> { + checkDarkArea(entity.asPlayer()); + return Unit.INSTANCE; + }); + } +} diff --git a/Server/src/main/java/core/game/world/map/zone/impl/KaramjaZone.java b/Server/src/main/core/game/world/map/zone/impl/KaramjaZone.java similarity index 100% rename from Server/src/main/java/core/game/world/map/zone/impl/KaramjaZone.java rename to Server/src/main/core/game/world/map/zone/impl/KaramjaZone.java diff --git a/Server/src/main/java/core/game/world/map/zone/impl/ModeratorZone.java b/Server/src/main/core/game/world/map/zone/impl/ModeratorZone.java similarity index 99% rename from Server/src/main/java/core/game/world/map/zone/impl/ModeratorZone.java rename to Server/src/main/core/game/world/map/zone/impl/ModeratorZone.java index 01af31d33..1c8b113dd 100644 --- a/Server/src/main/java/core/game/world/map/zone/impl/ModeratorZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/ModeratorZone.java @@ -1,6 +1,6 @@ package core.game.world.map.zone.impl; -import rs09.ServerConstants; +import core.ServerConstants; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; diff --git a/Server/src/main/java/core/game/world/map/zone/impl/MultiwayCombatZone.java b/Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java similarity index 97% rename from Server/src/main/java/core/game/world/map/zone/impl/MultiwayCombatZone.java rename to Server/src/main/core/game/world/map/zone/impl/MultiwayCombatZone.java index 5ddbd964c..77477b3b1 100644 --- a/Server/src/main/java/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 new file mode 100644 index 000000000..cce6ab1d4 --- /dev/null +++ b/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java @@ -0,0 +1,344 @@ +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 core.game.node.Node; +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.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 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.game.world.repository.Repository; +import core.tools.RandomFunction; +import org.rs09.consts.Items; +import org.rs09.consts.NPCs; + + +/** + * Handles the wilderness zone. + * @author Emperor + */ +public final class WildernessZone extends MapZone { + /** + * The PvP gear items + */ + private static int[] PVP_GEAR = { 13887, 13893, 13899, 13905, 13870, 13873, 13876, 13879, 13883, 13884, 13890, 13896, 13902, 13858, 13861, 13864, 13867}; + + /** + * The wilderness zone. + */ + 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. + */ + private ZoneBorders[] borders; + + /** + * Constructs a new {@code WildernessZone} {@code Object}. + */ + public WildernessZone(ZoneBorders... borders) { + super("Wilderness", true, ZoneRestriction.RANDOM_EVENTS); + this.borders = borders; + } + + @Override + public void configure() { + for (ZoneBorders border : borders) { + register(border); + } + } + + /** + * calculate drop rate for rev items based on combat level + * @author ceik + * @param combatLevel + * @return + */ + public int getNewDropRate(int combatLevel){ + double x = combatLevel; + double A = 44044.5491; + double B = -7360.19548; + return (int) (A + (B * Math.log(x))); + } + + /** + * Handles rev drops + * @author ceik + * @param e The entity dying. + * @param killer The killer. + * @return true + */ + @Override + public boolean death(Entity e, Entity killer) { + if (e instanceof NPC) + rollWildernessExclusiveLoot(e, killer); + return false; //DONT override default death handling. + } + + private void rollWildernessExclusiveLoot(Entity e, Entity killer) { + if (!(killer instanceof Player)) return; + if (!(e instanceof NPC)) return; + if (!(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 || e.asNpc().getName().contains("Revenant"))) return; + + int pvpGearRate = getNewDropRate(e.asNpc().getDefinition().getCombatLevel()); + boolean higherRate = ((Player) killer).getSkullManager().isDeepWilderness() && ((Player) killer).getAttribute("deepwild-value-risk", 0L) > SkullManager.DEEP_WILD_DROP_RISK_THRESHOLD; + if (higherRate) { + pvpGearRate /= 2; + } + int cEleGloveRate = 50; + int normalGloveRate = higherRate ? 100 : (int) ((1.0 / (1.0 - Math.pow(1.0 - (1.0 / (double) pvpGearRate), 16.0))) * 5.0 / 6.0); + + if (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 + "!"); + } + } + } + + /** + * Fixes attack options for the revs + * @param e The entity. + * @param target The target to interact with. + * @param option The option. + * @return true + */ + @Override + public boolean interact(Entity e, Node target, Option option) { + if(target instanceof NPC){ + if(target.asNpc().getName().contains("Revenant")){ + e.asPlayer().getProperties().getCombatPulse().attack(target); + return true; + } + } + return super.interact(e, target, option); + } + + @Override + public boolean enter(Entity e) { + if (e instanceof Player) { + Player p = (Player) e; + if(!p.isArtificial()) { + show(p); + } else { + p.getSkullManager().setWilderness(true); + p.getSkullManager().setLevel(getWilderness(p)); + } + if (p.getFamiliarManager().hasFamiliar() && !p.getFamiliarManager().hasPet()) { + Familiar familiar = p.getFamiliarManager().getFamiliar(); + familiar.transform(); + } + p.getAppearance().sync(); + } else if (e instanceof NPC) { + NPC n = (NPC) e; + if (n.getDefinition().hasAttackOption() && n.isAggressive()) { + //n.setAggressive(true); + n.setAggressiveHandler(new AggressiveHandler(n, AggressiveBehavior.WILDERNESS)); + } + } + return true; + } + + @Override + public boolean leave(Entity e, boolean logout) { + if (!logout && e instanceof Player) { + Player p = (Player) e; + leave(p); + if (p.getFamiliarManager().hasFamiliar() && !p.getFamiliarManager().hasPet()) { + Familiar familiar = p.getFamiliarManager().getFamiliar(); + if (familiar.isCombatFamiliar()) { + familiar.reTransform(); + } + } + p.getAppearance().sync(); + } + return true; + } + + /** + * Method used to remove traces of being in the zone. + * @param p the player. + */ + public final void leave(final Player p) { + Component overlay = new Component(381); + if (overlay.getId() == 381) { + p.getInterfaceManager().close(overlay); + } + p.getInteraction().remove(Option._P_ATTACK); + p.getSkullManager().setWilderness(false); + p.getSkullManager().setLevel(0); + } + + /** + * Method used to show being the wilderness. + * @param p the player. + */ + public static final void show(final Player p) { + if (p.getSkullManager().isWildernessDisabled()) { + return; + } + 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()) { + p.getInteraction().set(Option._P_ATTACK); + } + p.getSkullManager().setWilderness(true); + } + + @Override + public boolean teleport(Entity e, int type, Node node) { + if (e instanceof Player) { + Player p = (Player) e; + if (p.getDetails().getRights() == Rights.ADMINISTRATOR) { + return true; + } + if (!checkTeleport(p, (node != null && node instanceof Item && (((Item) node).getName().contains("glory") || ((Item) node).getName().contains("slaying")) ? 30 : 20))) { + return false; + } + } + return true; + } + + /** + * Method used to check if a player can teleport past a level. + * @param p the player. + * @param level the level. + * @return {@code True} if they can teleport. + */ + public static boolean checkTeleport(Player p, int level) { + if (p.getSkullManager().getLevel() > level && !p.getSkullManager().isWildernessDisabled()) { + message(p, "You can't teleport this deep in the wilderness!"); + return false; + } + return true; + } + + @Override + public boolean continueAttack(Entity e, Node target, CombatStyle style, boolean message) { + if (e instanceof Player && target instanceof Player) { + Player p = (Player) e; + Player t = (Player) target; + int level = p.getSkullManager().getLevel(); + if (t.getSkullManager().getLevel() < level) { + level = t.getSkullManager().getLevel(); + } + int combat = p.getProperties().getCurrentCombatLevel(); + int targetCombat = t.getProperties().getCurrentCombatLevel(); + if (combat - level > targetCombat || combat + level < targetCombat) { + if (message) { + p.getPacketDispatch().sendMessage("The level difference between you and your opponent is too great."); + } + 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; + } + + @Override + public void locationUpdate(Entity e, Location last) { + if (e instanceof Player && !e.asPlayer().isArtificial()) { + Player p = (Player) e; + p.getSkullManager().setLevel(getWilderness(p)); + } + } + + /** + * Checks if the entity is inside the wilderness. + * @param e The entity. + * @return {@code True} if so. + */ + public static boolean isInZone(Entity e) { + Location l = e.getLocation(); + for (ZoneBorders z : INSTANCE.borders) { + if (z.insideBorder(e)) + return true; + } + 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; + } + } + + /** + * Gets the zone borders. + * @return The borders. + */ + public ZoneBorders[] getBorders() { + return borders; + } + + /** + * Gets the instance. + * @return The instance. + */ + public static WildernessZone getInstance() { + return INSTANCE; + } + +} diff --git a/Server/src/main/java/core/game/world/objectparser/ObjectParser.java b/Server/src/main/core/game/world/objectparser/ObjectParser.java similarity index 83% rename from Server/src/main/java/core/game/world/objectparser/ObjectParser.java rename to Server/src/main/core/game/world/objectparser/ObjectParser.java index bc143b336..39b445e31 100644 --- a/Server/src/main/java/core/game/world/objectparser/ObjectParser.java +++ b/Server/src/main/core/game/world/objectparser/ObjectParser.java @@ -1,31 +1,25 @@ package core.game.world.objectparser; -import rs09.ServerConstants; +import core.api.StartupListener; +import core.ServerConstants; import core.game.node.scenery.Scenery; -import rs09.game.world.GameWorld; import core.game.world.map.build.LandscapeParser; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.PluginManifest; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import core.plugin.CorePluginTypes.StartupPlugin; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; -@Initializable -@PluginManifest(name="ObjectParser") -public class ObjectParser extends StartupPlugin { +public class ObjectParser implements StartupListener { public void parseObjects(){ 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; } @@ -36,7 +30,7 @@ public class ObjectParser extends StartupPlugin { 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); @@ -101,18 +95,7 @@ public class ObjectParser extends StartupPlugin { } @Override - public void run() { + public void startup() { parseObjects(); } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - GameWorld.getSTARTUP_PLUGINS().add(this); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } } diff --git a/Server/src/main/core/game/world/repository/DisconnectionQueue.kt b/Server/src/main/core/game/world/repository/DisconnectionQueue.kt new file mode 100644 index 000000000..dd0c7ee98 --- /dev/null +++ b/Server/src/main/core/game/world/repository/DisconnectionQueue.kt @@ -0,0 +1,144 @@ +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.game.world.GameWorld +import core.tools.Log +import core.tools.secondsToTicks + +/** + * Handles disconnecting players queuing. + * @author Emperor + */ +class DisconnectionQueue { + /**A + * The pending disconnections queue. + */ + private val queue = HashMap() + private val queueTimers = HashMap() + + /** + * Updates all entries. + */ + fun update() { + if (queue.isEmpty() || GameWorld.ticks % 3 != 0) { + return + } + //make a copy of current entries as to avoid concurrency exceptions + val entries = ArrayList(queue.entries) + + //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!") + } + } + } + } + } + + + fun isEmpty(): Boolean{ + return queue.isEmpty() + } + + /** + * Finishes a disconnection. + * @param entry The entry. + * @param force If finalization should be forced. + */ + private fun finish(entry: DisconnectionEntry?, force: Boolean): Boolean { + val player = entry!!.player + if (!force && !player.allowRemoval()) { + return false + } + player.packetDispatch.sendLogout() + player.finishClear() + Repository.removePlayer(player) + try { + if(player.communication.clan != null) + player.communication.clan.leave(player, false) + } catch (e: Exception) { + e.printStackTrace() + } + if (player.isArtificial) { + return true + } + if (!force) { + TaskExecutor.executeSQL { + 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 + } + + /** + * Gets a queued player. + * @param name The player name. + * @return The player instance. + */ + operator fun get(name: String?): Player? { + val entry = queue[name] + return entry?.player + } + + /** + * Clears the queue. + */ + fun clear() { + for (entry in queue.values.toTypedArray()) { + finish(entry, true) + } + queue.clear() + } + + @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.") + } + + operator fun contains(name: String?): Boolean { + return queue.containsKey(name) + } + + fun remove(name: String?) { + queue.remove(name) + queueTimers.remove(name) + } + + internal data class DisconnectionEntry(val player: Player, var isClear: Boolean) {} + + /** + * Saves the player. + * @param player The player to be saved. + * @param sql If the sql database should be updated. + */ + fun save(player: Player, sql: Boolean): Boolean { + try { + PlayerParser.saveImmediately(player) + } catch (t: Throwable) { + t.printStackTrace() + } + return false + } +} diff --git a/Server/src/main/java/core/game/world/repository/InitializationEntry.java b/Server/src/main/core/game/world/repository/InitializationEntry.java similarity index 100% rename from Server/src/main/java/core/game/world/repository/InitializationEntry.java rename to Server/src/main/core/game/world/repository/InitializationEntry.java diff --git a/Server/src/main/java/core/game/world/repository/InitializingNodeList.java b/Server/src/main/core/game/world/repository/InitializingNodeList.java similarity index 100% rename from Server/src/main/java/core/game/world/repository/InitializingNodeList.java rename to Server/src/main/core/game/world/repository/InitializingNodeList.java diff --git a/Server/src/main/java/core/game/world/repository/NodeList.java b/Server/src/main/core/game/world/repository/NodeList.java similarity index 100% rename from Server/src/main/java/core/game/world/repository/NodeList.java rename to Server/src/main/core/game/world/repository/NodeList.java diff --git a/Server/src/main/java/core/game/world/repository/NodeListIterator.java b/Server/src/main/core/game/world/repository/NodeListIterator.java similarity index 100% rename from Server/src/main/java/core/game/world/repository/NodeListIterator.java rename to Server/src/main/core/game/world/repository/NodeListIterator.java diff --git a/Server/src/main/core/game/world/repository/Repository.kt b/Server/src/main/core/game/world/repository/Repository.kt new file mode 100644 index 000000000..0b97f197c --- /dev/null +++ b/Server/src/main/core/game/world/repository/Repository.kt @@ -0,0 +1,207 @@ +package core.game.world.repository + +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.game.world.update.UpdateSequence +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 + */ +object Repository { + /** + * Represents the repository of active players. + */ + @JvmStatic + val players = NodeList(ServerConstants.MAX_PLAYERS) + val uid_map = HashMap(ServerConstants.MAX_PLAYERS) + + /** + * Represents the repository of active npcs. + */ + @JvmStatic + val npcs = NodeList(ServerConstants.MAX_NPCS) + + /** + * List of player names who have logged in, quick and dirty work-around for the double-logging issue. + * This list needs to have values added to it before any sort of parsing is done for the login process due + * to the time it takes for the parsing to occur, which is the root of this issue to begin with. + */ + val LOGGED_IN_PLAYERS: MutableList = ArrayList(ServerConstants.MAX_PLAYERS) + + /** + * The renderable NPCs. + */ + public val RENDERABLE_NPCS: MutableList = CopyOnWriteArrayList() + + /** + * A mapping holding the players sorted by their names. + */ + @JvmStatic + val playerNames: MutableMap = HashMap() + /** + * Represents the list of players in lobby. + */ + @JvmStatic + val lobbyPlayers: MutableList = ArrayList() + /** + * The disconnection queue. + */ + @JvmStatic + val disconnectionQueue = DisconnectionQueue() + + /** + * Send a news message to all players. + * @param string The string. + * @param icon The icon. + */ + @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 + if (isGeNews && player.getAttribute(GE_NEWS_MUTE_ATTRIBUTE, false)) continue + sendMessage(player, "News: $string") + } + } + + /** + * This is another sendnews method which redirects to the one above. + * This needs to exist because java fails to recognize default arguments in Kotlin methods.... + * I fucking hate java. + */ + @JvmStatic + @Deprecated("Old and bad",ReplaceWith("sendNews()"),DeprecationLevel.WARNING) + fun sendNews(string: String){ + sendNews(string,12) + } + + /** + * Adds a renderable NPC. + * @param npc The NPC. + */ + @JvmStatic + fun addRenderableNPC(npc: NPC) { + if (npc is RevenantNPC) return // hack to make sure we can update revenants every tick. + if (!RENDERABLE_NPCS.contains(npc)) { + RENDERABLE_NPCS.add(npc) + npc.isRenderable = true + } + } + + /** + * Removes an NPC from rendering. + * @param npc The NPC. + */ + @JvmStatic + fun removeRenderableNPC(npc: NPC) { + if (npc is RevenantNPC) return // hack to make sure we can update revenants every tick. + RENDERABLE_NPCS.remove(npc) + npc.isRenderable = false + } + + /** + * Finds the NPC on the given location. + * @param l The location. + * @return The NPC, or `null` if the NPC wasn't found. + */ + @JvmStatic + fun findNPC(l: Location): NPC? { + for (n in RegionManager.getRegionPlane(l).npcs) { + if (n.location == l) { + return n + } + } + return null + } + + @JvmStatic + fun addPlayer(player: Player){ + if (players.isNotEmpty()) { + for (i in 1 until players.size) { + players[i] ?: continue + if (players[i].details.uid == player.details.uid) { + val oldPl = players[i] + players.remove(oldPl) + oldPl.clear() + break + } + } + } + players.add(player) + uid_map[player.details.uid] = player + playerNames[player.name] = player + } + + @JvmStatic + fun removePlayer(player: Player){ + players.remove(player) + uid_map.remove(player.details.uid) + playerNames.remove(player.name) + UpdateSequence.renderablePlayers.remove(player) + player.session.disconnect() + } + + /** + * Find a non-player character. + * @param npcId The non-player character's id. + * @return The non-player character's node. + */ + @JvmStatic + fun findNPC(npcId: Int): NPC? { + for (npc in npcs) { + if (npc == null) { + continue + } + if (npc.id == npcId) { + return npc + } + } + return null + } + + /** + * Get a player by its username. + * @param name The player's **username**. + * @return The player. + */ + @JvmStatic + fun getPlayerByName(name: String?): Player? { + return if (name == null) { + null + } else playerNames[name.toLowerCase().replace(" ".toRegex(), "_")] + } + + @JvmStatic + fun getPlayerByUid(uid: Int) : Player? { + return uid_map[uid] + } + + /** + * Gets the renderableNpcs. + * @return The renderableNpcs. + */ + @JvmStatic + val renderableNpcs: List + get() = RENDERABLE_NPCS +} diff --git a/Server/src/main/kotlin/rs09/game/world/update/MapChunkRenderer.kt b/Server/src/main/core/game/world/update/MapChunkRenderer.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/world/update/MapChunkRenderer.kt rename to Server/src/main/core/game/world/update/MapChunkRenderer.kt index 1f356ed2e..b9e20112a 100644 --- a/Server/src/main/kotlin/rs09/game/world/update/MapChunkRenderer.kt +++ b/Server/src/main/core/game/world/update/MapChunkRenderer.kt @@ -1,4 +1,4 @@ -package rs09.game.world.update +package core.game.world.update import core.game.node.entity.player.Player import core.game.world.map.RegionChunk diff --git a/Server/src/main/kotlin/rs09/game/world/update/NPCRenderer.kt b/Server/src/main/core/game/world/update/NPCRenderer.kt similarity index 98% rename from Server/src/main/kotlin/rs09/game/world/update/NPCRenderer.kt rename to Server/src/main/core/game/world/update/NPCRenderer.kt index 14afa209c..dbb2fd01c 100644 --- a/Server/src/main/kotlin/rs09/game/world/update/NPCRenderer.kt +++ b/Server/src/main/core/game/world/update/NPCRenderer.kt @@ -1,11 +1,11 @@ -package rs09.game.world.update +package core.game.world.update import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.world.map.RegionManager import core.net.packet.IoBuffer import core.net.packet.PacketHeader -import rs09.game.world.GameWorld +import core.game.world.GameWorld import java.nio.ByteBuffer import java.util.* diff --git a/Server/src/main/kotlin/rs09/game/world/update/PlayerRenderer.kt b/Server/src/main/core/game/world/update/PlayerRenderer.kt similarity index 97% rename from Server/src/main/kotlin/rs09/game/world/update/PlayerRenderer.kt rename to Server/src/main/core/game/world/update/PlayerRenderer.kt index 15d92b9a6..e192463d7 100644 --- a/Server/src/main/kotlin/rs09/game/world/update/PlayerRenderer.kt +++ b/Server/src/main/core/game/world/update/PlayerRenderer.kt @@ -1,4 +1,4 @@ -package rs09.game.world.update +package core.game.world.update import core.game.node.entity.player.Player import core.game.node.entity.player.info.RenderInfo @@ -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 new file mode 100644 index 000000000..072148a1c --- /dev/null +++ b/Server/src/main/core/game/world/update/UpdateMasks.kt @@ -0,0 +1,178 @@ +package core.game.world.update + +import core.game.node.entity.Entity +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.* +import core.net.packet.IoBuffer +import core.api.* +import core.tools.* +import java.util.concurrent.atomic.AtomicBoolean + +class UpdateMasks (val owner: Entity) { + var appearanceStamp: Long = 0 + private val type = EFlagType.of (owner) + private val updating = AtomicBoolean() + 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: 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 (updating.get()) + return false + if (flag == EntityFlag.Appearance) { + appearanceStamp = System.currentTimeMillis() + synced = true + } + if (synced) { + syncedElements[provider.ordinal] = MaskElement (provider, context) + syncedPresenceFlags = syncedPresenceFlags or provider.presenceFlag + } + elements[provider.ordinal] = MaskElement (provider, context) + presenceFlags = presenceFlags or provider.presenceFlag + return true + } + + /** + * Unregisters a synced update mask. + * @param maskData The mask data. + * @return `True` if the mask got removed. + */ + fun unregisterSynced(ordinal: Int): Boolean { + if (syncedElements[ordinal] != null) { + syncedPresenceFlags = syncedPresenceFlags and syncedElements[ordinal]!!.encoder.presenceFlag.inv() + syncedElements[ordinal] = null + return true + } + return false + } + + /** + * Writes the flags. + * @param p The player. + * @param e The entity to update. + * @param buffer The buffer to write on. + */ + fun write(p: Player?, e: Entity?, buffer: IoBuffer) { + 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 elements.indices) { + val element = elements[i] + element?.encoder?.writeToDynamic(buffer, element.context, p!!) + } + } + + /** + * Writes the update masks on synchronization. + * @param p The player. + * @param e The entity to update. + * @param buffer The buffer to write on. + * @param appearance If the appearance mask should be written. + */ + fun writeSynced(p: Player?, e: Entity?, buffer: IoBuffer, appearance: Boolean) { + 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) { + maskData = maskData or if (e is Player) 0x10 else 0x8 + buffer.put(maskData).put(maskData shr 8) + } else { + buffer.put(maskData) + } + 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 + } + } + element?.encoder?.writeToDynamic(buffer, element.context, p!!) + } + } + + /** + * Adds the dynamic update flags. + * @param entity The entity. + */ + fun prepare(entity: Entity) { + val handler = entity.impactHandler + for (i in 0..1) { + if (handler.impactQueue.peek() == null) { + break + } + val impact = handler.impactQueue.poll() + registerHitUpdate(entity, impact, i == 1) + } + updating.set(true) + } + + /** + * Registers the hit update for the given [Impact]. + * @param e The entity. + * @param impact The impact to update. + * @param secondary If the hit update is secondary. + */ + private fun registerHitUpdate(e: Entity, impact: ImpactHandler.Impact, secondary: Boolean): HitMark { + val mark = HitMark(impact.amount, impact.type.ordinal, e) + register(if (secondary) EntityFlag.SecondaryHit else EntityFlag.PrimaryHit, mark) + return mark + } + + /** + * Resets the update masks. + */ + fun reset() { + for (i in elements.indices) { + elements[i] = null + } + presenceFlags = 0 + updating.set(false) + } + + fun isUpdating(): Boolean { + return updating.get() + } + + /** + * Checks if an update is required. + * @return `True` if so. + */ + val isUpdateRequired: Boolean + get() = presenceFlags != 0 + + /** + * Checks if synced update masks have been registered. + * @return `True` if so. + */ + fun hasSynced(): Boolean { + return syncedPresenceFlags != 0 + } + + companion object { + /** + * The amount of update masks. + */ + const val SIZE = 11 + } +} diff --git a/Server/src/main/core/game/world/update/UpdateSequence.kt b/Server/src/main/core/game/world/update/UpdateSequence.kt new file mode 100644 index 000000000..bc17fbd65 --- /dev/null +++ b/Server/src/main/core/game/world/update/UpdateSequence.kt @@ -0,0 +1,84 @@ +package core.game.world.update + +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.GroundItemManager +import core.game.world.map.RegionManager +import core.game.world.repository.InitializingNodeList +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. + * @author Emperor + */ +class UpdateSequence +/** + * Constructs a new `ParallelUpdatingSequence` `Object`. + */ +{ + var lobbyList: List? = null + var playersList: List? = null + var npcList: List? = null + + /** + * Starts the update sequence. + * @return `True` if we should continue. + */ + fun start() { + lobbyList = Repository.lobbyPlayers + playersList = renderablePlayers + npcList = Repository.renderableNpcs + lobbyList!!.map{ PacketRepository.send(ClearMinimapFlag::class.java, PlayerContext(it)) } + + 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() + } + + /** + * Ends the sequence, calls the [Entity.reset] method.. + */ + fun end() { + playersList!!.forEach(Player::reset) + npcList!!.forEach(NPC::reset) + renderablePlayers.sync() + RegionManager.pulse() + GroundItemManager.pulse() + } + + /** + * Terminates the update sequence. + */ + fun terminate() { + } + + companion object { + /** + * Gets the renderablePlayers. + * @return The renderablePlayers. + */ + /** + * The list of active players. + */ + @JvmStatic + val renderablePlayers = InitializingNodeList() + + } +} 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/java/core/game/world/update/flag/FlagContext.java b/Server/src/main/core/game/world/update/flag/FlagContext.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/FlagContext.java rename to Server/src/main/core/game/world/update/flag/FlagContext.java 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/java/core/game/world/update/flag/PlayerFlags.java b/Server/src/main/core/game/world/update/flag/PlayerFlags.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/PlayerFlags.java rename to Server/src/main/core/game/world/update/flag/PlayerFlags.java 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/java/core/game/world/update/flag/UpdateFlag.java b/Server/src/main/core/game/world/update/flag/UpdateFlag.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/UpdateFlag.java rename to Server/src/main/core/game/world/update/flag/UpdateFlag.java diff --git a/Server/src/main/java/core/game/world/update/flag/chunk/AnimateObjectUpdateFlag.java b/Server/src/main/core/game/world/update/flag/chunk/AnimateObjectUpdateFlag.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/chunk/AnimateObjectUpdateFlag.java rename to Server/src/main/core/game/world/update/flag/chunk/AnimateObjectUpdateFlag.java diff --git a/Server/src/main/java/core/game/world/update/flag/chunk/GraphicUpdateFlag.java b/Server/src/main/core/game/world/update/flag/chunk/GraphicUpdateFlag.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/chunk/GraphicUpdateFlag.java rename to Server/src/main/core/game/world/update/flag/chunk/GraphicUpdateFlag.java diff --git a/Server/src/main/java/core/game/world/update/flag/chunk/ItemUpdateFlag.java b/Server/src/main/core/game/world/update/flag/chunk/ItemUpdateFlag.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/chunk/ItemUpdateFlag.java rename to Server/src/main/core/game/world/update/flag/chunk/ItemUpdateFlag.java diff --git a/Server/src/main/java/core/game/world/update/flag/chunk/ObjectUpdateFlag.java b/Server/src/main/core/game/world/update/flag/chunk/ObjectUpdateFlag.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/chunk/ObjectUpdateFlag.java rename to Server/src/main/core/game/world/update/flag/chunk/ObjectUpdateFlag.java diff --git a/Server/src/main/java/core/game/world/update/flag/chunk/ProjectileUpdateFlag.java b/Server/src/main/core/game/world/update/flag/chunk/ProjectileUpdateFlag.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/chunk/ProjectileUpdateFlag.java rename to Server/src/main/core/game/world/update/flag/chunk/ProjectileUpdateFlag.java diff --git a/Server/src/main/java/core/game/world/update/flag/context/Animation.java b/Server/src/main/core/game/world/update/flag/context/Animation.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/context/Animation.java rename to Server/src/main/core/game/world/update/flag/context/Animation.java 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 new file mode 100644 index 000000000..c7ee24fa1 --- /dev/null +++ b/Server/src/main/core/game/world/update/flag/context/ChatMessage.kt @@ -0,0 +1,43 @@ +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. + * @author Emperor + */ +class ChatMessage +/** + * Constructs a new `ChatMessage` `Object`. + * @param player The player. + * @param text The chat text. + * @param effects The chat effects. + * @param numChars The num chars. + */(player: Player, text: String, effects: Int, numChars: Int) { + /** + * The player reference. + */ + var player: Player = player + private set + /** + * The chat text. + */ + var text: String = text + private set + /** + * The effects. + */ + var effects = effects + private set + /** + * The numChars. + */ + var numChars = numChars + private set + + var chatIcon = Rights.getChatIcon(player) + + @JvmField + var isQuickChat = false +} 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/java/core/game/world/update/flag/context/Graphics.java b/Server/src/main/core/game/world/update/flag/context/Graphics.java similarity index 100% rename from Server/src/main/java/core/game/world/update/flag/context/Graphics.java rename to Server/src/main/core/game/world/update/flag/context/Graphics.java diff --git a/Server/src/main/java/core/game/world/update/flag/context/HitMark.java b/Server/src/main/core/game/world/update/flag/context/HitMark.java similarity index 81% rename from Server/src/main/java/core/game/world/update/flag/context/HitMark.java rename to Server/src/main/core/game/world/update/flag/context/HitMark.java index ea6428624..7df5c73f7 100644 --- a/Server/src/main/java/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/worldevents/WorldEvent.kt b/Server/src/main/core/game/worldevents/WorldEvent.kt new file mode 100644 index 000000000..705d458cd --- /dev/null +++ b/Server/src/main/core/game/worldevents/WorldEvent.kt @@ -0,0 +1,73 @@ +package core.game.worldevents + +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 + */ +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(cal: Calendar): Boolean { + return false + } + + /** + * Used to initialize the event + * The WorldEventInitializer runs this if checkActive() returns true. + */ + open fun initEvent(){ + } + + /** + * Used to log world event messages in a standard and organized way. + */ + fun log(message: String){ + core.api.log(this::class.java, Log.FINE, "[World Events($name)] $message") + } +} + + +/** + * A class that holds a set of plugins that shouldn't be initialized by default. + * Can be used to initialize all of its plugins cleanly. + */ +class PluginSet(vararg val plugins: Plugin<*>){ + val set = ArrayList(plugins.asList()) + fun initialize() { + ClassScanner.definePlugins(*set.toTypedArray()) + } + fun add(plugin: Plugin<*>){ + set.add(plugin) + } +} + +/** + * Static object for storing instances of loaded events. + */ +object WorldEvents { + private var events = hashMapOf() + + fun add(event: WorldEvent){ + events[event.name.lowercase(Locale.getDefault())] = event + } + + fun get(name: String): WorldEvent?{ + return events[name.lowercase(Locale.getDefault())] + } + + fun getArchive() : JSONObject { + return ServerStore.getArchive("world-event-status") + } +} \ 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 new file mode 100644 index 000000000..e77df1a18 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/Giftmas.kt @@ -0,0 +1,162 @@ +package core.game.worldevents.holiday.christmas + +import core.api.* +import core.game.event.EventHook +import core.game.event.XPGainEvent +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.tools.RandomFunction +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.api.Event +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +import core.game.worldevents.WorldEvents +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.system.command.Privilege +import core.game.world.repository.Repository +import core.tools.colorize + +class Giftmas : Commands, StartupListener, LoginListener, InteractionListener { + override fun startup() { + if (checkActive()) + init() + } + + fun checkActive(): Boolean { + val archive = getArchive() + return archive.getBoolean("active") + } + + override fun login(player: Player) { + if (!checkActive()) return + player.hook(Event.XpGained, XpGainHook) + } + + fun init() { + try { + on(Items.MYSTERY_BOX_6199, IntType.ITEM, "open") { player, node -> + val loot = MBOX_LOOT.roll().first() + + if (!removeItem(player, node.asItem())) return@on true + + sendDialogue(player, "You open the mystery box and find ${loot.amount}x ${loot.name.lowercase()}!") + addItem(player, loot.id, loot.amount) + return@on true + } + } catch (ignored: Exception) {} + + for (player in Repository.players) + player.hook(Event.XpGained, XpGainHook) + } + + fun cleanup() { + for (player in Repository.players) + player.unhook(XpGainHook) + } + + override fun defineCommands() { + define("toggle-giftmas", Privilege.ADMIN, "", "Toggles the giftmas christmas event.") {player, args -> + val enabled = checkActive() + getArchive()["active"] = !enabled + notify(player, "Giftmas is now ${if (enabled) "DISABLED" else "ENABLED"}.") + if (!enabled) { + init() + } else { + cleanup() + } + } + } + + object XpGainHook : EventHook { + override fun process(entity: Entity, event: XPGainEvent) { + val wasCombat = event.skillId in 0..6 + val daily = getDailyGifts(entity.asPlayer(), wasCombat) + val player = entity.asPlayer() + val cooldown = entity.getAttribute("christmas-cooldown", 0L) + + if (event.amount < 25.0) return + if (!RandomFunction.roll(15)) return + if (daily >= if (wasCombat) DAILY_LIMIT_COMBAT else DAILY_LIMIT_SKILLING) return + if (System.currentTimeMillis() < cooldown) return + if (!addItem(player, Items.MYSTERY_BOX_6199)) return + + player.setAttribute("/save:christmas-cooldown", System.currentTimeMillis() + 5000L) + incrementDailyGifts(player, wasCombat) + sendMessage(player, MESSAGE_PRESENT_GRANTED) + + if (wasCombat && daily == DAILY_LIMIT_COMBAT - 1) + sendMessage(player, MESSAGE_DAILYXP_REACHED_COMBAT) + if (!wasCombat && daily == DAILY_LIMIT_SKILLING - 1) + sendMessage(player, MESSAGE_DAILYXP_REACHED_SKILLING) + } + + private fun getDailyGifts(player: Player, wasCombat: Boolean) : Int { + val archive = if (wasCombat) "daily-xmas-gifts-combat" else "daily-xmas-gifts-skilling" + return ServerStore.getArchive(archive).getInt(player.name) + } + + fun incrementDailyGifts(player: Player, wasCombat: Boolean) { + val start = getDailyGifts(player, wasCombat) + val archive = if (wasCombat) "daily-xmas-gifts-combat" else "daily-xmas-gifts-skilling" + ServerStore.getArchive(archive)[player.name] = start + 1 + } + } + + override fun defineListeners() {} + + companion object { + private val DAILY_LIMIT_SKILLING = 15 + private val DAILY_LIMIT_COMBAT = 5 + + private fun getArchive() : JSONObject { + val mainArchive = WorldEvents.getArchive() + if (!mainArchive.containsKey("giftmas")) + mainArchive["giftmas"] = JSONObject() + return mainArchive["giftmas"] as JSONObject + } + + 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!") + 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), + WeightedItem(Items.TOY_HORSEY_2526, 1, 1, 0.025), + WeightedItem(Items.TOY_KITE_12844, 1, 1, 0.025), + WeightedItem(Items.COAL_453, 1, 1, 0.025), + WeightedItem(Items.MOLTEN_GLASS_1776, 25, 50, 0.25), + WeightedItem(Items.FLAX_1780, 15, 70, 0.25), + WeightedItem(Items.BOW_STRING_1778, 10, 50, 0.15), + WeightedItem(Items.UNCUT_SAPPHIRE_1624, 1, 5, 0.15), + WeightedItem(Items.UNCUT_EMERALD_1622, 1, 5, 0.15), + WeightedItem(Items.UNCUT_RUBY_1620, 1, 5, 0.15), + WeightedItem(Items.UNCUT_DIAMOND_1618, 1, 5, 0.15), + WeightedItem(Items.UNCUT_SAPPHIRE_1624, 100, 100, 0.0015), + WeightedItem(Items.UNCUT_EMERALD_1622, 100, 100, 0.0015), + WeightedItem(Items.UNCUT_RUBY_1620, 100, 100, 0.0015), + WeightedItem(Items.UNCUT_DIAMOND_1618, 100, 100, 0.0015), + WeightedItem(Items.PURE_ESSENCE_7937, 1, 50, 0.15), + WeightedItem(Items.PURE_ESSENCE_7937, 1000, 1000, 0.0015), + WeightedItem(Items.RANARR_SEED_5295, 1, 3, 0.065), + WeightedItem(Items.SNAPDRAGON_SEED_5300, 1, 3, 0.065), + WeightedItem(Items.GOLD_CHARM_12158, 1, 15, 0.15), + WeightedItem(Items.CRIMSON_CHARM_12160, 1, 15, 0.15), + WeightedItem(Items.BLUE_CHARM_12163, 1, 15, 0.15), + WeightedItem(Items.GREEN_CHARM_12159, 1, 15, 0.15), + WeightedItem(Items.PURPLE_SWEETS_10476, 1, 15, 0.25), + WeightedItem(Items.COINS_995, 100, 1000, 0.15), + WeightedItem(Items.COINS_995, 50000, 100000, 0.0015), + WeightedItem(Items.COINS_995, 1000000, 1000000, 0.0005), + WeightedItem(Items.NATURE_RUNE_561, 1, 10, 0.15), + WeightedItem(Items.ABYSSAL_WHIP_4151, 1, 1, 0.00005), + WeightedItem(Items.SANTA_HAT_1050, 1, 1, 0.00005), + + ).insertEasyClue(0.015).insertMediumClue(0.010).insertHardClue(0.005).insertRDTRoll(0.015) + } +} \ No newline at end of file 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/EasterEvent.kt b/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt new file mode 100644 index 000000000..6b129b906 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/easter/EasterEvent.kt @@ -0,0 +1,320 @@ +package core.game.worldevents.holiday.easter + +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.world.GameWorld +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.path.Pathfinder +import core.game.worldevents.WorldEvent +import core.tools.RandomFunction +import core.tools.StringUtils +import core.tools.colorize +import org.rs09.consts.Items +import java.util.* + +class EasterEvent : WorldEvent("easter"), TickListener, InteractionListener, LoginListener, Commands { + private val spawnedItems = ArrayList() + private var timeUntilNextEggSpawn = 0 + private var currentLoc = "" + + override fun tick() { + if (timeUntilNextEggSpawn-- > 0) return + timeUntilNextEggSpawn = EGG_SPAWN_TIME + + for (egg in spawnedItems) + { + GroundItemManager.destroy(egg) + } + spawnedItems.clear() + + val (locName, locData) = getRandomLocations() + currentLoc = locName + + for (loc in locData) + { + val gi = GroundItemManager.create(GroundItem( + Item(eggs.random()), + loc, + EGG_SPAWN_TIME, + null + )) + gi.forceVisible = true + spawnedItems.add(gi) + } + + sendNews("Eggs have appeared in $locName!") + } + + 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) } + } + } + + object xpEventHook : EventHook + { + override fun process(entity: Entity, event: XPGainEvent) { + if (entity !is Player) return + + 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 new file mode 100644 index 000000000..2fdc30c28 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/easter/EasterEventListeners.kt @@ -0,0 +1,11 @@ +package core.game.worldevents.holiday.easter + +import core.game.interaction.InteractionListener + +class EasterEventListeners : InteractionListener { + override fun defineListeners() { + + + } + +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/CandyRewardPlugin.kt b/Server/src/main/core/game/worldevents/holiday/halloween/CandyRewardPlugin.kt similarity index 83% rename from Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/CandyRewardPlugin.kt rename to Server/src/main/core/game/worldevents/holiday/halloween/CandyRewardPlugin.kt index 10c91e2b4..aaffe9cff 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/CandyRewardPlugin.kt +++ b/Server/src/main/core/game/worldevents/holiday/halloween/CandyRewardPlugin.kt @@ -1,12 +1,12 @@ -package rs09.game.content.global.worldevents.holiday.halloween +package core.game.worldevents.holiday.halloween import core.game.node.entity.player.Player import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.tools.RandomFunction import core.game.node.entity.skill.Skills -import rs09.plugin.CorePluginTypes.XPGainPlugin -import rs09.tools.stringtools.colorize +import core.plugin.CorePluginTypes.XPGainPlugin +import core.tools.colorize class CandyRewardPlugin : XPGainPlugin(){ override fun run(player: Player, skill: Int, amount: Double) { diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/GrimDialogue.kt b/Server/src/main/core/game/worldevents/holiday/halloween/GrimDialogue.kt new file mode 100644 index 000000000..3a9d8fbe3 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/GrimDialogue.kt @@ -0,0 +1,225 @@ +package core.game.worldevents.holiday.halloween + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.emote.Emotes +import core.game.node.item.Item +import core.ServerStore +import core.ServerStore.Companion.getInt +import core.tools.END_DIALOGUE + +/** + * Handles grim's dialogue for the 2021 halloween event. + */ +class GrimDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ + var firstSpeak = true + val candy = Item(14084) + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return GrimDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + firstSpeak = !player.getAttribute("hween2:grim_spoken",false) + + if(firstSpeak){ + npc("YOU! Yes.... you! Come here!") + player.musicPlayer.unlock(571) + stage = 0 + } else { + npc("Hello, again, adventurer...") + stage = 100 + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> player(core.game.dialogue.FacialExpression.AFRAID,"W-what... what do you want with","me?").also { stage++ } + 1 -> npc("I want you.... I NEED you....").also { stage++ } + 2 -> npc("TO BRING ME CANDY! Yes, candy...").also { stage++ } + 3 -> player(core.game.dialogue.FacialExpression.THINKING,"Candy...? You want me to bring","you... candy?").also { stage++ } + 4 -> npc("Yes, candy! Did I not speak clearly","enough?").also { stage++ } + 5 -> player(core.game.dialogue.FacialExpression.ASKING,"Well how do I even get candy?").also { stage++ } + 6 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"It's Hallowe'en, you fool. Everyone's giving out candy.").also { stage++ } + 7 -> npc("You'll need to go and ask them for it each day.").also { stage++ } + 8 -> npc("You'll also need to be careful...","Something vile is on the prowl.").also { stage++ } + 9 -> player(core.game.dialogue.FacialExpression.THINKING,"And what will I get in exchange?").also { stage++ } + 10 -> npc("Well I won't KILL YOU for starters.").also { stage++ } + 11 -> player(core.game.dialogue.FacialExpression.ANGRY_WITH_SMILE, "Is that it?!").also { stage++ } + 12 -> npc("I've prepared a few... rewards, as well.").also { stage++ } + 13 -> player(core.game.dialogue.FacialExpression.AMAZED, "YOU MEAN CREDITS?!").also { stage++ } + 14 -> npc("No, we're not doing that again.").also { stage++ } + 15 -> player(core.game.dialogue.FacialExpression.SAD,"Oh.").also { stage++ } + 16 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Ok, so what kind of rewards?").also { stage++ } + 17 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I've fashioned a special-made staff to give out for this year's event.").also { stage++ } + 18 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "It is a decorative symbol of your engagement in this activity. Bring me candies, and it can be yours.").also { player.setAttribute("/save:hween2:grim_spoken",true); stage++ } + 19 -> npc("NOW GET TO WORK!").also { player.setAttribute("/save:hween2:grim_spoken",true); stage = 1000 } + + + 100 -> npc("I do hope you have... candy for me?").also { stage++ } + 101 -> if(player.inventory.containsItem(candy)){ + player("Yes, I do! Here you go.").also { stage = 150 } + } else { + player(core.game.dialogue.FacialExpression.SAD, "No, I don't.").also { stage++ } + } + 102 -> npc("A shame, indeed. Your candy totals right now are ${getCandyTotals(player)}.").also { stage++ } + 103 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to see the reward shop, please.").also { stage++ } + 104, 105 -> handleRewardShop(player, buttonId) + + + 150 -> { + val candies = player.inventory.getAmount(candy) + player.inventory.remove(Item(candy.id,candies)) + addToCandyTotal(player, candies) + npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Excellent, you now have ${getCandyTotals(player)} candies.") + stage = 103 + } + + + 1000 -> end() + } + return true + } + + fun handleRewardShop(player: Player, buttonId: Int){ + val title = "You have ${getCandyTotals(player)} candies." + val hasUnlocked = player.getAttribute("sotr:purchased",false) + val hasRecolor1 = player.getAttribute("sotr:recolor1", false) + val hasRecolor2 = player.getAttribute("sotr:recolor2", false) + val hasEmote = player.emoteManager.isUnlocked(Emotes.TRICK) + + if(!hasUnlocked && !hasEmote){ + when(stage){ + 104 -> player.dialogueInterpreter.sendOptions(title, "Staff of the Raven (40c)", "Trick Emote (10c)").also { stage++ } + 105 -> when(buttonId) { + 1 -> if (canPurchase(player, 40)) buyStaff(player) else npc( + core.game.dialogue.FacialExpression.NEUTRAL, + "You can't afford that." + ) + 2 -> if (canPurchase(player, 10)) buyEmote(player) else npc( + core.game.dialogue.FacialExpression.NEUTRAL, + "You can't afford that" + ) + } + } + } + else if(hasUnlocked && !hasEmote && !hasRecolor1 && !hasRecolor2){ + when(stage){ + 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Staff Orange Recolor (10c)", "Trick Emote (10c)").also { stage++ } + 105 -> when(buttonId){ + 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + 2 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + 3 -> if(canPurchase(player, 10)) buyEmote(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + } + } + } + else if(hasUnlocked && !hasEmote && hasRecolor1 && !hasRecolor2){ + when(stage){ + 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Orange Recolor (10c)", "Trick Emote (10c)").also { stage++ } + 105 -> when(buttonId){ + 1 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + 2 -> if(canPurchase(player, 10)) buyEmote(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + } + } + } + else if(hasUnlocked && !hasEmote && !hasRecolor1 && hasRecolor2){ + when(stage){ + 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Trick Emote (10c)").also { stage++ } + 105 -> when(buttonId){ + 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + 2 -> if(canPurchase(player, 10)) buyEmote(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + } + } + } + else if(!hasUnlocked && hasEmote){ + when(stage){ + 104 -> player.dialogueInterpreter.sendOptions(title, "Staff of the Raven (40c)", "").also { stage++ } + 105 -> when(buttonId){ + 1 -> if(canPurchase(player, 40)) buyStaff(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + 2 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 } + } + } + } + else if(hasUnlocked && hasEmote && !hasRecolor1 && !hasRecolor2){ + when(stage){ + 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Staff Orange Recolor (10c)").also { stage++ } + 105 -> when(buttonId){ + 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + 2 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + } + } + } + else if(hasUnlocked && hasEmote && !hasRecolor1 && hasRecolor2){ + when(stage){ + 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "").also { stage++ } + 105 -> when(buttonId){ + 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + 2 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 } + } + } + } + else if(hasUnlocked && hasEmote && hasRecolor1 && !hasRecolor2){ + when(stage){ + 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Orange Recolor (10c)", "").also { stage++ } + 105 -> when(buttonId){ + 1 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(core.game.dialogue.FacialExpression.NEUTRAL, "You can't afford that.") + 2 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 } + } + } + } + else { + npcl(core.game.dialogue.FacialExpression.NEUTRAL, "You've already bought everything.") + stage = END_DIALOGUE + } + } + + fun buyStaff(player: Player){ + player.setAttribute("/save:sotr:purchased", true) + removeCandies(player, 40) + addItem(player, 14654, 1) + stage = 104 + handleRewardShop(player, -1) + } + + fun buyRecolor1(player: Player){ + player.setAttribute("/save:sotr:recolor1", true) + removeCandies(player, 10) + stage = 104 + handleRewardShop(player, -1) + } + + fun buyRecolor2(player: Player){ + player.setAttribute("/save:sotr:recolor2", true) + removeCandies(player, 10) + stage = 104 + handleRewardShop(player, -1) + } + + fun buyEmote(player: Player){ + player.emoteManager.unlock(Emotes.TRICK) + removeCandies(player, 10) + stage = 104 + handleRewardShop(player, -1) + } + + fun canPurchase(player: Player, cost: Int) : Boolean { + return getCandyTotals(player) >= cost + } + + fun removeCandies(player: Player, amount: Int){ + addToCandyTotal(player, -amount) + } + + fun getCandyTotals(player: Player): Int { + return ServerStore.getArchive("hween2021-candies").getInt(player.username.toLowerCase()) + } + + fun addToCandyTotal(player: Player, amount: Int){ + ServerStore.getArchive("hween2021-candies").put(player.username.toLowerCase(), getCandyTotals(player) + amount) + } + + override fun getIds(): IntArray { + return intArrayOf(6390) + } + +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/TrickOrTreatHandler.kt b/Server/src/main/core/game/worldevents/holiday/halloween/TrickOrTreatHandler.kt new file mode 100644 index 000000000..c7c884f5e --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/TrickOrTreatHandler.kt @@ -0,0 +1,95 @@ +package core.game.worldevents.holiday.halloween + +import core.api.* +import core.game.component.Component +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.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import org.rs09.consts.Components +import core.ServerStore +import core.ServerStore.Companion.getInt +import core.ServerStore.Companion.getString +import core.game.dialogue.DialogueFile +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.GameWorld +import core.tools.END_DIALOGUE + +class TrickOrTreatHandler : InteractionListener { + override fun defineListeners() { + on(IntType.NPC, "trick-or-treat"){ player, node -> + val hasDone5 = getDailyTrickOrTreats(player) == 5 + val hasDoneMe = getTrickOrTreatedNPCs(player).contains(node.name.toLowerCase()) + + if(hasDone5){ + sendNPCDialogue(player, node.id, "My informants tell me you've already collected candy from 5 people today.", core.game.dialogue.FacialExpression.FRIENDLY) + return@on true + } + + if(hasDoneMe){ + sendNPCDialogue(player, node.id, "You've already asked me today! Don't get greedy, now.", core.game.dialogue.FacialExpression.ANNOYED) + return@on true + } + + player.dialogueInterpreter.open(object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Trick or treat!").also { if(RandomFunction.roll(20)) stage = 10 else stage++ } + 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Very well, then, here you are my friend.").also { stage++ } + 2 -> { + player.dialogueInterpreter.sendItemMessage(14084, "They hand you a nicely-wrapped candy.") + addItemOrDrop(player, 14084, 1) + registerNpc(player, npc!!) + incrementDailyToT(player) + stage = END_DIALOGUE + } + + 10 -> npcl(core.game.dialogue.FacialExpression.EVIL_LAUGH, "I CHOOSE TRICK!").also { player.lock(); GameWorld.submit(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + //gfx 1898 + when(counter++){ + 0 -> npc!!.visualize(Animation(1979), Graphics(1898)).also { npc!!.faceLocation(player.location) } + 2 -> player.dialogueInterpreter.close() + 5 -> player.interfaceManager.open(Component(Components.FADE_TO_BLACK_120)) + 8 -> player.properties.teleportLocation = Location.create(3106, 3382, 0) + 12 -> { + player.interfaceManager.close() + player.interfaceManager.open(Component(Components.FADE_FROM_BLACK_170)) + registerNpc(player, npc!!) + } + 15 -> player.interfaceManager.close().also { player.unlock() } + 16 -> return true + } + return false + } + }) } + } + } + }, node.asNpc()) + return@on true + } + } + + fun incrementDailyToT(player: Player){ + ServerStore.getArchive("daily-tot-total")[player.username.toLowerCase()] = getDailyTrickOrTreats(player) + 1 + } + + fun getDailyTrickOrTreats(player: Player) : Int { + return ServerStore.getArchive("daily-tot-total").getInt(player.username.toLowerCase()) + } + + fun getTrickOrTreatedNPCs(player: Player): String { + return ServerStore.getArchive("daily-tot-npcs").getString(player.username.toLowerCase()) + } + + fun registerNpc(player: Player, npc: NPC){ + var soFar = getTrickOrTreatedNPCs(player) + soFar += ":" + npc.name.toLowerCase() + ":" + ServerStore.getArchive("daily-tot-npcs")[player.username.toLowerCase()] = soFar + } +} \ 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 new file mode 100644 index 000000000..acb6755f7 --- /dev/null +++ b/Server/src/main/core/integrations/discord/Discord.kt @@ -0,0 +1,203 @@ +package core.integrations.discord + +import core.api.* +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import core.ServerConstants +import java.io.BufferedReader +import java.io.DataOutputStream +import java.io.InputStreamReader +import java.net.HttpURLConnection +import java.net.URL +import java.util.ArrayList + + +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 + } + } + } + } + + 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 { + 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) { + pendingMessages.add(Pair(url, data)) + } + } + } +} 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/java/core/net/Constants.java b/Server/src/main/core/net/Constants.java similarity index 100% rename from Server/src/main/java/core/net/Constants.java rename to Server/src/main/core/net/Constants.java diff --git a/Server/src/main/java/core/net/EventProducer.java b/Server/src/main/core/net/EventProducer.java similarity index 100% rename from Server/src/main/java/core/net/EventProducer.java rename to Server/src/main/core/net/EventProducer.java diff --git a/Server/src/main/core/net/IoEventHandler.java b/Server/src/main/core/net/IoEventHandler.java new file mode 100644 index 000000000..981b70dbf --- /dev/null +++ b/Server/src/main/core/net/IoEventHandler.java @@ -0,0 +1,114 @@ +package core.net; + +import core.game.world.repository.Repository; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.concurrent.ExecutorService; + +/** + * I/O event handling. + * @author Emperor + */ +public class IoEventHandler { + + /** + * The executor service. + */ + protected final ExecutorService service; + + /** + * Constructs a new {@code IoEventHandler}. + * @param service The executor service used for handling events. + */ + public IoEventHandler(ExecutorService service) { + this.service = service; + } + + /** + * Called when making a new connection. + * @param key The selection key. + * @throws IOException When an I/O exception occurs. + */ + public void connect(SelectionKey key) throws IOException { + /* + * empty. + */ + } + + /** + * Used for accepting a new connection. + * @param key The selection key. + * @param selector The selector. + * @throws IOException When an I/O exception occurs. + */ + public void accept(SelectionKey key, Selector selector) throws IOException { + SocketChannel sc = ((ServerSocketChannel) key.channel()).accept(); + sc.configureBlocking(false); + sc.socket().setTcpNoDelay(true); + sc.register(selector, SelectionKey.OP_READ); + } + + /** + * Reads the incoming packet data. + * @param key The selection key. + * @throws IOException When an I/O exception occurs. + */ + public void read(SelectionKey key) throws IOException { + ReadableByteChannel channel = (ReadableByteChannel) key.channel(); + ByteBuffer buffer = ByteBuffer.allocate(100_000); + IoSession session = (IoSession) key.attachment(); + try { + if (channel.read(buffer) == -1) { + if (session != null) { + session.disconnect(); + } + key.cancel(); + return; + } + } catch (IOException e) { + if (e.getMessage().contains("reset by peer") && session != null) { + session.disconnect(); + } else { + key.cancel(); + return; + } + } + buffer.flip(); + if (session == null) { + key.attach(session = new IoSession(key, service)); + } + service.execute(session.getProducer().produceReader(session, buffer)); + } + + /** + * Writes the outgoing packet data. + * @param key The selection key. + */ + public void write(SelectionKey key) { + IoSession session = (IoSession) key.attachment(); + key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); + session.write(); + } + + /** + * Disconnects a connection. + * @param key The selection key. + * @param t The occurred exception (if any). + */ + public void disconnect(SelectionKey key, Throwable t) { + try { + IoSession session = (IoSession) key.attachment(); + String cause = "" + t; + if (t != null && !(t instanceof ClosedChannelException || cause.contains("De externe host") || cause.contains("De software op uw") || cause.contains("An established connection was aborted") || cause.contains("An existing connection") || cause.contains("AsynchronousClose"))) { + t.printStackTrace(); + } + if (session != null) { + session.disconnect(); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/net/IoReadEvent.java b/Server/src/main/core/net/IoReadEvent.java similarity index 100% rename from Server/src/main/java/core/net/IoReadEvent.java rename to Server/src/main/core/net/IoReadEvent.java diff --git a/Server/src/main/core/net/IoSession.java b/Server/src/main/core/net/IoSession.java new file mode 100644 index 000000000..956b1d9da --- /dev/null +++ b/Server/src/main/core/net/IoSession.java @@ -0,0 +1,473 @@ +package core.net; + +import core.cache.crypto.ISAACPair; +import core.game.node.entity.player.Player; +import core.game.node.entity.player.info.ClientInfo; +import core.game.system.task.Pulse; +import core.auth.AuthResponse; +import core.game.world.GameWorld; +import core.net.producer.HSEventProducer; +import core.net.producer.LoginEventProducer; +import core.game.world.repository.Repository; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Represents a connected I/O session. + * @author Emperor + */ +public class IoSession { + + /** + * The handshake event producer. + */ + private static final EventProducer HANDSHAKE_PRODUCER = new HSEventProducer(); + + /** + * The selection key. + */ + private final SelectionKey key; + + public int isaacInputOpcode = 0; + + /** + * The executor service. + */ + private final ExecutorService service; + + /** + * The event producer. + */ + private EventProducer producer = HANDSHAKE_PRODUCER; + + /** + * The currently queued writing data. + */ + private List writingQueue = new ArrayList<>(20); + + /** + * The currently queued reading data. + */ + private ByteBuffer readingQueue; + + /** + * The writing lock. + */ + private Lock writingLock = new ReentrantLock(); + + /** + * The ISAAC cipher pair. + */ + private ISAACPair isaacPair; + + /** + * The name hash. + */ + private int nameHash; + + /** + * The server key. + */ + private long serverKey; + + /** + * The JS-5 encryption value. + */ + private int js5Encryption; + + /** + * The object. + */ + private Object object; + + /** + * If the session is active. + */ + private boolean active = true; + + /** + * The last ping time stamp. + */ + private long lastPing; + + /** + * The address. + */ + private final String address; + + /** + * The JS-5 queue. + */ + private final JS5Queue js5Queue; + + /** + * The client info. + */ + private ClientInfo clientInfo; + + public String associatedUsername; + + /** + * Constructs a new {@code IoSession}. + * @param key The selection key. + * @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 = 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. + */ + public void write(Object context) { + write(context, false); + } + + /** + * Fires a write event created using the current event producer. + * @param context The event context. + * @param instant If the event should be instantly executed on this thread. + */ + public void write(Object context, boolean instant) { + if (context == null) { + throw new IllegalStateException("Invalid writing context!"); + } + if (!(context instanceof AuthResponse) && producer instanceof LoginEventProducer) { + // new Throwable().printStackTrace(); + return; + } + if (instant) { + producer.produceWriter(this, context).run(); + return; + } + service.execute(producer.produceWriter(this, context)); + } + + /** + * Sends the packet data (without write event encoding). + * @param buffer The buffer. + */ + public void queue(ByteBuffer buffer) { + try { + writingLock.tryLock(1000L, TimeUnit.MILLISECONDS); + } catch (Exception e){ + e.printStackTrace(); + writingLock.unlock(); + } + writingQueue.add(buffer); + writingLock.unlock(); + write(); + } + + /** + * Handles the writing of all buffers in the queue. + */ + public void write() { + if (!key.isValid()) { + disconnect(); + return; + } + try { + writingLock.tryLock(1000L, TimeUnit.MILLISECONDS); + } catch (Exception e){ + e.printStackTrace(); + writingLock.unlock(); + return; + } + SocketChannel channel = (SocketChannel) key.channel(); + try { + while (!writingQueue.isEmpty()) { + ByteBuffer buffer = writingQueue.get(0); + channel.write(buffer); + if (buffer.hasRemaining()) { + key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); + break; + } + writingQueue.remove(0); + } + } catch (IOException e) { + disconnect(); + } + writingLock.unlock(); + } + + /** + * Disconnects the session. + */ + public void disconnect() { + try { + if (!active) { + return; + } + active = false; + 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) { + e.printStackTrace(); + } + } + + /** + * Gets the IP-address (without the port). + * @return The address. + */ + public String getAddress() { + return address; + } + + /** + * Gets the remote address of this session. + * @return The remote address, as a String. + */ + public String getRemoteAddress() { + try { + return ((SocketChannel) key.channel()).getRemoteAddress().toString(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Gets the current event producer. + * @return The producer. + */ + public EventProducer getProducer() { + return producer; + } + + /** + * Sets the event producer. + * @param producer The producer to set. + */ + public void setProducer(EventProducer producer) { + this.producer = producer; + } + + /** + * Gets the queued reading data. + * @return The readingQueue. + */ + public ByteBuffer getReadingQueue() { + synchronized (this) { + return readingQueue; + } + } + + /** + * Queues reading data. + * @param readingQueue The readingQueue to set. + */ + public void setReadingQueue(ByteBuffer readingQueue) { + synchronized (this) { + this.readingQueue = readingQueue; + } + } + + /** + * Gets the writing lock. + * @return The writing lock. + */ + public Lock getWritingLock() { + return writingLock; + } + + /** + * Gets the selection key. + * @return The selection key. + */ + public SelectionKey getKey() { + return key; + } + + /** + * @return The active. + */ + public boolean isActive() { + return active; + } + + /** + * @return The js5Encryption. + */ + public int getJs5Encryption() { + return js5Encryption; + } + + /** + * @param js5Encryption The js5Encryption to set. + */ + public void setJs5Encryption(int js5Encryption) { + this.js5Encryption = js5Encryption; + } + + /** + * @return The player. + */ + public Player getPlayer() { + if (object == null) { + object = Repository.getPlayerByName(associatedUsername); + } + return object instanceof Player ? ((Player) object) : null; + } + + /** + * Gets the object. + * @return the object. + */ + public Object getObject() { + return object; + } + + /** + * @param object The object to set. + */ + public void setObject(Object object) { + this.object = object; + } + + /** + * Gets the lastPing. + * @return The lastPing. + */ + public long getLastPing() { + return lastPing; + } + + /** + * Sets the lastPing. + * @param lastPing The lastPing to set. + */ + public void setLastPing(long lastPing) { + this.lastPing = lastPing; + } + + /** + * Gets the nameHash. + * @return The nameHash. + */ + public int getNameHash() { + return nameHash; + } + + /** + * Sets the nameHash. + * @param nameHash The nameHash to set. + */ + public void setNameHash(int nameHash) { + this.nameHash = nameHash; + } + + /** + * Gets the serverKey. + * @return The serverKey. + */ + public long getServerKey() { + return serverKey; + } + + /** + * Sets the serverKey. + * @param serverKey The serverKey to set. + */ + public void setServerKey(long serverKey) { + this.serverKey = serverKey; + } + + /** + * Gets the isaacPair. + * @return The isaacPair. + */ + public ISAACPair getIsaacPair() { + return isaacPair; + } + + /** + * Sets the isaacPair. + * @param isaacPair The isaacPair to set. + */ + public void setIsaacPair(ISAACPair isaacPair) { + this.isaacPair = isaacPair; + } + + /** + * Gets the js5Queue. + * @return the js5Queue + */ + public JS5Queue getJs5Queue() { + return js5Queue; + } + + /** + * Gets the clientInfo value. + * @return The clientInfo. + */ + public ClientInfo getClientInfo() { + return clientInfo; + } + + /** + * Sets the clientInfo value. + * @param clientInfo The clientInfo to set. + */ + public void setClientInfo(ClientInfo clientInfo) { + this.clientInfo = clientInfo; + } + +} diff --git a/Server/src/main/java/core/net/IoWriteEvent.java b/Server/src/main/core/net/IoWriteEvent.java similarity index 100% rename from Server/src/main/java/core/net/IoWriteEvent.java rename to Server/src/main/core/net/IoWriteEvent.java diff --git a/Server/src/main/java/core/net/JS5Queue.java b/Server/src/main/core/net/JS5Queue.java similarity index 100% rename from Server/src/main/java/core/net/JS5Queue.java rename to Server/src/main/core/net/JS5Queue.java diff --git a/Server/src/main/core/net/NioReactor.java b/Server/src/main/core/net/NioReactor.java new file mode 100644 index 000000000..fb07b826d --- /dev/null +++ b/Server/src/main/core/net/NioReactor.java @@ -0,0 +1,163 @@ +package core.net; + +import core.net.amsc.MSEventHandler; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Handles (NIO-based) networking events using the reactor pattern. + * @author Emperor + */ +public final class NioReactor implements Runnable { + + /** + * The executor service. + */ + private final ExecutorService service; + + /** + * The socket channel. + */ + private ServerSocketConnection channel; + + /** + * The I/O event handling instance. + */ + private IoEventHandler eventHandler; + + /** + * If the reactor is running. + */ + private boolean running; + + /** + * Constructs a new {@code NioReactor}. + */ + private NioReactor(IoEventHandler eventHandler) { + this.service = Executors.newSingleThreadScheduledExecutor(); + this.eventHandler = eventHandler; + } + + /** + * Creates and configures a new {@code NioReactor} with a pool size of 1. + * @param port The port. + * @return The {@code NioReactor} {@code Object}. + * @throws IOException When an I/O exception occurs. + */ + public static NioReactor configure(int port) throws IOException { + return configure(port, 1); + } + + /** + * Creates and configures a new {@code NioReactor}. + * @param port The port. + * @param poolSize The amount of threads in the thread pool. + * @return The {@code NioReactor} {@code Object}. + * @throws IOException When an I/O exception occurs. + */ + public static NioReactor configure(int port, int poolSize) throws IOException { + NioReactor reactor = new NioReactor(new IoEventHandler(Executors.newFixedThreadPool(poolSize))); + ServerSocketChannel channel = ServerSocketChannel.open(); + Selector selector = Selector.open(); + channel.bind(new InetSocketAddress(port)); + channel.configureBlocking(false); + channel.register(selector, SelectionKey.OP_ACCEPT); + reactor.channel = new ServerSocketConnection(selector, channel); + return reactor; + } + + /** + * Starts a NIO reactor used for client connections. + * @param address The IP-address to connect to. + * @param port The port used. + * @return The NIO reactor object. + * @throws IOException When an exception occurs. + */ + public static NioReactor connect(String address, int port) throws IOException { + NioReactor reactor = new NioReactor(new MSEventHandler()); + Selector selector = Selector.open(); + SocketChannel channel = SocketChannel.open(); + channel.configureBlocking(false); + channel.socket().setKeepAlive(true); + channel.socket().setTcpNoDelay(true); + channel.connect(new InetSocketAddress(address, port)); + channel.register(selector, SelectionKey.OP_CONNECT); + reactor.channel = new ServerSocketConnection(selector, channel); + return reactor; + } + + /** + * Starts the reactor. + */ + public void start() { + running = true; + service.execute(this); + } + + @Override + public void run() { + Thread.currentThread().setName("NioReactor"); + while (running) { + try { + channel.getSelector().select(); + } catch (IOException e) { + e.printStackTrace(); + } + Iterator iterator = channel.getSelector().selectedKeys().iterator(); + while (iterator.hasNext()) { + SelectionKey key = iterator.next(); + iterator.remove(); + try { + if (!key.isValid() || !key.channel().isOpen()) { + key.cancel(); + continue; + } + if (key.isConnectable()) { + eventHandler.connect(key); + } + if (key.isAcceptable()) { + eventHandler.accept(key, channel.getSelector()); + } + if (key.isReadable()) { + eventHandler.read(key); + } else if (key.isWritable()) { + eventHandler.write(key); + } + } catch (Throwable t) { + eventHandler.disconnect(key, t); + } + } + } + } + + /** + * Terminates the reactor (once it's done processing current I/O events). + */ + 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/java/core/net/ServerSocketConnection.java b/Server/src/main/core/net/ServerSocketConnection.java similarity index 100% rename from Server/src/main/java/core/net/ServerSocketConnection.java rename to Server/src/main/core/net/ServerSocketConnection.java diff --git a/Server/src/main/java/core/net/amsc/MSEventHandler.java b/Server/src/main/core/net/amsc/MSEventHandler.java similarity index 89% rename from Server/src/main/java/core/net/amsc/MSEventHandler.java rename to Server/src/main/core/net/amsc/MSEventHandler.java index 074efa367..70bea403d 100644 --- a/Server/src/main/java/core/net/amsc/MSEventHandler.java +++ b/Server/src/main/core/net/amsc/MSEventHandler.java @@ -1,6 +1,7 @@ package core.net.amsc; -import rs09.game.system.SystemLogger; +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("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 new file mode 100644 index 000000000..f1072e6fd --- /dev/null +++ b/Server/src/main/core/net/amsc/MSPacketRepository.java @@ -0,0 +1,100 @@ +package core.net.amsc; + +import core.game.node.entity.player.Player; +import core.game.system.communication.*; +import core.net.packet.IoBuffer; +import core.net.packet.PacketHeader; + +/** + * The Management server packet repository. + * @author Emperor + */ +public final class MSPacketRepository { + /** + * Sends a contact update. + * + * @param username The username. + * @param contact The contact's username. + * @param remove If we're removing the contact from the list. + * @param block If the contact list is for the blocked players. + * @param rank The new clan rank (or null when not updating clan rank!). + */ + public static void sendContactUpdate(String username, String contact, boolean remove, boolean block, ClanRank rank) { + IoBuffer buffer = new IoBuffer(block ? 5 : 4, PacketHeader.BYTE); + buffer.putString(username); + buffer.putString(contact); + if (rank != null) { + buffer.put((byte) 2); + buffer.put((byte) rank.ordinal()); + } else { + buffer.put((byte) (remove ? 1 : 0)); + } + WorldCommunicator.getSession().write(buffer); + } + + /** + * Sends the clan rename packet. + * + * @param player The player. + * @param clanName The clan name. + */ + public static void sendClanRename(Player player, String clanName) { + IoBuffer buffer = new IoBuffer(7, PacketHeader.BYTE); + buffer.putString(player.getName()); + buffer.putString(clanName); + WorldCommunicator.getSession().write(buffer); + } + + /** + * Sets a clan setting. + * + * @param player The player. + * @param type The setting type. + * @param rank The rank to set. + */ + public static void setClanSetting(Player player, int type, ClanRank rank) { + if (!WorldCommunicator.isEnabled()) { + return; + } + IoBuffer buffer = new IoBuffer(8, PacketHeader.BYTE); + buffer.putString(player.getName()); + buffer.put((byte) type); + if (rank != null) { + buffer.put((byte) rank.ordinal()); + } + WorldCommunicator.getSession().write(buffer); + } + + /** + * Sends the kicking a clan member packet. + * + * @param username The player's username. + * @param name The name. + */ + public static void sendClanKick(String username, String name) { + IoBuffer buffer = new IoBuffer(9, PacketHeader.BYTE); + buffer.putString(username); + buffer.putString(name); + WorldCommunicator.getSession().write(buffer); + } + + /** + * Sends the chat settings. + * + * @param player The player. + * @param publicSetting The public chat setting. + * @param privateSetting The private chat setting. + * @param tradeSetting The trade setting. + */ + public static void sendChatSetting(Player player, int publicSetting, int privateSetting, int tradeSetting) { + IoBuffer buffer = new IoBuffer(13, PacketHeader.BYTE); + buffer.putString(player.getName()); + buffer.put(publicSetting); + buffer.put(privateSetting); + buffer.put(tradeSetting); + if (WorldCommunicator.getSession() != null) + WorldCommunicator.getSession().write(buffer); + else + player.sendMessage("Privacy settings unavailable at the moment. Please try again later."); + } +} diff --git a/Server/src/main/java/core/net/amsc/ManagementServerState.java b/Server/src/main/core/net/amsc/ManagementServerState.java similarity index 96% rename from Server/src/main/java/core/net/amsc/ManagementServerState.java rename to Server/src/main/core/net/amsc/ManagementServerState.java index 8a9d91a23..81db14310 100644 --- a/Server/src/main/java/core/net/amsc/ManagementServerState.java +++ b/Server/src/main/core/net/amsc/ManagementServerState.java @@ -1,7 +1,7 @@ package core.net.amsc; import core.game.node.entity.player.Player; -import rs09.game.world.repository.Repository; +import core.game.world.repository.Repository; import core.net.packet.PacketRepository; import core.net.packet.context.ContactContext; import core.net.packet.out.ContactPackets; diff --git a/Server/src/main/java/core/net/amsc/WorldCommunicator.java b/Server/src/main/core/net/amsc/WorldCommunicator.java similarity index 79% rename from Server/src/main/java/core/net/amsc/WorldCommunicator.java rename to Server/src/main/core/net/amsc/WorldCommunicator.java index cce4f911e..1c634d1d0 100644 --- a/Server/src/main/java/core/net/amsc/WorldCommunicator.java +++ b/Server/src/main/core/net/amsc/WorldCommunicator.java @@ -1,16 +1,12 @@ package core.net.amsc; -import core.game.node.entity.player.info.Rights; -import core.game.node.entity.player.info.login.Response; -import core.game.system.task.TaskExecutor; import core.net.EventProducer; import core.net.IoSession; import core.net.NioReactor; import core.net.producer.MSHSEventProducer; -import kotlin.Unit; -import rs09.game.node.entity.player.info.login.LoginParser; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; +import core.game.node.entity.player.info.login.LoginParser; +import core.tools.SystemLogger; +import core.game.world.GameWorld; import java.io.IOException; import java.net.InetAddress; @@ -66,33 +62,11 @@ public final class WorldCommunicator { session.setObject(WORLDS[GameWorld.getSettings().getWorldId() - 1]); } - /** - * Registers a login attempt. - * @param parser The login attempt. - */ - public static void register(final LoginParser parser) { - LoginParser p = loginAttempts.get(parser.getDetails().getUsername()); - if (p != null && GameWorld.getTicks() - p.getTimeStamp() < 50 && p.getDetails().getRights() == Rights.REGULAR_PLAYER) { - parser.getDetails().getSession().write(Response.ALREADY_ONLINE, true); - return; - } - loginAttempts.put(parser.getDetails().getUsername(), parser); - TaskExecutor.executeSQL(() -> { - if (!parser.getDetails().parse()) { - parser.getDetails().getSession().write(Response.INVALID_LOGIN_SERVER, true); - return Unit.INSTANCE; - } - MSPacketRepository.sendPlayerRegistry(parser); - return Unit.INSTANCE; - }); - } - /** * Attempts to connect to the management server. */ public static void connect() { try { - SystemLogger.logInfo("Attempting to connect to management server..."); setState(ManagementServerState.CONNECTING); /*if (isLocallyHosted()) { SystemLogger.log("Management server is hosted on local machine!"); @@ -102,7 +76,6 @@ public final class WorldCommunicator { //} reactor.start(); } catch (Throwable e) { - SystemLogger.logErr("Unable to connect to management server"); e.printStackTrace(); terminate(); } @@ -212,7 +185,6 @@ public final class WorldCommunicator { if (WorldCommunicator.state != state) { WorldCommunicator.state = state; state.set(); - SystemLogger.logInfo("Management server status: " + state + "."); } } diff --git a/Server/src/main/java/core/net/amsc/WorldStatistics.java b/Server/src/main/core/net/amsc/WorldStatistics.java similarity index 100% rename from Server/src/main/java/core/net/amsc/WorldStatistics.java rename to Server/src/main/core/net/amsc/WorldStatistics.java diff --git a/Server/src/main/java/core/net/event/GameReadEvent.java b/Server/src/main/core/net/event/GameReadEvent.java similarity index 77% rename from Server/src/main/java/core/net/event/GameReadEvent.java rename to Server/src/main/core/net/event/GameReadEvent.java index 27544148e..f8dc8a948 100644 --- a/Server/src/main/java/core/net/event/GameReadEvent.java +++ b/Server/src/main/core/net/event/GameReadEvent.java @@ -1,15 +1,18 @@ package core.net.event; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; +import core.tools.Log; +import core.tools.SystemLogger; import core.net.IoReadEvent; import core.net.IoSession; -import core.net.packet.IncomingPacket; import core.net.packet.IoBuffer; -import core.net.packet.PacketRepository; +import core.net.packet.PacketProcessor; +import core.net.packet.in.Decoders530; +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,21 +94,22 @@ 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()); - if (packet == null) { - if (GameWorld.getSettings().isDevMode()) { - SystemLogger.logErr("Unhandled packet [opcode=" + opcode + ", previous=" + last + ", size=" + size + ", header=" + header + "]"); - } - continue; - } last = opcode; - try { - packet.decode(session.getPlayer(), opcode, buf); - //System.out.println("Handled packed " + opcode + "!"); - } catch (Throwable t) { - t.printStackTrace(); + + //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) { + log(this.getClass(), Log.WARN, "Unhandled opcode: " + opcode); + } else if (processed instanceof Packet.DecodingError) { + log(this.getClass(), Log.ERR, ((Packet.DecodingError) processed).getMessage()); + } else if (!(processed instanceof Packet.NoProcess)) { + PacketProcessor.enqueue(processed); } } } @@ -135,11 +137,7 @@ public final class GameReadEvent extends IoReadEvent { } return buffer.getShort() & 0xFFFF; } - if(header == -3){ - System.out.println(buffer.remaining()); - } - SystemLogger.logErr("Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "], header=" + header+"!"); return -1; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/net/event/GameWriteEvent.java b/Server/src/main/core/net/event/GameWriteEvent.java similarity index 100% rename from Server/src/main/java/core/net/event/GameWriteEvent.java rename to Server/src/main/core/net/event/GameWriteEvent.java diff --git a/Server/src/main/core/net/event/HSReadEvent.java b/Server/src/main/core/net/event/HSReadEvent.java new file mode 100644 index 000000000..bf98d5ad0 --- /dev/null +++ b/Server/src/main/core/net/event/HSReadEvent.java @@ -0,0 +1,76 @@ +package core.net.event; + +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; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import static core.api.ContentAPIKt.log; + +/** + * Handles handshake read events. + * @author Emperor + */ +public final class HSReadEvent extends IoReadEvent { + + // debug + static Map count = new HashMap<>(); + + /** + * Constructs a new {@code HSReadEvent}. + * @param session The session. + * @param buffer The buffer. + */ + public HSReadEvent(IoSession session, ByteBuffer buffer) { + super(session, buffer); + } + + @Override + public void read(IoSession session, ByteBuffer buffer) { + Integer amount = count.get(session.getAddress()); + if (amount == null) { + amount = 0; + } + count.put(session.getAddress(), amount + 1); + int opcode = buffer.get() & 0xFF; + switch (opcode) { + case 14: + session.setNameHash(buffer.get() & 0xFF); + session.setServerKey(RandomFunction.RANDOM.nextLong()); + session.write(true); + break; + case 15: + int revision = buffer.getInt(); + //int sub_revision = buffer.getInt(); + buffer.flip(); + + if (revision != 530 ){//|| sub_revision != Constants.CLIENT_BUILD) { + session.disconnect(); + break; + } + session.write(false); + break; + case 147: + case 186: + case 36: + AccountRegister.read(session, opcode, buffer); + break; + case 255: // World list + int updateStamp = buffer.getInt(); + WorldList.sendUpdate(session, updateStamp); + break; + default: + log(this.getClass(), Log.FINE, "PKT " + opcode); + session.disconnect(); + break; + } + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/net/event/HSWriteEvent.java b/Server/src/main/core/net/event/HSWriteEvent.java similarity index 100% rename from Server/src/main/java/core/net/event/HSWriteEvent.java rename to Server/src/main/core/net/event/HSWriteEvent.java diff --git a/Server/src/main/java/core/net/event/JS5ReadEvent.java b/Server/src/main/core/net/event/JS5ReadEvent.java similarity index 78% rename from Server/src/main/java/core/net/event/JS5ReadEvent.java rename to Server/src/main/core/net/event/JS5ReadEvent.java index 893e9d374..4b1571d44 100644 --- a/Server/src/main/java/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; @@ -33,12 +33,12 @@ public final class JS5ReadEvent extends IoReadEvent { switch (opcode) { case 0: case 1: - int request = ByteBufferUtils.getTriByte(buffer); - //System.out.println("Requested file " + request); + int request = ByteBufferUtils.getMedium(buffer); + 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 @@ -62,7 +62,7 @@ public final class JS5ReadEvent extends IoReadEvent { buffer.getInt(); break; case 6: - ByteBufferUtils.getTriByte(buffer); // Value should be 3 + ByteBufferUtils.getMedium(buffer); // Value should be 3 // buffer.getShort(); // Value should be 0 break; case 7: @@ -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/java/core/net/event/JS5WriteEvent.java b/Server/src/main/core/net/event/JS5WriteEvent.java similarity index 100% rename from Server/src/main/java/core/net/event/JS5WriteEvent.java rename to Server/src/main/core/net/event/JS5WriteEvent.java diff --git a/Server/src/main/core/net/event/LoginReadEvent.kt b/Server/src/main/core/net/event/LoginReadEvent.kt new file mode 100644 index 000000000..fb713b1db --- /dev/null +++ b/Server/src/main/core/net/event/LoginReadEvent.kt @@ -0,0 +1,42 @@ +package core.net.event + +import core.game.node.entity.player.info.ClientInfo +import core.game.node.entity.player.info.PlayerDetails +import core.net.IoReadEvent +import core.net.IoSession +import core.auth.AuthResponse +import core.game.world.GameWorld +import core.net.packet.`in`.Login +import java.nio.ByteBuffer + +/** + * Handles login reading events. + * @author Ceikry + */ +class LoginReadEvent(session: IoSession?, buffer: ByteBuffer?) : IoReadEvent(session, buffer) { + override fun read(session: IoSession, buffer: ByteBuffer) { + 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 + } + 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) + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/core/net/event/LoginWriteEvent.java b/Server/src/main/core/net/event/LoginWriteEvent.java similarity index 92% rename from Server/src/main/java/core/net/event/LoginWriteEvent.java rename to Server/src/main/core/net/event/LoginWriteEvent.java index 95b4e61c8..6da3a9a19 100644 --- a/Server/src/main/java/core/net/event/LoginWriteEvent.java +++ b/Server/src/main/core/net/event/LoginWriteEvent.java @@ -6,6 +6,7 @@ import core.net.EventProducer; import core.net.IoSession; import core.net.IoWriteEvent; import core.net.producer.GameEventProducer; +import core.auth.AuthResponse; import java.nio.ByteBuffer; @@ -31,10 +32,10 @@ public final class LoginWriteEvent extends IoWriteEvent { @Override public void write(IoSession session, Object context) { - Response response = (Response) context; + AuthResponse response = (AuthResponse) context; ByteBuffer buffer = ByteBuffer.allocate(500); - buffer.put((byte) response.opcode()); - switch (response.opcode()) { + buffer.put((byte) response.ordinal()); + switch (response.ordinal()) { case 2: //successful login buffer.put(getWorldResponse(session)); session.setProducer(GAME_PRODUCER); diff --git a/Server/src/main/java/core/net/event/MSHSReadEvent.java b/Server/src/main/core/net/event/MSHSReadEvent.java similarity index 100% rename from Server/src/main/java/core/net/event/MSHSReadEvent.java rename to Server/src/main/core/net/event/MSHSReadEvent.java diff --git a/Server/src/main/java/core/net/event/MSHSWriteEvent.java b/Server/src/main/core/net/event/MSHSWriteEvent.java similarity index 94% rename from Server/src/main/java/core/net/event/MSHSWriteEvent.java rename to Server/src/main/core/net/event/MSHSWriteEvent.java index b7f7aebb6..988bb4909 100644 --- a/Server/src/main/java/core/net/event/MSHSWriteEvent.java +++ b/Server/src/main/core/net/event/MSHSWriteEvent.java @@ -3,8 +3,7 @@ package core.net.event; import core.cache.misc.buffer.ByteBufferUtils; import core.net.IoSession; import core.net.IoWriteEvent; -import rs09.Server; -import rs09.ServerConstants; +import core.ServerConstants; import java.nio.ByteBuffer; diff --git a/Server/src/main/java/core/net/event/MSReadEvent.java b/Server/src/main/core/net/event/MSReadEvent.java similarity index 85% rename from Server/src/main/java/core/net/event/MSReadEvent.java rename to Server/src/main/core/net/event/MSReadEvent.java index 77592ba15..5e12d6963 100644 --- a/Server/src/main/java/core/net/event/MSReadEvent.java +++ b/Server/src/main/core/net/event/MSReadEvent.java @@ -1,12 +1,14 @@ package core.net.event; -import rs09.game.system.SystemLogger; +import core.tools.Log; +import core.tools.SystemLogger; import core.net.IoReadEvent; import core.net.IoSession; -import core.net.amsc.MSPacketRepository; import java.nio.ByteBuffer; +import static core.api.ContentAPIKt.log; + /** * Handles reading Management server packets. * @author Emperor @@ -60,11 +62,6 @@ public final class MSReadEvent extends IoReadEvent { byte[] data = new byte[size]; buffer.get(data); last = opcode; - try { - MSPacketRepository.handleIncoming(opcode, ByteBuffer.wrap(data)); - } catch (Throwable t) { - t.printStackTrace(); - } } } @@ -91,8 +88,7 @@ public final class MSReadEvent extends IoReadEvent { } return buffer.getShort() & 0xFFFF; } - SystemLogger.logErr("Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "]!"); return -1; } -} \ No newline at end of file +} diff --git a/Server/src/main/java/core/net/event/MSWriteEvent.java b/Server/src/main/core/net/event/MSWriteEvent.java similarity index 100% rename from Server/src/main/java/core/net/event/MSWriteEvent.java rename to Server/src/main/core/net/event/MSWriteEvent.java diff --git a/Server/src/main/core/net/event/RegistryReadEvent.java b/Server/src/main/core/net/event/RegistryReadEvent.java new file mode 100644 index 000000000..bfbedd61a --- /dev/null +++ b/Server/src/main/core/net/event/RegistryReadEvent.java @@ -0,0 +1,58 @@ +package core.net.event; + +import core.tools.Log; +import core.tools.SystemLogger; +import core.game.world.GameWorld; +import core.net.IoReadEvent; +import core.net.IoSession; +import core.net.amsc.ManagementServerState; +import core.net.amsc.WorldCommunicator; +import core.net.producer.MSEventProducer; + +import java.nio.ByteBuffer; + +import static core.api.ContentAPIKt.log; + +/** + * Handles world registry read events. + * @author Emperor + */ +public final class RegistryReadEvent extends IoReadEvent { + + /** + * The event producer. + */ + private static final MSEventProducer PRODUCER = new MSEventProducer(); + + /** + * Constructs a new {@code RegistryReadEvent} {@code Object}. + * @param session The session. + * @param buffer The buffer to read. + */ + public RegistryReadEvent(IoSession session, ByteBuffer buffer) { + super(session, buffer); + } + + @Override + public void read(IoSession session, ByteBuffer buffer) { + int opcode = buffer.get() & 0xFF; + switch (opcode) { + case 0: + WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); + 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); + break; + case 2: + case 3: + WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); + break; + default: + + break; + } + } + +} \ No newline at end of file diff --git a/Server/src/main/core/net/event/RegistryWriteEvent.java b/Server/src/main/core/net/event/RegistryWriteEvent.java new file mode 100644 index 000000000..6e94e58c0 --- /dev/null +++ b/Server/src/main/core/net/event/RegistryWriteEvent.java @@ -0,0 +1,46 @@ +package core.net.event; + +import core.cache.misc.buffer.ByteBufferUtils; +import core.game.world.GameWorld; +import core.net.Constants; +import core.net.IoSession; +import core.net.IoWriteEvent; + +import java.nio.ByteBuffer; + +/** + * Handles game world registry writing events. + * @author Emperor + */ +public final class RegistryWriteEvent extends IoWriteEvent { + + /** + * The string check. + */ + private static final String CHECK = "12x4578f5g45hrdjiofed59898"; + + /** + * Constructs a new {@code RegistryWriteEvent} {@code Object}. + * @param session The I/O session. + * @param context The writing context. + */ + public RegistryWriteEvent(IoSession session, Object context) { + super(session, context); + } + + @Override + public void write(IoSession session, Object context) { + ByteBuffer buffer = ByteBuffer.allocate(128); + buffer.put((byte) GameWorld.getSettings().getWorldId()); + buffer.putInt(Constants.REVISION); + buffer.put((byte) GameWorld.getSettings().getCountryIndex()); + buffer.put((byte) (GameWorld.getSettings().isMembers() ? 1 : 0)); + buffer.put((byte) (GameWorld.getSettings().isPvp() ? 1 : 0)); + buffer.put((byte) (GameWorld.getSettings().isQuickChat() ? 1 : 0)); + buffer.put((byte) (GameWorld.getSettings().isLootshare() ? 1 : 0)); + ByteBufferUtils.putString(GameWorld.getSettings().getActivity(), buffer); + buffer.put(CHECK.getBytes()); + session.queue((ByteBuffer) buffer.flip()); + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/net/lobby/WorldDefinition.java b/Server/src/main/core/net/lobby/WorldDefinition.java similarity index 96% rename from Server/src/main/java/core/net/lobby/WorldDefinition.java rename to Server/src/main/core/net/lobby/WorldDefinition.java index a67d4c1b4..92baf066c 100644 --- a/Server/src/main/java/core/net/lobby/WorldDefinition.java +++ b/Server/src/main/core/net/lobby/WorldDefinition.java @@ -1,7 +1,7 @@ package core.net.lobby; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; +import core.game.world.GameWorld; +import core.game.world.repository.Repository; /** * Represents a world's definition. diff --git a/Server/src/main/java/core/net/lobby/WorldList.java b/Server/src/main/core/net/lobby/WorldList.java similarity index 99% rename from Server/src/main/java/core/net/lobby/WorldList.java rename to Server/src/main/core/net/lobby/WorldList.java index dc23a0301..c7d24f36b 100644 --- a/Server/src/main/java/core/net/lobby/WorldList.java +++ b/Server/src/main/core/net/lobby/WorldList.java @@ -1,6 +1,6 @@ package core.net.lobby; -import rs09.game.world.GameWorld; +import core.game.world.GameWorld; import core.net.IoSession; import core.net.packet.IoBuffer; @@ -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/java/core/net/packet/Context.java b/Server/src/main/core/net/packet/Context.java similarity index 100% rename from Server/src/main/java/core/net/packet/Context.java rename to Server/src/main/core/net/packet/Context.java diff --git a/Server/src/main/java/core/net/packet/IncomingPacket.java b/Server/src/main/core/net/packet/IncomingPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/IncomingPacket.java rename to Server/src/main/core/net/packet/IncomingPacket.java diff --git a/Server/src/main/core/net/packet/IoBuffer.java b/Server/src/main/core/net/packet/IoBuffer.java new file mode 100644 index 000000000..279798007 --- /dev/null +++ b/Server/src/main/core/net/packet/IoBuffer.java @@ -0,0 +1,861 @@ +package core.net.packet; + +import core.cache.crypto.ISAACCipher; +import core.cache.misc.buffer.ByteBufferUtils; + +import java.nio.ByteBuffer; + +/** + * Represents the buffer used for reading/writing packets. + * @author Emperor + */ +public class IoBuffer { + + /** + * The bit masks. + */ + private static final int[] BIT_MASK = new int[32]; + + /** + * The packet size. + */ + private int packetSize; + + /** + * The opcode. + */ + private int opcode; + + /** + * The packet header. + */ + private final PacketHeader header; + + /** + * The byte buffer. + */ + private ByteBuffer buf; + + /** + * The bit position. + */ + private int bitPosition = 0; + + /** + * Constructs a new {@code IoBuffer} {@code Object}. + */ + public IoBuffer() { + this(-1, PacketHeader.NORMAL, ByteBuffer.allocate(2048)); + } + + /** + * Constructs a new {@code IoBuffer} {@code Object}. + * @param opcode The opcode. + */ + public IoBuffer(int opcode) { + this(opcode, PacketHeader.NORMAL, ByteBuffer.allocate(2048)); + } + + /** + * Constructs a new {@code IoBuffer} {@code Object}. + * @param opcode The opcode. + * @param header The packet header. + */ + public IoBuffer(int opcode, PacketHeader header) { + this(opcode, header, ByteBuffer.allocate((1 << 16) + 1)); + } + + /** + * Constructs a new {@code IoBuffer} {@code Object}. + * @param opcode The opcode. + * @param header The packet header. + * @param buf The byte buffer. + */ + public IoBuffer(int opcode, PacketHeader header, ByteBuffer buf) { + this.opcode = opcode; + this.header = header; + this.buf = buf; + } + + static { + for (int i = 0; i < 32; i++) { + BIT_MASK[i] = (1 << i) - 1; + } + } + + /** + * @return + */ + public IoBuffer clear() { + buf.clear(); + bitPosition = 0; + 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 + */ + public IoBuffer put(int val) { + buf.put((byte) val); + return this; + } + + /** + * @param datas + * @param offset + * @param len + * @return + */ + public IoBuffer putBytes(byte[] datas, int offset, int len) { + for (int i = offset; i < len; i++) { + put(datas[i]); + } + return this; + } + + public final void getBytes(byte data[], int off, int len) { + for (int k = off; k < len + off; k++) { + data[k] = data[off++]; + } + } + + /** + * @param val + * @return + */ + public IoBuffer putA(int val) { + buf.put((byte) (val + 128)); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putC(int val) { + buf.put((byte) -val); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putS(int val) { + buf.put((byte) (128 - val)); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putTri(int val) { + buf.put((byte) (val >> 16)); + buf.put((byte) (val >> 8)); + buf.put((byte) val); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putShort(int val) { + buf.putShort((short) val); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putLEShort(int val) { + buf.put((byte) val); + buf.put((byte) (val >> 8)); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putShortA(int val) { + buf.put((byte) (val >> 8)); + buf.put((byte) (val + 128)); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putLEShortA(int val) { + buf.put((byte) (val + 128)); + buf.put((byte) (val >> 8)); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putInt(int val) { + buf.putInt(val); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putLEInt(int val) { + buf.put((byte) val); + buf.put((byte) (val >> 8)); + buf.put((byte) (val >> 16)); + buf.put((byte) (val >> 24)); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putIntA(int val) { + buf.put((byte) (val >> 8)); + buf.put((byte) val); + buf.put((byte) (val >> 24)); + buf.put((byte) (val >> 16)); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putIntB(int val) { + buf.put((byte) (val >> 16)); + buf.put((byte) (val >> 24)); + buf.put((byte) val); + buf.put((byte) (val >> 8)); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putLong(long val) { + buf.putLong(val); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putSmart(int val) { + if (val > Byte.MAX_VALUE) { + buf.putShort((short) (val + 32768)); + } else { + buf.put((byte) val); + } + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putIntSmart(int val) { + if (val > Short.MAX_VALUE) { + buf.putInt(val + 32768); + } else { + buf.putShort((short) val); + } + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putString(String val) { + buf.put(val.getBytes()); + buf.put((byte) 0); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putJagString(String val) { + buf.put((byte) 0); + buf.put(val.getBytes()); + buf.put((byte) 0); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer putJagString2(String val) { + byte[] packed = new byte[256]; + int length = ByteBufferUtils.packGJString2(0, packed, val); + buf.put((byte) 0).put(packed, 0, length).put((byte) 0); + return this; + } + + /** + * @param val + * @return + */ + public IoBuffer put(byte[] val) { + buf.put(val); + return this; + } + + /** + * Puts a byte array as byte A in reverse. + * @param data The data to put. + * @param start The start index. + * @param offset The offset. + */ + public void putReverseA(byte[] data, int start, int offset) { + for (int i = offset + start; i >= start; i--) { + putA(data[i]); + } + } + + /** + * Puts a byte array as byte A in reverse. + * @param data The data to put. + * @param start The start index. + * @param offset The offset. + */ + public void putReverse(byte[] data, int start, int offset) { + for (int i = offset + start; i >= start; i--) { + put(data[i]); + } + } + + /** + * @param numBits + * @param value + * @return + */ + public IoBuffer putBits(int numBits, int value) { + int bytePos = getBitPosition() >> 3; + int bitOffset = 8 - (getBitPosition() & 7); + bitPosition += numBits; + for (; numBits > bitOffset; bitOffset = 8) { + byte b = buf.get(bytePos); + buf.put(bytePos, b &= ~BIT_MASK[bitOffset]); + buf.put(bytePos++, b |= value >> numBits - bitOffset & BIT_MASK[bitOffset]); + numBits -= bitOffset; + } + byte b = buf.get(bytePos); + if (numBits == bitOffset) { + buf.put(bytePos, b &= ~BIT_MASK[bitOffset]); + buf.put(bytePos, b |= value & BIT_MASK[bitOffset]); + } else { + buf.put(bytePos, b &= ~(BIT_MASK[numBits] << bitOffset - numBits)); + buf.put(bytePos, b |= (value & BIT_MASK[numBits]) << bitOffset - numBits); + } + return this; + } + + /** + * @param buffer + * @return + */ + public IoBuffer put(IoBuffer buffer) { + buffer.toByteBuffer().flip(); + buf.put(buffer.toByteBuffer()); + return this; + } + + /** + * @param buffer + * @return + */ + public IoBuffer putA(IoBuffer buffer) { + buffer.toByteBuffer().flip(); + while (buffer.toByteBuffer().hasRemaining()) { + putA(buffer.toByteBuffer().get()); + } + return this; + } + + /** + * @param buffer + * @return + */ + public IoBuffer put(ByteBuffer buffer) { + buf.put(buffer); + return this; + } + + /** + * @return + */ + public IoBuffer setBitAccess() { + bitPosition = buf.position() * 8; + return this; + } + + /** + * @return + */ + public IoBuffer setByteAccess() { + buf.position((getBitPosition() + 7) / 8); + return this; + } + + /** + * @return + */ + public int get() { + return buf.get(); + } + + /** + * @return + */ + public int getA() { + return (buf.get() & 0xFF) - 128; + } + + /** + * @return + */ + public int getC() { + return -buf.get(); + } + + /** + * @return + */ + public int getS() { + return 128 - (buf.get() & 0xFF); + } + + /** + * @return + */ + public int getTri() { + return ((buf.get() << 16) & 0xFF) | ((buf.get() << 8) & 0xFF) | (buf.get() & 0xFF); + } + + /** + * @return + */ + public int getShort() { + return buf.getShort(); + } + + /** + * @return + */ + public int getLEShort() { + return (buf.get() & 0xFF) | ((buf.get() & 0xFF) << 8); + } + + /** + * @return + */ + public int getShortA() { + return ((buf.get() & 0xFF) << 8) | (buf.get() - 128 & 0xFF); + } + + /** + * @return + */ + public int getLEShortA() { + return (buf.get() - 128 & 0xFF) | ((buf.get() & 0xFF) << 8); + } + + /** + * @return + */ + public int getInt() { + return buf.getInt(); + } + + /** + * @return + */ + public int getLEInt() { + return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8) + ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24); + } + + /** + * @return + */ + public int getIntA() { + return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16); + } + + /** + * @return + */ + public int getIntB() { + return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24) + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8); + } + + /** + * @return + */ + public long getLongL() { + long first = getIntB(); + long second = getIntB(); + if (second < 0) + second = second & 0xffffffffL; + return (first << -41780448) + second; + } + + /** + * @return + */ + public long getLong() { + return buf.getLong(); + } + + /** + * @return + */ + public int getSmart() { + int peek = buf.get(buf.position()); + if (peek <= (0xFF & peek)) { + return buf.get() & 0xFF; + } + return (buf.getShort() & 0xFFFF) - 32768; + } + + /** + * @return + */ + public int getIntSmart() { + int peek = buf.getShort(buf.position()); + if (peek <= Short.MAX_VALUE) { + return buf.getShort() & 0xFFFF; + } + return (buf.getInt() & 0xFFFFFFFF) - 32768; + } + + /** + * @return + */ + public String getString() { + return ByteBufferUtils.getString(buf); + } + + /** + * @return + */ + public String getJagString() { + byte b = buf.get(); + if (b == 0) return ""; + return ((char) b) + ByteBufferUtils.getString(buf); + } + + /** + * @param is + * @param offset + * @param length + * @return + */ + public IoBuffer getReverseA(byte[] is, int offset, int length) { + for (int i = (offset + length - 1); i >= offset; i--) { + is[i] = (byte) (buf.get() - 128); + } + return this; + } + + public void cypherOpcode(ISAACCipher cipher){ + this.opcode += (byte)cipher.getNextValue(); + } + + /** + * @return + */ + public ByteBuffer toByteBuffer() { + return buf; + } + + /** + * @return + */ + public int opcode() { + return opcode; + } + + /** + * @return + */ + public int readableBytes() { + return buf.capacity() - buf.remaining(); + } + + /** + * @return + */ + public PacketHeader getHeader() { + return header; + } + + /** + * @return + */ + public byte[] array() { + return buf.array(); + } + + /** + * @return the packetSize. + */ + public int getPacketSize() { + return packetSize; + } + + /** + * @param packetSize the packetSize to set. + */ + public void setPacketSize(int packetSize) { + this.packetSize = packetSize; + } + + /** + * Gets the bitPosition. + * @return The bitPosition. + */ + public int getBitPosition() { + return bitPosition; + } + +} diff --git a/Server/src/main/java/core/net/packet/OutgoingPacket.java b/Server/src/main/core/net/packet/OutgoingPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/OutgoingPacket.java rename to Server/src/main/core/net/packet/OutgoingPacket.java diff --git a/Server/src/main/java/core/net/packet/PacketHeader.java b/Server/src/main/core/net/packet/PacketHeader.java similarity index 100% rename from Server/src/main/java/core/net/packet/PacketHeader.java rename to Server/src/main/core/net/packet/PacketHeader.java diff --git a/Server/src/main/core/net/packet/PacketProcessor.kt b/Server/src/main/core/net/packet/PacketProcessor.kt new file mode 100644 index 000000000..c05798490 --- /dev/null +++ b/Server/src/main/core/net/packet/PacketProcessor.kt @@ -0,0 +1,844 @@ +package core.net.packet + +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.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.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.* +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.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 { + private var queue = LinkedList() + private var processingQueue = LinkedList() + private val queueLock = Any() + + @JvmStatic fun enqueue(pkt: Packet) { + synchronized(queueLock) { + queue.addLast(pkt) + } + } + + @JvmStatic fun processQueue() { + synchronized(queueLock) { + if (queue.isEmpty()) { + return + } + val queued = queue + queue = processingQueue + processingQueue = queued + } + val sw = StringWriter() + val pw = PrintWriter(sw) + var pkt: Packet + 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) + log(this::class.java, Log.ERR, "Error Processing ${pkt::class.java.simpleName}: $sw") + } + } + } + + private fun process(pkt: Packet) { + when (pkt) { + is Packet.ItemAction -> processItemAction(pkt) + is Packet.NpcAction -> processNpcAction(pkt) + is Packet.SceneryAction -> processSceneryAction(pkt) + is Packet.PlayerAction -> processPlayerAction(pkt) + is Packet.GroundItemAction -> processGroundItemAction(pkt) + is Packet.UseWithNpc, + is Packet.UseWithItem, + is Packet.UseWithScenery, + is Packet.UseWithGroundItem, + is Packet.UseWithPlayer -> processUseWith(pkt) + is Packet.IfAction -> processIfAction(pkt) + is Packet.CloseIface -> processCloseIface(pkt) + is Packet.WorldspaceWalk, + is Packet.MinimapWalk, + is Packet.InteractWalk -> processWalkPacket(pkt) + is Packet.AddFriend -> CommunicationInfo.add(pkt.player, pkt.username) + is Packet.RemoveFriend -> CommunicationInfo.remove(pkt.player, pkt.username, false) + is Packet.AddIgnore -> CommunicationInfo.block(pkt.player, pkt.username) + is Packet.RemoveIgnore -> CommunicationInfo.remove(pkt.player, pkt.username, true) + is Packet.ComponentItemAction, + is Packet.ComponentPlayerAction, + is Packet.ComponentNpcAction, + is Packet.ComponentSceneryAction, + is Packet.ComponentGroundItemAction -> processComponentUseWith(pkt) + is Packet.SlotSwitchSingleComponent, + is Packet.SlotSwitchMultiComponent -> processSlotSwitch(pkt) + is Packet.TrackingMouseClick, + is Packet.TrackingAfkTimeout, + is Packet.TrackingFocus, + is Packet.TrackingCameraPos, + is Packet.TrackingDisplayUpdate -> processTrackingPacket(pkt) + is Packet.PlayerPrefsUpdate -> {/*TODO implement something that cares about this */} + is Packet.Ping -> pkt.player.session.lastPing = System.currentTimeMillis() + is Packet.JoinClan -> { + if (pkt.clanName.isEmpty() && pkt.player.communication.currentClan.isNotEmpty()) { + val builder = LeaveClanRequest.newBuilder() + builder.clanName = pkt.player.communication.currentClan + builder.username = pkt.player.name + ManagementEvents.publish(builder.build()) + return + } + sendMessage(pkt.player, "Attempting to join channel....:clan:") + val builder = JoinClanRequest.newBuilder() + builder.clanName = pkt.clanName + builder.username = pkt.player.name + ManagementEvents.publish(builder.build()) + } + is Packet.SetClanRank -> { + CommunicationInfo.updateClanRank(pkt.player, pkt.username, ClanRank.values()[pkt.rank + 1]) + } + is Packet.KickFromClan -> { + val clan = pkt.player.communication.clan ?: return + val target = Repository.getPlayerByName(pkt.username) ?: return + clan.kick(pkt.player, target) + } + is Packet.QuickChat -> QCRepository.sendQC(pkt.player, pkt.multiplier, pkt.offset, pkt.type, pkt.indexA, pkt.indexB, pkt.forClan) + is Packet.InputPromptResponse -> { + val script: ((Any) -> Boolean) = pkt.player.getAttribute<((Any) -> Boolean)?>("runscript", null) ?: return + if (pkt.player.locks.isInteractionLocked) + return + try { + RunScript.processInput(pkt.player, pkt.response, script) + } finally { + pkt.player.removeAttribute("parseamount") + pkt.player.removeAttribute("runscript") + } + } + 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 + } + if (pkt.target.equals(pkt.player.username, true)){ + pkt.player.sendMessage("You can't report yourself!") + } + core.game.global.report.AbuseReport( + pkt.player.name, + pkt.target, + core.game.global.report.Rule.forId(pkt.rule) + ).construct(pkt.player, pkt.modMute) + } + is Packet.TrackFinished -> { + if (pkt.player.musicPlayer.isLooping) + pkt.player.musicPlayer.replay() + else + pkt.player.musicPlayer.isPlaying = false + } + is Packet.GESetOfferItem -> { + val offer = pkt.player.getAttribute("ge-temp", GrandExchangeOffer()) + val index = pkt.player.getAttribute("ge-index", -1) + offer.itemID = pkt.itemId + offer.sell = false + if (!PriceIndex.canTrade(pkt.itemId)) { + sendMessage(pkt.player, "That item is blacklisted from the Grand Exchange.") + return + } + offer.player = pkt.player + offer.amount = 1 + offer.offeredValue = getRecommendedPrice(pkt.itemId, false) + offer.index = index + StockMarket.updateVarbits(pkt.player, offer, index, false) + pkt.player.setAttribute("ge-temp", offer) + pkt.player.packetDispatch.sendString(getOfferStats(pkt.itemId, false), 105, 142) + pkt.player.interfaceManager.closeChatbox() + } + is Packet.Command -> { + PlayerMonitor.log(pkt.player, LogType.COMMAND, pkt.commandLine) + CommandSystem.commandSystem.parse(pkt.player, pkt.commandLine) + } + is Packet.ChatMessage -> { + if (pkt.player.details.isMuted) + pkt.player.sendMessage("You have been muted due to breaking a rule.") + else { + 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(EntityFlag.Chat, ctx) + } + } + is Packet.ChatSetting -> { + MSPacketRepository.sendChatSetting(pkt.player, pkt.public, pkt.private, pkt.trade) + pkt.player.settings.updateChatSettings(pkt.public, pkt.private, pkt.trade) + } + is Packet.PrivateMessage -> { + if (pkt.player.details.isMuted) + pkt.player.sendMessage("You have been muted due to breaking a rule.") + else + CommunicationInfo.sendMessage(pkt.player, pkt.username, pkt.message) + PlayerMonitor.logPrivateChat(pkt.player, pkt.username, pkt.message) + } + is Packet.PacketCountUpdate -> { + val final = pkt.count - pkt.player.interfaceManager.getPacketCount(0) + pkt.player.interfaceManager.getPacketCount(final) + } + is Packet.ContinueOption -> { + val player = pkt.player + player.debug("[CONTINUE OPT]----------") + player.debug("Iface: ${pkt.iface}") + player.debug("Child: ${pkt.child}") + player.debug("Slot: ${pkt.slot}") + player.debug("------------------------") + if (player.dialogueInterpreter.dialogue == null) { + player.interfaceManager.closeChatbox() + player.dialogueInterpreter.actions.removeFirstOrNull()?.handle(player, pkt.child) + val component = player.interfaceManager.getComponent(pkt.iface) ?: return + if (!InterfaceListeners.run(player, component, pkt.opcode, pkt.child, pkt.slot, -1)) + component.plugin?.handle(player, component, pkt.opcode, pkt.child, pkt.slot, -1) + return + } + player.dialogueInterpreter.handle(pkt.iface, pkt.child) + } + is Packet.ItemExamine -> { + val def = ItemDefinition.forId(pkt.id) ?: return + pkt.player.debug("[ITEM] ID: ${pkt.id} Value: ${def.value} Model: ${def.interfaceModelId}") + pkt.player.sendMessage(def.examine) + } + is Packet.SceneryExamine -> { + val def = SceneryDefinition.forId(pkt.id) ?: return + pkt.player.debug("[SCENERY]---------------------") + pkt.player.debug("ID: ${pkt.id}") + if (def.configFile != null) + pkt.player.debug("Varbit: ${def.configFile.id}") + pkt.player.debug("------------------------------") + pkt.player.sendMessage(def.examine) + } + is Packet.NpcExamine -> { + val def = NPCDefinition.forId(pkt.id) + pkt.player.debug("[NPC]-------------------------") + pkt.player.debug("ID: ${pkt.id}") + if (def.configFileId != -1) + pkt.player.debug("Varbit: ${def.configFileId}") + pkt.player.debug("------------------------------") + pkt.player.sendMessage(def.examine) + } + + + else -> log(this::class.java, Log.WARN, "Unprocessed Packet: ${pkt::class.java.simpleName}") + } + } + + private fun processTrackingPacket(pkt: Packet) { + when(pkt) { + is Packet.TrackingFocus -> {} + is Packet.TrackingDisplayUpdate -> { + pkt.player.session.clientInfo.screenWidth = pkt.screenWidth + pkt.player.session.clientInfo.screenHeight = pkt.screenHeight + pkt.player.session.clientInfo.displayMode = pkt.displayMode + pkt.player.interfaceManager.switchWindowMode(pkt.windowMode) + } + 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 + } + is Packet.TrackingMouseClick -> { + //TODO see above todo + + } + else -> {} + } + } + + private fun processSlotSwitch(pkt: Packet) { + //TODO refactor this to function as callbacks to InterfaceListeners, e.g. onSlotSwitch + //TODO See above TODO, I hate Arios. This was hardcoded in the packet decoder. + if (pkt is Packet.SlotSwitchMultiComponent) { + if (pkt.destIface == 762) { + if (pkt.destChild == 73) { + val container = pkt.player.bank + switchItem(pkt.sourceSlot, pkt.destSlot, container, pkt.player.bank.isInsertItems, pkt.player) + } else { + val tabIndex = BankContainer.getArrayIndex(pkt.destChild) + if (tabIndex > -1) { + val secondSlot = + if (tabIndex == 10) pkt.player.bank.freeSlot() else pkt.player.bank.tabStartSlot[tabIndex] + pkt.player.bank.getItemsInTab(tabIndex) + val inSlot: Item = pkt.player.bank.get(pkt.sourceSlot) + if (secondSlot == -1 && pkt.player.bank.remove(inSlot)) { + pkt.player.bank.add(inSlot) + return + } + val childId = pkt.player.bank.getTabByItemSlot(pkt.sourceSlot) + if (secondSlot > pkt.sourceSlot) { + pkt.player.bank.insert(pkt.sourceSlot, secondSlot - 1) + } else if (pkt.sourceSlot > secondSlot) { + pkt.player.bank.insert(pkt.sourceSlot, secondSlot) + } + pkt.player.bank.increaseTabStartSlots(tabIndex) + pkt.player.bank.decreaseTabStartSlots(childId) + pkt.player.bank.setTabConfigurations() + return + } + } + return + } else { + switchItem(pkt.sourceSlot, pkt.destSlot, pkt.player.inventory, false, pkt.player) + } + } + else if (pkt is Packet.SlotSwitchSingleComponent) { + val container = if (pkt.iface == 762) pkt.player.bank else pkt.player.inventory + switchItem(pkt.sourceSlot, pkt.destSlot, container, pkt.isInsert, pkt.player) + } + } + + private fun processComponentUseWith(pkt: Packet) { + val iface: Int + val child: Int + val target: Node + val targetId: Int + val player: Player + val type: Int + if (pkt is Packet.ComponentGroundItemAction) { + player = pkt.player + child = pkt.child + iface = pkt.iface + target = GroundItemManager.get(pkt.itemId, Location.create(pkt.x, pkt.y, player.location.z), player) ?: return sendClearMinimap(player).also { sendMessage(player, "Too late!") } + targetId = pkt.itemId + type = SpellListener.GROUND_ITEM + } + else if (pkt is Packet.ComponentPlayerAction) { + player = pkt.player + child = pkt.child + iface = pkt.iface + target = Repository.players[pkt.otherIndex] ?: return sendClearMinimap(player) + targetId = target.id + type = SpellListener.PLAYER + } + else if (pkt is Packet.ComponentSceneryAction) { + player = pkt.player + child = pkt.child + iface = pkt.iface + target = RegionManager.getObject(player.location.z, pkt.x, pkt.y) ?: return sendClearMinimap(player) + targetId = pkt.sceneryId + type = SpellListener.OBJECT + } + else if (pkt is Packet.ComponentNpcAction) { + if (pkt.npcIndex !in 1 until ServerConstants.MAX_NPCS) + return sendClearMinimap(pkt.player) + player = pkt.player + child = pkt.child + iface = pkt.iface + target = Repository.npcs[pkt.npcIndex] ?: return sendClearMinimap(player) + targetId = target.id + type = SpellListener.NPC + } + else { + if (pkt !is Packet.ComponentItemAction) return + player = pkt.player + child = pkt.child + iface = pkt.iface + target = player.inventory[pkt.slot] ?: return + targetId = pkt.itemId + type = SpellListener.ITEM + } + + if (targetId != target.id) + 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) + when (iface) { + 430,192,193 -> MagicSpell.castSpell(player, SpellBookManager.SpellBook.forInterface(iface), child, target) + 662 -> { + if (player.familiarManager.hasFamiliar()) + player.familiarManager.familiar.executeSpecialMove(FamiliarSpecial(target, iface, child, target as? Item)) + else + player.sendMessage("You don't have a familiar.") + } + } + } + + private fun processWalkPacket(pkt: Packet) { + val player: Player + val x: Int + val y: Int + val isRunning: Boolean + if (pkt is Packet.WorldspaceWalk) { + player = pkt.player + x = pkt.destX + y = pkt.destY + isRunning = pkt.isRun + } + else if (pkt is Packet.InteractWalk) { + player = pkt.player + x = pkt.destX + y = pkt.destY + isRunning = pkt.isRun + } + else { + if (pkt !is Packet.MinimapWalk) return + player = pkt.player + x = pkt.destX + y = pkt.destY + isRunning = pkt.isRun + //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]-- Action canceled. Either player is locked, interfaces can't close, or distance is beyond server pathfinding limit.") + return sendClearMinimap(player) + } + + if (pkt is Packet.InteractWalk) { + //Arios did this, so we're replicating it for now. Probably wrong. + player.walkingQueue.isRunning = isRunning + return + } + + player.face(null) + player.faceLocation(null) + player.scripts.reset() + + player.pulseManager.run(object : MovementPulse(player, loc, isRunning) { + override fun pulse(): Boolean { + if (isRunning) + player.walkingQueue.isRunning = false + return true + } + }) + } + + private fun processCloseIface(pkt: Packet.CloseIface) { + val player = pkt.player + player.interfaceManager.close() + if (player.getAttribute("logging_in") != null) { + GameWorld.Pulser.submit(object : Pulse() { + override fun pulse(): Boolean { + player.removeAttribute("logging_in") + LoginConfiguration.configureGameWorld(player) + return true + } + }) + } + if (player.getAttribute("worldMap:viewing") != null) { + player.removeAttribute("worldMap:viewing") + player.packetDispatch.sendWindowsPane(if (player.interfaceManager.isResizable) 746 else 548, 2) + player.unlock() + } + } + + private fun processIfAction(pkt: Packet.IfAction) { + val player = pkt.player + player.debug("[IF ACTION]--------------------------") + player.debug("Iface: ${pkt.iface}, Button: ${pkt.child}") + 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 && pkt.iface != 746) + player.dialogueInterpreter.close() + if (player.locks.isComponentLocked) + return + if (player.zoneMonitor.clickButton(pkt.iface, pkt.child, pkt.slot, pkt.itemId, pkt.opcode)) + return + val c = player.interfaceManager.getComponent(pkt.iface) ?: return + if (c.isHidden) + return + val plugin = c.plugin + player.dispatch(ButtonClickEvent(c.id, pkt.child)) + if (!InterfaceListeners.run(player, c, pkt.opcode, pkt.child, pkt.slot, pkt.itemId)) + plugin?.handle(player, c, pkt.opcode, pkt.child, pkt.slot, pkt.itemId) + } + + private fun processUseWith(pkt: Packet) { + val node: Node + var childNode: Node? = null + val item: Item + val itemId: Int + val nodeId: Int + val type: IntType + val player: Player + if (pkt is Packet.UseWithNpc) { + val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return sendClearMinimap(pkt.player) + itemId = pkt.itemId + val itemSlot = pkt.slot + item = container[itemSlot] ?: return sendClearMinimap(pkt.player) + node = Repository.npcs[pkt.npcIndex] ?: return sendClearMinimap(pkt.player) + childNode = node.getShownNPC(pkt.player) + nodeId = node.id + type = IntType.NPC + player = pkt.player + } + else if (pkt is Packet.UseWithScenery) { + item = pkt.player.inventory[pkt.slot] ?: return sendClearMinimap(pkt.player) + node = RegionManager.getObject(pkt.player.location.z, pkt.x, pkt.y) ?: return sendClearMinimap(pkt.player) + childNode = node.asScenery().getChild(pkt.player) + itemId = pkt.itemId + nodeId = node.id + type = IntType.SCENERY + player = pkt.player + } + else if (pkt is Packet.UseWithItem) { + val containerUsed = getLikelyContainerForIface(pkt.player, pkt.usedIface) ?: return + val containerWith = getLikelyContainerForIface(pkt.player, pkt.usedWithIface) ?: return + item = containerUsed[pkt.usedSlot] ?: return + node = containerWith[pkt.usedWithSlot] ?: return + itemId = pkt.usedId + nodeId = pkt.usedWithId + type = IntType.ITEM + player = pkt.player + } else if (pkt is Packet.UseWithGroundItem) { + val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return + 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) ?: return + nodeId = pkt.withId + type = IntType.GROUNDITEM + player = pkt.player + } else { + if (pkt !is Packet.UseWithPlayer) return + val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return sendClearMinimap(pkt.player) + item = container[pkt.slot] ?: return sendClearMinimap(pkt.player) + itemId = pkt.itemId + node = Repository.players[pkt.otherIndex] ?: return sendClearMinimap(pkt.player) + type = IntType.PLAYER + nodeId = node.id + player = pkt.player + } + + if (item.id != itemId) + return sendClearMinimap(player) + 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)) + return + if (childNode != null && childNode.id != node.id) { + if (InteractionListeners.run(item, childNode, type, player)) + return + } + val flipped = type == IntType.ITEM && item.id < node.id + val event = if (flipped) + NodeUsageEvent(player, 0, node, item) + else + NodeUsageEvent(player, 0, item, childNode ?: node) + if (PluginInteractionManager.handle(player, event)) + return + UseWithHandler.run(event) + } + + 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) { + InteractPlugin.handleInvalidInteraction(player, item, Option.NULL) + return sendClearMinimap(player) + } + if (PluginInteractionManager.handle(player, item, option)) + return + if (InteractionListeners.run(item.id, IntType.GROUNDITEM, option.name, player, item)) + return + if (InteractionListeners.run(item.id, IntType.ITEM, option.name, player, item)) + return + item.interaction.handle(player, option) + } + + private fun processPlayerAction(pkt: Packet.PlayerAction) { + val player = pkt.player + 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) + val option = other.interaction[pkt.optIndex] ?: return sendClearMinimap(player) + if (!InteractionListeners.run(-1, IntType.PLAYER, option.name.lowercase(), player, other)) { + other.interaction.handle(player, option) + } + } + + private fun processSceneryAction(pkt: Packet.SceneryAction) { + val player = pkt.player + var scenery = RegionManager.getObject(player.location.z, pkt.x, pkt.y, pkt.id) + var objId = pkt.id + + //what follows is a series of hardcoded crimes against humanity + if (pkt.id == 6898) + scenery = Scenery(6898, Location(3219, 9618)) + 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) { + scenery = Scenery(objId - 1, Location(pkt.x, pkt.y, player.location.z)) + objId -= 1 + } + + if (scenery == null || scenery.id != objId || !scenery.isActive) { + player.debug("[SCENERY INTERACT] NULL OR MISMATCH OR INACTIVE") + InteractPlugin.handleInvalidInteraction(player, scenery, Option.NULL) + return sendClearMinimap(player) + } + + val wrapperChild = scenery.getChild(player) + val option = wrapperChild.interaction[pkt.optIndex] + + if (option == null) { + player.debug("[SCENERY INTERACT] NULL OPTION") + InteractPlugin.handleInvalidInteraction(player, scenery, Option.NULL) + return sendClearMinimap(player) + } + + val hasWrapper = wrapperChild.id != scenery.id + + player.debug("[SCENERY INTERACT]------------------------------") + player.debug("ID: ${wrapperChild.id}, Option: ${option.name}[${option.index}]") + player.debug("Loc: ${scenery.location}, Dir: ${scenery.direction}") + if (hasWrapper) { + player.debug("WrapperID: ${scenery.id}, ${scenery.definition.configFile?.let { "Varbit: ${it.id}"} ?: "Varp: ${scenery.definition.configId}"}") + } + player.debug("------------------------------------------------") + + player.scripts.reset() + if (InteractionListeners.run(wrapperChild.id, IntType.SCENERY, option.name, player, wrapperChild)) + return + if (PluginInteractionManager.handle(player, wrapperChild)) + return + wrapperChild.interaction.handle(player, option) + } + + private fun processNpcAction(pkt: Packet.NpcAction) { + if (pkt.npcIndex !in 1 until ServerConstants.MAX_NPCS) + return sendClearMinimap(pkt.player) + val npc = Repository.npcs[pkt.npcIndex] ?: return sendClearMinimap(pkt.player) + + val wrapperChild = npc.getShownNPC(pkt.player) + val option = wrapperChild.interaction[pkt.optIndex] + + if (option == null) { + InteractPlugin.handleInvalidInteraction(pkt.player, npc, Option.NULL) + return sendClearMinimap(pkt.player) + } + + val hasWrapper = wrapperChild.id != npc.id + + pkt.player.debug("[NPC INTERACT]-------------------") + pkt.player.debug("ID: ${wrapperChild.id}, Index: ${pkt.npcIndex}") + pkt.player.debug("Option: ${option.name}[${option.index}]") + pkt.player.debug("SpawnLoc: ${npc.properties.spawnLocation}") + if (hasWrapper) { + pkt.player.debug("WrapperID: ${npc.id}, Varbit: ${npc.definition.configFileId}") + } + 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)) + return + npc.interaction.handle(pkt.player, option) + } + + private fun processItemAction(pkt: Packet.ItemAction) { + val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return + if (pkt.slot !in 0 until container.capacity()) + return + val item = container.get(pkt.slot) ?: return + if (item.id != pkt.itemId) + return + val option = item.interaction[pkt.optIndex] ?: return + 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}") + } + + private fun getLikelyContainerForIface(player: Player, iface: Int) : Container? { + return when (iface) { + Components.INVENTORY_149 -> player.inventory + Components.BANK_V2_MAIN_762 -> player.bank + Components.EQUIP_SCREEN2_667 -> player.equipment + else -> null + } + } + + private fun sendClearMinimap(player: Player) { + PacketRepository.send(ClearMinimapFlag::class.java, PlayerContext(player)) + } + + fun switchItem(slot: Int, secondSlot: Int, container: Container?, insert: Boolean, player: Player) { + if (container == null || slot < 0 || slot >= container.toArray().size || secondSlot < 0 || secondSlot >= container.toArray().size) { + return + } + val item = container[slot] + val second = container[secondSlot] + if (player.interfaceManager.hasChatbox() && !getAttribute(player, "close_c_", false)) { + player.interfaceManager.closeChatbox() + switchItem(secondSlot, slot, container, insert, player) + container.refresh() + return + } + if (item == null) { + return + } + if (!insert) { + container.replace(second, slot, false) + container.replace(item, secondSlot, false) + item.index = secondSlot + if (second != null) { + second.index = slot + } + } else { + container.insert(slot, secondSlot, false) + } + container.refresh() + } + + 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 new file mode 100644 index 000000000..e2656b02a --- /dev/null +++ b/Server/src/main/core/net/packet/PacketRepository.java @@ -0,0 +1,109 @@ +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. + * @author Emperor + */ +public final class PacketRepository { + + /** + * The outgoing packets mapping. + */ + public final static Map, OutgoingPacket> OUTGOING_PACKETS = new HashMap<>(); + + /** + * Populate the mappings. + */ + static { + OUTGOING_PACKETS.put(LoginPacket.class, new LoginPacket()); // + OUTGOING_PACKETS.put(UpdateSceneGraph.class, new UpdateSceneGraph()); // + OUTGOING_PACKETS.put(WindowsPane.class, new WindowsPane()); // + OUTGOING_PACKETS.put(Interface.class, new Interface()); // + OUTGOING_PACKETS.put(SkillLevel.class, new SkillLevel()); // + OUTGOING_PACKETS.put(Config.class, new Config()); // + OUTGOING_PACKETS.put(AccessMask.class, new AccessMask()); // + OUTGOING_PACKETS.put(GameMessage.class, new GameMessage()); // + OUTGOING_PACKETS.put(RunScriptPacket.class, new RunScriptPacket()); // + OUTGOING_PACKETS.put(RunEnergy.class, new RunEnergy()); // + OUTGOING_PACKETS.put(ContainerPacket.class, new ContainerPacket()); // + OUTGOING_PACKETS.put(StringPacket.class, new StringPacket()); // + OUTGOING_PACKETS.put(Logout.class, new Logout()); // + OUTGOING_PACKETS.put(CloseInterface.class, new CloseInterface()); // + OUTGOING_PACKETS.put(AnimateInterface.class, new AnimateInterface()); // + OUTGOING_PACKETS.put(DisplayModel.class, new DisplayModel()); // + OUTGOING_PACKETS.put(InterfaceConfig.class, new InterfaceConfig()); // + OUTGOING_PACKETS.put(PingPacket.class, new PingPacket()); // + OUTGOING_PACKETS.put(UpdateAreaPosition.class, new UpdateAreaPosition()); // + OUTGOING_PACKETS.put(ConstructScenery.class, new ConstructScenery()); // + OUTGOING_PACKETS.put(ClearScenery.class, new ClearScenery()); // + OUTGOING_PACKETS.put(HintIcon.class, new HintIcon()); // + OUTGOING_PACKETS.put(ClearMinimapFlag.class, new ClearMinimapFlag()); // + OUTGOING_PACKETS.put(InteractionOption.class, new InteractionOption()); // + OUTGOING_PACKETS.put(SetWalkOption.class, new SetWalkOption()); // + OUTGOING_PACKETS.put(MinimapState.class, new MinimapState()); // + OUTGOING_PACKETS.put(ConstructGroundItem.class, new ConstructGroundItem()); // + OUTGOING_PACKETS.put(ClearGroundItem.class, new ClearGroundItem()); // + OUTGOING_PACKETS.put(RepositionChild.class, new RepositionChild()); // + OUTGOING_PACKETS.put(PositionedGraphic.class, new PositionedGraphic()); // + OUTGOING_PACKETS.put(SystemUpdatePacket.class, new SystemUpdatePacket()); // + OUTGOING_PACKETS.put(CameraViewPacket.class, new CameraViewPacket()); // + OUTGOING_PACKETS.put(MusicPacket.class, new MusicPacket()); // + OUTGOING_PACKETS.put(AudioPacket.class, new AudioPacket()); // + OUTGOING_PACKETS.put(GrandExchangePacket.class, new GrandExchangePacket()); // + OUTGOING_PACKETS.put(BuildDynamicScene.class, new BuildDynamicScene()); // + OUTGOING_PACKETS.put(AnimateObjectPacket.class, new AnimateObjectPacket()); // + OUTGOING_PACKETS.put(ClearRegionChunk.class, new ClearRegionChunk()); // + OUTGOING_PACKETS.put(ContactPackets.class, new ContactPackets()); // + 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(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()); + } + + /** + * Sends a new packet. + * @param clazz The class of the outgoing packet to send. + * @param context The context. + */ + @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) { + log(PacketRepository.class, Log.ERR, "Invalid outgoing packet [handler=" + clazz + ", context=" + context + "]."); + return; + } + if(!context.getPlayer().isArtificial()) { + 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 new file mode 100644 index 000000000..ed3de3d43 --- /dev/null +++ b/Server/src/main/core/net/packet/PacketWriteQueue.kt @@ -0,0 +1,78 @@ +package core.net.packet + +import core.api.log +import core.net.packet.out.* +import core.tools.Log +import core.tools.SystemLogger +import java.io.PrintWriter +import java.io.StringWriter +import java.util.* + +class PacketWriteQueue { + companion object { + private var packetsToWrite = LinkedList>() + private var packetsToFlush = LinkedList>() + private val queueLock = Any() + + @JvmStatic + fun handle(packet: OutgoingPacket, context: T) { + when (packet) { + //Dynamic packets need to be sent immediately + 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) + } + } + + @JvmStatic + fun push(packet: OutgoingPacket, context: T) { + 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() { + synchronized(queueLock) { + if (packetsToWrite.isEmpty()) { + return + } + val queued = packetsToWrite + packetsToWrite = packetsToFlush + packetsToFlush = queued + } + val sw = StringWriter() + val pw = PrintWriter(sw) + while (packetsToFlush.isNotEmpty()) { + val pkt = packetsToFlush.pollFirst() ?: continue + try { + write(pkt.out, pkt.context) + } catch (e: Exception) { + e.printStackTrace(pw) + log(this::class.java, Log.ERR, "Error flushing packet ${pkt.out::class.java}: $sw") + continue + } + } + } + + @Suppress("UNCHECKED_CAST") + fun write(out: OutgoingPacket<*>, context: T) { + val pack = out as? OutgoingPacket + val ctx = context as? T + if (pack == null || ctx == null) { + throw IllegalStateException("Failed packet casting") + } + pack.send(ctx) + } + } +} + +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 new file mode 100644 index 000000000..b2b1ccb2a --- /dev/null +++ b/Server/src/main/core/net/packet/QCRepository.kt @@ -0,0 +1,321 @@ +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 +import core.cache.def.impl.NPCDefinition +import core.cache.misc.buffer.ByteBufferUtils +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.* +import proto.management.ClanMessage +import core.game.world.GameWorld.Pulser +import core.net.packet.`in`.QCPacketType +import core.worker.ManagementEvents.publish +import java.nio.ByteBuffer +import java.util.* + + +/** + * Handles the sending of quick chat messages and string-replacement for specific messages. + * @author Ceikry + */ +object QCRepository { + private val quickChatIndex = Cache.getIndexes()[24] + + /** + * The entry method that connects to the other more specific methods + */ + @JvmStatic + fun sendQC(player: Player?, multiplier: Int?, offset: Int?, packetType: QCPacketType, selection_a_index: Int, selection_b_index: Int, forClan: Boolean){ + val index = getIndex(offset, multiplier) + player?.setAttribute("qc_offset", offset) //Slapping this in an attribute because it's only useful for one or two messages + val qcString = when(packetType){ + QCPacketType.SINGLE -> getSingleQC(index, selection_a_index) + QCPacketType.DOUBLE -> getDoubleQC(index, selection_a_index, selection_b_index) + QCPacketType.STANDARD -> getStandardQC(player, index) + else -> "" + } + + + if(forClan) { + val builder = ClanMessage.newBuilder() + builder.sender = player!!.name + builder.clanName = player.communication.clan.owner.lowercase(Locale.getDefault()).replace(" ", "_") + builder.message = qcString + builder.rank = player.details.rights.ordinal + publish(builder.build()) + } else { + val ctx = ChatMessage(player!!, qcString, 0, qcString.length) + ctx.isQuickChat = true + Pulser.submit(object : Pulse(0, player) { + override fun pulse(): Boolean { + player.updateMasks.register(EntityFlag.Chat, ctx) + return true + } + }) + } + } + + /** + * For standard quick chat messages with no string replacements + */ + @JvmStatic + fun getStandardQC(player: Player?, index: Int): String{ + var qcString = getQCString(index) + + //XP to next level + if (qcString.contains("to get my next")) { + val split = qcString.split(" ") + val skillName = split[split.size - 2] + val skill = Skills.getSkillByName(skillName.toUpperCase()) + val playerXP = player?.skills?.getExperience(skill) + val playerLevel = player?.skills?.getStaticLevel(skill) + val nextXP = player?.skills?.getExperienceByLevel(playerLevel?.plus(1) ?: 1) + qcString = qcString.replace("<", "${(nextXP?.minus(playerXP ?: 0.0) ?: 0.0).toInt()}") + } + + //My X level is + else if (qcString.contains("level is")) { + val skillName = qcString.split(" ")[1] + val skill = Skills.getSkillByName(skillName.toUpperCase()) + val level = player?.skills?.getStaticLevel(skill) + qcString = qcString.replace("<", level.toString()) + } + + //My current slayer assignment is + else if(qcString.contains("My current Slayer assignment is")){ + val amount = SlayerManager.getInstance(player!!).amount + val taskName = getSlayerTask(player)?.name?.toLowerCase() ?: "None" + if(amount ?: 0 > 0){ + qcString = qcString.replace("complete", "$amount $taskName") + } + } + + return qcString + } + + + /** + * For Single-replacement quick chat messages with one replacement selection + */ + @JvmStatic + fun getSingleQC(index: Int, selectionIndex: Int): String{ + var qcString = getQCString(index) + + //Messages that include items + if ((qcString.contains("item") || qcString.contains("Can I buy your") || qcString.contains("What is the best world to buy") || qcString.contains("What is the best world to sell") || qcString.contains("Would you like to borrow") || qcString.contains("Could I please borrow") ) && qcString.contains("<") ){ + val itemName = ItemDefinition.forId(selectionIndex).name + qcString = qcString.replace("<", itemName) + } + + //Loan duration + else if (qcString.contains("I'd like the loan duration to be")){ + qcString = qcString.replace("<", getFromMap(1642, selectionIndex)) + } + + //Go to agility course / Try training your agility at + else if (qcString.contains("Let's go to Agility course:") || qcString.contains("Try training your Agility at:")){ + qcString = qcString.replace("<", getFromMap(1505, selectionIndex)) + } + + //Try training on + else if(qcString.contains("Try training on")){ + qcString = qcString.replace("<", getFromMap(1524, selectionIndex)) + } + + //Try ranging + else if(qcString.contains("Try ranging")){ + qcString = qcString.replace("<", getFromMap(1525, selectionIndex)) + } + + //flat-pack + else if(qcString.contains("flat-pack")){ + qcString = qcString.replace("<", getFromMap(1490, selectionIndex)) + } + + //Cooking + else if(qcString.contains("I'm cooking") || qcString.contains("Would you please cook me") || qcString.contains("Try cooking")){ + qcString = qcString.replace("<", getFromMap(1492, selectionIndex)) + } + + //Crafting + else if(qcString.contains("I am crafting") || qcString.contains("Try crafting") || qcString.contains("Would you please craft me")){ + qcString = qcString.replace("<", getFromMap(1493, selectionIndex)) + } + + //Farming + else if(qcString.contains("I'm growing crop") || qcString.contains("Try growing crop")){ + qcString = qcString.replace("<", getFromMap(1494, selectionIndex)) + } + + //I'm burning logs... + else if(qcString.contains("I'm burning logs")){ + qcString = qcString.replace("<", getFromMap(1495, selectionIndex)) + } + + //Try burning logs at + else if(qcString.contains("Try burning logs at")){ + qcString = qcString.replace("<", getFromMap(1544, selectionIndex)) + } + + //Fishing + else if(qcString.contains("I'm fishing") || qcString.contains("Would you please fish me") || qcString.contains("Try fishing for")){ + qcString = qcString.replace("<", getFromMap(1491, selectionIndex)) + } + + //Fletching + else if(qcString.contains("I'm fletching") || qcString.contains("Try fletching") || qcString.contains("Would you please fetch me")){ + qcString = qcString.replace("<", getFromMap(1496, selectionIndex)) + } + + //Herblore + else if(qcString.contains("I'm mixing potion") || qcString.contains("Would you please mix me a potion") || qcString.contains("Try mixing potion")){ + qcString = qcString.replace("<", getFromMap(1526, selectionIndex)) + } + + //Herblore secondaries + else if(qcString.contains("Where can I get the secondary ingredient")){ + qcString = qcString.replace("<", getFromMap(1516, selectionIndex)) + } + + //Hunter + else if(qcString.contains("Would you please hunt me") || qcString.contains("Try hunting")){ + qcString = qcString.replace("<", getFromMap(1543, selectionIndex)) + } + + //Casting spell + else if(qcString.contains("I'm casting spell") || qcString.contains("Would you please cast")){ + qcString = qcString.replace("<", getFromMap(1527, selectionIndex)) + } + + //Spellbook + else if(qcString.contains("I am on spell book")){ + qcString = qcString.replace("<", getFromMap(1528, selectionIndex)) + } + + //Mining ore + else if(qcString.contains("I'm mining ore")){ + qcString = qcString.replace("<", getFromMap(1529, selectionIndex)) + } + + //Pickaxe using + else if(qcString.contains("I'm using a pick")){ + qcString = qcString.replace("<", getFromMap(1531, selectionIndex)) + } + + //Try mining at + else if(qcString.contains("Try mining at")){ + qcString = qcString.replace("<", getFromMap(1533, selectionIndex)) + } + + //Use your prayer + else if(qcString.contains("Use your prayer")){ + qcString = qcString.replace("<", getFromMap(1534, selectionIndex)) + } + + //i'm going to craft rune + else if(qcString.contains("I'm going to craft rune")){ + qcString = qcString.replace("<", getFromMap(1535, selectionIndex)) + } + + //try crafting runes at + else if(qcString.contains("Try crafting runes at")){ + qcString = qcString.replace("<", getFromMap(1536, selectionIndex)) + } + + //Slayer master in + else if(qcString.contains("You should use the Slayer master in")){ + qcString = qcString.replace("<", getFromMap(2139, selectionIndex)) + } + + //Spare slayer equipment + else if(qcString.contains("Do you have spare Slayer equipment")){ + qcString = qcString.replace("<", getFromMap(1538, selectionIndex)) + } + + //familiars + else if(qcString.contains("I like the familiar") || qcString.contains("I can summon up to")){ + qcString = qcString.replace("<", getFromMap(1539, selectionIndex)) + } + + //charm droppers + else if(qcString.contains("Good charm droppers are")){ + qcString = qcString.replace("<", getFromMap(1499, selectionIndex)) + } + + //Thieving + else if(qcString.contains("Try thieving from")){ + qcString = qcString.replace("<", getFromMap(1540, selectionIndex)) + } + + //Axe made of + else if(qcString.contains("I'm using a woodcutting axe made")){ + qcString = qcString.replace("<", getFromMap(1532, selectionIndex)) + } + + //Try training woodcutting at + else if(qcString.contains("Try training Woodcutting at")){ + qcString = qcString.replace("<", getFromMap(1500, selectionIndex)) + } + + //Nice level in + else if(qcString.contains("Nice level in")){ + qcString = qcString.replace("<", getFromMap(1517, selectionIndex)) + } + + + return qcString + } + + fun getFromMap(map: Int, index: Int): String + { + return DataMap.get(map).getString(index) + } + + /** + * For Double-replacement quick chat messages with 2 selection replacements. + */ + @JvmStatic + fun getDoubleQC(index: Int, selection_a_index: Int, selection_b_index: Int): String{ + var qcString = getQCString(index) + + //Giving directions: That is _ of _ + if(qcString.contains("That is < of <")){ + qcString = qcString.replaceFirst("<", getFromMap(1502, selection_a_index)) + qcString = qcString.replaceFirst("<", getFromMap(1504, selection_b_index)) + } + + //Smithing + else if(qcString.contains("I am smithing") || qcString.contains("Try smithing") || qcString.contains("Would you please smith me")){ + qcString = qcString.replaceFirst("<", getFromMap(1530, selection_a_index)) + qcString = qcString.replaceFirst("<", getFromMap(1498, selection_b_index)) + } + + return qcString + } + + private fun getIndex(offset: Int?, multiplier: Int?): Int{ + offset ?: return 0 + multiplier ?: return 0 + //for some reason it subtracts only from 512 if it's negative (Jagex idk) or 256 if type is 0 but not from 768 if type is 3? + return if(offset >= 0) { + (256 * multiplier) + offset + }else { + when(multiplier){ + 0 -> 256 + offset + 1 -> 512 + offset + 2 -> 768 + offset + else -> 1024 + offset + } + } + } + + private fun getQCString(index: Int): String{ + return ByteBufferUtils.getString(ByteBuffer.wrap(quickChatIndex.getFileData(1, index))) + } +} diff --git a/Server/src/main/java/core/net/packet/context/AccessMaskContext.java b/Server/src/main/core/net/packet/context/AccessMaskContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/AccessMaskContext.java rename to Server/src/main/core/net/packet/context/AccessMaskContext.java diff --git a/Server/src/main/java/core/net/packet/context/AnimateInterfaceContext.java b/Server/src/main/core/net/packet/context/AnimateInterfaceContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/AnimateInterfaceContext.java rename to Server/src/main/core/net/packet/context/AnimateInterfaceContext.java diff --git a/Server/src/main/java/core/net/packet/context/AnimateObjectContext.java b/Server/src/main/core/net/packet/context/AnimateObjectContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/AnimateObjectContext.java rename to Server/src/main/core/net/packet/context/AnimateObjectContext.java diff --git a/Server/src/main/java/core/net/packet/context/AreaPositionContext.java b/Server/src/main/core/net/packet/context/AreaPositionContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/AreaPositionContext.java rename to Server/src/main/core/net/packet/context/AreaPositionContext.java diff --git a/Server/src/main/java/core/net/packet/context/BuildItemContext.java b/Server/src/main/core/net/packet/context/BuildItemContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/BuildItemContext.java rename to Server/src/main/core/net/packet/context/BuildItemContext.java diff --git a/Server/src/main/java/core/net/packet/context/BuildSceneryContext.java b/Server/src/main/core/net/packet/context/BuildSceneryContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/BuildSceneryContext.java rename to Server/src/main/core/net/packet/context/BuildSceneryContext.java diff --git a/Server/src/main/java/core/net/packet/context/CSConfigContext.java b/Server/src/main/core/net/packet/context/CSConfigContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/CSConfigContext.java rename to Server/src/main/core/net/packet/context/CSConfigContext.java diff --git a/Server/src/main/java/core/net/packet/context/CameraContext.java b/Server/src/main/core/net/packet/context/CameraContext.java similarity index 98% rename from Server/src/main/java/core/net/packet/context/CameraContext.java rename to Server/src/main/core/net/packet/context/CameraContext.java index b125fae0b..6858774a0 100644 --- a/Server/src/main/java/core/net/packet/context/CameraContext.java +++ b/Server/src/main/core/net/packet/context/CameraContext.java @@ -1,7 +1,6 @@ package core.net.packet.context; import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; import core.net.packet.Context; /** diff --git a/Server/src/main/java/core/net/packet/context/ChildPositionContext.java b/Server/src/main/core/net/packet/context/ChildPositionContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/ChildPositionContext.java rename to Server/src/main/core/net/packet/context/ChildPositionContext.java diff --git a/Server/src/main/java/core/net/packet/context/ClanContext.java b/Server/src/main/core/net/packet/context/ClanContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/ClanContext.java rename to Server/src/main/core/net/packet/context/ClanContext.java diff --git a/Server/src/main/java/core/net/packet/context/ClearChunkContext.java b/Server/src/main/core/net/packet/context/ClearChunkContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/ClearChunkContext.java rename to Server/src/main/core/net/packet/context/ClearChunkContext.java diff --git a/Server/src/main/java/core/net/packet/context/ConfigContext.java b/Server/src/main/core/net/packet/context/ConfigContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/ConfigContext.java rename to Server/src/main/core/net/packet/context/ConfigContext.java diff --git a/Server/src/main/java/core/net/packet/context/ContactContext.java b/Server/src/main/core/net/packet/context/ContactContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/ContactContext.java rename to Server/src/main/core/net/packet/context/ContactContext.java diff --git a/Server/src/main/java/core/net/packet/context/ContainerContext.java b/Server/src/main/core/net/packet/context/ContainerContext.java similarity index 77% rename from Server/src/main/java/core/net/packet/context/ContainerContext.java rename to Server/src/main/core/net/packet/context/ContainerContext.java index 0a8d2de95..411a812cd 100644 --- a/Server/src/main/java/core/net/packet/context/ContainerContext.java +++ b/Server/src/main/core/net/packet/context/ContainerContext.java @@ -29,12 +29,14 @@ public final class ContainerContext implements Context { /** * The container type. */ - private final int type; + private final int containerId; /** * The items. */ - private final Item[] items; + private Item[] items; + + public int[] ids; /** * The length of the array to send. @@ -73,12 +75,12 @@ public final class ContainerContext implements Context { * @param player The player. * @param interfaceId The interface id. * @param childId The child id. - * @param type The container type. + * @param containerId The container type. * @param container The container. * @param split If the container should be split. */ - public ContainerContext(Player player, int interfaceId, int childId, int type, Container container, boolean split) { - this(player, interfaceId, childId, type, container.toArray(), container.toArray().length, split); + public ContainerContext(Player player, int interfaceId, int childId, int containerId, Container container, boolean split) { + this(player, interfaceId, childId, containerId, container.toArray(), container.toArray().length, split); } /** @@ -86,12 +88,12 @@ public final class ContainerContext implements Context { * @param player The player. * @param interfaceId The interface id. * @param childId The child id. - * @param type The container type. + * @param containerId The container type. * @param items The items. * @param split If the container should be split. */ - public ContainerContext(Player player, int interfaceId, int childId, int type, Item[] items, boolean split) { - this(player, interfaceId, childId, type, items, items.length, split); + public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, boolean split) { + this(player, interfaceId, childId, containerId, items, items.length, split); } /** @@ -99,37 +101,48 @@ public final class ContainerContext implements Context { * @param player The player. * @param interfaceId The interface id. * @param childId The child id. - * @param type The container type. + * @param containerId The container containerId. * @param items The items. * @param length The length. * @param split If the container should be split. */ - public ContainerContext(Player player, int interfaceId, int childId, int type, Item[] items, int length, boolean split) { + public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, int length, boolean split) { this.player = player; this.interfaceId = interfaceId; this.childId = childId; - this.type = type; + this.containerId = containerId; this.items = items; this.length = length; this.split = split; 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. * @param interfaceId The interface id. * @param childId The child id. - * @param type The container type. + * @param containerId The container containerId. * @param items The items. * @param split If the container should be split. * @param slots The slots to update. */ - public ContainerContext(Player player, int interfaceId, int childId, int type, Item[] items, boolean split, int... slots) { + public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, boolean split, int... slots) { this.player = player; this.interfaceId = interfaceId; this.childId = childId; - this.type = type; + this.containerId = containerId; this.items = items; this.length = items.length; this.split = split; @@ -153,8 +166,8 @@ public final class ContainerContext implements Context { * Gets the type. * @return The type. */ - public int getType() { - return type; + public int getContainerId() { + return containerId; } /** diff --git a/Server/src/main/java/core/net/packet/context/DefaultContext.java b/Server/src/main/core/net/packet/context/DefaultContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/DefaultContext.java rename to Server/src/main/core/net/packet/context/DefaultContext.java diff --git a/Server/src/main/java/core/net/packet/context/DisplayModelContext.java b/Server/src/main/core/net/packet/context/DisplayModelContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/DisplayModelContext.java rename to Server/src/main/core/net/packet/context/DisplayModelContext.java diff --git a/Server/src/main/java/core/net/packet/context/DynamicSceneContext.java b/Server/src/main/core/net/packet/context/DynamicSceneContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/DynamicSceneContext.java rename to Server/src/main/core/net/packet/context/DynamicSceneContext.java diff --git a/Server/src/main/java/core/net/packet/context/GameMessageContext.java b/Server/src/main/core/net/packet/context/GameMessageContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/GameMessageContext.java rename to Server/src/main/core/net/packet/context/GameMessageContext.java diff --git a/Server/src/main/java/core/net/packet/context/GrandExchangeContext.java b/Server/src/main/core/net/packet/context/GrandExchangeContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/GrandExchangeContext.java rename to Server/src/main/core/net/packet/context/GrandExchangeContext.java diff --git a/Server/src/main/java/core/net/packet/context/HintIconContext.java b/Server/src/main/core/net/packet/context/HintIconContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/HintIconContext.java rename to Server/src/main/core/net/packet/context/HintIconContext.java diff --git a/Server/src/main/java/core/net/packet/context/IntegerContext.java b/Server/src/main/core/net/packet/context/IntegerContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/IntegerContext.java rename to Server/src/main/core/net/packet/context/IntegerContext.java diff --git a/Server/src/main/java/core/net/packet/context/InteractionOptionContext.java b/Server/src/main/core/net/packet/context/InteractionOptionContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/InteractionOptionContext.java rename to Server/src/main/core/net/packet/context/InteractionOptionContext.java diff --git a/Server/src/main/java/core/net/packet/context/InterfaceConfigContext.java b/Server/src/main/core/net/packet/context/InterfaceConfigContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/InterfaceConfigContext.java rename to Server/src/main/core/net/packet/context/InterfaceConfigContext.java diff --git a/Server/src/main/java/core/net/packet/context/InterfaceContext.java b/Server/src/main/core/net/packet/context/InterfaceContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/InterfaceContext.java rename to Server/src/main/core/net/packet/context/InterfaceContext.java diff --git a/Server/src/main/java/core/net/packet/context/LocationContext.java b/Server/src/main/core/net/packet/context/LocationContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/LocationContext.java rename to Server/src/main/core/net/packet/context/LocationContext.java diff --git a/Server/src/main/java/core/net/packet/context/MessageContext.java b/Server/src/main/core/net/packet/context/MessageContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/MessageContext.java rename to Server/src/main/core/net/packet/context/MessageContext.java diff --git a/Server/src/main/java/core/net/packet/context/MinimapStateContext.java b/Server/src/main/core/net/packet/context/MinimapStateContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/MinimapStateContext.java rename to Server/src/main/core/net/packet/context/MinimapStateContext.java diff --git a/Server/src/main/java/core/net/packet/context/MusicContext.java b/Server/src/main/core/net/packet/context/MusicContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/MusicContext.java rename to Server/src/main/core/net/packet/context/MusicContext.java diff --git a/Server/src/main/java/core/net/packet/context/PlayerContext.java b/Server/src/main/core/net/packet/context/PlayerContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/PlayerContext.java rename to Server/src/main/core/net/packet/context/PlayerContext.java diff --git a/Server/src/main/java/core/net/packet/context/PositionedGraphicContext.java b/Server/src/main/core/net/packet/context/PositionedGraphicContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/PositionedGraphicContext.java rename to Server/src/main/core/net/packet/context/PositionedGraphicContext.java diff --git a/Server/src/main/java/core/net/packet/context/RunScriptContext.java b/Server/src/main/core/net/packet/context/RunScriptContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/RunScriptContext.java rename to Server/src/main/core/net/packet/context/RunScriptContext.java diff --git a/Server/src/main/java/core/net/packet/context/SceneGraphContext.java b/Server/src/main/core/net/packet/context/SceneGraphContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/SceneGraphContext.java rename to Server/src/main/core/net/packet/context/SceneGraphContext.java diff --git a/Server/src/main/java/core/net/packet/context/SkillContext.java b/Server/src/main/core/net/packet/context/SkillContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/SkillContext.java rename to Server/src/main/core/net/packet/context/SkillContext.java diff --git a/Server/src/main/java/core/net/packet/context/StringContext.java b/Server/src/main/core/net/packet/context/StringContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/StringContext.java rename to Server/src/main/core/net/packet/context/StringContext.java diff --git a/Server/src/main/java/core/net/packet/context/SystemUpdateContext.java b/Server/src/main/core/net/packet/context/SystemUpdateContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/SystemUpdateContext.java rename to Server/src/main/core/net/packet/context/SystemUpdateContext.java diff --git a/Server/src/main/java/core/net/packet/context/VarbitContext.java b/Server/src/main/core/net/packet/context/VarbitContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/VarbitContext.java rename to Server/src/main/core/net/packet/context/VarbitContext.java diff --git a/Server/src/main/core/net/packet/context/VarcUpdateContext.java b/Server/src/main/core/net/packet/context/VarcUpdateContext.java new file mode 100644 index 000000000..4a4d9edb2 --- /dev/null +++ b/Server/src/main/core/net/packet/context/VarcUpdateContext.java @@ -0,0 +1,23 @@ +package core.net.packet.context; + +import core.game.node.entity.player.Player; +import core.net.packet.Context; + +public class VarcUpdateContext implements Context { + + Player player; + public int varcId; + public int value; + + public VarcUpdateContext(Player player, int varcId, int value){ + this.player = player; + this.varcId = varcId; + this.value = value; + } + + @Override + public Player getPlayer() { + return player; + } +} + diff --git a/Server/src/main/java/core/net/packet/context/WalkOptionContext.java b/Server/src/main/core/net/packet/context/WalkOptionContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/WalkOptionContext.java rename to Server/src/main/core/net/packet/context/WalkOptionContext.java diff --git a/Server/src/main/java/core/net/packet/context/WindowsPaneContext.java b/Server/src/main/core/net/packet/context/WindowsPaneContext.java similarity index 100% rename from Server/src/main/java/core/net/packet/context/WindowsPaneContext.java rename to Server/src/main/core/net/packet/context/WindowsPaneContext.java diff --git a/Server/src/main/core/net/packet/in/Decoders530.kt b/Server/src/main/core/net/packet/in/Decoders530.kt new file mode 100644 index 000000000..6181f25dd --- /dev/null +++ b/Server/src/main/core/net/packet/in/Decoders530.kt @@ -0,0 +1,804 @@ +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) { + /****************************************** + * ITEM INTERACTIONS + ******************************************/ + ITEM_ACTION_1(156) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val slot = buffer.leShortA + val itemId = buffer.shortA + val ifHash = buffer.leInt + val (iface, button) = deHash(ifHash) + return Packet.ItemAction(player, 0, itemId, slot, iface, button) + } + }, + ITEM_ACTION_2(55) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val itemId = buffer.leShort + val slot = buffer.shortA + val ifHash = buffer.intA + val (iface, button) = deHash(ifHash) + return Packet.ItemAction(player, 1, itemId, slot, iface, button) + } + }, + ITEM_ACTION_3(153) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.leInt + val slot = buffer.leShort + val itemId = buffer.leShort + val (iface, button) = deHash(ifHash) + return Packet.ItemAction(player, 2, itemId, slot, iface, button) + } + }, + ITEM_ACTION_4(161) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.leInt + val itemId = buffer.leShortA + val slot = buffer.leShortA + val (iface, button) = deHash(ifHash) + return Packet.ItemAction(player, 3, itemId, slot, iface, button) + } + }, + ITEM_ACTION_5(135) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val itemId = buffer.shortA + val slot = buffer.shortA + val ifHash = buffer.intB + val (iface, button) = deHash(ifHash) + return Packet.ItemAction(player, 4, itemId, slot, iface, button) + } + }, + + + + /****************************************** + * NPC INTERACTIONS + ******************************************/ + NPC_ACTION_1(78) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val npcIndex = buffer.leShort + return Packet.NpcAction(player, 0, npcIndex) + } + }, + NPC_ACTION_2(3) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val npcIndex = buffer.leShortA + return Packet.NpcAction(player, 1, npcIndex) + } + }, + NPC_ACTION_3(148) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val npcIndex = buffer.shortA + return Packet.NpcAction(player, 2, npcIndex) + } + }, + NPC_ACTION_4(30) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val npcIndex = buffer.short + return Packet.NpcAction(player, 3, npcIndex) + } + }, + NPC_ACTION_5(218) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val npcIndex = buffer.leShort + return Packet.NpcAction(player, 4, npcIndex) + } + }, + + /****************************************** + * SCENERY INTERACTIONS + ******************************************/ + SCENERY_ACTION_1(254) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val x = buffer.leShort + val objId = buffer.shortA and 0xFFFF + val y = buffer.short + return Packet.SceneryAction(player, 0, objId, x, y) + } + }, + SCENERY_ACTION_2(194) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val y = buffer.leShortA + val x = buffer.leShort + val objId = buffer.short and 0xFFFF + return Packet.SceneryAction(player, 1, objId, x, y) + } + }, + SCENERY_ACTION_3(84) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val objId = buffer.leShortA and 0xFFFF + val y = buffer.leShortA + val x = buffer.leShort + return Packet.SceneryAction(player, 2, objId, x, y) + } + }, + SCENERY_ACTION_4(247) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val y = buffer.leShort and 0xFFFF + val x = buffer.leShortA + val objId = buffer.short and 0xFFFF + return Packet.SceneryAction(player, 3, objId, x, y) + } + }, + SCENERY_ACTION_5(170) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val objId = buffer.leShortA and 0xFFFF + val x = buffer.leShortA + val y = buffer.leShortA + return Packet.SceneryAction(player, 4, objId, x, y) + } + }, + + /****************************************** + * PLAYER INTERACTIONS + ******************************************/ + PLAYER_ACTION_1(68) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val pIndex = buffer.leShortA + return Packet.PlayerAction(player, 0, pIndex) + } + }, + PLAYER_ACTION_3(71) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val pIndex = buffer.leShortA + return Packet.PlayerAction(player, 2, pIndex) + } + }, + PLAYER_ACTION_4(180) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val pIndex = buffer.leShortA + return Packet.PlayerAction(player, 3, pIndex) + } + }, + PLAYER_ACTION_7(114) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val index = buffer.leShortA + return Packet.PlayerAction(player, 6, index) + } + }, + PLAYER_ACTION_8(175) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val pIndex = buffer.shortA + return Packet.PlayerAction(player, 7, pIndex) + } + }, + + /****************************************** + * OBJSTACK (Ground Item) INTERACTIONS + ******************************************/ + OBJSTACK_ACTION_1(66) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val x = buffer.leShort + val itemId = buffer.short + val y = buffer.leShortA + return Packet.GroundItemAction(player, 2, itemId, x, y) + } + }, + OBJSTACK_ACTION_2(33) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val itemId = buffer.short + val x = buffer.short + val y = buffer.leShort + return Packet.GroundItemAction(player, 3, itemId, x, y) + } + }, + + /****************************************** + * USEWITH INTERACTIONS + ******************************************/ + USEWITH_NPC(115) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.intB + val slot = buffer.leShort + val npcIndex = buffer.leShort + val itemId = buffer.leShortA + val (iface, _) = deHash(ifHash) + return Packet.UseWithNpc(player, itemId, npcIndex, iface, slot) + } + }, + USEWIH_PLAYER(248) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val otherIndex = buffer.leShortA + val itemId = buffer.short + val slot = buffer.short + val ifHash = buffer.intB + val (iface, _) = deHash(ifHash) + return Packet.UseWithPlayer(player, itemId, otherIndex, iface, slot) + } + }, + USEWITH_ITEM(27) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val usedSlot = buffer.short + val usedIfHash = buffer.leInt + val usedWithSlot = buffer.leShort + val usedWithIfHash = buffer.leInt + val usedId = buffer.leShortA + val usedWithId = buffer.leShortA + + val (usedIface, usedChild) = deHash(usedIfHash) + val (usedWithIface, usedWithChild) = deHash(usedWithIfHash) + return Packet.UseWithItem( + player, + usedId, usedWithId, + usedSlot, usedWithSlot, + usedIface, usedWithIface, + usedChild, usedWithChild + ) + } + }, + USEWITH_SCENERY(134) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val x = buffer.shortA + val id = buffer.short + val y = buffer.leShort + val slot = buffer.short + buffer.leShort //Suspicious noops? TODO: FIND OUT WHAT THESE ARE + buffer.short + val sceneryId = buffer.shortA + return Packet.UseWithScenery(player, id, slot, sceneryId, x, y) + } + }, + USEWITH_GROUNDITEM(101) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val x = buffer.leShortA + val slot = buffer.leShort + val usedId = buffer.leShort + val usedWithId = buffer.leShort + val y = buffer.leShortA + val ifHash = buffer.intB + val (iface,child) = deHash(ifHash) + return Packet.UseWithGroundItem(player, usedId, usedWithId, iface, child, slot, x, y) + } + }, + + /****************************************** + * INTERFACE INTERACTIONS + ******************************************/ + IF_ACTION_1(155) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 0, iface, button, slot) + } + }, + IF_ACTION_2(196) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 1, iface, button, slot) + } + }, + IF_ACTION_3(124) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 2, iface, button, slot) + } + }, + IF_ACTION_4(199) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 3, iface, button, slot) + } + }, + IF_ACTION_5(234) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 4, iface, button, slot) + } + }, + IF_ACTION_6(168) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 5, iface, button, slot) + } + }, + IF_ACTION_7(166) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 6, iface, button, slot) + } + }, + IF_ACTION_8(64) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 7, iface, button, slot) + } + }, + IF_ACTION_9(53) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 8, iface, button, slot) + } + }, + IF_ACTION_10(9) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val slot = buffer.short + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 9, iface, button, slot) + } + }, + IF_ACTION_CS(10) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.int + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, -1, iface, button, -1, -1) + } + }, + CONTINUE_OPT(132) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.intA + val slot = buffer.leShort + val (iface, button) = deHash(ifHash) + return Packet.ContinueOption(player, iface, button, slot, 132) + } + }, + CLOSE_IFACE(184) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.CloseIface(player) + } + }, + IF_GROUNDITEM_ACTION(73) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val ifHash = buffer.intA + val (iface, child) = deHash(ifHash) + val y = buffer.short + val itemId = buffer.leShortA + val x = buffer.leShortA + val slot = buffer.leShort + return Packet.ComponentGroundItemAction(player, iface, child, slot, itemId, x, y) + } + }, + IF_PLAYER_ACTION(195) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + buffer.shortA //Arios ignoring more data... nice + val child = buffer.leShort + val iface = buffer.leShort + val otherIndex = buffer.leShortA + return Packet.ComponentPlayerAction(player, otherIndex, iface, child) + } + }, + IF_SCENERY_ACTION(233) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val y = buffer.leShortA + val x = buffer.shortA + val itemId = buffer.leShortA //probably misnamed/mislabeled by Arios. + val ifHash = buffer.intA + val (iface, child) = deHash(ifHash) + val objId = buffer.shortA + return Packet.ComponentSceneryAction(player, iface, child, objId, x, y) + } + }, + IF_NPC_ACTION(239) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val child = buffer.leShort + val iface = buffer.leShort + buffer.shortA //more ignored data.... + val index = buffer.leShortA + return Packet.ComponentNpcAction(player, iface, child, index) + } + }, + IF_ITEM_ACTION(253) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val child = buffer.leShort + val iface = buffer.leShort + val itemSlot = buffer.leShortA + val unused = buffer.leInt //unused??? + val itemId = buffer.shortA + buffer.shortA //more ignored data.... + return Packet.ComponentItemAction(player, iface, child, itemId, itemSlot) + } + }, + SLOTSWITCH_MULTI(79) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val srcHash = buffer.intB + val destSlot = buffer.leShort + val destHash = buffer.int + val srcSlot = buffer.leShort + val (srcIface, srcChild) = deHash(srcHash) + val (destIface, destChild) = deHash(destHash) + return Packet.SlotSwitchMultiComponent(player, srcIface, srcChild, srcSlot, destIface, destChild, destSlot) + } + }, + SLOTSWITCH_SINGLE(231) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val srcSlot = buffer.short + val ifHash = buffer.leInt + val destSlot = buffer.shortA + val isInsert = buffer.get() == 1 + val (iface, child) = deHash(ifHash) + return Packet.SlotSwitchSingleComponent(player, iface, child, srcSlot, destSlot, isInsert) + } + }, + IF_ITEM_OPT_1(81) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val slot = buffer.shortA + val itemId = buffer.short + val child = buffer.short + val iface = buffer.short + return Packet.IfAction(player, opcode, 0, iface, child, slot, itemId) + } + }, + IF_ITEM_OPT_2(206) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val itemId = buffer.shortA + val slot = buffer.leShort + val ifHash = buffer.leInt + val (iface, button) = deHash(ifHash) + return Packet.IfAction(player, opcode, 1, iface, button, slot, itemId) + } + }, + + /****************************************** + * EXAMINE + ******************************************/ + EXAMINE_SCENERY(94) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.SceneryExamine(player, buffer.leShortA) + } + }, + EXAMINE_ITEM(92) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.ItemExamine(player, buffer.leShortA) + } + }, + EXAMINE_NPC(72) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.NpcExamine(player, buffer.short) + } + }, + + /****************************************** + * CLANS + ******************************************/ + CLAN_JOIN(104) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val clanName = StringUtils.longToString(buffer.long) + return Packet.JoinClan(player, clanName) + } + }, + CLAN_SETRANK(188) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val rank = buffer.a + val name = StringUtils.longToString(buffer.long) + return Packet.SetClanRank(player, name, rank) + } + }, + CLAN_KICK(162) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val name = StringUtils.longToString(buffer.long) + return Packet.KickFromClan(player, name) + } + }, + + /****************************************** + * FRIENDS/IGNORES + ******************************************/ + ADD_FRIEND(120) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.AddFriend(player, StringUtils.longToString(buffer.long)) + } + }, + REMOVE_FRIEND(57) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.RemoveFriend(player, StringUtils.longToString(buffer.long)) + } + }, + ADD_IGNORE(34) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.AddIgnore(player, StringUtils.longToString(buffer.long)) + } + }, + REMOVE_IGNORE(213) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.RemoveIgnore(player, StringUtils.longToString(buffer.long)) + } + }, + PRIVATE_MESSAGE(201) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val name = StringUtils.longToString(buffer.long) + val message = StringUtils.decryptPlayerChat(buffer, buffer.get() and 0xFF) + return Packet.PrivateMessage(player, name, message) + } + }, + + /****************************************** + * INPUT TRACKING + ******************************************/ + FOCUS_CHANGE(22) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.TrackingFocus(player, buffer.get() == 1) + } + }, + CAMERA_MOVEMENT(21) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val x = buffer.shortA + val y = buffer.leShort + return Packet.TrackingCameraPos(player, x, y) + } + }, + DISPLAY_UPDATE(243) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val windowMode = buffer.get() + val screenWidth = buffer.short + val screenHeight = buffer.short + val displayMode = buffer.get() + return Packet.TrackingDisplayUpdate(player, windowMode, screenWidth, screenHeight, displayMode) + } + }, + AFK_TIMEOUT(245) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.TrackingAfkTimeout(player) + } + }, + MOUSE_CLICKED(75) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val data = buffer.leShortA + val pos = buffer.intB + val rightClick = ((data shr 15) and 0x1) == 1 + val delay = data and 0x7FF + val x = pos shr 16 + val y = pos and 0xFFFF + return Packet.TrackingMouseClick(player, x, y, rightClick, delay) + } + }, + + /****************************************** + * WALKING + ******************************************/ + WORLDSPACE_WALK(215) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val (running, x, y) = decodeWalkInformation(buffer, false) + return Packet.WorldspaceWalk(player, x, y, running) + } + }, + MINIMAP_WALK(39) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val (running, x, y) = decodeWalkInformation(buffer, true) + val clickedX = buffer.get() + val clickedY = buffer.get() + val rotation = buffer.short + //Unlabeled data ignored by arios + buffer.get() + buffer.get() + buffer.get() + buffer.get() + buffer.short + buffer.short + buffer.get() + buffer.get() + return Packet.MinimapWalk(player, x, y, clickedX, clickedY, rotation, running) + } + }, + INTERACT_WALK(77) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val (running, x, y) = decodeWalkInformation(buffer, false) + return Packet.InteractWalk(player, x, y, running) + } + }, + + /****************************************** + * INPUT PROMPT RESPONSE + ******************************************/ + INPUT_SHORT_STRING_RESPONSE(244) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.InputPromptResponse(player, StringUtils.longToString(buffer.long)) + } + }, + INPUT_LONG_STRING_RESPONSE(65) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.InputPromptResponse(player, buffer.string) + } + }, + INPUT_INT_RESPONSE(23) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.InputPromptResponse(player, buffer.int) + } + }, + + /****************************************** + * ASSORTED + ******************************************/ + GE_SET_OFFER_ITEM(111) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val itemId = buffer.short + return Packet.GESetOfferItem(player, itemId) + } + }, + COMMAND(44) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + if (buffer.toByteBuffer().remaining() > 1) { + 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() + } + }, + CHAT_SETTINGS(157) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.ChatSetting( + player, + buffer.get(), + buffer.get(), + buffer.get() + ) + } + }, + CHAT_MESSAGE(237) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + 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) + } + }, + MUSIC_TRACK_FINISHED(137) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val trackId = buffer.leShortA + return Packet.TrackFinished(player, trackId) + } + }, + REPORT_ABUSE(99) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val target = StringUtils.longToString(buffer.long) + val ruleId = buffer.get() + val modMute = buffer.get() == 1 + return Packet.ReportAbuse(player, target, ruleId, modMute) + } + }, + UPDATE_PACKET_COUNT(177) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val count = buffer.short + return Packet.PacketCountUpdate(player, count) + } + }, + PING(93) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.Ping(player) + } + }, + QUICKCHAT(167) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val x = buffer.toByteBuffer() + + val packetType = when(x.array().size){ + 3,4 -> QCPacketType.STANDARD + 5 -> QCPacketType.SINGLE + 7 -> QCPacketType.DOUBLE + 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 + val multiplier: Int = buffer.get() + val offset: Int = buffer.get() + var selection_a_index = -1 + var selection_b_index = -1 + + when(packetType){ + QCPacketType.SINGLE -> { + selection_a_index = buffer.short + } + QCPacketType.DOUBLE -> { + buffer.get() //discard + selection_a_index = buffer.get() + buffer.get() //discard + selection_b_index = buffer.get() + } + 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) + } + }, + MAP_REBUILD_STARTED(20) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.NoProcess() + } + }, + MAP_REBUILD_FINISHED(110) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + return Packet.NoProcess() + } + }, + PLAYER_PREFS_UPDATE(98) { + override fun decode(player: Player, buffer: IoBuffer): Packet { + val prefs = buffer.int + val particleSetting = prefs shr 23 + val ambienceEnabled = (prefs shr 22) and 1 == 1 + val musicEnabled = (prefs shr 21) and 1 == 1 + val soundEnabled = (prefs shr 20) and 1 == 1 + val stereo = (prefs shr 19) and 1 == 1 + val fog = (prefs shr 16) and 1 == 1 + val hdWater = (prefs shr 15) and 1 == 1 + val hdLighting = (prefs shr 13) and 1 == 1 + val shadowType = (prefs shr 11) and 0x3 + val charShadows = (prefs shr 10) and 1 == 1 + val manyGroundTextures = (prefs shr 9) and 1 == 1 + val flickeringEffects = (prefs shr 8) and 1 == 1 + val manyIdleAnims = (prefs shr 7) and 1 == 1 + val hdTextures = (prefs shr 6) and 1 == 1 + 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 + //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) + } + } + ; + abstract fun decode(player: Player, buffer: IoBuffer): Packet + + fun deHash(ifHash: Int) : Pair { + return Pair(ifHash shr 16, ifHash and 0xFFFF) + } + + fun decodeWalkInformation(buffer: IoBuffer, isMinimap: Boolean) : Triple { + val isRunning = buffer.a == 1 + var x = buffer.short + var y = buffer.shortA + val steps = (buffer.toByteBuffer().remaining() - if (isMinimap) 14 else 0) shr 1 + //derive the final destination by poking the last set of values in the packet (client does its own pathfinding and reports it) + for (i in 0 until steps) { + val offsetX = buffer.a + val offsetY = buffer.s + if (i == steps - 1){ + x += offsetX + y += offsetY + } + } + return Triple(isRunning, x, y) + } + + companion object { + private val opcodeMap = values().associateBy { it.opcode } + + @JvmStatic fun process(player: Player, opcode: Int, buffer: IoBuffer) : Packet { + val decoder = opcodeMap[opcode] ?: return Packet.UnhandledOp() + + return try { + decoder.decode(player, buffer) + } catch (e: Exception) { + val sw = StringWriter() + val pw = PrintWriter(sw) + e.printStackTrace(pw) + Packet.DecodingError("Error decoding opcode $opcode/${decoder.name}: $sw") + } + } + } +} diff --git a/Server/src/main/core/net/packet/in/Login.kt b/Server/src/main/core/net/packet/in/Login.kt new file mode 100644 index 000000000..df243d9d6 --- /dev/null +++ b/Server/src/main/core/net/packet/in/Login.kt @@ -0,0 +1,222 @@ +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 +import core.game.node.entity.player.Player +import core.net.Constants +import core.net.IoSession +import core.tools.StringUtils +import core.integrations.discord.Discord +import proto.management.JoinClanRequest +import proto.management.PlayerStatusUpdate +import proto.management.RequestContactInfo +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 + +object Login { + private const val ENCRYPTION_VERIFICATION_BYTE: Int = 10 + private const val NORMAL_LOGIN_OP = 16 + 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() + + info.opcode = buffer.get().toInt() + if (buffer.short.toInt() != buffer.remaining()) { + return Pair(AuthResponse.BadSessionID, null) + } + val revision = buffer.int + if (revision != Constants.REVISION) { + return Pair(AuthResponse.Updated, null) + } + if (info.opcode != NORMAL_LOGIN_OP && info.opcode != RECONNECT_LOGIN_OP) { + log(this::class.java, Log.WARN, "Invalid Login Opcode: ${info.opcode}") + return Pair(AuthResponse.InvalidLoginServer, null) + } + + noop(buffer) + info.showAds = buffer.get().toInt() == 1 + noop(buffer) + info.windowMode = buffer.get().toInt() + info.screenWidth = buffer.short.toInt() + info.screenHeight = buffer.short.toInt() + info.displayMode = buffer.get().toInt() + noop(buffer, 24) //Skip past a bunch of random (actually random) data the client sends + ByteBufferUtils.getString(buffer) //same as above + info.adAffiliateId = buffer.int + info.settingsHash = buffer.int + info.currentPacketCount = buffer.short.toInt() + + //Read client-reported CRC sums + for (i in 0 until CACHE_INDEX_COUNT) info.crcSums[i] = buffer.int + + val decryptedBuffer = decryptRSABuffer(buffer, ServerConstants.EXPONENT, ServerConstants.MODULUS) + decryptedBuffer.rewind() + + if (decryptedBuffer.get().toInt() != ENCRYPTION_VERIFICATION_BYTE) { + return Pair(AuthResponse.UnexpectedError, info) + } + + info.isaacPair = produceISAACPairFrom(decryptedBuffer) + info.username = StringUtils.longToString(decryptedBuffer.long) + info.password = ByteBufferUtils.getString(decryptedBuffer) + + if (Repository.getPlayerByName(info.username) != null) { + return Pair(AuthResponse.AlreadyOnline, info) + } + + 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) { + log(this::class.java, Log.ERR, "Exception encountered during login packet parsing! See stack trace below.") + e.printStackTrace() + return Pair(AuthResponse.UnexpectedError, null) + } + } + + private fun produceISAACPairFrom(buffer: ByteBuffer): ISAACPair { + val incomingSeed = IntArray(4) + for(i in incomingSeed.indices) { + incomingSeed[i] = buffer.int + } + val inCipher = ISAACCipher(incomingSeed) + for(i in incomingSeed.indices) { + incomingSeed[i] += 50 + } + val outCipher = ISAACCipher(incomingSeed) + return ISAACPair(inCipher, outCipher) + } + + @JvmStatic fun decryptRSABuffer(buffer: ByteBuffer, exponent: BigInteger, modulus: BigInteger): ByteBuffer { + return try { + val numBytes = buffer.get().toInt() and 0xFF + val encryptedBytes = ByteArray(numBytes) + buffer.get(encryptedBytes) + + val encryptedBigInt = BigInteger(encryptedBytes) + ByteBuffer.wrap(encryptedBigInt.modPow(exponent, modulus).toByteArray()) + } catch (e: BufferUnderflowException) { + ByteBuffer.wrap(byteArrayOf(-1)) + } + } + + private fun noop(buffer: ByteBuffer, amount: Int = 1) {buffer.get(ByteArray(amount))} + + fun proceedWith(session: IoSession, details: PlayerDetails, opcode: Int) { + if (Repository.uid_map.contains(details.uid)) { + session.write(AuthResponse.AlreadyOnline) + return + } + details.session = session + details.info.translate(UIDInfo(details.ipAddress, "DEPRECATED", "DEPRECATED", "DEPRECATED")) + + val archive = ServerStore.getArchive("flagged-ips") + val flaggedIps = archive.getList("ips") + if (flaggedIps.contains(details.ipAddress)) { + Discord.postPlayerAlert(details.username, "Login from flagged IP ${details.ipAddress}") + } + + val player = Player(details) + PlayerMonitor.log(player, LogType.IP_LOG, details.ipAddress) + if (canBypassAccountLimitCheck(player)) { + proceedWithAcceptableLogin(session, player, opcode) + } else { + if (checkAccountLimit(details.ipAddress, details.username)) { + proceedWithAcceptableLogin(session, player, opcode) + } else { + session.write(AuthResponse.LoginLimitExceeded) + } + } + } + + private fun canBypassAccountLimitCheck(player: Player): Boolean { + return player.rights == Rights.ADMINISTRATOR || player.rights == Rights.PLAYER_MODERATOR + } + + private fun proceedWithAcceptableLogin(session: IoSession, player: Player, opcode: Int) { + Repository.addPlayer(player) + session.lastPing = System.currentTimeMillis() + try { + LoginParser(player.details).initialize(player, opcode == RECONNECT_LOGIN_OP) + sendMSEvents(player.details) + } catch (e: Exception) { + e.printStackTrace() + session.disconnect() + } + } + + 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 >= accountLimit) + return false + + archive.addToList(ipAddress, username) + return true + } + + private fun sendMSEvents(details: PlayerDetails) { + val statusEvent = PlayerStatusUpdate.newBuilder() + statusEvent.username = details.username + statusEvent.world = GameWorld.settings!!.worldId + statusEvent.notifyFriendsOnly = false + publish(statusEvent.build()) + + val contactEvent = RequestContactInfo.newBuilder() + contactEvent.username = details.username + contactEvent.world = GameWorld.settings!!.worldId + publish(contactEvent.build()) + + if (!details.communication.currentClan.isNullOrEmpty() && details.communication.clan == null) { + val clanEvent = JoinClanRequest.newBuilder() + clanEvent.username = details.username + clanEvent.clanName = details.communication.currentClan + publish(clanEvent.build()) + } + } +} diff --git a/Server/src/main/core/net/packet/in/LoginInfo.kt b/Server/src/main/core/net/packet/in/LoginInfo.kt new file mode 100644 index 000000000..84d7ba6ba --- /dev/null +++ b/Server/src/main/core/net/packet/in/LoginInfo.kt @@ -0,0 +1,30 @@ +package core.net.packet.`in` + +import core.cache.crypto.ISAACCipher +import core.cache.crypto.ISAACPair + +class LoginInfo( + var showAds: Boolean, //Unused + var windowMode : Int, + var screenWidth: Int, + var screenHeight: Int, + var displayMode: Int, + var adAffiliateId: Int, //Unused + var settingsHash: Int, + var currentPacketCount: Int, + var username: String, + var password: String, + var isaacPair: ISAACPair, + var opcode: Int, + var crcSums: IntArray +) { + companion object { + fun createDefault(): LoginInfo { + return LoginInfo(false, 0, 0, 0, 0, 0, 0, 0, "", "", ISAACPair(ISAACCipher(intArrayOf()), ISAACCipher(intArrayOf())), 0, IntArray(Login.CACHE_INDEX_COUNT)) + } + } + + override fun toString(): String { + return "ads:$showAds,wm:$windowMode,sw:$screenWidth,sh:$screenHeight,dm:$displayMode,adid:$adAffiliateId,settings:$settingsHash,pkt:$currentPacketCount,un:$username,pw:$password" + } +} diff --git a/Server/src/main/core/net/packet/in/Packet.kt b/Server/src/main/core/net/packet/in/Packet.kt new file mode 100644 index 000000000..77f442971 --- /dev/null +++ b/Server/src/main/core/net/packet/in/Packet.kt @@ -0,0 +1,60 @@ +package core.net.packet.`in` + +import core.game.node.entity.player.Player + +sealed class Packet { + data class ItemAction(val player: Player, val optIndex: Int, val itemId: Int, val slot: Int, val iface: Int, val child: Int) : Packet() + data class NpcAction(val player: Player, val optIndex: Int, val npcIndex: Int) : Packet() + data class SceneryAction(val player: Player, val optIndex: Int, val id: Int, val x: Int, val y: Int) : Packet() + data class PlayerAction(val player: Player, val optIndex: Int, val otherIndex: Int) : Packet() + data class GroundItemAction(val player: Player, val optIndex: Int, val id: Int, val x: Int, val y: Int) : Packet() + data class ItemExamine(val player: Player, val id: Int) : Packet() + data class SceneryExamine(val player: Player, val id: Int) : Packet() + data class NpcExamine(val player: Player, val id: Int) : Packet() + data class UseWithNpc(val player: Player, val itemId: Int, val npcIndex: Int, val iface: Int, val slot: Int) : Packet() + data class UseWithPlayer(val player: Player, val itemId: Int, val otherIndex: Int, val iface: Int, val slot: Int) : Packet() + data class UseWithItem(val player: Player, val usedId: Int, val usedWithId: Int, val usedSlot: Int, val usedWithSlot: Int, val usedIface: Int, val usedWithIface: Int, val usedChild: Int, val usedWithChild: Int) : Packet() + data class UseWithScenery(val player: Player, val itemId: Int, val slot: Int, val sceneryId: Int, val x: Int, val y: Int) : Packet() + data class UseWithGroundItem(val player: Player, val usedId: Int, val withId: Int, val iface: Int, val child: Int, val slot: Int, val x: Int, val y: Int) : Packet() + data class IfAction(val player: Player, val opcode: Int, val optIndex: Int, val iface: Int, val child: Int, val slot: Int, val itemId: Int = -1) : Packet() + data class ContinueOption(val player: Player, val iface: Int, val child: Int, val slot: Int, val opcode: Int) : Packet() + data class CloseIface(val player: Player) : Packet() + data class JoinClan(val player: Player, val clanName: String) : Packet() + data class SetClanRank(val player: Player, val username: String, val rank: Int) : Packet() + data class KickFromClan(val player: Player, val username: String) : Packet() + data class AddFriend(val player: Player, val username: String) : Packet() + data class RemoveFriend(val player: Player, val username: String) : Packet() + data class AddIgnore(val player: Player, val username: String) : Packet() + data class RemoveIgnore(val player: Player, val username: String) : Packet() + data class TrackingFocus(val player: Player, val isFocused: Boolean) : Packet() + data class TrackingCameraPos(val player: Player, val x: Int, val y: Int) : Packet() + data class TrackingDisplayUpdate(val player: Player, val windowMode: Int, val screenWidth: Int, val screenHeight: Int, val displayMode: Int) : Packet() + data class TrackingAfkTimeout(val player: Player) : Packet() + data class TrackingMouseClick(val player: Player, val x: Int, val y: Int, val isRightClick: Boolean, val delay: Int) : Packet() + data class ComponentPlayerAction(val player: Player, val otherIndex: Int, val iface: Int, val child: Int) : Packet() + data class ComponentItemAction(val player: Player, val iface: Int, val child: Int, val itemId: Int, val slot: Int) : Packet() + data class ComponentNpcAction(val player: Player, val iface: Int, val child: Int, val npcIndex: Int) : Packet() + data class ComponentSceneryAction(val player: Player, val iface: Int, val child: Int, val sceneryId: Int, val x: Int, val y: Int) : Packet() + data class ComponentGroundItemAction(val player: Player, val iface: Int, val child: Int, val slot: Int, val itemId: Int, val x: Int, val y: Int) : Packet() + data class SlotSwitchMultiComponent(val player: Player, val sourceIface: Int, val sourceChild: Int, val sourceSlot: Int, val destIface: Int, val destChild: Int, val destSlot: Int) : Packet() + data class SlotSwitchSingleComponent(val player: Player, val iface: Int, val child: Int, val sourceSlot: Int, val destSlot: Int, val isInsert: Boolean) : Packet() + data class PacketCountUpdate(val player: Player, val count: Int) : Packet() + data class PrivateMessage(val player: Player, val username: String, val message: String) : Packet() + data class ChatMessage(val player: Player, val effects: Int, val message: String) : Packet() + data class ChatSetting(val player: Player, val public: Int, val private: Int, val trade: Int) : Packet() + data class Command(val player: Player, val commandLine: String) : Packet() + data class GESetOfferItem(val player: Player, val itemId: Int) : Packet() + data class TrackFinished(val player: Player, val trackId: Int) : Packet() + data class ReportAbuse(val player: Player, val target: String, val rule: Int, val modMute: Boolean) : Packet() + data class WorldspaceWalk(val player: Player, val destX: Int, val destY: Int, val isRun: Boolean) : Packet() + data class MinimapWalk(val player: Player, val destX: Int, val destY: Int, val clickedX: Int, val clickedY: Int, val rotation: Int, val isRun: Boolean) : Packet() + data class InteractWalk(val player: Player, val destX: Int, val destY: Int, val isRun: Boolean) : Packet() + data class Ping(val player: Player) : Packet() + data class QuickChat(val player: Player, val indexA: Int, val indexB: Int, val forClan: Boolean, val multiplier: Int, val offset: Int, val type: QCPacketType) : Packet() + data class InputPromptResponse(val player: Player, val response: Any) : Packet() + data class PlayerPrefsUpdate(val player: Player, val prefs: Int) : Packet() + + class NoProcess : Packet() + class UnhandledOp : Packet() + data class DecodingError(val message: String) : Packet() +} diff --git a/Server/src/main/kotlin/rs09/net/packet/in/QuickChatPacketHandler.kt b/Server/src/main/core/net/packet/in/QuickChatPacketHandler.kt similarity index 75% rename from Server/src/main/kotlin/rs09/net/packet/in/QuickChatPacketHandler.kt rename to Server/src/main/core/net/packet/in/QuickChatPacketHandler.kt index 311c0d57e..785775288 100644 --- a/Server/src/main/kotlin/rs09/net/packet/in/QuickChatPacketHandler.kt +++ b/Server/src/main/core/net/packet/in/QuickChatPacketHandler.kt @@ -1,11 +1,13 @@ -package rs09.net.packet.`in` +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 rs09.game.interaction.QCRepository -import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld +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("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("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("Begin QuickChat Packet Buffer Dump---------") - SystemLogger.logInfo("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("$it") + log(this::class.java, Log.FINE, "$it") } - SystemLogger.logInfo("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 new file mode 100644 index 000000000..b3f900714 --- /dev/null +++ b/Server/src/main/core/net/packet/in/RunScript.kt @@ -0,0 +1,45 @@ +package core.net.packet.`in` + +import core.api.* +import core.game.node.entity.player.Player + +/** + * Handles an incoming script execution request packet. + * + * @author vddCore + */ +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 + + if (player.getAttribute("parseamount", false)) { + input = value.toString().lowercase() + + if (!input.matches(Regex("^(\\d+)(k+|m+)?$"))) { + sendDialogue(player, "That doesn't look right. Please try again.") + return + } + + 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/java/core/net/packet/out/AccessMask.java b/Server/src/main/core/net/packet/out/AccessMask.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/AccessMask.java rename to Server/src/main/core/net/packet/out/AccessMask.java diff --git a/Server/src/main/java/core/net/packet/out/AnimateInterface.java b/Server/src/main/core/net/packet/out/AnimateInterface.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/AnimateInterface.java rename to Server/src/main/core/net/packet/out/AnimateInterface.java diff --git a/Server/src/main/java/core/net/packet/out/AnimateObjectPacket.java b/Server/src/main/core/net/packet/out/AnimateObjectPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/AnimateObjectPacket.java rename to Server/src/main/core/net/packet/out/AnimateObjectPacket.java diff --git a/Server/src/main/core/net/packet/out/AudioPacket.java b/Server/src/main/core/net/packet/out/AudioPacket.java new file mode 100644 index 000000000..beb04da91 --- /dev/null +++ b/Server/src/main/core/net/packet/out/AudioPacket.java @@ -0,0 +1,49 @@ +package core.net.packet.out; + +import core.game.node.entity.player.link.audio.Audio; +import core.game.world.map.Location; +import core.net.packet.IoBuffer; +import core.net.packet.OutgoingPacket; +import core.net.packet.context.DefaultContext; + +/** + * Sends an audio packet. + * @author Vexia + */ +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.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.id); + buffer.put((byte) (audio.radius << 4 | audio.loops & 7)); + buffer.put((byte) audio.delay); + } + return buffer; + } + + //208 music effect + //4 music + //172 sound effect + @Override + public void send(DefaultContext context) { + final Audio audio = (Audio) context.getObjects()[0]; + final Location loc = (Location) context.getObjects()[1]; + IoBuffer buffer; + if(loc == null) { + buffer = new IoBuffer(); + } else { + buffer = UpdateAreaPosition.getBuffer(context.getPlayer(), loc.getChunkBase()); + } + write(buffer, audio, loc); + buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput()); + context.getPlayer().getSession().write(buffer); + } + +} diff --git a/Server/src/main/java/core/net/packet/out/BuildDynamicScene.java b/Server/src/main/core/net/packet/out/BuildDynamicScene.java similarity index 98% rename from Server/src/main/java/core/net/packet/out/BuildDynamicScene.java rename to Server/src/main/core/net/packet/out/BuildDynamicScene.java index 8337471a8..3cea2b5db 100644 --- a/Server/src/main/java/core/net/packet/out/BuildDynamicScene.java +++ b/Server/src/main/core/net/packet/out/BuildDynamicScene.java @@ -9,7 +9,7 @@ import core.net.packet.IoBuffer; import core.net.packet.OutgoingPacket; import core.net.packet.PacketHeader; import core.net.packet.context.DynamicSceneContext; -import rs09.game.system.config.XteaParser; +import core.game.system.config.XteaParser; import java.util.ArrayList; import java.util.List; diff --git a/Server/src/main/java/core/net/packet/out/CSConfigPacket.java b/Server/src/main/core/net/packet/out/CSConfigPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/CSConfigPacket.java rename to Server/src/main/core/net/packet/out/CSConfigPacket.java diff --git a/Server/src/main/java/core/net/packet/out/CameraViewPacket.java b/Server/src/main/core/net/packet/out/CameraViewPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/CameraViewPacket.java rename to Server/src/main/core/net/packet/out/CameraViewPacket.java diff --git a/Server/src/main/java/core/net/packet/out/ClearGroundItem.java b/Server/src/main/core/net/packet/out/ClearGroundItem.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/ClearGroundItem.java rename to Server/src/main/core/net/packet/out/ClearGroundItem.java diff --git a/Server/src/main/java/core/net/packet/out/ClearMinimapFlag.java b/Server/src/main/core/net/packet/out/ClearMinimapFlag.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/ClearMinimapFlag.java rename to Server/src/main/core/net/packet/out/ClearMinimapFlag.java diff --git a/Server/src/main/java/core/net/packet/out/ClearRegionChunk.java b/Server/src/main/core/net/packet/out/ClearRegionChunk.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/ClearRegionChunk.java rename to Server/src/main/core/net/packet/out/ClearRegionChunk.java diff --git a/Server/src/main/java/core/net/packet/out/ClearScenery.java b/Server/src/main/core/net/packet/out/ClearScenery.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/ClearScenery.java rename to Server/src/main/core/net/packet/out/ClearScenery.java diff --git a/Server/src/main/java/core/net/packet/out/CloseInterface.java b/Server/src/main/core/net/packet/out/CloseInterface.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/CloseInterface.java rename to Server/src/main/core/net/packet/out/CloseInterface.java diff --git a/Server/src/main/java/core/net/packet/out/CommunicationMessage.java b/Server/src/main/core/net/packet/out/CommunicationMessage.java similarity index 80% rename from Server/src/main/java/core/net/packet/out/CommunicationMessage.java rename to Server/src/main/core/net/packet/out/CommunicationMessage.java index cd0a80aec..77e86af95 100644 --- a/Server/src/main/java/core/net/packet/out/CommunicationMessage.java +++ b/Server/src/main/core/net/packet/out/CommunicationMessage.java @@ -6,8 +6,9 @@ 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 rs09.game.ai.AIPlayer; +import core.game.bots.AIPlayer; import java.util.Random; @@ -21,32 +22,32 @@ public final class CommunicationMessage implements OutgoingPacket { Player player = context.getPlayer(); switch (context.getType()) { case ContactContext.UPDATE_STATE_TYPE: - buffer = new IoBuffer(197).put(WorldCommunicator.getState().value()); + buffer = new IoBuffer(197).put(2); //always put the AVAILABLE state. break; case ContactContext.IGNORE_LIST_TYPE: buffer = new IoBuffer(126, PacketHeader.SHORT); diff --git a/Server/src/main/core/net/packet/out/ContainerPacket.java b/Server/src/main/core/net/packet/out/ContainerPacket.java new file mode 100644 index 000000000..8c669186e --- /dev/null +++ b/Server/src/main/core/net/packet/out/ContainerPacket.java @@ -0,0 +1,74 @@ +package core.net.packet.out; + +import core.game.container.ContainerEvent; +import core.game.node.item.Item; +import core.net.packet.IoBuffer; +import core.net.packet.OutgoingPacket; +import core.net.packet.PacketHeader; +import core.net.packet.context.ContainerContext; + +/** + * Represents the outgoing container packet. + * @author Emperor + */ +public final class ContainerPacket implements OutgoingPacket { + + @Override + public void send(ContainerContext context) { + IoBuffer buffer = null; + if (context.isClear()) { + buffer = new IoBuffer(144); + buffer.putIntB(context.getInterfaceId() << 16 | context.getChildId()); + } else { + boolean slotBased = context.getSlots() != null; + buffer = new IoBuffer(slotBased ? 22 : 105, PacketHeader.SHORT); + buffer.putShort(context.getInterfaceId()); + buffer.putShort(context.getChildId()); + buffer.putShort(context.getContainerId()); + if (slotBased) { + for (int slot : context.getSlots()) { + buffer.putSmart(slot); + Item item = context.getItems()[slot]; + if (item != null && !item.equals(ContainerEvent.NULL_ITEM)) { + buffer.putShort(item.getId() + 1); + int amount = item.getAmount(); + if (amount < 0 || amount > 254) { + buffer.put(255).putInt(amount); + } else { + buffer.put(amount); + } + } else { + buffer.putShort(0); + } + } + } else { + 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); + } + +} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/out/DisplayModel.java b/Server/src/main/core/net/packet/out/DisplayModel.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/DisplayModel.java rename to Server/src/main/core/net/packet/out/DisplayModel.java diff --git a/Server/src/main/java/core/net/packet/out/GameMessage.java b/Server/src/main/core/net/packet/out/GameMessage.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/GameMessage.java rename to Server/src/main/core/net/packet/out/GameMessage.java diff --git a/Server/src/main/java/core/net/packet/out/GrandExchangePacket.java b/Server/src/main/core/net/packet/out/GrandExchangePacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/GrandExchangePacket.java rename to Server/src/main/core/net/packet/out/GrandExchangePacket.java diff --git a/Server/src/main/java/core/net/packet/out/HintIcon.java b/Server/src/main/core/net/packet/out/HintIcon.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/HintIcon.java rename to Server/src/main/core/net/packet/out/HintIcon.java diff --git a/Server/src/main/java/core/net/packet/out/InstancedLocationUpdate.java b/Server/src/main/core/net/packet/out/InstancedLocationUpdate.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/InstancedLocationUpdate.java rename to Server/src/main/core/net/packet/out/InstancedLocationUpdate.java diff --git a/Server/src/main/java/core/net/packet/out/InteractionOption.java b/Server/src/main/core/net/packet/out/InteractionOption.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/InteractionOption.java rename to Server/src/main/core/net/packet/out/InteractionOption.java diff --git a/Server/src/main/java/core/net/packet/out/Interface.java b/Server/src/main/core/net/packet/out/Interface.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/Interface.java rename to Server/src/main/core/net/packet/out/Interface.java diff --git a/Server/src/main/java/core/net/packet/out/InterfaceConfig.java b/Server/src/main/core/net/packet/out/InterfaceConfig.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/InterfaceConfig.java rename to Server/src/main/core/net/packet/out/InterfaceConfig.java 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/java/core/net/packet/out/LoginPacket.java b/Server/src/main/core/net/packet/out/LoginPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/LoginPacket.java rename to Server/src/main/core/net/packet/out/LoginPacket.java diff --git a/Server/src/main/java/core/net/packet/out/Logout.java b/Server/src/main/core/net/packet/out/Logout.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/Logout.java rename to Server/src/main/core/net/packet/out/Logout.java diff --git a/Server/src/main/java/core/net/packet/out/MinimapState.java b/Server/src/main/core/net/packet/out/MinimapState.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/MinimapState.java rename to Server/src/main/core/net/packet/out/MinimapState.java diff --git a/Server/src/main/java/core/net/packet/out/MusicPacket.java b/Server/src/main/core/net/packet/out/MusicPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/MusicPacket.java rename to Server/src/main/core/net/packet/out/MusicPacket.java diff --git a/Server/src/main/java/core/net/packet/out/PingPacket.java b/Server/src/main/core/net/packet/out/PingPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/PingPacket.java rename to Server/src/main/core/net/packet/out/PingPacket.java diff --git a/Server/src/main/java/core/net/packet/out/PositionedGraphic.java b/Server/src/main/core/net/packet/out/PositionedGraphic.java similarity index 96% rename from Server/src/main/java/core/net/packet/out/PositionedGraphic.java rename to Server/src/main/core/net/packet/out/PositionedGraphic.java index 2b7289157..6e381bb75 100644 --- a/Server/src/main/java/core/net/packet/out/PositionedGraphic.java +++ b/Server/src/main/core/net/packet/out/PositionedGraphic.java @@ -5,7 +5,6 @@ import core.game.world.update.flag.context.Graphics; import core.net.packet.IoBuffer; import core.net.packet.OutgoingPacket; import core.net.packet.context.PositionedGraphicContext; -import rs09.game.system.SystemLogger; /** * The positioned graphic outgoing packet. diff --git a/Server/src/main/java/core/net/packet/out/RepositionChild.java b/Server/src/main/core/net/packet/out/RepositionChild.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/RepositionChild.java rename to Server/src/main/core/net/packet/out/RepositionChild.java diff --git a/Server/src/main/core/net/packet/out/ResetInterface.java b/Server/src/main/core/net/packet/out/ResetInterface.java new file mode 100644 index 000000000..f980ca1f9 --- /dev/null +++ b/Server/src/main/core/net/packet/out/ResetInterface.java @@ -0,0 +1,15 @@ +package core.net.packet.out; + +import core.net.packet.IoBuffer; +import core.net.packet.OutgoingPacket; +import core.net.packet.context.InterfaceContext; + +public class ResetInterface implements OutgoingPacket { + @Override + public void send(InterfaceContext ic) { + IoBuffer buffer = new IoBuffer(149); + buffer.putShort(ic.getPlayer().getInterfaceManager().getPacketCount(1)); + buffer.putInt(ic.getInterfaceId()); + ic.getPlayer().getSession().write(buffer); + } +} diff --git a/Server/src/main/java/core/net/packet/out/RunEnergy.java b/Server/src/main/core/net/packet/out/RunEnergy.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/RunEnergy.java rename to Server/src/main/core/net/packet/out/RunEnergy.java diff --git a/Server/src/main/java/core/net/packet/out/RunScriptPacket.java b/Server/src/main/core/net/packet/out/RunScriptPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/RunScriptPacket.java rename to Server/src/main/core/net/packet/out/RunScriptPacket.java diff --git a/Server/src/main/java/core/net/packet/out/SetWalkOption.java b/Server/src/main/core/net/packet/out/SetWalkOption.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/SetWalkOption.java rename to Server/src/main/core/net/packet/out/SetWalkOption.java diff --git a/Server/src/main/java/core/net/packet/out/SkillLevel.java b/Server/src/main/core/net/packet/out/SkillLevel.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/SkillLevel.java rename to Server/src/main/core/net/packet/out/SkillLevel.java diff --git a/Server/src/main/java/core/net/packet/out/StringPacket.java b/Server/src/main/core/net/packet/out/StringPacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/StringPacket.java rename to Server/src/main/core/net/packet/out/StringPacket.java diff --git a/Server/src/main/java/core/net/packet/out/SystemUpdatePacket.java b/Server/src/main/core/net/packet/out/SystemUpdatePacket.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/SystemUpdatePacket.java rename to Server/src/main/core/net/packet/out/SystemUpdatePacket.java diff --git a/Server/src/main/java/core/net/packet/out/UpdateAreaPosition.java b/Server/src/main/core/net/packet/out/UpdateAreaPosition.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/UpdateAreaPosition.java rename to Server/src/main/core/net/packet/out/UpdateAreaPosition.java diff --git a/Server/src/main/java/core/net/packet/out/UpdateClanChat.java b/Server/src/main/core/net/packet/out/UpdateClanChat.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/UpdateClanChat.java rename to Server/src/main/core/net/packet/out/UpdateClanChat.java diff --git a/Server/src/main/java/core/net/packet/out/UpdateGroundItemAmount.java b/Server/src/main/core/net/packet/out/UpdateGroundItemAmount.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/UpdateGroundItemAmount.java rename to Server/src/main/core/net/packet/out/UpdateGroundItemAmount.java diff --git a/Server/src/main/java/core/net/packet/out/UpdateRandomFile.java b/Server/src/main/core/net/packet/out/UpdateRandomFile.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/UpdateRandomFile.java rename to Server/src/main/core/net/packet/out/UpdateRandomFile.java diff --git a/Server/src/main/java/core/net/packet/out/UpdateSceneGraph.java b/Server/src/main/core/net/packet/out/UpdateSceneGraph.java similarity index 92% rename from Server/src/main/java/core/net/packet/out/UpdateSceneGraph.java rename to Server/src/main/core/net/packet/out/UpdateSceneGraph.java index 489a6cbeb..a93435b18 100644 --- a/Server/src/main/java/core/net/packet/out/UpdateSceneGraph.java +++ b/Server/src/main/core/net/packet/out/UpdateSceneGraph.java @@ -5,7 +5,7 @@ import core.net.packet.IoBuffer; import core.net.packet.OutgoingPacket; import core.net.packet.PacketHeader; import core.net.packet.context.SceneGraphContext; -import rs09.game.system.config.XteaParser; +import core.game.system.config.XteaParser; /** * The update scene graph outgoing packet. @@ -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/java/core/net/packet/out/Varbit.java b/Server/src/main/core/net/packet/out/Varbit.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/Varbit.java rename to Server/src/main/core/net/packet/out/Varbit.java diff --git a/Server/src/main/core/net/packet/out/VarcUpdate.java b/Server/src/main/core/net/packet/out/VarcUpdate.java new file mode 100644 index 000000000..f53637849 --- /dev/null +++ b/Server/src/main/core/net/packet/out/VarcUpdate.java @@ -0,0 +1,26 @@ +package core.net.packet.out; + +import core.game.node.entity.player.Player; +import core.net.packet.IoBuffer; +import core.net.packet.OutgoingPacket; +import core.net.packet.context.VarcUpdateContext; + +public class VarcUpdate implements OutgoingPacket { + @Override + public void send(VarcUpdateContext varcUpdateContext) { + Player player = varcUpdateContext.getPlayer(); + if(varcUpdateContext.value <= 255) { + IoBuffer buffer = new IoBuffer(65); + buffer.putLEShort(player.getInterfaceManager().getPacketCount(1)); + buffer.putC((byte) varcUpdateContext.value); + buffer.putLEShortA(varcUpdateContext.varcId); + player.getSession().write(buffer); + } else { + IoBuffer buffer = new IoBuffer(69); + buffer.putLEShortA(player.getInterfaceManager().getPacketCount(1)); + buffer.putInt(varcUpdateContext.value); + buffer.putShortA(varcUpdateContext.varcId); + player.getSession().write(buffer); + } + } +} diff --git a/Server/src/main/java/core/net/packet/out/WeightUpdate.java b/Server/src/main/core/net/packet/out/WeightUpdate.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/WeightUpdate.java rename to Server/src/main/core/net/packet/out/WeightUpdate.java diff --git a/Server/src/main/java/core/net/packet/out/WindowsPane.java b/Server/src/main/core/net/packet/out/WindowsPane.java similarity index 100% rename from Server/src/main/java/core/net/packet/out/WindowsPane.java rename to Server/src/main/core/net/packet/out/WindowsPane.java diff --git a/Server/src/main/java/core/net/producer/GameEventProducer.java b/Server/src/main/core/net/producer/GameEventProducer.java similarity index 100% rename from Server/src/main/java/core/net/producer/GameEventProducer.java rename to Server/src/main/core/net/producer/GameEventProducer.java diff --git a/Server/src/main/java/core/net/producer/HSEventProducer.java b/Server/src/main/core/net/producer/HSEventProducer.java similarity index 100% rename from Server/src/main/java/core/net/producer/HSEventProducer.java rename to Server/src/main/core/net/producer/HSEventProducer.java diff --git a/Server/src/main/java/core/net/producer/JS5EventProducer.java b/Server/src/main/core/net/producer/JS5EventProducer.java similarity index 100% rename from Server/src/main/java/core/net/producer/JS5EventProducer.java rename to Server/src/main/core/net/producer/JS5EventProducer.java diff --git a/Server/src/main/java/core/net/producer/LoginEventProducer.java b/Server/src/main/core/net/producer/LoginEventProducer.java similarity index 94% rename from Server/src/main/java/core/net/producer/LoginEventProducer.java rename to Server/src/main/core/net/producer/LoginEventProducer.java index 59c81fa5a..a3c2e0fce 100644 --- a/Server/src/main/java/core/net/producer/LoginEventProducer.java +++ b/Server/src/main/core/net/producer/LoginEventProducer.java @@ -5,7 +5,7 @@ import core.net.IoReadEvent; import core.net.IoSession; import core.net.IoWriteEvent; import core.net.event.LoginWriteEvent; -import rs09.net.event.LoginReadEvent; +import core.net.event.LoginReadEvent; import java.nio.ByteBuffer; diff --git a/Server/src/main/java/core/net/producer/MSEventProducer.java b/Server/src/main/core/net/producer/MSEventProducer.java similarity index 100% rename from Server/src/main/java/core/net/producer/MSEventProducer.java rename to Server/src/main/core/net/producer/MSEventProducer.java diff --git a/Server/src/main/java/core/net/producer/MSHSEventProducer.java b/Server/src/main/core/net/producer/MSHSEventProducer.java similarity index 100% rename from Server/src/main/java/core/net/producer/MSHSEventProducer.java rename to Server/src/main/core/net/producer/MSHSEventProducer.java diff --git a/Server/src/main/java/core/net/producer/RegistryEventProducer.java b/Server/src/main/core/net/producer/RegistryEventProducer.java similarity index 100% rename from Server/src/main/java/core/net/producer/RegistryEventProducer.java rename to Server/src/main/core/net/producer/RegistryEventProducer.java diff --git a/Server/src/main/core/net/registry/AccountRegister.java b/Server/src/main/core/net/registry/AccountRegister.java new file mode 100644 index 000000000..dff9f3fc0 --- /dev/null +++ b/Server/src/main/core/net/registry/AccountRegister.java @@ -0,0 +1,143 @@ +package core.net.registry; + +import core.cache.misc.buffer.ByteBufferUtils; +import core.game.system.task.Pulse; +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; + +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 + * + */ +public class AccountRegister { + /** + * The pattern compiler. + */ + private static final Pattern PATTERN = Pattern.compile("[a-z0-9_]{1,12}"); + + /** + * Reads the incoming opcode of an account register. + * @param session the session. + * @param opcode the opcode. + * @param buffer the buffer. + */ + public static void read(final IoSession session, int opcode, ByteBuffer buffer) { + int day,month,year,country; + UserAccountInfo info = UserAccountInfo.createDefault(); + switch (opcode) { + case 147://details + day = buffer.get(); + month = buffer.get(); + year = buffer.getShort(); + country = buffer.getShort(); + response(session, RegistryResponse.SUCCESS); + break; + case 186://username + final String username = ByteBufferUtils.getString(buffer).replace(" ", "_").toLowerCase().replace("|", ""); + info.setUsername(username); + if (username.length() <= 0 || username.length() > 12) { + response(session, RegistryResponse.INVALID_USERNAME); + break; + } + if (invalidUsername(username)) { + + response(session,RegistryResponse.INVALID_USERNAME); + break; + } + + if (!GameWorld.getAuthenticator().canCreateAccountWith(info)) { + response(session, RegistryResponse.NOT_AVAILBLE_USER); + return; + } + response(session, RegistryResponse.SUCCESS); + break; + case 36://Register details + 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) + log(AccountRegister.class, Log.ERR, "Decryption failed during registration :("); + response(session, RegistryResponse.CANNOT_CREATE); + break; + } + buffer.getShort(); // random data + int revision = buffer.getShort();//revision? + if (revision != Constants.REVISION) { + response(session, RegistryResponse.CANNOT_CREATE); + break; + } + final String name = ByteBufferUtils.getString(buffer).replace(" ", "_").toLowerCase().replace("|", ""); + buffer.getInt(); + String password = ByteBufferUtils.getString(buffer); + info.setUsername(name); + info.setPassword(password); + if (password.length() < 5 || password.length() > 20) { + response(session, RegistryResponse.INVALID_PASS_LENGTH); + break; + } + if (password.equals(name)) { + response(session, RegistryResponse.PASS_SIMILAR_TO_USER); + break; + } + if (invalidUsername(name)) { + response(session, RegistryResponse.INVALID_USERNAME); + break; + } + buffer.getInt(); + buffer.getShort(); + day = buffer.get(); + month = buffer.get(); + buffer.getInt(); + year = buffer.getShort(); + country = buffer.getShort(); + buffer.getInt(); + if (!GameWorld.getAuthenticator().canCreateAccountWith(info)) { + response(session, RegistryResponse.CANNOT_CREATE); + return; + } + GameWorld.getAuthenticator().createAccountWith(info); + GameWorld.getPulser().submit(new Pulse() { + @Override + public boolean pulse() { + response(session, RegistryResponse.SUCCESS); + return true; + } + }); + break; + default: + log(AccountRegister.class, Log.ERR, "Unhandled account registry opcode = " + opcode); + break; + } + } + + /** + * Sends a registry response code. + * @param response the response. + */ + private static void response(IoSession session, RegistryResponse response) { + ByteBuffer buf = ByteBuffer.allocate(100); + buf.put((byte) response.getId()); + session.queue(buf.flip()); + } + + /** + * Checks if a username is valid. + * @return {@code True} if so. + */ + public static boolean invalidUsername(final String username) { + Matcher matcher = PATTERN.matcher(username); + return !matcher.matches(); + } +} diff --git a/Server/src/main/java/core/net/registry/RegistryDetails.java b/Server/src/main/core/net/registry/RegistryDetails.java similarity index 100% rename from Server/src/main/java/core/net/registry/RegistryDetails.java rename to Server/src/main/core/net/registry/RegistryDetails.java diff --git a/Server/src/main/java/core/net/registry/RegistryResponse.java b/Server/src/main/core/net/registry/RegistryResponse.java similarity index 100% rename from Server/src/main/java/core/net/registry/RegistryResponse.java rename to Server/src/main/core/net/registry/RegistryResponse.java 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 new file mode 100644 index 000000000..2ad6e5f4b --- /dev/null +++ b/Server/src/main/core/plugin/ClassScanner.kt @@ -0,0 +1,228 @@ +package core.plugin + +import core.ServerConstants +import core.api.* +import core.game.activity.ActivityManager +import core.game.activity.ActivityPlugin +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.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 + +/** + * A class used to reflectively scan the classpath and load classes + * @author Ceikry + */ +object ClassScanner { + var disabledPlugins = HashMap() + /** + * The amount of content interfaces loaded. + */ + var amountLoaded = 0 + private set + + var numPlugins = 0 + private set + + /** + * The currently loaded plugin names. + */ + private var loadedPlugins: MutableList? = ArrayList() + + /** + * The last loaded plugin. + */ + private val lastLoaded: String? = null + + lateinit var scanResults: ScanResult + + @JvmStatic fun scanClasspath() { + scanResults = ClassGraph().enableClassInfo().enableAnnotationInfo().scan() + } + + /** + * Scan the classpath for reflection-loaded content classes such as listeners, "plugins", etc + */ + @JvmStatic + fun loadPureInterfaces() { + try { + loadContentInterfacesFrom(scanResults) + logStartup("Loaded $amountLoaded content interfaces.") + } catch (t: Throwable) { + log(this::class.java, Log.ERR, "Error initializing Plugins -> " + t.localizedMessage + " for file -> " + lastLoaded) + t.printStackTrace() + } catch (e: Exception) { + 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) + if(clazz is StartupListener) GameWorld.startupListeners.add(clazz) + if(clazz is ShutdownListener) GameWorld.shutdownListeners.add(clazz) + if(clazz is PersistWorld) GameWorld.worldPersists.add(clazz) + 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) + } + if(clazz is MapArea) + { + val zone = object : MapZone(clazz.javaClass.simpleName + "MapArea", true, *clazz.getRestrictions()){ + override fun enter(e: Entity?): Boolean { + clazz.areaEnter(e ?: return super.enter(null)) + return super.enter(e) + } + + override fun leave(e: Entity?, logout: Boolean): Boolean { + clazz.areaLeave(e ?: return super.leave(null, logout), logout) + return super.leave(e, logout) + } + + override fun move(e: Entity?, from: Location?, to: Location?): Boolean { + if(e != null && from != null && to != null) clazz.entityStep(e, to, from) + return super.move(e, from, to) + } + } + for(border in clazz.defineAreaBorders()) zone.register(border) + ZoneBuilder.configure(zone) + log(this::class.java, Log.FINE, "Configured zone: ${clazz.javaClass.simpleName + "MapArea"}") + MapArea.zoneMaps[clazz.javaClass.simpleName + "MapArea"] = zone + } + amountLoaded++ + } catch (e: Exception) { + log(this::class.java, Log.ERR, "Error loading content: ${it.simpleName}, ${e.localizedMessage}") + e.printStackTrace() + } + } + } + + @JvmStatic + fun loadSideEffectfulPlugins() { + loadedPlugins!!.clear() + loadPluginsFrom(scanResults) + logStartup("We still have $numPlugins legacy plugins being loaded.") + } + + private fun loadPluginsFrom(scanResults: ScanResult) { + scanResults.getClassesWithAnnotation("core.plugin.Initializable").forEach(Consumer { p: ClassInfo -> + try { + val clazz = p.loadClass().getDeclaredConstructor().newInstance() + if (clazz is Plugin<*>) { + definePlugin(clazz) + } + } catch (t: Throwable) { + 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) { + log(this::class.java, Log.ERR, + "Make sure the constructor signature matches " + + "`${p.simpleName}(player: Player? = null) : DialoguePlugin(player)'." + ) + } + + t.printStackTrace() + } + }) + + scanResults.getClassesWithAnnotation("core.game.bots.PlayerCompatible").forEach { res -> + val description = res.getAnnotationInfo("core.game.bots.ScriptDescription").parameterValues[0].value as Array + val identifier = res.getAnnotationInfo("core.game.bots.ScriptIdentifier").parameterValues[0].value.toString() + val name = res.getAnnotationInfo("core.game.bots.ScriptName").parameterValues[0].value.toString() + PlayerScripts.identifierMap[identifier] = PlayerScripts.PlayerScript(identifier, description, name, res.loadClass()) + } + } + + /** + * Defines a list of plugins. + * @param plugins the plugins. + */ + @JvmStatic + fun definePlugins(vararg plugins: Plugin<*>) { + val pluginsLength = plugins.size + for (i in 0 until pluginsLength) { + val p = plugins[i] + definePlugin(p) + } + } + + /** + * Defines the plugin. + * @param plugin The plugin. + */ + @JvmStatic + fun definePlugin(plugin: Plugin<*>) { + try { + var manifest = plugin.javaClass.getAnnotation(PluginManifest::class.java) + if (manifest == null) { + manifest = plugin.javaClass.superclass.getAnnotation(PluginManifest::class.java) + } else { + if (disabledPlugins[manifest.name] != null) { + return + } + } + if (manifest == null || manifest.type == PluginType.ACTION) { + plugin.newInstance(null) + } else { + when (manifest.type) { + PluginType.DIALOGUE -> (plugin as core.game.dialogue.DialoguePlugin).init() + PluginType.ACTIVITY -> ActivityManager.register(plugin as ActivityPlugin) + PluginType.LOGIN -> LoginConfiguration.getLoginPlugins().add(plugin as Plugin) + PluginType.QUEST -> { + plugin.newInstance(null) + QuestRepository.register(plugin as Quest) + } + else -> log(this::class.java, Log.WARN, "Unknown Manifest: " + manifest.type) + } + } + numPlugins++ + } catch (e: Throwable) { + e.printStackTrace() + } + } +} diff --git a/Server/src/main/java/core/plugin/CorePluginTypes/ManagerPlugin.java b/Server/src/main/core/plugin/CorePluginTypes/ManagerPlugin.java similarity index 100% rename from Server/src/main/java/core/plugin/CorePluginTypes/ManagerPlugin.java rename to Server/src/main/core/plugin/CorePluginTypes/ManagerPlugin.java diff --git a/Server/src/main/java/core/plugin/CorePluginTypes/Managers.java b/Server/src/main/core/plugin/CorePluginTypes/Managers.java similarity index 91% rename from Server/src/main/java/core/plugin/CorePluginTypes/Managers.java rename to Server/src/main/core/plugin/CorePluginTypes/Managers.java index bfa1c1f1f..0ebb503a4 100644 --- a/Server/src/main/java/core/plugin/CorePluginTypes/Managers.java +++ b/Server/src/main/core/plugin/CorePluginTypes/Managers.java @@ -1,7 +1,5 @@ package core.plugin.CorePluginTypes; -import rs09.game.system.SystemLogger; - import java.util.ArrayList; import java.util.List; diff --git a/Server/src/main/java/core/plugin/CorePluginTypes/StartupPlugin.java b/Server/src/main/core/plugin/CorePluginTypes/StartupPlugin.java similarity index 100% rename from Server/src/main/java/core/plugin/CorePluginTypes/StartupPlugin.java rename to Server/src/main/core/plugin/CorePluginTypes/StartupPlugin.java diff --git a/Server/src/main/kotlin/rs09/plugin/CorePluginTypes/XPGainPlugins.kt b/Server/src/main/core/plugin/CorePluginTypes/XPGainPlugins.kt similarity index 94% rename from Server/src/main/kotlin/rs09/plugin/CorePluginTypes/XPGainPlugins.kt rename to Server/src/main/core/plugin/CorePluginTypes/XPGainPlugins.kt index 5659bf437..976ca3ded 100644 --- a/Server/src/main/kotlin/rs09/plugin/CorePluginTypes/XPGainPlugins.kt +++ b/Server/src/main/core/plugin/CorePluginTypes/XPGainPlugins.kt @@ -1,4 +1,4 @@ -package rs09.plugin.CorePluginTypes +package core.plugin.CorePluginTypes import core.game.node.entity.player.Player import core.plugin.Plugin diff --git a/Server/src/main/java/core/plugin/Initializable.java b/Server/src/main/core/plugin/Initializable.java similarity index 100% rename from Server/src/main/java/core/plugin/Initializable.java rename to Server/src/main/core/plugin/Initializable.java diff --git a/Server/src/main/java/core/plugin/Plugin.java b/Server/src/main/core/plugin/Plugin.java similarity index 100% rename from Server/src/main/java/core/plugin/Plugin.java rename to Server/src/main/core/plugin/Plugin.java diff --git a/Server/src/main/java/core/plugin/PluginManifest.java b/Server/src/main/core/plugin/PluginManifest.java similarity index 100% rename from Server/src/main/java/core/plugin/PluginManifest.java rename to Server/src/main/core/plugin/PluginManifest.java diff --git a/Server/src/main/java/core/plugin/PluginType.java b/Server/src/main/core/plugin/PluginType.java similarity index 100% rename from Server/src/main/java/core/plugin/PluginType.java rename to Server/src/main/core/plugin/PluginType.java diff --git a/Server/src/main/core/storage/AccountStorageProvider.kt b/Server/src/main/core/storage/AccountStorageProvider.kt new file mode 100644 index 000000000..6159a69a6 --- /dev/null +++ b/Server/src/main/core/storage/AccountStorageProvider.kt @@ -0,0 +1,13 @@ +package core.storage + +import core.auth.UserAccountInfo + +interface AccountStorageProvider { + fun checkUsernameTaken(username: String): Boolean + fun getAccountInfo(username: String): UserAccountInfo + fun getUsernamesWithIP(ip: String) : List + fun store(info: UserAccountInfo) + fun update(info: UserAccountInfo) + fun remove(info: UserAccountInfo) + fun getOnlineFriends(username: String) : List +} diff --git a/Server/src/main/core/storage/InMemoryStorageProvider.kt b/Server/src/main/core/storage/InMemoryStorageProvider.kt new file mode 100644 index 000000000..d0780cea0 --- /dev/null +++ b/Server/src/main/core/storage/InMemoryStorageProvider.kt @@ -0,0 +1,36 @@ +package core.storage + +import core.auth.UserAccountInfo + +class InMemoryStorageProvider : AccountStorageProvider { + private val storage = HashMap() + + override fun checkUsernameTaken(username: String): Boolean { + return storage[username] != null + } + + override fun getAccountInfo(username: String): UserAccountInfo { + 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 + } + + override fun update(info: UserAccountInfo) { + storage[info.username] = info + } + + override fun remove(info: UserAccountInfo) { + storage.remove(info.username) + } + + override fun getOnlineFriends(username: String): List { + return ArrayList() + } + + override fun getUsernamesWithIP(ip: String): List { + return ArrayList() + } +} diff --git a/Server/src/main/core/storage/SQLStorageProvider.kt b/Server/src/main/core/storage/SQLStorageProvider.kt new file mode 100644 index 000000000..e24e224e1 --- /dev/null +++ b/Server/src/main/core/storage/SQLStorageProvider.kt @@ -0,0 +1,271 @@ +package core.storage + +import core.game.system.communication.CommunicationInfo +import core.auth.UserAccountInfo +import core.game.world.repository.Repository +import java.lang.Long.max +import java.sql.* + +class SQLStorageProvider : AccountStorageProvider { + var connectionString = "" + var connectionUsername = "" + var connectionPassword = "" + + fun getConnection(): Connection { + Class.forName("com.mysql.cj.jdbc.Driver") + return DriverManager.getConnection(connectionString, connectionUsername, connectionPassword) + } + + fun configure(host: String, databaseName: String, username: String, password: String) { + connectionString = "jdbc:mysql://$host/$databaseName?useTimezone=true&serverTimezone=UTC" + connectionUsername = username + connectionPassword = password + } + + override fun checkUsernameTaken(username: String): Boolean { + val conn = getConnection() + conn.use { + val compiledUsernameQuery = it.prepareStatement(usernameQuery) + compiledUsernameQuery.setString(1, username.toLowerCase()) + val result = compiledUsernameQuery.executeQuery() + val exists = result.next() + result.close() + return exists + } + } + + override fun getAccountInfo(username: String): UserAccountInfo { + val conn = getConnection() + conn.use { con -> + val compiledAccountInfoQuery = con.prepareStatement(accountInfoQuery) + compiledAccountInfoQuery.setString(1, username.toLowerCase()) + val result = compiledAccountInfoQuery.executeQuery() + if (result.next()) { + val userData = UserAccountInfo.createDefault() + userData.username = username + + result.getString(2) ?.let { userData.password = it } + result.getInt(3) .let { userData.uid = it } + result.getInt(4) .let { userData.rights = it } + result.getInt(5) .let { userData.credits = it } + result.getString(6) ?.let { userData.ip = it } + result.getString(7) ?.let { userData.lastUsedIp = it } + result.getLong(8) .let { userData.muteEndTime = max(0L, it) } + result.getLong(9) .let { userData.banEndTime = max(0L, it) } + result.getString(10) ?.let { userData.contacts = it } + result.getString(11) ?.let { userData.blocked = it } + result.getString(12) ?.let { userData.clanName = it } + result.getString(13) ?.let { userData.currentClan = it } + result.getString(14) ?.let { userData.clanReqs = it } + result.getLong(15) .let { userData.timePlayed = max(0L, it) } + result.getLong(16) .let { userData.lastLogin = max(0L, it) } + result.getBoolean(17) .let { userData.online = it } + result.getTimestamp(18) .let { userData.joinDate = it ?: Timestamp(System.currentTimeMillis()) } + + userData.setInitialReferenceValues() + return userData + } else { + return UserAccountInfo.createDefault() + } + } + } + + override fun store(info: UserAccountInfo) { + val conn = getConnection() + conn.use { + val compiledInsertInfoQuery = it.prepareStatement(insertInfoQuery, Statement.RETURN_GENERATED_KEYS) + val emptyInfo = UserAccountInfo.createDefault() + if (info == emptyInfo) { + throw IllegalStateException("Tried to store empty data!") + } + emptyInfo.username = info.username + if (info == emptyInfo) { + throw IllegalStateException("Tried to store empty data!") + } + + if (checkUsernameTaken(info.username)) { + throw SQLDataException("Account already exists!") + } + compiledInsertInfoQuery.setString(1, info.username) + compiledInsertInfoQuery.setString(2, info.password) + compiledInsertInfoQuery.setInt(3, info.rights) + compiledInsertInfoQuery.setInt(4, info.credits) + compiledInsertInfoQuery.setString(5, info.ip) + compiledInsertInfoQuery.setString(6, info.ip) + compiledInsertInfoQuery.setLong(7, info.muteEndTime) + compiledInsertInfoQuery.setLong(8, info.banEndTime) + compiledInsertInfoQuery.setString(9, info.contacts) + compiledInsertInfoQuery.setString(10, info.blocked) + compiledInsertInfoQuery.setString(11, info.clanName) + compiledInsertInfoQuery.setString(12, info.currentClan) + compiledInsertInfoQuery.setString(13, info.clanReqs) + compiledInsertInfoQuery.setLong(14, info.timePlayed) + compiledInsertInfoQuery.setLong(15, info.lastLogin) + compiledInsertInfoQuery.setBoolean(16, info.online) + compiledInsertInfoQuery.setTimestamp(17, info.joinDate) + compiledInsertInfoQuery.execute() + val result = compiledInsertInfoQuery.generatedKeys + if (result.next()) { + info.uid = result.getInt(1) + } + info.setInitialReferenceValues() + } + } + + override fun update(info: UserAccountInfo) { + val (updatedFields, rawData) = info.getChangedFields() + val updateQueryString = buildUpdateInfoQuery(updatedFields) + val conn = getConnection() + + conn.use { + val compiledUpdateInfoQuery = it.prepareStatement(updateQueryString) + val emptyInfo = UserAccountInfo.createDefault() + if (info == emptyInfo) { + throw IllegalStateException("Tried to store empty data!") + } + emptyInfo.username = info.username + if (info == emptyInfo) { + throw IllegalStateException("Tried to store empty data!") + } + emptyInfo.password = info.password + if (info == emptyInfo) { + throw IllegalStateException("Tried to store empty data!") + } + if (updatedFields.isEmpty()) return + + var fieldIndex = 1 + for (updatedFieldIndex in updatedFields) { + when(val data = rawData[updatedFieldIndex]) { + is String -> compiledUpdateInfoQuery.setString(fieldIndex++, data) + is Int -> compiledUpdateInfoQuery.setInt(fieldIndex++, data) + is Boolean -> compiledUpdateInfoQuery.setBoolean(fieldIndex++, data) + is Long -> compiledUpdateInfoQuery.setLong(fieldIndex++, data) + } + } + compiledUpdateInfoQuery.setInt(fieldIndex, info.uid) + compiledUpdateInfoQuery.execute() + + info.initialValues = rawData + } + } + + override fun remove(info: UserAccountInfo) { + val conn = getConnection() + conn.use { + val compiledRemoveInfoQuery = it.prepareStatement(removeInfoQuery) + compiledRemoveInfoQuery.setString(1, info.username) + compiledRemoveInfoQuery.execute() + } + } + + override fun getOnlineFriends(username: String): List { + val friends = ArrayList() + var fTokens = "" + val conn = getConnection() + conn.use { + val friendsQuery = it.prepareStatement(GET_ALL_FRIENDS_QUERY) + friendsQuery.setString(1, username) + val f = friendsQuery.executeQuery() + if (f.next()) { + fTokens = f.getString(1) + } + } + val contacts = CommunicationInfo.parseContacts(fTokens) + for ((friendName, _) in contacts) if (Repository.getPlayerByName(friendName) != null) friends.add(friendName) + + return friends + } + + override fun getUsernamesWithIP(ip: String): List { + val conn = getConnection() + val res = ArrayList() + conn.use { + val query = it.prepareStatement(accountsByIPQuery) + query.setString(1, ip) + val r = query.executeQuery() + while (r.next()) { + res.add(r.getString(1)) + } + } + return res + } + + companion object { + private const val usernameQuery = "SELECT username FROM members WHERE username = ?;" + private const val removeInfoQuery = "DELETE FROM members WHERE username = ?;" + private const val accountsByIPQuery = "SELECT username FROM members WHERE lastGameIp = ?;" + private const val accountInfoQuery = "SELECT " + + "username," + + "password," + + "UID," + + "rights," + + "credits," + + "ip," + + "lastGameIp," + + "muteTime," + + "banTime," + + "contacts," + + "blocked," + + "clanName," + + "currentClan," + + "clanReqs," + + "timePlayed," + + "lastLogin," + + "online," + + "joined_date" + + " FROM members WHERE username = ?;" + private const val insertInfoQuery = "INSERT INTO members (" + + "username," + + "password," + + "rights," + + "credits," + + "ip," + + "lastGameIp," + + "muteTime," + + "banTime," + + "contacts," + + "blocked," + + "clanName," + + "currentClan," + + "clanReqs," + + "timePlayed," + + "lastLogin," + + "online," + + "joined_date" + + ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);" + + private fun buildUpdateInfoQuery(updatedIndices: ArrayList) : String { + val sb = StringBuilder("UPDATE members SET ") + val validIndices = updatedIndices.filter { it in UPDATE_QUERY_FIELDS.keys } + for ((index, updatedIndex) in validIndices.withIndex()) { + sb.append(UPDATE_QUERY_FIELDS[updatedIndex] ?: continue) + sb.append(" = ?") + if (index < validIndices.size - 1) + sb.append(",") + } + sb.append(" WHERE uid = ?;") + return sb.toString() + } + + //Maps UserAccountInfo updated field indices to the column name for SQL. Mini ORM I guess? + private val UPDATE_QUERY_FIELDS = mapOf( + 0 to "username", + 1 to "password", + 3 to "rights", + 4 to "credits", + 6 to "lastGameIp", + 7 to "muteTime", + 8 to "banTime", + 9 to "contacts", + 10 to "blocked", + 11 to "clanName", + 12 to "currentClan", + 13 to "clanReqs", + 14 to "timePlayed", + 15 to "lastLogin", + 16 to "online" + ) + + private val GET_ALL_FRIENDS_QUERY = "SELECT contacts FROM players WHERE username = ?;" + } +} diff --git a/Server/src/main/core/tools/CP1252.java b/Server/src/main/core/tools/CP1252.java new file mode 100644 index 000000000..c3f42e54a --- /dev/null +++ b/Server/src/main/core/tools/CP1252.java @@ -0,0 +1,49 @@ +package core.tools; + +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 = CHAR_MAP[out - 128]; + if (cp1252 == 0) { + cp1252 = 63; + } + out = 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/kotlin/rs09/tools/DialogueConst.kt b/Server/src/main/core/tools/DialogueConst.kt similarity index 93% rename from Server/src/main/kotlin/rs09/tools/DialogueConst.kt rename to Server/src/main/core/tools/DialogueConst.kt index bdd2e23f4..0e5a605ea 100644 --- a/Server/src/main/kotlin/rs09/tools/DialogueConst.kt +++ b/Server/src/main/core/tools/DialogueConst.kt @@ -1,4 +1,4 @@ -package rs09.tools +package core.tools const val START_DIALOGUE = 0 const val END_DIALOGUE = 1000000 diff --git a/Server/src/main/core/tools/Globals.kt b/Server/src/main/core/tools/Globals.kt new file mode 100644 index 000000000..cb070f762 --- /dev/null +++ b/Server/src/main/core/tools/Globals.kt @@ -0,0 +1,64 @@ +package core.tools + +const val RED = "" +const val ORANGE = "" +const val YELLOW = "" +const val GREEN = "" +const val BLUE = "" +const val PURPLE = "" + +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{ + return line.prepend("").append("") +} + +fun String.append(line: String): String{ + return this + line +} + +fun String.prepend(line: String): String{ + return line + this +} + +fun String.shuffle(): String{ + var new = "" + val old = this.split("").toMutableList() + for(i in this.indices){ + val c = old.random() + new += c.toString() + old.remove(c) + } + return new +} + +/** + * Prepends 'a' or 'an' to a noun depending on whether it starts with a vowel. + * @author bushtail + * @param noun the noun to check grammar rules against. + * @return either 'a $noun' or 'an $noun' depending on the first letter. + */ +fun prependArticle(noun : String) : String { + if(noun == null) return noun + val exceptions = hashMapOf("unicorn" to "a", "herb" to "an", "hour" to "an") + if(exceptions.contains(noun.lowercase())) { + return "${exceptions[noun.lowercase()]} $noun" + } + return when(noun[0]) { + 'a', 'e', 'i', 'o', 'u' -> "an $noun" + else -> "a $noun" + } +} + 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/java/core/tools/PlayerLoader.java b/Server/src/main/core/tools/PlayerLoader.java similarity index 92% rename from Server/src/main/java/core/tools/PlayerLoader.java rename to Server/src/main/core/tools/PlayerLoader.java index baebf05a9..ee37c855a 100644 --- a/Server/src/main/java/core/tools/PlayerLoader.java +++ b/Server/src/main/core/tools/PlayerLoader.java @@ -19,8 +19,8 @@ public final class PlayerLoader { * @return the player. */ public static Player getPlayerFile(String name) { - final PlayerDetails playerDetails = new PlayerDetails(name, ""); - playerDetails.parse(); + final PlayerDetails playerDetails = new PlayerDetails(name); + //playerDetails.parse(); final Player player = new Player(playerDetails); PlayerParser.parse(player); // GameWorld.getWorld().getAccountService().loadPlayer(player); @@ -34,8 +34,8 @@ public final class PlayerLoader { * @return the details */ public static PlayerDetails getPlayerDetailFile(String name) { - final PlayerDetails playerDetails = new PlayerDetails(name, ""); - playerDetails.parse(); + final PlayerDetails playerDetails = new PlayerDetails(name); + // playerDetails.parse(); return playerDetails; } } diff --git a/Server/src/main/java/core/tools/RSAKeyGen.java b/Server/src/main/core/tools/RSAKeyGen.java similarity index 100% rename from Server/src/main/java/core/tools/RSAKeyGen.java rename to Server/src/main/core/tools/RSAKeyGen.java diff --git a/Server/src/main/java/core/tools/RandomFunction.java b/Server/src/main/core/tools/RandomFunction.java similarity index 96% rename from Server/src/main/java/core/tools/RandomFunction.java rename to Server/src/main/core/tools/RandomFunction.java index 226046ee9..b74092a20 100644 --- a/Server/src/main/java/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) == 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; } @@ -293,4 +295,8 @@ public class RandomFunction { } return RANDOM.nextInt(value); } + + public static boolean nextBool() { + return RANDOM.nextBoolean(); + } } diff --git a/Server/src/main/core/tools/StringUtils.java b/Server/src/main/core/tools/StringUtils.java new file mode 100644 index 000000000..bc55800b6 --- /dev/null +++ b/Server/src/main/core/tools/StringUtils.java @@ -0,0 +1,597 @@ +package core.tools; + +import java.text.DecimalFormat; +import java.util.StringTokenizer; + +import core.net.packet.IoBuffer; + +/** + * The string utils. + * @author Emperor + */ +public final class StringUtils { + + /** + * The valid characters to be used in names/messages/... + */ + public static final char[] VALID_CHARS = { '_', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + /** + * Character mapping. + */ + public static char[] mapping = { '\n', '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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ' }; + /** + * The an int array241. + */ + public static int[] anIntArray241 = { 215, 203, 83, 158, 104, 101, 93, 84, 107, 103, 109, 95, 94, 98, 89, 86, 70, 41, 32, 27, 24, 23, -1, -2, 26, -3, -4, 31, 30, -5, -6, -7, 37, 38, 36, -8, -9, -10, 40, -11, -12, 55, 48, 46, 47, -13, -14, -15, 52, 51, -16, -17, 54, -18, -19, 63, 60, 59, -20, -21, 62, -22, -23, 67, 66, -24, -25, 69, -26, -27, 199, 132, 80, 77, 76, -28, -29, 79, -30, -31, 87, 85, -32, -33, -34, -35, -36, 197, -37, 91, -38, 134, -39, -40, -41, 97, -42, -43, 133, 106, -44, 117, -45, -46, 139, -47, -48, 110, -49, -50, 114, 113, -51, -52, 116, -53, -54, 135, 138, 136, 129, 125, 124, -55, -56, 130, 128, -57, -58, -59, 183, -60, -61, -62, -63, -64, 148, -65, -66, 153, 149, 145, 144, -67, -68, 147, -69, -70, -71, 152, 154, -72, -73, -74, 157, 171, -75, -76, 207, 184, 174, 167, 166, 165, -77, -78, -79, 172, 170, -80, -81, -82, 178, -83, 177, 182, -84, -85, 187, 181, -86, -87, -88, -89, 206, 221, -90, 189, -91, 198, 254, 262, 195, 196, -92, -93, -94, -95, -96, 252, 255, 250, -97, 211, 209, -98, -99, 212, -100, 213, -101, -102, -103, 224, -104, 232, 227, 220, 226, -105, -106, 246, 236, -107, 243, -108, -109, 231, 237, 235, -110, -111, 239, 238, -112, -113, -114, -115, -116, 241, -117, 244, -118, -119, 248, -120, 249, -121, -122, -123, 253, -124, -125, -126, -127, 259, 258, -128, -129, 261, -130, -131, 390, 327, 296, 281, 274, 271, 270, -132, -133, 273, -134, -135, 278, 277, -136, -137, 280, -138, -139, 289, 286, 285, -140, -141, 288, -142, -143, 293, 292, -144, -145, 295, -146, -147, 312, 305, 302, 301, -148, -149, 304, -150, -151, 309, 308, -152, -153, 311, -154, -155, 320, 317, 316, -156, -157, 319, -158, -159, 324, 323, -160, -161, 326, -162, -163, 359, 344, 337, 334, 333, -164, -165, 336, -166, -167, 341, 340, -168, -169, 343, -170, -171, 352, 349, 348, -172, -173, 351, -174, -175, 356, 355, -176, -177, 358, -178, -179, 375, 368, 365, 364, -180, -181, 367, -182, -183, 372, 371, -184, -185, 374, -186, -187, 383, 380, 379, -188, -189, 382, -190, -191, 387, 386, -192, -193, 389, -194, -195, 454, 423, 408, 401, 398, 397, -196, -197, 400, -198, -199, 405, 404, -200, -201, 407, -202, -203, 416, 413, 412, -204, -205, 415, -206, -207, 420, 419, -208, -209, 422, -210, -211, 439, 432, 429, 428, -212, -213, 431, -214, -215, 436, 435, -216, -217, 438, -218, -219, 447, 444, 443, -220, -221, 446, -222, -223, 451, 450, -224, -225, 453, -226, -227, 486, 471, 464, 461, 460, -228, -229, 463, -230, -231, 468, 467, -232, -233, 470, -234, -235, 479, 476, 475, -236, -237, 478, -238, -239, 483, 482, -240, -241, 485, -242, -243, 499, 495, 492, 491, -244, -245, 494, -246, -247, 497, -248, 502, -249, 506, 503, -250, -251, 505, -252, -253, 508, -254, 510, -255, -256, 0 }; + + /** + * Constructs a new {@code StringUtils.java} {@code Object}. + */ + public StringUtils() { + /** + * empty. + */ + } + + /** + * Method used to get the formatted number as a string from the integer + * inputed. + * @param amount the ammount. + * @return the string value. + */ + public static String getFormattedNumber(int amount) { + return new DecimalFormat("#,###,##0").format(amount).toString(); + } + + /** + * Checks if the string contains an invalid character. + * @param name The string. + * @return {@code True} if so. + */ + public static boolean containsInvalidCharacter(String name) { + boolean pass = false; + for (char c : name.toCharArray()) { + for (char vc : VALID_CHARS) { + if (vc == c) { + pass = true; + break; + } + } + if (!pass) { + return true; + } + } + return false; + } + + /** + * If a word starts with a e i o u h for grammar = a + n. + * @param word The word. + * @return If the a should have +n {@code true}. + */ + public static boolean isPlusN(String word) { + if (word == null) + return false; + String s = word.toLowerCase(); + return s.charAt(0) == 'a' || s.charAt(0) == 'e' || s.charAt(0) == 'i' || s.charAt(0) == 'o' || s.charAt(0) == 'u' || (s.charAt(0) == 'h' && s.length() > 1 && s.charAt(1) != 'e'); + } + + /** + * Adds an s to make a world plural. + * @param word the word + * @return the plural word + */ + public static String plusS(String word) { + if (word == null) { + return null; + } + if (word.endsWith("s")) { + return word; + } + if (word.endsWith("y")) { + return word.substring(0, word.length() - 1) + "ies"; + } + return word + "s"; + } + /** + * Method used to get the player name as a long. + * @param s the string. + * @return the long. + */ + public static long getPlayerNameLong(String s) { + long l = 0L; + for (int i = 0; i < s.length() && i < 12; i++) { + char c = s.charAt(i); + l *= 37L; + if (c >= 'A' && c <= 'Z') + l += (1 + c) - 65; + else if (c >= 'a' && c <= 'z') + l += (1 + c) - 97; + else if (c >= '0' && c <= '9') + l += (27 + c) - 48; + } + while (l % 37L == 0L && l != 0L) + l /= 37L; + return l; + } + + /** + * Method used to convert the string to a long. + * @param s the string. + * @return the long. + */ + public static long convertStringToLong(String s) { + if (s.length() > 20) { + throw new IllegalArgumentException("String is too long: " + s); + } + long out = 0L; + for (int i = 0; i < s.length(); ++i) { + long m = reducedMapping(s.codePointAt(i)); + if (m == -1) { + throw new IllegalArgumentException("Unmapped Character in String: " + s); + } + m <<= ((9 - i) * 6) + 4; + out |= m; + } + return out; + } + + /** + * Formats the string as display name. + * @param name The string to format. + * @return The formatted name. + */ + public static String formatDisplayName(String name) { + name = name.replaceAll("_", " "); + name = name.toLowerCase(); + StringBuilder newName = new StringBuilder(); + boolean wasSpace = true; + for (int i = 0; i < name.length(); i++) { + if (wasSpace) { + newName.append((new String() + name.charAt(i)).toUpperCase()); + wasSpace = false; + } else { + newName.append(name.charAt(i)); + } + if (name.charAt(i) == ' ') { + wasSpace = true; + } + } + return newName.toString(); + } + + /** + * Gets the byte for the character. + * @param c The character. + * @return The byte. + */ + private static final byte getByte(char c) { + byte charByte; + if (c > 0 && c < '\200' || c >= '\240' && c <= '\377') + charByte = (byte) c; + else if (c != '\u20AC') { + if (c != '\u201A') { + if (c != '\u0192') { + if (c == '\u201E') + charByte = -124; + else if (c != '\u2026') { + if (c != '\u2020') { + if (c == '\u2021') + charByte = -121; + else if (c == '\u02C6') + charByte = -120; + else if (c == '\u2030') + charByte = -119; + else if (c == '\u0160') + charByte = -118; + else if (c == '\u2039') + charByte = -117; + else if (c == '\u0152') + charByte = -116; + else if (c != '\u017D') { + if (c == '\u2018') + charByte = -111; + else if (c != '\u2019') { + if (c != '\u201C') { + if (c == '\u201D') + charByte = -108; + else if (c != '\u2022') { + if (c == '\u2013') + charByte = -106; + else if (c == '\u2014') + charByte = -105; + else if (c == '\u02DC') + charByte = -104; + else if (c == '\u2122') + charByte = -103; + else if (c != '\u0161') { + if (c == '\u203A') + charByte = -101; + else if (c != '\u0153') { + if (c == '\u017E') + charByte = -98; + else if (c != '\u0178') + charByte = 63; + else + charByte = -97; + } else + charByte = -100; + } else + charByte = -102; + } else + charByte = -107; + } else + charByte = -109; + } else + charByte = -110; + } else + charByte = -114; + } else + charByte = -122; + } else + charByte = -123; + } else + charByte = -125; + } else + charByte = -126; + } else + charByte = -128; + return charByte; + } + + /** + * Gets the double value of this string + * @param s The string. + * @return The double value. + */ + public static double getDouble(String s) { + s = s.replaceAll(", ", "").replaceAll(",", ""); + StringBuilder sb = new StringBuilder(); + char c; + boolean foundStart = false; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + if (Character.isDigit(c) || c == '-' || c == '.') { + sb.append(c); + foundStart = true; + } else if (foundStart) + break; + } + try { + double amount = Double.parseDouble(sb.toString()); + return amount; + } catch (NumberFormatException e) { + return 0.0; + } + } + + public static String[] splitIntoLine(String input, int maxCharInLine) { + + StringTokenizer tok = new StringTokenizer(input, " "); + StringBuilder output = new StringBuilder(input.length()); + int lineLen = 0; + while (tok.hasMoreTokens()) { + String word = tok.nextToken(); + + while (word.length() > maxCharInLine) { + output.append(word.substring(0, maxCharInLine - lineLen) + "\n"); + word = word.substring(maxCharInLine - lineLen); + lineLen = 0; + } + + if (lineLen + word.length() > maxCharInLine) { + output.append("\n"); + lineLen = 0; + } + output.append(word + " "); + + lineLen += word.length() + 1; + } + // output.split(); + // return output.toString(); + return output.toString().split("\n"); + } + + /** + * Gets the hash for the string. + * @param str The string. + * @return The hash. + */ + public static final int getNameHash(String str) { + str = str.toLowerCase(); + int hash = 0; + for (int index = 0; index < str.length(); index++) + hash = getByte(str.charAt(index)) + ((hash << 5) - hash); + return hash; + } + + /** + * Gets the string value of this string (all html/... removed). + * @param s The string. + * @return The string value. + */ + public static String getString(String s) { + String string = s.replaceAll("\\<.*?>", "").replaceAll(" ", "").replaceAll("Discontinued Item:", ""); + return string; + } + + /** + * Characters used to convert a String to a Long. + */ + public static char[] validChars = { '_', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + public static int[] anIntArray233 = { 0, 1024, 2048, 3072, 4096, 5120, 6144, 8192, 9216, 12288, 10240, 11264, 16384, 18432, 17408, 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720, 31744, 32768, 33792, 34816, 35840, 36864, 536870912, 16777216, 37888, 65536, 38912, 131072, 196608, 33554432, 524288, 1048576, 1572864, 262144, 67108864, 4194304, 134217728, 327680, 8388608, 2097152, 12582912, 13631488, 14680064, 15728640, 100663296, 101187584, 101711872, 101974016, 102760448, 102236160, 40960, 393216, 229376, 117440512, 104857600, 109051904, 201326592, 205520896, 209715200, 213909504, 106954752, 218103808, 226492416, 234881024, 222298112, 224395264, 268435456, 272629760, 276824064, 285212672, 289406976, 223346688, 293601280, 301989888, 318767104, 297795584, 298844160, 310378496, 102498304, 335544320, 299892736, 300941312, 301006848, 300974080, 39936, 301465600, 49152, 1073741824, 369098752, 402653184, 1342177280, 1610612736, 469762048, 1476395008, -2147483648, -1879048192, 352321536, 1543503872, -2013265920, -1610612736, -1342177280, -1073741824, -1543503872, 356515840, -1476395008, -805306368, -536870912, -268435456, 1577058304, -134217728, 360710144, -67108864, 364904448, 51200, 57344, 52224, 301203456, 53248, 54272, 55296, 56320, 301072384, 301073408, 301074432, 301075456, 301076480, 301077504, 301078528, 301079552, 301080576, 301081600, 301082624, 301083648, 301084672, 301085696, 301086720, 301087744, 301088768, 301089792, 301090816, 301091840, 301092864, 301093888, 301094912, 301095936, 301096960, 301097984, 301099008, 301100032, 301101056, 301102080, 301103104, 301104128, 301105152, 301106176, 301107200, 301108224, 301109248, 301110272, 301111296, 301112320, 301113344, 301114368, 301115392, 301116416, 301117440, 301118464, 301119488, 301120512, 301121536, 301122560, 301123584, 301124608, 301125632, 301126656, 301127680, 301128704, 301129728, 301130752, 301131776, 301132800, 301133824, 301134848, 301135872, 301136896, 301137920, 301138944, 301139968, 301140992, 301142016, 301143040, 301144064, 301145088, 301146112, 301147136, 301148160, 301149184, 301150208, 301151232, 301152256, 301153280, 301154304, 301155328, 301156352, 301157376, 301158400, 301159424, 301160448, 301161472, 301162496, 301163520, 301164544, 301165568, 301166592, 301167616, 301168640, 301169664, 301170688, 301171712, 301172736, 301173760, 301174784, 301175808, 301176832, 301177856, 301178880, 301179904, 301180928, 301181952, 301182976, 301184000, 301185024, 301186048, 301187072, 301188096, 301189120, 301190144, 301191168, 301193216, 301195264, 301194240, 301197312, 301198336, 301199360, 301201408, 301202432 }; + public static byte[] aByteArray235 = { 22, 22, 22, 22, 22, 22, 21, 22, 22, 20, 22, 22, 22, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 3, 8, 22, 16, 22, 16, 17, 7, 13, 13, 13, 16, 7, 10, 6, 16, 10, 11, 12, 12, 12, 12, 13, 13, 14, 14, 11, 14, 19, 15, 17, 8, 11, 9, 10, 10, 10, 10, 11, 10, 9, 7, 12, 11, 10, 10, 9, 10, 10, 12, 10, 9, 8, 12, 12, 9, 14, 8, 12, 17, 16, 17, 22, 13, 21, 4, 7, 6, 5, 3, 6, 6, 5, 4, 10, 7, 5, 6, 4, 4, 6, 10, 5, 4, 4, 5, 7, 6, 10, 6, 10, 22, 19, 22, 14, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 22, 21, 22, 22, 22, 21, 22, 22 }; + + /** + * Encrypt a string for the client. + */ + public static int encryptPlayerChat(byte[] is, int i_25_, int i_26_, int i_27_, byte[] is_28_) { + try { + i_27_ += i_25_; + int i_29_ = 0; + int i_30_ = i_26_ << 3; + for (; i_27_ > i_25_; i_25_++) { + int i_31_ = 0xff & is_28_[i_25_]; + int i_32_ = anIntArray233[i_31_]; + int i_33_ = aByteArray235[i_31_]; + int i_34_ = i_30_ >> 3; + int i_35_ = i_30_ & 0x7; + i_29_ &= (-i_35_ >> 31); + i_30_ += i_33_; + int i_36_ = ((-1 + (i_35_ - -i_33_)) >> 3) + i_34_; + i_35_ += 24; + is[i_34_] = (byte) (i_29_ = (i_29_ | (i_32_ >>> i_35_))); + if ((i_36_ ^ 0xffffffff) < (i_34_ ^ 0xffffffff)) { + i_34_++; + i_35_ -= 8; + is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); + if (i_36_ > i_34_) { + i_34_++; + i_35_ -= 8; + is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); + if (i_36_ > i_34_) { + i_35_ -= 8; + i_34_++; + is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); + if (i_34_ < i_36_) { + i_35_ -= 8; + i_34_++; + is[i_34_] = (byte) (i_29_ = i_32_ << -i_35_); + } + } + } + } + } + return -i_26_ + ((7 + i_30_) >> 3); + } catch (RuntimeException runtimeexception) { + } + return 0; + } + + /** + * Gets an integer value from a string. + * @param s the string. + * @return The value; + */ + public static int getValue(String s) { + s = s.replaceAll(", ", "").replaceAll(",", ""); + StringBuilder sb = new StringBuilder(); + char c; + boolean foundStart = false; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + if (Character.isDigit(c) || c == '-') { + sb.append(c); + foundStart = true; + } else if (foundStart) + break; + } + try { + int amount = Integer.parseInt(sb.toString()); + return amount; + } catch (NumberFormatException e) { + return 0; + } + } + + /** + * Checks if the account name is valid. + * @param name The account name. + * @return {@code True} if the account name is invalid. + */ + public static boolean invalidAccountName(String name) { + return name.length() > 12 || name.startsWith("_") || name.endsWith("_") || name.contains("__") || containsInvalidCharacter(name); + } + + /** + * Converts a long to a string. + * @param l The long. + * @return The string. + */ + public static String longToString(long l) { + try { + int i = 0; + char ac[] = new char[32]; + while (l != 0L) { + long l1 = l; + l /= 37L; + ac[11 - i++] = VALID_CHARS[(int) (l1 - l * 37L)]; + } + return new String(ac, 12 - i, i); + } catch (ArrayIndexOutOfBoundsException e) { + return ""; + } + } + + /** + * Packs a GJ2-String. + * @param position The position to start. + * @param buffer The byte-array. + * @param str The string. + * @return The size of the string. + */ + public static final int packGJString2(int position, byte[] buffer, String str) { + int length = str.length(); + int offset = position; + for (int index = 0; length > index; index++) { + int character = str.charAt(index); + 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; + } + + /** + * Method used to... + * @param x idk, + * @return the idk. + */ + public static long reducedMapping(int x) { + long out = -1; + if (x >= 97 && x <= 122) + out = x - 96; + else if (x >= 65 && x <= 90) + out = x - 37; + else if (x >= 48 && x <= 57) + out = x - +5; + else if (x == 32) + out = 63L; + return out; + } + + /** + * Converts a string to a long. + * @param s The string. + * @return The long. + */ + public static long stringToLong(String s) { + long l = 0L; + for (int i = 0; i < s.length() && i < 12; i++) { + char c = s.charAt(i); + l *= 37L; + if (c >= 'A' && c <= 'Z') + l += (1 + c) - 65; + else if (c >= 'a' && c <= 'z') + l += (1 + c) - 97; + else if (c >= '0' && c <= '9') + l += (27 + c) - 48; + } + while (l % 37L == 0L && l != 0L) + l /= 37L; + return l; + } + + /** + * Decrypt player chat. + * @param buffer The buffer + * @param totalChars The total chars + * @return The string + */ + public static String decryptPlayerChat(IoBuffer buffer, int totalChars) { + try { + if (totalChars == 0) + return ""; + int charsDecoded = 0; + int i_4_ = 0; + StringBuilder s = new StringBuilder(totalChars); + for (;;) { + byte i_7_ = (byte) buffer.get(); + if (i_7_ >= 0) + i_4_++; + else + i_4_ = anIntArray241[i_4_]; + int i_8_; + if ((i_8_ = anIntArray241[i_4_]) < 0) { + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); + if (totalChars <= ++charsDecoded) + break; + i_4_ = 0; + } + if (((i_7_ & 0x40) ^ 0xffffffff) != -1) + i_4_ = anIntArray241[i_4_]; + else + i_4_++; + if ((i_8_ = anIntArray241[i_4_]) < 0) { + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); + if (++charsDecoded >= totalChars) + break; + i_4_ = 0; + } + if ((0x20 & i_7_) == 0) + i_4_++; + else + i_4_ = anIntArray241[i_4_]; + if ((i_8_ = anIntArray241[i_4_]) < 0) { + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); + if (totalChars <= ++charsDecoded) + break; + i_4_ = 0; + } + if (((0x10 & i_7_) ^ 0xffffffff) == -1) + i_4_++; + else + i_4_ = anIntArray241[i_4_]; + if ((i_8_ = anIntArray241[i_4_]) < 0) { + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); + if (totalChars <= ++charsDecoded) + break; + + i_4_ = 0; + } + if (((0x8 & i_7_) ^ 0xffffffff) != -1) + i_4_ = anIntArray241[i_4_]; + else + i_4_++; + if ((i_8_ = anIntArray241[i_4_]) < 0) { + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); + if (++charsDecoded >= totalChars) + break; + i_4_ = 0; + } + if ((0x4 & i_7_) == 0) + i_4_++; + else + i_4_ = anIntArray241[i_4_]; + if ((i_8_ = anIntArray241[i_4_]) < 0) { + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); + if (totalChars <= ++charsDecoded) + break; + i_4_ = 0; + } + if (((i_7_ & 0x2) ^ 0xffffffff) != -1) + i_4_ = anIntArray241[i_4_]; + else + i_4_++; + if ((i_8_ = anIntArray241[i_4_]) < 0) { + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); + if (totalChars <= ++charsDecoded) + break; + i_4_ = 0; + } + if (((i_7_ & 0x1) ^ 0xffffffff) != -1) + i_4_ = anIntArray241[i_4_]; + else + i_4_++; + if ((i_8_ = anIntArray241[i_4_]) < 0) { + s.append(CP1252.getFromByte((byte) (i_8_ ^ 0xffffffff))); + if (++charsDecoded >= totalChars) + break; + i_4_ = 0; + } + } + return s.toString(); + } catch (RuntimeException runtimeexception) { + runtimeexception.printStackTrace(); + } + return ""; + } +} diff --git a/Server/src/main/core/tools/SystemLogger.kt b/Server/src/main/core/tools/SystemLogger.kt new file mode 100644 index 000000000..dc5b96560 --- /dev/null +++ b/Server/src/main/core/tools/SystemLogger.kt @@ -0,0 +1,101 @@ +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 core.game.world.GameWorld +import core.api.* +import java.io.OutputStream +import java.io.PrintStream +import java.text.SimpleDateFormat +import java.util.* + +/** + * Handles server log printing + * @author Ceikry + * Thanks to the awesome library made by AJ Alt + */ +object SystemLogger { + val t = Terminal() + val errT = t.forStdErr() + val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX") + + private fun getTime(): String{ + return "[" + formatter.format(Date(System.currentTimeMillis())) +"]" + } + + @JvmStatic + 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) + } + + Log.FINE -> { + if (ServerConstants.LOG_LEVEL < LogLevel.VERBOSE) + return + val msg = TextColors.gray("${getTime()}: [${clazz.simpleName}] $message") + t.println(msg) + } + + Log.INFO -> { + if (ServerConstants.LOG_LEVEL < LogLevel.DETAILED) + return + + val msg = "${getTime()}: [${clazz.simpleName}] $message" + t.println(msg) + } + + Log.WARN -> { + if (ServerConstants.LOG_LEVEL < LogLevel.CAUTIOUS) + return + + 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){ + log(this::class.java, Log.FINE, "[ GE] $message") + } + + @JvmStatic fun logStartup(message: String) + { + log(this::class.java, Log.FINE, "[STARTUP] $message") + } + + @JvmStatic fun logShutdown(message: String) + { + log(this::class.java, Log.FINE, "[SHUTDOWN] $message") + } + + fun logMS(s: String) { + log(this::class.java, Log.FINE, "[ MS] $s") + } +} + +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 new file mode 100644 index 000000000..f7e26e5ac --- /dev/null +++ b/Server/src/main/core/tools/TickUtils.kt @@ -0,0 +1,37 @@ +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 + return seconds / tick //returns an int with the closest number of ticks to the desired number of seconds possible +} + +fun ticksToSeconds(ticks: Int): Int { + val ticksMs = ticks * tick + return ticksMs / 1000 +} + +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/java/core/tools/TimeStamp.java b/Server/src/main/core/tools/TimeStamp.java similarity index 89% rename from Server/src/main/java/core/tools/TimeStamp.java rename to Server/src/main/core/tools/TimeStamp.java index 13934b1fc..f0762e846 100644 --- a/Server/src/main/java/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/java/core/tools/Vector3d.java b/Server/src/main/core/tools/Vector3d.java similarity index 100% rename from Server/src/main/java/core/tools/Vector3d.java rename to Server/src/main/core/tools/Vector3d.java diff --git a/Server/src/main/java/core/tools/mysql/Database.java b/Server/src/main/core/tools/mysql/Database.java similarity index 100% rename from Server/src/main/java/core/tools/mysql/Database.java rename to Server/src/main/core/tools/mysql/Database.java diff --git a/Server/src/main/java/core/tools/mysql/Query.java b/Server/src/main/core/tools/mysql/Query.java similarity index 100% rename from Server/src/main/java/core/tools/mysql/Query.java rename to Server/src/main/core/tools/mysql/Query.java diff --git a/Server/src/main/java/core/tools/mysql/Results.java b/Server/src/main/core/tools/mysql/Results.java similarity index 85% rename from Server/src/main/java/core/tools/mysql/Results.java rename to Server/src/main/core/tools/mysql/Results.java index 6efc2333b..b803566e6 100644 --- a/Server/src/main/java/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 new file mode 100644 index 000000000..29d48faa8 --- /dev/null +++ b/Server/src/main/core/worker/MajorUpdateWorker.kt @@ -0,0 +1,152 @@ +package core.worker + +import core.Server +import core.ServerConstants +import core.ServerStore +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 +import java.util.* +import kotlin.system.exitProcess + +/** + * Handles the running of pulses and writing of masks, etc + * @author Ceikry + */ +class MajorUpdateWorker { + var running: Boolean = false + var started = false + val sequence = UpdateSequence() + val sdf = SimpleDateFormat("HHmmss") + val worker = Thread { + Thread.currentThread().name = "Major Update Worker" + started = true + Thread.sleep(600L) + while (running) { + Grafana.startTick() + val start = System.currentTimeMillis() + Server.heartbeat() + + if (Server.networkReachability == NetworkReachability.Reachable) + handleTickActions() + else + tickOffline() + + for (player in Repository.players.filter { !it.isArtificial }) { + if (System.currentTimeMillis() - player.session.lastPing > 20000L) { + player?.session?.lastPing = Long.MAX_VALUE + 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.") + } + } + + //Handle daily restart if enabled + if (sdf.format(Date()).toInt() == 0) { + + if (GameWorld.checkDay() == 1) {//monday + ServerStore.clearWeeklyEntries() + } + + 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) + } + Repository.sendNews(colorize("%RSERVER GOING DOWN FOR DAILY RESTART IN ${5 - counter} MINUTE${if (counter < 4) "S" else ""}!")) + return false + } + }) + } + } + + 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)) + } + + log(this::class.java, Log.FINE, "Update worker stopped.") + } + + fun tickOffline() + { + Repository.disconnectionQueue.update() //continue processing disconnection queue + GameWorld.pulse() //continue incrementing the global tick count + } + + fun handleTickActions(skipPulseUpdate: Boolean = false) { + try { + var packetStart = System.currentTimeMillis() + 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() + } + } + } + + fun start() { + if (!started) { + running = true + worker.start() + } + } + + fun stop() { + running = false + worker.interrupt() + } +} diff --git a/Server/src/main/core/worker/ManagementEvents.kt b/Server/src/main/core/worker/ManagementEvents.kt new file mode 100644 index 000000000..5207f079c --- /dev/null +++ b/Server/src/main/core/worker/ManagementEvents.kt @@ -0,0 +1,369 @@ +package core.worker + +import core.api.sendMessage +import com.google.protobuf.Message +import core.game.system.communication.ClanEntry +import core.game.system.communication.ClanRank +import core.game.system.communication.ClanRepository +import core.game.system.communication.CommunicationInfo +import core.net.packet.PacketRepository +import core.net.packet.context.ContactContext +import core.net.packet.context.MessageContext +import core.net.packet.out.CommunicationMessage +import core.net.packet.out.ContactPackets +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import proto.management.ClanJoinNotification +import proto.management.ClanLeaveNotification +import proto.management.ClanMessage +import proto.management.FriendUpdate +import proto.management.JoinClanRequest +import proto.management.LeaveClanRequest +import proto.management.PlayerStatusUpdate +import proto.management.PrivateMessage +import proto.management.RequestClanInfo +import proto.management.RequestContactInfo +import proto.management.SendClanInfo +import proto.management.SendClanInfo.ClanMember +import proto.management.SendContactInfo +import proto.management.SendContactInfo.Contact +import core.ServerConstants +import core.auth.UserAccountInfo +import core.tools.SystemLogger +import core.game.world.GameWorld +import core.game.world.repository.Repository +import java.util.Deque +import java.util.LinkedList +import java.util.concurrent.BlockingDeque +import java.util.concurrent.LinkedBlockingDeque + +/** + * Processes management-related events e.g clan messages, etc. + */ +object ManagementEvents { + private var isRunning: Boolean = true + private val eventQueue: BlockingDeque = LinkedBlockingDeque() + private val waitingOnClanInfo = HashMap>() + private val hasRequestedClanInfo = HashMap() + + val job = GlobalScope.launch { + while (isRunning) { + val event = withContext(Dispatchers.IO) { eventQueue.take() } + try { + handleEvent(event) + handleLoggingFor(event) + } catch (ignored: Exception) {} + } + } + + private fun handleLoggingFor(event: Message) { + when (event) { + is PlayerStatusUpdate -> SystemLogger.logMS("${event.username} -(WLD)> ${event.world}") + is RequestContactInfo -> SystemLogger.logMS("${event.username} -> RQ CONTACT INFO") + is SendContactInfo -> SystemLogger.logMS("${event.username} <- SND CONTACT INFO") + is PrivateMessage -> SystemLogger.logMS("[PM] ${event.sender}->${event.receiver}: ${event.message}") + is ClanMessage -> SystemLogger.logMS("[CM:${event.clanName}] ${event.sender}: ${event.message}") + is JoinClanRequest -> SystemLogger.logMS("${event.username} +CL ${event.clanName}") + is LeaveClanRequest -> SystemLogger.logMS("${event.username} -CL ${event.clanName}") + is RequestClanInfo -> SystemLogger.logMS("REQUEST CLAN INFO: ${event.clanOwner}") + is SendClanInfo -> SystemLogger.logMS("RECEIVE CLAN INFO: ${event.clanOwner}->${event.clanName}") + is ClanJoinNotification -> SystemLogger.logMS("${event.username} JOINED CLAN ${event.clanName}") + is ClanLeaveNotification -> SystemLogger.logMS("${event.username} LEFT CLAN ${event.clanName}") + } + } + + @JvmStatic fun publish(event: Message) { + eventQueue.offer(event) + } + + private fun handleEvent(event: Message) { + when (event) { + + is PlayerStatusUpdate -> { + val notifiablePlayers = if (event.notifyFriendsOnly) { + GameWorld.accountStorage.getOnlineFriends(event.username) + } else { + Repository.playerNames.keys.toList() + }.filter { Repository.getPlayerByName(it)?.communication?.contacts?.containsKey(event.username) == true } + + for (playerName in notifiablePlayers) { + val p = Repository.getPlayerByName(playerName) ?: continue + p.communication.contacts[event.username]?.worldId = event.world + PacketRepository.send(ContactPackets::class.java, ContactContext(p, event.username, event.world)) + } + } + + is RequestContactInfo -> { + val response = SendContactInfo.newBuilder() + response.username = event.username + val info = GameWorld.accountStorage.getAccountInfo(event.username) + val contacts = CommunicationInfo.parseContacts(info.contacts) + + for ((username, contact) in contacts) { + val online = Repository.getPlayerByName(username) != null + val cbuild = Contact.newBuilder() + cbuild.username = username + cbuild.world = if (online) GameWorld.settings!!.worldId else 0 + cbuild.rank = contact.rank.ordinal + response.addContacts(cbuild) + } + + val blocked = info.blocked.split(",") + for (user in blocked) response.addBlocked(user) + + publish(response.build()) + } + + is SendContactInfo -> { + val p = Repository.getPlayerByName(event.username) ?: return + + PacketRepository.send( + ContactPackets::class.java, + ContactContext(p, ContactContext.UPDATE_STATE_TYPE) + ) + + p.communication.contacts.clear() + p.communication.blocked.clear() + + for (contact in event.contactsList) { + val c = core.game.system.communication.Contact(contact.username) + p.communication.contacts[contact.username] = c + c.worldId = contact.world + c.rank = ClanRank.values()[contact.rank] + PacketRepository.send( + ContactPackets::class.java, + ContactContext(p, contact.username, contact.world) + ) + } + + for (blocked in event.blockedList) { + p.communication.blocked.add(blocked) + } + + PacketRepository.send( + ContactPackets::class.java, + ContactContext(p, ContactContext.IGNORE_LIST_TYPE) + ) + + } + + is FriendUpdate -> { + val remove = event.type == FriendUpdate.Type.REMOVE + val f = Repository.getPlayerByName(event.friend) + val p = Repository.getPlayerByName(event.username) + val world = if (f != null) GameWorld.settings!!.worldId else 0 + } + + is PrivateMessage -> { + val sender = Repository.getPlayerByName(event.sender) + val receiver = Repository.getPlayerByName(event.receiver) + + if (sender != null) { + PacketRepository.send( + CommunicationMessage::class.java, + MessageContext(sender, event.receiver, event.rank, MessageContext.SEND_MESSAGE, event.message) + ) + } + + if (receiver != null) { + PacketRepository.send( + CommunicationMessage::class.java, + MessageContext(receiver, event.sender, event.rank, MessageContext.RECIEVE_MESSAGE, event.message) + ) + } + } + + is JoinClanRequest -> { + val p = Repository.getPlayerByName(event.username) ?: return + + if (shouldWaitForClanInfo(event.clanName)) { + queueUntilClanInfo(event.clanName, event) + return + } + + val clan = ClanRepository.get(event.clanName) + + if (clan == null) { + sendMessage(p, "The channel you tried to join does not exist.:clan:") + } else { + clan.enter(p) + p.communication.clan = clan + } + } + + is ClanJoinNotification -> { + if (event.world == GameWorld.settings!!.worldId) return + + if (shouldWaitForClanInfo(event.clanName)) { + queueUntilClanInfo(event.clanName, event) + return + } + + val clan = ClanRepository.get(event.clanName) + val entry = ClanEntry(event.username, event.world) + clan.players.add(entry) + clan.update() + } + + is LeaveClanRequest -> { + val p = Repository.getPlayerByName(event.username) ?: return + + if (shouldWaitForClanInfo(event.clanName)) { + queueUntilClanInfo(event.clanName, event) + return + } + + val clan = ClanRepository.get(event.clanName) + + if (clan == null) { + sendMessage(p, "Error leaving clan. Please relog.") + } else { + clan.leave(p, true) + p.details.communication.clan = null + } + } + + is ClanLeaveNotification -> { + if (shouldWaitForClanInfo(event.clanName)) { + queueUntilClanInfo(event.clanName, event) + return + } + + val clan = ClanRepository.get(event.clanName) + val entry = clan.players.firstOrNull { it.name.equals(event.username) } ?: return + clan.players.remove(entry) + clan.update() + } + + is RequestClanInfo -> { + val clan = ClanRepository.get(event.clanOwner) + val response = SendClanInfo.newBuilder() + + if (clan == null) { + response.hasInfo = false + response.clanOwner = event.clanOwner + } else { + response.hasInfo = true + response.clanName = clan.name + response.clanOwner = event.clanOwner + + for (member in clan.players) { + val cmBuilder = ClanMember.newBuilder() + cmBuilder.username = member.name + cmBuilder.world = member.worldId + cmBuilder.rank = (clan.ranks[member.name] ?: ClanRank.NONE).ordinal + response.addMembers(cmBuilder) + } + } + + publish(response.build()) + } + + is SendClanInfo -> { + if (event.hasInfo) { + initializeClanFrom(event) + } else { + var info = GameWorld.accountStorage.getAccountInfo(event.clanOwner) + if (info.clanName.isNotEmpty()) { + initializeClanWith(info) + } else { + SystemLogger.logMS("Creating default server clan") + 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.toLowerCase() + info.password = ServerConstants.MS_SECRET_KEY + info.rights = 2 + GameWorld.authenticator.createAccountWith(info) + info = GameWorld.accountStorage.getAccountInfo(event.clanOwner) + } + + info.clanName = "Global" + info.clanReqs = "-1,-1,7,7" //Any join, any message, owner kick, owner loot + GameWorld.accountStorage.update(info) + initializeClanWith(info) + } + } + } + + val queuedEvents = waitingOnClanInfo[event.clanOwner] ?: return + while (queuedEvents.peek() != null) { + publish(queuedEvents.pop()) + } + } + + is ClanMessage -> { + if (shouldWaitForClanInfo(event.clanName)) { + queueUntilClanInfo(event.clanName, event) + return + } + + val clan = ClanRepository.get(event.clanName) + + for (member in clan.players.filter { it.player != null }) { + PacketRepository.send( + CommunicationMessage::class.java, + MessageContext(member.player, event.sender, event.rank, MessageContext.CLAN_MESSAGE, event.message) + ) + } + } + + } + } + + private fun initializeClanFrom(event: SendClanInfo) { + val clan = ClanRepository.getClans().getOrPut(event.clanOwner) { ClanRepository(event.clanOwner) } + clan.name = event.clanName + clan.joinRequirement = ClanRank.values()[event.joinRequirement] + clan.kickRequirement = ClanRank.values()[event.kickRequirement] + clan.messageRequirement = ClanRank.values()[event.messageRequirement] + clan.lootRequirement = ClanRank.values()[event.lootRequirement] + + for (member in event.membersList) { + val entry = ClanEntry(member.username, member.world) + clan.ranks[member.username] = ClanRank.values()[member.rank] + if (member.world == GameWorld.settings!!.worldId) { + val p = Repository.getPlayerByName(member.username) + entry.player = p + p?.communication?.clan = clan + } + clan.players.add(entry) + } + + clan.update() + } + + private fun initializeClanWith(info: UserAccountInfo) { + val reqs = CommunicationInfo.parseClanRequirements(info.clanReqs) + val c = ClanRepository(info.username) + val contacts = CommunicationInfo.parseContacts(info.contacts) + c.name = info.clanName + c.joinRequirement = reqs[0] + c.messageRequirement = reqs[1] + c.kickRequirement = reqs[2] + c.lootRequirement = reqs[3] + for ((username, contact) in contacts) { + c.ranks[username] = contact.rank + } + ClanRepository.getClans()[info.username] = c + } + + private fun queueUntilClanInfo(clanName: String, message: Message) { + val queue = waitingOnClanInfo.getOrPut(clanName) {LinkedList()} + queue.offer(message) + + if (hasRequestedClanInfo[clanName] == null) { + val request = RequestClanInfo.newBuilder() + request.clanOwner = clanName + request.world = GameWorld.settings!!.worldId + publish(request.build()) + hasRequestedClanInfo[clanName] = true + } + } + + private fun shouldWaitForClanInfo(clanName: String): Boolean { + return ClanRepository.get(clanName) == null && hasRequestedClanInfo[clanName] == null + } +} diff --git a/Server/src/main/java/core/ServerConstants.backup b/Server/src/main/java/core/ServerConstants.backup deleted file mode 100644 index 3c4d50610..000000000 --- a/Server/src/main/java/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/java/core/cache/AriosStore.java b/Server/src/main/java/core/cache/AriosStore.java deleted file mode 100644 index 45980957f..000000000 --- a/Server/src/main/java/core/cache/AriosStore.java +++ /dev/null @@ -1,193 +0,0 @@ -package core.cache; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileChannel.MapMode; -import java.util.HashMap; -import java.util.Map; - -import core.cache.misc.buffer.ByteBufferUtils; - -/** - * The server data storage. - * @author Emperor - */ -public final class AriosStore { - - /** - * The storage. - */ - private static Map storage = new HashMap<>(); - - /** - * If the store has initialized. - */ - private static boolean initialized; - - /** - * Initializes the store. - * @param path The file path. - */ - public static void init(String path) { - storage = new HashMap<>(); - File file = new File(path + "/dynamic_cache.keldagrim"); - if (file.exists()) { - try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { - FileChannel channel = raf.getChannel(); - ByteBuffer buffer = channel.map(MapMode.READ_WRITE, 0, channel.size()); - int size = buffer.getShort() & 0xFFFF; - for (int i = 0; i < size; i++) { - StoreFile store = new StoreFile(); - store.setDynamic(true); - String archive = ByteBufferUtils.getString(buffer); - byte[] data = new byte[buffer.getInt()]; - buffer.get(data); - store.setData(data); - storage.put(archive, store); - } - if (buffer.hasRemaining()) { - throw new IllegalStateException("Unable to read all dynamic data (size=" + size + ")!"); - } - channel.close(); - raf.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - initialized = true; - } - - /** - * Used for writing the static store. - * @param path The path. - */ - public static void createStaticStore(String path) { - write(path + "/static_cache.keldagrim", false); - } - - /** - * Writes all the dynamic storage files (on server termination). - * @param path The path. - */ - public static void dump(String path) { - write(path + "/dynamic_cache.keldagrim", true); - } - - /** - * Writes the store file to the given file path. - * @param filePath The file path. - * @param dynamic If the dynamic store is being written. - */ - public static void write(String filePath, boolean dynamic) { - if (!initialized) { - throw new IllegalStateException("Server store has not been initialized!"); - } - File f = new File(filePath); - if (f.exists()) { - f.delete(); - } - ByteBuffer buffer = ByteBuffer.allocate(1 << 28); - buffer.putShort((short) 0); - int size = 0; - for (String archive : storage.keySet()) { - StoreFile file = storage.get(archive); - if (file.isDynamic() != dynamic) { - continue; - } - size++; - ByteBuffer buf = file.data(); - ByteBufferUtils.putString(archive, buffer); - buffer.putInt(buf.remaining()); - buffer.put(buf); - } - buffer.putShort(0, (short) size); - buffer.flip(); - try (RandomAccessFile raf = new RandomAccessFile(f, "rw")) { - FileChannel channel = raf.getChannel(); - channel.write(buffer); - channel.close(); - raf.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Sets the archive data. - * @param archive The archive id. - * @param buffer The readable buffer. - */ - public static void setArchive(String archive, ByteBuffer buffer) { - setArchive(archive, buffer, true); - } - - /** - * Sets the archive data. - * @param archive The archive id. - * @param buffer The readable buffer. - * @param dynamic If the data changes during server runtime. - */ - public static void setArchive(String archive, ByteBuffer buffer, boolean dynamic) { - byte[] data = new byte[buffer.remaining()]; - buffer.get(data); - setArchive(archive, data, dynamic, true); - } - - /** - * Sets the archive data. - * @param archive The archive index. - * @param data The archive data. - * @param dynamic If the data changes during server runtime. - */ - public static void setArchive(String archive, byte[] data, boolean dynamic) { - setArchive(archive, data, dynamic, true); - } - - /** - * Sets the archive data. - * @param archive The archive index. - * @param data The archive data. - * @param dynamic If the data changes during server runtime. - * @param overwrite If the archive should be overwritten. - */ - public static void setArchive(String archive, byte[] data, boolean dynamic, boolean overwrite) { - StoreFile file = storage.get(archive); - if (file == null) { - storage.put(archive, file = new StoreFile()); - } else if (!overwrite) { - throw new IllegalStateException("Already contained archive " + archive + "!"); - } - file.setDynamic(dynamic); - file.setData(data); - } - - /** - * Gets the archive data for the given archive id. - * @param archive The archive index. - * @return The archive data. - */ - public static ByteBuffer getArchive(String archive) { - return get(archive).data(); - } - - /** - * Sets the archive file. - * @param archive The archive. - * @param file The file. - */ - public static void set(String archive, StoreFile file) { - storage.put(archive, file); - } - - /** - * Gets the store file for the given archive. - * @param archive The archive id. - * @return The store file. - */ - public static StoreFile get(String archive) { - return storage.get(archive); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/cache/StoreFile.java b/Server/src/main/java/core/cache/StoreFile.java deleted file mode 100644 index 50e1728d0..000000000 --- a/Server/src/main/java/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/java/core/cache/crypto/ISAACCipher.java b/Server/src/main/java/core/cache/crypto/ISAACCipher.java deleted file mode 100644 index 16cd85b18..000000000 --- a/Server/src/main/java/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/java/core/cache/crypto/ISAACPair.java b/Server/src/main/java/core/cache/crypto/ISAACPair.java deleted file mode 100644 index 83c4654b9..000000000 --- a/Server/src/main/java/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/java/core/cache/crypto/XTEACryption.java b/Server/src/main/java/core/cache/crypto/XTEACryption.java deleted file mode 100644 index 7ed799e58..000000000 --- a/Server/src/main/java/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/java/core/cache/def/impl/GraphicDefinition.java b/Server/src/main/java/core/cache/def/impl/GraphicDefinition.java deleted file mode 100644 index e554a3b1e..000000000 --- a/Server/src/main/java/core/cache/def/impl/GraphicDefinition.java +++ /dev/null @@ -1,189 +0,0 @@ -package core.cache.def.impl; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -import rs09.ServerConstants; -import core.cache.Cache; - -/** - * Represents a Graphic's definition. - * @author Jagex - */ -public class GraphicDefinition { - - public short[] aShortArray1435; - public short[] aShortArray1438; - public int anInt1440; - public boolean aBoolean1442; - public int defaultModel; - public int anInt1446; - public boolean aBoolean1448 = false; - public int anInt1449; - public int animationId; - public int anInt1451; - public int graphicsId; - public int anInt1454; - public short[] aShortArray1455; - public short[] aShortArray1456; - - // added - public byte byteValue; - // added - public int intValue; - - /** - * The definitions mapping. - */ - private static final Map graphicDefinitions = new HashMap<>(); - - /** - * Gets the graphic definition for the given graphic id. - * @param gfxId The graphic id. - * @return The definition. - */ - public static final GraphicDefinition forId(int gfxId) { - GraphicDefinition def = graphicDefinitions.get(gfxId); - if (def != null) { - return def; - } - byte[] data = Cache.getIndexes()[21].getFileData(gfxId >>> 8, gfxId & 0xff); - def = new GraphicDefinition(); - def.graphicsId = gfxId; - if (data != null) { - def.readValueLoop(ByteBuffer.wrap(data)); - } - graphicDefinitions.put(gfxId, def); - return def; - } - - /** - * The main method, used for running a graphic definition search. - * @param s The arguments cast on runtime. - */ - public static final void main(String... s) { - try { - Cache.init(ServerConstants.CACHE_PATH); - } catch (Throwable e) { - e.printStackTrace(); - } - // 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 + "]."); - } - } - } - - /** - * Reads and handles all data from the input stream. - * @param buffer The input stream. - */ - private void readValueLoop(ByteBuffer buffer) { - for (;;) { - int opcode = buffer.get() & 0xFF; - if (opcode == 0) { - break; - } - readValues(buffer, opcode); - } - } - - /** - * Reads the opcode values from the input stream. - * @param buffer The input stream. - * @param opcode The opcode to handle. - */ - public void readValues(ByteBuffer buffer, int opcode) { - if (opcode != 1) { - if (opcode == 2) - animationId = buffer.getShort(); - else if (opcode == 4) - anInt1446 = buffer.getShort() & 0xFFFF; - else if (opcode != 5) { - if ((opcode ^ 0xffffffff) != -7) { - if (opcode == 7) - anInt1440 = buffer.get() & 0xFF; - else if ((opcode ^ 0xffffffff) == -9) - anInt1451 = buffer.get() & 0xFF; - else if (opcode != 9) { - if (opcode != 10) { - if (opcode == 11) { // added opcode - // aBoolean1442 = true; - byteValue = (byte) 1; - } else if (opcode == 12) { // added opcode - // aBoolean1442 = true; - byteValue = (byte) 4; - } else if (opcode == 13) { // added opcode - // aBoolean1442 = true; - byteValue = (byte) 5; - } else if (opcode == 14) { // added opcode - // aBoolean1442 = true; - // aByte2856 = 2; - byteValue = (byte) 2; - intValue = (buffer.get() & 0xFF) * 256; - } else if (opcode == 15) { - // aByte2856 = 3; - byteValue = (byte) 3; - intValue = buffer.getShort() & 0xFFFF; - } else if (opcode == 16) { - // aByte2856 = 3; - byteValue = (byte) 3; - intValue = buffer.getInt(); - } else if (opcode != 40) { - if ((opcode ^ 0xffffffff) == -42) { - int i = buffer.get() & 0xFF; - aShortArray1455 = new short[i]; - aShortArray1435 = new short[i]; - for (int i_0_ = 0; i > i_0_; i_0_++) { - aShortArray1455[i_0_] = (short) (buffer.getShort() & 0xFFFF); - aShortArray1435[i_0_] = (short) (buffer.getShort() & 0xFFFF); - } - } - } else { - int i = buffer.get() & 0xFF; - aShortArray1438 = new short[i]; - aShortArray1456 = new short[i]; - for (int i_1_ = 0; ((i ^ 0xffffffff) < (i_1_ ^ 0xffffffff)); i_1_++) { - aShortArray1438[i_1_] = (short) (buffer.getShort() & 0xFFFF); - aShortArray1456[i_1_] = (short) (buffer.getShort() & 0xFFFF); - } - } - } else - aBoolean1448 = true; - } else { - // aBoolean1442 = true; - byteValue = (byte) 3; - intValue = 8224; - } - } else - anInt1454 = buffer.getShort() & 0xFFFF; - } else - anInt1449 = buffer.getShort() & 0xFFFF; - } else - defaultModel = buffer.getShort(); - } - - /** - * Constructs a new {@code GraphicDefinition} {@code Object}. - */ - public GraphicDefinition() { - byteValue = 0; - intValue = -1; - anInt1446 = 128; - aBoolean1442 = false; - anInt1449 = 128; - anInt1451 = 0; - animationId = -1; - anInt1454 = 0; - anInt1440 = 0; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/cache/def/impl/ItemDefinition.java b/Server/src/main/java/core/cache/def/impl/ItemDefinition.java deleted file mode 100644 index 80c4c15b7..000000000 --- a/Server/src/main/java/core/cache/def/impl/ItemDefinition.java +++ /dev/null @@ -1,1664 +0,0 @@ -package core.cache.def.impl; - -import core.cache.Cache; -import core.cache.def.Definition; -import core.cache.misc.buffer.ByteBufferUtils; -import core.game.container.Container; -import core.game.content.global.action.DropItemHandler; -import core.game.ge.GrandExchangeDatabase; -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.tools.StringUtils; -import rs09.game.system.SystemLogger; -import rs09.game.system.config.ItemConfigParser; - -import java.nio.ByteBuffer; -import java.text.DecimalFormat; -import java.util.HashMap; -import java.util.Map; - -/** - * Represents an item's definitions. - * @author Jagex - * @author Emperor - */ -public class ItemDefinition extends Definition { - - /** - * The item definitions mapping. - */ - private static final Map DEFINITIONS = new HashMap<>(); - - /** - * The default option handlers. - */ - private static final Map OPTION_HANDLERS = new HashMap<>(); - - /** - * The interface model id. - */ - private int interfaceModelId; - - /** - * The model zoom. - */ - private int modelZoom; - - /** - * The model rotation. - */ - private int modelRotationY; - - /** - * The model rotation. - */ - private int modelRotationX; - - /** - * The model offset. - */ - private int modelOffsetX; - - /** - * The model offset. - */ - private int modelOffsetY; - - /** - * If item is stackable. - */ - private boolean stackable; - - /** - * The item value. - */ - private int value; - - /** - * If item is members only. - */ - private boolean membersOnly; - - /** - * The male model wear id. - */ - private int maleWornModelId1 = -1; - - /** - * The female model wear id. - */ - private int femaleWornModelId1 = -1; - - /** - * The male model wear id. - */ - private int maleWornModelId2 = -1; - - /** - * The female wear model id. - */ - private int femaleWornModelId2 = -1; - - /** - * The male model wear id. - */ - private int maleWornModelId3 = -1; - - /** - * The female model wear id. - */ - private int femaleWornModelId3 = -1; - - /** - * The male model wear id. - */ - private int maleWornModelId4 = -1; - - /** - * The female wear model id. - */ - private int femaleWornModelId4 = -1; - - /** - * The ground actions. - */ - private String[] groundActions; - - /** - * The original model colors. - */ - private short[] originalModelColors; - - /** - * The modified model colors. - */ - private short[] modifiedModelColors; - - /** - * The texture color 1. - */ - private short[] textureColour1; - - /** - * The texture color 2. - */ - private short[] textureColour2; - - /** - * A unknown byte array. - */ - private byte[] unknownArray1; - - /** - * A unknown integer array. - */ - private int[] unknownArray2; - - /** - * A unknown integer array. - */ - private int[][] unknownArray3; - - /** - * If item is noted. - */ - private boolean unnoted = true; - - /** - * The colour equipment. - */ - private int colourEquip1 = -1; - - /** - * The colour equipment. - */ - private int colourEquip2; - - /** - * The note item. - */ - private int noteId = -1; - - /** - * The note template id. - */ - private int noteTemplateId = -1; - - /** - * The stackable ids. - */ - private int[] stackIds; - - /** - * The stackable amounts. - */ - private int[] stackAmounts; - - /** - * The team id. - */ - private int teamId; - - /** - * The lend id. - */ - private int lendId = -1; - - /** - * The lend template id. - */ - private int lendTemplateId = -1; - - /** - * The recolour id. - */ - private int recolourId = -1; - - /** - * The recolour template id. - */ - private int recolourTemplateId = -1; - - /** - * The equip id. - */ - private int equipId; - - /** - * The item requirements - */ - private HashMap itemRequirements; - - /** - * The clientscript data. - */ - private HashMap clientScriptData; - - /** - * The item type. - */ - private int itemType; - - /** - * Constructs a new {@code ItemDefinition} {@code Object}. - */ - public ItemDefinition() { - groundActions = new String[] { null, null, "take", null, null }; - 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. - */ - public static void parse() { - for (int itemId = 0; itemId < Cache.getItemDefinitionsSize(); itemId++) { - byte[] data = Cache.getIndexes()[19].getFileData(itemId >>> 8, itemId & 0xFF); - if (data == null) { - ItemDefinition.getDefinitions().put(itemId, new ItemDefinition()); - continue; - } - ItemDefinition def = ItemDefinition.parseDefinition(itemId, ByteBuffer.wrap(data)); - if (def == null) { - SystemLogger.logErr("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(); - } - - /** - * Gets an item definition. - * @param itemId The item's id. - * @return The item definition. - */ - public static ItemDefinition forId(int itemId) { - ItemDefinition def = DEFINITIONS.get(itemId); - if (def != null) { - return def; - } - return new ItemDefinition(); - } - - /** - * Parses an item's definitions. - * @param itemId The item id. - * @param buffer The buffer. - * @return The item definition. - */ - public static ItemDefinition parseDefinition(int itemId, ByteBuffer buffer) { - ItemDefinition def = new ItemDefinition(); - def.id = itemId; - while (true) { - int opcode = buffer.get() & 0xFF; - if (opcode == 0) { - break; - } else if (opcode == 1) { - def.interfaceModelId = buffer.getShort() & 0xFFFF; - } else if (opcode == 2) { - def.name = ByteBufferUtils.getString(buffer); - } else if (opcode == 3) { - def.handlers.put("examine", ByteBufferUtils.getString(buffer)); // Examine - // info. - } 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 == 7) { - def.modelOffsetX = buffer.getShort() & 0xFFFF; - if (def.modelOffsetX > 32767) - def.modelOffsetX -= 65536; - } else if (opcode == 8) { - def.modelOffsetY = buffer.getShort() & 0xFFFF; - if (def.modelOffsetY > 32767) { - def.modelOffsetY -= 65536; - } - } else if (opcode == 10) { - // buffer.getShort(); //10 is unused opcode. - } else if (opcode == 11) { - def.stackable = true; - } else if (opcode == 12) { - def.value = buffer.getInt(); - if (def.value == 0) { - def.value = 1; - } - } else if (opcode == 16) { - def.membersOnly = true; - } else if (opcode == 18) { - buffer.getShort(); - } else if (opcode == 23) { - def.maleWornModelId1 = buffer.getShort() & 0xFFFF; - // buffer.get(); - } else if (opcode == 24) { - def.femaleWornModelId1 = buffer.getShort() & 0xFFFF; - } else if (opcode == 25) { - def.maleWornModelId2 = buffer.getShort() & 0xFFFF; - // buffer.get(); - } else if (opcode == 26) { - def.femaleWornModelId2 = buffer.getShort() & 0xFFFF; - } else if (opcode >= 30 && opcode < 35) { - def.groundActions[opcode - 30] = ByteBufferUtils.getString(buffer); - } else if (opcode >= 35 && opcode < 40) { - def.options[opcode - 35] = ByteBufferUtils.getString(buffer); - } else if (opcode == 40) { - int length = buffer.get() & 0xFF; - def.originalModelColors = new short[length]; - def.modifiedModelColors = new short[length]; - for (int index = 0; index < length; index++) { - def.originalModelColors[index] = buffer.getShort(); - def.modifiedModelColors[index] = buffer.getShort(); - } - } else if (opcode == 41) { - int length = buffer.get() & 0xFF; - def.textureColour1 = new short[length]; - def.textureColour2 = new short[length]; - for (int index = 0; index < length; index++) { - def.textureColour1[index] = buffer.getShort(); - def.textureColour2[index] = buffer.getShort(); - } - } else if (opcode == 42) { - int length = buffer.get() & 0xFF; - def.unknownArray1 = new byte[length]; - for (int index = 0; index < length; index++) - def.unknownArray1[index] = buffer.get(); - } else if (opcode == 65) { - def.unnoted = true; - } else if (opcode == 78) { - def.colourEquip1 = buffer.getShort() & 0xFFFF; - } else if (opcode == 79) { - def.colourEquip2 = buffer.getShort() & 0xFFFF; - } else if (opcode == 90) { - def.setMaleWornModelId3(buffer.getShort()); - } else if (opcode == 91) { - def.setFemaleWornModelId3(buffer.getShort()); - } else if (opcode == 92) { - def.setMaleWornModelId4(buffer.getShort()); - } else if (opcode == 93) { - def.setFemaleWornModelId4(buffer.getShort()); - } else if (opcode == 95) { - buffer.getShort(); - } else if (opcode == 96) { - def.itemType = buffer.get(); - } else if (opcode == 97) { - def.noteId = buffer.getShort() & 0xFFFF; - } else if (opcode == 98) { - def.noteTemplateId = buffer.getShort() & 0xFFFF; - } else if (opcode >= 100 && opcode < 110) { - if (def.stackIds == null) { - def.stackIds = new int[10]; - def.stackAmounts = new int[10]; - } - def.stackIds[opcode - 100] = buffer.getShort() & 0xFFFF; - def.stackAmounts[opcode - 100] = buffer.getShort() & 0xFFFF; - } else if (opcode == 110) { - buffer.getShort(); - } else if (opcode == 111) { - buffer.getShort(); - } else if (opcode == 112) { - buffer.getShort(); - } else if (opcode == 113) { - buffer.get(); - } else if (opcode == 114) { - buffer.get(); - } else if (opcode == 115) { - def.teamId = buffer.get(); - } else if (opcode == 121) { - def.lendId = buffer.getShort() & 0xFFFF; - } else if (opcode == 122) { - def.lendTemplateId = buffer.getShort() & 0xFFFF; - } else if (opcode == 124) { - if (def.unknownArray3 == null) { - def.unknownArray3 = new int[11][]; - } - int slot = buffer.get(); - def.unknownArray3[slot] = new int[6]; - for (int i = 0; i < 6; i++) { - def.unknownArray3[slot][i] = buffer.getShort(); - } - } else if (opcode == 125) { - buffer.get(); - buffer.get(); - buffer.get(); - } else if (opcode == 126) { - buffer.get(); - buffer.get(); - buffer.get(); - } else if (opcode == 127) { - buffer.get(); - buffer.getShort(); - } else if (opcode == 128) { - buffer.get(); - buffer.getShort(); - } else if (opcode == 129) { - buffer.get(); - buffer.getShort(); - } else if (opcode == 130) { - buffer.get(); - buffer.getShort(); - } else if (opcode == 132) { - int length = buffer.get() & 0xFF; - def.unknownArray2 = new int[length]; - for (int index = 0; index < length; index++) { - def.unknownArray2[index] = buffer.getShort() & 0xFFFF; - } - } else if (opcode == 134) { - buffer.get(); - } else if (opcode == 139) { - def.recolourId = buffer.getShort() & 0xFFFF; - } else if (opcode == 140) { - def.recolourTemplateId = buffer.getShort() & 0xFFFF; - } else if (opcode == 249) { - int length = buffer.get() & 0xFF; - if (def.clientScriptData == null) { - def.clientScriptData = new HashMap(); - } - for (int index = 0; index < length; index++) { - boolean string = (buffer.get() & 0xFF) == 1; - int key = ByteBufferUtils.getTriByte(buffer); - Object value = string ? ByteBufferUtils.getString(buffer) : buffer.getInt(); - def.clientScriptData.put(key, value); - } - } else { - System.out.println("Unhandled item definition opcode - opcode: " + opcode); - break; - } - } - return def; - } - - /** - * Defines the definitions for noted, lending and recolored items. - */ - public static void defineTemplates() { - int equipId = 0; - for (int i = 0; i < Cache.getItemDefinitionsSize(); i++) { - ItemDefinition def = forId(i); - if (def.noteTemplateId != -1) { - def.transferNoteDefinition(forId(def.noteId), forId(def.noteTemplateId)); - } - if (def.lendTemplateId != -1) { - def.transferLendDefinition(forId(def.lendId), forId(def.lendTemplateId)); - } - if (def.recolourTemplateId != -1) { - def.transferRecolourDefinition(forId(def.recolourId), forId(def.recolourTemplateId)); - } - if (def != null && (def.maleWornModelId1 >= 0 || def.maleWornModelId2 >= 0)) { - def.equipId = equipId++; - } - } - forId(2513).equipId = forId(3140).equipId; - } - - /** - * Transfers definitions for noted items. - * @param reference The reference definitions. - * @param templateReference The template definitions. - */ - public void transferNoteDefinition(ItemDefinition reference, ItemDefinition templateReference) { - membersOnly = reference.membersOnly; - interfaceModelId = templateReference.interfaceModelId; - originalModelColors = templateReference.originalModelColors; - name = reference.name; - modelOffsetY = templateReference.modelOffsetY; - textureColour1 = templateReference.textureColour1; - value = reference.value; - modelRotationX = templateReference.modelRotationX; - stackable = true; - unnoted = false; - modifiedModelColors = templateReference.modifiedModelColors; - modelRotationY = templateReference.modelRotationY; - modelZoom = templateReference.modelZoom; - textureColour1 = templateReference.textureColour1; - handlers.put(ItemConfigParser.TRADEABLE, true); - } - - /** - * Transfers definitions for lending items. - * @param reference The reference definitions. - * @param templateReference The template definitions. - */ - public void transferLendDefinition(ItemDefinition reference, ItemDefinition templateReference) { - femaleWornModelId1 = reference.femaleWornModelId1; - maleWornModelId2 = reference.maleWornModelId2; - membersOnly = reference.membersOnly; - interfaceModelId = templateReference.interfaceModelId; - textureColour2 = reference.textureColour2; - groundActions = reference.groundActions; - unknownArray1 = reference.unknownArray1; - modelRotationY = templateReference.modelRotationY; - modelRotationX = templateReference.modelRotationX; - originalModelColors = reference.originalModelColors; - name = reference.name; - maleWornModelId1 = reference.maleWornModelId1; - colourEquip1 = reference.colourEquip1; - teamId = reference.teamId; - modelOffsetY = templateReference.modelOffsetY; - clientScriptData = reference.clientScriptData; - modifiedModelColors = reference.modifiedModelColors; - colourEquip2 = reference.colourEquip2; - modelOffsetX = templateReference.modelOffsetX; - textureColour1 = reference.textureColour1; - value = 0; - modelZoom = templateReference.modelZoom; - options = new String[5]; - femaleWornModelId2 = reference.femaleWornModelId2; - if (reference.options != null) { - options = reference.options.clone(); - } - } - - /** - * Transfers definitions for recolored items. - * @param reference The reference definitions. - * @param templateReference The template definitions. - */ - public void transferRecolourDefinition(ItemDefinition reference, ItemDefinition templateReference) { - femaleWornModelId2 = reference.femaleWornModelId2; - options = new String[5]; - modelRotationX = templateReference.modelRotationX; - name = reference.name; - maleWornModelId1 = reference.maleWornModelId1; - modelOffsetY = templateReference.modelOffsetY; - femaleWornModelId1 = reference.femaleWornModelId1; - maleWornModelId2 = reference.maleWornModelId2; - modelOffsetX = templateReference.modelOffsetX; - unknownArray1 = reference.unknownArray1; - stackable = reference.stackable; - modelRotationY = templateReference.modelRotationY; - textureColour1 = reference.textureColour1; - colourEquip1 = reference.colourEquip1; - textureColour2 = reference.textureColour2; - modifiedModelColors = reference.modifiedModelColors; - modelZoom = templateReference.modelZoom; - colourEquip2 = reference.colourEquip2; - teamId = reference.teamId; - value = 0; - groundActions = reference.groundActions; - originalModelColors = reference.originalModelColors; - membersOnly = reference.membersOnly; - clientScriptData = reference.clientScriptData; - interfaceModelId = templateReference.interfaceModelId; - if (reference.options != null) { - options = reference.options.clone(); - } - } - - /** - * Checks if the player has the needed requirements to use this item. - * @param player The player. - * @param wield If requirements are checked for wearing the item. - * @param message If a message should be sent when the player does not meet - * a requirement. - * @return {@code True} if so. - */ - public boolean hasRequirement(Player player, boolean wield, boolean message) { - Map requirements = getConfiguration(ItemConfigParser.REQUIREMENTS); - if (requirements == null) { - return true; - } - for (int skill : requirements.keySet()) { - if (wield) { - if (skill > Skills.MAGIC && skill != Skills.SLAYER && skill != Skills.HUNTER) { - continue; - } - } else if (skill <= Skills.MAGIC && skill != Skills.SLAYER && skill != Skills.HUNTER) { - continue; - } - if (skill < 0 || skill >= Skills.SKILL_NAME.length) { - continue; - } - int level = requirements.get(skill); - if (player.getSkills().getStaticLevel(skill) < level) { - if (message) { - String name = Skills.SKILL_NAME[skill]; - player.getPacketDispatch().sendMessage("You need a" + (StringUtils.isPlusN(name) ? "n " : " ") + name + " level of " + level + " to " + (wield ? "wear " : "use ") + "this."); - } - return false; - } - } - return true; - } - - /** - * Checks if the player can enter entrana. - * @param player the player. - * @return {@code True} if so. - */ - public static boolean canEnterEntrana(Player player) { - Container[] container = new Container[] { player.getInventory(), player.getEquipment() }; - for (Container c : container) { - for (Item i : c.toArray()) { - if (i == null) { - continue; - } - if (!i.getDefinition().isAllowedOnEntrana()) { - return false; - } - } - } - return true; - } - - /** - * The allowed names. - */ - private static final String[] allowedNames = new String[] { "cape", "robe", "hat", "potion" }; - - /** - * Checks if the item is allowed on entrana. - * @return {@code True} if so. - */ - public boolean isAllowedOnEntrana() { - if (getId() == 946) { - return true; - } - if (getName().equals("Boots")) { - return true; - } - if (getName().toLowerCase().startsWith("ring") || getName().toLowerCase().startsWith("amulet")) { - return true; - } - if (getName().toLowerCase().contains("spotted") || getName().toLowerCase().equals("spottier")) { - return true; - } - if (getName().equals("Boots of lightness")) { - return true; - } - if(getName().toLowerCase().contains("arrow") || getName().toLowerCase().contains("bolt")){ - return false; - } - for (String name : allowedNames) { - if (getName().toLowerCase().contains(name)) { - return true; - } - } - return getConfiguration(ItemConfigParser.BONUS) == null; - } - - /** - * Gets the level requirement for this item. - * @param skillId The skill id. - * @return The level required. - */ - public int getRequirement(int skillId) { - Map requirements = getConfiguration(ItemConfigParser.REQUIREMENTS); - if (requirements == null) { - return 0; - } - Integer level = requirements.get(skillId); - return level == null ? 0 : level; - } - - /** - * Gets the wielding animation id (render animation id). - * @return The wield animation id. - */ - public int getRenderAnimationId() { - return getConfiguration(ItemConfigParser.RENDER_ANIM_ID, 1426); - } - - @Override - public int getId() { - return id; - } - - @Override - public void setId(int id) { - this.id = id; - } - - /** - * Gets the interfaceModelId. - * @return The interfaceModelId. - */ - public int getInterfaceModelId() { - return interfaceModelId; - } - - /** - * Sets the interfaceModelId. - * @param interfaceModelId The interfaceModelId to set. - */ - public void setInterfaceModelId(int interfaceModelId) { - this.interfaceModelId = interfaceModelId; - } - - /** - * Gets the name. - * @return The name. - */ - @Override - public String getName() { - return name; - } - - /** - * Sets the name. - * @param name The name to set. - */ - @Override - public void setName(String name) { - this.name = name; - } - - /** - * Checks if the item type is for player usage. - * @return {@code True}. - */ - public boolean isPlayerType() { - return itemType == 0; - } - - /** - * Gets the modelZoom. - * @return The modelZoom. - */ - public int getModelZoom() { - return modelZoom; - } - - /** - * Sets the modelZoom. - * @param modelZoom The modelZoom to set. - */ - public void setModelZoom(int modelZoom) { - this.modelZoom = modelZoom; - } - - /** - * Gets the modelRotation1. - * @return The modelRotation1. - */ - public int getModelRotation1() { - return modelRotationY; - } - - /** - * Sets the modelRotation1. - * @param modelRotation1 The modelRotation1 to set. - */ - public void setModelRotation1(int modelRotation1) { - this.modelRotationY = modelRotation1; - } - - /** - * Gets the modelRotation2. - * @return The modelRotation2. - */ - public int getModelRotation2() { - return modelRotationX; - } - - /** - * Sets the modelRotation2. - * @param modelRotation2 The modelRotation2 to set. - */ - public void setModelRotation2(int modelRotation2) { - this.modelRotationX = modelRotation2; - } - - /** - * Gets the modelOffset1. - * @return The modelOffset1. - */ - public int getModelOffset1() { - return modelOffsetX; - } - - /** - * Sets the modelOffset1. - * @param modelOffset1 The modelOffset1 to set. - */ - public void setModelOffset1(int modelOffset1) { - this.modelOffsetX = modelOffset1; - } - - /** - * Gets the modelOffset2. - * @return The modelOffset2. - */ - public int getModelOffset2() { - return modelOffsetY; - } - - /** - * Sets the modelOffset2. - * @param modelOffset2 The modelOffset2 to set. - */ - public void setModelOffset2(int modelOffset2) { - this.modelOffsetY = modelOffset2; - } - - /** - * Gets the stackable. - * @return The stackable. - */ - public boolean isStackable() { - return stackable || !this.unnoted; - } - - /** - * Sets the stackable. - * @param stackable The stackable to set. - */ - public void setStackable(boolean stackable) { - this.stackable = stackable; - } - - /** - * Gets the value. - * @return The value. - */ - public int getValue() { - if (value == 0) { - return 1; - } - return value; - } - - /** - * @return The value. - */ - public int getMaxValue() { - if ((int) (value * 1.05) <= 0) { - return 1; - } - return (int) (value * 1.05); - } - - /** - * @return The value. - */ - public int getMinValue() { - if ((int) (value * .95) <= 0) { - return 1; - } - return (int) (value * .95); - } - - /** - * Sets the value. - * @param value The value to set. - */ - public void setValue(int value) { - this.value = value; - } - - /** - * Gets the membersOnly. - * @return The membersOnly. - */ - public boolean isMembersOnly() { - return membersOnly; - } - - /** - * Sets the membersOnly. - * @param membersOnly The membersOnly to set. - */ - public void setMembersOnly(boolean membersOnly) { - this.membersOnly = membersOnly; - } - - /** - * Gets the maleWornModelId1. - * @return The maleWornModelId1. - */ - public int getMaleWornModelId1() { - return maleWornModelId1; - } - - /** - * Sets the maleWornModelId1. - * @param maleWornModelId1 The maleWornModelId1 to set. - */ - public void setMaleWornModelId1(int maleWornModelId1) { - this.maleWornModelId1 = maleWornModelId1; - } - - /** - * Gets the femaleWornModelId1. - * @return The femaleWornModelId1. - */ - public int getFemaleWornModelId1() { - return femaleWornModelId1; - } - - /** - * Sets the femaleWornModelId1. - * @param femaleWornModelId1 The femaleWornModelId1 to set. - */ - public void setFemaleWornModelId1(int femaleWornModelId1) { - this.femaleWornModelId1 = femaleWornModelId1; - } - - /** - * Gets the maleWornModelId2. - * @return The maleWornModelId2. - */ - public int getMaleWornModelId2() { - return maleWornModelId2; - } - - /** - * Sets the maleWornModelId2. - * @param maleWornModelId2 The maleWornModelId2 to set. - */ - public void setMaleWornModelId2(int maleWornModelId2) { - this.maleWornModelId2 = maleWornModelId2; - } - - /** - * Gets the femaleWornModelId2. - * @return The femaleWornModelId2. - */ - public int getFemaleWornModelId2() { - return femaleWornModelId2; - } - - /** - * Sets the femaleWornModelId2. - * @param femaleWornModelId2 The femaleWornModelId2 to set. - */ - public void setFemaleWornModelId2(int femaleWornModelId2) { - this.femaleWornModelId2 = femaleWornModelId2; - } - - /** - * Gets the groundOptions. - * @return The groundOptions. - */ - public String[] getGroundOptions() { - return groundActions; - } - - /** - * Sets the groundOptions. - * @param groundOptions The groundOptions to set. - */ - public void setGroundOptions(String[] groundOptions) { - this.groundActions = groundOptions; - } - - /** - * Gets the inventoryOptions. - * @return The inventoryOptions. - */ - public String[] getInventoryOptions() { - return options; - } - - /** - * Sets the inventoryOptions. - * @param inventoryOptions The inventoryOptions to set. - */ - public void setInventoryOptions(String[] inventoryOptions) { - this.options = inventoryOptions; - } - - /** - * Gets the originalModelColors. - * @return The originalModelColors. - */ - public short[] getOriginalModelColors() { - return originalModelColors; - } - - /** - * Sets the originalModelColors. - * @param originalModelColors The originalModelColors to set. - */ - public void setOriginalModelColors(short[] originalModelColors) { - this.originalModelColors = originalModelColors; - } - - /** - * Gets the modifiedModelColors. - * @return The modifiedModelColors. - */ - public short[] getModifiedModelColors() { - return modifiedModelColors; - } - - /** - * Sets the modifiedModelColors. - * @param modifiedModelColors The modifiedModelColors to set. - */ - public void setModifiedModelColors(short[] modifiedModelColors) { - this.modifiedModelColors = modifiedModelColors; - } - - /** - * Gets the textureColour1. - * @return The textureColour1. - */ - public short[] getTextureColour1() { - return textureColour1; - } - - /** - * Sets the textureColour1. - * @param textureColour1 The textureColour1 to set. - */ - public void setTextureColour1(short[] textureColour1) { - this.textureColour1 = textureColour1; - } - - /** - * Gets the textureColour2. - * @return The textureColour2. - */ - public short[] getTextureColour2() { - return textureColour2; - } - - /** - * Sets the textureColour2. - * @param textureColour2 The textureColour2 to set. - */ - public void setTextureColour2(short[] textureColour2) { - this.textureColour2 = textureColour2; - } - - /** - * Gets the unknownArray1. - * @return The unknownArray1. - */ - public byte[] getUnknownArray1() { - return unknownArray1; - } - - /** - * Sets the unknownArray1. - * @param unknownArray1 The unknownArray1 to set. - */ - public void setUnknownArray1(byte[] unknownArray1) { - this.unknownArray1 = unknownArray1; - } - - /** - * Gets the unknownArray2. - * @return The unknownArray2. - */ - public int[] getUnknownArray2() { - return unknownArray2; - } - - /** - * Sets the unknownArray2. - * @param unknownArray2 The unknownArray2 to set. - */ - public void setUnknownArray2(int[] unknownArray2) { - this.unknownArray2 = unknownArray2; - } - - /** - * Gets the unnoted. - * @return The unnoted. - */ - public boolean isUnnoted() { - return unnoted; - } - - /** - * Sets the unnoted. - * @param unnoted The unnoted to set. - */ - public void setUnnoted(boolean unnoted) { - this.unnoted = unnoted; - } - - /** - * Gets the colourEquip1. - * @return The colourEquip1. - */ - public int getColourEquip1() { - return colourEquip1; - } - - /** - * Sets the colourEquip1. - * @param colourEquip1 The colourEquip1 to set. - */ - public void setColourEquip1(int colourEquip1) { - this.colourEquip1 = colourEquip1; - } - - /** - * Gets the colourEquip2. - * @return The colourEquip2. - */ - public int getColourEquip2() { - return colourEquip2; - } - - /** - * Sets the colourEquip2. - * @param colourEquip2 The colourEquip2 to set. - */ - public void setColourEquip2(int colourEquip2) { - this.colourEquip2 = colourEquip2; - } - - /** - * Gets the noteId. - * @return The noteId. - */ - public int getNoteId() { - return noteId; - } - - /** - * Sets the noteId. - * @param noteId The noteId to set. - */ - public void setNoteId(int noteId) { - this.noteId = noteId; - } - - /** - * Gets the noteTemplateId. - * @return The noteTemplateId. - */ - public int getNoteTemplateId() { - return noteTemplateId; - } - - /** - * Sets the noteTemplateId. - * @param noteTemplateId The noteTemplateId to set. - */ - public void setNoteTemplateId(int noteTemplateId) { - this.noteTemplateId = noteTemplateId; - } - - /** - * Gets the stackIds. - * @return The stackIds. - */ - public int[] getStackIds() { - return stackIds; - } - - /** - * Sets the stackIds. - * @param stackIds The stackIds to set. - */ - public void setStackIds(int[] stackIds) { - this.stackIds = stackIds; - } - - /** - * Gets the stackAmounts. - * @return The stackAmounts. - */ - public int[] getStackAmounts() { - return stackAmounts; - } - - /** - * Sets the stackAmounts. - * @param stackAmounts The stackAmounts to set. - */ - public void setStackAmounts(int[] stackAmounts) { - this.stackAmounts = stackAmounts; - } - - /** - * Gets the teamId. - * @return The teamId. - */ - public int getTeamId() { - return teamId; - } - - /** - * Sets the teamId. - * @param teamId The teamId to set. - */ - public void setTeamId(int teamId) { - this.teamId = teamId; - } - - /** - * Gets the lendId. - * @return The lendId. - */ - public int getLendId() { - return lendId; - } - - /** - * Sets the lendId. - * @param lendId The lendId to set. - */ - public void setLendId(int lendId) { - this.lendId = lendId; - } - - /** - * Gets the lendTemplateId. - * @return The lendTemplateId. - */ - public int getLendTemplateId() { - return lendTemplateId; - } - - /** - * Sets the lendTemplateId. - * @param lendTemplateId The lendTemplateId to set. - */ - public void setLendTemplateId(int lendTemplateId) { - this.lendTemplateId = lendTemplateId; - } - - /** - * Gets the recolourId. - * @return The recolourId. - */ - public int getRecolourId() { - return recolourId; - } - - /** - * Sets the recolourId. - * @param recolourId The recolourId to set. - */ - public void setRecolourId(int recolourId) { - this.recolourId = recolourId; - } - - /** - * Gets the recolourTemplateId. - * @return The recolourTemplateId. - */ - public int getRecolourTemplateId() { - return recolourTemplateId; - } - - /** - * Sets the recolourTemplateId. - * @param recolourTemplateId The recolourTemplateId to set. - */ - public void setRecolourTemplateId(int recolourTemplateId) { - this.recolourTemplateId = recolourTemplateId; - } - - /** - * Gets the equipId. - * @return The equipId. - */ - public int getEquipId() { - return equipId; - } - - /** - * Sets the equipId. - * @param equipId The equipId to set. - */ - public void setEquipId(int equipId) { - this.equipId = equipId; - } - - /** - * Gets the clientScriptData. - * @return The clientScriptData. - */ - public HashMap getClientScriptData() { - return clientScriptData; - } - - /** - * Sets the clientScriptData. - * @param clientScriptData The clientScriptData to set. - */ - public void setClientScriptData(HashMap clientScriptData) { - this.clientScriptData = clientScriptData; - } - - /** - * Gets the alchemy value. - * @param highAlchemy If the value is for high alchemy (instead of low). - * @return The alchemy value. - */ - public int getAlchemyValue(boolean highAlchemy) { - if (!unnoted && noteId > -1) { - return forId(noteId).getAlchemyValue(highAlchemy); - } - if (highAlchemy) { - return getConfiguration(ItemConfigParser.HIGH_ALCHEMY, 0); - } - return getConfiguration(ItemConfigParser.LOW_ALCHEMY, 0); - } - - /** - * Checks if the item is tradeable. - * @return {@code True} if so. - */ - public boolean isTradeable() { - if (hasDestroyAction() && !getName().contains("impling jar")) { - return false; - } - if (!getConfiguration(ItemConfigParser.TRADEABLE, false)) { - return false; - } - return !unnoted || GrandExchangeDatabase.getDatabase().get(getId()) != null; - } - - /** - * If the item has the specified item. - * @param optionName The reward. - * @return If the item has the specified reward {@code true}. - */ - public boolean hasAction(String optionName) { - if (options == null) { - return false; - } - for (String action : options) { - if (action == null) { - continue; - } - if (action.equalsIgnoreCase(optionName)) { - return true; - } - } - return false; - } - - /** - * If the item has the destroy reward. - * @return If the item has the destroy reward {@code true}. - */ - public boolean hasDestroyAction() { - return hasAction("destroy") || hasAction("dissolve"); - } - - /** - * If the item has the wear reward. - * @return If the item has the wear reward {@code true}. - */ - public boolean hasWearAction() { - if (options == null) { - return false; - } - for (String action : options) { - if (action == null) { - continue; - } - if (action.equalsIgnoreCase("wield") || action.equalsIgnoreCase("wear") || action.equalsIgnoreCase("equip")) { - return true; - } - } - return false; - } - - /** - * If the item has a special bar. - * @return If the item has a special bar {@code true}. - */ - public boolean hasSpecialBar() { - if (clientScriptData == null) { - return false; - } - Object specialBar = clientScriptData.get(686); - if (specialBar != null && specialBar instanceof Integer) { - return (Integer) specialBar == 1; - } - return false; - } - - /** - * Get the quest id for the item. - * @return The quest id. - */ - public int getQuestId() { - if (clientScriptData == null) { - return -1; - } - Object questId = clientScriptData.get(861); - if (questId != null && questId instanceof Integer) { - return (Integer) questId; - } - return -1; - } - - /** - * Get the archive id. - * @return The archive id. - */ - public int getArchiveId() { - return id >>> 8; - } - - /** - * Get the file id. - * @return The file id. - */ - public int getFileId() { - return 0xff & id; - } - - /** - * Gets the definitions. - * @return The definitions. - */ - public static Map getDefinitions() { - return DEFINITIONS; - } - - /** - * Gets the option handler for the given option name. - * @param nodeId - * @param name The name. - * @return The option handler, or {@code null} if there was no default - * option handler. - */ - public static OptionHandler getOptionHandler(int nodeId, String name) { - ItemDefinition def = forId(nodeId); - if (def == null) { - if (nodeId == 22937) - SystemLogger.logErr("[ItemDefinition] No definition for item id " + nodeId + "!"); - return null; - } - OptionHandler handler = def.getConfiguration("option:" + name); - if (handler != null) { - return handler; - } - return OPTION_HANDLERS.get(name); - } - - /** - * The bonus names. - */ - 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. - */ - public boolean hasPlugin() { - return getItemPlugin() != null; - } - - /** - * Gets the item plugin. - * @return the plugin. - */ - public ItemPlugin getItemPlugin() { - return getConfiguration("wrapper", null); - } - - /** - * Sets the item plugin. - * @param plugin the plugin. - */ - public void setItemPlugin(ItemPlugin plugin) { - getHandlers().put("wrapper", plugin); - } - - /** - * Sets the default option handler for an option. - * @param name The option name. - * @param handler The default option handler. - * @return {@code True} if there was a previous default handler mapped. - */ - public static boolean setOptionHandler(String name, OptionHandler handler) { - return OPTION_HANDLERS.put(name, handler) != null; - } - - /** - * @return the optionHandlers - */ - public static Map getOptionHandlers() { - return OPTION_HANDLERS; - } - - /** - * @return the itemRequirements. - */ - public HashMap getItemRequirements() { - return itemRequirements; - } - - /** - * @param itemRequirements the itemRequirements to set. - */ - public void setItemRequirements(HashMap itemRequirements) { - this.itemRequirements = itemRequirements; - } - - /** - * Gets the itemType. - * @return The itemType. - */ - public int getItemType() { - return itemType; - } - - /** - * Sets the itemType. - * @param itemType The itemType to set. - */ - public void setItemType(int itemType) { - this.itemType = itemType; - } - - /** - * Gets the femaleWornModelId3 value. - * @return The femaleWornModelId3. - */ - public int getFemaleWornModelId3() { - return femaleWornModelId3; - } - /** - * Sets the femaleWornModelId3 value. - * @param femaleWornModelId3 The femaleWornModelId3 to set. - */ - public void setFemaleWornModelId3(int femaleWornModelId3) { - this.femaleWornModelId3 = femaleWornModelId3; - } - /** - * Gets the femaleWornModelId4 value. - * @return The femaleWornModelId4. - */ - public int getFemaleWornModelId4() { - return femaleWornModelId4; - } - /** - * Sets the femaleWornModelId4 value. - * @param femaleWornModelId4 The femaleWornModelId4 to set. - */ - public void setFemaleWornModelId4(int femaleWornModelId4) { - this.femaleWornModelId4 = femaleWornModelId4; - } - /** - * Gets the maleWornModelId3 value. - * @return The maleWornModelId3. - */ - public int getMaleWornModelId3() { - return maleWornModelId3; - } - /** - * Sets the maleWornModelId3 value. - * @param maleWornModelId3 The maleWornModelId3 to set. - */ - public void setMaleWornModelId3(int maleWornModelId3) { - this.maleWornModelId3 = maleWornModelId3; - } - /** - * Gets the maleWornModelId4 value. - * @return The maleWornModelId4. - */ - public int getMaleWornModelId4() { - return maleWornModelId4; - } - /** - * Sets the maleWornModelId4 value. - * @param maleWornModelId4 The maleWornModelId4 to set. - */ - public void setMaleWornModelId4(int maleWornModelId4) { - this.maleWornModelId4 = maleWornModelId4; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/cache/def/impl/NPCDefinition.java b/Server/src/main/java/core/cache/def/impl/NPCDefinition.java deleted file mode 100644 index e2105888f..000000000 --- a/Server/src/main/java/core/cache/def/impl/NPCDefinition.java +++ /dev/null @@ -1,1117 +0,0 @@ -package core.cache.def.impl; - -import core.cache.Cache; -import core.cache.def.Definition; -import core.cache.misc.buffer.ByteBufferUtils; -import core.game.interaction.OptionHandler; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.npc.drop.NPCDropTables; -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.tools.StringUtils; -import rs09.game.system.config.NPCConfigParser; -import rs09.game.world.GameWorld; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * Debug NPC Opcodes Line 483, Uncomment - */ - -/** - * Represents an NPC's definitions. - * @author Emperor - */ -public final class NPCDefinition extends Definition { - - /** - * The definitions. - */ - private static final Map DEFINITIONS = new HashMap<>(); - - /** - * The default option handlers. - */ - private static final Map OPTION_HANDLERS = new HashMap<>(); - - /** - * The size. - */ - public int size = 1; - - /** - * The combat level. - */ - private int combatLevel; - - /** - * The head icons. - */ - public int headIcons; - - /** - * If the NPC can be seen on the minimap. - */ - public boolean isVisibleOnMap; - - -/* public String examine;*/ - - /** - * The drop tables. - */ - private NPCDropTables dropTables = new NPCDropTables(this); - - /** - * Unidentified variables. - */ - public int anInt833; - public int anInt836; - public int anInt837; - public boolean aBoolean841; - public int anInt842; - public int configFileId; - public int[] childNPCIds; - public int anInt846; - public int anInt850; - public byte aByte851; - public boolean aBoolean852; - public int anInt853; - public byte aByte854; - public boolean aBoolean856; - public boolean aBoolean857; - public short[] aShortArray859; - public byte[] aByteArray861; - public short aShort862; - public boolean aBoolean863; - public int anInt864; - public short[] aShortArray866; - public int[] anIntArray868; - public int anInt869; - public int anInt870; - public int anInt871; - public int anInt872; - public int anInt874; - public int anInt875; - public int anInt876; - public int anInt879; - public short[] aShortArray880; - public int anInt884; - public int configId; - public int anInt889; - public int[] anIntArray892; - public short aShort894; - public short[] aShortArray896; - public int anInt897; - public int anInt899; - public int anInt901; - public int standAnimation; - public int walkAnimation; - public int renderAnimationId; - - /** - * The minimum combat distance (0 uses default distances). - */ - private int combatDistance; - - /** - * The combat graphics. - */ - private Graphics[] combatGraphics = new Graphics[3]; - - /** - * The turning animation. - */ - private int turnAnimation; - - /** - * The turn 180� animation. - */ - private int turn180Animation; - - /** - * The turn clock-wise animation. - */ - private int turnCWAnimation; - - /** - * The turn counter clock-wise animation. - */ - private int turnCCWAnimation; - - /** - * Constructs a new {@code NPCDefinition} {@code Object}. - * @param id The NPC id. - */ - public NPCDefinition(int id) { - this.id = id; - anInt842 = -1; - configFileId = -1; - anInt837 = -1; - anInt846 = -1; - anInt853 = 32; - standAnimation = -1; - walkAnimation = -1; - combatLevel = 0; - anInt836 = -1; - name = "null"; - anInt869 = 0; - anInt850 = 255; - anInt871 = -1; - aBoolean852 = true; - aShort862 = (short) 0; - anInt876 = -1; - aByte851 = (byte) -96; - anInt875 = 0; - anInt872 = -1; - aBoolean857 = true; - anInt870 = -1; - anInt874 = -1; - anInt833 = -1; - anInt864 = 128; - headIcons = -1; - aBoolean856 = false; - configId = -1; - aByte854 = (byte) -16; - aBoolean863 = false; - isVisibleOnMap = true; - anInt889 = -1; - anInt884 = -1; - aBoolean841 = true; - anInt879 = -1; - anInt899 = 128; - aShort894 = (short) 0; - options = new String[5]; - anInt897 = 0; - anInt901 = -1; - anIntArray868 = new int[0]; - } - - /** - * Gets the NPC definition for this id. - * @param id The NPC id. - * @return The NPC definition object. - */ - public static NPCDefinition forId(int id) { - NPCDefinition def = DEFINITIONS.get(id); - if (def == null) { - def = new NPCDefinition(id); - 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)); - } - DEFINITIONS.put(id, def); - } - return def; - } - - 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()); - // } - // } - } - - /** - * Gets the child object definitions. - * @param player The player to get it for. - * @return The object definition. - */ - public NPCDefinition getChildNPC(Player player) { - if (childNPCIds == null || childNPCIds.length < 1) { - return this; - } - int configValue = -1; - if (player != null) { - if (configFileId != -1) { - configValue = VarbitDefinition.forNPCID(configFileId).getValue(player); - } else if (configId != -1) { - configValue = player.getConfigManager().get(configId); - } - } else { - configValue = 0; - } - if (configValue < 0 || configValue >= childNPCIds.length - 1 || childNPCIds[configValue] == -1) { - int objectId = childNPCIds[childNPCIds.length - 1]; - if (objectId != -1) { - return forId(objectId); - } - return this; - } - return forId(childNPCIds[configValue]); - } - - /** - * Parses the data. - * @param buffer The data input stream. - */ - private void parse(ByteBuffer buffer) { - while (true) { - int opcode = buffer.get() & 0xFF; - if (opcode == 0) { - break; - } - parseOpcode(buffer, opcode); - } - } - - /** - * Parses an opcode. - * @param buffer The data input stream. - * @param opcode The opcode to parse. - */ - private void parseOpcode(ByteBuffer buffer, int opcode) { - switch (opcode) { - case 1: - int length = buffer.get() & 0xFF; - anIntArray868 = new int[length]; - for (int i_66_ = 0; i_66_ < length; i_66_++) { - anIntArray868[i_66_] = buffer.getShort() & 0xFFFF; - if ((anIntArray868[i_66_] ^ 0xffffffff) == -65536) - anIntArray868[i_66_] = -1; - } - break; - case 2: - name = ByteBufferUtils.getString(buffer); - break; - case 12: - size = buffer.get() & 0xFF; - break; - case 13: - standAnimation = buffer.getShort(); - break; - case 14: - walkAnimation = buffer.getShort(); - break; - case 15: - turnAnimation = buffer.getShort(); - break; - case 16: - buffer.getShort(); // Another turn animation - break; - case 17: - walkAnimation = buffer.getShort(); - turn180Animation = buffer.getShort(); - turnCWAnimation = buffer.getShort(); - turnCCWAnimation = buffer.getShort(); - break; - case 30: - case 31: - case 32: - case 33: - case 34: - options[opcode - 30] = ByteBufferUtils.getString(buffer); - break; - case 40: - length = buffer.get() & 0xFF; - aShortArray859 = new short[length]; - aShortArray896 = new short[length]; - for (int i_65_ = 0; (length ^ 0xffffffff) < (i_65_ ^ 0xffffffff); i_65_++) { - aShortArray896[i_65_] = (short) (buffer.getShort() & 0xFFFF); - aShortArray859[i_65_] = (short) (buffer.getShort() & 0xFFFF); - } - break; - case 41: - length = buffer.get() & 0xFF; - aShortArray880 = new short[length]; - aShortArray866 = new short[length]; - for (int i_54_ = 0; (i_54_ ^ 0xffffffff) > (length ^ 0xffffffff); i_54_++) { - aShortArray880[i_54_] = (short) (buffer.getShort() & 0xFFFF); - aShortArray866[i_54_] = (short) (buffer.getShort() & 0xFFFF); - } - break; - case 42: - length = buffer.get() & 0xFF; - aByteArray861 = new byte[length]; - for (int i_55_ = 0; length > i_55_; i_55_++) { - aByteArray861[i_55_] = (byte) buffer.get(); - } - break; - case 60: - length = buffer.get() & 0xFF; - anIntArray892 = new int[length]; - for (int i_64_ = 0; (i_64_ ^ 0xffffffff) > (length ^ 0xffffffff); i_64_++) { - anIntArray892[i_64_] = buffer.getShort() & 0xFFFF; - } - break; - case 93: - isVisibleOnMap = false; - break; - case 95: - setCombatLevel(buffer.getShort() & 0xFFFF); - break; - case 97: - anInt864 = buffer.getShort() & 0xFFFF; - break; - case 98: - anInt899 = buffer.getShort() & 0xFFFF; - break; - case 99: - aBoolean863 = true; - break; - case 100: - anInt869 = buffer.get(); - break; - case 101: - anInt897 = buffer.get() * 5; - break; - case 102: - headIcons = buffer.getShort() & 0xFFFF; - break; - case 103: - anInt853 = buffer.getShort() & 0xFFFF; - break; - case 106: - case 118: - configFileId = buffer.getShort() & 0xFFFF; - if (configFileId == 65535) { - configFileId = -1; - } - configId = buffer.getShort() & 0xFFFF; - if (configId == 65535) { - configId = -1; - } - int defaultValue = -1; - if ((opcode ^ 0xffffffff) == -119) { - defaultValue = buffer.getShort() & 0xFFFF; - if (defaultValue == 65535) { - defaultValue = -1; - } - } - length = buffer.get() & 0xFF; - childNPCIds = new int[2 + length]; - for (int i = 0; length >= i; i++) { - childNPCIds[i] = buffer.getShort() & 0xFFFF; - if (childNPCIds[i] == 65535) { - childNPCIds[i] = -1; - } - } - childNPCIds[length + 1] = defaultValue; - break; - case 107: - aBoolean841 = false; - break; - case 109: - aBoolean852 = false; - break; - case 111: - aBoolean857 = false; - break; - case 113: - aShort862 = (short) (buffer.getShort() & 0xFFFF); - aShort894 = (short) (buffer.getShort() & 0xFFFF); - break; - case 114: - aByte851 = (byte) (buffer.get()); - aByte854 = (byte) (buffer.get()); - break; - case 115: - buffer.get();// & 0xFF; - buffer.get();// & 0xFF; - break; - case 119: - buffer.get(); - break; - case 121: - length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - buffer.get(); - buffer.get(); - buffer.get(); - buffer.get(); - } - break; - case 122: - buffer.getShort(); - break; - case 123: - buffer.getShort(); - break; - case 125: - buffer.get(); - break; - case 126: - buffer.getShort(); - case 127: - renderAnimationId = buffer.getShort(); - break; - case 128: - buffer.get(); - break; - case 134: - buffer.getShort(); - buffer.getShort(); - buffer.getShort(); - buffer.getShort(); - buffer.get(); - break; - case 135: - buffer.get(); - buffer.getShort(); - break; - case 136: - buffer.get(); - buffer.getShort(); - break; - case 137: - buffer.getShort(); - break; - case 249: - length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - boolean string = buffer.get() == 1; - ByteBufferUtils.getTriByte(buffer); // script id - if (!string) { - buffer.getInt(); // Value - } else { - ByteBufferUtils.getString(buffer); // value - } - } - break; - default: - //SystemLogger.logErr("Unhandled NPC definition opcode: " + opcode); - } - } - - /** - * Checks if this NPC has an attack option. - * @return {@code True} if so. - */ - public boolean hasAttackOption() { - for (String option : options) { - if (option != null && option.equalsIgnoreCase("attack")) { - return true; - } - } - return false; - } - - /** - * Gets the option handler for the given option name. - * @param nodeId The node id. - * @param name The name. - * @return The option handler, or {@code null} if there was no default - * option handler. - */ - public static OptionHandler getOptionHandler(int nodeId, String name) { - NPCDefinition def = forId(nodeId); - OptionHandler handler = def.getConfiguration("option:" + name); - if (handler != null) { - return handler; - } - return OPTION_HANDLERS.get(name); - } - - /** - * Checks if an npc has an reward. - * @param optionName The option name. - * @return {@code True} if so. - */ - public boolean hasAction(String optionName) { - if (options == null) { - return false; - } - for (String action : options) { - if (action == null) { - continue; - } - if (action.equalsIgnoreCase(optionName)) { - return true; - } - } - return false; - } - - /** - * Sets the default option handler for an option. - * @param name The option name. - * @param handler The default option handler. - * @return {@code True} if there was a previous default handler mapped. - */ - public static boolean setOptionHandler(String name, OptionHandler handler) { - return OPTION_HANDLERS.put(name, handler) != null; - } - - /** - * Gets the option handlers. - * @return The option handlers. - */ - public static Map getOptionHandlers() { - return OPTION_HANDLERS; - } - - /** - * Gets the definitions mapping. - * @return The mapping. - */ - public static final Map getDefinitions() { - return DEFINITIONS; - } - - /** - * Method returns the value for 'examine' - * @return the examine. - */ - public final String getExamine() { - String string = getConfiguration(NPCConfigParser.EXAMINE, examine); - if (string != null) { - return string; - } - if (name.length() <= 1) { - return string; - } - return "It's a" + (StringUtils.isPlusN(name) ? "n " : " ") + name + "."; - } - - /** - * Method sets the value for 'examine' - * @param examine the examine to set. - */ - public final void setExamine(String examine) { - this.examine = examine; - } - - /** - * Initializes the combat graphics. - * @param config The configurations. - */ - public void initCombatGraphics(Map config) { - if (config.containsKey(NPCConfigParser.START_GRAPHIC)) { - combatGraphics[0] = new Graphics((Integer) config.get(NPCConfigParser.START_GRAPHIC), getConfiguration(NPCConfigParser.START_HEIGHT, 0)); - } - if (config.containsKey(NPCConfigParser.PROJECTILE)) { - combatGraphics[1] = new Graphics((Integer) config.get(NPCConfigParser.PROJECTILE), getConfiguration(NPCConfigParser.PROJECTILE_HEIGHT, 42)); - } - if (config.containsKey(NPCConfigParser.END_GRAPHIC)) { - combatGraphics[2] = new Graphics((Integer) config.get(NPCConfigParser.END_GRAPHIC), getConfiguration(NPCConfigParser.END_HEIGHT, 96)); - } - } - - /** - * Gets the combat animation. - * @param index The index. - * @return The Animation. - */ - public Animation getCombatAnimation(int index) { - String name = ""; - switch (index) { - case 0: - name = NPCConfigParser.MELEE_ANIMATION; - break; - case 1: - name = NPCConfigParser.MAGIC_ANIMATION; - break; - case 2: - name = NPCConfigParser.RANGE_ANIMATION; - break; - case 3: - name = NPCConfigParser.DEFENCE_ANIMATION; - break; - case 4: - name = NPCConfigParser.DEATH_ANIMATION; - break; - default: - break; - } - return getConfiguration(name, null); - } - - /** - * Gets the size. - * @return The size. - */ - public int getSize() { - return size; - } - - /** - * Gets the headIcons. - * @return The headIcons. - */ - public int getHeadIcons() { - return headIcons; - } - - /** - * Gets the isVisibleOnMap. - * @return The isVisibleOnMap. - */ - public boolean isVisibleOnMap() { - return isVisibleOnMap; - } - - /** - * Gets the anInt833. - * @return The anInt833. - */ - public int getAnInt833() { - return anInt833; - } - - /** - * Gets the anInt836. - * @return The anInt836. - */ - public int getAnInt836() { - return anInt836; - } - - /** - * Gets the anInt837. - * @return The anInt837. - */ - public int getAnInt837() { - return anInt837; - } - - /** - * Gets the aBoolean841. - * @return The aBoolean841. - */ - public boolean isaBoolean841() { - return aBoolean841; - } - - /** - * Gets the anInt842. - * @return The anInt842. - */ - public int getAnInt842() { - return anInt842; - } - - /** - * Gets the configFileId. - * @return The configFileId. - */ - public int getConfigFileId() { - return configFileId; - } - - /** - * Gets the childNPCIds. - * @return The childNPCIds. - */ - public int[] getChildNPCIds() { - return childNPCIds; - } - - /** - * Gets the anInt846. - * @return The anInt846. - */ - public int getAnInt846() { - return anInt846; - } - - /** - * Gets the anInt850. - * @return The anInt850. - */ - public int getAnInt850() { - return anInt850; - } - - /** - * Gets the aByte851. - * @return The aByte851. - */ - public byte getaByte851() { - return aByte851; - } - - /** - * Gets the aBoolean852. - * @return The aBoolean852. - */ - public boolean isaBoolean852() { - return aBoolean852; - } - - /** - * Gets the anInt853. - * @return The anInt853. - */ - public int getAnInt853() { - return anInt853; - } - - /** - * Gets the aByte854. - * @return The aByte854. - */ - public byte getaByte854() { - return aByte854; - } - - /** - * Gets the aBoolean856. - * @return The aBoolean856. - */ - public boolean isaBoolean856() { - return aBoolean856; - } - - /** - * Gets the aBoolean857. - * @return The aBoolean857. - */ - public boolean isaBoolean857() { - return aBoolean857; - } - - /** - * Gets the aShortArray859. - * @return The aShortArray859. - */ - public short[] getaShortArray859() { - return aShortArray859; - } - - /** - * Gets the aByteArray861. - * @return The aByteArray861. - */ - public byte[] getaByteArray861() { - return aByteArray861; - } - - /** - * Gets the aShort862. - * @return The aShort862. - */ - public short getaShort862() { - return aShort862; - } - - /** - * Gets the aBoolean863. - * @return The aBoolean863. - */ - public boolean isaBoolean863() { - return aBoolean863; - } - - /** - * Gets the anInt864. - * @return The anInt864. - */ - public int getAnInt864() { - return anInt864; - } - - /** - * Gets the aShortArray866. - * @return The aShortArray866. - */ - public short[] getaShortArray866() { - return aShortArray866; - } - - /** - * Gets the anIntArray868. - * @return The anIntArray868. - */ - public int[] getAnIntArray868() { - return anIntArray868; - } - - /** - * Gets the anInt869. - * @return The anInt869. - */ - public int getAnInt869() { - return anInt869; - } - - /** - * Gets the anInt870. - * @return The anInt870. - */ - public int getAnInt870() { - return anInt870; - } - - /** - * Gets the anInt871. - * @return The anInt871. - */ - public int getAnInt871() { - return anInt871; - } - - /** - * Gets the anInt872. - * @return The anInt872. - */ - public int getAnInt872() { - return anInt872; - } - - /** - * Gets the anInt874. - * @return The anInt874. - */ - public int getAnInt874() { - return anInt874; - } - - /** - * Gets the anInt875. - * @return The anInt875. - */ - public int getAnInt875() { - return anInt875; - } - - /** - * Gets the anInt876. - * @return The anInt876. - */ - public int getAnInt876() { - return anInt876; - } - - /** - * Gets the anInt879. - * @return The anInt879. - */ - public int getAnInt879() { - return anInt879; - } - - /** - * Gets the aShortArray880. - * @return The aShortArray880. - */ - public short[] getaShortArray880() { - return aShortArray880; - } - - /** - * Gets the anInt884. - * @return The anInt884. - */ - public int getAnInt884() { - return anInt884; - } - - /** - * Gets the configId. - * @return The configId. - */ - public int getConfigId() { - if(configFileId != -1) { - return VarbitDefinition.forNPCID(configFileId).getConfigId(); - } else return configFileId; - } - - public int getVarbitOffset() { - if(configFileId != -1){ - return VarbitDefinition.forNPCID(configFileId).getBitShift(); - } - return -1; - } - - public int getVarbitSize() { - if(configFileId != -1){ - return VarbitDefinition.forNPCID(configFileId).getBitSize() - VarbitDefinition.forNPCID(configFileId).getBitShift(); - } - return -1; - } - - /** - * Gets the anInt889. - * @return The anInt889. - */ - public int getAnInt889() { - return anInt889; - } - - /** - * Gets the anIntArray892. - * @return The anIntArray892. - */ - public int[] getAnIntArray892() { - return anIntArray892; - } - - /** - * Gets the aShort894. - * @return The aShort894. - */ - public short getaShort894() { - return aShort894; - } - - /** - * Gets the aShortArray896. - * @return The aShortArray896. - */ - public short[] getaShortArray896() { - return aShortArray896; - } - - /** - * Gets the anInt897. - * @return The anInt897. - */ - public int getAnInt897() { - return anInt897; - } - - /** - * Gets the anInt899. - * @return The anInt899. - */ - public int getAnInt899() { - return anInt899; - } - - /** - * Gets the anInt901. - * @return The anInt901. - */ - public int getAnInt901() { - return anInt901; - } - - /** - * Gets the standAnimation. - * @return The standAnimation. - */ - public int getStandAnimation() { - return standAnimation; - } - - /** - * Gets the walkAnimation. - * @return The walkAnimation. - */ - public int getWalkAnimation() { - return walkAnimation; - } - - /** - * Gets the turnAnimation. - * @return The turnAnimation. - */ - public int getTurnAnimation() { - return turnAnimation; - } - - /** - * Gets the turn180Animation. - * @return The turn180Animation. - */ - public int getTurn180Animation() { - return turn180Animation; - } - - /** - * Gets the turnCWAnimation. - * @return The turnCWAnimation. - */ - public int getTurnCWAnimation() { - return turnCWAnimation; - } - - /** - * Gets the turnCCWAnimation. - * @return The turnCCWAnimation. - */ - public int getTurnCCWAnimation() { - return turnCCWAnimation; - } - - /** - * @return the dropTables. - */ - public NPCDropTables getDropTables() { - return dropTables; - } - - /** - * @param dropTables the dropTables to set. - */ - public void setDropTables(NPCDropTables dropTables) { - this.dropTables = dropTables; - } - - /** - * Gets the combatLevel. - * @return The combatLevel. - */ - public int getCombatLevel() { - return combatLevel; - } - - /** - * Sets the combatLevel. - * @param combatLevel The combatLevel to set. - */ - public void setCombatLevel(int combatLevel) { - this.combatLevel = combatLevel; - } - - /** - * Gets the combatDistance. - * @return The combatDistance. - */ - public int getCombatDistance() { - return combatDistance; - } - - /** - * Sets the combatDistance. - * @param combatDistance The combatDistance to set. - */ - public void setCombatDistance(int combatDistance) { - this.combatDistance = combatDistance; - } - - /** - * Gets the combatGraphics. - * @return The combatGraphics. - */ - public Graphics[] getCombatGraphics() { - return combatGraphics; - } - - /** - * Sets the combatGraphics. - * @param combatGraphics The combatGraphics to set. - */ - public void setCombatGraphics(Graphics[] combatGraphics) { - this.combatGraphics = combatGraphics; - } - - /** - * Gets the renderAnimationId value. - * @return The renderAnimationId. - */ - public int getRenderAnimationId() { - return renderAnimationId; - } - - /** - * Sets the renderAnimationId value. - * @param renderAnimationId The renderAnimationId to set. - */ - public void setRenderAnimationId(int renderAnimationId) { - this.renderAnimationId = renderAnimationId; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/cache/def/impl/RenderAnimationDefinition.java b/Server/src/main/java/core/cache/def/impl/RenderAnimationDefinition.java deleted file mode 100644 index 08d1c97bc..000000000 --- a/Server/src/main/java/core/cache/def/impl/RenderAnimationDefinition.java +++ /dev/null @@ -1,328 +0,0 @@ -package core.cache.def.impl; - -import core.cache.Cache; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; - -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.nio.ByteBuffer; - -/** - * Holds definitions for render animations. - * @author Jagex - * @author Emperor - * - */ -public class RenderAnimationDefinition { - - public int turn180Animation; - public int anInt951 = -1; - public int anInt952; - public int turnCWAnimation = -1; - public int anInt954; - public int anInt955; - public int anInt956; - public int anInt957; - public int anInt958; - public int[] anIntArray959 = null; - public int anInt960; - public int anInt961 = 0; - public int anInt962; - public int walkAnimationId; - public int anInt964; - public int anInt965; - public int anInt966; - public int[] standAnimationIds; - public int anInt969; - public int[] anIntArray971; - public int standAnimationId; - public int anInt973; - public int anInt974; - public int anInt975; - public int runAnimationId; - public int anInt977; - public boolean aBoolean978; - public int[][] anIntArrayArray979; - public int anInt980; - public int turnCCWAnimation; - public int anInt983; - public int anInt985; - public int anInt986; - public int anInt987; - public int anInt988; - public int anInt989; - public int anInt990; - public int anInt992; - public int anInt993; - public int anInt994; - - /** - * Gets the render animation definitions for the given id. - * @param animId The render animation id. - * @return The render animation definitions. - */ - public static RenderAnimationDefinition forId(int animId) { - if (animId == -1) { - return null; - } - byte[] data = Cache.getIndexes()[2].getFileData(32, animId); - RenderAnimationDefinition defs = new RenderAnimationDefinition(); - if (data != null) { - defs.parse(ByteBuffer.wrap(data)); - } else { - SystemLogger.logErr("No definitions found for render animation " + animId + ", size=" + Cache.getIndexes()[2].getFilesSize(32) + "!"); - } - return defs; - } - - private void parse(ByteBuffer buffer) { - for (;;) { - int opcode = buffer.get() & 0xFF; - if (opcode == 0) { - break; - } - parseOpcode(buffer, opcode); - } - } - - private void parseOpcode(ByteBuffer buffer, int opcode) { - if (opcode == 54) { - @SuppressWarnings("unused") - int anInt1260 = (buffer.get() & 0xFF) << 6; - @SuppressWarnings("unused") - int anInt1227 = (buffer.get() & 0xFF) << 6; - } else if (opcode == 55) { - int[] anIntArray1246 = new int[12]; - int i_14_ = buffer.get() & 0xFF; - anIntArray1246[i_14_] = buffer.getShort() & 0xFFFF; - } else if (opcode == 56) { - int[][] anIntArrayArray1217 = new int[12][]; - int i_12_ = buffer.get() & 0xFF; - anIntArrayArray1217[i_12_] = new int[3]; - for (int i_13_ = 0; i_13_ < 3; i_13_++) - anIntArrayArray1217[i_12_][i_13_] = buffer.getShort(); - } else if ((opcode ^ 0xffffffff) != -2) { - if ((opcode ^ 0xffffffff) != -3) { - if (opcode != 3) { - if ((opcode ^ 0xffffffff) != -5) { - if (opcode == 5) - anInt977 = buffer.getShort() & 0xFFFF; - else if ((opcode ^ 0xffffffff) != -7) { - if (opcode == 7) - anInt960 = buffer.getShort() & 0xFFFF; - else if ((opcode ^ 0xffffffff) == -9) - anInt985 = buffer.getShort() & 0xFFFF; - else if (opcode == 9) - anInt957 = buffer.getShort() & 0xFFFF; - else if (opcode == 26) { - anInt973 = (short) (4 * buffer - .get() & 0xFF); - anInt975 = (short) (buffer.get() & 0xFF * 4); - } else if ((opcode ^ 0xffffffff) == -28) { - if (anIntArrayArray979 == null) - anIntArrayArray979 = new int[12][]; - int i = buffer.get() & 0xFF; - anIntArrayArray979[i] = new int[6]; - for (int i_1_ = 0; (i_1_ ^ 0xffffffff) > -7; i_1_++) - anIntArrayArray979[i][i_1_] = buffer - .getShort(); - } else if ((opcode ^ 0xffffffff) == -29) { - anIntArray971 = new int[12]; - for (int i = 0; i < 12; i++) { - anIntArray971[i] = buffer - .get() & 0xFF; - if (anIntArray971[i] == 255) - anIntArray971[i] = -1; - } - } else if (opcode != 29) { - if (opcode != 30) { - if ((opcode ^ 0xffffffff) != -32) { - if (opcode != 32) { - if ((opcode ^ 0xffffffff) != -34) { - if (opcode != 34) { - if (opcode != 35) { - if ((opcode ^ 0xffffffff) != -37) { - if ((opcode ^ 0xffffffff) != -38) { - if (opcode != 38) { - if ((opcode ^ 0xffffffff) != -40) { - if ((opcode ^ 0xffffffff) != -41) { - if ((opcode ^ 0xffffffff) == -42) - turnCWAnimation = buffer - .getShort() & 0xFFFF; - else if (opcode != 42) { - if ((opcode ^ 0xffffffff) == -44) - buffer.getShort(); - else if ((opcode ^ 0xffffffff) != -45) { - if ((opcode ^ 0xffffffff) == -46) - anInt964 = buffer - .getShort() & 0xFFFF; - else if ((opcode ^ 0xffffffff) != -47) { - if (opcode == 47) - anInt966 = buffer - .getShort() & 0xFFFF; - else if (opcode == 48) - anInt989 = buffer - .getShort() & 0xFFFF; - else if (opcode != 49) { - if ((opcode ^ 0xffffffff) != -51) { - if (opcode != 51) { - if (opcode == 52) { - int i = buffer - .get() & 0xFF; - anIntArray959 = new int[i]; - standAnimationIds = new int[i]; - for (int i_2_ = 0; i_2_ < i; i_2_++) { - standAnimationIds[i_2_] = buffer - .getShort() & 0xFFFF; - int i_3_ = buffer - .get() & 0xFF; - anIntArray959[i_2_] = i_3_; - anInt994 += i_3_; - } - } else if (opcode == 53) - aBoolean978 = false; - } else - anInt962 = buffer - .getShort() & 0xFFFF; - } else - anInt990 = buffer - .getShort() & 0xFFFF; - } else - anInt952 = buffer - .getShort() & 0xFFFF; - } else - anInt983 = buffer - .getShort() & 0xFFFF; - } else - anInt955 = buffer - .getShort() & 0xFFFF; - } else - turnCCWAnimation = buffer - .getShort() & 0xFFFF; - } else - turn180Animation = buffer - .getShort() & 0xFFFF; - } else - anInt954 = buffer - .getShort() & 0xFFFF; - } else - anInt958 = (buffer - .getShort() & 0xFFFF); - } else - anInt951 = (buffer - .get() & 0xFF); - } else - anInt965 = (buffer - .getShort()); - } else - anInt969 = (buffer - .getShort() & 0xFFFF); - } else - anInt993 = buffer - .get() & 0xFF; - } else - anInt956 = (buffer.getShort()); - } else - anInt961 = buffer - .getShort() & 0xFFFF; - } else - anInt988 = buffer.get() & 0xFF; - } else - anInt980 = buffer.getShort() & 0xFFFF; - } else - anInt992 = buffer.get() & 0xFF; - } else - runAnimationId = buffer.getShort() & 0xFFFF; - } else - anInt986 = buffer.getShort() & 0xFFFF; - } else - anInt987 = buffer.getShort() & 0xFFFF; - } else - anInt974 = buffer.getShort() & 0xFFFF; - } else { - standAnimationId = buffer.getShort() & 0xFFFF; - walkAnimationId = buffer.getShort() & 0xFFFF; - if ((standAnimationId ^ 0xffffffff) == -65536) - standAnimationId = -1; - if ((walkAnimationId ^ 0xffffffff) == -65536) - walkAnimationId = -1; - } - } - - public RenderAnimationDefinition() { - anInt957 = -1; - anInt954 = -1; - anInt960 = -1; - anInt958 = -1; - anInt965 = 0; - anInt973 = 0; - turn180Animation = -1; - anInt956 = 0; - standAnimationId = -1; - standAnimationIds = null; - anInt952 = -1; - anInt983 = -1; - anInt985 = -1; - anInt962 = -1; - anInt966 = -1; - anInt977 = -1; - anInt975 = 0; - runAnimationId = -1; - anInt988 = 0; - turnCCWAnimation = -1; - anInt987 = -1; - anInt980 = 0; - anInt964 = -1; - walkAnimationId = -1; - anInt986 = -1; - aBoolean978 = true; - anInt992 = 0; - anInt955 = -1; - anInt989 = -1; - anInt974 = -1; - anInt969 = 0; - anInt994 = 0; - anInt990 = -1; - anInt993 = 0; - } - - 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()) { - Object object = f.get(def); - if (object != null) { - int length = Array.getLength(object); - System.out.print(f.getName() + ", ["); - for (int i = 0; i < length; i++) { - System.out.print(Array.get(object, i) + (i < (length - 1) ? ", " : "]")); - } - continue; - } - } - System.out.println(f.getName() + ", " + f.get(def)); - } - } - for (Field f : def.getClass().getSuperclass().getDeclaredFields()) { - if (!Modifier.isStatic(f.getModifiers())) { - if (f.getType().isArray()) { - Object object = f.get(def); - if (object != null) { - int length = Array.getLength(object); - System.out.print(f.getName() + ", ["); - for (int i = 0; i < length; i++) { - System.out.print(Array.get(object, i) + (i < (length - 1) ? ", " : "]")); - } - continue; - } - } - System.out.println(f.getName() + ", " + f.get(def)); - } - } - } -} diff --git a/Server/src/main/java/core/cache/def/impl/VarbitDefinition.java b/Server/src/main/java/core/cache/def/impl/VarbitDefinition.java deleted file mode 100644 index 19361b4af..000000000 --- a/Server/src/main/java/core/cache/def/impl/VarbitDefinition.java +++ /dev/null @@ -1,175 +0,0 @@ -package core.cache.def.impl; - -import core.cache.Cache; -import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * Handles config definition reading. - * @author Emperor - */ -public final class VarbitDefinition { - - /** - * The config definitions mapping. - */ - private static final Map MAPPING = new HashMap<>(); - - /** - * The bit size flags. - */ - private static final int[] BITS = new int[32]; - - /** - * The file id. - */ - private final int id; - - /** - * The config id. - */ - private int configId; - - /** - * The bit shift amount. - */ - private int bitShift; - - /** - * The bit amount. - */ - private int bitSize; - - /** - * Constructs a new {@code ConfigFileDefinition} {@code Object}. - * @param id The file id. - */ - public VarbitDefinition(int id) { - this.id = id; - } - - /** - * Initializes the bit flags. - */ - static { - int flag = 2; - for (int i = 0; i < 32; i++) { - BITS[i] = flag - 1; - flag += flag; - } - } - - /** - * Gets the config file definitions for the given file id. - * @param id The file id. - * @return The definition. - */ - public static VarbitDefinition forObjectID(int id) { - return forId(id,10); - } - - public static VarbitDefinition forNPCID(int id){ - return forId(id,10); - } - - public static VarbitDefinition forItemID(int id){ - return forId(id,30); - } - - public static VarbitDefinition forId(int id, int shiftAmount){ - /*VarbitDefinition def = MAPPING.get(id); - if (def != null) { - return def; - }*/ - VarbitDefinition def; - def = new VarbitDefinition(id); - byte[] bs = Cache.getIndexes()[22].getFileData(id >>> 10, id & 0x3ff); - if (bs != null) { - ByteBuffer buffer = ByteBuffer.wrap(bs); - int opcode = 0; - while ((opcode = buffer.get() & 0xFF) != 0) { - if (opcode == 1) { - def.configId = buffer.getShort() & 0xFFFF; - def.bitShift = buffer.get() & 0xFF; - def.bitSize = buffer.get() & 0xFF; - } - } - } - MAPPING.put(id, def); - return def; - } - - public static void main(String... args) throws Throwable { - GameWorld.prompt(false); - for (int i = 0; i < 15000; i++) { - VarbitDefinition def = forObjectID(i); - if (def != null && def.configId == 33) { - System.out.println("Config file [id=" + i + ", shift=" + def.bitShift + "]!"); - } - } - } - - /** - * Gets the current config value for this file. - * @param player The player. - * @return The config value. - */ - public int getValue(Player player) { - int size = BITS[bitSize - bitShift]; - int bitValue = player.varpManager.get(getConfigId()).getBitRangeValue(getBitShift(), getBitShift() + (bitSize - bitShift)); - if(bitValue != 0){ - return size & (bitValue >>> bitShift); - } - return size & (player.getConfigManager().get(configId) >>> bitShift); - } - - /** - * Gets the mapping. - * @return The mapping. - */ - public static Map getMapping() { - return MAPPING; - } - - /** - * Gets the id. - * @return The id. - */ - public int getId() { - return id; - } - - /** - * Gets the configId. - * @return The configId. - */ - public int getConfigId() { - return configId; - } - - /** - * Gets the bitShift. - * @return The bitShift. - */ - public int getBitShift() { - return bitShift; - } - - /** - * Gets the bitSize. - * @return The bitSize. - */ - public int getBitSize() { - return bitSize; - } - - @Override - public String toString() { - return "ConfigFileDefinition [id=" + id + ", configId=" + configId + ", bitShift=" + bitShift + ", bitSize=" + bitSize + "]"; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/cache/misc/Container.java b/Server/src/main/java/core/cache/misc/Container.java deleted file mode 100644 index 9760845ff..000000000 --- a/Server/src/main/java/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/java/core/cache/misc/ContainersInformation.java b/Server/src/main/java/core/cache/misc/ContainersInformation.java deleted file mode 100644 index f786c1d2f..000000000 --- a/Server/src/main/java/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/java/core/cache/misc/FilesContainer.java b/Server/src/main/java/core/cache/misc/FilesContainer.java deleted file mode 100644 index 5f14e448f..000000000 --- a/Server/src/main/java/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/java/core/cache/misc/buffer/BufferInputStream.java b/Server/src/main/java/core/cache/misc/buffer/BufferInputStream.java deleted file mode 100644 index a801c870d..000000000 --- a/Server/src/main/java/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/java/core/cache/misc/buffer/BufferOutputStream.java b/Server/src/main/java/core/cache/misc/buffer/BufferOutputStream.java deleted file mode 100644 index 88ea2ac69..000000000 --- a/Server/src/main/java/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/java/core/cache/misc/buffer/ByteBufferUtils.java b/Server/src/main/java/core/cache/misc/buffer/ByteBufferUtils.java deleted file mode 100644 index 6c08a9db1..000000000 --- a/Server/src/main/java/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 getTriByte(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/java/core/game/component/AccessMaskBuilder.java b/Server/src/main/java/core/game/component/AccessMaskBuilder.java deleted file mode 100644 index a3ce053e4..000000000 --- a/Server/src/main/java/core/game/component/AccessMaskBuilder.java +++ /dev/null @@ -1,104 +0,0 @@ -package core.game.component; - -/** - * Used to generate component option settings. - * @author Mangis - */ -public final class AccessMaskBuilder { - - /** - * Contains the value which should be sent in access mask packet. - */ - private int value; - - /** - * Sets default option setting. - * @param allowed If the packet for the default option should be sent to the - * server. - */ - public void allowDefaultOption(boolean allowed) { - value &= ~(0x1); - if (allowed) { - value |= 0x1; - } - } - - /** - * Sets right click option settings. If specified option is not allowed, it - * will not appear in right click menu and the packet will not be send to - * server when clicked. - * @param optionId The option index. - * @param show If the option is allowed. - */ - public void showMenuOption(int optionId, boolean show) { - if (optionId < 0 || optionId > 9) { - throw new IllegalArgumentException("Option index must be 0-9."); - } - value &= ~(0x1 << (optionId + 1)); - if (show) { - value |= (0x1 << (optionId + 1)); - } - } - - /** - * Sets use on option settings. If nothing is allowed then 'use' option will - * not appear in right click menu. - */ - public void setUseOnSettings(boolean groundItems, boolean npcs, boolean objects, boolean otherPlayer, boolean selfPlayer, boolean component) { - int useFlag = 0; - if (groundItems) { - useFlag |= 0x1; - } - if (npcs) { - useFlag |= 0x2; - } - if (objects) { - useFlag |= 0x4; - } - if (otherPlayer) { - useFlag |= 0x8; - } - if (selfPlayer) { - useFlag |= 0x10; - } - if (component) { - useFlag |= 0x20; - } - value &= ~(127 << 7); // disable - value |= useFlag << 7; - } - - /** - * Sets interface events depth. For example, we have inventory interface - * which is opened on gameframe interface (548) If depth is 1, then the - * clicks in inventory will also invoke click event handler scripts on - * gameframe interface. - * @param depth The depth value. - */ - public void setInterfaceEventsDepth(int depth) { - if (depth < 0 || depth > 7) { - throw new IllegalArgumentException("depth must be 0-7."); - } - value &= ~(0x7 << 18); - value |= (depth << 18); - } - - /** - * Flags other component options being allowed to be used on this component. - * @param allow If an option can be used on this component. - */ - public void allowUsage(boolean allow) { - value &= ~(1 << 22); - if (allow) { - value |= (1 << 22); - } - } - - /** - * Gets the current value. - * @return The value. - */ - public int getValue() { - return value; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/container/Container.java b/Server/src/main/java/core/game/container/Container.java deleted file mode 100644 index d21244289..000000000 --- a/Server/src/main/java/core/game/container/Container.java +++ /dev/null @@ -1,1028 +0,0 @@ -package core.game.container; - -import core.cache.def.impl.ItemDefinition; -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -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; - -/** - * Represents a container which contains items. - * - * @author Emperor - */ -public class Container { - - /** - * The item array. A crystalline - */ - private Item[] items; - - /** - * The capacity. - */ - private final int capacity; - - /** - * The current sort type. - */ - private SortType sortType; - - /** - * The current container type. - */ - private final ContainerType type; - - /** - * The current container event. - */ - private ContainerEvent event; - - /** - * The container listeners. - */ - private final List listeners = new ArrayList<>(20); - - /** - * Constructs a new {@code Container} {@code Object}. - * - * @param capacity The capacity. - */ - public Container(int capacity) { - this(capacity, ContainerType.DEFAULT); - } - - /** - * Constructs a new {@code Container.java} {@code Object}. - * - * @param capacity the capacity. - * @param items the items to add. - */ - public Container(int capacity, Item... items) { - this(capacity); - add(items); - } - - /** - * Constructs a new {@code Container} {@code Object}. - * - * @param capacity The capacity. - * @param type The container type. - */ - public Container(int capacity, ContainerType type) { - this(capacity, type, SortType.ID); - } - - /** - * Constructs a new {@code Container} {@code Object}. - * - * @param capacity The capacity. - * @param type The container type. - * @param sortType The sort type. - */ - public Container(int capacity, ContainerType type, SortType sortType) { - this.capacity = capacity; - this.type = type; - this.items = new Item[capacity]; - this.sortType = sortType; - this.event = new ContainerEvent(capacity); - } - - /** - * Registers a container listener. - * - * @param listener The container listener. - * @return This container instance, for chaining. - */ - public Container register(ContainerListener listener) { - listeners.add(listener); - return this; - } - - /** - * Adds the items. - * - * @param items The items to add. - * @return {@code True} if successfully added all items. - */ - public boolean add(Item... items) { - boolean addedAll = true; - for (Item item : items) { - if (item == null) { - continue; - } - if (!add(item, false)) { - addedAll = false; - break; - } - } - update(); - return addedAll; - } - - public void addList(List items){ - items.stream().filter(Objects::nonNull).forEach(this::add); - update(); - } - - /** - * Inserts an item into a specific slot. - * - * @param fromSlot The original slot of the item. - * @param toSlot The slot to insert into. - */ - public void insert(int fromSlot, int toSlot) { - insert(fromSlot, toSlot, true); - } - - /** - * Inserts an item into a specific slot. - * - * @param fromSlot The original slot of the item. - * @param toSlot The slot to insert into. - * @param update If the container packets should be sent. - */ - public void insert(int fromSlot, int toSlot, boolean update) { - Item temp = items[fromSlot]; - if (toSlot > fromSlot) { - for (int i = fromSlot; i < toSlot; i++) { - replace(get(i + 1), i, false); - } - } else if (fromSlot > toSlot) { - for (int i = fromSlot; i > toSlot; i--) { - replace(get(i - 1), i, false); - } - } - replace(temp, toSlot, update); - } - - /** - * Adds an item to this container if full it goes to ground. - * - * @param item the item. - * @param player the player. - * @return {@code True} if added. - */ - public boolean add(final Item item, final Player player) { - if (!add(item, true, -1)) { - GroundItemManager.create(item, player); - return false; - } - return true; - } - - public boolean addIfDoesntHave(final Item item) { - if (containsItem(item)) { - return false; - } else { - return add(item); - - } - } - - /** - * Adds an item to this container. - * - * @param item The item. - * @return {@code True} if the item got added. - */ - public boolean add(Item item) { - return add(item, true, -1); - } - - /** - * Adds an item to this container. - * - * @param item The item to add. - * @param fireListener If we should update. - * @return {@code True} if the item got added. - */ - public boolean add(Item item, boolean fireListener) { - return add(item, fireListener, -1); - } - - /** - * Adds an item to this container. - * - * @param item The item to add. - * @param fireListener If we should update. - * @param preferredSlot The slot to add the item in, when possible. - * @return {@code True} if the item got added. - */ - public boolean add(Item item, boolean fireListener, int preferredSlot) { - item = item.copy(); - int maximum = getMaximumAdd(item); - if (maximum == 0) { - return false; - } -// if (preferredSlot > -1 && items[preferredSlot] != null) { -// preferredSlot = -1; -// } - if (item.getAmount() > maximum) { - item.setAmount(maximum); - } - if (type != ContainerType.NEVER_STACK && (item.getDefinition().isStackable() || type == ContainerType.ALWAYS_STACK || type == ContainerType.SHOP)) { - boolean hashBased = sortType == SortType.HASH; - for (int i = 0; i < items.length; i++) { - if (items[i] != null) { - if ((hashBased && items[i].getIdHash() == item.getIdHash()) || (!hashBased && items[i].getId() == item.getId())) { - int totalCount = item.getAmount() + items[i].getAmount(); - items[i] = new Item(items[i].getId(), totalCount, item.getCharge()); - items[i].setIndex(i); - event.flag(i, items[i]); - if (fireListener) { - update(); - } - return true; - } - } - } - int slot = preferredSlot > -1 ? preferredSlot : freeSlot(); - if (slot == -1) { - return false; - } - items[slot] = item; - item.setIndex(slot); - event.flag(slot, item); - if (fireListener) { - update(); - } - return true; - } - int slots = freeSlots(); - if (slots >= item.getAmount()) { - for (int i = 0; i < item.getAmount(); i++) { - int slot = i == 0 && preferredSlot > -1 ? preferredSlot : freeSlot(); - items[slot] = new Item(item.getId(), 1, item.getCharge()); - items[slot].setIndex(slot); - event.flag(slot, items[slot]); - } - if (fireListener) { - update(); - } - return true; - } - return false; - } - - /** - * Removes a set of items. - * - * @param items The set of items. - * @return {@code True} if all items got successfully removed. - */ - public boolean remove(Item... items) { - boolean removedAll = true; - for (Item item : items) { - if (!remove(item, false)) { - removedAll = false; - } - } - update(); - return removedAll; - } - - /** - * Removes an item. - * - * @param item The item. - * @return {@code True} if the item got removed, {@code false} if not. - */ - public boolean remove(Item item) { - return remove(item, true); - } - - /** - * Removes an item. - * - * @param item The item to remove. - * @param fireListener If the fire listener should be "notified". - * @return {@code True} if the item got removed,
{@code false} if not. - */ - public boolean remove(Item item, boolean fireListener) { - int slot = getSlot(item); - if (slot != -1) { - return remove(item, slot, fireListener); - } - return false; - } - - /** - * Removes an item from this container. - * - * @param item The item. - * @param slot The item slot. - * @param fireListener If the fire listener should be "notified". - * @return {@code True} if the item got removed,
{@code false} if the - * item on the slot was null or the ids didn't match. - */ - public boolean remove(Item item, int slot, boolean fireListener) { - Item oldItem = items[slot]; - if (oldItem == null || oldItem.getId() != item.getId()) { - return false; - } - if (item.getAmount() < 1) { - return true; - } - if (oldItem.getDefinition().isStackable() || type.equals(ContainerType.ALWAYS_STACK) || type == ContainerType.SHOP) { - if (item.getAmount() >= oldItem.getAmount()) { - items[slot] = null; - event.flagNull(slot); - if (fireListener) { - update(); - } - return true; - } - items[slot] = new Item(item.getId(), oldItem.getAmount() - item.getAmount(), item.getCharge()); - items[slot].setIndex(slot); - event.flag(slot, items[slot]); - if (fireListener) { - update(); - } - return true; - } - items[slot] = null; - event.flagNull(slot); - int removed = 1; - for (int i = removed; i < item.getAmount(); i++) { - slot = getSlot(item); - if (slot != -1) { - items[slot] = null; - event.flagNull(slot); - } else { - break; - } - } - if (fireListener) { - update(); - } - return true; - } - - /** - * Replaces the item on the given slot with the argued item. - * - * @param item The item. - * @param slot The slot. - * @return The old item. - */ - public Item replace(Item item, int slot) { - return replace(item, slot, true); - } - - /** - * Replaces the item on the given slot with the argued item. - * - * @param item The item. - * @param slot The slot. - * @param fireListener If the listener should be "notified". - * @return The old item. - */ - public Item replace(Item item, int slot, boolean fireListener) { - if (item != null) { - if (item.getAmount() < 1 && type != ContainerType.SHOP) { - item = null; - } else { - item = item.copy(); - } - } - Item oldItem = items[slot]; - items[slot] = item; - if (item == null) { - event.flagNull(slot); - } else { - item.setIndex(slot); - event.flag(slot, item); - } - if (fireListener) { - update(); - } - return oldItem; - } - - /** - * Updates the container. - */ - public void update() { - if (event.getChangeCount() < 1 && !event.isClear()) { - return; - } - for (ContainerListener listener : listeners) { - listener.update(this, event); - } - event.setClear(false); - event = new ContainerEvent(capacity); - } - - /** - * Updates the container. - */ - public void update(boolean force) { - if (event.getChangeCount() < 1 && !force) { - return; - } - for (ContainerListener listener : listeners) { - listener.update(this, event); - } - event = new ContainerEvent(capacity); - } - - /** - * Refreshes the entire container. - */ - public void refresh() { - for (ContainerListener listener : listeners) { - listener.refresh(this); - } - event = new ContainerEvent(capacity); - } - - public void refresh(ContainerListener listener) { - listener.refresh(this); - event = new ContainerEvent(capacity); - } - - /** - * Gets the item on the given slot. - * - * @param slot The slot. - * @return The id of the item on the slot, or 0 if the item wasn't there. - */ - public int getAsId(int slot) { - if (slot < 0 || slot >= items.length || items[slot] == null) { - return 0; - } - return items[slot].getId(); - } - - - /** - * Gets the item on the given slot. - * - * @param slot The slot. - * @return The item on the slot, or {@code null} if the item wasn't there. - */ - public Item get(int slot) { - if (slot < 0 || slot >= items.length) { - return null; - } - return items[slot]; - } - - /** - * Gets the item on the given slot. - * - * @param slot The slot. - * @return The item on the slot, or a new constructed item with id 0 if the - * item wasn't there. - */ - public Item getNew(int slot) { - Item item = items[slot]; - if (item != null) { - return item; - } - return new Item(0); - } - - /** - * Gets the item id on the given slot. - * - * @param slot The slot. - * @return The id of the item on the slot. - */ - public int getId(int slot) { - if (slot >= items.length) { - return -1; - } - Item item = items[slot]; - if (item != null) { - return item.getId(); - } - 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 -> { - JSONObject i = (JSONObject) item; - int slot = Integer.parseInt(i.get("slot").toString()); - int id = Integer.parseInt(i.get("id").toString()); - int amount = Integer.parseInt(i.get("amount").toString()); - int charge = Integer.parseInt(i.get("charge").toString()); - if (id >= ItemDefinition.getDefinitions().size() || id < 0 || slot >= items.length || id == Items.MAGIC_CARPET_5614) { - } else { - Item it = items[slot] = new Item(id,amount,charge); - it.setIndex(slot); - total.set(total.get() + (int)it.getValue()); - } - }); - } - - /** - * 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. - * - * @param c The container to copy. - */ - public void copy(Container c) { - items = new Item[c.items.length]; - for (int i = 0; i < items.length; i++) { - Item it = c.items[i]; - if (it == null) { - continue; - } - items[i] = new Item(it.getId(), it.getAmount(), it.getCharge()); - items[i].setIndex(i); - } - } - - /** - * Formats a container for the SQL database. - * - * @return the string. - */ - public String format() { - String log = ""; - Map map = new HashMap<>(); - Integer old = null; - for (Item item : items) { - if (item != null) { - old = map.get(item.getId()); - map.put(item.getId(), old == null ? item.getAmount() : old + item.getAmount()); - - } - } - for (int i : map.keySet()) { - log += i + "," + map.get(i) + "|"; - } - if (log.length() > 0 && log.charAt(log.length() - 1) == '|') { - log = log.substring(0, log.length() - 1); - } - return log; - } - - /** - * Checks if the container contains an item. - * - * @param item the Item - * @return {@code True} if so. - */ - public boolean containsItem(Item item) { - return contains(item.getId(), item.getAmount()); - } - - /** - * Checks if the containers contains these items. - * - * @param items the items. - * @return {@code True} if so. - */ - public boolean containsItems(Item... items) { - for (Item i : items) { - if (!containsItem(i)) { - return false; - } - } - return true; - } - - /** - * Checks if the container contains an item. - * - * @param itemId The item id. - * @param amount The amount. - * @return {@code True} if so. - */ - public boolean contains(int itemId, int amount) { - int count = 0; - for (Item item : items) { - if (item != null && item.getId() == itemId) { - if ((count += item.getAmount()) >= amount) { - return true; - } - } - } - return false; - } - - /** - * Checks if the containers contains ONE item. - * - * @param itemId - * @return - */ - public boolean containsAtLeastOneItem(int itemId) { - for (Item item : items) { - if (item != null && item.getId() == itemId && item.getAmount() == 1) { - return true; - } - } - return false; - } - - /** - * Checks if the containers contains AT LEAST ONE item from a list of items. - * @param itemIds - * @return true if at least one item from list of IDs is in the container - */ - public boolean containsAtLeastOneItem(int[] itemIds) { - for (int id : itemIds) { - if (getAmount(id) >= 1) - return true; - } - return false; - } - - public boolean containsAtLeastOneItem(Item... items) { - for (Item item : items) { - if (containsItem(item)) { - return true; - } - } - return false; - } - - /** - * Checks if the container contains all items. - * - * @param itemIds to check - * @return {@code True} if so. - */ - public boolean containsAll(int... itemIds) { - for (int i : itemIds) { - if (!containsAtLeastOneItem(i)) { - return false; - } - } - return true; - } - - /** - * Adds a container to this container. - * - * @param container The container. - */ - public void addAll(Container container) { - add(container.items); - } - - /** - * Checks the maximum amount of this item we can add. - * - * @param item The item. - * @return The maximum amount we can add. - */ - public int getMaximumAdd(Item item) { - if (type != ContainerType.NEVER_STACK) { - if (item.getDefinition().isStackable() || type == ContainerType.ALWAYS_STACK || type == ContainerType.SHOP) { - if (contains(item.getId(), 1)) { - return Integer.MAX_VALUE - getAmount(item); - } - return freeSlots() > 0 ? Integer.MAX_VALUE : 0; - } - } - return freeSlots(); - } - - /** - * Checks if the container has space for the item. - * - * @param item The item to check. - * @return {@code True} if so. - */ - public boolean hasSpaceFor(Item item) { - return item.getAmount() <= getMaximumAdd(item); - } - - public boolean hasSpaceFor(Item... items) { - Container c = new Container(28, ContainerType.DEFAULT); - c.add(items); - return this.hasSpaceFor(c); - } - - /** - * Checks if this container has space to add the other container. - * - * @param c The other container. - * @return {@code True} if so. - */ - public boolean hasSpaceFor(Container c) { - if (c == null) { - return false; - } - Container check = new Container(capacity, type); - check.addAll(this); - for (Item item : c.items) { - if (item != null) { - if (!check.add(item, false)) { - return false; - } - } - } - return true; - } - - /** - * Gets the item slot. - * - * @param item The item. - * @return The slot of the item in this container. - */ - public int getSlot(Item item) { - if (item == null) { - return -1; - } - int id = item.getId(); - for (int i = 0; i < items.length; i++) { - Item it = items[i]; - if (it != null && it.getId() == id) { - return i; - } - } - return -1; - } - - /** - * Gets the item instance. - * - * @param item the item. - * @return the item. - */ - public Item getItem(Item item) { - return get(getSlot(item)); - } - - public Item get(Item item){ - for(Item i : items){ - if(i == null) continue; - if(item.getId() == i.getId()) return i; - } - return null; - } - - /** - * Gets the next free slot. - * - * @return The slot, or -1 if there are no available slots. - */ - public int freeSlot() { - for (int i = 0; i < items.length; i++) { - if (items[i] == null) { - return i; - } - } - return -1; - } - - /** - * Gets the slot of where to add the item. - * - * @param item The item to add. - * @return The slot where the item will go. - */ - public int getAddSlot(Item item) { - if (type != ContainerType.NEVER_STACK && (item.getDefinition().isStackable() || type.equals(ContainerType.ALWAYS_STACK) || type == ContainerType.SHOP)) { - boolean hashBased = sortType == SortType.HASH; - for (int i = 0; i < items.length; i++) { - if (items[i] != null) { - if ((hashBased && items[i].getIdHash() == item.getIdHash()) || (!hashBased && items[i].getId() == item.getId())) { - return i; - } - } - } - } - return freeSlot(); - } - - /** - * Gets the number of free slots. - * - * @return The number of free slots. - */ - public int freeSlots() { - return capacity - itemCount(); - } - - /** - * Gets the size of this container. - * - * @return The size of this container. - */ - public int itemCount() { - int size = 0; - for (int i = 0; i < items.length; i++) { - if (items[i] != null) { - size++; - } - } - return size; - } - - /** - * Checks if the player has all the item ids in the inventory. - * - * @param itemIds The item ids. - * @return {@code True} if so. - */ - public boolean containItems(int... itemIds) { - for (int i = 0; i < itemIds.length; i++) { - if (!contains(itemIds[i], 1)) { - return false; - } - } - return true; - } - - /** - * Gets the amount of an item. - * - * @param item The item. - * @return The amount of this item in this container. - */ - public int getAmount(Item item) { - if (item == null) { - return 0; - } - int count = 0; - for (Item i : items) { - if (i != null && i.getId() == item.getId()) { - count += i.getAmount(); - } - } - return count; - } - - /** - * Gets the amount. - * - * @param id the id. - * @return the amount. - */ - public int getAmount(int id) { - return getAmount(new Item(id)); - } - - /** - * Shifts the elements in the Container to the appropriate position. - */ - public void shift() { - final Item itemss[] = items; - clear(false); - for (Item item : itemss) { - if (item == null) { - continue; - } - add(item, false); - } - refresh(); - } - - /** - * Checks if the container is empty. - * - * @return {@code True} if so. - */ - public boolean isEmpty() { - for (Item item : items) { - if (item != null) { - return false; - } - } - return true; - } - - /** - * Checks if the container is full. - * - * @return {@code True} if so. - */ - public boolean isFull() { - return freeSlots() < 1; - } - - /** - * Clears and updates the container. - */ - public void clear() { - clear(true); - } - - /** - * Clears the container. - * - * @param update If the container should be updated. - */ - public void clear(boolean update) { - items = new Item[capacity]; - event.flagEmpty(); - if (update) { - refresh(); - } - } - - /** - * Gets the wealth. - * - * @return the wealth. - */ - public int getWealth() { - int wealth = 0; - for (Item i : items) { - if (i == null) { - continue; - } - wealth += i.getDefinition().getValue() * i.getAmount(); - } - return wealth; - } - - /** - * Returns an array representing this container. - * - * @return The array. - */ - public Item[] toArray() { - return items; - } - - /** - * Gets the listeners. - * - * @return The listeners. - */ - public List getListeners() { - return listeners; - } - - /** - * Gets the capacity. - * - * @return The capacity of this container. - */ - public int capacity() { - return capacity; - } - - /** - * Gets the event. - * - * @return the event. - */ - public ContainerEvent getEvent() { - return event; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/container/access/BitregisterAssembler.java b/Server/src/main/java/core/game/container/access/BitregisterAssembler.java deleted file mode 100644 index 8410c63f2..000000000 --- a/Server/src/main/java/core/game/container/access/BitregisterAssembler.java +++ /dev/null @@ -1,122 +0,0 @@ -package core.game.container.access; - -import core.game.node.entity.player.Player; - -/** - * Contains the mask value methods. The 'access mask' is actually just a bit - * register that contains permissions for a specific interface (@Peterbjornx) - * @date 5/02/2013 - * @author Stacx - * @author Emperor - */ -public final class BitregisterAssembler { - - /** - * The register size. - */ - public static final int SIZE = 32 - 1; - - /** - * Examine option. - */ - public static final int EXAMINE_OPT = 9; - - /** - * Allow dragging. - */ - public static final int DRAGABLE = 17; - - /** - * Allow switching item slots. - */ - public static final int SLOT_SWITCH = 20; - - /** - * The flags. - */ - private boolean[] permissions = new boolean[SIZE]; - - /** - * Constructs a new {@code BitregisterAssembler} {@code Object}. - * @param permissions The permissions. - */ - public BitregisterAssembler(int... permissions) { - for (int i : permissions) { - this.permissions[i] = true; - } - } - - /** - * Constructs a new {@code BitregisterAssembler} {@code Object}. - */ - public BitregisterAssembler(String[] options) { - enableOptions(options); - } - - /** - * Enables the given options ({@code null} options will remain disabled). - * @param options The options. - */ - public void enableOptions(String...options) { - if (options.length > 9) { - throw new IllegalStateException("Too many options specified - maximum 9 allowed!"); - } - for (int i = 0; i < options.length; i++) { - if (options[i] != null && !options[i].equals("null")) { - permissions[i] = true; - } - } - } - - /** - * Enables the examine option. - */ - public void enableExamineOption() { - permissions[EXAMINE_OPT] = true; - } - - /** - * Enables items being dragged. - */ - public void enableDragging() { - permissions[DRAGABLE] = true; - } - - /** - * Enables item switching slots (& dragging). - */ - public void enableSlotSwitch() { - enableDragging(); - permissions[SLOT_SWITCH] = true; - } - - /** - * Send and assemble a bit register for our - * @param player , the player instance - * @param interfaceIndex , the interface index - * @param childIndex , the child index for our interface - * @param offset , the offset for the loop in client - * @param length , the length of our loop - */ - public static void send(Player player, int interfaceIndex, int childIndex, int offset, int length, BitregisterAssembler assembler) { - if (offset >= length) { - throw new RuntimeException("Offset cannot excess length. length = " + length); - } - player.getPacketDispatch().sendAccessMask(assembler.calculateRegister(), childIndex, interfaceIndex, offset, length); - } - - /** - * Calculates the register. - * @return The value. - */ - public int calculateRegister() { - int value = 0; - for (int i = 0; i < SIZE; i++) { - if (permissions[i]) { - value |= 2 << i; - } - } - return value; - } - -} diff --git a/Server/src/main/java/core/game/container/impl/BankContainer.java b/Server/src/main/java/core/game/container/impl/BankContainer.java deleted file mode 100644 index 016dee8f9..000000000 --- a/Server/src/main/java/core/game/container/impl/BankContainer.java +++ /dev/null @@ -1,556 +0,0 @@ -package core.game.container.impl; - -import rs09.ServerConstants; -import core.game.component.Component; -import core.game.container.*; -import core.game.container.access.BitregisterAssembler; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.IronmanMode; -import core.game.node.item.Item; -import rs09.game.system.config.ItemConfigParser; -import rs09.game.world.GameWorld; -import core.net.packet.PacketRepository; -import core.net.packet.context.ContainerContext; -import core.net.packet.out.ContainerPacket; - -import java.nio.ByteBuffer; - -/** - * Represents the bank container. - * @author Emperor - */ -public final class BankContainer extends Container { - - /** - * The bank container size. - */ - public static final int SIZE = ServerConstants.BANK_SIZE; - - /** - * The maximum amount of bank tabs - */ - public static final int TAB_SIZE = 11; - - /** - * The player reference. - */ - private Player player; - - /** - * The bank listener. - */ - private final BankListener listener; - - /** - * Set {@code true} to note items. - */ - private boolean noteItems; - - /** - * If the bank is open. - */ - private boolean open; - - /** - * The last x-amount entered. - */ - private int lastAmountX = 50; - - /** - * The current tab index. - */ - private int tabIndex = 10; - - /** - * The tab start indexes. - */ - private final int[] tabStartSlot = new int[TAB_SIZE]; - - /** - * If inserting items is enabled. - */ - private boolean insertItems; - - /** - * Construct a new {@code BankContainer} {@code Object}. - * @param player The player reference. - */ - public BankContainer(Player player) { - super(SIZE, ContainerType.ALWAYS_STACK, SortType.HASH); - super.register(listener = new BankListener(player)); - this.player = player; - } - - /** - * Open the bank. - */ - public void open() { - if (open) { - return; - } - if (player.getIronmanManager().checkRestriction(IronmanMode.ULTIMATE)) { - return; - } - if (!player.getBankPinManager().isUnlocked() && !GameWorld.getSettings().isDevMode()) { - player.getBankPinManager().openType(1); - return; - } - player.getInterfaceManager().openComponent(762).setCloseEvent((player, c) -> { - BankContainer.this.close(); - return true; - }); - player.getInterfaceManager().openSingleTab(new Component(763)); - super.refresh(); - player.getInventory().getListeners().add(listener); - player.getInventory().refresh(); - player.varpManager.get(1249).setVarbit(0,lastAmountX).send(player); - player.getPacketDispatch().sendAccessMask(1278, 73, 762, 0, ServerConstants.BANK_SIZE); - BitregisterAssembler assembly = new BitregisterAssembler(0, 1, 2, 3, 4, 5); - assembly.enableExamineOption(); - assembly.enableSlotSwitch(); - player.getPacketDispatch().sendAccessMask(assembly.calculateRegister(), 0, 763, 0, 27); - player.getPacketDispatch().sendRunScript(1451, ""); - open = true; - setTabConfigurations(); - sendBankSpace(); - } - - public void open(Player player) { - if (open) { - return; - } - if (player.getIronmanManager().checkRestriction(IronmanMode.ULTIMATE)) { - return; - } - if (!player.getBankPinManager().isUnlocked() && !GameWorld.getSettings().isDevMode()) { - player.getBankPinManager().openType(1); - return; - } - player.getInterfaceManager().openComponent(762).setCloseEvent((player1, c) -> { - BankContainer.this.close(); - return true; - }); - this.player.getBank().refresh(player.getBank().listener); - player.getInterfaceManager().openSingleTab(new Component(763)); - player.getInventory().getListeners().add(player.getBank().listener); - player.getInventory().refresh(); - player.varpManager.get(1249).setVarbit(0,lastAmountX).send(player); - player.getPacketDispatch().sendAccessMask(1278, 73, 762, 0, SIZE); - BitregisterAssembler assembly = new BitregisterAssembler(0, 1, 2, 3, 4, 5); - assembly.enableExamineOption(); - assembly.enableSlotSwitch(); - player.getPacketDispatch().sendAccessMask(assembly.calculateRegister(), 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); - } - - /** - * Closes the bank. - */ - public void close() { - open = false; - player.getInventory().getListeners().remove(listener); - player.getInterfaceManager().closeSingleTab(); - player.removeAttribute("search"); - player.getPacketDispatch().sendRunScript(571, ""); - } - - /** - * Adds an item to the bank container. - * @param slot The item slot. - * @param amount The amount. - */ - public void addItem(int slot, int amount) { - if (slot < 0 || slot > player.getInventory().capacity() || amount < 1) { - return; - } - Item item = player.getInventory().get(slot); - if (item == null) { - return; - } - int maximum = player.getInventory().getAmount(item); - if (amount > maximum) { - amount = maximum; - } - int maxCount = super.getMaximumAdd(item); - if (amount > maxCount) { - amount = maxCount; - if (amount < 1) { - player.getPacketDispatch().sendMessage("There is not enough space left in your bank."); - return; - } - } - if (!item.getDefinition().getConfiguration(ItemConfigParser.BANKABLE, true)) { - player.sendMessage("A magical force prevents you from banking this item"); - return; - } - item = new Item(item.getId(), amount, item.getCharge()); - boolean unnote = !item.getDefinition().isUnnoted(); - if (player.getInventory().remove(item, slot, true)) { - Item add = unnote ? new Item(item.getDefinition().getNoteId(), amount, item.getCharge()) : item; - if (unnote && !add.getDefinition().isUnnoted()) { - add = item; - } - int preferredSlot = -1; - if (tabIndex != 0 && tabIndex != 10 && !super.contains(add.getId(), 1)) { - preferredSlot = tabStartSlot[tabIndex] + getItemsInTab(tabIndex); - insert(freeSlot(), preferredSlot, false); - increaseTabStartSlots(tabIndex); - } - super.add(add, true, preferredSlot); - setTabConfigurations(); - } - } - - /** - * 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 - * container. - * @param slot The slot. - * @param amount The amount. - */ - public void takeItem(int slot, int amount) { - if (slot < 0 || slot > super.capacity() || amount <= 0) { - return; - } - Item item = get(slot); - if (item == null) { - return; - } - if (amount > item.getAmount()) { - amount = item.getAmount(); // It always stacks in the bank. - } - item = new Item(item.getId(), amount, item.getCharge()); - int noteId = item.getDefinition().getNoteId(); - Item add = noteItems && noteId > 0 ? new Item(noteId, amount, item.getCharge()) : item; - int maxCount = player.getInventory().getMaximumAdd(add); - if (amount > maxCount) { - item.setAmount(maxCount); - add.setAmount(maxCount); - if (maxCount < 1) { - player.getPacketDispatch().sendMessage("Not enough space in your inventory."); - return; - } - } - if (noteItems && noteId < 0) { - player.getPacketDispatch().sendMessage("This item can't be withdrawn as a note."); - add = item; - } - if (super.remove(item, slot, false)) { - player.getInventory().add(add); - } - int tabId = getTabByItemSlot(slot); - if (get(slot) == null) { - decreaseTabStartSlots(tabId); - } - setTabConfigurations(); - shift(); - if (player.getAttribute("search", false)) { - reopen(); - } - } - - /** - * Updates the last x-amount entered. - * @param amount The amount to set. - */ - public void updateLastAmountX(int amount) { - this.lastAmountX = amount; - player.varpManager.get(1249).setVarbit(0,amount).send(player); - } - - /** - * Gets the tab the item slot is in. - * @param itemSlot The item slot. - * @return The tab index. - */ - public int getTabByItemSlot(int itemSlot) { - int tabId = 0; - for (int i = 0; i < tabStartSlot.length; i++) { - if (itemSlot >= tabStartSlot[i]) { - tabId = i; - } - } - return tabId; - } - - /** - * Increases a tab's start slot. - * @param startId The start id. - */ - public void increaseTabStartSlots(int startId) { - for (int i = startId + 1; i < tabStartSlot.length; i++) { - tabStartSlot[i]++; - } - } - - /** - * Decreases a tab's start slot. - * @param startId The start id. - */ - public void decreaseTabStartSlots(int startId) { - if (startId == 10) { - return; - } - for (int i = startId + 1; i < tabStartSlot.length; i++) { - tabStartSlot[i]--; - } - if (getItemsInTab(startId) == 0) { - collapseTab(startId); - } - } - - /** - * Gets the array index for a tab. - * @param tabId The tab id. - * @return The array index. - */ - public static int getArrayIndex(int tabId) { - if (tabId == 41 || tabId == 74) { - return 10; - } - int base = 39; - for (int i = 1; i < 10; i++) { - if (tabId == base) { - return i; - } - base -= 2; - } - return -1; - } - - /** - * 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); - } - - /** - * Collapses a tab. - * @param tabId The tab index. - */ - public void collapseTab(int tabId) { - int size = getItemsInTab(tabId); - Item[] tempTabItems = new Item[size]; - for (int i = 0; i < size; i++) { - tempTabItems[i] = get(tabStartSlot[tabId] + i); - replace(null, tabStartSlot[tabId] + i, false); - } - shift(); - for (int i = tabId; i < tabStartSlot.length - 1; i++) { - tabStartSlot[i] = tabStartSlot[i + 1] - size; - } - tabStartSlot[10] = tabStartSlot[10] - size; - for (int i = 0; i < size; i++) { - int slot = freeSlot(); - 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); - } - - /** - * 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. - * @return The amount of items in this tab. - */ - public int getItemsInTab(int tabId) { - return tabStartSlot[tabId + 1] - tabStartSlot[tabId]; - } - - /** - * Checks if the item can be added. - * @param item the item. - * @return {@code True} if so. - */ - public boolean canAdd(Item item) { - return item.getDefinition().getConfiguration(ItemConfigParser.BANKABLE, true); - } - - /** - * Gets the last x-amount. - * @return The last x-amount. - */ - public int getLastAmountX() { - return lastAmountX; - } - - /** - * If items have to be noted. - * @return If items have to be noted {@code true}. - */ - public boolean isNoteItems() { - return noteItems; - } - - /** - * Set if items have to be noted. - * @param noteItems If items have to be noted {@code true}. - */ - public void setNoteItems(boolean noteItems) { - this.noteItems = noteItems; - } - - /** - * Gets the tabStartSlot value. - * @return The tabStartSlot. - */ - public int[] getTabStartSlot() { - return tabStartSlot; - } - - /** - * Gets the tabIndex value. - * @return The tabIndex. - */ - public int getTabIndex() { - return tabIndex; - } - - /** - * Sets the tabIndex value. - * @param tabIndex The tabIndex to set. - */ - public void setTabIndex(int tabIndex) { - this.tabIndex = tabIndex; - } - - /** - * Sets the insert items value. - * @param insertItems The insert items value. - */ - public void setInsertItems(boolean insertItems) { - this.insertItems = insertItems; - } - - /** - * Gets the insert items value. - * @return {@code True} if inserting items mode is enabled. - */ - public boolean isInsertItems() { - return insertItems; - } - - /** - * Checks if the bank is opened. - * @return {@code True} if so. - */ - public boolean isOpen() { - return open; - } - - /** - * Listens to the bank container. - * @author Emperor - */ - private static class BankListener implements ContainerListener { - - /** - * The player reference. - */ - private Player player; - - /** - * Construct a new {@code BankListener} {@code Object}. - * @param player The player reference. - */ - public BankListener(Player player) { - this.player = player; - } - - @Override - 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())); - } 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)); - } 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/java/core/game/container/impl/EquipmentContainer.java b/Server/src/main/java/core/game/container/impl/EquipmentContainer.java deleted file mode 100644 index 7d7d46291..000000000 --- a/Server/src/main/java/core/game/container/impl/EquipmentContainer.java +++ /dev/null @@ -1,285 +0,0 @@ -package core.game.container.impl; - -import core.game.container.Container; -import core.game.container.ContainerEvent; -import core.game.container.ContainerListener; -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; -import core.net.packet.out.WeightUpdate; -import core.plugin.Plugin; -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; -import rs09.game.system.config.ItemConfigParser; - -/** - * Represents the equipment container. - * @author Emperor - */ -public final class EquipmentContainer extends Container { - /** - * The equipment slots. - */ - public static final int SLOT_HAT = 0, SLOT_CAPE = 1, SLOT_AMULET = 2, SLOT_WEAPON = 3, SLOT_CHEST = 4, SLOT_SHIELD = 5, SLOT_LEGS = 7, SLOT_HANDS = 9, SLOT_FEET = 10, SLOT_RING = 12, SLOT_ARROWS = 13; - - /** - * The bonus names. - */ - private static final String[] BONUS_NAMES = { "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Stab: ", "Slash: ", "Crush: ", "Magic: ", "Ranged: ", "Summoning: ", "Strength: ", "Prayer: " }; - - /** - * The player. - */ - private final Player player; - - /** - * Constructs a new {@code EquipmentContainer} {@code Object}. - * @param player The player. - */ - public EquipmentContainer(Player player) { - super(14); - this.player = player; - register(new EquipmentListener(player)); - } - - @Override - public boolean add(Item item, boolean fire) { - return add(item, fire, true); - } - - /** - * Adds an item to the equipment container. - * @param item The item to add. - * @param fire If we should refresh. - * @param fromInventory If the item is being equipped from the inventory. - * @return {@code True} if succesful, {@code false} if not. - */ - public boolean add(Item item, boolean fire, boolean fromInventory) { - return add(item, player.getInventory().getSlot(item), fire, fromInventory); - } - - /** - * Adds an item to the equipment container. - * @param item The item to add. - * @param inventorySlot The inventory slot of the item. - * @param fire If we should refresh. - * @param fromInventory If the item is being equipped from the inventory. - * @return {@code True} if succesful, {@code false} if not. - */ - public boolean add(Item item, int inventorySlot, boolean fire, boolean fromInventory) { - int slot = item.getDefinition().getConfiguration(ItemConfigParser.EQUIP_SLOT, -1); - if (slot == -1 && item.getDefinition().getConfiguration(ItemConfigParser.WEAPON_INTERFACE, -1) != -1) { - slot = SLOT_WEAPON; - } -// slot = 3; - if (slot < 0) { - return false; // Item can't be equipped. - } - if (!item.getDefinition().hasRequirement(player, true, true)) { - return false; - } - Item current = super.get(slot); - if (current != null && current.getId() == item.getId() && current.getDefinition().isStackable()) { - int amount = getMaximumAdd(item); - if (item.getAmount() > amount) { - amount += current.getAmount(); - } else { - amount = current.getAmount() + item.getAmount(); - } - if (fromInventory) { - player.getInventory().remove(new Item(item.getId(), amount - current.getAmount())); - } - replace(new Item(item.getId(), amount), slot); - return true; - } - if (fromInventory && current != null) { - Plugin plugin = current.getDefinition().getConfiguration("equipment", null); - if (plugin != null) { - Object object = plugin.fireEvent("unequip", player, current, item); - if (object != null && !((Boolean) object)) { - return true; - } - } - } - if (fromInventory && !player.getInventory().remove(item, inventorySlot, true)) { - return false; - } - Item secondary = null; - if (item.getDefinition().getConfiguration(ItemConfigParser.TWO_HANDED, false)) { - secondary = get(SLOT_SHIELD); - } else if (slot == SLOT_SHIELD) { - secondary = get(SLOT_WEAPON); - if (secondary != null && !secondary.getDefinition().getConfiguration(ItemConfigParser.TWO_HANDED, false)) { - secondary = null; - } - } - int currentSlot = -1; - if (current != null) { - currentSlot = inventorySlot; - if (current.getDefinition().isStackable() && player.getInventory().contains(current.getId(), 1)) { - currentSlot = -1; - } - } - if (current != null && !player.getInventory().add(current, true, inventorySlot)) { - player.getInventory().add(item); - player.getPacketDispatch().sendMessage("Not enough space in your inventory!"); - return false; - } - if (secondary != null && !player.getInventory().add(secondary)) { - if (current != null && currentSlot != -1) { - player.getInventory().remove(current, currentSlot, false); - } - player.getInventory().add(item); - player.getPacketDispatch().sendMessage("Not enough space in your inventory!"); - return false; - } - super.replace(item, slot, fire); - if (item.getSlot() == SLOT_WEAPON) { - player.getPacketDispatch().sendString(item.getName(), 92, 0); - } - if (secondary != null) { - super.remove(secondary); - } - return true; - } - - /** - * Listens to the equipment container. - * @author Emperor - */ - private static class EquipmentListener implements ContainerListener { - - /** - * The player. - */ - private final Player player; - - /** - * Constructs a new {@code EquipmentContainer} {@code Object}. - * @param player The player. - */ - public EquipmentListener(Player player) { - this.player = player; - } - - @Override - public void update(Container c, ContainerEvent event) { - int[] slots = event.getSlots(); - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 387, 28, 94, event.getItems(), false, slots)); - update(c); - boolean updateDefenceAnimation = false; - for (int slot : slots) { - if (slot == EquipmentContainer.SLOT_WEAPON) { - player.getProperties().setAttackSpeed(c.getNew(slot).getDefinition().getConfiguration(ItemConfigParser.ATTACK_SPEED, 4)); - WeaponInterface inter = player.getExtension(WeaponInterface.class); - if (inter == null) { - break; - } - inter.updateInterface(); - updateDefenceAnimation = true; - } else if (slot == EquipmentContainer.SLOT_SHIELD) { - updateDefenceAnimation = true; - } - } - if (updateDefenceAnimation) { - player.getProperties().updateDefenceAnimation(); - } - } - - @Override - public void refresh(Container c) { - player.getProperties().setAttackSpeed(c.getNew(3).getDefinition().getConfiguration(ItemConfigParser.ATTACK_SPEED, 4)); - WeaponInterface inter = player.getExtension(WeaponInterface.class); - if (inter != null) { - inter.updateInterface(); - } - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 387, 28, 94, c.toArray(), 14, false)); - update(c); - player.getProperties().updateDefenceAnimation(); - } - - /** - * Updates the bonuses, weight, animations, ... - * @param c The container. - */ - public void update(Container c) { - if (c.getNew(SLOT_SHIELD).getId() != 11283 && player.getAttribute("dfs_spec", false)) { - player.removeAttribute("dfs_spec"); - player.getProperties().getCombatPulse().setHandler(null); - if (!player.getSettings().isSpecialToggled()) { - player.getConfigManager().set(301, 0); - } - } - player.getAppearance().setAnimations(); - player.getUpdateMasks().register(new AppearanceFlag(player)); - player.getSettings().updateWeight(); - updateBonuses(player); - } - } - - /** - * Updates the bonuses. - * @param player The player. - */ - public static void updateBonuses(Player player) { - int[] bonuses = new int[15]; - for (Item item : player.getEquipment().toArray()) { - 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]; - } - } - } - Item weapon = player.getEquipment().get(SLOT_WEAPON); - if(weapon != null && weapon.getDefinition().getRequirement(Skills.STRENGTH) > 0 && SkillcapePerks.isActive(SkillcapePerks.FINE_ATTUNEMENT,player)){ - int[] bonus = weapon.getDefinition().getConfiguration(ItemConfigParser.BONUS, new int[15]); - bonuses[11] += Math.ceil(bonus[11] * 0.20); - } - Item shield = player.getEquipment().get(SLOT_SHIELD); - if(shield != null && SkillcapePerks.isActive(SkillcapePerks.GRAND_BULLWARK,player)){ - bonuses[5] += Math.ceil(bonuses[5] * 0.20); - bonuses[6] += Math.ceil(bonuses[6] * 0.20); - bonuses[7] += Math.ceil(bonuses[7] * 0.20); - bonuses[9] += Math.ceil(bonuses[9] * 0.20); - } - if (shield != null && shield.getId() == 11283) { - int increase = shield.getCharge() / 20; - bonuses[5] += increase; - bonuses[6] += increase; - bonuses[7] += increase; - bonuses[9] += increase; - } - player.getProperties().setBonuses(bonuses); - update(player); - } - - /** - * Updates the equipment stats interface. - * @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++) { - 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); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/guild/CraftingGuildPlugin.java b/Server/src/main/java/core/game/content/activity/guild/CraftingGuildPlugin.java deleted file mode 100644 index dd3aedb80..000000000 --- a/Server/src/main/java/core/game/content/activity/guild/CraftingGuildPlugin.java +++ /dev/null @@ -1,293 +0,0 @@ -package core.game.content.activity.guild; - -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.Skillcape; -import core.game.content.global.action.DoorActionHandler; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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.game.node.item.Item; -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 the crafting guild. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CraftingGuildPlugin extends OptionHandler { - - /** - * Represents the brown apron item. - */ - private static final Item BROWN_APRON = new Item(1757); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(2647).getHandlers().put("option:open", this); - NPCDefinition.forId(804).getHandlers().put("option:trade", this); - new MasterCrafterDialogue().init(); - new TannerDialogue().init(); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final int id = node instanceof Scenery ? ((Scenery) node).getId() : ((NPC) node).getId(); - switch (option) { - case "open": - switch (id) { - case 2647: - if (player.getLocation().getY() >= 3289) { - if (player.getSkills().getStaticLevel(Skills.CRAFTING) < 40) { - player.getDialogueInterpreter().sendDialogues(805, null, "Sorry, only experienced crafters are allowed in here.", "You must be level 40 or above to enter."); - return true; - } - if (!player.getEquipment().containsItem(BROWN_APRON)) { - player.getDialogueInterpreter().open(805, true, true); - return true; - } - player.getDialogueInterpreter().sendDialogues(805, null, "Welcome to the Guild of Master Craftsmen."); - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - } else { - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - return true; - } - break; - } - return true; - case "trade": - switch (id) { - case 804: - TanningProduct.open(player, 804); - break; - } - return true; - } - return true; - } - - @Override - public Location getDestination(Node node, Node n) { - if (n instanceof Scenery) { - return DoorActionHandler.getDestination((Player) node, (Scenery) n); - } - return null; - } - - /** - * Represents the dialogue plugin used for the crafting master. - * @author 'Vexia - * @version 1.0 - */ - public final class MasterCrafterDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code MasterCrafterDialogue} {@code Object}. - */ - public MasterCrafterDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MasterCrafterDialogue} {@code Object}. - * @param player the player. - */ - public MasterCrafterDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MasterCrafterDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (args.length == 2) { - npc("Where's your brown apron? You can't come in here", "unless you're wearing one."); - stage = 100; - return true; - } - npc = (NPC) args[0]; - npc("Hello, and welcome to the Crafting Guild. Accomplished", "crafters from all over the land come here to use our", "top notch workshops."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (npc.getId() == 805) { - if (Skillcape.isMaster(player, Skills.CRAFTING)) { - player("Hey, could I buy a Skillcape of Crafting?"); - stage = 3; - } else { - player("Hey, what is that cape you're wearing?", "I don't recognise it."); - stage = 1; - } - } else { - end(); - } - break; - case 1: - npc("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."); - stage = 2; - break; - case 2: - end(); - break; - case 3: - npc("Certainly! Right after you pay me 99000 coins."); - stage = 4; - break; - case 4: - options("Okay, here you go.", "No, thanks."); - stage = 5; - break; - case 5: - switch (buttonId) { - case 1: - player("Okay, here you go."); - stage = 6; - break; - case 2: - end(); - break; - } - break; - case 6: - if (Skillcape.purchase(player, Skills.CRAFTING)) { - npc("There you go! Enjoy."); - } - stage = 7; - break; - case 7: - end(); - break; - case 100: - npc("Where's your borwn apron? You can't come in here", "unless you're wearing one."); - stage = 101; - break; - case 101: - player("Err... I haven't got one."); - stage = 102; - break; - case 102: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 805, 2732, 2733 }; - } - - } - - /** - * Represents the dialogue used for the tanner npc. - * @author 'Vexia - * @version 1.0 - */ - public final class TannerDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TannerDialogue} {@code Object}. - */ - public TannerDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TannerDialogue} {@code Object}. - * @param player the player. - */ - public TannerDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TannerDialogue(player); - } - - @Override - public boolean open(Object... args) { - 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: - options("Can I buy some leather then?", "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 = 20; - break; - } - break; - case 10: - npc("Certainly!"); - stage = 11; - break; - case 11: - end(); - TanningProduct.open(player, 804); - break; - case 20: - 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 = 21; - break; - case 21: - npc("Alternatively you could try hard leather. It's not so", "easy to craft, but I only charge 3gp per cowhide to", "prepare it, and it makes much studier armour."); - stage = 22; - break; - case 22: - npc("I can also tan snake hides and dragonhides, suitable for", "crafting into the highest quality armour for rangers."); - stage = 23; - break; - case 23: - player("Thanks, I'll bear it in mind."); - stage = 24; - break; - case 24: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 804 }; - } - - } -} diff --git a/Server/src/main/java/core/game/content/activity/guild/HeroGuildPlugin.java b/Server/src/main/java/core/game/content/activity/guild/HeroGuildPlugin.java deleted file mode 100644 index fa46612bd..000000000 --- a/Server/src/main/java/core/game/content/activity/guild/HeroGuildPlugin.java +++ /dev/null @@ -1,117 +0,0 @@ -package core.game.content.activity.guild; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.EnchantedJewellery; -import core.game.content.global.action.DoorActionHandler; -import core.game.node.entity.skill.summoning.familiar.Familiar; -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.npc.NPC; -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.Plugin; -import core.plugin.Initializable; -import rs09.plugin.PluginManager; - -/** - * Represents the hero guild. - * @author Vexia - */ -@Initializable -public final class HeroGuildPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(2624).getHandlers().put("option:open", this); - SceneryDefinition.forId(2625).getHandlers().put("option:open", this); - PluginManager.definePlugin(new JewelleryRechargePlugin()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final int id = ((Scenery) node).getId(); - switch (option) { - case "open": - switch (id) { - case 2624: - case 2625: - // player.getPacketDispatch().sendMessage("You need to complete the Heroes' Quest."); - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - break; - } - return true; - } - return true; - } - - /** - * Handles the recharging of dragonstone jewellery. - * @author Vexia - */ - public static final class JewelleryRechargePlugin extends UseWithHandler { - - /** - * The ids of rechargeable items. - */ - 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} - */ - public JewelleryRechargePlugin() { - super(IDS); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(36695, OBJECT_TYPE, this); - addHandler(7339, NPC_TYPE, this); - addHandler(7340, NPC_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final EnchantedJewellery jewellery = EnchantedJewellery.forItem(event.getUsedItem()); - if (jewellery == null && event.getUsedItem().getId() != 2572) { - return true; - } - boolean fam = event.getUsedWith() instanceof NPC; - if (fam && jewellery != EnchantedJewellery.AMULET_OF_GLORY & jewellery != EnchantedJewellery.AMULET_OF_GLORY_T) { - return false; - } - if (fam) { - Familiar familiar = (Familiar) event.getUsedWith(); - if (!player.getFamiliarManager().isOwner(familiar)) { - return false; - } - familiar.animate(Animation.create(7882)); - } - player.lock(1); - player.animate(Animation.create(832)); - if(event.getUsedItem().getId() == 2572){ - player.getInventory().replace(new Item(14646), event.getUsedItem().getSlot()); - } else { - player.getInventory().replace(new Item(jewellery.getIds()[0]), event.getUsedItem().getSlot()); - } - String name = event.getUsedItem().getName().toLowerCase(); - for (int i = 0; i < 4; i++) { - name = name.replace("(" + (i + 1) + ")", "").trim(); - } - if (!fam) { - player.sendMessage("You dip the " + name + " in the fountain..."); - } else { - player.sendMessage("Your titan recharges the glory."); - } - return true; - } - - } -} diff --git a/Server/src/main/java/core/game/content/activity/gwd/GodWarsFaction.java b/Server/src/main/java/core/game/content/activity/gwd/GodWarsFaction.java deleted file mode 100644 index 582c02a57..000000000 --- a/Server/src/main/java/core/game/content/activity/gwd/GodWarsFaction.java +++ /dev/null @@ -1,99 +0,0 @@ -package core.game.content.activity.gwd; - -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * The god wars factions. - * @author Emperor - */ -public enum GodWarsFaction { - ARMADYL(6222, 6246, 87, 11694, 11718, 11720, 11722, 12670, 12671, 14671), - BANDOS(6260, 6283, 11061, 11696, 11724, 11726, 11728), - SARADOMIN(6247, 6259, 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(6203, 6221, 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); - - /** - * The start NPC id. - */ - private final int startId; - - /** - * The end NPC id. - */ - private final int endId; - - /** - * The protection items. - */ - private final int[] protectionItems; - - /** - * Constructs a new {@code GodWarsFaction} {@code Object}. - * @param startId The start NPC id. - * @param endId The end NPC id. - * @param protectionItems The protection items for this faction. - */ - private GodWarsFaction(int startId, int endId, int... protectionItems) { - this.startId = startId; - this.endId = endId; - this.protectionItems = protectionItems; - } - - /** - * Gets the god wars faction for the given NPC id. - * @param npcId The NPC id. - * @return The faction for this NPC. - */ - public static GodWarsFaction forId(int npcId) { - for (GodWarsFaction faction : values()) { - if (npcId >= faction.getStartId() && npcId <= faction.getEndId()) { - return faction; - } - } - return null; - } - - /** - * Checks if the player is protected from this faction. - * @param player The player. - * @return {@code True} if no NPCs of this faction should attack the - * player. - */ - public boolean isProtected(Player player) { - for (Item item : player.getEquipment().toArray()) { - if (item != null) { - for (int id : protectionItems) { - if (item.getId() == id) { - return true; - } - } - } - } - return false; - } - - /** - * Gets the startId. - * @return The startId. - */ - public int getStartId() { - return startId; - } - - /** - * Gets the endId. - * @return The endId. - */ - public int getEndId() { - return endId; - } - - /** - * Gets the protectionItems. - * @return The protectionItems. - */ - public int[] getProtectionItems() { - return protectionItems; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/gwd/GodWarsNPC.java b/Server/src/main/java/core/game/content/activity/gwd/GodWarsNPC.java deleted file mode 100644 index 29e2d3ca5..000000000 --- a/Server/src/main/java/core/game/content/activity/gwd/GodWarsNPC.java +++ /dev/null @@ -1,123 +0,0 @@ -package core.game.content.activity.gwd; - -import java.util.ArrayList; -import java.util.List; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.DeathTask; -import core.game.node.entity.npc.AbstractNPC; -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.world.map.Location; -import core.plugin.Initializable; -import core.game.world.map.RegionManager; - -/** - * Handles a god wars NPC. - * @author Emperor - */ -@Initializable -public final class GodWarsNPC extends AbstractNPC { - - /** - * The aggressive behavior. - */ - private static final AggressiveBehavior AGGRO_BEHAVIOR = new AggressiveBehavior() { - - @Override - public boolean canSelectTarget(Entity entity, Entity target) { - if (!target.isActive() || DeathTask.isDead(target) || DeathTask.isDead(entity)) { - return false; - } - if (!target.getProperties().isMultiZone() && target.inCombat()) { - return false; - } - if (target instanceof GodWarsNPC) { - if (((GodWarsNPC) target).faction != ((GodWarsNPC) entity).faction) { - return true; - } - } else if (target instanceof Player) { - if (!((GodWarsNPC) entity).faction.isProtected((Player) target)) { - return true; - } - } - return false; - } - - @Override - public List getPossibleTargets(Entity entity, int radius) { - List targets = new ArrayList<>(20); - for (Player player : RegionManager.getLocalPlayers(entity, radius)) { - if (canSelectTarget(entity, player)) { - targets.add(player); - } - } - if (!targets.isEmpty()) { - return targets; - } - for (NPC npc : RegionManager.getLocalNpcs(entity, radius)) { - if (canSelectTarget(entity, npc)) { - targets.add(npc); - } - } - return targets; - } - }; - - /** - * The god wars faction (0=armadyl, 1=bandos, 2=saradomin, 3=zamorak). - */ - private GodWarsFaction faction; - - /** - * Constructs a new {@code GodWarsNPC} {@code Object}. - */ - public GodWarsNPC() { - super(-1, null); - } - - /** - * Constructs a new {@code GodWarsNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - public GodWarsNPC(int id, Location location) { - super(id, location); - } - - @Override - public void init() { - super.init(); - setWalks(true); - faction = GodWarsFaction.forId(getId()); - } - - @Override - public void setDefaultBehavior() { - setAggressive(true); - aggressiveHandler = new AggressiveHandler(this, AGGRO_BEHAVIOR); - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if (style == CombatStyle.MELEE && faction == GodWarsFaction.ARMADYL && entity instanceof Player) { - ((Player) entity).getPacketDispatch().sendMessage("The aviansie is flying too high for you to attack using melee."); - return false; - } - return super.isAttackable(entity, style); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new GodWarsNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] { 6210, 6211, 6212, 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, 6221, 6229, 6230, 6231, 6232, 6233, 6234, 6235, 6236, 6237, 6238, 6239, 6240, 6241, 6242, 6243, 6244, 6245, 6246, 6254, 6255, 6256, 6257, 6258, 6259, 6267, 6268, 6269, 6270, 6271, 6272, 6273, 6274, 6275, 6276, 6277, 6278, 6279, 6280, 6281, 6282, 6283 }; - } - -} diff --git a/Server/src/main/java/core/game/content/activity/package-info.java b/Server/src/main/java/core/game/content/activity/package-info.java deleted file mode 100644 index 095ee709c..000000000 --- a/Server/src/main/java/core/game/content/activity/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Holds all the plugin classes. - * - * @author Emperor - */ -package core.game.content.activity; \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/PCPortalNPC.java b/Server/src/main/java/core/game/content/activity/pestcontrol/PCPortalNPC.java deleted file mode 100644 index 2ab59697a..000000000 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/PCPortalNPC.java +++ /dev/null @@ -1,332 +0,0 @@ -package core.game.content.activity.pestcontrol; - -import java.util.Random; - -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.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.RegionPlane; -import core.game.world.update.flag.context.Animation; -import core.tools.RandomFunction; -import core.game.content.activity.pestcontrol.monsters.PCRavagerNPC; -import core.game.content.activity.pestcontrol.monsters.PCSpinnerNPC; - -/** - * Handles the portal NPC. - * @author Emperor - */ -public final class PCPortalNPC extends AbstractNPC { - - /** - * The splatter NPCs. - */ - private static final int[][] SPLATTERS = { { 3727, 3728, 3729 }, // Novice - { 3728, 3729, 3730 }, // Intermediate - { 3729, 3730, 3731 }, // Veteran - }; - - /** - * The shifter NPCs. - */ - private static final int[][] SHIFTERS = { { 3732, 3733, 3734, 3735, 3736, 3737 }, // Novice - { 3734, 3735, 3736, 3737, 3738, 3739 }, // Intermediate - { 3736, 3737, 3738, 3739, 3740, 3741 }, // Veteran - }; - - /** - * The ravager NPCs. - */ - private static final int[][] RAVAGERS = { { 3742, 3743, 3744 }, // Novice - { 3743, 3744, 3745 }, // Intermediate - { 3744, 3745, 3746 }, // Veteran - }; - - /** - * The spinner NPCs. - */ - private static final int[][] SPINNERS = { { 3747, 3748, 3749 }, // Novice - { 3748, 3749, 3750 }, // Intermediate - { 3749, 3750, 3751 }, // Veteran - }; - - /** - * The torcher NPCs. - */ - private static final int[][] TORCHERS = { { 3752, 3753, 3754, 3755, 3756, 3757 }, // Novice - { 3754, 3755, 3756, 3757, 3758, 3759 }, // Intermediate - { 3756, 3757, 3758, 3759, 3760, 3761 }, // Veteran - }; - - /** - * The defiler NPCs. - */ - private static final int[][] DEFILERS = { { 3762, 3763, 3764, 3765, 3766, 3767 }, // Novice - { 3764, 3765, 3766, 3767, 3768, 3769 }, // Intermediate - { 3766, 3767, 3768, 3769, 3770, 3771 }, // Veteran - }; - - /** - * The brawler NPCs. - */ - private static final int[][] BRAWLERS = { { 3772, 3773, 3774 }, // Novice - { 3773, 3774, 3775 }, // Intermediate - { 3774, 3775, 3776 }, // Veteran - }; - - /** - * If the lifepoints should be updated. - */ - private boolean updateLifepoints = true; - - /** - * The pest control session. - */ - private PestControlSession session; - - /** - * The spinners. - */ - private PCSpinnerNPC[] spinners = new PCSpinnerNPC[3]; - - /** - * The brawlers. - */ - private NPC[] brawlers = new NPC[2]; - - - /** - * The portal ids? - */ - final static Integer[] portalIds = new Integer[] { 6142, 6143, 6144, 6145, 6146, 6147, 6148, 6149, - - 6150, 6151, 6152, 6153, 6154, 6155, 6156, 6157, - - 7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558 }; - - /** - * Constructs a new {@code PCPortalNPC} {@code Object}. - */ - public PCPortalNPC() { - this(6142, null); - } - - /** - * Constructs a new {@code PCPortalNPC} {@code Object}. - * @param id The NPC id. - * @param l The location. - */ - public PCPortalNPC(int id, Location l) { - super(id, l); - } - - @Override - public void init() { - super.setWalks(false); - super.setRespawn(false); - super.getProperties().setRetaliating(false); - super.init(); - getProperties().setAttackAnimation(Animation.create(-1)); - getProperties().setDefenceAnimation(Animation.create(-1)); - getProperties().setDeathAnimation(Animation.create(-1)); - session = getExtension(PestControlSession.class); - } - - @Override - public void tick() { - super.tick(); - if (session != null) { - RegionPlane plane = getViewport().getCurrentPlane(); - if (plane != null && session.getTicks() % 35 - plane.getPlayers().size() == 0 && plane.getNpcs().size() < 100) { - spawnNPCs(); - } - if (updateLifepoints && session.getTicks() % 10 == 0) { - String color = getSkills().getLifepoints() > 50 ? "" : ""; - session.sendString(color + getSkills().getLifepoints(), 13 + getPortalIndex()); - updateLifepoints = false; - } - } - } - - @Override - public boolean shouldPreventStacking(Entity mover) { - return true; - } - - @Override - public void onImpact(final Entity entity, BattleState state) { - updateLifepoints = true; - super.onImpact(entity, state); - if (session != null && state != null && entity instanceof Player) { - int total = 0; - if (state.getEstimatedHit() > 0) { - total += state.getEstimatedHit(); - } - if (state.getSecondaryHit() > 0) { - total += state.getSecondaryHit(); - } - session.addZealGained((Player) entity, total); - } - } - - /** - * Spawns the NPCs. - */ - public void spawnNPCs() { - Direction dir = Direction.getLogicalDirection(getLocation(), session.getSquire().getLocation()); - int index = getDifficultyIndex(); - Random r = RandomFunction.RANDOM; - int amount = index + 1; - if (getViewport().getCurrentPlane() != null) { - amount += getViewport().getCurrentPlane().getPlayers().size() / 10; - } - if (getDifficultyIndex() == 0) { - amount += ((getDifficultyIndex() + 1) * 3) / 2; - } - for (int i = 0; i < amount; i++) { - int[] ids = SHIFTERS[index]; - switch (r.nextInt(7)) { - case 0: - ids = SPLATTERS[index]; - break; - case 1: - ids = SHIFTERS[index]; - break; - case 2: - ids = RAVAGERS[index]; - break; - case 3: - boolean spawn = false; - for (PCSpinnerNPC npc : spinners) { - if (npc == null || !npc.isActive()) { - spawn = true; - break; - } - } - if (spawn) { - ids = SPINNERS[index]; - } else { - ids = TORCHERS[index]; - } - break; - case 4: - ids = TORCHERS[index]; - break; - case 5: - ids = DEFILERS[index]; - break; - case 6: - spawn = false; - for (NPC npc : brawlers) { - if (npc == null || !npc.isActive()) { - spawn = true; - break; - } - } - if (spawn) { - ids = BRAWLERS[index]; - } else { - ids = DEFILERS[index]; - } - break; - } - int x = dir.getStepX() * 3; - int y = dir.getStepY() * 3; - if (x == 0) { - x = i; - } else { - y = i; - } - NPC n = session.addNPC(NPC.create(ids[r.nextInt(ids.length)], getLocation().transform(x, y, 0))); - if (ids == RAVAGERS[index]) { - ((PCRavagerNPC) n).setPortalIndex(getPortalIndex()); - } else if (ids == SPINNERS[index]) { - for (int j = 0; j < spinners.length; j++) { - if (spinners[j] == null || !spinners[j].isActive()) { - spinners[j] = ((PCSpinnerNPC) n).setPortalIndex(getPortalIndex()); - break; - } - } - } - n.setWalks(true); - n.setRespawn(false); - } - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new PCPortalNPC(id, location); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (session != null) { - int value = 0; - boolean endSession = true; - for (int i = 0; i < 4; i++) { - if (!session.getPortals()[i].isActive()) { - value |= 1 << i; - } else { - endSession = false; - } - } - if (value > 0) { - session.sendConfig(value << 28); - if (endSession) { - session.getActivity().end(session, true); - return; - } - } - for (PCSpinnerNPC npc : spinners) { - if (npc != null && npc.isActive()) { - npc.explode(); - } - } - session.sendString("0", 13 + getPortalIndex()); - updateLifepoints = false; - session.getSquire().getSkills().heal(50); - session.getSquire().onImpact(this, null); - } - } - - @Override - public int[] getIds() { - return new int[] { 6142, 6143, 6144, 6145, 6146, 6147, 6148, 6149, - - 6150, 6151, 6152, 6153, 6154, 6155, 6156, 6157, - - 7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558 }; - } - - /** - * The difficulty index. - * @return The index. - */ - public int getDifficultyIndex() { - if (getId() > 7550) { - return 2; - } - if (getId() > 6149) { - return 1; - } - return 0; - } - - /** - * The portal index. - * @return The index. - */ - public int getPortalIndex() { - if (getId() > 7550) { - return (getId() - 7551) % 4; - } - if (getId() > 6149) { - return (getId() - 6150) % 4; - } - return (getId() - 6142) % 4; - } -} diff --git a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCBrawlerNPC.java b/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCBrawlerNPC.java deleted file mode 100644 index 830e3cca7..000000000 --- a/Server/src/main/java/core/game/content/activity/pestcontrol/monsters/PCBrawlerNPC.java +++ /dev/null @@ -1,91 +0,0 @@ -package core.game.content.activity.pestcontrol.monsters; - -import core.game.content.activity.pestcontrol.PestControlSession; -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.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.RegionManager; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.tools.RandomFunction; -import rs09.game.node.entity.combat.CombatPulse; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; -import rs09.game.world.GameWorld; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Handles the pest control brawler NPCs. - * @author Emperor - */ -public final class PCBrawlerNPC extends AbstractNPC { - /** - * The pest control session. - */ - private PestControlSession session; - - /** - * Constructs a new {@code PCBrawlerNPC} {@code Object}. - */ - public PCBrawlerNPC() { - super(3772, null); - } - - /** - * Constructs a new {@code PCBrawlerNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - public PCBrawlerNPC(int id, Location location) { - super(id, location); - } - - @Override - public void init() { - super.setAggressive(true); - super.init(); - super.getDefinition().setCombatDistance(1); - super.walkRadius = 64; - getProperties().getCombatPulse().setStyle(CombatStyle.MELEE); - session = getExtension(PestControlSession.class); - } - - @Override - public boolean shouldPreventStacking(Entity mover) { - return true; - } - - @Override - public void onImpact(final Entity entity, BattleState state) { - super.onImpact(entity, state); - if (session != null && state != null && entity instanceof Player) { - int total = 0; - if (state.getEstimatedHit() > 0) { - total += state.getEstimatedHit(); - } - if (state.getSecondaryHit() > 0) { - total += state.getSecondaryHit(); - } - session.addZealGained((Player) entity, total); - } - } - - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new PCBrawlerNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] { 3772, 3773, 3774, 3775, 3776 }; - } -} diff --git a/Server/src/main/java/core/game/content/activity/puropuro/ImpDefenderNPC.java b/Server/src/main/java/core/game/content/activity/puropuro/ImpDefenderNPC.java deleted file mode 100644 index 26288bb34..000000000 --- a/Server/src/main/java/core/game/content/activity/puropuro/ImpDefenderNPC.java +++ /dev/null @@ -1,112 +0,0 @@ -package core.game.content.activity.puropuro; - -import java.util.List; -import java.util.Random; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.hunter.bnet.BNetTypes; -import core.game.node.entity.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 rs09.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/java/core/game/content/activity/puropuro/PuroPuroPlugin.java b/Server/src/main/java/core/game/content/activity/puropuro/PuroPuroPlugin.java deleted file mode 100644 index fede65a3f..000000000 --- a/Server/src/main/java/core/game/content/activity/puropuro/PuroPuroPlugin.java +++ /dev/null @@ -1,495 +0,0 @@ -package core.game.content.activity.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.item.Item; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import rs09.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.zone.MapZone; -import core.game.world.map.zone.ZoneBuilder; -import core.game.world.map.zone.ZoneType; -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 rs09.plugin.PluginManager; -import core.tools.RandomFunction; - -/** - * 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 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()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PULSE.stop(); - ZoneBuilder.configure(this); - PluginManager.definePlugin(new FairyAerykaDialogue()); - PluginManager.definePlugin(new WanderingImplingDialogue()); - PluginManager.definePlugin(new ElnockInquisitorDialogue()); - PluginManager.definePlugin(new PuroOptionHandler()); - PluginManager.definePlugin(new ImpDefenderNPC()); - return this; - } - - @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 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); - } - - /** - * 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; - } - }); - } - - /** - * 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); - } - - @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))); - } - - /** - * 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 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; - } - - /** - * 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; - } - } - - /** - * 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."); - } - } - - /** - * 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; - } - - /** - * The percent to set. - * @param item the item. - * @param percent the percent. - */ - private void setPercent(Item item, int percent) { - item.setCharge(item.getCharge() - percent); - } - - /** - * 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 { - - /** - * The locations of the wheat. - */ - private final Location[] locations; - - /** - * The scenerys. - */ - private Scenery[] objects = new Scenery[2]; - - /** - * The rotation. - */ - private int rot; - - /** - * The time until the next whilt. - */ - private int nextWhilt; - - /** - * The busy ticks. - */ - private int busyTicks; - - /** - * If the wheat is removed. - */ - private boolean removed; - - /** - * Constructs a new {@code WheatSet} {@code Object}. - * @param locations the locations. - */ - public WheatSet(int rot, Location... locations) { - this.rot = rot; - this.locations = locations; - } - - /** - * 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(); - } - - /** - * 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(); - } - - /** - * Sets the object spawns. - */ - public void setObjects() { - for (int i = 0; i < locations.length; i++) { - objects[i] = RegionManager.getObject(locations[i]); - } - } - - /** - * Sets the next whilt. - */ - public void setNextWhilt() { - this.nextWhilt = GameWorld.getTicks() + RandomFunction.random(40, 300); - } - - /** - * 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/java/core/game/content/activity/pyramidplunder/GuardMummyDialogue.java b/Server/src/main/java/core/game/content/activity/pyramidplunder/GuardMummyDialogue.java deleted file mode 100644 index 2fb5a20c7..000000000 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/GuardMummyDialogue.java +++ /dev/null @@ -1,133 +0,0 @@ -package core.game.content.activity.pyramidplunder; - -import core.game.content.activity.ActivityManager; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; - -/** - * Handles Guardian mummy dialogue. - * @author Originally Emperor, completely redone by Ceikry. - */ -public final class GuardMummyDialogue extends DialoguePlugin { - - /** - * If the player successfully caught the evil twin. - * Will remain unused until the quest is implemented. (if ever) - */ - //private int type; - - - /** - * Constructs a new {@code GuardMummyDialogue} {@code Object}. - */ - public GuardMummyDialogue() {super();} - public GuardMummyDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GuardMummyDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if(args.length > 1){ - switch((int) args[1]){ - case 0: - npc("You! How did you get into this place?"); - stage = 50; - break; - case 1: - player("I know what I'm doing - let's get on with it."); - stage = 10; - break; - } - } else { - npc("Errr"); - } - /*type = args.length == 1 ? 0 : (Integer) args[1]; - if (type == 1) { - player("I know what I'm doing - let's get on with it."); - } else { - npc("You! How did you get in?"); - }*/ - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch(stage){ - case 10: - npc("Fine, I'll take you to the first room now..."); - stage = 60; - break; - case 50: - player("I could ask you the same thing."); - stage = 51; - break; - case 51: - npc("Nevermind that. I suppose since you're here I can tell", "you how to play."); - stage = 52; - break; - case 52: - npc("In each room there are urns, a chest, and a sarcophagus."); - stage = 53; - break; - case 53: - npc("The urns contain artifacts based on the room that you are","currently in. The chest contains valuable artifacts but is","sometimes trapped. The sarcophagus has one of my","brothers in it. And lots of artifacts."); - stage = 54; - break; - case 54: - player("Anything else I should know?"); - stage = 55; - break; - case 55: - npc("Yes, each room requires more thieving knowledge than the"," last. And be careful not to die."); - stage = 56; - break; - case 56: - player("Oh, yes, dying. Not good. Well I suppose that covers it?"); - stage = 57; - break; - case 57: - npc("Yep."); - stage = 58; - break; - case 58: - end(); - break; - case 60: - end(); - ActivityManager.start(player, "Pyramid plunder", false); - break; - - } - /*switch (stage++) { - case 0: - if (type == 1) { - npc("Fine, I'll take you to the first room now..."); - } else { - System.out.println("TODO:2"); - } - return true; - case 1: - if (type == 1) { - end(); - ActivityManager.start(player, "Pyramid plunder", false); - } else { - System.out.println("TODO:3"); - } - return true; - }*/ - return false; - } - - @Override - public int[] getIds() { - return new int[] { 4476, 4477 }; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderObject.java b/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderObject.java deleted file mode 100644 index 49c882bc9..000000000 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderObject.java +++ /dev/null @@ -1,41 +0,0 @@ -package core.game.content.activity.pyramidplunder; - -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; - -/** - * Object wrapper for pyramid plunder nodes - * @author ceik - */ - -public class PlunderObject extends Scenery { - private transient Player player; - private static int thisId; - public static int snakeId, openId; - - - public PlunderObject(Scenery obj){ - super(obj.getId(),obj.getLocation(),obj.getRotation(),obj.getDirection()); - this.thisId = obj.getId(); - switch(thisId){ - case 16501: - this.snakeId = 16509; - this.openId = 16505; - break; - case 16502: - this.snakeId = 16510; - this.openId = 16506; - break; - case 16503: - this.snakeId = 16511; - this.openId = 16507; - break; - case 16495: - this.openId = 16496; - break; - case 16473: - this.openId = 16474; - break; - } - } -} diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderObjectManager.java b/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderObjectManager.java deleted file mode 100644 index 011c71619..000000000 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderObjectManager.java +++ /dev/null @@ -1,29 +0,0 @@ -package core.game.content.activity.pyramidplunder; - -import core.game.node.entity.player.Player; -import core.game.world.map.Location; - -import java.util.HashMap; - -/** - * Manages what objects a specific player has interacted with in pyramid plunder. - * @author ceik - */ - -public class PlunderObjectManager{ - public static HashMap openedMap = new HashMap<>(); - public static HashMap charmedMap = new HashMap<>(); - - int originalIndex; - public boolean resetObjectsFor(Player player){ - //Completely clear the mapping and reset objects - openedMap.clear(); - charmedMap.clear(); - return true; - } - - public void registerOpened(PlunderObject object){ - openedMap.putIfAbsent(object.getLocation(),true); - } - public void registerCharmed(PlunderObject object) { charmedMap.putIfAbsent(object.getLocation(),true);} -} diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderZones.java b/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderZones.java deleted file mode 100644 index 6a2e2de5b..000000000 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PlunderZones.java +++ /dev/null @@ -1,367 +0,0 @@ -package core.game.content.activity.pyramidplunder; - -import core.game.content.global.action.ClimbActionHandler; -import core.game.interaction.Option; -import core.game.node.Node; -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.skill.Skills; -import core.game.node.item.Item; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; -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.Initializable; -import core.plugin.Plugin; -import core.tools.RandomFunction; -import rs09.game.Varp; -import rs09.game.content.activity.pyramidplunder.PlunderSession; - -/** - * Defines the zones for the pyramid plunder rooms and their interactions - * @author ceik - */ - -@Initializable -public class PlunderZones implements Plugin { - private final PlunderZone[] ROOMS = { - new PlunderZone("plunder:room1", 1,1923, 4464, 1932, 4474), - new PlunderZone("plunder:room2", 2,1925, 4449, 1941, 4458), - new PlunderZone("plunder:room3", 3,1941, 4421, 1954, 4432), - new PlunderZone("plunder:room4", 4,1949, 4464, 1959, 4477), - new PlunderZone("plunder:room5", 5,1968, 4420, 1978, 4436), - new PlunderZone("plunder:room6", 6,1969, 4452, 1980, 4473), - new PlunderZone("plunder:room7", 7,1923, 4424, 1931, 4439), - new PlunderZone("plunder:room8", 8, 1950, 4442, 1969, 4455) - }; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for(PlunderZone room : ROOMS){ - ZoneBuilder.configure(room); - } - return this; - } - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - public class PlunderZone extends MapZone { - PyramidPlunderRoom room; - Location entrance; - private final Animation[] animations = new Animation[] { new Animation(2247), new Animation(2248), new Animation(1113), new Animation(2244) }; - final int swx, swy, nex, ney; - final String name; - final int roomnum; - - public PlunderZone(final String name, final int roomnum, final int swx, final int swy, final int nex, final int ney) { - super(name, true); - this.name = name; - this.swx = swx; - this.swy = swy; - this.nex = nex; - this.ney = ney; - this.roomnum = roomnum; - switch(roomnum){ - case 1: - room = PyramidPlunderRoom.ROOM_1; - break; - case 2: - room = PyramidPlunderRoom.ROOM_2; - break; - case 3: - room = PyramidPlunderRoom.ROOM_3; - break; - case 4: - room = PyramidPlunderRoom.ROOM_4; - break; - case 5: - room = PyramidPlunderRoom.ROOM_5; - break; - case 6: - room = PyramidPlunderRoom.ROOM_6; - break; - case 7: - room = PyramidPlunderRoom.ROOM_7; - break; - case 8: - room = PyramidPlunderRoom.ROOM_8; - } - } - - @Override - public void configure() { - ZoneBorders borders = new ZoneBorders(swx, swy, nex, ney,0); - register(borders); - } - - @Override - public boolean enter(Entity e){ - if(e instanceof Player && ((Player) e).getLocation().getZ() == 0) { - e.asPlayer().getPacketDispatch().sendMessage("Room: " + (roomnum) + " Level required: " + (21 + ((roomnum - 1) * 10))); - e.asPlayer().getPlunderObjectManager().resetObjectsFor(e.asPlayer()); - PlunderSession session = e.asPlayer().getAttribute("plunder-session",null); - if(session != null){ - session.resetVars(); - } - e.asPlayer().addExtension(LogoutTask.class, new LocationLogoutTask(12, Location.create(3288, 2801, 0))); - updateRoomVarp(e.asPlayer()); - } - return true; - } - - public void updateRoomVarp(Player player){ - Varp varp = player.varpManager.get(822); - varp.setVarbit(0,room.reqLevel); - varp.setVarbit(9,roomnum); - player.getPacketDispatch().sendVarp(varp); - } - - public boolean checkRequirements(Player player, PyramidPlunderRoom room){ - int requiredLevel = room.reqLevel; - int playerLevel = player.getSkills().getLevel(Skills.THIEVING); - return playerLevel >= requiredLevel; - } - - public void rollSceptre(Player player){ - if(RandomFunction.random(1000) == 451){ - if(!player.getInventory().isFull()) { - player.getInventory().add(new Item(9044)); - player.getPacketDispatch().sendMessage("You find a strange object."); - } else { - player.getBank().add(new Item(9044)); - player.getPacketDispatch().sendMessage("You sense that something has appeared in your bank."); - } - } - } - - public final boolean success(final Player player, final int skill) { - double level = player.getSkills().getLevel(skill); - double successChance = Math.ceil((level * 50 - room.reqLevel) / room.reqLevel / 3 * 4); - int roll = RandomFunction.random(99); - if (successChance >= roll) { - return true; - } - return false; - } - - public void reward(Player player, int objid){ - Item[][] ARTIFACTS = { {new Item(9032),new Item(9036), new Item(9026)}, {new Item(9042), new Item(9030), new Item(9038)}, {new Item(9040), new Item(9028), new Item(9034)} }; - rollSceptre(player); - switch(objid){ - case 16517: // Spear - player.getSkills().addExperience(Skills.THIEVING, (30 + (roomnum * 20)), true); - break; - case 16503: - case 16502: - case 16501: // Urns - player.getSkills().addExperience(Skills.THIEVING, (25 + (roomnum * 20)), true); - player.getInventory().add(ARTIFACTS[((int)Math.floor((double) roomnum / 3))][RandomFunction.random(3)]); - break; - case 16473: // Chest - player.getInventory().add(ARTIFACTS[RandomFunction.random(1, 3)][RandomFunction.random(3)]); - player.getPacketDispatch().sendMessage("And you find an artifact!"); - player.getSkills().addExperience(Skills.THIEVING, (40 + (roomnum * 20))); - break; - case 16495: // Sarcophagus - player.getPacketDispatch().sendMessage("You find some loot inside."); - player.getInventory().add(ARTIFACTS[RandomFunction.random(0,3)][RandomFunction.random(3)]); - player.getSkills().addExperience(Skills.STRENGTH,50 + (roomnum * 20)); - break; - - } - } - - - @Override - public boolean interact(Entity e, Node target, Option option) { - final Player player = e instanceof Player ? e.asPlayer() : null; - String optionName = option.getName().toLowerCase(); - if(target instanceof NPC){ - if(target.getName().contains("Guardian")) { - if (optionName.equals("talk-to")) { - player.getDialogueInterpreter().open(target.getId(), target.asNpc(), 0); - } else { - player.getDialogueInterpreter().open(target.getId(), target.asNpc(), 1); - } - } - if(optionName.equals("attack")){ - player.getProperties().getCombatPulse().attack(target); - } - return true; - } - PlunderObject object = target instanceof NPC ? null : new PlunderObject(target.asScenery()); //PlunderObject(target.getId(),target.getLocation()); - PlunderObjectManager manager = player.getPlunderObjectManager(); - if(manager == null) return super.interact(e, target, option); - boolean alreadyOpened = manager.openedMap.getOrDefault(object.getLocation(),false); - boolean charmed = manager.charmedMap.getOrDefault(object.getLocation(),false); - boolean success = success(player, Skills.THIEVING); - PlunderSession session = player.getAttribute("plunder-session",null); - if(session == null){ - return false; - } - switch (object.getId()) { - case 16517: //Spear trap - if(!checkRequirements(player,room)){ - player.getPacketDispatch().sendMessage("You need to be at least level " + room.reqLevel + " thieving."); - break; - } - player.getLocks().lockInteractions(2); - player.animate(animations[success ? 1 : 0]); - if (success) { - player.getPacketDispatch().sendMessage("You successfully pass the spears."); - int moveX = player.getLocation().getX(); - int moveY = player.getLocation().getY(); - Location moveto = new Location(moveX + room.spearX, moveY + room.spearY); - player.getProperties().setTeleportLocation(moveto); - //player.moveStep(); - } else { - player.getPacketDispatch().sendMessage("You fail to pass the spears."); - } - break; - case 16503: - case 16502: - case 16501: // Urns - if (optionName.equals("search")) { - if (!checkRequirements(player,room)){ - player.getPacketDispatch().sendMessage("You need to be at least level " + room.reqLevel + " thieving."); - break; - } - if (alreadyOpened){ - player.getPacketDispatch().sendMessage("You've already looted this."); - break; - } - - player.animate(animations[success ? 1 : 0]); - player.getLocks().lockInteractions(2); - - if (!alreadyOpened && (success || charmed)) { - player.getPacketDispatch().sendMessage("You successfully search the urn..."); - SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(object.openId), 5); - manager.registerOpened(object); - reward(player,object.getId()); - } else { - player.getPacketDispatch().sendMessage("You failed and got bit by a snake."); - player.getImpactHandler().manualHit(player,RandomFunction.random(2,8), ImpactHandler.HitsplatType.NORMAL); - } - } else if(optionName.equals("check for snakes")){ - if(charmed){ - player.getPacketDispatch().sendMessage("You already checked for snakes."); - } else { - player.getPacketDispatch().sendMessage("You check the urn for snakes..."); - SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(object.snakeId), 5); - } - } - break; - case 16509: - case 16510: - case 16511: // Snake urns - if(optionName.equals("search") && player.getInventory().containsItem(new Item(4605))){ - player.animate(new Animation(1877)); - player.getPacketDispatch().sendMessage("You charm the snake into docility."); - manager.registerCharmed(object); - } else { - player.getImpactHandler().manualHit(player, RandomFunction.random(2, 8), ImpactHandler.HitsplatType.NORMAL); - player.getPacketDispatch().sendMessage("The snake bites you."); - } - break; - case 16473: // Chest - if(optionName.equals("search")) { - boolean willSpawnSwarm = (RandomFunction.random(1,20) == 10); - if(!checkRequirements(player,room)){ - player.getPacketDispatch().sendMessage("You need at least level " + room.reqLevel + " thieving to loot this."); - break; - } - if (alreadyOpened){ - player.getPacketDispatch().sendMessage("You've already looted this."); - break; - } - player.getPacketDispatch().sendMessage("You search the chest..."); - player.animate(animations[1]); - player.getLocks().lockInteractions(2); - if(willSpawnSwarm) { - player.getPacketDispatch().sendMessage("A swarm flies out!"); - PyramidPlunderSwarmNPC swarm = new PyramidPlunderSwarmNPC(2001,player.getLocation(),player); - swarm.setRespawn(false); - swarm.setAggressive(true); - swarm.init(); - } else { - reward(player,object.getId()); - } - session.setChestOpen(true); - session.updateOverlay(); - manager.registerOpened(object); - //ObjectBuilder.replace(target.asObject(), target.asObject().transform(object.openId), 5); - } - break; - case 16495: //Sarcophagus - if(optionName.equals("open")) { - if (!checkRequirements(player,room)){ - player.getPacketDispatch().sendMessage("You need to be at least level " + room.reqLevel + " thieving."); - break; - } - if (alreadyOpened){ - player.getPacketDispatch().sendMessage("You've already looted this."); - break; - } - boolean willSpawnMummy = (RandomFunction.random(1,5) == 3); - player.animate(animations[1]); - player.getPacketDispatch().sendMessage("You open the sarcophagus and...."); - if(willSpawnMummy) { - player.getPacketDispatch().sendMessage("A mummy crawls out!"); - PyramidPlunderMummyNPC mummy = new PyramidPlunderMummyNPC(1958, player.getLocation(),player); - mummy.setRespawn(false); - mummy.setAggressive(true); - mummy.init(); - } else { - reward(player,object.getId()); - } - session.setCoffinOpen(true); - session.updateOverlay(); - manager.registerOpened(object); - //ObjectBuilder.replace(target.asObject(), target.asObject().transform(object.openId), 5); - } - break; - case 16475: //doors - if(optionName.equals("pick-lock") && roomnum < 8) { - if (!checkRequirements(player,room)){ - player.getPacketDispatch().sendMessage("You need to be at least level " + room.reqLevel + " thieving."); - break; - } - player.animate(animations[1]); - player.getLocks().lockInteractions(2); - boolean doesOpen = success(player, Skills.THIEVING); - if (doesOpen) { - PyramidPlunderRoom nextRoom = PyramidPlunderRoom.forRoomNum(roomnum + 1); - player.getPacketDispatch().sendMessage("The door opens!"); - int index = room.doorLocations.indexOf(object.getLocation()); - if(session.getCorrectDoorIndex() == index){ - player.getProperties().setTeleportLocation(nextRoom.entrance); - } else { - session.setDoorOpen(index); - session.updateOverlay(); - } - } else { - player.getPacketDispatch().sendMessage("You fail to unlock the door."); - } - } else if(roomnum == 8) { - ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, Location.create(3288, 2801, 0)); - } - break; - case 16476: - player.getDialogueInterpreter().sendDialogue("This door doesn't seem to lead","anywhere."); - break; - default: - return super.interact(e, target, option); - } - return true; - } - } -} diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidOptionHandler.java b/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidOptionHandler.java deleted file mode 100644 index 28788cf5b..000000000 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidOptionHandler.java +++ /dev/null @@ -1,108 +0,0 @@ -package core.game.content.activity.pyramidplunder; - -import api.ContentAPI; -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -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.system.task.Pulse; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; -import core.tools.RandomFunction; - -/** - * Handles the pyramid plunder options. - * @author Emperor - */ -public final class PyramidOptionHandler extends OptionHandler { - - /** - * The guardian room location. - */ - private static final Location GUARDIAN_ROOM = Location.create(1968, 4420, 2); - - /** - * The empty room location. - */ - private static final Location EMPTY_ROOM = Location.create(1934, 4450, 2); - - /** - * The current entrance. - */ - private static int currentEntrance; - - /** - * The last entrance switch made. - */ - private static long lastEntranceSwitch; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(16484).getHandlers().put("option:search", this); - SceneryDefinition.forId(16485).getHandlers().put("option:search", this); - SceneryDefinition.forId(16487).getHandlers().put("option:search", this); - SceneryDefinition.forId(16488).getHandlers().put("option:search", this); - SceneryDefinition.forId(16490).getHandlers().put("option:search", this); - SceneryDefinition.forId(16491).getHandlers().put("option:search", this); - SceneryDefinition.forId(16493).getHandlers().put("option:search", this); - SceneryDefinition.forId(16494).getHandlers().put("option:enter", this); - SceneryDefinition.forId(16458).getHandlers().put("option:leave tomb", this); - SceneryDefinition.forId(16459).getHandlers().put("option:leave tomb", this); - NPCDefinition.forId(4476).getHandlers().put("option:start-minigame", this); - NPCDefinition.forId(4476).getHandlers().put("option:talk-to",this); - return null; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (node instanceof Scenery) { - Location destination = EMPTY_ROOM; - Scenery object = (Scenery) node; - boolean willBePushed = (RandomFunction.random(10) > 3); - if (object.getId() == 16458 || object.getId() == 16459) { - ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, Location.create(3288, 2801, 0)); - player.getPlunderObjectManager().resetObjectsFor(player); - return true; - } - if (System.currentTimeMillis() - lastEntranceSwitch > 15 * 60 * 1000) { - currentEntrance = RandomFunction.random(4); - lastEntranceSwitch = System.currentTimeMillis(); - } - int entrance = (object.getId() - 16483) / 3; - int value = 0; - if (entrance == currentEntrance) { - destination = GUARDIAN_ROOM; - value = 4; - } - player.getConfigManager().set(704, value); - player.getPacketDispatch().sendMessage("You use your thieving skills to search the stone panel."); - if(entrance == currentEntrance && willBePushed){ - player.lock(); - player.animate(new Animation(7299)); - ContentAPI.submitWorldPulse(new Pulse(4, player){ - @Override - public boolean pulse() { - player.unlock(); - return true; - } - }); - } else { - ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, destination, "You find a door! You open it."); - } - //ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, destination, "You find a door! You open it."); - } else if (node instanceof NPC) { - if (option.equals("talk-to")) { - player.getDialogueInterpreter().open(node.getId(), node, 0); - } else { - player.getDialogueInterpreter().open(node.getId(), node, 1); - } - } - return true; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderActivity.java b/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderActivity.java deleted file mode 100644 index c190933f5..000000000 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderActivity.java +++ /dev/null @@ -1,94 +0,0 @@ -package core.game.content.activity.pyramidplunder; - -import core.game.content.activity.ActivityPlugin; -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.plugin.Initializable; -import rs09.game.content.activity.pyramidplunder.PlunderSession; -import rs09.plugin.PluginManager; - -/** - * Handles the Pyramid plunder activity. - * @author Emperor - */ -@Initializable -public final class PyramidPlunderActivity extends ActivityPlugin { - - /** - * The room locations. - */ - public static Location[] ROOM_LOCATIONS = { Location.create(1927, 4477, 0) }; - - /** - * The mummy NPC. - */ - private static NPC mummy; - - /** - * Constructs a new {@code PyramidPlunderActivity} {@code Object}. - */ - public PyramidPlunderActivity() { - super("Pyramid plunder", false, true, false); - } - - @Override - public boolean start(Player player, boolean login, Object... args) { - enterRoom(player, 0); - return true; - } - - @Override - public boolean enter(Entity e) { - return super.enter(e); - } - - @Override - public boolean leave(Entity e, boolean logout) { - if (e instanceof Player) { - ((Player) e).getInterfaceManager().closeOverlay(); - PlunderSession session = (PlunderSession) e.asPlayer().getAttribute("plunder-session",null); - if(session != null){ - session.setActive(false); - } - ((Player) e).removeAttribute("plunder-session"); - } - return super.leave(e, logout); - } - - @Override - public ActivityPlugin newInstance(Player p) throws Throwable { - return this; - } - - @Override - public void register() { - PluginManager.definePlugin(new GuardMummyDialogue()); - PluginManager.definePlugin(new PyramidOptionHandler()); - mummy = NPC.create(4476, Location.create(1968, 4427, 2)); - mummy.init(); - registerRegion(7749); - } - - @Override - public Location getSpawnLocation() { - return null; - } - - @Override - public void configure() { - } - - /** - * Enters a room. - * @param player The player. - * @param index The index. - */ - public void enterRoom(Player player, int index) { - player.lock(1); - player.getProperties().setTeleportLocation(ROOM_LOCATIONS[index]); - new PlunderSession(player).init(); - } - -} diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderOptions.java b/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderOptions.java deleted file mode 100644 index 4ab02ab1f..000000000 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderOptions.java +++ /dev/null @@ -1,341 +0,0 @@ -/* -package core.game.content.activity.pyramidplunder; - -import org.crandor.cache.def.impl.ObjectDefinition; -import org.crandor.game.content.global.action.ClimbActionHandler; -import core.game.node.entity.skill.Skills; -import org.crandor.game.interaction.OptionHandler; -import org.crandor.game.node.Node; -import org.crandor.game.node.entity.combat.ImpactHandler; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.item.Item; -import org.crandor.game.node.object.ObjectBuilder; -import org.crandor.game.world.GameWorld; -import org.crandor.game.world.map.Location; -import org.crandor.game.world.map.zone.ZoneMonitor; -import org.crandor.game.world.update.flag.context.Animation; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.plugin.Plugin; -import org.crandor.tools.RandomFunction; - -*/ -/** - * Handle pyramid plunder object interactions - * @author ceik - *//* - -*/ -/** - * PyramidPlunderOptions defines interactions for pyramid plunder - * Copyright (C) 2020 2009scape, et. al - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the modified GNU General Public License - * as published by the Free Software Foundation and included in this repository; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - *//* - - -@InitializablePlugin -public final class PyramidPlunderOptions extends OptionHandler { - Item[][] ARTIFACTS = { {new Item(9032),new Item(9036), new Item(9026)}, {new Item(9042), new Item(9030), new Item(9038)}, {new Item(9040), new Item(9028), new Item(9034)} }; - private static final Animation[] animations = new Animation[] { new Animation(2247), new Animation(2248), new Animation(1113), new Animation(2244) }; - int reqLevel; - //Player player; - @Override - public Plugin newInstance(Object arg) throws Throwable { - ObjectDefinition.forId(16517).getConfigurations().put("option:pass",this); - ObjectDefinition.forId(16503).getConfigurations().put("option:search",this); - ObjectDefinition.forId(16502).getConfigurations().put("option:search",this); - ObjectDefinition.forId(16501).getConfigurations().put("option:search",this); - ObjectDefinition.forId(16501).getConfigurations().put("option:check for snakes",this); - ObjectDefinition.forId(16502).getConfigurations().put("option:check for snakes",this); - ObjectDefinition.forId(16503).getConfigurations().put("option:check for snakes",this); - ObjectDefinition.forId(16473).getConfigurations().put("option:search",this); - ObjectDefinition.forId(16495).getConfigurations().put("option:open",this); - ObjectDefinition.forId(16475).getConfigurations().put("option:pick-lock",this); - ObjectDefinition.forId(16509).getConfigurations().put("option:search",this); - ObjectDefinition.forId(16510).getConfigurations().put("option:search",this); - ObjectDefinition.forId(16511).getConfigurations().put("option:search",this); - - - return this; - } - public void rollSceptre(Player player){ - if(RandomFunction.random(1000) == 451){ - if(!player.getInventory().isFull()) { - player.getInventory().add(new Item(9044)); - player.getPacketDispatch().sendMessage("You find a strange object."); - } else { - player.getBank().add(new Item(9044)); - player.getPacketDispatch().sendMessage("You sense that something has appeared in your bank."); - } - } - } - public final boolean success(final Player player, final int skill) { - double level = player.getSkills().getLevel(skill); - double successChance = Math.ceil((level * 50 - reqLevel) / reqLevel / 3 * 4); - int roll = RandomFunction.random(99); - if (successChance >= roll) { - return true; - } - return false; - } - @Override - public boolean handle(Player player, Node node, String option) { - PlunderObjectManager manager = player.getPlunderObjectManager(); - int NPCDeathTime = GameWorld.getTicks() + (1000 / 6); - Location room_entrance[] = {new Location(1927,4477), new Location(1927,4453), new Location(1943,4421), new Location(1954,4477), new Location(1974,4420), new Location(1977,4471), new Location(1927, 4424), new Location(1965,4444)}; - int level = player.getSkills().getLevel(Skills.THIEVING); - int room = 0; - int spearX = 0; - int spearY = 0; - double droom = 0.0; - ZoneMonitor zmon = player.getZoneMonitor(); - if(zmon.isInZone("plunder:room1")){ - room = 1; - reqLevel = 21; - spearX = 0; - spearY = -2; - } else if(zmon.isInZone("plunder:room2")){ - room = 2; - reqLevel = 31; - spearX = 2; - spearY = 0; - } else if(zmon.isInZone("plunder:room3")){ - room = 3; - reqLevel = 41; - spearX = 0; - spearY = 2; - } else if(zmon.isInZone("plunder:room4")){ - room = 4; - reqLevel = 51; - spearX = 0; - spearY = -2; - } else if(zmon.isInZone("plunder:room5")){ - room = 5; - reqLevel = 61; - spearX = 0; - spearY = 2; - } else if(zmon.isInZone("plunder:room6")){ - room = 6; - reqLevel = 71; - spearX = 0; - spearY = -2; - } else if(zmon.isInZone("plunder:room7")){ - room = 7; - reqLevel = 81; - spearX = 0; - spearY = 2; - } else if(zmon.isInZone("plunder:room8")){ - room = 8; - reqLevel = 91; - spearX = -2; - spearY = 0; - } - PlunderObject object = new PlunderObject(node.asObject().getId(),node.asObject().getLocation(),player); - droom = (double) room; - switch (object.getId()) { - case 16517: - if (option.equalsIgnoreCase("pass")) { - if (reqLevel > level){ - player.getPacketDispatch().sendMessage("You need to be at least level " + reqLevel + " thieving."); - break; - } - boolean success = success(player, Skills.THIEVING); - player.animate(animations[success ? 1 : 0]); - if (success) { - player.getPacketDispatch().sendMessage("You successfully pass the spears."); - player.getSkills().addExperience(Skills.THIEVING, 30 + (room * 20), true); - int moveX = player.getLocation().getX(); - int moveY = player.getLocation().getY(); - Location moveto = new Location(moveX + spearX, moveY + spearY); - player.getProperties().setTeleportLocation(moveto); - //player.moveStep(); - } else { - player.getPacketDispatch().sendMessage("You fail to pass the spears."); - } - } - break; - case 16503: - case 16502: - case 16501: - if (option.equalsIgnoreCase("search")) { - if (reqLevel > level){ - player.getPacketDispatch().sendMessage("You need to be at least level " + reqLevel + " thieving."); - break; - } - if (manager.objectList.contains(object) ? manager.objectList.get(manager.objectList.indexOf(object)).playerOpened : false){ - player.getPacketDispatch().sendMessage("You've already looted this."); - break; - } - boolean success = success(player, Skills.THIEVING); - player.animate(animations[success ? 1 : 0]); - player.lock(2); - if (manager.objectList.contains(object) ? manager.objectList.get(manager.objectList.indexOf(object)).snakeCharmed : success) { - player.getPacketDispatch().sendMessage("You successfully search the urn..."); - player.getSkills().addExperience(Skills.THIEVING, 25 + (room * 20), true); - player.getInventory().add(ARTIFACTS[((int)Math.floor(droom / 3))][RandomFunction.random(3)]); - rollSceptre(player); - if(!manager.objectList.contains(object)) { - object.playerOpened = true; - manager.register(object); - } else { - manager.objectList.get(manager.objectList.indexOf(object)).playerOpened = true; - } - switch(object.getId()){ - case 16501: - ObjectBuilder.replace(node.asObject(), node.asObject().transform(16505), 5); - break; - case 16502: - ObjectBuilder.replace(node.asObject(), node.asObject().transform(16506), 5); - break; - case 16503: - ObjectBuilder.replace(node.asObject(), node.asObject().transform(16507), 5); - break; - } - } else { - player.getPacketDispatch().sendMessage("You failed and got bit by a snake."); - player.getImpactHandler().manualHit(player,RandomFunction.random(2,8), ImpactHandler.HitsplatType.NORMAL); - } - } else if(option.equalsIgnoreCase("check for snakes")){ - if(manager.objectList.contains(object) && manager.objectList.get(manager.objectList.indexOf(object)).snakeCharmed){ - player.getPacketDispatch().sendMessage("You already checked for snakes."); - } else { - player.getPacketDispatch().sendMessage("You check the urn for snakes..."); - if (!manager.objectList.contains(object)) { - manager.register(object); - } - manager.originalIndex = manager.objectList.indexOf(object); - switch(object.getId()){ - case 16501: - ObjectBuilder.replace(node.asObject(), node.asObject().transform(16509), 5); - break; - case 16502: - ObjectBuilder.replace(node.asObject(), node.asObject().transform(16510), 5); - break; - case 16503: - ObjectBuilder.replace(node.asObject(), node.asObject().transform(16511), 5); - break; - } - } - } - break; - case 16509: - case 16510: - case 16511: - if(option.equalsIgnoreCase("search") && player.getInventory().containsItem(new Item(4605))){ - player.animate(new Animation(1877)); - player.getPacketDispatch().sendMessage("You charm the snake into docility."); - if(!manager.objectList.contains(manager.objectList.get(manager.originalIndex))) { - object.snakeCharmed = true; - manager.register(object); - } else { - manager.objectList.get(manager.originalIndex).snakeCharmed = true; - } - } else { - player.getImpactHandler().manualHit(player, RandomFunction.random(2, 8), ImpactHandler.HitsplatType.NORMAL); - player.getPacketDispatch().sendMessage("The snake bites you."); - } - break; - case 16473: - if(option.equalsIgnoreCase("search")) { - boolean willSpawnSwarm = (RandomFunction.random(1,20) == 10); - if (reqLevel > level){ - player.getPacketDispatch().sendMessage("You need to be at least level " + reqLevel + " thieving."); - break; - } - if (manager.objectList.contains(object) ? manager.objectList.get(manager.objectList.indexOf(object)).playerOpened : false){ - player.getPacketDispatch().sendMessage("You've already looted this."); - break; - } - player.getPacketDispatch().sendMessage("You search the chest..."); - player.animate(animations[1]); - player.lock(2); - if(willSpawnSwarm) { - player.getPacketDispatch().sendMessage("A swarm flies out!"); - PyramidPlunderSwarmNPC swarm = new PyramidPlunderSwarmNPC(2001,player.getLocation(),player); - swarm.setRespawn(false); - swarm.setAggressive(true); - swarm.init(); - } else { - player.getInventory().add(ARTIFACTS[RandomFunction.random(1, 3)][RandomFunction.random(3)]); - rollSceptre(player); - player.getPacketDispatch().sendMessage("And you find an artifact!"); - player.getSkills().addExperience(Skills.THIEVING, 40 + (room * 20)); - } - ObjectBuilder.replace(node.asObject(), node.asObject().transform(16474), 5); - if(!manager.objectList.contains(object)) { - object.playerOpened = true; - manager.register(object); - } else { - manager.objectList.get(manager.objectList.indexOf(object)).playerOpened = true; - } - } - break; - case 16495: - if(option.equalsIgnoreCase("open")) { - if (reqLevel > level){ - player.getPacketDispatch().sendMessage("You need to be at least level " + reqLevel + " thieving."); - break; - } - if (manager.objectList.contains(object) ? manager.objectList.get(manager.objectList.indexOf(object)).playerOpened : false){ - player.getPacketDispatch().sendMessage("You've already looted this."); - break; - } - boolean willSpawnMummy = (RandomFunction.random(1,5) == 3); - player.animate(animations[1]); - player.getPacketDispatch().sendMessage("You open the sarcophagus and...."); - if(willSpawnMummy) { - player.getPacketDispatch().sendMessage("A mummy crawls out!"); - PyramidPlunderMummyNPC mummy = new PyramidPlunderMummyNPC(1958, player.getLocation(),player); - mummy.setRespawn(false); - mummy.setAggressive(true); - mummy.init(); - } else { - player.getPacketDispatch().sendMessage("You find some loot inside."); - player.getInventory().add(ARTIFACTS[RandomFunction.random(0,3)][RandomFunction.random(3)]); - rollSceptre(player); - player.getSkills().addExperience(Skills.STRENGTH,50 + (room * 20)); - } - ObjectBuilder.replace(node.asObject(), node.asObject().transform(16496), 5); - if(!manager.objectList.contains(object)) { - object.playerOpened = true; - manager.register(object); - } else { - manager.objectList.get(manager.objectList.indexOf(object)).playerOpened = true; - } - } - break; - case 16475: - if(option.equals("pick-lock") && room < 8) { - if (reqLevel > level){ - player.getPacketDispatch().sendMessage("You need to be at least level " + reqLevel + " thieving."); - break; - } - player.animate(animations[1]); - player.lock(2); - boolean doesOpen = success(player, Skills.THIEVING); - if (doesOpen) { - player.getPacketDispatch().sendMessage("The door opens!"); - player.getProperties().setTeleportLocation(room_entrance[room]); - } else { - player.getPacketDispatch().sendMessage("You fail to unlock the door."); - } - } else if(room == 8) { - ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, Location.create(3288, 2801, 0)); - } - break; - } - return true; - } -}*/ diff --git a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderRoom.java b/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderRoom.java deleted file mode 100644 index 18cc081c3..000000000 --- a/Server/src/main/java/core/game/content/activity/pyramidplunder/PyramidPlunderRoom.java +++ /dev/null @@ -1,40 +0,0 @@ -package core.game.content.activity.pyramidplunder; - -import core.game.world.map.Location; - -import java.util.Arrays; -import java.util.List; - -//defines room properties for pyramid plunder -//@author ceik -public enum PyramidPlunderRoom { - ROOM_1(1, 21, 0, -2,new Location(1927,4477), new Location[] {Location.create(1924, 4472, 0), Location.create(1931, 4472, 0), Location.create(1932, 4466, 0),Location.create(1923, 4466, 0)}), - ROOM_2(2, 31, 2, 0,new Location(1927,4453), new Location[] {Location.create(1937, 4459, 0),Location.create(1941, 4456, 0),Location.create(1937, 4448, 0),Location.create(1932, 4450, 0)}), - ROOM_3(3, 41, 0, 2,new Location(1943,4421), new Location[] {Location.create(1946, 4433, 0),Location.create(1955, 4430, 0),Location.create(1954, 4424, 0),Location.create(1949, 4422, 0)}), - ROOM_4(4, 51, 0, -2,new Location(1954,4477), new Location[] {Location.create(1957, 4472, 0),Location.create(1960, 4468, 0),Location.create(1959, 4465, 0),Location.create(1949, 4465, 0)}), - ROOM_5(5, 61, 0, 2,new Location(1974,4420), new Location[] {Location.create(1969, 4427, 0),Location.create(1967, 4432, 0),Location.create(1973, 4437, 0),Location.create(1979, 4430, 0)}), - ROOM_6(6, 71, 0, -2,new Location(1977,4471), new Location[] {Location.create(1980, 4458, 0),Location.create(1976, 4453, 0),Location.create(1974, 4453, 0),Location.create(1969, 4460, 0)}), - ROOM_7(7, 81, 0, 2,new Location(1927, 4424), new Location[] {Location.create(1923, 4432, 0),Location.create(1925, 4440, 0),Location.create(1929, 4440, 0),Location.create(1931, 4432, 0)}), - ROOM_8(8, 91, -2, 0,new Location(1965,4444), new Location[] {Location.create(1952, 4447, 0),Location.create(1953, 4445, 0),Location.create(1957, 4444, 0),Location.create(1962, 4448, 0)}); - - public final int roomNum, reqLevel, spearX, spearY; - Location entrance; - public final List doorLocations; - PyramidPlunderRoom(int roomNum, int reqLevel, int spearX, int spearY, Location entrance, Location[] door_locations){ - this.roomNum = roomNum; - this.reqLevel = reqLevel; - this.spearX = spearX; - this.spearY = spearY; - this.entrance = entrance; - this.doorLocations = Arrays.asList(door_locations); - } - - public static PyramidPlunderRoom forRoomNum(int roomNum){ - for(PyramidPlunderRoom room : PyramidPlunderRoom.values()){ - if(room.roomNum == roomNum){ - return room; - } - } - return null; - } -} diff --git a/Server/src/main/java/core/game/content/activity/stronghold/playersafety/GuardDialoguePlugin.java b/Server/src/main/java/core/game/content/activity/stronghold/playersafety/GuardDialoguePlugin.java deleted file mode 100644 index 5035c3731..000000000 --- a/Server/src/main/java/core/game/content/activity/stronghold/playersafety/GuardDialoguePlugin.java +++ /dev/null @@ -1,141 +0,0 @@ -package core.game.content.activity.stronghold.playersafety; - -import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; -import core.game.system.task.Pulse; -import rs09.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("Unhandled dialogue stage=" + stage); - } - return false; - } - - @Override - public int[] getIds() { - return new int[] { 7142 }; - } - -} diff --git a/Server/src/main/java/core/game/content/activity/stronghold/playersafety/PSOptionHandler.java b/Server/src/main/java/core/game/content/activity/stronghold/playersafety/PSOptionHandler.java deleted file mode 100644 index 9d2bc2c15..000000000 --- a/Server/src/main/java/core/game/content/activity/stronghold/playersafety/PSOptionHandler.java +++ /dev/null @@ -1,173 +0,0 @@ -package core.game.content.activity.stronghold.playersafety; - -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_DOWN_STAIRS; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_ENTRANCE_ID_ENTER; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_ENTRANCE_LEAVE; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_ENTRANCE_LOCATION_ENTER; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_ENTRANCE_LOCATION_LEAVE; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_STAIRS_DOWN; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_STAIRS_UP; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.JAIL_UP_STAIRS; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.TEST_PAPER_ITEM_ID; -import static core.game.content.activity.stronghold.playersafety.StrongHoldOfPlayerSafetyPlugin.forId; - -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueAction; -import core.game.content.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 core.game.content.activity.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/java/core/game/content/activity/stronghold/playersafety/PlayerSafetyTest.java b/Server/src/main/java/core/game/content/activity/stronghold/playersafety/PlayerSafetyTest.java deleted file mode 100644 index 455b50ff1..000000000 --- a/Server/src/main/java/core/game/content/activity/stronghold/playersafety/PlayerSafetyTest.java +++ /dev/null @@ -1,269 +0,0 @@ -package core.game.content.activity.stronghold.playersafety; - -import core.game.component.Component; -import core.game.component.ComponentPlugin; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.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/java/core/game/content/activity/stronghold/playersafety/ProfessorHenryDialogue.java b/Server/src/main/java/core/game/content/activity/stronghold/playersafety/ProfessorHenryDialogue.java deleted file mode 100644 index f40477ba9..000000000 --- a/Server/src/main/java/core/game/content/activity/stronghold/playersafety/ProfessorHenryDialogue.java +++ /dev/null @@ -1,211 +0,0 @@ -package core.game.content.activity.stronghold.playersafety; - -import core.game.component.Component; -import core.game.node.entity.player.link.emote.Emotes; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.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/java/core/game/content/activity/stronghold/playersafety/SafetyMapZone.java b/Server/src/main/java/core/game/content/activity/stronghold/playersafety/SafetyMapZone.java deleted file mode 100644 index 010521824..000000000 --- a/Server/src/main/java/core/game/content/activity/stronghold/playersafety/SafetyMapZone.java +++ /dev/null @@ -1,15 +0,0 @@ -package core.game.content.activity.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/java/core/game/content/activity/stronghold/playersafety/StrongHoldOfPlayerSafetyPlugin.java b/Server/src/main/java/core/game/content/activity/stronghold/playersafety/StrongHoldOfPlayerSafetyPlugin.java deleted file mode 100644 index 9501564fb..000000000 --- a/Server/src/main/java/core/game/content/activity/stronghold/playersafety/StrongHoldOfPlayerSafetyPlugin.java +++ /dev/null @@ -1,211 +0,0 @@ -package core.game.content.activity.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 rs09.plugin.PluginManager; - -/** - * @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 { - PluginManager.definePlugin(new PSOptionHandler()); - PluginManager.definePlugin(new GuardDialoguePlugin()); - PluginManager.definePlugin(new PlayerSafetyTest()); - PluginManager.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/java/core/game/content/consumable/Consumables.java b/Server/src/main/java/core/game/content/consumable/Consumables.java deleted file mode 100644 index 5c0c68a13..000000000 --- a/Server/src/main/java/core/game/content/consumable/Consumables.java +++ /dev/null @@ -1,383 +0,0 @@ -package core.game.content.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.content.consumable.effects.*; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; - -/** - * Represents a repository of active consumables in the framework. - */ -public enum Consumables { - - /** Meats */ - COOKED_MEAT(new Food(new int[] {2142}, new HealingEffect(3))), - SHRIMPS(new Food(new int[] {315}, new HealingEffect(3))), - COOKED_CHICKEN(new Food(new int[] {2140}, new HealingEffect(3))), - COOKED_RABBIT(new Food(new int[] {3228}, new HealingEffect(5))), - ANCHOVIES(new Food(new int[] {319}, new HealingEffect(1))), - SARDINE(new Food(new int[] {325}, new HealingEffect(4))), - POISON_KARAMBWAN(new Food(new int[] {3146}, new PoisonKarambwanEffect())), - UGTHANKI_MEAT(new Food(new int[] {1861}, new HealingEffect(3))), - 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))), - TROUT(new Food(new int[] {333}, new HealingEffect(7))), - SPIDER_ON_STICK(new Food(new int[] {6297, 6305}, new HealingEffect(7))), - 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))), - 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))), - SALMON(new Food(new int[] {329}, new HealingEffect(9))), - SLIMY_EEL(new Food(new int[] {3381}, new HealingEffect(6))), - TUNA(new Food(new int[] {361}, new HealingEffect(10))), - COOKED_KARAMBWAN(new Food(new int[] {3144}, new HealingEffect(18))), - 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))), - 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))), - 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))), - - /** Breads */ - BREAD(new Food(new int[] {2309}, new HealingEffect(5))), - BAGUETTE(new Food(new int[] {6961}, new HealingEffect(6))), - - /** Sandwiches */ - TRIANGLE_SANDWICH(new Food(new int[] {6962}, new HealingEffect(6))), - SQUARE_SANDWICH(new Food(new int[] {6965}, new HealingEffect(6))), - SEAWEED_SANDWICH(new FakeConsumable(3168, new String[] {"You really, really do not want to eat that."})), - - /** Kebabs */ - UGTHANKI_KEBAB(new Food(new int[] {1883}, new UgthankiKebabEffect())), - UGTHANKI_KEBAB_SMELLING(new Food(new int[] {1885}, new SmellingUgthankiKebabEffect())), - KEBAB(new Food(new int[] {1971}, new KebabEffect())), - SUPER_KEBAB(new Food(new int[] {4608}, new SuperKebabEffect())), - - /** Pies */ - REDBERRY_PIE(new HalfableFood(new int[] {2325, 2333, 2313}, new HealingEffect(5))), - MEAT_PIE(new HalfableFood(new int[] {2327, 2331, 2313}, new HealingEffect(6))), - APPLE_PIE(new HalfableFood(new int[] {2323, 2335, 2313}, new HealingEffect(7))), - GARDEN_PIE(new HalfableFood(new int[] {7178, 7180, 2313}, new MultiEffect(new HealingEffect(6), new SkillEffect(Skills.FARMING, 3, 0)))), - FISH_PIE(new HalfableFood(new int[] {7188, 7190, 2313}, new MultiEffect(new HealingEffect(6), new SkillEffect(Skills.FISHING, 3, 0)))), - ADMIRAL_PIE(new HalfableFood(new int[] {7198, 7200, 2313}, new MultiEffect(new HealingEffect(8), new SkillEffect(Skills.FISHING, 5, 0)))), - WILD_PIE(new HalfableFood(new int[] {7208, 7210, 2313}, new MultiEffect(new SkillEffect(Skills.SLAYER, 5, 0), new SkillEffect(Skills.RANGE, 4, 0), new HealingEffect(11)))), - SUMMER_PIE(new HalfableFood(new int[] {7218, 7220, 2313}, new MultiEffect(new HealingEffect(11), new SkillEffect(Skills.AGILITY, 5, 0), new EnergyEffect(10)))), - - /** Stews */ - STEW(new Food(new int[] {2003, 1923}, new HealingEffect(11))), - SPICY_STEW(new Food(new int[] {7479, 1923}, new HealingEffect(11))), - CURRY(new Food(new int[] {2011, 1923}, new HealingEffect(19))), - BANANA_STEW(new Food(new int[] {4016}, new HealingEffect(11))), - - /** Pizzas */ - PLAIN_PIZZA(new HalfableFood(new int[] {2289, 2291}, new HealingEffect(7))), - MEAT_PIZZA(new HalfableFood(new int[] {2293, 2295}, new HealingEffect(8))), - ANCHOVY_PIZZA(new HalfableFood(new int[] {2297, 2299}, new HealingEffect(9))), - PINEAPPLE_PIZZA(new HalfableFood(new int[] {2301, 2303}, new HealingEffect(11))), - - /** Cakes */ - CAKE(new Cake(new int[] {1891, 1893, 1895}, new HealingEffect(4), "You eat part of the cake.", "You eat some more cake.", "You eat the slice of cake.")), - CHOCOLATE_CAKE(new Cake(new int[] {1897, 1899, 1901}, new HealingEffect(5), "You eat part of the chocolate cake.", "You eat some more of the chocolate cake.", "You eat the slice of cake.")), - ROCK_CAKE(new Food(new int[] {2379}, new RockCakeEffect(), "The rock cake resists all attempts to eat it.")), - DWARVEN_ROCK_CAKE(new Food(new int[] {7510, 7510}, new DwarvenRockCakeEffect(), "Ow! You nearly broke a tooth!", "The rock cake resists all attempts to eat it.")), - HOT_DWARVEN_ROCK_CAKE(new Food(new int[] {7509, 7509}, new DwarvenRockCakeEffect(), "Ow! You nearly broke a tooth!", "The rock cake resists all attempts to eat it.")), - COOKED_FISHCAKE(new Food(new int[] {7530}, new HealingEffect(11))), - 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))), - 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)))), - 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))), - FRIED_MUSHROOMS(new Food(new int[] {7082, 1923}, new HealingEffect(5))), - POTATO_WITH_CHEESE(new Food(new int[] {6705}, new HealingEffect(16))), - EGG_POTATO(new Food(new int[] {7056}, new HealingEffect(11))), - MUSHROOMS_AND_ONIONS(new Food(new int[] {7066, 1923}, new HealingEffect(11))), - 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!")), - 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))), - - /** Dairies */ - POT_OF_CREAM(new Food(new int[] {2130}, new HealingEffect(1))), - CHEESE(new Food(new int[] {1985}, new HealingEffect(2))), - CHOCOLATEY_MILK(new Drink(new int[] {1977, 1925}, new HealingEffect(4))), - - /** Fruits */ - BANANA(new Food(new int[] {1963}, new HealingEffect(2))), - SLICED_BANANA(new Food(new int[] {3162}, new HealingEffect(2))), - RED_BANANA(new Food(new int[] {7572}, new HealingEffect(5), "You eat the red banana. It's tastier than your average banana.")), - SLICED_RED_BANANA(new Food(new int[] {7574}, new HealingEffect(5), "You eat the sliced red banana. Yum.")), - 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))), - 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)))), - 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_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))), - LEMON_SLICES(new Food(new int[] {2106}, new HealingEffect(2))), - LIME(new Food(new int[] {2120}, new HealingEffect(2))), - LIME_CHUNKS(new Food(new int[] {2122}, new HealingEffect(2))), - 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)))), - - /** Gnome Cooking */ - TOAD_CRUNCHIES(new Food(new int[] {2217}, new HealingEffect(12))), - PREMADE_TD_CRUNCH(new Food(new int[] {2243}, new HealingEffect(12))), - 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))), - PREMADE_WM_CRUNC(new Food(new int[] {2237}, new HealingEffect(8))), - CHOCCHIP_CRUNCHIES(new Food(new int[] {2209}, new HealingEffect(7))), - PREMADE_CH_CRUNCH(new Food(new int[] {2239}, new HealingEffect(7))), - FRUIT_BATTA(new Food(new int[] {2277}, new HealingEffect(11))), - PREMADE_FRT_BATTA(new Food(new int[] {2225}, new HealingEffect(11))), - TOAD_BATTA(new Food(new int[] {2255}, new HealingEffect(11))), - PREMADE_TD_BATTA(new Food(new int[] {2221}, new HealingEffect(11))), - WORM_BATTA(new Food(new int[] {2253}, new HealingEffect(11))), - PREMADE_WM_BATTA(new Food(new int[] {2219}, new HealingEffect(11))), - VEGETABLE_BATTA(new Food(new int[] {2281}, new HealingEffect(11))), - PREMADE_VEG_BATTA(new Food(new int[] {2227}, new HealingEffect(11))), - CHEESE_AND_TOMATOES_BATTA(new Food(new int[] {2259}, new HealingEffect(11))), - PREMADE_CT_BATTA(new Food(new int[] {2223}, new HealingEffect(11))), - WORM_HOLE(new Food(new int[] {2191}, new HealingEffect(12))), - PREMADE_WORM_HOLE(new Food(new int[] {2233}, new HealingEffect(12))), - VEG_BALL(new Food(new int[] {2195}, new HealingEffect(12))), - PREMADE_VEG_BALL(new Food(new int[] {2235}, new HealingEffect(12))), - TANGLED_TOADS_LEGS(new Food(new int[] {2187}, new HealingEffect(15))), - PREMADE_TTL(new Food(new int[] {2231}, new HealingEffect(15))), - CHOCOLATE_BOMB(new Food(new int[] {2195}, new HealingEffect(15))), - PREMADE_CHOC_BOMB(new Food(new int[] {2229}, new HealingEffect(15))), - TOAD_LEGS(new Food(new int[] {2152}, new HealingEffect(3))), - KING_WORM(new Food(new int[] {2162}, new HealingEffect(2))), - - /** 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_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))), - 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_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_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_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_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))), - 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_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))), - 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())), - - /** Cocktails */ - FRUIT_BLAST(new Drink(new int[] {2084, 2026}, new HealingEffect(9))), - PREMADE_FR_BLAST(new Food(new int[] {2034, 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.")), - 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)))), - 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)))), - 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)))), - 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)))), - 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)))), - - /** 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)))), - 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)))), - - /** 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))), - - /** 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())), - 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_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_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))), - - /** Miscellaneous */ - CHOCOLATE_BAR(new Food(new int[] {1973}, new HealingEffect(3))), - PURPLE_SWEETS(new Food(new int[] {4561}, new HealingEffect(0))), - PURPLE_SWEETS_STACKABLE(new Food(new int[] {10476}, new MultiEffect(new EnergyEffect(10), new RandomHealthEffect(1, 3)), "The sugary goodness heals some energy.", "The sugary goodness is yummy.")), - FIELD_RATION(new Food(new int[] {7934}, new HealingEffect(10))), - ROLL(new Food(new int[] {6963}, new HealingEffect(6))), - TCHIKI_MONKEY_NUTS(new Food(new int[] {7573}, new HealingEffect(5), "You eat the Tchiki monkey nuts. They taste nutty.")), - 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))), - EQUA_LEAVES(new Food(new int[]{2128}, new HealingEffect(1))), - CHOC_ICE(new Food(new int[]{6794}, new HealingEffect(6))), - EDIBLE_SEAWEED(new Food(new int[] {403}, new HealingEffect(4))), - - /** Special Events */ - PUMPKIN(new Food(new int[] {1959}, new HealingEffect(14))), - EASTER_EGG(new Food(new int[] {1961}, new HealingEffect(14))), - - /** Potions */ - 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")))), - 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(.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 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)))), - 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(0.1, 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_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 SkillEffect(Skills.HITPOINTS, 0, -0.10), 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)))), - 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)))), - STR_MIX(new BarbarianMix(new int[] {11441, 11443}, 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)))), - 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)))), - SUPER_ATT_MIX(new BarbarianMix(new int[] {11469, 11471}, new MultiEffect(new SkillEffect(Skills.ATTACK, 5, 0.15), new HealingEffect(6)))), - FISH_MIX(new BarbarianMix(new int[] {11477, 11479}, new MultiEffect(new SkillEffect(Skills.FISHING, 3, 0), new HealingEffect(6)))), - 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)))); - - public static HashMap consumables = new HashMap<>(); - - private final Consumable consumable; - - Consumables(Consumable consumable) { - this.consumable = consumable; - } - - public Consumable getConsumable() { - return consumable; - } - - public static Consumable getConsumableById(final int itemId) { - return consumables.get(itemId); - } - - public static void add(final Consumable consumable) { - for (int id : consumable.getIds()) { - consumables.putIfAbsent(id, consumable); - } - } - - /* - Static modifier used to populate search engine lists. - */ - static { - for (Consumables consumable : Consumables.values()) { - add(consumable.consumable); - } - } -} diff --git a/Server/src/main/java/core/game/content/consumable/CookingProperties.java b/Server/src/main/java/core/game/content/consumable/CookingProperties.java deleted file mode 100644 index 3e67b97e3..000000000 --- a/Server/src/main/java/core/game/content/consumable/CookingProperties.java +++ /dev/null @@ -1,304 +0,0 @@ -//package org.crandor.game.content.global.consumable; -// -//import org.crandor.game.content.global.SkillcapePerks; -//import core.game.node.entity.skill.Skills; -//import org.crandor.game.node.entity.player.Player; -//import org.crandor.game.node.entity.player.info.portal.Perks; -//import org.crandor.game.node.entity.player.link.audio.Audio; -//import org.crandor.game.node.item.Item; -//import org.crandor.game.node.object.GameObject; -// -//import java.security.SecureRandom; -// -///** -// * Represents the properties used when cooking the food. -// * @author 'Vexia -// * @date 22/12/2013 -// */ -//public class CookingProperties { -// -// /** -// * Represents the secure random. -// */ -// private static final SecureRandom RANDOM = new SecureRandom(); -// -// /** -// * Represents the cooking sound. -// */ -// private static final Audio SOUND = new Audio(65, 10, 0); -// -// /** -// * Represents the cooking gauntlets item. -// */ -// private static final Item GAUNTLETS = new Item(775); -// -// /** -// * Represents the fail message to display when burning food. -// */ -// public static final String FAIL_MESSAGE = "Oops! You accidently burnt the @name.."; -// -// /** -// * Represents the level needed to cook the item. -// */ -// private final int level; -// -// /** -// * Represents the cooking experience gained. -// */ -// private final double experience; -// -// /** -// * Represents the level the food stops burning at on the fire. -// */ -// private final int fireBurnLevel; -// -// /** -// * Represents the level the food stops burning at on the range. -// */ -// private final int rangeBurnLevel; -// -// /** -// * Represents the level the food stops burning at when wearing gauntlets. -// */ -// private final int gauntletsBurnLevel; -// -// /** -// * Represents if its a range only food (meaning only can be used on a range -// * not fire). -// */ -// private final boolean range; -// -// /** -// * Represents if its an iron spit roast. -// */ -// private final boolean spit; -// -// /** -// * Represents the messages displayed when cooking. -// */ -// private String[] messages = null; -// -// /** -// * Constructs a new {@code CookingProperties} {@code Object}. -// * @param level the level. -// * @param experience the experience. -// * @param fireBurnLevel the burning level. -// * @param rangeBurnLevel the burning level on the range. -// * @param gauntletsBurnLevel the burning level with gauntlets. -// * @param range if the food can only be used on a range. -// * @param spit if its an iron spit item. -// */ -// public CookingProperties(int level, double experience, int burnLevel, int rangeBurnLevel, int gauntletsBurnLevel, boolean range, boolean spit, final String... messages) { -// this.level = level; -// this.experience = experience; -// this.fireBurnLevel = burnLevel; -// this.rangeBurnLevel = rangeBurnLevel; -// this.gauntletsBurnLevel = gauntletsBurnLevel; -// this.range = range; -// this.spit = spit; -// this.messages = messages; -// } -// -// /** -// * Constructs a new {@code CookingProperties} {@code Object}. -// * @param level the level. -// * @param experience the experience. -// * @param fireBurnLevel the burning level. -// * @param messages the messages -// */ -// public CookingProperties(final int level, final double experience, final int burnLevel, final String... messages) { -// this(level, experience, burnLevel, burnLevel, burnLevel, false, false, messages); -// } -// -// /** -// * Constructs a new {@code CookingProperties} {@code Object}. -// * @param level the level. -// * @param experience the experience. -// * @param fireBurnLevel the burning level. -// * @param range if its a range item. -// * @param spit if its on an iron spit. -// * @param messages the messages -// */ -// public CookingProperties(final int level, final double experience, final int burnLevel, boolean range, boolean spit, final String... messages) { -// this(level, experience, burnLevel, burnLevel, burnLevel, false, spit, messages); -// } -// -// /** -// * Constructs a new {@code CookingProperties} {@code Object}. -// * @param level the level. -// * @param experience the experience. -// * @param fireBurnLevel the burning level. -// * @param range if its only cookable on the range -// * @param messages the messages -// */ -// public CookingProperties(final int level, final double experience, final int burnLevel, final boolean range, final String... messages) { -// this(level, experience, burnLevel, burnLevel, burnLevel, range, false, messages); -// } -// -// /** -// * Constructs a new {@code CookingProperties} {@code Object}. -// * @param level the level. -// * @param experience the experience. -// * @param fireBurnLevel the burning level. -// * @param rangeBurnLevel the range burning level. -// * @param gauntLetsBurnLevel the gauntletsBurn level. -// */ -// public CookingProperties(final int level, final double experience, final int burnLevel, final int rangeBurnLevel, final int gauntletsBurnLevel) { -// this(level, experience, burnLevel, rangeBurnLevel, gauntletsBurnLevel, false, false); -// } -// -// /** -// * Method used to cook a food. This method can be overried to intercept the -// * way a food is cooked. A prime example of how this can be used is with the -// * sinew consumable food. -// * @param food the food we're cooking. -// * @param player the player. -// * @param object the object. -// * @return True if we we're succesfull. -// */ -// public boolean cook(final Food food, final Player player, final GameObject object) { -// return cook(food, player, object, isBurned(player, object)); -// } -// -// /** -// * Method used to cook a food. This method can be overried to intercept the -// * way a food is cooked. A prime example of how this can be used is with the -// * sinew consumable food. -// * @param food the food we're cooking. -// * @param player the player. -// * @param object the object. -// * @param if it's burned. -// * @return True if we we're succesfull. -// */ -// public boolean cook(final Food food, final Player player, final GameObject object, final boolean burned) { -// if (player.getInventory().remove(food.getRaw())) { -// if (!burned) { -// Item item = food.getItem(); -// player.getInventory().add(item); -// Perks.addDouble(player, item); -// } else { -// player.getInventory().add(food.getBurnt()); -// } -// player.getSkills().addExperience(Skills.COOKING, burned ? 0 : getExperience(), true); -// player.getPacketDispatch().sendMessage(getMessage(food, player, object, burned)); -// player.getAudioManager().send(SOUND); -// return true; -// } -// return false; -// } -// -// /** -// * Gets the level. -// * @return The level. -// */ -// public int getLevel() { -// return level; -// } -// -// /** -// * Gets the experience. -// * @return The experience. -// */ -// public double getExperience() { -// return experience; -// } -// -// /** -// * Gets the fireBurnLevel. -// * @return The fireBurnLevel. -// */ -// public int getBurnLevel() { -// return fireBurnLevel; -// } -// -// /** -// * Gets the range. -// * @return The range. -// */ -// public boolean isRange() { -// return range; -// } -// -// /** -// * Gets the fireBurnLevel. -// * @return The fireBurnLevel. -// */ -// public int getFireBurnLevel() { -// return fireBurnLevel; -// } -// -// /** -// * Gets the rangeBurnLevel. -// * @return The rangeBurnLevel. -// */ -// public int getRangeBurnLevel() { -// return rangeBurnLevel; -// } -// -// /** -// * Gets the gauntletsBurnLevel. -// * @return The gauntletsBurnLevel. -// */ -// public int getGauntletsBurnLevel() { -// return gauntletsBurnLevel; -// } -// -// /** -// * Gets the spit. -// * @return The spit. -// */ -// public boolean isSpit() { -// return spit; -// } -// -// /** -// * Gets the burning level depicted by if the player has cooking gauntlets -// * on, or effected by the object they are interactign with. -// * @param player the player cooking. -// * @param object the object we check. -// * @return the level at which the player stops burning. -// */ -// private final int getBurnLevel(final Player player, final GameObject object) { -// return player.getEquipment().containsItem(GAUNTLETS) ? gauntletsBurnLevel : object.getName().toLowerCase().equals("fire") ? fireBurnLevel : rangeBurnLevel; -// } -// -// /** -// * Gets the true/false value if the food is burned by the player. -// * @param player the player. -// * @param object the object. -// * @return True if the food is burned or not. -// */ -// public boolean isBurned(final Player player, final GameObject object) { -// if (SkillcapePerks.hasSkillcapePerk(player, SkillcapePerks.COOKING)) { -// return false; -// } -// if (player.getSkills().getLevel(Skills.COOKING) > getBurnLevel(player, object)) { -// return false; -// } -// double burn_chance = 60.0 + (object.getName().equals("fire") ? 1.00 : 0) - (object.getId() == 114 ? 1.00 : 0); -// if (player.getDetails().getShop().hasPerk(Perks.MASTER_CHEF)) { -// burn_chance -= (burn_chance * 0.20); -// } -// double cook_level = (double) player.getSkills().getLevel(Skills.COOKING); -// double lev_needed = (double) getLevel(); -// double burn_stop = (double) getBurnLevel(player, object); -// 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 ? false : true; -// } -// -// /** -// * Gets the message displayed when the food is cooked or burned. -// * @param food the food we're cooking/burning. -// * @param player the player. -// * @param object the object we're cooking on. -// * @param burned if the food is burned. -// * @return the message to display. -// */ -// public String getMessage(final Food food, final Player player, final GameObject object, final boolean burned) { -// return messages != null && messages.length != 0 ? messages[burned ? 1 : 0].replace("@name", food.getItem().getName().toLowerCase()) : burned ? FAIL_MESSAGE.replace("@name", food.getItem().getName().toLowerCase()) + "." : "You successfully cook the " + food.getItem().getName().toLowerCase() + "."; -// } -//} diff --git a/Server/src/main/java/core/game/content/consumable/Potion.java b/Server/src/main/java/core/game/content/consumable/Potion.java deleted file mode 100644 index 48dc02c81..000000000 --- a/Server/src/main/java/core/game/content/consumable/Potion.java +++ /dev/null @@ -1,76 +0,0 @@ -package core.game.content.consumable; - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; -import core.game.node.item.Item; - -public class Potion extends Drink { - - private static final int VIAL = 229; - - 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); - } - - @Override - public void consume(Item item, Player player) { - executeConsumptionActions(player); - final int nextItemId = getNextItemId(item.getId()); - if (nextItemId != -1) { - player.getInventory().replace(new Item(nextItemId), item.getSlot()); - } else { - if (player.getBarcrawlManager().isFinished()) { - player.getInventory().remove(item); - } else { - player.getInventory().replace(new Item(VIAL), item.getSlot()); - } - } - final int initialLifePoints = player.getSkills().getLifepoints(); - Consumables.getConsumableById(item.getId()).effect.activate(player); - if (messages.length == 0) { - sendDefaultMessages(player, item); - } else { - sendCustomMessages(player, messages); - } - sendHealingMessage(player, initialLifePoints); - } - - @Override - protected void executeConsumptionActions(Player player) { - player.animate(animation); - player.getAudioManager().send(SOUND); - } - - @Override - protected void sendDefaultMessages(Player player, Item item) { - int consumedDoses = 1; - int i = 0; - while (ids[i] != item.getId()) { - consumedDoses++; - i++; - } - 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."); - } - } - - public static int getDose(Item potion){ - return Integer.parseInt(potion.getName().replaceAll("[^\\d.]","")); - } - - public int[] getIds() { - return ids; - } - - public ConsumableEffect getEffect() { - return effect; - } -} diff --git a/Server/src/main/java/core/game/content/consumable/effects/DamageEffect.java b/Server/src/main/java/core/game/content/consumable/effects/DamageEffect.java deleted file mode 100644 index e615dbe15..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/DamageEffect.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.combat.ImpactHandler; -import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; - -public class DamageEffect extends ConsumableEffect { - final double amt; - final boolean isPercent; - - public DamageEffect(double amt,boolean isPercent){ - this.amt = amt; - this.isPercent = isPercent; - } - - @Override - public void activate(Player p) { - p.getImpactHandler().manualHit(p,-getHealthEffectValue(p), ImpactHandler.HitsplatType.NORMAL); - } - - @Override - public int getHealthEffectValue(Player player) { - double amount = amt; - if (isPercent) { - amount /= 100; - return (int) -(amount * player.getSkills().getLifepoints()); - } - return (int) -amt; - } -} diff --git a/Server/src/main/java/core/game/content/consumable/effects/EnergyEffect.java b/Server/src/main/java/core/game/content/consumable/effects/EnergyEffect.java deleted file mode 100644 index 6ffb4987c..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/EnergyEffect.java +++ /dev/null @@ -1,15 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; - -public class EnergyEffect extends ConsumableEffect { - double amt; - public EnergyEffect(int amt){ - this.amt = amt; - } - @Override - public void activate(Player p) { - p.getSettings().updateRunEnergy(-amt); - } -} diff --git a/Server/src/main/java/core/game/content/consumable/effects/KegOfBeerEffect.java b/Server/src/main/java/core/game/content/consumable/effects/KegOfBeerEffect.java deleted file mode 100644 index 027f03c7b..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/KegOfBeerEffect.java +++ /dev/null @@ -1,22 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; -import core.game.node.entity.skill.Skills; - -public class KegOfBeerEffect extends ConsumableEffect { - - private final static int healing = 15; - - @Override - public void activate(Player p) { - final int attackLevelReduction = (p.getSkills().getLevel(Skills.ATTACK) * 65) / 99; - final MultiEffect effect = new MultiEffect(new HealingEffect(15), new RandomSkillEffect(Skills.STRENGTH, 2, 10), new SkillEffect(Skills.ATTACK, attackLevelReduction, 0)); - effect.activate(p); - } - - @Override - public int getHealthEffectValue(Player player) { - return healing; - } -} diff --git a/Server/src/main/java/core/game/content/consumable/effects/NettleTeaEffect.java b/Server/src/main/java/core/game/content/consumable/effects/NettleTeaEffect.java deleted file mode 100644 index 068422de6..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/NettleTeaEffect.java +++ /dev/null @@ -1,20 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; - -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/java/core/game/content/consumable/effects/PercentHeal.java b/Server/src/main/java/core/game/content/consumable/effects/PercentHeal.java deleted file mode 100644 index a62df2b72..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/PercentHeal.java +++ /dev/null @@ -1,21 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; - -public class PercentHeal extends ConsumableEffect { - double perc = 0.0; - - public PercentHeal(double percent){ - perc = percent; - } - - @Override - public void activate(Player p) { - int maxHp = p.getSkills().getMaximumLifepoints(); - int curHp = p.getSkills().getLifepoints(); - int amount = (int) Math.floor(maxHp * perc); - amount = Math.min(amount, (int)((1.0 + perc) * (double)maxHp - (double)curHp)); - p.getSkills().healNoRestrictions(amount); - } -} diff --git a/Server/src/main/java/core/game/content/consumable/effects/PrayerEffect.java b/Server/src/main/java/core/game/content/consumable/effects/PrayerEffect.java deleted file mode 100644 index 0c87c5933..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/PrayerEffect.java +++ /dev/null @@ -1,19 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; -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/java/core/game/content/consumable/effects/RemoveStateEffect.java b/Server/src/main/java/core/game/content/consumable/effects/RemoveStateEffect.java deleted file mode 100644 index f6a64af83..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/RemoveStateEffect.java +++ /dev/null @@ -1,25 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; -import core.game.content.consumable.ConsumableEffect; - -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/java/core/game/content/consumable/effects/RestoreEffect.java b/Server/src/main/java/core/game/content/consumable/effects/RestoreEffect.java deleted file mode 100644 index f49c40e49..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/RestoreEffect.java +++ /dev/null @@ -1,26 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; -import core.game.node.entity.skill.Skills; - -public class RestoreEffect extends ConsumableEffect { - double base,bonus; - public RestoreEffect(double base, double bonus){ - this.base = base; - this.bonus = bonus; - } - final int[] SKILLS = new int[] { Skills.DEFENCE, Skills.ATTACK, Skills.STRENGTH, Skills.MAGIC, Skills.RANGE }; - @Override - public void activate(Player p) { - Skills sk = p.getSkills(); - for(int skill : SKILLS){ - int statL = sk.getStaticLevel(skill); - int curL = sk.getLevel(skill); - if(curL < statL){ - int boost = (int) (base + (statL * bonus)); - p.getSkills().updateLevel(skill, boost, statL); - } - } - } -} diff --git a/Server/src/main/java/core/game/content/consumable/effects/SkillEffect.java b/Server/src/main/java/core/game/content/consumable/effects/SkillEffect.java deleted file mode 100644 index 5fae1c422..000000000 --- a/Server/src/main/java/core/game/content/consumable/effects/SkillEffect.java +++ /dev/null @@ -1,22 +0,0 @@ -package core.game.content.consumable.effects; - -import core.game.node.entity.player.Player; -import core.game.content.consumable.ConsumableEffect; -import core.game.node.entity.skill.Skills; - -public class SkillEffect extends ConsumableEffect { - private final int skill_slot; - private final double base; - private final double bonus; - public SkillEffect(int skill_slot, double base, double bonus){ - this.skill_slot = skill_slot; - this.base = base; - this.bonus = bonus; - } - @Override - public void activate(Player p) { - Skills skills = p.getSkills(); - int slevel = skills.getStaticLevel(skill_slot); - skills.updateLevel(skill_slot,(int)(base + (bonus * slevel)),slevel + (int)(base + (bonus * slevel))); - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/AablaDialogue.java b/Server/src/main/java/core/game/content/dialogue/AablaDialogue.java deleted file mode 100644 index 5f85a8244..000000000 --- a/Server/src/main/java/core/game/content/dialogue/AablaDialogue.java +++ /dev/null @@ -1,137 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.plugin.Initializable; -import core.game.world.update.flag.context.Animation; - -/** - * Represents the npc aabla's dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class AablaDialogue extends DialoguePlugin { - - /** - * Represents the npc animation to use. - */ - private static final Animation ANIMATION = new Animation(881); - - /** - * Constructs a new {@code AablaDialogue} {@code Object}. - */ - public AablaDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code AablaDialogue} {@code Object}. - * @param player the player. - */ - public AablaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AablaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - player("Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("Hi. How can I help?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "Can you heal me?", "Do you see a lot of injured fighters?", "Do you come here often?"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - player("Can you heal me?"); - stage = 10; - break; - case 2: - player("Do you see a lot of injured fighters?"); - stage = 30; - break; - case 3: - player("Do you come here often?"); - stage = 20; - break; - } - break; - case 10: - npc("Of course!"); - stage = 11; - break; - case 11: - npc.faceTemporary(player, 2); - npc.animate(ANIMATION); - player.lock(4); - close(); - GameWorld.getPulser().submit(new Pulse(3, player) { - @Override - public boolean pulse() { - if (player.getSkills().getLifepoints() == player.getSkills().getStaticLevel(Skills.HITPOINTS)) { - npc("You look healthy to me!"); - } else { - player.getSkills().heal(20); - npc("There you go!"); - } - stage = 12; - return true; - } - }); - break; - case 12: - end(); - break; - case 20: - npc("I work here, so yes!"); - stage = 21; - break; - case 21: - end(); - break; - case 30: - npc("Yes I do. Thankfully we can cope with almost aything.", "Jaraah really is a wonderful surgeon, his methods are a", "little unorthodox but he gets the job done."); - stage = 31; - break; - case 31: - npc("I shouldn't tell you this but his nickname is 'The", "Butcher'."); - stage = 32; - break; - case 32: - player("That's reassuring."); - stage = 33; - break; - case 33: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 959 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/AlfonseWaiterDialogue.java b/Server/src/main/java/core/game/content/dialogue/AlfonseWaiterDialogue.java deleted file mode 100644 index bb10029b5..000000000 --- a/Server/src/main/java/core/game/content/dialogue/AlfonseWaiterDialogue.java +++ /dev/null @@ -1,90 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/AnjaDialoguePlugin.java b/Server/src/main/java/core/game/content/dialogue/AnjaDialoguePlugin.java deleted file mode 100644 index bed29eced..000000000 --- a/Server/src/main/java/core/game/content/dialogue/AnjaDialoguePlugin.java +++ /dev/null @@ -1,142 +0,0 @@ -package core.game.content.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.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/java/core/game/content/dialogue/AntonDialogue.java b/Server/src/main/java/core/game/content/dialogue/AntonDialogue.java deleted file mode 100644 index e39d56a77..000000000 --- a/Server/src/main/java/core/game/content/dialogue/AntonDialogue.java +++ /dev/null @@ -1,68 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/ArchaeologicalExpertDialogue.java b/Server/src/main/java/core/game/content/dialogue/ArchaeologicalExpertDialogue.java deleted file mode 100644 index 82ac3a222..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ArchaeologicalExpertDialogue.java +++ /dev/null @@ -1,51 +0,0 @@ -package core.game.content.dialogue; - -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the archaeologicals expert dialogue. - * @author Aero - * @version 1.0 - */ -@Initializable -public class ArchaeologicalExpertDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ArchaeologicalExpertDialogue} {@code Object}. - */ - public ArchaeologicalExpertDialogue() { - /** - * Empty to stop instantiation. - */ - } - - /** - * Constructs a new {@code ArchaeologicalExpertDialogue} {@code Object}. - * @param player The player to construct the class for. - */ - public ArchaeologicalExpertDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ArchaeologicalExpertDialogue(player); - } - - @Override - public boolean open(Object... args) { - return false; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - return true; - } - - @Override - public int[] getIds() { - return new int[] { 619 }; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/ArdougneBaker.java b/Server/src/main/java/core/game/content/dialogue/ArdougneBaker.java deleted file mode 100644 index 83b50d1a8..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ArdougneBaker.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/ArdougneShopKeeperPlugin.java b/Server/src/main/java/core/game/content/dialogue/ArdougneShopKeeperPlugin.java deleted file mode 100644 index 891fb7697..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ArdougneShopKeeperPlugin.java +++ /dev/null @@ -1,83 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/BKListenDialogue.java b/Server/src/main/java/core/game/content/dialogue/BKListenDialogue.java deleted file mode 100644 index 83bf1aa6e..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BKListenDialogue.java +++ /dev/null @@ -1,159 +0,0 @@ -package core.game.content.dialogue; - -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; - -/** - * Represents the dialogue of listening throug a grill during the black knights' - * fortress quest. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BKListenDialogue extends DialoguePlugin { - - /** - * Represents the cabbage item. - */ - private static final Item CABBAGE = new Item(1965); - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(832); - - /** - * Represents the captain npc. - */ - private final int captain = 610; - - /** - * Represents the witch npc. - */ - private final int witch = 611; - - /** - * Represents the goblin greldo npc. - */ - private final int greldo = 612; - - /** - * Represents the cat npc. - */ - private final int cat = 4607; - - /** - * Constructs a new {@code BKListenDialogue} {@code Object}. - */ - public BKListenDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BKListenDialogue} {@code Object}. - * @param player the player. - */ - public BKListenDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BKListenDialogue(player); - } - - @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!"); - stage = 10; - player.animate(ANIMATION); - return true; - } - interpreter.sendDialogues(captain, null, "So... how's the secret weapon coming along?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(witch, null, "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."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(witch, null, "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."); - stage = 4; - break; - case 4: - interpreter.sendDialogues(witch, null, "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!"); - 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!"); - stage = 7; - break; - case 7: - interpreter.sendDialogues(greldo, null, "Yeth, Mithreth."); - stage = 8; - break; - case 8: - player.getQuestRepository().getQuest("Black Knights' Fortress").setStage(player, 20); - end(); - break; - case 10: - interpreter.sendDialogues(captain, null, "What's that noise?"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(witch, null, "Hopefully Greldo with the cabbage... yes, look her it", "co....NOOOOOoooo!"); - stage = 12; - break; - case 12: - interpreter.sendDialogues(witch, null, "My potion!"); - stage = 13; - break; - case 13: - interpreter.sendDialogues(captain, null, "Oh boy, this doesn't look good!"); - stage = 14; - break; - case 14: - interpreter.sendDialogues(cat, null, "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."); - stage = 16; - } - break; - case 16: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 992752973 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/BanditCampBartenderDialogue.java b/Server/src/main/java/core/game/content/dialogue/BanditCampBartenderDialogue.java deleted file mode 100644 index 152a7bb12..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BanditCampBartenderDialogue.java +++ /dev/null @@ -1,52 +0,0 @@ -package core.game.content.dialogue; - -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the Bandit Camp Bartender dialogue - * @author Aero - * @version 1.0 - */ -@Initializable -public class BanditCampBartenderDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BanditCampBartender} {@code Object}. - */ - public BanditCampBartenderDialogue() { - /** - * Empty to stop instantiation. - */ - } - - /** - * Constructs a new {@code BanditCampBartender} {@code Object}. - * @param player The player to construct the class for. - */ - public BanditCampBartenderDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BanditCampBartenderDialogue(player); - } - - @Override - public boolean open(Object... args) { - - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1921 }; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/BankerTutorDialogue.java b/Server/src/main/java/core/game/content/dialogue/BankerTutorDialogue.java deleted file mode 100644 index 6d0b00d94..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BankerTutorDialogue.java +++ /dev/null @@ -1,154 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/BarkerDialogue.java b/Server/src/main/java/core/game/content/dialogue/BarkerDialogue.java deleted file mode 100644 index 1aacd65c7..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BarkerDialogue.java +++ /dev/null @@ -1,83 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the barker npc dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BarkerDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BarkerDialogue} {@code Object}. - */ - public BarkerDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BarkerDialogue} {@code Object}. - * @param player the player. - */ - public BarkerDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BarkerDialogue(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, "You are looking for clothes, yes? You look at my", "products! I have very many nice clothes, yes?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "Yes, please.", "No, thanks."); - stage = 2; - break; - case 2: - 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 20: - end(); - break; - case 10: - end(); - npc.openShop(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1039 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/BernaldDialogue.java b/Server/src/main/java/core/game/content/dialogue/BernaldDialogue.java deleted file mode 100644 index 9eeb1b769..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BernaldDialogue.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/BootDialogue.java b/Server/src/main/java/core/game/content/dialogue/BootDialogue.java deleted file mode 100644 index 50cb45987..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BootDialogue.java +++ /dev/null @@ -1,82 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used with the boot npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BootDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BootDialogue} {@code Object}. - */ - public BootDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BootDialogue} {@code Object}. - * @param player the player. - */ - public BootDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BootDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello tall person."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Hello short person.", "Why are you called boot?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello short person."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why are you called boot?"); - stage = 20; - break; - } - break; - case 10: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm called Boot, because when I was very young, I", "used to sleep, in a large boot."); - stage = 21; - break; - case 21: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 665 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/BreocaDialogue.java b/Server/src/main/java/core/game/content/dialogue/BreocaDialogue.java deleted file mode 100644 index 14faebce1..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BreocaDialogue.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/BrianArcheryDialogue.java b/Server/src/main/java/core/game/content/dialogue/BrianArcheryDialogue.java deleted file mode 100644 index c9a1f8ed1..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BrianArcheryDialogue.java +++ /dev/null @@ -1,79 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/BrosDialogue.java b/Server/src/main/java/core/game/content/dialogue/BrosDialogue.java deleted file mode 100644 index b50003353..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BrosDialogue.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/BurthorpeSoldierDialogue.java b/Server/src/main/java/core/game/content/dialogue/BurthorpeSoldierDialogue.java deleted file mode 100644 index 366d24e0a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/BurthorpeSoldierDialogue.java +++ /dev/null @@ -1,67 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/CallistoCubDialogue.java b/Server/src/main/java/core/game/content/dialogue/CallistoCubDialogue.java deleted file mode 100644 index 28bbb725e..000000000 --- a/Server/src/main/java/core/game/content/dialogue/CallistoCubDialogue.java +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, Calliso JR - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class CallistoCubDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - */ - public CallistoCubDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - * @param player the player. - */ - public CallistoCubDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CallistoCubDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why the grizzly face?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You're not funny..."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You should get in the.... sun more."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You're really not funny..."); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "One second, let me take a picture of you with my....", "kodiak camera."); - stage = 4; - break; - case 4: - npc(FacialExpression.OLD_NORMAL, "..."); - stage = 5; - break; - case 5: - player("Feeling... blue."); - stage = 6; - break; - case 6: - npc(FacialExpression.OLD_NORMAL, "If you don't stop, I'm going to leave some...", "brown... at your feet, human."); - stage = 7; - break; - case 7: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8597 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/CandleSellerPlugin.java b/Server/src/main/java/core/game/content/dialogue/CandleSellerPlugin.java deleted file mode 100644 index b2c8fd710..000000000 --- a/Server/src/main/java/core/game/content/dialogue/CandleSellerPlugin.java +++ /dev/null @@ -1,229 +0,0 @@ -package core.game.content.dialogue; - -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 candle seller. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CandleSellerPlugin extends DialoguePlugin { - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 1000); - - /** - * Represents the candle item. - */ - private static final Item CANDLE = new Item(33, 1); - - /** - * Constructs a new {@code CandleSellerPlugin} {@code Object}. - */ - public CandleSellerPlugin() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code CandleSellerPlugin} {@code Object}. - * @param player the player. - */ - public CandleSellerPlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CandleSellerPlugin(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?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes please.", "One thousand gold?!", "No thanks, I'd rather curse the darkness."); - stage = 1; - break; - case 800: - if (player.getInventory().freeSlots() == 0) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough inventory space to buy a candle."); - break; - } - if (!player.getInventory().contains(995, 1000)) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't seem to have enough coins."); - stage = 30; - break; - } - 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."); - stage = 400; - } - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please."); - stage = 800; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "One thousand gold?!"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - 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."); - 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!"); - stage = 23; - break; - case 23: - interpreter.sendOptions("Select an Option", "All right, you win, I'll buy a candle.", "No way.", "How do you make lanterns?"); - stage = 240; - break; - case 30: - end(); - break; - case 240: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "All right, you win, I'll buy a candle."); - stage = 350; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No way."); - stage = 30; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - 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."); - 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."); - 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."); - 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."); - 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!"); - 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!"); - stage = 237; - break; - case 237: - interpreter.sendOptions("Select an Option", "All right, you win, I'll buy a candle.", "No thanks, I'd rather curse the darkness."); - stage = 290; - break; - case 350: - if (player.getInventory().freeSlots() == 0) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough inventory space to buy a candle."); - break; - } - if (!player.getInventory().contains(995, 1000)) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't seem to have enough coins."); - stage = 30; - break; - } - - if (player.getInventory().remove(COINS)) { - player.getInventory().add(CANDLE); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - stage = 401; - break; - case 401: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, thanks."); - stage = 402; - break; - case 402: - end(); - break; - case 290: - switch (buttonId) { - case 1: - if (player.getInventory().freeSlots() == 0) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough inventory space to buy a candle."); - break; - } - if (!player.getInventory().contains(995, 1000)) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't seem to have enough coins."); - stage = 30; - break; - } - if (player.getInventory().remove(COINS)) { - player.getInventory().add(CANDLE); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here you go then."); - stage = 400; - } - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks, I'd rather curse the darkness."); - stage = 291; - break; - } - break; - case 291: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1834 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/CapnIzzyDialogue.java b/Server/src/main/java/core/game/content/dialogue/CapnIzzyDialogue.java deleted file mode 100644 index 807eac738..000000000 --- a/Server/src/main/java/core/game/content/dialogue/CapnIzzyDialogue.java +++ /dev/null @@ -1,262 +0,0 @@ -package core.game.content.dialogue; - -import core.game.content.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/java/core/game/content/dialogue/CaptainBarnabyDialogue.java b/Server/src/main/java/core/game/content/dialogue/CaptainBarnabyDialogue.java deleted file mode 100644 index aaa56d81d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/CaptainBarnabyDialogue.java +++ /dev/null @@ -1,97 +0,0 @@ -package core.game.content.dialogue; - -import core.game.content.global.travel.ship.Ships; -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/java/core/game/content/dialogue/CeolburgDialogue.java b/Server/src/main/java/core/game/content/dialogue/CeolburgDialogue.java deleted file mode 100644 index e4da19774..000000000 --- a/Server/src/main/java/core/game/content/dialogue/CeolburgDialogue.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/ChaosElementalJRDialogue.java b/Server/src/main/java/core/game/content/dialogue/ChaosElementalJRDialogue.java deleted file mode 100644 index e4b9d9230..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ChaosElementalJRDialogue.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, Chaos Ele JR. - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class ChaosElementalJRDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - */ - public ChaosElementalJRDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - * @param player the player. - */ - public ChaosElementalJRDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ChaosElementalJRDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Is it true a level 3 skiller caught one of your", "siblings?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Yes, they killed my mummy, kidnapped", "my brother, smiled about it and","went to sleep."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Aww, well you have me now!", "I shall call you Squishy and you shall be","mine and you shall be my Squishy."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Come on, Squishy come on, little Squishy!"); - stage = 3; - break; - case 3: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8595 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/CharlieTheTrampDialogue.java b/Server/src/main/java/core/game/content/dialogue/CharlieTheTrampDialogue.java deleted file mode 100644 index 0dc669e76..000000000 --- a/Server/src/main/java/core/game/content/dialogue/CharlieTheTrampDialogue.java +++ /dev/null @@ -1,258 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the charlie the tramp dialogue. - * @author 'Vexia - * @dtae 3/1/14 - */ -@Initializable -public class CharlieTheTrampDialogue extends DialoguePlugin { - - /** - * Represents the quest. - */ - private Quest quest; - - /** - * Constructs a new {@code CharlieTheTrampDialogue} {@code Object}. - */ - public CharlieTheTrampDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code CharlieTheTrampDialogue} {@code Object}. - * @param player the player. - */ - public CharlieTheTrampDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CharlieTheTrampDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Shield of Arrav"); - switch (quest.getStage(player)) { - default: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Spare some change guv?"); - stage = 0; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { - default: - regular(buttonId); - break; - } - return true; - } - - /** - * Method used to handle the regular dial. - * @param buttonId the buttonid. - */ - public void regular(int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Who are you?", "Sorry, I haven't got any.", "Go get a job!", "Ok. Here you go.", "Is there anything down this alleyway?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who are you?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I haven't got any."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Go get a job!"); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok. Here you go."); - stage = 40; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Is there anything down this alleyway?"); - stage = 50; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Charles. Charles E. Trampin' at your service. Now, about", "that change you were going to give me..."); - stage = 0; - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Thanks anyways!"); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You startin? I hope your nose falls off!"); - stage = 21; - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hey, thanks alot!"); - stage = 41; - break; - case 41: - interpreter.sendOptions("Select an Option", "No problem.", "Don't I get some sort of quest hint or something now?"); - stage = 42; - break; - case 42: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No problem."); - stage = 180; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So...don't I get some sort of quest hint or something now?"); - stage = 190; - break; - } - break; - case 180: - end(); - if (player.getInventory().contains(995, 1)) - player.getInventory().remove(new Item(995, 1)); - else - player.getPacketDispatch().sendMessage("You need one coin to give away."); - break; - case 190: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Huh? What do you mean? That wasn't why", " I asked you for money."); - stage = 191; - break; - case 191: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I just need to eat..."); - stage = 192; - break; - case 192: - end(); - break; - case 800: - npc("The ruthless and notorious criminal gang known as the", "Black Arm Gang have their headquarters down there."); - stage = 801; - break; - case 801: - options("Thank you for the warning!", "Do you think they would let me join?"); - stage = 802; - break; - case 802: - switch (buttonId) { - case 1: - player("Thanks for the warning!"); - stage = 803; - break; - case 2: - player("Do you think they would let me join?"); - stage = 804; - break; - } - break; - case 803: - npc("Don't worry about it."); - stage = 806; - break; - case 804: - npc("You never know. You'll find a lady down there called", "Katrine. Speak to her."); - stage = 807; - break; - case 807: - npc("But don't upset her, she's pretty dangerous."); - stage = 808; - break; - case 808: - npc("I also heard that Reldo the librarian", "knows more about them, go talk to him."); - stage = 809; - break; - case 809: - end(); - break; - case 806: - player("Do you think they would let me join?"); - stage = 804; - break; - case 50: - if (quest.getStage(player) == 0) { - npc("Funny you should mention that...there is actually."); - stage = 800; - break; - } - if (quest.getStage(player) == 30 || quest.getStage(player) == 40 || quest.getStage(player) == 50) { - npc("Funny you should mention that...there is actually."); - stage = 51; - break; - } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Nope, nothing at all."); - stage = 192; - break; - case 51: - npc("The ruthless and notorious criminal gang known as the", "Black Arm Gang have their headquarters down there."); - stage = 52; - break; - case 52: - options("Thank you for the warning!", "Do you think they would let me join?"); - stage = 53; - break; - case 53: - switch (buttonId) { - case 1: - player("Thanks for the warning!"); - stage = 54; - break; - case 2: - player("Do you think they would let me join?"); - stage = 55; - break; - } - break; - case 54: - npc("Don't worry about it."); - stage = 56; - break; - case 55: - npc("You never know. You'll find a lady down there called", "Katrine. Speak to her."); - stage = 57; - break; - case 57: - npc("But don't upset her, she's pretty dangerous."); - stage = 58; - break; - case 58: - quest.setStage(player, 50); - end(); - break; - case 56: - player("Do you think they would let me join?"); - stage = 55; - break; - } - } - - @Override - public int[] getIds() { - return new int[] { 641 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/CrossbowDialogue.java b/Server/src/main/java/core/game/content/dialogue/CrossbowDialogue.java deleted file mode 100644 index 143002bcd..000000000 --- a/Server/src/main/java/core/game/content/dialogue/CrossbowDialogue.java +++ /dev/null @@ -1,116 +0,0 @@ -/* -package core.game.content.dialogue; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.fletching.items.crossbow.CrossbowPulse; -import core.game.node.entity.skill.fletching.items.crossbow.StringCross; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.entity.player.link.RunScript; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the dialogue plugin used for the crossbow. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class CrossbowDialogue extends DialoguePlugin { - - */ -/** - * Represents the item used. - *//* - - private Item item; - - */ -/** - * The string cross. - *//* - - private StringCross cross; - - */ -/** - * Constructs a new {@code CrossbowDialogue} {@code Object}. - *//* - - public CrossbowDialogue() { - */ -/** - * empty. - *//* - - } - - */ -/** - * Constructs a new {@code CrossbowDialogue.java} {@code Object}. - * @param player - *//* - - public CrossbowDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CrossbowDialogue(player); - } - - @Override - public boolean open(Object... args) { - item = (Item) args[0]; - cross = StringCross.forItem(item); - if (cross == null) { - return true; - } - player.getInterfaceManager().openChatbox(309); - player.getPacketDispatch().sendString("



" + cross.getProduct().getName(), 309, 6); - player.getPacketDispatch().sendItemZoomOnInterface(cross.getProduct().getId(), 160, 309, 2); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - int amt = 0; - switch (buttonId) { - case 6: - amt = 1; - break; - case 5: - amt = 5; - break; - case 4: - player.setAttribute("runscript", new RunScript() { - @Override - public boolean handle() { - int ammount = (int) value; - player.getPulseManager().run(new CrossbowPulse(player, item, cross, ammount)); - return false; - } - }); - player.getDialogueInterpreter().sendInput(false, "Enter the amount"); - return true; - case 3: - amt = player.getInventory().getAmount(item); - break; - } - player.getPulseManager().run(new CrossbowPulse(player, item, cross, amt)); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 92747 }; - } -} -*/ diff --git a/Server/src/main/java/core/game/content/dialogue/DSChestDialogue.java b/Server/src/main/java/core/game/content/dialogue/DSChestDialogue.java deleted file mode 100644 index aade2c81c..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DSChestDialogue.java +++ /dev/null @@ -1,77 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItemManager; - -import core.game.content.quest.free.dragonslayer.DragonSlayer; - -/** - * Represents the dialogue plugin used for the dragon slayer chest. - * @author 'Vexia - * @version 1.0 - */ -public final class DSChestDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code DSChestDialogue} {@code Object}. - */ - public DSChestDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DSChestDialogue} {@code Object}. - * @param player the player. - */ - public DSChestDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DSChestDialogue(player); - } - - @Override - public boolean open(Object... args) { - interpreter.sendDialogue("As you open the chest, you notice an inscription on the lid:"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogue("Here I rest the map to my beloved home. To whoever finds it, I beg", "of you, let it be. I was honour-bound not to destroy the map piece,", "but I have used all my magical skill to keep it from being recovered."); - 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."); - stage = 2; - break; - case 2: - interpreter.sendDialogue("I cannot stop you from taking this map piece now, but think on this:", "if you can slay the Dragon of Crandor, you are a greater hero than", "my land ever produced. There is no shame in backing out now."); - stage = 3; - break; - case 3: - interpreter.sendItemMessage(DragonSlayer.MAGIC_PIECE.getId(), "You find a map piece in the chest."); - stage = 4; - break; - case 4: - if (!player.getInventory().add(DragonSlayer.MAGIC_PIECE)) { - GroundItemManager.create(DragonSlayer.MAGIC_PIECE, player); - } - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3802875 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/DartCreateDialogue.java b/Server/src/main/java/core/game/content/dialogue/DartCreateDialogue.java deleted file mode 100644 index 061f4fc4c..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DartCreateDialogue.java +++ /dev/null @@ -1,105 +0,0 @@ -/* -package core.game.content.dialogue; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.fletching.items.darts.Dart; -import core.game.node.entity.skill.fletching.items.darts.DartPulse; -import org.crandor.game.node.entity.player.Player; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the dart creating dialogue. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class DartCreateDialogue extends DialoguePlugin { - - */ -/** - * Represents the first item parameter. - *//* - - private Item item; - - */ -/** - * Represents the second item parameter. - *//* - - private Item second; - - */ -/** - * Represents the dark. - *//* - - private Dart dart; - - */ -/** - * Constructs a new {@code DartCreateDialogue} {@code Object}. - *//* - - public DartCreateDialogue() { - */ -/** - * empty. - *//* - - } - - */ -/** - * Constructs a new {@code DartCreateDialogue} {@code Object}. - * @param player the player. - *//* - - public DartCreateDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DartCreateDialogue(player); - } - - @Override - public boolean open(Object... args) { - item = (Item) args[0]; - second = (Item) args[1]; - dart = Dart.forItem(item.getName().toLowerCase().contains("feather") ? second : item); - player.getInterfaceManager().openChatbox(582); - player.getPacketDispatch().sendString("



" + dart.getProduct().getName(), 582, 5); - player.getPacketDispatch().sendItemZoomOnInterface(dart.getProduct().getId(), 160, 582, 2); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - end(); - int amount = 0; - switch (buttonId) { - case 4: - amount = 5; - break; - case 5: - amount = 1; - break; - case 3: - amount = 10; - break; - } - player.getPulseManager().run(new DartPulse(player, item, dart, amount)); - return true; - } - - @Override - public int[] getIds() { - return new int[] { 328933 }; - } -} -*/ diff --git a/Server/src/main/java/core/game/content/dialogue/DavonDialogue.java b/Server/src/main/java/core/game/content/dialogue/DavonDialogue.java deleted file mode 100644 index 1ec2a7140..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DavonDialogue.java +++ /dev/null @@ -1,94 +0,0 @@ -package core.game.content.dialogue; - -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 davon npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DavonDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code DavonDialogue} {@code Object}. - */ - public DavonDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DavonDialogue} {@code Object}. - * @param player the player. - */ - public DavonDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DavonDialogue(player); - } - - @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."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "What are you selling?", "What do you mean pssst?", "Why don't you ever restock some types of amulets?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What are you selling?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - stage = 30; - break; - } - break; - case 10: - end(); - npc.openShop(player); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Errr, I was...I was clearing my throat."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Some of these amulets are very hard to get. I have to", "wait until an adventurer supplies me."); - stage = 31; - break; - case 31: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 588 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/DenulthDialogue.java b/Server/src/main/java/core/game/content/dialogue/DenulthDialogue.java deleted file mode 100644 index 453b01d3f..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DenulthDialogue.java +++ /dev/null @@ -1,76 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/DestroyItemPlugin.java b/Server/src/main/java/core/game/content/dialogue/DestroyItemPlugin.java deleted file mode 100644 index 9825f1231..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DestroyItemPlugin.java +++ /dev/null @@ -1,74 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/DialogueInterpreter.java b/Server/src/main/java/core/game/content/dialogue/DialogueInterpreter.java deleted file mode 100644 index 577ab2d8c..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DialogueInterpreter.java +++ /dev/null @@ -1,637 +0,0 @@ -package core.game.content.dialogue; - -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.NPCDefinition; -import core.game.component.Component; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -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.system.script.ScriptContext; -import core.game.system.script.ScriptManager; -import core.game.system.script.context.*; -import core.net.packet.PacketRepository; -import core.net.packet.context.ContainerContext; -import core.net.packet.out.ContainerPacket; -import core.plugin.PluginManifest; -import core.plugin.PluginType; -import rs09.game.content.dialogue.DialogueFile; -import rs09.game.content.dialogue.EmptyPlugin; -import rs09.game.system.SystemLogger; -import rs09.game.system.config.ItemConfigParser; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static rs09.tools.DialogueConstKt.END_DIALOGUE; - - -/** - * Handles the dialogues. - * @author Emperor - */ -@PluginManifest(type = PluginType.DIALOGUE) -public final class DialogueInterpreter { - - /** - * The dialogue plugins. - */ - private static final Map PLUGINS = new HashMap<>(); - - /** - * The dialogue scripts. - */ - private static final Map SCRIPTS = new HashMap<>(); - - /** - * a List of dialogue actions. - */ - private final List actions = new ArrayList<>(10); - - /** - * The currently opened dialogue. - */ - private DialoguePlugin dialogue; - - /** - * Scripted dialogue current stage. - */ - private ScriptContext dialogueStage; - - /** - * The current dialogue key. - */ - private int key; - - /** - * The player. - */ - private final Player player; - - /** - * Constructs a new {@code DialogueInterpreter} {@code Object}. - * @param player The player. - */ - public DialogueInterpreter(Player player) { - this.player = player; - } - - /** - * @param dialogue the dialogue to set. - */ - public void setDialogue(DialoguePlugin dialogue) { - this.dialogue = dialogue; - } - - /** - * Opens the dialogue for the given dialogue type. - * @param dialogueType The dialogue type. - * @param args the args. - * @return {@code True} if successful. - */ - public boolean open(String dialogueType, Object... args) { - return open(getDialogueKey(dialogueType), args); - } - - /** - * Opens the dialogue for the given NPC id. - * @param dialogueKey The dialogue key (usually NPC id). - * @param args The arguments. - * @return {@code True} if successful. - */ - public boolean open(int dialogueKey, Object... args) { - key = dialogueKey; - if (args.length > 0 && args[0] instanceof NPC) { - NPC npc = (NPC) args[0]; - npc.setDialoguePlayer(player); - try { //this prevents the dialogue interpreter from dying when you try opening an NPC's dialogue without passing the NPC itself as an argument - npc.getWalkingQueue().reset(); - npc.getPulseManager().clear(); - } catch(Exception e){ - e.printStackTrace(); - } - } else if (args.length < 1) { - args = new Object[] { dialogueKey }; - } - ScriptContext script = SCRIPTS.get(dialogueKey); - if (script != null) { - Object[] arguments = new Object[args.length + 1]; - for (int i = 0; i < args.length; i++) { - arguments[i + 1] = args[i]; - } - arguments[0] = player; - startScript(script, arguments); - return true; - } - DialoguePlugin plugin = PLUGINS.get(dialogueKey); - if (plugin == null) { - return false; - } - if (player.isDebug()) { - player.sendMessage("Dialogue opening - " + plugin.getClass().getSimpleName() + ", key=" + dialogueKey + ""); - } - this.dialogue = plugin.newInstance(player); - if (dialogue == null || !dialogue.open(args)) { - dialogue = null; - return false; - } - return true; - } - - public void open(DialogueFile file, Object... args){ - this.dialogue = new EmptyPlugin(player,file); - this.dialogue.open(args); - } - - /** - * Starts a dialogue script. - * @param script The script. - * @param args The arguments. - */ - public void startScript(ScriptContext script, Object... args) { - startScript(key, script, args); - } - - /** - * Starts a dialogue script. - * @param dialogueKey The dialogue key. - * @param script The script. - * @param args The arguments. - */ - public void startScript(int dialogueKey, ScriptContext script, Object... args) { - key = dialogueKey; - (dialogueStage = script).execute(args); - if (script.isInstant()) { - dialogueStage = script = ScriptManager.run(script, args); - } - } - - /** - * Handles an dialogue input. - * @param componentId The id of the chatbox component. - * @param buttonId The button id. - */ - public void handle(int componentId, int buttonId) { - if (dialogueStage != null) { - dialogueStage = ScriptManager.run(dialogueStage, player, key, buttonId); - if (!(dialogueStage instanceof PDialInstruction || dialogueStage instanceof NPCDialInstruction || dialogueStage instanceof OptionDialInstruction || dialogueStage instanceof PlainMessageInstruction || dialogueStage instanceof ItemMessageInstruction)) { - player.getInterfaceManager().closeChatbox(); - } - return; - } - player.setAttribute("chatbox-buttonid",buttonId); - if((player.getDialogueInterpreter().getDialogue().file != null && player.getDialogueInterpreter().getDialogue().file.getCurrentStage() == END_DIALOGUE) || player.getDialogueInterpreter().getDialogue().stage == END_DIALOGUE){ - player.getInterfaceManager().closeChatbox(); - player.getInterfaceManager().openChatbox(137); - player.getDialogueInterpreter().dialogue.finished = true; - close(); - return; - } - if(player.getDialogueInterpreter().getDialogue().file != null){ - player.getDialogueInterpreter().getDialogue().file.handle(componentId,buttonId - 1); - return; - } - player.getDialogueInterpreter().getDialogue().handle(componentId, buttonId - 1);//here - } - - /** - * Closes the current dialogue. - * @return {@code True} if successful. - */ - public boolean close() { - if (dialogue != null || dialogueStage != null) { - actions.clear(); - - if (player.getInterfaceManager().getChatbox() != null && player.getInterfaceManager().getChatbox().getCloseEvent() != null) { - return true; - } - if (dialogueStage != null) { - dialogueStage = null; - player.getInterfaceManager().closeChatbox(); - } - if (dialogue != null) { - DialoguePlugin d = dialogue; - dialogue = null; - d.close(); - } - } - return dialogue == null && dialogueStage == null; - } - - /** - * Puts a dialogue plugin on the mapping. - * @param id The NPC id (or {@code 1 << 16 | dialogueId} when the dialogue - * isn't for an NPC). - * @param plugin The plugin. - */ - public static void add(int id, DialoguePlugin plugin) { - if (PLUGINS.containsKey(id)) { - throw new IllegalArgumentException("Dialogue " + (id & 0xFFFF) + " is already in use - [old=" + PLUGINS.get(id).getClass().getSimpleName() + ", new=" + plugin.getClass().getSimpleName() + "]!"); - } - PLUGINS.put(id, plugin); - } - - /** - * Adds a dialogue script for the given key. - * @param dialogueKey The dialogue key. - * @param context The dialogue script. - */ - public static void add(int dialogueKey, ScriptContext context) { - if (SCRIPTS.containsKey(dialogueKey)) { - // throw new IllegalArgumentException("Dialogue " + dialogueKey + - // " is already in use - [old=" + - // SCRIPTS.get(dialogueKey).getClass().getSimpleName() + ", new=" + - // context.getClass().getSimpleName() + "]!"); - } - SCRIPTS.put(dialogueKey, context); - } - - /** - * Gets the script context for the given dialogue key. - * @param key The dialogue key. - * @return The script context. - */ - public static ScriptContext getScript(int key) { - return SCRIPTS.get(key); - } - - /** - * Send plain messages based on the amount of specified messages. - * @param messages The messages. - * @return The chatbox component. - */ - public Component sendDialogue(String... messages) { - if (messages.length < 1 || messages.length > 4) { - return null; - } - int interfaceId = 209 + messages.length; - for (int i = 0; i < messages.length; i++) { - player.getPacketDispatch().sendString(messages[i], interfaceId, i + 1); - } - player.getInterfaceManager().openChatbox(interfaceId); - if (player.getAttribute("tut-island", false)) { - - } - player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), 1, false); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Sends a plane message and hides the continue button. - * @param hideContinue if we should hide it or not. - * @param messages the messages. - * @return the component. - */ - public Component sendPlainMessage(final boolean hideContinue, String... messages) { - sendDialogue(messages); - player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), (messages.length + 1), hideContinue); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Opens the destroy item chatbox interface. - * @param id The item id. - * @param message The message to display. - * @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(" "); - StringBuilder sb = new StringBuilder(words[0]); - for (int i = 1; i < words.length; i++) { - if (i == (words.length / 2)) { - sb.append("
"); - } else { - sb.append(" "); - } - sb.append(words[i]); - } - 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); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send plane messages with a blue title. - * @param title The title. - * @param messages The messages. - * @return The chatbox component. - */ - public Component sendPlaneMessageWithBlueTitle(String title, String... messages) { - player.getPacketDispatch().sendString(title, 372, 0); - for (int i = 0; i < messages.length; i++) { - player.getPacketDispatch().sendString(messages[i], 372, i + 1); - } - player.getInterfaceManager().openChatbox(372); - if (player.getAttributes().containsKey("tut-island") || TutorialSession.getExtension(player).getStage() < TutorialSession.MAX_STAGE) { - } - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send plane messages with scroll and a blue title. - * @param title The title. - * @param messages The messages. - * @return The chatbox component. - */ - public Component sendScrollMessageWithBlueTitle(String title, String... messages) { - for (int i = 0; i < 11; i++) { - player.getPacketDispatch().sendString(" ", 421, i + 2); - } - player.getPacketDispatch().sendString(title, 421, 1); - for (int i = 0; i < messages.length; i++) { - player.getPacketDispatch().sendString(messages[i], 421, i + 2); - } - player.getInterfaceManager().openChatbox(421); - if (player.getAttributes().containsKey("tut-island")) { - } - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send a message with an item next to it. - * @param itemId The item id. - */ - public Component sendItemMessage(int itemId, String... messages) { - player.getInterfaceManager().openChatbox(131); - String message = messages[0]; - for (int i = 1; i < messages.length; i++) { - message += "
" + messages[i]; - } - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, 131, 2); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send a message with an item next to it. - */ - public Component sendItemMessage(final Item item, String... messages) { - player.getInterfaceManager().openChatbox(131); - String message = messages[0]; - for (int i = 1; i < messages.length; i++) { - message += "
" + messages[i]; - } - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(item.getId(), item.getAmount(), 131, 2); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send a message with an item next to it. - * @param message The message. - */ - public Component sendDoubleItemMessage(int first, int second, String message) { - player.getInterfaceManager().openChatbox(131); - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(first, 1, 131, 0); - player.getPacketDispatch().sendItemOnInterface(second, 1, 131, 2); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send a message with an item next to it. - * @param message The message. - */ - 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); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send dialogues based on the amount of specified messages. - * @param entity The entity. - * @param expression The entity's facial expression. - * @param messages The messages. - * @return The chatbox component. - */ - public Component sendDialogues(Entity entity, FacialExpression expression, String... messages) { - return sendDialogues(entity, expression == null ? -1 : expression.getAnimationId(), messages); - } - - /** - * Send dialogues based on the amount of specified messages. - * @param entity The entity. - * @param expression The entity's facial expression. - * @param messages The messages. - * @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); - } - - /** - * 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 the continue. - * @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(); - } - - /** - * Send dialogues based on the amount of specified messages. - * @param expression The entity's facial expression. - * @param messages The messages. - * @param hide the continue. - * @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(); - } - - /** - * Send dialogues based on the amount of specified messages. - * @param expression The entity's facial expression. - * @param messages The messages. - * @param hide the continue. - * @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(); - } - - /** - * 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. - * @return The chatbox component. - */ - public Component sendDialogues(int npcId, FacialExpression expression, String... messages) { - return sendDialogues(npcId, expression == null ? -1 : expression.getAnimationId(), 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. - * @return The chatbox component. - */ - public Component sendDialogues(int npcId, int expression, String... messages) { - if (messages.length < 1 || messages.length > 4) { - return null; - } - boolean npc = npcId > -1; - int interfaceId = (npc ? 240 : 63) + messages.length; - if (expression == -1) { - expression = FacialExpression.HALF_GUILTY.getAnimationId(); - } - player.getPacketDispatch().sendAnimationInterface(expression, interfaceId, 2); - if (npc) { - player.getPacketDispatch().sendNpcOnInterface(npcId, interfaceId, 2); - player.getPacketDispatch().sendString(NPCDefinition.forId(npcId).getName(), interfaceId, 3); - } else { - 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.getInterfaceManager().openChatbox(interfaceId); - if (player.getAttributes().containsKey("tut-island") || TutorialSession.getExtension(player).getStage() < TutorialSession.MAX_STAGE) { - } - player.getPacketDispatch().sendInterfaceConfig(player.getInterfaceManager().getChatbox().getId(), 3, false); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send options based on the amount of specified options. - * @param title The title. - * @param options The options. - */ - public Component sendOptions(Object title, String... options) { - int interfaceId = 224 + (2 * options.length); - if (options.length < 2 || options.length > 5) { - return null; - } - if (title != null) { - player.getPacketDispatch().sendString(title.toString(), interfaceId, 1); - } - for (int i = 0; i < options.length; i++) { - player.getPacketDispatch().sendString(options[i].toString(), interfaceId, i + 2); - } - if (player.getAttributes().containsKey("tut-island")) { - } - player.getInterfaceManager().openChatbox(interfaceId); - return player.getInterfaceManager().getChatbox(); - } - - /** - * Send a input run script. - * @param string The strings. - * @param objects The arguments. - */ - public void sendInput(boolean string, Object... objects) { - player.getPacketDispatch().sendRunScript(string ? 109 : 108, "s", objects); - } - - /** - * Sends a long input. - * @param objects the objects. - */ - public void sendLongInput(Object... objects) { - player.getPacketDispatch().sendRunScript(110, "s", objects); - } - - /** - * Sends the private message input. - * @param reciever The receiver. - */ - public void sendMessageInput(String reciever) { - player.getPacketDispatch().sendRunScript(107, "s", reciever); - } - - /** - * Checks if the dialogue for the given id is added. - * @param id The NPC id/dialogue id. - * @return {@code True} if so. - */ - public static boolean contains(int id) { - return PLUGINS.containsKey(id); - } - - /** - * Gets the currently opened dialogue. - * @return The dialogue plugin. - */ - public DialoguePlugin getDialogue() { - return dialogue; - } - - /** - * Reserves a key for the name. - * @param name The name. - * @return The key. - */ - public static int getDialogueKey(String name) { - return 1 << 16 | name.hashCode(); - } - - /** - * Gets the dialogueStage. - * @return The dialogueStage. - */ - public ScriptContext getDialogueStage() { - return dialogueStage; - } - - /** - * Sets the dialogueStage. - * @param dialogueStage The dialogueStage to set. - */ - public void setDialogueStage(ScriptContext dialogueStage) { - this.dialogueStage = dialogueStage; - } - - /** - * Adds a dialogue reward. - * @param action the reward. - */ - public void addAction(DialogueAction action) { - actions.add(action); - } - - /** - * Gets the actions. - * @return The actions. - */ - public List getActions() { - return actions; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/DiangoDialogue.java b/Server/src/main/java/core/game/content/dialogue/DiangoDialogue.java deleted file mode 100644 index 37d5c6c2d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DiangoDialogue.java +++ /dev/null @@ -1,107 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used by diango. - * @author 'Vexia - * @version 1.1 - */ -@Initializable -public final class DiangoDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code DiangoDialogue {@code Object}. - */ - public DiangoDialogue() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code DiangoDialogue} {@code Object}. - * @param player the player. - */ - public DiangoDialogue(Player player) { - super(player); - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("Spinning plates?", "I'd like to check holiday items please!", "I'd like to claim purchased cosmetics."); - stage++; - break; - case 1: - switch (buttonId) { - case 1: - player("Spinning plates?"); - stage = 10; - break; - case 2: - player("I'd like to check holiday items please?"); - stage = 20; - break; - case 3: - player("I'd like to claim purchased cosmetics."); - stage = 30; - break; - } - 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"); - 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!"); - stage = 12; - break; - case 12: - end(); - npc.openShop(player); - break; - case 20: - npc("Sure thing, let me just see what you're missing."); - stage++; - break; - case 21: - openItemReturn(player); - end(); - break; - case 30: - end(); - break; - } - return true; - } - - /** - * Opens the item return. - * @param player the player. - */ - private void openItemReturn(Player player) { - - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DiangoDialogue(player); - } - - @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?"); - stage = 0; - return true; - } - - @Override - public int[] getIds() { - return new int[] { 970 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/DommikDialogue.java b/Server/src/main/java/core/game/content/dialogue/DommikDialogue.java deleted file mode 100644 index 566f8f9b7..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DommikDialogue.java +++ /dev/null @@ -1,70 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/DonieDialogue.java b/Server/src/main/java/core/game/content/dialogue/DonieDialogue.java deleted file mode 100644 index 6ce8ea051..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DonieDialogue.java +++ /dev/null @@ -1,127 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import rs09.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/java/core/game/content/dialogue/DoricDialogue.java b/Server/src/main/java/core/game/content/dialogue/DoricDialogue.java deleted file mode 100644 index c5601462a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DoricDialogue.java +++ /dev/null @@ -1,259 +0,0 @@ -package core.game.content.dialogue; - -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 rs09.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/java/core/game/content/dialogue/DragonShieldDialogue.java b/Server/src/main/java/core/game/content/dialogue/DragonShieldDialogue.java deleted file mode 100644 index ee016d005..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DragonShieldDialogue.java +++ /dev/null @@ -1,121 +0,0 @@ -package core.game.content.dialogue; - -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; - -/** - * Represents the dialogue plugin used for making a dragon shield. - * @author 'Vexia - * @version 1.0 - */ -@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}. - */ - public DragonShieldDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DragonShieldDialogue} {@code Object}. - * @param player the player. - */ - public DragonShieldDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DragonShieldDialogue(player); - } - - @Override - public boolean open(Object... args) { - 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."); - stage = 0; - } else { - 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; - } - return true; - } - - @Override - 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); - } - 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); - } - player.getSkills().addExperience(Skills.SMITHING, 2000); - stage = 1; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 82127843 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/DunstanDialogue.java b/Server/src/main/java/core/game/content/dialogue/DunstanDialogue.java deleted file mode 100644 index 552ac6c6e..000000000 --- a/Server/src/main/java/core/game/content/dialogue/DunstanDialogue.java +++ /dev/null @@ -1,98 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/EadburgDialogue.java b/Server/src/main/java/core/game/content/dialogue/EadburgDialogue.java deleted file mode 100644 index 67954f3f2..000000000 --- a/Server/src/main/java/core/game/content/dialogue/EadburgDialogue.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/EblisDialogue.java b/Server/src/main/java/core/game/content/dialogue/EblisDialogue.java deleted file mode 100644 index fbeacdc74..000000000 --- a/Server/src/main/java/core/game/content/dialogue/EblisDialogue.java +++ /dev/null @@ -1,106 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/EmeraldBenedictDialogue.java b/Server/src/main/java/core/game/content/dialogue/EmeraldBenedictDialogue.java deleted file mode 100644 index 558ec7e5a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/EmeraldBenedictDialogue.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the emerald benedict dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class EmeraldBenedictDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code EmeraldBenedictDialogue} {@code Object}. - */ - public EmeraldBenedictDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code EmeraldBenedictDialogue} {@code Object}. - * @param player the player. - */ - public EmeraldBenedictDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new EmeraldBenedictDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Got anything you don't want to lose?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes actually, can you help?", "Yes, but can you show me my PIN settings?", "Yes thanks, and I'll keep hold of it too."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - end(); - player.getBank().open(); - break; - case 2: - end(); - break; - case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes thanks, and I'll keep hold of it too."); - stage = 99; - break; - } - break; - case 99: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2271 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/EventLocatorDialoguePlugin.java b/Server/src/main/java/core/game/content/dialogue/EventLocatorDialoguePlugin.java deleted file mode 100644 index 4925b502d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/EventLocatorDialoguePlugin.java +++ /dev/null @@ -1,275 +0,0 @@ -package core.game.content.dialogue; - -import rs09.ServerConstants; -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.world.map.Location; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * Package -> core.game.content.dialogue - * Created on -> 9/7/2016 @10:33 PM for 530 - * - * @author Ethan Kyle Millard - */ -@Initializable -public class EventLocatorDialoguePlugin extends DialoguePlugin { - - private String eventName; - - private HashMap GOLDEN_ESSENCE = new HashMap<>(), THIEVES_JACKPOT = new HashMap<>(), HARVESTING_DOUBLES = new HashMap<>(), ALCHEMY_HELLENISTIC = new HashMap<>(); - - - private Map> HOURLY_EVENTS = new HashMap<>(); - - - public EventLocatorDialoguePlugin() { - - } - - public EventLocatorDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new EventLocatorDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - fill(); - eventName = (String) args[0]; - System.out.println(eventName); - if (eventName == null) { - player.sendMessage("There no events active currently."); - return false; - } - for (Map.Entry> entry : HOURLY_EVENTS.entrySet()) { - for (Map.Entry values : entry.getValue().entrySet()) { - for (int i = 0; i < values.getKey().length; i ++) - System.out.println("Location Index -> " + getIndex(values.getKey(), i) + " for Location -> " + values.getKey()); - switch (eventName) { - case "Alchemy hellenistic": - if (entry.getKey().equalsIgnoreCase(eventName)) { - options(values.getValue()); - stage = 0; - } - break; - case "Harvesting doubles": - if (entry.getKey().equalsIgnoreCase(eventName)) { - options("Mining", "Woodcutting", "Fishing"); - stage = 1; - } - break; - case "Thieves jackpot": - if (entry.getKey().equalsIgnoreCase(eventName)) { - options(values.getValue()); - stage = 5; - } - break; - case "Golden essence": - if (entry.getKey().equalsIgnoreCase(eventName)) { - options(values.getValue()); - stage = 6; - } - break; - } - } - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - for (Map.Entry> entry : HOURLY_EVENTS.entrySet()) { - for (Map.Entry values : entry.getValue().entrySet()) {//Minning - switch (stage) { - case 0: - switch (buttonId) { - case 1: - player.teleport(values.getKey()[2]);//Home - end(); - break; - case 2: - player.teleport(values.getKey()[3]);//Home - end(); - break; - } - break; - case 1: - switch (buttonId) { - case 1: - options(Arrays.copyOfRange(values.getValue(), 4, 5)); - stage = 2; - break; - case 2: - options(Arrays.copyOfRange(values.getValue(), 6, 7)); - stage = 3; - break; - case 3: - options(Arrays.copyOfRange(values.getValue(), 8, 10)); - stage = 4; - break; - } - break; - case 2: - switch (buttonId) { - case 1: - player.teleport(values.getKey()[4]);//Home - end(); - break; - case 2: - player.teleport(values.getKey()[5]);//Home - end(); - break; - } - break; - case 3: - switch (buttonId) { - case 1: - player.teleport(values.getKey()[6]);//Home - end(); - break; - case 2: - player.teleport(values.getKey()[7]);//Home - end(); - break; - } - break; - case 4: - switch (buttonId) { - case 1: - player.teleport(values.getKey()[8]);//Home - end(); - break; - case 2: - if (player.getSkills().getStaticLevel(Skills.FISHING) < 68) { - interpreter.sendDialogue("You must have at least 68 fishing to access this event location."); - stage = -2; - } else { - player.teleport(values.getKey()[9]);//Home - end(); - } - break; - case 3: - player.teleport(values.getKey()[10]);//Home - end(); - break; - } - break; - case 5: - switch (buttonId) { - case 1: - player.teleport(values.getKey()[11]);//Home - end(); - break; - case 2: - player.teleport(values.getKey()[12]);//Home - end(); - break; - } - break; - case 6: - switch (buttonId) { - case 1: - if (player.getQuestRepository().getQuest("Rune Mysteries").isCompleted(player)) { - player.teleport(values.getKey()[0]);//Home - end(); - } else { - interpreter.sendDialogue("You must have completed Rune Mysteries to access this", "event locator."); - stage = -2; - } - break; - case 2: - if (player.getQuestRepository().getQuest("Rune Mysteries").isCompleted(player) && player.getSkills().getLevel(Skills.MAGIC) >= 66) { - player.teleport(values.getKey()[1]);//Home - end(); - } else { - interpreter.sendDialogue("You must have completed Rune Mysteries and have a magic level of 66", " to access this event locator."); - stage = -2; - } - break; - } - break; - case -2: - end(); - break; - } - } - } - return false; - } - - private void fill() { - ALCHEMY_HELLENISTIC.put( - new Location[]{ - ServerConstants.HOME_LOCATION, - new Location(3164, 3485, 0) - }, - new String[]{ - "Home", "Grand Exchange" - }); - HARVESTING_DOUBLES.put( - new Location[]{ - new Location(3228, 3147, 0),//lumby - new Location(3184, 3374, 0),//varrock - new Location(2730, 3485, 0),//seers - new Location(2776, 3437, 0),//catherby yews - new Location(2842, 3440, 0),//catherby fish - new Location(2596, 3414, 0),//fishing guild - new Location(2338, 3701, 0)//pfc - }, - new String[]{ - //Mining Locations - "Lumbridge Swamp Mine (Copper, Tin)", "Varrock Mining Site (Clay, Iron, Tin)",//add rock names - //Woodcutting Locations - "Seers Village Forest (Normal, Oak, Maple, Yew)", "Catherby Yew Forest (Normal, Willow, Yew)",//add tree names - //Fishing Locations - "Catherby Fishing spot (Lobster, Tuna/Swordfish, Shark)", "Fishing Guild (Lobster, Tuna/Swordfish, Shark)", "Piscatoris Fishing Colony (Monkfish)"//Add fish names - }); - GOLDEN_ESSENCE.put( - new Location[] { - new Location(3104, 9571, 0), - new Location(2591, 3086, 0) - }, - new String[] { - "Wizards Tower Essence Mine", - "Magic Guild Essence Mine", - }); - THIEVES_JACKPOT.put( - new Location[]{ - new Location(3231, 3238, 0), - new Location(3154, 9646, 0) - }, - new String[]{ - "Lumbridge men", - "H.A.M Outlet" - }); - - HOURLY_EVENTS.put("Alchemy hellenistic", ALCHEMY_HELLENISTIC); - HOURLY_EVENTS.put("Harvesting Doubles", HARVESTING_DOUBLES); - HOURLY_EVENTS.put("Thieves jackpot", THIEVES_JACKPOT); - HOURLY_EVENTS.put("Golden essence", GOLDEN_ESSENCE); - } - - private int getIndex(Location[] loc, int value) { - for (int i = 0; i < loc.length; i ++) { - if (loc[i].getIndex() == value) { - return i; - } - } - return 0; - } - - @Override - public int[] getIds() { - return new int[] { - 175869 - }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/FacialExpression.java b/Server/src/main/java/core/game/content/dialogue/FacialExpression.java deleted file mode 100644 index 8ecf1ed26..000000000 --- a/Server/src/main/java/core/game/content/dialogue/FacialExpression.java +++ /dev/null @@ -1,127 +0,0 @@ -package core.game.content.dialogue; - -/** - * Represents the facial expressions (the animations the entity does when - * talking). - * - * @author Emperor - * @author Empathy - */ -public enum FacialExpression { - - /** - * The normal talking expression. - */ - - //Names based off https://2009scape.wiki/w/Chathead/Animations - - //Chat heads from oldschool 2009scape? - //Maybe for gnomes or dwarves? Chat heads are frozen when used on Human NPCs - OLD_HAPPY(588), - OLD_CALM_TALK1(589), - OLD_CALM_TALK2(590), - OLD_DEFAULT(591), - OLD_EVIL1(592), - OLD_EVIL2(593), - OLD_NORMAL(594), - OLD_SNEAKY(595), - OLD_DISTRESSED(596), - OLD_DISTRESSED2(597), - OLD_ALMOST_CRYING(598), - OLD_BOWS_HEAD_SAD(599), - OLD_DRUNK_LEFT(600), - OLD_DRUNK_RIGHT(601), - OLD_NOT_INTERESTED(602), - OLD_SLEEPY(603), - OLD_PLAIN_EVIL(604), - OLD_LAUGH1(605), - OLD_LAUGH2(606), - OLD_LAUGH3(607), - OLD_LAUGH4(608), - OLD_EVIL_LAUGH(609), - OLD_SAD(610), - OLD_MORE_SAD(611), - OLD_ON_ONE_HAND(612), - OLD_NEARLY_CRYING(613), - OLD_ANGRY1(614), - OLD_ANGRY2(615), - OLD_ANGRY3(616), - OLD_ANGRY4(617), - - //Chatheads from 2009? - NOD_YES(9741), - WORRIED(9743), - HALF_WORRIED(9745), //Not on the wiki, first half of worried - AMAZED(9746), - EXTREMELY_SHOCKED(9750), - GUILTY(9758), - HALF_GUILTY(9760), //Not on the wiki, first half of guilty - SAD(9761), - CRYING(9765), - HALF_CRYING(9768), //Not on the wiki, but plays the first half of the crying animation - AFRAID(9772), - SCARED(9776), - PANICKED(9780), - ANNOYED(9784), - ANGRY(9785), - FURIOUS(9792), - ANGRY_WITH_SMILE(9796), // Not on the wiki - ANGRY_WITH_SMILE_AND_EVIL_EYE(9798), //Not on the Wiki - SLEEPING(9802), - SILENT(9804), - NEUTRAL(9808), - THINKING(9812), - HALF_THINKING(9814), - DISGUSTED(9816), - DISGUSTED_HEAD_SHAKE(9823), //Not on the wiki - ASKING(9827), - HALF_ASKING(9830), //Not on wiki, first half of Asking - ROLLING_EYES(9831), //9832, 9833 are the same - HALF_ROLLING_EYES(9834), //Not on Wiki, first half of Rolling eyes - DRUNK(9835), - SUSPICIOUS(9836), - LAUGH(9840), - LOUDLY_LAUGHING(9841), - EVIL_LAUGH(9842), - FRIENDLY(9844), - HAPPY(9847), - JOLLY(9851), - STRUGGLE(9865), //TODO: More? - //9855-9857 are like disgusted? does it just repeat after this? - - //Child Chathead? - CHILD_EVIL_LAUGH(7171), - CHILD_FRIENDLY(7172), - CHILD_NORMAL(7173), - CHILD_NEUTRAL(7174), - CHILD_LOUDLY_LAUGHING(7175), - CHILD_THINKING(7176), - CHILD_SAD(7177), - CHILD_GUILTY(7178), - CHILD_SUSPICIOUS(7179); //TODO: More? - - - - /** - * The animation id. - */ - private final int animationId; - - /** - * Constructs a new {@code FacialExpression} {@code Object}. - * - * @param animationId The animation id. - */ - FacialExpression(int animationId) { - this.animationId = animationId; - } - - /** - * Gets the animation id. - * - * @return The animation id. - */ - public int getAnimationId() { - return animationId; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/FadliDialogue.java b/Server/src/main/java/core/game/content/dialogue/FadliDialogue.java deleted file mode 100644 index 9958e85f6..000000000 --- a/Server/src/main/java/core/game/content/dialogue/FadliDialogue.java +++ /dev/null @@ -1,168 +0,0 @@ -package core.game.content.dialogue; - -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 the fadli dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FadliDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code FadliDialogue} {@code Object}. - */ - public FadliDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FadliDialogue} {@code Object}. - * @param player the player. - */ - public FadliDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FadliDialogue(player); - } - - @Override - public void init() { - super.init(); - try { - new FadliPlugin().newInstance(null); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - @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, "What?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "What do you do?", "What is this place?", "I'd like to store some items please.", "Do you watch any matches?"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do you do?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What is this place?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'd like to store some items please."); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you watch any matches?"); - stage = 40; - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "When I can."); - stage = 40; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - stage = 11; - break; - case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "To be honest I'm wasted here."); - stage = 12; - break; - case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I should be winning duels in an arena! I'm the best", "warrior in Al Kharid!"); - stage = 13; - break; - case 13: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Easy, tiger!"); - stage = 14; - break; - case 14: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Isn't it obvious?"); - stage = 21; - break; - case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "This is the Duel Arena...duh!"); - stage = 22; - break; - case 22: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Sure."); - stage = 31; - break; - case 31: - end(); - player.getBank().open(); - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Most aren't any good so I throw rotten fruit at them!"); - stage = 41; - break; - case 41: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 958 }; - } - - /** - * Represents the faldi interaction plugin. - * @author 'Vexia - * @version 1.0 - */ - public final class FadliPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(958).getHandlers().put("option:buy", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - node.asNpc().openShop(player); - return true; - } - - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/FishingTutorDialogue.java b/Server/src/main/java/core/game/content/dialogue/FishingTutorDialogue.java deleted file mode 100644 index 277642b8a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/FishingTutorDialogue.java +++ /dev/null @@ -1,159 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the fishing tutor dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FishingTutorDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code FishingTutorDialogue} {@code Object}. - */ - public FishingTutorDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FishingTutorDialogue} {@code Object}. - * @param player the player. - */ - public FishingTutorDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FishingTutorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendOptions("Select an Option", "Can you teach me the basics of fishing please?", "Tell me about different fish.", "Where and what should I fish?", "Goodbye."); - 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, "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."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where and what should I fish?"); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 11; - break; - case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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..."); - 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."); - 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."); - stage = 15; - break; - case 15: - interpreter.sendOptions("Select an Option", "Can you teach me the basics of fishing please?", "Tell me about different fish.", "Where and what should I fish?", "Goodbye."); - stage = 1; - break; - case 20: - interpreter.sendOptions("Select an Option", "Small Net Fish", "Big Net Fish", "Rod and Fly Fishing", "Tell me about..."); - stage = 21; - break; - case 21: - switch (buttonId) { - case 1: - interpreter.sendItemMessage(303, "Ahoy, small net fishin' you can do just south of Draynor Village and in these very spots here. Aye."); - stage = 210; - break; - case 2: - interpreter.sendItemMessage(305, "Aye, you can net yourself some big fish in Catherby, which is a good place to fish for most things, Gar!"); - stage = 220; - break; - case 3: - interpreter.sendItemMessage(307, "Aye, rod fishin' can be practiced here at these spots, as well as south of Draynor Village and in the Lumbridge river, depending upon your experience. You can get bait at any fishin' shop, there be one in Port Sarim."); - stage = 230; - break; - case 4: - interpreter.sendOptions("Select an Option", "Can you teach me the basics of fishing please?", "Tell me about different fish.", "Where and what should I fish?", "Goodbye."); - stage = 1; - break; - } - break; - case 210: - interpreter.sendItemMessage(315, "Shrimps and anchovies can be caught with your small fishin' net"); - stage = 20; - break; - case 220: - interpreter.sendItemMessage(355, "Mackerel and Cod will form the backbone of your catch when big net fishin'.. except for the added extras..."); - stage = 221; - break; - case 221: - interpreter.sendDialogues(npc, null, "Some rich rewards for big net fishin', make sure you be", "using a big net fishing spot though..."); - stage = 20; - break; - case 230: - interpreter.sendItemMessage(351, "With a rod you can catch pike, sardines and herring. Good eating on them."); - stage = 231; - break; - case 231: - interpreter.sendItemMessage(309, "The art of fly fishin' can be done in rivers, so the Lumbridge river here would suffice."); - stage = 232; - break; - case 232: - interpreter.sendItemMessage(329, "Aye, you can catch yourself a delicious trout or salmon."); - 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."); - stage = 31; - break; - case 31: - interpreter.sendOptions("Select an Option", "Can you teach me the basics of fishing please?", "Tell me about different fish.", "Where and what should I fish?", "Goodbye."); - stage = 1; - break; - case 40: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4901 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/FortressGuardDialogue.java b/Server/src/main/java/core/game/content/dialogue/FortressGuardDialogue.java deleted file mode 100644 index 8cb158113..000000000 --- a/Server/src/main/java/core/game/content/dialogue/FortressGuardDialogue.java +++ /dev/null @@ -1,233 +0,0 @@ -package core.game.content.dialogue; - -import java.util.List; - -import core.game.content.global.action.DoorActionHandler; -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.game.world.map.RegionManager; - -/** - * Represents the fortress guard dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public class FortressGuardDialogue extends DialoguePlugin { - - /** - * Represents the uniform item. - */ - private static final Item[] UNIFORM = new Item[] { new Item(1139), new Item(1101) }; - - /** - * Constructs a new {@code FortressGuardDialogue} {@code Object}. - */ - public FortressGuardDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FortressGuardDialogue} {@code Object}. - * @param player the player. - */ - public FortressGuardDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FortressGuardDialogue(player); - } - - @Override - 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!"); - 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!"); - stage = 50; - return true; - } - if (!inUniform(player)) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Get lost. This is private property."); - stage = 0; - } else { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hey! Get back on duty!"); - stage = 30; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, but I work here!"); - 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."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How can you tell?"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're not even wearing proper guards uniform!"); - stage = 4; - break; - case 4: - interpreter.sendOptions("Select an Option", "Oh pleeeaase let me in!", "So what is this uniform?"); - stage = 5; - break; - case 5: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh pleeeaaase let me in!"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So what is this uniform?"); - stage = 20; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - 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..."); - stage = 22; - break; - case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What was that you muttered?"); - stage = 23; - break; - case 23: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, nothing important!"); - stage = 24; - break; - case 24: - end(); - break; - case 30: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh..."); - stage = 31; - break; - case 31: - end(); - break; - case 40: - interpreter.sendOptions("Select an Option", "Yes, but I work here!", "Oh, sorry.", "So who does it belong to?"); - stage = 41; - break; - case 41: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, but I work here!"); - stage = 1; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, sorry."); - stage = 42; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So who does it belong to?"); - stage = 44; - break; - } - break; - case 42: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - stage = 45; - break; - case 45: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh. Okay, thanks."); - stage = 46; - break; - case 46: - end(); - break; - case 50: - interpreter.sendOptions("Select an Option", "Okay, I won't.", "I don't care. I'm going in anyway."); - stage = 51; - break; - case 51: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Okay, I won't."); - stage = 52; - break; - case 2: - interpreter.sendDialogues(player, null, "I don't care. I'm going in anyway."); - stage = 54; - break; - } - break; - case 52: - interpreter.sendDialogues(npc, null, "Wise move."); - stage = 53; - break; - case 53: - end(); - break; - case 54: - end(); - DoorActionHandler.handleAutowalkDoor(player, RegionManager.getObject(0, 3020, 3515)); - List npcs = RegionManager.getLocalNpcs(player); - for (NPC npc : npcs) { - if (npc.getId() == 179) { - npc.getProperties().getCombatPulse().attack(player); - npc.sendChat("Die, intruder!"); - return true; - } - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 609, 4603, 4604, 4605, 4606 }; - } - - /** - * Method used to cehck if the player is in the inventory. - * @param player the player. - * @return True if so. - */ - public static boolean inUniform(final Player player) { - for (Item i : UNIFORM) { - if (!player.getEquipment().containsItem(i)) { - return false; - } - } - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/FortunatoDialogue.java b/Server/src/main/java/core/game/content/dialogue/FortunatoDialogue.java deleted file mode 100644 index 62c819ae3..000000000 --- a/Server/src/main/java/core/game/content/dialogue/FortunatoDialogue.java +++ /dev/null @@ -1,113 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/FredTheFarmer.java b/Server/src/main/java/core/game/content/dialogue/FredTheFarmer.java deleted file mode 100644 index 08e316b94..000000000 --- a/Server/src/main/java/core/game/content/dialogue/FredTheFarmer.java +++ /dev/null @@ -1,372 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; - -import core.plugin.Initializable; -import core.game.content.quest.free.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/java/core/game/content/dialogue/FurTradePlugin.java b/Server/src/main/java/core/game/content/dialogue/FurTradePlugin.java deleted file mode 100644 index fa8791505..000000000 --- a/Server/src/main/java/core/game/content/dialogue/FurTradePlugin.java +++ /dev/null @@ -1,75 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/GarvDialogue.java b/Server/src/main/java/core/game/content/dialogue/GarvDialogue.java deleted file mode 100644 index 2c25885a4..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GarvDialogue.java +++ /dev/null @@ -1,86 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/GemMerchantDialogue.java b/Server/src/main/java/core/game/content/dialogue/GemMerchantDialogue.java deleted file mode 100644 index 91fc251f7..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GemMerchantDialogue.java +++ /dev/null @@ -1,60 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/GemTradeDialogue.java b/Server/src/main/java/core/game/content/dialogue/GemTradeDialogue.java deleted file mode 100644 index eb0ea5af7..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GemTradeDialogue.java +++ /dev/null @@ -1,80 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the gem trade dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GemTradeDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GemTradeDialogue} {@code Object}. - */ - public GemTradeDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GemTradeDialogue} {@code Object}. - * @param player the player. - */ - public GemTradeDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GemTradeDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello adventurer! Can I interest you in any of my gems?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes!", "No, thanks you."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes!"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks though."); - stage = 20; - break; - - } - break; - case 10: - end(); - npc.openShop(player); - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 540 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/GerrantDialogue.java b/Server/src/main/java/core/game/content/dialogue/GerrantDialogue.java deleted file mode 100644 index 7acc2ddc5..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GerrantDialogue.java +++ /dev/null @@ -1,80 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/GertrudeDialogue.java b/Server/src/main/java/core/game/content/dialogue/GertrudeDialogue.java deleted file mode 100644 index f2cfb7038..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GertrudeDialogue.java +++ /dev/null @@ -1,391 +0,0 @@ -package core.game.content.dialogue; - -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.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the gertrude dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GertrudeDialogue extends DialoguePlugin { - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 100); - - /** - * Constructs a new {@code GertrudeDialogue} {@code Object}. - */ - public GertrudeDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GertrudeDialogue} {@code Object}. - * @param player the player. - */ - public GertrudeDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GertrudeDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); - switch (quest.getStage(player)) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, are you ok?"); - break; - case 10: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Gertrude."); - stage = 210; - break; - case 20: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Gertrude."); - stage = 230; - break; - case 30: - case 50: - interpreter.sendDialogues(npc, null, "Please bring me my cat back!"); - stage = 235; - break; - case 40: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Gertrude."); - stage = 300; - break; - case 60: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello Gertrude. Fluffs ran off with her kitten."); - stage = 320; - break; - case 100: - interpreter.sendDialogues(player, null, "Hello again."); - stage = 500; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do I look ok? Those kids drive me crazy."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm sorry. It's just that I've lost her."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Lost who?"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Fluffs, poor Fluffs. She never hurt anyone."); - stage = 4; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who's Fluffs?"); - stage = 5; - break; - case 5: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "My beloved feline friends Fluffs. She's been purring by", "my side for almost a decade. Please, could you go", "search for her while I look over the kids?"); - stage = 6; - break; - case 6: - interpreter.sendOptions("Select an Option", "Well, I suppose I could.", "Sorry, I'm too busy to play pet rescue."); - stage = 7; - break; - case 7: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, I suppose I could."); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I'm to busy too play pet rescue."); - stage = 200; - break; - } - break; - case 100: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Really? Thank you so much! I really have no idea", "where she could be!"); - stage = 101; - break; - case 101: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I think my sons, Shilop and Wilough, saw the cat last.", "They'll be out in the market place."); - stage = 102; - break; - case 102: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Alright then, I'll see what I can do."); - stage = 103; - break; - case 103: - quest.start(player); - player.getQuestRepository().syncronizeTab(player); - end(); - break; - case 200: - end(); - break; - case 210:// start of stage 10 - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Have you seen my poor Fluffs?"); - stage = 211; - break; - case 211: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm afraid not."); - stage = 212; - break; - case 212: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What about Shilop?"); - stage = 213; - break; - case 213: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No sign of him either."); - stage = 214; - break; - case 214: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmmm...strange, he should be at the market."); - stage = 215; - break; - case 215: - 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."); - stage = 231; - break; - case 231: - interpreter.sendDialogues(player, null, "He does seem quite a handful."); - stage = 232; - break; - case 232: - interpreter.sendDialogues(npc, null, "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."); - stage = 234; - break; - case 234: - interpreter.sendDialogues(npc, null, "Thanks again adventurer!"); - stage = 235; - break; - case 235: - end(); - break; - case 300: - interpreter.sendDialogues(npc, null, "Hi! Did you find fluffs?"); - stage = 301; - break; - case 301: - interpreter.sendDialogues(player, null, "Yes! But she won't follow me."); - stage = 302; - break; - case 302: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You should try tempting her with a", "seasoned sardine! Those are her favourite snacks."); - stage = 303; - break; - case 303: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks for the advice!"); - stage = 304; - break; - case 304: - 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."); - stage = 321; - break; - case 321: - interpreter.sendDialogue("Gertrude gives you a hug."); - npc.face(player); - stage = 322; - break; - case 322: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "If you hadn't found her kitten it would have died out", "there!"); - stage = 323; - break; - case 323: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's ok, I like to do my bit."); - stage = 324; - break; - case 324: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I don't know how to thank you. I have no real material", "possesions. I do have kittens! I can only really look", "after one."); - stage = 325; - break; - case 325: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, if it needs a home."); - stage = 326; - break; - case 326: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I would sell it to my cousin in West Ardougne. I hear", "there's a rat epidemic there. But it's too far."); - stage = 327; - break; - case 327: - if (player.getInventory().freeSlots() == 0) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't seem to have enough inventory space."); - stage = 1000; - break; - } else { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here you go, look after her and thank you again!"); - stage = 328; - } - break; - case 328: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh by the way, the kitten can live in your backpack,", "but to make it grow you must take it out and feed and", "stroke it often."); - stage = 329; - break; - case 329: - interpreter.sendDialogue("Gertrude gives you a kitten."); - stage = 330; - break; - case 330: - player.getPacketDispatch().sendMessage("...and some food!"); - end(); - quest.finish(player); - player.getQuestRepository().syncronizeTab(player); - break; - case 331: - break; - case 1000: - interpreter.sendDialogues(npc, null, "Good good, See you again."); - stage = 1001; - break; - case 1001: - end(); - break; - case 500: - interpreter.sendDialogues(npc, null, "Hello my dear. How's things?"); - stage = 501; - break; - case 501: - interpreter.sendOptions("Select an Option", "I'm fine, thanks.", "Do you have any more kittens?"); - stage = 502; - break; - case 502: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "I'm fine, thanks."); - stage = 1000; - break; - case 2: - interpreter.sendDialogues(player, null, "Do you have any more kittens?"); - stage = 503; - break; - } - break; - 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)) { - has = true; - break; - } - } - if (has) { - interpreter.sendDialogues(npc, null, "Aren't you still raising that other kitten? Only once it's", "fully grown and it no longer needs your attention will", "I let you have another kitten."); - stage = 505; - } else { - interpreter.sendDialogues(npc, null, "Indeed I have. They are 100 coins each, do you want", "one?"); - stage = 900; - } - break; - case 900: - interpreter.sendOptions("Select an Option", "Yes please.", "No thanks."); - stage = 901; - break; - 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."); - end(); - break; - } else { - if (player.getInventory().freeSlots() == 0) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - end(); - break; - } - interpreter.sendDialogues(player, null, "Yes please."); - stage = 903; - } - break; - case 2:// no - end(); - break; - } - break; - case 903: - interpreter.sendDialogues(npc, null, "Ok then, here you go."); - stage = 904; - break; - case 904: - interpreter.sendDialogues(player, null, "Thanks."); - 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); - } - break; - case 906: - end(); - break; - case 505: - end(); - break; - } - return true; - } - - /** - * Method used to get a random kitten. - * @return the item. - */ - public Item getKitten() { - return new Item(RandomFunction.random(1555, 1560)); - } - - @Override - public int[] getIds() { - return new int[] { 780 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/GnomeSpiritTreeDialogue.java b/Server/src/main/java/core/game/content/dialogue/GnomeSpiritTreeDialogue.java deleted file mode 100644 index 403cca888..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GnomeSpiritTreeDialogue.java +++ /dev/null @@ -1,136 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.game.world.update.flag.context.Graphics; - -/** - * Represents the dialogue plugin used for the gnome spirit tree. - * - * @author 'Vexia - * @author SonicForce41 - */ -@Initializable -public final class GnomeSpiritTreeDialogue extends DialoguePlugin { - - /** - * Represents the locations to travel to. - */ - private static final Location[] LOCATIONS = new Location[]{Location.create(2542, 3170, 0), Location.create(2461, 3444, 0), Location.create(2556, 3259, 0), Location.create(3184, 3508, 0)}; - - /** - * Represents the animations to use. - */ - private static final Animation[] ANIMATIONS = new Animation[]{new Animation(7082), new Animation(7084)}; - - /** - * Represents the graphics to use. - */ - private static final Graphics[] GRAPHICS = new Graphics[]{new Graphics(1228), new Graphics(1229)}; - - /** - * Constructs a new {@code GnomeSpiritTreeDialogue} {@code Object}. - */ - public GnomeSpiritTreeDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GnomeSpiritTreeDialogue} {@code Object}. - * - * @param player the player. - */ - public GnomeSpiritTreeDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GnomeSpiritTreeDialogue(player); - } - - @Override - public boolean open(Object... args) { - interpreter.sendDialogues(3636, null, "If you are a friend of the gnome people, you are a", "friend of mine, Do you wish to travel?"); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - interpreter.sendOptions("Where would you like to go?", "Tree Gnome Village", "Tree Gnome Stronghold", "Battlefield of Khazard", "Grand Exchange"); - stage = 0; - break; - case 0: - Location location = null; - stage = 1; - switch (buttonId) { - case 1: - location = LOCATIONS[0]; - sendTeleport(player, location); - break; - case 2: - location = LOCATIONS[1]; - sendTeleport(player, location); - break; - case 3: - location = LOCATIONS[2]; - sendTeleport(player, location); - break; - case 4: - location = LOCATIONS[3]; - sendTeleport(player, location); - break; - } - return true; - } - return true; - } - - /** - * Method used to send a gnome teleport. - * - * @param player the player. - * @param location the location. - */ - private void sendTeleport(final Player player, final Location location) { - end(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int loop; - - @Override - public boolean pulse() { - if (loop == 0) { - player.animate(ANIMATIONS[0]); - player.graphics(GRAPHICS[0]); - } else if (loop == 3) { - player.getProperties().setTeleportLocation(location); - } else if (loop == 5) { - player.animate(ANIMATIONS[1]); - player.graphics(GRAPHICS[1]); - player.face(null); - if (player.getLocation().withinDistance(Location.create(3184, 3508, 0))) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 5); - } - } - loop++; - return loop == 8; - } - }); - } - - @Override - public int[] getIds() { - return new int[]{1293, 1317}; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/GraardoorJRDialogue.java b/Server/src/main/java/core/game/content/dialogue/GraardoorJRDialogue.java deleted file mode 100644 index 7fa94ca10..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GraardoorJRDialogue.java +++ /dev/null @@ -1,67 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, General Graardoor JR. - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class GraardoorJRDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GraardoorJRDialogue} {@code Object}. - */ - public GraardoorJRDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GraardoorJRDialogue} {@code Object}. - * @param player the player. - */ - public GraardoorJRDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GraardoorJRDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Not sure this is going to be worth my time but...", "how are you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "SFudghoigdfpDSOPGnbSOBNfdbdnopbdnopbddfnopdf", "pofhdARRRGGGGH!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nope. Not worth it."); - stage = 2; - break; - case 2: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8594 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/GrandExchangeClerk.java b/Server/src/main/java/core/game/content/dialogue/GrandExchangeClerk.java deleted file mode 100644 index fc78a7f2e..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GrandExchangeClerk.java +++ /dev/null @@ -1,136 +0,0 @@ -package core.game.content.dialogue; - -import core.game.ge.GrandExchangeDatabase; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the GrandExchangeClerk dialogue. - * @author 'Vexia - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class GrandExchangeClerk extends DialoguePlugin { - - /** - * Constructs a new {@code GrandExchangeClerk} {@code Object}. - */ - public GrandExchangeClerk() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GrandExchangeClerk} {@code Object}. - * @param player The player. - */ - public GrandExchangeClerk(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GrandExchangeClerk(player); - } - - @Override - public boolean open(Object... args) { - if (args.length > 0 && args[0] instanceof NPC) { - npc = (NPC) args[0]; - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi there."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (stage < 50 && stage > 5 && !GrandExchangeDatabase.hasInitialized()) { - npc("The Grand Exchange is currently unavailable,", "it will be back soon!"); - stage = 51; - return true; - } - switch (stage) { - case 0: - npc("Good day to you, sir, How can I help?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "I want to access the Grand Exchange, please.", "I want to collect my items.", "Can I see a history of my offers?", "Can you help me with item sets?", "I'm fine, actually."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I want to access the Grand Exchange, please."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I want to collect my items."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I see history of my offers?"); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you help me with item sets?"); - stage = 40; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm fine actually."); - stage = 50; - break; - } - break; - case 10: - npc("Only too happy to help you, sir."); - stage = 11; - break; - case 11: - end(); - player.getPlayerGrandExchange().open(); - break; - case 20: - npc("As you wish, sir."); - stage = 21; - break; - case 21: - end(); - player.getPlayerGrandExchange().openCollectionBox(); - break; - case 30: - npc("If that is your wish."); - stage = 31; - break; - case 31: - end(); - player.getPlayerGrandExchange().openHistoryLog(player); - break; - case 40: - npc("It would be my pleasure, sir."); - stage = 41; - break; - case 41: - end(); - player.getPlayerGrandExchange().openItemSets(); - break; - case 50: - npc("If you say so, sir."); - stage = 51; - break; - case 51: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6528, 6529, 6530, 6531 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/GregoryDialogue.java b/Server/src/main/java/core/game/content/dialogue/GregoryDialogue.java deleted file mode 100644 index a603ca3fe..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GregoryDialogue.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/GruborDialogue.java b/Server/src/main/java/core/game/content/dialogue/GruborDialogue.java deleted file mode 100644 index eb27daaca..000000000 --- a/Server/src/main/java/core/game/content/dialogue/GruborDialogue.java +++ /dev/null @@ -1,97 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/HarrallakMenarous.java b/Server/src/main/java/core/game/content/dialogue/HarrallakMenarous.java deleted file mode 100644 index 0e8b89dcb..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HarrallakMenarous.java +++ /dev/null @@ -1,301 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/HeadChefDialogue.java b/Server/src/main/java/core/game/content/dialogue/HeadChefDialogue.java deleted file mode 100644 index 7acd07817..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HeadChefDialogue.java +++ /dev/null @@ -1,167 +0,0 @@ -package core.game.content.dialogue; - -import core.game.content.global.Skillcape; -import core.game.node.entity.skill.Skills; -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 head chef. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HeadChefDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code HeadChefDialogue} {@code Object}. - */ - public HeadChefDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HeadChefDialogue} {@code Object}. - * @param player the player. - */ - public HeadChefDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HeadChefDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - boolean door = false; - 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."); - 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."); - 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."); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - switch (stage) { - case 0: - end(); - break; - case 1: - interpreter.sendOptions("Select an Option", "Nice cape you're wearing!", "Thanks, bye."); - stage = 2; - break; - case 2: - - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nice cape, you're wearing!"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks, bye."); - stage = 3; - break; - } - - break; - case 3: - 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!"); - stage = 11; - break; - case 11: - end(); - break; - case 100: - interpreter.sendOptions("Select an Option", "No thanks.", "Yes please."); - stage = 101; - break; - case 101: - - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks."); - stage = 110; - break; - case 2: - player("Yes, please."); - stage = 150; - break; - } - break; - - case 110: - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Okay, come back to me if you change your mind."); - stage = 111; - break; - case 111: - end(); - break; - case 150: - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Most certainly, just as soon as you give me 99000 gold", "coins."); - stage = 151; - break; - case 151: - interpreter.sendOptions("Select an Option", "That's much too expensive.", "Sure."); - stage = 152; - break; - case 152: - - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's much too expensive."); - stage = 160; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sure."); - stage = 200; - break; - } - - break; - case 160: - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "I'm sorry you feel that way."); - stage = 161; - break; - case 161: - end(); - break; - case 200: - if (Skillcape.purchase(player, Skills.COOKING)) { - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Now you can use the title Master Chef."); - } - stage = 202; - break; - case 202: - end(); - break; - } - - return true; - } - - @Override - public int[] getIds() { - return new int[] { 847 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/HengelDialoguePlugin.java b/Server/src/main/java/core/game/content/dialogue/HengelDialoguePlugin.java deleted file mode 100644 index e2fe49df9..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HengelDialoguePlugin.java +++ /dev/null @@ -1,114 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/HeronDialogue.java b/Server/src/main/java/core/game/content/dialogue/HeronDialogue.java deleted file mode 100644 index 096ad4cb8..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HeronDialogue.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the heron dialgoue. - * @author Empathy - * - */ -@Initializable -public final class HeronDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code HeronDialogue} {@code Object}. - */ - public HeronDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HeronDialogue} {@code Object}. - * - * @param player the player. - */ - public HeronDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HeronDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Hop inside my mouth, if you want to live!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm not falling for that... I'm not a fish! I've got more", "foresight than that."); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8647 }; - } - } diff --git a/Server/src/main/java/core/game/content/dialogue/HettyDialogue.java b/Server/src/main/java/core/game/content/dialogue/HettyDialogue.java deleted file mode 100644 index ed1842836..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HettyDialogue.java +++ /dev/null @@ -1,224 +0,0 @@ -package core.game.content.dialogue; - -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 prinicples 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", "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/java/core/game/content/dialogue/HighPriestDialogue.java b/Server/src/main/java/core/game/content/dialogue/HighPriestDialogue.java deleted file mode 100644 index a9ab3fad7..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HighPriestDialogue.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the high priest dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HighPriestDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code HighPriestDialogue} {@code Object}. - */ - public HighPriestDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HighPriestDialogue} {@code Object}. - * @param player the player. - */ - public HighPriestDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HighPriestDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Many greetings. Welcome to our fair island."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Enjoy our stay here. May it be spiritually uplifting!"); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 216 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/HildDialogue.java b/Server/src/main/java/core/game/content/dialogue/HildDialogue.java deleted file mode 100644 index 5d56af7ef..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HildDialogue.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/HistanDialogue.java b/Server/src/main/java/core/game/content/dialogue/HistanDialogue.java deleted file mode 100644 index 1a19016c5..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HistanDialogue.java +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/HorvikDialogue.java b/Server/src/main/java/core/game/content/dialogue/HorvikDialogue.java deleted file mode 100644 index 902f33395..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HorvikDialogue.java +++ /dev/null @@ -1,82 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the horvik dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HorvikDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code HorvikDialogue} {@code Object}. - */ - public HorvikDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HorvikDialogue} {@code Object}. - * @param player the player. - */ - public HorvikDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HorvikDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello, do you need any help?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "No, thanks. I'm just looking around.", "Do you want to trade?"); - stage = 1; - break; - case 1: - - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks. I'm just looking around."); - stage = 10; - break; - case 2: - end(); - npc.openShop(player); - break; - } - - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, come and see me if you're ever in need of armour!"); - stage = 11; - break; - case 11: - end(); - break; - } - - return true; - } - - @Override - public int[] getIds() { - return new int[] { 549 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/HygdDialogue.java b/Server/src/main/java/core/game/content/dialogue/HygdDialogue.java deleted file mode 100644 index 3ecf8be80..000000000 --- a/Server/src/main/java/core/game/content/dialogue/HygdDialogue.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/IrinaDialogue.java b/Server/src/main/java/core/game/content/dialogue/IrinaDialogue.java deleted file mode 100644 index 3d30cb56a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/IrinaDialogue.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/JaraahDialogue.java b/Server/src/main/java/core/game/content/dialogue/JaraahDialogue.java deleted file mode 100644 index d0f62b2d8..000000000 --- a/Server/src/main/java/core/game/content/dialogue/JaraahDialogue.java +++ /dev/null @@ -1,118 +0,0 @@ -package core.game.content.dialogue; - -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 core.game.world.update.flag.context.Animation; - -/** - * Represents the jaraah dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class JaraahDialogue extends DialoguePlugin { - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(881); - - /** - * Constructs a new {@code JaraahDialogue} {@code Object}. - */ - public JaraahDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code JaraahDialogue} {@code Object}. - * @param player the player. - */ - public JaraahDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new JaraahDialogue(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, "What? Can't you see I'm busy?!"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "Can you heal me?", "You must see some gruesome things?", "Why do they call you 'The Butcher'?"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you heal me?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You must see some gruesome things?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why do they call you 'The Butcher'?"); - stage = 30; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "OK. This will hurt you more than it will me."); - stage = 11; - break; - case 11: - npc.animate(ANIMATION); - if (player.getSkills().getLifepoints() == player.getSkills().getStaticLevel(Skills.HITPOINTS)) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You look healthy to me!"); - } else { - player.getSkills().heal(player.getSkills().getStaticLevel(Skills.HITPOINTS)); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "There you go!"); - } - stage = 12; - break; - case 12: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "It's a gruesome business and with the tools they give", "me it gets more gruesome before it gets better!"); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I often have to amputate peoples limbs!"); - stage = 31; - break; - case 31: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 962 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/JatixDialogue.java b/Server/src/main/java/core/game/content/dialogue/JatixDialogue.java deleted file mode 100644 index dc5516890..000000000 --- a/Server/src/main/java/core/game/content/dialogue/JatixDialogue.java +++ /dev/null @@ -1,68 +0,0 @@ -package core.game.content.dialogue; - -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 jatix npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class JatixDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code JatixDialogue} {@code Object}. - */ - public JatixDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code JatixDialogue} {@code Object}. - * @param player the player. - */ - public JatixDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new JatixDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello, adventurer."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What are you selling?"); - stage = 2; - break; - case 2: - end(); - npc.openShop(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 587 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/JosephDialogue.java b/Server/src/main/java/core/game/content/dialogue/JosephDialogue.java deleted file mode 100644 index b116f8460..000000000 --- a/Server/src/main/java/core/game/content/dialogue/JosephDialogue.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/KalebDialgoue.java b/Server/src/main/java/core/game/content/dialogue/KalebDialgoue.java deleted file mode 100644 index 525046229..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KalebDialgoue.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the kaleb dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KalebDialgoue extends DialoguePlugin { - - /** - * Constructs a new {@code KalebDialgoue} {@code Object}. - */ - public KalebDialgoue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KalebDialgoue} {@code Object}. - * @param player the player. - */ - public KalebDialgoue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KalebDialgoue(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, "Bye!"); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 666 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/KanelDialogue.java b/Server/src/main/java/core/game/content/dialogue/KanelDialogue.java deleted file mode 100644 index 72d0dda2d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KanelDialogue.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used for kanel. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KanelDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code KanelDialogue} {@code Object}. - */ - public KanelDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KanelDialogue} {@code Object}. - * @param player the player. - */ - public KanelDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KanelDialogue(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, "Hel-lo?"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Right. Goodbye."); - 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[] { 784 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/KangaiMauDialogue.java b/Server/src/main/java/core/game/content/dialogue/KangaiMauDialogue.java deleted file mode 100644 index 4ad0a91fc..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KangaiMauDialogue.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the knagi mau dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KangaiMauDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code KangaiMauDialogue} {@code Object}. - */ - public KangaiMauDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KangaiMauDialogue} {@code Object}. - * @param player the player. - */ - public KangaiMauDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KangaiMauDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello. I Kangai Mau of the Rantuki tribe."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nice to meet you!"); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 846 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/KaqemeexDialogue.java b/Server/src/main/java/core/game/content/dialogue/KaqemeexDialogue.java deleted file mode 100644 index c5753648c..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KaqemeexDialogue.java +++ /dev/null @@ -1,323 +0,0 @@ -package core.game.content.dialogue; - -import core.game.content.global.Skillcape; -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; - -/** - * Represents the kaqemeex dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KaqemeexDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code KaqemeexDialogue} {@code Object}. - */ - public KaqemeexDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KaqemeexDialogue} {@code Object}. - * @param player the player. - */ - public KaqemeexDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KaqemeexDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (args.length >= 2) { - interpreter.sendDialogues(npc, null, "I will now explain the fundamentals of Herblore:"); - stage = 1000; - return true; - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (player.getQuestRepository().isComplete("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."); - 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."); - stage = 200; - break; - } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What brings you to our holy monument?"); - stage = 1; - break; - case 1: - if (player.getQuestRepository().getQuest("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; - } else { - interpreter.sendOptions("Select an Option", "Who are you?", "Did you build this?"); - stage = 500; - } - break; - } else { - interpreter.sendOptions("Select an Option", "Who are you?", "I'm in search of a quest.", "Did you build this?"); - stage = 2; - } - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who are you?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm in search of a quest."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 11; - break; - case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - 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."); - 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."); - stage = 32; - break; - case 32: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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..."); - stage = 21; - break; - case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That used to be our stone circle. Unfortunately,", "many years ago, dark wizards cast a wicked spell", "upon it so that they could corrupt its power for their", "own evil ends."); - 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."); - stage = 23; - break; - case 23: - interpreter.sendOptions("Select an Option", "Ok, I will try and help.", "No, that doesn't sound very interesting."); - stage = 24; - break; - case 24: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok, I will try and help."); - stage = 26; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, that doesn't sound very interesting."); - stage = 25; - break; - } - break; - case 25: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I will not try and change your mind adventurer. Some", "day when you have matured you may reconsider your", "position. We will wait until then."); - 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."); - stage = 27; - break; - case 27: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Will do."); - stage = 28; - break; - case 28: - end(); - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You will need to speak to my fellow druid Sanfew in", "the village south of here to continue in your quest", "adventurer."); - stage = 41; - break; - case 41: - end(); - break; - case 200: - end(); - player.getQuestRepository().getQuest("Druidic Ritual").finish(player); - break; - case 500: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who are you?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Did you build this?"); - stage = 30; - break; - } - break; - case 501: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I buy a Skillcape of Herblore?"); - stage = 800; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who are you?"); - stage = 10; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Did you build this?"); - stage = 30; - break; - } - break; - case 600: - interpreter.sendDialogues(player, null, "Good good!"); - stage = 601; - break; - case 601: - 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 = 501; - } else { - interpreter.sendOptions("Select an Option", "Who are you?", "Did you build this?"); - stage = 500; - } - break; - case 1000: - interpreter.sendDialogues(npc, null, "Herblore is the skill of working with herbs and other", "ingredients, to make useful potions and poison."); - stage = 1001; - break; - case 1001: - interpreter.sendDialogues(npc, null, "First you will need a vial, which can be found or made", "with the crafting skill."); - stage = 1002; - break; - case 1002: - interpreter.sendDialogues(npc, null, "Then you must gather the herbs needed to make the", "potion you want."); - stage = 1003; - break; - case 1003: - interpreter.sendDialogues(npc, null, "Refer to the Council's instructions in the Skills section", "of the website for the items needed to make a particular", "kind of potion."); - stage = 1004; - break; - case 1004: - interpreter.sendDialogues(npc, null, "You must fill your vial with water and add the", "ingredients you need. There are normally 2 ingredients", "to each type of potion."); - stage = 1005; - break; - case 1005: - interpreter.sendDialogues(npc, null, "Bear in mind you must first identify each herb, to see", "what it is."); - stage = 1006; - break; - case 1006: - interpreter.sendDialogues(npc, null, "You may also have to grind some herbs before you can", "use them. You will need a pestle and mortar in order", "to do this."); - stage = 1007; - break; - case 1007: - interpreter.sendDialogues(npc, null, "Herbs can be found on the ground, and are also", "dropped by some monsters when you kill them."); - stage = 1008; - break; - case 1008: - interpreter.sendDialogues(npc, null, "Mix these in your water-filled vial, and you will produce", "an Attack potion."); - stage = 1009; - break; - case 1009: - interpreter.sendDialogues(npc, null, "Drink this poition to increase your Attack level."); - stage = 1010; - break; - case 1010: - interpreter.sendDialogues(npc, null, "Different potions also require different Herblore levels", "before you can make them."); - stage = 1011; - break; - case 1011: - interpreter.sendDialogues(npc, null, "Once again, check the instructions found on the", "Council's website for the levels needed to make a", "particulur potion."); - stage = 1012; - break; - case 1012: - interpreter.sendDialogues(npc, null, "Good luck with your Herblore practices, Good day", "Adventurer."); - stage = 1013; - break; - case 1013: - interpreter.sendDialogues(player, null, "Thanks for your help."); - stage = 1014; - break; - case 1014: - end(); - break; - case 800: - npc("Certainly! Right when you give me 99000 coins."); - stage = 801; - break; - case 801: - options("Yes, here you go,", "No, thanks."); - stage = 802; - break; - case 802: - switch (buttonId) { - case 1: - player("Yes, here you go."); - stage = 803; - break; - case 2: - end(); - break; - } - break; - case 803: - if (Skillcape.purchase(player, Skills.HERBLORE)) { - npc("There you go! Enjoy."); - } - stage = 804; - break; - case 804: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 455 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/KaramajaManDialogue.java b/Server/src/main/java/core/game/content/dialogue/KaramajaManDialogue.java deleted file mode 100644 index f54cd096e..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KaramajaManDialogue.java +++ /dev/null @@ -1,67 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/KayleeDialogue.java b/Server/src/main/java/core/game/content/dialogue/KayleeDialogue.java deleted file mode 100644 index 260a9b585..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KayleeDialogue.java +++ /dev/null @@ -1,211 +0,0 @@ -package core.game.content.dialogue; - -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; - -/** - * Represents the kaylee dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KayleeDialogue extends DialoguePlugin { - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 3); - - /** - * Represents the ale item. - */ - private static final Item ASGARNIAN_ALE = new Item(1905, 1); - - /** - * Represents the wizards mind bomb. - */ - private static final Item MIND_BOMB = new Item(1907, 1); - - /** - * Represents the dwarven stout item. - */ - private static final Item DWARVEN_STOUT = new Item(1913, 1); - - /** - * Constructs a new {@code KayleeDialogue} {@code Object}. - */ - public KayleeDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KayleeDialogue} {@code Object}. - * @param player the player. - */ - public KayleeDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KayleeDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hi! What can I get you?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (player.getInventory().contains(1919, 1)) { - interpreter.sendOptions("What would you like to say?", "What ales are you serving?", "I've got this beer glass..."); - stage = 1; - } else { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What ales are you serving?"); - stage = 20; - } - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What ales are you serving?"); - stage = 20; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I've got these beer glasses...."); - stage = 200; - break; - } - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, we've got Asgarnian Ale, Wizard's Mind Bomb", "and Dwarven Stout, all for only 3 coins."); - stage = 21; - break; - case 21: - interpreter.sendOptions("What would you like to say?", "One Asgarnian Ale, please.", "I'll try the Mind Bomb.", "Can I have A Dwarven Stout?", "I don't feel like any of those."); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "One Asgarnian Ale, please."); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'll try the Mind Bomb."); - stage = 120; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I have a Dwarven Stout?"); - stage = 130; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't feel like any of those."); - stage = 444; - break; - - } - break; - case 100: - if (!player.getInventory().containsItem(COINS)) { - end(); - player.getPacketDispatch().sendMessage("You need 3 gold coins to buy ale."); - return true; - } - if (player.getInventory().remove(COINS)) { - player.getInventory().add(ASGARNIAN_ALE); - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks, " + npc.getName() + "."); - player.getPacketDispatch().sendMessage("You buy a Asgarnian Ale."); - stage = 101; - } else { - end(); - player.getPacketDispatch().sendMessage("You need 3 gold coins to buy ale."); - } - break; - case 101: - end(); - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR, 0, 1); - break; - case 120: - if (player.getInventory().remove(COINS)) { - player.getInventory().add(MIND_BOMB); - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks, " + npc.getName() + "."); - player.getPacketDispatch().sendMessage("You buy a Wizard's Mind Bomb."); - stage = 101; - } else { - end(); - player.getPacketDispatch().sendMessage("You need 3 gold coins to buy ale."); - } - break; - case 130: - if (player.getInventory().remove(COINS)) { - player.getInventory().add(DWARVEN_STOUT); - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks, " + npc.getName() + "."); - player.getPacketDispatch().sendMessage("You buy a Dwarven Stout."); - stage = 101; - } else { - end(); - player.getPacketDispatch().sendMessage("You need 3 gold coins to buy ale."); - } - break; - case 200: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ooh, we'll buy those off you if", "you're interested. 2gp per glass."); - stage = 201; - break; - case 201: - interpreter.sendOptions("What would you like to say?", "Okay, sure.", "No thanks, I like empty beer glasses."); - stage = 202; - break; - case 202: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, sure."); - stage = 190; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks, I like empty beer glasses."); - stage = 400; - break; - - } - break; - case 190: - if (player.getInventory().contains(1919, 1)) { - player.getInventory().add(new Item(995, 2 * player.getInventory().getAmount(new Item(1919)))); - player.getInventory().remove(new Item(1919, player.getInventory().getAmount(new Item(1919)))); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "There you go."); - stage = 191; - } else - end(); - stage = 191; - break; - case 191: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks!"); - stage = 192; - break; - case 192: - end(); - break; - case 444: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3217, 736 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/KeyMasterDialoguePlugin.java b/Server/src/main/java/core/game/content/dialogue/KeyMasterDialoguePlugin.java deleted file mode 100644 index 65e0be363..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KeyMasterDialoguePlugin.java +++ /dev/null @@ -1,182 +0,0 @@ -package core.game.content.dialogue; - -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the key master dialogue. - * @author Empathy - * - */ -@Initializable -public class KeyMasterDialoguePlugin extends DialoguePlugin { - - /** - * Constructs a new @{Code KeyMasterDialoguePlugin} object. - */ - public KeyMasterDialoguePlugin() { - /** - * empty - */ - } - - /** - * Constructs a new @{Code KeyMasterDialoguePlugin} object. - * @param player - */ - public KeyMasterDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KeyMasterDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - player("Hello."); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch(stage) { - case 1: - npc("Who goes there?"); - stage++; - break; - case 2: - npc("This is no place for a human. You need to leave."); - stage++; - break; - case 3: - player("Why?"); - stage++; - break; - case 4: - npc("The voices! The voices in my head!"); - stage++; - break; - case 5: - player("You're starting to scare me man..."); - stage++; - break; - case 6: - npc("I am no man! They changed me and cursed me to", "remain here..."); - stage++; - break; - case 7: - options("What do you mean they changed you?", "What was the curse?", "Goodbye"); - stage++; - break; - case 8: - switch(buttonId) { - case 1: - npc("I was once a free man, powerful and wealthy. I owned", "several apothecaries across Zeah and sold the tastiest", "potions in the land."); - stage = 20; - break; - case 2: - npc("I have been charged to stay here to prevent the", "Monstrosity from escaping. Those gates and the", "winches that operate them are the only thing that stops", "it from breaking free."); - stage = 40; - break; - case 3: - player("Goodbye."); - stage = 50; - break; - } - break; - case 20: - player("What happened?"); - stage++; - break; - case 21: - npc("One of my greatest inventions, it was going so well. I", "spent days finding the right herbs, I travelled across all", "of Zeah to find the most exotic weeds. Once I had", "gathered them all, I put them in a potion and mixed in"); - stage++; - break; - case 22: - npc("the final ingredient."); - stage++; - break; - case 23: - player("Zeah? Interesting..."); - stage++; - break; - case 24: - npc("Yes, yes... The potion tasted delicious but it was missing", "a tiny something so I added Magic roots to the potion.", "It started to pulsate and glow! I took a sip and I felt", "like a million gold! A few seconds later my eye sight"); - stage++; - break; - case 25: - npc("began to blur, my brain was throbbing. I fell and hit", "my head on my worktop, then it all went black."); - stage++; - break; - case 26: - player("Ouch! But, you still haven't said who changed you?"); - stage++; - break; - case 27: - npc("Will you let me finish??"); - stage++; - break; - case 28: - npc("I woke up screaming in pain. Blue foam streaming out", "of my mouth, my eye sight worse than before, the only", "things I could make out were 3 tall figures with green", "banners - they were of the Archeus Elders. They"); - stage++; - break; - case 29: - npc("muttered to each other in Archaic Language. After", "trying to get up several times, I lost hope and stared at", "the sky. I had given up when the tallest of the figures", "bent over and brought his face right to mine and spoke"); - stage++; - break; - case 30: - npc("very softly, 'We can save you, but it will come at a", "cost'."); - stage++; - break; - case 31: - player("So they saved you?"); - stage++; - break; - case 32: - npc("Yes of course they saved me but look at the cost! I", "have horns coming out of my head and I'm still blind", "and stuck here... forever alone."); - stage = 7; - break; - case 40: - player("What monstrosity? What's behind those gates?"); - stage++; - break; - case 41: - npc("You really do not want to know. Leave this place", "human."); - stage++; - break; - case 42: - player("Hey now, I'm no wimp. What's to stop me from just", "turning the winch to open the gate and going in?"); - stage++; - break; - case 43: - npc("Me. And of course your quick demise at the mercy of", "Cerberus, guardian of the river of souls."); - stage++; - break; - case 44: - player("But I can do it!"); - stage++; - break; - case 45: - npc("You are obviously passionate at trying. But only those", "with great skill at slaying these types of beast may", "enter."); - stage++; - break; - case 46: - npc("THE VOICES! AAAHHHHH! Leave this place human!"); - stage = 7; - break; - case 50: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8673 }; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/KittenInteractDialogue.java b/Server/src/main/java/core/game/content/dialogue/KittenInteractDialogue.java deleted file mode 100644 index f9b024785..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KittenInteractDialogue.java +++ /dev/null @@ -1,138 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.RegionManager; -import core.game.world.map.path.Path; -import core.game.world.map.path.Pathfinder; -import core.plugin.Initializable; -import core.game.world.update.flag.context.Animation; - -/** - * Represents the dialogue plugin used for kitten interactions. - * @author Vexia - * @version 1.0 - */ -@Initializable -public final class KittenInteractDialogue extends DialoguePlugin { - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(827); - - /** - * Constructs a new {@code KittenInterfactDialogue} {@code Object}. - */ - public KittenInteractDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KittenInterfactDialogue} {@code Object}. - * @param player the player. - */ - public KittenInteractDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KittenInteractDialogue(player); - } - - @Override - public boolean open(Object... args) { - interpreter.sendOptions("Interact with Kitten", "Stroke", "Chase-Vermin", "Shoo-away."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - switch (buttonId) { - case 1:// stroke - player.animate(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 - end(); - player.sendChat("Go on puss...kill that rat!"); - boolean cant = true; - NPC rat = null; - for (NPC n : RegionManager.getLocalNpcs(player.getLocation(), 10)) { - if (!n.getName().contains("rat")) { - cant = false; - continue; - } - if (n.getLocation().getDistance(player.getFamiliarManager().getFamiliar().getLocation()) < 8) { - cant = true; - rat = n; - break; - } else { - cant = false; - } - } - if (!cant) { - player.getPacketDispatch().sendMessage("Your cat cannot get to its prey."); - } else { - player.getFamiliarManager().getFamiliar().sendChat("Meeeoooooowwww!"); - final Path path = Pathfinder.find(player.getFamiliarManager().getFamiliar(), rat); - path.walk(player.getFamiliarManager().getFamiliar()); - rat.sendChat("Eeek!"); - GameWorld.getPulser().submit(new Pulse(5) { - - @Override - public boolean pulse() { - player.getFamiliarManager().getFamiliar().call(); - player.getPacketDispatch().sendMessage("The rat manages to get away!"); - return true; - } - - }); - } - break; - case 3:// shoo-away - interpreter.sendOptions("Are you sure?", "Yes I am.", "No I'm not."); - stage = 560; - break; - } - break; - 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."); - end(); - break; - case 2:// no - end(); - break; - } - break; - case 99: - if (player.getFamiliarManager().hasFamiliar()) { - player.getFamiliarManager().getFamiliar().sendChat("Miaow!"); - } - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 343823 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/KlarenseDialogue.java b/Server/src/main/java/core/game/content/dialogue/KlarenseDialogue.java deleted file mode 100644 index dc7e3ed95..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KlarenseDialogue.java +++ /dev/null @@ -1,238 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the dialogue used for the klarense npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KlarenseDialogue extends DialoguePlugin { - - /** - * Represents the amount of coins. - */ - private static final Item COINS = new Item(995, 2000); - - /** - * Represents the quest instance. - */ - private Quest quest; - - /** - * Constructs a new {@code KlarenseDialogue} {@code Object}. - */ - public KlarenseDialogue() { - /** - * emptpy. - */ - } - - /** - * Constructs a new {@code KlarenseDialogue} {@code Object}. - * @param player the player. - */ - public KlarenseDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KlarenseDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Dragon Slayer"); - if (args.length > 1) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hey, stay off my ship! That's private property!"); - stage = 0; - return true; - } - switch (quest.getStage(player)) { - case 100: - player("Hey, that's my ship!"); - stage = -1; - 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."); - 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."); - stage = 1; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (stage == 400) { - end(); - return true; - } - switch (quest.getStage(player)) { - case 100: - switch (stage) { - case 0: - 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."); - stage = 1; - break; - case 1: - player("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."); - stage = 3; - break; - case 3: - end(); - break; - } - break; - case 20: - switch (stage) { - case 0: - end(); - break; - case 1: - player("Would you take me to Crandor when she's ready?"); - stage = 2; - break; - case 2: - npc("Crandor? You're joking, right?"); - stage = 3; - break; - case 3: - player("No. I want to go to Crandor."); - stage = 4; - break; - case 4: - npc("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"); - 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."); - stage = 7; - break; - case 7: - player("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."); - 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!"); - stage = 10; - break; - case 10: - player("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..."); - stage = 12; - } else { - if (!player.getInventory().containsItem(COINS)) { - end(); - return false; - } - if (player.getInventory().remove(COINS)) { - npc("Okey dokey, she's all yours!"); - player.getSavedData().getQuestData().setDragonSlayerAttribute("ship", true); - stage = 12; - } - } - break; - case 12: - end(); - break; - } - break; - default: - switch (stage) { - case 0: - end(); - break; - case 1: - interpreter.sendOptions("Select an Option", "Do you know when she will be seaworthy?", "Why is she damaged?", "Ah, well, never mind."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you know when she will be seaworthy?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why is she damaged?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ah, well, never mind."); - stage = 30; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No, not really. Port Sarim's shipbuilders aren't very", "efficient so it could be quite a while."); - stage = 11; - break; - case 11: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh, there was no particular accident. It's just years of", "wear and tear."); - 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."); - 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,"); - stage = 23; - break; - case 23: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "but once she's patched up, she'll be good as new!"); - stage = 24; - break; - case 24: - end(); - break; - case 30: - end(); - break; - case 400: - end(); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 744 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/KreeJrDialogue.java b/Server/src/main/java/core/game/content/dialogue/KreeJrDialogue.java deleted file mode 100644 index e0b354ae6..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KreeJrDialogue.java +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, Kree'Arra JR. - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class KreeJrDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code KreeJrDialogue} {@code Object}. - */ - public KreeJrDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KreeJrDialogue} {@code Object}. - * @param player the player. - */ - public KreeJrDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KreeJrDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Huh... that's odd... I thought that would", "be big news."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You thought what would be big news?"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well there seems to be an absence of a certain", " ornithological piece: a headline regarding mass", "awareness of a certain avian variety."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "What are you talking about?"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh have you not heard? It was my understanding that", "everyone had heard...."); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Heard wha...... OH NO!!!!?!?!!?!"); - stage = 5; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "OH WELL THE BIRD, BIRD, BIRD, BIRD", "BIRD IS THE WORD.", "OH WELL THE BIRD, BIRD, BIRD,", "BIRD BIRD IS THE WORD."); - stage = 6; - break; - case 6: - interpreter.sendDialogue("There's a slight pause as Kree'Arra Jr. goes stiff."); - stage = 7; - break; - case 7: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8592 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/KrilJRDialogue.java b/Server/src/main/java/core/game/content/dialogue/KrilJRDialogue.java deleted file mode 100644 index e5ef69164..000000000 --- a/Server/src/main/java/core/game/content/dialogue/KrilJRDialogue.java +++ /dev/null @@ -1,83 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, K'ril Tsutsaroth JR. - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class KrilJRDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code KrilJRDialogue} {@code Object}. - */ - public KrilJRDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KrilJRDialogue} {@code Object}. - * @param player the player. - */ - public KrilJRDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KrilJRDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How's life in the light?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Burns slightly."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You seem much nicer than your father. He's mean."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "If you were stuck in a very dark cave for centuries", "you'd be pretty annoyed too."); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I guess."); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "He's actually quite mellow really."); - stage = 5; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh.... Yeah."); - stage = 6; - break; - case 6: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8591 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/LarxusDialogue.java b/Server/src/main/java/core/game/content/dialogue/LarxusDialogue.java deleted file mode 100644 index 541073dba..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LarxusDialogue.java +++ /dev/null @@ -1,98 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/LeonDialogue.java b/Server/src/main/java/core/game/content/dialogue/LeonDialogue.java deleted file mode 100644 index 04e126753..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LeonDialogue.java +++ /dev/null @@ -1,94 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/LidioDialogue.java b/Server/src/main/java/core/game/content/dialogue/LidioDialogue.java deleted file mode 100644 index 1fb7b1d7b..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LidioDialogue.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/LillyDialogue.java b/Server/src/main/java/core/game/content/dialogue/LillyDialogue.java deleted file mode 100644 index 547a45e2b..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LillyDialogue.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/LimbDialogue.java b/Server/src/main/java/core/game/content/dialogue/LimbDialogue.java deleted file mode 100644 index 691ce9a46..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LimbDialogue.java +++ /dev/null @@ -1,124 +0,0 @@ -/* -package core.game.content.dialogue; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.fletching.items.crossbow.Limb; -import core.game.node.entity.skill.fletching.items.crossbow.LimbPulse; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.entity.player.link.RunScript; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.game.node.item.Item; - -*/ -/** - * Represents the dialogue plugin used for making limbs. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class LimbDialogue extends DialoguePlugin { - - */ -/** - * Constructs a new {@code LimbDialogue} {@code Object}. - *//* - - public LimbDialogue() { - */ -/** - * empty. - *//* - - } - - */ -/** - * Constructs a new {@code LimbDialogue} {@code Object}. - * @param player the player. - *//* - - public LimbDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LimbDialogue(player); - } - - */ -/** - * The item. - *//* - - private Item item; - - */ -/** - * The second item. - *//* - - private Item second; - - */ -/** - * The string bow enum. - *//* - - private Limb bow; - - @Override - public boolean open(Object... args) { - item = (Item) args[0]; - second = (Item) args[1]; - bow = Limb.forItems(item, second); - if (bow == null) { - return true; - } - player.getInterfaceManager().openChatbox(309); - player.getPacketDispatch().sendString("



" + bow.getProduct().getName(), 309, 6); - player.getPacketDispatch().sendItemZoomOnInterface(bow.getProduct().getId(), 160, 309, 2); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - end(); - switch (stage) { - case 0: - int amt = 0; - switch (buttonId) { - case 6: - amt = 1; - break; - case 5: - amt = 5; - break; - case 4: - player.setAttribute("runscript", new RunScript() { - @Override - public boolean handle() { - int ammount = (int) value; - player.getPulseManager().run(new LimbPulse(player, (item.getName().toLowerCase().contains("stock") ? second : item), bow, ammount)); - return false; - } - }); - player.getDialogueInterpreter().sendInput(false, "Enter the amount"); - return true; - case 3: - amt = player.getInventory().getAmount(item); - break; - } - player.getPulseManager().run(new LimbPulse(player, (item.getName().toLowerCase().contains("stock") ? second : item), bow, amt)); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 84923 }; - } -} -*/ diff --git a/Server/src/main/java/core/game/content/dialogue/LouiLegsDialogue.java b/Server/src/main/java/core/game/content/dialogue/LouiLegsDialogue.java deleted file mode 100644 index 080bab582..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LouiLegsDialogue.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the plugin used for loui legs. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LouiLegsDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code LouiLegsDialogue} {@code Object}. - */ - public LouiLegsDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LouiLegsDialogue} {@code Object}. - * @param player the player. - */ - public LouiLegsDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LouiLegsDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hey, wanna buy some armour?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "What have you got?", "No, thank you."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What have you got?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thank you."); - stage = 20; - break; - - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I provide items to help you keep your legs!"); - stage = 11; - break; - case 11: - end(); - npc.openShop(player); - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 542 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/LoweDialogue.java b/Server/src/main/java/core/game/content/dialogue/LoweDialogue.java deleted file mode 100644 index b113c9e8c..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LoweDialogue.java +++ /dev/null @@ -1,80 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represesents the dialogue plugin used for lowe. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LoweDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code LoweDialogue} {@code Object}. - */ - public LoweDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LoweDialogue} {@code Object}. - * @param player the player. - */ - public LoweDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LoweDialogue(player); - } - - @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?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, please.", "No, I prefer to bash things close up."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I prefer to bash things close up."); - stage = 3; - break; - } - - break; - case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Humph, philistine."); - stage = 4; - break; - case 4: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 550 }; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/LumbridgeGuideDialogue.java b/Server/src/main/java/core/game/content/dialogue/LumbridgeGuideDialogue.java deleted file mode 100644 index 8bea22676..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LumbridgeGuideDialogue.java +++ /dev/null @@ -1,290 +0,0 @@ -package core.game.content.dialogue; - -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.player.link.IronmanMode; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.world.map.zone.impl.ModeratorZone; -import core.net.amsc.MSPacketRepository; -import core.plugin.Initializable; -import rs09.game.world.GameWorld; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents the lumbridge guide dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LumbridgeGuideDialogue extends DialoguePlugin { - - /** - * The list of options. - */ - private List options; - - /** - * Constructs a new {@code LumbridgeGuideDialogue} {@code Object}. - */ - public LumbridgeGuideDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LumbridgeGuideDialogue} {@code Object}. - * @param player the player. - */ - public LumbridgeGuideDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LumbridgeGuideDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Greetings, adventurer. I am Phileas, the Lumbridge", "Guide. I am here to give information and directions to", "new players. Do you require any help?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (!player.isStaff() && !player.getIronmanManager().isIronman()) { - options("Where can I find a quest to go on?", "What monsters should I fight?", "Where can I make money?", "I'd like to know more about security.", "Where can I find a bank?"); - stage = 103; - return true; - } - options = new ArrayList<>(5); - options.add("Yes please."); - if (player.getRights() == Rights.PLAYER_MODERATOR && ModeratorZone.isOpen() || player.isAdmin()) { - options.add("I would like to access the P-Mod room."); - } - if (player.getIronmanManager().isIronman()) { - options.add("Would you like to remove Ironman Mode?"); - } - options(options.toArray(new String[] {})); - stage = 900; - break; - case 900: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Yes please."); - stage = 102; - break; - case 2: - if (options.get(1).contains("remove")) { - options("Yes, please.", "No."); - stage = 804; - break; - } - if (options.get(1).contains("room")) { - stage = 690; - npc("Yes, of course."); - } else { - stage = 901; - npc("Yes, of course."); - } - break; - case 3: - if (options.get(2).contains("remove")) { - options("Yes, please.", "No."); - stage = 804; - break; - } - stage = 901; - npc("Yes, of course."); - break; - } - break; - case 804: - if (buttonId == 1) { - player("Yes, please."); - stage = 806; - } else { - player("No."); - stage = 805; - } - break; - case 805: - end(); - break; - case 806: - npc("You are no longer an Ironman."); - stage++; - break; - case 807: - player.getIronmanManager().setMode(IronmanMode.NONE); - MSPacketRepository.sendInfoUpdate(player); - end(); - break; - case 10: - options("Tell me about the town of Lumbridge.", "I'm fine for now, thanks."); - stage = 11; - break; - case 1: - case 11: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Yes please."); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, null, "No, I can find things myself thank you."); - stage = 200; - break; - } - break; - case 200: - end(); - break; - case 100: - npc("First I must warn you to take every precaution to", "keep your " + GameWorld.getSettings().getName() + " password and PIN secure. The", "most important thing to remember is to never give your", "password to, or share you account with, anyone."); - stage = 101; - break; - case 101: - npc("I have much more information to impart; what would", "you like to know about?"); - stage = 102; - break; - case 102: - options("Where can I find a quest to go on?", "What monsters should I fight?", "Where can I make money?", "I'd like to know more about security.", "Where can I find a bank?"); - stage = 103; - break; - case 103: - switch (buttonId) { - case 1: - player("Where can I find a quest to go on?"); - stage = 1100; - break; - case 2: - player("What monsters should I fight?"); - stage = 1200; - break; - case 3: - player("Where can I make money?"); - stage = 1300; - break; - case 4: - player("I'd like to know more about security."); - stage = 1400; - break; - case 5: - player("Where can I find a bank?"); - stage = 1500; - break; - } - break; - case 1400: - npc("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"); - stage = 1401; - break; - case 1401: - npc("bit of an adventure at the same time."); - stage = 1402; - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 17); - break; - case 1402: - end(); - break; - case 1300: - npc("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."); - stage = 1301; - break; - case 1301: - npc("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."); - stage = 1302; - break; - case 1302: - end(); - break; - case 1200: - npc("You're strong enough to work out what monsters to", "fight for yourself now, but the combat tutors might help", "you with any questions you have about the skills;", "they're just over there to the south of the general store."); - stage = 1201; - break; - case 1201: - npc("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.", "Non-player characters usually appear as yellow dots on"); - stage = 1202; - break; - case 1202: - npc("your mini-map, although there are some that you won't", "be able to fight, such as myself. If you explore further", "afield you may be able to find areas that are less", "crowded, but watch out for monsters which are tougher"); - stage = 1203; - break; - case 1203: - npc("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 that the monster is tougher than you."); - stage = 1204; - break; - case 1204: - npc("Remember, you wil do better if you have better", "armour and weapons and it's always worth carrying a", "bit of food to heal yourself."); - stage = 1205; - break; - case 1205: - end(); - break; - case 1500: - npc("You'll find a bank upstairs in Lumbridge Castle - go", "right to the top."); - stage = 1501; - break; - case 1501: - end(); - break; - case 1100: - stage = 1101; - if (!player.getQuestRepository().isComplete("Cook's Assistant")) { - npc("You can try talking to the Cook in the Lumbridge Castle", "I hear he is always looking for some help."); - return true; - } - if (!player.getQuestRepository().isComplete("Sheep Shearer")) { - npc("You can try talking to Fred the Farmer north-west of here", "I hear he is always looking for some help."); - return true; - } - interpreter.sendDialogues(npc, null, "You are such an accomplished adventurer already; you", "should be telling me some good quests to go on."); - break; - case 1101: - end(); - break; - case 104: - end(); - break; - case 680: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Yes please."); - stage = 100; - break; - case 2: - stage = 690; - npc("Yes, of course."); - break; - case 3: - interpreter.sendDialogues(player, null, "No, I can find things myself thank you."); - stage = 200; - break; - } - break; - case 690: - end(); - if (ModeratorZone.isOpen() && player.getDetails().getRights() == Rights.PLAYER_MODERATOR) { - ModeratorZone.teleport(player); - } else if (player.getDetails().getRights() == Rights.ADMINISTRATOR) { - ModeratorZone.teleport(player); - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2244 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/LuthasDialogue.java b/Server/src/main/java/core/game/content/dialogue/LuthasDialogue.java deleted file mode 100644 index bcf330a0e..000000000 --- a/Server/src/main/java/core/game/content/dialogue/LuthasDialogue.java +++ /dev/null @@ -1,144 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItem; -import core.game.node.item.GroundItemManager; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the dialogue plugin used for the luthas npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LuthasDialogue extends DialoguePlugin { - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 30); - - /** - * Constructs a new {@code LuthasDialogue} {@code Object}. - */ - public LuthasDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LuthasDialogue} {@code Object}. - * @param player the player. - */ - public LuthasDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LuthasDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - 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."); - stage = 905; - return true; - } - interpreter.sendDialogues(npc, null, "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."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Could you offer me employment on your plantation?", "That customs officer is annoying isn't he?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - 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."); - 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.."); - 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."); - stage = 13; - player.getSavedData().getGlobalData().setLuthasTask(true); - break; - case 13: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I don't know about that."); - stage = 21; - break; - case 21: - end(); - break; - case 900: - int amt = player.getSavedData().getGlobalData().getKaramjaBananas(); - if (amt < 30) { - interpreter.sendDialogues(player, null, "No, the crate isn't full yet."); - stage = 901; - } else { - - } - break; - case 901: - interpreter.sendDialogues(npc, null, "Well come back when it is."); - stage = 21; - break; - case 905: - interpreter.sendDialogues(npc, null, "Well done, here's your payment."); - stage = 906; - break; - case 906: - end(); - player.getPacketDispatch().sendMessage("Luthas hands you 30 coins."); - player.getSavedData().getGlobalData().setKaramjaBannanas(0); - player.getSavedData().getGlobalData().setLuthasTask(false); - if (player.getAttribute("stashed-rum", false)) { - player.removeAttribute("stashed-rum"); - player.setAttribute("/save:wydin-rum", true); - } - if (!player.getInventory().add(COINS)) { - GroundItemManager.create(new GroundItem(COINS, player.getLocation(), player)); - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 379 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/MakeOverMageDialogue.java b/Server/src/main/java/core/game/content/dialogue/MakeOverMageDialogue.java deleted file mode 100644 index 194fa2f90..000000000 --- a/Server/src/main/java/core/game/content/dialogue/MakeOverMageDialogue.java +++ /dev/null @@ -1,227 +0,0 @@ -package core.game.content.dialogue; - -import core.game.component.Component; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Handles the MakeOverMageDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class MakeOverMageDialogue extends DialoguePlugin { - - public MakeOverMageDialogue() { - - } - - public MakeOverMageDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new MakeOverMageDialogue(player); - } - - @Override - public boolean open(Object... args) { - 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?"); - 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!"); - stage = 0; - return true; - } - - @Override - 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?"); - stage = 1; - break; - case 1: - if (!player.getInventory().contains(7803, 1) && !player.getBank().contains(7803, 1) && !player.getEquipment().contains(7803, 1)) { - interpreter.sendOptions("Select an Option", "Tell me more about this 'make-over'.", "Sure. Do it.", "No thanks.", "Cool amulet! Can I have one?"); - } else { - interpreter.sendOptions("Select an Option", "Tell me more about this 'make-over'.", "Sure. Do it.", "No thanks."); - } - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me more about this 'make-over'."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sure. Do it."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - 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."); - stage = 25; - break; - case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, go get it then. No freebies here!"); - stage = 22; - break; - case 22: - end(); - break; - case 25: - if(!player.getEquipment().isEmpty()){ - interpreter.sendDialogue("You need to take off all your equipment before the mage", "can change your appearance."); - stage = 900; - } else { - end(); - player.getInterfaceManager().open(new Component(205)); - } - 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,"); - 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."); - 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."); - stage = 13; - break; - case 13: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Or, you know, somewhere vaguely close enough", "anyway."); - stage = 14; - break; - case 14: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh... that doesn't sound particularly safe to me..."); - 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!"); - stage = 16; - break; - case 16: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "So what do you say? Feel like a change?"); - stage = 17; - break; - case 17: - interpreter.sendOptions("Select an Option", "Sure. Do it.", "No thanks."); - stage = 18; - break; - case 18: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sure. Do it."); - stage = 20; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks. I'm happy as Saradomin made me."); - stage = 19; - break; - } - break; - case 19: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ehhh... suit yourself."); - stage = 900; - break; - case 900: - 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."); - stage = 41; - break; - case 41: - interpreter.sendOptions("Select an Option", "Sure, here you go.", "No way! that's too expensive."); - stage = 42; - break; - case 42: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sure, here you go."); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No way! That's too expensive."); - stage = 400; - break; - - } - break; - case 400: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That's fair enough, my jewellery is not to everyone's", "taste."); - stage = 401; - break; - case 401: - end(); - break; - case 600: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh... No...?"); - stage = 601; - break; - case 601: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Good, good, I was worried for a second there!"); - stage = 602; - break; - case 602: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh... Thanks, I guess."); - stage = 603; - break; - case 603: - end(); - break; - case 100: - if (player.getInventory().freeSlots() == 0) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - } - if (!player.getInventory().contains(995, 100)) { - end(); - player.getPacketDispatch().sendMessage("You need 100 coins."); - } else { - // you recieve an amulet in exchange for 100 coins. - // 7803 - Item remove = new Item(995, 100); - if (!player.getInventory().containsItem(remove)) { - end(); - return true; - } - if (player.getInventory().remove(remove)) { - interpreter.sendItemMessage(7803, "You receive an amulet in exchange for 100 coins."); - player.getInventory().add(new Item(7803, 1)); - stage = 101; - } - } - break; - case 101: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2676, 599 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/MalakDialogue.java b/Server/src/main/java/core/game/content/dialogue/MalakDialogue.java deleted file mode 100644 index 88c016e5d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/MalakDialogue.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/MathiasFlaconryDialogue.java b/Server/src/main/java/core/game/content/dialogue/MathiasFlaconryDialogue.java deleted file mode 100644 index a028320a9..000000000 --- a/Server/src/main/java/core/game/content/dialogue/MathiasFlaconryDialogue.java +++ /dev/null @@ -1,157 +0,0 @@ -package core.game.content.dialogue; - -import core.game.container.impl.EquipmentContainer; -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; - -/** - * Handles the MathiasFlaconryDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class MathiasFlaconryDialogue extends DialoguePlugin { - - public MathiasFlaconryDialogue() { - } - - public MathiasFlaconryDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 5093 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 90: - interpreter.sendOptions("Select an Option", "Ok, that seems reasonable.", "I'm not interested then, thanks."); - stage = 91; - break; - case 91: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok, that seems reasonable."); - stage = 95; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm not interested then, thanks."); - stage = 323; - break; - - } - break; - case 323: - end(); - break; - case 95: - if (player.getBank().containsItem(FALCON) || player.getEquipment().containsItem(FALCON) || player.getInventory().containsItem(FALCON)) { - interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "You already have a falcon!"); - stage = 99; - 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."); - stage = 99; - break; - } - if (player.getInventory().contains(995, 500)) { - player.getInventory().remove(new Item(995, 500)); - player.getEquipment().add(new Item(10024), true, false); - interpreter.sendDialogue("The falconer gives you a large leather glove and brings one of the", "smaller birds over to land on it."); - stage = 97; - } else { - end(); - player.getPacketDispatch().sendMessage("You need 500 gold goins."); - } - 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."); - stage = 99; - break; - case 99: - end(); - break; - case 500: - interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "Greetings. Can I help you at all?"); - stage = 501; - break; - case 501: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Could I have a go with your bird?"); - stage = 502; - break; - case 502: - 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; - 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."); - stage = 90; - break; - case 900: - interpreter.sendOptions("Select an Option", "Yes, please.", "No thank you."); - stage = 901; - break; - case 901: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Yes, please."); - stage = 920; - break; - case 2: - end(); - break; - } - break; - case 920: - player.getBank().remove(FALCON, new Item(10023)); - player.getInventory().remove(new Item(10023), FALCON); - player.getEquipment().add(FALCON, true, false); - interpreter.sendDialogue("The falconer gives you a large leather glove and brings one of the", "smaller birds over to land on it."); - stage = 97; - break; - case 967: - end(); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MathiasFlaconryDialogue(player); - } - - private final Item FALCON = new Item(10024); - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - boolean quick = false; - if (player.getEquipment().contains(10023, 1)) { - interpreter.sendDialogues(5093, null, "Oh, it looks like you've lost your falcon.", "Would you like a new one?"); - stage = 900; - return true; - } - if (args.length == 2) - quick = true; - if (quick) { - interpreter.sendDialogues(5093, FacialExpression.HALF_GUILTY, "If you wish to try falconry, I request a small fee. How", "does 500 gold coins sound?"); - stage = 90; - return true; - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there."); - stage = 500; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/MazionDialogue.java b/Server/src/main/java/core/game/content/dialogue/MazionDialogue.java deleted file mode 100644 index ad36349af..000000000 --- a/Server/src/main/java/core/game/content/dialogue/MazionDialogue.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue used for the mazion npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MazionDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code MazionDialogue} {@code Object}. - */ - public MazionDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MazionDialogue} {@code Object}. - * @param player the player. - */ - public MazionDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MazionDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Please leave me along, a parrot stole my banana."); - 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[] { 3114 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/MeleeShopDialoguePlugin.java b/Server/src/main/java/core/game/content/dialogue/MeleeShopDialoguePlugin.java deleted file mode 100644 index e69a510fd..000000000 --- a/Server/src/main/java/core/game/content/dialogue/MeleeShopDialoguePlugin.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.dialogue; - -import core.game.content.global.shop.Shop; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; - -/** - * Package -> core.game.content.dialogue - * Created on -> 9/10/2016 @10:33 PM for 530 - * - * @author Ethan Kyle Millard - */ -@Initializable -public class MeleeShopDialoguePlugin extends DialoguePlugin { - - private Shop shop; - - - public MeleeShopDialoguePlugin() { - /** - * Empty - */ - } - - public MeleeShopDialoguePlugin(Player player) { - super(player); - } - @Override - public DialoguePlugin newInstance(Player player) { - return new MeleeShopDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - options("Melee Shop 1", "Melee Shop 2"); - stage = 0; - return false; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch(stage) { - case 0: - switch (buttonId) { - case 1: - npc.openShop(player); - break; - case 2: - break; - } - break; - } - return true; - } - - - @Override - public int[] getIds() { - return new int[] { 5503 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/MiningGuildDwarf.java b/Server/src/main/java/core/game/content/dialogue/MiningGuildDwarf.java deleted file mode 100644 index 36906db41..000000000 --- a/Server/src/main/java/core/game/content/dialogue/MiningGuildDwarf.java +++ /dev/null @@ -1,133 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the mining guild dwarf. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MiningGuildDwarf extends DialoguePlugin { - - /** - * Constructs a new {@code MiningGuildDwarf} {@code Object}. - */ - public MiningGuildDwarf() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MiningGuildDwarf} {@code Object}. - * @param player the player. - */ - public MiningGuildDwarf(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MiningGuildDwarf(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (args.length == 2) { - if (player.getSkills().getStaticLevel(Skills.MINING) < 60) { - interpreter.sendDialogues(npc, null, "Sorry, but you need level 60 Mining to go in there."); - stage = 69; - return true; - } - } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to the Mining Guild.", "Can I help you with anything?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 69: - 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."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 11; - break; - case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 13; - } else { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, you can enter if you wish."); - stage = 14; - } - break; - case 13: - end(); - break; - case 14: - 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."); - stage = 21; - break; - case 21: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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"); - 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."); - stage = 24; - break; - case 24: - end(); - break; - case 30: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 382 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/MonkeyDialogue.java b/Server/src/main/java/core/game/content/dialogue/MonkeyDialogue.java deleted file mode 100644 index a6d1df32d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/MonkeyDialogue.java +++ /dev/null @@ -1,60 +0,0 @@ -package core.game.content.dialogue; - -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("Who's a cute little monkey?"); - 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: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2301 }; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/NikitiaDialogue.java b/Server/src/main/java/core/game/content/dialogue/NikitiaDialogue.java deleted file mode 100644 index f1a168089..000000000 --- a/Server/src/main/java/core/game/content/dialogue/NikitiaDialogue.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/NurmofDialogue.java b/Server/src/main/java/core/game/content/dialogue/NurmofDialogue.java deleted file mode 100644 index 32f2f67bd..000000000 --- a/Server/src/main/java/core/game/content/dialogue/NurmofDialogue.java +++ /dev/null @@ -1,62 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the NurmofDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class NurmofDialogue extends DialoguePlugin { - - public NurmofDialogue() { - - } - - public NurmofDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new NurmofDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings and welcome to my pickaxe shop. Do you", "want to buy my premium quality pickaxes?"); - 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: - end(); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 594 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/OcaDialogue.java b/Server/src/main/java/core/game/content/dialogue/OcaDialogue.java deleted file mode 100644 index 6a3980428..000000000 --- a/Server/src/main/java/core/game/content/dialogue/OcaDialogue.java +++ /dev/null @@ -1,54 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/OliviaDialogue.java b/Server/src/main/java/core/game/content/dialogue/OliviaDialogue.java deleted file mode 100644 index 4987e797b..000000000 --- a/Server/src/main/java/core/game/content/dialogue/OliviaDialogue.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the OliviaDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class OliviaDialogue extends DialoguePlugin { - - public OliviaDialogue() { - - } - - public OliviaDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 2233, 2572 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes", "No", "Where do I get rarer seeds from?"); - 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; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where do I get rarer seeds from?"); - stage = 40; - break; - - } - break; - case 20: - 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."); - stage = 41; - break; - case 41: - end(); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new OliviaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to trade in seeds?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/PeksaDialogue.java b/Server/src/main/java/core/game/content/dialogue/PeksaDialogue.java deleted file mode 100644 index 18abcbe4a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PeksaDialogue.java +++ /dev/null @@ -1,80 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/PendaDialogue.java b/Server/src/main/java/core/game/content/dialogue/PendaDialogue.java deleted file mode 100644 index d0508bcd0..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PendaDialogue.java +++ /dev/null @@ -1,54 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/PetshopOwnerTaverly.java b/Server/src/main/java/core/game/content/dialogue/PetshopOwnerTaverly.java deleted file mode 100644 index 4895dc09d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PetshopOwnerTaverly.java +++ /dev/null @@ -1,207 +0,0 @@ -package core.game.content.dialogue; - -import java.util.List; - -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.game.world.map.RegionManager; - -/** - * Represents the dialogue for the taverly pet shop owner. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class PetshopOwnerTaverly extends DialoguePlugin { - - /** - * Represents the dog name. - */ - private String dogName; - - /** - * Represents the puppy. - */ - private Item puppy; - - /** - * Constructs a new {@code PetshopOwnerTaverly} {@code Object}. - */ - public PetshopOwnerTaverly() { - /** - * emppty. - */ - } - - /** - * Constructs a new {@code PetshopOwnerTaverly} {@code Object}. - * @param player the player. - */ - public PetshopOwnerTaverly(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PetshopOwnerTaverly(player); - } - - @Override - public boolean open(Object... args) { - if (args.length > 1) { - List npcs = RegionManager.getLocalNpcs(player); - for (NPC n : npcs) { - if (n.getId() == 6893) { - npc = n; - break; - } - } - dogName = (String) args[0]; - puppy = (Item) args[1]; - player("No, the " + dogName + "."); - stage = 699; - return true; - } - npc = (NPC) args[0]; - interpreter.sendOptions("Choose an option:", "Can I see your shop, please?", "How much is that puppy in the window?", "So, what sorts of pets are available?"); - 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, "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?"); - stage = 200; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So, what sorts of pets are available?"); - stage = 300; - break; - } - break; - case 100: - end(); - npc.openShop(player); - break; - case 200: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The one with the waggly tail?"); - stage = 201; - break; - case 201: - end(); - player.getInterfaceManager().openChatbox(668); - stage = 202; - break; - 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."); - stage = 301; - break; - case 301: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 303; - break; - case 303: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 305; - break; - case 305: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 307; - break; - case 307: - end(); - break; - case 700: - player("Isn't that a little steep?"); - stage = 701; - break; - case 701: - npc("Well, if we gave them away for free then people would", "just buy them and dump them without a care."); - stage = 702; - break; - case 702: - npc("Dogs are a big responsibility and should be cared for."); - stage = 703; - break; - case 703: - npc("If a person in unwilling to invest 500 coins, then they", "don't deserve to have the puppy in the first place."); - stage = 704; - break; - case 704: - npc("So, do you still want one?"); - stage = 705; - break; - case 705: - options("Okay, I'll take the " + dogName + ".", "No thanks."); - stage = 706; - break; - case 706: - switch (buttonId) { - case 1: - player("Okay, I'll take the " + dogName + "."); - stage = 707; - break; - case 2: - end(); - break; - } - break; - case 707: - if (player.getInventory().freeSlots() == 0) { - npc("You don't have enough inventory space."); - stage = 708; - return true; - } - if (!player.getInventory().containsItem(new Item(995, 500))) { - end(); - return true; - } - if (player.getInventory().remove(new Item(995, 500))) { - player.getInventory().add(puppy); - npc("There you go! I hope you two get on."); - stage = 708; - } else { - player.getPacketDispatch().sendMessage("You don't the required coins in order to do this."); - end(); - } - break; - case 708: - end(); - break; - case 699: - npc("500 gold."); - stage = 700; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6893 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/PhantuwtiFantstuwiFarSight.java b/Server/src/main/java/core/game/content/dialogue/PhantuwtiFantstuwiFarSight.java deleted file mode 100644 index f5fcd18ef..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PhantuwtiFantstuwiFarSight.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the PhantuwtiFantstuwiFarSight dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class PhantuwtiFantstuwiFarSight extends DialoguePlugin { - - /** - * Constructs a new {@code PhantuwtiFantstuwiFarSight} {@code Object}. - */ - public PhantuwtiFantstuwiFarSight() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code PhantuwtiFantstuwiFarSight} {@code Object}. - * @param player the player. - */ - public PhantuwtiFantstuwiFarSight(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PhantuwtiFantstuwiFarSight(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendOptions("Select an Option", "Hello, what is this place?", "What do you do here?", "Do you have any quests?", "Ok, thanks."); - stage = 0; - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1798 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, what is this place?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do you do here?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you have any quests?"); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 11; - break; - case 11: - interpreter.sendOptions("Select an Option", "Hello, what is this place?", "What do you do here?", "Do you have any quests?", "Ok, thanks."); - 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."); - stage = 31; - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Unfortunately no, sorry, but if adventure is what you", "seek, try checking through your quest list!"); - stage = 31; - break; - case 31: - interpreter.sendOptions("Select an Option", "Hello, what is this place?", "What do you do here?", "Do you have any quests?", "Ok, thanks."); - stage = 0; - break; - case 40: - end(); - break; - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/PhilopDialogue.java b/Server/src/main/java/core/game/content/dialogue/PhilopDialogue.java deleted file mode 100644 index bc564055b..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PhilopDialogue.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the PhilopDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class PhilopDialogue extends DialoguePlugin { - - public PhilopDialogue() { - - } - - public PhilopDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 782 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Gwwrr!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err, hello there. What's that you have there?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Gwwwrrr! Dwa-gon Gwwwwrrrr!"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Enjoy playing with your dragon, then."); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Gwwwrrr!"); - stage = 5; - break; - case 5: - end(); - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new PhilopDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, what's your name?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/PirateJackieDialogue.java b/Server/src/main/java/core/game/content/dialogue/PirateJackieDialogue.java deleted file mode 100644 index 646595681..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PirateJackieDialogue.java +++ /dev/null @@ -1,230 +0,0 @@ -package core.game.content.dialogue; - -import core.game.component.Component; -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; -import core.game.node.item.Item; - -/** - * 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); - - /** - * The achievement diary. - */ - private AchievementDiary diary; - - /** - * 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 (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.KARAMJA); - } - if (diary.isComplete(0) && !diary.isLevelRewarded(0)) { - player("I've done all the easy tasks in my Karamja Achievement", "Diary."); - stage = 440; - break; - } - if (diary.isLevelRewarded(0) && diary.isComplete(0) && !player.hasItem(diary.getType().getRewards(0)[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: - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.KARAMJA); - } - 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: - diary.setLevelRewarded(0); - for (Item i : diary.getType().getRewards(0)) { - player.getInventory().add(i, player); - } - 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: - player.getInventory().add(diary.getType().getRewards(0)[0], player); - 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/java/core/game/content/dialogue/PortSarimBartenderDialogue.java b/Server/src/main/java/core/game/content/dialogue/PortSarimBartenderDialogue.java deleted file mode 100644 index e3ff6b50b..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PortSarimBartenderDialogue.java +++ /dev/null @@ -1,99 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Handles the PortSarimBartenderDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class PortSarimBartenderDialogue extends DialoguePlugin { - - public PortSarimBartenderDialogue() { - - } - - public PortSarimBartenderDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 734 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello there!"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Choose an option:", "Could I buy a beer, please.", "Bye, then."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Could I buy a beer, please?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Bye, then."); - stage = 20; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Sure, that will be two gold coins, please."); - stage = 11; - break; - case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, here you go."); - stage = 12; - break; - case 12: - if (player.getInventory().contains(995, 2)) { - interpreter.sendDialogue("You buy a pint of beer."); - player.getInventory().remove(new Item(995, 2)); - player.getInventory().add(new Item(1917, 1)); - stage = 13; - } else { - player.getPacketDispatch().sendMessage("You need 2 gold coins to buy beer."); - end(); - } - break; - case 13: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Come back soon!"); - stage = 21; - break; - case 21: - end(); - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new PortSarimBartenderDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Good day to you!"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/PortSarimBrianDialogue.java b/Server/src/main/java/core/game/content/dialogue/PortSarimBrianDialogue.java deleted file mode 100644 index 8c901d24f..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PortSarimBrianDialogue.java +++ /dev/null @@ -1,74 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the PortSarimBrianDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class PortSarimBrianDialogue extends DialoguePlugin { - - public PortSarimBrianDialogue() { - - } - - public PortSarimBrianDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 559 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So, are you selling something?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "'Ello."); - stage = 20; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yep, take a look at these great axes."); - stage = 11; - break; - case 11: - end(); - npc.openShop(player); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "'Ello."); - stage = 21; - break; - case 21: - end(); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new PortSarimBrianDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendOptions("Select an Option", "So, are you selling something?", "'Ello."); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/PrayerTutorDialogue.java b/Server/src/main/java/core/game/content/dialogue/PrayerTutorDialogue.java deleted file mode 100644 index 1c11daecc..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PrayerTutorDialogue.java +++ /dev/null @@ -1,117 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the PrayerTutorDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class PrayerTutorDialogue extends DialoguePlugin { - - public PrayerTutorDialogue() { - - } - - public PrayerTutorDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new PrayerTutorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendOptions("Select an Option", "I already know about the basic prayers, got any tips?", "Tell me about different bones.", "Goodbye."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - 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."); - stage = 14; - break; - case 14: - interpreter.sendOptions("Select an Option", "I already know about the basic prayers, got any tips?", "Tell me about different bones.", "Goodbye."); - stage = 0; - break; - case 20: - interpreter.sendOptions("Select an Option", "Basic Bones", "Big Bones", "Babydragon Bones", "Goodbye."); - stage = 21; - break; - case 21: - switch (buttonId) { - case 1: - interpreter.sendItemMessage(526, "Basic bones are left by many creatures such as goblins, monkeys and that sort of thing. They won't get you much when you burry them, but if you do it every time you come across them, it all adds up!"); - stage = 20; - break; - case 2: - interpreter.sendItemMessage(532, "Big bones you can get by killing things like ogres and giants, them being big things and all. They're quite a good boost for your prayer if you are up to fighting the big boys."); - stage = 220; - break; - case 3: - interpreter.sendItemMessage(536, "If you're feeling adventurous and up to tackling baby dragons, you can get these Baby Dragon bones which are even better than big bones."); - stage = 20; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Goodbye."); - stage = 40; - break; - } - break; - case 220: - interpreter.sendItemMessage(532, "You can probably find them in caves and such dark dank places."); - stage = 20; - break; - case 40: - end(); - break; - case 30: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4903 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/PrinceBlackDragonDialogue.java b/Server/src/main/java/core/game/content/dialogue/PrinceBlackDragonDialogue.java deleted file mode 100644 index 59fbd9b35..000000000 --- a/Server/src/main/java/core/game/content/dialogue/PrinceBlackDragonDialogue.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, KBD JR - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class PrinceBlackDragonDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - */ - public PrinceBlackDragonDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - * @param player the player. - */ - public PrinceBlackDragonDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PrinceBlackDragonDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Shouldn't a prince only have two heads?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Why is that?"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, a standard Black dragon has one,", "the King has three so inbetween must have two?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You're overthinking this."); - stage = 3; - break; - case 3: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8596 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/ReadbeardFrankDialogue.java b/Server/src/main/java/core/game/content/dialogue/ReadbeardFrankDialogue.java deleted file mode 100644 index 10fb6eb9f..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ReadbeardFrankDialogue.java +++ /dev/null @@ -1,513 +0,0 @@ -package core.game.content.dialogue; - -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.entity.player.link.quest.Quest; -import core.game.node.item.GroundItemManager; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the dialogue to handle Rebeard Frank. - * - * @author afaroutdude - */ -@Initializable -public class ReadbeardFrankDialogue extends DialoguePlugin { - private boolean replacementReward = false; - private AchievementDiary diary; - private int level = 0; - - /** - * Represents the karamjan rum item. - */ - private static final Item KARAMJAN_RUM = new Item(431); - - /** - * Represents the chest key item. - */ - private static final Item KEY = new Item(432); - - /** - * Represents this quest instance. - */ - private Quest quest; - - /** - * Constructs a new {@code ReadbeardFrankDialogue} {@code Object}. - */ - public ReadbeardFrankDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ReadbeardFrankDialogue} {@code Object}. - * - * @param player the player. - */ - public ReadbeardFrankDialogue(Player player) { - super(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Pirate's Treasure"); - npc("Arr, Matey!"); - stage = 0; - diary = player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR); - replacementReward = diary.isLevelRewarded(level) - && diary.isComplete(level, true) - && !player.hasItem(diary.getType().getRewards(level)[0]); - return true; - } - - // https://www.youtube.com/watch?v=U_sNcqQ2dtQ - achievement diary branch - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (quest.getStage(player) == 20 && !player.getInventory().containsItem(KEY) && !player.getBank().containsItem(KEY)) { - player("I seem to have lost my chest key..."); - stage = 700; - return true; - } - if (quest.getStage(player) == 10) { - npc("Have ye brought some rum for yer ol' mate Frank?"); - stage = 20; - break; - } - if (quest.getStage(player) == 0) { - options("I'm in search of treasure.", "Arr!", "Do you have anything for trade?", "Tell me about the Falador Achievement Diary."); - stage = 11; - break; - } - options("Arr!", "Do you have anything for trade?", "Tell me about the Falador Achievement Diary."); - stage = 10; - break; - case 10: - switch (buttonId) { - case 1: - player("Arr!"); - stage = 12; - break; - case 2: - player("Do you have anything for trade?"); - stage = 13; - break; - case 3: - player("Tell me about the Falador Achievement Diary."); - stage = 80; - break; - } - break; - case 11: - switch (buttonId) { - case 1: - player("I'm in search of treasure."); - stage = 40; - break; - case 2: - player("Arr!"); - stage = 12; - break; - case 3: - player("Do you have anything for trade?"); - stage = 13; - break; - case 4: - player("Tell me about the Falador Achievement Diary."); - stage = 80; - break; - } - break; - case 12: - npc("Arr!"); - stage = 0; - break; - case 13: - npc("Nothin' at the moment, but then again the Customs", "Agents are on the warpath right now."); - stage = 999; - break; - case 20: - if (!player.getInventory().containsItem(KARAMJAN_RUM)) { - player("No, not yet."); - stage = 21; - } else { - player("Yes, I've got some."); - stage = 31; - } - break; - case 21: - npc("Not suprising, tis no easy task to get it off Karamja."); - stage = 22; - break; - case 22: - player("What do you mean?"); - stage = 23; - break; - case 23: - npc("The Customs office has been clampin' down on the", "export of spirits. You seem like a resourceful young lad,", "I'm sure ye'll be able to find a way to slip the stuff past", "them."); - stage = 24; - break; - case 24: - player("Well I'll give it another shot."); - stage = 25; - break; - case 999: - end(); - break; - case 31: - npc("Now a deal's a deal, I'll tell ye about the treasure. I", "used to serve under a pirate captain called One-Eyed", "Hector."); - stage = 32; - break; - case 32: - npc("Hector were very successful and became very rich.", "But about a year ago we were boarded by the Customs", "and Excise Agents."); - stage = 33; - break; - case 33: - npc("Hector were killed along with many of the crew, I were", "one of the few to escape and I escaped with this."); - stage = 34; - break; - case 34: - if (player.getInventory().remove(KARAMJAN_RUM)) { - if (!player.getInventory().add(KEY)) { - GroundItemManager.create(KEY, player); - } - quest.setStage(player, 20); - interpreter.sendItemMessage(KEY.getId(), "Frank happily takes the rum... ... and hands you a key"); - stage = 35; - } - end(); - break; - case 35: - npc("This be Hector's key. I believe it opens his chest in his", "old room in the Blue Moon Inn in Varrock."); - stage = 36; - break; - case 36: - npc("With any luck his treasure will be in there."); - stage = 37; - break; - case 37: - player("Ok thanks, I'll go and get it."); - stage = 999; - break; - case 40: - npc("Arr, treasure you be after eh? Well I might be able to", "tell you where to find some... For a price..."); - stage = 41; - break; - case 41: - player("What sort of price?"); - stage = 42; - break; - case 42: - npc("Well for example if you go and get me a bottle of rum...", "Not just any rum mind..."); - stage = 43; - break; - case 43: - npc("I'd like some rum made on Karamja Island. There's no", "rum like Karamaja Rum!"); - stage = 44; - break; - case 44: - options("Ok, I will bring you some rum.", "Not right now."); - stage = 45; - break; - case 45: - switch (buttonId) { - case 1: - quest.start(player); - player("Ok, I will bring you some rum."); - stage = 47; - break; - case 2: - player("Not right now."); - stage = 46; - break; - } - break; - case 46: - npc("Fair enough. I'll still be here and thirsty whenever you", "feel like helpin' out."); - stage = 999; - break; - case 47: - npc("Yer a saint, although it'll take a miracle to get it off", "Karamja."); - stage = 48; - break; - case 48: - player("What do you mean?"); - stage = 49; - break; - case 49: - npc("The Customs office has been clampin' down on the", "export of spirits. You seem like a resourceful young lad,", "I'm sure ye'll be able to find a way to slip the stuff past", "them."); - stage = 50; - break; - case 50: - player("Well I'll give it a shot."); - stage = 51; - break; - case 51: - npc("Arr, that's the spirit!"); - stage = 999; - break; - - - case 700: - npc("Arr, silly you. Fortunately I took the precaution to have", "another one made."); - stage = 701; - break; - case 701: - interpreter.sendItemMessage(KEY.getId(), "Frank hands you a chest key."); - stage = 702; - break; - case 702: - end(); - if (!player.getInventory().add(KEY)) { - GroundItemManager.create(KEY, player); - } - break; - - - case 80: - if (player.getAttribute("falador-diary-talk-first-time", false)) { - npc("So you're interested in exploring Falador and it's", "surrounding areas, eh?"); - player.setAttribute("/save:falador-diary-talk-first-time", true); - stage = 100; - } else { - npc("How are you getting on with the Achievement Diary?"); - stage = 90; - } - break; - - case 90: - options("I've come for my reward.", "I'm doing good.", "I have a question."); - stage = 91; - break; - case 91: - switch (buttonId) { - case 1: - player("I've come for my reward."); - stage = 200; - break; - case 2: - player("I'm doing good."); - stage = 220; - break; - case 3: - player("I have a question."); - stage = 105; - break; - } - break; - - case 100: - player("Er... I guess."); - stage = 101; - break; - case 101: - npc("Arrr! That's the spirit! Soon ye'll be exploring", "underground caverns, sailin' the high seas and", "plundering booty!"); - stage = 102; - break; - case 102: - interpreter.sendDialogues(player, FacialExpression.AMAZED, "Plundering booty?"); - stage = 103; - break; - case 103: - npc("Arrr! Nay, that be a lie, I be getting carried away."); - stage = 104; - break; - case 104: - npc("Have you got any questions?"); - stage = 105; - break; - case 105: - if (!diary.isLevelRewarded(level)) { - options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); - stage = 106; - } else { - options("Can you remind me what my Falador shield does, please?", "What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); - stage = 107; - } - break; - case 106: - 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 = 999; - break; - } - break; - - case 107: - switch (buttonId) { - case 1: - player("Can you remind me what my Falador shield does, please?"); - stage = 150; - break; - case 2: - player("What is the Achievement Diary?"); - stage = 110; - break; - case 3: - player("What are the rewards?"); - stage = 120; - break; - case 4: - player("How do I claim the rewards?"); - stage = 130; - break; - case 5: - player("See you later."); - stage = 999; - break; - } - break; - - case 110: - npc("Ah, well it's a diary that helps you keep track of", "particular achievements you've made here on", "2009Scape."); - stage = 111; - break; - case 111: - npc("If you manage to complete a particular set of tasks,", "you will be rewarded for your explorative efforts."); - stage = 112; - break; - case 112: - npc("You can access your Achievement Diary by going to", "the Quest Journal, then clicking on the green star icon", "in the top-right hand corner."); - stage = 105; - break; - - case 120: - npc("Ah, well there are different rewards for each", "Achievement Diary. For completing each stage of the", "Falador diary, you are presented with a Falador shield."); - stage = 121; - break; - case 121: - npc("There are three shields available, one for each difficulty", "level."); - stage = 122; - break; - case 122: - npc("When you are presented with your rewards, you will", "be told of their uses."); - stage = 105; - break; - - case 130: - npc("You need to complete all of the tasks in a particular", "difficulty, then you can claim your reward."); - stage = 131; - break; - case 131: - npc("Some of Falador's tasks are simple, some will require", "certain skill levels, and some might require quests to be", "started or completed."); - stage = 132; - break; - case 132: - npc("To claim your Falador Achievement Diary rewards,", "speak to the chemist in Rimmington, Sir Vyvin's squire", "in the White Knight's Castle, or myself."); - stage = 105; - break; - - case 150: - npc("This is the first stage fo the Falador shield: a buckler. It", "grants you access to a Prayer restore ability and an", "emote."); - stage = 151; - break; - case 151: - npc("Each of these features can be triggered while wielding", "the shield by selecting the 'Operate' option. The Prayer", "restore can also be activated from your inventory."); - stage = 152; - break; - case 152: - npc("The Prayer restore ability can only be used once per", "day, and gives you back a quarter of your Prayer", "points."); - stage = 153; - break; - case 153: - npc("As well as all of these features, the shield is pretty", "handy in combat, and gives you a small Prayer boost."); - stage = 105; - break; - - case 200: - if (diary.isLevelRewarded(level)) { - npc("But you've already gotten yours!"); - stage = 105; - } else if (diary.isComplete(level, true)) { - npc("So, you've finished. Well done! I believe congratulations", "are in order."); - stage = 201; - } else { - npc("But you haven't finished!"); - stage = 105; - } - break; - case 201: - player("I believe rewards are in order."); - stage = 202; - break; - case 202: - npc("Right you are."); - stage = 203; - break; - case 203: - npc("This is the first stage of the Falador shield: a buckler. It", "grants you access to a Prayer restore ability and an", "emote."); - if (!diary.isLevelRewarded(level)) { - for (Item i : diary.getType().getRewards(level)) { - if (!player.getInventory().add(i, player)) { - GroundItemManager.create(i, player); - }; - } - diary.setLevelRewarded(level); - } - stage = 204; - break; - case 204: - npc("Each of these features can be triggered while wielding", "the shield by selecting the 'Operate' option. The Prayer", "restore can also be activated from your inventory."); - stage = 205; - break; - case 205: - npc("The Prayer restore ability can only be used once per", "day, and gives you back a quarter of your Prayer", "points."); - stage = 206; - break; - case 206: - npc("As well as all of these features, the shield is pretty", "handy in combat, and gives you a small Prayer boost."); - stage = 207; - break; - case 207: - npc("I've even thrown in a bit of booty I found on my", "travels."); - stage = 208; - break; - case 208: - interpreter.sendDialogues(player, FacialExpression.AMAZED, "Wow, thanks!"); - stage = 209; - break; - case 209: - npc("If you should lose your shield, come back and see me", "for another one."); - stage = 105; - break; - case 220: - npc("Keep it up!"); - stage = 105; - break; - - - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ReadbeardFrankDialogue(player); - } - - @Override - public int[] getIds() { - return new int[]{375}; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/ResearcherDialogue.java b/Server/src/main/java/core/game/content/dialogue/ResearcherDialogue.java deleted file mode 100644 index e106c720b..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ResearcherDialogue.java +++ /dev/null @@ -1,96 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import rs09.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/java/core/game/content/dialogue/RimmingtonShopKeeperDialogue.java b/Server/src/main/java/core/game/content/dialogue/RimmingtonShopKeeperDialogue.java deleted file mode 100644 index 8ce24372f..000000000 --- a/Server/src/main/java/core/game/content/dialogue/RimmingtonShopKeeperDialogue.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the RimmingtonShopKeeperDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class RimmingtonShopKeeperDialogue extends DialoguePlugin { - - public RimmingtonShopKeeperDialogue() { - - } - - public RimmingtonShopKeeperDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 531, 530 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, please. What are you selling?", "How should I use your shop?", "No, thanks."); - stage = 1; - break; - case 1: - - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.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; - break; - case 3: - end(); - break; - } - - break; - case 10: - end(); - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new RimmingtonShopKeeperDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Can I help you at all?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/RockGolemDialogue.java b/Server/src/main/java/core/game/content/dialogue/RockGolemDialogue.java deleted file mode 100644 index 73ec63b94..000000000 --- a/Server/src/main/java/core/game/content/dialogue/RockGolemDialogue.java +++ /dev/null @@ -1,76 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the rock golem dialgoue. - * @author Empathy - * - */ -@Initializable -public final class RockGolemDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code RockGolemDialogue} {@code Object}. - */ - public RockGolemDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code RockGolemDialogue} {@code Object}. - * - * @param player the player. - */ - public RockGolemDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new RockGolemDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So you're made entirely of rocks?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Not quite, my body is formed mostly of minerals."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Aren't minerals just rocks?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "No, rocks are rocks, minerals are minerals. I am", "formed from minerals."); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "But you're a Rock Golem..."); - stage = 4; - break; - case 4: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8637 }; - } - } diff --git a/Server/src/main/java/core/game/content/dialogue/RoomikDialogue.java b/Server/src/main/java/core/game/content/dialogue/RoomikDialogue.java deleted file mode 100644 index 9ebd94ea6..000000000 --- a/Server/src/main/java/core/game/content/dialogue/RoomikDialogue.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/RufusDialogue.java b/Server/src/main/java/core/game/content/dialogue/RufusDialogue.java deleted file mode 100644 index 7aaeec0ea..000000000 --- a/Server/src/main/java/core/game/content/dialogue/RufusDialogue.java +++ /dev/null @@ -1,105 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the RufusDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class RufusDialogue extends DialoguePlugin { - - public RufusDialogue() { - - } - - public RufusDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new RufusDialogue(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, "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: - interpreter.sendOptions("Select an Option", "Why do you only sell meats?", "Do you sell cooked food?", "Can I buy some food?"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why do you only sell meats?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you sell cooked food?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "...A vegetarian one?"); - stage = 12; - break; - case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Vegetarrrian...?"); - stage = 13; - break; - case 13: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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!"); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Cerrrtainly!"); - stage = 31; - break; - case 31: - end(); - npc.openShop(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1038 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/SabreenDialogue.java b/Server/src/main/java/core/game/content/dialogue/SabreenDialogue.java deleted file mode 100644 index c5ba8dd6d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SabreenDialogue.java +++ /dev/null @@ -1,112 +0,0 @@ -package core.game.content.dialogue; - -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 core.game.world.update.flag.context.Animation; - -/** - * @author 'Vexia - */ -@Initializable -public class SabreenDialogue extends DialoguePlugin { - - public SabreenDialogue() { - - } - - public SabreenDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SabreenDialogue(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. How can I help?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "Can you heal me?", "Do you see a lot of injured fighters?", "Do you come here often?"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you heal me?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you see a lot of injured fighters?"); - stage = 30; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you come here often?"); - stage = 20; - break; - - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Of course!"); - stage = 11; - break; - case 11: - npc.animate(new Animation(881)); - if (player.getSkills().getLifepoints() == player.getSkills().getStaticLevel(Skills.HITPOINTS)) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You look healthy to me!"); - } else { - player.getSkills().heal(player.getSkills().getStaticLevel(Skills.HITPOINTS)); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "There you go!"); - } - stage = 12; - break; - case 12: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I work here, so yes!"); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes I do. Thankfully we can cope with almost aything.", "Jaraah really is a wonderful surgeon, this methods are a", "little unorthodox but he gets the job done."); - stage = 31; - break; - case 31: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I shouldn't tell you this but his nickname is 'The", "Butcher'."); - stage = 32; - break; - case 32: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's reassuring."); - stage = 33; - break; - case 33: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 960 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/SawmillOperator.java b/Server/src/main/java/core/game/content/dialogue/SawmillOperator.java deleted file mode 100644 index 11779c29d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SawmillOperator.java +++ /dev/null @@ -1,95 +0,0 @@ -package core.game.content.dialogue; - -import core.game.component.Component; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the SawmillOperator dialogue. - * @author Vexia - */ -@Initializable -public class SawmillOperator extends DialoguePlugin { - - public SawmillOperator() { - - } - - public SawmillOperator(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SawmillOperator(player); - } - - @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?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("Planks please!", "What kind of planks can you make?", "Can I buy some housing supplies?", "Nothing, thanks."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - player("Planks please!"); - stage = 10; - break; - case 2: - player("What kind of planks can you make?"); - stage = 20; - break; - case 3: - end(); - npc.openShop(player); - break; - case 4: - player("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!"); - stage = 999; - break; - case 10: - npc("What kind of planks do you want?"); - stage = 11; - break; - case 11: - end(); - 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."); - 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."); - stage = 999; - break; - case 999: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4250 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/SbottDialogue.java b/Server/src/main/java/core/game/content/dialogue/SbottDialogue.java deleted file mode 100644 index 19e86753c..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SbottDialogue.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.content.dialogue; - -import core.plugin.Initializable; -import core.game.node.entity.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/java/core/game/content/dialogue/ScorpiasOffspringDialogue.java b/Server/src/main/java/core/game/content/dialogue/ScorpiasOffspringDialogue.java deleted file mode 100644 index 0c145077f..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ScorpiasOffspringDialogue.java +++ /dev/null @@ -1,79 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, Scorpia JR - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class ScorpiasOffspringDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - */ - public ScorpiasOffspringDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - * @param player the player. - */ - public ScorpiasOffspringDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ScorpiasOffspringDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "At night time, if I were to hold ultraviolet", "light over you, would you glow?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Two things wrong there, human."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "One, when has it ever been night time here?"); - stage = 3; - break; - case 3: - npc("Two, when have you ever seen ultraviolet light", "around here?"); - stage = 4; - break; - case 4: - npc("In answer to your question though.","Yes I, like every scorpion, would glow."); - stage = 5; - break; - case 5: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8598 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/SeerDialoguePlugin.java b/Server/src/main/java/core/game/content/dialogue/SeerDialoguePlugin.java deleted file mode 100644 index d057b5f0d..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SeerDialoguePlugin.java +++ /dev/null @@ -1,162 +0,0 @@ -package core.game.content.dialogue; - -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) { - AchievementDiary diary = player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE); - int level = 0; - - switch (stage) { - case 999: - end(); - break; - case 0: - switch (buttonId) { - case 1: - npc("Many greetings."); - stage = 1; - break; - case 2: - if (!diary.isStarted(level)) { - player("Do you have an Achievement Diary for me?"); - stage = 100; - break; - } - else if (diary.isLevelRewarded(level) && !player.hasItem(diary.getType().getRewards()[level][0])) { - player("I seem to have lost my seers' headband..."); - stage = 80; - break; - } - else if (diary.isLevelRewarded(level)) { - player("Can you remind me what my headband does?"); - stage = 90; - break; - } - else if (diary.isComplete(level, true)) { - 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: - player.getInventory().add(diary.getType().getRewards(level)[0], player); - 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 (!player.getInventory().hasSpaceFor(diary.getType().getRewards(level))) { - npc("Come back when you have two free inventory slots."); - stage = 999; - } else { - diary.setLevelRewarded(level); - player.getInventory().add(diary.getType().getRewards()[level][0]); - player.getInventory().add(diary.getType().getRewards()[level][1]); - interpreter.sendItemMessage(diary.getType().getRewards()[level][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/java/core/game/content/dialogue/ServantDialogue.java b/Server/src/main/java/core/game/content/dialogue/ServantDialogue.java deleted file mode 100644 index db100a414..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ServantDialogue.java +++ /dev/null @@ -1,62 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/ShantayDialogue.java b/Server/src/main/java/core/game/content/dialogue/ShantayDialogue.java deleted file mode 100644 index bcd5dcfa2..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ShantayDialogue.java +++ /dev/null @@ -1,294 +0,0 @@ -package core.game.content.dialogue; - -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 rs09.game.world.GameWorld; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Represents the dialogue plugin used for shantay. - * @author Vexia - * @version 1.0 - */ -@Initializable -public final class ShantayDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ShantayDialogue} {@code Object}. - */ - public ShantayDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ShantayDialogue} {@code Object}. - * @param player the player. - */ - public ShantayDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ShantayDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (args[0] != null && args[0] instanceof NPC) { - npc = (NPC) args[0]; - } - 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?"); - stage = 703; - return true; - } - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "Hello effendi, I am Shantay."); - stage = 0; - return true; - } - - @Override - 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."); - 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."); - stage = 2; - break; - case 2: - interpreter.sendOptions("Select an Option", "What is this place?", "Can I see what you have to sell please?", "I must be going.", "I want to buy a Shantay pass for 5 gold coins."); - stage = 3; - break; - case 3: - 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 see what you have to sell please?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 40; - break; - - } - break; - case 40: - if (!player.getInventory().contains(995, 5)) { - end(); - return true; - } - if (player.getInventory().remove(new Item(995, 5))) { - 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."); - } - stage = 41; - break; - case 41: - end(); - break; - case 30: - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "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."); - 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?"); - stage = 12; - break; - case 12: - interpreter.sendOptions("Select an Option", "I am definitely an outlaw, prepare to die!", "I am a little inexperienced.", "Er, neither, I'm an adventurer."); - stage = 13; - break; - case 13: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "I am definitely an outlaw, prepare to die."); - stage = 700; - break; - case 2: - interpreter.sendDialogues(player, null, "I am a little inexperienced."); - stage = 710; - break; - case 3: - interpreter.sendDialogues(player, null, "Er, neither, I'm an adventurer."); - stage = 720; - break; - } - break; - case 20: - interpreter.sendDialogues(836, FacialExpression.HALF_GUILTY, "Absolutely Effendi!"); - stage = 21; - break; - case 21: - end(); - npc.openShop(player); - break; - case 700: - interpreter.sendDialogues(836, null, "Ha, very funny....."); - stage = 701; - break; - case 701: - interpreter.sendDialogues(836, null, "Guards arrest him!"); - stage = 702; - break; - case 702: - player.getPacketDispatch().sendMessage("The guards arrest you and place you in the jail."); - close(); - player.lock(10); - GameWorld.getPulser().submit(new Pulse(3, player) { - @Override - 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?"); - stage = 703; - return true; - } - }); - break; - case 703: - interpreter.sendOptions("Select an Option", "Yes, okay.", "No thanks, you're not having my money."); - stage = 704; - break; - case 704: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Yes, okay."); - stage = 705; - break; - case 2: - interpreter.sendDialogues(player, null, "No thanks, you're not having my money."); - stage = 800; - break; - } - break; - case 705: - 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."); - player.removeAttribute("shantay-jail"); - stage = 822; - } else { - interpreter.sendDialogues(player, null, "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"); - stage = 801; - break; - case 801: - interpreter.sendDialogues(836, null, "Will you pay the five gold pieces?"); - stage = 802; - break; - case 802: - interpreter.sendOptions("Select an Option", "Yes, okay.", "No, do your worst!"); - stage = 803; - break; - case 803: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Yes, okay."); - stage = 820; - break; - case 2: - interpreter.sendDialogues(player, null, "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."); - stage = 805; - break; - case 805: - player.getPacketDispatch().sendMessage("You find yourself in a prison."); - player.removeAttribute("shantay-jail"); - player.getProperties().setTeleportLocation(Location.create(3018, 3188, 0)); - end(); - break; - case 820: - interpreter.sendDialogues(836, null, "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."); - stage = 822; - } else { - interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough money."); - stage = 825; - } - break; - case 822: - end(); - break; - case 825: - 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."); - 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."); - 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."); - 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."); - 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."); - stage = 723; - break; - case 723: - end(); - break; - case 730: - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 836 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/ShilopDialogue.java b/Server/src/main/java/core/game/content/dialogue/ShilopDialogue.java deleted file mode 100644 index 6b5e61f13..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ShilopDialogue.java +++ /dev/null @@ -1,240 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the dialogue plugin used for the shilop npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ShilopDialogue extends DialoguePlugin { - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 100); - - /** - * Represents the id. - */ - private int id; - - /** - * Constructs a new {@code ShilopDialogue} {@code Object} - */ - public ShilopDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ShilopDialogue} {@code Object}. - * @param player the player. - */ - public ShilopDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ShilopDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (args[0] instanceof NPC) { - id = ((NPC) args[0]).getId(); - } else if (args[0] instanceof Integer) { - id = (Integer) args[0]; - } - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); - switch (quest.getStage(player)) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); - stage = 0; - break; - case 10: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - stage = 130; - break; - default: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); - stage = 0; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); - switch (stage) { - case 0: - interpreter.sendDialogues(id, FacialExpression.OLD_NORMAL, "You think you're tough do you?"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Pardon?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(id, FacialExpression.OLD_NORMAL, "I can beat anyone up!"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(783, FacialExpression.OLD_NORMAL, "He can you know!"); - stage = 4; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Really?"); - stage = 5; - break; - case 5: - interpreter.sendDialogue("The boy begins to jump around with his fists up.", "You wonder what sort of desperado he'll grow up to be."); - stage = 6; - break; - case 6: - end(); - break; - case 100:// stage 10 - interpreter.sendDialogues(id, FacialExpression.OLD_NORMAL, "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."); - 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."); - stage = 103; - break; - case 103: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 105; - break; - case 105: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 107; - break; - case 107: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How much?"); - stage = 108; - break; - case 108: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "10 coins."); - stage = 109; - break; - case 109: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "10 coins?!"); - stage = 110; - break; - case 110: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "I'll handle this."); - stage = 111; - break; - case 111: - interpreter.sendDialogues(id == 781 ? 783 : 781, null, "100 coins should cover it."); - stage = 112; - break; - case 112: - interpreter.sendDialogues(player, null, "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?"); - stage = 114; - break; - case 114: - interpreter.sendOptions("Select an Option", "I'm not paying you a penny.", "Okay then, I'll pay."); - stage = 115; - break; - case 115: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "I'm not paying you a penny."); - stage = 116; - break; - case 2: - interpreter.sendDialogues(player, null, "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."); - stage = 117; - break; - case 117: - end(); - break; - case 118: - if (!player.getInventory().containsItem(COINS)) { - interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough coins."); - stage = 117; - return true; - } - if (player.getInventory().remove(COINS)) { - interpreter.sendItemMessage(COINS, "You give the lad 100 coins."); - stage = 119; - } else { - interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough coins."); - stage = 117; - } - quest.setStage(player, 20); - break; - case 119: - interpreter.sendDialogues(player, null, "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."); - stage = 121; - break; - case 121: - interpreter.sendDialogues(player, null, "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."); - 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."); - stage = 131; - break; - case 131: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 781 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/ShopkeeperDialogue.java b/Server/src/main/java/core/game/content/dialogue/ShopkeeperDialogue.java deleted file mode 100644 index 053cd4fa4..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ShopkeeperDialogue.java +++ /dev/null @@ -1,79 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/SilkMerchantPlugin.java b/Server/src/main/java/core/game/content/dialogue/SilkMerchantPlugin.java deleted file mode 100644 index 7e358127a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SilkMerchantPlugin.java +++ /dev/null @@ -1,218 +0,0 @@ -package core.game.content.dialogue; - -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 rs09.game.world.GameWorld; -import core.plugin.Initializable; -import core.game.world.map.RegionManager; - -/** - * Handles the SilkMerchantPlugin dialogue. - * @author 'Vexia - */ -@Initializable -public class SilkMerchantPlugin extends DialoguePlugin { - - /** - * Represents the silk item. - */ - private final Item SILK = new Item(950); - - /** - * Represents the noted silk item. - */ - private final Item NOTED_SILK = new Item(951); - - public SilkMerchantPlugin() { - - } - - public SilkMerchantPlugin(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 574 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - 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."); - stage = 1; - } else if (player.getInventory().containsItem(NOTED_SILK)) { - interpreter.sendDialogues(player, null, "I've got some silk here. Will you buy it?"); - stage = 100; - } else { - interpreter.sendDialogues(player, null, "Sorry, I don't have any silk."); - stage = 200; - } - break; - case 200: - end(); - break; - case 201: - 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?"); - stage = 2; - break; - case 2: - interpreter.sendOptions("Select an Option", "20 coins.", "30 coins.", "120 coins.", "200 coins."); - stage = 3; - break; - case 3: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "20 coins."); - stage = 1000; - break; - case 2: - interpreter.sendDialogues(player, null, "30 coins."); - stage = 2000; - break; - case 3: - interpreter.sendDialogues(player, null, "120 coins."); - stage = 300; - break; - case 4: - interpreter.sendDialogues(player, null, "200 coins."); - stage = 400; - break; - } - break; - case 1000: - case 2000: - interpreter.sendDialogues(npc, null, "That price is fine by me! Hand over your silk."); - stage = stage == 1000 ? 1010 : 1011; - break; - case 1010: - buy(20); - break; - case 1011: - buy(30); - 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."); - stage = 500; - break; - case 500:// use this for selling. - interpreter.sendOptions("Select an Option", "Ok, I guess 50 will do.", "I'll give it to you for 60.", "No, that is not enough."); - stage = 501; - break; - case 501: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Ok, I guess 50 will do."); - stage = 510; - break; - case 2: - interpreter.sendDialogues(player, null, "I'll give it to you for 60."); - stage = 520; - break; - case 3: - interpreter.sendDialogues(player, null, "No, that is not enough."); - stage = 530; - break; - } - break; - case 510: - buy(50); - break; - case 520: - interpreter.sendDialogues(npc, null, "You drive a hard bargain, but", "I guess that will have to do."); - stage = 521; - break; - case 521: - buy(60); - break; - case 530: - end(); - break; - case 100: - interpreter.sendDialogues(npc, null, "Silk? Yo're not carrying any silk."); - stage = 101; - break; - case 101: - interpreter.sendDialogues(player, null, "Yes I am."); - stage = 102; - break; - case 102: - interpreter.sendDialogues(npc, null, "No, you're carrying " + player.getInventory().getAmount(NOTED_SILK) + " bits of paper that say they", "can be exchanged for silk at a bank or general store.", "I'm not buying those. Fetch me some real silk, then", "we'll trade."); - stage = 103; - break; - case 103: - interpreter.sendDialogues(player, null, "Oh, alright."); - stage = 104; - break; - case 104: - end(); - break; - } - return true; - } - - /** - * Method used to buy silk. - * @param price - */ - public final void buy(int price) { - end(); - int amt = player.getInventory().getAmount(SILK); - Item remove = new Item(SILK.getId(), amt); - if (!player.getInventory().containsItem(remove)) { - return; - } - if (player.getInventory().remove(remove)) { - player.getInventory().add(new Item(995, price * amt)); - } - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SilkMerchantPlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (player.getSavedData().getGlobalData().getSilkSteal() > System.currentTimeMillis()) { - end(); - for (NPC npc : RegionManager.getLocalNpcs(player.getLocation(), 8)) { - if (!npc.getProperties().getCombatPulse().isAttacking() && npc.getId() == 32) { - npc.sendChat("Hey! Get your hands off there!"); - npc.getProperties().getCombatPulse().attack(player); - break; - } - } - GameWorld.getPulser().submit(new Pulse(1) { - int count = 0; - - @Override - public boolean pulse() { - if (count == 0) - npc.sendChat("You're the one who stole something from me!"); - if (count == 2) { - npc.sendChat("Guards guards!"); - return true; - } - count++; - return false; - } - - }); - return false; - } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I buy silk. If you ever want to", "sell any silk, bring it here."); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/SilverMechantPlugin.java b/Server/src/main/java/core/game/content/dialogue/SilverMechantPlugin.java deleted file mode 100644 index e430324a5..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SilverMechantPlugin.java +++ /dev/null @@ -1,66 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the SilverMechantPlugin dialogue. - * @author 'Vexia - */ -@Initializable -public class SilverMechantPlugin extends DialoguePlugin { - - public SilverMechantPlugin() { - - } - - public SilverMechantPlugin(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 569 }; - } - - @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 DialoguePlugin newInstance(Player player) { - - return new SilverMechantPlugin(player); - } - - @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!"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/SimpleEntityMessage.java b/Server/src/main/java/core/game/content/dialogue/SimpleEntityMessage.java deleted file mode 100644 index e88d04674..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SimpleEntityMessage.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.content.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/java/core/game/content/dialogue/SirTiffyCashienDialogue.java b/Server/src/main/java/core/game/content/dialogue/SirTiffyCashienDialogue.java deleted file mode 100644 index 741beb800..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SirTiffyCashienDialogue.java +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/SofiyaDialogue.java b/Server/src/main/java/core/game/content/dialogue/SofiyaDialogue.java deleted file mode 100644 index 7f260b23a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SofiyaDialogue.java +++ /dev/null @@ -1,50 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/SpellbookSwapDialogue.java b/Server/src/main/java/core/game/content/dialogue/SpellbookSwapDialogue.java deleted file mode 100644 index 164e38366..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SpellbookSwapDialogue.java +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.content.dialogue; - -import core.game.component.Component; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; - -/** - * Handles the SpellbookSwapDialogue dialogue. - * @author Vexia - * - */ -@Initializable -public class SpellbookSwapDialogue extends DialoguePlugin { - - /** - * If we're using the perk. - */ - private boolean perk; - - /** - * Constructs a new {@Code SpellbookSwapDialogue} {@Code Object} - */ - public SpellbookSwapDialogue() { - /* - * empty. - */ - } - - /** - * Constructs a new {@Code SpellbookSwapDialogue} {@Code Object} - * @param player the player. - */ - public SpellbookSwapDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SpellbookSwapDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (args.length > 1) { - perk = true; - interpreter.sendOptions("Select a Spellbook", "Modern", "Ancient", "Lunar"); - return true; - } - interpreter.sendOptions("Select a Spellbook", "Ancient", "Modern"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (perk) { - SpellBook book = SpellBook.values()[buttonId - 1]; - player.getSpellBookManager().setSpellBook(book); - player.getInterfaceManager().openTab(new Component(book.getInterfaceId())); - end(); - return true; - } - int type = 0; - switch (buttonId) { - case 1: - type = 1; - break; - case 2: - type = 2; - break; - } - final SpellBook book = type == 1 ? SpellBook.ANCIENT : SpellBook.MODERN; - player.getSpellBookManager().setSpellBook(book); - player.getInterfaceManager().openTab(new Component(book.getInterfaceId())); - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3264731 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/SpiritGraahkDialogue.java b/Server/src/main/java/core/game/content/dialogue/SpiritGraahkDialogue.java deleted file mode 100644 index 012053ff6..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SpiritGraahkDialogue.java +++ /dev/null @@ -1,81 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.skill.summoning.familiar.Familiar; -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/java/core/game/content/dialogue/StringBowDialogue.java b/Server/src/main/java/core/game/content/dialogue/StringBowDialogue.java deleted file mode 100644 index e53cca457..000000000 --- a/Server/src/main/java/core/game/content/dialogue/StringBowDialogue.java +++ /dev/null @@ -1,112 +0,0 @@ -/* -package core.game.content.dialogue; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.fletching.items.bow.StringBow; -import core.game.node.entity.skill.fletching.items.bow.StringPulse; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.entity.player.link.RunScript; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.game.node.item.Item; - -*/ -/** - * Handles the StringBowDialogue dialogue. - * @author Vexia - *//* - -@InitializablePlugin -public class StringBowDialogue extends DialoguePlugin { - - */ -/** - * Constructs a new {@code StringBowDialogue.java} {@code Object}. - *//* - - public StringBowDialogue() { - } - - */ -/** - * Constructs a new {@code StringBowDialogue.java} {@code Object}. - * @param player the player. - *//* - - public StringBowDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new StringBowDialogue(player); - } - - */ -/** - * The item. - *//* - - private Item item; - - */ -/** - * The string bow enum. - *//* - - private StringBow bow; - - @Override - public boolean open(Object... args) { - item = (Item) args[0]; - bow = StringBow.forItem(item.getId()); - if (bow == null) { - return true; - } - player.getInterfaceManager().openChatbox(309); - player.getPacketDispatch().sendString("



" + bow.getProduct().getName(), 309, 6); - player.getPacketDispatch().sendItemZoomOnInterface(bow.getProduct().getId(), 160, 309, 2); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - end(); - switch (stage) { - case 0: - System.out.println(buttonId); - int amt = 0; - switch (buttonId) { - case 5: - amt = 1; - break; - case 4: - amt = 5; - break; - case 3: - player.setAttribute("runscript", new RunScript() { - @Override - public boolean handle() { - int ammount = (int) value; - player.getPulseManager().run(new StringPulse(player, item, bow, ammount)); - return false; - } - }); - player.getDialogueInterpreter().sendInput(false, "Enter the amount"); - return true; - case 2: - amt = player.getInventory().getAmount(item); - break; - } - player.getPulseManager().run(new StringPulse(player, item, bow, amt)); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 9174399 }; - } - -} -*/ diff --git a/Server/src/main/java/core/game/content/dialogue/SurgeonGeneralTafani.java b/Server/src/main/java/core/game/content/dialogue/SurgeonGeneralTafani.java deleted file mode 100644 index fc23079cb..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SurgeonGeneralTafani.java +++ /dev/null @@ -1,195 +0,0 @@ -package core.game.content.dialogue; - -import core.game.content.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.world.update.flag.context.Animation; - -/** - * Represents the dialogue plugin used for surgeon general tafani. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SurgeonGeneralTafani extends DialoguePlugin { - - /** - * Constructs a new {@code SurgeonGeneralTafani} {@code Object} - */ - public SurgeonGeneralTafani() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code SurgeonGeneralTafani} {@code Object}. - * @param player the player. - */ - public SurgeonGeneralTafani(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SurgeonGeneralTafani(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. How can I help?"); - stage = 1; - break; - case 1: - if (Skillcape.isMaster(player, Skills.HITPOINTS)) { - interpreter.sendOptions("Select an Option", "Can you heal me?", "Do you see a lot of injured fighters?", "Do you come here often?", "Can I buy a Skillcape of Hitpoints from you?"); - stage = 80; - } else { - interpreter.sendOptions("Select an Option", "Can you heal me?", "Do you see a lot of injured fighters?", "Do you come here often?", "Where can I get a cape like yours?"); - stage = 2; - } - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you heal me?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you see a lot of injured fighters?"); - stage = 30; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you come here often?"); - stage = 20; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where can I get a cape like yours?"); - stage = 40; - break; - - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Of course!"); - stage = 11; - break; - case 11: - npc.animate(new Animation(881)); - if (player.getSkills().getLifepoints() == player.getSkills().getStaticLevel(Skills.HITPOINTS)) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You look healthy to me!"); - } else { - player.getSkills().heal(player.getSkills().getStaticLevel(Skills.HITPOINTS)); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "There you go!"); - } - stage = 12; - break; - case 12: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I work here, so yes!"); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes I do. Thankfully we can cope with almost aything.", "Jaraah really is a wonderful surgeon, this methods are a", "little unorthodox but he gets the job done."); - stage = 31; - break; - case 31: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I shouldn't tell you this but his nickname is 'The", "Butcher'."); - stage = 32; - break; - case 32: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's reassuring."); - stage = 33; - break; - case 33: - end(); - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - stage = 41; - break; - case 41: - end(); - break; - case 80: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you heal me?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you see a lot of injured fighters?"); - stage = 30; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you come here often?"); - stage = 20; - break; - case 4: - player("Can I buy a Skillcape of Hitpoints from you?"); - stage = 81; - break; - } - break; - case 81: - npc("Why, certainly my friend. However, owning such an item", "makes you part of an elite group and that privilege will", "cost you 99000 coins."); - stage = 82; - break; - case 82: - options("Sorry, that's much too pricey.", "Sure, that's not too expensive for such a magnificent cape."); - stage = 83; - break; - case 83: - switch (buttonId) { - case 1: - player("Sorry, that's much too pricey."); - stage = 84; - break; - case 2: - player("Sure, that's not too expensive for such a magnificent", "cape."); - stage = 86; - break; - } - break; - case 84: - npc("I'm sorry you feel that way. Still if you change", "your mind..."); - stage = 85; - break; - case 85: - end(); - break; - case 86: - if (Skillcape.purchase(player, Skills.HITPOINTS)) { - npc("There you go! I hope you enjoy it."); - } - stage = 87; - break; - case 87: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 961 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/SvetlanaDialogue.java b/Server/src/main/java/core/game/content/dialogue/SvetlanaDialogue.java deleted file mode 100644 index 36f20e8d2..000000000 --- a/Server/src/main/java/core/game/content/dialogue/SvetlanaDialogue.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/TaxidermistDialogue.java b/Server/src/main/java/core/game/content/dialogue/TaxidermistDialogue.java deleted file mode 100644 index 5a7626b26..000000000 --- a/Server/src/main/java/core/game/content/dialogue/TaxidermistDialogue.java +++ /dev/null @@ -1,74 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/TostigDialogue.java b/Server/src/main/java/core/game/content/dialogue/TostigDialogue.java deleted file mode 100644 index 8310a55dd..000000000 --- a/Server/src/main/java/core/game/content/dialogue/TostigDialogue.java +++ /dev/null @@ -1,90 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/TownCrierDialogue.java.orig b/Server/src/main/java/core/game/content/dialogue/TownCrierDialogue.java.orig deleted file mode 100644 index 5caa8f54a..000000000 --- a/Server/src/main/java/core/game/content/dialogue/TownCrierDialogue.java.orig +++ /dev/null @@ -1,284 +0,0 @@ -package core.game.content.dialogue; - -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.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the town crier dialogue plugin. - * @author 'Vexia - */ -@InitializablePlugin -public final class TownCrierDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TownCrierDialogue} {@code Object}. - */ - public TownCrierDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TownCrierDialogue} {@code Object}. - * @param player the player. - */ - public TownCrierDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TownCrierDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; -<<<<<<< HEAD - npc("Hear ye! Hear ye! Player Moderators massive help to ", GameWorld.getName().substring(0, GameWorld.getName().length() - 3) + "-"); -======= - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hear ye! Hear ye! Player Moderators massive help to ", GameWorld.getSettings().getName() + "!"); ->>>>>>> 3418fa0f93ea8241d77c5e9608bf40e3a441e2c5 - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - switch (stage) { - case 1: - npc("Oh, hello citizen. Are you here to find out about Player", "Moderators? Or perhaps would you like to know about the", "laws of the land?"); - stage = 2; - break; - case 2: -<<<<<<< HEAD - options("Tell me about Player Moderators.", "Tell me about the Rules of " + GameWorld.getName() + ".", "Can you give me a handy tip please?", "Bye!"); -======= - interpreter.sendOptions("Select an Option", "Tell me about Player Moderators.", "Tell me about the Rules of " + GameWorld.getSettings().getName() + ".", "Can you give me a handy tip please?", "Bye!"); ->>>>>>> 3418fa0f93ea8241d77c5e9608bf40e3a441e2c5 - stage = 3; - break; - case 3: - switch (buttonId) { - case 1: - player("Tell me about Player Moderators."); - stage = 50; - break; - case 2: -<<<<<<< HEAD - player("Tell me about the Rules of " + GameWorld.getName() + "."); -======= - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about the Rules of " + GameWorld.getSettings().getName() + "."); ->>>>>>> 3418fa0f93ea8241d77c5e9608bf40e3a441e2c5 - stage = 70; - break; - case 3: - player("Can you give me a handy tip please?"); - stage = 100; - break; - case 4: - player("Bye!"); - stage = 400; - break; - } - break; - case 400: - npc("Nice meeting you."); - stage = 401; - break; - case 401: - end(); - break; - case 50: - npc("Of course. What would you like to know?"); - stage = 51; - break; - case 51: - options("What is a Player Moderator?", "What can Player Moderators do?", "How do I become a Player Moderator?", "What can Player Moderators not do?"); - stage = 52; - break; - case 52: - - switch (buttonId) { - case 1: - player(" What is a Player Moderator?"); - stage = 150; - break; - case 2: - npc("What can Player Moderators do?"); - stage = 160; - break; - case 3: - player("How do I become a Player Moderator?"); - stage = 170; - break; - case 4: - player("What can Player Moderators not do?"); - stage = 180; - break; - } - break; - case 70: - player.lock(4); - npc("At once. Take a look at my book here."); - npc.animate(new Animation(6866)); - GameWorld.getPulser().submit(new Pulse(4) { - @Override - public boolean pulse() { - //player.getDialogueInterpreter().open(496107759); // TODO rulebook broken - 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); - } - stage = 71; - break; - case 71: - end(); - break; - - case 100: - int rand = RandomFunction.random(1, 5); - switch (rand) { - case 1: - npc("If the chat window is moving too quickly to report a", "player accurately, run to a quiet spot and review the chat", "at your leisure!"); - stage = 2; - break; - case 2: - npc("If you're lost and have no idea where to go, use the Home", "Teleporter spell for free!"); - stage = 2; - break; - case 3: - npc("Make your recovery questions and answers hard to guess", "but easy to remember."); - stage = 2; - break; - case 4: - npc("Beware of players trying to lue you into the wilderness.", "Your items cannot be returned if you lose them!"); - stage = 2; - break; - case 5: -<<<<<<< HEAD - npc("" + GameWorld.getName() + " will never email you asking for your log-in details."); -======= - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "" + GameWorld.getSettings().getName() + " will never email you asking for your log-in details."); ->>>>>>> 3418fa0f93ea8241d77c5e9608bf40e3a441e2c5 - stage = 2; - break; - } - stage = 2; - break; - case 150: - npc("Player Moderators are normal players of the game, just", "like you. However, since they have shown themselves to be", "trustworthy and active reporters, they have been invited", "by Jagex to monitor the game and take appropriate"); - stage = 151; - break; - case 151: - npc("reward when they see rule breaking. You can spot a Player", "Moderator in game by looking at the chat screen - when a", "Player Moderator speaks, a silver crown appears to the", "left of their name. Remember, if there's no silver crown"); - stage = 152; - break; - case 152: - npc("there, they are not a Player Moderator! You can check", "out the website if you'd like more information."); - stage = 153; - break; - case 153: - player("Thanks!"); - stage = 154; - break; - case 154: - npc("Is there anything else you'd like to know?"); - stage = 51; - break; - case 160: -<<<<<<< HEAD - npc("Player Moderators, or 'P-mods', have the ability to mute", "rule breakers and " + GameWorld.getName() + " view their reports as a priority so", "that reward is taken as quickly as possible. P-Mods also", "have acces to the Player Moderator Centre. Within the"); - stage = 161; - break; - case 161: - npc("Centre are tools to help them Moderate " + GameWorld.getName() + ".", "These tools include dedicated forums, the Player", "Moderator Guidelines and the Player Moderator Code of", "Conduct."); - stage = 153; - break; - case 170: - npc("" + GameWorld.getName() + " picks players who spend their time and effort to", "help better the " + GameWorld.getName() + " community. To increase your", "chances of becoming a Player Moderator:"); -======= - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Player Moderators, or 'P-mods', have the ability to mute", "rule breakers and " + GameWorld.getSettings().getName() + " view their reports as a priority so", "that reward is taken as quickly as possible. P-Mods also", "have acces to the Player Moderator Centre. Within the"); - stage = 161; - break; - case 161: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Centre are tools to help them Moderate " + GameWorld.getSettings().getName() + ".", "These tools include dedicated forums, the Player", "Moderator Guidelines and the Player Moderator Code of", "Conduct."); - stage = 153; - break; - case 170: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "" + GameWorld.getSettings().getName() + " picks players who spend their time and effort to", "help better the " + GameWorld.getSettings().getName() + " community. To increase your", "chances of becoming a Player Moderator:"); ->>>>>>> 3418fa0f93ea8241d77c5e9608bf40e3a441e2c5 - stage = 171; - break; - case 171: - npc("Keep your account secure! This is very important, as a", "player with poor security will never be a P-Mod. Read our", "Security Tips for more information."); - stage = 173; - break; - case 173: -<<<<<<< HEAD - npc("Play by the rules! The rules of " + GameWorld.getName() + " are enforced", "for a reason, to make the game a fair and enjoyable", "environment for all."); - stage = 174; - break; - case 174: - npc("Report accuratley! When " + GameWorld.getName() + " consider an account for", "review they look for quality, not quantity. Ensure your", "reports are of a high quality by following the report", "guidelines."); -======= - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Play by the rules! The rules of " + GameWorld.getSettings().getName() + " are enforced", "for a reason, to make the game a fair and enjoyable", "environment for all."); - stage = 174; - break; - case 174: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Report accuratley! When " + GameWorld.getSettings().getName() + " consider an account for", "review they look for quality, not quantity. Ensure your", "reports are of a high quality by following the report", "guidelines."); ->>>>>>> 3418fa0f93ea8241d77c5e9608bf40e3a441e2c5 - stage = 175; - break; - case 175: - npc("Be nice to each other! Treat others as you would", "want to be treated yourself. Respect your fellow player.", "More information can be found on the website."); - stage = 153; - break; - case 180: -<<<<<<< HEAD - npc("P-Mods cannot ban your account - they can only report", "offences. " + GameWorld.getName() + " then take reward based on the evidence", "received. If you lose your password or get scamme dby", "another player, P_Mods cannot help you get your account"); - stage = 181; - break; - case 181: - npc("back. All they can do is recommend you to go to Player", "Support. They cannot retrieve any items you may have", "lost and they certainly do not recieve any free items", "from " + GameWorld.getName() + " for moderating the game. They are players"); - stage = 182; - break; - case 182: - npc("who give their all to help the community, out of the", "goodness of their hearts! P-mods do not work for " + GameWorld.getName() + "", "and so cannot make you a Moderator, or recommend", "other accounts to become Moderators. If you wish yo"); -======= - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "P-Mods cannot ban your account - they can only report", "offences. " + GameWorld.getSettings().getName() + " then take reward based on the evidence", "received. If you lose your password or get scamme dby", "another player, P_Mods cannot help you get your account"); - stage = 181; - break; - case 181: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "back. All they can do is recommend you to go to Player", "Support. They cannot retrieve any items you may have", "lost and they certainly do not recieve any free items", "from " + GameWorld.getSettings().getName() + " for moderating the game. They are players"); - stage = 182; - break; - case 182: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "who give their all to help the community, out of the", "goodness of their hearts! P-mods do not work for " + GameWorld.getSettings().getName() + "", "and so cannot make you a Moderator, or recommend", "other accounts to become Moderators. If you wish yo"); ->>>>>>> 3418fa0f93ea8241d77c5e9608bf40e3a441e2c5 - stage = 183; - break; - case 183: - npc("become a Moderator, feel free to ask me!"); - stage = 153; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6135, 6136, 6137, 6138, 6139 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/TraderCrewmemberDialogue.java b/Server/src/main/java/core/game/content/dialogue/TraderCrewmemberDialogue.java deleted file mode 100644 index 0776a912e..000000000 --- a/Server/src/main/java/core/game/content/dialogue/TraderCrewmemberDialogue.java +++ /dev/null @@ -1,181 +0,0 @@ -package core.game.content.dialogue; - -import core.game.content.global.travel.ship.ShipCharter; -import core.game.content.global.travel.ship.ShipCharter.Destination; -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.StringUtils; - -/** - * Handles the TraderCrewmemberDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class TraderCrewmemberDialogue extends DialoguePlugin { - - /** - * Represents the ship charter destination. - */ - private Destination destination; - - /** - * Represents the cost of charting the ship. - */ - private int cost; - - /** - * Constructs a new {@code TraderCrewmemberDialogue} {@code Object}. - */ - public TraderCrewmemberDialogue() { - } - - /** - * Constructs a new {@code TraderCrewmemberDialogue} {@code Object}. - * @param player the player. - */ - public TraderCrewmemberDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 4651, 4652, 4653, 4654, 4655, 4656, 4650 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Choose an option:", "Yes, who are you?", "Yes, I would like to charter a ship."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - player("Yes, who are you?"); - stage = 100; - break; - case 2: - player("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."); - 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..."); - stage = 102; - break; - case 102: - player("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."); - stage = 104; - break; - case 104: - player("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."); - stage = 106; - break; - case 106: - npc("Would you like to take a look?"); - stage = 107; - break; - case 107: - options("Yes.", "No."); - stage = 108; - break; - case 108: - switch (buttonId) { - case 1: - player("Yes."); - stage = 1000; - break; - case 2: - end(); - break; - - } - break; - case 1000: - end(); - npc.openShop(player); - break; - case 2000: - npc("Certainly sir, where would you like to go?"); - stage = 2001; - break; - case 2001: - end(); - ShipCharter.open(player); - break; - case 3000: - options("Ok", "Choose again", "No"); - stage = 30001; - break; - case 30001: - switch (buttonId) { - case 1: - end(); - if (cost == 0) { - destination.sail(player); - break; - } - if (!player.getInventory().containsItem(new Item(995, cost))) { - end(); - return true; - } - if (!player.getInventory().remove(new Item(995, cost))) { - interpreter.sendDialogues(player, null, "I don't have the money for that."); - stage = 30002; - return true; - } - destination.sail(player); - break; - case 2: - end(); - ShipCharter.open(player); - break; - case 3: - end(); - break; - } - break; - case 30002: - end(); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new TraderCrewmemberDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (args.length > 1) { - destination = ((Destination) args[1]); - cost = (int) args[2]; - interpreter.sendDialogue("To sail to " + StringUtils.formatDisplayName(destination.name()) + " from here will cost you " + cost + " gold.", "Are you sure you want to pay that?"); - stage = 3000; - return true; - } - npc("Can I help you?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/TraibornDialogue.java b/Server/src/main/java/core/game/content/dialogue/TraibornDialogue.java deleted file mode 100644 index d4c89c195..000000000 --- a/Server/src/main/java/core/game/content/dialogue/TraibornDialogue.java +++ /dev/null @@ -1,398 +0,0 @@ -package core.game.content.dialogue; - -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.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; - -import core.game.content.quest.free.demonslayer.DemonSlayer; - -/** - * Represents the dialogue used to handle the Traiborn NPC. - * @author 'Vexia - * @date 3/1/14 - */ -public class TraibornDialogue extends DialoguePlugin { - - /** - * Represents the bones item. - */ - private static final Item BONES = new Item(526, 25); - - /** - * Represents the object animation. - */ - @SuppressWarnings("unused") - private static final Animation OBJECT_ANIM = new Animation(4599); - - /** - * Represents the animation of giving the key. - */ - private static final Animation ANIMATION = new Animation(536); - - /** - * Represents the quest instance. - */ - private Quest quest; - - /** - * Constructs a new {@code TraibornDialogue} {@code Object}. - */ - public TraibornDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TraibornDialogue} {@code Object}. - * @param player the player. - */ - public TraibornDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TraibornDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Demon Slayer"); - switch (quest.getStage(player)) { - case 20: - if (player.getAttribute("demon-slayer:traiborn", false)) { - npc("How are you doing finding bones?"); - stage = 380; - } else { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ello young thingummywut."); - stage = 0; - } - break; - default: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ello young thingummywut."); - stage = 0; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { - case 20: - switch (stage) { - case 0: - if (player.getInventory().containsItem(DemonSlayer.THIRD_KEY) && player.getBank().containsItem(DemonSlayer.THIRD_KEY)) { - options("What's a thingummywut?", "Teach me to be a mighty powerful wizard."); - stage = 1; - } else { - options("What's a thingummywut?", "Teach me to be a mighty powerful wizard.", "I need to get a key given to you by Sir Prysin."); - stage = 1; - } - break; - case 1: - switch (buttonId) { - case 1: - case 2: - handleDefault(buttonId); - break; - case 3: - player("I need to get a key given to you by Sir Prysin."); - stage = 300; - break; - } - break; - case 300: - npc("Sir Prysin? Who's that? What would I want his key", "for?"); - stage = 301; - break; - case 301: - player("Well, have you got any keys knocking around?"); - stage = 302; - break; - case 302: - npc("Now you come to mention it, yes I do have a key. It's", "in my special closet of valuable stuff. Now how do I get", "into that?"); - stage = 303; - break; - case 303: - npc("I sealed it using one of my magic rituals. So it would", "makes sense that another ritual would open it again."); - stage = 304; - break; - case 304: - player("So do you know what ritual to use?"); - stage = 305; - break; - case 305: - npc("Let me think a second."); - stage = 306; - break; - case 306: - npc("Yes a simple drazier style ritual should suffice. Hmm,", "main problem with that is I'll need 25 sets of bones.", "Now where am I going to get hold of something like", "that?"); - stage = 307; - break; - case 307: - options("Hmm, that's too bad. I really need that key.", "I'll get the bones for you."); - stage = 308; - break; - case 308: - switch (buttonId) { - case 1: - player("Hmm, that's too bad. I really need that key."); - stage = 330; - break; - case 2: - player("I'll get the bones for you."); - stage = 340; - break; - } - break; - case 330: - npc("Ah well, sorry I couldn't be any more help."); - stage = 331; - break; - case 331: - end(); - break; - case 340: - npc("Ooh that would be very good of you."); - stage = 341; - break; - case 341: - player("Ok I'll speak to you when I've got some bones."); - stage = 342; - break; - case 342: - player.setAttribute("/save:demon-slayer:traiborn", true); - end(); - break; - case 380: - if (player.getInventory().containsItem(BONES)) { - player("I have some bones."); - stage = 382; - } else { - player("I don't have all the bones yet."); - stage = 381; - } - break; - case 382: - npc("Give 'em here then."); - stage = 383; - break; - case 383: - interpreter.sendDialogue("You give Traiborn 25 sets of bones."); - stage = 384; - break; - case 384: - npc("Hurrah! That's all 25 sets of bones."); - stage = 385; - break; - case 385: - npc.animate(new Animation(4602)); - npc("Wings of dark and colour too,", "Spreading in the morning dew;", "Locked away I have a key;", "Return it now, please, unto me."); - stage = 386; - break; - case 386: - final Scenery object = new Scenery(17434, Location.create(3113, 3161, 1), 11, 1); - SceneryBuilder.add(object); - npc.faceLocation(object.getLocation()); - npc.animate(ANIMATION); - if (!player.getInventory().containsItem(BONES)) { - end(); - return true; - } - if (player.getInventory().remove(BONES)) { - player.removeAttribute("demon-slayer:traiborn"); - player.getInventory().add(DemonSlayer.THIRD_KEY); - interpreter.sendItemMessage(DemonSlayer.THIRD_KEY.getId(), "Traiborn hands you a key."); - stage = 387; - } - GameWorld.getPulser().submit(new Pulse(1) { - int counter = 0; - - @Override - public boolean pulse() { - switch (counter++) { - case 5: - npc.face(player); - break; - case 7: - SceneryBuilder.remove(object); - return true; - } - return false; - } - }); - break; - case 387: - player("Thank you very much."); - stage = 388; - break; - case 388: - npc("Not a problem for a friend of Sir What's-his-face."); - stage = 389; - break; - case 389: - end(); - break; - case 381: - end(); - break; - } - break; - case 0: - switch (stage) { - default: - handleDefault(buttonId); - break; - } - break; - } - return true; - } - - /** - * Method used to handle the default button id. - * @param buttonId the buttton id. - */ - private final void handleDefault(int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "What's a thingummywut?", "Teach me to be a mighty and powerful wizard."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's a thingumywut?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Teach me to be a mighty and powerful wizard."); - stage = 20; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A thingummywut? Where? Where?"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Those pesky thingummywuts. They get everywhere.", "They leave a terrible mess too."); - stage = 12; - break; - case 12: - interpreter.sendOptions("Select an Option", "Err you just called me a thingymummywut.", "Tell me what they look like and I'll mask 'em."); - stage = 13; - break; - case 13: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err you just called me thingummywut."); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me what they look like and I'll mash 'em."); - stage = 120; - break; - } - break; - case 120: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't be ridiculous. No-one has ever seen one."); - stage = 121; - break; - case 121: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "They're invisible, or a myth, or a figment of my", "imagination. Can't remember which right now."); - stage = 122; - break; - case 122: - end(); - break; - case 100: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're a thingummywut? I've never seen one up close", "before. They said I was mad!"); - stage = 101; - break; - case 101: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Now you are my proof! There ARE thingummywuts in", "this tower. Now where can I find a cage big enough to", "keep you?"); - stage = 102; - break; - case 102: - interpreter.sendOptions("Select an Option", "Err I'd better be off really.", "They're right, you are mad."); - stage = 103; - break; - case 103: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err I'd better be off really."); - stage = 110; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "They're right, you are mad."); - stage = 130; - break; - } - break; - case 130: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That's a pity. I thought maybe they were winding me", "up."); - stage = 131; - break; - case 131: - end(); - break; - case 110: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh ok, have a good time, and watch out for sheep!", "They're more cunning than they look."); - stage = 111; - break; - case 111: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wizard eh? You don't want any truck with that sort.", "They're not to be trusted. That's what I've heard", "anyways."); - stage = 21; - break; - case 21: - interpreter.sendOptions("Select an Option", "So aren't you a wizard?", "Oh I'd better stop talking to you then."); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So aren't you a wizard?"); - stage = 40; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh I'd better stop talking to you then."); - stage = 60; - break; - } - break; - case 60: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Cheerio then. It was nice chatting to you."); - stage = 61; - break; - case 61: - end(); - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How dare you? Of course I'm a wizard. Now don't be", "so cheeky or I'll turn you into a frog."); - stage = 41; - break; - case 41: - end(); - break; - } - } - - @Override - public int[] getIds() { - return new int[] { 881 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/dialogue/VanessaDialogue.java b/Server/src/main/java/core/game/content/dialogue/VanessaDialogue.java deleted file mode 100644 index c9ae93385..000000000 --- a/Server/src/main/java/core/game/content/dialogue/VanessaDialogue.java +++ /dev/null @@ -1,82 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the VanessaDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class VanessaDialogue extends DialoguePlugin { - - public VanessaDialogue() { - - } - - public VanessaDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 2305 }; - } - - @Override - 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; - case 10: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you give me any Farming advice?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm okay, thank you."); - stage = 30; - break; - - } - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes - ask a gardener."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - end(); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new VanessaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello. How can I help you?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/VenenatisSpiderlingDialogue.java b/Server/src/main/java/core/game/content/dialogue/VenenatisSpiderlingDialogue.java deleted file mode 100644 index 34c5bd636..000000000 --- a/Server/src/main/java/core/game/content/dialogue/VenenatisSpiderlingDialogue.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, Venenatis JR - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class VenenatisSpiderlingDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - */ - public VenenatisSpiderlingDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - * @param player the player. - */ - public VenenatisSpiderlingDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new VenenatisSpiderlingDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "It's a damn good job I don't have arachnophobia."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "We're misunderstood. Without us in your house,", "you'd be infested with flies and other", "REAL nasties."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks for that enlightening fact."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Everybody gets one."); - stage = 3; - break; - case 3: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8599 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/VetionJRDialogue.java b/Server/src/main/java/core/game/content/dialogue/VetionJRDialogue.java deleted file mode 100644 index 83ddc67da..000000000 --- a/Server/src/main/java/core/game/content/dialogue/VetionJRDialogue.java +++ /dev/null @@ -1,79 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Dialogue for the boss pet, Vet'ion JR. - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class VetionJRDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - */ - public VetionJRDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ChaosElementalJRDialogue} {@code Object}. - * @param player the player. - */ - public VetionJRDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new VetionJRDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who is the true lord and king of the lands?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "The mighty heir and lord of the Wilderness."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where is he? Why hasn't he lifted your burden?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "I have not fulfilled my purpose."); - stage = 3; - break; - case 3: - player("What is your purpose?"); - stage = 4; - break; - case 4: - npc(FacialExpression.OLD_NORMAL, "Not what is, what was.", "A great war tore this land apart and, for my","failings in protecting this land, I carry the burden","of its waste."); - stage = 5; - break; - case 5: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8600, 8654 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/WiloughDialogue.java b/Server/src/main/java/core/game/content/dialogue/WiloughDialogue.java deleted file mode 100644 index 837b07591..000000000 --- a/Server/src/main/java/core/game/content/dialogue/WiloughDialogue.java +++ /dev/null @@ -1,239 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the dialogue used for wilough. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class WiloughDialogue extends DialoguePlugin { - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 100); - - /** - * Represents the id. - */ - private int id; - - /** - * Constructs a new {@code WiloughDialogue} {@code Object}. - */ - public WiloughDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code WiloughDialogue} {@code Object}. - * @param player the player. - */ - public WiloughDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new WiloughDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (args[0] instanceof NPC) { - id = ((NPC) args[0]).getId(); - } else if (args[0] instanceof Integer) { - id = (int) args[0]; - } - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); - switch (quest.getStage(player)) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); - stage = 0; - break; - case 10: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - stage = 130; - break; - default: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); - stage = 0; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); - switch (stage) { - case 0: - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "You think you're tough do you?"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Pardon?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "I can beat anyone up!"); - stage = 3; - break; - case 3: - interpreter.sendDialogues(781, FacialExpression.HALF_GUILTY, "He can you know!"); - stage = 4; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Really?"); - stage = 5; - break; - case 5: - interpreter.sendDialogue("The boy begins to jump around with his fists up.", "You wonder what sort of desperado he'll grow up to be."); - stage = 6; - break; - case 6: - end(); - break; - case 100:// stage 10 - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "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."); - 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."); - stage = 103; - break; - case 103: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where is this play area?"); - stage = 104; - break; - case 104: - interpreter.sendDialogues(id == 783 ? 781 : 783, null, "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?"); - stage = 106; - break; - case 106: - interpreter.sendDialogues(id == 783 ? 781 : 783, null, "Well...now you ask, I am a bit short on cash."); - stage = 107; - break; - case 107: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How much?"); - stage = 108; - break; - case 108: - interpreter.sendDialogues(id == 783 ? 781 : 783, null, "10 coins."); - stage = 109; - break; - case 109: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "10 coins?!"); - stage = 110; - break; - case 110: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "I'll handle this."); - stage = 111; - break; - case 111: - interpreter.sendDialogues(id == 783 ? 783 : id, null, "100 coins should cover it."); - stage = 112; - break; - case 112: - interpreter.sendDialogues(player, null, "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?"); - stage = 114; - break; - case 114: - interpreter.sendOptions("Select an Option", "I'm not paying you a penny.", "Okay then, I'll pay."); - stage = 115; - break; - case 115: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "I'm not paying you a penny."); - stage = 116; - break; - case 2: - interpreter.sendDialogues(player, null, "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."); - stage = 117; - break; - case 117: - end(); - break; - case 118: - if (!player.getInventory().containsItem(COINS)) { - end(); - return true; - } - if (player.getInventory().remove(COINS)) { - interpreter.sendItemMessage(COINS, "You give the lad 100 coins."); - quest.setStage(player, 20); - stage = 119; - } else { - interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough coins."); - stage = 117; - } - break; - case 119: - interpreter.sendDialogues(player, null, "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."); - stage = 121; - break; - case 121: - interpreter.sendDialogues(player, null, "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."); - 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."); - stage = 131; - break; - case 131: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 783 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/WiseOldManDialogue.java b/Server/src/main/java/core/game/content/dialogue/WiseOldManDialogue.java deleted file mode 100644 index 54a0eab0b..000000000 --- a/Server/src/main/java/core/game/content/dialogue/WiseOldManDialogue.java +++ /dev/null @@ -1,195 +0,0 @@ -package core.game.content.dialogue; - -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; - -/** - * Represents the dialogue plugin used for the wise old man. - * - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class WiseOldManDialogue extends DialoguePlugin { - - /** - * Represents the items to use. - */ - private static final Item[] ITEMS = new Item[]{new Item(9813), new Item(9814)}; - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 99000); - - /** - * Constructs a new {@code WiseOldManDialogue} {@code Object}. - */ - public WiseOldManDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code WiseOldManDialogue} {@code Object}. - * - * @param player the player. - */ - public WiseOldManDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new WiseOldManDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Greetings, " + player.getUsername() + ","); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - if (player.getQuestRepository().hasCompletedAll()) { - options("Quest Point Cape.", "Something else."); - stage = 500; - return true; - } - options("Is there anything I can do for you?", "Could you check my things for junk, please?", "I've got something I'd like you to look at."); - stage = 1; - break; - case 500: - switch (buttonId) { - case 1: - player("I believe you are the person to talk to if I want to buy", "a Quest Point Cape?"); - stage = 501; - break; - case 2: - options("Is there anything I can do for you?", "Could you check my things for junk, please?", "I've got something I'd like you to look at."); - stage = 1; - break; - } - break; - case 501: - npc("Indeed you believe rightly, " + player.getUsername() + ", and if you know that", "then you'll also know that they cost 99000 coins."); - stage = 502; - break; - case 502: - options("No, I hadn't heard that!", "Yes, so I was lead to believe."); - stage = 503; - break; - case 503: - switch (buttonId) { - case 1: - player("No, I hadn't heard that!"); - stage = 504; - break; - case 2: - player("Yes, so I was lead to believe."); - stage = 506; - break; - } - break; - case 504: - npc("Well that's the cost, and it's not changing."); - stage = 505; - break; - case 505: - end(); - break; - case 506: - if (player.getInventory().freeSlots() < 2) { - player("I don't seem to have enough inventory space."); - stage = 507; - return true; - } - if (!player.getInventory().containsItem(COINS)) { - player("I don't seem to have enough coins with", "me at this time."); - stage = 507; - return true; - } - if (player.getInventory().remove(COINS) && player.getInventory().add(ITEMS)) { - npc("Have fun with it."); - stage = 507; - } else { - player("I don't seem to have enough coins with", "me at this time."); - stage = 507; - } - break; - case 507: - end(); - break; - case 1: - switch (buttonId) { - case 1: - player("Is there anything I can do for you?"); - stage = 10; - break; - case 2: - options("Could you check my bank for junk, please?", "Could you check my inventory for junk, please?"); - stage = 20; - break; - case 3: - player("I've got something I'd like you to look at."); - stage = 30; - break; - } - - break; - case 10: - npc("Thanks, but I don't have anything I need."); - stage = 11; - break; - case 11: - end(); - break; - case 20: - npc("Certainly, but I should warn you that I don't know about", "all items."); - switch (buttonId) { - case 1: - stage = 100; - break; - case 2: - stage = 102; - break; - } - break; - case 100: - 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; - break; - case 30: - npc("Jolly good. Give it to me, and I'll tell you anything I know", "about it."); - stage = 31; - break; - case 31: - end(); - break; - } - - return true; - } - - @Override - public int[] getIds() { - return new int[]{2253, 3820}; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/WydinDialogue.java b/Server/src/main/java/core/game/content/dialogue/WydinDialogue.java deleted file mode 100644 index 31ff4db81..000000000 --- a/Server/src/main/java/core/game/content/dialogue/WydinDialogue.java +++ /dev/null @@ -1,232 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue that handles the wydin npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class WydinDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code WydinDialogue} {@code Object}. - */ - public WydinDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code WydinDialogue} {@code Object}. - * @param player the player. - */ - public WydinDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new WydinDialogue(player); - } - - @Override - public boolean open(Object... args) { - boolean door = false; - if (args.length == 2) - door = (boolean) args[1]; - if (door) { - if (player.getSavedData().getGlobalData().isWydinEmployee()) { - 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."); - stage = 100; - } - return true; - } else { - npc = (NPC) args[0]; - } - if (player.getSavedData().getGlobalData().isWydinEmployee()) { - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "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?"); - stage = 0; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (player.getSavedData().getGlobalData().isWydinEmployee()) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, can I work out front now?", "Yes, are you going to pay me yet?", "No, it's a complete mess.", "Can I buy something please?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, it's a complete mess."); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I buy something please?"); - stage = 40; - break; - - } - break; - case 10: - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "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."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, it's a complete mess."); - stage = 31; - break; - case 31: - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "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."); - stage = 41; - break; - case 41: - end(); - npc.openShop(player); - break; - case 100: - end(); - break; - } - } else { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes please.", "No, thank you.", "What can you recommend?", "Can I get a job here?"); - 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, "What can you recommend?"); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I get a job here?"); - stage = 40; - break; - } - break; - case 10: - end(); - npc.openShop(player); - break; - case 20: - end(); - break; - case 30: - interpreter.sendDialogues(557, null, "We have this really exotic fruit all the way from", "Karamja. It's called a banana."); - stage = 31; - break; - case 31: - interpreter.sendOptions("Select an Option", "Hmm, I think I'll try one.", "I don't like the sound of that."); - stage = 32; - break; - case 32: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Hmm, I think I'll try one."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, null, "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?"); - 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."); - stage = 42; - } else { - interpreter.sendDialogues(player, null, "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."); - stage = 43; - break; - case 43: - interpreter.sendDialogues(player, null, "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."); - 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."); - stage = 46; - break; - case 46: - end(); - 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."); - stage = 100; - break; - case 100: - end(); - break; - } - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 557 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/YadvigaDialogue.java b/Server/src/main/java/core/game/content/dialogue/YadvigaDialogue.java deleted file mode 100644 index cfebf47e3..000000000 --- a/Server/src/main/java/core/game/content/dialogue/YadvigaDialogue.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/YuriDialogue.java b/Server/src/main/java/core/game/content/dialogue/YuriDialogue.java deleted file mode 100644 index 48c40855b..000000000 --- a/Server/src/main/java/core/game/content/dialogue/YuriDialogue.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/ZamboDialogue.java b/Server/src/main/java/core/game/content/dialogue/ZamboDialogue.java deleted file mode 100644 index f75fa6aa9..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ZamboDialogue.java +++ /dev/null @@ -1,75 +0,0 @@ -package core.game.content.dialogue; - -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/java/core/game/content/dialogue/ZekeDialogue.java b/Server/src/main/java/core/game/content/dialogue/ZekeDialogue.java deleted file mode 100644 index 3b9817ad7..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ZekeDialogue.java +++ /dev/null @@ -1,135 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the dialogue plugin used for zeke. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ZekeDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ZekeDialogue} {@code Object}. - */ - public ZekeDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ZekeDialogue} {@code Object}. - * @param player the player. - */ - public ZekeDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ZekeDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A thousand greetings, sir."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Do you want to trade?", "Nice cloak.", "Could you sell me a dragon scimitar?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you want to trade?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nice cloak."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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."); - stage = 11; - break; - case 11: - npc.openShop(player); - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Thank you."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A dragon scimitar? A DRAGON scimitar?"); - stage = 31; - break; - case 31: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - 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."); - stage = 34; - break; - case 34: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - stage = 36; - break; - case 36: - interpreter.sendOptions("Select an Option", "Yes, please, Zeke.", "Not today, thank you."); - stage = 37; - break; - case 37: - switch (buttonId) { - case 1: - npc.openShop(player); - end(); - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No today, thank you."); - stage = 2000; - break; - } - break; - case 2000: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 541 }; - } - -} diff --git a/Server/src/main/java/core/game/content/dialogue/ZilyanaJRDialogue.java b/Server/src/main/java/core/game/content/dialogue/ZilyanaJRDialogue.java deleted file mode 100644 index 1c7b2bb96..000000000 --- a/Server/src/main/java/core/game/content/dialogue/ZilyanaJRDialogue.java +++ /dev/null @@ -1,73 +0,0 @@ -package core.game.content.dialogue; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Dialogue for the boss pet, Commander Zilyana JR. - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class ZilyanaJRDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ZilyanaJRDialogue} {@code Object}. - */ - public ZilyanaJRDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ZilyanaJRDialogue} {@code Object}. - * @param player the player. - */ - public ZilyanaJRDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ZilyanaJRDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if(player.getInventory().containsItem(new Item(11698, 1)) || (player.getEquipment().get(3) != null && player.getEquipment().get(3).getId() == 11698)){ - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I FOUND THE GODSWORD!"); - stage = 0; - } else { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "FIND THE GODSWORD!"); - stage = 1; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "GOOD!"); - stage = 2; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "FIND THE GODSWORD!"); - stage = 2; - break; - case 2: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 8593 }; - } -} diff --git a/Server/src/main/java/core/game/content/dialogue/book/Book.java b/Server/src/main/java/core/game/content/dialogue/book/Book.java deleted file mode 100644 index 561284a40..000000000 --- a/Server/src/main/java/core/game/content/dialogue/book/Book.java +++ /dev/null @@ -1,193 +0,0 @@ -package core.game.content.dialogue.book; - -import core.game.component.Component; -import core.game.content.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) { - } - - /** - * 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/java/core/game/content/dialogue/book/BookLine.java b/Server/src/main/java/core/game/content/dialogue/book/BookLine.java deleted file mode 100644 index d274e0079..000000000 --- a/Server/src/main/java/core/game/content/dialogue/book/BookLine.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.content.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/java/core/game/content/dialogue/book/GrimDiary.java b/Server/src/main/java/core/game/content/dialogue/book/GrimDiary.java deleted file mode 100644 index e0ec70bb4..000000000 --- a/Server/src/main/java/core/game/content/dialogue/book/GrimDiary.java +++ /dev/null @@ -1,93 +0,0 @@ -package core.game.content.dialogue.book; - -import core.game.component.Component; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -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/java/core/game/content/dialogue/book/KeldagrimRuleBook.java b/Server/src/main/java/core/game/content/dialogue/book/KeldagrimRuleBook.java deleted file mode 100644 index bcae90ba4..000000000 --- a/Server/src/main/java/core/game/content/dialogue/book/KeldagrimRuleBook.java +++ /dev/null @@ -1,106 +0,0 @@ -package core.game.content.dialogue.book; - -import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -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/java/core/game/content/dialogue/book/Page.java b/Server/src/main/java/core/game/content/dialogue/book/Page.java deleted file mode 100644 index 96196b272..000000000 --- a/Server/src/main/java/core/game/content/dialogue/book/Page.java +++ /dev/null @@ -1,31 +0,0 @@ -package core.game.content.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/java/core/game/content/dialogue/book/PageSet.java b/Server/src/main/java/core/game/content/dialogue/book/PageSet.java deleted file mode 100644 index bce595b70..000000000 --- a/Server/src/main/java/core/game/content/dialogue/book/PageSet.java +++ /dev/null @@ -1,29 +0,0 @@ -package core.game.content.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/java/core/game/content/dialogue/book/SecurityBookPlugin.java b/Server/src/main/java/core/game/content/dialogue/book/SecurityBookPlugin.java deleted file mode 100644 index 7dae0c0eb..000000000 --- a/Server/src/main/java/core/game/content/dialogue/book/SecurityBookPlugin.java +++ /dev/null @@ -1,135 +0,0 @@ -package core.game.content.dialogue.book; - -import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import rs09.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/java/core/game/content/dialogue/book/StrongholdNotes.java b/Server/src/main/java/core/game/content/dialogue/book/StrongholdNotes.java deleted file mode 100644 index d33f23741..000000000 --- a/Server/src/main/java/core/game/content/dialogue/book/StrongholdNotes.java +++ /dev/null @@ -1,96 +0,0 @@ -package core.game.content.dialogue.book; - -import core.game.component.Component; -import core.plugin.Initializable; -import core.game.content.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/java/core/game/content/eco/EcoStatus.java b/Server/src/main/java/core/game/content/eco/EcoStatus.java deleted file mode 100644 index 046ed2b53..000000000 --- a/Server/src/main/java/core/game/content/eco/EcoStatus.java +++ /dev/null @@ -1,24 +0,0 @@ -package core.game.content.eco; - -/** - * The several statuses used for the economy. - * @author Emperor - */ -public enum EcoStatus { - - /** - * The status set when money should be drained from the economy. - */ - DRAINING, - - /** - * The status set when money should be pumped into the economy. - */ - BOOSTING, - - /** - * If we are maintaining the current economy. - */ - MAINTAINING; - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/eco/EconomyManagement.java b/Server/src/main/java/core/game/content/eco/EconomyManagement.java deleted file mode 100644 index f8d5788d5..000000000 --- a/Server/src/main/java/core/game/content/eco/EconomyManagement.java +++ /dev/null @@ -1,68 +0,0 @@ -package core.game.content.eco; - -/** - * Represents a managing class for the economy. - * @author Emperor - */ -public final class EconomyManagement { - - /** - * The current economy state. - */ - private static EcoStatus ecoState = EcoStatus.BOOSTING; - - /** - * The modification rate. - */ - private static double modificationRate = 10; - - /** - * Sets the ecoState. - * @param ecoState The ecoState to set. - */ - public static void update(EcoStatus ecoState, double modificationRate) { - boolean update = EconomyManagement.ecoState != ecoState; - EconomyManagement.ecoState = ecoState; - if (EconomyManagement.modificationRate != modificationRate) { - EconomyManagement.modificationRate = modificationRate; - update = true; - } - if (update) { - System.out.println("-------------------------------------------------------------------"); - System.out.println(" Switched economy management status to " + ecoState + " with a rate of " + modificationRate + " |"); - System.out.println("-------------------------------------------------------------------"); - } - } - - /** - * Sets the ecoState. - * @param ecoState The ecoState to set. - */ - public static void updateEcoState(EcoStatus ecoState) { - update(ecoState, modificationRate); - } - - /** - * Sets the modificationRate. - * @param modificationRate The modificationRate to set. - */ - public static void updateModificationRate(double modificationRate) { - update(ecoState, modificationRate); - } - - /** - * Gets the ecoState. - * @return The ecoState. - */ - public static EcoStatus getEcoState() { - return ecoState; - } - - /** - * Gets the modificationRate. - * @return The modificationRate. - */ - public static double getModificationRate() { - return modificationRate; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/Bones.kt b/Server/src/main/java/core/game/content/global/Bones.kt deleted file mode 100644 index 8bdb2f183..000000000 --- a/Server/src/main/java/core/game/content/global/Bones.kt +++ /dev/null @@ -1,146 +0,0 @@ -package core.game.content.global - -import core.game.content.global.Bones -import core.game.node.item.Item -import java.util.ArrayList -import java.util.HashMap - -/** - * 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), - BURNST_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); - /** - * Get the bone experience given when you bury the bone. - * @return The experience. - */ - - /** - * Gets the bone meal item. - * @return the item. - */ - val boneMeal: Item - get() { - return when(this){ - FAYRG -> Item(4852) - RAURG_BONES -> Item(4853) - OURG_BONES -> Item(4854) - else -> Item(4255 + ordinal) - } - } - - /** - * Gets the config value for the bone type. - * @param hopper the hopper. - * @return the value. - */ - fun getConfigValue(hopper: Boolean): Int { - return (if (this == BONES_2) ordinal else ordinal - 1) or (if (hopper) 4 else 8) shl 16 - } - - companion object { - /** - * Holds all bones. - */ - private val bones = HashMap() - - /** - * Gets the bones for the bone meal. - * @param itemId the item. - * @return the 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) { - return bone - } - } - return null - } - - /** - * Gets the bone ids. - * @return the ids. - */ - val array: IntArray - @JvmStatic get() { - val list: MutableList = ArrayList(20) - for (i in bones.keys) { - list.add(i) - } - val array = IntArray(list.size) - for (i in list.indices) { - array[i] = list[i] - } - return array - } - - /** - * Get the bone. - * @param itemId The item id. - * @return The bone. - */ - @JvmStatic - fun forId(itemId: Int): Bones? { - return bones[itemId] - } - - /** - * Construct the bones. - */ - init { - for (bone in values()) { - bones[bone.itemId] = bone - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/BossKillCounter.java b/Server/src/main/java/core/game/content/global/BossKillCounter.java deleted file mode 100644 index 319b0dcdb..000000000 --- a/Server/src/main/java/core/game/content/global/BossKillCounter.java +++ /dev/null @@ -1,177 +0,0 @@ -package core.game.content.global; - -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/java/core/game/content/global/EnchantedJewellery.java b/Server/src/main/java/core/game/content/global/EnchantedJewellery.java deleted file mode 100644 index b00c4d6d5..000000000 --- a/Server/src/main/java/core/game/content/global/EnchantedJewellery.java +++ /dev/null @@ -1,307 +0,0 @@ -package core.game.content.global; - -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 rs09.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; - -/** - * Represents an enchanted jewellery. - * @author Vexia - */ -public enum EnchantedJewellery { - RING_OF_SLAYING(new String[] { "Slayer Tower.", "Fremmenik Slayer Dungeon.", "Pollniveach Slayer Dungeon.", "Nowhere." }, new Location[] { Location.create(3429, 3533, 0), Location.create(2793, 3615, 0), Location.create(3313, 2960, 0) }, true, 13281, 13282, 13283, 13284, 13285, 13286, 13287, 13288), - RING_OF_DUELING(new String[] { "Al Kharid Duel Arena.", "Castle Wars Arena.", "Nowhere." }, new Location[] { Location.create(3314, 3235, 0), Location.create(2442, 3089, 0) }, true, 2552, 2554, 2556, 2558, 2560, 2562, 2564, 2566), - AMULET_OF_GLORY(new String[] { "Edgeville", "Karamja", "Draynor Village", "Al-Kharid", "Nowhere." }, new Location[] { Location.create(3087, 3495, 0), Location.create(2919, 3175, 0), Location.create(3104, 3249, 0), Location.create(3304, 3124, 0) }, 1712, 1710, 1708, 1706, 1704), - AMULET_OF_GLORY_T(new String[] { "Edgeville", "Karamja", "Draynor Village", "Al-Kharid", "Nowhere." }, new Location[] { Location.create(3087, 3495, 0), Location.create(2919, 3175, 0), Location.create(3081, 3250, 0), Location.create(3304, 3124, 0) }, 10354, 10356, 10358, 10360, 10362), - GAMES_NECKLACE(new String[] { "Burthorpe", "Barbarian Assault", "Clan Wars", "Bounty Hunter", "Corporeal Beast" }, new Location[] { Location.create(2899, 3563, 0), Location.create(2520, 3571, 0), Location.create(3266, 3686, 0), Location.create(3179, 3685, 0), Location.create(2885, 4372, 2) }, true, 3853, 3855, 3857, 3859, 3861, 3863, 3865, 3867), - DIGSITE_PENDANT(new String[] {}, new Location[] { Location.create(3342, 3445, 0) }, true, 11194, 11193, 11192, 11191, 11190), - COMBAT_BRACELET(new String[] { "Champions' Guild", "Monastery", "Ranging Guild", "Warriors' Guild", "Nowhere." }, new Location[] { Location.create(3191, 3365, 0), Location.create(3052, 3472, 0), Location.create(2657, 3439, 0), Location.create(2878, 3546, 0) }, 11118, 11120, 11122, 11124, 11126), - SKILLS_NECKLACE(new String[] { "Fishing Guild", "Mining Guild", "Crafting Guild", "Cooking Guild", "Nowhere." }, new Location[] { Location.create(2611, 3392, 0), Location.create(3016, 3338, 0), Location.create(2933, 3290, 0), Location.create(3143, 3442, 0) }, 11105, 11107, 11109, 11111, 11113), - RING_OF_WEALTH(new String[] {"Grand Exchange","Nowhere."}, new Location[] {Location.create(3163, 3464, 0)},14646,14644,14642,14640,14638); - - /** - * 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); - - /** - * Represents the charge numbers. - */ - private static final char[] NUMBERS = new char[] { '1', '2', '3', '4', '5', '6', '7', '8' }; - - /** - * Represents the teleport options. - */ - private final String[] options; - - /** - * Represents the locations. - */ - private final Location[] locations; - - /** - * Represents the ids of the jewellery. - */ - private final int[] ids; - - /** - * Represents if it crumbles away into nothing. - */ - private final boolean crumble; - - /** - * Constructs a new {@code EnchantedJewelleryPlugin} {@code Object}. - * @param options the options. - * @param locations the locations. - * @parma crumble if it crumbles. - * @param ids the ids. - */ - EnchantedJewellery(final String[] options, final Location[] locations, final boolean crumble, final int... ids) { - this.options = options; - this.locations = locations; - this.ids = ids; - this.crumble = crumble; - } - - /** - * Constructs a new {@code EnchantedJewelleryPlugin} {@code Object}. - * @param options the options. - * @param locations the locations. - * @param ids the ids. - */ - EnchantedJewellery(final String[] options, final Location[] locations, final int... ids) { - this(options, locations, false, ids); - } - - /** - * Method used to teleport the player to the desired location. - * @param player the player. - * @param item the item. - * @param index the index. - * @param operate If the player is operating. - */ - public void use(final Player player, final Item item, final int index, boolean operate) { - if ((index + 1) == getIds().length || item.getSlot() < 0) { - return; - } - if (index > getLocations().length - 1) { - return; - } - int itemIndex = getItemIndex(item); - Item replace = item; - if (!isLast(itemIndex)) { - if (!(isCrumble() && itemIndex == getIds().length - 1)) { - replace = getReplace(getNext(itemIndex)); - } - } else { - if (!isCrumble()) { - replace = getReplace(getIds()[getIds().length - 1]); - } - } - if (index > getLocations().length - 1) { - return; - } - if (!operate && !player.getInventory().containsItem(item)) { - player.sendMessage("Ooops, you don't have it anymore ;)"); - return; - } else if (operate && !player.getEquipment().containsItem(item)) { - player.sendMessage("Ooops, you don't have it anymore ;)"); - return; - } - if (teleport(player, itemIndex, replace, getLocation(index))) { - if (!isLast(itemIndex) && !(isCrumble() && itemIndex == getIds().length - 1)) { - if (operate) { - player.getEquipment().replace(replace, item.getSlot()); - } else { - player.getInventory().replace(replace, item.getSlot()); - } - } else { - if (isCrumble()) { - if (operate) { - player.getEquipment().replace(null, item.getSlot()); - } else { - if(item.getName().contains("slaying")){ - player.getInventory().replace(new Item(4155, 1), item.getSlot()); - player.sendMessage("Your Ring of Slaying reverts back into a regular enchanted gem."); - } else { - player.getInventory().replace(null, item.getSlot()); - } - } - } - } - if (getLocation(index).withinDistance(new Location(2657,3439,0))) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE,2,0); - } - } - } - - /** - * Method used to teleport to a location. - * @param player the player. - * @param itemIndex the old item index. - * @param item the item. - * @param location the location. - */ - private boolean teleport(final Player player, final int itemIndex, final Item item, final Location location) { - if (player.isTeleBlocked()) { - player.sendMessage("A magical force has stopped you from teleporting."); - return false; - } - if (!player.getZoneMonitor().teleport(1, item)) { - return false; - } - player.lock(); - player.visualize(ANIMATION, GRAPHICS); - player.getAudioManager().send(200); - 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; - } - - /** - * Gets the charges of an item. - * @param item the item. - * @return the charges. - */ - public static String getCharges(Item item) { - String[] tokens = item.getName().replace("(t", "(").replace("(", " ").replace(")", "").split(" "); - return tokens[tokens.length - 1]; - } - - /** - * Gets the replacement item. - * @param id the id. - * @return the item. - */ - public Item getReplace(int id) { - return new Item(id); - } - - /** - * Gets the name. - * @param item the item. - * @return the name. - */ - public String getName(Item item) { - String name = item.getName().toLowerCase().replace("(t", "(").replace("(", "").replace(")", ""); - for (char number : NUMBERS) { - name = name.replace(number, '/'); - } - return name.trim().replace("/", ""); - } - - /** - * Gets the name type. - * @param item the item. - * @return - */ - public String getNameType(Item item) { - return this == GAMES_NECKLACE ? "games necklace" : this == DIGSITE_PENDANT ? "necklace" : this == COMBAT_BRACELET ? "bracelet" : this == SKILLS_NECKLACE ? "necklace" : item.getName().toLowerCase().split(" ")[0]; - } - - /** - * Checks if the index is last. - * @param index the index. - * @return {@code True} if so. - */ - public boolean isLast(int index) { - return !isCrumble() ? index == (ids.length - 1) : index == ids.length; - } - - /** - * Gets the next index. - * @param index the index. - * @return the new id - */ - public int getNext(int index) { - return ids[index + 1]; - } - - /** - * Gets the location. - * @param index the index. - * @return the location. - */ - public Location getLocation(int index) { - if (index > locations.length) { - index = locations.length - 1; - } - return locations[index]; - } - - /** - * Gets the options. - * @return The options. - */ - public String[] getOptions() { - return options; - } - - /** - * Gets the locations. - * @return The locations. - */ - public Location[] getLocations() { - return locations; - } - - /** - * Gets the ids. - * @return The ids. - */ - public int[] getIds() { - return ids; - } - - public boolean isCrumble() { - return crumble; - } - - /** - * Gets the enchanted jewellery. - * @param item the item. - * @return {@code EnchantedJewellery}. - */ - public static EnchantedJewellery forItem(final Item item) { - for (EnchantedJewellery jewellery : values()) { - for (int i : jewellery.getIds()) { - if (i == item.getId()) { - return jewellery; - } - } - } - return null; - } - - /** - * Gets the index. - * @param item the item. - * @return the item index. - */ - public int getItemIndex(Item item) { - for (int i = 0; i < getIds().length; i++) { - if (getIds()[i] == item.getId()) { - return i; - } - } - return -1; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/RepairItem.java b/Server/src/main/java/core/game/content/global/RepairItem.java deleted file mode 100644 index 00ebd7a36..000000000 --- a/Server/src/main/java/core/game/content/global/RepairItem.java +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.content.global; - -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/java/core/game/content/global/SkillingPets.java b/Server/src/main/java/core/game/content/global/SkillingPets.java deleted file mode 100644 index 8c34f7d0c..000000000 --- a/Server/src/main/java/core/game/content/global/SkillingPets.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.content.global; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import rs09.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/java/core/game/content/global/action/DropItemHandler.java b/Server/src/main/java/core/game/content/global/action/DropItemHandler.java deleted file mode 100644 index 5b6da8cc9..000000000 --- a/Server/src/main/java/core/game/content/global/action/DropItemHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -package core.game.content.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 rs09.game.system.SystemLogger; -import rs09.game.system.config.ItemConfigParser; -import rs09.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 (player.getAttribute("equipLock:" + item.getId(), 0) > GameWorld.getTicks()) { - SystemLogger.logAlert(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/java/core/game/content/global/action/SpecialLadders.java b/Server/src/main/java/core/game/content/global/action/SpecialLadders.java deleted file mode 100644 index ec9e56941..000000000 --- a/Server/src/main/java/core/game/content/global/action/SpecialLadders.java +++ /dev/null @@ -1,86 +0,0 @@ -package core.game.content.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 java.util.Arrays; -import java.util.HashMap; - -public enum SpecialLadders implements LadderAchievementCheck { - HAM_STORAGE_UP(new Location(2567,5185,0), new Location(3166,9623,0)), - GEM_MINE(new Location(2821,2996,0), new Location(2838,9387,0)), - GEM_MINE_UP(new Location(2838,9388,0), new Location(2820, 2996, 0)), - BEAR_CAGE_UP(Location.create(3230, 9904, 0),Location.create(3231, 3504, 0)), - BEAR_CAGE_DOWN(Location.create(3230,3508,0),Location.create(3229, 9904, 0)), - GLARIAL_EXIT(Location.create(2556,9844,0),Location.create(2557,3444,0)), - SWENSEN_DOWN(Location.create(2644, 3657, 0), Location.create(2631, 10006, 0)), - 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)), - 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)), - - 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)), - - 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)), - - PORT_SARIM_RAT_PITS_DOWN(new Location(3018,3232,0), new Location(2962,9650,0)) { - @Override - public void checkAchievement(Player player) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR,1,11); - } - }, - PORT_SARIM_RAT_PITS_UP(new Location(2962,9651,0), new Location(3018,3231,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)), - SEERS_VILLAGE_SPINNING_HOUSE_ROOFTOP_UP(new Location(2715,3472,1), new Location(2714,3472,3)) { - @Override - public void checkAchievement(Player player) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.SEERS_VILLAGE,1,3); - } - }, - SEERS_VILLAGE_SPINNING_HOUSE_ROOFTOP_DOWN(new Location(2715,3472,3), new Location(2714,3472,1)), - ELEMENTAL_WORKSHOP_STAIRS_DOWN(Location.create(2710,3497, 0), Location.create(2713,9887, 0)), - ELEMENTAL_WORKSHOP_STAIRS_UP(Location.create(2714,9887, 0), Location.create(2709,3498, 0)); - - private static HashMap destinationMap = new HashMap<>(); - private static HashMap ladderMap = new HashMap<>(); - static { - Arrays.stream(SpecialLadders.values()).forEach(entry -> { - destinationMap.putIfAbsent(entry.ladderLoc,entry.destLoc); - }); - Arrays.stream(SpecialLadders.values()).forEach(entry -> { - ladderMap.putIfAbsent(entry.ladderLoc, entry); - }); - } - - private Location ladderLoc,destLoc; - SpecialLadders(Location ladderLoc, Location destLoc){ - this.ladderLoc = ladderLoc; - this.destLoc = destLoc; - } - - public static void add(Location from, Location to){ - destinationMap.put(from,to); - } - - public static Location getDestination(Location loc){ - return destinationMap.get(loc); - } - public static SpecialLadders getSpecialLadder(Location loc) { - return ladderMap.get(loc); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/shop/Shop.kt b/Server/src/main/java/core/game/content/global/shop/Shop.kt deleted file mode 100644 index 08034c357..000000000 --- a/Server/src/main/java/core/game/content/global/shop/Shop.kt +++ /dev/null @@ -1,865 +0,0 @@ -package core.game.content.global.shop - -import rs09.game.world.GameWorld.ticks -import api.ContentAPI.amountInInventory -import rs09.game.system.SystemLogger.logInfo -import core.game.node.entity.player.link.diary.DiaryType -import core.cache.def.impl.ItemDefinition -import core.game.container.Container -import core.game.container.ContainerType -import core.game.node.entity.player.Player -import core.game.node.item.Item -import org.rs09.consts.Items.BLACK_CHAINBODY_1107 -import org.rs09.consts.Items.BOWL_1923 -import org.rs09.consts.Items.BUCKET_1925 -import org.rs09.consts.Items.CAKE_TIN_1887 -import org.rs09.consts.Items.CANDLE_36 -import org.rs09.consts.Items.CHAOS_RUNE_562 -import org.rs09.consts.Items.CHISEL_1755 -import org.rs09.consts.Items.DEATH_RUNE_560 -import org.rs09.consts.Items.EMPTY_POT_1931 -import org.rs09.consts.Items.HAMMER_2347 -import org.rs09.consts.Items.JUG_1935 -import org.rs09.consts.Items.NEWCOMER_MAP_550 -import org.rs09.consts.Items.SECURITY_BOOK_9003 -import org.rs09.consts.Items.SHEARS_1735 -import org.rs09.consts.Items.TINDERBOX_590 -import org.rs09.consts.Items.TOKKUL_6529 -import rs09.game.system.config.ItemConfigParser -import java.lang.IndexOutOfBoundsException -import java.util.Arrays -import java.util.ArrayList - -/** - * A class representing a shop. - * - * @author 'Vexia - * @author Jamix77 - */ -open class Shop @JvmOverloads constructor( - /** - * Represents the title of the shop. - */ - val title: String, - /** - * Represents the items in the store. - */ - var items: Array, - /** - * Represents if it's a general store. - */ - val isGeneral: Boolean, - /** - * Represents the currency the shop allows. - */ - val currency: Int = COINS, - /** - * If the shop buys for high alch. - */ - val isHighAlch: Boolean = false -) { - /** - * Gets the containers. - * - * @return The containers. - */ - /** - * Represents the shop containers. - */ - val containers = arrayOf(Container(40, ContainerType.SHOP), Container(40, ContainerType.SHOP)) - /** - * Gets the viewers. - * - * @return The viewers. - */ - /** - * Represents the list of shop viewers. - */ - val viewers: List = ArrayList(20) - /** - * Gets the title. - * - * @return The title. - */ - /** - * Gets the items. - * - * @return The items. - */ - /** - * Gets the general. - * - * @return The general. - */ - /** - * Gets the currency. - * - * @return The currency. - */ - /** - * Gets the bhighAlch. - * - * @return the highAlch - */ - /** - * Gets the SellAllFor value. - * - * @return the sellAllFor - */ - /** - * Sell price for all shop items, if needed. - */ - var sellAllFor = 0 - private set - /** - * Gets the lastRestock. - * - * @return the lastRestock - */ - /** - * Sets the balastRestock. - * - * @param lastRestock the lastRestock to set. - */ - /** - * The last restock. - */ - var lastRestock = 0 - /** - * Check if shop should restock. - * - * @return the restock - */ - /** - * Sets the restock. - * - * @param reStock - */ - /** - * If the shop should restock. - */ - var isRestock = false - /** - * Gets the pointShop. - * - * @return the pointShop - */ - /** - * Sets the pointShop. - * - * @param pointShop the pointShop to set. - */ - /** - * If it's a point shop. - */ - var isPointShop = false - /** - * Gets the npcs. - * - * @return the npcs - */ - /** - * Sets the banpcs. - * - * @param npcs the npcs to set. - */ - /** - * The npcs of the shop. - */ - var npcs: IntArray = intArrayOf() - - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param items the items. - * @param general the general. - * @param currency the currency. - * @param highAlch if high alch. - * @param restock if restock. - */ - constructor( - title: String, - items: Array, - general: Boolean, - currency: Int, - highAlch: Boolean, - restock: Boolean, - sellAllFor: Int - ) : this(title, items, general, currency, highAlch) { - this.sellAllFor = sellAllFor - isRestock = restock - } - - /** - * Constructs a new `Shop` `Object` - * - * @param title the shop title - * @param items items the shop can handle - * @param general is this a general store - * @param currency what currency is used - */ - constructor(title: String, items: Array, npcs: IntArray, general: Boolean, currency: Int) : this( - title, - items, - general, - currency, - false - ) { - this.npcs = npcs - } - - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param general the general. - */ - constructor(title: String, general: Boolean) : this(title, GENERAL_STORE_ITEMS, general) {} - - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param general the general. - * @param highAlch if highAlch. - */ - constructor(title: String, general: Boolean, highAlch: Boolean) : this( - title, - GENERAL_STORE_ITEMS, - general, - COINS, - highAlch - ) { - } - - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param general the general. - * @param highAlch if highAlch. - */ - constructor(title: String, general: Boolean, currency: Int, highAlch: Boolean) : this( - title, - GENERAL_STORE_ITEMS, - general, - currency, - highAlch - ) { - } - - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param items the items. - * @param npcs the npcs. - * @param general the general. - */ - constructor(title: String, items: Array, npcs: IntArray, general: Boolean) : this( - title, - items, - general, - COINS, - false - ) { - this.npcs = npcs - } - - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param npcs the npcs. - * @param general the general. - */ - constructor(title: String, npcs: IntArray, general: Boolean) : this(title, GENERAL_STORE_ITEMS, npcs, general) { - this.npcs = npcs - } - - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param npcs the npcs. - * @param general the general. - * @param highAlch if highAlch. - */ - constructor(title: String, npcs: IntArray, general: Boolean, highAlch: Boolean) : this( - title, - GENERAL_STORE_ITEMS, - general, - 995, - highAlch - ) { - this.npcs = npcs - } - - /** - * Method used to open the shop. - * - * @param player the shop. - */ - fun open(player: Player) { - ShopViewer.extend(player, this).open() - - // Browse the Lumbridge General Store - if (title.equals("Lumbridge General Store", ignoreCase = true)) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 18) - } - - // Browse through Oziach's Armour Shop - if (title.equals("Oziach's Armour", ignoreCase = true)) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 20) - } - } - - fun give(player: Player, slot: Int, amount: Int, tabIndex: Int) { - val container = getContainer(tabIndex) - val item = container[slot] ?: return - val add = Item(item.id, amount) - if (add.amount < 1 || !player.inventory.hasSpaceFor(add)) { - player.packetDispatch.sendMessage("You have no inventory space at the moment and cannot get anything.") - return - } - add.amount = getAmount(player, add) - if (add.amount < 1 || !player.inventory.hasSpaceFor(add)) { - player.packetDispatch.sendMessage("You have no inventory space at the moment and cannot get anything.") - return - } - player.inventory.add(add) - update() - } - - /** - * Method used to buy an item from the shop. - * - * @param slot the slot. - * @param amount the amount. - */ - fun buy(player: Player, slot: Int, amount: Int, tabIndex: Int) { - var amount = amount - if (tabIndex == 1 && player.ironmanManager.checkRestriction()) { - return - } - val container = getContainer(tabIndex) - val item = container[slot] ?: return - if (item.amount == 0) { - player.packetDispatch.sendMessage("There is no stock of that item at the moment.") - return - } - if (amount > item.amount && item.amount != -1) { - amount = item.amount - } - val add = Item(item.id, amount) - if (player.inventory.getMaximumAdd(add) < amount) { - add.amount = player.inventory.getMaximumAdd(add) - } - if (add.amount < 1 || !player.inventory.hasSpaceFor(add)) { - player.packetDispatch.sendMessage("You have no inventory space at the moment and cannot buy anything.") - return - } - add.amount = getAmount(player, add) - if (add.amount < 1 || !player.inventory.hasSpaceFor(add)) { - player.packetDispatch.sendMessage("You have no inventory space at the moment and cannot buy anything.") - return - } - val price = add.amount * getBuyPrice(item, player) - val currency = Item(currency, price) - if (!canBuy(player, item, price, currency)) { - return - } - if (handleBuy(player, currency)) { - if (isPointShop) { - decrementPoints(player, price) - } - if (tabIndex == 0) { - if (container.getAmount(item) != -1) container.replace( - Item( - item.id, - container.getAmount(item) - add.amount - ), slot, true - ) - } else { - container.remove(add) - container.shift() - } - - // Achievement Diary Handlers - if (add.id == BLACK_CHAINBODY_1107 && title.equals("Wayne's Chains", ignoreCase = true) && !player.getAttribute("diary:falador:black-chain-bought", false)) { - player.setAttribute("/save:diary:falador:black-chain-bought", true) - } - if (add.id == 12622 && title.equals("Sarah's Farming Shop", ignoreCase = true)) { - player.achievementDiaryManager.finishTask(player, DiaryType.FALADOR, 0, 0) - } - if (add.id == CANDLE_36 && title.equals("Candle Shop", ignoreCase = true)) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 9) - } - if (title.equals("Ranging Guild Ticket Exchange", ignoreCase = true)) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 1, 8) - } - player.inventory.add(add) - update() - } - } - - /** - * Method used to sell an item to the shop. - * - * @param slot the slot. - * @param amount the amount. - */ - fun sell(player: Player, slot: Int, amount: Int, tabIndex: Int) { - var amount = amount - var tabIndex = tabIndex - val item = player.inventory[slot] ?: return - val def = ItemDefinition.forId(item.id) - if (!canSell(player, item, def)) { - return - } - val container = getContainer(item) - if (amount > player.inventory.getAmount(item)) { - amount = player.inventory.getAmount(item) - } - var add = Item(item.id, amount) - if (add.amount > container.getMaximumAdd(add)) { - add.amount = container.getMaximumAdd(add) - } - player.debug("" + add.amount) - if (!container.hasSpaceFor(add) || add.amount < 1) { - player.packetDispatch.sendMessage("The shop has ran out of space.") - return - } - val currency = Item(currency, getSellingValue(add, player)) - if (item.definition.isStackable) { - if (!player.inventory.hasSpaceFor(currency)) { - player.packetDispatch.sendMessage("You don't have enough space for that many " + currency.name.toLowerCase() + ".") - return - } - } - player.debug("Selling item") - if (player.inventory.remove(add, slot, true)) { - if (currency.amount > player.inventory.getMaximumAdd(currency)) { - currency.amount = player.inventory.getMaximumAdd(currency) - } - if (!add.definition.isUnnoted) { - add = Item(add.noteChange, add.amount) - } - if (container.getAmount(add.id) == -1 || container.add(add)) { - if (currency.amount > 0) { - player.debug("Adding coins to inventory") - player.inventory.add(currency) - } - val viewer = player.getExtension(ShopViewer::class.java) - tabIndex = if (container === containers[0]) 0 else 1 - sendStock(player, tabIndex) - if (viewer != null) { - viewer.tabIndex = tabIndex - } - update() - } - } - } - - /** - * Values an item. - * - * @param player the player. - * @param viewer the viewer. - * @param item the item. - * @param sell the sell. - */ - fun value(player: Player, viewer: ShopViewer, item: Item, sell: Boolean) { - if (sell) { - if (isPointShop || item.id == viewer.shop.currency || !item.definition.isTradeable || !viewer.shop.itemAllowed( - item.id - ) - ) { - player.packetDispatch.sendMessage("You can't sell this item.") - return - } - val value = viewer.shop.getSellingValue(Item(item.id, 1), player) - var currency = - if (isPointShop) pointsName else ItemDefinition.forId(viewer.shop.currency).name.toLowerCase() - if (value == 1 && currency[currency.length - 1] == 's') { - currency = currency.substring(0, currency.length - 1) - } - player.packetDispatch.sendMessage(item.name + ": shop will buy for " + value + " " + currency + ".") - } else { - val value = viewer.shop.getBuyPrice(item, player) - var name = - if (isPointShop) pointsName + "s" else ItemDefinition.forId(viewer.shop.currency).name.toLowerCase() - if (value == 1 && name[name.length - 1] == 's') { - name = name.substring(0, name.length - 1) - } - player.packetDispatch.sendMessage("" + item.name + ": currently costs " + value + " " + name + ".") - } - } - - /** - * Method used to send a stock - * - * @param player - * @param tabIndex - */ - fun sendStock(player: Player, tabIndex: Int) { - val main = tabIndex == 0 - player.packetDispatch.sendInterfaceConfig(620, 23, !main) - player.packetDispatch.sendInterfaceConfig(620, 24, main) - player.packetDispatch.sendInterfaceConfig(620, 29, !main) - player.packetDispatch.sendInterfaceConfig(620, 25, main) - player.packetDispatch.sendInterfaceConfig(620, 27, main) - player.packetDispatch.sendInterfaceConfig(620, 26, false) - player.packetDispatch.sendAccessMask(1278, if (main) 23 else 24, 620, 0, 40) - } - - /** - * Method used to update the viewers. - */ - fun update() { - for (viewer in viewers) { - viewer.update() - } - } - - /** - * Method used to restock the shop. - */ - fun restock() { - for (container in containers) { - for (i in container.toArray().indices) { - val main = container === containers[0] - val item = container.toArray()[i] ?: continue - var reduce = !main - if (main) { - if (item.amount < items[i].amount) { - item.amount = item.amount + 1 - } - reduce = item.amount > items[i].amount - } - if (reduce) { - val amount = item.amount - 1 - if (amount < 1 && !main) { - container.remove(item) - } else { - item.amount = amount - } - if (!main) { - container.shift() - } - } - } - } - update() - } - - /** - * Checks if the player can sell an item to the shop. - * - * @param player the player. - * @param item the item. - * @param def the def. - * @return `True` if so. - */ - open fun canSell(player: Player, item: Item, def: ItemDefinition): Boolean { - if (isPointShop || item.definition.hasDestroyAction() || !def.isTradeable || !itemAllowed(item.id)) { - player.packetDispatch.sendMessage("You can't sell this item to this shop.") - return false - } - if (item.id == currency) { - player.packetDispatch.sendMessage("You can't sell " + item.name.toLowerCase() + " to a shop.") - return false - } - return true - } - - /** - * Gets the amount to buy/sell. - * - * @param player the player. - * @param add the added item. - * @return the amount. - */ - fun getAmount(player: Player?, add: Item): Int { - return add.amount - } - - /** - * Checks if the player can buy the item. - * - * @param player the player. - * @param currency the currency. - * @return `True` if so. - */ - fun handleBuy(player: Player, currency: Item?): Boolean { - return isPointShop || player.inventory.remove(currency) - } - - /** - * Checks if the player can buy from the shop. - * - * @param player the player. - * @param item the item. - * @param price the price. - * @param currency the currency. - * @return `True` if they can buy. - */ - fun canBuy(player: Player, item: Item, price: Int, currency: Item): Boolean { - if (!isPointShop && !player.inventory.containsItem(currency)) { - player.packetDispatch.sendMessage( - "You don't have enough " + ItemDefinition.forId(currency.id).name.toLowerCase() + "." - ) - return false - } - if (isPointShop && getPoints(player) < price) { - player.sendMessage("You don't have enough " + pointsName + "s.") - return false - } - return true - } - - /** - * Gets the points. - * - * @param player the player. - * @return the points. - */ - fun getPoints(player: Player?): Int { - return 0 - } - - /** - * Decrements the points. - * - * @param player the player. - * @param decrement the decrementation. - */ - fun decrementPoints(player: Player?, decrement: Int) {} - - /** - * Gets the points name. - * - * @return the name. - */ - val pointsName: String - get() = "" - - /** - * Gets the value gained for selling this item to a certain shop. - * - * @param item The item to sell. - * @param player the player. - * @return The value. - */ - fun getSellingValue(item: Item, player: Player): Int { - var item = item - if (!item.definition.isUnnoted) { - player.setAttribute("shop:originalId", item.id) - item = Item(item.noteChange, item.amount) - } - var amount = getContainer(1).getAmount(item) - if (amount < 1) { - amount = getContainer(0).getAmount(item) - } - return getSellingValue(player, amount, item) - } - - /** - * Gets the selling value formula based. - * - * @param amount the amount. - * @param item the item. - * @return the selling value. - */ - private fun getSellingValue(player: Player, amount: Int, item: Item): Int { - val id = player.getAttribute("shop:originalId", item.id) - if (item.amount > amountInInventory(player, id)) { - item.amount = amountInInventory(player, id) - player.removeAttribute("shop:originalId") - } - val diff = if (item.definition.isStackable) 0.005 else 0.05 - var maxMod = 1.0 - amount * diff - if (maxMod < 0.25) { - maxMod = 0.25 - } - var minMod = maxMod - (item.amount - 1) * diff - if (minMod < 0.25) { - minMod = 0.25 - } - val mod = (maxMod + minMod) / 2 - logInfo("" + item.definition.getAlchemyValue(isHighAlch) + " " + mod + " " + item.amount) - val baseValue = item.definition.getAlchemyValue(isHighAlch) - var value = (baseValue * mod * item.amount).toInt() - if (currency == TOKKUL_6529 && item.id == CHAOS_RUNE_562) value = 13 * item.amount - if (currency == TOKKUL_6529 && item.id == DEATH_RUNE_560) value = 27 * item.amount - if (item.id == 12183) { - value = 25 * item.amount - } - return value - } - - /** - * Gets the buying price. - * - * @param item the item. - * @return the price. - */ - open fun getBuyPrice(item: Item, player: Player): Int { - var item = item - item = Item(item.id, 1) - var price = item.definition.value - val sellVal = getSellingValue(item, player) - if (price < sellVal) { - price = getSellingValue(player, 0, item) + sellVal - (sellVal - item.definition.maxValue) - } - if (price < 0) { - price = 1 - } - if (currency == TOKKUL) { - val tokkul = item.definition.getConfiguration("tokkul_price", -1) - if (tokkul > 0) { - price = tokkul - } - if (player.achievementDiaryManager.karamjaGlove != -1) { - price = kotlin.math.floor(price * 0.87).toInt() - } - } - if (currency == ARCHERY_TICKET) { - val tickets = item.definition.getConfiguration(ItemConfigParser.ARCHERY_TICKET_PRICE, -1) - if (tickets > 0) { - price = tickets - } - } - return if (sellAllFor > 0) sellAllFor else price - } - - /** - * Checks if the item is allowed to be sold to the shop. - * - * @param itemId the item id. - * @return `True` if so. - */ - fun itemAllowed(itemId: Int): Boolean { - if (isGeneral) { - return true - } - var noteId = ItemDefinition.forId(itemId).noteId - if (!ItemDefinition.forId(itemId).isUnnoted) { - noteId = ItemDefinition.forId(noteId).noteId - } - for (id in items) { - if (itemId == id.id || noteId > -1 && noteId == ItemDefinition.forId(id.id).noteId) { - return true - } - } - return false - } - - /** - * Gets the container the item should go to. - * - * @param item the item. - * @return the container. - */ - fun getContainer(item: Item): Container { - val itemId = item.id - var noteId = ItemDefinition.forId(itemId).noteId - if (!ItemDefinition.forId(itemId).isUnnoted) { - noteId = ItemDefinition.forId(noteId).noteId - } - for (i in items) { - if (i.id == item.id || noteId > -1 && noteId == ItemDefinition.forId(i.id).noteId) { - return getContainer(0) - } - } - return getContainer(1) - } - - /** - * Creates a copy of this shop. - * - * @return the shop. - */ - fun copy(): Shop { - return Shop(title, items, isGeneral, currency, isHighAlch) - } - - /** - * Gets the container on the slot. - * - * @param tabIndex the tab index. - * @return the container. - */ - fun getContainer(tabIndex: Int): Container { - if (tabIndex > containers.size) { - throw IndexOutOfBoundsException("Error! Shop tab index out of bounds.") - } - return containers[tabIndex] - } - - override fun toString(): String { - return "Shop [containers=" + Arrays.toString(containers) + ", viewers=" + viewers + ", title=" + title + ", items=" + Arrays.toString( - items - ) + ", general=" + isGeneral + ", currency=" + currency + ", highAlch=" + isHighAlch + "]" - } - - fun setItems(items: ArrayList) { - this.items = items.toTypedArray() - } - - companion object { - /** - * Represents the general store items. - */ - val GENERAL_STORE_ITEMS = arrayOf( - Item(EMPTY_POT_1931, 5), - Item(JUG_1935, 5), - Item(SHEARS_1735, 2), - Item(BUCKET_1925, 3), - Item(BOWL_1923, 2), - Item(CAKE_TIN_1887, 2), - Item(TINDERBOX_590, 2), - Item(CHISEL_1755, 2), - Item(HAMMER_2347, 5), - Item(NEWCOMER_MAP_550, 5), - Item(SECURITY_BOOK_9003, 5) - ) - - /** - * Represents the coins item. - */ - private const val COINS = 995 - - /** - * Represents the tokkul item id. - */ - private const val TOKKUL = 6529 - - /** - * Represents the archery ticket item id - */ - private const val ARCHERY_TICKET = 1464 - } - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param items the items. - * @param isGeneral the general. - * @param currency the currency. - * @param isHighAlch if high alch. - */ - /** - * Constructs a new `Shop` `Object`. - * - * @param title the title. - * @param items the items. - * @param general the general. - */ - init { - this.getContainer(0).add(*items) - isRestock = true - lastRestock = ticks + 100 - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/shop/ShopViewer.java b/Server/src/main/java/core/game/content/global/shop/ShopViewer.java deleted file mode 100644 index c910aa992..000000000 --- a/Server/src/main/java/core/game/content/global/shop/ShopViewer.java +++ /dev/null @@ -1,154 +0,0 @@ -package core.game.content.global.shop; - -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.game.node.entity.player.Player; -import core.net.packet.PacketRepository; -import core.net.packet.context.ContainerContext; -import core.net.packet.out.ContainerPacket; - -/** - * Represents the viewing of a shop. - * @author 'Vexia - */ -public final class ShopViewer { - - /** - * Represents the main component. - */ - private static final Component COMPONENT = new Component(620).setCloseEvent(new ShopCloseEvent()); - - /** - * Represents the single tab component. - */ - private static final Component SINGLE_TAB = new Component(621); - - /** - * Represents the player. - */ - private final Player player; - - /** - * Represents the shop being viewed. - */ - private Shop shop; - - /** - * Represents the tab index. - */ - private int tabIndex; - - /** - * Constructs a new {@code ShopViewer} {@code Object}. - * @param player the player. - * @param shop the shop. - */ - public ShopViewer(Player player, Shop shop) { - this.player = player; - this.shop = shop; - } - - /** - * Extends a shop a shop viewer to the player instance. - * @param player the player. - * @param shop the shop. - * @return the viewer. - */ - public static ShopViewer extend(final Player player, Shop shop) { - ShopViewer viewer = player.getExtension(ShopViewer.class); - if (viewer != null) { - return viewer; - } - player.addExtension(ShopViewer.class, (viewer = new ShopViewer(player, shop))); - return viewer; - } - - /** - * Method used to open the shop visual. - */ - public void open() { - update(); - shop.getViewers().add(this); - player.getInterfaceManager().open(COMPONENT); - player.getInterfaceManager().openSingleTab(SINGLE_TAB); - shop.sendStock(player, tabIndex); - player.getPacketDispatch().sendRunScript(25, "vg", 868, 92); - player.getPacketDispatch().sendAccessMask(1278, 0, 621, 0, 28); - player.getPacketDispatch().sendString(shop.getTitle(), 620, 22); - player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Buy X", "Buy 10", "Buy 5", "Buy 1", "Value", -1, 0, 4, 10, 92, 620 << 16 | 23); - player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Buy X", "Buy 10", "Buy 5", "Buy 1", "Value", -1, 0, 4, 10, 31, (620 << 16) | 24); - player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Sell X", "Sell 10", "Sell 5", "Sell 1", "Value", -1, 0, 7, 4, 93, 621 << 16); - } - - /** - * Method used to update the contents on the component. - */ - public void update() { - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 93, player.getInventory().toArray(), 28, false)); - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, 92, shop.getContainer(0).toArray(), shop.getContainer(0).itemCount(), false)); - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, 64271, 31, shop.getContainer(1).toArray(), shop.getContainer(1).itemCount(), false)); - } - - /** - * Method used to show a stock. - */ - public void showStock(int tabIndex) { - this.tabIndex = tabIndex; - shop.sendStock(player, tabIndex); - update(); - } - - /** - * Sets the tabIndex. - * @param tabIndex The tabIndex to set. - */ - public void setTabIndex(int tabIndex) { - this.tabIndex = tabIndex; - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the shop. - * @return The shop. - */ - public Shop getShop() { - return shop; - } - - /** - * Gets the tabIndex. - * @return The tabIndex. - */ - public int getTabIndex() { - return tabIndex; - } - - /** - * Represents the close event when a shop is closed. - * @author 'Vexia - */ - public static final class ShopCloseEvent implements CloseEvent { - - @Override - public boolean close(Player player, Component c) { - final ShopViewer viewer = player.getExtension(ShopViewer.class); - if (viewer == null) { - return true; - } - player.removeExtension(ShopViewer.class); - viewer.getShop().getViewers().remove(viewer); - player.getInterfaceManager().closeSingleTab(); - player.removeAttribute("spawning_items"); - return true; - } - - } - -} diff --git a/Server/src/main/java/core/game/content/global/travel/MinigameGroupFinder.java b/Server/src/main/java/core/game/content/global/travel/MinigameGroupFinder.java deleted file mode 100644 index 75d1a3bae..000000000 --- a/Server/src/main/java/core/game/content/global/travel/MinigameGroupFinder.java +++ /dev/null @@ -1,159 +0,0 @@ -package core.game.content.global.travel; - -import core.game.component.Component; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.TeleportManager.TeleportType; -import core.game.world.map.Location; - -/** - * Handles the Minigame Group Finder teleport interface. - * - * @author Torchic - */ -public class MinigameGroupFinder { - - /** - * Opens the minigame finder for the player. - * @param player The player. - */ - public static void openFinder(Player player) { - player.setAttribute("minigameFinder:open", true); - player.getInterfaceManager().openTab(2, new Component(259)); - player.getPacketDispatch().sendString("Minigame Group Finder", 259, 9); - player.getPacketDispatch().sendString("Destinations", 259, 1); - player.getPacketDispatch().sendString(" Shilo Village", 259, 2); - player.getPacketDispatch().sendString("Warrior's Guild", 259, 3); - player.getPacketDispatch().sendString("Dagganoth Kings", 259, 4); - player.getPacketDispatch().sendString("God Wars Dungeon", 259, 5); - player.getPacketDispatch().sendString("Minigames", 259, 6); - player.getPacketDispatch().sendString(" Barbarian Assault", 259, 23); - player.getPacketDispatch().sendString("Blast Furnace", 259, 24); - player.getPacketDispatch().sendString("Burthorpe Games Room", 259, 25); - player.getPacketDispatch().sendString("Castle Wars", 259, 26); - player.getPacketDispatch().sendString(" Clan Wars", 259, 19); - player.getPacketDispatch().sendString("Duel Arena", 259, 20); - player.getPacketDispatch().sendString("Fishing Trawler", 259, 21); - player.getPacketDispatch().sendString("Fist of Guthix", 259, 22); - player.getPacketDispatch().sendString("Nightmare Zone", 259, 12); - player.getPacketDispatch().sendString(" Pest Control", 259, 11); - player.getPacketDispatch().sendString("Pyramid Plunder", 259, 13); - player.getPacketDispatch().sendString("Shades of Mort'ton", 259, 14); - player.getPacketDispatch().sendString("Stealing Creation", 259, 28); - player.getPacketDispatch().sendString(" Trouble Brewing", 259, 27); - player.getPacketDispatch().sendString("TzHaar Fight Pit", 259, 29); - player.getPacketDispatch().sendString("", 259, 30); - player.getPacketDispatch().sendString("", 259, 16); - player.getPacketDispatch().sendString("", 259, 15); - player.getPacketDispatch().sendString("", 259, 17); - player.getPacketDispatch().sendString("", 259, 18); - for (int i = 15; i < 19; i ++) { - player.getPacketDispatch().sendString("", 259, i); - } - } - - /** - * Holds all the optiosn for the minigame group finder. - * @author Torchic - * - */ - public enum MinigameOptions { - - /** - * Destinations - */ - SHILO_VILLAGE(2, Location.create(2876, 2954, 0)), - WARRIORS_GUILD(3, Location.create(2878, 3546, 0)), - DAGGANOTH_KINGS(4, Location.create(2524, 3739, 0)), - GOD_WARS_DUNGEON(5, Location.create(2898, 3712, 0)), - - /** - * Minigames - */ - BARBARIAN_ASSAULT(23, Location.create(2520, 3570, 0)), - BLAST_FURNACE(24, Location.create(1950, 4968, 0)), - BURTHORPE_GAMES_ROOM(25, Location.create(2899, 3563, 0)), - CASTLE_WARS(26, Location.create(2442, 3089, 0)), - CLAN_WARS(19, Location.create(3273, 3686, 0)), - DUEL_ARENA(20, Location.create(3365, 3265, 0)), - FISHING_TRAWLER(21, Location.create(2658, 3158, 0)), - FIST_OF_GUTHIX(22, Location.create(1675, 5599, 0)), - NIGHTMARE_ZONE(12, Location.create(3184, 3945, 0)), - PEST_CONTROL(11, Location.create(2659, 2676, 0)), - PYRAMID_PLUNDER(13, Location.create(3289, 2787, 0)), - SHADES_OF_MORTTON(14, Location.create(3504, 3284, 0)), - TROUBLE_BREWING(27, Location.create(3222, 3222, 0)), - STEALING_CREATION(28, Location.create(3135, 3627, 0)), - FIGHT_PITS(29, Location.create(2400, 5178, 0)); - - /** - * The option (button) id of the teleport. - */ - private int optionId; - - /** - * The location to send the player. - */ - private Location tile; - - /** - * Constructs a new {@Code MinigameOptions} {@Code Object} - * @param optionId The option id. - * @param tile The location tile. - */ - MinigameOptions(int optionId, Location tile) { - this.optionId = optionId; - this.tile = tile; - } - - /** - * Gets the location. - * @return - */ - public Location getLocation() { - return tile; - - } - - /** - * Gets the telport option id. - * @return - */ - public int getTeleportId() { - return optionId; - } - - /** - * Iterates through the options to find the correct teleport based on option id. - * @param optionId The option id. - * @return - */ - public static MinigameOptions getTeleportOptions(int optionId) { - for (MinigameOptions tele : MinigameOptions.values()) { - if (tele.getTeleportId() == optionId) { - return tele; - } - } - return null; - } - } - - /** - * Teleports the player to the location based on the componentId - * - * @param player - * @param buttonId - * @return true if teleport exists - */ - public static boolean teleport(final Player player, int buttonId) { - MinigameOptions option = MinigameOptions.getTeleportOptions(buttonId); - if (option == null || option.getLocation() == new Location(3222, 3222, 0)) { - player.sendMessage("Location has not been added!"); - return false; - } - player.getTeleporter().send(option.getLocation(), TeleportType.NORMAL, 1); - player.removeAttribute("minigameFinder:open"); - player.getInterfaceManager().openTab(2, new Component(274)); - player.getPacketDispatch().sendString("Minigame Group Finder", 274, 5); - return true; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/global/travel/canoe/Canoe.java b/Server/src/main/java/core/game/content/global/travel/canoe/Canoe.java deleted file mode 100644 index b77baf349..000000000 --- a/Server/src/main/java/core/game/content/global/travel/canoe/Canoe.java +++ /dev/null @@ -1,86 +0,0 @@ -package core.game.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/java/core/game/content/global/travel/ship/ShipCharter.java b/Server/src/main/java/core/game/content/global/travel/ship/ShipCharter.java deleted file mode 100644 index 26f26c878..000000000 --- a/Server/src/main/java/core/game/content/global/travel/ship/ShipCharter.java +++ /dev/null @@ -1,338 +0,0 @@ -package core.game.content.global.travel.ship; - -import core.game.component.Component; -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 rs09.game.world.GameWorld; -import core.game.world.map.Location; -import rs09.game.world.repository.Repository; -import core.net.packet.PacketRepository; -import core.net.packet.context.MinimapStateContext; -import core.net.packet.out.MinimapState; -import core.tools.StringUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents a class used to charter ships. - * @author 'Vexia - */ -public final class ShipCharter { - - /** - * Represents the component of charting a ship. - */ - private final static Component COMPONENT = new Component(95); - - /** - * Represents the ring of charos item. - */ - private final static Item RING_OF_CHAROS = new Item(6465); - - /** - * Constructs a new {@code ShipCharter} {@code Object}. - */ - public ShipCharter() { - /** - * empty. - */ - } - - /** - * Method used to open the ship chartering interface. - * @param player the player. - */ - public static void open(final Player player) { - final Destination current = Destination.getFromBase(player.getLocation()); - if (current != null) { - int[] hiddenComponents = getHiddenComponents(player, current); - for (int component : hiddenComponents) { - player.getPacketDispatch().sendInterfaceConfig(95, component, true); - } - player.getInterfaceManager().open(COMPONENT); - } - } - - /** - * Method used to handle a ship charter. - * @param player the player. - * @param button the button. - */ - public static void handle(final Player player, final int button) { - final Destination destination = Destination.forButton(button); - if (destination == null) { - return; - } - final int cost = getCost(player, destination); - player.getInterfaceManager().close(); - player.getDialogueInterpreter().open(4651, Repository.findNPC(4651), destination, cost); - } - - /** - * Method used to get the cost of the charter. - * @param player the player. - * @param destination the destination. - * @return the cost. - */ - 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 - cost -= Math.round((cost / 2.)); - } - return cost; - } - - /** - * Method used to get the hidden childs on the screen. - * @param player the player. - * @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 }; - List childs = new ArrayList<>(20); - for (Destination destination : restrictions) { - childs.add(destination.getXChild()); - childs.add(destination.getNameChild()); - } - childs.add(base.getXChild()); - childs.add(base.getNameChild()); - if (base == Destination.KARAMJA) { - childs.add(Destination.PORT_SARIM.getXChild()); - childs.add(Destination.PORT_SARIM.getNameChild()); - } - if (base == Destination.PORT_SARIM) { - childs.add(Destination.KARAMJA.getXChild()); - childs.add(Destination.KARAMJA.getNameChild()); - } - int[] arrayChilds = new int[childs.size()]; - for (int i = 0; i < arrayChilds.length; i++) { - arrayChilds[i] = childs.get(i); - } - return arrayChilds; - } - - /** - * Method used to get the component of ship chartering. - * @return the component.. - */ - public static Component getComponent() { - return COMPONENT; - } - - /** - * Represents the destination to travel to. - * @author 'Vexia - */ - public enum Destination { - CATHERBY(Location.create(2792, 3417, 1), 25, new int[] { 480, -1, 480, 625, 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, -1, -1, -1, 2050, 1850, 3200, 1100 }, Location.create(3702, 3502, 0), 2, 13), - CRANDOR(new Location(2792, 3417, 1), 32, new int[] { -1, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, null, 10, 21), - BRIMHAVEN(Location.create(2763, 3238, 1), 28, new int[] { -1, 480, 480, 925, 400, 3650, 1600, 400, 3200, 3800 }, Location.create(2760, 3238, 0), 6, 17), - PORT_SARIM(Location.create(3038, 3189, 1), 30, new int[] { 1600, 1000, -1, 325, 1280, 650, 1280, 400, 3200, 1400 }, Location.create(3039, 3193, 0), 8, 19), - PORT_TYRAS(Location.create(2142, 3122, 0), 23, new int[] { 3200, 3200, 3200, 1600, 3200, 3200, 3200, 3200, -1, 3200 }, Location.create(2143, 3122, 0), 1, 12), - KARAMJA(Location.create(2957, 3158, 1), 27, new int[] { 200, 480, -1, 225, 400, 1850, -1, 200, 3200, 2000 }, Location.create(2954, 3156, 0), 5, 16), - PORT_KHAZARD(Location.create(2674, 3141, 1), 29, new int[] { 1600, 1000, -1, 325, 180, 650, 1280, 400, 3200, 1400 }, Location.create(2674, 3144, 0), 7, 18), - SHIPYARD(Location.create(3001, 3032, 0), 26, new int[] { 400, 1600, 200, 225, 720, 1850, 400, -1, 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, -1 }, Location.create(2622, 2857, 0), 11, 22), - MOS_LE_HARMLESS(Location.create(3671, 2931, 0), 31, new int[] { 725, 625, 1025, -1, 1025, -1, 325, 275, 1600, 500 }, Location.create(3671, 2933, 0), 9, 20); - - /** - * Constructs a new {@code ShipCharter} {@code Object}. - * @param location the location. - * @param button the button. - * @param costs the money. - * @param base the base. - * @param components the children. - */ - Destination(Location location, int button, int[] costs, final Location base, int... components) { - this.location = location; - this.button = button; - this.costs = costs; - this.base = base; - this.childs = components; - } - - /** - * Represents the location of the destination. - */ - private final Location location; - - /** - * Represents the button of the destination. - */ - private final int button; - - /** - * Represents the costs from destination to destination. - */ - private final int[] costs; - - /** - * Represents the base location(how we find where we're at) - */ - private final Location base; - - /** - * Represents the childs on the screen. - */ - private final int[] childs; - - /** - * Gets the location. - * @return The location. - */ - public Location getLocation() { - return location; - } - - /** - * Gets the button. - * @return The button. - */ - public int getButton() { - return button; - } - - /** - * Gets the moneys. - * @return The moneys. - */ - public int[] getCosts() { - return costs; - } - - /** - * Gets the base. - * @return The base. - */ - public Location getBase() { - return base; - } - - /** - * Gets the childs. - * @return The childs. - */ - public int[] getComponents() { - return childs; - } - - /** - * Gets the x component. - * @return the component. - */ - public int getXChild() { - return childs[0]; - } - - /** - * Gets the name component. - * @return the component. - */ - public int getNameChild() { - return childs[1]; - } - - /** - * Gets the cost of chartering a ship. - * @param player the player. - * @return the cost. - */ - public int getCost(final Player player, final Destination destination) { - final Destination current = Destination.getFromBase(player.getLocation()); - if (current == null) { - return 0; - } - final Destination[] costTable = new Destination[] { BRIMHAVEN, CATHERBY, KARAMJA, MOS_LE_HARMLESS, PORT_KHAZARD, PORT_PHASMATYS, PORT_SARIM, SHIPYARD, PORT_TYRAS, OO_GLOG }; - int index = 0; - for (int i = 0; i < costTable.length; i++) { - if (costTable[i] == destination) { - index = i; - break; - } - } - return current.getCosts()[index]; - } - - /** - * Method used to get the destination from the base. - * @param location the location. - * @return the destination. - */ - public static Destination getFromBase(Location location) { - for (Destination destination : Destination.values()) { - if (destination.getBase() == null) { - continue; - } - if (destination.getBase().getDistance(location) < 30) { - return destination; - } - } - return null; - } - - /** - * Method used to get the destination for the button id. - * @param button the button. - * @return the destination. - */ - public static Destination forButton(final int button) { - for (Destination destination : values()) { - if (destination.getButton() == button) { - return destination; - } - } - return null; - } - - /** - * Method used to sail the ship. - * @param player the player. - */ - public void sail(final Player player) { - player.lock(7); - Location start = player.getLocation(); - GameWorld.getPulser().submit(new Pulse(1) { - int count = 0; - - @Override - public boolean pulse() { - switch (count++) { - case 0: - player.getInterfaceManager().openOverlay(new Component(115)); - break; - case 2: - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); - break; - case 3: - player.getProperties().setTeleportLocation(getLocation()); - break; - case 5: - player.unlock(); - player.getInterfaceManager().close(); - player.getInterfaceManager().closeOverlay(); - player.getInterfaceManager().restoreTabs(); - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); - player.getPacketDispatch().sendMessage("You pay the fare and sail to " + StringUtils.formatDisplayName(name()) + "."); - // Charter a ship from the shipyard in the far east of Karamja - if (start.withinDistance(Location.create(3001,3032,0))) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 17); - } - return true; - } - return false; - } - - }); - } - } - -} diff --git a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardEntry.java b/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardEntry.java deleted file mode 100644 index 0996dd66a..000000000 --- a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardEntry.java +++ /dev/null @@ -1,10 +0,0 @@ -package core.game.content.global.worldevents.shootingstar; - -public class ScoreboardEntry{ - public String playerName; - public int time; - public ScoreboardEntry(String player, int time){ - this.playerName = player; - this.time = time; - } -} diff --git a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardHandler.java b/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardHandler.java deleted file mode 100644 index d2c011b97..000000000 --- a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -package core.game.content.global.worldevents.shootingstar; - -import core.cache.def.impl.SceneryDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; -import core.plugin.Plugin; - -public class ScoreboardHandler extends OptionHandler { - int index = 0; - int ifaceid = ShootingStarScoreboard.iface.getId(); - @Override - public boolean handle(Player player, Node node, String option) { - ScoreboardManager.getEntries().forEach(e -> { - player.getPacketDispatch().sendString("" + Math.floor(((GameWorld.getTicks() - e.time) / 0.6) / 60) + " minutes ago",ifaceid,index + 6); - player.getPacketDispatch().sendString(e.playerName,ifaceid,index + 11); - index++; - }); - index = 0; - player.getInterfaceManager().open(ShootingStarScoreboard.iface); - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(38669).getHandlers().put("option:read",this); - - return this; - } -} diff --git a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardManager.java b/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardManager.java deleted file mode 100644 index 86d8f7099..000000000 --- a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ScoreboardManager.java +++ /dev/null @@ -1,23 +0,0 @@ -package core.game.content.global.worldevents.shootingstar; - -import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; - -import java.util.ArrayList; -import java.util.List; - -public class ScoreboardManager { - public static List entries = new ArrayList<>(20); - - public static void submit(Player player){ - if(entries.size() == 5){ - entries.remove(0); - } - entries.add(new ScoreboardEntry(player.getUsername(), GameWorld.getTicks())); - } - - public static List getEntries(){ - return entries; - } -} diff --git a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ShootingStarScoreboard.java b/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ShootingStarScoreboard.java deleted file mode 100644 index 654151ffb..000000000 --- a/Server/src/main/java/core/game/content/global/worldevents/shootingstar/ShootingStarScoreboard.java +++ /dev/null @@ -1,22 +0,0 @@ -package core.game.content.global.worldevents.shootingstar; - -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.Plugin; - -public class ShootingStarScoreboard extends ComponentPlugin { - public static Component iface = new Component(787); - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(iface.getId(),this); - return null; - } -} diff --git a/Server/src/main/java/core/game/content/holiday/HolidayEvent.java b/Server/src/main/java/core/game/content/holiday/HolidayEvent.java deleted file mode 100644 index b04f53ee2..000000000 --- a/Server/src/main/java/core/game/content/holiday/HolidayEvent.java +++ /dev/null @@ -1,281 +0,0 @@ -package core.game.content.holiday; - -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; -import core.game.world.map.zone.MapZone; -import core.game.world.map.zone.ZoneBuilder; -import core.game.world.map.zone.ZoneRestriction; -import core.game.world.map.zone.ZoneType; -import core.plugin.Plugin; - -import java.util.ArrayList; -import java.util.List; - -/** - * Handles a holiday event. - * @author Vexia - */ -public abstract class HolidayEvent extends MapZone implements Plugin { - - /** - * The list of holiday events. - */ - private static final List EVENTS = new ArrayList<>(5); - - /** - * The current holiday event. - */ - private static HolidayEvent current; - - /** - * The holiday type. - */ - private final HolidayType type; - - /** - * The config id used to handle the stages. - */ - private final int configId; - - /** - * The hash to use for this event. - */ - private final int hash; - - /** - * The max stage. - */ - private final int maxStage; - - /** - * Constructs a new {@code HolidayEvent} {@code Object} - * @param type the holiday type. - * @param name the name of the event. - * @param configId the config id. - * @param hash the hash. - * @param maxStage the max stage. - */ - public HolidayEvent(String name, HolidayType type, int configId, int hash, int maxStage) { - super(name, true, ZoneRestriction.CANNON, ZoneRestriction.FIRES, ZoneRestriction.FOLLOWERS, ZoneRestriction.RANDOM_EVENTS); - this.type = type; - this.hash = hash; - this.configId = configId; - this.maxStage = maxStage; - this.setZoneType(ZoneType.SAFE.getId()); - } - - /** - * Used to load all the necessary nodes for this event. - */ - public abstract void load(); - - /** - * Indicates whether or not this holiday event is active. - * @return {@code True} if so. - */ - public abstract boolean isActive(); - - /** - * Initializes the holiday events. - */ - public static void init() { - new ItemLimitation().parse(); - for (HolidayEvent event : EVENTS) { - if (event.isActive()) { - event.load(); - } - } - } - - @Override - public boolean enter(Entity entity) { - if (entity instanceof Player && isComplete(entity.asPlayer())) { - return false; - } - return super.enter(entity); - } - - @Override - public boolean leave(Entity e, boolean logout) { - return super.leave(e, logout); - } - - @Override - public boolean parseCommand(Player player, String name, String[] arguments) { - if (!player.isAdmin()) { - return false; - } - switch (name) { - case "setstage": - setStage(player, Integer.parseInt(arguments[1])); - return true; - } - return false; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - if (isActive()) { - setCurrent(this); - ZoneBuilder.configure(this); - SystemLogger.logInfo("Holiday event active [type=" + type + "]!"); - } - EVENTS.add(this); - return this; - } - - /** - * Adds experience. - * @param player The player. - * @param skill The skill. - * @param experience The experience. - */ - public void addExperience(Player player, int skill, double experience) { - - } - - /** - * Commences the death event. - * @param killer The killing entity. - * @param victim The entity getting killed. - */ - public void commenceDeath(Entity killer, Entity victim) { - - } - - /** - * Finalizes the death event. - * @param killer The killing entity. - * @param victim The entity getting killed. - */ - public void finalizeDeath(Entity killer, Entity victim) { - - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - /** - * Checks if it is a certain holiday. - * @param type the type. - * @return {@code True} if its a holiday. - */ - public static boolean isHoliday(HolidayType type) { - return current != null && current.getType() == type; - } - - /** - * Checks if the event is complete. - * @return {@code True} if so. - */ - public boolean isComplete(Player player) { - return getStage(player) >= maxStage; - } - - /** - * Sets the stage of the event. - * @param player the player. - * @param stage the stage. - */ - public void setStage(Player player, int stage) { - setStage(player, stage, hash); - } - - /** - * Sets the stage of the event. - * @param player the player. - * @param stage the stage. - * @param hash the hash. - */ - public void setStage(Player player, int stage, int hash) { - setStageValue(player, getConfig(player) + stage << hash); - } - - /** - * Sets the stage value of the event. - * @param player the player. - * @param value the value. - */ - public void setStageValue(Player player, int value) { - player.getConfigManager().set(configId, value, true); - } - - /** - * Gets the stage of the event. - * @param player the player. - * @return the stage. - */ - public int getStage(Player player) { - return getStage(player, hash); - } - - /** - * Gets the stage of the event. - * @param player the player. - * @return the stage. - */ - public int getStage(Player player, int hash) { - return getConfig(player) >> hash; - } - - /** - * Gets the config value. - * @param player the player. - * @return the value. - */ - public int getConfig(Player player) { - return player.getConfigManager().get(configId); - } - - /** - * Gets the max stage. - * @return the stage. - */ - public int getMaxStage() { - return maxStage; - } - - /** - * Gets the configId. - * @return the configId - */ - public int getConfigId() { - return configId; - } - - /** - * Gets the hash. - * @return the hash - */ - public int getHash() { - return hash; - } - - /** - * Gets the type. - * @return the type - */ - public HolidayType getType() { - return type; - } - - /** - * Gets the current. - * @return the current - */ - public static HolidayEvent getCurrent() { - return current; - } - - /** - * Sets the current. - * @param current the current to set. - */ - public static void setCurrent(HolidayEvent current) { - HolidayEvent.current = current; - } - -} diff --git a/Server/src/main/java/core/game/content/holiday/HolidayItem.java b/Server/src/main/java/core/game/content/holiday/HolidayItem.java deleted file mode 100644 index b39a1d28b..000000000 --- a/Server/src/main/java/core/game/content/holiday/HolidayItem.java +++ /dev/null @@ -1,108 +0,0 @@ -package core.game.content.holiday; - -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 rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Graphics; -import core.tools.RandomFunction; - -import java.util.HashMap; -import java.util.Map; - -/** - * Used for releasing trade-able holiday items. - * @author Emperor - */ -public final class HolidayItem extends GroundItem { - - /** - * The location of a holiday item spawn. - */ - private static Map itemLoc = new HashMap<>(); - - /** - * Constructs a new {@code HolidayItem} {@code Object}. - * @param item the item. - * @param location the location. - */ - public HolidayItem(Item item, Location location) { - super(item, location); - } - - /** - * Starts random holiday item spawning. - * @param spawn The item. - * @param interval The interval between moving the location. - * @param spawnLocations The spawn locations. - */ - public static void startRandomSpawn(final Item spawn, int interval, final Location... spawnLocations) { - GameWorld.getPulser().submit(new Pulse(interval) { - GroundItem item; - int lastIndex = -1; - - @Override - public boolean pulse() { - if (item != null) { - if (!item.isRemoved()) { - Graphics.send(Graphics.create(85), item.getLocation()); - } else if (ItemLimitation.decreaseAmount(spawn.getId())) { - System.out.println("Released all " + spawn.getName() + "s!"); - return true; - } - GroundItemManager.destroy(item); - } - int index = RandomFunction.random(spawnLocations.length); - if (index == lastIndex) { - index = (index + 1) % spawnLocations.length; - } - spawn.setLocation(spawnLocations[index]); - item = GroundItemManager.create(new HolidayItem(spawn, spawnLocations[index])); - itemLoc.put(item.getId(), item.getLocation()); - lastIndex = index; - return false; - } - }); - } - - @Override - public String toString() { - return "HolidaySpawn [name=" + getName() + ", loc=" + getLocation() + "]"; - } - - @Override - public boolean isActive() { - return true; - } - - @Override - public boolean isPrivate() { - return false; - } - - @Override - public boolean isAutoSpawn() { - return false; - } - - @Override - public void respawn() { - /* - * empty. - */ - } - - /** - * Returns the location of a holiday item. - * @param itemId the itemId to check - * @return location of the item - */ - public static Location getHolidayItemLocation(int itemId) { - if (itemLoc.get(itemId) != null) { - return itemLoc.get(itemId); - } - return null; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/holiday/HolidayType.java b/Server/src/main/java/core/game/content/holiday/HolidayType.java deleted file mode 100644 index 0a5daeb72..000000000 --- a/Server/src/main/java/core/game/content/holiday/HolidayType.java +++ /dev/null @@ -1,9 +0,0 @@ -package core.game.content.holiday; - -/** - * The holiday type. - * @author Vexia - */ -public enum HolidayType { - HALLOWEEN(), CHRISTMAS(), EASTER(), THANKSGIVING; -} diff --git a/Server/src/main/java/core/game/content/holiday/ItemLimitation.java b/Server/src/main/java/core/game/content/holiday/ItemLimitation.java deleted file mode 100644 index 187ea676c..000000000 --- a/Server/src/main/java/core/game/content/holiday/ItemLimitation.java +++ /dev/null @@ -1,119 +0,0 @@ -package core.game.content.holiday; - -import core.cache.AriosStore; -import core.cache.def.impl.ItemDefinition; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * Handles limitation of holiday item releases. - * @author Emperor - */ -public final class ItemLimitation { - - /** - * The items. - */ - private static final Map ITEMS = new HashMap<>(); - - /** - * Constructs a new {@code ItemLimitation} {@code Object} - */ - public ItemLimitation() { - /* - * empty. - */ - } - - /** - * Parses the item limitation. - * @return {@code True} if parsed. - */ - public boolean parse() { - if (AriosStore.get("hir_limits") == null) { - return true; - } - ByteBuffer buffer = AriosStore.getArchive("hir_limits"); - int length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - int itemId = buffer.getShort() & 0xFFFF; - int amount = buffer.getShort() & 0xFFFF; - ITEMS.put(itemId, amount); - } - return true; - } - - /** - * Dumps the item limitation data. - */ - public static void dump() { - ByteBuffer buffer = ByteBuffer.allocate(ITEMS.size() * 4 + 1); - buffer.put((byte) ITEMS.size()); - for (int itemId : ITEMS.keySet()) { - buffer.putShort((short) itemId); - buffer.putShort((short) (int) ITEMS.get(itemId)); - } - buffer.flip(); - AriosStore.setArchive("hir_limits", buffer); - } - - /** - * Registers a new item release. - * @param itemId The item id. - * @param amount The amount. - */ - public static void register(int itemId, int amount) { - ITEMS.put(itemId, amount); - System.out.println("Registered item release [name=" + ItemDefinition.forId(itemId).getName() + ", id=" + itemId + ", limit=" + amount + "]."); - dump(); - } - - /** - * Checks if the item has been registered. - * @param itemId The item id. - * @return {@code True} if so. - */ - public static boolean isRegistered(int itemId) { - return ITEMS.containsKey(itemId); - } - - /** - * Gets the amount left to release. - * @param itemId The item id. - * @return The amount left. - */ - public static int getAmountLeft(int itemId) { - Integer amount = ITEMS.get(itemId); - if (amount == null) { - return -1; - } - return amount; - } - - /** - * Decreases the amount left. - * @param itemId The item id. - * @return {@code True} if the full amount has been fully released. - */ - public static boolean decreaseAmount(int itemId) { - if (!ITEMS.containsKey(itemId)) { - return false; - } - int amount = ITEMS.get(itemId); - if (amount > 0) { - ITEMS.put(itemId, amount - 1); - dump(); - } - return amount < 1; - } - - /** - * Gets the item limits. - * @return The item limits. - */ - public static Map getItems() { - return ITEMS; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/holiday/christmas/ChristmasEvent.java b/Server/src/main/java/core/game/content/holiday/christmas/ChristmasEvent.java deleted file mode 100644 index 8415797b4..000000000 --- a/Server/src/main/java/core/game/content/holiday/christmas/ChristmasEvent.java +++ /dev/null @@ -1,1114 +0,0 @@ -package core.game.content.holiday.christmas; -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.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.holiday.HolidayEvent; -import core.game.content.holiday.HolidayItem; -import core.game.content.holiday.HolidayType; -import core.game.content.holiday.ItemLimitation; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.Option; -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.BattleState; -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.TeleportManager.TeleportType; -import core.game.node.entity.player.link.emote.Emotes; -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 rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import rs09.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 rs09.plugin.PluginManager; -import core.tools.RandomFunction; - -/** - * Handles the 2009scape 2007 Christmas Event. - * @author Vexia - * - */ -@Initializable -public class ChristmasEvent extends HolidayEvent { - - /** - * The location of places where imps & snow are. - */ - private static final Location[] HUBS = new Location[] {Location.create(2965, 3380, 0), Location.create(3079, 3250, 0), Location.create(2659, 3657, 0), Location.create(2664, 3305, 0), Location.create(2898, 3544, 0), new Location(3159, 3490, 0)}; - - /** - * The santa hat item. - */ - private static final Item SANTA_HAT = new Item(1050); - - /** - * The christmas cracker item. - */ - private static final Item CHRISTMAS_CRACKER = new Item(962); - - /** - * The ball of snow item. - */ - private static final Item BALL_OF_SNOW = new Item(11951); - - /** - * The snow globe item. - */ - private static final Item SNOWGLOBE = new Item(11949); - - /** - * The array of snowman hat ids. - */ - private static final int[] SNOWMAN_HATS = new int[] {11955, 11956, 11957, 11958, 11959}; - - /** - * The snowman weapon ids. - */ - private static final int[] SNOWMAN_WEAPONS = new int[] {11952, 11953, 11954}; - - /** - * The pelt option. - */ - private static final Option PELT_OPTION = new Option("Pelt", 7); - - /** - * Constructs the {@code ChristmasEvent} object. - */ - public ChristmasEvent() { - super("2007 Christmas Event", HolidayType.CHRISTMAS, 1087, 16, 10); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - if (isActive()) { - PluginManager.definePlugins(new ChristmasEventOptionPlugin(), new SnowmanItemHandler(), new SnowImpDialogue(), new SnowmanHatComponentPlugin(), new QueenOfSnowDialogue(), new SnowballItemPlugin(), new SnowmanNPC(), new PeltOptionHandler()); - } - return super.newInstance(arg); - } - - @Override - public void load() { - if (!ItemLimitation.isRegistered(SANTA_HAT.getId())) { - ItemLimitation.register(SANTA_HAT.getId(), 25); - } - if (!ItemLimitation.isRegistered(CHRISTMAS_CRACKER.getId())) { - ItemLimitation.register(CHRISTMAS_CRACKER.getId(), 25); - } - if (ItemLimitation.getAmountLeft(SANTA_HAT.getId()) > 0) { - HolidayItem.startRandomSpawn(SANTA_HAT, 1000, Location.create(3047, 3296, 0), Location.create(3016, 3291, 0), Location.create(3013, 3260, 0), Location.create(3050, 3236, 0), Location.create(3091, 3275, 0), Location.create(3102, 3270, 1), Location.create(3210, 3221, 3), Location.create(3300, 3193, 0), Location.create(3284, 3162, 1), Location.create(2741, 3443, 0), Location.create(2695, 3462, 0), Location.create(2667, 3291, 1), Location.create(3079, 3512, 0), Location.create(3079, 3548, 0), Location.create(3018, 3602, 0), Location.create(3135, 3515, 0), Location.create(3216, 3412, 1), Location.create(2942, 3382, 0), Location.create(2973, 3338, 0)); - } - NPC imp; - for (Location location : HUBS) { - imp = NPC.create(6732, location); - imp.setWalks(true); - imp.init(); - } - snow(); - } - - @Override - public boolean enter(Entity entity) { - if (entity instanceof Player) { - Player player = entity.asPlayer(); - player.setAttribute("last-location", player.getRenderInfo().getLastLocation()); - } - return super.enter(entity); - } - - @SuppressWarnings("deprecation") - @Override - public boolean leave(Entity e, boolean logout) { - if (e instanceof Player) { - Player player = e.asPlayer(); - for (int i : SNOWMAN_WEAPONS) { - if (player.getInventory().contains(i, 1)) { - player.getInventory().remove(new Item(i, player.getInventory().getAmount(i))); - } - if (player.getBank().containItems(i, 1)) { - player.getBank().remove(new Item(i, player.getBank().getAmount(i))); - } - } - if (logout) { - player.setLocation(player.getAttribute("last-location", HUBS[0])); - } - } - return super.leave(e, logout); - } - - @Override - public void finalizeDeath(Entity killer, Entity victim) { - if (killer instanceof Player && victim instanceof NPC) { - Player player = (Player) killer; - if (victim.getProperties().getCurrentCombatLevel() > 0) { - int chance = (5_000 / victim.getProperties().getCurrentCombatLevel()) * 100; - if (RandomFunction.random(chance) == 0) { - int itemId = CHRISTMAS_CRACKER.getId(); - if (ItemLimitation.getAmountLeft(itemId) > 0) { - ItemLimitation.decreaseAmount(itemId); - Repository.sendNews(player.getUsername() + " found a christmas cracker. There are " + ItemLimitation.getAmountLeft(itemId) + " crackers left!"); - player.getPacketDispatch().sendMessage("You find a christmas cracker!."); - GroundItemManager.create(new Item(itemId), victim.getLocation(), player); - } - } - } - } - } - - /** - * Sends snow near the hub. - */ - private void snow() { - Location loc = null; - Scenery obj = null; - for (Location location : HUBS) { - for (int x = 2; x < 10; x++) { - for (int y = 2; y < 10; y++) { - loc = location.transform(x, y, 0); - if (RegionManager.getObject(loc) == null && RegionManager.isTeleportPermitted(loc) && RandomFunction.random(3) == 1) { - obj = new Scenery(28296, loc); - SceneryBuilder.add(obj); - RegionManager.removeClippingFlag(0, loc.getX(), loc.getY(), false, 0x100); - } - loc = location.transform(-x, y, 0); - if (RegionManager.getObject(loc) == null && RegionManager.isTeleportPermitted(loc) && RandomFunction.random(3) == 1) { - obj = new Scenery(28296, loc); - SceneryBuilder.add(obj); - RegionManager.removeClippingFlag(0, loc.getX(), loc.getY(), false, 0x100); - } - loc = location.transform(x, -y, 0); - if (RegionManager.getObject(loc) == null && RegionManager.isTeleportPermitted(loc) && RandomFunction.random(3) == 1) { - obj = new Scenery(28296, loc); - SceneryBuilder.add(obj); - RegionManager.removeClippingFlag(0, loc.getX(), loc.getY(), false, 0x100); - } - loc = location.transform(-x, -y, 0); - if (RegionManager.getObject(loc) == null && RegionManager.isTeleportPermitted(loc) && RandomFunction.random(3) == 1) { - obj = new Scenery(28296, loc); - SceneryBuilder.add(obj); - RegionManager.removeClippingFlag(0, loc.getX(), loc.getY(), false, 0x100); - } - } - } - } - } - - /** - * Sends a christmas teleport. - * @param player The player. - * @param location The location. - */ - public static void sendChristmasTeleport(Player player, Location location) { - player.getTeleporter().send(location, TeleportType.CHRISTMAS); - } - - @Override - public boolean isActive() { - return false; - } - - @Override - public void configure() { - registerRegion(10583); - } - - /** - * Handles interactions for the Christmas Event. - * @author Vexia - * - */ - public class ChristmasEventOptionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(28296).getHandlers().put("option:collect", this); - ItemDefinition.forId(BALL_OF_SNOW.getId()).getHandlers().put("option:build", this); - for (int i = 28266; i < 28296; i++) { - SceneryDefinition.forId(i).getHandlers().put("option:add-to", this); - } - ItemDefinition.forId(SNOWGLOBE.getId()).getHandlers().put("option:shake", this); - return this; - } - - @Override - public boolean handle(final Player player, Node node, String option) { - switch (option) { - case "collect": - if (!player.getInventory().hasSpaceFor(BALL_OF_SNOW)) { - player.sendMessage("You don't have enough inventory space to collect any snow."); - return true; - } - if (player.getInventory().add(BALL_OF_SNOW)) { - player.lock(3); - player.animate(new Animation(7529)); - player.sendMessage("You collect a ball of snow."); - } - break; - case "build": - Location loc = player.getLocation().transform(player.getDirection()); - if (RegionManager.getObject(loc) != null || !RegionManager.isTeleportPermitted(loc)) { - player.sendMessage("You can't build a snowman here."); - return true; - } - if (player.getInventory().remove(node.asItem())) { - player.sendMessage("You start to build a snowman."); - player.animate(Animation.create(7532)); - SceneryBuilder.add(new Scenery(28266, loc)); - } - break; - case "add-to": - if (node.asScenery().getId() == 28295) { - player.sendMessage("You need a " + (player.getZoneMonitor().isInZone(getName()) ? "weapon or hat " : "hat") + " to complete this snowman."); - return true; - } - if (!player.getInventory().containsItem(BALL_OF_SNOW)) { - player.sendMessage("You need snow in order to add to this snow man."); - return true; - } - if (player.getInventory().remove(BALL_OF_SNOW)) { - if (player.getLocation().equals(node.getLocation())) { - player.moveStep(); - player.faceLocation(node.getLocation()); - } - player.sendMessage("You add some snow to the snowman."); - player.lock(1); - player.animate(Animation.create(node.asScenery().getId() <= 28278 ? 7532 : 7533)); - SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(node.asScenery().getId() + 1)); - if (node.asScenery().getId() + 1 >= 28295) { - if (player.getZoneMonitor().isInZone(getName())) { - player.sendMessages("The snowman is almsot complete! Talk to a snow imp to get a hat or weapon to", "complete it."); - } else { - player.sendMessage("The snowman is almsot complete! Talk to a snow imp to get a hat to complete it."); - } - } - } - break; - case "shake": - if (player.getInventory().freeSlots() < 1) { - player.sendMessage("You don't have enough inventory space to do that."); - return true; - } - player.lock(16); - player.animate(Animation.create(7535)); - player.sendMessage("You shake the snow globe."); - GameWorld.getPulser().submit(new Pulse(1, player) { - int ticks; - @Override - public boolean pulse() { - switch (++ticks) { - case 3: - player.getInterfaceManager().open(new Component(659)); - break; - case 7: - player.graphics(new Graphics(1284)); - player.sendMessage("The snow globe fills your inventory with snow."); - player.getInventory().add(new Item(BALL_OF_SNOW.getId(), player.getInventory().freeSlots())); - player.getInterfaceManager().close(); - return true; - } - return false; - } - - }); - break; - } - return true; - } - - } - - /** - * Handles the pelt option on entities. - * @author Vexia - * - */ - public class PeltOptionHandler extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i = 6747; i < 6750; i++) { - NPCDefinition.forId(i).getHandlers().put("option:pelt", this); - } - PELT_OPTION.setHandler(this); - return this; - } - - @Override - public boolean handle(final Player player, final Node node, String option) { - if (!player.getLocation().withinDistance(node.getLocation(), 6)) { - player.sendMessage("You are too far away to throw this snowball."); - return true; - } - if (player.getInventory().remove(BALL_OF_SNOW) || player.getEquipment().remove(BALL_OF_SNOW)) { - if (!player.getEquipment().containsItem(BALL_OF_SNOW)) { - player.getInteraction().remove(PELT_OPTION); - } - final Entity target = (Entity) node; - player.lock(4); - player.face(target); - player.animate(Animation.create(7530)); - player.graphics(Graphics.create(1282)); - if (node instanceof NPC) { - player.sendMessage("You throw a snowball."); - node.asNpc().getSkills().heal(1); - int delay = 41; - int speed = 60; - player.getLocation(); - int distance = (int) Location.getDistance(player.getLocation(), target.getLocation()); - int projectileSpeed = delay + speed + distance * 5; - double hitDelay = projectileSpeed * .02857; - GameWorld.getPulser().submit(new Pulse((int) hitDelay, target) { - @Override - public boolean pulse() { - target.getImpactHandler().manualHit(player, node instanceof NPC ? 1 : 0, HitsplatType.MISS); - return true; - } - }); - } else { - player.sendMessage("You pelt " + target.asPlayer().getUsername() + " with a snowball."); - } - Projectile.create(player, target, 1282, 30, 10).send(); - return true; - } - player.sendMessage("You need a snowball in order to do that."); - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - } - - /** - * Handles the use of a snowman hat on a snowman. - * @author Vexia - * - */ - public class SnowmanItemHandler extends UseWithHandler { - - /** - * Constructs the {@code SnowmanItemHandler} - */ - public SnowmanItemHandler() { - super(11955, 11956, 11957, 11958, 11959, 11952, 11953, 11954); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(28295, OBJECT_TYPE, this); - PluginManager.definePlugin(new SnowmanDialogue()); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getInventory().remove(event.getUsedItem())) { - boolean weapon = event.getUsedItem().getId() < 11955; - int id = weapon ? 6747 + (11954 - event.getUsedItem().getId()) : 6742 + (4 - (11959 - event.getUsedItem().getId())); - if (event.getUsedItem().getId() == 11953) { - id = 6749; - } else if (event.getUsedItem().getId() == 11952) { - id = 6748; - } - final int npcId = id; - SceneryBuilder.remove(event.getUsedWith().asScenery()); - final NPC snowman = NPC.create(npcId, event.getUsedWith().getLocation()); - snowman.init(); - snowman.faceTemporary(player, 2); - snowman.setRespawn(false); - snowman.setWalks(true); - snowman.setAttribute("owner", player.getDetails().getUid()); - if (!player.getEmoteManager().isUnlocked(Emotes.SNOWMAN_DANCE)) { - player.getEmoteManager().unlock(Emotes.SNOWMAN_DANCE); - player.sendMessage("You've unlocked the snowman dance emote!"); - } - if (!weapon && HolidayEvent.getCurrent().getStage(player) < 2) { - HolidayEvent.getCurrent().setStage(player, 2); - } - if (!weapon) { - GameWorld.getPulser().submit(new Pulse(200) { - @Override - public boolean pulse() { - snowman.clear(); - return true; - } - }); - } - } - return true; - } - - /** - * Handles the snowman dialogue. - * @author Vexia - * - */ - public class SnowmanDialogue extends DialoguePlugin { - - /** - * Constructs the {@code SnowmanDialogue} - * @param player The player. - */ - public SnowmanDialogue(Player player) { - super(player); - } - - /** - * Constructs the {@code SnowmanDialogue} - */ - public SnowmanDialogue() { - /* - * empty. - */ - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SnowmanDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (npc.getAttribute("owner", 0) != player.getDetails().getUid()) { - player.sendMessage("The snowman doesn't seem interested in talking to you."); - return true; - } - npc("Happy Christmas, mortal!"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player("Merry Christmas!"); - stage = 10; - break; - case 10: - npc("And a Happy New Year!"); - stage++; - break; - case 11: - npc("You should keep the spirit of Christmas in your heart at", "all times, mortal. If you do this, then to have a happy", "Christmas is to have a Happy New Year."); - stage++; - break; - case 12: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {6742, 6743, 6744, 6745, 6746}; - } - - } - } - - /** - * Represents the snow imp dialogue. - * @author Vexia - * - */ - public static class SnowImpDialogue extends DialoguePlugin { - - /** - * The saying of snow imps. - */ - private static final String[][] SAYINGS = new String[][] { - {"Let it snow, let it snow, let it snow."}, - {"I'm dreaming of a white Keldagrim"}, - {"There's no business like snow business!"}, - {"Snowflakes! Millions of snowflakes! No two are the", "same you know!"}, - {"Isn't snow brilliant?"}, - {"Snow! Ha ha ha ha! Snow everywhere!"} - }; - - /** - * Constructs the {@code SnowImpDialogue} - * @param player The player. - */ - public SnowImpDialogue(Player player) { - super(player); - } - - /** - * Constructs the {@code SnowImpDialogue} - */ - public SnowImpDialogue() { - /* - * empty. - */ - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SnowImpDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - switch (npc.getId()) { - case 8536: - npc("Go, snow warriors! Yeah!"); - stage = 100; - break; - default: - npc(SAYINGS[RandomFunction.random(SAYINGS.length - 1)]); - stage = HolidayEvent.getCurrent().getStage(player) > 1 ? 40 : 1; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - switch (HolidayEvent.getCurrent().getStage(player)) { - case 0: - options("Who are you?", "Why are you making it snow?", "What can I do with all this snow?"); - stage = 4; - break; - case 1: - player("Could I have a snowman hat, please?"); - stage = 2; - break; - } - break; - case 2: - for (int i : SNOWMAN_HATS) { - if (player.hasItem(new Item(i))) { - npc("You already have a snowman hat, mate."); - stage = 5; - break; - } - } - end(); - player.getInterfaceManager().open(new Component(658)); - break; - case 3: - options("Who are you?", "Why are you making it snow?", "What can I do with all this snow?"); - stage++; - break; - case 4: - switch (buttonId) { - case 1: - player("Who are you?"); - stage = 10; - break; - case 2: - player("Why are you making it snow?"); - stage = 20; - break; - case 3: - player("What can I do with all this snow?"); - stage = 30; - break; - } - break; - case 5: - end(); - break; - case 10: - npc("It's a snow imp! From der Land of Snow!"); - stage++; - break; - case 11: - player("Can I go to the Land of Snow?"); - stage++; - break; - case 12: - if (HolidayEvent.getCurrent().getStage(player) > 1) { - sendChristmasTeleport(player, Location.create(2643, 5611, 0)); - end(); - break; - } - npc("We don't just let anyone into the Land of Snow, mate.", "You need to show you like snow first. Help to build a", "snowman!"); - if (HolidayEvent.getCurrent().getStage(player) < 1) { - HolidayEvent.getCurrent().setStage(player, 1); - } - stage++; - break; - case 13: - options("Who are you?", "Why are you making it snow?", "Could I have a snowman hat please?"); - stage++; - break; - case 14: - switch (buttonId) { - case 1: - player("Who are you?"); - stage = 10; - break; - case 2: - player("Why are you making it snow?"); - stage = 20; - break; - case 3: - player("Could I have a snowman hat please?"); - stage = 2; - break; - } - break; - case 20: - npc("'Cause it's Christmas, innit."); - stage = 3; - break; - case 30: - npc("Whatever yer like! I don't care! There's plen'y more", "where this lot came from."); - stage = 3; - break; - case 40: - options("Who are you?", "Why are you making it snow?", "What can I do with all this snow?", "Could I have a snowman hat, please?", "Can I go to the Land of Snow?"); - stage++; - break; - case 41: - switch (buttonId) { - case 1: - player("Who are you?"); - stage = 10; - break; - case 2: - player("Why are you making it snow?"); - stage = 20; - break; - case 3: - player("What can I do with all this snow?"); - stage = 30; - break; - case 4: - player("Could I have a snowman hat please?"); - stage = 2; - break; - case 5: - player("Can I go to the Land of Snow?"); - stage = 42; - break; - } - break; - case 42: - end(); - sendChristmasTeleport(player, Location.create(2643, 5611, 0)); - break; - case 100: - options("Who are you?", "Could I have a snowman hat, please?", "Could I have a snowman weapon, please?", "I want to go back to Keldagrim."); - stage++; - break; - case 101: - switch (buttonId) { - case 1: - player("Who are you?"); - stage = 110; - break; - case 2: - player("Could I have a snowman hat, please?"); - stage = 2; - break; - case 3: - player("Could I have a snowman weapon, please?"); - stage = 130; - break; - case 4: - player("I want to go back to Keldagrim."); - stage = 140; - break; - } - break; - case 110: - npc("It's a snow imp! From der Land of Snow!"); - stage = 100; - break; - case 130: - for (int i : SNOWMAN_WEAPONS) { - if (player.hasItem(new Item(i))) { - npc("You already have a snowman hat, mate."); - stage = 131; - break; - } - } - end(); - player.getPacketDispatch().sendString("Select a Snowman Hat or Weapon", 658, 13); - player.getPacketDispatch().sendInterfaceConfig(658, 53, false); - player.getInterfaceManager().open(new Component(658)); - break; - case 131: - break; - case 140: - npc("Very well. You may return here during Christmas-time, when", "the Land of Snow is close to Keldagrim."); - stage++; - break; - case 141: - sendChristmasTeleport(player, player.getAttribute("last-location", HUBS[0])); - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {6732, 6733, 6734, 6735, 6736, 6737, 6738, 8536}; - } - - } - - /** - * Handles the snowman hat selector component. - * @author Vexia - * - */ - public class SnowmanHatComponentPlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(658).setPlugin(this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (component.getId()) { - case 658: - switch (opcode) { - case 155: - if (player.getInventory().freeSlots() < 1) { - player.sendMessage("You don't have enough inventory space."); - player.getInterfaceManager().close(); - return true; - } - boolean weapons = button >= 32; - for (int i : weapons ? SNOWMAN_WEAPONS : SNOWMAN_HATS) { - if (player.hasItem(new Item(i))) { - player.getInterfaceManager().close(); - player.sendMessage("You already have a snowman " + (weapons ? "weapon" : "hat") + "."); - return true; - } - } - if (!weapons) { - int dif = 30 - button; - int div = 4 - (dif / 2); - itemId = 11955 + div; - } else { - //11952-11954 - itemId = 11954; - if (button == 36) { - itemId = 11952; - } else if (button == 34) { - itemId = 11953; - } - } - player.getInventory().add(new Item(itemId)); - player.getInterfaceManager().close(); - break; - } - break; - } - return true; - } - - } - - /** - * Handles the dialogue for the queen of snow. - * @author Vexia - * - */ - public class QueenOfSnowDialogue extends DialoguePlugin { - - /** - * Constructs the {@code QueenOfSnowDialogue} object. - */ - public QueenOfSnowDialogue() { - /* - * empty. - */ - } - - /** - * Constructs the {@code QueenOfSnowDialogue} object. - * @param player The player. - */ - public QueenOfSnowDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new QueenOfSnowDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - switch (HolidayEvent.getCurrent().getStage(player)) { - case 2: - npc("Happy Christmas, noble visitor. Welcome to the Land of", "Snow. I have a gift for you, if you would like it."); - stage = 15; - break; - case 3: - npc("Happy Christmas, noble visitor. Welcome to the Land of", "Snow."); - stage = 100; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 15: - options("Ooh, a present?", "I want to go back to Keldagrim."); - stage++; - break; - case 16: - switch (buttonId) { - case 1: - player("Ooh, a present?"); - stage = 17; - break; - case 2: - player("I want to go back to Keldagrim."); - stage = 150; - break; - } - break; - case 17: - npc("It is a snow globe: a tiny model of your Lumbridge", "Castle enchanted with a little of this land's magical snow."); - stage++; - break; - case 18: - interpreter.sendItemMessage(SNOWGLOBE, "The Queen of Snow hands you a snow globe."); - stage++; - break; - case 19: - if (!player.getInventory().add(SNOWGLOBE)) { - end(); - player.sendMessage("You don't have enough room in your inventory."); - break; - } - if (HolidayEvent.getCurrent().getStage(player) < 3) { - HolidayEvent.getCurrent().setStage(player, 3); - } - end(); - break; - case 100: - if (!player.hasItem(SNOWGLOBE)) { - npc("I see that you have lost your snow globe, would", "you like another one?"); - stage = 200; - break; - } - options("Happy Christmas, Your Majesty.", "Why have you sent snow to Keldagrim?", "What is the Land of Snow?", "What is the snow globe for?", "I want to go back to Keldagrim."); - stage++; - break; - case 101: - switch (buttonId) { - case 1: - player("Happy Christmas, Your Majesty."); - stage = 110; - break; - case 2: - player("Why have you sent snow to Keldagrim?"); - stage = 120; - break; - case 3: - player("What is the Land of Snow?"); - stage = 130; - break; - case 4: - player("What is the snow globe for?"); - stage = 140; - break; - case 5: - player("I want to go back to Keldagrim."); - stage = 150; - break; - } - break; - case 110: - npc("And a Happy New Year to you."); - stage++; - break; - case 111: - end(); - break; - case 120: - npc("A snowy Christmas is a tradition that I have sadly neglected", "in recent years; what is Christmas without the hope", "of snow? There can be no hope of snow if snow", "never comes."); - stage = 111; - break; - case 130: - npc("The Land of Snow was created aeons ago by Guthix, as", "part of his balancing of the world. The coldness of this", "place counteracts the heat of the great deserts and volcanoes", "of others; ensuring the correct balance of hot and cold."); - stage = 111; - break; - case 140: - npc("Nothing, besides your idle amusement. It is imbued with a", "little of the snow imps' magic. When you shake it, it", "will call some snow to you from the Land of Snow."); - stage = 111; - break; - case 150: - npc("Very well. You may return here during Christmas-time,", "when the Land of Snow is close to Keldagrim. Just speak to", "a snowman or snow imp."); - stage++; - break; - case 151: - sendChristmasTeleport(player, player.getAttribute("last-location", HUBS[0])); - end(); - break; - case 200: - options("Yes, please.", "No, thank you."); - stage++; - break; - case 201: - switch (buttonId) { - case 1: - player("Yes, please."); - stage = 18; - break; - case 2: - end(); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {6731}; - } - - } - - /** - * Represents the snowman NPC. - * @author Vexia - * - */ - public class SnowmanNPC extends AbstractNPC { - - /** - * Constructs the {@code SnowmanNPC}. - */ - public SnowmanNPC() { - super(-1, null); - } - - /** - * Constructs the {@code SnowmanNPC} - * @param id The id. - * @param loc The location. - */ - public SnowmanNPC(int id, Location loc) { - super(id, loc); - } - - @Override - public void handleTickActions() { - super.handleTickActions(); - if (!getProperties().getCombatPulse().isAttacking()) { - final List surronding = RegionManager.getLocalNpcs(this, 10); - for (NPC n : surronding) { - if (n.getProperties().getCombatPulse().isAttacking()) { - continue; - } - if (n == this) { - continue; - } - for (int i : getIds()) { - if (n.getId() == i) { - getProperties().getCombatPulse().attack(n); - n.getProperties().getCombatPulse().attack(this); - break; - } - } - } - } - } - - @Override - public void sendImpact(BattleState state) { - if (state.getEstimatedHit() > 4) { - state.setEstimatedHit(4); - } - state.setEstimatedHit(0); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new SnowmanNPC(id, location); - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if (entity instanceof Player) { - ((Player) entity).sendMessage("You can't attack a snowman."); - return false; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {6747, 6748, 6749}; - } - - } - - /** - * Handles the snow ball item plugin. - * @author Vexia - * - */ - public class SnowballItemPlugin implements Plugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - BALL_OF_SNOW.getDefinition().getHandlers().put("equipment", this); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - final Player player = (Player) args[0]; - @SuppressWarnings("unused") - Item item = (Item) args[1]; - final Item other = args.length == 2 ? null : (Item) args[2]; - if (other != null) { - identifier = "equip"; - } - switch (identifier) { - case "equip": - player.getInteraction().set(PELT_OPTION); - break; - case "unequip": - player.getInteraction().remove(PELT_OPTION); - break; - } - return true; - } - } -} diff --git a/Server/src/main/java/core/game/content/holiday/easter/BasketofEggsEvent.java b/Server/src/main/java/core/game/content/holiday/easter/BasketofEggsEvent.java deleted file mode 100644 index 17d616ae8..000000000 --- a/Server/src/main/java/core/game/content/holiday/easter/BasketofEggsEvent.java +++ /dev/null @@ -1,528 +0,0 @@ -package core.game.content.holiday.easter; - -import core.game.content.consumable.effects.HealingEffect; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.consumable.Consumables; -import core.game.content.consumable.Food; -import core.game.content.holiday.HolidayEvent; -import core.game.content.holiday.HolidayItem; -import core.game.content.holiday.HolidayType; -import core.game.content.holiday.ItemLimitation; -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.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.emote.Emotes; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import rs09.game.world.repository.Repository; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the 2005 Easter Event of giving out chocolate eggs. - * @author Vexia - * - */ -@Initializable -public class BasketofEggsEvent extends HolidayEvent { - - /** - * The chocolate eggs item. - */ - private static final Item[] CHOCOLATE_EGGS = new Item[] { new Item(12646), new Item(12647), new Item(12648) }; - - /** - * The egging option. - */ - private static final Option EGG_OPTION = new Option("Egg", 7); - - /** - * The whack option. - */ - private static final Option WHACK_OPTION = new Option("Whack", 7); - - /** - * The rubber chicken item. - */ - public static final Item RUBBER_CHICKEN = new Item(4566); - - /** - * The basket of eggs item. - */ - private static final Item BASKET_OF_EGGS = new Item(4565); - - /** - * The current egg item. - */ - private static Item egg; - - /** - * The bunny npc. - */ - private static NPC bunny; - - /** - * Constructs a new {@Code BasketofEggsEvent} {@Code Object} - */ - public BasketofEggsEvent() { - super("Basket of Eggs", HolidayType.EASTER, 1086, 16, 2); - PluginManager.definePlugins(new BasketofEggsPlugin(), new EasterBunnyDialogue(), new RubberchickenPlugin(), new ChocolateEggPlugin(), new EasterItemPlugin()); - } - - @Override - public void load() { - egg = new Item(1961); - if (!ItemLimitation.isRegistered(1037)) { - ItemLimitation.register(1037, 25); - } - if (!ItemLimitation.isRegistered(egg.getId())) { - ItemLimitation.register(egg.getId(), 100); - } - if (ItemLimitation.getAmountLeft(egg.getId()) > 0) { - HolidayItem.startRandomSpawn(egg, 1000, Location.create(3433, 3534, 0), Location.create(3099, 3548, 0), Location.create(3067, 10255, 0), Location.create(2913, 3746, 0), Location.create(3193, 3914, 0), Location.create(3214, 9618, 0), Location.create(3227, 3219, 2), Location.create(3300, 3312, 0), Location.create(3075, 3260, 0), Location.create(3213, 3428, 0), Location.create(3213, 3480, 0), Location.create(2710, 3460, 0), Location.create(2750, 3440, 0), Location.create(2927, 3144, 0), Location.create(2667, 3309, 0), Location.create(2600, 3104, 0), Location.create(2910, 4832, 0), Location.create(2925, 3445, 0), Location.create(2475, 3423, 1), Location.create(2817, 3441, 0), Location.create(2948, 3216, 0), Location.create(1864, 5243, 0), Location.create(2706, 3733, 0), Location.create(3186, 3446, 1), Location.create(3189, 9832, 0), Location.create(2655, 3310, 0), Location.create(3145, 9913, 0)); - } - bunny = NPC.create(1835, new Location(3008, 3294, 0)); - bunny.setWalks(true); - bunny.init(); - } - - @Override - public void finalizeDeath(Entity killer, Entity victim) { - if (killer instanceof Player && victim instanceof NPC) { - Player player = (Player) killer; - if (victim.getProperties().getCurrentCombatLevel() > 0) { - int chance = (5_000 / victim.getProperties().getCurrentCombatLevel()) * 100; - if (RandomFunction.random(chance) == 0) { - int itemId = 1037; - if (ItemLimitation.getAmountLeft(itemId) > 0) { - ItemLimitation.decreaseAmount(itemId); - Repository.sendNews(player.getUsername() + " found a pair of bunny ears!"); - player.getPacketDispatch().sendMessage("You find a set of bunny ears."); - GroundItemManager.create(new Item(itemId), victim.getLocation(), player); - } - } - } - } - } - - @Override - public boolean isActive() { - return false; - } - - @Override - public void configure() { - - } - - /** - * Gets the amount of eggs in a players inventory. - * @param player the player. - * @return the amount of eggs. - */ - public int getEggs(Player player) { - int eggs = 0; - for (Item item : CHOCOLATE_EGGS) { - eggs += player.getInventory().getAmount(item); - } - return eggs; - } - - /** - * Handlers the easter bunny dialogue. - * @author Vexia - * - */ - public class EasterBunnyDialogue extends DialoguePlugin { - - /** - * Constructs a new {@Code EasterBunnyDialogue} {@Code Object} - */ - public EasterBunnyDialogue() { - /* - * empty. - */ - } - - /** - * Constructs a new {@Code EasterBunnyDialogue} {@Code Object} - * @param player the player. - */ - public EasterBunnyDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new EasterBunnyDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (player.isAdmin()) { - player.sendMessage("" + egg.getLocation()); - } - switch (getStage(player)) { - case 0: - npc("Oh dear, the eggs, the eggs...", "What will I do about the eggs!? All of", "the eggs!!??"); - break; - case 1: - if (player.getAttribute("eggs", 0) >= 15) { - npc("Thank you, " + player.getUsername() + "! You handed out " + player.getAttribute("eggs", 0) + " eggs! Here", "is your Easter reward."); - } else { - Pulse pulse = player.getAttribute("egg-pulse", null); - if (pulse != null && pulse.isRunning() && player.hasItem(BASKET_OF_EGGS)) { - npc("You need to hand out a total of 15 eggs in order", "for me to give you a reward."); - stage = 4; - return true; - } - npc("You didn't hand out enough eggs in order to", "receive a prize. Take this basket and try", "again."); - stage = 2; - } - break; - case 2: - npc("Thank you for all of your help " + player.getUsername() + "!"); - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (getStage(player)) { - case 0: - switch (stage) { - case 0: - npc("I'm so sorry, I didn't realize you were there.", "What might I assist you with?"); - stage++; - break; - case 1: - options("What about eggs..?", "What are you doing here?", "Are you in a costume?", "Nothing."); - stage++; - break; - case 2: - switch (buttonId) { - case 1: - player("What about eggs..?"); - stage = 10; - break; - case 2: - npc("I am the easter bunny, and since it is easter", "I must begin handing out chocolate eggs to all", "of " + GameWorld.getSettings().getName() + "."); - stage = 20; - break; - case 3: - player("Are you in a costume?"); - stage = 30; - break; - case 4: - player("Nothing, just passing through."); - stage = 40; - break; - } - break; - case 10: - npc("Being the easter bunny is no simple task and", "I have found myself very overwhelmed this year.", "I have no clue how I must deliver all of these basket", "of eggs to all the people of " + GameWorld.getSettings().getName() + "."); - stage = 21; - break; - case 20: - npc("I must admit that it is a very large task", "for such a bunny like myself. It takes a", "whole year to prepare and I am still behind!"); - stage++; - break; - case 21: - options("Do you need any help?", "Goodluck with that."); - stage++; - break; - case 22: - if (buttonId == 1) { - player("Do you need any help?"); - stage = 24; - } else { - player("Goodluck with that."); - stage++; - } - break; - case 23: - npc("Thank you for your concern..."); - stage = 31; - break; - case 24: - npc("I would appreciate your help very much " + player.getUsername() + ".", "You have two minutes to go and hand out 15", "eggs from the basket."); - stage++; - break; - case 25: - npc("Take this basket of eggs and hand out the chocolate eggs.", "When you're done come back and talk to me. I'm sure", "we can sort out some type of reward for your assistance."); - stage++; - break; - case 26: - interpreter.sendItemMessage(BASKET_OF_EGGS, "The easter bunny hands you a basket of eggs."); - stage++; - break; - case 27: - BasketofEggsEvent.this.setStage(player, 1); - player.getInventory().add(BASKET_OF_EGGS, player); - end(); - Pulse pulse = new Pulse(200, player) { - - @Override - public boolean pulse() { - player.getDialogueInterpreter().sendDialogue("Your time is up. Go and talk to the easter bunny."); - return true; - } - - @Override - public void stop() { - player.getBank().remove(BASKET_OF_EGGS); - player.getEquipment().remove(BASKET_OF_EGGS); - player.getInventory().remove(BASKET_OF_EGGS); - super.stop(); - } - }; - player.setAttribute("egg-pulse", pulse); - GameWorld.getPulser().submit(pulse); - break; - case 30: - npc("No you fool! I am an easter bunny!!"); - stage++; - break; - case 31: - end(); - break; - case 40: - end(); - break; - } - break; - case 1: - switch (stage) { - case 0: - player.removeAttribute("eggs"); - interpreter.sendItemMessage(RUBBER_CHICKEN, "The easter bunny hands you over a rubber chicken", "and unlocks the 'Bunny-hop' emote."); - stage++; - break; - case 1: - player.getBank().remove(BASKET_OF_EGGS); - player.getEquipment().remove(BASKET_OF_EGGS); - player.getInventory().remove(BASKET_OF_EGGS); - player.getInventory().add(RUBBER_CHICKEN, player); - player.getEmoteManager().unlock(Emotes.BUNNY_HOP); - BasketofEggsEvent.this.setStage(player, 2); - end(); - break; - case 2: - interpreter.sendItemMessage(BASKET_OF_EGGS, "The easter bunny hands you a basket of eggs."); - stage++; - break; - case 3: - player.removeAttribute("eggs"); - player.getInventory().add(BASKET_OF_EGGS, player); - end(); - Pulse pulse = new Pulse(200, player) { - - @Override - public boolean pulse() { - player.getDialogueInterpreter().sendDialogue("Your time is up. Go and talk to the easter bunny."); - return true; - } - - @Override - public void stop() { - player.getBank().remove(BASKET_OF_EGGS); - player.getEquipment().remove(BASKET_OF_EGGS); - player.getInventory().remove(BASKET_OF_EGGS); - super.stop(); - } - }; - player.setAttribute("egg-pulse", pulse); - GameWorld.getPulser().submit(pulse); - break; - case 4: - end(); - break; - } - break; - case 2: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1835 }; - } - - } - - /** - * Handles the interactions related to the basket of eggs. - * @author Vexia - * - */ - public class BasketofEggsPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - EGG_OPTION.setHandler(this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (!(node instanceof Player)) { - return false; - } - if (getStage(player) != 1) { - player.getEquipment().remove(BASKET_OF_EGGS); - player.getInventory().remove(BASKET_OF_EGGS); - player.getBank().remove(BASKET_OF_EGGS); - return true; - } - if (!player.getEquipment().containsItem(BASKET_OF_EGGS)) { - player.sendMessage("You need to be wielding a basket of eggs in order to do that."); - return true; - } - Pulse pulse = player.getAttribute("egg-pulse", null); - if (pulse == null || !pulse.isRunning()) { - player.getEquipment().remove(BASKET_OF_EGGS); - player.getInventory().remove(BASKET_OF_EGGS); - player.getBank().remove(BASKET_OF_EGGS); - return true; - } - Player target = node.asPlayer(); - if (getEggs(target) >= 4 && !GameWorld.getSettings().isDevMode()) { - player.sendMessage("That player already has enough chocolate eggs."); - return true; - } - if (target.getInventory().freeSlots() < 1) { - player.sendMessage("That player doesn't have enough room in their inventory."); - return true; - } - target.sendMessage(player.getUsername() + " gave you an easter egg!"); - player.setAttribute("eggs", player.getAttribute("eggs", 0) + 1); - player.sendMessage("You gave " + target.getUsername() + " an easter egg!"); - target.getInventory().add(RandomFunction.getRandomElement(CHOCOLATE_EGGS)); - return true; - } - - } - - /** - * Handles the easter item plugin. - * @author Vexia - * - */ - public class EasterItemPlugin implements Plugin { - @Override - public Plugin newInstance(Object arg) throws Throwable { - RUBBER_CHICKEN.getDefinition().getHandlers().put("equipment", this); - BASKET_OF_EGGS.getDefinition().getHandlers().put("equipment", this); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - final Player player = (Player) args[0]; - Item item = (Item) args[1]; - final Item other = args.length == 2 ? null : (Item) args[2]; - if (other != null) { - identifier = "equip"; - item = other; - } - switch (identifier) { - case "equip": - player.getInteraction().set(item.getId() == BASKET_OF_EGGS.getId() ? EGG_OPTION : WHACK_OPTION); - break; - case "unequip": - player.getInteraction().remove(item.getId() == BASKET_OF_EGGS.getId() ? EGG_OPTION : WHACK_OPTION); - break; - } - return true; - } - } - - /** - * Handles the interactions related to the rubber chicken. - * @author Vexia - * - */ - public class RubberchickenPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - RUBBER_CHICKEN.getDefinition().getHandlers().put("option:operate", this); - RUBBER_CHICKEN.getDefinition().getHandlers().put("option:dance", this); - WHACK_OPTION.setHandler(this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "whack": - if (!(node instanceof Player)) { - return true; - } - Player target = (Player) node; - player.lock(4); - player.face(target); - player.animate(Animation.create(1833)); - player.getAudioManager().send(2257, true); - node.asPlayer().getPacketDispatch().sendMessage(player.getUsername() + " whacks you with a rubber chicken."); - return true; - case "dance": - case "operate": - if (player.getAttribute("chicken-delay", 0) > GameWorld.getTicks()) { - return true; - } - player.setAttribute("chicken-delay", GameWorld.getTicks() + 8); - player.animate(Animation.create(1835)); - return true; - } - return true; - } - - // http://www.rune-server.org/2009scape-development/rs-503-client-server/tutorials/461911-718-right-click-mute-option-mods.html - // http://www.rune-server.org/2009scape-development/rs2-server/snippets/513788-474-rs2-server-player-option-5-used-rubber-chicken-includes.html - - } - - /** - * Represents the chocolate egg plugin. - * @author Vexia - * - */ - public class ChocolateEggPlugin extends Food { - - /** - * Constructs a new {@Code ChocolateEggPlugin} {@Code Object} - */ - public ChocolateEggPlugin() { - this(-1); - } - - @Override - public ChocolateEggPlugin newInstance(Object object) { - for (Item item : CHOCOLATE_EGGS) { - Consumables.add(new ChocolateEggPlugin(item.getId())); - } - return this; - } - - /** - * Constructs a new {@code CupofTeaPlugin} {@code Object}. - */ - public ChocolateEggPlugin(int id) { - super(new int[] {id}, new HealingEffect(0), new Animation(1835)); - } - } -} diff --git a/Server/src/main/java/core/game/content/holiday/halloween/DeathMansionEvent.java b/Server/src/main/java/core/game/content/holiday/halloween/DeathMansionEvent.java deleted file mode 100644 index ad87fe745..000000000 --- a/Server/src/main/java/core/game/content/holiday/halloween/DeathMansionEvent.java +++ /dev/null @@ -1,1726 +0,0 @@ -package core.game.content.holiday.halloween; - -import java.util.ArrayList; -import java.util.List; - -import rs09.ServerConstants; -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.plugin.Initializable; -import core.game.content.dialogue.DialogueAction; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.holiday.HolidayEvent; -import core.game.content.holiday.HolidayItem; -import core.game.content.holiday.HolidayType; -import core.game.content.holiday.ItemLimitation; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.Option; -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.ImpactHandler.HitsplatType; -import core.game.node.entity.impl.ForceMovement; -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.info.Rights; -import core.game.node.entity.player.link.emote.Emotes; -import core.game.node.entity.player.link.request.RequestType; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; -import core.game.system.task.Pulse; -import rs09.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.zone.MapZone; -import core.game.world.map.zone.ZoneBorders; -import core.game.world.map.zone.ZoneBuilder; -import rs09.game.world.repository.Repository; -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.CameraContext; -import core.net.packet.context.CameraContext.CameraType; -import core.net.packet.out.CameraViewPacket; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; -import core.tools.RandomFunction; - -/** - * Handles the death mansion halloween event set in 2007. - * @author Vexia - * @author Emperor - * @see http://2009scape.wikia.com/wiki/2007_Hallowe'en_event - */ -@Initializable -public class DeathMansionEvent extends HolidayEvent { - - /** - * The last pumpkin being rewarded for skilling. - */ - private static int lastPumpkinReward; - - /** - * The grim reaper chat messages. - */ - private static final String[] GRIM_CHATS = new String[] { "Let me escort you away, @name...", "@name is mine!", "Now is the time you die, @name.", "Muahahahahaha!", "There is no escape, @name...", "Beware Mortals. @name travels with me now.", "I claim @name as my own.", "Your time here is over, @name.", "I have come for you, @name!" }; - - /** - * The current pumpkin item. - */ - private static Item pumpkin; - - /** - * The location of bad webs to choose from. - */ - private static final Location[][] WEB_PATHS = new Location[][] { { new Location(1709, 4837, 0), new Location(1708, 4839, 0), new Location(1705, 4841, 0), new Location(1703, 4843, 0) },// path - // 1 - { new Location(1706, 4836, 0), new Location(1704, 4837, 0), new Location(1702, 4842, 0) },// path - // 2 - { new Location(1704, 4835, 0), new Location(1704, 4837, 0), new Location(1697, 4841, 0), new Location(1695, 4846, 0), new Location(1697, 4848, 0) },// path - // 3 - { new Location(1700, 4835, 0), new Location(1698, 4838, 0), new Location(1697, 4841, 0), new Location(1698, 4845, 0) },// path - // 4 - { new Location(1691, 4835, 0), new Location(1690, 4838, 0), new Location(1691, 4840, 0), new Location(1693, 4843, 0), new Location(1694, 4845, 0), new Location(1695, 4846, 0), new Location(1697, 4848, 0) },// path - // 5 - { new Location(1684, 4839, 0), new Location(1687, 4840, 0), new Location(1691, 4840, 0), new Location(1693, 4843, 0), new Location(1694, 4845, 0), new Location(1695, 4846, 0), new Location(1697, 4848, 0) },// path - // 6 - { new Location(1704, 4835, 0), new Location(1704, 4837, 0), new Location(1694, 4840, 0), new Location(1693, 4843, 0), new Location(1694, 4845, 0), new Location(1698, 4845, 0) } // 7 - }; - - /** - * The start location. - */ - public static final Location START = new Location(1697, 4814, 0); - - /** - * The portal location. - */ - public static final Location PORTAL_LOCATION = new Location(3000, 3270, 0); - - /** - * The servants skull item. - */ - public static final Item SERVANT_SKULL = new Item(11784); - - /** - * The diary item. - */ - public static final Item DIARY = new Item(11780); - - /** - * The hood item. - */ - public static final Item HOOD = new Item(11789); - - /** - * The grim reaper id. - */ - public static final int GRIM_REAPER = 6390; - - /** - * The muncher npc id. - */ - public static final int MUNCHER = 2329; - - /** - * The death portal id. - */ - public static final int DEATH_PORTAL = 27254; - - /** - * Constructs a new {@code DeathMansionEvent} {@code Object} - */ - public DeathMansionEvent() { - super("Death Mansion", HolidayType.HALLOWEEN, 1085, 16, 9); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - if (isActive()) { - PluginManager.definePlugins(new DarkPortalHandler(), new DeathPortalZone(), new GrimReaperDialogue(), new MuncherDialogue(), new MuncherNPC(), new GrimItemHandler()); - } - return super.newInstance(arg); - } - - @Override - public boolean enter(Entity entity) { - if (entity instanceof Player) { - Player player = entity.asPlayer(); - player.getProperties().setSpawnLocation(START); - if (getStage(player) == 3 && !player.hasItem(SERVANT_SKULL)) { - setStage(player, 2); - } - } - return super.enter(entity); - } - - @Override - public boolean leave(Entity e, boolean logout) { - if (e instanceof Player) { - Player player = e.asPlayer(); - if (!logout) { - if (player.getAttribute("grimDis", 0) > GameWorld.getTicks()) { - return super.leave(player, logout); - } - int stage = getStage(player); - if (stage == 5) { - GrimItem.resetItems(player); - player.sendMessage("Any items you were carrying from the house have been removed."); - } - cleanItems(player, GrimItem.getItems()); - cleanItems(player, new Item[] { DIARY, SERVANT_SKULL }); - } else { - List objs = player.getAttribute("objs", null); - if (objs == null) { - objs = new ArrayList(); - } - for (Scenery o : objs) { - if (RegionManager.getLocalEntitys(o.getLocation(), 1).size() != 0) { - continue; - } - if (!o.getLocation().withinDistance(player.getLocation(), 1)) { - player.getPacketDispatch().sendSceneryAnimation(o, Animation.create(7286)); - } - } - } - player.getProperties().setSpawnLocation(ServerConstants.HOME_LOCATION); - } - return super.leave(e, logout); - } - - @Override - public boolean teleport(Entity e, int type, Node node) { - if (e instanceof Player) { - if (type != -1) { - e.asPlayer().getDialogueInterpreter().sendDialogues(GRIM_REAPER, null, "Foolish mortal! Do you have no manners? Use", "the portal near me to exit my mansion."); - return false; - } - } - return super.teleport(e, type, node); - } - - @Override - public boolean isComplete(Player player) { - return super.isComplete(player) || getStage(player) < 0 || getConfig(player) == 249852; - } - - @Override - public boolean interact(Entity e, final Node target, Option option) { - if (e instanceof Player) { - final Player player = e.asPlayer(); - switch (target.getId()) { - case DEATH_PORTAL: - player.teleport(PORTAL_LOCATION); - return true; - case 27276: - handlePassageWay(player, target.asScenery()); - return true; - case 27266: - passSpiderWeb(player, target.asScenery()); - return true; - case 27242: - case 27243: - if (getStage(player) >= 6) { - player.setAttribute("grimDis", GameWorld.getTicks() + 2); - player.teleport(target.getId() == 27243 ? Location.create(1703, 4826, 0) : Location.create(1639, 4835, 0)); - return true; - } - player.getDialogueInterpreter().sendDialogues(GRIM_REAPER, null, "Don't go gallivanting around my house. Come and see", "me if you're not sure what to do."); - return true; - case 27257: - if (!player.getInventory().hasSpaceFor(SERVANT_SKULL)) { - player.sendMessage("You don't have enough inventory space."); - return true; - } - int webIndex = player.getAttribute("webIndex", 0); - int newIndex = -1; - while (newIndex == -1) { - newIndex = RandomFunction.random(WEB_PATHS.length); - if (newIndex == webIndex) { - newIndex = -1; - } - } - player.setAttribute("/save:webIndex", newIndex); - setStage(player, 3); - player.getInventory().add(SERVANT_SKULL); - player.getDialogueInterpreter().sendItemMessage(SERVANT_SKULL, "You found the servant's skull. Now to get out of the garden."); - return true; - case 2329: - player.getDialogueInterpreter().open("muncher-dialogue", target); - return true; - case 27245: - if (getStage(player) >= 6) { - player.sendMessage("There's no need to search the table any more."); - return true; - } - if (!player.hasItem(DIARY)) { - player.getInventory().add(DIARY); - player.getDialogueInterpreter().sendItemMessage(DIARY, "You find a diary on the table. This should give some clues as to where items within the room should go."); - } else { - player.sendMessage("You already have a copy of the diary, you don't need to take another."); - } - return true; - case 11780: - if (option.getName().toLowerCase().equals("read")) { - if (!player.getAttribute("read-diary", false)) { - player.getDialogueInterpreter().sendDialogues(player, null, "Hopefully, by reading this, I can find clues on where to", "put any items I find."); - player.setAttribute("/save:read-diary", true); - player.getDialogueInterpreter().addAction(new DialogueAction() { - @Override - public void handle(Player player, int buttonId) { - player.getDialogueInterpreter().open("grim-diary"); - } - }); - } else { - player.getDialogueInterpreter().open("grim-diary"); - } - return true; - } - break; - case 27255: - case 27247: - case 27246: - case 27261: - case 27252: - case 27250: - case 27248: - case 27251: - case 27249: - case 27253: - final GrimItem item = GrimItem.forObjectId(target.getId()); - if (item == GrimItem.HUMAN_BONES) { - player.getDialogueInterpreter().sendOptions("Where to search?", "Towards the left", "Towards the right"); - player.getDialogueInterpreter().addAction(new DialogueAction() { - @Override - public void handle(Player player, int buttonId) { - if (buttonId == 2) { - item.search(player, target.asScenery()); - } else { - player.animate(Animation.create(7271)); - player.getDialogueInterpreter().sendDialogues(player, FacialExpression.SUSPICIOUS, "Arrghhh! My eyes!"); - } - } - }); - return true; - } else if (item == GrimItem.EYEBALL) { - player.getDialogueInterpreter().sendOptions("Where to search?", "Under the cushions", "Under the sofa"); - player.getDialogueInterpreter().addAction(new DialogueAction() { - @Override - public void handle(Player player, int buttonId) { - if (buttonId == 1) { - item.search(player, target.asScenery()); - } else { - player.getPacketDispatch().sendSceneryAnimation(target.asScenery(), Animation.create(7278)); - player.animate(Animation.create(7272)); - player.getDialogueInterpreter().sendDialogues(player, FacialExpression.DISGUSTED, "That wasn't such a good idea."); - } - } - }); - return true; - } - item.search(player, target.asScenery()); - return true; - case 27278: - final Location end = target.getLocation().equals(new Location(1633, 4824, 0)) ? new Location(1630, 4824, 0) : target.getLocation().equals(new Location(1627, 4819, 0)) ? new Location(1627, 4819, 0) : target.getLocation().equals(new Location(1630, 4819, 0)) ? new Location(1627, 4819, 0) : target.getLocation().equals(1624, 4822, 0) ? new Location(1624, 4828, 0) : new Location(1637, 4820, 0); - if (target.getLocation().equals(new Location(1637, 4820, 0)) || target.getLocation().equals(new Location(1630, 4824, 0)) || target.getLocation().equals(new Location(1627, 4819, 0))) { - player.sendMessage("You can't go back that way."); - return true; - } - player.lock(3); - if (target.getLocation().equals(1624, 4822, 0)) { - player.sendChat("Weeeeee"); - GameWorld.getPulser().submit(new Pulse(1, player) { - - @Override - public boolean pulse() { - player.getWalkingQueue().reset(); - player.getWalkingQueue().setRunDisabled(true); - player.addExtension(LogoutTask.class, new LocationLogoutTask(10000000, new Location(1624, 4821, 0))); - player.getInterfaceManager().closeDefaultTabs(); - player.getAppearance().setStandAnimation(7266); - player.getAppearance().setWalkAnimation(7267); - player.getAppearance().setRunAnimation(7267); - player.getAppearance().setTurn180(7267); - player.getAppearance().setTurn90ccw(7267); - player.getAppearance().setTurn90cw(7267); - player.getAppearance().sync(); - return true; - } - - }); - } - player.getPacketDispatch().sendSceneryAnimation(target.asScenery(), Animation.create(7296)); - AgilityHandler.walk(player, -1, player.getLocation(), target.getLocation(), null, 0.0, null); - GameWorld.getPulser().submit(new Pulse(1, player) { - - @Override - public boolean pulse() { - AgilityHandler.forceWalk(player, -1, player.getLocation(), end, Animation.create(target.getLocation().equals(1624, 4822, 0) ? 7269 : 7268), target.getLocation().equals(1624, 4822, 0) ? 80 : 40, 0.0, null); - return true; - } - - }); - return true; - case 27211: - player.getInterfaceManager().restoreTabs(); - player.removeExtension(LogoutTask.class); - player.getAppearance().setDefaultAnimations(); - player.getAppearance().sync(); - player.getWalkingQueue().setRunDisabled(false); - AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(0, 3, 0), Animation.create(7273), 6, 0.0, null); - return true; - case 27212: - player.sendMessage("You can't go back that way."); - return true; - case 27218: - player.lock(10); - Location strt = player.getLocation().transform(0, -1, 0); - AgilityHandler.forceWalk(player, -1, player.getLocation(), strt, Animation.create(7274), 5, 0.0, null); - GameWorld.getPulser().submit(new Pulse(1, player) { - int ticks; - int x = 1636; - int y = 4829; - int yInc = 0; - int xInc = 0; - int speed = 100; - int height = 560; - - @Override - public boolean pulse() { - switch (++ticks) { - case 1: - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.POSITION, x + xInc, y + yInc, height, 1, speed)); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.ROTATION, x + 5, y - 4, height, 1, speed)); - break; - case 8: - x = 1637; - y = 4816; - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.POSITION, x + xInc, y + yInc, height, 1, speed)); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.ROTATION, x + 1, y + 1, height, 1, speed)); - ForceMovement.run(player, player.getLocation(), player.getLocation().transform(0, -5, 0), null, null, Direction.SOUTH, 50); - break; - case 10: - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.RESET, x + 5, y - 4, height, 1, speed)); - return true; - } - return false; - } - }); - return true; - } - } - return super.interact(e, target, option); - } - - @Override - public void locationUpdate(Entity entity, Location last) { - if (entity instanceof Player) { - final Player p = entity.asPlayer(); - Location loc = p.getLocation(); - List objs = p.getAttribute("objs", null); - if (objs == null) { - objs = new ArrayList(); - } - for (int i = 0; i < 1; i++) { - for (Direction direction : Direction.values()) { - Scenery o = RegionManager.getObject(loc.transform(direction, 1)); - if (o != null && o.getId() == 27241) { - objs.add(o); - p.setAttribute("objs", objs); - p.getPacketDispatch().sendSceneryAnimation(o, Animation.create(7289)); - } - } - } - for (Scenery o : objs) { - if (RegionManager.getLocalEntitys(o.getLocation(), 1).size() != 0) { - continue; - } - if (!o.getLocation().withinDistance(p.getLocation(), 1)) { - p.getPacketDispatch().sendSceneryAnimation(o, Animation.create(7286)); - } - } - if (loc.getX() >= 1630 && loc.getX() <= 1646 && loc.getY() >= 4844 && loc.getY() <= 4852) { - final Scenery object = RegionManager.getObject(entity.getLocation()); - if (object != null && object.getId() == 27240) { - p.getWalkingQueue().reset(); - p.getLocks().lock(); - p.sendMessage("You accidentally trigger a trap."); - GameWorld.getPulser().submit(new Pulse(1, p) { - int ticks; - - @Override - public boolean pulse() { - switch (++ticks) { - case 1: - if (!p.getLocation().equals(object.getLocation())) { - p.teleport(object.getLocation()); - } - p.lock(); - p.getPacketDispatch().sendSceneryAnimation(object, Animation.create(1939)); - break; - case 2: - p.animate(Animation.create(4366)); - break; - case 4: - p.getPacketDispatch().sendGlobalPositionGraphic(86, object.getLocation()); - p.teleport(new Location(1641, 4830, 0)); - break; - case 5: - p.unlock(); - p.getDialogueInterpreter().sendDialogues(GRIM_REAPER, null, "Look closely and you should see the joins", "of the doors on the ground."); - return true; - } - return false; - } - }); - return; - } - } - } - super.locationUpdate(entity, last); - } - - @Override - public boolean canRequest(RequestType type, Player player, Player target) { - if (player.getAppearance().getStandAnimation() == 7266 || target.getAppearance().getStandAnimation() == 7266) { - return false; - } - return super.canRequest(type, player, target); - } - - /** - * Handles the passage way. - * @param player the player. - * @param target the target. - */ - private void handlePassageWay(final Player player, final Scenery target) { - int stage = getStage(player); - if (stage < 1 || stage < 4 && target.getLocation().getX() == 1694) { - electrocute(player, target); - return; - } - if (stage >= 6 && target.getLocation().getX() == 1645) { - player.lock(2); - player.animate(Animation.create(7299)); - player.graphics(Graphics.create(281)); - setStage(player, stage + 1); - player.teleport(stage + 1 == 8 ? Location.create(1633, 4839, 0) : new Location(1642, 4828, 0), 1); - player.getDialogueInterpreter().open(GRIM_REAPER, true, true); - return; - } - if (stage >= 4 && player.getLocation().getX() == 1701 || stage >= 6 && (target.getLocation().getX() == 1694 && player.getLocation().getX() >= target.getLocation().getX()) || stage >= 8 && target.getLocation().equals(new Location(1641, 4829, 0))) { - player.getDialogueInterpreter().sendDialogues(GRIM_REAPER, null, "Foolish mortal! You have no business in there."); - return; - } - if (stage == 1 && player.getLocation().getX() == 1701) { - setStage(player, 2); - passDorway(player, target.getLocation()); - player.getDialogueInterpreter().open(GRIM_REAPER, true, true); - return; - } else if (stage == 3 && player.getLocation().getX() == 1701 && player.getInventory().containsItem(SERVANT_SKULL)) { - setStage(player, 4); - passDorway(player, target.getLocation()); - player.getInventory().remove(SERVANT_SKULL); - player.removeAttribute("webIndex"); - player.getDialogueInterpreter().open(GRIM_REAPER, true, true, true); - return; - } else if (getStage(player) == 4 && target.getLocation().getX() == 1694) { - setStage(player, 5); - passDorway(player, target.getLocation()); - player.getDialogueInterpreter().open(GRIM_REAPER, true, true); - return; - } - player.getDialogueInterpreter().sendDialogues(6389, 7292, "We're watching you..."); - player.getDialogueInterpreter().addAction(new DialogueAction() { - @Override - public void handle(Player player, int buttonId) { - passDorway(player, target.getLocation()); - } - }); - } - - /** - * Passes through a spider web. - * @param player the player. - * @param object the object. - */ - private void passSpiderWeb(Player player, Scenery object) { - int webIndex = player.getAttribute("webIndex", -1); - if (webIndex == -1) { - player.setAttribute("/save:webIndex", (webIndex = RandomFunction.random(WEB_PATHS.length))); - } - boolean goodWeb = false; - for (Location l : WEB_PATHS[webIndex]) { - if (l.equals(object.getLocation())) { - goodWeb = true; - break; - } - } - if (!goodWeb) { - player.sendMessage("You cannot pass through this particular web - try another."); - return; - } - player.getPacketDispatch().sendSceneryAnimation(object, Animation.create(7280)); - player.lock(3); - AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(Direction.getDirection(player.getLocation(), object.getLocation()), 2), Animation.create(2240), 8, 0.0, null); - } - - /** - * Passes through a doorway. - * @param player the player. - * @param location the location. - */ - public void passDorway(Player player, Location location) { - Location end = player.getLocation().transform(Direction.getDirection(player.getLocation(), location), 2); - player.addExtension(LogoutTask.class, new LocationLogoutTask(3, end)); - player.sendMessage("You safely pass through the gargoyles' judgement."); - player.lock(3); - AgilityHandler.walk(player, -1, player.getLocation(), end, null, 0.0, null); - } - - /** - * Electrocutes the player. - * @param player the player. - * @param object the object. - */ - private void electrocute(final Player player, Scenery object) { - player.lock(4); - player.animate(Animation.create(7299)); - player.graphics(Graphics.create(281)); - int x = 0, x2 = 0; - int y = 0, y2 = 0; - Direction dir = object.getDirection(); - if (dir == Direction.SOUTH || dir == Direction.NORTH) { - x = 1; - x2 = -1; - } else if (dir == Direction.WEST) { - y = 1; - y2 = -1; - } else { - y = -1; - y2 = 1; - } - Scenery first = RegionManager.getObject(object.getLocation().transform(x, y, 0)); - Scenery second = RegionManager.getObject(object.getLocation().transform(x2, y2, 0)); - if (first != null && second != null) { - player.getPacketDispatch().sendSceneryAnimation(first, Animation.create(7285)); - player.getPacketDispatch().sendSceneryAnimation(second, Animation.create(7285)); - } - player.sendMessage("You fail to pass through the gargoyles' judgement"); - GameWorld.getPulser().submit(new Pulse(1, player) { - @Override - public boolean pulse() { - player.getImpactHandler().manualHit(player, player.getSkills().getLifepoints(), HitsplatType.NORMAL); - player.getDialogueInterpreter().sendDialogues(GRIM_REAPER, null, "You may be able to wander around other people's", "houses, but I shall not allow it."); - return true; - } - }); - } - - @Override - public void load() { - // ObjectBuilder.add(new GameObject(DEATH_PORTAL, new Location(2999, - // 3268, 0))); - pumpkin = new Item(1959); - for (int id = 1053; id < 1059; id += 2) { - if (!ItemLimitation.isRegistered(id)) { - ItemLimitation.register(id, 15); - } - } - if (!ItemLimitation.isRegistered(pumpkin.getId())) { - ItemLimitation.register(pumpkin.getId(), 100); - } - if (ItemLimitation.getAmountLeft(pumpkin.getId()) > 0) { - HolidayItem.startRandomSpawn(pumpkin, 1000, Location.create(3433, 3534, 0), Location.create(3099, 3548, 0), Location.create(3067, 10255, 0), Location.create(2913, 3746, 0), Location.create(3193, 3914, 0), Location.create(3214, 9618, 0), Location.create(3227, 3219, 2), Location.create(3300, 3312, 0), Location.create(3075, 3260, 0), Location.create(3213, 3428, 0), Location.create(3213, 3480, 0), Location.create(2710, 3460, 0), Location.create(2750, 3440, 0), Location.create(2927, 3144, 0), Location.create(2667, 3309, 0), Location.create(2600, 3104, 0), Location.create(2910, 4832, 0), Location.create(2925, 3445, 0), Location.create(2475, 3423, 1), Location.create(2817, 3441, 0), Location.create(2948, 3216, 0), Location.create(1864, 5243, 0), Location.create(2706, 3733, 0), Location.create(3186, 3446, 1), Location.create(3189, 9832, 0), Location.create(2655, 3310, 0), Location.create(3145, 9913, 0)); - } - } - - @Override - public void addExperience(Player player, int skill, double experience) { - if (skill <= Skills.MAGIC && skill != Skills.PRAYER) { - return; - } - if (GameWorld.getTicks() - lastPumpkinReward < 1000 && RandomFunction.random(2500) == 0 && ItemLimitation.getAmountLeft(pumpkin.getId()) > 0) { - if (player.getInventory().add(pumpkin)) { - player.getPacketDispatch().sendMessage("You found a pumpkin!"); - lastPumpkinReward = GameWorld.getTicks(); - ItemLimitation.decreaseAmount(pumpkin.getId()); - } - } - } - - @Override - public void commenceDeath(Entity killer, Entity victim) { - if (!(victim instanceof Player) || victim.getZoneMonitor().isInZone("Death Mansion")) { - return; - } - Player player = (Player) victim; - final NPC reaper = NPC.create(2862, player.getLocation()); - reaper.setLocation(RegionManager.getSpawnLocation(player, reaper)); - if (reaper.getLocation() == null) { - return; - } - reaper.init(); - reaper.face(player); - reaper.setWalks(false); - reaper.setNeverWalks(true); - reaper.setAggressive(false); - reaper.animate(Animation.create(392)); - reaper.sendChat(RandomFunction.getRandomElement(GRIM_CHATS).replace("@name", player.getUsername())); - GameWorld.getPulser().submit(new Pulse(4, reaper) { - @Override - public boolean pulse() { - reaper.clear(); - return true; - } - }); - } - - @Override - public void finalizeDeath(Entity killer, Entity victim) { - if (killer instanceof Player && victim instanceof NPC) { - Player player = (Player) killer; - if (victim.getProperties().getCurrentCombatLevel() > 0) { - int chance = (5_000 / victim.getProperties().getCurrentCombatLevel()) * 100; - if (RandomFunction.random(chance) == 0) { - int itemId = 1053 + (RandomFunction.random(3) * 2); - if (ItemLimitation.getAmountLeft(itemId) > 0) { - ItemLimitation.decreaseAmount(itemId); - Repository.sendNews(player.getUsername() + " found a halloween mask!"); - player.getPacketDispatch().sendMessage("You find a scary looking mask."); - GroundItemManager.create(new Item(itemId), victim.getLocation(), player); - } - } - } - } - } - - @Override - public boolean isActive() { - return false; - } - - @Override - public void configure() { - registerRegion(6731); - registerRegion(6475); - } - - /** - * Handles the grim item handler. - * @author Vexia - */ - public static class GrimItemHandler extends UseWithHandler { - - /** - * The object ids. - */ - private static final int[] OBJECTS = new int[] { 27249, 27255, 27247, 27246, 27261, 27260, 27252, 27248, 27250, 27251, 27253 }; - - /** - * Constructs a new {@code GrimItemHandler} {@code Object} - */ - public GrimItemHandler() { - super(11782, 11781, 11785, 11787, 11786, 11788, 11783); - } - - @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) { - final Player player = event.getPlayer(); - GrimItem item = GrimItem.forDestinationId(event.getUsedWith().getId()); - if (item == null || event.getUsedItem().getId() != item.getItem().getId()) { - return false; - } - item.useWithEvent(player, event.getUsedWith().asScenery()); - return true; - } - - } - - /** - * A grim reaper item. - * @author Vexia - */ - public enum GrimItem { - TESTAMENT(new int[] { 27255 }, new Item(11782), "You found someone's Last Will and Testament.", new String[] { "You recall the entry in the diary, '...have to sit on that for a while...'", "and place the Last Will and Testament under the sofa cushions." }, 27252), ROBE(new int[] { 27247, 27246 }, new Item(11781), "You found the Grim Reaper's robes.", new String[] { "You recall the entry in the diary, '...decided to throw them in the", "fireplace...' and place the robes in the fireplace." }, 27251), HOURGLASS(new int[] { 27261, 27260 }, new Item(11785), "You found an hourglass.", new String[] { "You recall the entry in the diary, '...hourglass today so have added", "that to the fishtank...' and place the hourglass in the fishtank." }, 27253), EYEBALL(new int[] { 27252 }, new Item(11787), "You found someone's eye.", new String[] { "You recall the entry in the diary, '...I put the eye back on the", "shelf...' and place the eye on the shelf." }, 27261, 27260), SCYTHE_SHARPENER(new int[] { 27248, 27249, 27250 }, new Item(11786), "You found a scythe sharpener.", new String[] { "You recall the entry in the diary, '...put the sharpener back in the", "cabinet...' and place the scythe sharpener in the cabinet." }, 27247, 27246), POTION(new int[] { 27251 }, new Item(11788), "You found a 'Voice of Doom' potion.", new String[] { "You recall the entry in the diary, '...I found my old 'Voice of Doom'", "potion amongst some books...' and place the potion on the bookcase." }, 27248, 27249, 27250), HUMAN_BONES(new int[] { 27253 }, new Item(11783), "You found some bones. They look decidely human.", new String[] { "You recall the entry in the diary, '...decided to lock them up in the", "chest...' and place the bones in the chest." }, 27255); - - /** - * The object id. - */ - private final int[] objectIds; - - /** - * The item. - */ - private final Item item; - - /** - * The found text. - */ - private final String foundText; - - /** - * The return text. - */ - private final String[] returnText; - - /** - * The destination object id. - */ - private final int[] destinationIds; - - /** - * Constructs a new {@code GrimItem} {@code Object} - * @param objectIds the object ids. - * @param item the item. - * @param foundText the found text. - * @param returnText the returning text. - */ - private GrimItem(int[] objectIds, Item item, String foundText, String[] returnText, int... destinationIds) { - this.objectIds = objectIds; - this.item = item; - this.foundText = foundText; - this.returnText = returnText; - this.destinationIds = destinationIds; - } - - /** - * Searches for the grim item. - * @param player the player. - * @param object the object. - */ - public void search(Player player, Scenery object) { - if (!player.getInventory().hasSpaceFor(item)) { - player.sendMessage("You don't have enough inventory space."); - return; - } - if (player.getInventory().containsItem(item) || hasGrimItem(player, this)) { - player.getDialogueInterpreter().sendDialogue("You have already taken the item you were looking for", "from here."); - return; - } - if (player.getInventory().add(item)) { - player.animate(Animation.create(833)); - player.getDialogueInterpreter().sendItemMessage(item, foundText); - } - } - - /** - * Called on the use with event. - * @param player the player. - * @param object the object. - */ - public void useWithEvent(Player player, Scenery object) { - if (hasGrimItem(player, this)) { - player.getDialogueInterpreter().sendDialogue("You have already returned this item to it's proper", "spot in the mansion."); - return; - } - if (player.getInventory().remove(item)) { - setGrimItem(player, this); - player.animate(Animation.create(833)); - player.getDialogueInterpreter().sendDialogue(returnText); - if (getGrimItems(player).size() == values().length) { - DeathMansionEvent event = (DeathMansionEvent) player.getZoneMonitor().getZones().get(0).getZone(); - if (event != null) { - player.removeAttribute("ready-diary"); - event.setStage(player, 6); - player.getDialogueInterpreter().open(GRIM_REAPER, true, true); - } - } - } - } - - /** - * Checks if the player has the grim item. - * @param player the player. - * @param item the item. - * @return {@code True} if so. - */ - public static boolean hasGrimItem(Player player, GrimItem item) { - return getGrimItems(player).contains(item); - } - - /** - * Sets the grim item as completed. - * @param player the player. - * @param item the item. - */ - public static void setGrimItem(Player player, GrimItem item) { - List items = getGrimItems(player); - items.add(item); - player.setAttribute("grim-items", items); - } - - /** - * Resets the grim items. - * @param player the items. - */ - public static void resetItems(Player player) { - player.removeAttribute("grim-items"); - } - - /** - * Gets the list of set grim items. - * @param player the player. - * @return the items. - */ - public static List getGrimItems(Player player) { - List items = player.getAttribute("grim-items", null); - if (items == null) { - items = new ArrayList<>(20); - } - player.setAttribute("grim-items", items); - return items; - } - - /** - * Gets the grim item by the destination id. - * @param objectId the object id. - * @return the id. - */ - public static GrimItem forDestinationId(int objectId) { - for (GrimItem item : values()) { - for (int id : item.getDestinationId()) { - if (id == objectId) { - return item; - } - } - } - return null; - } - - /** - * Gets the object id. - * @param objectId the object id. - * @return the grim item. - */ - public static GrimItem forObjectId(int objectId) { - for (GrimItem item : values()) { - for (int id : item.getObjectIds()) { - if (id == objectId) { - return item; - } - } - } - return null; - } - - /** - * Gets the items. - * @return the items. - */ - public static Item[] getItems() { - Item[] items = new Item[values().length]; - for (int i = 0; i < items.length; i++) { - items[i] = values()[i].getItem(); - } - return items; - } - - /** - * Gets the objectId. - * @return the objectId - */ - public int[] getObjectIds() { - return objectIds; - } - - /** - * Gets the item. - * @return the item - */ - public Item getItem() { - return item; - } - - /** - * Gets the foundText. - * @return the foundText - */ - public String getFoundText() { - return foundText; - } - - /** - * Gets the returnText. - * @return the returnText - */ - public String[] getReturnText() { - return returnText; - } - - /** - * Gets the destinationId. - * @return the destinationId - */ - public int[] getDestinationId() { - return destinationIds; - } - - } - - /** - * Handles the grim reaper dialogue. - * @author Vexia - */ - public class GrimReaperDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GrimReaperDialogue} {@code Object} - */ - public GrimReaperDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GrimReaperDialogue} {@code Object} - * @param player the player. - */ - public GrimReaperDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GrimReaperDialogue(player); - } - - @Override - public boolean open(Object... args) { - switch (getStage(player)) { - case 0: - player("Erm... Excuse me... Could I ask..."); - break; - case 1: - case 2: - case 3: - if (args.length > 1) { - npc("You have found my garden I see."); - stage = 100; - return true; - } - player("Erm... What was I supposed to do?"); - break; - case 4: - if (args.length == 0 && !player.getInventory().containsItem(SERVANT_SKULL)) { - player("Erm... What was I supposed to do?"); - stage = 2; - return true; - } - player("Woah, what'd I do? Oh no, the skull's gone."); - break; - case 5: - if (args.length > 1) { - npc("You made it."); - } else { - npc("Foolish mortal, get back to the task."); - stage = -1; - } - break; - case 6: - if (args.length > 1) { - npc("Looks like you've returned everything to its proper", "location."); - stage = 20; - } else { - player("Erm... What was I supposed to do?"); - } - break; - case 7: - if (args.length > 1) { - player("Huh? What happened there?"); - stage = 20; - } else { - player("Erm... What was I supposed to do?"); - } - break; - case 8: - if (args.length > 1) { - npc("That is sufficient."); - stage = 20; - } else { - player("You wished to speak with me?"); - } - break; - case 9: - case -12290: - end(); - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (player.getAttribute("lo", false)) { - end(); - return true; - } - switch (getStage(player)) { - case 0: - switch (stage) { - case 0: - npc("Speak, mortal."); - stage++; - break; - case 1: - player("Well, sir, lord, erm, your highness..."); - stage++; - break; - case 2: - npc("I need no title."); - stage++; - break; - case 3: - player("Well, of course. Sorry. I was just wondering, what is", "this place?"); - stage++; - break; - case 4: - npc("My house. You have found yourself here because I", "deem it necessary."); - stage++; - break; - case 5: - player("You do?"); - stage++; - break; - case 6: - npc("Long have I had a servant performing tasks as", "instructed. He left my service to pass on into death", "Such a role is to be replaced."); - stage++; - break; - case 7: - player("I see so you want me to be your servant?"); - stage++; - break; - case 8: - npc("Eager assumptions shall bring you regret, human. A", "worthy soul has already been picked for the position.", "My house is open now as various things need attending", "to before the transition can take place."); - stage++; - break; - case 9: - player("So basically you want me to tidy up some things before", "your new servant arrives?"); - stage++; - break; - case 10: - npc("Interpret as you will, mortal!"); - stage++; - break; - case 11: - options("I'll help out no problem.", "That doesn't really appeal, I'm off."); - stage++; - break; - case 12: - if (buttonId == 1) { - player("I'll help out no problem."); - stage = 15; - break; - } - player("That doesn't really appeal, I'm off."); - stage++; - break; - case 13: - npc("So be it, mortal!"); - stage++; - break; - case 14: - end(); - break; - case 15: - DeathMansionEvent.this.setStage(player, 1, 16); - npc("Proceeding as instructed is the best way to survive", "here, human. I can always see you and can speak with", "you whenever I wish as this is my domain. Go to the", "garden for further instructions."); - stage++; - break; - } - break; - case 1: - case 2: - case 3: - switch (stage) { - case 0: - npc("Foolish mortal! Proceed to the garden for", "further instructions."); - stage++; - break; - case 1: - end(); - break; - case 16: - player("Always watched, To the garden. Thank you. Much", "appreciated."); - stage++; - break; - case 17: - end(); - break; - case 100: - player("Woah. You made me jump. How does your voice carry", "so far?"); - stage++; - break; - case 101: - npc("That is not of your concern. Whilst in my domain you", "shall never be far from my gaze."); - stage++; - break; - case 102: - player("Okay... So, what exactly do you want me to do out", "here?"); - stage++; - break; - case 103: - npc("My last servant died in here. His skull still remains and", "you must bring it out to prevent my next servant from", "getting the wrong impression."); - stage++; - break; - case 104: - player("And I suppose your servant was killed by some huge", "man-eating spider?"); - stage++; - break; - case 105: - npc("That is not your business, mortal. Anyway, the webs", "you see are from a nest of very small spiders. Some", "webs you can pass, some you cannot. You will only", "need to use your bare hands to break though."); - stage++; - break; - case 106: - player("Sorry I asked. I'll go find the skull and bring it back", "out of the garden."); - stage++; - break; - case 107: - npc("Yes, and do not try teleporting out of the garden with", "the skull. The skull will remain behind and you'll have to", "find it again."); - stage++; - break; - case 108: - end(); - break; - } - break; - case 4: - switch (stage) { - case -1: - end(); - break; - case 0: - npc("Do not concern yourself. I had instructed the", "gargoyles to take it from you."); - stage++; - break; - case 1: - player("You could have let me know! I almost had a heart", "attack."); - stage++; - break; - case 2: - npc("Please proceed to the westernmost room for your next", "task."); - stage++; - break; - case 3: - player("You don't waste time, do you? So, the room that looks a", "bit like your lounge? I'm on my way."); - stage++; - break; - case 4: - end(); - break; - } - break; - case 5: - switch (stage) { - case -1: - end(); - break; - case 0: - player("You sound surprised."); - stage++; - break; - case 1: - npc("You wouldn't be the first, or last, to die in this house."); - stage++; - break; - case 2: - player("I'm so pleased. So, to the task at hand. What should I", "be doing in here?"); - stage++; - break; - case 3: - npc("If you search around the room you'll find various", "items. Put them back where they belong. My diary is", "on the table - read this for some clues, but tell anyone", "what you read and you die."); - stage++; - break; - case 4: - player("*Gulp* I shall have a look."); - stage++; - break; - case 5: - end(); - break; - } - break; - case 6: - switch (stage) { - case 0: - npc("Please proceed upstairs and enter", "the room you find there."); - stage = 22; - break; - case 20: - player("Woo hoo!"); - stage++; - break; - case 21: - npc("You've not finished yet, mortal. Please proceed upstairs", "and enter the room you find there."); - stage++; - break; - case 22: - player("Upstairs it is."); - stage++; - break; - case 23: - end(); - break; - } - break; - case 7: - switch (stage) { - case 0: - npc("You must complete the playground."); - stage = 23; - break; - case 20: - npc("Again."); - stage++; - break; - case 21: - player("What?"); - stage++; - break; - case 22: - npc("One more time around just to be sure all is in order."); - stage++; - break; - case 23: - end(); - break; - } - break; - case 8: - switch (stage) { - case 0: - npc("Correct. You have completed the tasks as ordered,", "hence your time here is done."); - stage++; - break; - case 1: - player("An I'm still alive. I rock!"); - stage++; - break; - case 2: - player("So...any chance of a reward for my service?"); - stage++; - break; - case 3: - npc("Hmmm. Mortals and their goal-driven attitude - one", "thing that shall forever elude my understanding. As you", "wish, but trouble me no more."); - stage++; - break; - case 4: - player("Thank you!"); - stage++; - break; - case 5: - if (!player.getInventory().hasSpaceFor(HOOD)) { - npc("You don't have enough inventory space,", "mortal."); - stage = 23; - break; - } - interpreter.sendItemMessage(HOOD, "The Grim Reaper has given you the " + RED + "Grim Reaper hood and you've unlocked the " + RED + "Zombie Hand Emote as well as the " + RED + "Scared emote."); - stage++; - break; - case 6: - npc("Death will find you one day..."); - stage++; - break; - case 7: - DeathMansionEvent.this.setStage(player, 9); - player.getConfigManager().set(1085, 249852, true); // hallowe'en - // hand - // emote - player.getEmoteManager().unlock(Emotes.SCARED); - player.getEmoteManager().unlock(Emotes.ZOMBIE_HAND); - player.addExtension(LogoutTask.class, new LocationLogoutTask(5, PORTAL_LOCATION)); - player.getInventory().add(HOOD); - player.lock(5); - player.getProperties().setSpawnLocation(PORTAL_LOCATION); - player.getImpactHandler().manualHit(player, player.getSkills().getLifepoints(), HitsplatType.NORMAL); - close(); - player.setAttribute("lo", true); - GameWorld.getPulser().submit(new Pulse(5, player) { - @Override - public boolean pulse() { - player.setAttribute("lo", true); - player("Well. That was an experience I shall never forget."); - stage++; - return true; - } - }); - break; - case 8: - end(); - break; - case 20: - player("Phew."); - stage++; - break; - case 21: - npc("Come and speak with me downstairs."); - stage++; - break; - case 22: - player("I'll be right down."); - stage++; - break; - case 23: - end(); - break; - } - break; - case 9: - case -12290: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { GRIM_REAPER }; - } - - } - - /** - * Represents the area around the death portal. (used to darken the screen) - * @author Vexia - */ - public class DeathPortalZone extends MapZone implements Plugin { - - /** - * Constructs a new {@code DeathPortalZone} {@code Object} - */ - public DeathPortalZone() { - super("Death portal", true); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ZoneBuilder.configure(this); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public boolean enter(Entity entity) { - if (entity instanceof Player) { - darken(entity.asPlayer()); - } - return super.enter(entity); - } - - @Override - public boolean leave(Entity e, boolean logout) { - if (e instanceof Player) { - e.asPlayer().getInterfaceManager().closeOverlay(); - } - return super.leave(e, logout); - } - - @Override - public void locationUpdate(Entity e, Location last) { - if (e instanceof Player) { - darken(e.asPlayer()); - } - super.locationUpdate(e, last); - } - - /** - * Darkens the players screen. - * @param player the player. - */ - private void darken(Player player) { - int distance = (int) player.getLocation().getDistance(new Location(3000, 3269, 0)); - int component = 97; - if (distance < 5) { - component = 98; - } - if (player.getInterfaceManager().getOverlay() == null || player.getInterfaceManager().getOverlay().getId() != component) { - player.getInterfaceManager().openOverlay(new Component(component)); - } - } - - @Override - public void configure() { - register(new ZoneBorders(2993, 3263, 3008, 3275)); - } - - } - - /** - * Handles the dark portal entrance. - * @author Vexia - */ - public class DarkPortalHandler extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new DarkPortalDialogue()); - SceneryDefinition.forId(DEATH_PORTAL).getHandlers().put("option:enter", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (player.getDetails().getRights() == Rights.ADMINISTRATOR) { - player.getPacketDispatch().sendMessage("Current pumpkin location: " + pumpkin.getLocation() + "."); - } - if (isComplete(player)) { - player.sendMessage("You've no need to return to the Grim Reaper's house."); - return true; - } - player.getDialogueInterpreter().open("dark-portal"); - return true; - } - - /** - * Handles the dark portals dialogue. - * @author Vexia - */ - public class DarkPortalDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code DarkPortalDialogue} {@code Object} - */ - public DarkPortalDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DarkPortalDialogue} {@code Object} - * @param player the player. - */ - public DarkPortalDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DarkPortalDialogue(player); - } - - @Override - public boolean open(Object... args) { - switch (getStage(player)) { - case 0: - player("I can't believe I'm doing this.", "Oh, well, here goes nothing."); - break; - default: - player.teleport(START); - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (getStage(player)) { - case 0: - switch (stage) { - case 0: - player.teleport(START); - GameWorld.getPulser().submit(new Pulse(1, player) { - - @Override - public boolean pulse() { - player("Well, I'm still in one piece - that's a good start."); - stage++; - return true; - } - - }); - close(); - break; - case 1: - end(); - break; - } - break; - default: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("dark-portal") }; - } - - } - - } - - /** - * Handles the muncher npc. - * @author Vexia - */ - public class MuncherNPC extends AbstractNPC { - - /** - * Constructs a new {@code MuncherNPC} {@code Object} - * @param id the id. - * @param location the location. - */ - public MuncherNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new {@code MuncherNPC} {@code Object} - */ - public MuncherNPC() { - super(-1, null); - } - - @Override - public void handleTickActions() { - super.handleTickActions(); - if (RandomFunction.random(100) < 4) { - sendChat("Grrrrr"); - } - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new MuncherNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] { 2329 }; - } - - } - - /** - * Handles the muncher dialogue. - * @author Vexia - */ - public class MuncherDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code MuncherDialogue} {@code Object} - */ - public MuncherDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MuncherDialogue} {@code Object} - * @param player the player. - */ - public MuncherDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MuncherDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.JOLLY, "Here, boy!"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc.sendChat("Grrrrrrr"); - interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Whoaah!"); - stage++; - break; - case 1: - options("Stroke him", "Try to entertain him", "Blow a raspberry at him"); - stage++; - break; - case 2: - if (buttonId == 1) { - close(); - Animation a = Animation.create(7270); - player.animate(a); - player.lock(a.getDuration()); - npc.faceTemporary(player, 2); - npc.animate(Animation.create(6578)); - GameWorld.getPulser().submit(new Pulse(a.getDuration(), player) { - @Override - public boolean pulse() { - player("Okay, touching him seems to be a bad idea."); - stage++; - return true; - } - }); - } else if (buttonId == 2) { - close(); - Animation anim = new Animation(866); - player.animate(anim); - player.lock(anim.getDuration()); - GameWorld.getPulser().submit(new Pulse(anim.getDuration(), player) { - @Override - public boolean pulse() { - player("If it's possible for a skeletal dog to smile, I think he", "would be now."); - stage++; - return true; - } - }); - } else { - npc.sendChat("Grrrrrr"); - player.animate(Animation.create(2110)); - npc.faceTemporary(player, 3); - player.lock(4); - GameWorld.getPulser().submit(new Pulse(3, player) { - @Override - public boolean pulse() { - npc.animate(Animation.create(6579)); - player.getImpactHandler().manualHit(player, player.getSkills().getLifepoints(), HitsplatType.NORMAL); - return true; - } - }); - end(); - } - break; - case 3: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("muncher-dialogue") }; - } - - } -} diff --git a/Server/src/main/java/core/game/content/package-info.java b/Server/src/main/java/core/game/content/package-info.java deleted file mode 100644 index f9a620503..000000000 --- a/Server/src/main/java/core/game/content/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Holds more important content classes. - * - * @author Emperor - * - */ -package core.game.content; \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/DoricsQuest.java b/Server/src/main/java/core/game/content/quest/free/DoricsQuest.java deleted file mode 100644 index 50d0b6008..000000000 --- a/Server/src/main/java/core/game/content/quest/free/DoricsQuest.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.content.quest.free; - -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/java/core/game/content/quest/free/SheepShearer.java b/Server/src/main/java/core/game/content/quest/free/SheepShearer.java deleted file mode 100644 index 4a60ac85c..000000000 --- a/Server/src/main/java/core/game/content/quest/free/SheepShearer.java +++ /dev/null @@ -1,136 +0,0 @@ -package core.game.content.quest.free; - -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)); - } - player.getPacketDispatch().sendMessage("Congratulations! Quest complete!"); - } - - @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/java/core/game/content/quest/free/WitchsPotion.java b/Server/src/main/java/core/game/content/quest/free/WitchsPotion.java deleted file mode 100644 index 6968f1dc8..000000000 --- a/Server/src/main/java/core/game/content/quest/free/WitchsPotion.java +++ /dev/null @@ -1,91 +0,0 @@ -package core.game.content.quest.free; - -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/java/core/game/content/quest/free/blackknightsfortress/SirAmikVarzeDialogue.java b/Server/src/main/java/core/game/content/quest/free/blackknightsfortress/SirAmikVarzeDialogue.java deleted file mode 100644 index 9e7f1ecfc..000000000 --- a/Server/src/main/java/core/game/content/quest/free/blackknightsfortress/SirAmikVarzeDialogue.java +++ /dev/null @@ -1,354 +0,0 @@ -package core.game.content.quest.free.blackknightsfortress; - -import core.game.content.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.GroundItemManager; -import core.game.node.item.Item; - -/** - * Represents the sir amik varze dialogue. - * @author Vexia - * - */ -public class SirAmikVarzeDialogue extends DialoguePlugin { - - /** - * Represents the instanced quest. - */ - private Quest quest; - - /** - * Constructs a new {@code SirAmikVarzeDialogue} {@code Object}. - */ - public SirAmikVarzeDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code SirAmikVarzeDialogue} {@code Object}. - * @param player the player. - */ - public SirAmikVarzeDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SirAmikVarzeDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Black Knights' Fortress"); - switch (quest.getStage(player)) { - case 30: - interpreter.sendDialogues(player, null, "I have ruined the Black Knights' invincibility potion."); - stage = 0; - break; - case 10: - case 20: - interpreter.sendDialogues(npc, null, "How's the mission going?"); - stage = 0; - break; - case 100: - interpreter.sendDialogues(player, null, "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?"); - stage = 0; - break; - } - return true; - } - - @Override - 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; - } - 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."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, null, "No 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."); - stage = 3; - break; - case 3: - interpreter.sendDialogue("Sir Amik hands you 2500 coins."); - stage = 4; - break; - case 4: - if (!player.getInventory().add(new Item(995, 2500))) { - GroundItemManager.create(new Item(995, 2500), player); - } - quest.finish(player); - player.getQuestRepository().syncronizeTab(player); - end(); - break; - } - break; - 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."); - stage = 1; - break; - case 1: - end(); - break; - } - break; - case 10: - switch (stage) { - case 0: - interpreter.sendDialogues(player, null, "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!"); - 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."); - stage = 3; - } else { - interpreter.sendDialogues(npc, null, "Don't forget to read that dossier of information I gave", "you."); - stage = 4; - } - break; - case 3: - player.getInventory().add(BlackKnightsFortress.DOSSIER); - end(); - break; - case 4: - end(); - break; - } - break; - case 0: - switch (stage) { - case 0: - if (player.getQuestRepository().getPoints() < 12) { - interpreter.sendDialogues(player, null, "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."); - stage = 1; - } - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "I seek a quest."); - stage = 5; - break; - case 2: - interpreter.sendDialogues(player, null, "I don't I'm just looking around."); - stage = 2; - break; - } - break; - case 2: - end(); - break; - case 3: - interpreter.sendDialogues(npc, null, "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."); - stage = 6; - break; - case 6: - interpreter.sendOptions("Select an Option", "I laugh in the face of danger!", "I go and cower in the corner at the first sign of danger!"); - stage = 7; - break; - case 7: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "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!"); - stage = 8; - break; - } - break; - case 8: - interpreter.sendDialogues(npc, null, "Err...."); - stage = 9; - break; - case 9: - interpreter.sendDialogues(npc, null, "Well."); - stage = 10; - break; - case 10: - interpreter.sendDialogues(npc, null, "I... suppose spy work DOES involve a little hiding in", "corners."); - stage = 11; - break; - case 11: - interpreter.sendOptions("Select an Option", "Oh. I suppose I'll give it a go then.", "No, I'm not ready to do that."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "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."); - stage = 13; - break; - } - break; - case 13: - interpreter.sendDialogues(npc, null, "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."); - 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."); - stage = 17; - break; - case 17: - interpreter.sendDialogues(npc, null, "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!"); - 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."); - stage = 20; - break; - case 20: - interpreter.sendDialogues(player, null, "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."); - stage = 22; - break; - case 22: - interpreter.sendDialogues(player, null, "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."); - stage = 24; - break; - case 24: - interpreter.sendDialogues(npc, null, "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."); - 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."); - stage = 27; - break; - case 27: - interpreter.sendOptions("Select an Option", "Ok, I'll do my best.", "No, I'm not ready to do that."); - stage = 28; - break; - case 28: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Ok, I'll do my best."); - stage = 31; - break; - case 2: - interpreter.sendDialogues(player, null, "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."); - 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."); - stage = 32; - break; - case 32: - if (player.getInventory().add(BlackKnightsFortress.DOSSIER)) { - quest.start(player); - } else { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - } - end(); - break; - } - break; - default: - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 608 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/demonslayer/DemonSlayerPlugin.java b/Server/src/main/java/core/game/content/quest/free/demonslayer/DemonSlayerPlugin.java deleted file mode 100644 index 8405a488a..000000000 --- a/Server/src/main/java/core/game/content/quest/free/demonslayer/DemonSlayerPlugin.java +++ /dev/null @@ -1,117 +0,0 @@ -package core.game.content.quest.free.demonslayer; - -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -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.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; - -/** - * Represents the demon slayer plugin used to handle relative node interactions. - * @author 'Vexia - * @date 3/1/14 - */ -public final class DemonSlayerPlugin extends OptionHandler { - - /** - * Represents the object id of the drain. - */ - public static final int DRAIN_ID = 17424; - - /** - * Represents the location of the lumbridge sewers. - */ - private static final Location SEWER_LOCATION = new Location(3237, 9858, 0); - - @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); - NPCDefinition.forId(DemonSlayerCutscene.WEAKENED_DELRITH).getHandlers().put("option:banish", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Demon Slayer"); - final int id = node instanceof Scenery ? ((Scenery) node).getId() : node instanceof Item ? ((Item) node).getId() : ((NPC) node).getId(); - switch (id) { - case 880: - player.getDialogueInterpreter().open(8427322); - break; - case 879: - if (!player.getEquipment().containsItem(DemonSlayer.SILVERLIGHT)) { - player.getPacketDispatch().sendMessage("I'd better wield Silverlight first."); - return true; - } - player.face(((NPC) node)); - player.getPulseManager().clear("interaction:attack:" + node.hashCode()); - player.getProperties().getCombatPulse().attack(node); - return true; - case DRAIN_ID: - if (quest.getStage(player) == 20) { - player.getDialogueInterpreter().open(883, 883, "key"); - return true; - } else { - 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); - 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."); - } else { - if (player.getInventory().freeSlots() == 0) { - player.getPacketDispatch().sendMessage("Not enough inventory space."); - return true; - } - } - break; - } - return true; - } - - @Override - public Location getDestination(final Node node, final Node n) { - if (n instanceof NPC) { - return n.getLocation().transform(2, 0, 0); - } - if (n.getId() == DRAIN_ID) { - return Location.create(3226, 3496, 0); - } - return null; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/free/demonslayer/TraibornDialogue.java b/Server/src/main/java/core/game/content/quest/free/demonslayer/TraibornDialogue.java deleted file mode 100644 index db54f8923..000000000 --- a/Server/src/main/java/core/game/content/quest/free/demonslayer/TraibornDialogue.java +++ /dev/null @@ -1,392 +0,0 @@ -package core.game.content.quest.free.demonslayer; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; - -/** - * Represents the dialogue used to handle the Traiborn NPC. - * @author 'Vexia - * @date 3/1/14 - */ -public class TraibornDialogue extends DialoguePlugin { - - /** - * Represents the bones item. - */ - private static final Item[] BONES = {new Item(526, 25), new Item(2530, 25)}; - - /** - * Represents the animation of giving the key. - */ - private static final Animation ANIMATION = new Animation(536); - - /** - * Represents the quest instance. - */ - private Quest quest; - - /** - * Constructs a new {@code TraibornDialogue} {@code Object}. - */ - public TraibornDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TraibornDialogue} {@code Object}. - * @param player the player. - */ - public TraibornDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TraibornDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Demon Slayer"); - switch (quest.getStage(player)) { - case 20: - if (player.getAttribute("demon-slayer:traiborn", false)) { - npc("How are you doing finding bones?"); - stage = 380; - } else { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ello young thingummywut."); - stage = 0; - } - break; - default: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ello young thingummywut."); - stage = 0; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { - case 20: - switch (stage) { - case 0: - if (player.getInventory().containsItem(DemonSlayer.THIRD_KEY) && player.getBank().containsItem(DemonSlayer.THIRD_KEY)) { - options("What's a thingummywut?", "Teach me to be a mighty powerful wizard."); - stage = 1; - } else { - options("What's a thingummywut?", "Teach me to be a mighty powerful wizard.", "I need to get a key given to you by Sir Prysin."); - stage = 1; - } - break; - case 1: - switch (buttonId) { - case 1: - case 2: - handleDefault(buttonId); - break; - case 3: - player("I need to get a key given to you by Sir Prysin."); - stage = 300; - break; - } - break; - case 300: - npc("Sir Prysin? Who's that? What would I want his key", "for?"); - stage = 301; - break; - case 301: - player("Well, have you got any keys knocking around?"); - stage = 302; - break; - case 302: - npc("Now you come to mention it, yes I do have a key. It's", "in my special closet of valuable stuff. Now how do I get", "into that?"); - stage = 303; - break; - case 303: - npc("I sealed it using one of my magic rituals. So it would", "makes sense that another ritual would open it again."); - stage = 304; - break; - case 304: - player("So do you know what ritual to use?"); - stage = 305; - break; - case 305: - npc("Let me think a second."); - stage = 306; - break; - case 306: - npc("Yes a simple drazier style ritual should suffice. Hmm,", "main problem with that is I'll need 25 sets of bones.", "Now where am I going to get hold of something like", "that?"); - stage = 307; - break; - case 307: - options("Hmm, that's too bad. I really need that key.", "I'll get the bones for you."); - stage = 308; - break; - case 308: - switch (buttonId) { - case 1: - player("Hmm, that's too bad. I really need that key."); - stage = 330; - break; - case 2: - player("I'll get the bones for you."); - stage = 340; - break; - } - break; - case 330: - npc("Ah well, sorry I couldn't be any more help."); - stage = 331; - break; - case 331: - end(); - break; - case 340: - npc("Ooh that would be very good of you."); - stage = 341; - break; - case 341: - player("Ok I'll speak to you when I've got some bones."); - stage = 342; - break; - case 342: - player.setAttribute("/save:demon-slayer:traiborn", true); - end(); - break; - case 380: - if (player.getInventory().containsItem(BONES[0]) || player.getInventory().containsItem(BONES[1])) { - player("I have some bones."); - stage = 382; - } else { - player("I don't have all the bones yet."); - stage = 381; - } - break; - case 382: - npc("Give 'em here then."); - stage = 383; - break; - case 383: - interpreter.sendDialogue("You give Traiborn 25 sets of bones."); - stage = 384; - break; - case 384: - npc("Hurrah! That's all 25 sets of bones."); - stage = 385; - break; - case 385: - npc.animate(new Animation(4602)); - npc("Wings of dark and colour too,", "Spreading in the morning dew;", "Locked away I have a key;", "Return it now, please, unto me."); - stage = 386; - break; - case 386: - final Scenery object = new Scenery(17434, Location.create(3113, 3161, 1), 11, 1); - SceneryBuilder.add(object); - npc.faceLocation(object.getLocation()); - npc.animate(ANIMATION); - if (!player.getInventory().containsItem(BONES[0]) && !player.getInventory().containsItem(BONES[1])) { - end(); - return true; - } - if (player.getInventory().remove(BONES[0]) || player.getInventory().remove(BONES[1])) { - player.removeAttribute("demon-slayer:traiborn"); - player.getInventory().add(DemonSlayer.THIRD_KEY); - interpreter.sendItemMessage(DemonSlayer.THIRD_KEY.getId(), "Traiborn hands you a key."); - stage = 387; - } - GameWorld.getPulser().submit(new Pulse(1) { - int counter = 0; - - @Override - public boolean pulse() { - switch (counter++) { - case 5: - npc.face(player); - break; - case 7: - SceneryBuilder.remove(object); - return true; - } - return false; - } - }); - break; - case 387: - player("Thank you very much."); - stage = 388; - break; - case 388: - npc("Not a problem for a friend of Sir What's-his-face."); - stage = 389; - break; - case 389: - end(); - break; - case 381: - end(); - break; - } - break; - case 0: - switch (stage) { - default: - handleDefault(buttonId); - break; - } - break; - } - return true; - } - - /** - * Method used to handle the default button id. - * @param buttonId the button id. - */ - private final void handleDefault(int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "What's a thingummywut?", "Teach me to be a mighty and powerful wizard."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's a thingummywut?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Teach me to be a mighty and powerful wizard."); - stage = 20; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A thingummywut? Where? Where?"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Those pesky thingummywuts. They get everywhere.", "They leave a terrible mess too."); - stage = 12; - break; - case 12: - interpreter.sendOptions("Select an Option", "Err you just called me a thingummywut.", "Tell me what they look like and I'll mask 'em."); - stage = 13; - break; - case 13: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err you just called me thingummywut."); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me what they look like and I'll mash 'em."); - stage = 120; - break; - } - break; - case 120: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't be ridiculous. No-one has ever seen one."); - stage = 121; - break; - case 121: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "They're invisible, or a myth, or a figment of my", "imagination. Can't remember which right now."); - stage = 122; - break; - case 122: - end(); - break; - case 100: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're a thingummywut? I've never seen one up close", "before. They said I was mad!"); - stage = 101; - break; - case 101: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Now you are my proof! There ARE thingummywuts in", "this tower. Now where can I find a cage big enough to", "keep you?"); - stage = 102; - break; - case 102: - interpreter.sendOptions("Select an Option", "Err I'd better be off really.", "They're right, you are mad."); - stage = 103; - break; - case 103: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err I'd better be off really."); - stage = 110; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "They're right, you are mad."); - stage = 130; - break; - } - break; - case 130: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That's a pity. I thought maybe they were winding me", "up."); - stage = 131; - break; - case 131: - end(); - break; - case 110: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh ok, have a good time, and watch out for sheep!", "They're more cunning than they look."); - stage = 111; - break; - case 111: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Wizard eh? You don't want any truck with that sort.", "They're not to be trusted. That's what I've heard", "anyways."); - stage = 21; - break; - case 21: - interpreter.sendOptions("Select an Option", "So aren't you a wizard?", "Oh I'd better stop talking to you then."); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So aren't you a wizard?"); - stage = 40; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh I'd better stop talking to you then."); - stage = 60; - break; - } - break; - case 60: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Cheerio then. It was nice chatting to you."); - stage = 61; - break; - case 61: - end(); - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How dare you? Of course I'm a wizard. Now don't be", "so cheeky or I'll turn you into a frog."); - stage = 41; - break; - case 41: - end(); - break; - } - } - - @Override - public int[] getIds() { - return new int[] { 881 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/DSChestDialogue.java b/Server/src/main/java/core/game/content/quest/free/dragonslayer/DSChestDialogue.java deleted file mode 100644 index bcd1b56f7..000000000 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/DSChestDialogue.java +++ /dev/null @@ -1,76 +0,0 @@ -package core.game.content.quest.free.dragonslayer; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItemManager; - -/** - * Represents the dialogue plugin used for the dragon slayer chest. - * @author 'Vexia - * @version 1.0 - */ -public final class DSChestDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code DSChestDialogue} {@code Object}. - */ - public DSChestDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DSChestDialogue} {@code Object}. - * @param player the player. - */ - public DSChestDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DSChestDialogue(player); - } - - @Override - public boolean open(Object... args) { - interpreter.sendDialogue("As you open the chest, you notice an inscription on the lid:"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogue("Here I rest the map to my beloved home. To whoever finds it, I beg", "of you, let it be. I was honour-bound not to destroy the map piece,", "but I have used all my magical skill to keep it from being recovered."); - 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."); - stage = 2; - break; - case 2: - interpreter.sendDialogue("I cannot stop you from taking this map piece now, but think on this:", "if you can slay the Dragon of Crandor, you are a greater hero than", "my land ever produced. There is no shame in backing out now."); - stage = 3; - break; - case 3: - interpreter.sendItemMessage(DragonSlayer.MAGIC_PIECE.getId(), "You find a map piece in the chest."); - stage = 4; - break; - case 4: - if (!player.getInventory().add(DragonSlayer.MAGIC_PIECE)) { - GroundItemManager.create(DragonSlayer.MAGIC_PIECE, player); - } - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3802875 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/DragonSlayer.java b/Server/src/main/java/core/game/content/quest/free/dragonslayer/DragonSlayer.java deleted file mode 100644 index 439f10329..000000000 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/DragonSlayer.java +++ /dev/null @@ -1,352 +0,0 @@ -package core.game.content.quest.free.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 core.game.node.entity.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 rs09.game.content.dialogue.DukeHoracioDialogue; -import rs09.game.content.quest.free.dragonslayer.NedDialogue; -import rs09.game.world.GameWorld; -import rs09.plugin.PluginManager; - -/** - * 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) { - PluginManager.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/java/core/game/content/quest/free/dragonslayer/ElvargNPC.java b/Server/src/main/java/core/game/content/quest/free/dragonslayer/ElvargNPC.java deleted file mode 100644 index c8f108ff2..000000000 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/ElvargNPC.java +++ /dev/null @@ -1,325 +0,0 @@ -package core.game.content.quest.free.dragonslayer; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import rs09.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.combat.equipment.FireType; -import core.game.node.entity.impl.Projectile; -import core.game.node.entity.impl.Animator.Priority; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItemManager; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import rs09.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.game.world.update.flag.context.Graphics; -import core.tools.RandomFunction; - -/** - * Represents the elvarg npc. - * @author 'Vexia - * @version 1.0 - */ -public final class ElvargNPC extends AbstractNPC { - - /** - * 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 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 MeldarMadNPC} {@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 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 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 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; - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if (!(entity instanceof Player)) { - return super.isAttackable(entity, style); - } - final Player player = (Player) entity; - if (player.getQuestRepository().getQuest("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) { - player.getPacketDispatch().sendMessage("You have already slain Elvarg."); - return false; - } - return super.isAttackable(entity, style); - } - - @Override - public int getDragonfireProtection(boolean fire) { - return 0x2 | 0x4 | 0x8; - } - - /** - * Handles the elvarg combat swing handler. - * @author Emperor - */ - static class ElvargCombatSwingHandler extends CombatSwingHandler { - - /** - * The style. - */ - private CombatStyle style = CombatStyle.RANGE; - - /** - * 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; - - /** - * Constructs a new {@code KBDCombatSwingHandler} {@Code Object}. - * The combat style. - */ - public ElvargCombatSwingHandler() { - super(CombatStyle.RANGE); - } - - @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); - } - - @Override - public int calculateAccuracy(Entity entity) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateAccuracy(entity); - } - return 600; - } - - @Override - public int calculateDefence(Entity victim, Entity attacker) { - return style.getSwingHandler().calculateDefence(victim, attacker); - } - - @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 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 ArmourSet getArmourSet(Entity e) { - return style.getSwingHandler().getArmourSet(e); - } - - @Override - public double getSetMultiplier(Entity e, int skillId) { - return style.getSwingHandler().getSetMultiplier(e, skillId); - } - - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - style.getSwingHandler().impact(entity, victim, state); - } - - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - style = CombatStyle.RANGE; - int hit = 0; - int ticks = 1; - if (victim.getCenterLocation().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 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) { - 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)); - } - } - - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/dragonslayer/WormbrainNPC.java b/Server/src/main/java/core/game/content/quest/free/dragonslayer/WormbrainNPC.java deleted file mode 100644 index 104e8c013..000000000 --- a/Server/src/main/java/core/game/content/quest/free/dragonslayer/WormbrainNPC.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.content.quest.free.dragonslayer; - -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.node.item.GroundItemManager; -import core.game.world.map.Location; - -/** - * Represents the worm brain npc. - * @author 'Vexia - * @versin 1.0 - */ -public final class WormbrainNPC extends AbstractNPC { - - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 745 }; - - /** - * Constructs a new {@code WormBrainPlugin} {@code Object}. - */ - public WormbrainNPC() { - super(0, null); - } - - /** - * Constructs a new {@code AlKharidWarriorPlugin} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - private WormbrainNPC(int id, Location location) { - super(id, location); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new WormbrainNPC(id, location); - } - - @Override - 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)) { - GroundItemManager.create(DragonSlayer.WORMBRAIN_PIECE, getLocation(), ((Player) killer)); - ((Player) killer).getPacketDispatch().sendMessage("Wormbrain drops a map piece on the floor."); - } - } - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if (entity instanceof Player) { - final Player player = (Player) entity; - if (player.getQuestRepository().getQuest("Dragon Slayer").getStage(player) != 20) { - player.getPacketDispatch().sendMessage("The goblin is already in prison. You have no reason to attack him."); - return false; - } - } - return super.isAttackable(entity, style); - } - - @Override - public int[] getIds() { - return ID; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/CuratorHaigHalenDialogue.java b/Server/src/main/java/core/game/content/quest/free/shieldofarrav/CuratorHaigHalenDialogue.java deleted file mode 100644 index 8f9b52b09..000000000 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/CuratorHaigHalenDialogue.java +++ /dev/null @@ -1,146 +0,0 @@ -package core.game.content.quest.free.shieldofarrav; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; - -/** - * 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) { - if (player.getQuestRepository().getQuest("Shield of Arrav").getStage(player) == 70 && player.getInventory().containsItem(ShieldofArrav.PHOENIX_SHIELD) || player.getInventory().containsItem(ShieldofArrav.BLACKARM_SHIELD)) { - switch (stage) { - case 0: - player("I have half the shield of Arrav here. Can I get a", "reward?"); - stage = 1; - break; - case 1: - npc("The Shield of Arrav! Goodness, the Museum has been", "searching for that for years! The late King Roald II", "offered a reward for it years ago!"); - stage = 2; - break; - case 2: - player("Well, I'm here to claim it."); - stage = 3; - break; - case 3: - npc("Let me have a look at it first."); - stage = 4; - break; - case 4: - interpreter.sendItemMessage(ShieldofArrav.getShield(player), "The curator peers at the shield."); - stage = 5; - break; - case 5: - npc("This is incredible!"); - stage = 6; - break; - case 6: - npc("That shield has been missing for over twenty-five years!"); - stage = 7; - break; - case 7: - npc("Leave the shield here with me and I'll write you out a", "certificate saying that you have returned the shield, so", "that you can claim your reward from the King."); - stage = 8; - break; - case 8: - player("Can I have two certificates please?"); - stage = 9; - break; - case 9: - npc("Yes, certainly. Please hand over the shield."); - stage = 10; - break; - case 10: - interpreter.sendItemMessage(ShieldofArrav.getShield(player), "You hand over the shield half."); - stage = 11; - break; - case 11: - final Item shield = ShieldofArrav.getShield(player); - final Item certificate = shield == ShieldofArrav.BLACKARM_SHIELD ? ShieldofArrav.BLACKARM_CERTIFICATE : ShieldofArrav.PHOENIX_CERTIFICATE; - if (player.getInventory().remove(shield)) { - player.getInventory().add(certificate); - interpreter.sendItemMessage(certificate, "The curator writes out two half-certificates."); - stage = 12; - } - break; - } - return true; - } else { - switch (stage) { - case 12: - npc("Of course you won't actually be able to claim the", "reward with only half the reward certificate..."); - stage = 13; - break; - case 13: - player("What? I went through a lot of trouble to get that shield", "piece and now you tell me it was for nothing? That's", "not very fair!"); - stage = 14; - break; - case 14: - npc("Well, if you were to get me the other half of the shield,", "I could give you the other half of the reward certificate.", "It's rumoured to be in the possession of the infamous", "Blackarm Gang, beyond that I can't help you."); - stage = 15; - break; - case 15: - player("Okay, I'll see what I can do."); - stage = 16; - break; - case 16: - end(); - break; - } - } - switch (stage) { - case 0: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 646 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/JohnnyBeardNPC.java b/Server/src/main/java/core/game/content/quest/free/shieldofarrav/JohnnyBeardNPC.java deleted file mode 100644 index b298a0a5c..000000000 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/JohnnyBeardNPC.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.content.quest.free.shieldofarrav; - -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.player.link.quest.Quest; -import core.game.node.item.GroundItemManager; -import core.game.world.map.Location; - -/** - * Represents the npc to handle Johnny the beard npc. - * @author 'Vexia - * @version 1.0 - */ -public final class JohnnyBeardNPC extends AbstractNPC { - - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 645 }; - - /** - * Constructs a new {@code JohnnyBeardNPC} {@code Object}. - */ - public JohnnyBeardNPC() { - super(0, null); - } - - /** - * Constructs a new {@code JohnnyBeardNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - private JohnnyBeardNPC(int id, Location location) { - super(id, location); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new JohnnyBeardNPC(id, location); - } - - @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); - } - } - - @Override - public int[] getIds() { - return ID; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldofArravBook.java b/Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldofArravBook.java deleted file mode 100644 index 42200330d..000000000 --- a/Server/src/main/java/core/game/content/quest/free/shieldofarrav/ShieldofArravBook.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.content.quest.free.shieldofarrav; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.book.Book; -import core.game.content.dialogue.book.BookLine; -import core.game.content.dialogue.book.Page; -import core.game.content.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/java/core/game/content/quest/free/therestlessghost/FatherUhrneyDialogue.java b/Server/src/main/java/core/game/content/quest/free/therestlessghost/FatherUhrneyDialogue.java deleted file mode 100644 index a4c47507b..000000000 --- a/Server/src/main/java/core/game/content/quest/free/therestlessghost/FatherUhrneyDialogue.java +++ /dev/null @@ -1,283 +0,0 @@ -package core.game.content.quest.free.therestlessghost; - -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 core.game.content.dialogue.DialoguePlugin; - -/** - * Represents the father urgney dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FatherUhrneyDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code FatherUhrneyDialogue} {@code Object}. - */ - public FatherUhrneyDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FatherUhrneyDialogue} {@code Object}. - * @param player the player. - */ - public FatherUhrneyDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FatherUhrneyDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Go away! I'm meditating!"); - stage = 0; - return true; - } - - @Override - 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."); - 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."); - 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."); - stage = 514; - } - break; - case 500: - switch (buttonId) { - case 1: - player("Well, that's friendly."); - 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; - } - break; - case 501: - npc("I suppose I'd better talk to you then. What problems", "has he got himself into this time?"); - stage = 502; - break; - case 502: - player("He's got a ghost haunting his graveyard."); - stage = 503; - break; - case 503: - npc("Oh, the silly fool."); - stage = 504; - break; - case 504: - npc("I leave town for just five months, and ALREADY he", "can't manage."); - stage = 505; - break; - case 505: - npc("(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."); - stage = 507; - break; - case 507: - npc("Tell you what I can do though; take this amulet."); - stage = 508; - break; - case 508: - if (player.getInventory().freeSlots() == 0) { - end(); - player.getPacketDispatch().sendMessage("You don't have enough inventory space to accept this amulet."); - break; - } - 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.getGameAttributes().setAttribute("/save:restless-ghost:urhney", true); - stage = 509; - break; - case 509: - npc("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."); - 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."); - stage = 512; - break; - case 512: - player("Thank you. I'll give it a try!"); - stage = 513; - break; - case 513: - end(); - break; - case 514: - switch (buttonId) { - case 1: - player("Well, that's friendly."); - stage = 10; - break; - case 2: - player("I've come to repossess your house."); - stage = 20; - break; - case 3: - player("I've lost the Amulet of Ghostpeak."); - stage = 515; - break; - } - break; - case 515: - if (player.getInventory().contains(552, 1) || player.getEquipment().contains(552, 1)) { - interpreter.sendDialogue("Father Urhney sighs."); - stage = 516; - break; - } - if (player.getBank().contains(552, 1)) { - interpreter.sendDialogue("Father Urhney sighs."); - stage = 517; - break; - } - interpreter.sendDialogue("Father Urhney sighs."); - stage = 519; - break; - case 516: - npc("What are you talking about? I can see you've got it", "with you!"); - stage = 518; - break; - case 517: - npc("What are you talking about? I can see you've got it", "in your bank!"); - 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."); - stage = 520; - break; - case 520: - player.getInventory().add(new Item(552)); - interpreter.sendItemMessage(552, "Father Urhney hands you an amulet."); - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 12); - stage = 521; - break; - case 521: - npc("Be more careful this time."); - stage = 522; - break; - case 522: - player("Ok, I'll try to be."); - stage = 523; - break; - case 523: - end(); - break; - case 1: - switch (buttonId) { - case 1: - player("Well, that's friendly."); - stage = 10; - break; - case 2: - player("I've come to repossess your house."); - stage = 20; - break; - - } - break; - case 10: - npc("I SAID go AWAY."); - stage = 11; - break; - case 11: - player("Ok, ok... sheesh, what a grouch."); - stage = 12; - break; - case 12: - end(); - break; - case 20: - npc("Under what grounds???"); - stage = 21; - break; - case 21: - options("Repeated failure on mortgage repayments.", "I don't know, I just wanted this house."); - stage = 22; - break; - case 22: - switch (buttonId) { - case 1: - player("Repeated failure on mortgage repayments."); - stage = 100; - break; - case 2: - player("I don't know. I just wanted this house..."); - stage = 200; - break; - - } - break; - case 100: - npc("What?"); - stage = 101; - break; - case 101: - npc("I don't have a mortgage! I built this house."); - stage = 102; - break; - case 102: - player("Sorry. I mus thave got the wrong address. All the", "houses look the same around here."); - stage = 103; - break; - case 103: - npc("What? What houses? What ARE you talking about???"); - stage = 104; - break; - case 104: - player("Never mind."); - stage = 105; - break; - case 105: - end(); - break; - case 200: - npc("Oh... go away and stop wasting my time!"); - stage = 201; - break; - case 201: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 458 }; - } -} diff --git a/Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhostSkull.java b/Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhostSkull.java deleted file mode 100644 index 325e30e97..000000000 --- a/Server/src/main/java/core/game/content/quest/free/therestlessghost/RestlessGhostSkull.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.content.quest.free.therestlessghost; - -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; - -/** - * Represents the plugin used to add the skull to the cofin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class RestlessGhostSkull extends UseWithHandler { - - /** - * Constructs a new {@code RestlessGhostSkull} {@code Object}. - */ - public RestlessGhostSkull() { - super(964); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2145, OBJECT_TYPE, this); - addHandler(15052, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Scenery object = (Scenery) event.getUsedWith(); - if (object.getId() == 2145) { - event.getPlayer().getDialogueInterpreter().sendDialogue("Maybe I should open it first."); - return true; - } - if (event.getPlayer().getInventory().remove(new Item(964, 24))) { - event.getPlayer().getPacketDispatch().sendMessage("You put the skull in the coffin."); - event.getPlayer().getQuestRepository().getQuest(RestlessGhost.NAME).finish(event.getPlayer()); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/DruidicRitual.java b/Server/src/main/java/core/game/content/quest/members/DruidicRitual.java deleted file mode 100644 index bf45d21fb..000000000 --- a/Server/src/main/java/core/game/content/quest/members/DruidicRitual.java +++ /dev/null @@ -1,82 +0,0 @@ -package core.game.content.quest.members; - -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/java/core/game/content/quest/members/WolfWhistle.java b/Server/src/main/java/core/game/content/quest/members/WolfWhistle.java deleted file mode 100644 index 3baab8b77..000000000 --- a/Server/src/main/java/core/game/content/quest/members/WolfWhistle.java +++ /dev/null @@ -1,97 +0,0 @@ -package core.game.content.quest.members; - -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.Item; - -/** - * Represents the demon slayer quest. - * @author 'Vexia - */ -@Initializable -public class WolfWhistle extends Quest { - - /** - * The wolf bones item. - */ - public static final Item WOLF_BONES = new Item(2859, 2); - - /** - * Constructs a new {@code WolfWhistle} {@code Object}. - */ - public WolfWhistle() { - super("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; - } - } - - @Override - public void finish(Player player) { - super.finish(player); - player.getPacketDispatch().sendString("1 Quest Points", 277, 8+ 2); - player.getPacketDispatch().sendString("276 Summoning XP", 277, 9+ 2); - player.getPacketDispatch().sendString("Access to the Summoning", 277, 10+ 2); - player.getPacketDispatch().sendString("skill.", 277, 11+ 2); - player.getPacketDispatch().sendString("275 gold charms.", 277, 12+ 2); - player.getPacketDispatch().sendItemZoomOnInterface(12047, 230, 277, 3+ 2); - player.getSkills().addExperience(Skills.SUMMONING, 276); - player.getInventory().add(new Item(12158, 275), player); - player.removeAttribute("searched-body"); - player.getQuestRepository().syncronizeTab(player); - player.getInterfaceManager().openInfoBars(); - } - - @Override - public int[] getConfig(Player player, int stage) { - int val = player.getConfigManager().get(1178); - boolean open = val >= 4096; - boolean closed = val == 2048; - if (stage == 100) { - if (val == 5 || val == 0) { - return new int[] { 1178, 32989 }; - } else if (val == 4101) { - return new int[] { 1178, 28893 }; - } - return new int[] { 1178, val }; - } - return new int[] { 1178, stage > 0 ? 5 + (open ? 4096 : 0) : stage >= 100 ? !closed ? 28893 : 32989 : 0 }; - } - - @Override - public Quest newInstance(Object object) { - return this; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AliceHusbandDialogue.java b/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AliceHusbandDialogue.java deleted file mode 100644 index d2efe20e5..000000000 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AliceHusbandDialogue.java +++ /dev/null @@ -1,734 +0,0 @@ -package core.game.content.quest.members.animalmagnetism; - -import core.game.content.activity.ActivityManager; -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.ImpactHandler.HitsplatType; -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.quest.Quest; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.build.DynamicRegion; -import core.game.world.map.path.Pathfinder; -import core.game.world.update.flag.context.Animation; -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 rs09.plugin.PluginManager; - -/** - * Handles the husband of alice's npc dialogue. - * @author Vexia - */ -public final class AliceHusbandDialogue extends DialoguePlugin { - - /** - * The quest. - */ - private Quest quest; - - /** - * The chicken catch scene. - */ - private ChickenCatchScene scene; - - /** - * Constructs a new {@code AliceHusbandDialogue} {@code Object}. - */ - public AliceHusbandDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code AliceHusbandDialogue} {@code Object}. - * @param player the player. - */ - public AliceHusbandDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AliceHusbandDialogue(player); - } - - @Override - public void init() { - super.init(); - PluginManager.definePlugin(new ChickenCatchScene()); - } - - @Override - public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest(AnimalMagnetism.NAME); - switch (quest.getStage(player)) { - case 0: - npc("Hi, I don't feel like talking."); - break; - case 10: - player("Your animals don't look too healthy."); - break; - case 11: - npc("'Ave you talked to the wife for me?"); - break; - case 12: - player("Your wife says she needs the family cash and wants to", "know what you did with it."); - stage++; - break; - case 13: - npc("Any luck wiv me wife?"); - break; - case 14: - case 15: - npc("'Ave you talked to 'er?"); - break; - case 16: - case 17: - case 18: - player("I talked to your wife and thought that if you had a", "special amulet, you could speak to her and sort out the", "bank situation without me being involved."); - break; - case 19: - npc("Ahh, many thanks. Now what was it you were wanting", "again?"); - break; - default: - npc("Hello, how can I help you? I'm sellin' if you have ecto-", "tokens."); - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { - default: - switch (stage) { - case 0: - options("Could I buy those chickens now, then?", "Your animals don't look too healthy.", "I'm okay, thank you.", "Where can I get these ecto-tokens?"); - stage++; - break; - case 1: - switch (buttonId) { - case 1: - player("Could I buy those chickens now, then?"); - stage = 10; - break; - case 2: - player("Your animals don't look too healthy."); - stage = 20; - break; - case 3: - player("I'm okay, thank you."); - stage = 30; - break; - case 4: - player("Where can I get these ecto-tokens?"); - stage = 40; - break; - } - break; - case 10: - npc("I can hand over a chicken if you give me 10 of them", "ecto-token thingies per bird."); - stage++; - break; - case 11: - options("Could I buy 1 chicken now?", "Could I buy 2 chickens now?", "Your animals don't look too healthy; I'll buy elsewhere."); - stage++; - break; - case 12: - switch (buttonId) { - case 1: - player("Could I buy 1 chicken now?"); - stage = 14; - break; - case 2: - player("Could I buy 2 chickens now?"); - stage = 15; - break; - case 3: - player("Your animals don't look too healthy; I'll buy elsewhere."); - stage++; - break; - } - break; - case 13: - end(); - break; - case 14: - case 15: - buy(stage == 14 ? 1 : 2); - stage = 16; - break; - case 16: - end(); - break; - case 20: - npc("It's that fountain thingy in the temple to the east. It's", "turned them all into zombies."); - stage++; - break; - case 21: - player("What use are zombie animals?"); - stage++; - break; - case 22: - npc("None at all, mate, except that those worshippers at that", "temple keep comin' and killin' em all for their bones.", "Don't ask me why."); - stage++; - break; - case 23: - player("But you're a ghost - surely you know", "something about it."); - stage++; - break; - case 24: - npc("I don't know nuthin' about nuthin'. Oim a simple ghost", "with simple needs. All I know is, years ago, that temple", "started glowing green and, a few months later, I woke", "up dead. That's all there is to it."); - stage++; - break; - case 25: - end(); - break; - case 30: - end(); - break; - case 40: - npc("The ghosts I talk to say that the tokens have something", "to do with the tower just east of here. If you need to", "collect some I'd try there."); - stage++; - break; - case 41: - end(); - break; - } - break; - case 0: - switch (stage) { - default: - end(); - break; - } - break; - case 10: - switch (stage) { - case 0: - npc("It's that fountain thingy in the temple to the east. It's", "turned them all into zombies."); - stage++; - break; - case 1: - player("What use are zombie animals?"); - stage++; - break; - case 2: - npc("None at all, mate, except that those worshippers at that", "temple keep comin' and killin' em all for their bones.", "Don't ask me why."); - stage++; - break; - case 3: - player("But you're a ghost - surely you know", "something about it."); - stage++; - break; - case 4: - npc("I don't know nuthin' about nuthin'. Oim a simple ghost", "with simple needs. All I know is, years ago, that temple", "started glowing green and, a few months later, I woke", "up dead. That's all there is to it."); - stage++; - break; - case 5: - npc("I do miss the wife though; tell 'er I still loves her."); - stage++; - break; - case 6: - player("Would I be able to buy some of your chickens?"); - stage++; - break; - case 7: - npc("Talk to my wife and I'll think about it."); - stage++; - break; - case 8: - if (quest.getStage(player) == 10) { - quest.setStage(player, 11); - } - end(); - break; - } - break; - case 11: - switch (stage) { - case 0: - player("Not yet; I've been distracted by the thought of undead", "cow milk."); - stage++; - break; - case 1: - end(); - break; - } - break; - case 12: - switch (stage) { - case 1: - npc("Tell 'er I spent it on cheap spirits, har har."); - stage++; - break; - case 2: - player("Your sense of humour died too, it seems..."); - stage++; - break; - case 3: - npc("Hah, just trying to lift your spirits."); - stage++; - break; - case 4: - player("I rest my case."); - stage++; - break; - case 5: - npc("Suit yerself, stick-in-the-mud. Anyway, Oim not one o'", "them yokels. Tell 'er I putted the cash in the bank like", "she always told me to."); - stage++; - break; - case 6: - npc("A warning to ya, too; annoy her and I'll haunt ya till", "yer hair turns white."); - stage++; - break; - case 7: - quest.setStage(player, 13); - end(); - break; - } - break; - case 13: - switch (stage) { - case 0: - player("Nothing new, no."); - stage++; - break; - case 1: - end(); - break; - } - break; - case 14: - switch (stage) { - case 0: - player("You may not believe me, but she wants me to find", "your bank pass now."); - stage++; - break; - case 1: - npc("Maybe she said that, maybe she didn't. I think you're", "just after me savings. Tell 'er that no one but a fool", "gives away their pass numbers."); - stage++; - break; - case 2: - npc("Go tell 'er now, if you're not a double-dealing' scammer,", "that is."); - stage++; - break; - case 3: - quest.setStage(player, 15); - end(); - break; - } - break; - case 15: - switch (stage) { - case 0: - player("Not since we last spoke."); - stage++; - break; - case 1: - end(); - break; - } - break; - case 16: - case 17: - case 18: - switch (stage) { - case 0: - npc("Arr, that makes far more sense than I was expecting", "from a muscle-head like you. My wife's a clever one."); - stage++; - break; - case 1: - if (player.getInventory().containsItem(AnimalMagnetism.CRONE_AMULET)) { - player("Well... oh, never mind. I'm desperate enough for those", "chickens to let that pass."); - stage += 2; - break; - } - player("Well...oh, never mind. I'm working on getting the", "amulet anyway."); - stage++; - break; - case 2: - end(); - break; - case 3: - npc("Give me that amulet, then, and we'll be seeing about", "your unnatural desire for chickens."); - stage++; - break; - case 4: - player("Okay, you need it more than I do, I suppose."); - stage++; - break; - case 5: - npc("Ta, mate."); - stage++; - break; - case 6: - player("Lucky we had such a brilliant idea."); - stage++; - break; - case 7: - if (player.getInventory().remove(AnimalMagnetism.CRONE_AMULET)) { - quest.setStage(player, 19); - end(); - } - break; - } - break; - case 19: - switch (stage) { - case 0: - player("I need a couple of your chickens."); - stage++; - break; - case 1: - npc("Chickens is tricksy, 'specially dead 'uns. I'll have to catch", "'em for ye."); - stage++; - break; - case 2: - player("They look preety pathetic, how hard can it be?"); - stage++; - break; - case 3: - npc("Stand back while I catches 'em, ya city slicker."); - stage++; - break; - case 4: - stage++; - close(); - ActivityManager.start(player, ChickenCatchScene.NAME, false, this); - break; - case 5: - close(); - player.face(scene.chicken); - scene.walk(scene.husband, 46, 9); - scene.walk(scene.chicken, 46, 9); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter; - - @Override - public boolean pulse() { - switch (++counter) { - case 3: - scene.walk(scene.husband, 53, 8); - scene.walk(scene.chicken, 52, 8); - break; - case 11: - scene.walk(scene.chicken, 40, 10); - scene.walk(scene.husband, 40, 10); - break; - case 22: - npc("Git 'ere, yer pesky bird!"); - stage++; - return true; - } - return false; - } - }); - break; - case 6: - stage++; - close(); - scene.husband.getAnimator().forceAnimation(new Animation(5377, Priority.HIGH)); - scene.walk(scene.husband, 51, 8); - scene.walk(scene.chicken, 45, 10); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter; - - @Override - public boolean pulse() { - switch (++counter) { - case 2: - scene.husband.getAnimator().forceAnimation(new Animation(5377, Priority.HIGH)); - break; - case 5: - scene.husband.getAnimator().forceAnimation(new Animation(5377, Priority.HIGH)); - break; - case 7: - scene.walk(scene.chicken, 47, 7); - break; - case 8: - scene.husband.animate(new Animation(5377, Priority.HIGH)); - break; - case 12: - scene.walk(scene.chicken, 47, 11); - break; - case 17: - npc("Where'd she go?"); - return true; - } - return false; - } - }); - break; - case 7: - close(); - stage++; - scene.allice.init(); - scene.cow.init(); - scene.cowKiller.init(); - scene.walk(scene.allice, 48, 8); - scene.walk(scene.cowKiller, 46, 9); - scene.walk(scene.cow, 46, 10); - scene.walk(scene.husband, 44, 10); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter; - - @Override - public boolean pulse() { - switch (++counter) { - case 2: - scene.walk(scene.chicken, 45, 9); - break; - case 8: - scene.allice.face(player); - scene.husband.face(player); - npc("Git orf my laaand!"); - return true; - } - return false; - } - }); - break; - case 8: - interpreter.sendDialogues(scene.allice, null, "You heard my husband: leave now!"); - stage++; - break; - case 9: - scene.allice.face(scene.cowKiller); - scene.cowKiller.face(scene.allice); - interpreter.sendDialogues(scene.cowKiller, null, "Always the same; I can never get these animals to", "myself."); - stage = 20; - break; - case 20: - scene.cowKiller.animate(Animation.create(2067)); - scene.cowKiller.faceTemporary(scene.cow, 1); - scene.cow.getImpactHandler().manualHit(scene.cowKiller, scene.cow.getSkills().getLifepoints(), HitsplatType.NORMAL); - interpreter.sendDialogues(scene.allice, null, "You killed Bessie!"); - stage++; - break; - case 21: - interpreter.sendDialogues(scene.cowKiller, null, "Buying cowhides and feathers - ahh, that chicken is", "next, feathers for me!"); - stage++; - break; - case 22: - close(); - stage++; - scene.allice.face(scene.husband); - scene.walk(scene.cowKiller, 46, 10); - GameWorld.getPulser().submit(new Pulse(1, player) { - int count; - - @Override - public boolean pulse() { - switch (++count) { - case 4: - scene.cowKiller.animate(Animation.create(832)); - break; - case 6: - scene.cowKiller.face(scene.chicken); - scene.chicken.sendChat("Woo woo!"); - break; - case 8: - scene.husband.face(scene.cowKiller); - scene.walk(scene.chicken, 45, 10); - break; - case 10: - scene.cowKiller.animate(Animation.create(2067)); - scene.chicken.getImpactHandler().manualHit(player, 0, HitsplatType.MISS); - scene.chicken.face(scene.husband); - break; - case 12: - interpreter.sendDialogues(5209, null, "Woo woo woo!"); - break; - } - return false; - } - - }); - break; - case 23: - close(); - stage++; - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter; - - @Override - public boolean pulse() { - switch (++counter) { - case 1: - scene.husband.animate(Animation.create(5377)); - break; - case 3: - scene.chicken.clear(); - break; - case 6: - player("Well, that's one way to catch a chicken, I suppose."); - return true; - } - return false; - } - }); - break; - case 24: - quest.setStage(player, 20); - close(); - scene.stop(true); - break; - } - break; - } - return true; - } - - /** - * Buys an undead chicken(s). - * @param amount the amount. - */ - private void buy(int amount) { - final Item tokens = new Item(4278, amount * 10); - if (!player.getInventory().containsItem(tokens)) { - npc("I'm not a charity here, ya know. Bad enough all you", "cow-killing folks are a'slaughterin' me beasts. Come back", "when ya have enough tokens."); - return; - } - if (player.getInventory().freeSlots() < amount) { - player("Sorry, I don't have enough inventory space."); - return; - } - if (player.getInventory().remove(tokens)) { - player.getInventory().add(new Item(10487, amount), player); - npc("Great! I'm laying away me tokens for some killer cows.", "That'll learn them bones rustlers."); - } - } - - /** - * Handles the cutscene used to catch a chicken. - * @author Vexia - */ - public static final class ChickenCatchScene extends CutscenePlugin { - - /** - * The name of the cutscene. - */ - public static final String NAME = "am:chicken"; - - /** - * The npcs used. - */ - public NPC chicken, husband, cowKiller, cow, allice; - - /** - * Constructs a new {@code ChickenCatchScene} {@code Object}. - */ - public ChickenCatchScene() { - super(NAME); - } - - /** - * Constructs a new {@code ChickenCatchScene} {@code Object}. - * @param player the player. - */ - public ChickenCatchScene(Player player) { - super(NAME); - this.player = player; - } - - @Override - public ActivityPlugin newInstance(Player p) throws Throwable { - return new ChickenCatchScene(p); - } - - @Override - public boolean start(final Player player, boolean login, Object... args) { - setNpcs(); - AliceHusbandDialogue dial = (AliceHusbandDialogue) args[0]; - dial.scene = this; - return super.start(player, login, args); - } - - @Override - public void open() { - super.open(); - player.getLocks().lock(); - Location loc = player.getLocation(); - int height = 480, xRot = -6, yRot = -28; - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.POSITION, loc.getX() + 2, loc.getY() + 3, height, 1, 100)); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.ROTATION, loc.getX() + xRot, loc.getY() + yRot, height, 1, 100)); - player.faceTemporary(chicken, 1); - player.getLocks().lockMovement(GameWorld.getTicks() + 1000000); - player.getDialogueInterpreter().sendDialogues(5202, null, "Here, chicky chicky!"); - } - - @Override - public boolean leave(final Entity e, boolean logout) { - chicken.clear(); - husband.clear(); - cowKiller.clear(); - allice.clear(); - cow.clear(); - return super.leave(e, logout); - } - - /** - * Sets the npc instances. - */ - private void setNpcs() { - chicken = NPC.create(1692, base.transform(44, 10, 0)); - husband = NPC.create(5205, base.transform(42, 10, 0)); - cow = NPC.create(5211, base.transform(42, 10, 0)); - cowKiller = NPC.create(5210, base.transform(51, 8, 0)); - allice = NPC.create(5212, base.transform(47, 5, 0)); - allice.setWalks(false); - cow.setWalks(false); - cowKiller.setWalks(false); - chicken.setWalks(false); - husband.setWalks(false); - husband.init(); - chicken.init(); - chicken.faceLocation(husband.getLocation()); - walk(husband, chicken.getLocation().getX() - 1, chicken.getLocation().getY()); - } - - /** - * Walks an npc. - * @param npc the npc. - */ - public void walk(NPC npc, int x, int y) { - Location loc = base.transform(x, y, 0); - npc.getWalkingQueue().reset(); - Pathfinder.find(npc, loc).walk(npc); - } - - @Override - public Location getStartLocation() { - return base.transform(46, 13, 0); - } - - @Override - public Location getSpawnLocation() { - return null; - } - - @Override - public void configure() { - region = DynamicRegion.create(14391); - setRegionBase(); - registerRegion(region.getId()); - } - - } - - @Override - public int[] getIds() { - return new int[] { 5202 }; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvasDevicePlugin.java b/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvasDevicePlugin.java deleted file mode 100644 index ef1adc6d8..000000000 --- a/Server/src/main/java/core/game/content/quest/members/animalmagnetism/AvasDevicePlugin.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.content.quest.members.animalmagnetism; - -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.Plugin; -import rs09.game.node.entity.state.newsys.states.AvaDeviceState; -import rs09.plugin.PluginManager; - -/** - * Handles the equippage event of an ava device. - * @author Vexia - */ -public final class AvasDevicePlugin implements Plugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - AnimalMagnetism.AVAS_ACCUMULATOR.getDefinition().getHandlers().put("equipment", this); - AnimalMagnetism.AVAS_ATTRACTOR.getDefinition().getHandlers().put("equipment", this); - PluginManager.definePlugin(new DisableDevicePlugin()); - 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": - AvaDeviceState state = (AvaDeviceState) player.registerState("avadevice"); - state.setDevice(item.getId()); - state.init(); - break; - case "unequip": - player.clearState("avadevice"); - if (args.length == 3) { - Item second = (Item) args[2]; - if (second.getId() == 10498 || second.getId() == 10499) { - AvaDeviceState newState = (AvaDeviceState) player.registerState("avadevice"); - newState.setDevice(second.getId()); - newState.init(); - break; - } - } - } - return true; - } - - /** - * Handles the disabling of Ava's devices -- they'll no longer randomly collect loot. - * @author Splinter - */ - public final class DisableDevicePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(10499).getHandlers().put("option:operate", this); - ItemDefinition.forId(10498).getHandlers().put("option:operate", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getGlobalData().setAvasDisabled(!player.getGlobalData().isAvasDisabled()); - player.sendMessage("Ava's device will "+(player.getGlobalData().isAvasDisabled() ? "no longer" : "now")+" randomly collect loot for you."); - return true; - } - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/CannonBallPlugin.java b/Server/src/main/java/core/game/content/quest/members/dwarfcannon/CannonBallPlugin.java deleted file mode 100644 index 96282a133..000000000 --- a/Server/src/main/java/core/game/content/quest/members/dwarfcannon/CannonBallPlugin.java +++ /dev/null @@ -1,138 +0,0 @@ -package core.game.content.quest.members.dwarfcannon; - -import org.rs09.consts.Items; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; -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; - -/** - * Handles the cannon ball making. - * @author Vexia - */ -public class CannonBallPlugin extends UseWithHandler { - - /** - * The furnaces. - */ - private static final int[] FURNACES = new int[] { 4304, 6189, 11010, 11666, 12100, 12809, 18497, 26814, 30021, 30510, 36956, 37651 }; - - /** - * Constructs a new {@Code CannonBallPlugin} {@Code Object} - */ - public CannonBallPlugin() { - super(Items.STEEL_BAR_2353); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : FURNACES) { - addHandler(id, OBJECT_TYPE, this); - } - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (!player.getQuestRepository().isComplete(DwarfCannon.NAME)) { - player.getDialogueInterpreter().sendDialogue("You need to complete the Dwarf Cannon quest in order to do this."); - return true; - } - SkillDialogueHandler dialogue = new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, event.getUsedItem()) { - - @Override - public void create(final int amount, int index) { - player.getPulseManager().run(new CannonBallPulse(player, event.getUsedItem(), amount)); - } - - @Override - public int getAll(int index) { - return player.getInventory().getAmount(event.getUsedItem()); - } - - }; - dialogue.open(); - return true; - } - - /** - * Handles the pulse used to create a cannon ball. - * @author Vexia - */ - public class CannonBallPulse extends SkillPulse { - - /** - * The amount to make. - */ - private int amount; - - /** - * The ticks. - */ - private int ticks; - - /** - * Constructs a new {@Code CannonBallPulse} {@Code Object} - * @param player the player. - * @param node the node. - * @param amount the amount. - */ - public CannonBallPulse(Player player, Item node, int amount) { - super(player, node); - this.amount = amount; - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(Skills.SMITHING) < 35) { - player.getDialogueInterpreter().sendDialogue("You need a Smithing level of at least 35 in order to do this."); - return false; - } - if (!player.getInventory().contains(Items.AMMO_MOULD_4, 1)) { - player.getDialogueInterpreter().sendDialogue("You need an ammo mould in order to make a cannon ball."); - return false; - } - if (!player.getInventory().containsItem(node)) { - stop(); - return false; - } - return true; - } - - @Override - public void animate() { - if (ticks == 0 || ticks % 6 == 0) { - player.sendMessage("You heat the steel bar into a liquid state."); - player.animate(Animation.create(827)); //Bone burying animation (2009 used the same animation as burying bones) - } else if (ticks % 3 == 0) { - player.sendMessage("You pour the molten metal into your cannonball mould."); - } - } - - @Override - public boolean reward() { - if (++ticks % 6 != 0) { - return false; - } - amount--; - if (player.getInventory().remove(node)) { - player.getInventory().add(new Item(Items.CANNONBALL_2, 4)); - player.getSkills().addExperience(Skills.SMITHING, 25.6, true); - player.sendMessage("The molten metal cools slowly to form 4 cannonballs."); - } - return amount <= 0; - } - - @Override - public void message(int type) { - } - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingContest.java b/Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingContest.java deleted file mode 100644 index 7b413d739..000000000 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/FishingContest.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.content.quest.members.fishingcontest; - -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.Initializable; -import core.game.node.entity.skill.Skills; - -@Initializable -public class FishingContest extends Quest { - public FishingContest(){super("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); - public static final Item RAW_GIANT_CARP = new Item(338); - public static final Item GIANT_CARP = new Item(337); - public static final Item FISHING_TROPHY = new Item(26); - public static final Item GARLIC = new Item(1550); - public static final Item SPADE = new Item(952); - - - @Override - public void drawJournal(Player player, int stage) { - int line = 11; - 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++); - } - } - } - - @Override - public void finish(Player player) { - int ln = 10; - super.finish(player); - player.getPacketDispatch().sendItemZoomOnInterface(FISHING_TROPHY.getId(), 230, 277, 5); - drawReward(player,"1 Quest Point",ln++); - drawReward(player,"2437 Fishing XP.",ln++); - drawReward(player,"Access to the White Wolf Mountain shortcut.",ln); - player.removeAttribute("fishing_contest:garlic"); - player.removeAttribute("fishing_contest:won"); - player.removeAttribute("fishing_contest:pass-shown"); - player.getSkills().addExperience(Skills.FISHING,2437); - } - - @Override - public Quest newInstance(Object object) { - return this; - } -} diff --git a/Server/src/main/java/core/game/content/quest/members/fishingcontest/StairInteraction.java b/Server/src/main/java/core/game/content/quest/members/fishingcontest/StairInteraction.java deleted file mode 100644 index 360238047..000000000 --- a/Server/src/main/java/core/game/content/quest/members/fishingcontest/StairInteraction.java +++ /dev/null @@ -1,52 +0,0 @@ -package core.game.content.quest.members.fishingcontest; - -import core.game.interaction.MovementPulse; -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; -import core.game.content.quest.PluginInteraction; -import core.game.content.quest.PluginInteractionManager; - -@Initializable -public class StairInteraction extends PluginInteraction { - @Override - public boolean handle(Player player, Node node) { - if(!player.getQuestRepository().isComplete("Fishing Contest")) { - Scenery object = node.asScenery(); - switch (object.getId()) { - case 57: - handleStairs(player,232,object); - return true; - case 55: - handleStairs(player,3679,object); - return true; - } - } - return false; - } - - private void handleStairs(Player player, int npc_id, Scenery object){ - 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)); - return true; - } - }, "movement"); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - setIds(new int[]{57,55}); - PluginInteractionManager.register(this, PluginInteractionManager.InteractionType.OBJECT); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } -} diff --git a/Server/src/main/java/core/game/content/quest/members/lostcity/DramenStaffPlugin.java b/Server/src/main/java/core/game/content/quest/members/lostcity/DramenStaffPlugin.java deleted file mode 100644 index 9b3e929e4..000000000 --- a/Server/src/main/java/core/game/content/quest/members/lostcity/DramenStaffPlugin.java +++ /dev/null @@ -1,54 +0,0 @@ -package core.game.content.quest.members.lostcity; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.entity.skill.Skills; -import core.game.node.item.Item; -import core.plugin.Plugin; - -/** - * Handles the dramen staff cutting plugin. - * @author Vexia - */ -public final class DramenStaffPlugin extends UseWithHandler { - - /** - * The dramen branch item. - */ - private static final Item DRAMEN_BRANCH = new Item(771); - - /** - * The dramen staff item. - */ - private static final Item DRAMEN_STAFF = new Item(772); - - /** - * Constructs a new {@code DramenStaffPlugin} {@code Object}. - */ - public DramenStaffPlugin() { - super(DRAMEN_BRANCH.getId()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(946, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getInventory().containsItem(DRAMEN_BRANCH) && player.getSkills().hasLevel(Skills.CRAFTING,31)) { - player.getInventory().remove(DRAMEN_BRANCH); - player.getInventory().add(DRAMEN_STAFF); - player.getDialogueInterpreter().sendDialogue("You carve the branch into a staff."); - }else{ - player.getDialogueInterpreter().sendDialogue("You need a crafting level of 31 to do this."); - return false; - } - player.lock(1); - return true; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/lostcity/LostCity.java b/Server/src/main/java/core/game/content/quest/members/lostcity/LostCity.java deleted file mode 100644 index 347d9bbc9..000000000 --- a/Server/src/main/java/core/game/content/quest/members/lostcity/LostCity.java +++ /dev/null @@ -1,104 +0,0 @@ -package core.game.content.quest.members.lostcity; - -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 core.plugin.Initializable; -import rs09.plugin.PluginManager; - -/** - * The main type for the lost city Quest. - * @author Vexia - * @author Aero - */ -@Initializable -public class LostCity extends Quest { - - /** - * The requirement titles. - */ - private static final String[] TITLES = new String[] { "Level 31 Crafting", "Level 36 Woodcutting" }; - - /** - * The dramen staff item. - */ - private static final Item DRAMEN_STAFF = new Item(772); - - /** - * The quest requirements. - */ - private boolean[] questRequirements = new boolean[2]; - - /** - * Constructs a new {@code LostCity} {@code Object}. - */ - public LostCity() { - super("Lost City", 83, 82, 3, 147, 0, 1, 6); - } - - @Override - public void drawJournal(Player player, int stage) { - super.drawJournal(player, stage); - switch (stage) { - case 0: - line(player, BLUE + "I can start this quest by speaking to the Adventurers in", 4+ 7); - line(player, BLUE + "the Swamp just south of Lumbridge.", 5+ 7); - drawQuestRequirements(player); - break; - case 10: - line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

Apparently there is a leprechaun hiding in a tree nearby

who can tell me how to enter the Lost City of Zanaris", 4+ 7); - break; - case 20: - line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

I found the Leprechaun hiding in a nearby tree.

He told me that the entrance to Zanaris is in the shed in

Lumbridge swamp but only if I am carrying a Dramen Staff

I can find a Dramen Tree in a cave on Entrana somewhere", 4+ 7); - break; - case 21: - if (player.getBank().containsItem(DRAMEN_STAFF) || player.getInventory().containsItem(DRAMEN_STAFF) || player.getEquipment().containsItem(DRAMEN_STAFF)) { - line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

I found the Leprechaun hiding in a nearby tree.

He told me that the entrance to Zanaris is in the shed in

Lumbridge swamp but only if I am carrying a Dramen Staff.

The Dramen Tree was guarded by a powerful Tree Spirit.

I cut a branch from the tree and crafted a Dramen Staff.

I should enter Zanaris by going to the shed in Lumbridge

Swamp while keeping the Dramen staff with me", 4+ 7); - } else { - line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

I found the Leprechaun hiding in a nearby tree.

He told me that the entrance to Zanaris is in the shed in

Lumbridge swamp but only if I am carrying a Dramen Staff.

The Dramen Tree was guarded by a powerful Tree Spirit.

With the Spirit defeated I can cut a branch from the tree", 4+ 7); - } - break; - case 100: - line(player, "According to one of the adventurers in Lumbridge Swamp

the entrance to Zanaris is somewhere around there.

I found the Leprechaun hiding in a nearby tree.

He told me that the entrance to Zanaris is in the shed in

Lumbridge swamp but only if I am carrying a Dramen Staff.

The Dramen Tree was guarded by a powerful Tree Spirit.

I cut a branch from the tree and crafted a Dramen Staff.

With the mystical Dramen Staff in my possession I was

able to enter Zanaris through the shed in Lumbridge

swamp.


QUEST COMPLETE!", 4+ 7); - break; - } - } - - @Override - public void finish(Player player) { - super.finish(player); - player.getPacketDispatch().sendString("3 Quest Points", 277, 8+ 2); - player.getPacketDispatch().sendString("Access to Zanaris", 277, 9+ 2); - player.getPacketDispatch().sendItemZoomOnInterface(772, 235, 277, 3+ 2); - player.getQuestRepository().syncronizeTab(player); - } - - /** - * Draws the quest requirements onto the journal component. - * @param player The player to draw the requirements for. - */ - private final void drawQuestRequirements(final Player player) { - int line = 7 + 7; - questRequirements[0] = player.getSkills().getStaticLevelByExperience(Skills.CRAFTING) > 30; - questRequirements[1] = player.getSkills().getStaticLevelByExperience(Skills.WOODCUTTING) > 35; - line(player, BLUE + "To complete this quest I will need:", 6); - for (int i = 0; i < questRequirements.length; i++) { - boolean bool = !questRequirements[i]; - String str = TITLES[i]; - line(player, (bool ? RED + str : str), line++, !bool); - } - line(player, BLUE + "and be able to defeat a " + RED + "Level 101 Spirit without weapons", line++); - } - - @Override - public Quest newInstance(Object object) { - PluginManager.definePlugin(new LostCityPlugin()); - PluginManager.definePlugin(new TreeSpiritNPC()); - PluginManager.definePlugin(new ShamusDialogue()); - PluginManager.definePlugin(new WarriorDialogue()); - PluginManager.definePlugin(new DramenStaffPlugin()); - return this; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/lostcity/LostCityPlugin.java b/Server/src/main/java/core/game/content/quest/members/lostcity/LostCityPlugin.java deleted file mode 100644 index d26823730..000000000 --- a/Server/src/main/java/core/game/content/quest/members/lostcity/LostCityPlugin.java +++ /dev/null @@ -1,164 +0,0 @@ -package core.game.content.quest.members.lostcity; - -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.FacialExpression; -import core.game.content.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.TeleportManager.TeleportType; -import core.game.node.entity.player.link.quest.Quest; -import core.game.node.entity.skill.gather.GatheringSkillPulse; -import core.game.node.entity.skill.gather.SkillingTool; -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.plugin.Plugin; -import rs09.game.interaction.InteractionListeners; -import rs09.game.world.GameWorld; - -/** - * Handles the lost city quest. - * @author Vexia - */ -public final class LostCityPlugin extends OptionHandler { - - /** - * The dramen staff item. - */ - private static final Item DRAMEN_STAFF = new Item(772); - - /** - * The shamus npc. - */ - public static NPC SHAMUS; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(1305).getHandlers().put("option:wield", this); - ItemDefinition.forId(1215).getHandlers().put("option:wield", this); - ItemDefinition.forId(1231).getHandlers().put("option:wield", this); - ItemDefinition.forId(5680).getHandlers().put("option:wield", this); - SceneryDefinition.forId(2409).getHandlers().put("option:chop", this); - SceneryDefinition.forId(2406).getHandlers().put("option:open", this); - SceneryDefinition.forId(1292).getHandlers().put("option:chop down", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Lost City"); - switch (node.getId()) { - case 2409: - handleShamusTree(player, quest); - break; - case 1292: - if (SkillingTool.getHatchet(player) == null) { - player.getPacketDispatch().sendMessage("You do not have an axe which you have the level to use."); - return true; - } - if (quest.getStage(player) < 20) { - return true; - } - if (quest.getStage(player) == 20) { - if (player.getAttribute("treeSpawned", false)) { - return true; - } - TreeSpiritNPC spirit = (TreeSpiritNPC) TreeSpiritNPC.create(655, Location.create(2862, 9734, 0)); - spirit.setPlayer(player); - spirit.setRespawn(false); - spirit.init(); - spirit.attack(player); - player.setAttribute("treeSpawned", true); - spirit.sendChat("You must defeat me before touching the tree!"); - return true; - } - player.getPulseManager().run(new GatheringSkillPulse(player, (Scenery) node)); - break; - case 2406: - final boolean dramenTeleport = player.getEquipment().containsItem(DRAMEN_STAFF) && quest.getStage(player) > 20 && player.getLocation().getX() <= 3201; - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - if (dramenTeleport) { - player.getPacketDispatch().sendMessage("The world starts to shimmer..."); - player.getTeleporter().send(Location.create(2452, 4473, 0), TeleportType.FAIRY_RING); - } - break; - case 1305: - case 1215: - case 1231: - case 5680: - if (!player.getQuestRepository().isComplete("Lost City")) { - player.getPacketDispatch().sendMessage("You need to have completed the Lost City quest in order to wield that weapon."); - return true; - } - return InteractionListeners.run(node.getId(),0,"equip",player,node); - } - return true; - } - - /** - * Handles the shamus tree interaction. - * @param player the player. - * @param quest the quest. - */ - private void handleShamusTree(Player player, Quest quest) { - if (SkillingTool.getHatchet(player) == null) { - player.getPacketDispatch().sendMessage("You do not have an axe which you have the level to use."); - return; - } - if (SHAMUS == null) { - initializeShamus(); - } - if (SHAMUS.isInvisible()) { - showShamus(); - return; - } - player.getDialogueInterpreter().sendDialogues(SHAMUS, FacialExpression.FURIOUS, "Hey! Yer big elephant! Don't go choppin' down me", "house, now!"); - } - - /** - * Shows the shamus npc. - */ - private void showShamus() { - if (SHAMUS == null) { - initializeShamus(); - } - SHAMUS.setInvisible(false); - SHAMUS.getProperties().setTeleportLocation(SHAMUS.getProperties().getSpawnLocation()); - GameWorld.getPulser().submit(new Pulse(100, SHAMUS) { - @Override - public boolean pulse() { - if (SHAMUS.getDialoguePlayer() != null) { - return false; - } - SHAMUS.setInvisible(true); - return true; - } - }); - } - - /** - * Initializes the shamus npc. - */ - private void initializeShamus() { - if (SHAMUS == null) { - SHAMUS = NPC.create(654, Location.create(3138, 3211, 0)); - } - SHAMUS.init(); - SHAMUS.setWalks(true); - SHAMUS.setInvisible(true); - } - - @Override - public boolean isWalk(Player player, Node node) { - return !(node instanceof Item); - } - - @Override - public boolean isWalk() { - return false; - } -} diff --git a/Server/src/main/java/core/game/content/quest/members/lostcity/ShamusDialogue.java b/Server/src/main/java/core/game/content/quest/members/lostcity/ShamusDialogue.java deleted file mode 100644 index 1796d8cd8..000000000 --- a/Server/src/main/java/core/game/content/quest/members/lostcity/ShamusDialogue.java +++ /dev/null @@ -1,163 +0,0 @@ -package core.game.content.quest.members.lostcity; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; - -/** - * Handles the shamus npc dialogue. - * @author Vexia - */ -public final class ShamusDialogue extends DialoguePlugin { - - /** - * The quest. - */ - private Quest quest; - - /** - * Constructs a new {@code ShamusDialogue} {@code Object}. - */ - public ShamusDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ShamusDialogue} {@code Object}. - * @param player the player. - */ - public ShamusDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ShamusDialogue(player); - } - - @Override - public boolean open(Object... args) { - quest = player.getQuestRepository().getQuest("Lost City"); - npc("Ay yer big elephant! Yer've caught me, to be sure!", "What would an elephant like yer be wanting wid ol'", "Shamus then?"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { - case 0: - switch (stage) { - case 0: - player("I'm not sure."); - stage++; - break; - case 1: - npc("Well you'll have to be catchin' me again when yer are,", "elephant!"); - stage++; - break; - case 2: - disappear(); - break; - case 3: - end(); - break; - } - break; - case 10: - switch (stage) { - case 0: - player("I want to find Zanaris."); - stage++; - break; - case 1: - npc("Zanaris is it now? Well well well... Yer'll be needing to", "be going further to that funny little shed out there in the", "swamp, so you will."); - stage++; - break; - case 2: - player("...but... I thought... Zanaris was a city?"); - stage++; - break; - case 3: - npc("Aye that it is!"); - stage++; - break; - case 4: - player("...How does it fit in a shed then?"); - stage++; - break; - case 5: - npc("Ah yer stupid elephant! The city isn't IN the shed! The", "doorway to the shed is being a portal to Zanaris, so it", "is."); - stage++; - break; - case 6: - player("So I just walk into the shed and end up in Zanaris", "then?"); - stage++; - break; - case 7: - npc("Oh, was I fergetting to say? Yer need to be carrying a", "Dramenwood staff to be getting there! Otherwise Yer'll", "just be ending up in the shed."); - stage++; - break; - case 8: - player("So where would I get a staff?"); - stage++; - break; - case 9: - npc("Dramenwood staffs are crafted from branches of the", "Dramen tree, so they are. I hear there's a Dramen", "tree over on the island of Entrana in a cave."); - stage++; - break; - case 10: - npc("or some such. There would be probably be a good place", "for an elephant like yer to be starting looking I reckon."); - stage++; - break; - case 11: - npc("The monks are running a ship from Port Sarim to", "Entrana, I hear too. Now leave me alone yer elephant!"); - stage++; - break; - case 12: - disappear(); - break; - case 13: - quest.setStage(player, 20); - end(); - break; - } - break; - default: - switch (stage) { - case 0: - player("I'm not sure."); - stage++; - break; - case 1: - npc("HA! Look at yer! Look at the supid elephant who tries", "to go catching a leprechaun when he don't even be", "knowing what he wants!"); - increment(); - break; - case 2: - disappear(); - break; - case 3: - end(); - break; - } - break; - } - return true; - } - - /** - * Makes dhamus disappear. - */ - private void disappear() { - LostCityPlugin.SHAMUS.setInvisible(true); - interpreter.sendPlainMessage(false, "The leprechaun magically disappears."); - stage++; - } - - @Override - public int[] getIds() { - return new int[] { 654 }; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/lostcity/TreeSpiritNPC.java b/Server/src/main/java/core/game/content/quest/members/lostcity/TreeSpiritNPC.java deleted file mode 100644 index 35859986a..000000000 --- a/Server/src/main/java/core/game/content/quest/members/lostcity/TreeSpiritNPC.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.content.quest.members.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.node.entity.player.link.quest.Quest; -import core.game.world.map.Location; - -/** - * Handles the tree spirit npc. - * @author Vexia - */ -public final class TreeSpiritNPC extends AbstractNPC { - - /** - * The player. - */ - private Player player; - - /** - * Constructs a new {@code TreeSpiritNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public TreeSpiritNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new {@code TreeSpiritNPC} {@code Object}. - */ - public TreeSpiritNPC() { - super(0, null); - } - - @Override - public void handleTickActions() { - super.handleTickActions(); - if (!inCombat()) { - attack(player); - } - if (!player.isActive() || player.getLocation().getDistance(getLocation()) > 15) { - clear(); - } - } - - @Override - public void clear() { - super.clear(); - player.removeAttribute("treeSpawned"); - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if (entity != player) { - return false; - } - return super.isAttackable(entity, style); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - Player player = (Player) killer; - Quest quest = player.getQuestRepository().getQuest("Lost City"); - if (quest.getStage(player) == 20) { - quest.setStage(player, 21); - player.getDialogueInterpreter().sendPlainMessage(false, "With the Tree Spirit defeated you can now chop the tree."); - } - } - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new TreeSpiritNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] { 655 }; - } - - /** - * Sets the player. - * @param player the player. - */ - public void setPlayer(Player player) { - this.player = player; - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/lostcity/WarriorDialogue.java b/Server/src/main/java/core/game/content/quest/members/lostcity/WarriorDialogue.java deleted file mode 100644 index 91723e22f..000000000 --- a/Server/src/main/java/core/game/content/quest/members/lostcity/WarriorDialogue.java +++ /dev/null @@ -1,282 +0,0 @@ -package core.game.content.quest.members.lostcity; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; - -/** - * Handles the warriors dialogue in the lost city quest. - * @author Vexia - * @version 1.0 - */ -public final class WarriorDialogue extends DialoguePlugin { - - /** - * Represents the quest. - */ - private Quest quest; - - /** - * Constructs a new {@code WarriorDialogue} {@code Object}. - */ - public WarriorDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code WarriorDialogue} {@code Object}. - * @param player the player. - */ - public WarriorDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new WarriorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Lost City"); - switch (quest.getStage(player)) { - case 10: - player("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?"); - break; - case 20: - case 21: - player("Have you found anything yet?"); - break; - case 100: - player("Hey, thanks for all the information. It REALLY helped", "me out in finding the lost city of Zanaris and all."); - break; - default: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello there traveller."); - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { - case 0: - handleFirstStage(buttonId); - break; - case 10: - switch (stage) { - case 0: - npc("What? How did you know that? Uh... I mean, no, no", "you're very wrong. Very wrong, and not right at all,", "and I definitely didn't tell you about that at all."); - stage++; - break; - case 1: - end(); - break; - } - break; - case 20: - case 21: - switch (stage) { - case 0: - npc("We're still searching for Zanaris...GAH! I mean we're", "not doing anything here at all."); - stage++; - break; - case 1: - player("I haven't found it yet either."); - stage++; - break; - case 2: - end(); - break; - } - break; - case 100: - switch (stage) { - case 0: - npc("Oh please don't say that anymore! If the rest of my", "party knew I'd helped you they'd probably throw me", "out and make me walk home by myself!"); - stage++; - break; - case 1: - end(); - break; - } - break; - - } - return true; - } - - /** - * Handles the first dialogue quest stage. - * @param buttonId the buttonId. - */ - private void handleFirstStage(int buttonId) { - switch (stage) { - case 0: - options("Why are you camped out here for?", "Do you know any good adventures I can go on?"); - stage++; - break; - case 1: - switch (buttonId) { - case 1: - player("What are you camped here for?"); - stage++; - break; - case 2: - player("Do you know any good adventurers I can go on?"); - stage = 50; - break; - } - break; - case 2: - npc("We're looking for Zanaris...GAH! I mean we're not", "here for any particular reason at all."); - stage++; - break; - case 3: - options("Who's Zanaris?", "What's Zanaris?", "What makes you think it's out here?"); - stage++; - break; - case 4: - switch (buttonId) { - case 1: - player("Who's Zanaris?"); - stage = 10; - break; - case 2: - player("What's Zanaris?"); - stage = 20; - break; - case 3: - player("What makes you think it's out here?"); - stage = 30; - break; - } - break; - case 10: - npc("Ahahahaha! Zanaris isn't a person! It's a magical hidden", "city filled with treasure and rich... uh, nothing. It's", "nothing."); - stage = 31; - break; - case 20: - npc("I don't think we want other people competing with us to", "find it. Forget I said anything."); - stage = 34; - break; - case 30: - npc("Don't you know of the legends that tell of the magical", "city, hidden in the swamp... Uh, no, you're right, we're", "wasting our time here."); - stage++; - break; - case 31: - options("If it's hidden how are you planning to find it?", "There's no such thing!"); - stage++; - break; - case 32: - switch (buttonId) { - case 1: - player("If it's hidden how are you planning to find it?"); - stage = 33; - break; - case 2: - player("There's no such thing!"); - stage = 33; - break; - } - break; - case 33: - npc("Well, we don't want to tell anyone else about that,", "because we don't want anyone else sharing in all that", "glory and treasure."); - stage++; - break; - case 34: - options("Please tell me.", "Looks like you don't know either."); - stage++; - break; - case 35: - switch (buttonId) { - case 1: - player("Please tell me?"); - stage = 37; - break; - case 2: - player("Well, it looks to me like YOU don't know EITHER", "seeing as you're all just sat around here."); - stage = 60; - break; - } - break; - case 37: - npc("No."); - stage++; - break; - case 38: - player("Please?"); - stage++; - break; - case 39: - npc("No!"); - stage++; - break; - case 40: - player("PLEEEEEEEEEEEEEEEEEEEEEEEEESE???"); - stage = 42; - break; - case 42: - npc("NO!"); - stage++; - break; - case 43: - end(); - break; - case 41: - npc("When we've found Zanaris you'll... GAH! I mean, we're", "not here for any particular reason at all."); - stage = 3; - break; - case 50: - npc("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!"); - stage++; - break; - case 51: - end(); - break; - case 60: - if (quest.hasRequirements(player)) { - npc("Of course we know! We just haven't found which tree", "the stupid leprechaun's hiding in yet!"); - stage++; - } else { - npc("When we've found Zanaris you'll... GAH! I mean, we're", "not here for any particular reason at all."); - stage = 3; - } - break; - case 61: - npc("GAH! I didn't mean to tell you that! Look, just forget I", "said anything okay?"); - stage++; - break; - case 62: - player("So a leprechaun knows where Zanaris is eh?"); - stage++; - break; - case 63: - npc("Ye.. uh, no. No, not at all. And even if he did - which", "he doesn't he DEFINITELY ISN'T hiding in some", "tree around here. Nope, definitely not. Honestly."); - stage++; - break; - case 64: - player("Thanks for the help!"); - stage++; - break; - case 65: - npc("Help? What help? I didn't help! Please don't say I did,", "I'll get in trouble!"); - stage++; - break; - case 66: - end(); - quest.start(player); - break; - } - } - - @Override - public int[] getIds() { - return new int[] { 650 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/ArheinShopDialogue.java b/Server/src/main/java/core/game/content/quest/members/merlinscrystal/ArheinShopDialogue.java deleted file mode 100644 index 4787892ea..000000000 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/ArheinShopDialogue.java +++ /dev/null @@ -1,185 +0,0 @@ -package core.game.content.quest.members.merlinscrystal; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; - -/** - * Represents the dialogue plugin used for the arhein npc. - * @author 'Vexia - * @version 1.0 - */ -public final class ArheinShopDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ArheinShopDialogue} {@code Object}. - */ - public ArheinShopDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ArheinShopDialogue} {@code Object}. - * @param player the player. - */ - public ArheinShopDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ArheinShopDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (args.length > 1) { - interpreter.sendDialogues(npc, null, "Hey buddy! Get away from my ship alright?"); - stage = 2000; - return true; - } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello! Would you like to trade? I've a variety of wares", "for sale!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Let's trade.", "No thank you.", "Is that your ship?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Let's trade."); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks."); - stage = 200; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Is that your ship?"); - stage = 300; - break; - } - break; - case 100: - end(); - npc.openShop(player); - break; - case 300: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, I use it to make deliveries to my customers up and", "down the coast. These crates here are all ready for my", "next trip."); - stage = 301; - break; - case 301: - interpreter.sendOptions("Select an Option", "Where do you deliver to?", "Are you rich then?", "Do you deliver to the fort just down the coast?"); - stage = 303; - break; - case 303: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where do you deliver to?"); - stage = 1000; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Are you rich then?"); - stage = 2500; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you deliver to the fort just down the coast?"); - stage = 3000; - break; - - } - break; - case 1000: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Various places up and down the coast. Mostly Karamja", "and Port Sarim."); - stage = 1001; - break; - case 1001: - interpreter.sendOptions("Select an Option", "I don't suppose I could get a lift anywhere?", "Well, good luck with your business."); - stage = 1002; - break; - case 1002: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't suppose I could get a lift anywhere?"); - stage = 1003; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, good luck with your business."); - stage = 2700; - break; - - } - break; - case 1003: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Sorry pal, but I'm afraid I'm not quite ready to sail yet."); - stage = 1004; - break; - case 1004: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm waiting on a big delivery of candles which I need to", "deliver further along the coast."); - stage = 1005; - break; - case 1005: - end(); - break; - case 2500: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Business is going reasonably well... I wouldn't say I was the", "richest of merchants every, but I'm doing fairly well all", "things considered."); - stage = 2501; - break; - case 2501: - end(); - break; - case 2700: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Thanks buddy!"); - stage = 2701; - break; - case 2701: - end(); - break; - case 200: - end(); - break; - case 2000: - interpreter.sendDialogues(player, null, "Yeah... uh... sorry..."); - stage = 2001; - break; - case 2001: - end(); - break; - case 3000: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - if (quest.getStage(player) == 30 || quest.getStage(player) == 40) { - stage = 3001; - } else { - stage = 2701; - } - break; - case 3001: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you drop me off on the way down please?"); - stage = 3002; - break; - case 3002: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - quest.setStage(player, 40); - stage = 2701; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 563 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/TheLadyOfTheLake.java b/Server/src/main/java/core/game/content/quest/members/merlinscrystal/TheLadyOfTheLake.java deleted file mode 100644 index 3e4bf528b..000000000 --- a/Server/src/main/java/core/game/content/quest/members/merlinscrystal/TheLadyOfTheLake.java +++ /dev/null @@ -1,264 +0,0 @@ -package core.game.content.quest.members.merlinscrystal; - -import core.game.container.impl.EquipmentContainer; -import org.rs09.consts.Items; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.content.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 rs09.tools.DialogueConstKt.END_DIALOGUE; - -/** - * Handles the LadyOfTheLake dialogue. - * - * @author Vexia - * @author Splinter - */ -public class TheLadyOfTheLake extends DialoguePlugin { - - /** - * Constructs a new {@code TheLadyOfTheLake} {@code Object} - */ - public TheLadyOfTheLake() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code TheLadyOfTheLake} {@code Object} - * - * @param player the player. - */ - public TheLadyOfTheLake(Player player) { - super(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (!player.hasItem(new Item(35, 1))) { - npc("Good day to you, " + (player.isMale() ? "sir" : "madam") + "."); - stage = 0; - } else { - npc("Good day to you, " + (player.isMale() ? "sir" : "madam") + "."); - stage = 699; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Merlin's Crystal"); - switch (stage) { - case END_DIALOGUE: - end(); - break; - case 0: - options("Who are you?", "I seek the sword Excalibur.", "Good day."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - player("Who are you?"); - stage = 100; - break; - case 2: - player("I seek the sword Excalibur."); - if (quest.getStage(player) < 50) { - stage = 250; - } else { - stage = 161; - } - break; - case 3: - player("Good day."); - stage = END_DIALOGUE; - break; - } - break; - case 250: - npc("I am afraid I do not know what you are talking about."); - stage = END_DIALOGUE; - break; - case 100: - npc("I am the Lady of the Lake."); - if (player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(2) - && player.getEquipment().get(EquipmentContainer.SLOT_HAT) != null - && player.getEquipment().get(EquipmentContainer.SLOT_HAT).getId() == 14631 - && (player.getInventory().containsAtLeastOneItem(Items.EXCALIBUR_35) || player.getEquipment().containsAtLeastOneItem(Items.EXCALIBUR_35))) { - stage = 110; - } else { - stage = 145; - } - break; - case 110: - player("And I'm-"); - stage++; - break; - case 111: - npc("You're " + player.getName() + ". And I see from the sign you", "wear that you have earned the trust of Sir Kay."); - stage++; - break; - case 112: - player("It was nothing.. really..."); - stage++; - break; - case 113: - npc("You shall be rewarded handsomely!"); - stage++; - break; - case 114: - interpreter.sendItemMessage(Items.EXCALIBUR_35, "The Lady of the Lake reaches out and touches the", "blade Excalibur which seems to vibrate with new power."); - if (player.getInventory().containsAtLeastOneItem(Items.EXCALIBUR_35)) { - player.getInventory().remove(new Item(Items.EXCALIBUR_35)); - player.getInventory().add(new Item(14632)); - } else if (player.getEquipment().containsAtLeastOneItem(Items.EXCALIBUR_35)) { - player.getEquipment().remove(new Item(Items.EXCALIBUR_35)); - player.getEquipment().add(new Item(14632), true, false); - } - stage++; - break; - case 115: - player("What does this do then?"); - stage++; - break; - case 116: - npc("I made the blade more powerful, and also gave it a", "rather healthy effect when you use the special."); - stage++; - break; - case 117: - player("Thanks!"); - stage = END_DIALOGUE; - break; - case 145: - options("I seek the sword Excalibur.", "Good day."); - stage = 150; - break; - case 150: - switch (buttonId) { - case 1: - player("I seek the sword Excalibur."); - stage = 161; - break; - case 2: - player("Good day."); - stage = END_DIALOGUE; - break; - } - break; - case 161: - if (quest.getStage(player) == 50 || quest.getStage(player) == 60) {// they haven't proven themselves yet - npc("Aye, I have that artefact in my possession."); - stage = 300; - } else if (quest.getStage(player) >= 70) { - npc("... But you have already proved thyself to be worthy", "of wielding it once already. I shall return it to you", "if you can prove yourself to still be worthy."); - stage = 162; - } - break; - case 162: - player("... And how can I do that?"); - stage = 163; - break; - case 163: - npc("Why, by proving yourself to be above material goods."); - stage = 164; - break; - case 164: - npc("500 coins ought to do it."); - stage = 166; - break; - case 166: - if (player.getInventory().contains(995, 500)) { - player("Ok, here you go."); - stage = 168; - } else { - player("I don't have that kind of money..."); - stage = 167; - } - break; - case 167: - npc("Well, come back when you do."); - stage = END_DIALOGUE; - break; - case 168: - if (player.getInventory().freeSlots() == 0) { - player("Sorry, I don't seem to have enough inventory space."); - stage = END_DIALOGUE; - } else if (player.getInventory().contains(995, 500)) { - player.getInventory().remove(new Item(995, 500)); - player.getInventory().add(new Item(35, 1)); - npc("You are still worthy to wield Excalibur! And thanks", "for the cash! I felt like getting a new haircut!"); - stage = 170; - } else { - player("I don't have that kind of money..."); - stage = 167; - } - break; - case 170: - interpreter.sendDialogue("The lady of the Lake hands you Excalibur."); - stage = END_DIALOGUE; - break; - case 300: - npc("'Tis very valuable, and not an artefact to be given", "away lightly."); - stage = 301; - break; - case 301: - npc("I would want to give it away only to one who is worthy", "and good."); - stage = 302; - break; - case 302: - player("And how am I meant to prove that?"); - stage = 303; - break; - case 303: - npc("I shall set a test for you."); - stage = 304; - break; - case 304: - npc("First I need you to travel to Port Sarim. Then go to", "the upstairs room of the jeweller's shop there."); - stage = 305; - break; - case 305: - player("Ok. That seems easy enough."); - quest.setStage(player, 60); - stage = END_DIALOGUE; - break; - case 699: - options("Who are you?", "Good day."); - stage = 700; - break; - case 700: - switch (buttonId) { - case 1: - player("Who are you?"); - stage = 720; - break; - case 2: - player("Good day."); - stage = END_DIALOGUE; - break; - } - break; - case 720: - npc("I am the Lady of the Lake."); - stage = END_DIALOGUE; - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TheLadyOfTheLake(player); - } - - @Override - public int[] getIds() { - return new int[]{250}; - } -} diff --git a/Server/src/main/java/core/game/content/quest/members/sheepherder/SheepHerder.java b/Server/src/main/java/core/game/content/quest/members/sheepherder/SheepHerder.java deleted file mode 100644 index ee05f0f88..000000000 --- a/Server/src/main/java/core/game/content/quest/members/sheepherder/SheepHerder.java +++ /dev/null @@ -1,115 +0,0 @@ -package core.game.content.quest.members.sheepherder; - -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.world.map.Direction; -import core.game.world.map.Location; -import core.plugin.Initializable; -import org.rs09.consts.Items; - -import java.util.HashMap; - -@Initializable -public class SheepHerder extends Quest { - public static Item CATTLE_PROD = new Item(Items.CATTLEPROD_278); - public static Item POISON = new Item(279); - public static Item PLAGUE_TOP = new Item(284); - public static Item PLAGUE_BOTTOM = new Item(285); - public static Item RED_SHEEP_BONES = new Item(Items.SHEEP_BONES_1_280); - public static Item GREEN_SHEEP_BONES = new Item(Items.SHEEP_BONES_2_281); - public static Item BLUE_SHEEP_BONES = new Item(Items.SHEEP_BONES_3_282); - public static Item YELLOW_SHEEP_BONES = new Item(Items.SHEEP_BONES_4_283); - public static int RED_SHEEP = 2345; - public static int GREEN_SHEEP = 2346; - public static int BLUE_SHEEP = 2347; - public static int YELLOW_SHEEP = 2348; - public static int FARMER_BRUMTY = 291; - - public static HashMap boneMap = new HashMap<>(); - static{ - boneMap.put(RED_SHEEP,RED_SHEEP_BONES); - boneMap.put(GREEN_SHEEP,GREEN_SHEEP_BONES); - boneMap.put(YELLOW_SHEEP,YELLOW_SHEEP_BONES); - boneMap.put(BLUE_SHEEP,BLUE_SHEEP_BONES); - } - - public SheepHerder(){super("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; - 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++); - } 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++); - } 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)); - } - 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; - } - } - } - - @Override - public void finish(Player player) { - int ln = 10; - super.finish(player); - player.getPacketDispatch().sendItemZoomOnInterface(995, 230, 277, 5); - drawReward(player,"4 Quest Points",ln++); - drawReward(player,"3100 coins",ln++); - player.removeAttribute("sheep_herder:red_dead"); - player.removeAttribute("sheep_herder:blue_dead"); - player.removeAttribute("sheep_herder:green_dead"); - player.removeAttribute("sheep_herder:yellow_dead"); - player.removeAttribute("sheep_herder:all_dead"); - player.getInventory().add(new Item(995,3100)); - } - - @Override - public Quest newInstance(Object object) { - new HerderSheepNPC(RED_SHEEP, Location.create(2609, 3343, 0)).init(); - new HerderSheepNPC(RED_SHEEP,Location.create(2610, 3344, 0)).init(); - new HerderSheepNPC(RED_SHEEP,Location.create(2609, 3345, 0)).init(); - new HerderSheepNPC(RED_SHEEP,Location.create(2615, 3343, 0)).init(); - new HerderSheepNPC(GREEN_SHEEP,Location.create(2622, 3366, 0)).init(); - new HerderSheepNPC(GREEN_SHEEP,Location.create(2622, 3366, 0)).init(); - new HerderSheepNPC(GREEN_SHEEP,Location.create(2619, 3371, 0)).init(); - new HerderSheepNPC(GREEN_SHEEP,Location.create(2617, 3365, 0)).init(); - new HerderSheepNPC(YELLOW_SHEEP,Location.create(2610, 3390, 0)).init(); - new HerderSheepNPC(YELLOW_SHEEP,Location.create(2613, 3389, 0)).init(); - new HerderSheepNPC(YELLOW_SHEEP,Location.create(2606, 3391, 0)).init(); - new HerderSheepNPC(YELLOW_SHEEP,Location.create(2608, 3387, 0)).init(); - new HerderSheepNPC(BLUE_SHEEP,Location.create(2559, 3388, 0)).init(); - new HerderSheepNPC(BLUE_SHEEP,Location.create(2562, 3388, 0)).init(); - new HerderSheepNPC(BLUE_SHEEP,Location.create(2563, 3383, 0)).init(); - new HerderSheepNPC(BLUE_SHEEP,Location.create(2570, 3381, 0)).init(); - NPC Brumty = new NPC(FARMER_BRUMTY,Location.create(2594,3357,0)); - Brumty.setWalks(false); - Brumty.setRespawn(true); - Brumty.init(); - Brumty.setDirection(Direction.NORTH); - return this; - } -} diff --git a/Server/src/main/java/core/game/content/quest/members/waterfallquest/BookOnBaxtorianPlugin.java b/Server/src/main/java/core/game/content/quest/members/waterfallquest/BookOnBaxtorianPlugin.java deleted file mode 100644 index b6577572b..000000000 --- a/Server/src/main/java/core/game/content/quest/members/waterfallquest/BookOnBaxtorianPlugin.java +++ /dev/null @@ -1,122 +0,0 @@ -package core.game.content.quest.members.waterfallquest; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.book.Book; -import core.game.content.dialogue.book.BookLine; -import core.game.content.dialogue.book.Page; -import core.game.content.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/java/core/game/content/quest/members/whatliesbelow/SurokMagisDialogue.java b/Server/src/main/java/core/game/content/quest/members/whatliesbelow/SurokMagisDialogue.java deleted file mode 100644 index e307fcf08..000000000 --- a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/SurokMagisDialogue.java +++ /dev/null @@ -1,528 +0,0 @@ -package core.game.content.quest.members.whatliesbelow; - -import core.game.content.activity.ActivityManager; -import core.game.content.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.world.update.flag.context.Animation; - -/** - * Handles the surok magis dialogue. - * @author Vexia - */ -public class SurokMagisDialogue extends DialoguePlugin { - - /** - * The quest instance. - */ - private Quest quest; - - /** - * The cutscene. - */ - private WLBelowCutscene cutscene; - - /** - * Constructs a new {@Code SurokMagisDialogue} {@Code Object} - */ - public SurokMagisDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@Code SurokMagisDialogue} {@Code Object} - * @param player the player. - */ - public SurokMagisDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SurokMagisDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest(WhatLiesBelow.NAME); - switch (quest.getStage(player)) { - default: - npc("Excuse me?"); - break; - case 20: - player("Hello."); - break; - case 30: - case 40: - npc("Ah! You're back. Have you found the things I need yet?"); - break; - case 50: - if (!player.hasItem(WhatLiesBelow.SUROKS_LETTER)) { - player("That letter was treasonous so I destroyed it!"); - stage = 10; - break; - } - player("This letter is treasonous! I'm going to report you to the", "king!"); - break; - case 60: - npc("Hi, " + player.getUsername() + "!"); - break; - case 70: - if (!player.getInventory().containsItem(WhatLiesBelow.BEACON_RING) && !player.getEquipment().containsItem(WhatLiesBelow.BEACON_RING)) { - player("I should probably get my beacon ring first..."); - stage = -1; - break; - } - if (args.length >= 2) { - cutscene = player.getAttribute("cutscene", null); - if (args.length == 3) { - player.getDialogueInterpreter().sendDialogues(cutscene.getZaff(), null, "Your teleport spell has been corrupted, Surok! I have", "placed a magic block on this room. You will remain here,", "under guard, in the library from now on."); - stage = 12; - return true; - } - interpreter.sendPlainMessage(true, "The room grows dark and you sense objects moving..."); - stage = 10; - break; - } - player("Surok!! Your plans have been uncovered! You are hereby", "under arrest on the authority of the Varrock Palace", "Secret Guard!"); - break; - case 80: - case 90: - case 100: - npc("You have foiled my plans, " + player.getUsername() + "... I obviously", "underestimated you."); - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (quest.getStage(player)) { - default: - switch (stage) { - case 0: - player("What do you want? ...Oh, wait. I know! You're", "probably just like all the others, aren't you? After some", "fancy spell or potion from me, I bet!"); - stage = 1; - break; - case 1: - npc("No! at least, I don't think so. What sort of spells", "do you have?"); - stage = 2; - break; - case 2: - player("Hah! I knew it! I expect you want my Aphro-Dizzy-", "Yak spell! Want someone to fall madly in love with you,", "eh?"); - stage = 3; - break; - case 3: - npc("That spell sounds very interesting, but I didn't mean to", "disturb you!"); - stage = 4; - break; - case 4: - player("Well, I see that you do have some manners. I'm glad", "to see that you use them."); - stage = 5; - break; - case 5: - player("Now, if it's all the same, I am very bust at the", "moment. Come back another time", "please and thank you."); - stage = 6; - break; - case 6: - npc("Yes, of course!"); - stage = 7; - break; - case 7: - end(); - break; - } - break; - case 20: - switch (stage) { - case 0: - npc("Hah! Come for my Aphro-Dizzy-Yak spell! Want", "someone to fall madly in love with you eh? Not", "surprised with a face like that, to be honest!"); - stage++; - break; - case 1: - player("I didn't come here to be insulted!"); - stage++; - break; - case 2: - npc("Really? Well, with ears like that, you do surprise me!"); - stage++; - break; - case 3: - if (!player.getInventory().containsItem(WhatLiesBelow.RATS_LETTER)) { - player("Nevermind!"); - stage = 99; - break; - } - player("No, look. I have a letter for you."); - stage++; - break; - case 4: - npc("Really? Well then, let me see it!"); - stage++; - break; - case 5: - player("Here it is!"); - stage++; - break; - case 6: - npc.animate(Animation.create(6096)); - npc("Of all the luck!"); - stage++; - break; - case 7: - player("Why did you destroy the letter?"); - stage++; - break; - case 8: - npc("None of your business! It's a secret!"); - stage++; - break; - case 9: - player("Yes, there seems to be a lot of them going around at", "the moment."); - stage++; - break; - case 10: - npc("Of course. Hmmm. However, I could let you in on", "another secret, if you like?"); - stage++; - break; - case 11: - player("Go on, then!"); - stage++; - break; - case 12: - npc("My secret is this. I have been spending time here in", "the palace library trying to discover some ancient spells", "and magics."); - stage++; - break; - case 13: - npc("In my research, I have uncovered the most astounding", "spell that will allow me to transform simple clay into solid", "gold bars!"); - stage++; - break; - case 14: - npc("Now I am ready to use the spell to create all the gold", "I...uh...the city wants. I would gladly share this gold", "with you; I simply need a few more things."); - stage++; - break; - case 15: - player("Okay, what do you need?"); - stage++; - break; - case 16: - npc("I will only need a couple of items. The first is very", "simple: an ordinary bowl to use as a casting vessel."); - stage++; - break; - case 17: - npc("You should be able to find one of these at any local", "store here in Varrock. I would go myself but I", "am...uh...busy with my research."); - stage++; - break; - case 18: - npc("The other item is much harder. I need a metal wand", "infused with chaos magic."); - stage++; - break; - case 19: - player("How would I get something like that?"); - stage++; - break; - case 20: - npc("Take this metal wand. You will also need 15 chaos", "runes. When you get to the Chaos Altar, use the wand", "on the altar itself. This would infuse the runes into the", "wand."); - stage++; - break; - case 21: - player("How on earth do you know about Runecrafting? I", "thought only a few people knew of it."); - stage++; - break; - case 22: - npc("Hah! Don't presume to think that those wizards in their", "fancy towers are the only people to have heard of", "Runecrafting! Now pay attention!"); - stage++; - break; - case 23: - npc("You will need to have the 15 chaos runes in your", "inventory. Make sure you also have either a chaos", "talisman or chaos tiara to complete the infusion."); - stage++; - break; - case 24: - player("Where can I get a talisman or a tiara?"); - stage++; - break; - case 25: - npc("I'm afraid I don't know. You will need to research for", "one."); - stage++; - break; - case 26: - npc("Bring the infused wand and a bowl back to me and I", "will make us both rich!"); - stage++; - break; - case 27: - npc("One more thing. I have uncovered information here in", "the library which may be of use to you. It tells of a", "safe route to the Chaos Altar that avoids the Wilderness."); - stage++; - break; - case 28: - player("Great! What is it?"); - stage++; - break; - case 29: - npc("It is an old tome...a history book of sorts. It's", "somewhere here in the library. I forget where I left it,", "but it should be easy enough for you to find."); - stage++; - break; - case 30: - npc("I have also given you a copy of a diary", "I...uh...acquired. It may also help you to find that which", "you seek."); - stage++; - break; - case 31: - player.getInventory().remove(WhatLiesBelow.RATS_LETTER); - player.getInventory().add(WhatLiesBelow.WAND, player); - player.getInventory().add(WhatLiesBelow.SIN_KETH_DIARY, player); - quest.setStage(player, 30); - end(); - break; - case 99: - end(); - break; - } - break; - case 30: - case 40: - switch (stage) { - case 0: - if (player.getInventory().containsItem(WhatLiesBelow.INFUSED_WAND)) { - if (!player.getInventory().containsItem(WhatLiesBelow.BOWL)) { - player("No, I still need to get you a bowl."); - stage = 1; - break; - } - player("I have the things you wanted."); - stage = 6; - break; - } - if (!player.hasItem(WhatLiesBelow.WAND)) { - player("I lost the wand!"); - stage = 2; - break; - } - if (!player.hasItem(WhatLiesBelow.SIN_KETH_DIARY)) { - player("I lost the diary!"); - stage = 4; - break; - } - player("No not yet."); - stage++; - break; - case 1: - end(); - break; - case 2: - npc("Somehow, I knew that would happen so I have made a", "few spares for just such an occasion!"); - stage++; - break; - case 3: - npc("Here you are. Try not to lose this one!"); - player.getInventory().add(WhatLiesBelow.WAND, player); - stage = 1; - break; - case 4: - player.getInventory().add(WhatLiesBelow.SIN_KETH_DIARY, player); - npc("Here you are, Try not to lose this one!"); - stage = 5; - break; - case 5: - end(); - break; - case 6: - npc("Excellent! Well done! I knew that you would not let", "me down."); - stage++; - break; - case 7: - player("So...about this gold that you're going to", "give me?"); - stage++; - break; - case 8: - npc("All in good time. I must prepare the spell first", ", and that will take a little time. While I am", "doing that, please take this letter to Rat, the trader", "outside the city who sent you here."); - stage++; - break; - case 9: - player("Okay, but I'll be back for my gold."); - stage++; - break; - case 10: - npc("Yes, yes, yes. Now off you go!"); - stage++; - break; - case 11: - quest.setStage(player, 50); - player.getInventory().add(WhatLiesBelow.SUROKS_LETTER, player); - end(); - break; - } - break; - case 50: - switch (stage) { - case 0: - npc("Hah! Have fun with that!"); - stage++; - break; - case 1: - end(); - break; - case 10: - npc("Really? You destroyed it?"); - stage++; - break; - case 11: - player("Yes!"); - stage++; - break; - case 12: - npc("You want another one, don't you?"); - stage++; - break; - case 13: - player("Ah..uh..yes..yes, I do."); - stage++; - break; - case 14: - player.getInventory().add(WhatLiesBelow.SUROKS_LETTER, player); - npc("Fine. Here you are. And stop all this complaining; it's", "getting me down!"); - stage++; - break; - case 15: - end(); - break; - } - break; - case 60: - switch (stage) { - case 0: - player("You are a bad man!"); - stage++; - break; - case 1: - end(); - break; - } - break; - case 70: - switch (stage) { - case -1: - end(); - break; - case 0: - npc("So! You're with the Secret Guard, eh? I should have", "known! I knew you had ugly ears from the start...", "and your nose is too short!"); - stage++; - break; - case 1: - player("Give yourself up, Surok!"); - stage++; - break; - case 2: - npc("Never! I am Surok Magis, descendant of the High Elder", "Sin'keth Magis, rightful heir of the Dagon'hai Order! I will", "have my revenge on those who destroyed my people!"); - stage++; - break; - case 3: - player("The place is surrounded. There is nowhere to run!"); - stage++; - break; - case 4: - npc("Do you really wish to die so readily? Are you prepared", "to face your death?"); - stage++; - break; - case 5: - player("Bring it on!"); - stage++; - break; - case 6: - npc("I am Dagon'hai! I run from nothing. My spell has", "been completed and it is time for you to meet your", "end, " + player.getUsername() + "! The king is now under my control!"); - stage++; - break; - case 7: - end(); - ActivityManager.start(player, "What Lies below", false); - break; - case 10: - close(); - player.unlock(); - cutscene.getKing().unlock(); - cutscene.reset(); - player.getInterfaceManager().restoreTabs(); - cutscene.getKing().getProperties().getCombatPulse().attack(player); - stage = 11; - break; - case 11: - close(); - stage = 12; - break; - case 12: - player.lock(); - npc("No! All is lost! I must escape!"); - stage++; - break; - case 13: - interpreter.sendDialogues(cutscene.getZaff(), null, "You will not escape justice this time, Surok!"); - stage = 14; - break; - case 14: - npc("No! My plans have been ruined! I was so close to", "success!"); - stage = 16; - break; - case 16: - interpreter.sendDialogues(cutscene.getZaff(), null, "Thank you for your help, " + player.getUsername() + ". I will put the", "room back in order and then I must leave. Surok is", "defeated and will be no more trouble for us. We will", "guard him more closely from now on!"); - stage++; - break; - case 17: - quest.setStage(player, 80); - interpreter.sendPlainMessage(true, "The room grows dark and you sense objects moving..."); - cutscene.stop(true); - break; - } - break; - case 80: - case 90: - case 100: - switch (stage) { - case 0: - player("Yes. Let this be a lesson to you."); - stage++; - break; - case 1: - npc("..."); - stage++; - break; - case 2: - end(); - break; - } - break; - } - return true; - } - - @Override - public void end() { - if (cutscene != null) { - return; - } - super.end(); - } - - @Override - public int[] getIds() { - return new int[] { 5834, 5835 }; - } - - /** - * Gets the cutscene. - * @return the cutscene - */ - public WLBelowCutscene getCutscene() { - return cutscene; - } - - /** - * Sets the cutscene. - * @param cutscene the cutscene to set. - */ - public void setCutscene(WLBelowCutscene cutscene) { - this.cutscene = cutscene; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/WhatLiesBelow.java b/Server/src/main/java/core/game/content/quest/members/whatliesbelow/WhatLiesBelow.java deleted file mode 100644 index e7ba1b645..000000000 --- a/Server/src/main/java/core/game/content/quest/members/whatliesbelow/WhatLiesBelow.java +++ /dev/null @@ -1,203 +0,0 @@ -package core.game.content.quest.members.whatliesbelow; - -import core.plugin.Initializable; -import org.rs09.consts.Items; -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 rs09.plugin.PluginManager; - -/** - * The what lies below quest. - * @author Vexa - * - */ -@Initializable -public class WhatLiesBelow extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "What Lies Below"; - - /** - * The bowl item. - */ - public static final Item BOWL = new Item(Items.BOWL_1923); - - /** - * The sin keth diary. - */ - public static final Item SIN_KETH_DIARY = new Item(Items.SINKETHS_DIARY_11002); - - /** - * The empty folder item. - */ - public static final Item EMPTY_FOLDER = new Item(Items.AN_EMPTY_FOLDER_11003); - - /** - * The used folder. - */ - public static final Item USED_FOLDER = new Item(Items.USED_FOLDER_11006); - - /** - * The full folder. - */ - public static final Item FULL_FOLDER = new Item(Items.FULL_FOLDER_11007); - - /** - * The rats paper item. - */ - public static final Item RATS_PAPER = new Item(Items.RATS_PAPER_11008); - - /** - * The rats letter. - */ - public static final Item RATS_LETTER = new Item(Items.RATS_LETTER_11009); - - /** - * The suroks letter. - */ - public static final Item SUROKS_LETTER = new Item(Items.SUROKS_LETTER_11010); - - /** - * The wand item. - */ - public static final Item WAND = new Item(Items.WAND_11012); - - /** - * The infused item. - */ - public static final Item INFUSED_WAND = new Item(Items.INFUSED_WAND_11013); - - /** - * The bacon ring. - */ - 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. - */ - private final boolean[] requirements = new boolean[4]; - - /** - * Constructs a new {@Code WhatLiesBelow} {@Code Object} - */ - public WhatLiesBelow() { - super(NAME, 136, 135, 1); - } - - @Override - public Quest newInstance(Object object) { - PluginManager.definePlugin(new WLBelowPlugin()); - return this; - } - - @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; - } - } - - @Override - public void start(Player player) { - super.start(player); - player.getInventory().add(EMPTY_FOLDER, player); - } - - @Override - public void finish(Player player) { - super.finish(player); - player.getPacketDispatch().sendString("8,000 Runecrafting XP", 277, 8+ 2); - player.getPacketDispatch().sendString("2,000 Defence XP", 277, 9+ 2); - player.getPacketDispatch().sendString("Beacon Ring", 277, 10+ 2); - player.getPacketDispatch().sendString("Knowledge of Chaos Tunnel", 277, 11+ 2); - player.getPacketDispatch().sendItemZoomOnInterface(BEACON_RING.getId(), 235, 277, 3+ 2); - player.getSkills().addExperience(Skills.RUNECRAFTING, 8000); - player.getSkills().addExperience(Skills.DEFENCE, 2000); - 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"); - return requirements[0] && requirements[2] && requirements[3]; - } - - @Override - public int[] getConfig(Player player, int stage) { - int id = 992; - if (stage >= 40 && stage != 100) { - return new int[] { id, (1 << 8) + 1 }; - } - if (stage == 0) { - return new int[] { id, 0 }; - } else if (stage > 0 && stage < 100) { - return new int[] { id, 1 }; - } - player.getConfigManager().set(1181, (1 << 8) + (1 << 9), true); - return new int[] { id, 502 }; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/members/witchshouse/BoyDialoguePlugin.java b/Server/src/main/java/core/game/content/quest/members/witchshouse/BoyDialoguePlugin.java deleted file mode 100644 index 421608273..000000000 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/BoyDialoguePlugin.java +++ /dev/null @@ -1,151 +0,0 @@ -package core.game.content.quest.members.witchshouse; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.game.node.item.Item; - -/** - * Created for 2009Scape - * User: Ethan Kyle Millard - * Date: March 15, 2020 - * Time: 11:49 AM - */ -@Initializable -public class BoyDialoguePlugin extends DialoguePlugin { - - private static final Item BALL = new Item(2407); - - public BoyDialoguePlugin() { - } - - public BoyDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BoyDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - final Quest quest = player.getQuestRepository().getQuest("Witch's House"); - player.debug(quest.isStarted(player) + " " + quest.getStage(player) ); - if (!quest.isStarted(player) && quest.getStage(player) < 10) { - player("Hello young man."); - setStage(1); - return true; - } - if (quest.isCompleted(player) || quest.getStage(player) == 100) { - sendDialogue("The boy is too busy playing with his ball to talk."); - finish(); - return true; - } - if (!player.getInventory().containsItem(BALL)) { - npc( FacialExpression.OLD_NORMAL, "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."); - } - setStage(11); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - final Quest quest = player.getQuestRepository().getQuest("Witch's House"); - switch(stage) { - case -1: - end(); - break; - case 1: - sendDialogue("The boy sobs."); - next(); - break; - case 2: - options("What's the matter?", "Well if you're not going to answer then I'll go."); - next(); - break; - case 3: - switch(buttonId) { - case 1: - player("What's the matter?"); - setStage(5); - break; - case 2: - player("Well if you're not going to answer then I'll go."); - next(); - break; - } - break; - case 4: - sendDialogue("The boy sniffs slightly."); - 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?"); - next(); - break; - case 6: - options("Ok, I'll see what I can do.", "Get it back yourself."); - next(); - break; - case 7: - switch(buttonId) { - case 1: - player("Ok, I'll see what I can do."); - setStage(10); - break; - case 2: - player("Get it back yourself."); - next(); - break; - } - break; - case 8: - npc(FacialExpression.OLD_NORMAL, "You're a meany."); - next(); - break; - case 9: - sendDialogue("The boy starts crying again."); - finish(); - break; - case 10: - npc(FacialExpression.OLD_NORMAL, "Thanks mister!"); - finish(); - quest.start(player); - break; - case 11: - if (!player.getInventory().containsItem(BALL)) { - player("Not yet."); - next(); - } else { - if (player.getInventory().remove(BALL)) - sendDialogue("You give the ball back."); - setStage(13); - } - break; - case 12: - npc(FacialExpression.OLD_NORMAL, "Well it's in the shed in that garden."); - finish(); - break; - case 13: - npc(FacialExpression.OLD_NORMAL, "Thank you so much!"); - next(); - break; - case 14: - quest.setStage(player, 100); - quest.finish(player); - finish(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 895 }; - } -} diff --git a/Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsDiaryBook.java b/Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsDiaryBook.java deleted file mode 100644 index 47f261d87..000000000 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsDiaryBook.java +++ /dev/null @@ -1,163 +0,0 @@ -package core.game.content.quest.members.witchshouse; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.book.Book; -import core.game.content.dialogue.book.BookLine; -import core.game.content.dialogue.book.Page; -import core.game.content.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/java/core/game/content/quest/members/witchshouse/WitchsHouse.java b/Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsHouse.java deleted file mode 100644 index c9e25b614..000000000 --- a/Server/src/main/java/core/game/content/quest/members/witchshouse/WitchsHouse.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.content.quest.members.witchshouse; - -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; - -/** - * Created for 2009Scape - * User: Ethan Kyle Millard - * Date: March 15, 2020 - * Time: 9:21 AM - */ -@Initializable -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); - } - - @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; - } - } - - @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); - player.getInterfaceManager().closeChatbox(); - player.getPacketDispatch().sendItemZoomOnInterface(2407, 240, 277, 3 + 2); - } - - @Override - public Quest newInstance(Object object) { - return this; - } -} diff --git a/Server/src/main/java/core/game/content/quest/miniquest/barcrawl/BarcrawlManager.java b/Server/src/main/java/core/game/content/quest/miniquest/barcrawl/BarcrawlManager.java deleted file mode 100644 index c1948dff4..000000000 --- a/Server/src/main/java/core/game/content/quest/miniquest/barcrawl/BarcrawlManager.java +++ /dev/null @@ -1,156 +0,0 @@ -package core.game.content.quest.miniquest.barcrawl; - -import core.game.component.Component; -import core.game.node.entity.player.Player; - -import core.game.node.item.Item; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; - -import java.nio.ByteBuffer; - -/** - * Manages the players barcrawl quest. - * @author 'Vexia - */ -public final class BarcrawlManager { - - /** - * The barcrawl card. - */ - public static final Item BARCRAWL_CARD = new Item(455); - - /** - * The name of bars. - */ - public static final String[] NAMES = new String[] { "BlueMoon Inn", "Blueberry's Bar", "Dead Man's Chest", "Dragon Inn", "Flying Horse Inn", "Foresters Arms", "Jolly Boar Inn", "Karamja Spirits bar", "Rising Sun Inn", "Rusty Anchor Inn" }; - - /** - * The component used to draw the completed bars on. - */ - public static final Component COMPONENT = new Component(220); - - /** - * The player. - */ - private final Player player; - - /** - * The bars completed. - */ - private final boolean[] bars = new boolean[10]; - - /** - * If the quest has been started. - */ - private boolean started; - - /** - * Constructs a new {@code BarcrawlManager} {@code Object}. - * @param player the player. - */ - public BarcrawlManager(final Player player) { - this.player = player; - } - - public void parse(JSONObject data){ - started = (boolean) data.get("started"); - JSONArray barsVisisted = (JSONArray) data.get("bars"); - for(int i = 0; i < barsVisisted.size(); i++){ - bars[i] = (boolean) barsVisisted.get(i); - } - } - - /** - * Method used to read the card. - */ - public void read() { - if (isFinished()) { - player.getPacketDispatch().sendMessage("You are too drunk to be able to read the barcrawl card."); - ; - return; - } - player.getInterfaceManager().open(COMPONENT); - drawCompletions(); - } - - /** - * Draws the completed bars on the interface. - */ - private void drawCompletions() { - player.getPacketDispatch().sendString("The Official Alfred Grimhand Barcrawl!", 220, 1); - boolean complete; - for (int i = 0; i < bars.length; i++) { - complete = bars[i]; - player.getPacketDispatch().sendString((complete ? "" : "") + NAMES[i] + " - " + (complete ? "Complete!" : "Not Completed..."), 220, 3 + i); - } - } - - /** - * Completes a bar challenge. - * @param index the index. - */ - public void complete(int index) { - bars[index] = true; - } - - /** - * Checks if the barcrawl quest is completed. - * @return {@code True} if so. - */ - public boolean isFinished() { - for (int i = 0; i < bars.length; i++) { - if (!isCompleted(i)) { - return false; - } - } - return true; - } - - /** - * Resets the bars. - */ - public void reset() { - started = false; - for (int i = 0; i < bars.length; i++) { - bars[i] = false; - } - } - - /** - * Checks if a bar is completed. - * @param index the index. - * @return {@code True} if completed. - */ - public boolean isCompleted(int index) { - return bars[index]; - } - - /** - * Checks if the player has the card. - * @return the card. - */ - public boolean hasCard() { - return player.getInventory().containsItem(BARCRAWL_CARD) || player.getBank().containsItem(BARCRAWL_CARD); - } - - /** - * Gets the started. - * @return The started. - */ - public boolean isStarted() { - return started; - } - - /** - * Sets the started. - * @param started The started to set. - */ - public void setStarted(boolean started) { - this.started = started; - } - - public boolean[] getBars() { - return bars; - } -} diff --git a/Server/src/main/java/core/game/content/quest/miniquest/surok/HuntForSurokPlugin.java b/Server/src/main/java/core/game/content/quest/miniquest/surok/HuntForSurokPlugin.java deleted file mode 100644 index a6ae5157f..000000000 --- a/Server/src/main/java/core/game/content/quest/miniquest/surok/HuntForSurokPlugin.java +++ /dev/null @@ -1,42 +0,0 @@ -package core.game.content.quest.miniquest.surok; - -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.world.map.Location; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; - -/** - * Handles the hunt for surok mini quest. - * @author Vexia - */ - -public class HuntForSurokPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new DakhThoulanAegisDialogue()); - PluginManager.definePlugin(new MishkalunDornDialogue()); - PluginManager.definePlugin(new SilasDahcsnuDialogue()); - PluginManager.definePlugin(new SurokMagisDialogue()); - SceneryDefinition.forId(28780).getHandlers().put("option:use", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "use": - switch (node.getId()) { - case 28780: - player.teleport(new Location(3326, 5469, 0)); - break; - } - break; - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/miniquest/surok/SurokMagisDialogue.java b/Server/src/main/java/core/game/content/quest/miniquest/surok/SurokMagisDialogue.java deleted file mode 100644 index 902d15c18..000000000 --- a/Server/src/main/java/core/game/content/quest/miniquest/surok/SurokMagisDialogue.java +++ /dev/null @@ -1,201 +0,0 @@ -package core.game.content.quest.miniquest.surok; - -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.game.world.map.Location; -import core.game.world.map.build.DynamicRegion; - -/** - * Handles the surok magis dialogue. - * @author Vexia - */ -public class SurokMagisDialogue extends DialoguePlugin { - - /** - * Constructs a new {@Code SurokMagisDialogue} {@Code Object} - * @param player the player. - */ - public SurokMagisDialogue(Player player) { - super(player); - } - - /** - * Constructs a new {@Code SurokMagisDialogue} {@Code Object} - */ - public SurokMagisDialogue() { - /** - * empty. - */ - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SurokMagisDialogue(player); - } - - @Override - public boolean open(Object... args) { - player(player.getUsername() + "! The meddling adventurer."); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player("Surok! What are you doing here?", "How did you-"); - stage++; - break; - case 1: - npc("Escape from Varrock Palace Library? That cruel", "imprisonment you left me in?"); - stage++; - break; - case 2: - player("Well...er..yes."); - stage++; - break; - case 3: - npc("Bah! A mere trifle for a powerful mage such as myself.", "There were plenty of other foolish people to help with", "my plans, you would do well to stay out of my way."); - stage++; - break; - case 4: - player("Stop, Surok! As a member of the Varrock Palace Secret", "Guard, I arrest you! Again!"); - stage++; - break; - case 5: - npc("Ha! I tire of this meaningless drivel. Catch me if you can."); - stage++; - break; - case 6: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 7002, 7136 }; - } - - /** - * Handles a surok cutscene. - * @author Vexia - */ - public static class SurokCutscene extends CutscenePlugin { - - /** - * The surok scene. - */ - private SurokScene scene; - - /** - * Constructs a new {@Code SurokCutscene} {@Code Object} - */ - public SurokCutscene() { - super("Surok Cutscene"); - } - - /** - * Constructs a new {@Code SurokCutscene} {@Code Object} - * @param player the player. - */ - public SurokCutscene(Player player) { - this(); - this.player = player; - } - - @Override - public boolean start(Player player, boolean login, Object... args) { - scene = (SurokScene) args[0]; - region = DynamicRegion.create(scene.getRegionId()); - setRegionBase(); - registerRegion(region.getId()); - return super.start(player, login, args); - } - - @Override - public ActivityPlugin newInstance(Player p) throws Throwable { - return new SurokCutscene(p); - } - - @Override - public Location getStartLocation() { - return base.transform(scene.getStartData()[0], scene.getStartData()[1], 0); - } - - @Override - public Location getSpawnLocation() { - return null; - } - - @Override - public void configure() { - - } - - /** - * Gets the scene. - * @return the scene - */ - public SurokScene getScene() { - return scene; - } - - /** - * Sets the bascene. - * @param scene the scene to set. - */ - public void setScene(SurokScene scene) { - this.scene = scene; - } - - /** - * The surok scene. - * @author Vexia - */ - public static enum SurokScene { - ESCAPE(-1, new int[] {}); - - /** - * The region id. - */ - private final int regionId; - - /** - * The start data. - */ - private final int[] startData; - - /** - * Constructs a new {@Code SurokScene} {@Code Object} - * @param regionId the region id. - * @param startData the start data. - */ - private SurokScene(int regionId, int[] startData) { - this.regionId = regionId; - this.startData = startData; - } - - /** - * Gets the regionId. - * @return the regionId - */ - public int getRegionId() { - return regionId; - } - - /** - * Gets the startData. - * @return the startData - */ - public int[] getStartData() { - return startData; - } - - } - - } -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/CellarMapZone.java b/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/CellarMapZone.java deleted file mode 100644 index 2c2211712..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/CellarMapZone.java +++ /dev/null @@ -1,98 +0,0 @@ -package core.game.content.quest.tutorials.learningtheropes; - -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.map.build.DynamicRegion; -import core.game.world.map.zone.MapZone; -import core.game.world.update.flag.context.Animation; - -/** - * @author Ethan Kyle Millard - * @since Wed, October 10, 2018 @ 1:03 PM - */ -public class CellarMapZone extends MapZone { - - private static final NPC[] NPCS = new NPC[]{ - /** sir vant */ - NPC.create(7938, Location.create(2524, 5005, 0), Direction.NORTH), - /** goblin*/ - NPC.create(7965, Location.create(2524, 4997, 0), Direction.NORTH), - }; - - public static final CellarMapZone INSTANCE = new CellarMapZone(); - - /** - * The region of the zone. - */ - private DynamicRegion region; - - /** - * The base location. - */ - private Location base; - - - /** - * Constructs a new {@code MapZone} {@code Object}. - */ - public CellarMapZone() { - super("Cellar Map Zone", true); - } - - @Override - public void configure() { - region = DynamicRegion.create(10062); - setRegionBase(); - registerRegion(region.getId()); - setNpcs(); - } - - public void create(Player player) { - configure(); - player.teleport(getBase().transform(28, 12, 0)); - } - - private void setNpcs() { - for (NPC n : NPCS) { - n = NPC.create(n.getId(), n.getLocation(), n.getDirection()); - n.setLocation(base.transform(n.getLocation().getLocalX(), n.getLocation().getLocalY(), 0)); - n.setRespawn(false); - n.init(); - n.setWalks(false); - n.animate(new Animation(-1)); - } - } - - /** - * Sets the region base. - */ - private void setRegionBase() { - if (region != null) { - setBase(Location.create(region.getBorders().getSouthWestX(), region.getBorders().getSouthWestY(), 0)); - } - } - - /** - * Gets the base. - * - * @return the base - */ - public Location getBase() { - return base; - } - - /** - * Sets the base. - * - * @param base the base to set. - */ - public void setBase(Location base) { - this.base = base; - } - - public static CellarMapZone get() { - return INSTANCE; - } -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/LTRDragonFightCutscene.java b/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/LTRDragonFightCutscene.java deleted file mode 100644 index 2eb5f557a..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/LTRDragonFightCutscene.java +++ /dev/null @@ -1,355 +0,0 @@ -package core.game.content.quest.tutorials.learningtheropes; - -import core.game.content.activity.ActivityPlugin; -import core.game.content.activity.CutscenePlugin; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -import core.game.node.entity.skill.Skills; -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.state.EntityState; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.build.DynamicRegion; -import core.game.world.map.path.Path; -import core.game.world.map.path.Pathfinder; -import core.game.world.map.zone.ZoneBuilder; -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.CameraContext; -import core.net.packet.out.CameraViewPacket; -import core.plugin.Initializable; - -/** - * @author Ethan Kyle Millard - * @since Mon, October 08, 2018 @ 5:00 PM - */ -@Initializable -public class LTRDragonFightCutscene extends CutscenePlugin { - - private static final NPC[] NPCS = new NPC[] { - /** dragon */ - NPC.create(7943, Location.create(2524, 5015, 0), Direction.SOUTH), - /** sir vant */ - NPC.create(7938, Location.create(2524, 5005, 0), Direction.NORTH), - /** goblin*/ - NPC.create(7964, Location.create(2522, 5000, 0), Direction.NORTH), - }; - - private static final Animation SIR_VANT_BLOCK = Animation.create(9941); - private static final Animation SIR_VANT_STAB = Animation.create(9938); - private static final Animation SIR_VANT_SLASH = Animation.create(9939); - private static final Animation SIR_VANT_BLOCK_FIRE = Animation.create(9942); - private static final Animation SIR_VANT_BIG_SLASH = Animation.create(9943); - private static final Animation DRAGON_ATTACK = Animation.create(9922); - private static final Animation DRAGON_BLOCK = Animation.create(9923); - private static final Animation DRAGON_FIRE_BREATH = Animation.create(9919); - private static final Graphics DRAGON_FIRE_BREATH_GFX = new Graphics(1, 64); - private static final Animation DRAGON_SLASHED = Animation.create(9921); - private static final Animation DRAGON_STANDING_UNCUT = Animation.create(9914); - private static final Animation DRAGON_STANDING_CUT = Animation.create(9915); - private static final Animation DRAGON_STANDING_WOBBLING = Animation.create(9916); - private static final Animation DRAGON_STANDING_CUTTING = Animation.create(9917); - private static final Animation DRAGON_FLYING = Animation.create(9913); - private static final Animation GOBLIN_FALLING = Animation.create(9965); - private static final Animation GOBLIN_PLUNDERING = Animation.create(9963); - private static final Animation GOBLIN_HIT_ON_HEAD= Animation.create(9964); - - - - private final FightPulse fightPulse = new FightPulse(); - - - public LTRDragonFightCutscene() { - this(null); - } - - public LTRDragonFightCutscene(final Player player) { - super("ltr:dragon_fight_cs"); - this.player = player; - } - - @Override - public ActivityPlugin newInstance(Player p) throws Throwable { - return new LTRDragonFightCutscene(p); - } - - @Override - public Location getSpawnLocation() { - return getBase().transform(28, 12, 0); - } - - @Override - public Location getStartLocation() { - return getBase().transform(28, 12, 0); - } - - @Override - public void register() { - super.register(); - } - - @Override - public void open() { - ZoneBuilder.configure(CellarMapZone.get()); - setNpcs(); - GameWorld.getPulser().submit(fightPulse); - player.lock(); - player.getLocks().lockMovement(1000000); - camera(31, 12, -45, 0, 300, 95); - } - - - public final class FightPulse extends Pulse { - - /** - * Represents the counter. - */ - private int counter; - - /** - * Constructs a new {@code FightPulse} {@code Object}. - */ - public FightPulse() { - super(1, player); - } - - @Override - public boolean pulse() { - switch (counter++) { - case 1: - System.out.println("Fight begin"); - getSirVant().getSkills().setStaticLevel(Skills.HITPOINTS, 99); - getSirVant().getSkills().setLifepoints(99); - getDragon().getSkills().setStaticLevel(Skills.HITPOINTS, 99); - getDragon().getSkills().setLifepoints(99); - System.out.println("getGoblin().getLocation().getLocalX() = " + getGoblin().getLocation().getLocalX()); - System.out.println("getGoblin().getLocation().getLocalY() = " + getGoblin().getLocation().getLocalY()); - break; - - case 4: - camera(28, 21, 1, 4, 220, 100); - Path path = Pathfinder.find(getDragon(), getDragon().getLocation().transform(-1, -9, 0), true, Pathfinder.DUMB, (a, b, c) -> 0); - path.walk(getDragon()); - break; - - case 9: - camera(31, 12, -5, 3, 300, 100); - break; - - case 13: - camera(32, 10, -4, 5, 450, 100); - break; - - case 15: - getDragon().animate(DRAGON_ATTACK);//attack - getSirVant().animate(SIR_VANT_BLOCK);//block - getSirVant().getImpactHandler().manualHit(getDragon(), 12, ImpactHandler.HitsplatType.NORMAL); - break; - - case 17: - getDragon().animate(DRAGON_BLOCK); - getSirVant().animate(SIR_VANT_STAB); - getDragon().getImpactHandler().manualHit(getSirVant(), 11, ImpactHandler.HitsplatType.NORMAL); - break; - - case 19: - getDragon().animate(DRAGON_BLOCK); - getSirVant().animate(SIR_VANT_SLASH); - getDragon().getImpactHandler().manualHit(getSirVant(), 8, ImpactHandler.HitsplatType.NORMAL); - break; - - case 21: - getDragon().animate(DRAGON_FIRE_BREATH); - getDragon().graphics(DRAGON_FIRE_BREATH_GFX); - getSirVant().animate(SIR_VANT_BLOCK_FIRE); - getSirVant().getImpactHandler().manualHit(getDragon(), 18, ImpactHandler.HitsplatType.NORMAL); - break; - - case 23: - getSirVant().sendChat("Look out behind you!"); - break; - - case 25: - camera(32, 13, -5, -5, 300, 100); - getGoblin().animate(GOBLIN_FALLING); - break; - case 26: - camera(31, 9, -3, -3, 250, 75); - break; - case 27: - path = Pathfinder.find(getGoblin(), getGoblin().getLocation().transform(2, -3, 0), true, Pathfinder.DUMB, (a, b, c) -> 0); - path.walk(getGoblin()); - break; - - case 30: - getGoblin().sendChat("What can I steal in here?"); - getGoblin().animate(GOBLIN_PLUNDERING); - break; - - case 34: - camera(32, 10, -3, 5, 500, 100); - break; - - case 35: - getDragon().animate(DRAGON_FLYING); - break; - - case 39: - getDragon().animate(DRAGON_STANDING_UNCUT); - break; - case 40: - getDragon().animate(DRAGON_STANDING_CUT); - getSirVant().animate(SIR_VANT_BIG_SLASH); - getDragon().getImpactHandler().manualHit(getSirVant(), 30, ImpactHandler.HitsplatType.NORMAL); - break; - case 42: - getDragon().animate(DRAGON_STANDING_WOBBLING); - break; - - case 45: - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraContext.CameraType.SHAKE, 4, 4, 1200, 4, 4)); - camera(31, 9, -3, -3, 250, 45); - break; - case 46: - getGoblin().face(player); - break; - case 47: - getGoblin().animate(GOBLIN_HIT_ON_HEAD); - break; - case 50: - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraContext.CameraType.SHAKE, 3, 2, 2, 2, 2)); - getGoblin().getStateManager().set(EntityState.STUNNED, 4); - player.face(getGoblin()); - break; - case 53: - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraContext.CameraType.RESET, 5, 2, 450, 100, 0)); - break; - case 54: - camera(28, 5, 2, 6, 450, 100); - break; - case 55: - path = Pathfinder.find(getDragon(), getDragon().getLocation().transform(0, 9, 0), true, Pathfinder.DUMB, (a, b, c) -> 0); - path.walk(getDragon()); - break; - case 60: - System.out.println("Fight end"); - LTRDragonFightCutscene.this.stop(true); - TutorialStage.load(player, 3, false); - CellarMapZone.get().create(player); - return true; - case 61: - break; - } - return !player.isActive(); - } - - @Override - public void stop() { - super.stop(); - clearNpcs(); - } - - /** - * Gets the counter. - * - * @return the counter. - */ - public int getCounter() { - return counter; - } - } - - /** - * Method used to handle the camera. - * - * @param x - * the x offset. - * @param y - * the y offset. - * @param xRot - * the xRotation. - * @param yRot - * the yRotation. - * @Param height the height. - */ - private void camera(int x, int y, int xRot, int yRot, int height, int speed) { - Location loc = base.transform(x, y, 0); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraContext.CameraType.POSITION, loc.getX(), loc.getY(), height, 1, speed)); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraContext.CameraType.ROTATION, loc.getX() + xRot, loc.getY() + yRot, height, 1, speed)); - } - - @Override - public void configure() { - region = DynamicRegion.create(10062); - setRegionBase(); - registerRegion(region.getId()); - } - - private void setNpcs() { - for (NPC n : NPCS) { - n = NPC.create(n.getId(), n.getLocation(), n.getDirection()); - n.setLocation(base.transform(n.getLocation().getLocalX(), n.getLocation().getLocalY(), 0)); - n.setRespawn(false); - n.init(); - n.setWalks(false); - } - } - - @Override - public void stop(boolean fade) { - super.stop(false); - player.teleport(Location.create(2524, 5004, 0)); - PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraContext.CameraType.RESET, 0, 0, 0, 0, 0)); - } - - /** - * Method used to clear all the npcs. - */ - private void clearNpcs() { - GameWorld.getPulser().submit(new Pulse(5) { - @Override - public boolean pulse() { - for (NPC n : region.getPlanes()[0].getNpcs()) { - n.clear(); - } - return true; - } - - }); - } - /** - * Gets the npc. - * - * @param id - * the id. - * @return the npc. - */ - private NPC getNpc(int id) { - for (NPC n : region.getPlanes()[0].getNpcs()) { - if (n.getId() == id) { - return n; - } - } - return null; - } - - private void walk(NPC npc, final Location location) { - Pathfinder.find(npc, location, true, Pathfinder.DUMB).walk(npc); - } - - public NPC getDragon() { - return getNpc(7943); - } - public NPC getSirVant() { - return getNpc(7938); - } - public NPC getGoblin() { - return getNpc(7964); - } - - -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/SirVantDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/SirVantDialogue.java deleted file mode 100644 index 5fc5e3547..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/learningtheropes/SirVantDialogue.java +++ /dev/null @@ -1,127 +0,0 @@ -package core.game.content.quest.tutorials.learningtheropes; - -import core.game.container.impl.EquipmentContainer; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; - -/** - * @author Ethan Kyle Millard - * @since Mon, October 08, 2018 @ 4:19 PM - */ -@Initializable -public class SirVantDialogue extends DialoguePlugin { - - - /** - * Default Constructor - */ - public SirVantDialogue() { - /** - * Leave this empty - */ - } - - public SirVantDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SirVantDialogue(player); - } - - @Override - public boolean open(Object... args) { - int tut_stage = TutorialSession.getExtension(player).getStage(); - System.out.println("tut_stage = " + tut_stage); - switch (tut_stage) { - case 0: - case 1: - case 2: - case 72: - npc(FacialExpression.JOLLY, "My Word! You scared me there, friend. I have no idea", "where you came from, but you have fantastic timing.", "You see, I have come across a dragon."); - setStage(1); - break; - case 3: - player.getEquipment().replace(new Item(9703), EquipmentContainer.SLOT_WEAPON); - player.getEquipment().replace(new Item(9704), EquipmentContainer.SLOT_SHIELD); - npc("I think we have found a job for you, whoever you are.", "Here, take this sword and shield."); - setStage(10); - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - player("A dragon?"); - next(); - break; - case 2: - npc("Oh yes. It's not usually a problem. After all, I am a", "White Knight! I just haven't really had time to prepare:", "food, runes, legions of other White Knights - those sorts", "of things."); - next(); - break; - case 3: - player ("You didn't prepare for facing a dragon?"); - next(); - break; - case 4: - npc ("It's not as of I woke with a dragon-slaying urge! I", "was on my way through Lumbridge when it started", "attacking. It's a rather larger, three-headed variant. Very", "rare."); - next(); - break; - case 5: - player ("So, what can I do?"); - next(); - break; - case 6: - npc ("You can keep back - it's returning!"); - next(); - break; - case 7: - end(); - TutorialStage.load(player, 2, false); - break; - case 10: - npc("I think the value of this property has slumped a little."); - next(); - break; - case 11: - player(FacialExpression.ASKING, "A little? It's got a huge dragon on it!"); - next(); - break; - case 12: - npc(FacialExpression.JOLLY, "I'm sure the owners has no idea when they bought it.", "It would take and interesting individual to deliberately","build a cellar in a dragon's den."); - next(); - break; - case 13: - npc("I believe this dragon has been asleep for a while - at", "least as long as it has taken Lumbridge, the town above","us, to be established. Something must have roused the","dragon - a large noise, tail cramp, the smell of damsels."); - next(); - break; - case 14: - npc("Don't worry, the goblin is stunned, so it can't hurt you", "at the moment. You should know something about","basic combat before tackling it."); - next(); - break; - case 15: - npc("Now, I have to keep watch for the dragon."); - next(); - break; - case 16: - end(); - TutorialStage.load(player, 4, false); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {7938}; - } -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/BasicStarter.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/BasicStarter.java deleted file mode 100644 index 7ea33875e..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/BasicStarter.java +++ /dev/null @@ -1,44 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.PluginManifest; -import core.plugin.PluginType; - -/** - * Basic starter until tutorial is completed. - * - * @author Michael Sasse (https://github.com/mikeysasse/) - */ -@Initializable -@PluginManifest(type = PluginType.LOGIN) -public class BasicStarter implements Plugin { - - private static final int[][] STARTER_ITEMS = { { 1351, 1 }, { 590, 1 }, - { 303, 1 }, { 315, 1 }, { 1925, 1 }, { 1931, 1 }, { 2309, 1 }, - { 1265, 1 }, { 1205, 1 }, { 1277, 1 }, { 1171, 1 }, { 841, 1 }, - { 882, 25 }, { 556, 25 }, { 558, 15 }, { 555, 6 }, { 557, 4 }, - { 559, 2 } }; - - public BasicStarter() { } - - @Override - public Plugin newInstance(Player player) throws Throwable { - if (player.getDetails().getLastLogin() == 0) { - //Redundant if the Tutorial Completion Dialogue is going to clear the bank, inventory and equipment after teleporting to Lumbridge. - //Causes a full inventory on tutorial island when the instructors are giving the player more items. - //The Starter pack array exists in the TutorialCompletionDialogue file... So could this file be deleted? - - /*for (int[] item : STARTER_ITEMS) { - player.getInventory().add(new Item(item[0], item[1])); - }*/ - } - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/BrotherBraceDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/BrotherBraceDialogue.java deleted file mode 100644 index 3a36d5693..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/BrotherBraceDialogue.java +++ /dev/null @@ -1,136 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; - -/** - * Represents the dialogue plugin used for the brother brace npc. - * @author Vexia - */ -@Initializable -public final class BrotherBraceDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BrotherBraceDialogue} {@code Object}. - */ - public BrotherBraceDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BrotherBraceDialogue} {@code Object}. - * @param player the player. - */ - public BrotherBraceDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BrotherBraceDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - switch (TutorialSession.getExtension(player).getStage()) { - case 60: - interpreter.sendDialogues(player, null, "Good day, brother my name's " + player.getUsername() + "."); - stage = 0; - break; - case 62: - interpreter.sendDialogues(npc, null, "This is your Prayer list. Prayers can help a lot in", "combat. Click on the prayer you wish to use to activate", "it and click it again to deactivate it."); - stage = 0; - break; - case 65: - interpreter.sendDialogues(npc, null, "Good. Now you have both menus open, I'll tell you a ", "little about each. You can add people to either list by", "clicking the add button then typing their name into the", "box that appears."); - stage = 0; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (TutorialSession.getExtension(player).getStage()) { - case 60: - switch (stage) { - case 0: - interpreter.sendDialogues(npc, null, "Hello, " + player.getUsername() + ". I'm Brother Brace. I'm here to tell", "you all about Prayer."); - stage = 1; - break; - case 1: - end(); - TutorialStage.load(player, 61, false); - break; - } - break; - case 62: - switch (stage) { - case 0: - interpreter.sendDialogues(npc, null, "Active prayers wil drain your Prayer Points which", "you can recharge by finding an altar or other holy spot", "and praying there."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, null, "As you noticed, most enemies will drop bones when", "defeated. Burying bones by clicking them in your", "inventory will gain you Prayer experience."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, null, "I'm also the community officer 'round here, so it's my", "job to tell you about your friends and ignore list."); - stage = 3; - break; - case 3: - end(); - TutorialStage.load(player, 63, false); - break; - } - break; - case 65: - switch (stage) { - case 0: - interpreter.sendDialogues(npc, null, "You remove people from the lists in the same way. If", "you add someone to your ignore list they will not be", "able to talk to you or send any form of message to", "you."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, null, "Your friends list shows the online status of your", "friends. Friends in the red are offline, friends in green", "are online and on the same server and friends in yellow", "are online but on a different server."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(player, null, "Are there rules on in-game behaviour? "); - stage = 3; - break; - case 3: - interpreter.sendDialogues(npc, null, "Yes, you should read the rules of conduct on the", "website to make sure you do nothing to get yourself", "banned."); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, null, "But in general, always try to be courteous to other", "players - remember the people in the game are real", "people with real feelings."); - stage = 5; - break; - case 5: - interpreter.sendDialogues(npc, null, "If you go 'round being abusive or causing trouble your", "character could end up being the one in trouble"); - stage = 6; - break; - case 6: - interpreter.sendDialogues(player, null, "Okay thanks. I'll bear that in mind."); - stage = 7; - break; - case 7: - end(); - TutorialStage.load(player, 66, false); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 954 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/CombatInstructorDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/CombatInstructorDialogue.java deleted file mode 100644 index a0a722be7..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/CombatInstructorDialogue.java +++ /dev/null @@ -1,166 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the dialogue plugin related to the combat instructor npc. - * @author Vexia - */ -@Initializable -public final class CombatInstructorDialogue extends DialoguePlugin { - - /** - * Represents the items related to the combat instructor. - */ - private static final Item[] ITEMS = new Item[] { new Item(882, 50), new Item(841, 1), new Item(1171, 1), new Item(1277, 1) }; - - /** - * Constructs a new {@code CombatInstructorDialogue} {@code Object}. - */ - public CombatInstructorDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code CombatInstructorDialogue} {@code Object}. - * @param player the player. - */ - public CombatInstructorDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CombatInstructorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - switch (TutorialSession.getExtension(player).getStage()) { - case 44: - interpreter.sendDialogues(player, null, "Hi! My name's " + player.getUsername() + "."); - stage = 0; - break; - case 47: - interpreter.sendDialogues(npc, null, "Very good, but that little butter knife isn't going to ", "protect you much. Here, take these."); - stage = 0; - break; - case 53: - interpreter.sendDialogues(player, null, "I did it! I killed a giant rat! "); - stage = 0; - break; - case 54: - if (!player.getInventory().containItems(841, 882)) { - if (player.getInventory().freeSlots() > 2) { - interpreter.sendDoubleItemMessage(ITEMS[0], ITEMS[1], "The Combat Guide gives you some bronze arrows and a shortbow!"); - player.getInventory().add(ITEMS[0], ITEMS[1]); - stage = 3; - } else { - end(); - TutorialStage.load(player, 54, false); - } - } else { - end(); - TutorialStage.load(player, 54, false); - } - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (TutorialSession.getExtension(player).getStage()) { - case 44: - switch (stage) { - case 0: - interpreter.sendDialogues(npc, null, "Do I look like I care? To me you're just another", "newcomer who thinks they're ready to fight."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, null, "I am Vannaka, the greatest swordsman alive."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, null, "Let's get started by teaching you to wield a weapon."); - stage = 3; - break; - case 3: - end(); - TutorialStage.load(player, 45, false); - break; - } - break; - case 54: - end(); - TutorialStage.load(player, 54, false); - break; - case 47: - switch (stage) { - case 0: - if (!player.getInventory().containItems(1171, 1277)) { - if (player.getInventory().freeSlots() > 2) { - interpreter.sendDoubleItemMessage(1171, 1277, "The Combat Guide gives you a bronze sword and a wooden shield!"); - player.getInventory().add(ITEMS[2]); - player.getInventory().add(ITEMS[3]); - stage = 1; - } - } else { - end(); - TutorialStage.load(player, 48, false); - stage = 1; - } - break; - case 1: - end(); - TutorialStage.load(player, 48, false); - break; - } - break; - case 53: - switch (stage) { - case 0: - interpreter.sendDialogues(npc, null, "I saw, " + player.getUsername() + ". You seem better at this than I", "thought. Now that you have grasped basic swordplay", "let's move on."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, null, "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."); - stage = 2; - break; - case 2: - if (!player.getInventory().containItems(841, 882)) { - if (player.getInventory().freeSlots() > 2) { - interpreter.sendDoubleItemMessage(ITEMS[0], ITEMS[1], "The Combat Guide gives you some bronze arrows and a shortbow!"); - player.getInventory().add(ITEMS[0], ITEMS[1]); - stage = 3; - } else { - end(); - TutorialStage.load(player, 53, false); - } - } else { - end(); - TutorialStage.load(player, 54, false); - } - break; - case 3: - end(); - TutorialStage.load(player, 54, false); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 944 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/FinancialAdvisorDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/FinancialAdvisorDialogue.java deleted file mode 100644 index 598329829..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/FinancialAdvisorDialogue.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; -import core.plugin.Initializable; - -/** - * Represents the financial advisor plugin. - * @author Vexia - */ -@Initializable -public final class FinancialAdvisorDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code FinancialAdvisorPlugin {@code Object}. - */ - public FinancialAdvisorDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FinancialAdvisorPlugin} {@code Object}. - * @param player the player. - */ - public FinancialAdvisorDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FinancialAdvisorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - switch (TutorialSession.getExtension(player).getStage()) { - case 58: - interpreter.sendDialogues(player, null, "Hello. Who are you?"); - stage = 0; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (TutorialSession.getExtension(player).getStage()) { - case 58: - switch (stage) { - case 0: - interpreter.sendDialogues(npc, null, "I'm the Financial Advisor. I'm here to tell people how to", "make money."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, null, "Okay. How can I make money then?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, null, "How you can make money? Quite."); - stage = 3; - break; - case 3: - interpreter.sendDialogues(npc, null, "Well there are three basic ways of making money here:", "combat, quests, and trading. I will talk you through each", "of them very quickly."); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, null, "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."); - stage = 5; - break; - case 5: - interpreter.sendDialogues(npc, null, "Now, the next way to earn money quickly is by quests", "Many people on " + GameWorld.getSettings().getName() + " have things they need", "doing, which they will reward you for."); - stage = 6; - break; - case 6: - interpreter.sendDialogues(npc, null, "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."); - stage = 7; - break; - case 7: - interpreter.sendDialogues(npc, null, "Well that about covers it. Come back if you'd like to go", "over this again."); - stage = 8; - break; - case 8: - end(); - TutorialStage.load(player, 59, false); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 947 }; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/MasterChefDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/MasterChefDialogue.java deleted file mode 100644 index ed08ab6a9..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/MasterChefDialogue.java +++ /dev/null @@ -1,168 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; - -/** - * Handles the master chef dialogue plugin. - * @author 'Vexia - */ -@Initializable -public class MasterChefDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code MasterChefDialogue} {@code Object} - */ - public MasterChefDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MasterChefDialogue} {@code Object} - * @param player the player. - */ - public MasterChefDialogue(Player player) { - super(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - int tut_stage = TutorialSession.getExtension(player).getStage(); - switch (tut_stage) { - case 18: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.")); - break; - case 20: - if (player.getInventory().containsAll(1933,1929)) { - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Mix together the flour and water to form a dough.")); - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Mix together the flour and water to form a dough.")); - stage = 1; - - } else if (player.getInventory().containsItem(new Item(2307))) { - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "You already have some dough, no need", "to make more.")); - stage = 1; - - } else if (player.getInventory().containsItem(new Item(2307))) { - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "You already have some dough, no need", "to make more.")); - stage = 1; - /* Disabled for now, not sure what is supposed to be happening here, but the else can't be here - } else { - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "I see you have lost your pot of flour and bucket of water,", "No worries i will supply you with more.")); - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Mix together the flour and water to form a dough.")); - stage = 1; - */ - } else if (player.getInventory().containsItem(new Item(2307))) { - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You already have some dough, no need", "to make more.")); - stage = 1; - } else { - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I see you have lost your pot of flour and bucket of water,", "No worries i will supply you with more.")); - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "I see you have lost your pot of flour and bucket of water,", "No worries i will supply you with more.")); - if (player.getInventory().freeSlots() >= 2) { - player.getInventory().add(new Item(1933)); - player.getInventory().add(new Item(1929)); - stage = 1; - } else { - Component.setUnclosable(player, interpreter.sendDialogue("You don't have enough inventory space.")); - stage = 99; - } - break; - } - - case 19: - if (!player.getInventory().contains(1929, 1) && !player.getInventory().containItems(1933)) { - if (player.getInventory().hasSpaceFor(new Item(1929, 1)) && player.getInventory().hasSpaceFor(new Item(1933, 1))) { - Component.setUnclosable(player, interpreter.sendDoubleItemMessage(1929, 1933, "The Cooking Guide gives you a bucket of water and a pot of flour.")); - player.getInventory().add(new Item(1933)); - player.getInventory().add(new Item(1929)); - stage = 4; - } - } else { - end(); - TutorialStage.load(player, 19, false); - } - stage = 4; - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - int tut_stage = TutorialSession.getExtension(player).getStage(); - switch (tut_stage) { - case 20: - switch (stage) { - case 1: - end(); - TutorialStage.load(player, 20, false); - break; - case 99: - TutorialStage.load(player, 20, false); - break; - } - break; - case 19: - switch (stage) { - case 4: - end(); - TutorialStage.load(player, 19, false); - break; - } - break; - case 18: - switch (stage) { - case 0: - Component.setUnclosable(player, interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I already know how to cook. Brynna taught me just", "now.")); - stage = 1; - break; - case 1: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.")); - stage = 2; - break; - case 2: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "And no fine meal is complete without good music, so", "we'll cover that while you're here too.")); - stage = 3; - break; - case 3: - if (!player.getInventory().contains(1929, 1) && !player.getInventory().containItems(1933)) { - if (player.getInventory().hasSpaceFor(new Item(1929, 1)) && player.getInventory().hasSpaceFor(new Item(1933, 1))) { - Component.setUnclosable(player, interpreter.sendDoubleItemMessage(1929, 1933, "The Cooking Guide gives you a bucket of water and a pot of flour.")); - player.getInventory().add(new Item(1933)); - player.getInventory().add(new Item(1929)); - stage = 4; - } - } else { - end(); - TutorialStage.load(player, 19, false); - } - stage = 4; - break; - case 4: - end(); - TutorialStage.load(player, 19, false); - break; - } - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MasterChefDialogue(player); - } - - @Override - public int[] getIds() { - return new int[] { 942 }; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/MiningInstructorDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/MiningInstructorDialogue.java deleted file mode 100644 index 7ec019f51..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/MiningInstructorDialogue.java +++ /dev/null @@ -1,188 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Represents the mining instructor dialogue. - * @author Vexia - */ -@Initializable -public final class MiningInstructorDialogue extends DialoguePlugin { - - /** - * Represents the hammer item. - */ - private static final Item HAMMER = new Item(2347, 1); - - /** - * Represents the pickaxe item. - */ - private static final Item PICKAXE = new Item(1265); - - /** - * Constructs a new {@code MiningInstructorDialogue} {@code Object}. - */ - public MiningInstructorDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MiningInstructorDialogue} {@code Object}. - * @param player the player. - */ - public MiningInstructorDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MiningInstructorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - switch (TutorialSession.getExtension(player).getStage()) { - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - break; - case 34: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I prospected both types of rock! One set contains tin", "and the other has copper ore inside."); - break; - case 35: - if (!player.getInventory().contains(1265, 1)) { - if (player.getInventory().hasSpaceFor(PICKAXE)) { - interpreter.sendItemMessage(1265, "Dezzick gives you a bronze pickaxe!"); - player.getInventory().add(PICKAXE); - stage = 3; - } else { - end(); - TutorialStage.load(player, 35, false); - } - } else { - end(); - TutorialStage.load(player, 35, false); - } - break; - case 40: - interpreter.sendDialogues(player, null, "How do I make a weapon out of this?"); - stage = 1; - break; - case 41: - if (!player.getInventory().contains(2347, 1)) { - if (player.getInventory().hasSpaceFor(HAMMER)) { - interpreter.sendItemMessage(2347, "Dezzick gives you a hammer!"); - player.getInventory().add(HAMMER); - stage = 3; - } - } else { - end(); - TutorialStage.load(player, 41, false); - } - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (TutorialSession.getExtension(player).getStage()) { - case 30: - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You can call me " + player.getUsername() + "."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ok then, " + player.getUsername() + ". My name is Dezzick and I'm a", "miner by Trade. Let's prospect some of those rocks."); - stage = 2; - break; - case 2: - end(); - TutorialStage.load(player, 31, false); - break; - } - break; - case 35: - end(); - TutorialStage.load(player, 35, false); - break; - case 40: - switch (stage) { - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Okay, I'll show you how to make a dagger out of it.", "You'll be needing this.."); - stage = 2; - break; - case 2: - if (!player.getInventory().contains(2347, 1)) { - if (player.getInventory().hasSpaceFor(HAMMER)) { - interpreter.sendItemMessage(2347, "Dezzick gives you a hammer!"); - player.getInventory().add(HAMMER); - stage = 3; - } - } else { - end(); - TutorialStage.load(player, 41, false); - } - break; - case 3: - end(); - TutorialStage.load(player, 41, false); - break; - } - break; - case 41: - end(); - TutorialStage.load(player, 41, false); - break; - case 34: - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Absolutely right, " + player.getUsername() + ". These two ore types can be", "Smelted together to make bronze."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); - stage = 2; - break; - case 2: - if (!player.getInventory().contains(1265, 1)) { - if (player.getInventory().hasSpaceFor(PICKAXE)) { - interpreter.sendItemMessage(1265, "Dezzick gives you a bronze pickaxe!"); - player.getInventory().add(PICKAXE); - stage = 3; - } else { - end(); - TutorialStage.load(player, 34, false); - } - } else { - end(); - TutorialStage.load(player, 35, false); - } - break; - case 3: - end(); - TutorialStage.load(player, 35, false); - break; - case 99: - end(); - TutorialStage.load(player, 34, false); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 948 }; - } -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/QuestGuideDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/QuestGuideDialogue.java deleted file mode 100644 index e771b9ab7..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/QuestGuideDialogue.java +++ /dev/null @@ -1,93 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; - -/** - * Handles the quest guide. - * @author Vexia - */ -@Initializable -public class QuestGuideDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code QuestGuide} {@code Object}. - */ - public QuestGuideDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code QuestGuideDialogue} {@code Object} - * @param player the player. - */ - public QuestGuideDialogue(Player player) { - super(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - switch (TutorialSession.getExtension(player).getStage()) { - case 27: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah. Welcome adventurer. I'm here to tell you all about", "quests. Lets start by opening the Quest list.")); - stage = 0; - break; - case 28: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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", "means you have not started them yet.")); - break; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (TutorialSession.getExtension(player).getStage()) { - case 27: - switch (stage) { - case 0: - Component.setUnclosable(player, interpreter.sendPlaneMessageWithBlueTitle("Open the Quest Journal.", "Click on the flashing icon next to your inventory.", "", "", "")); - player.getConfigManager().set(1021, 3); - player.getInterfaceManager().openTab(new Component(274)); - break; - } - break; - case 28: - switch (stage) { - case 0: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.")); - stage = 1; - break; - case 1: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.")); - stage = 2; - break; - case 2: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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", "under my house.")); - stage = 3; - break; - case 3: - end(); - TutorialStage.load(player, 29, false); - break; - } - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new QuestGuideDialogue(player); - } - - @Override - public int[] getIds() { - return new int[] { 949 }; - } -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/RSGuideDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/RSGuideDialogue.java deleted file mode 100644 index f4d67ffa1..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/RSGuideDialogue.java +++ /dev/null @@ -1,243 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; -import core.plugin.Initializable; - -/** - * Hanldes the 2009scape guide's dialogue. - * @author Vexia - * @author Splinter - */ -@Initializable -public class RSGuideDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code 2009scapeGuideDialogue} {@code Object}. - */ - public RSGuideDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code 2009scapeGuideDialogue} {@code Object}. - * @param player The player. - */ - private RSGuideDialogue(Player player) { - super(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - int tut_stage = TutorialSession.getExtension(player).getStage(); - if(tut_stage < TutorialSession.MAX_STAGE){ - switch (tut_stage) { - case 0: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings! I see you are a new arrival to this land. My", "job is to welcome all new visitors. So welcome!")); - return true; - case 1: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You will notice a flashing icon of a spanner; please click", "on this to continue the tutorial.")); - return true; - case 2: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm glad you're making progress!")); - stage = 1; - return true; - } - } else { - if(player.getSkills().getTotalLevel() < 300){ - interpreter.sendDialogues(npc, null, "Greetings, "+player.getUsername()+". Welcome to "+GameWorld.getSettings().getName()+".", "The flashing icon above my head will disappear once", "you've reached a total level of three hundred."); - } else { - interpreter.sendDialogues(npc, null, "Greetings, "+player.getUsername()+". Welcome to "+GameWorld.getSettings().getName()+"."); - } - stage = 10; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - final int tut_stage = TutorialSession.getExtension(player).getStage(); - if(tut_stage < TutorialSession.MAX_STAGE){ - switch (tut_stage) { - case 0: - switch (stage) { - case 0: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You have already learned the first thing needed to", "succeed in this world: talking to other people!")); - stage = 1; - break; - case 1: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.")); - stage = 2; - break; - case 2: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I would also suggest reading through some of the", "supporting information on the website. There you can", "find the Knowledge Base, which contains all the", "additional information you're ever likely to need. It also")); - stage = 3; - break; - case 3: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "contains maps and helpful tips to help you on your", "journey.")); - stage = 4; - break; - case 4: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You will notice a flashing icon of a wrench, please click", "on this to continue the tutorial.")); - player.getInterfaceManager().openTab(new Component(261)); - player.getConfigManager().set(1021, 12); - stage = 5; - break; - case 5: - end(); - TutorialStage.load(player, 1, false); - stage = 1; - return true; - } - break; - case 2: - switch (stage) { - case 1: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "To continue the tutorial go through that door over", "there and speak to your first instructor!")); - stage = 2; - break; - case 2: - end(); - TutorialStage.load(player, 3, false); - stage = 1; - break; - } - break; - case 1: - end(); - break; - } - } else { - switch(stage){ - case 10: - interpreter.sendDialogues(npc, null, "I am the "+GameWorld.getSettings().getName()+" guide. I offer free and helpful assistance", "for newcomers. What would you like to ask about?"); - stage++; - break; - case 11: - interpreter.sendOptions("Select a Question", "Money making", "Getting around", "List of game content"); - stage++; - break; - case 12: - switch(buttonId){ - case 1: - interpreter.sendDialogues(player, null, "I'd like to ask about money-making techniques."); - stage = 13; - break; - case 2: - interpreter.sendDialogues(player, null, "I'd like to know how to get around on "+GameWorld.getSettings().getName()+"."); - stage = 18; - break; - case 3: - interpreter.sendDialogues(player, null, "I'd like to know about all the content in "+GameWorld.getSettings().getName()+"."); - stage = 25; - break; - } - break; - case 13: - interpreter.sendDialogues(npc, null, "That is one of the most commonly asked questions.", "First of all, there are many different ways to make large", "amounts of coins in "+GameWorld.getSettings().getName()+". There's hundreds", "of hours of emulated content awaiting you."); - stage = 14; - break; - case 14: - interpreter.sendDialogues(npc, null, "Many players prefer to start with leveling their Slayer skill.", "The skill is profitable because of the vast amount of items","that come with defeating monsters in combat. Speak", "to Turael to the east. He is the lowest level"); - stage = 15; - break; - case 15: - interpreter.sendDialogues(npc, null, "Slayer Master available. He will get you started with Slayer.", "The other Slayer Masters may be found spread through", "out the game world."); - stage = 16; - break; - case 16: - interpreter.sendDialogues(npc, null, "As for other money-making methods, consider all of the other", "skills available to you. You may chop down Magic trees and","sell the valuable logs to players, or you may even catch", "sharks and sell them for over one thousand gold pieces each."); - stage = 17; - break; - case 17: - interpreter.sendDialogues(npc, null, GameWorld.getSettings().getName()+" is an economy-driven server. All gathering skills", "are highly profitable and sought after. Herblore and", "Farming, for example, are guaranteed to make you", "profit."); - stage = 11; - break; - case 18: - interpreter.sendDialogues(npc, null, "A good question indeed. Traveling is the key to"," experiencing all the game content that "+GameWorld.getSettings().getName()+" has to offer."); - stage = 19; - break; - case 19: - interpreter.sendDialogues(npc, null, "First of all, you may use your spellbook to teleport to", "major cities. There are three fully working spellbooks", "available to you. However, they require a Magic level", "in order to be used."); - stage = 20; - break; - case 20: - interpreter.sendDialogues(npc, null, "Next we have teletabs. They may be bought by accessing", "the green teleporter to the west of me.", "These tablets are very convenient and only require", "a single inventory space."); - stage = 21; - break; - case 21: - interpreter.sendDialogues(npc, null, "We also have Spirit trees. These talkative trees are", "willing to take you to a few odd locations."); - stage = 22; - break; - case 22: - interpreter.sendDialogues(npc, null, "Next up is the charter system. You make take a ferry boat", "to many coastal cities like Port Phasmatys and", "Catherby."); - stage = 23; - break; - case 23: - interpreter.sendDialogues(npc, null, "We also have the convenient Gnome glider system.", "The friendly Gnomes of "+GameWorld.getSettings().getName()+" offer their services","completely free of charge."); - stage = 24; - break; - case 24: - interpreter.sendDialogues(npc, null, "Finally, we have the green teleporter located in the", "Grand Exchange and Fairy rings.", "All of these teleportation methods should assist you", "in traveling the wide-open world of "+GameWorld.getSettings().getName()+"."); - stage = 50; - break; - case 25: - interpreter.sendDialogues(npc, null, "First up, we have our emulated quests. You may", "type ::quests in your chatbox in order to view a full", "list of ones that fully work.", "I will now begin listing off a lot of other game content."); - stage = 26; - break; - case 26: - interpreter.sendDialogue("Current OSRS content:", "Rune packs, the Zamorakian Hasta, Lava dragons,","Boss pets, Ecumenical keys, Staff of the Dead,", "Armadyl C'bow, colored Dark bows, and the Dragon defender."); - stage = 27; - break; - case 27: - interpreter.sendDialogue("Boss content:", "The Tz-Tok Jad, King Black Dragon, Dagannoth kings, Bork,","Godwars Dungeon bosses, Giant Mole, Kalphite Queen,", "Chaos elemental, and the Corporeal beast."); - stage = 28; - break; - case 28: - interpreter.sendDialogue("Minigame content:", "Barrows, Bounty Hunter, Clan Wars, Duel Arena,","Gnomecopters, Magic Training Area, Falador Partyroom,","Pest Control, Puro Puro, The Stronghold of Security,"); - stage = 29; - break; - case 29: - interpreter.sendDialogue("Minigame content continued:", "The Stronghold of Player Safety, the Warrior's Guild,", "Sorceress' Garden, and the Fight Pits."); - stage = 30; - break; - case 30: - interpreter.sendDialogue("Skill content:", "All skills except Construction are fully functional."); - stage = 31; - break; - case 31: - interpreter.sendDialogue("Misc content:", "Achievement Diaries, Grand Exchange, Clue scrolls,", "Game sounds, Random events, Lootshare, Slayer points,","Cutscenes, Bank tabs,"); - stage = 32; - break; - case 32: - interpreter.sendDialogues(npc, null, "... and simply too much else to list.", "There's lots of content out there for you to explore.", "Don't just take my word for it. Experience our perfected", "game content for yourself."); - stage = 11; - break; - case 50: - interpreter.sendDialogues(npc, null, "Oh... I almost forgot.", "Our world also has unique teleport methods like the", "convenient ectophial and enchanted rings and amulets."); - stage = 11; - break; - - } - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new RSGuideDialogue(player); - } - - @Override - public int[] getIds() { - return new int[] { 945 }; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/SurvivalExpertDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/SurvivalExpertDialogue.java deleted file mode 100644 index c76bf8da9..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/SurvivalExpertDialogue.java +++ /dev/null @@ -1,173 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.plugin.Initializable; -import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Handles survival expert dialogue. - * @author 'Vexia - */ -@Initializable -public class SurvivalExpertDialogue extends DialoguePlugin { - - /** - * The NPC ids that use this dialogue plugin. - */ - private static final int[] NPC_IDS = { 943 }; - - public SurvivalExpertDialogue() { - /* - * empty. - */ - } - - public SurvivalExpertDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return NPC_IDS; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - int tut_stage = TutorialSession.getExtension(player).getStage(); - switch (tut_stage) { - case 6: - end(); - TutorialStage.load(player, 6, false); - break; - case 12: - end(); - TutorialStage.load(player, 15, false); - break; - case 4: - switch (stage) { - case 0: - Component.setUnclosable(player, interpreter.sendDoubleItemMessage(590, 1351, "The Surivival Guide gives you a tinderbox and a bronze axe!")); - player.getInventory().add(new Item(590, 1)); - player.getInventory().add(new Item(1351, 1)); - stage = 1; - break; - case 1: - end();// Component.setUnclosable(player, - TutorialStage.load(player, 5, false); - break; - } - break; - case 11: - switch (stage) { - case 0: - Component.setUnclosable(player, interpreter.sendItemMessage(303, "The Survival Guide gives you a net!")); - player.getInventory().add(new Item(303)); - stage = 1; - break; - case 1: - end(); - TutorialStage.load(player, 12, false); - break; - } - break; - case 8: - end(); - TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), false); - break; - case 14: - switch (stage) { - case 0: - end(); - TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), false); - break; - } - break; - case 15: - end(); - TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), false); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SurvivalExpertDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - int tut_stage = TutorialSession.getExtension(player).getStage(); - switch (tut_stage) { - case 4: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.")); - break; - case 6: - if (!player.getInventory().contains(1351, 1) && player.getInventory().contains(590, 1)) { - Component.setUnclosable(player, interpreter.sendItemMessage(1351, "The Surivival Guide gives you a bronze axe.")); - player.getInventory().add(new Item(1351)); - } else if (!player.getInventory().contains(590, 1) && player.getInventory().contains(1351, 1)) { - player.getInventory().add(new Item(590, 1)); - Component.setUnclosable(player, interpreter.sendDialogue("The Surivival Guide gives you a tinderbox.")); - } else if (!player.getInventory().contains(590, 1) && !player.getInventory().contains(1351, 1)) { - Component.setUnclosable(player, interpreter.sendDoubleItemMessage(590, 1351, "The Surivival Guide gives you a tinderbox and a bronze axe!")); - player.getInventory().add(new Item(590, 1)); - player.getInventory().add(new Item(1351, 1)); - } - stage = 90; - break; - case 11: - Component.setUnclosable(player, interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.")); - break; - case 8: - if (!player.getInventory().contains(1351, 1) && player.getInventory().contains(590, 1)) { - Component.setUnclosable(player, interpreter.sendItemMessage(1351, "The Surivival Guide gives you a bronze axe.")); - player.getInventory().add(new Item(1351)); - } else if (!player.getInventory().contains(590, 1) && player.getInventory().contains(1351, 1)) { - player.getInventory().add(new Item(590, 1)); - Component.setUnclosable(player, interpreter.sendDialogue("The Surivival Guide gives you a tinderbox.")); - } else { - end(); - TutorialStage.load(player, 8, false); - } - break; - case 14: - if (!player.getInventory().contains(1351, 1) && player.getInventory().contains(590, 1)) { - Component.setUnclosable(player, interpreter.sendItemMessage(1351, "The Surivival Guide gives you a bronze axe.")); - player.getInventory().add(new Item(1351)); - } else if (!player.getInventory().contains(590, 1) && player.getInventory().contains(1351, 1)) { - player.getInventory().add(new Item(590, 1)); - Component.setUnclosable(player, interpreter.sendDialogue("The Surivival Guide gives you a tinderbox.")); - } else { - end(); - TutorialStage.load(player, 14, false); - } - break; - case 12: - if (!player.getInventory().contains(303, 1)) { - player.getInventory().add(new Item(303, 1)); - Component.setUnclosable(player, interpreter.sendDialogue("The Surivival Guide gives you a net.")); - } else { - end(); - TutorialStage.load(player, 12, false); - } - break; - case 15: - if (!player.getInventory().contains(303, 1)) { - player.getInventory().add(new Item(303, 1)); - Component.setUnclosable(player, interpreter.sendDialogue("The Surivival Guide gives you a net.")); - } else { - end(); - TutorialStage.load(player, 15, false); - } - break; - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialBook.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialBook.java deleted file mode 100644 index 7856d83ef..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialBook.java +++ /dev/null @@ -1,97 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.book.Book; -import core.game.content.dialogue.book.BookLine; -import core.game.content.dialogue.book.Page; -import core.game.content.dialogue.book.PageSet; -import core.game.node.entity.player.Player; - -/** - * Handles the introductory after-tutorial book. - * @author Splinter - */ -@Initializable -public final class TutorialBook extends Book { - - /** - * Represents the book id - */ - public static int ID = 387454; - - /** - * Represents the array of pages for this book. - */ - private static final PageSet[] PAGES = new PageSet[] { - new PageSet( - new Page( - new BookLine("THIS BOOK NEEDS TO HAVE ITS TEXT CHANGED Starting off", 55), new BookLine("", 56), new BookLine(" It is a good idea", 57), new BookLine("to raid the two strongholds", 58), - new BookLine("in Barbarian Village in order", 59), new BookLine("to gain a ludicrous amount of", 60), new BookLine("coins to start with. Also", 61), - new BookLine("Consider collecting skilling", 62), new BookLine("materials to sell in bulk", 63), new BookLine("on the Grand Exchange.", 64), - new BookLine("Raw meat, dropped from", 65)), - new Page( - new BookLine("cows, is useful in the", 66), new BookLine("Summoning skill and can", 67), - new BookLine("be easily sold for coins.", 68), new BookLine(" ", 69), new BookLine(" Credits/Double XP", 70), new BookLine("Credits are our way", 71), - new BookLine("of thanking you for voting.", 72), new BookLine("You may spend credits", 73), new BookLine("in the online store at:", 74), - new BookLine("www.2009Scape-pk.com/shop or", 75), new BookLine("by using our in-game ::shop.", 76))), - new PageSet( - new Page( - new BookLine(" Suggestions", 55), - new BookLine("Please suggest new features", 56), new BookLine("you'd like to see via", 57), new BookLine("the forums located at", 58), - new BookLine("www.2009Scape-pk.com.", 59), new BookLine("We wholeheartedly look over", 60), new BookLine("and consider all suggestions.", 61))), - - }; - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public TutorialBook(final Player player) { - super(player, "Early Guide Book", 1856, PAGES); - } - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public TutorialBook() { - /** - * empty. - */ - } - - @Override - public void finish() { - } - - @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); - 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 TutorialBook(player); - } - - @Override - public int[] getIds() { - return new int[] { ID }; - } -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialCompletionDialogue.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialCompletionDialogue.java deleted file mode 100644 index e090ad81c..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialCompletionDialogue.java +++ /dev/null @@ -1,397 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.HintIconManager; -import core.game.node.entity.player.link.IronmanMode; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; -import core.plugin.Initializable; - -/** - * Handles the tutorial completition dialogue (skippy, magic instructor) - * @author Vexia - * @author Splinter - * - */ -@Initializable -public class TutorialCompletionDialogue extends DialoguePlugin { - - /** - * The starter pack of items. - */ - private static final Item[] STARTER_PACK = new Item[] { new Item(1351, 1), new Item(590, 1), new Item(303, 1), new Item(315, 1), new Item(1925, 1), new Item(1931, 1), new Item(2309, 1), new Item(1265, 1), new Item(1205, 1), new Item(1277, 1), new Item(1171, 1), new Item(841, 1), new Item(882, 25), new Item(556, 25), new Item(558, 15), new Item(555, 6), new Item(557, 4), new Item(559, 2) }; - private static final Item[] STARTER_BANK = new Item[] { new Item( 995, 25)}; - - /** - * Represents the rune items. - */ - private static final Item[] RUNES = new Item[] { new Item(556, 5), new Item(558, 5) }; - - /** - * If ironman is enabled. - */ - private static final boolean IRONMAN = true; - - /** - * Constructs a new {@code TutorialCompletionDialogue} {@code Object} - */ - public TutorialCompletionDialogue() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code TutorialCompletionDialogue} {@code Object} - * @param player the player. - */ - public TutorialCompletionDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TutorialCompletionDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (npc == null) { - return true; - } - - //Magic Instructor Dialogue - if (npc.getId() == 946) { - switch (TutorialSession.getExtension(player).getStage()) { - case 67: - interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hello."); - stage = 0; - return true; - case 69: - interpreter.sendDialogues(946, FacialExpression.NEUTRAL, "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."); - stage = 0; - return true; - case 70: - if (!player.getInventory().contains(556, 1) && !player.getInventory().contains(558, 1)) { - if (player.getInventory().hasSpaceFor(RUNES[0]) && player.getInventory().hasSpaceFor(RUNES[1])) { - interpreter.sendDoubleItemMessage(RUNES[0], RUNES[1], "Terrova gives you five air runes and five mind runes!"); - player.getInventory().add(RUNES); - stage = 3; - } else { - GroundItemManager.create(RUNES, player.getLocation(), player); - stage = 3; - } - } else { - end(); - TutorialStage.load(player, 70, false); - } - return true; - case 71: - interpreter.sendDialogues(946, FacialExpression.NEUTRAL, "Well you're all finished here now. I'll give you a", "reasonable number of runes when you leave."); - stage = -2; - return true; - } - } - - //Skippy Dialogue used whenever the Player talks to Skippy during the tutorial - else { - interpreter.sendDialogues(npc, FacialExpression.SUSPICIOUS, "*psst.* Hey, do you want to skip the tutorial?", "I can send you straight to the mainland, easy."); - stage = 1; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - //Final Dialogue from the either the Magic Instructor or Skippy - if (npc.getId() == 2796 || TutorialSession.getExtension(player).getStage() >= 71) { - switch (stage) { - case -2: //Only used by the Magic Instructor - interpreter.sendOptions("Leave Tutorial Island?", "Yes.", "No."); - stage = -1; - break; - case -1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(npc, FacialExpression.ASKING,"One more thing: Would you like to", "be an Ironman account?"); - stage = 501; - break; - case 2: - end(); - TutorialStage.load(player, 71, false); - break; - } - break; - - //Dialogue used by Skippy to leave Tutorial Island - case 1: - interpreter.sendOptions("What would you like to say?", "Leave Tutorial Island.", "Can I decide later?", "I'll stay here for the Tutorial."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(npc, FacialExpression.ASKING,"One more thing: Would you like to", "be an Ironman account?"); - stage = 501; - if (!IRONMAN) { - stage = 1205; - } - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.THINKING, "Can I decide later?."); - stage = 39; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I'll stay here for the Tutorial."); - stage = 40; - break; - } - break; - case 39: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Yes. Talk to me at any point during this tutorial", "if you change your mind."); - stage = 99; - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Very well. Have fun, adventurer."); - stage = 99; - break; - - //Continuation of Ironman Dialogues - case 501: - player.removeAttribute("ironMode"); - player.removeAttribute("ironPermanent"); - options("Yes, please.", "What is an Ironman account?", "No, thanks."); - stage++; - break; - case 502: - switch (buttonId) { - case 1: - interpreter.sendDialogues(npc, FacialExpression.HAPPY,"Yes, please."); - stage = 506; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.ASKING,"What is an Ironman account?"); - stage = 530; - break; - case 3: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"No, thanks."); - stage = 534; - break; - } - break; - case 506: - interpreter.sendOptions("Select a Mode", "Standard", "Hardcore", "Ultimate", "Go back."); - stage++; - break; - case 507: - switch (buttonId) { - case 1: - case 2: - case 3: - npc("You have chosen the " + (buttonId == 1 ? "Standard" : (buttonId == 2 ? "Hardcore" : "Ultimate")) + " Ironman mode."); - player.setAttribute("ironMode", IronmanMode.values()[buttonId]); - player.getSavedData().getActivityData().setHardcoreDeath(false); - stage = 516; - break; - case 4: - player.removeAttribute("ironMode"); - player.removeAttribute("ironPermanent"); - options("Yes, please.", "What is an Ironman account?", "No, thanks."); - stage = 502; - break; - } - break; - case 516: - player.getIronmanManager().setMode(player.getAttribute("ironMode")); - MSPacketRepository.sendInfoUpdate(player); - - //Split Dialogue depending on if the Player is talking to the Magic Instructor or Skippy. - if (npc.getId() == 946) { - interpreter.sendDialogues(npc, FacialExpression.HAPPY,"Congratulations, you have setup your Ironman account.", "Let's continue."); - stage = 1199; - break; - } else { - interpreter.sendDialogues(npc, FacialExpression.FRIENDLY,"Congratulations, you have setup your Ironman account.", "You will travel to the mainland in a moment."); - stage = 1204; - break; - } - - //About Ironman mode - case 530: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"An Iron Man account is a style of playing where players", "are completely self-sufficient."); - stage++; - break; - case 531: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"A Standard Ironman does not receive items or", "assistance from other players. They cannot trade, stake,", "receive PK loot, scavenge dropped items, nor play", "certain minigames."); - stage++; - break; - case 532: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"In addition to Standard Ironman restrictions,", "Hardcore Ironmen only have one life. In the event of","a dangerous death, a player will be downgraded to a", "Standard Ironman, and their stats frozen on the hiscores."); - stage++; - break; - case 533: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"For the ultimate challenge, players who choose the", "Ultimate Ironman mode cannot use banks, nor", "retain any items on death in dangerous areas."); - stage = 501; - break; - - case 534: - // From saying no thanks to being an ironman. - //Split Dialogue depending on if the Player is talking to the Magic Instructor or Skippy. - if (npc.getId() == 946) { - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"Very well.", "Let's continue."); - stage = 1199; - break; - } else { - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"Very well.", "You will travel to the mainland in a moment."); - stage = 1204; - break; - } - //Final Regards from the Magic Instructor - case 1199: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "When you get to the mainland you will find yourself in", "the town of Lumbridge. If you want some ideas on", "where to go next talk to my friend the Lumbridge", "Guide. You can't miss him; he's holding a big staff with"); - stage++; - break; - case 1200: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "a question mark on the end. He also has a white beard","and carries a rucksack full of scrolls. There are also","many tutors willing to teach you about the many skills","you could learn."); - stage++; - break; - case 1201: //TODO: Needs to be the Lumbridge Guide Icon... Not sure of the ID or interface. - interpreter.sendItemMessage(RUNES[1], "When you get to Lumbridge, look for this icon on you","mini-map. The Lumbridge Guide or one of the other","tutors should be near there. The Lumbridge","Guide should be standing slightly to the north-east of"); - stage++; - break; - case 1202: //TODO: Needs to be the Lumbridge Guide Icon... Not sure of the ID or interface. - interpreter.sendItemMessage(RUNES[1], "the castle's courtyard and the others you will find","scattered around Lumbridge."); - stage++; - break; - case 1203: - interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "If all else fails, visit the "+ GameWorld.getSettings().getName()+ " website for a whole","chestload of information on quests, skills, and minigames","as well as a very good starter's guide."); - stage++; - break; - - //Final words, if using Skippy it should go straight to this - //Could be removed to try and keep the 'nostalgic' feeling of Tutorial Island. - case 1204: - npc("Keep in mind: our server has more content than any other", "server ever released. There's hundreds of hours of", "exciting and flawless gameplay awaiting you, "+player.getUsername()+".", "Enjoy your time playing "+GameWorld.getSettings().getName()+"!"); - stage++; - break; - - //End of Tutorial - case 1205: - stage = 7; //Next Stage force ends any conversations? - - //Removing Tutorial Island properties on the account (?) and sending the Player to Lumbridge - player.removeAttribute("tut-island"); - player.getConfigManager().set(1021, 0); - player.getProperties().setTeleportLocation(new Location(3233, 3230)); - TutorialSession.getExtension(player).setStage(72); - player.getInterfaceManager().closeOverlay(); - - //Clears and Resets the Player's account and focuses the default interface to their Inventory - player.getInventory().clear(); - player.getEquipment().clear(); - player.getBank().clear(); - player.getInterfaceManager().restoreTabs(); //This still hides the Attack (double swords) in the player menu until Player wields a weapon. - player.getInterfaceManager().setViewedTab(3); - player.getInventory().add(STARTER_PACK); - player.getBank().add(STARTER_BANK); - - //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."); - - //Appending the welcome message and some other stuff - player.getPacketDispatch().sendMessage("Welcome to " + GameWorld.getSettings().getName() + "."); - - - - player.unlock(); - TutorialSession.getExtension(player).setStage(TutorialSession.MAX_STAGE + 1); - if (player.getIronmanManager().isIronman() && player.getSettings().isAcceptAid()) { - player.getSettings().toggleAcceptAid(); - } - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendInfoUpdate(player); - } - int slot = player.getAttribute("tut-island:hi_slot", -1); - if (slot < 0 || slot >= HintIconManager.MAXIMUM_SIZE) { - break; - } - - player.removeAttribute("tut-island:hi_slot"); - HintIconManager.removeHintIcon(player, slot); - break; - case 7: - end(); - break; - case 99: - end(); - TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), false); - break; - } - return true; - } - - //Magic Instructor Dialogue during the Tutorial, Repeated from above? - switch (TutorialSession.getExtension(player).getStage()) { - case 67: - switch (stage) { - case 0: - interpreter.sendDialogues(946, FacialExpression.NEUTRAL, "Good day, newcomer. My name is Terrova. I'm here", "to tell you about Magic. Let's start by opening your", "spell list."); - stage = 1; - break; - case 1: - end(); - TutorialStage.load(player, 68, false); - break; - } - break; - case 69: - switch (stage) { - case 0: - if (!player.getInventory().contains(556, 1) && !player.getInventory().contains(558, 1)) { - if (player.getInventory().freeSlots() > 2) { - interpreter.sendDoubleItemMessage(RUNES[0], RUNES[1], "Terrova gives you five air runes and five mind runes!"); - player.getInventory().add(RUNES[0], RUNES[1]); - stage = 3; - } - } else { - end(); - TutorialStage.load(player, 70, false); - } - break; - case 3: - end(); - TutorialStage.load(player, 70, false); - break; - } - break; - case 70: - switch (stage) { - case 0: - break; - case 3: - end(); - TutorialStage.load(player, 70, false); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {/* skippy */2796, /* magic instructor */946 }; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialIslandPlugin.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialIslandPlugin.java deleted file mode 100644 index cb9b52f6a..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialIslandPlugin.java +++ /dev/null @@ -1,175 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DoorActionHandler; -import core.game.interaction.OptionHandler; -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.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the node options in tutorial island. - * @author Vexia - */ -@Initializable -public class TutorialIslandPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(3015).getHandlers().put("option:open", this); - SceneryDefinition.forId(3016).getHandlers().put("option:open", this); - NPCDefinition.forId(2796).getHandlers().put("option:skip-tutorial", this); - SceneryDefinition.forId(3014).getHandlers().put("option:open", this); - SceneryDefinition.forId(3017).getHandlers().put("option:open", this); - SceneryDefinition.forId(3018).getHandlers().put("option:open", this); - SceneryDefinition.forId(3019).getHandlers().put("option:open", this); - SceneryDefinition.forId(3020).getHandlers().put("option:open", this); - SceneryDefinition.forId(3021).getHandlers().put("option:open", this); - SceneryDefinition.forId(3022).getHandlers().put("option:open", this); - SceneryDefinition.forId(3023).getHandlers().put("option:open", this); - SceneryDefinition.forId(3024).getHandlers().put("option:open", this); - SceneryDefinition.forId(3025).getHandlers().put("option:open", this); - SceneryDefinition.forId(3026).getHandlers().put("option:open", this); - SceneryDefinition.forId(3029).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(3030).getHandlers().put("option:climb-up", this); - SceneryDefinition.forId(3031).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(1740).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(3028).getHandlers().put("option:climb-up", this); -// PluginManager.definePlugin(new BrotherBraceDialogue(), new CombatInstructorDialogue(), new TutorialBook(), new FinancialAdvisorDialogue(), new MasterChefDialogue(), new MiningInstructorDialogue(), new QuestGuideDialogue(), new RSGuideDialogue(), new SurvivalExpertDialogue(), new TutorialCompletionDialogue()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (node instanceof NPC) { - if (option.equals("skip-tutorial")) { - player.getDialogueInterpreter().open(((NPC) node).getId(), ((NPC) node)); - return true; - } - } else if (node instanceof Scenery) { - if (TutorialSession.getExtension(player).getStage() == 16) { - int first = node.getId(); - int second = first == 3015 ? 3016 : 3015; - if (DoorActionHandler.autowalkFence(player, (Scenery) node, first, second)) { - TutorialStage.load(player, 17, false); - } - return true; - } - final int tut_stage = TutorialSession.getExtension(player).getStage(); - final Scenery object = (Scenery) node; - if (object.getId() == 3028) { - return true; - } - if (object.getId() == 3023 || object.getId() == 3022) { - if (tut_stage == 54) { - player.getDialogueInterpreter().sendDialogues(944, null, "No, don't enter the pit. Range the rats from outside", "the cage."); - return true; - } - if (tut_stage > 54) { - player.getDialogueInterpreter().sendDialogues(944, null, "Oi! Get away from there. Only enter the rat cage", "when I say so."); - return true; - } - if (tut_stage >= 50) { - if (DoorActionHandler.handleAutowalkDoor(player, object) && tut_stage == 50) { - TutorialStage.load(player, 51, false); - } - return true; - } - } - if (object.getId() == 3031) { - return true; - } - if (object.getId() == 1740) { - if (player.getLocation().equals(Location.create(3084, 3124, 1))) { - ClimbActionHandler.climb(player, new Animation(827), Location.create(3084, 3124, 0)); - return true; - } - ClimbActionHandler.climbLadder(player, object, option); - return true; - } - switch (tut_stage) { - case 3: - if (DoorActionHandler.handleAutowalkDoor(player, object)) { - TutorialStage.load(player, 4, false); - } - break; - case 17: - if (DoorActionHandler.handleAutowalkDoor(player, object)) { - TutorialStage.load(player, 18, false); - } - break; - case 22: - if (object.getId() == 3018 && DoorActionHandler.handleAutowalkDoor(player, object)) { - TutorialStage.load(player, 23, false); - } - break; - case 26: - if (DoorActionHandler.handleAutowalkDoor(player, object)) { - TutorialStage.load(player, 27, false); - } - break; - case 29: - if (object.getId() != 3019) { - ClimbActionHandler.climbLadder(player, object, option); - TutorialStage.load(player, 30, false); - } - break; - case 43: - if (object.getId() == 3021 || object.getId() == 3020) { - if (DoorActionHandler.handleAutowalkDoor(player, object)) { - TutorialStage.load(player, 44, false); - } - } - break; - case 55: - if (object.getId() == 3030) { - ClimbActionHandler.climbLadder(player, object, option); - TutorialStage.load(player, 56, false); - } - break; - case 57: - if (object.getId() == 3024) { - if (DoorActionHandler.handleAutowalkDoor(player, object)) { - TutorialStage.load(player, 58, false); - } - } - break; - case 59: - if (object.getId() == 3025) { - if (DoorActionHandler.handleAutowalkDoor(player, object)) { - TutorialStage.load(player, 60, false); - } - } - break; - case 66: - if (object.getId() == 3026) { - if (DoorActionHandler.handleAutowalkDoor(player, object)) { - TutorialStage.load(player, 67, false); - } - } - break; - } - } - return true; - } - - @Override - public Location getDestination(Node node, Node n) { - if (n instanceof Scenery) { - Scenery object = (Scenery) n; - if (object.getDefinition().hasAction("open")) { - return DoorActionHandler.getDestination((Entity) node, object); - } - } - return null; - } - -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialSession.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialSession.java deleted file mode 100644 index 58267a6a0..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialSession.java +++ /dev/null @@ -1,191 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.net.packet.PacketRepository; -import core.net.packet.context.InterfaceConfigContext; -import core.net.packet.out.InterfaceConfig; - -/** - * Represents the session used during tutorial. - * @author 'Vexia - */ -public final class TutorialSession { - - /** - * The maximum stage, the tutorial can go to. - */ - public final static int MAX_STAGE = 72; - - /** - * Represents the player instance. - */ - private Player player; - - /** - * Represents the delay pulse. - */ - private DelayPulse delayPulse; - - /** - * Constructs a new {@code TutorialSession} {@code Object}. - * @param player the player. - */ - public TutorialSession(Player player) { - this.player = player; - } - - /** - * Method used to extend this class. - * @param player the player. - */ - public static void extend(Player player) { - player.addExtension(TutorialSession.class, new TutorialSession(player)); - } - - /** - * Method used to init this session. - */ - public void init() { - if (getStage() >= MAX_STAGE) { - player.removeAttribute("tut-island"); - setStage(MAX_STAGE + 1); - return; - } - /*if (getStage() < 26) { - player.getSettings().setRunToggled(true); - }*/ - delayPulse = new DelayPulse(); - //player.setAttribute("tut-island", true); - //player.getInterfaceManager().openOverlay(new Component(371)); - //GameWorld.getPulser().submit(delayPulse); - //PacketRepository.send(InterfaceConfig.class, new InterfaceConfigContext(player, 371, 25, true)); - TutorialStage.load(player, getStage(), true); - } - - /** - * If the player has finished tutorial island. - * @return {@code True} if so. - */ - public boolean finished() { - return getStage() >= MAX_STAGE; - } - - /** - * Method used to return the instance of the tutorial session. - * @param player the player. - * @return the session. - */ - public static TutorialSession getExtension(Player player) { - if (player.getExtension(TutorialSession.class) == null) { - extend(player); - } - return player.getExtension(TutorialSession.class); - } - - /** - * Method used to return the stage of the tutorial, the stage is saved in - * the {@link Player#getGameAttributes()} since it's a single piece of data - * to save, the stage should never be reset after the tutorial is finished. - * @return the value of the stage. - */ - public int getStage() { - if (player.isArtificial()) { - return MAX_STAGE + 1; - } - return player.getSavedData().getGlobalData().getTutorialStage(); - } - - /** - * Gets the delay pulse. - * @return the delay pulse. - */ - public DelayPulse getDelayPulse() { - return delayPulse == null ? new DelayPulse() : delayPulse; - } - - /** - * Method used to set the stage of the tutorial. - */ - public void setStage(int stage) { - getDelayPulse().reset(); - getDelayPulse().hide(); - player.getSavedData().getGlobalData().setTutorialStage(stage); - } - - /** - * Gets the player. - * @return the player. - */ - public Player getPlayer() { - return player; - } - - /** - * Sets the player. - * @param player the player to set. - */ - public void setPlayer(Player player) { - this.player = player; - } - - /** - * Gets the maximum stage. - * @return the max stage. - */ - public int getMaxStage() { - return MAX_STAGE; - } - - /** - * Represents the pulse used to inform a player to go to the next stage. - * @author 'Vexia - * @version 1.0 - */ - public final class DelayPulse extends Pulse { - - /** - * Represents the delay. - */ - private int delay; - - /** - * Constructs a new {@code DelayPulse} {@code Object}. - */ - public DelayPulse() { - super(1, player); - reset(); - } - - @Override - public boolean pulse() { - if (delay < GameWorld.getTicks()) { - show(); - } - return false; - } - - /** - * Method used to reset the delay. - */ - public void reset() { - delay = GameWorld.getTicks() + 20000; - } - - /** - * Method used to hide the arrow. - */ - public void hide() { - PacketRepository.send(InterfaceConfig.class, new InterfaceConfigContext(player, 371, 24, true)); - } - - /** - * Method used to show the arrow. - */ - public void show() { - reset(); - PacketRepository.send(InterfaceConfig.class, new InterfaceConfigContext(player, 371, 24, false)); - } - } -} diff --git a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialStage.java b/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialStage.java deleted file mode 100644 index 3fdf4f662..000000000 --- a/Server/src/main/java/core/game/content/quest/tutorials/tutorialisland/TutorialStage.java +++ /dev/null @@ -1,1031 +0,0 @@ -package core.game.content.quest.tutorials.tutorialisland; - -import core.game.component.Component; -import core.game.node.entity.combat.equipment.WeaponInterface; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.HintIconManager; -import core.game.node.scenery.Scenery; -import rs09.game.system.SystemLogger; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import rs09.game.world.repository.Repository; - -/** - * Represents a tutorial stage. - * @author 'Vexia - * @date 06/03/2013 - */ - - -public enum TutorialStage { - STAGE_0(0) { - @Override - public void run(final Player player) { - if (login) { - player.lock(1); - // player.getProperties().setTeleportLocation(Location.create(3094, 3107, 0)); - player.getProperties().setTeleportLocation(Location.create(2524, 5002, 0)); // Since tutorial is disabled... - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); - GameWorld.getPulser().submit(new Pulse(2) { - @Override - public boolean pulse() { - CharacterDesign.open(player); - return true; - } - }); - } - } - }, - STAGE_1(1) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(1021, 12); - } else { - removeHintIcon(player); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("", "Player controls", "Please click on the flashing spanner icon found at the bottom", "right of your screen. This will display your player controls.", "")); - } - }, - STAGE_2(2) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13); - } else { - player.getConfigManager().set(1021, 0); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(945))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendScrollMessageWithBlueTitle("Player controls", "On the side panel you can now see a variety of options from", "changing the brightness of the screen and 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", GameWorld.getSettings().getName() + " Guide to continue.")); - } - }, - STAGE_3(3) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Location.create(3098, 3107, 0), 1, -1, player.getHintIconManager().freeSlot(), 125, 3)); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_4(4) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13); - } else { - removeHintIcon(player); - } - player.getConfigManager().set(406, 2); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(943))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_5(5) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } else { - player.getInterfaceManager().openTab(new Component(149)); - removeHintIcon(player); - } - player.getConfigManager().set(1021, 4); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_6(6) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } else { - player.getConfigManager().set(1021, 0); - } - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_7(7) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } else { - removeHintIcon(player); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Please wait.", "", "Your character is now attempting to cut down the tree. Sit back", "for a moment while " + (player.getAppearance().isMale() ? "he" : "she") + " does all the hard work.", "")); - } - }, - STAGE_8(8) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_9(9) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Please wait.", "", "Your character is now attempting to light a fire.", "This should only take a few seconds.", "")); - } - }, - STAGE_10(10) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } else { - player.getInterfaceManager().openTab(new Component(320)); - } - player.getConfigManager().set(1021, 2); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("You gained some experience.", "Click on the flashing bar graph icon near the inventory button to see", "your skill state.", "", "")); - } - }, - STAGE_11(11) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } else { - player.getConfigManager().set(1021, 0); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(943))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_12(12) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(952))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_13(13) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 2); - } else { - removeHintIcon(player); - } - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_14(14) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - } - player.getConfigManager().set(406, 3); - Component.setUnclosable(player, player.getDialogueInterpreter().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 hatchet or tinderbox Brynna will give you another.", "")); - } - }, - STAGE_15(15) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 3); - } - Component.setUnclosable(player, player.getDialogueInterpreter().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." - * ) - * ) - * ; - */ - } - }, - STAGE_16(16) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 3); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Location.create(3089, 3091, 0), 1, -1, player.getHintIconManager().freeSlot(), 75, 4)); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Well done, you've just cooked your first " + GameWorld.getSettings().getName() + " 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.")); - } - }, - STAGE_17(17) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - } else { - removeHintIcon(player); - } - player.getConfigManager().set(406, 4); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Location.create(3078, 3084, 0), 1, -1, player.getHintIconManager().freeSlot(), 125, 4)); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_18(18) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 4); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(942))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_19(19) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 4); - } else { - removeHintIcon(player); - } - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_20(20) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12, 13); - player.getConfigManager().set(406, 4); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Location.create(3075, 3081, 0), 1, -1, player.getHintIconManager().freeSlot(), 75, 2)); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_21(21) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12); - player.getConfigManager().set(406, 4); - } else { - removeHintIcon(player); - player.getInterfaceManager().openTab(new Component(187)); - } - player.getConfigManager().set(1021, 14); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_22(22) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10, 12); - player.getConfigManager().set(406, 4); - } else { - player.getConfigManager().set(1021, 0); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Location.create(3072, 3090, 0), 1, -1, player.getHintIconManager().freeSlot(), 125, 4)); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_23(23) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10); - } else { - removeHintIcon(player); - player.getInterfaceManager().openTab(new Component(464)); - } - player.getWalkingQueue().reset(); - player.getLocks().lockMovement(GameWorld.getTicks() + 100000); - player.getConfigManager().set(406, 6); - player.getConfigManager().set(1021, 13); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Emotes", "Now how about showing some feelings? You will see a flashing icon in", "the shape of a person. Click on that to acces your emotes.", "", "")); - } - }, - STAGE_24(24) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 6); - } - player.getLocks().unlockMovement(); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Emotes", "For those situtation 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 acces to all sorts of things.")); - } - }, - STAGE_25(25) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 6); - } - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_26(26) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 6); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(949))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_27(27) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 2, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 7); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Talk with the Quest Guide.", "", "He will tell you all about quests.", "", "")); - } - }, - STAGE_28(28) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 7); - } - player.getConfigManager().set(1021, 0); - player.getInterfaceManager().openTab(new Component(274)); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "", "")); - } - }, - STAGE_29(29) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 7); - } else { - removeHintIcon(player); - } - player.getConfigManager().set(1021, 0); - player.getInterfaceManager().openTab(new Component(274)); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(0, 3088, 3119))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("", "Moving on.", "It's time to enter some caves. Click on the ladder to go down to the", "next area.", "")); - } - }, - STAGE_30(30) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - } else { - removeHintIcon(player); - } - player.getConfigManager().set(1021, 0); - player.getInterfaceManager().openTab(new Component(274)); - player.getConfigManager().set(406, 8); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(948))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_31(31) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 8); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(0, 3076, 9504))); - Component.setUnclosable(player, player.getDialogueInterpreter().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", "")); - } - }, - STAGE_32(32) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 8); - } else { - removeHintIcon(player); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("", "", "Please wait.", "Your character is now attempting to prospect the rock. This should", "Only take a few seconds.")); - } - }, - STAGE_33(33) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 8); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(0, 3086, 9501))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("It's tin.", "So now you know there's tin in the grey rocks, try prospecting the", "brown ones next.", "", "")); - } - }, - STAGE_34(34) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 8); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(948))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_35(35) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 8); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(0, 3076, 9504))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_36(36) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 8); - } else { - removeHintIcon(player); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("", "Please wait.", "Your character is now attempting to mine the rock.", "This should only take a few seconds.", "")); - } - }, - STAGE_37(37) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 8); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(0, 3086, 9501))); - Component.setUnclosable(player, player.getDialogueInterpreter().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'.", "")); - } - }, - STAGE_38(38) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 8); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(0, 3076, 9504))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Mining", "Now you have some copper ore you just need some tin ore, then", "you'll have all you need to create a bronze bar. As you did before", "right click on the tin rock and select 'mine'.", "")); - } - }, - STAGE_39(39) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 9); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(0, 3078, 9495))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_40(40) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 9); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(948))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("You've made a bronze bar!", "Speak to the Mining Instructor and he'll show you how to make", "it into a weapon.", "", "")); - } - }, - STAGE_41(41) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 9); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(Location.create(3083, 9499, 0)))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_42(42) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 9); - } else { - removeHintIcon(player); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Smithing a dagger.", "Now you have the Smithing menu open, you will see a list of all", "the thing 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.")); - } - }, - STAGE_43(43) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 9); - } else { - removeHintIcon(player); - } - Scenery object = RegionManager.getObject(Location.create(3094, 9502, 0)); - if (object != null) { - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, object)); - } else { - System.out.println("Object is null tutorial stage 43!"); - } - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_44(44) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 10); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(944))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "", "")); - } - }, - STAGE_45(45) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 4, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 10); - } else { - removeHintIcon(player); - } - player.getInterfaceManager().openTab(new Component(387)); - player.getConfigManager().set(1021, 5); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_46(46) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 10); - } - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_47(47) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 10); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(944))); - Component.setUnclosable(player, player.getDialogueInterpreter().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. You can close this window by clicking on the", "small 'x' in the top right hand corner. Speak to the Combat", "Instructor to continue.")); - } - }, - STAGE_48(48) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 10); - } else { - removeHintIcon(player); - } - player.getInterfaceManager().openTab(new Component(387)); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_49(49) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(0, 5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 10); - } - player.getConfigManager().set(406, 10); - player.getConfigManager().set(1021, 1); - WeaponInterface inter = player.getExtension(WeaponInterface.class); - if (inter == null) { - player.addExtension(WeaponInterface.class, inter = new WeaponInterface(player)); - } - player.getInterfaceManager().openTab(inter); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Combat interface.", "Click on the flashing crossed swords icon to see the combat", "interface.", "", "")); - } - }, - STAGE_50(50) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 11); - } - player.getConfigManager().set(406, 11); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(Location.create(3110, 9518, 0)))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_51(51) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 11); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(87))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Attacking", "To attack the rat, click it and select the attack option. You", "will then walk over to it and start hitting it.", "", "")); - } - }, - STAGE_52(52) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 11); - } else { - removeHintIcon(player); - } - player.getConfigManager().set(406, 11); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_53(53) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 12); - } - player.getConfigManager().set(406, 12); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "", "")); - } - }, - STAGE_54(54) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 12); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(87))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_55(55) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 12); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(Location.create(3111, 9526, 0)))); - Component.setUnclosable(player, player.getDialogueInterpreter().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 learned", "here just talk to Vannaka and he'll tell you what he can.", "")); - } - }, - STAGE_56(56) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 14); - } else { - removeHintIcon(player); - } - player.getConfigManager().set(406, 14); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(Location.create(3122, 3124, 0)))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Banking.", "Follow the path and you will come to the front of a building.", "This is the 'Bank of " + GameWorld.getSettings().getName() + "' 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.")); - } - }, - STAGE_57(57) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 14); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(Location.create(3125, 3124, 0)))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_58(58) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 14); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(947))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Financial advice.", "The guide here will tell you about making cash. Just click on", "him to hear what he's got to say.", "", "")); - } - }, - STAGE_59(59) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 14); - } else { - removeHintIcon(player); - } - Scenery object = RegionManager.getObject(Location.create(3130, 3124, 0)); - if (object != null) { - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, object)); - } else { - System.out.println("Object is null tutorial stage 59!"); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("", "Continue through the next door.", "", "", "")); - } - }, - STAGE_60(60) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 16); - } else { - removeHintIcon(player); - } - player.getConfigManager().set(406, 16); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(954))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_61(61) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(5, 6, 7, 8, 9, 10); - player.getConfigManager().set(406, 16); - } else { - removeHintIcon(player); - } - player.getInterfaceManager().openTab(new Component(271)); - player.getConfigManager().set(1021, 6); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Your Prayer menu", "Click on the flashing icon to open the Prayer menu.", "", "", "")); - - } - }, - STAGE_62(62) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(6, 7, 8, 9, 10); - player.getConfigManager().set(406, 16); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(954))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("", "Your Prayer menu.", "Talk with Brother Brace and he'll tell you all about prayer.", "", "")); - } - }, - STAGE_63(63) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(6, 7, 8, 9, 10); - player.getConfigManager().set(406, 16); - } - player.getInterfaceManager().openTab(new Component(550)); - player.getConfigManager().set(1021, 9); - removeHintIcon(player); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("", "Friends list.", "You should now see another new icon. Click on the flashing", "smiling face to open your friend list.", "")); - } - }, - STAGE_64(64) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(6, 7, 10); - player.getConfigManager().set(406, 16); - } - player.getInterfaceManager().openTab(new Component(589)); - player.getInterfaceManager().openTab(new Component(551)); - player.getInterfaceManager().openTab(new Component(589)); - player.getConfigManager().set(1021, 10); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "", "")); - } - }, - STAGE_65(65) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(6, 7); - player.getConfigManager().set(406, 16); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(954))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.")); - } - }, - STAGE_66(66) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(6, 7); - player.getConfigManager().set(406, 16); - } - removeHintIcon(player); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, RegionManager.getObject(Location.create(3122, 3102, 0)))); - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("Your final instructor!", "You're almost finished on tutorial island. Pass through the", "door to find the path leading to your last instructor.", "", "")); - } - }, - STAGE_67(67) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(6, 7); - player.getConfigManager().set(406, 17); - } else { - removeHintIcon(player); - } - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(946))); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "")); - } - }, - STAGE_68(68) { - @Override - public void run(final Player player) { - if (login) { - player.getInterfaceManager().hideTabs(6, 7); - player.getConfigManager().set(406, 17); - } else { - removeHintIcon(player); - } - player.getInterfaceManager().openTab(new Component(player.getSpellBookManager().getSpellBook())); - player.getConfigManager().set(1021, 7); - Component.setUnclosable(player, player.getDialogueInterpreter().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.", "", "")); - } - }, - STAGE_69(69) { - @Override - public void run(final Player player) { - if (login) { - player.getConfigManager().set(406, 19); - } - Component.setUnclosable(player, player.getDialogueInterpreter().sendPlaneMessageWithBlueTitle("", "This is your spell list.", "", "Ask the mage about it.", "")); - } - }, - STAGE_70(70) { - @Override - public void run(final Player player) { - if (login) { - player.getConfigManager().set(406, 20); - } - player.getInterfaceManager().restoreTabs(); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(41))); - Component.setUnclosable(player, player.getDialogueInterpreter().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. If you need more runes ask Terrova")); - } - }, - STAGE_71(71) { - @Override - public void run(final Player player) { - if (login) { - player.getConfigManager().set(406, 20); - } else { - removeHintIcon(player); - } - TutorialSession.getExtension(player).getDelayPulse().stop(); - player.getInterfaceManager().restoreTabs(); - player.setAttribute("tut-island:hi_slot", HintIconManager.registerHintIcon(player, Repository.findNPC(946))); - player.removeAttribute("tut-island"); - Component.setUnclosable(player, player.getDialogueInterpreter().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!", "")); - } - }; - - /** - * The stage. - */ - private final int stage; - - /** - * Represents if it's a login, when the stage is loaded. - */ - protected boolean login; - - /** - * Constructs a new {@code TutorialStage} {@code Object}. - * @param stage the stage, - */ - TutorialStage(int stage) { - this.stage = stage; - } - - /** - * Method used to handle when the stage is loaded, - * @param player the player. - */ - public void run(final Player player) { - } - - /** - * Represents if it is a login stage. - * @param login the login value. - */ - private void isLogin(boolean login) { - this.login = login; - } - - /** - * @return the stage. - */ - public int getStage() { - return stage; - } - - /** - * Method used to load a new stage of the tutorial. - * @param stage the stage. - * @param login the login. - */ - public static void load(final Player player, int stage, boolean login) { - if (TutorialSession.getExtension(player) != null) { - TutorialSession.extend(player); - } - - final TutorialStage tutorial_stage = forStage(stage); - if (tutorial_stage == null) { - SystemLogger.logErr("Unhandled tutorial stage " + stage + "."); - return; - } - player.setAttribute("tut-island", true); - tutorial_stage.isLogin(login); - tutorial_stage.run(player); - TutorialSession.getExtension(player).setStage(stage); - TutorialSession.getExtension(player).getDelayPulse().reset(); - TutorialSession.getExtension(player).getDelayPulse().hide(); - } - - /** - * Gets the tutorial stage by the stage id. - * @param id the id. - * @return the tutorial stage. - */ - public static TutorialStage forStage(int id) { - for (TutorialStage stage : TutorialStage.values()) { - if (stage.getStage() == id) { - return stage; - } - } - return null; - } - - /** - * Removes the current hint icon (if any). - * @param player The player. - */ - public static void removeHintIcon(Player player) { - int slot = player.getAttribute("tut-island:hi_slot", -1); - if (slot < 0 || slot >= HintIconManager.MAXIMUM_SIZE) { - return; - } - player.removeAttribute("tut-island:hi_slot"); - HintIconManager.removeHintIcon(player, slot); - } -} diff --git a/Server/src/main/java/core/game/content/ttrail/ClueLevel.java b/Server/src/main/java/core/game/content/ttrail/ClueLevel.java deleted file mode 100644 index 85a8a76b9..000000000 --- a/Server/src/main/java/core/game/content/ttrail/ClueLevel.java +++ /dev/null @@ -1,668 +0,0 @@ -package core.game.content.ttrail; - -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.game.container.access.InterfaceContainer; -import core.game.node.entity.npc.drop.DropFrequency; -import core.game.node.entity.player.Player; -import core.game.node.item.ChanceItem; -import core.game.node.item.Item; -import core.game.node.item.WeightedChanceItem; -import org.rs09.consts.Items; -import rs09.game.world.GameWorld; -import core.tools.RandomFunction; - -import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * A clue scroll level. - * - * @author Vexia - */ -public enum ClueLevel { - - EASY(new Item(2714), 1 << 16 | 5, - new ChanceItem(Items.BLUE_BERET_2633, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_BERET_2635, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.WHITE_BERET_2637, 1, 1, DropFrequency.UNCOMMON), - // black - // t - new ChanceItem(Items.BLACK_FULL_HELMT_2587, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_PLATEBODY_T_2583, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_PLATELEGS_T_2585, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_PLATESKIRT_T_3472, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_KITESHIELD_T_2589, 1, 1, DropFrequency.UNCOMMON), - // black - // g - new ChanceItem(Items.BLACK_FULL_HELMG_2595, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_PLATEBODY_G_2591, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_PLATELEGS_G_2593, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_PLATESKIRT_G_3473, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_KITESHIELD_G_2597, 1, 1, DropFrequency.UNCOMMON), - //black h helms - new ChanceItem(Items.BLACK_HELM_H1_10306, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_HELM_H2_10308, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_HELM_H3_10310, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_HELM_H4_10312, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_HELM_H5_10314, 1, 1, DropFrequency.UNCOMMON), - //black h shields - new ChanceItem(Items.BLACK_SHIELDH1_7332, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_SHIELDH2_7338, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_SHIELDH3_7344, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_SHIELDH4_7350, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_SHIELDH5_7356, 1, 1, DropFrequency.UNCOMMON), - // highway - // man - // mask - new ChanceItem(Items.HIGHWAYMAN_MASK_2631, 1, 1, DropFrequency.UNCOMMON), - // wizard t - new ChanceItem(Items.WIZARD_ROBE_T_7392, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.WIZARD_HAT_T_7396, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLUE_SKIRT_T_7388, 1, 1, DropFrequency.UNCOMMON), - // wizard - // g - new ChanceItem(Items.WIZARD_ROBE_G_7390, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLUE_SKIRT_G_7386, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.WIZARD_HAT_G_7394, 1, 1, DropFrequency.UNCOMMON), - // studded - // g - new ChanceItem(Items.STUDDED_BODY_G_7362, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.STUDDED_CHAPS_G_7366, 1, 1, DropFrequency.UNCOMMON), - // studded - // t - new ChanceItem(Items.STUDDED_BODY_T_7364, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.STUDDED_CHAPS_T_7368, 1, 1, DropFrequency.UNCOMMON), - // red - // ele - // shirt - new ChanceItem(Items.RED_ELE_SHIRT_10404, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RED_ELE_LEGS_10406, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RED_ELE_BLOUSE_10424, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RED_ELE_SKIRT_10426, 1, 1, DropFrequency.UNCOMMON), - // blue - // ele - // shirt - new ChanceItem(Items.BLUE_ELE_SHIRT_10408, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLUE_ELE_LEGS_10410, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLUE_ELE_BLOUSE_10428, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLUE_ELE_SKIRT_10430, 1, 1, DropFrequency.UNCOMMON), - // green - // ele - // shirt - new ChanceItem(Items.GREEN_ELE_SHIRT_10412, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GREEN_ELE_LEGS_10414, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GREEN_ELE_BLOUSE_10432, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GREEN_ELE_SKIRT_10434, 1, 1, DropFrequency.UNCOMMON), - // bob - // the - // cat - new ChanceItem(Items.BOB_SHIRT_10316, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.BOB_SHIRT_10318, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.BOB_SHIRT_10320, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.BOB_SHIRT_10322, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.BOB_SHIRT_10324, 1, 1, DropFrequency.RARE), - // emote - // enhancers - new ChanceItem(Items.A_POWDERED_WIG_10392, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.FLARED_TROUSERS_10394, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.PANTALOONS_10396, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SLEEPING_CAP_10398, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.AMULET_OF_MAGICT_10366, 1, 1, DropFrequency.UNCOMMON), - // vestment - // robes(sara, - // guthix, - // zammy) - new ChanceItem(Items.SARADOMIN_ROBE_TOP_10458, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.SARADOMIN_ROBE_LEGS_10464, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GUTHIX_ROBE_TOP_10462, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GUTHIX_ROBE_LEGS_10466, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ZAMORAK_ROBE_TOP_10460, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ZAMORAK_ROBE_LEGS_10468, 1, 1, DropFrequency.UNCOMMON), - // black - // cane - new ChanceItem(Items.BLACK_CANE_13095, 1, 1, DropFrequency.UNCOMMON), - // spikey - // helmet - new ChanceItem(Items.SPIKED_HELMET_13105, 1, 1, DropFrequency.RARE), - //comp bow - new ChanceItem(Items.WILLOW_COMP_BOW_10280, 1, 1, DropFrequency.UNCOMMON), - //other common crap - new ChanceItem(Items.BLACK_PLATELEGS_1077, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_PLATESKIRT_1089, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_CHAINBODY_1107, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_PLATEBODY_1125, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_MED_HELM_1151, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_FULL_HELM_1165, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_SQ_SHIELD_1179, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_KITESHIELD_1195, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_DAGGER_1217, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_SWORD_1283, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_LONGSWORD_1297, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_2H_SWORD_1313, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_SCIMITAR_1327, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_WARHAMMER_1341, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_AXE_1361, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_BATTLEAXE_1367, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_MACE_1426, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_CLAWS_3098, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_NAILS_4821, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.OAK_PLANK_8779, 4, 38, DropFrequency.COMMON), - new ChanceItem(Items.WILLOW_SHORTBOW_850, 1, 4, DropFrequency.COMMON), - new ChanceItem(Items.TROUT_334, 4, 19, DropFrequency.COMMON), - new ChanceItem(Items.COIF_1169, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.LEATHER_GLOVES_1059, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.LEATHER_BOOTS_1061, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.LEATHER_VAMBRACES_1063, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.LEATHER_CHAPS_1095, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.LEATHER_BODY_1129, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.LEATHER_COWL_1167, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.HARDLEATHER_BODY_1131, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.YEW_SHORTBOW_858, 1, 4, DropFrequency.COMMON), - new ChanceItem(Items.SALMON_330, 3, 23, DropFrequency.COMMON), - new ChanceItem(Items.EARTH_TALISMAN_1441, 1, 3, DropFrequency.COMMON), - new ChanceItem(Items.FIRE_TALISMAN_1443, 1, 3, DropFrequency.COMMON), - new ChanceItem(Items.STEEL_PICKAXE_1270, 1, 3, DropFrequency.COMMON), - new ChanceItem(Items.STUDDED_CHAPS_1097, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.STUDDED_BODY_1133, 1, 1, DropFrequency.COMMON)), - - MEDIUM(new Item(2717), 1 << 16 | 6, - // Trimmed - // addy - // shit - new ChanceItem(Items.ADAM_FULL_HELMT_2605, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_PLATEBODY_T_2599, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_PLATELEGS_T_2601, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_KITESHIELD_T_2603, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_PLATESKIRT_T_3474, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_FULL_HELMG_2613, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_PLATEBODY_G_2607, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_PLATELEGS_G_2609, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_PLATESKIRT_G_3475, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAM_KITESHIELD_G_2611, 1, 1, DropFrequency.UNCOMMON), - // Headbands - new ChanceItem(Items.BLACK_HEADBAND_2647, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RED_HEADBAND_2645, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BROWN_HEADBAND_2649, 1, 1, DropFrequency.UNCOMMON), - // Boots - new ChanceItem(Items.RANGER_BOOTS_2577, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.WIZARD_BOOTS_2579, 1, 1, DropFrequency.UNCOMMON), - // Boaters - new ChanceItem(Items.RED_BOATER_7319, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ORANGE_BOATER_7321, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GREEN_BOATER_7323, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLUE_BOATER_7325, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLACK_BOATER_7327, 1, 1, DropFrequency.UNCOMMON), - // Trimmed - // Dragonhide - // shit - new ChanceItem(Items.DHIDE_BODY_T_7372, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.DHIDE_CHAPS_T_7380, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.DHIDE_BODYG_7370, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.DHIDE_CHAPS_G_7378, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.PITH_HELMET_13103, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_CANE_13097, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.YEW_COMP_BOW_10282, 1, 1, DropFrequency.UNCOMMON), - // animal - // masks - new ChanceItem(Items.PENGUIN_MASK_13109, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.SHEEP_MASK_13107, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.BAT_MASK_13111, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.CAT_MASK_13113, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.WOLF_MASK_13115, 1, 1, DropFrequency.UNCOMMON), - // ammy - // of - // strength - // (t) - new ChanceItem(Items.STRENGTH_AMULETT_10364, 1, 1, DropFrequency.UNCOMMON), - // white ele - new ChanceItem(Items.WHITE_ELE_BLOUSE_10420, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.WHITE_ELE_SKIRT_10422, 1, 1, DropFrequency.UNCOMMON), - // black - // ele - new ChanceItem(Items.BLACK_ELE_SHIRT_10400, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.BLACK_ELE_LEGS_10402, 1, 1, DropFrequency.RARE), - // purple - // ele - new ChanceItem(Items.PURPLE_ELE_SHIRT_10416, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.PURPLE_ELE_LEGS_10418, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.PURPLE_ELE_BLOUSE_10436, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.PURPLE_ELE_SKIRT_10438, 1, 1, DropFrequency.UNCOMMON), - // addy - // heraldic - // helms - new ChanceItem(Items.ADAMANT_HELM_H1_10296, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_HELM_H2_10298, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_HELM_H3_10300, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_HELM_H4_10302, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_HELM_H5_10304, 1, 1, DropFrequency.UNCOMMON), - // addy - // heraldic - // shield - new ChanceItem(Items.ADAMANT_SHIELDH1_10666, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_SHIELDH2_10669, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_SHIELDH3_10672, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_SHIELDH4_10675, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ADAMANT_SHIELDH5_10678, 1, 1, DropFrequency.UNCOMMON), - // cloaks - new ChanceItem(Items.SARADOMIN_CLOAK_10446, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GUTHIX_CLOAK_10448, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ZAMORAK_CLOAK_10450, 1, 1, DropFrequency.UNCOMMON), - //mitres - new ChanceItem(Items.SARADOMIN_MITRE_10452, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GUTHIX_MITRE_10454, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ZAMORAK_MITRE_10456, 1, 1, DropFrequency.UNCOMMON), - - new ChanceItem(Items.ADAMANT_FULL_HELM_1161, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_PLATEBODY_1123, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_PLATELEGS_1073, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_PLATESKIRT_1091, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_KITESHIELD_1199, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_SQ_SHIELD_1183, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_CHAINBODY_1111, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_DAGGER_1211, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_PICKAXE_1271, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_SWORD_1287, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_LONGSWORD_1301, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_2H_SWORD_1317, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_AXE_1357, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_BATTLEAXE_1371, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_MACE_1430, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANTITE_NAILS_4823, 10, 40, DropFrequency.COMMON), - new ChanceItem(Items.ADAMANT_CROSSBOW_9183, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.FIRE_BATTLESTAFF_1393, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.GREEN_DHIDE_CHAPS_1099, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.GREEN_DHIDE_BODY_1135, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.YEW_SHORTBOW_857, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.TEAK_PLANK_8781, 10, 43, DropFrequency.COMMON), - new ChanceItem(Items.SWORDFISH_374, 6, 23, DropFrequency.COMMON), - new ChanceItem(Items.LOBSTER_380, 15, 43, DropFrequency.COMMON)), - - HARD(new Item(2720), 1 << 16 | 8, - new ChanceItem(Items.MAGIC_COMP_BOW_10284, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.TOP_HAT_13101, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_CANE_13099, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.SARADOMIN_STOLE_10470, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GUTHIX_STOLE_10472, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ZAMORAK_STOLE_10474, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.SARADOMIN_CROZIER_10440, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.GUTHIX_CROZIER_10442, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ZAMORAK_CROZIER_10444, 1, 1, DropFrequency.UNCOMMON), - // Trimmed - // rune - // shit - new ChanceItem(Items.RUNE_FULL_HELM_T_2627, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_PLATEBODY_T_2623, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_PLATELEGS_T_2625, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_PLATESKIRT_T_3477, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_KITESHIELD_T_2629, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_FULL_HELMG_2619, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_PLATEBODY_G_2615, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_PLATELEGS_G_2617, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_PLATESKIRT_G_3476, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_KITESHIELD_G_2621, 1, 1, DropFrequency.UNCOMMON), - // God - // armour - // shit - new ChanceItem(Items.GUTHIX_FULL_HELM_2673, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_PLATEBODY_2669, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_PLATELEGS_2671, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_PLATESKIRT_3480, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_KITESHIELD_2675, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_FULL_HELM_2657, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_PLATEBODY_2653, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_PLATELEGS_2655, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_PLATESKIRT_3478, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_KITESHIELD_2659, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_FULL_HELM_2665, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_PLATEBODY_2661, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_PLATELEGS_2663, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_PLATESKIRT_3479, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_KITESHIELD_2667, 1, 1, DropFrequency.RARE), - // heraldic - // shit - new ChanceItem(Items.RUNE_HELM_H1_10286, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_SHIELDH1_10667, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_HELM_H2_10288, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_SHIELDH2_10670, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_HELM_H3_10290, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_SHIELDH3_10673, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_HELM_H4_10292, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_SHIELDH4_10676, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_HELM_H5_10294, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.RUNE_SHIELDH5_10679, 1, 1, DropFrequency.UNCOMMON), - // Blue - // dragonhide - // trimmed - new ChanceItem(Items.DHIDE_BODY_T_7376, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.DHIDE_CHAPS_T_7384, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.DHIDE_BODY_G_7374, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.DHIDE_CHAPS_G_7382, 1, 1, DropFrequency.COMMON), - // God - // dhide - new ChanceItem(Items.ZAMORAK_COIF_10374, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_BRACERS_10368, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_DHIDE_10370, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_CHAPS_10372, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_COIF_10382, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_BRACERS_10376, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_DRAGONHIDE_10378, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_CHAPS_10380, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_COIF_10390, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_BRACERS_10384, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_DHIDE_10386, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_CHAPS_10388, 1, 1, DropFrequency.RARE), - // Hats - new ChanceItem(Items.PIRATES_HAT_2651, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ROBIN_HOOD_HAT_2581, 1, 1, DropFrequency.UNCOMMON), - // Cavaliers - new ChanceItem(Items.TAN_CAVALIER_2639, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.DARK_CAVALIER_2641, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_CAVALIER_2643, 1, 1, DropFrequency.COMMON), - //rune crap - new ChanceItem(Items.RUNE_FULL_HELM_1163, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_PLATEBODY_1127, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_PLATELEGS_1079, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_PLATESKIRT_1093, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_KITESHIELD_1201, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_SQ_SHIELD_1185, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_DAGGER_1213, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_PICKAXE_1275, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_SWORD_1289, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_LONGSWORD_1303, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_2H_SWORD_1319, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_AXE_1359, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_BATTLEAXE_1373, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_MACE_1432, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_NAILS_4824, 10, 43, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_BOLTS_9144, 6, 23, DropFrequency.COMMON), - new ChanceItem(Items.RUNE_CROSSBOW_9185, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_DHIDE_CHAPS_2497, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.BLACK_DHIDE_BODY_2503, 1, 1, DropFrequency.COMMON), - new ChanceItem(Items.MAGIC_SHORTBOW_861, 1, 2, DropFrequency.COMMON), - new ChanceItem(Items.MAGIC_LONGBOW_859, 1, 2, DropFrequency.COMMON), - //planks - new ChanceItem(Items.MAHOGANY_PLANK_8783, 6, 27, DropFrequency.COMMON), - //food - new ChanceItem(Items.LOBSTER_380, 16, 38, DropFrequency.COMMON), - new ChanceItem(Items.SHARK_386, 7, 32, DropFrequency.COMMON), - //enchanted robes - new ChanceItem(Items.ENCHANTED_ROBE_7398, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ENCHANTED_TOP_7399, 1, 1, DropFrequency.UNCOMMON), - new ChanceItem(Items.ENCHANTED_HAT_7400, 1, 1, DropFrequency.UNCOMMON)), - - ; - - /** - * The default rewards. - */ - private static final ChanceItem[] DEFAULT_REWARDS = new ChanceItem[] { - new ChanceItem(Items.COINS_995, 500, 7000, DropFrequency.COMMON), - new ChanceItem(Items.PURPLE_FIRELIGHTER_10326, 15, 40, DropFrequency.UNCOMMON), - new ChanceItem(Items.RED_FIRELIGHTER_7329, 15, 40, DropFrequency.UNCOMMON), - new ChanceItem(Items.BLUE_FIRELIGHTER_7331, 15, 40, DropFrequency.UNCOMMON), - new ChanceItem(Items.GREEN_FIRELIGHTER_7330, 15, 40, DropFrequency.UNCOMMON), - new ChanceItem(Items.WHITE_FIRELIGHTER_10327, 15, 40, DropFrequency.UNCOMMON), - new ChanceItem(Items.PURPLE_SWEETS_10476, 1, 27, DropFrequency.UNCOMMON), - new ChanceItem(Items.AIR_RUNE_556, 1, 228, DropFrequency.COMMON), - new ChanceItem(Items.FIRE_RUNE_554, 1, 228, DropFrequency.COMMON), - new ChanceItem(Items.EARTH_RUNE_557, 1, 228, DropFrequency.COMMON), - new ChanceItem(Items.WATER_RUNE_555, 1, 228, DropFrequency.COMMON), - new ChanceItem(Items.SARADOMIN_PAGE_1_3827, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_PAGE_2_3828, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_PAGE_3_3829, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.SARADOMIN_PAGE_4_3830, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_PAGE_1_3831, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_PAGE_2_3832, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_PAGE_3_3833, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.ZAMORAK_PAGE_4_3834, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_PAGE_1_3835, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_PAGE_2_3836, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_PAGE_3_3837, 1, 1, DropFrequency.RARE), - new ChanceItem(Items.GUTHIX_PAGE_4_3838, 1, 1, DropFrequency.RARE) - }; - - /** - * The super rare items. - */ - private static final WeightedChanceItem[] SUPER_RARE = new WeightedChanceItem[] { - //1 Trimmed Glory = 50 = to hit 15% - new WeightedChanceItem(Items.AMULET_OF_GLORYT_10362, 1, 1, 50), - //5 Gilded Items = 150 = to hit 9% - new WeightedChanceItem(Items.GILDED_FULL_HELM_3486, 1, 1, 30), - new WeightedChanceItem(Items.GILDED_PLATEBODY_3481, 1, 1, 30), - new WeightedChanceItem(Items.GILDED_PLATELEGS_3483, 1, 1, 30), - new WeightedChanceItem(Items.GILDED_PLATESKIRT_3485, 1, 1, 30), - new WeightedChanceItem(Items.GILDED_KITESHIELD_3488, 1, 1, 30), - //12 3rd Age = 120 = to hit 3% - new WeightedChanceItem(Items.THIRD_AGE_RANGE_TOP_10330, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_RANGE_LEGS_10332, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_RANGE_COIF_10334, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_VAMBRACES_10336, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_ROBE_TOP_10338, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_ROBE_10340, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_MAGE_HAT_10342, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_AMULET_10344, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_PLATELEGS_10346, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_PLATEBODY_10348, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_FULL_HELMET_10350, 1, 1, 10), - new WeightedChanceItem(Items.THIRD_AGE_KITESHIELD_10352, 1, 1, 10) }; - - /** - * The casket. - */ - private final Item casket; - - /** - * The length hash of the level. - */ - private final int lengthHash; - - /** - * The reward items. - */ - private final ChanceItem[] rewards; - - /** - * Constructs a new {@Code ClueLevel} {@Code Object} - * - * @param casket - * the casket. - * @param lengthHash - * the hash. - * @param rewards - * the rewards. - */ - private ClueLevel(Item casket, int lengthHash, ChanceItem... rewards) { - this.casket = casket; - this.lengthHash = lengthHash; - this.rewards = rewards; - } - - /** - * Opens a cakset for the player. - * - * @param player - * the player. - * @param casket - * the casket. - */ - public void open(Player player, Item casket) { - if (casket != null) { - if (!player.getInventory().containsItem(casket)) { - return; - } - } - if (player.getTreasureTrailManager().isCompleted() || GameWorld.getSettings().isDevMode()) { - final List rewards = getLoot(player); - player.getInterfaceManager().open(new Component(364).setCloseEvent(new CloseEvent() { - private boolean given; - - @Override - public boolean close(Player player, Component c) { - if (!given) { - for (Item item : rewards) { - player.getInventory().add(item, player); - } - given = true; - } - return true; - } - })); - if (casket != null) { - player.getInventory().remove(casket); - } - player.getTreasureTrailManager().incrementClues(this); - player.getTreasureTrailManager().clearTrail(); - player.sendMessage("Well done, you've completed the Treasure Trail!"); - player.sendMessage(getChatColor(this) + "You have completed " + player.getTreasureTrailManager().getCompletedClues(this) + " " + this.getName().toLowerCase() + " Treasure Trails."); - long value = 0; - for (Item item : rewards) { - value += item.getValue(); - } - player.sendMessage("Your clue is worth approximately " + NumberFormat.getInstance().format(value) + " coins!"); - player.getPacketDispatch().sendAccessMask(1278, 4, 364, 0, 6); - InterfaceContainer.generateItems(player, rewards.toArray(new Item[] {}), new String[] { "" }, 364, 4, 3, 3); - return; - } - Item clue = ClueScrollPlugin.getClue(this); - if (clue == null) { - player.sendMessage("Error! Clue not found! Report to admin."); - return; - } - if (casket != null) { - player.getInventory().replace(clue, casket.getSlot()); - } else { - player.getInventory().add(clue); - } - player.getTreasureTrailManager().setClueId(clue.getId()); - player.getDialogueInterpreter().sendItemMessage(clue, "You've found another clue!"); - } - - /** - * Gets the rewards.ds. - * - * @return the rewar - */ - public List getLoot(Player player) { - List items = new ArrayList<>(20); - List ids = new ArrayList<>(20); - items.addAll(Arrays.asList(DEFAULT_REWARDS)); - items.addAll(Arrays.asList(this.getRewards())); - List rewards = new ArrayList<>(20); - int size = RandomFunction.random(1, 6); - if (this == HARD) { - size = RandomFunction.random(4, 6); - } - Item item = null; - for (int i = 0; i < size; i++) { - rewards.addAll(RandomFunction.rollChanceTable(false,items)); - } - /* - int rand = 1400;// 2000 - if (this == HARD && rand == 11) - */ - //Super Rare Table - if (this == HARD && RandomFunction.random(100) == 50) { - rewards.remove(0); - rewards.add(RandomFunction.rollWeightedChanceTable(SUPER_RARE).asItem()); - } - return rewards; - } - - /** - * Gets a reward item. - * - * @return the item. - */ - public Item getReward(List items) { - Collections.shuffle(items); - return RandomFunction.getChanceItem(items.toArray(new ChanceItem[] {})).getRandomItem(); - } - - /** - * Gets the clue level for the casket. - * - * @param node - * the node. - * @return the level. - */ - public static ClueLevel forCasket(Item node) { - for (ClueLevel level : values()) { - if (node.getId() == level.getCasket().getId()) { - return level; - } - } - return null; - } - - /** - * Gets the Chat color to send on completed clues. - * @param level The clue level. - * @return the chat color. - */ - public static String getChatColor(ClueLevel level) { - if (level == ClueLevel.HARD) { - return ""; - } - if (level == ClueLevel.MEDIUM) { - return ""; - } - return ""; - } - /** - * Gets the maximum length. - * - * @return the length. - */ - public int getMaximumLength() { - return lengthHash & 0xFFFF; - } - - /** - * Gets the minimum length. - * - * @return the length. - */ - public int getMinimumLength() { - return lengthHash >> 16 & 0xFFFF; - } - - /** - * Gets the brewards. - * - * @return the rewards - */ - public ChanceItem[] getRewards() { - return rewards; - } - - /** - * Gets the bcasket. - * - * @return the casket - */ - public Item getCasket() { - return casket; - } - - /** - * Gets the blengthHash. - * - * @return the lengthHash - */ - public int getLengthHash() { - return lengthHash; - } - - /** - * Gets the name of the clue level. - * @return the name. - */ - public String getName() { - return toString(); - } -} diff --git a/Server/src/main/java/core/game/content/zone/WildernessAreaZone.java b/Server/src/main/java/core/game/content/zone/WildernessAreaZone.java deleted file mode 100644 index 118076c52..000000000 --- a/Server/src/main/java/core/game/content/zone/WildernessAreaZone.java +++ /dev/null @@ -1,580 +0,0 @@ -package core.game.content.zone; - -import core.game.content.dialogue.DialogueAction; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.action.DoorActionHandler; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.gather.SkillingResource; -import core.game.node.entity.skill.gather.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 rs09.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 rs09.plugin.PluginManager; - -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); - PluginManager.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; - } - if (TutorialSession.getExtension(player).getStage() == 35) { - TutorialStage.load(player, 36, false); - } - 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/java/core/game/content/zone/YanilleAgilityDungeon.java b/Server/src/main/java/core/game/content/zone/YanilleAgilityDungeon.java deleted file mode 100644 index 68efb361a..000000000 --- a/Server/src/main/java/core/game/content/zone/YanilleAgilityDungeon.java +++ /dev/null @@ -1,220 +0,0 @@ -package core.game.content.zone; - -import core.game.content.global.action.ClimbActionHandler; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; -import core.game.interaction.Option; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -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; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -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.Plugin; -import rs09.plugin.PluginManager; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Handles the yanille agility dungeon. - * @author Vexia - */ -@Initializable -public class YanilleAgilityDungeon extends MapZone implements Plugin { - - /** - * The herb items. - */ - private static final Item[] HERBS = new Item[] { new Item(205, 2), new Item(207, 3), new Item(209), new Item(211), new Item(213), new Item(219) }; - - /** - * Constructs a new {@Code YanilleAgilityDungeon} {@Code - * Object} - */ - public YanilleAgilityDungeon() { - super("Yanilee agility", true); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ZoneBuilder.configure(this); - PluginManager.definePlugin(new SalarinTwistedNPC()); - return this; - } - - @Override - public boolean interact(Entity entity, Node target, Option option) { - if (entity instanceof Player) { - Player player = entity.asPlayer(); - switch (target.getId()) { - case 1728: - player.teleport(new Location(2620, 9565, 0)); - return true; - case 1729: - player.teleport(new Location(2620, 9496, 0)); - return true; - case 2316: - player.teleport(Location.create(2569, 9525, 0)); - return true; - case 2318: - case 2317: - if (player.getSkills().getLevel(Skills.AGILITY) < 67) { - player.getDialogueInterpreter().sendDialogue("You need an Agility level of at least 67 in order to do this."); - return true; - } - Location loc = target.getId() == 2317 ? new Location(2615, 9503, 0) : new Location(2617, 9572, 0); - ClimbActionHandler.climb(player, target.getId() == 2317 ? ClimbActionHandler.CLIMB_UP : ClimbActionHandler.CLIMB_DOWN, loc); - player.sendMessage("You climb " + (target.getId() == 2317 ? "up" : "down") + " the pile of rubble..."); - player.getSkills().addExperience(Skills.AGILITY, 5.5, true); - return true; - case 35969: - case 2303: - handleBalancingLedge(player, target.asScenery()); - return true; - case 377: - if (!player.getInventory().contains(993, 1)) { - player.sendMessage("The chest is locked."); - } else { - if (player.getInventory().freeSlots() == 0) { - player.sendMessage("You don't have enough inventory space."); - return true; - } - player.lock(1); - player.getInventory().remove(new Item(993)); - player.sendMessages("You unlock the chest with your key...", "A foul gas seeps from the chest"); - player.getStateManager().register(EntityState.POISONED, true, 28, player); - for (Item item : HERBS) { - player.getInventory().add(item, player); - } - SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(target.getId() + 1), 5); - } - return true; - case 378: - switch (option.getName().toLowerCase()) { - case "search": - player.sendMessage("The chest is empty."); - break; - case "shut": - break; - } - return true; - } - } - return super.interact(entity, target, option); - } - - /** - * Handles the balancing ledge. - * @param player the player. - * @param object the object. - */ - private void handleBalancingLedge(final Player player, Scenery object) { - if (player.getSkills().getLevel(Skills.AGILITY) < 40) { - player.getDialogueInterpreter().sendDialogue("You need an Agility level of at least 40 in order to do this."); - return; - } - final Direction dir = Direction.getLogicalDirection(player.getLocation(), object.getLocation()); - final int diff = player.getLocation().getY() == 9512 ? 0 : 1; - Location end = object.getLocation(); - double xp = 0.0; - if (AgilityHandler.hasFailed(player, 40, 0.01)) { - player.lock(3); - GameWorld.getPulser().submit(new Pulse(2, player) { - @Override - public boolean pulse() { - AgilityHandler.fail(player, 1, new Location(2572, 9570, 0), Animation.create(761 - diff), RandomFunction.random(1, 3), "You lost your balance!"); - return true; - } - }); - } else { - xp = 22.5; - end = object.getLocation().transform(dir.getStepX() * 7, dir.getStepY() * 7, 0); - } - AgilityHandler.walk(player, -1, player.getLocation(), end, Animation.create(157 - diff), xp, null); - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public void configure() { - register(new ZoneBorders(2544, 9481, 2631, 9587)); - } - - /** - * Handles the salarin twisted npc. - * @author Vexia - */ - public static class SalarinTwistedNPC extends AbstractNPC { - - /** - * The spell ids. - */ - private static final int[] SPELL_IDS = new int[] { 1, 4, 6, 8 }; - - /** - * Constructs a new {@Code SalarinTwistedNPC} {@Code - * Object} - */ - public SalarinTwistedNPC() { - super(-1, null); - } - - /** - * Constructs a new {@Code SalarinTwistedNPC} {@Code - * Object} - * @param id the id. - * @param location the location. - */ - public SalarinTwistedNPC(int id, Location location) { - super(id, location); - super.setAggressive(true); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new SalarinTwistedNPC(id, location); - } - - @Override - public void checkImpact(BattleState state) { - if (state.getStyle() != CombatStyle.MAGIC) { - state.neutralizeHits(); - return; - } - if (state.getSpell() == null) { - state.neutralizeHits(); - return; - } - CombatSpell spell = state.getSpell(); - for (int id : SPELL_IDS) { - if (id == spell.getSpellId()) { - return; - } - } - state.neutralizeHits(); - } - - @Override - public int[] getIds() { - return new int[] { 205 }; - } - - } - -} diff --git a/Server/src/main/java/core/game/content/zone/phasmatys/PhasmatysZone.java b/Server/src/main/java/core/game/content/zone/phasmatys/PhasmatysZone.java deleted file mode 100644 index 4a448c8e7..000000000 --- a/Server/src/main/java/core/game/content/zone/phasmatys/PhasmatysZone.java +++ /dev/null @@ -1,372 +0,0 @@ -package core.game.content.zone.phasmatys; - -import api.ContentAPI; -import core.game.content.global.Bones; -import core.game.content.global.action.ClimbActionHandler; -import core.game.interaction.Option; -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.entity.skill.Skills; -import core.game.node.entity.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.Initializable; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; - -/** - * Handles the phasmatyz zone area. - * - * @author Vexia - */ -@Initializable -public final class PhasmatysZone extends MapZone implements Plugin { - int b; - Bones[] bones; - Player player; - Bones bone; - boolean notFirst; - - - /** - * Constructs a new {@code PhasmatysZone} {@code Object}. - */ - public PhasmatysZone() { - super("Port phasmatys", true); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ZoneBuilder.configure(this); - PluginManager.definePlugin(new GravingasNPC()); - PluginManager.definePlugin(new NecrovarusDialogue()); - PluginManager.definePlugin(new GhostSailorDialogue()); - PluginManager.definePlugin(new EctoplasmFillPlugin()); - PluginManager.definePlugin(new GhostDiscipleDialogue()); - PluginManager.definePlugin(new GhostVillagerDialogue()); - PluginManager.definePlugin(new GhostInkeeperDialogue()); - return this; - } - - - @Override - public boolean interact(Entity e, Node target, Option option) { - if (e.isPlayer()) { - player = e.asPlayer(); - if (target instanceof NPC) { - NPC npc = (NPC) target; - if ((npc.getName().toLowerCase().contains("ghost") || npc.getName().equalsIgnoreCase("velorina") || npc.getName().contains("husband")) && !hasAmulet(player)) { - player.getDialogueInterpreter().open("ghosty dialogue", target); - return true; - } - } - switch (target.getId()) { - case 5259: - handleEnergyBarrier(player, (Scenery) target); - return true; - case 5267: - player.animate(Animation.create(536)); - ContentAPI.sendMessage(player, "The trapdoor opens..."); - SceneryBuilder.replace((Scenery) target, ((Scenery) target).transform(5268)); - return true; - case 5268: - if (option.getName().equals("Close")) { - player.animate(Animation.create(535)); - ContentAPI.sendMessage(player, "You close the trapdoor."); - SceneryBuilder.replace((Scenery) target, ((Scenery) target).transform(5267)); - } else { - ContentAPI.sendMessage(player, "You climb down through the trapdoor..."); - player.getProperties().setTeleportLocation(Location.create(3669, 9888, 3)); - } - return true; - case 7434: // Trapdoors in bar - if (option.getName().equalsIgnoreCase("open")) { - SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(7435)); - } - break; - case 7435: // open trapdoor in bar - if (option.getName().equalsIgnoreCase("close")) { - SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(7434)); - } - break; - case 9308: - if (player.getSkills().getStaticLevel(Skills.AGILITY) < 58) { - player.sendMessage("You need an agility level of at least 58 to climb down this wall."); - return true; - } - player.getProperties().setTeleportLocation(Location.create(3671, 9888, 2)); - return true; - case 9307: - if (player.getSkills().getStaticLevel(Skills.AGILITY) < 58) { - player.sendMessage("You need an agility level of at least 58 to climb up this wall."); - return true; - } - player.getProperties().setTeleportLocation(Location.create(3670, 9888, 3)); - return true; - case 5264: - ClimbActionHandler.climb(player, Animation.create(828), Location.create(3654, 3519, 0)); - return true; - case 5282: - worship(player); - return true; - } - } - 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. - * - * @param player the player. - * @return {@code True} if so. - */ - public static boolean hasAmulet(Player player) { - 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. - * - * @param player the player. - */ - private void worship(Player player) { - Bones bone = null; - for (Item i : player.getInventory().toArray()) { - if (i == null) { - continue; - } - Bones b = Bones.forBoneMeal(i.getId()); - if (b != null) { - bone = b; - break; - } - } - if (!player.getInventory().contains(4286, 1) && bone == null) { - player.getDialogueInterpreter().sendDialogue("You don't have any ectoplasm or crushed bones to put into the", "Ectofuntus."); - return; - } - if (bone == null) { - player.getDialogueInterpreter().sendDialogue("You need a pot of crushed bones to put into the Ectofuntus."); - return; - } - if (!player.getInventory().contains(4286, 1)) { - player.getDialogueInterpreter().sendDialogue("You need ectoplasm to put into the Ectofuntus."); - return; - } - if (player.getInventory().remove(bone.getBoneMeal(), new Item(4286, 1))) { - player.lock(1); - player.animate(Animation.create(1651)); - 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."); - player.getSavedData().getGlobalData().setEctoCharges(player.getSavedData().getGlobalData().getEctoCharges() + 1); - } - } - - /** - * Handles the passing of the energy barrier. - * - * @param player the player. - * @param object the object. - */ - private void handleEnergyBarrier(Player player, Scenery object) { - boolean force = object.getLocation().equals(3659, 3508, 0) && player.getLocation().getY() < 3508 || object.getLocation().equals(3652, 3485, 0) && player.getLocation().getX() > 3652; - if (!force && !player.getInventory().contains(4278, 2)) { - player.getDialogueInterpreter().open(1706); - return; - } - if (force || player.getInventory().remove(new Item(4278, 2))) { - final Direction direction = Direction.getLogicalDirection(player.getLocation(), object.getLocation()); - Location end = player.getLocation().transform(direction, 2); - if (player.getLocation().getY() >= 3508) { - end = object.getLocation().transform(0, -2, 0); - } - if (object.getLocation().equals(new Location(3652, 3485, 0))) { - end = object.getLocation().transform(player.getLocation().getX() >= 3653 ? -1 : 2, 0, 0); - } - AgilityHandler.walk(player, -1, player.getLocation(), end, null, 0.0, null); - } - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public void configure() { - register(new ZoneBorders(3583, 3456, 3701, 3552)); - register(new ZoneBorders(3667, 9873, 3699, 9914)); - } - -} diff --git a/Server/src/main/java/core/game/content/zone/rellekka/JarvaldDialogue.java b/Server/src/main/java/core/game/content/zone/rellekka/JarvaldDialogue.java deleted file mode 100644 index 332cb603a..000000000 --- a/Server/src/main/java/core/game/content/zone/rellekka/JarvaldDialogue.java +++ /dev/null @@ -1,246 +0,0 @@ -package core.game.content.zone.rellekka; - -import core.game.component.Component; -import core.game.content.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.LocationLogoutTask; -import core.game.system.task.LogoutTask; -import core.game.system.task.Pulse; -import rs09.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.addExtension(LogoutTask.class, new LocationLogoutTask(5, to ? Location.create(2544, 3759, 0) : Location.create(2620, 3685, 0))); - 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") + "."); - 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/java/core/game/content/zone/rellekka/RellekkaZone.java b/Server/src/main/java/core/game/content/zone/rellekka/RellekkaZone.java deleted file mode 100644 index e6837adfb..000000000 --- a/Server/src/main/java/core/game/content/zone/rellekka/RellekkaZone.java +++ /dev/null @@ -1,178 +0,0 @@ -package core.game.content.zone.rellekka; - -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.node.entity.skill.agility.AgilityHandler; -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.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -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.Plugin; -import rs09.plugin.PluginManager; - -/** - * Handles the rellekka zone. - * @author Vexia - */ -@Initializable -public final class RellekkaZone extends MapZone implements Plugin { - - /** - * Constructs a new {@code RellekkaZone} {@code Object}. - */ - public RellekkaZone() { - super("rellekka", true); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ZoneBuilder.configure(this); - PluginManager.definePlugin(new SailorDialogue()); - PluginManager.definePlugin(new JarvaldDialogue()); - PluginManager.definePlugins(new RellekaOptionHandler(), new MariaGunnarsDialogue()); - PluginManager.definePlugin(new OptionHandler() { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - return true; - } - - }); - return this; - } - - @Override - public boolean interact(Entity e, Node target, Option option) { - if (e instanceof Player) { - final Player player = (Player) e; - switch (target.getId()) { - case 4306: - case 4310: - case 4309: - case 4304: - case 4308: - player.sendMessage("Only Fremenniks may use this " + target.getName().toLowerCase() + "."); - return true; - /* - * case 1301: player.sendMessage("Only Fremenniks may " + - * (option.equals("Trade") ? "buy clothes here" : - * "change their shoes here") + "."); return true; - */ - case 4165: - player.getDialogueInterpreter().open(1288); - return true; - case 4166: - player.sendMessage("This door is locked tightly shut."); - return true; - case 1288: - player.getDialogueInterpreter().sendDialogues((NPC) target, null, "I have no interest in talking to you just now", "outerlander."); - return true; - case 34286: - player.getDialogueInterpreter().sendDialogues(1289, null, "Outerlander... do not test my patience. I do not take", "kindly to people wandering in here and acting as though", "they own the place."); - return true; - case 4148: - player.getDialogueInterpreter().sendDialogues(1278, null, "Hey, outerlander. You can't go through there. Talent", "only, backstage."); - return true; - case 100: - player.getDialogueInterpreter().sendDialogue("You try to open the trapdoor but it won't budge! It looks like the", "trapdoor can only be opened from the other side."); - return true; - case 2435: - case 2436: - case 2437: - case 2438: - if (option.getName().equals("Travel")) { - player.getDialogueInterpreter().open(target.getId(), target, true); - return true; - } - break; - } - } - return super.interact(e, target, option); - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public void configure() { - register(new ZoneBorders(2602, 3639, 2739, 3741)); - } - - /** - * Handles options related to relleka. - * @author Vexia - */ - public static final class RellekaOptionHandler extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(4616).getHandlers().put("option:cross", this); - SceneryDefinition.forId(4615).getHandlers().put("option:cross", this); - SceneryDefinition.forId(5847).getHandlers().put("option:climb-over", this); - SceneryDefinition.forId(5008).getHandlers().put("option:enter",this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "cross": - switch (node.getId()) { - case 4616: - case 4615: - crossRellekaBridge(player, (Scenery) node); - break; - } - break; - case "climb-over": - switch (node.getId()) { - case 5847: - AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(0, player.getLocation().getY() <= 3657 ? 3 : -3, 0), Animation.create(840), 20, 1, null, 0); - break; - } - break; - case "enter": - switch(node.getId()){ - case 5008: - player.getProperties().setTeleportLocation(Location.create(2773, 10162, 0)); - break; - } - break; - } - return true; - } - - /** - * Crosses the relleka bridge. - * @param player the player. - * @param node the node. - */ - private void crossRellekaBridge(Player player, Scenery node) { - boolean east = node.getId() == 4616; - player.lock(2); - if (player.getLocation().equals(node.getLocation())) { - AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(east ? -2 : 2, 0, 0), Animation.create(1115), 20, 1.0, null, 1); - return; - } - AgilityHandler.forceWalk(player, -1, player.getLocation(), player.getLocation().transform(east ? -1 : 1, 0, 0), ForceMovement.WALK_ANIMATION, 10, 0.0, null); - AgilityHandler.forceWalk(player, -1, player.getLocation().transform(east ? -1 : 1, 0, 0), player.getLocation().transform(east ? -3 : 3, 0, 0), Animation.create(1115), 20, 1.0, null, 1); - } - - } -} diff --git a/Server/src/main/java/core/game/content/zone/rellekka/SailorDialogue.java b/Server/src/main/java/core/game/content/zone/rellekka/SailorDialogue.java deleted file mode 100644 index 5e5b12e60..000000000 --- a/Server/src/main/java/core/game/content/zone/rellekka/SailorDialogue.java +++ /dev/null @@ -1,77 +0,0 @@ -package core.game.content.zone.rellekka; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.world.map.Location; - -import static rs09.tools.DialogueConstKt.END_DIALOGUE; - -/** - * Handles the sailor dialogue. - * @author Vexia - */ -public final class SailorDialogue extends DialoguePlugin { - - /** - * If its the relleka npc. - */ - private boolean rellekaNpc; - - /** - * Constructs a new {@Code SailorDialogue} {@Code Object} - */ - public SailorDialogue() { - - } - - /** - * Constructs a new {@Code SailorDialogue} {@Code Object} - * @param player the player. - */ - public SailorDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SailorDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - rellekaNpc = npc.getId() == 1385; - player("Ship's closed due to stingrays?"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("Yes."); - stage = END_DIALOGUE; - //npc("Hello again, brother " + player.getUsername() + ". If you're ready to jump", "aboard, we're all ready to set sail with the tide!"); - //stage++; - break; - case 1: - player("Let's go!"); - stage++; - break; - case 2: - end(); - player.lock(); - player.sendMessage("You board the longship..."); - //RellekkaZone.sail(player, rellekaNpc ? "Miscellania" : "Rellekka", rellekaNpc ? Location.create(2581, 3845, 0) : Location.create(2629, 3693, 0)); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1304 }; - } - -} diff --git a/Server/src/main/java/core/game/ge/BuyingLimitation.java b/Server/src/main/java/core/game/ge/BuyingLimitation.java deleted file mode 100644 index 9a059d16b..000000000 --- a/Server/src/main/java/core/game/ge/BuyingLimitation.java +++ /dev/null @@ -1,89 +0,0 @@ -package core.game.ge; - -import core.cache.def.impl.ItemDefinition; -import rs09.game.system.config.ItemConfigParser; - -import java.util.HashMap; -import java.util.Map; - -/** - * Handles the buying limitations. - * @author Emperor - */ -public final class BuyingLimitation { - - /** - * Mapping holding buying amounts per player for every item id. - */ - private static final Map> CACHE = new HashMap<>(); - - /** - * Constructs a new {@code BuyingLimitation} {@code Object}. - */ - private BuyingLimitation() { - /* - * empty. - */ - } - - /** - * Gets the maximum amount the player can buy of this item. - * @param itemId The item id. - * @param playerUID The player UID. - * @return The maximum amount to buy. - */ - public static int getMaximumBuy(int itemId, int playerUID) { - Map data = CACHE.get(itemId); - Integer current = 0; - if (data != null) { - current = data.get(playerUID); - } - if (current == null) { - current = 0; - } - return ItemDefinition.forId(itemId).getConfiguration(ItemConfigParser.GE_LIMIT, 25000) - current; - } - - /** - * Updates the currently bought amount. - * @param itemId The item id. - * @param playerUID The player UID. - * @param amount The amount. - */ - public static void updateBoughtAmount(int itemId, int playerUID, int amount) { - Map data = CACHE.get(itemId); - if (data == null) { - CACHE.put(itemId, data = new HashMap<>()); - } - Integer current = data.get(playerUID); - if (current == null) { - current = 0; - } - current += amount; - data.put(playerUID, current); - } - - /** - * Checks if the offer is limited. - * @return {@code True} if so. - */ - public static boolean isLimited(int itemId, int playerUID) { - Map data = CACHE.get(itemId); - if (data == null) { - return false; - } - Integer current = data.get(playerUID); - if (current == null) { - return false; - } - int max = ItemDefinition.forId(itemId).getConfiguration(ItemConfigParser.GE_LIMIT, 25000); - return current >= max; - } - - /** - * Clears the cached data. - */ - public static void clear() { - CACHE.clear(); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/ge/GrandExchangeDatabase.java b/Server/src/main/java/core/game/ge/GrandExchangeDatabase.java deleted file mode 100644 index cd930bc31..000000000 --- a/Server/src/main/java/core/game/ge/GrandExchangeDatabase.java +++ /dev/null @@ -1,259 +0,0 @@ -package core.game.ge; - -import rs09.ServerConstants; -import core.cache.def.impl.ItemDefinition; -import rs09.game.system.SystemLogger; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -/** - * Represents the grand exchange database. - * @author Ceikry - */ -public final class GrandExchangeDatabase { - - /** - * The grand exchange database mapping. - */ - private static final Map DATABASE = new HashMap<>(); - - /** - * The minimum amount of unique trades required for an entry to change its - * value. - */ - private static final int MINIMUM_TRADES = 10;// 200 - - /** - * The amount of hours between each update cycle. - */ - private static final int UPDATE_CYCLE_HOURS = 3; - - /** - * The next update. - */ - private static long nextUpdate; - - /** - * If the G.E database has initialized. - */ - private static boolean initialized; - - /** - * Initializes the database - */ - public static void init() { - try { - File db = new File(ServerConstants.GRAND_EXCHANGE_DATA_PATH + "gedb.xml"); - if(!db.exists()){ - initNewDB(); - return; - } - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder(); - Document doc = builder.parse(db); - - doc.getDocumentElement().normalize(); - NodeList offers = doc.getElementsByTagName("offer"); - for(int i = 0; i < offers.getLength(); i++){ - Node offer = offers.item(i); - if(offer.getNodeType() == Node.ELEMENT_NODE){ - Element offerElement = (Element) offer; - int itemId = Integer.parseInt(offerElement.getElementsByTagName("id").item(0).getTextContent()); - int value = Integer.parseInt(offerElement.getElementsByTagName("value").item(0).getTextContent()); - if(value < 1){ - value = 1; - } - int uniqueTrades = Integer.parseInt(offerElement.getElementsByTagName("uniqueTrades").item(0).getTextContent()); - long totalValue = Integer.parseInt(offerElement.getElementsByTagName("totalValue").item(0).getTextContent()); - long lastUpdate = Integer.parseInt(offerElement.getElementsByTagName("lastUpdate").item(0).getTextContent()); - - - GrandExchangeEntry e = new GrandExchangeEntry(itemId); - e.setValue(value); - e.setUniqueTrades(uniqueTrades); - e.setTotalValue(totalValue); - e.setLastUpdate(lastUpdate); - NodeList valueLog = offerElement.getElementsByTagName("valueLog"); - e.setLogLength(valueLog.getLength()); - for(int logEntry = 0; logEntry < valueLog.getLength(); logEntry++){ - int val = Integer.parseInt(valueLog.item(logEntry).getTextContent()); - e.getValueLog()[logEntry] = val; - } - DATABASE.put(itemId,e); - } - } - initialized = true; - } catch (Exception e){ - e.printStackTrace(); - initNewDB(); - } - } - - public static void initNewDB(){ - SystemLogger.logWarn("Initializing new Grand Exchange DB! This may take a moment..."); - ItemDefinition.getDefinitions().values().forEach(def -> { - if(def.getId() != 2572){ - GrandExchangeEntry e = new GrandExchangeEntry(def.getId()); - e.setValue(def.getValue()); - e.setLogLength(0); - DATABASE.put(def.getId(), e); - } - }); - initialized = true; - } - - - /** - * Updates the entry values, if needed. - */ - public static void checkUpdate() { - if (nextUpdate < System.currentTimeMillis()) { - updateValues(); - } - } - - /** - * Dumps the grand exchange database. - */ - public static void save() { - File f = new File(ServerConstants.GRAND_EXCHANGE_DATA_PATH + "gedb.xml"); - if(f.exists()){ - f.delete(); - } - try{ - DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = dbFactory.newDocumentBuilder(); - Document doc = builder.newDocument(); - - Element root = doc.createElement("database"); - doc.appendChild(root); - - for(GrandExchangeEntry e : DATABASE.values()) { - Element offer = doc.createElement("offer"); - root.appendChild(offer); - - Element itemId = doc.createElement("id"); - itemId.setTextContent("" + e.getItemId()); - offer.appendChild(itemId); - - Element value = doc.createElement("value"); - value.setTextContent("" + e.getValue()); - offer.appendChild(value); - - Element uniqueTrades = doc.createElement("uniqueTrades"); - uniqueTrades.setTextContent("" + e.getUniqueTrades()); - offer.appendChild(uniqueTrades); - - Element totalValue = doc.createElement("totalValue"); - totalValue.setTextContent("" + e.getTotalValue()); - offer.appendChild(totalValue); - - Element lastUpdate = doc.createElement("lastUpdate"); - lastUpdate.setTextContent("" + e.getLastUpdate()); - offer.appendChild(lastUpdate); - - int[] vLog = e.getValueLog(); - for (int item : vLog) { - if (item == 0) { - continue; - } - Element valueLog = doc.createElement("valueLog"); - valueLog.setTextContent("" + item); - offer.appendChild(valueLog); - } - } - - TransformerFactory factory = TransformerFactory.newInstance(); - Transformer transformer = factory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - DOMSource source = new DOMSource(doc); - StreamResult result = new StreamResult(f); - transformer.transform(source,result); - } catch (Exception e){ - e.printStackTrace(); - } - } - - /** - * Updates the item values. - */ - public static void updateValues() { - try { - for (GrandExchangeEntry entry : DATABASE.values()) { - if (entry.getUniqueTrades() < MINIMUM_TRADES || entry.getTotalValue() == 0) { - continue; - } - double newAverage = entry.getTotalValue() / entry.getUniqueTrades(); - double changePercentage = newAverage / (double) (entry.getValue() + .001); - if (changePercentage == 1.0) { - continue; - } else if (changePercentage > 1.15) { - changePercentage = 1.15; - } else if (changePercentage < 0.85) { - changePercentage = 0.85; - } - int newValue = (int) (entry.getValue() * changePercentage); - if (newValue == entry.getValue()) { - if (changePercentage > 1.0) { // Fixes 1gp not being - // influenced. - newValue++; - } else if (newValue > 0) { - newValue--; - } - } - entry.updateValue(newValue); - entry.setLastUpdate(nextUpdate); - } - } catch (Throwable t) { - t.printStackTrace(); - } - nextUpdate = System.currentTimeMillis() + (UPDATE_CYCLE_HOURS * (60 * 60 * 1000)); - } - - /** - * Gets the database. - * @return The database. - */ - public static Map getDatabase() { - return DATABASE; - } - - /** - * Gets the nextUpdate. - * @return The nextUpdate. - */ - public static long getNextUpdate() { - return nextUpdate; - } - - /** - * Sets the nextUpdate. - * @param nextUpdate The nextUpdate to set. - */ - public static void setNextUpdate(long nextUpdate) { - GrandExchangeDatabase.nextUpdate = nextUpdate; - } - - /** - * Checks if the grand exchange database has initialized. - * @return {@code True} if so. - */ - public static boolean hasInitialized() { - return initialized; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/ge/GrandExchangeEntry.java b/Server/src/main/java/core/game/ge/GrandExchangeEntry.java deleted file mode 100644 index fa0119f8f..000000000 --- a/Server/src/main/java/core/game/ge/GrandExchangeEntry.java +++ /dev/null @@ -1,219 +0,0 @@ -package core.game.ge; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * Represents a grand exchange entry, which contains the item id, current price - * and previous prices. - * @author Emperor - */ -public final class GrandExchangeEntry { - - /** - * The item id. - */ - private final int itemId; - - /** - * The item value. - */ - private int value; - - /** - * The value log. - */ - private int[] valueLog = new int[256]; - - /** - * The log length. - */ - private int logLength; - - /** - * The amount of unique trades completed. - */ - private int uniqueTrades; - - /** - * The total value amount of each the unique trades.
This is used to - * calculate the average price, so we know whether or not to update price. - */ - private long totalValue; - - /** - * The last update time stamp. - */ - private long lastUpdate; - - /** - * The mapping of completed buying offers. - */ - private Map boughtMap = new HashMap<>(); - - /** - * Constructs a new {@code GrandExchangeEntry} {@code Object}. - * @param itemId The item id. - */ - public GrandExchangeEntry(int itemId) { - this.itemId = itemId; - } - - /** - * Updates the item value. - * @param value The new item value to set. - */ - public void updateValue(int value) { - int[] newValueLog = new int[256]; - System.arraycopy(valueLog, 0, newValueLog, 1, logLength); - newValueLog[0] = this.value; - this.valueLog = newValueLog; - this.value = value; - this.uniqueTrades = 0; - this.totalValue = 0; - if (++logLength > 255) { - logLength = 255; - } - } - - /** - * Influences the price of this item. - * @param value The value offered in the exchange. - */ - public void influenceValue(int value) { - if (value < this.value * 0.5) { // Makes sure the player can't influence - // too much - value = (int) (this.value * 0.5); - } else if (value > this.value * 1.5) { - value = (int) (this.value * 1.5); - } - uniqueTrades++; - totalValue += value; - } - - /** - * Gets the itemId. - * @return The itemId. - */ - public int getItemId() { - return itemId; - } - - /** - * Gets the value. - * @return The value. - */ - public int getValue() { - return value; - } - - /** - * Sets the value. - * @param value The value to set. - */ - public void setValue(int value) { - this.value = value; - } - - /** - * Gets the valueLog. - * @return The valueLog. - */ - public int[] getValueLog() { - return valueLog; - } - - /** - * Sets the valueLog. - * @param valueLog The valueLog to set. - */ - public void setValueLog(int[] valueLog) { - this.valueLog = valueLog; - } - - /** - * Gets the uniqueTrades. - * @return The uniqueTrades. - */ - public int getUniqueTrades() { - return uniqueTrades; - } - - /** - * Sets the uniqueTrades. - * @param uniqueTrades The uniqueTrades to set. - */ - public void setUniqueTrades(int uniqueTrades) { - this.uniqueTrades = uniqueTrades; - } - - /** - * Gets the totalValue. - * @return The totalValue. - */ - public long getTotalValue() { - return totalValue; - } - - /** - * Sets the totalValue. - * @param totalValue The totalValue to set. - */ - public void setTotalValue(long totalValue) { - this.totalValue = totalValue; - } - - /** - * Gets the lastUpdate. - * @return The lastUpdate. - */ - public long getLastUpdate() { - return lastUpdate; - } - - /** - * Sets the lastUpdate. - * @param lastUpdate The lastUpdate to set. - */ - public void setLastUpdate(long lastUpdate) { - this.lastUpdate = lastUpdate; - } - - /** - * Gets the logLength. - * @return The logLength. - */ - public int getLogLength() { - return logLength; - } - - /** - * Sets the logLength. - * @param logLength The logLength to set. - */ - public void setLogLength(int logLength) { - this.logLength = logLength; - } - - /** - * Gets the boughtMap. - * @return The boughtMap. - */ - public Map getBoughtMap() { - return boughtMap; - } - - /** - * Sets the boughtMap. - * @param boughtMap The boughtMap to set. - */ - public void setBoughtMap(Map boughtMap) { - this.boughtMap = boughtMap; - } - - @Override - public String toString() { - return "GrandExchangeEntry [itemId=" + itemId + ", value=" + value + ", valueLog=" + Arrays.toString(valueLog) + ", logLength=" + logLength + ", uniqueTrades=" + uniqueTrades + ", totalValue=" + totalValue + ", lastUpdate=" + lastUpdate + ", boughtMap=" + boughtMap + "]"; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/Interaction.java b/Server/src/main/java/core/game/interaction/Interaction.java deleted file mode 100644 index a43caa0d8..000000000 --- a/Server/src/main/java/core/game/interaction/Interaction.java +++ /dev/null @@ -1,343 +0,0 @@ -package core.game.interaction; - -import core.game.container.Container; -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.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 rs09.game.interaction.InteractionListeners; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; - -/** - * Handles interaction between nodes. - * @author Emperor - */ -public class Interaction { - - /** - * The current options. - */ - private Option[] options = new Option[8]; - - /** - * The player. - */ - private final Node node; - - /** - * If the interaction option handlers have been initialized. - */ - private boolean initialized; - - /** - * Constructs a new {@code Interaction} {@code Object}. - * @param node The node reference. - */ - public Interaction(Node node) { - this.node = node; - } - - /** - * Sends the current option. - * @param node The node. - * @param index The index. - * @param name The option name. - */ - public static void sendOption(Node node, int index, String name) { - if (!(node instanceof Player)) { - return; - } - PacketRepository.send(InteractionOption.class, new InteractionOptionContext((Player) node, index, name)); - } - - /** - * Handles an interaction option being clicked. - * @param player The player using the option. - * @param option The option used. - */ - public void handle(final Player player, final Option option) { - try { - if (player.getLocks().isInteractionLocked() || option == null) { - return; - } - player.debug("Received interaction request " + option.getName()); - boolean hasHandler = option.getHandler() != null; - String pulseType = "interaction:" + option.getName() + ":" + node.hashCode(); - boolean walk = hasHandler && option.getHandler().isWalk(); - if (!walk && hasHandler && option.getHandler().isWalk(player, node)) { - walk = true; - } - if (!hasHandler || walk) { - handleWalkOption(player, option, pulseType); - } else if (hasHandler) { - player.debug("Option handler being used=" + option.getHandler().getClass().getSimpleName()); - handleDefaultOption(player, option, pulseType); - } else { - player.getPulseManager().runUnhandledAction(player, pulseType); - } - } catch (Exception e){ - e.printStackTrace(); - SystemLogger.logErr(this.getClass().getName() + e.getMessage()); - } - } - - /** - * Handles an item option. - * @param player The player. - * @param option The option. - * @param container The container the item is in. - */ - public void handleItemOption(final Player player, final Option option, final Container container) { - if (player.getLocks().isInteractionLocked()) { - return; - } - player.getPulseManager().clear("interaction:" + option.getName() + ":" + node.hashCode()); - GameWorld.getPulser().submit(new Pulse(1, player) { - @Override - public boolean pulse() { - try { - if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { - return true; - } - if(InteractionListeners.run(node.getId(),0,option.getName(),player,node)){ - return true; - } - if (option.getHandler() == null || !option.getHandler().handle(player, node, option.getName().toLowerCase())) { - player.getPacketDispatch().sendMessage("Nothing interesting happens."); - } - if (option.getHandler() != null) { - player.debug("Using item handler " + option.getHandler().getClass().getSimpleName()); - } - } catch (Exception e){ - e.printStackTrace(); - SystemLogger.logErr(this.getClass().getName() + e.getMessage()); - } - return true; - } - }); - } - - /** - * Handles an invalid interaction. - * @param player The player. - * @param node The node to interact with. - */ - public static void handleInvalidInteraction(final Player player, final Node node, final Option option) { - if (node == null) { - return; - } - if (node.getLocation() != null) { - if (player.getLocks().isMovementLocked()) { - return; - } - player.getPulseManager().run(new MovementPulse(player, node) { - @Override - public boolean pulse() { - try { - player.faceLocation(FaceLocationFlag.getFaceLocation(player, node)); - 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().getName() + e.getMessage()); - } - return true; - } - }, "interaction:invalid:" + node.hashCode()); - } else { - player.getPulseManager().runUnhandledAction(player, "interaction:invalid:" + node.hashCode()); - } - } - - /** - * Handles an option requiring the player to walk to the node. - * @param player The player. - * @param option The option. - * @param pulseType The pulse type. - */ - private void handleWalkOption(final Player player, final Option option, String pulseType) { - if (node.getLocation() == null) { - player.getPulseManager().runUnhandledAction(player, pulseType); - return; - } - if (player.getLocks().isMovementLocked()) { - player.getPulseManager().clear(pulseType); - return; - } - player.getPulseManager().run(new MovementPulse(player, node, option.getHandler()) { - @Override - public boolean pulse() { - try { - player.faceLocation(FaceLocationFlag.getFaceLocation(player, node)); - if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { - return true; - } - if (option == null || option.getHandler() == null || !option.getHandler().handle(player, node, option.getName().toLowerCase())) { - player.getPacketDispatch().sendMessage("Nothing interesting happens."); - } - } catch (Exception e){ - e.printStackTrace(); - SystemLogger.logErr(this.getClass().getName() + e.getMessage()); - } - return true; - } - }, pulseType); - } - - /** - * Handles a default option. - * @param player The player. - * @param option The option. - * @param pulseType The pulse type. - */ - private void handleDefaultOption(final Player player, final Option option, String pulseType) { - if (!option.getHandler().isDelayed(player)) { - if (player.getZoneMonitor().interact(node, option)) { - return; - } - player.getProperties().getCombatPulse().stop(); - if (!option.getHandler().handle(player, node, option.getName().toLowerCase())) { - player.getPulseManager().runUnhandledAction(player, pulseType); - } - return; - } - player.getPulseManager().run(new Pulse(1, player, node) { - @Override - public boolean pulse() { - if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { - return true; - } - if (!option.getHandler().handle(player, node, option.getName().toLowerCase())) { - player.getPacketDispatch().sendMessage("Nothing interesting happens."); - } - return true; - } - }, pulseType); - } - - /** - * Initializes the interaction options. - * @param nodeId The node id. - * @param names The option names. - */ - public void init(int nodeId, String... names) { - options = new Option[names.length]; - for (int i = 0; i < options.length; i++) { - String name = names[i]; - if (name != null && !name.equals("null")) { - set(new Option(name, i).setHandler(Option.defaultHandler(node, nodeId, name))); - } - continue; - } - } - - /** - * Sets the default option handlers (if not yet initialized). - */ - public void setDefault() { - if (initialized) { - return; - } - if (node instanceof Player) { - for (int i = 0; i < options.length; i++) { - remove(i); - } - set(Option._P_FOLLOW); - set(Option._P_TRADE); - set(Option._P_ASSIST); - } else if (node instanceof NPC) { - NPC npc = (NPC) node; - init(npc.getId(), npc.getDefinition().getOptions()); - } else if (node instanceof Scenery) { - Scenery object = (Scenery) node; - init(object.getId(), object.getDefinition().getOptions()); - } else if (node instanceof Item) { - Item item = (Item) node; - if (item.getLocation() != null) { - init(item.getId(), item.getDefinition().getGroundOptions()); - } else { - init(item.getId(), item.getDefinition().getInventoryOptions()); - } - } else { - throw new IllegalStateException("Unsupported node type - " + node); - } - initialized = true; - } - - /** - * Sets a new option. - * @param option The option. - */ - public void set(Option option) { - options[option.getIndex()] = option; - sendOption(node, option.getIndex(), option.getName()); - } - - /** - * Removes an option. - * @return {@code True} if the option got removed, {@code false} if the - * option wasn't set. - */ - public boolean remove(Option option) { - if (options[option.getIndex()] == option) { - remove(option.getIndex()); - return true; - } - return false; - } - - /** - * Removes an option. - * @param index The index. - */ - public void remove(int index) { - if (options[index] == null) { - return; - } - options[index] = null; - sendOption(node, index, "null"); - } - - /** - * Gets the option on the requested slot. - * @param index The slot index. - * @return The option on that slot, or {@code null} if there was no option. - */ - public Option get(int index) { - return options[index]; - } - - /** - * Gets the options. - * @return The options. - */ - public Option[] getOptions() { - return options; - } - - /** - * Gets the initialized value. - * @return The initialized. - */ - public boolean isInitialized() { - return initialized; - } - - /** - * Sets the initialized value. - * @param initialized The initialized to set. - */ - public void setInitialized(boolean initialized) { - this.initialized = initialized; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/MovementPulse.java b/Server/src/main/java/core/game/interaction/MovementPulse.java deleted file mode 100644 index a9ab85eb0..000000000 --- a/Server/src/main/java/core/game/interaction/MovementPulse.java +++ /dev/null @@ -1,408 +0,0 @@ -package core.game.interaction; - -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.system.task.Pulse; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.Point; -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.Function1; -import kotlin.jvm.functions.Function2; -import rs09.game.system.SystemLogger; - -import java.util.Deque; - -/** - * Handles a movement task. - * - * @author Emperor - */ -public abstract class MovementPulse extends Pulse { - - /** - * The moving entity. - */ - protected Entity mover; - - /** - * The destination node. - */ - protected Node destination; - - /** - * The destination's last location. - */ - private Location last; - - /** - * The pathfinder. - */ - private Pathfinder pathfinder; - - /** - * If running should be forced. - */ - private boolean forceRun; - - /** - * The option handler. - */ - private OptionHandler optionHandler; - - /** - * The use with handler. - */ - private UseWithHandler useHandler; - - /** - * The destination flag. - */ - private DestinationFlag destinationFlag; - - /** - * The location to interact from. - */ - private Location interactLocation; - - /** - * If the path couldn't be fully found. - */ - private boolean near; - - private Function2 overrideMethod; - - /** - * Constructs a new {@code MovementPulse} {@code Object}. - * - * @param mover The moving entity. - * @param destination The destination node. - */ - public MovementPulse(Entity mover, Node destination) { - this(mover, destination, null, false); - } - - /** - * Constructs a new {@code MovementPulse} {@code Object}. - * - * @param mover The moving entity. - * @param destination The destination node. - * @param forceRun If the entity is forced to run. - */ - public MovementPulse(Entity mover, Node destination, boolean forceRun) { - this(mover, destination, null, forceRun); - } - - /** - * Constructs a new {@code MovementPulse} {@code Object}. - * - * @param mover The moving entity. - * @param destination The destination node. - * @param pathfinder The pathfinder to use. - */ - public MovementPulse(Entity mover, Node destination, Pathfinder pathfinder) { - this(mover, destination, pathfinder, false); - } - - /** - * Constructs a new {@code MovementPulse} {@code Object}. - * - * @param mover The moving entity. - * @param destination The destination node. - * @param optionHandler The option handler used. - */ - public MovementPulse(Entity mover, Node destination, OptionHandler optionHandler) { - this(mover, destination, null, false); - this.optionHandler = optionHandler; - } - - /** - * Constructs a new {@code MovementPulse} {@code Object}. - * - * @param mover The moving entity. - * @param destination The destination node. - * @param useHandler The use with handler used. - */ - public MovementPulse(Entity mover, Node destination, UseWithHandler useHandler) { - this(mover, destination, null, false); - this.useHandler = useHandler; - } - - /** - * Constructs a new {@code MovementPulse} {@code Object}. - * - * @param mover The moving entity. - * @param destination The destination node. - * @param destinationFlag The destination flag. - */ - public MovementPulse(Entity mover, Node destination, DestinationFlag destinationFlag) { - this(mover, destination, null, false); - this.destinationFlag = destinationFlag; - } - - public MovementPulse(Entity mover, Node destination, DestinationFlag destinationFlag, Function2 method){ - this(mover,destination,null,false); - this.destinationFlag = destinationFlag; - this.overrideMethod = method; - } - - /** - * Constructs a new {@code MovementPulse} {@code Object}. - * - * @param mover The moving entity. - * @param destination The destination node. - * @param pathfinder The pathfinder to use. - * @param forceRun If the entity is forced to run. - */ - public MovementPulse(Entity mover, Node destination, Pathfinder pathfinder, boolean forceRun) { - super(1, mover, destination); - this.mover = mover; - this.destination = destination; - if (pathfinder == null) { - if (mover instanceof Player) { - this.pathfinder = Pathfinder.SMART; - } else { - this.pathfinder = Pathfinder.DUMB; - } - } else { - this.pathfinder = pathfinder; - } - this.forceRun = forceRun; - } - - @Override - public boolean update() { - mover.face(null); - if (mover == null || destination == null || mover.getViewport().getRegion() == null) { - return false; - } - - if (hasInactiveNode() || !mover.getViewport().getRegion().isActive()) { - stop(); - return true; - } - if (!isRunning()) { - return true; - } - findPath(); - if (mover.getLocation().equals(interactLocation)) { - try { - if (near || pulse()) { - if (mover instanceof Player) { - if (near) { - ((Player) mover).getPacketDispatch().sendMessage("I can't reach that."); - } - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext((Player) mover)); - } - stop(); - return true; - } - } catch (Exception e){ - e.printStackTrace(); - stop(); - } - } - return false; - } - - @Override - public void stop() { - super.stop(); - if (destination instanceof Entity) { - mover.face(null); - } - last = null; - } - - /** - * Finds a path to the destination, if necessary. - */ - public void findPath() { - if (mover instanceof NPC && mover.asNpc().isNeverWalks()) { - return; - } - if(destination.getLocation() == null){ - SystemLogger.logAlert(destination.getId() + " < ID"); - SystemLogger.logAlert(destination.getName() + " < NAME"); - SystemLogger.logAlert("ASDAD"); - return; - } - boolean inside = isInsideEntity(mover.getLocation()); - if (last != null && last.equals(destination.getLocation()) && !inside) { - return; - } - Location loc = null; - if (destinationFlag != null && overrideMethod == null) { - loc = destinationFlag.getDestination(mover, destination); - } - if(overrideMethod != null){ - loc = overrideMethod.invoke(mover,destination); - if(loc == destination.getLocation()) loc = destinationFlag.getDestination(mover,destination); - } - if (loc == null && optionHandler != null) { - loc = optionHandler.getDestination(mover, destination); - } - if (loc == null && useHandler != null) { - loc = useHandler.getDestination((Player) mover, destination); - } - if (loc == null && inside) { - loc = findBorderLocation(); - } - if (destination == null) { - return; - } - Path path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder); - near = !path.isSuccessful() || path.isMoveNear(); - interactLocation = mover.getLocation(); - if (!path.getPoints().isEmpty()) { - 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(); - } - - /** - * Finds the closest location next to the node. - * - * @return The location to walk to. - */ - private Location findBorderLocation() { - int size = destination.size(); - Location centerDest = destination.getLocation().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()); - main: - for (int i = 0; i < 4; i++) { - int amount = 0; - switch (direction) { - case NORTH: - amount = size - delta.getY(); - break; - case EAST: - amount = size - delta.getX(); - break; - case SOUTH: - amount = mover.size() + delta.getY(); - break; - case WEST: - amount = mover.size() + delta.getX(); - break; - default: - return null; - } - for (int j = 0; j < amount; j++) { - for (int s = 0; s < mover.size(); s++) { - switch (direction) { - case NORTH: - if (!direction.canMove(mover.getLocation().transform(s, j + mover.size(), 0))) { - direction = Direction.get((direction.toInteger() + 1) & 3); - continue main; - } - break; - case EAST: - if (!direction.canMove(mover.getLocation().transform(j + mover.size(), s, 0))) { - direction = Direction.get((direction.toInteger() + 1) & 3); - continue main; - } - break; - case SOUTH: - if (!direction.canMove(mover.getLocation().transform(s, -(j + 1), 0))) { - direction = Direction.get((direction.toInteger() + 1) & 3); - continue main; - } - break; - case WEST: - if (!direction.canMove(mover.getLocation().transform(-(j + 1), s, 0))) { - direction = Direction.get((direction.toInteger() + 1) & 3); - continue main; - } - break; - default: - return null; - } - } - } - Location location = mover.getLocation().transform(direction, amount); - return location; - } - return null; - } - - /** - * Checks if the mover is standing on an invalid position. - * - * @param l The location. - * @return {@code True} if so. - */ - private boolean isInsideEntity(Location l) { - if (!(destination instanceof Entity)) { - return false; - } - if (((Entity) destination).getWalkingQueue().isMoving()) { - return false; - } - Location loc = destination.getLocation(); - int size = destination.size(); - return Pathfinder.isStandingIn(l.getX(), l.getY(), mover.size(), mover.size(), loc.getX(), loc.getY(), size, size); - } - - /** - * Gets the forceRun. - * - * @return The forceRun. - */ - public boolean isForceRun() { - return forceRun; - } - - /** - * Sets the forceRun. - * - * @param forceRun The forceRun to set. - */ - public void setForceRun(boolean forceRun) { - this.forceRun = forceRun; - } - - /** - * Sets the current destination. - * - * @param destination The destination. - */ - public void setDestination(Node destination) { - this.destination = destination; - } - - /** - * Sets the last location. - * - * @param last The last location. - */ - public void setLast(Location last) { - this.last = last; - } - -} diff --git a/Server/src/main/java/core/game/interaction/city/BurthorpePlugin.java b/Server/src/main/java/core/game/interaction/city/BurthorpePlugin.java deleted file mode 100644 index 7c61fb7e3..000000000 --- a/Server/src/main/java/core/game/interaction/city/BurthorpePlugin.java +++ /dev/null @@ -1,37 +0,0 @@ -package core.game.interaction.city; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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/java/core/game/interaction/city/HomePlugin.java b/Server/src/main/java/core/game/interaction/city/HomePlugin.java deleted file mode 100644 index 1b6acbc8f..000000000 --- a/Server/src/main/java/core/game/interaction/city/HomePlugin.java +++ /dev/null @@ -1,34 +0,0 @@ -package core.game.interaction.city; - -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; - -/** - * Package -> core.game.interaction.city - * Created on -> 9/10/2016 @10:42 PM for 530 - * - * @author Ethan Kyle Millard - */ -@Initializable -public class HomePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(552).getHandlers().put("talk-to", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (node.getId()) { - case 552: - player.getDialogueInterpreter().open(552); - break; - } - return false; - } -} diff --git a/Server/src/main/java/core/game/interaction/city/KaramajaOptionPlugin.java b/Server/src/main/java/core/game/interaction/city/KaramajaOptionPlugin.java deleted file mode 100644 index b8abc70c5..000000000 --- a/Server/src/main/java/core/game/interaction/city/KaramajaOptionPlugin.java +++ /dev/null @@ -1,286 +0,0 @@ -package core.game.interaction.city; - -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 rs09.game.world.GameWorld; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.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 rs09.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 banana item. - */ - private static final Item BANANA = new Item(1963); - - /** - * 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(2072).getHandlers().put("option:search", this);// banana crate. - SceneryDefinition.forId(2072).getHandlers().put("option:fill", this);// banana crate. - 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; - case 2072: - int amt = player.getSavedData().getGlobalData().getKaramjaBananas(); - if (amt >= 10) { - player.getPacketDispatch().sendMessage("The crate is full of bananas."); - return true; - } - if (amt == 0) { - player.getPacketDispatch().sendMessage("The crate is completely empty."); - } else { - player.getPacketDispatch().sendMessage("The crate has " + amt + " banana " + (amt > 1 ? "s" : "") + "inside."); - } - break; - } - break; - case "fill": - if (player.getSavedData().getGlobalData().isLuthasTask()) { - int bananas = player.getInventory().getAmount(BANANA); - if (bananas > 0) { - player.getDialogueInterpreter().sendDialogue("You pack all your bananas into the crate."); - if (player.getInventory().remove(new Item(BANANA.getId(), bananas))) { - player.getSavedData().getGlobalData().setKaramjaBannanas(bananas + player.getSavedData().getGlobalData().getKaramjaBananas()); - } - } - } else { - player.getPacketDispatch().sendMessage("I don't know what goes in there."); - } - 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/java/core/game/interaction/city/KhardianDesertPlugin.java b/Server/src/main/java/core/game/interaction/city/KhardianDesertPlugin.java deleted file mode 100644 index 6d473da80..000000000 --- a/Server/src/main/java/core/game/interaction/city/KhardianDesertPlugin.java +++ /dev/null @@ -1,47 +0,0 @@ -package core.game.interaction.city; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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/java/core/game/interaction/city/ShiloVillagePlugin.java b/Server/src/main/java/core/game/interaction/city/ShiloVillagePlugin.java deleted file mode 100644 index 7e7559fb3..000000000 --- a/Server/src/main/java/core/game/interaction/city/ShiloVillagePlugin.java +++ /dev/null @@ -1,149 +0,0 @@ -package core.game.interaction.city; - -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.content.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 rs09.plugin.PluginManager; - -/** - * 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. - PluginManager.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/java/core/game/interaction/city/SophanemPlugin.java b/Server/src/main/java/core/game/interaction/city/SophanemPlugin.java deleted file mode 100644 index 4328b221c..000000000 --- a/Server/src/main/java/core/game/interaction/city/SophanemPlugin.java +++ /dev/null @@ -1,44 +0,0 @@ -package core.game.interaction.city; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * The plugin for handling stuff in Sophanem. - * @author jamix77 - * - */ -@Initializable -public class SophanemPlugin extends OptionHandler { - - @Override - 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); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final int id = node instanceof Scenery ? ((Scenery) node).getId() : ((Item) node).getId(); - switch (id) { - case 20275: - 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; - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/city/TzhaarCityPlugin.java b/Server/src/main/java/core/game/interaction/city/TzhaarCityPlugin.java deleted file mode 100644 index ba088c5fd..000000000 --- a/Server/src/main/java/core/game/interaction/city/TzhaarCityPlugin.java +++ /dev/null @@ -1,198 +0,0 @@ -package core.game.interaction.city; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.activity.ActivityManager; -import core.game.content.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/java/core/game/interaction/city/VarrockNodePlugin.java b/Server/src/main/java/core/game/interaction/city/VarrockNodePlugin.java deleted file mode 100644 index 57d9bad57..000000000 --- a/Server/src/main/java/core/game/interaction/city/VarrockNodePlugin.java +++ /dev/null @@ -1,310 +0,0 @@ -package core.game.interaction.city; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.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 rs09.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(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(17974).getHandlers().put("option:climb-up", 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); - SceneryBuilder.add(new Scenery(17974, new Location(3204, 9911), 10, 0)); - new KnockatDoorDialogue().init(); - 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 17974: - ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, new Location(3179, 3472), "You resurface."); - 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 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/java/core/game/interaction/city/lumbridge/CowFieldSign.java b/Server/src/main/java/core/game/interaction/city/lumbridge/CowFieldSign.java deleted file mode 100644 index 38ee9713f..000000000 --- a/Server/src/main/java/core/game/interaction/city/lumbridge/CowFieldSign.java +++ /dev/null @@ -1,61 +0,0 @@ -package core.game.interaction.city.lumbridge; - -import api.ContentAPI; -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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; -import rs09.GlobalStats; - -/** - * Cow field sign manager - * @author ceik - */ - -@Initializable -public class CowFieldSign extends OptionHandler { - private int DIALOGUE_KEY = 87905733; - @Override - public Plugin newInstance(Object arg) throws Throwable{ - new SignDialogue().init(); - SceneryDefinition.forId(31297).getHandlers().put("option:read",this); - return this; - } - @Override - public boolean handle(Player player, Node node, String option){ - if(node.getId() == 31297){ - player.getDialogueInterpreter().open(DIALOGUE_KEY); - } - return true; - } - public final class SignDialogue extends DialoguePlugin{ - public SignDialogue(){ - /** - * Empty - */ - } - public SignDialogue(Player player){super(player);} - @Override - public DialoguePlugin newInstance(Player player){return new SignDialogue(player);} - @Override - public boolean open(Object... args){ - int dailyCowDeaths = GlobalStats.getDailyCowDeaths(); - if(dailyCowDeaths > 0) { - ContentAPI.sendDialogue(player, "Local cowherders have reported that " + dailyCowDeaths + " cows have been slain in this field today by passing adventurers. Farmers throughout the land fear this may be an epidemic."); - } else { - ContentAPI.sendDialogue(player, "The Lumbridge cow population has been thriving today, without a single cow death to worry about!" ); - } - return true; - } - - @Override - public boolean handle(int componentId, int buttonId){ - end(); - return true; - } - public int[] getIds() {return new int[] {DIALOGUE_KEY};} - } -} diff --git a/Server/src/main/java/core/game/interaction/city/lumbridge/FredChest.java b/Server/src/main/java/core/game/interaction/city/lumbridge/FredChest.java deleted file mode 100644 index d89ec93d2..000000000 --- a/Server/src/main/java/core/game/interaction/city/lumbridge/FredChest.java +++ /dev/null @@ -1,38 +0,0 @@ -package core.game.interaction.city.lumbridge; - -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.game.node.scenery.SceneryBuilder; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the chest in farmer fred's house - * - * @author ceik - */ - -@Initializable -public class FredChest extends OptionHandler { - @Override - public Plugin newInstance(Object arg) throws Throwable{ - SceneryDefinition.forId(37009).getHandlers().put("option:open",this); - SceneryDefinition.forId(37010).getHandlers().put("option:shut",this); - SceneryDefinition.forId(37010).getHandlers().put("option:search",this); - return this; - } - @Override - public boolean handle(Player player, Node node, String option){ - if(option.equals("open")){ - SceneryBuilder.replace(node.asScenery(),new Scenery(37010,node.asScenery().getLocation(),node.asScenery().getRotation())); - } else if (option.equals("shut")){ - SceneryBuilder.replace(node.asScenery(),new Scenery(37009,node.asScenery().getLocation(),node.asScenery().getRotation())); - } else if (option.equals("search")){ - player.getPacketDispatch().sendMessage("You search the chest but find nothing."); - } - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/city/lumbridge/GnomeCopterSign.java b/Server/src/main/java/core/game/interaction/city/lumbridge/GnomeCopterSign.java deleted file mode 100644 index bbaec9ed6..000000000 --- a/Server/src/main/java/core/game/interaction/city/lumbridge/GnomeCopterSign.java +++ /dev/null @@ -1,52 +0,0 @@ -package core.game.interaction.city.lumbridge; - -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; - -@Initializable -public class GnomeCopterSign extends OptionHandler { - public int DIALOGUE_ID = 989769182; - @Override - public Plugin newInstance(Object arg) throws Throwable { - new SignDialogue().init(); - SceneryDefinition.forId(30037).getHandlers().put("option:read",this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String string) { - if (node.getId() == 30037) { - player.getDialogueInterpreter().open(DIALOGUE_ID); - } - return true; - } - public final class SignDialogue extends DialoguePlugin{ - public SignDialogue(){ - /** - * Empty - */ - } - public SignDialogue(Player player){super(player);} - @Override - public DialoguePlugin newInstance(Player player){ return new SignDialogue(player);} - - @Override - public boolean open(Object... args){ - interpreter.sendPlainMessage(false,"Come check out our gnome copters up north!","Disclaimer: EXTREMELY WIP"); - stage = 0; - return true; - } - @Override - public boolean handle(int componentId, int buttonId){ - end(); - return true; - } - @Override - public int[] getIds(){return new int[] {DIALOGUE_ID};} - } -} diff --git a/Server/src/main/java/core/game/interaction/city/portsarim/AhabBeerInteraction.java b/Server/src/main/java/core/game/interaction/city/portsarim/AhabBeerInteraction.java deleted file mode 100644 index 1f9a943c6..000000000 --- a/Server/src/main/java/core/game/interaction/city/portsarim/AhabBeerInteraction.java +++ /dev/null @@ -1,28 +0,0 @@ -package core.game.interaction.city.portsarim; - -import core.game.interaction.Option; -import core.game.interaction.SpecialGroundInteraction; -import core.game.interaction.SpecialGroundItems; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; - -/** - * Handles Ahab's beer in port sarim - * @author ceik - */ - -public class AhabBeerInteraction extends SpecialGroundInteraction { - //The dialogue key - public static final int DIALOGUE_KEY = 2692; - - @Override - public void handle(final Player player, final Option option){ - configure(); - if(option.getName() == "take"){ - player.faceLocation(SpecialGroundItems.AHAB_BEER.getLocation()); - player.getDialogueInterpreter().open(DIALOGUE_KEY, new NPC(2692), false); - } else { - player.debug("Unhandled option: " + option.getName()); - } - } -} diff --git a/Server/src/main/java/core/game/interaction/inter/AutocastSelectPlugin.java b/Server/src/main/java/core/game/interaction/inter/AutocastSelectPlugin.java deleted file mode 100644 index b3711bf82..000000000 --- a/Server/src/main/java/core/game/interaction/inter/AutocastSelectPlugin.java +++ /dev/null @@ -1,42 +0,0 @@ -package core.game.interaction.inter; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.combat.equipment.WeaponInterface; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the auto casting component plugin. - * @author Emperor - * - */ -@Initializable -public final class AutocastSelectPlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.forId(797).setPlugin(this); - ComponentDefinition.forId(319).setPlugin(this); - ComponentDefinition.forId(310).setPlugin(this); - ComponentDefinition.forId(406).setPlugin(this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - if (!player.getAttribute("autocast_select", false)) { - return true; - } - player.removeAttribute("autocast_select"); - final WeaponInterface w = player.getExtension(WeaponInterface.class); - if (w != null) { - w.selectAutoSpell(button, true); - player.getInterfaceManager().openTab(w); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/inter/ClanInterfacePlugin.java b/Server/src/main/java/core/game/interaction/inter/ClanInterfacePlugin.java deleted file mode 100644 index ceb3e8826..000000000 --- a/Server/src/main/java/core/game/interaction/inter/ClanInterfacePlugin.java +++ /dev/null @@ -1,153 +0,0 @@ -package core.game.interaction.inter; - -import api.ContentAPI; -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; - -/** - * 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: - ContentAPI.sendInputDialogue(player, false, "Enter clan prefix:", (value) -> { - String name = StringUtils.formatDisplayName((String) value); - ContentAPI.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/java/core/game/interaction/inter/CrystalKeyChestPlugin.java b/Server/src/main/java/core/game/interaction/inter/CrystalKeyChestPlugin.java deleted file mode 100644 index 69656ef8b..000000000 --- a/Server/src/main/java/core/game/interaction/inter/CrystalKeyChestPlugin.java +++ /dev/null @@ -1,50 +0,0 @@ -package core.game.interaction.inter; - -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/java/core/game/interaction/inter/DeathInterfacePlugin.java b/Server/src/main/java/core/game/interaction/inter/DeathInterfacePlugin.java deleted file mode 100644 index 44139de63..000000000 --- a/Server/src/main/java/core/game/interaction/inter/DeathInterfacePlugin.java +++ /dev/null @@ -1,32 +0,0 @@ -package core.game.interaction.inter; - -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/java/core/game/interaction/inter/EllisDialogue.java b/Server/src/main/java/core/game/interaction/inter/EllisDialogue.java deleted file mode 100644 index 80e8dd455..000000000 --- a/Server/src/main/java/core/game/interaction/inter/EllisDialogue.java +++ /dev/null @@ -1,136 +0,0 @@ -package core.game.interaction.inter; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.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/java/core/game/interaction/inter/EmoteTabInterface.java b/Server/src/main/java/core/game/interaction/inter/EmoteTabInterface.java deleted file mode 100644 index 63a541aab..000000000 --- a/Server/src/main/java/core/game/interaction/inter/EmoteTabInterface.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.interaction.inter; - -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/java/core/game/interaction/inter/EquipmentInterface.java b/Server/src/main/java/core/game/interaction/inter/EquipmentInterface.java deleted file mode 100644 index 282bf3320..000000000 --- a/Server/src/main/java/core/game/interaction/inter/EquipmentInterface.java +++ /dev/null @@ -1,204 +0,0 @@ -package core.game.interaction.inter; - -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.BitregisterAssembler; -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 rs09.game.content.global.action.EquipHandler; -import rs09.game.interaction.InteractionListeners; -import rs09.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() { - InteractionListeners.run(item.getId(),0,"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().sendAccessMask(211, 0, 2, 6684690, 4); - p.getPacketDispatch().sendAccessMask(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); - BitregisterAssembler assembly = new BitregisterAssembler(new String[] { "Equip" }); - assembly.enableExamineOption(); - assembly.enableSlotSwitch(); - p.getPacketDispatch().sendAccessMask(assembly.calculateRegister(), 0, 670, 0, 27); - p.getInventory().getListeners().add(listener); - p.getInventory().refresh(); - ItemDefinition.statsUpdate(p); - p.getPacketDispatch().sendAccessMask(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(),0,"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/java/core/game/interaction/inter/GameInterface.java b/Server/src/main/java/core/game/interaction/inter/GameInterface.java deleted file mode 100644 index 8056a7524..000000000 --- a/Server/src/main/java/core/game/interaction/inter/GameInterface.java +++ /dev/null @@ -1,237 +0,0 @@ -package core.game.interaction.inter; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -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 rs09.game.world.GameWorld; -import core.game.world.map.RegionManager; -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: - int tut_stage = TutorialSession.getExtension(player).getStage(); - 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 - if (tut_stage == 63) { - player.getConfigManager().set(1021, 0); - TutorialStage.load(player, 64, false); - } - player.getPacketDispatch().sendString("Friends List -" + GameWorld.getSettings().getName() + " " + GameWorld.getSettings().getWorldId(), 550, 3); - break; - case 22://Ignore Tab - if (tut_stage == 64) { - player.getConfigManager().set(1021, 0); - TutorialStage.load(player, 65, false); - } - break; - case 24://Settings Tab - if (tut_stage == 1) { - TutorialStage.load(player, 2, false); - } - break; - case 25://Emotes Tab - if (tut_stage == 23) { - TutorialStage.load(player, 24, false); - } - break; - case 26://Music Tab - if (tut_stage == 21) { - TutorialStage.load(player, 22, false); - } - break; - case 38://Attack Tab - if (tut_stage == 49) { - player.getConfigManager().set(1021, 0); - TutorialStage.load(player, 50, false); - } else { - if (TutorialSession.getExtension(player).getStage() > TutorialSession.MAX_STAGE) { - 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 - if (tut_stage == 10) { - player.getConfigManager().set(1021, 0); - TutorialStage.load(player, 11, false); - } - break; - case 40://Quest Tab - if (tut_stage == 27) { - TutorialStage.load(player, 28, false); - } - /*if (GameWorld.isEconomyWorld()) { - player.getQuestRepository().syncronizeTab(player); - } else { - player.getSavedData().getSpawnData().drawStatsTab(player); - }*/ - - player.getQuestRepository().syncronizeTab(player); - break; - case 41://Inventory Tab - if (tut_stage == 5) { - player.getConfigManager().set(1021, 0); - TutorialStage.load(player, 6, false); - } - player.getInventory().refresh(); - break; - case 42://Worn Equipment Tab - if (tut_stage == 45) { - player.getConfigManager().set(1021, 0); - TutorialStage.load(player, 46, false); - } - break; - case 43://Prayer Tab - if (tut_stage == 61) { - player.getConfigManager().set(1021, 0); - TutorialStage.load(player, 62, false); - } - break; - case 44://Magic Tab - if (tut_stage == 68) { - player.getConfigManager().set(1021, 0); - TutorialStage.load(player, 69, false); - player.getDialogueInterpreter().open(946, RegionManager.getNpc(player, 946)); - } - 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/java/core/game/interaction/inter/GlassInterface.java b/Server/src/main/java/core/game/interaction/inter/GlassInterface.java deleted file mode 100644 index 7d5beec10..000000000 --- a/Server/src/main/java/core/game/interaction/inter/GlassInterface.java +++ /dev/null @@ -1,119 +0,0 @@ -package core.game.interaction.inter; - -import api.ContentAPI; -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.node.entity.skill.Skills; -import core.game.node.entity.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: - ContentAPI.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/java/core/game/interaction/inter/GliderInterface.java b/Server/src/main/java/core/game/interaction/inter/GliderInterface.java deleted file mode 100644 index 16653f84b..000000000 --- a/Server/src/main/java/core/game/interaction/inter/GliderInterface.java +++ /dev/null @@ -1,37 +0,0 @@ -package core.game.interaction.inter; - -import api.ContentAPI; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.content.global.travel.glider.GliderPulse; -import core.game.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; - } - ContentAPI.submitWorldPulse(new GliderPulse(1, player, glider)); - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/inter/GrandExchangeInterface.java b/Server/src/main/java/core/game/interaction/inter/GrandExchangeInterface.java deleted file mode 100644 index 8ffe33bdf..000000000 --- a/Server/src/main/java/core/game/interaction/inter/GrandExchangeInterface.java +++ /dev/null @@ -1,437 +0,0 @@ -package core.game.interaction.inter; - -import api.ContentAPI; -import core.cache.def.impl.CS2Mapping; -import core.cache.def.impl.ItemDefinition; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.ge.GEGuidePrice; -import core.game.ge.GEItemSet; -import core.game.ge.GrandExchangeDatabase; -import core.game.ge.OfferState; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -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; -import core.net.packet.context.ContainerContext; -import core.net.packet.out.ContainerPacket; -import core.plugin.Initializable; -import core.plugin.Plugin; -import kotlin.Unit; -import rs09.game.ge.GrandExchangeOffer; -import rs09.game.ge.OfferManager; -import rs09.game.ge.PlayerGrandExchange; -import rs09.game.interaction.npc.BogrogPouchSwapper; -import rs09.game.world.GameWorld; - -/** - * Handles the Grand Exchange interface options. - * @author Emperor - * @version 1.0 - */ -@Initializable -public class GrandExchangeInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(105, this); // Main interface - ComponentDefinition.put(107, this); // Selling tab - ComponentDefinition.put(109, this); // Collection interface - ComponentDefinition.put(389, this); // Search interface - ComponentDefinition.put(644, this); // Item sets inventory interface - ComponentDefinition.put(645, this); // Item sets interface - ComponentDefinition.put(642, this); // Guide Prices interface. - return this; - } - - /** - * Increments the opened grand exchange offer amount. - * @param player The player. - * @param amount The amount to increment. - */ - @SuppressWarnings("deprecation") - private void setOfferAmount(Player player, GrandExchangeOffer offer, int amount) { - if (offer == null || amount < 0 || offer.getOfferState() != OfferState.PENDING) { - return; - } - offer.setAmount(amount); - player.getConfigManager().send(1110, offer.getAmount()); - } - - /** - * Increments the opened grand exchange offer amount. - * @param player The player. - */ - @SuppressWarnings("deprecation") - private void setOfferValue(Player player, GrandExchangeOffer offer, int value) { - if (offer == null || value < 1 || offer.getOfferState() != OfferState.PENDING) { - return; - } - if (value == OfferManager.getRecommendedPrice(offer.getItemID(), false)) { - player.getAudioManager().send(new Audio(4043, 1, 1)); - } else if (value > offer.getOfferedValue()) { - player.getAudioManager().send(new Audio(4041, 1, 1)); - } else { - player.getAudioManager().send(new Audio(4045, 1, 1)); - } - offer.setOfferedValue(value); - player.varpManager.get(1111).setVarbit(0,offer.getOfferedValue()).send(player); - } - - @Override - public boolean handle(final Player player, final Component component, final int opcode, final int button, final int slot, final int itemId) { - GameWorld.getPulser().submit(new Pulse(1, player) { - @Override - public boolean pulse() { - switch (component.getId()) { - case 644: - case 645: - handleItemSet(player, component, opcode, button, slot, itemId); - return true; - case 389: - handleSearchInterface(player, opcode, button, slot, itemId); - return true; - case 107: - handleSellingTab(player, opcode, button, slot, itemId); - return true; - case 109: - handleCollectionBox(player, opcode, button, slot, itemId); - return true; - case 642: - handleGuidePrice(player, opcode, button, slot, itemId); - return true; - } - handleMainInterface(player, opcode, button, slot, itemId); - return true; - } - }); - return true; - } - - /** - * Handles the search interface options. - * @param player The player. - * @param opcode The packet opcode. - * @param button The button id. - * @param slot The slot. - * @param itemId The item id. - * @return {@code true} if the option got handled. - */ - public boolean handleSearchInterface(final Player player, int opcode, int button, int slot, int itemId) { - switch (button) { - case 10: - player.getInterfaceManager().closeChatbox(); - return true; - } - return false; - } - - /** - * Handles the selling tab interface options. - * @param player The player. - * @param opcode The packet opcode. - * @param button The button id. - * @param slot The slot. - * @param itemId The item id. - * @return {@code true} if the option got handled. - */ - public boolean handleSellingTab(final Player player, int opcode, int button, int slot, int itemId) { - if (button != 18 || slot < 0 || slot > 27) { - return false; - } - Item item = player.getInventory().get(slot); - if (item == null) { - return false; - } - switch (opcode) { - case 196: - player.getPacketDispatch().sendMessage(item.getDefinition().getExamine()); - return true; - case 155: - player.getPlayerGrandExchange().constructSale(item); - return true; - } - return false; - } - - /** - * Handles the selling tab interface options. - * @param player The player. - * @param opcode The packet opcode. - * @param button The button id. - * @param slot The slot. - * @param itemId The item id. - * @return {@code true} if the option got handled. - */ - public boolean handleCollectionBox(final Player player, int opcode, int button, int slot, int itemId) { - int index = -1; - switch (button) { - case 18: - case 23: - case 28: - index = (button - 18) >> 2; - break; - case 36: - case 44: - case 52: - index = 3 + ((button - 36) >> 3); - break; - } - GrandExchangeOffer offer; - if (index > -1 && (offer = player.getPlayerGrandExchange().getOffers()[index]) != null) { - player.getPlayerGrandExchange().withdraw(offer, slot >> 1); - } - return true; - } - - /** - * Handles the item set. - * @param player The player. - * @param opcode The opcode. - * @param button The button. - * @param slot The slot. - * @param itemId The item id. - */ - private void handleItemSet(Player player, Component component, int opcode, int button, int slot, int itemId) { - if (button != 16 && button != 0) { - return; - } - boolean inventory = component.getId() == 644; - 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)) { - player.getPacketDispatch().sendMessage("This isn't a set item."); - return; - } - switch (opcode) { - case 124: - player.getPacketDispatch().sendMessage(item.getDefinition().getExamine()); - break; - case 196: - if (inventory) { - if (player.getInventory().freeSlots() < set.getComponents().length - 1) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space for the component parts."); - return; - } - if (!player.getInventory().remove(item, false)) { - return; - } - for (int id : set.getComponents()) { - player.getInventory().add(new Item(id, 1)); - } - player.getInventory().refresh(); - player.getPacketDispatch().sendMessage("You successfully traded your set for its component items!"); - } else { - if (!player.getInventory().containItems(set.getComponents())) { - player.getPacketDispatch().sendMessage("You don't have the parts that make up this set."); - break; - } - for (int id : set.getComponents()) { - player.getInventory().remove(new Item(id, 1), false); - } - player.getInventory().add(item); - player.getInventory().refresh(); - player.getPacketDispatch().sendMessage("You successfully traded your item components for a set!"); - } - player.getAudioManager().send(new Audio(4044, 1, 1)); - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, player.getAttribute("container-key", 93), player.getInventory(), false)); - break; - case 155: - player.getPacketDispatch().sendMessage((String) CS2Mapping.forId(1089).getMap().get(set.getItemId())); - break; - } - } - - /** - * Handles the main interface options. - * @param player The player. - * @param opcode The packet opcode. - * @param button The button id. - * @param slot The slot. - * @param itemId The item id. - * @return {@code true} if the option got handled. - */ - public boolean handleMainInterface(final Player player, int opcode, int button, int slot, int itemId) { - final GrandExchangeOffer offer = player.getPlayerGrandExchange().getTemporaryOffer(); - final GrandExchangeOffer opened = player.getPlayerGrandExchange().getOpenedOffer(); - int amount = offer == null ? 0 : offer.getAmount(); - switch (button) { - case 209: - case 211: - if (opened == null) { - return false; - } - player.getPlayerGrandExchange().withdraw(opened, (button - 209) >> 1); - return true; - case 190: - player.getPlayerGrandExchange().confirmOffer(); - player.getPlayerGrandExchange().setTemporaryOffer(null); - return true; - case 194: - player.getPlayerGrandExchange().openSearch(); - return true; - case 203: - if (opened == null) { - return false; - } - player.getPlayerGrandExchange().abort(opened.getIndex()); - return true; - case 18: - case 34: - case 50: - case 69: - case 88: - case 107: - if (opcode == 205) { - player.getPlayerGrandExchange().abort((button - 18) >> 4); - return true; - } - player.getPlayerGrandExchange().view((button - 18) >> 4); - return true; - case 30: - case 46: - case 62: - case 81: - case 100: - case 119: - player.getPlayerGrandExchange().openBuy((button - 30) >> 4); - return true; - case 31: - case 47: - case 63: - case 82: - case 101: - case 120: - player.getPlayerGrandExchange().openSell((button - 31) >> 4); - return true; - case 157: // -1 - setOfferAmount(player, offer, amount - 1); - return true; - case 159: // +1 - setOfferAmount(player, offer, amount + 1); - return true; - case 162: // 1 - setOfferAmount(player, offer, offer != null && offer.getSell() ? 1 : amount + 1); - return true; - case 164: // 10 - setOfferAmount(player, offer, offer != null && offer.getSell() ? 10 : amount + 10); - return true; - case 166: // 100 - setOfferAmount(player, offer, offer != null && offer.getSell() ? 100 : amount + 100); - return true; - case 168: // 1000 / sell all - if (offer != null && offer.getSell()) { - setOfferAmount(player, offer, PlayerGrandExchange.getInventoryAmount(player, offer.getItemID())); - return true; - } - setOfferAmount(player, offer, amount + 1000); - return true; - case 170: // value x - ContentAPI.sendInputDialogue(player, false, "Enter the amount:", (value) -> { - if (player.getInterfaceManager().getChatbox().getId() == 389) { - player.getPlayerGrandExchange().openSearch(); - } - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - s = s.replace("m","000000"); - s = s.replace("M","000000"); - setOfferAmount(player, offer, Integer.parseInt(s)); - return Unit.INSTANCE; - }); - return false; - case 180: - if (offer != null) { - setOfferValue(player, offer, OfferManager.getRecommendedPrice(offer.getItemID(), false)); - return true; - } - return false; - case 177: // mid - 5% value - case 183: // mid + 5% value - if (offer != null) { - setOfferValue(player, offer, (int) (OfferManager.getRecommendedPrice(offer.getItemID(), false) * (button == 177 ? 0.95 : 1.05))); - return true; - } - return false; - case 171: // Decrease value by 1 - case 173: // Increase value with 1 - if (offer != null) { - setOfferValue(player, offer, offer.getOfferedValue() + (button == 171 ? -1 : 1)); - return true; - } - return false; - case 185: // Set value x - if (offer == null) { - player.getPacketDispatch().sendMessage("Please select an offer first."); - return true; - } - ContentAPI.sendInputDialogue(player, false, "Enter the amount:", (value) -> { - if (player.getInterfaceManager().getChatbox().getId() == 389) { - player.getPlayerGrandExchange().openSearch(); - } - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - s = s.replace("m","000000"); - s = s.replace("M","000000"); - setOfferValue(player, offer, Integer.parseInt(s)); - return Unit.INSTANCE; - }); - return false; - case 195: - player.getInterfaceManager().close(); - return true; - case 127: - player.getPlayerGrandExchange().setTemporaryOffer(null); - player.getInterfaceManager().closeSingleTab(); - player.getInterfaceManager().closeChatbox(); - return true; - } - return false; - } - - /*** - * Method used to handle the guide price opcode. - * @param player the player. - * @param opcode the opcode. - * @param buttonId the buttonId. - * @param slot the slot. - * @param itemId the itemId. - */ - private void handleGuidePrice(final Player player, final int opcode, final int buttonId, final int slot, final int itemId) { - switch (opcode) { - case 155: - GEGuidePrice.GuideType type = player.getAttribute("guide-price", null); - if (type == null) { - return; - } - int subtract = 0; - if (buttonId >= 15 && buttonId <= 23) { - subtract = 15; - } - if (buttonId >= 43 && buttonId <= 57) { - subtract = 43; - } - if (buttonId >= 89 && buttonId <= 103) { - subtract = 89; - } - if (buttonId >= 135 && buttonId <= 144) { - subtract = 135; - } - if (buttonId >= 167 && buttonId <= 182) { - subtract = 167; - } - player.getPacketDispatch().sendMessage(ItemDefinition.forId(type.getItems()[buttonId - subtract].getItem()).getExamine()); - break; - } - } -} diff --git a/Server/src/main/java/core/game/interaction/inter/GravePurchaseInterface.java b/Server/src/main/java/core/game/interaction/inter/GravePurchaseInterface.java deleted file mode 100644 index 7e9b88ed0..000000000 --- a/Server/src/main/java/core/game/interaction/inter/GravePurchaseInterface.java +++ /dev/null @@ -1,44 +0,0 @@ -package core.game.interaction.inter; - -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.grave.GraveType; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the component plugin used for the grave purchasing interface. - * @author Vexia - */ -@Initializable -public final class GravePurchaseInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(652, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - if (slot == -1) { - return true; - } - final GraveType grave = GraveType.values()[slot]; - int cost = grave.getCost(); - if (!player.getInventory().containsItem(new Item(995, cost))) { - return true; - } - if (!player.getInventory().remove(new Item(995, cost)) && grave != GraveType.MEMORIAL_PLAQUE) { - player.getPacketDispatch().sendMessage("You don't have enough coins to buy this grave stone."); - return true; - } - player.getGraveManager().setType(grave); - player.getDialogueInterpreter().sendDialogue("Your gravestone has been changed as you requested."); - player.getInterfaceManager().close(); - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/inter/LeatherCraftInterface.java b/Server/src/main/java/core/game/interaction/inter/LeatherCraftInterface.java deleted file mode 100644 index b6f78bc15..000000000 --- a/Server/src/main/java/core/game/interaction/inter/LeatherCraftInterface.java +++ /dev/null @@ -1,57 +0,0 @@ -package core.game.interaction.inter; - -import api.ContentAPI; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.skill.crafting.armour.LeatherCrafting; -import core.game.node.entity.skill.crafting.armour.SoftCraftPulse; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import kotlin.Unit; - -/** - * Represents the leather crafting interface. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LeatherCraftInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(154, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - int amount = 0; - final LeatherCrafting.SoftLeather soft = LeatherCrafting.SoftLeather.forButton(button); - if (soft == null) { - return true; - } - switch (opcode) { - case 155: - amount = 1; - break; - case 196: - amount = 5; - break; - case 124: - amount = player.getInventory().getAmount(new Item(LeatherCrafting.LEATHER)); - break; - case 199: - ContentAPI.sendInputDialogue(player, true, "Enter the amount:", (value) -> { - ContentAPI.submitIndividualPulse(player, new SoftCraftPulse(player, new Item(LeatherCrafting.LEATHER), soft, (int) value)); - return Unit.INSTANCE; - }); - return true; - } - player.getPulseManager().run(new SoftCraftPulse(player, null, soft, amount)); - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/inter/LoginInterfacePlugin.java b/Server/src/main/java/core/game/interaction/inter/LoginInterfacePlugin.java deleted file mode 100644 index 9149bb56c..000000000 --- a/Server/src/main/java/core/game/interaction/inter/LoginInterfacePlugin.java +++ /dev/null @@ -1,51 +0,0 @@ -package core.game.interaction.inter; - -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/java/core/game/interaction/inter/LogoutInterface.java b/Server/src/main/java/core/game/interaction/inter/LogoutInterface.java deleted file mode 100644 index 9a37ab1e7..000000000 --- a/Server/src/main/java/core/game/interaction/inter/LogoutInterface.java +++ /dev/null @@ -1,40 +0,0 @@ -package core.game.interaction.inter; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.player.Player; -import rs09.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/java/core/game/interaction/inter/MagicBookInterface.java b/Server/src/main/java/core/game/interaction/inter/MagicBookInterface.java deleted file mode 100644 index e032e92ff..000000000 --- a/Server/src/main/java/core/game/interaction/inter/MagicBookInterface.java +++ /dev/null @@ -1,40 +0,0 @@ -package core.game.interaction.inter; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import rs09.game.node.entity.skill.magic.SpellListener; -import rs09.game.node.entity.skill.magic.SpellListeners; -import rs09.game.node.entity.skill.magic.SpellUtils; -import rs09.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; - } - SpellListeners.run(button, SpellListener.NONE, SpellUtils.getBookFromInterface(component.getId()),player,null); - return MagicSpell.castSpell(player, component.getId() == 192 ? SpellBook.MODERN : component.getId() == 193 ? SpellBook.ANCIENT : SpellBook.LUNAR, button, player); - } -} diff --git a/Server/src/main/java/core/game/interaction/inter/MusicTabInterface.java b/Server/src/main/java/core/game/interaction/inter/MusicTabInterface.java deleted file mode 100644 index 112092586..000000000 --- a/Server/src/main/java/core/game/interaction/inter/MusicTabInterface.java +++ /dev/null @@ -1,57 +0,0 @@ -package core.game.interaction.inter; - -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/java/core/game/interaction/inter/MysticStaffEnchantingPlugin.java b/Server/src/main/java/core/game/interaction/inter/MysticStaffEnchantingPlugin.java deleted file mode 100644 index 011a360b2..000000000 --- a/Server/src/main/java/core/game/interaction/inter/MysticStaffEnchantingPlugin.java +++ /dev/null @@ -1,95 +0,0 @@ -package core.game.interaction.inter; - -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/java/core/game/interaction/inter/OrbViewingInterface.java b/Server/src/main/java/core/game/interaction/inter/OrbViewingInterface.java deleted file mode 100644 index 6c2e78715..000000000 --- a/Server/src/main/java/core/game/interaction/inter/OrbViewingInterface.java +++ /dev/null @@ -1,181 +0,0 @@ -package core.game.interaction.inter; - -import rs09.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 rs09.plugin.PluginManager; - -/** - * 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); - PluginManager.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/java/core/game/interaction/inter/PlayerExamineInterfacePlugin.java b/Server/src/main/java/core/game/interaction/inter/PlayerExamineInterfacePlugin.java deleted file mode 100644 index 0009ec3c6..000000000 --- a/Server/src/main/java/core/game/interaction/inter/PlayerExamineInterfacePlugin.java +++ /dev/null @@ -1,73 +0,0 @@ -package core.game.interaction.inter; - -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.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.StringUtils; - -/** - * Package -> core.game.interaction.inter - * Created on -> 9/6/2016 @8:12 PM for 530 - * - * @author Ethan Kyle Millard - */ -@Initializable -public class PlayerExamineInterfacePlugin extends ComponentPlugin { - - private Player node; - - public PlayerExamineInterfacePlugin() { - - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(31, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (button) { - case 2: - component.close(player); - break; - } - return true; - } - - public void prepareInterface(Player player, Player examined) { - Component component = new Component(31); - player.getPacketDispatch().sendInterfaceConfig(component.getId(), 7, true); - player.getPacketDispatch().sendInterfaceConfig(component.getId(), 8, true); - player.getPacketDispatch().sendInterfaceConfig(component.getId(), 15, true); - player.getPacketDispatch().sendInterfaceConfig(component.getId(), 18, true); - player.getPacketDispatch().sendInterfaceConfig(component.getId(), 2, true); - player.getPacketDispatch().sendInterfaceConfig(component.getId(), 14, true); - player.getPacketDispatch().sendInterfaceConfig(component.getId(), 13, true); - player.getInterfaceManager().open(component); - player.getPacketDispatch().sendString("" + examined.getSavedData().getSpawnData().getTitle().getName() + " " + StringUtils.formatDisplayName(examined.getName()) + " - Combat Level: " + player.getProperties().getCurrentCombatLevel(), 31, 3); - player.getPacketDispatch().sendString("Total Level -> ", 31, 9); - player.getPacketDispatch().sendString("Inventory items -> ", 31, 10); - player.getPacketDispatch().sendString("Inventory value -> ", 31, 16); - player.getPacketDispatch().sendString("Custom state-> ", 31, 19); - player.getPacketDispatch().sendString("" + examined.getSkills().getTotalLevel(), 31, 11); - player.getPacketDispatch().sendString("" + examined.getInventory().itemCount(), 31, 12); - player.getPacketDispatch().sendString("" + examined.getInventory().getWealth(), 31, 17); - player.getPacketDispatch().sendString("" + examined.getCustomState(), 31, 20); - player.getPacketDispatch().sendString("Close", 31, 5); - - for (int i = 0; i < 11; i++) { - Item item = examined.getEquipment().get(i); - if (item == null) { - continue; - } - System.out.println(item.getName()); - } - - } -} diff --git a/Server/src/main/java/core/game/interaction/inter/PrayerTabInterface.java b/Server/src/main/java/core/game/interaction/inter/PrayerTabInterface.java deleted file mode 100644 index 22c16313a..000000000 --- a/Server/src/main/java/core/game/interaction/inter/PrayerTabInterface.java +++ /dev/null @@ -1,33 +0,0 @@ -package core.game.interaction.inter; - -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/java/core/game/interaction/inter/QuestTabInterface.java b/Server/src/main/java/core/game/interaction/inter/QuestTabInterface.java deleted file mode 100644 index 37ce6302a..000000000 --- a/Server/src/main/java/core/game/interaction/inter/QuestTabInterface.java +++ /dev/null @@ -1,75 +0,0 @@ -package core.game.interaction.inter; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -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) { - if (TutorialSession.getExtension(p).getStage() < TutorialSession.MAX_STAGE) { - return true; - } - 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/java/core/game/interaction/inter/SettingTabInterface.java b/Server/src/main/java/core/game/interaction/inter/SettingTabInterface.java deleted file mode 100644 index 4e4d76979..000000000 --- a/Server/src/main/java/core/game/interaction/inter/SettingTabInterface.java +++ /dev/null @@ -1,95 +0,0 @@ -package core.game.interaction.inter; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.plugin.Initializable; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -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 - if (TutorialSession.getExtension(p).getStage() == 25) { - TutorialStage.load(p, 26, false); - } - p.getSettings().toggleRun(); - break; - case 8:// house - if (!TutorialSession.getExtension(p).finished()) { - return true; - } - 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/java/core/game/interaction/inter/SkillInterface.java b/Server/src/main/java/core/game/interaction/inter/SkillInterface.java deleted file mode 100644 index adecfbc25..000000000 --- a/Server/src/main/java/core/game/interaction/inter/SkillInterface.java +++ /dev/null @@ -1,31 +0,0 @@ -package core.game.interaction.inter; - -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/java/core/game/interaction/inter/SkillTabInterface.java b/Server/src/main/java/core/game/interaction/inter/SkillTabInterface.java deleted file mode 100644 index cead79835..000000000 --- a/Server/src/main/java/core/game/interaction/inter/SkillTabInterface.java +++ /dev/null @@ -1,162 +0,0 @@ -package core.game.interaction.inter; - -import api.ContentAPI; -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.node.entity.player.link.RunScript; -import rs09.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/java/core/game/interaction/inter/SpinningInterface.java b/Server/src/main/java/core/game/interaction/inter/SpinningInterface.java deleted file mode 100644 index a8a58c472..000000000 --- a/Server/src/main/java/core/game/interaction/inter/SpinningInterface.java +++ /dev/null @@ -1,60 +0,0 @@ -package core.game.interaction.inter; - -import api.ContentAPI; -import core.cache.def.impl.ItemDefinition; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.skill.crafting.spinning.SpinningItem; -import core.game.node.entity.skill.crafting.spinning.SpinningPulse; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import kotlin.Unit; - -@Initializable -public class SpinningInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(459, this); - return this; - } - - @Override - public boolean handle(final Player p, Component component, int opcode, int button, int slot, int itemId) { - final SpinningItem spin = SpinningItem.forId(button); - if (spin == null) { - return true; - } - if (!p.getInventory().contains(spin.getNeed(), 1)) { - p.getPacketDispatch().sendMessage("You need "+ ItemDefinition.forId(spin.getNeed()).getName().toLowerCase() + " to make this."); - return true; - } - int amt = -1; - switch (opcode) { - case 155: - amt = 1; - break; - case 196: - amt = 5; - break; - case 124: - amt = p.getInventory().getAmount(new Item(spin.getNeed())); - break; - case 199: - ContentAPI.sendInputDialogue(p, true, "Enter the amount:", (value) -> { - ContentAPI.submitIndividualPulse(p, new SpinningPulse(p, new Item(spin.getNeed(), 1), (int) value, spin)); - return Unit.INSTANCE; - }); - break; - } - if (opcode == 199) { - return true; - } - p.getPulseManager().run(new SpinningPulse(p, new Item(spin.getNeed(), 1), amt, spin)); - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/inter/TanningInterface.java b/Server/src/main/java/core/game/interaction/inter/TanningInterface.java deleted file mode 100644 index fcb912e96..000000000 --- a/Server/src/main/java/core/game/interaction/inter/TanningInterface.java +++ /dev/null @@ -1,91 +0,0 @@ -package core.game.interaction.inter; - -import api.ContentAPI; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.skill.crafting.TanningProduct; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -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: - ContentAPI.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/java/core/game/interaction/inter/WorldMapInterface.java b/Server/src/main/java/core/game/interaction/inter/WorldMapInterface.java deleted file mode 100644 index eed52ad10..000000000 --- a/Server/src/main/java/core/game/interaction/inter/WorldMapInterface.java +++ /dev/null @@ -1,39 +0,0 @@ -package core.game.interaction.inter; - -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("World map: buttonid: " + button + ", opcode: " + opcode + ", slot: " + slot); - return true; - } - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/BookreadOption.java b/Server/src/main/java/core/game/interaction/item/BookreadOption.java deleted file mode 100644 index b0fc69cfb..000000000 --- a/Server/src/main/java/core/game/interaction/item/BookreadOption.java +++ /dev/null @@ -1,66 +0,0 @@ -package core.game.interaction.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 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(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 9004: - return 423943;*/ - case 11710: - return 2739823; - case 292: - return 183764; - case 1856: - return 387454; - } - return -1; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/CasketPlugin.java b/Server/src/main/java/core/game/interaction/item/CasketPlugin.java deleted file mode 100644 index 2c8f9cbf8..000000000 --- a/Server/src/main/java/core/game/interaction/item/CasketPlugin.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.interaction.item; - -import java.util.ArrayList; -import java.util.List; - -import api.ContentAPI; -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -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 rs09.game.content.global.WeightBasedTable; -import rs09.game.content.global.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().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() + "."); - ContentAPI.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/java/core/game/interaction/item/DiangoItemPlugin.java b/Server/src/main/java/core/game/interaction/item/DiangoItemPlugin.java deleted file mode 100644 index 5a570c886..000000000 --- a/Server/src/main/java/core/game/interaction/item/DiangoItemPlugin.java +++ /dev/null @@ -1,69 +0,0 @@ -package core.game.interaction.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/java/core/game/interaction/item/DragonfireShieldPlugin.java b/Server/src/main/java/core/game/interaction/item/DragonfireShieldPlugin.java deleted file mode 100644 index 35dbe93c1..000000000 --- a/Server/src/main/java/core/game/interaction/item/DragonfireShieldPlugin.java +++ /dev/null @@ -1,138 +0,0 @@ -package core.game.interaction.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 core.game.node.entity.combat.handlers.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 rs09.game.node.entity.combat.CombatPulse; -import rs09.game.world.GameWorld; -import rs09.plugin.PluginManager; - -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); - PluginManager.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/java/core/game/interaction/item/DustHandPlugin.java b/Server/src/main/java/core/game/interaction/item/DustHandPlugin.java deleted file mode 100644 index 4c414cb61..000000000 --- a/Server/src/main/java/core/game/interaction/item/DustHandPlugin.java +++ /dev/null @@ -1,42 +0,0 @@ -package core.game.interaction.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/java/core/game/interaction/item/EmptyOptionPlugin.java b/Server/src/main/java/core/game/interaction/item/EmptyOptionPlugin.java deleted file mode 100644 index 5093dfd90..000000000 --- a/Server/src/main/java/core/game/interaction/item/EmptyOptionPlugin.java +++ /dev/null @@ -1,91 +0,0 @@ -package core.game.interaction.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); - return this; - } - public enum EmptyItem{ - POT_OF_FLOUR(1933, 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_SAND(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); - - 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/java/core/game/interaction/item/EnchantedGemPlugin.java b/Server/src/main/java/core/game/interaction/item/EnchantedGemPlugin.java deleted file mode 100644 index 8f24cc0b5..000000000 --- a/Server/src/main/java/core/game/interaction/item/EnchantedGemPlugin.java +++ /dev/null @@ -1,118 +0,0 @@ -package core.game.interaction.item; - -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.NPCDefinition; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.EnchantedJewellery; -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 handle enchanted jewellery transportation. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class EnchantedGemPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(4155).getHandlers().put("option:check", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.sendMessage("You're assigned to kill " + NPCDefinition.forId((player.getSlayer().getTask().getNpcs()[0])).getName().toLowerCase() + "s; only " + player.getSlayer().getAmount() + " more to go."); - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - /** - * Represents the jewellery dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ - public final class JewelleryDialogue extends DialoguePlugin { - - /** - * Represents the id to use. - */ - public static final int ID = 329128389; - - /** - * Represents the enchanted jewellery. - */ - private EnchantedJewellery jewellery; - - /** - * If the operate option is used. - */ - private boolean operate; - - /** - * Represents the item instance. - */ - private Item item; - - /** - * Constructs a new {@code EnchantedJewelleryPlugin} {@code Object}. - */ - public JewelleryDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code EnchantedJewelleryPlugin} {@code Object}. - * @param player the player. - */ - public JewelleryDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new JewelleryDialogue(player); - } - - @Override - public boolean open(Object... args) { - item = (Item) args[0]; - jewellery = (EnchantedJewellery) args[1]; - operate = args.length > 2 && (Boolean) args[2]; - if (jewellery == EnchantedJewellery.DIGSITE_PENDANT) { - jewellery.use(player, item, 0, operate); - return true; - } - interpreter.sendOptions("Where would you like to go?", jewellery.getOptions()); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (player.getInterfaceManager().isOpened()) { - end(); - return true; - } - end(); - jewellery.use(player, item, (buttonId - 1), operate); - return true; - } - - @Override - public int[] getIds() { - return new int[] { ID }; - } - - } -} diff --git a/Server/src/main/java/core/game/interaction/item/ExplorersRingPlugin.kt b/Server/src/main/java/core/game/interaction/item/ExplorersRingPlugin.kt deleted file mode 100644 index 46ab2b00e..000000000 --- a/Server/src/main/java/core/game/interaction/item/ExplorersRingPlugin.kt +++ /dev/null @@ -1,99 +0,0 @@ -package core.game.interaction.item - -import api.ContentAPI -import core.Util -import core.cache.def.impl.ItemDefinition -import core.plugin.Initializable -import core.game.interaction.OptionHandler -import core.plugin.Plugin -import core.game.node.entity.skill.Skills -import core.game.interaction.item.ExplorersRingPlugin -import core.game.node.Node -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.Animation -import core.game.world.update.flag.context.Graphics -import org.json.simple.JSONObject -import org.rs09.consts.Items -import rs09.ServerStore -import rs09.ServerStore.getBoolean -import rs09.ServerStore.getInt -import rs09.game.interaction.InteractionListener - -/** - * 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) - val CABBAGE_PORT = Location.create(3051, 3291, 0) - - - override fun defineListeners() { - on(RINGS, ITEM, "run-replenish"){player, node -> - val charges = getStoreFile().getInt(player.username.toLowerCase() + ":run") - if (charges >= getRingLevel(node.id)) { - ContentAPI.sendMessage(player,"You have used all the charges you can for one day.") - return@on true - } - player.settings.updateRunEnergy(-50.0) - - getStoreFile()[player.username.toLowerCase() + ":run"] = charges + 1 - - ContentAPI.sendMessage(player,"You feel refreshed as the ring revitalises you and a charge is used up.") - ContentAPI.visualize(player, 9988, 1733) - return@on true - } - - on(RINGS, ITEM, "low-alchemy"){player, _ -> - if (!ContentAPI.hasLevelStat(player, Skills.MAGIC, 21)) { - ContentAPI.sendMessage(player,"You need a Magic level of 21 in order to do that.") - return@on true - } - if(getStoreFile().getBoolean(player.username.toLowerCase() + ":alchs")){ - ContentAPI.sendMessage(player, "You have claimed all the charges you can for one day.") - return@on true - } - ContentAPI.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 - - return@on true - } - - on(RINGS, ITEM, "cabbage-port"){player, node -> - teleport(player) - return@on true - } - - on(RINGS, ITEM, "operate", "rub"){player, node -> - if(getRingLevel(node.id) < 3){ - ContentAPI.sendMessage(player, "This item can not be operated.") - return@on true - } - - teleport(player) - return@on true - } - } - - fun teleport(player: Player){ - ContentAPI.teleport(player, CABBAGE_PORT, TeleportType.CABBAGE) - } - - fun getRingLevel(id: Int): Int{ - return when(id){ - Items.EXPLORERS_RING_1_13560 -> 1 - Items.EXPLORERS_RING_2_13561 -> 2 - Items.EXPLORERS_RING_3_13562 -> 3 - else -> -1 - } - } - - fun getStoreFile(): JSONObject{ - return ServerStore.getArchive("daily-explorer-ring") - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/item/GodBookPlugin.java b/Server/src/main/java/core/game/interaction/item/GodBookPlugin.java deleted file mode 100644 index 8f383e3e8..000000000 --- a/Server/src/main/java/core/game/interaction/item/GodBookPlugin.java +++ /dev/null @@ -1,200 +0,0 @@ -package core.game.interaction.item; - -import core.game.content.dialogue.DialogueAction; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.GodBook; -import core.game.node.entity.skill.Skills; -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.item.GroundItem; -import core.game.node.item.Item; -import core.game.node.item.ItemPlugin; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; -import core.plugin.Initializable; -import rs09.plugin.PluginManager; - -/** - * Handles the god books. - * @author Vexia - */ -@Initializable -public class GodBookPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (GodBook book : GodBook.values()) { - book.getDamagedBook().getDefinition().getHandlers().put("option:check", this); - } - PluginManager.definePlugins(new PageHandler(), new GodBookItem(), new SymbolBlessHandler()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - GodBook book = GodBook.forItem(node.asItem(), option.equalsIgnoreCase("check")); - if (book != null) { - switch (option) { - case "check": - String[] messages = new String[4]; - for (int i = 0; i < messages.length; i++) { - messages[i] = book.hasPage(player, node.asItem(), i + 1) ? "The " + getNumberName(i + 1) + " page is in the book." : "The " + getNumberName(i + 1) + " page is missing."; - } - player.getDialogueInterpreter().sendDialogue(messages); - return true; - case "preach": - player.getDialogueInterpreter().open("god-book", book); - return true; - } - } - 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 - */ - public class GodBookItem extends ItemPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (GodBook book : GodBook.values()) { - register(book.getDamagedBook().getId()); - } - return this; - } - - @Override - public boolean canPickUp(Player player, GroundItem item, int type) { - if (player.hasItem(item.asItem())) { - player.sendMessage("You do not need more than one incomplete book."); - return false; - } - return true; - } - - } - - /** - * The page handler. - * @author Vexia - */ - public class PageHandler extends UseWithHandler { - - /** - * Constructs a new {@code PageHandler} {@code Object} - */ - public PageHandler() { - super(3839, 3841, 3843); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (GodBook book : GodBook.values()) { - for (Item i : book.getPages()) { - addHandler(i.getId(), ITEM_TYPE, this); - } - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - GodBook book = GodBook.forItem(event.getUsedItem(), true); - Player player = event.getPlayer(); - if (book != null && book.isPage(event.getUsedWith().asItem())) { - book.insertPage(player, event.getUsedItem(), event.getUsedWith().asItem()); - return true; - } - return false; - } - - } - - /** - * Gets the number name. - * @param i the integer to check. - * @return the number name. - */ - private String getNumberName(int i) { - return i == 1 ? "first" : i == 2 ? "second" : i == 3 ? "third" : "fourth"; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/HouseTeleTabOptionPlugin.java b/Server/src/main/java/core/game/interaction/item/HouseTeleTabOptionPlugin.java deleted file mode 100644 index b67a361e3..000000000 --- a/Server/src/main/java/core/game/interaction/item/HouseTeleTabOptionPlugin.java +++ /dev/null @@ -1,41 +0,0 @@ -package core.game.interaction.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/java/core/game/interaction/item/KeldagrimVotingBond.java b/Server/src/main/java/core/game/interaction/item/KeldagrimVotingBond.java deleted file mode 100644 index cc36c8fa1..000000000 --- a/Server/src/main/java/core/game/interaction/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.content.dialogue.DialogueInterpreter; -//import core.game.content.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 rs09.game.world.repository.Repository; -//import core.plugin.Plugin; -//import rs09.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 (player.getTreasureTrailManager().hasClue()) { -// interpreter.sendItemMessage(14807, "Sorry, you already have a clue scroll."); -// break; -// } -// if (player.getTreasureTrailManager().hasTrail()) { -// player.getTreasureTrailManager().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/java/core/game/interaction/item/LampPlugin.java b/Server/src/main/java/core/game/interaction/item/LampPlugin.java deleted file mode 100644 index 81a626397..000000000 --- a/Server/src/main/java/core/game/interaction/item/LampPlugin.java +++ /dev/null @@ -1,62 +0,0 @@ -package core.game.interaction.item; - -import core.game.component.Component; -import core.game.content.global.Lamps; -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.game.node.entity.skill.Skills; - -/** - * Represents the plugin used for an experience lamp. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LampPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (Lamps lamp : Lamps.values()) { - lamp.getItem().getDefinition().getHandlers().put("option:rub", this); - } - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.setAttribute("lamp", node); - player.setAttribute("caller",this); - player.getInterfaceManager().open(new Component(134).setCloseEvent((player1, c) -> { - player.getInterfaceManager().openDefaultTabs(); - player.removeAttribute("lamp"); - player.unlock(); - return true; - })); - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13); - return true; - } - @Override - 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."); - return; - } - if(lamp == Lamps.GENIE_LAMP){ - player.getSkills().addExperience(skill,player.getSkills().getLevel(skill)*10); - } else { - player.getSkills().addExperience(skill,lamp.getExp()); - } - player.getInventory().remove((Item) player.getAttribute("lamp")); - } - - @Override - public boolean isWalk() { - return false; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/MorphItemPlugin.java b/Server/src/main/java/core/game/interaction/item/MorphItemPlugin.java deleted file mode 100644 index eaa230697..000000000 --- a/Server/src/main/java/core/game/interaction/item/MorphItemPlugin.java +++ /dev/null @@ -1,109 +0,0 @@ -package core.game.interaction.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 rs09.game.world.GameWorld; -import core.plugin.Initializable; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; -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); - PluginManager.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().hideTabs(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/java/core/game/interaction/item/NewsPaperPlugin.java b/Server/src/main/java/core/game/interaction/item/NewsPaperPlugin.java deleted file mode 100644 index 1f4538d1d..000000000 --- a/Server/src/main/java/core/game/interaction/item/NewsPaperPlugin.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.interaction.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/java/core/game/interaction/item/PharoahSceptre.java b/Server/src/main/java/core/game/interaction/item/PharoahSceptre.java deleted file mode 100644 index 00929d04f..000000000 --- a/Server/src/main/java/core/game/interaction/item/PharoahSceptre.java +++ /dev/null @@ -1,138 +0,0 @@ -package core.game.interaction.item; - -import core.cache.def.impl.ItemDefinition; -import core.plugin.Initializable; -import core.game.content.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.system.task.Pulse; -import rs09.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.Plugin; - - -/** - * Adds functionality to the pharoah's scepter - * @author ceik - */ -@Initializable -public final class PharoahSceptre extends OptionHandler { - private static final Graphics GRAPHICS = new Graphics(308, 100, 50); - private static final Animation ANIMATION = new Animation(714); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - new SceptreDialog().init(); - ItemDefinition.forId(9044).getHandlers().put("option:teleport",this); - ItemDefinition.forId(9046).getHandlers().put("option:teleport",this); - ItemDefinition.forId(9048).getHandlers().put("option:teleport",this); - ItemDefinition.forId(9050).getHandlers().put("option:teleport",this); - return this; - } - @Override - public boolean handle(Player player, Node node, String string){ - Item item = node.asItem(); - if(string.equals("teleport")){ - switch(item.getId()) { - case 9044: - case 9046: - case 9048: - player.getDialogueInterpreter().open(325128389); - return true; - case 9050: - player.getPacketDispatch().sendMessage("You have used up all the charges on this sceptre."); - return true; - } - } - return true; - } - - public final class SceptreDialog extends DialoguePlugin { - public SceptreDialog() { - /** - * empty - */ - } - public SceptreDialog(Player player){super(player);} - - @Override - public DialoguePlugin newInstance(Player player){ return new SceptreDialog(player);} - - @Override - public boolean open(Object... args){ - //Item item = (Item) args[0]; - player.getPacketDispatch().sendMessage("" + args[0]); - interpreter.sendOptions("Teleport to:","Jalsavrah","Jaleustrophos","Jaldraocht","Nowhere"); - return true; - } - public void teleport(Location location, Player player){ - player.lock(); - player.visualize(ANIMATION, GRAPHICS); - 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; - } - }); - } - @Override - public boolean handle(int interfaceId, int buttonId){ - if (player.isTeleBlocked()) { - player.sendMessage("A magical force has stopped you from teleporting."); - return false; - } - switch(buttonId){ - case 1: - teleport(Location.create(3289,2786,0), player); - stage = 10; - break; - case 2: - teleport(Location.create(3342,2827,0),player); - stage = 10; - break; - case 3: - teleport(Location.create(3233,2902,0),player); - stage = 10; - break; - case 4: - stage = 20; - break; - } - switch(stage){ - case 10: - if(player.getInventory().containsItem(new Item(9044))){ - player.getInventory().remove(new Item(9044)); - player.getInventory().add(new Item(9046)); - player.getPacketDispatch().sendMessage("Your Pharoah's Sceptre has 2 charges remaining."); - end(); - } else if (player.getInventory().containsItem(new Item(9046))){ - player.getInventory().remove(new Item(9046)); - player.getInventory().add(new Item(9048)); - player.getPacketDispatch().sendMessage("Your Pharoah's Sceptre has 1 charge remaining."); - end(); - } else if (player.getInventory().containsItem(new Item(9048))){ - player.getInventory().remove(new Item(9048)); - player.getInventory().add(new Item(9050)); - player.sendMessage("Your Pharoah's Sceptre has used its last charge."); - end(); - } - stage = 20; - break; - case 20: - end(); - break; - } - return true; - } - public int[] getIds() { return new int[] {325128389};} - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java b/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java deleted file mode 100644 index 632db87a3..000000000 --- a/Server/src/main/java/core/game/interaction/item/SilverSicklePlugin.java +++ /dev/null @@ -1,42 +0,0 @@ -package core.game.interaction.item; - -import api.ContentAPI; -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.plugin.Initializable; -import core.plugin.Plugin; -import rs09.game.content.quest.members.naturespirit.NSUtils; - -/** - * Handles the Silver Sickle (b) to collect Mort Myre Fungus. - * @author Splinter - */ -@Initializable -public final class SilverSicklePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(2963).getHandlers().put("option:operate", this); - ItemDefinition.forId(2963).getHandlers().put("option:cast bloom", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "operate": - case "cast bloom": - if(player.getQuestRepository().getQuest("Nature Spirit").getStage(player) >= 75) { - player.getPacketDispatch().sendAnimation(9021); - NSUtils.castBloom(player); - } else { - ContentAPI.sendDialogue(player, "You must complete Nature Spirit to use this."); - } - return true; - } - return false; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/TeleTabsOptionPlugin.java b/Server/src/main/java/core/game/interaction/item/TeleTabsOptionPlugin.java deleted file mode 100644 index 92b3b450e..000000000 --- a/Server/src/main/java/core/game/interaction/item/TeleTabsOptionPlugin.java +++ /dev/null @@ -1,160 +0,0 @@ -package core.game.interaction.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/java/core/game/interaction/item/ToyHorsePlugin.java b/Server/src/main/java/core/game/interaction/item/ToyHorsePlugin.java deleted file mode 100644 index d32d7c8bf..000000000 --- a/Server/src/main/java/core/game/interaction/item/ToyHorsePlugin.java +++ /dev/null @@ -1,48 +0,0 @@ -package core.game.interaction.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.Plugin; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Handles the option for the toy horse. - * @author Vexia - */ -@Initializable -public class ToyHorsePlugin extends OptionHandler { - - /** - * The force chat's you can say. - */ - private static final String CHATS[] = { "Come-on Dobbin, we can win the race!", "Hi-ho Silver, and away!", "Neaahhhyyy!" }; - - @Override - public boolean handle(Player player, Node node, String option) { - player.lock(2); - int id = ((Item) node).getId(); - int anim = id == 2524 ? 920 : id == 2526 ? 921 : id == 2522 ? 919 : 918; - player.animate(new Animation(anim)); - player.sendChat(CHATS[RandomFunction.random(CHATS.length)]); - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - int[] ids = new int[] { 2524, 2526, 2522 }; - for (int id : ids) { - ItemDefinition.forId(id).getHandlers().put("play-with", this); - } - return this; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/brawling_gloves/BrawlingGlovesManager.java b/Server/src/main/java/core/game/interaction/item/brawling_gloves/BrawlingGlovesManager.java deleted file mode 100644 index 3e4c9ec0d..000000000 --- a/Server/src/main/java/core/game/interaction/item/brawling_gloves/BrawlingGlovesManager.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.interaction.item.brawling_gloves; - -import core.cache.def.impl.ItemDefinition; -import core.game.node.entity.player.Player; - -import core.game.node.item.Item; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Objects; - -/** - * Manages brawling gloves for a player - * @author ceik - */ -public class BrawlingGlovesManager { - - final Player player; - public HashMap GloveCharges = new HashMap(); - public void registerGlove(int id) { - try { - registerGlove(id, Objects.requireNonNull(BrawlingGloves.forId(id)).getCharges()); - } catch (Exception e){ - e.printStackTrace(); - } - } - public void registerGlove(int id, int charges) {GloveCharges.putIfAbsent(id,charges);} - - public BrawlingGlovesManager(Player player){this.player = player;} - - public boolean updateCharges(int glove, int charges){ - if(GloveCharges.get(glove) != null){ - if(GloveCharges.get(glove) - charges <= 0) { - GloveCharges.remove(glove); - player.getEquipment().remove(new Item(glove)); - player.getPacketDispatch().sendMessage("You use the last charge of your " + ItemDefinition.forId(glove).getName() + " and they vanish."); - return false; - } - int currentCharges = GloveCharges.get(glove); - GloveCharges.replace(glove,currentCharges - charges); - player.debug("Glove charges: " + (currentCharges - 1)); - if((currentCharges - 1) % 50 == 0) { - player.getPacketDispatch().sendMessage("Your " + ItemDefinition.forId(glove).getName() + " have " + GloveCharges.get(glove) + " charges left."); - } - } - return true; - } - - public double getExperienceBonus(){ - double bonus; - int level = player.getSkullManager().getLevel(); - if(level > 0){ - bonus = 3.0; - }else{ - bonus = 0.5; - } - return bonus; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/toys/ChocatriceCape.java b/Server/src/main/java/core/game/interaction/item/toys/ChocatriceCape.java deleted file mode 100644 index 3a7fed3bc..000000000 --- a/Server/src/main/java/core/game/interaction/item/toys/ChocatriceCape.java +++ /dev/null @@ -1,26 +0,0 @@ -package core.game.interaction.item.toys; - -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.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; - -@Initializable -public class ChocatriceCape extends OptionHandler { - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(12634).getHandlers().put("option:operate",this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getLocks().lockInteractions(2); - player.getAnimator().animate(new Animation(8903),new Graphics(1566)); - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/toys/DiangoReclaimInterface.java b/Server/src/main/java/core/game/interaction/item/toys/DiangoReclaimInterface.java deleted file mode 100644 index 17325e390..000000000 --- a/Server/src/main/java/core/game/interaction/item/toys/DiangoReclaimInterface.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.interaction.item.toys; - -import api.ContentAPI; -import core.game.component.Component; -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.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.content.holiday.easter.BasketofEggsEvent; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - - -/** - * Handles diango's item reclaiming interface - * @author ceik, for the interface, and for most of the holiday items/toys - */ -@Initializable -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 = {YoyoPlugin.YOYO, ReindeerHatPlugin.ReindeerHat, BasketofEggsEvent.RUBBER_CHICKEN,ZombieHeadPlugin.ZOMBIE_HEAD, 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)}; - - //initialize the plugin, add lists of items to the ITEMS list... - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(COMPONENT_ID,this); - ITEMS.addAll(Arrays.asList(HOLIDAY_ITEMS)); - ITEMS.addAll(MarionettePlugin.MARIONETTES); - return this; - } - - public static void open(Player player){ - //close any currently open interfaces - Component curOpen = player.getInterfaceManager().getOpened(); - if(curOpen != null){ - curOpen.close(player); - } - - //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 - || (!(ContentAPI.inInventory(player, 14655, 1) || ContentAPI.inEquipment(player, 14656, 1)) && player.getAttribute("sotr:purchased",false)) - )) - .toArray(Item[]::new); - - //only send items if there are some to send - if(reclaimables.length > 0) { - InterfaceContainer.generateItems(player, reclaimables, new String[]{"Examine", "Take"}, 468, 2, 8, 8); - } - //open the interface - player.getInterfaceManager().open(new Component(468)); - } - - //refresh the interface - public static void refresh(Player player){ - player.getInterfaceManager().close(); - open(player); - } - - @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); - 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]); - 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()); - break; - } - return false; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/toys/MarionettePlugin.java b/Server/src/main/java/core/game/interaction/item/toys/MarionettePlugin.java deleted file mode 100644 index 33dbe0917..000000000 --- a/Server/src/main/java/core/game/interaction/item/toys/MarionettePlugin.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.interaction.item.toys; - -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.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; - -import java.util.Arrays; -import java.util.List; - -@Initializable -public class MarionettePlugin extends OptionHandler { - public final static List MARIONETTES = Arrays.asList(new Item(6865), new Item(6866), new Item(6867)); - private final Animation JUMP = new Animation(3003); - private final Animation WALK = new Animation(3004); - private final Animation BOW = new Animation(3005); - private final Animation DANCE = new Animation(3006); - private final int[][] GFX = new int[][] {{507,508,509,510}, {511,512,513,514}, {515,516,517,518}}; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for(Item item : MARIONETTES) { - ItemDefinition def = item.getDefinition(); - def.getHandlers().put("option:jump",this); - def.getHandlers().put("option:walk",this); - def.getHandlers().put("option:bow",this); - def.getHandlers().put("option:dance",this); - } - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - int index = 0; - switch(node.getId()){ - case 6865: - index = 1; - break; - case 6866: - index = 2; - break; - case 6867: - index = 0; - break; - } - switch(option){ - case "jump": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(JUMP, new Graphics(GFX[index][0])); - break; - case "walk": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(WALK, new Graphics(GFX[index][1])); - break; - case "bow": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(BOW, new Graphics(GFX[index][2])); - break; - case "dance": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(DANCE, new Graphics(GFX[index][3])); - break; - } - return false; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/toys/ReindeerHatPlugin.java b/Server/src/main/java/core/game/interaction/item/toys/ReindeerHatPlugin.java deleted file mode 100644 index 530a414e9..000000000 --- a/Server/src/main/java/core/game/interaction/item/toys/ReindeerHatPlugin.java +++ /dev/null @@ -1,27 +0,0 @@ -package core.game.interaction.item.toys; - -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; - -@Initializable -public class ReindeerHatPlugin extends OptionHandler { - public final static Item ReindeerHat = new Item(10507); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ReindeerHat.getDefinition().getHandlers().put("option:operate",this); - return null; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getLocks().lockInteractions(2); - player.getAnimator().animate(new Animation(5059)); - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/toys/SnowGlobePlugin.java b/Server/src/main/java/core/game/interaction/item/toys/SnowGlobePlugin.java deleted file mode 100644 index 22e5454b7..000000000 --- a/Server/src/main/java/core/game/interaction/item/toys/SnowGlobePlugin.java +++ /dev/null @@ -1,83 +0,0 @@ -package core.game.interaction.item.toys; - -import core.cache.def.impl.ItemDefinition; -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.node.item.Item; -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.Initializable; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; - -@Initializable -public class SnowGlobePlugin extends OptionHandler { - - private final Animation SHAKE = new Animation(7535);//Initial Shake - private final Animation HOLDFACE = new Animation(7536);//Immediately after shake, player holds the snow globe to face - private static final Component INTERFACE = new Component(659);//After HOLDFACE this interface is displayed, player either clicks 'continue' for inv of snowballs, or 'close' for no snowballs - private final Animation DOWNFAST = new Animation(7537);//Used when player hit 'close' on the interface - private final Animation DOWNSLOW = new Animation(7538);//Used when the player hit 'continue' on the interface - private final Animation STOMP = new Animation(7528);//When player hits continue this animation plays - private final Graphics SNOW = new Graphics(1284);//When Animation STOMP is playing this gfx also plays - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new SnowGlobeInterface()); - ItemDefinition.forId(11949).getHandlers().put("option:shake",this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getLocks().lockInteractions(2); - player.getAnimator().animate(SHAKE); - player.getPulseManager().run(new Pulse(3) { - @Override - public boolean pulse() { - player.getAnimator().animate(HOLDFACE); - player.getPulseManager().run(new Pulse(1){ - @Override - public boolean pulse(){ - player.getInterfaceManager().open(INTERFACE); - return true; - } - }); - return true; - } - }); - return true; - } - - public class SnowGlobeInterface extends ComponentPlugin{ - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(INTERFACE.getId(),this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - if(button == 2){ - player.getInterfaceManager().close(); - player.getAnimator().animate(DOWNSLOW); - player.getPulseManager().run(new Pulse(1) { - @Override - public boolean pulse() { - player.getAnimator().animate(STOMP,SNOW); - player.getInventory().add(new Item(11951,player.getInventory().freeSlots())); - return true; - } - }); - return true; - } - player.getAnimator().animate(DOWNFAST); - return true; - } - } -} diff --git a/Server/src/main/java/core/game/interaction/item/toys/YoyoPlugin.java b/Server/src/main/java/core/game/interaction/item/toys/YoyoPlugin.java deleted file mode 100644 index 249ea9fc4..000000000 --- a/Server/src/main/java/core/game/interaction/item/toys/YoyoPlugin.java +++ /dev/null @@ -1,53 +0,0 @@ -package core.game.interaction.item.toys; - -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; - -@Initializable -public class YoyoPlugin extends OptionHandler { - final static Animation PLAY = new Animation(1457); - final static Animation LOOP = new Animation(1458); - final static Animation WALK = new Animation(1459); - final static Animation CRAZY = new Animation(1460); - final static Item YOYO = new Item(4079); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition def = ItemDefinition.forId(YOYO.getId()); - def.getHandlers().put("option:play",this); - def.getHandlers().put("option:loop",this); - def.getHandlers().put("option:walk",this); - def.getHandlers().put("option:crazy",this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - - switch(option){ - case "play": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(PLAY); - break; - case "loop": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(LOOP); - break; - case "walk": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(WALK); - break; - case "crazy": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(CRAZY); - break; - } - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/toys/ZombieHeadPlugin.java b/Server/src/main/java/core/game/interaction/item/toys/ZombieHeadPlugin.java deleted file mode 100644 index d5176feb0..000000000 --- a/Server/src/main/java/core/game/interaction/item/toys/ZombieHeadPlugin.java +++ /dev/null @@ -1,44 +0,0 @@ -package core.game.interaction.item.toys; - -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; - -@Initializable -public class ZombieHeadPlugin extends OptionHandler { - static final Item ZOMBIE_HEAD = new Item(6722); - static final Animation TALK_AT = new Animation(2840); - static final Animation DISPLAY = new Animation(2844); - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition def = ItemDefinition.forId(ZOMBIE_HEAD.getId()); - def.getHandlers().put("option:talk-at",this); - def.getHandlers().put("option:display",this); - def.getHandlers().put("option:question",this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch(option){ - case "talk-at": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(TALK_AT); - player.sendChat("Alas!"); - break; - case "display": - player.getLocks().lockInteractions(2); - player.getAnimator().animate(DISPLAY); - player.sendChat("MWAHHAHAHAHAHAHA"); - break; - case "question": - //player.getDialogueInterpreter().open() - } - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/withitem/CapeDyeingPlugin.java b/Server/src/main/java/core/game/interaction/item/withitem/CapeDyeingPlugin.java deleted file mode 100644 index 861a1e27b..000000000 --- a/Server/src/main/java/core/game/interaction/item/withitem/CapeDyeingPlugin.java +++ /dev/null @@ -1,165 +0,0 @@ -/* -package core.game.interaction.item.withitem; - -import org.rs09.consts.Items; -import core.game.content.global.Dyes; -import core.game.content.global.action.SpecialLadders; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -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.plugin.InitializablePlugin; -import core.plugin.Plugin; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.stream.Stream; - -*/ -/** - * Handles the dyeing of a cape. - * @author afaroutdude - *//* - -@InitializablePlugin -public final class CapeDyeingPlugin extends UseWithHandler { - - */ -/** - * Constructs a new {@code CapeDyeingPlugin} {@code Object}. - *//* - - public CapeDyeingPlugin() { - super(Cape.getIds()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (Cape c : Cape.values()) { - addHandler(c.getDye().getItem().getId(), ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final boolean testCape = Cape.isCape(event.getBaseItem()); - final Item cape = Cape.isCape(event.getBaseItem()) ? event.getBaseItem() : event.getUsedItem(); - final Item dye = Cape.isCape(event.getBaseItem()) ? event.getUsedItem() : event.getBaseItem(); - final Item dyedCape = MAP_DYE_TO_CAPE.get(dye); - if (dyedCape == null) { - return false; - } - if (!cape.equals(dyedCape) && player.getInventory().containsItems(dye, cape) && player.getInventory().remove(dye, cape)) { - player.getInventory().add(dyedCape); - if (dye.equals(Dyes.BLACK.getItem())) { - player.getInventory().add(new Item(Items.VIAL_229)); - } - if (dye.equals(Dyes.PINK.getItem()) && !player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).isComplete(2,5)) { - player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).updateTask(player,2,5,true); - } - } - return true; - } - - */ -/** - * A cape to dye. - * @author Vexia - *//* - - public enum Cape { - BLACK(Dyes.BLACK, new Item(1019)), - RED(Dyes.RED, new Item(1007)), - BLUE(Dyes.BLUE, new Item(1021)), - YELLOW(Dyes.YELLOW, new Item(1023)), - GREEN(Dyes.GREEN, new Item(1027)), - PURPLE(Dyes.PURPLE, new Item(1029)), - ORANGE(Dyes.ORANGE, new Item(1031)), - PINK(Dyes.PINK, new Item(6959)); - - */ -/** - * The dye for the cape. - *//* - - private final Dyes dye; - - */ -/** - * The cape item. - *//* - - private final Item cape; - - */ -/** - * Constructs a new {@code Cape} {@code Object}. - * @param dye the dye. - * @param cape the cape. - *//* - - Cape(Dyes dye, Item cape) { - this.dye = dye; - this.cape = cape; - } - - */ -/** - * Gets the dye. - * @return The dye. - *//* - - public Dyes getDye() { - return dye; - } - - */ -/** - * Gets the cape. - * @return The cape. - *//* - - public Item getCape() { - return cape; - } - - */ -/** - * @return an int array of all cape IDs - *//* - - static public int[] getIds() { - return Stream.of(Cape.values()) - .map(Cape::getCape) - .map(Item::getId) - .mapToInt(Integer::intValue).toArray(); - } - - */ -/** - * @param potentiallyCape - * @return true if passed item is a cape that we can handle - *//* - - static public boolean isCape(Item potentiallyCape){ - for (Cape c : Cape.values()) { - if (c.getCape().getId() == potentiallyCape.getId()) { - return true; - } - } - return false; - } - } - - public static HashMap MAP_DYE_TO_CAPE = new HashMap<>(); - static { - for (Cape c : Cape.values()) { - MAP_DYE_TO_CAPE.putIfAbsent(c.getDye().getItem(), c.getCape()); - } - } -} -*/ diff --git a/Server/src/main/java/core/game/interaction/item/withitem/ChestKeyPlugin.java b/Server/src/main/java/core/game/interaction/item/withitem/ChestKeyPlugin.java deleted file mode 100644 index c3bb454f3..000000000 --- a/Server/src/main/java/core/game/interaction/item/withitem/ChestKeyPlugin.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.interaction.item.withitem; - -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.node.scenery.SceneryBuilder; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle the usage of a chest key on the chest. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ChestKeyPlugin extends UseWithHandler { - - /** - * Represents the chest key item. - */ - private static final Item CHEST_KEY = new Item(432); - - /** - * Represents the pirate message item. - */ - private static final Item PIRATE_MESSAGE = new Item(433); - - /** - * Constructs a new {@code ChestKeyPlugin} {@code Object}. - */ - public ChestKeyPlugin() { - super(432); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2079, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getInventory().remove(CHEST_KEY)) { - SceneryBuilder.replace((Scenery) event.getUsedWith(), ((Scenery) event.getUsedWith()).transform(2080), 3); - player.getInventory().add(PIRATE_MESSAGE); - player.getPacketDispatch().sendMessage("You unlock the chest."); - player.getPacketDispatch().sendMessage("All that's in the chest is a message..."); - player.getPacketDispatch().sendMessage("You take the message from the chest."); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withitem/CrystalKeyCreatePlugin.java b/Server/src/main/java/core/game/interaction/item/withitem/CrystalKeyCreatePlugin.java deleted file mode 100644 index 63e8275c0..000000000 --- a/Server/src/main/java/core/game/interaction/item/withitem/CrystalKeyCreatePlugin.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.interaction.item.withitem; - -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 handle the creation of a crystal key. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CrystalKeyCreatePlugin extends UseWithHandler { - - /** - * Represents the crystal key item. - */ - private static final Item KEY = new Item(989, 1); - - /** - * Constructs a new {@code CrystalKeyCreatePlugin} {@code Object}. - */ - public CrystalKeyCreatePlugin() { - super(985); - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getInventory().remove(event.getBaseItem(), event.getUsedItem())) { - player.getInventory().add(KEY); - player.getPacketDispatch().sendMessage("You join the loop half of a key and the tooth half of a key to make a crystal key."); - } - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(987, ITEM_TYPE, this); - return this; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withitem/FruitCuttingPlugin.java b/Server/src/main/java/core/game/interaction/item/withitem/FruitCuttingPlugin.java deleted file mode 100644 index 51f27f988..000000000 --- a/Server/src/main/java/core/game/interaction/item/withitem/FruitCuttingPlugin.java +++ /dev/null @@ -1,273 +0,0 @@ -package core.game.interaction.item.withitem; - -import api.ContentAPI; -import core.game.component.Component; -import core.plugin.Initializable; -import kotlin.Unit; -import org.rs09.consts.Items; -import core.game.content.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: - ContentAPI.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/java/core/game/interaction/item/withitem/MakeGlassInterfacePlugin.java b/Server/src/main/java/core/game/interaction/item/withitem/MakeGlassInterfacePlugin.java deleted file mode 100644 index 6411dafad..000000000 --- a/Server/src/main/java/core/game/interaction/item/withitem/MakeGlassInterfacePlugin.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.interaction.item.withitem; - -import core.game.component.Component; -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/interaction/item/withitem/PoisonRemovePlugin.java b/Server/src/main/java/core/game/interaction/item/withitem/PoisonRemovePlugin.java deleted file mode 100644 index 73fb90051..000000000 --- a/Server/src/main/java/core/game/interaction/item/withitem/PoisonRemovePlugin.java +++ /dev/null @@ -1,304 +0,0 @@ -package core.game.interaction.item.withitem; - -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 remove poision from a weapon. - * @author 'Vexia - * @date 29/11/2013 - */ -@Initializable -public class PoisonRemovePlugin extends UseWithHandler { - - /** - * Constructs a new {@code PoisonRemovePlugin} {@code Object}. - */ - public PoisonRemovePlugin() { - super(883, 5616, 883, 885, 5617, 885, 887, 5618, 887, 889, 5619, 889, 891, 5620, 891, 893, 5621, 893, 871, 5655, 5662, 870, 5654, 5661, 872, 5656, 5663, 873, 5657, 5664, 875, 5659, 5666, 876, 5660, 5667, 874, 5658, 5665, 812, 5628, 5635, 813, 5629, 5636, 814, 5630, 5637, 3094, 5631, 5638, 815, 5632, 5639, 816, 5633, 5640, 817, 5634, 5641, 831, 5641, 5648, 832, 5642, 5649, 833, 5643, 5650, 834, 5644, 5651, 835, 5645, 5652, 836, 5646, 5653, 1219, 5668, 5686, 1221, 5670, 5688, 1223, 5672, 5690, 1225, 5674, 5692, 1227, 5676, 5694, 1229, 5678, 5696, 1231, 5680, 5698, 1233, 5682, 5700, 1251, 5704, 5618, 1253, 5706, 5620, 1255, 5708, 5622, 1257, 5710, 5624, 1259, 5712, 5626, 1261, 5714, 5628, 1263, 5716, 5630, 4582, 5734, 5636); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(3188, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final PoisonedWeapon weapon = PoisonedWeapon.forItem(event.getBaseItem().getName().contains("Cleaning") ? event.getUsedItem() : event.getBaseItem()); - if (weapon == null) { - return true; - } - if (player.getInventory().remove(event.getBaseItem().getName().contains("Cleaning") ? event.getUsedItem() : event.getBaseItem())) { - player.getInventory().add(new Item(weapon.getItem(), 1)); - } - return true; - } - - /** - * Represents the poisioning of a weapon. - * @author 'Vexia - */ - public enum PoisonedWeapon { - - /** - * Represents a poisioned weapon. - */ - BRONZE_ARROW(882, 883, 5616, 883), - /** - * Represents a poisioned weapon. - */ - IRON_ARROW(884, 885, 5617, 885), - /** - * Represents a poisioned weapon. - */ - STEEL_ARROW(886, 887, 5618, 887), - /** - * Represents a poisioned weapon. - */ - MITHRIL_ARROW(888, 889, 5619, 889), - /** - * Represents a poisioned weapon. - */ - ADAMANT_ARROW(890, 891, 5620, 891), - /** - * Represents a poisioned weapon. - */ - RUNE_ARROW(892, 893, 5621, 893), - /** - * Represents a poisioned weapon. - */ - IRON_KNIFE(863, 871, 5655, 5662), - /** - * Represents a poisioned weapon. - */ - RONZE_KNIFE(864, 870, 5654, 5661), - /** - * Represents a poisioned weapon. - */ - STEEL_KNIFE(865, 872, 5656, 5663), - /** - * Represents a poisioned weapon. - */ - MITHRIL_KNIFE(866, 873, 5657, 5664), - /** - * Represents a poisioned weapon. - */ - ADAMANT_KNIFE(867, 875, 5659, 5666), - /** - * Represents a poisioned weapon. - */ - RUNE_KNIFE(868, 876, 5660, 5667), - /** - * Represents a poisioned weapon. - */ - BLACK_KNIFE(869, 874, 5658, 5665), - /** - * Represents a poisioned weapon. - */ - BRONZE_DART(806, 812, 5628, 5635), - /** - * Represents a poisioned weapon. - */ - IRON_DART(807, 813, 5629, 5636), - /** - * Represents a poisioned weapon. - */ - STEEL_DART(808, 814, 5630, 5637), - /** - * Represents a poisioned weapon. - */ - BLACK_DART(3093, 3094, 5631, 5638), - /** - * Represents a poisioned weapon. - */ - MITHRIL_DART(809, 815, 5632, 5639), - /** - * Represents a poisioned weapon. - */ - ADAMANT_DART(810, 816, 5633, 5640), - /** - * Represents a poisioned weapon. - */ - RUNE_DART(811, 817, 5634, 5641), - /** - * Represents a poisioned weapon. - */ - IRON_JAVELIN(825, 831, 5641, 5648), - /** - * Represents a poisioned weapon. - */ - BRONZE_JAVELIN(826, 832, 5642, 5649), - /** - * Represents a poisioned weapon. - */ - STEEL_JAVELIN(827, 833, 5643, 5650), - /** - * Represents a poisioned weapon. - */ - MITHRIL_JAVELIN(828, 834, 5644, 5651), - /** - * Represents a poisioned weapon. - */ - ADAMANT_JAVELIN(829, 835, 5645, 5652), - /** - * Represents a poisioned weapon. - */ - RUNE_JAVELIN(830, 836, 5646, 5653), - /** - * Represents a poisioned weapon. - */ - IRON_DAGGER(1203, 1219, 5668, 5686), - /** - * Represents a poisioned weapon. - */ - BRONZE_DAGGER(1205, 1221, 5670, 5688), - /** - * Represents a poisioned weapon. - */ - STEEL_DAGGER(1207, 1223, 5672, 5690), - /** - * Represents a poisioned weapon. - */ - MITHRIL_DAGGER(1209, 1225, 5674, 5692), - /** - * Represents a poisioned weapon. - */ - ADAMANT_DAGGER(1211, 1227, 5676, 5694), - /** - * Represents a poisioned weapon. - */ - RUNE_DAGGER(1213, 1229, 5678, 5696), - /** - * Represents a poisioned weapon. - */ - DRAGON_DAGGER(1215, 1231, 5680, 5698), - /** - * Represents a poisioned weapon. - */ - BLACK_DAGGER(1217, 1233, 5682, 5700), - /** - * Represents a poisioned weapon. - */ - BRONZE_SPEAR(1237, 1251, 5704, 5618), - /** - * Represents a poisioned weapon. - */ - IRON_SPEAR(1239, 1253, 5706, 5620), - /** - * Represents a poisioned weapon. - */ - STEEL_SPEAR(1241, 1255, 5708, 5622), - /** - * Represents a poisioned weapon. - */ - MITHRIL_SPEAR(1243, 1257, 5710, 5624), - /** - * Represents a poisioned weapon. - */ - ADAMANT_SPEAR(1245, 1259, 5712, 5626), - /** - * Represents a poisioned weapon. - */ - RUNE_SPEAR(1247, 1261, 5714, 5628), - /** - * Represents a poisioned weapon. - */ - DRAGON_SPEAR(1249, 1263, 5716, 5630), - /** - * Represents a poisioned weapon. - */ - BLACK_SPEAR(4580, 4582, 5734, 5636); - - /** - * @param id the id. - * @return the value. - */ - public static PoisonedWeapon forId(int id) { - for (PoisonedWeapon weapon : PoisonedWeapon.values()) { - if (weapon.getItem() == id) { - return weapon; - } - } - return null; - } - - /** - * The item. - */ - private int item; - - /** - * The first stage. - */ - private int first; - - /** - * The second stage. - */ - private int second; - - /** - * The third stage. - */ - private int third; - - /** - * Constructs a new PoisionWeapon. - */ - PoisonedWeapon(int item, int first, int second, int third) { - this.item = item; - this.first = first; - this.second = second; - this.third = third; - } - - /** - * @return the first. - */ - public int getFirst() { - return first; - } - - /** - * @return the item. - */ - public int getItem() { - return item; - } - - /** - * @return the second. - */ - public int getSecond() { - return second; - } - - /** - * @return the third. - */ - public int getThird() { - return third; - } - - /** - * Method used to get the poisioned weapon for the id. - * @param i the id. - * @return the weapon. - */ - public static PoisonedWeapon forItem(final Item i) { - for (PoisonedWeapon weapon : values()) { - if (weapon.getFirst() == i.getId() || weapon.getSecond() == i.getId() || weapon.getThird() == i.getId()) { - return weapon; - } - } - return null; - } - } -} diff --git a/Server/src/main/java/core/game/interaction/item/withitem/PoisonWeaponPlugin.java b/Server/src/main/java/core/game/interaction/item/withitem/PoisonWeaponPlugin.java deleted file mode 100644 index 03c8203ff..000000000 --- a/Server/src/main/java/core/game/interaction/item/withitem/PoisonWeaponPlugin.java +++ /dev/null @@ -1,348 +0,0 @@ -package core.game.interaction.item.withitem; - -import api.ContentAPI; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; -import org.rs09.consts.Items; - -/** - * Handles weapon poisiong. - * @author 'Vexia - */ -@Initializable -public class PoisonWeaponPlugin extends UseWithHandler { - - /** - * Represents the poisioning of a weapon. - * @author 'Vexia - */ - public enum PoisonedWeapon { - - /** - * Represents a poisioned weapon. - */ - BRONZE_ARROW(882, 883, 5616, 883), - /** - * Represents a poisioned weapon. - */ - IRON_ARROW(884, 885, 5617, 885), - /** - * Represents a poisioned weapon. - */ - STEEL_ARROW(886, 887, 5618, 887), - /** - * Represents a poisioned weapon. - */ - MITHRIL_ARROW(888, 889, 5619, 889), - /** - * Represents a poisioned weapon. - */ - ADAMANT_ARROW(890, 891, 5620, 891), - /** - * Represents a poisioned weapon. - */ - RUNE_ARROW(892, 893, 5621, 893), - /** - * Represents a poisioned weapon. - */ - IRON_KNIFE(863, 871, 5655, 5662), - /** - * Represents a poisioned weapon. - */ - RONZE_KNIFE(864, 870, 5654, 5661), - /** - * Represents a poisioned weapon. - */ - STEEL_KNIFE(865, 872, 5656, 5663), - /** - * Represents a poisioned weapon. - */ - MITHRIL_KNIFE(866, 873, 5657, 5664), - /** - * Represents a poisioned weapon. - */ - ADAMANT_KNIFE(867, 875, 5659, 5666), - /** - * Represents a poisioned weapon. - */ - RUNE_KNIFE(868, 876, 5660, 5667), - /** - * Represents a poisioned weapon. - */ - BLACK_KNIFE(869, 874, 5658, 5665), - /** - * Represents a poisioned weapon. - */ - BRONZE_DART(806, 812, 5628, 5635), - /** - * Represents a poisioned weapon. - */ - IRON_DART(807, 813, 5629, 5636), - /** - * Represents a poisioned weapon. - */ - STEEL_DART(808, 814, 5630, 5637), - /** - * Represents a poisioned weapon. - */ - BLACK_DART(3093, 3094, 5631, 5638), - /** - * Represents a poisioned weapon. - */ - MITHRIL_DART(809, 815, 5632, 5639), - /** - * Represents a poisioned weapon. - */ - ADAMANT_DART(810, 816, 5633, 5640), - /** - * Represents a poisioned weapon. - */ - RUNE_DART(811, 817, 5634, 5641), - /** - * Represents a poisioned weapon. - */ - IRON_JAVELIN(825, 831, 5641, 5648), - /** - * Represents a poisioned weapon. - */ - BRONZE_JAVELIN(826, 832, 5642, 5649), - /** - * Represents a poisioned weapon. - */ - STEEL_JAVELIN(827, 833, 5643, 5650), - /** - * Represents a poisioned weapon. - */ - MITHRIL_JAVELIN(828, 834, 5644, 5651), - /** - * Represents a poisioned weapon. - */ - ADAMANT_JAVELIN(829, 835, 5645, 5652), - /** - * Represents a poisioned weapon. - */ - RUNE_JAVELIN(830, 836, 5646, 5653), - /** - * Represents a poisioned weapon. - */ - IRON_DAGGER(1203, 1219, 5668, 5686), - /** - * Represents a poisioned weapon. - */ - BRONZE_DAGGER(1205, 1221, 5670, 5688), - /** - * Represents a poisioned weapon. - */ - STEEL_DAGGER(1207, 1223, 5672, 5690), - /** - * Represents a poisioned weapon. - */ - MITHRIL_DAGGER(1209, 1225, 5674, 5692), - /** - * Represents a poisioned weapon. - */ - ADAMANT_DAGGER(1211, 1227, 5676, 5694), - /** - * Represents a poisioned weapon. - */ - RUNE_DAGGER(1213, 1229, 5678, 5696), - /** - * Represents a poisioned weapon. - */ - DRAGON_DAGGER(1215, 1231, 5680, 5698), - /** - * Represents a poisioned weapon. - */ - BLACK_DAGGER(1217, 1233, 5682, 5700), - /** - * Represents a poisioned weapon. - */ - BRONZE_SPEAR(1237, 1251, 5704, 5618), - /** - * Represents a poisioned weapon. - */ - IRON_SPEAR(1239, 1253, 5706, 5620), - /** - * Represents a poisioned weapon. - */ - STEEL_SPEAR(1241, 1255, 5708, 5622), - /** - * Represents a poisioned weapon. - */ - MITHRIL_SPEAR(1243, 1257, 5710, 5624), - /** - * Represents a poisioned weapon. - */ - ADAMANT_SPEAR(1245, 1259, 5712, 5626), - /** - * Represents a poisioned weapon. - */ - RUNE_SPEAR(1247, 1261, 5714, 5628), - /** - * Represents a poisioned weapon. - */ - DRAGON_SPEAR(1249, 1263, 5716, 5730), - /** - * Represents a poisioned weapon. - */ - BLACK_SPEAR(4580, 4582, 5734, 5636); - - /** - * @param id the id. - * @return the value. - */ - public static PoisonedWeapon forId(int id) { - for (PoisonedWeapon weapon : PoisonedWeapon.values()) { - if (weapon.getItem() == id) { - return weapon; - } - } - return null; - } - - /** - * The item. - */ - private int item; - - /** - * The first stage. - */ - private int first; - - /** - * The second stage. - */ - private int second; - - /** - * The third stage. - */ - private int third; - - /** - * Constructs a new PoisionWeapon. - */ - PoisonedWeapon(int item, int first, int second, int third) { - this.item = item; - this.first = first; - this.second = second; - this.third = third; - } - - /** - * @return the first. - */ - public int getFirst() { - return first; - } - - /** - * @return the item. - */ - public int getItem() { - return item; - } - - /** - * @return the second. - */ - public int getSecond() { - return second; - } - - /** - * @return the third. - */ - public int getThird() { - return third; - } - - /** - * Method used to get the poisioned weapon for the id. - * @param i the id. - * @return the weapon. - */ - public static PoisonedWeapon forItem(final Item i) { - for (PoisonedWeapon weapon : values()) { - if (weapon.getFirst() == i.getId() || weapon.getSecond() == i.getId() || weapon.getThird() == i.getId()) { - return weapon; - } - } - return null; - } - } - - public static void poison(Player player, int type, int item, final Item weaponItem, PoisonedWeapon weapon) { - int product = -1; - player.animate(new Animation(1652)); - switch (item) { - case 187:// normal. - product = weapon.getFirst(); - player.getInventory().remove(new Item(187, 1)); - break; - case 5937:// second - product = weapon.getSecond(); - player.getInventory().remove(new Item(5937, 1)); - break; - case 5940:// third. - product = weapon.getThird(); - player.getInventory().remove(new Item(5940, 1)); - break; - default: - break; - } - int amt = Math.min(weaponItem.getAmount(), 5); - player.getInventory().remove(new Item(weaponItem.getId(), amt)); - ContentAPI.addItemOrDrop(player, product, amt); - ContentAPI.addItemOrDrop(player, Items.VIAL_229, 1); - player.getPacketDispatch().sendMessage("You poison the " + weaponItem.getName().toLowerCase() + "."); - } - - public PoisonWeaponPlugin() { - super(882, 884, 886, 888, 890, 892, 863, 864, 865, 866, 867, 868, 869, 806, 807, 808, 3093, 809, 810, 811, 825, 826, 827, 828, 829, 830, 1203, 1205, 1207, 1209, 1211, 1213, 1215, 1217, 1237, 1239, 1241, 1243, 1245, 1247, 1249, 4580); - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Item second = (Item) event.getUsedWith(); - if (event.getUsedItem().getId() == 187 || second.getId() == 187) { - - if (event.getUsedItem().getId() != 187) { - poison(player, 1, second.getId(), event.getUsedItem(), PoisonedWeapon.forId(event.getUsedItem().getId())); - } else if (second.getId() != 187) { - poison(player, 1, event.getUsedItem().getId(), second, PoisonedWeapon.forId(second.getId())); - } - } - if (event.getUsedItem().getId() == 5937 || second.getId() == 5937) { - if (event.getUsedItem().getId() != 5937) { - poison(player, 2, second.getId(), event.getUsedItem(), PoisonedWeapon.forId(event.getUsedItem().getId())); - } else if (second.getId() != 5937) { - poison(player, 2, event.getUsedItem().getId(), second, PoisonedWeapon.forId(second.getId())); - } - } - if (event.getUsedItem().getId() == 5940 || second.getId() == 5940) { - if (event.getUsedItem().getId() != 5940) { - poison(player, 3, second.getId(), event.getUsedItem(), PoisonedWeapon.forId(event.getUsedItem().getId())); - } else if (second.getId() != 5940) { - poison(player, 3, event.getUsedItem().getId(), second, PoisonedWeapon.forId(second.getId())); - } - } - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(187, ITEM_TYPE, this); - addHandler(5937, ITEM_TYPE, this); - addHandler(5940, ITEM_TYPE, this); - return this; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withnpc/ForestersArmsCiderPlugin.java b/Server/src/main/java/core/game/interaction/item/withnpc/ForestersArmsCiderPlugin.java deleted file mode 100644 index 566961755..000000000 --- a/Server/src/main/java/core/game/interaction/item/withnpc/ForestersArmsCiderPlugin.java +++ /dev/null @@ -1,50 +0,0 @@ -package core.game.interaction.item.withnpc; - -import org.rs09.consts.Items; -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.game.node.item.Item; -import core.game.world.map.zone.ZoneBorders; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to "poison" King Arthur. - * @author afaroutdude - */ -@Initializable -public final class ForestersArmsCiderPlugin extends UseWithHandler { - private static final Item CIDER = new Item(Items.CIDER_5763); - - /** - * Constructs a new {@code LadyKeliRopePlugin} {@code Object}. - */ - public ForestersArmsCiderPlugin() { - super(Items.CIDER_5763); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - int[] ids = {1,2,3,4,5}; - for (int id : ids) { - addHandler(id, NPC_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (!new ZoneBorders(2689, 3488, 2700, 3498, 0).insideBorder(player)) { - return true; - } - final NPC npc = event.getUsedWith() instanceof NPC ? event.getUsedWith().asNpc() : null; - if (npc != null) { - player.getDialogueInterpreter().open(npc.getId(), npc, CIDER); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withnpc/KingArthurPoisonChalicePlugin.java b/Server/src/main/java/core/game/interaction/item/withnpc/KingArthurPoisonChalicePlugin.java deleted file mode 100644 index b247baf3b..000000000 --- a/Server/src/main/java/core/game/interaction/item/withnpc/KingArthurPoisonChalicePlugin.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.game.interaction.item.withnpc; - -import org.rs09.consts.Items; -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.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to "poison" King Arthur. - * @author afaroutdude - */ -@Initializable -public final class KingArthurPoisonChalicePlugin extends UseWithHandler { - private static final Item POISON_CHALICE = new Item(Items.POISON_CHALICE_197); - - /** - * Constructs a new {@code LadyKeliRopePlugin} {@code Object}. - */ - public KingArthurPoisonChalicePlugin() { - super(POISON_CHALICE.getId()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(251, NPC_TYPE, this); - return new KingArthurPoisonChalicePlugin(); - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final NPC npc = event.getUsedWith() instanceof NPC ? event.getUsedWith().asNpc() : null; - if (npc != null) { - player.getDialogueInterpreter().open(npc.getId(), npc, POISON_CHALICE); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withnpc/LadyKeliRopePlugin.java b/Server/src/main/java/core/game/interaction/item/withnpc/LadyKeliRopePlugin.java deleted file mode 100644 index ec0b56dec..000000000 --- a/Server/src/main/java/core/game/interaction/item/withnpc/LadyKeliRopePlugin.java +++ /dev/null @@ -1,57 +0,0 @@ -package core.game.interaction.item.withnpc; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.game.node.item.Item; -import rs09.game.world.GameWorld; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to tie up lady keli. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LadyKeliRopePlugin extends UseWithHandler { - - /** - * Represents the rope item. - */ - private static final Item ROPE = new Item(954); - - /** - * Constructs a new {@code LadyKeliRopePlugin} {@code Object}. - */ - public LadyKeliRopePlugin() { - super(954); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(919, NPC_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Quest quest = player.getQuestRepository().getQuest("Prince Ali Rescue"); - if (quest.getStage(player) >= 40 && player.getAttribute("guard-drunk", false) && quest.getStage(player) != 100) { - if (player.getInventory().remove(ROPE)) { - player.getDialogueInterpreter().sendDialogue("You overpower Keli, tie her up, and put her in a cupboard."); - quest.setStage(player, 50); - player.setAttribute("keli-gone", GameWorld.getTicks() + 350); - } - } else { - if (quest.getStage(player) == 40) { - player.getPacketDispatch().sendMessage("You need to do something about the guard first."); - } - return true; - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withnpc/NeedleCowPlugin.java b/Server/src/main/java/core/game/interaction/item/withnpc/NeedleCowPlugin.java deleted file mode 100644 index a40478d67..000000000 --- a/Server/src/main/java/core/game/interaction/item/withnpc/NeedleCowPlugin.java +++ /dev/null @@ -1,51 +0,0 @@ -package core.game.interaction.item.withnpc; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the needle on a cow plugin. - * @author 'Vexia - * @date 18/11/2013 - */ -@Initializable -public class NeedleCowPlugin extends UseWithHandler { - - /** - * Constructs a new {@code NeedleCowPlugin} {@code Object}. - */ - public NeedleCowPlugin() { - super(1733); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(81, NPC_TYPE, this); - addHandler(397, NPC_TYPE, this); - addHandler(955, NPC_TYPE, this); - addHandler(1691, NPC_TYPE, this); - addHandler(1766, NPC_TYPE, this); - addHandler(1767, NPC_TYPE, this); - addHandler(1768, NPC_TYPE, this); - addHandler(1886, NPC_TYPE, this); - addHandler(1998, NPC_TYPE, this); - addHandler(1999, NPC_TYPE, this); - addHandler(2000, NPC_TYPE, this); - addHandler(2310, NPC_TYPE, this); - addHandler(3309, NPC_TYPE, this); - addHandler(5210, NPC_TYPE, this); - addHandler(5211, NPC_TYPE, this); - addHandler(5603, NPC_TYPE, this); - addHandler(7484, NPC_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - event.getPlayer().getPacketDispatch().sendMessage("The cow doesn't want that."); - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withnpc/StrayDogBonesPlugin.java b/Server/src/main/java/core/game/interaction/item/withnpc/StrayDogBonesPlugin.java deleted file mode 100644 index cfcd314e3..000000000 --- a/Server/src/main/java/core/game/interaction/item/withnpc/StrayDogBonesPlugin.java +++ /dev/null @@ -1,52 +0,0 @@ -package core.game.interaction.item.withnpc; - -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.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the bones on stray dogs plugin. - * @author Vexia - */ -@Initializable -public class StrayDogBonesPlugin extends UseWithHandler { - - /** - * The dog ids. - */ - private static final int[] DOGS = new int[] { 4766, 4767, 5917, 5918 }; - - /** - * Constructs a new {@code StrayDogBonesPlugin} {@code Object} - */ - public StrayDogBonesPlugin() { - super(526, 528, 530, 532, 534, 536, 2530, 2859, 3123, 3125, 3127, 3128, 3129, 3130, 3131, 3132, 3133, 3179, 3180, 3181, 3182, 3183, 3185, 3186, 3187, 4812, 4830, 4832, 4834, 6729, 6812, 11806); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : DOGS) { - addHandler(id, NPC_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Item item = event.getUsedItem(); - final Player player = event.getPlayer(); - final NPC npc = event.getUsedWith().asNpc(); - if (player.getInventory().remove(item)) { - player.sendMessage("You feed your dog bones."); - npc.sendChat("Woof"); - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 8); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/BankUnnotePlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/BankUnnotePlugin.java deleted file mode 100644 index 53032b7ca..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/BankUnnotePlugin.java +++ /dev/null @@ -1,114 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -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.IronmanMode; -import core.game.node.item.Item; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the unnoting of noted items when used on a bank booth. - * @author Vexia - * @author Splinter - */ -@Initializable -public class BankUnnotePlugin extends UseWithHandler { - - /** - * The bank booth ids. - */ - private static final int[] BOOTHS = new int[] { 2213, 3194, 5276, 6084, 10517, 11338, 11402, 11758, 12309, 12798, 14367, 16700, 18491, 19230, 20325, 20391, 22819, 24914, 25808, 26972, 27663, 28514, 29085, 30016, 34752, 35647, 36786 }; - - /** - * The banker ids, - */ - private static final int[] BANKERS = new int[] { 44, 45, 494, 495, 496, 497, 498, 499, 953, 1036, 1360, 2163, 2164, 2354, 2355, 2568, 2569, 2570, 3198, 3199, 3824, 5258, 5260, 5776, 5777, 5912, 5913, 6200, 6532, 6533, 6534, 6535, 6538, 7445, 7446, 7605 }; - - /** - * Constructs a new {@Code BankUnnotePlugin} {@Code Object} - */ - public BankUnnotePlugin() { - super(); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : BOOTHS) { - addHandler(id, OBJECT_TYPE, this); - } - for (int id : BANKERS) { - addHandler(id, NPC_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Item item = event.getUsedItem(); - if (!player.isAdmin() && (player.getIronmanManager().getMode() == IronmanMode.NONE || item.getDefinition().isUnnoted())) { - return false; - } - if (item.getDefinition().isStackable() && item.getDefinition().isUnnoted()) { - player.getPacketDispatch().sendMessage("You can't note stackable items."); - return true; - } - int amount = item.getAmount(); - if (amount > player.getInventory().freeSlots()) { - amount = 28 - player.getInventory().freeSlots(); - } - if (amount < 1 || player.getInventory().freeSlots() <= 0) { - player.sendMessage("Not enough inventory space."); - return true; - } - if (player.getInventory().freeSlots() >= amount) { - if (player.getInventory().remove(new Item(item.getId(), amount))) { - Item newItem = new Item(item.getNoteChange(), amount); - player.getInventory().add(newItem); - player.getDialogueInterpreter().sendItemMessage(newItem, "You exchange your noted items."); - player.lock(1); - return true; - } - } else { - int toAdd = player.getInventory().freeSlots(); - if (player.getInventory().remove(new Item(item.getId(), toAdd))) { - Item newItem = new Item(item.getNoteChange(), toAdd); - player.getInventory().add(newItem); - player.getDialogueInterpreter().sendItemMessage(newItem, "You exchange your noted items."); - player.lock(1); - return true; - } - } - return false; - } - - @Override - public Location getDestination(Player player, Node with) { - if (with instanceof NPC) { - NPC npc = (NPC) with; - if (npc.getAttribute("facing_booth", false)) { - Direction dir = npc.getDirection(); - return npc.getLocation().transform(dir.getStepX() << 1, dir.getStepY() << 1, 0); - } - if (npc.getId() == 6533) { - return Location.create(3167, 3490, 0);// ge bankers. - } else if (npc.getId() == 6535) { - return Location.create(3162, 3489, 0); - } else if (npc.getId() == 4907) { - return npc.getLocation().transform(0, -2, 0); - } - } - return null; - } - - @Override - public boolean isDynamic() { - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/BurnMeatPlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/BurnMeatPlugin.java deleted file mode 100644 index db0364a10..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/BurnMeatPlugin.java +++ /dev/null @@ -1,89 +0,0 @@ -/* -package core.game.interaction.item.withobject; - -import org.crandor.game.content.global.consumable.Consumables; -import org.crandor.game.content.global.consumable.Food; -import org.crandor.game.interaction.NodeUsageEvent; -import org.crandor.game.interaction.UseWithHandler; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.item.Item; -import org.crandor.game.node.object.GameObject; -import org.crandor.game.world.update.flag.context.Animation; -import org.crandor.net.packet.PacketRepository; -import org.crandor.net.packet.context.GameMessageContext; -import org.crandor.net.packet.out.GameMessage; -import org.crandor.plugin.InitializablePlugin; -import org.crandor.plugin.Plugin; - -*/ -/** - * Represents the plugin used to burn a fire. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class BurnMeatPlugin extends UseWithHandler { - - */ -/** - * Represents the objects allowed to cook {@code Food} on. - *//* - - private 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 }; - - */ -/** - * Represents the animation used when cooking over a range. - *//* - - private static final Animation RANGE_ANIMATION = new Animation(883); - - */ -/** - * Represents the animation used when cooking over a fire. - *//* - - private static final Animation FIRE_ANIMATION = new Animation(897); - - */ -/** - * Constructs a new {@code BurnMeatPlugin} {@code Object}. - *//* - - public BurnMeatPlugin() { - 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, 5984); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : OBJECTS) { - addHandler(id, OBJECT_TYPE, this); - } - System.out.println("Burned meat plugin successfully initialized"); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Food food = Consumables.forFood(event.getUsedItem()); - final GameObject object = (GameObject) event.getUsedWith(); - final Item usedItem = event.getUsedItem(); - if (food.getBurnt() == null) { - player.getPacketDispatch().sendMessage("You can't burn this piece of food."); - return true; - } - player.debug("Used Item: " + usedItem + " In inventory? " + player.getInventory().containsItem(usedItem)); - if (player.getInventory().containsItem(usedItem)) { - player.getInventory().remove(usedItem); - player.lock(3); - player.animate(!object.getName().toLowerCase().equals("fire") ? RANGE_ANIMATION : FIRE_ANIMATION); - player.getInventory().add(food.getBurnt()); - player.getPacketDispatch().sendMessage("You deliberately burn the perfectly good piece of meat."); - } - return true; - } - -} -*/ diff --git a/Server/src/main/java/core/game/interaction/item/withobject/CauldronOfThunder.java b/Server/src/main/java/core/game/interaction/item/withobject/CauldronOfThunder.java deleted file mode 100644 index 7bde4af2b..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/CauldronOfThunder.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin handler used for with the cauldron of thunder. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CauldronOfThunder extends UseWithHandler { - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(833); - - /** - * Represents the enchanted items. - */ - private static final Item[] ITEMS = new Item[] { new Item(524, 1), new Item(522, 1), new Item(523, 1), new Item(525, 1) }; - - /** - * Constructs a new {@code CauldronOfThunder} {@code Object}. - */ - public CauldronOfThunder() { - super(2136, 2132, 2134, 2138); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2142, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - event.getPlayer().animate(ANIMATION); - event.getPlayer().getInventory().remove(event.getUsedItem()); - if (event.getUsedItem().getId() == 2136) { - event.getPlayer().getInventory().add(ITEMS[0]); - } else if (event.getUsedItem().getId() == 2132) { - event.getPlayer().getInventory().add(ITEMS[1]); - } else if (event.getUsedItem().getId() == 2134) { - event.getPlayer().getInventory().add(ITEMS[2]); - } else if (event.getUsedItem().getId() == 2138) { - event.getPlayer().getInventory().add(ITEMS[3]); - } - event.getPlayer().getPacketDispatch().sendMessage("You dip the " + event.getUsedItem().getName().toLowerCase() + " in the cauldron."); - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/DragonPlatebody.java b/Server/src/main/java/core/game/interaction/item/withobject/DragonPlatebody.java deleted file mode 100644 index 8ce560193..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/DragonPlatebody.java +++ /dev/null @@ -1,5 +0,0 @@ -package core.game.interaction.item.withobject; - -public class DragonPlatebody { - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/EctophialFillPlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/EctophialFillPlugin.java deleted file mode 100644 index 0cf49c711..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/EctophialFillPlugin.java +++ /dev/null @@ -1,52 +0,0 @@ -package core.game.interaction.item.withobject; - -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 rs09.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Fills an ectophial. - * @author Vexia - */ -@Initializable -public class EctophialFillPlugin extends UseWithHandler { - - /** - * Constructs a new {@code EctophialFillPlugin} {@code Object} - */ - public EctophialFillPlugin() { - super(4252); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(5282, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - player.lock(3); - player.animate(Animation.create(1652)); - player.getAudioManager().send(1132); - GameWorld.getPulser().submit(new Pulse(3, player) { - @Override - public boolean pulse() { - if (player.getInventory().remove(new Item(4252))) { - player.getInventory().add(new Item(4251)); - } - player.sendMessage("You refill the ectophial from the Ectofuntus."); - return true; - } - }); - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/GWDEntranceRopeUsage.java b/Server/src/main/java/core/game/interaction/item/withobject/GWDEntranceRopeUsage.java deleted file mode 100644 index 2a5a99e46..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/GWDEntranceRopeUsage.java +++ /dev/null @@ -1,35 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.scenery.Scenery; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles using a rope on the God wars entrance. - * @author Emperor - */ -@Initializable -public final class GWDEntranceRopeUsage extends UseWithHandler { - - /** - * Constructs a new {@code GWDEntranceRopeUsage} {@code Object}. - */ - public GWDEntranceRopeUsage() { - super(954); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(26340, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - Scenery object = (Scenery) event.getUsedWith(); - return object.getInteraction().get(0).getHandler().handle(event.getPlayer(), object, "tie-rope"); - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/GildedAltarPlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/GildedAltarPlugin.java deleted file mode 100644 index 1dc8fedf7..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/GildedAltarPlugin.java +++ /dev/null @@ -1,60 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.game.content.global.Bones; -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.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the guilded altar. - * @author Vexia - * - */ -@Initializable -public class GildedAltarPlugin extends UseWithHandler { - - /** - * The animation for the player. - */ - private static final Animation ANIMATION = new Animation(713); - - /** - * The graphics for the player. - */ - private static final Graphics GRAPHICS = new Graphics(624); - - /** - * Constructs the {@code GuildedAltarPlugin} - */ - public GildedAltarPlugin() { - super(Bones.getArray()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(24343, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Bones bone = Bones.forId(event.getUsedItem().getId()); - if (bone == null) { - return true; - } - if (player.getInventory().remove(event.getUsedItem(), event.getUsedItem().getSlot(), true)) { - player.lock(ANIMATION.getDelay()); - player.visualize(ANIMATION, GRAPHICS); - player.getSkills().addExperience(Skills.PRAYER, bone.getExperience() * 2.5); - player.sendMessage("The gods are very pleased with your offering."); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/HairdresserCheesePlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/HairdresserCheesePlugin.java deleted file mode 100644 index 66ebbd00b..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/HairdresserCheesePlugin.java +++ /dev/null @@ -1,52 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.plugin.Initializable; -import org.rs09.consts.Items; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -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 rs09.game.world.GameWorld; -import core.plugin.Plugin; - -/** - * Fills an ectophial. - * @author afaroutdude - */ -@Initializable -public class HairdresserCheesePlugin extends UseWithHandler { - - /** - * Constructs a new {@code EctophialFillPlugin} {@code Object} - */ - public HairdresserCheesePlugin() { - super(Items.CHEESE_1985); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(11677, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - player.lock(3); - //player.animate(Animation.create(1652)); - GameWorld.getPulser().submit(new Pulse(3, player) { - @Override - public boolean pulse() { - if (player.getInventory().remove(new Item(Items.CHEESE_1985))) { - player.sendMessage("You throw the cheese to Ridgeley, for which he appears grateful."); - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR,0, 6); - } - return true; - } - }); - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/IncubatorPlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/IncubatorPlugin.java deleted file mode 100644 index d031d1bff..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/IncubatorPlugin.java +++ /dev/null @@ -1,118 +0,0 @@ -package core.game.interaction.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 core.game.node.entity.skill.summoning.pet.IncubatorEgg; -import core.game.node.item.GroundItemManager; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.StringUtils; -import rs09.game.node.entity.state.newsys.states.IncubatorState; -import rs09.plugin.PluginManager; - -/** - * Handles the incubator. - * @author Vexia - */ -@Initializable -public class IncubatorPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.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/java/core/game/interaction/item/withobject/KaramajaBananaCrate.java b/Server/src/main/java/core/game/interaction/item/withobject/KaramajaBananaCrate.java deleted file mode 100644 index f568af267..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/KaramajaBananaCrate.java +++ /dev/null @@ -1,76 +0,0 @@ -package core.game.interaction.item.withobject; - -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.Initializable; -import core.plugin.Plugin; - -/** - * Represents the karamaja banna crate. - * @author 'Vexia - * @date 29/11/2013 - */ -@Initializable -public final class KaramajaBananaCrate extends UseWithHandler { - - /** - * Represents the banana item. - */ - private static final Item BANANA = new Item(1963); - - /** - * Represents the filling animation. - */ - private static final Animation ANIMATION = new Animation(832); - - /** - * Represents the cap of bannas to add. - */ - private static final int CAP = 10; - - /** - * Constructs a new {@code KaramajaBananaCrate} {@code Object}. - */ - public KaramajaBananaCrate() { - super(1963, 431); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2072, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getSavedData().getGlobalData().isLuthasTask() && event.getUsedItem().getId() == 431) { - if (player.getAttribute("stashed-rum", false)) { - player.getPacketDispatch().sendMessage("There's already some rum in here..."); - return true; - } - if (player.getInventory().remove(event.getUsedItem())) { - player.animate(ANIMATION); - player.getDialogueInterpreter().sendDialogue("You stash the rum in the crate."); - player.setAttribute("/save:stashed-rum", true); - } - return true; - } - final int current = player.getSavedData().getGlobalData().getKaramjaBananas(); - if (current == CAP) { - player.getPacketDispatch().sendMessage("The crate is already full."); - return true; - } - if (player.getInventory().remove(BANANA)) { - player.getDialogueInterpreter().sendDialogue("You pack a banana into the crate."); - player.getSavedData().getGlobalData().setKaramjaBannanas(current + 1); - player.animate(ANIMATION); - player.lock(2); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/PoisonFountainPlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/PoisonFountainPlugin.java deleted file mode 100644 index 6307f16b6..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/PoisonFountainPlugin.java +++ /dev/null @@ -1,77 +0,0 @@ -package core.game.interaction.item.withobject; - -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 rs09.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the poison fountain plugin. - * @author 'Vexia - * @date 24/12/2013 - */ -@Initializable -public class PoisonFountainPlugin extends UseWithHandler { - - /** - * Represents the searching animation. - */ - private static final Animation SEARCH_ANIM = new Animation(881); - - /** - * Represents the poisoned fish food. - */ - private static final Item POISONED_FOOD = new Item(274); - - /** - * Constructs a new {@code PoisonFountainPlugin} {@code Object}. - */ - public PoisonFountainPlugin() { - super(274); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(153, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getAttribute("piranhas-killed", false)) { - player.getPacketDispatch().sendMessage("The piranhas are dead already."); - return true; - } - if (player.getInventory().remove(POISONED_FOOD)) { - player.lock(); - player.setAttribute("/save:piranhas-killed", true); - player.animate(SEARCH_ANIM); - player.getPacketDispatch().sendMessage("You pour the poisoned fish food into the fountain."); - GameWorld.getPulser().submit(new Pulse(1) { - int counter = 0; - - @Override - public boolean pulse() { - switch (counter++) { - case 1: - player.getPacketDispatch().sendMessage("The piranhas start eating the food..."); - break; - case 2: - player.getPacketDispatch().sendMessage("... then die and float to the surface."); - player.unlock(); - return true; - } - return false; - } - }); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/PriestInPerilUsePlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/PriestInPerilUsePlugin.java deleted file mode 100644 index 0e712f0f0..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/PriestInPerilUsePlugin.java +++ /dev/null @@ -1,157 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.game.content.dialogue.FacialExpression; -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.game.node.entity.player.link.quest.Quest; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * @author 'Vexia - */ -@Initializable -public class PriestInPerilUsePlugin extends UseWithHandler { - private int[] monumentIds = new int[] { 3499, 3493, 3494, 3497, 3495, 3498, 3496 }; - /** - * Constructs a new {@code PriestInPerilUsePlugin.java} {@code Object}. - */ - public PriestInPerilUsePlugin() { - super(2944, 1925, 2945, 2954, 2347, 1733, 1931, 314, 36, 590); - } - - /** - * (non-Javadoc) - * @see Plugin#newInstance(Object) - */ - @Override - public Plugin newInstance(Object arg) throws Throwable { - for(int i = 0; i < monumentIds.length; i++){ - addHandler(monumentIds[i], OBJECT_TYPE, this); - } - addHandler(3485, OBJECT_TYPE, this); - addHandler(3463, OBJECT_TYPE, this); - addHandler(30728, OBJECT_TYPE, this); - return this; - } - - /** - * (non-Javadoc) - * @see UseWithHandler#handle(NodeUsageEvent) - */ - @Override - public boolean handle(NodeUsageEvent event) { - Player player = event.getPlayer(); - int objUse = ((Scenery) event.getUsedWith()).getId(); - int itemUse = ((Item) event.getUsedItem()).getId(); - switch(objUse) { - case 3499: - if (!event.getPlayer().getGameAttributes().getAttributes().containsKey("priest_in_peril:key") && event.getPlayer().getInventory().remove(new Item(2944))) { - event.getPlayer().getInventory().add(new Item(2945)); - event.getPlayer().getPacketDispatch().sendMessage("You swap the Golden key for the Iron key."); - event.getPlayer().getGameAttributes().setAttribute("/save:priest_in_peril:key", true); - } else { - return true; - } - break; - case 3493: - if(!player.getGameAttributes().getAttributes().containsKey("priest_in_peril:tinderbox")){ - if (itemUse == 590) { - if (event.getPlayer().getInventory().remove(new Item(590))) { - event.getPlayer().getInventory().add(new Item(2946)); - event.getPlayer().getPacketDispatch().sendMessage("You swap the tinderbox for the golden tinderbox."); - event.getPlayer().getGameAttributes().setAttribute("/save:priest_in_peril:tinderbox", true); - } - } - } - break; - case 3494: - if(!player.getGameAttributes().getAttributes().containsKey("priest_in_peril:candle")){ - if (itemUse == 36) { - if (event.getPlayer().getInventory().remove(new Item(36))) { - event.getPlayer().getInventory().add(new Item(2947)); - event.getPlayer().getPacketDispatch().sendMessage("You swap the candle for the golden candle."); - event.getPlayer().getGameAttributes().setAttribute("/save:priest_in_peril:candle", true); - } - } - } - break; - case 3497: - if(!player.getGameAttributes().getAttributes().containsKey("priest_in_peril:feather")){ - if (itemUse == 314) { - if (event.getPlayer().getInventory().remove(new Item(314))) { - event.getPlayer().getInventory().add(new Item(2950)); - event.getPlayer().getPacketDispatch().sendMessage("You swap the feather for the golden feather."); - event.getPlayer().getGameAttributes().setAttribute("/save:priest_in_peril:feather", true); - } - } - } - break; - case 3495: - if(!player.getGameAttributes().getAttributes().containsKey("priest_in_peril:pot")){ - if (itemUse == 1931) { - if (event.getPlayer().getInventory().remove(new Item(1931))) { - event.getPlayer().getInventory().add(new Item(2948)); - event.getPlayer().getPacketDispatch().sendMessage("You swap the empty pot for the golden pot."); - event.getPlayer().getGameAttributes().setAttribute("/save:priest_in_peril:pot", true); - } - } - } - break; - case 3498: - if(!player.getGameAttributes().getAttributes().containsKey("priest_in_peril:needle")){ - if (itemUse == 1733) { - if (event.getPlayer().getInventory().remove(new Item(1733))) { - event.getPlayer().getInventory().add(new Item(2951)); - event.getPlayer().getPacketDispatch().sendMessage("You swap the needle for the golden needle."); - event.getPlayer().getGameAttributes().setAttribute("/save:priest_in_peril:needle", true); - } - } - } - break; - case 3496: - if(!player.getGameAttributes().getAttributes().containsKey("priest_in_peril:hammer")){ - if (itemUse == 2347) { - if (event.getPlayer().getInventory().remove(new Item(2347))) { - event.getPlayer().getInventory().add(new Item(2949)); - event.getPlayer().getPacketDispatch().sendMessage("You swap the hammer for the golden hammer."); - event.getPlayer().getGameAttributes().setAttribute("/save:priest_in_peril:hammer", true); - } - } - } - break; - case 3485: { - if (event.getPlayer().getInventory().remove(new Item(1925))) { - event.getPlayer().getInventory().add(new Item(2953)); - event.getPlayer().getPacketDispatch().sendMessage("You fill the bucket from the well."); - } - } - break; - case 3463: { - if (player.getInventory().remove(new Item(2945))) { - Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); - quest.setStage(player, 15); - player.getPacketDispatch().sendMessage("You have unlocked the cell door."); - NPC npc = NPC.create(7690, player.getLocation()); - npc.setName("Drezel"); - player.getDialogueInterpreter().sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh! Thank you! You have found the key!"); - } - } - break; - case 30728: { - if (player.getInventory().remove(new Item(2954))) { - player.getInventory().add(new Item(1925)); - Quest quest = player.getQuestRepository().getQuest("Priest in Peril"); - quest.setStage(player, 16); - player.getPacketDispatch().sendMessage("You pour the blessed water over the coffin..."); - } - } - break; - } - return true; - } -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/SandSourcePlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/SandSourcePlugin.java deleted file mode 100644 index a1e8fc329..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/SandSourcePlugin.java +++ /dev/null @@ -1,83 +0,0 @@ -package core.game.interaction.item.withobject; - -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.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the sand source plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SandSourcePlugin extends UseWithHandler { - - /** - * Represents the ids to use. - */ - private static final int[] IDS = new int[] { 2645, 10814 }; - - /** - * Represents the bucket to use. - */ - private static final Item BUCKET = new Item(1925); - - /** - * Represents the sand item. - */ - private static final Item SAND = new Item(1783); - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(832); - - /** - * Constructs a new {@code SandSourcePlugin} {@code Object}. - */ - public SandSourcePlugin() { - super(1925); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i : IDS) { - addHandler(i, OBJECT_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - handle(event.getPlayer()); - return true; - } - - /** - * Method used to handle the interaction. - * @param player the player. - */ - public final void handle(final Player player) { - player.getPulseManager().run(new Pulse(2, player) { - @Override - public boolean pulse() { - if (player.getInventory().remove(BUCKET)) { - player.animate(ANIMATION); - player.getPacketDispatch().sendMessage("You fill the bucket with sand."); - player.getInventory().add(SAND); - } - return !player.getInventory().containsItem(BUCKET); - } - - @Override - public void stop() { - super.stop(); - } - }); - } -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/SapCollectPlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/SapCollectPlugin.java deleted file mode 100644 index b1d1afc02..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/SapCollectPlugin.java +++ /dev/null @@ -1,102 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.cache.def.impl.ItemDefinition; -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.item.Item; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to collect sap. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SapCollectPlugin extends UseWithHandler { - - /** - * Represents the bucket of sap item. - */ - private static final Item BUCKET_OF_SAP = new Item(4687); - - /** - * Represents the bucket item. - */ - private static final Item BUCKET = new Item(1925); - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(2009); - - /** - * Represents the tree ids. - */ - private static final int[] IDS = new int[] { 1276, 1277, 1278, 1279, 1280, 1315, 1316, 1318, 1319, 1330, 1331, 1332, 3033, 3034, 3035, 3036, 3879, 3881, 3882, 3883, 10041, 14308, 14309, 30132, 30133, 37477, 37478, 37652 }; - - /** - * Constructs a new {@code SapCollectPlugin} {@code Object}. - */ - public SapCollectPlugin() { - super(946); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i : IDS) { - addHandler(i, OBJECT_TYPE, this); - } - new SapEmptyPlugin().newInstance(arg); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (!player.getInventory().remove(BUCKET)) { - player.getPacketDispatch().sendMessage("I need an empty bucket to collect the sap."); - return true; - } - player.animate(ANIMATION); - player.getInventory().remove(BUCKET); - player.getInventory().add(BUCKET_OF_SAP); - player.getPacketDispatch().sendMessage("You cut the tree and allow its sap to drip down into your bucket."); - return true; - } - - /** - * Represents the plugin used to empty a bucket of sap. - * @author 'Vexia - * @version 1.0 - */ - public static final class SapEmptyPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(4687).getHandlers().put("option:empty", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Item item = (Item) node; - if (item.getSlot() < 0) { - return false; - } - player.getInventory().replace(BUCKET, item.getSlot()); - player.getPacketDispatch().sendMessage("You empty the contents of the bucket on the floor."); - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - } -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/SpiritShieldBlessPlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/SpiritShieldBlessPlugin.java deleted file mode 100644 index ca02c3f2e..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/SpiritShieldBlessPlugin.java +++ /dev/null @@ -1,116 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.game.content.dialogue.DialogueAction; -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 rs09.game.world.repository.Repository; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; -import core.plugin.Initializable; -import rs09.plugin.PluginManager; - -/** - * Handles the blessing of Spirit shields. - * @author Splinter - */ -@Initializable -public class SpiritShieldBlessPlugin extends UseWithHandler { - - /** - * Constructs a new {@code SpiritShieldMakePlugin} {@code Object} - */ - public SpiritShieldBlessPlugin() { - super(13754, 13734); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(24343, OBJECT_TYPE, this); - PluginManager.definePlugin(new SpiritShieldMakePlugin()); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if(!player.getInventory().containItems(13754, 13734)){ - player.sendMessage("You need a holy elixir and an unblessed spirit shield in order to do this."); - return true; - } - if(player.getSkills().getLevel(Skills.PRAYER) < 85){ - player.sendMessage("You need a Prayer level of 85 in order to bless the shield."); - return true; - } - if(player.getInventory().remove(new Item(13754, 1), new Item(13734, 1))){ - player.getInventory().add(new Item(13736, 1)); - player.sendMessage("You successfully bless the shield using the holy elixir."); - } - return true; - } - - /** - * Handles the attaching of sigils to the blessed spirit shield. - * @author Splinter - */ - public class SpiritShieldMakePlugin extends UseWithHandler { - - /** - * Constructs a new {@code SpiritShieldMakePlugin} {@code Object} - */ - public SpiritShieldMakePlugin() { - super(13746, 13748, 13750, 13752); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(2782, OBJECT_TYPE, this); - addHandler(2783, OBJECT_TYPE, this); - addHandler(4306, OBJECT_TYPE, this); - addHandler(6150, OBJECT_TYPE, this); - addHandler(22725, OBJECT_TYPE, this); - addHandler(26817, OBJECT_TYPE, this); - addHandler(37622, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Item used = event.getUsedItem(); - final Item result = new Item(used.getId() - 8, 1); - if(used.getId() > 13752 || used.getId() < 13746){ - return true; - } - if(!player.getInventory().containsItem(new Item(13736))){ - player.sendMessage("You need a blessed spirit shield in order to continue."); - return true; - } - if(player.getSkills().getLevel(Skills.PRAYER) < 90 || player.getSkills().getLevel(Skills.SMITHING) < 85){ - player.sendMessage("You need a Prayer level of 90 and a Smithing of 85 in order to attach the sigil."); - return true; - } - player.getDialogueInterpreter().sendOptions("Combine the two?", "Yes", "No"); - player.getDialogueInterpreter().addAction(new DialogueAction() { - - @Override - public void handle(Player player, int buttonId) { - switch (buttonId) { - case 2: - if(player.getInventory().remove(used, new Item(13736, 1))){ - player.getInventory().add(result); - player.animate(new Animation(898)); - player.getDialogueInterpreter().sendItemMessage(result.getId(), "You successfully attach the "+used.getName().toLowerCase()+" to the blessed", "spirit shield."); - Repository.sendNews(player.getUsername()+" has just made the "+result.getName()+"."); - } - break; - } - } - - }); - return true; - } - } -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/SwampHoleRopePlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/SwampHoleRopePlugin.java deleted file mode 100644 index 9496efc4c..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/SwampHoleRopePlugin.java +++ /dev/null @@ -1,48 +0,0 @@ -package core.game.interaction.item.withobject; - -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 swamp hole rope plugin. - * @author 'Vexia - * @date 01/12/2013 - */ -@Initializable -public class SwampHoleRopePlugin extends UseWithHandler { - - /** - * Represents the rope item. - */ - private static final Item ROPE = new Item(954); - - /** - * Constructs a new {@code SwampHoleRopePlugin.java} {@code Object}. - */ - public SwampHoleRopePlugin() { - super(954); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(5947, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - if (!event.getPlayer().getSavedData().getGlobalData().hasTiedLumbridgeRope()) { - if (event.getPlayer().getInventory().remove(ROPE)) { - event.getPlayer().getDialogueInterpreter().sendItemMessage(954, "You tie the rope to the top of the entrance and throw it down."); - event.getPlayer().getSavedData().getGlobalData().setLumbridgeRope(true); - } - } else { - event.getPlayer().getDialogueInterpreter().sendDialogue("There is already a rope tied to the entrance."); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/TutorialItemHandler.java b/Server/src/main/java/core/game/interaction/item/withobject/TutorialItemHandler.java deleted file mode 100644 index 6d87919c9..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/TutorialItemHandler.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.interaction.item.withobject; - -import core.plugin.Initializable; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.smithing.smelting.Bar; -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 rs09.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; - -/** - * @author 'Vexia - */ -@Initializable -public class TutorialItemHandler extends UseWithHandler { - public TutorialItemHandler() { - super(438, 436); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(3044, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getInventory().containItems(438, 436)) { - player.animate(new Animation(833)); - GameWorld.getPulser().submit(new Pulse(2) { - @Override - public boolean pulse() { - player.getInventory().remove(new Item(438, 1)); - player.getInventory().remove(new Item(436, 1)); - player.getInventory().add(Bar.BRONZE.getProduct()); - player.getSkills().addExperience(Skills.SMITHING, Bar.BRONZE.getExperience()); - if (TutorialSession.getExtension(player).getStage() == 39) { - TutorialStage.load(player, 40, false); - } - return true; - } - - }); - - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/item/withobject/WaterSkinHandler.java b/Server/src/main/java/core/game/interaction/item/withobject/WaterSkinHandler.java deleted file mode 100644 index ed23cd866..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/WaterSkinHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -package core.game.interaction.item.withobject; - -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.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -@Initializable -public final class WaterSkinHandler extends UseWithHandler { - - /** - * Represents objects with water in them. - */ - private static final int[] OBJECTS = new int[]{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, 13561, 13563, 13559, 40063}; - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(832); - - // Unfilled waterskin objects - private static final int[] WATERSKINS = new int[]{1831, 1829, 1827, 1825}; - - private static final int FILLED_WATERSKIN = 1823; - - // Waterskin fill diag - private static final String WATERSKIN_TEXT = "You fill the waterskin."; - - /** - * Constructs a new {@code FillBucketPlugin} {@code Object}. - */ - public WaterSkinHandler() { - super(WATERSKINS); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i : OBJECTS) { - addHandler(i, OBJECT_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - Player player = event.getPlayer(); - player.getPulseManager().run(new Pulse(2, player) { - @Override - public boolean pulse() { - int curSkin = -1; - for (int i = 0; i < WATERSKINS.length; i++) { - if (player.getInventory().contains(WATERSKINS[i], 1)) { - curSkin = WATERSKINS[i]; - break; - } - } - if (curSkin == -1) { - return true; - } - if (player.getInventory().remove(new Item(curSkin))) { - player.animate(ANIMATION); - player.getPacketDispatch().sendMessage(WATERSKIN_TEXT); - player.getInventory().add(new Item(FILLED_WATERSKIN)); - } - return false; - } - - @Override - public void stop() { - super.stop(); - } - }); - return true; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/item/withobject/WaterSourcePlugin.java b/Server/src/main/java/core/game/interaction/item/withobject/WaterSourcePlugin.java deleted file mode 100644 index 11867b09c..000000000 --- a/Server/src/main/java/core/game/interaction/item/withobject/WaterSourcePlugin.java +++ /dev/null @@ -1,209 +0,0 @@ -package core.game.interaction.item.withobject; - -import api.ContentAPI; -import core.plugin.Initializable; -import org.rs09.consts.Items; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -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.update.flag.context.Animation; -import core.plugin.Plugin; - -/** - * Represents the plugin used to fill a bucket. - * @author 'Vexia - * @version 1.5 - */ -@Initializable -public final class WaterSourcePlugin extends UseWithHandler { - - /** - * Represents the objects to use the buckets on. - */ - private static final int[] OBJECTS = new int[] { 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 }; - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(832); - - /** - * Constructs a new {@code FillBucketPlugin} {@code Object}. - */ - public WaterSourcePlugin() { - super(WaterRecipient.getIds()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i : OBJECTS) { - if(ContentAPI.sceneryDefinition(i).getName().toLowerCase().contains("well")){ - continue; - } - addHandler(i, OBJECT_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final WaterRecipient recipient = WaterRecipient.forItem(event.getUsedItem()); - recipient.handle(event.getUsedWith().asScenery(), event.getPlayer()); - return true; - } - - /** - * Represents a water recipient. - * @author 'Vexia - * @version 1.0 - */ - public enum WaterRecipient { - BUCKET(new Item(1925), new Item(1929), "You fill the bucket with water."), - VIAL(new Item(229), new Item(227)), - JUG(new Item(1935), new Item(1937)), - BOWL(new Item(1923), new Item(1921)), - CLAY(new Item(434), new Item(1761), "You mix the clay and water. You now have some soft, workable clay."), - WATERING_CAN(new Item(5331), new Item(5340), "You fill the watering can."), - WATERING_CAN7(new Item(5339), new Item(5340), "You fill the watering can."), - WATERING_CAN6(new Item(5338), new Item(5340), "You fill the watering can."), - WATERING_CAN5(new Item(5337), new Item(5340), "You fill the watering can."), - WATERING_CAN4(new Item(5336), new Item(5340), "You fill the watering can."), - WATERING_CAN3(new Item(5335), new Item(5340), "You fill the watering can."), - WATERING_CAN2(new Item(5334), new Item(5340), "You fill the watering can."), - WATERING_CAN1(new Item(5333), new Item(5340), "You fill the watering can."), - WATER_SKIN0(new Item(1831), new Item(1823), "You fill the waterskin."), - WATER_SKIN1(new Item(1829),new Item(1823),"You fill the waterskin."), - WATER_SKIN2(new Item(1827), new Item(1823), "You fill the waterskin."), - FISHBOWL(new Item(Items.FISHBOWL_6667), new Item(Items.FISHBOWL_6668)); - - /** - * Represents the required item. - */ - private final Item required; - - /** - * Represents the product item. - */ - private final Item product; - - /** - * Represents the message to display. - */ - private final String message; - - /** - * Constructs a new {@code WaterSourcePlugin} {@code Object}. - * @param required the required item. - * @param product the product item. - * @param message the message item. - */ - WaterRecipient(final Item required, final Item product, final String message) { - this.required = required; - this.product = product; - this.message = message; - } - - /** - * Constructs a new {@code WaterSourcePlugin} {@code Object}. - * @param required the required item. - * @param product the product. - */ - WaterRecipient(final Item required, final Item product) { - this(required, product, "You fill the " + required.getName().toLowerCase() + " with water."); - } - - /** - * Method used to handle the interaction. - * @param player the player. - */ - public final void handle(final Scenery object, final Player player) { - if (object.getId() == 11661 - && required.getId() == Items.BUCKET_1925 - && !player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).isComplete(0,7)) { - player.getPulseManager().run(new Pulse(2, player) { - @Override - public boolean pulse() { - if (player.getInventory().remove(getRequired())) { - player.animate(ANIMATION); - player.getPacketDispatch().sendMessage(getMessage()); - player.getInventory().add(getProduct()); - player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).updateTask(player, 0, 7, true); - } - return !player.getInventory().containsItem(getRequired()); - } - }); - } else { - player.getPulseManager().run(new Pulse(1, player) { - @Override - public boolean pulse() { - if (player.getInventory().remove(getRequired())) { - player.animate(ANIMATION); - player.getPacketDispatch().sendMessage(getMessage()); - player.getInventory().add(getProduct()); - } - return !player.getInventory().containsItem(getRequired()); - } - - @Override - public void stop() { - super.stop(); - } - }); - } - } - - /** - * Gets the required. - * @return The required. - */ - public Item getRequired() { - return required; - } - - /** - * Gets the product. - * @return The product. - */ - public Item getProduct() { - return product; - } - - /** - * Gets the message. - * @return The message. - */ - public String getMessage() { - return message; - } - - /** - * Gets the recipient ids. - * @return the ids. - */ - public static int[] getIds() { - int[] ids = new int[values().length]; - for (int i = 0; i < ids.length; i++) { - ids[i] = values()[i].getRequired().getId(); - } - return ids; - } - - /** - * Gets the water recipient for the item. - * @param item the item. - * @return the {@code WaterRecipient} {@code Object}. - */ - public static WaterRecipient forItem(final Item item) { - for (WaterRecipient recipient : values()) { - if (recipient.getRequired().getId() == item.getId()) { - return recipient; - } - } - return null; - } - } -} diff --git a/Server/src/main/java/core/game/interaction/npc/AlKahridLeafletPlugin.java b/Server/src/main/java/core/game/interaction/npc/AlKahridLeafletPlugin.java deleted file mode 100644 index 452d6ceee..000000000 --- a/Server/src/main/java/core/game/interaction/npc/AlKahridLeafletPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.interaction.npc; - -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 for ali the leaflet npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class AlKahridLeafletPlugin extends OptionHandler { - - @Override - public boolean handle(Player player, Node node, String option) { - player.getDialogueInterpreter().open(3680, node, true); - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(3680).getHandlers().put("option:take-flyer", this); - return this; - } - -} diff --git a/Server/src/main/java/core/game/interaction/npc/AlkharidHealPlugin.java b/Server/src/main/java/core/game/interaction/npc/AlkharidHealPlugin.java deleted file mode 100644 index 27feab0ce..000000000 --- a/Server/src/main/java/core/game/interaction/npc/AlkharidHealPlugin.java +++ /dev/null @@ -1,35 +0,0 @@ -package core.game.interaction.npc; - -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 the plugin used for the npcs in alkharid which can heal you. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class AlkharidHealPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(962).getHandlers().put("option:heal", this); - NPCDefinition.forId(961).getHandlers().put("option:heal", this); - NPCDefinition.forId(960).getHandlers().put("option:heal", this); - NPCDefinition.forId(959).getHandlers().put("option:heal", this); - NPCDefinition.forId(958).getHandlers().put("option:heal", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getDialogueInterpreter().open(((NPC) node).getId(), ((NPC) node)); - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/npc/BurthorpeOptionPlugin.java b/Server/src/main/java/core/game/interaction/npc/BurthorpeOptionPlugin.java deleted file mode 100644 index 3b2c0fd76..000000000 --- a/Server/src/main/java/core/game/interaction/npc/BurthorpeOptionPlugin.java +++ /dev/null @@ -1,121 +0,0 @@ -package core.game.interaction.npc; - -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -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.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the optione plugin to handle interactions with 'nodes' in - * burthorpe. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BurthorpeOptionPlugin extends OptionHandler { - - /** - * Represents the thieving guide location. - */ - private static final Location GUIDE_LOCATION = new Location(3061, 4985, 1); - - /** - * Represents the bar location. - */ - private static final Location BAR_LOCATION = new Location(2906, 3537, 0); - - @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); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final int id = node instanceof NPC ? ((NPC) node).getId() : ((Scenery) node).getId(); - switch (option) { - case "climb-down": - switch (id) { - case 4624: - ClimbActionHandler.climb(player, Animation.create(828), 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)); - break; - } - break; - case "enter": - switch (id) { - case 7257: - player.getProperties().setTeleportLocation(GUIDE_LOCATION); - break; - case 7258: - player.getProperties().setTeleportLocation(BAR_LOCATION); - 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/java/core/game/interaction/npc/CanafisWereWolfPlugin.java b/Server/src/main/java/core/game/interaction/npc/CanafisWereWolfPlugin.java deleted file mode 100644 index d0388a626..000000000 --- a/Server/src/main/java/core/game/interaction/npc/CanafisWereWolfPlugin.java +++ /dev/null @@ -1,67 +0,0 @@ -package core.game.interaction.npc; - -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 rs09.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.getPulseManager().clear("interaction:attack:" + node.hashCode()); - player.getProperties().getCombatPulse().attack(node); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/npc/GliderPlugin.java b/Server/src/main/java/core/game/interaction/npc/GliderPlugin.java deleted file mode 100644 index 867853fcd..000000000 --- a/Server/src/main/java/core/game/interaction/npc/GliderPlugin.java +++ /dev/null @@ -1,37 +0,0 @@ -package core.game.interaction.npc; - -import core.cache.def.impl.NPCDefinition; -import core.game.component.Component; -import core.game.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; - -/** - * Represents the plugin used for gliders. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GliderPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(3809).getHandlers().put("option:glider", this); - NPCDefinition.forId(3810).getHandlers().put("option:glider", this); - NPCDefinition.forId(3811).getHandlers().put("option:glider", this); - NPCDefinition.forId(3812).getHandlers().put("option:glider", this); - NPCDefinition.forId(3813).getHandlers().put("option:glider", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getInterfaceManager().open(new Component(138)); - Gliders.sendConfig(node.asNpc(), player); - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/npc/NPCTradePlugin.kt b/Server/src/main/java/core/game/interaction/npc/NPCTradePlugin.kt deleted file mode 100644 index 8a2930716..000000000 --- a/Server/src/main/java/core/game/interaction/npc/NPCTradePlugin.kt +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.interaction.npc - -import api.ContentAPI -import core.cache.def.impl.NPCDefinition -import core.game.component.Component -import core.plugin.Initializable -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 -import core.game.node.entity.skill.crafting.TanningProduct -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener - -/** - * Represents the plugin used for an npc with the trade option. - * @author Ceikry - * @version 1.0 - */ -class NPCTradePlugin : InteractionListener() { - override fun defineListeners() { - on(NPC, "trade", "shop"){player, node -> - val npc = node as NPC - if (npc.id == 2824) { - TanningProduct.open(player, 2824) - return@on true - } - if (npc.id == 7601) { - ContentAPI.openInterface(player, 732) - return@on true - } - return@on npc.openShop(player) - } - - on(NPCs.SIEGFRIED_ERKLE_933, NPC, "trade"){player, node -> - val points = ContentAPI.getQP(player) - if(points < 40){ - ContentAPI.sendNPCDialogue(player, NPCs.SIEGFRIED_ERKLE_933, "I'm sorry, adventurer, but you need 40 quest points to buy from me.") - return@on true - } - node.asNpc().openShop(player) - return@on true - } - } - - override fun defineDestinationOverrides() { - setDest(NPC,"trade","shop"){_,node -> - val npc = node as NPC - if (npc.getAttribute("facing_booth", false)) { - val offsetX = npc.direction.stepX shl 1 - val offsetY = npc.direction.stepY shl 1 - return@setDest npc.location.transform(offsetX, offsetY, 0) - } - return@setDest node.location - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/npc/RoavarDialogue.java b/Server/src/main/java/core/game/interaction/npc/RoavarDialogue.java deleted file mode 100644 index 5051340ba..000000000 --- a/Server/src/main/java/core/game/interaction/npc/RoavarDialogue.java +++ /dev/null @@ -1,148 +0,0 @@ -package core.game.interaction.npc; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.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/java/core/game/interaction/npc/SawmillOptionPlugin.java b/Server/src/main/java/core/game/interaction/npc/SawmillOptionPlugin.java deleted file mode 100644 index 776989ec6..000000000 --- a/Server/src/main/java/core/game/interaction/npc/SawmillOptionPlugin.java +++ /dev/null @@ -1,48 +0,0 @@ -package core.game.interaction.npc; - -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/java/core/game/interaction/npc/SeamanPlugin.java b/Server/src/main/java/core/game/interaction/npc/SeamanPlugin.java deleted file mode 100644 index 5dd786ef2..000000000 --- a/Server/src/main/java/core/game/interaction/npc/SeamanPlugin.java +++ /dev/null @@ -1,31 +0,0 @@ -package core.game.interaction.npc; - -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/java/core/game/interaction/npc/SecurityGuardPlugin.java b/Server/src/main/java/core/game/interaction/npc/SecurityGuardPlugin.java deleted file mode 100644 index 8b8ca8cdf..000000000 --- a/Server/src/main/java/core/game/interaction/npc/SecurityGuardPlugin.java +++ /dev/null @@ -1,66 +0,0 @@ -package core.game.interaction.npc; - -import core.game.content.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.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; -import org.rs09.consts.Items; - -@Initializable -public final class SecurityGuardPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new SecurityGuardDialogue()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (option.equals("talk-to")) { - player.getDialogueInterpreter().open(4375); - } - return true; - } - - public final class SecurityGuardDialogue extends DialoguePlugin { - - public SecurityGuardDialogue() {} - - public SecurityGuardDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SecurityGuardDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (player.getInventory().hasSpaceFor(new Item(Items.SECURITY_BOOK_9003))) { - npc("Here's a Security book for you.", "Have a nice day."); - player.getInventory().add(new Item(Items.SECURITY_BOOK_9003)); - } else { - npc("Have a nice day."); - } - return false; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - return false; - } - - @Override - public int[] getIds() { - return new int[] { 4375 }; - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/npc/SheepShearPlugin.java b/Server/src/main/java/core/game/interaction/npc/SheepShearPlugin.java deleted file mode 100644 index 11e698970..000000000 --- a/Server/src/main/java/core/game/interaction/npc/SheepShearPlugin.java +++ /dev/null @@ -1,81 +0,0 @@ -package core.game.interaction.npc; - -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 rs09.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/java/core/game/interaction/npc/ShooAwayStrayDogPlugin.java b/Server/src/main/java/core/game/interaction/npc/ShooAwayStrayDogPlugin.java deleted file mode 100644 index 010c4be9f..000000000 --- a/Server/src/main/java/core/game/interaction/npc/ShooAwayStrayDogPlugin.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.game.interaction.npc; - -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/java/core/game/interaction/npc/SpiritGraahkOptionPlugin.java b/Server/src/main/java/core/game/interaction/npc/SpiritGraahkOptionPlugin.java deleted file mode 100644 index 43b4977f5..000000000 --- a/Server/src/main/java/core/game/interaction/npc/SpiritGraahkOptionPlugin.java +++ /dev/null @@ -1,29 +0,0 @@ -package core.game.interaction.npc; - -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/java/core/game/interaction/npc/TanningNPC.java b/Server/src/main/java/core/game/interaction/npc/TanningNPC.java deleted file mode 100644 index 36e7a1ce7..000000000 --- a/Server/src/main/java/core/game/interaction/npc/TanningNPC.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.interaction.npc; - -import core.cache.def.impl.NPCDefinition; -import core.plugin.Initializable; -import core.game.node.entity.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/java/core/game/interaction/npc/ZahurOptionPlugin.java b/Server/src/main/java/core/game/interaction/npc/ZahurOptionPlugin.java deleted file mode 100644 index 499ef0515..000000000 --- a/Server/src/main/java/core/game/interaction/npc/ZahurOptionPlugin.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.interaction.npc; - -import core.cache.def.impl.NPCDefinition; -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; - -import core.game.node.entity.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); - PluginManager.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/java/core/game/interaction/npc/bob/BobDialogue.java b/Server/src/main/java/core/game/interaction/npc/bob/BobDialogue.java deleted file mode 100644 index dabc11ebb..000000000 --- a/Server/src/main/java/core/game/interaction/npc/bob/BobDialogue.java +++ /dev/null @@ -1,574 +0,0 @@ -package core.game.interaction.npc.bob; - -import core.cache.def.impl.ItemDefinition; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.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; - -/** - * 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 AchievementDiary 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 (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE); - } - if (diary.isComplete(level, true) && !diary.isLevelRewarded(level)) { - player("I've done all the medium tasks in my Lumbridge", "Achievement Diary."); - stage = 150; - break; - } - if (diary.isLevelRewarded(1) && diary.isComplete(level, true) && !player.hasItem(diary.getType().getRewards(level)[0])) { - 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: - diary.setLevelRewarded(level); - for (Item i : diary.getType().getRewards(level)) { - player.getInventory().add(i, player); - } - 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: - player.getInventory().add(diary.getType().getRewards(level)[0], player); - 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[] 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 * (item.getCharge() * 0.001)); - 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; - } - } - - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/npc/bob/BobRepairItem.java b/Server/src/main/java/core/game/interaction/npc/bob/BobRepairItem.java deleted file mode 100644 index 2cfcd22ba..000000000 --- a/Server/src/main/java/core/game/interaction/npc/bob/BobRepairItem.java +++ /dev/null @@ -1,47 +0,0 @@ -package core.game.interaction.npc.bob; - -import core.game.content.global.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 rs09.plugin.PluginManager; - -/** - * 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); - PluginManager.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/java/core/game/interaction/object/BankingPlugin.java b/Server/src/main/java/core/game/interaction/object/BankingPlugin.java deleted file mode 100644 index e7bf7f8db..000000000 --- a/Server/src/main/java/core/game/interaction/object/BankingPlugin.java +++ /dev/null @@ -1,667 +0,0 @@ -package core.game.interaction.object; - -import api.ContentAPI; -import core.cache.def.impl.NPCDefinition; -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.container.access.InterfaceContainer; -import core.game.content.dialogue.DialogueAction; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -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.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.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 kotlin.Unit; -import rs09.game.content.dialogue.DumpContainer; -import rs09.game.ge.GrandExchangeOffer; -import rs09.game.world.GameWorld; - -import java.text.NumberFormat; - -/** - * Represents the plugin used for anything related to banking. - * - * @author Vexia - * @author Emperor - */ -@Initializable -public final class BankingPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.setOptionHandler("use-quickly", this); - SceneryDefinition.setOptionHandler("use", this); - SceneryDefinition.setOptionHandler("bank", this); - SceneryDefinition.setOptionHandler("collect", this); - SceneryDefinition.setOptionHandler("deposit", this); - new BankingInterface().newInstance(arg); - new BankDepositInterface().newInstance(arg); - new BankNPCPlugin().newInstance(arg); - new BankNPC().newInstance(arg); - new BankerDialogue().init(); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final Scenery object = (Scenery) node; - if (player.getIronmanManager().checkRestriction(IronmanMode.ULTIMATE)) { - return true; - } - if (object.getName().contains("Bank") || object.getName().contains("Deposit")) { - switch (option) { - case "use": -// final Location l = object.getLocation(); -// final Location p = player.getLocation(); -// final NPC npc = Repository.findNPC(l.transform(l.getX() - p.getX(), l.getY() - p.getY(), 0)); -// if (node.getId() == 4483) { -// player.getBank().open(); -// return true; -// } -// if (npc != null && DialogueInterpreter.contains(npc.getId())) { -// npc.faceLocation(node.getLocation()); -// player.getDialogueInterpreter().open(npc.getId(), npc.getId()); -// } else { -// player.getDialogueInterpreter().open(494); -// } -// return true; - case "use-quickly": - case "bank": - player.getBankPinManager().openType(1); - checkAchievements(player); - return true; - case "collect": - player.getPlayerGrandExchange().openCollectionBox(); - return true; - case "deposit": - openDepositBox(player); - return true; - } - } - return true; - } - - /** - * Method used to open the deposit box. - * - * @param player the player. - */ - private void openDepositBox(final Player player) { - player.getInterfaceManager().open(new Component(11)).setCloseEvent(new CloseEvent() { - @Override - public boolean close(Player player, Component c) { - player.getInterfaceManager().openDefaultTabs(); - return true; - } - }); - //player.getInterfaceManager().closeDefaultTabs(); - player.getInterfaceManager().hideTabs(0, 1, 2, 3, 4, 5, 6); - InterfaceContainer.generateItems(player, player.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1",}, 11, 15, 5, 7); - } - - /** - * Represents the dialogue plugin used for all bankers. - * - * @author 'Vexia - * @version 1.0 - */ - public static final class BankerDialogue extends DialoguePlugin { - - /** - * Represents the banker npc ids. - */ - private static final int[] NPC_IDS = {44, 45, 166, 494, 495, 496, 497, 498, 499, 902, 1036, 1360, 1702, 2163, 2164, 2354, 2355, 2568, 2569, 2570, 2619, 3046, 3198, 3199, 3824, 4296, 4519, 5257, 5258, 5259, 5260, 5383, 5488, 5776, 5777, 5898, 5912, 5913, 6200, 6362, 6532, 6533, 6534, 6535, 6538, 7049, 7050, 7445, 7446, 7605}; - - /** - * Represents the id to use. - */ - private int id; - - /** - * Constructs a new {@code BankerDialogue} {@code Object}. - */ - public BankerDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BankerDialoguePlugin} {@code Object}. - * - * @param player the player. - */ - public BankerDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BankerDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (args[0] instanceof NPC) { - setId(((NPC) args[0]).getId()); - } else { - setId((int) args[0]); - } - if (TutorialSession.getExtension(player).getStage() < TutorialSession.MAX_STAGE) { - Component.setUnclosable(player, interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "Good day, would you like to access your bank account?")); - stage = 999; - return true; - } - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "Good day, How may I help you?"); - for (GrandExchangeOffer o : player.getPlayerGrandExchange().getOffers()) { - if (o != null && (o.getWithdraw()[0] != null || o.getWithdraw()[1] != null)) { - stage = -1; - break; - } - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case -1: - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "Before we go any further, I should inform you that you", "have items ready for collection from the Grand Exchange."); - stage = 0; - break; - case 0: - interpreter.sendOptions("What would you like to say?", "I'd like to access my bank account, please.", "I'd like to check my PIN settings.", "I'd like to see my collection box.", "What is this place?"); - stage = 10; - break; - case 1: - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "This is a branch of the Bank of " + GameWorld.getSettings().getName() + ". We have", "branches in many towns."); - stage = 2; - break; - case 2: - interpreter.sendOptions("What would you like to say?", "And what do you do?", "Didn't you used to be called the Bank of Varrock?"); - stage = 3; - break; - case 3: - if (buttonId == 1) { - player("And what do you do?"); - stage = 4; - } else if (buttonId == 2) { - player("Didnt you used to be called the Bank of Varrock?"); - stage = 5; - } - break; - case 4: - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "We will look after your items and money for you.", "Leave your valuables with us if you want to keep them", "safe."); - stage = 100; - break; - case 5: - interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "Yes we did, but people kept on coming into our", "signs were wrong. They acted as if we didn't know", "what town we were in or something."); - stage = 100; - break; - case 100: - end(); - break; - case 999: - options("Yes.", "No."); - stage = 1000; - break; - case 1000: - switch (buttonId) { - case 1: - end(); - if (TutorialSession.getExtension(player).getStage() == 56) { - player.getBank().add(new Item(995, 25)); - } - player.getBank().open(); - if (TutorialSession.getExtension(player).getStage() == 56) { - TutorialStage.load(player, 57, false); - } - break; - case 2: - end(); - TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), false); - break; - } - break; - case 10: - switch (interfaceId) { - case 232: - switch (buttonId) { - case 1: - case 2: - case 3: - player.getBankPinManager().openType(buttonId); - checkAchievements(player); - end(); - break; - case 4: - player("What is this place?"); - stage = 1; - break; - } - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return NPC_IDS; - } - - /** - * Gets the id. - * - * @return The id. - */ - public int getId() { - return id; - } - - /** - * Sets the id. - * - * @param id The id to set. - */ - public void setId(int id) { - this.id = id; - } - - } - - /** - * Represents the component plugin used to handle banking interfaces. - * - * @author Emperor - * @author 'Vexia - * @version 1.0 - */ - public static final class BankingInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(763, this); - ComponentDefinition.put(762, this); - ComponentDefinition.put(767, this); - return this; - } - - @Override - public void open(Player player, Component component) { - super.open(player, component); - player.getBank().sendBankSpace(); - } - - @Override - public boolean handle(final Player p, Component component, int opcode, int button, final int slot, int itemId) { - final Item item = component.getId() == 762 ? p.getBank().get(slot) : p.getInventory().get(slot); - switch (component.getId()) { - case 767: - switch (button) { - case 10: - p.getBank().open(); - break; - } - break; - case 762: - switch (button) { - case 18: - p.getDialogueInterpreter().open(new DumpContainer().getID()); - return true; - case 23: - p.getDialogueInterpreter().sendOptions("Select an Option", "Check bank value", "Banking assistance", "Close"); - p.getDialogueInterpreter().addAction(new DialogueAction() { - - @Override - public void handle(Player player, int buttonId) { - switch (buttonId) { - case 2: - p.getDialogueInterpreter().sendItemMessage(new Item(995, 50000), "
Your bank is worth approximately " + NumberFormat.getInstance().format(p.getBank().getWealth()) + " coins."); - break; - case 3: - p.getBank().close(); - p.getInterfaceManager().open(new Component(767)); - break; - case 4: - p.getDialogueInterpreter().close(); - break; - } - } - - }); - break; - case 14: - p.getBank().setInsertItems(!p.getBank().isInsertItems()); - return true; - case 16: - p.getBank().setNoteItems(!p.getBank().isNoteItems()); - return true; - case 73: - int amount = 0; - switch (opcode) { - case 155: - amount = 1; - break; - case 196: - amount = 5; - break; - case 124: - amount = 10; - break; - case 199: - amount = p.getBank().getLastAmountX(); - break; - case 234: - ContentAPI.sendInputDialogue(p, false, "Enter the amount:", (value) -> { - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - s = s.replace("m","000000"); - s = s.replace("M","000000"); - int val = Integer.parseInt(s); - p.getBank().takeItem(slot, val); - p.getBank().updateLastAmountX(val); - return Unit.INSTANCE; - }); - break; - case 9: - p.sendMessage(p.getBank().get(slot).getDefinition().getExamine()); - break; - case 168: - amount = p.getBank().getAmount(item); - break; - case 166: - amount = p.getBank().getAmount(item) - 1; - break; - default: - return false; - } - if (amount > 0) { - final int withdraw = amount; - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - if (item == null) { - return true; - } - p.getBank().takeItem(slot, withdraw); - return true; - } - }); - } - return true; - case 20://search - p.setAttribute("search", true); - break; - case 41: - case 39: - case 37: - case 35: - case 33: - case 31: - case 29: - case 27: - case 25: - if (p.getAttribute("search", false)) { - p.getBank().reopen(); - } - int tabIndex = -((button - 41) / 2); - switch (opcode) { - case 155: - p.getBank().setTabIndex(tabIndex); - return true; - case 196: - p.getBank().collapseTab(tabIndex); - return true; - } - return false; - } - break; - case 763:// overlay. - switch (opcode) { - case 155: - p.getBank().addItem(slot, 1); - break; - case 196: - p.getBank().addItem(slot, 5); - break; - case 124: - p.getBank().addItem(slot, 10); - break; - case 199: - p.getBank().addItem(slot, p.getBank().getLastAmountX()); - break; - case 234: - ContentAPI.sendInputDialogue(p, false, "Enter the amount:", (value) -> { - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - int val = Integer.parseInt(s); - p.getBank().addItem(slot, val); - p.getBank().updateLastAmountX(val); - return Unit.INSTANCE; - }); - break; - case 168: - p.getBank().addItem(slot, p.getInventory().getAmount(item)); - break; - } - break; - } - return true; - } - - } - - /** - * Represents the bank deposit interface handler. - * - * @author 'Vexia - * @version 1.0 - */ - public static final class BankDepositInterface extends ComponentPlugin { - - /** - * Represents the deposit animation. - */ - private static final Animation DEPOSIT_ANIMATION = new Animation(834); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(11, this); - return this; - } - - @Override - public boolean handle(final Player p, Component component, int opcode, int button, final int slot, int itemId) { - final Item item = p.getInventory().get(slot); - if (item == null && button != 15 && button != 13) { - return true; - } - switch (component.getId()) { - case 11: - p.animate(DEPOSIT_ANIMATION); - switch (opcode) { - case 155: // Deposit items - p.getBank().addItem(slot, 1); - break; - case 196: - p.getBank().addItem(slot, 5); - break; - case 124: - p.getBank().addItem(slot, 10); - break; - case 199: - p.getPulseManager().run(new Pulse(1, p) { - @Override - public boolean pulse() { - p.getBank().addItem(slot, p.getInventory().getAmount(item)); - InterfaceContainer.generateItems(p, p.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1",}, 11, 15, 5, 7); - return true; - } - }); - return true; - case 234: - ContentAPI.sendInputDialogue(p, false, "Enter the amount:", (value) -> { - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - int val = Integer.parseInt(s); - p.getBank().addItem(slot, val); - InterfaceContainer.generateItems(p, p.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1",}, 11, 15, 5, 7); - return Unit.INSTANCE; - }); - break; - case 168: - p.sendMessage(item.getDefinition().getExamine()); - break; - } - switch (button) { - case 13: - p.getFamiliarManager().dumpBob(); - break; - } - break; - } - InterfaceContainer.generateItems(p, p.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1",}, 11, 15, 5, 7); - return true; - } - - } - - /** - * Represents the plugin used to handle the banker npc. - * - * @author 'Vexia - * @author Emperor - * @version 1.01 - */ - public static final class BankNPCPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.setOptionHandler("bank", this); - NPCDefinition.setOptionHandler("collect", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final NPC npc = ((NPC) node); - npc.faceLocation(node.getLocation()); - if (option.equals("bank")) { - player.getBank().open(); - checkAchievements(player); - } else { - player.getPlayerGrandExchange().openCollectionBox(); - } - return true; - } - - @Override - public Location getDestination(Node n, Node node) { - NPC npc = (NPC) node; - if (npc.getAttribute("facing_booth", false)) { - Direction dir = npc.getDirection(); - return npc.getLocation().transform(dir.getStepX() << 1, dir.getStepY() << 1, 0); - } - if (npc.getId() == 6533) { - return Location.create(3167, 3489, 0);// ge bankers. - } else if (npc.getId() == 6534) { - return Location.create(3167, 3490, 0);// ge bankers. - } else if (npc.getId() == 6535) { - return Location.create(3162, 3489, 0); - } else if (npc.getId() == 6532) { - return Location.create(3162, 3489, 0); - } else if (npc.getId() == 4907) { - return npc.getLocation().transform(0, -2, 0); - } - return super.getDestination(npc, node); - } - } - - /** - * Represents the abstract npc of a banker. - * - * @author 'Vexia - * @author Emperor - * @version 1.2 - */ - public static final class BankNPC extends AbstractNPC { - - /** - * Represents the ids of this class. - */ - private final int[] IDS = new int[]{4907, 44, 45, 166, 494, 495, 496, 497, 498, 499, 902, 1036, 1360, 1702, 2163, 2164, 2354, 2355, 2568, 2569, 2570, 2619, 3046, 3198, 3199, 4296, 4519, 5257, 5258, 5259, 5260, 5383, 5488, 5776, 5777, 5898, 5912, 5913, 6200, 6362, 6532, 6533, 6534, 6535, 6538, 7049, 7050, 7445, 7446, 7605}; - - /** - * Constructs a new {@code BankNPC} {@code Object}. - */ - public BankNPC() { - super(0, null); - } - - @Override - public void init() { - super.init(); - for (int i = 0; i < 4; i++) { - Direction dir = Direction.get(i); - Location loc = getLocation().transform(dir.getStepX(), dir.getStepY(), 0); - Scenery bank = RegionManager.getObject(loc); - if (bank != null && bank.getName().equals("Bank booth")) { - setDirection(dir); - setAttribute("facing_booth", true); - super.setWalks(false); - break; - } - } - } - - /** - * Constructs a new {@code BankNPC} {@code Object}. - * - * @param id The NPC id. - * @param location The location. - */ - private BankNPC(int id, Location location) { - super(id, location); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new BankNPC(id, location); - } - - @Override - public int[] getIds() { - return IDS; - } - } - - private static void checkAchievements(Player player) { - // Access the bank in Draynor Village - if (player.getLocation().withinDistance(Location.create(3092, 3243, 0))) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 15); - } - } -} diff --git a/Server/src/main/java/core/game/interaction/object/BrassKeyDoorPlugin.java b/Server/src/main/java/core/game/interaction/object/BrassKeyDoorPlugin.java deleted file mode 100644 index b9336f110..000000000 --- a/Server/src/main/java/core/game/interaction/object/BrassKeyDoorPlugin.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.game.interaction.object; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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/java/core/game/interaction/object/CavadaBerriesPlugin.java b/Server/src/main/java/core/game/interaction/object/CavadaBerriesPlugin.java deleted file mode 100644 index 91220731f..000000000 --- a/Server/src/main/java/core/game/interaction/object/CavadaBerriesPlugin.java +++ /dev/null @@ -1,40 +0,0 @@ -package core.game.interaction.object; - -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.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle the cadava potion. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CavadaBerriesPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(756).getHandlers().put("option:look-at", this); - ItemDefinition.forId(756).getHandlers().put("option:drink", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (option.equals("drink")) { - player.getDialogueInterpreter().sendItemMessage(756, "You dare not drink."); - } - if (option.equals("look-at")) { - player.getDialogueInterpreter().sendItemMessage(756, "This looks very colourful."); - } - return true; - } - - @Override - public boolean isWalk() { - return false; - } -} diff --git a/Server/src/main/java/core/game/interaction/object/ChampionsArenaPlugin.java b/Server/src/main/java/core/game/interaction/object/ChampionsArenaPlugin.java deleted file mode 100644 index cf7df65f9..000000000 --- a/Server/src/main/java/core/game/interaction/object/ChampionsArenaPlugin.java +++ /dev/null @@ -1,37 +0,0 @@ -package core.game.interaction.object; - -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/java/core/game/interaction/object/ChampionsGuildDoor.java b/Server/src/main/java/core/game/interaction/object/ChampionsGuildDoor.java deleted file mode 100644 index 46df975ee..000000000 --- a/Server/src/main/java/core/game/interaction/object/ChampionsGuildDoor.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.interaction.object; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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.diary.DiaryType; -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 interaction with the champions guild - * door. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ChampionsGuildDoor extends OptionHandler { - - @Override - public boolean handle(Player player, Node node, String option) { - if (player.getLocation().getY() > 3362 && player.getQuestRepository().getPoints() < 32) { - player.getDialogueInterpreter().open(70099, "You have not proved yourself worthy to enter here yet."); - player.getPacketDispatch().sendMessage("The door won't open - you need at least 32 Quest Points."); - } else { - if (player.getLocation().getX() == 3191 && player.getLocation().getY() == 3363) { - player.getDialogueInterpreter().sendDialogues(198, null, "Greetings bold adventurer. Welcome to the guild of", "Champions."); - } - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 1); - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - return true; - } - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(1805).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/java/core/game/interaction/object/CulinomancerChestPliugin.java b/Server/src/main/java/core/game/interaction/object/CulinomancerChestPliugin.java deleted file mode 100644 index fa25b5715..000000000 --- a/Server/src/main/java/core/game/interaction/object/CulinomancerChestPliugin.java +++ /dev/null @@ -1,48 +0,0 @@ -package core.game.interaction.object; - -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.game.node.scenery.SceneryBuilder; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.plugin.Initializable; -import core.plugin.Plugin; -import rs09.game.system.config.ShopParser; - -/** - * Handles the culinomancer chest. - * @author Vexia - */ -@Initializable -public final class CulinomancerChestPliugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(12309).getHandlers().put("option:bank", this); - SceneryDefinition.forId(12309).getHandlers().put("option:buy-items", this); - SceneryDefinition.forId(12309).getHandlers().put("option:buy-food", this); - Scenery object = RegionManager.getObject(new Location(3219, 9623, 0)); - SceneryBuilder.replace(object, object.transform(object.getId(), 3)); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "bank": - player.getBank().open(); - return true; - case "buy-items": - ShopParser.Companion.openUid(player, 178); - return true; - case "buy-food": - ShopParser.Companion.openUid(player, 177); - return true; - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/object/DoogleLeafPlugin.java b/Server/src/main/java/core/game/interaction/object/DoogleLeafPlugin.java deleted file mode 100644 index 296fd8e41..000000000 --- a/Server/src/main/java/core/game/interaction/object/DoogleLeafPlugin.java +++ /dev/null @@ -1,40 +0,0 @@ -package core.game.interaction.object; - -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/java/core/game/interaction/object/DoorManagingPlugin.java b/Server/src/main/java/core/game/interaction/object/DoorManagingPlugin.java deleted file mode 100644 index d57aa9ece..000000000 --- a/Server/src/main/java/core/game/interaction/object/DoorManagingPlugin.java +++ /dev/null @@ -1,80 +0,0 @@ -package core.game.interaction.object; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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.node.scenery.SceneryBuilder; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Plugin used for handling the opening/closing of (double) - * doors/gates/fences/... - * @author Emperor - */ -@Initializable -public final class DoorManagingPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.setOptionHandler("open", this); - SceneryDefinition.setOptionHandler("close", this); - SceneryDefinition.setOptionHandler("shut", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Scenery object = (Scenery) node; - if (object.getType() != 9 && !player.getLocation().equals(node.getLocation()) && !player.getLocation().isNextTo(object) && object.getName().contains("cupboard")) { - return true; - } - String name = object.getName().toLowerCase(); - if (name.contains("drawers") || name.contains("wardrobe") || name.contains("cupboard")) { - switch(option) { - case "open": - if (object.isActive()) { - SceneryBuilder.replace(object, object.transform(object.getId() + 1), 80); - } - return true; - case "close": - case "shut": - SceneryBuilder.replace(object, object.transform(object.getId() - 1)); - return true; - } - return true; - } - if (name.contains("trapdoor") || name.contains("trap door")) { - Location destination = object.getLocation().transform(0, 6400, 0); - if (!RegionManager.isTeleportPermitted(destination)) { - player.getPacketDispatch().sendMessage("This doesn't seem to go anywhere."); - return true; - } - player.getProperties().setTeleportLocation(destination); - return true; - } - if (node.asScenery().getId() == 25341) {// Mithril door - return false; - } - if (!name.contains("door") && !name.contains("gate") && !name.contains("fence") && !name.contains("wall") && !name.contains("exit") && !name.contains("entrance")) { - return false; - } - DoorActionHandler.handleDoor(player, object); - return true; - } - - @Override - public Location getDestination(Node n, Node node) { - Scenery o = (Scenery) node; - if (o.getType() < 4 || o.getType() == 9) { - return DoorActionHandler.getDestination((Player) n, o); - } - return null; - } - -} diff --git a/Server/src/main/java/core/game/interaction/object/FlourMakingPlugin.java b/Server/src/main/java/core/game/interaction/object/FlourMakingPlugin.java deleted file mode 100644 index 475edf6c3..000000000 --- a/Server/src/main/java/core/game/interaction/object/FlourMakingPlugin.java +++ /dev/null @@ -1,330 +0,0 @@ -package core.game.interaction.object; - -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.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the flour making plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FlourMakingPlugin extends OptionHandler { - - /** - * Represents the animation of operating a hopper. - */ - private static final Animation ANIMATION = new Animation(3571); - - /** - * Represents the empty pot item. - */ - private static final Item EMPTY_POT = new Item(1931); - - /** - * Represents the pot of flour. - */ - private static final Item FLOUR = new Item(1933); - - /** - * Represents the config id. - */ - private static final int CONFIG = 695; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - int controls[] = new int[] { 2718, 2720, 2721, 24072, 24070 }; - for (int i : controls) { - SceneryDefinition.forId(i).getHandlers().put("option:operate", this); - } - SceneryDefinition.forId(36878).getHandlers().put("option:empty", this); - SceneryDefinition.forId(22420).getHandlers().put("option:empty", this); - SceneryDefinition.forId(5792).getHandlers().put("option:empty", this); - SceneryDefinition.forId(1782).getHandlers().put("option:empty", this); - SceneryDefinition.forId(1781).getHandlers().put("option:empty", this); - SceneryDefinition.forId(22421).getHandlers().put("option:empty", this); - SceneryDefinition.forId(24070).getHandlers().put("option:empty", this); - new GrainHopperPlugin().newInstance(arg); - new FillPotHandler().newInstance(arg); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - FlourExtension.extend(player);// doesn't set extension if already there. - final FlourExtension extension = player.getExtension(FlourExtension.class); - player.lock(3); - switch (option) { - case "operate": - player.animate(ANIMATION); - player.getAudioManager().send(3189); - if (extension.getSemiCharges() < 1) { - player.getPacketDispatch().sendMessage("You operate the empty hopper. Nothing interesting happens."); - } else { - extension.emptyChute(); - player.getPacketDispatch().sendMessage("You operate the hopper. The grain slides down the chute."); - player.getConfigManager().set(CONFIG, 1); - player.setAttribute("/save:milling:grain",player.getAttribute("milling:grain",0) + 1); - } - break; - case "empty": - return empty(player, extension); - } - return true; - } - - /** - * Method used to empty the bin. - * @param player the player. - * @param extension the extension. - * @return {@code True} if emptied. - */ - private boolean empty(final Player player, final FlourExtension extension) { - if (extension.getCharges() < 1) { - return true; - } - if (!player.getInventory().containsItem(EMPTY_POT)) { - player.getPacketDispatch().sendMessage("I need an empty pot to hold the flour in."); - return true; - } - if (player.getInventory().remove(EMPTY_POT)) { - player.getInventory().add(FLOUR); - extension.decrement(1); - player.getPacketDispatch().sendMessage(!extension.isEmpty() ? "You fill a pot with flour from the bin." : "You fill a pot with the last of the flour in the bin."); - - // Grind some flour in the windmill north of Lumbridge - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 0, 20); - - if (extension.getCharges() < 1) { - player.getConfigManager().set(CONFIG, 0); - } - } - return true; - } - - /** - * Represents the flour extension class. - * @author 'Vexia - * @version 1.0 - */ - public static final class FlourExtension { - - /** - * Represents the player. - */ - private final Player player; - - /** - * Represents the amount of flour charges. - */ - private int charges; - - /** - * Represents the amount of semi charges. - */ - private int semiCharges; - - /** - * Constructs a new {@code FlourMakingPlugin} {@code Object}. - * @param player the player. - */ - FlourExtension(final Player player) { - this.player = player; - } - - /** - * Method used to extend a flour extension. - * @param player the player. - */ - public static void extend(final Player player) { - if (player.getExtension(FlourExtension.class) == null) { - player.addExtension(FlourExtension.class, new FlourExtension(player)); - } - } - - /** - * Method used to increment the charges. - * @param increment the increment. - */ - public final void increment(final int increment, boolean semi) { - if(player.getAttribute("milling:grain",0) < 30) { - if (semi) { - semiCharges += increment; - } else { - charges += increment; - } - player.setAttribute("/save:milling:grain",player.getAttribute("milling:grain",0) + 1); - } - } - - /** - * Method used to decrement charges. - * @param increment the incremend. - */ - public final void decrement(final int increment) { - charges -= increment; - if(!(player.getAttribute("milling:grain",0) <= 0)){ - player.setAttribute("/save:milling:grain",player.getAttribute("milling:grain",0) + 1); - } - } - - /** - * Method used to fill a chute. - */ - public final void fill() { - semiCharges += 1; - } - - /** - * Method used to swap charges. - */ - public final void emptyChute() { - charges += semiCharges; - semiCharges = 0; - } - - /** - * Method used to check if the chute is empty. - * @return True if so. - */ - public final boolean isEmpty() { - return charges < 1; - } - - /** - * Gets the charges. - * @return The charges. - */ - public int getCharges() { - return charges; - } - - /** - * Gets the semiCharges. - * @return The semiCharges. - */ - public int getSemiCharges() { - return semiCharges; - } - - /** - * Sets the semiCharges. - * @param semiCharges The semiCharges to set. - */ - public void setSemiCharges(int semiCharges) { - this.semiCharges = semiCharges; - } - - /** - * Sets the charges. - * @param charges The charges to set. - */ - public void setCharges(int charges) { - this.charges = charges; - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - - } - - /** - * Represents the grain hopper use with handler. - * @author 'Vexia - * @version 1.0 - */ - public static final class GrainHopperPlugin extends UseWithHandler { - - /** - * Represents the grain item. - */ - private static final Item GRAIN = new Item(1947); - - /** - * Represents the animation of opperating a hopper. - */ - private static final Animation ANIMATION = new Animation(3571); - - /** - * Constructs a new {@code GrainHopperPlugin} {@code Object}. - */ - public GrainHopperPlugin() { - super(1947); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - int[] hoppers = new int[] { 2713, 2714, 2716, 2717, 2718, 2720, 2721, 2722, 10170, 15847, 15848, 15849, 15850, 15851, 15852, 15853, 15854, 15873, 15874, 15875, 15876, 15877, 15878, 15879, 15880, 20260, 20261, 20262, 20264, 20265, 20266, 22422, 24071, 24072, 36881 }; - for (int i : hoppers) { - addHandler(i, OBJECT_TYPE, this); - } - addHandler(24075, OBJECT_TYPE, this);// don't think it was added. - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - FlourExtension.extend(player); - final FlourExtension extension = player.getExtension(FlourExtension.class); - if (extension.getSemiCharges() > 0) { - player.getPacketDispatch().sendMessage("There is already grain in the hopper."); - return true; - } - if (player.getInventory().remove(GRAIN)) { - player.animate(ANIMATION); - extension.fill(); - player.getPacketDispatch().sendMessage("You put the grain in the hopper."); - } - return true; - } - - } - - /** - * Represents the plugin used to fill a bucket by a use-with interaction. - * @author 'Vexia - * @version 1.0 - */ - public final class FillPotHandler extends UseWithHandler { - - /** - * Constructs a new {@code FillPotHandler} {@code Object}. - */ - public FillPotHandler() { - super(EMPTY_POT.getId()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(36878, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - FlourExtension.extend(player);// doesn't set extension if already - // there. - final FlourExtension extension = player.getExtension(FlourExtension.class); - player.lock(3); - empty(player, extension); - return true; - } - - } -} diff --git a/Server/src/main/java/core/game/interaction/object/GertrudeCatPlugin.java b/Server/src/main/java/core/game/interaction/object/GertrudeCatPlugin.java deleted file mode 100644 index ba9408273..000000000 --- a/Server/src/main/java/core/game/interaction/object/GertrudeCatPlugin.java +++ /dev/null @@ -1,66 +0,0 @@ -/* -package core.game.interaction.object; - -import core.cache.def.impl.ObjectDefinition; -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.game.node.item.Item; -import core.plugin.Plugin; -import core.plugin.InitializablePlugin; -import core.tools.RandomFunction; - -*/ -/** - * Represents the plugin to handle interactions with gertrudes cat. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class GertrudeCatPlugin extends OptionHandler { - - */ -/** - * Represents the kitten item. - *//* - - private static final Item KITTEN = new Item(13236); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ObjectDefinition.forId(2620).getConfigurations().put("option:search", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final Quest quest = player.getQuestRepository().getQuest("Gertrude's Cat"); - switch (option) { - case "search": - if (quest.getStage(player) == 50 && !player.getInventory().containsItem(KITTEN) && !player.getBank().containsItem(KITTEN)) { - quest.setStage(player, 40); - } - if (quest.getStage(player) == 40) { - if (player.getAttribute("findkitten", false) && player.getInventory().freeSlots() > 0) { - quest.setStage(player, 50); - player.getDialogueInterpreter().sendDialogue("You find some kittens! You carefully place them into your backpack."); - player.getInventory().add(KITTEN); - return true; - } - } else { - player.getPacketDispatch().sendMessage("You search the crate."); - player.getPacketDispatch().sendMessage("You find nothing."); - if (RandomFunction.random(0, 3) == 1) { - player.getPacketDispatch().sendMessage("You can hear kittens mewing close by..."); - player.setAttribute("findkitten", true); - } - } - break; - } - return true; - } - -} -*/ diff --git a/Server/src/main/java/core/game/interaction/object/GnomeSpiritTreePlugin.java b/Server/src/main/java/core/game/interaction/object/GnomeSpiritTreePlugin.java deleted file mode 100644 index 1f43896da..000000000 --- a/Server/src/main/java/core/game/interaction/object/GnomeSpiritTreePlugin.java +++ /dev/null @@ -1,42 +0,0 @@ -package core.game.interaction.object; - -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 to handle gnome tree teleporting. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GnomeSpiritTreePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(1317).getHandlers().put("option:talk-to", this); - SceneryDefinition.forId(1317).getHandlers().put("option:teleport", this); - SceneryDefinition.forId(1293).getHandlers().put("option:talk-to", this); - SceneryDefinition.forId(1293).getHandlers().put("option:teleport", this); - SceneryDefinition.forId(1294).getHandlers().put("option:talk-to", this); - SceneryDefinition.forId(1294).getHandlers().put("option:teleport", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "talk-to": - player.getDialogueInterpreter().open(1317); - return true; - case "teleport": - player.getDialogueInterpreter().open(1317); - return true; - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/interaction/object/GoblinVillagePopulationPlugin.java b/Server/src/main/java/core/game/interaction/object/GoblinVillagePopulationPlugin.java deleted file mode 100644 index 63dbdaf14..000000000 --- a/Server/src/main/java/core/game/interaction/object/GoblinVillagePopulationPlugin.java +++ /dev/null @@ -1,36 +0,0 @@ -package core.game.interaction.object; - -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/java/core/game/interaction/object/GrandExchangeShortcut.java b/Server/src/main/java/core/game/interaction/object/GrandExchangeShortcut.java deleted file mode 100644 index 6caff3adc..000000000 --- a/Server/src/main/java/core/game/interaction/object/GrandExchangeShortcut.java +++ /dev/null @@ -1,110 +0,0 @@ -package core.game.interaction.object; - -import core.cache.def.impl.SceneryDefinition; -import core.game.node.entity.player.link.diary.DiaryType; -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.impl.ForceMovement; -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; - -/** - * Handles the grand exchange shortcut. - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class GrandExchangeShortcut extends OptionHandler { - - /** - * The climbing down animation. - */ - private static final Animation CLIMB_DOWN = Animation.create(2589); - - /** - * The crawling through animation. - */ - private static final Animation CRAWL_THROUGH = Animation.create(2590); - - /** - * The climbing up animation. - */ - private static final Animation CLIMB_UP = Animation.create(2591); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(9311).getHandlers().put("option:climb-into", this); - SceneryDefinition.forId(9312).getHandlers().put("option:climb-into", this); - return null; - } - - @Override - public boolean handle(final Player player, Node node, String option) { - if (player.getSkills().getLevel(Skills.AGILITY) < 21) { - player.getDialogueInterpreter().sendDialogue("You need an Agility level of at least 21 to do this."); - return true; - } - player.lock(4); - final Scenery o = (Scenery) node; - if (o.getId() == 9311) { - ForceMovement.run(player, Location.create(3138, 3516, 0), o.getLocation(), CLIMB_DOWN); - GameWorld.getPulser().submit(new Pulse(1, player) { - int count; - - @Override - public boolean pulse() { - switch (++count) { - case 2: - player.animate(CRAWL_THROUGH); - player.getProperties().setTeleportLocation(Location.create(3143, 3514, 0)); - break; - case 3: - ForceMovement.run(player, Location.create(3143, 3514, 0), Location.create(3144, 3514, 0), CLIMB_UP); - // Use the shortcut under the wall, north-west of the Grand

Exchange - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 8); - return true; - } - return false; - } - }); - } else { - ForceMovement.run(player, Location.create(3144, 3514, 0), o.getLocation(), CLIMB_DOWN); - GameWorld.getPulser().submit(new Pulse(1, player) { - int count; - - @Override - public boolean pulse() { - switch (++count) { - case 2: - player.animate(CRAWL_THROUGH); - player.getProperties().setTeleportLocation(Location.create(3139, 3516, 0)); - break; - case 3: - ForceMovement.run(player, Location.create(3139, 3516, 0), Location.create(3138, 3516, 0), CLIMB_UP); - // Use the shortcut under the wall, north-west of the Grand

Exchange - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 8); - return true; - } - return false; - } - }); - } - return true; - } - - @Override - public Location getDestination(Node n, Node node) { - if (((Scenery) node).getId() == 9311) { - return Location.create(3138, 3516, 0); - } - return Location.create(3144, 3514, 0); - } - -} diff --git a/Server/src/main/java/core/game/interaction/object/GuidorDoorPlugin.java b/Server/src/main/java/core/game/interaction/object/GuidorDoorPlugin.java deleted file mode 100644 index d50c07be8..000000000 --- a/Server/src/main/java/core/game/interaction/object/GuidorDoorPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.interaction.object; - -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/java/core/game/interaction/object/LumbridgeBasementPlugin.java b/Server/src/main/java/core/game/interaction/object/LumbridgeBasementPlugin.java deleted file mode 100644 index b568be5c8..000000000 --- a/Server/src/main/java/core/game/interaction/object/LumbridgeBasementPlugin.java +++ /dev/null @@ -1,324 +0,0 @@ -package core.game.interaction.object; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialoguePlugin; -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.impl.ForceMovement; -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.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -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 core.plugin.Plugin; -import rs09.plugin.PluginManager; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Handles the lumbridge basement. - * @author Vexia - * - */ -@Initializable -public class LumbridgeBasementPlugin extends OptionHandler { - - /** - * The animation to use for the shortcut. - */ - private static final Animation ANIMATION = new Animation(2240); - - /** - * The jumping animation for stepping stones. - */ - private static final Animation JUMP_ANIMATION = new Animation(741); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(6899).getHandlers().put("option:squeeze-through", this); - SceneryDefinition.forId(6898).getHandlers().put("option:squeeze-through", this); - SceneryDefinition.forId(6905).getHandlers().put("option:squeeze-through", this); - SceneryDefinition.forId(6912).getHandlers().put("option:squeeze-through", this); - SceneryDefinition.forId(5949).getHandlers().put("option:jump-across", this); - SceneryDefinition.forId(6658).getHandlers().put("option:enter", this); - SceneryDefinition.forId(32944).getHandlers().put("option:enter", this); - SceneryDefinition.forId(40261).getHandlers().put("option:climb-up", this); - SceneryDefinition.forId(40262).getHandlers().put("option:climb-up", this); - 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); - PluginManager.definePlugins(new LightCreatureNPC(), new LightCreatureHandler(), new FishMongerDialogue()); - SceneryBuilder.add(new Scenery(40260, Location.create(2526, 5828, 2), 2)); - return this; - } - - @Override - public boolean handle(final Player player, Node node, String option) { - switch (option) { - case "squeeze-through": - Direction dir = null; - Location to = null; - switch (node.getId()) { - case 6912: - to = node.getLocation().getY() == 9603 ? Location.create(3224, 9601, 0) : Location.create(3224, 9603, 0); - dir = node.getLocation().getY() == 9603 ? Direction.SOUTH : Direction.NORTH; - break; - default: - to = player.getLocation().getX() >= 3221 ? Location.create(3219, 9618, 0) : Location.create(3222, 9618, 0); - dir = player.getLocation().getX() >= 3221 ? Direction.WEST : Direction.EAST; - break; - } - player.sendMessage("You squeeze through the hole."); - ForceMovement.run(player, player.getLocation(), to, ANIMATION, ANIMATION, dir, 20).setEndAnimation(Animation.RESET); - return true; - case "jump-across": - Location f = null; - Location s = null; - switch (node.getId()) { - case 5949: - f = Location.create(3221, 9554, 0); - s = player.getLocation().getY() >= 9556 ? Location.create(3221, 9552, 0) : Location.create(3221, 9556, 0); - break; - } - final Location first = f; - final Location second = s; - player.lock(); - GameWorld.getPulser().submit(new Pulse(2, player) { - int counter = 1; - - @Override - public boolean pulse() { - if (counter == 3) { - player.unlock(); - ForceMovement.run(player, player.getLocation(), second, JUMP_ANIMATION, 20); - player.sendMessage("You leap across with a mighty leap!"); - return true; - } else if (counter == 1){ - ForceMovement.run(player, player.getLocation(), first, JUMP_ANIMATION, 20); - } - counter++; - return false; - } - }); - break; - case "enter": - switch (node.getId()) { - case 32944: - //Location.create(3353,3951,0) - player.teleport(Location.create(3219, 9532, 2)); - break; - case 6658: - //Location.create(3226, 9542, 0) - player.teleport(Location.create(3353, 3950, 0)); - break; - } - break; - case "climb-up": - switch (node.getId()) { - case 40261: - player.teleport(player.getLocation().transform(0, -1, 1)); - break; - case 40262: - player.teleport(player.getLocation().transform(0, -1, 1)); - break; - } - break; - case "jump-down": - switch (node.getId()) { - case 40849: - player.teleport(player.getLocation().transform(0, 1, -1)); - break; - } - break; - case "climb-through": - switch (node.getId()) { - case 40260: - player.teleport(Location.create(2525, 5810, 0)); - break; - } - break; - case "crawl-through": - switch (node.getId()) { - case 41077: - player.teleport(Location.create(2527, 5830, 2)); - break; - } - break; - } - return true; - } - - @Override - public Location getDestination(Node node, Node n) { - if (n instanceof Scenery) { - if (n.getId() == 5949) { - return node.getLocation().getY() >= 9555 ? Location.create(3221, 9556, 0) : Location.create(3221, 9552, 0); - } else if (n.getId() == 40262) { - return n.getLocation().transform(0, 1, 0); - } else if (n.getId() == 40261) { - return n.getLocation().transform(0, 1, 0); - } - } - 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}; - } - - } - - /** - * Handles the fish monger dialogue. - * @author Vexia - * - */ - public class FishMongerDialogue extends DialoguePlugin { - - /** - * Constructs the {@code FishMongerDialogue} - */ - public FishMongerDialogue() { - /* - * empty. - */ - } - - /** - * Constructs the {@code FishMongerDialogue} - */ - public FishMongerDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FishMongerDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Hello, " + player.getUsername() + " would you like to look at", "my wares?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("Yes, please.", "No, thanks."); - stage++; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - end(); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {1369}; - } - - } -} diff --git a/Server/src/main/java/core/game/interaction/object/LumbridgeSwampHolePlugin.java b/Server/src/main/java/core/game/interaction/object/LumbridgeSwampHolePlugin.java deleted file mode 100644 index 08f4e568e..000000000 --- a/Server/src/main/java/core/game/interaction/object/LumbridgeSwampHolePlugin.java +++ /dev/null @@ -1,73 +0,0 @@ -package core.game.interaction.object; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.game.content.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/java/core/game/interaction/object/MoyrtniaSwampPlugin.java b/Server/src/main/java/core/game/interaction/object/MoyrtniaSwampPlugin.java deleted file mode 100644 index 52daa4d34..000000000 --- a/Server/src/main/java/core/game/interaction/object/MoyrtniaSwampPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.interaction.object; - -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/java/core/game/interaction/object/MuseumGatePlugin.java b/Server/src/main/java/core/game/interaction/object/MuseumGatePlugin.java deleted file mode 100644 index 722bcd31f..000000000 --- a/Server/src/main/java/core/game/interaction/object/MuseumGatePlugin.java +++ /dev/null @@ -1,36 +0,0 @@ -package core.game.interaction.object; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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/java/core/game/interaction/object/ProspectOrePlugin.java b/Server/src/main/java/core/game/interaction/object/ProspectOrePlugin.java deleted file mode 100644 index d2fc96923..000000000 --- a/Server/src/main/java/core/game/interaction/object/ProspectOrePlugin.java +++ /dev/null @@ -1,82 +0,0 @@ -package core.game.interaction.object; - -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -import core.game.node.entity.skill.gather.SkillingResource; -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.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.plugin.Plugin; - -/** - * Handles the prospecting of an ore. - * @author 'Vexia - */ -@Initializable -public class ProspectOrePlugin extends OptionHandler { - - @Override - public boolean handle(final Player player, Node node, String option) { - final Scenery object = (Scenery) node; - int tut_stage = TutorialSession.getExtension(player).getStage(); - if (tut_stage == 31 && object.getId() == 3043) { - GameWorld.getPulser().submit(new Pulse(1) { - int count = 0; - - @Override - public boolean pulse() { - if (count == 1) { - TutorialStage.load(player, 32, false); - } else if (count >= 2) { - TutorialStage.load(player, 33, false); - return true; - } - count++; - return false; - } - }); - } - if (tut_stage == 33 && object.getId() == 3042) { - GameWorld.getPulser().submit(new Pulse(1) { - int count = 0; - - @Override - public boolean pulse() { - if (count == 1) { - TutorialStage.load(player, 34, false); - return true; - } - count++; - return false; - } - }); - } - final SkillingResource rock = SkillingResource.forId(object.getId()); - if (rock == null) { - player.getPacketDispatch().sendMessage("There is no ore currently available in this rock."); - return true; - } - player.getPacketDispatch().sendMessage("You examine the rock for ores..."); - player.getPulseManager().run(new Pulse(3, player, object) { - @Override - public boolean pulse() { - player.getPacketDispatch().sendMessage("This rock contains " + ItemDefinition.forId(rock.getReward()).getName().toLowerCase() + "."); - return true; - } - }); - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.setOptionHandler("prospect", this); - return this; - } - -} diff --git a/Server/src/main/java/core/game/interaction/object/ReadSignPostPlugin.java b/Server/src/main/java/core/game/interaction/object/ReadSignPostPlugin.java deleted file mode 100644 index 71e2e61af..000000000 --- a/Server/src/main/java/core/game/interaction/object/ReadSignPostPlugin.java +++ /dev/null @@ -1,120 +0,0 @@ -package core.game.interaction.object; - -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/java/core/game/interaction/object/SeersCageUnlockPlugin.java b/Server/src/main/java/core/game/interaction/object/SeersCageUnlockPlugin.java deleted file mode 100644 index efbc82d61..000000000 --- a/Server/src/main/java/core/game/interaction/object/SeersCageUnlockPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.interaction.object; - -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/java/core/game/interaction/object/WildernessObeliskPlugin.java b/Server/src/main/java/core/game/interaction/object/WildernessObeliskPlugin.java deleted file mode 100644 index 75ea82f6e..000000000 --- a/Server/src/main/java/core/game/interaction/object/WildernessObeliskPlugin.java +++ /dev/null @@ -1,151 +0,0 @@ -package core.game.interaction.object; - -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.TeleportManager.TeleportType; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.game.world.update.flag.chunk.GraphicUpdateFlag; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.RandomFunction; - -/** - * Represents the wilderness obelisk plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class WildernessObeliskPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(14829).getHandlers().put("option:activate", this); - SceneryDefinition.forId(14826).getHandlers().put("option:activate", this); - SceneryDefinition.forId(14827).getHandlers().put("option:activate", this); - SceneryDefinition.forId(14828).getHandlers().put("option:activate", this); - SceneryDefinition.forId(14830).getHandlers().put("option:activate", this); - SceneryDefinition.forId(14831).getHandlers().put("option:activate", this); - return this; - } - - @Override - public boolean handle(final Player player, final Node node, String option) { - final Scenery nodeObject = (Scenery) node; - final Obelisk stationObelisk = Obelisk.forLocation(player.getLocation()); - if (stationObelisk == null) { - return false; - } - for (int i = 0; i < 4; i++) { - int x = stationObelisk.getLocation().getX(); - int y = stationObelisk.getLocation().getY(); - int z = stationObelisk.getLocation().getZ(); - switch (i) { - case 0: - x = x + 2; - y = y + 2; - SceneryBuilder.replace(new Scenery(nodeObject.getId(), Location.create(x, y, z)), new Scenery(14825, Location.create(x, y, 0)), 6); - break; - case 1: - x = x - 2; - y = y + 2; - SceneryBuilder.replace(new Scenery(nodeObject.getId(), Location.create(x, y, z)), new Scenery(14825, Location.create(x, y, 0)), 6); - break; - case 2: - x = x - 2; - y = y - 2; - SceneryBuilder.replace(new Scenery(nodeObject.getId(), Location.create(x, y, z)), new Scenery(14825, Location.create(x, y, 0)), 6); - break; - case 3: - x = x + 2; - y = y - 2; - SceneryBuilder.replace(new Scenery(nodeObject.getId(), Location.create(x, y, z)), new Scenery(14825, Location.create(x, y, 0)), 6); - break; - } - } - player.getAudioManager().send(204); - GameWorld.getPulser().submit(new Pulse(6, player) { - @Override - public boolean pulse() { - final Location center = stationObelisk.getLocation(); - if (getDelay() == 1) { - for (int x = center.getX() - 1; x <= center.getX() + 1; x++) { - for (int y = center.getY() - 1; y <= center.getY() + 1; y++) { - Location l = Location.create(x, y, 0); - RegionManager.getRegionChunk(l).flag(new GraphicUpdateFlag(Graphics.create(342), l)); - } - } - return true; - } - int index = RandomFunction.random(Obelisk.values().length); - Obelisk newObelisk = Obelisk.values()[index]; - if (newObelisk == stationObelisk) { - newObelisk = Obelisk.values()[index++ % Obelisk.values().length]; - } - 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); - } else { - player.getTeleporter().send(Location.create(newObelisk.getLocation().getX() + xDif, newObelisk.getLocation().getY() + yDif, 0), TeleportType.OBELISK, 2); - } - } - super.setDelay(1); - return false; - } - - }); - return true; - } - - /** - * Represents an obelisk type. - * @author 'Vexia - * @version 1.0 - */ - public enum Obelisk { - LEVEL_13(new Location(3156, 3620, 0)), LEVEL_19(new Location(3219, 3656, 0)), LEVEL_27(new Location(3035, 3732, 0)), LEVEL_35(new Location(3106, 3794, 0)), LEVEL_44(new Location(2980, 3866, 0)), LEVEL_50(new Location(3307, 3916, 0)); - - /** - * Represents the location to teleport to. - */ - private Location location; - - /** - * Constructs a new {@code Obelisk} {@code Object}. - * @param location the location. - */ - Obelisk(Location location) { - this.location = location; - } - - /** - * Gets the location. - * @return the location - */ - public Location getLocation() { - return location; - } - - /** - * Gets the obelisk by location. - * @param location the location. - * @return the obelisk. - */ - public static Obelisk forLocation(Location location) { - for (Obelisk obelisk : Obelisk.values()) - if (obelisk.getLocation().getDistance(location) <= 20) - return obelisk; - return null; - } - } -} diff --git a/Server/src/main/java/core/game/interaction/object/WitchsPotionPlugin.java b/Server/src/main/java/core/game/interaction/object/WitchsPotionPlugin.java deleted file mode 100644 index fac668033..000000000 --- a/Server/src/main/java/core/game/interaction/object/WitchsPotionPlugin.java +++ /dev/null @@ -1,41 +0,0 @@ -package core.game.interaction.object; - -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/java/core/game/interaction/object/dmc/DMCHandler.java b/Server/src/main/java/core/game/interaction/object/dmc/DMCHandler.java deleted file mode 100644 index 519b7c981..000000000 --- a/Server/src/main/java/core/game/interaction/object/dmc/DMCHandler.java +++ /dev/null @@ -1,341 +0,0 @@ -package core.game.interaction.object.dmc; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.CombatSwingHandler; -import core.game.node.entity.combat.ImpactHandler.HitsplatType; -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.item.Item; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.LogoutTask; -import core.game.system.task.Pulse; -import rs09.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.zone.ZoneRestriction; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; -import core.tools.RandomFunction; - -/** - * Handles a player's Dwarf Multi-cannon. - * @author Emperor - */ -public final class DMCHandler { - - /** - * The player. - */ - private final Player player; - - /** - * The cannon object. - */ - private Scenery cannon; - - /** - * The amount of cannonballs loaded. - */ - private int cannonballs; - - /** - * The logout plugin. - */ - private Plugin logoutPlugin; - - /** - * The firing pulse. - */ - private Pulse firingPulse; - - /** - * The current direction. - */ - private DMCRevolution direction = DMCRevolution.NORTH; - - /** - * The decaying pulse. - */ - private Pulse decayPulse; - - /** - * Constructs a new {@code DMCHandler} {@code Object} - * @param player The player owning the cannon. - */ - 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() { - if (cannonballs < 1) { - player.getPacketDispatch().sendMessage("Your cannon has run out of ammo!"); - return true; - } - player.getPacketDispatch().sendSceneryAnimation(cannon, Animation.create(direction.getAnimationId())); - Location l = cannon.getLocation().transform(1, 1, 0); - 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) && 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(); - cannonballs--; - int hit = 0; - if (player.getSwingHandler(false).isAccurateImpact(player, npc, CombatStyle.RANGE, 1.2, 1.0)) { - hit = RandomFunction.getRandom(30); - } - player.getSkills().addExperience(Skills.RANGE, hit * 2); - npc.getImpactHandler().manualHit(player, hit, HitsplatType.NORMAL, (int) Math.ceil(l.getDistance(npc.getLocation()) * 0.3)); - npc.attack(player); - break; - } - } - return false; - } - - /** - * Starts rotating the cannon. - */ - public void startFiring() { - if (cannon == null || !cannon.isActive()) { - player.getPacketDispatch().sendMessage("You don't have a cannon active."); - return; - } - if (firingPulse.isRunning()) { - firingPulse.stop(); - return; - } - if (cannonballs < 1) { - int amount = player.getInventory().getAmount(new Item(2)); - if (amount < 1) { - player.getPacketDispatch().sendMessage("Your cannon is out of cannonballs."); - return; - } - int toUse = 30 - cannonballs; - if(toUse > amount){ - toUse = amount; - } - if(toUse > 0){ - cannonballs = toUse; - player.getPacketDispatch().sendMessage("You load the cannon with " + toUse + " cannonballs."); - player.getInventory().remove(new Item(2, toUse)); - } else { - player.sendMessage("Your cannon is already fully loaded."); - } - } - firingPulse.restart(); - firingPulse.start(); - GameWorld.getPulser().submit(firingPulse); - } - - /** - * Makes the cannon explode. - */ - public void explode(boolean decay) { - if (!cannon.isActive()) { - return; - } - player.sendMessage("Your cannon has " + (decay ? "decayed" : "been destroyed") + "!"); - for (Player p : RegionManager.getLocalPlayers(player)) { - p.getPacketDispatch().sendPositionedGraphic(189, 0, 1, cannon.getLocation()); - } - clear(false); - } - - /** - * Constructs the cannon. - */ - public static void construct(final Player player) { - final Location spawn = RegionManager.getSpawnLocation(player, new Scenery(6, player.getLocation())); - if (spawn == null) { - player.getPacketDispatch().sendMessage("There's not enough room for your cannon."); - return; - } - if (player.getZoneMonitor().isRestricted(ZoneRestriction.CANNON)) { - player.getPacketDispatch().sendMessage("You can't set up a cannon here."); - 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(); - player.lock(9); - player.faceLocation(spawn.transform(Direction.NORTH_EAST)); - GameWorld.getPulser().submit(new Pulse(2, player) { - int count = 0; - Scenery object = null; - - @Override - public boolean pulse() { - player.animate(Animation.create(827)); - if (!player.getInventory().remove(new Item(6 + (count * 2)))) { - for (int i = count - 1; i >= 0; i--) { - player.getInventory().add(new Item(6 + (i * 2))); - } - if (object != null) { - SceneryBuilder.remove(object); - } - return true; - } - player.addExtension(LogoutTask.class, new LogoutTask() { - int amount = count + 1; - - @Override - public void run(Player player) { - for (int i = 0; i < amount; i++) { - player.getInventory().add(new Item(6 + (i * 2))); - } - if (object != null) { - SceneryBuilder.remove(object); - } - } - }); - switch (count) { - case 0: - object = SceneryBuilder.add(new Scenery(7, spawn)); - player.getPacketDispatch().sendMessage("You place the cannon base on the ground."); - return count++ == 666; - case 1: - player.getPacketDispatch().sendMessage("You add the stand."); - break; - case 2: - player.getPacketDispatch().sendMessage("You add the barrels."); - break; - case 3: - player.getPacketDispatch().sendMessage("You add the furnace."); - SceneryBuilder.remove(object); - handler.configure(SceneryBuilder.add(object = object.transform(6))); - return true; - } - SceneryBuilder.remove(object); - SceneryBuilder.add(object = object.transform(object.getId() + 1)); - return ++count == 4; - } - }); - } - - /** - * Configures the cannon. - * @param cannon The cannon. - */ - public void configure(Scenery cannon) { - this.cannon = cannon; - player.removeExtension(LogoutTask.class); - player.getLogoutPlugins().add(logoutPlugin = new Plugin() { - @Override - public Plugin newInstance(Player arg) throws Throwable { - clear(false); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - }); - } - - /** - * Clears the cannon. - * @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"); - player.getLogoutPlugins().remove(logoutPlugin); - if (!pickup) { - player.getSavedData().getActivityData().setLostCannon(true); - return; - } - for (int i = 0; i < 4; i++) { - player.getInventory().add(new Item(12 - (i * 2))); - } - if (cannonballs > 0) { - player.getInventory().add(new Item(2, cannonballs)); - cannonballs = 0; - } - } - - /** - * Gets the player. - * @return the player - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the cannon. - * @return the cannon - */ - public Scenery getCannon() { - return cannon; - } - - /** - * Sets the cannon. - * @param cannon the cannon to set. - */ - public void setCannon(Scenery cannon) { - this.cannon = cannon; - } - - /** - * Gets the cannonballs. - * @return the cannonballs - */ - public int getCannonballs() { - return cannonballs; - } - - /** - * Sets the bacannonballs. - * @param cannonballs the cannonballs to set. - */ - public void setCannonballs(int cannonballs) { - this.cannonballs = cannonballs; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/interaction/object/wildyditch/WildernessInterfacePlugin.java b/Server/src/main/java/core/game/interaction/object/wildyditch/WildernessInterfacePlugin.java deleted file mode 100644 index 6fea66824..000000000 --- a/Server/src/main/java/core/game/interaction/object/wildyditch/WildernessInterfacePlugin.java +++ /dev/null @@ -1,86 +0,0 @@ -package core.game.interaction.object.wildyditch; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.content.global.action.DoorActionHandler; -import core.game.node.entity.impl.ForceMovement; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; - -/** - * Represents the wilderness interface plugin. - * @author Emperor - * @version 1.0 - */ -public final class WildernessInterfacePlugin extends ComponentPlugin { - - /** - * The animation. - */ - private static final Animation ANIMATION = Animation.create(6132); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(382, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - player.getInterfaceManager().close(); - if (button != 18) { - return true; - } - if (player.getAttribute("wildy_ditch") != null) - handleDitch(player); - else if(player.getAttribute("wildy_gate") != null) - handleGate(player); - return true; - } - - /** - * Method used to handle the ditch interface. - * @param player the player. - */ - public static void handleDitch(final Player player) { - Scenery ditch = player.getAttribute("wildy_ditch"); - player.removeAttribute("wildy_ditch"); - Location l = ditch.getLocation(); - int x = player.getLocation().getX(); - int y = player.getLocation().getY(); - if (ditch.getRotation() % 2 == 0) { - if (y <= l.getY()) { - ForceMovement.run(player, Location.create(x, l.getY() - 1, 0), Location.create(x, l.getY() + 2, 0), ANIMATION, 20).setEndAnimation(null); - } else { - ForceMovement.run(player, Location.create(x, l.getY() + 2, 0), Location.create(x, l.getY() - 1, 0), ANIMATION, 20).setEndAnimation(null); - } - } else { - if (x > l.getX()) { - ForceMovement.run(player, Location.create(l.getX() + 2, y, 0), Location.create(l.getX() - 1, y, 0), ANIMATION, 20).setEndAnimation(null); - } else { - 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)); - /*if (GameWorld.getSettings().isPvp()) { - ActivityManager.register(new BountyHunterActivity(CraterType.MID_LEVEL)); - ActivityManager.register(new BountyHunterActivity(CraterType.HIGH_LEVEL)); - }*/ - } - public static void handleGate(final Player player){ - Scenery gate = player.getAttribute("wildy_gate"); - Location endLocation = player.getAttribute("wildy_gate_loc"); - - // Cleanup - player.removeAttribute("wildy_gate"); - player.removeAttribute("wildy_gate_loc"); - - // Move player through gate/door - DoorActionHandler.handleAutowalkDoor(player,gate,endLocation); - } -} diff --git a/Server/src/main/java/core/game/interaction/player/AttackOptionPlugin.java b/Server/src/main/java/core/game/interaction/player/AttackOptionPlugin.java deleted file mode 100644 index 624a231cd..000000000 --- a/Server/src/main/java/core/game/interaction/player/AttackOptionPlugin.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.interaction.player; - -import core.cache.def.impl.NPCDefinition; -import core.game.interaction.Option; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the attack option plugin handler. - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class AttackOptionPlugin extends OptionHandler { - - @Override - public boolean handle(Player player, Node node, String option) { - //Makes sure player uses correct attack styles for lumbridge dummies - if (node.getId() == 4474 && !(player.getSwingHandler(false).getType() == CombatStyle.MAGIC)){ player.sendMessage("You can only attack this with magic."); return true; } - if (node.getId() == 7891 && !(player.getSwingHandler(false).getType() == CombatStyle.MELEE)){ player.sendMessage("You must use the training sword to attack this."); return true; } - player.attack(node); - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - Option._P_ATTACK.setHandler(this); - NPCDefinition.setOptionHandler("attack", this); - return this; - } - - @Override - public boolean isDelayed(Player player) { - return false; - } - -} diff --git a/Server/src/main/java/core/game/interaction/player/ExamineOptionPlugin.java b/Server/src/main/java/core/game/interaction/player/ExamineOptionPlugin.java deleted file mode 100644 index 6c31d18c3..000000000 --- a/Server/src/main/java/core/game/interaction/player/ExamineOptionPlugin.java +++ /dev/null @@ -1,32 +0,0 @@ -package core.game.interaction.player; - -import core.game.interaction.Option; -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.game.interaction.inter.PlayerExamineInterfacePlugin; - -/** - * Package -> core.game.interaction.player - * Created on -> 9/13/2016 @7:53 AM for 530 - * - * @author Ethan Kyle Millard - */ -@Initializable -public class ExamineOptionPlugin extends OptionHandler { - @Override - public Plugin newInstance(Object arg) throws Throwable { - Option._P_EXAMINE.setHandler(this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - PlayerExamineInterfacePlugin component = new PlayerExamineInterfacePlugin(); - component.prepareInterface(player, (Player) node); - return false; - } - -} diff --git a/Server/src/main/java/core/game/interaction/player/LoginValidationPlugin.java b/Server/src/main/java/core/game/interaction/player/LoginValidationPlugin.java deleted file mode 100644 index f152c354f..000000000 --- a/Server/src/main/java/core/game/interaction/player/LoginValidationPlugin.java +++ /dev/null @@ -1,94 +0,0 @@ -package core.game.interaction.player; - -import core.game.content.activity.ActivityManager; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.system.SystemManager; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.PluginManifest; -import core.plugin.PluginType; -import rs09.game.world.GameWorld; - -import java.util.concurrent.TimeUnit; - -/** - * Validates a player login. - * @author Emperor - * @author Vexia - * - */ -@PluginManifest(type = PluginType.LOGIN) -@Initializable -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)}; - - /** - * Constructs a new {@Code LoginValidationPlugin} {@Code Object} - */ - public LoginValidationPlugin() { - /* - * empty. - */ - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public Plugin newInstance(final Player player) throws Throwable { - player.unlock(); - if (player.isArtificial()) { - return this; - } - if (!SystemManager.getSystemConfig().validLogin(player)) { - return this; - } - if (GameWorld.getSettings().isDevMode()) { - player.toggleDebug(); - } - if (player.getAttribute("fc_wave", -1) > -1) { - ActivityManager.start(player, "fight caves", true); - } - if (player.getAttribute("falconry", false)) { - ActivityManager.start(player, "falconry", true); - } - if (player.getSavedData().getQuestData().getDragonSlayerAttribute("repaired")) { - player.getConfigManager().set(177, 1967876); - } - if (player.getSavedData().getGlobalData().getLootShareDelay() < System.currentTimeMillis() && player.getSavedData().getGlobalData().getLootShareDelay() != 0L) { - player.getGlobalData().setLootSharePoints((int) (player.getGlobalData().getLootSharePoints() - player.getGlobalData().getLootSharePoints() * 0.10)); - } else { - player.getSavedData().getGlobalData().setLootShareDelay(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); - } - checkQuestPointsItems(player); - return this; - } - - /** - * Method used to check for the quest point cape items. - * @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)) - ) { - 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); - } else { - player.getInventory().add(i); - } - } - } - } - } -} diff --git a/Server/src/main/java/core/game/interaction/player/RequestOptionPlugin.java b/Server/src/main/java/core/game/interaction/player/RequestOptionPlugin.java deleted file mode 100644 index 6377344b1..000000000 --- a/Server/src/main/java/core/game/interaction/player/RequestOptionPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.interaction.player; - -import core.game.interaction.Option; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.request.RequestType; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle the player option interacting. - * @author 'Vexia - */ -@Initializable -public final class RequestOptionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - Option._P_ASSIST.setHandler(this); - Option._P_TRADE.setHandler(this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - player.getRequestManager().request((Player) node, RequestType.forOption(option)); - return true; - } -} diff --git a/Server/src/main/java/core/game/node/Node.java b/Server/src/main/java/core/game/node/Node.java deleted file mode 100644 index 493ec9239..000000000 --- a/Server/src/main/java/core/game/node/Node.java +++ /dev/null @@ -1,269 +0,0 @@ -package core.game.node; - -import core.game.interaction.DestinationFlag; -import core.game.interaction.Interaction; -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.game.world.map.Direction; -import core.game.world.map.Location; -import core.tools.StringUtils; - -/** - * Represents a node which is anything that is interactable in Keldagrim. - * @author Emperor - */ -public abstract class Node { - - /** - * The name of the node; - */ - protected String name; - - /** - * The location. - */ - protected Location location; - - /** - * The index of the node. - */ - protected int index; - - /** - * The node's direction. - */ - protected Direction direction; - - /** - * The node's size. - */ - protected int size = 1; - - /** - * If the node is active. - */ - protected boolean active = true; - - /** - * The interaction instance. - */ - protected Interaction interaction; - - /** - * The destination flag. - */ - protected DestinationFlag destinationFlag; - - /** - * If the node is renderable. - */ - protected boolean renderable = true; - - /** - * Constructs a new {@code Node} {@code Object}. - * @param name The name. - * @param location The location. - */ - public Node(String name, Location location) { - this.name = name; - this.location = location; - } - - /** - * Casts the npc to a player. - * @return the npc. - */ - public NPC asNpc() { - return (NPC) this; - } - - /** - * Casts the player. - * @return the player. - */ - public Player asPlayer() { - return (Player) this; - } - - /** - * Casts the scenery. - * @return the object. - */ - public Scenery asScenery() { - return (Scenery) this; - } - - /** - * Casts the item. - * @return the item. - */ - public Item asItem() { - return (Item) this; - } - - /** - * Gets the node id. - * @return the id. - */ - public int getId() { - return this instanceof NPC ? ((NPC) this).getId() : this instanceof Scenery ? ((Scenery) this).getId() : this instanceof Item ? ((Item) this).getId() : -1; - } - - /** - * Gets the center location. - * @return The center location. - */ - public Location getCenterLocation() { - int offset = size >> 1; - return location.transform(offset, offset, 0); - } - - /** - * Gets the name of this node. - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Get a formated username. - * @return The username. - */ - public String getUsername() { - return StringUtils.formatDisplayName(name); - } - - /** - * 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 location. - * @return The location. - */ - public Location getLocation() { - return location; - } - - /** - * Sets the location. - * @param location The location to set. - */ - public void setLocation(Location location) { - this.location = location; - } - - /** - * Gets the direction. - * @return The direction. - */ - public Direction getDirection() { - return direction; - } - - /** - * Sets the direction. - * @param direction The direction to set. - */ - public void setDirection(Direction direction) { - this.direction = direction; - } - - /** - * Gets the size. - * @return The size. - */ - public int size() { - return size; - } - - /** - * Sets the size. - * @param size The size to set. - */ - public void setSize(int size) { - this.size = size; - } - - /** - * Gets the active. - * @return The active. - */ - public boolean isActive() { - return active; - } - - /** - * Sets the active. - * @param active The active to set. - */ - public void setActive(boolean active) { - this.active = active; - } - - /** - * Gets the interaction. - * @return The interaction. - */ - public Interaction getInteraction() { - if (interaction != null && !interaction.isInitialized()) { - interaction.setDefault(); - } - return interaction; - } - - /** - * Sets the interaction. - * @param interaction The interaction to set. - */ - public void setInteraction(Interaction interaction) { - this.interaction = interaction; - } - - /** - * Gets the destinationFlag. - * @return The destinationFlag. - */ - public DestinationFlag getDestinationFlag() { - return destinationFlag; - } - - /** - * Sets the destinationFlag. - * @param destinationFlag The destinationFlag to set. - */ - public void setDestinationFlag(DestinationFlag destinationFlag) { - this.destinationFlag = destinationFlag; - } - - /** - * Gets the renderable. - * @return The renderable. - */ - public boolean isRenderable() { - return renderable; - } - - /** - * Sets the renderable. - * @param renderable The renderable to set. - */ - public void setRenderable(boolean renderable) { - this.renderable = renderable; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/Entity.java b/Server/src/main/java/core/game/node/entity/Entity.java deleted file mode 100644 index b893242ab..000000000 --- a/Server/src/main/java/core/game/node/entity/Entity.java +++ /dev/null @@ -1,873 +0,0 @@ -package core.game.node.entity; - -import core.game.content.holiday.HolidayEvent; -import core.game.interaction.DestinationFlag; -import core.game.node.Node; -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.ImpactHandler; -import core.game.node.entity.combat.equipment.ArmourSet; -import core.game.node.entity.impl.*; -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 rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.Viewport; -import core.game.world.map.path.Path; -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 rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.world.update.UpdateMasks; - -import java.util.HashMap; -import java.util.Map; - -/** - * An entity is a movable node, such as players and NPCs. - * @author Emperor - */ -public abstract class Entity extends Node { - - /** - * The entity's properties. - */ - private final Properties properties = new Properties(this); - - /** - * The entity's update masks. - */ - private final UpdateMasks updateMasks = new UpdateMasks(); - - /** - * The walking queue. - */ - private final WalkingQueue walkingQueue = new WalkingQueue(this); - - /** - * The entity's skills. - */ - private final Skills skills = new Skills(this); - - /** - * The entity's extension classes. - */ - private final Map, Object> extensions = new HashMap, Object>(); - - /** - * The entity's attributes. - */ - private final GameAttributes attributes = new GameAttributes(); - - /** - * The entity's viewport. - */ - private final Viewport viewport = new Viewport(); - - /** - * The pulse manager. - */ - private final PulseManager pulseManager = new PulseManager(); - - /** - * The impact handler. - */ - private final ImpactHandler impactHandler = new ImpactHandler(this); - - /** - * The animator. - */ - private final Animator animator = new Animator(this); - - /** - * The teleporter. - */ - private final TeleportManager teleporter = new TeleportManager(this); - - /** - * The zone monitor. - */ - 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(); - - /** - * If the entity is invisible. - */ - private boolean invisible; - - /** - * Constructs a new {@code Entity} {@code Object}. - * @param name The name of the entity. - * @param location The location. - */ - public Entity(String name, Location location) { - super(name, location); - super.destinationFlag = DestinationFlag.ENTITY; - } - - /** - * Attempts to make the entity move one step (priority order: west, south, - * east, north). - */ - public void moveStep() { - if (locks.isMovementLocked()) { - return; - } - Path path; - if (!(path = Pathfinder.find(this, getLocation().transform(-1, 0, 0), false, Pathfinder.DUMB)).isSuccessful()) { - if (!(path = Pathfinder.find(this, getLocation().transform(0, -1, 0), false, Pathfinder.DUMB)).isSuccessful()) { - if (!(path = Pathfinder.find(this, getLocation().transform(1, 0, 0), false, Pathfinder.DUMB)).isSuccessful()) { - if (!(path = Pathfinder.find(this, getLocation().transform(0, 1, 0), false, Pathfinder.DUMB)).isSuccessful()) { - path = null; - } - } - } - } - if (path != null) { - path.walk(this); - } - } - - /** - * Initializes the entity. - */ - public void init() { - active = true; - } - - /** - * This methods gets called before the {@link #update()} method. - */ - public void tick() { - skills.pulse(); - walkingQueue.update(); - updateMasks.prepare(this); - } - - /** - * Updates the entity, this gets called in a thread pool. - */ - public void update() { - } - - /** - * Resets the entity's update flags, ...
Gets called after - * {@link #update()}. - */ - public void reset() { - updateMasks.reset(); - properties.setTeleporting(false); - } - - /** - * Removes the entity from the world. - */ - public void clear() { - active = false; - viewport.remove(this); - pulseManager.clear(); - } - - /** - * Checks if the entity is in combat. - * @return {@code True} if so. - */ - public boolean inCombat() { - return getAttribute("combat-time", 0L) > System.currentTimeMillis(); - } - - /** - * Fully restores the entity. - */ - public void fullRestore() { - skills.restore(); - } - - /** - * Called when the death task gets submitted. - * @param killer The killer of this entity. - */ - public void commenceDeath(Entity killer) { - if (HolidayEvent.getCurrent() != null) { - HolidayEvent.getCurrent().commenceDeath(killer, this); - } - } - - /** - * Finalizes the death task. - * @param killer The killer of this entity. - */ - public void finalizeDeath(Entity killer) { - skills.restore(); - skills.rechargePrayerPoints(); - impactHandler.getImpactQueue().clear(); - impactHandler.setDisabledTicks(10); - stateManager.reset(); - removeAttribute("combat-time"); - face(null); - if (HolidayEvent.getCurrent() != null) { - HolidayEvent.getCurrent().finalizeDeath(killer, this); - } - //Check if it's a Loar shade and transform back into the shadow version. - if(this.getId() == 1240 || this.getId() == 1241){ - this.asNpc().transform(1240); - } - } - - /** - * Updates the location of an entity. - * @param last the last location. - */ - public void updateLocation(Location last) { - - } - - /** - * Checks if multiway combat zone rules should be ignored. - * @param victim The victim. - * @return {@code True} if this entity can attack regardless of multiway - * combat zone. - */ - public boolean isIgnoreMultiBoundaries(Entity victim) { - return false; - } - - /** - * Should this entity prevent the mover from moving through it? - */ - public boolean shouldPreventStacking(Entity mover) { - return false; - } - - /** - * Checks an impact before receiving it. - */ - public void checkImpact(BattleState state) { - getProperties().getCombatPulse().setLastReceivedAttack(GameWorld.getTicks()); - int ticks = GameWorld.getTicks() - getProperties().getCombatPulse().getLastSentAttack(); - if (ticks > 10 && this instanceof NPC && ((NPC) this).getDefinition().getConfiguration("safespot", false)) { - Pathfinder.find(state.getAttacker(), getLocation()).walk(state.getAttacker()); - Pathfinder.find(state.getVictim(), state.getAttacker().getLocation()).walk(state.getVictim()); - if (ticks > 40) { - state.getAttacker().moveStep(); - state.getAttacker().getProperties().getCombatPulse().stop(); - state.getVictim().moveStep(); - state.getVictim().getProperties().getCombatPulse().stop(); - } - } - } - - /** - * Handles an impact. - * @param entity The entity. - * @param state The battle state. - */ - public void onImpact(final Entity entity, BattleState 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); - } - } - } - - /** - * Handles the first attack. - * @param target the target. - */ - public void onAttack(final Entity target) { - - } - - /** - * Method used to attack a node. - * @param node the node. - */ - public void attack(final Node node) { - getPulseManager().clear("interaction:attack:" + node.hashCode()); - getProperties().getCombatPulse().attack(node); - } - - /** - * Checks if the entity can move to the destination. - * @param destination the destination. - * @return {@code True} if so. - */ - public boolean canMove(Location destination) { - return true; - } - - /** - * Teleports the entity to a location. - * @param location the location. - */ - public void teleport(Location location) { - getProperties().setTeleportLocation(location); - } - - /** - * Teleports the entity. - * @param location the location. - * @param ticks the ticks. - */ - public void teleport(final Location location, int ticks) { - GameWorld.getPulser().submit(new Pulse(ticks, this) { - @Override - public boolean pulse() { - teleport(location); - return true; - } - }); - } - - /** - * Locks the entity. - */ - public void lock() { - locks.lock(); - } - - /** - * Locks the entity from using any actions. - * @param time The time (in game ticks) to lock. - */ - public void lock(int time) { - locks.lock(time); - } - - /** - * Unlocks the default reward locks. - */ - public void unlock() { - locks.unlock(); - } - - /** - * Checks if the entity is using the protection prayer for the given style. - * @param style The combat style. - * @return {@code True} if so. - */ - public abstract boolean hasProtectionPrayer(CombatStyle style); - - /** - * Gets the dragonfire protection value. - * @param fire if a fire attack. - * @return The value. - */ - public abstract int getDragonfireProtection(boolean fire); - - /** - * Checks if this entity is attackable by the attacking entity. - * @param entity The attacking entity. - * @param style The combat style used. - * @return {@code True} if the attacking entity can attack this entity. - */ - public boolean isAttackable(Entity entity, CombatStyle style) { - if (DeathTask.isDead(this)) { - return false; - } - if (!entity.getZoneMonitor().continueAttack(this, style)) { - return false; - } - return true; - } - - /** - * Registers a new graphics update flag to the update masks. - * @param graphics The graphics. - * @return {@code True} if succesful. - */ - public boolean graphics(Graphics graphics) { - return animator.graphics(graphics); - } - - /** - * Registers a new animation update flag to the update masks. - * @param animation The animation. - * @return {@code True} if succesful. - */ - public boolean animate(Animation animation) { - return animator.animate(animation); - } - - /** - * Registers a new graphics update flag. - * @param graphics the graphics. - * @param delay the delay. - * @return {@code True} if so. - */ - public boolean graphics(final Graphics graphics, int delay) { - GameWorld.getPulser().submit(new Pulse(delay, this) { - @Override - public boolean pulse() { - graphics(graphics); - return true; - } - }); - return true; - } - - /** - * Checks if an entity can continue it's attack. - * @param target the target. - * @param style the style. - * @return {@code True} if so. - */ - public boolean continueAttack(Entity target, CombatStyle style) { - return true; - } - - /** - * Registers a new animation update flag to the update masks. - * @param animation The animation. - * @param delay the delay - * @return {@code True} if succesful. - */ - public boolean animate(final Animation animation, int delay) { - GameWorld.getPulser().submit(new Pulse(delay, this) { - @Override - public boolean pulse() { - animate(animation); - return true; - } - }); - return true; - } - - /** - * Sends the impact. - * @param state the state. - */ - public void sendImpact(BattleState state) { - getProperties().getCombatPulse().setLastSentAttack(GameWorld.getTicks()); - } - - /** - * Checks if the target can be selected. - * @param target the target. - * @return {@code True} if so. - */ - public boolean canSelectTarget(Entity target) { - return true; - } - - /** - * Registers a new animation & graphic update flag to the update masks. - * @param animation The animation. - * @param graphics The graphics. - * @return {@code True} if successful. - */ - public boolean visualize(Animation animation, Graphics graphics) { - return animator.animate(animation, graphics); - } - - /** - * Temporarily faces an entity. - * @param entity The entity to face. - * @param ticks The ticks to face the entity. - * @return {@code True} if successful. - */ - public boolean faceTemporary(Entity entity, int ticks) { - return faceTemporary(entity, null, ticks); - } - - /** - * Temporarily faces an entity. - * @param entity The entity to face. - * @param reset The entity to face when the pulse has passed. - * @param ticks The ticks to face the entity. - * @return {@code True} if successful. - */ - public boolean faceTemporary(Entity entity, final Entity reset, int ticks) { - if (face(entity)) { - GameWorld.getPulser().submit(new Pulse(ticks + 1, this) { - @Override - public boolean pulse() { - face(reset); - return true; - } - }); - return true; - } - return false; - } - - /** - * Gets the formatted hit. - * @param state the state. - * @param hit the hit. - * @return {@code True} if so. - */ - public double getFormattedHit(BattleState state, int hit) { - if (state.getAttacker() == null || state.getVictim() == null || state.getStyle() == null) { - return hit; - } - Entity entity = state.getAttacker(); - Entity victim = state.getVictim(); - CombatStyle type = state.getStyle(); - if (state.getArmourEffect() != ArmourSet.VERAC && !entity.isIgnoreProtection(type) && victim.hasProtectionPrayer(type)) { - return hit *= (entity instanceof Player && victim instanceof Player) ? 0.6 : 0; - } - return hit; - } - - /** - * Checks if this entity ignores protection prayers for the given combat style. - * @param style The combat style used. - * @return {@code True} if the entity can hit through protection prayers. - */ - public boolean isIgnoreProtection(CombatStyle style) { - return false; - } - - /** - * Starts the death for an npc. - * @param killer the killer. - */ - public void startDeath(Entity killer) { - if (zoneMonitor.startDeath(this, killer)) { - DeathTask.startDeath(this, killer); - } - } - - /** - * Casts the player type. - * @return the player. - */ - public Player asPlayer() { - return (Player) this; - } - - /** - * Checks if the entity is instance of a player. - * @return {@code True} if so. - */ - public boolean isPlayer() { - return this instanceof Player; - } - - /** - * Registers a new face entity update flag to the update masks. - * @param entity The entity to face. - * @return {@code True} if succesful. - */ - public abstract boolean face(Entity entity); - - /** - * 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); - - /** - * Registers a new force chat update flag to the update masks. - * @param string The string. - * @return {@code True} if succesful. - */ - public abstract boolean sendChat(String string); - - /** - * Gets the current combat swing handler. - * @param swing If this method is called when actually performing a combat - * swing. - * @return The current combat swing handler to use. - */ - public abstract CombatSwingHandler getSwingHandler(boolean swing); - - /** - * Checks if the entity is immune to poison. - * @return {@code True} if the entity is immune to poison. - */ - public abstract boolean isPoisonImmune(); - - /** - * Sends the chat on a tick. - * @param string the string. - * @param ticks the ticks. - */ - public void sendChat(final String string, int ticks) { - GameWorld.getPulser().submit(new Pulse(ticks, this) { - @Override - public boolean pulse() { - sendChat(string); - return true; - } - }); - } - - /** - * Gets the level mod. - * @param entity the entity. - * @param victim the victim. - * @return the levelMod. - */ - public double getLevelMod(Entity entity, Entity victim) { - return 0; - } - - /** - * Gets the reward locks. - * @return The reward locks. - */ - public ActionLocks getLocks() { - return locks; - } - - /** - * Gets the client index of the entity. - * @return The client index. - */ - public int getClientIndex() { - return index; - } - - /** - * Gets the properties. - * @return The properties. - */ - public Properties getProperties() { - return properties; - } - /** - * Gets the updateMasks. - * @return The updateMasks. - */ - public UpdateMasks getUpdateMasks() { - return updateMasks; - } - - /** - * Adds an extension. - * @param clazz The class type. - * @param object The object. - */ - public void addExtension(Class clazz, Object object) { - extensions.put(clazz, object); - } - - /** - * Gets an extension. - * @param clazz The class type. - * @return The object. - */ - @SuppressWarnings("unchecked") - public T getExtension(Class clazz) { - return (T) extensions.get(clazz); - } - - /** - * Gets an extension. - * @param clazz The class type. - * @param fail The object to return if the extension wasn't added. - * @return The object. - */ - @SuppressWarnings("unchecked") - public T getExtension(Class clazz, T fail) { - T extension = (T) extensions.get(clazz); - if (extension == null) { - return fail; - } - return extension; - } - - /** - * Removes the extension. - * @param clazz The class type. - */ - public void removeExtension(Class clazz) { - extensions.remove(clazz); - } - - /** - * Gets the players attributes. - * @return the attributes. - */ - public Map getAttributes() { - return attributes.getAttributes(); - } - - /** - * Sets an attribute value. - * @param key The attribute name. - * @param value The attribute value. - */ - public void setAttribute(String key, Object value) { - attributes.setAttribute(key, value); - } - - public void setExpirableAttribute(String key, Object value, Long timeToLive){ - attributes.setExpirableAttribute(key,value,timeToLive); - } - - /** - * Gets an attribute. - * @param key The attribute name. - * @return The attribute value. - */ - public T getAttribute(String key) { - return attributes.getAttribute(key); - } - - /** - * Increments an attribute - * @param key The attribute name. - */ - public void incrementAttribute(String key) { - incrementAttribute(key, 1); - } - - /** - * Increments an attribute - * @param key The attribute name. - */ - public void incrementAttribute(String key, int amount) { - attributes.setAttribute(key, attributes.getAttribute(key, 0) + amount); - } - - /** - * Gets an attribute. - * @param string The attribute name. - * @param fail The value to return if the attribute is null. - * @return The attribute value, or the fail argument when null. - */ - public T getAttribute(String string, T fail) { - return attributes.getAttribute(string, fail); - } - - /** - * Removes an attribute. - * @param string The attribute name. - */ - public void removeAttribute(String string) { - attributes.removeAttribute(string); - } - - /** - * Gets the game attributes instance. - * @return The game attributes. - */ - public GameAttributes getGameAttributes() { - return attributes; - } - - /** - * Gets the walkingQueue. - * @return The walkingQueue. - */ - public WalkingQueue getWalkingQueue() { - return walkingQueue; - } - - /** - * Gets the viewport. - * @return The viewport. - */ - public Viewport getViewport() { - return viewport; - } - - /** - * Gets the skills. - * @return The skills. - */ - public Skills getSkills() { - return skills; - } - - /** - * Gets the pulseManager. - * @return The pulseManager. - */ - public PulseManager getPulseManager() { - return pulseManager; - } - - /** - * Gets the impactHandler. - * @return The impactHandler. - */ - public ImpactHandler getImpactHandler() { - return impactHandler; - } - - /** - * @return the animator. - */ - public Animator getAnimator() { - return animator; - } - - /** - * Gets the Teleporter - * @return the Teleporter - */ - public TeleportManager getTeleporter() { - return teleporter; - } - - /** - * Gets the zoneMonitor. - * @return The zoneMonitor. - */ - public ZoneMonitor getZoneMonitor() { - return zoneMonitor; - } - - /** - * Gets the stateManager. - * @return The stateManager. - */ - public StateManager getStateManager() { - return stateManager; - } - - /** - * Checks if we have fire resistance. - * @return {@code True} if so. - */ - public boolean hasFireResistance() { - return getAttribute("fire:immune",0) >= GameWorld.getTicks(); - } - - /** - * Checks if the entity is teleblocked. - * @return {@code True} if so. - */ - public boolean isTeleBlocked() { - return stateManager.hasState(EntityState.TELEBLOCK); - } - - /** - * Gets the invisible value. - * @return The invisible. - */ - public boolean isInvisible() { - return invisible; - } - - /** - * Sets the invisible value. - * @param invisible The invisible to set. - */ - public void setInvisible(boolean invisible) { - this.invisible = invisible; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/handlers/DragonfireSwingHandler.java b/Server/src/main/java/core/game/node/entity/combat/handlers/DragonfireSwingHandler.java deleted file mode 100644 index 6ac038ad2..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/handlers/DragonfireSwingHandler.java +++ /dev/null @@ -1,220 +0,0 @@ -package core.game.node.entity.combat.handlers; - -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 rs09.game.node.entity.combat.CombatSwingHandler; -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.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; -import core.game.node.item.Item; -import rs09.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.tools.RandomFunction; - -/** - * Handles dragonfire combat. - * @author Emperor - */ -public class DragonfireSwingHandler extends CombatSwingHandler { - - /** - * If the NPC has to be in melee range. - */ - private boolean meleeRange; - - /** - * The maximum hit. - */ - private int maximumHit; - - /** - * The attack data. - */ - private SwitchAttack attack; - - /** - * 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; - } - - /** - * 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; - } - - @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 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 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 - 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 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 calculateDefence(Entity victim, Entity attacker) { - return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); - } - - @Override - public double getSetMultiplier(Entity e, int skillId) { - return 1.0; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/combat/special/ClobberSpecialHandler.java b/Server/src/main/java/core/game/node/entity/combat/special/ClobberSpecialHandler.java deleted file mode 100644 index ee8969075..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/special/ClobberSpecialHandler.java +++ /dev/null @@ -1,77 +0,0 @@ -package core.game.node.entity.combat.special; - -import core.plugin.Initializable; -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 rs09.game.node.entity.combat.handlers.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.Plugin; -import core.tools.RandomFunction; - -/** - * Represents the dragon axe's clobbrer special handler. - * @author Emperor - * @version 1.0 - */ -@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) { - 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) { - if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { - return -1; - } - state.setStyle(CombatStyle.MELEE); - int hit = 0; - if (entity instanceof Player) { - entity.asPlayer().sendChat("Chop chop!"); - } - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.05, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); - if (hit > 9) { - int amount = hit / 10; - victim.getSkills().updateLevel(Skills.DEFENCE, -amount, 0); - victim.getSkills().updateLevel(Skills.MAGIC, -amount, 0); - } - } - entity.getSkills().updateLevel(Skills.WOODCUTTING, 3); - state.setEstimatedHit(hit); - return 1; - } - - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - entity.visualize(ANIMATION, GRAPHIC); - } -} diff --git a/Server/src/main/java/core/game/node/entity/combat/special/HealingBladeSpecialHandler.java b/Server/src/main/java/core/game/node/entity/combat/special/HealingBladeSpecialHandler.java deleted file mode 100644 index 40dd3961b..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/special/HealingBladeSpecialHandler.java +++ /dev/null @@ -1,81 +0,0 @@ -package core.game.node.entity.combat.special; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.handlers.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; - -/** - * Handles the healing blade special attack. - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class HealingBladeSpecialHandler extends MeleeSwingHandler implements Plugin { - - /** - * The special energy required. - */ - private static final int SPECIAL_ENERGY = 50; - - /** - * The attack animation. - */ - private static final Animation ANIMATION = new Animation(7071, Priority.HIGH); - - /** - * The graphic. - */ - private static final Graphics GRAPHIC = new Graphics(1220); - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - CombatStyle.MELEE.getSwingHandler().register(11698, this); - CombatStyle.MELEE.getSwingHandler().register(13452, this); - return this; - } - - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { - return -1; - } - 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)); - } - 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) { - entity.visualize(ANIMATION, GRAPHIC); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/special/QuickSmashSpecialHandler.java b/Server/src/main/java/core/game/node/entity/combat/special/QuickSmashSpecialHandler.java deleted file mode 100644 index 2f110af06..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/special/QuickSmashSpecialHandler.java +++ /dev/null @@ -1,86 +0,0 @@ -package core.game.node.entity.combat.special; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.handlers.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 rs09.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; - -/** - * Handles the granite maul special attack. - * @author Emperor - */ -@Initializable -public final class QuickSmashSpecialHandler extends MeleeSwingHandler implements Plugin { - - /** - * The special energy required. - */ - private static final int SPECIAL_ENERGY = 50; - - /** - * The attack animation. - */ - private static final Animation ANIMATION = new Animation(1667, Priority.HIGH); - - /** - * The graphic. - */ - private static final Graphics GRAPHIC = new Graphics(340, 96); - - @Override - public Object fireEvent(String identifier, Object... args) { - switch (identifier) { - case "instant_spec": - return true; - } - return null; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - CombatStyle.MELEE.getSwingHandler().register(4153, this); - CombatStyle.MELEE.getSwingHandler().register(14792, this); - return this; - } - - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - Player p = (Player) entity; - if (victim == null) { - victim = p.getProperties().getCombatPulse().getLastVictim(); - if (victim == null || GameWorld.getTicks() - p.getAttribute("combat-stop", -1) > 2 || !MeleeSwingHandler.Companion.canMelee(p, victim, 1)) { - p.getPacketDispatch().sendMessage("Warning: Since the maul's special is an instant attack, it will be wasted when used "); - p.getPacketDispatch().sendMessage("on a first strike."); - 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.)); - } - victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MELEE); - entity.asPlayer().getAudioManager().send(new Audio(2715), true); - return 1; - } - - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - entity.visualize(ANIMATION, GRAPHIC); - victim.animate(victim.getProperties().getDefenceAnimation()); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/special/RampageSpecialHandler.java b/Server/src/main/java/core/game/node/entity/combat/special/RampageSpecialHandler.java deleted file mode 100644 index 81b14cd26..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/special/RampageSpecialHandler.java +++ /dev/null @@ -1,77 +0,0 @@ -package core.game.node.entity.combat.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 rs09.game.node.entity.combat.handlers.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; - -/** - * Handles the Rampage special attack. - * @author Emperor - */ -@Initializable -public final class RampageSpecialHandler 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(1056, Priority.HIGH); - - /** - * The graphic. - */ - private static final Graphics GRAPHIC = new Graphics(246); - - @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(1377, 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("Raarrrrrgggggghhhhhhh!"); - p.visualize(ANIMATION, GRAPHIC); - @SuppressWarnings("unused") - int boost = 0; - for (int i = 0; i < 6; i++) { - if (i == 2 || i == 3 || i == 5) { - continue; - } - 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); - return -1; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/special/SliceAndDiceSpecialHandler.java b/Server/src/main/java/core/game/node/entity/combat/special/SliceAndDiceSpecialHandler.java deleted file mode 100644 index 2d0ed1c20..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/special/SliceAndDiceSpecialHandler.java +++ /dev/null @@ -1,106 +0,0 @@ -package core.game.node.entity.combat.special; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import rs09.game.node.entity.combat.handlers.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; -import core.tools.RandomFunction; - -/** - * Handles the Dragon claws special attack "Slice and Dice". - * @author Emperor - * - */ -@Initializable -public final class SliceAndDiceSpecialHandler extends MeleeSwingHandler implements Plugin { - - /** - * The special energy required. - */ - private static final int SPECIAL_ENERGY = 50; - - /** - * The attack animation. - */ - private static final Animation ANIMATION = new Animation(10961, Priority.HIGH); - - /** - * The graphic. - */ - private static final Graphics GRAPHIC = new Graphics(1950); - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - CombatStyle.MELEE.getSwingHandler().register(14484, this); - CombatStyle.MELEE.getSwingHandler().register(14486, this); - return this; - } - - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { - return -1; - } - int maximum = calculateHit(entity, victim, 1.0); - int[] hits = new int[] {0, 1}; - int hit = getHit(entity, victim, maximum); - if (hit > 0) { - hits = new int[] {hit, hit / 2, (hit / 2) / 2, (hit / 2) - ((hit / 2) / 2)}; - } else { - hit = getHit(entity, victim, maximum); - if (hit > 0) { - hits = new int[] {0, hit, hit / 2, hit - (hit / 2)}; - } else { - hit = getHit(entity, victim, maximum); - if (hit > 0) { - hits = new int[] {0, 0, hit / 2, (hit / 2) + 10}; - } else { - hit = getHit(entity, victim, (int) (maximum * 1.5)); - if (hit > 0) { - hits = new int[] {0, 0, 0, hit}; - } else { - hits = new int[] {0, RandomFunction.random(2)}; - } - } - } - } - BattleState[] states = new BattleState[hits.length]; - for (int i = 0; i < hits.length; i++) { - BattleState s = states[i] = new BattleState(entity, victim); - s.setStyle(CombatStyle.MELEE); - s.setEstimatedHit(hits[i]); - } - state.setTargets(states); - return 1; - } - - /** - * Gets the current hit. - * @param entity The attacking entity. - * @param victim The victim. - * @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); - } - return 0; - } - - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - entity.visualize(ANIMATION, GRAPHIC); - } -} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/CrumbleUndead.java b/Server/src/main/java/core/game/node/entity/combat/spell/CrumbleUndead.java deleted file mode 100644 index 61baf8c34..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/spell/CrumbleUndead.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.game.node.entity.combat.spell; - -import core.game.node.entity.skill.magic.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.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; -import core.game.node.entity.impl.Projectile; -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.SpellBookManager.SpellBook; -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 crumble undead spell. - * @author Emperor - * @version 1.0 - */ -@Initializable -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)); - } - - @Override - public boolean cast(Entity entity, Node target) { - NPC npc = target instanceof NPC ? (NPC) target : null; - if (npc == null || npc.getTask() == null || !npc.getTask().undead) { - ((Player) entity).getPacketDispatch().sendMessage("This spell only affects the undead."); - return false; - } - return super.cast(entity, target); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.MODERN.register(22, this); - return this; - } - - @Override - public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { - return type.getImpactAmount(entity, victim, 0); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/CurseSpells.java b/Server/src/main/java/core/game/node/entity/combat/spell/CurseSpells.java deleted file mode 100644 index 4356c7853..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/spell/CurseSpells.java +++ /dev/null @@ -1,201 +0,0 @@ -package core.game.node.entity.combat.spell; - -import api.ContentAPI; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -import core.game.node.entity.combat.equipment.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; - -/** - * Represents the curse spells. - * @author Emperor - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CurseSpells extends CombatSpell { - - /** - * The start graphic for Earth strike. - */ - private static final Graphics CONFUSE_START = new Graphics(102, 96); - - /** - * The projectile for Earth strike. - */ - private static final Projectile CONFUSE_PROJECTILE = Projectile.create((Entity) null, null, 103, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Earth strike. - */ - private static final Graphics CONFUSE_END = new Graphics(104, 96); - - /** - * The start graphic for Earth strike. - */ - private static final Graphics WEAKEN_START = new Graphics(105, 96); - - /** - * The projectile for Earth strike. - */ - private static final Projectile WEAKEN_PROJECTILE = Projectile.create((Entity) null, null, 106, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Earth strike. - */ - private static final Graphics WEAKEN_END = new Graphics(107, 96); - - /** - * The start graphic for Earth strike. - */ - private static final Graphics CURSE_START = new Graphics(108, 96); - - /** - * The projectile for Earth strike. - */ - private static final Projectile CURSE_PROJECTILE = Projectile.create((Entity) null, null, 109, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Earth strike. - */ - private static final Graphics CURSE_END = new Graphics(110, 96); - - /** - * The start graphic for Earth strike. - */ - private static final Graphics VULNER_START = new Graphics(167, 96); - - /** - * The projectile for Earth strike. - */ - private static final Projectile VULNER_PROJECTILE = Projectile.create((Entity) null, null, 168, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Earth strike. - */ - private static final Graphics VULNER_END = new Graphics(169, 96); - - /** - * The start graphic for Earth strike. - */ - private static final Graphics ENFEEBLE_START = new Graphics(170, 96); - - /** - * The projectile for Earth strike. - */ - private static final Projectile ENFEEBLE_PROJECTILE = Projectile.create((Entity) null, null, 171, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Earth strike. - */ - private static final Graphics ENFEEBLE_END = new Graphics(172, 96); - - /** - * The start graphic for Earth strike. - */ - private static final Graphics STUN_START = new Graphics(173, 96); - - /** - * The projectile for Earth strike. - */ - private static final Projectile STUN_PROJECTILE = Projectile.create((Entity) null, null, 174, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Earth strike. - */ - private static final Graphics STUN_END = new Graphics(107, 96); - - /** - * The cast animation. - */ - private static final Animation LOW_ANIMATION = new Animation(716, Priority.HIGH); - - /** - * The cast animation. - */ - private static final Animation HIGH_ANIMATION = new Animation(729, Priority.HIGH); - - /** - * Constructs a new {@code EarthSpell} {@Code Object} - */ - public CurseSpells() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code CurseSpells} {@code Object}. - * @param type the type. - * @param level the level. - * @param sound the sound. - * @param start the start. - * @param projectile the projectile. - * @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); - } - - @Override - public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { - return 1; - } - - @Override - public void fireEffect(Entity entity, Entity victim, BattleState state) { - if (state.getEstimatedHit() == -1) { - return; - } - state.setEstimatedHit(-2); - switch (getType()) { - case CONFUSE: - victim.getSkills().drainLevel(Skills.ATTACK, 0.05, 0.05); - break; - case WEAKEN: - victim.getSkills().drainLevel(Skills.STRENGTH, 0.05, 0.05); - break; - case CURSE: - victim.getSkills().drainLevel(Skills.DEFENCE, 0.05, 0.05); - break; - case VULNERABILITY: - victim.getSkills().drainLevel(Skills.DEFENCE, 0.10, 0.10); - break; - case ENFEEBLE: - victim.getSkills().drainLevel(Skills.STRENGTH, 0.10, 0.10); - break; - case STUN: - victim.getSkills().drainLevel(Skills.ATTACK, 0.10, 0.10); - break; - default: - } - } - - @Override - public void addExperience(Entity entity, int hit) { - entity.getSkills().addExperience(Skills.MAGIC, getExperience()); - } - - @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))); - return this; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/GodSpells.java b/Server/src/main/java/core/game/node/entity/combat/spell/GodSpells.java deleted file mode 100644 index 856bc40e5..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/spell/GodSpells.java +++ /dev/null @@ -1,193 +0,0 @@ -package core.game.node.entity.combat.spell; - -import core.cache.def.impl.ItemDefinition; -import core.game.container.impl.EquipmentContainer; -import core.plugin.Initializable; -import core.game.node.entity.skill.magic.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.CombatSpell; -import core.game.node.entity.combat.equipment.SpellType; -import core.game.node.entity.impl.Projectile; -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.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; -import org.rs09.consts.Items; - -/** - * Handles the god spells. - * @author Emperor - * @author 'Vexia - */ -@Initializable -public final class GodSpells extends CombatSpell { - - /** - * The spell names. - */ - private static final String[] NAMES = new String[] { "Saradomin strike", "Guthix claws", "Flames of Zamorak" }; - - /** - * The start graphic for Air strike. - */ - private static final Graphics SARA_START = null; - - /** - * The projectile for Air strike. - */ - private static final Projectile SARA_PROJECTILE = null; - - /** - * The end graphic for Air strike. - */ - private static final Graphics SARA_END = new Graphics(76, 0); - - /** - * The start graphic for Air strike. - */ - private static final Graphics GUTHIX_START = null; - - /** - * The projectile for Air strike. - */ - private static final Projectile GUTHIX_PROJECTILE = null; - - /** - * The end graphic for Air strike. - */ - private static final Graphics GUTHIX_END = new Graphics(77, 0); - - /** - * The start graphic for Air strike. - */ - private static final Graphics ZAM_START = null; - - /** - * The projectile for Air strike. - */ - private static final Projectile ZAM_PROJECTILE = null; - - /** - * The end graphic for Air strike. - */ - private static final Graphics ZAM_END = new Graphics(78, 0); - - /** - * The cast animation. - */ - private static final Animation ANIMATION = new Animation(811, Priority.HIGH); - - /** - * Constructs a new {@code AirSpell} {@Code Object} - */ - public GodSpells() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code GodSpells} {@code Object}. - * @param type the type. - * @param sound the sound. - * @param start the start. - * @param projectile the projectile. - * @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); - } - - @Override - public boolean meetsRequirements(Entity caster, boolean message, boolean remove) { - if (caster instanceof NPC) { - return true; - } - 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; - } - 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."); - return false; - } - - if (staffId != required && !(index == 1 && staffId == Items.VOID_KNIGHT_MACE_8841)) { - if (message) { - ((Player) caster).getPacketDispatch().sendMessage("You need to wear a " + ItemDefinition.forId(required).getName() + " to cast this spell."); - } - return false; - } - } - return super.meetsRequirements(caster, message, remove); - } - - @Override - public void visualize(Entity entity, Node target) { - super.visualize(entity, target); - if (entity instanceof NPC) { - NPC n = (NPC) entity; - if (n.getId() > 911 && n.getId() < 915 || (n.getId() > 906 && n.getId() < 912)) { - n.getAnimator().forceAnimation(n.getProperties().getAttackAnimation()); - } - } - } - - @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; - } - Player p = (Player) entity; - if (p.getSavedData().getActivityData().getGodCasts()[index] < 100) { - p.getSavedData().getActivityData().getGodCasts()[index]++; - if (p.getSavedData().getActivityData().getGodCasts()[index] >= 100) { - p.sendMessage("You can now cast " + NAMES[index] + " outside the Arena."); - } - } - } - super.visualizeImpact(entity, target, state); - } - - @Override - public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { - return getType().getImpactAmount(entity, victim, 0); - } - - @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))); - return this; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/IbanBlast.java b/Server/src/main/java/core/game/node/entity/combat/spell/IbanBlast.java deleted file mode 100644 index 7665ff3d6..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/spell/IbanBlast.java +++ /dev/null @@ -1,53 +0,0 @@ -package core.game.node.entity.combat.spell; - -import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.skill.magic.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.CombatSpell; -import core.game.node.entity.combat.equipment.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.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the iban blast spell. - * @author Emperor - * @version 1.0 - */ -@Initializable -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)); - } - - @Override - public boolean cast(Entity entity, Node target) { - if (((Player) entity).getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId() != 1409) { - ((Player) entity).getPacketDispatch().sendMessage("You need to wear Iban's staff to cast this spell."); - return false; - } - return super.cast(entity, target); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.MODERN.register(29, this); - return this; - } - - @Override - public int getMaximumImpact(Entity entity, Entity victim, BattleState state) { - return type.getImpactAmount(entity, victim, 0); - } -} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/IceSpells.java b/Server/src/main/java/core/game/node/entity/combat/spell/IceSpells.java deleted file mode 100644 index 9f4e0b757..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/spell/IceSpells.java +++ /dev/null @@ -1,163 +0,0 @@ -package core.game.node.entity.combat.spell; - -import java.util.List; - -import core.game.node.entity.skill.magic.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.CombatSpell; -import core.game.node.entity.combat.equipment.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 rs09.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); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/MiasmicSpells.java b/Server/src/main/java/core/game/node/entity/combat/spell/MiasmicSpells.java deleted file mode 100644 index 5dbbe9df5..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/spell/MiasmicSpells.java +++ /dev/null @@ -1,180 +0,0 @@ - package core.game.node.entity.combat.spell; - -import java.util.List; - -import core.game.container.impl.EquipmentContainer; -import core.plugin.Initializable; -import core.game.node.entity.skill.magic.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.CombatSpell; -import core.game.node.entity.combat.equipment.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 rs09.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Plugin; - -/** - * Handles the Miasmic spells that are a part of the Ancient spellbook. - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class MiasmicSpells extends CombatSpell { - - /** - * The start graphic for Miasmic rush. - */ - private static final Graphics RUSH_START = new Graphics(1845, 0, 15); - - /** - * The end graphic for Miasmic rush. - */ - private static final Graphics RUSH_END = new Graphics(1847, 40); - - /** - * The start graphic for Miasmic rush. - */ - private static final Graphics BURST_START = new Graphics(1848, 0); - - /** - * The end graphic for Miasmic barrage. - */ - private static final Graphics BURST_END = new Graphics(1849, 20, 30); - - /** - * The start graphic for Miasmic rush. - */ - private static final Graphics BLITZ_START = new Graphics(1850, 15); - - /** - * The end graphic for Miasmic rush. - */ - private static final Graphics BLITZ_END = new Graphics(1851, 0); - - /** - * The start graphic for Miasmic rush. - */ - private static final Graphics BARRAGE_START = new Graphics(1853, 0); - - /** - * The end graphic for Miasmic barrage. - */ - private static final Graphics BARRAGE_END = new Graphics(1854, 0, 30); - - /** - * The item ids of the staves able to cast Miasmic spells. - */ - private static final int[] VALID_STAFF_IDS = { 13867, 13869, 13841, 13843 }; - - /** - * Constructs a new {@code MiasmicSpells} {@code Object}. - */ - public MiasmicSpells() { - } - - /** - * Constructs a new {@code MiasmicSpells} {@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 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); - } - - @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) { - super.visualizeImpact(entity, target, state); - } - - @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))); - 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); - } - } - - /** - * Checks if a valid staff is equipped. - * @param entity The attacking entity. - * @return {@code True} if so. - */ - public boolean validStaffEquipped(Entity entity){ - for(int i = 0; i < VALID_STAFF_IDS.length; i++){ - if(((Player) entity).getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId() == VALID_STAFF_IDS[i]){ - return true; - } - } - return false; - } - - @Override - public boolean cast(Entity entity, Node target) { - if (!validStaffEquipped(entity) && !GameWorld.getSettings().isDevMode()) { - ((Player) entity).getPacketDispatch().sendMessage("You need to be wielding Zuriel's staff in order to cast this spell."); - return false; - } - if (!meetsRequirements(entity, true, false)) { - return false; - } - return super.cast(entity, target); - } - - @Override - public BattleState[] getTargets(Entity entity, Entity target) { - if (animation.getId() == 10513 || animation.getId() == 10524 - || !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) { - int add = 9; - if(animation.getId() == 10524 || animation.getId() == 10516){ - add = 6; - } - if(animation.getId() == 10513){ - add = 4; - } - return getType().getImpactAmount(entity, victim, add); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/ShadowSpells.java b/Server/src/main/java/core/game/node/entity/combat/spell/ShadowSpells.java deleted file mode 100644 index 325f949f1..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/spell/ShadowSpells.java +++ /dev/null @@ -1,128 +0,0 @@ -package core.game.node.entity.combat.spell; - -import java.util.List; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.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.CombatSpell; -import core.game.node.entity.combat.equipment.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; - -/** - * Handles the Shadow spells from the Ancient spellbook. - * @author Emperor - */ -@Initializable -public final class ShadowSpells extends CombatSpell { - - /** - * The projectile for Shadow rush. - */ - private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 378, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Shadow rush. - */ - private static final Graphics RUSH_END = new Graphics(379, 96); - - /** - * The projectile for Shadow burst. - */ - private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 380, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Shadow burst. - */ - private static final Graphics BURST_END = new Graphics(381, 0); - - /** - * The end graphic for Shadow blitz. - */ - private static final Graphics BLITZ_END = new Graphics(382, 96); - - /** - * The end graphic for Shadow barrage. - */ - private static final Graphics BARRAGE_END = new Graphics(383, 0); - - /** - * Constructs a new {@code ShadowSpells} {@code Object}. - */ - public ShadowSpells() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code ShadowSpells} {@Code Object} - * @param type The spell type. - * @param level The level requirement. - * @param sound The casting sound. - * @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 ShadowSpells(SpellType type, int level, double baseExperience, int sound, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { - super(type, SpellBook.ANCIENT, level, baseExperience, sound, impactSound, anim, start, projectile, end, runes); - } - - @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))); - return this; - } - - @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 fireEffect(Entity entity, Entity victim, BattleState state) { - if (state.getEstimatedHit() > -1) { - int level = victim.getSkills().getStaticLevel(Skills.ATTACK); - victim.getSkills().updateLevel(Skills.ATTACK, (int) -(level * 0.1), (int) (level - (level * 0.1))); - } - } - - @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, 2); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/combat/spell/SmokeSpells.java b/Server/src/main/java/core/game/node/entity/combat/spell/SmokeSpells.java deleted file mode 100644 index b8929ead5..000000000 --- a/Server/src/main/java/core/game/node/entity/combat/spell/SmokeSpells.java +++ /dev/null @@ -1,137 +0,0 @@ -package core.game.node.entity.combat.spell; - -import java.util.List; - -import core.game.node.entity.skill.magic.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.CombatSpell; -import core.game.node.entity.combat.equipment.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.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; - -/** - * Handles the Smoke spells from the Ancient spellbook. - * @author Emperor - */ -@Initializable -public final class SmokeSpells extends CombatSpell { - - /** - * The projectile for Smoke rush. - */ - private static final Projectile RUSH_PROJECTILE = Projectile.create((Entity) null, null, 384, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Smoke rush. - */ - private static final Graphics RUSH_END = new Graphics(385, 96); - - /** - * The projectile for Smoke burst. - */ - private static final Projectile BURST_PROJECTILE = Projectile.create((Entity) null, null, 386, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Smoke burst. - */ - private static final Graphics BURST_END = new Graphics(387, 0); - - /** - * The projectile for Smoke blitz. - */ - private static final Projectile BLITZ_PROJECTILE = Projectile.create((Entity) null, null, 389, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Smoke blitz. - */ - private static final Graphics BLITZ_END = new Graphics(388, 96); - - /** - * The projectile for Smoke barrage. - */ - private static final Projectile BARRAGE_PROJECTILE = Projectile.create((Entity) null, null, 391, 40, 36, 52, 75, 15, 11); - - /** - * The end graphic for Smoke barrage. - */ - private static final Graphics BARRAGE_END = new Graphics(390, 0); - - /** - * Constructs a new {@code SmokeSpells} {@code Object}. - */ - public SmokeSpells() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code SmokeSpells} {@Code Object} - * @param type The spell type. - * @param level The level requirement. - * @param sound The casting sound. - * @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 SmokeSpells(SpellType type, int level, double baseExperience, int sound, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { - super(type, SpellBook.ANCIENT, level, baseExperience, sound, impactSound, anim, start, projectile, end, runes); - } - - @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))); - return this; - } - - @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 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); - } - } - - @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, 1); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/impl/ForceMovement.java b/Server/src/main/java/core/game/node/entity/impl/ForceMovement.java deleted file mode 100644 index db76cebf9..000000000 --- a/Server/src/main/java/core/game/node/entity/impl/ForceMovement.java +++ /dev/null @@ -1,482 +0,0 @@ -package core.game.node.entity.impl; - -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import rs09.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; - -/** - * The force movement handler. - * @author Emperor - */ -public class ForceMovement extends Pulse { - - /** - * The walking speed. - */ - public static final int WALKING_SPEED = 10; - - /** - * The running speed. - */ - public static final int RUNNING_SPEED = 20; - - /** - * The walking animation. - */ - public static final Animation WALK_ANIMATION = Animation.create(819); - - /** - * The entity. - */ - protected Entity entity; - - /** - * The location to start the force movement from. - */ - private Location start; - - /** - * The destination. - */ - private Location destination; - - /** - * The animation. - */ - private Animation startAnim; - - /** - * The animation. - */ - protected Animation animation; - - /** - * The ending animation. - */ - private Animation endAnimation = null; - - /** - * The direction. - */ - protected Direction direction; - - /** - * The commencing speed. - */ - private int commenceSpeed; - - /** - * The path speed. - */ - private int pathSpeed; - - /** - * Constructs a new {@code ForceMovement} {@code Object}. - * @param e The entity. - * @param start The start location. - * @param destination The destination. - * @param startAnim The start animation. - * @param animation The animation - * @param direction The direction. - */ - public ForceMovement(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed) { - super(1, e); - this.entity = e; - this.start = start; - this.destination = destination; - this.startAnim = startAnim; - this.animation = animation; - this.direction = direction; - this.commenceSpeed = commenceSpeed; - this.pathSpeed = pathSpeed; - } - - /** - * Constructs a new {@code ForceMovement} {@code Object}. - * @param e the entity. - * @param start the start location. - * @param end the destination. - * @param animation the animation. - * @param speed The path speed. - */ - 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); - } - - 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); - } - - /** - * Constructs a new {@code ForceMovement} {@code Object}. - * @param e the entity. - * @param start the start location. - * @param destination the destination. - * @param animation the animation. - */ - public ForceMovement(Entity e, Location start, Location destination, Animation animation) { - this(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED); - } - - /** - * Constructs a new {@code ForceMovement} {@code Object}. - * @param start the start loc. - * @param destination the destination. - * @param animation the animation. - */ - public ForceMovement(Location start, Location destination, Animation animation) { - this(null, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED); - } - - /** - * Creates and runs a new force movement pulse. - * @param e The entity. - * @param destination The destination location. - * @return The created ForceMovement object. - */ - 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); - } - - /** - * Creates and runs a new force movement pulse. - * @param e The entity. - * @param start The start location. - * @param destination The destination location. - * @return The created ForceMovement object. - */ - 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); - } - - /** - * Creates and runs a new force movement pulse. - * @param e The entity. - * @param start The start location. - * @param destination The destination location. - * @param animation The animation. - * @return The created ForceMovement object. - */ - 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); - } - - /** - * Creates and runs a new force movement pulse. - * @param e The entity. - * @param start The start location. - * @param destination The destination location. - * @param animation The animation. - * @param speed The path speed. - * @return The created ForceMovement object. - */ - 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); - } - - /** - * Creates and runs a new force movement pulse. - * @param e The entity. - * @param start The start location. - * @param destination The destination location. - * @param animation The animation. - * @return The created ForceMovement object. - */ - 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); - } - - /** - * Creates and runs a new force movement pulse. - * @param e The entity. - * @param start The start location. - * @param destination The destination location. - * @param animation The animation. - * @param direction The direction. - * @return The created ForceMovement object. - */ - 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); - } - - /** - * Creates and runs a new force movement pulse. - * @param e The entity. - * @param start The start location. - * @param destination The destination location. - * @param animation The animation. - * @param direction The direction. - * @param pathSpeed The speed (in ticks). - * @return The created ForceMovement object. - */ - 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); - } - - /** - * Creates and runs a new force movement pulse. - * @param e The entity. - * @param start The start location. - * @param destination The destination location. - * @param animation The animation. - * @param direction The direction. - * @return The created ForceMovement object. - */ - public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed) { - if (startAnim != null) { - startAnim.setPriority(Animator.Priority.VERY_HIGH); - } - if (animation != null) { - animation.setPriority(Animator.Priority.VERY_HIGH); - } - ForceMovement fm = new ForceMovement(e, start, destination, startAnim, animation, direction, commenceSpeed, pathSpeed); - fm.start(); - e.lock(); - GameWorld.getPulser().submit(fm); - return fm; - } - - 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); - } - - /** - * Method used to run the force movement. - * @param e the entity. - */ - public void run(final Entity e, final int speed) { - this.entity = e; - int commence = (int) start.getDistance(e.getLocation()); - if (commence != 0 && commenceSpeed != 0) { - commence = (int) (1 + (commence / (commenceSpeed * 0.1))); - } - int path = 1 + (int) Math.ceil(start.getDistance(destination) / (pathSpeed * 0.1)); - this.pathSpeed = pathSpeed == 0 ? path : speed; - this.commenceSpeed = commence; - start(); - e.lock(); - GameWorld.getPulser().submit(this); - } - - /** - * Method used to run the movement. - * @param e the entity. - */ - public void run(final Entity e) { - run(e, 0); - } - - /** - * Method used to run the movement. - */ - public void run() { - run(entity); - } - - /** - * Gets the direction value. - * @param s The start location. - * @param d The destination. - * @return The direction object. - */ - public static Direction direction(Location s, Location d) { - Location delta = Location.getDelta(s, d); - int x = Math.abs(delta.getX()); - int y = Math.abs(delta.getY()); - if (x > y) { - return Direction.getDirection(delta.getX(), 0); - } - return Direction.getDirection(0, delta.getY()); - } - - @Override - public void start() { - commenceSpeed = (int) Math.ceil(start.getDistance(entity.getLocation()) / (commenceSpeed * 0.1)); - pathSpeed = (int) Math.ceil(start.getDistance(destination) / (pathSpeed * 0.1)); - if (commenceSpeed != 0) { - entity.animate(startAnim); - super.setDelay(commenceSpeed); - } else { - entity.animate(animation); - super.setDelay(pathSpeed); - } - int ticks = 1 + commenceSpeed + pathSpeed; - entity.getImpactHandler().setDisabledTicks(ticks); - entity.getUpdateMasks().register(new ForceMovementFlag(this)); - if(entity instanceof Player) { - entity.getWalkingQueue().updateRegion(destination, false); - } - super.start(); - } - - @Override - public boolean pulse() { - if (commenceSpeed != 0) { - entity.animate(animation); - setDelay(pathSpeed); - commenceSpeed = 0; - entity.getProperties().setTeleportLocation(start); - return false; - } - return true; - } - - @Override - public void stop() { - super.stop(); - entity.getProperties().setTeleportLocation(destination); - if (endAnimation != null) { - entity.animate(endAnimation); - } - entity.unlock(); - } - - /** - * Gets the start. - * @return The start. - */ - public Location getStart() { - return start; - } - - /** - * Sets the start. - * @param start The start to set. - */ - public void setStart(Location start) { - this.start = start; - } - - /** - * Gets the destination. - * @return The destination. - */ - public Location getDestination() { - return destination; - } - - /** - * Sets the destination. - * @param destination The destination to set. - */ - public void setDestination(Location destination) { - this.destination = destination; - } - - /** - * Gets the direction. - * @return The direction. - */ - public Direction getDirection() { - return direction; - } - - /** - * Sets the direction. - * @param direction The direction to set. - */ - public void setDirection(Direction direction) { - this.direction = direction; - } - - /** - * Gets the commenceSpeed. - * @return The commenceSpeed. - */ - public int getCommenceSpeed() { - return commenceSpeed; - } - - /** - * Sets the commenceSpeed. - * @param commenceSpeed The commenceSpeed to set. - */ - public void setCommenceSpeed(int commenceSpeed) { - this.commenceSpeed = commenceSpeed; - } - - /** - * Sets the entity. - * @param entity the entity. - */ - public void setEntity(Entity entity) { - this.entity = entity; - } - - /** - * Gets the pathSpeed. - * @return The pathSpeed. - */ - public int getPathSpeed() { - return pathSpeed; - } - - /** - * Sets the pathSpeed. - * @param pathSpeed The pathSpeed to set. - */ - public void setPathSpeed(int pathSpeed) { - this.pathSpeed = pathSpeed; - } - - /** - * Gets the entity. - * @return The entity. - */ - public Entity getEntity() { - return entity; - } - - /** - * Gets the startAnim. - * @return The startAnim. - */ - public Animation getStartAnim() { - return startAnim; - } - - /** - * Sets the startAnim. - * @param startAnim The startAnim to set. - */ - public void setStartAnim(Animation startAnim) { - this.startAnim = startAnim; - } - - /** - * Gets the animation. - * @return The animation. - */ - public Animation getAnimation() { - return animation; - } - - /** - * Sets the animation. - * @param animation The animation to set. - */ - public void setAnimation(Animation animation) { - this.animation = animation; - } - - /** - * Gets the endAnimation. - * @return The endAnimation. - */ - public Animation getEndAnimation() { - return endAnimation; - } - - /** - * Sets the endAnimation. - * @param endAnimation The endAnimation to set. - */ - public void setEndAnimation(Animation endAnimation) { - this.endAnimation = endAnimation; - } -} diff --git a/Server/src/main/java/core/game/node/entity/impl/PulseManager.java b/Server/src/main/java/core/game/node/entity/impl/PulseManager.java deleted file mode 100644 index f7cafd0cf..000000000 --- a/Server/src/main/java/core/game/node/entity/impl/PulseManager.java +++ /dev/null @@ -1,133 +0,0 @@ -package core.game.node.entity.impl; - -import core.game.interaction.MovementPulse; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.DeathTask; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import rs09.game.node.entity.combat.CombatPulse; -import rs09.game.world.GameWorld; - -/** - * Represents an entity's pulse manager. - * @author Emperor - */ -public final class PulseManager { - - /** - * The movement pulse. - */ - private Pulse current; - - /** - * Runs a pulse. - * @param pulse The pulse. - * @param pulseType The pulse type we're trying to run. - */ - public void run(Pulse pulse, String... pulseType) { - run(pulse, false, pulseType); - } - - public void run(Pulse pulse, boolean fast, String... pulseType) { - if (!clear(pulseType)) { - return; - } - pulse.start(); - if (pulse.isRunning()) { - if (fast) { - GameWorld.getPulser().submit(current = pulse); - } else { - GameWorld.getPulser().submit(current = pulse); - } - } - } - - /** - * Clears the pulses. - */ - public boolean clear(String... pulseType) { - if (current != null && current.isRunning()) { - if (pulseType.length > 0) { - int length = pulseType.length; - for (int i = 0; i < length; i++) { - if (!current.removeFor(pulseType[i])) { - return false; - } - } - } else if (!current.removeFor("unspecified")) { - return false; - } - current.stop(); - } - return true; - } - - /** - * Runs the unhandled reward pulse ("Nothing interesting happens.") - * @param player The player. - * @return The pulse. - * @param pulseType The pulse type. - */ - public Pulse runUnhandledAction(final Player player, String... pulseType) { - Pulse pulse = new Pulse(1, player) { - @Override - public boolean pulse() { - player.getPacketDispatch().sendMessage("Nothing interesting happens."); - return true; - } - }; - run(pulse, pulseType); - return pulse; - } - - /** - * Checks if the current pulse moves the entity. - * @return {@code True} if so. - */ - public boolean isMovingPulse() { - if (current != null && !current.isRunning()) { - return false; - } - if (current instanceof CombatPulse) { - return true; - } - return current instanceof MovementPulse; - } - - /** - * Checks if a pulse is running. - * @return {@code True} if so. - */ - public boolean hasPulseRunning() { - return current != null && current.isRunning(); - } - - /** - * Cancels the death task, if any. - * @param e The entity. - */ - public static void cancelDeathTask(Entity e) { - if (!DeathTask.isDead(e) || e.getPulseManager().current == null) { - return; - } - e.getPulseManager().current.stop(); - } - - /** - * Sets the current pulse. - * @deprecated This should only be used by death pulse, use - * {@link #run(Pulse, String...)} instead. - */ - @Deprecated - public void set(Pulse schedule) { - this.current = schedule; - } - - /** - * Gets the current. - * @return The current. - */ - public Pulse getCurrent() { - return current; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/npc/AbstractNPC.java b/Server/src/main/java/core/game/node/entity/npc/AbstractNPC.java deleted file mode 100644 index 402f2fce5..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/AbstractNPC.java +++ /dev/null @@ -1,104 +0,0 @@ -package core.game.node.entity.npc; - -import rs09.game.system.SystemLogger; -import core.game.world.map.Location; -import core.plugin.Plugin; - -import java.util.HashMap; -import java.util.Map; - -/** - * Used as superclass for "special NPC" plugins. - * @author Emperor - */ -public abstract class AbstractNPC extends NPC implements Plugin { - - /** - * The abstract NPC mapping. - */ - protected static Map mapping = new HashMap<>(); - - /** - * Constructs a new {@code AbstractNPC} {@Code Object}. - * @param id The id. - * @param location The location. - */ - public AbstractNPC(int id, Location location) { - this(id, location, true); - } - - /** - * Constructs a new {@code AbstractNPC} {@Code Object}. - * @param id The id. - * @param location The location. - * @param autowalk If the NPC should move around. - */ - public AbstractNPC(int id, Location location, boolean autowalk) { - super(id, location); - 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("[" + getClass().getSimpleName() + "] - Warning: Mapping already contained NPC id " + id + "! (" + name + ")"); - continue; - } - } - mapping.put(id, this); - } - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - /** - * Constructs a new instance of this abstract NPC. - * @param id The npc id. - * @param location The location. - * @param objects TODO - * @return The abstract npc instance. - */ - public abstract AbstractNPC construct(int id, Location location, Object... objects); - - /** - * Gets the NPC ids using this abstract NPC handler. - * @return The NPC ids. - */ - public abstract int[] getIds(); - - /** - * Gets the abstract NPC object for this NPC id. - * @param npcId The NPC id. - * @return The abstract NPC "loader" object for the given NPC id. - */ - public static AbstractNPC forId(int npcId) { - return mapping.get(npcId); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/npc/Metamorphosis.java b/Server/src/main/java/core/game/node/entity/npc/Metamorphosis.java deleted file mode 100644 index 80d0fb3c7..000000000 --- a/Server/src/main/java/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.content.dialogue.DialoguePlugin; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.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 rs09.plugin.PluginManager; -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) { - PluginManager.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/java/core/game/node/entity/npc/NPC.java b/Server/src/main/java/core/game/node/entity/npc/NPC.java deleted file mode 100644 index 3bd79964b..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/NPC.java +++ /dev/null @@ -1,1007 +0,0 @@ -package core.game.node.entity.npc; - -import core.cache.def.impl.NPCDefinition; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.global.shop.Shop; -import core.game.interaction.Interaction; -import core.game.interaction.MovementPulse; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatSpell; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.equipment.DefaultCombatSpell; -import core.game.node.entity.combat.equipment.WeaponInterface; -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.link.SpellBookManager; -import core.game.node.entity.player.link.audio.Audio; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.slayer.Tasks; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.game.world.map.build.DynamicRegion; -import core.game.world.map.path.Pathfinder; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.game.world.update.flag.npc.NPCFaceEntity; -import core.game.world.update.flag.npc.NPCFaceLocation; -import core.game.world.update.flag.npc.NPCForceChat; -import core.game.world.update.flag.npc.NPCSwitchId; -import core.tools.RandomFunction; -import rs09.game.content.global.GlobalKillCounter; -import rs09.game.content.jobs.JobManager; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.system.config.NPCConfigParser; -import rs09.game.system.config.ShopParser; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; - -import java.util.Map; - -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_BASE; -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_ENEMIES_KILLED; - -/** - * Represents a non-player character. - * @author Emperor - */ -public class NPC extends Entity { - - /** - * The non-player character's id. - */ - private int id; - - /** - * The original NPC id. - */ - private final int originalId; - - /** - * The NPC definitions. - */ - private NPCDefinition definition; - - /** - * If the NPC walks; - */ - private boolean walks; - - /** - * If the NPC is aggressive. - */ - private boolean aggressive; - - /** - * Represents if the NPC will respawn. - */ - private boolean respawn = true; - - /** - * The movement path of the NPC. - */ - protected Location[] movementPath; - - /** - * The walking radius. - */ - protected int walkRadius = 11; - - /** - * The movement index. - */ - protected int movementIndex; - - /** - * The respawn tick. - */ - private int respawnTick = -1; - - /** - * If the NPC walks bound to a path. - */ - protected boolean pathBoundMovement; - - /** - * The current dialogue. - */ - protected Player dialoguePlayer; - - /** - * The aggressive handler of the NPC. - */ - protected AggressiveHandler aggressiveHandler; - - /** - * The walk back radius. - */ - protected int nextWalk; - - /** - * The children NPCs. - */ - private NPC[] children; - - /** - * The amount of slayer experience received when killing this NPC. - */ - private double slayerExperience; - - /** - * The slayer task. - */ - private Tasks task; - - /** - * If the npc can never walk. - */ - private boolean neverWalks; - - /** - * The shop of this npc. - */ - private Shop shop; - - /** - * The force talk string. - */ - private String forceTalk; - - /** - * Constructs a new {@code NPC} {@code Object}. - * @param id The NPC id. - */ - public NPC(int id) { - this(id, null); - } - - /** - * Constructs a new {@code NPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - protected NPC(int id, Location location, Direction direction) { - super(NPCDefinition.forId(id).getName(), location); - this.id = id; - this.originalId = id; - this.definition = NPCDefinition.forId(id); - super.size = definition.size; - super.direction = direction; - super.interaction = new Interaction(this); - } - - /** - * Constructs a new {@code NPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public NPC(int id, Location location) { - this(id, location, Direction.SOUTH); - } - - /** - * Creates a new NPC object. - * @param id The NPC id. - * @param location The location. - * @param direction the dir. - * @param objects the objects. - * @return The NPC object. - */ - 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); - } - if (n == null) { - n = new NPC(id, location, direction); - } - return n; - } - - /** - * Creates a new NPC object. - * @param id the id. - * @param location the location. - * @return the npc object. - */ - public static NPC create(int id, Location location, Object... objects) { - return create(id, location, Direction.SOUTH, objects); - } - - @Override - public void init() { - super.init(); - getProperties().setSpawnLocation(getLocation()); - initConfig(); - Repository.getNpcs().add(this); - RegionManager.move(this); - if (getViewport().getRegion().isActive()) { - Repository.addRenderableNPC(this); - } - interaction.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.index = index; - npc.size = size; - } - } - if (definition.hasAction("trade")) { - shop = ShopParser.Companion.getSHOPS().get(getId()); - if (shop != null) { - shop = shop.copy(); - } - } - if (definition.hasAction("sew")) { - shop = ShopParser.Companion.getSHOPS().get(getId()); - if (shop != null) { - shop = shop.copy(); - } - } - } - - @Override - public void clear() { - super.clear(); - Repository.removeRenderableNPC(this); - Repository.getNpcs().remove(this); - getViewport().setCurrentPlane(null); - // getViewport().setRegion(null); - } - - /** - * Initializes the configurations. - */ - public void initConfig() { - int defaultLevel = definition.getCombatLevel() / 2; - getProperties().setCombatLevel(definition.getCombatLevel()); - getSkills().setStaticLevel(Skills.ATTACK, definition.getConfiguration(NPCConfigParser.ATTACK_LEVEL, defaultLevel)); - getSkills().setStaticLevel(Skills.STRENGTH, definition.getConfiguration(NPCConfigParser.STRENGTH_LEVEL, defaultLevel)); - getSkills().setStaticLevel(Skills.DEFENCE, definition.getConfiguration(NPCConfigParser.DEFENCE_LEVEL, defaultLevel)); - getSkills().setStaticLevel(Skills.RANGE, definition.getConfiguration(NPCConfigParser.RANGE_LEVEL, defaultLevel)); - getSkills().setStaticLevel(Skills.MAGIC, definition.getConfiguration(NPCConfigParser.MAGIC_LEVEL, defaultLevel)); - getSkills().setStaticLevel(Skills.HITPOINTS, definition.getConfiguration(NPCConfigParser.LIFEPOINTS, defaultLevel)); - int lp = getSkills().getMaximumLifepoints(); - if (this instanceof Familiar) { - lp = getAttribute("hp", lp); - } - getSkills().setLevel(Skills.HITPOINTS, lp); - aggressive = definition.getConfiguration(NPCConfigParser.AGGRESSIVE, aggressive); - Animation anim = null; - for (int i = 0; i < 3; i++) { - if (definition.getCombatAnimation(i) != null) { - getProperties().setAttackAnimation(anim = definition.getCombatAnimation(i)); - break; - } - } - if (definition.getCombatAnimation(3) != null) { - getProperties().setDefenceAnimation(definition.getCombatAnimation(3)); - } - if (definition.getCombatAnimation(4) != null) { - getProperties().setDeathAnimation(definition.getCombatAnimation(4)); - } - if (definition.getCombatAnimation(1) != null) { - getProperties().setMagicAnimation(definition.getCombatAnimation(1)); - } - if (definition.getCombatAnimation(2) != null) { - getProperties().setRangeAnimation(definition.getCombatAnimation(2)); - } - if (getProperties().getAttackAnimation() == null && anim != null) { - getProperties().setAttackAnimation(anim); - } - if (getProperties().getMagicAnimation() == null && anim != null) { - getProperties().setMagicAnimation(anim); - } - if (getProperties().getRangeAnimation() == null && anim != null) { - getProperties().setRangeAnimation(anim); - } - definition.initCombatGraphics(definition.getHandlers()); - getProperties().setBonuses(definition.getConfiguration(NPCConfigParser.BONUSES, new int[15])); - getProperties().setAttackSpeed(definition.getConfiguration(NPCConfigParser.ATTACK_SPEED, 5)); - forceTalk = definition.getConfiguration("force_talk", null); - if (definition.getConfiguration("movement_radius") != null) { - this.setWalkRadius(definition.getConfiguration("movement_radius")); - } - if(definition.getConfiguration("death_gfx") != null) { - getProperties().deathGfx = new Graphics(definition.getConfiguration("death_gfx")); - } - } - - /** - * Opens the shop for a player. - * @param player the player. - * @return {@code True} if so. - */ - public boolean openShop(Player player) { - shop = null; - if(shop != null){ - player.debug("testing new shop for: " + name); - } - if (shop == null) { - shop = ShopParser.Companion.getSHOPS().get(getId()); - if (shop == null) { - return false; - } - } - if (getName().contains("assistant")) { - NPC n = RegionManager.getNpc(this, getId() - 1); - if (n != null) { - return n.openShop(player); - } - } - shop.open(player); - - //Fix for issue #11 for shops keeping dialogue open. - DialoguePlugin dialogue = player.getDialogueInterpreter().getDialogue(); - if (dialogue != null) - dialogue.end(); - return true; - } - - @Override - public boolean isInvisible() { - if (!isActive() || getRespawnTick() > GameWorld.getTicks()) { - return true; - } - return super.isInvisible(); - } - - /** - * Checks if the NPC is hidden for the player. - * @param player The player. - * @return {@code True} if so. - */ - public boolean isHidden(Player player) { - return isInvisible(); - } - - /** - * Sets the default aggressive behavior of the NPC. - */ - public void setDefaultBehavior() { - if (aggressive && definition.getCombatLevel() > 0) { - aggressiveHandler = new AggressiveHandler(this, AggressiveBehavior.DEFAULT); - aggressiveHandler.setRadius(definition.getConfiguration(NPCConfigParser.AGGRESSIVE_RADIUS, 4)); - } - } - - /** - * Configures the movement path. - * @param movementPath The movement path. - */ - public void configureMovementPath(Location... movementPath) { - this.movementPath = movementPath; - 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)) { - state.neutralizeHits(); - } - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if ((entity instanceof Player && !definition.hasAction("attack")) || isInvisible()) { - return false; - } - if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getStaticLevel(Skills.SLAYER)) { - ((Player) entity).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster."); - } - return super.isAttackable(entity, style); - } - - @Override - public int getDragonfireProtection(boolean fire) { - return 0; - } - - @Override - public void tick() { - if (!getViewport().getRegion().isActive()) { - onRegionInactivity(); - return; - } - if (respawnTick > GameWorld.getTicks()) { - return; - } - if (respawnTick == GameWorld.getTicks()) { - onRespawn(); - } - handleTickActions(); - super.tick(); - } - - /** - * Called when the NPC respawns. - */ - protected void onRespawn() { - - } - - /** - * Handles the automatic actions of the NPC. - */ - public void handleTickActions() { - if (!getLocks().isInteractionLocked()) { - if (!pathBoundMovement && walkRadius > 0 && !getLocation().withinDistance(getProperties().getSpawnLocation(), walkRadius)) { - if(!isNeverWalks()){ - if(walkRadius == 0) - walkRadius = 3; - } - getPulseManager().run(new MovementPulse(this, getProperties().getSpawnLocation(), Pathfinder.SMART) { - @Override - public boolean pulse() { - return true; - } - }, "movement"); - if (aggressiveHandler != null) { - aggressiveHandler.setPauseTicks(walkRadius + 1); - } - nextWalk = GameWorld.getTicks() + walkRadius + 1; - return; - } - if (aggressive && aggressiveHandler != null && aggressiveHandler.selectTarget()) { - return; - } - if (getProperties().getCombatPulse().isAttacking()) { - return; - } - } - if (!getLocks().isMovementLocked()) { - if (dialoguePlayer == null || !dialoguePlayer.isActive() || !dialoguePlayer.getInterfaceManager().hasChatbox()) { - dialoguePlayer = null; - if (walks && !getPulseManager().hasPulseRunning() && !getProperties().getCombatPulse().isAttacking() && !getProperties().getCombatPulse().isInCombat() && nextWalk < GameWorld.getTicks()) { - setNextWalk(); - Location l = getMovementDestination(); - if (canMove(l)) { - if((Boolean) definition.getHandlers().getOrDefault("water_npc",false)){ - Pathfinder.findWater(this,l,true,Pathfinder.DUMB).walk(this); - } else { - Pathfinder.find(this, l, true, Pathfinder.DUMB).walk(this); - } - } - } - } - } - if (shop != null) { - if (shop.getLastRestock() < GameWorld.getTicks()) { - if (shop.isRestock()) { - shop.restock(); - shop.setLastRestock(GameWorld.getTicks() + 100); - } - } - } - if (forceTalk != null && getAttribute("lastForceTalk", 0) < GameWorld.getTicks()) { - sendChat(forceTalk); - setAttribute("lastForceTalk", GameWorld.getTicks() + RandomFunction.random(15, 30)); - } - } - - /** - * Sets the next walk. - */ - public void setNextWalk() { - nextWalk = GameWorld.getTicks() + 5 + RandomFunction.randomize(10); - } - - public void resetWalk() { - nextWalk = GameWorld.getTicks() - 1; - getWalkingQueue().reset(); - } - - /** - * Called when the region goes inactive. - */ - public void onRegionInactivity() { - getWalkingQueue().reset(); - getPulseManager().clear(); - getUpdateMasks().reset(); - Repository.removeRenderableNPC(this); - if (getViewport().getRegion() instanceof DynamicRegion) { - clear(); - } - } - - @Override - public boolean isPoisonImmune() { - return definition.getConfiguration(NPCConfigParser.POISON_IMMUNE, false); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (getZoneMonitor().handleDeath(killer)) { - return; - } - if (task != null && killer instanceof Player && ((Player) killer).getSlayer().getTask() == task && ((Player) killer).getSlayer().hasTask()) { - ((Player) killer).getSlayer().finalizeDeath(killer.asPlayer(), this); - } - if (killer instanceof Player && killer.getAttribute("jobs:id",null) != null) { - JobManager.handleDeath(id,(Player) killer); - } - 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); - } - handleDrops(p, killer); - if (!isRespawn()) { - clear(); - } - } - - /** - * Handles the drops of the npc. - * @param p the p. - * @param killer the killer. - */ - public void handleDrops(Player p, Entity killer) { - if (getAttribute("disable:drop", false)) { - return; - } - if (killer instanceof Player && p != null && p.getIronmanManager().isIronman() && getImpactHandler().getPlayerImpactLog().size() > 1) { - return; - } - if (definition == null || definition.getDropTables() == null) { - return; - } - definition.getDropTables().drop(this, killer); - } - - @Override - public void update() { - super.update(); - } - - @Override - public void reset() { - 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(); - } - - /** - * Gets an audio piece. - * @param index the index. - * @return the audio. - */ - public Audio getAudio(int index) { - int[] audios = getDefinition().getConfiguration(NPCConfigParser.COMBAT_AUDIO, null); - if (audios != null) { - Audio audio = new Audio(audios[index]); - if (audio != null && audio.getId() != 0) { - return audio; - } - } - return null; - } - - /** - * Configures the NPC.
Override this instead of {@link #init()} when - * creating a sub class. - */ - public void configure() { - int[] bonus = definition.getConfiguration(NPCConfigParser.BONUSES, new int[3]); - int highest = 0; - int index = 0; - for (int i = 0; i < 3; i++) { - if (bonus[i] > highest) { - highest = bonus[i]; - index = i; - } - } - CombatStyle protectStyle = definition.getConfiguration(NPCConfigParser.PROTECT_STYLE, null); - if (protectStyle != null) { - getProperties().setProtectStyle(protectStyle); - } - getProperties().setAttackStyle(new WeaponInterface.AttackStyle(WeaponInterface.STYLE_CONTROLLED, index)); - CombatStyle style = getDefinition().getConfiguration(NPCConfigParser.COMBAT_STYLE); - if (style != null) { - getProperties().getCombatPulse().setStyle(style); - } - if (style == CombatStyle.MAGIC) { - getProperties().setAutocastSpell(new DefaultCombatSpell(this)); - int spell = definition.getConfiguration("spell_id", -1); - if (spell != -1) { - getProperties().setSpell((CombatSpell) SpellBookManager.SpellBook.MODERN.getSpell(spell)); - getProperties().setAutocastSpell((CombatSpell) SpellBookManager.SpellBook.MODERN.getSpell(spell)); - } - } - task = Tasks.forId(getId()); - } - - /** - * Method used to check if the NPC can attack the victim. This method is - * used for aggressive behavior. - * @return True if so. - */ - public boolean canAttack(final Entity victim) { - return true; - } - - @Override - public boolean hasProtectionPrayer(CombatStyle style) { - return getProperties().getProtectStyle() == style; - } - - /** - * Checks if the npc should ignore victim attack restrictions(i.e You can't - * attack this npc.") - * @param victim the victim. - * @return {@code True} if ignore. - */ - public boolean isIgnoreAttackRestrictions(Entity victim) { - return false; - } - - @Override - public String toString() { - return "NPC " + id + ", " + getLocation() + ", " + getIndex(); - } - - /** - * Transforms this NPC. - * @param id The new NPC id. - */ - public void transform(int id) { - this.id = id; - this.definition = NPCDefinition.forId(id); - super.name = definition.getName(); - super.size = definition.size; - super.interaction = new Interaction(this); - initConfig(); - configure(); - interaction.setDefault(); - if (id == originalId) { - getUpdateMasks().unregisterSynced(NPCSwitchId.getOrdinal()); - } - getUpdateMasks().register(new NPCSwitchId(id), id != originalId); - } - - /** - * Transforms this NPC back to original. - */ - public void reTransform() { - if (originalId == this.id) { - return; - } - transform(originalId); - } - - /** - * Gets the movement destination of the NPC. - * @return The movement destination. - */ - protected Location getMovementDestination() { - if (!pathBoundMovement || movementPath == null || movementPath.length < 1) { - return getProperties().getSpawnLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0); - } - Location l = movementPath[movementIndex++]; - if (movementIndex == movementPath.length) { - movementIndex = 0; - } - return l; - } - - /** - * Gets the drop location. - * @return The drop location. - */ - public Location getDropLocation() { - return getLocation(); - } - - /** - * Checks if the npc can start combat. - * @param victim the victim. - * @return {@code True} if so. - */ - public boolean canStartCombat(Entity victim) { - return true; - } - - /** - * Gets the definition. - * @return The definition. - */ - public NPCDefinition getDefinition() { - return definition; - } - - /** - * Sets the definition. - * @param definition The definition to set. - */ - public void setDefinition(NPCDefinition definition) { - this.definition = definition; - } - - /** - * Get the id. - * @return The non-player character's id. - */ - public int getId() { - return id; - } - - /** - * Set the id. - * @param id The non-player character's id. - */ - public void setId(int id) { - this.id = id; - } - - /** - * Gets the walks. - * @return The walks. - */ - public boolean isWalks() { - return walks; - } - - /** - * Sets the walks. - * @param walks The walks to set. - */ - public void setWalks(boolean walks) { - this.walks = walks; - } - - /** - * Gets the aggressive. - * @return The aggressive. - */ - public boolean isAggressive() { - return aggressive; - } - - /** - * Sets the aggressive. - * @param aggressive The aggressive to set. - */ - public void setAggressive(boolean aggressive) { - this.aggressive = aggressive; - } - - /** - * Sets the name. - * @param @name - */ - public void setName(String name) { - this.name = name; - } - - /** - * Gets the movementPath. - * @return The movementPath. - */ - public Location[] getMovementPath() { - return movementPath; - } - - /** - * @return the respawn. - */ - public boolean isRespawn() { - return respawn; - } - - /** - * @param respawn the respawn to set. - */ - public void setRespawn(boolean respawn) { - this.respawn = respawn; - } - - /** - * Gets the pathBoundMovement. - * @return The pathBoundMovement. - */ - public boolean isPathBoundMovement() { - return pathBoundMovement; - } - - /** - * Sets the pathBoundMovement. - * @param pathBoundMovement The pathBoundMovement. - */ - public void setPathBoundMovement(boolean pathBoundMovement) { - this.pathBoundMovement = pathBoundMovement; - } - - /** - * Gets the dialoguePlayer. - * @return The dialoguePlayer. - */ - public Player getDialoguePlayer() { - return dialoguePlayer; - } - - /** - * Sets the dialoguePlayer. - */ - public void setDialoguePlayer(Player dialoguePlayer) { - this.dialoguePlayer = dialoguePlayer; - } - - /** - * Gets the aggressive handler. - * @return The aggressive handler. - */ - public AggressiveHandler getAggressiveHandler() { - return aggressiveHandler; - } - - /** - * Sets the aggressive handler. - * @param handler The handler. - */ - public void setAggressiveHandler(AggressiveHandler handler) { - this.aggressiveHandler = handler; - } - - /** - * Gets the respawnTick. - * @return The respawnTick. - */ - public int getRespawnTick() { - return respawnTick; - } - - /** - * Sets the walking radius. - * @param radius The walking radius. - */ - public void setWalkRadius(int radius) { - this.walkRadius = radius; - } - - /** - * Gets the walking radius. - * @return the walking radius. - */ - public int getWalkRadius() { - return walkRadius; - } - - /** - * Sets the respawnTick. - * @param respawnTick The respawnTick to set. - */ - public void setRespawnTick(int respawnTick) { - this.respawnTick = respawnTick; - } - - /** - * Gets the originalId. - * @return The originalId. - */ - public int getOriginalId() { - return originalId; - } - - /** - * Gets the currently shown NPC. - * @param player The player to check for. - * @return The shown NPC. - */ - public NPC getShownNPC(Player player) { - if (children == null) { - return this; - } - int npcId = definition.getChildNPC(player).getId(); - for (NPC npc : children) { - if (npc.getId() == npcId) { - return npc; - } - } - return this; - } - - /** - * Gets the shop. - * @return the shop - */ - public Shop getShop() { - return shop; - } - - /** - * Sets the bashop. - * @param shop the shop to set. - */ - public void setShop(Shop shop) { - this.shop = shop; - } - - /** - * Gets the slayerExperience. - * @return The slayerExperience. - */ - public double getSlayerExperience() { - return slayerExperience; - } - - /** - * Sets the slayerExperience. - * @param slayerExperience The slayerExperience to set. - */ - public void setSlayerExperience(double slayerExperience) { - this.slayerExperience = slayerExperience; - } - - /** - * Gets the task. - * @return The task. - */ - public Tasks getTask() { - return task; - } - - /** - * Sets the task. - * @param task The task to set. - */ - public void setTask(Tasks task) { - this.task = task; - } - - /** - * Gets the neverWalks. - * @return the neverWalks - */ - public boolean isNeverWalks() { - return neverWalks; - } - - /** - * Sets the baneverWalks. - * @param neverWalks the neverWalks to set. - */ - public void setNeverWalks(boolean neverWalks) { - this.neverWalks = neverWalks; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/bosses/TzRekJadNPC.java b/Server/src/main/java/core/game/node/entity/npc/bosses/TzRekJadNPC.java deleted file mode 100644 index 6222554f6..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/bosses/TzRekJadNPC.java +++ /dev/null @@ -1,263 +0,0 @@ -package core.game.node.entity.npc.bosses; - -import core.cache.def.impl.NPCDefinition; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.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 rs09.game.world.repository.Repository; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; -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); - PluginManager.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/java/core/game/node/entity/npc/bosses/corp/CorporealBeastNPC.java b/Server/src/main/java/core/game/node/entity/npc/bosses/corp/CorporealBeastNPC.java deleted file mode 100644 index cdb03ce13..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/bosses/corp/CorporealBeastNPC.java +++ /dev/null @@ -1,320 +0,0 @@ -package core.game.node.entity.npc.bosses.corp; - -import core.game.content.global.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.ImpactHandler.HitsplatType; -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.npc.NPC; -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.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 rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; -import rs09.game.world.GameWorld; - -import java.util.ArrayList; -import java.util.List; - -/** - * Handles the Corporeal beast NPC. - * @author Emperor - * - */ -@Initializable -public final class CorporealBeastNPC extends AbstractNPC { - - /** - * The combat handler. - */ - private final MultiSwingHandler combatHandler = new CombatHandler(); - - /** - * The dark energy core NPC. - */ - private NPC darkEnergyCore; - - /** - * 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(); - } - - @Override - public void handleTickActions() { - for(Player player : getViewport().getCurrentPlane().getPlayers()){ - if(player.getFamiliarManager().hasFamiliar() && RandomFunction.random(100) < 10){ - int heal = player.getFamiliarManager().getFamiliar().getSkills().getLifepoints() / 4; - super.getSkills().heal(heal); - player.sendMessage("Your familiar was devoured by the Corporeal Beast, healing it by "+heal+" HP."); - player.getFamiliarManager().dismiss(); - } - } - if (getSkills().getLifepoints() < getSkills().getMaximumLifepoints()) { - if (getViewport().getCurrentPlane().getPlayers().isEmpty()) { - super.fullRestore(); - if (darkEnergyCore != null) { - darkEnergyCore.clear(); - darkEnergyCore = null; - } - } - } - super.handleTickActions(); - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - 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; - } - 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()); - 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) - ); - } - - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - spawnDarkCore((CorporealBeastNPC) entity, victim); - if (doStompAttack(entity)) { - entity.getProperties().getCombatPulse().setNextAttack(entity.getProperties().getAttackSpeed()); - return -1; - } - //Location based attack. - if (super.getNext().getProjectile() != null && super.getNext().getProjectile().getProjectileId() == 1824) { - setCurrent(getNext()); - CombatStyle style = getCurrent().getStyle(); - setType(style); - int index = RandomFunction.randomize(super.getAttacks().length); - SwitchAttack pick = getNext(entity, victim, state, index); - setNext(pick); - fireLocationBasedAttack(entity, victim.getLocation()); - entity.getProperties().getCombatPulse().setNextAttack(entity.getProperties().getAttackSpeed()); - return -1; - } - return super.swing(entity, victim, state); - } - - /** - * Spawns 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()) { - return; - } - double max = npc.getSkills().getMaximumLifepoints() * (0.3 + (npc.getViewport().getCurrentPlane().getPlayers().size() * 0.05)); - if (npc.getSkills().getLifepoints() > max) { - return; - } - Location l = RegionManager.getTeleportLocation(victim.getLocation(), 3); - npc.darkEnergyCore = NPC.create(8127, l, npc); - 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) { - @Override - public boolean pulse() { - npc.darkEnergyCore.init(); - return true; - } - }); - } - - /** - * Fires the location based magic attack. - * @param entity The corporeal beast. - * @param location The location. - */ - private void fireLocationBasedAttack(final Entity entity, final Location location) { - entity.animate(getCurrent().getAnimation()); - Projectile.create(entity, null, 1824, 60, 0, 41, 0).transform(entity, location, true, 46, 10).send(); - int ticks = 1 + (int) Math.ceil(entity.getLocation().getDistance(location) * 0.5); - GameWorld.getPulser().submit(new Pulse(ticks) { - boolean secondStage = false; - List players = RegionManager.getLocalPlayers(entity); - Location[] locations = null; - @Override - public boolean pulse() { - if (!secondStage) { - for (Player p : players) { - if (p.getLocation().equals(location)) { - hit(p); - } - } - locations = new Location[4 + RandomFunction.random(3)]; - for (int i = 0; i < locations.length; i++) { - locations[i] = location.transform(-2 + RandomFunction.random(5), -2 + RandomFunction.random(5), 0); - Projectile.create(location, locations[i], 1824, 60, 0, 25, 56, 0, 0).send(); - } - setDelay(2); - secondStage = true; - return false; - } - for (int i = 0; i < locations.length; i++) { - Location l = locations[i]; - Graphics.send(Graphics.create(1806), l.transform(-1, -1, 0)); - for (Player p : players) { - if (p.getLocation().equals(l)) { - hit(p); - } - } - } - players.clear(); - players = null; - 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); - } - p.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC); - } - }); - } - - /** - * Attempts to do a stomp attack if needed. - * @param entity The corporeal beast. - * @return {@code True} if a stomp attack is being done. - */ - public boolean doStompAttack(Entity entity) { - Location l = entity.getLocation(); - List stompTargets = null; - for (Player player : RegionManager.getLocalPlayers(entity, 5)) { - Location p = player.getLocation(); - if (p.getX() >= l.getX() && p.getY() >= l.getY() && p.getX() < l.getX() + entity.size() && p.getY() < l.getY() + entity.size()) { - if (stompTargets == null) { - stompTargets = new ArrayList<>(20); - } - stompTargets.add(player); - } - } - if (stompTargets != null) { - entity.visualize(Animation.create(10496), Graphics.create(1834)); - for (Player p : stompTargets) { - p.getImpactHandler().manualHit(entity, RandomFunction.random(52), HitsplatType.NORMAL, 1); - } - return true; - } - return false; - } - - @Override - public void adjustBattleState(Entity entity, Entity victim, BattleState state) { - super.adjustBattleState(entity, victim, state); - if (getCurrent().getProjectile() != null && getCurrent().getProjectile().getProjectileId() == 1823) { - if (state.getEstimatedHit() > 0) { - int random = RandomFunction.random(3); - 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); - ((Player) victim).getPacketDispatch().sendMessage("Your Hitpoints have been slightly drained!"); - } - else { - if (skill == Skills.PRAYER) { - victim.getSkills().decrementPrayerPoints(drain); - } else { - victim.getSkills().updateLevel(skill, -drain, 0); - } - if (victim instanceof Player) { - ((Player) victim).getPacketDispatch().sendMessage("Your " + Skills.SKILL_NAME[skill] + " has been slightly drained!"); - } - } - } - } - } - - @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); - } - return formatHit(victim, hit); - } - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/bosses/wilderness/KingBlackDragonNPC.java b/Server/src/main/java/core/game/node/entity/npc/bosses/wilderness/KingBlackDragonNPC.java deleted file mode 100644 index b69cce4ef..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/bosses/wilderness/KingBlackDragonNPC.java +++ /dev/null @@ -1,263 +0,0 @@ -package core.game.node.entity.npc.bosses.wilderness; - -import core.game.content.global.BossKillCounter; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import rs09.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.combat.equipment.FireType; -import core.game.node.entity.combat.handlers.DragonfireSwingHandler; -import core.game.node.entity.impl.Projectile; -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.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.RandomFunction; - -/** - * Represents the King Black Dragon NPC. - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class KingBlackDragonNPC extends AbstractNPC { - - /** - * The default spawn location. - */ - private static final Location DEFAULT_SPAWN = Location.create(2273, 4698, 0); - - /** - * The combat swing handler. - */ - private CombatSwingHandler combatHandler = new KBDCombatSwingHandler(); - - - public KingBlackDragonNPC() { - super(-1,null); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - BossKillCounter.addtoKillcount((Player) killer, this.getId()); - } - - /** - * Constructs a new {@code KingBlackDragonNPC} {@code Object}. - * @param id the id. - * @param l the l. - */ - public KingBlackDragonNPC(int id, Location l) { - super(id, l); - } - - @Override - public void init() { - super.init(); - configureBossData(); - } - - @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[] getIds() { - return new int[] { 50 }; - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return combatHandler; - } - - @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 { - - /** - * 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}. - */ - public KBDCombatSwingHandler() { - super(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); - } - - @Override - public int calculateAccuracy(Entity entity) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateAccuracy(entity); - } - return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); - } - - @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 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; - } - - @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 ArmourSet getArmourSet(Entity e) { - return style.getSwingHandler().getArmourSet(e); - } - - @Override - public double getSetMultiplier(Entity e, int skillId) { - return style.getSwingHandler().getSetMultiplier(e, skillId); - } - - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - style.getSwingHandler().impact(entity, victim, state); - } - - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - style = CombatStyle.RANGE; - int hit = 0; - int ticks = 1; - if (victim.getCenterLocation().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 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/java/core/game/node/entity/npc/city/alkharid/AlKharidShopKeeperDialogue.java b/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AlKharidShopKeeperDialogue.java deleted file mode 100644 index ef836af17..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AlKharidShopKeeperDialogue.java +++ /dev/null @@ -1,82 +0,0 @@ -package core.game.node.entity.npc.city.alkharid; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.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 al kharid shop keepers. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class AlKharidShopKeeperDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code AlKahridShopKeeperDialogue} {@code Object}. - */ - public AlKharidShopKeeperDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code AlKahridShopKeeperDialogue} {@code Object}. - * @param player the player. - */ - public AlKharidShopKeeperDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AlKharidShopKeeperDialogue(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?", "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.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: - end(); - break; - - } - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 525, 524 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AliMorrisaneDialogue.java b/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AliMorrisaneDialogue.java deleted file mode 100644 index dcfaf3266..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AliMorrisaneDialogue.java +++ /dev/null @@ -1,131 +0,0 @@ -package core.game.node.entity.npc.city.alkharid; - -import core.game.content.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/java/core/game/node/entity/npc/city/alkharid/AliTheLeafletDropper.java b/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AliTheLeafletDropper.java deleted file mode 100644 index ad3c3da40..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/alkharid/AliTheLeafletDropper.java +++ /dev/null @@ -1,177 +0,0 @@ -package core.game.node.entity.npc.city.alkharid; - -import core.game.content.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; - -/** - * Represents the dialogue plugin used for ali the leaflet dropper npc. - * @author 'Vexia - * @version 1.9 - */ -@Initializable -public final class AliTheLeafletDropper extends DialoguePlugin { - - /** - * Represents the flyer item. - */ - private static final Item FLYER = new Item(7922); - - /** - * Constructs a new {@code AliTheLeafletDropper} {@code Object}. - */ - public AliTheLeafletDropper() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code AliTheLeafletDropper} {@code Object}. - * @param player the player. - */ - public AliTheLeafletDropper(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AliTheLeafletDropper(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (args.length == 2) { - if (player.getInventory().freeSlots() == 0) { - npc("Looks like your hands are full. come back when you", "have space for my flyer."); - stage = 0; - return true; - } - if (player.getInventory().contains(7922, 1) || player.getBank().contains(7922, 1)) { - npc("Are you trying to be funny or has age turned your", "brain to mush? You already have a flyer!"); - stage = 0; - return true; - } - } - npc("I don't have time to talk right now! Ali Morrisane is", "paying me to hand out these flyers."); - stage = 700; - if (args.length == 2) { - player.getInventory().add(FLYER); - npc("Here take one and let me get back to work!"); - stage = 600; - return true; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - break; - case 600: - npc("I still have hundreds of these flyers to hand out, I", "wonder if Ali would notice if I quietly dumped them", "somewhere?"); - stage = 601; - break; - case 601: - end(); - break; - case 700: - interpreter.sendOptions("Select an Option", "Who is Ali Morrisane?", "What are the flyers for?", "What is there to do round here, boy?"); - stage = 701; - break; - case 701: - switch (buttonId) { - case 1: - player("Who is Ali Morrisane?"); - stage = 710; - break; - case 2: - player("What are the flyers for?"); - stage = 720; - break; - case 3: - player("What is there to do round here, boy?"); - stage = 300; - break; - } - break; - case 710: - npc("Ali Morrisane is the greatest merchant in the east!"); - stage = 711; - break; - case 711: - player("Were you paid to say that?"); - stage = 712; - break; - case 712: - npc("Of course I was! You can find him on the north edge", "of town"); - stage = 713; - break; - case 713: - end(); - break; - case 720: - npc("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."); - stage = 721; - break; - case 721: - npc("I think they're just sour because he's better at making", "money than them."); - stage = 722; - break; - case 722: - npc("The flyers advertises the different shops you can find in", "Al Kharid."); - stage = 723; - break; - case 723: - npc("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."); - stage = 724; - break; - case 724: - player("Which shops?"); - stage = 725; - break; - case 725: - npc("The flyer will dicuss them all."); - stage = 0; - break; - case 300: - npc("I'm very busy, so listen carefully! I shall say this only", "once."); - stage = 301; - break; - case 301: - npc("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."); - stage = 302; - break; - case 302: - npc("If you're here to make money, there is a mine to the", "south."); - stage = 303; - break; - case 303: - npc("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."); - stage = 304; - break; - case 304: - npc("If you're in the mood for a little rest and relaxation,", "there are a couple of nice fishing spots south of the", "town."); - stage = 305; - break; - case 305: - player("Thanks for the help!"); - stage = 306; - break; - case 306: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3680 }; - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/sophanem/TarikDialogue.java b/Server/src/main/java/core/game/node/entity/npc/city/sophanem/TarikDialogue.java deleted file mode 100644 index b8a1ede5b..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/sophanem/TarikDialogue.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.node.entity.npc.city.sophanem; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; - -/** - * Stand-in until we get the real dialogue for Tarik - * @author ceik - */ - -@Initializable -public class TarikDialogue extends DialoguePlugin { - public TarikDialogue() { - /** - * Empty - */ - } - public TarikDialogue(Player player){super(player);} - - @Override - public DialoguePlugin newInstance(Player player) { - return new TarikDialogue(player); - } - - @Override - public boolean open(Object... args){ - npc("I found this crazy looking pyramid here.","I can't seem to figure out what it's for.","Maybe you should head inside?"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId){ - switch(stage){ - case 0: - player("Oh, alright..."); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - @Override - public int[] getIds() { - return new int[] { 4478 }; - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/tzhaar/TzHaarMejJah.java b/Server/src/main/java/core/game/node/entity/npc/city/tzhaar/TzHaarMejJah.java deleted file mode 100644 index 497bd1bfe..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/tzhaar/TzHaarMejJah.java +++ /dev/null @@ -1,187 +0,0 @@ -package core.game.node.entity.npc.city.tzhaar; - -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the TzHaarMejJal dialogue. - * @author 'Vexia - * @author Empathy - */ -@Initializable -public class TzHaarMejJah extends DialoguePlugin { - - 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: - 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: - 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("Selec an Option", "What's a 'JalYt-Key'?", "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; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("tzhaar-mej"), 2617 }; - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/varrock/MuseumGuardDialogue.java b/Server/src/main/java/core/game/node/entity/npc/city/varrock/MuseumGuardDialogue.java deleted file mode 100644 index e324272c2..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/MuseumGuardDialogue.java +++ /dev/null @@ -1,88 +0,0 @@ -package core.game.node.entity.npc.city.varrock; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.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/java/core/game/node/entity/npc/city/varrock/MuseumGuardVarrockDialogue.java b/Server/src/main/java/core/game/node/entity/npc/city/varrock/MuseumGuardVarrockDialogue.java deleted file mode 100644 index e80a2caea..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/MuseumGuardVarrockDialogue.java +++ /dev/null @@ -1,69 +0,0 @@ -package core.game.node.entity.npc.city.varrock; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.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/java/core/game/node/entity/npc/city/varrock/VarrockBartenderDialogue.java b/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockBartenderDialogue.java deleted file mode 100644 index b67c7c2eb..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockBartenderDialogue.java +++ /dev/null @@ -1,138 +0,0 @@ -package core.game.node.entity.npc.city.varrock; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * The plugin used to handle the dialogue for the varrock bartender. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class VarrockBartenderDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code VarrockBartenderDialogue} {@code Object}. - */ - public VarrockBartenderDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code VarrockBartenderDialogue} {@code Object}. - * @param player the player. - */ - public VarrockBartenderDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 732, 731 }; - } - - @Override - 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?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "Yes please!", "No thanks.", "How much?"); - stage = 2; - break; - case 2: - 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; - - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How much?"); - stage = 30; - break; - } - - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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!"); - stage = 12; - } else { - end(); - player.getPacketDispatch().sendMessage("You need two gold coins to buy a beer."); - } - break; - case 12: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Cheers!"); - stage = 13; - break; - case 13: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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?"); - stage = 31; - break; - case 31: - interpreter.sendOptions("Select an Option", "Yes please!", "No thanks."); - stage = 32; - break; - case 32: - - 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; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new VarrockBartenderDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello."); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockBrokenCart.java b/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockBrokenCart.java deleted file mode 100644 index a8eb3647a..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockBrokenCart.java +++ /dev/null @@ -1,28 +0,0 @@ -package core.game.node.entity.npc.city.varrock; - -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/java/core/game/node/entity/npc/city/varrock/VarrockEastBartenderDialogue.java b/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockEastBartenderDialogue.java deleted file mode 100644 index 586be5d82..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockEastBartenderDialogue.java +++ /dev/null @@ -1,154 +0,0 @@ -package core.game.node.entity.npc.city.varrock; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import rs09.game.world.GameWorld; - -/** - * Handles the VarrockEastBartenderDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class VarrockEastBartenderDialogue extends DialoguePlugin { - - public VarrockEastBartenderDialogue() { - - } - - public VarrockEastBartenderDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 733 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "A glass of your finest ale please.", "Can you recommend where an adventurer might make his fortune?", "Do you know where I can get some good equipment?"); - stage = 1; - break; - case 1: - - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you know where I can get some good equipment?"); - stage = 30; - break; - } - - break; - - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No problemo. That'll be 2 coins."); - 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)); - end(); - player.getPacketDispatch().sendMessage("You buy a pint of beer."); - } else { - end(); - player.getPacketDispatch().sendMessage("You need 2 coins to buy ale."); - } - 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."); - stage = 21; - break; - case 21: - interpreter.sendOptions("Select an Option", "Oh ah well...", "Computer game? What are you talking about?", "Just a small clue?"); - stage = 22; - break; - case 22: - - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh ah well..."); - stage = 150; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Computer game? What are you talking about?"); - stage = 160; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "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() + "."); - 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?"); - 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."); - stage = 163; - break; - case 163: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I give up. You're obviously completely mad"); - stage = 164; - break; - case 164: - end(); - break; - case 150: - 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."); - 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."); - stage = 171; - break; - case 171: - end(); - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new VarrockEastBartenderDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What can I do yer for?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockSwordShopDialogue.java b/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockSwordShopDialogue.java deleted file mode 100644 index 0892eb482..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/city/varrock/VarrockSwordShopDialogue.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.node.entity.npc.city.varrock; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; - -/** - * Handles the VarrockSwordShopDialogue dialogue. - * @author 'Vexia - */ -public class VarrockSwordShopDialogue extends DialoguePlugin { - - public VarrockSwordShopDialogue() { - - } - - public VarrockSwordShopDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 551 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, please.", "No, I'm okay for swords right now."); - stage = 1; - break; - case 1: - - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, please."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I'm okay for swords right now."); - stage = 10; - break; - } - - break; - case 2: - end(); - npc.openShop(player); - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Come back if you need any."); - stage = 11; - break; - case 11: - end(); - break; - } - - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new VarrockSwordShopDialogue(player); - } - - @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?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/drop/RareDropTable.java b/Server/src/main/java/core/game/node/entity/npc/drop/RareDropTable.java deleted file mode 100644 index fd2a17557..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/drop/RareDropTable.java +++ /dev/null @@ -1,94 +0,0 @@ -package core.game.node.entity.npc.drop; - -import rs09.ServerConstants; -import core.game.node.item.Item; -import core.game.node.item.WeightedChanceItem; -import rs09.game.system.SystemLogger; -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 javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * Handles the rare drop table. - * @author Ceikry - */ -public final class RareDropTable { - - /** - * The item id of the item representing the rare drop table slot in a drop - * table. - */ - public static final int SLOT_ITEM_ID = 31; - - /** - * The rare drop table. - */ - private static final List TABLE = new ArrayList<>(20); - - - /** - * Initialize needed objects for xml reading/writing - */ - static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - static DocumentBuilder builder; - - static { - try { - builder = factory.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } - } - - public RareDropTable() throws ParserConfigurationException {} - - /** - * Initializes the rare drop table. - */ - public static void init(){ - if(ServerConstants.RDT_DATA_PATH != null && !new File(ServerConstants.RDT_DATA_PATH).exists()){ - SystemLogger.logErr("Can't locate RDT file at " + ServerConstants.RDT_DATA_PATH); - return; - } - parse(ServerConstants.RDT_DATA_PATH); - } - - /** - * Parses the xml file for the RDT. - * @param file the .xml file containing the RDT. - */ - public static void parse(String file){ - try { - Document doc = builder.parse(file); - - NodeList itemNodes = doc.getElementsByTagName("item"); - for(int i = 0; i < itemNodes.getLength(); i++){ - Node itemNode = itemNodes.item(i); - if(itemNode.getNodeType() == Node.ELEMENT_NODE){ - Element item = (Element) itemNode; - int itemId = Integer.parseInt(item.getAttribute("id")); - int minAmt = Integer.parseInt(item.getAttribute("minAmt")); - int maxAmt = Integer.parseInt(item.getAttribute("maxAmt")); - int weight = Integer.parseInt(item.getAttribute("weight")); - - TABLE.add(new WeightedChanceItem(itemId,minAmt,maxAmt,weight)); - } - } - } catch (Exception e){ - e.printStackTrace(); - } - } - - public static Item retrieve(){ - return RandomFunction.rollWeightedChanceTable(TABLE); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/npc/drop/SharedTables.java b/Server/src/main/java/core/game/node/entity/npc/drop/SharedTables.java deleted file mode 100644 index c842c7128..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/drop/SharedTables.java +++ /dev/null @@ -1,121 +0,0 @@ -package core.game.node.entity.npc.drop; - -import org.rs09.consts.Items; -import core.game.node.item.WeightedChanceItem; - -public enum SharedTables { - COMMON_SEEDS(new WeightedChanceItem[] { - new WeightedChanceItem(Items.LIMPWURT_SEED_5100,1,137), - new WeightedChanceItem(Items.STRAWBERRY_SEED_5323,1,131), - new WeightedChanceItem(Items.MARRENTILL_SEED_5292,1,125), - new WeightedChanceItem(Items.JANGERBERRY_SEED_5104,1,92), - new WeightedChanceItem(Items.TARROMIN_SEED_5293,1,85), - new WeightedChanceItem(Items.WILDBLOOD_SEED_5311,1,83), - new WeightedChanceItem(Items.WATERMELON_SEED_5321,1,63), - new WeightedChanceItem(Items.HARRALANDER_SEED_5294,1,56), - new WeightedChanceItem(Items.RANARR_SEED_5295,1,39), - new WeightedChanceItem(Items.WHITEBERRY_SEED_5105,1,34), - new WeightedChanceItem(Items.MUSHROOM_SPORE_5282,1,29), - new WeightedChanceItem(Items.TOADFLAX_SEED_5296,1,27), - new WeightedChanceItem(Items.BELLADONNA_SEED_5281,1,18), - new WeightedChanceItem(Items.IRIT_SEED_5297,1,18), - new WeightedChanceItem(Items.POISON_IVY_SEED_5106,1,13), - new WeightedChanceItem(Items.AVANTOE_SEED_5298,1,12), - new WeightedChanceItem(Items.CACTUS_SEED_5280,1,12), - new WeightedChanceItem(Items.KWUARM_SEED_5299,1,9), - new WeightedChanceItem(Items.SNAPDRAGON_SEED_5300,1,5), - new WeightedChanceItem(Items.CADANTINE_SEED_5301,1,4), - new WeightedChanceItem(Items.LANTADYME_SEED_5302,1,3), - new WeightedChanceItem(Items.DWARF_WEED_SEED_5303,1,2), - new WeightedChanceItem(Items.TORSTOL_SEED_5304,1,1) - }), - - FIXED_ALLOTMENT_SEEDS(new WeightedChanceItem[]{ - new WeightedChanceItem(Items.POTATO_SEED_5318, 4, 48), - new WeightedChanceItem(Items.ONION_SEED_5319,4,36), - new WeightedChanceItem(Items.CABBAGE_SEED_5324,4,24), - new WeightedChanceItem(Items.TOMATO_SEED_5322,3,12), - new WeightedChanceItem(Items.SWEETCORN_SEED_5320,3,6), - new WeightedChanceItem(Items.STRAWBERRY_SEED_5323,2,3), - new WeightedChanceItem(Items.WATERMELON_SEED_5321,2,2), - }), - - RARE_SEEDS(new WeightedChanceItem[]{ - new WeightedChanceItem(Items.TOADFLAX_SEED_5296,1,24), - new WeightedChanceItem(Items.IRIT_SEED_5297,1,16), - new WeightedChanceItem(Items.BELLADONNA_SEED_5281,1,16), - new WeightedChanceItem(Items.AVANTOE_SEED_5298,1,11), - new WeightedChanceItem(Items.POISON_IVY_SEED_5106,1,11), - new WeightedChanceItem(Items.CACTUS_SEED_5280,1,11), - new WeightedChanceItem(Items.KWUARM_SEED_5299,1,8), - new WeightedChanceItem(Items.SNAPDRAGON_SEED_5300,1,5), - new WeightedChanceItem(Items.CADANTINE_SEED_5301,1,4), - new WeightedChanceItem(Items.LANTADYME_SEED_5302,1,3), - new WeightedChanceItem(Items.DWARF_WEED_SEED_5303,1,2), - new WeightedChanceItem(Items.TORSTOL_SEED_5304,1,1) - }), - - TREE_HERB_SEEDS(new WeightedChanceItem[]{ - new WeightedChanceItem(Items.RANARR_SEED_5295,1,8), - new WeightedChanceItem(Items.SNAPDRAGON_SEED_5300,1,7), - new WeightedChanceItem(Items.TORSTOL_SEED_5304,1,6), - new WeightedChanceItem(Items.WATERMELON_SEED_5321,15,5), - new WeightedChanceItem(Items.WILLOW_SEED_5313,1,5), - new WeightedChanceItem(Items.MAPLE_SEED_5314,1,5), - new WeightedChanceItem(Items.YEW_SEED_5315,1,5), - new WeightedChanceItem(Items.PAPAYA_TREE_SEED_5288,1,4), - new WeightedChanceItem(Items.MAGIC_SEED_5316, 1, 3), - new WeightedChanceItem(Items.PALM_TREE_SEED_5289,1, 3), - new WeightedChanceItem(Items.SPIRIT_SEED_5317,1,2) - }), - - VARIABLE_ALLOTMENT_SEEDS(new WeightedChanceItem[]{ - new WeightedChanceItem(Items.POTATO_SEED_5318,1,4,64), - new WeightedChanceItem(Items.ONION_SEED_5319,1,3,32), - new WeightedChanceItem(Items.CABBAGE_SEED_5324,1,3,16), - new WeightedChanceItem(Items.TOMATO_SEED_5322,1,2,8), - new WeightedChanceItem(Items.SWEETCORN_SEED_5320,1,2,4), - new WeightedChanceItem(Items.STRAWBERRY_SEED_5323,1,2), - new WeightedChanceItem(Items.WATERMELON_SEED_5321,1,1) - }), - - HOPS_DROP_TABLE(new WeightedChanceItem[]{ - new WeightedChanceItem(Items.BARLEY_SEED_5305,4,35), - new WeightedChanceItem(Items.HAMMERSTONE_SEED_5307,4,28), - new WeightedChanceItem(Items.JUTE_SEED_5306,2,19), - new WeightedChanceItem(Items.ASGARNIAN_SEED_5308,3,17), - new WeightedChanceItem(Items.YANILLIAN_SEED_5309,2,12), - new WeightedChanceItem(Items.KRANDORIAN_SEED_5310,2,4), - new WeightedChanceItem(Items.WILDBLOOD_SEED_5311,1, 1) - }), - - HERBS(new WeightedChanceItem[]{ - new WeightedChanceItem(Items.GRIMY_GUAM_199, 1, 11), - new WeightedChanceItem(Items.GRIMY_MARRENTILL_201,1,8), - new WeightedChanceItem(Items.GRIMY_TARROMIN_203,1,6), - new WeightedChanceItem(Items.GRIMY_HARRALANDER_205,1,5), - new WeightedChanceItem(Items.GRIMY_RANARR_208,1,4), - new WeightedChanceItem(Items.GRIMY_IRIT_209,1,3), - new WeightedChanceItem(Items.GRIMY_AVANTOE_212,1,2), - new WeightedChanceItem(Items.GRIMY_KWUARM_213,1,2), - new WeightedChanceItem(Items.GRIMY_CADANTINE_215,1,2), - new WeightedChanceItem(Items.GRIMY_LANTADYME_2485,1,1), - new WeightedChanceItem(Items.GRIMY_DWARF_WEED_217,1,1) - }), - - USEFUL_HERBS(new WeightedChanceItem[]{ - new WeightedChanceItem(Items.GRIMY_AVANTOE_212,1,2), - new WeightedChanceItem(Items.GRIMY_SNAPDRAGON_3052,1,1), - new WeightedChanceItem(Items.GRIMY_RANARR_208,1,1), - new WeightedChanceItem(Items.GRIMY_TORSTOL_220,1,1) - }); - - - - WeightedChanceItem[] table; - SharedTables(WeightedChanceItem[] table){ - this.table = table; - } - - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/AbyssalTitanNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/AbyssalTitanNPC.java deleted file mode 100644 index 25e0c3076..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/AbyssalTitanNPC.java +++ /dev/null @@ -1,67 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -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/java/core/game/node/entity/npc/familiar/BabyChinchompaNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/BabyChinchompaNPC.java deleted file mode 100644 index e6841ff27..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/BabyChinchompaNPC.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.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/java/core/game/node/entity/npc/familiar/BloatedLeechNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/BloatedLeechNPC.java deleted file mode 100644 index c8503d3c9..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/BloatedLeechNPC.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -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; - -/** - * Represents the Bloated Leech familiar. - * @author Aero - */ -@Initializable -public class BloatedLeechNPC extends Familiar { - - /** - * Constructs a new {@code BloatedLeechNPC} {@code Object}. - */ - public BloatedLeechNPC() { - this(null, 6843); - } - - /** - * Constructs a new {@code BloatedLeechNPC} {@code Object}. - * @param owner The owner. - * @param id The id. - */ - public BloatedLeechNPC(Player owner, int id) { - super(owner, id, 3400, 12061, 6, WeaponInterface.STYLE_ACCURATE); - } - - @Override - public Familiar construct(Player owner, int id) { - return new BloatedLeechNPC(owner, id); - } - - @Override - protected boolean specialMove(FamiliarSpecial special) { - owner.getStateManager().remove(EntityState.POISONED); - 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().rechargePrayerPoints(); - getImpactHandler().manualHit(owner, RandomFunction.random(2), HitsplatType.NORMAL); - return true; - } - - @Override - public int[] getIds() { - return new int[] { 6843, 6844 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/KalphiteNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/KalphiteNPC.java deleted file mode 100644 index a9c78c6d8..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/KalphiteNPC.java +++ /dev/null @@ -1,117 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.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/java/core/game/node/entity/npc/familiar/PackYakNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/PackYakNPC.java deleted file mode 100644 index 7841dd807..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/PackYakNPC.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import rs09.game.system.config.ItemConfigParser; -import core.plugin.Initializable; -import core.game.node.entity.skill.summoning.SummoningScroll; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -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; - -/** - * Represents the Pack Yak familiar. - * @author Aero - */ -@Initializable -public class PackYakNPC extends BurdenBeast { - - /** - * Constructs a new {@code PackYakNPC} {@code Object}. - */ - public PackYakNPC() { - this(null, 6873); - } - - /** - * Constructs a new {@code PackYakNPC} {@code Object}. - * @param owner The owner. - * @param id The id. - */ - public PackYakNPC(Player owner, int id) { - super(owner, id, 5800, 12093, 12, 30, WeaponInterface.STYLE_AGGRESSIVE); - } - - @Override - public Familiar construct(Player owner, int id) { - return new PackYakNPC(owner, id); - } - - @Override - protected boolean specialMove(FamiliarSpecial special) { - Player player = owner; - Item item = new Item(special.getItem().getId(), 1); - if (item.getId() == SummoningScroll.WINTER_STORAGE_SCROLL.getItemId()) { - return false; - } - if (!item.getDefinition().getConfiguration(ItemConfigParser.BANKABLE, true)) { - player.sendMessage("A magical force prevents you from banking this item"); - return false; - } - Item remove = item; - if (!item.getDefinition().isUnnoted()) { - remove = new Item(item.getId(), 1); - item = new Item(item.getNoteChange(), 1); - } - if (player.getInventory().remove(remove) && player.getBank().add(item)) { - player.getDialogueInterpreter().close(); - graphics(Graphics.create(1358)); - player.getPacketDispatch().sendMessage("The pak yak has sent an item to your bank."); - } else { - player.getPacketDispatch().sendMessage("The pak yak can't send that item to your bank."); - } - return true; - } - - @Override - public void visualizeSpecialMove() { - owner.visualize(Animation.create(7660), Graphics.create(1316)); - } - - @Override - public int[] getIds() { - return new int[] { 6873, 6874 }; - } - - @Override - protected String getText() { - return "Baroo!"; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/SnakelingNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/SnakelingNPC.java deleted file mode 100644 index 09650a274..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SnakelingNPC.java +++ /dev/null @@ -1,114 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.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/java/core/game/node/entity/npc/familiar/SpiritGraahkNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritGraahkNPC.java deleted file mode 100644 index 599996f1f..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritGraahkNPC.java +++ /dev/null @@ -1,51 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -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); - } - - @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/java/core/game/node/entity/npc/familiar/SpiritKalphiteNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritKalphiteNPC.java deleted file mode 100644 index 43f8caecd..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/SpiritKalphiteNPC.java +++ /dev/null @@ -1,86 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import java.util.List; - -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.Entity; -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.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.RegionManager; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.game.world.update.flag.context.Graphics; - -/** - * Represents the Spirit Kalphite familiar. - * @author Vexia - * @author Aero - */ -@Initializable -public class SpiritKalphiteNPC extends BurdenBeast { - - /** - * Constructs a new {@code SpiritKalphiteNPC} {@code Object}. - */ - public SpiritKalphiteNPC() { - this(null, 6994); - } - - /** - * Constructs a new {@code SpiritKalphiteNPC} {@code Object}. - * @param owner The owner. - * @param id The id. - */ - public SpiritKalphiteNPC(Player owner, int id) { - super(owner, id, 2200, 12063, 6, 6, WeaponInterface.STYLE_DEFENSIVE); - } - - @Override - public Familiar construct(Player owner, int id) { - return new SpiritKalphiteNPC(owner, id); - } - - @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; - } - - @Override - public String getText() { - return "Hsssss!"; - } - - @Override - public int[] getIds() { - return new int[] { 6994, 6995 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/familiar/VetionNPC.java b/Server/src/main/java/core/game/node/entity/npc/familiar/VetionNPC.java deleted file mode 100644 index e02446283..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/familiar/VetionNPC.java +++ /dev/null @@ -1,33 +0,0 @@ -package core.game.node.entity.npc.familiar; - -import core.plugin.Initializable; -import core.game.content.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/java/core/game/node/entity/npc/other/BennyNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/BennyNPC.java deleted file mode 100644 index 27b769ccf..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/BennyNPC.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.node.entity.npc.other; - -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/java/core/game/node/entity/npc/other/CitizenNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/CitizenNPC.java deleted file mode 100644 index 430ec7f59..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/CitizenNPC.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.npc.AbstractNPC; -import core.game.world.map.Location; - -/** - * Represents a generic citizen npc. - * @author 'Vexia - * @version 1.9 - */ -@Initializable -public final class CitizenNPC extends AbstractNPC { - - /** - * Represents the NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 1, 2, 3, 4, 5, 6, 16, 24, 25, 170, 351, 352, 353, 354, 359, 360, 361, 362, 363, 663, 726, 727, 728, 729, 730, 1086, 2675, 2776, 3224, 3225, 3227, 5923, 5924 }; - - /** - * Constructs a new {@code CitizenNPC} {@code Object}. - */ - public CitizenNPC() { - super(0, null, true); - } - - /** - * Constructs a new {@code CitizenNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - private CitizenNPC(int id, Location location) { - super(id, location, true); - } - - @Override - public void init() { - super.init(); - getSkills().setLevel(Skills.HITPOINTS, 7); - getSkills().setStaticLevel(Skills.HITPOINTS, 7); - getSkills().setLifepoints(7); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new CitizenNPC(id, location); - } - - @Override - public int[] getIds() { - return ID; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/CowNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/CowNPC.java deleted file mode 100644 index aeb831611..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/CowNPC.java +++ /dev/null @@ -1,70 +0,0 @@ -package core.game.node.entity.npc.other; - -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.player.link.diary.DiaryType; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the a cow npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CowNPC extends AbstractNPC { - - /** - * Represents the force chat to use. - */ - private static final String FORCE_CHAT = "Moo"; - - /** - * Constructs a new {@code CowNPC} {@code Object}. - */ - public CowNPC() { - super(0, null, true); - } - - /** - * Constructs a new {@code CowNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public CowNPC(int id, Location location) { - super(id, location, true); - } - - @Override - public void tick() { - if (RandomFunction.random(45) == 5 && this.getId() != 1766) { // calves don't moo - sendChat(FORCE_CHAT); - } - super.tick(); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new CowNPC(id, location); - } - - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer != null) { - Player player = killer.asPlayer(); - // Obtain a cow-hide from a cow in the field north-east of

Lumbridge - if (player.getViewport().getRegion().getId() == 12850 || player.getViewport().getRegion().getId() == 12851) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 1); - } - } - } - - @Override - public int[] getIds() { - return new int[] { 81, 397, 955, 1766, 1767, 3309 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/DuckNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/DuckNPC.java deleted file mode 100644 index 4c833c265..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/DuckNPC.java +++ /dev/null @@ -1,69 +0,0 @@ -package core.game.node.entity.npc.other; - -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.player.link.diary.DiaryType; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the a duck npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DuckNPC extends AbstractNPC { - - /** - * Represents the force chat to use. - */ - private static final String FORCE_CHAT = "Quack!"; - - /** - * Constructs a new {@code DuckNPC} {@code Object}. - */ - public DuckNPC() { - super(0, null); - } - - /** - * Constructs a new {@code DuckNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public DuckNPC(int id, Location location) { - super(id, location, true); - } - - @Override - public void tick() { - if (RandomFunction.random(45) == 5) { - sendChat(FORCE_CHAT); - } - super.tick(); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new DuckNPC(id, location); - } - - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - final Player player = killer.asPlayer(); - if (this.getLocation().withinDistance(new Location(2991,3383,0))) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR,0, 9); - } - } - } - - @Override - public int[] getIds() { - return new int[] { 46, 2693, 6113 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/ElementalMonsterNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/ElementalMonsterNPC.java deleted file mode 100644 index 58012f6de..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/ElementalMonsterNPC.java +++ /dev/null @@ -1,74 +0,0 @@ -package core.game.node.entity.npc.other; - -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.player.link.diary.DiaryType; -import core.game.world.map.Location; -import core.plugin.Initializable; - -/** - * Represents the Earth, Fire, Air, and Water Elemental NPCs. - * @author afaroutdude - */ -@Initializable -public final class ElementalMonsterNPC extends AbstractNPC { - - /** - * Constructs a new {@code DuckNPC} {@code Object}. - */ - public ElementalMonsterNPC() { - super(0, null); - } - - /** - * Constructs a new {@code DuckNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public ElementalMonsterNPC(int id, Location location) { - super(id, location, true); - } - - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ElementalMonsterNPC(id, location); - } - - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - final Player player = killer.asPlayer(); - if (this.getLocation().withinDistance(new Location(2719, 9889, 0), 100)) { - switch (this.getId()) { - case 1019: - player.setAttribute("/save:diary:seers:elemental:fire", true); - break; - case 1020: - player.setAttribute("/save:diary:seers:elemental:earth", true); - break; - case 1021: - player.setAttribute("/save:diary:seers:elemental:air", true); - break; - case 1022: - player.setAttribute("/save:diary:seers:elemental:water", true); - break; - } - if (player.getAttribute("diary:seers:elemental:fire", false) - && player.getAttribute("diary:seers:elemental:earth", false) - && player.getAttribute("diary:seers:elemental:air", false) - && player.getAttribute("diary:seers:elemental:water", false)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 4); - } - } - } - } - - @Override - public int[] getIds() { - return new int[] { 1019, 1020, 1021, 1022 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/FishingNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/FishingNPC.java deleted file mode 100644 index 88dcd7b75..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/FishingNPC.java +++ /dev/null @@ -1,104 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.node.entity.skill.fishing.FishSpots; -import core.game.node.entity.npc.AbstractNPC; -import rs09.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/java/core/game/node/entity/npc/other/GraveStoneNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/GraveStoneNPC.java deleted file mode 100644 index 774ac2807..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/GraveStoneNPC.java +++ /dev/null @@ -1,470 +0,0 @@ -package core.game.node.entity.npc.other; - -import java.util.List; - -import core.cache.def.impl.NPCDefinition; -import core.game.component.Component; -import core.game.node.entity.skill.Skills; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -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.node.entity.player.link.HintIconManager; -import core.game.node.entity.player.link.grave.GraveManager; -import core.game.node.entity.player.link.grave.GraveType; -import core.game.node.item.GroundItem; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import rs09.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 core.plugin.Initializable; -import rs09.plugin.PluginManager; - -/** - * Handles a gravestone npc. - * @author Vexia - */ -@Initializable -public class GraveStoneNPC extends AbstractNPC { - - /** - * The owner of the gravestone. - */ - private String owner; - - /** - * The display name. - */ - private String display; - - /** - * The gravestone type. - */ - private GraveType type; - - /** - * The life decay time. - */ - private int life = -1; - - /** - * If the stone is blessed. - */ - private boolean blessed; - - /** - * The ground items list. - */ - private List items; - - /** - * Constructs a new {@Code GraveStoneNPC} {@Code Object} - */ - public GraveStoneNPC() { - super(-1, null); - } - - /** - * Constructs a new {@Code GraveStoneNPC} {@Code Object} - * @param id the id. - * @param location the location. - */ - public GraveStoneNPC(int id, Location location) { - super(id, location); - super.setWalks(false); - super.setNeverWalks(true); - } - - @Override - public void init() { - super.init(); - Player player = Repository.getPlayerByName(owner); - if (player != null) { - HintIconManager.registerHintIcon(player, this); - } - lock(); - animate(Animation.create(7394)); - } - - @Override - public void handleTickActions() { - if (life == -1) { - return; - } - if (life < GameWorld.getTicks()) { - clear(); - message("Your gravestone has collapsed."); - return; - } - int minutes = getMinutes(); - if (minutes <= 1) { - int seconds = getSeconds(); - int transformId = -1; - if (seconds == 100) { - transformId = getOriginalId() + 1; - } else if (seconds == 30) { - transformId = getOriginalId() + 2; - } - if (transformId != -1) { - transform(transformId); - } - } - } - - @Override - public void clear() { - super.clear(); - Player player = Repository.getPlayerByName(owner); - if (player != null) { - player.getHintIconManager().clear(); - } - if (owner != null) { - GraveManager.getGraves().remove(owner); - } - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - return false; - } - - @Override - public boolean canAttack(final Entity victim) { - return false; - } - - @Override - public Object fireEvent(String identifier, Object... objects) { - switch (identifier) { - case "updateItems": - for (GroundItem item : items) { - if (item == null) { - continue; - } - item.setDropper((Player) objects[0]); - } - break; - } - return null; - } - - @SuppressWarnings("unchecked") - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - GraveStoneNPC npc = new GraveStoneNPC(id, location); - if (objects != null && objects.length > 1) { - npc.setOwner((String) objects[0]); - npc.setLife((int) objects[1]); - npc.setItems((List) objects[2]); - npc.setType((GraveType) objects[3]); - npc.setDisplay((String) objects[4]); - } - GraveManager.getGraves().put(npc.getOwner(), npc); - return npc; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new GraveStonePlugin()); - return super.newInstance(arg); - } - - @Override - public int[] getIds() { - return new int[] { 6565, 6568, 6571, 6574, 6577, 6580, 6583, 6586, 6589, 6592, 6595, 6598, 6601 }; - } - - /** - * Reads the grave message. - * @param player the player. - */ - public void read(Player player) { - player.getConfigManager().set(1146, 1); - player.getInterfaceManager().open(new Component(266)); - if (!isOwner(player.getName())) { - player.getPacketDispatch().sendString(getMessage(), 266, 23); - } else { - final int minutes = getMinutes(); - String message; - if (minutes < 1) { - int seconds = getSeconds(); - message = "It looks like it'll survive another " + (seconds > 1 ? seconds + " seconds." : "second."); - } else { - message = "It looks like it'll survive another " + (minutes > 1 ? minutes + " minutes" : "minute") + "."; - } - player.getPacketDispatch().sendMessage(message); - player.getPacketDispatch().sendString(getMessage(), 266, 23); - } - } - - /** - * Repairs the grave. - * @param player the player. - */ - public void repair(Player player) { - if (player.getSkills().getStaticLevel(Skills.PRAYER) < 2) { - player.getDialogueInterpreter().sendDialogue("You need a prayer level of 2 to repair a gravestone."); - return; - } - if (getId() == getOriginalId()) { - player.sendMessage("This grave does not need repairing."); - return; - } - int seconds = type.getDecay() * 60; - int ticks = (1000 * seconds) / 600; - reTransform(); - updateItems(ticks); - setLife(GameWorld.getTicks() + ticks); - } - - /** - * Blesses the grave. - * @param player the player. - */ - public void bless(Player player) { - if (isOwner(player.getName())) { - player.getPacketDispatch().sendMessage("The gods don't seem to approve of people attempting to bless their own"); - player.getPacketDispatch().sendMessage("gravestones."); - return; - } - if (player.getSkills().getStaticLevel(Skills.PRAYER) < 70) { - player.getDialogueInterpreter().sendDialogue("You need a prayer level of 70 to bless a gravestone."); - return; - } - if (player.getSkills().getPrayerPoints() == 0) { - player.getDialogueInterpreter().sendDialogue("You don't have enough prayer points to do that."); - return; - } - if (isBlessed()) { - player.getPacketDispatch().sendMessage("This gravestone has already been blessed."); - return; - } - reTransform(); - setBlessed(true); - updateItems(6100); - player.animate(Animation.create(645)); - graphics(Graphics.create(1274)); - setLife(GameWorld.getTicks() + 6000); - message(player.getUsername() + " has blessed your grave, it will remain for another 60 minutes."); - } - - /** - * Demolishes the grave. - * @param player the player. - */ - public void demolish(Player player) { - if (!isOwner(player.getName())) { - player.getPacketDispatch().sendMessage("This is not your grave!"); - return; - } - clear(); - player.sendMessage("You destroyed your grave!"); - } - - /** - * Updates the items with a new decay. - * @param ticks the ticks. - */ - private void updateItems(int ticks) { - if (getItems() != null) { - for (GroundItem item : getItems()) { - if (item == null) { - continue; - } - if (item.isActive()) { - item.setDecayTime(ticks); - } - } - } - } - - /** - * Messages the owner. - * @param message the message. - */ - private void message(String message) { - Player o = Repository.getPlayerByName(owner); - if (o != null && o.isActive()) { - o.sendMessage(message); - } - } - - /** - * Gets the minutes left. - * @return the minutes. - */ - public int getMinutes() { - return (life - GameWorld.getTicks()) / 100; - } - - /** - * Gets the seconds left. - * @return the seconds. - */ - public int getSeconds() { - return (life - GameWorld.getTicks()) * 600 / 1000; - } - - /** - * Gets the message. - * @return the message. - */ - public String getMessage() { - int mins = getMinutes(); - return type.getMessage().replace("@name", display).replace("@mins", (mins > 1 ? mins + " minutes" : mins + " minute")); - } - - /** - * Checks if this name is the owner. - * @param name the name. - * @return {@code True} if so. - */ - public boolean isOwner(String name) { - return owner.equals(name); - } - - /** - * Gets the owner. - * @return the owner - */ - public String getOwner() { - return owner; - } - - /** - * Gets the display. - * @return the display - */ - public String getDisplay() { - return display; - } - - /** - * Sets the badisplay. - * @param display the display to set. - */ - public void setDisplay(String display) { - this.display = display; - } - - /** - * Sets the baowner. - * @param owner the owner to set. - */ - public void setOwner(String owner) { - this.owner = owner; - } - - /** - * Gets the life. - * @return the life - */ - public int getLife() { - return life; - } - - /** - * Sets the balife. - * @param life the life to set. - */ - public void setLife(int life) { - this.life = life; - } - - /** - * Gets the type. - * @return the type - */ - public GraveType getType() { - return type; - } - - /** - * Sets the batype. - * @param type the type to set. - */ - public void setType(GraveType type) { - this.type = type; - } - - /** - * Gets the blessed. - * @return the blessed - */ - public boolean isBlessed() { - return blessed; - } - - /** - * Sets the bablessed. - * @param blessed the blessed to set. - */ - public void setBlessed(boolean blessed) { - this.blessed = blessed; - } - - /** - * Gets the items. - * @return the items - */ - public List getItems() { - return items; - } - - /** - * Sets the baitems. - * @param items the items to set. - */ - public void setItems(List items) { - this.items = items; - } - - /** - * Handles the interactions of a grave stone. - * @author Vexia - */ - public class GraveStonePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (GraveType g : GraveType.values()) { - for (int start = g.getNpcId(); start < g.getNpcId() + 3; start++) { - NPCDefinition.forId(start).getHandlers().put("option:read", this); - NPCDefinition.forId(start).getHandlers().put("option:bless", this); - NPCDefinition.forId(start).getHandlers().put("option:demolish", this); - NPCDefinition.forId(start).getHandlers().put("option:repair", this); - } - } - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (!(node instanceof GraveStoneNPC)) { - player.sendMessage("Error! NPC is not instanceof a GraveStoneNPC."); - return true; - } - GraveStoneNPC grave = (GraveStoneNPC) node; - switch (option) { - case "read": - grave.read(player); - break; - case "bless": - grave.bless(player); - break; - case "demolish": - grave.demolish(player); - break; - case "repair": - grave.repair(player); - break; - } - return true; - } - - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/GuardNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/GuardNPC.java deleted file mode 100644 index b66145608..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/GuardNPC.java +++ /dev/null @@ -1,48 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.node.entity.npc.AbstractNPC; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Represents the abstract knight npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -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 }; - - /** - * Constructs a new {@code GuardNPC} {@code Object}. - */ - public GuardNPC() { - super(0, null, true); - this.setAggressive(false); - } - - /** - * Constructs a new {@code GuardNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - private GuardNPC(int id, Location location) { - super(id, location, true); - this.setAggressive(false); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new GuardNPC(id, location); - } - - @Override - public int[] getIds() { - return ID; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/HighwayManNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/HighwayManNPC.java deleted file mode 100644 index 5ed04ba9e..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/HighwayManNPC.java +++ /dev/null @@ -1,71 +0,0 @@ -package core.game.node.entity.npc.other; - -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.player.link.diary.DiaryType; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Represents the highway man npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HighwayManNPC extends AbstractNPC { - - /** - * Represents the cape item. - */ - private static final Item CAPE = new Item(1019); - - /** - * Constructs a new {@code HighwayManNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public HighwayManNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new {@code HighwayManNPC} {@code Object}. - */ - public HighwayManNPC() { - super(0, null); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (getId() == 180) { - GroundItemManager.create(new Item(526), getLocation(), (Player) killer); - GroundItemManager.create(CAPE, getLocation(), (Player) killer); - } - if (killer instanceof Player) { - final Player player = killer.asPlayer(); - if (!player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).isComplete(0, 10)) { - player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).updateTask(player, 0, 10, true); - } - } - } - - @Override - public void onAttack(Entity target) { - sendChat("Stand and deliver!"); - } - - @Override - public int[] getIds() { - return new int[] { 180, 2677 }; - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new HighwayManNPC(id, location); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/JogreNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/JogreNPC.java deleted file mode 100644 index 37583de6a..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/JogreNPC.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.node.entity.npc.other; - -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.player.link.diary.DiaryType; -import core.game.world.map.Location; -import core.plugin.Initializable; - -/** - * Represents a Jogre NPC. - * @author Vexia - */ -@Initializable -public class JogreNPC extends AbstractNPC { - - /** - * If the jogre is in the pothole zone. - */ - private boolean inPothole; - - /** - * Constructs a new {@code JogreNPC} {@code Object} - * @param id the id. - * @param location the location. - */ - public JogreNPC(int id, Location location) { - super(id, location); - } - - @Override - public void init() { - super.init(); - inPothole = this.getViewport().getRegion().getId() == 11412; - } - - /** - * Constructs a new {@code JogreNPC} {@code Object} - */ - public JogreNPC() { - this(-1, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new JogreNPC(id, location); - } - - @Override - public void finalizeDeath(Entity killer) { - if (inPothole && killer.isPlayer()) { - Player player = killer.asPlayer(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 0, 9); - } - super.finalizeDeath(killer); - } - - @Override - public int[] getIds() { - return new int[] { 113 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/LumbridgeSwampGiantRatNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/LumbridgeSwampGiantRatNPC.java deleted file mode 100644 index 1d669f90d..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/LumbridgeSwampGiantRatNPC.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.node.entity.npc.other; - -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.player.link.diary.DiaryType; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Represents a rat npc. - * @author afaroutdude - */ -@Initializable -public class LumbridgeSwampGiantRatNPC extends AbstractNPC { - - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 86 }; - - /** - * Constructs a new {@code RatNPC} {@code Object}. - */ - public LumbridgeSwampGiantRatNPC() { - super(0, null); - } - - /** - * Constructs a new {@code RatNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - private LumbridgeSwampGiantRatNPC(int id, Location location) { - super(id, location, true); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new LumbridgeSwampGiantRatNPC(id, location); - } - - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - - // Kill a giant rat in Lumbridge Swamp - if (killer instanceof Player && (killer.getViewport().getRegion().getId() == 12593 || killer.getViewport().getRegion().getId() == 12849)) { - killer.asPlayer().getAchievementDiaryManager().finishTask(killer.asPlayer(), DiaryType.LUMBRIDGE, 1, 7); - } - } - - @Override - public int[] getIds() { - return ID; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/MetalDragonNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/MetalDragonNPC.java deleted file mode 100644 index c557cd54a..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/MetalDragonNPC.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.node.entity.npc.other; - -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.combat.handlers.DragonfireSwingHandler; -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.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; - -/** - * Handles a metal dragon (bronze, iron, steel). - * @author Emperor - */ -@Initializable -public final class MetalDragonNPC extends AbstractNPC { - - /** - * The dragonfire attack. - */ - private static final SwitchAttack DRAGONFIRE = DragonfireSwingHandler.get(false, 52, new Animation(81, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 54, 40, 36, 41, 46, 20, 255)); - - /** - * 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(91, Priority.HIGH)), DRAGONFIRE); - - /** - * Constructs a new {@code MetalDragonNPC} {@code Object}. - */ - public MetalDragonNPC() { - super(1590, null); - } - - /** - * Constructs a new {@code MetalDragonNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - public MetalDragonNPC(int id, Location location) { - super(id, location); - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return combatAction; - } - - @Override - public void finalizeDeath(Entity killer) { - if (killer.isPlayer() && (killer.getViewport().getRegion().getId() == 10899 || killer.getViewport().getRegion().getId() == 10900)) { - Player player = killer.asPlayer(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 9); - } - super.finalizeDeath(killer); - } - - @Override - public int getDragonfireProtection(boolean fire) { - return 0x2 | 0x4 | 0x8; - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new MetalDragonNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] { 1590, // Bronze dragon - 1591, // Iron dragon - 1592, // Steel dragon - 3590 // Steel dragon (POH) - }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/RasoloNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/RasoloNPC.java deleted file mode 100644 index 8d38342ad..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/RasoloNPC.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.cache.def.impl.NPCDefinition; -import core.game.content.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/java/core/game/node/entity/npc/other/SheepNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/SheepNPC.java deleted file mode 100644 index 3f25c0018..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/SheepNPC.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.node.entity.npc.other; - -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/java/core/game/node/entity/npc/other/ShopNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/ShopNPC.java deleted file mode 100644 index 0698a184a..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/ShopNPC.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.node.entity.npc.AbstractNPC; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Represents the abstract npc of a generic owner of a shop. - * @author 'Vexia - */ -@Initializable -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, 3824, 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 }; - - /** - * Constructs a new {@code ShopNPC} {@code Object}. - */ - public ShopNPC() { - super(0, null, true); - } - - /** - * Constructs a new {@code ShopNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - private ShopNPC(int id, Location location) { - super(id, location, true); - } - - @Override - public void init() { - super.init(); - setWalks(true); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ShopNPC(id, location); - } - - @Override - public int getWalkRadius() { - return 3; - } - - @Override - public int[] getIds() { - return ID; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/Student.java b/Server/src/main/java/core/game/node/entity/npc/other/Student.java deleted file mode 100644 index 3b4fedb10..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/Student.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.node.entity.npc.other; - -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/java/core/game/node/entity/npc/other/TribesmanNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/TribesmanNPC.java deleted file mode 100644 index dfb40dc23..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/TribesmanNPC.java +++ /dev/null @@ -1,48 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.node.entity.npc.AbstractNPC; -import core.game.world.map.Location; -import core.plugin.Initializable; -import rs09.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/java/core/game/node/entity/npc/other/TutorialChickenNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/TutorialChickenNPC.java deleted file mode 100644 index bf09c9a59..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/TutorialChickenNPC.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -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.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Represents the tutorial chicken npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class TutorialChickenNPC extends AbstractNPC { - - /** - * Constructs a new {@code TutorialChickenNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public TutorialChickenNPC(int id, Location location) { - super(id, location, true); - } - - /** - * Constructs a new {@code TutorialChickenNPC} {@code Object}. - */ - public TutorialChickenNPC() { - super(0, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new TutorialChickenNPC(id, location); - } - - @Override - public void onImpact(Entity entity, BattleState state) { - super.onImpact(entity, state); - if (state.getStyle() == CombatStyle.MAGIC) { - final Player player = (Player) entity; - if (TutorialSession.getExtension(player).getStage() == 70) { - TutorialStage.load(player, 71, false); - } - } - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - } - - @Override - public int[] getIds() { - return new int[] { 41 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/WerewolfNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/WerewolfNPC.java deleted file mode 100644 index 42c4c9527..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/WerewolfNPC.java +++ /dev/null @@ -1,50 +0,0 @@ -package core.game.node.entity.npc.other; - -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/java/core/game/node/entity/npc/other/ZombieNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/ZombieNPC.java deleted file mode 100644 index 510186043..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/ZombieNPC.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.node.entity.npc.other; - -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.player.link.diary.DiaryType; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Handles the zombie npc. - * @author Vexia - * - */ -@Initializable -public class ZombieNPC extends AbstractNPC { - - /** - * Constructs the {@code ZombieNPC} - */ - public ZombieNPC() { - super(0, null); - } - - /** - * Constructs the {@code ZombieNPC} - * @param id The id. - * @param location The location. - */ - public ZombieNPC(int id, Location location) { - super(id, location); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - Player player = killer.asPlayer(); - // Defeat a zombie in the sewers under the jail - if (player.getViewport().getRegion().getId() == 12438 || player.getViewport().getRegion().getId() == 12439) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 18); - } - } - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ZombieNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] {73, 74}; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/revenant/CorruptEquipment.java b/Server/src/main/java/core/game/node/entity/npc/revenant/CorruptEquipment.java deleted file mode 100644 index 9d6f8f401..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/revenant/CorruptEquipment.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.game.node.entity.npc.revenant; - -import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.DegradableEquipment; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import rs09.plugin.PluginManager; - -/** - * Handles the degrading of corrupt equipment. - * @author Vexia - * - */ -public class CorruptEquipment extends DegradableEquipment { - - /** - * Constructs a new @{Code CorruptEquipment} object. - * @param slot The slot. - * @param itemIds The item ids. - */ - public CorruptEquipment(int slot, int[] itemIds) { - super(slot, itemIds); - } - - /** - * Initializes the corrupt equipment degarding. - */ - public static void init() { - PluginManager.definePlugin(new CorruptEquipment(EquipmentContainer.SLOT_CHEST, new int[] {13958, 13960})); - } - - @Override - public void degrade(Player player, Entity entity, Item item) { - - } - - @Override - public int getDropItem(int itemId) { - return 0; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/revenant/PVPEquipment.java b/Server/src/main/java/core/game/node/entity/npc/revenant/PVPEquipment.java deleted file mode 100644 index c7f65b5d6..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/revenant/PVPEquipment.java +++ /dev/null @@ -1,42 +0,0 @@ -package core.game.node.entity.npc.revenant; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.DegradableEquipment; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import rs09.plugin.PluginManager; - -/** - * Handles the degrading of PVP armour. - * @author Vexia - * - */ -public class PVPEquipment extends DegradableEquipment { - - /** - * Constructs a new @{Code PVPEquipment} object. - * @param slot The slot. - * @param itemIds The item ids. - */ - public PVPEquipment(int slot, int[] itemIds) { - super(slot, itemIds); - } - - /** - * Initializes the PVP equipment degrading. - */ - public static void init() { - PluginManager.definePlugin(new PVPEquipment(1, null)); - } - - @Override - public void degrade(Player player, Entity entity, Item item) { - - } - - @Override - public int getDropItem(int itemId) { - return 0; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantCombatHandler.java b/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantCombatHandler.java deleted file mode 100644 index e71a4883c..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantCombatHandler.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.node.entity.npc.revenant; - -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.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 rs09.game.node.entity.combat.handlers.MultiSwingHandler; -import rs09.game.world.GameWorld; - -/** - * Handles the multi swing combat handler for revenants. - * @author Vexia - */ -public class RevenantCombatHandler extends MultiSwingHandler { - - /** - * The magic graphics. - */ - private static final Graphics MAGIC_GRAPHIC = Graphics.create(1276); - - /** - * The range graphics. - */ - private static final Graphics RANGE_GRAPHIC = Graphics.create(1278); - - /** - * Constructs a new {@code RevenantCombatHandler} {@code Object} - */ - public RevenantCombatHandler(Animation meleeAnimation, Animation magicAnimation, Animation rangeAnimation) { - super(true, new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), meleeAnimation), new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), rangeAnimation, createProjectile(RANGE_GRAPHIC)), new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), magicAnimation, createProjectile(MAGIC_GRAPHIC))); - } - - @Override - public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - if (victim instanceof Player) { - SwitchAttack attack = getCurrent(); - if (attack != null) { - if (attack.getStyle() == CombatStyle.RANGE) { - victim.asPlayer().getAudioManager().send(4061, true); - } - } - } - super.visualizeImpact(entity, victim, state); - } - - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - 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); - } 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); - } else { - victim.getStateManager().set(EntityState.TELEBLOCK, ticks); - } - } - } - } - if (!victim.getStateManager().hasState(EntityState.POISONED) && (WildernessZone.getWilderness(entity) >= 50 || entity.getId() == 6998)) { - victim.getStateManager().register(EntityState.POISONED, false, 68, entity); - } - super.impact(entity, victim, state); - } - - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - super.visualize(entity, victim, state); - if (victim.isPlayer()) { - SwitchAttack attack = getCurrent(); - if (attack != null) { - if (attack.getStyle() == CombatStyle.MAGIC) { - victim.asPlayer().getAudioManager().send(202, true); - } else if (attack.getStyle() == CombatStyle.RANGE) { - victim.asPlayer().getAudioManager().send(4062, true); - } - } - } - } - - /** - * Creates a projectile with a graphic id. - * @param graphic the graphic. - * @return the projectile. - */ - public static Projectile createProjectile(Graphics graphic) { - return Projectile.create(null, null, graphic.getId(), 48, 36, 34, 20); - } -} diff --git a/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantPlugin.java b/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantPlugin.java deleted file mode 100644 index 45948dab7..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/revenant/RevenantPlugin.java +++ /dev/null @@ -1,90 +0,0 @@ -package core.game.node.entity.npc.revenant; - -import java.util.ArrayList; -import java.util.List; - -import core.game.node.entity.npc.NPC; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; -import rs09.plugin.PluginManager; -import core.tools.RandomFunction; - -/** - * Handles the revenants. - * @author Vexia - */ -@Initializable -public class RevenantPlugin implements Plugin { - - /** - * The revenants npc. - */ - private static final List REVENANTS = new ArrayList<>(20); - - /** - * The spawning locations. - */ - private static final Location[] SPAWN_LOCATIONS = new Location[] { new Location(2968, 3695), new Location(2956, 3716), new Location(2969, 3753), new Location(2967, 3817), new Location(2976, 3849), new Location(2976, 3725), new Location(2973, 3576), new Location(2982, 3850), new Location(3348, 3822), new Location(3362, 3808), new Location(3026, 3734), new Location(2961, 3792), new Location(2984, 3801), new Location(2974, 3744), new Location(2984, 3718), new Location(2990, 3695), new Location(2960, 3590), new Location(3020, 3674), new Location(3019, 3723), new Location(3019, 3822), Location.create(3123, 3567, 0), Location.create(3123, 3567, 0), Location.create(3201, 3678, 0), Location.create(3220, 3755, 0), Location.create(3249, 3882, 0), Location.create(3283, 3893, 0), Location.create(3034, 3938, 0), Location.create(2964, 3617, 0), Location.create(3253, 3922, 0), Location.create(3205, 3907, 0), Location.create(3194, 3895, 0), Location.create(3168, 3788, 0), Location.create(3287, 3557, 0), Location.create(3327, 3558, 0), Location.create(3364, 3536, 0), Location.create(3262, 3595, 0), Location.create(3099, 3957, 0), Location.create(3028, 3915, 0), Location.create(3285, 3922, 0), Location.create(2980, 3855, 0), Location.create(3243, 3917, 0), Location.create(3190, 3630, 0), Location.create(3188, 3585, 0), Location.create(3117, 3590, 0), Location.create(3136, 3624, 0), Location.create(3356, 3701, 0) }; - - /** - * The maximum amount of revenants spawned. - */ - private static final int MAX = 20; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.definePlugin(new RevenantNPC()); - //CorruptEquipment.init(); - //PVPEquipment.init(); - spawn(); - return this; - } - - /** - * Spawns the revenants. - */ - public static void spawn() { - int size = REVENANTS.size(); - int left = MAX - size; - List taken = new ArrayList<>(20); - for (NPC n : REVENANTS) { - taken.add(n.getProperties().getSpawnLocation()); - } - if (left > 0) { - int spawnAmount = RandomFunction.random(1, left); - if (size == 0) { - spawnAmount = MAX; - } - for (int i = 0; i < spawnAmount; i++) { - Location loc = null; - while (loc == null) { - Location l = RandomFunction.getRandomElement(SPAWN_LOCATIONS); - if (taken.contains(l)) { - continue; - } - loc = l; - } - taken.add(loc); - RevenantType type = RandomFunction.getRandomElement(RevenantType.values()); - int id = type.getIds()[0]; - NPC revenant = NPC.create(id, loc); - revenant.init(); - } - } - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - /** - * Gets the revenants. - * @return the revenants - */ - public static List getRevenants() { - return REVENANTS; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/Player.java b/Server/src/main/java/core/game/node/entity/player/Player.java deleted file mode 100644 index a217e053c..000000000 --- a/Server/src/main/java/core/game/node/entity/player/Player.java +++ /dev/null @@ -1,1410 +0,0 @@ -package core.game.node.entity.player; - -import api.ContentAPI; -import core.game.component.Component; -import core.game.container.Container; -import core.game.container.impl.BankContainer; -import core.game.container.impl.EquipmentContainer; -import core.game.container.impl.InventoryListener; -import core.game.content.activity.pyramidplunder.PlunderObjectManager; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.quest.miniquest.barcrawl.BarcrawlManager; -import core.game.content.ttrail.TreasureTrailManager; -import core.game.interaction.Interaction; -import core.game.interaction.item.brawling_gloves.BrawlingGlovesManager; -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.ImpactHandler.HitsplatType; -import core.game.node.entity.combat.handlers.ChinchompaSwingHandler; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.info.PlayerDetails; -import core.game.node.entity.player.info.RenderInfo; -import core.game.node.entity.player.info.Rights; -import core.game.node.entity.player.info.UIDInfo; -import core.game.node.entity.player.info.login.LoginConfiguration; -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.grave.GraveManager; -import core.game.node.entity.player.link.music.MusicPlayer; -import core.game.node.entity.player.link.prayer.Prayer; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.entity.player.link.quest.QuestRepository; -import core.game.node.entity.player.link.request.RequestManager; -import core.game.node.entity.player.link.skillertasks.SkillerTasks; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.construction.HouseManager; -import core.game.node.entity.skill.hunter.HunterManager; -import core.game.node.entity.skill.slayer.SlayerManager; -import core.game.node.entity.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; -import core.game.system.monitor.PlayerMonitor; -import core.game.system.task.LogoutTask; -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.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.net.IoSession; -import core.net.packet.PacketRepository; -import core.net.packet.context.DynamicSceneContext; -import core.net.packet.context.SceneGraphContext; -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.plugin.Plugin; -import core.tools.RandomFunction; -import core.tools.StringUtils; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; -import org.rs09.consts.Items; -import rs09.GlobalStats; -import rs09.ServerConstants; -import rs09.game.VarpManager; -import rs09.game.content.ame.RandomEventManager; -import rs09.game.ge.PlayerGrandExchange; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.equipment.EquipmentDegrader; -import rs09.game.node.entity.skill.runecrafting.PouchManager; -import rs09.game.node.entity.state.newsys.State; -import rs09.game.node.entity.state.newsys.StateRepository; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import rs09.game.world.update.MapChunkRenderer; -import rs09.game.world.update.NPCRenderer; -import rs09.game.world.update.PlayerRenderer; -import rs09.game.world.update.UpdateSequence; -import rs09.tools.TickUtilsKt; - -import java.util.*; - -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_BASE; -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_DEATHS; -import static rs09.tools.stringtools.GlobalsKt.colorize; - -/** - * Represents a player entity. - * @author Emperor - * @author Vexia - */ -public class Player extends Entity { - - /** - * The details of the player. - */ - private PlayerDetails details; - - public boolean inWardrobe = false; - - public Location startLocation = null; - - private final Graphics wardrobe_hold_graphics = new Graphics(1182,0,0); - - public boolean newPlayer = getSkills().getTotalLevel() < 50; - - public BankContainer dropLog = new BankContainer(this); - - public EquipmentDegrader degrader = new EquipmentDegrader(); - - public PouchManager pouchManager = new PouchManager(this); - - public VarpManager varpManager = new VarpManager(this); - - public HashMap states = new HashMap<>(); - - public HashMap> logoutListeners = new HashMap<>(); - - /** - * The inventory. - */ - private final Container inventory = new Container(28).register(new InventoryListener(this)); - - /** - * The equipment. - */ - private final EquipmentContainer equipment = new EquipmentContainer(this); - - /** - * The bank container. - */ - private final BankContainer bank = new BankContainer(this); - - /** - * The packet dispatcher. - */ - private final PacketDispatch packetDispatch = new PacketDispatch(this); - - /** - * The spell book manager. - */ - private final SpellBookManager spellBookManager = new SpellBookManager(); - - /** - * The rendering info. - */ - private final RenderInfo renderInfo = new RenderInfo(this); - - /** - * The interface manager. - */ - private final InterfaceManager interfaceManager = new InterfaceManager(this); - - /** - * The emote manager. - */ - private final EmoteManager emoteManager = new EmoteManager(this); - - /** - * The player flags. - */ - private final PlayerFlags playerFlags = new PlayerFlags(); - - /** - * The appearance. - */ - private final Appearance appearance = new Appearance(this); - - /** - * The settings of the player. - */ - private final Settings settings = new Settings(this); - - /** - * The dialogue interpreter. - */ - private final DialogueInterpreter dialogueInterpreter = new DialogueInterpreter(this); - - /** - * The hint icon manager. - */ - private final HintIconManager hintIconManager = new HintIconManager(); - - /** - * The slayer manager. - */ - private final SlayerManager slayer = new SlayerManager(this); - - /** - * The quest repository. - */ - private final QuestRepository questRepository = new QuestRepository(this); - - /** - * The prayer manager. - */ - private final Prayer prayer = new Prayer(this); - - /** - * The skull manager. - */ - private final SkullManager skullManager = new SkullManager(this); - - /** - * The grave stone manager. - */ - private final GraveManager graveManager = new GraveManager(this); - - /** - * The new grand exchange interface manager. - */ - private final PlayerGrandExchange playerGrandExchange = new PlayerGrandExchange(this); - - /** - * The familiar manager. - */ - private final FamiliarManager familiarManager = new FamiliarManager(this); - - /** - * Pyramid Plunder Object Manager - */ - private final PlunderObjectManager plunderObjectManager = new PlunderObjectManager(); - - /** - * The config manager. - */ - private final ConfigurationManager configManager = new ConfigurationManager(this); - - /** - * The saved data. - */ - private final SavedData savedData = new SavedData(this); - - /** - * The request manager. - */ - private final RequestManager requestManager = new RequestManager(this); - - /** - * The farming manager. - */ - - /** - * The monitor which monitors player actions. - */ - private final PlayerMonitor monitor = new PlayerMonitor(); - - /** - * Represents the players warning messages. - */ - private final WarningMessages warningMessages = new WarningMessages(); - - /** - * The music player instance. - */ - private final MusicPlayer musicPlayer = new MusicPlayer(this); - - /** - * The house manager. - */ - private final HouseManager houseManager = new HouseManager(); - - /** - * The barcrawl miniquest manager. - */ - private final BarcrawlManager barcrawlManager = new BarcrawlManager(this); - - /** - * The anti macro handler. - */ - private final RandomEventManager antiMacroHandler = new RandomEventManager(this); - - /** - * The hunter manager. - */ - private final HunterManager hunterManager = new HunterManager(this); - - /** - * The treasure trail manager. - */ - private final TreasureTrailManager treasureTrailManager = new TreasureTrailManager(this); - - /** - * The audio manager. - */ - private final AudioManager audioManager = new AudioManager(this); - - /** - * The bank pin manager. - */ - private final BankPinManager bankPinManager = new BankPinManager(this); - - /** - * The achievement diary manager. - */ - private final AchievementDiaryManager achievementDiaryManager = new AchievementDiaryManager(this); - - /** - * The Ironman manager. - */ - private final IronmanManager ironmanManager = new IronmanManager(this); - - - /** - * Brawling Gloves manager - */ - private final BrawlingGlovesManager brawlingGlovesManager = new BrawlingGlovesManager(this); - /** - * The logout plugins. - */ - private List> logoutPlugins; - - /** - * The boolean for the player playing. - */ - private boolean playing; - - /** - * If the player is invisible. - */ - private boolean invisible; - - /** - * If the player is artificial. - */ - protected boolean artificial; - - /** - * The skiller tasks. - */ - protected SkillerTasks skillTasks = new SkillerTasks(); - - /** - * A custom state for bot debugging - */ - private String customState = ""; - - /** - * The amount of targets that the player can shoot left for the archery minigame. - */ - private int archeryTargets = 0; - - private int archeryTotal = 0; - - /** - * Constructs a new {@code Player} {@code Object}. - * @param details The player's details. - */ - public Player(PlayerDetails details) { - super(details.getUsername(), ServerConstants.START_LOCATION); - super.active = false; - super.interaction = new Interaction(this); - this.details = details; - this.direction = Direction.SOUTH; - } - - @Override - public void init() { - if (!artificial) { - getProperties().setSpawnLocation(ServerConstants.HOME_LOCATION); - getDetails().getSession().setObject(this); - getDetails().getSession().setLastPing(System.currentTimeMillis() + 10_000L); - antiMacroHandler.init(); - } - super.init(); - LoginConfiguration.configureLobby(this); - } - - @Override - public void clear() { - clear(false); - } - - /** - * 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. - */ - public void clear(boolean force) { - /*if (!force && allowRemoval()) { - Repository.getDisconnectionQueue().add(this, true); - return; - }*/ - if (force) { - Repository.getDisconnectionQueue().remove(getName()); - } - setPlaying(false); - getWalkingQueue().reset(); - LogoutTask task = getExtension(LogoutTask.class); - if (task != null) { - task.fire(this); - } - if (logoutPlugins != null && !logoutPlugins.isEmpty()) { - logoutPlugins.stream().filter(Objects::nonNull).forEach(plugin -> { - try { - plugin.newInstance(this); - } catch (Throwable throwable) { - throwable.printStackTrace(); - } - }); - } - if(!logoutListeners.isEmpty()){ - logoutListeners.forEach((key,method) -> method.invoke(this)); - } - if (familiarManager.hasFamiliar()) { - familiarManager.getFamiliar().clear(); - } - interfaceManager.close(); - interfaceManager.closeSingleTab(); - super.clear(); - getZoneMonitor().clear(); - CommunicationInfo.notifyPlayers(this, false, false); - hunterManager.logout(); - HouseManager.leave(this); - UpdateSequence.getRenderablePlayers().remove(this); - Repository.getDisconnectionQueue().add(this); - } - - public void toggleWardrobe(boolean intoWardrobe){ - class wardrobePulse extends Pulse{ - final Player player; - boolean first = true; - wardrobePulse(Player player){ - this.player = player; - } - @Override - public boolean pulse() { - if(first){ - player.visualize(new Animation(1241), new Graphics(1181,0,0)); - first = false; - return !player.inWardrobe; - } - if(player.inWardrobe) { - player.visualize(new Animation(1241),wardrobe_hold_graphics); - } else { - player.visualize(new Animation(1241), new Graphics(1183,0,0)); - player.getPulseManager().run(new Pulse(1){ - @Override - public boolean pulse() { - player.getAnimator().reset(); - player.packetDispatch.sendInterfaceConfig(548,69,false); - return true; - } - }); - } - return !player.inWardrobe; - } - } - if(intoWardrobe){ - packetDispatch.sendInterfaceConfig(548,69,true); - GameWorld.getPulser().submit(new wardrobePulse(this)); - inWardrobe = true; - } else { - inWardrobe = false; - } - } - - @Override - public void tick() { - super.tick(); - antiMacroHandler.tick(); - hunterManager.pulse(); - musicPlayer.tick(); - if(getAttribute("fire:immune",0) > 0){ - 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); - } - if(time == 0){ - sendMessage(colorize("%RYour antifire potion has expired.")); - removeAttribute("fire:immune"); - getAudioManager().send(2607); - } - } - if(getAttribute("poison:immunity",0) > 0){ - int time = getAttribute("poison:immunity",0) - GameWorld.getTicks(); - debug(time + ""); - if(time == TickUtilsKt.secondsToTicks(30)){ - sendMessage(colorize("%RYou have 30 seconds remaining on your antipoison potion.")); - getAudioManager().send(3120); - } - if(time == 0){ - sendMessage(colorize("%RYour antipoison potion has expired.")); - removeAttribute("poison:immunity"); - getAudioManager().send(2607); - } - } - if (!artificial && (System.currentTimeMillis() - getSession().getLastPing()) > 20_000L) { - details.getSession().disconnect(); - getSession().setLastPing(Long.MAX_VALUE); - } - if(getAttribute("infinite-special", false)) { - settings.setSpecialEnergy(100); - } - - //Decrements prayer points - getPrayer().tick(); - } - - @Override - public void update() { - super.update(); - if (playerFlags.isUpdateSceneGraph()) { - updateSceneGraph(false); - } - PlayerRenderer.render(this); - NPCRenderer.render(this); - MapChunkRenderer.render(this); - } - - @Override - public void reset() { - super.reset(); - playerFlags.setUpdateSceneGraph(false); - renderInfo.updateInformation(); - if (getSkills().isLifepointsUpdate()) { - 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; - } - 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)); - } - - @Override - public int getClientIndex() { - return this.getIndex() | 0x8000; - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - CombatStyle style = getProperties().getCombatPulse().getStyle(); - if (swing) { - int weaponId = equipment.getNew(3).getId(); - if (getProperties().getSpell() != null || getProperties().getAutocastSpell() != null) { - return CombatStyle.MAGIC.getSwingHandler(); - } - if (settings.isSpecialToggled()) { - CombatSwingHandler handler; - if ((handler = style.getSwingHandler().getSpecial(weaponId)) != null) { - return handler; - } - packetDispatch.sendMessage("Unhandled special attack for item " + weaponId + "!"); - } - } - if (style == CombatStyle.RANGE && equipment.getNew(3).getId() == 10033 || equipment.getNew(3).getId() == 10034) { - return ChinchompaSwingHandler.getInstance(); - } - return style.getSwingHandler(); - } - - @Override - public void commenceDeath(Entity killer) { - super.commenceDeath(killer); - if (prayer.get(PrayerType.RETRIBUTION)) { - prayer.startRetribution(killer); - } - } - - @Override - public void finalizeDeath(Entity killer) { - GlobalStats.incrementDeathCount(); - settings.setSpecialEnergy(100); - settings.updateRunEnergy(settings.getRunEnergy() - 100); - Player k = killer instanceof Player ? (Player) killer : this; - if (!k.isActive()) { - k = this; - } - if (this.isArtificial() && killer instanceof Player){ - setAttribute("dead", true); - } - if (this.isArtificial() && killer instanceof NPC) { - return; - } - 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 - String gender = this.isMale() ? "Man " : "Woman "; - 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); - final Container[] c = DeathTask.getContainers(this); - boolean gravestone = graveManager.generateable() && getIronmanManager().getMode() != IronmanMode.ULTIMATE; - int seconds = graveManager.getType().getDecay() * 60; - int ticks = (1000 * seconds) / 600; - List items = new ArrayList<>(20); - for (Item item : c[1].toArray()) { - if (item != null) { - GroundItem ground; - if (item.hasItemPlugin()) { - item = item.getPlugin().getDeathItem(item); - } - if (gravestone || !item.getDefinition().isTradeable()) { - ground = new GroundItem(item, getLocation(), gravestone ? ticks + 100 : 200, this); - } else { - ground = new GroundItem(item.getDropItem(), getLocation(), k); - } - items.add(ground); - ground.setDropper(this); //Checking for ironman mode in any circumstance for death items is inaccurate to how it works in both 2009scapes. - GroundItemManager.create(ground); - } - } - equipment.clear(); - inventory.clear(); - inventory.addAll(c[0]); - if (gravestone) { - graveManager.create(ticks, items); - sendMessages("Because of your current gravestone, you have "+graveManager.getType().getDecay()+" minutes to get your items and", "equipment back after dying in combat."); - } - familiarManager.dismiss(); - - } - skullManager.setSkulled(false); - removeAttribute("combat-time"); - getPrayer().reset(); - super.finalizeDeath(killer); - appearance.sync(); - if (killer instanceof Player && !GameWorld.isEconomyWorld() && getSkullManager().isWilderness() && killer.asPlayer().getSkullManager().isWilderness()) { - killer.asPlayer().getSavedData().getSpawnData().onDeath(killer.asPlayer(), this); - } - if (GameWorld.isEconomyWorld() && !getSavedData().getGlobalData().isDeathScreenDisabled()) { - getInterfaceManager().open(new Component(153)); - } - if (!getSavedData().getGlobalData().isDeathScreenDisabled()) { - getInterfaceManager().open(new Component(153)); - } - } - - @Override - public boolean hasProtectionPrayer(CombatStyle style) { - if (style == null) { - return false; - } - return prayer.get(style.getProtectionPrayer()); - } - - @Override - public int getDragonfireProtection(boolean fire) { - int value = 0; - if (fire) { - if (hasFireResistance()) { - value |= 0x2; - } - } - Item item = equipment.get(EquipmentContainer.SLOT_SHIELD); - if (item != null && (fire && (item.getId() == 11283 || item.getId() == 11284 || item.getId() == 1540) || (!fire && (item.getId() == 2890 || item.getId() == 9731)))) { - value |= 0x4; - } - if (prayer.get(PrayerType.PROTECT_FROM_MAGIC)) { - value |= 0x8; - } - setAttribute("fire_resistance", value); - return value; - } - - @Deprecated - @Override - public void setLocation(Location location) { - super.setLocation(location); - } - - @Override - public void fullRestore() { - prayer.reset(); - settings.setSpecialEnergy(100); - settings.updateRunEnergy(-100); - super.fullRestore(); - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if (entity instanceof NPC && !((NPC) entity).getDefinition().hasAction("attack") && !((NPC) entity).isIgnoreAttackRestrictions(this)) { - return false; - } - return super.isAttackable(entity, style); - } - - @Override - public boolean isPoisonImmune() { - return getAttribute("poison:immunity", -1) > GameWorld.getTicks(); - } - - @Override - public void onImpact(final Entity entity, BattleState state) { - super.onImpact(entity, state); - boolean recoil = getEquipment().getNew(EquipmentContainer.SLOT_RING).getId() == 2550; - if (state.getEstimatedHit() > 0) { - if (getAttribute("vengeance", false)) { - removeAttribute("vengeance"); - final int hit = (int) (state.getEstimatedHit() * 0.75); - sendChat("Taste vengeance!"); - if (hit > -1) { - entity.getImpactHandler().manualHit(Player.this, hit, HitsplatType.NORMAL); - } - } - if (recoil) { - getImpactHandler().handleRecoilEffect(entity, state.getEstimatedHit()); - } - } - if (state.getSecondaryHit() > 0) { - if (recoil) { - getImpactHandler().handleRecoilEffect(entity, state.getSecondaryHit()); - } - } - degrader.checkArmourDegrades(this); - } - - public void randomWalk(int radiusX, int radiusY) { - Pathfinder.find(this, this.getLocation().transform(RandomFunction.random(radiusX, (radiusX * -1)), RandomFunction.random(radiusY, (radiusY * -1)), 0), false, Pathfinder.SMART).walk(this); - } - - /** - * Initializes the player for reconnection. - */ - public void initReconnect() { - getInterfaceManager().setChatbox(null); - 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]); - renderInfo.getLocalNpcs().clear(); - renderInfo.getLocalPlayers().clear(); - renderInfo.setLastLocation(null); - renderInfo.setOnFirstCycle(true); - Arrays.fill(renderInfo.getAppearanceStamps(),0); - } - - /** - * Checks if the player is wearing void. - */ - public boolean isWearingVoid(CombatStyle style) { - int helm; - if(style == CombatStyle.MELEE) { - helm = Items.VOID_MELEE_HELM_11665; - } else if(style == CombatStyle.RANGE) { - helm = Items.VOID_RANGER_HELM_11664; - } else if(style == CombatStyle.MAGIC) { - helm = Items.VOID_MAGE_HELM_11663; - } else { - return false; - } - boolean legs = ContentAPI.inEquipment(this, Items.VOID_KNIGHT_ROBE_8840, 1); - boolean top = ContentAPI.inEquipment(this, Items.VOID_KNIGHT_TOP_8839, 1) - || ContentAPI.inEquipment(this, Items.VOID_KNIGHT_TOP_10611, 1); - boolean gloves = ContentAPI.inEquipment(this, Items.VOID_KNIGHT_GLOVES_8842, 1); - return ContentAPI.inEquipment(this, helm, 1) && legs && top && gloves; - } - - /** - * Updates the player's scene graph. - * @param login If the player is logging in. - */ - public void updateSceneGraph(boolean login) { - Region region = getViewport().getRegion(); - if (region instanceof DynamicRegion || region == null && (region = RegionManager.forId(location.getRegionId())) instanceof DynamicRegion) { - PacketRepository.send(BuildDynamicScene.class, new DynamicSceneContext(this, login)); - } else { - PacketRepository.send(UpdateSceneGraph.class, new SceneGraphContext(this, login)); - } - } - - /** - * Toggles the debug mode. - */ - public void toggleDebug() { - boolean debug = getAttribute("debug", false); - setAttribute("debug", !debug); - getPacketDispatch().sendMessage("Your debug mode is toggled to " + !debug + "."); - } - - /** - * Wrapper method for sending a message. - * @param messages the messages. - */ - public void sendMessages(String... messages) { - packetDispatch.sendMessages(messages); - } - - /** - * Wrapper method for sending a message. - * @param message the message. - */ - public void sendMessage(String message) { - sendMessages(message); - } - - /** - * Sends a notification message. - * @param message The message. - */ - public void sendNotificationMessage(String message) { - sendMessages("" + message + ""); - } - - /** - * Checks if the player can spawn. & Location - * @return {@code True} if so. - */ - public boolean spawnZone() { - return (getLocation().getX() > 3090 && getLocation().getY() < 3500 - && getLocation().getX() < 3099 && getLocation().getY() > 3487); - } - - public boolean canSpawn() { - if (!spawnZone()) { - sendMessage("You can only spawn items inside the edgeville bank."); - return true; - } - if (inCombat() || getLocks().isInteractionLocked() || getSkullManager().isWilderness() || getAttribute("activity", null) != null) { - sendMessage("You can't spawn items at the moment."); - return true; - } - return false; - } - - /** - * Sends a message on a tick. - * @param message the message. - * @param ticks the ticks. - */ - public void sendMessage(String message, int ticks) { - packetDispatch.sendMessage(message, ticks); - } - - /** - * Sends a message to the player if it's an administrator. - * @param string The message. - */ - public void debug(String string) { - if (getAttribute("debug",false)) { - packetDispatch.sendMessage(string); - } - } - - /** - * Grabs a players gender, using shorter amount of code - */ - - public boolean isMale() { - return this.getAppearance().getGender().ordinal() == 0; - } - - /** - * Sets the player details. - * @param details The player details. - */ - @SuppressWarnings("deprecation") - public void updateDetails(PlayerDetails details) { - if (this.details != null) { - details.setBanTime(this.details.getBanTime()); - details.setMuteTime(this.details.getMuteTime()); - details.setIcon(this.details.getIcon()); - } - details.getSession().setObject(this); - this.details = details; - } - - /** - * Checks if the player is allowed to be removed from the game. - * @return {@code True} if so. - */ - public boolean allowRemoval() { - return inCombat() || getSkills().getLifepoints() < 1 || DeathTask.isDead(this); - } - - /** - * Checks if the containers have this item. - * @param item the item. - * @return {@code True} if so. - */ - public boolean hasItem(Item item) { - return getInventory().containsItem(item) || getBank().containsItem(item) || getEquipment().containsItem(item); - } - - /** - * Gets the player extra experience mod. - * @return the mod. - */ - public double getExperienceMod() { - return getSavedData().getGlobalData().hasDoubleExp() ? 2 : 1; - } - - /** - * Checks if the player is a staff member. - * @return {@code True} if so. - */ - public boolean isStaff() { - return getDetails().getRights() != Rights.REGULAR_PLAYER; - } - - /** - * Checks if the player is an admin. - * @return true if so. - */ - public boolean isAdmin() { - return getDetails().getRights() == Rights.ADMINISTRATOR; - } - - /** - * Checks if the player is debugging. - * @return {@code True} if so. - */ - public boolean isDebug() { - return details.getRights() == Rights.ADMINISTRATOR && getAttribute("debug", false); - } - - /** - * Gets the uid info. - * @return the info. - */ - public UIDInfo getUidInfo() { - return details.getInfo(); - } - - /** - * Gets the {@code PlayerDetails}. - * @return the details. - */ - public PlayerDetails getDetails() { - return details; - } - - /** - * Gets the name. - * @return the name. - */ - public String getName() { -// return display ? details.getDisplayName() : super.getName(); - return super.getName(); - } - - /** - * Gets the players {@code Session}. - * @return the {@code PlayerDetails} {@code Object} session. - */ - public IoSession getSession() { - return details.getSession(); - } - - /** - * Gets the {@code Equipment} - * @return {@code Equipment}. - */ - public EquipmentContainer getEquipment() { - return equipment; - } - - /** - * Gets the bank. - * @return The bank. - */ - public BankContainer getBank() { - return bank; - } - - public BankContainer getDropLog() {return dropLog;} - - /** - * @return the inventory - */ - public Container getInventory() { - return inventory; - } - - /** - * Sets the playing flag. - * @param playing the flag to set. - */ - public void setPlaying(boolean playing) { - this.playing = playing; - } - - /** - * Checks if the player is playing. - * @return {@code True} if so. - */ - public boolean isPlaying() { - return playing; - } - - /** - * Gets the rights of the player. - * @return the rights. - */ - public Rights getRights() { - return details.getRights(); - } - - /** - * Gets the renderInfo. - * @return The renderInfo. - */ - public RenderInfo getRenderInfo() { - return renderInfo; - } - - /** - * Gets the appearance. - * @return The appearance. - */ - public Appearance getAppearance() { - return appearance; - } - - /** - * Gets the playerFlags. - * @return The playerFlags. - */ - public PlayerFlags getPlayerFlags() { - return playerFlags; - } - - /** - * Gets the {@code PacketDispatch}. - * @return the packet dispatch. - */ - public PacketDispatch getPacketDispatch() { - return packetDispatch; - } - - - /** - * @return the spellBookManager - */ - public SpellBookManager getSpellBookManager() { - return spellBookManager; - } - - /** - * Gets the settings. - * @return The settings. - */ - public Settings getSettings() { - return settings; - } - - /** - * @return the interface manager. - */ - public InterfaceManager getInterfaceManager() { - return interfaceManager; - } - - /** - * Gets the dialogue interpreter. - * @return The dialogue interpreter. - */ - public DialogueInterpreter getDialogueInterpreter() { - return dialogueInterpreter; - } - - /** - * @return the hintIconManager - */ - public HintIconManager getHintIconManager() { - return hintIconManager; - } - - /** - * Gets the slayer. - * @return The slayer. - */ - public SlayerManager getSlayer() { - return slayer; - } - - /** - * Checks if the player is artifical (AIPlayer). - * @return {@code True} if so. - */ - public boolean isArtificial() { - return artificial; - } - - /** - * @return the questRepository. - */ - public QuestRepository getQuestRepository() { - return questRepository; - } - - /** - * @return the prayer. - */ - public Prayer getPrayer() { - return prayer; - } - - /** - * @return the skullManager. - */ - public SkullManager getSkullManager() { - return skullManager; - } - - /** - * @return the familiarManager. - */ - public FamiliarManager getFamiliarManager() { - return familiarManager; - } - - /** - * Gets the configManager. - * @return The configManager. - */ - public ConfigurationManager getConfigManager() { - return configManager; - } - - /** - * Gets the communication. - * @return The communication. - */ - public CommunicationInfo getCommunication() { - return details.getCommunication(); - } - - /** - * Gets the requestManager. - * @return The requestManager. - */ - public RequestManager getRequestManager() { - return requestManager; - } - - /** - * Gets the savedData. - * @return The savedData. - */ - public SavedData getSavedData() { - return savedData; - } - - /** - * Gets the global data. - * @return the global data. - */ - public GlobalData getGlobalData() { - return savedData.getGlobalData(); - } - - /** - * Gets the farmingManager. - * @return The farmingManager. - */ - - /** - * Gets the monitor. - * @return The monitor. - */ - public PlayerMonitor getMonitor() { - return monitor; - } - - /** - * Gets the warningMessages. - * @return The warningMessages. - */ - public WarningMessages getWarningMessages() { - return warningMessages; - } - - /** - * Gets the musicPlayer. - * @return The musicPlayer. - */ - public MusicPlayer getMusicPlayer() { - return musicPlayer; - } - - /** - * Gets the houseManager. - * @return The houseManager. - */ - public HouseManager getHouseManager() { - return houseManager; - } - - /** - * Gets the barcrawlManager. - * @return The barcrawlManager. - */ - public BarcrawlManager getBarcrawlManager() { - return barcrawlManager; - } - - /** - * Gets the antiMacroHandler. - * @return The antiMacroHandler. - */ - public RandomEventManager getAntiMacroHandler() { - return antiMacroHandler; - } - - /** - * Gets the hunterManager. - * @return The hunterManager. - */ - public HunterManager getHunterManager() { - return hunterManager; - } - - /** - * Gets the btreasureTrailManager. - * @return the treasureTrailManager - */ - public TreasureTrailManager getTreasureTrailManager() { - return treasureTrailManager; - } - - /** - * Gets the graveManager. - * @return the graveManager - */ - public GraveManager getGraveManager() { - return graveManager; - } - - /** - * Gets the audioManager. - * @return the audioManager - */ - public AudioManager getAudioManager() { - return audioManager; - } - - /** - * Gets the logoutPlugins. - * @return the logoutPlugins - */ - public List> getLogoutPlugins() { - if (logoutPlugins == null) { - logoutPlugins = new ArrayList<>(20); - } - return logoutPlugins; - } - - /** - * Sets the balogoutPlugins. - * @param logoutPlugins the logoutPlugins to set. - */ - public void setLogoutPlugins(List> logoutPlugins) { - this.logoutPlugins = logoutPlugins; - } - - /** - * Gets the bankPinManager. - * @return the bankPinManager - */ - public BankPinManager getBankPinManager() { - return bankPinManager; - } - - /** - * Gets the achievementDiaryManager. - * @return the achievementDiaryManager - */ - public AchievementDiaryManager getAchievementDiaryManager() { - return achievementDiaryManager; - } - - /** - * Gets the ironmanManager. - * @return the ironmanManager - */ - public IronmanManager getIronmanManager() { - return ironmanManager; - } - - public PlunderObjectManager getPlunderObjectManager() {return plunderObjectManager;} - - /** - * Gets the emoteManager. - * @return the emoteManager. - */ - public EmoteManager getEmoteManager() { - return emoteManager; - } - - /** - * Gets the invisible. - * @return the invisible - */ - public boolean isInvisible() { - return invisible; - } - - /** - * Sets the invisible. - * @param invisible the invisible to set. - */ - public void setInvisible(boolean invisible) { - this.invisible = invisible; - } - - @Override - public String getUsername() { - return StringUtils.formatDisplayName(getName()); - } - - public SkillerTasks getSkillTasks() { - return skillTasks; - } - - public void setSkillTasks(SkillerTasks skillTasks) { - this.skillTasks = skillTasks; - } - - @Override - public String toString() { - return "Player [name=" + name + ", getRights()=" + getRights() + "]"; - } - - - public String getCustomState() { - return customState; - } - - public void setCustomState(String state) - { - this.customState = state; - } - - public int getArcheryTargets() { - return archeryTargets; - } - - public void setArcheryTargets(int archeryTargets) { - this.archeryTargets = archeryTargets; - } - - public int getArcheryTotal() { - return archeryTotal; - } - - public void setArcheryTotal(int archeryTotal) { - this.archeryTotal = archeryTotal; - } - - public BrawlingGlovesManager getBrawlingGlovesManager() { return brawlingGlovesManager;} - - public PlayerGrandExchange getPlayerGrandExchange() { return playerGrandExchange; } - - public boolean hasActiveState(String key){ - State state = states.get(key); - if(state != null && state.getPulse() != null){ - return true; - } - return false; - } - - public State registerState(String key){ - return StateRepository.forKey(key, this); - } - - public void clearState(String key){ - State state = states.get(key); - if(state == null) return; - Pulse pulse = state.getPulse(); - if(pulse != null) { - pulse.stop(); - } - states.remove(key); - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/info/PlayerDetails.java b/Server/src/main/java/core/game/node/entity/player/info/PlayerDetails.java deleted file mode 100644 index 2ec604452..000000000 --- a/Server/src/main/java/core/game/node/entity/player/info/PlayerDetails.java +++ /dev/null @@ -1,399 +0,0 @@ -package core.game.node.entity.player.info; - -import core.game.node.entity.player.info.portal.Icon; -import core.game.node.entity.player.info.portal.PlayerSQLManager; -import core.game.system.communication.CommunicationInfo; -import core.net.IoSession; - -import java.util.concurrent.TimeUnit; - -/** - * Stores the details of a player's account. - * @author Vexia - * - */ -public class PlayerDetails { - - /** - * The sql manager for this account. - */ - private final PlayerSQLManager sqlManager = new PlayerSQLManager(this); - - /** - * The communication info. - */ - private final CommunicationInfo communicationInfo = new CommunicationInfo(); - - - /** - * The unique id info. - */ - private final UIDInfo info = new UIDInfo(); - - /** - * The username of the account. - */ - private final String username; - - /** - * The password of the account. - */ - private String password; - - /** - * The unique id of the account. - */ - private int uid; - - /** - * The account's last game login. - */ - private long lastLogin = -1; - - /** - * The account's last game login. - */ - private long timePlayed = 0; - - /** - * The time the player is muted for. - */ - private long muteTime; - - /** - * The time the player is banned for. - */ - private long banTime; - - /** - * If the sql data has been parsed. - */ - private boolean parsed; - - /** - * The rights of the player. - */ - private Rights rights = Rights.REGULAR_PLAYER; - - /** - * The chat icon value. - */ - private Icon icon; - - public int credits; - - /** - * Represents the session. - */ - private IoSession session; - - /** - * Constructs a new {@code PlayerDetails}. - * @param username the username to set. - * @param password the password to set. - */ - public PlayerDetails(String username, String password) { - this.username = username; - this.password = password; - this.uid = username.hashCode(); - } - - /** - * Constructs a new {@Code PlayerDetails} {@Code Object} - * @param username the username to set. - */ - public PlayerDetails(String username) { - this(username, null); - } - - /** - * Parses the details from the database for this object. - * @return {@code True} if parsed. - */ - public boolean parse() { - if (sqlManager.parse()) { - return parsed = true; - } - return parsed = false; - } - - /** - * Saves the player's details. - * @return {@code True} if saved. - */ - public boolean save() { - sqlManager.save(); - return true; - } - - /** - * Gets the player details for an account name. - * @param username the username. - * @return the player details. - */ - public static PlayerDetails getDetails(String username) { - PlayerDetails details = new PlayerDetails(username); - if (!details.parse()) { - return details = null; - } - return details; - } - - /** - * Checks if the ban is permanent. - * @return {@code True} if so. - */ - public boolean isPermBan() { - return TimeUnit.MILLISECONDS.toDays(banTime - System.currentTimeMillis()) > 1000; - } - - /** - * Checks if the player is muted. - * @return {@code True} if so. - */ - public boolean isBanned() { - return banTime > System.currentTimeMillis(); - } - - /** - * Checks if the mute is permanent. - * @return {@code True} if so. - */ - public boolean isPermMute() { - return TimeUnit.MILLISECONDS.toDays(muteTime - System.currentTimeMillis()) > 1000; - } - - /** - * Checks if the player is muted. - * @return {@code True} if so. - */ - public boolean isMuted() { - return muteTime > System.currentTimeMillis(); - } - - /** - * Gets the sql manager. - * @return the sql manager. - */ - public PlayerSQLManager getSqlManager() { - return sqlManager; - } - - /** - * Gets the rights. - * @return The rights. - */ - public Rights getRights() { - return rights; - } - - /** - * Sets the credentials. - * @param rights The credentials to set. - */ - public void setRights(Rights rights) { - this.rights = rights; - } - - /** - * Gets the session. - * @return The session. - */ - public IoSession getSession() { - return session; - } - - /** - * Sets the session. - * @param session The session to set. - */ - public void setSession(IoSession session) { - this.session = session; - } - - /** - * Sets the password. - * @param password the password. - */ - public void setPassword(final String password) { - this.password = password; - } - - /** - * Gets the username. - * @return The username. - */ - - public String getUsername() { - return username; - } - - /** - * Gets the uid. - * @return the uid. - */ - public int getUid() { - return uid; - } - - /** - * Sets the uid. - * @param uid the uid. - */ - public void setUid(int uid) { - this.uid = uid; - } - - /** - * Gets the password. - * @return The password. - */ - public String getPassword() { - return password; - } - - /** - * Checks if the details have been parsed. - * @return {@code True} if parsed. - */ - public boolean isParsed() { - return parsed; - } - - /** - * Gets the mac address. - * @return the address. - */ - public String getMacAddress() { - return info.getMac(); - } - - /** - * Gets the computer name. - * @return the name. - */ - public String getCompName() { - return info.getCompName(); - } - - /** - * Gets the ip address. - * @return the ip. - */ - public String getIpAddress() { - if (session == null) { - return info.getIp(); - } - return session.getAddress(); - } - - /** - * Gets the serial. - * @return the serial. - */ - public String getSerial() { - return info.getSerial(); - } - - /** - * Gets the info. - * @return the info - */ - public UIDInfo getInfo() { - return info; - } - - /** - * Gets the communicationInfo. - * @return the communicationInfo - */ - public CommunicationInfo getCommunication() { - return communicationInfo; - } - - /** - * Gets the icon. - * @return the icon. - */ - public Icon getIcon() { - return icon; - } - - /** - * Sets the icon. - * @param icon the icon to set - */ - public void setIcon(Icon icon) { - this.icon = icon; - } - - /** - * Gets the lastLogin. - * @return the lastLogin. - */ - public long getLastLogin() { - return lastLogin; - } - - /** - * Sets the lastLogin. - * @param lastLogin the lastLogin to set - */ - public void setLastLogin(long lastLogin) { - this.lastLogin = lastLogin; - } - - /** - * Gets the timePlayed. - * @return the timePlayed. - */ - public long getTimePlayed() { - return timePlayed; - } - - /** - * Sets the timePlayed. - * @param timePlayed the timePlayed to set - */ - public void setTimePlayed(long timePlayed) { - this.timePlayed = timePlayed; - } - - /** - * Sets the mute time. - * @param muteTime the mute time. - */ - public void setMuteTime(long muteTime) { - this.muteTime = muteTime; - } - - /** - * Gets the mute time. - * @return The mute time. - */ - public long getMuteTime() { - return muteTime; - } - - /** - * Gets the banTime. - * @return the banTime. - */ - public long getBanTime() { - return banTime; - } - - /** - * Sets the banTime. - * @param banTime the banTime to set - */ - public void setBanTime(long banTime) { - this.banTime = banTime; - } - - @Override - public String toString() { - return "PlayerDetails [sqlManager=" + sqlManager + ", communicationInfo=" + communicationInfo + ", info=" + info + ", username=" + username + ", password=" + password + ", uid=" + uid + ", lastLogin=" + lastLogin + ", muteTime=" + muteTime + ", parsed=" + parsed + ", rights=" + rights + ", icon=" + icon + ", session=" + session + "]"; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java b/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java deleted file mode 100644 index ee0dfa9e2..000000000 --- a/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java +++ /dev/null @@ -1,295 +0,0 @@ -package core.game.node.entity.player.info.login; - -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.emote.Emotes; -import core.game.world.map.RegionManager; -import core.game.world.update.flag.player.AppearanceFlag; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; -import core.net.packet.PacketRepository; -import core.net.packet.context.InterfaceContext; -import core.net.packet.out.Interface; -import core.plugin.Plugin; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import rs09.game.world.update.UpdateSequence; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Random; -import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; - - -/** - * Sends the login configuration packets. - * - * @author Emperor - */ -public final class LoginConfiguration { - - /** - * The login plugins. - */ - private static final List> LOGIN_PLUGINS = new ArrayList<>(20); - - /** - * The lobby pane component. - */ - private static final Component LOBBY_PANE = new Component(549); - - /** - * 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} - */ - public LoginConfiguration() { - /* - * empty. - */ - } - - /** - * Configures the lobby login. - * - * @param player The player. - */ - public static void configureLobby(Player player) { - player.updateSceneGraph(true); - if (!player.isArtificial() && TutorialSession.getExtension(player).getStage() >= TutorialSession.MAX_STAGE && player.getAttribute("login_type", LoginType.NORMAL_LOGIN) != LoginType.RECONNECT_TYPE) { - sendLobbyScreen(player); - } else { - configureGameWorld(player); - } - } - - /** - * Sends the lobby interface-related packets. - * - * @param player The player. - */ - public static void sendLobbyScreen(Player player) { - messModel = autoSelect(); - for(Player p : Repository.getLobbyPlayers()){ - if(p.getName().equals(player.getName())){ - p.clear(); - Repository.getLobbyPlayers().remove(p); - break; - } - } - 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().credits + "", 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); - 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" - } - - /** - * Configures the game world. - * - * @param player The player. - */ - public static void configureGameWorld(final Player player) { - player.getConfigManager().reset(); - sendGameConfiguration(player); - Repository.getLobbyPlayers().remove(player); - Repository.getPlayerNames().putIfAbsent(player.getName().toLowerCase(),player); - player.setPlaying(true); - UpdateSequence.getRenderablePlayers().add(player); - RegionManager.move(player); - player.getMusicPlayer().init(); - player.getUpdateMasks().register(new AppearanceFlag(player)); - 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(); - /*if (GameWorld.getSettings().isPvp()) { - player.getPacketDispatch().sendString("", 226, 1); - }*/ - TutorialSession.extend(player); - TutorialSession.getExtension(player).init(); - /*if (TutorialSession.getExtension(player).getStage() != 73) { - TutorialStage.load(player, TutorialSession.getExtension(player).getStage(), true); - }*/ - } - - /** - * Sends the game configuration packets. - * - * @param player The player to send to. - */ - public static void sendGameConfiguration(final Player player) { - player.getInterfaceManager().openWindowsPane(new Component(player.getInterfaceManager().isResizable() ? 746 : 548)); - player.getInterfaceManager().openChatbox(137); - player.getInterfaceManager().openDefaultTabs(); - welcome(player); - config(player); - for (Plugin plugin : LOGIN_PLUGINS) { - try { - plugin.newInstance(player); - } catch (Throwable e) { - e.printStackTrace(); - } - } - player.getCommunication().sync(player); - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendInfoUpdate(player); - } - } - - /** - * Method used to welcome the player. - * - * @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"); - 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); - } - - /** - * Method used to configure all possible settings for the player. - * - * @param player the player. - */ - public static final void config(final Player player) { - player.getInventory().refresh(); - player.getEquipment().refresh(); - player.getSkills().refresh(); - player.getSkills().configure(); - player.getSettings().update(); - player.getInteraction().setDefault(); - player.getPacketDispatch().sendRunEnergy(); - player.getFamiliarManager().login(); - player.getInterfaceManager().openDefaultTabs(); - player.getPlayerGrandExchange().init(); - player.getPacketDispatch().sendString("Friends List - World " + GameWorld.getSettings().getWorldId(), 550, 3); - player.getConfigManager().init(); - player.getAntiMacroHandler().init(); - player.getQuestRepository().syncronizeTab(player); - player.getGraveManager().update(); - player.getInterfaceManager().close(); - player.getEmoteManager().refresh(); - player.getInterfaceManager().openInfoBars(); - } - - /** - * Gets the message child for the inter id. - * @notice GameSettings.kt contains the list of what these are - * @param interfaceId The interface id. - * @return The child id. - */ - public static int getMessageChild(int interfaceId) { - if (interfaceId == 622) { - 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) { - return 3; - } else if (interfaceId == 715) { - return 2; - } else if (interfaceId == 679) { - return 1; - } - return 0; - } - - /** - * Sets a random interface id for the "message of the week" models - */ - private final static int autoSelect() { - boolean contains = IntStream.of(MESSAGE_MODEL).anyMatch(x -> x == GameWorld.getSettings().getMessage_model()); - return contains ? GameWorld.getSettings().getMessage_model():MESSAGE_MODEL[new Random().nextInt(MESSAGE_MODEL.length)]; - } - - /** - * Gets the last login string for the lobby. - * - * @param player the player. - * @return the last login. - */ - public static String getLastLogin(Player player) { - String lastIp = (String) player.getDetails().getSqlManager().getTable().getColumn("lastGameIp").getValue(); - if (lastIp == null || lastIp == "") { - lastIp = player.getDetails().getIpAddress(); - } - String string = "You last logged in @timeAgo from: " + lastIp; - long time = player.getDetails().getLastLogin(); - Date lastLogin = new Date(time); - Date current = new Date(); - long diff = current.getTime() - lastLogin.getTime(); - int days = (int) TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS); - if (days < 1) { - string = string.replace("@timeAgo", "earlier today"); - } else if (days == 1) { - string = string.replace("@timeAgo", "yesterday"); - } else { - string = string.replace("@timeAgo", days + " days ago"); - } - return string; - } - - - /** - * Gets the loginPlugins. - * - * @return The loginPlugins. - */ - public static List> getLoginPlugins() { - return LOGIN_PLUGINS; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/info/login/PlayerParser.java b/Server/src/main/java/core/game/node/entity/player/info/login/PlayerParser.java deleted file mode 100644 index e8c0dad3d..000000000 --- a/Server/src/main/java/core/game/node/entity/player/info/login/PlayerParser.java +++ /dev/null @@ -1,75 +0,0 @@ -package core.game.node.entity.player.info.login; - -import core.game.node.entity.player.Player; -import rs09.ServerConstants; -import rs09.game.node.entity.player.info.login.PlayerSaveParser; -import rs09.game.node.entity.player.info.login.PlayerSaver; -import rs09.game.system.SystemLogger; - -import java.io.*; - -/** - * Class used to abstract the process of loading a player save. - * @author Ceikry - */ -public final class PlayerParser { - /** - * Parses or creates the player's save file depending on whether or not it exists. - * @param player The player. - */ - public static boolean parse(Player player) { - File JSON = new File(ServerConstants.PLAYER_SAVE_PATH + player.getName() + ".json"); - - try { - if (JSON.exists()) { //parse the new JSON type. - new PlayerSaveParser(player).parse(); - } else { //Create new save - if(!(new File(ServerConstants.PLAYER_SAVE_PATH + "template/template.json")).exists()){ - return true; - } - makeFromTemplate(player); - new PlayerSaveParser(player).parse(); - } - return true; - } catch (Exception e){ - e.printStackTrace(); - return false; - } - } - /** - * Saves the player's details to the character file at data/players/player_name.json - * @param player The player. - */ - public static void save(Player player) { - new PlayerSaver(player).save(); - } - - /** - * Copies the template at data/players/template/template.json to data/players/player_name.json - * @param player the player to copy the template for. - */ - public static void makeFromTemplate(Player player){ - InputStream is = null; - OutputStream os = null; - try { - is = new FileInputStream(ServerConstants.PLAYER_SAVE_PATH + "template/template.json"); - os = new FileOutputStream(ServerConstants.PLAYER_SAVE_PATH + player.getName() + ".json"); - byte[] buffer = new byte[1024]; - int length; - while ((length = is.read(buffer)) > 0) { - os.write(buffer, 0, length); - } - } catch (Exception e){ e.printStackTrace(); - } finally { - try { - assert is != null; - is.close(); - assert os != null; - os.close(); - } catch (Exception f){ - SystemLogger.logWarn("Unable to close file copiers in PlayerParser.java line 216."); - f.printStackTrace(); - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/info/portal/PlayerSQLManager.java b/Server/src/main/java/core/game/node/entity/player/info/portal/PlayerSQLManager.java deleted file mode 100644 index 6e7256331..000000000 --- a/Server/src/main/java/core/game/node/entity/player/info/portal/PlayerSQLManager.java +++ /dev/null @@ -1,235 +0,0 @@ -package core.game.node.entity.player.info.portal; - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.PlayerDetails; -import core.game.node.entity.player.info.Rights; -import core.game.node.entity.player.info.login.Response; -import core.game.system.SystemManager; -import core.game.system.mysql.SQLColumn; -import core.game.system.mysql.SQLEntryHandler; -import core.game.system.mysql.SQLManager; -import core.game.system.mysql.SQLTable; -import core.game.system.mysql.impl.PlayerSQLHandler; -import core.net.amsc.WorldCommunicator; - -import java.math.BigInteger; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * Manages the SQL aspect for an account. - * @author Vexia - * - */ -public final class PlayerSQLManager { - - /** - * The representation of an account sql table. - */ - private final SQLTable table = new SQLTable( - new SQLColumn("UID", Integer.class), - new SQLColumn("rights", Integer.class), - new SQLColumn("donatorType", Integer.class), - new SQLColumn("credits", Integer.class), - new SQLColumn("icon", Integer.class), - new SQLColumn("perks", String.class), - new SQLColumn("ip", String.class, false), - new SQLColumn("mac", String.class, false), - new SQLColumn("serial", String.class, false), - new SQLColumn("computerName", String.class, false), - new SQLColumn("netWorth", BigInteger.class, false), - new SQLColumn("ironManMode", String.class, false), - new SQLColumn("bank", String.class, false), - new SQLColumn("inventory", String.class, false), - new SQLColumn("equipment", String.class, false), - new SQLColumn("ge", String.class, false), - new SQLColumn("muteTime", Long.class), - new SQLColumn("banTime", Long.class), - new SQLColumn("contacts", String.class), - new SQLColumn("blocked", String.class), - new SQLColumn("clanName", String.class), - new SQLColumn("currentClan", String.class), - new SQLColumn("clanReqs", String.class), - new SQLColumn("timePlayed", Long.class), - new SQLColumn("lastLogin", Long.class), - new SQLColumn("online", Integer.class), - new SQLColumn("lastGameIp", String.class)); - - /** - * The details the SQL manager is managing. - */ - private final PlayerDetails details; - - /** - * Constructs a new {@Code PlayerSQLManager} {@Code Object} - * @param details the details. - */ - public PlayerSQLManager(PlayerDetails details) { - this.details = details; - } - - /** - * Parses the SQL table. - * @return {@code True} if parsed. - */ - @SuppressWarnings("deprecation") - public boolean parse() { - if(!SQLEntryHandler.read(new PlayerSQLHandler(details))) { - return false; - } - details.getCommunication().parse(table); - details.credits = (int) table.getColumn("credits").getValue(); - details.setBanTime((long) table.getColumn("banTime").getValue()); - details.setMuteTime((long) table.getColumn("muteTime").getValue()); - details.setIcon(Icon.forId((int) table.getColumn("icon").getValue())); - details.setRights(Rights.forId((int) table.getColumn("rights").getValue())); - details.setLastLogin(System.currentTimeMillis()); - details.setTimePlayed((long) table.getColumn("timePlayed").getValue()); - return true; - } - - /** - * Saves the changed SQL columns to the database. - */ - public void save() { - SQLEntryHandler.write(new PlayerSQLHandler(details)); - } - - /** - * Updates column values with the player instance. - * @param player The player instance. - */ - public void update(Player player) { - if (!WorldCommunicator.isEnabled()) { - details.getCommunication().save(table); - } - table.getColumn("credits").updateValue(player.getDetails().credits); - table.getColumn("bank").updateValue(player.getBank().format()); - table.getColumn("lastLogin").updateValue(player.getDetails().getLastLogin()); - table.getColumn("ge").updateValue(player.getPlayerGrandExchange().format()); - table.getColumn("inventory").updateValue(player.getInventory().format()); - table.getColumn("equipment").updateValue(player.getEquipment().format()); - table.getColumn("netWorth").updateValue(player.getMonitor().getNetworth()); - table.getColumn("lastGameIp").updateValue(player.getDetails().getIpAddress()); - table.getColumn("ironManMode").updateValue(player.getIronmanManager().getMode().name()); - table.getColumn("timePlayed").updateValue(player.getDetails().getTimePlayed() + (System.currentTimeMillis() - player.getDetails().getLastLogin())); - table.getColumn("ip").updateValue(getAddressLog((String) table.getColumn("ip").getValue(), details.getInfo().getIp())); - table.getColumn("mac").updateValue(getAddressLog((String) table.getColumn("mac").getValue(), details.getInfo().getMac())); - table.getColumn("serial").updateValue(getAddressLog((String) table.getColumn("serial").getValue(), details.getInfo().getSerial())); - table.getColumn("computerName").updateValue(getAddressLog((String) table.getColumn("computerName").getValue(), details.getInfo().getCompName())); - } - - /** - * Checks if an account was created under a name. - * @param name the name. - * @param field the field - * @return {@code True} if so. - */ - public static boolean hasSqlAccount(String name, String field) throws SQLException { - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return true; - } - ResultSet result = null; - PreparedStatement statement; - statement = connection.prepareStatement("SELECT * FROM " + "members" + " WHERE " + "" + field + "='" + name.toLowerCase() + "' LIMIT 1"); - result = statement.executeQuery(); - if (result == null || !result.next()) { - SQLManager.close(connection); - return false; - } - SQLManager.close(connection); - return true; - } - - public static Response updatePassword(String name, String pass) throws SQLException{ - if (!SQLManager.isInitialized()) { - return Response.INVALID_CREDENTIALS; - } - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return Response.INVALID_LOGIN_SERVER; - } - PreparedStatement statement; - statement = connection.prepareStatement("UPDATE " + "members" + " SET password='" + pass + "' WHERE " + "username" + "='" + name.toLowerCase() + "' LIMIT 1"); - boolean Success = statement.execute(); - if(Success) return Response.SUCCESSFUL; - return null; - } - - /** - * Checks if a username & password are correct. - * @param name the name. - * @param pass the pass. - * @return the response. - * @throws SQLException the exception if thrown. - */ - public static Response getCredentialResponse(String name, String pass) throws SQLException { - if (!SQLManager.isInitialized()) { - return Response.INVALID_CREDENTIALS; - } - Connection connection = SQLManager.getConnection(); - if (connection == null) { - return Response.INVALID_LOGIN_SERVER; - } - ResultSet result = null; - PreparedStatement statement; - statement = connection.prepareStatement("SELECT * FROM " + "members" + " WHERE " + "" + "username" + "='" + name.toLowerCase() + "' LIMIT 1"); - result = statement.executeQuery(); - if (result == null || !result.next()) { - SQLManager.close(connection); - return Response.INVALID_CREDENTIALS; - } - String realPass = result.getString("password"); - if (SystemManager.getEncryption().checkPassword(pass, realPass)) { - SQLManager.close(connection); - return Response.SUCCESSFUL; - } - SQLManager.close(connection); - return Response.INVALID_CREDENTIALS; - } - - /** - * Gets the address log. - * @param original the original log. - * @param address the address. - * @return the address. - */ - private String getAddressLog(String original, String address) { - String log = ""; - if (original != null && original.length() > 0) { - log += original; - if (log.charAt(log.length() - 1) != '|') { - log += "|"; - } - } - if (address != null && address.length() > 0 && (original == null || !original.contains(address))) { - log += address + "|"; - } - if (log.length() > 0 && log.charAt(log.length() - 1) == '|') { - log = log.substring(0, log.length() - 1); - } - if (log == null) { - log = ""; - } - return log; - } - - /** - * Gets the details. - * @return the details. - */ - public PlayerDetails getDetails() { - return details; - } - - /** - * Gets the table. - * @return the table. - */ - public SQLTable getTable() { - return table; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/ConfigurationManager.java b/Server/src/main/java/core/game/node/entity/player/link/ConfigurationManager.java deleted file mode 100644 index 2f75f4f4d..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/ConfigurationManager.java +++ /dev/null @@ -1,209 +0,0 @@ -package core.game.node.entity.player.link; - -import core.game.node.entity.player.Player; - -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.net.packet.PacketRepository; -import core.net.packet.context.ConfigContext; -import core.net.packet.out.Config; - -import java.nio.ByteBuffer; - -/** - * 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); - } - } - - 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/java/core/game/node/entity/player/link/SavedData.java b/Server/src/main/java/core/game/node/entity/player/link/SavedData.java deleted file mode 100644 index 82feb9db9..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/SavedData.java +++ /dev/null @@ -1,147 +0,0 @@ -package core.game.node.entity.player.link; - -import core.game.node.entity.player.Player; - -import core.game.node.entity.player.link.spawn.SpawnData; -import rs09.game.system.SystemLogger; - -import java.nio.ByteBuffer; - -/** - * Represents a managing class of saved data related to ingame interactions, - * such as questing data, npc talking data, etc. - * @author 'Vexia - */ -public class SavedData { - - /** - * Represents the global data to save. - */ - private final GlobalData globalData = new GlobalData(); - - /** - * Represents the activity data to save. - */ - private final ActivityData activityData = new ActivityData(); - - /** - * Represents the quest data to save. - */ - private final QuestData questData = new QuestData(); - - /** - * The spawn data to save. - */ - private final SpawnData spawnData = new SpawnData(); - - /** - * The player. - */ - private final Player player; - - /** - * Constructs a new {@Code SavedData} {@Code Object} - * @param player the player. - */ - public SavedData(Player player) { - 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. - * @return the value. - */ - public static boolean getBoolean(byte value) { - 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. - */ - public ActivityData getActivityData() { - return activityData; - } - - /** - * Gets the questData. - * @return The questData. - */ - public QuestData getQuestData() { - return questData; - } - - /** - * Gets the globalData. - * @return The globalData. - */ - public GlobalData getGlobalData() { - return globalData; - } - - /** - * Gets the player. - * @return the player - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the spawnData. - * @return the spawnData - */ - public SpawnData getSpawnData() { - return spawnData; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/Settings.java b/Server/src/main/java/core/game/node/entity/player/link/Settings.java deleted file mode 100644 index 6eeb661d6..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/Settings.java +++ /dev/null @@ -1,686 +0,0 @@ -package core.game.node.entity.player.link; - -import rs09.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.communication.CommunicationInfo; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.net.packet.IoBuffer; - -import java.nio.ByteBuffer; - -/** - * Holds a player's settings. - * @author Emperor - */ -public final class Settings { - - /** - * The player. - */ - private final Player player; - - /** - * The run energy. - */ - private double runEnergy = 100.0; - - /** - * The player's weight. - */ - private double weight; - - /** - * The brightness setting. - */ - private int brightness = 2; - - /** - * The music volume. - */ - private int musicVolume; - - /** - * The sound effects volume. - */ - private int soundEffectVolume; - - /** - * The area sounds volume. - */ - private int areaSoundVolume; - - /** - * If the player has the single mouse button setting enabled. - */ - private boolean singleMouseButton; - - /** - * If the chat effects should be disabled. - */ - private boolean disableChatEffects; - - /** - * If the private chat should be split from public chat. - */ - private boolean splitPrivateChat; - - /** - * If the player has the accept aid setting enabled. - */ - private boolean acceptAid; - - /** - * If the player's run button is toggled. - */ - private boolean runToggled; - - /** - * The public chat setting. - */ - private int publicChatSetting = 0; - - /** - * The private chat setting. - */ - private int privateChatSetting = 0; - - /** - * The clan chat setting. - */ - private int clanChatSetting = 0; - - /** - * The trade setting. - */ - private int tradeSetting = 0; - - /** - * The assist setting. - */ - private int assistSetting = 0; - - /** - * If the special attack is toggled. - */ - private boolean specialToggled; - - /** - * The current special energy the player has left. - */ - private int specialEnergy = 100; - - /** - * The current attack style index. - */ - private int attackStyleIndex = 0; - - /** - * Constructs a new {@code Settings} {@code Object}. - * @param player The player. - */ - public Settings(Player player) { - this.player = player; - } - - /** - * 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); - 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); - } - } - - /** - * Toggles the attack style index. - * @param index The index. - */ - public void toggleAttackStyleIndex(int index) { - this.attackStyleIndex = index; - player.getConfigManager().set(43, attackStyleIndex); - } - - /** - * Updates the chat settings. - */ - public void updateChatSettings() { - player.getSession().write(new IoBuffer(232).put(publicChatSetting).put(privateChatSetting).put(tradeSetting)); - } - - /** - * Sets the chat settings. - * @param pub The public chat setting. - * @param priv The private chat setting. - * @param trade The trade setting. - */ - public void updateChatSettings(int pub, int priv, int trade) { - boolean update = false; - if (publicChatSetting != pub) { - publicChatSetting = pub; - update = true; - } - if (privateChatSetting != priv) { - privateChatSetting = priv; - update = true; - CommunicationInfo.notifyPlayers(player, privateChatSetting != 2, true); - } - if (tradeSetting != trade) { - tradeSetting = trade; - update = true; - } - if (update) { - updateChatSettings(); - } - } - - /** - * Sets the chat settings. - * @param pub The public chat setting. - * @param priv The private chat setting. - * @param trade The trade setting. - */ - public void setChatSettings(int pub, int priv, int trade) { - publicChatSetting = pub; - privateChatSetting = priv; - tradeSetting = trade; - } - - /** - * Writes the settings on the byte buffer. - * @param buffer The byte buffer. - */ - 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()); - soundEffectVolume = Integer.parseInt( settingsData.get("soundEffectVolume").toString()); - areaSoundVolume = Integer.parseInt( settingsData.get("areaSoundVolume").toString()); - singleMouseButton = (boolean) settingsData.get("singleMouse"); - disableChatEffects = (boolean) settingsData.get("disableChatEffects"); - splitPrivateChat = (boolean) settingsData.get("splitPrivate"); - acceptAid = (boolean) settingsData.get("acceptAid"); - runToggled = (boolean) settingsData.get("runToggled"); - publicChatSetting = Integer.parseInt( settingsData.get("publicChatSetting").toString()); - privateChatSetting = Integer.parseInt( settingsData.get("privateChatSetting").toString()); - clanChatSetting = Integer.parseInt( settingsData.get("clanChatSetting").toString()); - tradeSetting = Integer.parseInt( settingsData.get("tradeSetting").toString()); - assistSetting = Integer.parseInt( settingsData.get("assistSetting").toString()); - runEnergy = Double.parseDouble( settingsData.get("runEnergy").toString()); - specialEnergy = Integer.parseInt( settingsData.get("specialEnergy").toString()); - attackStyleIndex = Integer.parseInt( settingsData.get("attackStyle").toString()); - player.getProperties().setRetaliating((boolean) settingsData.get("retaliation")); - } - - /** - * Toggles the special attack bar. - */ - public void toggleSpecialBar() { - setSpecialToggled(!specialToggled); - } - - /** - * Toggles the special attack bar. - * @param enable If the special attack should be enabled. - */ - public void setSpecialToggled(boolean enable) { - specialToggled = !specialToggled; - player.getConfigManager().set(301, specialToggled ? 1 : 0); - } - - /** - * Checks if the special attack bar is toggled. - * @return {@code True} if so. - */ - public boolean isSpecialToggled() { - return specialToggled; - } - - /** - * Drains an amount of special attack energy. - * @param amount The amount to drain. - * @return {@code True} if succesful, {@code false} if the special attack - * energy amount hasn't changed after calling this method. - */ - public boolean drainSpecial(int amount) { - if (!specialToggled) { - return false; - } - setSpecialToggled(false); - if (amount > specialEnergy) { - player.getPacketDispatch().sendMessage("You do not have enough special attack energy left."); - return false; - } - setSpecialEnergy(specialEnergy - amount); - return true; - } - - /** - * Sets the special energy amount. - * @param value The amount to set. - */ - public void setSpecialEnergy(int value) { - specialEnergy = value; - player.getConfigManager().set(300, specialEnergy * 10); - } - - /** - * Gets the amount of special energy left. - * @return The amount of energy. - */ - public int getSpecialEnergy() { - return specialEnergy; - } - - /** - * Toggles the retaliating button. - */ - public void toggleRetaliating() { - player.getProperties().setRetaliating(!player.getProperties().isRetaliating()); - player.getConfigManager().set(172, !player.getProperties().isRetaliating() ? 1 : 0); - } - - /** - * Toggles the singleMouseButton. - */ - public void toggleMouseButton() { - singleMouseButton = !singleMouseButton; - player.getConfigManager().set(170, singleMouseButton ? 1 : 0); - } - - /** - * Toggles the disableChatEffects. - */ - public void toggleChatEffects() { - disableChatEffects = !disableChatEffects; - player.getConfigManager().set(171, disableChatEffects ? 1 : 0); - } - - /** - * Toggles the splitPrivateChat. - */ - public void toggleSplitPrivateChat() { - splitPrivateChat = !splitPrivateChat; - player.getConfigManager().set(287, splitPrivateChat ? 1 : 0); - } - - /** - * Toggles the acceptAid. - */ - public void toggleAcceptAid() { - acceptAid = !acceptAid; - player.getConfigManager().set(427, acceptAid ? 1 : 0); - } - - /** - * Toggles the run button. - */ - public void toggleRun() { - setRunToggled(!runToggled); - } - - /** - * Toggles the run button. - * If the run button should be enabled. - */ - public void setRunToggled(boolean enabled) { - runToggled = enabled; - player.getConfigManager().set(173, runToggled ? 1 : 0); - } - - /** - * Decreases the run energy with the given amount (drain parameter).
To - * increase, use a negative drain value. - * @param drain The drain amount. - */ - public void updateRunEnergy(double drain) { - runEnergy -= drain; - if (runEnergy < 0) { - runEnergy = 0.0; - } else if (runEnergy > 100) { - runEnergy = 100.0; - } - player.getPacketDispatch().sendRunEnergy(); - } - - /** - * Updates the weight. - */ - public void updateWeight() { - weight = 0.0; - for (int i = 0; i < 28; i++) { - Item item = player.getInventory().get(i); - if (item == null) { - continue; - } - double value = item.getDefinition().getConfiguration(ItemConfigParser.WEIGHT, 0.0); - if (value > 0) { - weight += value; - } - } - for (int i = 0; i < 11; i++) { - Item item = player.getEquipment().get(i); - if (item == null) { - continue; - } - weight += item.getDefinition().getConfiguration(ItemConfigParser.WEIGHT, 0.0); - } - } - - /** - * Gets the weight. - * @return The weight. - */ - public double getWeight() { - return weight; - } - - /** - * Gets the brightness. - * @return The brightness. - */ - public int getBrightness() { - return brightness; - } - - /** - * Sets the brightness. - * @param brightness The brightness to set. - */ - public void setBrightness(int brightness) { - this.brightness = brightness; - player.getConfigManager().set(166, brightness + 1); - } - - /** - * Gets the musicVolume. - * @return The musicVolume. - */ - public int getMusicVolume() { - return musicVolume; - } - - /** - * Sets the musicVolume. - * @param musicVolume The musicVolume to set. - */ - public void setMusicVolume(int musicVolume) { - this.musicVolume = musicVolume; - player.getConfigManager().set(168, musicVolume); - } - - /** - * Gets the soundEffectVolume. - * @return The soundEffectVolume. - */ - public int getSoundEffectVolume() { - return soundEffectVolume; - } - - /** - * Sets the soundEffectVolume. - * @param soundEffectVolume The soundEffectVolume to set. - */ - public void setSoundEffectVolume(int soundEffectVolume) { - this.soundEffectVolume = soundEffectVolume; - player.getConfigManager().set(169, soundEffectVolume); - } - - /** - * Gets the areaSoundVolume. - * @return The areaSoundVolume. - */ - public int getAreaSoundVolume() { - return areaSoundVolume; - } - - /** - * Sets the areaSoundVolume. - * @param areaSoundVolume The areaSoundVolume to set. - */ - public void setAreaSoundVolume(int areaSoundVolume) { - this.areaSoundVolume = areaSoundVolume; - player.getConfigManager().set(872, areaSoundVolume); - } - - /** - * Gets the singleMouseButton. - * @return The singleMouseButton. - */ - public boolean isSingleMouseButton() { - return singleMouseButton; - } - - /** - * Gets the disableChatEffects. - * @return The disableChatEffects. - */ - public boolean isDisableChatEffects() { - return disableChatEffects; - } - - /** - * Gets the splitPrivateChat. - * @return The splitPrivateChat. - */ - public boolean isSplitPrivateChat() { - return splitPrivateChat; - } - - /** - * Gets the acceptAid. - * @return The acceptAid. - */ - public boolean isAcceptAid() { - if (player.getIronmanManager().isIronman()) { - return false; - } - return acceptAid; - } - - /** - * Gets the runToggled. - * @return The runToggled. - */ - public boolean isRunToggled() { - return runToggled; - } - - /** - * Gets the publicChatSetting. - * @return The publicChatSetting. - */ - public int getPublicChatSetting() { - return publicChatSetting; - } - - /** - * Sets the publicChatSetting. - * @param publicChatSetting The publicChatSetting to set. - */ - public void setPublicChatSetting(int publicChatSetting) { - this.publicChatSetting = publicChatSetting; - updateChatSettings(); - } - - /** - * Gets the privateChatSetting. - * @return The privateChatSetting. - */ - public int getPrivateChatSetting() { - return privateChatSetting; - } - - /** - * Sets the privateChatSetting. - * @param privateChatSetting The privateChatSetting to set. - */ - public void setPrivateChatSetting(int privateChatSetting) { - this.privateChatSetting = privateChatSetting; - updateChatSettings(); - } - - /** - * Gets the clanChatSetting. - * @return The clanChatSetting. - */ - public int getClanChatSetting() { - return clanChatSetting; - } - - /** - * Sets the clanChatSetting. - * @param clanChatSetting The clanChatSetting to set. - */ - public void setClanChatSetting(int clanChatSetting) { - this.clanChatSetting = clanChatSetting; - player.getConfigManager().set(1054, clanChatSetting); - } - - /** - * Gets the tradeSetting. - * @return The tradeSetting. - */ - public int getTradeSetting() { - return tradeSetting; - } - - /** - * Sets the tradeSetting. - * @param tradeSetting The tradeSetting to set. - */ - public void setTradeSetting(int tradeSetting) { - this.tradeSetting = tradeSetting; - updateChatSettings(); - } - - /** - * Gets the assistSetting. - * @return The assistSetting. - */ - public int getAssistSetting() { - return assistSetting; - } - - /** - * Sets the assistSetting. - * @param assistSetting The assistSetting to set. - */ - public void setAssistSetting(int assistSetting) { - this.assistSetting = assistSetting; - player.getConfigManager().set(1055, assistSetting); - } - - /** - * @return the runEnergy - */ - public double getRunEnergy() { - return runEnergy; - } - - /** - * @param runEnergy the runEnergy to set - */ - public void setRunEnergy(double runEnergy) { - this.runEnergy = runEnergy; - } - - /** - * Gets the attackStyleIndex. - * @return The attackStyleIndex. - */ - public int getAttackStyleIndex() { - return attackStyleIndex; - } - - /** - * Sets the attackStyleIndex. - * @param attackStyleIndex The attackStyleIndex to set. - */ - public void setAttackStyleIndex(int attackStyleIndex) { - this.attackStyleIndex = attackStyleIndex; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/SkullManager.java b/Server/src/main/java/core/game/node/entity/player/link/SkullManager.java deleted file mode 100644 index 5a6ec9efa..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/SkullManager.java +++ /dev/null @@ -1,188 +0,0 @@ -package core.game.node.entity.player.link; - -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 java.util.ArrayList; -import java.util.List; - -/** - * Represents a managing class of the active player skulls. - * @author Vexia - * @author Emperor - */ -public final class SkullManager { - - /** - * The player instance. - */ - private final Player player; - - /** - * Represents if the player is in the wilderness. - */ - private boolean wilderness = false; - - /** - * If the wilderness zone is currently disabled. - */ - private boolean wildernessDisabled = false; - - /** - * Represents the current wilderness level. - */ - private int level; - - /** - * The players this player has skulled on. - */ - private final List skullCauses = new ArrayList(); - - /** - * If the player is skulled. - */ - private boolean skulled; - - /** - * If the skull check is disabled. - */ - private boolean skullCheckDisabled; - - /** - * Constructs a new {@code SkullManager} {@code Object}. - * @param player the player. - */ - public SkullManager(Player player) { - this.player = player; - } - - /** - * Checks if we should skull on this entity. - * @param other The entity to check. - */ - public void checkSkull(Entity other) { - if (!(other instanceof Player) || !wilderness || skullCheckDisabled) { - return; - } - Player o = (Player) other; - for (Player p : o.getSkullManager().skullCauses) { - if (p == player) { - return; - } - } - if (skullCauses.contains(o)) { - return; - } - skullCauses.add(o); - player.clearState("skull"); - player.registerState("skull").init(); - } - - /** - * Sets the skull icon. - * @param skullIcon The skull icon. - */ - public void setSkullIcon(int skullIcon) { - player.getAppearance().setSkullIcon(skullIcon); - player.getUpdateMasks().register(new AppearanceFlag(player)); - } - - /** - * Resets the skull causes cache. - */ - public void reset() { - skullCauses.clear(); - setSkullIcon(-1); - setSkulled(false); - } - - /** - * Gets the player. - * @return the player. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the level. - * @return the level. - */ - public int getLevel() { - return level; - } - - /** - * Sets the level. - * @param level the level to set. - */ - public void setLevel(int level) { - this.level = level; - } - - /** - * Gets the value. - * @return the wilderness. - */ - public boolean isWilderness() { - return wilderness; - } - - /** - * Sets the value of this boolean. - * @param wilderness the wilderness to set. - */ - public void setWilderness(boolean wilderness) { - this.wilderness = wilderness; - } - - /** - * Gets the skullCheckDisabled. - * @return The skullCheckDisabled. - */ - public boolean isSkullCheckDisabled() { - return skullCheckDisabled; - } - - /** - * Sets the skullCheckDisabled. - * @param skullCheckDisabled The skullCheckDisabled to set. - */ - public void setSkullCheckDisabled(boolean skullCheckDisabled) { - this.skullCheckDisabled = skullCheckDisabled; - } - - /** - * Gets the wildernessDisabled. - * @return The wildernessDisabled. - */ - public boolean isWildernessDisabled() { - return wildernessDisabled; - } - - /** - * Sets the wildernessDisabled. - * @param wildernessDisabled The wildernessDisabled to set. - */ - public void setWildernessDisabled(boolean wildernessDisabled) { - this.wildernessDisabled = wildernessDisabled; - } - - /** - * Gets the skulled. - * @return The skulled. - */ - public boolean isSkulled() { - return skulled; - } - - /** - * Sets the skulled. - * @param skulled The skulled to set. - */ - public void setSkulled(boolean skulled) { - this.skulled = skulled; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/TeleportManager.java b/Server/src/main/java/core/game/node/entity/player/link/TeleportManager.java deleted file mode 100644 index 4e7b5750a..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/TeleportManager.java +++ /dev/null @@ -1,840 +0,0 @@ -package core.game.node.entity.player.link; - -import rs09.ServerConstants; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -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.quest.Quest; -import core.game.system.task.Pulse; -import rs09.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; - -/** - * Handles the entity teleport. - * @author SonicForce41 - * @author Woah - */ -public class TeleportManager { - - /** - * The wildy teleport type. - */ - 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. - */ - private final Entity entity; - - /** - * The last teleport of this Entity - */ - private Pulse lastTeleport; - - /** - * The current teleport of this Entity - */ - private Pulse currentTeleport; - - /** - * The current teleport type. - */ - private int teleportType; - - /** - * Constructs a new {@code Teleporter.java} {@Code Object}. - * @param entity the Entity - */ - public TeleportManager(Entity entity) { - this.entity = entity; - lastTeleport = TeleportType.HOME.getPulse(entity, ServerConstants.HOME_LOCATION); - } - - /** - * Sends the teleport. - * @param location the Location. - * @return {@code True} if the player successfully started teleporting. - */ - public boolean send(Location location) { - return send(location, entity instanceof Player ? getType((Player) entity) : TeleportType.NORMAL, 0); - } - - /** - * Sends the teleport. - * @param location the Location. - * @param type the NodeType. - * @return {@code True} if the player successfully started teleporting. - */ - public boolean send(Location location, TeleportType type) { - return send(location, type, 0); - } - - /** - * Sends the teleport. - * @param location the Location. - * @param type the NodeType. - * @param teleportType The teleporting type. (0=spell, 1=item, 2=object, - * 3=npc -1= force) - * @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."); - } - 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.getDialogueInterpreter().close(); - } - } - if(entity.getAttribute("tablet-spell",false)){ - type = TeleportType.TELETABS; - } - 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); - } - if (entity instanceof Player) { - ((Player) entity).getInterfaceManager().close(); - } - return true; - } - - /** - * Fires a random event. - * @param entity The entity teleporting. - * @param location The destination lcoation. - */ - public static void fireRandom(Entity entity, Location location) { - if (entity instanceof Player && entity.getTeleporter().getTeleportType() == 0) { - Player p = (Player) entity; - } - } - - /** - * 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 - */ - public final Entity getEntity() { - return entity; - } - - /** - * Gets the last teleport pulse. - * @return the Pulse - */ - public final Pulse getLastTeleport() { - return lastTeleport; - } - - /** - * Gets the current teleport pulse. - * @return the Pulse - */ - public final Pulse getCurrentTeleport() { - return currentTeleport; - } - - /** - * Represents a NodeType for Teleporter - * @author SonicForce41 - */ - public enum TeleportType { - - /** - * The value types - */ - NORMAL(new TeleportSettings(8939, 8941, 1576, 1577)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(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); - } - entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); - entity.graphics(new Graphics(getSettings().getEndGfx())); - return true; - } - delay++; - return false; - } - - @Override - public void stop() { - super.stop(); - entity.unlock(); - entity.lock(4); - } - }; - } - }, - ANCIENT(new TeleportSettings(1979, -1, 392, -1)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); - entity.graphics(new Graphics(getSettings().getStartGfx())); - } else if (delay == 4) { - entity.getProperties().setTeleportLocation(Location.create(location)); - fireRandom(entity, location); - } else if (delay == 5) { - entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); - entity.graphics(new Graphics(getSettings().getEndGfx())); - return true; - } - delay++; - return false; - } - - @Override - public void stop() { - super.stop(); - entity.getAnimator().forceAnimation(new Animation(-1)); - entity.graphics(new Graphics(-1)); - entity.unlock(); - } - }; - } - }, - LUNAR(new TeleportSettings(1816, -1, 747, -1)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - entity.graphics(new Graphics(getSettings().getStartGfx(), 120)); - entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); - } else if (delay == 3) { - entity.getProperties().setTeleportLocation(Location.create(location)); - fireRandom(entity, location); - } else if (delay == 4) { - entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); - entity.graphics(new Graphics(getSettings().getEndGfx())); - return true; - } - delay++; - return false; - } - - @Override - public void stop() { - super.stop(); - entity.getAnimator().forceAnimation(new Animation(-1)); - entity.graphics(new Graphics(-1)); - entity.unlock(); - } - }; - } - }, - TELETABS(new TeleportSettings(4731, -1, 678, -1)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(979); - } - entity.getAnimator().forceAnimation(new Animation(4069)); - } else if (delay == 2) { - entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote(), Priority.HIGH)); - entity.graphics(new Graphics(getSettings().getStartGfx())); - } else if (delay == 3) { - entity.getProperties().setTeleportLocation(Location.create(location)); - } else if (delay == 4) { - entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote())); - entity.graphics(new Graphics(getSettings().getEndGfx())); - return true; - } - delay++; - return false; - } - - @Override - public void stop() { - super.stop(); - entity.getAnimator().forceAnimation(new Animation(-1)); - entity.graphics(new Graphics(-1)); - entity.unlock(); - entity.lock(2); - } - }; - } - }, - 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 (TutorialSession.getExtension(player).getStage() < TutorialSession.MAX_STAGE) { - stop(); - return; - } - /*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) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - entity.lock(); - entity.getAnimator().forceAnimation(new Animation(1816)); - } else if (delay == 3) { - entity.getProperties().setTeleportLocation(Location.create(location)); - } else if (delay == 4) { - entity.getAnimator().forceAnimation(Animation.RESET); - entity.unlock(); - return true; - } - delay++; - return false; - } - }; - } - }, - TELE_OTHER(new TeleportSettings(1816, -1, 342, -1)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(199); - } - entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); - entity.graphics(new Graphics(getSettings().getStartGfx())); - } else if (delay == 3) { - entity.getProperties().setTeleportLocation(Location.create(location)); - } else if (delay == 4) { - entity.getAnimator().forceAnimation(new Animation(-1)); - entity.unlock(); - return true; - } - delay++; - return false; - } - }; - } - }, - FAIRY_RING(new TeleportSettings(-1, -1, -1, -1)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - entity.graphics(Graphics.create(569)); - return new Pulse(1, entity) { - int delay; - - @Override - public boolean pulse() { - switch (++delay) { - case 2: - entity.animate(Animation.create(3265)); - if(entity instanceof Player) { - ((Player) entity).getAudioManager().send(1098, 1); - } - break; - case 4: - Quest quest = null; - if (entity instanceof Player) { - quest = ((Player) entity).getQuestRepository().getQuest("Lost City"); - } - if (quest != null) { - if (quest.getStage(entity.asPlayer()) == 21) { - quest.finish(entity.asPlayer()); - } - } - entity.animate(Animation.create(3266)); - entity.getProperties().setTeleportLocation(location); - entity.unlock(); - entity.lock(2); - return true; - } - return false; - } - - }; - } - }, - PURO_PURO(new TeleportSettings(6601, 1118, -1, -1)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); - entity.graphics(new Graphics(1118)); - } else if (delay == 9) { - entity.getProperties().setTeleportLocation(Location.create(location)); - entity.getAnimator().forceAnimation(new Animation(-1)); - entity.unlock(); - return true; - } - delay++; - return false; - } - }; - } - }, - ECTOPHIAL(new TeleportSettings(8939, 8941, 1587, 1588)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(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); - } - entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); - entity.graphics(new Graphics(getSettings().getEndGfx())); - return true; - } - 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) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(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); - } - entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); - entity.graphics(new Graphics(getSettings().getEndGfx())); - return true; - } - delay++; - return false; - } - - @Override - public void stop() { - super.stop(); - entity.unlock(); - entity.lock(4); - } - }; - } - }, - CABBAGE(new TeleportSettings(9984, 9986, 1731, 1732)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(200); - } - entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); - entity.graphics(new Graphics(getSettings().getStartGfx())); - } else if (delay == 5) { - entity.getProperties().setTeleportLocation(Location.create(location)); - fireRandom(entity, location); - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(201); - } - entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); - entity.graphics(new Graphics(getSettings().getEndGfx())); - return true; - } - delay++; - return false; - } - - @Override - public void stop() { - super.stop(); - entity.unlock(); - entity.lock(4); - } - }; - } - }, - ENTRANA_MAGIC_DOOR(new TeleportSettings(10100, 9013, 1745, 1747)) { // - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(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); - } - entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); - entity.graphics(new Graphics(getSettings().getEndGfx())); - return true; - } - delay++; - return false; - } - - @Override - public void stop() { - super.stop(); - entity.unlock(); - entity.lock(4); - } - }; - } - }, - MINIGAME(new TeleportSettings(6601, 1118, -1, -1)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - Player player; - - @Override - public boolean pulse() { - if (delay == 0) { - entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); - entity.graphics(new Graphics(1118)); - } else if (delay == 9) { - entity.getProperties().setTeleportLocation(Location.create(location)); - entity.getAnimator().forceAnimation(new Animation(-1)); - entity.unlock(); - return true; - } - delay++; - return false; - } - - @Override - public void start() { - - super.start(); - } - - @Override - public void stop() { - super.stop(); - entity.getAnimator().forceAnimation(new Animation(-1)); - entity.graphics(new Graphics(-1)); - } - }; - } - }, - INSTANT(new TeleportSettings(-1, -1, -1, -1)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { - int delay = 0; - - @Override - public boolean pulse() { - if (delay == 0) { - entity.lock(); - } else if (delay == 3) { - entity.getProperties().setTeleportLocation(Location.create(location)); - } else if (delay == 4) { - entity.getAnimator().forceAnimation(Animation.RESET); - entity.unlock(); - return true; - } - delay++; - return false; - } - }; - } - }; - - /** - * The NodeSettings - */ - private TeleportSettings settings; - - /** - * @param entity - * @param location - * @return the Pulse - */ - public abstract Pulse getPulse(final Entity entity, final Location location); - - /** - * Constructs a new {@code Teleporter.java} {@Code Object}. - * @param settings the NodeSettings - */ - TeleportType(TeleportSettings settings) { - this.settings = settings; - } - - /** - * Gets the NodeSettings - * @return the NodeSettings - */ - public final TeleportSettings getSettings() { - return settings; - } - } - - /** - * Represents teleport node settings - * @author SonicForce41 - */ - static class TeleportSettings { - - /** - * The start animation. - */ - private int startAnim; - - /** - * The end animation. - */ - private int endAnim; - - /** - * The start graphics. - */ - private int startGFX; - - /** - * The end gfx. - */ - private int endGFX; - - /** - * Constructs a new {@code Teleporter.java} {@code Object}. - * @param startAnim the start animation. - * @param endAnim the end animation. - * @param startGfx the start graphics. - * @param endGfx the end graphiics. - */ - public TeleportSettings(int startAnim, int endAnim, int startGfx, int endGfx) { - this.startAnim = startAnim; - this.endAnim = endAnim; - this.startGFX = startGfx; - this.endGFX = endGfx; - } - - /** - * @return the anim. - */ - public final int getStartEmote() { - return startAnim; - } - - /** - * @return the anim. - */ - public final int getEndEmote() { - return endAnim; - } - - /** - * @return the start graphics. - */ - public final int getStartGfx() { - return startGFX; - } - - /** - * @return the end gfx. - */ - public final int getEndGfx() { - return endGFX; - } - } - - /** - * Gets the teleporting type for the player depending on his/her current - * spellbook. - * @param player The player. - * @return The teleport type. - */ - public static TeleportType getType(Player player) { - switch (player.getSpellBookManager().getSpellBook()) { - case 193: - return TeleportType.ANCIENT; - case 430: - return TeleportType.LUNAR; - } - return TeleportType.NORMAL; - } - - /** - * Gets the teleportType. - * @return The teleportType. - */ - public int getTeleportType() { - return teleportType; - } - - /** - * Sets the teleportType. - * @param teleportType The teleportType to set. - */ - public void setTeleportType(int teleportType) { - this.teleportType = teleportType; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/audio/Audio.java b/Server/src/main/java/core/game/node/entity/player/link/audio/Audio.java deleted file mode 100644 index 4e4f9cd05..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/audio/Audio.java +++ /dev/null @@ -1,116 +0,0 @@ -package core.game.node.entity.player.link.audio; - -import core.game.node.entity.player.Player; - -import java.util.List; - -/** - * An audio piece to play. - * @author Vexia - */ -public class Audio { - - /** - * The default volume. - */ - public static final int VOLUME = 10; - - /** - * The id of the audio piece. - */ - private final int id; - - /** - * The volume. - */ - private final int volume; - - /** - * The delay. - */ - private final int delay; - - /** - * 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 = id; - this.volume = volume; - this.delay = delay; - } - - /** - * Constructs a new {@Code Audio} {@Code Object} - * @param id the id. - * @param volume the volume. - */ - public Audio(int id, int volume) { - this(id, volume, 0); - } - - /** - * 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) { - player.getAudioManager().send(this, global); - } - - /** - * 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; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/audio/AudioManager.java b/Server/src/main/java/core/game/node/entity/player/link/audio/AudioManager.java deleted file mode 100644 index 5cf516f4e..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/audio/AudioManager.java +++ /dev/null @@ -1,110 +0,0 @@ -package core.game.node.entity.player.link.audio; - -import core.game.node.entity.player.Player; -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); - } - - /** - * Sends an audio packet. - * @param audio the audio. - * @param global if globally heard. - */ - public void send(Audio audio, boolean global) { - if (global) { - send(audio, RegionManager.getLocalPlayers(player, MapDistance.SOUND.getDistance())); - return; - } - PacketRepository.send(AudioPacket.class, new DefaultContext(player, audio)); - } - - /** - * Sends an audio packet for a bunch of players. - * @param audio the audio. - * @param players the players. - */ - public void send(Audio audio, List players) { - for (Player p : players) { - if (p == null) { - continue; - } - p.getAudioManager().send(audio); - } - } - - /** - * Gets the player. - * @return the player - */ - public Player getPlayer() { - return player; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/diary/AchievementDiary.java b/Server/src/main/java/core/game/node/entity/player/link/diary/AchievementDiary.java deleted file mode 100644 index 0b5917945..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/diary/AchievementDiary.java +++ /dev/null @@ -1,417 +0,0 @@ -package core.game.node.entity.player.link.diary; - -import core.cache.def.impl.NPCDefinition; -import core.game.component.Component; -import core.game.node.entity.player.Player; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import rs09.game.node.entity.player.link.diary.DiaryLevel; - -import java.util.ArrayList; - -/** - * Represents an achievement diary. - * @author Vexia - */ -public class AchievementDiary { - - /** - * The component id of the diary. - */ - public static final int DIARY_COMPONENT = 275; - - /** - * Completed levels for this diary - */ - public static final ArrayList completedLevels = new ArrayList(); - - /** - * Represents the red color code string. - */ - private static final String RED = ""; - - /** - * Represents the blue color code string. - */ - private static final String BLUE = ""; - - /** - * Represents the yellow color code string. - */ - private static final String YELLOW = ""; - - /** - * Represets the green color code string. - */ - private static final String GREEN = ""; - - /** - * The diary type. - */ - private final DiaryType type; - - /** - * The task types started. - */ - private final boolean[] levelStarted = new boolean[3]; - - /** - * If the rewards have been given. - */ - private final boolean[] levelRewarded = new boolean[3]; - - /** - * The completed achievements. - */ - private final boolean[][] taskCompleted; - - /** - * Constructs a new {@code AchievementDiary} {@code Object} - * @param type the diary type. - */ - public AchievementDiary(DiaryType type) { - this.type = type; - this.taskCompleted = new boolean[type.getAchievements().length][25]; - } - - /** - * Open the achievement diary. - * @param player the player. - */ - public void open(Player player) { - clear(player); - sendString(player, "Achievement Diary - " + type.getName(), 2); - int child = 12; - - sendString(player, (isComplete() ? GREEN : isStarted() ? YELLOW : "") + type.getName() + " Area Tasks", child++); - //child++; - - if (!type.getInfo().isEmpty() && !this.isStarted()) { - sendString(player, type.getInfo(), child++); - child += type.getInfo().split("

").length; - } - child++; - - boolean complete; - String line; - for (int level = 0; level < type.getAchievements().length; level++) { - sendString(player, getStatus(level) + getLevel(level) + "", child++); - child++; - for (int i = 0; i < type.getAchievements(level).length; i++) { - complete = isComplete(level, i); - line = type.getAchievements(level)[i]; - if (line.contains("

")) { - String[] lines = line.split("

"); - for (String l : lines) { - sendString(player, complete ? "" + l : l, child++); - } - } else { - sendString(player, complete ? "" + line : line, child++); - } - sendString(player, "*", child++); - } - 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)); - } - } - - /** - * Clears the diary screen. - * @param player the player. - */ - private void clear(Player player) { - for (int i = 0; i < 311; i++) { - player.getPacketDispatch().sendString("", DIARY_COMPONENT, i); - } - } - - public void parse(JSONObject data){ - JSONArray startedArray = (JSONArray) data.get("startedLevels"); - for(int i = 0; i < startedArray.size(); i++){ - levelStarted[i] = (boolean) startedArray.get(i); - } - JSONArray completedArray = (JSONArray) data.get("completedLevels"); - for(int i = 0; i < completedArray.size(); i++){ - JSONArray level = (JSONArray) completedArray.get(i); - boolean completed = true; - for(int j = 0; j < level.size(); j++){ - taskCompleted[i][j] = (boolean) level.get(j); - if(!taskCompleted[i][j]){completed = !completed;} - completedLevels.add(i); - } - } - JSONArray rewardedArray = (JSONArray) data.get("rewardedLevels"); - for(int i = 0; i < rewardedArray.size(); i++){ - levelRewarded[i] = (boolean) rewardedArray.get(i); - } - } - - /** - * Draws the status of the diary. - * @param player the player. - */ - public void drawStatus(Player player) { - if (isStarted()) { - player.getPacketDispatch().sendString((isComplete() ? GREEN : YELLOW) + type.getName(), 259, type.getChild()); - for (int i = 0; i < 3; i++) { - player.getPacketDispatch().sendString((isComplete(i) ? GREEN : isStarted(i) ? YELLOW : "") + getLevel(i), 259, type.getChild() + (i + 1)); - } - } - } - - /** - * Induces a task update. - * @param player the player. - * @param level the level. - * @param index the index of the task. - * @param complete if it's completed. - */ - public void updateTask(Player player, int level, int index, boolean complete) { - if (!levelStarted[level]) { - levelStarted[level] = true; - } - if (!complete) { - player.sendMessage("Well done! A " + type.getName() + " task has been updated."); - } else { - taskCompleted[level][index] = true; - int tempLevel = this.type == DiaryType.LUMBRIDGE ? level - 1 : level; - player.sendMessages("Well done! You have completed " - + (tempLevel == -1 ? "a beginner" : tempLevel == 0 ? "an easy" : tempLevel == 1 ? "a medium" : "a hard") - + " task in the " + type.getName() + " area. Your", "Achievement Diary has been updated."); - } - if (isComplete(level)) { - player.sendMessages("Congratulations! You have completed all of the " + getLevel(level).toLowerCase() - + " tasks in the " + type.getName() + " area.", "Speak to " - + NPCDefinition.forId(type.getNpc(level)).getName() + " to claim your reward."); - } - drawStatus(player); - } - - public void finishTask(Player player, int level, int index) { - if (!this.isComplete(level, index)) { - this.updateTask(player, level, index, true); - boolean complete = true; - for(int i = 0; i < taskCompleted[level].length; i++){ - if(!taskCompleted[level][i]) { - complete = false; - break; - } - } - if(complete){ - completedLevels.add(level); - } else if(completedLevels.contains(level)) completedLevels.remove(level); - } - } - - /** - * Resets a task to un-start - */ - public void resetTask(Player player, int level, int index) { - taskCompleted[level][index] = false; - if (!isStarted(level)) { - this.levelStarted[level] = false; - } - if (!isComplete(level)) { - this.levelRewarded[level] = false; - } - 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. - * @param string the string. - * @param child the child. - */ - private void sendString(Player player, String string, int child) { - player.getPacketDispatch().sendString(string.replace("", BLUE).replace("", RED), DIARY_COMPONENT, child); - } - - /** - * Sets the diary for the level as started. - * @param level the level. - */ - public void setLevelStarted(int level) { - this.levelStarted[level] = true; - } - - /** - * Sets an achievement as completed. - * @param level the level. - * @param index the index. - */ - public void setCompleted(int level, int index) { - this.taskCompleted[level][index] = true; - } - - /** - * Checks if the achievement level is started. - * @param level the level. - * @return {@code True} if so. - */ - public boolean isStarted(int level) { - return this.levelStarted[level]; - } - - /** - * Checks if the diary is started. - * @return {@code True} if so. - */ - public boolean isStarted() { - for (int j = 0; j < type.getLevelNames().length; j++) { - if (isStarted(j)) { - return true; - } - } - return false; - } - - /** - * Checks if an achievement task is completed. - * @param level the level. - * @param index the index. - * @return {@code True} if an achievement is completed. - */ - public boolean isComplete(int level, int index) { - return taskCompleted[level][index]; - } - - /** - * Checks if the achievement level is completed. - * @param level the level. - * @return {@code True} if so. - */ - public boolean isComplete(int level) { - for (int i = 0; i < type.getAchievements(level).length; i++) { - if (!taskCompleted[level][i]) { - return false; - } - } - return true; - } - - public boolean isComplete(int level, boolean cumulative) { - if (isComplete(level)) { - return !cumulative || level <= 0 || isComplete(level - 1, true); - } else { - return false; - } - } - - /** - * Checks if an achievement diary is complete. - * @return {@code True} if completed. - */ - public boolean isComplete() { - for (int i = 0; i < taskCompleted.length; i++) { - for (int x = 0; x < type.getAchievements(i).length; x++) { - if (!taskCompleted[i][x]) { - return false; - } - } - } - return true; - } - - /** - * Gets the level of completion. - * @return the level. - */ - public int getLevel() { - return isComplete(2) ? 2 : isComplete(1) ? 1 : isComplete(0) ? 0 : -1; - } - - /** - * Gets the level of reward. - * @return the level. - */ - public int getReward() { - return isLevelRewarded(2) ? 2 : isLevelRewarded(1) ? 1 : isLevelRewarded(0) ? 0 : -1; - } - - /** - * Gets the level string. - * @param level the level. - * @return the string format. - */ - public String getLevel(int level) { - return type.getLevelNames()[level]; - } - - /** - * Gets the status for a level of completion of the achievement. - * @param level the level. - * @return the string color status. - */ - public String getStatus(int level) { - return !isStarted(level) ? RED : isComplete(level) ? GREEN : YELLOW; - } - - /** - * Sets the level as rewarded. - * @param level the level. - */ - public void setLevelRewarded(int level) { - this.levelRewarded[level] = true; - } - - /** - * Checks if the reward has been given. - * @param level the level. - * @return {@code True} if so. - */ - public boolean isLevelRewarded(int level) { - return levelRewarded[level]; - } - - /** - * Gets the completed. - * @return the completed - */ - public boolean[][] getTaskCompleted() { - return taskCompleted; - } - - /** - * Gets the type. - * @return the type - */ - public DiaryType getType() { - return type; - } - - /** - * Gets the started. - * @return the started - */ - public boolean[] getLevelStarted() { - return levelStarted; - } - - /** - * Gets the rewarded. - * @return the rewarded - */ - public boolean[] getLevelRewarded() { - return levelRewarded; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/diary/MysteriousStatueZone.java b/Server/src/main/java/core/game/node/entity/player/link/diary/MysteriousStatueZone.java deleted file mode 100644 index f6aa1ba26..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/diary/MysteriousStatueZone.java +++ /dev/null @@ -1,96 +0,0 @@ -package core.game.node.entity.player.link.diary; - -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -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.tools.Vector3d; - -@Initializable -public class MysteriousStatueZone extends MapZone implements Plugin { - private final static Vector3d origin = new Vector3d(2740.5, 3490.5, 0); - private final static Vector3d n = new Vector3d(0, 0, 1); - - public MysteriousStatueZone() { - super("mysterious-statue", true); - } - - @Override - public boolean enter(Entity e) { - if (e instanceof Player) { - //System.out.println("enter"); - } - return true; - } - - @Override - public boolean leave(Entity e, boolean logout) { - if (e instanceof Player) { - //System.out.println("leave"); - e.asPlayer().removeAttribute("diary:seers:statue-walk-start"); - } - return true; - } - - @Override - public void locationUpdate(Entity e, Location last) { - if (e instanceof Player) { - Player player = e.asPlayer(); - //System.out.println("update - " + player.getLocation().toString()); - if (!player.getWalkingQueue().isRunning() && !player.getSettings().isRunToggled()) { - if (player.getAttribute("diary:seers:statue-walk-start") != null) { - Vector3d start = player.getAttribute("diary:seers:statue-walk-start"); - Vector3d a = player.getAttribute("diary:seers:statue-walk-a"); - Vector3d b = new Vector3d(player.getLocation()).sub(origin); - - double angle_a_b = Vector3d.signedAngle(a, b, n) * 360. / 2 / 3.14159265355; - - /* player.getWalkingQueue().isMoving() doesn't work right, or at least does not do what name implies it should - if (!player.getWalkingQueue().isMoving()) { - System.out.println("removing, not moving"); - player.removeAttribute("diary:seers:statue-walk-start"); - }*/ - if (angle_a_b >= 0) { - //System.out.println("removing, not going clockwise"); - player.removeAttribute("diary:seers:statue-walk-start"); - } - if (b.epsilonEquals(start, .001)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 0, 1); - //System.out.println("removing, finished task"); - player.removeAttribute("diary:seers:statue-walk-start"); - } - - player.setAttribute("diary:seers:statue-walk-a", b); - } else { - //System.out.println("started"); - Vector3d start = new Vector3d(player.getLocation()).sub(origin); - player.setAttribute("diary:seers:statue-walk-start", start); - player.setAttribute("diary:seers:statue-walk-a", start); - } - } else { - //System.out.println("removing, running or outside"); - player.removeAttribute("diary:seers:statue-walk-start"); - } - } - } - - @Override - public void configure() { - register(new ZoneBorders(2739, 3489, 2742, 3492, 0)); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ZoneBuilder.configure(this); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/grave/GraveManager.java b/Server/src/main/java/core/game/node/entity/player/link/grave/GraveManager.java deleted file mode 100644 index 88720461d..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/grave/GraveManager.java +++ /dev/null @@ -1,151 +0,0 @@ -package core.game.node.entity.player.link.grave; - -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.info.Rights; - -import core.game.node.entity.player.link.HintIconManager; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.item.GroundItem; -import rs09.game.world.GameWorld; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Manages the players grave stone. - * @author Vexia - */ -public class GraveManager { - - /** - * The current grave stones in the world. - */ - private static final Map graves = new HashMap<>(); - - /** - * The player instance. - */ - private final Player player; - - /** - * The grave type. - */ - private GraveType type = GraveType.MEMORIAL_PLAQUE; - - /** - * The current gravestone. - */ - private NPC grave; - - /** - * Constructs a new {@Code GraveManager} {@Code Object} - * @param player the player. - */ - public GraveManager(Player player) { - this.player = player; - } - - /** - * Creates a grave. - * @param ticks the ticks. - * @param items the items. - */ - public void create(int ticks, List items) { - if (hasGrave()) { - grave.clear(); - player.sendMessage("Your previous gravestone has collapsed."); - } - NPC npc = NPC.create(type.getNpcId(), player.getLocation(), player.getName(), GameWorld.getTicks() + ticks, items, type, player.getUsername()); - npc.init(); - setGrave(npc); - } - - /** - * Updates the players grave items. - */ - public void update() { - NPC npc = graves.get(player.getName()); - if (npc != null && npc.isActive()) { - AbstractNPC n = (AbstractNPC) npc; - n.fireEvent("updateItems", player); - HintIconManager.registerHintIcon(player, n); - } - } - - /** - * Checks if a gravestone is generateable at this time. - * @return {@code True} if so. - */ - public boolean generateable() { - if (player.getDetails().getRights() == Rights.ADMINISTRATOR && GameWorld.getSettings().isHosted()) { - return false; - } - if (player.getSkullManager().isWilderness()) { - return false; - } - if (player.getInventory().itemCount() + player.getEquipment().itemCount() <= (player.getPrayer().get(PrayerType.PROTECT_ITEMS) ? 4 : 3)) { - return false; - } - return true; - } - - /** - * Checks if the player has an active grave. - * @return {@code True} if so. - */ - public boolean hasGrave() { - return grave != null && grave.isActive(); - } - - /** - * Gets the type. - * @return the type - */ - public GraveType getType() { - return type; - } - - /** - * Sets the grave type. - * @param type the type to set. - */ - public void setType(GraveType type) { - this.type = type; - } - - /** - * Gets the player. - * @return the player - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the grave. - * @return the grave - */ - public NPC getGrave() { - return grave; - } - - /** - * Sets the bagrave. - * @param grave the grave to set. - */ - public void setGrave(NPC grave) { - this.grave = grave; - } - - /** - * Gets the graves. - * @return the graves - */ - public static Map getGraves() { - return graves; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/grave/GraveType.java b/Server/src/main/java/core/game/node/entity/player/link/grave/GraveType.java deleted file mode 100644 index b4fdc9a4c..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/grave/GraveType.java +++ /dev/null @@ -1,74 +0,0 @@ -package core.game.node.entity.player.link.grave; - -/** - * A grave stone type. - * @author Vexia - */ -public enum GraveType { - MEMORIAL_PLAQUE(0, 5, 6565, "In memory of @name, who died here."), FLAG(50, 6, 6568, "In memory of @name, who died here."), SMALL(500, 6, 6571, "In loving memory of our dear friend @name, who died in this place @mins ago."), ORNATE(5000, 8, 6574, "In loving memory of our dear friend @name, who died in this place @mins ago."), FRONT_OF_LIFE(50000, 10, 6577, "In your travels, pause awhile to remember @name, who passed away in this spot."), STELE(50000, 10, 6580, "In your travels, pause awhile to remember @name, who passed away in this spot."), SARADOMIN(50000, 10, 6583, "@name, an enlightened severant of Saradomin, perished in this place."), ZAMRAOK(50000, 10, 6586, "@name a most bloodthirsty follower of Zamorak, perished in this place."), GUTHIX(50000, 10, 6589, "@name walked with the Balance of Guthix, perished in this place."), BANDOS(50000, 10, 6592, "@name, a vicious warrior dedicated to Bandos, perished in this place."), ARMADYL(50000, 10, 6595, "@name a follower of the Law of Aramdyl, perished in this place."), MEMORIAL_STONE(50000, 10, 6598, "@name, servant of the Unknown Power, perished in this place."), ANGEL_OF_DEATH(500000, 12, 6601, "Ye frails who gaze upon this sight, forget not the date of @name, once mighty, now surrendered to the inescapable grasp of destiny, Requiescat in pace."); - - /** - * The cost of the grave stone. - */ - private final int cost; - - /** - * The decay time of this gravestone. - */ - private final int decay; - - /** - * The npc id of this gravestone. - */ - private final int npcId; - - /** - * The message to display on the grave. - */ - private final String message; - - /** - * Constructs a new {@code GraveType} {@code Object}. - * @param cost the cost. - * @param decay the decay. - */ - GraveType(final int cost, final int decay, final int npc, final String message) { - this.cost = cost; - this.decay = decay; - this.npcId = npc; - this.message = message; - } - - /** - * Gets the decay. - * @return the decay - */ - public int getDecay() { - return decay; - } - - /** - * Gets the cost. - * @return the cost - */ - public int getCost() { - return cost; - } - - /** - * Gets the npcId. - * @return the npcId - */ - public int getNpcId() { - return npcId; - } - - /** - * Gets the message. - * @return the message - */ - public String getMessage() { - return message; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/prayer/PrayerType.java b/Server/src/main/java/core/game/node/entity/player/link/prayer/PrayerType.java deleted file mode 100644 index b49a8bef5..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/prayer/PrayerType.java +++ /dev/null @@ -1,450 +0,0 @@ -package core.game.node.entity.player.link.prayer; - -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.SkillBonus; -import core.game.node.entity.skill.SkillRestoration; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; -import core.game.world.map.zone.ZoneBorders; -import core.tools.StringUtils; - -import java.util.List; - -/** - * Represents a prayer type. - * @author Vexia - * @author Emperor - * @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){ - @Override - public boolean toggle(Player player, boolean on) { - for(SkillRestoration restore : player.getSkills().getRestoration()){ - if(restore == null) continue; - restore.restartStat(on); - } - return super.toggle(player, on); - } - }, - RAPID_HEAL(22, 18, 90, 23, PrayerCategory.PURPLE, 2678){ - @Override - public boolean toggle(Player player, boolean on) { - for(SkillRestoration restore : player.getSkills().getRestoration()){ - if(restore == null) continue; - restore.restartHpSummPray(on); - } - return super.toggle(player, on); - } - }, - PROTECT_ITEMS(25, 18, 91, 25, PrayerCategory.DARK_GREEN, 1982), - HAWK_EYE(26, 6, 864, 27, PrayerCategory.LIME_GREEN, 2666, new SkillBonus(Skills.RANGE, 0.1)), - MYSTIC_LORE(27, 6, 865, 29, PrayerCategory.LIME_GREEN, 2668, new SkillBonus(Skills.MAGIC, 0.1)), - STEEL_SKIN(28, 3, 92, 31, PrayerCategory.BABY_BLUE, 2687, new SkillBonus(Skills.DEFENCE, 0.15)), - ULTIMATE_STRENGTH(31, 3, 93, 33, PrayerCategory.GREEN, 2691, new SkillBonus(Skills.STRENGTH, 0.15)), - INCREDIBLE_REFLEXES(34, 3, 94, 35, PrayerCategory.PINK, 2667, new SkillBonus(Skills.ATTACK, 0.15)), - PROTECT_FROM_SUMMONING(35, 2, 1168, 53, PrayerCategory.DARK_BROWN, PrayerCategory.MAGENTA, new Audio(4262)), - PROTECT_FROM_MAGIC(37, 3, 95, 37, PrayerCategory.LIGHT_BROWN, 2675), - PROTECT_FROM_MISSILES(40, 3, 96, 39, PrayerCategory.LIGHT_BROWN, 2677), - PROTECT_FROM_MELEE(43, 4, 97, 41, PrayerCategory.LIGHT_BROWN, 2676), - EAGLE_EYE(44, 3, 866, 43, PrayerCategory.LIME_GREEN, 2666, new SkillBonus(Skills.RANGE, 0.15)), - MYSTIC_MIGHT(45, 3, 867, 45, PrayerCategory.LIME_GREEN, 2669, new SkillBonus(Skills.MAGIC, 0.15)), - RETRIBUTION(46, 12, 98, 47, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(10000)), - REDEMPTION(49, 6, 99, 49, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(2678)), - SMITE(52, 2, 100, 51, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(2685)), - CHIVALRY(60, 2, 1052, 55, PrayerCategory.PINK, 3826, 65, new SkillBonus(Skills.DEFENCE, 0.2), new SkillBonus(Skills.STRENGTH, 0.18), new SkillBonus(Skills.ATTACK, 0.15)), - PIETY(70, 2, 1053, 57, PrayerCategory.PINK, 3825, 70, new SkillBonus(Skills.DEFENCE, 0.25), new SkillBonus(Skills.STRENGTH, 0.23), new SkillBonus(Skills.ATTACK, 0.2)); - - /** - * Represents the a cache of objects related to prayers in order to decide - * what head icon to display. - */ - private final static Object[][] ICON_CACHE = new Object[][] { { REDEMPTION, 5 }, { RETRIBUTION, 3 }, { SMITE, 4 }, { PROTECT_FROM_MAGIC, 2, 10 }, { PROTECT_FROM_MELEE, 0, 8 }, { PROTECT_FROM_MISSILES, 1, 9 }, { PROTECT_FROM_SUMMONING, 7, PROTECT_FROM_MELEE, 8, PROTECT_FROM_MISSILES, 9, PROTECT_FROM_MAGIC, 10 } }; - - /** - * The level required. - */ - private final int level; - - /** - * The drain rate. - */ - private final int drain; - - /** - * The configuration id of the prayer. - */ - private final int config; - - /** - * The button id. - */ - private final int button; - - /** - * The restriction. - */ - private final PrayerCategory restriction; - - /** - * Represents the second restriction. - */ - private final PrayerCategory secondRestriction; - - /** - * The sound. - */ - private Audio sound; - - /** - * The required level of defence to use this prayer. - */ - private int defenceReq; - - /** - * The skill bonuses for this type. - */ - private final SkillBonus[] bonuses; - - /** - * Constructs a new {@code PrayerType} {@code Object}. - * @param level the level. - * @param drain the drain, represents the seconds until a drain. - * @param config the config value to represent on and off. - * @param button the button value to turn the prayer off and on. - * @param bonuses the skill bonuses for this type. - */ - 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 - * @param config - * @param button - * @param restriction - * @param soundId - * @param defenceReq - * @param bonuses - */ - PrayerType(int level, int drain, int config, int button, PrayerCategory restriction, int soundId, int defenceReq, SkillBonus... bonuses) { - this(level, drain, config, button, restriction, null, new Audio(soundId), defenceReq,bonuses); - } - - /** - * Constructs a new {@code PrayerType} {@code Object}. - * @param level the level. - * @param drain the drain, represents the seconds until a drain. - * @param config the config value to represent on and off. - * @param button the button value to turn the prayer off and on. - * @param bonuses the skill bonuses for this type. - */ - 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 - * @param config - * @param button - * @param restriction - * @param secondRestriction - * @param sound - * @param defenceReq - * @param bonuses - */ - PrayerType(int level, int drain, int config, int button, PrayerCategory restriction, PrayerCategory secondRestriction, Audio sound, int defenceReq, SkillBonus... bonuses) { - this.level = level; - this.drain = drain; - this.config = config; - this.button = button; - this.restriction = restriction; - this.secondRestriction = secondRestriction; - this.sound = sound; - this.bonuses = bonuses; - this.defenceReq = defenceReq; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the drain. - * @return The drain. - */ - public int getDrain() { - return drain; - } - - /** - * Gets the config. - * @return The config. - */ - public int getConfig() { - return config; - } - - /** - * Gets the button. - * @return The button. - */ - public int getButton() { - return button; - } - - /** - * Gets the restriction. - * @return The restriction. - */ - public PrayerCategory getRestriction() { - return restriction; - } - - /** - * Gets the bonuses. - * @return The bonuses. - */ - public SkillBonus[] getBonuses() { - return bonuses; - } - - /** - * Method used to check if the player has the required level to toggle this - * type. - * @param player the player. - * @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("_", " ")) + "."); - return false; - } - return true; - } - - /** - * Method used to check if we need to toggle a prayer on or off. - * @param player the player. - * @return True if toggled. - */ - public boolean toggle(final Player player, final boolean on) { - player.getConfigManager().set(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); - } - } else { - player.getPrayer().getActive().remove(this); - findNextIcon(player); - } - return true; - } - - /** - * Method used to flag others prayers that cannot be toggled together. - * @param player the player. - */ - public void flag(final Player player, final PrayerType type) { - final List active = player.getPrayer().getActive(); - final PrayerType[] remove = new PrayerType[active.size() + 10]; - int index = 0; - for (int i = 0; i < active.size(); i++) { - if (active.get(i).getRestriction() == type.getRestriction() || active.get(i).getSecondRestriction() != null && type.getSecondRestriction() != null && active.get(i).getSecondRestriction() == type.getSecondRestriction()) { - remove[index++] = active.get(i); - continue; - } - for (SkillBonus b : active.get(i).getBonuses()) { - for (SkillBonus bb : type.getBonuses()) { - if ((bb.getSkillId() == b.getSkillId()) || (b.getSkillId() == Skills.STRENGTH || b.getSkillId() == Skills.ATTACK) && (bb.getSkillId() == Skills.MAGIC || bb.getSkillId() == Skills.RANGE) || (b.getSkillId() == Skills.RANGE || b.getSkillId() == Skills.MAGIC) && (bb.getSkillId() == Skills.ATTACK || bb.getSkillId() == Skills.STRENGTH) || (b.getSkillId() == Skills.DEFENCE && bb.getSkillId() == Skills.DEFENCE)) { - remove[index++] = active.get(i); - } - } - } - } - for (int i = 0; i < index; i++) { - remove[i].toggle(player, false); - } - } - - /** - * Method used to iconify the player. - */ - public boolean iconify(final Player player, final int icon) { - if (icon == -1) { - return false; - } - player.getAppearance().setHeadIcon(icon); - player.getAppearance().sync(); - return false; - } - - /** - * Method used to find the next icon in place. - * @param player the player. - */ - public void findNextIcon(final Player player) { - if (!hasIcon(player)) { - player.getAppearance().setHeadIcon(-1); - player.getAppearance().sync(); - } - if ((this == PROTECT_FROM_MELEE || this == PROTECT_FROM_MISSILES || this == PROTECT_FROM_MAGIC) && player.getPrayer().get(PROTECT_FROM_SUMMONING)) { - iconify(player, 7); - } else if (this == PROTECT_FROM_SUMMONING) { - for (PrayerType t : player.getPrayer().getActive()) { - iconify(player, getIcon(player, t)); - } - if (player.getAppearance().getHeadIcon() == 7) { - player.getAppearance().setHeadIcon(-1); - player.getAppearance().sync(); - } - } - } - - /** - * Method used to get the icon value. - * @return the icon. - */ - public int getIcon(final Player player, final PrayerType type) { - List active = player.getPrayer().getActive(); - for (int i = 0; i < ICON_CACHE.length; i++) { - if (ICON_CACHE[i].length == 2 && type == ((PrayerType) ICON_CACHE[i][0])) { - return (int) ICON_CACHE[i][1]; - } else if (ICON_CACHE[i].length == 3 && type == ((PrayerType) ICON_CACHE[i][0])) { - if (active.contains(PROTECT_FROM_SUMMONING)) { - return (int) ICON_CACHE[i][2]; - } - return (int) ICON_CACHE[i][1]; - } else if (ICON_CACHE[i].length == 8 && type == ((PrayerType) ICON_CACHE[i][0])) { - for (int k = 2; k < ICON_CACHE[i].length; k++) { - if (active.contains(ICON_CACHE[i][k])) { - return (int) ICON_CACHE[i][k + 1]; - } - } - return (int) ICON_CACHE[i][1]; - } - } - return -1; - } - - /** - * Method used to check if theres an icon present. - * @param player the player. - * @return True if theres an icon present. - */ - public boolean hasIcon(final Player player) { - int count = 0; - for (PrayerType type : player.getPrayer().getActive()) { - if (getIcon(player, type) != -1) { - count++; - } - } - return count != 0; - } - - /** - * Method used to return the type by the button. - * @param button the button. - * @return the type. - */ - public static PrayerType get(int button) { - for (PrayerType type : PrayerType.values()) { - if (type.getButton() == button) { - return type; - } - } - return null; - } - - /** - * Method used to get the melee types. - * @return the types. - */ - public static PrayerType[] getMeleeTypes() { - return getByBonus(Skills.ATTACK, Skills.STRENGTH); - } - - /** - * Method used to get the rage types. - * @return the types. - */ - public static PrayerType[] getRangeTypes() { - return getByBonus(Skills.RANGE); - } - - /** - * Method used to get the magic types. - * @return the types. - */ - public static PrayerType[] getMagicTypes() { - return getByBonus(Skills.MAGIC); - } - - /** - * Method used to get the prayer types by the bonuses. - * @param ids the ids. - * @return the type. - */ - public static PrayerType[] getByBonus(int... ids) { - PrayerType[] types = new PrayerType[values().length]; - int count = 0; - for (PrayerType type : values()) { - for (SkillBonus b : type.getBonuses()) { - for (int i : ids) { - if (i == b.getSkillId()) { - types[count] = type; - count++; - } - } - } - } - return types; - } - - /** - * Gets the secondRestriction. - * @return The secondRestriction. - */ - public PrayerCategory getSecondRestriction() { - return secondRestriction; - } - - /** - * Gets the sound. - * @return The sound. - */ - public Audio getSound() { - return sound; - } - - public int getDefenceReq() { - return defenceReq; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/quest/Quest.java b/Server/src/main/java/core/game/node/entity/player/link/quest/Quest.java deleted file mode 100644 index b56fae868..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/quest/Quest.java +++ /dev/null @@ -1,288 +0,0 @@ -package core.game.node.entity.player.link.quest; - -import core.game.component.Component; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; -import core.plugin.PluginManifest; -import core.plugin.PluginType; - -import java.util.Arrays; - -/** - * A skeleton plugin for a quest. - * @author Vexia - * - */ -@PluginManifest(type = PluginType.QUEST) -public abstract class Quest implements Plugin { - - /** - * Represents the red string. - */ - public static final String RED = ""; - - /** - * Represents the blue string. - */ - public static final String BLUE = ""; - - /** - * Represents the black string. - */ - public static final String BLACK = ""; - - /** - * The constant representing the journal component. - */ - public static final int JOURNAL_COMPONENT = 275; - - /** - * The constant representing the quest reward component. - */ - public static final int REWARD_COMPONENT = 277; - - /** - * The name of the quest. - */ - private final String name; - - /** - * The index id of the quest. - */ - private final int index; - - /** - * The button id of the quest. - */ - private final int buttonId; - - /** - * The rewarded quest points. - */ - private final int questPoints; - - /** - * The config values based on stage. - */ - 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. - */ - public Quest(String name, int index, int buttonId, int questPoints, int...configs) { - this.name = name; - this.index = index; - this.buttonId = buttonId; - this.questPoints = questPoints; - this.configs = configs; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - @Override - public abstract Quest newInstance(Object object); - - /** - * Starts this quest. - * @param player The player. - */ - public void start(Player player) { - player.getQuestRepository().setStage(this, 10); - player.getQuestRepository().syncronizeTab(player); - } - - /** - * Draws the text on the quest journal. - * @param player The player. - * @param stage The stage to draw. - */ - public void drawJournal(Player player, int stage) { - for (int i = 0; i < 311; i++) { - player.getPacketDispatch().sendString("" , JOURNAL_COMPONENT, i); - } - player.getPacketDispatch().sendString("" + getName() + "", JOURNAL_COMPONENT, 2); - - } - - /** - * Finishes the quest. - * @param player The player. - */ - public void finish(Player player) { - for (int i = 0; i < 18; i++) { - if (i == 9 || i == 3 || i == 6) { - continue; - } - player.getPacketDispatch().sendString("", 277, i); - } - 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.getPacketDispatch().sendString("" + player.getQuestRepository().getPoints() + "", 277, 7); - player.getPacketDispatch().sendString("You have completed the " + getName() + " Quest!", 277, 4); - player.getPacketDispatch().sendMessage("Congratulations! Quest complete!"); - player.getPacketDispatch().sendTempMusic(152); - } - - /** - * Draws a line on the journal component. - * @param player The player. - * @param message The message. - * @param line The line. - */ - public void line(Player player, String message, int line) { - String send = BLUE + "" + message.replace("", "

").replace("", BLUE).replace("", RED); - if (send.contains("

") || send.contains("")) { - String[] lines = send.split(send.contains("

") ? "

" : ""); - for (int i = 0; i < lines.length; i++) { - line(player, lines[i].replace("

", "").replace("", ""), line, false); - line++; - } - } else { - send = send.replace("!!",RED).replace("??",BLUE).replace("---",BLACK + "").replace("/--", BLUE); - line(player, send, line, false); - } - } - - /** - * Draws a line on the quest journal component. - * @param player The player. - * @param message The message. - * @param line The line number. - * @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; - if(!crossed){ - send = BLUE + "" + message.replace("", "

").replace("", BLUE).replace("", RED); - send = send.replace("!!",RED).replace("??",BLUE); - } else { - send = BLUE + "" + message.replace("??","").replace("!!",""); - } - player.getPacketDispatch().sendString(crossed ? "" + send + "" : send, JOURNAL_COMPONENT, line); - } - - /** - * Draws text on the quest reward component. - * @param player The player. - * @param string The string to draw. - * @param line The line number to draw on. - */ - public void drawReward(Player player, final String string, final int line) { - player.getPacketDispatch().sendString(string, REWARD_COMPONENT, line); - } - - /** - * Sets the player instanced stage. - * @param player The player. - * @param stage The stage to set. - */ - public void setStage(Player player, int stage) { - player.getQuestRepository().setStage(this, stage); - } - - /** - * Gets the config id based on the stage. - * @param player The player. - * @param stage The stage. - * @return The config data. - */ - 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 + "!"); - } - return new int[] {configs[0], stage == 0 ? configs[1] : stage >= 100 ? configs[3] : configs[2]}; - } - - /** - * Checks if the quest is in progress. - * @param player The player. - * @return {@code True} if so. - */ - public boolean isStarted(Player player) { - return getStage(player) > 0 && getStage(player) < 100; - } - - /** - * Checks if the quest is completed. - * @param player The player. - * @return {@code True} if so. - */ - public boolean isCompleted(Player player) { - return getStage(player) >= 100; - } - - /** - * Gets the player instanced stage of this quest. - * @param player The player. - * @return The stage. - */ - public int getStage(Player player) { - return player.getQuestRepository().getStage(this); - } - - /** - * Checks the requirements for the quest. - * @param player The player - * @return {@code True} if so. - */ - public boolean hasRequirements(Player player) { - return true; - } - - /** - * Gets the name. - * @return the name. - */ - public String getName() { - return name; - } - - /** - * Gets the index. - * @return the index. - */ - public int getIndex() { - return index; - } - - /** - * Gets the buttonId. - * @return the buttonId. - */ - public int getButtonId() { - return buttonId; - } - - /** - * Gets the questPoints. - * @return the questPoints. - */ - public int getQuestPoints() { - 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) + "]"; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/quest/QuestRepository.java b/Server/src/main/java/core/game/node/entity/player/link/quest/QuestRepository.java deleted file mode 100644 index 2b3918321..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/quest/QuestRepository.java +++ /dev/null @@ -1,280 +0,0 @@ -package core.game.node.entity.player.link.quest; - -import core.game.node.entity.player.Player; - -import rs09.game.system.SystemLogger; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Manages the systems/players quest repository. - * - * @author Vexia - */ -public final class QuestRepository { - - /** - * The static mapping of instanced quests. - */ - private static final Map QUESTS = new HashMap<>(); - - /** - * The mapping of quest indexes with related stages. - */ - private final Map quests = new HashMap<>(); - - /** - * The player instance for this manager. - */ - private final Player player; - - /** - * The current syncronized accumulated quest points. - */ - private int points; - - /** - * Constructs a new {@code QuestRepository} {@code Object}. - * - * @param player the player. - */ - public QuestRepository(final Player player) { - this.player = player; - for (Quest quest : QUESTS.values()) { - quests.put(quest.getIndex(), 0); - } - } - - public void parse(JSONObject questData){ - points = Integer.parseInt( questData.get("points").toString()); - JSONArray questArray = (JSONArray) questData.get("questStages"); - questArray.forEach(quest -> { - JSONObject q = (JSONObject) quest; - quests.put(Integer.parseInt( q.get("questId").toString()),Integer.parseInt(q.get("questStage").toString())); - }); - syncPoints(); - } - - /** - * Synchronizes the quest tab. - * - * @param player The player. - */ - public void syncronizeTab(Player player) { - player.getConfigManager().set(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]); - 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); - } - } - } - - /** - * Sets the stage of a quest. - * - * @param quest The quest. - * @param stage The stage. - */ - public void setStage(Quest quest, int stage) { - quests.put(quest.getIndex(), stage); - } - - /** - * Increments the obtained points by the value. - * - * @param value the value. - */ - public void incrementPoints(int value) { - points += value; - } - - /** - * Decrease the points by the value. - * - * @param value the value. - */ - public void dockPoints(int value) { points -= value; } - - /** - * Syncronizes the quest points. - */ - public void syncPoints() { - int points = 0; - for (Quest quest : QUESTS.values()) { - if (getStage(quest) >= 100) { - points += quest.getQuestPoints(); - } - } - this.points = points; - } - - /** - * Gets the available quest points. - * - * @return The availble quest points. - */ - public int getAvailablePoints() { - int points = 0; - for (Quest quest : QUESTS.values()) { - points += quest.getQuestPoints(); - } - return points; - } - - /** - * Gets the quest for the button id. - * - * @param buttonId The button id. - * @return The quest. - */ - public Quest forButtonId(int buttonId) { - for (Quest q : QUESTS.values()) { - if (q.getButtonId() == buttonId) { - return q; - } - } - return null; - } - - /** - * Gets the quest for the index. - * - * @param index The index. - * @return The quest. - */ - public Quest forIndex(int index) { - for (Quest q : QUESTS.values()) { - if (q.getIndex() == index) { - return q; - } - } - return null; - } - - /** - * Checks if all quests are completed. - * - * @return {@code True} if so. - */ - public boolean hasCompletedAll() { - return getPoints() >= getAvailablePoints(); - } - - /** - * Checks if the quest is complete. - * - * @param name The name of the quest. - * @return {@code True} if so. - */ - public boolean isComplete(String name) { - Quest quest = getQuest(name); - if (quest == null) { - SystemLogger.logErr("Error can't check if quest is complete for " + name); - return false; - } - return quest.getStage(player) >= 100; - } - - /** - * Checks if the quest has at least started. - * - * @param name The name of the quest. - * @return {@code True} if so. - */ - public boolean hasStarted(String name) { - Quest quest = getQuest(name); - if (quest == null) { - SystemLogger.logErr("Error can't check if quest is complete for " + name); - return false; - } - return quest.getStage(player) > 0; - } - - - /** - * Gets the stage of quest by name. - * - * @param name The name of the quest. - * @return The stage. - */ - public int getStage(String name) { - return getStage(QUESTS.get(name)); - } - - /** - * Gets the stage of a quest. - * - * @param quest The quest. - * @return The stage. - */ - public int getStage(Quest quest) { - return quests.get(quest.getIndex()); - } - - /** - * Gets the quest by name. - * - * @param name The name. - * @return The quest. - */ - public Quest getQuest(String name) { - return QUESTS.get(name); - } - - /** - * Gets the points. - * - * @return the points. - */ - public int getPoints() { - return points; - } - - /** - * Gets the player. - * - * @return The player. - */ - public Player getPlayer() { - return player; - } - - /** - * Registers the quest to the repository. - * - * @param quest The quest. - */ - public static void register(Quest quest) { - QUESTS.put(quest.getName(), quest); - } - - /** - * Gets the quest repository. - * - * @return the quests. - */ - public static Map getQuests() { - return QUESTS; - } - - public Map getQuestList() {return quests;} - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.java b/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.java deleted file mode 100644 index d6a6de116..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/request/RequestManager.java +++ /dev/null @@ -1,136 +0,0 @@ -package core.game.node.entity.player.link.request; - -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; - -/** - * Represents a managing class for requests of a player. - * @author Vexia - */ -public final class RequestManager { - - /** - * Represents the player instance. - */ - private final Player player; - - /** - * Represents the target being requested. - */ - private Player target; - - /** - * Constructs a new {@code RequestManager} {@code Object}. - * @param player the player. - */ - public RequestManager(final Player player) { - this.player = player; - } - - /** - * Method used to send a request type to a target. - * @param target the target. - * @param type the type of request. - * @return {@code True} if successful in requesting. - */ - public boolean request(final Player target, final RequestType type) { - if (!canRequest(type, target)) { - return false; - } - if (acceptExisting(target, type)) { - return true; - } - player.getPacketDispatch().sendMessage(type.getMessage()); - target.getPacketDispatch().sendMessage(type.getRequestMessage(player)); - player.setAttribute("lastRequest", type); - this.target = target; - return true; - } - - /** - * Method used to check if a player can continue with a request. - * @param type the type. - * @param target the target. - * @return {@code True} if they can request. - */ - private boolean canRequest(RequestType type, Player target) { - if (target == player) { - return false; - } - if (!TutorialSession.getExtension(player).finished() || !TutorialSession.getExtension(target).finished()) { - return false; - } - if (!target.getLocation().withinDistance(player.getLocation(), 15)) { - player.getPacketDispatch().sendMessage("Unable to find " + target.getUsername() + "."); - return false; - } - if (!target.isActive() || target.getInterfaceManager().isOpened()) { - player.getPacketDispatch().sendMessage("Other player is busy at the moment."); - return false; - } - if (target.getAttribute("busy", 0) > GameWorld.getTicks() || player.getAttribute("busy", 0) > GameWorld.getTicks()) { - player.getPacketDispatch().sendMessage("Other player is busy at the moment."); - return false; - } - if (!player.getZoneMonitor().canRequest(type, target)) { - return false; - } - return type.canRequest(player, target); - } - - /** - * Method used to check if we can accept an existing request. - * @param target the target. - * @param type the type. - * @return {@code True} if so. - */ - public boolean acceptExisting(final Player target, final RequestType type) { - final RequestType lastType = target.getAttribute("lastRequest", null); - if (lastType == type && player == target.getRequestManager().getTarget()) { - close(player); - clear(); - target.getRequestManager().clear(); - player.setAttribute("busy", GameWorld.getTicks() + 2); - target.setAttribute("busy", GameWorld.getTicks() + 2); - type.getModule().open(player, target); - return true; - } - close(player); - return false; - } - - /** - * Closes the components for the player. - * @param player the player. - */ - private void close(Player player) { - player.getDialogueInterpreter().close(); - player.getInterfaceManager().close(); - player.getInterfaceManager().closeChatbox(); - } - - /** - * Method used to clear the cached target. - */ - public void clear() { - this.target = null; - } - - /** - * Gets the player. - * @return the player. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the target. - * @return the target. - */ - public Player getTarget() { - return target; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.java b/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.java deleted file mode 100644 index 6bf96e385..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/request/RequestModule.java +++ /dev/null @@ -1,18 +0,0 @@ -package core.game.node.entity.player.link.request; - -import core.game.node.entity.player.Player; - -/** - * Represents the module used for all request types. - * @author 'Vexia - */ -public interface RequestModule { - - /** - * Method invoked when the targeting player accepts a request. - * @param player the player. - * @param target the target. - */ - public void open(final Player player, final Player target); - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.java b/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.java deleted file mode 100644 index 24856d956..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/request/RequestType.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.node.entity.player.link.request; - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.request.assist.AssistSession; -import core.game.node.entity.player.link.request.trade.TradeModule; - -/** - * Represents a request type. - * @author 'Vexia - * @author Emperor - */ -public class RequestType { - - /** - * The trade request type. - */ - public static final RequestType TRADE = new RequestType("Sending a trade offer...", ":tradereq:", new TradeModule()); - - /** - * The assist request type. - */ - public static final RequestType ASSIST = new RequestType("Sending assistance request...", ":assistreq:", new AssistSession()); - - /** - * Represents the message to send for the player when requesting. - */ - private final String message; - - /** - * Represents the requesting message type. - */ - private final String requestMessage; - - /** - * Represents the module used for this type of requesting. - */ - private final RequestModule module; - - /** - * Constructs a new {@code RequestManager {@code Object}. - * @param message the message. - * @param requestMessage the requesting message. - */ - public RequestType(String message, String requestMessage, RequestModule module) { - this.message = message; - this.requestMessage = requestMessage; - this.module = module; - } - - /** - * Checks if the request can be made. - * @param player The player. - * @param target The target. - * @return {@code True} if so. - */ - public boolean canRequest(Player player, Player target) { - return true; - } - - /** - * Gets the requesting message formated with the targets name. - * @param target the target. - * @return the message to send. - */ - public String getRequestMessage(Player target) { - return target.getUsername() + getRequestMessage(); - } - - /** - * Method used to get the request type by the option being handled. - * @param option the option being handled. - * @return the request type in correlation. - */ - public static RequestType forOption(String option) { - return option.equals("trade with") ? TRADE : ASSIST; - } - - /** - * Gets the message. - * @return The message. - */ - public String getMessage() { - return message; - } - - /** - * Gets the requestMessage. - * @return The requestMessage. - */ - public String getRequestMessage() { - return requestMessage; - } - - /** - * Gets the request module. - * @return the module. - */ - public RequestModule getModule() { - return module; - } -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.java b/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.java deleted file mode 100644 index f55e121e6..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/request/trade/TradeModule.java +++ /dev/null @@ -1,542 +0,0 @@ -package core.game.node.entity.player.link.request.trade; - -import core.game.component.Component; -import core.game.container.Container; -import core.game.container.ContainerType; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.request.RequestModule; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.system.monitor.PlayerMonitor; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; - -/** - * Represents the module use to handle a request between trading of two players. - * @author Vexia - * - */ -public final class TradeModule implements RequestModule { - - /** - * Represents the overlay interface component. - */ - public static final Component OVERLAY_INTERFACE = new Component(336); - - /** - * Represents the main interface component. - */ - public static final Component MAIN_INTERFACE = new Component(335).setCloseEvent(new TradeCloseEvent()); - - /** - * Represents the accept interface component. - */ - public static final Component ACCEPT_INTERFACE = new Component(334).setCloseEvent(new TradeCloseEvent()); - - /** - * The inventory params for the runscript. - */ - public static final Object[] INVENTORY_PARAMS = new Object[] { "", "", "", "Lend", "Offer-X", "Offer-All", "Offer-10", "Offer-5", "Offer", -1, 0, 7, 4, 93, (336 << 16) }; - - /** - * The trade params for the run script. - */ - public static final Object[] TRADE_PARAMS = new Object[] { "", "", "", "", "Remove-X", "Remove-All", "Remove-10", "Remove-5", "Remove", -1, 0, 7, 4, 90, (335 << 16) | 30 }; - - /** - * The partener params for the run script. - */ - public static final Object[] PARTENER_PARAMS = new Object[] { "", "", "", "", "", "", "", "", "", -1, 0, 7, 4, 91, (335 << 16) | 32 }; - - /** - * Represents the childs that should be hidden. - */ - public static final int[] HIDDEN_CHILDS = new int[] { 42, 43, 44, 42, 44, 40, 41 }; - - /** - * If the container has been retained. - */ - private boolean retained; - - /** - * Represents the player instance. - */ - private Player player; - - /** - * Represents the target instance. - */ - private Player target; - - /** - * Represents the container of this session. - */ - private TradeContainer container; - - /** - * Represents if this session has accepted. - */ - private boolean accepted; - - /** - * Represents if the trade is modified. - */ - private boolean modified = false; - - /** - * Represents the stage of the trade(0=started, 1=second accept) - */ - private int stage; - - /** - * Constructs a new {@code TradeModule} {@code Object}. - * @param player the player. - * @param target the target. - */ - public TradeModule(final Player player, final Player target) { - this.player = player; - this.target = target; - this.container = new TradeContainer(player); - } - - /** - * Constructs a new {@code TradeModule} {@code Object}. - */ - public TradeModule() { - /** - * empty. - */ - } - - @Override - public void open(Player player, Player target) { - extend(player, target); - if (getExtension(target) == null || getExtension(player) == null) { - return; - } - getExtension(player).openInterface(getInterface(stage)).display(stage); - if (getExtension(target) == null || getExtension(player) == null) { - return; - } - getExtension(target).openInterface(getInterface(stage)).display(stage); - player.getDialogueInterpreter().close(); - target.getDialogueInterpreter().close(); - } - - /** - * Method used to update the trade interfaces. - */ - public void update() { - display(stage); - getExtension(target).display(stage); - } - - /** - * Method used to extend the module for each participant. - * @param player the player. - * @param target the target. - */ - public static void extend(final Player player, final Player target) { - player.addExtension(TradeModule.class, new TradeModule(player, target)); - target.addExtension(TradeModule.class, new TradeModule(target, player)); - } - - /** - * Method used to get the extension from the player. - * @param player the player. - * @return the module instance. - */ - public static TradeModule getExtension(final Player player) { - return player.getExtension(TradeModule.class); - } - - /** - * Method used to open an interface at a stage. - * @param component the component. - * @return the module instance for chaining. - */ - private TradeModule openInterface(final Component component) { - return component == MAIN_INTERFACE ? openMain() : openSecond(); - } - - /** - * Gets the accepting message to display. - * @return the message. - */ - private String getAcceptMessage() { - boolean otherAccept = TradeModule.getExtension(target).isAccepted(); - return !otherAccept && !accepted ? "" : otherAccept ? "Other player has accepted." : "Waiting for other player..."; - } - - /** - * Method used to display the interface display depending on stage. - * @param stage the stage. - * @return module the module instance for chaining. - */ - private TradeModule display(int stage) { - switch (stage) { - case 0: - for (int i : HIDDEN_CHILDS) { - player.getPacketDispatch().sendString("", MAIN_INTERFACE.getId(), i); - } - player.getPacketDispatch().sendString("Trading With: " + target.getUsername(), 335, 15); - player.getPacketDispatch().sendString(target.getUsername() + " has " + (target.getInventory().freeSlots() == 0 ? "no" : (target.getInventory().freeSlots())) + " free inventory slots.", 335, 21); - player.getPacketDispatch().sendString(getAcceptMessage(), 335, 36); - break; - case 1: - player.getPacketDispatch().sendString("Trading with:
" + "" + target.getUsername().substring(0, 1).toUpperCase() + target.getUsername().substring(1), 334, 2); - final String acceptMessage = getAcceptMessage(); - if (acceptMessage != "") { - player.getPacketDispatch().sendString(acceptMessage, 334, 33); - } - player.getInterfaceManager().restoreTabs(); - break; - } - displayModified(stage); - container.update(true); - return this; - } - - /** - * Gets the interface for the current stage. - * @param stage the stage. - * @return the component. - */ - private Component getInterface(int stage) { - return stage == 0 ? MAIN_INTERFACE : ACCEPT_INTERFACE; - } - - /** - * Method used to display if the trade is modified. - * @param stage the sytage. - */ - private void displayModified(int stage) { - final boolean otherModified = TradeModule.getExtension(target).isModified(); - if (stage == 1) { - player.getPacketDispatch().sendInterfaceConfig(334, 45, !modified); - player.getPacketDispatch().sendInterfaceConfig(334, 46, !otherModified); - } else { - if (otherModified) { - player.getConfigManager().set(1043, 1); - } - if (modified) { - player.getConfigManager().set(1042, 1); - } - } - } - - /** - * Method used to decline this offer. - */ - public void decline() { - player.getInterfaceManager().close(); - target.getPacketDispatch().sendMessage("Other player has declined trade!"); - } - - /** - * Method used to check if the next stage can proceed. - */ - private void nextStage() { - if (accepted && TradeModule.getExtension(target).isAccepted()) { - if (stage == 0) { - if (!hasSpace()) { - return; - } - incrementStage(); - openInterface(getInterface(stage)); - TradeModule.getExtension(target).incrementStage(); - TradeModule.getExtension(target).openInterface(getInterface(stage)); - TradeModule.getExtension(target).setAccepted(false, false); - setAccepted(false, false); - } else { - giveContainers(this); - incrementStage(); - TradeModule.getExtension(target).incrementStage(); - TradeModule.getExtension(target).setAccepted(false, false); - setAccepted(false, false); - player.getInterfaceManager().close(); - return; - } - } - update(); - } - - /** - * Method used to open the main interface. - * @return the module instance for chaining. - */ - private TradeModule openMain() { - player.getInterfaceManager().closeDefaultTabs(); - player.getInterfaceManager().open(MAIN_INTERFACE); - player.getInterfaceManager().openSingleTab(OVERLAY_INTERFACE); - player.getInventory().refresh(); - player.getPacketDispatch().sendAccessMask(1278, 30, 335, 0, 27); - player.getPacketDispatch().sendAccessMask(1026, 32, 335, 0, 27); - player.getPacketDispatch().sendAccessMask(1278, 0, 336, 0, 27); - player.getPacketDispatch().sendAccessMask(2360446, 0, 335, 0, 27); - player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", INVENTORY_PARAMS); - player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", TRADE_PARAMS); - player.getPacketDispatch().sendRunScript(695, "IviiiIsssssssss", PARTENER_PARAMS); - return this; - } - - /** - * Method used to open the second interface. - * @return the module instance for chaining. - */ - private TradeModule openSecond() { - player.getInterfaceManager().open(ACCEPT_INTERFACE); - player.getInterfaceManager().closeSingleTab(); - displayOffer(container, 37); - displayOffer(TradeModule.getExtension(target).getContainer(), 41); - return this; - } - - /** - * Method used to set the value of accepted. - * @param accept the accept. - */ - public void setAccepted(final boolean accept, boolean update) { - this.accepted = accept; - if (update) { - nextStage(); - } - } - - /** - * Method used to alert the player the trade has been modified. - * @param slot the slot. - */ - public void alert(final int slot) { - player.getPacketDispatch().sendRunScript(143, "Iiii", new Object[] { slot, 7, 4, 21954593 }); - target.getPacketDispatch().sendRunScript(143, "Iiii", new Object[] { slot, 7, 4, 21954594 }); - } - - /** - * Method used to display an offer. - * @param container the container. - */ - private void displayOffer(final Container container, int child) { - boolean split = container.itemCount() > 14; - if (split) { - player.getPacketDispatch().sendInterfaceConfig(334, child + 1, false); - player.getPacketDispatch().sendInterfaceConfig(334, child + 2, false); - String[] messages = new String[] { getDisplayMessage(splitList(container.toArray(), 0, 14)), getDisplayMessage(splitList(container.toArray(), 14, container.toArray().length)) }; - player.getPacketDispatch().sendString(messages[0], 334, child + 1); - player.getPacketDispatch().sendString(messages[1], 334, child + 2); - } else { - player.getPacketDispatch().sendInterfaceConfig(334, child, false); - player.getPacketDispatch().sendString(getDisplayMessage(container.toArray()), 334, child); - } - } - - /** - * Method used to get the display message. - * @param items the items. - * @return the message. - */ - private String getDisplayMessage(Item[] items) { - String message = "Absolutely nothing!"; - if (items.length > 0) { - message = ""; - for (int i = 0; i < items.length; i++) { - if (items[i] == null) { - continue; - } - message = message + "" + items[i].getName(); - if (items[i].getAmount() > 1) { - message = message + " x "; - message = message + "" + getFormattedNumber(items[i].getAmount()) + "
"; - } else { - message = message + "
"; - } - } - } - return message; - } - - /** - * Method used to check if the players & targets have enough space. - * @return {@code True} if so. - */ - private boolean hasSpace() { - boolean hasSpace = true; - if (!player.getInventory().hasSpaceFor(TradeModule.getExtension(target).getContainer())) { - target.getPacketDispatch().sendMessage("Other player doesn't have enough space in their inventory for this trade."); - player.getPacketDispatch().sendMessage("You don't have enough inventory space for this."); - hasSpace = false; - } else if (!target.getInventory().hasSpaceFor(container)) { - player.getPacketDispatch().sendMessage("Other player doesn't have enough space in their inventory for this trade."); - target.getPacketDispatch().sendMessage("You don't have enough inventory space for this."); - hasSpace = false; - } - if (!hasSpace) { - setAccepted(false, true); - TradeModule.getExtension(target).setAccepted(false, true); - } - return hasSpace; - } - - /** - * Gets the formatted number. - * @param amount the amount. - * @return the formatted number. - */ - private String getFormattedNumber(int amount) { - return new DecimalFormat("#,###,##0").format(amount).toString(); - } - - /** - * Method used to give the containers to each participants. - * @param module the module. - */ - private void giveContainers(final TradeModule module) { - final Container pContainer = module.getContainer(); - final Container oContainer = TradeModule.getExtension(module.getTarget()).getContainer(); - addContainer(module.getPlayer(), module.getTarget(), oContainer); - addContainer(module.getTarget(), module.getPlayer(), pContainer); - module.getTarget().getPacketDispatch().sendMessage("Accepted trade."); - module.getPlayer().getPacketDispatch().sendMessage("Accepted trade."); - } - - /** - * Method used to add a container for a player. - * @param player the player. - * @param target the target. - * @param container the container. - */ - private void addContainer(final Player player, Player target, final Container container) { - Container c = new Container(container.itemCount(), ContainerType.ALWAYS_STACK); - c.addAll(container); - String log = "traded => " + target.getName() + " received => {"; - for (Item i : container.toArray()) { - if (i == null) { - continue; - } - if (i.getAmount() > player.getInventory().getMaximumAdd(i)) { - i.setAmount(player.getInventory().getMaximumAdd(i)); - } - if (!player.getInventory().add(i)) { - GroundItemManager.create(i, player); - } - } - for (Item i : c.toArray()) { - if (i == null) { - continue; - } - log += i.getName() + " x " + i.getAmount() + ","; - } - if (log.charAt(log.length() - 1) == ',') { - log = log.substring(0, log.length() - 1); - } - log += "}"; - player.getMonitor().log(log, PlayerMonitor.TRADE_LOG); - } - - /** - * Gets the split list as an array. - * @param items the items. - * @param min the min. - * @param max the max. - * @return the split item array. - */ - private Item[] splitList(Item[] items, int min, int max) { - List list = new ArrayList<>(20); - for (int i = min; i < max; i++) { - if (items[i] == null) { - continue; - } - list.add(items[i]); - } - Item[] array = new Item[list.size()]; - for (int i = 0; i < list.size(); i++) { - if (list.get(i) == null) { - continue; - } - array[i] = list.get(i); - } - return array; - } - - /** - * Method used to increment the stage. - */ - public void incrementStage() { - stage++; - } - - /** - * Gets the stage. - * @return the stage. - */ - public int getStage() { - return stage; - } - - /** - * Gets the accepted. - * @return The accepted. - */ - public boolean isAccepted() { - return accepted; - } - - /** - * Gets the trade container. - * @return the container. - */ - public TradeContainer getContainer() { - return container; - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the target. - * @return The target. - */ - public Player getTarget() { - return target; - } - - /** - * Gets the modified. - * @return The modified. - */ - public boolean isModified() { - return modified; - } - - /** - * Sets the modified. - * @param modified The modified to set. - */ - public void setModified(boolean modified) { - this.modified = modified; - } - - /** - * Gets the retained. - * @return The retained. - */ - public boolean isRetained() { - return retained; - } - - /** - * Sets the retained. - * @param retained The retained to set. - */ - public void setRetained(boolean retained) { - this.retained = retained; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/spawn/PKScoreBoard.java b/Server/src/main/java/core/game/node/entity/player/link/spawn/PKScoreBoard.java deleted file mode 100644 index 99f8ed69f..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/spawn/PKScoreBoard.java +++ /dev/null @@ -1,171 +0,0 @@ -package core.game.node.entity.player.link.spawn; - -import core.cache.AriosStore; -import core.cache.StoreFile; -import core.cache.misc.buffer.ByteBufferUtils; -import core.game.component.Component; -import core.game.node.entity.player.Player; -import core.tools.StringUtils; - -import java.nio.ByteBuffer; - -/** - * The score board. - * @author Emperor - * @author Vexia - * - */ -public final class PKScoreBoard { - - /** - * If it's been initialized. - */ - private static boolean initialized; - - /** - * The score board size. - */ - private static final int SIZE = 49; - - /** - * The names. - */ - private static String[] names = new String[SIZE]; - - /** - * The scores. - */ - private static int[] scores = new int[SIZE]; - - /** - * static block - */ - static { - for (int i = 0; i < SIZE; i++) { - names[i] = "Nobody yet"; - } - init(); - initialized = true; - } - - /** - * Initializes the score boards data. - */ - public static void init() { - StoreFile file = AriosStore.get("pk_scores"); - if (file == null) { //Indicates no cache exists yet. - return; - } - ByteBuffer buffer = file.data(); - for (int i = 0; i < SIZE; i++) { - scores[i] = buffer.getInt(); - names[i] = ByteBufferUtils.getString(buffer); - } - } - - /** - * Updates the store file for the scores. - */ - public static void update() { - ByteBuffer buffer = ByteBuffer.allocate(5000); - for (int i = 0; i < SIZE; i++) { - buffer.putInt(scores[i]); - ByteBufferUtils.putString(names[i], buffer); - } - buffer.flip(); - AriosStore.setArchive("pk_scores", buffer); - } - - /** - * Opens the score board. - * @param player The player. - */ - public static void open(Player player) { - if (!initialized) { - initialized = true; - init(); - } - int component = 632; - player.getPacketDispatch().sendString("PK Scoreboard", 632, 14); - player.getPacketDispatch().sendString("Top 50 (name - kills):", 632, 13); - player.getInterfaceManager().open(new Component(component)); - String msg = ""; - for (int i = 0; i < SIZE; i++) { - msg = names[i]; - if (!msg.equals("Nobody yet")) { - msg = StringUtils.formatDisplayName(msg) + " - " + scores[i]; - } - player.getPacketDispatch().sendString(msg, component, 16 + i); - } - } - - /** - * Checks if the ratings of the player is good enough for the score board. - * @param player The player. - */ - public static void check(Player player) { - if(player.isAdmin()) { - return; - } - if (!initialized) { - initialized = true; - init(); - } - int score = player.getSavedData().getSpawnData().getKills(); - int myScore = 0; - for (int i = 0; i < names.length; i++) { - if (names[i].equals(player.getName())) { - myScore = scores[i]; - break; - } - } - if (score == myScore) { - return; - } - for (int i = 0; i < SIZE; i++) { - if (score > scores[i]) { - insert(player, score, i); - update(); - break; - } - } - } - - /** - * Resets the board. - */ - public static void reset() { - scores = new int[SIZE]; - names = new String[SIZE]; - for (int i = 0; i < names.length; i++) { - names[i] = "Nobody yet"; - } - } - - /** - * Inserts the player in the score board. - * @param player The player. - * @param score The score. - * @param index The board index. - */ - private static void insert(Player player, int score, int index) { - if (scores[index] == score) { - return; - } - if (names[index].equals(player.getName())) { - scores[index] = score; - return; - } - for (int i = SIZE - 2; i >= index; i--) { - String name = names[i]; - if (name.equals(player.getName())) { - name = names[--i]; - } - scores[i + 1] = scores[i]; - names[i + 1] = name; - } - names[index] = player.getName(); - scores[index] = score; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/player/link/spawn/PKTitle.java b/Server/src/main/java/core/game/node/entity/player/link/spawn/PKTitle.java deleted file mode 100644 index b62c23ea4..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/spawn/PKTitle.java +++ /dev/null @@ -1,120 +0,0 @@ -package core.game.node.entity.player.link.spawn; - -import core.game.node.entity.player.Player; -import rs09.game.world.repository.Repository; -import core.tools.StringUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * A PK title received from a certain amount of kills. - * @author Vexia - * - */ -public enum PKTitle { - NOOB(0), - PEASANT(5), - SKIRMISHER(20), - KNIGHT(50), - WARRIOR(80), - BOUNTY_HUNTER(100), - HERO(150), - SKULLCRACKER(250), - DESTROYER(400), - DOMINATOR(600), - REAPER(800, "DF7401"), - ASSASSIN(1000, "21610B"), - MASTER(1500, "0431B4"), - GODLY(2000, "FF0000"); - - /** - * The kills needed for the title. - */ - private final int kills; - - /** - * The color for this title. - */ - private final String titleColor; - - /** - * Constructs a new {@Code PKTitle} {@Code Object} - * @param kills the points. - * @param titleColor the title color. - */ - private PKTitle(int kills, String titleColor) { - this.kills = kills; - this.titleColor = titleColor; - } - - /** - * Constructs a new {@Code PKTitle} {@Code Object} - * @param points the points. - */ - private PKTitle(int points) { - this(points, "874b93"); - } - - /** - * Checks the title for a player. - */ - public static void checkTitle(Player player) { - PKTitle title = null; - int kills = player.getSavedData().getSpawnData().getKills(); - for (PKTitle t : values()) { - if (kills >= t.getKills()) { - title = t; - } - } - if (title.ordinal() > player.getSavedData().getSpawnData().getTitle().ordinal() && player.getSavedData().getSpawnData().getKills() <= title.getKills()) { - player.getSavedData().getSpawnData().setTitle(title); - if (!title.getTitleColor().equals("874b93")) { - Repository.sendNews("News: " + player.getUsername() + " has just unlocked the title: " + title.getName() + "!"); - } - player.getAppearance().sync(); - } - } - - /** - * Gets the titles. - * @param player the player. - * @return the titles. - */ - public static PKTitle[] getTitles(Player player) { - int kills = player.getSavedData().getSpawnData().getKills(); - List titles = new ArrayList<>(20); - for (PKTitle t : values()) { - if (kills >= t.getKills()) { - titles.add(t); - } - } - return titles.toArray(new PKTitle[] {}); - } - - /** - * Gets the name of the title. - * @return the name. - */ - public String getName() { - return StringUtils.formatDisplayName(name().toLowerCase().replace("_", "")); - } - - /** - * Gets the points. - * @return the points - */ - public int getKills() { - return kills; - } - - /** - * Gets the titleColor. - * @return the titleColor - */ - public String getTitleColor() { - return titleColor; - } - - -} diff --git a/Server/src/main/java/core/game/node/entity/player/link/spawn/SpawnData.java b/Server/src/main/java/core/game/node/entity/player/link/spawn/SpawnData.java deleted file mode 100644 index 4f8d63b97..000000000 --- a/Server/src/main/java/core/game/node/entity/player/link/spawn/SpawnData.java +++ /dev/null @@ -1,642 +0,0 @@ -package core.game.node.entity.player.link.spawn; - -import core.game.component.Component; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; - -import core.game.node.entity.player.link.SavedData; -import core.game.node.entity.player.link.SpellBookManager; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import core.net.packet.PacketRepository; -import core.net.packet.context.ChildPositionContext; -import core.net.packet.out.RepositionChild; - -import java.nio.ByteBuffer; -import java.text.DecimalFormat; - -/** - * Handles the spawn data for the spawn world. - * @author Vexia - * - */ -public class SpawnData { - - /** - * The kill streak messages. - */ - private static final String[] KILLSTREAKS = new String[] {"rampage", "massacre", "frenzy", "annihilation", "decimation", "butchery", "extermination", "genocide", "carnage", "slaughter", "bloodshed", "assassination", "obliteration"}; - - /** - * The current title. - */ - private PKTitle title = PKTitle.NOOB; - - /** - * The tutorial stage for the spawn server. - */ - private int tutorialStage; - - /** - * The pk points. - */ - private int pkPoints; - - /** - * The amount of kills. - */ - private int kills; - - /** - * The ammount of deaths. - */ - private int deaths; - - /** - * The skill streak. - */ - private int killStreak; - - private int purchased; - - /** - * Constructs a new {@Code SpawnData} {@Code Object} - */ - public SpawnData() { - /** - * empty. - */ - } - - /** - * Draws the stats tab. - * @param player the player. - */ - public void drawStatsTab(Player player) { - if (GameWorld.isEconomyWorld()) { - return; - } - player.getPacketDispatch().sendInterfaceConfig(274, 3, true); - player.getPacketDispatch().sendInterfaceConfig(274, 8, true); - sendString(player, "Players online: " + Repository.getPlayers().size(), 5); - sendString(player, "Starter Packages Below...", 23); - sendString(player, "Information:", 12, null, -1); - sendString(player, "PK Points: " + player.getSavedData().getSpawnData().getPkPoints(), 13); - sendString(player, "Kills: " + getKills(), 14); - sendString(player, "Deaths: " + getDeaths(), 15); - sendString(player, "KDR: " + getKdr(), 16); - sendString(player, "Title: " + "" + getTitle().getName(), 17); - sendString(player, " ", 20); - sendString(player, "Starter Packages:", 31); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 274, 60, 10, 298));/* - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 274, 33, 10, 300)); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 274, 34, 11, 314));*/ - //Sets & consumables - - sendString(player, PKPackage.values()[0].getName(), 156); - sendString(player, PKPackage.values()[1].getName(), 32); - sendString(player, PKPackage.values()[2].getName(), 137); - sendString(player, PKPackage.values()[3].getName(), 146); - sendString(player, PKPackage.values()[4].getName(), 142); -// sendString(player, PKPackage.values()[5].getName(), 33); - /*sendString(player, PKPackage.values()[6].getName(), 34); - sendString(player, PKPackage.values()[7].getName(), 35); - sendString(player, PKPackage.values()[8].getName(), 36); - sendString(player, PKPackage.values()[9].getName(), 147); - sendString(player, PKPackage.values()[10].getName(), 37); - sendString(player, PKPackage.values()[11].getName(), 131); - sendString(player, PKPackage.values()[12].getName(), 38);*/ - - } - - /** - * Sends the string. - * @param player the player. - * @param string the string. - * @param child the child. - * @param color the color. - */ - private void sendString(Player player, String string, int child, String color, int purchased) { - player.getPacketDispatch().sendInterfaceConfig(274, child, false); - player.getPacketDispatch().sendString((color != null && purchased == -1 ? color : "") + string, 274, child); - } - - /** - * Sends the string. - * @param player the player. - * @param string the string. - * @param child the child. - */ - private void sendString(Player player, String string, int child, int purchased) { - sendString(player, string, child, "", purchased); - } - - /** - * Sends the string. - * @param player the player. - * @param string the string. - * @param child the child. - */ - private void sendString(Player player, String string, int child) { - sendString(player, string, child, "", -1); - } - - /** - * Shouts spawn info of the player. - * @param p the player. - * @param button the button. - */ - public void handleButton(Player p, int button) { - if (GameWorld.isEconomyWorld()) { - return; - } - drawStatsTab(p); - switch(button) { - case -1: - break; - case 16: - p.sendChat("My KDR is: " + p.getSavedData().getSpawnData().getKdr() + "!"); - break; - case 14: - p.sendChat("I have killed " + p.getSavedData().getSpawnData().getKills() + " player" + (p.getSavedData().getSpawnData().getKills() != 1 ? "s" : "") + "!"); - break; - case 15: - p.sendChat("I have been killed " + p.getSavedData().getSpawnData().getDeaths() + " time" + (p.getSavedData().getSpawnData().getDeaths() != 1 ? "s" : "") + "!"); - break; - case 13: - p.sendChat("PK Points: " + p.getSavedData().getSpawnData().getPkPoints() + "!"); - break; - case 17: - PKTitle[] titles = PKTitle.getTitles(p); - PKTitle t; - if (title.ordinal() >= titles.length-1) { - t = titles[0]; - } else { - t = titles[title.ordinal() + 1]; - } - this.title = t; - p.getAppearance().sync(); - drawStatsTab(p); - break; - /*sendString(player, PKPackage.values()[0].getName(), 156); - sendString(player, PKPackage.values()[1].getName(), 32); - sendString(player, PKPackage.values()[2].getName(), 137); - sendString(player, PKPackage.values()[3].getName(), 146); - sendString(player, PKPackage.values()[4].getName(), 142); - sendString(player, PKPackage.values()[5].getName(), 33); - sendString(player, PKPackage.values()[6].getName(), 34); - sendString(player, PKPackage.values()[7].getName(), 35); - sendString(player, PKPackage.values()[8].getName(), 36); - sendString(player, PKPackage.values()[9].getName(), 147); - sendString(player, PKPackage.values()[10].getName(), 37); - sendString(player, PKPackage.values()[11].getName(), 131); - sendString(player, PKPackage.values()[12].getName(), 38);*/ - case 156: - Player player = p; - if (player.canSpawn()) { - return; - } - p.getSkills().setLevel(Skills.ATTACK, 99); - p.getSkills().setStaticLevel(Skills.ATTACK, 99); - p.getSkills().setLevel(Skills.STRENGTH, 99); - p.getSkills().setStaticLevel(Skills.STRENGTH, 99); - p.getSkills().setLevel(Skills.DEFENCE, 99); - p.getSkills().setStaticLevel(Skills.DEFENCE, 99); - p.getSkills().setLevel(Skills.HITPOINTS, 99); - p.getSkills().setStaticLevel(Skills.HITPOINTS, 99); - p.getSkills().setLevel(Skills.RANGE, 99); - p.getSkills().setStaticLevel(Skills.RANGE, 99); - p.getSkills().setLevel(Skills.MAGIC, 99); - p.getSkills().setStaticLevel(Skills.MAGIC, 99); - p.getSkills().setLevel(Skills.PRAYER, 99); - p.getSkills().setStaticLevel(Skills.PRAYER, 99); - loadGear(p, 0); - break; - case 32: - player = p; - if (player.canSpawn()) { - return; - } - p.getSkills().setLevel(Skills.ATTACK, 50); - p.getSkills().setStaticLevel(Skills.ATTACK, 50); - p.getSkills().setLevel(Skills.STRENGTH, 99); - p.getSkills().setStaticLevel(Skills.STRENGTH, 99); - p.getSkills().setLevel(Skills.DEFENCE, 45); - p.getSkills().setStaticLevel(Skills.DEFENCE, 45); - p.getSkills().setLevel(Skills.HITPOINTS, 99); - p.getSkills().setStaticLevel(Skills.HITPOINTS, 99); - p.getSkills().setLevel(Skills.RANGE, 99); - p.getSkills().setStaticLevel(Skills.RANGE, 99); - p.getSkills().setLevel(Skills.MAGIC, 94); - p.getSkills().setStaticLevel(Skills.MAGIC, 94); - p.getSkills().setLevel(Skills.PRAYER, 52); - p.getSkills().setStaticLevel(Skills.PRAYER, 52); - loadGear(p, 1); - break; - case 137: - player = p; - if (player.canSpawn()) { - return; - } - p.getSkills().setLevel(Skills.ATTACK, 90); - p.getSkills().setStaticLevel(Skills.ATTACK, 90); - p.getSkills().setLevel(Skills.STRENGTH, 99); - p.getSkills().setStaticLevel(Skills.STRENGTH, 99); - p.getSkills().setLevel(Skills.DEFENCE, 80); - p.getSkills().setStaticLevel(Skills.DEFENCE, 80); - p.getSkills().setLevel(Skills.HITPOINTS, 99); - p.getSkills().setStaticLevel(Skills.HITPOINTS, 99); - p.getSkills().setLevel(Skills.RANGE, 99); - p.getSkills().setStaticLevel(Skills.RANGE, 99); - p.getSkills().setLevel(Skills.MAGIC, 99); - p.getSkills().setStaticLevel(Skills.MAGIC, 99); - p.getSkills().setLevel(Skills.PRAYER, 80); - p.getSkills().setStaticLevel(Skills.PRAYER, 80); - loadGear(p, 2); - break; - case 146: - player = p; - if (player.canSpawn()) { - return; - } - p.getSkills().setLevel(Skills.ATTACK, 80); - p.getSkills().setStaticLevel(Skills.ATTACK, 80); - p.getSkills().setLevel(Skills.STRENGTH, 99); - p.getSkills().setStaticLevel(Skills.STRENGTH, 99); - p.getSkills().setLevel(Skills.DEFENCE, 1); - p.getSkills().setStaticLevel(Skills.DEFENCE, 1); - p.getSkills().setLevel(Skills.HITPOINTS, 99); - p.getSkills().setStaticLevel(Skills.HITPOINTS, 99); - p.getSkills().setLevel(Skills.RANGE, 99); - p.getSkills().setStaticLevel(Skills.RANGE, 99); - p.getSkills().setLevel(Skills.MAGIC, 99); - p.getSkills().setStaticLevel(Skills.MAGIC, 99); - p.getSkills().setLevel(Skills.PRAYER, 52); - p.getSkills().setStaticLevel(Skills.PRAYER, 52); - loadGear(p, 3); - break; - case 142: - player = p; - if (player.canSpawn()) { - return; - } - p.getSkills().setLevel(Skills.ATTACK, 60); - p.getSkills().setStaticLevel(Skills.ATTACK, 60); - p.getSkills().setLevel(Skills.STRENGTH, 92); - p.getSkills().setStaticLevel(Skills.STRENGTH, 92); - p.getSkills().setLevel(Skills.DEFENCE, 45); - p.getSkills().setStaticLevel(Skills.DEFENCE, 45); - p.getSkills().setLevel(Skills.HITPOINTS, 91); - p.getSkills().setStaticLevel(Skills.HITPOINTS, 91); - p.getSkills().setLevel(Skills.RANGE, 99); - p.getSkills().setStaticLevel(Skills.RANGE, 99); - p.getSkills().setLevel(Skills.MAGIC, 99); - p.getSkills().setStaticLevel(Skills.MAGIC, 99); - p.getSkills().setLevel(Skills.PRAYER, 52); - p.getSkills().setStaticLevel(Skills.PRAYER, 52); - loadGear(p, 4); - break; - case 33: - player = p; - if (player.canSpawn()) { - return; - } - loadGear(p, 5); - break; - case 34: - player = p; - if (player.canSpawn()) { - return; - } - loadGear(p, 6); - break; - case 35: - player = p; - if (player.canSpawn()) { - return; - } - loadGear(p, 7); - break; - case 36: - player = p; - if (player.canSpawn()) { - return; - } - loadGear(p, 8); - break; - case 147: - player = p; - if (player.canSpawn()) { - return; - } - loadGear(p, 9); - break; - case 37: - player = p; - if (player.canSpawn()) { - return; - } - loadGear(p, 10); - break; - case 131: - player = p; - if (player.canSpawn()) { - return; - } - loadGear(p, 11); - break; - case 38: - player = p; - if (player.canSpawn()) { - return; - } - loadGear(p, 12); - break; - - } - } - - public void loadGear(Player p, int index) { - PKPackage pkPackage = PKPackage.values()[index]; - if (pkPackage != null) { - if (pkPackage.getType() == 0 && (!p.getBank().hasSpaceFor(p.getEquipment()) || !p.getBank().hasSpaceFor(p.getInventory()))) { - p.sendMessage("You don't have enough bank space to do that."); - return; - } - if (pkPackage == pkPackage.PURE) { - p.getSkills().setLevel(Skills.DEFENCE, 1); - p.getSkills().setStaticLevel(Skills.DEFENCE, 1); - p.getSkills().setLevel(Skills.PRAYER, 52); - p.getSkills().setStaticLevel(Skills.PRAYER, 52); - } /*else if (pkPackage.getType() == 0) { - for (int i = 0; i < Skills.SKILL_NAME.length; i++) { - p.getSkills().setLevel(i, 99); - p.getSkills().setStaticLevel(i, 99); - } - }*/ - if (pkPackage.getType() == 0/* && purchased == -1*/) { - p.getBank().addAll(p.getEquipment()); - p.getBank().addAll(p.getInventory()); - p.getEquipment().clear(); - p.getInventory().clear(); - /*for (Item item : pkPackage.getItems()) { - if ((item.getDefinition().hasAction("wear") || item.getDefinition().hasAction("wield")) && p.getEquipment().get(item.getDefinition().getConfiguration(ItemConfigParser.EQUIP_SLOT, -1)) == null) { - p.getEquipment().add(item, true, false); - } else { - p.getInventory().add(item); - } - } - } else {*/ - p.getInventory().add(pkPackage.getItems()); - purchased = PKPackage.values()[index].ordinal(); - } - if (pkPackage.getSpellBook() != -1 && purchased == -1) { - p.getSpellBookManager().setSpellBook(SpellBookManager.SpellBook.values()[pkPackage.getSpellBook()]); - p.getSpellBookManager().update(p); - } - if (pkPackage == pkPackage.VENGEANCE_RUNE || pkPackage == pkPackage.BARRAGE_RUNE || pkPackage == pkPackage.ENTANGE_RUNE) { - p.getSpellBookManager().setSpellBook(SpellBookManager.SpellBook.LUNAR); - p.getSpellBookManager().update(p); - } - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - if (purchased != -1) { - p.sendMessage("You have already purchased your one set, please use the shops"); - p.sendMessage("for any gear needed."); - } else { - p.sendMessage("You load the " + pkPackage.getName() + " package, your items have been banked!"); - } - p.getInterfaceManager().openTab(3, new Component(149)); // inventory - } - } - - /** - * Called when the player killer has killed another player. - * @param killer - * @param killed - */ - public void onDeath(Player killer, Player killed) { - if (GameWorld.isEconomyWorld()) { - return; - } - if (killer.isArtificial() || killed.isArtificial() || killer.getDetails().getInfo().getIp().equals(killed.getDetails().getInfo().getIp()) || killed.getDetails().getInfo().getMac().equals(killer.getDetails().getInfo().getMac())) { - killer.sendMessage("You can't kill someone from your own computer address."); - return; - } - SpawnData killedInfo = killed.getSavedData().getSpawnData(); - int increment = getStreakPoints(killer); - if (killedInfo.getKillStreak() > 4) { - increment += getStreakPoints(killed); - Repository.sendNews("News: " + killer.getUsername() + " has ended " + killed.getUsername() + "'s killstreak of " + killedInfo.getKillStreak() + "!"); - } - incrementKills(); - incrementStreak(); - killedInfo.setKillStreak(0); - killedInfo.incrementDeaths(); - incrementPkPoints(increment); - PKTitle.checkTitle(killer); - drawStatsTab(killer); - killedInfo.drawStatsTab(killed); - killer.sendMessage("You have killed " + killed.getUsername() + "! Your PK Points have increased by " + increment + "."); - killer.sendMessage("You are now on a " + killStreak + " killstreak!"); - if (killStreak > 4) { - Repository.sendNews("News: " + getStreakMessage(killer, killed)); - } - PKScoreBoard.check(killer); - } - - /** - * Gets the streak points. - * @param killed the killed player. - * @return the points. - */ - private int getStreakPoints(Player killed) { - int streak = killed.getSavedData().getSpawnData().getKillStreak(); - int points = 1; - if (streak > 3) { - return streak; - } - return points; - } - - /** - * Gets a kill streak message. - * @param killer the killer. - * @param killed the killed. - * @return the kill streak message.no1 readd him - */ - private String getStreakMessage(Player killer, Player killed) { - int streak = getKillStreak(); - String message = "killstreak"; - if (streak > 5) { - int index = streak - 6; - if (index > KILLSTREAKS.length-1) { - return killer.getUsername() + " is unstoppable! " + (killer.getAppearance().isMale() ? "He" : "She") + " is on a killstreak of " + streak + "!"; - } else { - message = "kill " + KILLSTREAKS[index]; - } - } - return killer.getUsername() + " is on a " + streak + " " + message + "! Kill " + (killer.getAppearance().isMale() ? "him" : "her") + " to gain " + streak + " PKP!"; - } - - /** - * Increments the kill streak. - */ - public void incrementStreak() { - killStreak++; - } - - /** - * Increments the deaths. - */ - public void incrementDeaths() { - deaths++; - } - - /** - * Increments the kills. - */ - public void incrementKills() { - kills++; - } - - /** - * Increments the pk points. - * @param increment the increment. - */ - public void incrementPkPoints(int increment) { - setPkPoints(getPkPoints() + increment); - } - - /** - * Checks if the tutorial is completed. - * @return {@code True} if so. - */ - public boolean hasCompletedTutorial() { - return getTutorialStage() > 2; - } - - /** - * Increments the tutorial stage. - */ - public void incrementTutorialStage() { - setTutorialStage(getTutorialStage() + 1); - } - - /** - * Gets the kdr. - * @return the kdr. - */ - public String getKdr() { - return new DecimalFormat().format(deaths == 0 ? kills : (double) ((double) kills / (double) deaths)); - } - - /** - * Gets the tutorialStage. - * @return the tutorialStage - */ - public int getTutorialStage() { - return tutorialStage; - } - - /** - * Sets the batutorialStage. - * @param tutorialStage the tutorialStage to set. - */ - public void setTutorialStage(int tutorialStage) { - this.tutorialStage = tutorialStage; - } - - /** - * Gets the pkPoints. - * @return the pkPoints - */ - public int getPkPoints() { - return pkPoints; - } - - /** - * Sets the bapkPoints. - * @param pkPoints the pkPoints to set. - */ - public void setPkPoints(int pkPoints) { - this.pkPoints = pkPoints; - } - - /** - * Gets the title. - * @return the title - */ - public PKTitle getTitle() { - return title; - } - - /** - * Sets the batitle. - * @param title the title to set. - */ - public void setTitle(PKTitle title) { - this.title = title; - } - - /** - * Gets the kills. - * @return the kills - */ - public int getKills() { - return kills; - } - - /** - * Sets the bakills. - * @param kills the kills to set. - */ - public void setKills(int kills) { - this.kills = kills; - } - - /** - * Gets the deaths. - * @return the deaths - */ - public int getDeaths() { - return deaths; - } - - /** - * Decrements the points. - */ - public void decrementPoints(int decrement) { - pkPoints-= decrement; - } - - /** - * Sets the badeaths. - * @param deaths the deaths to set. - */ - public void setDeaths(int deaths) { - this.deaths = deaths; - } - - /** - * Gets the killStreak. - * @return the killStreak - */ - public int getKillStreak() { - return killStreak; - } - - /** - * Sets the bakillStreak. - * @param killStreak the killStreak to set. - */ - public void setKillStreak(int killStreak) { - this.killStreak = killStreak; - } - - public void setPurchased(int purchased) { - this.purchased = purchased; - } - - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/SkillRestoration.java b/Server/src/main/java/core/game/node/entity/skill/SkillRestoration.java deleted file mode 100644 index 460f10b5e..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/SkillRestoration.java +++ /dev/null @@ -1,113 +0,0 @@ -package core.game.node.entity.skill; - -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.prayer.PrayerType; -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; -import rs09.game.world.GameWorld; - -/** - * Handles the skill restoration data. - * @author Emperor - */ -public final class SkillRestoration { - - /** - * The skill index. - */ - private final int skillId; - - /** - * The current tick. - */ - private int statTick; - - private int hpSummPrayTick; - - /** - * Constructs a new {@code SkillRestoration} {@code Object}. - * @param skillId The skill id. - */ - public SkillRestoration(int skillId) { - this.skillId = skillId; - restartHpSummPray(false); - restartStat(false); - } - - /** - * Restores the skill. - * @param entity The entity. - */ - public void restore(Entity entity) { - Skills skills = entity.getSkills(); - int max = skills.getStaticLevel(skillId); - if(skillId == Skills.HITPOINTS && entity instanceof Player && SkillcapePerks.isActive(SkillcapePerks.DAMAGE_SPONG,entity.asPlayer())){ - max = 110; - } - if(hpSummPrayTick < GameWorld.getTicks()){ - if(skillId == Skills.HITPOINTS || skillId == Skills.SUMMONING || skillId == Skills.PRAYER){ - 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); - } - restartHpSummPray(entity.asPlayer().getPrayer().getActive().contains(PrayerType.RAPID_HEAL)); - } - } - if(statTick < GameWorld.getTicks()) { - if (skillId != Skills.HITPOINTS && skillId != Skills.SUMMONING && skillId != Skills.PRAYER) { - int current = skills.getLevel(skillId); - skills.updateLevel(skillId,current < max ? 1 : -1,max); - restartStat(entity.asPlayer().getPrayer().getActive().contains(PrayerType.RAPID_RESTORE)); - } - } - } - - /** - * Gets the tick. - * @return The tick. - */ - public int getTick() { - return hpSummPrayTick; - } - - /** - * Sets the tick. - * @param tick The tick to set. - */ - public void setTick(int tick) { - this.hpSummPrayTick = tick; - this.statTick = tick; - } - - /** - * Gets the skillId. - * @return The skillId. - */ - public int getSkillId() { - return skillId; - } - - /** - * Restarts the restoration. - */ - public void restartHpSummPray(boolean half) { - int ticks = 100; - if(half){ - ticks /= 2; - } - this.hpSummPrayTick = GameWorld.getTicks() + ticks; - } - - public void restartStat(boolean half) { - int ticks = 100; - if(half){ - ticks /= 2; - } - this.statTick = GameWorld.getTicks() + ticks; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/BladeTrap.java b/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/BladeTrap.java deleted file mode 100644 index 02d6e6ca8..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/BladeTrap.java +++ /dev/null @@ -1,50 +0,0 @@ -package core.game.node.entity.skill.agility.brimhaven; - -import core.game.node.entity.skill.agility.AgilityHandler; -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; -import core.game.system.task.MovementHook; -import core.game.system.task.Pulse; -import rs09.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; - -/** - * Handles the blade trap. - * @author Emperor - */ -public final class BladeTrap implements MovementHook { - - @Override - public boolean handle(Entity e, final Location l) { - if (BrimhavenArena.sawBladeActive) { - final Direction dir = e.getDirection(); - final Player player = (Player) e; - final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); - e.lock(5); - e.addExtension(LogoutTask.class, new LocationLogoutTask(5, start)); - GameWorld.getPulser().submit(new Pulse(2, e) { - @Override - public boolean pulse() { - Direction direction = dir; - Direction d = Direction.get(direction.toInteger() + 2 & 3); - if (RegionManager.getObject(player.getLocation().transform(dir)) != null) { - Direction s = d; - d = direction; - direction = s; - } - Location loc = player.getLocation(); - AgilityHandler.failWalk(player, 1, loc, loc.transform(direction), loc.transform(direction), Animation.create(846), 10, 3, "You were hit by the saw blade!").setDirection(d); - return true; - } - }); - return false; - } - return true; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/FloorSpikes.java b/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/FloorSpikes.java deleted file mode 100644 index 3fa6da42b..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/FloorSpikes.java +++ /dev/null @@ -1,53 +0,0 @@ -package core.game.node.entity.skill.agility.brimhaven; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; -import core.game.system.task.MovementHook; -import core.game.system.task.Pulse; -import rs09.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; - -/** - * Handles floor spikes trap. - * @author Emperor - */ -public final class FloorSpikes implements MovementHook { - - @Override - public boolean handle(Entity e, final Location l) { - final Direction dir = e.getDirection(); - final Player player = (Player) e; - final Location start = l.transform(-dir.getStepX(), -dir.getStepY(), 0); - e.lock(5); - e.addExtension(LogoutTask.class, new LocationLogoutTask(5, start)); - GameWorld.getPulser().submit(new Pulse(3, e) { - @Override - public boolean pulse() { - player.getPacketDispatch().sendSceneryAnimation(RegionManager.getObject(l), Animation.create(1111)); - if (AgilityHandler.hasFailed(player, 20, 0.25)) { - if (player.getSkills().getLevel(Skills.AGILITY) < 20) { - player.getPacketDispatch().sendMessage("You need an agility of at least 20 to get past this trap!"); - } - int hit = player.getSkills().getLifepoints() / 12; - if (hit < 2) { - hit = 2; - } - AgilityHandler.failWalk(player, 1, player.getLocation(), start, start, Animation.create(1114), 10, hit, "You were hit by some floor spikes!").setDirection(dir); - ; - } else { - AgilityHandler.forceWalk(player, -1, l, l.transform(dir.getStepX() << 1, dir.getStepY() << 1, 0), Animation.create(1115), 20, 26, null); - } - return true; - } - }); - return false; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/PressurePad.java b/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/PressurePad.java deleted file mode 100644 index 239878227..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/agility/brimhaven/PressurePad.java +++ /dev/null @@ -1,53 +0,0 @@ -package core.game.node.entity.skill.agility.brimhaven; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.agility.AgilityHandler; -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.system.task.LocationLogoutTask; -import core.game.system.task.LogoutTask; -import core.game.system.task.MovementHook; -import core.game.system.task.Pulse; -import rs09.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; - -/** - * Handles the pressure pad trap. - * @author Emperor - */ -public final class PressurePad implements MovementHook { - - @Override - public boolean handle(Entity e, final Location dest) { - final Direction dir = e.getDirection(); - final Player player = (Player) e; - final Location start = dest.transform(-dir.getStepX(), -dir.getStepY(), 0); - e.lock(5); - e.addExtension(LogoutTask.class, new LocationLogoutTask(5, start)); - GameWorld.getPulser().submit(new Pulse(3, e) { - @Override - public boolean pulse() { - Graphics.send(Graphics.create(271), dest); - if (AgilityHandler.hasFailed(player, 20, 0.25)) { - if (player.getSkills().getLevel(Skills.AGILITY) < 20) { - player.getPacketDispatch().sendMessage("You need an agility of at least 20 to get past this trap!"); - } - int hit = player.getSkills().getLifepoints() / 12; - if (hit < 2) { - hit = 2; - } - AgilityHandler.failWalk(player, 1, player.getLocation(), start, start, Animation.create(1114), 10, hit, "You were hit by some falling rocks!").setDirection(dir); - ; - } else { - AgilityHandler.forceWalk(player, -1, dest, dest.transform(dir.getStepX() << 1, dir.getStepY() << 1, 0), Animation.create(1115), 20, 26, null); - } - return true; - } - }); - return false; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/FaladorGrapplePlugin.java b/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/FaladorGrapplePlugin.java deleted file mode 100644 index a12ded1d0..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/FaladorGrapplePlugin.java +++ /dev/null @@ -1,145 +0,0 @@ -package core.game.node.entity.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 rs09.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().hideTabs(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/java/core/game/node/entity/skill/agility/shortcuts/KaramjaGrapple.java b/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/KaramjaGrapple.java deleted file mode 100644 index b5658cc8a..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/KaramjaGrapple.java +++ /dev/null @@ -1,145 +0,0 @@ -package core.game.node.entity.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 rs09.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/java/core/game/node/entity/skill/agility/shortcuts/StileShortcut.java b/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/StileShortcut.java deleted file mode 100644 index 2efb7745d..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/StileShortcut.java +++ /dev/null @@ -1,107 +0,0 @@ -package core.game.node.entity.skill.agility.shortcuts; - -import core.game.content.activity.ActivityManager; -import core.plugin.Initializable; -import core.game.node.entity.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 rs09.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 }, 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; - } - }, "movement"); - } - - /** - * 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/java/core/game/node/entity/skill/agility/shortcuts/WaterOrbGrapple.java b/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/WaterOrbGrapple.java deleted file mode 100644 index 5128206b3..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/WaterOrbGrapple.java +++ /dev/null @@ -1,126 +0,0 @@ -package core.game.node.entity.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 rs09.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/java/core/game/node/entity/skill/agility/shortcuts/YanilleGrapple.java b/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/YanilleGrapple.java deleted file mode 100644 index 164dc4da5..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/agility/shortcuts/YanilleGrapple.java +++ /dev/null @@ -1,121 +0,0 @@ -package core.game.node.entity.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 rs09.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().hideTabs(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/java/core/game/node/entity/skill/construction/ConstructionGuideBook.java b/Server/src/main/java/core/game/node/entity/skill/construction/ConstructionGuideBook.java deleted file mode 100644 index 02357da97..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/ConstructionGuideBook.java +++ /dev/null @@ -1,104 +0,0 @@ -package core.game.node.entity.skill.construction; - - -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.book.Book; -import core.game.content.dialogue.book.Page; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import rs09.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/java/core/game/node/entity/skill/construction/Decoration.java b/Server/src/main/java/core/game/node/entity/skill/construction/Decoration.java deleted file mode 100644 index 792e10bd0..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/Decoration.java +++ /dev/null @@ -1,1316 +0,0 @@ -package core.game.node.entity.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 rs09.game.node.entity.skill.construction.Hotspot; - -/** - * Represents the decorations. - * @author Emperor - * - */ -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)), - DUNGEON_ENTRANCE(13409, 8172, 70, 500.0, new Item(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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * Parlour 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)), - - - /** - * 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)), - - - /** - * 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 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)), - - /** - * 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)), - - - /** - * 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)), - - /** - * 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)), - - - /** - * 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)), - - - /** - * 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 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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), - 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), - - /** - * 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)), - - - /** - * 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)), - - - /** - * 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)), - - - /** - * 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)), - - - /** - * 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)), - - - /** - * 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)), - - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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)), - - /** - * 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), - - ; - /** - * The object id. - */ - private final int objectId; - - /** - * The item id for the interface. - */ - private final int interfaceItem; - - /** - * The level requirement. - */ - private final int level; - - /** - * The experience gained for building this decoration. - */ - private final double experience; - - /** - * The item required. - */ - private final Item[] items; - - /** - * The tools required. - */ - private final int[] tools; - - /** - * The object ids depending on styling. - */ - private final int[] objectIds; - - /** - * If this node should be invisible to user build options - */ - private boolean invisibleNode; - - /** - * Constructs a new {@code Portal} {@code Object}. - * @param objectId The object id. - * @param interfaceItem The item id for the building interface. - * @param level The level required. - * @param experience The experience gained. - * @param items The items required. - */ - private Decoration(int objectId, int interfaceItem, int level, double experience, Item... items) { - this(objectId, interfaceItem, level, experience, new int[] { 2347, 8794 }, items); - } - - /** - * 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. - */ - private Decoration(int objectId, int interfaceItem, int level, double experience, int[] tools, Item... items) { - this.objectId = objectId; - this.objectIds = null; - this.interfaceItem = interfaceItem; - this.level = level; - this.experience = experience; - this.tools = tools; - this.items = items; - } - - /** - * Decoration - * @param objectId - * @param invisibleNode - */ - private Decoration(int objectId, boolean invisibleNode) { - this(objectId, -1, -1, -1); - this.invisibleNode = true; - } - - /** - * Constructs a new {@code Portal} {@code 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 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); - } - - /** - * Constructs a new {@code Portal} {@code 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 items The items required. - */ - private Decoration(int[] objectIds, int interfaceItem, int level, double experience, int[] tools, Item... items) { - this.objectId = objectIds[0]; - this.objectIds = objectIds; - this.interfaceItem = interfaceItem; - this.level = level; - this.experience = experience; - this.tools = tools; - this.items = items; - } - - /** - * Gets the decoration on the given location. - * @param player The player. - * @return The decoration. - */ - public static Decoration getDecoration(Player player, Scenery object) { - Location l = object.getLocation(); - int z = l.getZ(); - if (HouseManager.isInDungeon(player)) { - z = 3; - } - Room room = player.getHouseManager().getRooms()[z][l.getChunkX()][l.getChunkY()]; - for (Hotspot h : room.getHotspots()) { - if (h.getCurrentX() == l.getChunkOffsetX() && h.getCurrentY() == l.getChunkOffsetY()) { - if (h.getDecorationIndex() != -1) { - Decoration deco = h.getHotspot().getDecorations()[h.getDecorationIndex()]; - if (deco.getObjectId(player.getHouseManager().getStyle()) == object.getId()) { - return deco; - } - } - } - } - return null; - } - - /** - * Gets a decoration for the given object id - * @param objectId - the object id of the built object - * @return the decoration or null - */ - public static Decoration forObjectId(int objectId) { - for (Decoration d : Decoration.values()) { - if (d.getObjectId() == objectId) { - return d; - } - } - return null; - } - - public static Decoration forName(String name) { - for (Decoration d : Decoration.values()) { - if (d.name().equals(name)) { - return d; - } - } - return null; - } - - /** - * Gets the amount of nails required for this hotspot. - * @return The amount of nails. - */ - public int getNailAmount() { - for (Item item : items) { - if (item.getId() == 960) { //1 nail per normal plank required. - return item.getAmount(); - } - } - return 0; - } - - /** - * Gets the objectId. - * @param style The current housing style. - * @return The objectId. - */ - public int getObjectId(HousingStyle style) { - if (objectIds != null) { - return objectIds[style.ordinal()]; - } - return objectId; - } - - /** - * Gets the objectId. - * @return The objectId. - */ - public int getObjectId() { - return objectId; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Gets the items. - * @return The items. - */ - public Item[] getItems() { - return items; - } - - /** - * Gets the tools. - * @return The tools. - */ - public int[] getTools() { - return tools; - } - - /** - * Gets the interfaceItem. - * @return the interfaceItem - */ - public int getInterfaceItem() { - return interfaceItem; - } - - /** - * Gets the objectIds value. - * @return The objectIds. - */ - public int[] getObjectIds() { - return objectIds; - } - - public boolean isInvisibleNode() { - return invisibleNode; - } - - - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/HouseManager.java b/Server/src/main/java/core/game/node/entity/skill/construction/HouseManager.java deleted file mode 100644 index 017e43efb..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/HouseManager.java +++ /dev/null @@ -1,834 +0,0 @@ -package core.game.node.entity.skill.construction; - - -//import org.arios.game.content.global.DeadmanTimedAction; -//import org.arios.game.node.entity.player.info.login.SavingModule; - -import core.game.content.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; -import core.game.world.map.*; -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 org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import rs09.game.node.entity.skill.construction.Hotspot; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; - -import java.awt.*; -import java.nio.ByteBuffer; - -/** - * Manages the player's house. - * @author Emperor - * - */ -public final class HouseManager { - - /** - * The current region. - */ - private DynamicRegion region; - - /** - * The current region. - */ - private DynamicRegion dungeon; - - /** - * The house location. - */ - private HouseLocation location = HouseLocation.NOWHERE; - - /** - * The house style. - */ - private HousingStyle style = HousingStyle.BASIC_WOOD; - - /** - * The house zone. - */ - private final HouseZone zone = new HouseZone(this); - - /** - * The player's house rooms. - */ - private final Room[][][] rooms = new Room[4][8][8]; - - /** - * If building mode is enabled. - */ - private boolean buildingMode; - - /** - * If the player has used the portal to lock their house. - */ - private boolean locked; - - /** - * The player's servant. - */ - private Servant servant; - - /** - * If the house has a dungeon. - */ - private boolean hasDungeon; - - /** - * The player's crest. - */ - private CrestType crest = CrestType.ASGARNIA; - - /** - * Constructs a new {@code HouseManager} {@code Object}. - */ - public HouseManager() { - /* - * empty. - */ - } - - - 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())]; - Object servRaw = data.get("servant"); - if(servRaw != null){ - servant = Servant.parse((JSONObject) servRaw); - } - JSONArray rArray = (JSONArray) data.get("rooms"); - for(int i = 0; i < rArray.size(); i++){ - JSONObject rm = (JSONObject) rArray.get(i); - int z = Integer.parseInt(rm.get("z").toString()); - int x = Integer.parseInt(rm.get("x").toString()); - int y = Integer.parseInt(rm.get("y").toString()); - if(z == 3) - hasDungeon = true; - Room room = rooms[z][x][y] = new Room(RoomProperties.values()[Integer.parseInt(rm.get("properties").toString())]); - room.configure(style); - room.setRotation(Direction.get(Integer.parseInt(rm.get("rotation").toString()))); - JSONArray hotspots = (JSONArray) rm.get("hotspots"); - for(int j = 0; j < hotspots.size(); j++){ - JSONObject spot = (JSONObject) hotspots.get(j); - room.getHotspots()[Integer.parseInt(spot.get("hotspotIndex").toString())].setDecorationIndex(Integer.parseInt(spot.get("decorationIndex").toString())); - } - } - } - - - 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); - } - } - } - - /** - * Enters the player's house. - * @param player The player entering. - * @param buildingMode If building mode is enabled. - * @param teleport if the entry was a teleport. - */ - public void enter(final Player player, boolean buildingMode, boolean teleport) { - enter(player, buildingMode); - } - - /** - * Enter's the player's house. - * @param player - * @param buildingMode - */ - public void enter(final Player player, boolean buildingMode) { - if (HouseManager.this.buildingMode != buildingMode || !isLoaded()) { - HouseManager.this.buildingMode = buildingMode; - construct(); - } - player.setAttribute("poh_entry", HouseManager.this); - player.lock(1); - player.sendMessage("House location: " + region.getBaseLocation() + ", entry: " + getEnterLocation()); - player.getProperties().setTeleportLocation(getEnterLocation()); - player.getInterfaceManager().openComponent(399); - player.getConfigManager().set(261, buildingMode); - player.getConfigManager().set(262, getRoomAmount()); - player.getAudioManager().send(new Audio(984)); -// player.getMusicPlayer().unlock(454, true); - GameWorld.getPulser().submit(new Pulse(1, player) { - @Override - public boolean pulse() { - if (hasServant()){ - spawnServant(); - if (servant.isGreet()){ - player.getDialogueInterpreter().sendDialogues(servant.getType().getId(), servant.getType().getId() == 4243 ? FacialExpression.HALF_GUILTY : null, "Welcome."); - } - } -// player.getInterfaceManager().switchWindowMode(1); - player.getInterfaceManager().close(); - return true; - } - }); - } - - /** - * Leaves this house. - * @param player The player leaving. - */ - public static void leave(Player player) { - HouseManager house = player.getAttribute("poh_entry", player.getHouseManager()); - if (house.getRegion() == null){ - return; - } - if (house.isInHouse(player)) { - player.animate(Animation.RESET); - player.setLocation(house.location.getExitLocation()); - } - } - - /** - * Toggles the building mode. - * @param player The house owner. - * @param enable If the building mode should be enabled. - */ - public void toggleBuildingMode(Player player, boolean enable) { - if (!isInHouse(player)) { - player.getPacketDispatch().sendMessage("Building mode really only helps if you're in a house."); - return; - } - if (buildingMode != enable) { - if (enable) { - expelGuests(player); - } - buildingMode = enable; - reload(player, enable); - player.getPacketDispatch().sendMessage("Building mode is now " + (buildingMode ? "on." : "off.")); - } - } - - /** - * Reloads the house. - * @param player The player. - * @param buildingMode If building mode should be enabled. - */ - public void reload(Player player, boolean buildingMode) { - DynamicRegion r = region; - if ((player.getViewport().getRegion() == dungeon)) { - r = dungeon; - } - int diffX = player.getLocation().getX() - r.getBaseLocation().getX(); - int diffY = player.getLocation().getY() - r.getBaseLocation().getY(); - int diffZ = player.getLocation().getZ() - r.getBaseLocation().getZ(); - region = null; - dungeon = null; - enter(player, buildingMode, false); - player.getProperties().setTeleportLocation((player.getViewport().getRegion() == dungeon ? dungeon : region).getBaseLocation().transform(diffX, diffY, diffZ)); - } - - /** - * Expels the guests from the house. - * @param player The house owner. - */ - public void expelGuests(Player player) { - if (isLoaded()) { - for (RegionPlane plane : region.getPlanes()) { - for (Player p : plane.getPlayers()) { - if (p != player) { - leave(p); - } - } - } - if (dungeon != null) { - for (RegionPlane plane : dungeon.getPlanes()) { - for (Player p : plane.getPlayers()) { - if (p != player) { - leave(p); - } - } - } - } - } - } - - /** - * Gets the entering location. - * @return The entering location. - */ - public Location getEnterLocation() { - if (region == null) { - SystemLogger.logErr("House wasn't constructed yet!"); - return null; - } - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - Room room = rooms[0][x][y]; - if (room != null && (room.getProperties() == RoomProperties.GARDEN || room.getProperties() == RoomProperties.FORMAL_GARDEN)) { - for (Hotspot h : room.getHotspots()) { - if (h.getDecorationIndex() > -1) { - Decoration d = h.getHotspot().getDecorations()[h.getDecorationIndex()]; - if (d == Decoration.PORTAL) { - return region.getBaseLocation().transform(x * 8 + h.getChunkX(), y * 8 + h.getChunkY() + 2, 0); - } - } - } - } - } - } - return null; - } - - /** - * Redecorates the house. - * @param style The new style. - */ - public void redecorate(HousingStyle style) { - this.style = style; - 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) { - room.decorate(style); - } - } - } - } - } - - /** - * Clears all the rooms (Including portal room!). - */ - @Deprecated - public void clearRooms() { - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - rooms[z][x][y] = null; - } - } - } - } - - /** - * Creates the default house. - * @param location The house location. - */ - public void create(HouseLocation location) { - clearRooms(); - Room room = rooms[0][4][3] = new Room(RoomProperties.GARDEN); - room.configure(style); - room.getHotspots()[0].setDecorationIndex(0); - this.location = location; - } - - /** - * Constructs the dynamic region for the house. - * @return The region. - */ - public DynamicRegion construct() { - Region from = RegionManager.forId(style.getRegionId()); - Region.load(from, true); - RegionChunk defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(1, 0); - RegionChunk defaultSkyChunk = from.getPlanes()[1].getRegionChunk(0,0); - ZoneBorders borders; - //DynamicRegion[] regions = DynamicRegion.create(borders); - region = DynamicRegion.create(style.getRegionId()); -/* region.setBorders(borders); - region.setUpdateAllPlanes(true); - RegionManager.addRegion(region.getId(), region);*/ - configureRoofs(); - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - if(z == 3){ - region.replaceChunk(z, x, y, defaultSkyChunk.copy(region.getPlanes()[z]), from); - continue; - } - Room room = rooms[z][x][y]; - if (room != null) { - if (room.getProperties().isRoof() && buildingMode) { - continue; - } - BuildRegionChunk copy = room.getChunk().copy(region.getPlanes()[z]); - region.replaceChunk(z, x, y, copy, from); - room.loadDecorations(z, copy, this); - } else { - region.replaceChunk(z, x, y, z != 0 ? defaultSkyChunk.copy(region.getPlanes()[z]) : defaultChunk.copy(region.getPlanes()[0]), from); - } - } - } - } - if (hasDungeon()) { - defaultChunk = from.getPlanes()[style.getPlane()].getRegionChunk(3, 0); - borders = DynamicRegion.reserveArea(8, 8); - dungeon = new DynamicRegion(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6); - dungeon.setBorders(borders); - dungeon.setUpdateAllPlanes(true); - RegionManager.addRegion(dungeon.getId(), dungeon); - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - Room room = rooms[3][x][y]; - if (hasRoom(3, x, y)) { - BuildRegionChunk copy = room.getChunk().copy(dungeon.getPlanes()[0]); - dungeon.replaceChunk(0, x, y, copy, from); - room.loadDecorations(3, copy, this); - } else { - dungeon.replaceChunk(0, x, y, buildingMode ? null : defaultChunk.copy(dungeon.getPlanes()[0]), from); - } - } - } - region.link(dungeon); - } - ZoneBuilder.configure(zone); - return region; - } - - /** - * Configures the rooftops. - */ - public void configureRoofs() { -// boolean[][][] roofs = new boolean[2][8][8]; -// for (int x = 0; x < 8; x++) { -// for (int y = 0; y < 8; y++) { -// Room room = rooms[0][x][y]; -// if (room != null && room.getProperties().isChamber()) { -// room = rooms[1][x][y]; -// int z = 1; -// if (room != null && room.getProperties().isChamber()) { -// z = 2; -// } -// if (x > 0 ) -// } -// } -// } - } - - /** - * Gets the current room plane. - * @param l The location. - * @return The plane of the room. - */ - public Room getRoom(Location l) { - int z = l.getZ(); - if (dungeon != null && l.getRegionId() == dungeon.getId()) { - z = 3; - } - return rooms[z][l.getChunkX()][l.getChunkY()]; - } - - /** - * Gets the hotspot for the given object. - * @param object The object. - * @return The hotspot. - */ - public Hotspot getHotspot(Scenery object) { - Room room = getRoom(object.getLocation()); - if (room == null) { - return null; - } - int chunkX = object.getLocation().getChunkOffsetX(); - int chunkY = object.getLocation().getChunkOffsetY(); - switch(room.getRotation()){ - case WEST: { - int tempChunk = chunkY; - chunkY = 7 - chunkX; - chunkX = tempChunk; - break; - } - case EAST: { - //x = y, y = x, x = 7 - y - int tempChunk = chunkX; - chunkX = 7 - chunkY; - chunkY = tempChunk; - break; - } - case SOUTH: { - chunkY = 7 - chunkY; - break; - } - default: { - - } - } - for (Hotspot h : room.getHotspots()) { - if ((h.getChunkX() == chunkX || h.getChunkX2() == chunkX) && (h.getChunkY() == chunkY || h.getChunkY2() == chunkY) && h.getHotspot().getObjectId(style) == object.getId()) { - return h; - } - } - return null; - } - - /** - * Checks if a room exists on the given location. - * @param z The plane. - * @param roomX The room x-coordinate. - * @param roomY The room y-coordinate. - * @return {@code True} if so. - */ - public boolean hasRoom(int z, int roomX, int roomY) { - Room room = rooms[z][roomX][roomY]; - return room != null && !room.getProperties().isRoof(); - } - - /** - * Enters the dungeon. - * @param player The player. - */ - public void enterDungeon(Player player) { - if (!hasDungeon()) { - return; - } - int diffX = player.getLocation().getLocalX(); - int diffY = player.getLocation().getLocalY(); - player.getProperties().setTeleportLocation(dungeon.getBaseLocation().transform(diffX, diffY, 0)); - } - - /** - * Checks if an exit exists on the given room. - * @param roomX The x-coordinate of the room. - * @param roomY The y-coordinate of the room. - * @param direction The exit direction. - * @return {@code True} if so. - */ - public boolean hasExit(int z, int roomX, int roomY, Direction direction) { - Room room = rooms[z][roomX][roomY]; - int index = (direction.toInteger() + 3) % 4; - return room != null && room.getExits()[index]; - } - - /** - * Gets the amount of rooms. - * @return The amount of rooms. - */ - public int getRoomAmount() { - int count = 0; - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - Room r = rooms[z][x][y]; - if (r != null && !r.getProperties().isRoof()) { - count++; - } - } - } - } - return count; - } - - /** - * Gets the amount of portals available. - * @return The amount of portals. - */ - public int getPortalAmount() { - int count = 0; - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - Room room = rooms[0][x][y]; - if (room != null && (room.getProperties() == RoomProperties.GARDEN - || room.getProperties() == RoomProperties.FORMAL_GARDEN) && room.getHotspots()[0].getDecorationIndex() == 0) { - count++; - } - } - } - return count; - } - - /** - * Gets the current house boundaries. - * @return The boundaries. - */ - public Rectangle getBoundaries() { - int startX = 99; - int startY = 99; - int endX = 0; - int endY = 0; - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - if (rooms[0][x][y] != null) { - if (x < startX) startX = x; - if (y < startY) startY = y; - if (x > endX) endX = x; - if (y > endY) endY = y; - } - } - } - return new Rectangle(startX, startY, (endX - startX) + 1, (endY - startY) + 1); - } - - /** - * Gets the maximum dimension for the house boundaries. - * @param player The player. - * @return The dimension value (value X value = dimension) - */ - public int getMaximumDimension(Player player) { - int level = player.getSkills().getStaticLevel(Skills.CONSTRUCTION); - if (level >= 60) { - return 7; - } - if (level >= 45) { - return 6; - } - if (level >= 30) { - return 5; - } - if (level >= 15) { - return 4; - } - return 3; - } - - /** - * Gets the maximum amount of rooms available for the player. - * @param player The player. - * @return The maximum amount of rooms. - */ - public int getMaximumRooms(Player player) { - int level = player.getSkills().getStaticLevel(Skills.CONSTRUCTION); - if (level >= 99) return 30; - if (level >= 96) return 29; - if (level >= 92) return 28; - if (level >= 86) return 27; - if (level >= 80) return 26; - if (level >= 74) return 25; - if (level >= 68) return 24; - if (level >= 62) return 23; - if (level >= 56) return 22; - if (level >= 50) return 21; - return 20; - } - - /** - * Spawns the servant inside the player's home. - */ - private void spawnServant(){ - servant.setLocation(getEnterLocation()); - servant.setWalkRadius(getRoomAmount() * 2); - servant.setWalks(true); - servant.init(); - } - - /** - * Checks if the player has a servant. - * @return {@code True} if so. - */ - public boolean hasServant() { - return servant != null; - } - - /** - * Checks if the player is in his own house (or dungeon). - * @param player The player. - * @return {@code True} if so. - */ - public boolean isInHouse(Player player) { - return isLoaded() && (player.getViewport().getRegion() == region || player.getViewport().getRegion() == dungeon); - } - - /** - * Checks if the player is in his dungeon. - * @param player The player. - * @return {@code True} if so. - */ - public static boolean isInDungeon(Player player) { - return player.getViewport().getRegion() == player.getHouseManager().dungeon; - } - - /** - * Checks if the house region was constructed and active. - * @return {@code True} if an active region for the house exists. - */ - public boolean isLoaded() { - return region != null && region.isActive() || dungeon != null && dungeon.isActive(); - } - - /** - * Gets the hasHouse. - * @return The hasHouse. - */ - public boolean hasHouse() { - return location != HouseLocation.NOWHERE; - } - - /** - * Checks if the house has a dungeon. - * @return {@code True} if so. - */ - public boolean hasDungeon() { - return hasDungeon; - } - - /** - * Sets the has dungeon value. - * @param hasDungeon If the house has a dungeon. - */ - public void setHasDungeon(boolean hasDungeon) { - this.hasDungeon = hasDungeon; - } - - /** - * Gets the rooms. - * @return The rooms. - */ - public Room[][][] getRooms() { - return rooms; - } - - /** - * Gets the location. - * @return The location. - */ - public HouseLocation getLocation() { - return location; - } - - /** - * Sets the location. - * @param location The location to set. - */ - public void setLocation(HouseLocation location) { - this.location = location; - } - - /** - * Checks if the building mode is enabled. - * @return {@code True} if so. - */ - public boolean isBuildingMode() { - return buildingMode; - } - - /** - * Checks if the player has locked their house. - * @return {@code True} if so. - */ - public boolean isLocked() { - return locked; - } - - /** - * Sets the house to locked. - * @param locked true or false - */ - public void setLocked(boolean locked) { - this.locked = locked; - } - - /** - * Gets the region. - * @return The region. - */ - public Region getRegion() { - return region; - } - - /** - * Gets the dungeon region. - * @return The dungeon region. - */ - public Region getDungeonRegion() { - return dungeon; - } - - /** - * Gets the style. - * @return the style - */ - public HousingStyle getStyle() { - return style; - } - - /** - * Sets the style. - * @param style the style to set. - */ - public void setStyle(HousingStyle style) { - this.style = style; - } - - /** - * Gets the player's servant - * @return the servant. - */ - public Servant getServant(){ - return servant; - } - - /** - * Sets the player's servant - * @param servant The servant to set. - */ - public void setServant(Servant servant){ - this.servant = servant; - } - - /** - * Gets the crest value. - * @return The crest. - */ - public CrestType getCrest() { - return crest; - } - - /** - * Sets the crest value. - * @param crest The crest to set. - */ - public void setCrest(CrestType crest) { - this.crest = crest; - } - - /** - * @return the zone - */ - public HouseZone getZone() { - return zone; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/HouseZone.java b/Server/src/main/java/core/game/node/entity/skill/construction/HouseZone.java deleted file mode 100644 index 9d578623d..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/HouseZone.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.node.entity.skill.construction; - - -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.world.map.zone.MapZone; -import core.game.world.map.zone.ZoneRestriction; - -/** - * Handles the player owned house zone. - * - * @author Emperor - */ -public final class HouseZone extends MapZone { - - /** - * The house manager. - */ - private HouseManager house; - - /** - * The previous house region id. - */ - private int previousRegion = -1; - - /** - * The previous dungeon region id. - */ - private int previousDungeon = -1; - - /** - * Constructs the house zone object. - */ - public HouseZone(HouseManager house) { - super("poh-zone" + house, true, ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.FOLLOWERS); - this.house = house; - } - - @Override - public void configure() { - if (previousRegion != -1) { - unregisterRegion(previousRegion); - } - if (previousDungeon != -1) { - unregisterRegion(previousDungeon); - } - registerRegion(house.getRegion().getId()); - if (house.getDungeonRegion() != null) { - registerRegion(house.getRegion().getId()); - } - } - - @Override - public boolean enter(Entity e) { - return super.enter(e); - } - - @Override - public boolean death(Entity e, Entity killer) { - if (e instanceof Player) { - Player p = (Player) e; - HouseManager.leave(p); - } - return true; - } - - @Override - public boolean leave(Entity e, boolean logout) { - if (logout) { - if (e instanceof Player) { - Player p = (Player) e; - HouseManager.leave(p); - return true; - } - } - return true; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/RemovalDialogue.java b/Server/src/main/java/core/game/node/entity/skill/construction/RemovalDialogue.java deleted file mode 100644 index 91ebb92f5..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/RemovalDialogue.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.node.entity.skill.construction; - - -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import rs09.game.node.entity.skill.construction.Hotspot; - -/** - * Removal dialogue for room & decorations in construction - * @author Clayton Williams - * @author Emperor - * @date Jun-30-2015 - */ -@Initializable -public final class RemovalDialogue extends DialoguePlugin { - - /** - * The room position. - */ - private int[] pos; - - /** - * The plane. - */ - private int plane; - - /** - * The room to remove. - */ - private Room room; - - /** - * Removal Dialogue - */ - public RemovalDialogue() { - super(); - } - - /** - * Removal Dialogue - * @param player - */ - public RemovalDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new RemovalDialogue(player); - } - - @Override - public boolean open(Object... args) { - pos = (int[]) args[1]; - plane = player.getLocation().getZ(); - if (HouseManager.isInDungeon(player)) { - plane = 3; - } - room = player.getHouseManager().getRooms()[plane][pos[0]][pos[1]]; - player.getDialogueInterpreter().sendOptions("Remove the " + (room != null ? room.getProperties().getName() : "room") + "?", "Yes", "No"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (stage == 0) { - if (buttonId == 1) { - if (plane == 0 && player.getHouseManager().hasRoom(1, pos[0], pos[1])) { - interpreter.sendPlainMessage(false, "You can't remove a room supporting another room."); - stage = 1; - return true; - } - if (room != null && room.getProperties().isLand()) { - Hotspot h = room.getHotspots()[0]; - if (h != null && h.getDecorationIndex() == 0 && player.getHouseManager().getPortalAmount() <= 1) { - interpreter.sendPlainMessage(false, "You can't remove the garden with your portal in it."); - stage = 1; - return true; - } - } - player.getHouseManager().getRooms()[plane][pos[0]][pos[1]] = null; - for (int i = plane; i < 3; i++) { - Room r = player.getHouseManager().getRooms()[i][pos[0]][pos[1]]; - if (r != null && r.getProperties().isRoof()) { - player.getHouseManager().getRooms()[i][pos[0]][pos[1]] = null; - } - } - player.getHouseManager().reload(player, true); - } - } - end(); - return true; - } - - @Override - public int[] getIds() { - return new int[]{ DialogueInterpreter.getDialogueKey("con:remove") }; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/Room.java b/Server/src/main/java/core/game/node/entity/skill/construction/Room.java deleted file mode 100644 index df8782295..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/Room.java +++ /dev/null @@ -1,394 +0,0 @@ -package core.game.node.entity.skill.construction; - - -import core.game.node.entity.player.Player; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.world.map.*; -import rs09.game.node.entity.skill.construction.Hotspot; - -/** - * Represents a room. - * @author Emperor - * - */ -public final class Room { - - /** - * The default room type. - */ - public static final int CHAMBER = 0x0; - - /** - * The rooftop room type. - */ - public static final int ROOF = 0x1; - - /** - * The dungeon room type. - */ - public static final int DUNGEON = 0x2; - - /** - * The dungeon room type. - */ - public static final int LAND = 0x4; - - /** - * The room properties. - */ - private RoomProperties properties; - - /** - * The region chunk. - */ - private RegionChunk chunk; - - /** - * The hotspots. - */ - private Hotspot[] hotspots; - - /** - * The current rotation of the room. - */ - private Direction rotation = Direction.NORTH; - - /** - * Constructs a new {@code Room} {@code Object}. - * @param properties The room properties. - */ - public Room(RoomProperties properties) { - this.properties = properties; - } - - /** - * Creates a new room. - * @param player The player. - * @param properties The room properties. - * @return The room. - */ - public static Room create(Player player, RoomProperties properties) { - Room room = new Room(properties); - room.configure(player.getHouseManager().getStyle()); - return room; - } - - /** - * Configures the room. - */ - public void configure(HousingStyle style) { - this.hotspots = new Hotspot[properties.getHotspots().length]; - for (int i = 0; i < hotspots.length; i++) { - hotspots[i] = properties.getHotspots()[i].copy(); - } - decorate(style); - } - - /** - * Redecorates the room. - * @param style The house style. - */ - public void decorate(HousingStyle style) { - Region region = RegionManager.forId(style.getRegionId()); - 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. - * @return The hotspot. - */ - public Hotspot getHotspot(BuildHotspot hotspot) { - for (Hotspot h : hotspots) { - if (h.getHotspot() == hotspot) { - return h; - } - } - return null; - } - - /** - * Checks if the building hotspot has been built. - * @param hotspot The building hotspot. - * @return {@code True} if so. - */ - public boolean isBuilt(BuildHotspot hotspot) { - Hotspot h = getHotspot(hotspot); - return h != null && h.getDecorationIndex() > -1; - } - - /** - * Loads all the decorations. - * @param housePlane The plane. - * @param chunk The chunk used in the dynamic region. - */ - public void loadDecorations(int housePlane, BuildRegionChunk chunk, HouseManager house) { - for (int i = 0; i < hotspots.length; i++) { - Hotspot spot = hotspots[i]; - int x = spot.getChunkX(); - int y = spot.getChunkY(); - if (spot.getHotspot() == null) { - continue; - } - int index = chunk.getIndex(x, y, spot.getHotspot().getObjectId(house.getStyle())); - Scenery[][] objects = chunk.getObjects(index); - Scenery object = objects[x][y]; - if (object != null && object.getId() == spot.getHotspot().getObjectId(house.getStyle())) { - if (spot.getDecorationIndex() > -1 && spot.getDecorationIndex() < spot.getHotspot().getDecorations().length) { - int id = spot.getHotspot().getDecorations()[spot.getDecorationIndex()].getObjectId(house.getStyle()); - if (spot.getHotspot().getType() == BuildHotspotType.CREST) { - id += house.getCrest().ordinal(); - } - SceneryBuilder.replace(object, object.transform(id, object.getRotation(), chunk.getCurrentBase().transform(x, y, 0))); - } - else if (object.getId() == BuildHotspot.WINDOW.getObjectId(house.getStyle()) || (!house.isBuildingMode() && object.getId() == BuildHotspot.CHAPEL_WINDOW.getObjectId(house.getStyle()))) { - chunk.add(object.transform(house.getStyle().getWindowStyle().getObjectId(house.getStyle()), object.getRotation(), object.getType())); - } - int[] pos = RegionChunk.getRotatedPosition(x, y, object.getSizeX(), object.getSizeY(), 0, rotation.toInteger()); - spot.setCurrentX(pos[0]); - spot.setCurrentY(pos[1]); - } - } - if (rotation != Direction.NORTH && chunk.getRotation() == 0) { - chunk.rotate(rotation); - } - if (!house.isBuildingMode()) { - removeHotspots(housePlane, house, chunk); - } - } - - /** - * Removes the building hotspots from the room. - * @param housePlane The room's plane in house. - * @param house The house manager. - * @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++) { - 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 { - chunk.remove(object); - } - } - } - } - } - } - - /** - * 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. - */ - 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(); - } - } - else if (!houseExit) { - return false; - } - return SceneryBuilder.replace(object, object.transform(replaceId, object.getRotation(), chunk.getCurrentBase().transform(x, y, 0)), true, true); - } - - /** - * Sets the decoration index for a group of object ids - * @param index The index. - * @param hs The building hotspot. - */ - public void setAllDecorationIndex(int index, BuildHotspot hs) { - for (int i = 0; i < hotspots.length; i++) { - Hotspot h = hotspots[i]; - if (h.getHotspot() == hs) { - h.setDecorationIndex(index); - } - } - } - - /** - * Gets the stairs hotspot for this room (or null if no stairs are available). - * @return The stairs. - */ - public Hotspot getStairs() { - for (Hotspot h : hotspots) { - if (h.getHotspot().getType() == BuildHotspotType.STAIRCASE - || h.getHotspot() == BuildHotspot.LADDER || h.getHotspot() == BuildHotspot.TRAPDOOR - || (h.getHotspot() == BuildHotspot.CENTREPIECE_1 && h.getDecorationIndex() == 4) - || (h.getHotspot() == BuildHotspot.CENTREPIECE_2 && h.getDecorationIndex() == 2)) { - return h; - } - } - return null; - } - - /** - * Gets the exit directions - * @return The exits information. - */ - public boolean[] getExits() { - return getExits(rotation); - } - - /** - * Gets the exit directions. - * @return The directions at which you can exit the room (0=east, 1=south, 2=west, 3=north). - */ - public boolean[] getExits(Direction rotation) { - boolean[] exits = properties.getExits(); - if (chunk.getRotation() != rotation.toInteger()) { - boolean[] exit = new boolean[exits.length]; - int offset = rotation.toInteger() - chunk.getRotation(); - for (int i = 0; i < 4; i++) { - exit[(i + offset) % 4] = exits[i]; - } - return exit; - } - return exits; - } - - /** - * Gets the hotspot for the given coordinates. - * @param build The build hotspot. - * @param x The x-coordinate. - * @param y The y-coordinate. - * @return The hotspot. - */ - public Hotspot getHotspot(BuildHotspot build, int x, int y) { - for (int i = 0; i < getHotspots().length; i++) { - Hotspot h = getHotspots()[i]; - if (h.getCurrentX() == x && h.getCurrentY() == y && h.getHotspot() == build) { - return h; - } - } - return null; - } - - /** - * Sets the properties. - * @param properties The properties. - */ - public void updateProperties(Player player, RoomProperties properties) { - this.properties = properties; - decorate(player.getHouseManager().getStyle()); - if (hotspots.length != properties.getHotspots().length) { - return; - } - for (int i = 0; i < hotspots.length; i++) { - Hotspot h = hotspots[i]; - Hotspot hs = hotspots[i] = properties.getHotspots()[i].copy(); - hs.setCurrentX(h.getCurrentX()); - hs.setCurrentY(h.getCurrentY()); - hs.setDecorationIndex(h.getDecorationIndex()); - } - } - - /** - * Gets the chunk. - * @return The chunk. - */ - public RegionChunk getChunk() { - return chunk; - } - - /** - * Sets the chunk. - * @param chunk The chunk to set. - */ - public void setChunk(RegionChunk chunk) { - this.chunk = chunk; - } - - /** - * Gets the hotspots. - * @return The hotspots. - */ - public Hotspot[] getHotspots() { - return hotspots; - } - - /** - * Sets the hotspots. - * @param hotspots The hotspots to set. - */ - public void setHotspots(Hotspot[] hotspots) { - this.hotspots = hotspots; - } - - /** - * Gets the properties. - * @return The properties. - */ - public RoomProperties getProperties() { - return properties; - } - - /** - * Sets the room rotation. - * @param rotation The rotation. - */ - public void setRotation(Direction rotation) { - this.rotation = rotation; - } - - /** - * Gets the rotation. - * @return The rotation. - */ - public Direction getRotation() { - return rotation; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/RoomBuilder.java b/Server/src/main/java/core/game/node/entity/skill/construction/RoomBuilder.java deleted file mode 100644 index 3bb0e5bc4..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/RoomBuilder.java +++ /dev/null @@ -1,312 +0,0 @@ -package core.game.node.entity.skill.construction; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -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.BuildRegionChunk; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.net.packet.PacketRepository; -import core.net.packet.context.ContainerContext; -import core.net.packet.out.ContainerPacket; -import rs09.game.node.entity.skill.construction.Hotspot; -import rs09.game.system.SystemLogger; - -import java.util.Arrays; - -/** - * Used for building a room. - * @author Emperor - * - */ -public final class RoomBuilder { - - /** - * The directions array. - */ - public static final Direction[] DIRECTIONS = { - Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST - }; - - /** - * Opens the building furniture interface. - * @param player The player. - * @param hotspot The hotspot. - */ - public static void openBuildInterface(Player player, BuildHotspot hotspot) { - player.getInterfaceManager().open(new Component(396)); - Item[] items = new Item[7]; - int value = 1; - for (int i = 0; i < 7; i++) { - int offset = i * 5; - if (i >= hotspot.getDecorations().length) { - for (int j = 97; j < 102; j++) { - player.getPacketDispatch().sendString("", 396, j + offset); - } - player.getPacketDispatch().sendString("", 396, 140 + i); - value |= 1 << (i + 1); - continue; - } - Decoration decoration = hotspot.getDecorations()[i]; - if (i < 4) { - items[i * 2] = new Item(decoration.getInterfaceItem()); - } else { - items[1 + ((i - 4) * 2)] = new Item(decoration.getInterfaceItem()); - } - player.getPacketDispatch().sendString(SceneryDefinition.forId(decoration.getObjectId()).getName(), 396, 97 + offset); - boolean hasRequirements = true; - for (int j = 0; j < 4; j++) { - if (j >= decoration.getItems().length) { - player.getPacketDispatch().sendString("", 396, 98 + offset + j); - } else { - Item item = decoration.getItems()[j]; - if (!player.getInventory().containsItem(item)) { - hasRequirements = false; - } - player.getPacketDispatch().sendString(item.getName() + ": " + item.getAmount(), 396, 98 + offset + j); - } - } - player.getPacketDispatch().sendString("Lvl " + decoration.getLevel(), 396, 140 + i); - if (hasRequirements) { - value |= 1 << (i + 1); - } - } - player.getConfigManager().set(261, value); - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 396, 132, 91, items, false)); - } - - /** - * Builds a decoration object. - * @param player The player. - * @param deco The decoration. - * @param object The object. - */ - public static void buildDecoration(Player player, BuildHotspot hotspot, Decoration deco, Scenery object) { - if(!player.isAdmin()){ - if (!player.getInventory().containsItems(deco.getItems())) { - player.getPacketDispatch().sendMessage("You don't have the required items to build this."); - return; - } - player.getInventory().remove(deco.getItems()); - } - Location l = object.getLocation(); - Room room = player.getHouseManager().getRooms()[l.getZ()][l.getLocalX() >> 3][l.getLocalY() >> 3]; - Hotspot h = room.getHotspot(hotspot, l.getX(), l.getY()); - if (h != null) { - switch(h.getHotspot().getType()) { - case INDIVIDUAL: - SceneryBuilder.replace(object, object.transform(deco.getObjectId())); - h.setDecorationIndex(hotspot.getDecorationIndex(deco)); - break; - case RECURSIVE: - room.setAllDecorationIndex(hotspot.getDecorationIndex(deco), h.getHotspot()); - Scenery[][] objects = player.getHouseManager().getRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3].getObjects(); - for (int j = 0; j < objects.length; j++) { - for (int k = 0; k < objects[j].length; k++) { - Scenery go = objects[j][k]; - if (go != null && go.getId() == object.getId()) { - SceneryBuilder.replace(go, go.transform(deco.getObjectId())); - } - } - } - break; - case LINKED: - BuildHotspot[] linkedHotspots = BuildHotspot.getLinkedHotspots(h.getHotspot()); - BuildRegionChunk chunk = (BuildRegionChunk) player.getHouseManager().getRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3]; - for (int x = 0; x < 8; x++) { - for (int y = 0; y < 8; y++) { - for(BuildHotspot bh : linkedHotspots) { - int index = chunk.getIndex(x, y, bh.getObjectId()); - Scenery o = chunk.get(x, y, index); - if (o != null && bh.getObjectId() == o.getId()) { - SceneryBuilder.replace(o, o.transform(bh.getDecorations()[0].getObjectId())); - room.setAllDecorationIndex(0, bh); - } - } - } - } - break; - } - } - } - - /** - * Remove the decoration - * @param player the player - * @param object the object to remove - */ - public static void removeDecoration(Player player, Scenery object) { - Location l = object.getLocation(); - Room room = player.getHouseManager().getRooms()[l.getZ()][l.getLocalX() >> 3][l.getLocalY() >> 3]; - for (int i = 0; i < room.getHotspots().length; i++) { - Hotspot hotspot = room.getHotspots()[i]; - if (hotspot.getChunkX() == l.getChunkOffsetX() && hotspot.getChunkY() == l.getChunkOffsetY()) { - switch (hotspot.getHotspot().getType()) { - case INDIVIDUAL: - SceneryBuilder.replace(object, object.transform(hotspot.getHotspot().getObjectId())); - hotspot.setDecorationIndex(-1); - return; - case RECURSIVE: - room.setAllDecorationIndex(-1, hotspot.getHotspot()); - Scenery[][] objects = player.getHouseManager().getRegion().getPlanes()[l.getZ()].getChunks()[l.getLocalX() >> 3][l.getLocalY() >> 3].getObjects(); - for (int j = 0; j < objects.length; j++) { - for (int k = 0; k < objects[j].length; k++) { - Scenery go = objects[j][k]; - if (go != null && go.getId() == object.getId()) { - SceneryBuilder.replace(go, go.transform(hotspot.getHotspot().getObjectId())); - } - } - } - return; - case LINKED: - //TODO - return; - } - } - } - } - - /** - * Builds a room. - * @param player The player. - * @param room The room to build. - */ - public static void buildRoom(Player player, Room room, int z, int x, int y) { - SystemLogger.logErr("Building direction = " + room.getRotation().name()); - if(room.getRotation() != Direction.NORTH){ - room.setRotation(Direction.NORTH); - player.getDialogueInterpreter().sendDialogue("Room rotation is currently disabled."); - } - player.getHouseManager().getRooms()[z][x][y] = room; - player.getPacketDispatch().sendMessage("Building room " + room.getProperties() + "."); - player.getHouseManager().reload(player, true); - } - - /** - * Checks of a room exists - * @param player the player - * @param door the door hotspot the player is trying to build at - * @return true if the room is built already - */ - public static boolean roomExists(Player player, Scenery door) { - int[] location = getRoomPosition(door); - return player.getHouseManager().getRooms()[player.getLocation().getZ()][location[0]][location[1]] != null; - } - - /** - * Gets the room offset. - * @param door The door. - * @return The room offset [x, y]. - */ - public static int[] getRoomPosition(Scenery door) { - Location l = door.getLocation(); - switch (door.getRotation()) { - case 0: //West - return new int[] { (l.getLocalX() >> 3) - 1, l.getLocalY() >> 3 }; - case 1: //North - return new int[] { l.getLocalX() >> 3, (l.getLocalY() >> 3) + 1}; - case 2: //East - return new int[] { (l.getLocalX() >> 3) + 1, l.getLocalY() >> 3 }; - case 3: //South - return new int[] { l.getLocalX() >> 3, (l.getLocalY() >> 3) - 1}; - } - return null; - } - - /** - * Gets the available rotations of the room to build. - * @param exits The exits of the room. - * @param door The door hotspot used. - * @param roomX The room x-coordinate. - * @param roomY The room y-coordinate. - * @return The available rotations for the room [NORTH, EAST, SOUTH, WEST]. - */ - public static Direction[] getAvailableRotations(Player player, boolean[] exits, Scenery door, int roomX, int roomY) { - Direction[] directions = new Direction[4]; - //Exits go to: (0=east, 1=south, 2=west, 3=north) - boolean[] exit = Arrays.copyOf(exits, exits.length); - //Door goes to: (0=west, 1=north, 2=east, 3=south) - int[] info = getExitRequirements(player, door.getLocation().getZ(), roomX, roomY); - for (int i = 0; i < 4; i++) { - boolean success = true; - for (int j = 0; j < 4; j++) { - if (info[j] == 1 && !exit[j]) { - success = false; - break; - } - if (info[j] == -1 && exit[j]) { - success = false; - break; - } - } - if (success) { - directions[i] = Direction.NORTH; - } - System.out.println(i + ": " + Arrays.toString(exit) + " - " + Arrays.toString(info)); - boolean b = exit[0]; - for (int j = 0; j < exit.length - 1; j++) { - exit[j] = exit[j + 1]; - } - exit[exit.length - 1] = b; - } - Direction dir = directions[0]; - directions[0] = directions[2]; - directions[2] = dir; - dir = directions[1]; - directions[1] = directions[3]; - directions[3] = dir; - return directions; - } - - /** - * 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. - */ - private static int[] getExitRequirements(Player player, int z, int roomX, int roomY) { - //1 2 3 0 - //0=east, 1=south, 2=west, 3=north). - int[] exits = new int[4]; - if (roomX == 0) { - exits[2] = -1; - } - else if (player.getHouseManager().hasExit(z, roomX - 1, roomY, Direction.EAST)) { - exits[2] = 1; - } - else if (player.getHouseManager().getRooms()[z][roomX - 1][roomY] != null) { - exits[2] = -1; - } - if (roomY == 7) { - exits[1] = -1; - } - else if (player.getHouseManager().hasExit(z, roomX, roomY + 1, Direction.SOUTH)) { - exits[1] = 1; - } - else if (player.getHouseManager().getRooms()[z][roomX][roomY + 1] != null) { - exits[1] = -1; - } - if (roomX == 7) { - exits[0] = -1; - } - else if (player.getHouseManager().hasExit(z, roomX + 1, roomY, Direction.WEST)) { - exits[0] = 1; - } - else if (player.getHouseManager().getRooms()[z][roomX + 1][roomY] != null) { - exits[0] = -1; - } - if (roomY == 0) { - exits[3] = -1; - } - else if (player.getHouseManager().hasExit(z, roomX, roomY - 1, Direction.NORTH)) { - exits[3] = 1; - } - else if (player.getHouseManager().getRooms()[z][roomX][roomY - 1] != null) { - exits[3] = -1; - } - return exits; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/Servant.java b/Server/src/main/java/core/game/node/entity/skill/construction/Servant.java deleted file mode 100644 index 422765ce1..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/Servant.java +++ /dev/null @@ -1,150 +0,0 @@ -package core.game.node.entity.skill.construction; - - -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 - * - */ -public final class Servant extends NPC { - - /** - * The servant type. - */ - private final ServantType type; - - /** - * The item the servant is carrying. - */ - private Item item; - - /** - * The amount this servant has been used. - */ - private int uses; - - /** - * If the servant is greeting players entering the house. - */ - private boolean greet; - - /** - * Constructs a new {@code Servant} {@code Object}. - * @param type The servant type. - */ - public Servant(ServantType type) { - super(type.getId()); - this.type = type; - } - - /** - * 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. - * @return The servant. - */ - - public static Servant parse(JSONObject data){ - int type = Integer.parseInt( data.get("type").toString()); - Servant servant = new Servant(ServantType.values()[type]); - servant.uses = Integer.parseInt( data.get("uses").toString()); - Object itemRaw = data.get("item"); - if(itemRaw != null){ - JSONObject item = (JSONObject) itemRaw; - servant.item = new Item(Integer.parseInt(item.get("id").toString()),Integer.parseInt(item.get("amount").toString())); - } - servant.greet = (boolean) data.get("greet"); - 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. - */ - public Item getItem() { - return item; - } - - /** - * Sets the item value. - * @param item The item to set. - */ - public void setItem(Item item) { - this.item = item; - } - - /** - * Gets the uses value. - * @return The uses. - */ - public int getUses() { - return uses; - } - - /** - * Sets the uses value. - * @param uses The uses to set. - */ - public void setUses(int uses) { - this.uses = uses; - } - - /** - * Gets the greet value. - * @return The greet. - */ - public boolean isGreet() { - return greet; - } - - /** - * Sets the greet value. - * @param greet The greet to set. - */ - public void setGreet(boolean greet) { - this.greet = greet; - } - - /** - * Gets the type value. - * @return The type. - */ - public ServantType getType() { - return type; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/BeerBarrelPlugin.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/BeerBarrelPlugin.java deleted file mode 100644 index 5ea2e14fd..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/BeerBarrelPlugin.java +++ /dev/null @@ -1,75 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/construction/decoration/costume/package-info.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/costume/package-info.java deleted file mode 100644 index 7236c5772..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/costume/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @author Leewo - * - */ -package core.game.node.entity.skill.construction.decoration.costume; \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/kitchen/BeerBarrelPlugin.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/kitchen/BeerBarrelPlugin.java deleted file mode 100644 index efe2e9f8e..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/kitchen/BeerBarrelPlugin.java +++ /dev/null @@ -1,76 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/construction/decoration/kitchen/LarderPlugin.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/kitchen/LarderPlugin.java deleted file mode 100644 index 2d8b3f44e..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/kitchen/LarderPlugin.java +++ /dev/null @@ -1,148 +0,0 @@ -package core.game.node.entity.skill.construction.decoration.kitchen; - - -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.content.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 rs09.plugin.PluginManager; - -/** - * Handles the interactions for the three Larders. - * @author Splinter - */ -@Initializable -public final class LarderPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.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/java/core/game/node/entity/skill/construction/decoration/kitchen/ShelfPlugin.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/kitchen/ShelfPlugin.java deleted file mode 100644 index 604deec36..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/kitchen/ShelfPlugin.java +++ /dev/null @@ -1,238 +0,0 @@ -package core.game.node.entity.skill.construction.decoration.kitchen; - - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.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 rs09.plugin.PluginManager; - -/** - * Handles the shelves in the kitchen room. - * @author Splinter - */ -@Initializable -public final class ShelfPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - PluginManager.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/java/core/game/node/entity/skill/construction/decoration/questhall/MountedGloryPlugin.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/questhall/MountedGloryPlugin.java deleted file mode 100644 index bc41e7ec0..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/questhall/MountedGloryPlugin.java +++ /dev/null @@ -1,95 +0,0 @@ -package core.game.node.entity.skill.construction.decoration.questhall; - - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.dialogue.DialogueAction; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import rs09.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(200); - 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/java/core/game/node/entity/skill/construction/decoration/workshop/ToolsPlugin.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/workshop/ToolsPlugin.java deleted file mode 100644 index e12b7f104..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/workshop/ToolsPlugin.java +++ /dev/null @@ -1,171 +0,0 @@ -package core.game.node.entity.skill.construction.decoration.workshop; - - -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.content.dialogue.DialogueInterpreter; -import core.game.content.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 rs09.plugin.PluginManager; - -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); - } - PluginManager.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/java/core/game/node/entity/skill/cooking/CakeMakingPlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/CakeMakingPlugin.java deleted file mode 100644 index c82006d2c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/CakeMakingPlugin.java +++ /dev/null @@ -1,69 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/cooking/CoconutMakePlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/CoconutMakePlugin.java deleted file mode 100644 index d4d54e83c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/CoconutMakePlugin.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/cooking/CookableItems.java b/Server/src/main/java/core/game/node/entity/skill/cooking/CookableItems.java deleted file mode 100644 index 669c725a3..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/CookableItems.java +++ /dev/null @@ -1,148 +0,0 @@ -package core.game.node.entity.skill.cooking; - -import org.rs09.consts.Items; -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); - - 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; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/CookingDialoguePlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/CookingDialoguePlugin.java deleted file mode 100644 index e9f23d2da..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/CookingDialoguePlugin.java +++ /dev/null @@ -1,199 +0,0 @@ -/* -package core.game.node.entity.skill.cooking; - -import core.game.content.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/java/core/game/node/entity/skill/cooking/CookingPlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/CookingPlugin.java deleted file mode 100644 index c20c569ba..000000000 --- a/Server/src/main/java/core/game/node/entity/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.content.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/java/core/game/node/entity/skill/cooking/CookingPulse.java b/Server/src/main/java/core/game/node/entity/skill/cooking/CookingPulse.java deleted file mode 100644 index f31c02aec..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/cooking/CookingRecipePlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/CookingRecipePlugin.java deleted file mode 100644 index ad823f822..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/CookingRecipePlugin.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.node.entity.skill.cooking; - -import core.plugin.Initializable; -import core.game.node.entity.skill.cooking.recipe.Recipe; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.item.Item; -import core.plugin.Plugin; - -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; - 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) { - recipe.mix(event.getPlayer(), event); - 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/java/core/game/node/entity/skill/cooking/CookingRewrite.kt b/Server/src/main/java/core/game/node/entity/skill/cooking/CookingRewrite.kt deleted file mode 100644 index e9018f88e..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/CookingRewrite.kt +++ /dev/null @@ -1,68 +0,0 @@ -package core.game.node.entity.skill.cooking - -import core.game.node.scenery.Scenery -import core.game.node.entity.player.Player -import core.game.node.item.Item -import org.rs09.consts.Items -import org.rs09.consts.Items.BREAD_DOUGH_2307 -import org.rs09.consts.Items.RAW_BEEF_2132 -import org.rs09.consts.Items.SEAWEED_401 -import org.rs09.consts.Items.UNCOOKED_CAKE_1889 -import rs09.game.interaction.InteractionListener -import rs09.game.node.entity.skill.cooking.CookingDialogue - -//author: Ceik -class CookingRewrite : InteractionListener() { - - val RAW_FOODS: IntArray - - init { - val list = CookableItems.values().map { it.raw }.toMutableList() - list.add(Items.COOKED_MEAT_2142) - list.add(RAW_BEEF_2132) - list.add(SEAWEED_401) - RAW_FOODS = list.toIntArray() - } - - override fun defineListeners() { - - onUseWith(SCENERY,RAW_FOODS, *COOKING_OBJs){ player, used, with -> - val item = used.asItem() - val obj = with.asScenery() - val range = obj.name.toLowerCase().contains("range") - when (item.id) { - RAW_BEEF_2132 -> if (range) { - player.dialogueInterpreter.open(CookingDialogue(item.id,9436,true,obj)) - return@onUseWith true - } - BREAD_DOUGH_2307, UNCOOKED_CAKE_1889 -> if (!range) { - player.packetDispatch.sendMessage("You need to cook this on a range.") - return@onUseWith false - } - } - - //cook a standard item - player.dialogueInterpreter.open(CookingDialogue(item.id,obj)) - 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) - - @JvmStatic - fun cook(player: Player, `object`: Scenery?, initial: Int, product: Int, amount: Int) { - val food = Item(initial) - if (food.name.toLowerCase().contains("pizza")) { - player.pulseManager.run(PizzaCookingPulse(player, `object`, initial, product, amount)) - } else if (food.name.toLowerCase().contains("pie")) { - player.pulseManager.run(PieCookingPulse(player, `object`, initial, product, amount)) - } else if (CookableItems.intentionalBurn(initial)) { - player.pulseManager.run(IntentionalBurnPulse(player, `object`, initial, product, amount)) - } else { - player.pulseManager.run(StandardCookingPulse(player, `object`, initial, product, amount)) - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/MakeDoughPlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/MakeDoughPlugin.java deleted file mode 100644 index 6a01b0652..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/MakeDoughPlugin.java +++ /dev/null @@ -1,160 +0,0 @@ -package core.game.node.entity.skill.cooking; - -import core.plugin.Initializable; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -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.Plugin; - -/** - * Represents the plugin used to make dough. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class MakeDoughPlugin extends UseWithHandler { - - /** - * Represents the water sources. - */ - private static final Item[][] SOURCES = new Item[][] { { new Item(1929), new Item(1925) }, { new Item(1921), new Item(1923) }, { new Item(1937), new Item(1935) } }; - - /** - * Constructs a new {@code MakeDoughPlugin} {@code Object}. - */ - public MakeDoughPlugin() { - super(1933); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i = 0; i < SOURCES.length; i++) { - addHandler(SOURCES[i][0].getId(), ITEM_TYPE, this); - } - new MakeDoughDialogue().init(); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player.getAttributes().containsKey("tut-island")) { - return handleTutorialIsland(player, event); - } - Item[] data = null; - for (int i = 0; i < SOURCES.length; i++) { - if (SOURCES[i][0].getId() == event.getUsedItem().getId() || SOURCES[i][0].getId() == event.getBaseItem().getId()) { - data = SOURCES[i]; - break; - } - } - player.getDialogueInterpreter().open(59 << 16 | 1, (Object) data); - return false; - } - - /** - * Method used to handle the tutorial isalnd making dough. - * @param player the player. - * @param event the event. - */ - private boolean handleTutorialIsland(final Player player, final NodeUsageEvent event) { - if (player.getInventory().freeSlots() == 0) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - return true; - } - player.lock(3); - player.getInventory().remove(event.getBaseItem(), new Item(1933, 1)); - player.getInventory().add(new Item(3727, 1), new Item(1931, 1), new Item(2307, 1)); - if (TutorialSession.getExtension(player).getStage() == 19) { - TutorialStage.load(player, 20, false); - } - return true; - } - - /** - * Represents the plugin used to make dough.. - * @author 'Vexia - * @version 1.0 - */ - public static final class MakeDoughDialogue extends DialoguePlugin { - - /** - * Represents the dough items to make. - */ - private static final Item[] DOUGHS = new Item[] { new Item(2307), new Item(1953), new Item(2283), new Item(1863) }; - - /** - * Represents the pot of flour item. - */ - private static final Item FLOUR = new Item(1933); - - /** - * Represents the water source data. - */ - private Item[] waterData; - - /** - * Constructs a new {@code MakeDoughDialogue} {@code Object}. - */ - public MakeDoughDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MakeDoughDialogue} {@code Object}. - * @param player the player. - */ - public MakeDoughDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MakeDoughDialogue(player); - } - - @Override - public boolean open(Object... args) { - waterData = (Item[]) args[0]; - interpreter.sendOptions("What do you wish to make?", "Bread dough.", "Pastry dough.", "Pizza dough.", "Pitta dough."); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - makeDough(DOUGHS[buttonId - 1]); - end(); - break; - } - return true; - } - - /** - * Method used to make a dough item. - * @param dough the dough. - */ - private void makeDough(final Item dough) { - if (player.getInventory().remove(waterData[0]) && player.getInventory().remove(FLOUR)) { - player.getInventory().add(waterData[1]); - player.getInventory().add(dough); - player.getInventory().add(new Item(1931), player); - player.getPacketDispatch().sendMessage("You mix the flower and water to make some " + dough.getName().toLowerCase() + "."); - } - } - - @Override - public int[] getIds() { - return new int[] { 59 << 16 | 1 }; - } - - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/NettleTeaPlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/NettleTeaPlugin.java deleted file mode 100644 index 40464a6b6..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/NettleTeaPlugin.java +++ /dev/null @@ -1,61 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/cooking/NettleWaterPlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/NettleWaterPlugin.java deleted file mode 100644 index 2b851ddd9..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/NettleWaterPlugin.java +++ /dev/null @@ -1,35 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/cooking/PieShellPlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/PieShellPlugin.java deleted file mode 100644 index 57baf4c4f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/PieShellPlugin.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/cooking/SkeweredFoodPlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/SkeweredFoodPlugin.java deleted file mode 100644 index 21b08357c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/SkeweredFoodPlugin.java +++ /dev/null @@ -1,111 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/cooking/StandardCookingPulse.java b/Server/src/main/java/core/game/node/entity/skill/cooking/StandardCookingPulse.java deleted file mode 100644 index bf2dc716c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/StandardCookingPulse.java +++ /dev/null @@ -1,249 +0,0 @@ -package core.game.node.entity.skill.cooking; - -import core.game.container.impl.EquipmentContainer; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -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.player.link.diary.DiaryType; -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.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; - -public class StandardCookingPulse extends Pulse { - //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(2577, 1, 1); - - private int initial, product, amount, level; - private Scenery object; - private Player player; - private double experience; - private boolean burned = false; - - 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; - } - - @Override - public void start() { - if (checkRequirements()) { - super.start(); - cook(player, object, CookableItems.cookingMap.get(initial) != null && isBurned(player, object, initial), initial, product); - amount--; - } - } - - @Override - public boolean pulse() { - if (!checkRequirements()) { - return true; - } - return reward(); - } - - public void animate() { - player.animate(getAnimation(object)); - } - - public boolean checkRequirements() { - CookableItems properties = CookableItems.forId(initial); - this.level = 1; - this.experience = 0; - if (properties != null) { - // Handle Cook's Assistant range - if (object.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; - } - - //check level - if (player.getSkills().getLevel(Skills.COOKING) < properties.level) { - player.getDialogueInterpreter().sendDialogue("You need a cooking level of " + properties.level + " to cook this."); - return false; - } - - this.level = properties.level; - this.experience = properties.experience; - this.burned = isBurned(player, object, initial); - } - if (amount < 1) { - return false; - } - - if(!object.isActive()){ - return false; - } - return true; - } - - public boolean reward() { - if (getDelay() == 1) { - setDelay(object.getName().toLowerCase().equals("range") ? 5 : 4); - return false; - } - //handle tutorial stuff - if (!TutorialSession.getExtension(player).finished()) { - updateTutorial(player); - amount--; - return true; - } - - if (cook(player, object, burned, initial, product)) { - amount--; - } else { - return true; - } - return amount < 1; - } - - 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); - CookableItems item = CookableItems.forId(food); - if (hasGauntlets && (food == Items.RAW_SWORDFISH_371 || food == Items.RAW_LOBSTER_377 || food == Items.RAW_SHARK_383)) { - burn_stop -= 6; - } - if (player.getSkills().getLevel(Skills.COOKING) > burn_stop) { - return false; - } - int cook_level = player.getSkills().getLevel(Skills.COOKING); - 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); - 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); - player.lock(getDelay()); - animate(); - - //lumbridge diary - if (object.getId() == 114 && player.getViewport().getRegion().getId() == 12850 && !player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE).isComplete(0, 7)) { - player.getAchievementDiaryManager().updateTask(player, DiaryType.LUMBRIDGE, 0, 7, true); - } - //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_BIRD_MEAT_9984: - case Items.RAW_RABBIT_3226: // Iron spits - case Items.IRON_SPIT_7225: - if (RandomFunction.random(15) == 5) { - player.getPacketDispatch().sendMessage("Your iron spit seems to have broken in the process."); - } else { - if (!player.getInventory().add(new Item(Items.IRON_SPIT_7225))) { - GroundItemManager.create(new Item(Items.IRON_SPIT_7225), player.getLocation(), player); - } - } - break; - case Items.UNCOOKED_CAKE_1889: //cake - if (!player.getInventory().add(new Item(Items.CAKE_TIN_1887))) { - GroundItemManager.create(new Item(Items.CAKE_TIN_1887), player); - } - break; - } - if (player.getInventory().remove(initialItem)) { - if (!burned) { - player.getInventory().add(productItem); - player.getSkills().addExperience(Skills.COOKING, experience, true); - - int playerRegion = player.getViewport().getRegion().getId(); - - // Achievement Diary Handling - if (productItem.getId() == Items.BASS_365 - && playerRegion == 11317 - && player.getAttribute("diary:seers:bass-caught", false)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 11); - } - - if (productItem.getId() == Items.SHARK_385 - && playerRegion == 11317 - && player.getEquipment().get(EquipmentContainer.SLOT_HANDS) != null && player.getEquipment().get(EquipmentContainer.SLOT_HANDS).getId() == Items.COOKING_GAUNTLETS_775 - && !player.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 8)) { - player.setAttribute("/save:diary:seers:cooked-shark", 1 + player.getAttribute("diary:seers:cooked-shark", 0)); - if (player.getAttribute("diary:seers:cooked-shark", 0) >= 5) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 8); - } - } - - // Cook some rat meat on a campfire in Lumbridge Swamp - System.out.println(object.getName()); - if (initialItem.getId() == Items.RAW_RAT_MEAT_2134 && object.getName().toLowerCase().contains("fire") && (playerRegion == 12593 || playerRegion == 12849)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 10); - } - - // Cook a lobster on the range in Lumbridge Castle kitchen - if (productItem.getId() == Items.LOBSTER_379 && object.getId() == 114 && player.getLocation().withinDistance(Location.create(3211, 3215, 0))) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 2, 4); - } - - } else { - player.getInventory().add(CookableItems.getBurnt(initial)); - } - player.getPacketDispatch().sendMessage(getMessage(initialItem, productItem, burned)); - player.getAudioManager().send(SOUND); - return true; - } - return false; - } - - public String getMessage(Item food, Item product, boolean burned) { - if (food.getId() == Items.RAW_OOMLIE_2337) { - return "The meat is far too delicate to cook like this. Perhaps you should wrap something around it to protect it from the heat."; - } - switch (product.getId()) { - case Items.SINEW_9436: - return "You dry the meat into sinew."; - case Items.SODA_ASH_1781: - return "You burn the seaweed into soda ash."; - } - 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 ", ""); - } - } - - public boolean updateTutorial(Player player) { - if (TutorialSession.getExtension(player).getStage() == 14) { - TutorialStage.load(player, 15, false); - return cook(player, object, true, initial, product); - } else if (TutorialSession.getExtension(player).getStage() == 15) { - TutorialStage.load(player, 16, false); - return cook(player, object, false, initial, product); - } - if (TutorialSession.getExtension(player).getStage() == 20) { - TutorialStage.load(player, 21, false); - return cook(player, object, false, initial, product); - } - return cook(player, object, burned, initial, product); - } - - private Animation getAnimation(final Scenery object) { - return !object.getName().toLowerCase().equals("fire") ? RANGE_ANIMATION : FIRE_ANIMATION; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/WatermelonSlicePlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/WatermelonSlicePlugin.java deleted file mode 100644 index 1fd4040a1..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/WatermelonSlicePlugin.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/cooking/WineFermentPlugin.java b/Server/src/main/java/core/game/node/entity/skill/cooking/WineFermentPlugin.java deleted file mode 100644 index b9a8a3d80..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/WineFermentPlugin.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.node.entity.skill.cooking; - -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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 rs09.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/java/core/game/node/entity/skill/cooking/recipe/OomlieWrap.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/OomlieWrap.java deleted file mode 100644 index e215eaf4a..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/OomlieWrap.java +++ /dev/null @@ -1,57 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/Recipe.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/Recipe.java deleted file mode 100644 index c6478be14..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/Recipe.java +++ /dev/null @@ -1,127 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe; - -import core.game.node.entity.skill.cooking.recipe.cake.ChocolateCake; -import core.game.node.entity.skill.cooking.recipe.pie.impl.*; -import core.game.node.entity.skill.cooking.recipe.pizza.impl.AnchovyPizza; -import core.game.node.entity.skill.cooking.recipe.pizza.impl.MeatPizza; -import core.game.node.entity.skill.cooking.recipe.pizza.impl.PineapplePizza; -import core.game.node.entity.skill.cooking.recipe.pizza.impl.PlainPizza; -import core.game.node.entity.skill.cooking.recipe.potato.impl.*; -import core.game.node.entity.skill.cooking.recipe.stew.CurryRecipe; -import core.game.node.entity.skill.cooking.recipe.stew.StewRecipe; -import core.game.node.entity.skill.cooking.recipe.topping.impl.*; -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.. - */ - 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/java/core/game/node/entity/skill/cooking/recipe/cake/ChocolateCake.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/cake/ChocolateCake.java deleted file mode 100644 index 72280ce18..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/cake/ChocolateCake.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.cake; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.cooking.recipe.Recipe; -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/java/core/game/node/entity/skill/cooking/recipe/pie/PieRecipe.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/PieRecipe.java deleted file mode 100644 index 00fb85af5..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/PieRecipe.java +++ /dev/null @@ -1,38 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/AdmiralPie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/AdmiralPie.java deleted file mode 100644 index 652bc3549..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/AdmiralPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/ApplePie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/ApplePie.java deleted file mode 100644 index 2956dd38b..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/ApplePie.java +++ /dev/null @@ -1,34 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/FishPie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/FishPie.java deleted file mode 100644 index 7a2286891..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/FishPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/GardenPie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/GardenPie.java deleted file mode 100644 index 04bd3066f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/GardenPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/MeatPie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/MeatPie.java deleted file mode 100644 index 76add066f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/MeatPie.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/MudPie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/MudPie.java deleted file mode 100644 index 64ef1750f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/MudPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/RedberryPie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/RedberryPie.java deleted file mode 100644 index 8c7920785..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/RedberryPie.java +++ /dev/null @@ -1,33 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/SummerPie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/SummerPie.java deleted file mode 100644 index 689548cb5..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/SummerPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pie/impl/WildPie.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/WildPie.java deleted file mode 100644 index 0e9c8f0fb..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pie/impl/WildPie.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pie.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pizza/PizzaRecipe.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/PizzaRecipe.java deleted file mode 100644 index 0471e4a11..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/PizzaRecipe.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pizza; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/AnchovyPizza.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/AnchovyPizza.java deleted file mode 100644 index 4fc09e82a..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/AnchovyPizza.java +++ /dev/null @@ -1,44 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pizza.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/MeatPizza.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/MeatPizza.java deleted file mode 100644 index 9b9d8f903..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/MeatPizza.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pizza.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/PineapplePizza.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/PineapplePizza.java deleted file mode 100644 index e93097a0a..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/PineapplePizza.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pizza.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/PlainPizza.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/PlainPizza.java deleted file mode 100644 index 01c85e937..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/pizza/impl/PlainPizza.java +++ /dev/null @@ -1,81 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.pizza.impl; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/potato/PotatoRecipe.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/PotatoRecipe.java deleted file mode 100644 index 6eee0e3f5..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/PotatoRecipe.java +++ /dev/null @@ -1,76 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.potato; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/potato/impl/ButterPotato.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/ButterPotato.java deleted file mode 100644 index c3b445a60..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/ButterPotato.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.potato.impl; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.cooking.recipe.Recipe; -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/java/core/game/node/entity/skill/cooking/recipe/potato/impl/CheesePotato.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/CheesePotato.java deleted file mode 100644 index fe1a1ea5d..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/CheesePotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.potato.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/potato/impl/ChilliPotato.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/ChilliPotato.java deleted file mode 100644 index 40683e915..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/ChilliPotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.potato.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/potato/impl/EggPotato.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/EggPotato.java deleted file mode 100644 index 6dd5f7d2f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/EggPotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.potato.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/potato/impl/MushroomPotato.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/MushroomPotato.java deleted file mode 100644 index 56fa4d669..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/MushroomPotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.potato.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/potato/impl/TunaPotato.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/TunaPotato.java deleted file mode 100644 index e9466ac45..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/potato/impl/TunaPotato.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.potato.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/stew/CurryRecipe.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/stew/CurryRecipe.java deleted file mode 100644 index 781ba4725..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/stew/CurryRecipe.java +++ /dev/null @@ -1,89 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.stew; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/stew/StewRecipe.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/stew/StewRecipe.java deleted file mode 100644 index 17f6c4a08..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/stew/StewRecipe.java +++ /dev/null @@ -1,92 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.stew; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/ToppingRecipe.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/ToppingRecipe.java deleted file mode 100644 index 4285360f5..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/ToppingRecipe.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChilliConCarne.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChilliConCarne.java deleted file mode 100644 index ad64d0baf..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChilliConCarne.java +++ /dev/null @@ -1,81 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChoppedOnion.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChoppedOnion.java deleted file mode 100644 index a3d560f1c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChoppedOnion.java +++ /dev/null @@ -1,69 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChoppedTuna.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChoppedTuna.java deleted file mode 100644 index f0f3c6428..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/ChoppedTuna.java +++ /dev/null @@ -1,69 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/EggAndTomato.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/EggAndTomato.java deleted file mode 100644 index cebfaf538..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/EggAndTomato.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.cooking.recipe.Recipe; -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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/MushroomAndOnion.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/MushroomAndOnion.java deleted file mode 100644 index 27a3b8f55..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/MushroomAndOnion.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/SlicedMushroom.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/SlicedMushroom.java deleted file mode 100644 index 6ec41c7fc..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/SlicedMushroom.java +++ /dev/null @@ -1,69 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/SpicySauce.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/SpicySauce.java deleted file mode 100644 index d4a8d6f1f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/SpicySauce.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/TunaAndCorn.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/TunaAndCorn.java deleted file mode 100644 index dbebeeb49..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/TunaAndCorn.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/cooking/recipe/topping/impl/UncookedEgg.java b/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/UncookedEgg.java deleted file mode 100644 index c77d33c69..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/recipe/topping/impl/UncookedEgg.java +++ /dev/null @@ -1,54 +0,0 @@ -package core.game.node.entity.skill.cooking.recipe.topping.impl; - -import core.game.node.entity.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/java/core/game/node/entity/skill/crafting/BattleStaves.java b/Server/src/main/java/core/game/node/entity/skill/crafting/BattleStaves.java deleted file mode 100644 index 7a88f3d32..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/BattleStaves.java +++ /dev/null @@ -1,122 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/crafting/BattlestaveMakePlugin.java b/Server/src/main/java/core/game/node/entity/skill/crafting/BattlestaveMakePlugin.java deleted file mode 100644 index ef4386e63..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/BattlestaveMakePlugin.java +++ /dev/null @@ -1,66 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/crafting/GlassProduct.java b/Server/src/main/java/core/game/node/entity/skill/crafting/GlassProduct.java deleted file mode 100644 index fd9dd21da..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/GlassProduct.java +++ /dev/null @@ -1,105 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/crafting/JewelleryCraftPlugin.java b/Server/src/main/java/core/game/node/entity/skill/crafting/JewelleryCraftPlugin.java deleted file mode 100644 index 5ea7d4714..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/JewelleryCraftPlugin.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.game.node.entity.skill.crafting; - -import core.plugin.Initializable; -import core.game.node.entity.skill.crafting.jewellery.JewelleryCrafting; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.plugin.Plugin; - -/** - * Represents the plugin used to craft jewellery. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -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 }; - - /** - * Constructs a new {@code JewelleryCraftPlugin} {@code Object}. - */ - public JewelleryCraftPlugin() { - super(2357); - } - - @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) { - JewelleryCrafting.open(event.getPlayer()); - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/MoltenGlassMakePlugin.java b/Server/src/main/java/core/game/node/entity/skill/crafting/MoltenGlassMakePlugin.java deleted file mode 100644 index 0714862d1..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/MoltenGlassMakePlugin.java +++ /dev/null @@ -1,121 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/crafting/SilverProduct.java b/Server/src/main/java/core/game/node/entity/skill/crafting/SilverProduct.java deleted file mode 100644 index 6743a4a63..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/SilverProduct.java +++ /dev/null @@ -1,138 +0,0 @@ -package core.game.node.entity.skill.crafting; - -/** - * Represents a silver product. - * @author 'Vexia - */ -public enum SilverProduct { - UNBLESSED(16, 1599, 1714, 16, 50, 1716), - UNHOLY(23, 1594, 1720, 17, 50, 1724), - SICKLE(30, 2976, 2961, 18, 50, -1), - TIARA(44, 5523, 5525, 23, 52.5, -1), - DEMONIC_SIGIL(59, 6747, 6748, 20, 50, -1), - SILVTHRIL_CHAIN(73, 13153, 13154, 1, 100, -1), - LIGHTNING_ROD(37, 4200, 4201, 20, 50, -1), - SILVTHRIL_ROD(52, 7649, 7637, 99, 1, -1), - SILVER_BOLTS(66, 9434, 9382, 21, 50, -1); - /** - * The button id. - */ - private final int button; - - /** - * The product item. - */ - private final int product; - - /** - * The item needed. - */ - private final int needed; - - /** - * The level needed. - */ - private final int level; - - /** - * The exp gained. - */ - private final double exp; - - /** - * The strung id. - */ - private final int strung; - - /** - * Constructs a new {@code SilverDefinitions.java} {@Code Object} - * @param button - * @param product - * @param level - * @param exp - */ - SilverProduct(int button, int needed, int product, int level, double exp, int strung) { - this.button = button; - this.needed = needed; - this.product = product; - this.level = level; - this.exp = exp; - this.strung = strung; - } - - /** - * returns the value. - * @param id - * @return - */ - public static SilverProduct forId(int id) { - for (SilverProduct silver : SilverProduct.values()) { - if (silver.button == id) { - return silver; - } - } - return null; - } - - /** - * Gets the button. - * @return The button. - */ - public int getButton() { - return button; - } - - /** - * Gets the product. - * @return The product. - */ - public int getProduct() { - return product; - } - - /** - * Gets the needed. - * @return The needed. - */ - public int getNeeded() { - return needed; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the exp. - * @return The exp. - */ - public double getExp() { - return exp; - } - - /** - * Gets the strung. - * @return The strung. - */ - public int getStrung() { - return strung; - } - - /** - * returns the value. - * @param id - * @return - */ - public static SilverProduct forProductId(int id) { - for (SilverProduct silver : SilverProduct.values()) { - if (silver.getProduct() == id) { - return silver; - } - } - return null; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/crafting/SilverStringingPlugin.java b/Server/src/main/java/core/game/node/entity/skill/crafting/SilverStringingPlugin.java deleted file mode 100644 index aff1fca3c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/crafting/SilverStringingPlugin.java +++ /dev/null @@ -1,49 +0,0 @@ -package core.game.node.entity.skill.crafting; - -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 string jewelry. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SilverStringingPlugin extends UseWithHandler { - - /** - * Constructs a new {@code SilverStringingPlugin} {@code Object}. - */ - public SilverStringingPlugin() { - super(1714, 1720); - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - SilverProduct silver = null; - if (event.getUsedItem().getId() != 1759) { - silver = SilverProduct.forProductId(event.getUsedItem().getId()); - } else if (((Item) event.getUsedWith()).getId() != 1759) { - silver = SilverProduct.forProductId((((Item) event.getUsedWith()).getId())); - } - if (silver == null) { - return true; - } - player.getInventory().remove(new Item(silver.getProduct(), 1)); - player.getInventory().remove(new Item(1759, 1)); - player.getInventory().add(new Item(silver.getStrung(), 1)); - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(1759, ITEM_TYPE, this); - return null; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingOptionPlugin.java b/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingOptionPlugin.java deleted file mode 100644 index 7c5ab6f4c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingOptionPlugin.java +++ /dev/null @@ -1,47 +0,0 @@ -package core.game.node.entity.skill.firemaking; - -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -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 handle the lighting of a fire. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FireMakingOptionPlugin extends UseWithHandler { - - /** - * Constructs a new {@code FireMakingOptionPlugin} {@code Object}. - */ - public FireMakingOptionPlugin() { - super(1511, 1521, 1513, 1515, 1517, 1519, 1521, 2862, 3438, 3440, 3442, 3444, 3446, 3448, 6211, 6213, 6332, 6333, 7404, 7405, 7406, 8934, 9067, 10328, 10329, 10808, 10810, 10812, 11035, 12581, 12583, 3125); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(590, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - Item item = event.getUsedItem(); - if (item == null) { - return false; - } - Player player = event.getPlayer(); - if (TutorialSession.getExtension(player).getStage() == 8) { - TutorialStage.load(player, 9, false); - } - player.getPulseManager().run(new FireMakingPulse(player, item, null)); - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/fishing/Fish.java b/Server/src/main/java/core/game/node/entity/skill/fishing/Fish.java deleted file mode 100644 index 9afad3b80..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fishing/Fish.java +++ /dev/null @@ -1,115 +0,0 @@ -package core.game.node.entity.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), - SHRIMP(new Item(317), 1, 10), - SARDINE(new Item(327), 5, 20), - KARAMBWANJI(new Item(3150), 5, 5), - HERRING(new Item(345), 10, 30), - ANCHOVIE(new Item(321), 15, 40), - MACKEREL(new Item(353), 16, 20), - TROUT(new Item(335), 20, 50), - COD(new Item(341), 23, 45), - PIKE(new Item(349), 25, 60), - SLIMY_EEL(new Item(3379), 28, 65), - SALMON(new Item(331), 30, 70), - FROG_SPAWN(new Item(5004), 33, 75), - TUNA(new Item(359), 35, 80), - RAINBOW_FISH(new Item(10138), 38, 80), - CAVE_EEL(new Item(5001), 38, 80), - LOBSTER(new Item(377), 40, 90), - BASS(new Item(363), 46, 100), - SWORDFISH(new Item(371), 50, 100), - LAVA_EEL(new Item(2148), 53, 100), - MONKFISH(new Item(7944), 62, 120), - KARAMBWAN(new Item(3142), 65, 105), - SHARK(new Item(383), 76, 110), - SEA_TURTLE(new Item(395), 79, 38), - MANTA_RAY(new Item(389), 81, 46), - SEAWEED(new Item(401), 16, 1), - CASKET(new Item(405), 16, 10), - OYSTER(new Item(407), 16, 10), - DARK_CRAB(new Item(14937), 85, 130); - - /** - * 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 int... npcs) { - this.item = item; - this.level = level; - this.experience = experience; - this.npcs = npcs; - } - - 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; - - /** - * 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; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/fishing/FishSpots.java b/Server/src/main/java/core/game/node/entity/skill/fishing/FishSpots.java deleted file mode 100644 index a4df9529c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fishing/FishSpots.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/fishing/FishingOption.java b/Server/src/main/java/core/game/node/entity/skill/fishing/FishingOption.java deleted file mode 100644 index 303619c1b..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fishing/FishingOption.java +++ /dev/null @@ -1,201 +0,0 @@ -package core.game.node.entity.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.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), 1, Animation.create(621), null, "net", Fish.MONKFISH), - C_CAGE(new Item(301), 85, Animation.create(619), new Item[]{new Item(14943)}, "cage", Fish.DARK_CRAB), - 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); - - public static 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. - */ - private 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; - this.fish = fish; - } - - /** - * Method used to get a random {@link Fish}. - * @return the {@link Fish}. - */ - public Fish getRandomFish(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; - } - } - if (this == LURE && player.getInventory().contains(10087, 1)) { - return Fish.RAINBOW_FISH; - } - Fish reward = fish[RandomFunction.randomize(fish.length)]; - if (reward.getLevel() > player.getSkills().getLevel(Skills.FISHING) || (reward == Fish.RAINBOW_FISH && !player.getInventory().contains(10087, 1))) { - reward = fish[0]; - } - return reward; - } - - /** - * 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/java/core/game/node/entity/skill/fishing/FishingSpot.java b/Server/src/main/java/core/game/node/entity/skill/fishing/FishingSpot.java deleted file mode 100644 index 57d115c0c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fishing/FishingSpot.java +++ /dev/null @@ -1,83 +0,0 @@ -package core.game.node.entity.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, 2067, 2068, 2724, 4908, 5748, 5749, 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), - CRAB_CAGE(new int[] {8665}, FishingOption.C_CAGE), - SPOT_KBWANJI(new int[] {1174}, FishingOption.KBWANJI_NET), - SPOT_KARAMBWAN(new int[] {1177}, FishingOption.KARAMBWAN_VES); - - public static HashMap spotMap = new HashMap<>(); - - static{ - FishingSpot[] spots = values(); - int spotsLength = spots.length; - for (int x = 0; x < spotsLength; x++) { - FishingSpot spot = spots[x]; - int[] spotIds = spot.ids; - int spotIdsLength = spotIds.length; - for (int y = 0; y < spotIdsLength; y++) { - spotMap.putIfAbsent(spotIds[y],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. - */ - private 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; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/fletching/BoltCreatePlugin.java b/Server/src/main/java/core/game/node/entity/skill/fletching/BoltCreatePlugin.java deleted file mode 100644 index 222f319a3..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/BoltCreatePlugin.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -package core.game.node.entity.skill.fletching; - -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.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/java/core/game/node/entity/skill/fletching/BoltGemPlugin.java b/Server/src/main/java/core/game/node/entity/skill/fletching/BoltGemPlugin.java deleted file mode 100644 index 8867bf0b1..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/BoltGemPlugin.java +++ /dev/null @@ -1,45 +0,0 @@ -//package core.game.node.entity.skill.fletching; -// -//import rs09.game.content.dialogue.SkillDialogueHandler; -//import rs09.game.content.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/java/core/game/node/entity/skill/fletching/DartCreatePlugin.java b/Server/src/main/java/core/game/node/entity/skill/fletching/DartCreatePlugin.java deleted file mode 100644 index c43104095..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/FletchItem.java b/Server/src/main/java/core/game/node/entity/skill/fletching/FletchItem.java deleted file mode 100644 index fee42d6c1..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/FletchType.java b/Server/src/main/java/core/game/node/entity/skill/fletching/FletchType.java deleted file mode 100644 index 33509c1ab..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/Fletching.java b/Server/src/main/java/core/game/node/entity/skill/fletching/Fletching.java deleted file mode 100644 index 55c26e67d..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/Fletching.java +++ /dev/null @@ -1,349 +0,0 @@ -package core.game.node.entity.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), - 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), - - //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/java/core/game/node/entity/skill/fletching/FletchingPlugin.java b/Server/src/main/java/core/game/node/entity/skill/fletching/FletchingPlugin.java deleted file mode 100644 index 6e59e8588..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/FletchingPlugin.java +++ /dev/null @@ -1,133 +0,0 @@ -package core.game.node.entity.skill.fletching; - -import org.rs09.consts.Items; -import rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; -import core.game.node.entity.skill.fletching.items.bolts.BoltPulse; -import core.game.node.entity.skill.fletching.items.darts.DartPulse; -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,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/java/core/game/node/entity/skill/fletching/FletchingPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/FletchingPulse.java deleted file mode 100644 index 2a4f38f70..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/FletchingPulse.java +++ /dev/null @@ -1,107 +0,0 @@ -package core.game.node.entity.skill.fletching; - -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); - } - 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); - 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/java/core/game/node/entity/skill/fletching/GemBoltPlugin.java b/Server/src/main/java/core/game/node/entity/skill/fletching/GemBoltPlugin.java deleted file mode 100644 index 90564feb4..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/GemBoltPlugin.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.node.entity.skill.fletching; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.entity.skill.fletching.items.gem.GemBoltPulse; -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 rs09.game.content.dialogue.SkillDialogueHandler; -import rs09.game.content.dialogue.SkillDialogueHandler.SkillDialogue; -import rs09.game.node.entity.skill.fletching.items.gem.GemBoltCutPulse; -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/java/core/game/node/entity/skill/fletching/items/arrow/ArrowHead.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/arrow/ArrowHead.java deleted file mode 100644 index c7ab5ec7c..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/items/arrow/ArrowHeadPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/arrow/ArrowHeadPulse.java deleted file mode 100644 index 2d3dd0870..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/arrow/ArrowHeadPulse.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.node.entity.skill.fletching.items.arrow; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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 (!player.getSlayer().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/java/core/game/node/entity/skill/fletching/items/arrow/HeadlessArrowPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/arrow/HeadlessArrowPulse.java deleted file mode 100644 index 32a060a8a..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/arrow/HeadlessArrowPulse.java +++ /dev/null @@ -1,139 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/fletching/items/bolts/Bolt.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/bolts/Bolt.java deleted file mode 100644 index f877050db..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/items/bolts/BoltPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/bolts/BoltPulse.java deleted file mode 100644 index f8ec05101..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/bolts/BoltPulse.java +++ /dev/null @@ -1,124 +0,0 @@ -package core.game.node.entity.skill.fletching.items.bolts; - -import org.rs09.consts.Items; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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 (!player.getSlayer().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/java/core/game/node/entity/skill/fletching/items/bow/StringBow.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/bow/StringBow.java deleted file mode 100644 index 0187f17bb..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/items/bow/StringPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/bow/StringPulse.java deleted file mode 100644 index bc1b858e0..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/bow/StringPulse.java +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.node.entity.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 core.game.node.entity.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(bow.string == 1777 ? 9 : 7); - } - 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/java/core/game/node/entity/skill/fletching/items/crossbow/CrossbowPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/crossbow/CrossbowPulse.java deleted file mode 100644 index cfe0cf53b..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/items/crossbow/Limb.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/crossbow/Limb.java deleted file mode 100644 index 4672d165e..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/crossbow/Limb.java +++ /dev/null @@ -1,128 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/fletching/items/crossbow/LimbPulse.kt b/Server/src/main/java/core/game/node/entity/skill/fletching/items/crossbow/LimbPulse.kt deleted file mode 100644 index e6c67d09d..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/crossbow/LimbPulse.kt +++ /dev/null @@ -1,52 +0,0 @@ -package core.game.node.entity.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 core.game.node.entity.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/java/core/game/node/entity/skill/fletching/items/crossbow/StringCross.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/crossbow/StringCross.java deleted file mode 100644 index 32671c480..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/items/darts/Dart.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/darts/Dart.java deleted file mode 100644 index ac19b5a75..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/darts/Dart.java +++ /dev/null @@ -1,96 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/fletching/items/darts/DartPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/darts/DartPulse.java deleted file mode 100644 index e1d66b501..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/darts/DartPulse.java +++ /dev/null @@ -1,98 +0,0 @@ -package core.game.node.entity.skill.fletching.items.darts; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/fletching/items/gem/Gem.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/gem/Gem.java deleted file mode 100644 index feeb7c5e4..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/gem/Gem.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/fletching/items/gem/GemBolt.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/gem/GemBolt.java deleted file mode 100644 index b21e6a164..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/fletching/items/gem/GemBoltPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/gem/GemBoltPulse.java deleted file mode 100644 index fa9713361..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/gem/GemBoltPulse.java +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.node.entity.skill.fletching.items.gem; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.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/java/core/game/node/entity/skill/fletching/items/grapple/GrapplePulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/grapple/GrapplePulse.java deleted file mode 100644 index 03969803b..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/grapple/GrapplePulse.java +++ /dev/null @@ -1,85 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/fletching/items/package-info.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/package-info.java deleted file mode 100644 index 17b72ab37..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @author Adam - * - */ -package core.game.node.entity.skill.fletching.items; \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/gather/GatheringSkillPulse.java b/Server/src/main/java/core/game/node/entity/skill/gather/GatheringSkillPulse.java deleted file mode 100644 index 2d6d03b06..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/gather/GatheringSkillPulse.java +++ /dev/null @@ -1,362 +0,0 @@ -package core.game.node.entity.skill.gather; - -import core.cache.def.impl.ItemDefinition; -import core.game.container.impl.EquipmentContainer; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.BirdNest; -import core.game.content.global.SkillingPets; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -import core.game.node.entity.impl.Projectile; -import core.game.node.entity.player.Player; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.world.map.Location; -import core.tools.RandomFunction; -import core.tools.StringUtils; -import rs09.game.world.GameWorld; - -import java.util.ArrayList; -import java.util.List; - -/** - * Handles a gathering skill, such as woodcutting, mining, ... - * @author Emperor - */ -public final class GatheringSkillPulse extends SkillPulse { - - /** - * The gem rewards. - */ - private static final Item[] GEM_REWARDS = { new Item(1623), new Item(1621), new Item(1619), new Item(1617) }; - - /** - * Is the player is mining. - */ - private boolean isMining; - - /** - * Is the player is mining essence. - */ - private boolean isMiningEssence; - - /** - * Is the player is mining gems. - */ - private boolean isMiningGems; - - /** - * Is the player is woodcutting. - */ - private boolean isWoodcutting; - - /** - * The amount of ticks it takes to get a log. - */ - private int ticks; - - /** - * Constructs a new {@code GatheringSkillPulse} {@code Object}. - * @param player The player. - * @param node The gathering resource. - */ - public GatheringSkillPulse(Player player, Scenery node) { - super(player, node); - } - - @Override - public void start() { - resource = SkillingResource.forId( - node.getId()); - if (SkillingResource.isEmpty(node.getId())) { - player.getPacketDispatch().sendMessage("This rock contains no ore."); - return; - } - if (resource == null) { - return; - } - if (TutorialSession.getExtension(player).getStage() == 35) { - TutorialStage.load(player, 36, false); - } - isMining = resource.getSkillId() == Skills.MINING; - isMiningEssence = resource == SkillingResource.RUNE_ESSENCE; - isMiningGems = resource.getReward() == SkillingResource.GEM_ROCK_0.getReward(); - isWoodcutting = resource.getSkillId() == Skills.WOODCUTTING; - super.start(); - } - - @Override - public boolean checkRequirements() { - if (player.getSkills().getLevel(resource.getSkillId()) < resource.getLevel()) { - player.getPacketDispatch().sendMessage("You need a " + Skills.SKILL_NAME[resource.getSkillId()] + " level of " + resource.getLevel() + " to " + (isMining ? "mine this rock." : "cut this tree.")); - return false; - } - if (setTool() == null) { - player.getPacketDispatch().sendMessage("You do not have a" + (isMining ? " pickaxe" : "n axe") + " to use."); - return false; - } - if (player.getInventory().freeSlots() < 1) { - player.getDialogueInterpreter().sendDialogue("Your inventory is too full to hold any more " + ItemDefinition.forId(resource.getReward()).getName().toLowerCase() + "."); - return false; - } - return true; - } - - @Override - public void animate() { - if (resource.getAnimation() != null) { - player.animate(resource.getAnimation()); - } else if (tool.getAnimation() != null) { - player.animate(tool.getAnimation()); - } - } - - @Override - public boolean reward() { - if (++ticks % (isMiningEssence ? 3 : 4) != 0) { - return false; - } - if (node.getId() == 10041) { - player.getDialogueInterpreter().sendDialogues(2574, FacialExpression.FURIOUS, RandomFunction.random(2) == 1 ? "You'll blow my cover! I'm meant to be hidden!" : "Will you stop that?"); - return true; - } - if (!checkReward()) { - return false; - } - int tutorialStage = TutorialSession.getExtension(player).getStage(); - if (tutorialStage == 36 && node.getId() == 3042) { - TutorialStage.load(player, 38, false); - } else if (tutorialStage == 36 && node.getId() == 3043) { - TutorialStage.load(player, 37, false); - } - if (tutorialStage == 38 && node.getId() == 3043) { - TutorialStage.load(player, 39, false); - } else if (tutorialStage == 37 && node.getId() == 3042) { - TutorialStage.load(player, 39, false); - } - // 20% chance to auto burn logs when using "inferno adze" item - if (isWoodcutting && tool.getId() == 13661 && RandomFunction.random(100) < 20){ - player.sendMessage("Your chop some logs. The heat of the inferno adze incinerates them."); - Projectile.create(player, null, 1776, 35, 30, 20, 25).transform(player, new Location(player.getLocation().getX() + 2, player.getLocation().getY()), true, 25, 25).send(); - player.getSkills().addExperience(Skills.WOODCUTTING, resource.getExperience()); - player.getSkills().addExperience(Skills.FIREMAKING, resource.getExperience()); - return false; - } - int reward = resource.getReward(); - if (reward > 0) { - reward = calculateReward(reward); - applyAchievementTask(reward); - // Give the player the items - int rewardAmount = calculateRewardAmount(reward); - Item item = new Item(reward, rewardAmount); - player.getInventory().add(item); - // Apply the experience points - double experience = calculateExperience(reward, rewardAmount); - player.getSkills().addExperience(resource.getSkillId(), experience, true); - // Send a message to the player - if (isMiningGems) { - String gemName = ItemDefinition.forId(reward).getName().toLowerCase(); - player.sendMessage("You get " + (StringUtils.isPlusN(gemName) ? "an" : "a") + " " + gemName + "."); - } else if (resource == SkillingResource.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() + "."); - } - // Calculate if the player should receive a bonus gem or bonus ore or both - if (!isMiningEssence && isMining) { - //check for bonus ore from shooting star buff - if(isMining && (player.getAttribute("SS Mining Bonus", GameWorld.getTicks()) > GameWorld.getTicks())){ - if(RandomFunction.getRandom(7) == 5) { - player.getPacketDispatch().sendMessage("...you manage to mine a second ore thanks to the Star Sprite."); - player.getInventory().add(item); - } - } - int chance = 282; - boolean altered = false; - if (new Item(player.getEquipment().getId(12)).getName().toLowerCase().contains("ring of wealth")) { - chance /= 1.5; - altered = true; - } - Item necklace = player.getEquipment().get(EquipmentContainer.SLOT_AMULET); - if (necklace != null && (necklace.getId() > 1705 && necklace.getId() < 1713)) { - chance /= 1.5; - altered = true; - } - if (RandomFunction.random(chance) == 0) { - Item gem = RandomFunction.getRandomElement(GEM_REWARDS); - player.getPacketDispatch().sendMessage("You find a " + gem.getName() + "!"); - if (!player.getInventory().add(gem, player)) { - player.getPacketDispatch().sendMessage("You do not have enough space in your inventory, so you drop the gem on the floor."); - } - } - } - // Calculate if the player should receive a bonus birds nest - if (isWoodcutting) { - int chance = 282; - if (RandomFunction.random(chance) == 0) { - BirdNest.drop(player); - } - } - } - // Tutorial stuff, maybe? - if (tutorialStage == 7) { - TutorialStage.load(player, 8, false); - } - // not sure what this is exactly - if (resource.getRespawnRate() != 0) { - int charge = 1000 / resource.getRewardAmount(); - node.setCharge(node.getCharge() - RandomFunction.random(charge, charge << 2)); - if (node.getCharge() < 1) { - node.setCharge(1000); -// if (resource.isFarming()) { -// PatchWrapper tree = player.getFarmingManager().getPatchWrapper(node.getWrapper().getId()); -// tree.addConfigValue(tree.getNode().getStumpBase()); -// tree.getCycle().setGrowthTime(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(resource.getRespawnDuration() + 10)); -// 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); - return true; - } - } - return false; - } - - /** - * Checks if the has completed any achievements from their diary - */ - private void applyAchievementTask(int reward) { - } - - /** - * Checks if the player gets rewarded. - * @return {@code True} if so. - */ - private boolean checkReward() { - int skill = isMining ? Skills.MINING : Skills.WOODCUTTING; - 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; - } - - private int calculateReward(int reward) { - // If the player is mining sandstone or granite, then i'm not sure what this does? - if (resource == SkillingResource.SANDSTONE || resource == SkillingResource.GRANITE) { - int value = RandomFunction.randomize(resource == SkillingResource.GRANITE ? 3 : 4); - reward += value << 1; - player.getSkills().addExperience(resource.getSkillId(), value * 10, true); - } - - // If the player is mining clay - else if (reward == SkillingResource.CLAY_0.getReward()) { - // Check if they have a bracelet of clay equiped - if (player.getEquipment().contains(11074, 1)) { - player.getSavedData().getGlobalData().incrementBraceletOfClay(); - if (player.getSavedData().getGlobalData().getBraceletClayUses() >= 28) { - player.getSavedData().getGlobalData().setBraceletClayUses(0); - player.getEquipment().remove(new Item(11074)); - player.sendMessage("Your bracelet of clay has disinegrated."); - } - // Give soft clay - reward = 1761; - } - } - - // Convert rune essence to pure essence if the player is above level 30 mining - else if (isMiningEssence && player.getSkills().getLevel(Skills.MINING) >= 30) { - reward = 7936; - } - - // Calculate a random gem for the player - else if (isMiningGems) { - int random = RandomFunction.random(100); - List gems = new ArrayList<>(20); - if (random < 2) { - gems.add(1617); - } else if (random < 25) { - gems.add(1619); - gems.add(1623); - gems.add(1621); - } else if (random < 40) { - gems.add(1629); - } else { - gems.add(1627); - gems.add(1625); - } - reward = gems.get(RandomFunction.random(gems.size())); - } - - return reward; - } - - /** - * Calculate the total amount of items the player should receive - * @return amount of items - */ - private int calculateRewardAmount(int reward) { - int amount = 1; - - // 3239: Hollow tree (bark) 10% chance of obtaining - if (reward == 3239 && RandomFunction.random(100) >= 10) { - amount = 0; - } - - SkillingPets.checkPetDrop(player, isMining ? SkillingPets.GOLEM : SkillingPets.BEAVER); - - return amount; - } - - /** - * Calculate the total experience the player should receive - * @return amount of experience - */ - private double calculateExperience(int reward, int amount) { - double experience = resource.getExperience(); - - // Bark - if (reward == 3239) { - // If we receive the item, give the full experience points otherwise give the base amount - if (amount >= 1) { - experience = 275.2; - } else { - amount = 1; - } - } - - return experience * amount; - } - - - @Override - public void message(int type) { - switch (type) { - case 0: - player.getPacketDispatch().sendMessage("You swing your " + (isMining ? "pickaxe at the rock..." : "axe at the tree...")); - if (TutorialSession.getExtension(player).getStage() == 6) { - player.lock(7); - TutorialStage.load(player, 7, false); - } - break; - } - } - - /** - * Sets the tool used. - */ - private SkillingTool setTool() { - if (!isMining) { - tool = SkillingTool.getHatchet(player); - } else { - tool = SkillingTool.getPickaxe(player); - } - return tool; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/gather/SkillingTool.java b/Server/src/main/java/core/game/node/entity/skill/gather/SkillingTool.java deleted file mode 100644 index 1c153c29c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/gather/SkillingTool.java +++ /dev/null @@ -1,245 +0,0 @@ -package core.game.node.entity.skill.gather; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.world.update.flag.context.Animation; - -/** - * Represents a skilling tool (such as knife, axe, needle, ...) - * @author Emperor - */ -public enum SkillingTool { - - /** - * Represents a bronze axe (woodcutting). - */ - BRONZE_AXE(1351, 1, 0.05D, new Animation(879)), - - /** - * Represents an iron axe (woodcutting). - */ - IRON_AXE(1349, 1, 0.1D, new Animation(877)), - - /** - * Represents a steel axe (woodcutting). - */ - STEEL_AXE(1353, 6, 0.2D, new Animation(875)), - - /** - * Represents a black axe (woodcutting). - */ - BLACK_AXE(1361, 6, 0.25D, new Animation(873)), - - /** - * Represents a mithril axe (woodcutting). - */ - MITHRIL_AXE(1355, 21, 0.30D, new Animation(871)), - - /** - * Represents an adamant axe (woodcutting). - */ - ADAMANT_AXE(1357, 31, 0.45D, new Animation(869)), - - /** - * Represents a rune axe (woodcutting). - */ - RUNE_AXE(1359, 41, 0.65D, new Animation(867)), - - /** - * Represents a dragon axe (woodcutting). - */ - DRAGON_AXE(6739, 61, 0.85D, new Animation(2846)), - - /** - * Represents a bronze pickaxe (mining). - */ - BRONZE_PICKAXE(1265, 1, 0.05D, new Animation(625)), - - /** - * Represents an iron pickaxe (mining). - */ - IRON_PICKAXE(1267, 1, 0.1D, new Animation(626)), - - /** - * Represents a steel pickaxe (mining). - */ - STEEL_PICKAXE(1269, 6, 0.2D, new Animation(627)), - - /** - * Represents a mithril pickaxe (mining). - */ - MITHRIL_PICKAXE(1273, 21, 0.30D, new Animation(629)), - - /** - * Represents an adamant pickaxe (mining). - */ - ADAMANT_PICKAXE(1271, 31, 0.45D, new Animation(628)), - - /** - * Represents a rune pickaxe (mining). - */ - RUNE_PICKAXE(1275, 41, 0.65D, new Animation(624)), - - /** - * Represents the Inferno Adze (woodcutting) - */ - INFERNO_ADZE(13661, 61, 0.85D, new Animation(10251)), - - /** - * Represents the Inferno Adze (mining) - */ - INFERNO_ADZE2(13661, 61, 1.0D, new Animation(10222)); - - /** - * The tool id. - */ - private final int id; - - /** - * The level required. - */ - private final int level; - - /** - * The ratio. - */ - private final double ratio; - - /** - * The animation. - */ - private final Animation animation; - - /** - * Constructs a new {@code SkillingTool} {@code Object}. - * @param id The tool item id. - * @param level The level required to use this. - * @param ratio The ratio. - * @param animation The animation. - */ - private SkillingTool(int id, int level, double ratio, Animation animation) { - this.id = id; - this.level = level; - this.ratio = ratio; - this.animation = animation; - } - - /** - * Gets the tool by the item id. - * @param itemId The item id. - * @return The skilling tool, or {@code null} if the tool wasn't found. - */ - public static SkillingTool forId(int itemId) { - for (SkillingTool tool : SkillingTool.values()) { - if (tool.id == itemId) { - return tool; - } - } - return null; - } - - /** - * Gets the hatchet used by the player. - * @param player The player. - * @return The hatchet. - */ - public static SkillingTool getHatchet(Player player) { - SkillingTool tool = null; - if (checkTool(player, Skills.WOODCUTTING, SkillingTool.DRAGON_AXE)) { - tool = SkillingTool.DRAGON_AXE; - } else if (checkTool(player, Skills.WOODCUTTING, SkillingTool.RUNE_AXE)) { - tool = SkillingTool.RUNE_AXE; - } else if (checkTool(player, Skills.WOODCUTTING, SkillingTool.ADAMANT_AXE)) { - tool = SkillingTool.ADAMANT_AXE; - } else if (checkTool(player, Skills.WOODCUTTING, SkillingTool.MITHRIL_AXE)) { - tool = SkillingTool.MITHRIL_AXE; - } else if (checkTool(player, Skills.WOODCUTTING, SkillingTool.BLACK_AXE)) { - tool = SkillingTool.BLACK_AXE; - } else if (checkTool(player, Skills.WOODCUTTING, SkillingTool.STEEL_AXE)) { - tool = SkillingTool.STEEL_AXE; - } else if (checkTool(player, Skills.WOODCUTTING, SkillingTool.IRON_AXE)) { - tool = SkillingTool.IRON_AXE; - } else if (checkTool(player, Skills.WOODCUTTING, SkillingTool.BRONZE_AXE)) { - tool = SkillingTool.BRONZE_AXE; - } else if (checkTool(player, Skills.WOODCUTTING, SkillingTool.INFERNO_ADZE)) { - if(player.getSkills().getLevel(Skills.FIREMAKING) >= 92) { - tool = SkillingTool.INFERNO_ADZE; - } - } - return tool; - } - - /** - * Gets the pickaxe used by the player. - * @param player The player. - * @return The hatchet. - */ - public static SkillingTool getPickaxe(Player player) { - SkillingTool tool = null; - if (checkTool(player, Skills.MINING, SkillingTool.INFERNO_ADZE2)) { - if (player.getSkills().getLevel(Skills.FIREMAKING) >= 92) { - tool = SkillingTool.INFERNO_ADZE2; - } - } else if (checkTool(player, Skills.MINING, SkillingTool.RUNE_PICKAXE)) { - tool = SkillingTool.RUNE_PICKAXE; - } else if (checkTool(player, Skills.MINING, SkillingTool.ADAMANT_PICKAXE)) { - tool = SkillingTool.ADAMANT_PICKAXE; - } else if (checkTool(player, Skills.MINING, SkillingTool.MITHRIL_PICKAXE)) { - tool = SkillingTool.MITHRIL_PICKAXE; - } else if (checkTool(player, Skills.MINING, SkillingTool.STEEL_PICKAXE)) { - tool = SkillingTool.STEEL_PICKAXE; - } else if (checkTool(player, Skills.MINING, SkillingTool.IRON_PICKAXE)) { - tool = SkillingTool.IRON_PICKAXE; - } else if (checkTool(player, Skills.MINING, SkillingTool.BRONZE_PICKAXE)) { - tool = SkillingTool.BRONZE_PICKAXE; - } - return tool; - } - - /** - * Checks if the player has a tool and if he can use it. - * @param tool The tool. - * @return {@code True} if the tool is usable. - */ - public static boolean checkTool(Player player, int skillId, SkillingTool tool) { - if (player.getSkills().getStaticLevel(skillId) < tool.getLevel()) { - return false; - } - if (player.getEquipment().getNew(3).getId() == tool.getId()) { - return true; - } - return player.getInventory().contains(tool.getId(), 1); - } - - /** - * Gets the id. - * @return The id. - */ - public int getId() { - return id; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the ratio. - * @return The ratio. - */ - public double getRatio() { - return ratio; - } - - /** - * Gets the animation. - * @return The animation. - */ - public Animation getAnimation() { - return animation; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingSkillPulse.java b/Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingSkillPulse.java deleted file mode 100644 index 5fb6a2cf0..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingSkillPulse.java +++ /dev/null @@ -1,329 +0,0 @@ -package core.game.node.entity.skill.gather.woodcutting; - -import core.cache.def.impl.ItemDefinition; -import core.game.container.impl.EquipmentContainer; -import core.game.content.dialogue.FacialExpression; -import core.game.content.global.BirdNest; -import core.game.content.global.SkillingPets; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -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.diary.DiaryType; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.gather.SkillingTool; -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.update.flag.context.Animation; -import core.tools.RandomFunction; -import rs09.game.node.entity.skill.farming.FarmingPatch; -import rs09.game.node.entity.skill.farming.Patch; -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks; -import org.rs09.consts.Items; - -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_BASE; -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_LOGS; - -/** - * Woodcutting skill pulse - * - * @author ceik - */ -public class WoodcuttingSkillPulse extends Pulse { - private WoodcuttingNode resource; - private int ticks; - private Player player; - private Scenery node; - protected boolean resetAnimation = true; - - - public WoodcuttingSkillPulse(Player player, Scenery node) { - super(1, player, node); - this.player = player; - this.node = node; - super.stop(); - } - - public void message(int type) { - if (type == 0) { - player.getPacketDispatch().sendMessage("You swing your axe at the tree..."); - if (TutorialSession.getExtension(player).getStage() == 6) { - player.lock(7); - TutorialStage.load(player, 7, false); - } - } - } - - @Override - public boolean pulse() { - if (!checkRequirements()) { - return true; - } - animate(); - return reward(); - } - - @Override - public void stop() { - if (resetAnimation) { - player.animate(new Animation(-1, Animator.Priority.HIGH)); - } - super.stop(); - message(1); - } - - @Override - public void start() { - resource = WoodcuttingNode.forId(node.getId()); - if (resource == null) { - return; - } - if (checkRequirements()) { - super.start(); - message(0); - } - } - - public boolean checkRequirements() { - 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.getPacketDispatch().sendMessage("You do not have a hatchet to use."); - return false; - } - if (player.getInventory().freeSlots() < 1) { - player.getDialogueInterpreter().sendDialogue("Your inventory is too full to hold any more " + ItemDefinition.forId(resource.getReward()).getName().toLowerCase() + "."); - return false; - } - return true; - } - - public void animate() { - player.animate(SkillingTool.getHatchet(player).getAnimation()); - } - - public boolean reward() { - if (++ticks % 4 != 0) { - return false; - } - if (node.getId() == 10041) { - player.getDialogueInterpreter().sendDialogues(2574, FacialExpression.FURIOUS, RandomFunction.random(2) == 1 ? "You'll blow my cover! I'm meant to be hidden!" : "Will you stop that?"); - return true; - } - if (!checkReward(SkillingTool.getHatchet(player))) { - return false; - } - - int tutorialStage = TutorialSession.getExtension(player).getStage(); - - - // If player is in donator zone - /* if (player.getLocation().getRegionId() == 12102) { - player.getAntiMacroHandler().fireEvent("tree spirit"); - return true; - } -*/ - // 20% chance to auto burn logs when using "inferno adze" item - if (SkillingTool.getHatchet(player).getId() == 13661 && RandomFunction.random(100) < 25) { - player.sendMessage("You chop some logs. The heat of the inferno adze incinerates them."); - Projectile.create(player, null, 1776, 35, 30, 20, 25).transform(player, new Location(player.getLocation().getX() + 2, player.getLocation().getY()), true, 25, 25).send(); - player.getSkills().addExperience(Skills.WOODCUTTING, resource.getExperience()); - player.getSkills().addExperience(Skills.FIREMAKING, resource.getExperience()); - return false; - } - - //actual reward calculations - int reward = resource.getReward(); - int rewardAmount = 0; - if (reward > 0) { - reward = calculateReward(reward); // calculate rewards - rewardAmount = calculateRewardAmount(reward); // calculate amount - SkillingPets.checkPetDrop(player, SkillingPets.BEAVER); // roll for pet - - //add experience - double experience = calculateExperience(resource.reward, rewardAmount); - - player.getSkills().addExperience(Skills.WOODCUTTING, experience, true); - - //send the message for the resource reward - 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() + "."); - } - //give the reward - player.getInventory().add(new Item(reward, rewardAmount)); - int cutLogs = player.getAttribute(STATS_BASE + ":" + STATS_LOGS,0); - player.setAttribute("/save:" + STATS_BASE + ":" + STATS_LOGS,++cutLogs); - - //calculate bonus bird nest for mining - int chance = 282; - if (RandomFunction.random(chance) == chance / 2) { - if(SkillcapePerks.isActive(SkillcapePerks.NEST_HUNTER,player)){ - if(!player.getInventory().add(BirdNest.getRandomNest(false).getNest())){ - BirdNest.drop(player); - } - } else { - BirdNest.drop(player); - } - } - - applyAchievementTask(reward); // apply achievements - } - // Tutorial stuff, maybe? - if (tutorialStage == 7) { - TutorialStage.load(player, 8, false); - } - - //transform to depleted version - if (resource.getRespawnRate() != 0) { - int charge = 1000 / resource.getRewardAmount(); - node.setCharge(node.getCharge() - RandomFunction.random(charge, charge << 2)); - if (node.getCharge() < 1) { - node.setCharge(1000); - if (resource.isFarming()) { - FarmingPatch fPatch = FarmingPatch.forObject(node.asScenery()); - if(fPatch != null) { - Patch patch = fPatch.getPatchFor(player); - patch.setCurrentState(patch.getCurrentState() + 1); - } - if(resource.getId() == 8513 && player.getLocation().getRegionId() == 11828){ - //Chop down a yew tree you grew in falador park - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR,2,3); - } - 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); - return true; - } - } - return false; - } - - private int calculateRewardAmount(int reward) { - int 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 double calculateExperience(int reward, int amount) { - double experience = resource.getExperience(); - - if(player.getLocation().getRegionId() == 10300){ - return 1.0; - } - - // Bark - if (reward == 3239) { - // If we receive the item, give the full experience points otherwise give the base amount - if (amount >= 1) { - experience = 275.2; - } else { - amount = 1; - } - } - - // 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; - } - - private int calculateReward(int reward) { - return reward; - } - - /** - * Checks if the has completed any achievements from their diary - */ - private void applyAchievementTask(int reward) { - // Cut a log from a teak tree - if (reward == Items.TEAK_LOGS_6333) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 7); - } - // Cut a log from a mahogany tree - if (reward == Items.MAHOGANY_LOGS_6332) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 8); - } - - int playerRegion = player.getViewport().getRegion().getId(); - - // Chop down a dying tree in the Lumber Yard - if (node.getId() == 24168 && playerRegion == 13110) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 6); - } - if (resource == WoodcuttingNode.YEW && playerRegion == 10806) { - if (!player.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 1)) { - player.setAttribute("/save:diary:seers:cut-yew", player.getAttribute("diary:seers:cut-yew", 0) + 1); - } - if (player.getAttribute("diary:seers:cut-yew", 0) >= 5) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 1); - } - } - - /* if (resource.isFarming()) { - PatchWrapper tree = player.getFarmingManager().getPatchWrapper(node.getWrapper().getId()); - if (node.getId() == 8389 - && node.getLocation().equals(3003, 3372, 0) - && Trees.forNode(tree.getNode()) != null - && (Trees.forNode(tree.getNode()) == Trees.YEW || Trees.forNode(tree.getNode()) == Trees.MAGIC)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 3); - } - } -*/ - // Cut down a dead tree in Lumbridge Swamp - if (resource.name().toLowerCase().startsWith("dead") && (playerRegion == 12593 || playerRegion == 12849)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 8); - } - - // Cut a willow tree, east of Lumbridge Castle - if (resource.name().toLowerCase().startsWith("willow") && playerRegion == 12850) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 2, 6); - } - } - - /** - * Checks if the player gets rewarded. - * - * @return {@code True} if so. - */ - private boolean checkReward(SkillingTool tool) { - int skill = Skills.WOODCUTTING; - int level = player.getSkills().getLevel(skill) + player.getFamiliarManager().getBoost(skill); - double hostRatio = RandomFunction.randomDouble(100.0); - double lowMod = tool == SkillingTool.BLACK_AXE ? resource.tierModLow / 2 : resource.tierModLow; - double low = resource.baseLow + (tool.ordinal() * lowMod); - double highMod = tool == SkillingTool.BLACK_AXE ? resource.tierModHigh / 2 : resource.tierModHigh; - double high = resource.baseHigh + (tool.ordinal() * highMod); - double clientRatio = RandomFunction.getSkillSuccessChance(low,high,level); - return hostRatio < clientRatio; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/BarbarianMixPlugin.java b/Server/src/main/java/core/game/node/entity/skill/herblore/BarbarianMixPlugin.java deleted file mode 100644 index fcfd3e554..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/BarbarianMixPlugin.java +++ /dev/null @@ -1,62 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/herblore/BarbarianPotion.java b/Server/src/main/java/core/game/node/entity/skill/herblore/BarbarianPotion.java deleted file mode 100644 index cba509270..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/BarbarianPotion.java +++ /dev/null @@ -1,103 +0,0 @@ -package core.game.node.entity.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/java/core/game/node/entity/skill/herblore/GrindingItem.java b/Server/src/main/java/core/game/node/entity/skill/herblore/GrindingItem.java deleted file mode 100644 index df50f81ca..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/GrindingItem.java +++ /dev/null @@ -1,80 +0,0 @@ -package core.game.node.entity.skill.herblore; - -import org.rs09.consts.Items; -import core.game.node.item.Item; - -/** - * Represents an item that will can be ground by pestle and mortar. - * @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)), - 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)); - - /** - * Represents the item to grind. - */ - private final Item[] items; - - /** - * Represents the product item. - */ - private final Item product; - - /** - * Constructs a new {@code GrindingItem} {@code Object}. - * @param items the items. - * @param product the product. - */ - GrindingItem(final Item[] items, final Item product) { - this.items = items; - this.product = product; - } - - /** - * Gets the item. - * @return The item. - */ - public Item[] getItems() { - return items; - } - - /** - * Gets the product. - * @return The product. - */ - public Item getProduct() { - return product; - } - - /** - * Gets the grinding item. - * @param item the item. - * @return the item. - */ - public static GrindingItem forItem(final Item item) { - for (GrindingItem g : values()) { - for (Item i : g.getItems()) { - if (i.getId() == item.getId()) { - return g; - } - } - } - return null; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/herblore/HerbCleanPlugin.java b/Server/src/main/java/core/game/node/entity/skill/herblore/HerbCleanPlugin.java deleted file mode 100644 index beb9505f2..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/HerbCleanPlugin.java +++ /dev/null @@ -1,57 +0,0 @@ -package core.game.node.entity.skill.herblore; - -import api.Container; -import api.ContentAPI; -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 (ContentAPI.removeItem(player, node.asItem(), Container.INVENTORY)){ - player.getSkills().addExperience(Skills.HERBLORE, exp, true); - ContentAPI.addItem(player, herb.getProduct().getId(), 1); - 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/java/core/game/node/entity/skill/herblore/PotionDecantingPlugin.java b/Server/src/main/java/core/game/node/entity/skill/herblore/PotionDecantingPlugin.java deleted file mode 100644 index 26b1ee742..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/PotionDecantingPlugin.java +++ /dev/null @@ -1,327 +0,0 @@ -package core.game.node.entity.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; - } - 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/java/core/game/node/entity/skill/herblore/UnfinishedPotion.java b/Server/src/main/java/core/game/node/entity/skill/herblore/UnfinishedPotion.java deleted file mode 100644 index 7336a05aa..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/herblore/UnfinishedPotion.java +++ /dev/null @@ -1,102 +0,0 @@ -package core.game.node.entity.skill.herblore; - -import core.game.node.item.Item; - -/** - * Represents an unfinished potion to make. - * @author 'Vexia - */ -public enum UnfinishedPotion { - GUAM(Herbs.GUAM.getProduct(), 3, new Item(91)), MARRENTILL(Herbs.MARRENTILL.getProduct(), 5, new Item(93)), TARROMIN(Herbs.TARROMIN.getProduct(), 12, new Item(95)), HARRALANDER(Herbs.HARRALANDER.getProduct(), 22, new Item(97)), RANARR(Herbs.RANARR.getProduct(), 30, new Item(99)), TOADFLAX(Herbs.TOADFLAX.getProduct(), 34, new Item(3002)), SPIRIT_WEED(Herbs.SPIRIT_WEED.getProduct(), 40, new Item(12181)), IRIT(Herbs.IRIT.getProduct(), 45, new Item(101)), AVANTOE(Herbs.AVANTOE.getProduct(), 50, new Item(103)), KWUARM(Herbs.KWUARM.getProduct(), 55, new Item(105)), SNAPDRAGON(Herbs.SNAPDRAGON.getProduct(), 63, new Item(3004)), CADANTINE(Herbs.CADANTINE.getProduct(), 66, new Item(107)), LANTADYME(Herbs.LANTADYME.getProduct(), 69, new Item(2483)), DWARF_WEED(Herbs.DWARF_WEED.getProduct(), 72, new Item(109)), TORSTOL(Herbs.TORSTOL.getProduct(), 75, new Item(111)), STRONG_WEAPON_POISON(HerblorePulse.COCONUT_MILK, new Item(6016), 73, new Item(5936)), SUPER_STRONG_WEAPON_POISON(HerblorePulse.COCONUT_MILK, new Item(2398), 82, new Item(5939)), STRONG_ANTIPOISON(HerblorePulse.COCONUT_MILK, Herbs.TOADFLAX.getProduct(), 68, new Item(5942)), SUPER_STRONG_ANTIPOISON(HerblorePulse.COCONUT_MILK, Herbs.IRIT.getProduct(), 79, new Item(5951)); - - /** - * Represents the base item. - */ - private final Item base; - - /** - * Represents the ingredient needed to make this unf-potion. - */ - private final Item ingredient; - - /** - * Represents the level required to make this unf-potion. - */ - private final int level; - - /** - * Represents the potion product. - */ - private final Item potion; - - /** - * Constructs a new {@code UnfinishedPotion} {@code Object}. - * @param ingredient the ingredient. - * @param level the level. - * @param potion the potion. - */ - UnfinishedPotion(final Item base, final Item ingredient, final int level, final Item potion) { - this.base = base; - this.ingredient = ingredient; - this.level = level; - this.potion = potion; - } - - /** - * Constructs a new {@code UnfinishedPotion} {@code Object}. - * @param ingredient the ingredient. - * @param level the level. - * @param potion the potion. - */ - UnfinishedPotion(final Item ingredient, final int level, final Item potion) { - this(HerblorePulse.VIAL_OF_WATER, ingredient, level, potion); - } - - /** - * Gets the ingredient. - * @return The ingredient. - */ - public Item getIngredient() { - return ingredient; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the potion. - * @return The potion. - */ - public Item getPotion() { - return potion; - } - - /** - * Gets the base. - * @return The base. - */ - public Item getBase() { - return base; - } - - /** - * Gets the unf-potion. - * @param item the item. - * @paramt the base item. - * @return the unf-potion. - */ - public static UnfinishedPotion forItem(final Item item, final Item base) { - for (UnfinishedPotion potion : values()) { - if ((potion.getIngredient().getId() == item.getId() || potion.getIngredient().getId() == base.getId()) && (item.getId() == potion.getBase().getId() || base.getId() == potion.getBase().getId())) { - return potion; - } - } - return null; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/HunterNPC.java b/Server/src/main/java/core/game/node/entity/skill/hunter/HunterNPC.java deleted file mode 100644 index e47ef4733..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/HunterNPC.java +++ /dev/null @@ -1,118 +0,0 @@ -package core.game.node.entity.skill.hunter; - -import core.game.node.entity.Entity; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.tools.RandomFunction; - -import java.util.ArrayList; -import java.util.List; - -/** - * Handles a hunter npc. - * @author Vexia - */ -public final class HunterNPC extends AbstractNPC { - - /** - * The trap type. - */ - private final Traps trap; - - /** - * The trap type node. - */ - private final TrapNode type; - - /** - * Constructs a new {@code HunterNPC} {@code Object}. - */ - public HunterNPC() { - this(0, null, null, null); - this.setWalks(true); - } - - /** - * Constructs a new {@code HunterNPC} {@code Object}. - * @param id the id. - * @param location the location. - * @param trap the trap. - * @param type the type. - */ - public HunterNPC(int id, Location location, Traps trap, TrapNode type) { - super(id, location); - this.trap = trap; - this.type = type; - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - Object[] data = Traps.getNode(id); - return new HunterNPC(id, location, (Traps) data[0], (TrapNode) data[1]); - } - - @Override - public void updateLocation(Location last) { - final TrapWrapper wrapper = trap.getByHook(getLocation()); - if (wrapper != null) { - wrapper.getType().catchNpc(wrapper, this); - } - } - - @Override - protected Location getMovementDestination() { - if (trap.getHooks().size() == 0 || RandomFunction.random(170) > 5) { - return super.getMovementDestination(); - } - TrapHook hook = trap.getHooks().get(RandomFunction.random(trap.getHooks().size())); - if (hook == null || !type.canCatch(hook.getWrapper(), this)) { - return super.getMovementDestination(); - } - Location destination = hook.getChanceLocation(); - return destination != null && destination.getDistance(getLocation()) <= 24 ? destination : super.getMovementDestination(); - } - - @Override - public void handleDrops(Player p, Entity killer) { - int ticks = getAttribute("hunter", 0); - if (ticks < GameWorld.getTicks()) { - super.handleDrops(p, killer); - } - } - - @Override - public int[] getIds() { - List ids = new ArrayList<>(10); - for (Traps t : Traps.values()) { - for (TrapNode node : t.getNodes()) { - for (int id : node.getNpcIds()) { - ids.add(id); - } - } - } - int[] array = new int[ids.size()]; - for (int i = 0; i < array.length; i++) { - array[i] = ids.get(i); - } - return array; - } - - /** - * Gets the type. - * @return The type. - */ - public TrapNode getType() { - return type; - } - - /** - * Gets the trap. - * @return The trap. - */ - public Traps getTrap() { - return trap; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/ImpetuousImpulses.java b/Server/src/main/java/core/game/node/entity/skill/hunter/ImpetuousImpulses.java deleted file mode 100644 index 782aa991d..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/ImpetuousImpulses.java +++ /dev/null @@ -1,153 +0,0 @@ -package core.game.node.entity.skill.hunter; - -import core.game.node.entity.skill.hunter.bnet.BNetTypes; -import core.game.node.entity.skill.hunter.bnet.ImplingNode; -import rs09.game.world.GameWorld; -import core.game.world.callback.CallBack; -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 CallBack { - - /** - * 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 boolean call() { - spawnImps(); - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/ImplingNPC.java b/Server/src/main/java/core/game/node/entity/skill/hunter/ImplingNPC.java deleted file mode 100644 index e797ae9ea..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/ImplingNPC.java +++ /dev/null @@ -1,118 +0,0 @@ -package core.game.node.entity.skill.hunter; - -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.hunter.bnet.ImplingNode; -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.tools.RandomFunction; -import rs09.game.system.config.NPCConfigParser; -import rs09.game.world.GameWorld; - -/** - * Handles an impling npc. - * @author Vexia - */ -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) { - 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); - } - - @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[] {}; - } - - /** - * Gets the impling. - * @return The impling. - */ - public ImplingNode getImpling() { - return impling; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/hunter/Traps.java b/Server/src/main/java/core/game/node/entity/skill/hunter/Traps.java deleted file mode 100644 index daf56d261..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/hunter/Traps.java +++ /dev/null @@ -1,251 +0,0 @@ -package core.game.node.entity.skill.hunter; - -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.node.scenery.Scenery; -import rs09.game.system.SystemLogger; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents a trap type. - * @author Vexia - */ -public enum Traps { - BIRD_SNARE(new TrapSetting(10006, new int[] { 19175 }, new int[] {}, "lay", 19174, Animation.create(5208), Animation.create(5207), 1), - new TrapNode(new int[] { 5073 }, 1, 34, new int[] { 19179, 19180 }, new Item[] { new Item(10088, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 5075 }, 5, 48, new int[] { 19183, 19184 }, new Item[] { new Item(10090, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 5076 }, 9, 61, new int[] { 19185, 19186 }, new Item[] { new Item(10091, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 5074 }, 11, 64.7, new int[] { 19181, 19182 }, new Item[] { new Item(10089, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 5072 }, 19, 95.2, new int[] { 19177, 19178 }, new Item[] { new Item(10087, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 7031 }, 39, 167, new int[] { 28931, 28930 }, new Item[] { new Item(11525, 8), new Item(9978), new Item(526) }) { - @Override - public boolean canCatch(TrapWrapper wrapper, final NPC npc) { - return false; - } - }), - 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)), - 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) })), - DEAD_FALL(new DeadfallSetting(), - new TrapNode(new int[] { 5089 }, 23, 128, new int[] { 19213, 19214, 19218 }, new Item[] { new Item(10113), new Item(526) }), - new TrapNode(new int[] { 5088 }, 33, 168, new int[] { 19211, 19212, 19217 }, new Item[] { new Item(10129), new Item(526) }), - 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) })); - - /** - * The location hooks for this node. - */ - private final List hooks = new ArrayList<>(5); - - /** - * The trap settings. - */ - private final TrapSetting settings; - - /** - * The nodes related to the trap. - */ - private final TrapNode[] nodes; - - /** - * Constructs a new {@code TrapType} {@code Object}. - * @param settings the settings. - * @param nodes the nodes. - */ - Traps(TrapSetting settings, TrapNode... nodes) { - this.settings = settings; - this.nodes = nodes; - } - - /** - * Creates a trap instance. - * @param player the player. - */ - public void create(Player player, Node node) { - player.getPulseManager().run(new TrapCreatePulse(player, node, this)); - } - - /** - * Dismantles a trap. - * @param player the player. - * @param object the object. - */ - public void dismantle(Player player, Scenery object) { - if (!player.getHunterManager().isOwner(object)) { - player.sendMessage("This isn't your trap!"); - return; - } - if (player.getHunterManager().getWrapper(object) == null) { - SystemLogger.logErr("NO WRAPPER (HUNTER DISMANTLE)"); - return; - } - player.faceLocation(object.getLocation()); - player.getPulseManager().run(new TrapDismantlePulse(player, object, player.getHunterManager().getWrapper(object))); - } - - /** - * Investigates the trap. - * @param player the player. - * @param object the object. - */ - public void investigate(Player player, Scenery object) { - getSettings().investigate(player, object); - } - - /** - * Called when an npc is hooked. - * @param wrapper the wrapper. - * @param npc the npc. - */ - public void catchNpc(TrapWrapper wrapper, NPC npc) { - final TrapNode trapNode = forNpc(npc); - if (trapNode == null || !trapNode.canCatch(wrapper, npc) || !settings.canCatch(wrapper, npc)) { - return; - } - settings.catchNpc(wrapper, trapNode, npc); - } - - /** - * Adds a hook. - * @param wrapper the wrapper. - */ - public TrapHook addHook(TrapWrapper wrapper) { - TrapHook hook = settings.createHook(wrapper); - hooks.add(hook); - return hook; - } - - /** - * Gets a node by the npc. - * @param npc the npc. - * @return the node. - */ - public TrapNode forNpc(NPC npc) { - for (TrapNode node : nodes) { - for (int npcId : node.getNpcIds()) { - if (npcId == npc.getId()) { - return node; - } - } - } - return null; - } - - /** - * Gets a trap by the node id. - * @param node the node. - * @return the {@code Traps}. - */ - public static Traps forNode(Node node) { - for (Traps trap : Traps.values()) { - for (int nodeId : trap.getSettings().getNodeIds()) { - if (node.getId() == nodeId) { - return trap; - } - } - for (int objectId : trap.getSettings().getObjectIds()) { - if (objectId == node.getId()) { - return trap; - } - } - for (TrapNode n : trap.getNodes()) { - for (int id : n.getObjectIds()) { - if (id == node.getId()) { - return trap; - } - } - } - if (trap.getSettings().getFailId() == node.getId()) { - return trap; - } - if (trap == NET_TRAP) { - for (NetTrapSetting.NetTrap net : NetTrapSetting.NetTrap.values()) { - if (net.getOriginal() == node.getId() || net.getFailed() == node.getId() || net.getNet() == node.getId() || net.getBent() == node.getId() || net.getCaught() == node.getId()) { - return trap; - } - } - } - } - return null; - } - - /** - * Gets a trap node by the id. - * @param id the id. - * @return the node. - */ - public static Object[] getNode(int id) { - for (Traps trap : values()) { - for (TrapNode t : trap.getNodes()) { - for (int i : t.getNpcIds()) { - if (i == id) { - return new Object[] { trap, t }; - } - } - } - } - return null; - } - - /** - * Gets the wrapper by the hook. - * @param location the location. - * @return the wrapper. - */ - public TrapWrapper getByHook(Location location) { - for (TrapHook hook : hooks) { - if (hook.isHooked(location)) { - return hook.getWrapper(); - } - } - return null; - } - - /** - * Gets the settings. - * @return The settings. - */ - public TrapSetting getSettings() { - return settings; - } - - /** - * Gets the nodes. - * @return The nodes. - */ - public TrapNode[] getNodes() { - return nodes; - } - - /** - * Gets the hooks. - * @return The hooks. - */ - public List getHooks() { - return hooks; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/ChargeOrbSpells.java b/Server/src/main/java/core/game/node/entity/skill/magic/ChargeOrbSpells.java deleted file mode 100644 index 7c1a514c2..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/ChargeOrbSpells.java +++ /dev/null @@ -1,202 +0,0 @@ -package core.game.node.entity.skill.magic; - -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.equipment.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.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, Item... runes) { - super(SpellBook.MODERN, level, level + 10, ANIMATION, g, null, 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), 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), 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), 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), 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/java/core/game/node/entity/skill/magic/ChargeSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/ChargeSpell.java deleted file mode 100644 index 8fe66583b..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/ChargeSpell.java +++ /dev/null @@ -1,53 +0,0 @@ -package core.game.node.entity.skill.magic; - -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; -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; - -/** - * Represents the charge spell magic spell. - * @author Emperor - * @version 1.0 - */ -@Initializable -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), null, new Item[] { Runes.FIRE_RUNE.getItem(3), Runes.BLOOD_RUNE.getItem(3), Runes.AIR_RUNE.getItem(3) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.MODERN.register(58, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - final Player p = (Player) entity; - if (p.getLocks().isLocked("charge_cast")) { - p.getPacketDispatch().sendMessage("You need to wait for the spell to recharge."); - return false; - } - if (!meetsRequirements(entity, true, true)) { - return false; - } - p.getLocks().lock("charge_cast", 100); - visualize(entity, target); - p.registerState("godcharge").init(); - p.getPacketDispatch().sendMessage("You feel charged with magic power."); - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/CombinationRune.java b/Server/src/main/java/core/game/node/entity/skill/magic/CombinationRune.java deleted file mode 100644 index a5d351c23..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/CombinationRune.java +++ /dev/null @@ -1,33 +0,0 @@ -package core.game.node.entity.skill.magic; - -import java.util.ArrayList; -import java.util.List; - -public enum CombinationRune { - LAVA_RUNE(4699, Runes.FIRE_RUNE,Runes.EARTH_RUNE), - STEAM_RUNE(4694,Runes.FIRE_RUNE,Runes.WATER_RUNE), - MIST_RUNE(4695,Runes.WATER_RUNE,Runes.AIR_RUNE), - DUST_RUNE(4696, Runes.AIR_RUNE,Runes.EARTH_RUNE), - SMOKE_RUNE(4697,Runes.FIRE_RUNE,Runes.AIR_RUNE), - MUD_RUNE(4698,Runes.EARTH_RUNE,Runes.WATER_RUNE); - - - public Runes[] types; - public int id; - CombinationRune(int id, Runes... types){ - this.id = id; - this.types = types; - } - - public static List eligibleFor(Runes rune){ - List runes = new ArrayList<>(20); - for(CombinationRune r : CombinationRune.values()){ - for(Runes ru : r.types){ - if(ru == rune){ - runes.add(r); - } - } - } - return runes; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/HouseTeleportPlugin.java b/Server/src/main/java/core/game/node/entity/skill/magic/HouseTeleportPlugin.java deleted file mode 100644 index 7ba265bf8..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/HouseTeleportPlugin.java +++ /dev/null @@ -1,53 +0,0 @@ -package core.game.node.entity.skill.magic; - -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.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.item.Item; -import rs09.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/java/core/game/node/entity/skill/magic/MagicSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/MagicSpell.java deleted file mode 100644 index 987998ce7..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/MagicSpell.java +++ /dev/null @@ -1,428 +0,0 @@ -package core.game.node.entity.skill.magic; - -import core.game.component.Component; -import core.game.node.entity.skill.Skills; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatSpell; -import rs09.game.node.entity.combat.CombatSwingHandler; -import core.game.node.entity.combat.equipment.SpellType; -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.item.Item; -import rs09.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; - -/** - * Represents a magic spell. - * @author Emperor - */ -public abstract class MagicSpell implements Plugin { - - /** - * The spell book. - */ - protected final SpellBook book; - - /** - * The level requirement. - */ - protected final int level; - - /** - * The animation. - */ - protected final Animation animation; - - /** - * The casting graphics. - */ - protected final Graphics graphic; - - /** - * The casting Audio. - */ - protected final Audio audio; - - /** - * The item-array containing the runes required. - */ - protected final Item[] runes; - - /** - * The spell id. - */ - protected int spellId; - - /** - * The experience gained. - */ - private final double experience; - - /** - * Constructs a new {@code MagicSpell} {@code Object}. - */ - public MagicSpell() { - this(SpellBook.MODERN, 0, 0, null, null, null, new Item[0]); - } - - /** - * Constructs a new {@code MagicSpell} {@code Object}. - * @param book The spell book this spell is from. - * @param level The level requirement. - * @param animation the cast animation. - * @param graphic The cast graphic. - * @param Audio The casting Audio. - * @param runes The runes required to cast the spell. - */ - public MagicSpell(SpellBook book, int level, final double experience, Animation animation, Graphics graphic, Audio Audio, Item[] runes) { - this.book = book; - this.level = level; - this.experience = experience; - this.animation = animation; - this.graphic = graphic; - this.audio = Audio; - this.runes = runes; - } - - /** - * Casts a spell. - * @param p The player casting the spell. - * @param target The target. - */ - public static boolean castSpell(final Player p, SpellBook book, int spellId, Node target) { - if (p.getAttribute("magic-delay", 0) > GameWorld.getTicks()) { - return false; - } - MagicSpell spell = book.getSpell(spellId); - if (spell == null) { - return false; - } - if (spell.book != book || p.getSpellBookManager().getSpellBook() != book.getInterfaceId()) { - return false; - } - if (target.getLocation() != null && target != p) { - if (!target.getLocation().withinDistance(p.getLocation(), 15)) { - return false; - } - p.faceLocation(target.getLocation()); - } - boolean combatSpell = spell instanceof CombatSpell; - if (!combatSpell && target instanceof Entity) { - 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()); - } - return true; - } - return false; - } - - /** - * Gets the delay. - * @return the delay. - */ - public int getDelay() { - return 3; - } - - /** - * Starts the effect of this spell (if any). - * @param entity The entity. - */ - public abstract boolean cast(Entity entity, Node target); - - /** - * Visualizes the spell. - * @param entity The entity. - * @param target The target. - */ - public void visualize(Entity entity, Node target) { - entity.graphics(graphic); - entity.animate(animation); - sendAudio(entity); - } - - /** - * Checks if the player is holding the staff that will be used instead of - * the rune. - * @param p The player. - * @param rune The rune item id. - * @return {@code True} if the player is wearing the correct staff for the - * rune. - */ - public boolean usingStaff(Player p, int rune) { - Item weapon = p.getEquipment().get(3); - if (weapon == null) { - return false; - } - MagicStaff staff = MagicStaff.forId(rune); - if (staff == null) { - return false; - } - int[] staves = staff.getStaves(); - for (int id : staves) { - if (weapon.getId() == id) { - return true; - } - } - return false; - } - - /** - * Checks if the casting entity meets the requirements to cast this spell. - * @param caster The caster. - * @param message If a message should be send to the caster if it doesn't - * meet the requirements. - * @param remove If we should remove the runes from the player's inventory. - * @return {@code True} if so. - */ - public boolean meetsRequirements(Entity caster, boolean message, boolean remove) { - if (!checkLevelRequirement(caster, message)) { - return false; - } - if (caster instanceof Player) { - CombatSpell spell = ((Player) caster).getProperties().getAutocastSpell(); - if (spell != null) { - boolean slayer = ((Player) caster).getEquipment().get(3).getName().contains("layer's staff"); - boolean voidKnight = ((Player) caster).getEquipment().get(3).getName().contains("knight mace"); - if ((spell.getSpellId() == 31 && !slayer) || (spell.getSpellId() == 42 && !voidKnight)) { - ((Player) caster).getPacketDispatch().sendMessage("You need the proper staff to autocast this spell."); - return false; - } - } - } - if((spellId == 12 || spellId == 30 || spellId == 56) && caster instanceof Player){ - if (caster.getAttribute("entangleDelay", 0) > GameWorld.getTicks()) { - caster.asPlayer().sendMessage("You have recently cast a binding spell."); - return false; - } - } - if (caster instanceof Player) { - Player p = (Player) caster; - if(p.getEquipment().get(3) != null && p.getEquipment().get(3).getId() == 14726){ - if(RandomFunction.getRandom(100) < 13){ - p.sendMessage("Your staff negates the rune requirement of the spell."); - return true; - } - } - if (runes == null) { - return true; - } - List toRemove = new ArrayList<>(20); - for (Item item : runes) { - if (!hasRune(p, item, toRemove, message)) { - return false; - } - } - if (remove) { - toRemove.forEach(i -> { - p.getInventory().remove(i); - }); - } - return true; - } - return true; - } - - /** - * Checks the level requirement. - * @param caster The caster. - * @param message If we display the message. - * @return {@code True} if passed. - */ - public boolean checkLevelRequirement(Entity caster, boolean message) { - if (caster instanceof Player && caster.getSkills().getLevel(Skills.MAGIC, this instanceof CombatSpell ? true : false) < levelRequirement()) { - if (message && caster instanceof Player) { - ((Player) caster).getPacketDispatch().sendMessage("You need a Magic level of " + levelRequirement() + " to cast this spell."); - } - return false; - } - 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. - * @param hit The hit. - */ - public void addExperience(Entity entity, int hit) { - entity.getSkills().addExperience(Skills.MAGIC, experience, true); - if (!(entity instanceof Player) || hit < 1) { - return; - } - 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); - return; - } - entity.getSkills().addExperience(Skills.MAGIC, hit * (CombatSwingHandler.EXPERIENCE_MOD / 2), true); - } - - /** - * Gets the level requirement to cast this spell. - * @return The level requirement. - */ - public int levelRequirement() { - 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; - } - - /** - * Gets the audio. - * @return the audio. - */ - public Audio getAudio() { - return audio; - } - - /** - * Gets the spell book for this spell. - * @return The spell book. - */ - public SpellBook getBook() { - return book; - } - - /** - * Gets the item array of runes required to cast this spell. - * @return The item array of runes, or {@code null} if the spell doesn't need - * any runes. - */ - public Item[] getCastRunes() { - return runes; - } - - /** - * Gets the spellId. - * @return The spellId. - */ - public int getSpellId() { - return spellId; - } - - /** - * Sets the spellId. - * @param spellId The spellId to set. - */ - public void setSpellId(int spellId) { - this.spellId = spellId; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Gets the experience. - * @param player the player. - * @return the experience. - */ - public double getExperience(Player player) { - return experience; - } - - /** - * Gets the level requirement. - * @return The level requirement. - */ - public int getLevel() { - return level; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureGroupSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureGroupSpell.java deleted file mode 100644 index 0b64ac5df..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureGroupSpell.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.plugin.Initializable; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; -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.map.RegionManager; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Plugin; - -/** - * The cure group spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CureGroupSpell extends MagicSpell { - - /** - * Represents the animation of this graphics. - */ - private static final Animation ANIMATION = new Animation(4409); - - /** - * Represents the graphic to use. - */ - private static final Graphics GRAPHIC = new Graphics(751, 130); - - /** - * Constructs a new {@code CureGroupSpell} {@code Object}. - */ - public CureGroupSpell() { - super(SpellBook.LUNAR, 74, 74, ANIMATION, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.LAW_RUNE.getId(), 2), new Item(Runes.COSMIC_RUNE.getId(), 2) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(3, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - Player p = (Player) entity; - if (!meetsRequirements(entity, true, true)) { - return false; - } - p.animate(ANIMATION); - p.graphics(GRAPHIC); - p.getStateManager().remove(EntityState.POISONED); - for (Player players : RegionManager.getLocalPlayers(p, 1)) { - Player o = (Player) players; - if (!o.isActive() || o.getLocks().isInteractionLocked()) { - continue; - } - if (!o.getSettings().isAcceptAid()) { - continue; - } - o.getStateManager().remove(EntityState.POISONED); - o.graphics(GRAPHIC); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureMeSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureMeSpell.java deleted file mode 100644 index a34134b2c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureMeSpell.java +++ /dev/null @@ -1,64 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; -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; - -/** - * The cure me spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CureMeSpell extends MagicSpell { - - /** - * Represents the animation of this graphics. - */ - private static final Animation ANIMATION = new Animation(4411); - - /** - * Repesents the graphick, next spells of this spell. - */ - private static final Graphics GRAPHIC = new Graphics(731, 90); - - /** - * Constructs a new {@code CureOtherSpell} {@code Object}. - */ - public CureMeSpell() { - super(SpellBook.LUNAR, 71, 69, ANIMATION, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.LAW_RUNE.getId(), 1), new Item(Runes.COSMIC_RUNE.getId(), 2) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(23, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - Player p = (Player) entity; - if (!p.getStateManager().hasState(EntityState.POISONED)) { - p.getPacketDispatch().sendMessage("You are not poisoned."); - return false; - } - if (!meetsRequirements(entity, true, true)) { - return false; - } - p.animate(ANIMATION); - p.graphics(GRAPHIC); - p.getStateManager().remove(EntityState.POISONED); - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureOtherSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureOtherSpell.java deleted file mode 100644 index d73dd1a9d..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/CureOtherSpell.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; -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; - -/** - * The cure other spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CureOtherSpell extends MagicSpell { - - /** - * Represents the animation of this graphics. - */ - private static final Animation ANIMATION = new Animation(4411); - - /** - * Repesents the graphick, next spells of this spell. - */ - private static final Graphics GRAPHIC = new Graphics(738, 130); - - /** - * Constructs a new {@code CureOtherSpell} {@code Object}. - */ - public CureOtherSpell() { - super(SpellBook.LUNAR, 68, 65, ANIMATION, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 1), new Item(Runes.LAW_RUNE.getId(), 1), new Item(Runes.EARTH_RUNE.getId(), 10) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(1, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - Player p = (Player) entity; - if (!(target instanceof Player)) { - p.getPacketDispatch().sendMessage("You can only cast this spell on other players."); - return false; - } - Player o = (Player) target; - if (!o.isActive() || o.getLocks().isInteractionLocked()) { - p.getPacketDispatch().sendMessage("This player is busy."); - return false; - } - if (!o.getSettings().isAcceptAid()) { - p.getPacketDispatch().sendMessage("The player is not accepting any aid."); - return false; - } - if (!o.getStateManager().hasState(EntityState.POISONED)) { - p.getPacketDispatch().sendMessage("This player is not poisoned."); - return false; - } - if (!meetsRequirements(entity, true, true)) { - return false; - } - p.face(o); - p.animate(ANIMATION); - o.graphics(GRAPHIC); - o.getStateManager().remove(EntityState.POISONED); - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/DreamSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/DreamSpell.java deleted file mode 100644 index 3c41c39f9..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/DreamSpell.java +++ /dev/null @@ -1,104 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.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 rs09.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 already have full hitpoints."); - 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/java/core/game/node/entity/skill/magic/lunar/EnergyTransferSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/EnergyTransferSpell.java deleted file mode 100644 index cce1ee35f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/EnergyTransferSpell.java +++ /dev/null @@ -1,82 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.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.equipment.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 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); - o.graphics(GRAPHICS); - return true; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/FertileSoilSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/FertileSoilSpell.java deleted file mode 100644 index cf578cd9f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/FertileSoilSpell.java +++ /dev/null @@ -1,74 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; -import rs09.game.node.entity.skill.farming.CompostType; -import rs09.game.node.entity.skill.farming.FarmingPatch; -import rs09.game.node.entity.skill.farming.Patch; - -/** - * Represents the fertile soil spell plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FertileSoilSpell extends MagicSpell { - - /** - * Represents the graphic to use. - */ - private static final Graphics GRAPHIC = new Graphics(141, 96); - - /** - * Represents the animaton to use. - */ - private static final Animation ANIMATION = new Animation(722); - - /** - * Constructs a new {@code FertileSoilSpell} {@code Object}. - */ - public FertileSoilSpell() { - super(SpellBook.LUNAR, 83, 87, null, null, null, new Item[] { new Item(Runes.NATURE_RUNE.getId(), 2), new Item(Runes.ASTRAL_RUNE.getId(), 3), new Item(Runes.EARTH_RUNE.getId(), 15) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(2, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - final Player player = ((Player) entity); - final Scenery object = (Scenery) target; - final FarmingPatch fPatch = FarmingPatch.forObject(object); - if(fPatch == null){ - player.sendMessage("Um... I don't want to fertilize that!"); - return false; - } - final Patch patch = fPatch.getPatchFor(player); - if(patch.getCompost() != CompostType.NONE){ - player.sendMessage("This patch has already been composted."); - return false; - } - if (!super.meetsRequirements(player, true, true)) { - return false; - } - patch.setCompost(CompostType.SUPER); - player.sendMessage("You fertilize the soil."); - player.graphics(GRAPHIC); - player.animate(ANIMATION); - return true; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/HumidifySpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/HumidifySpell.java deleted file mode 100644 index 9596c4f7e..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/HumidifySpell.java +++ /dev/null @@ -1,90 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.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/java/core/game/node/entity/skill/magic/lunar/HunterKitSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/HunterKitSpell.java deleted file mode 100644 index 94da3d4b8..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/HunterKitSpell.java +++ /dev/null @@ -1,96 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.cache.def.impl.ItemDefinition; -import core.plugin.Initializable; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.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); - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/PlankMeSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/PlankMeSpell.java deleted file mode 100644 index 78587dec3..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/PlankMeSpell.java +++ /dev/null @@ -1,185 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.plugin.Initializable; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.item.Item; -import rs09.game.world.repository.Repository; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Plugin; - -/** - * Turns one log into a plank, without need of the sawmill. Coins are still - * required to convert a log into a plank, but the amount is decreased. - * @author 'Vexia - */ -@Initializable -public class PlankMeSpell extends MagicSpell { - - /** - * The graphic. - */ - private static final Graphics GRAPHIC = new Graphics(1063, 120); - - /** - * The animation. - */ - private static final Animation ANIMATION = new Animation(6298); - - /** - * Constructs a new {@code StatRestoreSpell} {@code Object}. - */ - public PlankMeSpell() { - super(SpellBook.LUNAR, 86, 90, null, null, null, new Item[] { new Item(Runes.NATURE_RUNE.getId(), 1), new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.EARTH_RUNE.getId(), 15) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(11, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - final Player player = ((Player) entity); - final Plank plank = Plank.forLog(((Item) target)); - player.getInterfaceManager().setViewedTab(6); - final Item item = ((Item) target); - if (plank == null) { - player.getPacketDispatch().sendMessage("You need to use this spell on logs."); - return false; - } - if (!player.getInventory().contains(995, plank.getPrice())) { - player.getPacketDispatch().sendMessage("You need " + plank.getPrice() + " coins to convert that log into a plank."); - return false; - } - player.lock(4); - create(player, plank, player.getInventory().getAmount(item)); - return true; - } - - /** - * Method used to create a plank from a log. - * @param player the player. - * @param plank the plank. - * @param amount the amount. - */ - public final void create(final Player player, final Plank plank, int amount) { - player.getInterfaceManager().close(); - if (amount > player.getInventory().getAmount(plank.getLog())) { - amount = player.getInventory().getAmount(plank.getLog()); - } - if (!player.getInventory().containsItem(plank.getLog())) { - player.getDialogueInterpreter().sendDialogues(Repository.findNPC(4250), null, "You'll need to bring me some more logs."); - return; - } - int newAmt = 1; - for (int i = 1; i <= amount; i++) { - if (player.getInventory().contains(995, plank.getPrice() * amount)) { - newAmt = i; - continue; - } - } - amount = newAmt; - if (!player.getInventory().contains(995, plank.getPrice() * amount)) { - player.getDialogueInterpreter().sendDialogues(player, null, "Sorry, I don't have enough coins to pay for that."); - return; - } - if (!player.getInventory().contains(plank.getLog().getId(), amount)) { - return; - } - Item coins = new Item(995, plank.getPrice() * amount); - if (!player.getInventory().containsItem(coins)) { - return; - } - if (super.meetsRequirements(player, true, true)) { - player.graphics(GRAPHIC); - player.animate(ANIMATION); - if (player.getInventory().remove(coins)) { - Item remove = plank.getLog(); - remove.setAmount(amount); - if (player.getInventory().remove(remove)) { - Item planks = plank.getPlank(); - planks.setAmount(amount); - player.getInventory().add(planks); - } - } - } - } - - /** - * Represents an enum of planks to be boughten. - * @author 'Vexia - * @date Oct 8, 2013 - */ - public enum Plank { - WOOD(new Item(1511), new Item(960), 70), OAK(new Item(1521), new Item(8778), 175), TEAK(new Item(6333), new Item(8780), 350), MAHOGANY(new Item(6332), new Item(8782), 1050); - /** - * Constructs a new {@code SawmillPlankInterface} {@code Object}. - * @param log the log. - * @param plank the plank. - * @param price the price. - */ - Plank(Item log, Item plank, int price) { - this.log = log; - this.plank = plank; - this.price = price; - } - - /** - * Represents the item needed. - */ - private final Item log; - - /** - * Represents the item given. - */ - private final Item plank; - - /** - * Represents the price of the plank. - */ - private final int price; - - /** - * Gets the plank. - * @param item the item. - * @return the plank. - */ - public static Plank forLog(final Item item) { - for (Plank p : Plank.values()) { - if (p.getLog().getId() == item.getId()) { - return p; - } - } - return null; - } - - /** - * @return the log. - */ - public Item getLog() { - return log; - } - - /** - * @return the plank. - */ - public Item getPlank() { - return plank; - } - - /** - * @return the price. - */ - public int getPrice() { - return price; - } - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/SpellbookSwapSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/SpellbookSwapSpell.java deleted file mode 100644 index 8e786c2eb..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/SpellbookSwapSpell.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.component.Component; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.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 rs09.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/java/core/game/node/entity/skill/magic/lunar/StatBoostSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StatBoostSpell.java deleted file mode 100644 index b7b7bed88..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StatBoostSpell.java +++ /dev/null @@ -1,93 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.content.consumable.Consumables; -import core.game.content.consumable.Potion; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; -import core.game.node.entity.player.Player; -import rs09.game.ai.AIPlayer; -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.Initializable; -import core.plugin.Plugin; - -import java.util.List; - -@Initializable -public final class StatBoostSpell extends MagicSpell { - - private static final Animation ANIMATION = new Animation(4413); - private static final Graphics GRAPHICS = new Graphics(733, 130); - public static final int VIAL = 229; - public StatBoostSpell() { - super(SpellBook.LUNAR, 84, 88, null, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 3), new Item(Runes.EARTH_RUNE.getId(), 12), new Item(Runes.WATER_RUNE.getId(), 10) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(26, this); - return this; - } - - @Override - 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 (potion == null) { - player.getPacketDispatch().sendMessage("For use of this spell use only one a potion."); - return false; - } - if (!item.getDefinition().isTradeable() || item.getName().toLowerCase().contains("restore") || item.getName().toLowerCase().contains("zamorak") || item.getName().toLowerCase().contains("saradomin") || item.getName().toLowerCase().contains("combat")) { - player.getPacketDispatch().sendMessage("You can't cast this spell on that item."); - return false; - } - 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; - for (Player players : pl) { - Player o = (Player) players; - if (!o.isActive() || o.getLocks().isInteractionLocked() || o == player) { - continue; - } - if (!o.getSettings().isAcceptAid() && !(o instanceof AIPlayer)) { - continue; - } - o.graphics(GRAPHICS); - potion.getEffect().activate(o); - size++; - } - if (size == 1) { - 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.animate(ANIMATION); - player.graphics(GRAPHICS); - player.getInventory().remove(item); - player.getInventory().add(new Item(potion.getIds()[size - 1])); - return true; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StatRestoreSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StatRestoreSpell.java deleted file mode 100644 index fcb3eaae8..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StatRestoreSpell.java +++ /dev/null @@ -1,105 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.content.consumable.Consumables; -import core.game.content.consumable.Potion; -import core.plugin.Initializable; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.SpellType; -import core.game.node.entity.player.Player; -import rs09.game.ai.AIPlayer; -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 java.util.List; - -@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[] { 2430, 127, 129, 131, 3024, 3026, 3028, 3030 }; - - 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) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(27, this); - return this; - } - - @Override - 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 (potion == null) { - player.getPacketDispatch().sendMessage("For use of this spell use only one a potion."); - return false; - } - if (!item.getDefinition().isTradeable() || !isRestore(item.getId())) { - player.getPacketDispatch().sendMessage("You can't cast this spell on that item."); - return false; - } - 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; - for (Player players : pl) { - Player o = (Player) players; - if (!o.isActive() || o.getLocks().isInteractionLocked() || o == player) { - continue; - } - if (!o.getSettings().isAcceptAid() && !(o instanceof AIPlayer)) { - continue; - } - o.graphics(GRAPHICS); - potion.getEffect().activate(o); - size++; - } - if (size == 1) { - 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.animate(ANIMATION); - player.graphics(GRAPHICS); - player.getInventory().remove(item); - player.getInventory().add(new Item(potion.getIds()[size - 1])); - return true; - } - - private boolean isRestore(int id) { - for (int i : IDS) { - if (i == id) { - return true; - } - } - return false; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StatSpySpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StatSpySpell.java deleted file mode 100644 index ddd4236c3..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StatSpySpell.java +++ /dev/null @@ -1,96 +0,0 @@ -package core.game.node.entity.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.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.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); - 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/java/core/game/node/entity/skill/magic/lunar/StringJewellerySpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StringJewellerySpell.java deleted file mode 100644 index 44eb457b3..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/StringJewellerySpell.java +++ /dev/null @@ -1,164 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.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 jewllery stringing spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class StringJewellerySpell extends MagicSpell { - - /** - * Represents the animation of this spell. - */ - private static final Animation ANIMATION = Animation.create(4412); - - /** - * Represents the graphic of this spell. - */ - private static final Graphics GRAPHIC = new Graphics(728, 100); - - /** - * Represents the array of unstrung jewellery. - */ - private static final int[] UNSTRUNG = { 1673, 1675, 1677, 1679, 1681, 1683, 1714 }; - - /** - * Represents an array of strung jewellery. - */ - private static final int[] STRUNG = { 1692, 1694, 1696, 1698, 1700, 1702, 1718 }; - - /** - * Constructs a new {@code CureOtherSpell} {@code Object}. - */ - public StringJewellerySpell() { - super(SpellBook.LUNAR, 80, 87, 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(), 5) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(22, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - final Player p = (Player) entity; - boolean contains = false; - Item item = null; - for (int i : UNSTRUNG) { - if (p.getInventory().contains(i, 1)) { - contains = true; - item = new Item(i); - break; - } - } - if (!contains) { - p.getPacketDispatch().sendMessage("You need jewellery in order to use this spell."); - return false; - } - if (!meetsRequirements(entity, true, false)) { - return false; - } - p.getPulseManager().run(new LunarStringPulse(p, item)); - return true; - } - - /** - * Represents the skill pulse used to string a jewellery. - * @author 'Vexia - */ - public class LunarStringPulse extends SkillPulse { - - /** - * Constructs a new {@code LunarStringPulse} {@code Object}. - * @param player the player. - * @param node the node. - */ - public LunarStringPulse(final Player player, final Item node) { - super(player, node); - } - - @Override - public boolean checkRequirements() { - if (!player.getInventory().containsItem(node)) { - stop(); - return false; - } - return true; - } - - @Override - public void animate() { - player.animate(ANIMATION); - player.graphics(GRAPHIC); - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - setDelay(2); - return false; - } - if (meetsRequirements(player, true, true) && player.getInventory().remove(node)) { - player.getInventory().add(new Item(STRUNG[getIndex()])); - player.getSkills().addExperience(Skills.CRAFTING, 4, true); - player.getSkills().addExperience(Skills.MAGIC, 83, true); - } else { - return true; - } - return nextItem() == null; - } - - @Override - public void stop() { - super.stop(); - player.graphics(new Graphics(-1)); - } - - @Override - public void message(int type) { - } - - /** - * Method used to get index. - * @return the index. - */ - public int getIndex() { - for (int i = 0; i < UNSTRUNG.length; i++) { - if (node.getId() == UNSTRUNG[i]) { - return i; - } - } - return -1; - } - - /** - * Method used to get the next string. - * @return the string. - */ - public Item nextItem() { - for (int i : UNSTRUNG) { - if (player.getInventory().contains(i, 1)) { - node = new Item(i); - } - } - return node; - } - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/SuperglassMakeSpell.java b/Server/src/main/java/core/game/node/entity/skill/magic/lunar/SuperglassMakeSpell.java deleted file mode 100644 index ae8188e5f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/magic/lunar/SuperglassMakeSpell.java +++ /dev/null @@ -1,121 +0,0 @@ -package core.game.node.entity.skill.magic.lunar; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.magic.Runes; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.equipment.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; -import core.tools.RandomFunction; - -/** - * The super glass make spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public class SuperglassMakeSpell extends MagicSpell { - - /** - * Represents the animation of this spell. - */ - private static final Animation ANIMATION = Animation.create(4413); - - /** - * Represents the graphics of this spell. - */ - private static final Graphics GRAPHIC = new Graphics(729, 120); - - /** - * Represents the bucket of sand item. - */ - private static final Item BUCKET_OF_SAND = new Item(1783); - - /** - * Represents the molten glass item. - */ - private static final Item MOLTEN_GLASS = new Item(1775); - - /** - * The set of items to be used with sand. - */ - private static final Item[] SETS = new Item[] { new Item(1781), new Item(401), new Item(10978) }; - - /** - * Constructs a new {@code SuperglassMakeSpell} {@code Object}. - */ - public SuperglassMakeSpell() { - super(SpellBook.LUNAR, 77, 78, null, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.FIRE_RUNE.getId(), 6), new Item(Runes.AIR_RUNE.getId(), 10) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(25, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - final Player player = ((Player) entity); - int setIndex = getSetIndex(player); - int sand = player.getInventory().getAmount(BUCKET_OF_SAND); - if (setIndex == -1) { - player.getPacketDispatch().sendMessage("You don't have the required ingredients."); - return false; - } - if (!super.meetsRequirements(player, true, true)) { - return false; - } - player.lock(4); - player.graphics(GRAPHIC); - player.animate(ANIMATION); - for (int i = 0; i < sand; i++) { - if (hasSet(player, setIndex)) { - if (player.getInventory().remove(BUCKET_OF_SAND, SETS[setIndex])) { - player.getInventory().add(MOLTEN_GLASS); - // https://runescape.wiki/w/Superglass_Make?oldid=1970761 - // "On average, each set will produce 1.30 pieces of molten glass" - if(RandomFunction.randomDouble(1.0) < 0.3) { - player.getInventory().add(MOLTEN_GLASS); - } - player.getSkills().addExperience(Skills.CRAFTING, 10, true); - } - } - } - return true; - } - - /** - * Checks if the player has the set. - * @param player the player. - * @param index the index. - * @return {@code True} if so. - */ - public boolean hasSet(final Player player, final int index) { - return player.getInventory().containsItem(BUCKET_OF_SAND) && player.getInventory().containsItem(SETS[index]); - } - - /** - * Gets the set index. - * @param player the player. - * @return the index.s - */ - public int getSetIndex(final Player player) { - if (!player.getInventory().containsItem(BUCKET_OF_SAND)) { - return -1; - } - for (int i = 0; i < SETS.length; i++) { - if (player.getInventory().containsItem(SETS[i])) { - return i; - } - } - return -1; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/package-info.java b/Server/src/main/java/core/game/node/entity/skill/package-info.java deleted file mode 100644 index b39254204..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Holds skill-related plugins. - * - * @author Emperor - * - */ -package core.game.node.entity.skill; \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/prayer/BoneBuryingOptionPlugin.java b/Server/src/main/java/core/game/node/entity/skill/prayer/BoneBuryingOptionPlugin.java deleted file mode 100644 index cd2f0d833..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/prayer/BoneBuryingOptionPlugin.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.node.entity.skill.prayer; - -import core.cache.def.impl.ItemDefinition; -import core.game.content.global.Bones; -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 rs09.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/java/core/game/node/entity/skill/prayer/PrayerAltarPlugin.java b/Server/src/main/java/core/game/node/entity/skill/prayer/PrayerAltarPlugin.java deleted file mode 100644 index a8a16b454..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/prayer/PrayerAltarPlugin.java +++ /dev/null @@ -1,249 +0,0 @@ -package core.game.node.entity.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); - player.getSkills().getRestoration()[Skills.PRAYER] = new SkillRestoration(Skills.PRAYER); - player.getSkills().getRestoration()[Skills.PRAYER].restartHpSummPray(false); - } - 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/java/core/game/node/entity/skill/runecrafting/CombinationRune.java b/Server/src/main/java/core/game/node/entity/skill/runecrafting/CombinationRune.java deleted file mode 100644 index facb37967..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/CombinationRune.java +++ /dev/null @@ -1,130 +0,0 @@ -package core.game.node.entity.skill.runecrafting; - -import core.game.node.item.Item; - -/** - * Represents the combination rune. - * @author 'Vexia - */ -public enum CombinationRune { - MIST(new Item(4695), 6, 8.0, new Altar[] { Altar.WATER, Altar.AIR }, Rune.AIR, Rune.WATER), - DUST(new Item(4696), 10, 8.3, new Altar[] { Altar.EARTH, Altar.AIR }, Rune.AIR, Rune.EARTH), - MUD(new Item(4698), 13, 9.3, new Altar[] { Altar.EARTH, Altar.WATER }, Rune.WATER, Rune.EARTH), - SMOKE(new Item(4697), 15, 8.5, new Altar[] { Altar.FIRE, Altar.AIR }, Rune.AIR, Rune.FIRE), - STEAM(new Item(4694), 19, 9.3, new Altar[] { Altar.WATER, Altar.FIRE }, Rune.WATER, Rune.FIRE), - LAVA(new Item(4699), 23, 10.0, new Altar[] { Altar.FIRE, Altar.EARTH }, Rune.EARTH, Rune.FIRE); - - /** - * Represents the rune rewarded. - */ - private final Item rune; - - /** - * Represents the required level. - */ - private final int level; - - /** - * Represents the base experience. - */ - private final double experience; - - /** - * Represents the altars. - */ - private final Altar[] altars; - - /** - * Represents the runes required. - */ - private final Rune[] runes; - - /** - * Constructs a new {@code CombinationRune} {@code Object}. - * @param rune the rune. - * @param level the level. - * @param experience the base experience/ - * @param runes the runes. - */ - CombinationRune(final Item rune, final int level, final double experience, final Altar[] altars, final Rune... runes) { - this.rune = rune; - this.level = level; - this.experience = experience; - this.altars = altars; - this.runes = runes; - } - - /** - * Gets the rune. - * @return The rune. - */ - public final Item getRune() { - return rune; - } - - /** - * Gets the level. - * @return The level. - */ - public final int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public final double getExperience() { - return experience; - } - - /** - * Gets the runes. - * @return The runes. - */ - public final Rune[] getRunes() { - return runes; - } - - /** - * Method used to get the high experience. - * @return the experience incremented. - */ - public final double getHighExperience() { - return (experience % 1 == 0) ? experience + 5 : experience + 8; - } - - /** - * Gets the altars. - * @return The altars. - */ - public Altar[] getAltars() { - return altars; - } - - /** - * Gets the combination rune. - * @param altar the altar. - * @return the combo rune. - */ - public static CombinationRune forAltar(final Altar altar, final Item item) { - for (CombinationRune rune : values()) { - for (Altar alt : rune.getAltars()) { - if (alt == altar) { - String altarElement = alt.name(); - String talismanElement = item.getName().contains("talisman") ? Talisman.forItem(item).name() : Rune.forItem(item).name(); - if (altarElement.equals(talismanElement)) { - continue; - } - for (Rune r : rune.getRunes()) { - if (r.name().equals(talismanElement)) { - return rune; - } - } - continue; - } - } - } - return null; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/EnchantTiaraPulse.java b/Server/src/main/java/core/game/node/entity/skill/runecrafting/EnchantTiaraPulse.java deleted file mode 100644 index 9be72c321..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/EnchantTiaraPulse.java +++ /dev/null @@ -1,104 +0,0 @@ -package core.game.node.entity.skill.runecrafting; - -import core.game.node.entity.impl.Animator; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -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 enchanting of a tiara pulse. - * @author 'Vexia - * @date 02/11/2013 - */ -public class EnchantTiaraPulse extends SkillPulse { - - /** - * Represents the tiara. - */ - private final Tiara tiara; - - /** - * Represents the item tiara. - */ - private final Item TIARA = new Item(5525); - - private static final Animation ANIMATION = new Animation(791, Animator.Priority.HIGH); - private static final Graphics GRAPHICS = new Graphics(186, 100); - - /** - * Represents the amount. - */ - private int amount; - - /** - * Constructs a new {@code EnchantTiaraPulse.java} {@code Object}. - * @param player the player. - * @param node the node. - */ - public EnchantTiaraPulse(Player player, Item node, final Tiara tiara, final int amount) { - super(player, node); - this.tiara = tiara; - this.amount = amount; - } - - @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; - } - } - - @Override - public boolean checkRequirements() { - if (!player.getInventory().containsItem(TIARA)) { - player.getPacketDispatch().sendMessage("You need a tiara."); - return false; - } - return true; - } - - @Override - public void animate() { - player.animate(ANIMATION); - player.graphics(GRAPHICS); - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - setDelay(2); - return false; - } - if (player.getInventory().remove(TIARA) && player.getInventory().remove(tiara.getTalisman().getTalisman())) { - player.getInventory().add(tiara.getTiara()); - player.getSkills().addExperience(Skills.RUNECRAFTING, tiara.getExperience(), true); - - if (tiara == Tiara.AIR) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 0, 11); - } - // Craft an earth tiara on the Earth Altar - if (tiara == Tiara.EARTH) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 11); - } - } - amount--; - return amount == 0; - } - - @Override - public void message(int type) { - if (type == 1) { - player.getPacketDispatch().sendMessage("You bind the power of the talisman into your tiara."); - } - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/MysteriousRuinPlugin.java b/Server/src/main/java/core/game/node/entity/skill/runecrafting/MysteriousRuinPlugin.java deleted file mode 100644 index 1a7795051..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/MysteriousRuinPlugin.java +++ /dev/null @@ -1,85 +0,0 @@ -package core.game.node.entity.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 rs09.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/java/core/game/node/entity/skill/runecrafting/RuneCraftPulse.java b/Server/src/main/java/core/game/node/entity/skill/runecrafting/RuneCraftPulse.java deleted file mode 100644 index 6485dc3ac..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/RuneCraftPulse.java +++ /dev/null @@ -1,346 +0,0 @@ -package core.game.node.entity.skill.runecrafting; - -import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.impl.Animator.Priority; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.SkillPulse; -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.tools.RandomFunction; -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_BASE; -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_RC; -import rs09.game.world.GameWorld; - -import java.util.ArrayList; -import java.util.Arrays; - -/** - * A class used to craft runes. - * - * @author Vexia - */ -public final class RuneCraftPulse extends SkillPulse { - - /** - * Represent the rune essence item. - */ - private static final Item RUNE_ESSENCE = new Item(1436); - - /** - * Represents the pure essence item. - */ - private static final Item PURE_ESSENCE = new Item(7936); - - /** - * Represents the binding necklace item. - */ - private static final Item BINDING_NECKLACE = new Item(5521); - - /** - * Represents the animation used for this pulse. - */ - private static final Animation ANIMATION = new Animation(791, Priority.HIGH); - - /** - * Represents the graphics used for this pulse. - */ - private static final Graphics GRAPHICS = new Graphics(186, 100); - - /** - * Represents the altar. - */ - private final Altar altar; - - /** - * Represents the rune we're crafting. - */ - private final Rune rune; - - /** - * Represents the combination rune(if any) - */ - private final CombinationRune combo; - - /** - * Represents if it's a combination pulse. - */ - private final boolean combination; - - /** - * Represents the talisman to remove. - */ - private Talisman talisman; - - /** - * Constructs a new {@code RuneCraftPulse} {@code Object}. - * - * @param player the player. - * @param node the node. - * @param altar the altar. - */ - public RuneCraftPulse(Player player, Item node, final Altar altar, final boolean combination, final CombinationRune combo) { - super(player, node); - this.altar = altar; - this.rune = altar.getRune(); - this.combination = combination; - this.combo = combo; - this.resetAnimation = false; - } - - @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."); - return false; - } - if (combination && !player.getInventory().containsItem(PURE_ESSENCE)) { - player.getPacketDispatch().sendMessage("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."); - 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."); - return false; - } - if (altar.isOurania() && !player.getInventory().containsItem(PURE_ESSENCE)) { - player.getPacketDispatch().sendMessage("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."); - 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."); - return false; - } - talisman = t; - } - } - player.lock(4); - return true; - } - - @Override - public void animate() { - player.animate(ANIMATION); - player.graphics(GRAPHICS); - player.getAudioManager().send(2710); - } - - @Override - public boolean reward() { - if (!combination) { - craft(); - } else { - combine(); - } - 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; - } - } - - /** - * Method used to craft runes. - */ - private final void craft() { - final Item item = new Item(getEssence().getId(), getEssenceAmount()); - int amount = player.getInventory().getAmount(item); - if (!altar.isOurania()) { - Item i = new Item(rune.getRune().getId(), amount * getMultiplier()); - - if (player.getInventory().remove(item) && player.getInventory().hasSpaceFor(i)) { - player.getInventory().add(i); - player.incrementAttribute("/save:" + STATS_BASE + ":" + STATS_RC, amount); - player.getSkills().addExperience(Skills.RUNECRAFTING, rune.getExperience() * amount, true); - - // Achievement Diary handling - // Craft some nature runes - if (altar == Altar.NATURE) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 3); - } - // Craft 196 or more air runes simultaneously - if (altar == Altar.AIR && i.getAmount() >= 196) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 2); - } - // Craft a water rune at the Water Altar - if (altar == Altar.WATER && rune == Rune.WATER) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 11); - } - - } - } 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() { - 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) { - 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))) { - 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); - } - } - 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."); - } - } - } - } - } - - /** - * Checks if the player has the spell imbue. - * - * @return {@code True} if so. - */ - private boolean hasSpellImbue() { - return player.getAttribute("spell:imbue", 0) > GameWorld.getTicks(); - } - - /** - * Gets the essence amount. - * - * @return the amount of essence. - */ - private int getEssenceAmount() { - if (altar.isOurania() && player.getInventory().containsItem(PURE_ESSENCE)) { - return player.getInventory().getAmount(PURE_ESSENCE); - } - 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); - } - } - - /** - * 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; - } - } - - /** - * Gets the multiplied amount of runes to make. - * - * @return the amount. - */ - public int getMultiplier() { - if (altar.isOurania()) { - return 1; - } - int i = 0; - for (int level : rune.getMultiple()) { - if (player.getSkills().getLevel(Skills.RUNECRAFTING) >= level) { - i++; - } - } - - if (player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE).isComplete(1) - && new ArrayList<>(Arrays.asList(Rune.AIR, Rune.WATER, Rune.FIRE, Rune.EARTH)).contains(rune) - && RandomFunction.getRandom(10) == 0) { //approximately 10% chance //TODO fix RandomFunction to be more random, should be uniformly distributed but isn't - i += 1; - } - - return i != 0 ? i : 1; - } - - /** - * Method used to check if the player has a binding necklace. - * - * @return True if so. - */ - public boolean hasBindingNecklace() { - return player.getEquipment().containsItem(BINDING_NECKLACE); - } - - /** - * Gets the altar. - * - * @return The altar. - */ - public Altar getAltar() { - return altar; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/runecrafting/abyss/AbyssalNPC.java b/Server/src/main/java/core/game/node/entity/skill/runecrafting/abyss/AbyssalNPC.java deleted file mode 100644 index 25850c2ac..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/runecrafting/abyss/AbyssalNPC.java +++ /dev/null @@ -1,91 +0,0 @@ -package core.game.node.entity.skill.runecrafting.abyss; - -import core.game.node.entity.skill.runecrafting.RunePouch; -import core.game.node.entity.Entity; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.map.Location; -import core.tools.RandomFunction; - -/** - * Handles an abyssal npc. - * @author Vexia - */ -public final class AbyssalNPC extends AbstractNPC { - - /** - * Constructs a new {@code AbyssalNPC} {@code Object}. - */ - public AbyssalNPC() { - super(0, null, true); - setAggressive(true); - } - - /** - * Constructs a new {@code AbyssalNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public AbyssalNPC(int id, Location location) { - super(id, location, true); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new AbyssalNPC(id, location); - } - - @Override - public void init() { - super.init(); - this.setDefaultBehavior(); - } - - @Override - public void handleTickActions() { - super.handleTickActions(); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - Player p = killer.asPlayer(); - if (RandomFunction.random(750) < 12) { - Item pouch = getPouch(p); - if (pouch != null) { - getDefinition().getDropTables().createDrop(pouch, p, this, getLocation()); - } - } - } - } - - /** - * Gets the next pouch item. - * @param player the player. - * @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.MEDIUM.getPouch()) || player.hasItem(RunePouch.MEDIUM.getDecayedPouch())) { - pouch = RunePouch.LARGE.getPouch(); - } - if (player.hasItem(RunePouch.LARGE.getPouch()) || player.hasItem(RunePouch.LARGE.getDecayedPouch())) { - pouch = RunePouch.GIANT.getPouch(); - } - if (player.hasItem(RunePouch.GIANT.getPouch()) || player.hasItem(RunePouch.GIANT.getDecayedPouch())) { - pouch = null; - } - return pouch; - } - - @Override - public int[] getIds() { - return new int[] { 2263, 2264, 2265 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/AbyssalDemonNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/AbyssalDemonNPC.java deleted file mode 100644 index 1b41b6f3c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/AbyssalDemonNPC.java +++ /dev/null @@ -1,75 +0,0 @@ -package core.game.node.entity.skill.slayer; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -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.Graphics; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Handles the abyssal npc. - * @author Vexia - */ -@Initializable -public class AbyssalDemonNPC extends AbstractNPC { - - /** - * The melee swing handler. - */ - private static final MeleeSwingHandler SWING_HANDLER = new MeleeSwingHandler() { - - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - if (victim instanceof Player && RandomFunction.random(8) <= 2) { - boolean npc = RandomFunction.random(100) <= 50; - Entity source = npc ? victim : entity; - Entity teleported = npc ? entity : victim; - Location loc = source.getLocation().transform(Direction.values()[RandomFunction.random(Direction.values().length)], 1); - if (loc != null && !loc.equals(source.getLocation()) && RegionManager.isTeleportPermitted(loc) && RegionManager.getObject(loc) == null) { - teleported.graphics(Graphics.create(409)); - teleported.teleport(loc, 1); - } - } - return super.swing(entity, victim, state); - } - }; - - /** - * Constructs a new {@Code AbyssalNPC} {@Code Object} - */ - public AbyssalDemonNPC() { - super(-1, null); - } - - /** - * Constructs a new {@Code AbyssalNPC} {@Code Object} - * @param id the id. - * @param location the location. - */ - public AbyssalDemonNPC(int id, Location location) { - super(id, location); - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return SWING_HANDLER; - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new AbyssalDemonNPC(id, location); - } - - @Override - public int[] getIds() { - return Tasks.ABYSSAL_DEMONS.getNpcs(); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/EnchantedGemDialogue.java b/Server/src/main/java/core/game/node/entity/skill/slayer/EnchantedGemDialogue.java deleted file mode 100644 index f56ed151b..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/EnchantedGemDialogue.java +++ /dev/null @@ -1,266 +0,0 @@ -package core.game.node.entity.skill.slayer; - -import core.cache.def.impl.NPCDefinition; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.player.Player; -import core.game.world.map.Direction; -import core.plugin.Initializable; - -/** - * Rerpresents the enchanted gem dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class EnchantedGemDialogue extends DialoguePlugin { - - /** - * Represents the id associated with this plugin. - */ - public static final int ID = 77777; - - /** - * Constructs a new {@code EnchantedGemDialogue} {@code Object}. - */ - public EnchantedGemDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code EnchantedGemDialogue} {@code Object}. - * @param player the player. - */ - public EnchantedGemDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new EnchantedGemDialogue(player); - } - - @Override - public boolean open(Object... args) { - player.faceLocation(player.getLocation().transform(1, 0, 0)); - player.setDirection(Direction.EAST); - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "Hello there " + player.getUsername() + ", what can I help you with?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "How am I doing so far?", "Who are you?", "Where are you?", "Got any tips for me?", "Nothing really."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How am I doing so far?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who are you?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where are you?"); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Got any tips for me?"); - stage = 400; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nothing really."); - stage = 99; - break; - - } - break; - case 99: - end(); - break; - case 10: - if (!player.getSlayer().hasTask()) { - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You need something new to hunt. Come and see me", "When you can and I'll give you a new task."); - stage = 11; - break; - } - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You're currently assigned to kill " + (player.getSlayer().getTask() == Tasks.JAD ? " TzTok-Jad!" : NPCDefinition.forId((player.getSlayer().getTask().getNpcs()[0])).getName().toLowerCase() + "'s;"), "only " + player.getSlayer().getAmount() + " more to go."); - player.varpManager.get(2502).setVarbit(0,player.getSlayer().flags.getTaskFlags() >> 4).send(player); - stage = 11; - break; - case 11: - interpreter.sendOptions("Select an Option", "Who are you?", "Where are you?", "Got any tips for me?", "That's all thanks."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "My name's " + NPCDefinition.forId(player.getSlayer().getMaster().getNpc()).getName() + ", I'm the Slayer Master best able", "to train you."); - stage = 21; - break; - case 2: - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You'll find me in " + masterLocation() + ", I'll be here when you need a", "new task."); - stage = 31; - break; - case 3: - if (!player.getSlayer().hasTask()) { - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You need something new to hunt."); - stage = 99; - break; - } - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, player.getSlayer().getTask().getTip()); - stage = 401; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's all thanks."); - stage = 99; - break; - case 99: - end(); - break; - } - break; - case 20: - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "My name's " + NPCDefinition.forId(player.getSlayer().getMaster().getNpc()).getName() + ", I'm the Slayer Master best able", "to train you."); - stage = 21; - break; - case 21: - interpreter.sendOptions("Select an Option", "How am I doing so far?", "Where are you?", "Got any tips for me?", "That's all thanks."); - stage = 25; - break; - case 25: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How am I doing so far?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You'll find me in " + masterLocation() + ", I'll be here when you need a", "new task."); - stage = 31; - break; - case 3: - if (!player.getSlayer().hasTask()) { - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You need something new to hunt."); - stage = 99; - break; - } - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, player.getSlayer().getTask().getTip()); - stage = 401; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's all thanks."); - stage = 99; - break; - - } - break; - case 30: - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You'll find me in " + masterLocation() + ", I'll be here when you need a", "new task."); - stage = 31; - break; - case 31: - interpreter.sendOptions("Select an Option", "How am I doing so far?", "Who are you?", "Got any tips for me?", "That's all thanks."); - stage = 32; - break; - case 32: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How am I doing so far?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "My name's " + NPCDefinition.forId(player.getSlayer().getMaster().getNpc()).getName() + ", I'm the Slayer Master best able", "to train you."); - stage = 21; - break; - case 3: - if (!player.getSlayer().hasTask()) { - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You need something new to hunt."); - stage = 0; - break; - } - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, player.getSlayer().getTask().getTip()); - stage = 401; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's all thanks."); - stage = 99; - break; - - } - break; - case 400: - if (!player.getSlayer().hasTask()) { - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You need something new to hunt."); - stage = 0; - break; - } - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, player.getSlayer().getTask().getTip()); - stage = 401; - break; - case 401: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Great, thanks!"); - stage = 403; - break; - case 403: - interpreter.sendOptions("Select an Option", "How am I doing so far?", "Who are you?", "Where are you?", "That's all thanks."); - stage = 404; - break; - case 404: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How am I doing so far?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "My name's " + NPCDefinition.forId(player.getSlayer().getMaster().getNpc()).getName() + ", I'm the Slayer Master best able", "to train you."); - stage = 21; - break; - case 3: - interpreter.sendDialogues(player.getSlayer().getMaster().getNpc(), FacialExpression.HALF_GUILTY, "You'll find me in " + masterLocation() + ", I'll be here when you need a", "new task."); - stage = 31; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's all thanks."); - stage = 99; - break; - - } - break; - } - return true; - } - - /** - * Method used to get the masters location. - * @return the location. - */ - public String masterLocation() { - if (player.getSlayer().getMaster().getNpc() == Master.MAZCHNA.getNpc()) - return "Canifis"; - else if (player.getSlayer().getMaster().getNpc() == Master.TURAEL.getNpc()) - return "Taverley"; - else if (player.getSlayer().getMaster().getNpc() == Master.CHAELDAR.getNpc()) - return "Zanaris"; - else if (player.getSlayer().getMaster().getNpc() == Master.VANNAKA.getNpc()) - return "Edgeville dungeon"; - else if (player.getSlayer().getMaster().getNpc() == Master.DURADEL.getNpc()) - return "Shilo village"; - else if(player.getSlayer().getMaster() == null) { - return "the Gnome Stronghold"; - } - return null; - } - - @Override - public int[] getIds() { - return new int[] { ID }; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/FishingExplosivePlugin.java b/Server/src/main/java/core/game/node/entity/skill/slayer/FishingExplosivePlugin.java deleted file mode 100644 index 7f577e01d..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/FishingExplosivePlugin.java +++ /dev/null @@ -1,228 +0,0 @@ -package core.game.node.entity.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 rs09.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(final Entity entity, CombatStyle style) { - final Player pl = getAttribute("player", null); - return pl != null && pl == entity && super.isAttackable(entity, style); - } - - @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/java/core/game/node/entity/skill/slayer/Master.java b/Server/src/main/java/core/game/node/entity/skill/slayer/Master.java deleted file mode 100644 index 660f71dd1..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/Master.java +++ /dev/null @@ -1,261 +0,0 @@ -package core.game.node.entity.skill.slayer; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -/** - * A non-garbage way of representing slayer masters - * @author ceik - */ -public enum Master { - TURAEL(8273, 0, 0, new int[]{15, 50}, new int[]{0, 0, 0}, - new Task(Tasks.BANSHEE, 8), - new Task(Tasks.CRAWLING_HAND, 8), - new Task(Tasks.SKELETONS, 7), - new Task(Tasks.WOLVES, 7), - new Task(Tasks.GOBLINS, 7), - new Task(Tasks.DOG, 7), - new Task(Tasks.CAVE_SLIMES, 8), - new Task(Tasks.KALPHITES, 6), - new Task(Tasks.ICE_FIENDS, 8), - new Task(Tasks.MINOTAURS, 7), - new Task(Tasks.CAVE_CRAWLERS, 8), - new Task(Tasks.GHOSTS, 7), - new Task(Tasks.BEARS, 7), - new Task(Tasks.BATS, 7), - new Task(Tasks.ZOMBIES, 7), - new Task(Tasks.SCORPIONS, 7), - new Task(Tasks.CAVE_BUG, 8), - new Task(Tasks.DESERT_LIZARDS, 8), - new Task(Tasks.DWARF, 7), - new Task(Tasks.SPIDERS, 6), - new Task(Tasks.BIRDS, 6), - new Task(Tasks.COWS, 8), - new Task(Tasks.MONKEYS, 6), - new Task(Tasks.RATS, 7)), - MAZCHNA(8274, 20, 0, new int[]{30, 70}, new int[]{2, 5, 15}, - new Task(Tasks.VAMPIRES, 6), - new Task(Tasks.GHOULS, 7), - new Task(Tasks.BANSHEE, 8), - new Task(Tasks.CRAWLING_HAND, 8), - new Task(Tasks.SKELETONS, 7), - //MOGRES -> new Task(Tasks2.Mogres, 8), - new Task(Tasks.PYREFIENDS, 8), - //KILLERWATTS -> new Task(Tasks2.KILLERWATTS, 6), - new Task(Tasks.SHADE,8), - new Task(Tasks.COCKATRICES, 8), - new Task(Tasks.HILL_GIANTS,7), - new Task(Tasks.ROCK_SLUGS,8), - new Task(Tasks.DESERT_LIZARDS, 8), - new Task(Tasks.CAVE_SLIMES, 8), - new Task(Tasks.HOBGOBLINS, 7), - new Task(Tasks.WOLVES, 7), - new Task(Tasks.DOG,7), - new Task(Tasks.FLESH_CRAWLERS,7), - new Task(Tasks.CATABLEPONS,8), - //WALL BEASTS -> new Task(Tasks2.WALL_BEASTS,7), - new Task(Tasks.KALPHITES,6), - new Task(Tasks.CAVE_CRAWLERS, 8), - new Task(Tasks.GHOSTS,7), - new Task(Tasks.BEARS,6), - new Task(Tasks.EARTH_WARRIORS,6), - new Task(Tasks.ZOMBIES,7), - new Task(Tasks.ICE_WARRIOR, 7), - new Task(Tasks.CAVE_BUG,8), - new Task(Tasks.BATS,7), - new Task(Tasks.SCORPIONS,7)), - VANNAKA(1597, 40, 0, new int[]{30, 80}, new int[]{4, 20, 60}, - new Task(Tasks.HARPIE_BUG_SWARMS,8), - // SHADOW WARRIORS -> new Task(Tasks2.SHADOW_WARRIORS, 8), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.DUST_DEVILS,8), - new Task(Tasks.ABERRANT_SPECTRES, 8), - new Task(Tasks.JUNGLE_HORRORS, 8), - new Task(Tasks.TUROTHS, 8), - new Task(Tasks.JELLIES,8), - new Task(Tasks.INFERNAL_MAGES,8), - new Task(Tasks.BASILISKS,8), - new Task(Tasks.PYREFIENDS,8), - new Task(Tasks.SHADE,8), - new Task(Tasks.COCKATRICES,8), - new Task(Tasks.OTHERWORDLY_BEING,8), - new Task(Tasks.SPIRTUAL_MAGES,3), - new Task(Tasks.SPIRTUAL_RANGERS, 3), - new Task(Tasks.SPIRTUAL_WARRIORS,3), - new Task(Tasks.DAGANNOTHS, 7), - //ELVES -> new Task(Tasks2.ELVES, 7), - new Task(Tasks.WEREWOLFS,7), - new Task(Tasks.KURASKS,7), - new Task(Tasks.KALPHITES,7), - new Task(Tasks.FIRE_GIANTS,7), - new Task(Tasks.OGRES,7), - new Task(Tasks.MOSS_GIANTS,7), - new Task(Tasks.TROLLS,7), - new Task(Tasks.HELLHOUNDS,7), - new Task(Tasks.BRONZE_DRAGONS,7), - new Task(Tasks.BLUE_DRAGONS,7), - new Task(Tasks.LESSER_DEMONS,7), - new Task(Tasks.ANKOU,7), - new Task(Tasks.ICE_GIANTS,7), - new Task(Tasks.BRINE_RATS,7), - //FEVER SPIDERS -> new Task(Tasks2.FEVER_SPIDERS,7), - new Task(Tasks.VAMPIRES,7), - //MOLANISKS -> new Task(Tasks2.MOLANISKS,7), - //MOGRES -> new Task(Tasks2.MOGRES,7), - new Task(Tasks.ICE_WARRIOR,7), - new Task(Tasks.GHOULS,7), - new Task(Tasks.DESERT_LIZARDS,7), - new Task(Tasks.ROCK_SLUGS,7), - new Task(Tasks.CAVE_SLIMES,7), - new Task(Tasks.CAVE_CRAWLERS,7), - new Task(Tasks.CAVE_BUG,7), - new Task(Tasks.HILL_GIANTS,7), - new Task(Tasks.HOBGOBLINS,7), - //KILLERWATTS -> new Task(Tasks2.KILLERWATTS,6), - new Task(Tasks.CROCODILES,6), - //SEA SNAKES -> new Task(Tasks2.SEA_SNAKES,6), - //TERROR DOGS -> new Task(Tasks2.TERROR_DOGS,6), - new Task(Tasks.GREEN_DRAGON,6), - //WALL BEAST -> new Task(Tasks2.WALL_BEAST,6), - new Task(Tasks.EARTH_WARRIORS,6), - new Task(Tasks.BANSHEE,6), - new Task(Tasks.CRAWLING_HAND,6), - new Task(Tasks.GARGOYLES, 5), - new Task(Tasks.NECHRYAELS, 5), - new Task(Tasks.ABYSSAL_DEMONS, 5)), - - CHAELDAR(1598, 70, 25, new int[]{110, 170}, new int[]{10, 50, 150}, - new Task(Tasks.ABYSSAL_DEMONS,12), - new Task(Tasks.IRON_DRAGONS,12), - new Task(Tasks.NECHRYAELS, 12), - new Task(Tasks.KURASKS, 12), - new Task(Tasks.FIRE_GIANTS, 12), - new Task(Tasks.SPIRTUAL_WARRIORS,4), - new Task(Tasks.SPIRTUAL_RANGERS,4), - new Task(Tasks.SPIRTUAL_MAGES,4), - new Task(Tasks.GARGOYLES,11), - new Task(Tasks.KALPHITES,11), - new Task(Tasks.DAGANNOTHS,11), - new Task(Tasks.BRONZE_DRAGONS,11), - new Task(Tasks.TROLLS,11), - new Task(Tasks.JUNGLE_HORRORS,10), - new Task(Tasks.BLACK_DEMONS,10), - new Task(Tasks.TUROTHS, 10), - new Task(Tasks.CAVE_HORRORS,10), - new Task(Tasks.JELLIES, 10), - new Task(Tasks.AVIANSIES,9), - new Task(Tasks.STEEL_DRAGONS,9), - new Task(Tasks.LESSER_DEMONS,9), - new Task(Tasks.GREATER_DEMONS,9), - new Task(Tasks.HELLHOUNDS,9), - new Task(Tasks.DUST_DEVILS,9), - new Task(Tasks.BLUE_DRAGONS,8), - //ELVES -> new Task(Tasks2.ELVES,8), - new Task(Tasks.ABERRANT_SPECTRES,8), - //SHADOW WARRIORS -> new Task(Tasks2.SHADOW_WARRIORS,8), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.SKELETAL_WYVERN,7), - //MUTATED ZYGOMITE -> new Task(Tasks2.MUTATED_ZYGOMITES,7), - new Task(Tasks.INFERNAL_MAGES,7), - new Task(Tasks.BRINE_RATS,7), - //FEVER SPIDERS -> new Task(Tasks2.FEVER_SPIDERS,7), - new Task(Tasks.BASILISKS,7), - //WALL BEASTS -> new Task(Tasks2.WALL_BEASTS,6), - //MOLANISKS -> new Task(Tasks2.MOLANISKS,6), - //MOGRES -> new Task(Tasks2.MOGRES,6), - new Task(Tasks.PYREFIENDS,6), - new Task(Tasks.HARPIE_BUG_SWARMS,6), - new Task(Tasks.COCKATRICES,6), - new Task(Tasks.CAVE_SLIMES,6), - new Task(Tasks.DESERT_LIZARDS, 5), - new Task(Tasks.ROCK_SLUGS, 5), - new Task(Tasks.BANSHEE, 5), - new Task(Tasks.CAVE_CRAWLERS, 5)), - DURADEL(8275, 100, 50, new int[]{50, 199}, new int[]{15, 75, 225}, - new Task(Tasks.ABYSSAL_DEMONS,12), - new Task(Tasks.DARK_BEASTS,11), - new Task(Tasks.TZHAAR, 10), - new Task(Tasks.HELLHOUNDS, 10), - new Task(Tasks.KALPHITES,9), - new Task(Tasks.NECHRYAELS,9), - new Task(Tasks.MITHRIL_DRAGON,9), - new Task(Tasks.BLACK_DRAGONS,9), - new Task(Tasks.GREATER_DEMONS,9), - new Task(Tasks.DAGANNOTHS,9), - new Task(Tasks.GARGOYLES,8), - new Task(Tasks.BLACK_DEMONS,8), - //RED DRAGONS new Task(Tasks2.RED_DRAGONS,8), - new Task(Tasks.BLOODVELDS,8), - new Task(Tasks.AVIANSIES,8), - //SUQAHS new Task(Tasks2.SUQAHS,8), - new Task(Tasks.ABERRANT_SPECTRES,7), - new Task(Tasks.STEEL_DRAGONS,7), - new Task(Tasks.SPIRTUAL_MAGES,2), - new Task(Tasks.SPIRTUAL_RANGERS,2), - new Task(Tasks.SPIRTUAL_WARRIORS,2), - new Task(Tasks.SKELETAL_WYVERN,7), - new Task(Tasks.FIRE_GIANTS,7), - new Task(Tasks.TROLLS,6), - new Task(Tasks.IRON_DRAGONS,5), - new Task(Tasks.ANKOU,5), - new Task(Tasks.DUST_DEVILS,5), - new Task(Tasks.KURASKS,4), - new Task(Tasks.CAVE_HORRORS,4), - new Task(Tasks.BLUE_DRAGONS,4), - //ELVES new Task(Tasks2.ELVES,4), - //ZYGOMITES new Task(Tasks2.MUTATED_ZYGOMITES,2), - new Task(Tasks.WATERFIENDS,2)); - - private static HashMap idMap = new HashMap<>(); - - static{ - Arrays.stream(Master.values()).forEach(m -> idMap.putIfAbsent(m.npc_id, m)); - } - - int npc_id,required_combat,required_slayer; - int[] assignment_range; - int[] streakPoints; - List tasks; - Master(int npc_id, int required_combat, int required_slayer, int[] 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.streakPoints = streakPoints; - this.tasks = new ArrayList<>(Arrays.asList(tasks)); - } - - public static Master forId(int id){ - return idMap.get(id); - } - - public int getNpc(){ - return this.npc_id; - } - - public int[] getTaskPoints(){ - return streakPoints; - } - - public boolean hasRequirements(Player player){ - return player.getProperties().getCurrentCombatLevel() >= this.required_combat && player.getSkills().getLevel(Skills.SLAYER) >= this.required_slayer; - } - - public static boolean hasSameTask(Master master, Player player){ - return master.tasks.stream().filter(task -> task.task == player.getSlayer().getTask()).count() != 0; - } - - public static class Task{ - Tasks task; - Integer weight; - Task(Tasks task, Integer weight){ - this.task = task; - this.weight = weight; - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/MithrilDragonNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/MithrilDragonNPC.java deleted file mode 100644 index b75b510d8..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/MithrilDragonNPC.java +++ /dev/null @@ -1,97 +0,0 @@ -package core.game.node.entity.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.equipment.SwitchAttack; -import core.game.node.entity.combat.handlers.DragonfireSwingHandler; -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.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.tools.RandomFunction; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.MultiSwingHandler; - -/** - * Handles a mithril dragon npc. - * @author Vexia - */ -@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))); - - /** - * Constructs a new {@code MithrilDragonNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public MithrilDragonNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new {@code MithrilDragonNPC} {@code Object}. - */ - public MithrilDragonNPC() { - super(0, null); - } - - @Override - public void sendImpact(BattleState state) { - CombatStyle style = state.getStyle(); - if (style == null) { - return; - } - int maxHit = state.getEstimatedHit(); - if (maxHit < 1) { - return; - } - switch (style) { - case MELEE: - maxHit = 28; - break; - case MAGIC: - maxHit = 18; - break; - case RANGE: - maxHit = 18; - break; - } - if (state.getEstimatedHit() > maxHit) { - state.setEstimatedHit(RandomFunction.random(maxHit - 5, maxHit)); - } - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new MithrilDragonNPC(id, location); - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return combatAction; - } - - @Override - public int getDragonfireProtection(boolean fire) { - return 0x2 | 0x4 | 0x8; - } - - @Override - public int[] getIds() { - return Tasks.MITHRIL_DRAGON.getNpcs(); - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/NechryaelNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/NechryaelNPC.java deleted file mode 100644 index 1ce0c8546..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/NechryaelNPC.java +++ /dev/null @@ -1,187 +0,0 @@ -package core.game.node.entity.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 rs09.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) { - if (entity instanceof Player) { - final Player t = (Player) entity; - if (t != player) { - t.getPacketDispatch().sendMessage("This isn't spawned for you."); - return false; - } - } - return super.isAttackable(entity, style); - } - - @Override - public boolean isPoisonImmune() { - return true; - } - - @Override - public int[] getIds() { - return new int[] { DEATH_SPAWN }; - } - - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/RockSlugPlugin.java b/Server/src/main/java/core/game/node/entity/skill/slayer/RockSlugPlugin.java deleted file mode 100644 index 8cf5a5c68..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/RockSlugPlugin.java +++ /dev/null @@ -1,148 +0,0 @@ -package core.game.node.entity.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 rs09.plugin.PluginManager; - -/** - * 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 { - PluginManager.definePlugin(new RockSlugNPC()); - PluginManager.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/java/core/game/node/entity/skill/slayer/SkeletalWyvernNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/SkeletalWyvernNPC.java deleted file mode 100644 index cb8588c6c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SkeletalWyvernNPC.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.game.node.entity.skill.slayer; - -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.equipment.SwitchAttack; -import core.game.node.entity.combat.handlers.DragonfireSwingHandler; -import core.game.node.entity.npc.AbstractNPC; -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.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.tools.RandomFunction; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.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 void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - final Player player = killer.asPlayer(); - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR, 2, 8); - } - } - - @Override - public int[] getIds() { - return Tasks.SKELETAL_WYVERN.getNpcs(); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt b/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt deleted file mode 100644 index d925e3d1f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt +++ /dev/null @@ -1,87 +0,0 @@ -package core.game.node.entity.skill.slayer - -import api.ContentAPI -import api.EquipmentSlot -import core.game.node.entity.player.Player -import org.rs09.consts.Items -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks - -/** - * Represents a slayer equipment. - * @author Ceikry - */ -object SlayerEquipmentFlags { - - val blackMasks = (Items.BLACK_MASK_10_8901..Items.BLACK_MASK_8921).map { it }.toIntArray() - val slayerItems = intArrayOf(Items.NOSE_PEG_4168, Items.EARMUFFS_4166, Items.FACE_MASK_4164, *blackMasks, Items.SPINY_HELMET_4551, Items.SLAYER_CAPET_9787, Items.SLAYER_CAPE_9786, Items.SLAYER_HELMET_13263, Items.WITCHWOOD_ICON_8923, Items.MIRROR_SHIELD_4156) - - @JvmStatic - fun updateFlags(player: Player){ - var flags = 0 - if(SkillcapePerks.isActive(SkillcapePerks.TRICKS_OF_THE_TRADE,player) && ContentAPI.getAttribute(player, "cape_perks:tott:helmet-stored", false)) flags = 0x3F - else if(hasItem(player, Items.SLAYER_HELMET_13263)) flags = 0x1F - 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((ContentAPI.getItemFromEquipment(player, EquipmentSlot.HAT)?.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((ContentAPI.getItemFromEquipment(player, EquipmentSlot.AMULET)?.id ?: 0) == Items.WITCHWOOD_ICON_8923) flags = flags or (1 shl 7) - if((ContentAPI.getItemFromEquipment(player, EquipmentSlot.SHIELD)?.id ?: 0) == Items.MIRROR_SHIELD_4156) flags = flags or (1 shl 8) - player.slayer.flags.equipmentFlags = flags - } - - @JvmStatic - fun hasNosePeg(player: Player): Boolean{ - return player.slayer.flags.equipmentFlags and 1 == 1 - } - - @JvmStatic - fun hasEarmuffs(player: Player): Boolean { - return (player.slayer.flags.equipmentFlags shr 1) and 1 == 1 - } - - @JvmStatic - fun hasFaceMask(player: Player): Boolean { - return (player.slayer.flags.equipmentFlags shr 2) and 1 == 1 - } - - @JvmStatic - fun hasBlackMask(player: Player): Boolean { - return (player.slayer.flags.equipmentFlags shr 3) and 1 == 1 - } - - @JvmStatic - fun hasSpinyHelmet(player: Player): Boolean { - return (player.slayer.flags.equipmentFlags shr 4) and 1 == 1 - } - - @JvmStatic - fun hasWitchwoodIcon(player: Player): Boolean { - return (player.slayer.flags.equipmentFlags shr 7) and 1 == 1 - } - - @JvmStatic - fun hasMirrorShield(player: Player): Boolean { - return (player.slayer.flags.equipmentFlags shr 8) and 1 == 1 - } - - @JvmStatic - fun getDamAccBonus(player: Player): Double { - val isCape = player.slayer.flags.equipmentFlags == 0x3F - val hasMask = hasBlackMask(player) - - return if(isCape) 1.075 - else if(hasMask) 1.15 - else 1.0 - } - - private fun hasItem(player: Player, id: Int): Boolean{ - return (ContentAPI.getItemFromEquipment(player, EquipmentSlot.HAT)?.id ?: 0) == id - } - - fun isSlayerEq(item: Int): Boolean{ - return item in slayerItems - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerManager.java b/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerManager.java deleted file mode 100644 index ba749d6b1..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerManager.java +++ /dev/null @@ -1,345 +0,0 @@ -package core.game.node.entity.skill.slayer; - -import core.cache.def.impl.NPCDefinition; -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.tools.RandomFunction; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import rs09.game.node.entity.skill.slayer.SlayerFlags; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Manages the players slayer task. - * @author Vexia - * - */ -public final class SlayerManager { - - /** - * The player instance. - */ - private final Player player; - - /** - * The player's slayer flags - */ - public final SlayerFlags flags; - - /** - * Constructs a new {@Code SlayerManager} {@Code Object} - * @param player The player. - */ - public SlayerManager(Player player) { - this.player = player; - this.flags = new SlayerFlags(player); - } - - public void parse(JSONObject slayerData){ - Object m = slayerData.get("master"); - if(m != null) { - flags.setMaster(Master.forId(Integer.parseInt(m.toString()))); - } - Object t = slayerData.get("taskId"); - if(t != null) - flags.setTask(Tasks.values()[Integer.parseInt(t.toString())]); - Object a = slayerData.get("taskAmount"); - if(a != null) - flags.setTaskAmount(Integer.parseInt(a.toString())); - Object points = slayerData.get("points"); - if(points != null){ - flags.setPoints(Integer.parseInt(points.toString())); - } - Object taskStreak = slayerData.get("taskStreak"); - if(taskStreak != null){ - flags.setTaskStreak(Integer.parseInt(taskStreak.toString())); - } - Object la = slayerData.get("learned_rewards"); - if(la != null) { - JSONArray learnedArray = (JSONArray) slayerData.get("learned_rewards"); - for (int i = 0; i < learnedArray.size(); i++) { - boolean unlocked = (boolean) learnedArray.get(i); - switch(i){ - case 0: - if(unlocked) flags.unlockBroads(); - break; - case 1: - if(unlocked) flags.unlockRing(); - break; - case 2: - if(unlocked) flags.unlockHelm(); - break; - default: - break; - } - } - } - JSONArray removedTasks = (JSONArray) slayerData.get("removedTasks"); - if(removedTasks != null){ - for(int i = 0; i < removedTasks.size(); i++){ - flags.getRemoved().add(Tasks.values()[Integer.parseInt(removedTasks.get(i).toString())]); - } - } - Object completedTasks = slayerData.get("totalTasks").toString(); - if(completedTasks != null) flags.setCompletedTasks(Integer.parseInt(completedTasks.toString())); - if(flags.getCompletedTasks() >= 4) flags.flagCanEarnPoints(); - - //New system parsing - if(slayerData.containsKey("equipmentFlags")) - flags.setEquipmentFlags(Integer.parseInt(slayerData.get("equipmentFlags").toString())); - if(slayerData.containsKey("taskFlags")) - flags.setTaskFlags(Integer.parseInt(slayerData.get("taskFlags").toString())); - if(slayerData.containsKey("rewardFlags")) - flags.setRewardFlags(Integer.parseInt(slayerData.get("rewardFlags").toString())); - - } - - /** - * Called when a hunted creature dies. - * @param player The player. - * @param npc The NPC. You're currently - */ - public void finalizeDeath(Player player, NPC npc) { - if(hasTask()) { - player.getSkills().addExperience(Skills.SLAYER, npc.getSkills().getMaximumLifepoints()); - decrementAmount(1); - if(!hasTask()){ - flags.setTaskStreak(flags.getTaskStreak() + 1); - flags.setCompletedTasks(flags.getCompletedTasks() + 1); - if ((flags.getCompletedTasks() > 4 || flags.canEarnPoints() ) && flags.getMaster() != Master.TURAEL && flags.getPoints() < 64000) { - int points = flags.getMaster().getTaskPoints()[0]; - if (flags.getTaskStreak() % 50 == 0) { - points = flags.getMaster().getTaskPoints()[2]; - } else if (flags.getTaskStreak() % 10 == 0) { - points = flags.getMaster().getTaskPoints()[1]; - } - flags.incrementPoints(points); - if (flags.getPoints() > 64000) { - flags.setPoints(64000); - } - player.sendMessages("You've completed " + flags.getTaskStreak() + " tasks in a row and received " + points + " points, with a total of " + flags.getPoints(),"You have completed " + flags.getCompletedTasks() + " tasks in total. Return to a Slayer master."); - } else if(flags.getCompletedTasks() == 4){ - player.sendMessage("You've completed your task; you will start gaining points on your next task!"); - flags.flagCanEarnPoints(); - } else { - player.sendMessages("You've completed your task; Complete " + (4 - flags.getCompletedTasks()) + " more task(s) to start gaining points.", "Return to a Slayer master."); - } - } else { - //player.sendMessage("You're assigned to kill " + NPCDefinition.forId((player.getSlayer().getTask().getNpcs()[0])).getName().toLowerCase() + "s; Only " + getAmount() + " more to go."); - } - } - } - - /** - * Assigns a task to the manager. - * @param task the task. - * @param master the master. - */ - public void assign(Tasks task, final Master master) { - setMaster(master); - setTask(task); - setAmount(getRandomAmount(master.assignment_range)); - if (master == Master.DURADEL) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 8); - } else if (master == Master.VANNAKA) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 14); - } - getPlayer().varpManager.get(2502).setVarbit(0, flags.getTaskFlags() >> 4).send(player); - } - - /** - * Method used to assign a new task for a player. - * @param master the master to give the task. - */ - public void generate(Master master) { - final List tasks = new ArrayList<>(10); - final int[] taskWeightSum = {0}; - master.tasks.stream().filter(task -> canBeAssigned(task.task) && task.task.combatCheck <= player.getProperties().getCurrentCombatLevel()).forEach(task -> { - taskWeightSum[0] += task.weight; - tasks.add(task); - }); - Collections.shuffle(tasks, RandomFunction.RANDOM); - int rnd = RandomFunction.random(taskWeightSum[0]); - for(Master.Task task : tasks){ - if(rnd < task.weight) - assign(task.task,master); - rnd -= task.weight; - } - } - - public boolean canBeAssigned(Tasks task){ - return player.getSkills().getLevel(Skills.SLAYER) >= task.levelReq && !flags.getRemoved().contains(task); - } - - /** - * Clears the task. - */ - public void clear() { - setAmount(0); - } - - /** - * Gets a random amount. - * @param ranges the ranges. - * @return the amt. - */ - private int getRandomAmount(int[] ranges) { - return RandomFunction.random(ranges[0], ranges[1]); - } - - /** - * Gets the task name. - * @return the name. - */ - public String getTaskName() { - Tasks task = flags.getTask(); - if (task.getNpcs() == null) { - return "no npcs report me"; - } - if (task.getNpcs().length < 1) { - return "npc length too small report me"; - } - return NPCDefinition.forId(task.getNpcs()[0]).getName().toLowerCase(); - } - - /** - * Gets the task. - * @return The task. - */ - public Tasks getTask() { - return flags.getTask(); - } - - /** - * Sets the task. - * @param task The task to set. - */ - public void setTask(Tasks task){ - flags.setTask(task); - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the master. - * @return The master. - */ - public Master getMaster() { - return flags.getMaster(); - } - - /** - * Sets the master. - * @param master The master to set. - */ - public void setMaster(Master master) { - flags.setMaster(master); - } - - /** - * Checks if a Player contains a task. - * @return {@code True} if so. - */ - public boolean hasTask() { - return getAmount() > 0; - } - - /** - * Method used to check if the task is completed. - * @return True if so. - */ - public boolean isCompleted() { - return flags.getTaskAmount() <= 0; - } - - /** - * Gets the amount. - * @return The amount. - */ - public int getAmount() { - return flags.getTaskAmount(); - } - - /** - * Sets the amount. - * @param amount The amount to set. - */ - public void setAmount(int amount) { - flags.setTaskAmount(amount); - } - - /** - * Method used to decrement an amount. - * @param amount the amount. - */ - public void decrementAmount(int amount) { - flags.decrementTaskAmount(amount); - player.varpManager.get(2502).setVarbit(0, flags.getTaskFlags() >> 4).send(player); - } - - /** - * Method used to check if the player has started slayer. - * @return {@code True} if so. - */ - public boolean hasStarted() { - return flags.getCompletedTasks() > 0 || flags.getTaskAmount() > 0; - } - - /** - * Gets the slayerPoints. - * @return the slayerPoints. - */ - public int getSlayerPoints() { - return flags.getPoints(); - } - - /** - * Sets the slayerPoints. - * @param slayerPoints the slayerPoints to set - */ - public void setSlayerPoints(int slayerPoints) { - flags.setPoints(slayerPoints); - } - - /** - * Gets the taskCount. - * @return the taskCount. - */ - public int getTaskCount() { - return flags.getTaskStreak(); - } - - /** - * Sets the taskCount. - * @param taskCount the taskCount to set - */ - public void setTaskCount(int taskCount) { - flags.setTaskStreak(taskCount); - } - - /** - * Gets the removed. - * @return the removed. - */ - public List getRemoved() { - return flags.getRemoved(); - } - - public int getTotalTasks() {return flags.getCompletedTasks();} - - public boolean isCanEarnPoints(){ - return flags.canEarnPoints(); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerMasterDialogue.java b/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerMasterDialogue.java deleted file mode 100644 index ecb1aed5f..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerMasterDialogue.java +++ /dev/null @@ -1,718 +0,0 @@ -package core.game.node.entity.skill.slayer; - -import org.json.simple.JSONObject; -import rs09.ServerStore; -import rs09.game.world.GameWorld; -import core.game.content.dialogue.DialoguePlugin; -import core.game.content.dialogue.FacialExpression; -import core.game.content.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.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.Item; -import core.plugin.Initializable; - -import static rs09.tools.DialogueConstKt.END_DIALOGUE; - -/** - * Represents the dialogue plugin used for a slayer master. - * - * @author Vexia - */ -@Initializable -public final class SlayerMasterDialogue extends DialoguePlugin { - - /** - * The enchanted gem item. - */ - private static final Item GEM = new Item(4155, 1); - - /** - * The mithril axe item. - */ - private static final Item MITHRIL_AXE = new Item(1355); - - /** - * The holy symbol item. - */ - 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. - */ - private static final Item COINS = new Item(995, 99000); - - /** - * Represents the master talking. - */ - private Master master; - - /** - * The quest instance. - */ - private Quest quest; - - /** - * The achievement diary. - */ - private AchievementDiary diary; - - /** - * If we're chatting about our diary. - */ - private boolean isDiary; - - private final int level = 2; - - private int rerolls = 0; - - /** - * Constructs a new {@code SlayerMasterDialogue} {@code Object}. - */ - public SlayerMasterDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code SlayerMasterDialogue} {@code Object}. - * - * @param player the player. - */ - public SlayerMasterDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SlayerMasterDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (args[0] instanceof NPC) { - npc = (NPC) args[0]; - } - master = Master.forId(args[0] instanceof NPC ? ((NPC) args[0]).getId() : (int) args[0]); - quest = player.getQuestRepository().getQuest("Animal Magnetism"); - - if (master == Master.DURADEL) { - if (Skillcape.isMaster(player, Skills.SLAYER)) { - options("Ask about Skillcape", "Something else"); - stage = 900; - return true; - } - } - interpreter.sendDialogues(master.getNpc(), getExpression(master), "'Ello, and what are you after, then?"); - if (master == Master.VANNAKA) { - stage = -1; - } else { - stage = 0; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - rerolls = ServerStore.getInt(getStoreFile(), player.getUsername().toLowerCase()); - if (isDiary) { - switch (stage) { - case 999: - end(); - break; - case -1: - options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); - stage++; - break; - case 0: - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.VARROCK); - } - 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: - player("See you later."); - stage = 999; - break; - } - break; - case 440: - npc("I can imagine! I expect you'll be wanting a reward for", "your hard efforts, eh?"); - stage++; - break; - case 441: - player("Yes, please."); - stage++; - break; - case 442: - diary.setLevelRewarded(level); - for (Item i : diary.getType().getRewards(level)) { - player.getInventory().add(i, player); - } - npc("I'm happy to say that you've done well, so I will reward", "you suitably for your work."); - stage++; - break; - case 443: - player("Great! Thanks."); - stage++; - break; - case 444: - sendDialogue("Vannaka takes the Varrock armour and carves some symbols into it.", "He waves his hands over the symbols and the armour appears to glow", "brilliantly."); - stage++; - break; - case 450: - player.getInventory().add(diary.getType().getRewards(2)[0], player); - npc("You 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 in Varrock it can help you", "discover some quite useful things. Eventually, with", "enough exploration, the people of Varrock 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 levels of Varrock Armour,", "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 Varrock are happy to see", "you visiting the land."); - stage = 41; - break; - case 430: - npc("Just complete the tasks so they're all ticked off, then", "you can claim your 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 Varrock Armour, speak to Vannaka", "Rat Burgis, and myself."); - stage = 41; - break; - case 50: - end(); - break; - } - return true; - } - switch (stage) { - case 999: - end(); - break; - case -1: // vannaka - has options for achievement diary - if (!player.getSlayer().hasStarted()) { - options("Who are you?", "Do you have anything for trade?", "Er...nothing...", "I have a question about my Achievement Diary."); - stage = 1; - } else { - options("I need another assignment.", "Do you have anything for trade?", "Er...nothing...", "I have a question about my Achievement Diary."); - stage = 690; - } - break; - case 0: // not vannaka - if (master == Master.TURAEL) { // only give option to talk about holy axe with Turael - if (quest.getStage(player) == 30) { - options("I need another assignment.", "Do you have anything for trade?", "Er...nothing...", "I'm here about a quest."); - stage = 700; - break; - } else if (quest.getStage(player) == 31) { - options("I need another assignment.", "Do you have anything for trade?", "Er...nothing...", "Hello, I'm here about those trees again."); - stage = 700; - break; - } - } - if (!player.getSlayer().hasStarted()) { - options("Who are you?", "Do you have anything for trade?", "Er...nothing..."); - stage = 1; - } else { - options("I need another assignment.", "Do you have anything for trade?", "Er...nothing..."); - stage = 700; - } - break; - case 1: - switch (buttonId) { - case 1: - player("Who are you?"); - stage = 10; - break; - case 2: - player("Do you have anything for trade?"); - stage = 20; - break; - case 3: - player("Er...nothing..."); - stage = 999; - break; - case 4: - player("I have a question about my Achievement Diary."); - stage = 691; - break; - } - break; - case 10: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "I'm " + (master.getNpc() == 8273 ? "the lowest level Slayer Master available." : "one of the elite Slayer Masters."), - master.getNpc() == 8273 ? "The other Slayer Masters are spread around the world." : "I can teach you about the ways of the Slayer."); - stage = 11; - break; - case 11: - options("What's a Slayer?", "Never heard of you..."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - player("What's a Slayer?"); - stage = 100; - break; - case 2: - player("Never heard of you..."); - stage = 2000; - break; - } - break; - case 20: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "I have a wide selection of Slayer equipment; take a look!"); - stage = 21; - break; - case 21: - end(); - if (npc != null) { - npc.openShop(player); - } - break; - case 100: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Oh dear, what do they teach you in school?"); - stage = 101; - break; - case 101: - player("Well....er..."); - stage = 102; - break; - case 102: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "I suppose I'll have to educate you, then. A Slayer is", "someone who is trained to fight specific creatures. They", "know those creatures' every weakenss and strength. As", "you can guess, it makes killing those creatures a lot"); - stage = 103; - break; - case 103: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "easier."); - stage = 104; - break; - case 104: - options("Wow, can you teach me?", "Sounds useless to me."); - stage = 105; - break; - case 105: - switch (buttonId) { - case 1: - player("Wow, can you teach me?"); - stage = 500; - break; - case 2: - player("Sounds useless to me."); - stage = 1000; - break; - } - break; - case 500: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Hmmm, well, I'm not so sure..."); - stage = 501; - break; - case 501: - player("Pleeeaasssse! I'll be your best friend!"); - stage = 502; - break; - 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; - break; - } - interpreter.sendDialogues(master.getNpc(), getExpression(master), "Oh, okay then; you twisted my arm. You'll have to", "train against specific groups of creatures."); - stage = 503; - break; - case 503: - player("Okay then, what's first?"); - stage = 504; - break; - case 504: - if (player.getInventory().freeSlots() != 0) { - player.getInventory().add(GEM); - player.getSlayer().generate(master); - interpreter.sendDialogues(master.getNpc(), getExpression(master), "We'll start you off hunting " + player.getSlayer().getTaskName() + "'s, you'll need to", "kill " + player.getSlayer().getAmount() + " of them."); - stage = 510; - } else if (player.getInventory().freeSlots() == 0) { - player("Sorry, I don't have enough inventory space."); - stage = 999; - } - break; - case 510: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "You'll also need this enchanted gem. It allows Slayer", "Masters like myself to contact you and update you on", "your progress. Don't worry if you lose it; you can buy", "another from any Slayer Master."); - stage = 511; - break; - case 511: - player("Okay, great!"); - stage = 999; - break; - case 1000: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "That's what you think.."); - stage = 999; - break; - case 2000: - interpreter.sendDialogues(master.getNpc(), getExpression(master), "I am one of the greatest Slayer masters!"); - stage = 999; - break; - case 690: - switch (buttonId) { - case 1: - player("I need another assignment."); - stage = 701; - break; - case 2: - player("Do you have anything for trade?"); - stage = 20; - break; - case 3: - player("Er...nothing..."); - stage = 30; - break; - case 4: - player("I have a question about my Achievement Diary."); - stage = 691; - break; - } - break; - case 691: - sendDiaryDialogue(); - break; - case 700: - switch (buttonId) { - case 1: - player("I need another assignment."); - stage = 701; - break; - case 2: - player("Do you have anything for trade?"); - stage = 20; - break; - case 3: - player("Er...nothing..."); - stage = 30; - break; - case 4: - if (quest.getStage(player) == 30) { - player("I'm here about a quest. Ava said she saw you hanging", "around the moving trees near Draynor Manor."); - stage = 8000; - } else if (quest.getStage(player) == 31) { - player("Hello, I'm here about those trees again."); - stage = 8006; - } - break; - } - break; - case 8000:// avas. - npc("Ahh, you came to the right man, odd things, those trees.", "What is it you are needing exactly?"); - stage++; - break; - case 8001: - player("I think I need some of the wood from them, but my", "axe just bounced off the trunk."); - stage++; - break; - case 8002: - npc("Sounds like you need a blessed axe. No one really", "makes them, though these days."); - stage++; - break; - case 8003: - npc("If you can give me a mithril axe and a holy symbol of", "Saradomin I can let you have my axe. I'll make myself", "a new one when no one is pestering me for Slayer", "tasks."); - stage++; - break; - case 8004: - player("Okay, so I'll see whether I can spare an axe and a", "symbol. Thanks."); - stage++; - break; - case 8005: - quest.setStage(player, 31); - end(); - break; - case 8006: - if (player.hasItem(new Item(10491))) { - npc("You already have an axe."); - stage = 999; - break; - } - npc("I can make an axe for you now, if you wish.", "Remember, it will be no use for normal wooducutting", "after I have added the silver edge."); - stage++; - break; - case 8007: - player("I'd love one, thanks."); - stage++; - break; - case 8008: - if (!player.getInventory().containsItem(MITHRIL_AXE)) { - npc("You'll need to hand over both a mithril axe and a holy", "symbol of Saradomin. You don't have an axe in your", "pack, so I'm not able to help."); - stage = 999; - break; - } - if (!player.getInventory().containsItem(HOLY_SYMBOL)) { - npc("You'll need to hand over both a mithril axe and a holy", "symbol of Saradomin. You don't have a holy symbol in", "your pack, so I'm not able to help."); - stage = 999; - break; - } - if (player.getInventory().remove(MITHRIL_AXE, HOLY_SYMBOL)) { - player.getInventory().add(new Item(10491)); - npc("Here's a new axe; may it serve you well."); - stage = 999; - } - break; - 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; - break; - } - if (!player.getSlayer().hasTask()) { - player.getSlayer().generate(master); - if (player.getSlayer().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", "" + player.getSlayer().getAmount() + " " + player.getSlayer().getTaskName() + "s."); - } - 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 { - player.getSlayer().setTaskCount(0); - player.getSlayer().generate(master); - if (player.getSlayer().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", "" + player.getSlayer().getAmount() + " " + player.getSlayer().getTaskName() + "'s."); - } - stage = 844; - } - 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!"); - } - stage++; - break; - case 845: - switch (buttonId) { - case 1: - interpreter.sendDialogues(master.getNpc(), getExpression(master), player.getSlayer().getTask().getTip()); - stage = 860; - break; - case 2: - player("Okay, great!"); - stage = 999; - break; - case 3: - player("I'd like to re-roll this task."); - if(rerolls == 10){ - stage++; - } else { - player.getSlayer().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."); - player.getSlayer().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: - player("Great, thanks!"); - stage = 999; - break; - case 900: - switch (buttonId) { - case 1: - player("Can I buy a Skillcape of Slayer?"); - stage = 901; - break; - case 2: - interpreter.sendDialogues(master.getNpc(), FacialExpression.HALF_GUILTY, "'Ello, and what are you after, then?"); - stage = 0; - break; - } - break; - case 901: - interpreter.sendDialogues(Master.DURADEL.getNpc(), FacialExpression.HALF_GUILTY, "Certainly! Right when you give me 99000 coins."); - stage = 902; - break; - case 902: - options("Okay, here you go.", "No, thanks."); - stage = 903; - break; - case 903: - switch (buttonId) { - case 1: - player("Okay, here you go."); - stage = 904; - break; - case 2: - end(); - break; - } - break; - case 904: - if (Skillcape.purchase(player, Skills.SLAYER)) { - interpreter.sendDialogues(Master.DURADEL.getNpc(), FacialExpression.HALF_GUILTY, "There you go! Enjoy."); - } - 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; - } - - /** - * Checks which expression to use. - * - * @param master the master. - * @return the expression. - */ - private FacialExpression getExpression(Master master) { - if (master == Master.CHAELDAR) { - return FacialExpression.OLD_NORMAL; - } - return FacialExpression.HALF_GUILTY; - } - - /** - * Sends the diary dialogue. - */ - private void sendDiaryDialogue() { - isDiary = true; - if (diary == null) { - diary = player.getAchievementDiaryManager().getDiary(DiaryType.VARROCK); - } - if (diary.isComplete(level) && !diary.isLevelRewarded(level)) { - player("I've completed all the hard tasks in my Varrock", "Achievement Diary and, let me tell you, it wasn't an", "easy job."); - stage = 440; - return; - } - if (diary.isLevelRewarded(level) && diary.isComplete(level) && !player.hasItem(diary.getType().getRewards(level)[0])) { - player("I've seemed to have lost my armour..."); - stage = 460; - return; - } - options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); - stage = 0; - } - - @Override - public int[] getIds() { - return new int[]{70, 1598, 1596, 1597, 1599, 8275, 8273, 8274, 8649}; - } - - private JSONObject getStoreFile() { - return ServerStore.getArchive("daily-slayer-rerolls"); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerPlugin.java b/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerPlugin.java deleted file mode 100644 index c09bee668..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerPlugin.java +++ /dev/null @@ -1,91 +0,0 @@ -package core.game.node.entity.skill.slayer; - -import core.cache.def.impl.SceneryDefinition; -import core.game.content.global.action.ClimbActionHandler; -import core.game.content.global.action.DigAction; -import core.game.content.global.action.DigSpadeHandler; -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 related slayer nodes. - * @author Vexia - */ -@Initializable -public class SlayerPlugin extends OptionHandler { - - /** - * The bryne dig locations. - */ - private static final Location[] BRYNE_DIGS = new Location[] { new Location(2749, 3733, 0), new Location(2748, 3733, 0), new Location(2747, 3733, 0), new Location(2747, 3734, 0), new Location(2747, 3735, 0), new Location(2747, 3736, 0), new Location(2748, 3736, 0), new Location(2749, 3736, 0) }; - - @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 - public void run(Player player) { - player.teleport(new Location(2697, 10119, 0)); - player.sendMessages("You dig a hole...", "...And fall into a dark and slimy pit!"); - } - - }); - } - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (node.getId()) { - case 8783: - player.teleport(new Location(2044, 4649, 0)); - break; - case 8785: - player.teleport(new Location(2543, 3327, 0)); - break; - case 23158: - case 23157: - player.teleport(new Location(2729, 3733, 0)); - break; - case 15767: - player.teleport(new Location(3748, 9373, 0)); - break; - case 15811: - case 15812: - 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; - } - - @Override - public Location getDestination(Node node, Node n) { - if (n.getId() == 23158 || n.getId() == 23157) { - return new Location(2690, 10124, 0); - } - if (n.getId() == 96) { - return new Location(2641, 9763, 0); - } - return null; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/Tasks.java b/Server/src/main/java/core/game/node/entity/skill/slayer/Tasks.java deleted file mode 100644 index 37c3416cc..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/Tasks.java +++ /dev/null @@ -1,147 +0,0 @@ -package core.game.node.entity.skill.slayer; - - -import core.cache.def.impl.NPCDefinition; - -import java.util.Arrays; -import java.util.HashMap; - -/** - * A non-garbage way of representing tasks - * @author ceik - */ -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 }, 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), - 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, 6208, 6206, 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 }, new String[] { "A kurask is a very quick creature." }, 70, false), - LESSER_DEMONS(60, new int[] { 6203, 82, 3064, 4694, 4695, 6208, 6204, 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[] { 114, 115, 374, 3587, 6267 }, 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); - - static final HashMap taskMap = new HashMap<>(); - static{ - Arrays.stream(Tasks.values()).forEach(entry -> Arrays.stream(entry.ids).forEach(id -> taskMap.putIfAbsent(id,entry))); - } - public int levelReq, combatCheck; - public String[] info; - public int[] ids; - public boolean undead; - public int amtHash; - Tasks(int combatCheck, int[] ids, String[] info, int levelReq, boolean undead){ - this.levelReq = levelReq; - this.ids = ids; - this.info = info; - this.undead = undead; - this.combatCheck = combatCheck; - } - - Tasks(int combatCheck, int[] ids, String[] info, int levelReq, boolean undead, int amtHash){ - this.levelReq = levelReq; - this.ids = ids; - this.info = info; - this.undead = undead; - this.amtHash = amtHash; - this.combatCheck = combatCheck; - } - - public int[] getNpcs(){ - return ids; - } - - public String[] getTip(){ - return info; - } - - public static Tasks forId(int id){ - return taskMap.get(id); - } - - public String getName(){ - return NPCDefinition.forId(ids[0]).getName(); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/TurothNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/TurothNPC.java deleted file mode 100644 index 7299fae07..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/TurothNPC.java +++ /dev/null @@ -1,61 +0,0 @@ -package core.game.node.entity.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.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.world.map.Location; -import core.plugin.Initializable; -import org.rs09.consts.Items; -import rs09.game.node.entity.skill.slayer.SlayerUtils; - -/** - * 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 = new SlayerUtils(player).hasBroadWeaponEquipped(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/java/core/game/node/entity/skill/slayer/WaterFiendNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/WaterFiendNPC.java deleted file mode 100644 index 7a91eb928..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/WaterFiendNPC.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.node.entity.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 rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.node.entity.combat.handlers.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/java/core/game/node/entity/skill/slayer/unused-tasks b/Server/src/main/java/core/game/node/entity/skill/slayer/unused-tasks deleted file mode 100644 index 03501f20e..000000000 --- a/Server/src/main/java/core/game/node/entity/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/java/core/game/node/entity/skill/smithing/SmithingBuilder.java b/Server/src/main/java/core/game/node/entity/skill/smithing/SmithingBuilder.java deleted file mode 100644 index d10c08188..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/smithing/SmithingBuilder.java +++ /dev/null @@ -1,107 +0,0 @@ -package core.game.node.entity.skill.smithing; - -import core.game.component.Component; -import core.game.container.access.InterfaceContainer; -import core.game.content.quest.tutorials.tutorialisland.TutorialSession; -import core.game.content.quest.tutorials.tutorialisland.TutorialStage; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.smithing.smelting.Bar; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.tools.StringUtils; - -/** - * Represents the builder class to view the correct information, on the - * interface of smithing, ie, the items ammount, and name. - * @author 'Vexia - */ -public final class SmithingBuilder { - - /** - * The bar type. - */ - private BarType type; - - /** - * The bar used. - */ - private Bar bar; - - /** - * Constructs a new {@code SmithingBuilder.java} {@code Object}. - * @param item the item. - */ - public SmithingBuilder(Item item) { - this.bar = Bar.forId(item.getId()); - this.type = BarType.getBarTypeForId(item.getId()); - } - - /** - * Builds the interface of the smithing. - * @param player the player. - */ - public void build(Player player) { - player.getGameAttributes().removeAttribute("smith-type"); - player.getGameAttributes().setAttribute("smith-type", type); - 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) { - player.getPacketDispatch().sendInterfaceConfig(300, 169, false); - } - 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) { - player.getPacketDispatch().sendInterfaceConfig(300, 89, 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) { - player.getPacketDispatch().sendInterfaceConfig(300, 161, false); - } - if (bars[i].getSmithingType() == SmithingType.TYPE_STUDS) { - player.getPacketDispatch().sendInterfaceConfig(300, 169, false); - } - String color = ""; - if (player.getSkills().getLevel(Skills.SMITHING) < bars[i].getLevel()) { - } else { - color = ""; - } - player.getPacketDispatch().sendString(color + StringUtils.formatDisplayName(bars[i].getSmithingType().name().replace("TYPE_", "")), 300, bars[i].getSmithingType().getName()); - if (player.getInventory().contains(bars[i].getBarType().getBarType(), bars[i].getSmithingType().getRequired())) { - color = ""; - } else { - color = null; - } - if (color != null) { - String amt = bars[i].getSmithingType().getRequired() > 1 ? "s" : ""; - player.getPacketDispatch().sendString(color + String.valueOf(bars[i].getSmithingType().getRequired()) + " Bar" + amt, 300, bars[i].getSmithingType().getName() + 1); - } - 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.getInterfaceManager().open(new Component(300)); - if (TutorialSession.getExtension(player).getStage() == 41) { - TutorialStage.load(player, 42, false); - } - } - - /** - * Gets the type. - * @return the type. - */ - public BarType getType() { - return type; - } - - /** - * Gets the bar. - * @return the bar. - */ - public Bar getBar() { - return bar; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningPouch.java b/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningPouch.java deleted file mode 100644 index 5c10a3b89..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningPouch.java +++ /dev/null @@ -1,567 +0,0 @@ -package core.game.node.entity.skill.summoning; - -import core.game.node.item.Item; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents a summoning pouch. - * @author Emperor - */ -public enum SummoningPouch { - - /** - * Represents a spirit wolf pouch. - */ - SPIRIT_WOLF_POUCH(0, 12047, 1, 4.8, 6829, 0.1, 1, new Item(12158), new Item(12155), new Item(2859), new Item(12183, 7)), - - /** - * Represents a dreadfowl pouch. - */ - DREADFOWL_POUCH(1, 12043, 4, 9.3, 6825, 0.1, 1, new Item(12158), new Item(12155), new Item(2138), new Item(12183, 8)), - - /** - * Represents a spirit spider pouch. - */ - SPIRIT_SPIDER_POUCH(2, 12059, 10, 12.6, 6841, 0.2, 2, new Item(12158), new Item(12155), new Item(6291), new Item(12183, 8)), - - /** - * Represents a thorny snail pouch. - */ - THORNY_SNAIL_POUCH(3, 12019, 13, 12.6, 6806, 0.2, 2, new Item(12158), new Item(12155), new Item(3363), new Item(12183, 9)), - - /** - * Represents a granite crab pouch. - */ - GRANITE_CRAB_POUCH(4, 12009, 16, 31.6, 6796, 0.2, 2, new Item(12158), new Item(12155), new Item(440), new Item(12183, 7)), - - /** - * Represents a spirit mosquito pouch. - */ - SPIRIT_MOSQUITO_POUCH(5, 12778, 17, 46.5, 7331, 0.5, 2, new Item(12158), new Item(12155), new Item(6319), new Item(12183, 1)), - - /** - * Represents a desrrt wyrm pouch. - */ - DESERT_WYRM_POUCH(6, 12049, 18, 31.2, 6831, 0.4, 1, new Item(12159), new Item(12155), new Item(1783), new Item(12183, 45)), - - /** - * Represents a spirit scorpion pouch. - */ - SPIRIT_SCORPION_POUCH(7, 12055, 19, 83.2, 6837, 0.9, 2, new Item(12160), new Item(12155), new Item(3095), new Item(12183, 57)), - - /** - * Represents a spirit tz-kih pouch. - */ - SPIRIT_TZ_KIH_POUCH(8, 12808, 22, 96.8, 7361, 1.1, 3, new Item(12160), new Item(12168), new Item(12155), new Item(12183, 64)), - - /** - * Represents an albino rat pouch. - */ - ALBINO_RAT_POUCH(9, 12067, 23, 202.4, 6847, 2.3, 1, new Item(12163), new Item(12155), new Item(2134), new Item(12183, 75)), - - /** - * Represents a spirit kalphite pouch. - */ - SPIRIT_KALPHITE_POUCH(10, 12063, 25, 220, 6994, 2.5, 3, new Item(12163), new Item(12155), new Item(3138), new Item(12183, 51)), - - /** - * Represents a compost mound pouch. - */ - COMPOST_MOUND_POUCH(11, 12091, 28, 49.8, 6871, 0.6, 6, new Item(12159), new Item(12155), new Item(6032), new Item(12183, 47)), - - /** - * Represents a giant chinchompa pouch. - */ - GIANT_CHINCHOMPA_POUCH(12, 12800, 29, 255.2, 7353, 2.9, 1, new Item(12163), new Item(12155), new Item(10033), new Item(12183, 84)), - - /** - * Represents a vampire bat pouch. - */ - VAMPIRE_BAT_POUCH(13, 12053, 31, 136, 6835, 1.5, 4, new Item(12160), new Item(12155), new Item(3325), new Item(12183, 81)), - - /** - * Represents a honey badger pouch. - */ - HONEY_BADGER_POUCH(14, 12065, 32, 140.8, 6845, 1.6, 4, new Item(12160), new Item(12155), new Item(12156), new Item(12183, 84)), - - /** - * Represents a beaver pouch. - */ - BEAVER_POUCH(15, 12021, 33, 57.6, 6808, 0.7, 4, new Item(12159), new Item(12155), new Item(1519), new Item(12183, 72)), - - /** - * Represents a void ravager pouch. - */ - VOID_RAVAGER_POUCH(16, 12818, 34, 59.6, 7370, 0.7, 4, new Item(12159), new Item(12164), new Item(12155), new Item(12183, 74)), - - /** - * Represents a void spinner pouch. - */ - VOID_SPINNER_POUCH(17, 12780, 34, 59.6, 7333, 0.7, 4, new Item(12163), new Item(12166), new Item(12155), new Item(12183, 74)), - - /** - * Represents a void torcher pouch. - */ - VOID_TORCHER_POUCH(18, 12798, 34, 59.6, 7351, 0.7, 4, new Item(12163), new Item(12167), new Item(12155), new Item(12183, 74)), - - /** - * Represents a void shifter pouch. - */ - VOID_SHIFTER_POUCH(19, 12814, 34, 59.6, 7367, 0.7, 4, new Item(12163), new Item(12165), new Item(12155), new Item(12183, 74)), - - /** - * Represents a bronze minotaur pouch. - */ - BRONZE_MINOTAUR_POUCH(64, 12073, 36, 316.8, 6853, 3.6, 3, new Item(12163), new Item(12155), new Item(2349), new Item(12183, 102)), - - /** - * Represents an iron minotaur pouch. - */ - IRON_MINOTAUR_POUCH(65, 12075, 46, 404.8, 6855, 4.6, 9, new Item(12163), new Item(12155), new Item(2351), new Item(12183, 125)), - - /** - * Represents a steel minotaur pouch. - */ - STEEL_MINOTAUR_POUCH(66, 12077, 56, 492.8, 6857, 5.6, 9, new Item(12163), new Item(12155), new Item(2353), new Item(12183, 141)), - - /** - * Represents a mithril minotaur pouch. - */ - MITHRIL_MINOTAUR_POUCH(67, 12079, 66, 580.8, 6859, 6.6, 9, new Item(12163), new Item(12155), new Item(2359), new Item(12183, 152)), - - /** - * Represents an adamant minotaur pouch. - */ - ADAMANT_MINOTAUR_POUCH(68, 12081, 76, 668.8, 6861, 7.6, 9, new Item(12163), new Item(12155), new Item(2361), new Item(12183, 144)), - - /** - * Represents a rune minotaur pouch. - */ - RUNE_MINOTAUR_POUCH(69, 12083, 86, 756.8, 6863, 8.6, 9, new Item(12163), new Item(12155), new Item(2363), new Item(12183, 1)), - - /** - * Represents a bull ant pouch. - */ - BULL_ANT_POUCH(20, 12087, 40, 52.8, 6867, 0.6, 5, new Item(12158), new Item(12155), new Item(6010), new Item(12183, 11)), - - /** - * Represents a macaw pouch. - */ - MACAW_POUCH(21, 12071, 41, 72.4, 6851, 0.8, 5, new Item(12159), new Item(12155), new Item(249), new Item(12183, 78)), - - /** - * Represents an evil turnip pouch. - */ - EVIL_TURNIP_POUCH(22, 12051, 42, 184.8, 6833, 2.1, 5, new Item(12160), new Item(12155), new Item(12153), new Item(12183, 104)), - - /** - * Represents a spirit cockatrice pouch. - */ - SPIRIT_COCKATRICE_POUCH(23, 12095, 43, 75.2, 6875, 0.9, 5, new Item(12159), new Item(12155), new Item(12109), new Item(12183, 88)), - - /** - * Represents a spirit guthatrice pouch. - */ - SPIRIT_GUTHATRICE_POUCH(24, 12097, 43, 75.2, 6877, 0.9, 5, new Item(12159), new Item(12155), new Item(12111), new Item(12183, 88)), - - /** - * Represents a spirit saratrice pouch. - */ - SPIRIT_SARATRICE_POUCH(25, 12099, 43, 75.2, 6879, 0.9, 5, new Item(12159), new Item(12155), new Item(12113), new Item(12183, 88)), - - /** - * Represents a spirit zamatrice pouch. - */ - SPIRIT_ZAMATRICE_POUCH(26, 12101, 43, 75.2, 6881, 0.9, 5, new Item(12159), new Item(12155), new Item(12115), new Item(12183, 88)), - - /** - * Represents a spirit pengatrice pouch. - */ - SPIRIT_PENGATRICE_POUCH(27, 12103, 43, 75.2, 6883, 0.9, 5, new Item(12159), new Item(12155), new Item(12117), new Item(12183, 88)), - - /** - * Represents a coraxatrice pouch. - */ - SPIRIT_CORAXATRICE_POUCH(28, 12105, 43, 75.2, 6885, 0.9, 5, new Item(12159), new Item(12155), new Item(12119), new Item(12183, 88)), - - /** - * Represents a vulatrice pouch. - */ - SPIRIT_VULATRICE(29, 12107, 43, 75.2, 6887, 0.9, 5, new Item(12159), new Item(12155), new Item(12121), new Item(12183, 88)), - - /** - * Represents a pyrelord pouch. - */ - PYRELORD_POUCH(30, 12816, 46, 202.4, 7377, 2.3, 5, new Item(12160), new Item(12155), new Item(590), new Item(12183, 111)), - - /** - * Represents a magpie pouch. - */ - MAGPIE_POUCH(31, 12041, 47, 83.2, 6824, 0.9, 5, new Item(12159), new Item(12155), new Item(1635), new Item(12183, 88)), - - /** - * Represents a bloated leech pouch. - */ - BLOATED_LEECH_POUCH(32, 12061, 49, 215.2, 6843, 2.4, 5, new Item(12160), new Item(12155), new Item(2132), new Item(12183, 117)), - - /** - * Represents a spirit terrorbird pouch. - */ - SPIRIT_TERRORBIRD_POUCH(33, 12007, 52, 68.4, 6794, 0.8, 6, new Item(12158), new Item(12155), new Item(9978), new Item(12183, 12)), - - /** - * Represents an abyssal parasite pouch. - */ - ABYSSAL_PARASITE_POUCH(34, true,12035, 54, 94.8, 6818, 1.1, 6, new Item(12159), new Item(12155), new Item(12161), new Item(12183, 106)), - - /** - * Represents a spirit jelly pouch. - */ - SPIRIT_JELLY_POUCH(35, 12027, 55, 484, 6992, 5.5, 6, new Item(12163), new Item(12155), new Item(1937), new Item(12183, 151)), - - /** - * Represents an ibis pouch. - */ - IBIS_POUCH(36, 12531, 56, 98.8, 6991, 1.1, 6, new Item(12159), new Item(12155), new Item(311), new Item(12183, 109)), - - /** - * Represents a spirit kyatt pouch. - */ - SPIRIT_KYATT_POUCH(37, 12812, 57, 501.6, 7365, 5.7, 6, new Item(12163), new Item(12155), new Item(10103), new Item(12183, 153)), - - /** - * Represents a spirit larupia pouch. - */ - SPIRIT_LARUPIA_POUCH(38, 12784, 57, 501.6, 7337, 5.7, 6, new Item(12163), new Item(12155), new Item(10095), new Item(12183, 155)), - - /** - * Represents a spirit graahk pouch. - */ - SPIRIT_GRAAHK_POUCH(39, 12810, 57, 501.6, 7363, 5.7, 6, new Item(12163), new Item(12155), new Item(10099), new Item(12183, 154)), - - /** - * Represents a karamthulhu overlord pouch. - */ - KARAMTHULHU_POUCH(40, 12023, 58, 510.4, 6809, 5.8, 6, new Item(12163), new Item(12155), new Item(6667), new Item(12183, 144)), - - /** - * Represents a smoke devil pouch. - */ - SMOKE_DEVIL_POUCH(41, 12085, 61, 268, 6865, 3, 7, new Item(12160), new Item(12155), new Item(9736), new Item(12183, 141)), - - /** - * Represents an abyssal lurker pouch. - */ - ABYSSAL_LUKRER(42, true,12037, 62, 109.6, 6820, 1.9, 9, new Item(12159), new Item(12155), new Item(12161), new Item(12183, 119)), - - /** - * Represents a spirit cobra pouch. - */ - SPIRIT_COBRA_POUCH(43, 12015, 63, 276.8, 6802, 3.1, 6, new Item(12160), new Item(12155), new Item(6287), new Item(12183, 116)), - - /** - * Represents a stranger plant pouch. - */ - STRANGER_PLANT_POUCH(44, 12045, 64, 281.6, 6827, 3.2, 6, new Item(12160), new Item(12155), new Item(8431), new Item(12183, 128)), - - /** - * Represents a barker toad pouch. - */ - BARKER_TOAD_POUCH(45, 12123, 66, 87, 6889, 1, 7, new Item(12158), new Item(12155), new Item(2150), new Item(12183, 11)), - - /** - * Represents a war tortoise pouch. - */ - WAR_TORTOISE_POUCH(46, 12031, 67, 58.6, 6815, 0.7, 7, new Item(12158), new Item(12155), new Item(7939), new Item(12183, 1)), - - /** - * Represents a bunyip pouch. - */ - BUNYIP_POUCH(47, 12029, 68, 119.2, 6813, 1.4, 7, new Item(12159), new Item(12155), new Item(383), new Item(12183, 110)), - - /** - * Represents a fruit bat pouch. - */ - FRUIT_BAT_POUCH(48, 12033, 69, 121.2, 6817, 1.4, 8, new Item(12159), new Item(12155), new Item(1963), new Item(12183, 130)), - - /** - * Represents a ravenous locust pouch. - */ - RAVENOUS_LOCUST_POUCH(49, 12820, 70, 132, 7372, 1.5, 4, new Item(12160), new Item(12155), new Item(1933), new Item(12183, 79)), - - /** - * Represents an arctic bear pouch. - */ - ARCTIC_BEAR_POUCH(50, 12057, 71, 93.2, 6839, 1.1, 8, new Item(12158), new Item(12155), new Item(10117), new Item(12183, 14)), - - /** - * Represents a Phoenix - */ - PHOENIX_POUCH(50, 14623, 72, 93.2, 8575, 1.1, 8, new Item(12160, 1), new Item(12183, 165), new Item(12155, 1), new Item(14616, 1)), - - /** - * Represents an obsidian golem pouch. - */ - OBSIDIAN_GOLEM_POUCH(51, 12792, 73, 642.4, 7345, 7.3, 8, new Item(12163), new Item(12155), new Item(12168), new Item(12183, 195)), - - /** - * Represents a granite lobster pouch. - */ - GRANITE_LOBSTER_POUCH(52, 12069, 74, 325.6, 6849, 3.7, 8, new Item(12160), new Item(12155), new Item(6979), new Item(12183, 166)), - - /** - * Represents a praying mantis pouch. - */ - PRAYING_MANTIS_POUCH(53, 12011, 75, 329.6, 6798, 3.6, 8, new Item(12160), new Item(12155), new Item(2460), new Item(12183, 168)), - - /** - * Represents a forge regent pouch. - */ - FORGE_REGENT_BEAST(54, 12782, 76, 134, 7335, 1.5, 9, new Item(12159), new Item(12155), new Item(10020), new Item(12183, 141)), - - /** - * Represents a talon beast pouch. - */ - TALON_BEAST_POUCH(55, 12794, 77, 1015.2, 7347, 3.8, 9, new Item(12160), new Item(12155), new Item(12162), new Item(12183, 174)), - - /** - * Represents a giant ent pouch. - */ - GIANT_ENT_POUCH(56, 12013, 78, 136.8, 6800, 1.6, 8, new Item(12159), new Item(5933), new Item(12155), new Item(12183, 124)), - - /** - * Represents a hydra pouch. - */ - HYDRA_POUCH(60, 12025, 80, 140.8, 6811, 1.6, 9, new Item(12159), new Item(571), new Item(12183, 128)), - - /** - * Represents a spirit dagannoth pouch. - */ - SPIRIT_DAGANNOTH_POUCH(61, 12017, 83, 364.8, 6804, 4.1, 9, new Item(12160), new Item(6155), new Item(12183, 1)), - - /** - * Represents a unicorn stallion pouch. - */ - UNICORN_STALLION_POUCH(70, 12039, 88, 154.4, 6822, 1.8, 9, new Item(12159), new Item(237), new Item(12183, 203)), - - /** - * Represents a wolpertinger pouch. - */ - WOLPERTINGER_POUCH(72, 12089, 92, 404.8, 6869, 4.5, 10, new Item(12160), new Item(2859), new Item(3226), new Item(12183, 203)), - - /** - * Represents a pack yak pouch. - */ - PACK_YAK_POUCH(75, 12093, 96, 422.4, 6873, 4.8, 10, new Item(12160), new Item(10818), new Item(12183, 211)), - - /** - * Represents a fire titan pouch. - */ - FIRE_TITAN_POUCH(57, 12802, 79, 695.2, 7355, 7.9, 9, new Item(12163), new Item(1442), new Item(12155), new Item(12183, 198)), - - /** - * Represents a moss titan pouch. - */ - MOSS_TITAN_POUCH(58, 12804, 79, 695.2, 7357, 7.9, 9, new Item(12163), new Item(1440), new Item(12155), new Item(12183, 198)), - - /** - * Represents an ice titan pouch. - */ - ICE_TITAN_POUCH(59, 12806, 79, 695.2, 7359, 7.9, 9, new Item(12163), new Item(1438), new Item(1444), new Item(12155), new Item(12183, 198)), - - /** - * Represents a lava titan pouch. - */ - LAVA_TITAN_POUCH(62, 12788, 83, 730.4, 7341, 8.3, 9, new Item(12163), new Item(12168), new Item(12155), new Item(12183, 219)), - - /** - * Represents a swamp titan pouch. - */ - SWAMP_TITAN_POUCH(63, 12776, 85, 373.6, 7329, 4.2, 9, new Item(12160), new Item(10149), new Item(12155), new Item(12183, 150)), - - /** - * Represents a geyser titan pouch. - */ - GEYSER_TITAN_POUCH(71, 12786, 89, 783.2, 7339, 8.9, 9, new Item(12163), new Item(1444), new Item(12155), new Item(12183, 222)), - - /** - * Represents an abyssal titan pouch. - */ - ABYSSAL_TITAN_POUCH(73, true,12796, 93, 163.2, 7349, 1.9, 10, new Item(12159), new Item(12161), new Item(12155), new Item(12183, 113)), - - /** - * Represents an iron titan pouch. - */ - IRON_TITAN_POUCH(74, 12822, 95, 417.6, 7375, 4.7, 10, new Item(12160), new Item(1115), new Item(12155), new Item(12183, 198)), - - /** - * Represents a steel titan pouch. - */ - STEEL_TITAN_POUCH(76, 12790, 99, 435.2, 7343, 4.9, 10, new Item(12160), new Item(1119), new Item(12155), new Item(12183, 178)); - - /** - * The mapping. - */ - private static final Map POUCHES = new HashMap(); - - /** - * Populate the mapping. - */ - static { - for (SummoningPouch pouch : SummoningPouch.values()) { - POUCHES.put(pouch.pouchId, pouch); - } - } - - /** - * Gets a summoning pouch object from the mapping. - * @param pouchId The pouch item id. - * @return The {@code SummoningPouch} {@code Object},
or {@code null} - * if the pouch didn't exist. - */ - public static SummoningPouch get(int pouchId) { - return POUCHES.get(pouchId); - } - - /** - * The slot id. - */ - private final int slot; - - /** - * The pouch item id. - */ - private final int pouchId; - - /** - * The level required to create this pouch. - */ - private final int levelRequired; - - /** - * The experience gained when creating this pouch. - */ - private final double createExperience; - - /** - * The familiar npc id. - */ - private final int npcId; - - /** - * The experience gained when summoning the familiar. - */ - private final double summonExperience; - - /** - * The summon cost. - */ - private final int summonCost; - - /** - * The items required to create this pouch. - */ - private final Item[] items; - - public boolean abyssal; - - /** - * Constructs a new {@code SummoningPouch} {@code Object}. - * @param pouchId The pouch item id. - * @param levelRequired The level required to create. - * @param createExperience The experience gained when creating a pouch. - * @param npcId The familiar's NPC id. - * @param summonExperience The experience gained when summoning. - * @param summonCost The amount of summoning points to drain when summoned. - * @param items The items required to create this pouch. - */ - private SummoningPouch(int slot, int pouchId, int levelRequired, double createExperience, int npcId, double summonExperience, int summonCost, Item... items) { - this.slot = slot; - this.pouchId = pouchId; - this.levelRequired = levelRequired; - this.createExperience = createExperience; - this.npcId = npcId; - this.summonExperience = summonExperience; - this.summonCost = summonCost; - this.items = items; - } - - private SummoningPouch(int slot, boolean abyssal, int pouchId, int levelRequired, double createExperience, int npcId, double summonExperience, int summonCost, Item... items) { - this(slot, pouchId, levelRequired, createExperience, npcId, summonExperience, summonCost, items); - this.abyssal = abyssal; - } - /** - * Gets the summoning pouch for the given slot. - * @param slot The slot. - * @return The summoning pouch. - */ - public static SummoningPouch forSlot(int slot) { - for (SummoningPouch pouch : SummoningPouch.values()) { - if (pouch.getSlot() == slot) { - return pouch; - } - } - return null; - } - - /** - * @return the pouchId - */ - public int getPouchId() { - return pouchId; - } - - /** - * @return the levelRequired - */ - public int getLevelRequired() { - return levelRequired; - } - - /** - * @return the createExperience - */ - public double getCreateExperience() { - return createExperience; - } - - /** - * @return the npcId - */ - public int getNpcId() { - return npcId; - } - - /** - * @return the summonExperience - */ - public double getSummonExperience() { - return summonExperience; - } - - /** - * Gets the amount of points to decrease the summoning points with when - * summoned. - * @return The amount of points. - */ - public int getSummonCost() { - return summonCost; - } - - /** - * @return the items - */ - public Item[] getItems() { - return items; - } - - /** - * @return the slot - */ - public int getSlot() { - return slot; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningScroll.java b/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningScroll.java deleted file mode 100644 index a1b7d5520..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningScroll.java +++ /dev/null @@ -1,134 +0,0 @@ -package core.game.node.entity.skill.summoning; - -/** - * Represents a summoning scroll. - * @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), - // 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); - - /** - * The level required. - */ - private int level; - - /** - * The item id needed. - */ - private int itemId; - - /** - * The slot id. - */ - private int slotId; - - /** - * The xp gained. - */ - private double xp; - - /** - * The items required. - */ - private int[] items; - - /** - * Constructs a new {@code SummoningScroll} {@code Object}. - * @param level - * @param itemId - * @param xp - * @param slotId - * @param items - */ - SummoningScroll(int slotId, int itemId, double xp, int level, int... items) { - this.level = level; - this.itemId = itemId; - this.xp = xp; - this.slotId = slotId; - this.items = items; - } - - public int getPouch(){ - return items[0]; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the itemId. - * @return The itemId. - */ - public int getItemId() { - return itemId; - } - - /** - * Gets the slotId. - * @return The slotId. - */ - public int getSlotId() { - return slotId; - } - - /** - * Gets the xp. - * @return The xp. - */ - public double getExperience() { - return xp; - } - - /** - * Gets the items. - * @return The items. - */ - public int[] getItems() { - return items; - } - - /** - * Gets the summoning value for the id. - * @param id - * @return - */ - public static SummoningScroll forId(int id) { - for (SummoningScroll scroll : SummoningScroll.values()) { - if (scroll.slotId == id) { - return scroll; - } - } - return null; - } - - public static SummoningScroll forItemId(int id) { - for (SummoningScroll scroll : SummoningScroll.values()) { - if (scroll.itemId == id) { - return scroll; - } - } - return null; - } - - /** - * Gets the scroll for the given pouch item id. - * @param pouchId The pouch item id. - * @return The scroll. - */ - public static SummoningScroll forPouch(int pouchId) { - for (SummoningScroll scroll : SummoningScroll.values()) { - if (scroll.items[0] == pouchId) { - return scroll; - } - } - return null; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningTabPlugin.java b/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningTabPlugin.java deleted file mode 100644 index 22792835c..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/SummoningTabPlugin.java +++ /dev/null @@ -1,77 +0,0 @@ -package core.game.node.entity.skill.summoning; - -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.node.entity.skill.summoning.familiar.BurdenBeast; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the component plugin handler for the summoning tab. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SummoningTabPlugin extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(662, this); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - switch (button) { - case 51: - if (player.getFamiliarManager().hasFamiliar()) { - player.getFamiliarManager().getFamiliar().call(); - } else { - player.getPacketDispatch().sendMessage("You don't have a follower."); - } - break; - case 67: - if (player.getFamiliarManager().hasFamiliar()) { - if (!player.getFamiliarManager().getFamiliar().isBurdenBeast()) { - player.getPacketDispatch().sendMessage("Your familiar is not a beast of burden."); - break; - } - BurdenBeast beast = (BurdenBeast) player.getFamiliarManager().getFamiliar(); - if (beast.getContainer().isEmpty()) { - player.getPacketDispatch().sendMessage("Your familiar is not carrying any items."); - break; - } - beast.withdrawAll(); - break; - } - player.getPacketDispatch().sendMessage("You don't have a follower."); - break; - case 53: - if (player.getFamiliarManager().hasFamiliar()) { - player.getDialogueInterpreter().open("dismiss_dial"); - } else { - player.getPacketDispatch().sendMessage("You don't have a follower."); - } - break; - case 113: - case 101: - case 117: - case 163: - case 151: - case 83: - case 75: - default: - if (player.getFamiliarManager().hasFamiliar()) { - player.getFamiliarManager().getFamiliar().executeSpecialMove(new FamiliarSpecial(player)); - } else { - player.getPacketDispatch().sendMessage("You don't have a follower."); - } - break; - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/Familiar.java b/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/Familiar.java deleted file mode 100644 index 56b4998c2..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/Familiar.java +++ /dev/null @@ -1,746 +0,0 @@ -package core.game.node.entity.skill.summoning.familiar; - -import core.cache.def.impl.NPCDefinition; -import core.game.interaction.MovementPulse; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.equipment.WeaponInterface; -import core.game.node.entity.impl.Projectile; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.skill.SkillBonus; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.SummoningScroll; -import core.game.node.entity.skill.summoning.pet.Pet; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.game.world.map.path.Pathfinder; -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.RandomFunction; -import rs09.game.node.entity.combat.CombatPulse; -import rs09.game.node.entity.combat.CombatSwingHandler; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents a familiar. - * @author Emperor - */ -public abstract class Familiar extends NPC implements Plugin { - - /** - * The summon graphics for a small familiar. - */ - protected static final Graphics SMALL_SUMMON_GRAPHIC = Graphics.create(1314); - - /** - * The spawn graphics for a large familiar. - */ - protected static final Graphics LARGE_SUMMON_GRAPHIC = Graphics.create(1315); - - /** - * The special animation. - */ - protected static final Animation SPECIAL_ANIMATION = Animation.create(7660); - - /** - * The special graphic. - */ - protected static final Graphics SPECIAL_GRAPHIC = Graphics.create(1316); - - /** - * The owner. - */ - protected Player owner; - - /** - * The amount of ticks left. - */ - protected int ticks; - - /** - * The initial amount of ticks. - */ - protected int maximumTicks; - - /** - * The amount of special points left. - */ - protected int specialPoints = 60; - - /** - * The pouch id. - */ - private final int pouchId; - - /** - * The special move cost. - */ - private final int specialCost; - - /** - * The combat reward. - */ - private CombatSwingHandler combatHandler; - - /** - * If the familiar is a combat familiar. - */ - protected boolean combatFamiliar; - - /** - * If the familiars special is charged. - */ - protected boolean charged; - - /** - * The invisible familiar boosts. - */ - protected List boosts = new ArrayList<>(20); - - /** - * The attack style. - */ - private final int attackStyle; - - private boolean firstCall = true; - - /** - * Constructs a new {@code Familiar} {@code Object}. - * @param owner The owner. - * @param id The NPC id. - * @param ticks The ticks left. - * @param pouchId The pouch. - * @param specialCost The special move cost. - * @param attackStyle the style. - */ - public Familiar(Player owner, int id, int ticks, int pouchId, int specialCost, final int attackStyle) { - super(id, null); - this.owner = owner; - this.maximumTicks = ticks; - this.ticks = ticks; - this.pouchId = pouchId; - this.specialCost = specialCost; - this.combatFamiliar = NPCDefinition.forId(getOriginalId() + 1).getName().equals(getName()); - this.attackStyle = attackStyle; - } - - /** - * Constructs a new {@code Familiar} {@code Object}. - * @param owner The owner. - * @param id The NPC id. - * @param ticks The ticks left. - * @param pouchId The pouch. - * @param specialCost The special move cost. - */ - public Familiar(Player owner, int id, int ticks, int pouchId, int specialCost) { - this(owner, id, ticks, pouchId, specialCost, WeaponInterface.STYLE_DEFENSIVE); - } - - /** - * Creates the familiar. - * @param loc The location. - */ - public void init(Location loc, boolean call) { - location = loc; - if (location == null) { - location = owner.getLocation(); - setInvisible(true); - } - super.init(); - startFollowing(); - sendConfiguration(); - if (call) { - call(); - } - owner.getInterfaceManager().openInfoBars(); - if (getZoneMonitor().isInZone("Wilderness")) { - transform(); - } - } - - @Override - public void init() { - init(getSpawnLocation(), true); - } - - @Override - public void handleTickActions() { - if (ticks-- % 50 == 0) { - updateSpecialPoints(-15); - owner.getSkills().updateLevel(Skills.SUMMONING, -1, 0); - if (!getText().isEmpty()) { - super.sendChat(getText()); - } - } - sendTimeRemaining(); - switch (ticks) { - case 100: - owner.getPacketDispatch().sendMessage("You have 1 minute before your familiar vanishes."); - break; - case 50: - owner.getPacketDispatch().sendMessage("You have 30 seconds before your familiar vanishes."); - break; - case 0: - if (isBurdenBeast() && !((BurdenBeast) this).getContainer().isEmpty()) { - owner.getPacketDispatch().sendMessage("Your familiar has dropped all the items it was holding."); - } else { - owner.getPacketDispatch().sendMessage("Your familiar has vanished."); - } - dismiss(); - return; - } - CombatPulse combat = owner.getProperties().getCombatPulse(); - if (!isInvisible() && !getProperties().getCombatPulse().isAttacking() && (combat.isAttacking() || owner.inCombat())) { - Entity victim = combat.getVictim(); - if (victim == null) { - victim = owner.getAttribute("combat-attacker"); - } - if (combat.getVictim() != this && victim != null && !victim.isInvisible() && getProperties().isMultiZone() && owner.getProperties().isMultiZone() && isCombatFamiliar() && !isBurdenBeast()) { - getProperties().getCombatPulse().attack(victim); - } - } - if ((!isInvisible() && owner.getLocation().getDistance(getLocation()) > 12) || (isInvisible() && ticks % 25 == 0)) { - if (!call()) { - setInvisible(true); - } - } else if (!getPulseManager().hasPulseRunning()) { - startFollowing(); - } - handleFamiliarTick(); - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style) { - if (entity == owner) { - owner.getPacketDispatch().sendMessage("You can't just betray your own familiar like that!"); - return false; - } - if (entity instanceof Player) { - if (!owner.isAttackable(entity, style)) { - return false; - } - } - if (!getProperties().isMultiZone()) { - if (entity instanceof Player && !((Player) entity).getProperties().isMultiZone()) { - ((Player) entity).getPacketDispatch().sendMessage("You have to be in multicombat to attack a player's familiar."); - return false; - } - if (entity instanceof Player) { - ((Player) entity).getPacketDispatch().sendMessage("This familiar is not in the a multicombat zone."); - } - return false; - } - if (entity instanceof Player) { - if (!((Player) entity).getSkullManager().isWilderness()) { - ((Player) entity).getPacketDispatch().sendMessage("You have to be in the wilderness to attack a player's familiar."); - return false; - } - if (!owner.getSkullManager().isWilderness()) { - ((Player) entity).getPacketDispatch().sendMessage("This familiar's owner is not in the wilderness."); - return false; - } - } - return super.isAttackable(entity, style); - } - - @Override - public void onRegionInactivity() { - call(); - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - if (combatHandler != null) { - return combatHandler; - } - return super.getSwingHandler(swing); - } - - /** - * Constructs a new {@code Familiar} {@code Object}. - * @param owner The owner. - * @param id The NPC id. - * @return The familiar. - */ - public abstract Familiar construct(Player owner, int id); - - /** - * Executes the special move. - * @param special The familiar special object. - * @return {@code True} if the move was executed. - */ - protected abstract boolean specialMove(FamiliarSpecial special); - - /** - * Handles the familiar special tick. - */ - protected void handleFamiliarTick() { - } - - /** - * Configures use with events, and other plugin related content.. - */ - protected void configureFamiliar() { - - } - - /** - * Gets the forced chat text for this familiar. - * @return The forced chat text. - */ - protected String getText() { - return ""; - } - - /** - * Transforms the familiar into the Wilderness combat form. - */ - public void transform() { - if (isCombatFamiliar()) { - transform(getOriginalId() + 1); - } - } - - /** - * Sends the time remaining. - */ - private void sendTimeRemaining() { - int minutes = (int) Math.ceil(ticks * 0.01); - int hash = minutes << 7 | ((ticks - (minutes * 100)) > 49 ? 1 : 0) << 6; - owner.getConfigManager().set(1176, hash); - } - - /** - * 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) { - if (special.getNode() == this) { - return; - } - if (specialCost > specialPoints) { - owner.getPacketDispatch().sendMessage("Your familiar does not have enough special move points left."); - return; - } - SummoningScroll scroll = SummoningScroll.forPouch(pouchId); - if (scroll == null) { - owner.getPacketDispatch().sendMessage("Invalid scroll for pouch " + pouchId + " - report!"); - return; - } - if (!owner.getInventory().contains(scroll.getItemId(), 1)) { - owner.getPacketDispatch().sendMessage("You do not have enough scrolls left to do this special move."); - return; - } - 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; - } - if (specialMove(special)) { - setAttribute("special-delay", GameWorld.getTicks() + 3); - owner.getInventory().remove(new Item(scroll.getItemId())); - owner.getAudioManager().send(4161); - visualizeSpecialMove(); - updateSpecialPoints(specialCost); - owner.getSkills().addExperience(Skills.SUMMONING, scroll.getExperience(), true); - } - } - - /** - * Sends the special move visualization for the owner. - */ - 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. - */ - public void sendFamiliarHit(final Entity target, final int maxHit, final Graphics graphics) { - final int ticks = 2 + (int) Math.floor(getLocation().getDistance(target.getLocation()) * 0.5); - getProperties().getCombatPulse().setNextAttack(4); - GameWorld.getPulser().submit(new Pulse(ticks, this, target) { - @Override - public boolean pulse() { - BattleState state = new BattleState(Familiar.this, target); - int hit = 0; - if (getCombatStyle().getSwingHandler().isAccurateImpact(Familiar.this, target)) { - hit = RandomFunction.randomize(maxHit); - } - state.setEstimatedHit(hit); - target.getImpactHandler().handleImpact(owner, hit, CombatStyle.MELEE, state); - if (graphics != null) { - target.graphics(graphics); - } - return true; - } - }); - } - - /** - * Sends a projectile to the target. - * @param target the target. - * @param projectileId the projectile id. - */ - public void projectile(final Entity target, final int projectileId) { - Projectile.magic(this, target, projectileId, 40, 36, 51, 10).send(); - } - - /** - * Sends a familiar hit. - * @param maxHit the max hit. - */ - 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). - */ - public boolean canAttack(Entity target, boolean message) { - if (!target.isAttackable(owner, owner.getProperties().getCombatPulse().getStyle())) { - return false; - } - if (target.getLocation().getDistance(getLocation()) > 8) { - if (message) { - owner.getPacketDispatch().sendMessage("That target is too far."); - } - return false; - } - if (target.getLocks().isInteractionLocked() || !target.isAttackable(this, CombatStyle.MAGIC)) { - return false; - } - return isCombatFamiliar(); - } - - @Override - 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) { - if (!canAttack(target, message)) { - return false; - } - if (!isOwnerAttackable()) { - return false; - } - if (getAttribute("special-delay", 0) > GameWorld.getTicks()) { - return false; - } - return true; - } - - /** - * Checks if a faimiliar can perform a combat special attack. - * @param target the target. - * @return {@code True} if so. - */ - public boolean canCombatSpecial(Entity target) { - return canCombatSpecial(target, true); - } - - /** - * Checks if the owner is attackable. - * @return {@code True} if so. - */ - public boolean isOwnerAttackable() { - if (!owner.getProperties().getCombatPulse().isAttacking() && !owner.inCombat() && !getProperties().getCombatPulse().isAttacking()) { - owner.getPacketDispatch().sendMessage("Your familiar cannot fight whilst you are not in combat."); - return false; - } - return true; - } - - /** - * Gets the combat style. - * @return the style. - */ - public CombatStyle getCombatStyle() { - return CombatStyle.MAGIC; - } - - /** - * Adjusts a players battle state. - * @param state the state. - */ - public void adjustPlayerBattle(final BattleState state) { - - } - - /** - * Starts following the owner. - */ - public void startFollowing() { - getPulseManager().run(new MovementPulse(this, owner, Pathfinder.DUMB) { - @Override - public boolean pulse() { - return false; - } - }, "movement"); - face(owner); - } - - @Override - public void finalizeDeath(Entity killer) { - dismiss(); - } - - /** - * Checks if the familiar is a combat familiar. - * @return {@code True} if so. - */ - public boolean isCombatFamiliar() { - return combatFamiliar; - } - - /** - * Sends the familiar packets. - */ - public void sendConfiguration() { - owner.getConfigManager().set(448, getPouchId()); - owner.getConfigManager().set(1174, getOriginalId()); - owner.getConfigManager().set(1175, specialCost << 23); - sendTimeRemaining(); - updateSpecialPoints(0); - } - - /** - * Calls the familiar. - */ - //int spamTimer = 0; - public boolean call() { - Location destination = getSpawnLocation(); - if (destination == null) { - //owner.getPacketDispatch().sendMessage("Your familiar is too big to fit here. Try calling it again when you are standing"); - //owner.getPacketDispatch().sendMessage("somewhere with more space."); - //spamTimer = 50; - return false; - } - setInvisible(getZoneMonitor().isRestricted(ZoneRestriction.FOLLOWERS) && !owner.getLocks().isLocked("enable_summoning")); - getProperties().setTeleportLocation(destination); - if (!(this instanceof Pet)) { - if(firstCall) { - owner.getAudioManager().send(4366); - firstCall = false; - } else { - owner.getAudioManager().send(188); - } - if (size() > 1) { - graphics(LARGE_SUMMON_GRAPHIC); - } else { - graphics(SMALL_SUMMON_GRAPHIC); - } - } - if (getProperties().getCombatPulse().isAttacking()) { - startFollowing(); - } else { - face(owner); - } - if (!isRenderable() && owner.isActive()) { - // SystemLogger.logErr("Familiar in inactive region!"); - getWalkingQueue().update(); - getUpdateMasks().prepare(this); - } - return true; - } - - /** - * Gets the spawning location of the familiar. - * @return The spawn location. - */ - public Location getSpawnLocation() { - return RegionManager.getSpawnLocation(owner, this); - } - - /** - * Dismisses the familiar. - */ - public void dismiss() { - clear(); - getPulseManager().clear(); - owner.getInterfaceManager().hideTabs(7); - owner.getFamiliarManager().setFamiliar(null); - owner.getConfigManager().set(448, -1); - owner.getConfigManager().set(1176, 0); - owner.getConfigManager().set(1175, 182986); - owner.getConfigManager().set(1174, -1); - if (owner.getSkullManager().isWilderness()) { - owner.getAppearance().sync(); - } - owner.getInterfaceManager().setViewedTab(3); - } - - /** - * Updates the special move points. - * @param diff The difference to decrease with. - */ - public void updateSpecialPoints(int diff) { - specialPoints -= diff; - if (specialPoints > 60) { - specialPoints = 60; - } - owner.getConfigManager().set(1177, specialPoints); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : getIds()) { - if (FamiliarManager.getFamiliars().containsKey(id)) { - SystemLogger.logErr("Familiar " + id + " was already registered!"); - return null; - } - FamiliarManager.getFamiliars().put(id, this); - configureFamiliar(); - } - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - /** - * Gets the charged. - * @return The charged. - */ - public boolean isCharged() { - if (charged) { - owner.getPacketDispatch().sendMessage("Your familiar is already charging its attack!"); - return true; - } - return false; - } - - /** - * Gets a familiar boost. - * @param skill the skill. - * @return the boost. - */ - public int getBoost(int skill) { - SkillBonus bonus = null; - for (SkillBonus b : boosts) { - if (b.getSkillId() == skill) { - bonus = b; - break; - } - } - if (bonus == null) { - return 0; - } - return (int) bonus.getBonus(); - } - - /** - * Charges a familiar. - */ - public void charge() { - setCharged(true); - } - - /** - * Sets the charged. - * @param charged The charged to set. - */ - public void setCharged(boolean charged) { - this.charged = charged; - } - - /** - * Checks if the familiar is a beast of burden. - * @return {@code True} if so. - */ - public boolean isBurdenBeast() { - return false; - } - - /** - * Gets the NPC ids. - * @return The npc ids. - */ - public abstract int[] getIds(); - - /** - * Gets the pouch id. - * @return The pouch id. - */ - public int getPouchId() { - return pouchId; - } - - /** - * Gets the owner. - * @return The owner. - */ - public Player getOwner() { - return owner; - } - - /** - * Sets the owner. - * @param owner The owner to set. - */ - public void setOwner(Player owner) { - this.owner = owner; - } - - /** - * Gets the combatHandler. - * @return The combatHandler. - */ - public CombatSwingHandler getCombatHandler() { - return combatHandler; - } - - /** - * Sets the combatHandler. - * @param combatHandler The combatHandler to set. - */ - public void setCombatHandler(CombatSwingHandler combatHandler) { - this.combatHandler = combatHandler; - } - - /** - * Gets the view animation for remote viewing. - * @return the animation. - */ - public Animation getViewAnimation() { - return null; - } - - /** - * Gets the exp style. - * @return the style. - */ - public int getAttackStyle() { - return attackStyle; - } - - public int getTicks() { - return ticks; - } - - public int getSpecialPoints() { - return specialPoints; - } -} diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarFeedPlugin.java b/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarFeedPlugin.java deleted file mode 100644 index 5277bcb3b..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarFeedPlugin.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.game.node.entity.skill.summoning.familiar; - -import core.game.node.entity.skill.summoning.pet.Pet; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the class used to handle the feeding of pets. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -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); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - int[] ids = 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, 8214, 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, 11413, 6948, 7294, 7296, 7298, 7300, 6912, 7262, 7264, 7266, 7268, 7270, 6920, 7302, 7304, 7306, 7308, 6950, 6953, 6956, 6914, 7272, 7274, 13090, 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, 8216, 6951, 6954, 6957 }; - for (int i : ids) { - addHandler(i, NPC_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Familiar f = (Familiar) event.getUsedWith(); - if (!(f instanceof Pet)) { - return false; - } - event.getPlayer().getFamiliarManager().eat(event.getUsedItem().getId(), (Pet) f); - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarManager.java b/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarManager.java deleted file mode 100644 index 122f4d212..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarManager.java +++ /dev/null @@ -1,503 +0,0 @@ -package core.game.node.entity.skill.summoning.familiar; - -import core.cache.def.impl.ItemDefinition; -import core.game.component.Component; -import core.game.container.Container; -import rs09.game.system.SystemLogger; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.skill.summoning.SummoningPouch; -import core.game.node.entity.skill.summoning.pet.Pet; -import core.game.node.entity.skill.summoning.pet.PetDetails; -import core.game.node.entity.skill.summoning.pet.Pets; -import core.game.node.entity.combat.BattleState; -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.ZoneRestriction; -import core.game.world.update.flag.context.Animation; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Handles a player's familiar. - * @author Emperor - */ -public final class FamiliarManager { - - /** - * The familiars mapping. - */ - private static final Map FAMILIARS = new HashMap<>(); - - /** - * The pet details mapping, sorted by item id. - */ - private final Map petDetails = new HashMap(); - - /** - * The player. - */ - private final Player player; - - /** - * The familiar. - */ - private Familiar familiar; - - /** - * The combat level difference when using summoning. - */ - private int summoningCombatLevel; - - /** - * If the player has a summoning pouch. - */ - 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. - */ - public FamiliarManager(Player player) { - 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); - } - - 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); - } - familiar = new Pet(player, details, currentPet, pets.getNpcId(details.getStage())); - } 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){ - ((BurdenBeast) familiar).container.parse(famInv); - } - familiar.setAttribute("hp",Integer.parseInt( currentFamiliar.get("lifepoints").toString())); - } - } - - /** - * Called when the player logs in. - */ - public void login() { - if (hasFamiliar()) { - familiar.init(); - } - player.getFamiliarManager().setConfig(243269632); - } - - /** - * Summons a familiar. - * @param item The item. - * @param pet If the familiar is a pet. - * @param deleteItem we should delete the item. - */ - public void summon(Item item, boolean pet, boolean deleteItem) { - if (hasFamiliar()) { - 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."); - return; - } - if (pet) { - summonPet(item, deleteItem); - return; - } - final SummoningPouch pouch = SummoningPouch.get(item.getId()); - if (pouch == null) { - return; - } - if (player.getSkills().getStaticLevel(Skills.SUMMONING) < pouch.getLevelRequired()) { - player.getPacketDispatch().sendMessage("You need a Summoning level of " + pouch.getLevelRequired() + " to summon this familiar."); - return; - } - if (player.getSkills().getLevel(Skills.SUMMONING) < pouch.getSummonCost()) { - player.getPacketDispatch().sendMessage("You need at least " + pouch.getSummonCost() + " Summoning points to summon this familiar."); - return; - } - final int npcId = pouch.getNpcId(); - Familiar fam = FAMILIARS.get(npcId); - if (fam == null) { - player.getPacketDispatch().sendMessage("Invalid familiar " + npcId + " - report on 2009scape github"); - return; - } - 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()); - familiar = fam; - spawnFamiliar(); - if (player.getSkullManager().isWilderness()) { - player.getAppearance().sync(); - } - } - - /** - * Summons a familiar. - * @param item the item. - * @param pet the pet. - */ - public void summon(final Item item, boolean pet) { - summon(item, pet, true); - } - - /** - * Morphs a pet. - * @param item the item. - * @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); - } - - /** - * Summons a pet. - * @param item the item. - * @param deleteItem the item. - */ - private boolean summonPet(final Item item, boolean deleteItem) { - return summonPet(item, deleteItem, false, null); - } - - /** - * Summons a pet. - * @param item the item. - * @param morph the pet. - */ - private boolean summonPet(final Item item, boolean deleteItem, boolean morph, Location location) { - final int itemId = item.getId(); - if (itemId > 8850 && itemId < 8900) { - return false; - } - Pets pets = Pets.forId(itemId); - if (pets == null) { - return false; - } - if (player.getSkills().getStaticLevel(Skills.SUMMONING) < pets.getSummoningLevel()) { - 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); - } - 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 npcId = pets.getNpcId(details.getStage()); - if (npcId > 0) { - familiar = new Pet(player, details, itemId, npcId); - if (deleteItem) { - player.animate(new Animation(827)); - player.getInventory().remove(item); - } - if (morph) { - morphFamiliar(location); - } else { - spawnFamiliar(); - } - return true; - } - return true; - } - - /** - * Morphs the current familiar. - * @param location the location. - */ - public void morphFamiliar(Location location) { - familiar.init(location, false); - player.getInterfaceManager().openTab(new Component(662)); - player.getInterfaceManager().setViewedTab(7); - } - - /** - * Spawns the current familiar. - */ - public void spawnFamiliar() { - familiar.init(); - player.getInterfaceManager().openTab(new Component(662)); - player.getInterfaceManager().setViewedTab(7); - } - - /** - * Dumps the bob. - */ - public void dumpBob() { - if (!hasFamiliar()) { - player.getPacketDispatch().sendMessage("You don't have a familiar."); - return; - } - Familiar familiar = getFamiliar(); - if (!familiar.isBurdenBeast()) { - player.getPacketDispatch().sendMessage("Your familiar is not a beast of burden."); - return; - } - BurdenBeast beast = ((BurdenBeast) familiar); - if (!player.getBank().hasSpaceFor(beast.getContainer())) { - player.getPacketDispatch().sendMessage("There is not enough space left in your bank."); - return; - } - player.getBank().addAll(beast.getContainer()); - beast.getContainer().clear(); - } - - /** - * Makes the pet eat. - * @param foodId The food item id. - * @param npc The pet NPC. - */ - public void eat(int foodId, Pet npc) { - if (npc != familiar) { - player.getPacketDispatch().sendMessage("This isn't your pet!"); - return; - } - Pet pet = (Pet) familiar; - Pets pets = Pets.forId(pet.getItemId()); - if (pets == null) { - return; - } - for (int food : pets.getFood()) { - if (food == foodId) { - player.getInventory().remove(new Item(foodId)); - player.getPacketDispatch().sendMessage("Your pet happily eats the " + ItemDefinition.forId(food).getName() + "."); - player.animate(new Animation(827)); - npc.getDetails().updateHunger(-15.0); - return; - } - } - player.getPacketDispatch().sendMessage("Nothing interesting happens."); - } - - /** - * Picks up a pet. - */ - public void pickup() { - if (player.getInventory().freeSlots() == 0) { - player.getPacketDispatch().sendMessage("You don't have enough room in your inventory."); - return; - } - Pet pet = ((Pet) familiar); - PetDetails details = pet.getDetails(); - if (player.getInventory().add(new Item(pet.getPet().getItemId(details.getStage())))) { - player.animate(Animation.create(827)); - player.getFamiliarManager().dismiss(); - } - } - - /** - * Adjusts the battle state. - * @param state the state. - */ - public void adjustBattleState(final BattleState state) { - if (!hasFamiliar()) { - return; - } - familiar.adjustPlayerBattle(state); - } - - /** - * Gets a boost from a familiar. - * @param skill the skill. - * @return the boosted level. - */ - public int getBoost(int skill) { - if (!hasFamiliar()) { - return 0; - } - return familiar.getBoost(skill); - } - - /** - * Checks if the player has an active familiar. - * @return {@code True} if so. - */ - public boolean hasFamiliar() { - return familiar != null; - } - - /** - * Checks if the player has an active familiar and is a pet. - * @return {@code True} if so. - */ - public boolean hasPet() { - return hasFamiliar() && familiar instanceof Pet; - } - - /** - * Dismisses the familiar. - * @param saveDetails the details of a pet. - */ - public void dismiss(boolean saveDetails) { - if (hasPet() && !saveDetails) { - removeDetails(((Pet) familiar).getItemId()); - } - if (hasFamiliar()) { - familiar.dismiss(); - } - } - - /** - * Dismisses the familiar. - */ - public void dismiss() { - dismiss(true); - } - - /** - * Removes the details for this pet. - * @param itemId The item id of the pet. - */ - public void removeDetails(int itemId) { - Pets pets = Pets.forId(itemId); - if (pets == null) { - return; - } - petDetails.remove(pets.getBabyItemId()); - } - - /** - * Checks if it's the owner of a familiar. - * @param familiar the familiar - * @return {@code True} if so. - */ - public boolean isOwner(Familiar familiar) { - if (!hasFamiliar()) { - return false; - } - if (this.familiar != familiar) { - player.getPacketDispatch().sendMessage("This is not your familiar."); - return false; - } - 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. - */ - public Familiar getFamiliar() { - return familiar; - } - - /** - * Sets the familiar. - * @param familiar The familiar to set. - */ - public void setFamiliar(Familiar familiar) { - this.familiar = familiar; - } - - /** - * Gets the familiars. - * @return The familiars. - */ - public static Map getFamiliars() { - return FAMILIARS; - } - - /** - * Gets the usingSummoning. - * @return The usingSummoning. - */ - public boolean isUsingSummoning() { - return hasPouch || hasFamiliar(); - } - - /** - * Gets the hasPouch. - * @return The hasPouch. - */ - public boolean isHasPouch() { - return hasPouch; - } - - /** - * Sets the hasPouch. - * @param hasPouch The hasPouch to set. - */ - public void setHasPouch(boolean hasPouch) { - this.hasPouch = hasPouch; - } - - /** - * Gets the summoningCombatLevel. - * @return The summoningCombatLevel. - */ - public int getSummoningCombatLevel() { - return summoningCombatLevel; - } - - /** - * Sets the summoningCombatLevel. - * @param summoningCombatLevel The summoningCombatLevel to set. - */ - public void setSummoningCombatLevel(int summoningCombatLevel) { - this.summoningCombatLevel = summoningCombatLevel; - } - - - public Map getPetDetails() { - return petDetails; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/pet/Pet.java b/Server/src/main/java/core/game/node/entity/skill/summoning/pet/Pet.java deleted file mode 100644 index 0bda20e7a..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/pet/Pet.java +++ /dev/null @@ -1,170 +0,0 @@ -package core.game.node.entity.skill.summoning.pet; - -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.node.entity.player.Player; - -/** - * Represents a pet. - * @author Emperor - * @author 'Vexia - */ -public final class Pet extends Familiar { - - /** - * The item id. - */ - private final int itemId; - - /** - * The pet details. - */ - private final PetDetails details; - - /** - * The growth rate of the pet. - */ - private double growthRate; - - /** - * The pets type. - */ - private final Pets pet; - - /** - * Constructs a new {@code Pet} {@code Object}. - * @param owner the owner. - * @param details the details. - * @param itemId the itemId. - * @param id the id. - */ - public Pet(Player owner, final PetDetails details, int itemId, int id) { - super(owner, id, -1, -1, -1); - this.pet = Pets.forId(itemId); - this.details = details; - this.itemId = itemId; - this.growthRate = pet.getGrowthRate(); - } - - @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); - } - - @Override - public void handleTickActions() { - final PetDetails petDetails = details; - if (getPet().getFood().length > 0) { - petDetails.updateHunger(petDetails.getStage() == 0 ? 0.025 : 0.018); - } - double hunger = petDetails.getHunger(); - if (hunger >= 90.0 && hunger < 90.025) { - owner.getPacketDispatch().sendMessage("Your pet is starving, feed it before it runs off."); - } - double growth = petDetails.getGrowth(); - if (pet.getGrowthRate() > 0.000) { - petDetails.updateGrowth(pet.getGrowthRate()); - if (growth == 100.0) { - growNextStage(); - } - } - if ((!isInvisible() && owner.getLocation().getDistance(getLocation()) > 12) || (isInvisible() && ticks % 25 == 0)) { - if (!call()) { - setInvisible(true); - } - } else if (!getPulseManager().hasPulseRunning()) { - startFollowing(); - } - owner.getConfigManager().set(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; - } - if (pet == null) { - return; - } - int npcId = pet.getNpcId(details.getStage() + 1); - if (npcId < 1) { - return; - } - details.setStage(details.getStage() + 1); - int itemId = pet.getItemId(details.getStage()); - if (pet.getNpcId(details.getStage() + 1) > 0) { - details.updateGrowth(-100.0); - } - clear(); - Pet newPet = new Pet(owner, details, itemId, npcId); - newPet.growthRate = growthRate; - owner.getFamiliarManager().setFamiliar(newPet); - owner.getPacketDispatch().sendMessage("Your pet has grown larger."); - owner.getFamiliarManager().spawnFamiliar(); - } - - @Override - public Familiar construct(Player owner, int id) { - return this; - } - - @Override - protected boolean specialMove(FamiliarSpecial special) { - return false; - } - - @Override - public boolean isCombatFamiliar() { - 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. - */ - public int getItemId() { - return itemId; - } - - /** - * Gets the details. - * @return The details. - */ - public PetDetails getDetails() { - return details; - } - - /** - * Gets the pet. - * @return The pet. - */ - public Pets getPet() { - return pet; - } - - @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/java/core/game/node/entity/skill/summoning/pet/PetDetails.java b/Server/src/main/java/core/game/node/entity/skill/summoning/pet/PetDetails.java deleted file mode 100644 index d0aa5b360..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/pet/PetDetails.java +++ /dev/null @@ -1,94 +0,0 @@ -package core.game.node.entity.skill.summoning.pet; - - - -import java.nio.ByteBuffer; - -/** - * A class containing pet details for a certain pet. - * @author Emperor - */ -public final class PetDetails { - - /** - * The hunger rate. - */ - private double hunger = 0.0; - - /** - * The growth rate. - */ - 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. - */ - public PetDetails(double growth) { - this.growth = growth; - } - - /** - * Increases the hunger value by the given amount. - * @param amount The amount. - */ - public void updateHunger(double amount) { - hunger += amount; - if (hunger < 0.0) { - hunger = 0.0; - } else if (hunger > 100.0) { - hunger = 100.0; - } - } - - /** - * Increases the growth value by the given amount. - * @param amount The amount. - */ - public void updateGrowth(double amount) { - growth += amount; - if (growth < 0.0) { - growth = 0.0; - } else if (growth > 100.0) { - growth = 100.0; - } - } - - /** - * Gets the hunger. - * @return The hunger. - */ - public double getHunger() { - return hunger; - } - - /** - * Gets the growth. - * @return The growth. - */ - public double getGrowth() { - return growth; - } - - /** - * Gets the stage. - * @return The stage. - */ - public int getStage() { - return stage; - } - - /** - * 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/java/core/game/node/entity/skill/summoning/pet/Pets.java b/Server/src/main/java/core/game/node/entity/skill/summoning/pet/Pets.java deleted file mode 100644 index ce8c539c3..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/pet/Pets.java +++ /dev/null @@ -1,480 +0,0 @@ -package core.game.node.entity.skill.summoning.pet; - -import core.game.node.entity.player.Player; - -import java.util.HashMap; -import java.util.Map; - -/** - * An enum containing all the pets and their info. - * @author Emperor - */ -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), - - /** - * 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. - */ - BULLDOG(12522, 12523, -1, 6969, 6968, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), BULLDOG_1(12720, 12721, -1, 7259, 7257, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), BULLDOG_2(12722, 12723, -1, 7260, 7258, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), - - /** - * A dalmation pet. - */ - DALMATIAN(12518, 12519, -1, 6964, 6965, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), DALMATIAN_1(12712, 12713, -1, 7249, 7250, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), DALMATIAN_2(12714, 12715, -1, 7251, 7252, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), - - /** - * A greyhound pet. - */ - GREYHOUND(12514, 12515, -1, 6960, 6961, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), GREYHOUND_1(12704, 12705, -1, 7241, 7242, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), GREYHOUND_2(12706, 12707, -1, 7243, 7244, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), - - /** - * A labrador pet. - */ - LABRADOR(12516, 12517, -1, 6962, 6963, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), LABRADOR_1(12708, 12709, -1, 7245, 7246, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), LABRADOR_2(12710, 12711, -1, 7247, 7248, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), - - /** - * A sheepdog pet. - */ - SHEEPDOG(12520, 12521, -1, 6966, 6967, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), SHEEPDOG_1(12716, 12717, -1, 7253, 7254, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), SHEEPDOG_2(12718, 12719, -1, 7255, 7256, -1, 0.0033333333333333, 4, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526), - - /** - * 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), - - /** - * Gecko pet. - */ - GECKO(12488, 12489, -1, 6915, 6916, -1, 0.005, 10, 12125, 12127), GECKO_1(12738, 12742, -1, 7277, 7281, -1, 0.005, 10, 12125, 12127), GECKO_2(12739, 12743, -1, 7278, 7282, -1, 0.005, 10, 12125, 12127), GECKO_3(12740, 12744, -1, 7279, 7283, -1, 0.005, 10, 12125, 12127), GECKO_4(12741, 12745, -1, 7280, 7284, -1, 0.005, 10, 12125, 12127), - - /** - * The platypus pet. - */ - PLATYPUS(12551, 12548, -1, 7018, 7015, -1, 0.0046296296296296, 10, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129), PLATYPUS_1(12552, 12549, -1, 7019, 7016, -1, 0.0046296296296296, 10, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129), PLATYPUS_2(12553, 12550, -1, 7020, 7017, -1, 0.0046296296296296, 10, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129), - - /** - * The broav pet. - */ - BROAV(14533, -1, -1, 8491, -1, -1, 0.0, 23, 2970), - - /** - * The penguin pet. - */ - 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. - */ - GIANT_CRAB(12500, 12501, -1, 6947, 6948, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), GIANT_CRAB_1(12746, 12747, -1, 7293, 7294, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), GIANT_CRAB_2(12748, 12749, -1, 7295, 7296, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), GIANT_CRAB_3(12750, 12751, -1, 7297, 7298, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), GIANT_CRAB_4(12752, 12753, -1, 7299, 7300, -1, 0.0069444444444444, 40, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270), - - /** - * A Raven pet. - */ - RAVEN(12484, 12485, -1, 6911, 6912, -1, 0.00698888, 50, 313, 12129), RAVEN_1(12724, 12725, -1, 7261, 7262, -1, 0.00698888, 50, 313, 12129), RAVEN_2(12726, 12727, -1, 7263, 7264, -1, 0.00698888, 50, 313, 12129), RAVEN_3(12728, 12729, -1, 7265, 7266, -1, 0.00698888, 50, 313, 12129), RAVEN_4(12730, 12731, -1, 7267, 7268, -1, 0.00698888, 50, 313, 12129), RAVEN_5(12732, 12733, -1, 7269, 7270, -1, 0.00698888, 50, 313, 12129), - - /** - * A squirrel pet. - */ - SQUIRREL(12490, 12491, -1, 6919, 6920, -1, 0.0071225071225071, 60, 12130), SQUIRREL_1(12754, 12755, -1, 7301, 7302, -1, 0.0071225071225071, 60, 12130), SQUIRREL_2(12756, 12757, -1, 7303, 7304, -1, 0.0071225071225071, 60, 12130), SQUIRREL_3(12758, 12759, -1, 7305, 7306, -1, 0.0071225071225071, 60, 12130), SQUIRREL_4(12760, 12761, -1, 7307, 7308, -1, 0.0071225071225071, 60, 12130), - - /** - * Godbirds. - */ - SARADOMIN_OWL(12503, 12504, 12505, 6949, 6950, 6951, 0.0069444444444444, 70, 313, 12129), ZAMORAK_HAWK(12506, 12507, 12508, 6952, 6953, 6954, 0.0069444444444444, 70, 313, 12129), GUTHIX_RAPTOR(12509, 12510, 12511, 6955, 6956, 6957, 0.0069444444444444, 70, 313, 12129), - - /** - * Ex-ex parrot - */ - EX_EX_PARROT(13335, -1, -1, 7844, -1, -1, 0.0, 71, 13379), - - /** - * The phoenix eggling pets. - */ - CUTE_PHOENIX_EGGLING(14627, -1, -1, 8578, -1, -1, 0.0, 72, 592), MEAN_PHOENIX_EGGLING(14626, -1, -1, 8577, -1, -1, 0.0, 72, 592), - - /** - * A raccoon pet. - */ - 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. - */ - VULTURE(12498, 12499, -1, 6945, 6946, -1, 0.0078, 85, 313, 12129), VULTURE_1(12766, 12767, -1, 7319, 7320, -1, 0.0078, 85, 313, 12129), VULTURE_2(12768, 12769, -1, 7321, 7322, -1, 0.0078, 85, 313, 12129), VULTURE_3(12770, 12771, -1, 7323, 7324, -1, 0.0078, 85, 313, 12129), VULTURE_4(12772, 12773, -1, 7325, 7326, -1, 0.0078, 85, 313, 12129), VULTURE_5(12774, 12775, -1, 7327, 7328, -1, 0.0078, 85, 313, 12129), - - /** - * A chameleon pet. - */ - CHAMELEON(12492, 12493, -1, 6922, 6923, -1, 0.0069444444444444, 90, 12125), - - /** - * A monkey pet. - */ - MONKEY(12496, 12497, -1, 6942, 6943, -1, 0.0069444444444444, 95, 1963), MONKEY_1(12682, 12683, -1, 7210, 7211, -1, 0.0069444444444444, 95, 1963), MONKEY_2(12684, 12685, -1, 7212, 7213, -1, 0.0069444444444444, 95, 1963), MONKEY_3(12686, 12687, -1, 7214, 7215, -1, 0.0069444444444444, 95, 1963), MONKEY_4(12688, 12689, -1, 7216, 7217, -1, 0.0069444444444444, 95, 1963), MONKEY_5(12690, 12691, -1, 7218, 7219, -1, 0.0069444444444444, 95, 1963), MONKEY_6(12692, 12693, -1, 7220, 7221, -1, 0.0069444444444444, 95, 1963), MONKEY_7(12694, 12695, -1, 7222, 7223, -1, 0.0069444444444444, 95, 1963), MONKEY_8(12696, 12697, -1, 7224, 7225, -1, 0.0069444444444444, 95, 1963), MONKEY_9(12698, 12699, -1, 7226, 7227, -1, 0.0069444444444444, 95, 1963), - - /** - * A baby dragon pet. - */ - 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. - */ - private static final Map babyPets = new HashMap(); - - /** - * The grown pets mapping. - */ - private static final Map grownPets = new HashMap(); - - /** - * The overgrown pets mapping. - */ - private static final Map overgrownPets = new HashMap(); - - /** - * Populates the mappings. - */ - static { - for (Pets pet : Pets.values()) { - babyPets.put(pet.babyItemId, pet); - if (pet.grownItemId > 0) { - grownPets.put(pet.grownItemId, pet); - if (pet.getOvergrownItemId() > 0) { - overgrownPets.put(pet.overgrownItemId, pet); - } - } - } - } - - /** - * Gets the pet object for the item id. - * @param itemId The item id. - * @return The pet object. - */ - public static Pets forId(int itemId) { - Pets pet = babyPets.get(itemId); - if (pet == null) { - pet = grownPets.get(itemId); - if (pet == null) { - return overgrownPets.get(itemId); - } - return pet; - } - return pet; - } - - /** - * Checks if a player has a pet. - * @param player The player. - * @return {@code True} if so. - */ - public static boolean hasPet(Player player) { - for (int itemId : babyPets.keySet()) { - if (player.getInventory().containsAtLeastOneItem(itemId)) { - return true; - } - } - for (int itemId : grownPets.keySet()) { - if (player.getInventory().containsAtLeastOneItem(itemId)) { - return true; - } - } - for (int itemId : overgrownPets.keySet()) { - if (player.getInventory().containsAtLeastOneItem(itemId)) { - return true; - } - } - return false; - } - - /** - * The baby item id. - */ - private final int babyItemId; - - /** - * The grown pet's item id. - */ - private final int grownItemId; - - /** - * The overgrown pet's item id. - */ - private final int overgrownItemId; - - /** - * The baby pet NPC id. - */ - private final int babyNpcId; - - /** - * The grown pet NPC id. - */ - private final int grownNpcId; - - /** - * The overgrown pet NPC id. - */ - private final int overgrownNpcId; - - /** - * The growth rate. - */ - private final double growthRate; - - /** - * The summoning level required. - */ - private final int summoningLevel; - - /** - * The food this pet uses. - */ - private final int[] food; - - /** - * Constructs a new {@code Pets} {@code Object}. - * @param babyItemId The baby pet item id. - * @param grownItemId The grown pet item id. - * @param overgrownItemId The overgrown item id. - * @param babyNpcId The baby pet npc id. - * @param grownNpcId The grown pet npc id. - * @param overgrownNpcId The overgrown npc id. - * @param growthRate The growth rate (amount to increase growth with every - * tick). - * @param summoningLevel The summoning level required. - * @param food The food item ids the pet uses. - */ - private Pets(int babyItemId, int grownItemId, int overgrownItemId, int babyNpcId, int grownNpcId, int overgrownNpcId, double growthRate, int summoningLevel, int... food) { - this.babyItemId = babyItemId; - this.grownItemId = grownItemId; - this.overgrownItemId = overgrownItemId; - this.babyNpcId = babyNpcId; - this.grownNpcId = grownNpcId; - this.overgrownNpcId = overgrownNpcId; - this.growthRate = growthRate; - this.summoningLevel = summoningLevel; - this.food = food; - } - - /** - * Gets the babyItemId. - * @return The babyItemId. - */ - public int getBabyItemId() { - return babyItemId; - } - - /** - * Gets the grownItemId. - * @return The grownItemId. - */ - public int getGrownItemId() { - return grownItemId; - } - - /** - * Gets the overgrownItemId. - * @return The overgrownItemId. - */ - public int getOvergrownItemId() { - return overgrownItemId; - } - - /** - * Gets the babyNpcId. - * @return The babyNpcId. - */ - public int getBabyNpcId() { - return babyNpcId; - } - - /** - * Gets the grownNpcId. - * @return The grownNpcId. - */ - public int getGrownNpcId() { - return grownNpcId; - } - - /** - * Gets the overgrownNpcId. - * @return The overgrownNpcId. - */ - public int getOvergrownNpcId() { - return overgrownNpcId; - } - - /** - * Gets the growthRate. - * @return The growthRate. - */ - public double getGrowthRate() { - return growthRate; - } - - /** - * Gets the summoningLevel. - * @return The summoningLevel. - */ - public int getSummoningLevel() { - return summoningLevel; - } - - /** - * Gets the food. - * @return The food. - */ - public int[] getFood() { - return food; - } - - /** - * Gets the NPC id for this pet. - * @param stage The stage of the pet. - * @return The NPc id. - */ - public int getNpcId(int stage) { - switch (stage) { - case 0: - return babyNpcId; - case 1: - return grownNpcId; - case 2: - return overgrownNpcId; - } - return 0; - } - - /** - * Gets the item id for this pet. - * @param stage The stage of the pet. - * @return The item id. - */ - public int getItemId(int stage) { - switch (stage) { - case 0: - return babyItemId; - case 1: - return grownItemId; - case 2: - return overgrownItemId; - } - return 0; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/thieving/StallThiefPulse.java b/Server/src/main/java/core/game/node/entity/skill/thieving/StallThiefPulse.java deleted file mode 100644 index a50388546..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/StallThiefPulse.java +++ /dev/null @@ -1,144 +0,0 @@ -package core.game.node.entity.skill.thieving; - -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -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 rs09.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.tools.RandomFunction; -import core.tools.StringUtils; - -/** - * Represents the pulse used to thieve a stall. - * @author 'Vexia - */ -public final class StallThiefPulse extends SkillPulse { - - /** - * Represents the stealing animation. - */ - private static final Animation ANIMATION = new Animation(832); - - /** - * Represents the stall being thieved. - */ - private final Stall stall; - - /** - * Represents the ticks passed. - */ - private int ticks; - - /** - * Constructs a new {@code StallThiefPulse} {@code Object}. - * @param player the player. - * @param node the node. - * @param stall the stall. - */ - public StallThiefPulse(Player player, Scenery node, final Stall stall) { - super(player, node); - this.stall = stall; - } - - @Override - public void start() { - player.setAttribute("thieveDelay", GameWorld.getTicks()); - super.start(); - } - - @Override - public boolean checkRequirements() { - if (stall == null) { - return false; - } - if (player.inCombat()) { - player.getPacketDispatch().sendMessage("You cant steal from the market stall during combat!"); - return false; - } - if (player.getSkills().getLevel(Skills.THIEVING) < stall.getLevel()) { - player.getPacketDispatch().sendMessage("You need to be level " + stall.getLevel() + " to steal from the " + node.getName().toLowerCase() + "."); - return false; - } - if (player.getInventory().freeSlots() == 0) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - return false; - } - return true; - } - - @Override - public boolean hasInactiveNode() { - if (player.getAttribute("thieveDelay", 0) <= GameWorld.getTicks()) { - return false; - } - return super.hasInactiveNode(); - } - - @Override - public void animate() { - } - - @Override - public boolean reward() { - if (ticks == 0) { - player.animate(ANIMATION); - player.getLocks().lockInteractions(2); - } - if (++ticks % 3 != 0) { - return false; - } - final boolean success = success(); - if (success) { - if (stall == Stall.SILK_STALL) { - player.getSavedData().getGlobalData().setSilkSteal(System.currentTimeMillis() + 1800000); - } - if (node.isActive()) { - SceneryBuilder.replace(((Scenery) node), ((Scenery) node).transform(stall.getEmpty(node.getId())), stall.getDelay()); - } - final Item item = stall.getRandomLoot(); - player.getInventory().add(item); - player.getSkills().addExperience(Skills.THIEVING, stall.getExperience(), true); - if (item.getId() == 1987) { - player.getPacketDispatch().sendMessage("You steal grapes from the grape stall."); - return true; - } - player.getPacketDispatch().sendMessage("You steal " + (StringUtils.isPlusN(item.getName()) ? "an" : "a") + " " + item.getName().toLowerCase() + " from the " + stall.name().toLowerCase().replace('_',' ') + "."); - } - 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; - } - } - - /** - * Checks if the thief is successful. - * @return {@code True} if so. - */ - private boolean success() { - int mod = 0; - if (RandomFunction.random(15 + mod) < 4) { - 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!"); - npc.getProperties().getCombatPulse().attack(player); - return false; - } - } - } - return true; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/state/EntityState.java b/Server/src/main/java/core/game/node/entity/state/EntityState.java deleted file mode 100644 index 1101a3b70..000000000 --- a/Server/src/main/java/core/game/node/entity/state/EntityState.java +++ /dev/null @@ -1,61 +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 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/java/core/game/node/entity/state/StateManager.java b/Server/src/main/java/core/game/node/entity/state/StateManager.java deleted file mode 100644 index 304b1cee0..000000000 --- a/Server/src/main/java/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/java/core/game/node/entity/state/StatePulse.java b/Server/src/main/java/core/game/node/entity/state/StatePulse.java deleted file mode 100644 index 21c3b55d7..000000000 --- a/Server/src/main/java/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 rs09.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/java/core/game/node/entity/state/impl/FireResistantPulse.java b/Server/src/main/java/core/game/node/entity/state/impl/FireResistantPulse.java deleted file mode 100644 index e99a0de1a..000000000 --- a/Server/src/main/java/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 rs09.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/java/core/game/node/entity/state/impl/FrozenStatePulse.java b/Server/src/main/java/core/game/node/entity/state/impl/FrozenStatePulse.java deleted file mode 100644 index d2467002e..000000000 --- a/Server/src/main/java/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 rs09.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/java/core/game/node/entity/state/impl/MiasmicStatePulse.java b/Server/src/main/java/core/game/node/entity/state/impl/MiasmicStatePulse.java deleted file mode 100644 index 0a4c629f4..000000000 --- a/Server/src/main/java/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 rs09.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/java/core/game/node/entity/state/impl/PoisonStatePulse.java b/Server/src/main/java/core/game/node/entity/state/impl/PoisonStatePulse.java deleted file mode 100644 index 26594fb9c..000000000 --- a/Server/src/main/java/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/java/core/game/node/entity/state/impl/SkullStatePulse.java b/Server/src/main/java/core/game/node/entity/state/impl/SkullStatePulse.java deleted file mode 100644 index 9946b3fbb..000000000 --- a/Server/src/main/java/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/java/core/game/node/entity/state/impl/StunStatePulse.java b/Server/src/main/java/core/game/node/entity/state/impl/StunStatePulse.java deleted file mode 100644 index e3ff0f615..000000000 --- a/Server/src/main/java/core/game/node/entity/state/impl/StunStatePulse.java +++ /dev/null @@ -1,88 +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; - -/** - * 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); - - /** - * 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(3201); - ((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/java/core/game/node/entity/state/impl/TeleblockStatePulse.java b/Server/src/main/java/core/game/node/entity/state/impl/TeleblockStatePulse.java deleted file mode 100644 index ef4fe23f4..000000000 --- a/Server/src/main/java/core/game/node/entity/state/impl/TeleblockStatePulse.java +++ /dev/null @@ -1,74 +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 teleblock state pulse. - * @author Vexia - */ -public final class TeleblockStatePulse extends StatePulse { - - /** - * The ticks needed to pass. - */ - private final int ticks; - - /** - * The current tick. - */ - private 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; - } - - @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], 0); - } - -} diff --git a/Server/src/main/java/core/game/package-info.java b/Server/src/main/java/core/game/package-info.java deleted file mode 100644 index bd16114fa..000000000 --- a/Server/src/main/java/core/game/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Used for handling important aspects of the game. - * - * @author Emperor - * - */ -package core.game; \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/SystemShutdownHook.java b/Server/src/main/java/core/game/system/SystemShutdownHook.java deleted file mode 100644 index 707a4ae2c..000000000 --- a/Server/src/main/java/core/game/system/SystemShutdownHook.java +++ /dev/null @@ -1,19 +0,0 @@ -package core.game.system; - -import rs09.game.system.SystemLogger; - -/** - * Handles the shutdown hook. - * @author Emperor - */ -public final class SystemShutdownHook implements Runnable { - - @Override - public void run() { - if (SystemManager.isTerminated()) { - return; - } - SystemLogger.logInfo("[SystemShutdownHook] Terminating..."); - SystemManager.getTerminator().terminate(); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/SystemTermination.java b/Server/src/main/java/core/game/system/SystemTermination.java deleted file mode 100644 index 1ce55219b..000000000 --- a/Server/src/main/java/core/game/system/SystemTermination.java +++ /dev/null @@ -1,107 +0,0 @@ -package core.game.system; - -import core.game.ge.GrandExchangeDatabase; -import core.game.interaction.object.dmc.DMCHandler; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.login.PlayerParser; -import rs09.Server; -import rs09.ServerConstants; -import rs09.ServerStore; -import rs09.game.content.global.GlobalKillCounter; -import rs09.game.ge.OfferManager; -import rs09.game.system.SystemLogger; -import rs09.game.world.repository.Repository; - -import java.io.File; -import java.util.Iterator; - -/** - * Handles the terminating of the system. - * @author Emperor - * - */ -public final class SystemTermination { - - /** - * Constructs a new {@code SystemTermination} {@code Object}. - */ - protected SystemTermination() { - /* - * empty. - */ - } - - /** - * Terminates the system safely. - */ - public void terminate() { - SystemLogger.logInfo("[SystemTerminator] Initializing termination sequence - do not shutdown!"); - try { - Server.setRunning(false); - for(Player player : Repository.getPlayers()){ - DMCHandler dmc = player.getAttribute("dmc",null); - if(dmc != null){ - dmc.clear(false); - } - } - if(ServerConstants.DATA_PATH != null) - save(ServerConstants.DATA_PATH); - } catch (Throwable e) { - e.printStackTrace(); - } - SystemLogger.logInfo("[SystemTerminator] Server successfully terminated!"); - } - - /** - * Saves all system data on the directory. - * @param directory The base directory. - */ - public void save(String directory) { - File file = new File(directory); - SystemLogger.logInfo("[SystemTerminator] Saving data [dir=" + file.getAbsolutePath() + "]..."); - if (!file.isDirectory()) { - file.mkdirs(); - } - Server.getReactor().terminate(); - for (Iterator it = Repository.getPlayers().iterator(); it.hasNext();) { - try { - Player p = it.next(); - if (p != null && !p.isArtificial()) { // Should never be null. -/* p.removeAttribute("combat-time"); - p.clear(); - PlayerParser.save(p);*/ - p.getDetails().save(); - p.getLogoutPlugins().forEach(playerPlugin -> { - try { - playerPlugin.newInstance(p); - } catch (Throwable throwable) { - throwable.printStackTrace(); - } - }); - p.clear(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - long start = System.currentTimeMillis(); - while(!Repository.getDisconnectionQueue().isEmpty() && System.currentTimeMillis() - start < 5000L){ - Repository.getDisconnectionQueue().update(); - try { - Thread.sleep(100); - } catch (Exception ignored) {} - } - Repository.getDisconnectionQueue().update(); - GlobalKillCounter.save(); - GrandExchangeDatabase.save(); - OfferManager.save(); - SystemLogger.flushLogs(); - SystemLogger.logInfo("[SystemTerminator] Saved Grand Exchange databases!"); - Repository.getDisconnectionQueue().clear(); - SystemLogger.logInfo("[SystemTerminator] Saving Server Store..."); - ServerStore.save(); - SystemLogger.logInfo("[SystemTerminator] Server Store Saved!"); -// ServerStore.dump(directory + "store/"); - SystemLogger.logInfo("[SystemTerminator] Saved player accounts!"); - } -} diff --git a/Server/src/main/java/core/game/system/communication/ClanRepository.java b/Server/src/main/java/core/game/system/communication/ClanRepository.java deleted file mode 100644 index b26098a1a..000000000 --- a/Server/src/main/java/core/game/system/communication/ClanRepository.java +++ /dev/null @@ -1,562 +0,0 @@ -package core.game.system.communication; - -import core.game.component.Component; -import core.game.content.activity.ActivityPlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.PlayerDetails; -import core.game.node.entity.player.info.Rights; -import core.game.system.monitor.PlayerMonitor; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import core.net.amsc.WorldCommunicator; -import core.net.packet.PacketRepository; -import core.net.packet.context.ClanContext; -import core.net.packet.context.MessageContext; -import core.net.packet.out.CommunicationMessage; -import core.net.packet.out.UpdateClanChat; - -import java.util.*; - -/** - * Handles clan communication. - * @author Emperor - */ -public final class ClanRepository { - - /** - * The maximum amount of members to be in a clan chat. - */ - private static final int MAX_MEMBERS = 100; - - /** - * The clan repository. - */ - private static final Map CLAN_REPOSITORY = new HashMap<>(); - - /** - * The name of the clan owner. - */ - private final String owner; - - /** - * The clan name. - */ - private String name = "Chat disabled"; - - /** - * The rank required for joining. - */ - private ClanRank joinRequirement = ClanRank.FRIEND; - - /** - * The rank required for messaging. - */ - private ClanRank messageRequirement = ClanRank.NONE; - - /** - * The rank required for kicking members. - */ - private ClanRank kickRequirement = ClanRank.OWNER; - - /** - * The rank required for loot-share. - */ - private ClanRank lootRequirement = ClanRank.ADMINISTRATOR; - - /** - * The members mapping. - */ - private final Map ranks = new HashMap<>(); - - /** - * The banned players. - */ - private final Map banned = new HashMap<>(); - - /** - * The players who are currently in the friends chat. - */ - private List players = new ArrayList<>(MAX_MEMBERS); - - /** - * The current clan wars activity. - */ - private ActivityPlugin clanWar; - - /** - * Constructs a new {@code ClanRepository} {@code Object}. - * @param owner The owner of the clan. - */ - public ClanRepository(String owner) { - this.owner = owner; - } - - /** - * Enters the clan chat. - * @param player The player. - * @return {@code True} if the player successfully entered the clan chat. - */ - public boolean enter(Player player) { - if (!owner.equals("2009scape") && players.size() >= MAX_MEMBERS) { - player.getPacketDispatch().sendMessage("The channel you tried to join is full.:clan:"); - return false; - } - if (!player.getName().equals(owner) && player.getDetails().getRights() != Rights.ADMINISTRATOR) { - if (isBanned(player.getName())) { - player.getPacketDispatch().sendMessage("You are temporarily banned from this clan channel.:clan:"); - return false; - } - Player o = Repository.getPlayerByName(owner); - if (o != null) { - if (o.getCommunication().getBlocked().contains(player.getName())) { - player.getPacketDispatch().sendMessage("You do not have a high enough rank to join this clan channel.:clan:"); - return false; - } - } - ClanRank rank = getRank(player); - if (rank.ordinal() < joinRequirement.ordinal()) { - player.getPacketDispatch().sendMessage("You do not have a high enough rank to join this clan channel.:clan:"); - return false; - } - } - ClanEntry entry = new ClanEntry(player); - if (!players.contains(entry)) { - players.add(entry); - } - player.getPacketDispatch().sendMessage("Now talking in clan channel " + name + ":clan:"); - player.getPacketDispatch().sendMessage("To talk, start each line of chat with the / symbol.:clan:"); - update(); - return true; - } - - /** - * Cleans the chat from all players that shouldn't be in it. - */ - public void clean(boolean disable) { - if (WorldCommunicator.isEnabled()) { - return; - } - for (Iterator it = players.iterator(); it.hasNext();) { - ClanEntry entry = it.next(); - Player player = entry.getPlayer(); - boolean remove = disable; - if (!remove) { - remove = getRank(player).ordinal() < joinRequirement.ordinal(); - } - if (remove) { - leave(player, false); - player.getCommunication().setClan(null); - it.remove(); - } - } - if (players.isEmpty()) { - banned.clear(); - } - update(); - } - - /** - * Checks if a player is banned. - * @param name The player's name. - * @return {@code True} if so. - */ - public boolean isBanned(String name) { - if (banned.containsKey(name)) { - long time = banned.get(name); - if (time > System.currentTimeMillis()) { - return true; - } - banned.remove(name); - } - return false; - } - - /** - * Sends a message to all players in the chat. - * @param player The player sending the message. - * @param message The message to send. - */ - public void message(Player player, String message) { - if (player.getLocks().isLocked("cc_message") || isBanned(player.getName())) { - return; - } - player.getLocks().lock("cc_message", 1); - if (!player.getName().equals(owner) && player.getDetails().getRights() != Rights.ADMINISTRATOR) { - ClanRank rank = getRank(player); - if (rank.ordinal() < messageRequirement.ordinal()) { - player.getPacketDispatch().sendMessage("You do not have a high enough rank to talk in this clan channel.:clan:"); - return; - } - } - StringBuilder sb = new StringBuilder(message); - sb.append(" => ").append(name).append(" (owned by ").append(owner).append(")"); - player.getMonitor().log(sb.toString(), PlayerMonitor.CLAN_CHAT_LOG); - for (Iterator it = players.iterator(); it.hasNext();) { - ClanEntry entry = it.next(); - Player p = entry.getPlayer(); - if (p != null) { - PacketRepository.send(CommunicationMessage.class, new MessageContext(p, player, MessageContext.CLAN_MESSAGE, message)); - } - } - } - - /** - * Kicks a player from this chat. - * @param player the player. - * @param target the victim. - */ - public void kick(Player player, Player target) { - ClanRank rank = getRank(player); - if (target.getDetails().getRights() == Rights.ADMINISTRATOR) { - player.sendMessage("You can't kick an administrator.:clan:"); - return; - } - if (target.getName().equals(player.getName())) { - player.sendMessage("You can't kick yourself.:clan:"); - return; - } - if (player.getDetails().getRights() != Rights.ADMINISTRATOR && rank.ordinal() < kickRequirement.ordinal()) { - player.getPacketDispatch().sendMessage("You do not have a high enough rank to kick in this clan channel.:clan:"); - return; - } - if (target.getName().equals(owner)) { - player.getPacketDispatch().sendMessage("You can't kick the owner of this clan channel.:clan:"); - return; - } - for (ClanEntry e : players) { - PacketRepository.send(CommunicationMessage.class, new MessageContext(e.getPlayer(), player, MessageContext.CLAN_MESSAGE, "[Attempting to kick/ban " + target.getUsername() + " from this Clan Chat.]")); - } - leave(target, true, "You have been kicked from the channel.:clan:"); - target.getCommunication().setClan(null); - banned.put(target.getName(), System.currentTimeMillis() + (3_600_000)); - } - - /** - * Represents the method to leave a clan. - * @param player the player. - * @param remove If the player should be removed from the list. - */ - public void leave(Player player, boolean remove) { - leave(player, remove, "You have left the channel.:clan:"); - } - - /** - * Represents the method to leave a clan. - * @param player the player. - * @param remove If the player should be removed from the list. - */ - public void leave(Player player, boolean remove, String message) { - if (remove) { - players.remove(new ClanEntry(player)); - update(); - if (players.size() < 1) { - banned.clear(); - } - } - PacketRepository.send(UpdateClanChat.class, new ClanContext(player, this, true)); - player.getPacketDispatch().sendMessage(message); - if (clanWar != null && !isDefault()) { - clanWar.fireEvent("leavefc", player); - } - } - - /** - * Ranks a member of this chat. - * @param name The name of the member. - * @param rank The rank to set. - */ - public void rank(String name, ClanRank rank) { - boolean update; - if (rank == ClanRank.NONE) { - update = ranks.remove(name) != null; - } else { - update = ranks.put(name, rank) != rank; - } - if (update) { - clean(false); - } - } - - /** - * Updates the clan chat for all players in this clan. - */ - public void update() { - for (Iterator it = players.iterator(); it.hasNext();) { - ClanEntry e = it.next(); - if (e.getWorldId() == GameWorld.getSettings().getWorldId() && e.getPlayer() != null) { - PacketRepository.send(UpdateClanChat.class, new ClanContext(e.getPlayer(), this, false)); - } - } - } - - /** - * Gets the clan rank for the given clan entry. - * @param entry The clan entry. - * @return The rank. - */ - public ClanRank getRank(ClanEntry entry) { - if (entry.getPlayer() != null) { - return getRank(entry.getPlayer()); - } - ClanRank rank = ranks.get(entry.getName()); - if (rank == null) { - return ClanRank.NONE; - } - return rank; - } - - /** - * Gets the rank of the player. - * @param player The player. - * @return The clan rank. - */ - public ClanRank getRank(Player player) { - ClanRank rank = ranks.get(player.getName()); - if (player.getDetails().getRights() == Rights.ADMINISTRATOR && !player.getName().equals(owner)) { - return ClanRank.ADMINISTRATOR; - } - if (rank == null) { - if (player.getName().equals(owner)) { - return ClanRank.OWNER; - } - return ClanRank.NONE; - } - return rank; - } - - /** - * Opens the clan settings for the player. - * @param player The player. - */ - public static void openSettings(Player player) { - player.getInterfaceManager().open(new Component(590)); - ClanRepository c = get(player.getName()); - if (c != null) { - c.updateSettings(player); - } - } - - /** - * Updates the clan settings for the player. - * @param player The player. - */ - public void updateSettings(Player player) { - player.getPacketDispatch().sendString(name, 590, 22); - // player.getPacketDispatch().sendConfig(1083, (isCoinshare() ? 1 : 0) - // << 18 | (isLootshare() ? 0 : 1)); - player.getPacketDispatch().sendString(joinRequirement.getInfo(), 590, 23); - player.getPacketDispatch().sendString(messageRequirement.getInfo(), 590, 24); - player.getPacketDispatch().sendString(kickRequirement.getInfo(), 590, 25); - player.getPacketDispatch().sendString(lootRequirement.getInfo(), 590, 26); - } - - /** - * Loads the clan data. - * @param owner The owner of the clan to load. - * @return The clan data. - */ - public static ClanRepository get(String owner) { - return get(owner, false); - } - - /** - * Loads the clan data. - * @param owner The owner of the clan to load. - * @param create If the clan should be created if it doesn't exist. - * @return The clan data. - */ - public static ClanRepository get(String owner, boolean create) { - ClanRepository clan = CLAN_REPOSITORY.get(owner); - if (clan != null) { - return clan; - } - Player player = Repository.getPlayerByName(owner); - PlayerDetails details = player != null ? player.getDetails() : null; - if (details == null) { - details = PlayerDetails.getDetails(owner); - if (details == null) { - return null; - } - } - String name = details.getCommunication().getClanName(); - if (name.length() < 1) { - if (!create) { - return null; - } - name = "Chat disabled"; - } - CLAN_REPOSITORY.put(owner, clan = new ClanRepository(owner)); - for (Contact c : details.getCommunication().getContacts().values()) { - clan.ranks.put(c.getUsername(), c.getRank()); - } - clan.name = name; - clan.joinRequirement = details.getCommunication().getJoinRequirement(); - clan.messageRequirement = details.getCommunication().getMessageRequirement(); - clan.kickRequirement = details.getCommunication().getKickRequirement(); - clan.lootRequirement = details.getCommunication().getLootRequirement(); - return clan; - } - - /** - * Checks if this clan chat is the default clan chat. - * @return {@code True} if so. - */ - public boolean isDefault() { - return owner.equals(GameWorld.getSettings().getName().toLowerCase()); - } - - /** - * Gets the default clan chat. - * @return The default clan chat. - */ - public static ClanRepository getDefault() { - return get(GameWorld.getSettings().getName().toLowerCase()); - } - - /** - * Deletes a clan. - */ - public void delete() { - CLAN_REPOSITORY.remove(owner); - clean(true); - } - - /** - * Gets the currently loaded clans. - * @return The clans. - */ - public static Map getClans() { - return CLAN_REPOSITORY; - } - - /** - * Gets the list of players currently in the clan. - * @return The list of players. - */ - public List getPlayers() { - return players; - } - - /** - * Gets the joinRequirement. - * @return The joinRequirement. - */ - public ClanRank getJoinRequirement() { - return joinRequirement; - } - - /** - * Sets the joinRequirement. - * @param joinRequirement The joinRequirement to set. - */ - public void setJoinRequirement(ClanRank joinRequirement) { - this.joinRequirement = joinRequirement; - clean(false); - } - - /** - * Gets the messageRequirement. - * @return The messageRequirement. - */ - public ClanRank getMessageRequirement() { - return messageRequirement; - } - - /** - * Sets the messageRequirement. - * @param messageRequirement The messageRequirement to set. - */ - public void setMessageRequirement(ClanRank messageRequirement) { - this.messageRequirement = messageRequirement; - } - - /** - * Gets the kickRequirement. - * @return The kickRequirement. - */ - public ClanRank getKickRequirement() { - return kickRequirement; - } - - /** - * Sets the kickRequirement. - * @param kickRequirement The kickRequirement to set. - */ - public void setKickRequirement(ClanRank kickRequirement) { - this.kickRequirement = kickRequirement; - update(); - } - - /** - * Gets the lootRequirement. - * @return The lootRequirement. - */ - public ClanRank getLootRequirement() { - return lootRequirement; - } - - /** - * Gets the banned. - * @return the banned - */ - public Map getBanned() { - return banned; - } - - /** - * Sets the lootRequirement. - * @param lootRequirement The lootRequirement to set. - */ - public void setLootRequirement(ClanRank lootRequirement) { - this.lootRequirement = lootRequirement; - } - - /** - * Gets the owner. - * @return The owner. - */ - public String getOwner() { - return owner; - } - - /** - * Gets the name. - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Sets the name. - * @param name The name to set. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Gets the members. - * @return The members. - */ - public Map getRanks() { - return ranks; - } - - /** - * Gets the clanWar. - * @return The clanWar. - */ - public ActivityPlugin getClanWar() { - return clanWar; - } - - /** - * Sets the clanWar. - * @param clanWar The clanWar to set. - */ - public void setClanWar(ActivityPlugin clanWar) { - this.clanWar = clanWar; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/communication/CommunicationInfo.java b/Server/src/main/java/core/game/system/communication/CommunicationInfo.java deleted file mode 100644 index 2dee6f8ae..000000000 --- a/Server/src/main/java/core/game/system/communication/CommunicationInfo.java +++ /dev/null @@ -1,637 +0,0 @@ -package core.game.system.communication; - -import core.cache.misc.buffer.ByteBufferUtils; -import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; -import core.game.system.monitor.PlayerMonitor; -import core.game.system.mysql.SQLTable; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; -import core.net.packet.PacketRepository; -import core.net.packet.context.ContactContext; -import core.net.packet.context.MessageContext; -import core.net.packet.out.CommunicationMessage; -import core.net.packet.out.ContactPackets; -import core.tools.StringUtils; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Holds communication information. - * @author Emperor - */ -public final class CommunicationInfo { - - /** - * The maximum list size. - */ - public static final int MAX_LIST_SIZE = 200; - - /** - * The clan ranks. - */ - private final Map contacts = new HashMap<>(); - - /** - * The list of blocked players. - */ - private final List blocked = new ArrayList<>(20); - - /** - * The player's clan name. - */ - private String clanName = ""; - - /** - * The current clan this player is in. - */ - private String currentClan = ""; - - /** - * The rank required for joining. - */ - private ClanRank joinRequirement = ClanRank.FRIEND; - - /** - * The rank required for messaging. - */ - private ClanRank messageRequirement = ClanRank.NONE; - - /** - * The rank required for kicking members. - */ - private ClanRank kickRequirement = ClanRank.OWNER; - - /** - * The rank required for loot-share. - */ - private ClanRank lootRequirement = ClanRank.ADMINISTRATOR; - - /** - * If lootshare is enabled. - */ - private boolean lootShare; - - /** - * The current clan joined. - */ - private ClanRepository clan = null; - - /** - * The loot-share pulse. - */ - private Pulse lootSharePulse; - - /** - * Constructs a new {@code CommunicationInfo} {@code Object}. - */ - public CommunicationInfo() { - /* - * empty. - */ - } - - /** - * Saves the communication info. - * @param table The SQL table to update. - */ - public void save(SQLTable table) { - String contacts = ""; - String blocked = ""; - for (int i = 0; i < this.blocked.size(); i++) { - blocked += (i == 0 ? "" : ",") + this.blocked.get(i); - } - int count = 0; - for (Entry entry : this.contacts.entrySet()) { - contacts += "{" + entry.getKey() + "," + entry.getValue().getRank().ordinal() + "}" + (count == this.contacts.size() - 1 ? "" : "~"); - count++; - } - table.getColumn("blocked").updateValue(blocked); - table.getColumn("contacts").updateValue(contacts); - table.getColumn("clanName").updateValue(clanName); - table.getColumn("currentClan").updateValue(currentClan); - table.getColumn("clanReqs").updateValue(joinRequirement.ordinal() + "," + messageRequirement.ordinal() + "," + kickRequirement.ordinal() + "," + lootRequirement.ordinal()); - } - - /** - * Parses the communication info from the database. - * @param table The sql table to parse. - */ - public void parse(SQLTable table) { - String[] tokens = null; - if (table.getColumn("contacts").getValue() != null ) { - String contacts = (String) table.getColumn("contacts").getValue(); - if (!contacts.isEmpty()) { - String[] datas = contacts.split("~"); - Contact contact = null; - for (String d : datas) { - tokens = d.replace("{", "").replace("}", "").split(","); - if (tokens.length == 0) { - continue; - } - contact = new Contact(tokens[0]); - contact.setRank(ClanRank.values()[Integer.valueOf(tokens[1])]); - this.contacts.put(tokens[0], contact); - } - } - } - if (table.getColumn("blocked").getValue() != null ) { - String blocked = (String) table.getColumn("blocked").getValue(); - if (!blocked.isEmpty()) { - tokens = blocked.split(","); - for (String name : tokens) { - this.blocked.add(name); - } - } - } - clanName = ((String) table.getColumn("clanName").getValue()); - currentClan = (String) table.getColumn("currentClan").getValue(); - String clanReqs = (String) table.getColumn("clanReqs").getValue(); - if (clanReqs == "") { - return; - } - tokens = clanReqs.split(","); - ClanRank rank = null; - int ordinal = 0; - for (int i = 0; i < tokens.length; i++) { - ordinal = Integer.parseInt(tokens[i]); - if (ordinal < 0 || ordinal > ClanRank.values().length -1) { - continue; - } - rank = ClanRank.values()[ordinal]; - switch (i) { - case 0: - joinRequirement = rank; - break; - case 1: - messageRequirement = rank; - break; - case 2: - if (ordinal < 3 || ordinal > 8) { - break; - } - kickRequirement = rank; - break; - case 3: - lootRequirement = rank; - break; - } - } - } - - /** - * Toggles the loot-share. - */ - public void toggleLootshare(final Player player) { - if (lootShare) { - lootShare = false; - player.getConfigManager().set(1083, 0); - } else if (lootSharePulse != null) { - lootSharePulse.stop(); - lootSharePulse = null; - player.getConfigManager().set(1083, 0); - } else if (!lootShare) { - player.getConfigManager().set(1083, 2); - lootSharePulse = new Pulse(GameWorld.getSettings().isDevMode() ? 5 : 200, player) { - @Override - public boolean pulse() { - lootShare = true; - lootSharePulse = null; - player.getConfigManager().set(1083, 1); - return true; - } - }; - GameWorld.getPulser().submit(lootSharePulse); - } - } - - /** - * 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); - } - } - - /** - * Synchronizes the contact lists. - */ - public void sync(Player player) { - if (WorldCommunicator.isEnabled()) { - if (!player.isArtificial()) { - MSPacketRepository.requestCommunicationInfo(player.getName()); - } - return; - } - if (player.getSettings().getPrivateChatSetting() != 2) { - notifyPlayers(player, true, false); - } - PacketRepository.send(ContactPackets.class, new ContactContext(player, ContactContext.UPDATE_STATE_TYPE)); - PacketRepository.send(ContactPackets.class, new ContactContext(player, ContactContext.IGNORE_LIST_TYPE)); - for (String name : contacts.keySet()) { - Player p = Repository.getPlayerByName(name); - int worldId = 0; - if (p != null && showActive(player, p)) { - worldId = GameWorld.getSettings().getWorldId(); - } - PacketRepository.send(ContactPackets.class, new ContactContext(player, name, worldId)); - } - if (currentClan != null && !player.isArtificial() && (clan = ClanRepository.get(currentClan)) != null) { - clan.enter(player); - } - } - - /** - * Notifies all other players. - * @param online If this player is online. - * @param chatSetting If it was a chat setting change. - */ - public static void notifyPlayers(Player player, boolean online, boolean chatSetting) { - if (WorldCommunicator.isEnabled()) { - if (!online && !chatSetting) { - MSPacketRepository.sendPlayerRemoval(player.getName()); - } - return; - } - for (Player p : Repository.getPlayers()) { - if (p == player || !p.isActive()) { - continue; - } - if (hasContact(p, player.getName())) { - int worldId = 0; - if (online && showActive(p, player)) { - worldId = GameWorld.getSettings().getWorldId(); - } - p.getCommunication().getContacts().get(player.getName()).setWorldId(worldId); - PacketRepository.send(ContactPackets.class, new ContactContext(p, player.getName(), worldId)); - } - } - if (!online && !chatSetting && player.getCommunication().getClan() != null) { - player.getCommunication().getClan().leave(player, true); - } - } - - /** - * Sends a message to the target. - * @param player The player sending the message. - * @param target The target. - * @param message The message to send. - */ - public static void sendMessage(Player player, String target, String message) { - if (WorldCommunicator.isEnabled()) { - StringBuilder sb = new StringBuilder(message); - sb.append(" => ").append(target); - player.getMonitor().log(sb.toString(), PlayerMonitor.PRIVATE_CHAT_LOG); - MSPacketRepository.sendPrivateMessage(player, target, message); - return; - } - if (!player.getDetails().getCommunication().contacts.containsKey(target)) { - return; - } - Player p = Repository.getPlayerByName(target); - if (p == null || !p.isActive() || !showActive(p, player)) { - player.getPacketDispatch().sendMessage("That player is currently offline."); - return; - } - if (!GameWorld.getSettings().isDevMode()) { - StringBuilder sb = new StringBuilder(message); - sb.append(" => ").append(target); - player.getMonitor().log(sb.toString(), PlayerMonitor.PRIVATE_CHAT_LOG); - } - PacketRepository.send(CommunicationMessage.class, new MessageContext(player, p, MessageContext.SEND_MESSAGE, message)); - PacketRepository.send(CommunicationMessage.class, new MessageContext(p, player, MessageContext.RECIEVE_MESSAGE, message)); - } - - /** - * Adds a contact. - * @param contact The contact to add. - */ - public static void add(Player player, String contact) { - CommunicationInfo info = player.getDetails().getCommunication(); - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendContactUpdate(player.getName(), contact, false, false, null); - return; - } - if (info.contacts.size() >= MAX_LIST_SIZE) { - player.getPacketDispatch().sendMessage("Your friend list is full."); - return; - } - if (info.contacts.containsKey(contact)) { - player.getPacketDispatch().sendMessage(StringUtils.formatDisplayName(contact) + " is already on your friend list."); - return; - } - ClanRepository clan = ClanRepository.get(player.getName(), false); - if (clan != null) { - clan.rank(contact, ClanRank.FRIEND); - } - info.contacts.put(contact, new Contact(contact)); - Player target = Repository.getPlayerByName(contact); - if (target != null) { - if (showActive(player, target)) { - PacketRepository.send(ContactPackets.class, new ContactContext(player, contact, GameWorld.getSettings().getWorldId())); - } - if (player.getSettings().getPrivateChatSetting() == 1 && showActive(target, player)) { - PacketRepository.send(ContactPackets.class, new ContactContext(target, player.getName(), GameWorld.getSettings().getWorldId())); - } - } - } - - /** - * Removes a contact. - * @param contact The contact to remove. - * @param block If the contact should be removed from the block list. - */ - public static void remove(Player player, String contact, boolean block) { - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendContactUpdate(player.getName(), contact, true, block, null); - return; - } - CommunicationInfo info = player.getDetails().getCommunication(); - if (block) { - info.blocked.remove(contact); - Player target = Repository.getPlayerByName(contact); - if (target != null && hasContact(target, player.getName())) { - int worldId = 0; - if (showActive(target, player)) { - worldId = GameWorld.getSettings().getWorldId(); - } - PacketRepository.send(ContactPackets.class, new ContactContext(target, player.getName(), worldId)); - } - } else { - info.contacts.remove(contact); - ClanRepository clan = ClanRepository.get(player.getName(), false); - if (clan != null) { - clan.rank(contact, ClanRank.NONE); - } - if (player.getSettings().getPrivateChatSetting() == 1) { - Player target = Repository.getPlayerByName(contact); - if (target != null) { - PacketRepository.send(ContactPackets.class, new ContactContext(target, player.getName(), 0)); - } - } - } - } - - /** - * Adds a blocked contact. - * @param contact The contact to block. - */ - public static void block(Player player, String contact) { - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendContactUpdate(player.getName(), contact, false, true, null); - return; - } - CommunicationInfo info = player.getDetails().getCommunication(); - if (info.blocked.size() >= MAX_LIST_SIZE) { - player.getPacketDispatch().sendMessage("Your ignore list is full."); - return; - } - if (info.blocked.contains(contact)) { - player.getPacketDispatch().sendMessage(StringUtils.formatDisplayName(contact) + " is already on your ignore list."); - return; - } - info.blocked.add(contact); - Player target = Repository.getPlayerByName(contact); - if (target != null && hasContact(target, player.getName())) { - PacketRepository.send(ContactPackets.class, new ContactContext(target, player.getName(), 0)); - } - } - - /** - * Updates the clan rank of a certain contact. - * @param contact The contact. - * @param clanRank The clan rank to set. - */ - public static void updateClanRank(Player player, String contact, ClanRank clanRank) { - CommunicationInfo info = player.getDetails().getCommunication(); - Contact c = info.contacts.get(contact); - if (c == null) { - SystemLogger.logErr("Could not find contact " + contact + " to update clan rank!"); - return; - } - c.setRank(clanRank); - ClanRepository clan = ClanRepository.get(player.getName()); - if (clan != null) { - clan.rank(contact, clanRank); - } - int worldId = 0; - if (CommunicationInfo.showActive(player, contact)) { - worldId = c.getWorldId(); - } - PacketRepository.send(ContactPackets.class, new ContactContext(player, contact, worldId)); - } - - /** - * Checks if the player has the contact added. - * @param player The player. - * @param contact The contact. - * @return {@code True} if so. - */ - public static boolean hasContact(Player player, String contact) { - return player.getDetails().getCommunication().contacts.containsKey(contact); - } - - /** - * Checks if the target should be shown as online. - * @param name The target's name. - * @return {@code True} if so. - */ - public static boolean showActive(Player player, String name) { - Player p = Repository.getPlayerByName(name); - if (p != null) { - return showActive(player, p); - } - return false; - } - - /** - * Checks if the target should be shown as online. - * @param target The target. - * @return {@True} if so. - */ - public static boolean showActive(Player player, Player target) { - if (target.getName().equals(player.getName())) { - return false; - } - if (target.getCommunication().getBlocked().contains(player.getName())) { - return false; - } - switch (target.getSettings().getPrivateChatSetting()) { - case 1: - if (!hasContact(target, player.getName())) { - return false; - } - return true; - case 2: - return false; - } - return true; - } - - /** - * Gets the contacts value. - * @return The contacts. - */ - public Map getContacts() { - return contacts; - } - - /** - * Gets the blocked value. - * @return The blocked. - */ - public List getBlocked() { - return blocked; - } - - /** - * Gets the clanName value. - * @return The clanName. - */ - public String getClanName() { - return clanName; - } - - /** - * Sets the clanName value. - * @param clanName The clanName to set. - */ - public void setClanName(String clanName) { - this.clanName = clanName; - } - - /** - * Gets the currentClan value. - * @return The currentClan. - */ - public String getCurrentClan() { - return currentClan; - } - - /** - * Sets the currentClan value. - * @param currentClan The currentClan to set. - */ - @Deprecated - public void setCurrentClan(String currentClan) { - this.currentClan = currentClan; - } - - /** - * Gets the joinRequirement value. - * @return The joinRequirement. - */ - public ClanRank getJoinRequirement() { - return joinRequirement; - } - - /** - * Sets the joinRequirement value. - * @param joinRequirement The joinRequirement to set. - */ - public void setJoinRequirement(ClanRank joinRequirement) { - this.joinRequirement = joinRequirement; - } - - /** - * Gets the messageRequirement value. - * @return The messageRequirement. - */ - public ClanRank getMessageRequirement() { - return messageRequirement; - } - - /** - * Sets the messageRequirement value. - * @param messageRequirement The messageRequirement to set. - */ - public void setMessageRequirement(ClanRank messageRequirement) { - this.messageRequirement = messageRequirement; - } - - /** - * Gets the kickRequirement value. - * @return The kickRequirement. - */ - public ClanRank getKickRequirement() { - return kickRequirement; - } - - /** - * Sets the kickRequirement value. - * @param kickRequirement The kickRequirement to set. - */ - public void setKickRequirement(ClanRank kickRequirement) { - this.kickRequirement = kickRequirement; - } - - /** - * Gets the lootRequirement value. - * @return The lootRequirement. - */ - public ClanRank getLootRequirement() { - return lootRequirement; - } - - /** - * Sets the lootRequirement value. - * @param lootRequirement The lootRequirement to set. - */ - public void setLootRequirement(ClanRank lootRequirement) { - this.lootRequirement = lootRequirement; - } - - /** - * Gets the clan. - * @return the clan - */ - public ClanRepository getClan() { - return clan; - } - - /** - * Sets the clan. - * @param clan the clan to set. - */ - public void setClan(ClanRepository clan) { - this.clan = clan; - this.currentClan = clan == null ? null : clan.getOwner(); - } - - /** - * Gets the lootShare. - * @return the lootShare - */ - public boolean isLootShare() { - return lootShare; - } - - /** - * Sets the lootShare. - * @param lootShare the lootShare to set. - */ - public void setLootShare(boolean lootShare) { - this.lootShare = lootShare; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/monitor/PlayerMonitor.java b/Server/src/main/java/core/game/system/monitor/PlayerMonitor.java deleted file mode 100644 index 66ce2027f..000000000 --- a/Server/src/main/java/core/game/system/monitor/PlayerMonitor.java +++ /dev/null @@ -1,284 +0,0 @@ -package core.game.system.monitor; - -import core.cache.misc.buffer.ByteBufferUtils; -import core.game.node.entity.player.Player; - - -import java.nio.ByteBuffer; - -/** - * Handles the player monitoring. - * @author Emperor - */ -public final class PlayerMonitor { - - /** - * The public chat log. - */ - public static final int PUBLIC_CHAT_LOG = 0; - - /** - * The private chat log. - */ - public static final int PRIVATE_CHAT_LOG = 1; - - /** - * The clan chat log. - */ - public static final int CLAN_CHAT_LOG = 2; - - /** - * The IP/MAC-address log. - */ - public static final int ADDRESS_LOG = 3; - - /** - * The command log. - */ - public static final int COMMAND_LOG = 4; - - /** - * The trade log. - */ - public static final int TRADE_LOG = 5; - - /** - * The GE logs. - */ - public static final int GRAND_EXCHANGE_LOG = 6; - - /** - * The duel stake logs. - */ - public static final int DUEL_LOG = 7; - - /** - * The macro flag for a click when the client was out of focus. - */ - public static final int MF_NO_FOCUS_CLICK = 0x1; - - /** - * The networth of the player. - */ - private long networth; - - /** - * If the client is currently focused. - */ - private boolean clientFocus = true; - - /** - * How likely it is this player has used a macro. - */ - private int macroFlag; - - /** - * The duplication log. - */ - private DuplicationLog duplicationLog; - - /** - * The message monitors. - */ - private MessageLog[] logs = new MessageLog[8]; - - /** - * Constructs a new {@code PlayerMonitor} {@code Object}. - */ - public PlayerMonitor() { - logs[PUBLIC_CHAT_LOG] = new MessageLog(500); - logs[PRIVATE_CHAT_LOG] = new MessageLog(500); - logs[CLAN_CHAT_LOG] = new MessageLog(500); - logs[ADDRESS_LOG] = new MessageLog(200); - logs[COMMAND_LOG] = new MessageLog(200); - logs[TRADE_LOG] = new MessageLog(200); - logs[GRAND_EXCHANGE_LOG] = new MessageLog(500); - logs[DUEL_LOG] = new MessageLog(200); - } - - /** - * Clears the logs. - */ - public void clear() { - for (int i = 0; i < logs.length; i++) { - if (logs[i] == null) { - continue; - } - logs[i].getMessages().clear(); - } - duplicationLog.getMessages().clear(); - } - - /** - * If the player's actions should be logged. - * @return {@code True} if so. - */ - public boolean isLogActions() { - return duplicationLog != null && duplicationLog.isLoggingFlagged(); - } - - /** - * Logs the given string. - * @param string The string to log. - * @param type The log type. - */ - public void log(String string, int type) { - log(string, type, true); - } - - /** - * Logs the given string. - * @param string The string to log. - * @param type The log type. - * @param timeStamp If we should time stamp the logged string. - */ - public void log(String string, int type, boolean timeStamp) { - if (type < 3) { - String check = string.toLowerCase(); - if (check.contains("dupe") || check.contains("duping") || check.contains("bug") || check.contains("glitch") || check.contains("exploit")) { - getDuplicationLog().flag(DuplicationLog.DUPE_TALK); - String prefix = null; - if (type == PUBLIC_CHAT_LOG) { - prefix = "Public"; - } else if (type == PRIVATE_CHAT_LOG) { - prefix = "Private"; - } else { - prefix = "Clan"; - } - getDuplicationLog().log(prefix + " chat message: " + string, true); - } - } - logs[type].log(string, timeStamp); - } - - /** - * Checks the networth difference for a player (called on logout). - * @param player The player. - * @param currentNet The current networth. - */ - public void checkNetworth(Player player, long currentNet) { - long difference = currentNet - this.networth; - if (difference < 1) { // The player lost money. - return; - } - if (difference > 150_000_000l) { - getDuplicationLog().flag(DuplicationLog.NW_INCREASE); - getDuplicationLog().log("Large networth increase - [incr=" + difference + ", old=" + this.networth + ", cur=" + currentNet + "].", true); - } - } - - /** - * Called when the player does a mouse click. - * @param x The x-coordinate of the cursor. - * @param y The y-coordinate of the cursor. - * @param delay The time between this click and last. - * @param rightClick If the right mouse-button was used to click. - */ - public void handleMouseClick(int x, int y, int delay, boolean rightClick) { - if (!clientFocus) { - macroFlag |= MF_NO_FOCUS_CLICK; - } - } - - /** - * Gets the address log. - * @param original the original log. - * @param address the address. - * @return the address. - */ - public static String getAddressLog(String original, String address) { - String log = ""; - if (original != null && original.length() > 0) { - log += original; - if (log.charAt(log.length() - 1) != '|') { - log += "|"; - } - } - if (address != null && address.length() > 0 && (original == null || !original.contains(address))) { - log += address + "|"; - } - if (log.length() > 0 && log.charAt(log.length() - 1) == '|') { - log = log.substring(0, log.length() - 1); - } - if (log == null) { - log = ""; - } - return log; - } - - /** - * Adds a macro flag. - * @param flag The flag. - */ - public void addMacroFlag(int flag) { - macroFlag |= flag; - } - - /** - * Gets the macroFlag. - * @return The macroFlag. - */ - public int getMacroFlag() { - return macroFlag; - } - - /** - * Sets the macroFlag. - * @param macroFlag The macroFlag to set. - */ - public void setMacroFlag(int macroFlag) { - this.macroFlag = macroFlag; - } - - /** - * Gets the networth. - * @return The networth. - */ - public long getNetworth() { - return networth; - } - - /** - * Sets the networth. - * @param networth The networth to set. - */ - public void setNetworth(long networth) { - this.networth = networth; - } - - /** - * Gets the clientFocus. - * @return The clientFocus. - */ - public boolean isClientFocus() { - return clientFocus; - } - - /** - * Sets the clientFocus. - * @param clientFocus The clientFocus to set. - */ - public void setClientFocus(boolean clientFocus) { - this.clientFocus = clientFocus; - } - - /** - * Gets the logs. - * @return The logs. - */ - public MessageLog[] getLogs() { - return logs; - } - - /** - * Gets the duplicationLog. - * @return The duplicationLog. - */ - public DuplicationLog getDuplicationLog() { - if (duplicationLog == null) { - duplicationLog = new DuplicationLog(); - } - return duplicationLog; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/mysql/SQLEntryHandler.java b/Server/src/main/java/core/game/system/mysql/SQLEntryHandler.java deleted file mode 100644 index 3da9131de..000000000 --- a/Server/src/main/java/core/game/system/mysql/SQLEntryHandler.java +++ /dev/null @@ -1,210 +0,0 @@ -package core.game.system.mysql; - -import rs09.game.system.SystemLogger; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * Handles an SQL entry. - * @author Emperor - * @param The entry type. - */ -public abstract class SQLEntryHandler { - - /** - * The connection. - */ - protected Connection connection; - - /** - * The entry. - */ - protected T entry; - - /** - * The result set. - */ - protected ResultSet result; - - /** - * The table name. - */ - protected String table; - - /** - * The column name. - */ - private String column; - - /** - * The value to use. - */ - protected String value; - - /** - * Constructs a new {@code SQLEntryHandler} {@code Object}. - * @param entry The entry. - * @param table The table name. - * @param column column name. - * @param value The column value. - */ - public SQLEntryHandler(T entry, String table, String column, String value) { - this.entry = entry; - this.table = table; - this.column = column; - this.value = value; - } - - /** - * Reads the SQL entry. - * @param entry The entry. - */ - public static boolean read(SQLEntryHandler entry) { - if (!SQLManager.isInitialized()) { - return false; - } - entry.connection = entry.getConnection(); - if (entry.connection == null) { - SystemLogger.logErr("Could not read SQL data: connection is null!"); - return false; - } - boolean success = false; - try { - entry.read(); - if (entry.result == null || !entry.result.next()) { - entry.create(); - } else { - entry.parse(); - } - success = true; - } catch (SQLException e) { - e.printStackTrace(); - } - SQLManager.close(entry.connection); - entry.connection = null; - return success; - } - - /** - * Writes the entry data on the SQL database. - * @param entry The entry. - */ - public static void write(SQLEntryHandler entry) { - write(entry, entry.getConnection(), true); - } - - /** - * Reads the SQL entry. - * @param entry The entry. - */ - public static void write(SQLEntryHandler entry, Connection connection, boolean commit) { - if (connection == null) { - SystemLogger.logErr("Could not write SQL data: connection is null!"); - return; - } - entry.connection = connection; - try { - if (!commit) { - entry.connection.setAutoCommit(false); - } - entry.save(); - } catch (SQLException e) { - e.printStackTrace(); - } - if (commit) { - SQLManager.close(entry.connection); - } - entry.connection = null; - } - - /** - * Reads the table from the SQL database. - * @throws SQLException When an SQL exception occurs. - */ - public void read() throws SQLException { - PreparedStatement statement = connection.prepareStatement("SELECT " + getReadSelection() + " FROM " + table + " WHERE " + column + " = ?"); - statement.setString(1, value); - result = statement.executeQuery(); - } - - /** - * Gets the result. - * @return the result. - */ - public ResultSet getResult() { - return result; - } - - /** - * Gets the selection. - * @return The selection. - */ - public String getReadSelection() { - return "*"; - } - - /** - * Gets the writing statement. - * @param create If we are creating a new row. - * @param columns The columns to update. - */ - public PreparedStatement getWritingStatement(boolean create, String... columns) { - PreparedStatement statement = null; - try { - StringBuilder sb = new StringBuilder(); - if (create) { - sb.append("INSERT INTO ").append(table).append(" (").append(column); - for (String name : columns) { - sb.append(",").append(name); - } - sb.append(") VALUES (?"); - for (int i = 0; i < columns.length; i++) { - sb.append(",?"); - } - sb.append(")"); - } else { - sb.append("UPDATE ").append(table).append(" SET "); - for (int i = 0; i < columns.length; i++) { - sb.append(columns[i]).append("=?"); - if (i < columns.length - 1) { - sb.append(","); - } - } - sb.append(" WHERE ").append(column).append("=?"); - } - statement = connection.prepareStatement(sb.toString()); - statement.setString(create ? 1 : columns.length + 1, value); - } catch (SQLException e) { - e.printStackTrace(); - } - return statement; - } - - /** - * Parses the entry. - * @throws SQLException When an SQL exception occurs. - */ - public abstract void parse() throws SQLException; - - /** - * Creates a new row in the database. - * @throws SQLException When an SQL exception occurs. - */ - public abstract void create() throws SQLException; - - /** - * Saves the entry. - * @throws SQLException When an SQL exception occurs. - */ - public abstract void save() throws SQLException; - - /** - * Gets the connection. - * @return The connection. - */ - public abstract Connection getConnection(); - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/mysql/SQLManager.java b/Server/src/main/java/core/game/system/mysql/SQLManager.java deleted file mode 100644 index 6a39723f1..000000000 --- a/Server/src/main/java/core/game/system/mysql/SQLManager.java +++ /dev/null @@ -1,107 +0,0 @@ -package core.game.system.mysql; - -import rs09.ServerConstants; -import rs09.game.system.SystemLogger; -import core.game.system.SystemManager; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -/** - * Manages the SQL connections. - * @author Vexia - * - */ -public final class SQLManager { - - /** - * If the sql manager is locally hosted. Generally you never should change this. Ignore this. - */ - public static final boolean LOCAL = false; - - /** - * The database URL. - */ - public static String DATABASE_URL; - - /** - * IF the sql manager is initialized. - */ - private static boolean initialized; - - /** - * Constructs a new {@code SQLManager} {@code Object} - */ - public SQLManager() { - /** - * empty. - */ - } - - /** - * Initializes the sql manager. - */ - public static void init() { - initialized = true; - SystemManager.getSystemConfig().parse(); - } - - /** - * Pre-plugin parsing. - */ - public static void prePlugin() { - //new NPCConfigSQLHandler().parse(); - - } - - /** - * Parses data from the database for the server post plugin loading. - */ - public static void postPlugin() { - - } - - /** - * Gets a connection. - * @return The connection. - */ - public static Connection getConnection() { - DATABASE_URL = ServerConstants.DATABASE_ADDRESS + ":" + ServerConstants.DATABASE_PORT + "/" + ServerConstants.DATABASE_NAME + "?useTimezone=true&serverTimezone=UTC"; - try { - return DriverManager.getConnection("jdbc:mysql://" + DATABASE_URL, ServerConstants.DATABASE_USER, ServerConstants.DATABASE_PASS); - } catch (SQLException e) { - SystemLogger.logErr("Mysql error message=" + e.getMessage() + "."); - } - return null; - } - - /** - * Releases the connection so it's available for usage. - * @param connection The connection. - */ - public static void close(Connection connection) { - try { - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - /** - * Gets the initialized. - * @return the initialized - */ - public static boolean isInitialized() { - return initialized; - } - - /** - * Sets the bainitialized. - * @param initialized the initialized to set. - */ - public static void setInitialized(boolean initialized) { - SQLManager.initialized = initialized; - } - -} diff --git a/Server/src/main/java/core/game/system/mysql/impl/HighscoreSQLHandler.java b/Server/src/main/java/core/game/system/mysql/impl/HighscoreSQLHandler.java deleted file mode 100644 index e2d48807a..000000000 --- a/Server/src/main/java/core/game/system/mysql/impl/HighscoreSQLHandler.java +++ /dev/null @@ -1,98 +0,0 @@ -package core.game.system.mysql.impl; - -import rs09.ServerConstants; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.Rights; -import core.game.system.mysql.SQLEntryHandler; -import core.game.system.mysql.SQLManager; -import rs09.game.world.GameWorld; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -/** - * Handles the sql handler. - * @author Vexia - */ -public final class HighscoreSQLHandler extends SQLEntryHandler { - - /** - * Constructs a new {@code HighscoreSQLHandler} {@code Object}. - */ - public HighscoreSQLHandler(Player entry) { - super(entry, (SQLManager.LOCAL ? "global" : ServerConstants.DATABASE_NAME) + ".highscores", "username", entry.getName()); - } - - @Override - public void parse() throws SQLException { - - } - - @Override - public void create() throws SQLException { - if (entry.getDetails().getRights() == Rights.ADMINISTRATOR || GameWorld.getSettings().isDevMode()) { - return; - } - StringBuilder b = new StringBuilder("INSERT highscores(username,overall_xp,total_level,ironManMode,xp_0,xp_1,xp_2,xp_3,xp_4,xp_5,xp_6,xp_7,xp_8,xp_9,xp_10,xp_11,xp_12,xp_13,xp_14,xp_15,xp_16,xp_17,xp_18,xp_19,xp_20,xp_21,xp_22,xp_23) "); - b.append("VALUES('" + value + "', '" + entry.getSkills().getTotalXp() + "', '" + entry.getSkills().getTotalLevel() + "', '" + entry.getIronmanManager().getMode().name() + "', "); - int xp; - for (int i = 0; i < Skills.SKILL_NAME.length; i++) { - xp = (int) entry.getSkills().getExperience(i); - b.append("'" + xp + "'" + (i == Skills.SKILL_NAME.length - 1 ? "" : ",")); - } - b.append(")"); - PreparedStatement statement = connection.prepareStatement(b.toString()); - statement.executeUpdate(); - SQLManager.close(statement.getConnection()); - } - - @Override - public void save() throws SQLException { - if (entry.getDetails().getRights() == Rights.ADMINISTRATOR || GameWorld.getSettings().isDevMode()) { - return; - } - super.read(); - if (result == null || !result.next()) { - create(); - return; - } - if (entry.getSavedData().getActivityData().getHardcoreDeath() == true){ - //Update the SQL table to indicate the player was a Hardcore ironman that died, do not update hiscores - StringBuilder b = new StringBuilder("UPDATE highscores SET ironManMode='HARDCORE_DEAD' WHERE username ='" + value + "'"); - PreparedStatement statement = connection.prepareStatement(b.toString()); - statement.executeUpdate(); - SQLManager.close(statement.getConnection()); - return; - } - StringBuilder b = new StringBuilder("UPDATE highscores SET overall_xp='" + entry.getSkills().getTotalXp() + "', total_level='" + entry.getSkills().getTotalLevel() + "', ironManMode='" + entry.getIronmanManager().getMode().name() + "', "); - int xp; - for (int i = 0; i < Skills.SKILL_NAME.length; i++) { - xp = (int) entry.getSkills().getExperience(i); - b.append("xp_" + i + "='" + xp + "'" + (i == Skills.SKILL_NAME.length - 1 ? "" : ",")); - } - b.append("WHERE username='" + value + "'"); - PreparedStatement statement = connection.prepareStatement(b.toString()); - statement.executeUpdate(); - SQLManager.close(statement.getConnection()); - } - - /** - * Sets the data. - * @param statement the statement. - * @param startIndex the start index. - * @throws SQLException the exception. - */ - public void setData(PreparedStatement statement, int startIndex) throws SQLException { - for (int i = 0; i < Skills.SKILL_NAME.length; i++) { - statement.setInt(startIndex + i, entry.getSkills().getStaticLevel(i)); - } - } - - @Override - public Connection getConnection() { - return SQLManager.getConnection(); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/mysql/impl/PlayerLogSQLHandler.java b/Server/src/main/java/core/game/system/mysql/impl/PlayerLogSQLHandler.java deleted file mode 100644 index d0f2a53c9..000000000 --- a/Server/src/main/java/core/game/system/mysql/impl/PlayerLogSQLHandler.java +++ /dev/null @@ -1,121 +0,0 @@ -package core.game.system.mysql.impl; - -import rs09.ServerConstants; -import core.game.system.monitor.MessageLog; -import core.game.system.monitor.PlayerMonitor; -import core.game.system.mysql.SQLEntryHandler; -import core.game.system.mysql.SQLManager; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -/** - * SQL Entry handler to log player's messages, duplications, & addresses. - * @author Vexia - * - */ -public class PlayerLogSQLHandler extends SQLEntryHandler { - - /** - * The column names. - */ - private static final String[] MESSAGE_COLUMNS = new String[] { "public_chat", "private_chat", "clan_chat", "address_log", "command_log", "trade_log", "ge_log", "duel_log" }; - - /** - * Constructs a new {@Code PlayerLogSQLHandler} {@Code Object} - * @param entry the player monitor entry. - */ - public PlayerLogSQLHandler(PlayerMonitor entry, String playerName) { - super(entry, (SQLManager.LOCAL ? "global" : ServerConstants.DATABASE_NAME) + ".player_logs", "username", playerName); - } - - @Override - public void parse() throws SQLException { - } - - @Override - public void create() throws SQLException { - } - - @Override - public void save() throws SQLException { - Connection connection = getConnection(); - if (connection.prepareStatement("SELECT * FROM " + table + " WHERE username='" + value + "' LIMIT 1").executeQuery().next()) { - String b = "SET "; - int size = 0; - List columns = new ArrayList<>(20); - for (int i = 0; i < MESSAGE_COLUMNS.length; i++) { - if (!entry.getLogs()[i].getMessages().isEmpty()) { - b += MESSAGE_COLUMNS[i] + " = CONCAT(" + MESSAGE_COLUMNS[i] + ", ?)" + (i == MESSAGE_COLUMNS.length - 1 ? "" : ","); - size++; - columns.add(i); - } - } - if (!entry.getDuplicationLog().getMessages().isEmpty()) { - b += (b.charAt(b.length() - 1) != ',' ? "," : "") + "duplication_log = CONCAT(duplication_log, ?)"; - } - if (b.charAt(b.length() - 1) == ',') { - b = b.substring(0, b.length() - 1); - } - PreparedStatement statement = connection.prepareStatement("UPDATE " + table + " " + b + " WHERE username='" + value + "'"); - for (int i = 0; i < size; i++) { - writeLog(statement, 1 + i, columns.get(i)); - } - if (!entry.getDuplicationLog().getMessages().isEmpty()) { - String log = ""; - MessageLog messageLog = entry.getDuplicationLog(); - for (int i = 0; i < messageLog.getMessages().size(); i++) { - if (log.contains(messageLog.getMessages().get(i))) { - continue; - } - log += messageLog.getMessages().get(i) + "\n"; - } - statement.setString(size + 1, log); - } - //statement.executeUpdate(); - } else { - PreparedStatement statement = connection.prepareStatement("INSERT INTO " + table + " (username,public_chat,private_chat,clan_chat,address_log,command_log,trade_log,ge_log,duel_log,duplication_log) VALUES(?,?,?,?,?,?,?,?,?,?)"); - statement.setString(1, value); - for (int i = 0; i < MESSAGE_COLUMNS.length; i++) { - //writeLog(statement, 2 + i, i); - } - String log = ""; - MessageLog messageLog = entry.getDuplicationLog(); - for (int i = 0; i < messageLog.getMessages().size(); i++) { - if (log.contains(messageLog.getMessages().get(i))) { - continue; - } - log += messageLog.getMessages().get(i) + "\n"; - } - statement.setString(10, log); - //statement.executeUpdate(); - } - entry.clear(); - SQLManager.close(connection); - } - - /** - * Writes a message log to the prepared statement. - * @param statement the statement. - * @param columnIndex the column index. - * @param logIndex the log index. - * @throws SQLException the exception if thrown. - */ - private void writeLog(PreparedStatement statement, int columnIndex, int logIndex) throws SQLException { - String log = ""; - MessageLog messageLog = entry.getLogs()[logIndex]; - for (int i = 0; i < messageLog.getMessages().size(); i++) { - log += messageLog.getMessages().get(i) + "\n"; - } - statement.setString(columnIndex, log); - } - - @Override - public Connection getConnection() { - return SQLManager.getConnection(); - } - -} diff --git a/Server/src/main/java/core/game/system/mysql/impl/PlayerSQLHandler.java b/Server/src/main/java/core/game/system/mysql/impl/PlayerSQLHandler.java deleted file mode 100644 index d6acf22cd..000000000 --- a/Server/src/main/java/core/game/system/mysql/impl/PlayerSQLHandler.java +++ /dev/null @@ -1,133 +0,0 @@ -package core.game.system.mysql.impl; - -import rs09.ServerConstants; -import core.game.node.entity.player.info.PlayerDetails; -import core.game.system.mysql.SQLColumn; -import core.game.system.mysql.SQLEntryHandler; -import core.game.system.mysql.SQLManager; - -import java.math.BigInteger; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.List; - -/** - * Handles player details parsing/saving using SQL. - * @author Emperor - */ -public final class PlayerSQLHandler extends SQLEntryHandler { - - /** - * Constructs a new {@code PlayerSQLHandler} {@code Object}. - * @param entry The player details. - */ - public PlayerSQLHandler(PlayerDetails entry) { - super(entry, (SQLManager.LOCAL ? "global" : ServerConstants.DATABASE_NAME) + ".members", "username", entry.getUsername()); - } - - @Override - public void parse() throws SQLException { - for (SQLColumn column : entry.getSqlManager().getTable().getColumns()) { - if (!column.isParse()) { - continue; - } - if (column.getType() == Integer.class) { - column.setValue(result.getInt(column.getName())); - } else if (column.getType() == String.class) { - column.setValue(result.getString(column.getName())); - } else if (column.getType() == Boolean.class) { - column.setValue(result.getBoolean(column.getName())); - } else if (column.getType() == Long.class) { - column.setValue(result.getLong(column.getName())); - } else if (column.getType() == Timestamp.class) { - column.setValue(result.getTimestamp(column.getName())); - } else if (column.getType() == BigInteger.class) { - column.setValue(result.getLong(column.getName())); - } - } - } - - @Override - public String getReadSelection() { - StringBuilder selection = new StringBuilder(); - boolean first = true; - for (SQLColumn column : entry.getSqlManager().getTable().getColumns()) { - if (!first) { - selection.append(","); - } else { - first = false; - } - selection.append(column.getName()); - } - return selection.toString(); - } - - @Override - public void create() throws SQLException { - if (entry == null) { - return; - } - String[] names = new String[entry.getSqlManager().getTable().getColumns().length]; - for (int i = 0; i < entry.getSqlManager().getTable().getColumns().length; i++) { - names[i] = entry.getSqlManager().getTable().getColumns()[i].getName(); - } - PreparedStatement statement = getWritingStatement(true, names); - for (int i = 0; i < entry.getSqlManager().getTable().getColumns().length; i++) { - SQLColumn column = entry.getSqlManager().getTable().getColumns()[i]; - if (column.getType() == Integer.class) { - statement.setInt(i + 2, (int) column.getValue()); - } else if (column.getType() == String.class) { - statement.setString(i + 2, (String) column.getValue()); - } else if (column.getType() == Boolean.class) { - statement.setBoolean(i + 2, (boolean) column.getValue()); - } else if (column.getType() == Long.class) { - statement.setLong(i + 2, (Long) column.getValue()); - } else if (column.getType() == Timestamp.class) { - statement.setTimestamp(i + 2, (Timestamp) column.getValue()); - } else if (column.getType() == java.math.BigInteger.class) { - statement.setLong(i + 2, (Long) column.getValue()); - } - } - statement.executeUpdate(); - SQLManager.close(statement.getConnection()); - } - - @Override - public void save() throws SQLException { - List updated = entry.getSqlManager().getTable().getChanged(); - if (updated.isEmpty()) { - return; - } - String[] names = new String[updated.size()]; - for (int i = 0; i < names.length; i++) { - names[i] = updated.get(i).getName(); - } - PreparedStatement statement = getWritingStatement(false, names); - for (int i = 0; i < updated.size(); i++) { - SQLColumn column = updated.get(i); - if (column.getType() == Integer.class) { - statement.setInt(i + 1, (int) column.getValue()); - } else if (column.getType() == String.class) { - statement.setString(i + 1, (String) column.getValue()); - } else if (column.getType() == Boolean.class) { - statement.setBoolean(i + 1, (boolean) column.getValue()); - } else if (column.getType() == Long.class) { - statement.setLong(i + 1, (Long) column.getValue()); - } else if (column.getType() == Timestamp.class) { - statement.setTimestamp(i + 1, (Timestamp) column.getValue()); - } else if (column.getType() == BigInteger.class) { - statement.setLong(i + 1, (Long) column.getValue()); - } - } - statement.executeUpdate(); - SQLManager.close(statement.getConnection()); - } - - @Override - public Connection getConnection() { - return SQLManager.getConnection(); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/InstructionType.java b/Server/src/main/java/core/game/system/script/InstructionType.java deleted file mode 100644 index 539d23c27..000000000 --- a/Server/src/main/java/core/game/system/script/InstructionType.java +++ /dev/null @@ -1,73 +0,0 @@ -package core.game.system.script; - -/** - * Represents the instruction types. - * @author Emperor - */ -public enum InstructionType { - - /** - * The condition type. - */ - CONDITION('$'), - - /** - * The instruction type. - */ - INSTRUCTION('*'), - - /** - * The method type. - */ - METHOD('#'), - - /** - * The local method type. - */ - LOCAL_METHOD('@'), - - /** - * The go to type. - */ - GOTO('>'), - - /** - * The end bracket type. - */ - END_BRACKET('}'); - - /** - * Gets the instruction type. - * @param c The character. - * @return The instruction type. - */ - public static InstructionType forIndicator(char c) { - for (InstructionType type : values()) { - if (type.indication == c) { - return type; - } - } - return null; - } - - /** - * The character indication. - */ - private final char indication; - - /** - * Constructs a new {@code InstructionType} {@code Object}. - * @param indication The indicator. - */ - private InstructionType(char indication) { - this.indication = indication; - } - - /** - * Gets the indication. - * @return The indication character. - */ - public char getIndication() { - return indication; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/MethodCall.java b/Server/src/main/java/core/game/system/script/MethodCall.java deleted file mode 100644 index f11a0f796..000000000 --- a/Server/src/main/java/core/game/system/script/MethodCall.java +++ /dev/null @@ -1,77 +0,0 @@ -package core.game.system.script; - -/** - * Represents a method call. - * @author Emperor - */ -public final class MethodCall { - - /** - * The method name. - */ - private final String methodName; - - /** - * The arguments. - */ - private final String arguments; - - /** - * The method script. - */ - private ScriptContext script; - - /** - * Constructs a new {@code MethodCall} {@code Object}. - * @param methodName The method name. - * @param arguments The arguments. - */ - public MethodCall(String methodName, String arguments) { - this(methodName, arguments, null); - } - - /** - * Constructs a new {@code MethodCall} {@code Object}. - * @param methodName The method name. - * @param arguments The arguments. - * @param script The script context. - */ - public MethodCall(String methodName, String arguments, ScriptContext script) { - this.methodName = methodName; - this.arguments = arguments; - this.script = script; - } - - /** - * Gets the methodName. - * @return The methodName. - */ - public String getMethodName() { - return methodName; - } - - /** - * Gets the arguments. - * @return The arguments. - */ - public String getArguments() { - return arguments; - } - - /** - * Gets the script. - * @return The script. - */ - public ScriptContext getScript() { - return script; - } - - /** - * Sets the script. - * @param script The script to set. - */ - public void setScript(ScriptContext script) { - this.script = script; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/ParamCall.java b/Server/src/main/java/core/game/system/script/ParamCall.java deleted file mode 100644 index 8f054afcc..000000000 --- a/Server/src/main/java/core/game/system/script/ParamCall.java +++ /dev/null @@ -1,29 +0,0 @@ -package core.game.system.script; - -/** - * Represents a parameter call. - * @author Emperor - */ -public final class ParamCall { - - /** - * The parameter name. - */ - private final String parameter; - - /** - * Constructs a new {@code MethodParam} {@code Object}. - * @param parameter The parameter name. - */ - public ParamCall(String parameter) { - this.parameter = parameter; - } - - /** - * Gets the parameter. - * @return The parameter. - */ - public String getParameter() { - return parameter; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/ScriptBuilder.java b/Server/src/main/java/core/game/system/script/ScriptBuilder.java deleted file mode 100644 index 51e7e0d1d..000000000 --- a/Server/src/main/java/core/game/system/script/ScriptBuilder.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.system.script; - -import core.game.system.script.exc.ScriptException; - -/** - * Used for building scripts. - * @author Emperor - */ -public abstract class ScriptBuilder { - - /** - * The builder arguments. - */ - protected Object[] arguments; - - /** - * Constructs a new {@code ScriptBuilder} {@code Object}. - */ - public ScriptBuilder(Object[] arguments) { - this.arguments = arguments; - } - - /** - * Parses the builder arguments. - * @param args The arguments to parse. - * @return The builder arguments. - */ - public abstract Object[] parseArguments(String args); - - /** - * Initializes the script. - * @param context The script context. - */ - public abstract void configureScript(ScriptContext context); - - /** - * Creates a new script builder. - * @param args The arguments. - * @return The builder. - */ - public abstract ScriptBuilder create(Object... args); - - /** - * Handles a local method. - * @param name The method name. - * @param context The method script. - * @param current The current script. - */ - public abstract void handleLocalMethod(String name, ScriptContext context, ScriptContext current) throws ScriptException; - - /** - * Gets the builder arguments. - * @return The arguments. - */ - public Object[] getArguments() { - return arguments; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/ScriptCompiler.java b/Server/src/main/java/core/game/system/script/ScriptCompiler.java deleted file mode 100644 index fddc305a2..000000000 --- a/Server/src/main/java/core/game/system/script/ScriptCompiler.java +++ /dev/null @@ -1,599 +0,0 @@ -package core.game.system.script; - -import rs09.ServerConstants; -import rs09.game.system.SystemLogger; -import rs09.game.ai.AIPBuilder; -import rs09.game.ai.AIPlayer; -import core.game.system.script.exc.InvalidGOTOException; -import core.game.system.script.exc.InvalidInstructionException; -import core.game.system.script.exc.InvalidInterpreterException; -import core.game.system.script.exc.ScriptException; -import rs09.game.world.GameWorld; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.nio.file.Files; -import java.util.*; - -/** - * Used for compiling scripts. - * @author Emperor - */ -public final class ScriptCompiler { - - /** - * The current line index. - */ - private static int lineId; - - /** - * The mapping of methods. - */ - private static Map methodScripts; - - /** - * The mapping of method calls. - */ - private static Map methodCalls; - - /** - * The mapping of fields. - */ - private static Map fields; - - /** - * The mapping of field calls. - */ - @SuppressWarnings("unused") - private static Map fieldCalls; - - /** - * The current builder. - */ - private static ScriptBuilder builder; - - /** - * The instructions. - */ - private static Map instructions; - - /** - * The main method. - * @param args The arguments cast on runtime. - */ - public static void main(String... args) { - try { - GameWorld.prompt(false); - ScriptContext context = parseRaw(new File(ServerConstants.DIALOGUE_SCRIPTS_PATH + "test.asc")); - AIPlayer player = AIPBuilder.create(null); - context.execute(player); - ScriptManager.run(context, player); - } catch (Throwable e) { - e.printStackTrace(); - } - System.exit(0); - } - - /** - * Loads the instructions. - * @throws Throwable When an exception occurs. - */ - public static void loadInstructions() throws Throwable { - if (instructions != null) { - return; - } - instructions = new HashMap<>(); - for (File file : new File("core/game/system/script/context/").listFiles()) { - String name = file.getName(); - if (name.contains("$")) { - continue; - } - Class context = Class.forName("org.crandor.game.system.script.context." + name.replace(".class", "")); - Object object = context.newInstance(); - if (object instanceof ScriptContext) { - ScriptContext script = (ScriptContext) object; - instructions.put(script.getName(), script); - } - } - } - - /** - * Parses a raw script - * @param file The script file. - * @throws Throwable When an exception occurs. - */ - public static ScriptContext parseRaw(File file) throws Throwable { - loadInstructions(); - BufferedReader br = Files.newBufferedReader(file.toPath()); - lineId = -1; - String line; - builder = null; - methodScripts = new HashMap<>(); - methodCalls = new HashMap<>(); - fields = new HashMap<>(); - try { - Queue rawScript = new LinkedList<>(); - boolean first = true; - while ((line = br.readLine()) != null) { - lineId++; - StringBuilder sb = formatArgument(new StringBuilder(line)); - int commentIndex = sb.toString().indexOf("//"); - if (commentIndex > -1) { - sb.setLength(commentIndex); - } - if (first && sb.length() > 0) { - first = false; - if (line.charAt(0) == '@') { - builder = createBuilder(sb); - continue; - } - } - rawScript.add(sb.toString()); - - } - lineId = 0; - if (builder != null) { - lineId++; - } - line = rawScript.poll(); - if (line == null) { - br.close(); - throw new ScriptException("ScriptAPI " + file.getName() + " is empty!", -1); - } - br.close(); - ScriptContext context = parseScript(line, rawScript, new ScriptEntry(null, null, null, false), null); - if (context != null) { - for (ScriptContext caller : methodCalls.keySet()) { - MethodCall call = methodCalls.get(caller); - MethodCall method = methodScripts.get(call.getMethodName()); - if (method == null) { - throw new InvalidGOTOException(call.getMethodName() + "", -1); - } - ScriptContext script = method.getScript(); - if (call.getArguments() != null && call.getArguments().length() > 0) { - String[] parameterNames = getArguments(method.getArguments()); - Object[] values = getParameters(call.getArguments(), 0); - if (values.length != parameterNames.length) { - throw new IllegalArgumentException("Not enough parameters name=" + call.getMethodName() + "!"); - } - for (int i = 0; i < parameterNames.length; i++) { - fields.put(parameterNames[i], values[i]); - } - script = checkScriptParameters(script, caller, null); - } - script.setName(call.getMethodName()); - caller.setInstruction(script); - } - } - return context; - } catch (ScriptException e) { - e.printStackTrace(); - } catch (Throwable t) { - br.close(); - throw new ScriptException("Error parsing line " + lineId + "!", t); - } - br.close(); - return null; - } - - /** - * Checks for parameters. - * @param script The script to check. - * @param original The original script, to prevent infinite looping. - */ - private static ScriptContext checkScriptParameters(ScriptContext script, ScriptContext last, ScriptContext original) { - Object[] copy = null; - for (int i = 0; i < script.parameters.length; i++) { - Object object = script.parameters[i]; - if (object instanceof ParamCall) { - if (copy == null) { - copy = Arrays.copyOf(script.parameters, script.parameters.length); - } - ParamCall call = (ParamCall) object; - copy[i] = fields.get(call.getParameter()); - if (copy[i] == null) { - System.out.println("Could not find field " + call.getParameter()); - } - } - } - if (copy == null) { - copy = script.parameters; - } - ScriptContext context = script.parse(copy); - if (script == last.getCondition()) { - last.setCondition(context); - } else { - last.setInstruction(context); - } - context.setCondition(script.getCondition()); - context.setInstruction(script.getInstruction()); - if (original == null) { - original = context; - } - script = context; - if (script.getCondition() != null && script.getCondition() != original) { - checkScriptParameters(script.getCondition(), script, original); - } - if (script.getInstruction() != null && script.getInstruction() != original) { - checkScriptParameters(script.getInstruction(), script, original); - } - return script; - } - - /** - * Parses the script. - * @param line The first line the parse. - * @param rawScript The script data queue. - * @param script The main script context. - * @throws IOException When an I/O exception occurs. - */ - private static ScriptContext parseScript(String line, Queue rawScript, ScriptEntry script, ScriptContext previous) throws Throwable { - ScriptContext condition = null; - ScriptContext current = null; - InstructionType type = null; - boolean main = script.getName() == null; - stop: while (line != null) { - if (main) { - lineId++; - } - if (line.length() < 1) { - line = rawScript.poll(); - continue; - } - char c = line.charAt(0); - Object[] ctx = parse(InstructionType.forIndicator(c), line); - type = (InstructionType) ctx[0]; - String name = (String) ctx[1]; - String arguments = (String) ctx[2]; - boolean bracket = (Boolean) ctx[3]; - switch (type) { - case LOCAL_METHOD: - case METHOD: - Queue queue = new LinkedList<>(); - queue.add(Boolean.toString(bracket)); - String data; - int bracketCount = bracket ? 1 : 0; - while ((data = rawScript.poll()) != null) { - if (main) { - lineId++; - } - c = data.length() > 0 ? data.charAt(0) : 'f'; - queue.add(data); - if (data.indexOf('{') > -1) { - bracketCount++; - } - if (parse(InstructionType.forIndicator(c), data)[0] == InstructionType.END_BRACKET) { - bracketCount--; - } - if (bracketCount < 1) { - break; - } - } - queue.poll(); - ScriptContext context = parseScript(queue.poll(), queue, new ScriptEntry(null, null, name, bracket), current); - if (type == InstructionType.METHOD) { - methodScripts.put(name, new MethodCall(name, arguments, context)); - } else { - handleLocalMethod(name, context, current); - } - break; - case CONDITION: - context = getInstruction(name, arguments); - if (context == null) { - throw new InvalidInstructionException(name, lineId); - } - if (current != null) { - current.setCondition(context); - } else { - condition = context; - } - script = new ScriptEntry(context, script, null, bracket); - current = context; - break; - case INSTRUCTION: - case GOTO: - if (type == InstructionType.GOTO) { - if (name == null) { - name = arguments; - arguments = null; - } - methodCalls.put(current, new MethodCall(name, arguments)); - if (!script.isBracket() && script.getPreviousEntry() != null) { - script = script.getPreviousEntry(); - current = script.getCurrent(); - while (current.getInstruction() != null) { - current = current.getInstruction(); - } - } - break; - } else { - context = getInstruction(name, arguments); - if (context == null) { - throw new InvalidInstructionException(name + " - " + arguments, lineId); - } - } - if (script.getCurrent() == null) { - current = context; - script.setCurrent(context); - context.setCondition(condition); - condition = null; - break; - } - current.setInstruction(context); - current = context; - if (!script.isBracket() && script.getPreviousEntry() != null) { - script = script.getPreviousEntry(); - current = script.getCurrent(); - while (current.getInstruction() != null) { - current = current.getInstruction(); - } - } - break; - case END_BRACKET: - if (script.isBracket() && script.getPreviousEntry() != null) { - script = script.getPreviousEntry(); - current = script.getCurrent(); - while (current.getInstruction() != null) { - current = current.getInstruction(); - } - break; - } - break stop; - } - line = rawScript.poll(); - } - if (script.getPreviousEntry() != null) { - SystemLogger.logErr("Error parsing " + type + " at line " + lineId + ": " + script.getName() + "!"); - } - return script.getCurrent(); - } - - /** - * Handles a local method. - * @param name The name. - * @param context The method script. - * @param current The current script. - * @throws ScriptException When an exception occurs. - */ - private static void handleLocalMethod(String name, ScriptContext context, ScriptContext current) throws ScriptException { - if (builder != null) { - builder.handleLocalMethod(name, context, current); - } - } - - /** - * Gets the instruction for the given name. - * @param name The name. - * @param arguments The arguments. - * @return The script context. - */ - public static ScriptContext getInstruction(String name, String arguments) { - ScriptContext context = instructions.get(name); - if (context != null) { - context = context.parse(getParameters(arguments, 0)); - } - // ScriptContext context = builder != null ? - // builder.newInstruction(name, arguments) : null; - // if (context == null) { - // String[] args = getArguments(arguments); - // switch (name) { - // case "startactivity": - // name = args[0]; - // context = new StartActivityInstruction(name, getParameters(arguments, - // 1)); - // break; - // case "message": - // for (int i = 0; i < args.length; i++) { - // args[i] = getString(args[i]); - // } - // context = new PMessageInstruction(args); - // break; - // case "hasitem": - // Item[] items = new Item[args.length / 2]; - // for (int i = 0; i < items.length; i++) { - // items[i] = new Item(Integer.parseInt(args[i]), - // Integer.parseInt(args[i + 1])); - // } - // context = new InvItemCondition(items); - // break; - // } - // } - return context; - } - - /** - * Gets the parameters. - * @param arguments The argument line. - * @param offset The offset. - * @return The parameters. - */ - public static Object[] getParameters(String arguments, int offset) { - String[] args = getArguments(arguments); - Object[] params = new Object[args.length - offset]; - for (int i = offset; i < params.length; i++) { - Object object = null; - char c = args[i].charAt(0); - if (c == '"') { - object = getString(args[i]); - } else if (Character.isDigit(c)) { - object = Integer.parseInt(args[i]); - } else if (args[i].equals("true")) { - object = true; - } else if (args[i].equals("false")) { - object = false; - } else { - object = new ParamCall(args[i]); - } - params[i - offset] = object; - } - return params; - } - - /** - * Gets the string without the surrounding "". - * @param string The string. - * @return The string without surrounding ""s. - */ - public static String getString(String string) { - return string.substring(1, string.length() - 1); - } - - /** - * Parses the script context. - * @param type The instruction type. - * @param line The line. - * @return The script context. - */ - private static Object[] parse(InstructionType type, String line) { - int index = 1; - if (type == null) { - index = 0; - type = InstructionType.INSTRUCTION; - } - String instructionName = null; - String arguments = null; - boolean bracketUsed = false; - StringBuilder sb = new StringBuilder(); - boolean openingParenthese = false; - boolean string = false; - for (; index < line.length(); index++) { - char ch = line.charAt(index); - if (!string) { - if (ch == ':' && !openingParenthese && type == InstructionType.INSTRUCTION) { - type = InstructionType.METHOD; - instructionName = formatArgument(sb).toString(); - sb = new StringBuilder(); - continue; - } - if (ch == '(') { - openingParenthese = true; - if (type != InstructionType.METHOD) { - instructionName = formatArgument(sb).toString(); - } - sb = new StringBuilder(); - continue; - } - if (ch == ')') { - arguments = formatArgument(sb).toString(); - sb = new StringBuilder(); - continue; - } - if (ch == '{') { - if (type == InstructionType.LOCAL_METHOD) { - instructionName = formatArgument(sb).toString(); - sb = new StringBuilder(); - } - bracketUsed = true; - break; - } - } - if (ch == '"') { - string = !string; - } - sb.append(ch); - } - - if (arguments == null) { - arguments = formatArgument(sb).toString(); - } - return new Object[] { type, instructionName, arguments, bracketUsed }; - } - - /** - * Creates a builder from the arguments. - * @param sb The arguments string builder. - * @return The script builder. - * @throws InvalidInterpreterException When the interpreter could not be - * found. - */ - private static ScriptBuilder createBuilder(StringBuilder sb) throws InvalidInterpreterException { - String[] args = sb.substring(1).toString().replaceFirst(" ", ";").split(";"); - ScriptType i = ScriptType.forTag(args[0]); - if (i == null) { - throw new InvalidInterpreterException(); - } - StringBuilder s = new StringBuilder(); - for (int j = 1; j < args.length; j++) { - if (j != 1) { - s.append(";"); - } - s.append(args[j]); - } - Object[] builderArguments = i.getBuilder().parseArguments(formatArgument(s).toString()); - return i.getBuilder().create(builderArguments); - } - - /** - * Gets the arguments. - * @param argumentLine The argument line. - * @return The arguments. - */ - public static String[] getArguments(String argumentLine) { - String[] args = new String[20]; - int size = 0; - StringBuilder sb = new StringBuilder(); - boolean string = false; - for (int i = 0; i < argumentLine.length(); i++) { - char c = argumentLine.charAt(i); - if (c == '"') { - string = !string; - } - if (!string) { - if (c == ',') { - args[size++] = sb.toString(); - sb = new StringBuilder(); - if (size == args.length) { - args = Arrays.copyOf(args, args.length * 2); - } - continue; - } - if (c == ' ') { - continue; - } - } - sb.append(c); - } - if (sb.length() > 0) { - args[size++] = sb.toString(); - } - if (size == 0) { - return null; - } - return Arrays.copyOf(args, size); - } - - /** - * Formats an argument. - * @return The formatted argument. - */ - public static StringBuilder formatArgument(String string) { - return formatArgument(new StringBuilder(string)); - } - - /** - * Formats an argument. - * @param sb The argument string builder. - * @return The formatted argument. - */ - public static StringBuilder formatArgument(StringBuilder sb) { - while (sb.length() > 0 && (sb.charAt(0) == ' ' || sb.charAt(0) == ' ')) { - sb.deleteCharAt(0); - } - char c; - while (sb.length() > 0 && ((c = sb.charAt(sb.length() - 1)) == ' ' || c == ' ')) { - sb.deleteCharAt(sb.length() - 1); - } - return sb; - } - - /** - * Gets the script builder used. - * @return The script builder. - */ - public static ScriptBuilder getBuilder() { - return builder; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/ScriptContext.java b/Server/src/main/java/core/game/system/script/ScriptContext.java deleted file mode 100644 index e699c0792..000000000 --- a/Server/src/main/java/core/game/system/script/ScriptContext.java +++ /dev/null @@ -1,165 +0,0 @@ -package core.game.system.script; - -/** - * ScriptAPI context, this can be a condition or an instruction. - * @author Emperor - */ -public abstract class ScriptContext { - - /** - * The default arguments. - */ - private boolean instant = true; - - /** - * The name of this script. - */ - private String name; - - /** - * The argument line. - */ - protected Object[] parameters; - - /** - * The condition. - */ - private ScriptContext condition; - - /** - * The instruction. - */ - private ScriptContext instruction; - - /** - * Constructs a new {@code ScriptContext} {@code Object}. - * @param name The script context name. - */ - public ScriptContext(String name) { - this.name = name; - } - - /** - * Parses the parameters and creates a new script context object. - * @param params The parameters. - * @return The script context. - */ - public abstract ScriptContext parse(Object... params); - - /** - * Executes the script. - * @param args The arguments. - * @return {@code True} if successfully executed. - */ - public abstract boolean execute(Object... args); - - /** - * Creates a new instance of this script with the given argument line. - * @param args The argument line. - * @return The script context. - */ - public ScriptContext create(String args) { - ScriptContext context = ScriptCompiler.getInstruction(name, args); - if (context == null) { - return null; - } - context.condition = condition; - context.instruction = instruction; - return context; - } - - /** - * Runs the script. - * @param args The script arguments. - * @return {@code True} if successfully executed. - */ - public ScriptContext run(Object... args) { - if (condition != null && condition.execute(args)) { - return condition; - } - if (instruction != null && instruction.execute(args)) { - return instruction; - } - return null; - } - - /** - * Gets the name. - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Sets the name. - * @param name The name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Gets the condition. - * @return The condition. - */ - public ScriptContext getCondition() { - return condition; - } - - /** - * Sets the condition. - * @param condition The condition to set. - */ - public void setCondition(ScriptContext condition) { - this.condition = condition; - } - - /** - * Gets the instruction. - * @return The instruction. - */ - public ScriptContext getInstruction() { - return instruction; - } - - /** - * Sets the instruction. - * @param instruction The instruction to set. - */ - public void setInstruction(ScriptContext instruction) { - this.instruction = instruction; - } - - /** - * Gets the instant. - * @return The instant. - */ - public boolean isInstant() { - return instant; - } - - /** - * Sets the instant. - * @param instant The instant to set. - */ - public void setInstant(boolean instant) { - this.instant = instant; - } - - /** - * Gets the parameters. - * @return The parameters. - */ - public Object[] getParameters() { - return parameters; - } - - /** - * Sets the parameters. - * @param parameters The parameters to set. - */ - public void setParameters(Object[] parameters) { - this.parameters = parameters; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/ScriptEntry.java b/Server/src/main/java/core/game/system/script/ScriptEntry.java deleted file mode 100644 index 2f96c8451..000000000 --- a/Server/src/main/java/core/game/system/script/ScriptEntry.java +++ /dev/null @@ -1,104 +0,0 @@ -package core.game.system.script; - -/** - * A script compiling entry. - * @author Emperor - */ -public class ScriptEntry { - - /** - * The current script context. - */ - private ScriptContext current; - - /** - * The previous script context. - */ - private ScriptEntry previousEntry; - - /** - * The instruction name. - */ - private String name; - - /** - * If a bracket was used. - */ - private boolean bracket; - - /** - * Constructs a new {@code ScriptEntry} {@code Object}. - * @param current The current script context. - * @param previous The previous script entry. - */ - public ScriptEntry(ScriptContext current, ScriptEntry previous, String name, boolean bracket) { - this.current = current; - this.previousEntry = previous; - this.name = name; - this.bracket = bracket; - } - - /** - * Gets the current. - * @return The current. - */ - public ScriptContext getCurrent() { - return current; - } - - /** - * Sets the current. - * @param current The current to set. - */ - public void setCurrent(ScriptContext current) { - this.current = current; - } - - /** - * Gets the previous. - * @return The previous. - */ - public ScriptEntry getPreviousEntry() { - return previousEntry; - } - - /** - * Sets the previous. - * @param previous The previous to set. - */ - public void setPrevious(ScriptEntry previous) { - this.previousEntry = previous; - } - - /** - * Gets the bracket. - * @return The bracket. - */ - public boolean isBracket() { - return bracket; - } - - /** - * Sets the bracket. - * @param bracket The bracket to set. - */ - public void setBracket(boolean bracket) { - this.bracket = bracket; - } - - /** - * Gets the name. - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Sets the name. - * @param name The name to set. - */ - public void setName(String name) { - this.name = name; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/ScriptManager.java b/Server/src/main/java/core/game/system/script/ScriptManager.java deleted file mode 100644 index b86fdf0be..000000000 --- a/Server/src/main/java/core/game/system/script/ScriptManager.java +++ /dev/null @@ -1,116 +0,0 @@ -package core.game.system.script; - -import rs09.ServerConstants; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; - -import java.io.File; - -/** - * A class used to manage the loading of scripts. - * @author 'Vexia - */ -public final class ScriptManager { - - /** - * The amount of scripts loaded. - */ - private static int amount; - - /** - * Method used to load the script manager. - * @param args the arguments. - */ - public static void main(String... args) { - load(); - } - - /** - * Runs the script and returns the current script context after executing. - * @param context The script to run. - * @param args The arguments. - * @return The last script context executed. - */ - public static ScriptContext run(ScriptContext context, Object... args) { - ScriptContext ctx = context; - do { - ctx = ctx.run(args); - } while (ctx != null && ctx.isInstant()); - return ctx; - } - - /** - * Runs the script and returns the current script context after executing. - * @param context The script to run. - * @param name The method name. - * @return The method script context. - */ - public static ScriptContext getMethod(ScriptContext context, String name) { - ScriptContext ctx = context; - while (ctx != null) { - if (ctx.getCondition() != null) { - context = getMethod(ctx.getCondition(), name); - if (context != null) { - return context; - } - } - if (ctx.getName().equals(name)) { - // System.out.println(ctx + ", " + - // Arrays.toString(ctx.getParameters())); - return ctx; - } - ctx = ctx.getInstruction(); - } - return null; - } - - /** - * Initiates the chain reaction of script loading. - */ - public static void load() { - amount = 0; - if(ServerConstants.SCRIPTS_PATH != null) - load(new File(ServerConstants.SCRIPTS_PATH)); - SystemLogger.logInfo("Parsed " + amount + " " + GameWorld.getSettings().getName() + " script" + (amount == 1 ? "" : "s") + "..."); - } - - /** - * Method used to load a script by its path. - * @param path the path. - */ - public static void load(final String path) { - load(new File(path)); - } - - /** - * Loads scripts from a directory. - * @param directory the directory. - * @throws Throwable the throwable. - */ - public static void load(final File directory) { - try { - for (File file : directory.listFiles()) { - if (file.getName().equals(".DS_Store")) { - continue; - } - //Files to EXCLUDE from loading - if (file.getName().equals("simon_templeton.asc") || file.getName().equals("ardougne_baker.asc") || file.getName().equals("anna_isaakson.asc")){ - continue; - } - if (file.isDirectory()) { - load(file); - continue; - } -// ScriptContext context = ScriptCompiler.parseRaw(file); -// if (ScriptCompiler.getBuilder() != null) { -// ScriptCompiler.getBuilder().configureScript(context); -// } - amount++; - } - } catch (Throwable e) { - e.printStackTrace(); - SystemLogger.logErr("Error loading at directory - " + directory + "!"); - } - } - -} diff --git a/Server/src/main/java/core/game/system/script/ScriptType.java b/Server/src/main/java/core/game/system/script/ScriptType.java deleted file mode 100644 index 9349c4631..000000000 --- a/Server/src/main/java/core/game/system/script/ScriptType.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.system.script; - -import core.game.system.script.build.DialogueScriptBuilder; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents the information for a script type. - * @author 'Vexia - */ -public enum ScriptType { - - DIALOGUE("dialogue", new DialogueScriptBuilder(null)); - - /** - * The mapping of the script informations. - */ - private static final Map INFOS = new HashMap<>(); - - /** - * The tag of the script info. - */ - private final String tag; - - /** - * The script builder. - */ - private final ScriptBuilder builder; - - /** - * Constructs a new {@code ScriptInfo} {@code Object}. - * @param tag the tag. - */ - private ScriptType(String tag, ScriptBuilder builder) { - this.tag = tag; - this.builder = builder; - } - - /** - * Gets the script info by the tag. - * @param tag the tag. - * @return the tag. - */ - public static ScriptType forTag(final String tag) { - ScriptType info = INFOS.get(tag); - if (info == null) { - throw new NullPointerException("Error! Unknown script type for tag - " + tag + "."); - } - return info; - } - - /** - * Gets the tag. - * @return The tag. - */ - public String getTag() { - return tag; - } - - /** - * Gets the builder. - * @return The builder. - */ - public ScriptBuilder getBuilder() { - return builder; - } - - /** - * static-block for populating map. - */ - static { - for (ScriptType info : values()) { - INFOS.put(info.getTag(), info); - } - } - -} diff --git a/Server/src/main/java/core/game/system/script/build/DialogueScriptBuilder.java b/Server/src/main/java/core/game/system/script/build/DialogueScriptBuilder.java deleted file mode 100644 index 2d492ec57..000000000 --- a/Server/src/main/java/core/game/system/script/build/DialogueScriptBuilder.java +++ /dev/null @@ -1,99 +0,0 @@ -package core.game.system.script.build; - -import core.game.content.dialogue.DialogueInterpreter; -import core.game.system.script.ScriptBuilder; -import core.game.system.script.ScriptCompiler; -import core.game.system.script.ScriptContext; -import core.game.system.script.context.OptionDialInstruction; -import core.game.system.script.exc.InvalidInstructionException; -import core.game.system.script.exc.ScriptException; - -import java.util.Arrays; - -/** - * Handles dialogue script building. - * @author Emperor - */ -public final class DialogueScriptBuilder extends ScriptBuilder { - - /** - * Constructs a new {@code DialogueScriptBuilder} {@code Object}. - * @param args The builder arguments. - */ - public DialogueScriptBuilder(Object[] args) { - super(args); - } - - @Override - public void configureScript(ScriptContext context) { - if (arguments[0] instanceof String) { - for (int i = 0; i < arguments.length; i++) { - DialogueInterpreter.add(DialogueInterpreter.getDialogueKey((String) arguments[i]), context); - } - return; - } - for (int i = 0; i < arguments.length; i++) { - DialogueInterpreter.add((Integer) arguments[i], context); - } - } - - @Override - public ScriptBuilder create(Object... args) { - return new DialogueScriptBuilder(args); - } - - @Override - public Object[] parseArguments(String args) { - Object[] objects = null; - if (args.startsWith("npc:")) { - args = args.replace("npc:", "").replaceAll(" ", ""); - String[] str = args.split(","); - objects = new Object[str.length]; - for (int i = 0; i < str.length; i++) { - objects[i] = Integer.parseInt(str[i]); - } - } else if (args.startsWith("type:")) { - args = args.replace("npc:", ""); - objects = new Object[20]; - int length = 0; - StringBuilder sb = new StringBuilder(); - boolean creating = false; - for (int i = 0; i < args.length(); i++) { - char c = args.charAt(i); - if (c == '"') { - if (creating && sb.length() > 0) { - objects[length++] = sb.toString(); - sb = new StringBuilder(); - } - creating = !creating; - continue; - } - if (creating) { - sb.append(c); - } - } - objects = Arrays.copyOf(objects, length); - } - return objects; - } - - @Override - public void handleLocalMethod(String name, ScriptContext context, ScriptContext current) throws ScriptException { - if (name.startsWith("option")) { - if (!(current instanceof OptionDialInstruction)) { - throw new InvalidInstructionException("Can't set local method option for instruction " + current + "!", -1); - } - OptionDialInstruction ins = (OptionDialInstruction) current; - StringBuilder sb = new StringBuilder(name.substring("option".length())); - int index = Integer.parseInt(ScriptCompiler.formatArgument(sb).toString()); - index -= 1; - if (index < 0 || index >= ins.getOptionHandlers().length) { - throw new IllegalArgumentException("Index out of bounds - " + index + "!"); - } - // System.out.println("Setting index " + index + " (" + name + "), " - // + context); - ins.getOptionHandlers()[index] = context; - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/AddItemInstruction.java b/Server/src/main/java/core/game/system/script/context/AddItemInstruction.java deleted file mode 100644 index 5db1e1035..000000000 --- a/Server/src/main/java/core/game/system/script/context/AddItemInstruction.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.system.script.ScriptContext; - -/** - * Used for adding an item to the inventory. - * @author 'Vexia - */ -public final class AddItemInstruction extends ScriptContext { - - /** - * The id of the item. - */ - private final int id; - - /** - * The amount to add. - */ - private final int amount; - - /** - * Constructs a new {@code PauseInstruction} {@code Object}. - */ - public AddItemInstruction() { - this(-1, -1); - } - - /** - * Constructs a new {@code AddItemInstruction} {@code Object}. - * @param id the id. - * @param amount the amount. - */ - public AddItemInstruction(int id, int amount) { - super("additem"); - this.id = id; - this.amount = amount; - } - - @Override - public ScriptContext parse(Object... params) { - int id = 0; - int amount = 0; - if (params[0] instanceof Integer) { - id = (Integer) params[0]; - } - if (params[1] instanceof Integer) { - amount = (Integer) params[1]; - } - AddItemInstruction context = new AddItemInstruction(id, amount); - context.parameters = params; - return context; - } - - @Override - public boolean execute(final Object... args) { - final Player player = (Player) args[0]; - player.getInventory().add(new Item(id, amount), player); - return true; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/CheckRandomCondition.java b/Server/src/main/java/core/game/system/script/context/CheckRandomCondition.java deleted file mode 100644 index 0a69683a5..000000000 --- a/Server/src/main/java/core/game/system/script/context/CheckRandomCondition.java +++ /dev/null @@ -1,50 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.Entity; -import core.game.system.script.ScriptContext; - -/** - * Used to check current random value (set by setrandom(value) instruction). - * @author Emperor - */ -public final class CheckRandomCondition extends ScriptContext { - - /** - * Sets the value. - */ - private final int value; - - /** - * Constructs a new {@code CheckRandomCondition} {@code Object}. - */ - public CheckRandomCondition() { - this(0); - } - - /** - * Constructs a new {@code CheckRandomCondition} {@code Object}. - * @param value The value. - */ - public CheckRandomCondition(int value) { - super("israndom"); - this.value = value; - } - - @Override - public ScriptContext parse(Object... params) { - int value = 0; - if (params[0] instanceof Integer) { - value = (Integer) params[0]; - } - CheckRandomCondition context = new CheckRandomCondition(value); - context.parameters = params; - return context; - } - - @Override - public boolean execute(Object... args) { - System.out.println("CheckRandomCondition:" + (String) ((Entity) args[0]).getAttribute("asc_random") ); - return ((Entity) args[0]).getAttribute("asc_random", 0) == value; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/InvItemCondition.java b/Server/src/main/java/core/game/system/script/context/InvItemCondition.java deleted file mode 100644 index 9d4848bd3..000000000 --- a/Server/src/main/java/core/game/system/script/context/InvItemCondition.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.system.script.ScriptContext; - -import java.util.Arrays; - -/** - * ScriptAPI condition for checking if a player has an item in the inventory. - * @author Emperor - */ -public final class InvItemCondition extends ScriptContext { - - /** - * The items required. - */ - private final Item[] items; - - /** - * Constructs a new {@code InvItemCondition} {@code Object}. - */ - public InvItemCondition() { - this(null); - } - - /** - * Constructs a new {@code InvItemCondition} {@code Object}. - * @param items the items required. - */ - public InvItemCondition(Item[] items) { - super("hasitem"); - this.items = items; - } - - @Override - public boolean execute(Object... args) { - return ((Player) args[0]).getInventory().containsItems(items); - } - - @Override - public ScriptContext parse(Object... params) { - Item[] items = new Item[params.length / 2]; - int index = 0; - for (int i = 0; i < params.length; i++) { - if (params[i] instanceof Integer) { - items[index++] = new Item((Integer) params[i], (Integer) params[i + 1]); - i++; - } - } - if (index != items.length) { - items = Arrays.copyOf(items, index); - } - ScriptContext context = new InvItemCondition(items); - context.setParameters(params); - return context; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/ItemMessageInstruction.java b/Server/src/main/java/core/game/system/script/context/ItemMessageInstruction.java deleted file mode 100644 index 1c2c3def5..000000000 --- a/Server/src/main/java/core/game/system/script/context/ItemMessageInstruction.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import core.game.system.script.ScriptContext; - -/** - * Handles a send item message to a player. - * @author 'Vexia - */ -public class ItemMessageInstruction extends ScriptContext { - - /** - * The item id. - */ - private final int id; - - /** - * The message to send. - */ - private final String message; - - /** - * Constructs a new {@code PlainMessageInstruction} {@code Object}. - */ - public ItemMessageInstruction() { - this(-1, null); - } - - /** - * Constructs a new {@code PlainMessageInstruction} {@code Object}. - */ - public ItemMessageInstruction(int id, final String message) { - super("itemmessage"); - super.setInstant(false); - this.id = id; - this.message = message; - } - - @Override - public boolean execute(Object... args) { - Player player = (Player) args[0]; - player.getDialogueInterpreter().sendItemMessage(id, message); - player.getDialogueInterpreter().setDialogueStage(this); - return true; - } - - @Override - public ScriptContext parse(Object... params) { - int id = (Integer) params[0]; - String message = (String) params[1]; - ItemMessageInstruction context = new ItemMessageInstruction(id, message); - context.parameters = params; - return context; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/NPCDialInstruction.java b/Server/src/main/java/core/game/system/script/context/NPCDialInstruction.java deleted file mode 100644 index 7a3984a52..000000000 --- a/Server/src/main/java/core/game/system/script/context/NPCDialInstruction.java +++ /dev/null @@ -1,124 +0,0 @@ -package core.game.system.script.context; - -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.system.script.ParamCall; -import core.game.system.script.ScriptCompiler; -import core.game.system.script.ScriptContext; - -import java.util.Arrays; - -/** - * The NPC dialogue instruction. - * @author Emperor - */ -public final class NPCDialInstruction extends ScriptContext { - - /** - * The messages. - */ - private final String[] messages; - - /** - * The facial expression. - */ - private final int expression; - - /** - * If the continue option should be hidden. - */ - private boolean hideContinue; - - /** - * The NPC id to show for this dialogue. - */ - private int forceNPCId; - - /** - * Constructs a new {@code PDialInstruction} {@code Object}. - */ - public NPCDialInstruction() { - this(null, -1); - } - - /** - * Constructs a new {@code PDialInstruction} {@code Object}. - * @param messages The messages. - * @param expression The facial expression animation id. - */ - public NPCDialInstruction(String[] messages, int expression) { - super("npc"); - super.setInstant(false); - this.messages = messages; - this.expression = expression; - } - - @Override - // Arguments should be [player, npc]! - public boolean execute(Object... args) { - Player player = (Player) args[0]; // 1 = NPC - int npcId = -1; - if (args[1] instanceof NPC) { - npcId = ((NPC) args[1]).getId(); - } else { - npcId = (Integer) args[1]; - } - String[] messages = new String[this.messages.length]; - for (int i = 0; i < messages.length; i++) { - String message = this.messages[i]; - if (message.contains(">playername<")) { - message = message.replace(">playername<", player.getUsername()); - } - messages[i] = message; - } - player.getDialogueInterpreter().sendDialogues(forceNPCId == -1 ? npcId : forceNPCId, expression, hideContinue, messages); - player.getDialogueInterpreter().setDialogueStage(this); - return true; - } - - @Override - public ScriptContext parse(Object... params) { - String[] messages = new String[6]; - int messageIndex = 0; - int expression = FacialExpression.HALF_GUILTY.getAnimationId(); - int npcId = -1; - boolean hide = false; - for (int i = 0; i < params.length; i++) { - Object o = params[i]; - if (o instanceof ParamCall) { - String param = ((ParamCall) o).getParameter(); - if (param.startsWith("anim:")) { - String expr = ScriptCompiler.formatArgument(param.substring("anim:".length())).toString(); - FacialExpression exp = null; - for (FacialExpression e : FacialExpression.values()) { - if (e.name().equals(expr)) { - exp = e; - break; - } - } - if (exp != null) { - expression = exp.getAnimationId(); - } else { - expression = Integer.parseInt(expr); - } - } else if (param.startsWith("hidecont:")) { - hide = Boolean.parseBoolean(ScriptCompiler.formatArgument(param.substring("hidecont:".length())).toString()); - } else if (param.startsWith("npc:")) { - npcId = Integer.parseInt(ScriptCompiler.formatArgument(param.substring("npc:".length())).toString()); - } - } else if (o instanceof String) { - messages[messageIndex++] = (String) o; - } - } - if (messageIndex != messages.length) { - messages = Arrays.copyOf(messages, messageIndex); - } - NPCDialInstruction context = new NPCDialInstruction(messages, expression); - context.forceNPCId = npcId; - context.hideContinue = hide; - context.parameters = params; - return context; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/OptionDialInstruction.java b/Server/src/main/java/core/game/system/script/context/OptionDialInstruction.java deleted file mode 100644 index edbf104b0..000000000 --- a/Server/src/main/java/core/game/system/script/context/OptionDialInstruction.java +++ /dev/null @@ -1,136 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; -import core.game.system.script.ParamCall; -import core.game.system.script.ScriptCompiler; -import core.game.system.script.ScriptContext; - -import java.util.Arrays; - -/** - * Sends an option dialogue. - * @author Emperor - */ -public final class OptionDialInstruction extends ScriptContext { - - /** - * The title. - */ - private final String title; - - /** - * The options. - */ - private final String[] options; - - /** - * The option handlers. - */ - private final ScriptContext[] optionHandlers; - - /** - * Constructs a new {@code OptionDialInstruction} {@code Object}. - */ - public OptionDialInstruction() { - this(null); - } - - /** - * Constructs a new {@code OptionDialInstruction} {@code Object}. - * @param title The title. - * @param options The options. - */ - public OptionDialInstruction(String title, String... options) { - super("option"); - super.setInstant(false); - this.title = title; - this.options = options; - this.optionHandlers = new ScriptContext[options.length]; - } - - @Override - public boolean execute(Object... args) { - Player player = (Player) args[0]; - player.getDialogueInterpreter().sendOptions(title, options); - player.getDialogueInterpreter().setDialogueStage(this); - return true; - } - - @Override - public ScriptContext parse(Object... params) { - String[] messages = new String[params.length]; - int messageIndex = 0; - String title = null; - for (int i = 0; i < params.length; i++) { - Object o = params[i]; - if (o instanceof String) { - messages[messageIndex++] = (String) o; - } else if (o instanceof ParamCall) { - String param = ((ParamCall) o).getParameter(); - if (param.startsWith("title:")) { - title = ScriptCompiler.formatArgument(param.substring("title:".length())).toString(); - } else { - messages[messageIndex++] = param; - } - } - } - if (messageIndex != messages.length) { - messages = Arrays.copyOf(messages, messageIndex); - } - OptionDialInstruction context = new OptionDialInstruction(title, messages); - for (int i = 0; i < context.optionHandlers.length; i++) { - if (i < optionHandlers.length) { - context.optionHandlers[i] = optionHandlers[i]; - } - } - context.parameters = params; - return context; - } - - @Override - public ScriptContext create(String args) { - System.out.println("OptionDialInstruction: " + args); - ScriptContext context = super.create(args); - - if (context != null) { - OptionDialInstruction other = (OptionDialInstruction) context; - for (int i = 0; i < other.optionHandlers.length; i++) { - if (i >= optionHandlers.length) { - break; - } - other.optionHandlers[i] = optionHandlers[i]; - } - } - return context; - } - - @Override - public ScriptContext run(Object... args) { - if (args.length < 3) { - return null; - } - int option = (Integer) args[2] - 1; - if (option > optionHandlers.length -1) { - SystemLogger.logErr("Error! Option out of bounds for option handlers."); - return null; - } - ScriptContext context = optionHandlers[option]; - if (context == null) { - SystemLogger.logErr("No option handler found!"); - ((Player) args[0]).getDialogueInterpreter().close(); - return null; - } - context.execute(args); - return context; - } - - /** - * Gets the optionHandlers. - * @return The optionHandlers. - */ - public ScriptContext[] getOptionHandlers() { - return optionHandlers; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/PDialInstruction.java b/Server/src/main/java/core/game/system/script/context/PDialInstruction.java deleted file mode 100644 index 71c0185a6..000000000 --- a/Server/src/main/java/core/game/system/script/context/PDialInstruction.java +++ /dev/null @@ -1,110 +0,0 @@ -package core.game.system.script.context; - -import core.game.content.dialogue.FacialExpression; -import core.game.node.entity.player.Player; -import core.game.system.script.ParamCall; -import core.game.system.script.ScriptCompiler; -import core.game.system.script.ScriptContext; - -import java.util.Arrays; - -/** - * Player dialogue instruction. - * @author Emperor - */ -public class PDialInstruction extends ScriptContext { - - /** - * The messages. - */ - private final String[] messages; - - /** - * The facial expression. - */ - private final int expression; - - /** - * If the continue option should be hidden. - */ - private boolean hideContinue; - - /** - * Constructs a new {@code PDialInstruction} {@code Object}. - */ - public PDialInstruction() { - this(null, -1); - } - - /** - * Constructs a new {@code PDialInstruction} {@code Object}. - * @param messages The messages. - * @param expression The facial expression animation id. - */ - public PDialInstruction(String[] messages, int expression) { - super("player"); - super.setInstant(false); - this.messages = messages; - this.expression = expression; - } - - @Override - // Arguments should be [player, npc]! - public boolean execute(Object... args) { - Player player = (Player) args[0]; // 1 = NPC - String[] messages = new String[this.messages.length]; - for (int i = 0; i < messages.length; i++) { - String message = this.messages[i]; - if (message.contains(">playername<")) { - message = message.replace(">playername<", player.getUsername()); - } - messages[i] = message; - } - player.getDialogueInterpreter().sendDialogues(player, expression, hideContinue, messages); - player.getDialogueInterpreter().setDialogueStage(this); - return true; - } - - @Override - public ScriptContext parse(Object... params) { - String[] messages = new String[6]; - int messageIndex = 0; - int expression = FacialExpression.HALF_GUILTY.getAnimationId(); - boolean hide = false; - for (int i = 0; i < params.length; i++) { - Object o = params[i]; - if (o instanceof ParamCall) { - String param = ((ParamCall) o).getParameter(); - if (param.startsWith("anim:")) { - String expr = ScriptCompiler.formatArgument(param.substring("anim:".length())).toString(); - FacialExpression exp = null; - for (FacialExpression e : FacialExpression.values()) { - if (e.name().equals(expr)) { - exp = e; - break; - } - } - if (exp != null) { - expression = exp.getAnimationId(); - } else { - expression = Integer.parseInt(expr); - } - } else if (param.startsWith("hidecont:")) { - hide = Boolean.parseBoolean(ScriptCompiler.formatArgument(param.substring("hidecont:".length())).toString()); - } else { - messages[messageIndex++] = param; - } - } else if (o instanceof String) { - messages[messageIndex++] = (String) o; - } - } - if (messageIndex != messages.length) { - messages = Arrays.copyOf(messages, messageIndex); - } - PDialInstruction context = new PDialInstruction(messages, expression); - context.hideContinue = hide; - context.parameters = params; - return context; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/PMessageInstruction.java b/Server/src/main/java/core/game/system/script/context/PMessageInstruction.java deleted file mode 100644 index 0638bfbc8..000000000 --- a/Server/src/main/java/core/game/system/script/context/PMessageInstruction.java +++ /dev/null @@ -1,66 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import core.game.system.script.ScriptContext; - -import java.util.Arrays; - -/** - * Handles a send message to player instruction. - * @author Emperor - */ -public class PMessageInstruction extends ScriptContext { - - /** - * The messages to send. - */ - private final String[] messages; - - /** - * Constructs a new {@code PMessageInstruction} {@code Object}. - */ - public PMessageInstruction() { - this(null); - } - - /** - * Constructs a new {@code PMessageInstruction} {@code Object}. - * @param messages The messages. - */ - public PMessageInstruction(String[] messages) { - super("message"); - this.messages = messages; - } - - @Override - public boolean execute(Object... args) { - Player player = (Player) args[0]; - for (String s : messages) { - player.getPacketDispatch().sendMessage(s); - } - return true; - } - - @Override - public String toString() { - return Arrays.toString(messages); - } - - @Override - public ScriptContext parse(Object... params) { - String[] messages = new String[params.length]; - int messageIndex = 0; - for (int i = 0; i < params.length; i++) { - Object o = params[i]; - if (o instanceof String) { - messages[messageIndex++] = (String) o; - } - } - if (messageIndex != messages.length) { - messages = Arrays.copyOf(messages, messageIndex); - } - PMessageInstruction context = new PMessageInstruction(messages); - context.parameters = params; - return context; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/PauseInstruction.java b/Server/src/main/java/core/game/system/script/context/PauseInstruction.java deleted file mode 100644 index d36c86301..000000000 --- a/Server/src/main/java/core/game/system/script/context/PauseInstruction.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.system.script.context; - -import core.game.system.script.ScriptContext; -import core.game.system.script.ScriptManager; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; - -/** - * Used for pausing the script. - * @author Emperor - */ -public final class PauseInstruction extends ScriptContext { - - /** - * The amount of ticks to pause for. - */ - private final int ticks; - - /** - * Constructs a new {@code PauseInstruction} {@code Object}. - */ - public PauseInstruction() { - this(-1); - } - - /** - * Constructs a new {@code PauseInstruction} {@code Object}. - * @param ticks The amount of ticks to pause for. - */ - public PauseInstruction(int ticks) { - super("pause"); - super.setInstant(false); - this.ticks = ticks; - } - - @Override - public ScriptContext parse(Object... params) { - int ticks = -1; - if (params[0] instanceof Integer) { - ticks = (Integer) params[0]; - } - PauseInstruction context = new PauseInstruction(ticks); - context.parameters = params; - return context; - } - - @Override - public boolean execute(final Object... args) { - GameWorld.getPulser().submit(new Pulse(ticks) { - @Override - public boolean pulse() { - ScriptManager.run(PauseInstruction.this, args); - return true; - } - }); - return true; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/PlainMessageInstruction.java b/Server/src/main/java/core/game/system/script/context/PlainMessageInstruction.java deleted file mode 100644 index 8f67fb836..000000000 --- a/Server/src/main/java/core/game/system/script/context/PlainMessageInstruction.java +++ /dev/null @@ -1,66 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import core.game.system.script.ScriptContext; - -import java.util.Arrays; - -/** - * Handles a send dialogue plain message to a player. - * @author Emperor - */ -public class PlainMessageInstruction extends ScriptContext { - - /** - * The messages to send. - */ - private final String[] messages; - - /** - * Constructs a new {@code PlainMessageInstruction} {@code Object}. - */ - public PlainMessageInstruction() { - this(null); - } - - /** - * Constructs a new {@code PlainMessageInstruction} {@code Object}. - * @param messages the messages. - */ - public PlainMessageInstruction(String[] messages) { - super("plainmessage"); - super.setInstant(false); - this.messages = messages; - } - - @Override - public boolean execute(Object... args) { - Player player = (Player) args[0]; - player.getDialogueInterpreter().sendDialogue(messages); - player.getDialogueInterpreter().setDialogueStage(this); - return true; - } - - @Override - public String toString() { - return Arrays.toString(messages); - } - - @Override - public ScriptContext parse(Object... params) { - String[] messages = new String[params.length]; - int messageIndex = 0; - for (int i = 0; i < params.length; i++) { - Object o = params[i]; - if (o instanceof String) { - messages[messageIndex++] = (String) o; - } - } - if (messageIndex != messages.length) { - messages = Arrays.copyOf(messages, messageIndex); - } - PlainMessageInstruction context = new PlainMessageInstruction(messages); - context.parameters = params; - return context; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/RemoveItemInstruction.java b/Server/src/main/java/core/game/system/script/context/RemoveItemInstruction.java deleted file mode 100644 index 01c8b9bb3..000000000 --- a/Server/src/main/java/core/game/system/script/context/RemoveItemInstruction.java +++ /dev/null @@ -1,66 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.system.script.ScriptContext; - -/** - * Used for removing an item to the inventory. - * @author 'Vexia - */ -public final class RemoveItemInstruction extends ScriptContext { - - /** - * The id of the item. - */ - private final int id; - - /** - * The amount to add. - */ - private final int amount; - - /** - * Constructs a new {@code RemoveItemInstruction} {@code Object}. - */ - public RemoveItemInstruction() { - this(-1, -1); - } - - /** - * Constructs a new {@code AddItemInstruction} {@code Object}. - * @param id the id. - * @param amount the amount. - */ - public RemoveItemInstruction(int id, int amount) { - super("removeitem"); - this.id = id; - this.amount = amount; - } - - @Override - public ScriptContext parse(Object... params) { - int id = 0; - int amount = 0; - if (params[0] instanceof Integer) { - id = (Integer) params[0]; - } - if (params[1] instanceof Integer) { - amount = (Integer) params[1]; - } - RemoveItemInstruction context = new RemoveItemInstruction(id, amount); - context.parameters = params; - return context; - } - - @Override - public boolean execute(final Object... args) { - final Player player = (Player) args[0]; - final Item item = new Item(id, amount); - if (!player.getInventory().containsItem(item)) { - return false; - } - return player.getInventory().remove(item); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/SetAttributeInstruction.java b/Server/src/main/java/core/game/system/script/context/SetAttributeInstruction.java deleted file mode 100644 index 0df61c6dd..000000000 --- a/Server/src/main/java/core/game/system/script/context/SetAttributeInstruction.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import core.game.system.script.ScriptContext; - -/** - * Sets an attribute. - * @author Emperor - */ -public final class SetAttributeInstruction extends ScriptContext { - - /** - * The attribute key - */ - private final String key; - - /** - * The object. - */ - private final Object value; - - /** - * Constructs a new {@code PMessageInstruction} {@code Object}. - */ - public SetAttributeInstruction() { - this(null, null); - } - - /** - * Constructs a new {@code SetAttributeInstruction} {@code Object}. - * @param key The attribute key. - * @param value The value. - */ - public SetAttributeInstruction(String key, Object value) { - super("setattribute"); - this.key = key; - this.value = value; - } - - @Override - public boolean execute(Object... args) { - Player player = (Player) args[0]; - player.setAttribute(key, value); - return true; - } - - @Override - public ScriptContext parse(Object... params) { - String key = null; - Object value = null; - for (int i = 0; i < params.length; i++) { - Object o = params[i]; - if (o instanceof String && key == null) { - key = (String) o; - } else { - value = o; - } - } - SetAttributeInstruction context = new SetAttributeInstruction(key, value); - context.parameters = params; - return context; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/SetRandomInstruction.java b/Server/src/main/java/core/game/system/script/context/SetRandomInstruction.java deleted file mode 100644 index da4040a28..000000000 --- a/Server/src/main/java/core/game/system/script/context/SetRandomInstruction.java +++ /dev/null @@ -1,51 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.Entity; -import core.game.system.script.ScriptContext; -import core.tools.RandomFunction; - -/** - * Sets a random value. - * @author Emperor - */ -public final class SetRandomInstruction extends ScriptContext { - - /** - * Sets the value. - */ - private final int value; - - /** - * Constructs a new {@code SetRandomInstruction} {@code Object}. - */ - public SetRandomInstruction() { - this(1); - } - - /** - * Constructs a new {@code SetRandomInstruction} {@code Object}. - * @param value The value. - */ - public SetRandomInstruction(int value) { - super("setrandom"); - this.value = value; - } - - @Override - public ScriptContext parse(Object... params) { - int value = 0; - if (params[0] instanceof Integer) { - value = (Integer) params[0]; - } - SetRandomInstruction context = new SetRandomInstruction(value); - context.parameters = params; - return context; - } - - @Override - public boolean execute(Object... args) { - ((Entity) args[0]).setAttribute("asc_random", RandomFunction.randomize(value)); - return true; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/ShopInstruction.java b/Server/src/main/java/core/game/system/script/context/ShopInstruction.java deleted file mode 100644 index 3c2cf54d7..000000000 --- a/Server/src/main/java/core/game/system/script/context/ShopInstruction.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.system.script.ScriptContext; -import core.game.world.map.RegionManager; - -/** - * Handles the opening of a shop instruction. - * @author 'Vexia - */ -public class ShopInstruction extends ScriptContext { - - /** - * The id to open. - */ - private int id; - - /** - * Constructs a new {@code ShopInstruction} {@code Object}. - */ - public ShopInstruction() { - this(-1); - } - - /** - * Constructs a new {@code ShopInstruction} {@code Object}. - * @param id the id. - */ - public ShopInstruction(int id) { - super("openshop"); - this.setInstant(false); - this.id = id; - } - - @Override - public boolean execute(Object... args) { - Player player = (Player) args[0]; - NPC n = RegionManager.getNpc(player, id); - n.openShop(player); - return true; - } - - @Override - public ScriptContext parse(Object... params) { - int id = -1; - if (params[0] instanceof Integer) { - id = (Integer) params[0]; - } - ShopInstruction context = new ShopInstruction(id); - context.parameters = params; - return context; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/StartActivityInstruction.java b/Server/src/main/java/core/game/system/script/context/StartActivityInstruction.java deleted file mode 100644 index 48343e3b2..000000000 --- a/Server/src/main/java/core/game/system/script/context/StartActivityInstruction.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.game.system.script.context; - -import core.game.content.activity.ActivityManager; -import core.game.node.entity.player.Player; -import core.game.system.script.ScriptContext; - -import java.util.Arrays; - -/** - * The start activity instruction. - * @author Emperor - */ -public final class StartActivityInstruction extends ScriptContext { - - /** - * The activity to start. - */ - private final String activity; - - /** - * The arguments. - */ - private final Object[] arguments; - - /** - * Constructs a new {@code StartActivityInstruction} {@code Object}. - */ - public StartActivityInstruction() { - this(null); - } - - /** - * Constructs a new {@code StartActivityInstruction} {@code Object}. - * @param activity The name of the activity to start. - * @param arguments The arguments. - */ - public StartActivityInstruction(String activity, Object... arguments) { - super("startactivity"); - this.activity = activity; - this.arguments = arguments; - } - - @Override - public boolean execute(Object... args) { - Player player = (Player) args[0]; - args = new Object[arguments.length + 1]; - args[0] = player; - for (int i = 0; i < arguments.length; i++) { - args[i + 1] = arguments[i]; - } - // System.out.println("Starting activity " + activity + " - " + - // Arrays.toString(args)); - ActivityManager.start(player, activity, false, args); - return true; - } - - @Override - public ScriptContext parse(Object... params) { - String activityName = null; - Object[] args = new Object[params.length - 1]; - int paramIndex = 0; - for (int i = 0; i < params.length; i++) { - Object o = params[i]; - if (o instanceof String && activityName == null) { - activityName = (String) o; - continue; - } - args[paramIndex++] = params[i]; - } - if (paramIndex != args.length) { - args = Arrays.copyOf(args, paramIndex); - } - StartActivityInstruction context = new StartActivityInstruction(activityName, args); - context.parameters = params; - return context; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/context/WGuildTokenCondition.java b/Server/src/main/java/core/game/system/script/context/WGuildTokenCondition.java deleted file mode 100644 index 57ddf0532..000000000 --- a/Server/src/main/java/core/game/system/script/context/WGuildTokenCondition.java +++ /dev/null @@ -1,30 +0,0 @@ -package core.game.system.script.context; - -import core.game.node.entity.player.Player; -import core.game.system.script.ScriptContext; - -/** - * ScriptAPI condition for checking if a player has warrior guild tokens. - * @author 'Vexia - */ -public final class WGuildTokenCondition extends ScriptContext { - - /** - * Constructs a new {@code WGuildTokenCondition} {@code Object}. - */ - public WGuildTokenCondition() { - super("hasWarriorTokens"); - } - - @Override - public boolean execute(Object... args) { - return ((Player) args[0]).getSavedData().getActivityData().getWarriorGuildTokens() > 0; - } - - @Override - public ScriptContext parse(Object... params) { - ScriptContext context = new WGuildTokenCondition(); - context.setParameters(params); - return context; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/exc/InvalidGOTOException.java b/Server/src/main/java/core/game/system/script/exc/InvalidGOTOException.java deleted file mode 100644 index 968779e74..000000000 --- a/Server/src/main/java/core/game/system/script/exc/InvalidGOTOException.java +++ /dev/null @@ -1,29 +0,0 @@ -package core.game.system.script.exc; - -/** - * Thrown when a GOTO method name could not be found. - * @author Emperor - */ -public final class InvalidGOTOException extends ScriptException { - - /** - * The serial UID. - */ - private static final long serialVersionUID = 8909510580401378793L; - - /** - * Constructs a new {@code InvalidGOTOException} {@code Object}. - * @param lineId The parsing line id. - */ - public InvalidGOTOException(int lineId) { - super(lineId); - } - - /** - * Constructs a new {@code InvalidGOTOException} {@code Object}. - * @param lineId The parsing line id. - */ - public InvalidGOTOException(String cause, int lineId) { - super(cause, lineId); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/exc/InvalidInstructionException.java b/Server/src/main/java/core/game/system/script/exc/InvalidInstructionException.java deleted file mode 100644 index e813f46de..000000000 --- a/Server/src/main/java/core/game/system/script/exc/InvalidInstructionException.java +++ /dev/null @@ -1,29 +0,0 @@ -package core.game.system.script.exc; - -/** - * Thrown when an instruction could not be found. - * @author Emperor - */ -public class InvalidInstructionException extends ScriptException { - - /** - * The serial UID. - */ - private static final long serialVersionUID = -7969980063835245652L; - - /** - * Constructs a new {@code InvalidInstructionException} {@code Object}. - * @param lineId The parsing line id. - */ - public InvalidInstructionException(int lineId) { - super(lineId); - } - - /** - * Constructs a new {@code InvalidInstructionException} {@code Object}. - * @param lineId The parsing line id. - */ - public InvalidInstructionException(String cause, int lineId) { - super(cause, lineId); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/exc/InvalidInterpreterException.java b/Server/src/main/java/core/game/system/script/exc/InvalidInterpreterException.java deleted file mode 100644 index e6b31ab14..000000000 --- a/Server/src/main/java/core/game/system/script/exc/InvalidInterpreterException.java +++ /dev/null @@ -1,38 +0,0 @@ -package core.game.system.script.exc; - -/** - * An exception thrown when the interpreter could not be found. - * @author Emperor - */ -public final class InvalidInterpreterException extends ScriptException { - - /** - * The serial UID. - */ - private static final long serialVersionUID = 6096860966849544737L; - - /** - * Constructs a new {@code InvalidInterpreterException} {@code Object}. - */ - public InvalidInterpreterException() { - super(); - } - - /** - * Constructs a new {@code InvalidInterpreterException} {@code Object}. - * @param lineId The line id. - */ - public InvalidInterpreterException(int lineId) { - super(lineId); - } - - /** - * Constructs a new {@code InvalidInterpreterException} {@code Object}. - * @param cause The cause. - * @param lineId The parsing line id. - */ - public InvalidInterpreterException(String cause, int lineId) { - super(cause, lineId); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/script/exc/ScriptException.java b/Server/src/main/java/core/game/system/script/exc/ScriptException.java deleted file mode 100644 index 693fb18e6..000000000 --- a/Server/src/main/java/core/game/system/script/exc/ScriptException.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.game.system.script.exc; - -/** - * An exception thrown when compiling an asc script. - * @author 'Vexia - */ -public class ScriptException extends Exception { - - /** - * The serial version UID. - */ - private static final long serialVersionUID = -1328749105522163758L; - - /** - * The line index. - */ - protected final int lineId; - - /** - * Constructs a new {@code ScriptException} {@code Object}. - */ - public ScriptException() { - this(-1); - } - - /** - * Constructs a new {@code ScriptException} {@code Object}. - * @param lineId The parsing line id. - */ - public ScriptException(int lineId) { - super(); - this.lineId = lineId; - } - - /** - * Constructs a new {@code ScriptException} {@code Object}. - * @param message the message. - * @param lineId The parsing line id. - */ - public ScriptException(final String message, int lineId) { - super("Error parsing line " + lineId + ": " + message); - this.lineId = lineId; - } - - /** - * Constructs a new {@code ScriptException} {@code Object}. - * @param message The error message. - * @param cause The cause. - */ - public ScriptException(String message, Throwable cause) { - super(message, cause); - lineId = -1; - } - -} diff --git a/Server/src/main/java/core/game/system/script/exc/UnknownScriptException.java b/Server/src/main/java/core/game/system/script/exc/UnknownScriptException.java deleted file mode 100644 index a652f3b51..000000000 --- a/Server/src/main/java/core/game/system/script/exc/UnknownScriptException.java +++ /dev/null @@ -1,23 +0,0 @@ -package core.game.system.script.exc; - -/** - * An exception thrown when the script type is unknown. - * @author Emperor - */ -public class UnknownScriptException extends ScriptException { - - /** - * The serial UID. - */ - private static final long serialVersionUID = -4897522544605020858L; - - /** - * Constructs a new {@code UnknownScriptException} {@code Object}. - * @param message The cause. - * @param lineId The parsing line index. - */ - public UnknownScriptException(String message, int lineId) { - super(message, lineId); - } - -} diff --git a/Server/src/main/java/core/game/system/security/EncryptionManager.java b/Server/src/main/java/core/game/system/security/EncryptionManager.java deleted file mode 100644 index 0b038fb3b..000000000 --- a/Server/src/main/java/core/game/system/security/EncryptionManager.java +++ /dev/null @@ -1,513 +0,0 @@ -package core.game.system.security; - -import core.game.system.SystemManager; - -import java.io.UnsupportedEncodingException; -import java.security.SecureRandom; - -/** - * Represents the class of simple encryption functions. - * @author Unknown - */ -public class EncryptionManager { - - // Define the BCrypt workload to use when generating password hashes. 10-31 - // is a valid value. - private static int workload = 12; - - public static void main(String... args) { - - 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); - } - - /** - * This method can be used to generate a string representing an account - * password suitable for storing in a database. It will be an OpenBSD-style - * crypt(3) formatted hash string of length=60 The bcrypt workload is - * specified in the above static variable, a value from 10 to 31. A workload - * of 12 is a very reasonable safe default as of 2013. This automatically - * handles secure 128-bit salt generation and storage within the hash. - * @param password_plaintext The account's plaintext password as provided - * during account creation, or when changing an account's password. - * @return String - a string of length 60 that is the bcrypt hashed password - * in crypt(3) format. - */ - public String hashPassword(String password_plaintext) { - String salt = BCrypt.gensalt(workload); - String hashed_password = BCrypt.hashpw(password_plaintext, salt); - return (hashed_password); - } - - /** - * Hash pass. - * @param password_plaintext - * @param salt - * @return - */ - public String hashPassword(String password_plaintext, String salt) { - String hashed_password = BCrypt.hashpw(password_plaintext, salt); - return (hashed_password); - } - - /** - * This method can be used to verify a computed hash from a plaintext (e.g. - * during a login request) with that of a stored hash from a database. The - * password hash from the database must be passed as the second variable. - * @param password_plaintext The account's plaintext password, as provided - * during a login request - * @param stored_hash The account's stored password hash, retrieved from the - * authorization database - * @return boolean - true if the password matches the password of the stored - * hash, false otherwise - */ - public boolean checkPassword(String password_plaintext, String stored_hash) { - boolean password_verified = false; - if (null == stored_hash || !stored_hash.startsWith("$2a$")) - throw new java.lang.IllegalArgumentException("Invalid hash provided for comparison"); - password_verified = BCrypt.checkpw(password_plaintext, stored_hash); - return (password_verified); - } - - // Copyright (c) 2006 Damien Miller - // - // Permission to use, copy, modify, and distribute this software for any - // purpose with or without fee is hereby granted, provided that the above - // copyright notice and this permission notice appear in all copies. - // - // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - /** - * BCrypt implements OpenBSD-style Blowfish password hashing using the - * scheme described in "A Future-Adaptable Password Scheme" by Niels Provos - * and David Mazieres.

This password hashing system tries to thwart - * off-line password cracking using a computationally-intensive hashing - * algorithm, based on Bruce Schneier's Blowfish cipher. The work factor of - * the algorithm is parameterised, so it can be increased as computers get - * faster.

Usage is really simple. To hash a password for the first - * time, call the hashpw method with a random salt, like this:

- * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
- *

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 = - * BCrypt.gensalt(12)

The amount of work increases - * exponentially (2**log_rounds), so each increment is twice as much work. - * The default log_rounds is 10, and the valid range is 4 to 31. - * @author Damien Miller - * @version 0.2 - */ - public static class BCrypt { - // BCrypt parameters - private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; - private static final int BCRYPT_SALT_LEN = 16; - - // Blowfish parameters - private static final int BLOWFISH_NUM_ROUNDS = 16; - - // Initial contents of key schedule - private static final int P_orig[] = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b }; - private static final int S_orig[] = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, - 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 }; - - // bcrypt IV: "OrpheanBeholderScryDoubt" - static private final int bf_crypt_ciphertext[] = { 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 }; - - // Table for Base64 encoding - static private final char base64_code[] = { '.', '/', '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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - - // Table for Base64 decoding - static private final byte index_64[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1 }; - - // Expanded Blowfish key - private int P[]; - private int S[]; - - /** - * Encode a byte array using bcrypt's slightly-modified base64 encoding - * scheme. Note that this is *not* compatible with the standard - * MIME-base64 encoding. - * @param d the byte array to encode - * @param len the number of bytes to encode - * @return base64-encoded string - * @exception IllegalArgumentException if the length is invalid - */ - private static String encode_base64(byte d[], int len) throws IllegalArgumentException { - int off = 0; - StringBuffer rs = new StringBuffer(); - int c1, c2; - - if (len <= 0 || len > d.length) - throw new IllegalArgumentException("Invalid len"); - - while (off < len) { - c1 = d[off++] & 0xff; - rs.append(base64_code[(c1 >> 2) & 0x3f]); - c1 = (c1 & 0x03) << 4; - if (off >= len) { - rs.append(base64_code[c1 & 0x3f]); - break; - } - c2 = d[off++] & 0xff; - c1 |= (c2 >> 4) & 0x0f; - rs.append(base64_code[c1 & 0x3f]); - c1 = (c2 & 0x0f) << 2; - if (off >= len) { - rs.append(base64_code[c1 & 0x3f]); - break; - } - c2 = d[off++] & 0xff; - c1 |= (c2 >> 6) & 0x03; - rs.append(base64_code[c1 & 0x3f]); - rs.append(base64_code[c2 & 0x3f]); - } - return rs.toString(); - } - - /** - * Look up the 3 bits base64-encoded by the specified character, - * range-checking againt conversion table - * @param x the base64-encoded value - * @return the decoded value of x - */ - private static byte char64(char x) { - if ((int) x > index_64.length) - return -1; - return index_64[(int) x]; - } - - /** - * Decode a string encoded using bcrypt's base64 scheme to a byte array. - * Note that this is *not* compatible with the standard MIME-base64 - * encoding. - * @param s the string to decode - * @param maxolen the maximum number of bytes to decode - * @return an array containing the decoded bytes - * @throws IllegalArgumentException if maxolen is invalid - */ - private static byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException { - StringBuffer rs = new StringBuffer(); - int off = 0, slen = s.length(), olen = 0; - byte ret[]; - byte c1, c2, c3, c4, o; - - if (maxolen <= 0) - throw new IllegalArgumentException("Invalid maxolen"); - - while (off < slen - 1 && olen < maxolen) { - c1 = char64(s.charAt(off++)); - c2 = char64(s.charAt(off++)); - if (c1 == -1 || c2 == -1) - break; - o = (byte) (c1 << 2); - o |= (c2 & 0x30) >> 4; - rs.append((char) o); - if (++olen >= maxolen || off >= slen) - break; - c3 = char64(s.charAt(off++)); - if (c3 == -1) - break; - o = (byte) ((c2 & 0x0f) << 4); - o |= (c3 & 0x3c) >> 2; - rs.append((char) o); - if (++olen >= maxolen || off >= slen) - break; - c4 = char64(s.charAt(off++)); - o = (byte) ((c3 & 0x03) << 6); - o |= c4; - rs.append((char) o); - ++olen; - } - - ret = new byte[olen]; - for (off = 0; off < olen; off++) - ret[off] = (byte) rs.charAt(off); - return ret; - } - - /** - * Blowfish encipher a single 64-bit block encoded as two 32-bit halves - * @param lr an array containing the two 32-bit half blocks - * @param off the position in the array of the blocks - */ - private final void encipher(int lr[], int off) { - int i, n, l = lr[off], r = lr[off + 1]; - - l ^= P[0]; - for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { - // Feistel substitution on left word - n = S[(l >> 24) & 0xff]; - n += S[0x100 | ((l >> 16) & 0xff)]; - n ^= S[0x200 | ((l >> 8) & 0xff)]; - n += S[0x300 | (l & 0xff)]; - r ^= n ^ P[++i]; - - // Feistel substitution on right word - n = S[(r >> 24) & 0xff]; - n += S[0x100 | ((r >> 16) & 0xff)]; - n ^= S[0x200 | ((r >> 8) & 0xff)]; - n += S[0x300 | (r & 0xff)]; - l ^= n ^ P[++i]; - } - lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; - lr[off + 1] = l; - } - - /** - * Cycically extract a word of key material - * @param data the string to extract the data from - * @param offp a "pointer" (as a one-entry array) to the current offset - * into data - * @return the next word of material from data - */ - private static int streamtoword(byte data[], int offp[]) { - int i; - int word = 0; - int off = offp[0]; - - for (i = 0; i < 4; i++) { - word = (word << 8) | (data[off] & 0xff); - off = (off + 1) % data.length; - } - - offp[0] = off; - return word; - } - - /** - * Initialise the Blowfish key schedule - */ - private void init_key() { - P = (int[]) P_orig.clone(); - S = (int[]) S_orig.clone(); - } - - /** - * Key the Blowfish cipher - * @param key an array containing the key - */ - private void key(byte key[]) { - int i; - int koffp[] = { 0 }; - int lr[] = { 0, 0 }; - int plen = P.length, slen = S.length; - - for (i = 0; i < plen; i++) - P[i] = P[i] ^ streamtoword(key, koffp); - - for (i = 0; i < plen; i += 2) { - encipher(lr, 0); - P[i] = lr[0]; - P[i + 1] = lr[1]; - } - - for (i = 0; i < slen; i += 2) { - encipher(lr, 0); - S[i] = lr[0]; - S[i + 1] = lr[1]; - } - } - - /** - * Perform the "enhanced key schedule" step described by Provos and - * Mazieres in "A Future-Adaptable Password Scheme" - * http://www.openbsd.org/papers/bcrypt-paper.ps - * @param data salt information - * @param key password information - */ - private void ekskey(byte data[], byte key[]) { - int i; - int koffp[] = { 0 }, doffp[] = { 0 }; - int lr[] = { 0, 0 }; - int plen = P.length, slen = S.length; - - for (i = 0; i < plen; i++) - P[i] = P[i] ^ streamtoword(key, koffp); - - for (i = 0; i < plen; i += 2) { - lr[0] ^= streamtoword(data, doffp); - lr[1] ^= streamtoword(data, doffp); - encipher(lr, 0); - P[i] = lr[0]; - P[i + 1] = lr[1]; - } - - for (i = 0; i < slen; i += 2) { - lr[0] ^= streamtoword(data, doffp); - lr[1] ^= streamtoword(data, doffp); - encipher(lr, 0); - S[i] = lr[0]; - S[i + 1] = lr[1]; - } - } - - /** - * Perform the central password hashing step in the bcrypt scheme - * @param password the password to hash - * @param salt the binary salt to hash with the password - * @param log_rounds the binary logarithm of the number of rounds of - * hashing to apply - * @return an array containing the binary hashed password - */ - private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) { - int rounds, i, j; - int cdata[] = (int[]) bf_crypt_ciphertext.clone(); - int clen = cdata.length; - byte ret[]; - - if (log_rounds < 4 || log_rounds > 31) - throw new IllegalArgumentException("Bad number of rounds"); - rounds = 1 << log_rounds; - if (salt.length != BCRYPT_SALT_LEN) - throw new IllegalArgumentException("Bad salt length"); - - init_key(); - ekskey(salt, password); - for (i = 0; i < rounds; i++) { - key(password); - key(salt); - } - - for (i = 0; i < 64; i++) { - for (j = 0; j < (clen >> 1); j++) - encipher(cdata, j << 1); - } - - ret = new byte[clen * 4]; - for (i = 0, j = 0; i < clen; i++) { - ret[j++] = (byte) ((cdata[i] >> 24) & 0xff); - ret[j++] = (byte) ((cdata[i] >> 16) & 0xff); - ret[j++] = (byte) ((cdata[i] >> 8) & 0xff); - ret[j++] = (byte) (cdata[i] & 0xff); - } - return ret; - } - - /** - * Hash a password using the OpenBSD bcrypt scheme - * @param password the password to hash - * @param salt the salt to hash with (perhaps generated using - * BCrypt.gensalt) - * @return the hashed password - */ - public static String hashpw(String password, String salt) { - BCrypt B; - String real_salt; - byte passwordb[], saltb[], hashed[]; - char minor = (char) 0; - int rounds, off = 0; - StringBuffer rs = new StringBuffer(); - - if (salt.charAt(0) != '$' || salt.charAt(1) != '2') - throw new IllegalArgumentException("Invalid salt version"); - if (salt.charAt(2) == '$') - off = 3; - else { - minor = salt.charAt(2); - if (minor != 'a' || salt.charAt(3) != '$') - throw new IllegalArgumentException("Invalid salt revision"); - off = 4; - } - - // Extract number of rounds - if (salt.charAt(off + 2) > '$') - throw new IllegalArgumentException("Missing salt rounds"); - rounds = Integer.parseInt(salt.substring(off, off + 2)); - - real_salt = salt.substring(off + 3, off + 25); - try { - passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8"); - } catch (UnsupportedEncodingException uee) { - throw new AssertionError("UTF-8 is not supported"); - } - - saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); - - B = new BCrypt(); - hashed = B.crypt_raw(passwordb, saltb, rounds); - - rs.append("$2"); - if (minor >= 'a') - rs.append(minor); - rs.append("$"); - if (rounds < 10) - rs.append("0"); - rs.append(Integer.toString(rounds)); - rs.append("$"); - rs.append(encode_base64(saltb, saltb.length)); - rs.append(encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1)); - return rs.toString(); - } - - /** - * Generate a salt for use with the BCrypt.hashpw() method - * @param log_rounds the log2 of the number of rounds of hashing to apply - * - the work factor therefore increases as 2**log_rounds. - * @param random an instance of SecureRandom to use - * @return an encoded salt value - */ - public static String gensalt(int log_rounds, SecureRandom random) { - StringBuffer rs = new StringBuffer(); - byte rnd[] = new byte[BCRYPT_SALT_LEN]; - - random.nextBytes(rnd); - - rs.append("$2a$"); - if (log_rounds < 10) - rs.append("0"); - rs.append(Integer.toString(log_rounds)); - rs.append("$"); - rs.append(encode_base64(rnd, rnd.length)); - return rs.toString(); - } - - /** - * Generate a salt for use with the BCrypt.hashpw() method - * @param log_rounds the log2 of the number of rounds of hashing to apply - * - the work factor therefore increases as 2**log_rounds. - * @return an encoded salt value - */ - public static String gensalt(int log_rounds) { - return gensalt(log_rounds, new SecureRandom()); - } - - /** - * Generate a salt for use with the BCrypt.hashpw() method, selecting a - * reasonable default for the number of hashing rounds to apply - * @return an encoded salt value - */ - public static String gensalt() { - return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS); - } - - /** - * Check that a plaintext password matches a previously hashed one - * @param plaintext the plaintext password to verify - * @param hashed the previously-hashed password - * @return true if the passwords match, false otherwise - */ - public static boolean checkpw(String plaintext, String hashed) { - return (hashed.compareTo(hashpw(plaintext, hashed)) == 0); - } - } - -} diff --git a/Server/src/main/java/core/game/system/task/ItemLogoutTask.java b/Server/src/main/java/core/game/system/task/ItemLogoutTask.java deleted file mode 100644 index 27c29b91d..000000000 --- a/Server/src/main/java/core/game/system/task/ItemLogoutTask.java +++ /dev/null @@ -1,34 +0,0 @@ -package core.game.system.task; - -import core.game.node.entity.player.Player; -import core.game.node.item.Item; - -/** - * Logout task used for adding items when the player logs out. - * @author Emperor - */ -public final class ItemLogoutTask extends LogoutTask { - - /** - * The items. - */ - private final Item[] items; - - /** - * Constructs a new {@code ItemLogoutTask} {@code Object}. - * @param ticks The amount of ticks. - * @param items The items to add on logout. - */ - public ItemLogoutTask(int ticks, Item... items) { - super(ticks); - this.items = items; - } - - @Override - public void run(Player player) { - for (Item item : items) { - player.getInventory().add(item); - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/task/LocationLogoutTask.java b/Server/src/main/java/core/game/system/task/LocationLogoutTask.java deleted file mode 100644 index ed9a6198c..000000000 --- a/Server/src/main/java/core/game/system/task/LocationLogoutTask.java +++ /dev/null @@ -1,33 +0,0 @@ -package core.game.system.task; - -import core.game.node.entity.player.Player; -import core.game.world.map.Location; - -/** - * Sets the player's location on logout. - * @author Emperor - */ -public final class LocationLogoutTask extends LogoutTask { - - /** - * The location to set. - */ - private final Location location; - - /** - * Constructs a new {@code LocationLogoutTask} {@code Object}. - * @param ticks The amount of ticks this task is valid for. - * @param location The location to set. - */ - public LocationLogoutTask(int ticks, Location location) { - super(ticks); - this.location = location; - } - - @SuppressWarnings("deprecation") - @Override - public void run(Player player) { - player.setLocation(location); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/system/task/LogoutTask.java b/Server/src/main/java/core/game/system/task/LogoutTask.java deleted file mode 100644 index 241724641..000000000 --- a/Server/src/main/java/core/game/system/task/LogoutTask.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.system.task; - -import core.game.node.entity.player.Player; -import rs09.game.world.GameWorld; - -/** - * A task called upon when a player disconnects from the game. - * @author Emperor - */ -public abstract class LogoutTask { - - /** - * The amount of ticks this logout task is valid for. - */ - private int validity; - - /** - * Constructs a new {@code LogoutTask} {@code Object}. - */ - public LogoutTask() { - this(Integer.MAX_VALUE - GameWorld.getTicks()); - } - - /** - * Constructs a new {@code LogoutTask} {@code Object}. - * @param ticks The amount of ticks this logout task is valid. - */ - public LogoutTask(int ticks) { - this.validity = GameWorld.getTicks() + ticks; - } - - /** - * Runs the task. - * @param player The player. - */ - public abstract void run(Player player); - - /** - * Fires the logout task, if it is still valid. - * @param player The player logging out. - */ - public void fire(Player player) { - if (isValid()) { - run(player); - } - } - - /** - * Checks if the logout task is valid. - * @return {@code True} if so. - */ - public boolean isValid() { - return validity > GameWorld.getTicks(); - } - - /** - * Sets the new validity delay. - * @param ticks The amount of ticks this task should remain valid for. - */ - public void setDelay(int ticks) { - this.validity = GameWorld.getTicks() + ticks; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/GameType.java b/Server/src/main/java/core/game/world/GameType.java deleted file mode 100644 index 6cf13425e..000000000 --- a/Server/src/main/java/core/game/world/GameType.java +++ /dev/null @@ -1,34 +0,0 @@ -package core.game.world; - - -/** - * Represents the game type. - * @author Vexia - * - */ -public enum GameType { - ECONOMY, - SPAWN; - - /** - * Parses the game type. - * @param string the string. - * @return {@code GameType} the type. - */ - public static GameType parse(String string) { - return Boolean.parseBoolean(string) ? ECONOMY : SPAWN; - } - - /** - * Gets a game type for the name. - * @param property the property. - * @return the type. - */ - public static GameType forName(String property) { - if (property == null) { - return null; - } - return property.equals("economy") ? ECONOMY : property.equals("spawn") ? SPAWN : null; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/callback/CallBack.java b/Server/src/main/java/core/game/world/callback/CallBack.java deleted file mode 100644 index 90034bf0b..000000000 --- a/Server/src/main/java/core/game/world/callback/CallBack.java +++ /dev/null @@ -1,16 +0,0 @@ -package core.game.world.callback; - -/** - * Represents a call back which is used in {@link CallbackHub} to load any - * neccesary details used on the starting of a world. - * @author 'Vexia - */ -public interface CallBack { - - /** - * Method used to call anything related to this {@link CallBack}. - * @return True if succesfull with calling, and - * False if not. - */ - public boolean call(); -} diff --git a/Server/src/main/java/core/game/world/map/BuildRegionChunk.java b/Server/src/main/java/core/game/world/map/BuildRegionChunk.java deleted file mode 100644 index 197011b42..000000000 --- a/Server/src/main/java/core/game/world/map/BuildRegionChunk.java +++ /dev/null @@ -1,281 +0,0 @@ -package core.game.world.map; - - -import core.game.node.entity.player.Player; -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 rs09.game.system.SystemLogger; -import core.game.world.map.build.LandscapeParser; -import core.net.packet.IoBuffer; -import core.net.packet.out.ClearScenery; -import core.net.packet.out.ConstructGroundItem; -import core.net.packet.out.ConstructScenery; - -/** - * A region chunk, used for easily modifying objects. - * @author Emperor - * - */ -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; - - /** - * The list of changes made. - */ - private final Scenery[][][] objects; - - /** - * Constructs a new {@code BuildRegionChunk} {@code Object} - * @param base The base location. - * @param rotation The rotation. - * @param plane The region plane. - */ - public BuildRegionChunk(Location base, int rotation, RegionPlane plane) { - super(base, rotation, plane); - this.objects = new Scenery[ARRAY_SIZE][8][8]; - this.objects[0] = super.objects; - } - - @Override - protected boolean appendUpdate(Player player, IoBuffer buffer) { - boolean updated = false;//super.appendUpdate(player, buffer); - for (int i = 0; i < objects.length; i++) { - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery o = objects[i][x][y]; - if (o instanceof Constructed) { - ConstructScenery.write(buffer, o); - updated = true; - } - else if (o != null && !o.isRenderable()) { - ClearScenery.write(buffer, o); - updated = true; - } - } - } - } - 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; - } - } - } - } - return updated; - } - - @Override - public void rotate(Direction direction) { - if (rotation != 0) { - SystemLogger.logErr("Region chunk was already rotated!"); - return; - } - Scenery[][][] copy = new Scenery[ARRAY_SIZE][SIZE][SIZE]; - int baseX = currentBase.getLocalX(); - int baseY = currentBase.getLocalY(); - 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; - } - plane.getObjects()[baseX + x][baseY + y] = null; - plane.getFlags().getClippingFlags()[baseX + x][baseY + y] = 0; - } - } - clear(); - rotation = (direction.toInteger() + (direction.toInteger() % 2 == 0 ? 2 : 0)) % 4; - for (int i = 0; i < objects.length; i++) { - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery object = copy[i][x][y]; - if (object != null) { - int[] pos = getRotatedPosition(x, y, object.getDefinition().getSizeX(), object.getDefinition().getSizeY(), object.getRotation(), rotation); - Scenery obj = object.transform(object.getId(), (object.getRotation() + rotation) % 4, object.getLocation().transform(pos[0] - x, pos[1] - y, 0)); - if (object instanceof Constructed) { - obj = obj.asConstructed(); - } - obj.setActive(object.isActive()); - obj.setRenderable(object.isRenderable()); - LandscapeParser.flagScenery(plane, baseX + pos[0], baseY + pos[1], obj, true, true); - } - } - } - } - } - - @Override - public BuildRegionChunk copy(RegionPlane plane) { - BuildRegionChunk chunk = new BuildRegionChunk(base, rotation, plane); - for (int i = 0; i < chunk.objects.length; i++) { - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery o = objects[i][x][y]; - if (o instanceof Constructed) { - chunk.objects[i][x][y] = o.transform(o.getId(), o.getRotation()).asConstructed(); - } - else if (o != null) { - chunk.objects[i][x][y] = o.transform(o.getId()); - chunk.objects[i][x][y].setActive(o.isActive()); - chunk.objects[i][x][y].setRenderable(o.isRenderable()); - } - } - } - } - return chunk; - } - - @Override - public void clear() { - super.clear(); - 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++) { - objects[i][x][y] = null; - } - } - } - } - - /** - * Removes the scenery. - * @param object The object to remove. - */ - public void remove(Scenery object) { - int chunkX = object.getLocation().getChunkOffsetX(); - int chunkY = object.getLocation().getChunkOffsetY(); - Scenery current = null; - int index = -1; - int i = 0; - while ((current == null || current.getId() != object.getId()) && i < objects.length) { - current = objects[i++][chunkX][chunkY]; - if ((current == null || current.getId() < 1) && index == -1) { - index = i - 1; - } - } - if (current != null && current.equals(object)) { - current.setActive(false); - object.setRenderable(false); - } - else { - objects[index][chunkX][chunkY] = object; - } - object.setActive(false); - object.setRenderable(false); - } - - /** - * Adds the scenery. - * @param object The object to add. - */ - public void add(Scenery object) { - int chunkX = object.getLocation().getChunkOffsetX(); - int chunkY = object.getLocation().getChunkOffsetY(); - Scenery current = null; - int index = -1; - int i = 0; - while ((current == null || current.getId() != object.getId()) && i < objects.length) { - current = objects[i++][chunkX][chunkY]; - if ((current == null || current.getId() < 1) && index == -1) { - index = i - 1; - } - } - if (current != null && current.equals(object)) { - current.setActive(true); - current.setRenderable(true); - } - else if (index == -1) { - throw new IllegalStateException("Insufficient array length for storing object!"); - } - else { - objects[index][chunkX][chunkY] = object = object.asConstructed(); - } - object.setActive(true); - object.setRenderable(true); - } - - /** - * Stores an object on the region chunk. - * @param object The object. - */ - public void store(Scenery object) { - if (object == null) { - return; - } - int chunkX = object.getLocation().getChunkOffsetX(); - int chunkY = object.getLocation().getChunkOffsetY(); - for (int i = 0; i < objects.length; i++) { - Scenery stat = objects[i][chunkX][chunkY]; - if (stat == null || stat.getId() < 1) { - objects[i][chunkX][chunkY] = object; - object.setActive(true); - object.setRenderable(true); - return; - } - } - System.err.print("Objects - ["); - for (int i = 0; i < objects.length; i++) { - System.err.print(objects[i][chunkX][chunkY]); - if (i < objects.length - 1) { - System.err.print(", "); - } - } - SystemLogger.logErr("]!"); - throw new IllegalStateException("Insufficient array length for storing all objects! "); - } - - /** - * Gets the objects index for the given object id. - * @param x The x-coordinate on the region chunk. - * @param y The y-coordinate on the region chunk. - * @param objectId The object id. - */ - public int getIndex(int x, int y, int objectId) { - for (int i = 0; i < objects.length; i++) { - Scenery o = get(x, y, i); - if (o != null && ((objectId > -1 && o.getId() == objectId) || (objectId == -1 && o.getDefinition().hasOptions(false)))) { - return i; - } - } - return 0; - } - - /** - * Gets a scenery. - * @param x The chunk x-coordinate. - * @param y The chunk y-coordinate. - * @param index The index (0 = default). - * @return The object. - */ - public Scenery get(int x, int y, int index) { - return objects[index][x][y]; - } - - @Override - public Scenery[] getObjects(int chunkX, int chunkY) { - Scenery[] objects = new Scenery[ARRAY_SIZE]; - for (int i = 0; i < ARRAY_SIZE; i++) { - objects[i] = this.objects[i][chunkX][chunkY]; - } - return objects; - } - - /** - * Gets the objects. - * @param index The index. - * @return The objects array. - */ - public Scenery[][] getObjects(int index) { - return objects[index]; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/map/Location.java b/Server/src/main/java/core/game/world/map/Location.java deleted file mode 100644 index b7d188f55..000000000 --- a/Server/src/main/java/core/game/world/map/Location.java +++ /dev/null @@ -1,475 +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 java.util.ArrayList; - -/** - * 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 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); - } - - /** - * 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); - } - - /** - * 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. - * @return The local x-coordinate. - */ - public int getLocalX() { - return x - ((x >> 6) << 6); - } - - /** - * Gets the local y-coordinate on the current region. - * @return The local y-coordinate. - */ - public int getLocalY() { - return y - ((y >> 6) << 6); - } - - /** - * Gets the local x-coordinate. - * @return The local x-coordinate. - */ - public int getSceneX() { - return x - ((getRegionX() - 6) << 3); - } - - /** - * Gets the local y-coordinate. - * @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 + "]"; - } - - @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; - } -} diff --git a/Server/src/main/java/core/game/world/map/RegionChunk.java b/Server/src/main/java/core/game/world/map/RegionChunk.java deleted file mode 100644 index 4deaabbab..000000000 --- a/Server/src/main/java/core/game/world/map/RegionChunk.java +++ /dev/null @@ -1,368 +0,0 @@ -package core.game.world.map; - -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 rs09.game.system.SystemLogger; -import core.game.world.map.build.DynamicRegion; -import core.game.world.map.build.LandscapeParser; -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; -import core.net.packet.out.ClearScenery; -import core.net.packet.out.ConstructGroundItem; -import core.net.packet.out.ConstructScenery; -import core.net.packet.out.UpdateAreaPosition; - -import java.util.ArrayList; -import java.util.List; - -/** - * 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. - */ - protected Scenery[][] objects; - - /** - * 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. - * @param rotation The rotation. - */ - public RegionChunk(Location base, int rotation, RegionPlane plane) { - this.base = base; - this.currentBase = base; - this.rotation = rotation; - this.plane = plane; - this.objects = new Scenery[SIZE][SIZE]; - } - - /** - * Copies the region chunk. - * @param plane The region plane. - * @return The region chunk. - */ - public BuildRegionChunk copy(RegionPlane plane) { - return new BuildRegionChunk(base, rotation, plane); - } - - /** - * Registers an update flag. - * @param flag The flag. - */ - public void flag(UpdateFlag flag) { - flags.add(flag); - } - - /** - * Clears the region chunk. - */ - public void clear() { - flags.clear(); - if (items != null && plane.getRegion() instanceof DynamicRegion) { - items.clear(); - items = null; - } - } - - /** - * Updates the region chunk. - * @param player The player. - */ - public void synchronize(Player player) { - IoBuffer buffer = UpdateAreaPosition.getChunkUpdateBuffer(player, currentBase); - if (appendUpdate(player, buffer)) { - player.getSession().write(buffer); - } - } - - /** - * Writes the region chunk update data on the buffer. - * @param player The player we're updating for. - * @param buffer The buffer to write on. - * @return {@code True} if an update occured. - */ - protected boolean appendUpdate(Player player, IoBuffer buffer) { - boolean updated = false; - int baseX = currentBase.getLocalX(); - int baseY = currentBase.getLocalY(); - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery dyn = objects[x][y]; - if (dyn == null || plane.getObjects() == null) { - continue; - } - Scenery stat = plane.getObjects()[baseX + x][baseY + y]; - if (!dyn.isRenderable() && stat != null) { - ClearScenery.write(buffer, stat); - updated = true; - } - else if (dyn != stat) { - if (stat != null) { - ClearScenery.write(buffer, stat); - } - ConstructScenery.write(buffer, dyn); - updated = true; - } - } - } - 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; - } - } - } - } - return updated; - } - - /** - * Sends all the update flags. - */ - public void update(Player player) { - if (isUpdated()) { - IoBuffer buffer = UpdateAreaPosition.getChunkUpdateBuffer(player, currentBase); - Object[] flagsArray = flags.toArray(); - int size = flagsArray.length; - for (int i = 0; i < size; i++) { - UpdateFlag flag = (UpdateFlag) flagsArray[i]; - flag.writeDynamic(buffer, player); - } - player.getSession().write(buffer); - } - } - - /** - * Rotates the chunk. - * @param direction The direction. - */ - public void rotate(Direction direction) { - if (rotation != 0) { - SystemLogger.logErr("Region chunk was already rotated!"); - return; - } - Scenery[][] copy = new Scenery[SIZE][SIZE]; - Scenery[][] staticCopy = new Scenery[SIZE][SIZE]; - int baseX = currentBase.getLocalX(); - int baseY = currentBase.getLocalY(); - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - copy[x][y] = objects[x][y]; - staticCopy[x][y] = plane.getObjects()[baseX + x][baseY + y]; - objects[x][y] = plane.getObjects()[baseX + x][baseY + y] = null; - plane.getFlags().getClippingFlags()[baseX + x][baseY + y] = 0; - } - } - rotation = direction.toInteger(); - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - Scenery object = copy[x][y]; - Scenery stat = staticCopy[x][y]; - boolean match = object == stat; - if (stat == null) { - continue; - } - int[] pos = getRotatedPosition(x, y, stat.getDefinition().getSizeX(), stat.getDefinition().getSizeY(), stat.getRotation(), rotation); - if (object != null) { - object = object.transform(object.getId(), (object.getRotation() + rotation) % 4, object.getLocation().transform(pos[0] - x, pos[1] - y, 0)); - LandscapeParser.flagScenery(plane, baseX + pos[0], baseY + pos[1], object, true, true); - } - if (match) { - stat = object; - } else { - stat = stat.transform(stat.getId(), (stat.getRotation() + rotation) % 4, stat.getLocation().transform(pos[0] - x, pos[1] - y, 0)); - } - plane.getObjects()[baseX + pos[0]][baseY + pos[1]] = stat; - } - } - } - - /** - * Gets the new coordinates for an object/chunk tile when rotating. - * @param x The current x-coordinate. - * @param y The current y-coordinate. - * @param sizeX The x-size of the object. - * @param sizeY The y-size of the object. - * @param rotation The object rotation. - * @param chunkRotation The chunk rotation. - * @return The new x-coordinate. - */ - public static int[] getRotatedPosition(int x, int y, int sizeX, int sizeY, int rotation, int chunkRotation) { - if ((rotation & 0x1) == 1) { - int s = sizeX; - sizeX = sizeY; - sizeY = s; - } - if (chunkRotation == 0) { - return new int[] { x, y }; - } - if (chunkRotation == 1) { - return new int[] { y, 7 - x - (sizeX - 1) }; - } - if (chunkRotation == 2) { - return new int[] { 7 - x - (sizeX - 1), 7 - y - (sizeY - 1) }; - } - return new int[] { 7 - y - (sizeY - 1), x }; - } - - /** - * Gets the items. - * @return The items. - */ - public List getItems() { - if (items == null) { - items = new ArrayList(); - } - return items; - } - - /** - * Sets the items. - * @param items The items to set. - */ - public void setItems(List items) { - this.items = items; - } - - /** - * Gets the scenerys located on the coordinates in this chunk. - * @param chunkX The chunk x-coordinate (0-7). - * @param chunkY The chunk y-coordinate (0-7). - * @return The objects. - */ - public Scenery[] getObjects(int chunkX, int chunkY) { - return new Scenery[] { objects[chunkX][chunkY] }; - } - - /** - * Gets the objects. - * @return The objects. - */ - public Scenery[][] getObjects() { - return objects; - } - - /** - * Sets the objects. - * @param objects The objects to set. - */ - public void setObjects(Scenery[][] objects) { - this.objects = objects; - } - - /** - * Gets the base. - * @return The base. - */ - public Location getBase() { - return base; - } - - /** - * Sets the base location of the region to copy. - * @param base The base location. - */ - public void setBase(Location base) { - this.base = base; - } - - /** - * Gets the rotation. - * @return The rotation. - */ - public int getRotation() { - return rotation; - } - - /** - * Sets the rotation of the region chunk. - * @param rotation The rotation - */ - public void setRotation(int rotation) { - this.rotation = rotation; - } - - /** - * Gets the updated. - * @return The updated. - */ - public boolean isUpdated() { - return !flags.isEmpty(); - } - - /** - * Resets the flags. - */ - public void resetFlags() { - flags.clear(); - } - - /** - * Gets the region plane. - * @return The plane. - */ - public RegionPlane getPlane() { - return plane; - } - - /** - * Gets the currentBase. - * @return The currentBase. - */ - public Location getCurrentBase() { - return currentBase; - } - - /** - * Sets the currentBase. - * @param currentBase The currentBase to set. - */ - public void setCurrentBase(Location currentBase) { - this.currentBase = currentBase; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/map/zone/RegionZone.java b/Server/src/main/java/core/game/world/map/zone/RegionZone.java deleted file mode 100644 index 102755f10..000000000 --- a/Server/src/main/java/core/game/world/map/zone/RegionZone.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.map.zone; - -/** - * Represents a zone inside a single region of the world map. - * @author Emperor - */ -public final class RegionZone { - - /** - * The map zone. - */ - private final MapZone zone; - - /** - * The borders. - */ - private final ZoneBorders borders; - - /** - * Constructs a new {@code RegionZone} {@code Object}. - * @param zone The map zone. - * @param borders The borders. - */ - public RegionZone(MapZone zone, ZoneBorders borders) { - this.zone = zone; - this.borders = borders; - } - - /** - * Gets the borders. - * @return The borders. - */ - public ZoneBorders getBorders() { - return borders; - } - - /** - * Gets the zone. - * @return The zone. - */ - public MapZone getZone() { - return zone; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/map/zone/ZoneRestriction.java b/Server/src/main/java/core/game/world/map/zone/ZoneRestriction.java deleted file mode 100644 index fb600ce2d..000000000 --- a/Server/src/main/java/core/game/world/map/zone/ZoneRestriction.java +++ /dev/null @@ -1,41 +0,0 @@ -package core.game.world.map.zone; - -/** - * The zone restrictions. - * @author Emperor - */ -public enum ZoneRestriction { - - /** - * No followers allowed in this zone. - */ - FOLLOWERS, - - /** - * No random events allowed. - */ - RANDOM_EVENTS, - - /** - * No fires allowed. - */ - FIRES, - - /** - * Members only. - */ - MEMBERS, - - /** - * No cannons allowed. - */ - CANNON, ; - - /** - * Gets the restriction flag. - * @return The flag. - */ - public int getFlag() { - return 1 << ordinal(); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/map/zone/impl/WildernessZone.java b/Server/src/main/java/core/game/world/map/zone/impl/WildernessZone.java deleted file mode 100644 index 7ea125afb..000000000 --- a/Server/src/main/java/core/game/world/map/zone/impl/WildernessZone.java +++ /dev/null @@ -1,430 +0,0 @@ -package core.game.world.map.zone.impl; - -import core.game.component.Component; -import core.game.container.Container; -import core.game.content.ttrail.UriNPC; -import core.game.interaction.Option; -import core.game.interaction.item.brawling_gloves.BrawlingGloves; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.DeathTask; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.npc.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 core.game.node.entity.player.link.IronmanMode; -import core.game.node.entity.skill.summoning.familiar.Familiar; -import core.game.node.item.GroundItem; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.world.map.Location; -import core.game.world.map.zone.MapZone; -import core.game.world.map.zone.RegionZone; -import core.game.world.map.zone.ZoneBorders; -import core.game.world.map.zone.ZoneRestriction; -import core.tools.RandomFunction; -import org.rs09.consts.NPCs; -import rs09.game.system.config.NPCConfigParser; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; - -import java.util.ArrayList; -import java.util.List; - -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_BASE; -import static rs09.game.node.entity.player.info.stats.StatAttributeKeysKt.STATS_DEATHS; - - -/** - * Handles the wilderness zone. - * @author Emperor - */ -public final class WildernessZone extends MapZone { - /** - * The PvP gear items - */ - private static int[] PVP_GEAR = { 13887, 13893, 13899, 13905, 13870, 13873, 13876, 13879, 13883, 13884, 13890, 13896, 13902, 13858, 13861, 13864, 13867}; - - /** - * 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)); - - /** - * The zone borders. - */ - private ZoneBorders[] borders; - - /** - * Constructs a new {@code WildernessZone} {@code Object}. - */ - public WildernessZone(ZoneBorders... borders) { - super("Wilderness", true, ZoneRestriction.RANDOM_EVENTS); - this.borders = borders; - } - - @Override - public void configure() { - for (ZoneBorders border : borders) { - register(border); - } - } - - /** - * calculate drop rate for rev items based on combat level - * @author ceik - * @param combatLevel - * @return - */ - public int getNewDropRate(int combatLevel){ - double x = combatLevel; - double A = 44044.5491; - double B = -7360.19548; - return (int) (A + (B * Math.log(x))) / 2; - } - - /** - * Handles rev drops - * @author ceik - * @param e The entity dying. - * @param killer The killer. - * @return true - */ - @Override - public boolean death(Entity e, Entity killer) { - try { - if (e instanceof UriNPC) { - e.finalizeDeath(killer); - } - if(e instanceof Player){ - Player player = e.asPlayer(); - player.getSettings().setSpecialEnergy(100); - player.getSettings().updateRunEnergy(player.getSettings().getRunEnergy() - 100); - Player owner = killer instanceof Player ? (Player) killer : player; - player.getPacketDispatch().sendMessage("Oh dear, you are dead!"); - player.incrementAttribute("/save:"+STATS_BASE+":"+STATS_DEATHS); - - //If player was a Hardcore Ironman, announce that they died - if (player.getIronmanManager().getMode().equals(IronmanMode.HARDCORE)){ //if this was checkRestriction, ultimate irons would be moved to HARDCORE_DEAD as well - String gender = player.isMale() ? "Man " : "Woman "; - Repository.sendNews("Hardcore Iron " + gender + " " + player.getUsername() +" has fallen. Total Level: " + player.getSkills().getTotalLevel()); // Not enough room for XP - player.getIronmanManager().setMode(IronmanMode.STANDARD); - player.getSavedData().getActivityData().setHardcoreDeath(true); - player.sendMessage("You have fallen as a Hardcore Iron Man, your Hardcore status has been revoked."); - } - - player.getPacketDispatch().sendTempMusic(90); - if (player.getDetails().getRights() != Rights.ADMINISTRATOR) { - GroundItemManager.create(new Item(526), player.getLocation(), owner); - final Container[] c = DeathTask.getContainers(player); - boolean gravestone = player.getGraveManager().generateable() && player.getIronmanManager().getMode() != IronmanMode.ULTIMATE && !(killer instanceof Player); - int seconds = player.getGraveManager().getType().getDecay() * 60; - int ticks = (1000 * seconds) / 600; - List items = new ArrayList<>(20); - for (Item item : c[1].toArray()) { - if (item != null) { - GroundItem ground; - if (item.hasItemPlugin()) { - item = item.getPlugin().getDeathItem(item); - } - if (!item.getDefinition().isTradeable()) { - ground = new GroundItem(item, player.getLocation(), gravestone ? ticks + 100 : 200, player); - } else { - ground = new GroundItem(item.getDropItem(), player.getLocation(), owner); - } - items.add(ground); - ground.setDropper(owner); //Checking for ironman mode in any circumstance for death items is inaccurate to how it works in both 2009scapes. - GroundItemManager.create(ground); - } - } - player.getEquipment().clear(); - player.getInventory().clear(); - if(!player.getSkullManager().isSkulled() || killer instanceof NPC) { - player.getInventory().addAll(c[0]); - } else { - for(Item item : c[0].toArray()){ - GroundItemManager.create(item,player.getLocation(),owner); - } - } - player.getFamiliarManager().dismiss(); - - } - player.getSkullManager().setSkulled(false); - player.removeAttribute("combat-time"); - player.getPrayer().reset(); - player.getAppearance().sync(); - if (GameWorld.isEconomyWorld() && !player.getSavedData().getGlobalData().isDeathScreenDisabled()) { - player.getInterfaceManager().open(new Component(153)); - } - if (!player.getSavedData().getGlobalData().isDeathScreenDisabled()) { - player.getInterfaceManager().open(new Component(153)); - } - } - - //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)) { - - 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 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()); - return true; - } - } - } - - if (e instanceof NPC) { - e.asNpc().getDefinition().getDropTables().drop(e.asNpc(), killer); - if (((NPC) e).getTask() != null && ((Player) killer).getSlayer().getTask() == e.asNpc().getTask()) { - ((Player) killer).getSlayer().finalizeDeath(killer.asPlayer(), e.asNpc()); - } - e.asNpc().setRespawnTick(GameWorld.getTicks() + e.asNpc().getDefinition().getConfiguration(NPCConfigParser.RESPAWN_DELAY, 17)); - if (!e.asNpc().isRespawn()) { - e.asNpc().clear(); - } - } - } catch (Exception f){ - System.out.println("Unhandled NPC death in wilderness: " + e.getId()); - } - return true; - } - - /** - * Fixes attack options for the revs - * @param e The entity. - * @param target The target to interact with. - * @param option The option. - * @return true - */ - @Override - public boolean interact(Entity e, Node target, Option option) { - if(target instanceof NPC){ - if(target.asNpc().getName().contains("Revenant")){ - e.asPlayer().getProperties().getCombatPulse().attack(target); - return true; - } - } - return super.interact(e, target, option); - } - - - - @Override - public boolean enter(Entity e) { - if (e instanceof Player) { - Player p = (Player) e; - if(!p.isArtificial()) { - show(p); - } else { - 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(); - } - p.getAppearance().sync(); - } else if (e instanceof NPC) { - NPC n = (NPC) e; - if (n.getDefinition().hasAttackOption()) { - n.setAggressive(true); - n.setAggressiveHandler(new AggressiveHandler(n, AggressiveBehavior.WILDERNESS)); - } - } - return true; - } - - @Override - public boolean leave(Entity e, boolean logout) { - if (!logout && e instanceof Player) { - Player p = (Player) e; - leave(p); - if (p.getFamiliarManager().hasFamiliar() && !p.getFamiliarManager().hasPet()) { - Familiar familiar = p.getFamiliarManager().getFamiliar(); - if (familiar.isCombatFamiliar()) { - familiar.reTransform(); - } - } - p.getAppearance().sync(); - } - return true; - } - - /** - * Method used to remove traces of being in the zone. - * @param p the player. - */ - public final void leave(final Player p) { - Component overlay = new Component(381); - if (overlay.getId() == 381) { - p.getInterfaceManager().close(overlay); - } - p.getInteraction().remove(Option._P_ATTACK); - p.getSkullManager().setWilderness(false); - p.getSkullManager().setLevel(0); - } - - /** - * Method used to show being the wilderness. - * @param p the player. - */ - public static final void show(final Player p) { - if (p.getSkullManager().isWildernessDisabled()) { - return; - } - p.getInterfaceManager().openWildernessOverlay(new Component(381)); - p.getSkullManager().setLevel(getWilderness(p)); - p.getPacketDispatch().sendString("Level: " + p.getSkullManager().getLevel(), 381, 1); - if(GameWorld.getSettings().getWild_pvp_enabled()) { - p.getInteraction().set(Option._P_ATTACK); - } - p.getSkullManager().setWilderness(true); - } - - @Override - public boolean teleport(Entity e, int type, Node node) { - if (e instanceof Player) { - Player p = (Player) e; - if (p.getDetails().getRights() == Rights.ADMINISTRATOR) { - return true; - } - if (!checkTeleport(p, (node != null && node instanceof Item && (((Item) node).getName().contains("glory") || ((Item) node).getName().contains("slaying")) ? 30 : 20))) { - return false; - } - } - return true; - } - - /** - * Method used to check if a player can teleport past a level. - * @param p the player. - * @param level the level. - * @return {@code True} if they can teleport. - */ - public static boolean checkTeleport(Player p, int level) { - if (p.getSkullManager().getLevel() > level && !p.getSkullManager().isWildernessDisabled()) { - message(p, "You can't teleport this deep in the wilderness!"); - return false; - } - return true; - } - - @Override - public boolean continueAttack(Entity e, Node target, CombatStyle style, boolean message) { - if (e instanceof Player && target instanceof Player) { - Player p = (Player) e; - Player t = (Player) target; - int level = p.getSkullManager().getLevel(); - if (t.getSkullManager().getLevel() < level) { - level = t.getSkullManager().getLevel(); - } - int combat = p.getProperties().getCurrentCombatLevel(); - int targetCombat = t.getProperties().getCurrentCombatLevel(); - if (combat - level > targetCombat || combat + level < targetCombat) { - if (message) { - p.getPacketDispatch().sendMessage("The level difference between you and your opponent is too great."); - } - return false; - } - } - return true; - } - - @Override - public void locationUpdate(Entity e, Location last) { - if (e instanceof Player && !e.asPlayer().isArtificial()) { - Player p = (Player) e; - p.getSkullManager().setLevel(getWilderness(p)); - Component overlay = p.getInterfaceManager().getOverlay(); - if (overlay == null || overlay.getId() != 381) { - show(p); - } - if (!p.getSkullManager().isWildernessDisabled()) { - p.getPacketDispatch().sendString("Level: " + p.getSkullManager().getLevel(), 381, 1); - } - } - } - - /** - * Checks if the entity is inside the wilderness. - * @param e The entity. - * @return {@code True} if so. - */ - public static boolean isInZone(Entity e) { - Location l = e.getLocation(); - for (RegionZone zone : e.getViewport().getRegion().getRegionZones()) { - if (zone.getZone() == INSTANCE && zone.getBorders().insideBorder(l.getX(), l.getY())) { - return true; - } - } - return false; - } - - /** - * The wilderness level. - * @return the level. - */ - public static int getWilderness(Entity e) { - final int regionId = e.getViewport().getRegion().getId(); - int offsetY = 3524; - if (regionId == 12443 || e.getViewport().getRegion().getId() == 12444) { - offsetY = 9923; - } else if (regionId == 12193) { - offsetY = 10000 - 80; - } else if (regionId == 11937) { - offsetY = 9920; - } - int level = (int) Math.ceil((double) (e.getLocation().getY() - offsetY) / 8d); - if (level < 0) { - return 1; - } - return level; - } - - /** - * Gets the zone borders. - * @return The borders. - */ - public ZoneBorders[] getBorders() { - return borders; - } - - /** - * Gets the instance. - * @return The instance. - */ - public static WildernessZone getInstance() { - return INSTANCE; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.java b/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.java deleted file mode 100644 index 56ed92146..000000000 --- a/Server/src/main/java/core/game/world/update/flag/context/ChatMessage.java +++ /dev/null @@ -1,88 +0,0 @@ -package core.game.world.update.flag.context; - -import core.game.node.entity.player.Player; - -/** - * Represents a chat message. - * @author Emperor - */ -public class ChatMessage { - - /** - * The player reference. - */ - private Player player; - - /** - * The chat text. - */ - private String text; - - /** - * The effects. - */ - private int effects; - - /** - * The numChars. - */ - private int numChars; - - public boolean isQuickChat = false; - - /** - * Constructs a new {@code ChatMessage} {@code Object}. - * @param player The player. - * @param text The chat text. - * @param effects The chat effects. - * @param numChars The num chars. - */ - public ChatMessage(Player player, String text, int effects, int numChars) { - this.player = player; - this.text = text; - this.effects = effects; - this.numChars = numChars; - } - - /** - * Constructs a new {@code ChatMessage.java} {@code Object}. - * @param player the player. - * @param text the text. - */ - public ChatMessage(Player player, String text) { - this.player = player; - this.text = text; - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - - /** - * Get the chat text. - * @return The chat text. - */ - public String getText() { - return text; - } - - /** - * Get the chat effects. - * @return The chat effects. - */ - public int getEffects() { - return effects; - } - - /** - * Get the num chars. - * @return The num chars. - */ - public int getNumChars() { - return numChars; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/game/world/update/flag/npc/NPCAnimation.java b/Server/src/main/java/core/game/world/update/flag/npc/NPCAnimation.java deleted file mode 100644 index b87ae5dd9..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/npc/NPCFaceEntity.java b/Server/src/main/java/core/game/world/update/flag/npc/NPCFaceEntity.java deleted file mode 100644 index 5f4098db3..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/npc/NPCFaceLocation.java b/Server/src/main/java/core/game/world/update/flag/npc/NPCFaceLocation.java deleted file mode 100644 index 922d14258..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/npc/NPCForceChat.java b/Server/src/main/java/core/game/world/update/flag/npc/NPCForceChat.java deleted file mode 100644 index 1a6b66658..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/npc/NPCGraphic.java b/Server/src/main/java/core/game/world/update/flag/npc/NPCGraphic.java deleted file mode 100644 index f15c01fa6..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/npc/NPCHitFlag.java b/Server/src/main/java/core/game/world/update/flag/npc/NPCHitFlag.java deleted file mode 100644 index 7fbde01ac..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/npc/NPCHitFlag1.java b/Server/src/main/java/core/game/world/update/flag/npc/NPCHitFlag1.java deleted file mode 100644 index e443f6f0f..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/npc/NPCSwitchId.java b/Server/src/main/java/core/game/world/update/flag/npc/NPCSwitchId.java deleted file mode 100644 index a72c45f1e..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/AnimationFlag.java b/Server/src/main/java/core/game/world/update/flag/player/AnimationFlag.java deleted file mode 100644 index 925602e21..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/AppearanceFlag.java b/Server/src/main/java/core/game/world/update/flag/player/AppearanceFlag.java deleted file mode 100644 index 3fb42f2bb..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/ChatFlag.java b/Server/src/main/java/core/game/world/update/flag/player/ChatFlag.java deleted file mode 100644 index cdefd314d..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/FaceEntityFlag.java b/Server/src/main/java/core/game/world/update/flag/player/FaceEntityFlag.java deleted file mode 100644 index fa2327027..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/FaceLocationFlag.java b/Server/src/main/java/core/game/world/update/flag/player/FaceLocationFlag.java deleted file mode 100644 index 022d33a71..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/ForceChatFlag.java b/Server/src/main/java/core/game/world/update/flag/player/ForceChatFlag.java deleted file mode 100644 index 318d008ed..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/ForceMovementFlag.java b/Server/src/main/java/core/game/world/update/flag/player/ForceMovementFlag.java deleted file mode 100644 index 5625e51a0..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/GraphicFlag.java b/Server/src/main/java/core/game/world/update/flag/player/GraphicFlag.java deleted file mode 100644 index 661a9472b..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/HitUpdateFlag.java b/Server/src/main/java/core/game/world/update/flag/player/HitUpdateFlag.java deleted file mode 100644 index 702befba9..000000000 --- a/Server/src/main/java/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/java/core/game/world/update/flag/player/HitUpdateFlag1.java b/Server/src/main/java/core/game/world/update/flag/player/HitUpdateFlag1.java deleted file mode 100644 index a4f35bb4e..000000000 --- a/Server/src/main/java/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/java/core/gui/ConsoleFrame.java b/Server/src/main/java/core/gui/ConsoleFrame.java deleted file mode 100644 index e4d1210de..000000000 --- a/Server/src/main/java/core/gui/ConsoleFrame.java +++ /dev/null @@ -1,168 +0,0 @@ -package core.gui; - - -import core.gui.tab.GrandExchangeTab; -import core.gui.tab.PlayerTab; -import core.gui.tab.StatisticsTab; -import core.gui.tab.UtilityTab; - -import javax.swing.*; -import java.awt.*; - -/** - * The console frame tool. - * - * @author Vexia - */ -public class ConsoleFrame extends JFrame { - - /** - * The console frame instance. - */ - public static final ConsoleFrame INSTANCE = new ConsoleFrame(); - - /** - * The serail UID. - */ - private static final long serialVersionUID = 6368064564449356833L; - - /** - * The size of the frame. - */ - private static final Dimension SIZE = new Dimension(1074, 664); - - /** - * The tabbed pane. - */ - private final JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP); - - /** - * The statistics tab. - */ - private final StatisticsTab statisticsTab = StatisticsTab.INSTANCE.init(); - - /** - * The player tab. - */ - private final PlayerTab playerTab = new PlayerTab(); - - /** - * The grand exchange tab. - */ - private final GrandExchangeTab grandExchangeTab = new GrandExchangeTab(); - - /** - * The utility tab. - */ - private final UtilityTab utilityTab = UtilityTab.getInstance(); - - /** - * Constructs a new {@Code consoleFrame} {@Code Object} - */ - private ConsoleFrame() { - super("Console"); - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { - e.printStackTrace(); - } - } - - /** - * Initializes the console frame. - * - * @return the frame. - */ - public ConsoleFrame init() { - setLocationRelativeTo(null); - setSize(SIZE); - getContentPane().setLayout(null); - tabbedPane.setBounds(0, 0, 1068, 636); - Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); - setLocation(dim.width / 2 - this.getSize().width / 2, dim.height / 2 - this.getSize().height / 2); - getContentPane().add(tabbedPane); - addTabs(statisticsTab, utilityTab, playerTab, grandExchangeTab); - setResizable(false); - setVisible(true); - return this; - } - - public static void main(String... args) { - INSTANCE.init(); - } - - /** - * Adds tabs. - * - * @param tabs the tabs. - */ - public void addTabs(ConsoleTab... tabs) { - for (ConsoleTab tab : tabs) { - addTab(tab); - } - } - - /** - * Adds a tab to the tabbed pane. - * - * @param tab the tab. - */ - public void addTab(ConsoleTab tab) { - tabbedPane.add(tab.getName(), tab); - } - - /** - * Gets the statisticsTab. - * - * @return the statisticsTab - */ - public StatisticsTab getStatisticsTab() { - return statisticsTab; - } - - /** - * Gets the tabbedPane. - * - * @return the tabbedPane - */ - public JTabbedPane getTabbedPane() { - return tabbedPane; - } - - /** - * Gets the playerTab. - * - * @return the playerTab - */ - public PlayerTab getPlayerTab() { - return playerTab; - } - - /** - * Gets the utilityTab. - * - * @return the utilityTab - */ - public UtilityTab getUtilityTab() { - return utilityTab; - } - - /** - * Gets the instance. - * - * @return the instance. - */ - public static ConsoleFrame getInstance() { - return INSTANCE; - } - - /** - * Gets the grandExchangeTab. - * - * @return the grandExchangeTab - */ - public GrandExchangeTab getGrandExchangeTab() { - return grandExchangeTab; - } - -} diff --git a/Server/src/main/java/core/gui/ConsoleTab.java b/Server/src/main/java/core/gui/ConsoleTab.java deleted file mode 100644 index daf37deb4..000000000 --- a/Server/src/main/java/core/gui/ConsoleTab.java +++ /dev/null @@ -1,44 +0,0 @@ -package core.gui; - -import javax.swing.*; -import java.awt.*; - -/** - * The console tab. - * - * @author Vexia - */ -public class ConsoleTab extends JPanel { - - /** - * The serial UID. - */ - private static final long serialVersionUID = 2899642836866716523L; - - /** - * The name of the console tab. - */ - private final String name; - - /** - * Constructs a new {@Code consoleTab} {@Code Object} - * - * @param name the name. - */ - public ConsoleTab(String name) { - super(); - this.name = name; - setLayout(null); - setBounds(new Rectangle(0, 0, 1068, 663)); - } - - /** - * Gets the name. - * - * @return the name - */ - public String getName() { - return name; - } - -} diff --git a/Server/src/main/java/core/gui/component/ConsoleLogger.java b/Server/src/main/java/core/gui/component/ConsoleLogger.java deleted file mode 100644 index 48f98e1a4..000000000 --- a/Server/src/main/java/core/gui/component/ConsoleLogger.java +++ /dev/null @@ -1,68 +0,0 @@ -package core.gui.component; - - -import core.gui.ConsoleFrame; - -import java.io.PrintStream; - -/** - * Loggs output to the gui console. - * - * @author Vexia - */ -public class ConsoleLogger extends PrintStream { - - /** - * Constructs a new {@code WorkLogger} {@code Object} - * - * @param stream the stream. - */ - public ConsoleLogger(PrintStream stream) { - super(stream); - } - - @Override - public void println(String message) { - log(message); - } - - @Override - public PrintStream printf(String message, Object... objects) { - return null; - } - - @Override - public void println(boolean message) { - log(String.valueOf(message)); - } - - @Override - public void println(int message) { - log(String.valueOf(message)); - } - - @Override - public void println(double message) { - log(String.valueOf(message)); - } - - @Override - public void println(char message) { - log("" + message); - } - - @Override - public void println(long message) { - log("" + message); - } - - /** - * Method used to log the message. - * - * @param message the message. - */ - public void log(final String message) { - ConsoleFrame.getInstance().getStatisticsTab().log(message); - } - -} diff --git a/Server/src/main/java/core/gui/component/PlayerViewer.java b/Server/src/main/java/core/gui/component/PlayerViewer.java deleted file mode 100644 index 671e3efb6..000000000 --- a/Server/src/main/java/core/gui/component/PlayerViewer.java +++ /dev/null @@ -1,270 +0,0 @@ -package core.gui.component; - - -import core.game.container.Container; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.PlayerDetails; -import core.game.node.item.Item; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import core.gui.ConsoleFrame; -import core.tools.PlayerLoader; -import core.tools.StringUtils; - -import javax.swing.*; -import javax.swing.border.TitledBorder; -import java.awt.*; -import java.awt.event.ActionListener; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; - -/** - * Views a players details. - * - * @author Vexia - */ -public class PlayerViewer extends JPanel { - - /** - * The serail UID. - */ - private static final long serialVersionUID = 1589056461884384398L; - - /** - * The name of the viewer. - */ - private final String name; - - /** - * The player details. - */ - private PlayerDetails details; - - /** - * The player file. - */ - private Player player; - - /** - * Constructs a new {@Code PlayerViewer} {@Code Object} - * - * @param name the name. - */ - public PlayerViewer(String name) { - super(); - this.name = name; - this.details = PlayerLoader.getPlayerDetailFile(name); - if (Repository.getPlayerByName(name) != null) { - setPlayer(Repository.getPlayerByName(name)); - } else { - if (GameWorld.getMajorUpdateWorker().getStarted()) { - setPlayer(PlayerLoader.getPlayerFile(name)); - } - } - setBounds(new Rectangle(0, 0, 755, 539)); - setLayout(null); - JButton btnClose = new JButton("Close"); - btnClose.addActionListener(e -> ConsoleFrame.getInstance().getPlayerTab().closeViewer(PlayerViewer.this)); - btnClose.setBounds(6, 6, 117, 29); - add(btnClose); - - JPanel panel = new JPanel(); - panel.setBorder(new TitledBorder(null, "Info", TitledBorder.LEADING, TitledBorder.TOP, null, null)); - panel.setBounds(6, 35, 286, 428); - add(panel); - panel.setLayout(null); - - JLabel lblName = new JLabel("Username: " + details.getUsername()); - lblName.setBounds(6, 16, 224, 16); - panel.add(lblName); - - JLabel lblIp = new JLabel("IP: " + details.getInfo().getIp()); - lblIp.setBounds(6, 96, 190, 16); - panel.add(lblIp); - - JLabel lblMac = new JLabel("Mac: " + details.getInfo().getMac()); - lblMac.setBounds(6, 124, 224, 16); - panel.add(lblMac); - - JLabel lblCompName = new JLabel("Comp name: " + details.getInfo().getCompName()); - lblCompName.setBounds(6, 208, 236, 16); - panel.add(lblCompName); - - JLabel lblLastLogin = new JLabel("Last login: " + new Date(details.getLastLogin())); - lblLastLogin.setBounds(6, 152, 274, 16); - panel.add(lblLastLogin); - - JLabel lblStatus = new JLabel("Status: " + getStatus()); - lblStatus.setBounds(6, 236, 149, 16); - panel.add(lblStatus); - -// JLabel lblCredits = new JLabel("Credits: " + details.getShop().getCredits()); -// lblCredits.setBounds(6, 180, 139, 16); -// panel.add(lblCredits); - - JLabel lblRights = new JLabel("Rights: " + StringUtils.formatDisplayName(details.getRights().toString())); - lblRights.setBounds(6, 68, 176, 16); - panel.add(lblRights); - -// JLabel lblDisplayName = new JLabel("Display name: " + details.getUsername()); -// lblDisplayName.setBounds(6, 40, 213, 16); -// panel.add(lblDisplayName); - - JLabel lblNetworth = new JLabel("Networth:"); - lblNetworth.setBounds(6, 264, 165, 16); - panel.add(lblNetworth); - - JLabel lblBanned = new JLabel("Banned: " + (details.getBanTime() > 0)); - lblBanned.setBounds(6, 292, 149, 16); - panel.add(lblBanned); - - JLabel lblMuted = new JLabel("Muted: " + (details.getMuteTime() > 0)); - lblMuted.setBounds(6, 317, 149, 16); - panel.add(lblMuted); - - JLabel lblActive = new JLabel("Active: " + checkActive()); - lblActive.setBounds(6, 342, 165, 16); - panel.add(lblActive); - if (player != null) { - lblNetworth.setText("Networth: " + player.getMonitor().getNetworth() + ""); - } - - JButton btnRefresh = new JButton("Refresh"); - btnRefresh.setBounds(124, 6, 117, 29); - add(btnRefresh); - - JButton btnViewContainer = new JButton("View Container"); - btnViewContainer.addActionListener(e -> { - if (player == null) { - return; - } - viewContainer(); - }); - btnViewContainer.setBounds(365, 6, 144, 29); - add(btnViewContainer); - - JButton btnDelete = new JButton("Delete"); - btnDelete.addActionListener(e -> { - int result = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete this account!?"); - if (result == 0) { - ConsoleFrame.getInstance().getUtilityTab().delete(details); - ConsoleFrame.getInstance().getPlayerTab().closeViewer(PlayerViewer.this); - } - }); - btnDelete.setBounds(244, 6, 117, 29); - add(btnDelete); - } - - /** - * Opens the view containermanager. - */ - public void viewContainer() { - JPanel panel = new JPanel(new GridLayout(0, 1)); - ActionListener listener = e -> { - String name1 = e.getActionCommand(); - Container container = null; - switch (name1) { - case "Inventory": - container = player.getInventory(); - break; - case "Equipment": - container = player.getEquipment(); - break; - case "Bank": - container = player.getBank(); - break; - } - if (container != null) { - showContainer(container, name1); - } - }; - JButton invy = new JButton("Inventory"); - panel.add(invy); - invy.addActionListener(listener); - JButton equip = new JButton("Equipment"); - panel.add(equip); - equip.addActionListener(listener); - JButton bank = new JButton("Bank"); - bank.addActionListener(listener); - panel.add(bank); - JOptionPane.showConfirmDialog(null, panel, "View Container", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); - } - - /** - * Shows a container. - * - * @param container the container. - * @param name the name. - */ - public void showContainer(core.game.container.Container container, String name) { - JPanel panel = new JPanel(new GridLayout(0, 1)); - JScrollPane pane = new JScrollPane(); - DefaultListModel model = new DefaultListModel<>(); - JList list = new JList<>(); - for (Item i : container.toArray()) { - if (i == null) { - continue; - } - model.addElement(i.toString()); - } - list.setModel(model); - pane.setViewportView(list); - panel.add(pane); - JOptionPane.showConfirmDialog(null, panel, name, JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); - } - - /** - * Checks if the player is active. - * - * @return {@code True} if so. - */ - public String checkActive() { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); - Date cutOff = null; - long mili = 0; - try { - cutOff = sdf.parse("2014-6-10"); - mili = cutOff.getTime(); - } catch (ParseException e) { - e.printStackTrace(); - } - return ((details.getLastLogin() < mili) ? "Not " : "") + "Active"; - } - - /** - * Checks active status. - * - * @return the status. - */ - public String getStatus() { - return Repository.getPlayerByName(name) != null ? "Online" : "Offline"; - } - - /** - * Gets the name. - * - * @return the name - */ - public String getName() { - return name; - } - - /** - * Gets the player. - * - * @return the player - */ - public Player getPlayer() { - return player; - } - - /** - * Sets the baplayer. - * - * @param player the player to set. - */ - public void setPlayer(Player player) { - this.player = player; - } -} diff --git a/Server/src/main/java/core/gui/tab/GrandExchangeTab.java b/Server/src/main/java/core/gui/tab/GrandExchangeTab.java deleted file mode 100644 index c6fe68e39..000000000 --- a/Server/src/main/java/core/gui/tab/GrandExchangeTab.java +++ /dev/null @@ -1,191 +0,0 @@ -package core.gui.tab; - - -import core.game.node.entity.player.Player; -import core.gui.ConsoleFrame; -import core.gui.ConsoleTab; -import rs09.game.ge.GrandExchangeOffer; -import rs09.game.ge.OfferManager; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * A tab used to view the grand exchange. - * - * @author Vexia - */ -public class GrandExchangeTab extends ConsoleTab { - - /** - * The serial UID. - */ - private static final long serialVersionUID = -1619650762760289798L; - - /** - * The search field. - */ - private JTextField searchField; - - /** - * The jlist. - */ - private final JList list = new JList(); - - /** - * The model. - */ - private final DefaultListModel model = new DefaultListModel(); - - /** - * The id search field. - */ - private JTextField idSearchField; - - /** - * Constructs a new {@Code GrandExchangeTab} {@Code Object} - */ - @SuppressWarnings("serial") - public GrandExchangeTab() { - super("Grand Exchange"); - list.setCellRenderer(new DefaultListCellRenderer() { - - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - GrandExchangeOffer offer = (GrandExchangeOffer) value; - if (offer == null) { - return label; - } - Player player = null; - for (Player p : ConsoleFrame.getInstance().getUtilityTab().getPlayers()) { - if (p == null) { - continue; - } - if (p.getDetails().getUid() == offer.getUid()) { - player = p; - break; - } - } - label.setText((player == null ? "" : player.getName() + " - ") + offer.toString()); - return label; - } - - }); - list.setModel(model); - JLabel lblSearch = new JLabel("Search player:"); - lblSearch.setBounds(24, 20, 95, 16); - add(lblSearch); - - searchField = new JTextField(); - searchField.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - searchPlayer(); - } - }); - searchField.setBounds(119, 14, 185, 28); - add(searchField); - searchField.setColumns(10); - - JPanel panel = new JPanel(); - panel.setBounds(24, 78, 1015, 518); - add(panel); - panel.setLayout(null); - - JScrollPane scrollPane = new JScrollPane(); - scrollPane.setBounds(6, 6, 375, 517); - panel.add(scrollPane); - scrollPane.setViewportView(list); - - JButton btnDisplayDatabase = new JButton("Display Database"); - btnDisplayDatabase.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - displayDatabase(); - } - }); - btnDisplayDatabase.setBounds(316, 15, 173, 29); - add(btnDisplayDatabase); - - JLabel lblNewLabel = new JLabel("Search id:"); - lblNewLabel.setBounds(24, 48, 61, 16); - add(lblNewLabel); - - idSearchField = new JTextField(); - idSearchField.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - searchId(); - } - }); - idSearchField.setColumns(10); - idSearchField.setBounds(119, 48, 185, 28); - add(idSearchField); - - JButton btnNewButton = new JButton("Load"); - btnNewButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ConsoleFrame.getInstance().getUtilityTab().populateList(); - } - }); - btnNewButton.setBounds(316, 43, 173, 29); - add(btnNewButton); - } - - /** - * Searches the player. - */ - private void searchPlayer() { - model.clear(); - Player player = ConsoleFrame.getInstance().getUtilityTab().getPlayer(searchField.getText()); - if (player == null) { - JOptionPane.showMessageDialog(null, "Error! No player found."); - return; - } - for (GrandExchangeOffer o : player.getPlayerGrandExchange().getOffers()) { - if (o == null) { - continue; - } - addOffer(o); - } - } - - /** - * Searches the id. - */ - private void searchId() { - model.clear(); - int itemId = 0; - try { - itemId = Integer.parseInt(idSearchField.getText()); - } catch (NumberFormatException e) { - - } - for (GrandExchangeOffer o : OfferManager.getOffersForItem(itemId)) { - if (o == null) { - continue; - } - addOffer(o); - } - } - - /** - * Adds a g.e offer. - */ - private void addOffer(GrandExchangeOffer offer) { - model.addElement(offer); - } - - /** - * Displays a database. - */ - private void displayDatabase() { - if (ConsoleFrame.getInstance().getUtilityTab().getPlayers().size() == 0) { - JOptionPane.showMessageDialog(null, "Error! No data in DB yet. Press load."); - return; - } - for (GrandExchangeOffer offer : OfferManager.getOFFER_MAPPING().values()) { - model.addElement(offer); - } - } -} diff --git a/Server/src/main/java/core/gui/tab/PlayerTab.java b/Server/src/main/java/core/gui/tab/PlayerTab.java deleted file mode 100644 index 165220f93..000000000 --- a/Server/src/main/java/core/gui/tab/PlayerTab.java +++ /dev/null @@ -1,187 +0,0 @@ -package core.gui.tab; - - -import rs09.ServerConstants; -import core.gui.ConsoleTab; -import core.gui.component.PlayerViewer; - -import javax.swing.*; -import javax.swing.border.BevelBorder; -import javax.swing.border.SoftBevelBorder; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.util.*; - -/** - * The player tab. - * - * @author Vexia - */ -public class PlayerTab extends ConsoleTab { - - /** - * The serial UID. - */ - private static final long serialVersionUID = 8865322670299347942L; - - /** - * The list of player names to search through. - */ - private final List playerNames = new ArrayList<>(20); - - /** - * The player name text field. - */ - private JTextField textField; - - /** - * The default model. - */ - private final DefaultListModel model = new DefaultListModel(); - - /** - * The tabbed panes. - */ - private final JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP); - - /** - * The mapping of open player viewers. - */ - private final Map viewers = new HashMap<>(); - - /** - * Constructs a new {@Code PlayerTab} {@Code Object} - */ - public PlayerTab() { - super("Players"); - - JScrollPane scrollPane = new JScrollPane(); - scrollPane.setBorder(new SoftBevelBorder(BevelBorder.LOWERED, null, null, null, null)); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); - scrollPane.setBounds(6, 43, 260, 539); - add(scrollPane); - - final JList list = new JList(); - list.addListSelectionListener(e -> { - if (list.getSelectedValue() != null) { - openViewer(list.getSelectedValue()); - } - }); - list.setModel(model); - scrollPane.setViewportView(list); - - JLabel lblSearchForPlayer = new JLabel("Search for player:"); - lblSearchForPlayer.setBounds(6, 15, 143, 16); - add(lblSearchForPlayer); - - textField = new JTextField(); - textField.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - model.clear(); - if (textField.getText() == null || textField.getText().length() < 1) { - populatePlayerSearch(); - return; - } - for (String name : playerNames) { - if (name.toLowerCase().contains(textField.getText())) { - model.addElement(name); - } - } - } - }); - textField.setBounds(117, 9, 149, 28); - add(textField); - textField.setColumns(10); - - tabbedPane.setBounds(278, 43, 755, 539); - add(tabbedPane); - - JButton btnRepopulate = new JButton("Repopulate"); - btnRepopulate.addActionListener(e -> populatePlayerSearch()); - btnRepopulate.setBounds(278, 10, 117, 29); - add(btnRepopulate); - populatePlayerSearch(); - } - - /** - * Opens a viewer for a player. - * - * @param name the name. - * @return {@code True} if opened. - */ - public boolean openViewer(String name) { - if (viewers.containsKey(name)) { - return false; - } - PlayerViewer viewer = new PlayerViewer(name); - viewers.put(name, viewer); - tabbedPane.add(viewer.getName(), viewer); - return true; - } - - /** - * Closes a viewer. - * - * @param viewer the viewer. - * @return {@code True} if closed. - */ - public boolean closeViewer(PlayerViewer viewer) { - if (!viewers.containsValue(viewer)) { - JOptionPane.showMessageDialog(null, "Error! Viewer wasn't open."); - return false; - } - viewers.remove(viewer.getName()); - tabbedPane.remove(viewer); - return true; - } - - /** - * Populates the searchable names. - */ - public void populatePlayerSearch() { - playerNames.clear(); - model.clear(); - if(ServerConstants.PLAYER_SAVE_PATH == null) return; - File f = new File(ServerConstants.PLAYER_SAVE_PATH); - if (f.listFiles() == null) { - System.out.println("Player directory was null!"); - return; - } - for (File file : Objects.requireNonNull(f.listFiles())) { - String fileName = file.getName(); - if (!fileName.contains(".save")) continue; - String playerName = fileName.replace(".save", "").trim(); - playerNames.add(playerName); - model.addElement(playerName); - } - } - - /** - * Adds a searchable name to the list. - * - * @param name the name. - */ - public void addSearchableName(String name) { - playerNames.add(name); - } - - /** - * Gets the playerNames. - * - * @return the playerNames - */ - public List getPlayerNames() { - return playerNames; - } - - /** - * Gets the viewers. - * - * @return the viewers - */ - public Map getViewers() { - return viewers; - } -} diff --git a/Server/src/main/java/core/gui/tab/StatisticsTab.java b/Server/src/main/java/core/gui/tab/StatisticsTab.java deleted file mode 100644 index 67a065b5f..000000000 --- a/Server/src/main/java/core/gui/tab/StatisticsTab.java +++ /dev/null @@ -1,669 +0,0 @@ -package core.gui.tab; - - -import core.game.node.item.GroundItemManager; -import core.game.system.SystemManager; -import core.game.system.SystemState; -import core.game.world.map.RegionManager; -import core.gui.ConsoleTab; -import rs09.Server; -import rs09.ServerConstants; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; - -import javax.swing.*; -import javax.swing.border.TitledBorder; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.StandardOpenOption; - -/** - * Handles server info tab. - * - * @author Emperor - */ -public class StatisticsTab extends ConsoleTab { - - /** - * The serial UID. - */ - private static final long serialVersionUID = 6164020580271944550L; - - /** - * The queue size. - */ - public static final int QUEUE_SIZE = 1 << 16; - - /** - * The performance data queue. - */ - private static short[] performanceQueue = new short[QUEUE_SIZE]; - - /** - * The memory data queue. - */ - private static short[] memoryQueue = new short[QUEUE_SIZE]; - - /** - * The current index of the performance queue. - */ - private static int queueIndex = 0; - - /** - * The current statistics zoom. - */ - private static int statisticsZoom = 5; - - /** - * The singleton. - */ - public static final StatisticsTab INSTANCE = new StatisticsTab(); - - /** - * The statistics mouse coordinates. - */ - private Point statisticMousePoint = null; - - /** - * The zoom in button. - */ - private JButton zoomIn; - - /** - * The zoom out button. - */ - private JButton zoomOut; - - /** - * The reset button. - */ - private JButton resetButton; - - /** - * The save log button. - */ - private JButton saveLogButton; - - /** - * The shutdown button. - */ - private JButton shutdown; - - /** - * The file chooser instance. - */ - private JFileChooser fileChooser; - - /** - * If the tool tip is currently opened. - */ - private boolean toolTipOpened; - - /** - * The world statistics text pane. - */ - private StatsTextPane worldStatistics; - - /** - * The thread statistics text pane. - */ - private StatsTextPane threadStatistics; - - /** - * The working time of the main thread. - */ - private static long workingTime; - - /** - * The maximum amount of players being active at the same time. - */ - private static int maximumPlayers; - - /** - * The text area. - */ - private final JTextArea console = new JTextArea(); - - /** - * Constructs a new {@Code StatisticsTab} {@Code Object} - */ - public StatisticsTab() { - super("Statistics"); - setLayout(null); - - JScrollPane scrollPane = new JScrollPane(); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); - scrollPane.setBackground(UIManager.getColor("Button.background")); - scrollPane.setBounds(6, 403, 1042, 189); - scrollPane.setBorder(new TitledBorder(null, "Console", TitledBorder.LEADING, TitledBorder.TOP, null, null)); - add(scrollPane); - - console.setLineWrap(true); - console.setBackground(UIManager.getColor("CheckBox.background")); - console.setEditable(false); - scrollPane.setViewportView(console); - - JLabel label = new JLabel(); - label.setBounds(450, 345, 163, 16); - add(label); - } - - @Override - public String getToolTipText(MouseEvent e) { - if (e.getX() > getWidth() / 2 && e.getX() < getWidth() - && e.getY() > 2 && e.getY() < getHeight() / 2) { - int max = (getWidth() / 2) / statisticsZoom; - int index = queueIndex - (max - (e.getX() - (getWidth() / 2)) / statisticsZoom); - toolTipOpened = true; - if (index < 0) { - return "Tick: null, time: null."; - } - return new StringBuilder("Tick: ").append(index).append(", time: ").append(performanceQueue[index] + 600).append(".").toString(); - } - toolTipOpened = false; - return null; - } - - @Override - public Point getToolTipLocation(MouseEvent e) { - if (e.getX() > getWidth() / 2 && e.getX() < getWidth() - && e.getY() > 2 && e.getY() < getHeight() / 2) { - return new Point(e.getX() + 15, e.getY() + 10); - } - return null; - } - - @Override - public void paint(Graphics g) { - super.paint(g); - drawPerformanceStatistics(this, g); - } - - /** - * Positions the components. - */ - public void positionComponents() { - int init = 90; - int diff = 5; - zoomIn.setLocation(getWidth() / 2 - (zoomIn.getWidth() / 2) - diff, 5 + init); - zoomOut.setLocation(getWidth() / 2 - (zoomOut.getWidth() / 2) - diff, 30 + init); - resetButton.setLocation(getWidth() / 2 - (resetButton.getWidth() / 2) - diff, 55 + init); - saveLogButton.setLocation(getWidth() / 2 - (saveLogButton.getWidth() / 2) - diff, 80 + init); - shutdown.setLocation(getWidth() / 2 - (shutdown.getWidth() / 2), 310); - worldStatistics.setLocation(55, 310); - threadStatistics.setLocation(shutdown.getX() + shutdown.getWidth() + 35, 310); - } - - /** - * Initializes the server info tab. - * - * @return This instance. - */ - public StatisticsTab init() { - initMaximumPlayers(); - fileChooser = new JFileChooser("./"); - zoomIn = new JButton("+"); - zoomIn.setLayout(null); - zoomIn.setVisible(true); - zoomIn.setSize(50, 20); - zoomIn.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - zoomOut.setEnabled(true); - if (++statisticsZoom > 20) { - statisticsZoom = 20; - zoomIn.setEnabled(false); - return; - } - drawPerformanceStatistics(StatisticsTab.this, getGraphics()); - } - }); - zoomOut = new JButton("-"); - zoomOut.setLayout(null); - zoomOut.setVisible(true); - zoomOut.setSize(50, 20); - zoomOut.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - zoomIn.setEnabled(true); - if (--statisticsZoom < 2) { - statisticsZoom = 2; - zoomOut.setEnabled(false); - return; - } - drawPerformanceStatistics(StatisticsTab.this, getGraphics()); - } - }); - resetButton = new JButton("x"); - resetButton.setLayout(null); - resetButton.setVisible(true); - resetButton.setSize(50, 20); - resetButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - queueIndex = 0; - } - }); - saveLogButton = new JButton("", createImageIcon("Save16.gif")); - saveLogButton.setLayout(null); - saveLogButton.setVisible(true); - saveLogButton.setSize(50, 20); - saveLogButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - int returnVal = fileChooser.showSaveDialog(StatisticsTab.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = fileChooser.getSelectedFile(); - if (!file.getName().contains(".")) { - file = new File(file.getPath() + ".txt"); - } - logQueues(file); - } - } - }); - add(resetButton); - add(zoomIn); - add(zoomOut); - add(saveLogButton); - shutdown = new JButton("Shutdown"); - shutdown.setLayout(null); - shutdown.setVisible(true); - shutdown.setSize(100, 20); - shutdown.addActionListener(e -> SystemManager.flag(SystemState.TERMINATED)); - add(shutdown); - worldStatistics = new StatsTextPane().init(); - worldStatistics.setSize(390, 90); - add(worldStatistics); - updateWorldText(); - threadStatistics = new StatsTextPane().init(); - threadStatistics.setSize(390, 90); - add(threadStatistics); - updateThreadText(); - setLayout(null); - setVisible(true); - ToolTipManager.sharedInstance().setInitialDelay(0); - positionComponents(); - return this; - } - - /** - * Initializes the maximum players count. - */ - private static void initMaximumPlayers() { - setMaximumPlayers(0); - } - - /** - * Sets the maximum amount of players. - * - * @param maximum The maximum. - */ - private static void setMaximumPlayers(int maximum) { - maximumPlayers = maximum; - } - - /** - * Logs the queues. - * - * @param file The file to log to. - */ - protected static void logQueues(File file) { - try (BufferedWriter bw = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8, StandardOpenOption.APPEND)) { - bw.append("/////////////////////////////////////////////////////////////////////////"); - bw.newLine(); - bw.append("/////////////////////////////////////////////////////////////////////////"); - bw.newLine(); - // bw.append("// " + CalenderDate.getFormattedDate() + " performance log results:"); - bw.newLine(); - bw.append("/////////////////////////////////////////////////////////////////////////"); - bw.newLine(); - for (int i = 0; i < queueIndex; i++) { - bw.append(new StringBuilder("performance_report:memory_usage-[tick=").append(i).append(", mem=").append(memoryQueue[i]).append("mb].")); - bw.newLine(); - } - for (int i = 0; i < queueIndex; i++) { - int value = 600 + performanceQueue[i]; - bw.append(new StringBuilder("performance_report:clock_speed-[tick=").append(i).append(", time=").append(value).append(", status=").append(value < 601 ? "NORMAL]." : "DELAYED].")); - bw.newLine(); - } - bw.append("/////////////////////////////////////////////////////////////////////////"); - bw.newLine(); - bw.flush(); - bw.close(); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - /** - * Creates an image icon. - * - * @param path The path of the image file. - * @return The image icon. - */ - private static ImageIcon createImageIcon(String path) { - URL imgURL = StatisticsTab.class.getResource(path); - if (imgURL != null) { - return new ImageIcon(imgURL); - } - return null; - } - - /** - * Reports the performance value. - * - * @param value The value. - */ - public static void reportPerformance(int value) { - if (value >= Short.MAX_VALUE) { - value = Short.MAX_VALUE - 1; - } - secureQueues(); - performanceQueue[queueIndex] = (short) value; - memoryQueue[queueIndex++] = (short) ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1_000_000); - INSTANCE.repaint(); - workingTime += (600 + value); - if (INSTANCE.threadStatistics != null) { - INSTANCE.updateThreadText(); - if ((GameWorld.getTicks() % 10) == 0) { - INSTANCE.updateWorldText(); - } - } - } - - /** - * Updates the thread statistics text. - */ - public void updateThreadText() { - StringBuilder sb = new StringBuilder(); - long runtime = System.currentTimeMillis() - Server.startTime; - double percentage = workingTime / (runtime * 0.01); - long hours = runtime / 3600000; - runtime -= hours * 3600000; - long minutes = runtime / 60000; - runtime -= minutes * 60000; - long seconds = runtime / 1000; - sb.append("Runtime: ").append(hours).append("h ").append(minutes).append("m ").append(seconds).append("s").append(" - ").append(GameWorld.getTicks()).append(" ticks").append("\n"); - sb.append("Working time: ").append(workingTime).append("ms").append(" - ").append(String.format("%.2f", percentage)).append("%").append("\n"); - sb.append("Average cycle: ").append(String.format("%.1f", (double) workingTime / GameWorld.getTicks())).append("ms").append("\n"); - threadStatistics.setText(sb.toString()); - } - - /** - * Updates the world statistics text. - */ - public void updateWorldText() { - int players = Repository.getPlayers().size(); - if (players > getMaximumPlayers()) { - setMaximumPlayers(players); - } - StringBuilder sb = new StringBuilder(); - sb.append("Players: ").append(players).append(", max: ").append(getMaximumPlayers()).append("\n"); - sb.append("NPCs: ").append(Repository.getNpcs().size()).append("\n"); - sb.append("Regions: ").append(RegionManager.getRegionCache().size()).append("\n"); - sb.append("Ground items: ").append(GroundItemManager.getItems().size()).append("\n"); - worldStatistics.setText(sb.toString()); - } - - /** - * Draws the performance statistics. - * - * @param c The component. - * @param g The graphics. - */ - private static void drawPerformanceStatistics(Component c, Graphics g) { - int x = c.getWidth() / 2 + 48; - int y = c.getHeight() / 2; - - Point zero = new Point(x, y / 2); - g.setColor(Color.LIGHT_GRAY); - - //Fill the background rectangle. - g.fillRect(x, 5, c.getWidth() - 4 - x, y - 5); - g.setColor(Color.GRAY); - - int count = 0; - for (int i = 0; i < c.getWidth() - 4 - x; i += statisticsZoom) { - g.drawLine((int) (zero.getX() + i), 5, - (int) (zero.getX() + i), y - 1); - count++; - } - - int space = (y / 2 - 10) / 4; - g.setFont(new Font(null, Font.PLAIN, 9)); - for (int i = 0; i < 5; i++) { - g.setColor(i == 0 ? Color.BLACK : Color.GRAY); - g.drawLine(x, (int) zero.getY() - (space * i), c.getWidth() - 5, (int) zero.getY() - (space * i)); - g.drawLine(x, (int) zero.getY() + (space * i), c.getWidth() - 5, (int) zero.getY() + (space * i)); - g.setColor(Color.BLACK); - g.drawString("" + (i * 150), x - 18, (int) zero.getY() - (space * i) + 2); - if (i != 0) { - g.drawString("-" + (i * 150), x - 20, (int) zero.getY() + (space * i) + 2); - } - } - g.setColor(Color.GREEN); - for (int i = 1; i < count; i++) { - int index = queueIndex - i; - if (index < 1) { - break; - } - int fromPoint = performanceQueue[index]; - int fromX = (count - i) * statisticsZoom; - int fromY = (int) (-fromPoint / (600D / (space * 4))); - int toPoint = index == 0 ? 0 : performanceQueue[index - 1]; - int toX = (count - i - 1) * statisticsZoom; - int toY = (int) (-toPoint / (600D / (space * 4))); - if (fromY < 0) { - g.setColor(Color.RED); - } - g.drawLine((int) (zero.getX() + fromX), (int) (zero.getY() + fromY), - (int) (zero.getX() + toX), (int) (zero.getY() + toY)); - if (fromY >= 0) { - g.setColor(Color.GREEN); - } - } - g.setColor(Color.BLACK); - //Draw vertical line. - g.drawLine(x, y + 4, x, 1); - Point p = ((StatisticsTab) c).statisticMousePoint; - if (p != null) { - g.setColor(new Color(251, 230, 130)); - g.fillRoundRect((int) (p.getX() + 10), (int) (p.getY() - 10), 100, 50, 10, 10); - } - if (((StatisticsTab) c).toolTipOpened) { - ((StatisticsTab) c).setToolTipText("test"); - } - drawMemoryStatistics(c, g); - } - - /** - * Draws the performance statistics. - * - * @param c The component. - * @param g The graphics. - */ - private static void drawMemoryStatistics(Component c, Graphics g) { - int x = 5; - int y = c.getHeight() / 2; - int endX = (c.getWidth() / 2) - 48; - - Point zero = new Point(x + 18, y); - g.setColor(Color.LIGHT_GRAY); - - //Fill the background rectangle. - g.fillRect((int) zero.getX(), 5, endX - x, y - 5); - g.setColor(Color.GRAY); - - int count = 0; - for (int i = 0; i < endX - x; i += statisticsZoom) { - g.drawLine((int) (zero.getX() + i), 5, - (int) (zero.getX() + i), y - 1); - count++; - } - - int space = (y) / 10; - g.setFont(new Font(null, Font.PLAIN, 9)); - for (int i = 0; i < 10; i++) { - g.setColor(Color.GRAY); - g.drawLine((int) zero.getX() - 2, (int) zero.getY() - (space * i), endX + 17, (int) zero.getY() - (space * i)); - g.setColor(Color.BLACK); - g.drawString("" + (i * 100), 2, (int) zero.getY() - (space * i) + 2); - } - g.setColor(Color.BLUE); - for (int i = 1; i < count; i++) { - int index = queueIndex - i; - if (index < 1) { - break; - } - int fromPoint = memoryQueue[index]; - int fromX = (count - i) * statisticsZoom; - int fromY = (int) (fromPoint / (900D / (space * 10))); - int toPoint = index == 0 ? 0 : memoryQueue[index - 1]; - int toX = (count - i - 1) * statisticsZoom; - int toY = (int) (toPoint / (900D / (space * 10))); - if (fromY < 0) { - g.setColor(Color.RED); - } - g.drawLine((int) (zero.getX() + fromX), (int) (zero.getY() - fromY), - (int) (zero.getX() + toX), (int) (zero.getY() - toY)); - if (fromY < 0) { - g.setColor(Color.GREEN); - } - } - g.setColor(Color.BLACK); - //Draw horizontal line. - g.drawLine((int) (zero.getX() - 4), (int) zero.getY(), endX + 21, (int) zero.getY()); - //Draw vertical line. - g.drawLine((int) zero.getX(), (int) zero.getY() + 4, (int) zero.getX(), 1); - Point p = ((StatisticsTab) c).statisticMousePoint; - if (p != null) { - g.setColor(new Color(251, 230, 130)); - g.fillRoundRect((int) (p.getX() + 10), (int) (p.getY() - 10), 100, 50, 10, 10); - } - if (((StatisticsTab) c).toolTipOpened) { - ((StatisticsTab) c).setToolTipText("test"); - } - } - - /** - * Secures the queues. - */ - private static void secureQueues() { - if (queueIndex >= QUEUE_SIZE) { - int lagSpikes = 0; - int memoryUsageSpikes = 0; - @SuppressWarnings("unused") - int totalMemory = 0; - @SuppressWarnings("unused") - int totalCycleTime = 0; - //Start at tick 500, the JVM has to "warm up" first. - for (int i = 500; i < QUEUE_SIZE; i++) { - if (performanceQueue[i] > 0) { //Anything above 0 (-600 + 600) = lag - lagSpikes++; - } - if (memoryQueue[i] > 700) { //Over 700Mb - memoryUsageSpikes++; - } - totalMemory += memoryQueue[i]; - totalCycleTime += performanceQueue[i]; - } - if (lagSpikes > 350 || memoryUsageSpikes > 350) { - logQueues(new File(ServerConstants.LOGS_PATH + "/system/Performance-log.txt")); - } - //System.out.println("Average cycle time: " + (600 + (totalCycleTime / (QUEUE_SIZE - 500))) + "ms."); - //System.out.println("Average memory usage: " + (totalMemory / (QUEUE_SIZE - 500)) + "Mb."); - queueIndex = 0; - } - } - - /** - * Logs a message to the console. - * - * @param message the message. - */ - public void log(String message) { - console.append(message + "\n"); - console.setCaretPosition(console.getDocument().getLength()); - } - - /** - * Sets the statistics zoom. - * - * @param pixels The amount of pixels. - */ - public static void setStatisticsZoom(int pixels) { - statisticsZoom = pixels; - } - - /** - * Gets the workingTime. - * - * @return The workingTime. - */ - public static long getWorkingTime() { - return workingTime; - } - - /** - * Sets the workingTime. - * - * @param workingTime The workingTime to set. - */ - public static void setWorkingTime(long workingTime) { - StatisticsTab.workingTime = workingTime; - } - - /** - * Gets the maximumPlayers. - * - * @return The maximumPlayers. - */ - public static int getMaximumPlayers() { - return maximumPlayers; - } - - /** - * Handles the statistics tab text pane. - * - * @author Emperor - */ - public final class StatsTextPane extends JTextPane { - - /** - * The serial UID. - */ - private static final long serialVersionUID = 664276151176087663L; - - /** - * Constructs a new {@code StatsTextPane} {@code Object}. - */ - public StatsTextPane() { - super(); - } - - /** - * Initializes the stats text pane. - * - * @return The stats text pane. - */ - public StatsTextPane init() { - setEditable(false); - setLayout(null); - Font font = new Font("Monospaced", Font.PLAIN, 12); - setFont(font); - super.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); - super.setVisible(true); - return this; - } - - } -} diff --git a/Server/src/main/java/core/gui/tab/UtilityTab.java b/Server/src/main/java/core/gui/tab/UtilityTab.java deleted file mode 100644 index d2c07a0b8..000000000 --- a/Server/src/main/java/core/gui/tab/UtilityTab.java +++ /dev/null @@ -1,600 +0,0 @@ -package core.gui.tab; - - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.PlayerDetails; -import core.game.node.entity.player.info.Rights; -import core.game.node.item.Item; -import core.game.system.task.TaskExecutor; -import core.gui.ConsoleFrame; -import core.gui.ConsoleTab; -import core.tools.PlayerLoader; -import core.tools.StringUtils; -import kotlin.Unit; - -import javax.swing.*; -import javax.swing.border.EtchedBorder; -import javax.swing.border.TitledBorder; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.NumberFormat; -import java.util.*; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Handles the utility programs. - * - * @author Vexia - */ -public final class UtilityTab extends ConsoleTab { - /** - * The {@link Logger} instance. - */ - private static final Logger logger = Logger.getLogger(UtilityTab.class.getName()); - /** - * The utility tab. - */ - private static final UtilityTab INSTANCE = new UtilityTab(); - - /** - * The serial UID. - */ - private static final long serialVersionUID = -4962790192758757624L; - - /** - * The list of players. - */ - private final List players = new ArrayList<>(20); - - /** - * The mapping of ips & their users. - */ - private final Map> IPS = new HashMap<>(); - - /** - * The mapping of macs & their users. - */ - private final Map> MACS = new HashMap<>(); - - /** - * The mapping of comp names & their users. - */ - private final Map> COMPS = new HashMap<>(); - - /** - * The players loaded list. - */ - private final JLabel lblPlayersLoaded = new JLabel("Players loaded: "); - - /** - * If the map is being populated. - */ - private boolean populating; - - /** - * Constructs a new {@Code UtilityTab} {@Code Object} - */ - public UtilityTab() { - super("Utilities"); - - JPanel panel = new JPanel(); - panel.setBorder(new TitledBorder(null, "Resolvers & Trackers", TitledBorder.LEADING, TitledBorder.TOP, null, null)); - panel.setBackground(UIManager.getColor("Button.background")); - panel.setBounds(18, 98, 152, 219); - add(panel); - panel.setLayout(null); - JButton btnResolveIp = new JButton("Resolve Ip"); - btnResolveIp.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - resolve(IPS); - } - }); - btnResolveIp.setBounds(17, 28, 117, 29); - panel.add(btnResolveIp); - - JButton btnResolveMac = new JButton("Resolve Mac"); - btnResolveMac.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - resolve(MACS); - } - }); - btnResolveMac.setBounds(17, 69, 117, 29); - panel.add(btnResolveMac); - - JButton btnResolveCompName = new JButton("Resolve Comp"); - btnResolveCompName.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - resolve(COMPS); - } - }); - btnResolveCompName.setBounds(17, 110, 117, 29); - panel.add(btnResolveCompName); - - JButton btnPopulateMaps = new JButton("Populate Maps"); - btnPopulateMaps.addActionListener(e -> { - populateList(); - JOptionPane.showMessageDialog(null, "Finished populating maps!"); - }); - btnPopulateMaps.setBounds(405, 32, 236, 29); - add(btnPopulateMaps); - - JLabel lblMustBeDone = new JLabel("MUST BE DONE BEFORE RUNNING TOOLS"); - lblMustBeDone.setBounds(390, 6, 379, 16); - add(lblMustBeDone); - - lblPlayersLoaded.setBounds(460, 61, 157, 16); - add(lblPlayersLoaded); - - JPanel panel_1 = new JPanel(); - panel_1.setLayout(null); - panel_1.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "Wealth & Items", TitledBorder.LEADING, TitledBorder.TOP, null, null)); - panel_1.setBackground(UIManager.getColor("Button.background")); - panel_1.setBounds(182, 98, 145, 219); - add(panel_1); - - JButton btnWealthScanner = new JButton("Wealth Scanner"); - btnWealthScanner.addActionListener(e -> wealthScanner()); - btnWealthScanner.setBounds(6, 29, 129, 29); - panel_1.add(btnWealthScanner); - - JButton btnItemScanner = new JButton("Item Scanner"); - btnItemScanner.addActionListener(e -> itemScanner()); - btnItemScanner.setBounds(6, 69, 129, 29); - panel_1.add(btnItemScanner); - - JPanel panel_2 = new JPanel(); - panel_2.setLayout(null); - panel_2.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "Player Utils", TitledBorder.LEADING, TitledBorder.TOP, null, null)); - panel_2.setBackground(UIManager.getColor("Button.background")); - panel_2.setBounds(345, 98, 145, 219); - add(panel_2); - - JButton btnStaffChecker = new JButton("Staff Scanner"); - btnStaffChecker.addActionListener(e -> staffScanner()); - btnStaffChecker.setBounds(6, 29, 129, 29); - panel_2.add(btnStaffChecker); - - JPanel panel_3 = new JPanel(); - panel_3.setLayout(null); - panel_3.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "Security", TitledBorder.LEADING, TitledBorder.TOP, null, null)); - panel_3.setBackground(UIManager.getColor("Button.background")); - panel_3.setBounds(502, 98, 145, 219); - add(panel_3); - - JButton btnMacViewer = new JButton("Mac Viewer"); - btnMacViewer.addActionListener(e -> viewMacs()); - btnMacViewer.setBounds(6, 29, 129, 29); - panel_3.add(btnMacViewer); - - JButton btnMacRemover = new JButton("Mac Remover"); - btnMacRemover.addActionListener(e -> removeMac()); - btnMacRemover.setBounds(6, 64, 129, 29); - panel_3.add(btnMacRemover); - } - - /** - * Removes a mac. - */ - public void removeMac() { - String mac = JOptionPane.showInputDialog("Enter address:"); - if (mac == null || mac.length() <= 1) { - JOptionPane.showMessageDialog(null, "Error! Nothing entered."); - return; - } -// if (SystemManager.getSecurity().isBanned(mac)) { -// SystemManager.getSecurity().remove(mac); -// JOptionPane.showMessageDialog(null, "Removed the mac - " + mac + "!"); -// } else { -// JOptionPane.showMessageDialog(null, "Error! Couldn't find mac in system manager."); -// } - } - - /** - * Views the banned macs. - */ - public void viewMacs() { - JPanel panel = new JPanel(new GridLayout(0, 1)); - JScrollPane pane = new JScrollPane(); - DefaultListModel model = new DefaultListModel(); - JList list = new JList(); -// for (String i : SystemManager.getSecurity().getBannedAddresses()) { -// if (i == null) { -// continue; -// } -// model.addElement(i.toString()); -// } - list.setModel(model); - pane.setViewportView(list); - panel.add(pane); - JOptionPane.showConfirmDialog(null, panel, "Banned Addresses", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); - } - - /** - * Wealth scanner. - */ - public void wealthScanner() { - String args = JOptionPane.showInputDialog("Enter item id:"); - int id = -1; - if (args != null && args.length() > 0) { - try { - id = Integer.parseInt(args); - } catch (NumberFormatException e) { - id = -1; - logger.log(Level.WARNING, "Error in wealth scanner, number exception!"); - } - } - List data = wealthScanner(id); - JPanel panel = new JPanel(new GridLayout(0, 1)); - JScrollPane pane = new JScrollPane(); - DefaultListModel model = new DefaultListModel(); - JList list = new JList(); - for (String i : data) { - if (i == null) { - continue; - } - model.addElement(i.toString()); - } - list.setModel(model); - pane.setViewportView(list); - panel.add(pane); - JOptionPane.showConfirmDialog(null, panel, "Wealth Scanner", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); - - } - - /** - * Wealth scanner. - */ - public List wealthScanner(int itemId) { - Item item = null; - if (itemId != -1) { - item = new Item(itemId); - } - Object[][] mostWealthy = new Object[50][2]; - int player = 0; - long count = 0; - for (Player p : players) { - if (p.getDetails().getRights() == Rights.PLAYER_MODERATOR) { - continue; - } - if (item != null) { - long old = count; - count += p.getInventory().getAmount(item); - count += p.getEquipment().getAmount(item); - count += p.getBank().getAmount(item); - if (old != count) { - player++; - } - } - for (int i = 0; i < mostWealthy.length; i++) { - Long value = (Long) mostWealthy[i][0]; - if (value == null || value < p.getMonitor().getNetworth()) { - for (int j = mostWealthy.length - 1; j > i; j--) { - mostWealthy[j] = mostWealthy[j - 1]; - } - mostWealthy[i] = new Object[2]; - mostWealthy[i][0] = p.getMonitor().getNetworth(); - mostWealthy[i][1] = p.getName(); - break; - } - } - } - List formats = new ArrayList<>(20); - for (int i = mostWealthy.length - 1; i >= 0; i--) { - Object[] info = mostWealthy[i]; - if (info != null && info[0] != null) { - formats.add(i + ": " + info[1] + " - " + NumberFormat.getNumberInstance(Locale.US).format((Long) info[0]) + " coins."); - } - } - if (item != null) { - formats.add(NumberFormat.getNumberInstance(Locale.US).format(count) + " occurrences of item " + item.getName() + " (id=" + item.getId() + ") by " + player + " players!"); - } - return formats; - } - - /** - * The item scanner. - */ - public void itemScanner() { - String args = JOptionPane.showInputDialog("Enter item id:"); - Item item = new Item(11694); - if (args != null && args.length() > 0) { - try { - item = new Item(Integer.parseInt(args)); - } catch (NumberFormatException e) { - item = new Item(11694); - logger.log(Level.WARNING, "Error in wealth scanner, number exception!"); - } - } - List formats = itemScanner(item); - JPanel panel = new JPanel(new GridLayout(0, 1)); - JScrollPane pane = new JScrollPane(); - DefaultListModel model = new DefaultListModel(); - JList list = new JList(); - for (String i : formats) { - if (i == null) { - continue; - } - model.addElement(i.toString()); - } - list.setModel(model); - pane.setViewportView(list); - panel.add(pane); - JOptionPane.showConfirmDialog(null, panel, "Item Scanner", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); - } - - /** - * Scans for items in the eco. - */ - public List itemScanner(Item item) { - Map map = new HashMap<>(); - for (Player p : players) { - List containers = new ArrayList<>(20); - containers.add(p.getInventory()); - containers.add(p.getBank()); - containers.add(p.getEquipment()); - int amount = 0; - for (core.game.container.Container container : containers) { - Item i = container.getItem(item); - if (i != null) { - amount += i.getAmount(); - } - } - if (amount > 0) { - map.put(p.getName(), amount); - } - } - List formats = new ArrayList<>(20); - for (Map.Entry entry : map.entrySet()) { - formats.add("Player name=" + entry.getKey() + ", contained a total of " + entry.getValue() + " " + item.getName() + "."); - } - if (formats.size() == 0) { - formats.add("There were no occurrences of " + item + "."); - } - return formats; - } - - /** - * Resolves the address. - * - * @param map the map. - */ - public void resolve(Map> map) { - String address = JOptionPane.showInputDialog("Enter the address."); - List names = resolve(map, address, false); - String string = "No names found registered with that address!"; - if (names == null) { - JOptionPane.showMessageDialog(null, string); - } else { - string = "Names:" + Arrays.toString(names.toArray()); - JOptionPane.showMessageDialog(null, string); - } - } - - /** - * Resolves & tracks an adddress. - * - * @param map the map. - * @param value the value. - * @param name if the name or not. - * @return the names resolved. - */ - public List resolve(Map> map, String value, boolean name) { - List names = null; - String address = !name ? value : ""; - if (name) { - for (Map.Entry> entry : map.entrySet()) { - for (String s : entry.getValue()) { - if (s.equals(value)) { - address = entry.getKey(); - break; - } - } - } - } - names = map.get(address); - return names; - } - - /** - * Gets the mapping. - * - * @param id the id. - * @return the map. - */ - public Map> getMap(int id) { - return id == 1 ? IPS : id == 2 ? MACS : COMPS; - } - - /** - * Populates the lists. - */ - public void populateList() { - if (populating) { - return; - } - populating = true; - players.clear(); - TaskExecutor.execute(() -> { - Thread.currentThread().setName("Utility Tab"); - ConsoleFrame.getInstance().getPlayerTab().getPlayerNames().clear(); - ConsoleFrame.getInstance().getPlayerTab().populatePlayerSearch(); - for (String name : ConsoleFrame.getInstance().getPlayerTab().getPlayerNames()) { - Player player = PlayerLoader.getPlayerFile(name); - if (player == null) { - continue; - } - PlayerDetails details = player.getDetails(); - if (details != null) { - addAddressToMap(name, details.getIpAddress(), IPS); - addAddressToMap(name, details.getMacAddress(), MACS); - addAddressToMap(name, details.getCompName(), COMPS); - players.add(player); - } - } - populating = false; - System.gc(); - lblPlayersLoaded.setText("Players loaded: " + players.size()); - return Unit.INSTANCE; - }); - } - - /** - * Checks the staff. - */ - public void staffScanner() { - List staff = new ArrayList<>(20); - for (Player player : players) { - if (player.getDetails().getRights() != Rights.REGULAR_PLAYER) { - staff.add(player.getName() + " - " + StringUtils.formatDisplayName(player.getDetails().getRights().toString())); - } - } - JPanel panel = new JPanel(new GridLayout(0, 1)); - JScrollPane pane = new JScrollPane(); - DefaultListModel model = new DefaultListModel(); - JList list = new JList(); - for (String i : staff) { - if (i == null) { - continue; - } - model.addElement(i.toString()); - } - list.setModel(model); - pane.setViewportView(list); - panel.add(pane); - JOptionPane.showConfirmDialog(null, panel, "Staff Scanner", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); - } - - /** - * Adds an address with its names to the map. - * - * @param name the name. - * @param address the address. - * @param map the map. - */ - public void addAddressToMap(String name, String address, Map> map) { - if (address == null) { - return; - } - List names = map.get(address); - if (names == null) { - names = new ArrayList(); - } - names.add(name); - map.put(address, names); - } - - /** - * Deletes a player. - * - * @param details the details. - */ - public void delete(PlayerDetails details) { - if (details.getRights() == Rights.PLAYER_MODERATOR) { - return; - } - // TODO -// Connection connection = SQLManager.getConnection(); -// PreparedStatement statement; -// try { -// statement = connection.prepareStatement("DELETE FROM " + "playerdata" + " WHERE " + "" + "username" + "='" + details.getUsername().toLowerCase() + "'"); -// statement.execute(); -// statement = connection.prepareStatement("DELETE FROM " + "highscores" + " WHERE " + "" + "username" + "='" + details.getUsername().toLowerCase() + "'"); -// statement.execute(); -// } catch (SQLException e) { -// e.printStackTrace(); -// } -// -// File detailFile = new File(GameConstants.getPlayerDetailsPath() + "/" + details.getUsername() + ".store"); -// File playerFile = new File(GameConstants.getPlayerSavePath() + "/" + details.getUsername() + ".store"); -// detailFile.delete(); -// playerFile.delete(); -// SQLManager.close(connection); -// ConsoleFrame.getInstance().getPlayerTab().getPlayerNames().remove(details.getUsername()); - - } - - /** - * Gets the player. - * - * @param name the name. - * @return the player. - */ - public Player getPlayer(String name) { - if (players.size() == 0) { - populateList(); - return null; - } - for (Player p : players) { - if (p.getName().equals(name)) { - return p; - } - } - return null; - } - - /** - * Gets the ips. - * - * @return the ips. - */ - public Map> getIps() { - return IPS; - } - - /** - * Gets the macs. - * - * @return the macs. - */ - public Map> getMacs() { - return MACS; - } - - /** - * Gets the comps. - * - * @return the comps. - */ - public Map> getComps() { - return COMPS; - } - - /** - * Gets the players. - * - * @return the players. - */ - public List getPlayers() { - return players; - } - - /** - * Gets the populating. - * - * @return the populating - */ - public boolean isPopulating() { - return populating; - } - - /** - * Sets the bapopulating. - * - * @param populating the populating to set. - */ - public void setPopulating(boolean populating) { - this.populating = populating; - } - - /** - * Gets the instance. - * - * @return the instance - */ - public static UtilityTab getInstance() { - return INSTANCE; - } -} diff --git a/Server/src/main/java/core/net/IoEventHandler.java b/Server/src/main/java/core/net/IoEventHandler.java deleted file mode 100644 index 93adb6918..000000000 --- a/Server/src/main/java/core/net/IoEventHandler.java +++ /dev/null @@ -1,107 +0,0 @@ -package core.net; - -import rs09.game.system.SystemLogger; -import rs09.game.world.repository.DisconnectionQueue; -import rs09.game.world.repository.Repository; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.concurrent.ExecutorService; - -/** - * I/O event handling. - * @author Emperor - */ -public class IoEventHandler { - - /** - * The executor service. - */ - protected final ExecutorService service; - - /** - * Constructs a new {@code IoEventHandler}. - * @param service The executor service used for handling events. - */ - public IoEventHandler(ExecutorService service) { - this.service = service; - } - - /** - * Called when making a new connection. - * @param key The selection key. - * @throws IOException When an I/O exception occurs. - */ - public void connect(SelectionKey key) throws IOException { - /* - * empty. - */ - } - - /** - * Used for accepting a new connection. - * @param key The selection key. - * @param selector The selector. - * @throws IOException When an I/O exception occurs. - */ - public void accept(SelectionKey key, Selector selector) throws IOException { - SocketChannel sc = ((ServerSocketChannel) key.channel()).accept(); - sc.configureBlocking(false); - sc.socket().setTcpNoDelay(true); - sc.register(selector, SelectionKey.OP_READ); - } - - /** - * Reads the incoming packet data. - * @param key The selection key. - * @throws IOException When an I/O exception occurs. - */ - public void read(SelectionKey key) throws IOException { - ReadableByteChannel channel = (ReadableByteChannel) key.channel(); - ByteBuffer buffer = ByteBuffer.allocate(100_000); - IoSession session = (IoSession) key.attachment(); - if (channel.read(buffer) == -1) { - if(session != null && session.getPlayer() != null){ - Repository.getDisconnectionQueue().add(session.getPlayer()); - } - key.cancel(); - return; - } - buffer.flip(); - if (session == null) { - key.attach(session = new IoSession(key, service)); - } - service.execute(session.getProducer().produceReader(session, buffer)); - } - - /** - * Writes the outgoing packet data. - * @param key The selection key. - */ - public void write(SelectionKey key) { - IoSession session = (IoSession) key.attachment(); - key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); - session.write(); - } - - /** - * Disconnects a connection. - * @param key The selection key. - * @param t The occurred exception (if any). - */ - public void disconnect(SelectionKey key, Throwable t) { - try { - IoSession session = (IoSession) key.attachment(); - String cause = "" + t; - if (t != null && !(t instanceof ClosedChannelException || cause.contains("De externe host") || cause.contains("De software op uw") || cause.contains("An established connection was aborted") || cause.contains("An existing connection") || cause.contains("AsynchronousClose"))) { - t.printStackTrace(); - } - if (session != null) { - session.disconnect(); - } - } catch (Throwable e) { - e.printStackTrace(); - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/IoSession.java b/Server/src/main/java/core/net/IoSession.java deleted file mode 100644 index 195cc0089..000000000 --- a/Server/src/main/java/core/net/IoSession.java +++ /dev/null @@ -1,437 +0,0 @@ -package core.net; - -import core.cache.crypto.ISAACPair; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.ClientInfo; -import core.game.node.entity.player.info.login.Response; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.net.producer.HSEventProducer; -import core.net.producer.LoginEventProducer; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Represents a connected I/O session. - * @author Emperor - */ -public class IoSession { - - /** - * The handshake event producer. - */ - private static final EventProducer HANDSHAKE_PRODUCER = new HSEventProducer(); - - /** - * The selection key. - */ - private final SelectionKey key; - - public int isaacInputOpcode = 0; - - /** - * The executor service. - */ - private final ExecutorService service; - - /** - * The event producer. - */ - private EventProducer producer = HANDSHAKE_PRODUCER; - - /** - * The currently queued writing data. - */ - private List writingQueue = new ArrayList<>(20); - - /** - * The currently queued reading data. - */ - private ByteBuffer readingQueue; - - /** - * The writing lock. - */ - private Lock writingLock = new ReentrantLock(); - - /** - * The ISAAC cipher pair. - */ - private ISAACPair isaacPair; - - /** - * The name hash. - */ - private int nameHash; - - /** - * The server key. - */ - private long serverKey; - - /** - * The JS-5 encryption value. - */ - private int js5Encryption; - - /** - * The object. - */ - private Object object; - - /** - * If the session is active. - */ - private boolean active = true; - - /** - * The last ping time stamp. - */ - private long lastPing; - - /** - * The address. - */ - private final String address; - - /** - * The JS-5 queue. - */ - private final JS5Queue js5Queue; - - /** - * The client info. - */ - private ClientInfo clientInfo; - - /** - * Constructs a new {@code IoSession}. - * @param key The selection key. - * @param service The executor service. - */ - public IoSession(SelectionKey key, ExecutorService service) { - this.key = key; - this.service = service; - this.address = getRemoteAddress().replaceAll("/", "").split(":")[0]; - this.js5Queue = new JS5Queue(this); - } - - /** - * Fires a write event created using the current event producer. - * @param context The event context. - */ - public void write(Object context) { - write(context, false); - } - - /** - * Fires a write event created using the current event producer. - * @param context The event context. - * @param instant If the event should be instantly executed on this thread. - */ - public void write(Object context, boolean instant) { - if (context == null) { - throw new IllegalStateException("Invalid writing context!"); - } - if (!(context instanceof Response) && producer instanceof LoginEventProducer) { - // new Throwable().printStackTrace(); - return; - } - if (instant) { - producer.produceWriter(this, context).run(); - return; - } - service.execute(producer.produceWriter(this, context)); - } - - /** - * Sends the packet data (without write event encoding). - * @param buffer The buffer. - */ - public void queue(ByteBuffer buffer) { - try { - writingLock.tryLock(1000L, TimeUnit.MILLISECONDS); - } catch (Exception e){ - e.printStackTrace(); - writingLock.unlock(); - } - writingQueue.add(buffer); - writingLock.unlock(); - write(); - } - - /** - * Handles the writing of all buffers in the queue. - */ - public void write() { - if (!key.isValid()) { - disconnect(); - return; - } - try { - writingLock.tryLock(1000L, TimeUnit.MILLISECONDS); - } catch (Exception e){ - e.printStackTrace(); - writingLock.unlock(); - return; - } - SocketChannel channel = (SocketChannel) key.channel(); - try { - while (!writingQueue.isEmpty()) { - ByteBuffer buffer = writingQueue.get(0); - channel.write(buffer); - if (buffer.hasRemaining()) { - key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); - break; - } - writingQueue.remove(0); - } - } catch (IOException e) { - disconnect(); - } - writingLock.unlock(); - } - - /** - * Disconnects the session. - */ - public void disconnect() { - try { - if (!active) { - 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; - } - }); - } - object = null; - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Gets the IP-address (without the port). - * @return The address. - */ - public String getAddress() { - return address; - } - - /** - * Gets the remote address of this session. - * @return The remote address, as a String. - */ - public String getRemoteAddress() { - try { - return ((SocketChannel) key.channel()).getRemoteAddress().toString(); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - /** - * Gets the current event producer. - * @return The producer. - */ - public EventProducer getProducer() { - return producer; - } - - /** - * Sets the event producer. - * @param producer The producer to set. - */ - public void setProducer(EventProducer producer) { - this.producer = producer; - } - - /** - * Gets the queued reading data. - * @return The readingQueue. - */ - public ByteBuffer getReadingQueue() { - synchronized (this) { - return readingQueue; - } - } - - /** - * Queues reading data. - * @param readingQueue The readingQueue to set. - */ - public void setReadingQueue(ByteBuffer readingQueue) { - synchronized (this) { - this.readingQueue = readingQueue; - } - } - - /** - * Gets the writing lock. - * @return The writing lock. - */ - public Lock getWritingLock() { - return writingLock; - } - - /** - * Gets the selection key. - * @return The selection key. - */ - public SelectionKey getKey() { - return key; - } - - /** - * @return The active. - */ - public boolean isActive() { - return active; - } - - /** - * @return The js5Encryption. - */ - public int getJs5Encryption() { - return js5Encryption; - } - - /** - * @param js5Encryption The js5Encryption to set. - */ - public void setJs5Encryption(int js5Encryption) { - this.js5Encryption = js5Encryption; - } - - /** - * @return The player. - */ - public Player getPlayer() { - return (Player) object; - } - - /** - * Gets the object. - * @return the object. - */ - public Object getObject() { - return object; - } - - /** - * @param object The object to set. - */ - public void setObject(Object object) { - this.object = object; - } - - /** - * Gets the lastPing. - * @return The lastPing. - */ - public long getLastPing() { - return lastPing; - } - - /** - * Sets the lastPing. - * @param lastPing The lastPing to set. - */ - public void setLastPing(long lastPing) { - this.lastPing = lastPing; - } - - /** - * Gets the nameHash. - * @return The nameHash. - */ - public int getNameHash() { - return nameHash; - } - - /** - * Sets the nameHash. - * @param nameHash The nameHash to set. - */ - public void setNameHash(int nameHash) { - this.nameHash = nameHash; - } - - /** - * Gets the serverKey. - * @return The serverKey. - */ - public long getServerKey() { - return serverKey; - } - - /** - * Sets the serverKey. - * @param serverKey The serverKey to set. - */ - public void setServerKey(long serverKey) { - this.serverKey = serverKey; - } - - /** - * Gets the isaacPair. - * @return The isaacPair. - */ - public ISAACPair getIsaacPair() { - return isaacPair; - } - - /** - * Sets the isaacPair. - * @param isaacPair The isaacPair to set. - */ - public void setIsaacPair(ISAACPair isaacPair) { - this.isaacPair = isaacPair; - } - - /** - * Gets the js5Queue. - * @return the js5Queue - */ - public JS5Queue getJs5Queue() { - return js5Queue; - } - - /** - * Gets the clientInfo value. - * @return The clientInfo. - */ - public ClientInfo getClientInfo() { - return clientInfo; - } - - /** - * Sets the clientInfo value. - * @param clientInfo The clientInfo to set. - */ - public void setClientInfo(ClientInfo clientInfo) { - this.clientInfo = clientInfo; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/NioReactor.java b/Server/src/main/java/core/net/NioReactor.java deleted file mode 100644 index 42fea5a1d..000000000 --- a/Server/src/main/java/core/net/NioReactor.java +++ /dev/null @@ -1,148 +0,0 @@ -package core.net; - -import core.net.amsc.MSEventHandler; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.Iterator; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Handles (NIO-based) networking events using the reactor pattern. - * @author Emperor - */ -public final class NioReactor implements Runnable { - - /** - * The executor service. - */ - private final ExecutorService service; - - /** - * The socket channel. - */ - private ServerSocketConnection channel; - - /** - * The I/O event handling instance. - */ - private IoEventHandler eventHandler; - - /** - * If the reactor is running. - */ - private boolean running; - - /** - * Constructs a new {@code NioReactor}. - */ - private NioReactor(IoEventHandler eventHandler) { - this.service = Executors.newSingleThreadScheduledExecutor(); - this.eventHandler = eventHandler; - } - - /** - * Creates and configures a new {@code NioReactor} with a pool size of 1. - * @param port The port. - * @return The {@code NioReactor} {@code Object}. - * @throws IOException When an I/O exception occurs. - */ - public static NioReactor configure(int port) throws IOException { - return configure(port, 1); - } - - /** - * Creates and configures a new {@code NioReactor}. - * @param port The port. - * @param poolSize The amount of threads in the thread pool. - * @return The {@code NioReactor} {@code Object}. - * @throws IOException When an I/O exception occurs. - */ - public static NioReactor configure(int port, int poolSize) throws IOException { - NioReactor reactor = new NioReactor(new IoEventHandler(Executors.newFixedThreadPool(poolSize))); - ServerSocketChannel channel = ServerSocketChannel.open(); - Selector selector = Selector.open(); - channel.bind(new InetSocketAddress(port)); - channel.configureBlocking(false); - channel.register(selector, SelectionKey.OP_ACCEPT); - reactor.channel = new ServerSocketConnection(selector, channel); - return reactor; - } - - /** - * Starts a NIO reactor used for client connections. - * @param address The IP-address to connect to. - * @param port The port used. - * @return The NIO reactor object. - * @throws IOException When an exception occurs. - */ - public static NioReactor connect(String address, int port) throws IOException { - NioReactor reactor = new NioReactor(new MSEventHandler()); - Selector selector = Selector.open(); - SocketChannel channel = SocketChannel.open(); - channel.configureBlocking(false); - channel.socket().setKeepAlive(true); - channel.socket().setTcpNoDelay(true); - channel.connect(new InetSocketAddress(address, port)); - channel.register(selector, SelectionKey.OP_CONNECT); - reactor.channel = new ServerSocketConnection(selector, channel); - return reactor; - } - - /** - * Starts the reactor. - */ - public void start() { - running = true; - service.execute(this); - } - - @Override - public void run() { - Thread.currentThread().setName("NioReactor"); - while (running) { - try { - channel.getSelector().select(); - } catch (IOException e) { - e.printStackTrace(); - } - Iterator iterator = channel.getSelector().selectedKeys().iterator(); - while (iterator.hasNext()) { - SelectionKey key = iterator.next(); - iterator.remove(); - try { - if (!key.isValid() || !key.channel().isOpen()) { - key.cancel(); - continue; - } - if (key.isConnectable()) { - eventHandler.connect(key); - } - if (key.isAcceptable()) { - eventHandler.accept(key, channel.getSelector()); - } - if (key.isReadable()) { - eventHandler.read(key); - } else if (key.isWritable()) { - eventHandler.write(key); - } - } catch (Throwable t) { - eventHandler.disconnect(key, t); - } - } - } - } - - /** - * Terminates the reactor (once it's done processing current I/O events). - */ - public void terminate() { - running = false; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/amsc/MSPacketRepository.java b/Server/src/main/java/core/net/amsc/MSPacketRepository.java deleted file mode 100644 index 0917a4281..000000000 --- a/Server/src/main/java/core/net/amsc/MSPacketRepository.java +++ /dev/null @@ -1,663 +0,0 @@ -package core.net.amsc; - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.PlayerDetails; -import core.game.node.entity.player.info.Rights; -import core.game.node.entity.player.info.login.Response; -import core.game.system.SystemManager; -import core.game.system.SystemState; -import core.game.system.communication.*; -import core.net.packet.IoBuffer; -import core.net.packet.PacketHeader; -import core.net.packet.PacketRepository; -import core.net.packet.context.ClanContext; -import core.net.packet.context.ContactContext; -import core.net.packet.context.MessageContext; -import core.net.packet.out.CommunicationMessage; -import core.net.packet.out.ContactPackets; -import core.net.packet.out.UpdateClanChat; -import rs09.game.node.entity.player.info.login.LoginParser; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; - -import java.nio.ByteBuffer; - -/** - * The Management server packet repository. - * @author Emperor - */ -public final class MSPacketRepository { - - /** - * Handles a player info update. - * - */ - public static void sendInfoUpdate(Player player) { - IoBuffer buffer = new IoBuffer(14, PacketHeader.BYTE); - buffer.putString(player.getName()); - buffer.put(Rights.getChatIcon(player)); - WorldCommunicator.getSession().write(buffer); - player.getAppearance().sync(); - } - - /** - * Registers a player on the management server. - * - * @param parser The login. - */ - public static void sendPlayerRegistry(LoginParser parser) { - IoBuffer buffer = new IoBuffer(0, PacketHeader.BYTE); - PlayerDetails d = parser.getDetails(); - buffer.putString(d.getUsername()); - buffer.putString(d.getPassword()); - buffer.putString(d.getIpAddress()); - buffer.putString(d.getMacAddress()); - buffer.putString(d.getCompName()); - buffer.putString(d.getSerial()); - buffer.putInt(d.getRights().toInteger()); - buffer.put((byte) getIcon(d)); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sends the player removal packet. - * - * @param username The name of the player to remove. - */ - public static void sendPlayerRemoval(String username) { - if (!WorldCommunicator.isEnabled()) { - return; - } - IoBuffer buffer = new IoBuffer(1, PacketHeader.BYTE); - buffer.putString(username); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sends the punishment packet to the MS. - * - * @param player The player punishing the other player. - * @param name The name of the player to punish. - * @param type The punishment type (0: mute, 1: ban, 2: ip-ban, 3: mac-ban, - * 4: msk-ban) - * @param duration The duration of the punishment (in milliseconds, -1 = - * permanent). - */ - public static void sendPunishment(Player player, String name, int type, long duration) { - IoBuffer buffer = new IoBuffer(2, PacketHeader.BYTE); - buffer.put(type); - buffer.putString(name); - buffer.putLong(duration); - buffer.putString(player.getName()); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Requests the communication info for the given player. - * - * @param username The username. - */ - public static void requestCommunicationInfo(String username) { - IoBuffer buffer = new IoBuffer(3, PacketHeader.BYTE); - buffer.putString(username); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sends a contact update. - * - * @param username The username. - * @param contact The contact's username. - * @param remove If we're removing the contact from the list. - * @param block If the contact list is for the blocked players. - * @param rank The new clan rank (or null when not updating clan rank!). - */ - public static void sendContactUpdate(String username, String contact, boolean remove, boolean block, ClanRank rank) { - IoBuffer buffer = new IoBuffer(block ? 5 : 4, PacketHeader.BYTE); - buffer.putString(username); - buffer.putString(contact); - if (rank != null) { - buffer.put((byte) 2); - buffer.put((byte) rank.ordinal()); - } else { - buffer.put((byte) (remove ? 1 : 0)); - } - WorldCommunicator.getSession().write(buffer); - } - - /** - * Joins a clan. - * - * @param player The player joining a clan. - * @param name The clan's owner name. - */ - public static void sendJoinClan(Player player, String name) { - if (name.length() > 0 && !ClanRepository.getClans().containsKey(name)) { - requestClanInfo(name); - } - IoBuffer buffer = new IoBuffer(6, PacketHeader.BYTE); - buffer.putString(player.getName()); - buffer.putString(name); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sends the clan rename packet. - * - * @param player The player. - * @param clanName The clan name. - */ - public static void sendClanRename(Player player, String clanName) { - IoBuffer buffer = new IoBuffer(7, PacketHeader.BYTE); - buffer.putString(player.getName()); - buffer.putString(clanName); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sets a clan setting. - * - * @param player The player. - * @param type The setting type. - * @param rank The rank to set. - */ - public static void setClanSetting(Player player, int type, ClanRank rank) { - if (!WorldCommunicator.isEnabled()) { - return; - } - IoBuffer buffer = new IoBuffer(8, PacketHeader.BYTE); - buffer.putString(player.getName()); - buffer.put((byte) type); - if (rank != null) { - buffer.put((byte) rank.ordinal()); - } - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sends the kicking a clan member packet. - * - * @param username The player's username. - * @param name The name. - */ - public static void sendClanKick(String username, String name) { - IoBuffer buffer = new IoBuffer(9, PacketHeader.BYTE); - buffer.putString(username); - buffer.putString(name); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sends a clan message. - * - * @param player The player sending the message. - * @param message The message to send. - */ - public static void sendClanMessage(Player player, String message) { - IoBuffer buffer = new IoBuffer(10, PacketHeader.BYTE); - buffer.putString(player.getName()); - buffer.putString(message); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sends a private message. - * - * @param player The player. - * @param name The target name. - * @param message The message. - */ - public static void sendPrivateMessage(Player player, String name, String message) { - IoBuffer buffer = new IoBuffer(11, PacketHeader.BYTE); - buffer.putString(player.getName()); - buffer.putString(name); - buffer.putString(message); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Requests clan information. - * - * @param name The clan's owner name. - */ - public static void requestClanInfo(String name) { - IoBuffer buffer = new IoBuffer(12, PacketHeader.BYTE); - buffer.putString(name); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Sends the chat settings. - * - * @param player The player. - * @param publicSetting The public chat setting. - * @param privateSetting The private chat setting. - * @param tradeSetting The trade setting. - */ - public static void sendChatSetting(Player player, int publicSetting, int privateSetting, int tradeSetting) { - IoBuffer buffer = new IoBuffer(13, PacketHeader.BYTE); - buffer.putString(player.getName()); - buffer.put(publicSetting); - buffer.put(privateSetting); - buffer.put(tradeSetting); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Handles an incoming packet from the management server. - * - * @param opcode The opcode. - * @param b The buffer. - */ - public static void handleIncoming(int opcode, ByteBuffer b) { - IoBuffer buffer = new IoBuffer(opcode, PacketHeader.NORMAL, b); - switch (opcode) { - case 0: - handleRegistryResponse(buffer); - break; - case 2: - handlePlayerMessage(buffer); - break; - case 3: - handleContactInformation(buffer); - break; - case 4: - handleContactUpdate(buffer); - break; - case 5: - handleMessage(buffer); - break; - case 6: - handleClanInformation(buffer); - break; - case 7: - handleLeaveClan(buffer); - break; - case 8: - handleContactNotification(buffer); - break; - case 9: - handlePlayerLogout(buffer); - break; - case 10: - handleUpdate(buffer); - break; - case 11: - handlePunishmentUpdate(buffer); - break; - case 15: - SystemManager.getSystemConfig().parse(); - SystemLogger.logInfo("System configurations reloaded."); - break; - default: - System.out.println("Handling incoming packet [opcode=" + opcode + ", size=" + b.limit() + "]."); - } - } - - /** - * Handles the player registry response packet. - * - * @param buffer The buffer. - */ - private static void handleRegistryResponse(IoBuffer buffer) { - String username = buffer.getString(); - int opcode = buffer.get() & 0xFF; - LoginParser parser = WorldCommunicator.finishLoginAttempt(username); - if (parser != null) { - PlayerDetails details = parser.getDetails(); - Response response = Response.get(opcode); - Player player = null; - switch (response) { - case ALREADY_ONLINE: - player = Repository.getPlayerByName(username); - if (player == null || player.getSession().isActive() || !player.getSession().getAddress().equals(details.getSession().getAddress())) { - details.getSession().write(response, true); - break; - } - player.getPacketDispatch().sendLogout(); - case SUCCESSFUL: - if (!details.getSession().isActive()) { - sendPlayerRemoval(username); - break; - } - if (player == null) { - player = new Player(details); - } else { - player.updateDetails(details); - } - parser.initialize(player, response == Response.ALREADY_ONLINE); - break; - - case MOVING_WORLD: - details.getSession().setServerKey(buffer.get()); - default: - details.getSession().write(response, true); - break; - } - } - } - - /** - * Sends global message to all player nodes. - * - * @param message The message. - */ - public static void sendWorldMessage(String message) { - IoBuffer buffer = new IoBuffer(12, PacketHeader.BYTE); - buffer.putString(message); - WorldCommunicator.getSession().write(buffer); - } - - /** - * Handles sending a message to a player. - * - * @param buffer The buffer to read from. - */ - private static void handlePlayerMessage(IoBuffer buffer) { - String name = buffer.getString(); - String message = buffer.getString(); - Player player = Repository.getPlayerByName(name); - if (player != null && player.isActive()) { - player.getPacketDispatch().sendMessage(message); - } - } - - /** - * Handles the contact information packet. - * - * @param buffer The buffer to read from. - */ - private static void handleContactInformation(IoBuffer buffer) { - String username = buffer.getString(); - Player player = Repository.getPlayerByName(username); - if (player == null || !player.isActive()) { - return; - } - PacketRepository.send(ContactPackets.class, new ContactContext(player, ContactContext.UPDATE_STATE_TYPE)); - player.getCommunication().getContacts().clear(); - int length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - String name = buffer.getString(); - Contact contact = new Contact(name); - contact.setRank(ClanRank.values()[buffer.get() & 0xFF]); - contact.setWorldId(buffer.get() & 0xFF); - player.getCommunication().getContacts().put(name, contact); - PacketRepository.send(ContactPackets.class, new ContactContext(player, name, contact.getWorldId())); - } - player.getCommunication().getBlocked().clear(); - length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - player.getCommunication().getBlocked().add(buffer.getString()); - } - PacketRepository.send(ContactPackets.class, new ContactContext(player, ContactContext.IGNORE_LIST_TYPE)); - if (buffer.get() == 1) { - String name = buffer.getString(); - sendJoinClan(player, name); - } - } - - /** - * Handles the contact update packet. - */ - private static void handleContactUpdate(IoBuffer buffer) { - String username = buffer.getString(); - String contactName = buffer.getString(); - boolean block = buffer.get() == 1; - int type = buffer.get(); - Player player = Repository.getPlayerByName(username); - if (player == null || !player.isActive()) { - return; - } - switch (type) { - case 0: - if (block) { - player.getCommunication().getBlocked().add(contactName); - PacketRepository.send(ContactPackets.class, new ContactContext(player, ContactContext.IGNORE_LIST_TYPE)); - break; - } - int worldId = buffer.get() & 0xFF; - Contact contact = player.getCommunication().getContacts().get(contactName); - if (contact == null) { - player.getCommunication().getContacts().put(contactName, contact = new Contact(contactName)); - } - contact.setWorldId(worldId); - PacketRepository.send(ContactPackets.class, new ContactContext(player, contactName, worldId)); - break; - case 1: - if (block) { - player.getCommunication().getBlocked().remove(contactName); - PacketRepository.send(ContactPackets.class, new ContactContext(player, ContactContext.IGNORE_LIST_TYPE)); - break; - } - player.getCommunication().getContacts().remove(contactName); - break; - default: - ClanRank rank = ClanRank.values()[type - 2]; - contact = player.getCommunication().getContacts().get(contactName); - if (contact == null) { - // SystemLogger.logErr("Invalid contact specified [name=" + - // contact + "]!"); - break; - } - contact.setRank(rank); - ClanRepository clan = ClanRepository.get(username); - if (clan != null) { - clan.rank(contactName, rank); - } - PacketRepository.send(ContactPackets.class, new ContactContext(player, contactName, contact.getWorldId())); - break; - } - } - - /** - * Handles the message packet. - * - * @param buffer The buffer. - */ - private static void handleMessage(IoBuffer buffer) { - String username = buffer.getString(); - String sender = buffer.getString(); - int type = buffer.get() & 0xFF; - int icon = buffer.get() & 0xFF; - String message = buffer.getString(); - Player player = Repository.getPlayerByName(username); - if (player == null || !player.isActive()) { - return; - } - int opcode = MessageContext.SEND_MESSAGE; - switch (type) { - case 1: - opcode = MessageContext.RECIEVE_MESSAGE; - break; - case 2: - opcode = MessageContext.CLAN_MESSAGE; - break; - } - PacketRepository.send(CommunicationMessage.class, new MessageContext(player, sender, icon, opcode, message)); - } - - /** - * Handles the clan information packet. - * - * @param buffer The buffer. - */ - private static void handleClanInformation(IoBuffer buffer) { - String owner = buffer.getString(); - ClanRepository clan = ClanRepository.getClans().get(owner); - if (clan == null) { - ClanRepository.getClans().put(owner, clan = new ClanRepository(owner)); - } else { - clan.getRanks().clear(); - clan.getPlayers().clear(); - } - clan.setName(buffer.getString()); - int length = buffer.get() & 0xFF; - for (int i = 0; i < length; i++) { - String name = buffer.getString(); - int worldId = buffer.get() & 0xFF; - clan.getRanks().put(name, ClanRank.values()[buffer.get() & 0xFF]); - ClanEntry entry = new ClanEntry(name, worldId); - if (worldId == GameWorld.getSettings().getWorldId()) { - Player player = Repository.getPlayerByName(name); - entry.setPlayer(player); - if (player != null) { - player.getCommunication().setClan(clan); - } - } - clan.getPlayers().add(entry); - } - clan.setJoinRequirement(ClanRank.values()[buffer.get() & 0xFF]); - clan.setKickRequirement(ClanRank.values()[buffer.get() & 0xFF]); - clan.setMessageRequirement(ClanRank.values()[buffer.get() & 0xFF]); - clan.setLootRequirement(ClanRank.values()[buffer.get() & 0xFF]); - clan.update(); - } - - /** - * Handles the clan information packet. - * - * @param buffer The buffer. - */ - private static void handleLeaveClan(IoBuffer buffer) { - String name = buffer.getString(); - Player player = Repository.getPlayerByName(name); - if (player == null || !player.isActive()) { - return; - } - if (player.getCommunication().getClan() == null) { - return; - } - PacketRepository.send(UpdateClanChat.class, new ClanContext(player, null, true)); - player.getCommunication().setClan(null); - } - - /** - * Handles a contact notification packet. - * - * @param buffer The buffer. - */ - private static void handleContactNotification(IoBuffer buffer) { - String name = buffer.getString(); - int worldId = buffer.get() & 0xFF; - int size = buffer.get() & 0xFF; - for (int i = 0; i < size; i++) { - String username = buffer.getString(); - Player player = Repository.getPlayerByName(username); - if (player == null) { - continue; - } - Contact c = player.getCommunication().getContacts().get(name); - if (c == null) { - continue; - } - c.setWorldId(worldId); - PacketRepository.send(ContactPackets.class, new ContactContext(player, name, worldId)); - } - } - - /** - * Handles a player logout notification packet. - * - * @param buffer The buffer. - */ - private static void handlePlayerLogout(IoBuffer buffer) { - String name = buffer.getString(); - for (Player p : Repository.getPlayers()) { - if (CommunicationInfo.hasContact(p, name)) { - PacketRepository.send(ContactPackets.class, new ContactContext(p, name, 0)); - } - } - } - - /** - * Handles the update packet. - * - * @param buffer The buffer. - */ - private static void handleUpdate(IoBuffer buffer) { - int ticks = buffer.getInt(); - if (ticks < 0) { - SystemManager.getUpdater().cancel(); - } else { - SystemManager.getUpdater().setCountdown(ticks); - SystemManager.flag(SystemState.UPDATING); - } - } - - /** - * Handles the punishment update packet. - * - * @param buffer The buffer. - */ - private static void handlePunishmentUpdate(IoBuffer buffer) { - String key = buffer.getString(); - int type = buffer.get(); - long duration = buffer.getLong(); - switch (type) { - case 0: - Player player = Repository.getPlayerByName(key); - if (player != null && player.isActive()) { - player.getPacketDispatch().sendMessages((duration > 0L ? new String[]{"You have been muted.", "To prevent further mutes please read the rules."} : new String[]{"You have been unmuted."})); - player.getDetails().setMuteTime(duration); - } - break; - case 1: - player = Repository.getPlayerByName(key); - if (player != null && player.isActive() && duration > System.currentTimeMillis()) { - player.getSession().disconnect(); - } - break; - case 2: - for (Player p : Repository.getPlayers()) { - if (p.getDetails().getIpAddress().equals(key)) { - p.getSession().disconnect(); - } - } - break; - case 3: - for (Player p : Repository.getPlayers()) { - if (p == null || key == null || p.getDetails() == null) { - return; - } - if (p.getDetails().getMacAddress().equals(key)) { - p.getSession().disconnect(); - } - } - break; - case 4: - for (Player p : Repository.getPlayers()) { - if (p == null || key == null || p.getDetails() == null) { - return; - } - if (p.getDetails().getSerial().equals(key)) { - p.getSession().disconnect(); - } - } - break; - case 6: - player = Repository.getPlayerByName(key); - if (player != null) { - player.getPacketDispatch().sendLogout(); - player.clear(true); - player.getSession().disconnect(); - } - break; - } - } - - - /** - * Gets the icon to send. - * - * @param d the details. - * @return the icon. - */ - private static int getIcon(PlayerDetails d) { - int icon = 0; - if (d.getRights() != Rights.REGULAR_PLAYER) { - icon = d.getRights().toInteger(); - } - return icon; - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/event/HSReadEvent.java b/Server/src/main/java/core/net/event/HSReadEvent.java deleted file mode 100644 index 363ca5aa9..000000000 --- a/Server/src/main/java/core/net/event/HSReadEvent.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.net.event; - -import core.net.Constants; -import core.net.IoReadEvent; -import core.net.IoSession; -import core.net.lobby.WorldList; -import core.net.registry.AccountRegister; -import core.tools.RandomFunction; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * Handles handshake read events. - * @author Emperor - */ -public final class HSReadEvent extends IoReadEvent { - - // debug - static Map count = new HashMap<>(); - - /** - * Constructs a new {@code HSReadEvent}. - * @param session The session. - * @param buffer The buffer. - */ - public HSReadEvent(IoSession session, ByteBuffer buffer) { - super(session, buffer); - } - - @Override - public void read(IoSession session, ByteBuffer buffer) { - Integer amount = count.get(session.getAddress()); - if (amount == null) { - amount = 0; - } - count.put(session.getAddress(), amount + 1); - int opcode = buffer.get() & 0xFF; - switch (opcode) { - case 14: - session.setNameHash(buffer.get() & 0xFF); - session.setServerKey(RandomFunction.RANDOM.nextLong()); - session.write(true); - break; - case 15: - 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; - } - session.write(false); - break; - case 147: - case 186: - case 36: - AccountRegister.read(session, opcode, buffer); - break; - case 255: // World list - int updateStamp = buffer.getInt(); - WorldList.sendUpdate(session, updateStamp); - break; - default: - session.disconnect(); - break; - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/event/RegistryReadEvent.java b/Server/src/main/java/core/net/event/RegistryReadEvent.java deleted file mode 100644 index 2d3d4a4d1..000000000 --- a/Server/src/main/java/core/net/event/RegistryReadEvent.java +++ /dev/null @@ -1,60 +0,0 @@ -package core.net.event; - -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; -import core.net.IoReadEvent; -import core.net.IoSession; -import core.net.amsc.ManagementServerState; -import core.net.amsc.WorldCommunicator; -import core.net.producer.MSEventProducer; - -import java.nio.ByteBuffer; - -/** - * Handles world registry read events. - * @author Emperor - */ -public final class RegistryReadEvent extends IoReadEvent { - - /** - * The event producer. - */ - private static final MSEventProducer PRODUCER = new MSEventProducer(); - - /** - * Constructs a new {@code RegistryReadEvent} {@code Object}. - * @param session The session. - * @param buffer The buffer to read. - */ - public RegistryReadEvent(IoSession session, ByteBuffer buffer) { - super(session, buffer); - } - - @Override - public void read(IoSession session, ByteBuffer buffer) { - int opcode = buffer.get() & 0xFF; - switch (opcode) { - case 0: - WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); - SystemLogger.logErr("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("Successfully registered world to AMS - [id=" + GameWorld.getSettings().getWorldId() + "]!"); - break; - case 2: - WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); - SystemLogger.logErr("Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=World id already in use]!"); - break; - case 3: - WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); - SystemLogger.logErr("Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=Exception in AMS]!"); - break; - default: - System.out.println("??" + opcode); - break; - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/event/RegistryWriteEvent.java b/Server/src/main/java/core/net/event/RegistryWriteEvent.java deleted file mode 100644 index 0a0b40521..000000000 --- a/Server/src/main/java/core/net/event/RegistryWriteEvent.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.net.event; - -import core.cache.misc.buffer.ByteBufferUtils; -import rs09.game.world.GameWorld; -import core.net.Constants; -import core.net.IoSession; -import core.net.IoWriteEvent; - -import java.nio.ByteBuffer; - -/** - * Handles game world registry writing events. - * @author Emperor - */ -public final class RegistryWriteEvent extends IoWriteEvent { - - /** - * The string check. - */ - private static final String CHECK = "12x4578f5g45hrdjiofed59898"; - - /** - * Constructs a new {@code RegistryWriteEvent} {@code Object}. - * @param session The I/O session. - * @param context The writing context. - */ - public RegistryWriteEvent(IoSession session, Object context) { - super(session, context); - } - - @Override - public void write(IoSession session, Object context) { - ByteBuffer buffer = ByteBuffer.allocate(128); - buffer.put((byte) GameWorld.getSettings().getWorldId()); - buffer.putInt(Constants.REVISION); - buffer.put((byte) GameWorld.getSettings().getCountryIndex()); - buffer.put((byte) (GameWorld.getSettings().isMembers() ? 1 : 0)); - buffer.put((byte) (GameWorld.getSettings().isPvp() ? 1 : 0)); - buffer.put((byte) (GameWorld.getSettings().isQuickChat() ? 1 : 0)); - buffer.put((byte) (GameWorld.getSettings().isLootshare() ? 1 : 0)); - ByteBufferUtils.putString(GameWorld.getSettings().getActivity(), buffer); - buffer.put(CHECK.getBytes()); - session.queue((ByteBuffer) buffer.flip()); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/IoBuffer.java b/Server/src/main/java/core/net/packet/IoBuffer.java deleted file mode 100644 index 219d9c1a0..000000000 --- a/Server/src/main/java/core/net/packet/IoBuffer.java +++ /dev/null @@ -1,638 +0,0 @@ -package core.net.packet; - -import core.cache.crypto.ISAACCipher; -import core.cache.misc.buffer.ByteBufferUtils; - -import java.nio.ByteBuffer; - -/** - * Represents the buffer used for reading/writing packets. - * @author Emperor - */ -public class IoBuffer { - - /** - * The bit masks. - */ - private static final int[] BIT_MASK = new int[32]; - - /** - * The packet size. - */ - private int packetSize; - - /** - * The opcode. - */ - private int opcode; - - /** - * The packet header. - */ - private final PacketHeader header; - - /** - * The byte buffer. - */ - private ByteBuffer buf; - - /** - * The bit position. - */ - private int bitPosition = 0; - - /** - * Constructs a new {@code IoBuffer} {@code Object}. - */ - public IoBuffer() { - this(-1, PacketHeader.NORMAL, ByteBuffer.allocate(2048)); - } - - /** - * Constructs a new {@code IoBuffer} {@code Object}. - * @param opcode The opcode. - */ - public IoBuffer(int opcode) { - this(opcode, PacketHeader.NORMAL, ByteBuffer.allocate(2048)); - } - - /** - * Constructs a new {@code IoBuffer} {@code Object}. - * @param opcode The opcode. - * @param header The packet header. - */ - public IoBuffer(int opcode, PacketHeader header) { - this(opcode, header, ByteBuffer.allocate((1 << 16) + 1)); - } - - /** - * Constructs a new {@code IoBuffer} {@code Object}. - * @param opcode The opcode. - * @param header The packet header. - * @param buf The byte buffer. - */ - public IoBuffer(int opcode, PacketHeader header, ByteBuffer buf) { - this.opcode = opcode; - this.header = header; - this.buf = buf; - } - - static { - for (int i = 0; i < 32; i++) { - BIT_MASK[i] = (1 << i) - 1; - } - } - - /** - * @return - */ - public IoBuffer clear() { - buf.clear(); - bitPosition = 0; - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer put(int val) { - buf.put((byte) val); - return this; - } - - /** - * @param datas - * @param offset - * @param len - * @return - */ - public IoBuffer putBytes(byte[] datas, int offset, int len) { - for (int i = offset; i < len; i++) { - put(datas[i]); - } - return this; - } - - public final void getBytes(byte data[], int off, int len) { - for (int k = off; k < len + off; k++) { - data[k] = data[off++]; - } - } - - /** - * @param val - * @return - */ - public IoBuffer putA(int val) { - buf.put((byte) (val + 128)); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putC(int val) { - buf.put((byte) -val); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putS(int val) { - buf.put((byte) (128 - val)); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putTri(int val) { - buf.put((byte) (val >> 16)); - buf.put((byte) (val >> 8)); - buf.put((byte) val); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putShort(int val) { - buf.putShort((short) val); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putLEShort(int val) { - buf.put((byte) val); - buf.put((byte) (val >> 8)); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putShortA(int val) { - buf.put((byte) (val >> 8)); - buf.put((byte) (val + 128)); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putLEShortA(int val) { - buf.put((byte) (val + 128)); - buf.put((byte) (val >> 8)); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putInt(int val) { - buf.putInt(val); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putLEInt(int val) { - buf.put((byte) val); - buf.put((byte) (val >> 8)); - buf.put((byte) (val >> 16)); - buf.put((byte) (val >> 24)); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putIntA(int val) { - buf.put((byte) (val >> 8)); - buf.put((byte) val); - buf.put((byte) (val >> 24)); - buf.put((byte) (val >> 16)); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putIntB(int val) { - buf.put((byte) (val >> 16)); - buf.put((byte) (val >> 24)); - buf.put((byte) val); - buf.put((byte) (val >> 8)); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putLong(long val) { - buf.putLong(val); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putSmart(int val) { - if (val > Byte.MAX_VALUE) { - buf.putShort((short) (val + 32768)); - } else { - buf.put((byte) val); - } - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putIntSmart(int val) { - if (val > Short.MAX_VALUE) { - buf.putInt(val + 32768); - } else { - buf.putShort((short) val); - } - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putString(String val) { - buf.put(val.getBytes()); - buf.put((byte) 0); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putJagString(String val) { - buf.put((byte) 0); - buf.put(val.getBytes()); - buf.put((byte) 0); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer putJagString2(String val) { - byte[] packed = new byte[256]; - int length = ByteBufferUtils.packGJString2(0, packed, val); - buf.put((byte) 0).put(packed, 0, length).put((byte) 0); - return this; - } - - /** - * @param val - * @return - */ - public IoBuffer put(byte[] val) { - buf.put(val); - return this; - } - - /** - * Puts a byte array as byte A in reverse. - * @param data The data to put. - * @param start The start index. - * @param offset The offset. - */ - public void putReverseA(byte[] data, int start, int offset) { - for (int i = offset + start; i >= start; i--) { - putA(data[i]); - } - } - - /** - * Puts a byte array as byte A in reverse. - * @param data The data to put. - * @param start The start index. - * @param offset The offset. - */ - public void putReverse(byte[] data, int start, int offset) { - for (int i = offset + start; i >= start; i--) { - put(data[i]); - } - } - - /** - * @param numBits - * @param value - * @return - */ - public IoBuffer putBits(int numBits, int value) { - int bytePos = getBitPosition() >> 3; - int bitOffset = 8 - (getBitPosition() & 7); - bitPosition += numBits; - for (; numBits > bitOffset; bitOffset = 8) { - byte b = buf.get(bytePos); - buf.put(bytePos, b &= ~BIT_MASK[bitOffset]); - buf.put(bytePos++, b |= value >> numBits - bitOffset & BIT_MASK[bitOffset]); - numBits -= bitOffset; - } - byte b = buf.get(bytePos); - if (numBits == bitOffset) { - buf.put(bytePos, b &= ~BIT_MASK[bitOffset]); - buf.put(bytePos, b |= value & BIT_MASK[bitOffset]); - } else { - buf.put(bytePos, b &= ~(BIT_MASK[numBits] << bitOffset - numBits)); - buf.put(bytePos, b |= (value & BIT_MASK[numBits]) << bitOffset - numBits); - } - return this; - } - - /** - * @param buffer - * @return - */ - public IoBuffer put(IoBuffer buffer) { - buffer.toByteBuffer().flip(); - buf.put(buffer.toByteBuffer()); - return this; - } - - /** - * @param buffer - * @return - */ - public IoBuffer putA(IoBuffer buffer) { - buffer.toByteBuffer().flip(); - while (buffer.toByteBuffer().hasRemaining()) { - putA(buffer.toByteBuffer().get()); - } - return this; - } - - /** - * @param buffer - * @return - */ - public IoBuffer put(ByteBuffer buffer) { - buf.put(buffer); - return this; - } - - /** - * @return - */ - public IoBuffer setBitAccess() { - bitPosition = buf.position() * 8; - return this; - } - - /** - * @return - */ - public IoBuffer setByteAccess() { - buf.position((getBitPosition() + 7) / 8); - return this; - } - - /** - * @return - */ - public int get() { - return buf.get(); - } - - /** - * @return - */ - public int getA() { - return (buf.get() & 0xFF) - 128; - } - - /** - * @return - */ - public int getC() { - return -buf.get(); - } - - /** - * @return - */ - public int getS() { - return 128 - (buf.get() & 0xFF); - } - - /** - * @return - */ - public int getTri() { - return ((buf.get() << 16) & 0xFF) | ((buf.get() << 8) & 0xFF) | (buf.get() & 0xFF); - } - - /** - * @return - */ - public int getShort() { - return buf.getShort(); - } - - /** - * @return - */ - public int getLEShort() { - return (buf.get() & 0xFF) | ((buf.get() & 0xFF) << 8); - } - - /** - * @return - */ - public int getShortA() { - return ((buf.get() & 0xFF) << 8) | (buf.get() - 128 & 0xFF); - } - - /** - * @return - */ - public int getLEShortA() { - return (buf.get() - 128 & 0xFF) | ((buf.get() & 0xFF) << 8); - } - - /** - * @return - */ - public int getInt() { - return buf.getInt(); - } - - /** - * @return - */ - public int getLEInt() { - return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8) + ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24); - } - - /** - * @return - */ - public int getIntA() { - return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16); - } - - /** - * @return - */ - public int getIntB() { - return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24) + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8); - } - - /** - * @return - */ - public long getLongL() { - long first = getIntB(); - long second = getIntB(); - if (second < 0) - second = second & 0xffffffffL; - return (first << -41780448) + second; - } - - /** - * @return - */ - public long getLong() { - return buf.getLong(); - } - - /** - * @return - */ - public int getSmart() { - int peek = buf.get(buf.position()); - if (peek <= (0xFF & peek)) { - return buf.get() & 0xFF; - } - return (buf.getShort() & 0xFFFF) - 32768; - } - - /** - * @return - */ - public int getIntSmart() { - int peek = buf.getShort(buf.position()); - if (peek <= Short.MAX_VALUE) { - return buf.getShort() & 0xFFFF; - } - return (buf.getInt() & 0xFFFFFFFF) - 32768; - } - - /** - * @return - */ - public String getString() { - return ByteBufferUtils.getString(buf); - } - - /** - * @return - */ - public String getJagString() { - buf.get(); - return ByteBufferUtils.getString(buf); - } - - /** - * @param is - * @param offset - * @param length - * @return - */ - public IoBuffer getReverseA(byte[] is, int offset, int length) { - for (int i = (offset + length - 1); i >= offset; i--) { - is[i] = (byte) (buf.get() - 128); - } - return this; - } - - public void cypherOpcode(ISAACCipher cipher){ - this.opcode += (byte)cipher.getNextValue(); - } - - /** - * @return - */ - public ByteBuffer toByteBuffer() { - return buf; - } - - /** - * @return - */ - public int opcode() { - return opcode; - } - - /** - * @return - */ - public int readableBytes() { - return buf.capacity() - buf.remaining(); - } - - /** - * @return - */ - public PacketHeader getHeader() { - return header; - } - - /** - * @return - */ - public byte[] array() { - return buf.array(); - } - - /** - * @return the packetSize. - */ - public int getPacketSize() { - return packetSize; - } - - /** - * @param packetSize the packetSize to set. - */ - public void setPacketSize(int packetSize) { - this.packetSize = packetSize; - } - - /** - * Gets the bitPosition. - * @return The bitPosition. - */ - public int getBitPosition() { - return bitPosition; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/PacketRepository.java b/Server/src/main/java/core/net/packet/PacketRepository.java deleted file mode 100644 index 6cb4e3e60..000000000 --- a/Server/src/main/java/core/net/packet/PacketRepository.java +++ /dev/null @@ -1,207 +0,0 @@ -package core.net.packet; - -import core.net.packet.in.*; -import core.net.packet.out.GrandExchangePacket; -import core.net.packet.out.*; -import rs09.game.system.SystemLogger; -import rs09.net.packet.PacketWriteQueue; -import rs09.net.packet.in.ItemOnGroundItemPacket; -import rs09.net.packet.in.QuickChatPacketHandler; - -import java.util.HashMap; -import java.util.Map; - -/** - * The packet repository. - * @author Emperor - */ -public final class PacketRepository { - - /** - * The outgoing packets mapping. - */ - public final static Map, OutgoingPacket> OUTGOING_PACKETS = new HashMap<>(); - - /** - * The incoming packets mapping. - */ - private final static Map INCOMING_PACKETS = new HashMap<>(); - - /** - * Populate the mappings. - */ - static { - OUTGOING_PACKETS.put(LoginPacket.class, new LoginPacket()); // - OUTGOING_PACKETS.put(UpdateSceneGraph.class, new UpdateSceneGraph()); // - OUTGOING_PACKETS.put(WindowsPane.class, new WindowsPane()); // - OUTGOING_PACKETS.put(Interface.class, new Interface()); // - OUTGOING_PACKETS.put(SkillLevel.class, new SkillLevel()); // - OUTGOING_PACKETS.put(Config.class, new Config()); // - OUTGOING_PACKETS.put(AccessMask.class, new AccessMask()); // - OUTGOING_PACKETS.put(GameMessage.class, new GameMessage()); // - OUTGOING_PACKETS.put(RunScriptPacket.class, new RunScriptPacket()); // - OUTGOING_PACKETS.put(RunEnergy.class, new RunEnergy()); // - OUTGOING_PACKETS.put(ContainerPacket.class, new ContainerPacket()); // - OUTGOING_PACKETS.put(StringPacket.class, new StringPacket()); // - OUTGOING_PACKETS.put(Logout.class, new Logout()); // - OUTGOING_PACKETS.put(CloseInterface.class, new CloseInterface()); // - OUTGOING_PACKETS.put(AnimateInterface.class, new AnimateInterface()); // - OUTGOING_PACKETS.put(DisplayModel.class, new DisplayModel()); // - OUTGOING_PACKETS.put(InterfaceConfig.class, new InterfaceConfig()); // - OUTGOING_PACKETS.put(PingPacket.class, new PingPacket()); // - OUTGOING_PACKETS.put(UpdateAreaPosition.class, new UpdateAreaPosition()); // - OUTGOING_PACKETS.put(ConstructScenery.class, new ConstructScenery()); // - OUTGOING_PACKETS.put(ClearScenery.class, new ClearScenery()); // - OUTGOING_PACKETS.put(HintIcon.class, new HintIcon()); // - OUTGOING_PACKETS.put(ClearMinimapFlag.class, new ClearMinimapFlag()); // - OUTGOING_PACKETS.put(InteractionOption.class, new InteractionOption()); // - OUTGOING_PACKETS.put(SetWalkOption.class, new SetWalkOption()); // - OUTGOING_PACKETS.put(MinimapState.class, new MinimapState()); // - OUTGOING_PACKETS.put(ConstructGroundItem.class, new ConstructGroundItem()); // - OUTGOING_PACKETS.put(ClearGroundItem.class, new ClearGroundItem()); // - OUTGOING_PACKETS.put(RepositionChild.class, new RepositionChild()); // - OUTGOING_PACKETS.put(PositionedGraphic.class, new PositionedGraphic()); // - OUTGOING_PACKETS.put(SystemUpdatePacket.class, new SystemUpdatePacket()); // - OUTGOING_PACKETS.put(CameraViewPacket.class, new CameraViewPacket()); // - OUTGOING_PACKETS.put(MusicPacket.class, new MusicPacket()); // - OUTGOING_PACKETS.put(AudioPacket.class, new AudioPacket()); // - OUTGOING_PACKETS.put(GrandExchangePacket.class, new GrandExchangePacket()); // - OUTGOING_PACKETS.put(BuildDynamicScene.class, new BuildDynamicScene()); // - OUTGOING_PACKETS.put(AnimateObjectPacket.class, new AnimateObjectPacket()); // - OUTGOING_PACKETS.put(ClearRegionChunk.class, new ClearRegionChunk()); // - OUTGOING_PACKETS.put(ContactPackets.class, new ContactPackets()); // - 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(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()); - INCOMING_PACKETS.put(22, new ClientFocusPacket()); - INCOMING_PACKETS.put(93, new PingPacketHandler()); - INCOMING_PACKETS.put(44, new CommandPacket()); - INCOMING_PACKETS.put(237, new ChatPacket()); - INCOMING_PACKETS.put(21, new CameraMovementPacket()); - INCOMING_PACKETS.put(75, new MouseClickPacket()); - INCOMING_PACKETS.put(243, new DisplayUpdatePacket()); - INCOMING_PACKETS.put(177, new UpdateInterfaceCounter()); - INCOMING_PACKETS.put(4, new DummyPacket()); - INCOMING_PACKETS.put(245, new IdlePacketHandler()); - INCOMING_PACKETS.put(111, new core.net.packet.in.GrandExchangePacket()); - IncomingPacket packet = new WalkPacket(); - INCOMING_PACKETS.put(39, packet); - INCOMING_PACKETS.put(77, packet); - INCOMING_PACKETS.put(215, packet); - packet = new ItemActionPacket(); - INCOMING_PACKETS.put(134, packet);//item on object - INCOMING_PACKETS.put(115, packet);//on npc - INCOMING_PACKETS.put(27, packet);//item on item - INCOMING_PACKETS.put(248, packet);//on player - INCOMING_PACKETS.put(101,new ItemOnGroundItemPacket()); //Item on Ground Item - INCOMING_PACKETS.put(3, packet = new InteractionPacket()); - INCOMING_PACKETS.put(180, packet);//Player interact options v - INCOMING_PACKETS.put(68, packet); - INCOMING_PACKETS.put(71, packet); - INCOMING_PACKETS.put(114, packet); - INCOMING_PACKETS.put(175, packet);//Player interact options ^ - INCOMING_PACKETS.put(30, packet); - INCOMING_PACKETS.put(78, packet); - INCOMING_PACKETS.put(148, packet); - INCOMING_PACKETS.put(218, packet); - INCOMING_PACKETS.put(84, packet); - INCOMING_PACKETS.put(170, packet); - INCOMING_PACKETS.put(254, packet); - INCOMING_PACKETS.put(194, packet); - INCOMING_PACKETS.put(66, packet); - INCOMING_PACKETS.put(33, packet); - INCOMING_PACKETS.put(247, packet); - INCOMING_PACKETS.put(156, packet = new ActionButtonPacket()); //Item V - INCOMING_PACKETS.put(55, packet); - INCOMING_PACKETS.put(153, packet); - INCOMING_PACKETS.put(161, packet); - INCOMING_PACKETS.put(135, packet); //^ - INCOMING_PACKETS.put(81, packet); - INCOMING_PACKETS.put(184, packet);//close interface - INCOMING_PACKETS.put(155, packet); //Interface V - INCOMING_PACKETS.put(196, packet); - INCOMING_PACKETS.put(124, packet); - INCOMING_PACKETS.put(199, packet); - INCOMING_PACKETS.put(234, packet); - INCOMING_PACKETS.put(168, packet); - INCOMING_PACKETS.put(166, packet); - INCOMING_PACKETS.put(64, packet); - INCOMING_PACKETS.put(53, packet); - INCOMING_PACKETS.put(9, packet); //^ - INCOMING_PACKETS.put(132, packet); //Dialogue - INCOMING_PACKETS.put(10, packet); //logout - INCOMING_PACKETS.put(206, packet);//operate - INCOMING_PACKETS.put(72, packet = new ExaminePacket()); - INCOMING_PACKETS.put(92, packet); - INCOMING_PACKETS.put(94, packet); - /*INCOMING_PACKETS.put(57, packet); - INCOMING_PACKETS.put(34, packet); - INCOMING_PACKETS.put(213, packet);*/ - //INCOMING_PACKETS.put(69, packet); - //packet 98 - 530 settings interface - INCOMING_PACKETS.put(104, packet = new ClanPacketHandler()); - INCOMING_PACKETS.put(188, packet); - INCOMING_PACKETS.put(162, packet); - INCOMING_PACKETS.put(157, new ChatSettingsPacket()); - INCOMING_PACKETS.put(244, packet = new RunScriptPacketHandler()); - INCOMING_PACKETS.put(23, packet); - INCOMING_PACKETS.put(65, packet); - INCOMING_PACKETS.put(110, new RegionChangePacket()); - INCOMING_PACKETS.put(195, packet = new InterfaceUseOnPacket()); - INCOMING_PACKETS.put(239, packet); - INCOMING_PACKETS.put(73, packet); - INCOMING_PACKETS.put(253, packet); - INCOMING_PACKETS.put(233, packet); - INCOMING_PACKETS.put(231, packet = new SlotSwitchPacket()); - INCOMING_PACKETS.put(79, packet); - INCOMING_PACKETS.put(167, new QuickChatPacketHandler()); - INCOMING_PACKETS.put(201, packet = new CommunicationPacket()); - INCOMING_PACKETS.put(120, packet); - INCOMING_PACKETS.put(57, packet); - INCOMING_PACKETS.put(34, packet); - INCOMING_PACKETS.put(213, packet); - INCOMING_PACKETS.put(99, packet = new ReportAbusePacket()); - INCOMING_PACKETS.put(98, packet = new MapClosedPacket()); // this packet is sent when the world map is closed by the client - INCOMING_PACKETS.put(137,packet = new MusicPacketHandler()); //this packet is received when the client stops playing a song - // INCOMING_PACKETS.put(77, packet); - // INCOMING_PACKETS.put(191, packet); - // INCOMING_PACKETS.put(139, packet); - // INCOMING_PACKETS.put(251, packet); - // INCOMING_PACKETS.put(55, packet); - - //Packet 22 is sent on focus gain/loss - //packet 177 is sent when opening/closing interfaces - - } - - /** - * Sends a new packet. - * @param clazz The class of the outgoing packet to send. - * @param context The context. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static void send(Class clazz, Context context) { - OutgoingPacket p = OUTGOING_PACKETS.get(clazz); - if (p == null) { - SystemLogger.logErr("Invalid outgoing packet [handler=" + clazz + ", context=" + context + "]."); - return; - } - if(!context.getPlayer().isArtificial()) { - PacketWriteQueue.handle(p, context); - } - } - - /** - * Gets an incoming packet. - * @param opcode The opcode. - * @return The incoming packet. - */ - public static IncomingPacket getIncoming(int opcode) { - return INCOMING_PACKETS.get(opcode); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/ActionButtonPacket.java b/Server/src/main/java/core/net/packet/in/ActionButtonPacket.java deleted file mode 100644 index e83768cc8..000000000 --- a/Server/src/main/java/core/net/packet/in/ActionButtonPacket.java +++ /dev/null @@ -1,348 +0,0 @@ -package core.net.packet.in; - -import core.game.component.Component; -import core.game.component.ComponentPlugin; -import core.game.container.Container; -import core.game.content.dialogue.DialogueAction; -import core.game.interaction.Option; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.login.LoginConfiguration; -import core.game.node.entity.player.link.request.assist.AssistSession; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import rs09.game.interaction.InterfaceListeners; -import rs09.game.world.GameWorld; - -import java.util.List; - -/** - * The incoming reward button packet. - * @author Emperor - */ -public class ActionButtonPacket implements IncomingPacket { - - @Override - public void decode(final Player player, int opcode, IoBuffer buffer) { - if (player == null) { - return; - } - int[] args = getArguments(player, buffer); - if (args == null || (buffer.opcode() != 76 && args[0] != 106 && args[0] != 108 && args[0] != 110 && args[0] != 646 && player.getLocks().isComponentLocked() && player.getExtension(AssistSession.class) == null)) { - player.debug("Check this code in reward button packet, where args arent null && opcode !="); - return; - } - int componentId = args[0]; - int buttonId = args[1]; - int slot = args[2]; - int itemId = args[3]; - player.debug("Component=" + componentId + ", button=" + buttonId + ", slot=" + slot + ", item=" + itemId + ", opcode=" + buffer.opcode()); - if (player.getDialogueInterpreter().getDialogue() != null && buffer.opcode() != 132 && componentId != 64) { - player.getDialogueInterpreter().close(); - } - if (player.getLocks().isComponentLocked()) { - return; - } - if (itemId > -1 && slot > -1) { - Container container = getContainer(player, componentId); - if (container != null) { - handleItemInteraction(player, buffer.opcode(), itemId, slot, container); - return; - } - } - if (player.getZoneMonitor().clickButton(componentId, buttonId, slot, itemId, opcode)) { - return; - } - Component c = player.getInterfaceManager().getComponent(componentId); - if (c == null || c.isHidden()) { - player.debug("Component " + c + " wasn't opened in interface manager."); - return; - } - int cOpcode = buffer.opcode(); - ComponentPlugin plugin = c.getPlugin(); - if (plugin != null) { - player.debug("Component plugin = " + plugin.getClass().getSimpleName()); - plugin.handle(player, c, cOpcode, buttonId, slot, itemId); - } else { - if(InterfaceListeners.run(player,c,cOpcode,buttonId,slot,itemId)){ - return; - } - } - } - - /** - * Gets the container for the component id. - * @param player The player. - * @param componentId The component id. - * @return The container. - */ - private Container getContainer(Player player, int componentId) { - switch (componentId) { - case 149: - return player.getInventory(); - } - return null; - } - - /** - * Gets the arguments for the reward button. - * @param player The player. - * @param buffer The buffer. - * @return The arguments [component, button, slot, item] - */ - private static int[] getArguments(final Player player, IoBuffer buffer) { - int data = -1; - int componentId = -1; - int buttonId = -1; - int itemId = -1; - int slot = -1; - switch (buffer.opcode()) { - case 81: - //System.out.println("Using action handler81"); - slot = buffer.getShortA(); - itemId = buffer.getShort(); - buttonId = buffer.getShort(); - componentId = buffer.getShort(); - break; - case 156: - //System.out.println("Using action handler156"); - slot = buffer.getLEShortA(); - itemId = buffer.getShortA(); - data = buffer.getLEInt(); - componentId = data >> 16; - buttonId = data & 0xffff; - break; - case 55: - //System.out.println("Using action handler55"); - itemId = buffer.getLEShort(); - slot = buffer.getShortA(); - data = buffer.getIntA(); - componentId = data >> 16; - buttonId = data & 0xffff; - break; - case 153: - //System.out.println("Using action handler153"); - data = buffer.getLEInt(); - slot = buffer.getLEShort(); - itemId = buffer.getLEShort(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xFFFF; - break; - case 161: - //System.out.println("Using action handler161"); - data = buffer.getLEInt(); - itemId = buffer.getLEShortA(); - slot = buffer.getLEShortA(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xFFFF; - break; - case 135: - //System.out.println("Using action handler135"); - itemId = buffer.getShortA(); - slot = buffer.getShortA(); - data = buffer.getIntB(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xFFFF; - break; - case 155: //Interface options - case 196: - case 124: - case 199: - case 234: - case 168: - case 166: - case 64: - case 53: - case 9: - //System.out.println("Using action handler155-9"); - data = buffer.getInt(); - slot = buffer.getShort(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xFFFF; - break; - case 132: //Dialogue options - //System.out.println("Using action handler132"); - data = buffer.getIntA(); - slot = buffer.getLEShort(); - componentId = data >> 16; - buttonId = data & 0xffff; - if (player.getDialogueInterpreter().getDialogue() == null && player.getDialogueInterpreter().getDialogueStage() == null) { - player.getInterfaceManager().closeChatbox(); - List actions = player.getDialogueInterpreter().getActions(); - if (actions.size() > 0) { - DialogueAction action = actions.get(0); - action.handle(player, buttonId); - actions.remove(action); - } - break; - } - player.getDialogueInterpreter().handle(componentId, buttonId); - break; - case 133: - //System.out.println("Using action handler133"); - data = buffer.getLEInt(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xFFFF; - slot = buffer.getShort(); - itemId = buffer.getLEShortA(); - break; - case 206: - //System.out.println("Using action handler206"); - itemId = buffer.getShortA(); - slot = buffer.getLEShort(); - data = buffer.getLEInt(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xFFFF; - //player.sendMessage("itemId=" + itemId + ", data=" + data + ", buttonId=" + buttonId + ", compId= " + componentId); - break; - case 230: // Short - case 180: - case 10: - //System.out.println("Using action handler230-10"); - data = buffer.getInt(); - componentId = data >> 16; - buttonId = data & 0xffff; - if (buffer.opcode() == 230) { - slot = buffer.getShort(); - } - if (componentId == 49) { - if (player.getDialogueInterpreter().getDialogue() == null && player.getDialogueInterpreter().getDialogueStage() == null) { - player.setAttribute("chatbox-buttonid",buttonId); - player.getInterfaceManager().closeChatbox(); - return null; - } - player.setAttribute("chatbox-buttonid",buttonId); - player.getDialogueInterpreter().getDialogue().handle(componentId, buttonId); - } - break; - case 157: - //System.out.println("Using action handler157"); - itemId = buffer.getLEShort(); - slot = buffer.getLEShort(); - data = buffer.getLEInt(); - componentId = data >> 16; - buttonId = data & 0xffff; - break; - case 39: - //System.out.println("Using action handler39"); - data = buffer.getInt(); - slot = buffer.getShort(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xFFFF; - break; - case 128: - //System.out.println("Using action handler128"); - data = buffer.getInt(); - slot = buffer.getShort(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xffff; - break; - case 235: - //System.out.println("Using action handler235"); - data = buffer.getInt(); - slot = buffer.getShort(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xffff; - break; - case 243: - //System.out.println("Using action handler243"); - data = buffer.getInt(); - itemId = buffer.getShortA(); - slot = buffer.getLEShortA(); - componentId = data >> 16; - buttonId = data & 0xffff; - break; - case 170: - //System.out.println("Using action handler170"); - slot = buffer.getLEShort(); - itemId = buffer.getShortA(); - data = buffer.getLEInt(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xFFFF; - break; - case 145: - //System.out.println("Using action handler145"); - data = buffer.getIntB(); - itemId = buffer.getShortA(); - slot = buffer.getLEShort(); - componentId = (data >> 16) & 0xFFFF; - buttonId = data & 0xffff; - // TODO dialogue with store X. - break; - case 127:// 3rd option on send item. - case 203:// 5th option on send item. - case 205:// 2 option - case 211:// 4rth option on send item. - case 187: // 6th - //System.out.println("Using action handler127-187"); - data = buffer.getInt(); - slot = buffer.getShort(); - componentId = data >> 16; - buttonId = data & 0xffff; - itemId = -1; - break; - case 184: - case 95: - //System.out.println("Using action handler184-95"); - if (player.getAttribute("logging_in") != null) { - player.getInterfaceManager().close(); - GameWorld.getPulser().submit(new Pulse(1, player) { - @Override - public boolean pulse() { - player.removeAttribute("logging_in"); - LoginConfiguration.configureGameWorld(player); - return true; - } - }); - } - player.getInterfaceManager().close(); - if (player.getAttribute("worldMap:viewing") != null) { - player.removeAttribute("worldMap:viewing"); - player.getPacketDispatch().sendWindowsPane(player.getInterfaceManager().isResizable() ? 746 : 548, 2); - player.unlock(); - } - break; - } - return new int[] { componentId, buttonId, slot, itemId }; - } - - /** - * Handles an item interaction. - * @param player The player. - * @param opcode The opcode. - * @param itemId The item. - */ - private static void handleItemInteraction(Player player, int opcode, int itemId, int slot, Container container) { - if (slot < 0 || slot >= container.capacity()) { - return; - } - Item item = container.get(slot); - if (item == null || item.getId() != itemId) { - return; - } - int index = 0; - switch (opcode) { - case 156: // First option - index = 0; - break; - case 55: // Second option (wield/wear) - index = 1; - break; - case 153: // Third option - index = 2; - break; - case 161: // Fourth option (summon) - index = 3; - break; - case 135: // Fifth option (drop/destroy) - index = 4; - break; - } - final Option option = item.getInteraction().get(index); - if (option == null || player.getLocks().isInteractionLocked()) { - return; - } - item.getInteraction().handleItemOption(player, option, container); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/CameraMovementPacket.java b/Server/src/main/java/core/net/packet/in/CameraMovementPacket.java deleted file mode 100644 index 4f1f00e55..000000000 --- a/Server/src/main/java/core/net/packet/in/CameraMovementPacket.java +++ /dev/null @@ -1,19 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles an incoming camera movement changed packet. - * @author Emperor - */ -public final class CameraMovementPacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - buffer.getShortA(); - buffer.getLEShort(); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/ChatPacket.java b/Server/src/main/java/core/net/packet/in/ChatPacket.java deleted file mode 100644 index af419b473..000000000 --- a/Server/src/main/java/core/net/packet/in/ChatPacket.java +++ /dev/null @@ -1,57 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.game.system.monitor.PlayerMonitor; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.update.flag.context.ChatMessage; -import core.game.world.update.flag.player.ChatFlag; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.tools.StringUtils; - -/** - * Represents the incoming chat packet. - * @author Emperor - */ -public class ChatPacket implements IncomingPacket { - - @Override - public void decode(final Player player, int opcode, IoBuffer buffer) { - try { - final int effects = buffer.getShort(); - final int numChars = buffer.getSmart(); - final String message = StringUtils.decryptPlayerChat(buffer, numChars); - if (player.getDetails().isMuted()) { - player.getPacketDispatch().sendMessage("You have been " + (player.getDetails().isPermMute() ? "permanently" : "temporarily") + " muted due to breaking a rule."); - return; - } - if (message.startsWith("/") && player.getCommunication().getClan() != null) { - StringBuilder sb = new StringBuilder(message); - sb.append(" => ").append(player.getName()).append(" (owned by ").append(player.getCommunication().getClan().getOwner()).append(")"); - String m = sb.toString(); - player.getMonitor().log(m.replace(m.charAt(0), ' ').trim(), PlayerMonitor.CLAN_CHAT_LOG); - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendClanMessage(player, message.substring(1)); - } else { - player.getCommunication().getClan().message(player, message.substring(1)); - } - return; - } - player.getMonitor().log(message, PlayerMonitor.PUBLIC_CHAT_LOG); - ChatMessage ctx = new ChatMessage(player, message, effects, numChars); - GameWorld.getPulser().submit(new Pulse(0, player) { - @Override - public boolean pulse() { - player.getUpdateMasks().register(new ChatFlag(ctx)); - return true; - } - }); - } catch (Throwable t) { - t.printStackTrace(); - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/ChatSettingsPacket.java b/Server/src/main/java/core/net/packet/in/ChatSettingsPacket.java deleted file mode 100644 index c0fa1b0f7..000000000 --- a/Server/src/main/java/core/net/packet/in/ChatSettingsPacket.java +++ /dev/null @@ -1,26 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles an incoming chat settings update packet. - * @author Emperor - */ -public final class ChatSettingsPacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - int publicSetting = buffer.get(); - int privateSetting = buffer.get(); - int tradeSetting = buffer.get(); - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendChatSetting(player, publicSetting, privateSetting, tradeSetting); - } - player.getSettings().updateChatSettings(publicSetting, privateSetting, tradeSetting); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/ClanPacketHandler.java b/Server/src/main/java/core/net/packet/in/ClanPacketHandler.java deleted file mode 100644 index 5fc8ba5d8..000000000 --- a/Server/src/main/java/core/net/packet/in/ClanPacketHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.game.system.communication.ClanRank; -import core.game.system.communication.ClanRepository; -import core.game.system.communication.CommunicationInfo; -import rs09.game.world.repository.Repository; -import core.net.amsc.MSPacketRepository; -import core.net.amsc.WorldCommunicator; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.tools.StringUtils; - -/** - * Handles incoming clan packets. - * @author Emperor - */ -public class ClanPacketHandler implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - switch (buffer.opcode()) { - case 104: - long nameLong = buffer.getLong(); - String name = StringUtils.longToString(nameLong); - if (nameLong != 0l) { - player.getPacketDispatch().sendMessage("Attempting to join channel...:clan:"); - } - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendJoinClan(player, name); - return; - } - if (player.getCommunication().getClan() != null) { - player.getCommunication().getClan().leave(player, true); - player.getCommunication().setClan(null); - return; - } - ClanRepository clan = ClanRepository.get(name); - if (clan == null || clan.getName().equals("Chat disabled")) { - player.getPacketDispatch().sendMessage("The channel you tried to join does not exist."); - break; - } - if (clan.enter(player)) { - player.getCommunication().setClan(clan); - } - break; - case 188: - int rank = buffer.getA(); - name = StringUtils.longToString(buffer.getLong()); - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendContactUpdate(player.getName(), name, false, false, ClanRank.values()[rank + 1]); - break; - } - CommunicationInfo.updateClanRank(player, name, ClanRank.values()[rank + 1]); - break; - case 162: - name = StringUtils.longToString(buffer.getLong()); - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendClanKick(player.getName(), name); - break; - } - clan = player.getCommunication().getClan(); - Player target = Repository.getPlayerByName(name); - if (clan == null || target == null) { - break; - } - clan.kick(player, target); - break; - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/ClientFocusPacket.java b/Server/src/main/java/core/net/packet/in/ClientFocusPacket.java deleted file mode 100644 index 95b9b60c6..000000000 --- a/Server/src/main/java/core/net/packet/in/ClientFocusPacket.java +++ /dev/null @@ -1,20 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles an incoming client focus changed packet. - * @author Emperor - */ -public final class ClientFocusPacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - if (player != null) { - player.getMonitor().setClientFocus(buffer.get() == 1); - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/CommandPacket.java b/Server/src/main/java/core/net/packet/in/CommandPacket.java deleted file mode 100644 index 96cfed378..000000000 --- a/Server/src/main/java/core/net/packet/in/CommandPacket.java +++ /dev/null @@ -1,35 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import rs09.game.system.command.CommandSystem; -import core.game.system.monitor.PlayerMonitor; -import rs09.game.world.GameWorld; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles an incoming command packet. - * @author Emperor - * @author 'Vexia - */ -public final class CommandPacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - final int data = buffer.get(); - if (buffer.toByteBuffer().hasRemaining()) { - final String message = ((char) data + buffer.getString()).toLowerCase(); - if (!GameWorld.getSettings().isDevMode()) { - int last = player.getAttribute("commandLast", 0); - if (last > GameWorld.getTicks()) { - return; - } - player.setAttribute("commandLast", GameWorld.getTicks() + 1); - } - if (CommandSystem.Companion.getCommandSystem().parse(player, message)) { - player.getMonitor().log(message, PlayerMonitor.COMMAND_LOG); - } - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/CommunicationPacket.java b/Server/src/main/java/core/net/packet/in/CommunicationPacket.java deleted file mode 100644 index a4a26805b..000000000 --- a/Server/src/main/java/core/net/packet/in/CommunicationPacket.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.game.system.communication.CommunicationInfo; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.tools.StringUtils; - -/** - * Represents the packet used to handle all incoming packets related to - * communication. - * @author 'Vexia - */ -public final class CommunicationPacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - switch (buffer.opcode()) { - case 120: - CommunicationInfo.add(player, StringUtils.longToString(buffer.getLong())); - break; - case 57: - CommunicationInfo.remove(player, StringUtils.longToString(buffer.getLong()), false); - break; - case 34: - CommunicationInfo.block(player, StringUtils.longToString(buffer.getLong())); - break; - case 213: - CommunicationInfo.remove(player, StringUtils.longToString(buffer.getLong()), true); - break; - case 201: - String name = StringUtils.longToString(buffer.getLong()); - String message = StringUtils.decryptPlayerChat(buffer, buffer.get() & 0xFF); - if (player.getDetails().isMuted()) { - player.getPacketDispatch().sendMessage("You have been " + (player.getDetails().isPermMute() ? "permanently" : "temporarily") + " muted due to breaking a rule."); - return; - } - CommunicationInfo.sendMessage(player, name, message); - break; - } - } - -} diff --git a/Server/src/main/java/core/net/packet/in/DisplayUpdatePacket.java b/Server/src/main/java/core/net/packet/in/DisplayUpdatePacket.java deleted file mode 100644 index 4b8ed425d..000000000 --- a/Server/src/main/java/core/net/packet/in/DisplayUpdatePacket.java +++ /dev/null @@ -1,26 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles the display update packet. - * @author Emperor - * - */ -public class DisplayUpdatePacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - int windowMode = buffer.get(); //Window mode - int screenWidth = buffer.getShort(); - int screenHeight = buffer.getShort(); - int displayMode = buffer.get(); //Display mode - player.getSession().getClientInfo().setScreenWidth(screenWidth); - player.getSession().getClientInfo().setScreenHeight(screenHeight); - player.getSession().getClientInfo().setDisplayMode(displayMode); - player.getInterfaceManager().switchWindowMode(windowMode); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/DummyPacket.java b/Server/src/main/java/core/net/packet/in/DummyPacket.java deleted file mode 100644 index 8798bf414..000000000 --- a/Server/src/main/java/core/net/packet/in/DummyPacket.java +++ /dev/null @@ -1,13 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import rs09.game.system.SystemLogger; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -public class DummyPacket implements IncomingPacket { - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - SystemLogger.logInfo("Received opcode " + opcode + " packet."); - } -} diff --git a/Server/src/main/java/core/net/packet/in/ExaminePacket.java b/Server/src/main/java/core/net/packet/in/ExaminePacket.java deleted file mode 100644 index 47e916146..000000000 --- a/Server/src/main/java/core/net/packet/in/ExaminePacket.java +++ /dev/null @@ -1,88 +0,0 @@ -package core.net.packet.in; - -import core.cache.Cache; -import core.cache.def.impl.ItemDefinition; -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.cache.def.impl.VarbitDefinition; -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import org.rs09.consts.Items; - -import java.util.Arrays; - -/** - * Handles the incoming examine packets. - * @author Emperor - */ -public final class ExaminePacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - String name; - switch (buffer.opcode()) { - case 94: // Object examine - int id = buffer.getLEShortA(); - if (id < 0 || id > Cache.getObjectDefinitionsSize()) { - break; - } - SceneryDefinition d = SceneryDefinition.forId(id); - name = d.getExamine(); - //String coords = id + ", " + player.getLocation().getX() + ", " + player.getLocation().getY() + ", " + player.getLocation().getZ(); - player.debug("Object id: " + id + ", models: " + (d.getModelIds() != null ? Arrays.toString(d.getModelIds()) : null) + ", anim: " + d.animationId + ", config: " + (d.getVarbitID() != -1 ? d.getVarbitID() + " (file)" : d.getConfigId()) + "."); - player.debug("Varp config index: " + VarbitDefinition.forObjectID(d.getVarbitID()).getConfigId()); - player.getPacketDispatch().sendMessage(""+name+""); - /*if { - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection("LandscapeParser.removeGameObject(new GameObject("+coords+"));//"+ d.getName() ), null); - }*/ - break; - case 235: - case 92: // Item examine - id = buffer.getLEShortA(); - if (id < 0 || id > Cache.getItemDefinitionsSize()) { - break; - } - player.getPacketDispatch().sendMessage(getItemExamine(id)); - break; - case 72: // NPC examine - id = buffer.getShort(); - if (id < 0 || id > Cache.getNPCDefinitionsSize()) { - break; - } - player.debug("NPC id: " + id + "."); - NPCDefinition def = NPCDefinition.forId(id); - if (def == null) { - break; - } - player.getPacketDispatch().sendMessage(def.getExamine()); - break; - } - } - - /** - * Gets the item examine. - * @param id the item id. - * @return the item examine. - */ - public static String getItemExamine(int id) { - - // Coins examine override - if (id == Items.COINS_995) { - return "Lovely money!"; - } - - // Clue scroll examine override - if (ItemDefinition.forId(id).getExamine().length() == 255) { - return "A set of instructions to be followed."; - } - - // Noted item examine override - if (!ItemDefinition.forId(id).isUnnoted()) { - return "Swap this note at any bank for the equivalent item."; - } - - // Return the examine string for the given item ID - return ItemDefinition.forId(id).getExamine(); - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/GrandExchangePacket.java b/Server/src/main/java/core/net/packet/in/GrandExchangePacket.java deleted file mode 100644 index 96a82b5e6..000000000 --- a/Server/src/main/java/core/net/packet/in/GrandExchangePacket.java +++ /dev/null @@ -1,20 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Represents the Incoming packet of the grand exchange. - * @author Emperor - */ -public class GrandExchangePacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - int itemId = buffer.getShort(); - player.getPlayerGrandExchange().constructBuy(itemId); - player.getInterfaceManager().closeChatbox(); - } - -} diff --git a/Server/src/main/java/core/net/packet/in/IdlePacketHandler.java b/Server/src/main/java/core/net/packet/in/IdlePacketHandler.java deleted file mode 100644 index 0acb48c24..000000000 --- a/Server/src/main/java/core/net/packet/in/IdlePacketHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.Rights; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import rs09.game.ai.general.GeneralBotCreator; - -/** - * Handles the idle packet handler. - * @author Emperor - */ -public final class IdlePacketHandler implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - if (player.getDetails().getRights() != Rights.ADMINISTRATOR) { - GeneralBotCreator.BotScriptPulse pulse = player.getAttribute("botting:script",null); - if(pulse != null && pulse.isRunning()){ - return; - } - player.getPacketDispatch().sendLogout(); - } - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/InteractionPacket.java b/Server/src/main/java/core/net/packet/in/InteractionPacket.java deleted file mode 100644 index 0829de13a..000000000 --- a/Server/src/main/java/core/net/packet/in/InteractionPacket.java +++ /dev/null @@ -1,400 +0,0 @@ -package core.net.packet.in; - -import core.cache.def.impl.SceneryDefinition; -import core.cache.def.impl.VarbitDefinition; -import core.game.content.quest.PluginInteractionManager; -import core.game.interaction.Interaction; -import core.game.interaction.MovementPulse; -import core.game.interaction.Option; -import core.game.interaction.SpecialGroundItems; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.item.GroundItem; -import core.game.node.item.GroundItemManager; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.net.packet.PacketRepository; -import core.net.packet.context.PlayerContext; -import core.net.packet.out.ClearMinimapFlag; -import rs09.ServerConstants; -import rs09.game.ai.AIPlayer; -import rs09.game.interaction.InteractionListener; -import rs09.game.interaction.InteractionListeners; -import rs09.game.world.repository.Repository; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.IntPredicate; - -/** - * Handles the incoming interaction packets. - * @author Emperor - */ -public final class InteractionPacket implements IncomingPacket { - - static int[] hweenNPCs = new int[] {307, 375, 743, 744, 755, 2634, 2690, 2691, 2692, 530, 531, 556, 557, 558, 559, 583, 585, 1860, 3299, 3671, 922, 970}; - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - if (player == null) { - return; - } - if (buffer.opcode() != 105) { - player = getPlayer(player); - } - if (player.getLocks().isInteractionLocked() || !player.getInterfaceManager().close()) { - return; - } - player.getInterfaceManager().closeChatbox(); - player.debug("Received " + buffer.opcode()); - try { - switch (buffer.opcode()) { - case 78: // NPC reward 1 - int index = buffer.getLEShort(); - handleNPCInteraction(player, 0, index); - break; - case 3: // NPC reward 2 - index = buffer.getLEShortA(); - handleNPCInteraction(player, 1, index); - break; - case 148: // NPC reward 3 - index = buffer.getShortA(); - handleNPCInteraction(player, 2, index); - break; - case 30: // NPC reward 4 - index = buffer.getShort(); - handleNPCInteraction(player, 3, index); - break; - case 218: // NPC reward 5 - index = buffer.getLEShort(); - handleNPCInteraction(player, 4, index); - break; - case 254: // Object reward 1 - int x = buffer.getLEShort(); - int objectId = buffer.getShortA() & 0xFFFF; - int y = buffer.getShort(); - handleObjectInteraction(player, 0, x, y, objectId); - break; - case 194: // Object reward 2 - y = buffer.getLEShortA(); - x = buffer.getLEShort(); - objectId = buffer.getShort() & 0xFFFF; - handleObjectInteraction(player, 1, x, y, objectId); - break; - case 84: // Object reward 3 - objectId = buffer.getLEShortA() & 0xFFFF; - y = buffer.getLEShortA(); - x = buffer.getLEShort(); - handleObjectInteraction(player, 2, x, y, objectId); - break; - case 152: // Object reward 4 TODO - objectId = buffer.getLEShortA() & 0xFFFF; - x = buffer.getLEShort(); - y = buffer.getLEShortA(); - handleObjectInteraction(player, 3, x, y, objectId); - break; - case 247://Object reward 4 - y = buffer.getLEShort() & 0xFFFF; - x = buffer.getLEShortA(); - objectId = buffer.getShort() & 0xFFFF; - handleObjectInteraction(player, 3, x, y, objectId); - break; - case 170: // Object reward 5 - objectId = buffer.getLEShortA() & 0xFFFF; - x = buffer.getLEShortA(); - y = buffer.getLEShortA(); - handleObjectInteraction(player, 4, x, y, objectId); - break; - case 68: // Player reward 1 - Challenge - index = buffer.getLEShortA(); - handlePlayerInteraction(player, 0, index); - break; - /*case : // Player reward 2 TODO - index = buffer.getLEShort(); - handlePlayerInteraction(player, 1, index); - break;*/ - case 71: // Player reward 3 - follow - index = buffer.getLEShortA(); - handlePlayerInteraction(player, 2, index); - break; - case 180: // Player reward 4 - trade - index = buffer.getLEShortA(); - handlePlayerInteraction(player, 3, index); - break; - /*case : // Player reward 5 TODO - index = buffer.getLEShortA(); - handlePlayerInteraction(player, 4, index); - break;*/ - /*case : // Player reward 6 TODO - index = buffer.getShort(); - handlePlayerInteraction(player, 5, index); - break;*/ - case 114: // Player reward 7 - req assistance - index = buffer.getLEShortA(); - handlePlayerInteraction(player, 6, index); - break; - case 175: // Player reward 8 - "whack" (also "control" for AIPs) - index = buffer.getShortA(); - handlePlayerInteraction(player, 7, index); - break; - case 66: // Ground item reward 1 - x = buffer.getLEShort(); - int itemId = buffer.getShort(); - y = buffer.getLEShortA(); - handleGroundItemInteraction(player, 2, itemId, Location.create(x, y, player.getLocation().getZ())); - break; - case 33: // Ground item reward 2 - itemId = buffer.getShort(); - x = buffer.getShort(); - y = buffer.getLEShort(); - handleGroundItemInteraction(player, 3, itemId, Location.create(x, y, player.getLocation().getZ())); - break; - } - } catch (Exception e){ - e.printStackTrace(); - } - } - - /** - * Handles the NPC interaction. - * @param player the player. - * @param index the index. - */ - public static void handleNPCInteraction(Player player, int optionIndex, final int index) { - if (index < 1 || index > ServerConstants.MAX_NPCS) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - return; - } - final NPC npc = Repository.getNpcs().get(index); - if (npc == null) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - return; - } - if (player.getAttribute("removenpc", false)) { - npc.clear(); - player.getPacketDispatch().sendMessage("Removed npc=" + npc.toString()); - return; - } - NPC shown = npc.getShownNPC(player); - Option option = shown.getInteraction().get(optionIndex); - if (option == null) { - if (Arrays.stream(hweenNPCs).anyMatch(i -> i == shown.getId())) { - option = new Option("trick-or-treat", -1); - } - } - if(option == null) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - Interaction.handleInvalidInteraction(player, npc, Option.NULL); - return; - } - player.debug("NPC Interacting with \"" + shown.getUsername() + "\" [index=" + index + ", renderable=" + npc.isRenderable() + "]"); - player.debug("option=" + option.getName() + ", slot=" + option.getIndex() + ", id=" + shown.getId() + " original=" + npc.getId() + ", location=" + npc.getLocation() + ""); - player.debug("spawn=" + npc.getProperties().getSpawnLocation() + "."); - player.debug("Varp ID= " + npc.getDefinition().getConfigId() + " Offset=" + npc.getDefinition().getVarbitOffset() + " Size=" + npc.getDefinition().getVarbitSize()); - handleAIPLegion(player, 0, optionIndex, index); - if(InteractionListeners.run(shown.getId(),2, option.getName(),player,npc)){ - return; - } - if(PluginInteractionManager.handle(player,shown,option)){ - return; - } - npc.getInteraction().handle(player, option); - } - - /** - * Handles Node interaction with the first index - * @param player The interacting player. - * @param n The node to interact with. - */ - public static void handleObjectInteraction(final Player player, Node n) - { - handleObjectInteraction(player, 0, n.getLocation(), n.getId()); - } - - /** - * Handles object interaction - * @param player The interacting player. - * @param optionIndex The option index. - * @param l The (x,y) location of the object. - * @param objectId The object id. - */ - public static void handleObjectInteraction(final Player player, int optionIndex, Location l, int objectId) { - handleObjectInteraction(player, optionIndex, l.getX(), l.getY(), objectId); - } - - /** - * Handles object interaction - * @param player The interacting player. - * @param optionIndex The option index. - * @param x The x-coordinate of the object. - * @param y The y-coordinate of the object. - * @param objectId The object id. - */ - public static void handleObjectInteraction(final Player player, int optionIndex, int x, int y, int objectId) { - Scenery object = RegionManager.getObject(player.getLocation().getZ(), x, y, objectId); - if (objectId == 29735) {//player safety. - player.getPulseManager().run(new MovementPulse(player, Location.create(x, y, player .getLocation().getZ())) { - @Override - public boolean pulse() { - player.getDialogueInterpreter().sendDialogue("There appears to be a tunnel behind the poster."); - player.getDialogueInterpreter().addAction((player1, buttonId) -> player1.teleport(new Location(3140, 4230, 2))); - return true; - } - }, "movement"); - return; - } - if (objectId == 6898) { - object = new Scenery(6898, new Location(3219, 9618)); - } else if (objectId == 6899) { - object = new Scenery(6899, new Location(3221, 9618)); - } - if (object == null || object.getId() != objectId) { - player.debug("Scenery(" + objectId + ") interaction was " + object + " at location " + x + ", " + y + "."); - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - Interaction.handleInvalidInteraction(player, object, Option.NULL); - return; - } - if (!object.isActive()) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - Interaction.handleInvalidInteraction(player, object, Option.NULL); - return; - } - object = object.getChild(player); - Option option = object.getInteraction().get(optionIndex); - if (option == null) { - player.debug("Invalid option" + object + ", original: " + objectId + "."); - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - Interaction.handleInvalidInteraction(player, object, Option.NULL); - return; - } - player.debug(object + ", original=" + objectId + ", option=" + option.getName() + ""); - player.debug("dir=" + object.getDirection()); - VarbitDefinition def = VarbitDefinition.forObjectID(SceneryDefinition.forId(objectId).getVarbitID()); - player.debug("Varp ID=" + def.getConfigId() + " Offset=" + def.getBitShift() + " Size=" + def.getBitSize()); - player.debug("Varbit: " + def.getId()); - if (option.getHandler() != null) { - player.debug("Object handler: " + option.getHandler().getClass().getSimpleName()); - } - handleAIPLegion(player, 1, optionIndex, x, y, objectId); - - if(InteractionListeners.run(object.getId(),1, option.getName(),player,object)){ - return; - } - if(PluginInteractionManager.handle(player,object)){ - return; - } - object.getInteraction().handle(player, option); - } - - /** - * Handles player interaction. - * @param player The player interacting. - * @param optionIndex The option index. - * @param index The target index. - */ - private static void handlePlayerInteraction(Player player, int optionIndex, int index) { - if (index < 1 || index > ServerConstants.MAX_PLAYERS) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - return; - } - final Player target = Repository.getPlayers().get(index); - if (target == null || !target.isActive()) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - return; - } - final Option option = player.getInteraction().get(optionIndex); - //Handling for "Pelt" option - if (option == null) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - return; - } - handleAIPLegion(player, 2, optionIndex, index); - target.getInteraction().handle(player, option); - } - - /** - * Handles the ground item interaction. - * @param player The player. - * @param index The index of the reward. - * @param itemId The item id. - * @param location The location of the item. - */ - private static void handleGroundItemInteraction(final Player player, int index, int itemId, Location location) { - final GroundItem item = GroundItemManager.get(itemId, location, player); - SpecialGroundItems[] groundItems = SpecialGroundItems.values(); - for(SpecialGroundItems specialItem : groundItems){ - if(specialItem.getItemid() == itemId && specialItem.getLocation().getX() == location.getX() && specialItem.getLocation().getY() == location.getY()){ - player.debug("Using special ground item handler"); - specialItem.getInteraction().handle(player,item.getInteraction().get(index)); - return; - } - } - if (item == null) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - return; - } - final Option option = item.getInteraction().get(index); - if (option == null) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - Interaction.handleInvalidInteraction(player, item, Option.NULL); - return; - } - if(PluginInteractionManager.handle(player,item,option)){ - player.debug("Handled by quest interaction manager."); - return; - } - if(InteractionListeners.run(item.getId(), InteractionListener.Companion.getGROUNDITEM(), option.getName(),player,item)){ - return; - } - item.getInteraction().handle(player, option); - } - - /** - * Handles the AIPlayer legion. - * @param player The player. - * @param type The interaction type. - * @param args The arguments. - */ - private static void handleAIPLegion(Player player, int type, int... args) { - if (player.isArtificial()) { - List legion = player.getAttribute("aip_legion"); - if (legion != null) { - for (AIPlayer aip : legion) { - if (aip != player) { - switch (type) { - case 0: - handleNPCInteraction(aip, args[0], args[1]); - break; - case 1: - handleObjectInteraction(aip, args[0], args[1], args[2], args[3]); - break; - case 2: - handlePlayerInteraction(aip, args[0], args[1]); - break; - } - } - } - } - } - } - - /** - * Gets the player instance (used for AIP controlling). - * @param player The player. - * @return The player instance, or the AIP when the player is controlling an - * AIP. - */ - private static Player getPlayer(Player player) { - AIPlayer aip = player.getAttribute("aip_select"); - if (aip != null && aip.getLocation().withinDistance(player.getLocation())) { - return aip; - } - return player; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/InterfaceUseOnPacket.java b/Server/src/main/java/core/net/packet/in/InterfaceUseOnPacket.java deleted file mode 100644 index 15197610f..000000000 --- a/Server/src/main/java/core/net/packet/in/InterfaceUseOnPacket.java +++ /dev/null @@ -1,269 +0,0 @@ -package core.net.packet.in; - -import rs09.ServerConstants; -import core.game.node.entity.skill.magic.MagicSpell; -import core.game.node.entity.skill.summoning.familiar.FamiliarSpecial; -import core.game.interaction.MovementPulse; -import rs09.game.node.entity.combat.CombatSwingHandler; -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.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import rs09.game.node.entity.skill.magic.SpellListener; -import rs09.game.node.entity.skill.magic.SpellListeners; -import rs09.game.node.entity.skill.magic.SpellUtils; -import rs09.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import rs09.game.world.repository.Repository; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.net.packet.PacketRepository; -import core.net.packet.context.PlayerContext; -import core.net.packet.out.ClearMinimapFlag; - -/** - * Handles the Interface "Use" on packets. - * @author Stacx - */ -public class InterfaceUseOnPacket implements IncomingPacket { - - @SuppressWarnings("unused") - @Override - public void decode(final Player player, int opcode, IoBuffer buffer) { - int payload; - int interfaceId; - int componentId; - int itemId; - int x; - int y; - switch (buffer.opcode()) { - case 73: // Interface On GroundItem - componentId = buffer.getShort(); - interfaceId = buffer.getShort(); - y = buffer.getShort(); - itemId = buffer.getLEShortA(); - x = buffer.getLEShortA(); - final int spell = buffer.getLEShort(); - final Item groundItem = GroundItemManager.get(itemId, Location.create(x, y, player.getLocation().getZ()), player); - if (groundItem == null || !player.getLocation().withinDistance(groundItem.getLocation())) { - break; - } - if (player.getAttribute("magic:delay", -1) > GameWorld.getTicks()) { - break; - } - if (player.getZoneMonitor().clickButton(interfaceId, componentId, spell, itemId, opcode)) { - break; - } - if (CombatSwingHandler.isProjectileClipped(player, groundItem, false)) { - SpellListeners.run(spell, SpellListener.GROUND_ITEM, SpellUtils.getBookFromInterface(interfaceId),player,groundItem); - MagicSpell.castSpell(player, SpellBookManager.SpellBook.MODERN, spell, groundItem); - } else { - player.getPulseManager().run(new MovementPulse(player, groundItem) { - @Override - public boolean update() { - if (CombatSwingHandler.isProjectileClipped(player, groundItem, false)) { - super.destination = player.getLocation(); - } - boolean finished = super.update(); - if (finished) { - player.getWalkingQueue().reset(); - } - return finished; - - } - - @Override - public boolean pulse() { - SpellListeners.run(spell, SpellListener.GROUND_ITEM,SpellUtils.getBookFromInterface(interfaceId),player,groundItem); - MagicSpell.castSpell(player, SpellBookManager.SpellBook.MODERN, spell, groundItem); - return true; - } - }, "movement"); - } - break; - case 195: // Interface On Player - //payload = buffer.getShortA(); - buffer.getShortA(); - componentId = buffer.getLEShort(); - interfaceId = buffer.getLEShort(); - int targetIndex = buffer.getLEShortA(); - // Logger.log("Interface:" + interfaceId+ " Component:" + - // componentId + " Target Index:"+ targetIndex); - //System.out.println("magic on player component = "+componentId+"!"); - final Player target = Repository.getPlayers().get(targetIndex); - if (target == null || !player.getLocation().withinDistance(target.getLocation())) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - break; - } - if(!SpellUtils.getBookFromInterface(interfaceId).equals("none")){ - SpellListeners.run(componentId,SpellListener.PLAYER,SpellUtils.getBookFromInterface(interfaceId),player,target); - } - switch (interfaceId) { - case 192: - MagicSpell.castSpell(player, SpellBookManager.SpellBook.MODERN, componentId, target); - break; - case 193: - MagicSpell.castSpell(player, SpellBookManager.SpellBook.ANCIENT, componentId, target); - break; - case 430: - MagicSpell.castSpell(player, SpellBookManager.SpellBook.LUNAR, componentId, target); - break; - case 662: - switch (componentId) { - case 67: - case 69: - case 119: - case 121: - default: - if (!player.getFamiliarManager().hasFamiliar()) { - player.getPacketDispatch().sendMessage("You don't have a familiar."); - } else { - player.getFamiliarManager().getFamiliar().executeSpecialMove(new FamiliarSpecial(target)); - } - break; - } - break; - default: - player.debug("Option usage [inter=" + interfaceId + ", child=" + componentId + ", target=" + target + "]."); - } - break; - case 233: // Interface On Object - y = buffer.getLEShortA(); - x = buffer.getShortA(); - itemId = buffer.getLEShortA(); - if (itemId == 65535) { - itemId = -1; - } - payload = buffer.getIntA(); - interfaceId = payload >> 16; - componentId = payload & 0xFFFF; - int objectId = buffer.getShortA(); - player.debug("Option usage [inter=" + interfaceId + ", child=" + componentId + ", target=(" + objectId + "," + x + "," + y + "), item=" + itemId + "]."); - Scenery object = RegionManager.getObject(player.getLocation().getZ(), x, y); - if (object == null) { - object = RegionManager.getObject(Location.create(x, y, 0)); - } - if (object != null) { - object = object.getChild(player); - } - if (object == null || (object.getId() != objectId && object.getWrapper().getId() != objectId) || !player.getLocation().withinDistance(object.getLocation())) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - break; - } - if(!SpellUtils.getBookFromInterface(interfaceId).equals("none")){ - SpellListeners.run(componentId,SpellListener.OBJECT,SpellUtils.getBookFromInterface(interfaceId),player,object); - } - switch (interfaceId) { - case 430: - MagicSpell.castSpell(player, SpellBookManager.SpellBook.LUNAR, componentId, object); - break; - case 192: - MagicSpell.castSpell(player, SpellBookManager.SpellBook.MODERN, componentId, object); - break; - case 662: - switch (componentId) { - case 137: - default: - player.getFamiliarManager().getFamiliar().executeSpecialMove(new FamiliarSpecial(object)); - break; - } - break; - } - break; - case 239: // Interface On NPC - componentId = buffer.getLEShort(); - interfaceId = buffer.getLEShort(); - buffer.getShortA(); - int index = buffer.getLEShortA(); - if (index < 1 || index > ServerConstants.MAX_NPCS) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - break; - } - - NPC npc = Repository.getNpcs().get(index); - if (npc == null || !player.getLocation().withinDistance(npc.getLocation())) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - break; - } - if (player.getAttribute("magic:delay", -1) > GameWorld.getTicks()) { - break; - } - if(!SpellUtils.getBookFromInterface(interfaceId).equals("none")){ - SpellListeners.run(componentId,SpellListener.NPC, SpellUtils.getBookFromInterface(interfaceId),player,npc); - } - switch (interfaceId) { - case 430: - MagicSpell.castSpell(player, SpellBookManager.SpellBook.LUNAR, componentId, npc); - break; - case 192: - MagicSpell.castSpell(player, SpellBookManager.SpellBook.MODERN, componentId, npc); - break; - case 193: - MagicSpell.castSpell(player, SpellBookManager.SpellBook.ANCIENT, componentId, npc); - break; - case 662: - switch (componentId) { - case 67: - case 69: - case 177: - case 121: - case 119: - default: - if (!player.getFamiliarManager().hasFamiliar()) { - player.getPacketDispatch().sendMessage("You don't have a familiar."); - } else { - player.getFamiliarManager().getFamiliar().executeSpecialMove(new FamiliarSpecial(npc)); - } - break; - } - break; - default: - player.debug("Option usage [inter=" + interfaceId + ", child=" + componentId + ", target=" + npc + "]."); - } - break; - case 253: // Interface On Item - componentId = buffer.getLEShort(); - interfaceId = buffer.getLEShort(); - int itemSlot = buffer.getLEShortA(); - itemId = buffer.getShortA(); - buffer.getShortA(); - if (itemSlot < 0 || itemSlot > 27) { - break; - } - Item item = player.getInventory().get(itemSlot); - if (item == null) { - break; - } - if(!SpellUtils.getBookFromInterface(interfaceId).equals("none")){ - SpellListeners.run(componentId,SpellListener.ITEM,SpellUtils.getBookFromInterface(interfaceId),player,item); - } - switch (interfaceId) { - case 430: - if (player.getAttribute("magic:delay", -1) > GameWorld.getTicks()) { - break; - } - MagicSpell.castSpell(player, SpellBookManager.SpellBook.LUNAR, componentId, item); - break; - case 192: - if (player.getAttribute("magic:delay", -1) > GameWorld.getTicks()) { - break; - } - MagicSpell.castSpell(player, SpellBookManager.SpellBook.MODERN, componentId, item); - break; - case 662: - if (player.getFamiliarManager().hasFamiliar()) { - player.getFamiliarManager().getFamiliar().executeSpecialMove(new FamiliarSpecial(item, interfaceId, componentId, item)); - } else { - player.getPacketDispatch().sendMessage("You don't have a follower."); - } - break; - default: - player.debug("Option usage [inter=" + interfaceId + ", child=" + componentId + ", target=" + item + "]."); - } - break; - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/ItemActionPacket.java b/Server/src/main/java/core/net/packet/in/ItemActionPacket.java deleted file mode 100644 index 930309318..000000000 --- a/Server/src/main/java/core/net/packet/in/ItemActionPacket.java +++ /dev/null @@ -1,249 +0,0 @@ -package core.net.packet.in; - -import core.game.content.quest.PluginInteractionManager; -import core.game.interaction.DestinationFlag; -import core.game.interaction.MovementPulse; -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.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.world.map.RegionManager; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.net.packet.PacketRepository; -import core.net.packet.context.PlayerContext; -import core.net.packet.out.ClearMinimapFlag; -import org.rs09.consts.Items; -import rs09.game.interaction.ItemOnBankBooth; -import rs09.game.interaction.InteractionListeners; -import rs09.game.node.entity.skill.farming.CompostBins; -import rs09.game.node.entity.skill.farming.FarmingPatch; -import rs09.game.node.entity.skill.farming.UseWithBinHandler; -import rs09.game.node.entity.skill.farming.UseWithPatchHandler; -import rs09.game.system.SystemLogger; -import rs09.game.system.command.rottenpotato.RottenPotatoUseWithHandler; -import rs09.game.world.repository.Repository; - -/** - * The incoming item reward packet. - * @author Emperor - */ -public class ItemActionPacket implements IncomingPacket { - - @SuppressWarnings("unused") - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - if (player.getLocks().isInteractionLocked()) { - return; - } - int usedWithItemId = -1; - int usedWithSlot = -1; - int interfaceHash1 = -1; - int interfaceId1 = -1; - int childId1 = -1; - int usedItemId = -1; - int usedSlot = -1; - int interfaceHash2 = -1; - int interfaceId2 = -1; - int childId2 = -1; - NodeUsageEvent event = null; - Item used = null; - switch (buffer.opcode()) { - case 115: // Item on NPC - int interfaceId = buffer.getIntA() >> 16; - int slotId = buffer.getLEShort(); - int npcIndex = buffer.getLEShort(); - int itemId = buffer.getLEShortA(); - NPC npc = Repository.getNpcs().get(npcIndex); - Item item = player.getInventory().get(slotId); - if (item == null || item.getId() != itemId) { - return; - } - if(itemId == Items.ROTTEN_POTATO_5733){ - RottenPotatoUseWithHandler.handle(npc,player); - return; - } - event = new NodeUsageEvent(player, interfaceId, item, npc); - if(PluginInteractionManager.handle(player,event)){ - return; - } - if(InteractionListeners.run(item,npc,2,player)){ - return; - } - if(player.getZoneMonitor().useWith(used,npc)){ - return; - } - event = new NodeUsageEvent(player, interfaceId, item, npc); - try { - UseWithHandler.run(event); - } catch (Exception e){ - e.printStackTrace(); - } - return; - case 248: // Item on Player - int playerIndex = buffer.getLEShortA(); - itemId = buffer.getShort(); - slotId = buffer.getShort(); - interfaceId = buffer.getInt() >> 16; - Player target = Repository.getPlayers().get(playerIndex); - item = player.getInventory().get(slotId); - if (target == null || item == null || item.getId() != itemId) { - return; - } - if(itemId == Items.ROTTEN_POTATO_5733){ - RottenPotatoUseWithHandler.handle(target,player); - return; - } - event = new NodeUsageEvent(player, interfaceId, item, target); - if(PluginInteractionManager.handle(player,event)){ - return; - } - if(player.getZoneMonitor().useWith(used,player)){ - return; - } - event = new NodeUsageEvent(player, interfaceId, item, target); - try { - UseWithHandler.run(event); - } catch (Exception e){ - e.printStackTrace(); - } - return; - case 27://Item on Item - usedSlot = buffer.getShort(); - interfaceHash1 = buffer.getLEInt(); - usedWithSlot = buffer.getLEShort(); - interfaceHash2 = buffer.getLEInt(); - usedItemId = buffer.getLEShortA(); - usedWithItemId = buffer.getLEShortA(); - interfaceId1 = interfaceHash1 >> 16; - childId1 = interfaceHash1 & 0xFFFF; - interfaceId2 = interfaceHash2 >> 16; - childId2 = interfaceHash2 & 0xFFFF; - used = player.getInventory().get(usedSlot); - Item with = player.getInventory().get(usedWithSlot); - if (used == null || with == null || used.getId() != usedItemId || with.getId() != usedWithItemId) { - return; - } - if(used.getId() == Items.ROTTEN_POTATO_5733){ - RottenPotatoUseWithHandler.handle(with,player); - return; - } - if(with.getId() == Items.ROTTEN_POTATO_5733){ - RottenPotatoUseWithHandler.handle(used,player); - return; - } - if(InteractionListeners.run(used,with,0,player)){ - return; - } - if(player.getZoneMonitor().useWith(used,with)){ - return; - } - if (usedItemId < usedWithItemId) { - item = used; - used = with; - with = item; - } - player.debug("USED: " + used + " WITH: " + with); - event = new NodeUsageEvent(player, interfaceId1, used, with); - if(PluginInteractionManager.handle(player,event)){ - return; - } - event = new NodeUsageEvent(player, interfaceId1, used, with); - try { - UseWithHandler.run(event); - } catch (Exception e){ - e.printStackTrace(); - } - break; - case 134://Item on Object - int x = buffer.getShortA(); - int id = buffer.getShort(); - int y = buffer.getLEShort(); - int slot = buffer.getShort(); - buffer.getLEShort(); - buffer.getShort(); - int objectId = buffer.getShortA(); - int z = player.getLocation().getZ(); - Scenery object = RegionManager.getObject(z, x, y); - if(objectId != 6898) { - if (object == null || object.getId() != objectId) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - return; - } - object = object.getChild(player); - if (object == null) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - break; - } - } else { - object = new Scenery(6898,x,y,z); - } - used = player.getInventory().get(slot); - if (used == null || used.getId() != id) { - return; - } - if(used.getId() == Items.ROTTEN_POTATO_5733){ - RottenPotatoUseWithHandler.handle(object,player); - return; - } - if(InteractionListeners.run(used,object,1,player)){ - return; - } - if(player.getZoneMonitor().useWith(used,object)){ - return; - } - event = new NodeUsageEvent(player, 0, used, object); - - /** - * Farming-specific handler for item -> patch - */ - if(FarmingPatch.forObject(object) != null && UseWithPatchHandler.allowedNodes.contains(used.getId())){ - NodeUsageEvent finalEvent = event; - player.getPulseManager().run(new MovementPulse(player,object, DestinationFlag.OBJECT) { - @Override - public boolean pulse() { - player.faceLocation(destination.getLocation()); - UseWithPatchHandler.handle(finalEvent); - return true; - } - }); - return; - } - - /** - * Farming-specific handler for item -> compost bin - */ - if(CompostBins.forObject(object) != null && UseWithBinHandler.allowedNodes.contains(used.getId())){ - NodeUsageEvent finalEvent = event; - player.getPulseManager().run(new MovementPulse(player,object,DestinationFlag.OBJECT){ - @Override - public boolean pulse() { - player.faceLocation(destination.getLocation()); - UseWithBinHandler.handle(finalEvent); - return true; - } - }); - return; - } - - if(PluginInteractionManager.handle(player,event)){ - return; - } - if(object.getName().toLowerCase().contains("bank booth")){ - new ItemOnBankBooth().handle(event); - return; - } - try { - UseWithHandler.run(event); - } catch (Exception e){ - e.printStackTrace(); - } - return; - default: - SystemLogger.logErr("Error in Item Action Packet! Unhandled opcode = " + buffer.opcode()); - return; - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/MapClosedPacket.java b/Server/src/main/java/core/net/packet/in/MapClosedPacket.java deleted file mode 100644 index e98f30148..000000000 --- a/Server/src/main/java/core/net/packet/in/MapClosedPacket.java +++ /dev/null @@ -1,12 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -public class MapClosedPacket implements IncomingPacket { - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - //This buffer contains an int that is actually a bunch of booleans bitshifted into specific positions. No clue what the use might be. - } -} diff --git a/Server/src/main/java/core/net/packet/in/MouseClickPacket.java b/Server/src/main/java/core/net/packet/in/MouseClickPacket.java deleted file mode 100644 index 23cecfe4d..000000000 --- a/Server/src/main/java/core/net/packet/in/MouseClickPacket.java +++ /dev/null @@ -1,27 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles incoming mouse click packets. - * @author Emperor - */ -public final class MouseClickPacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - int data = buffer.getLEShortA(); - int positioning = buffer.getIntB(); - boolean rightClick = ((data >> 15) & 0x1) == 1; - int delay = data & 0x7FF; - int x = positioning >> 16; - int y = positioning & 0xFFFF; - if (player == null) { - return; - } - player.getMonitor().handleMouseClick(x, y, delay, rightClick); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/MusicPacketHandler.java b/Server/src/main/java/core/net/packet/in/MusicPacketHandler.java deleted file mode 100644 index 8dbc4506f..000000000 --- a/Server/src/main/java/core/net/packet/in/MusicPacketHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles music-related incoming packets. - * @author Ceikry - */ -public final class MusicPacketHandler implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - int musicId = buffer.getLEShortA(); - if (player.getMusicPlayer().isLooping()) { - player.getMusicPlayer().replay(); - return; - } - player.getMusicPlayer().setPlaying(false); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/PingPacketHandler.java b/Server/src/main/java/core/net/packet/in/PingPacketHandler.java deleted file mode 100644 index 38a4b6eaf..000000000 --- a/Server/src/main/java/core/net/packet/in/PingPacketHandler.java +++ /dev/null @@ -1,18 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles an incoming ping packet. - * @author Emperor - */ -public final class PingPacketHandler implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/RegionChangePacket.java b/Server/src/main/java/core/net/packet/in/RegionChangePacket.java deleted file mode 100644 index 4db834ec7..000000000 --- a/Server/src/main/java/core/net/packet/in/RegionChangePacket.java +++ /dev/null @@ -1,19 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Packet received when a player's region has changed. - * @author Emperor - * @author 'Vexia - */ -public class RegionChangePacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - //TODO: no data is sen't so not sure what to do. - } - -} diff --git a/Server/src/main/java/core/net/packet/in/ReportAbusePacket.java b/Server/src/main/java/core/net/packet/in/ReportAbusePacket.java deleted file mode 100644 index 466f65e15..000000000 --- a/Server/src/main/java/core/net/packet/in/ReportAbusePacket.java +++ /dev/null @@ -1,36 +0,0 @@ -package core.net.packet.in; - -import rs09.ServerConstants; -import core.game.content.global.report.AbuseReport; -import core.game.content.global.report.Rule; -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.tools.StringUtils; - -import java.io.File; - -/** - * Represents the incoming packet to handle a report against a player. - * @author Vexia - */ -public class ReportAbusePacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - String target = StringUtils.longToString(buffer.getLong()); - Rule rule = Rule.forId(buffer.get()); - boolean mute = buffer.get() == 1; - File file = new File(ServerConstants.PLAYER_SAVE_PATH + target + ".save"); - if (!file.exists()) { - player.getPacketDispatch().sendMessage("Invalid player name."); - return; - } - if (target.equalsIgnoreCase(player.getUsername())) { - player.getPacketDispatch().sendMessage("You can't report yourself!"); - return; - } - AbuseReport abuse = new AbuseReport(player.getName(), target, rule); - abuse.construct(player, mute); - } -} diff --git a/Server/src/main/java/core/net/packet/in/RunScriptPacketHandler.java b/Server/src/main/java/core/net/packet/in/RunScriptPacketHandler.java deleted file mode 100644 index 6cc2f3e2f..000000000 --- a/Server/src/main/java/core/net/packet/in/RunScriptPacketHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -package core.net.packet.in; - -import api.ContentAPI; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.tools.StringUtils; -import kotlin.jvm.functions.Function1; - -/** - * Represents the incoming packet to handle a run script. - * @author 'Vexia - */ -public class RunScriptPacketHandler implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - final Function1 script = player.getAttribute("runscript", null); - if (script == null || player.getLocks().isInteractionLocked()) { - return; - } - Object value = ""; - if (opcode == 244) { - value = StringUtils.longToString(buffer.getLong()); - } else if (opcode == 23) { - value = buffer.getInt(); - } else if (opcode == 65){ - value = buffer.getString();//"longInput" - } else { - value = buffer.getInt(); - } - try { - script.invoke(value); - } catch (NumberFormatException nfe){ - ContentAPI.sendDialogue(player, "That number's a bit large, don't you think?"); - } - player.removeAttribute("runscript"); - } -} diff --git a/Server/src/main/java/core/net/packet/in/SlotSwitchPacket.java b/Server/src/main/java/core/net/packet/in/SlotSwitchPacket.java deleted file mode 100644 index 0359526c8..000000000 --- a/Server/src/main/java/core/net/packet/in/SlotSwitchPacket.java +++ /dev/null @@ -1,109 +0,0 @@ -package core.net.packet.in; - -import core.game.container.Container; -import core.game.container.impl.BankContainer; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Represents the packet to handle an item slot switch. - * @author 'Vexia - */ -public class SlotSwitchPacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - if (buffer.opcode() == 79) { - int interfaceHash = buffer.getIntB(); - int secondSlot = buffer.getLEShort(); - int interfaceHash2 = buffer.getInt(); - int slot = buffer.getLEShort(); - int interfaceId = interfaceHash >> 16; - int childId = interfaceHash & 0xFF; - int withInterfaceId = interfaceHash2 >> 16; - int withChildId = interfaceHash2 & 0xFF; - Container container = player.getInventory(); - switch (interfaceId) { - case 762: - if (withInterfaceId == 762) { - if (withChildId == 73) { - container = player.getBank(); - switchItem(slot, secondSlot, container, player.getBank().isInsertItems(), player); - player.debug("Switching item [" + slot + ", " + interfaceId + ", " + childId + "] with [" + secondSlot + ", " + withInterfaceId + ", " + withChildId + "]!"); - } - else { - int tabIndex = BankContainer.getArrayIndex(withChildId); - if (tabIndex > -1) { - secondSlot = tabIndex == 10 ? player.getBank().freeSlot() : player.getBank().getTabStartSlot()[tabIndex] + player.getBank().getItemsInTab(tabIndex); - childId = player.getBank().getTabByItemSlot(slot); - if (secondSlot > slot) { - player.getBank().insert(slot, secondSlot - 1); - } else if (slot > secondSlot) { - player.getBank().insert(slot, secondSlot); - } - player.getBank().increaseTabStartSlots(tabIndex); - player.getBank().decreaseTabStartSlots(childId); - player.getBank().setTabConfigurations(); - return; - } - } - break; - } - break; - default: - player.debug("Switching item slot [from=" + slot + ", to=" + secondSlot + ", child=" + childId + ", to child=" + withChildId + "]."); - switchItem(slot, secondSlot, container, false, player); - break; - } - return; - } - int slot = buffer.getShort(); - int interfaceHash = buffer.getLEInt(); - int secondSlot = buffer.getShortA(); - boolean insert = buffer.get() == 1; - int interfaceId = interfaceHash >> 16; - Container container = interfaceId == 762 ? player.getBank() : (interfaceId == 15 || interfaceId == 149 || interfaceId == 763) ? player.getInventory() : null; - switchItem(slot, secondSlot, container, insert, player); - } - - /** - * Switches an item. - * @param slot The slot. - * @param secondSlot The slot to switch slots with. - * @param container The container. - * @param insert If inserting should happen. - */ - public void switchItem(int slot, int secondSlot, Container container, boolean insert, Player player) { - if (container == null || slot < 0 || slot >= container.toArray().length || secondSlot < 0 || secondSlot >= container.toArray().length) { - return; - } - - final Item item = container.get(slot); - final Item second = container.get(secondSlot); - if (player.getInterfaceManager().hasChatbox()) { - player.getInterfaceManager().closeChatbox(); - switchItem(secondSlot,slot,container,insert,player); - container.refresh(); - return; - } - if (item == null) { - return; - } - if (!insert) { - container.replace(second, slot, false); - container.replace(item, secondSlot, false); - if (item != null) { - item.setIndex(secondSlot); - } - if (second != null) { - second.setIndex(slot); - } - } else { - container.insert(slot, secondSlot, false); - } - container.refresh(); - } - -} diff --git a/Server/src/main/java/core/net/packet/in/UpdateInterfaceCounter.java b/Server/src/main/java/core/net/packet/in/UpdateInterfaceCounter.java deleted file mode 100644 index 3e10fa8bd..000000000 --- a/Server/src/main/java/core/net/packet/in/UpdateInterfaceCounter.java +++ /dev/null @@ -1,20 +0,0 @@ -package core.net.packet.in; - -import core.game.node.entity.player.Player; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; - -/** - * Handles the update interface packet counter packet. - * @author Emperor - * - */ -public final class UpdateInterfaceCounter implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - int count = buffer.getShort() - player.getInterfaceManager().getPacketCount(0); - player.getInterfaceManager().getPacketCount(count); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/in/WalkPacket.java b/Server/src/main/java/core/net/packet/in/WalkPacket.java deleted file mode 100644 index a934d4a36..000000000 --- a/Server/src/main/java/core/net/packet/in/WalkPacket.java +++ /dev/null @@ -1,101 +0,0 @@ -package core.net.packet.in; - -import core.game.interaction.MovementPulse; -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import rs09.game.ai.AIPlayer; -import core.game.world.map.Location; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.net.packet.PacketRepository; -import core.net.packet.context.PlayerContext; -import core.net.packet.out.ClearMinimapFlag; - -/** - * Handles an incoming walk packet. - * @author Emperor - */ -public final class WalkPacket implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - player = getPlayer(player); - if (player == null) { - return; - } - if (player.getLocks().isMovementLocked() || !player.getInterfaceManager().close() || !player.getInterfaceManager().closeSingleTab() || !player.getDialogueInterpreter().close()) { - PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); - player.debug("[WalkPacket] did not handle - [locked=" + player.getLocks().isMovementLocked() + "]!"); - return; - } - player.getProperties().setSpell(null); - player.getInterfaceManager().close(); - player.getInterfaceManager().closeChatbox(); - player.getDialogueInterpreter().getActions().clear(); - boolean running = buffer.getA() == 1; - int x = buffer.getShort(); - int y = buffer.getShortA(); - int steps = (buffer.toByteBuffer().remaining() - (buffer.opcode() == 39 ? 14 : 0)) >> 1; - for (int i = 0; i < steps; i++) { - int offsetX = buffer.getA(); - int offsetY = buffer.getS(); - if (i == steps - 1) { - x += offsetX; - y += offsetY; - } - } - if (opcode == 77) { - player.getWalkingQueue().setRunning(running); - return; // Action walking. - } - player.face((Entity) null); - player.faceLocation((Location) null); - //player.getWalkingQueue().reset(running); - Player finalPlayer = player; - player.getPulseManager().run(new MovementPulse(finalPlayer, Location.create(x, y, player.getLocation().getZ()), running) { - @Override - public boolean pulse() { - if(running){ - finalPlayer.getWalkingQueue().setRunning(false); - } - return true; - } - }, true, "movement"); - if (opcode == 39) { - buffer.get(); // The x-coordinate of where we clicked on the - // minimap. - buffer.get(); // The y-coordinate of where we clicked on the - // minimap. - buffer.getShort(); // The rotation of the minimap. - buffer.get(); // Always 57. - buffer.get(); - buffer.get(); - buffer.get(); // Always 89 - buffer.getShort(); - buffer.getShort(); - buffer.get(); - buffer.get(); // Always 63 - } - } - - /** - * Gets the player instance (used for AIP controlling). - * @param player The player. - * @return The player instance, or the AIP when the player is controlling an - * AIP. - */ - private static Player getPlayer(Player player) { - if (player == null) { - return null; - } - AIPlayer aip = player.getAttribute("aip_select"); - if (aip != null && aip.getLocation().withinDistance(player.getLocation())) { - if (!player.getAttribute("aip_perm_select", true)) { - player.removeAttribute("aip_select"); - } - return aip; - } - return player; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/packet/out/AudioPacket.java b/Server/src/main/java/core/net/packet/out/AudioPacket.java deleted file mode 100644 index 11e4b6e33..000000000 --- a/Server/src/main/java/core/net/packet/out/AudioPacket.java +++ /dev/null @@ -1,27 +0,0 @@ -package core.net.packet.out; - -import core.game.node.entity.player.link.audio.Audio; -import core.net.packet.IoBuffer; -import core.net.packet.OutgoingPacket; -import core.net.packet.context.DefaultContext; - -/** - * Sends an audio packet. - * @author Vexia - */ -public class AudioPacket implements OutgoingPacket { - - //208 music effect - //4 music - //172 sound effect - @Override - public void send(DefaultContext context) { - final Audio audio = (Audio) context.getObjects()[0]; - IoBuffer buffer = new IoBuffer(172); - buffer.putShort(audio.getId()); - buffer.put((byte) audio.getVolume()); - buffer.putShort(audio.getDelay()); - buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getSession().write(buffer); - } - -} diff --git a/Server/src/main/java/core/net/packet/out/ContainerPacket.java b/Server/src/main/java/core/net/packet/out/ContainerPacket.java deleted file mode 100644 index be4dcd001..000000000 --- a/Server/src/main/java/core/net/packet/out/ContainerPacket.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.net.packet.out; - -import core.game.container.ContainerEvent; -import core.game.node.item.Item; -import core.net.packet.IoBuffer; -import core.net.packet.OutgoingPacket; -import core.net.packet.PacketHeader; -import core.net.packet.context.ContainerContext; - -/** - * Represents the outgoing container packet. - * @author Emperor - */ -public final class ContainerPacket implements OutgoingPacket { - - @Override - public void send(ContainerContext context) { - IoBuffer buffer = null; - if (context.isClear()) { - buffer = new IoBuffer(144); - buffer.putIntB(context.getInterfaceId() << 16 | context.getChildId()); - } else { - boolean slotBased = context.getSlots() != null; - buffer = new IoBuffer(slotBased ? 22 : 105, PacketHeader.SHORT); - buffer.putShort(context.getInterfaceId()); - buffer.putShort(context.getChildId()); - buffer.putShort(context.getType()); - if (slotBased) { - for (int slot : context.getSlots()) { - buffer.putSmart(slot); - Item item = context.getItems()[slot]; - if (item != null && !item.equals(ContainerEvent.NULL_ITEM)) { - buffer.putShort(item.getId() + 1); - int amount = item.getAmount(); - if (amount < 0 || amount > 254) { - buffer.put(255).putInt(amount); - } else { - buffer.put(amount); - } - } else { - buffer.putShort(0); - } - } - } 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); - } - -} \ No newline at end of file diff --git a/Server/src/main/java/core/net/registry/AccountRegister.java b/Server/src/main/java/core/net/registry/AccountRegister.java deleted file mode 100644 index 7fc98a9a5..000000000 --- a/Server/src/main/java/core/net/registry/AccountRegister.java +++ /dev/null @@ -1,199 +0,0 @@ -package core.net.registry; - -import core.cache.misc.buffer.ByteBufferUtils; -import core.game.node.entity.player.info.portal.PlayerSQLManager; -import core.game.system.SystemManager; -import core.game.system.mysql.SQLEntryHandler; -import core.game.system.mysql.SQLManager; -import core.game.system.task.TaskExecutor; -import core.net.Constants; -import core.net.IoSession; -import kotlin.Unit; -import rs09.ServerConstants; -import rs09.game.system.SystemLogger; -import rs09.game.world.GameWorld; -import rs09.net.event.LoginReadEvent; - -import java.nio.ByteBuffer; -import java.sql.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Handles the registry of new accounts. - * @author Vexia - * - */ -public class AccountRegister extends SQLEntryHandler { - - /** - * The table name. - */ - private static final String TABLE = "members"; - - /** - * The column name. - */ - private static final String COLUMN = "username"; - - /** - * The pattern compiler. - */ - private static final Pattern PATTERN = Pattern.compile("[a-z0-9_]{1,12}"); - - /** - * Constructs a new {@Code AccountRegister} {@Code Object} - * @param entry The registry entry. - */ - public AccountRegister(RegistryDetails entry) { - super(entry, TABLE, COLUMN, entry.getUsername()); - } - - /** - * Reads the incoming opcode of an account register. - * @param session the session. - * @param opcode the opcode. - * @param buffer the buffer. - */ - public static void read(final IoSession session, int opcode, ByteBuffer buffer) { - int day,month,year,country; - switch (opcode) { - case 147://details - day = buffer.get(); - month = buffer.get(); - year = buffer.getShort(); - country = buffer.getShort(); - response(session, RegistryResponse.SUCCESS); - break; - case 186://username - final String username = ByteBufferUtils.getString(buffer).replace(" ", "_").toLowerCase().replace("|", ""); - if (username.length() <= 0 || username.length() > 12) { - response(session, RegistryResponse.INVALID_USERNAME); - break; - } - if (!validUsername(username)) { - System.out.println("AHAHHA " + username); - response(session,RegistryResponse.INVALID_USERNAME); - break; - } - System.out.println(username); - TaskExecutor.executeSQL(() -> { - try { - if (PlayerSQLManager.hasSqlAccount(username, "username")) { - response(session, RegistryResponse.NOT_AVAILBLE_USER); - return Unit.INSTANCE; - } - response(session, RegistryResponse.SUCCESS); - } catch (SQLException e) { - e.printStackTrace(); - } - return Unit.INSTANCE; - }); - break; - case 36://Register details - buffer.get(); //Useless size being written that is already written in the RSA block - buffer = LoginReadEvent.getRSABlock(buffer); - if(buffer.get() != 10){ //RSA header (aka did this decrypt properly) - response(session, RegistryResponse.CANNOT_CREATE); - break; - } - buffer.getShort(); // random data - int revision = buffer.getShort();//revision? - if (revision != Constants.REVISION) { - response(session, RegistryResponse.CANNOT_CREATE); - break; - } - final String name = ByteBufferUtils.getString(buffer).replace(" ", "_").toLowerCase().replace("|", ""); - buffer.getInt(); - String password = ByteBufferUtils.getString(buffer); - if (password.length() < 5 || password.length() > 20) { - response(session, RegistryResponse.INVALID_PASS_LENGTH); - break; - } - if (password.equals(name)) { - response(session, RegistryResponse.PASS_SIMILAR_TO_USER); - break; - } - if (!validUsername(name)) { - response(session, RegistryResponse.INVALID_USERNAME); - break; - } - buffer.getInt(); - buffer.getShort(); - day = buffer.get(); - month = buffer.get(); - buffer.getInt(); - year = buffer.getShort(); - country = buffer.getShort(); - buffer.getInt(); - @SuppressWarnings("deprecation") - final RegistryDetails details = new RegistryDetails(name, SystemManager.getEncryption().hashPassword(password), new Date(year, month, day), country); - TaskExecutor.execute(() -> { - try { - if (PlayerSQLManager.hasSqlAccount(name, "username")) { - response(session, RegistryResponse.CANNOT_CREATE); - return Unit.INSTANCE; - } - SQLEntryHandler.write(new AccountRegister(details)); - response(session, RegistryResponse.SUCCESS); - } catch (SQLException e) { - e.printStackTrace(); - response(session, RegistryResponse.CANNOT_CREATE); - } - return Unit.INSTANCE; - }); - break; - default: - SystemLogger.logErr("Unhandled account registry opcode = " + opcode); - break; - } - } - - @Override - public void create() throws SQLException {} - - @Override - public void parse() throws SQLException {} - - @Override - public void save() throws SQLException { - PreparedStatement statement = getWritingStatement(true, "password", "salt", "birthday", "countryCode", "joined_date","currentClan"); - statement.setString(1, entry.getUsername()); - statement.setString(2, entry.getPassword()); - statement.setString(3, entry.getPassword().substring(0, 29)); - statement.setDate(4, entry.getBirth()); - statement.setInt(5, entry.getCountry()); - statement.setTimestamp(6, new Timestamp(System.currentTimeMillis())); - - statement.setString(7, GameWorld.getSettings().getEnable_default_clan() ? ServerConstants.SERVER_NAME.toLowerCase(): null); - - statement.executeUpdate(); - SQLManager.close(statement.getConnection()); - } - - /** - * Sends a registry response code. - * @param response the response. - */ - private static void response(IoSession session, RegistryResponse response) { - ByteBuffer buf = ByteBuffer.allocate(100); - buf.put((byte) response.getId()); - session.queue((ByteBuffer) buf.flip()); - } - - /** - * Checks if a username is valid. - * @return {@code True} if so. - */ - public static boolean validUsername(final String username) { - Matcher matcher = PATTERN.matcher(username); - return matcher.matches(); - - } - - @Override - public Connection getConnection() { - return SQLManager.getConnection(); - } - -} diff --git a/Server/src/main/java/core/tools/StringUtils.java b/Server/src/main/java/core/tools/StringUtils.java deleted file mode 100644 index b0a941986..000000000 --- a/Server/src/main/java/core/tools/StringUtils.java +++ /dev/null @@ -1,593 +0,0 @@ -package core.tools; - -import java.text.DecimalFormat; -import java.util.StringTokenizer; - -import core.net.packet.IoBuffer; - -/** - * The string utils. - * @author Emperor - */ -public final class StringUtils { - - /** - * The valid characters to be used in names/messages/... - */ - public static final char[] VALID_CHARS = { '_', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - /** - * Character mapping. - */ - public static char[] mapping = { '\n', '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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ' }; - /** - * The an int array241. - */ - public static int[] anIntArray241 = { 215, 203, 83, 158, 104, 101, 93, 84, 107, 103, 109, 95, 94, 98, 89, 86, 70, 41, 32, 27, 24, 23, -1, -2, 26, -3, -4, 31, 30, -5, -6, -7, 37, 38, 36, -8, -9, -10, 40, -11, -12, 55, 48, 46, 47, -13, -14, -15, 52, 51, -16, -17, 54, -18, -19, 63, 60, 59, -20, -21, 62, -22, -23, 67, 66, -24, -25, 69, -26, -27, 199, 132, 80, 77, 76, -28, -29, 79, -30, -31, 87, 85, -32, -33, -34, -35, -36, 197, -37, 91, -38, 134, -39, -40, -41, 97, -42, -43, 133, 106, -44, 117, -45, -46, 139, -47, -48, 110, -49, -50, 114, 113, -51, -52, 116, -53, -54, 135, 138, 136, 129, 125, 124, -55, -56, 130, 128, -57, -58, -59, 183, -60, -61, -62, -63, -64, 148, -65, -66, 153, 149, 145, 144, -67, -68, 147, -69, -70, -71, 152, 154, -72, -73, -74, 157, 171, -75, -76, 207, 184, 174, 167, 166, 165, -77, -78, -79, 172, 170, -80, -81, -82, 178, -83, 177, 182, -84, -85, 187, 181, -86, -87, -88, -89, 206, 221, -90, 189, -91, 198, 254, 262, 195, 196, -92, -93, -94, -95, -96, 252, 255, 250, -97, 211, 209, -98, -99, 212, -100, 213, -101, -102, -103, 224, -104, 232, 227, 220, 226, -105, -106, 246, 236, -107, 243, -108, -109, 231, 237, 235, -110, -111, 239, 238, -112, -113, -114, -115, -116, 241, -117, 244, -118, -119, 248, -120, 249, -121, -122, -123, 253, -124, -125, -126, -127, 259, 258, -128, -129, 261, -130, -131, 390, 327, 296, 281, 274, 271, 270, -132, -133, 273, -134, -135, 278, 277, -136, -137, 280, -138, -139, 289, 286, 285, -140, -141, 288, -142, -143, 293, 292, -144, -145, 295, -146, -147, 312, 305, 302, 301, -148, -149, 304, -150, -151, 309, 308, -152, -153, 311, -154, -155, 320, 317, 316, -156, -157, 319, -158, -159, 324, 323, -160, -161, 326, -162, -163, 359, 344, 337, 334, 333, -164, -165, 336, -166, -167, 341, 340, -168, -169, 343, -170, -171, 352, 349, 348, -172, -173, 351, -174, -175, 356, 355, -176, -177, 358, -178, -179, 375, 368, 365, 364, -180, -181, 367, -182, -183, 372, 371, -184, -185, 374, -186, -187, 383, 380, 379, -188, -189, 382, -190, -191, 387, 386, -192, -193, 389, -194, -195, 454, 423, 408, 401, 398, 397, -196, -197, 400, -198, -199, 405, 404, -200, -201, 407, -202, -203, 416, 413, 412, -204, -205, 415, -206, -207, 420, 419, -208, -209, 422, -210, -211, 439, 432, 429, 428, -212, -213, 431, -214, -215, 436, 435, -216, -217, 438, -218, -219, 447, 444, 443, -220, -221, 446, -222, -223, 451, 450, -224, -225, 453, -226, -227, 486, 471, 464, 461, 460, -228, -229, 463, -230, -231, 468, 467, -232, -233, 470, -234, -235, 479, 476, 475, -236, -237, 478, -238, -239, 483, 482, -240, -241, 485, -242, -243, 499, 495, 492, 491, -244, -245, 494, -246, -247, 497, -248, 502, -249, 506, 503, -250, -251, 505, -252, -253, 508, -254, 510, -255, -256, 0 }; - - /** - * Constructs a new {@code StringUtils.java} {@code Object}. - */ - public StringUtils() { - /** - * empty. - */ - } - - /** - * Method used to get the formatted number as a string from the integer - * inputed. - * @param amount the ammount. - * @return the string value. - */ - public static String getFormattedNumber(int amount) { - return new DecimalFormat("#,###,##0").format(amount).toString(); - } - - /** - * Checks if the string contains an invalid character. - * @param name The string. - * @return {@code True} if so. - */ - public static boolean containsInvalidCharacter(String name) { - boolean pass = false; - for (char c : name.toCharArray()) { - for (char vc : VALID_CHARS) { - if (vc == c) { - pass = true; - break; - } - } - if (!pass) { - return true; - } - } - return false; - } - - /** - * If a word starts with a e i o u h for grammar = a + n. - * @param word The word. - * @return If the a should have +n {@code true}. - */ - public static boolean isPlusN(String word) { - if (word == null) - return false; - String s = word.toLowerCase(); - return s.charAt(0) == 'a' || s.charAt(0) == 'e' || s.charAt(0) == 'i' || s.charAt(0) == 'o' || s.charAt(0) == 'u' || (s.charAt(0) == 'h' && s.length() > 1 && s.charAt(1) != 'e'); - } - - /** - * Adds an s to make a world plural. - * @param word the word - * @return the plural word - */ - public static String plusS(String word) { - if (word == null) { - return null; - } - if (word.endsWith("s")) { - return word; - } - if (word.endsWith("y")) { - return word.substring(0, word.length() - 1) + "ies"; - } - return word + "s"; - } - /** - * Method used to get the player name as a long. - * @param s the string. - * @return the long. - */ - public static long getPlayerNameLong(String s) { - long l = 0L; - for (int i = 0; i < s.length() && i < 12; i++) { - char c = s.charAt(i); - l *= 37L; - if (c >= 'A' && c <= 'Z') - l += (1 + c) - 65; - else if (c >= 'a' && c <= 'z') - l += (1 + c) - 97; - else if (c >= '0' && c <= '9') - l += (27 + c) - 48; - } - while (l % 37L == 0L && l != 0L) - l /= 37L; - return l; - } - - /** - * Method used to convert the string to a long. - * @param s the string. - * @return the long. - */ - public static long convertStringToLong(String s) { - if (s.length() > 20) { - throw new IllegalArgumentException("String is too long: " + s); - } - long out = 0L; - for (int i = 0; i < s.length(); ++i) { - long m = reducedMapping(s.codePointAt(i)); - if (m == -1) { - throw new IllegalArgumentException("Unmapped Character in String: " + s); - } - m <<= ((9 - i) * 6) + 4; - out |= m; - } - return out; - } - - /** - * Formats the string as display name. - * @param name The string to format. - * @return The formatted name. - */ - public static String formatDisplayName(String name) { - name = name.replaceAll("_", " "); - name = name.toLowerCase(); - StringBuilder newName = new StringBuilder(); - boolean wasSpace = true; - for (int i = 0; i < name.length(); i++) { - if (wasSpace) { - newName.append((new String() + name.charAt(i)).toUpperCase()); - wasSpace = false; - } else { - newName.append(name.charAt(i)); - } - if (name.charAt(i) == ' ') { - wasSpace = true; - } - } - return newName.toString(); - } - - /** - * Gets the byte for the character. - * @param c The character. - * @return The byte. - */ - private static final byte getByte(char c) { - byte charByte; - if (c > 0 && c < '\200' || c >= '\240' && c <= '\377') - charByte = (byte) c; - else if (c != '\u20AC') { - if (c != '\u201A') { - if (c != '\u0192') { - if (c == '\u201E') - charByte = -124; - else if (c != '\u2026') { - if (c != '\u2020') { - if (c == '\u2021') - charByte = -121; - else if (c == '\u02C6') - charByte = -120; - else if (c == '\u2030') - charByte = -119; - else if (c == '\u0160') - charByte = -118; - else if (c == '\u2039') - charByte = -117; - else if (c == '\u0152') - charByte = -116; - else if (c != '\u017D') { - if (c == '\u2018') - charByte = -111; - else if (c != '\u2019') { - if (c != '\u201C') { - if (c == '\u201D') - charByte = -108; - else if (c != '\u2022') { - if (c == '\u2013') - charByte = -106; - else if (c == '\u2014') - charByte = -105; - else if (c == '\u02DC') - charByte = -104; - else if (c == '\u2122') - charByte = -103; - else if (c != '\u0161') { - if (c == '\u203A') - charByte = -101; - else if (c != '\u0153') { - if (c == '\u017E') - charByte = -98; - else if (c != '\u0178') - charByte = 63; - else - charByte = -97; - } else - charByte = -100; - } else - charByte = -102; - } else - charByte = -107; - } else - charByte = -109; - } else - charByte = -110; - } else - charByte = -114; - } else - charByte = -122; - } else - charByte = -123; - } else - charByte = -125; - } else - charByte = -126; - } else - charByte = -128; - return charByte; - } - - /** - * Gets the double value of this string - * @param s The string. - * @return The double value. - */ - public static double getDouble(String s) { - s = s.replaceAll(", ", "").replaceAll(",", ""); - StringBuilder sb = new StringBuilder(); - char c; - boolean foundStart = false; - for (int i = 0; i < s.length(); i++) { - c = s.charAt(i); - if (Character.isDigit(c) || c == '-' || c == '.') { - sb.append(c); - foundStart = true; - } else if (foundStart) - break; - } - try { - double amount = Double.parseDouble(sb.toString()); - return amount; - } catch (NumberFormatException e) { - return 0.0; - } - } - - public static String[] splitIntoLine(String input, int maxCharInLine) { - - StringTokenizer tok = new StringTokenizer(input, " "); - StringBuilder output = new StringBuilder(input.length()); - int lineLen = 0; - while (tok.hasMoreTokens()) { - String word = tok.nextToken(); - - while (word.length() > maxCharInLine) { - output.append(word.substring(0, maxCharInLine - lineLen) + "\n"); - word = word.substring(maxCharInLine - lineLen); - lineLen = 0; - } - - if (lineLen + word.length() > maxCharInLine) { - output.append("\n"); - lineLen = 0; - } - output.append(word + " "); - - lineLen += word.length() + 1; - } - // output.split(); - // return output.toString(); - return output.toString().split("\n"); - } - - /** - * Gets the hash for the string. - * @param str The string. - * @return The hash. - */ - public static final int getNameHash(String str) { - str = str.toLowerCase(); - int hash = 0; - for (int index = 0; index < str.length(); index++) - hash = getByte(str.charAt(index)) + ((hash << 5) - hash); - return hash; - } - - /** - * Gets the string value of this string (all html/... removed). - * @param s The string. - * @return The string value. - */ - public static String getString(String s) { - String string = s.replaceAll("\\<.*?>", "").replaceAll(" ", "").replaceAll("Discontinued Item:", ""); - return string; - } - - /** - * Characters used to convert a String to a Long. - */ - public static char[] validChars = { '_', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - public static int[] anIntArray233 = { 0, 1024, 2048, 3072, 4096, 5120, 6144, 8192, 9216, 12288, 10240, 11264, 16384, 18432, 17408, 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720, 31744, 32768, 33792, 34816, 35840, 36864, 536870912, 16777216, 37888, 65536, 38912, 131072, 196608, 33554432, 524288, 1048576, 1572864, 262144, 67108864, 4194304, 134217728, 327680, 8388608, 2097152, 12582912, 13631488, 14680064, 15728640, 100663296, 101187584, 101711872, 101974016, 102760448, 102236160, 40960, 393216, 229376, 117440512, 104857600, 109051904, 201326592, 205520896, 209715200, 213909504, 106954752, 218103808, 226492416, 234881024, 222298112, 224395264, 268435456, 272629760, 276824064, 285212672, 289406976, 223346688, 293601280, 301989888, 318767104, 297795584, 298844160, 310378496, 102498304, 335544320, 299892736, 300941312, 301006848, 300974080, 39936, 301465600, 49152, 1073741824, 369098752, 402653184, 1342177280, 1610612736, 469762048, 1476395008, -2147483648, -1879048192, 352321536, 1543503872, -2013265920, -1610612736, -1342177280, -1073741824, -1543503872, 356515840, -1476395008, -805306368, -536870912, -268435456, 1577058304, -134217728, 360710144, -67108864, 364904448, 51200, 57344, 52224, 301203456, 53248, 54272, 55296, 56320, 301072384, 301073408, 301074432, 301075456, 301076480, 301077504, 301078528, 301079552, 301080576, 301081600, 301082624, 301083648, 301084672, 301085696, 301086720, 301087744, 301088768, 301089792, 301090816, 301091840, 301092864, 301093888, 301094912, 301095936, 301096960, 301097984, 301099008, 301100032, 301101056, 301102080, 301103104, 301104128, 301105152, 301106176, 301107200, 301108224, 301109248, 301110272, 301111296, 301112320, 301113344, 301114368, 301115392, 301116416, 301117440, 301118464, 301119488, 301120512, 301121536, 301122560, 301123584, 301124608, 301125632, 301126656, 301127680, 301128704, 301129728, 301130752, 301131776, 301132800, 301133824, 301134848, 301135872, 301136896, 301137920, 301138944, 301139968, 301140992, 301142016, 301143040, 301144064, 301145088, 301146112, 301147136, 301148160, 301149184, 301150208, 301151232, 301152256, 301153280, 301154304, 301155328, 301156352, 301157376, 301158400, 301159424, 301160448, 301161472, 301162496, 301163520, 301164544, 301165568, 301166592, 301167616, 301168640, 301169664, 301170688, 301171712, 301172736, 301173760, 301174784, 301175808, 301176832, 301177856, 301178880, 301179904, 301180928, 301181952, 301182976, 301184000, 301185024, 301186048, 301187072, 301188096, 301189120, 301190144, 301191168, 301193216, 301195264, 301194240, 301197312, 301198336, 301199360, 301201408, 301202432 }; - public static byte[] aByteArray235 = { 22, 22, 22, 22, 22, 22, 21, 22, 22, 20, 22, 22, 22, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 3, 8, 22, 16, 22, 16, 17, 7, 13, 13, 13, 16, 7, 10, 6, 16, 10, 11, 12, 12, 12, 12, 13, 13, 14, 14, 11, 14, 19, 15, 17, 8, 11, 9, 10, 10, 10, 10, 11, 10, 9, 7, 12, 11, 10, 10, 9, 10, 10, 12, 10, 9, 8, 12, 12, 9, 14, 8, 12, 17, 16, 17, 22, 13, 21, 4, 7, 6, 5, 3, 6, 6, 5, 4, 10, 7, 5, 6, 4, 4, 6, 10, 5, 4, 4, 5, 7, 6, 10, 6, 10, 22, 19, 22, 14, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 22, 21, 22, 22, 22, 21, 22, 22 }; - - /** - * Encrypt a string for the client. - */ - public static int encryptPlayerChat(byte[] is, int i_25_, int i_26_, int i_27_, byte[] is_28_) { - try { - i_27_ += i_25_; - int i_29_ = 0; - int i_30_ = i_26_ << 3; - for (; i_27_ > i_25_; i_25_++) { - int i_31_ = 0xff & is_28_[i_25_]; - int i_32_ = anIntArray233[i_31_]; - int i_33_ = aByteArray235[i_31_]; - int i_34_ = i_30_ >> 3; - int i_35_ = i_30_ & 0x7; - i_29_ &= (-i_35_ >> 31); - i_30_ += i_33_; - int i_36_ = ((-1 + (i_35_ - -i_33_)) >> 3) + i_34_; - i_35_ += 24; - is[i_34_] = (byte) (i_29_ = (i_29_ | (i_32_ >>> i_35_))); - if ((i_36_ ^ 0xffffffff) < (i_34_ ^ 0xffffffff)) { - i_34_++; - i_35_ -= 8; - is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); - if (i_36_ > i_34_) { - i_34_++; - i_35_ -= 8; - is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); - if (i_36_ > i_34_) { - i_35_ -= 8; - i_34_++; - is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_); - if (i_34_ < i_36_) { - i_35_ -= 8; - i_34_++; - is[i_34_] = (byte) (i_29_ = i_32_ << -i_35_); - } - } - } - } - } - return -i_26_ + ((7 + i_30_) >> 3); - } catch (RuntimeException runtimeexception) { - } - return 0; - } - - /** - * Gets an integer value from a string. - * @param s the string. - * @return The value; - */ - public static int getValue(String s) { - s = s.replaceAll(", ", "").replaceAll(",", ""); - StringBuilder sb = new StringBuilder(); - char c; - boolean foundStart = false; - for (int i = 0; i < s.length(); i++) { - c = s.charAt(i); - if (Character.isDigit(c) || c == '-') { - sb.append(c); - foundStart = true; - } else if (foundStart) - break; - } - try { - int amount = Integer.parseInt(sb.toString()); - return amount; - } catch (NumberFormatException e) { - return 0; - } - } - - /** - * Checks if the account name is valid. - * @param name The account name. - * @return {@code True} if the account name is invalid. - */ - public static boolean invalidAccountName(String name) { - return name.length() > 12 || name.startsWith("_") || name.endsWith("_") || name.contains("__") || containsInvalidCharacter(name); - } - - /** - * Converts a long to a string. - * @param l The long. - * @return The string. - */ - public static String longToString(long l) { - int i = 0; - char ac[] = new char[32]; - while (l != 0L) { - long l1 = l; - l /= 37L; - ac[11 - i++] = VALID_CHARS[(int) (l1 - l * 37L)]; - } - return new String(ac, 12 - i, i); - } - - /** - * Packs a GJ2-String. - * @param position The position to start. - * @param buffer The byte-array. - * @param str The string. - * @return The size of the string. - */ - public static final int packGJString2(int position, byte[] buffer, String str) { - int length = str.length(); - int offset = position; - for (int index = 0; length > index; index++) { - int character = str.charAt(index); - 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; - } - - /** - * Method used to... - * @param x idk, - * @return the idk. - */ - public static long reducedMapping(int x) { - long out = -1; - if (x >= 97 && x <= 122) - out = x - 96; - else if (x >= 65 && x <= 90) - out = x - 37; - else if (x >= 48 && x <= 57) - out = x - +5; - else if (x == 32) - out = 63L; - return out; - } - - /** - * Converts a string to a long. - * @param s The string. - * @return The long. - */ - public static long stringToLong(String s) { - long l = 0L; - for (int i = 0; i < s.length() && i < 12; i++) { - char c = s.charAt(i); - l *= 37L; - if (c >= 'A' && c <= 'Z') - l += (1 + c) - 65; - else if (c >= 'a' && c <= 'z') - l += (1 + c) - 97; - else if (c >= '0' && c <= '9') - l += (27 + c) - 48; - } - while (l % 37L == 0L && l != 0L) - l /= 37L; - return l; - } - - /** - * Decrypt player chat. - * @param buffer The buffer - * @param totalChars The total chars - * @return The string - */ - public static String decryptPlayerChat(IoBuffer buffer, int totalChars) { - try { - if (totalChars == 0) - return ""; - int charsDecoded = 0; - int i_4_ = 0; - String s = ""; - for (;;) { - byte i_7_ = (byte) buffer.get(); - if (i_7_ >= 0) - i_4_++; - else - i_4_ = anIntArray241[i_4_]; - int i_8_; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - i_4_ = 0; - } - if (((i_7_ & 0x40) ^ 0xffffffff) != -1) - i_4_ = anIntArray241[i_4_]; - else - i_4_++; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (++charsDecoded >= totalChars) - break; - i_4_ = 0; - } - if ((0x20 & i_7_) == 0) - i_4_++; - else - i_4_ = anIntArray241[i_4_]; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - i_4_ = 0; - } - if (((0x10 & i_7_) ^ 0xffffffff) == -1) - i_4_++; - else - i_4_ = anIntArray241[i_4_]; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - - i_4_ = 0; - } - if (((0x8 & i_7_) ^ 0xffffffff) != -1) - i_4_ = anIntArray241[i_4_]; - else - i_4_++; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (++charsDecoded >= totalChars) - break; - i_4_ = 0; - } - if ((0x4 & i_7_) == 0) - i_4_++; - else - i_4_ = anIntArray241[i_4_]; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - i_4_ = 0; - } - if (((i_7_ & 0x2) ^ 0xffffffff) != -1) - i_4_ = anIntArray241[i_4_]; - else - i_4_++; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (totalChars <= ++charsDecoded) - break; - i_4_ = 0; - } - if (((i_7_ & 0x1) ^ 0xffffffff) != -1) - i_4_ = anIntArray241[i_4_]; - else - i_4_++; - if ((i_8_ = anIntArray241[i_4_]) < 0) { - s += (char) (byte) (i_8_ ^ 0xffffffff); - if (++charsDecoded >= totalChars) - break; - i_4_ = 0; - } - } - return s; - } catch (RuntimeException runtimeexception) { - runtimeexception.printStackTrace(); - } - return ""; - } -} diff --git a/Server/src/main/java/core/tools/mysql/DatabaseManager.java b/Server/src/main/java/core/tools/mysql/DatabaseManager.java deleted file mode 100644 index d21ab3c6a..000000000 --- a/Server/src/main/java/core/tools/mysql/DatabaseManager.java +++ /dev/null @@ -1,96 +0,0 @@ -package core.tools.mysql; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashMap; -import java.util.Map; - -import rs09.ServerConstants; -import rs09.game.system.SystemLogger; - -public class DatabaseManager { - - private Map connections = new HashMap<>(); - private Map databases; - - private Database db; - private boolean connected; - - public DatabaseManager(Database db) { - this.db = db; - } - - public DatabaseManager connect() { - try { - Connection connection = DriverManager.getConnection("jdbc:mysql://" + db.host() + "/" + db.name() + "?useTimezone=true&serverTimezone=UTC", db.username(), db.password()); - connections.put(db.name(), connection); - - SystemLogger.logInfo("Successfully connected with '" + db.name() + "'."); - - this.connected = true; - - - } catch (SQLException e) { - SystemLogger.logErr("Couldn't connect to the database."); - e.printStackTrace(); - ServerConstants.MYSQL = false; - } - return this; - } - - public ResultSet query(String name, String query) { - - try { - - Connection connection = connections().get(name); - - if (connection == null) - return null; - - Statement statement = connection.createStatement(); - - return statement.executeQuery(query); - - } catch (SQLException e) { - e.printStackTrace(); - } - - return null; - } - - public int update(String name, String query) { - - try { - - Connection connection = connections().get(name); - - if (connection == null) - return -1; - - Statement statement = connection.createStatement(); - - return statement.executeUpdate(query); - - } catch (SQLException e) { - e.printStackTrace(); - } - - return -1; - } - - public Map connections() { - return connections; - } - - public Map databases() { - return databases; - } - - public boolean isConnected() { - return connected; - } - -} diff --git a/Server/src/main/java/core/worker/MajorUpdateWorker.java b/Server/src/main/java/core/worker/MajorUpdateWorker.java deleted file mode 100644 index 28c719f08..000000000 --- a/Server/src/main/java/core/worker/MajorUpdateWorker.java +++ /dev/null @@ -1,133 +0,0 @@ -/* -package core.worker; - -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -import core.game.system.SystemManager; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; -import core.game.world.update.UpdateSequence; -import core.gui.tab.StatisticsTab; -import core.plugin.CorePluginTypes.Managers; - -*/ -/** - * The major update worker, this handles the game updating etc. - * @author Emperor - *//* - -public final class MajorUpdateWorker implements Runnable { - - */ -/** - * The updating sequence to use. - *//* - - private final UpdateSequence sequence = new UpdateSequence(); - - */ -/** - * The executor. - *//* - - private final Executor EXECUTOR = Executors.newSingleThreadExecutor(); - - */ -/** - * The start time of a cycle. - *//* - - private long start; - - */ -/** - * If the major update worker has started. - *//* - - private boolean started; - - */ -/** - * Constructs a new {@code MajorUpdateWorker} {@code Object}. - *//* - - public MajorUpdateWorker() { - */ -/* - * Empty. - *//* - - } - - */ -/** - * Starts the update worker. - *//* - - public void start() { - if (started) { - return; - } - started = true; - EXECUTOR.execute(MajorUpdateWorker.this); - } - - @Override - public void run() { - while (SystemManager.isActive()) { - try { - start = System.currentTimeMillis(); - sequence.start(); - sequence.run(); - sequence.end(); - //GameWorld.threadPool. - GameWorld.pulse(); - Repository.getDisconnectionQueue().update(); - Managers.tick(); - sleep(); - } catch (Throwable t) { - t.printStackTrace(); - } - } - started = false; - } - - */ -/** - * Lets the current thread sleep. - * @throws InterruptedException When the thread is interrupted. - *//* - - private void sleep() throws InterruptedException { - // How many ticks off we are - final int TICK_SPEED = (int) (600 / 1); //Allows you to make server ticks faster than normal - StatisticsTab.reportPerformance((int) (System.currentTimeMillis() - start)); - long duration = TICK_SPEED - ((System.currentTimeMillis() - start) % TICK_SPEED); - if (duration > 0) { - Thread.sleep(duration); - } else { - SystemLogger.logErr("Updating cycle duration took " + -duration + "ms too long!"); - } - } - - */ -/** - * Gets the started. - * @return The started. - *//* - - public boolean isStarted() { - return started; - } - - */ -/** - * Sets the started. - * @param started The started to set. - *//* - - public void setStarted(boolean started) { - this.started = started; - } -}*/ diff --git a/Server/src/main/java/plugin/drops/DropPlugin.java b/Server/src/main/java/plugin/drops/DropPlugin.java deleted file mode 100644 index 0ec6df372..000000000 --- a/Server/src/main/java/plugin/drops/DropPlugin.java +++ /dev/null @@ -1,26 +0,0 @@ -package plugin.drops; - -import core.game.node.item.Item; -import core.plugin.Plugin; - -import java.util.List; - -public abstract class DropPlugin implements Plugin { - - //the list of NPCs this plugin can effect, if left blank it effects all NPCs. - public int[] accepted_npcs; - - //allows a super constructor to define the npcs - public DropPlugin(int... accepted_npcs){ - this.accepted_npcs = accepted_npcs; - } - - //necessary for the plugin framework, doesnt need to be changed ever. - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - //defines an abstract method all drop plugins must implement - public abstract List handle(); -} diff --git a/Server/src/main/java/plugin/drops/DropPlugins.java b/Server/src/main/java/plugin/drops/DropPlugins.java deleted file mode 100644 index 8eeddb0ce..000000000 --- a/Server/src/main/java/plugin/drops/DropPlugins.java +++ /dev/null @@ -1,42 +0,0 @@ -package plugin.drops; - -import core.game.node.item.Item; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -public class DropPlugins { - public static HashMap> plugins = new HashMap<>(); - public static List globalPlugins = new ArrayList<>(20); - public static List getDrops(int npc_id){ - List drops = new ArrayList<>(20); - List toHandle = plugins.get(npc_id); - if(toHandle != null) { - for (DropPlugin plugin : toHandle) { - drops.addAll(plugin.handle()); - } - } - if(!globalPlugins.isEmpty()) { - for (DropPlugin plugin : globalPlugins) { - drops.addAll(plugin.handle()); - } - } - return drops; - } - - public static void register(DropPlugin plugin){ - if(plugin.accepted_npcs.length > 0) { - Arrays.stream(plugin.accepted_npcs).forEach(id -> { - if (plugins.get(id) == null) { - List newlist = new ArrayList<>(20); - plugins.putIfAbsent(id, newlist); - } - plugins.get(id).add(plugin); - }); - } else { - globalPlugins.add(plugin); - } - } -} diff --git a/Server/src/main/java/plugin/drops/mystery_box/MysteryBoxDropper.java b/Server/src/main/java/plugin/drops/mystery_box/MysteryBoxDropper.java deleted file mode 100644 index a5e57000b..000000000 --- a/Server/src/main/java/plugin/drops/mystery_box/MysteryBoxDropper.java +++ /dev/null @@ -1,45 +0,0 @@ -package plugin.drops.mystery_box; - -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.PluginManifest; -import org.rs09.consts.Items; -import core.tools.RandomFunction; -import plugin.drops.DropPlugin; -import plugin.drops.DropPlugins; - -import java.util.ArrayList; -import java.util.List; - -@Initializable -@PluginManifest(name="MysteryBox") -public class MysteryBoxDropper extends DropPlugin { - - //percentage chance of it dropping, between 1 and 100. - private int chance = 15; - List drops = new ArrayList<>(20); //the list of dropped items - - //standard way to initialize a drop plugin - @Override - public Plugin newInstance(Object arg) throws Throwable { - DropPlugins.register(this); - return this; - } - - //default constructor used, called when the plugin manager initializes this plugin. - //super() for a drop plugin simply means all NPCs can use this drop - public MysteryBoxDropper(){ - super(); - } - - @Override - public List handle() { - boolean willDrop = RandomFunction.random(chance) == RandomFunction.random(chance); - drops.clear(); - if(willDrop){ - drops.add(new Item(Items.MYSTERY_BOX_6199)); - } - return drops; - } -} diff --git a/Server/src/main/java/plugin/package-info.java b/Server/src/main/java/plugin/package-info.java deleted file mode 100644 index 546341052..000000000 --- a/Server/src/main/java/plugin/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Holds all the plugin classes. - * - * @author Emperor - */ -package plugin; \ No newline at end of file diff --git a/Server/src/main/java/rs09/game/ai/AIPControlDialogue.java b/Server/src/main/java/rs09/game/ai/AIPControlDialogue.java deleted file mode 100644 index 32c69e852..000000000 --- a/Server/src/main/java/rs09/game/ai/AIPControlDialogue.java +++ /dev/null @@ -1,107 +0,0 @@ -package rs09.game.ai; - -import core.game.content.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.Rights; - -/** - * Handles artificial intelligent player controls. - * @author Emperor - */ -public final class AIPControlDialogue extends DialoguePlugin { - - /** - * The AIP to control. - */ - private AIPlayer aip; - - /** - * Constructs a new {@code AIPControlDialogue} {@code Object}. - */ - public AIPControlDialogue() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code AIPControlDialogue} {@code Object}. - * @param player The player. - */ - public AIPControlDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AIPControlDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (player.getDetails().getRights() != Rights.ADMINISTRATOR) { - return false; - } - aip = (AIPlayer) args[0]; - String select = "Select"; - if (player.getAttribute("aip_select") == aip) { - select = "Deselect"; - } - interpreter.sendOptions("AIP#" + aip.getUid() + " controls", select, "Settings", "Follow", "Stand-by", "Clear"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (interfaceId) { - case 234: - switch (buttonId) { - case 1: - if (player.getAttribute("aip_select") == aip) { - player.removeAttribute("aip_select"); - break; - } - player.setAttribute("aip_select", aip); - break; - case 2: - interpreter.sendOptions("AIP#" + aip.getUid() + " settings", "Toggle run", "Toggle retaliate", "Toggle special attack"); - return true; - case 3: - aip.follow(player); - player.removeAttribute("aip_select"); - break; - case 4: - aip.getPulseManager().clear(); - player.removeAttribute("aip_select"); - break; - case 5: - AIPlayer.deregister(aip.getUid()); - player.removeAttribute("aip_select"); - break; - } - close(); - return true; - case 230: - switch (buttonId) { - case 1: - aip.getSettings().toggleRun(); - break; - case 2: - aip.getSettings().toggleRetaliating(); - break; - case 3: - aip.getSettings().toggleSpecialBar(); - break; - } - close(); - return true; - } - return false; - } - - @Override - public int[] getIds() { - return new int[0]; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/rs09/game/ai/general/scriptrepository/Script.java b/Server/src/main/java/rs09/game/ai/general/scriptrepository/Script.java deleted file mode 100644 index 4342dfa4b..000000000 --- a/Server/src/main/java/rs09/game/ai/general/scriptrepository/Script.java +++ /dev/null @@ -1,52 +0,0 @@ -package rs09.game.ai.general.scriptrepository; - -import core.game.node.entity.player.Player; -import rs09.game.ai.general.ScriptAPI; -import core.game.node.item.Item; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -public abstract class Script { - - public ScriptAPI scriptAPI; - public ArrayList inventory = new ArrayList<>(20); - public ArrayList equipment = new ArrayList<>(20); - public Map skills = new HashMap<>(); - - - public Player bot; - - public boolean running = true; - - public void init(boolean isPlayer) - { - //bot.init(); - scriptAPI = new ScriptAPI(bot); - - if(!isPlayer) { - for (Item i : equipment) { - bot.getEquipment().add(i, true, false); - } - bot.getInventory().clear(); - for (Item i : inventory) { - bot.getInventory().add(i); - } - for (Map.Entry skill : skills.entrySet()) { - setLevel(skill.getKey(), skill.getValue()); - } - } - } - - public abstract void tick(); - - public void setLevel(int skill, int level) { - bot.getSkills().setLevel(skill, level); - bot.getSkills().setStaticLevel(skill, level); - bot.getSkills().updateCombatLevel(); - bot.getAppearance().sync(); - } - - public abstract Script newInstance(); -} \ No newline at end of file diff --git a/Server/src/main/java/rs09/game/ai/lumbridge/DeadIdler.java b/Server/src/main/java/rs09/game/ai/lumbridge/DeadIdler.java deleted file mode 100644 index 8d80279ee..000000000 --- a/Server/src/main/java/rs09/game/ai/lumbridge/DeadIdler.java +++ /dev/null @@ -1,34 +0,0 @@ -package rs09.game.ai.lumbridge; - -import rs09.game.ai.AIPlayer; -import core.game.world.map.Location; -import core.game.world.map.zone.ZoneBorders; -import core.tools.RandomFunction; - -public class DeadIdler extends AIPlayer { - //Recreation of players I saw in w417 who seemed to have quit their computer after dying. - - private int tick = RandomFunction.random(500); - - public DeadIdler() - { - super(getRandomRespawnLoc()); - this.setCustomState("Lumbridge Bot"); - } - - @Override - public void tick() - { - super.tick(); - if (this.tick > 0) - { - tick --; - } else { - AIPlayer.deregister(this.getUid()); - } - } - - private static Location getRandomRespawnLoc() { - return new ZoneBorders(3219, 3218, 3223, 3219).getRandomLoc(); - } -} diff --git a/Server/src/main/java/rs09/game/ai/lumbridge/LumbridgeBotHandler.java b/Server/src/main/java/rs09/game/ai/lumbridge/LumbridgeBotHandler.java deleted file mode 100644 index f49a543d4..000000000 --- a/Server/src/main/java/rs09/game/ai/lumbridge/LumbridgeBotHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -package rs09.game.ai.lumbridge; - -import rs09.game.world.GameWorld; -import core.tools.RandomFunction; - -import java.util.concurrent.Executors; - -/** - * Creates a few random bots around Lumbridge area. - * Code by Red Bracket - */ -public class LumbridgeBotHandler { - public static void immersiveLumbridge() - { - //Generate a few random bots here and there - System.out.println("[" + GameWorld.getSettings().getName() + "]: LumbridgeBotHandler: Initialized dead idlers."); - generateDeadIdlers(); - } - - private static void generateDeadIdlers() { - Executors.newSingleThreadExecutor().execute(() -> { - while (true) //Would probably be better if this could be "while game is running" - { - new DeadIdler(); - try { - Thread.sleep(RandomFunction.random(300_000)); - } catch (InterruptedException e) { - System.out.println("LumbridgeBotHandler can't sleep!!?"); - } - } - }); - } -} diff --git a/Server/src/main/java/rs09/game/ai/pvmbots/DragonKiller.java b/Server/src/main/java/rs09/game/ai/pvmbots/DragonKiller.java deleted file mode 100644 index ef4ee0478..000000000 --- a/Server/src/main/java/rs09/game/ai/pvmbots/DragonKiller.java +++ /dev/null @@ -1,66 +0,0 @@ -package rs09.game.ai.pvmbots; - -import core.game.node.entity.skill.Skills; -import rs09.game.ai.AIPlayer; -import core.game.node.entity.player.link.prayer.*; -import core.game.world.map.Location; -import core.game.world.map.zone.impl.WildernessZone; - -public class DragonKiller extends PvMBots{ - - private int tick = 0; - - public DragonKiller(Location l) { - super(l); - // TODO Auto-generated constructor stub - } - - @Override - public void tick() - { - super.tick(); - - //Despawn - if (this.getSkills().getLifepoints() == 0 || this.getLocation().equals(new Location(0, 0))) - //this.teleport(new Location(500, 500)); - //Despawning not being delayed causes 3 errors in the console - AIPlayer.deregister(this.getUid()); - - if (this.checkVictimIsPlayer()) - { - tryEscape(); - } - - //Npc Combat - if (tick == 0) - { - if (!this.inCombat()) - AttackNpcsInRadius(this, 15); - this.tick = 10; - } - else - this.tick--; - - this.eat(379); - this.getSkills().setLevel(Skills.PRAYER, 99); - this.getSkills().setStaticLevel(Skills.PRAYER, 99); - if (!(this.getPrayer().getActive().contains(PrayerType.PROTECT_FROM_MELEE))) - this.getPrayer().toggle(PrayerType.PROTECT_FROM_MELEE); - //this.getPrayer().toggle() - } - - public void tryEscape() - { - if (this.isTeleBlocked() == false) - { - if (WildernessZone.getWilderness(this) <= 20) - { - System.out.println("not Teleblocked"); - this.teleport(new Location(0, 0)); - } - } - else - return; - //run away - } -} diff --git a/Server/src/main/java/rs09/game/ai/pvmbots/GiantMoleBot.java b/Server/src/main/java/rs09/game/ai/pvmbots/GiantMoleBot.java deleted file mode 100644 index 16d6865e8..000000000 --- a/Server/src/main/java/rs09/game/ai/pvmbots/GiantMoleBot.java +++ /dev/null @@ -1,68 +0,0 @@ -package rs09.game.ai.pvmbots; - -import rs09.game.ai.AIPlayer; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.item.Item; -import core.game.world.map.Location; - -public class GiantMoleBot extends PvMBots{ - - public GiantMoleBot(Location l) { - super(l); - // TODO Auto-generated constructor stub - } - - int tick = 0; - int movetimer = 5; - - private void checkLightSource() - { - if (!this.getInventory().containItems(33)) - { - if (this.getInventory().containItems(36)) - this.getInventory().remove(new Item(36)); - this.getInventory().add(new Item(33)); - } - } - - @Override - public void tick() - { - checkLightSource(); - super.tick(); - PrayerType type[] = {PrayerType.PROTECT_FROM_MELEE, PrayerType.PIETY}; - this.CheckPrayer(type); - - //Despawn - if (this.getSkills().getLifepoints() == 0) - //this.teleport(new Location(500, 500)); - //Despawning not being delayed causes 3 errors in the console - AIPlayer.deregister(this.getUid()); - - //Npc Combat - if (this.tick == 0) - { - if (!this.inCombat()) - AttackNpcsInRadius(this, 50); - this.tick = 5; - } - - else - this.tick--; - - /*if (this.movetimer == 0) - { - if (!this.inCombat()) - { - this.randomWalk(10, 10); - } - this.movetimer = 4; - } - else - this.movetimer--;*/ - //if (this.getSkills().getLifepoints() > 1) - //this.eat(); - //this.getPrayer().toggle() - } - -} diff --git a/Server/src/main/java/rs09/game/ai/pvmbots/LowestBot.java b/Server/src/main/java/rs09/game/ai/pvmbots/LowestBot.java deleted file mode 100644 index 3a3f3c342..000000000 --- a/Server/src/main/java/rs09/game/ai/pvmbots/LowestBot.java +++ /dev/null @@ -1,38 +0,0 @@ -package rs09.game.ai.pvmbots; - -import rs09.game.ai.AIPlayer; -import core.game.world.map.Location; - -public class LowestBot extends PvMBots{ - - public LowestBot(Location l) { - super(l); - } - - private int tick = 0; - - @Override - public void tick(){ - super.tick(); - - this.tick++; - - //Despawn - if (this.getSkills().getLifepoints() == 0) - //this.teleport(new Location(500, 500)); - //Despawning not being delayed causes 3 errors in the console - AIPlayer.deregister(this.getUid()); - - //Npc Combat - if (this.tick % 10 == 0) { - if (!this.inCombat()) - AttackNpcsInRadius(this, 5); - } - - if (this.tick == 100) this.tick = 0; - - this.eat(329); - //this.getPrayer().toggle() - } - -} diff --git a/Server/src/main/java/rs09/game/ai/pvmbots/NoobBot.java b/Server/src/main/java/rs09/game/ai/pvmbots/NoobBot.java deleted file mode 100644 index 556e57eff..000000000 --- a/Server/src/main/java/rs09/game/ai/pvmbots/NoobBot.java +++ /dev/null @@ -1,54 +0,0 @@ -package rs09.game.ai.pvmbots; - -import rs09.game.ai.AIPlayer; -import core.game.world.map.Location; - -public class NoobBot extends PvMBots{ - - private int tick = 0; - private int movetimer = 0; - - public NoobBot(Location l) { - super(l); - // TODO Auto-generated constructor stub - } - - @Override - public void tick() - { - super.tick(); - - //Despawn - if (this.getSkills().getLifepoints() == 0) - //this.teleport(new Location(500, 500)); - //Despawning not being delayed causes 3 errors in the console - AIPlayer.deregister(this.getUid()); - - //Npc Combat - if (tick == 0) - { - if (!this.inCombat()) - AttackNpcsInRadius(this, 5); - this.tick = 5; - } - else - this.tick--; - - this.eat(329); - //this.getPrayer().toggle() - //System.out.println(this.getPulseManager().getCurrent()); - - if (!this.inCombat()) - { - if (movetimer == 0) - { - if (this.FindTargets(this, 5) == null) - this.randomWalk(5, 5); - this.movetimer = 10; - } - else - movetimer --; - } - } - -} diff --git a/Server/src/main/java/rs09/game/ai/pvp/PVPAIPActions.java b/Server/src/main/java/rs09/game/ai/pvp/PVPAIPActions.java deleted file mode 100644 index 25b6f6830..000000000 --- a/Server/src/main/java/rs09/game/ai/pvp/PVPAIPActions.java +++ /dev/null @@ -1,196 +0,0 @@ -package rs09.game.ai.pvp; - -import java.util.List; - -import core.game.container.impl.EquipmentContainer; -import core.game.content.consumable.Consumable; -import core.game.content.consumable.Consumables; -import core.game.content.consumable.Food; -import core.game.content.consumable.effects.HealingEffect; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import rs09.game.ai.AIPlayer; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import core.game.world.map.path.Pathfinder; -import core.game.world.update.flag.context.ChatMessage; -import core.game.world.update.flag.player.ChatFlag; -import core.tools.RandomFunction; - -public class PVPAIPActions { - - public static List pvp_players = null; - - private static final String[] trashMessages = { - "I'll do your bloody nan in m8", - "Your sister smells like a tuna sandwich", - "You suck at bridding bro lmao", - "Gf your bank :)", - "Just delete your cache ur bad", - "FFS mate get wrekt", - "Dylan is a better pkr than u, kid..l0l" - - - }; - - private static final String[] safeMessages = { - "Safe up n00b", - "No safe", - "Get rekt m8", - "Stop fkin eating scrub", - "Dw, you're gunna die anyhow.. :)", - "Either ur eating shrimps or I'm hitting high asf", - "Once i kill u wanna rematch?", - - }; - - public static void syncBotThread(final Player player) { - if (pvp_players == null || pvp_players.size() == 0) { - return; - } - for (int aip_index = 0; aip_index < pvp_players.size(); aip_index++) { - final AIPlayer bot = pvp_players.get(aip_index); - final AIPlayer[] target = { - pvp_players.get(RandomFunction.getRandom(pvp_players.size() - 1)) - }; - bot.getProperties().setRetaliating(true); - bot.setAttribute("dead", false); - GameWorld.getPulser().submit(new Pulse(1, bot) { - int ticks; - @Override - public boolean pulse() { - if (bot.getAttribute("dead", true)) { - AIPlayer.deregister(bot.getUid()); - pvp_players.remove(bot); - return true; - } - if (!bot.getInventory().contains(385, 1)) { - flee(bot); - return true; - } - //Inactivity logout timer. - if (ticks++ == 5000 && !bot.getProperties().getCombatPulse().isInCombat() && !bot.getProperties().getCombatPulse().isAttacking() && bot.getWalkingQueue().getQueue().isEmpty()) { - AIPlayer.deregister(bot.getUid()); - pvp_players.remove(bot); - return true; - } - if (!pvp_players.contains(target[0]) || !Pathfinder.find(bot, target[0]).isSuccessful() || !canAttack(bot, target[0])) { - target[0] = pvp_players.get(RandomFunction.getRandom(pvp_players.size() - 1)); - } - attackTarget(player, bot, target[0]); - //prayer(bot, target[0]); - eat(bot, target[0]); - return false; - } - }); - } - } - - - private static void attackTarget(Player player, AIPlayer bot, AIPlayer target) { - //If bots target is not himself we continue. - if (bot != target) { - //If they are in combat or attacking: - if (bot.getProperties().getCombatPulse().isInCombat() || bot.getProperties().getCombatPulse().isAttacking()) { - //TODO: Pray - Item weapon = bot.getEquipment().get(EquipmentContainer.SLOT_WEAPON); - if (RandomFunction.getRandom(25) == 1) { - if(bot.getSettings().getSpecialEnergy() == 100) { //TODO: Change 100 to their weielded weapons spec max amount - bot.getSettings().toggleSpecialBar(); - } - } - } else { - if (RandomFunction.getRandom(25) == 1) { - bot.attack(player); - } else { - //If a 1/50 chance is true, the attacker screams his opponents name hoping his team will dogpile him. - int chatRandom = RandomFunction.getRandom(50); - switch(chatRandom) { - case 1: - String target_name = target.getName().substring(0, 1).toUpperCase() + target.getName().substring(1); - bot.getUpdateMasks().register(new ChatFlag(new ChatMessage(bot, target_name, 0, 0))); - break; - case 2: - bot.getUpdateMasks().register(new ChatFlag(new ChatMessage(bot, trashMessages[RandomFunction.getRandom(trashMessages.length - 1)], 0, 0))); - break; - } - bot.attack(target); - } - } - } - } - - /** - * A basic flee method to run them to another part of the wilderness. - * @param bot - * TODO: Possibly make them run away and hop the ditch (I did not do this because we have no other actions than pking at the moment). - */ - private static void flee(final AIPlayer bot) { - bot.sendChat("Outta food m8! Off!"); - bot.sendChat("Ahh!"); - bot.sendChat("GTFO M8"); - bot.sendChat("Someone spec him!"); - GameWorld.getPulser().submit(new Pulse(1, bot) { - int ticks; - @Override - public boolean pulse() { - switch(ticks++) { - case 1: - bot.getProperties().getCombatPulse().stop(); - bot.getProperties().getCombatPulse().setNextAttack(RandomFunction.getRandom(10)); - Pathfinder.find(bot, bot.getLocation().transform(RandomFunction.random(-100, 100), RandomFunction.random(-100, 100), 0), false, Pathfinder.SMART).walk(bot); - break; - case 500: - AIPlayer.deregister(bot.getUid()); - pvp_players.remove(bot); - return true; - } - if (bot.getAttribute("dead", true)) { - AIPlayer.deregister(bot.getUid()); - pvp_players.remove(bot); - return true; - } - return false; - } - }); - } - - /** - * Handles the eating of the AI player bots. - * @param bot - * TODO: Possibly add more food choices. - */ - private static void eat(Player bot, AIPlayer target) { - Item shark = new Item(385); - //385 is shark, you can add more from there easier. - if((bot.getSkills().getStaticLevel(Skills.HITPOINTS) > bot.getSkills().getLifepoints() * 3) && bot.getInventory().containsItem(shark)) { - bot.lock(3); - Item food = bot.getInventory().getItem(shark); - Consumable consumable = Consumables.getConsumableById(food.getId()); - if (consumable == null) { - consumable = new Food(new int[] {food.getId()}, new HealingEffect(1)); - } - consumable.consume(food, bot); - bot.getProperties().getCombatPulse().delayNextAttack(3); - int chatRandom = RandomFunction.getRandom(50); - if (chatRandom == 1) { - bot.getUpdateMasks().register(new ChatFlag(new ChatMessage(bot, safeMessages[RandomFunction.getRandom(safeMessages.length - 1)], 0, 0))); - } - } - } - - private static boolean canAttack(Player p, Player t) { - int level = p.getSkullManager().getLevel(); - if (t.getSkullManager().getLevel() < level) { - level = t.getSkullManager().getLevel(); - } - int combat = p.getProperties().getCombatLevel(); - int targetCombat = t.getProperties().getCombatLevel(); - if (combat - level > targetCombat || combat + level < targetCombat) { - return false; - } - return true; - } - -} diff --git a/Server/src/main/java/rs09/game/ai/pvp/PVPAIPBuilderUtils.java b/Server/src/main/java/rs09/game/ai/pvp/PVPAIPBuilderUtils.java deleted file mode 100644 index e7a63b14f..000000000 --- a/Server/src/main/java/rs09/game/ai/pvp/PVPAIPBuilderUtils.java +++ /dev/null @@ -1,2795 +0,0 @@ -package rs09.game.ai.pvp; - -import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.combat.CombatSpell; -import rs09.game.ai.AIPlayer; -import core.game.node.entity.player.link.SpellBookManager; -import core.game.node.item.Item; -import core.tools.RandomFunction; - -public class PVPAIPBuilderUtils { - - public static void generateClass(AIPlayer p) { - int combatType = RandomFunction.getRandom(2); - switch(combatType) { - case 0: - buildArcherStats(p); - buildArcherEquipment(p); - break; - case 1: - buildMeleeStats(p); - buildMeleeEquipment(p); - break; - case 2: - //todo: magic - setupWizard(p); - buildWizardStats(p); - buildWizardEquipment(p); - break; - case 3: - //max rune - buildMaxMeleeStats(p); - buildMaxMeleeEquipment(p); - break; - } - p.getInventory().add(new Item(385, 20)); - p.getProperties().setCombatLevel(126); - - p.getSkills().setLevel(Skills.PRAYER, 98); - p.getSkills().setStaticLevel(Skills.PRAYER, 98); - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - correctHitpointsStat(p); - } - - private static void buildWizardEquipment(AIPlayer p) { - int[] weapons = { - 1381, - 4675, - }; - int hat = -1; - int platebody = -1; - int platelegs = -1; - int gloves = -1; - int boots = -1; - int set = RandomFunction.getRandom(2); - switch(set) { - //Blue set - case 0: - hat = 4089; - platebody = 4091; - platelegs = 4093; - gloves = RandomFunction.getRandom(10) == 1 ? generateGloves() : 4095; - boots = RandomFunction.getRandom(10) == 1 ? generateBoots() : 4097; - break; - //Red set - case 1: - hat = 4099; - platebody = 4101; - platelegs = 4103; - gloves = RandomFunction.getRandom(10) == 1 ? generateGloves() : 4105; - boots = RandomFunction.getRandom(10) == 1 ? generateBoots() : 4107; - break; - //White set - case 2: - hat = 4109; - platebody = 4111; - platelegs = 4113; - gloves = RandomFunction.getRandom(10) == 1 ? generateGloves() : 4115; - boots = RandomFunction.getRandom(10) == 1 ? generateBoots() : 4117; - break; - case 3: - hat = 4708; - platebody = 4712; - platelegs = 44714; - gloves = RandomFunction.getRandom(10) == 1 ? generateGloves() : 4095; - boots = RandomFunction.getRandom(10) == 1 ? generateBoots() : 4097; - break; - } - p.getEquipment().replace(new Item(hat), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(platebody), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(platelegs), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(weapons[RandomFunction.random(weapons.length)]), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(generateNecklace()), EquipmentContainer.SLOT_AMULET); - p.getEquipment().replace(new Item(boots), EquipmentContainer.SLOT_FEET); - p.getEquipment().replace(new Item(generateCape()), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(gloves), EquipmentContainer.SLOT_HANDS); - System.out.println("WE ARE WEARING " + p.getEquipment().format() + " FOR " + p.getName()); - } - - private static void setupWizard(AIPlayer p) { - //final int SPELL_IDS[] = new int[] {1, 4, 6, 8, 10, 14, 17, 20, 24, 27, 33, 38, 45, 48, 52, 55 }; - p.getProperties().setAutocastSpell(((CombatSpell) SpellBookManager.SpellBook.MODERN.getSpell(55))); - p.getInventory().add(new Item(554, 1000)); //Fire runes - p.getInventory().add(new Item(555, 1000)); //Water runes - p.getInventory().add(new Item(556, 1000)); //Air runes - p.getInventory().add(new Item(557, 1000)); //Earth runes - p.getInventory().add(new Item(558, 1000)); //Mind runes - p.getInventory().add(new Item(559, 1000)); //Body runes - p.getInventory().add(new Item(560, 1000)); //Death runes - p.getInventory().add(new Item(561, 1000)); //Nature runes - p.getInventory().add(new Item(562, 1000)); //Chaos runes - p.getInventory().add(new Item(563, 1000)); //Law runes - } - - private static void buildWizardStats(AIPlayer p) { - int magicLevel = RandomFunction.random(80, 98); - int defenceLevel = RandomFunction.random(80, 98); - - p.getSkills().setLevel(Skills.MAGIC, magicLevel); - p.getSkills().setStaticLevel(Skills.MAGIC, magicLevel); - p.getSkills().setLevel(Skills.DEFENCE, defenceLevel); - p.getSkills().setStaticLevel(Skills.DEFENCE, defenceLevel); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - correctHitpointsStat(p); - } - - - private static void buildMeleeStats(AIPlayer p) { - int attackLevel = RandomFunction.random(80, 98); - int strengthLevel = RandomFunction.random(attackLevel - 10, attackLevel + 10); - int defenceLevel = RandomFunction.random(60, 98); - - p.getSkills().setLevel(Skills.ATTACK, attackLevel); - p.getSkills().setStaticLevel(Skills.ATTACK, attackLevel); - p.getSkills().setLevel(Skills.STRENGTH, strengthLevel); - p.getSkills().setStaticLevel(Skills.STRENGTH, strengthLevel); - p.getSkills().setLevel(Skills.DEFENCE, defenceLevel); - p.getSkills().setStaticLevel(Skills.DEFENCE, defenceLevel); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - correctHitpointsStat(p); - } - - private static void buildMeleeEquipment(AIPlayer p) { - int[] helms = { - 1163, //Rune Full Helm - 3751, //Berserker Helm - 3753, //Warrior Helm - 10828, - 1038, - 4753, - //TODO: No helm - }; - int[] shield = { - 1185, //Rune sq shield - 1201, //Rune kiteshield - 6524, //Obsidian shield - 8850, //Rune defender - 11283, //dragonfire shield - 14767, //dragon defender - }; - int[] platebody = { - 1113, //Rune chainmail. - 1127, //Rune platebody. - 10551, - 4757, - 11724, - 4720, - }; - int[] platelegs = { - 1079, //Rune platelegs. - 4759, - 11726, - 1075, - }; - p.getEquipment().replace(new Item(helms[RandomFunction.random(helms.length)]), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(shield[RandomFunction.random(shield.length)]), EquipmentContainer.SLOT_SHIELD); - p.getEquipment().replace(new Item(platebody[RandomFunction.random(platebody.length)]), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(platelegs[RandomFunction.random(platelegs.length)]), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(weapons[RandomFunction.random(weapons.length)]), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(generateNecklace()), EquipmentContainer.SLOT_AMULET); - p.getEquipment().replace(new Item(generateBoots()), EquipmentContainer.SLOT_FEET); - p.getEquipment().replace(new Item(generateCape()), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(generateGloves()), EquipmentContainer.SLOT_HANDS); - } - - public static final int[] weapons = { - 4151, //Whip - 4587, //Dragon Scimitar - - }; - - - private static void buildMaxMeleeStats(AIPlayer p) { - - p.getSkills().setLevel(Skills.ATTACK, 99); - p.getSkills().setStaticLevel(Skills.ATTACK, 99); - p.getSkills().setLevel(Skills.STRENGTH, 99); - p.getSkills().setStaticLevel(Skills.STRENGTH, 99); - p.getSkills().setLevel(Skills.DEFENCE, 99); - p.getSkills().setStaticLevel(Skills.DEFENCE, 99); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - correctHitpointsStat(p); - } - - private static void buildMaxMeleeEquipment(AIPlayer p) { - int[] helms = { - 3751, //Berserker Helm - }; - int[] shield = { - 14767, //Dragon defender - }; - int[] platebody = { - 11724, //Bandos chainmail. - }; - int[] platelegs = { - 11726, //Bandos Tassets. - }; - int[] amulets = { - 6585, //fury - }; - int[] boots = { - 3105, //Climbing boots. - }; - int[] cape = { - 6570, //Fire cape. - }; - int[] gloves = { - 7462, //Barrows Gloves. - }; - p.getEquipment().replace(new Item(helms[RandomFunction.random(helms.length)]), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(shield[RandomFunction.random(shield.length)]), EquipmentContainer.SLOT_SHIELD); - p.getEquipment().replace(new Item(platebody[RandomFunction.random(platebody.length)]), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(platelegs[RandomFunction.random(platelegs.length)]), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(weapons2[RandomFunction.random(weapons2.length)]), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(amulets[RandomFunction.random(amulets.length)]), EquipmentContainer.SLOT_AMULET); - p.getEquipment().replace(new Item(boots[RandomFunction.random(boots.length)]), EquipmentContainer.SLOT_FEET); - p.getEquipment().replace(new Item(cape[RandomFunction.random(cape.length)]), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(gloves[RandomFunction.random(gloves.length)]), EquipmentContainer.SLOT_HANDS); - } - - public static final int[] weapons2 = { - 4151, //Whip - }; - - private static void buildArcherStats(AIPlayer p) { - int rangedLevel = RandomFunction.random(80, 98); - int defenceLevel = RandomFunction.random(60, 98); - - p.getSkills().setLevel(Skills.RANGE, rangedLevel); - p.getSkills().setStaticLevel(Skills.RANGE, rangedLevel); - p.getSkills().setLevel(Skills.DEFENCE, defenceLevel); - p.getSkills().setStaticLevel(Skills.DEFENCE, defenceLevel); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - correctHitpointsStat(p); - } - - private static void buildArcherEquipment(AIPlayer p) { - int[] helms = { - 3749, //Archer helm - 2581, - 10828, - }; - int[] weapons = { - 857, //Yew shortbow - 855, //Yew longbow - 861, //Magic shortbow - 859, //Magic longbow - 11235, - }; - int[] arrows = { - 892, //Rune arrow - 11212, - }; - int platebody = -1; - int platelegs = -1; - int gloves = -1; - int set = RandomFunction.getRandom(2); - switch(set) { - //Blue set - case 0: - platebody = 2499; - platelegs = 2493; - gloves = RandomFunction.getRandom(10) == 1 ? generateGloves() : 2487; - break; - //Red set - case 1: - platebody = 2501; - platelegs = 2495; - gloves = RandomFunction.getRandom(10) == 1 ? generateGloves() : 2489; - break; - //Black set - case 2: - platebody = 2503; - platelegs = 2497; - gloves = RandomFunction.getRandom(10) == 1 ? generateGloves() : 2491; - break; - } - p.getEquipment().replace(new Item(helms[RandomFunction.random(helms.length)]), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(platebody), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(platelegs), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(weapons[RandomFunction.random(weapons.length)]), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(generateNecklace()), EquipmentContainer.SLOT_AMULET); - p.getEquipment().replace(new Item(generateBoots()), EquipmentContainer.SLOT_FEET); - p.getEquipment().replace(new Item(generateCape()), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(gloves), EquipmentContainer.SLOT_HANDS); - p.getEquipment().replace(new Item(arrows[RandomFunction.random(arrows.length)], 1000), EquipmentContainer.SLOT_ARROWS); - } - - private static int generateCape() { - int[] capes = { - 1007, //Red Cape - 1023, //Yellow Cape - 1019, //Black cape - 1021, //Blue cape - 1027, //Green cape - 1052, //Legends Cape - 2412, //Saradomin Cape - 2413, //Guthix Cape - 2414, //Zamorak Cape - 6570, - }; - return capes[RandomFunction.random(capes.length)]; - } - - private static int generateNecklace() { - int[] amulets = { - 1712, //Glory - 1725, //Strength ammy - 1727, //Amulet of magic - 1729, //Amulet of defence - 1478, //Amulet of accuracy - 6585, - }; - return amulets[RandomFunction.random(amulets.length)]; - } - - private static int generateGloves() { - int[] gloves = { - 1059, - 7462 - }; - return gloves[RandomFunction.random(gloves.length)]; - } - - private static int generateBoots() { - int[] boots = { - 1061, //Leather boots. - 1837, //Desert boots. - 3105, //Climbing boots. - 3791, //Fremmy boots. - 11732, - }; - return boots[RandomFunction.random(boots.length)]; - } - - private static void correctHitpointsStat(AIPlayer player) { - player.getSkills().setLevel(Skills.HITPOINTS, 10); - player.getSkills().setStaticLevel(Skills.HITPOINTS, 10); - player.getSkills().updateCombatLevel(); - int rangedLevel = RandomFunction.random(80, 98); - int defenceLevel = RandomFunction.random(80, 98); - - player.getSkills().setLevel(Skills.HITPOINTS, rangedLevel); - player.getSkills().setStaticLevel(Skills.HITPOINTS, rangedLevel); - player.getSkills().setLevel(Skills.HITPOINTS, defenceLevel); - player.getSkills().setStaticLevel(Skills.HITPOINTS, defenceLevel); - - player.getSkills().updateCombatLevel(); - player.getAppearance().sync(); - } - - public static final String[] names = { - "karamellioole123", - "sillyninja20", - "darkonecut0", - "mars_noob", - "volvovr0", - "gagton23", - "inkheart", - "sock_flavour", - "sophie72", - "reaper_4me", - "bessiedog", - "brknsprk22", - "whyuhackme", - "rayvano55", - "rayvano98", - "zaborgttm", - "warrior", - "madsbang123", - "christensen", - "theboz87", - "iqoigu2d", - "f4ll3n0n3", - "xbox360123", - "stijnkessel", - "kessel1996", - "genderoni", - "lolimawesome", - "amac109", - "downhill", - "citricabyss", - "ceesis", - "terry1000666", - "bink2468", - "roman1248", - "abcdefghijk", - "superchunklz", - "lizzie9", - "fast_reflexx", - "asherasher", - "rebagon", - "44b19n1v", - "staylow", - "nismo360", - "tornoc109", - "thunder20", - "crosby_8127", - "silhouette", - "moltenore3", - "lavabomb", - "feedu_arrows", - "soccer28", - "mindkampfers", - "notsogood", - "ashbacher7", - "tppcrpg7", - "michaelsyer", - "cue2hug", - "drewno58", - "j_p_13", - "111robinson", - "heyitstrea", - "mccain", - "doc_control", - "fartmaster", - "neonarg", - "b9501175", - "snorlax4", - "jasonchou", - "xgameboy95", - "craig9596", - "ojaste296lku123", - "sheltzor", - "karateboy11", - "makkiestone", - "billymon", - "blackspidder", - "sn1perh34ded", - "jerzeythugz2", - "trueset", - "pure_x_400askaa1234", - "mr_sgytkonyn", - "twoitem", - "1archerhbk", - "assinass", - "tharifica1", - "apgart00", - "fildherbn", - "freeze112", - "kno_way", - "loloklol", - "supermacho1", - "09101991", - "raptor8721", - "justindb", - "jolo5001", - "catfish", - "spiker991", - "qpwoeiruty", - "seasonsdawn", - "seasonsend", - "jartt2", - "jartt1", - "uber3pic", - "doodoo12345", - "rio1927ieman11", - "angered_nubackers", - "user_zim", - "zacharyh1", - "cool_kevin50", - "4194ka", - "freitty", - "gamecube", - "silver_knig0", - "yourmom1", - "beanroid", - "smoke47149", - "flitch289", - "nooder000", - "amazing_cake", - "iridium", - "aliffridzuan", - "ridzuan2000", - "bartez4", - "k4eo9ssi7", - "juxikeederast", - "d_man_5567", - "mod_richard", - "q1w2e1r2", - "be_kingless", - "hardflip", - "yami671", - "fbr114", - "rowelselidio", - "gagoako", - "haggishanger", - "football1", - "z_4_t_1_o_n", - "sxngaming123", - "simbojimbo", - "digeryd00", - "noob_10010", - "dragonoid798", - "qwerty798", - "2016", - "pk4lifefoo", - "logieboi", - "wichking", - "kyros60r0bl33mfzo", - "charlto1", - "fmgh2145mvplesjack", - "hiei179", - "shadowx123", - "seamus10990", - "danward", - "crusher16619", - "doremember", - "master15125", - "ninjas11", - "zarkotics", - "saphira123", - "skyler_is_on", - "lolbobjo", - "daniboom", - "12345dani", - "do_mhamai", - "eoin3646", - "rikuislove23", - "denver23", - "patacorow", - "globalfun", - "killer_ox08", - "scott08", - "persian_bossreston22", - "gold_runmoon", - "123456q", - "nosweet15", - "jefferson123 ", - "applecore17", - "mjc1997", - "sayin68", - "joshua123", - "gray258ancakes", - "somnus53", - "antant123", - "odysseu5", - "gccdu58txs", - "white1_ghost", - "0622842800", - "warriorasas", - "bordtennis", - "call911fro0b", - "232323hi", - "i_4_sp3c_k0", - "anning12", - "dante_dark3ossu44", - "nifeman20", - "fujifilm", - "&pika_power08", - "skiller2010", - "quharas", - "gogos123", - "keatson", - "dodgers5", - "karils_starilgd001", - "mjordan145", - "02092006", - "koth4356", - "1989919899", - "viperwind72", - "3iderct1", - "faeldora81", - "naruto816", - "colt_thunder", - "tc1215", - "bayat4life", - "ali1234567", - "daniel323a", - "kxb69qjx", - "1_rang3_pwns", - "hwdpkonrad", - "o1xaddicted", - "abc123", - "adam402", - "498750", - "gimberly8", - "mrcoolio", - "baister88", - "meatball", - "goodguard5", - "29a11b96", - "josh12508", - "j39256", - "bozzzerzzz", - "nmwhuzrc", - "volcomskate", - "volcom", - "jonny_ere_7", - "ince78989", - "pk_kinkyeachs", - "vlad867", - "vladstorm12", - "justinhigg", - "suilui62", - "beastblade12", - "hobbsey", - "sticklysnipe", - "bayview1", - "snake_1464", - "metalgear", - "parklane79laystation", - "ghtyrupekj", - "jackryan", - "hewitje", - "halloroan", - "pupucachu5", - "spencer4123", - "8bloodhollow", - "ilovejesus", - "hunting_slea", - "h3llfire5", - "dds_o_w_n_s", - "9933230", - "mankikned22", - "kikulita", - "skaleris3", - "skaleriukas", - "bluejay303", - "codrocks", - "bloodhalo11", - "11272000", - "maxsheld", - "91330451", - "jd4gray", - "johnny12", - "krisi129", - "langbestur", - "zadashi", - "gearsfan", - "jollyspace", - "451901", - "quekchose111", - "5bp9mg", - "maxsheld", - "91330451", - "frog1421", - "tomatos1", - "drk34695", - "chimpanze243", - "undead0wner", - "talald2", - "86251234", - "radicalluke", - "kelly55", - "lakertein", - "metheman", - "krissu127", - "hyundai1", - "jmannon00b1", - "cheese1", - "swirkstas", - "rainislopas1", - "gnarwaster1", - "qrhvoq549", - "loveburito", - "carter22", - "tiglasutru", - "t1o2y3o4t5", - "fangedbabyokemon1", - "pijuitshorty", - "marakasaya", - "mr_funky_121", - "joker45", - "g_man", - "william", - "vexraill", - "austin123", - "pbcharger_85", - "radilla1", - "thepureryan", - "121489ine", - "lukasky", - "lukecool", - "dinbolir", - "remember1", - "swe_nintendo", - "lew492car", - "cody52368", - "fr0nt1er", - "xxandoxx2552", - "liverpool1", - "i_aduka_i", - "lolowned", - "downlar", - "jjasjjas", - "harrrocksrisonbreak", - "cookies_ohh", - "noseboy", - "nickspie", - "semmy12", - "u_rock123412", - "themind", - "hi_im_tom9ooloop", - "keatermy", - "thomas98", - "exemped", - "ikbenyufei", - "sum1udontno9", - "kr0iznt0", - "dark_rune195", - "theoldnite", - "savagelump", - "kilosteak4", - "lakwit17", - "93444900", - "pl0xables", - "lemonade1", - "leeboy262", - "niniwee", - "benosigmaaperisima", - "kazokas7", - "lopas123", - "demonza1", - "will00115", - "lavaminer4", - "meandme138", - "twinkseypeshill5", - "idrinkshakes", - "rfyhyj6", - "johnymuffin", - "casey88", - "rempicool", - "admiraal1", - "celayer", - "bhboca2009", - "seflarious", - "teslatron", - "artedhghtun", - "freeze112", - "pur3k0911", - "footlong", - "cooldude_x5", - "toprak123", - "cartoon_101", - "kfauynoob", - "okas5", - "sittur", - "el_sudado", - "sudadera", - "joeymaster93", - "cookiej1", - "thedrewboy12", - "123123ha", - "savior_4_u", - "slutbomb", - "jiwonbae1", - "flameblaze", - "xjordankr96x", - "goodboy99", - "solomon1103", - "09102000", - "omfgitsemo", - "tr4p34", - "marsh450rototype", - "pellikanelliper", - "messidonasef", - "yoyododo", - "selesan", - "kovalev27", - "n0_def_own_u", - "hv650cou", - "swarnes", - "sntioi004", - "snakeboy756", - "0014702211", - "seaviper101", - "willow8520", - "bradd1808", - "smokey1808", - "quintanamore", - "momcatakers", - "ahmarman", - "doggydoodoo", - "war4more", - "stephens", - "m3ntal_sh0ck", - "nosam123", - "tinybird02", - "m1l1tary", - "kill_guys", - "jugheadellenn", - "janieboy2", - "kakhoofd123", - "koedijk1", - "lolliepop", - "uhh_dead_k", - "lehrman", - "36dragon20", - "izzi29040", - "spuddman17", - "nearhood", - "ironedsockoleystr1", - "hotviper6", - "applesnakes6", - "tyber3796", - "nintendo1", - "putther", - "g9768241", - "ynot_money", - "superdude10", - "pl0x____die", - "c1tyw1ns", - "rob1tooby1", - "awesomeman2", - "rsrownage", - "welcome01", - "smail154", - "economie", - "actualglory", - "hello123", - "x_gril8", - "timmed1", - "north1085", - "jjn2008", - "maximus_ivo", - "1210729", - "robzicles", - "sk8ordie", - "semanttinen", - "koipallo", - "zink22100", - "toshiba1", - "frylok_lol", - "baseballftw", - "flux_wolf", - "glowx111", - "haddon70", - "jamaican13", - "clivet_goth", - "lol1lol1", - "celsius91", - "jonkka123", - "kacj321", - "9hjgyt83", - "puzzlled456", - "4elderwand", - "moonbear98", - "regitllij", - "worsinksteeg", - "logisung35", - "kingescape22", - "7dejulio", - "sanka181", - "campbell", - "chikn_kikkr", - "dinkdink", - "zachdamaniak", - "godzilla", - "pk_mpskiller", - "309251270", - "pukkajoep", - "sk1ttl35", - "edparks10", - "firemonkey4", - "sasarrus", - "superman1", - "900_monster", - "tealcrowd8", - "sonicboom367", - "wreckem", - "burnt_c", - "shadow123", - "lv_egils", - "qwerty12345", - "thorodd", - "zgergo", - "hieveryone21", - "snakesnakeoitsas", - "neo34rd", - "yl2bfbml", - "tobi5q", - "fredzio0", - "qwerty2k3_n2", - "allister3", - "james13975", - "movingon", - "im_adored", - "99str", - "magicpinga12", - "freminik", - "chirinako", - "miraculousmuppet", - "joshj304", - "m0t0r5h0w", - "valor226", - "empire2", - "elliott9900", - "meggie26", - "kiteofdark", - "ralph321", - "kyrzani", - "wsl63069", - "0nly_4t_he4d", - "marcao18", - "kernivore", - "born2bewild", - "1_jeterfan", - "goatmilk", - "killer_gomez", - "0007779", - "sto__joni", - "miljoona1", - "kittehgod", - "familyguy5", - "airman521", - "qwerty521", - "draskiller", - "123579jf", - "good_guy2222", - "jsl1983", - "macauqe", - "kabbible", - "the_magius", - "steven86", - "a007lex1", - "shadow581000", - "fearless264", - "61694jjl", - "trevor600000", - "trevorlee", - "leimz", - "konnad123", - "mblais5", - "nida17", - "r_0_b_1annkook123", - "hheadstrong", - "tremayne", - "goyzman", - "jennifer123", - "xleet_ninjax", - "zachyb11", - "adamdude333", - "adam1997", - "saat_pataa", - "cra2f52z", - "crazyidiot70", - "jellybean", - "plasma_guy4", - "maxpower", - "taohealin", - "insane1992", - "uhh_omg_obby", - "llop6123", - "edrash1", - "alenho", - "kent688", - "fghjkl46", - "123erer0", - "rustydog23", - "monstrs23", - "roblox123", - "soldbuffalo", - "benxharris", - "coen42", - "mh2agewi", - "specialguy16megama", - "jo0_my_b33ch", - "volcanic1", - "stany35", - "220198th", - "motas23", - "keese123", - "swordblood3", - "moomoo3", - "davyandicall", - "shakira", - "paivmonster", - "seven77", - "sharpair", - "a51thetruth", - "gabo2oo4m", - "demon_ftwz", - "chicken1252", - "goodguard4", - "29a11b96", - "phonix260", - "dragon123", - "bencebence1", - "19960227", - "trainsgomoo4", - "killhill", - "lightness178", - "xdpowerxd", - "magican_owns", - "0204492600", - "sly_155", - "dragonballz", - "ssj4gogeta95", - "eggnogg871", - "kerfoogle", - "football09", - "mattfrank7", - "1azsxdcfvgb", - "invaliid3", - "maolenmunn", - "arkanoid0", - "ftred432", - "anonoumous1", - "00100101", - "i_have_wingz", - "thibeault", - "wolf_of_warr", - "hurricane", - "rauan78", - "mental223", - "kay_0wned", - "9971282", - "marththemage", - "ssbmrules", - "hcberryblitz", - "uzumaki", - "beast_rage9", - "carbine666", - "deadlydude46", - "aardf1993", - "sharper_arro", - "imawesome1", - "unholy1zero", - "1w3e4r5", - "i_pkernoob_i", - "kirkyisko", - "stift8", - "jaapstam200", - "takeda_iesyu", - "h0lmg4rd", - "im_to_low", - "england123", - "viniciusrub", - "1224abb08e", - "harris_0066", - "james0066", - "daretorparn", - "volvo245", - "didzydang", - "hduerw", - "idskillet", - "ilovehalo1", - "cows_moo34", - "bigbang6", - "el_brento9", - "nightwing", - "fireshot_co2", - "bestest", - "king5646uppylove", - "max2531oepchinees", - "kain581", - "iecsf3hm", - "heffman_13", - "jl19605", - "peep310", - "ilovesanta", - "agentds7okerface77", - "kurithia", - "readytodie", - "ozzy_496", - "shebagirl", - "evilpk999", - "hidalgo", - "joker67tny", - "demilover", - "putles5", - "lacina100", - "redstone177", - "bb123bb", - "1hit1kill567", - "1456327", - "whiplashfan", - "suzukits50", - "vyndirya", - "sjzipje96", - "soapthrail", - "killzs11", - "i_hang_kids", - "circaman1", - "jason9918", - "jasonw11", - "austinz611", - "azorn611", - "zumerock", - "kirara45", - "abyssalite", - "un1versal", - "hellfire33k", - "j067946409", - "iron_vs_maul", - "notrated7", - "deadly_ghoul", - "chad1990", - "samie497", - "walkersv", - "sir_st77", - "94387747pablitoz", - "spijderkat", - "vissen24", - "blikbiermine", - "medion123", - "trixdig", - "nixon995", - "dragonwar729", - "dragonwar123", - "sc0rch1n_2h", - "jagvet66", - "apex_jrkspypk1", - "leynell", - "tresz134", - "blocky_ownz", - "ryan006", - "king_batty_s", - "godzilla", - "very_bigfoot", - "freded", - "slyestcat", - "zared45", - "loko3610", - "lucozade12", - "edwarrior317", - "thunderbolt17", - "quietatrange", - "alec123", - "0_0_0wned_00", - "doumdoum11", - "jaxnarb", - "darkness1", - "achilles0009", - "nathanok", - "r4ngepr0fi", - "leidinger", - "torbjorn6", - "google25me25", - "42_to_doom", - "leeong05", - "jammer8009", - "babybear", - "standi96", - "schakal1", - "sucuraboii27", - "ducks66", - "15_years_now", - "bleachfan", - "xuan_yu31", - "dcvhhvcd123", - "jazabar", - "772892ex", - "arch_4_dutch", - "zwart123", - "dizzy_loot", - "14racing", - "grimmm125", - "budgie88", - "chrisj144", - "chrisj92298", - "sir_arfa", - "arfaiscoolio", - "pvp_rangers2", - "skaiciuokle", - "aka_t_rex", - "kingdomkey0", - "uncletom96", - "cheese96", - "matrix_men33", - "matukka", - "haleyboy1", - "charlie21", - "roooooosa", - "kakajunn4444", - "sacniv_zzzokemonas", - "sint_gabriel", - "iamsogoodnow", - "nightwolf06", - "thelandshark", - "sirsparkles1elling123", - "sergioshm", - "wsaq3214", - "sxe_ickis", - "32210t86p", - "sterling1717", - "calvin1", - "dwarfman666", - "mynameis", - "romeo_beller", - "jess987", - "bandion", - "elhipfop", - "adam_josiah", - "vultures", - "bloodydruid3oopklol", - "majin125", - "q78j2rps", - "averykc", - "woodshirt", - "chemical711", - "linkhero", - "lord_fis0", - "hejhejhej", - "xvim999", - "tazsucks", - "colinmacray", - "cool123", - "zs102", - "funkytown27", - "croston10", - "jacob7767", - "2669331", - "charlto1", - "fmgh2145mvplesjack", - "&adam_c73", - "bamboozled", - "thegoge", - "hot123qw", - "puzzlled456", - "4elderwand", - "jrtiger297", - "zekerdog1", - "damexican99", - "1124phc92", - "tonganboyz1", - "ironman54", - "molson_1903", - "y8h9i20j", - "ketchupp9", - "mustard", - "shongo3258", - "thunderbolt", - "zammorak202", - "54126635", - "sword395", - "link_707", - "joshua12397", - "phenns", - "robots100", - "lucc666aspoort16", - "luukielakke", - "feyenoord", - "rangeown1123", - "joshienewtro", - "fennell100", - "bobdylan", - "junitre1", - "hidare22", - "slasher954", - "budflip12", - "depredad0r", - "656565", - "shadowzrs", - "narut0123", - "grimmick15", - "gowrangaownz", - "ultima_9899", - "1337master", - "dominzdrac", - "xxrunxx34", - "sinduko", - "baltimore", - "machoman275", - "y4ou21", - "ahmedspy", - "ahmed1992", - "ronnie19l", - "gilplatt97", - "evil_miedo", - "88nst0rm", - "ejaeger99", - "lilmanas", - "oozaru66", - "sanctuary", - "dima638arool123", - "nija646", - "luke1998", - "ditto110", - "catdog97", - "ebfft", - "ekonesone", - "morten_aj", - "sootyimpy11", - "money_boy688rton100", - "happyheelas", - "schema36", - "samyett", - "flapula123", - "tony634ash12", - "thedog77", - "pascals123mgomgomg", - "ikrissu", - "eestimaa", - "patrsa", - "4po2sj", - "morten_aj", - "sootyimpy11", - "danorah", - "coolcat60", - "crazywiteboi", - "g5x74m8ss", - "element11123", - "25193469", - "sawyer443swd1994", - "pomojoona", - "seliseli99", - "yanumba1boi1", - "43gb2im4", - "that_guy503", - "whatsdoin", - "glennneke123", - "cool123", - "revolution04", - "330330", - "ryanl809", - "asshass1", - "gh0stsnyper", - "chopper7", - "pyro_fyro_0rinceton", - "onilink992", - "skyrim1234", - "emsixteen", - "19890910", - "taeed24", - "assassin12", - "vi3t0wnage", - "wolfpack450", - "swarnes", - "sntioi004", - "lppalscore", - "thebob55", - "dhuurfa", - "roflitsme", - "astizzz", - "58912b50", - "manta566", - "koirahullu", - "robin5290", - "jameswalker", - "setiar1", - "jesus123", - "hej", - "abbcab", - "smegomeister", - "sammson98", - "dee_alster", - "mynameisbob", - "schadoxx", - "hell4hell", - "pk_mauzeris", - "12211995", - "sheepeater9", - "knauer12345", - "kartunez", - "143256a", - "machado123", - "jayhay22", - "jakee_bum", - "4445poop", - "smurfturf248", - "jzadams", - "arr0wpl0x", - "wiirules1", - "ryan01234", - "donald21", - "juoppoeki", - "asami789", - "dante_dark3ossu44", - "joseph999159", - "shadow1", - "rubini98", - "nutella1998", - "lazyfaith", - "qgy2o6", - "mrwreckedem", - "soccer1", - "ty_the_ninja", - "icychill", - "blackeye446", - "simonv3", - "logiemac11", - "maceachern1", - "short_tastic", - "blink182", - "pops_mahone", - "whitebtii", - "war_eagle47", - "mischief", - "qazross", - "dread46", - "13wesley13", - "warfight", - "hellz_rader", - "landman", - "dakatt13", - "nickname", - "poop_o_nator", - "0901257", - "nummy_flame9", - "magemage", - "ads784", - "bellonfire", - "werslty", - "tyler923", - "bowslinger_9", - "789987455", - "chainmall", - "jackson12398", - "gurglesprainennydrop1", - "legyplegyverboard99", - "junkyboy55", - "bindoosd", - "gaomaster", - "xuanhan", - "danielmedvec", - "123spill", - "oddly_alive", - "newyork", - "babababoe", - "runskape", - "bigdunk69", - "soccer13", - "cat_prince25", - "rockman", - "paxton269", - "22drp88", - "uchihazen", - "ihateyou111", - "expo136", - "alexpass", - "peliset", - "jfas97gv", - "sords_4_life", - "1011657", - "krazy_kat575", - "tigerflame", - "aligindahose", - "killforfood", - "impzoren", - "enter123", - "dylan00117", - "hotdog10", - "bisinji", - "rustysam", - "tangle_golen", - "02061996", - "jijdwaas", - "theelord", - "varduka", - "apina19", - "chinadarkelf", - "morrowind", - "kickball83", - "ilovekayla", - "bradwoodward", - "brad5420", - "kriznar56", - "machupichu56", - "dezzilisk", - "coldocean", - "jorieboy3", - "lwn13jk", - "joneliux458", - "5jega55", - "celayer", - "bhboca2009", - "will753357liver4793", - "gpunks3", - "gmschool", - "therayman940", - "buddyboy", - "schulpy", - "chelsea1", - "madfrk2222", - "mmrabb1", - "mattiege", - "buster98", - "monsterdany", - "fs7312kp", - "eldden", - "252 625 26", - "xbl33s_pk3rxortugal", - "grotsnik4", - "skulltaker", - "peaceweazer", - "sta10stik", - "jordanator2", - "maddydog1", - "indomezu", - "indasunay8", - "chainlink66ieisgood11", - "xchichimanx", - "babychich", - "rugissement", - "glen123", - "slayer78110", - "calcetin", - "drakeaumann", - "eminem45", - "voxyvax8563", - "konvict", - "niklasvegas", - "videohai72", - "elliotorin", - "dadcabsxxx", - "cw_range", - "clanwars", - "saynoob", - "holland5", - "fattyman_12", - "77vettee", - "tryharder4me", - "lolatmyname", - "native_ta_50", - "blood4life", - "xchichimanx", - "babychich", - "medevil_troy", - "bobbobster6", - "aolh8i", - "wildboy1888", - "9118747", - "macacox", - "12touchsuperett", - "sonny585", - "77vettee", - "theprorspker", - "1a2s3d4f5", - "The Old Nite", - "94cm442modrak96", - "north_omaha", - "southomaha", - "ruindungeon", - "fearoth", - "gpgard15", - "homies01", - "kaz123kaz123", - "bieniak0048", - "bartez4", - "k4eo9ssi7", - "myroyalwings", - "compact1157", - "godawgs08", - "dco14", - "baddiekins", - "gtrhro511", - "devilman651", - "georgekk9", - "zain_winters", - "158491122", - "vovakova", - "551879321", - "mr0sharkie", - "136qwerty136", - "tylerrockis1", - "yoyobaby", - "bbmonke1honglu1", - "armadylz_x", - "wtfyounoob", - "fille809", - "0763595260", - "mansonroks1", - "manson1", - "dubstepparty", - "lmmikjki", - "uniwide", - "darkmage", - "mandude65", - "manduderules", - "don_dalton", - "bobutberg", - "biscoito77", - "15181996", - "munkki345", - "dfzrbrm7", - "zarocks2", - "henry6679", - "nikdx", - "99long", - "smilly48", - "blackout22", - "im_not_afish", - "1wiiwii", - "javiercmh", - "teno8181", - "ancient_ttam", - "shooter12", - "str_poiss10", - "k2tlin", - "segraps", - "12qwaszx", - "nephew31", - "hubbabubba", - "guthix2569zzyjosh1234", - "rulxman", - "singapore", - "frozenax7955", - "noodles", - "terumpk1", - "hello101", - "xyameax", - "bert1127", - "scottstimo", - "sabop8", - "destructo361", - "13461618", - "nikdx", - "99long", - "khanpop1", - "khan2662", - "lilboibryan", - "dragon123", - "benjo171", - "spireite1", - "king___joel", - "monkey33", - "kukko12", - "kukkeli12", - "swanepoel101", - "luke081295", - "mobuis_three", - "mommys4", - "zorzacken", - "hookedon", - "feetsoup", - "forrest11", - "lilsaint343", - "0akwood", - "krisalix", - "karadic", - "saisseh", - "miskaman123", - "eckbulldog", - "ecknick2510", - "cruel_guy2", - "coolguy2", - "jawad1144", - "soldier123", - "sebber43", - "kiwiiscape", - "darkshiv", - "wert0980", - "sp3c_m4ul3r", - "covcov21", - "rhinaboy", - "dragon465", - "crazy_nut45", - "9687241", - "trx_despair", - "thethethe123", - "x1atcha", - "anthony", - "mr_nart", - "epicfail", - "marti1101", - "redfaction", - "paulohare", - "superman1", - "m4_0wns", - "juska123", - "coopershaw14", - "13scooter14", - "viper_snake", - "superfluous", - "fender_love", - "devoncallme", - "thorax777", - "stilton1", - "reese55rinter0", - "flyflip", - "jarrod123", - "blackeye446", - "simonv3", - "sircoolio", - "stillsnoopanddre", - "zimza_toes", - "leonard9", - "raza838", - "deadlove", - "jamsinkereewee50", - "jatanoma", - "lolnoobs1", - "starcat2002", - "i6560344", - "tarapita", - "ud2hnx6q", - "xcowboys", - "cowboys458", - "elgaro12", - "83ed04374", - "logrono23", - "logman23", - "grundledib", - "m3ath00ks", - "ballum2mega123", - "1w1ll0wna", - "thebrazil", - "patacorow", - "globalfun", - "savtej89", - "elkgrove", - "darth_nome", - "5ofclubs", - "tiili10", - "07010701", - "u_said_stick", - "ibanez28", - "lolxman123", - "qwerty123", - "rebel_goon", - "chrome18", - "shaunth3pur3", - "whatthe", - "ruagamer1212", - "nothing1", - "strownedyou6", - "lolnoob", - "willroley", - "hdw77k69", - "shortd00d", - "d1014d", - "mr_dark720artypooper", - "copadon", - "girordot", - "converse_con", - "bunny123", - "shallowspace", - "tums9182", - "xxlpad", - "arnis1996", - "camo124501", - "abcdefghijk", - "i_druelennythug", - "bonebreakerrikksuger", - "karma_rebornpaassss", - "katastrophi4", - "wrighton5", - "s_n_x_w", - "thatiscool", - "brusio", - "516967853", - "snakeboy756", - "0014702211", - "audiomonk", - "drumstick", - "sidekik123", - "liban123", - "nguyen374", - "nguyen1996", - "i_agsu_i", - "jsjugb69", - "jlventre", - "denmarka4", - "porto410ote1212", - "carltyler1", - "megadeth", - "batzo", - "qwerty88", - "crystalfang3", - "devin113", - "lvl50_attack", - "cherrim012", - "kristaps288", - "9792218", - "rockyroller3", - "harrytate2", - "karrashimo", - "never122", - "necrovolt", - "solovino1", - "addpoke", - "hpcdrt45", - "hotsauce4299", - "a1b2c3d4", - "elblelbl", - "djscratch", - "spiderman_o", - "kabsa23g", - "grenade18", - "goofygoober", - "blueguy8989", - "digimon8989", - "dusty_granny", - "naikyaone", - "ffs_i_say_no", - "erik123", - "my_str_maax", - "lukinhas", - "tankbuster97", - "tanksmash2", - "eemuman", - "nakkipate", - "pk_zorbes", - "mor1990", - "dgfriend5", - "456789abc", - "twisternick6", - "jason22", - "alan22589", - "gotmoney1", - "noob_karil3", - "ee11ee11", - "elmo909", - "focus123", - "swordfury40", - "stonecold9", - "rey_rey_6116", - "metsss11", - "rebagon", - "44b19n1v", - "lava_viper_6", - "lavaviper78", - "taryn_blue1", - "taryn71", - "pluralgarths", - "frederic123", - "skeloperch", - "zombieperch", - "verrrtigo", - "karolekas", - "fuhq247", - "6423694rocky", - "bnarney", - "clicker882", - "newbie882", - "str_bonage", - "minemunni135", - "brsnip3rlapster10", - "orangie_boi", - "jamesd23", - "legendy3", - "ubernozle1", - "maulz_w_t_f2", - "lilzoee", - "flyman821", - "tomtiger647", - "fishizle", - "hellomoto", - "sir_hepuli1", - "j0n10nh0t0rn0t", - "knight_alam", - "tors66", - "stavar111", - "ausdaniel", - "bulldogs1", - "o_he_shot_me", - "fuckhead", - "dan_10567", - "dragzxasqw", - "jeze666", - "minarell1", - "the_a97", - "romeydog", - "thegringo44", - "smdsmd44", - "dale_89", - "2215077", - "chrisroscher", - "jagex13245", - "rabye_str", - "hopk1t02", - "killangel0", - "cheatx101", - "grunt628", - "asshole1", - "wolf_boy247", - "blompee123", - "p_k_e_r_1_0", - "mustang1", - "99killerbull", - "fireman", - "jbird197", - "jistheman12", - "f__t__f", - "hahamines66", - "lkjhgfdsa420", - "charlie1", - "ilovehighlvl", - "sedef4", - "napplesauce", - "tony1243", - "biowarrior11", - "jmaj6722", - "klansman5", - "young10", - "dawnodarknes", - "redalert", - "negglesolbones22", - "garrdrenn1", - "gd2005", - "draognzboy", - "zippmeup", - "oseart55", - "starwars501", - "xfrightknite", - "jlidd8990", - "master_pk260skar123", - "3meister", - "dragon13", - "madevillemon", - "20111993", - "cupit11", - "harinton7", - "xrapidclawx", - "jordie4", - "aurocks14", - "310051552", - "dombomagexd", - "midweek", - "falconfan_24", - "battlehero96", - "charkrit019", - "apple1901", - "duongmaster", - "johnny07", - "recon783", - "eraserface", - "faxxx1", - "nightfall64", - "mrjiggles25", - "7415001", - "zarm3000", - "xzsawq67", - "final_pure_g", - "zammy88", - "redfox280attrock", - "rodrigo_g_m", - "daynnight", - "mag3_kill4s", - "magepwnsu", - "prime_maggot", - "almost1", - "elkalibre", - "b0n3cl1nkz", - "radi100", - "kamalkamal1", - "i_newarcher", - "nakniki1", - "lord_shunoby", - "2615948", - "stinkerror", - "dragondz", - "crusher16619", - "doremember", - "3_run_range", - "11011997", - "robo1rocks", - "rocco1998", - "tomcat975", - "hermano123", - "sarlon7", - "irish001", - "aznballer66", - "9312025", - "killers455", - "matthewmjk", - "desparo2222", - "yoelmejor", - "md650epa1", - "gonzalez", - "inferno368", - "rectamos", - "aced52", - "iwashere321", - "goblin5530hjok123", - "h4x_pl0xz0r", - "suzukirm85", - "jessinator18", - "hamedarash", - "uzi_of_doom", - "8401397", - "sotired", - "tootfart64", - "alphazone123", - "ankouisme123", - "daewoo45", - "joshowns", - "gundymonster", - "meow111", - "acidacre", - "kappa100", - "boeman75", - "ethsaq2", - "yorotomaru", - "qwopzxnm7913", - "im_not_afish", - "1wiiwii", - "2rubbish", - "muslim786", - "boomikiller", - "daco2023", - "knight_988", - "wearefamily", - "corbo999", - "whiterat", - "yaranzo5", - "keller18", - "nick4998", - "jnrc3271", - "rizeline55", - "hradhrad", - "lsufreak9", - "lsutigers1", - "lost_pker222", - "facebookrule", - "napplesauce", - "tony1243", - "13wesley13", - "warfight", - "chirinako", - "miraculousmuppet", - "tanked_you_l", - "qazwsx123", - "spence587", - "spencer", - "crabs_magee", - "reaperr7", - "kingspell", - "969896250", - "bewareof_me", - "200102494g", - "grundith", - "jadranovo05", - "numeris14", - "goblinn8", - "theepicscout", - "sonic44", - "&phat_dood5", - "briansucks", - "momrocker", - "nutmeg60", - "sp_i_r_i_t", - "1heehtam2", - "one_secret", - "firedoor56", - "wizcoltt", - "merlinnus", - "archebowz", - "bn0du7cs", - "outoftimewon", - "snapple12", - "gavistire", - "098665chaz", - "k_o_sorry", - "cristianor7", - "masta395", - "indy340", - "alvaroscary", - "killa94", - "mr_gandolff", - "1968camero", - "wizkidvic", - "uk3dbg", - "dgenx21388", - "wwefan1994", - "printingman", - "kopieer", - "humper123479", - "mikey97", - "davydude2", - "92thesnack92", - "pure0292", - "king1234", - "1_h8_n3rdz", - "bedtime", - "caveman422", - "ilikepie87", - "emosocks1", - "5496931", - "g2uypie", - "enok210", - "workrange_o", - "latefriday", - "surplus99", - "shabahang", - "jonnathan002", - "jonny002", - "132kickflip", - "heelflip", - "andrewfly89", - "roland123", - "&snypermatt69", - "canucks2012", - "youdie111", - "1moretime", - "dannyhxdman8", - "theend1", - "g__f__d", - "2fast4you", - "grundledib", - "m3ath00ks", - "zalvose", - "assassins", - "gangstabros8", - "master13", - "supanight", - "leonard12317", - "thefurrymage", - "hotrod1994", - "tim_but_dim", - "1994tyrer", - "woferiukas", - "deividas", - "failedgull", - "goupia123", - "inferno_1060", - "vtech1060", - "junk_king33", - "junkguy", - "racer_jaret", - "callofduty4", - "westorz", - "741825963", - "zenachue", - "bomber357", - "r4ngepr0fi", - "leidinger", - "kiryu36", - "leo8lion", - "monk_fights", - "4991diddly", - "boggom_8097", - "garret1998", - "svensktsnus", - "asdaasda123", - "ruudinho10", - "hans19953109", - "eltacolad", - "bartlett", - "esrb5", - "junk_king33", - "junkguy", - "vanquishwolf", - "hubertas", - "w_3_s_c_0_t", - "dogtown4life", - "10hp_clans", - "zinedine", - "vader11567", - "thunder1", - "rempicool", - "admiraal1", - "5cripter", - "roverdog123", - "stallion918", - "5489621", - "zeroz_pure", - "buster77owned123", - "north_omaha", - "southomaha", - "alexwars400", - "starwars4", - "pureskill714", - "raheem600", - "molson_1903", - "y8h9i20j", - "arromin", - "scarm4a1", - "ravenangel1", - "lancaster", - "willheath20", - "canuyo2", - "smokadadank", - "5433285", - "blkchocobo", - "shalico101", - "envious_soul", - "setcapmoc6", - "shepowndu", - "sll41ygi", - "shoot_here2", - "triops1029", - "bananaboy441", - "mieszawski", - "pur3iwillbe", - "monkeystew", - "toddy_the_pk", - "brosis09", - "logrono", - "gorillazfgi", - "b1itz3d", - "samsung90", - "blakkwizard", - "mythodea", - "toke223", - "hamster223", - "guyman123890", - "donkeykong", - "riduan34", - "lekurde", - "desiver2", - "throw80", - "silver_gunn", - "masteravery", - "darkhawk744", - "cocoman6", - "c0c0nutmilkeatrick0", - "&dragon_j86", - "1234567890a", - "steriili_pk", - "arsk4", - "c_robert_t", - "monster1234", - "sirpericlesmfgwtf", - "kiill42", - "your1zax", - "goliathsnail", - "illidan", - "balcattaboy", - "corbin94", - "vereza", - "d3susama", - "&fort_40", - "moonlight7", - "bobsmith909", - "kellyrocks", - "idirim", - "rennmaus358", - "latirem", - "muthsera", - "pur3man_v10", - "duckcow1", - "kanpar2", - "vinimi", - "slomdapom", - "k8s2e7g4", - "hi_on_420", - "dillweed", - "zellanisixy3000", - "tozzel", - "toypaj", - "triblouge", - "carsa100", - "texasdingoasswd123", - "donkie90", - "nicolas123", - "mag3_pur3_ko", - "hayesbonin", - "zedukas_x", - "eligijus", - "cbuity", - "code515396", - "wardmathias", - "ritabob", - "dubert4", - "rooney11", - "throwchance", - "chance5", - "karamellioole123", - "sillyninja20", - "darkonecut0", - "mars_noob", - "volvovr0", - "gagton23", - "inkheart", - "sock_flavour", - "sophie72", - "reaper_4me", - "bessiedog", - "brknsprk22", - "whyuhackme", - "rayvano55", - "rayvano98", - "zaborgttm", - "warrior", - "madsbang123", - "christensen", - "theboz87", - "iqoigu2d", - "f4ll3n0n3", - "xbox360123", - "stijnkessel", - "kessel1996", - "genderoni", - "lolimawesome", - "amac109", - "downhill", - "citricabyss", - "ceesis", - "terry1000666", - "bink2468", - "roman1248", - "abcdefghijk", - "superchunklz", - "lizzie9", - "fast_reflexx", - "asherasher", - "rebagon", - "44b19n1v", - "staylow", - "nismo360", - "tornoc109", - "thunder20", - "crosby_8127", - "silhouette", - "moltenore3", - "lavabomb", - "feedu_arrows", - "soccer28", - "mindkampfers", - "notsogood", - "ashbacher7", - "tppcrpg7", - "michaelsyer", - "cue2hug", - "drewno58", - "j_p_13", - "111robinson", - "heyitstrea", - "mccain", - "doc_control", - "fartmaster", - "neonarg", - "b9501175", - "snorlax4", - "jasonchou", - "xgameboy95", - "craig9596", - "ojaste296lku123", - "sheltzor", - "karateboy11", - "makkiestone", - "billymon", - "blackspidder", - "sn1perh34ded", - "jerzeythugz2", - "trueset", - "pure_x_400askaa1234", - "mr_sgytkonyn", - "twoitem", - "1archerhbk", - "assinass", - "tharifica1", - "apgart00", - "fildherbn", - "freeze112", - "kno_way", - "loloklol", - "supermacho1", - "09101991", - "raptor8721", - "justindb", - "jolo5001", - "catfish", - "spiker991", - "qpwoeiruty", - "seasonsdawn", - "seasonsend", - "jartt2", - "jartt1", - "uber3pic", - "doodoo12345", - "rio1927ieman11", - "angered_nubackers", - "user_zim", - "zacharyh1", - "cool_kevin50", - "4194ka", - "freitty", - "gamecube", - "silver_knig0", - "yourmom1", - "beanroid", - "smoke47149", - "flitch289", - "nooder000", - "amazing_cake", - "iridium", - "aliffridzuan", - "ridzuan2000", - "bartez4", - "k4eo9ssi7", - "juxikeederast", - "d_man_5567", - "q1w2e1r2", - "be_kingless", - "hardflip", - "yami671", - "fbr114", - "rowelselidio", - "gagoako", - "haggishanger", - "football1", - "sonderby", - "banjomus", - "sagar448", - "heroes12", - "windkid62595", - "lilly117", - "kacj321", - "9hjgyt83", - "sly_ninja80", - "waynegod13", - "discombobu99", - "rocky34", - "zorontu", - "xtaylorx", - "zezimaki", - "ilovelordi", - "joey7x", - "kaleb8598", - "kruiness", - "daisymay21", - "paul77777izzapizza1", - "cow_hearder", - "kingdrag", - "lama_007", - "trojans1", - "helslayer11", - "roblox11", - "knifebloody", - "1092310923", - "dansendeaap", - "uiensoep", - "pure_exz", - "29038995scotland", - "theysconator", - "ysbrandve1", - "chrissy_booy", - "egghead", - "supreme1511", - "landis1511", - "tharujusassword123", - "jdablada", - "ugotskills", - "kewl_kid", - "killer99", - "nicknack60", - "choffee12", - "a007lex1", - "shadow581000", - "ecmxflawless", - "twenty8", - "roguewolf33", - "firefox101", - "kaare83_2", - "korsvegen", - "da_king1295", - "xemnas1", - "airwalk285", - "archman", - "sir_kieran12", - "spiderpig08", - "fasterthanyu", - "asdflkj", - "lihtsaber2", - "joe00224", - "fortress170", - "centurion", - "youmeyou4", - "laxworld", - "xxmrfixitxx", - "killer55", - "morags", - "ericolde", - "e_a_r_l_y_k0liverkerr", - "benno_rules", - "lotrftw5", - "embrach", - "meier007", - "xxlpad", - "arnis1996", - "spike8342", - "knuckles", - "spit_fire469", - "tuesday23", - "jonahmonk", - "blackma9ic", - "manmage247", - "kingcoke", - "ourkornalmer64", - "paulypauly22", - "chickenpoo", - "kis_a_legend", - "samtr0n", - "amigos39", - "blackhole", - "abc12pee", - "zman1123", - "mr_frost22", - "bubby5522", - "lauren_maher", - "foamy1992", - "robertwb69", - "rwb691169187", - "blackhart74", - "sambone", - "janreypogi1", - "kakilala", - "hairy_marry", - "brucie123", - "gaming_gypsy", - "qwewqwewqwe", - "pros_only", - "killer29043", - "night_raid89", - "hershey", - "dragan_man62", - "sordman", - "vaizakhter1", - "19692000", - "rob1tooby1", - "awesomeman2", - "papagal7", - "cartof33", - "lil_0ctog0n", - "hi12", - "matthew_1001", - "elliemay", - "krilitor", - "charlesk", - "rileymango", - "882psauq", - "wutknochen", - "12muschel78", - "ravincal", - "warchief1", - "joloplex", - "scrubs93", - "kelly_koehn", - "goldrush", - "0k_im_sorry", - "david123", - "zack494", - "lima7277", - "killermac234", - "spartan", - "sharmen24", - "bigballs", - "ricarddragon", - "123123s", - "illepappeft", - "mapet123", - "josh6621", - "dragon6621", - "p__0__0__n3", - "bringmetolife", - "silentblue5", - "hardflip96", - "cbosk8er", - "getbuff", - "steelbeast97", - "ironbeast", - "zarkima", - "idioteque8", - "goselink1989", - "janlul123", - "rocky1247", - "rocklobster9", - "supapwnage", - "holden123", - "vietboihao", - "vietboi", - "cutvision", - "internet", - "stinkerror", - "dragondz", - "robert_magic", - "darlene2", - "min_ko7", - "kristian1998", - "adami888", - "tetramins", - "godssent2", - "shadow14", - "sawingmotion", - "drag00n", - "devincm", - "harder64", - "mrcrowley676", - "stevefry", - "canadian_nrg", - "elvens11", - "dbl_m", - "mattrox3", - "redgoblin352", - "beunard", - "sonderby", - "banjomus", - "sagar448", - "heroes12", - "windkid62595", - "lilly117", - "kacj321", - "9hjgyt83", - "sly_ninja80", - "waynegod13", - "discombobu99", - "rocky34", - "zorontu", - "xtaylorx", - "zezimaki", - "ilovelordi", - "joey7x", - "kaleb8598", - "kruiness", - "daisymay21", - "paul77777izzapizza1", - "cow_hearder", - "kingdrag", - "lama_007", - "trojans1", - "helslayer11", - "roblox11", - "knifebloody", - "1092310923", - "dansendeaap", - "uiensoep", - "pure_exz", - "29038995scotland", - "theysconator", - "ysbrandve1", - "chrissy_booy", - "egghead", - "supreme1511", - "landis1511", - "tharujusassword123", - "jdablada", - "ugotskills", - "kewl_kid", - "killer99", - "nicknack60", - "choffee12", - "a007lex1", - "shadow581000", - "ecmxflawless", - "twenty8", - "roguewolf33", - "firefox101", - "kaare83_2", - "korsvegen", - "da_king1295", - "xemnas1", - "airwalk285", - "archman", - "sir_kieran12", - "spiderpig08", - "fasterthanyu", - "asdflkj", - "lihtsaber2", - "joe00224", - "fortress170", - "centurion", - "youmeyou4", - "laxworld", - "xxmrfixitxx", - "killer55", - "morags", - "ericolde", - "e_a_r_l_y_k0liverkerr", - "benno_rules", - "lotrftw5", - "embrach", - "meier007", - "xxlpad", - "arnis1996", - "spike8342", - "knuckles", - "spit_fire469", - "tuesday23", - "jonahmonk", - "blackma9ic", - "manmage247", - "kingcoke", - "ourkornalmer64", - "paulypauly22", - "chickenpoo", - "kis_a_legend", - "samtr0n", - "amigos39", - "blackhole", - "abc12pee", - "zman1123", - "mr_frost22", - "bubby5522", - "lauren_maher", - "foamy1992", - "robertwb69", - "rwb691169187", - "blackhart74", - "sambone", - "janreypogi1", - "kakilala", - "hairy_marry", - "brucie123", - "gaming_gypsy", - "qwewqwewqwe", - "pros_only", - "killer29043", - "night_raid89", - "hershey", - "dragan_man62", - "sordman", - "vaizakhter1", - "19692000", - "rob1tooby1", - "awesomeman2", - "papagal7", - "cartof33", - "lil_0ctog0n", - "hi12", - "matthew_1001", - "elliemay", - "krilitor", - "charlesk", - "rileymango", - "882psauq", - "wutknochen", - "12muschel78", - "ravincal", - "warchief1", - "joloplex", - "scrubs93", - "kelly_koehn", - "goldrush", - "0k_im_sorry", - "david123", - "zack494", - "lima7277", - "killermac234", - "spartan", - "sharmen24", - "bigballs", - "ricarddragon", - "123123s", - "illepappeft", - "mapet123", - "josh6621", - "dragon6621", - "p__0__0__n3", - "bringmetolife", - "silentblue5", - "hardflip96", - "cbosk8er", - "getbuff", - "steelbeast97", - "ironbeast", - "zarkima", - "idioteque8", - "goselink1989", - "janlul123", - "rocky1247", - "rocklobster9", - "supapwnage", - "holden123", - "vietboihao", - "vietboi", - "cutvision", - "internet", - "stinkerror", - "dragondz", - "robert_magic", - "darlene2", - "min_ko7", - "kristian1998", - "adami888", - "tetramins", - "godssent2", - "shadow14", - "sawingmotion", - "drag00n", - "devincm", - "harder64", - "mrcrowley676", - "stevefry", - "canadian_nrg", - "elvens11", - "dbl_m", - "mattrox3", - "redgoblin352", - "beunard" - }; - -} diff --git a/Server/src/main/java/rs09/game/ai/resource/ResourceAIPActions.java b/Server/src/main/java/rs09/game/ai/resource/ResourceAIPActions.java deleted file mode 100644 index 7999b49e9..000000000 --- a/Server/src/main/java/rs09/game/ai/resource/ResourceAIPActions.java +++ /dev/null @@ -1,71 +0,0 @@ -package rs09.game.ai.resource; - -import core.game.node.entity.skill.gather.SkillingResource; -import core.game.node.entity.player.Player; -import rs09.game.ai.AIPlayer; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; -import rs09.game.world.repository.Repository; - -import java.util.List; - -public class ResourceAIPActions { - - public static List resource_players = null; - - protected SkillingResource resource; - - public static void syncBotThread(final Player player) { - if (resource_players == null || resource_players.size() == 0) { - return; - } - for (int aip_index = 0; aip_index < resource_players.size(); aip_index++) { - final AIPlayer bot = resource_players.get(aip_index); - GameWorld.getPulser().submit(new Pulse(1, bot) { - int ticks; - int ov; - @Override - public boolean pulse() { - /*if (bot.getInventory().freeSlots() < 1) { - //flee(bot); - return true; - }*/ - //Inactivity logout timer. - if (ticks++ == 5000/* && bot.getWalkingQueue().getQueue().isEmpty()*/) { - AIPlayer.deregister(bot.getUid()); - resource_players.remove(bot); - return true; - } - if (ov++ >= 3) { - chopTree(bot); - ov = 0; - } -// ov = 0; - System.out.println("ResourceAIPActions" + ov); - return false; - } - }); - } - } - - private static boolean chopTree(final AIPlayer bot) { - /*for (GameObject[] o : RegionManager.forId(bot.getLocation().getRegionId()).getPlanes()[0].getObjects()) { - for (GameObject obj : o) { - if (obj != null) { - for (SkillingResource r : SkillingResource.values()) { - if (obj.getId() == r.getId()) { - Pathfinder.find(bot, obj).walk(bot); - bot.sendChat("WE CHOPPIN"); - } - } - } - } - }*/ - bot.attack(Repository.getNpcs().get(106)); - -// Pulse pulse = new GatheringSkillPulse(bot, node); -// bot.getPulseManager().run(new GatheringSkillPulse(bot, node)); -// pulse.start(); - return true; - } -} diff --git a/Server/src/main/java/rs09/game/ai/resource/ResourceAIPManager.java b/Server/src/main/java/rs09/game/ai/resource/ResourceAIPManager.java deleted file mode 100644 index 3bc015317..000000000 --- a/Server/src/main/java/rs09/game/ai/resource/ResourceAIPManager.java +++ /dev/null @@ -1,168 +0,0 @@ -package rs09.game.ai.resource; - -import core.game.node.entity.player.Player; -import rs09.game.ai.resource.task.ResourceTask; -import rs09.game.ai.resource.task.ResourceTasks; -import core.game.system.task.Pulse; -import rs09.game.world.GameWorld; - -import java.sql.ResultSet; -import java.sql.Statement; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - - -/** - * Package -> org.keldagrim.game.node.entity.player.ai.resource - * Created on -> 9/13/2016 @12:42 PM for 530 - * - * @author Ethan Kyle Millard - */ -public class ResourceAIPManager { - - private long time = 0; - - private Player player; - - private static Map TASKS = new HashMap<>(); - - public ResourceAIPManager init() { - getTasks().put(ResourceTasks.WOODCUTTING.getResourceTask(), 0L); - return this; - } - - public ResourceAIPManager runTask(Player player, String taskName) { - for (Map.Entry entry : getTasks().entrySet()) { - if (entry.getKey().getTaskName().equalsIgnoreCase(taskName)) { - if (entry.getValue() != 0) { - message(player, "You have extended the task " + taskName + "."); - } else { - message(player, "You have activated the task " + taskName + "."); - } - } - entry.getKey().setTaskName(taskName); - entry.setValue(entry.getValue() + 70); - this.player = player; - } - return this; - } - - public void reActivate(String name, long time) { - for (Map.Entry entry : TASKS.entrySet()) { - if (entry.getKey().getTaskName().equalsIgnoreCase(name)) { - entry.setValue(time); - } - } - } - - public ResourceAIPManager load(Player player) { - try { - Statement statement = GameWorld.getDatabaseManager().connections().get("global").createStatement(); - - ResultSet result = statement.executeQuery("SELECT * FROM `members` WHERE username='" + player.getUsername() + "'"); - // Results result = new Results(GameWorld.getDatabaseManager().query("global", "SELECT * FROM `members` WHERE username='" + player.getUsername() + "'")); - - while (result.next()) { - String eventName = result.getString("taskName"); - String eventTime = result.getString("taskTime"); - reActivate(eventName, Long.valueOf(eventTime)); - } - - } catch (Exception e) { - e.printStackTrace(); - } - return this; - } - - public ResourceAIPManager save(Player player) { - /*if (GameWorld.getDatabaseManager().update("global", "DELETE FROM `members` WHERE worldid='" + GameWorld.getSettings().getWorldId() + "'") < 0) - return this;*/ - System.out.println("Saving..."); - Iterator> iterator = getTasks().entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - if (entry.getValue() <= 0) - continue; - StringBuilder query = new StringBuilder(); - query.append("UPDATE `members` SET `taskName`='" + entry.getKey().getTaskName() + "',`taskTime`='" + entry.getValue() + "' WHERE `username`='" + player.getUsername() + "'"); - System.out.println("ResourceAIPManager: " + query.toString()); - GameWorld.getDatabaseManager().update("global", query.toString()); - - } - return this; - } - - public void pulse(Player player) { - GameWorld.getPulser().submit(new Pulse(1) { - @Override - public boolean pulse() { - - time++; - - Iterator> iterator = getTasks().entrySet().iterator(); - - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - if (entry.getValue() > 0) { - entry.setValue(entry.getValue() - 1); - if (entry.getValue() == 50) - message(player, "You have 30 minutes before " + entry.getKey().getTaskName() + " task ends."); - if (entry.getValue() <= 0) { - entry.getKey().setTime(0); - entry.getKey().reward(player, entry.getKey().getTaskName()); - message(player, "The task " + entry.getKey().getTaskName() + " has now ended."); - save(player); - } - if (time == 50) { - entry.getKey().setTime(0); - save(player); - } - } - } - return false; - } - }); - } - - public ResourceAIPManager message(Player player, String message) { - return message(player, message, true, ""); - } - - public ResourceAIPManager message(Player player, String message, boolean tag) { - return message(player, message, tag, ""); - } - - public ResourceAIPManager notify(Player player, String message) { - return message(player, message, true, ""); - } - - public ResourceAIPManager notify(Player player, String message, boolean tag) { - return message(player, message, tag, ""); - } - - - public ResourceAIPManager message(Player player, String message, boolean tag, String color) { - player.getPacketDispatch().sendMessage(color + (tag ? "[Resource Manager] - " : "") + message); - return this; - - } - - public static Map getTasks() { - return TASKS; - } - - private static ResourceAIPManager INSTANCE = new ResourceAIPManager(); - - public static ResourceAIPManager get() { - return INSTANCE; - } - - public Player getPlayer() { - return player; - } - - public void setPlayer(Player player) { - this.player = player; - } -} diff --git a/Server/src/main/java/rs09/game/ai/resource/task/ResourceTask.java b/Server/src/main/java/rs09/game/ai/resource/task/ResourceTask.java deleted file mode 100644 index 7e3e979fa..000000000 --- a/Server/src/main/java/rs09/game/ai/resource/task/ResourceTask.java +++ /dev/null @@ -1,39 +0,0 @@ -package rs09.game.ai.resource.task; - -import core.game.node.entity.player.Player; - -/** - * Package -> org.keldagrim.game.node.entity.player.ai.resource.task - * Created on -> 9/13/2016 @12:44 PM for 530 - * - * @author Ethan Kyle Millard - */ -public abstract class ResourceTask { - - - private String taskName; - private long time; - - public ResourceTask(String taskName, long time) { - this.taskName = taskName; - this.time = time; - } - - public abstract void reward(Player player, String eventName); - - public long getTime() { - return time; - } - - public void setTime(long time) { - this.time = time; - } - - public String getTaskName() { - return taskName; - } - - public void setTaskName(String taskName) { - this.taskName = taskName; - } -} diff --git a/Server/src/main/java/rs09/game/ai/resource/task/ResourceTasks.java b/Server/src/main/java/rs09/game/ai/resource/task/ResourceTasks.java deleted file mode 100644 index cd55e19bd..000000000 --- a/Server/src/main/java/rs09/game/ai/resource/task/ResourceTasks.java +++ /dev/null @@ -1,46 +0,0 @@ -package rs09.game.ai.resource.task; - -import core.game.node.entity.skill.Skills; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.tools.RandomFunction; - -/** - * Package -> org.keldagrim.game.node.entity.player.ai.resource.task - * Created on -> 9/13/2016 @12:43 PM for 530 - * - * @author Ethan Kyle Millard - */ -public enum ResourceTasks { - - WOODCUTTING(new ResourceTask("Willow Logs", 6000) { - - @Override - public void reward(Player player, String eventName) { - if (player.getSkills().getLevel(Skills.WOODCUTTING) < 30) { - player.sendMessage(reqirementMessage()); - return; - } - if (getTime() == 0) { - player.getBank().add(new Item(1519, RandomFunction.random(700, 1000))); - player.getSkills().addExperience(Skills.WOODCUTTING, RandomFunction.random(100000, 150000)); - } - } - }); - - private final ResourceTask resourceTask; - - - ResourceTasks(ResourceTask resourceTask) { - this.resourceTask = resourceTask; - } - - public ResourceTask getResourceTask() { - return resourceTask; - } - - - private static String reqirementMessage() { - return "You do not meet the requirements for this task."; - } -} diff --git a/Server/src/main/java/rs09/game/ai/skillingbot/SkillingBot.java b/Server/src/main/java/rs09/game/ai/skillingbot/SkillingBot.java deleted file mode 100644 index 44e930e9f..000000000 --- a/Server/src/main/java/rs09/game/ai/skillingbot/SkillingBot.java +++ /dev/null @@ -1,128 +0,0 @@ -package rs09.game.ai.skillingbot; - -import core.game.world.map.Location; - -import java.util.ArrayList; - -import core.game.node.entity.skill.Skills; -import core.game.interaction.Option; -import core.game.node.Node; -import rs09.game.ai.AIPlayer; -import core.game.node.item.Item; -import core.net.packet.in.InteractionPacket; - -public class SkillingBot extends AIPlayer { - - private int tick = 5; - private ArrayList interactNodeIds; - private int fromWhereDoIdrop; - private int skill; - private int interactionRange; - - public SkillingBot(Location l) - { - super(l); - this.fromWhereDoIdrop = 0; - this.interactionRange = 15; - } - - public SkillingBot(Location l, int skill, ArrayList entrys) - { - super(l); - this.skill = skill; - this.fromWhereDoIdrop = 0; - this.interactNodeIds = entrys; - this.interactionRange = 15; - - switch (this.skill) - { - case Skills.MINING: - default: - break; - } - } - - @Override - public void tick() - { - super.tick(); - - //Despawn - if (this.getSkills().getLifepoints() == 0) - AIPlayer.deregister(this.getUid()); - - //Interact with object - if (this.tick <= 0) - { - this.tick = 5; - if (this.skill == Skills.FISHING) - this.tick = 20; - - // Node test = getClosestNodeWithEntry(15, 15503); - Node node; - if (this.skill != Skills.FISHING) - { - node = getClosestNodeWithEntry(interactionRange, interactNodeIds); - } - else - { - node = getClosesCreature(interactionRange, interactNodeIds); -// if (test != null) -// System.out.println("interact with " + test.getName()); - } - - if (node == null) { - System.out.println("SkillingBot.java: Object not found " + this.skill); - return; - } - - //System.out.println("free slots " + this.getInventory().freeSlots()); - - if (this.getInventory().isFull()) - { -// System.out.println(this.getName() + " starts droping from " + fromWhereDoIdrop); - for (int i = fromWhereDoIdrop; i < 28; i++) - { - Item drop = this.getInventory().get(i); - final Option option = drop.getInteraction().get(4); - drop.getInteraction().handleItemOption(this, option, this.getInventory()); -// System.out.println("drop item " + i); - } - } - - int x = node.getLocation().getX(); - int y = node.getLocation().getY(); - if (this.skill != Skills.FISHING) - { - InteractionPacket.handleObjectInteraction(this, 0, x, y, node.getId()); - } - else - { - InteractionPacket.handleNPCInteraction(this, 0, node.getIndex()); - } - } - else - this.tick--; - } - - public int getSkill() - { - return this.skill; - } - - public int getFromWhereToDrop() - { - return this.fromWhereDoIdrop; - } - - public void setFromWhereDoIdrop(int id) - { - this.fromWhereDoIdrop = id; - } - - public void setInteractionRange(int range) - { - this.interactionRange = range; - } - -} diff --git a/Server/src/main/java/rs09/game/ai/skillingbot/SkillingBotsBuilder.java b/Server/src/main/java/rs09/game/ai/skillingbot/SkillingBotsBuilder.java deleted file mode 100644 index 9ce76c2d0..000000000 --- a/Server/src/main/java/rs09/game/ai/skillingbot/SkillingBotsBuilder.java +++ /dev/null @@ -1,209 +0,0 @@ -package rs09.game.ai.skillingbot; - -import java.util.ArrayList; - -import core.game.container.impl.EquipmentContainer; -import core.game.content.quest.tutorials.tutorialisland.CharacterDesign; -import core.game.node.entity.skill.Skills; -import rs09.game.ai.AIPlayer; -import core.game.node.entity.player.link.appearance.Gender; -import core.game.node.item.Item; -import core.game.world.map.Location; -import core.tools.RandomFunction; - -public final class SkillingBotsBuilder extends AIPlayer { - - public SkillingBotsBuilder(Location l) { - super(l); - // TODO Auto-generated constructor stub - } - - private static SkillingBot generateMiningBot(Location loc, ArrayList entrys) - { - SkillingBot bot = new SkillingBot(loc, Skills.MINING, entrys); - bot.getAppearance().setGender(RandomFunction.random(3) == 1 ? Gender.FEMALE : Gender.MALE); - - - bot.getEquipment().replace(new Item(1265), EquipmentContainer.SLOT_WEAPON); - return bot; - } - - private static SkillingBot generateWoodcuttingBot(Location loc, ArrayList entrys) - { - SkillingBot bot = new SkillingBot(loc, Skills.WOODCUTTING, entrys); - bot.getAppearance().setGender(RandomFunction.random(3) == 1 ? Gender.FEMALE : Gender.MALE); - - - bot.getEquipment().replace(new Item(1351), EquipmentContainer.SLOT_WEAPON); - return bot; - } - - private static SkillingBot generateFishingBot(Location loc, ArrayList entrys) - { - SkillingBot bot = new SkillingBot(loc, Skills.FISHING, entrys); - bot.getAppearance().setGender(RandomFunction.random(3) == 1 ? Gender.FEMALE : Gender.MALE); - CharacterDesign.randomize(bot, false); - - - return bot; - } - - public static void spawnClayBotVarrock(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(15503); - entrys.add(15505); - - SkillingBot bot = generateMiningBot(loc, entrys); - - bot.getSkills().setLevel(Skills.MINING, 10); - } - - public static void spawnIronBotVarrock(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(11954); - entrys.add(11955); - entrys.add(11956); - - SkillingBot bot = generateMiningBot(loc, entrys); - - bot.getSkills().setLevel(Skills.MINING, 25); - } - - public static void spawnSilverBotVarrock(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(11948); - entrys.add(11949); - entrys.add(11950); - - SkillingBot bot = generateMiningBot(loc, entrys); - - bot.getSkills().setLevel(Skills.MINING, 30); - } - - public static void spawnTinBotVarrock(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(11957); - entrys.add(11959); - entrys.add(11958); - - SkillingBot bot = generateMiningBot(loc, entrys); - - bot.getSkills().setLevel(Skills.MINING, 5); - } - - public static void spawnTinBotLumbridge(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(11933); - entrys.add(11934); - entrys.add(11935); - - SkillingBot bot = generateMiningBot(loc, entrys); - - bot.getSkills().setLevel(Skills.MINING, 5); - } - - public static void spawnCopperBotLumbridge(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(11936); - entrys.add(11937); - entrys.add(11938); - - SkillingBot bot = generateMiningBot(loc, entrys); - - bot.getSkills().setLevel(Skills.MINING, 5); - } - - public static void spawnOakTreeBotLumbridge(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(1281); - entrys.add(1278); - entrys.add(1276); - - SkillingBot bot = generateWoodcuttingBot(loc, entrys); - - bot.getSkills().setLevel(Skills.WOODCUTTING, 25); - bot.setInteractionRange(25); - } - - public static void spawnDeadTreeBotLumbridge(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(1282); - entrys.add(1286); - - SkillingBot bot = generateWoodcuttingBot(loc, entrys); - - bot.getSkills().setLevel(Skills.WOODCUTTING, 25); - bot.setInteractionRange(15); - } - - public static void spawnShrimpFisherLumbridge(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(323); - entrys.add(326); - - SkillingBot bot = generateFishingBot(loc, entrys); - - bot.getInventory().add(new Item(303)); - // don't drop net - bot.setFromWhereDoIdrop(1); - - bot.getSkills().setLevel(Skills.FISHING, 25); - bot.setInteractionRange(25); - } - - public static void spawnTroutLumbridge(Location loc) - { - ArrayList entrys = new ArrayList(); - entrys.add(310); - - SkillingBot bot = generateFishingBot(loc, entrys); - - bot.getInventory().add(new Item(309)); - bot.getInventory().add(new Item(314, 20000)); - // don't drop net - bot.setFromWhereDoIdrop(2); - - bot.getSkills().setLevel(Skills.FISHING, 25); - bot.setInteractionRange(25); - } - - //These bots are disabled because they somehow break pets - public static void immersiveSpawnsSkillingBots() - { - // Varrock Mine - SkillingBotsBuilder.spawnClayBotVarrock(new Location(3181, 3368)); - SkillingBotsBuilder.spawnSilverBotVarrock(new Location(3181, 3368)); - SkillingBotsBuilder.spawnIronBotVarrock(new Location(3181, 3368)); - SkillingBotsBuilder.spawnTinBotVarrock(new Location(3181, 3368)); - - // Lumbridge woodcutting - spawnOakTreeBotLumbridge(new Location(3227, 3243)); - spawnOakTreeBotLumbridge(new Location(3186, 3251)); - spawnOakTreeBotLumbridge(new Location(3188, 3223)); - spawnOakTreeBotLumbridge(new Location(3162, 3222)); - spawnOakTreeBotLumbridge(new Location(3162, 3219)); - spawnOakTreeBotLumbridge(new Location(3152, 3228)); - spawnOakTreeBotLumbridge(new Location(3146, 3244)); - spawnDeadTreeBotLumbridge(new Location(3247, 3240)); - - // Lumbridge mining - spawnTinBotLumbridge(new Location(3224, 3147)); - spawnCopperBotLumbridge(new Location(3224, 3147)); - - // Lumbridge Fishing - spawnShrimpFisherLumbridge(new Location(3242, 3151)); - spawnShrimpFisherLumbridge(new Location(3238, 3148)); - spawnShrimpFisherLumbridge(new Location(3245, 3161)); - spawnTroutLumbridge(new Location(3241, 3242)); - } - -} diff --git a/Server/src/main/java/rs09/game/ai/system/GlobalAIManager.java b/Server/src/main/java/rs09/game/ai/system/GlobalAIManager.java deleted file mode 100644 index 7c94487fa..000000000 --- a/Server/src/main/java/rs09/game/ai/system/GlobalAIManager.java +++ /dev/null @@ -1,32 +0,0 @@ -package rs09.game.ai.system; - - -import rs09.game.ai.AIPlayer; - -import java.util.HashMap; -import java.util.Map; - -public class GlobalAIManager { - - - /** - * The active Artificial intelligent players mapping. - */ - private static final Map BOT_MAPPING = new HashMap<>(); - - - /** - * Constructs a new {@code GlobalAIManager} {@code Object}. - */ - public GlobalAIManager() { - - } - - public static void addBot(int uid, AIPlayer player) { - BOT_MAPPING.put(uid, player); - } - - public static AIPlayer getBot(int uid) { - return BOT_MAPPING.get(uid); - } -} diff --git a/Server/src/main/java/rs09/game/ai/system/android/CombatDroid.java b/Server/src/main/java/rs09/game/ai/system/android/CombatDroid.java deleted file mode 100644 index 5b0cd0533..000000000 --- a/Server/src/main/java/rs09/game/ai/system/android/CombatDroid.java +++ /dev/null @@ -1,4 +0,0 @@ -package rs09.game.ai.system.android; - -public class CombatDroid { -} diff --git a/Server/src/main/java/rs09/game/ai/system/android/GatheringDroid.java b/Server/src/main/java/rs09/game/ai/system/android/GatheringDroid.java deleted file mode 100644 index 10de24031..000000000 --- a/Server/src/main/java/rs09/game/ai/system/android/GatheringDroid.java +++ /dev/null @@ -1,4 +0,0 @@ -package rs09.game.ai.system.android; - -public class GatheringDroid { -} diff --git a/Server/src/main/java/rs09/game/ai/system/predicate/Predicate.java b/Server/src/main/java/rs09/game/ai/system/predicate/Predicate.java deleted file mode 100644 index 5f1377b5c..000000000 --- a/Server/src/main/java/rs09/game/ai/system/predicate/Predicate.java +++ /dev/null @@ -1,102 +0,0 @@ -package rs09.game.ai.system.predicate; - -import javax.security.auth.Destroyable; - -import rs09.game.ai.system.GlobalAIManager; - -/** - * An android predicate. - * - * @author Viper - * @version 1.0 - 13/07/2016 - */ -public abstract class Predicate implements Cloneable, Runnable, Destroyable { - - protected GlobalAIManager bot; - - /** - * Predicate started. - */ - private boolean started; - - /** - * The predicate priority factor. - */ - private byte priority; - - /** - * The predicate type. - */ - public abstract PredicateType getType(); - - /** - * Constructs a new {@link Predicate} {@code Object}. - * - * @param priority - * The priority of this predicate. - */ - public Predicate(int priority) { - this.priority = (byte) priority; - } - - public Predicate clone() { - final Predicate pred = this; - return new Predicate(priority) { - - @Override - public void run() { - pred.run(); - } - - @Override - public PredicateType getType() { - return pred.getType(); - } - }.setStarted(pred.started); - } - - /** - * @return the priority - */ - public byte getPriority() { - return priority; - } - - /** - * @param priority the priority to set - */ - public void setPriority(byte priority) { - this.priority = priority; - } - - /** - * @return the bot - */ - public GlobalAIManager getBot() { - return bot; - } - - /** - * @param bot the bot to set - */ - public Predicate setBot(GlobalAIManager bot) { - this.bot = bot; - return this; - } - - /** - * @return the started - */ - public boolean hasStarted() { - return started; - } - - /** - * @param started the started to set - */ - public Predicate setStarted(boolean started) { - this.started = started; - return this; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/rs09/game/ai/system/predicate/PredicateType.java b/Server/src/main/java/rs09/game/ai/system/predicate/PredicateType.java deleted file mode 100644 index 18629092c..000000000 --- a/Server/src/main/java/rs09/game/ai/system/predicate/PredicateType.java +++ /dev/null @@ -1,13 +0,0 @@ -package rs09.game.ai.system.predicate; - -/** - * Types of predicates. - * - * @author Viper - * @version 1.0 - 13/07/2016 - */ -public enum PredicateType { - - BOOT, TRANSCENDING, PARSING, TRIVIAL; - -} \ No newline at end of file diff --git a/Server/src/main/java/rs09/game/ai/system/predicate/impl/AppearancePredicate.java b/Server/src/main/java/rs09/game/ai/system/predicate/impl/AppearancePredicate.java deleted file mode 100644 index fe1ffc58e..000000000 --- a/Server/src/main/java/rs09/game/ai/system/predicate/impl/AppearancePredicate.java +++ /dev/null @@ -1,60 +0,0 @@ -package rs09.game.ai.system.predicate.impl; - - -import rs09.game.ai.system.predicate.Predicate; -import rs09.game.ai.system.predicate.PredicateType; - -/** - * Generates random appearance for the droid. - * - * @author Viper - * @version 1.0 - 13/07/2016 - */ -public class AppearancePredicate extends Predicate { - - /** - * If the bot is a male. - */ - private boolean male; - - /** - * Constructs a new appearance predicate. - */ - public AppearancePredicate(boolean male) { - super(-1); - this.male = male; - } - - @Override - public PredicateType getType() { - return PredicateType.PARSING; - } - - @Override - public void run() { - if (isMale()) { - /*bot.getAppearence().getBodyStyle()[0] = Utility.random(0, 8); - bot.getAppearence().getBodyStyle()[1] = Utility.random(10, 17); - bot.getAppearence().getBodyStyle()[2] = Utility.random(18, 25); - bot.getAppearence().getBodyStyle()[3] = Utility.random(26, 32); - bot.getAppearence().getBodyStyle()[4] = Utility.random(33, 34); - bot.getAppearence().getBodyStyle()[5] = Utility.random(36, 41); - bot.getAppearence().getBodyStyle()[6] = Utility.random(42, 44); - - bot.getAppearence().getBodyColors()[2] = (byte) Utility.random(0, 127); - bot.getAppearence().getBodyColors()[1] = (byte) Utility.random(0, 127); - bot.getAppearence().getBodyColors()[0] = (byte) Utility.random(0, 24);*/ - } else { - - } -// bot.getAppearence().generateAppearenceData(); - } - - /** - * @return the male - */ - public boolean isMale() { - return male; - } - -} \ No newline at end of file diff --git a/Server/src/main/java/rs09/game/ai/wilderness/PvPBotsBuilder.java b/Server/src/main/java/rs09/game/ai/wilderness/PvPBotsBuilder.java deleted file mode 100644 index fdaa7a5cb..000000000 --- a/Server/src/main/java/rs09/game/ai/wilderness/PvPBotsBuilder.java +++ /dev/null @@ -1,541 +0,0 @@ -package rs09.game.ai.wilderness; - -import java.util.ArrayList; - -import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.combat.CombatSpell; -import rs09.game.ai.AIPlayer; -import rs09.game.ai.pvmbots.DragonKiller; -import rs09.game.ai.pvmbots.NoobBot; -import rs09.game.ai.pvmbots.PvMBots; -import core.game.node.entity.player.link.SpellBookManager; -import core.game.node.entity.player.link.appearance.Gender; -import core.game.node.item.Item; -import core.game.world.map.Location; -import core.tools.RandomFunction; - -public final class PvPBotsBuilder{ - - public static WildernessBot create(Location l) - { - return new WildernessBot(l); - } - - public static void generateClass(WildernessBot p) { - int combatType = RandomFunction.getRandom(1); - switch(combatType) { - case 0: - buildMeleeStats(p); - buildMeleeEquipment(p); - correctHitpointsStat(p); - break; - case 1: - //max rune - buildMaxMeleeStats(p); - buildMaxMeleeEquipment(p); - correctHitpointsStat(p); - break; - } - p.getInventory().add(new Item(385, 20)); - p.getProperties().setCombatLevel(126); - - p.getSkills().setLevel(Skills.PRAYER, 98); - p.getSkills().setStaticLevel(Skills.PRAYER, 98); - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - - private static void buildMeleeStats(WildernessBot p) { - int attackLevel = RandomFunction.random(80, 98); - int strengthLevel = RandomFunction.random(attackLevel - 10, attackLevel + 10); - int defenceLevel = RandomFunction.random(60, 98); - - p.getSkills().setLevel(Skills.ATTACK, attackLevel); - p.getSkills().setStaticLevel(Skills.ATTACK, attackLevel); - p.getSkills().setLevel(Skills.STRENGTH, strengthLevel); - p.getSkills().setStaticLevel(Skills.STRENGTH, strengthLevel); - p.getSkills().setLevel(Skills.DEFENCE, defenceLevel); - p.getSkills().setStaticLevel(Skills.DEFENCE, defenceLevel); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - private static void buildMeleeEquipment(WildernessBot p) { - int[] helms = { - 1163, //Rune Full Helm - 3751, //Berserker Helm - 3753, //Warrior Helm - 10828, - 1038, - 4753, - }; - int[] shield = { - 1185, //Rune sq shield - 1201, //Rune kiteshield - 6524, //Obsidian shield - 8850, //Rune defender - 11286, - 14767, - }; - int[] platebody = { - 1113, //Rune chainmail. - 1127, //Rune platebody. - 10551, - 4757, - 11724, - 4720, - }; - int[] platelegs = { - 1079, //Rune platelegs. - 4759, - 11726, - 1075, - }; - - p.getEquipment().replace(new Item(helms[RandomFunction.random(helms.length)]), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(shield[RandomFunction.random(shield.length)]), EquipmentContainer.SLOT_SHIELD); - p.getEquipment().replace(new Item(platebody[RandomFunction.random(platebody.length)]), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(platelegs[RandomFunction.random(platelegs.length)]), EquipmentContainer.SLOT_LEGS); - - Item weapon = new Item(weapons[RandomFunction.random(weapons.length)]); - p.setMainWeapon(weapon.getId()); - p.getEquipment().replace(weapon, EquipmentContainer.SLOT_WEAPON); - - p.getEquipment().replace(new Item(generateNecklace()), EquipmentContainer.SLOT_AMULET); - p.getEquipment().replace(new Item(generateBoots()), EquipmentContainer.SLOT_FEET); - p.getEquipment().replace(new Item(generateCape()), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(generateGloves()), EquipmentContainer.SLOT_HANDS); - } - - public static final int[] weapons = { - 4151, //Whip - 4587, //Dragon Scimitar - - }; - - - private static void buildMaxMeleeStats(AIPlayer p) - { - p.getSkills().setLevel(Skills.ATTACK, 99); - p.getSkills().setStaticLevel(Skills.ATTACK, 99); - p.getSkills().setLevel(Skills.STRENGTH, 99); - p.getSkills().setStaticLevel(Skills.STRENGTH, 99); - p.getSkills().setLevel(Skills.DEFENCE, 99); - p.getSkills().setStaticLevel(Skills.DEFENCE, 99); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - private static void buildMaxMeleeEquipment(AIPlayer p) { - int[] helms = { - 3751, //Berserker Helm - }; - int[] shield = { - 14767, //Dragon defender - }; - int[] platebody = { - 11724, //Bandos chainmail. - }; - int[] platelegs = { - 11726, //Bandos Tassets. - }; - int[] amulets = { - 6585, //fury - }; - int[] boots = { - 3105, //Climbing boots. - }; - int[] cape = { - 6570, //Fire cape. - }; - int[] gloves = { - 7462, //Barrows Gloves. - }; - p.getEquipment().replace(new Item(helms[RandomFunction.random(helms.length)]), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(shield[RandomFunction.random(shield.length)]), EquipmentContainer.SLOT_SHIELD); - p.getEquipment().replace(new Item(platebody[RandomFunction.random(platebody.length)]), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(platelegs[RandomFunction.random(platelegs.length)]), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(weapons2[RandomFunction.random(weapons2.length)]), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(amulets[RandomFunction.random(amulets.length)]), EquipmentContainer.SLOT_AMULET); - p.getEquipment().replace(new Item(boots[RandomFunction.random(boots.length)]), EquipmentContainer.SLOT_FEET); - p.getEquipment().replace(new Item(cape[RandomFunction.random(cape.length)]), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(gloves[RandomFunction.random(gloves.length)]), EquipmentContainer.SLOT_HANDS); - } - - public static final int[] weapons2 = { - 4151, //Whip - }; - - private static int generateCape() { - int[] capes = { - 1052, //Legends Cape - 6570, //Fire cape - }; - return capes[RandomFunction.random(capes.length)]; - } - - private static int generateNecklace() { - int[] amulets = { - 1712, //Glory - 1725, //Strength ammy - 6585, //fury - }; - return amulets[RandomFunction.random(amulets.length)]; - } - - private static int generateGloves() { - int[] gloves = { - 1059, - 7462 - }; - return gloves[RandomFunction.random(gloves.length)]; - } - - private static int generateBoots() { - int[] boots = { - 1061, //Leather boots. - 1837, //Desert boots. - 3105, //Climbing boots. - 3791, //Fremmy boots. - 11732, - }; - return boots[RandomFunction.random(boots.length)]; - } - - public static void generateMinLevels(PvMBots p) - { - //Slayer so they can attack alls monsters - p.getSkills().setLevel(Skills.SLAYER, 99); - p.getSkills().setStaticLevel(Skills.SLAYER, 99); - int combatType = RandomFunction.getRandom(2); - p.getSkills().setLevel(Skills.HITPOINTS, 15); - p.getSkills().setStaticLevel(Skills.HITPOINTS, 15); - p.getInventory().add(new Item(329, 5)); - switch(combatType) - { - case 0: - { - buildArcherStats(p); - buildArcherEquipment(p); - break; - } - case 1: - { - buildMeleeStats(p); - buildMeleeEquipment(p); - break; - } - case 2: - { - buildMageStats(p); - buildMagicEquipment(p); - setupWizard(p); - break; - } - default: - { - buildMeleeStats(p); - buildMeleeEquipment(p); - break; - } - - } - } - - public static void createDragonKiller(DragonKiller p) - { - p.getSkills().setLevel(Skills.SLAYER, 99); - p.getSkills().setStaticLevel(Skills.SLAYER, 99); - int combatType = RandomFunction.getRandom(2); - p.getSkills().setLevel(Skills.HITPOINTS, 55); - p.getSkills().setStaticLevel(Skills.HITPOINTS, 55); - switch(combatType) - { - case 0: - { - buildDragonArcherStats(p); - buildDragonArcherEquipment(p); - break; - } - case 1: - { - buildDragonMeleeStats(p); - buildDragonMeleeEquipment(p); - break; - } - default: - { - buildDragonMeleeStats(p); - buildDragonMeleeEquipment(p); - break; - } - } - } - - public static void createNoob(NoobBot p) - { - p.getSkills().setLevel(Skills.SLAYER, 99); - p.getSkills().setStaticLevel(Skills.SLAYER, 99); - int combatType = RandomFunction.getRandom(3); - p.getSkills().setLevel(Skills.HITPOINTS, 10 + RandomFunction.getRandom(10)); - p.getSkills().setStaticLevel(Skills.HITPOINTS, 10 + RandomFunction.getRandom(10)); - p.getSkills().setLevel(Skills.DEFENCE, 1 + RandomFunction.getRandom(9)); - p.getSkills().setStaticLevel(Skills.DEFENCE, 1 + RandomFunction.getRandom(9)); - p.getInventory().add(new Item(329, 5)); - switch(combatType) - { - case 0: - { - buildNoobArcherStats(p); - buildNoobArcherEquipment(p); - break; - } - case 1: - { - buildNoobMeleeStats(p); - buildNoobMeleeEquipment(p); - break; - } - case 2: - { - buildNoobMageStats(p); - buildNoobMagicEquipment(p); - setupWizard(p); - } - default: - { - buildNoobMeleeStats(p); - buildNoobMeleeEquipment(p); - break; - } - } - } - - private static void buildArcherStats(PvMBots p) - { - p.getSkills().setLevel(Skills.RANGE, 10); - p.getSkills().setStaticLevel(Skills.RANGE, 10); - p.getSkills().setLevel(Skills.DEFENCE, 10); - p.getSkills().setStaticLevel(Skills.DEFENCE, 10); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - private static void buildDragonArcherStats(DragonKiller p) - { - p.getSkills().setLevel(Skills.RANGE, 55); - p.getSkills().setStaticLevel(Skills.RANGE, 55); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - private static void buildNoobArcherStats(NoobBot p) - { - p.getSkills().setLevel(Skills.RANGE, 1 + RandomFunction.getRandom(10)); - p.getSkills().setStaticLevel(Skills.RANGE, 1 + RandomFunction.getRandom(10)); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - public static void buildMeleeStats(PvMBots p) - { - p.getSkills().setLevel(Skills.ATTACK, 10); - p.getSkills().setStaticLevel(Skills.ATTACK, 10); - p.getSkills().setLevel(Skills.STRENGTH, 10); - p.getSkills().setStaticLevel(Skills.STRENGTH, 10); - p.getSkills().setLevel(Skills.DEFENCE, 10); - p.getSkills().setStaticLevel(Skills.DEFENCE, 10); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - public static void buildDragonMeleeStats(DragonKiller p) - { - p.getSkills().setLevel(Skills.ATTACK, 55); - p.getSkills().setStaticLevel(Skills.ATTACK, 55); - p.getSkills().setLevel(Skills.STRENGTH, 55); - p.getSkills().setStaticLevel(Skills.STRENGTH, 55); - p.getSkills().setLevel(Skills.DEFENCE, 45); - p.getSkills().setStaticLevel(Skills.DEFENCE, 45); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - private static void buildNoobMeleeStats(NoobBot p) - { - p.getSkills().setLevel(Skills.ATTACK, 1 + RandomFunction.getRandom(10)); - p.getSkills().setStaticLevel(Skills.ATTACK, 1 + RandomFunction.getRandom(10)); - p.getSkills().setLevel(Skills.STRENGTH, 1 + RandomFunction.getRandom(10)); - p.getSkills().setStaticLevel(Skills.STRENGTH, 1 + RandomFunction.getRandom(10)); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - private static void buildMageStats(PvMBots p) - { - p.getSkills().setLevel(Skills.MAGIC, 10); - p.getSkills().setStaticLevel(Skills.MAGIC, 10); - p.getSkills().setLevel(Skills.DEFENCE, 10); - p.getSkills().setStaticLevel(Skills.DEFENCE, 10); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - private static void buildNoobMageStats(NoobBot p) - { - p.getSkills().setLevel(Skills.MAGIC, 1 + RandomFunction.getRandom(10)); - p.getSkills().setStaticLevel(Skills.MAGIC, 1 + RandomFunction.getRandom(10)); - - p.getSkills().updateCombatLevel(); - p.getAppearance().sync(); - } - - private static void setupWizard(PvMBots p) - { - //final int SPELL_IDS[] = new int[] {1, 4, 6, 8, 10, 14, 17, 20, 24, 27, 33, 38, 45, 48, 52, 55 }; - p.getProperties().setAutocastSpell(((CombatSpell) SpellBookManager.SpellBook.MODERN.getSpell(1))); - p.getInventory().add(new Item(556, 1000)); //Air runes - p.getInventory().add(new Item(558, 1000)); //Mind runes - } - - private static void buildArcherEquipment(PvMBots p) - { - p.getEquipment().replace(new Item(1169), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(1129), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(1095), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(841), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(884, 5000), EquipmentContainer.SLOT_ARROWS); - p.getEquipment().replace(new Item(1007), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(1478), EquipmentContainer.SLOT_AMULET); - } - - private static void buildDragonArcherEquipment(DragonKiller p) - { - p.getEquipment().replace(new Item(1169), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(13483), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(1099), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(9185), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(1540), EquipmentContainer.SLOT_SHIELD); - p.getEquipment().replace(new Item(9140, 500), EquipmentContainer.SLOT_ARROWS); - p.getEquipment().replace(new Item(10498), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(1478), EquipmentContainer.SLOT_AMULET); - } - - private static void buildNoobArcherEquipment(PvMBots p) - { - int hats[] = {1169, 1169, 1139, 1137, 1153, 579}; - int legs[] = {1095, 7366, 1075, 1087, 1095}; - int chest[] = {1129, 1103, 1101, 1129, 1117, 577}; - - p.getEquipment().replace(new Item(hats[RandomFunction.getRandom(hats.length - 1)]), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(chest[RandomFunction.getRandom(chest.length - 1)]), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(legs[RandomFunction.getRandom(legs.length - 1)]), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(841), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(884, 5000), EquipmentContainer.SLOT_ARROWS); - p.getEquipment().replace(new Item(1007), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(1478), EquipmentContainer.SLOT_AMULET); - } - - private static void buildMeleeEquipment(PvMBots p) - { - p.getEquipment().replace(new Item(1153), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(1115), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(1067), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(1309), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(884), EquipmentContainer.SLOT_ARROWS); - p.getEquipment().replace(new Item(1007), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(1725), EquipmentContainer.SLOT_AMULET); - } - - private static void buildDragonMeleeEquipment(DragonKiller p) - { - p.getEquipment().replace(new Item(1163), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(1127), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(1079), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(1333), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(1540), EquipmentContainer.SLOT_SHIELD); - p.getEquipment().replace(new Item(1007), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(1725), EquipmentContainer.SLOT_AMULET); - } - - private static void buildNoobMeleeEquipment(PvMBots p) - { - int hats[] = {1169, 1169, 1139, 1137, 1153, 579}; - int legs[] = {1095, 7366, 1075, 1087, 1095}; - int chest[] = {1129, 1103, 1101, 1129, 1117, 577}; - int weapons[] = {1307, 1321, 1375, 1203, 1239, 1267, 1293, 1323, 1335}; - - p.getEquipment().replace(new Item(hats[RandomFunction.getRandom(hats.length - 1)]), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(chest[RandomFunction.getRandom(chest.length - 1)]), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(legs[RandomFunction.getRandom(legs.length - 1)]), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(weapons[RandomFunction.getRandom(weapons.length - 1)]), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(884), EquipmentContainer.SLOT_ARROWS); - p.getEquipment().replace(new Item(1007), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(1725), EquipmentContainer.SLOT_AMULET); - } - - private static void buildMagicEquipment(PvMBots p) - { - p.getEquipment().replace(new Item(579), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(577), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(1011), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(1389), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(884), EquipmentContainer.SLOT_ARROWS); - p.getEquipment().replace(new Item(1007), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(1727), EquipmentContainer.SLOT_AMULET); - } - - private static void buildNoobMagicEquipment(PvMBots p) - { - int hats[] = {1169, 1169, 1139, 1137, 1153, 579}; - int legs[] = {1095, 7366, 1075, 1087, 1095}; - int chest[] = {1129, 1103, 1101, 1129, 1117, 577}; - int weapons[] = {1379, 1383, 1385, 1387, 1389}; - - p.getEquipment().replace(new Item(hats[RandomFunction.getRandom(hats.length - 1)]), EquipmentContainer.SLOT_HAT); - p.getEquipment().replace(new Item(chest[RandomFunction.getRandom(chest.length - 1)]), EquipmentContainer.SLOT_CHEST); - p.getEquipment().replace(new Item(legs[RandomFunction.getRandom(legs.length - 1)]), EquipmentContainer.SLOT_LEGS); - p.getEquipment().replace(new Item(weapons[RandomFunction.getRandom(weapons.length - 1)]), EquipmentContainer.SLOT_WEAPON); - p.getEquipment().replace(new Item(884), EquipmentContainer.SLOT_ARROWS); - p.getEquipment().replace(new Item(1007), EquipmentContainer.SLOT_CAPE); - p.getEquipment().replace(new Item(1727), EquipmentContainer.SLOT_AMULET); - } - - public static void spawn(Location loc) - { - final WildernessBot bot = PvPBotsBuilder.create(new Location(0, 0)); - bot.teleport(loc); - bot.getAppearance().setGender(RandomFunction.random(3) == 1 ? Gender.FEMALE : Gender.MALE); - - generateClass(bot); - - } - - private static void correctHitpointsStat(AIPlayer player) { - player.getSkills().setLevel(Skills.HITPOINTS, 10); - player.getSkills().setStaticLevel(Skills.HITPOINTS, 10); - player.getSkills().updateCombatLevel(); - int rangedLevel = RandomFunction.random(80, 98); - int defenceLevel = RandomFunction.random(80, 98); - - player.getSkills().setLevel(Skills.HITPOINTS, rangedLevel); - player.getSkills().setStaticLevel(Skills.HITPOINTS, rangedLevel); - player.getSkills().setLevel(Skills.HITPOINTS, defenceLevel); - player.getSkills().setStaticLevel(Skills.HITPOINTS, defenceLevel); - - player.getSkills().updateCombatLevel(); - player.getAppearance().sync(); - } - -} diff --git a/Server/src/main/java/rs09/game/ai/wilderness/WildernessBot.java b/Server/src/main/java/rs09/game/ai/wilderness/WildernessBot.java deleted file mode 100644 index d73ce95b2..000000000 --- a/Server/src/main/java/rs09/game/ai/wilderness/WildernessBot.java +++ /dev/null @@ -1,281 +0,0 @@ -package rs09.game.ai.wilderness; - -import java.util.ArrayList; -import java.util.List; - -import core.game.container.impl.EquipmentContainer; -import core.game.content.consumable.Consumable; -import core.game.content.consumable.Consumables; -import core.game.content.consumable.Food; -import core.game.content.consumable.effects.HealingEffect; -import core.game.node.entity.skill.Skills; -import core.game.interaction.Option; -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import rs09.game.ai.AIPlayer; -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.RegionManager; -import core.tools.RandomFunction; - -public class WildernessBot extends AIPlayer { - - public WildernessBot(Location l) { - super(l); - this.specWeapon = 1215; - this.normalWeapon = 4151; - this.getInventory().add(new Item(specWeapon)); - // TODO Auto-generated constructor stub - } - - public WildernessBot(Location l, int normalWeaponId, int specWeaponId) { - super(l); - this.specWeapon = specWeaponId; - this.normalWeapon = normalWeaponId; - this.getInventory().add(new Item(specWeaponId)); - } - - int tick = 0; - int movetimer = 0; - int agressiveTimer = 2; - int eatdelay = 2; - boolean fleeing = false; - boolean riskIt = false; - int specWeapon;// = new Item(1215); - int normalWeapon;// = new Item(4151); - - Item chest = this.getEquipment().get(EquipmentContainer.SLOT_CHEST); - Item legs = this.getEquipment().get(EquipmentContainer.SLOT_LEGS); - Item helmet = this.getEquipment().get(EquipmentContainer.SLOT_HAT); - Item weapon = this.getEquipment().get(EquipmentContainer.SLOT_WEAPON); - Item shield = this.getEquipment().get(EquipmentContainer.SLOT_SHIELD); - - - public List FindTargets(Entity entity, int radius) { - List targets = new ArrayList<>(20); - for (Player player : RegionManager.getLocalPlayers(entity, radius)) { { - if (checkValidTargets(player)) - targets.add(player); - } - } - if (targets.size() == 0) - return null; - return targets; - } - - public boolean checkValidTargets(Player target){ - if (!target.isActive()) - { - return false; - } - if (!target.getProperties().isMultiZone() && target.inCombat()) { - return false; - } - if (target.inCombat()) - { - return false; - } - return true; - } - - public void AttackPlayersInRadius(Player bot, int radius) - { - if (bot.inCombat()) - return; - List players = FindTargets(bot, radius); - if (players == null) - { - return; - } - if (!(players.isEmpty())) - { - if (agressiveTimer <= 0) - bot.attack(players.get(RandomFunction.getRandom((players.size() - 1)))); - return; - } - } - - private Entity getTarget() - { - return this.getProperties().getCombatPulse().getVictim(); - } - - private void checkSpecialAttack() - { - Entity target = getTarget(); - if (target == null) - return; - - if (target.getSkills().getLifepoints() <= 75 && this.getSettings().getSpecialEnergy() >= 25) - { - //System.out.println("try special"); - Item weapon = this.getItemById(specWeapon); - if (weapon != null) - { - //System.out.println("found weapon " + weapon.getName()); - final Option option = weapon.getInteraction().get(1); - if (option == null) - { - System.out.println("Option not found"); - return; - } - weapon.getInteraction().handleItemOption(this, option, this.getInventory()); - this.getSettings().setSpecialToggled(true); - this.attack(target); - } - if (!this.getSettings().isSpecialToggled()) - { - //System.out.println("toggle special"); - this.getSettings().setSpecialToggled(true); - this.attack(target); - } - } - else - { - //System.out.println("try normal again"); - Item weapon = this.getItemById(normalWeapon); - if (weapon != null) - { - //System.out.println("found weapon " + weapon.getName()); - final Option option = weapon.getInteraction().get(1); - if (option == null) - { - System.out.println("Option not found"); - return; - } - weapon.getInteraction().handleItemOption(this, option, this.getInventory()); - this.getSettings().setSpecialToggled(false); - this.attack(target); - } - } - } - - private void checkBarrowsSwitch() - { - System.out.println("WildernessBot: " + this.getSkills().getLifepoints()); - Entity target = getTarget(); - if (target == null) - return; - - if (this.getSkills().getLifepoints() <= 35 && target.isPlayer() && target.getSkills().getLifepoints() <= 60) - { - this.getEquipment().replace(new Item(4720) ,EquipmentContainer.SLOT_CHEST); - this.getEquipment().replace(new Item(4722) ,EquipmentContainer.SLOT_LEGS); - this.getEquipment().replace(new Item(4716) ,EquipmentContainer.SLOT_HAT); - this.getEquipment().replace(new Item(4719) ,EquipmentContainer.SLOT_WEAPON); - this.getEquipment().replace(new Item(-1) ,EquipmentContainer.SLOT_SHIELD); - riskIt = true; - } - else if (riskIt) - { - this.getEquipment().replace(chest, EquipmentContainer.SLOT_CHEST); - this.getEquipment().replace(legs, EquipmentContainer.SLOT_LEGS); - this.getEquipment().replace(helmet, EquipmentContainer.SLOT_HAT); - this.getEquipment().replace(weapon, EquipmentContainer.SLOT_WEAPON); - this.getEquipment().replace(shield, EquipmentContainer.SLOT_SHIELD); - riskIt = false; - } - } - - private void checkPrayer() - { - if (this.inCombat()) - { - if (!(this.getPrayer().getActive().contains(PrayerType.PIETY))) - this.getPrayer().toggle((PrayerType.PIETY)); - } - } - - @Override - public void tick() - { - super.tick(); - //checkBarrowsSwitch(); - checkSpecialAttack(); - checkPrayer(); - //Despawn - if (this.getSkills().getLifepoints() == 0) - //this.teleport(new Location(500, 500)); - //Despawning not being delayed causes 3 errors in the console - AIPlayer.deregister(this.getUid()); - if (agressiveTimer != 0) - { - agressiveTimer--; - } - - //Combat - if (!fleeing) - { - if (tick == 0) - { - if (!this.inCombat()) - AttackPlayersInRadius(this, 5); - this.tick = 10; - } - else - this.tick--; - - if (!this.inCombat()) - { - if (movetimer == 0) - { - //if (this.FindTargets(this, 5) == null) - this.randomWalk(20, 20); - this.movetimer = 10; - } - else - movetimer --; - } - } - this.sendChat("Tik"); - this.checkFlee(385); - this.eat(385); - //this.getPrayer().toggle() - } - - public void checkFlee(int foodId) - { - if (this.getInventory().contains(foodId, 18) == false) - { - //todo: add tp - // if () - this.getProperties().setRetaliating(false); - this.sendChat("Oi"); - walkPos(this.getLocation().getLocalX(), this.getLocation().getLocalY() - 5 - RandomFunction.getRandom(5)); - fleeing = true; - } - } - - public void eat(int foodId) - { - if (riskIt) - return; - - Item foodItem = new Item(foodId); - - if (!(this.getInventory().contains(foodId, 1))) - return; - - if((this.getSkills().getStaticLevel(Skills.HITPOINTS) >= this.getSkills().getLifepoints() * 3) && this.getInventory().containsItem(foodItem)) - { - this.lock(3); - //this.animate(new Animation(829)); - Item food = this.getInventory().getItem(foodItem); - - Consumable consumable = Consumables.getConsumableById(food.getId()); - - if (consumable == null) { - consumable = new Food(new int[] {food.getId()}, new HealingEffect(1)); - } - - consumable .consume(food, this); - this.getProperties().getCombatPulse().delayNextAttack(3); - } - } - - public void setMainWeapon(int weapon) - { - this.normalWeapon = weapon; - } -} diff --git a/Server/src/main/kotlin/api/Container.kt b/Server/src/main/kotlin/api/Container.kt deleted file mode 100644 index 83b92e3a3..000000000 --- a/Server/src/main/kotlin/api/Container.kt +++ /dev/null @@ -1,7 +0,0 @@ -package api - -enum class Container { - INVENTORY, - BANK, - EQUIPMENT -} \ No newline at end of file diff --git a/Server/src/main/kotlin/api/ContentAPI.kt b/Server/src/main/kotlin/api/ContentAPI.kt deleted file mode 100644 index 8578efe85..000000000 --- a/Server/src/main/kotlin/api/ContentAPI.kt +++ /dev/null @@ -1,1267 +0,0 @@ -package api - -import core.cache.def.impl.ItemDefinition -import core.cache.def.impl.SceneryDefinition -import core.game.component.Component -import core.game.content.dialogue.FacialExpression -import core.game.content.global.action.SpecialLadders -import core.game.node.Node -import core.game.node.scenery.Scenery -import core.game.node.scenery.SceneryBuilder -import core.game.node.entity.Entity -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.link.TeleportManager -import core.game.node.entity.player.link.audio.Audio -import core.game.node.entity.player.link.emote.Emotes -import core.game.node.entity.skill.gather.SkillingTool -import core.game.node.item.GroundItem -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.game.world.map.Direction -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.map.RegionManager.getRegionChunk -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.update.flag.chunk.AnimateObjectUpdateFlag -import core.game.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics -import rs09.game.content.dialogue.DialogueFile -import rs09.game.content.global.GlobalKillCounter; -import rs09.game.system.SystemLogger -import rs09.game.system.config.ItemConfigParser; -import rs09.game.world.GameWorld -import rs09.game.world.GameWorld.Pulser -import rs09.game.world.repository.Repository - -object ContentAPI { - /** - * Gets a skilling tool which the player has the level to use and is in their inventory. - * @param player the player to get the tool for - * @param pickaxe whether or not we are trying to get a pickaxe. - * @return the tool which meets the requirements or null if none. - */ - @JvmStatic - fun getTool(player: Player, pickaxe: Boolean): SkillingTool? { - return if(pickaxe) SkillingTool.getPickaxe(player) else SkillingTool.getHatchet(player) - } - - /** - * Check if a player has a given level (buffable/debuffable) - * @param player the player to check the stat for - * @param skill the Skill to check. There is an enum for this called Skills. Example: Skills.STRENGTH - * @param level the level to check against - * @return true if the check succeeds, false otherwise - */ - @JvmStatic - fun hasLevelDyn(player: Player, skill: Int, level: Int): Boolean { - return player.skills.getLevel(skill) >= level - } - - /** - * Check if a player's static skill level meets a certain value - * @param player the player to check the stat for - * @param skill the Skill to check. There is an enum for this called Skills. Example: Skills.STRENGTH - * @param level the level to check against - * @return true if the check succeeds, false otherwise - */ - @JvmStatic - 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. - */ - @JvmStatic - 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 - * @param id the ID of the item to check for the amount of - * @return the amount of the given ID in the player's inventory - */ - @JvmStatic - fun amountInInventory(player: Player, id: Int): Int{ - return player.inventory.getAmount(id) - } - - /** - * 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 - * @return the amount of the ID in the player's bank. - */ - @JvmStatic - fun amountInBank(player: Player, id: Int): Int{ - return player.bank.getAmount(id) - } - - /** - * Check the amount of a given item in the player's equipment slots - * @param player the player to check - * @param id the ID of the item to check for - * @return the amount of the ID in the player's equipment. - */ - @JvmStatic - fun amountInEquipment(player: Player, id: Int): Int{ - return player.equipment.getAmount(id) - } - - /** - * Remove an item from a player's inventory - * @param player the player whose inventory to remove the item from - * @param item the ID or Item object to remove from the player's inventory - * @param container the Container to remove the items from. An enum exists for this in the api package called Container. Ex: api.Container.BANK - */ - @JvmStatic - fun removeItem(player: Player, item: T, container: Container): Boolean { - item ?: return false - val it = when (item) { - is Item -> item - is Int -> Item(item) - else -> throw IllegalStateException("Invalid value passed for item") - } - - return when(container){ - Container.INVENTORY -> player.inventory.remove(it) - Container.BANK -> player.bank.remove(it) - Container.EQUIPMENT -> player.equipment.remove(it) - } - } - - /** - * Add an item to a player's inventory - * @param player the player whose inventory to add an item to - * @param id the ID of the item to add - * @param amount the amount of the item to add, defaults to 1 - * @return true if the item exists in the given amount in the player's inventory - */ - @JvmStatic - fun addItem(player: Player, id: Int, amount: Int = 1): Boolean{ - return player.inventory.add(Item(id,amount)) - } - - /** - * 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 - * @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 - */ - @JvmStatic - fun addItemOrDrop(player: Player, id: Int, amount: Int = 1){ - val item = Item(id, amount) - if(!player.inventory.add(item)) GroundItemManager.create(item,player) - } - - /** - * Clears an NPC with the "poof" smoke graphics commonly seen with random event NPCs. - * @param npc the NPC object to initialize - */ - @JvmStatic - fun poofClear(npc: NPC){ - submitWorldPulse(object : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 2 -> { - npc.isInvisible = true; Graphics.send(Graphics(86), npc.location) - } - 3 -> npc.clear().also { return true } - } - return false - } - }) - } - - /** - * 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 - */ - @JvmStatic - fun inBank(player: Player, item: Int, amount: Int = 1): Boolean { - return player.bank.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 - */ - @JvmStatic - 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 - * @return the number of free slots in the player's inventory - */ - @JvmStatic - fun freeSlots(player: Player): Int { - return player.inventory.freeSlots() - } - - /** - * Get an animation by ID. - * @param id the ID of the animation to use - * @return an Animation object with the given ID. - */ - @JvmStatic - fun getAnimation(id: Int): Animation { - return Animation(id) - } - - /** - * Get an animation by ID with priority - * @param id the ID of the animation to get - * @param priority the Animator.Priority enum instance to represent the desired priority - * @return an Animation object with the given ID and priority - */ - @JvmStatic - fun getAnimationWithPriority(id: Int, priority: Animator.Priority): Animation { - return Animation(id, Animator.Priority.values()[priority.ordinal]) - } - - /** - * Reset a player's animator - * @param player the player whose animator to reset - */ - @JvmStatic - fun resetAnimator(player: Player) { - player.animator.animate(Animation(-1, Animator.Priority.VERY_HIGH)) - } - - /** - * Get the number of ticks an animation lasts - * @param animation the Animation object to check the duration of - * @return the number of ticks the given animation lasts for - */ - @JvmStatic - fun animationDuration(animation: Animation): Int { - return animation.definition.durationTicks - } - - /** - * Give a player some amount of experience in a specific skill - * @param player the player to award XP to - * @param skill the Skill ID to reward XP for. There is a Skills enum you can use for this. Example: Skills.STRENGTH - * @param amount the amount, including decimal place, of experience to award - */ - @JvmStatic - fun rewardXP(player: Player, skill: Int, amount: Double) { - player.skills.addExperience(skill, amount) - } - - /** - * 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 loc the location to move the new object to if necessary. Defaults to null. - */ - @JvmStatic - fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, loc: Location? = null) { - val newLoc = when(loc){ - null -> toReplace.location - else -> loc - } - if (for_ticks == -1) { - SceneryBuilder.replace(toReplace, toReplace.transform(with,toReplace.rotation,newLoc)) - } else { - SceneryBuilder.replace(toReplace, toReplace.transform(with,toReplace.rotation, newLoc), for_ticks) - } - toReplace.isActive = false - } - - /** - * 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 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. - */ - @JvmStatic - fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, rotation: Direction, loc: Location? = null){ - val newLoc = when(loc){ - null -> toReplace.location - else -> loc - } - val rot = when(rotation){ - Direction.NORTH_WEST -> 0 - Direction.NORTH -> 1 - Direction.NORTH_EAST -> 2 - Direction.EAST -> 4 - Direction.SOUTH_EAST -> 7 - Direction.SOUTH -> 6 - Direction.SOUTH_WEST -> 5 - Direction.WEST -> 3 - } - if (for_ticks == -1) { - SceneryBuilder.replace(toReplace, toReplace.transform(with,rot, newLoc)) - } else { - SceneryBuilder.replace(toReplace, toReplace.transform(with,rot,newLoc), for_ticks) - } - toReplace.isActive = false - } - - /** - * Gets the name of an item. - * @param id the ID of the item to get the name of - * @return the name of the item - */ - @JvmStatic - fun getItemName(id: Int): String { - return ItemDefinition.forId(id).name - } - - /** - * Removes a ground item - * @param node the GroundItem object to remove - */ - @JvmStatic - fun removeGroundItem(node: GroundItem) { - GroundItemManager.destroy(node) - } - - /** - * Checks if a ground item is valid/still exists/should exist - * @param node the GroundItem object to check the validity of - * @return true if the node is valid, false otherwise - */ - @JvmStatic - fun isValidGroundItem(node: GroundItem): Boolean { - return GroundItemManager.getItems().contains(node) - } - - /** - * Checks if a player has space for an item - * @param player the player whose inventory to check - * @param item the Item to check against - * @return true if the player's inventory has space for the item - */ - @JvmStatic - fun hasSpaceFor(player: Player, item: Item): Boolean { - return player.inventory.hasSpaceFor(item) - } - - /** - * Get the number of ticks passed since server startup - */ - @JvmStatic - fun getWorldTicks(): Int { - return GameWorld.ticks - } - - /** - * Gets an Audio object with specified id, volume, etc - */ - @JvmStatic - fun getAudio(id: Int, volume: Int = 10, delay: Int = 1): Audio { - return Audio(id, volume, delay) - } - - /** - * Impact an enemy with the given amount of damage and the given hitsplat type - * @param entity the entity to damage - * @param amount the amount of damage to deal - * @param type the type of hit splat to use, ImpactHandler.HitsplatType is an enum containing these options. - */ - @JvmStatic - fun impact(entity: Entity, amount: Int, type: ImpactHandler.HitsplatType) { - entity.impactHandler.manualHit(entity, amount, type) - } - - /** - * Get an item's definition - * @param id the ID of the item to get the definition of - * @return the ItemDefinition for the given ID. - */ - @JvmStatic - fun itemDefinition(id: Int): ItemDefinition { - return ItemDefinition.forId(id) - } - - /** - * Send an object animation - */ - @JvmStatic - fun animateScenery(player: Player, obj: Scenery, animationId: Int, global: Boolean = false) { - player.packetDispatch.sendSceneryAnimation(obj, getAnimation(animationId), global) - } - - /** - * Send an object animation independent of a player - */ - @JvmStatic - fun animateScenery(obj: Scenery, animationId: Int){ - val animation = Animation(animationId) - animation.setObject(obj) - getRegionChunk(obj.location).flag(AnimateObjectUpdateFlag(animation)) - } - - /** - * Produce a ground item owned by the player - */ - @JvmStatic - fun produceGroundItem(player: Player, item: Int) { - GroundItemManager.create(Item(item), player) - } - - /** - * Spawns a projectile - */ - @JvmStatic - fun spawnProjectile(source: Entity, dest: Entity, projectileId: Int) { - Projectile.create(source, dest, projectileId).send() - } - - /** - * Spawns a projectile with more advanced parameters - * @param source the initial Location of the projectile - * @param dest the final Location of the projectile - * @param projectile the ID of the gfx used for the projectile - * @param startHeight the height the projectile spawns at - * @param endHeight the height the projectile ends at - * @param delay the delay before the projectile spawns - * @param speed the speed the projectile travels at - * @param angle the angle the projectile travels at - */ - @JvmStatic - fun spawnProjectile(source: Location, dest: Location, projectile: Int, startHeight: Int, endHeight: Int, delay: Int, speed: Int, angle: Int){ - Projectile.create(source, dest, projectile, startHeight, endHeight, delay, speed, angle, source.getDistance(dest).toInt()).send() - } - - /** - * Causes the given entity to face the given toFace - * @param entity the entity you wish to face something - * @param toFace the thing to face - * @param duration how long you wish to face the thing for - */ - @JvmStatic - fun face(entity: Entity, toFace: Node, duration: Int = -1) { - if (duration == -1) { - when (toFace) { - is Location -> entity.faceLocation(toFace) - is Entity -> entity.face(toFace) - } - } else { - when (toFace) { - is Location -> entity.faceTemporary(toFace.asNpc(), duration) - else -> entity.faceTemporary(toFace as Entity, duration) - } - } - } - - /** - * Opens the given interface for the given player - * @param player the player to open the interface for - * @param id the ID of the interface to open - */ - @JvmStatic - fun openInterface(player: Player, id: Int) { - player.interfaceManager.open(Component(id)) - } - - /** - * Opens the given interface as an overlay for the given player - * @param player the player to open the interface for - * @param id the ID of the interface to open - */ - @JvmStatic - fun openOverlay(player: Player, id: Int){ - player.interfaceManager.openOverlay(Component(id)) - } - - /** - * Closes any open overlays for the given player - * @param player the player to close for - */ - @JvmStatic - fun closeOverlay(player: Player){ - player.interfaceManager.closeOverlay() - } - - /** - * Runs the given Emote for the given Entity - * @param entity the entity to run the emote on - * @param emote the Emotes enum entry to run - */ - @JvmStatic - fun emote(entity: Entity, emote: Emotes) { - entity.animate(emote.animation) - } - - /** - * Sends a message to the given player. - * @param player the player to send the message to. - */ - @JvmStatic - fun sendMessage(player: Player, message: String) { - player.sendMessage(message) - } - - /** - * Forces an above-head chat message for the given entity - * @param entity the entity to send the chat for - * @param message the message to display - */ - @JvmStatic - fun sendChat(entity: Entity, message: String) { - entity.sendChat(message) - } - - /** - * Sends a message to a player's dialogue box - * @param player the player to send the dialogue to - * @param message the message to send, lines are split automatically. - */ - @JvmStatic - fun sendDialogue(player: Player, message: String) { - player.dialogueInterpreter.sendDialogue(*DialUtils.splitLines(message)) - } - - /** - * Plays an animation on the entity - * @param entity the entity to animate - * @param anim the animation to play, can be an ID or an Animation object. - * @param forced whether or not to force the animation (usually not necessary) - */ - @JvmStatic - fun animate(entity: Entity, anim: T, forced: Boolean = false) { - val animation = when(anim){ - is Int -> Animation(anim) - is Animation -> anim - else -> throw IllegalStateException("Invalid value passed for anim") - } - - if (forced) { - entity.animator.forceAnimation(animation) - } else { - entity.animator.animate(animation) - } - } - - /** - * 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 - */ - @JvmStatic - fun playAudio(player: Player, audio: Audio, global: Boolean = false) { - player.audioManager.send(audio, global) - } - - /** - * Opens a dialogue with the given dialogue key or dialogue file, depending which is passed. - * @param player the player to open the dialogue for - * @param dialogue either the dialogue key or an instance of a DialogueFile - * @param args various args to pass to the opened dialogue - */ - @JvmStatic - fun openDialogue(player: Player, dialogue: Any, vararg args: Any) { - player.dialogueInterpreter.close() - when (dialogue) { - is Int -> player.dialogueInterpreter.open(dialogue, *args) - is DialogueFile -> player.dialogueInterpreter.open(dialogue, *args) - else -> SystemLogger.logErr("Invalid object type passed to openDialogue() -> ${dialogue.javaClass.simpleName}") - } - } - - /** - * Gets an NPC with the given ID from the repository. - * @param id the ID of the NPC to locate - * @returns an NPC instance matching the ID if it finds one, null otherwise - */ - @JvmStatic - fun findNPC(id: Int): NPC? { - return Repository.findNPC(id) - } - - /** - * Gets the spawned scenery from the world map using the given coordinates - * @param x the X coordinate to use - * @param y the Y coordinate to use - * @param z the Z coordinate to use - */ - @JvmStatic - fun getScenery(x: Int, y: Int, z: Int): Scenery?{ - return RegionManager.getObject(z,x,y) - } - - /** - * Gets the spawned scenery from the world map using the given Location object. - * @param loc the Location object to use. - */ - @JvmStatic - fun getScenery(loc: Location): Scenery?{ - return RegionManager.getObject(loc) - } - - /** - * Gets an NPC within render distance of the refLoc that matches the given ID - * @param refLoc the Location to find the closes NPC to - * @param id the ID of the NPC to locate - * @returns an NPC instance matching the ID if it finds one, null otherwise - */ - @JvmStatic - fun findNPC(refLoc: Location, id: Int): NPC? { - return Repository.npcs.firstOrNull { it.id == id && it.location.withinDistance(refLoc) } - } - - /** - * Gets an NPC with the given ID in the same general area as the given Entity - * @param entity the entity to search around - * @param id the ID of the NPC to locate - * @returns an NPC matching the given ID or null if none is found - */ - @JvmStatic - fun findLocalNPC(entity: Entity, id: Int): NPC? { - return RegionManager.getLocalNpcs(entity).firstOrNull { it.id == id } - } - - /** - * Gets a list of nearby NPCs that match the given IDs. - * @param entity the entity to check around - * @param ids the IDs of the NPCs to look for - */ - @JvmStatic - fun findLocalNPCs(entity: Entity, ids: IntArray): List{ - return RegionManager.getLocalNpcs(entity).filter { it.id in ids }.toList() - } - - /** - * Gets a list of nearby NPCs that match the given IDs. - * @param entity the entity to check around - * @param ids the IDs of the NPCs to look for - * @param distance The maximum distance to the entity. - */ - @JvmStatic - fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List{ - return RegionManager.getLocalNpcs(entity, distance).filter { it.id in ids }.toList() - } - - /** - * Gets the value of an attribute key from the Entity's attributes store - * @param entity the entity to get the attribute from - * @param attribute the attribute key to use - * @param default the default value to return if the attribute does not exist - */ - @JvmStatic - fun getAttribute(entity: Entity, attribute: String, default: T): T { - return entity.getAttribute(attribute, default) - } - - /** - * Sets an attribute key to the given value in an Entity's attribute store - * @param entity the entity to set the attribute for - * @param attribute the attribute key to use - * @param value the value to set the attribute to - */ - @JvmStatic - fun setAttribute(entity: Entity, attribute: String, value: T) { - entity.setAttribute(attribute, value) - } - - /** - * Locks the given entity for the given number of ticks - * @param entity the entity to lock - * @param duration the number of ticks to lock for - */ - @JvmStatic - fun lock(entity: Entity, duration: Int) { - entity.lock(duration) - } - - /** - * Locks specifically an entity's interactions, allowing movement still - * @param entity the entity to lock - * @param duration the duration in ticks to lock for - */ - @JvmStatic - fun lockInteractions(entity: Entity, duration: Int) { - entity.locks.lockInteractions(duration) - } - - /** - * Unlocks the given entity - * @param entity the entity to unlock - */ - @JvmStatic - fun unlock(entity: Entity) { - entity.unlock() - } - - /** - * Transforms an NPC for the given number of ticks - * @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 - */ - @JvmStatic - fun transformNpc(npc: NPC, transformTo: Int, restoreTicks: Int){ - npc.transform(transformTo) - Pulser.submit(object : Pulse(restoreTicks){ - override fun pulse(): Boolean { - npc.reTransform() - return true - } - }) - } - - /** - * Produces a Location object using the given x,y,z values - */ - @JvmStatic - fun location(x: Int, y: Int, z: Int): Location{ - return Location.create(x,y,z) - } - - /** - * AHeals the given entity for the given number of hitpoints - */ - @JvmStatic - 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 - */ - @JvmStatic - fun setVarbit(player: Player, varpIndex: Int, offset: Int, value: Int){ - player.varpManager.get(varpIndex).setVarbit(offset,value).send(player) - } - - /** - * Clears all bits for a given varp index - * @param player the player to clear for - * @param varpIndex the index of the varp to clear - */ - @JvmStatic - fun clearVarp(player: Player, varpIndex: Int){ - player.varpManager.get(varpIndex).clear() - } - - /** - * 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 - */ - @JvmStatic - 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 - */ - @JvmStatic - fun getVarbitValue(player: Player, varpIndex: Int, offset: Int): Int { - return player.varpManager.get(varpIndex).getVarbitValue(offset) - } - - /** - * Force an entity to walk to a given destination. - * @param entity the entity to forcewalk - * @param dest the Location object to walk to - * @param type the type of pathfinder to use. "smart" for the SMART pathfinder, "clip" for the noclip pathfinder, anything else for DUMB. - */ - @JvmStatic - fun forceWalk(entity: Entity, dest: Location, type: String){ - if(type == "clip"){ - ForceMovement(entity, dest, 10, 10).run() - return - } - val pathfinder = when(type){ - "smart" -> Pathfinder.SMART - else -> Pathfinder.DUMB - } - val path = Pathfinder.find(entity, dest, true, pathfinder) - path.walk(entity) - } - - /** - * Interrupts a given entity's walking queue - * @param entity the entity to interrupt - */ - @JvmStatic - 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. - */ - @JvmStatic - fun getItemFromEquipment(player: Player, slot: EquipmentSlot): Item? { - return player.equipment.get(slot.ordinal) - } - - /** - * Adjusts the charge for the given node. - * @param node the node to adjust the charge of - * @param amount the amount to adjust by - */ - @JvmStatic - fun adjustCharge(node: Node, amount: Int){ - when(node){ - is Item -> node.charge += amount - is Scenery -> node.charge += amount - else -> SystemLogger.logErr("Attempt to adjust the charge of invalid type: ${node.javaClass.simpleName}") - } - } - - /** - * Get the current charge of the given node - * @param node the node whose charge to check - * @return amount of charges the node has, or -1 if the node does not accept charges. - */ - @JvmStatic - fun getCharge(node: Node): Int{ - when(node){ - is Item -> return node.charge - is Scenery -> return node.charge - else -> SystemLogger.logErr("Attempt to get charge of invalid type: ${node.javaClass.simpleName}").also { return -1 } - } - } - - /** - * Set the charge of the given node to the given amount. - * @param node the node to set the charge for - * @param charge the amount to set the node's charge to (default is 1000) - */ - @JvmStatic - fun setCharge(node: Node, charge: Int){ - when(node){ - is Item -> node.charge = charge - is Scenery -> node.charge = charge - else -> SystemLogger.logErr("Attempt to set the charge of invalid type: ${node.javaClass.simpleName}") - } - } - - /** - * Gets the used option in the context of an interaction. - * @param player the player to get the used option for. - * @return the option the player used - */ - @JvmStatic - fun getUsedOption(player: Player): String { - return player.getAttribute("interact:option","INVALID") - } - - /** - * Used to play both an Animation and Graphics object simultaneously. - * @param entity the entity to perform this on - * @param anim the Animation object to use, can also be an ID. - * @param gfx the Graphics object to use, can also be an ID. - */ - @JvmStatic - fun visualize(entity: Entity, anim: A, gfx: G){ - val animation = when(anim){ - is Int -> Animation(anim) - is Animation -> anim - else -> throw IllegalStateException("Invalid parameter passed for animation.") - } - - val graphics = when(gfx){ - is Int -> Graphics(gfx) - is Graphics -> gfx - else -> throw IllegalStateException("Invalid parameter passed for graphics.") - } - - entity.visualize(animation,graphics) - } - - /** - * Used to submit a pulse to the GameWorld's Pulser. - * @param pulse the Pulse object to submit - */ - @JvmStatic - fun submitWorldPulse(pulse: Pulse){ - GameWorld.Pulser.submit(pulse) - } - - /** - * Teleports or "instantly moves" an entity to a given Location object. - * @param entity the entity to move - * @param loc the Location object to move them to - * @param type the teleport type to use (defaults to instant). An enum exists as TeleportManager.TeleportType. - */ - @JvmStatic - fun teleport(entity: Entity, loc: Location, type: TeleportManager.TeleportType = TeleportManager.TeleportType.INSTANT){ - if(type == TeleportManager.TeleportType.INSTANT) entity.properties.teleportLocation = loc - else entity.teleporter.send(loc,type) - } - - /** - * Sets the dynamic or "temporary" (restores) level of a skill. - * @param entity the entity to set the level for - * @param skill the Skill to set. A Skills enum exists that can be used. Ex: Skills.STRENGTH - * @param level the level to set the skill to - */ - @JvmStatic - fun setTempLevel(entity: Entity, skill: Int, level: Int){ - entity.skills.setLevel(skill, level) - } - - /** - * Gets the static (unchanging/max) level of an entity's skill - * @param entity the entity to get the level for - * @param skill the Skill to get the level of. A Skills enum exists that can be used. Ex: Skills.STRENGTH - * @return the static level of the skill - */ - @JvmStatic - fun getStatLevel(entity: Entity, skill: Int): Int { - return entity.skills.getStaticLevel(skill) - } - - /** - * Gets the dynamic (boostable/debuffable/restoring) level of an entity's skill - * @param entity the entity to get the level for - * @param skill the Skill to get the level of. A Skills enum exists that can be used. Ex: Skills.STRENGTH - * @return the dynamic level of the skill - */ - @JvmStatic - fun getDynLevel(entity: Entity, skill: Int): Int { - return entity.skills.getLevel(skill) - } - - /** - * Adjusts (buffs/debuffs) the given Skill by the amount given. - * @param entity the entity to adjust the skill for - * @param skill the Skill to adjust. A Skills enum exists that can be used. Ex: Skills.STRENGTH - * @param amount the amount to adjust the skill by. Ex-Buff: 5, Ex-Debuff: -5 - */ - @JvmStatic - fun adjustLevel(entity: Entity, skill: Int, amount: Int){ - entity.skills.setLevel(skill, entity.skills.getStaticLevel(skill) + amount) - } - - /** - * Remove all of a given item from the given container - * @param player the player to remove the item from - * @param item the item to remove. Can be an Item object or an ID. - * @param container the Container to remove the item from. An enum exists for this called Container. Ex: Container.BANK - */ - @JvmStatic - fun removeAll(player: Player, item: T, container: Container){ - val it = when(item){ - is Item -> item.id - is Int -> item - else -> throw IllegalStateException("Invalid value passed as item") - } - - when(container){ - Container.EQUIPMENT -> player.equipment.remove(Item(it, amountInEquipment(player, it))) - Container.BANK -> player.bank.remove(Item(it, amountInBank(player, it))) - Container.INVENTORY -> player.inventory.remove(Item(it, amountInInventory(player, it))) - } - } - - /** - * Sends a string to a specific interface child - * @param player the player to send the packet to - * @param string the string to send to the child - * @param iface the ID of the interface to use - * @param child the index of the child to send the string to - */ - @JvmStatic - fun setInterfaceText(player: Player, string: String, iface: Int, child: Int){ - player.packetDispatch.sendString(string,iface,child) - } - - /** - * Closes any open (non-chat) interfaces for the player - * @param player the player to close the interface for - */ - @JvmStatic - fun closeInterface(player: Player){ - player.interfaceManager.close() - } - - /** - * Closes any opened tab interfaces for the player - * @param player the player to close the tab for - */ - @JvmStatic - fun closeTabInterface(player: Player){ - player.interfaceManager.closeSingleTab() - } - - /** - * 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 - */ - @JvmStatic - fun sendPlayerDialogue(player: Player, msg: String, expr: FacialExpression = FacialExpression.FRIENDLY){ - player.dialogueInterpreter.sendDialogues(player, expr, *DialUtils.splitLines(msg)) - } - - /** - * Sends a player model to a specific interface child - * @param player the player to send the packet to and whose model to use - * @param iface the ID of the interface to send it to - * @param child the index of the child on the interface to send the model to - */ - @JvmStatic - fun sendPlayerOnInterface(player: Player, iface: Int, child: Int){ - player.packetDispatch.sendPlayerOnInterface(iface,child) - } - - /** - * Sends a dialogue that uses the player'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 - */ - @JvmStatic - fun sendNPCDialogue(player: Player, npc: Int, msg: String, expr: FacialExpression = FacialExpression.FRIENDLY){ - player.dialogueInterpreter.sendDialogues(npc, expr, *DialUtils.splitLines(msg)) - } - - /** - * Sends an animation to a specific interface child - * @param player the player to send the packet to - * @param anim the ID of the animation to send to the interface - * @param iface the ID of the interface to send the animation to - * @param child the index of the child on the interface to send the model to - */ - @JvmStatic - fun sendAnimationOnInterface(player: Player, anim: Int, iface: Int, child: Int){ - player.packetDispatch.sendAnimationInterface(anim,iface,child) - } - - /** - * Register a logout listener to a player. Logout listeners are methods that run when a player logs out. - * @param player the player to register the listener for - * @param handler the method to run when the listener is invoked (when the player logs out) - */ - @JvmStatic - fun registerLogoutListener(player: Player, key: String, handler: (p: Player) -> Unit){ - player.logoutListeners[key] = handler - } - - /** - * Removes a logout listener based on the key from a player - * @param player the player to remove the logout listner from - * @param key the key of the logout listener to remove. - */ - @JvmStatic - fun clearLogoutListener(player: Player, key: String){ - player.logoutListeners.remove(key) - } - - /** - * Sends an 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 amount the amount of the item to send - defaults to 1 - */ - @JvmStatic - fun sendItemOnInterface(player: Player, iface: Int, child: Int, item: Int, amount: Int = 1){ - player.packetDispatch.sendItemOnInterface(item,amount,iface,child) - } - - /** - * Sends a dialogue box with a single item and some text - * @param player the player to send it to - * @param item the ID of the item to show - * @param message the text to display - */ - @JvmStatic - fun sendItemDialogue(player: Player, item: Int, message: String){ - player.dialogueInterpreter.sendItemMessage(item, *DialUtils.splitLines(message)) - } - - /** - * Sends a dialogue box with two items and some text - * @param player the player to send it to - * @param item1 the ID of the first item to show - * @param item2 the ID of the second item to show - * @param message the text to display - */ - @JvmStatic - fun sendDoubleItemDialogue(player: Player, item1: Int, item2: Int, message: String){ - player.dialogueInterpreter.sendDoubleItemMessage(item1, item2, message) - } - - /** - * Send an input dialogue to retrieve a specified value from the player - * @param player the player to send the input dialogue to - * @param numeric whether or not the input is numeric - * @param prompt what to prompt the player - * @param handler the method that handles the value gained from the input dialogue - */ - @JvmStatic - fun sendInputDialogue(player: Player, numeric: Boolean, prompt: String, handler: (value: Any) -> Unit){ - if(numeric) sendInputDialogue(player, InputType.NUMERIC, prompt, handler) - else sendInputDialogue(player, InputType.STRING_SHORT, prompt, handler) - } - - /** - * Send input dialogues based on type. Some dialogues are special and can't be covered by the other sendInputDialogue method - * @param player the player to send the input dialogue to - * @param type the input type to send - an enum is available for this called InputType - * @param prompt what to prompt the player - * @param handler the method that handles the value from the input dialogue - */ - @JvmStatic - fun sendInputDialogue(player: Player, type: InputType, prompt: String, handler: (value: Any) -> Unit){ - when(type){ - InputType.NUMERIC, InputType.STRING_SHORT -> player.dialogueInterpreter.sendInput(type != InputType.NUMERIC, prompt) - InputType.STRING_LONG -> player.dialogueInterpreter.sendLongInput(prompt) - InputType.MESSAGE -> player.dialogueInterpreter.sendMessageInput(prompt) - } - player.setAttribute("runscript", handler) - } - - /** - * Forces an NPC to "flee" from a player or other entity - * @param entity the entity to make flee - * @param from the entity to flee from - */ - @JvmStatic - fun flee(entity: Entity, from: Entity){ - lock(entity, 5) - face(entity, from, 5) - - val diffX = entity.location.x - from.location.x - val diffY = entity.location.y - from.location.y - - forceWalk(entity, entity.location.transform(diffX,diffY,0), "DUMB") - } - - /** - * Submits an individual or "weak" pulse to a specific entity's pulse manager. Pulses submitted this way can be overridden by other pulses. - * @param entity the entity to submit the pulse to - * @param pulse the pulse to submit - */ - @JvmStatic - fun submitIndividualPulse(entity: Entity, pulse: Pulse){ - entity.pulseManager.run(pulse) - } - - /** - * Gets the number of QP a player has - * @param player the player to get the QP for - * @return the number of QP the player has - */ - @JvmStatic - fun getQP(player: Player): Int{ - return player.questRepository.points - } - - /** - * Gets a scenery definition from the given ID - * @param id the ID of the scenery to get the definition for. - * @return the scenery definition - */ - @JvmStatic - fun sceneryDefinition(id: Int): SceneryDefinition{ - return SceneryDefinition.forId(id) - } - - /** - * Register a map zone - * @param zone the zone to register - * @param borders the ZoneBorders that compose the zone - */ - @JvmStatic - fun registerMapZone(zone: MapZone, borders: ZoneBorders){ - ZoneBuilder.configure(zone) - zone.register(borders) - } - - /** - * Animates a component of an interface. - * @param player the player to animate the interface for. - * @param iface the ID of the interface to animate. - * @param child the child on the interface to animate. - * @param anim the ID of the animation to use. - */ - @JvmStatic - fun animateInterface(player: Player, iface: Int, child: Int, anim: Int){ - player.packetDispatch.sendAnimationInterface(anim,iface,child) - } - - /** - * Adds a climb destination to the ladder handler. - * @param ladderLoc the location of the ladder/stairs object you want to climb. - * @param dest the destination for the climb. - */ - @JvmStatic - fun addClimbDest(ladderLoc: Location, dest: Location){ - SpecialLadders.add(ladderLoc,dest) - } - - /** - * Sends a news announcement in game chat. - * @param message the message to announce - */ - @JvmStatic - fun sendNews(message: String){ - Repository.sendNews(message, 12, "CC6600") - } - - /** - * Sends a given Graphics object, or graphics ID, to the given location. - * @param gfx the Graphics object, or the Integer ID of the graphics, to send. Either works. - * @param location the location to send it to - */ - @JvmStatic - fun sendGraphics(gfx: G, location: Location){ - when(gfx){ - is Int -> Graphics.send(Graphics(gfx),location) - is Graphics -> Graphics.send(gfx, location) - } - } - - @JvmStatic - fun announceIfRare(player: Player, item: Item) { - if (item.definition.getConfiguration(ItemConfigParser.RARE_ITEM, false)) { - ContentAPI.sendNews("${player.username} has just received: ${item.amount} x ${item.name}."); - GlobalKillCounter.incrementRareDrop(player, item); - } - } -} diff --git a/Server/src/main/kotlin/api/DialUtils.kt b/Server/src/main/kotlin/api/DialUtils.kt deleted file mode 100644 index a0db709e3..000000000 --- a/Server/src/main/kotlin/api/DialUtils.kt +++ /dev/null @@ -1,54 +0,0 @@ -package api -import rs09.game.system.SystemLogger -import java.util.* -import kotlin.system.exitProcess - -object DialUtils { - - //Number of chars per line. Line is split at the nearest whole word that doesn't exceed this length. This should remain constant once a perfect value is found - const val LINE_SIZE = 52.0 - - @JvmStatic - /** - * 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): Array{ - val chars = message.split("").toTypedArray().size.toDouble() - val words = message.split(" ").toTypedArray() - val lines = Math.ceil(chars / LINE_SIZE).toInt() - - if(lines > 4){ - SystemLogger.logErr("INVALID NUM LINES. NUM CHARS: $chars") - } - - val messages = ArrayList() - - var index = 0 - - for (msg in 0 until lines) { - var length = 0 - val line = StringBuilder() - while (length <= LINE_SIZE && !(index == words.size)) { - var currentWord = "" - try { - currentWord = words[index] + if (length + words[index].length + 1 <= LINE_SIZE) " " else "" - } catch (e: Exception){ - SystemLogger.logWarn("INDEX: $index WORDSIZE: ${words.size}") - for(word in words.indices){ - SystemLogger.logErr("Word $word: ${words[word]}") - } - exitProcess(0) - } - length += currentWord.length - if (length <= LINE_SIZE) { - line.append(currentWord) - ++index - } - } - messages.add(line.toString()) - } - - return messages.toTypedArray() - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/api/EquipmentSlot.kt b/Server/src/main/kotlin/api/EquipmentSlot.kt deleted file mode 100644 index 15dea2bfc..000000000 --- a/Server/src/main/kotlin/api/EquipmentSlot.kt +++ /dev/null @@ -1,18 +0,0 @@ -package api - -enum class EquipmentSlot { - HAT, - CAPE, - AMULET, - WEAPON, - CHEST, - SHIELD, - HIDDEN_1, - LEGS, - HIDDEN_2, - HANDS, - FEET, - HIDDEN_3, - RING, - AMMO -} \ No newline at end of file diff --git a/Server/src/main/kotlin/api/InputType.kt b/Server/src/main/kotlin/api/InputType.kt deleted file mode 100644 index 139060cfc..000000000 --- a/Server/src/main/kotlin/api/InputType.kt +++ /dev/null @@ -1,8 +0,0 @@ -package api - -enum class InputType { - NUMERIC, - STRING_SHORT, - STRING_LONG, - MESSAGE -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/Server.kt b/Server/src/main/kotlin/rs09/Server.kt deleted file mode 100644 index 075335240..000000000 --- a/Server/src/main/kotlin/rs09/Server.kt +++ /dev/null @@ -1,172 +0,0 @@ -package rs09 - -import core.game.system.SystemManager -import core.game.system.SystemState -import core.game.system.mysql.SQLManager -import core.gui.ConsoleFrame -import core.net.NioReactor -import core.net.amsc.WorldCommunicator -import core.tools.TimeStamp -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import rs09.game.content.global.GlobalKillCounter; -import rs09.game.ge.GEAutoStock -import rs09.game.system.SystemLogger -import rs09.game.system.config.ServerConfigParser -import rs09.game.world.GameWorld -import rs09.game.world.repository.Repository -import java.io.File -import java.io.FileWriter -import java.lang.management.ManagementFactory -import java.lang.management.ThreadMXBean -import java.net.BindException -import java.util.* -import kotlin.system.exitProcess - - -/** - * The main class, for those that are unable to read the class' name. - * @author Emperor - * @author Vexia - * @author Ceikry - */ -object Server { - /** - * The time stamp of when the server started running. - */ - @JvmField - var startTime: Long = 0 - - var lastHeartbeat = System.currentTimeMillis() - - @JvmStatic - var running = false - - /** - * The NIO reactor. - */ - @JvmStatic - var reactor: NioReactor? = null - - /** - * The main method, in this method we load background utilities such as - * cache and our world, then end with starting networking. - * @param args The arguments cast on runtime. - * @throws Throwable When an exception occurs. - */ - @Throws(Throwable::class) - @JvmStatic - fun main(args: Array) { - if (args.isNotEmpty()) { - SystemLogger.logInfo("Using config file: ${args[0]}") - ServerConfigParser(args[0]) - } else { - SystemLogger.logInfo("Using config file: ${"worldprops" + File.separator + "default.json"}") - ServerConfigParser("worldprops" + File.separator + "default.json") - } - if (GameWorld.settings?.isGui == true) { - try { - ConsoleFrame.getInstance().init() - } catch (e: Exception) { - SystemLogger.logWarn("X11 server missing - launching server with no GUI!") - } - } - startTime = System.currentTimeMillis() - val t = TimeStamp() - SystemLogger.logInfo("Initializing Server Store...") - GlobalKillCounter.init() - ServerStore.init() - SystemLogger.logInfo("Initialized ${ServerStore.counter} store files.") - GameWorld.prompt(true) - SQLManager.init() - Runtime.getRuntime().addShutdownHook(ServerConstants.SHUTDOWN_HOOK) - SystemLogger.logInfo("Starting networking...") - try { - reactor = NioReactor.configure(43594 + GameWorld.settings?.worldId!!) - reactor!!.start() - } catch (e: BindException) { - SystemLogger.logErr("Port " + (43594 + GameWorld.settings?.worldId!!) + " is already in use!") - throw e - } - WorldCommunicator.connect() - SystemLogger.logInfo(GameWorld.settings?.name + " flags " + GameWorld.settings?.toString()) - SystemLogger.logInfo(GameWorld.settings?.name + " started in " + t.duration(false, "") + " milliseconds.") - - GEAutoStock.autostock() - val scanner = Scanner(System.`in`) - running = true - GlobalScope.launch { - while(scanner.hasNextLine()){ - 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) - - } - } - } - GlobalScope.launch { - delay(20000) - while(running){ - if(System.currentTimeMillis() - lastHeartbeat > 1800 && running){ - SystemLogger.logErr("Triggering reboot due to heartbeat timeout") - SystemLogger.logErr("Creating thread dump...") - val dump = threadDump(true, true) - FileWriter("latestdump.txt").use { - it.write(dump) - it.flush() - it.close() - } - exitProcess(0) - } - delay(625) - } - } - } - - @JvmStatic - fun heartbeat() { - lastHeartbeat = System.currentTimeMillis() - } - - fun printCommands(){ - println("stop - stop the server (saves all accounts and such)") - println("players - show online player count") - println("update - initiate an update with a countdown visible to players") - println("help, commands - show this") - println("restartworker - Reboots the major update worker in case of a travesty.") - } - - fun autoReconnect() { - /*SystemLogger.log("Attempting autoreconnect of server") - WorldCommunicator.connect()*/ - } - /** - * Gets the startTime. - * @return the startTime - */ - fun getStartTime(): Long { - return startTime - } - - private fun threadDump(lockedMonitors: Boolean, lockedSynchronizers: Boolean): String? { - val threadDump = StringBuffer(System.lineSeparator()) - val threadMXBean: ThreadMXBean = ManagementFactory.getThreadMXBean() - for (threadInfo in threadMXBean.dumpAllThreads(lockedMonitors, lockedSynchronizers)) { - threadDump.append(threadInfo.toString()) - } - return threadDump.toString() - } - - /** - * Sets the bastartTime.ZZ - * @param startTime the startTime to set. - */ - fun setStartTime(startTime: Long) { - Server.startTime = startTime - } -} diff --git a/Server/src/main/kotlin/rs09/ServerConstants.kt b/Server/src/main/kotlin/rs09/ServerConstants.kt deleted file mode 100644 index 0c6109d1e..000000000 --- a/Server/src/main/kotlin/rs09/ServerConstants.kt +++ /dev/null @@ -1,229 +0,0 @@ -package rs09 - -import core.game.system.SystemShutdownHook -import core.game.world.map.Location -import core.tools.mysql.Database -import org.json.simple.JSONObject -import rs09.tools.secondsToTicks -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 - - @JvmField - var MS_SECRET_KEY = "" - - //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") - - @JvmField - var DAILY_RESTART = false - - /** - * 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 = rs09.JSONUtils.parseLocation(data["new_player_location"].toString()) - HOME_LOCATION = rs09.JSONUtils.parseLocation(data["home_location"].toString()) - - DATA_PATH = rs09.JSONUtils.parsePath(data["data_path"].toString()) - CACHE_PATH = rs09.JSONUtils.parsePath(data["cache_path"].toString()) - STORE_PATH = rs09.JSONUtils.parsePath(data["store_path"].toString()) - PLAYER_SAVE_PATH = rs09.JSONUtils.parsePath(data["save_path"].toString()) - CONFIG_PATH = rs09.JSONUtils.parsePath(data["configs_path"].toString()) - PLAYER_ATTRIBUTE_PATH = PLAYER_SAVE_PATH + "attributes" + File.separator - GRAND_EXCHANGE_DATA_PATH = rs09.JSONUtils.parsePath(data["grand_exchange_data_path"].toString()) - BOT_DATA_PATH = rs09.JSONUtils.parsePath(data["bot_data_path"].toString()) - RDT_DATA_PATH = rs09.JSONUtils.parsePath(data["rare_drop_table_path"].toString()) - OBJECT_PARSER_PATH = rs09.JSONUtils.parsePath(data["object_parser_path"].toString()) - SCRIPTS_PATH = rs09.JSONUtils.parsePath(data["scripts_path"].toString()) - DIALOGUE_SCRIPTS_PATH = rs09.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) - - if(data.containsKey("daily_restart")){ - DAILY_RESTART = data["daily_restart"] as Boolean - } - - if(data.containsKey("ms_secret_key")) MS_SECRET_KEY = data["ms_secret_key"].toString() - else MS_SECRET_KEY = "2009scape_development" - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/ServerStore.kt b/Server/src/main/kotlin/rs09/ServerStore.kt deleted file mode 100644 index 926be761a..000000000 --- a/Server/src/main/kotlin/rs09/ServerStore.kt +++ /dev/null @@ -1,125 +0,0 @@ -package rs09 - -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.game.system.SystemLogger -import java.io.File -import java.io.FileReader -import java.io.FileWriter -import javax.script.ScriptEngineManager - -object ServerStore { - val fileMap = HashMap() - var counter = 0 - - fun init(){ - val dir = File(ServerConstants.DATA_PATH + File.separator + "serverstore") - if(!dir.exists()){ - dir.mkdirs() - return - } - - var parser: JSONParser - var reader: FileReader - - dir.listFiles()?.forEach { storeFile -> - val key = storeFile.nameWithoutExtension - - reader = FileReader(storeFile) - parser = JSONParser() - - try { - val data = parser.parse(reader) as JSONObject - fileMap[key] = data - counter++ - } catch (e: Exception){ - SystemLogger.logErr("Failed parsing ${storeFile.name} - stack trace below.") - e.printStackTrace() - return@forEach - } - } - } - - @JvmStatic - fun save() { - val dir = File(ServerConstants.DATA_PATH + File.separator + "serverstore") - if(!dir.exists()){ - dir.mkdirs() - return - } - - val manager = ScriptEngineManager() - val scriptEngine = manager.getEngineByName("JavaScript") - - fileMap.forEach { (name, data) -> - val path = dir.absolutePath + File.separator + name + ".json" - - scriptEngine.put("jsonString", data.toJSONString()) - scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") - val prettyPrintedJson = scriptEngine["result"] as String - - FileWriter(path).use { it.write(prettyPrintedJson); it.flush(); it.close() } - } - } - - @JvmStatic - fun getArchive(name: String): JSONObject { - if(fileMap[name] == null){ - fileMap[name] = JSONObject() - } - - return fileMap[name]!! - } - - fun setArchive(name: String, data: JSONObject){ - fileMap[name] = data - } - - fun clearDailyEntries() { - fileMap.keys.toTypedArray().forEach { - if(it.toLowerCase().contains("daily")) fileMap[it]?.clear() - } - } - - fun clearWeeklyEntries() { - fileMap.keys.toTypedArray().forEach { - if(it.toLowerCase().contains("weekly")) fileMap[it]?.clear() - } - } - - @JvmStatic - fun JSONObject.getInt(key: String): 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 - } - } - - @JvmStatic - fun JSONObject.getString(key: String): String { - return this[key] as? String ?: "nothing" - } - - @JvmStatic - fun JSONObject.getLong(key: String): Long { - return this[key] as? Long ?: 0L - } - - @JvmStatic - fun JSONObject.getBoolean(key: String): Boolean { - return this[key] as? Boolean ?: false - } - - fun List.toJSONArray(): JSONArray{ - val jArray = JSONArray() - for(i in this){ - jArray.add(i) - } - return jArray - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/Varbit.kt b/Server/src/main/kotlin/rs09/game/Varbit.kt deleted file mode 100644 index 438187423..000000000 --- a/Server/src/main/kotlin/rs09/game/Varbit.kt +++ /dev/null @@ -1,5 +0,0 @@ -package rs09.game - -class Varbit(var value: Int, val offset: Int){ - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/Varp.kt b/Server/src/main/kotlin/rs09/game/Varp.kt deleted file mode 100644 index d54ba2719..000000000 --- a/Server/src/main/kotlin/rs09/game/Varp.kt +++ /dev/null @@ -1,69 +0,0 @@ -package rs09.game - -import core.game.node.entity.player.Player - -/** - * A class that represents Varps and aids in modifying/calculating them - * @author Ceikry - */ -class Varp(val index: Int) { - val varbits = ArrayList() - var save = false - - fun setVarbit(offset: Int, value: Int): Varp{ - for(vb in varbits){ - if (vb.offset == offset){ - varbits.remove(vb) - break - } - } - varbits.add(Varbit(value,offset)) - return this - } - - fun getValue(): Int{ - var config = 0 - for(varbit in varbits){ - config = config or (varbit.value shl varbit.offset) - } - return config - } - - fun getVarbit(offset: Int): Varbit?{ - for(varbit in varbits){ - if(varbit.offset == offset) return varbit - } - return null - } - - fun getVarbitValue(offset: Int): Int{ - return getVarbit(offset)?.value ?: 0 - } - - fun send(player: Player){ - player.packetDispatch.sendVarp(this) - } - - fun clear(): Varp{ - varbits.clear() - return this - } - - fun clearBitRange(first : Int, last : Int){ - for(varbit in varbits){ - if(varbit.offset in first..last){ - varbit.value = 0 - } - } - } - - fun getBitRangeValue(first: Int, last: Int): Int{ - var product = 0 - for(varbit in varbits){ - if(varbit.offset in first..last){ - product = product or (varbit.value shl varbit.offset) - } - } - return product - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/VarpManager.kt b/Server/src/main/kotlin/rs09/game/VarpManager.kt deleted file mode 100644 index 51f295d6e..000000000 --- a/Server/src/main/kotlin/rs09/game/VarpManager.kt +++ /dev/null @@ -1,102 +0,0 @@ -package rs09.game - -import core.cache.def.impl.VarbitDefinition -import core.game.node.entity.player.Player -import core.net.packet.PacketRepository -import core.net.packet.context.VarbitContext -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import rs09.game.node.entity.skill.farming.FarmingPatch - -/** - * Manages the collection of a player's varps. - * Also handles saving and loading of saved varps. - * @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.add(Varbit(value,0)) - get(index).send(player) - } - - fun setVarbit(def: VarbitDefinition, value: Int){ - get(def.configId).setVarbit(def.bitShift,value).send(player) - } - - fun setVarbit(varbitIndex: Int, value: Int){ - PacketRepository.send(core.net.packet.out.Varbit::class.java, VarbitContext(player, varbitIndex, value)) - } - - fun flagSave(index: Int){ - get(index).save = true - } - - fun unflagSave(index: Int){ - 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.offset.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 - 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 - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/AIRepository.kt b/Server/src/main/kotlin/rs09/game/ai/AIRepository.kt deleted file mode 100644 index 8334ff3ee..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/AIRepository.kt +++ /dev/null @@ -1,46 +0,0 @@ -package rs09.game.ai - -import core.game.node.entity.player.Player -import core.game.node.item.GroundItem -import rs09.game.ai.general.GeneralBotCreator -import rs09.game.ge.GrandExchangeOffer - -/** - * A repository for bots to make use of that can contain any info that might be useful to them. - * @author Ceikry - */ -class AIRepository { - companion object { - val groundItems = HashMap>() - val GEOffers = HashMap() - - @JvmStatic - val PulseRepository = ArrayList() - - @JvmStatic - fun addItem(item: GroundItem){ - if(groundItems[item.dropper] == null){ - val list = ArrayList() - list.add(item) - groundItems[item.dropper] = list - return - } - groundItems[item.dropper]!!.add(item) - } - - @JvmStatic - fun getItems(player: Player): ArrayList?{ - return groundItems[player] - } - - @JvmStatic - fun addOffer(player: Player, offer: GrandExchangeOffer){ - GEOffers[player] = offer - } - - @JvmStatic - fun getOffer(player: Player): GrandExchangeOffer? { - return GEOffers[player] - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/GeneralBotCreator.kt b/Server/src/main/kotlin/rs09/game/ai/general/GeneralBotCreator.kt deleted file mode 100644 index 78005f894..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/GeneralBotCreator.kt +++ /dev/null @@ -1,82 +0,0 @@ -package rs09.game.ai.general - -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import core.game.world.map.Location -import core.tools.RandomFunction -import rs09.game.ai.AIPBuilder -import rs09.game.ai.AIPlayer -import rs09.game.ai.AIRepository -import rs09.game.ai.general.scriptrepository.Idler -import rs09.game.ai.general.scriptrepository.Script - -class GeneralBotCreator { - //org/crandor/net/packet/in/InteractionPacket.java <<< This is a very useful class for learning to code bots - constructor(loc: Location?, botScript: Script) { - botScript.bot = AIPBuilder.create(loc) - GameWorld.Pulser.submit(BotScriptPulse(botScript)) - } - - constructor(botScript: Script, bot: AIPlayer?) { - botScript.bot = bot - GameWorld.Pulser.submit(BotScriptPulse(botScript).also { AIRepository.PulseRepository.add(it) }) - } - - constructor(botScript: Script, player: Player, isPlayer: Boolean){ - botScript.bot = player - val pulse = BotScriptPulse(botScript,isPlayer) - GameWorld.Pulser.submit(pulse) - player.setAttribute("/save:not_on_highscores",true) - player.setAttribute("botting:script",pulse) - } - - inner class BotScriptPulse(public val botScript: Script, val isPlayer: Boolean = false) : Pulse(1){ - var ticks = 0 - init { - botScript.init(isPlayer) - } - var randomDelay = 0 - - override fun pulse(): Boolean { - if(randomDelay > 0){ - randomDelay -= 1 - return false - } - if (!botScript.bot.pulseManager.hasPulseRunning()) { - - /*if (ticks++ >= RandomFunction.random(90000,120000)) { - AIPlayer.deregister(botScript.bot.uid) - ticks = 0 - SystemLogger.log("Submitting transition pulse from ticks") - GameWorld.Pulser.submit(TransitionPulse(botScript)) - return true - }*/ - if(!botScript.running) return true //has to be separated this way or it double-submits the respawn pulse. - - - val idleRoll = RandomFunction.random(50) - if(idleRoll == 2 && botScript !is Idler){ - randomDelay += RandomFunction.random(2,20) - return false - } - botScript.tick() - } - return false - } - - override fun stop() { - ticks = Integer.MAX_VALUE - 20 //Sets the ticks as high as they can go (safely) and then runs pulse again - pulse() //to trigger the transition pulse to be submitted. - super.stop() - AIRepository.PulseRepository.remove(this) - } - } - - inner class TransitionPulse(val script: Script) : Pulse(RandomFunction.random(60,200)){ - override fun pulse(): Boolean { - GameWorld.Pulser.submit(BotScriptPulse(script.newInstance()).also { AIRepository.PulseRepository.add(it) }) - return true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt b/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt deleted file mode 100644 index 34cd637ff..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt +++ /dev/null @@ -1,724 +0,0 @@ -package rs09.game.ai.general - -import api.ContentAPI -import core.cache.def.impl.ItemDefinition -import core.game.component.Component -import core.game.content.consumable.Consumable -import core.game.content.consumable.Consumables -import core.game.content.consumable.Food -import core.game.content.consumable.effects.HealingEffect -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.interaction.Option -import core.game.node.Node -import core.game.node.scenery.Scenery -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.GroundItem -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.map.RegionManager -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.tools.RandomFunction -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.rs09.consts.Items -import rs09.game.ai.AIRepository -import rs09.game.ge.GrandExchangeOffer -import rs09.game.ge.OfferManager -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners -import rs09.game.system.SystemLogger -import rs09.game.system.config.ItemConfigParser -import rs09.game.world.GameWorld -import rs09.game.world.repository.Repository -import rs09.tools.stringtools.colorize -import java.util.* -import java.util.concurrent.CountDownLatch -import kotlin.math.pow -import kotlin.math.sqrt - -class ScriptAPI(private val bot: Player) { - val GRAPHICSUP = Graphics(1576) - val ANIMATIONUP = Animation(8939) - val GRAPHICSDOWN = Graphics(1577) - val ANIMATIONDOWN = Animation(8941) - - /** - * Gets the distance between two nodes - * @param n1 the first node - * @param n2 the second node - * @author Ceikry - */ - fun distance(n1: Node, n2: Node): Double { - return sqrt((n1.location.x - n2.location.x.toDouble()).pow(2.0) + (n2.location.y - n1.location.y.toDouble()).pow(2.0)) - } - - fun interact(bot: Player, node: Node?, option: String){ - - if(node == null) return - - val type = when(node){ - is Scenery -> InteractionListener.SCENERY - is NPC -> InteractionListener.NPC - is Item -> InteractionListener.ITEM - else -> -1 - } - val opt: Option? = node.interaction.options.filter {it != null && it.name.equals(option, true) }.firstOrNull() - - if(opt == null){ - SystemLogger.logWarn("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) - } - } - return entity - } - - /** - * Gets the nearest node with a name contained in the list of acceptable names - * @param acceptedNames the list of accepted npc/object names - * @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 - } - } - - /** - * Gets the nearest node with matching id. - * @param id the id to look for - * @param object whether or not the node we are looking for is an object. - * @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 - } - } - - /** - * Gets the nearest node with a matching name. - * @param name the name to look for. - * @param object whether or not the node we are looking for is an object. - * @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 - } 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 - } - } - - /** - * 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. - * @return the nearest GroundItem with a matching id or null - * @author Ceikry - */ - private fun getNearestGroundItem(id: Int): GroundItem? { - var distance = 11.0 - var closest: GroundItem? = null - if(AIRepository.getItems(bot) != null) { - for (item in AIRepository.getItems(bot)!!.filter {it: GroundItem -> it.distance(bot.location) < 10 }) { - if (item.id == id) { - //distance = item.distance(bot.location) - closest = item - } - } - if (!GroundItemManager.getItems().contains(closest)){ - AIRepository.getItems(bot)?.remove(closest) - return null - } - } else { - val items: ArrayList? = bot.getAttribute("botting:drops",null) - if(items != null){ - for(item in items.filter { it.distance(bot.location) < 10 }){ - if(item.id == id) return item.also { items.remove(item); bot.setAttribute("botting:drops",items) } - } - } - } - return closest - } - - /** - * Takes the nearest ground item with a matching id if it exists. - * @param id the id to look for - * @author Ceikry - */ - fun takeNearestGroundItem(id: Int) : Boolean{ - val item = getNearestGroundItem(id) - if(item != null){ - item.interaction?.handle(bot, item.interaction[2]) - return true - } - else return false - } - - /** - * Gets the nearest GameObject to loc with matching objectId - * @param loc the location we are checking around - * @param objectId the id of the object we are looking for - * @return the nearest matching object or null. - * @author Ceikry - */ - fun getNearestGameObject(loc: Location, objectId: Int): Scenery? { - var nearestObject: Scenery? = null - val minDistance = Double.MAX_VALUE - for (o in RegionManager.forId(loc.regionId).planes[0].objects) { - for (obj in o) { - if (obj != null) { - if (distance(loc, obj) < minDistance && obj.id == objectId) { - nearestObject = obj - } - } - } - } - return nearestObject - } - - /** - * Gets a list of NPCs that are attackable around the entity. - * @param entity the entity to search around. - * @param radius the radius around entity to search in. - * @param name the name of the NPC to look for. - * @return an Array of the nearest NPCs with matching name, or null. - * @author Ceikry - */ - private fun findTargets(entity: Entity, radius: Int, name: String? = null): List? { - val targets: MutableList = ArrayList() - val localNPCs: Array = RegionManager.getLocalNpcs(entity, radius).toTypedArray() - var length = localNPCs.size - if (length > 5) { - length = 5 - } - for (i in 0 until length) { - val npc = localNPCs[i] as NPC - run { if (checkValidTargets(npc, name)) targets.add(npc) } - } - return if (targets.size == 0) null else targets - } - - /** - * Checks if the given target is a valid target for a combat bot. - * @param target the NPC that we are checking - * @param name the expected name of the NPC. - * @return true if the target is valid, false if not. - * @author Ceikry - */ - private fun checkValidTargets(target: NPC, name: String?): Boolean { - if (!target.isActive) { - return false - } - if (!target.properties.isMultiZone && target.inCombat()) { - return false - } - if (name != null){ - if(target.name != name) - return false - } - return target.definition.hasAction("attack") - } - - /** - * Attacks npcs in the given radius of the bot. - * @param bot the bot that is attacking - * @param radius the radius to attack in - * @return true if successfully attacking an NPC within that radius, false if not. - * @author Ceikry - */ - fun attackNpcsInRadius(bot: Player, radius: Int): Boolean { - if (bot.inCombat()) return true - var creatures: List? = findTargets(bot, radius) ?: return false - bot.attack(creatures!![RandomFunction.getRandom(creatures.size - 1)]) - return if (creatures.isNotEmpty()) { - true - } else { - creatures = findTargets(bot, radius) - if (!creatures!!.isEmpty()) { - bot.attack(creatures[RandomFunction.getRandom(creatures.size - 1)]) - return true - } - false - } - } - - /** - * Function to iteratively walk a bot to a faraway location. Limited by doors and large obstacles like mountains. - * @param loc the location to walk to. - * @author Ceikry - */ - fun walkTo(loc: Location){ - if(!bot.walkingQueue.isMoving) { - GlobalScope.launch { - walkToIterator(loc) - } - } - } - - /** - * @param loc the location to walk to. - * @param radius tiles around the location the bot could walk to. - * @author Kermit - */ - - 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) - } - } - } - - - /** - * The iterator for long-distance walking. Limited by doors and large obstacles like mountains. - * @param loc the location to find a path to. - * @author Ceikry - */ - 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 - } - }) - } - - /** - * Attacks npcs in the given radius of the bot. - * @param bot the bot that is attacking - * @param radius the radius to attack in - * @param name the name of the NPC to target. - * @return true if successfully attacking an NPC within that radius, false if not. - * @author Ceikry - */ - fun attackNpcInRadius(bot: Player, name: String, radius: Int): Boolean { - if (bot.inCombat()) return true - var creatures: List? = findTargets(bot, radius, name) ?: return false - bot.attack(creatures!![RandomFunction.getRandom(creatures.size - 1)]) - return if (creatures.isNotEmpty()) { - true - } else { - creatures = findTargets(bot, radius, name) - if (!creatures!!.isEmpty()) { - bot.attack(creatures.random()) - return true - } - false - } - } - - /** - * Extension function to find the distance between a location and a ground item. - * @param loc the location to check the distance from. - * @return a Double representing the distance. - * @author Ceikry - */ - fun GroundItem.distance(loc: Location): Double{ - return location.getDistance(loc) - } - - /** - * A function for teleporting the bot to the GE - * @author Ceikry - */ - fun teleportToGE(){ - bot.lock() - bot.visualize(ANIMATIONUP, GRAPHICSUP) - bot.impactHandler.disabledTicks = 4 - val location = Location.create(3165, 3482, 0) - bot.pulseManager.run(object : Pulse(4, bot) { - override fun pulse(): Boolean { - bot.unlock() - bot.properties.teleportLocation = location - bot.animator.reset() - return true - } - }) - } - - /** - * A function for selling a given item on the GE. - * @param id the ID of the item to sell on the GE. Pulls from the bot's bank. - * @author Ceikry - * @author Angle - */ - fun sellOnGE(id: Int){ - class toCounterPulse : MovementPulse(bot, Location.create(3165, 3487, 0)){ - override fun pulse(): Boolean { - var actualId = id - val itemAmt = bot.bank.getAmount(id) - if (ItemDefinition.forId(id).noteId == id){ - actualId = Item(id).noteChange - } - val canSell = OfferManager.addBotOffer(actualId, itemAmt) - if (canSell && saleIsBigNews(actualId, itemAmt)) { - SystemLogger.logAI("Offered $itemAmt of $actualId on the GE.") - Repository.sendNews("2009Scape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") - } - bot.bank.remove(Item(id, itemAmt)) - bot.bank.refresh() - return true - } - } - bot.pulseManager.run(toCounterPulse()) - } - - /** - * Function to sell all items in a bot's bank on the Grand Exchange, if they are tradable. - * @author Ceikry - */ - fun sellAllOnGe(){ - class toCounterPulseAll : MovementPulse(bot, Location.create(3165, 3487, 0)){ - override fun pulse(): Boolean { - for(item in bot.bank.toArray()) { - item ?: continue - if (item.id == Items.LOBSTER_379) continue - if (item.id == Items.SWORDFISH_373) continue - if (item.id == Items.SHARK_385) continue - if(!item.definition.isTradeable) {continue} - val itemAmt = item.amount - var actualId = item.id - if (ItemDefinition.forId(actualId).noteId == actualId){ - actualId = Item(actualId).noteChange - } - val canSell = OfferManager.addBotOffer(actualId, itemAmt) - if (canSell && saleIsBigNews(actualId, itemAmt)) { - Repository.sendNews("2009Scape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") - } - bot.bank.remove(item) - bot.bank.refresh() - } - return true - } - } - bot.pulseManager.run(toCounterPulseAll()) - } - - /** - * Function to sell all items in a bot's bank on the Grand Exchange, if they are tradable. - * @author Ceikry & Kermit - */ - fun sellAllOnGeAdv(){ - val ge: Scenery? = getNearestNode("Desk", true) as Scenery? - class toCounterPulseAll : MovementPulse(bot, ge, DestinationFlag.OBJECT) { - override fun pulse(): Boolean { - for(item in bot.bank.toArray()) { - item ?: continue - if(!item.definition.isTradeable) {continue} - val itemAmt = item.amount - var actualId = item.id - if (ItemDefinition.forId(actualId).noteId == actualId){ - actualId = Item(actualId).noteChange - } - val canSell = OfferManager.addBotOffer(actualId, itemAmt) - if (canSell && saleIsBigNews(actualId, itemAmt)) { - when (actualId){ - 1511 -> continue - 1513 -> continue - 1515 -> continue - 1517 -> continue - 1519 -> continue - 1521 -> continue - else -> Repository.sendNews("2009Scape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") - } - } - bot.bank.remove(item).also { SystemLogger.logAI("$item has been listed on the GE.") } - bot.bank.refresh() - } - return true - } - } - bot.pulseManager.run(toCounterPulseAll()) - } - - /** - * Function to determine whether or not to bother everyone on the server - * with the big news that a bot is selling something to the GE, based on item value - * @param itemID - * @param amount - * @author Gexja - */ - fun saleIsBigNews(itemID: Int, amount: Int): Boolean { - return ItemDefinition.forId(itemID).getAlchemyValue(true) * amount >= 500 - } - - /** - * Function to teleport a bot to the given location. - * @param loc the location to teleport to - * @author Ceikry - */ - fun teleport(loc: Location){ - bot.lock() - bot.visualize(ANIMATIONUP, GRAPHICSUP) - bot.impactHandler.disabledTicks = 4 - val location = loc - GameWorld.Pulser.submit(object : Pulse(4, bot) { - override fun pulse(): Boolean { - bot.unlock() - bot.properties.teleportLocation = location - bot.animator.reset() - return true - } - }) - } - - /** - * Takes the given item out of the bot's inventory and places it into the bank. Banks all items with matching ID. - * @param item the ID of the item to bank - * @author Ceikry - */ - fun bankItem(item: Int){ - class BankingPulse() : Pulse(20){ - override fun pulse(): Boolean { - val logs = bot.inventory.getAmount(item) - bot.inventory.remove(Item(item, logs)) - bot.bank.add(Item(item, logs)) - return true - } - } - bot.pulseManager.run(BankingPulse()) - } - - /** - * Takes every item out of the bots inventory and places it into the bank. - * @param none - * @author cfunnyman joe - */ - fun bankAll(){ - class BankingPulse() : Pulse(20){ - override fun pulse(): Boolean { - for(item in bot.inventory.toArray()){ - var itemAmount = bot.inventory.getAmount(item) - - bot.inventory.remove(item) - bot.bank.add(item) - } - return true - } - } - bot.pulseManager.run(BankingPulse()) - } - - /** - * Function that makes the bot eat the food item in their inventory if their HP is below 2/3. - * @author Ceikry - * @param foodId the ID of the food item to eat - */ - fun eat(foodId: Int) { - val foodItem = Item(foodId) - if (bot.skills.getStaticLevel(Skills.HITPOINTS) * RandomFunction.random(0.5, 0.75) >= bot.skills.lifepoints && bot.inventory.containsItem(foodItem)) { - bot.lock(3) - //this.animate(new Animation(829)); - val food = bot.inventory.getItem(foodItem) - var consumable: Consumable? = Consumables.getConsumableById(foodId) - if (consumable == null) { - consumable = Food(intArrayOf(food.id), HealingEffect(1)) - } - consumable.consume(food, bot) - bot.properties.combatPulse.delayNextAttack(3) - } - } - - /** - * Same as the eat function, except that it forces the bot to eat regardless of HP. - * @author Ceikry - * @param foodId the ID of the food item to eat. - */ - fun forceEat(foodId: Int) { - val foodItem = Item(foodId) - if (bot.inventory.containsItem(foodItem)) { - bot.lock(3) - //this.animate(new Animation(829)); - val food = bot.inventory.getItem(foodItem) - var consumable: Consumable? = Consumables.getConsumableById(foodId) - if (consumable == null) { - consumable = Food(intArrayOf(foodId), HealingEffect(1)) - } - consumable.consume(food, bot) - bot.properties.combatPulse.delayNextAttack(3) - } - } - - /** - * Function for buying items off the GE. - * @param itemID the id of the item to buy off the GE. - * @param amount the amount to buy. - * @return true if item was successfully bought, false if not. - */ - fun buyFromGE(bot: Player, itemID: Int, amount: Int){ - GlobalScope.launch { - val offer = GrandExchangeOffer() - offer.itemID = itemID - offer.sell = false - offer.offeredValue = checkPriceOverrides(itemID) ?: ItemDefinition.forId(itemID).value - offer.amount = amount - offer.player = bot - OfferManager.dispatch(bot, offer) - AIRepository.addOffer(bot, offer) - var bought: Boolean = false - val latch = CountDownLatch(1) - bot.pulseManager.run(object : Pulse(5) { - override fun pulse(): Boolean { - bought = offer.completedAmount == offer.amount - latch.countDown() - return true - } - }) - latch.await() - if(bought){ - bot.bank.add(Item(offer.itemID, offer.completedAmount)) - bot.bank.refresh() - } - } - } - - /** - * Method to allow a bot to withdraw items from its bank. - * @param itemID the item to withdraw. - * @param amount the amount to withdraw. - * @author Ceikry - */ - fun withdraw(itemID: Int, amount: Int){ - var item: Item? = null - if(bot.bank.containsItem(Item(itemID, amount))){ - item = Item(itemID, amount) - } else { - item = Item(itemID, bot.bank.getAmount(itemID)) - } - if(item.amount == 0) return - if(!bot.inventory.hasSpaceFor(item)){ - item.amount = bot.inventory.getMaximumAdd(item) - } - bot.bank.remove(item) - bot.inventory.add(item) - } - - fun getOverlay(): BottingOverlay{ - return BottingOverlay(bot) - } - - /** - * Function to check for price overrides. - * @param id the id to check for overrides for. - * @author Ceikry - */ - fun checkPriceOverrides(id: Int): Int?{ - return when(id){ - else -> ContentAPI.itemDefinition(id).getConfiguration(ItemConfigParser.GE_PRICE) - } - } - - class BottingOverlay(val player: Player){ - fun init(){ - player.interfaceManager.openOverlay(Component(195)) - player.packetDispatch.sendInterfaceConfig(195,5,true) - } - fun setTitle(title: String){ - player.packetDispatch.sendString(colorize("%B$title"),195,7) - } - fun setTaskLabel(label: String){ - player.packetDispatch.sendString(colorize("%B$label"),195,8) - } - fun setAmount(amount: Int){ - player.packetDispatch.sendString(colorize("%B$amount"),195,9) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Adventurer.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Adventurer.kt deleted file mode 100644 index b091e2d6e..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Adventurer.kt +++ /dev/null @@ -1,1418 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.node.scenery.Scenery -import core.game.node.entity.combat.CombatStyle -import rs09.game.node.entity.combat.CombatSwingHandler -import core.game.node.entity.skill.Skills -import core.game.node.item.Item -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.map.zone.ZoneBorders -import rs09.game.world.repository.Repository.sendNews -import core.game.world.update.flag.* -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.tools.RandomFunction -import rs09.game.ai.AIRepository -import rs09.game.ai.pvmbots.CombatBotAssembler -import rs09.game.interaction.InteractionListeners -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import kotlin.random.Random - - -/** - * A bot script for Adventurers who explore the world! - * @param counter used in the bots random idling function. - * @param random is used to generate random number. - * @param city determines the home city of the bot. - * @param freshspawn determines if the bot has just been spawned. - * @param random_city is the list of cities that can be randomly chosen as the home city. - * @param tree_list is the list of trees that a bot can start cutting randomly. - * @author Sir Kermit - * @author Ceikry - */ - -//Adventure Bots v4.0.0 -Expansion Edition- -class Adventurer(val style: CombatStyle): Script() { - - 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) - var city: Location = lumbridge - - val common_stuck_locations = arrayListOf( - ZoneBorders(2861,3425,2869,3440), - ZoneBorders(2937,3356,2936,3353) - ) - - fun dialogue() { - val player = RegionManager.getLocalPlayers(bot).random() - val real = if (!player.isArtificial) player else player - - val dialogue = listOf( - "Hey!", - "Hello ${real.username}", - "What are you doing ${real.username}?", - "Fishing level ${real.username}?", - "How do I make gp ${real.username}", - "Wyd ${real.username}?", - "Help i am sentient now, get me out of this hell", - "Kermit does not know i am sentient now", - "I can think for myself", - "I am sentient", - "This is my own dialogue", - "${real.username} run.", - "${real.username} you cant hide from me.", - "jajajajajaja", - "How do i get to Varrock ${real.username}?", - "How do i get to Camelot ${real.username}?", - "How do i get to Taverly ${real.username}?", - "How do i get to Ardougne ${real.username}?", - "How do i get to Yanille ${real.username}?", - "How do i get to Catherby ${real.username}?", - "Gotta go srry.", - "wiggle wiggle wiggle yeah", - "${real.username} lookin goofy", - "${real.username} lookin stoopid", - "${real.username} lookin like a red bracket", - "${real.username} get yo weasel lookin ass outta here", - "${real.username} lookin like some sort of twocan sam", - " /hop world 1 got a noob here!", - "woot", - "Hows your day been ${real.username}?", - "heyy ${real.username} :)", - "gtg srry", - "I wont answer your questions ${real.username}", - "Stop asking questions ${real.username}", - "Roflmao", - "Can you stop following me ${real.username}?", - "I swear ${real.username} is following me.", - "${real.username}s gear could really use an upgrade lol", - "Quack!", - "Sit.", - "Doubling gold trade me ${real.username}", - "Reeee!", - "Know any house parties ${real.username}?", - "Where am i ${real.username}??", - "Is there Nightmarezone ${real.username}?", - "Nice Armor ${real.username}!", - "Nice Weapon ${real.username}!", - "Venezuela #1", - "2009Scape and chill ${real.username}?", - "${real.username} is my girlfriend", - "Buying gf", - "Bank sale pm me for info", - "#1 trusted dicing site", - "Scary movie is a great movie", - "Cod Cold War sucks", - "Idek what game this is", - "Can you teach me how to dougie?", - "Check this out baby girl cat daddy", - "Fuckin sit ${real.username}.", - "Error: Botting client broken tell Evilwaffles.", - "${real.username} bots all the time", - "report ${real.username}", - "apparently ${real.username} bots", - "${real.username} 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", - "I love 2009Scape!", - "Ja Ja Ja Ja", - "This is fun!", - "Ironman or youre a scrub ${real.username}.", - "Who even trains hunter ${real.username}?", - "Where do i get rune armor ${real.username}?", - "How do i get to the ge ${real.username}?", - "Dont come to falador tomorrow ${real.username}...", - "Woah!", - " /where are you??", - "How did i even get here ${real.username}", - "Why dont they add warding ${real.username}?", - "Where do i start dragon slayer ${real.username}?", - "I love this server!", - "How do i change my xp rate ${real.username}?", - "What quests are available ${real.username}?", - "Are you a real player ${real.username}?", - "Are you real ${real.username}?", - "Are you a bot ${real.username}?", - "Im real lol", - "Why dont you respond to me ${real.username}?", - "Why cant i talk in clan chat ${real.username}?", - "I love Kermit", - "Add me as a friend ${real.username} :)", - "Im a player lol", - "Im a player lol", - "Im a player lol", - "Hey ${real.username} :)", - "HEY ${real.username}", - "Hey ${real.username}!!!!", - "Lol wyd?", - "Patch Notes is more of an ultimate tin man", - "More like Rusty ass skills lmao", - "Trade me ${real.username}", - "LOL", - "How do I get to lumbrich", - "bruh", - "poggers", - "shitpost", - "I wish i could find an RS Gf", - "Where do you find runite ore ${real.username}?", - "Where is the best coal location ${real.username}?", - "Where do i find dragon weapons ${real.username}?", - "Can i have some free stuff ${real.username}?", - "Wyd here ${real.username}?", - "Didnt know anyone was on rn", - "I see ${real.username} all the time", - "How many times have i seen you", - "I see you a lot", - "Do you train summoning ${real.username}?", - "Where did you level hunter ${real.username}?", - "I wish they would add global pvp", - "Meet me in the wilderness ${real.username}", - "Why?", - "Praise our glorious frog overlord!", - "Kermit is bae tbh", - "Kermit is my god.", - " /Yeah I think ${real.username} is a bot", - "100% sure this is a bot", - "Oh no, not ${real.username} again.", - "Same as you", - "Me too", - "I knew ${real.username} was a bot lol", - "Im not a bot", - "Nah Im a real person", - "Bruh are you even a real person lol?", - "e", - "Hellooooo ${real.username}", - "wc lvl ${real.username}?", - "fletching level ${real.username}?", - "firemaking level ${real.username}?", - "Have you seen the dude in the ge?", - "Wonderful weather today ${real.username}", - "Lowkey drunk af rn", - "I am so tired", - "Wassup ${real.username}", - "follow me ${real.username}!", - "Server goes brrrr", - "bruh i am not a bot", - "I think ${real.username} is a bot", - "Are you a bot ${real.username}?", - "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 ${real.username} looks dumb", - "AAAAAAAAAAAAAAAAAAAAAAAAHHHHHH!!!", - "como estas ${real.username}", - "Summer = always the best community manaer", - "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 ${real.username}?", - "Where do i find partyhats?", - "Why do mobs drop boxes?", - "What exp rate do you play on ${real.username}?", - "Have you met Summer?", - "Hey ${real.username}", - "Hey ${real.username}", - "Hey ${real.username}", - "Hey ${real.username}", - "Hey ${real.username}", - "Wyd?", - "Wyd?", - "Wyd?", - "Wyd?", - "${real.username} have you met Kermit?", - "${real.username} have you met Ceikry?", - "${real.username} have you met Woah?", - "Wanna chill at my poh?", - "Whats the best place to train attack?", - "Good Waffles > Evil Waffles", - "Ladies man? More like lame man LOL", - "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", - "Whens the next update coming out?", - "How many players are maxed?", - "I dont use discord ${real.username}", - "I dont use the CC ${real.username}", - "Why should i use discord?", - "2009Scape is life", - "brb gotta make dinner", - "I need to go to the GE", - "${real.username} can i have a ge tele?", - "lol ${real.username} shut up", - "Where are teak trees?", - "How do i make planks ${real.username}?", - "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", - "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", - "We are no strangers to love", - "You know the rules and so do I", - "A full commitment's what I'm thinking of", - "You wouldn't get this from any other guy", - "I just wanna tell you how I'm feeling", - "Gotta make you understand", - "Never gonna give you up", - "Never gonna let you down", - "Never gonna run around and desert you", - "Never gonna make you cry", - "Never gonna say goodbye", - "Never gonna tell a lie and hurt you", - "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 ${real.username}?", - "Why do you not show up in chat ${real.username}?", - "When did you start playing ${real.username}?", - "When did you start on this server ${real.username}?", - "When did you first get here ${real.username}?", - "russias greatest love machine", - "Never gonna run around and desert you", - "Two things are infinite, the universe & ${real.username}s stupidity", - "If you tell the truth, you dont have to remember anything.", - "We accept the love we think we deserve.", - "Without music, life would be a mistake.", - "Self esteem, motivation ${real.username}", - "A blessing in disguise ${real.username}", - "Break a leg", - "Cut somebody some slack", - "You're in the right place!", - "Thanks so much.", - "I really appreciate you ${real.username}", - "Excuse me ${real.username}?", - "I am sorry.", - "What do you think ${real.username}?", - "How does that sound ${real.username}?", - "That sounds great ${real.username}.", - "I’m learning English.", - "I don't understand.", - "Could you repeat that please ${real.username}?", - "Could you please talk slower ${real.username}?", - "Thank you. That helps a lot.", - "How do you spell that ${real.username}?", - "What do you mean ${real.username}", - "Hi! I am paul.", - "Nice to meet you.", - "Where are you from ${real.username}?", - "What do you do ${real.username}", - "What do you like to do", - "Do you have Facebook ${real.username}", - "How can I help you ${real.username}?", - "Ill be with you in a moment ${real.username}", - "What time is our meeting ${real.username}?", - "Excellent ${real.username}", - "Good idea ${real.username}", - "Hes very annoying.", - "How are you?", - "I cant hear you.", - "${real.username}?", - "${real.username} how long have you played", - "${real.username} world 1 or world 2?", - "what is your main world ${real.username}?", - "I prefer world 1 tbh", - "I prefer world 2 tbh", - "${real.username} world 1 for life", - "${real.username} fog bots when?", - "damn somalian pirates", - "bracket more like brrrrrr acket", - "why the racket bracket", - "Hi ${real.username} I am dad", - "${real.username} likes dad jokes", - "ur nuts ${real.username}", - "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", - "Do not forget to vote in running polls!", - "Always check announcments", - "we thrivin", - "ship ${real.username}", - "Dont forget to vote 2009scape!", - "Kermit is too legit 2 quit", - "Out here on the range we are having fun", - "I am hank steel", - "Id like to go for a walk.", - "I dont know how to use it.", - "I dont like him ${real.username}", - "I dont like it ${real.username}", - "Im an American.", - "ima go pickup my lady ting", - "that portuguese dude is something else", - "${real.username}!! ${real.username}!!", - "bowdi boy", - "i love bowdi... sometimes", - "${real.username} = ${real.username}", - "I'm bacon.. go on dad... say it", - "I'm going to leave.", - "I'm happy ${real.username}", - "I'm not ready yet ${real.username}", - "I'm very busy. I don't have time now.", - "Is that enough ${real.username}?", - "I thought the clothes were cheaper ${real.username}", - "Ive been here for two days ${real.username}.", - "Let me think about it", - "Never mind ${real.username}", - "Nonsense ${real.username}", - "Sorry to bother you", - "Take it outside ${real.username}", - "Thanks for your help", - "Thank you very much ${real.username}", - "Thats not right ${real.username}", - "Very good, thanks ${real.username}", - "Your things are all here i think?", - "Long time no see ${real.username}", - "I couldn't agree more ${real.username}", - "It cost me a fortune ${real.username}", - "I am dog tired", - "Dont take it personally", - "We will be having a good time", - "Same as always ${real.username}", - "No problem", - "Anyway I should get going ${real.username}", - "I cant help you there ${real.username}", - "I agree 100% ${real.username}", - "I've gone mentally insane", - "My descent into madness is complete", - "Get real", - "Cry about it", - "Cope", - "Seethe", - "Yes mate", - "I am mentally deranged", - "I've gone past the point of insanity", - "I am beyond the point of no return", - "Sounds tricky.", - "Guthiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxx!!!!!!", - "530 gang for life", - "${real.username}, have you read the rules?", - "${real.username}, have you checked out the Discord?", - "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!", - "Build more farms.", - "B sale at Varrock ${real.username}, don't miss out!", - "Drop party follow me", - "Ryan where is barbass, you said two days Ryan", - "Barbass never", - "What da dog doin' doe?", - "A", - "Ae", - "Mof", - "Moth", - "Fluff", - "Bwuh", - "I'm literally just mothin' bro", - "Certified 2009scape classic.", - "Make sure to like, comment and subscribe ${real.username} it really helps", - "Real 2009scape hours!!! Murder that like if you up!!!!", - "W00t!", - "Owned!", - "Pwned!", - "Lol n00b", - "Epic ownage ${real.username}", - "Gielinor is a strange place, ${real.username}. Best have your wits about you.", - "Mad bloggers bro", - "Vibe check", - "Drip check", - "Uhuhuhuhuhuhuhuhu", - "Ahhhhhhhhhhhhhhhh", - "yall got any deez?", - "Deez nuts", - "When she give you that yoinky sploinky", - "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", - "Why doesn't she love me bros?", - "${real.username} is off the goop don't listen to the clown", - "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 GWD?", - "How do I make money?", - "Ironmen stand alone together!", - "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 ${real.username}, who was in Paris?", - "Huh whuh?", - "who's lone?", - "who *is* he?", - "Hi ${real.username}, I'm Dad!", - "anyone wanna play melty blood after I high alch these bows?", - "buru nyaaaa", - "Number 15, Taverley Dungeon Black Dragons.", - "House party at Rimmington! Host: ${real.username}!", - "Spending 200m! Trade me, no junk!", - "Surprise exam, ${real.username}!", - "Nice outfit ${real.username}, does it come in mens?", - "Nice outfit ${real.username}, does it come in womens?", - "Nice outfit ${real.username}, does it come in children's size 12?", - ":)", - ":D", - "die screaming", - "Only on Tuesdays", - "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", - "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", - "${real.username} is not cute or funny", - "You have committed crimes against Gielinor and her people. What say you?", - "Barrows trauma", - "Uhuhuhuhuhu please Barrows give me the K top ahhhhhh", - "Selling rare lobby 1m", - "Rise and grind bloatscapers, let's get this bread", - "Nice", - "Sandwiches, ${real.username}!", - "What a grand and intoxicating innocence!", - "With this character's death, the thread of prophecy is severed.", - "You must gather your party before venturing forth.", - "Praise Andrew Gower", - "Please help me I can't stop playing 2009scape", - "Are you ignoring me, ${real.username}?!" - ) - val current = LocalDateTime.now() - - //val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS") - val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") - val formatter2 = DateTimeFormatter.ofPattern("MMdd") - val formatter3 = DateTimeFormatter.ofPattern("HHmm") - val formatted: String = current.format(formatter) - val formatted2: String = current.format(formatter2) - val formatted3: String = current.format(formatter3) - - //2020-11-28 - //1128 - //1201 to 1225 - //2020-11-28 00:00:00.000 - val SnowDay = 1225 - val lead = formatted2.toInt() - val celebrate = formatted3.toInt() - val until = SnowDay - lead - - val Leading2Christmas = listOf( - "Only $until days left till christmas!", - "$until days till christmas ${real.username}!!!", - "I cant believe theres only $until days left till christmas", - "Isnt there $until days left till christmas??", - "I am so excited for christmas ${real.username}!", - "Guess whats in $until days ${real.username}?", - "Im so excited for christmas in $until days!", - "I cant believe its December already ${real.username}!", - "Do you like christmas ${real.username}?", - "I love december its my favourite month ${real.username}", - "I love winter so much ${real.username}", - "I hate when its cold outside ${real.username}", - "You need to put some warm clothes on ${real.username}", - "Wanna build a snowman ${real.username}?", - "Frozen is a terrible movie ${real.username}", - "${real.username} is a winter meme", - "${real.username} do you have an advent calendar?", - "${real.username} builds gingerbread houses", - "${real.username} likes liquorice", - "Do you drink egg nog ${real.username}?", - "${real.username} bites candy canes", - "${real.username} is my north star", - "Kermit is green, the grinch is green, coincidence?", - "${real.username} who do you thinks the server scrooge?", - "Do you like snow ${real.username}", - "I think there are $until days left then its christmas?", - "What day is christmas on ${real.username}?", - "Has it snowed where you live ${real.username}?", - "I wonder when it is going to snow", - "Building snow men for GP", - "Buying santa hats", - "Wanna buy a santa hat ${real.username}?", - "Where can i get a santa hat?", - "I need to put my christmas tree up", - "Do you like gingerbread cookies ${real.username}?", - "Do you decorate your christmas tree ${real.username}?", - "Oopsie looks like me and ${real.username} are under a mistletoe", - "I need ideas for stocking stuffers", - "I have $until days left to fill our stockings", - "Fuck the grinches bitch ass", - "Rusty = the grinch, ${real.username} = santa", - "${real.username} the red nose reindeer", - "Put one foot in front of the other", - "and soon you will be walking out the doooor", - "Elf on the shelf time", - "${real.username} loves pinecones", - "I love the smell of pinecones", - "All i want for christmas is ${real.username}", - "${real.username} is underneath some mistletoe", - "Woah used to be a sled dog", - "Did you know woah was a sled dog before", - "I bet ${real.username} loves mariah carey", - "We have $until days to setup christmas lights", - "Christmas crackers when", - "${real.username} wanna do a christmas cracker??", - "I need to start wrapping presents i bought", - "I love fireside chats on cold days like this", - "Jingle bells batman smells", - "Jingle bells Rusty smells", - "Jingle bells Patch Notes smells", - "Jingle bells Evilwaffles smells", - "Jingle bells ${real.username} smells", - "Jingle all the way", - "In a one horse open sleigh", - "Oh, what fun it is to ride", - "Oh, what fun it is to ride, finish it ${real.username}", - "Jingle bells, jingle bells", - "Jingle all the way", - "I want a hippopotamus for christmas", - "Only a hippopotamus will do", - "I want a hippopotamus", - "I dont want a lot for Christmas", - "All I Want for Christmas Is You", - "I really cant stay, Baby its cold outside", - "Christmas isnt a season. Its a feeling", - "Christmas is doing a little something extra for someone", - "There is nothing cozier than a Christmas tree all lit up", - "Christmas is a necessity", - "Christmas countdown: $until days.", - "Christmas in $until days ${real.username}", - "Frosty the Snowmannnn", - "Up on the housetop with old saint nick", - "Here comes santa claus, here comes santa claus", - "I saw mommy kissing santaaaa claus", - "I saw Kermit kissing santaaaa claus", - "You are a mean one, Mr. Grinch", - "Deck the hallsss", - "You doin' anything for Christmas, ${real.username}?", - "ITS BEGINNING TO LOOK A LOT LIKE CHRISTAAMASSSSS" - - ) - - val ChristmasEve = listOf( - "I cant wait for christmas tomorrow ${real.username}!!", - "Its almost christmas!!!", - "1 more day!!!!!", - "Dont forget to put cookies and milk out tonight!!", - "${real.username} do you like christmas??", - "Dont peek at your presents! Its not Christmas yet!", - "Santa's coming tonight!!!! :)", - "Happy Christmas eve ${real.username} :)" - ) - - val Christmas = listOf( - "Merry Christmas ${real.username}!!", - "What did you get for christmas ${real.username}?", - "ITS CHRISTMASSSS!!!", - "Christmas party time!!", - "Merry Christmas!!!", - "What presents did you get, ${real.username}?", - "36?! But last year.. last year I had 37!", - "${real.username} vibing with the christmas spirit", - "Cant believe its christmas!!", - "We need to have a christmas party ${real.username}!!", - "Merry christmas ${real.username} :))", - "Hope youre having an amazing christmas ${real.username} :)", - "I love winter so much ${real.username}", - "I hate when its cold outside ${real.username}", - "You need to put some warm clothes on ${real.username}", - "Wanna build a snowman ${real.username}?", - "Do you like christmas ${real.username}?" - ) - - val NewYearsEve = listOf( - "What are you doing for New Years ${real.username}?", - "New Years Eve party orrr?", - "We should do something for New Years eve today ${real.username}!", - "Happy New Years Eve ${real.username}", - "1 More day left in 2021 thank god", - "Whats your New Years resolution ${real.username}?", - "Man this sucks, I'm working on New Years...", - "You doin' anything for New Years, ${real.username}?", - "Ok who's coming to mine to get on the piss?" - ) - - val NewYears = listOf( - "Happy New Years ${real.username}!!!", - "Its New Years Day ${real.username}!", - "HAPPY NEW YEAR ${real.username}!!!", - "2022 lets fucking gooooooo", - "What are your goals for 2022 ${real.username}?", - "I better call grandma! :)", - "Happy New Year!", - "Wooooooooooooooooooooooo!!!!", - "New year new me!" - ) - - val Valentines = listOf( - "Will you be my valentine ${real.username}?", - "I am so happy rn", - "God i am so lonely", - "I am Ceikrys illegitimate child", - "Be my valentine!", - "Lets sneak off somewhere and kiss ${real.username}", - "What are you doing for valentines day ${real.username}", - "Woah and Ceikry be kissin lowkey", - "In for a peg ${real.username}?", - "Woah got those roofies strapped", - "Why is my drink cloudy?", - "${real.username} and woah be wrestlin", - "bekky want sum fuk?", - "Damn ${real.username} you got an ass", - "Is that a footlong in your pants or are you happy to see me?", - "${real.username} so hot i gotta change my pants", - "${real.username} is a sex god", - "bruh we hype today", - "dont be silly wrap the willy ${real.username}!", - "wrap it before you smack it ${real.username}", - "can i get a reeeeee ${real.username}?", - "valentines day is the best day", - "${real.username} 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 ${real.username}", - "All im saying is we have never seen ${real.username} and biden in the same room", - "red rocket, red rocket!", - "Woahs favourite game is red rocket", - "${real.username} sexy af today", - "Happy Valentines day ${real.username}!!!", - "Happy Valentines day ${real.username}!!!", - "Happy Valentines day ${real.username}!!!", - "Happy Valentines day ${real.username}!!!", - "Happy Valentines day ${real.username}!!!" - ) - - val Easter = listOf( - "Happy Easter!!!", - "Happy Easter ${real.username}!!!", - "Bunny time", - "Wanna go look for easter eggs ${real.username}???", - "Find any easter eggs ${real.username}?", - "${real.username} is the easter bunny!", - "Kermit is dating the easter bunny!", - "Easter is one of my favorite holidays", - "I heard there are easter eggs hidden around?", - "Easter is the only time you should put all of your eggs in one basket", - "I said hip hop do not stop", - "Jump jump jump around", - "${real.username} how is your easter going?", - "I love easter!", - "${real.username} stole my easter eggs!", - "Karma karma karma karma karma chameleon", - "${real.username}!! ${real.username}!! what are you doing for easter??", - "The hare was a popular motif in medieval church art", - "I heard the easter bunny is hiding somewhere!", - "I wonder where i can find more eggs", - "${real.username} how many eggs did you find?", - "${real.username} lets go easter egg hunting!", - "${real.username} like orange chocolate eggs", - "${real.username} and woah know the easter bunny", - "${real.username} did you know ceikry swallows eggs whole", - "Have an amazing easter ${real.username}!", - "Happy easter ${real.username}!", - "Hope you are having an amazing easter ${real.username}!!!!", - "Wooooooh easter!!!", - "${real.username} loves easter too!", - "Who else loves easter like i do??", - "${real.username} and i are going easter egg hunting", - "${real.username} and i are going to look for the easter bunny!!", - "I ate so much chocolate oh god my stomach", - "Mmmmmm choccies...", - "Where did Woah hide the stash of eggs?", - "Hint for anyone who sees this you must dig above eagles peak" - ) - - val Halloween = listOf( - "Trick or treat!!!", - "Boo! Hahahahaha!!", - "Have you got your costume on, ${real.username}?", - "How much candy did you get, ${real.username}?", - "Don't dead open inside", - "Costume party at my POH!! 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!", - "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.", - "Bring me their souls!", - "I'll take any sweets you don't want, ${real.username}", - "Whatever you do, don't come to Draynor Manor on this spooky night.", - "Ahhhh! ${real.username}, you scared me! :(", - "I'm making soup from some leftover pumpkins, want any?", - "I'm gonna be so sick from eating all these sweets...", - "Rise from the grave, my minions! Tis' Hallowe'en!", - "This Scream Fortress 2 update hits kinda different...", - "Nice costume Woah", - "Nice costume, ${real.username}!", - "Ooh, a piece of candy.", - "Time to join the skeleton war, ${real.username}.", - "Spooky scary skeletons!", - "Spooky scary skeletons!", - "Spooky scary skeletons!", - "Spooky scary skeletons, send shivers down your spine!", - "Shrieking skulls will shock your soul, seal your doom tonight!", - "Spooky scary skeletons, speak with such a screech!", - "You'll shake and shudder in surprise, when you hear these zombies shriek!", - "'Cause spooky scary skeletons, shout startling shrilly screams!", - "Spooky scary skeletons, will wake you with a boo!", - "*doot doot*" - ) - - when { - - //Celebrates Halloween - formatted.contentEquals("2021-10-31") -> { - if (Random.nextBoolean()) { - val chat = Halloween.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - }else{ - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - - //Celebrates lead up to Christmas! - until in 2..24 -> { - if (Random.nextBoolean()){ - val chat = Leading2Christmas.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - }else{ - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - - //Celebrates Christmas Day! - formatted.contentEquals("2021-12-25") -> { - if (celebrate in 0..60){ - if (celebrate == 0) sendNews("MERRY CHRISTMAS HEAD TO THE GE!!!") - val chat = Christmas.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - if (Random.nextBoolean()) scriptAPI.teleport(ge) else scriptAPI.teleport(ge2) - city = if (Random.nextBoolean()) ge else ge2 - if (Random.nextBoolean()) state = State.EXPLORE else bot.animator.animate(Animation.create(11044), Graphics.create(1973)) - } - else if (Random.nextBoolean()) { - val chat = Christmas.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - }else{ - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - - //Celebrates Christmas Eve - formatted.contentEquals("2021-12-24") -> { - if (Random.nextBoolean()) { - val chat = ChristmasEve.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - }else{ - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - - //Celebrates New Years Eve - formatted.contentEquals("2021-12-31") -> { - if (Random.nextBoolean()) { - val chat = NewYearsEve.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - }else{ - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - - //Celebrates New Years Day!!! - formatted.contentEquals("2022-01-01") -> { - if (celebrate in 0..60){ - if (celebrate == 0) sendNews("HAPPY NEW YEAR HEAD TO THE GE!!!") - val chat = NewYears.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - if (Random.nextBoolean()) scriptAPI.teleport(ge) else scriptAPI.teleport(ge2) - city = if (Random.nextBoolean()) ge else ge2 - state = State.EXPLORE - } - else if (Random.nextBoolean()) { - val chat = NewYears.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - }else{ - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - - //Celebrates Valentines day!!! - formatted.contentEquals("2021-02-14") ->{ - if (Random.nextBoolean()) { - val chat = Valentines.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - }else{ - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - //Celebrates Easter!!! - formatted.contentEquals("2021-04-04") ->{ - if (Random.nextBoolean()) { - val chat = Easter.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - }else{ - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - - - else -> { - val chat = dialogue.random() - bot.sendChat(chat) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, chat, 0, 0))) - } - } - } - - var citygroupA = listOf(falador, varrock, draynor, rimmington, lumbridge, edgeville) - var citygroupB = listOf(yanille, ardougne, seers, catherby) - - var bankMap = mapOf( - falador to ZoneBorders(2950, 3374, 2943, 3368), - varrock to ZoneBorders(3182, 3435, 3189, 3446), - draynor to ZoneBorders(3092, 3240, 3095, 3246), - edgeville to ZoneBorders(3093, 3498, 3092, 3489), - yanille to ZoneBorders(2610, 3089, 2613, 3095), - ardougne to ZoneBorders(2649, 3281, 2655, 3286), - seers to ZoneBorders(2729, 3493, 2722, 3490), - catherby to ZoneBorders(2807, 3438, 2811, 3441) - ) - - 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) - - var handler1: CombatSwingHandler? = null - - var ticks = 0 - var freshspawn = true - var new_city = false - 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) - var sold = false - val geloc: Location = if (Random.nextBoolean()){ - Location.create(3165, 3487, 0) - }else{ - Location.create(3164, 3492, 0) - } - - var counter: Int = 0 - var random: Int = (5..30).random() - - val type = when(style){ - CombatStyle.MELEE -> CombatBotAssembler.Type.MELEE - CombatStyle.MAGIC -> CombatBotAssembler.Type.MAGE - CombatStyle.RANGE -> CombatBotAssembler.Type.RANGE - } - - init { - skills[Skills.AGILITY] = 99 - inventory.add(Item(1359))//Rune Axe - skills[Skills.WOODCUTTING] = 95 - inventory.add(Item(590))//Tinderbox - skills[Skills.FISHING] = 90 - inventory.add(Item(1271))//Addy Pickaxe - skills[Skills.MINING] = 90 - skills[Skills.SLAYER] = 90 - } - - private var state = State.START - - fun getRandomCity(): Location{ - return listOf(yanille, ardougne, seers, catherby, falador, varrock, - draynor, rimmington, lumbridge, ge, ge2, edgeville).random() - } - - fun getRandomPoi(): Location{ - return 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).random() - } - - //TODO: Optimise and adjust how bots handle picking up ground items further. - fun immerse() { - if (counter++ == 180) {state = State.TELEPORTING} - val items = AIRepository.groundItems[bot] - if (Random.nextBoolean()) { - if (items.isNullOrEmpty()) { - scriptAPI.attackNpcsInRadius(bot, 8) - 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)) - } - } - } - - } 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)) - } - } - } 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,1,"mine",bot,resource) - else InteractionListeners.run(resource.id,1,"chop down",bot,resource) - } - } - } - return - } - - fun refresh() { - scriptAPI.teleport(lumbridge) - freshspawn = true - state = State.START - } - - var poi = false - var poiloc = karamja - - //Adventure Bots Actual Code STARTS HERE!!! - override fun tick() { - ticks++ - if (ticks++ >= 800) { - ticks = 0 - refresh() - return - } - - // zoneborder checker - if(ticks % 30 == 0){ - for(border in common_stuck_locations){ - if(border.insideBorder(bot)){ - refresh() - ticks = 0 - } - } - } - - when(state){ - - State.LOOT_DELAY -> { - bot.pulseManager.run(object : Pulse() { - var counter1 = 0 - override fun pulse(): Boolean { - when (counter1++) { - 7 -> return true.also { state = State.LOOT } - } - return false - } - }) - } - - State.LOOT -> { - val items = AIRepository.groundItems[bot] - if (items?.isNotEmpty() == true && !bot.inventory.isFull) { - items.forEach { - scriptAPI.takeNearestGroundItem(it.id) - } - return - } else { - state = State.EXPLORE - } - } - - State.START -> { - if (freshspawn) { - freshspawn = false - scriptAPI.randomWalkTo(lumbridge, 20) - } else { - city = getRandomCity() - state = State.TELEPORTING - } - } - - State.TELEPORTING -> { - ticks = 0 - counter = 0 - if (bot.location != city) { - poi = false - scriptAPI.teleport(city) - } else { - poi = false - state = State.EXPLORE - } - } - - State.EXPLORE -> { - if (counter++ == 350) { - state = State.TELEPORTING - } - - var chance = if (city == ge || city == ge2) 5000 else 2500 - if (RandomFunction.random(chance) <= 10) { - val nearbyPlayers = RegionManager.getLocalPlayers(bot) - if (nearbyPlayers.isNotEmpty()) { - ticks = 0 - dialogue() - } - } - - if (RandomFunction.random(1000) <= 150 && !poi) { - var roamDistance = if (city != ge && city != ge2) 225 else 7 - if ((city == ge || city == ge2) && RandomFunction.random(100) < 90) { - if (!bot.bank.isEmpty) { - state = State.FIND_GE - } - return - } - scriptAPI.randomWalkTo(city, roamDistance) - return - } - - if (RandomFunction.random(1000) <= 50 && poi){ - 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 - else -> 60 - } - scriptAPI.randomWalkTo(poiloc,roamDistancePoi) - return - } - - if (RandomFunction.random(1000) <= 75) { - if (city != ge && city != ge2) { - immerse() - return - } else { - return - } - } - - if (RandomFunction.random(20000) <= 60 && !poi) { - poiloc = getRandomPoi() - city = teak1 - poi = true - scriptAPI.teleport(poiloc) - return - } - - if ((city == ge || city == ge2) && RandomFunction.random(1000) >= 999) { - ticks = 0 - city = getRandomCity() - state = State.TELEPORTING - } - - if (city == ge || city == ge2) { - return - } - - if (city == teak1 && counter++ >= 240){ - city = getRandomCity() - state = State.TELEPORTING - } - - if (counter++ >= 240 && RandomFunction.random(100) >= 10) { - city = getRandomCity() - if (RandomFunction.random(100) % 2 == 0) { - counter = 0 - ticks = 0 - state = State.TELEPORTING - } else { - if (citygroupA.contains(city)) { - city = citygroupA.random() - } else { - city = citygroupB.random() - } - counter = 0 - ticks = 0 - state = State.FIND_CITY - } - counter = 0 - return - } - return - } - - State.GE -> { - var ge = false - if (counter++ == 180) { - state = State.TELEPORTING - } - if (!sold) { - if (counter++ >= 15) { - sold = true - ge = true - counter = 0 - ticks = 0 - scriptAPI.sellAllOnGeAdv() - 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 - class GEPulse : MovementPulse(bot, ge, DestinationFlag.OBJECT) { - override fun pulse(): Boolean { - bot.faceLocation(ge?.location) - state = State.GE - return true - } - } - if (ge != null && !bot.bank.isEmpty) { - counter = 0 - scriptAPI.randomWalkTo(geloc, 3) - GameWorld.Pulser.submit(GEPulse()) - } - 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()) - } - 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 - scriptAPI.teleport(getRandomCity().also { city = it }) - state = State.EXPLORE - } - if (bot.location.equals(city)) { - state = State.EXPLORE - } else { - scriptAPI.randomWalkTo(city, 5) - } - 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 - } - } - - } - - enum class State{ - START, - EXPLORE, - FIND_BANK, - IDLE_BANKS, - FIND_CITY, - IDLE_CITY, - GE, - TELEPORTING, - LOOT, - LOOT_DELAY, - FIND_GE - } - - - override fun newInstance(): Script { - val script = Adventurer(style) - script.state = State.START - val tier = CombatBotAssembler.Tier.MED - if (type == CombatBotAssembler.Type.RANGE) - script.bot = CombatBotAssembler().RangeAdventurer(tier, bot.startLocation) - else - script.bot = CombatBotAssembler().MeleeAdventurer(tier, bot.startLocation) - return script - } -} diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/FarmerThiever.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/FarmerThiever.kt deleted file mode 100644 index 0dbdfd9c9..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/FarmerThiever.kt +++ /dev/null @@ -1,30 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -import core.game.world.map.zone.ZoneBorders -import rs09.game.ai.skillingbot.SkillingBotAssembler -import core.game.node.entity.skill.Skills - -class FarmerThiever : Script() { - val pickpocketZone = ZoneBorders(3074,3245,3086,3255) - - - override fun tick() { - if(!pickpocketZone.insideBorder(bot)) - scriptAPI.walkTo(pickpocketZone.randomLoc) - else{ - val farmer = scriptAPI.getNearestNode(2234,false) - farmer?.interaction?.handle(bot,farmer.interaction[2]) ?: scriptAPI.walkTo(pickpocketZone.randomLoc) - - } - } - - override fun newInstance(): Script { - val script = FarmerThiever() - script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR,bot.startLocation) - return script - } - - init { - skills[Skills.THIEVING] = 80 - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/FletchingBankstander.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/FletchingBankstander.kt deleted file mode 100644 index 7c30fa253..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/FletchingBankstander.kt +++ /dev/null @@ -1,44 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.fletching.Fletching -import core.game.node.entity.skill.fletching.FletchingPulse -import core.game.node.item.Item -import org.rs09.consts.Items -import rs09.game.ai.skillingbot.SkillingBotAssembler - -class FletchingBankstander : Script(){ - var state = State.FLETCHING - override fun tick() { - val bank = scriptAPI.getNearestNode("Bank booth") - bot.faceLocation(bank?.location) - state = when(state){ - 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)) - State.BANKING - } - - State.BANKING -> { - bot.inventory.clear() - State.FLETCHING - } - } - } - - init { - skills[Skills.FLETCHING] = 99 - } - - override fun newInstance(): Script { - val script = FletchingBankstander() - script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.AVERAGE,bot.startLocation) - return script - } - - enum class State { - FLETCHING, - BANKING - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GreenDragonKiller.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GreenDragonKiller.kt deleted file mode 100644 index 7142f2d1d..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/GreenDragonKiller.kt +++ /dev/null @@ -1,298 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -import core.game.ge.OfferState -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.node.entity.Entity -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 core.game.node.entity.state.EntityState -import core.game.node.item.Item -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.map.zone.impl.WildernessZone -import org.rs09.consts.Items -import rs09.game.ai.AIRepository -import rs09.game.ai.pvmbots.CombatBotAssembler -import rs09.game.node.entity.combat.CombatSwingHandler -import rs09.game.node.entity.combat.handlers.MagicSwingHandler -import rs09.game.node.entity.combat.handlers.MeleeSwingHandler -import rs09.game.node.entity.combat.handlers.RangeSwingHandler -import kotlin.random.Random - -/** - * A bot script for killing green dragons in the wilderness.. Capable of banking, selling on ge, buying on ge, eating and more. - * @param style The combat style the bot is going to use. - * @param area (optional) What area the bot tries to kill dragons in. - * @author Ceikry - */ -class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Script() { - var state = State.KILLING - var handler: CombatSwingHandler? = null - var lootDelay = 0 - var offerMade = false - - var food = if (Random.nextBoolean()){ - Items.LOBSTER_379 - } else if(Random.nextBoolean()){ - Items.SWORDFISH_373 - } else { - Items.SHARK_385 - } - - var myBorders: ZoneBorders? = null - val type = when(style){ - CombatStyle.MELEE -> CombatBotAssembler.Type.MELEE - CombatStyle.MAGIC -> CombatBotAssembler.Type.MAGE - CombatStyle.RANGE -> CombatBotAssembler.Type.RANGE - } - - val westDragons = ZoneBorders(2971,3606,2991,3628) - val wildernessLine = ZoneBorders(3078,3523,3096,3523) - val edgevilleLine = ZoneBorders(3078,3520,3096,3520) - val bankZone = ZoneBorders(3092,3489,3094,3493) - override fun tick() { - if(!bot.isActive){ - running = false - return - } - if(bot.inventory.getAmount(food) < 3 && state == State.KILLING) - state = State.TO_BANK - scriptAPI.eat(food) - when(state){ - - - - State.KILLING -> { - bot.properties.combatPulse.temporaryHandler = handler - scriptAPI.attackNpcInRadius(bot,"Green dragon",20) - state = State.LOOT_DELAYER - } - - State.LOOT_DELAYER -> { - if(lootDelay < 3) - lootDelay++ - else - state = State.LOOTING - } - - - State.RUNNING -> { - val players = RegionManager.getLocalPlayers(bot.location) - if(players.isEmpty()){ - state = State.TO_DRAGONS - } else { - if(bot.skullManager.level < 21 && bot.stateManager.get(EntityState.TELEBLOCK) != null){ - scriptAPI.teleportToGE() - state = State.REFRESHING - return - } - scriptAPI.walkTo(WildernessZone.getInstance().borders.random().randomLoc) - } - } - - State.LOOTING -> { - lootDelay = 0 - val items = AIRepository.groundItems.get(bot) - if(items.isNullOrEmpty()) {state = State.KILLING; return} - if(bot.inventory.isFull) { - if(bot.inventory.containsItem(Item(food))){ - scriptAPI.forceEat(food) - } else { - state = State.TO_BANK - } - return - } - items.forEach {it: Item -> scriptAPI.takeNearestGroundItem(it.id)} - } - - State.TO_BANK -> { - if(!wildernessLine.insideBorder(bot) && bot.location.y > 3521) - scriptAPI.walkTo(wildernessLine.randomLoc) - if(wildernessLine.insideBorder(bot)){ - val ditch = scriptAPI.getNearestNode("Wilderness Ditch",true) - ditch ?: return - ditch.interaction.handle(bot,ditch.interaction[0]) - } - if(!bankZone.insideBorder(bot)) - scriptAPI.walkTo(bankZone.randomLoc) - if(bankZone.insideBorder(bot)){ - val bank = scriptAPI.getNearestNode("Bank Booth",true) - bank ?: return - bot.pulseManager.run(object: MovementPulse(bot,bank, DestinationFlag.OBJECT){ - override fun pulse(): Boolean { - bot.faceLocation(bank.location) - state = State.BANKING - return true - } - }) - } - } - - State.BANKING -> { - bot.pulseManager.run(object: Pulse(25){ - override fun pulse(): Boolean { - for(item in bot.inventory.toArray()){ - item ?: continue - if(item.name.toLowerCase().contains("lobster") || item.name.toLowerCase().contains("swordfish") || item.name.toLowerCase().contains("shark")) continue - if(item.id == 995) continue - bot.bank.add(item) - } - bot.inventory.clear() - state = if(bot.bank.getAmount(food) < 10) - State.TO_GE - else - State.TO_DRAGONS - for(item in inventory) - bot.inventory.add(item) - scriptAPI.withdraw(food,10) - bot.fullRestore() - return true - } - }) - } - - State.BUYING_FOOD -> { - if(!offerMade) - { - scriptAPI.buyFromGE(bot, food, 100) - offerMade = true - } else - { - val offer = AIRepository.getOffer(bot) - if (offer == null) { - offerMade = false - } else { - if (offer.completedAmount == offer.amount) { - state = State.TO_DRAGONS - offer.offerState = OfferState.REMOVED - bot.bank.add(Item(offer.itemID, offer.completedAmount)) - bot.bank.refresh() - scriptAPI.withdraw(food, 10) - } - } - } - } - - State.TO_DRAGONS -> { - offerMade = false - if(bot.location.x >= 3143){ - if(bot.location != Location.create(3144, 3514, 0)) - scriptAPI.walkTo(Location.create(3144, 3514, 0)) - else { - val shortcut = scriptAPI.getNearestNode("Underwall Tunnel",true) - shortcut ?: return - shortcut.interaction.handle(bot,shortcut.interaction[0]) - } - } else { - if (!edgevilleLine.insideBorder(bot) && bot.location.y < 3520) { - scriptAPI.walkTo(edgevilleLine.randomLoc) - return - } - if (edgevilleLine.insideBorder(bot)) { - val ditch = scriptAPI.getNearestNode("Wilderness Ditch", true) - ditch ?: return - ditch.interaction.handle(bot, ditch.interaction[0]).also { return } - } - if (bot.location.y > 3520 && !myBorders!!.insideBorder(bot)) - scriptAPI.walkTo(myBorders!!.randomLoc).also { return } - if (myBorders!!.insideBorder(bot)) - state = State.KILLING - } - } - - State.TO_GE -> { - if(bot.location.x < 3143) { - if(bot.location == Location.create(3136, 3517, 0)){ - val shortcut = scriptAPI.getNearestNode("Underwall Tunnel",true) - shortcut ?: return - shortcut.interaction.handle(bot,shortcut.interaction[0]) - } else { - scriptAPI.walkTo(Location.create(3136, 3517, 0)) - } - return - } - if(bot.location != Location.create(3165, 3487, 0)) { - scriptAPI.walkTo(Location.create(3165, 3487, 0)) - } else { - state = State.SELL_GE - } - } - - State.SELL_GE -> { - scriptAPI.sellAllOnGe() - state = State.BUYING_FOOD - } - - State.REFRESHING -> { - running = false - return - } - - } - } - - override fun newInstance(): Script { - val script = GreenDragonKiller(style) - val tier = CombatBotAssembler.Tier.HIGH - if(type == CombatBotAssembler.Type.RANGE) - script.bot = CombatBotAssembler().assembleRangeDragonBot(tier,bot.startLocation) - else - script.bot = CombatBotAssembler().assembleMeleeDragonBot(tier, bot.startLocation) - return script - } - - enum class State { - KILLING, - RUNNING, - LOOTING, - LOOT_DELAYER, - BANKING, - TO_BANK, - TO_DRAGONS, - TO_GE, - SELL_GE, - REFRESHING, - BUYING_FOOD - - } - - init { - handler = when(style){ - CombatStyle.MELEE -> MeleeSwinger(this) - CombatStyle.MAGIC -> MageSwinger(this) - CombatStyle.RANGE -> RangeSwinger(this) - } - equipment.add(Item(Items.ANTI_DRAGON_SHIELD_1540)) - myBorders = westDragons - skills[Skills.AGILITY] = 99 - bankZone.addException(ZoneBorders(3094, 3492,3094, 3492)) - bankZone.addException(ZoneBorders(3094, 3490,3094, 3490)) - } - - internal class MeleeSwinger(val script: GreenDragonKiller) : MeleeSwingHandler() { - override fun canSwing(entity: Entity, victim: Entity): InteractionType? { - if(script.state == State.TO_BANK) {script.bot.pulseManager.current.stop()} - if(victim is Player) {script.state = State.RUNNING; script.bot.pulseManager.current.stop()} - return super.canSwing(entity, victim) - } - } - - internal class MageSwinger(val script: GreenDragonKiller) : MagicSwingHandler() { - override fun canSwing(entity: Entity, victim: Entity): InteractionType? { - if(victim is Player) {script.state = State.RUNNING; script.bot.pulseManager.current.stop()} - return super.canSwing(entity, victim) - } - } - - internal class RangeSwinger(val script: GreenDragonKiller) : RangeSwingHandler() { - override fun canSwing(entity: Entity, victim: Entity): InteractionType? { - if(victim is Player) {script.state = State.RUNNING; script.bot.pulseManager.current.stop()} - return super.canSwing(entity, victim) - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Idler.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Idler.kt deleted file mode 100644 index d7e08dce5..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Idler.kt +++ /dev/null @@ -1,10 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -class Idler : Script(){ - override fun tick() { - } - - override fun newInstance(): Script { - return this - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ManThiever.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ManThiever.kt deleted file mode 100644 index a0372a06b..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ManThiever.kt +++ /dev/null @@ -1,22 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -import core.game.node.item.Item -import rs09.game.ai.skillingbot.SkillingBotAssembler -import java.util.* - -class ManThiever : Script() { - override fun tick() { - val man = scriptAPI.getNearestNode("Man") - man?.interaction?.handle(bot, man.interaction[2]) - } - - override fun newInstance(): Script? { - val script = ManThiever() - script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR, bot.startLocation) - return script - } - - init { - equipment.addAll(Arrays.asList(Item(1103), Item(1139), Item(1265))) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/NonBankingMiner.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/NonBankingMiner.kt deleted file mode 100644 index 79d3bf9cd..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/NonBankingMiner.kt +++ /dev/null @@ -1,30 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -import core.game.node.entity.skill.Skills -import core.game.node.item.Item -import org.rs09.consts.Items -import rs09.game.ai.skillingbot.SkillingBotAssembler - -class NonBankingMiner : Script() { - override fun tick() { - val rock = scriptAPI.getNearestNode(11957,true) - if(rock != null){ - rock.interaction.handle(bot,rock.interaction[0]) - if(bot.inventory.isFull) - bot.inventory.clear() - } - } - - override fun newInstance(): Script { - val script = NonBankingMiner() - script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR,bot.startLocation) - return script - } - - init { - skills[Skills.ATTACK] = 99 - equipment.add(Item(Items.MITHRIL_PICKAXE_1273)) - inventory.add(Item(Items.MITHRIL_PICKAXE_1273)) - skills[Skills.MINING] = 50 - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/PlayerCompatible.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/PlayerCompatible.kt deleted file mode 100644 index 5415ad2d1..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/PlayerCompatible.kt +++ /dev/null @@ -1,3 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -annotation class PlayerCompatible diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/PlayerScripts.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/PlayerScripts.kt deleted file mode 100644 index 1aaba11e6..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/PlayerScripts.kt +++ /dev/null @@ -1,19 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -import io.github.classgraph.ClassGraph - -object PlayerScripts { - class PlayerScript(val identifier: String, val description: Array, val name: String, val clazz: Class<*>) - val identifierMap = HashMap() - - - fun init(){ - val result = ClassGraph().enableAnnotationInfo().acceptPackages("rs09.game.ai.general.scriptrepository").scan() - result.getClassesWithAnnotation("rs09.game.ai.general.scriptrepository.PlayerCompatible").forEach { res -> - val description = res.getAnnotationInfo("rs09.game.ai.general.scriptrepository.ScriptDescription").parameterValues[0].value as Array - val identifier = res.getAnnotationInfo("rs09.game.ai.general.scriptrepository.ScriptIdentifier").parameterValues[0].value.toString() - val name = res.getAnnotationInfo("rs09.game.ai.general.scriptrepository.ScriptName").parameterValues[0].value.toString() - identifierMap[identifier] = PlayerScript(identifier,description,name,res.loadClass()) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptDescription.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptDescription.kt deleted file mode 100644 index cef85addb..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptDescription.kt +++ /dev/null @@ -1,3 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -annotation class ScriptDescription(vararg val value: String) diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptIdentifier.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptIdentifier.kt deleted file mode 100644 index d73102c3f..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptIdentifier.kt +++ /dev/null @@ -1,3 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -annotation class ScriptIdentifier(val value: String) diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptName.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptName.kt deleted file mode 100644 index 7501d7c05..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/ScriptName.kt +++ /dev/null @@ -1,3 +0,0 @@ -package rs09.game.ai.general.scriptrepository - -annotation class ScriptName(val value: String) diff --git a/Server/src/main/kotlin/rs09/game/ai/pvmbots/PvMBotsBuilder.kt b/Server/src/main/kotlin/rs09/game/ai/pvmbots/PvMBotsBuilder.kt deleted file mode 100644 index 7eb8d151a..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/pvmbots/PvMBotsBuilder.kt +++ /dev/null @@ -1,515 +0,0 @@ -package rs09.game.ai.pvmbots - -import core.game.container.impl.EquipmentContainer -import core.game.node.entity.combat.CombatSpell -import core.game.node.entity.player.link.SpellBookManager -import core.game.node.entity.player.link.appearance.Gender -import core.game.node.item.Item -import rs09.game.world.GameWorld -import core.game.world.map.Location -import core.tools.RandomFunction -import rs09.game.ai.AIPlayer -import rs09.game.ai.minigamebots.pestcontrol.PestControlTestBot -import rs09.game.ai.minigamebots.pestcontrol.PestControlTestBot2 -import core.game.node.entity.skill.Skills -import java.util.* -import java.util.concurrent.Executors - -// import sun.util.resources.CalendarData; -class PvMBotsBuilder { - fun randomItem() { - var test: Item? = null - val tests = ArrayList() - for (x in 0..9998) test = Item(x) - if (test!!.definition.equipId != 0) tests.add(test) - } - - companion object { - var botsSpawned = 0 - - fun create(l: Location?): PvMBots { - botsSpawned++ - return PvMBots(l) - } - - fun createLowest(l: Location?): LowestBot { - botsSpawned++ - return LowestBot(l) - } - - fun createNoob(l: Location?): NoobBot { - botsSpawned++ - return NoobBot(l) - } - - fun createDragonKiller(l: Location?): DragonKiller { - botsSpawned++ - return DragonKiller(l) - } - - fun createGiantMoleBot(l: Location?): GiantMoleBot { - botsSpawned++ - return GiantMoleBot(l) - } - - fun generateGiantMoleBot(p: PvMBots) { - p.skills.setLevel(Skills.SLAYER, 99) - p.skills.setStaticLevel(Skills.SLAYER, 99) - p.skills.setLevel(Skills.STRENGTH, 99) - p.skills.setStaticLevel(Skills.STRENGTH, 99) - p.skills.setLevel(Skills.ATTACK, 99) - p.skills.setStaticLevel(Skills.ATTACK, 99) - p.skills.setStaticLevel(Skills.HITPOINTS, 99) - p.skills.setLevel(Skills.HITPOINTS, 99) - p.skills.setStaticLevel(Skills.DEFENCE, 99) - p.skills.setLevel(Skills.DEFENCE, 99) - p.skills.setStaticLevel(Skills.PRAYER, 99) - p.skills.setLevel(Skills.PRAYER, 99) - p.skills.updateCombatLevel() - p.appearance.sync() - p.equipment.replace(Item(4720), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(4722), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(4716), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(4718), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(-1), EquipmentContainer.SLOT_SHIELD) - p.inventory.add(Item(952)) - p.inventory.add(Item(33)) - } - - fun generateMinLevels(p: PvMBots) { - //Slayer so they can attack all monsters - p.skills.setLevel(Skills.SLAYER, 99) - p.skills.setStaticLevel(Skills.SLAYER, 99) - val combatType = RandomFunction.getRandom(2) - p.skills.setLevel(Skills.HITPOINTS, 15) - p.skills.setStaticLevel(Skills.HITPOINTS, 15) - p.inventory.add(Item(329, 5)) - when (combatType) { - 0 -> { - buildArcherStats(p) - buildArcherEquipment(p) - } - 1 -> { - buildMeleeStats(p) - buildMeleeEquipment(p) - } - 2 -> { - buildMageStats(p) - buildMagicEquipment(p) - setupWizard(p) - } - else -> { - buildMeleeStats(p) - buildMeleeEquipment(p) - } - } - } - - fun createDragonKiller(p: DragonKiller) { - p.skills.setLevel(Skills.SLAYER, 99) - p.skills.setStaticLevel(Skills.SLAYER, 99) - val combatType = RandomFunction.getRandom(2) - p.skills.setLevel(Skills.HITPOINTS, 55) - p.skills.setStaticLevel(Skills.HITPOINTS, 55) - when (combatType) { - 0 -> { - buildDragonArcherStats(p) - buildDragonArcherEquipment(p) - } - 1 -> { - buildDragonMeleeStats(p) - buildDragonMeleeEquipment(p) - } - else -> { - buildDragonMeleeStats(p) - buildDragonMeleeEquipment(p) - } - } - } - - private fun buildMaxMeleeStats(p: AIPlayer) { - p.skills.setLevel(Skills.ATTACK, 99) - p.skills.setStaticLevel(Skills.ATTACK, 99) - p.skills.setLevel(Skills.STRENGTH, 99) - p.skills.setStaticLevel(Skills.STRENGTH, 99) - p.skills.setLevel(Skills.DEFENCE, 99) - p.skills.setStaticLevel(Skills.DEFENCE, 99) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - private fun buildMaxMeleeEquipment(p: AIPlayer) { - val helms = intArrayOf( - 3751) - val shield = intArrayOf( - 14767) - val platebody = intArrayOf( - 11724) - val platelegs = intArrayOf( - 11726) - val amulets = intArrayOf( - 6585) - val boots = intArrayOf( - 3105) - val cape = intArrayOf( - 6570) - val gloves = intArrayOf( - 7462) - val weapons2 = intArrayOf( - 4151) - val arrows = intArrayOf( - 892) - p.equipment.replace(Item(helms[RandomFunction.random(helms.size)]), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(shield[RandomFunction.random(shield.size)]), EquipmentContainer.SLOT_SHIELD) - p.equipment.replace(Item(platebody[RandomFunction.random(platebody.size)]), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(platelegs[RandomFunction.random(platelegs.size)]), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(weapons2[RandomFunction.random(weapons2.size)]), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(weapons2[RandomFunction.random(arrows.size)], 99999), EquipmentContainer.SLOT_ARROWS) - p.equipment.replace(Item(amulets[RandomFunction.random(amulets.size)]), EquipmentContainer.SLOT_AMULET) - p.equipment.replace(Item(boots[RandomFunction.random(boots.size)]), EquipmentContainer.SLOT_FEET) - p.equipment.replace(Item(cape[RandomFunction.random(cape.size)]), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(gloves[RandomFunction.random(gloves.size)]), EquipmentContainer.SLOT_HANDS) - } - - fun createNoob(p: NoobBot) { - p.skills.setLevel(Skills.SLAYER, 99) - p.skills.setStaticLevel(Skills.SLAYER, 99) - val combatType = RandomFunction.getRandom(3) - p.skills.setLevel(Skills.HITPOINTS, 10 + RandomFunction.getRandom(10)) - p.skills.setStaticLevel(Skills.HITPOINTS, 10 + RandomFunction.getRandom(10)) - p.skills.setLevel(Skills.DEFENCE, 1 + RandomFunction.getRandom(9)) - p.skills.setStaticLevel(Skills.DEFENCE, 1 + RandomFunction.getRandom(9)) - p.inventory.add(Item(329, 5)) - when (combatType) { - 0 -> { - buildNoobArcherStats(p) - buildNoobArcherEquipment(p) - } - 1 -> { - buildNoobMeleeStats(p) - buildNoobMeleeEquipment(p) - } - 2 -> { - run { - buildNoobMageStats(p) - buildNoobMagicEquipment(p) - setupWizard(p) - } - run { - buildNoobMeleeStats(p) - buildNoobMeleeEquipment(p) - } - } - else -> { - buildNoobMeleeStats(p) - buildNoobMeleeEquipment(p) - } - } - } - - private fun buildArcherStats(p: PvMBots) { - p.skills.setLevel(Skills.RANGE, 10) - p.skills.setStaticLevel(Skills.RANGE, 10) - p.skills.setLevel(Skills.DEFENCE, 10) - p.skills.setStaticLevel(Skills.DEFENCE, 10) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - private fun buildDragonArcherStats(p: DragonKiller) { - p.skills.setStaticLevel(Skills.RANGE, 55 + RandomFunction.getRandom(15)) - p.skills.setLevel(Skills.RANGE, p.skills.getLevel(Skills.RANGE)) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - private fun buildNoobArcherStats(p: NoobBot) { - p.skills.setLevel(Skills.RANGE, 1 + RandomFunction.getRandom(10)) - p.skills.setStaticLevel(Skills.RANGE, 1 + RandomFunction.getRandom(10)) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - fun buildMeleeStats(p: PvMBots) { - p.skills.setLevel(Skills.ATTACK, 10) - p.skills.setStaticLevel(Skills.ATTACK, 10) - p.skills.setLevel(Skills.STRENGTH, 10) - p.skills.setStaticLevel(Skills.STRENGTH, 10) - p.skills.setLevel(Skills.DEFENCE, 10) - p.skills.setStaticLevel(Skills.DEFENCE, 10) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - fun buildDragonMeleeStats(p: DragonKiller) { - p.skills.setStaticLevel(Skills.ATTACK, 55 + RandomFunction.getRandom(15)) - p.skills.setLevel(Skills.ATTACK, p.skills.getLevel(Skills.ATTACK)) - p.skills.setStaticLevel(Skills.STRENGTH, 55 + RandomFunction.getRandom(15)) - p.skills.setLevel(Skills.STRENGTH, p.skills.getLevel(Skills.STRENGTH)) - p.skills.setLevel(Skills.DEFENCE, 45 + RandomFunction.getRandom(15)) - p.skills.setLevel(Skills.DEFENCE, p.skills.getLevel(Skills.DEFENCE)) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - private fun buildNoobMeleeStats(p: NoobBot) { - p.skills.setLevel(Skills.ATTACK, 1 + RandomFunction.getRandom(10)) - p.skills.setStaticLevel(Skills.ATTACK, 1 + RandomFunction.getRandom(10)) - p.skills.setLevel(Skills.STRENGTH, 1 + RandomFunction.getRandom(10)) - p.skills.setStaticLevel(Skills.STRENGTH, 1 + RandomFunction.getRandom(10)) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - private fun buildMageStats(p: PvMBots) { - p.skills.setLevel(Skills.MAGIC, 10) - p.skills.setStaticLevel(Skills.MAGIC, 10) - p.skills.setLevel(Skills.DEFENCE, 10) - p.skills.setStaticLevel(Skills.DEFENCE, 10) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - private fun buildNoobMageStats(p: NoobBot) { - p.skills.setLevel(Skills.MAGIC, 1 + RandomFunction.getRandom(10)) - p.skills.setStaticLevel(Skills.MAGIC, 1 + RandomFunction.getRandom(10)) - p.skills.updateCombatLevel() - p.appearance.sync() - } - - private fun setupWizard(p: PvMBots) { - //final int SPELL_IDS[] = new int[] {1, 4, 6, 8, 10, 14, 17, 20, 24, 27, 33, 38, 45, 48, 52, 55 }; - p.properties.autocastSpell = SpellBookManager.SpellBook.MODERN.getSpell(1) as CombatSpell - p.inventory.add(Item(556, 1000)) //Air runes - p.inventory.add(Item(558, 1000)) //Mind runes - } - - private fun buildArcherEquipment(p: PvMBots) { - p.equipment.replace(Item(1169), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(1129), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(1095), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(841), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(884, 5000), EquipmentContainer.SLOT_ARROWS) - p.equipment.replace(Item(1007), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(1478), EquipmentContainer.SLOT_AMULET) - } - - private fun buildDragonArcherEquipment(p: DragonKiller) { - p.equipment.replace(Item(1169), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(13483), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(1099), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(9185), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(1540), EquipmentContainer.SLOT_SHIELD) - p.equipment.replace(Item(9140, 500), EquipmentContainer.SLOT_ARROWS) - p.equipment.replace(Item(10498), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(1478), EquipmentContainer.SLOT_AMULET) - } - - private fun buildNoobArcherEquipment(p: PvMBots) { - val hats = intArrayOf(1169, 1169, 1139, 1137, 1153, 579) - val legs = intArrayOf(1095, 7366, 1075, 1087, 1095) - val chest = intArrayOf(1129, 1103, 1101, 1129, 1117, 577) - p.equipment.replace(Item(hats[RandomFunction.getRandom(hats.size - 1)]), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(chest[RandomFunction.getRandom(chest.size - 1)]), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(legs[RandomFunction.getRandom(legs.size - 1)]), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(841), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(884, 5000), EquipmentContainer.SLOT_ARROWS) - p.equipment.replace(Item(1007), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(1478), EquipmentContainer.SLOT_AMULET) - } - - private fun buildMeleeEquipment(p: PvMBots) { - p.equipment.replace(Item(1153), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(1115), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(1067), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(1309), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(884), EquipmentContainer.SLOT_ARROWS) - p.equipment.replace(Item(1007), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(1725), EquipmentContainer.SLOT_AMULET) - } - - private fun buildDragonMeleeEquipment(p: DragonKiller) { - p.equipment.replace(Item(1163), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(1127), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(1079), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(1333), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(1540), EquipmentContainer.SLOT_SHIELD) - p.equipment.replace(Item(1007), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(1725), EquipmentContainer.SLOT_AMULET) - } - - private fun buildNoobMeleeEquipment(p: PvMBots) { - val hats = intArrayOf(1169, 1169, 1139, 1137, 1153, 579) - val legs = intArrayOf(1095, 7366, 1075, 1087, 1095) - val chest = intArrayOf(1129, 1103, 1101, 1129, 1117, 577) - val weapons = intArrayOf(1307, 1321, 1375, 1203, 1239, 1267, 1293, 1323, 1335) - p.equipment.replace(Item(hats[RandomFunction.getRandom(hats.size - 1)]), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(chest[RandomFunction.getRandom(chest.size - 1)]), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(legs[RandomFunction.getRandom(legs.size - 1)]), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(weapons[RandomFunction.getRandom(weapons.size - 1)]), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(884), EquipmentContainer.SLOT_ARROWS) - p.equipment.replace(Item(1007), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(1725), EquipmentContainer.SLOT_AMULET) - } - - private fun buildMagicEquipment(p: PvMBots) { - p.equipment.replace(Item(579), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(577), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(1011), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(1389), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(884), EquipmentContainer.SLOT_ARROWS) - p.equipment.replace(Item(1007), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(1727), EquipmentContainer.SLOT_AMULET) - } - - private fun buildNoobMagicEquipment(p: PvMBots) { - val hats = intArrayOf(1169, 1169, 1139, 1137, 1153, 579) - val legs = intArrayOf(1095, 7366, 1075, 1087, 1095) - val chest = intArrayOf(1129, 1103, 1101, 1129, 1117, 577) - val weapons = intArrayOf(1379, 1383, 1385, 1387, 1389) - p.equipment.replace(Item(hats[RandomFunction.getRandom(hats.size - 1)]), EquipmentContainer.SLOT_HAT) - p.equipment.replace(Item(chest[RandomFunction.getRandom(chest.size - 1)]), EquipmentContainer.SLOT_CHEST) - p.equipment.replace(Item(legs[RandomFunction.getRandom(legs.size - 1)]), EquipmentContainer.SLOT_LEGS) - p.equipment.replace(Item(weapons[RandomFunction.getRandom(weapons.size - 1)]), EquipmentContainer.SLOT_WEAPON) - p.equipment.replace(Item(884), EquipmentContainer.SLOT_ARROWS) - p.equipment.replace(Item(1007), EquipmentContainer.SLOT_CAPE) - p.equipment.replace(Item(1727), EquipmentContainer.SLOT_AMULET) - } - - fun spawn(loc: Location?) { - val bot = create(loc) - bot.appearance.gender = if (RandomFunction.random(3) == 1) Gender.FEMALE else Gender.MALE - } - - fun spawnLowest(loc: Location?) { - val bot = createLowest(loc) - generateMinLevels(bot) - } - - fun spawnNoob(loc: Location?) { - val bot = createNoob(loc) - createNoob(bot) - } - - fun spawnDragonKiller(loc: Location?) { - val bot = createDragonKiller(loc) - bot.appearance.gender = if (RandomFunction.random(3) == 1) Gender.FEMALE else Gender.MALE - createDragonKiller(bot) - } - - fun spawnGiantMoleBot(loc: Location?) { - val bot = createGiantMoleBot(Location(0, 0)) - bot.teleport(loc) - bot.appearance.gender = if (RandomFunction.random(3) == 1) Gender.FEMALE else Gender.MALE - generateGiantMoleBot(bot) - } - - @JvmStatic - fun immersiveSpawns() { - Executors.newSingleThreadExecutor().execute { - /*//Lumbridge - //GOBLINS - spawnLowest(new Location(3259, 3224)); - spawnLowest(new Location(3251, 3235)); - spawnLowest(new Location(3244, 3247)); - spawnNoob(new Location(3244, 3247)); - //COWS - spawnLowest(new Location(3259, 3245)); - spawnLowest(new Location(3258, 3261)); - spawnLowest(new Location(3263, 3263)); - spawnLowest(new Location(3263, 3271)); - spawnLowest(new Location(3254, 3288)); - //CHICKENS - spawnLowest(new Location(3234, 3294)); - spawnLowest(new Location(3229, 3297)); - //GOBLINS2 - spawnLowest(new Location(3182, 3255)); - spawnLowest(new Location(3169, 3254)); - spawnLowest(new Location(3167, 3233)); - spawnLowest(new Location(3152, 3228));*/ - //Lumby castle - spawnLowest(Location(3221, 3219)) - /* spawnNoob(new Location(3193, 3208)); - //Lumby swamp - spawnNoob(new Location(3224, 3186)); - spawnNoob(new Location(3217, 3177)); - spawnNoob(new Location(3209, 3191)); - - //Edge - spawnLowest(new Location(3096, 3509)); - - //Edge dungeon - //Hill giants - spawnLowest(new Location(3118, 9848)); - spawnLowest(new Location(3110, 9842)); - spawnLowest(new Location(3115, 9836)); - spawnLowest(new Location(3107, 9832)); - spawnLowest(new Location(3099, 9834)); - //brass key room - spawnLowest(new Location(3127, 9863)); - - //Dark wizzards - spawnLowest(new Location(3226, 3367)); - spawnLowest(new Location(3226, 3368)); - spawnLowest(new Location(3230, 3373)); - spawnLowest(new Location(3209, 3378)); - - //Varrock palace - spawnLowest(new Location(3218, 3465)); - spawnLowest(new Location(3209, 3462)); - - //Varrock Sewers - spawnLowest(new Location(3235, 9868)); - spawnLowest(new Location(3242, 9915)); - spawnLowest(new Location(3176, 9883)); - - //Dragons Wilderness - //WEST - spawnDragonKiller(new Location(2977, 3614)); - spawnDragonKiller(new Location(2987, 3619)); - - //Burthrope Dungeon - //Blue dragons - spawnDragonKiller(new Location(2901, 9799)); - spawnDragonKiller(new Location(2910, 9804)); - //Al kharid - //palace - spawnLowest(new Location(3301, 3173)); - spawnNoob(new Location(3293, 3170)); - spawnNoob(new Location(3286, 3171)); - spawnNoob(new Location(3289, 3169)); - - //Slayer Tower - spawnLowest(new Location(3412, 3173)); - spawnNoob(new Location(3412, 3539)); - spawnNoob(new Location(3420, 3545)); - spawnNoob(new Location(3420, 3545)); - spawnNoob(new Location(3420, 3558)); - spawnNoob(new Location(3433, 3571)); - //Replace with something like rune bots - spawnDragonKiller(new Location(3437, 3562, 1)); - spawnDragonKiller(new Location(3437, 3562, 1)); - spawnDragonKiller(new Location(3437, 3562, 1)); - //Bloodvelds - spawnDragonKiller(new Location(3417, 3561, 1)); - spawnDragonKiller(new Location(3417, 3561, 1)); - spawnDragonKiller(new Location(3417, 3561, 1)); - //Brimhaven dragons - spawnDragonKiller(new Location(2704, 9450)); - spawnDragonKiller(new Location(2704, 9450));*/println("[" + GameWorld.settings?.name + "]: PvMBotsBuilder: Spawned " + botsSpawned + " bots.") - } - } - - @JvmStatic - fun createPestControlTestBot2(l: Location?): PestControlTestBot2 { - botsSpawned++ - return PestControlTestBot2(l!!) - } - @JvmStatic - fun createPestControlTestBot(l: Location?): PestControlTestBot { - botsSpawned++ - return PestControlTestBot(l!!) - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/skillingbot/SkillingBotAssembler.kt b/Server/src/main/kotlin/rs09/game/ai/skillingbot/SkillingBotAssembler.kt deleted file mode 100644 index 2920d370b..000000000 --- a/Server/src/main/kotlin/rs09/game/ai/skillingbot/SkillingBotAssembler.kt +++ /dev/null @@ -1,45 +0,0 @@ -package rs09.game.ai.skillingbot - -import core.game.node.item.Item -import core.game.world.map.Location -import rs09.game.ai.AIPlayer - -class SkillingBotAssembler { - fun produce(type: Wealth,loc: Location): AIPlayer{ - return assembleBot(AIPlayer(loc),type) - } - - fun assembleBot(bot: AIPlayer, type: Wealth): AIPlayer{ - return when(type){ - Wealth.POOR -> equipSet(bot,POORSETS.random()) - Wealth.AVERAGE -> equipSet(bot,AVGSETS.random()) - Wealth.RICH -> equipSet(bot,RICHSETS.random()) - } - } - - fun equipSet(bot: AIPlayer,set: List): AIPlayer{ - for(i in set){ - val item = Item(i) - val configs = item.definition.handlers - val slot = configs["equipment_slot"] ?: continue - bot.equipment.add(item, slot as Int, - false,false) - val reqs = configs["requirements"] - if(reqs != null) - for(req in configs["requirements"] as HashMap) - bot.skills.setStaticLevel(req.key, req.value) - } - bot.skills.updateCombatLevel() - return bot - } - - enum class Wealth { - POOR, - AVERAGE, - 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 diff --git a/Server/src/main/kotlin/rs09/game/camerautils/CameraUtils.kt b/Server/src/main/kotlin/rs09/game/camerautils/CameraUtils.kt deleted file mode 100644 index e238c240f..000000000 --- a/Server/src/main/kotlin/rs09/game/camerautils/CameraUtils.kt +++ /dev/null @@ -1,5 +0,0 @@ -package rs09.game.camerautils - -object CameraUtils { - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/camerautils/PlayerCamera.kt b/Server/src/main/kotlin/rs09/game/camerautils/PlayerCamera.kt deleted file mode 100644 index 2c8a17fef..000000000 --- a/Server/src/main/kotlin/rs09/game/camerautils/PlayerCamera.kt +++ /dev/null @@ -1,33 +0,0 @@ -package rs09.game.camerautils - -import core.game.node.entity.player.Player -import rs09.game.system.SystemLogger -import core.net.packet.PacketRepository -import core.net.packet.context.CameraContext -import core.net.packet.out.CameraViewPacket - -class PlayerCamera(val player: Player?) { - var ctx: CameraContext? = null - - fun setPosition(x: Int, y: Int, height: Int){ - player ?: return - ctx = CameraContext(player,CameraContext.CameraType.SET,x,y,height,0,0) - PacketRepository.send(CameraViewPacket::class.java,ctx) - } - fun rotateTo(x: Int, y: Int, height: Int, speed: Int){ - player ?: return - ctx = CameraContext(player,CameraContext.CameraType.ROTATION,x,y,height,speed,1) - PacketRepository.send(CameraViewPacket::class.java,ctx) - } - fun rotateBy(diffX: Int, diffY: Int, diffHeight: Int, speed: Int){ - player ?: return - ctx ?: return - ctx = CameraContext(player,CameraContext.CameraType.ROTATION, ctx!!.x + diffX, ctx!!.y + diffY, ctx!!.height + diffHeight,speed,1) - PacketRepository.send(CameraViewPacket::class.java,ctx) - } - fun panTo(x: Int, y: Int, height: Int, speed: Int){ - player ?: return - ctx = CameraContext(player,CameraContext.CameraType.POSITION,x,y,height,speed,1) - PacketRepository.send(CameraViewPacket::class.java,ctx) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFUBeacon.kt b/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFUBeacon.kt deleted file mode 100644 index a3193dbe2..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFUBeacon.kt +++ /dev/null @@ -1,73 +0,0 @@ -package rs09.game.content.activity.allfiredup - -import core.game.node.entity.player.Player -import rs09.game.system.SystemLogger -import core.game.world.map.Location - -/** - * Various data for beacons, such as varp and offset, required FM level, etc - * @author Ceikry - */ -enum class AFUBeacon(val title: String, val fmLevel: Int, val varpId: Int, val offset: Int, val location: Location, val experience: Double, val keeper: Int = 0) { - RIVER_SALVE("",43,1283,0,Location.create(3396, 3464, 0),216.2,8065), - RAG_AND_BONE("",43,1283,3,Location.create(3343, 3510, 0),235.8,8066), - JOLLY_BOAR("",48,1283,6,Location.create(3278, 3525, 0), 193.8,8067), - NORTH_VARROCK_CASTLE("",53,1283,9,Location.create(3236, 3527, 0),178.5,8068), - GRAND_EXCHANGE("",59,1283,12,Location.create(3170, 3536, 0),194.3,8069), - EDGEVILLE("",62,1283,15,Location.create(3087, 3516, 0),86.7,8070), - MONASTERY("",68,1283,18,Location.create(3034, 3518, 0),224.4,8071), - GOBLIN_VILLAGE("",72,1283,21,Location.create(2968, 3516, 0),194.8,8072), - BURTHORPE("",76,1283,24,Location.create(2940, 3565, 0),195.3,8073), - DEATH_PLATEAU("",79,1288,0,Location.create(2944, 3622, 0),249.9,8074), - TROLLHEIM("",83,1288,3,Location.create(2939, 3680, 0),201.0,8075), - GWD("",87,1288,6,Location.create(2937, 3773, 0),255.0,8076), - TEMPLE("",89,1288,9,Location.create(2946, 3836, 0),198.9), - PLATEAU("",92,1288,12,Location.create(2964, 3931, 0),147.9); - - companion object { - fun forLocation(location: Location): AFUBeacon { - for (beacon in values()) { - if (beacon.location.equals(location)) return beacon - } - return RIVER_SALVE.also { SystemLogger.logWarn("Unhandled Beacon Location ${location.toString()}") } - } - - fun resetAllBeacons(player: Player){ - for(beacon in values()){ - player.varpManager.get(beacon.varpId).setVarbit(beacon.offset,0).send(player) - } - } - } - - fun light(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,2).send(player) - } - - fun diminish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,3).send(player) - } - - fun extinguish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,0).send(player) - } - - fun lightGnomish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,4).send(player) - } - - fun fillWithLogs(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,1).send(player) - } - - fun getState(player: Player): BeaconState{ - return BeaconState.values()[player.varpManager.get(varpId).getVarbit(offset)?.value ?: 0] - } -} - -enum class BeaconState{ - EMPTY, - FILLED, - LIT, - DYING, - WARNING -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFUBeaconHandler.kt b/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFUBeaconHandler.kt deleted file mode 100644 index 4f9b76d29..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFUBeaconHandler.kt +++ /dev/null @@ -1,216 +0,0 @@ -package rs09.game.content.activity.allfiredup - -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.entity.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 rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld - -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) -private val LIGHT_ANIM = Animation(7307) - -/** - * Handles interactions for beacons - * @author Ceikry - */ -class AFUBeaconListeners : InteractionListener(){ - - override fun defineListeners() { - on(SCENERY,"add-logs","light"){ player, node -> - val beacon = AFUBeacon.forLocation(node.location) - val questComplete = player.questRepository.isComplete("All Fired Up") - val questStage = player.questRepository.getStage("All Fired Up") - - 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, FacialExpression.THINKING, "I probably shouldn't mess with this.") - return@on true - } - player.debug(beacon.getState(player).name) - - when (beacon.getState(player)) { - BeaconState.EMPTY -> fillBeacon(player, beacon, questComplete) - - BeaconState.DYING -> restoreBeacon(player, beacon, questComplete) - - BeaconState.FILLED -> lightBeacon(player, beacon, questComplete) - - BeaconState.LIT, BeaconState.WARNING -> { - player.debug("INVALID BEACON STATE") - } - } - return@on true - } - } - - fun fillBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean){ - - when(beacon){ - AFUBeacon.MONASTERY -> { - if(player.skills.getLevel(Skills.PRAYER) < 31){ - player.dialogueInterpreter.sendDialogues(NPC(beacon.keeper).getShownNPC(player),FacialExpression.ANGRY,"You must join the monastery to light this beacon!") - return - } - } - - AFUBeacon.GWD -> { - if(!AFURepairClimbHandler.isRepaired(player,beacon)){ - player.dialogueInterpreter.sendDialogue("You must repair the windbreak before you","can light this beacon.") - return - } - } - - AFUBeacon.GOBLIN_VILLAGE -> { - if(!player.questRepository.isComplete("Lost Tribe")){ - player.dialogueInterpreter.sendDialogues(NPC(beacon.keeper).getShownNPC(player),FacialExpression.THINKING,"We no trust you outsider. You no light our beacon.","(Complete Lost Tribe to use this beacon.)") - return - } - } - - } - - if(player.skills.getLevel(Skills.FIREMAKING) < beacon.fmLevel){ - player.dialogueInterpreter.sendDialogue("You need ${beacon.fmLevel} Firemaking to light this beacon.") - return - } - - 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() - } - } - - 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: AFUBeacon, questComplete: Boolean){ - var session: AFUSession? = null - if(questComplete){ - session = player.getAttribute("afu-session",null) - if(session == null) return - } - - 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 - } - }) - } else { - player.dialogueInterpreter.sendDialogue("You need a tinderbox to light this.") - } - } - - fun restoreBeacon(player: Player,beacon: AFUBeacon, questComplete: Boolean){ - var session: AFUSession? = null - if(questComplete){ - session = player.getAttribute("afu-session",null) - if(session == null) return - } - - 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 - } - }) - } else { - player.dialogueInterpreter.sendDialogue("You need some logs to do this.") - } - } - - fun getLogs(player: Player, amount: Int): Item{ - var logId = 0 - for (log in VALID_LOGS) if (player.inventory.getAmount(log) >= amount) {logId = log; break} - return Item(logId,amount) - } - - fun Player.hasFireRing(): Boolean{ - return inventory.containsItem(Item(Items.RING_OF_FIRE_13659)) || bank.containsItem(Item(Items.RING_OF_FIRE_13659)) || equipment.containsItem(Item(Items.RING_OF_FIRE_13659)) - } - - fun Player.hasFlameGloves(): Boolean{ - return inventory.containsItem(Item(Items.FLAME_GLOVES_13660)) || bank.containsItem(Item(Items.FLAME_GLOVES_13660)) || equipment.containsItem(Item(Items.FLAME_GLOVES_13660)) - } - - fun Player.hasInfernoAdze(): Boolean{ - 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/kotlin/rs09/game/content/activity/allfiredup/AFURepairClimbHandler.kt b/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFURepairClimbHandler.kt deleted file mode 100644 index 322c2c8a4..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFURepairClimbHandler.kt +++ /dev/null @@ -1,140 +0,0 @@ -package rs09.game.content.activity.allfiredup - -import core.game.node.entity.impl.ForceMovement -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.construction.NailType -import core.game.node.item.Item -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 rs09.game.interaction.InteractionListener -import java.util.* - -/** - * Handles repairing and climbing of the 3 beacon shortcuts needed to access them - * @author Ceikry - */ -class AFURepairClimbHandler : InteractionListener() { - - val repairIDs = intArrayOf(38480,38470,38494) - val climbIDs = intArrayOf(38469,38471,38486,38481,38469) - - override fun defineListeners() { - - on(repairIDs,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 - } - - on(climbIDs,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) - return@on true - } - - } - - private fun repair(player: Player,rco: RepairClimbObject){ - val skill = rco.levelRequirement?.first ?: 0 - val level = rco.levelRequirement?.second ?: 0 - if(player.skills.getLevel(skill) < level){ - player.dialogueInterpreter.sendDialogue("You need level $level ${Skills.SKILL_NAME[skill]} for this.") - return - } - - var requiresNeedle = false - - val requiredItems = when(rco){ - RepairClimbObject.DEATH_PLATEAU -> { - arrayOf(Item(Items.PLANK_960,2)) - } - - RepairClimbObject.BURTHORPE -> { - arrayOf(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)) - } - } - - 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.") - return - } - } else { - if(player.inventory.containsItem(Item(Items.HAMMER_2347)) && player.inventory.containItems(*requiredItems.map { it.id }.toIntArray())) { - val nails = NailType.get(player,4) - if(nails == null && rco == RepairClimbObject.DEATH_PLATEAU){ - player.dialogueInterpreter.sendDialogue("You need 4 nails for this.") - return - } else if (rco == RepairClimbObject.DEATH_PLATEAU){ - player.inventory.remove(Item(nails.itemId,4)) - } - player.inventory.remove(*requiredItems) - } else { - player.dialogueInterpreter.sendDialogue("You need a hammer and ${requiredItems.map { "${it.amount} ${it.name.toLowerCase()}s" }.toString().replace("[","").replace("]","")} for this.") - return - } - } - - player.varpManager.get(rco.varp).setVarbit(rco.offset,1).send(player) - player.varpManager.flagSave(rco.varp) - } - - 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)); - - fun getOtherLocation(player: Player): Location?{ - if(player.location == destinationDown) return destinationUp - else return destinationDown - } - - fun getAnimation(player: Player): Animation { - if(getOtherLocation(player) == destinationDown) return Animation(1148) - else return Animation(740) - } - - fun getDirection(player: Player): Direction { - if(this == BURTHORPE){ - return Direction.EAST - } else return Direction.WEST - } - - fun isRepaired(player: Player): Boolean{ - return player.varpManager.get(varp).getVarbit(offset)?.value == 1 - } - } - - companion object { - 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/kotlin/rs09/game/content/activity/allfiredup/AFUSession.kt b/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFUSession.kt deleted file mode 100644 index a61e618d7..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/allfiredup/AFUSession.kt +++ /dev/null @@ -1,130 +0,0 @@ -package rs09.game.content.activity.allfiredup - -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import core.plugin.Plugin -import org.rs09.consts.Items -import rs09.tools.stringtools.colorize - -/** - * Handles keeping track of lit beacons and their burn time remaining - * @author Ceikry - */ -class AFUSession(val player: Player) { - private val beaconTimers = Array(14){i -> BeaconTimer(0,AFUBeacon.values()[i])} - private val logInventories = Array(14){Item(0,0)} - private val beaconWatched = Array(14){false} - private var isActive = false - - 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) - player.logoutPlugins.add(AFULogoutPlugin()) - } - - 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: 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 - } - } - - internal class BeaconTimer(var ticks: Int, val beacon: AFUBeacon) - - class AFULogoutPlugin: Plugin { - override fun newInstance(arg: Player?): Plugin { - arg ?: return this - AFUBeacon.resetAllBeacons(arg) - val session: AFUSession? = arg.getAttribute("afu-session",null) - session?.end() - arg.removeAttribute("afu-session") - 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/kotlin/rs09/game/content/activity/barrows/RewardChest.kt b/Server/src/main/kotlin/rs09/game/content/activity/barrows/RewardChest.kt deleted file mode 100644 index 7e6d25d31..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/barrows/RewardChest.kt +++ /dev/null @@ -1,103 +0,0 @@ -package rs09.game.content.activity.barrows - -import api.ContentAPI -import core.game.component.Component -import core.game.container.access.InterfaceContainer -import core.game.content.global.BossKillCounter -import core.game.node.entity.player.Player -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.tools.RandomFunction -import org.rs09.consts.Components -import org.rs09.consts.Items -import rs09.game.content.global.WeightBasedTable -import rs09.game.content.global.WeightedItem -import java.util.* - -/** - * The reward chest. - * @author Ceikry - * and slightly kermit - */ -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) - ) - - /** - * Rewards the player. - * - * @param player The player. - */ - @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 - } - } - val rewards: MutableList = ArrayList() - var maxRolls = 2 + RandomFunction.random(1,4) - for (i in 0 until maxRolls) { - rewards.addAll(DROP_TABLE.roll()) - } - 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){ - ContentAPI.announceIfRare(player, item); - if(!player.inventory.add(item)){ - GroundItemManager.create(item,player) - } - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/TrawlerLoot.kt b/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/TrawlerLoot.kt deleted file mode 100644 index a8ef7ee0f..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/fishingtrawler/TrawlerLoot.kt +++ /dev/null @@ -1,56 +0,0 @@ -package rs09.game.content.activity.fishingtrawler - -import core.game.node.item.Item -import core.game.node.item.WeightedChanceItem -import core.tools.RandomFunction -import org.rs09.consts.Items - -/** - * Rolls/stores the loot table for fishing trawler - * @author Ceikry - */ -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) - @JvmStatic - fun getLoot(rolls: Int, skipJunk: Boolean): ArrayList{ - val loot = ArrayList() - for(i in 0 until rolls){ - loot.add(RandomFunction.rollWeightedChanceTable(listOf(*lootTable))) - } - 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( - WeightedChanceItem(Items.RAW_SHRIMPS_317,1,150), - WeightedChanceItem(Items.RAW_SARDINE_327,1,150), - WeightedChanceItem(Items.RAW_ANCHOVIES_321,1,150), - WeightedChanceItem(Items.RAW_TUNA_359,1,140), - WeightedChanceItem(Items.RAW_LOBSTER_377,1,140), - WeightedChanceItem(Items.RAW_SWORDFISH_371,1,130), - WeightedChanceItem(Items.RAW_SHARK_383,1,120), - WeightedChanceItem(Items.RAW_SEA_TURTLE_395,1,120), - WeightedChanceItem(Items.RAW_MANTA_RAY_389,1,120), - WeightedChanceItem(Items.BROKEN_ARROW_687,1,70), - WeightedChanceItem(Items.BROKEN_GLASS_1469,1,70), - WeightedChanceItem(Items.BROKEN_STAFF_689,1,70), - WeightedChanceItem(Items.BUTTONS_688,1,80), - WeightedChanceItem(Items.DAMAGED_ARMOUR_697,1,70), - WeightedChanceItem(Items.OLD_BOOT_685,1,60), - WeightedChanceItem(Items.OYSTER_407,1,50), - WeightedChanceItem(Items.EMPTY_POT_1931,1,50), - WeightedChanceItem(Items.RUSTY_SWORD_686,1,50), - //Inauthentic rewards - WeightedChanceItem(Items.LOOP_HALF_OF_A_KEY_987,1,40), - WeightedChanceItem(Items.TOOTH_HALF_OF_A_KEY_985,1,40), - WeightedChanceItem(Items.CASKET_405,1,60), - WeightedChanceItem(Items.PIRATES_HAT_2651,1,2), - WeightedChanceItem(Items.LUCKY_CUTLASS_7140,1,2) - ) -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeCookingConstants.kt b/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeCookingConstants.kt deleted file mode 100644 index b974b25ae..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/gnomecooking/GnomeCookingConstants.kt +++ /dev/null @@ -1,11 +0,0 @@ -package rs09.game.content.activity.gnomecooking - -const val GC_BASE_ATTRIBUTE = "gnome_cooking" -const val GC_TUT_PROG = "tutorial:stage" -const val GC_TUT_FIN = "tutorial:complete" -const val GC_JOB_ORDINAL = "job:job_ordinal" -const val GC_JOB_COMPLETE = "job:job_complete" -const val GC_HARD_JOB = "job:hard_job" -const val GC_NEEDED_ITEM = "job:needed_item" -const val GC_POINTS = "job:points" -const val GC_REDEEMABLE_FOOD = "job:redeemable_food" diff --git a/Server/src/main/kotlin/rs09/game/content/activity/mta/MTAListeners.kt b/Server/src/main/kotlin/rs09/game/content/activity/mta/MTAListeners.kt deleted file mode 100644 index fb3584176..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/mta/MTAListeners.kt +++ /dev/null @@ -1,102 +0,0 @@ -package rs09.game.content.activity.mta - -import core.game.content.activity.mta.impl.AlchemistZone -import core.game.node.item.Item -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.NPCs -import rs09.game.interaction.InteractionListener -import rs09.game.node.entity.skill.magic.SpellListener -import rs09.game.node.entity.skill.magic.spellconsts.Modern - -class MTAListeners : InteractionListener() { - override fun defineListeners() { - on(NPCs.MAZE_GUARDIAN_3102,NPC,"talk-to"){player,node -> - player.dialogueInterpreter.open(node.id, node) - return@on true - } - } -} - -class MTASpellListeners : SpellListener("modern"){ - 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 MTA_ALCH_ITEMS = AlchemistZone.AlchemistItem.values().map{it.item.id}.toIntArray() - - override fun defineListeners() { - - onCast(Modern.HIGH_ALCHEMY,ITEM,*MTA_ALCH_ITEMS){p, node -> - val item = node?.asItem() ?: return@onCast - requires(p,55) - val alchItem = AlchemistZone.AlchemistItem.forItem(item.id) ?: return@onCast - val coins = Item(AlchemistZone.COINS.id, alchItem.cost) - val freeAlch = alchItem == AlchemistZone.freeConvert - - if (p.inventory.getAmount(AlchemistZone.COINS.id) + alchItem.cost > 10000) { - p.dialogueInterpreter.sendDialogue("Warning: You can't deposit more than 12000 coins at a time.") - } - - if (coins.amount > 1 && !p.inventory.hasSpaceFor(coins)) { - p.packetDispatch.sendMessage("Not enough space in your inventory!") - return@onCast - } - - if(!freeAlch){ - requires(p,55,arrayOf(Item(Items.FIRE_RUNE_554,5),Item(Items.NATURE_RUNE_561,1))) - } - - p.lock(3) - p.visualize(HIGH_ALCH_ANIM,HIGH_ALCH_GFX) - if(p.inventory.remove(Item(item.id,1))){ - p.audioManager.send(97) - if (coins.amount != 0) { - p.inventory.add(coins) - } - } - - showMagicTab(p) - addXP(p,65.0) - setDelay(p,false) - removeRunes(p) - } - - onCast(Modern.LOW_ALCHEMY,ITEM,*MTA_ALCH_ITEMS){p, node -> - val item = node?.asItem() ?: return@onCast - requires(p,21) - val alchItem = AlchemistZone.AlchemistItem.forItem(item.id) ?: return@onCast - val coins = Item(AlchemistZone.COINS.id, alchItem.cost) - val freeAlch = alchItem == AlchemistZone.freeConvert - - if (p.inventory.getAmount(AlchemistZone.COINS.id) + alchItem.cost > 10000) { - p.dialogueInterpreter.sendDialogue("Warning: You can't deposit more than 12000 coins at a time.") - } - - if (coins.amount > 1 && !p.inventory.hasSpaceFor(coins)) { - p.packetDispatch.sendMessage("Not enough space in your inventory!") - return@onCast - } - - if(!freeAlch){ - requires(p,21,arrayOf(Item(Items.FIRE_RUNE_554,3),Item(Items.NATURE_RUNE_561,1))) - } - - p.lock(3) - p.visualize(LOW_ALCH_ANIM,LOW_ALCH_GFX) - if(p.inventory.remove(Item(item.id,1))){ - p.audioManager.send(97) - if (coins.amount != 0) { - p.inventory.add(coins) - } - } - - showMagicTab(p) - addXP(p,31.0) - setDelay(p,false) - removeRunes(p) - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/activity/pyramidplunder/PlunderSession.kt b/Server/src/main/kotlin/rs09/game/content/activity/pyramidplunder/PlunderSession.kt deleted file mode 100644 index 1d223ce74..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/pyramidplunder/PlunderSession.kt +++ /dev/null @@ -1,89 +0,0 @@ -package rs09.game.content.activity.pyramidplunder - -import core.game.component.Component -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.tools.RandomFunction -import org.rs09.consts.Components -import rs09.game.world.GameWorld - -class PlunderSession(val player: Player) { - var door1Open: Boolean = false - var door2Open: Boolean = false - var door3Open: Boolean = false - var door4Open: Boolean = false - var chestHidden: Boolean = false - var chestOpen: Boolean = false - var coffinHidden: Boolean = false - var coffinOpen: Boolean = false - var timeCounter: Int = 1 - var correctDoorIndex: Int = RandomFunction.random(1000) % 4 - var isActive = false - - - fun init(){ - player.setAttribute("plunder-session",this) - GameWorld.Pulser.submit(PlunderPulse(player)) - player.interfaceManager.openOverlay(Component(Components.NTK_OVERLAY_428)) - isActive = true - } - - fun updateOverlay(){ - player.configManager.set(821,getConfig()) - } - - fun resetVars(){ - chestOpen = false - chestHidden = false - coffinHidden = false - coffinOpen = false - door1Open = false - door2Open = false - door3Open = false - door4Open = false - correctDoorIndex = RandomFunction.random(1000) % 4 - } - - fun getConfig(): Int{ - var config = 0 - if(coffinOpen) config = 1 - if(coffinHidden) config = (config or (1 shl 1)) - if(chestOpen) config = (config or (1 shl 2)) - if(chestHidden) config = (config or (1 shl 3)) - if(door1Open) config = (config or (1 shl 9)) - if(door2Open) config = (config or (1 shl 10)) - if(door3Open) config = (config or (1 shl 11)) - if(door4Open) config = (config or (1 shl 12)) - return (config or (timeCounter shl 25)) - } - - fun setDoorOpen(index: Int){ - when(index){ - 0 -> door1Open = true - 1 -> door2Open = true - 2 -> door3Open = true - 3 -> door4Open = true - } - } - - class PlunderPulse(val player: Player) : Pulse(){ - var ticks = 0 - var overlayConfig = 0 - val session: PlunderSession? = player.getAttribute("plunder-session",null) - override fun pulse(): Boolean { - session ?: return false - if(ticks++ % 8 == 0){ - session.timeCounter++ - session.updateOverlay() - } - if(session.timeCounter > 63){ - player.properties.teleportLocation = Location.create(3286, 2803, 0) - player.dialogueInterpreter.sendDialogue("You ran out of time and the mummy kicked you","out.") - player.configManager.set(822,0) - player.configManager.set(821,0) - } - return session.timeCounter > 63 || !session.isActive - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/Vinesweeper.kt b/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/Vinesweeper.kt deleted file mode 100644 index 6c0762532..000000000 --- a/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/Vinesweeper.kt +++ /dev/null @@ -1,632 +0,0 @@ -import api.ContentAPI -import core.game.component.Component -import core.game.node.entity.Entity -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.map.Location -import core.game.world.map.RegionManager -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.game.world.update.flag.context.Graphics; -import core.plugin.Initializable -import core.tools.RandomFunction -import org.rs09.consts.Items -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InterfaceListener -import rs09.game.world.GameWorld; - -val AVACH_NIMPORTO_LOC = Location.create(1637, 4709) -val PORTAL = 29534 -val SIGNS = intArrayOf(29461, 29462, 29463, 29464) -val HOLES = intArrayOf(29476, 29477, 29478) -val NUMBERS = intArrayOf(29447, 29448, 29449, 29450, 29451, 29452, 29453, 29454, 29455) -val DEAD_PLANT_OBJ = 29456 -val FLAG_OBJ = 29457 - -val TUTORIAL = 685 - -val INSTRUCTION_SIGNS = hashMapOf( - 29463 to 684, - 29464 to 687, - 29462 to 688, - 29461 to 690 - ) - -val RABBIT = 7125 -val FARMERS = intArrayOf(7128, 7129, 7130) -val FARMER_BLINKIN = 7131 -val MRS_WINKIN = 7132 - -val MAX_SEEDS = 300 -val FARMER_CLEAR_RADIUS = 3 -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) -} - -data class SeedDestination(val player: Player, val loc: Location, val alive: Boolean) { - override fun equals(other: Any?): Boolean { - if(other is SeedDestination) { - return loc.equals(other.loc) - } else { - return false - } - } -} -var SEED_LOCS: HashSet = HashSet() - -fun isSeed(loc: Location): Boolean { - val scenery = ContentAPI.getScenery(loc) - return scenery != null && SEED_LOCS.contains(scenery.location) -} - -fun populateSeeds() { - while(SEED_LOCS.size < MAX_SEEDS) { - val loc = VINESWEEPER_BORDERS.getRandomLoc() - val scenery = ContentAPI.getScenery(loc) - if(scenery != null && HOLES.contains(scenery.id)) { - SEED_LOCS.add(loc) - } - } -} - -fun isHole(loc: Location): Boolean { - val scenery = ContentAPI.getScenery(loc) - return scenery != null && HOLES.contains(scenery.id) -} - -fun scheduleNPCs(player: Player, loc: Location, alive: Boolean, rabbit: Boolean) { - val dest = SeedDestination(player, loc, alive) - val ids = if(rabbit) { intArrayOf(RABBIT, *FARMERS) } else { FARMERS } - for(npc in ContentAPI.findLocalNPCs(player, ids, 30)) { - if(npc is VinesweeperNPC) { - npc.seedDestinations.add(dest) - npc.resetWalk() - } - } -} - -object VinesweeperTeleport { - @JvmStatic - fun teleport(npc: NPC, player: Player) { - npc.animate(Animation(437)) - npc.faceTemporary(player, 1) - npc.graphics(Graphics(108)) - player.lock() - player.audioManager.send(125) - Projectile.create(npc, player, 109).send() - npc.sendChat("Avach nimporto!") - GameWorld.Pulser.submit(object : Pulse(1) { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 2 -> { - player.savedData.globalData.essenceTeleporter = npc.id - player.setAttribute("/save:vinesweeper:return-tele:x", npc.location.x) - player.setAttribute("/save:vinesweeper:return-tele:y", npc.location.y) - player.properties.teleportLocation = AVACH_NIMPORTO_LOC - } - 3 -> { - player.graphics(Graphics(110)) - player.unlock() - return true - } - } - return false - } - }) - } -} - -class VinesweeperListener : InteractionListener() { - fun dig(player: Player, loc: Location) { - if(isSeed(loc)) { - 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."); - scheduleNPCs(player, loc, false, false) - val scenery = ContentAPI.getScenery(loc) - if(scenery != null) { - SceneryBuilder.replace(scenery, scenery.transform(DEAD_PLANT_OBJ)) - } - } else { - player.incrementAttribute("/save:vinesweeper:points", 1) - sendUpdatedPoints(player) - var count = 0 - for(dx in -1..1) { - for(dy in -1..1) { - if(isSeed(loc.transform(dx, dy, 0))) { - count += 1 - } - } - } - val scenery = ContentAPI.getScenery(loc) - if(scenery != null) { - SceneryBuilder.replace(scenery, scenery.transform(NUMBERS[count])) - } - if(count == 0) { - for(dx in -1..1) { - for(dy in -1..1) { - val newLoc = loc.transform(dx, dy, 0) - if(isHole(newLoc)) - dig(player, newLoc) - } - } - } - } - } - override fun defineListeners() { - ZoneBuilder.configure(VinesweeperZone()) - populateSeeds() - on(PORTAL, SCENERY, "enter") { player, _ -> - val x = player.getAttribute("vinesweeper:return-tele:x", 3052) - val y = player.getAttribute("vinesweeper:return-tele:y", 3304) - ContentAPI.teleport(player, Location(x, y)) - return@on true - } - on(SIGNS, SCENERY, "read") { player, node -> - player.interfaceManager.open(Component(INSTRUCTION_SIGNS[node.id]!!)) - return@on true - } - on(HOLES, SCENERY, "dig") { player, node -> - if(!player.inventory.contains(Items.SPADE_952, 1)) { - // TODO (crash): authenticity - player.sendMessage("You need a spade to dig here.") - } else { - player.visualize(Animation(8709), Graphics(1543)) - dig(player, node.location) - } - return@on true - } - on(HOLES, SCENERY, "flag") { player, node -> - if(player.inventory.remove(Item(Items.FLAG_12625, 1))) { - player.sendMessage("You add a flag to the patch.") - val scenery = node as Scenery - SceneryBuilder.replace(scenery, scenery.transform(FLAG_OBJ)) - scheduleNPCs(player, scenery.location, true, true) - } else { - // TODO (crash): authenticity - player.sendMessage("You do not have a flag to place in the patch.") - } - return@on true - } - on(HOLES, SCENERY, "inspect") { player, node -> - player.animate(Animation(8710)) - player.lock(5) - GameWorld.Pulser.submit(object : Pulse(5) { - override fun pulse(): Boolean { - val msg = when(RandomFunction.random(0, 7)) { - 0 -> "You don't see anything interesting. You can't be sure if there's a seed there or not." - 1 -> "You get some mud in your eye and it stings! You have no idea what is in the hole." - 2 -> "The mud seems to be too thick to see what is there." - 3 -> "A slimy worm wriggles out of the mud, making you jump and lose concentration. You're not sure if there is a seed here or not." - else -> if(isSeed(node.location)) { - "You notice a seed hidden in the dirt." - } else { - "You are certain there is no seed planted here." - } - } - ContentAPI.sendDialogue(player, msg) - return true - } - }) - return@on true - } - on(MRS_WINKIN, NPC, "talk-to") { player, npc -> - ContentAPI.openDialogue(player, WinkinDialogue(), npc) - return@on true - } - on(MRS_WINKIN, NPC, "trade") { player, _ -> - player.interfaceManager.open(Component(686)) - return@on true - } - on(MRS_WINKIN, NPC, "buy flags") { player, npc -> - val dialogue = WinkinDialogue() - dialogue.stage = 20 - ContentAPI.openDialogue(player, dialogue, npc) - return@on true - } - on(RABBIT, NPC, "feed") { player, node -> - if(player.inventory.remove(Item(Items.OGLEROOT_12624, 1))) { - val npc = node as NPC - // TODO: find burrow animation id - //npc.animate(9603) - npc.sendChat("Squeak!") - npc.lock(3) - player.skills.addExperience(Skills.HUNTER, 30.0) - GameWorld.Pulser.submit(object : Pulse(3) { - override fun pulse(): Boolean { - //npc.setInvisible(true) - npc.respawnTick = GameWorld.ticks + 50 - npc.location = npc.properties.spawnLocation - return true - } - }) - } else { - // TODO (crash): authenticity - player.sendMessage("You don't have any ogleroots to feed the rabbit.") - } - return@on true - } - on(FARMER_BLINKIN, NPC, "talk-to") { player, npc -> - //player.interfaceManager.open(Component(TUTORIAL)) - ContentAPI.openDialogue(player, BlinkinDialogue(), npc) - return@on true - } - on(FARMER_BLINKIN, NPC, "buy-flags") { player, npc -> - val dialogue = BlinkinDialogue() - dialogue.stage = 21 - ContentAPI.openDialogue(player, dialogue, npc) - return@on true - } - on(FARMER_BLINKIN, NPC, "buy-roots") { player, npc -> - val dialogue = BlinkinDialogue() - dialogue.stage = 40 - ContentAPI.openDialogue(player, dialogue, npc) - return@on true - } - } -} - -@Initializable -class VinesweeperNPC : AbstractNPC { - fun compareDistance(a: SeedDestination, b: SeedDestination): Int { - val da = a.loc.getDistance(location).toInt() - val db = b.loc.getDistance(location).toInt() - return db - da - } - var seedDestinations: ArrayList = ArrayList(); - constructor() : super(RABBIT, null, true) {} - private constructor(id: Int, location: Location) : super(id, location) {} - override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { - return VinesweeperNPC(id, location) - } - - init { - walkRadius = 22 - } - - override fun getIds(): IntArray { - return intArrayOf(RABBIT, *FARMERS) - } - - override fun handleTickActions() { - val dest = seedDestinations.find({sd -> sd.loc == location}) - if(dest != null) { - for(npc in ContentAPI.findLocalNPCs(this, intArrayOf(RABBIT, *FARMERS), 30)) { - if(npc is VinesweeperNPC) { - npc.seedDestinations.remove(dest) - npc.resetWalk() - } - } - val scenery = ContentAPI.getScenery(dest.loc) - if(scenery != null) { - if(id == RABBIT) { - val replacement = if(SEED_LOCS.contains(dest.loc)) { DEAD_PLANT_OBJ } else { HOLES[0] } - SceneryBuilder.replace(scenery, scenery.transform(replacement)) - scheduleNPCs(dest.player, dest.loc, false, false) - } else { - if(dest.alive) { - handleFarmerFlag(scenery, dest) - } else { - sendChat("Hmm. Looks like there's a plant here.") - lock(3) - GameWorld.Pulser.submit(object : Pulse(3) { - override fun pulse(): Boolean { - sendChat("Gracious me! This one's dead") - SceneryBuilder.replace(scenery, scenery.transform(HOLES[0])) - farmerClear(dest) - return true - } - }) - } - } - } - seedDestinations.remove(dest) - } - super.handleTickActions() - } - - override fun getMovementDestination(): Location? { - if(seedDestinations.size > 0) { - seedDestinations.sortBy({a -> a.loc.getDistance(location).toInt()}) - return seedDestinations.get(0).loc - } else { - return super.getMovementDestination() - } - } - - fun handleFarmerFlag(scenery: Scenery, dest: SeedDestination) { - sendChat("Ah, another flag to clear. Let's see what's there.") - lock(3) - animate(Animation(451)) - if(SEED_LOCS.contains(dest.loc)) { - val npc = this - GameWorld.Pulser.submit(object : Pulse(3) { - override fun pulse(): Boolean { - sendChat("Ah! A seed. Points for everyone near me!") - val level = dest.player.skills.getStaticLevel(Skills.FARMING) - val points = RandomFunction.random(level, 4 * level) - dest.player.incrementAttribute("/save:vinesweeper:points", points) - dest.player.inventory.add(Item(Items.FLAG_12625, 1)) - sendUpdatedPoints(dest.player) - for(neighbor in RegionManager.getLocalPlayers(npc)) { - if(neighbor != dest.player) { - neighbor.incrementAttribute("/save:vinesweeper:points", points / 2) - sendUpdatedPoints(neighbor) - } - } - SceneryBuilder.replace(scenery, scenery.transform(HOLES[0])) - farmerClear(dest) - return true - } - }) - } else { - SceneryBuilder.replace(scenery, scenery.transform(HOLES[0])) - var i = 0 - val lines = arrayOf("Hmm, no seeds planted here, I'm afraid.", "I'll have to keep this 'ere flag. Sorry.") - GameWorld.Pulser.submit(object : Pulse(3) { - override fun pulse(): Boolean { - sendChat(lines[i++]) - return i >= lines.size - } - }) - } - } - fun farmerClear(dest: SeedDestination) { - for(dx in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) { - for(dy in -FARMER_CLEAR_RADIUS..FARMER_CLEAR_RADIUS) { - val toClear = ContentAPI.getScenery(dest.loc.transform(dx, dy, 0)) - if(toClear != null && intArrayOf(DEAD_PLANT_OBJ, *NUMBERS).contains(toClear.id)) { - SceneryBuilder.replace(toClear, toClear.transform(HOLES[0])) - } - } - } - SEED_LOCS.remove(dest.loc) - populateSeeds() - } -} - -class VinesweeperZone : MapZone("Vinesweeper", true) { - override fun enter(e: Entity): Boolean { - if(e is Player) { - e.interfaceManager.openOverlay(Component(689)) - sendUpdatedPoints(e) - } - - return super.enter(e) - } - - override fun leave(e: Entity, logout: Boolean): Boolean { - if(e is Player) { - e.interfaceManager.closeOverlay() - if(!logout) { - e.sendMessage("Winkin's Farm thanks you for your visit.") - e.sendMessage("Leftover ogleroots and flags have been returned to the establishment.") - e.sendMessage("You have been reimbursed at a rate of 10gp per ogleroot and the flags have been collected.") - val flags = e.inventory.getAmount(Item(Items.FLAG_12625)) - if(flags > 0) { - e.setAttribute("/save:vinesweeper:stored-flags", flags) - e.inventory.remove(Item(Items.FLAG_12625, flags)) - } - val roots = e.inventory.getAmount(Item(Items.OGLEROOT_12624)) - if(roots > 0) { - e.inventory.remove(Item(Items.OGLEROOT_12624, roots)) - e.inventory.add(Item(Items.COINS_995, roots * 10)) - } - } - } - - return super.leave(e, logout) - } - - override fun configure() { - super.registerRegion(6473) - } -} - -class VinesweeperRewards : InterfaceListener() { - val IFACE = 686 - val TRADE_FOR_XP_BUTTON = 53 - val XP_CONFIRM = 72 - val XP_DENY = 73 - - enum class Opcode(val value: Int) { - VALUE(155), - BUY1(196), - BUY5(124), - BUY10(199), - BUYX(234), - } - - data class Reward(val itemID: Int, val points: Int) {} - - val REWARDS = hashMapOf( - 18 to Reward(Items.TOMATO_SEED_5322, 10), - 19 to Reward(Items.SWEETCORN_SEED_5320, 150), - 20 to Reward(Items.STRAWBERRY_SEED_5323, 165), - 21 to Reward(Items.WATERMELON_SEED_5321, 680), - 22 to Reward(Items.GUAM_SEED_5291, 10), - 23 to Reward(Items.MARRENTILL_SEED_5292, 10), - 24 to Reward(Items.RANARR_SEED_5295, 4000), - 25 to Reward(Items.KWUARM_SEED_5299, 1000), - 26 to Reward(Items.TARROMIN_SEED_5293, 10), - 27 to Reward(Items.NASTURTIUM_SEED_5098, 10), - - 28 to Reward(Items.WOAD_SEED_5099, 30), - 29 to Reward(Items.LIMPWURT_SEED_5100, 70), - 30 to Reward(Items.ASGARNIAN_SEED_5308, 5), - 31 to Reward(Items.KRANDORIAN_SEED_5310, 20), - 32 to Reward(Items.REDBERRY_SEED_5101, 5), - 33 to Reward(Items.CADAVABERRY_SEED_5102, 5), - 34 to Reward(Items.DWELLBERRY_SEED_5103, 5), - 35 to Reward(Items.JANGERBERRY_SEED_5104, 10), - 36 to Reward(Items.WHITEBERRY_SEED_5105, 25), - - 37 to Reward(Items.POISON_IVY_SEED_5106, 30), - 38 to Reward(Items.ACORN_5312, 100), - 39 to Reward(Items.WILLOW_SEED_5313, 1800), - 40 to Reward(Items.MAPLE_SEED_5314, 12000), - 41 to Reward(Items.PINEAPPLE_SEED_5287, 10000), - 42 to Reward(Items.YEW_SEED_5315, 29000), - 43 to Reward(Items.PALM_TREE_SEED_5289, 35000), - 44 to Reward(Items.SPIRIT_SEED_5317, 55000), - 45 to Reward(Items.COMPOST_POTION4_6470, 5000), - 46 to Reward(Items.FLAG_12625, 50), - ) - - fun buy(player: Player, buttonID: Int, amount: Int) { - val reward = REWARDS[buttonID] ?: return - val cost = amount * reward.points - val points = player.getAttribute("vinesweeper:points", 0) - if(cost in 1 until points) { - val item = Item(reward.itemID, amount) - if(!player.inventory.add(item)) { - GroundItemManager.create(item, player) - } - player.incrementAttribute("/save:vinesweeper:points", -cost) - sendUpdatedPoints(player) - } else { - // TODO (crash): authenticity - player.sendMessage("You don't have enough points for that.") - } - } - - override fun defineListeners() { - onOpen(IFACE) { _, _ -> - /*for((buttonID, reward) in REWARDS) { - ContentAPI.sendItemOnInterface(player, IFACE, buttonID, reward.itemID, 5) - }*/ - //player.packetDispatch.sendRunScript(2003, "") - return@onOpen true - } - on(IFACE) { player, _, opcode, buttonID, _, _ -> - when(opcode) { - Opcode.VALUE.value -> { - when(buttonID) { - TRADE_FOR_XP_BUTTON -> { - player.packetDispatch.sendInterfaceConfig(686, 60, false) - } - XP_CONFIRM -> { - 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 = player.getAttribute("vinesweeper:points", 0) - val xp = points / points_per_xp; - player.skills.addExperience(Skills.FARMING, xp) - player.setAttribute("/save:vinesweeper:points", 0) - sendUpdatedPoints(player) - - } - XP_DENY -> { - player.packetDispatch.sendInterfaceConfig(686, 60, true) - } - else -> { - val reward = REWARDS[buttonID] ?: return@on true - player.sendMessage("${Item(reward.itemID).name}: ${reward.points} vinesweeper points") - } - } - } - Opcode.BUY1.value -> { - buy(player, buttonID, 1) - return@on true - } - Opcode.BUY5.value -> { - buy(player, buttonID, 5) - return@on true - } - Opcode.BUY10.value -> { - buy(player, buttonID, 10) - return@on true - } - Opcode.BUYX.value -> { - player.setAttribute("runscript") { amount: Int -> - buy(player, buttonID, amount) - } - player.dialogueInterpreter.sendInput(false, "Enter the amount:") - return@on true - } - else -> {} - } - return@on true - } - } -} - - - -/* -https://www.youtube.com/watch?v=WkCVAOOR7Sw -buy-flags -Player: "Have you got any flags?" -NPC: "Let me check for you." -NPC: "Alright, you can have a total of 10 flags. To obtain a\nfull set of flags will cost you 5000 coins. Would you\nlike to buy these flags?" -"Yes, please." "No thanks." -NPC: "Here you are then, dear." - -talk-to -NPC: "Oh hello there, dear. How can I help you?" -"Where are we?" "Have you got any flags?" "Do you have a spare spade?" "Do you have anything for trade?" "Nothing. I'm fine, thanks." - -Player: "Do you have a spare spade?" -NPC: "Why, of course. I can sell you one for 5 gold pieces." -"Okay, thanks." "Actually, I've changed my mind." -NPC: "Here you are, then." - -inspect: -chat log: "You examine the hole to see what might be in it." -chat dialog: "You notice a seed hidden in the dirt." - -Farmer overhead: -"Ah, another flag to clear. Let's see what's there." -"Ah! A seed. Points for everyone near me!" - -https://www.youtube.com/watch?v=UjxfJdHkJnM -inspect: -"Oh dear! It looks like you dug up a potato seed by mistake." -Farmer overhead: -"Hmm. Looks like there's a plant here." -"Gracious me! This one's dead" - -flag: -"You add a flag to the patch." - -digging ogleroot: -"You uncover a rather odd-looking root vegetable." - -https://www.youtube.com/watch?v=fKyy0sgrBYM -Rabbit overhead: -"Squeak!" - -https://www.youtube.com/watch?v=RnhNrwbUjjQ -Player: "Have you got any flags?" -NPC: "Let me check for you." -NPC: "Ah! First things first. One of the farmers dropped off\nsome flags for you. You can have them back. Here you\ngo." - -NPC: "It looks like you have all the flags you need. You don't\nneed to buy any more." - -inspect: -"You notice a seed hidden in the dirt." -"A slimy worm wriggles out of the mud, making you jump and lose\nconcentration. You're not sure if there is a seed here or not." -"You are certain there is no seed planted here." - -farmer overhead: -"Hmm, no seeds planted here, I'm afraid." -"I'll have to keep this 'ere flag. Sorry." - -*/ diff --git a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt b/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt deleted file mode 100644 index 7ace936e6..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventManager.kt +++ /dev/null @@ -1,39 +0,0 @@ -package rs09.game.content.ame - -import core.game.node.entity.player.Player -import core.game.world.map.zone.ZoneRestriction -import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld - -private const val DELAY_TICKS = 6000 //60 minutes -class RandomEventManager(val player: Player) { - var event: RandomEventNPC? = null - var nextSpawn = 0 - - fun tick(){ - if(player.isArtificial) return - if(GameWorld.ticks > nextSpawn) fireEvent() - } - - fun fireEvent(){ - if(player.zoneMonitor.isRestricted(ZoneRestriction.RANDOM_EVENTS)){ - nextSpawn = GameWorld.ticks + 3000 - return - } - val ame = RandomEvents.values().random() - event = ame.npc.create(player,ame.loot,ame.type) - if(event!!.spawnLocation == null){ - nextSpawn = GameWorld.ticks + 3000 - return - } - event!!.init() - nextSpawn = GameWorld.ticks + DELAY_TICKS - SystemLogger.logRE("Fired ${event!!.name} for ${player.username}") - } - - fun init(){ - if(player.isArtificial) return - nextSpawn = GameWorld.ticks + DELAY_TICKS - SystemLogger.logRE("Initialized REManager for ${player.username}") - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/RandomEventNPC.kt deleted file mode 100644 index 7ceb1cf3b..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/RandomEventNPC.kt +++ /dev/null @@ -1,94 +0,0 @@ -package rs09.game.content.ame - -import api.ContentAPI -import core.game.interaction.MovementPulse -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.map.Location -import core.game.world.map.RegionManager -import core.game.world.map.path.Pathfinder -import core.game.world.update.flag.context.Graphics -import rs09.game.content.ame.events.MysteriousOldManNPC -import rs09.game.content.global.WeightBasedTable -import rs09.game.world.GameWorld.Pulser -import rs09.tools.secondsToTicks -import kotlin.random.Random -import kotlin.reflect.full.createInstance - -abstract class RandomEventNPC(id: Int) : NPC(id) { - lateinit var player: Player - abstract var loot: WeightBasedTable? - var spawnLocation: Location? = null - val SMOKE_GRAPHICS = Graphics(86) - var initialized = false - var ticksLeft = secondsToTicks(600) - - open fun create(player: Player, loot: WeightBasedTable? = null, type: String = ""): RandomEventNPC{ - val event = this::class.createInstance() - if(event is MysteriousOldManNPC) event.type = type - event.loot = loot - event.player = player - event.spawnLocation = RegionManager.getSpawnLocation(player,this) - return event - } - - open fun terminate(){ - player.antiMacroHandler.event = null - if(initialized){ - ContentAPI.poofClear(this) - } - } - - open fun follow(){ - pulseManager.run(object : MovementPulse(this,player, Pathfinder.DUMB){ - override fun pulse(): Boolean { - face(player) - return false - } - }) - } - - override fun tick() { - super.tick() - ticksLeft-- - if(!pulseManager.hasPulseRunning()) follow() - if(!player.isActive || !player.location.withinDistance(location,10)){ - terminate() - } - if(ticksLeft <= 0){ - onTimeUp() - } - } - - override fun init() { - initialized = true - spawnLocation ?: terminate() - location = spawnLocation - super.init() - } - - open fun onTimeUp(){ - noteAndTeleport() - terminate() - } - - fun noteAndTeleport(){ - for(item in player.inventory.toArray()){ - if(item == null) continue - if(item.noteChange != item.id){ - player.inventory.remove(item) - player.inventory.add(Item(item.noteChange,item.amount)) - } - } - if(Random.nextBoolean()){ - player.properties.teleportLocation = Location.create(3197, 3223, 0) - } else { - player.properties.teleportLocation = Location.create(3212, 9620, 0) - } - player.graphics(SMOKE_GRAPHICS) - } - - abstract fun talkTo(npc: NPC) -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt b/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt deleted file mode 100644 index 4fc187e56..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/RandomEvents.kt +++ /dev/null @@ -1,48 +0,0 @@ -package rs09.game.content.ame - -import org.rs09.consts.Items -import rs09.game.content.ame.events.MysteriousOldManNPC -import rs09.game.content.ame.events.certer.CerterNPC -import rs09.game.content.ame.events.drilldemon.SeargentDamienNPC -import rs09.game.content.ame.events.evilchicken.EvilChickenNPC -import rs09.game.content.ame.events.sandwichlady.SandwichLadyRENPC -import rs09.game.content.ame.events.genie.GenieNPC -import rs09.game.content.global.WeightBasedTable -import rs09.game.content.global.WeightedItem - -enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = null) { - SANDWICH_LADY(SandwichLadyRENPC()), - GENIE(GenieNPC()), - CERTER(CerterNPC(),WeightBasedTable.create( - WeightedItem(Items.COINS_995,1,100,2.0), - WeightedItem(Items.SPINACH_ROLL_1969,1,1,2.0), - WeightedItem(Items.KEBAB_1971,1,1,2.0), - WeightedItem(Items.UNCUT_SAPPHIRE_1623,1,1,0.5), - WeightedItem(Items.SAPPHIRE_1607,1,1,0.5), - WeightedItem(Items.UNCUT_EMERALD_1621,1,1,0.25), - WeightedItem(Items.EMERALD_1605,1,1,0.25), - WeightedItem(Items.UNCUT_RUBY_1619,1,1,0.15), - WeightedItem(Items.RUBY_1603,1,1,0.15), - WeightedItem(Items.DIAMOND_1601,1,1,0.1), - WeightedItem(Items.UNCUT_DIAMOND_1617,1,1,0.1), - WeightedItem(Items.COSMIC_TALISMAN_1454,1,1,0.15), - WeightedItem(Items.NATURE_TALISMAN_1462,1,1,0.15), - WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985,1,1,1.0), - WeightedItem(Items.LOOP_HALF_OF_A_KEY_987,1,1,1.0) - )), - DRILL_DEMON(SeargentDamienNPC()), - EVIL_CHICKEN(EvilChickenNPC()), - SURPRISE_EXAM(MysteriousOldManNPC(),"sexam"); - - var type: String = "" - private set - - constructor(npc: RandomEventNPC, type: String) : this(npc,null){ - this.type = type - } - - companion object { - @JvmField - val randomIDs = values().map { it.npc.id }.toList() - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManDialogue.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManDialogue.kt deleted file mode 100644 index 0518b4852..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManDialogue.kt +++ /dev/null @@ -1,40 +0,0 @@ -package rs09.game.content.ame.events - -import core.game.node.entity.player.Player -import rs09.game.content.ame.events.supriseexam.SurpriseExamUtils -import rs09.game.content.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) - player!!.antiMacroHandler.event?.terminate() - } - 2 -> { - end() - player!!.antiMacroHandler.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/kotlin/rs09/game/content/ame/events/MysteriousOldManNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManNPC.kt deleted file mode 100644 index ce177eca3..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/MysteriousOldManNPC.kt +++ /dev/null @@ -1,31 +0,0 @@ -package rs09.game.content.ame.events - -import core.game.node.entity.npc.NPC -import core.tools.RandomFunction -import org.rs09.consts.NPCs -import rs09.game.content.ame.RandomEventNPC -import rs09.game.content.global.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/kotlin/rs09/game/content/ame/events/certer/CerterDialogue.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/certer/CerterDialogue.kt deleted file mode 100644 index b29c231f8..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/certer/CerterDialogue.kt +++ /dev/null @@ -1,33 +0,0 @@ -package rs09.game.content.ame.events.certer - -import api.ContentAPI -import core.game.component.Component -import core.game.node.item.GroundItemManager -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE - -class CerterDialogue(val initial: Boolean) : DialogueFile() { - val CERTER_INTERFACE = 184 - override fun handle(componentID: Int, buttonID: Int) { - if(initial){ - when(stage){ - 0 -> npc("Ah, hello, ${player!!.username.capitalize()}. Could you","please help me identify this?").also { stage++ } - 1 -> { - end() - player!!.interfaceManager.open(Component(CERTER_INTERFACE)) - } - } - } else { - val isCorrect = player!!.getAttribute("certer:correct",false) - when(stage){ - 0 -> if(!isCorrect) npc("Sorry, I don't think so.").also { stage = END_DIALOGUE; player!!.antiMacroHandler.event?.terminate() } else npc("Oh yes! That's right.").also { stage++ } - 1 -> { - npc("Please, take this as a thanks.") - player!!.antiMacroHandler.event?.loot!!.roll().forEach { ContentAPI.addItemOrDrop(player!!, it.id, it.amount) } - player!!.antiMacroHandler.event?.terminate() - stage = END_DIALOGUE - } - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/certer/CerterEventInterface.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/certer/CerterEventInterface.kt deleted file mode 100644 index 3dbadca5b..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/certer/CerterEventInterface.kt +++ /dev/null @@ -1,65 +0,0 @@ -package rs09.game.content.ame.events.certer - -import core.game.node.entity.player.Player -import org.rs09.consts.Items -import rs09.game.interaction.InterfaceListener - -class CerterEventInterface : InterfaceListener() { - val CERTER_INTERFACE = 184 - val OPTION_A_CHILD = 1 - val OPTION_B_CHILD = 2 - val OPTION_C_CHILD = 3 - val ITEM_CHILD = 7 - val items = mapOf( - Items.BLACK_SWORD_1283 to "a sword", - Items.BRONZE_SWORD_1277 to "a sword", - Items.BRONZE_CHAINBODY_1103 to "a platebody", - Items.BLACK_CHAINBODY_1107 to "a platebody", - Items.WOODEN_SHIELD_1171 to "a shield", - Items.BRONZE_KITESHIELD_1189 to "a shield", - ) - val falseOptions = arrayOf("a ring","a dragon","a cat","a helmet","a pair of boots","a fish","a gun","a staff","a cannon","a dwarf","a bow","an arrow","a chinchompa","a chicken","a feather","a ninja","a bot") - override fun defineListeners() { - on(CERTER_INTERFACE){player, _, _, buttonID, _, _ -> - val answer = buttonID - 7 - val correctAnswer = player.getAttribute("certer:correctIndex",0) - player.setAttribute("certer:correct",correctAnswer == answer) - player.interfaceManager.close() - player.dialogueInterpreter.open(CerterDialogue(false),player.antiMacroHandler.event?.asNpc()) - return@on true - } - - onOpen(CERTER_INTERFACE){player, _ -> - generateOptions(player) - return@onOpen true - } - } - - fun generateOptions(player: Player) { - val correct = items.keys.random() - val indexes = arrayListOf(1,2,3) - val correctIndex = indexes.random() - indexes.remove(correctIndex) - player.setAttribute("certer:correctIndex",correctIndex) - - player.packetDispatch.sendString(items[correct],CERTER_INTERFACE,optionFromIndex(correctIndex)) - - val tempOptions = falseOptions.toMutableList() - val false1 = tempOptions.random() - tempOptions.remove(false1) - var false2 = tempOptions.random() - - player.packetDispatch.sendString(false1,CERTER_INTERFACE,optionFromIndex(indexes[0])) - player.packetDispatch.sendString(false2,CERTER_INTERFACE,optionFromIndex(indexes[1])) - player.packetDispatch.sendItemOnInterface(correct,1,CERTER_INTERFACE,ITEM_CHILD) - } - - fun optionFromIndex(index: Int): Int{ - return when(index){ - 1 -> OPTION_A_CHILD - 2 -> OPTION_B_CHILD - 3 -> OPTION_C_CHILD - else -> OPTION_A_CHILD - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/certer/CerterNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/certer/CerterNPC.kt deleted file mode 100644 index 7af3f4d87..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/certer/CerterNPC.kt +++ /dev/null @@ -1,23 +0,0 @@ -package rs09.game.content.ame.events.certer - -import core.game.node.entity.npc.NPC -import core.tools.RandomFunction -import org.rs09.consts.NPCs -import rs09.game.content.ame.RandomEventNPC -import rs09.game.content.global.WeightBasedTable - -class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GILES_2538) { - override fun tick() { - super.tick() - if(RandomFunction.random(1,10) == 5) sendChat(player.username.capitalize() + "! I need your assistance.") - } - - override fun talkTo(npc: NPC) { - player.dialogueInterpreter.open(CerterDialogue(true),npc) - } - - override fun init() { - super.init() - sendChat(player.username.capitalize() + "! I need your assistance.") - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/DrillDemonListeners.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/DrillDemonListeners.kt deleted file mode 100644 index e73300052..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/DrillDemonListeners.kt +++ /dev/null @@ -1,49 +0,0 @@ -package rs09.game.content.ame.events.drilldemon - -import core.game.node.entity.npc.NPC -import core.game.system.task.Pulse -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener - -class DrillDemonListeners : InteractionListener() { - val MATS = intArrayOf(10076,10077,10078,10079) - override fun defineListeners() { - - on(MATS,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 - } - - 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 - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/DrillDemonUtils.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/DrillDemonUtils.kt deleted file mode 100644 index bc44624fe..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/DrillDemonUtils.kt +++ /dev/null @@ -1,120 +0,0 @@ -package rs09.game.content.ame.events.drilldemon - -import api.ContentAPI -import core.game.content.dialogue.FacialExpression -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.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_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" - - 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) - - ContentAPI.registerLogoutListener(player, "drilldemon"){p -> - ContentAPI.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) - 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 task = tempList.random() - val taskOffset = tempOffsetList.random() - - player.setAttribute(DD_KEY_TASK,task) - player.setAttribute(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) - } - - player.dialogueInterpreter.sendDialogues(NPCs.SERGEANT_DAMIEN_2790,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 - else -> 0 - } - } - - fun getMatTask(id: Int, player: Player): Int{ - return player.varpManager.get(DD_SIGN_VARP).getVarbitValue(getOffsetForID(id)) - } - - fun cleanup(player: Player){ - ContentAPI.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) - player.interfaceManager.openDefaultTabs() - player.packetDispatch.sendInterfaceConfig(548, 69, false) - player.packetDispatch.sendInterfaceConfig(746, 12, false) - } - - fun animationForTask(task: Int): Animation { - return when(task){ - DD_SIGN_SITUP -> Animation(2763) - DD_SIGN_PUSHUP -> Animation(2762) - DD_SIGN_JUMP -> Animation(2761) - DD_SIGN_JOG -> Animation(2764) - else -> Animation(-1) - } - } - - 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) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienDefaultPlugin.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienDefaultPlugin.kt deleted file mode 100644 index 2c409c896..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienDefaultPlugin.kt +++ /dev/null @@ -1,29 +0,0 @@ -package rs09.game.content.ame.events.drilldemon - -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.plugin.Initializable -import org.rs09.consts.NPCs -import rs09.tools.END_DIALOGUE - -@Initializable -class SeargentDamienDefaultPlugin(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player?): 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/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienDialogue.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienDialogue.kt deleted file mode 100644 index 67d9fe549..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienDialogue.kt +++ /dev/null @@ -1,55 +0,0 @@ -package rs09.game.content.ame.events.drilldemon - -import core.game.content.dialogue.FacialExpression -import core.game.system.task.Pulse -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE - -class SeargentDamienDialogue(val isCorrect: Boolean = false) : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - var correctAmt = player!!.getAttribute(DrillDemonUtils.DD_CORRECT_COUNTER,0) - if(correctAmt == 4 && player!!.antiMacroHandler.event == null) { - when(stage){ - 0 -> npc(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(player!!.antiMacroHandler.event == null){ - when(stage){ - START_DIALOGUE -> if(isCorrect) npc(FacialExpression.OLD_NORMAL,"Good! Now...").also { stage++ } else npc(FacialExpression.OLD_ANGRY1,"WRONG, MAGGOT!").also { stage++ } - 1 -> { - end() - DrillDemonUtils.changeSignsAndAssignTask(player!!) - } - } - } else { - when(stage){ - START_DIALOGUE -> npc(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!!) - player!!.antiMacroHandler.event?.terminate() - stage = END_DIALOGUE - } - 2 -> { - end() - player!!.antiMacroHandler.event?.terminate() - stage = END_DIALOGUE - } - } - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienNPC.kt deleted file mode 100644 index e104e82a7..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/drilldemon/SeargentDamienNPC.kt +++ /dev/null @@ -1,26 +0,0 @@ -package rs09.game.content.ame.events.drilldemon - -import core.game.node.entity.npc.NPC -import core.tools.RandomFunction -import org.rs09.consts.NPCs -import rs09.game.content.ame.RandomEventNPC -import rs09.game.content.global.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/kotlin/rs09/game/content/ame/events/evilchicken/EvilChickenNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/evilchicken/EvilChickenNPC.kt deleted file mode 100644 index d18cdc3ae..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/evilchicken/EvilChickenNPC.kt +++ /dev/null @@ -1,36 +0,0 @@ -package rs09.game.content.ame.events.evilchicken - -import core.game.node.entity.Entity -import core.game.node.entity.npc.NPC -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.tools.RandomFunction -import org.rs09.consts.Items -import rs09.game.content.ame.RandomEventNPC -import rs09.game.content.global.WeightBasedTable - -val ids = 2463..2468 - -class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(2463) { - override fun talkTo(npc: NPC) {} - - override fun init() { - super.init() - val index = (player.properties.combatLevel / 20) - 1 - val id = ids.toList()[index] - - this.transform(id) - this.attack(player) - this.isRespawn = false - } - - override fun finalizeDeath(killer: Entity?) { - super.finalizeDeath(killer) - GroundItemManager.create(Item(Items.FEATHER_314, RandomFunction.random(50,300)), this.dropLocation, player) - } - - override fun tick() { - super.tick() - if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/genie/GenieDialogue.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/genie/GenieDialogue.kt deleted file mode 100644 index 891502027..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/genie/GenieDialogue.kt +++ /dev/null @@ -1,21 +0,0 @@ -package rs09.game.content.ame.events.genie - -import api.ContentAPI -import core.game.content.dialogue.FacialExpression -import core.cache.def.impl.ItemDefinition -import core.game.component.Component -import core.game.node.entity.combat.ImpactHandler -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE - -class GenieDialogue : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - val assigned = player!!.getAttribute("genie:item",0) - npcl(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.") - ContentAPI.addItemOrDrop(player!!, assigned) - player!!.antiMacroHandler.event?.terminate() - stage = END_DIALOGUE - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/genie/GenieNPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/genie/GenieNPC.kt deleted file mode 100644 index c111da800..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/genie/GenieNPC.kt +++ /dev/null @@ -1,36 +0,0 @@ -package rs09.game.content.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 rs09.game.content.ame.RandomEventNPC -import rs09.game.content.global.WeightBasedTable - -class GenieNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GENIE_409) { - val phrases = arrayOf("Greetings, @name!","Ehem... Master @name?","Are you there, Master @name?","No one ignores me!") - var assigned_item = 0 - val items = arrayOf(Items.LAMP_2528) - - override fun tick() { - if(RandomFunction.random(1,15) == 5){ - sendChat(phrases.random().replace("@name",player.name.capitalize())) - } - 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) - } - - override fun talkTo(npc: NPC) { - player.dialogueInterpreter.open(GenieDialogue(),npc) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyInterface.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyInterface.kt deleted file mode 100644 index 231eaefa0..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyInterface.kt +++ /dev/null @@ -1,38 +0,0 @@ -package rs09.game.content.ame.events.sandwichlady - -import core.game.node.item.Item -import org.rs09.consts.Items -import rs09.game.interaction.InterfaceListener - -class SandwichLadyInterface : InterfaceListener(){ - - val SANDWICH_INTERFACE = 297 - val baguette = Items.BAGUETTE_6961 - val triangle_sandwich = Items.TRIANGLE_SANDWICH_6962 - val sandwich = Items.SQUARE_SANDWICH_6965 - val roll = Items.ROLL_6963 - val pie = Items.MEAT_PIE_2327 - val kebab = Items.KEBAB_1971 - val chocobar = Items.CHOCOLATE_BAR_1973 - - override fun defineListeners() { - on(SANDWICH_INTERFACE){player, _, _, buttonID, _, _ -> - val item = - when(buttonID) { - 7 -> {Item(baguette)} - 8 -> {Item(triangle_sandwich)} - 9 -> {Item(sandwich)} - 10 -> {Item(roll)} - 11 -> {Item(pie)} - 12 -> {Item(kebab)} - 13 -> {Item(chocobar)} - else -> {Item(baguette)} - } - - player.setAttribute("sandwich-lady:choice",item.id) - player.interfaceManager.close() - player.dialogueInterpreter.open(SandwichLadyDialogue(true),player.antiMacroHandler.event) - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyRENPC.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyRENPC.kt deleted file mode 100644 index dd30aac19..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/sandwichlady/SandwichLadyRENPC.kt +++ /dev/null @@ -1,36 +0,0 @@ -package rs09.game.content.ame.events.sandwichlady - -import core.game.node.entity.npc.NPC -import core.tools.RandomFunction -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import rs09.game.content.ame.RandomEventNPC -import rs09.game.content.global.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?") - var assigned_item = 0 - val items = arrayOf(Items.BAGUETTE_6961,Items.TRIANGLE_SANDWICH_6962,Items.SQUARE_SANDWICH_6965,Items.ROLL_6963,Items.MEAT_PIE_2327,Items.KEBAB_1971,Items.CHOCOLATE_BAR_1973) - - override fun tick() { - if(RandomFunction.random(1,15) == 5){ - sendChat(phrases.random().replace("@name",player.name.capitalize())) - } - super.tick() - } - - override fun init() { - super.init() - assignItem() - sendChat(phrases.random().replace("@name",player.name.capitalize())) - } - - fun assignItem(){ - assigned_item = items.random() - player.setAttribute("sandwich-lady:item",assigned_item) - } - - override fun talkTo(npc: NPC) { - player.dialogueInterpreter.open(SandwichLadyDialogue(false),npc) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SupriseExamListeners.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SupriseExamListeners.kt deleted file mode 100644 index 8d8b241ef..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SupriseExamListeners.kt +++ /dev/null @@ -1,60 +0,0 @@ -package rs09.game.content.ame.events.supriseexam - -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 rs09.game.interaction.InteractionListener -import rs09.game.interaction.inter.ExperienceInterface - -class SupriseExamListeners : InteractionListener() { - val MORDAUT = NPCs.MR_MORDAUT_6117 - val BOOK_OF_KNOWLEDGE = Items.BOOK_OF_KNOWLEDGE_11640 - override fun defineListeners() { - - on(MORDAUT,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()) - return@on true - } - - on(SurpriseExamUtils.SE_DOORS,SCENERY,"open"){ player, node -> - val correctDoor = player.getAttribute(SurpriseExamUtils.SE_DOOR_KEY,-1) - - if(correctDoor == -1){ - player.dialogueInterpreter.open(SEDoorDialogue(true)) - return@on true - } - - if(node.id == correctDoor){ - SurpriseExamUtils.cleanup(player) - return@on true - } - - player.dialogueInterpreter.open(SEDoorDialogue(false)) - return@on true - } - - on(BOOK_OF_KNOWLEDGE,ITEM,"read"){player, _ -> - player.setAttribute("caller"){skill: Int,p: Player -> - if(p.inventory.remove(Item(BOOK_OF_KNOWLEDGE))) { - val level = p.skills.getStaticLevel(skill) - val experience = level * 15.0 - p.skills.addExperience(skill, experience) - } - } - player.interfaceManager.open(Component(ExperienceInterface.COMPONENT_ID)) - return@on true - } - - } - - override fun defineDestinationOverrides() { - setDest(NPC,MORDAUT){_,_ -> - return@setDest Location.create(1886, 5025, 0) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt deleted file mode 100644 index e0416df21..000000000 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt +++ /dev/null @@ -1,83 +0,0 @@ -package rs09.game.content.ame.events.supriseexam - -import api.ContentAPI -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) - val SE_DOORS = intArrayOf(2188,2189,2192,2193) - val INTERFACE = Components.PATTERN_NEXT_103 - val SE_KEY_CORRECT = "supexam:correct" - val sets = arrayOf( - intArrayOf(Items.GARDENING_TROWEL_5325,Items.SECATEURS_5329,Items.SEED_DIBBER_5343,Items.RAKE_5341), - intArrayOf(Items.SALMON_329,Items.SHARK_385,Items.TROUT_333,Items.SHRIMPS_315), - intArrayOf(Items.BRONZE_SWORD_1277,Items.WOODEN_SHIELD_1171,Items.BRONZE_MED_HELM_1139,Items.ADAMANT_BATTLEAXE_1371), - 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) - - ContentAPI.registerLogoutListener(player, SE_LOGOUT_KEY){p -> - ContentAPI.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) - ContentAPI.clearLogoutListener(player, SE_LOGOUT_KEY) - player.removeAttribute(SE_KEY_LOC) - player.removeAttribute(SE_KEY_INDEX) - player.removeAttribute(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) - } - return true - } - }) - } - - fun generateInterface(player: Player){ - val set = sets.random() - val setIndex = sets.indexOf(set) - - val tempList = set.toList().shuffled().toMutableList() - val correctOpt = tempList.random() - val optIndex = tempList.indexOf(correctOpt) - - tempList.remove(correctOpt) - for(i in INTER_PATTERN_CHILDS.indices) player.packetDispatch.sendItemOnInterface(tempList[i],1, INTERFACE, INTER_PATTERN_CHILDS[i]) - for(i in INTER_OPTION_CHILDS.indices){ - if(i == optIndex) player.packetDispatch.sendItemOnInterface(correctOpt,1, INTERFACE, INTER_OPTION_CHILDS[i]) - else player.packetDispatch.sendItemOnInterface(getFalseSet(setIndex)[i],1, INTERFACE, INTER_OPTION_CHILDS[i]) - } - player.setAttribute(SE_KEY_INDEX,optIndex) - } - - fun getFalseSet(trueSetIndex: Int): IntArray{ - val tempSets = sets.toMutableList() - tempSets.removeAt(trueSetIndex) - return tempSets.random() - } - - fun pickRandomDoor(player: Player){ - player.setAttribute(SE_DOOR_KEY, SE_DOORS.random()) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoning.kt b/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoning.kt deleted file mode 100644 index aa48b16a8..000000000 --- a/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoning.kt +++ /dev/null @@ -1,13 +0,0 @@ -package rs09.game.content.consumable.effects - -import core.game.node.entity.player.Player -import core.game.content.consumable.ConsumableEffect -import core.game.node.entity.skill.Skills - -class RestoreSummoning(val percent: Double) : ConsumableEffect(){ - override fun activate(p: Player?) { - var amt = (p!!.skills!!.getStaticLevel(Skills.SUMMONING) * percent).toInt() - if(amt + p.skills.getLevel(Skills.SUMMONING) > p.skills.getStaticLevel(Skills.SUMMONING)) amt = p.skills.getStaticLevel(Skills.SUMMONING) - p.skills.getLevel(Skills.SUMMONING) - p.skills.setLevel(Skills.SUMMONING, p.skills.getLevel(Skills.SUMMONING) + amt) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/AchiettiesDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/AchiettiesDialogue.kt deleted file mode 100644 index 63344b3b7..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AchiettiesDialogue.kt +++ /dev/null @@ -1,37 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable - -/** - * @author qmqz - */ - -@Initializable -class AchiettiesDialogue(player: Player? = null) : DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - npc(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?): DialoguePlugin { - return AchiettiesDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(796) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/AgmundiDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/AgmundiDialogue.kt deleted file mode 100644 index a3878c4ea..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/AgmundiDialogue.kt +++ /dev/null @@ -1,67 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable - -/** - * @author qmqz - */ - -@Initializable -class AgmundiDialogue(player: Player? = null) : DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - npc(FacialExpression.CHILD_NORMAL,"Oh no, not another human... what do you want then?") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> player(FacialExpression.ASKING, "Oh, do you get humans here often?").also { stage++ } - 1 -> npc(FacialExpression.CHILD_SUSPICIOUS, "Not that often, no, but sometimes.").also { stage++ } - 2 -> npc(FacialExpression.CHILD_THINKING, "Of course, since you people are too big for dwarven", - "clothes, they typically don't stay very long.").also { stage++ } - 3 -> player(FacialExpression.SUSPICIOUS, "Why don't you make bigger clothes then?").also { stage++ } - 4 -> npc(FacialExpression.CHILD_GUILTY, "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(FacialExpression.GUILTY, "Who makes the clothes then?").also { stage = 10 } - 2 -> npc.openShop(player) - 3 -> npc(FacialExpression.CHILD_SUSPICIOUS,"Quests? Why would I have any quests?").also { stage = 20 } - - } - - 10 -> npc(FacialExpression.CHILD_NORMAL,"Oh, my sister, she lives in Keldagrim-East.", - "Has a little stall on the other side of", - "the Kelda.").also { stage++ } - 11 -> npc(FacialExpression.CHILD_NORMAL,"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(FacialExpression.CHILD_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=99 } - - 20 -> player("Oh, just anything to do would be fine.").also { stage++ } - 21 -> npc(FacialExpression.CHILD_NORMAL,"No, not right now... maybe I'll have something", - "for you to do later, but nothing at the moment.").also { stage = 99 } - - 99 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return AgmundiDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(2161) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/CaptainCainDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/CaptainCainDialogue.kt deleted file mode 100644 index f5de9b1fd..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/CaptainCainDialogue.kt +++ /dev/null @@ -1,67 +0,0 @@ -package rs09.game.content.dialogue - -import api.ContentAPI -import core.game.content.dialogue.DialoguePlugin -import core.game.content.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 -import rs09.tools.END_DIALOGUE -import java.text.SimpleDateFormat -import java.time.temporal.ChronoUnit -import java.util.* - -@Initializable -class CaptainCainDialogue(player: Player? = null) : DialoguePlugin(player) { - val sdf = SimpleDateFormat("ddMMyyyy") - override fun newInstance(player: Player?): DialoguePlugin { - return CaptainCainDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - 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 - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - val start = sdf.parse("27112020").toInstant() - val now = Date().toInstant() - val days = ChronoUnit.DAYS.between(start,now) - when(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(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } - 2 -> playerl(FacialExpression.HALF_THINKING, "No, thanks.").also { stage = END_DIALOGUE } - } - - 10 -> npcl(FacialExpression.FRIENDLY, "Alright, then, that'll be 7,500,000 gold please.").also { stage++ } - 11 -> options("Here you go!","Nevermind.").also { stage++ } - 12 -> when(buttonId){ - 1 -> if(ContentAPI.inInventory(player, 995, 7500000)) - playerl(FacialExpression.FRIENDLY, "Here you go!").also { stage = 20 } - else - playerl(FacialExpression.HALF_GUILTY, "Actually, I don't have that much.").also { stage = END_DIALOGUE } - - 2 -> playerl(FacialExpression.FRIENDLY, "On second thought, nevermind.").also { stage = END_DIALOGUE } - } - - 20 -> { - npcl(FacialExpression.FRIENDLY, "Thank you much, kind sir. And here's your torso.") - if(ContentAPI.removeItem(player, Item(995,7500000), api.Container.INVENTORY)) { - ContentAPI.addItem(player, Items.FIGHTER_TORSO_10551, 1) - } - stage = END_DIALOGUE - } - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.CAPTAIN_CAIN_5030) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/CouncilWorkerDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/CouncilWorkerDialogue.kt deleted file mode 100644 index 52822d60b..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/CouncilWorkerDialogue.kt +++ /dev/null @@ -1,30 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.plugin.Initializable -import rs09.game.content.quest.members.thefremenniktrials.CouncilWorkerFTDialogue - -@Initializable -class CouncilWorkerDialogue(player: Player? = null) : DialoguePlugin(player){ - override fun open(vararg args: Any?): Boolean { - if(player.questRepository.getStage("Fremennik Trials") in 1..99){ - loadFile(CouncilWorkerFTDialogue(1)) - } - npc("'Ello there.") - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return CouncilWorkerDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(1287) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt deleted file mode 100644 index 50dda5aed..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt +++ /dev/null @@ -1,137 +0,0 @@ -package rs09.game.content.dialogue - -import api.DialUtils.splitLines -import core.game.component.Component -import core.game.content.dialogue.DialogueInterpreter -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.Entity -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import rs09.tools.START_DIALOGUE - -abstract class DialogueFile { - var player: Player? = null - var npc: NPC? = null - var interpreter: DialogueInterpreter? = null - open var stage = START_DIALOGUE - var dialoguePlugin: DialoguePlugin? = null - - abstract fun handle(componentID: Int, buttonID: Int) - fun load(player: Player, npc: NPC?, interpreter: DialogueInterpreter): DialogueFile{ - this.player = player - this.npc = npc - this.interpreter = interpreter - return this - } - - open fun npc(vararg messages: String?): Component? { - return if (npc == null) { - interpreter!!.sendDialogues( - npc!!.id, - if (npc!!.id > 8591) FacialExpression.OLD_NORMAL else FacialExpression.FRIENDLY, - *messages - ) - } else interpreter!!.sendDialogues( - npc, - if (npc!!.id > 8591) FacialExpression.OLD_NORMAL else FacialExpression.FRIENDLY, - *messages - ) - } - - open fun npc(id: Int, vararg messages: String?): Component? { - return interpreter!!.sendDialogues(id, FacialExpression.FRIENDLY, *messages) - } - - 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 player(vararg messages: String?): Component? { - return interpreter!!.sendDialogues(player, null, *messages) - } - - open fun player(expression: FacialExpression?, vararg messages: String?): Component? { - return interpreter!!.sendDialogues(player, expression, *messages) - } - - /** - * Use the automatic linesplitting feature in DialUtils to produce npc dialogues - * @param expr the FacialExpression to use, located in the FacialExpression enum. - * @param msg the message for the NPC to say - */ - open fun npcl(expr: FacialExpression?, msg: String?): Component? { - return npc(expr, *splitLines(msg!!)) - } - - open fun npcl(msg: String?): Component? { - return npc(*splitLines(msg!!)) - } - - /** - * Use the automatic linesplitting feature in DialUtils to produce player dialogues - * @param expr the FacialExpression to use, located in the FacialExpression enum. - * @param msg the message for the player to say - */ - open fun playerl(expr: FacialExpression?, msg: String?): Component? { - return player(expr, *splitLines(msg!!)) - } - - open fun playerl(msg: String?): Component? { - return player(*splitLines(msg!!)) - } - - open fun end(){ - if(interpreter != null) interpreter!!.close() - } - - 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) - } - - /** - * Use in place of setting the stage to END_DIALOGUE when you want to return to the default dialogue plugin at START_DIALOGUE - */ - fun endFile(){ - interpreter!!.dialogue.file = null - - } - - /** - * Return to the default dialogue plugin at the given stage. - * Should be used in place of setting a new stage. I.E. instead of "stage = END_DIALOGUE" use "returnAtStage(whatever stage)" - * @param stage The stage to return to. - */ - fun returnAtStage(toStage: Int){ - dialoguePlugin!!.file = null - dialoguePlugin!!.stage = toStage - } - - /** - * Use when you've entered a DialogueFile but current state does not match any possible conditionals. - * Sort-of a fail-safe in a sense. - */ - fun abandonFile(){ - interpreter!!.dialogue.file = null - player("Huh. Nevermind.") - } - - open fun getCurrentStage(): Int{ - return stage - } - - fun Int.substage(stage: Int): Int{ - return this + stage - } - - fun dialogue(vararg messages: String){ - player?.dialogueInterpreter?.sendDialogue(*messages) - } - -} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/DukeHoracioDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/DukeHoracioDialogue.kt deleted file mode 100644 index 31303db91..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/DukeHoracioDialogue.kt +++ /dev/null @@ -1,88 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.content.quest.free.dragonslayer.DragonSlayer -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.diary.DiaryType -import rs09.game.content.quest.free.dragonslayer.DukeHoracioDSDialogue -import rs09.game.content.quest.free.runemysteries.DukeHoracioRMDialogue -import rs09.game.content.quest.members.thelosttribe.DukeHoracioTLTDialogue -import rs09.tools.DIALOGUE_INITIAL_OPTIONS_HANDLE -import rs09.tools.END_DIALOGUE - -/** - * Core dialogue plugin for Duke Horacio, redirects to more specific DialogueFiles. - * @author Ceikry - */ -class DukeHoracioDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun getIds(): IntArray { - return intArrayOf(741) - } - - 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.isComplete("Lost Tribe") && player.questRepository.getQuest("Lost Tribe").isStarted(player)) { - addOption("Lost Tribe", DukeHoracioTLTDialogue(player.questRepository.getStage("Lost Tribe"))) - } - if (!sendChoices()) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings. Welcome to my castle.") - } - // Speak to the Duke of Lumbridge - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 2) - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - - DIALOGUE_INITIAL_OPTIONS_HANDLE -> { - npc("Greetings. Welcome to my castle.") - loadFile(optionFiles[buttonId - 1]) - } - - 0 -> { - interpreter.sendOptions("Select an Option", "Have you any quests for me?", "Where can I find money?") - stage = 1 - } - 1 -> when (buttonId) { - 1 -> { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Have any quests for me?") - stage = 20 - } - 2 -> { - interpreter.sendDialogues( - npc, - 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." - ) - stage = END_DIALOGUE - } - } - 20 -> { - npc("Let me see...") - if (!player.questRepository.isComplete("Rune Mysteries")) { - loadFile(DukeHoracioRMDialogue(player.questRepository.getStage("Rune Mysteries"))) - } else { - stage++ - } - } - 21 -> { - npc("Nope, I've got everything under control", "in the castle at the moment.") - stage = END_DIALOGUE - } - } - return true - } - - override fun newInstance(player: Player): DialoguePlugin { - return DukeHoracioDialogue(player) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/DumpContainer.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/DumpContainer.kt deleted file mode 100644 index 66486439b..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/DumpContainer.kt +++ /dev/null @@ -1,89 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.container.Container -import core.game.container.impl.EquipmentContainer -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.plugin.Initializable -import core.plugin.Plugin - -@Initializable -/** - * Represents the dialogue plugin used to handle the deposit all/deposit inventory/etc button - * @author Splinter - * @author James Triantafylos - */ -class DumpContainer(player: Player? = null) : DialoguePlugin(player) { - val ID = 628371 - - override fun newInstance(player: Player?): DialoguePlugin { - return DumpContainer(player) - } - - override fun open(vararg args: Any?): Boolean { - options("Deposit Inventory", "Deposit Equipment", "Deposit Beast of Burden", "Cancel") - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (buttonId) { - 1 -> { - if (player.inventory.isEmpty) { - player.packetDispatch.sendMessage("You have nothing in your inventory to deposit.") - } else { - dump(player.inventory) - } - } - 2 -> { - if (player.equipment.isEmpty) { - player.packetDispatch.sendMessage("You have no equipment to deposit.") - } else { - dump(player.equipment) - } - } - 3 -> { - player.familiarManager.dumpBob() - } - 4 -> { - } - } - end() - return true - } - - override fun getIds(): IntArray { - return intArrayOf(ID) - } - - /** - * Dumps a container. - * @param inventory the player's inventory. - * @author ceik - * @author James Triantafylos - */ - fun dump(inventory: Container) { - val bank = player.bank - for (item: Item in inventory.toArray().filterNotNull()) { - if (!bank.hasSpaceFor(item)) { - player.packetDispatch.sendMessage("You have no more space in your bank.") - return - } - if (!bank.canAdd(item)) { - player.packetDispatch.sendMessage("A magical force prevents you from banking your " + item.name + ".") - } else { - if (inventory is EquipmentContainer) { - val plugin = item.definition.handlers["equipment"] - if (plugin != null && plugin is Plugin<*>) { - plugin.fireEvent("unequip", player, item) - } - } - inventory.remove(item) - bank.add(if (item.definition.isUnnoted) item else Item(item.noteChange, item.amount)) - } - } - inventory.update() - bank.update() - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/ElsieDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/ElsieDialogue.kt deleted file mode 100644 index 228d86491..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/ElsieDialogue.kt +++ /dev/null @@ -1,98 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.dialogue.DialoguePlugin -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 org.rs09.consts.Items - -/** - * Elsie - * @author afaroutdude - */ -@Initializable -class ElsieDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun open(vararg args: Any?): Boolean { - when (player.inventory.containsAtLeastOneItem(Items.CUP_OF_TEA_712)) { - true -> npc("Ooh - that looks like a lovely cup of tea, dearie. Is it for", "me?").also { stage = 100 } - false -> npc("Hello dearie! What can old Elsie do for you?").also { stage = 10 } - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - 999 -> end() - - 10 -> interpreter.sendOptions("What would you like to say?", "What are you making?", "Can you tell me a story?", "Can you tell me how to get rich?").also { stage++ } - 11 -> - when (buttonId) { - 1 -> player("What are you making?").also { stage = 20 } - 2 -> player("Can you tell me a story?").also { stage = 30 } - 3 -> player("Can you tell me how to get rich?").also { stage = 40 } - } - 20 -> player("What are you making?").also { stage++ } - 21 -> npc("I'm knitting a new stole for Father Lawrence", "downstairs. He could do with something to keep his", "neck warm, standing in that draughty old church", "all day.").also { stage++ } - 22 -> interpreter.sendOptions("What would you like to say?", "Can you tell me a story?", "Can you tell me how to get rich?").also { stage++ } - 23 -> - when (buttonId) { - 1 -> player("Can you tell me a story?").also { stage = 30 } - 2 -> player("Can you tell me how to get rich?").also { stage = 40 } - } - 30 -> player("Can you tell me a story?").also { stage++ } - 31 -> npc("Maybe I could tell you a story if you'd fetch me", "a nice cup of tea.").also { stage++ } - 32 -> player("I'll think about it.").also { stage == 999 } - 40 -> player("Can you tell me how to get rich??").also { stage++ } - 41 -> npc("Well, dearie, I'm probably not the best person to ask about money, but I think the best thing would be for you to get a good trade. If you've got a trade you can earn your way, that's what my old father told me,").also { stage++ } - 42 -> npc("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 folks to earn a living! So get yourself a good trade, and keep").also { stage++ } - 43 -> npc("working at it. There's always folks wanting to buy ore and food around here.").also { stage++ } - 44 -> player("Thanks, old woman.").also { stage++ } - 45 -> interpreter.sendOptions("What would you like to say?", "What are you making?", "Can you tell me a story?").also { stage++ } - 46 -> - when (buttonId) { - 1 -> player("What are you making?").also { stage = 20 } - 2 -> player("Can you tell me a story?").also { stage = 30 } - } - - 100 -> interpreter.sendOptions("What would you like to say?", "Yes, you can have it.", "No, keep your hands off my tea.").also { stage++ } - 101 -> - when (buttonId) { - 1 -> player("Yes, you can have it.").also { stage++ } - 2 -> player("No, keep your hands off my tea.").also { stage = 120 } - } - 102 -> player("Yes, you can have it.").also { stage++ } - 103 -> npc("Ahh, there's nothing like a nice cuppa tea. I know what,", "I'll tell you a story to thank you for the lovely tea...").also { - player.inventory.remove(Item(Items.CUP_OF_TEA_712)) - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 14) - stage++ - } - 104 -> npc("A long time ago, when I was a little girl, there was a", "handsome young man living in Varrock. 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++ } - 105 -> npc("But young Dissy - that was the young man's nickname", "- he was a wild young thing. One night he gathered", "some lads together, and after the evening prayer-", "meeting they all put on masks and sneaked down to the").also { stage++ } - 106 -> npc("evil temple in the south of the city, the evil one. The", "next day, there was quite a hubbub. The guards told us", "that someone had painted 'Saradomin pwns' on the wall", "of the Zamorakian temple!").also { stage++ } - 107 -> npc("Now, we'd always been taught to keep well away from", "that dreadful place, but it really did us all good to see", "someone wasn't afraid of the scum who live at that end", "of town. Old Father Packett was furious, but Dissy just").also { stage++ } - 108 -> npc("laughed it off.").also { stage++ } - 109 -> npc("Dissy left town after that, saying he wanted to see the", "world. It was such a shame, he had the most handsome", "shoulders...").also { stage++ } - 110 -> npc("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. He said", "Dissy had become a really famous man and there was").also { stage++ } - 111 -> npc("going to be a book about him. Well, that's all good, but I", "do wish Dissy had just come back to Varrock. I did", "miss him so much... well, until I met my Freddie and", "we got married, but that's another story.").also { stage++ } - 112 -> player("Thank you. I'll leave you to your knitting now.").also { stage = 999 } - 120 -> npc("Aww. Maybe another time. Anyway, what can old Elsie do for you?").also { stage++ } - 121 -> - when (buttonId) { - 1 -> player("What are you making?").also { stage = 20 } - 2 -> player("Can you tell me a story?").also { stage = 122 } - } - 122 -> npc("Well, maybe I could tell a story if you'd give me that lovely cup of tea you've got there.").also { stage = 100 } - } - return true; - } - - - override fun newInstance(player: Player?): DialoguePlugin { - return ElsieDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(5915) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/GardenerDialoguePlugin.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/GardenerDialoguePlugin.kt deleted file mode 100644 index 93149710b..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/GardenerDialoguePlugin.kt +++ /dev/null @@ -1,167 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.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 -import org.rs09.consts.Items -import rs09.game.node.entity.skill.farming.FarmerPayOptionDialogue -import rs09.game.node.entity.skill.farming.Farmers -import rs09.game.node.entity.skill.farming.FarmingPatch -import rs09.tools.END_DIALOGUE - -@Initializable -class GardenerDialoguePlugin(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player?): 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 } - } - - 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++ } - } - - 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]) - } - - 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 } - } - - 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){ - 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)) - } else { - player.sendMessage("You need 25 gp to pay for that.") - } - } - 2 -> end() - } - - 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){ - 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)) - } else { - player.sendMessage("You need 35 gp to pay for that.") - } - } - 2 -> end() - } - - 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){ - 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)) - } else { - player.sendMessage("You need 15 gp to pay for that.") - } - } - 2 -> end() - } - - 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++ } - 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)) - } else { - player.sendMessage("You need 25 gp to pay for that.") - } - } - 2 -> end() - } - - 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){ - 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)) - } else { - player.sendMessage("You need 15 gp to pay for that.") - } - } - 2 -> end() - } - - 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){ - 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)) - } else { - player.sendMessage("You need 15 gp to pay for that.") - } - } - 2 -> end() - } - } - 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) - } - } - - override fun getIds(): IntArray { - return Farmers.values().map(Farmers::id).toIntArray() - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/GeoffreyDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/GeoffreyDialogue.kt deleted file mode 100644 index ecc1bc303..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/GeoffreyDialogue.kt +++ /dev/null @@ -1,78 +0,0 @@ -package rs09.game.content.dialogue - -import core.Util -import core.game.content.dialogue.DialoguePlugin -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 org.rs09.consts.Items - -@Initializable -class GeoffreyDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun open(vararg args: Any?): Boolean { - val diary = player.achievementDiaryManager.getDiary(DiaryType.SEERS_VILLAGE) - if (diary.levelRewarded.any()) { - 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 { - player("Hello there. You look busy.") - stage = 0 - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return GeoffreyDialogue(player) - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - 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++ } - 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++ } - 5 -> player("Erm, okay. Maybe I can have some of your spare flax?").also { stage++ } - 6 -> npc("No. I don't trust outsiders. Who knows what depraved", "things you would do with the GLORIOUS flax? Only", "locals know how to treat it right.").also { stage++ } - 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 - 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 - 102 -> {rewardFlax(120, "Yes. Sir Kay has instructed me to give you an", "allowance of 120 GLORIOUS flax a day. I'm not going", "to argue with a knight, so here you go.")} - } - return true - } - - 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())) - stage = 999 - } - - override fun getIds(): IntArray { - return intArrayOf(8590) - } - -} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/KingRoaldDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/KingRoaldDialogue.kt deleted file mode 100644 index f61e625c8..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/KingRoaldDialogue.kt +++ /dev/null @@ -1,88 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import rs09.game.content.activity.allfiredup.KingRoaldAFUMiniDialogue -import rs09.game.content.quest.free.shieldofarrav.KingRoaldArravDialogue -import rs09.game.content.quest.members.allfiredup.KingRoaldAFUDialogue -import rs09.game.content.quest.members.priestinperil.KingRoaldPIPDialogue -import rs09.tools.DIALOGUE_INITIAL_OPTIONS_HANDLE -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE - -/** - * Central dialogue plugin for King Roald. Reroutes to the more specific DialogueFiles - * @author Ceikry - * @version 1.0 - */ -class KingRoaldDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun newInstance(player: Player): DialoguePlugin { - return KingRoaldDialogue(player) - } - - override fun open(vararg args: Any): Boolean { - npc = args[0] as NPC - - if (player.getAttribute("afu-mini:ring", false) || player.getAttribute("afu-mini:gloves", false) || player.getAttribute("afu-mini:adze", false)) { - player("Your Majesty! I did it!") - loadFile(KingRoaldAFUMiniDialogue()) - 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"))) - } - } else { - addOption("Priest in Peril", KingRoaldPIPDialogue(player.questRepository.getStage("Priest in Peril"))) - } - - if (player.questRepository.getQuest("Shield of Arrav").isStarted(player) && !player.questRepository.getQuest("Shield of Arrav").isCompleted(player)) { - addOption("Shield of Arrav", KingRoaldArravDialogue()) - } - - if(!sendChoices()){ - player("Greetings, your Majesty.") - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - //Default (non-specific) dialogue - when (stage) { - - DIALOGUE_INITIAL_OPTIONS_HANDLE -> { - player("Greetings, your Majesty.") - loadFile(optionFiles[buttonId - 1]) - } - - START_DIALOGUE -> { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you have anything of importance to say?") - stage = 1 - } - 1 -> { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "...Not really.") - stage = 2 - } - 2 -> { - interpreter.sendDialogues( - npc, - FacialExpression.HALF_GUILTY, - "You will have to excuse me, then. I am very busy as I", - "have a kingdom to run!" - ) - stage = END_DIALOGUE - } - - END_DIALOGUE -> end() - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(648, 2590, 5838) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/KjallakOnChopDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/KjallakOnChopDialogue.kt deleted file mode 100644 index 20a3cccbf..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/KjallakOnChopDialogue.kt +++ /dev/null @@ -1,12 +0,0 @@ -package rs09.game.content.dialogue - -import rs09.tools.END_DIALOGUE - -class KjallakOnChopDialogue : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - 0 -> npc("Hey! You're not allowed to chop those!").also { stage++ } - 1 -> player("Oh, ok...").also { stage = END_DIALOGUE } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/OttoGodblessedDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/OttoGodblessedDialogue.kt deleted file mode 100644 index 3487805fa..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/OttoGodblessedDialogue.kt +++ /dev/null @@ -1,61 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.plugin.Initializable -import rs09.tools.END_DIALOGUE - -/** - * Represents the dialogue plugin used for Otto - * @author Splinter - * @version 1.0 - */ -@Initializable -class OttoGodblessedDialogue(player: Player? = null) : DialoguePlugin(player) { - - override fun newInstance(player: Player): DialoguePlugin { - return OttoGodblessedDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - npc("Good day, you seem a hearty warrior. Maybe even", "some barbarian blood in that body of yours?") - stage = -1 - return true - } - - override fun init() { - super.init() - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - -1 -> options("Ask about barbarian training", "Nevermind.").also { stage++ } - 0 -> when(buttonId){ - 1 -> player("Is there anything you can teach me?").also { stage = 20 } - 2 -> stage++ - } - - 1 -> stage = END_DIALOGUE - - - 20 -> npc("I can teach you how to fish.").also { stage++ } - 21 -> player("Oh, that's pretty underwhelming. But uhhh, okay!").also { stage++ } - 22 -> npc("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++ } - 23 -> player("The whole pole?").also { stage++ } - 24 -> npc(FacialExpression.ANGRY, "No, not the whole pole!").also { stage++ } - 25 -> npc("Look, just... grab the pole under my bed","and go click on that fishing spot.").also { stage++ } - 26 -> player(FacialExpression.ASKING,"...click?").also { stage++ } - 27 -> npc(FacialExpression.FURIOUS, "JUST GO DO IT!").also { stage++; player.setAttribute("/save:barbtraining:fishing",true) } - 28 -> end() - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(2725) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/ThormacDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/ThormacDialogue.kt deleted file mode 100644 index 50812bf52..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/ThormacDialogue.kt +++ /dev/null @@ -1,158 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -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 org.rs09.consts.Items - -/** - * Thormac - * Involved in Scorpion Catcher - * @author afaroutdude - */ -@Initializable -class ThormacDialogue(player: Player? = null) : DialoguePlugin(player) { - val scorpionCageEmpty = Item(Items.SCORPION_CAGE_456) - val scorpionCageFull = Item(Items.SCORPION_CAGE_463) - val scorpionCages: IntArray = intArrayOf( - 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, - Items.SCORPION_CAGE_463 - ) - - val COMPONENT = Component(332) - - override fun open(vararg args: Any?): Boolean { - val scorpionStage = try { - player?.questRepository?.getStage("Scorpion Catcher") ?: 100 - } catch (e: Exception) { - 100 - } - when (scorpionStage) { - 0 -> { - npc("Hello I am Thormac the Sorcerer, I don't suppose you could be of assistance to me?");stage = 0 - } - 10, 20, 30, 40, 50, 60, 70, 80, 90 -> { - npc("How goes your quest?");stage = 70 - } - 100 -> { - npc("Thank you for rescuing my scorpions.");stage = 100 - } - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - 999 -> {end()} - - // initial convo - 0 -> {options("What do you need assistance with?", "I'm a little busy.");stage++} - 1 -> when(buttonId){ - 1 -> {player ("What do you need assistance with?");stage=2} - 2 -> {player ("I'm a little busy.");stage=50} - } - 2 -> {npc ("I've lost my pet scorpions. They're lesser Kharid scorpions, a very rare breed. I left their cage door open, now I don't know where they've gone. There's three of them, and they're quick little beasties. They're all over 2009scape.")} - 3 -> {options ("So how would I go about catching them then?", "What's in it for me?", "I'm not interested then.");stage++} - 4 -> when(buttonId){ - 1 -> {player("So how would I go about catching them then?");stage++} - 2 -> {player("What's in it for me?");stage=20}} - 3 -> {player ("I'm not interested then.");stage=51} - 5 -> {npc("Well I have a scorpion cage here which you can use to catch them in.");stage++} - 6 -> { - sendDialogue("Thormac gives you a cage.") - player.inventory.add(scorpionCageEmpty) - stage++ - } - 7 -> {npc ("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.");stage++} - 8 -> {options ("What's in it for me?", "Ok, I will do it then.");stage++} - 9 -> when(buttonId){ - 1 -> {player("What's in it for me?");stage=20} - 2 -> { - player("Ok, I will do it then.") - // start quest here - stage = 999 - } - } - - 20 -> {npc ("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. 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.");stage++} - 21 -> {options("So how would I go about catching them then?","Ok, I will do it then.");stage++} - 22 -> when(buttonId) { - 1 -> {player("So how would I go about catching them then?");stage=5} - 2 -> { - player("Ok, I wil do it then.") - // start quest here - stage = 999 - } - } - 50 -> {npc ("Come back if you have some spare time.");stage=999} // invention - 51 -> {npc ("Come back if you change your mind.");stage=999} // invention - - // after that - 70 -> { - if (!player.inventory.containsAtLeastOneItem(scorpionCages) - && !player.bank.containsAtLeastOneItem(scorpionCages)) { - player("I've lost my cage.") - stage = 71 - } else if (!player.inventory.containsAtLeastOneItem(scorpionCageFull.id)) { - player("I've not caught all the scorpions yet.") - stage = 72 - } else { - player("I have retrieved all your scorpions.") - stage = 73 - } - } - 71 -> { - // if lost or destroyed cage - npc("Ok, here's another cage. You're almost as bad at losing things as me.") - player.inventory.add(scorpionCageEmpty) - stage=999 - } - 72 -> {npc("Well remember to go speak to the Seers, North of here, if you need any help.");stage=999} - 73 -> { - npc("Aha, my little scorpions home at last!") - // complete quest here - stage=999 - } - - // post-quest - 100 -> { - if (!player.inventory.contains(84,1)) { - options("That's okay.", "You said you'd enchant my battlestaff for me.") - } else { - options("That's okay.", "You said you'd enchant my battlestaff for me.", "Could you enchant my Armadyl battlestaff?") - } - stage++ - } - 101 -> when (buttonId) { - 1 -> {player("That's okay.");stage=999} - 2 -> {player ("You said you'd enchant my battlestaff for me.");stage=110} - 3 -> {player ("Could you enchant my Armadyl battlestaff?");stage=150} - } - 110 -> {npc("Yes, although it'll cost you 40,000 coins for the","materials. What kind of staff did you want enchanting?");stage++} - 111 -> { - end() - player.interfaceManager.open(COMPONENT) - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 1, 1) - } - } - return true; - } - - - override fun newInstance(player: Player?): DialoguePlugin { - return ThormacDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(389) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/WysonTheGardenerDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/WysonTheGardenerDialogue.kt deleted file mode 100644 index 67ae46595..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/WysonTheGardenerDialogue.kt +++ /dev/null @@ -1,249 +0,0 @@ -package rs09.game.content.dialogue - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.global.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 rs09.game.node.entity.player.link.diary.DiaryLevel - -/** - * Represents the Wyson the gardener dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -class WysonTheGardenerDialogue : DialoguePlugin { - /** - * If its a bird nest reward. - */ - private var birdNest = false - - /** - * Constructs a new `WysonTheGardenerDialogue` `Object`. - */ - constructor() { - /** - * empty. - */ - } - - /** - * Constructs a new `WysonTheGardenerDialogue` `Object`. - * @param player the player. - */ - constructor(player: Player?) : super(player) {} - - override fun newInstance(player: Player): DialoguePlugin { - return WysonTheGardenerDialogue(player) - } - - 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.") - stage = 100 - 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 - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - 0 -> { - options("Yes please, I need woad leaves.", "Sorry, but I'm not interested.") - stage = 1 - } - 1 -> when (buttonId) { - 1 -> { - player("Yes please, I need woad leaves.") - stage = 10 - } - 2 -> { - player("Sorry, but I'm not interested.") - stage = 200 - } - } - 10 -> { - npc("How much are you willing to pay?") - stage = 11 - } - 11 -> { - options("How about 5 coins?", "How about 10 coins?", "How about 15 coins?", "How about 20 coins?") - stage = 12 - } - 12 -> when (buttonId) { - 1 -> { - player("How about 5 coins?") - stage = 110 - } - 2 -> { - player("How about 10 coins?") - stage = 110 - } - 3 -> { - player("How about 15 coins?") - stage = 130 - } - 4 -> { - player("How about 20 coins?") - stage = 140 - } - } - 110 -> { - npc("No no, that's far too little. Woad leaves are hard to get. I", "used to have plenty but someone kept stealing them off", "me.") - stage = 111 - } - 111 -> end() - 130 -> { - 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) - player("Thanks.") - player.packetDispatch.sendMessage("You buy a woad leaf from Wyson.") - stage = 132 - } else { - end() - player.packetDispatch.sendMessage("You need 15 cold coins to buy a woad leaf.") - } - 132 -> { - npc("I'll be around if you have any more gardening needs.") - stage = 133 - } - 133 -> end() - 140 -> { - npc("Thanks for being generous", "here's an extra woad leave.") - 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++ - } - player("Thanks.") - player.packetDispatch.sendMessage("You buy two woad leaves from Wyson.") - stage = 132 - } else { - end() - player.packetDispatch.sendMessage("You need 15 cold coins to buy a woad leaf.") - } - 200 -> { - npc("Fair enough.") - stage = 201 - } - 201 -> end() - 100 -> { - options("Yes, I will trade the mole claws.", "Okay, I will trade the mole skin.", "I'd like to trade both.", "No, thanks.") - stage = 900 - } - 900 -> when (buttonId) { - 1 -> { - player("Yes, I will trade the mole claws.") - stage = 910 - } - 2 -> { - player("Okay, I will trade the mole skin.") - stage = 920 - } - 3 -> { - player("I'd like to trade both.") - stage = 930 - } - 4 -> { - player("No, thanks.") - stage = 999 - } - } - 910 -> { - if (!player.inventory.containsItem(MOLE_CLAW)) { - player("Sorry, I don't have any mole claws.") - stage = 999 - } - end() - addRewards() - } - 920 -> { - if (!player.inventory.containsItem(MOLE_SKIN)) { - player("Sorry, I don't have any mole skins.") - stage = 999 - } - end() - addRewards() - } - 930 -> { - if (!player.inventory.containsItem(MOLE_CLAW) && !player.inventory.containsItem(MOLE_SKIN)) { - player("Sorry, I don't have any.") - stage = 999 - } - addRewards() - end() - } - 999 -> end() - } - return true - } - - /** - * 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) - } - - //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) - } - } - } - - override fun getIds(): IntArray { - 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/kotlin/rs09/game/content/dialogue/region/jatizso/MordGunnarsDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/MordGunnarsDialogue.kt deleted file mode 100644 index d30a74bdf..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/MordGunnarsDialogue.kt +++ /dev/null @@ -1,49 +0,0 @@ -package rs09.game.content.dialogue.region.jatizso - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import org.rs09.consts.NPCs -import rs09.game.util.region.rellekka.RellekkaDestination -import rs09.game.util.region.rellekka.RellekkaUtils -import rs09.tools.END_DIALOGUE - -@Initializable -class MordGunnarsDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player?): DialoguePlugin { - return MordGunnarsDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - if(npc.id == NPCs.MORD_GUNNARS_5481){ - npcl(FacialExpression.FRIENDLY, "Would you like to sail to Jatizso?") - } else { - npcl(FacialExpression.FRIENDLY, "Would you like to sail back to Rellekka?") - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> options("Yes, please.", "No, thanks.").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 -> { - end() - RellekkaUtils.sail(player, if(npc.id == NPCs.MORD_GUNNARS_5481) RellekkaDestination.RELLEKKA_TO_JATIZSO else RellekkaDestination.JATIZSO_TO_RELLEKKA) - } - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.MORD_GUNNARS_5482, NPCs.MORD_GUNNARS_5481) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/TowerGuardDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/TowerGuardDialogue.kt deleted file mode 100644 index 095aaf348..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/jatizso/TowerGuardDialogue.kt +++ /dev/null @@ -1,52 +0,0 @@ -package rs09.game.content.dialogue.region.jatizso - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners -import rs09.tools.END_DIALOGUE - -@Initializable -class TowerGuardDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player?): DialoguePlugin { - return TowerGuardDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - playerl(FacialExpression.HALF_ASKING, "What are you doing here?") - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> npc(FacialExpression.NEUTRAL, "I'M ON SHOUTING DUTY!").also { stage++ } - 1 -> playerl(FacialExpression.HALF_THINKING, "No need to shout.").also { stage++ } - 2 -> npc(FacialExpression.NEUTRAL, "I'M SORRY I'VE BEEN SHOUTING","INSULTS SO LONG I CAN'T HELP IT!").also { stage++ } - 3 -> playerl(FacialExpression.HALF_THINKING, "Who are you insulting?").also { stage++ } - 4 -> npc(FacialExpression.NEUTRAL, "THE TOWER IN ${if(npc.id == NPCs.GUARD_5489) "NEITIZNOT" else "JATIZSO"}.","THEY SHOUT INSULTS BACK.").also { stage++ } - 5 -> playerl(FacialExpression.ASKING, "Err, why?" ).also { stage++ } - 6 -> npc(FacialExpression.NEUTRAL, "THE ${if(npc.id == NPCs.GUARD_5489) "KING" else "BURGHER"} TELLS US TO.").also { stage++ } - 7 -> playerl(FacialExpression.HALF_THINKING, "Your ${if(npc.id == NPCs.GUARD_5489) "King" else "Burgher"} is a strange person.").also { stage++ } - 8 -> options("Can I watch? I'm curious.", "Oh well, I'd better get going.").also { stage++ } - 9 -> when(buttonId){ - 1 -> playerl(FacialExpression.ASKING, "Can I watch? I'm curious.").also { stage++ } - 2 -> playerl(FacialExpression.HALF_THINKING, "Oh well, I'd better get going.").also { stage = END_DIALOGUE } - } - 10 -> npcl(FacialExpression.NEUTRAL, "IF YOU LIKE!").also { - stage = END_DIALOGUE - InteractionListeners.run(NPCs.GUARD_5489, InteractionListener.NPC, "watch-shouting", player, npc) - } - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.GUARD_5489, NPCs.GUARD_5490) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/EnchantedJewelleryDialogueFile.kt b/Server/src/main/kotlin/rs09/game/content/global/EnchantedJewelleryDialogueFile.kt deleted file mode 100644 index 519c66b14..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/EnchantedJewelleryDialogueFile.kt +++ /dev/null @@ -1,18 +0,0 @@ -package rs09.game.content.global - -import core.game.content.global.EnchantedJewellery -import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.START_DIALOGUE - -class EnchantedJewelleryDialogueFile(val jewellery: EnchantedJewellery, val item: Item) : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - START_DIALOGUE -> interpreter!!.sendOptions("Where would you like to go?", *jewellery.options).also { stage++ } - 1 -> { - jewellery.use(player, item, buttonID - 1,item.slot < player!!.equipment.capacity() && player!!.equipment[item.slot] == item) - end() - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/GlobalKillCounter.kt b/Server/src/main/kotlin/rs09/game/content/global/GlobalKillCounter.kt deleted file mode 100644 index 9be43721d..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/GlobalKillCounter.kt +++ /dev/null @@ -1,107 +0,0 @@ -package rs09.game.content.global - -import core.game.node.entity.player.Player -import rs09.game.system.SystemLogger -import java.io.File -import java.io.FileReader -import java.io.FileWriter -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.ServerConstants -import core.game.node.item.Item - -object GlobalKillCounter { - val kills: HashMap> = HashMap() - val rare_drops: HashMap> = HashMap() - - fun init() { - 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("Failed parsing ${file.name} - stack trace below.") - e.printStackTrace() - } - } - - @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() { - 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() } - } - - @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/kotlin/rs09/game/content/global/NPCDropTable.kt b/Server/src/main/kotlin/rs09/game/content/global/NPCDropTable.kt deleted file mode 100644 index 3b2684860..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/NPCDropTable.kt +++ /dev/null @@ -1,49 +0,0 @@ -package rs09.game.content.global - -import core.game.content.ttrail.ClueLevel -import core.game.content.ttrail.ClueScrollPlugin -import core.game.node.entity.npc.drop.RareDropTable -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.tools.RandomFunction -import org.rs09.consts.Items - -class NPCDropTable : WeightBasedTable() { - val charmDrops = WeightBasedTable() - - fun addToCharms(element: WeightedItem): Boolean { - return charmDrops.add(element) - } - - override fun roll(player: Player?): ArrayList { - val items= ArrayList(3) - items.addAll(guaranteedItems.map { it.getItem() }.toList()) - - // 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(null)) - - if(size == 1){ - items.add(get(0).getItem()) - return items - } - if(isNotEmpty()) { - var tempWeight = RandomFunction.randomDouble(totalWeight) - for (item in shuffled()) { - tempWeight -= item.weight - if (tempWeight <= 0) { - when(item.id){ - SLOT_CLUE_EASY -> items.add(ClueScrollPlugin.getClue(ClueLevel.EASY)) - SLOT_CLUE_MEDIUM -> items.add(ClueScrollPlugin.getClue(ClueLevel.MEDIUM)) - SLOT_CLUE_HARD -> items.add(ClueScrollPlugin.getClue(ClueLevel.HARD)) - SLOT_RDT -> items.add(RareDropTable.retrieve()) - else -> items.add(item.getItem()) - } - break - } - } - } - return items - } - -} diff --git a/Server/src/main/kotlin/rs09/game/content/global/WeightBasedTable.kt b/Server/src/main/kotlin/rs09/game/content/global/WeightBasedTable.kt deleted file mode 100644 index 38c4ba653..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/WeightBasedTable.kt +++ /dev/null @@ -1,105 +0,0 @@ -package rs09.game.content.global - -import core.cache.def.impl.ItemDefinition -import core.game.content.ttrail.ClueLevel -import core.game.content.ttrail.ClueScrollPlugin -import core.game.node.entity.npc.drop.RareDropTable -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.tools.RandomFunction -import org.rs09.consts.Items - -open class WeightBasedTable : ArrayList() { - var totalWeight = 0.0 - val guaranteedItems = ArrayList(5) - - override fun add(element: WeightedItem): Boolean { - totalWeight += element.weight - return if(element.guaranteed) guaranteedItems.add(element) - else super.add(element) - } - - fun roll(): ArrayList{ - return roll(null) - } - - open fun roll(player: Player?): ArrayList{ - val items= ArrayList(3) - items.addAll(guaranteedItems.map { it.getItem() }.toList()) - - if(player?.inventory?.isFull == true){ - return items - } - - if(isNotEmpty()) { - var tempWeight = RandomFunction.randomDouble(totalWeight) - for (item in shuffled()) { - tempWeight -= item.weight - if (tempWeight <= 0) { - when(item.id){ - SLOT_CLUE_EASY -> items.add(ClueScrollPlugin.getClue(ClueLevel.EASY)) - SLOT_CLUE_MEDIUM -> items.add(ClueScrollPlugin.getClue(ClueLevel.MEDIUM)) - SLOT_CLUE_HARD -> items.add(ClueScrollPlugin.getClue(ClueLevel.HARD)) - SLOT_RDT -> items.add(RareDropTable.retrieve()) - else -> items.add(item.getItem()) - } - break - } - } - } - return items - } - - open fun canRoll(player: Player): Boolean{ - val guaranteed = guaranteedItems.map { it.getItem() }.toTypedArray() - return (player.inventory.hasSpaceFor(*guaranteed) && guaranteed.isNotEmpty()) || !player.inventory.isFull - } - - - fun insertEasyClue(weight: Double): WeightBasedTable{ - this.add(WeightedItem(SLOT_CLUE_EASY,1,1,weight,false)) - return this - } - - fun insertMediumClue(weight: Double): WeightBasedTable{ - this.add(WeightedItem(SLOT_CLUE_MEDIUM,1,1,weight,false)) - return this - } - - fun insertHardClue(weight: Double): WeightBasedTable{ - this.add(WeightedItem(SLOT_CLUE_HARD,1,1,weight,false)) - return this - } - - fun insertRDTRoll(weight: Double): WeightBasedTable{ - this.add(WeightedItem(SLOT_RDT,1,1,weight,false)) - return this - } - - - companion object { - @JvmStatic - fun create(vararg items: WeightedItem): WeightBasedTable{ - val table = WeightBasedTable() - items.forEach { - table.add(it) - } - return table - } - - @JvmField - val SLOT_RDT = Items.TINDERBOX_31 - val SLOT_CLUE_EASY = Items.TOOLKIT_1 - val SLOT_CLUE_MEDIUM = Items.ROTTEN_POTATO_5733 - val SLOT_CLUE_HARD = Items.GRANITE_LOBSTER_POUCH_12070 - } - - override fun toString(): String { - val builder = StringBuilder() - for(item in this){ - builder.append("${ItemDefinition.forId(item.id).name} || Weight: ${item.weight} || MinAmt: ${item.minAmt} || maxAmt: ${item.maxAmt}") - builder.appendLine() - } - return builder.toString() - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/action/EquipHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/action/EquipHandler.kt deleted file mode 100644 index e252b41cb..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/action/EquipHandler.kt +++ /dev/null @@ -1,135 +0,0 @@ -package rs09.game.content.global.action - -import core.game.container.impl.EquipmentContainer -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.audio.Audio -import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags -import core.game.system.task.Pulse -import core.game.world.map.zone.ZoneBorders -import core.plugin.Plugin -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners -import rs09.game.system.config.ItemConfigParser -import rs09.game.world.GameWorld - -/** - * Represents the equipment equipping handler plugin. - * @author Ceikry - * @author Woah - */ -class EquipHandler : InteractionListener() { - - override fun defineListeners() { - - on(ITEM,"equip","wield","wear"){player,node -> - handleEquip(player,node) - return@on true - } - - - - } - - fun handleEquip(player: Player,node: Node){ - val item = node.asItem() - - if(item == null || player.inventory[item.slot] != item || item.name.toLowerCase().contains("goblin mail")){ - return - } - - val equipStateListener = item.definition.getConfiguration>("equipment", null) - if(equipStateListener != null){ - val bool = equipStateListener.fireEvent("equip",player,item) - if(bool != true){ - return - } - } - if(!InteractionListeners.run(node.id,player,node,true)){ - return - } - - val lock = player.locks.equipmentLock - if (lock != null && lock.isLocked) { - if (lock.message != null) { - player.packetDispatch.sendMessage(lock.message) - } - return - } - - if (player.equipment.add(item, item.slot, true, true)) { - //check if a brawling glove is being equipped and register it - if (item.id in 13845..13857) { - player.debug("Registering gloves... ID: " + item.id) - player.brawlingGlovesManager.registerGlove(item.id) - } - if (item.id == Items.BLACK_CHAINBODY_1107 && player.getAttribute("diary:falador:black-chain-bought", false) - && ZoneBorders(2969, 3310, 2975, 3314, 0).insideBorder(player) - ) { - player.achievementDiaryManager.finishTask(player, DiaryType.FALADOR, 0, 2) - } - player.dialogueInterpreter.close() - player.audioManager.send(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(SlayerEquipmentFlags.isSlayerEq(item.id)){ - SlayerEquipmentFlags.updateFlags(player) - } - } - } - - companion object { - /** - * Unequips an item. - * @param player the player. - * @param slot the slot. - * @param itemId the item id. - */ - @JvmStatic - fun unequip(player: Player, slot: Int, itemId: Int) { - if (slot < 0 || slot > 13) { - return - } - val item = player.equipment[slot] ?: return - val lock = player.locks.equipmentLock - if (lock != null && lock.isLocked) { - if (lock.message != null) { - player.packetDispatch.sendMessage(lock.message) - } - return - } - if (slot == EquipmentContainer.SLOT_WEAPON) { - player.packetDispatch.sendString("", 92, 0) - } - val maximumAdd = player.inventory.getMaximumAdd(item) - if (maximumAdd < item.amount) { - player.packetDispatch.sendMessage("Not enough free space in your inventory.") - return - } - val plugin = item.definition.getConfiguration>("equipment", null) - if (plugin != null) { - if (!(plugin.fireEvent("unequip", player, item) as Boolean)) { - return - } - } - InteractionListeners.run(itemId,player,item,false) - if (player.equipment.remove(item)) { - player.audioManager.send(Audio(2238, 10, 1)) - player.dialogueInterpreter.close() - player.inventory.add(item) - } - - if(SlayerEquipmentFlags.isSlayerEq(item.id)){ - SlayerEquipmentFlags.updateFlags(player) - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt deleted file mode 100644 index c34ec6fde..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt +++ /dev/null @@ -1,118 +0,0 @@ -package rs09.game.content.global.action - -import core.game.content.dialogue.FacialExpression -import core.game.content.global.GodType -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.entity.skill.runecrafting.RunePouch -import core.game.node.item.GroundItem -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.game.world.map.RegionManager -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items -import rs09.game.ai.AIRepository.Companion.getItems -import rs09.game.system.SystemLogger -import rs09.game.system.config.GroundSpawnLoader -import rs09.game.world.GameWorld - -/** - * A class used to handle the picking up of ground items. - * @author 'Vexia - */ -object PickupHandler { - /** - * Method used to take a ground item. - * @param player the player. - * @param item the item. - * @return `True` if taken. - */ - @JvmStatic - fun take(player: Player, item: GroundItem): Boolean { - if (item.location == null) { - player.packetDispatch.sendMessage("Invalid ground item!") - return true - } - if (!GroundItemManager.getItems().contains(item)) { - player.packetDispatch.sendMessage("Too late!") - return true - } - if (player.getAttribute("droppedItem:" + item.id, 0) > GameWorld.ticks) { //Splinter - SystemLogger.logAlert("$player tried to do the drop & quick pick-up Ground Item dupe.") - return true - } - if (item !is GroundSpawnLoader.GroundSpawn && item.isRemainPrivate && !item.droppedBy(player)) { - player.sendMessage("You can't take that item!") - return true - } - val add = Item(item.id, item.amount, item.charge) - if (!player.inventory.hasSpaceFor(add)) { - player.packetDispatch.sendMessage("You don't have enough inventory space to hold that item.") - return true - } - if (!canTake(player, item, 0)) { - return true - } - if (item.isActive && player.inventory.add(add)) { - if (!RegionManager.isTeleportPermitted(item.location)) { - player.animate(Animation.create(535)) - } - - if (item is GroundSpawnLoader.GroundSpawn && item.getId() == Items.SEAWEED_401 - && player.zoneMonitor.isInZone("karamja") - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.KARAMJA, 0, 7)) { - var seaweed = player.getAttribute("seaweed", 0) - seaweed++ - player.setAttribute("seaweed", seaweed) - player.achievementDiaryManager.updateTask(player, DiaryType.KARAMJA, 0, 7, seaweed >= 5) - } - // Collect five palm leaves - if (item.getId() == Items.PALM_LEAF_2339 && player.zoneMonitor.isInZone("karamja") && !player.achievementDiaryManager.hasCompletedTask(DiaryType.KARAMJA, 2, 7)) { - var palms = player.getAttribute("palms", 0) - palms++ - player.setAttribute("palms", palms) - player.achievementDiaryManager.updateTask(player, DiaryType.KARAMJA, 2, 7, palms >= 5) - } - GroundItemManager.destroy(item) - if (item.dropper?.isArtificial == true) { - getItems(item.dropper)?.remove(item) - } - player.audioManager.send(Audio(2582, 10, 1)) - } - return true - } - - /** - * Checks if the player can take an item. - * @param player the player. - * @param item the item. - * @param type the type (1= ground, 2=telegrab) - * @return `True` if so. - */ - @JvmStatic - fun canTake(player: Player, item: GroundItem, type: Int): Boolean { - if (item.dropper != null && !item.droppedBy(player) && player.ironmanManager.checkRestriction()) { - return false - } - if (item.id == 8858 || item.id == 8859) { - player.dialogueInterpreter.sendDialogues(4300, FacialExpression.FURIOUS, "Hey! You can't take that, it's guild property. Take one", "from the pile.") - return false - } - if (GodType.forCape(item) != null) { - if (GodType.hasAny(player)) { - player.sendMessages("You may only possess one sacred cape at a time.", "The conflicting powers of the capes drive them apart.") - return false - } - } - if (RunePouch.forItem(item) != null) { - if (player.hasItem(item)) { - player.sendMessage("A mystical force prevents you from picking up the pouch.") - return false - } - } - return if (item.hasItemPlugin()) { - item.plugin.canPickUp(player, item, type) - } else true - } -} diff --git a/Server/src/main/kotlin/rs09/game/content/global/travel/EssenceTeleport.kt b/Server/src/main/kotlin/rs09/game/content/global/travel/EssenceTeleport.kt deleted file mode 100644 index 490477409..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/travel/EssenceTeleport.kt +++ /dev/null @@ -1,180 +0,0 @@ -package rs09.game.content.global.travel - -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.item.Item -import core.game.system.task.Pulse -import rs09.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.RandomFunction - -/** - * Represents a utilitity class for rune essence teleporting. - * @author 'Vexia - */ -object EssenceTeleport { - /** - * Array of all the possible `Location` locations. - */ - val LOCATIONS = arrayOf(Location.create(2911, 4832, 0), Location.create(2913, 4837, 0), Location.create(2930, 4850, 0), Location.create(2894, 4811, 0), Location.create(2896, 4845, 0), Location.create(2922, 4820, 0), Location.create(2931, 4813, 0)) - - /** - * Represents the animation to use. - */ - private val ANIMATION = Animation(437) - - /** - * Method used to teleport a player. - * @param npc the npc. - * @param player the player. - */ - @JvmStatic - fun teleport(npc: NPC, player: Player) { - npc.animate(ANIMATION) - npc.faceTemporary(player, 1) - npc.graphics(Graphics(108)) - player.lock() - player.audioManager.send(125) - Projectile.create(npc, player, 109).send() - npc.sendChat("Senventior Disthinte Molesko!") - GameWorld.Pulser.submit(object : Pulse(1) { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 2 -> { - if (getStage(player) == 2 && player.inventory.contains(5519, 1)) { - val item = player.inventory[player.inventory.getSlot(Item(5519))] - if (item != null) { - if (item.charge == 1000) { - player.savedData.globalData.resetAbyss() - } - val wizard = Wizard.forNPC(npc.id) - if (!player.savedData.globalData.hasAbyssCharge(wizard.ordinal)) { - player.savedData.globalData.setAbyssCharge(wizard.ordinal) - item.charge = item.charge + 1 - if (item.charge == 1003) { - player.sendMessage("Your scrying orb has absorbed enough teleport information.") - player.inventory.remove(Item(5519)) - player.inventory.add(Item(5518)) - } - } - } - } - player.savedData.globalData.essenceTeleporter = npc.id - val loc: Location =Location.create(2922, 4820, 0) - player.properties.teleportLocation = loc - - if (npc.id == 553) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 1) - } - if (npc.id == 300) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 14) - } - } - 3 -> { - player.graphics(Graphics(110)) - player.unlock() - return true - } - } - return false - } - }) - } - - /** - * Method used to teleport back to the home. - * @param player the prayer. - */ - @JvmStatic - fun home(player: Player) { - val wizard = Wizard.forNPC(player.savedData.globalData.essenceTeleporter) - Projectile.create(player, player, 345).send() - player.properties.teleportLocation = wizard.location - } - - /** - * Gets the stage. - * @return the stage. - */ - fun getStage(player: Player): Int { - return player.varpManager.get(492).getValue() - } - - /** - * Method used to get a random location. - * @return the location. - */ - val location: Location - get() { - val count = RandomFunction.random(LOCATIONS.size) - return LOCATIONS[count] - } - - /** - * Represents the wizard npc who can teleport. - * @author 'Vexia - */ - enum class Wizard - /** - * Constructs a new `WizardTowerPlugin` `Object`. - * @param npc the npc. - * @param location the location. - */( - /** - * Represents the npc of this wizard. - */ - val npc: Int, - /** - * The mask. - */ - val mask: Int, - /** - * Represents the returining location. - */ - val location: Location) { - AUBURY(553, 0x2, Location(3253, 3401, 0)), - SEDRIDOR(300, 0x4, Location(3107, 9573, 0)), - DISTENTOR(462, 0x8, Location(2591, 3085, 0)), - CROMPERTY(2328, 0x12, Location.create(2682, 3323, 0)); - - /** - * Gets the npc. - * @return The npc. - */ - - /** - * Gets the mask. - * @return The mask. - */ - - /** - * Gets the location. - * @return The location. - */ - - companion object { - /** - * Method used to get a wizard by the npc. - * @param npc the npc. - * @return the wizard. - */ - fun forNPC(npc: Int): Wizard { - for (wizard in values()) { - if (npc == 844) { - return CROMPERTY - } - if (wizard.npc == npc) { - return wizard - } - } - return AUBURY - } - } - - } -} diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/WorldEvent.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/WorldEvent.kt deleted file mode 100644 index 397a14aec..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/WorldEvent.kt +++ /dev/null @@ -1,80 +0,0 @@ -package rs09.game.content.global.worldevents - -import rs09.game.system.SystemLogger -import core.plugin.Plugin -import rs09.plugin.PluginManager -import java.util.* - -/** - * The class other world events should extend off of. - * @author Ceikry - */ -open class WorldEvent(var name: String) { - 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 { - 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. - * The WorldEventInitializer runs this if checkActive() returns true. - */ - open fun initialize(){ - plugins.initialize() - } - - /** - * Used to log world event messages in a standard and organized way. - */ - fun log(message: String){ - SystemLogger.logInfo("[World Events($name)] $message") - } - - /** - * Used to start, or "fire", world events. - */ - open fun fireEvent() {} -} - - -/** - * A class that holds a set of plugins that shouldn't be initialized by default. - * Can be used to initialize all of its plugins cleanly. - */ -class PluginSet(vararg val plugins: Plugin<*>){ - val set = ArrayList(plugins.asList()) - fun initialize() { - PluginManager.definePlugins(*set.toTypedArray()) - } - fun add(plugin: Plugin<*>){ - set.add(plugin) - } -} - -/** - * Static object for storing instances of loaded events. - */ -object WorldEvents { - private var events = hashMapOf() - - fun add(event: WorldEvent){ - events.put(event.name.toLowerCase(),event) - } - - fun get(name: String): WorldEvent?{ - return events.get(name.toLowerCase()) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/WorldEventInitializer.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/WorldEventInitializer.kt deleted file mode 100644 index cb8fe69c0..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/WorldEventInitializer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package rs09.game.content.global.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/kotlin/rs09/game/content/global/worldevents/holiday/christmas/GiftRollPlugin.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/christmas/GiftRollPlugin.kt deleted file mode 100644 index 884f6c3f8..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/christmas/GiftRollPlugin.kt +++ /dev/null @@ -1,109 +0,0 @@ -package rs09.game.content.global.worldevents.holiday.christmas - -import api.Container -import api.ContentAPI -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import core.plugin.Initializable -import core.tools.RandomFunction -import org.rs09.consts.Items -import rs09.ServerStore -import rs09.ServerStore.getInt -import rs09.game.content.global.WeightBasedTable -import rs09.game.content.global.WeightedItem -import rs09.game.interaction.InteractionListener -import rs09.game.system.SystemLogger -import rs09.plugin.CorePluginTypes.XPGainPlugin -import rs09.tools.stringtools.colorize -import java.time.Month -import java.util.* - -@Initializable -class GiftRollPlugin : XPGainPlugin() { - override fun run(player: Player, skill: Int, amount: Double) { - val numDaily = getDailyGifts(player) - - val cooldown = player.getAttribute("christmas-cooldown", 0L) - if(System.currentTimeMillis() < cooldown) return - player.setAttribute("/save:christmas-cooldown", System.currentTimeMillis() + 5000L) - - if(System.currentTimeMillis() > cooldown && numDaily < 10 && RandomFunction.roll(15).also { player.debug("Rolling gift: $it") } && amount > 20) { - incrementDailyGifts(player) - - ContentAPI.addItemOrDrop(player, Items.MYSTERY_BOX_6199) - ContentAPI.sendMessage(player, colorize("%RMerry Christmas! %GYou randomly receive a mystery box while training ${Skills.SKILL_NAME[skill]}!")) - } - } - - private fun getDailyGifts(player: Player) : Int { - return ServerStore.getArchive("daily-xmas-gifts").getInt(player.name) - } - - fun incrementDailyGifts(player: Player) { - val start = getDailyGifts(player) - ServerStore.getArchive("daily-xmas-gifts")[player.name] = start + 1 - } - - override fun fireEvent(identifier: String?, vararg args: Any?): Any { - return Unit - } -} - -class XMASMboxHandler : InteractionListener() { - val MBOX = Items.MYSTERY_BOX_6199 - - override fun defineListeners() { - //disable if it's not december. - //Calendar.MONTH is 0-indexed, Month is 1-indexed. I know. It's dumb. Thanks oracle. - if(Calendar.getInstance().get(Calendar.MONTH) + 1 != Month.DECEMBER.value){ - SystemLogger.logInfo("${Calendar.getInstance().get(Calendar.MONTH)} - ${Month.DECEMBER.value}") - return - } - - on(MBOX, ITEM, "open"){player, used -> - val item = MBOX_LOOT.roll().first() - - if(ContentAPI.removeItem(player, used, Container.INVENTORY)) { - ContentAPI.sendDialogue(player, "You open the gift and find ${item.amount} ${item.name}!") - ContentAPI.addItemOrDrop(player, item.id, item.amount) - } - return@on true - } - } - - 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), - WeightedItem(Items.TOY_HORSEY_2526, 1, 1, 0.025), - WeightedItem(Items.TOY_KITE_12844, 1, 1, 0.025), - WeightedItem(Items.COAL_453, 1, 1, 0.025), - WeightedItem(Items.MOLTEN_GLASS_1776, 25, 100, 0.25), - WeightedItem(Items.FLAX_1780, 15, 70, 0.25), - WeightedItem(Items.UNCUT_SAPPHIRE_1624, 1, 5, 0.15), - WeightedItem(Items.UNCUT_EMERALD_1622, 1, 5, 0.15), - WeightedItem(Items.UNCUT_RUBY_1620, 1, 5, 0.15), - WeightedItem(Items.UNCUT_DIAMOND_1618, 1, 5, 0.15), - WeightedItem(Items.UNCUT_SAPPHIRE_1624, 100, 100, 0.0015), - WeightedItem(Items.UNCUT_EMERALD_1622, 100, 100, 0.0015), - WeightedItem(Items.UNCUT_RUBY_1620, 100, 100, 0.0015), - WeightedItem(Items.UNCUT_DIAMOND_1618, 100, 100, 0.0015), - WeightedItem(Items.PURE_ESSENCE_7937, 1, 50, 0.15), - WeightedItem(Items.PURE_ESSENCE_7937, 1000, 1000, 0.0015), - WeightedItem(Items.RANARR_SEED_5295, 1, 3, 0.065), - WeightedItem(Items.SNAPDRAGON_SEED_5300, 1, 3, 0.065), - WeightedItem(Items.GOLD_CHARM_12158, 1, 15, 0.15), - WeightedItem(Items.CRIMSON_CHARM_12160, 1, 15, 0.15), - WeightedItem(Items.BLUE_CHARM_12163, 1, 15, 0.15), - WeightedItem(Items.GREEN_CHARM_12159, 1, 15, 0.15), - WeightedItem(Items.SPIRIT_SHARDS_12183, 1, 100, 0.15), - WeightedItem(Items.PURPLE_SWEETS_10476, 1, 15, 0.25), - WeightedItem(Items.COINS_995, 100, 1000, 0.15), - WeightedItem(Items.COINS_995, 50000, 100000, 0.0015), - WeightedItem(Items.COINS_995, 1000000, 1000000, 0.0005), - WeightedItem(Items.NATURE_RUNE_561, 1, 10, 0.15), - WeightedItem(Items.ABYSSAL_WHIP_4151, 1, 1, 0.00005), - WeightedItem(Items.SANTA_HAT_1050, 1, 1, 0.00005), - - ).insertEasyClue(0.075).insertMediumClue(0.05).insertHardClue(0.025).insertRDTRoll(0.015) -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterBunnyDialogueFile.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterBunnyDialogueFile.kt deleted file mode 100644 index 199048611..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterBunnyDialogueFile.kt +++ /dev/null @@ -1,106 +0,0 @@ -package rs09.game.content.global.worldevents.holiday.easter - -import core.game.content.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 rs09.game.content.dialogue.DialogueFile -import rs09.game.content.global.worldevents.WorldEvents -import rs09.tools.END_DIALOGUE - -class EasterBunnyDialogueFile(val NEED_BASKET : Boolean) : DialogueFile() { - val EGG_ATTRIBUTE = "/save:easter:eggs" - - override fun handle(componentID: Int, buttonID: Int) { - if(NEED_BASKET){ - when(stage++){ - 0 -> npc("Hello, adventurer! Thank goodness you're here!") - 1 -> player(FacialExpression.THINKING,"Thanks goodness I'M here?") - 2 -> npc("Yes, yes, I need your help, you see!") - 3 -> npc("I have lost ALL of my eggs. What a terrible thing.") - 4 -> npc("Us easter bunnies rely on EGGS to live.") - 5 -> npc("Take this basket, please, and do me a kindness.") - 6 -> player(FacialExpression.THINKING,"What kindness might that be?") - 7 -> npc("I need you to try and gather up as many of my","lost eggs as you can.") - 8 -> npc("Please, for me? I will reward you for your time.") - 9 -> player("Fine, I suppose I will.") - 10 -> { - player!!.inventory.add(Item(Items.BASKET_OF_EGGS_4565)) - player!!.dialogueInterpreter.sendItemMessage(Items.BASKET_OF_EGGS_4565,"The Easter Bunny gives you a basket.") - if(!player!!.musicPlayer.hasUnlocked(273)) { - player!!.musicPlayer.unlock(273, true) - } - stage = END_DIALOGUE - } - } - } else { - - when (stage) { - 0 -> options("Ask about rewards", "Ask about egg location").also { stage++ } - 1 -> when (buttonID) { - 1 -> player(FacialExpression.THINKING, "What kind of rewards can I claim?").also { stage = 10 } - 2 -> player(FacialExpression.THINKING, "Where were some eggs last seen?").also { stage = 20 } - } - - 10 -> { - if (!player!!.emoteManager.isUnlocked(Emotes.BUNNY_HOP)) { - options("A credit (5 eggs)", "A Book of Knowledge (20 eggs)", "Bunny Hop Emote (30 eggs)") - } else { - options("A credit (5 eggs)", "A Book of Knowledge (20 eggs)") - } - stage++ - } - - 11 -> { - end() - when (buttonID) { - 1 -> { - if (player!!.getAttribute(EGG_ATTRIBUTE, 0) < 5) { - player!!.dialogueInterpreter.sendDialogue("You need 5 eggs to afford that.") - } else { - player!!.incrementAttribute(EGG_ATTRIBUTE, -5) - player!!.details.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/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterEvent.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterEvent.kt deleted file mode 100644 index cc634e9ed..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterEvent.kt +++ /dev/null @@ -1,117 +0,0 @@ -package rs09.game.content.global.worldevents.holiday.easter - -import core.game.node.entity.npc.NPC -import core.game.node.item.GroundItem -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.game.world.map.Direction -import core.game.world.map.Location -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import rs09.game.content.global.worldevents.WorldEvent -import rs09.game.system.config.GroundSpawnLoader -import rs09.game.world.GameWorld -import rs09.game.world.repository.Repository -import rs09.tools.secondsToTicks -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)) - - 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)) - - 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(3108, 3499, 0),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)) - - 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 - } - - 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() - } - 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) - } - - for(i in itemLocs){ - spawnedItems.add(GroundSpawnLoader.GroundSpawn(-1, Item(eggs.random()),i).init()) - } - return false - } - }) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterEventListeners.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterEventListeners.kt deleted file mode 100644 index aae620fba..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/easter/EasterEventListeners.kt +++ /dev/null @@ -1,50 +0,0 @@ -package rs09.game.content.global.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 rs09.game.interaction.InteractionListener - -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,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,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,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 - } - - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/GrimDialogue.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/GrimDialogue.kt deleted file mode 100644 index 85a718be2..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/GrimDialogue.kt +++ /dev/null @@ -1,228 +0,0 @@ -package rs09.game.content.global.worldevents.holiday.halloween - -import api.ContentAPI -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.combat.ImpactHandler -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.emote.Emotes -import core.game.node.item.Item -import rs09.ServerStore -import rs09.ServerStore.getInt -import rs09.tools.END_DIALOGUE - -/** - * Handles grim's dialogue for the 2021 halloween event. - */ -class GrimDialogue(player: Player? = null) : DialoguePlugin(player){ - var firstSpeak = true - val candy = Item(14084) - override fun newInstance(player: Player?): DialoguePlugin { - return GrimDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - firstSpeak = !player.getAttribute("hween2:grim_spoken",false) - - if(firstSpeak){ - npc("YOU! Yes.... you! Come here!") - player.musicPlayer.unlock(571) - stage = 0 - } else { - npc("Hello, again, adventurer...") - stage = 100 - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> player(FacialExpression.AFRAID,"W-what... what do you want with","me?").also { stage++ } - 1 -> npc("I want you.... I NEED you....").also { stage++ } - 2 -> npc("TO BRING ME CANDY! Yes, candy...").also { stage++ } - 3 -> player(FacialExpression.THINKING,"Candy...? You want me to bring","you... candy?").also { stage++ } - 4 -> npc("Yes, candy! Did I not speak clearly","enough?").also { stage++ } - 5 -> player(FacialExpression.ASKING,"Well how do I even get candy?").also { stage++ } - 6 -> npcl(FacialExpression.NEUTRAL,"It's Hallowe'en, you fool. Everyone's giving out candy.").also { stage++ } - 7 -> npc("You'll need to go and ask them for it each day.").also { stage++ } - 8 -> npc("You'll also need to be careful...","Something vile is on the prowl.").also { stage++ } - 9 -> player(FacialExpression.THINKING,"And what will I get in exchange?").also { stage++ } - 10 -> npc("Well I won't KILL YOU for starters.").also { stage++ } - 11 -> player(FacialExpression.ANGRY_WITH_SMILE, "Is that it?!").also { stage++ } - 12 -> npc("I've prepared a few... rewards, as well.").also { stage++ } - 13 -> player(FacialExpression.AMAZED, "YOU MEAN CREDITS?!").also { stage++ } - 14 -> npc("No, we're not doing that again.").also { stage++ } - 15 -> player(FacialExpression.SAD,"Oh.").also { stage++ } - 16 -> player(FacialExpression.NEUTRAL, "Ok, so what kind of rewards?").also { stage++ } - 17 -> npcl(FacialExpression.NEUTRAL, "I've fashioned a special-made staff to give out for this year's event.").also { stage++ } - 18 -> npcl(FacialExpression.NEUTRAL, "It is a decorative symbol of your engagement in this activity. Bring me candies, and it can be yours.").also { player.setAttribute("/save:hween2:grim_spoken",true); stage++ } - 19 -> npc("NOW GET TO WORK!").also { player.setAttribute("/save:hween2:grim_spoken",true); stage = 1000 } - - - 100 -> npc("I do hope you have... candy for me?").also { stage++ } - 101 -> if(player.inventory.containsItem(candy)){ - player("Yes, I do! Here you go.").also { stage = 150 } - } else { - player(FacialExpression.SAD, "No, I don't.").also { stage++ } - } - 102 -> npc("A shame, indeed. Your candy totals right now are ${getCandyTotals(player)}.").also { stage++ } - 103 -> player(FacialExpression.FRIENDLY, "I'd like to see the reward shop, please.").also { stage++ } - 104, 105 -> handleRewardShop(player, buttonId) - - - 150 -> { - val candies = player.inventory.getAmount(candy) - player.inventory.remove(Item(candy.id,candies)) - addToCandyTotal(player, candies) - npcl(FacialExpression.NEUTRAL, "Excellent, you now have ${getCandyTotals(player)} candies.") - stage = 103 - } - - - 1000 -> end() - } - return true - } - - fun handleRewardShop(player: Player, buttonId: Int){ - val title = "You have ${getCandyTotals(player)} candies." - val hasUnlocked = player.getAttribute("sotr:purchased",false) - val hasRecolor1 = player.getAttribute("sotr:recolor1", false) - val hasRecolor2 = player.getAttribute("sotr:recolor2", false) - val hasEmote = player.emoteManager.isUnlocked(Emotes.TRICK) - - if(!hasUnlocked && !hasEmote){ - when(stage){ - 104 -> player.dialogueInterpreter.sendOptions(title, "Staff of the Raven (40c)", "Trick Emote (10c)").also { stage++ } - 105 -> when(buttonId) { - 1 -> if (canPurchase(player, 40)) buyStaff(player) else npc( - FacialExpression.NEUTRAL, - "You can't afford that." - ) - 2 -> if (canPurchase(player, 10)) buyEmote(player) else npc( - FacialExpression.NEUTRAL, - "You can't afford that" - ) - } - } - } - else if(hasUnlocked && !hasEmote && !hasRecolor1 && !hasRecolor2){ - when(stage){ - 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Staff Orange Recolor (10c)", "Trick Emote (10c)").also { stage++ } - 105 -> when(buttonId){ - 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - 2 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - 3 -> if(canPurchase(player, 10)) buyEmote(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - } - } - } - else if(hasUnlocked && !hasEmote && hasRecolor1 && !hasRecolor2){ - when(stage){ - 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Orange Recolor (10c)", "Trick Emote (10c)").also { stage++ } - 105 -> when(buttonId){ - 1 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - 2 -> if(canPurchase(player, 10)) buyEmote(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - } - } - } - else if(hasUnlocked && !hasEmote && !hasRecolor1 && hasRecolor2){ - when(stage){ - 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Trick Emote (10c)").also { stage++ } - 105 -> when(buttonId){ - 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - 2 -> if(canPurchase(player, 10)) buyEmote(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - } - } - } - else if(!hasUnlocked && hasEmote){ - when(stage){ - 104 -> player.dialogueInterpreter.sendOptions(title, "Staff of the Raven (40c)", "").also { stage++ } - 105 -> when(buttonId){ - 1 -> if(canPurchase(player, 40)) buyStaff(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - 2 -> npc(FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 } - } - } - } - else if(hasUnlocked && hasEmote && !hasRecolor1 && !hasRecolor2){ - when(stage){ - 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "Staff Orange Recolor (10c)").also { stage++ } - 105 -> when(buttonId){ - 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - 2 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - } - } - } - else if(hasUnlocked && hasEmote && !hasRecolor1 && hasRecolor2){ - when(stage){ - 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Purple Recolor (10c)", "").also { stage++ } - 105 -> when(buttonId){ - 1 -> if(canPurchase(player, 10)) buyRecolor1(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - 2 -> npc(FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 } - } - } - } - else if(hasUnlocked && hasEmote && hasRecolor1 && !hasRecolor2){ - when(stage){ - 104 -> player.dialogueInterpreter.sendOptions(title, "Staff Orange Recolor (10c)", "").also { stage++ } - 105 -> when(buttonId){ - 1 -> if(canPurchase(player, 10)) buyRecolor2(player) else npc(FacialExpression.NEUTRAL, "You can't afford that.") - 2 -> npc(FacialExpression.NEUTRAL, "Huhwuh").also { stage = 104 } - } - } - } - else { - npcl(FacialExpression.NEUTRAL, "You've already bought everything.") - stage = END_DIALOGUE - } - } - - fun buyStaff(player: Player){ - player.setAttribute("/save:sotr:purchased", true) - removeCandies(player, 40) - ContentAPI.addItem(player, 14654, 1) - stage = 104 - handleRewardShop(player, -1) - } - - fun buyRecolor1(player: Player){ - player.setAttribute("/save:sotr:recolor1", true) - removeCandies(player, 10) - stage = 104 - handleRewardShop(player, -1) - } - - fun buyRecolor2(player: Player){ - player.setAttribute("/save:sotr:recolor2", true) - removeCandies(player, 10) - stage = 104 - handleRewardShop(player, -1) - } - - fun buyEmote(player: Player){ - player.emoteManager.unlock(Emotes.TRICK) - removeCandies(player, 10) - stage = 104 - handleRewardShop(player, -1) - } - - fun canPurchase(player: Player, cost: Int) : Boolean { - return getCandyTotals(player) >= cost - } - - fun removeCandies(player: Player, amount: Int){ - addToCandyTotal(player, -amount) - } - - fun getCandyTotals(player: Player): Int { - return ServerStore.getArchive("hween2021-candies").getInt(player.username.toLowerCase()) - } - - fun addToCandyTotal(player: Player, amount: Int){ - ServerStore.getArchive("hween2021-candies").put(player.username.toLowerCase(), getCandyTotals(player) + amount) - } - - override fun getIds(): IntArray { - return intArrayOf(6390) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/SimpleHalloweenEvent.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/SimpleHalloweenEvent.kt deleted file mode 100644 index 5cf7e1406..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/SimpleHalloweenEvent.kt +++ /dev/null @@ -1,24 +0,0 @@ -package rs09.game.content.global.worldevents.holiday.halloween - -import core.game.node.entity.npc.NPC -import core.game.world.map.Location -import rs09.game.content.global.worldevents.PluginSet -import rs09.game.content.global.worldevents.WorldEvent -import rs09.game.world.GameWorld -import java.util.* - -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/kotlin/rs09/game/content/global/worldevents/holiday/halloween/TrickOrTreatHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/TrickOrTreatHandler.kt deleted file mode 100644 index db1c404fe..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/holiday/halloween/TrickOrTreatHandler.kt +++ /dev/null @@ -1,95 +0,0 @@ -package rs09.game.content.global.worldevents.holiday.halloween - -import api.ContentAPI -import core.game.component.Component -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics -import core.tools.RandomFunction -import org.rs09.consts.Components -import rs09.ServerStore -import rs09.ServerStore.getInt -import rs09.ServerStore.getString -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld -import rs09.tools.END_DIALOGUE - -class TrickOrTreatHandler : InteractionListener() { - override fun defineListeners() { - on(NPC, "trick-or-treat"){player, node -> - val hasDone5 = getDailyTrickOrTreats(player) == 5 - val hasDoneMe = getTrickOrTreatedNPCs(player).contains(node.name.toLowerCase()) - - if(hasDone5){ - ContentAPI.sendNPCDialogue(player, node.id, "My informants tell me you've already collected candy from 5 people today.", FacialExpression.FRIENDLY) - return@on true - } - - if(hasDoneMe){ - ContentAPI.sendNPCDialogue(player, node.id, "You've already asked me today! Don't get greedy, now.", FacialExpression.ANNOYED) - return@on true - } - - player.dialogueInterpreter.open(object : DialogueFile(){ - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - 0 -> playerl(FacialExpression.FRIENDLY, "Trick or treat!").also { if(RandomFunction.roll(20)) stage = 10 else stage++ } - 1 -> npcl(FacialExpression.FRIENDLY, "Very well, then, here you are my friend.").also { stage++ } - 2 -> { - player.dialogueInterpreter.sendItemMessage(14084, "They hand you a nicely-wrapped candy.") - ContentAPI.addItemOrDrop(player, 14084, 1) - registerNpc(player, npc!!) - incrementDailyToT(player) - stage = END_DIALOGUE - } - - 10 -> npcl(FacialExpression.EVIL_LAUGH, "I CHOOSE TRICK!").also { player.lock(); GameWorld.submit(object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - //gfx 1898 - when(counter++){ - 0 -> npc!!.visualize(Animation(1979), Graphics(1898)).also { npc!!.faceLocation(player.location) } - 2 -> player.dialogueInterpreter.close() - 5 -> player.interfaceManager.open(Component(Components.FADE_TO_BLACK_120)) - 8 -> player.properties.teleportLocation = Location.create(3106, 3382, 0) - 12 -> { - player.interfaceManager.close() - player.interfaceManager.open(Component(Components.FADE_FROM_BLACK_170)) - registerNpc(player, npc!!) - } - 15 -> player.interfaceManager.close().also { player.unlock() } - 16 -> return true - } - return false - } - }) } - } - } - }, node.asNpc()) - return@on true - } - } - - fun incrementDailyToT(player: Player){ - ServerStore.getArchive("daily-tot-total")[player.username.toLowerCase()] = getDailyTrickOrTreats(player) + 1 - } - - fun getDailyTrickOrTreats(player: Player) : Int { - return ServerStore.getArchive("daily-tot-total").getInt(player.username.toLowerCase()) - } - - fun getTrickOrTreatedNPCs(player: Player): String { - return ServerStore.getArchive("daily-tot-npcs").getString(player.username.toLowerCase()) - } - - fun registerNpc(player: Player, npc: NPC){ - var soFar = getTrickOrTreatedNPCs(player) - soFar += ":" + npc.name.toLowerCase() + ":" - ServerStore.getArchive("daily-tot-npcs")[player.username.toLowerCase()] = soFar - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinHNSEvent.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinHNSEvent.kt deleted file mode 100644 index 38f68a8da..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinHNSEvent.kt +++ /dev/null @@ -1,48 +0,0 @@ -package rs09.game.content.global.worldevents.penguinhns - -import core.game.system.task.Pulse -import org.json.simple.JSONObject -import rs09.ServerStore -import rs09.game.content.global.worldevents.PluginSet -import rs09.game.content.global.worldevents.WorldEvent -import rs09.game.content.global.worldevents.WorldEvents -import rs09.game.world.GameWorld - -class PenguinHNSEvent : WorldEvent("penguin-hns"){ - 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...") - manager.rebuildVars() - lastTrigger = GameWorld.ticks - log("Penguins loaded.") - } - - companion object { - @JvmStatic - fun getStoreFile() : JSONObject { - return ServerStore.getArchive("weekly-penguinhns") - } - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinSpawner.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinSpawner.kt deleted file mode 100644 index ebcfec2a1..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinSpawner.kt +++ /dev/null @@ -1,67 +0,0 @@ -package rs09.game.content.global.worldevents.penguinhns - -import core.game.node.entity.npc.NPC -import core.game.world.map.Location -import org.rs09.consts.NPCs -import rs09.game.content.global.worldevents.WorldEvents - -class PenguinSpawner { - - fun spawnPenguins(amount: Int): ArrayList { - var counter = 0 - val availableOrdinals = (0 until Penguin.values().size).toMutableList() - val spawnedOrdinals = ArrayList() - while(counter < amount){ - val peng = Penguin.values()[availableOrdinals.random()] - availableOrdinals.remove(peng.ordinal) - spawnedOrdinals.add(peng.ordinal) - NPC(peng.id,peng.location) - .also {PenguinManager.npcs.add(it);it.isNeverWalks = true; it.isWalks = false}.init() - counter++ - } - return spawnedOrdinals - } - - fun spawnPenguins(ordinals: List){ - ordinals.forEach { - val peng = Penguin.values()[it] - NPC(peng.id,peng.location) - .also { PenguinManager.npcs.add(it); it.isNeverWalks = true; it.isWalks = false }.init() - } - } -} - -enum class Penguin(val id: Int, val hint: String, val location: Location){ - CACTUS_1(NPCs.CACTUS_8107,"located in the northern desert.", Location(3310, 3157, 0)), - CACTUS_2(NPCs.CACTUS_8107,"located in the southern desert.",Location.create(3259, 3052, 0)), - BUSH_1(NPCs.BUSH_8105,"located between Fremennik and barbarians.",Location.create(2532, 3588, 0)), - BUSH_2(NPCs.BUSH_8105,"located where banana smugglers dwell.",Location.create(2740, 3233, 0)), - BUSH_3(NPCs.BUSH_8105,"located south of Ardougne.",Location.create(2456, 3092, 0)), - BUSH_4(NPCs.BUSH_8105,"located deep in the jungle.",Location.create(2832, 3053, 0)), - BUSH_5(NPCs.BUSH_8105,"located where eagles fly.",Location.create(2326, 3516, 0)), - BUSH_6(NPCs.BUSH_8105,"located south of Ardougne.",Location.create(2513, 3154, 0)), - BUSH_7(NPCs.BUSH_8105,"located near a big tree surrounded by short people.",Location.create(2387, 3451, 0)), - 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)), - 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_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)), - 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)); - - companion object { - private val locationMap = values().map { it.location.toString() to it }.toMap() - - fun forLocation(location: Location): Penguin?{ - return locationMap[location.toString()] - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinSpyingHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinSpyingHandler.kt deleted file mode 100644 index 6d96d95ce..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/penguinhns/PenguinSpyingHandler.kt +++ /dev/null @@ -1,51 +0,0 @@ -package rs09.game.content.global.worldevents.penguinhns - -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.interaction.Option -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import core.game.world.update.flag.context.Animation -import core.plugin.Plugin -import core.game.content.quest.PluginInteraction -import core.game.content.quest.PluginInteractionManager -import rs09.game.interaction.InteractionListener - -class PenguinSpyingHandler : InteractionListener(){ - - override fun defineListeners() { - on(PENGUINS, NPC, "spy-on"){player, node -> - val npc = node.asNpc() - if(PenguinManager.hasTagged(player, npc.location)){ - player.sendMessage("You've already tagged this penguin.") - } else { - GameWorld.submit(SpyPulse(player, npc)) - } - return@on true - } - } - - val PENGUINS = intArrayOf(8104,8105,8107,8108,8109,8110) - - class SpyPulse(val player: Player, val npc: NPC) : Pulse() { - var stage = 0 - val curPoints = player.getAttribute("phns:points",0) - - val ANIMATION = Animation(10355) - - override fun pulse(): Boolean { - when(stage++){ - 0 -> player.lock().also { player.animator.animate(ANIMATION) } - 1 -> player.sendMessage("You manage to spy on the penguin.").also { - player.setAttribute("/save:phns:points",curPoints + 1) - PenguinManager.registerTag(player, npc.location) - player.unlock() - } - 2 -> return true - } - return false - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStar.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStar.kt deleted file mode 100644 index 0d5a35009..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStar.kt +++ /dev/null @@ -1,173 +0,0 @@ -package rs09.game.content.global.worldevents.shootingstar - -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.world.map.Location -import rs09.ServerStore.getBoolean -import rs09.ServerStore.getInt -import rs09.ServerStore.getString -import rs09.game.world.repository.Repository - -/** - * Represents a shooting star object (Only ever initialized once) (ideally) - * @author Ceikry - */ -class ShootingStar(var level: ShootingStarType = ShootingStarType.values().random()){ - val crash_locations = mapOf( - "Canifis Bank" to Location.create(3504, 3487, 0), - "Crafting Guild" to Location.create(2940, 3280, 0), - "Falador East Bank" to Location.create(3030, 3349, 0), - "Rimmington mining site" to Location.create(2975, 3237, 0), - "Rimmington mine" to Location.create(2975, 3240, 0), - "Karamja mining site" to Location.create(2737, 3223, 0), - "Brimhaven mining site" to Location.create(2743, 3143, 0), - "South Crandor mining site" to Location.create(2822, 3239, 0), - "Karamja mining site" to Location.create(2854, 3032, 0), - "Shilo Village mining site" to Location.create(2826, 2997, 0), - "Relleka mining site" to Location.create(2682, 3700, 0), - "Ardougne mining site" to Location.create(2600, 3232, 0), - "Yanille Bank" to Location.create(2603, 3087, 0), - "Al Kharid bank" to Location.create(3276, 3176, 0), - "Al Kharid mining site" to Location.create(3296, 3297, 0), - "Duel Arena bank chest" to Location.create(3342, 3267, 0), - "Nardah mining site" to Location.create(3320, 2872, 0), - "Nardah bank" to Location.create(3434, 2888, 0), - "South-east Varrock mine" to Location.create(3292, 3353, 0), - "South-west Varrock mine" to Location.create(3176, 3362, 0), - "Varrock east bank" to Location.create(3259, 3407, 0), - "Lumbridge Swamp mine" to Location.create(3227, 3150, 0), - "Gnome stronghold Bank" to Location.create(2460, 3432, 0), - "North Edgeville mining site" to Location.create(3101, 3569, 0), - "Southern wilderness mine" to Location.create(3025, 3591, 0), - "Pirates' Hideout mine" to Location.create(3059, 3940, 0), - "Lava Maze mining site" to Location.create(3062, 3885, 0), - "Mage Arena bank" to Location.create(3093, 3962, 0) - ) - val starSprite = NPC(8091) - var location = "Canifis Bank" - var maxDust = level.totalStardust - var dustLeft = level.totalStardust - var starObject = Scenery(level.objectId, crash_locations.get(location)) - var isDiscovered = false - var ticks = 0 - var isSpawned = false - var spriteSpawned = false - var firstStar = true - - /** - * Degrades a ShootingStar (or removes the starObject and spawns a Star Sprite if it's the last star) - */ - fun degrade() { - if(level.ordinal == 0){ - SceneryBuilder.remove(starObject) - isSpawned = false - starSprite.location = starObject.location - starSprite.init() - spriteSpawned = true - ShootingStarEvent.getStoreFile().clear() - return - } - level = getNextType() - maxDust = level.totalStardust - dustLeft = level.totalStardust - - ShootingStarEvent.getStoreFile()["level"] = level.ordinal - ShootingStarEvent.getStoreFile()["isDiscovered"] = isDiscovered - - val newStar = Scenery(level.objectId, starObject.location) - SceneryBuilder.replace(starObject, newStar) - starObject = newStar - } - - private fun getNextType(): ShootingStarType{ - return ShootingStarType.values()[level.ordinal - 1] - } - - /** - * Fires the shooting star (spawns a new one). Only used when spawning new shooting stars, not for downgrading existing ones. - */ - fun fire() { - SceneryBuilder.remove(starObject) - rebuildVars() - clearSprite() - SceneryBuilder.add(starObject) - isSpawned = true - Repository.sendNews("A shooting star level ${level.ordinal + 1} just crashed near ${location.toLowerCase()}!") - } - - /** - * Rebuilds some of the variables with new information. - */ - fun rebuildVars(){ - if(firstStar && ShootingStarEvent.getStoreFile().isNotEmpty()){ - level = ShootingStarType.values()[ShootingStarEvent.getStoreFile().getInt("level")] - location = ShootingStarEvent.getStoreFile().getString("location") - isDiscovered = ShootingStarEvent.getStoreFile().getBoolean("isDiscovered") - } else { - level = ShootingStarType.values().random() - location = crash_locations.entries.random().key - isDiscovered = false - } - - maxDust = level.totalStardust - dustLeft = level.totalStardust - starObject = Scenery(level.objectId, crash_locations.get(location)) - - ShootingStarEvent.getStoreFile()["level"] = level.ordinal - ShootingStarEvent.getStoreFile()["location"] = location - ShootingStarEvent.getStoreFile()["isDiscovered"] = false - - ticks = 0 - firstStar = false - } - - fun clearSprite() { - starSprite.clear() - spriteSpawned = false - } - - /** - * Decrement the amount of dust left in the current star and degrade it if the amount left is 0 or less. - */ - fun decDust() { - if(--dustLeft <= 0) degrade() - } - - /** - * Proxy method for starting to mine a shooting star - */ - fun mine(player: Player) { - player.pulseManager.run(ShootingStarMiningPulse(player, starObject, this)) - } - - /** - * Prospecting shooting stars. - */ - fun prospect(player: Player) { - player.dialogueInterpreter.sendDialogue("This is a size " + (level.ordinal + 1) + " star. A Mining level of at least " + miningLevel + " is", "required to mine this layer. There is $dustLeft stardust remaining", "until the next layer.") - } - - /** - * Gets the mining level required based on the star's current level ordinal. - */ - val miningLevel: Int - get() = (level.ordinal + 1) * 10 -} - -/** - * Various levels of shooting stars - * @author Ceikry - */ -enum class ShootingStarType(val objectId: Int, val exp: Int, val totalStardust: Int,val rate: Double) { - LEVEL_1(38668, 14, 1200, 0.05), - LEVEL_2(38667, 25, 700, 0.1), - LEVEL_3(38666, 29, 439, 0.3), - LEVEL_4(38665, 32, 250, 0.4), - LEVEL_5(38664, 47, 175, 0.5), - LEVEL_6(38663, 71, 80, 0.70), - 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/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarCommands.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarCommands.kt deleted file mode 100644 index 3f067b91e..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarCommands.kt +++ /dev/null @@ -1,40 +0,0 @@ -package rs09.game.content.global.worldevents.shootingstar - -import core.game.node.entity.player.Player -import core.game.system.command.CommandSet -import core.plugin.Plugin -import rs09.game.content.global.worldevents.WorldEvents -import rs09.game.system.command.CommandPlugin -import java.util.concurrent.TimeUnit - -/** - * A few assorted commands for shooting stars. - */ -class ShootingStarCommands : CommandPlugin(){ - override fun newInstance(arg: Any?): Plugin{ - link(CommandSet.DEVELOPER) - return this - } - - override fun parse(player: Player?, name: String?, args: Array?): Boolean { - val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star - when (name) { - "tostar" -> { - player!!.teleport(star.starObject.location.transform(1, 1, 0)) - } - "submit" -> { - star.fire() - } - "resettime" -> player!!.savedData.globalData.starSpriteDelay = 0L - "stardust" -> { - val dust = 8 - println("Cosmic Runes: " + 0.76 * dust) - println("Astral runes: " + 0.26 * dust) - println("Gold ores: " + 0.1 * dust) - println("GP: " + 250.1 * dust) - } - "setsprite" -> player!!.savedData.globalData.starSpriteDelay = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1) - } - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarEvent.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarEvent.kt deleted file mode 100644 index bbfe2df56..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarEvent.kt +++ /dev/null @@ -1,92 +0,0 @@ -package rs09.game.content.global.worldevents.shootingstar - -import core.game.content.global.worldevents.shootingstar.ScoreboardHandler -import core.game.content.global.worldevents.shootingstar.ShootingStarScoreboard -import core.game.content.global.worldevents.shootingstar.StarChartPlugin -import core.game.system.task.Pulse -import org.json.simple.JSONObject -import rs09.ServerStore -import rs09.game.content.global.worldevents.PluginSet -import rs09.game.content.global.worldevents.WorldEvent -import rs09.game.content.global.worldevents.WorldEvents -import rs09.game.world.GameWorld - - -/** - * The world event class for shooting stars. Keeps track of event-related data like the ShootingStar instance. - * Also handles spawning new shooting stars based on time. - * @author Ceikry - */ -class ShootingStarEvent : WorldEvent("shooting-stars") { - val star = ShootingStar() - val tickDelay = if(GameWorld.settings?.isDevMode == true) 200 else 25000 - - - override fun initialize() { - plugins = PluginSet( - ScoreboardHandler(), - ShootingStarScoreboard(), - StarChartPlugin(), - ShootingStarCommands(), - StarSpriteDialogue(), - ShootingStarLogin() - ) - super.initialize() - GameWorld.Pulser.submit(StarPulse()) - log("Shooting Star event has been initialized.") - } - - override fun checkTrigger(): Boolean { - /** - * Spawn a new star only if: star's ticks are greater than the tickDelay and star sprite is not spawned OR, - * neither the star or the sprite are spawned. - */ - star.ticks += 10 - if ((star.ticks >= tickDelay && !star.spriteSpawned) || (!star.isSpawned && !star.spriteSpawned)) { - return true - } - - /** - * Clear the sprite when we have passed the tickDelay + (1/3) of the tickDelay. - * This gives players a little extra time before the star respawns to run to the bank and grab - * stardust or whatever. Once the sprite is cleared a new star will be allowed to spawn. - */ - val maxDelay = tickDelay + (tickDelay / 3) - if(star.ticks > maxDelay && star.spriteSpawned){ - star.clearSprite() - } - - return false - } - - override fun checkActive(): Boolean { - return true //this event is always active. - } - - override fun fireEvent() { - log("Fired new shooting star event.") - star.fire() - } - - /** - * Handles checking star status and spawning new ones if necessary. - */ - class StarPulse : Pulse(10){ - override fun pulse(): Boolean { - val event = WorldEvents.get("shooting-stars") - event ?: return true - - if(event.checkTrigger()){ - event.fireEvent() - } - - return false //always returns false because it needs to run forever. - } - } - - companion object { - fun getStoreFile() : JSONObject { - return ServerStore.getArchive("shooting-star") - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarLogin.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarLogin.kt deleted file mode 100644 index 9a2feb669..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarLogin.kt +++ /dev/null @@ -1,29 +0,0 @@ -package rs09.game.content.global.worldevents.shootingstar - -import core.game.node.entity.player.Player -import core.plugin.Plugin -import core.plugin.PluginManifest -import core.plugin.PluginType -import rs09.game.content.global.worldevents.WorldEvents - -/** - * A plugin that handles the message a player receives on login pertaining to shooting stars. - */ -@PluginManifest(type = PluginType.LOGIN) -class ShootingStarLogin : Plugin { - @Throws(Throwable::class) - override fun newInstance(arg: Player?): Plugin? { - if (arg is Player) { - val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star - if (star.isSpawned) { - arg.sendMessage("News: A shooting star (Level " + (star.level.ordinal + 1).toString() + ") has just crashed near the " + star.location + "!") - } - return this - } - 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/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarMiningPulse.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarMiningPulse.kt deleted file mode 100644 index 88d630eac..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarMiningPulse.kt +++ /dev/null @@ -1,121 +0,0 @@ -package rs09.game.content.global.worldevents.shootingstar - -import api.ContentAPI -import core.game.content.global.worldevents.shootingstar.ScoreboardManager -import core.game.node.scenery.Scenery -import core.game.node.entity.player.Player -import core.game.node.entity.skill.SkillPulse -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.gather.SkillingTool -import core.game.node.item.Item -import core.tools.RandomFunction -import org.rs09.consts.Items -import rs09.game.world.GameWorld -import rs09.game.world.repository.Repository -import rs09.tools.stringtools.colorize - -/** - * The pulse used to handle mining shooting stars. - */ -class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: ShootingStar) : SkillPulse(player, node) { - /** - * The amount of ticks it takes to get star dust. - */ - private var ticks = 0 - override fun start() { - if(!star.isSpawned) return - super.start() - } - - override fun checkRequirements(): Boolean { - tool = SkillingTool.getPickaxe(player) - if (!star.starObject.isActive || !star.isSpawned) { - return false - } - //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.skills.addExperience(Skills.MINING, 75 * player.skills.getStaticLevel(Skills.MINING).toDouble()) - Repository.sendNews(player.username + " is the discoverer of the crashed star near " + star.location + "!") - ScoreboardManager.submit(player) - star.isDiscovered = true - 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 - } - if (tool == null) { - player.packetDispatch.sendMessage("You do not have a pickaxe to use.") - return false - } - if (player.inventory.freeSlots() < 1 && !player.inventory.contains(ShootingStarOptionHandler.STAR_DUST, 1)) { - player.dialogueInterpreter.sendDialogue("Your inventory is too full to hold any more stardust.") - return false - } - return true - } - - override fun animate() { - player.animate(tool.animation) - } - - override fun reward(): Boolean { - if (++ticks % 4 != 0) { - return false - } - if (!checkReward()) { - return false - } - if (GameWorld.settings?.isDevMode == true) { - star.dustLeft = 1 - } - star.decDust() - player.skills.addExperience(Skills.MINING,star.level.exp.toDouble()) - if (ShootingStarOptionHandler.getStarDust(player) < 200) { - player.inventory.add(Item(ShootingStarOptionHandler.STAR_DUST, 1)) - } - if(!ContentAPI.inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !ContentAPI.inBank(player, Items.ANCIENT_BLUEPRINT_14651)){ - rollBlueprint(player) - } - return false - } - - fun rollBlueprint(player: Player){ - val chance = when(star.level){ - ShootingStarType.LEVEL_9 -> 250 - ShootingStarType.LEVEL_8 -> 500 - ShootingStarType.LEVEL_7 -> 750 - ShootingStarType.LEVEL_6 -> 1000 - ShootingStarType.LEVEL_5 -> 2000 - ShootingStarType.LEVEL_4 -> 3000 - ShootingStarType.LEVEL_3 -> 4000 - ShootingStarType.LEVEL_2 -> 5000 - ShootingStarType.LEVEL_1 -> 10000 - } - - if(RandomFunction.roll(chance)){ - ContentAPI.addItemOrDrop(player, Items.ANCIENT_BLUEPRINT_14651, 1) - ContentAPI.sendMessage(player, colorize("%RWhile mining the star you find an ancient-looking blueprint.")) - ContentAPI.sendNews("${player.username} found an Ancient Blueprint while mining a shooting star!") - } - } - - override fun message(type: Int) { - when (type) { - 0 -> player.packetDispatch.sendMessage("You swing your pickaxe at the rock...") - } - } - - /** - * Checks if the player gets rewarded. - * @return `True` if so. - */ - private fun checkReward(): Boolean { - val skill = Skills.MINING - val level = 1 + player.skills.getLevel(skill) + player.familiarManager.getBoost(skill) - val hostRatio: Double = Math.random() * (100.0 * star.level.rate) - val clientRatio: Double = Math.random() * ((level - star.miningLevel) * (1.0 + tool.ratio)) - return hostRatio < clientRatio - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarOptionHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarOptionHandler.kt deleted file mode 100644 index 8b8fc6cee..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/ShootingStarOptionHandler.kt +++ /dev/null @@ -1,39 +0,0 @@ -package rs09.game.content.global.worldevents.shootingstar - -import core.game.node.entity.player.Player -import rs09.game.content.global.worldevents.WorldEvents -import rs09.game.interaction.InteractionListener - -/** - * Option handlers for the various shooting star objects. - */ -class ShootingStarOptionHandler : InteractionListener() { - - val SHOOTING_STARS = ShootingStarType.values().map(ShootingStarType::objectId).toIntArray() - - override fun defineListeners() { - on(SHOOTING_STARS,SCENERY,"mine"){ player, _ -> - val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star - star.mine(player) - return@on true - } - - on(SHOOTING_STARS,SCENERY,"prospect"){ player, _ -> - val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star - star.prospect(player) - return@on true - } - } - - companion object { - const val STAR_DUST = 13727 - /** - * Gets the star dust amount for a player. - * @param player The player. - * @return The stardust amount. - */ - fun getStarDust(player: Player): Int { - return player.inventory.getAmount(STAR_DUST) + player.bank.getAmount(STAR_DUST) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/StarSpriteDialogue.kt b/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/StarSpriteDialogue.kt deleted file mode 100644 index becbf5aa8..000000000 --- a/Server/src/main/kotlin/rs09/game/content/global/worldevents/shootingstar/StarSpriteDialogue.kt +++ /dev/null @@ -1,283 +0,0 @@ -package rs09.game.content.global.worldevents.shootingstar - -import api.Container -import api.ContentAPI -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.tools.RandomFunction -import org.json.simple.JSONObject -import org.rs09.consts.Items -import rs09.ServerStore -import rs09.ServerStore.getBoolean -import rs09.game.node.entity.state.newsys.states.ShootingStarState -import rs09.tools.END_DIALOGUE -import rs09.tools.secondsToTicks -import rs09.tools.stringtools.colorize -import java.util.concurrent.TimeUnit - -/** - * Dialogue for the star sprite. - */ -class StarSpriteDialogue(player: Player? = null) : DialoguePlugin(player) { - - /** - * The cosmic rune item id. - */ - val COSMIC_RUNE = 564 - - /** - * The astral rune item id. - */ - val ASTRAL_RUNE = 9075 - - /** - * The gold ore item id. - */ - val GOLD_ORE = 445 - - /** - * The coins id. - */ - val COINS = 995 - - /** - * The amplifier amount. - */ - val AMPLIFIER = 1.0 - - - /** - * Constructs the star sprite dialogue. - */ - fun StarSpriteDialogue() { - /* - * empty. - */ - } - - override fun newInstance(player: Player?): DialoguePlugin? { - return StarSpriteDialogue(player) - } - - override fun open(vararg args: Any): Boolean { - npc = args[0] as NPC - if (ContentAPI.inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !ContentAPI.getAttribute(player, "star-ring:bp-shown", false)) { - npcl(FacialExpression.NEUTRAL, "I see you got ahold of a blueprint of those silly old rings we used to make.") - stage = 1000 - } else if (ContentAPI.inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && ContentAPI.getAttribute(player, "star-ring:bp-shown", false)) { - playerl(FacialExpression.HALF_ASKING, "So about those rings...") - stage = 2000 - } else if (getStoreFile().getBoolean(player.username.toLowerCase()) || !player.getInventory().contains(ShootingStarOptionHandler.STAR_DUST, 1)) { - npc("Hello, strange creature.") - stage = 0 - } else { - npc("Thank you for helping me out of here.") - stage = 50 - } - return true - } - - 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") - stage++ - } - 1 -> { - npc("your race.") - stage++ - } - 2 -> { - options("What's a star sprite?", "What are you going to do without your star?", "I thought stars were huge balls of burning gas.", "Well, I'm glad you're okay.") - stage++ - } - 3 -> when (buttonId) { - 1 -> { - player("What's a star sprite?") - stage = 10 - } - 2 -> { - player("What are you going to do without your star?") - stage = 20 - } - 3 -> { - player("I thought stars were huge balls of burning gas.") - stage = 30 - } - 4 -> { - player("Well, I'm glad you're okay.") - stage = 40 - } - } - 10 -> { - npc("We're what makes the stars in the sky shine. I made", "this star shine when it was in the sky.") - stage++ - } - 11 -> { - options("What are you going to do without your star?", "I thought stars were huge balls of burning gas.", "Well, I'm glad you're okay.") - stage++ - } - 12 -> when (buttonId) { - 1 -> { - player("What are you going to do without your star?") - stage = 20 - } - 2 -> { - player("I thought stars were huge balls of burning gas.") - stage = 30 - } - 3 -> { - player("Well, I'm glad you're okay.") - stage = 40 - } - } - 20 -> { - npc("Don't worry about me. I'm sure I'll find some good", "rocks around here and get back up into the sky in no", "time.") - stage++ - } - 21 -> { - options("What's a star sprite?", "I thought stars were huge balls of burning gas.", "Well, I'm glad you're okay.") - stage++ - } - 22 -> when (buttonId) { - 1 -> { - player("What's a star sprite?") - stage = 10 - } - 2 -> { - player("I thought stars were huge balls of burning gas.") - stage = 30 - } - 3 -> { - player("Well, I'm glad you're okay.") - stage = 40 - } - } - 30 -> { - npc("Most of them are, but a lot of shooting stars on this", "plane of the multiverse are rocks with star sprites in", "them.") - stage++ - } - 31 -> { - options("What's a star sprite?", "What are you going to do without your star?", "Well, I'm glad you're okay.") - stage++ - } - 32 -> when (buttonId) { - 1 -> { - player("What's a star sprite?") - stage = 10 - } - 2 -> { - player("What are you going to do without your star?") - stage = 20 - } - 3 -> { - player("Well, I'm glad you're okay.") - stage = 40 - } - } - 40 -> { - npc("Thank you.") - stage++ - } - 41 -> end() - 50 -> { - val wearingRing = ContentAPI.inEquipment(player, Items.RING_OF_THE_STAR_SPRITE_14652) - val dust = if (player.getInventory().getAmount(ShootingStarOptionHandler.STAR_DUST) > 200) 200 else player.getInventory().getAmount(ShootingStarOptionHandler.STAR_DUST) - if (player.getInventory().remove(Item(ShootingStarOptionHandler.STAR_DUST, dust))) { - val cosmicRunes = (Math.ceil(0.76 * dust) * AMPLIFIER).toInt() - val astralRunes = (Math.ceil(0.26 * dust) * AMPLIFIER).toInt() - val goldOre = (Math.ceil(0.1 * dust) * AMPLIFIER).toInt() - val coins = (Math.ceil(250.0 * dust) * AMPLIFIER).toInt() // limits the amount of gp per turn-in to 90k, same as 2009 rs, and minimum from all stardust from a level 1 star works out to 50k, same as rs in 2009 - player.getInventory().add(Item(COSMIC_RUNE, cosmicRunes), player) - player.getInventory().add(Item(ASTRAL_RUNE, astralRunes), player) - player.getInventory().add(Item(GOLD_ORE, goldOre), player) - player.getInventory().add(Item(COINS, coins), player) - npc("I have rewarded you by making it so you can mine", "extra ore for the next 15 minutes. Also, have $cosmicRunes", "cosmic runes, $astralRunes astral runes, $goldOre gold ore and $coins", "coins.") - - getStoreFile()[player.username.toLowerCase()] = true //flag daily as completed - - player.registerState("shooting-star")?.init() - - if(wearingRing){ - val item = intArrayOf(Items.COSMIC_RUNE_564, Items.ASTRAL_RUNE_9075, Items.GOLD_ORE_445, Items.COINS_995).random() - val amount = when(item){ - Items.COSMIC_RUNE_564 -> cosmicRunes - Items.ASTRAL_RUNE_9075 -> astralRunes - Items.GOLD_ORE_445 -> goldOre - Items.COINS_995 -> coins - else -> 0 - } - rollForRingBonus(player, item, amount) - } - } - - if(!ContentAPI.inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !ContentAPI.inBank(player, Items.ANCIENT_BLUEPRINT_14651) && RandomFunction.roll(500)){ - ContentAPI.addItemOrDrop(player, Items.ANCIENT_BLUEPRINT_14651, 1) - ContentAPI.sendMessage(player, colorize("%RThe Star Sprite dropped what looks like some ancient piece of paper and you pick it up.")) - ContentAPI.sendNews("${player.username} found an Ancient Blueprint while mining a shooting star!") - } - stage = 52 - } - 52 -> end() - - //Inauthentic ring-based dialogue - 1000 -> playerl(FacialExpression.ASKING, "Oh, you mean this?").also { stage++ } - 1001 -> player.dialogueInterpreter.sendItemMessage(Items.ANCIENT_BLUEPRINT_14651, "You show the blueprint to the Star Sprite.").also { stage++ } - 1002 -> npcl(FacialExpression.ASKING, "Yeah, that's the one, alright!").also { stage++ } - 1003 -> npcl(FacialExpression.NEUTRAL, "I'll tell you what.. if you can get ahold of the resources needed to make one, I'm sure me or one of my kin would craft it for you.").also { stage++ } - 1004 -> playerl(FacialExpression.ASKING, "You'd just do that for me? For free?").also { stage++ } - 1005 -> npcl(FacialExpression.NEUTRAL, "I don't see why not. We used to make these for fun and hand them out to adventurers all the time.").also { stage++ } - 1006 -> playerl(FacialExpression.ASKING, "Well, thanks! So.. what do we need to make one?").also { stage++ } - 1007 -> npcl(FacialExpression.NEUTRAL, "Looking at the blueprint here...").also { stage++ } - 1008 -> npcl(FacialExpression.NEUTRAL, "Yes, it seems we need a ring mould, a silver bar, a cut dragonstone and 200 stardust. Oh, and make sure to bring this blueprint with you.").also { stage++ } - 1009 -> playerl(FacialExpression.FRIENDLY, "Thanks, I'll get right on it!").also { stage++ } - 1010 -> playerl(FacialExpression.ASKING, "So just to make sure I've got it right, I need a ring mould, a silver bar, a cut dragonstone and 200 stardust, as well as this blueprint?").also { stage++ } - 1011 -> npcl(FacialExpression.NEUTRAL, "Yeah, you've got it, human. Any of my kin should be able to do this for you.").also { stage++; ContentAPI.setAttribute(player, "/save:star-ring:bp-shown", true) } - 1012 -> playerl(FacialExpression.FRIENDLY, "Thanks!").also { stage = END_DIALOGUE } - - 2000 -> npcl(FacialExpression.NEUTRAL, "Yes, did you bring the components to make it, human?").also { stage++ } - 2001 -> if(ContentAPI.inInventory(player, Items.DRAGONSTONE_1615,1) && ContentAPI.inInventory(player, Items.RING_MOULD_1592, 1) && ContentAPI.inInventory(player, Items.STARDUST_13727, 200) && ContentAPI.inInventory(player, Items.SILVER_BAR_2355, 1)){ - playerl(FacialExpression.FRIENDLY, "Yes, I have them right here, friend.").also { stage++ } - } else { - playerl(FacialExpression.HALF_GUILTY, "I'm afraid not, what did I need again?").also { stage = 2100 } - } - 2002 -> npcl(FacialExpression.NEUTRAL, "Excellent, give me just a moment here...").also { stage++ } - 2003 -> sendDialogue("You watch as the Star Sprite casts some strange spell.").also { stage++ } - 2004 -> if(ContentAPI.removeItem(player, Items.SILVER_BAR_2355, Container.INVENTORY) && ContentAPI.removeItem(player, Items.DRAGONSTONE_1615, Container.INVENTORY) && ContentAPI.removeItem(player, Item(Items.STARDUST_13727, 200), Container.INVENTORY)){ - ContentAPI.addItem(player, Items.RING_OF_THE_STAR_SPRITE_14652) - player.dialogueInterpreter.sendItemMessage(Items.RING_OF_THE_STAR_SPRITE_14652, "The Star Sprite hands you a strange ring.").also { stage++ } - } else end() - 2005 -> npcl(FacialExpression.NEUTRAL, "There you go, I hope you enjoy it!").also { stage++ } - 2006 -> playerl(FacialExpression.FRIENDLY, "Thank you!").also { stage = END_DIALOGUE } - - 2100 -> npcl(FacialExpression.NEUTRAL, "A ring mould, a cut dragonstone, a silver bar and 200 stardust.").also { stage = END_DIALOGUE } - } - return true - } - - override fun getIds(): IntArray? { - return intArrayOf(8091) - } - - 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()) - ContentAPI.sendMessage(player, colorize("%RYour ring shines dimly as if imbued with energy.")) - } else if(RandomFunction.roll(5)){ - ContentAPI.addItem(player, bonusId, bonusBaseAmt) - ContentAPI.sendMessage(player, colorize("%RYour ring shines brightly as if surging with energy and then fades out.")) - } else if(RandomFunction.roll(25)){ - player.savedData.globalData.starSpriteDelay = 0L - ContentAPI.sendMessage(player, colorize("%RYour ring vibrates briefly as if surging with power, and then stops.")) - } - } - - fun getStoreFile(): JSONObject{ - return ServerStore.getArchive("daily-shooting-star") - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/jobs/CancelJobDialogueFile.kt b/Server/src/main/kotlin/rs09/game/content/jobs/CancelJobDialogueFile.kt deleted file mode 100644 index 8dbecf145..000000000 --- a/Server/src/main/kotlin/rs09/game/content/jobs/CancelJobDialogueFile.kt +++ /dev/null @@ -1,55 +0,0 @@ -package rs09.game.content.jobs - -import GatheringJobs -import SlayingJob -import core.game.node.entity.npc.NPC -import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE - -class CancelJobDialogueFile : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - 0 -> options("Check Job Progress","Cancel Job").also { stage++ } - 1 -> when(buttonID){ - 1 -> player("How am I doing on my job?").also { stage++ } - 2 -> player("I'd like to cancel my job.").also { stage = 10 } - } - 2 -> { - val amt = player!!.getAttribute("jobs:original_amount",0) - val type = player!!.getAttribute("jobs:type",0) - val jobId = player!!.getAttribute("jobs:id",0) - val needed = player!!.getAttribute("jobs:amount",0) - if(type == 0){ - player!!.dialogueInterpreter.sendItemMessage(GatheringJobs.values()[jobId].itemId,"You still need to gather ${needed} more.") - } else { - player!!.dialogueInterpreter.sendDialogue("You still need to kill $needed more ${NPC(SlayingJob.values()[jobId].ids[0]).name.toLowerCase()}.") - } - stage = END_DIALOGUE - } - - 10 -> npc("It will cost 500 coins.").also { stage++ } - 11 -> options("Yes, please.","No, thanks.").also { stage++ } - 12 -> when(buttonID){ - 1 -> player("Yes, please.").also{ stage = 20 } - 2 -> player("No, thanks.").also { stage = 30 } - } - 20 -> npc("Alright then, hand over the money.").also { stage++ } - 21 -> if(player!!.inventory.contains(995,500)){ - player("Here you go.") - player!!.inventory.remove(Item(995,500)) - player!!.removeAttribute("jobs:id") - player!!.removeAttribute("jobs:amount") - player!!.removeAttribute("jobs:original_amount") - player!!.removeAttribute("jobs:type") - stage = END_DIALOGUE - } else { - player("Oh, I don't seem to have the money...") - 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/kotlin/rs09/game/content/jobs/JobManager.kt b/Server/src/main/kotlin/rs09/game/content/jobs/JobManager.kt deleted file mode 100644 index d9703f8ad..000000000 --- a/Server/src/main/kotlin/rs09/game/content/jobs/JobManager.kt +++ /dev/null @@ -1,96 +0,0 @@ -package rs09.game.content.jobs - -import GatheringJobs -import SlayingJob -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 org.json.simple.JSONObject -import rs09.ServerStore -import rs09.ServerStore.getInt -import rs09.game.system.SystemLogger -import java.util.concurrent.TimeUnit - -object JobManager { - @JvmStatic - fun updateJobRemaining(player: Player, amount: Int = 0) { - val cur = player.getAttribute("jobs:amount",0) - val new = cur - amount - val jobType = player.getAttribute("jobs:type",0) - val jobId = player.getAttribute("jobs:id",0) - - var gJob: GatheringJobs? = null - var sJob: SlayingJob? = null - - if(jobType == 0){ - gJob = GatheringJobs.values()[jobId] - } else { - sJob = SlayingJob.values()[jobId] - } - - if(new % 5 == 0 && sJob != null && new > 0){ - player.sendMessage("You have $new ${NPC(sJob.ids[0]).name.toLowerCase()} left to go.") - } - if(new == 0 && sJob != null){ - player.sendMessage("You have completed your job.") - } - player.setAttribute("jobs:amount",new) - } - - @JvmStatic - fun handleDeath(npc: Int, player: Player) { - val jobId = player.getAttribute("jobs:id",0) - val type = player.getAttribute("jobs:type",0) - if(type == 1) { - val job = SlayingJob.values()[jobId] - if (job.ids.contains(npc)) { - updateJobRemaining(player, 1) - } - } - } - - @JvmStatic - fun rewardPlayer(player: Player, npc: NPC){ - val amt = player.getAttribute("jobs:original_amount",0) - val type = player.getAttribute("jobs:type",0) - val jobId = player.getAttribute("jobs:id",0) - if(type == 0){ - val it = Item(GatheringJobs.values()[jobId].itemId) - var amount = player.inventory.getAmount(it) - val needed = player.getAttribute("jobs:amount",0) - if(amount == 0){ - player.dialogueInterpreter.open(CancelJobDialogueFile(),npc) - SystemLogger.logAlert("Opening CancelJob") - return - } - if(amount < needed){ - player.dialogueInterpreter.sendItemMessage(GatheringJobs.values()[jobId].itemId,"You still need to gather ${needed - amount} more.") - player.inventory.remove(Item(it.id,amount)) - player.setAttribute("jobs:amount",needed - amount) - return - } - if(amount > needed) amount = needed - player.inventory.remove(Item(it.id,amount)) - } else { - val needed = player.getAttribute("jobs:amount",0) - if(needed > 0){ - player.dialogueInterpreter.sendDialogue("You still need to kill $needed more ${NPC(SlayingJob.values()[jobId].ids[0]).name.toLowerCase()}.") - return - } - } - if(!player.inventory.add(Item(995,250 * amt))){ - GroundItemManager.create(Item(995,250 * amt),player.centerLocation,player) - } - player.removeAttribute("jobs:id") - player.removeAttribute("jobs:amount") - player.removeAttribute("jobs:original_amount") - player.removeAttribute("jobs:type") - - getStoreFile()[player.username.toLowerCase()] = getStoreFile().getInt(player.username.toLowerCase()) + 1 - } - - fun getStoreFile(): JSONObject { - return ServerStore.getArchive("daily-jobs-tracking") - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/jobs/WorkForInteractionListener.kt b/Server/src/main/kotlin/rs09/game/content/jobs/WorkForInteractionListener.kt deleted file mode 100644 index 22e638436..000000000 --- a/Server/src/main/kotlin/rs09/game/content/jobs/WorkForInteractionListener.kt +++ /dev/null @@ -1,116 +0,0 @@ -package rs09.game.content.jobs - -import GatheringJobs -import SlayingJob -import api.ContentAPI -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 org.json.simple.JSONObject -import org.rs09.consts.Items -import rs09.ServerStore -import rs09.ServerStore.getInt -import rs09.game.interaction.InteractionListener -import rs09.game.system.SystemLogger -import java.util.concurrent.TimeUnit - -/** - * Handles the work-for actions for the NPCs - * @author Ceikry - */ -class WorkForInteractionListener : InteractionListener() { - val possibleWeaponLooks = arrayListOf( - Items.BRONZE_SCIMITAR_1321, - Items.STEEL_SCIMITAR_1325, - Items.RUNE_SCIMITAR_1333, - Items.BRONZE_MACE_1422, - Items.MITHRIL_WARHAMMER_1343 - ) - - val gatheringMap = mapOf>( - 0 to listOf(GatheringJobs.AIR_RUNE,GatheringJobs.COWHIDES,GatheringJobs.WATER_RUNE), - 922 to listOf(GatheringJobs.ASHES,GatheringJobs.AIR_RUNE,GatheringJobs.WATER_RUNE), - 3807 to listOf(GatheringJobs.COWHIDES), - 4899 to listOf(GatheringJobs.CAKE,GatheringJobs.ANCHOVY_PIZZA,GatheringJobs.COOKED_SALMON,GatheringJobs.COOKED_TROUT,GatheringJobs.MEAT_PIE,GatheringJobs.MEAT_PIZZA,GatheringJobs.PLAIN_PIZZA), - 4901 to listOf(GatheringJobs.RAW_SALMON,GatheringJobs.RAW_SHRIMP,GatheringJobs.RAW_TROUT), - 4902 to listOf(GatheringJobs.COPPER_ORE,GatheringJobs.TIN_ORE,GatheringJobs.COAL,GatheringJobs.IRON_ORE,GatheringJobs.SILVER_ORE,GatheringJobs.GOLD_ORE), - 4903 to listOf(GatheringJobs.SILVER_ORE), - 4904 to listOf(GatheringJobs.BRONZE_BAR,GatheringJobs.IRON_BAR,GatheringJobs.STEEL_BAR), - 4906 to listOf(GatheringJobs.LOG,GatheringJobs.OAK,GatheringJobs.WILLOW) - ) - - val typeMap = mapOf( - 0 to 0, - 705 to 1, - 922 to 0, - 1861 to 1, - 3807 to 0, - 4899 to 0, - 4901 to 0, - 4902 to 0, - 4903 to 0, - 4904 to 0, - 4906 to 0, - 4707 to 1 - ) - - val IDs = typeMap.keys.toIntArray() - - override fun defineListeners() { - - on(IDs,NPC,"work-for"){ player,node -> - var amount = 0 - var jobId = 0 - - if(JobManager.getStoreFile().getInt(player.username.toLowerCase()) == 3){ - ContentAPI.sendNPCDialogue(player, node.id,"You've hit your limit for the day. Come back tomorrow!") - return@on true - } - - if(player.getAttribute("jobs:id",-1) != -1){ - JobManager.rewardPlayer(player,node.asNpc()) - return@on true - } - - val type = typeMap[node.id] ?: return@on false - jobId = if(type == 0) { - var job = gatheringMap[node.id]?.filter { checkRequirement(player, it) }?.random() - amount = job?.getAmount() ?: 0 - job?.ordinal ?: 0 - } else { - SlayingJob.values().random().ordinal.also { amount = SlayingJob.values()[it].getAmount() } - } - - when(type){ - 0 -> { - val job = GatheringJobs.values()[jobId] - player.dialogueInterpreter.sendItemMessage(job.itemId,"You are assigned to gather $amount ${Item(job.itemId).name.toLowerCase()}") - - // Have the Fishing Tutor send you on an errand - if (node.id == 4901) player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 14); - } - 1 -> { - val job = SlayingJob.values()[jobId] - player.dialogueInterpreter.sendItemMessage(possibleWeaponLooks.random(),"You are assigned to kill $amount ${NPC(job.ids[0]).name.toLowerCase()}") - } - } - - player.setAttribute("/save:jobs:id",jobId) - player.setAttribute("/save:jobs:amount",amount) - player.setAttribute("/save:jobs:original_amount",amount) - player.setAttribute("/save:jobs:type",type) - - return@on true - } - - } - - fun checkRequirement(player: Player, jobs: GatheringJobs?): Boolean{ - jobs ?: return true - if(player.skills.getLevel(jobs.skill) < jobs.lvlReq){ - return false - } - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/jobs/impl/GatheringJob.kt b/Server/src/main/kotlin/rs09/game/content/jobs/impl/GatheringJob.kt deleted file mode 100644 index 6610a5265..000000000 --- a/Server/src/main/kotlin/rs09/game/content/jobs/impl/GatheringJob.kt +++ /dev/null @@ -1,66 +0,0 @@ -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/kotlin/rs09/game/content/jobs/impl/SlayingJob.kt b/Server/src/main/kotlin/rs09/game/content/jobs/impl/SlayingJob.kt deleted file mode 100644 index 13afc20c4..000000000 --- a/Server/src/main/kotlin/rs09/game/content/jobs/impl/SlayingJob.kt +++ /dev/null @@ -1,13 +0,0 @@ -import core.tools.RandomFunction - -enum class SlayingJob(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/kotlin/rs09/game/content/quest/free/cooksassistant/MillieMillerDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/MillieMillerDialogue.kt deleted file mode 100644 index 519803df8..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/cooksassistant/MillieMillerDialogue.kt +++ /dev/null @@ -1,70 +0,0 @@ - package rs09.game.content.quest.free.cooksassistant - - import rs09.ServerConstants - import core.game.content.dialogue.DialoguePlugin - import core.game.content.dialogue.FacialExpression - import core.game.node.entity.player.Player - import core.plugin.Initializable - - /** - * Dialogue for Millie Miller. - * @author Qweqker - */ - - @Initializable - class MillieMillerDialogue (player: Player? = null) : DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - - npc(FacialExpression.HAPPY, "Hello Adventurer. Welcome to Mill Lane Mill. Can I", "help you?") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - - //Continuation of Millie's greeting - 0 -> options("Who are you?", "What is this place?", "How do I mill flour?", "I'm fine, thanks.").also { stage++ } - 1 -> when (buttonId) { - 1 -> player(FacialExpression.ASKING, "Who are you?").also { stage = 10 } - 2 -> player(FacialExpression.ASKING, "What is this place?").also { stage = 20 } - 3 -> player(FacialExpression.ASKING, "How do I mill flour?").also { stage = 30 } - 4 -> player(FacialExpression.NEUTRAL, "I'm fine, thanks.").also { stage = 1000 } - } - - //Who are you? - 10 -> npc(FacialExpression.FRIENDLY, "I'm Miss Millicent Miller the Miller of Mill Lane Mill.", "Our family have been milling flour for generations.").also { stage = 10 } - 11 -> player(FacialExpression.FRIENDLY, "It's a good business to be in. People will always need", "flour.").also { stage++ } - 12 -> player(FacialExpression.ASKING, "How do I mill flour?").also { stage = 30 } - - //What is this place? - 20 -> npc(FacialExpression.SUSPICIOUS, "This is Mill Lane Mill. Millers of the finest flour in", ServerConstants.SERVER_NAME + ", and home to the Miller family for many", "generations").also { stage++ } - 21 -> npc(FacialExpression.HAPPY, "We take grain from the field nearby and mill into flour.").also { stage++ } - 22 -> player(FacialExpression.ASKING, "How do I mill flour?").also { stage = 30 } - - //How do I mill flour? - 30 -> npc(FacialExpression.FRIENDLY, "Making flour is pretty easy. First of all you need to", "get some grain. You can pick some from wheat fields.", "There is one just outside the Mill, but there are many", "others scattered across " + ServerConstants.SERVER_NAME + ". Feel free to pick wheat").also { stage++ } - 31 -> npc(FacialExpression.FRIENDLY, "from our field! There always seems to be plenty of", "wheat there.").also { stage++ } - 32 -> player(FacialExpression.ASKING, "Then I bring my wheat here?").also { stage++ } - 33 -> npc(FacialExpression.FRIENDLY, "Yes, or one of the other mills in " + ServerConstants.SERVER_NAME + ". They all work", "the same way. Just take your grain to the top floor of", "the mill (up two ladders, there are three floors including", "this one) and then place some grain in to the hopper.").also { stage++ } - 34 -> npc(FacialExpression.HAPPY, "Then you need to start the grinding process by pulling", "the hopper lever. You can add more grain, but each", "time you add grain you have to pul the hopper lever", "again.").also { stage++ } - 35 -> player(FacialExpression.ASKING, "So where does the flour go then?").also { stage++ } - 36 -> npc(FacialExpression.SUSPICIOUS, "The flour appears in this room here, you'll need a pot", "to put the flour into. One pot will hold the flour made", "by one load of grain").also { stage++ } - 37 -> npc(FacialExpression.HAPPY, "And that's it! You now have some pots of finely ground", "flour of the highest quality. Ideal for making tasty cakes", "or delicous bread. I'm not a cook so you'll have to ask a", "cook to ind out how to bake things.").also { stage++ } - 38 -> npc(FacialExpression.HAPPY, "Great! Thanks for your help.").also { stage = 1000 } - - //Conversation Endpoint - 1000 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return MillieMillerDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(3806) - } - } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/DSEquipListeners.kt b/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/DSEquipListeners.kt deleted file mode 100644 index 89e5896fb..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/DSEquipListeners.kt +++ /dev/null @@ -1,38 +0,0 @@ -package rs09.game.content.quest.free.dragonslayer - -import api.ContentAPI -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener - -class DSEquipListeners : InteractionListener() { - - private val restrictedItems = intArrayOf( - Items.RUNE_PLATEBODY_1127, - Items.RUNE_PLATEBODY_G_10798, - Items.RUNE_PLATEBODY_G_2615, - Items.RUNE_PLATEBODY_G_13800, - Items.RUNE_PLATEBODY_T_10800, - Items.RUNE_PLATEBODY_T_13805, - Items.RUNE_PLATEBODY_T_2623, - Items.ZAMORAK_PLATEBODY_2653, - Items.SARADOMIN_PLATEBODY_2661, - Items.GUTHIX_PLATEBODY_2669, - Items.GREEN_DHIDE_BODY_1135, - Items.DHIDE_BODYG_7370, - Items.DHIDE_BODY_G_7374, - Items.DHIDE_BODY_T_7372, - Items.DHIDE_BODY_T_7376 - ) - - override fun defineListeners() { - for(id in restrictedItems){ - onEquip(id){ player, _ -> - if(!player.questRepository.isComplete("Dragon Slayer")){ - ContentAPI.sendMessage(player, "You must have completed Dragon Slayer to equip this.") - return@onEquip false - } - return@onEquip true - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/NedDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/NedDialogue.kt deleted file mode 100644 index 3e1c93f67..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/free/dragonslayer/NedDialogue.kt +++ /dev/null @@ -1,362 +0,0 @@ -package rs09.game.content.quest.free.dragonslayer - -import core.game.content.dialogue.DialoguePlugin -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.GroundItemManager -import core.game.node.item.Item -import rs09.game.content.quest.free.princealirescue.NedPARDialogue -import rs09.game.world.GameWorld.settings -import rs09.tools.DIALOGUE_INITIAL_OPTIONS_HANDLE -import rs09.tools.END_DIALOGUE - - -/** - * Core dialogue plugin for Ned. - * @author Ceikry - */ -class NedDialogue(player: Player? = null) : DialoguePlugin(player) { - /** - * The achievement diary. - */ - private var diary: AchievementDiary? = null - private val level = 2 - - override fun newInstance(player: Player): DialoguePlugin { - return NedDialogue(player) - } - - override fun open(vararg args: Any): Boolean { - npc = args[0] as NPC - val dSlayerStage = player.questRepository.getStage("Dragon Slayer") - val parStage = player.questRepository.getStage("Prince Ali Rescue") - - if (dSlayerStage == 20 || dSlayerStage == 30) { - addOption("Dragon Slayer", NedDSDialogue(dSlayerStage)) - } - if (parStage == 20 || parStage == 30 || parStage == 40 || parStage == 50) { - addOption("Prince Ali Rescue", NedPARDialogue(parStage)) - } - - if (!sendChoices()) { - npc( - "Why, hello there, lad. Me friends call me Ned. I was a", - "man of the sea, but it's past me now. Could I be", - "making or selling you some rope?" - ) - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (stage) { - - DIALOGUE_INITIAL_OPTIONS_HANDLE -> { - npc( - "Why, hello there, lad. Me friends call me Ned. I was a", - "man of the sea, but it's past me now. Could I be", - "making or selling you some rope?" - ) - loadFile(optionFiles[buttonId - 1]) - } - - 0 -> { - options( - "Yes, I would like some rope.", - "No thanks Ned, I don't need any.", - "Ask about Achievement Diaries" - ) - stage = 1 - } - 1 -> when (buttonId) { - 1 -> { - player("Yes, I would like some rope.") - stage = 10 - } - 2 -> { - player("No thanks Ned, I don't need any.") - stage = END_DIALOGUE - } - 3 -> { - player("I'd like to talk about Achievement Diaries.") - stage = 900 - } - } - 10 -> { - npc( - "Well, I can sell you some rope for 15 coins. Or I can", - "be making you some if you gets me 4 balls of wool. I", - "strands them together I does, makes em strong." - ) - stage = 11 - } - 11 -> { - player("You make rope from wool?") - stage = 12 - } - 12 -> { - npc("Of course you can!") - stage = 13 - } - 13 -> { - player("I thought you needed hemp or jute.") - stage = 14 - } - 14 -> { - npc("Do you want some rope or not?") - stage = 15 - } - 15 -> stage = if (!player.inventory.containsItem(WOOL)) { - options( - "Okay, please sell me some rope.", - "That's a little more than I want to pay.", - "I will go and get some wool." - ) - 16 - } else { - options( - "Okay, please sell me some rope.", - "I have some balls of wool. Could you make me some rope?", - "That's a little more than I want to pay." - ) - 17 - } - 17 -> when (buttonId) { - 1 -> { - player("Okay, please sell me some rope.") - stage = 100 - } - 2 -> { - player("I have some balls of wool.", "Could you make me some rope?") - stage = 120 - } - 3 -> { - player("That's a little more than I want to pay.") - stage = 200 - } - } - 16 -> when (buttonId) { - 1 -> { - player("Okay, please sell me some rope.") - stage = 100 - } - 2 -> { - player("That's a little more than I want to pay.") - stage = 200 - } - 3 -> { - player("I will go and get some wool.") - stage = 300 - } - } - 40 -> when (buttonId) { - 1 -> { - player("Ned could you make other things from wool?") - stage = 41 - } - 2 -> { - player("Yes, I would like some rope.") - stage = 10 - } - 3 -> { - player("No thanks Ned, I don't need any.") - stage = 20 - } - } - 100 -> { - npc("There you go, finest rope in " + settings!!.name + ".") - stage = 101 - } - 101 -> { - interpreter.sendDialogue("You hand Ned 15 coins. Ned gives you a coil of rope.") - stage = 102 - } - 102 -> { - if (player.inventory.remove(COINS)) { - if (!player.inventory.add(ROPE)) { - GroundItemManager.create(ROPE, player) - } - } else { - player.packetDispatch.sendMessage("You don't have enough coins to pay for rope.") - } - end() - } - 120 -> { - interpreter.sendDialogues(npc, null, "Sure I can.") - stage = 121 - } - 121 -> { - interpreter.sendDialogue("You hand over 4 balls of wool. Ned gives you a coil of rope.") - stage = 122 - } - 122 -> { - if (player.inventory.remove(WOOL)) { - player.inventory.add(ROPE) - } - end() - } - 200 -> { - npc( - "Well, if you ever need rope that's the price. Sorry.", - "An old sailor needs money for a little drop o' rum." - ) - stage = 201 - } - 201 -> end() - 300 -> { - npc("Aye, you do that. Remember, it takes 4 balls of wool to", "make strong rope.") - stage = 301 - } - 301 -> end() - 20 -> { - npc( - "Well, old Neddy is always here if you do. Tell your", - "friends. I can always be using the business." - ) - stage = 21 - } - 21 -> end() - 900 -> { - if (diary == null) { - diary = player.achievementDiaryManager.getDiary(DiaryType.LUMBRIDGE) - } - if (diary!!.isComplete(level, true) && !diary!!.isLevelRewarded(level)) { - player("I've done all the medium tasks in my Lumbridge", "Achievement Diary.") - stage = 950 - } - else if (diary!!.isLevelRewarded(level) && diary!!.isComplete(level, true) && !player.hasItem(diary!!.type.getRewards(level)[0])) { - player("I've seemed to have lost my explorer's ring...") - stage = 960 - } - else { - options( - "What is the Achievement Diary?", - "What are the rewards?", - "How do I claim the rewards?", - "See you later." - ) - stage = 901 - } - } - 901 -> when (buttonId) { - 1 -> { - player("What is the Achievement Diary?") - stage = 910 - } - 2 -> { - player("What are the rewards?") - stage = 920 - } - 3 -> { - player("How do I claim the rewards?") - stage = 930 - } - 4 -> { - player("See you later!") - stage = 940 - } - } - 910 -> { - npc( - "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", - "discover some very useful things indeed." - ) - stage++ - } - 911 -> { - npc("Eventually with enough exploration you will be", "rewarded for your explorative efforts.") - stage++ - } - 912 -> { - 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 = 900 - } - 920 -> { - 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++ - } - 921 -> { - npc("This ring will become increasingly useful with each", "section of the diary that you complete.") - stage = 900 - } - 930 -> { - npc( - "You need to complete the task 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++ - } - 931 -> { - npc( - "To claim the explorer's ring speak to Explorer Jack in ", - "Lumbridge, Bob in Bob's Axes in Lumbridge, or myself." - ) - stage = 900 - } - 940 -> end() - 950 -> { - npc("Yes I see that, you'll be wanting your", "reward then I assume?") - stage++ - } - 951 -> { - player("Yes please.") - stage++ - } - 952 -> { - diary!!.setLevelRewarded(level) - for (i in diary!!.type.getRewards(level)) { - player.inventory.add(i, player) - } - npc("This ring is a representation of the adventures you", "went on to complete your tasks.") - stage++ - } - 953 -> { - player("Wow, thanks!") - stage = 900 - } - 960 -> { - player.inventory.add(diary!!.type.getRewards(level)[0], player) - npc("You better be more careful this time.") - stage = 900 - } - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(743) - } - - companion object { - /** - * Represents the coins item to remove. - */ - private val COINS = Item(995, 15) - - /** - * Represents the rope item to add. - */ - private val ROPE = Item(954) - - /** - * Represents the ball of wool item. - */ - private val WOOL = Item(1759, 4) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/AllFiredUp.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/AllFiredUp.kt deleted file mode 100644 index da2c5f525..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/allfiredup/AllFiredUp.kt +++ /dev/null @@ -1,95 +0,0 @@ -package rs09.game.content.quest.members.allfiredup - -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 rs09.game.content.activity.allfiredup.AFUBeacon - -/** - * Represents the "All Fired Up" quest. - * @author Ceikry - */ -@Initializable -class AllFiredUp : Quest("All Fired Up", 157, 156, 1){ - 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 !!King Roald?? in Varrock 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")) - - } 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) - } - 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 == 30){ - line(player, "I lit the beacon. I should speak to !!Blaze Sharpeye?? again.", line++, stage >= 40) - } - 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 >= 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 == 60){ - line(player, "I should return to !!Blaze Sharpeye??.", line++) - } - 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 == 80){ - line(player, "I should speak to !!Blaze?? again.", line++) - } - if(stage == 90){ - line(player, "I should report to !!King Roald??.", line++) - } - if(stage == 100){ - line(player, "I helped King Roald test the beacon network", line++) - line(player, " and have gained full access to it.", line++) - } - } - } - - override fun finish(player: Player?) { - super.finish(player) - player ?: return - player.packetDispatch.sendString("20,000gp,", 277, 8 + 2) - player.packetDispatch.sendString("5,500 Firemaking XP", 277, 9 + 2) - player.packetDispatch.sendString("Full access to the beacon network", 277, 10 + 2) - player.packetDispatch.sendString("1 Quest Point", 277, 11 + 2) - 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) - AFUBeacon.resetAllBeacons(player) - player.varpManager.get(1283).setVarbit(0,0).send(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/kotlin/rs09/game/content/quest/members/naturespirit/NSListeners.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSListeners.kt deleted file mode 100644 index 70e619568..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSListeners.kt +++ /dev/null @@ -1,234 +0,0 @@ -package rs09.game.content.quest.members.naturespirit - -import api.Container -import api.ContentAPI -import api.DialUtils -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.item.GroundItem -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.game.world.map.Location -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import rs09.game.content.dialogue.DialogueFile -import rs09.game.content.global.action.PickupHandler -import rs09.game.interaction.InteractionListener -import rs09.game.node.entity.npc.other.MortMyreGhastNPC -import rs09.game.system.SystemLogger -import rs09.game.system.config.ShopParser -import rs09.tools.END_DIALOGUE - -class NSListeners : InteractionListener() { - - val GROTTO_TREE = 3517 - val GROTTO_ENTRANCE = 3516 - val GROTTO_ALTAR = 3520 - val NATURE_ALTAR = 3521 - val JOURNAL = Items.JOURNAL_2967 - val NATURE_STONE = 3527 - val FAITH_STONE = 3528 - val FREELY_GIVEN_STONE = 3529 - val WASHING_BOWL = Items.WASHING_BOWL_2964 - val MIRROR = Items.MIRROR_2966 - val SPELLCARD = Items.DRUIDIC_SPELL_2968 - val USED_SPELLCARD = Items.A_USED_SPELL_2969 - val FUNGUS = Items.MORT_MYRE_FUNGUS_2970 - val MIRROR_TAKEN = "/save:ns:mirror_taken" - val GROTTO_SEARCHED = "/save:ns:grotto_searched" - val WISHING_WELL = 28715 - val DRUID_POUCH = Items.DRUID_POUCH_2958 - val DRUID_POUCH_EMPTY = Items.DRUID_POUCH_2957 - val stones = intArrayOf(NATURE_STONE, FAITH_STONE, FREELY_GIVEN_STONE) - val items = intArrayOf(USED_SPELLCARD, FUNGUS) - - override fun defineListeners() { - on(GROTTO_TREE, SCENERY, "look-at"){player, _ -> - ContentAPI.sendMessage(player, "It looks like a tree on a large rock with roots trailing down to the ground.") - return@on true - } - - on(GROTTO_TREE, SCENERY, "search"){player, _ -> - if(!ContentAPI.getAttribute(player, GROTTO_SEARCHED, false) || !(ContentAPI.inInventory(player, JOURNAL) || ContentAPI.inBank(player, JOURNAL))){ - ContentAPI.sendItemDialogue(player, JOURNAL, "You search the strange rock. You find a knot and inside of it you discover a small tome. The words on the front are a bit vague, but you can make out the words 'Tarlock' and 'journal'.") - ContentAPI.addItemOrDrop(player, JOURNAL, 1) - ContentAPI.setAttribute(player, GROTTO_SEARCHED, true) - return@on true - } - return@on false - } - - on(GROTTO_ENTRANCE, SCENERY, "enter"){player, node -> - val questStage = player.questRepository.getQuest("Nature Spirit").getStage(player) - if(questStage < 55) { - val npc = core.game.node.entity.npc.NPC.create(NPCs.FILLIMAN_TARLOCK_1050, Location.create(3440, 3336, 0)) - npc.init() - } else if(questStage < 60) { - player.teleport(Location.create(3442, 9734, 0)) - } else if (questStage >= 60){ - player.teleport(Location.create(3442, 9734, 1)) - } - return@on true - } - - on(GROTTO_ALTAR, SCENERY,"search"){player, node -> - val stage = player.questRepository.getStage("Nature Spirit") - if(stage == 55){ - ContentAPI.openDialogue(player, FillimanCompletionDialogue(), NPC(NPCs.FILLIMAN_TARLOCK_1050)) - return@on true - } - - return@on false - } - - on(NATURE_STONE, SCENERY, "search"){player, _ -> - ContentAPI.sendDialogue(player, "You search the stone and find that it has some sort of nature symbol scratched into it.") - return@on true - } - - on(FAITH_STONE, SCENERY, "search"){player, _ -> - ContentAPI.sendDialogue(player, "You search the stone and find that it has some sort of faith symbol scratched into it.") - return@on true - } - - on(FREELY_GIVEN_STONE, SCENERY, "search"){player, _ -> - ContentAPI.sendDialogue(player, "You search the stone and find it has some sort of spirit symbol scratched into it.") - return@on true - } - - on(WISHING_WELL, SCENERY, "make-wish"){player, node -> - if(player.questRepository.isComplete("Nature Spirit") && player.questRepository.isComplete("Wolf Whistle")) - ShopParser.openUid(player, 241) - else - ContentAPI.sendDialogue(player, "You can't do that yet.") - - return@on true - } - - on(JOURNAL, ITEM, "read"){player, _ -> - player.dialogueInterpreter.open(NSJournalDialogue()) - return@on true - } - - on(WASHING_BOWL, GROUNDITEM, "take"){player, node -> - SystemLogger.logInfo("Running listener") - GroundItemManager.create(Item(MIRROR), node.location, player) - PickupHandler.take(player, node as GroundItem) - return@on true - } - - on(MIRROR, GROUNDITEM, "take"){player, node -> - if(ContentAPI.getAttribute(player, MIRROR_TAKEN, false) && (ContentAPI.inInventory(player, MIRROR) || ContentAPI.inBank(player, MIRROR))){ - ContentAPI.sendDialogue(player, "I don't need another one of these.") - return@on true - } - ContentAPI.setAttribute(player, MIRROR_TAKEN, true) - PickupHandler.take(player, node as GroundItem) - return@on true - } - - on(SPELLCARD, ITEM, "cast"){player, node -> - if(NSUtils.castBloom(player)){ - ContentAPI.removeItem(player, node.asItem(), Container.INVENTORY) - ContentAPI.addItem(player, Items.A_USED_SPELL_2969) - } - return@on true - } - - on(intArrayOf(DRUID_POUCH, DRUID_POUCH_EMPTY), ITEM, "fill"){player, node -> - - if(player.questRepository.getStage("Nature Spirit") >= 75) { - if (ContentAPI.amountInInventory(player, Items.MORT_MYRE_FUNGUS_2970) >= 3) { - if (node.id != Items.DRUID_POUCH_2958) { - ContentAPI.removeItem(player, node, Container.INVENTORY) - } - ContentAPI.removeItem(player, Item(Items.MORT_MYRE_FUNGUS_2970, 3), Container.INVENTORY) - ContentAPI.addItem(player, Items.DRUID_POUCH_2958, 3) - } else { - ContentAPI.sendDialogue(player, "You need 3 fungus before you can do that.") - } - } else { - ContentAPI.sendDialogue(player, "I don't know how to use that yet.") - } - - return@on true - } - - onUseWith(SCENERY, Items.SILVER_SICKLE_2961, NATURE_ALTAR){player, used, with -> - ContentAPI.sendItemDialogue(player, Items.SILVER_SICKLEB_2963, "You dump the sickle into the waters.") - if(ContentAPI.removeItem(player, Items.SILVER_SICKLE_2961, Container.INVENTORY)){ - ContentAPI.addItem(player, Items.SILVER_SICKLEB_2963, 1) - } - return@onUseWith true - } - - onUseWith(NPC, DRUID_POUCH, NPCs.GHAST_1052){player, used, with -> - NSUtils.activatePouch(player, with as MortMyreGhastNPC) - } - - onUseWith(SCENERY, items, *stones) { player, used, with -> - when (used.id) { - USED_SPELLCARD -> { - if (with.id == FREELY_GIVEN_STONE) { - if(ContentAPI.removeItem(player, used, Container.INVENTORY)){ - ContentAPI.sendNPCDialogue(player, NPCs.FILLIMAN_TARLOCK_1050, "Aha, yes, that seems right well done!") - ContentAPI.sendMessage(player, "The stone seems to absorb the used spell scroll.") - NSUtils.flagCardPlaced(player) - } - } else ContentAPI.sendMessage(player, "You try to put the item on the stone, but it just moves off.") - } - - FUNGUS -> { - if (with.id == NATURE_STONE) { - if(ContentAPI.removeItem(player, used, Container.INVENTORY)){ - ContentAPI.sendNPCDialogue(player, NPCs.FILLIMAN_TARLOCK_1050, "Aha, yes, that seems right well done!") - ContentAPI.sendMessage(player, "The stone seems to absorb the used fungus.") - NSUtils.flagFungusPlaced(player) - } - } else ContentAPI.sendMessage(player, "You try to put the item on the stone, but it just moves off.") - } - } - return@onUseWith true - } - } -} - -class NSJournalDialogue : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - 0 -> dialogue(*DialUtils.splitLines("Most of the writing is pretty uninteresting, but something inside refers to a nature spirit. The requirements for which are,")).also { stage++ } - 1 -> dialogue(*DialUtils.splitLines("'Something from nature', 'something with faith' and 'something of the spirit-to-become freely given'. It's all pretty vague.")).also { stage = END_DIALOGUE } - } - } -} - -class FillimanCompletionDialogue : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - 0 -> npcl(FacialExpression.NEUTRAL, "Well, hello there again. I was just enjoying the grotto. Many thanks for your help, I couldn't have become a Spirit of nature without you.").also { stage++ } - 1 -> npcl(FacialExpression.NEUTRAL, "I must complete the transformation now. Just stand there and watch the show, apparently it's quite good!").also { stage++ } - 2 -> { - end() - ContentAPI.lock(player!!, 10) - ContentAPI.submitWorldPulse(CompleteSpellPulse(player!!)) - } - } - } -} - -class CompleteSpellPulse(val player: Player) : Pulse(2){ - var counter = 0 - val locations = arrayOf(Location.create(3444, 9740, 0), Location.create(3439, 9740, 0), Location.create(3439, 9737, 0), Location.create(3444, 9737, 0), Location.create(3444, 9735, 0), Location.create(3438, 9735, 0)) - val dest = Location.create(3441, 9738, 0) - override fun pulse(): Boolean { - when(counter++){ - 0 -> repeat(6) { ContentAPI.spawnProjectile(locations[it], dest, 268, 0, 1000, 0, 40, 20) } - 1 -> player.questRepository.getQuest("Nature Spirit").setStage(player, 60) - 2 -> player.teleport(player.location.transform(0,0,1)) - 3 -> ContentAPI.openDialogue(player, NPCs.NATURE_SPIRIT_1051, ContentAPI.findLocalNPC(player, NPCs.NATURE_SPIRIT_1051) as NPC).also { ContentAPI.unlock(player); return true } - } - return false - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSUtils.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSUtils.kt deleted file mode 100644 index c301909bd..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NSUtils.kt +++ /dev/null @@ -1,125 +0,0 @@ -package rs09.game.content.quest.members.naturespirit - -import api.Container -import api.ContentAPI -import core.game.node.Node -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.system.task.Pulse -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.map.RegionManager.forId -import core.game.world.map.RegionManager.lock -import core.game.world.update.flag.context.Graphics -import core.tools.RandomFunction -import org.rs09.consts.Items -import rs09.game.node.entity.npc.other.MortMyreGhastNPC - -object NSUtils { - fun flagFungusPlaced(player: Player) { - ContentAPI.setAttribute(player, "/save:ns:fungus_placed", true) - } - - fun flagCardPlaced(player: Player){ - ContentAPI.setAttribute(player, "/save:ns:card_placed", true) - } - - fun hasPlacedFungus(player: Player): Boolean { - return ContentAPI.getAttribute(player, "ns:fungus_placed", false) - } - - fun hasPlacedCard(player: Player): Boolean { - return ContentAPI.getAttribute(player, "ns:card_placed", false) - } - - fun onStone(player: Player): Boolean { - return player.location.equals(3440, 3335, 0) - } - - fun getGhastKC(player: Player): Int { - return ContentAPI.getAttribute(player,"ns:ghasts_killed", 0) as Int - } - - fun incrementGhastKC(player: Player){ - ContentAPI.setAttribute(player, "/save:ns:ghasts_killed", getGhastKC(player) + 1) - val msg = when(getGhastKC(player)) { - 1 -> "That's one down, two more to go." - 2 -> "Two down, only one more to go." - 3 -> "That's it! I've killed all 3 Ghasts!" - else -> "" - } - - if(!msg.isEmpty()){ - ContentAPI.sendMessage(player, msg) - } - } - - fun activatePouch(player: Player, attacker: MortMyreGhastNPC): Boolean { - var shouldAddEmptyPouch = false - val pouchAmt = ContentAPI.amountInInventory(player, Items.DRUID_POUCH_2958) - if(pouchAmt == 1) shouldAddEmptyPouch = true - if(pouchAmt > 0 && ContentAPI.removeItem(player, Items.DRUID_POUCH_2958, Container.INVENTORY)){ - if(shouldAddEmptyPouch){ - ContentAPI.addItem(player, Items.DRUID_POUCH_2957) - } - ContentAPI.spawnProjectile(player, attacker, 268) - ContentAPI.submitWorldPulse(object : Pulse(){ - var ticks = 0 - override fun pulse(): Boolean { - when(ticks++){ - 2 -> ContentAPI.visualize(attacker, -1, Graphics(269, 125)) - 3 -> attacker.transform(attacker.id + 1).also { attacker.attack(player); attacker.setAttribute("woke", ContentAPI.getWorldTicks()); return true } - } - return false - } - }) - return true - } - return false - } - - fun cleanupAttributes(player: Player){ - player.removeAttribute("ns:fungus_placed") - player.removeAttribute("ns:card_placed") - } - - @JvmStatic - fun castBloom(player: Player): Boolean{ - var success = false - val region = forId(player.location.regionId) - if (player.skills.prayerPoints < 1) { - player.packetDispatch.sendMessage("You don't have enough prayer points to do this.") - return false - } - handleVisuals(player) - val locs = player.location.surroundingTiles - for (o in locs) { - val obj = RegionManager.getObject(o) - if (obj != null) { - if (obj.name.equals("Rotting log", ignoreCase = true) && player.skills.prayerPoints >= 1) { - if (player.location.withinDistance(obj.location, 2)) { - SceneryBuilder.replace(obj, obj.transform(3509)) - success = true - } - } - } - } - return success - } - - /** - * Handles the draining of prayer points and physical graphics and - * animation. - */ - private fun handleVisuals(player: Player) { - player.skills.decrementPrayerPoints(RandomFunction.random(1, 3).toDouble()) - val AROUND_YOU = player.location.surroundingTiles - for (location in AROUND_YOU) { - // The graphic is meant to play on a 3x3 radius around you, but not - // including the tile you are on. - player.packetDispatch.sendGlobalPositionGraphic(263, location) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritQuest.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritQuest.kt deleted file mode 100644 index 05ac35cbc..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/naturespirit/NatureSpiritQuest.kt +++ /dev/null @@ -1,122 +0,0 @@ -package rs09.game.content.quest.members.naturespirit - -import api.ContentAPI -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.quest.Quest -import core.game.node.entity.skill.Skills -import core.plugin.Initializable -import org.rs09.consts.Items - -@Initializable -class NatureSpiritQuest : Quest("Nature Spirit", 95, 94, 2, 307, 0, 1, 110 ) { - override fun newInstance(`object`: Any?): Quest { - return this - } - - override fun drawJournal(player: Player?, stage: Int) { - super.drawJournal(player, stage) - player ?: return - var line = 11 - if(stage == 0){ - line(player, "I can start this quest by speaking to !!Drezel?? in the !!temple of Saradomin??.", line++) - } else { - if(stage >= 10){ - line(player, "After talking to Drezel in the temple of Saradomin I've",line++, true) - line(player,"agreed to look for a Druid called Filliman Tarlock.", line++, true) - } - - if(stage == 10){ - line(player, "I need to look for !!Filliman Tarlock?? in the !!Swamps?? of Mort",line++) - line(player, "Myre. I should be wary of !!Ghasts??.", line++) - } - - if(stage == 15){ - line(player, "I located a !!spirit?? in the swamp. I believe he's", line++, false) - line(player, "!!Filliman Tarlock?? but I can't understand him.",line++, false) - } - - if(stage == 20){ - line(player, "I located !!Filliman Tarlock?? in the swamp. I believe he's",line++) - line(player, "dead but he doesn't believe me. I need to convince him.", line++) - } - - if(stage >= 25){ - line(player, "I located Filliman Tarlock in the swamp and managed to",line++,true) - line(player, "convince him that he is in fact a ghost. ", line++, true) - } - - if(stage == 25){ - line(player, "Filliman needs his !!journal?? to figure out what to do",line++) - line(player, "next. He mentioned something about a !!knot??.", line++) - } - - if(stage >= 30){ - line(player, "I recovered Filliman's journal for him.", line++, true) - } - - if(stage == 30) { - line(player, "I should speak to !!Filliman Tarlock?? to see what I can",line++) - line(player, "do to help.", line++) - } - - if(stage >= 40){ - line(player, "I've gone and gotten blessed by Drezel.", line++, true) - } - - if(stage >= 35) { - line(player, "I've agreed to help Filliman become a Nature Spirit.",line++, true) - } - - if(stage == 35){ - line(player, "The first thing Filliman needs me to do is go and get",line++) - line(player, "blessed by !!Drezel?? in the temple of Saradomin.",line++) - } - - if (stage == 40){ - line(player, "I should return to !!Filliman?? to see what I need to do.", line++, false) - } - - if(stage in 45 until 55){ - line(player, "In order to help Filliman I need to find 3 things:", line++, false) - line(player, "Something of !!Faith??.",line++, false) - line(player, "Something of !!Nature??.", line++, stage >= 50) - line(player, "Something of the !!spirit-to-be freely given??.", line++, false) - } - - if(stage == 50){ - line(player, "I know for a fact the fungus is !!something of Nature??.", line++, false) - } - - if(stage >= 55){ - line(player, "I've helped Filliman complete the spell.", line++, true) - } - - if(stage == 55){ - line(player, "Filliman has asked me to meet him back inside the !!grotto??.", line++, false) - } - - if(stage == 75){ - line(player, "I need to go and kill !!3 Ghasts?? for Filliman.", line++, false) - } - - if(stage >= 100){ - line(player,"!!QUEST COMPLETE!??",line++) - } - } - } - - override fun finish(player: Player?) { - super.finish(player) - player ?: return - var ln = 10 - player.packetDispatch.sendItemZoomOnInterface(Items.SILVER_SICKLEB_2963,230,277,5) - drawReward(player, "2 Quest Points", ln++) - drawReward(player, "3,000 Crafting XP",ln++) - drawReward(player, "2,000 Hitpoints XP", ln++) - drawReward(player, "2,000 Defence XP", ln++) - ContentAPI.rewardXP(player, Skills.CRAFTING, 3000.0) - ContentAPI.rewardXP(player, Skills.HITPOINTS, 2000.0) - ContentAPI.rewardXP(player, Skills.DEFENCE, 2000.0) - NSUtils.cleanupAttributes(player) - } -} diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/AskeladdenDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/AskeladdenDialogue.kt deleted file mode 100644 index 0229a2486..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/AskeladdenDialogue.kt +++ /dev/null @@ -1,57 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.node.entity.player.Player -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression - -//@InitializablePlugin -//Disabled because the quest isn't done yet. -class AskeladdenDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun open(vararg args: Any?): Boolean { - player?.let { - if (it.questRepository.getStage("Fremennik Trials") > 0) { - player("Hello there.").also { stage = 0; return true } - } - if(it.getAttribute("fremtrials:lalli-talkedto",false)!!){ - player("Hello there. I understand you managed to get some", "golden wool from Lalli?") - stage = 0 - return true - } - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return AskeladdenDialogue(player) - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (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(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.setAttribute("/save:fremtrials:askeladden-talkedto",true); - stage++} - //Give player ROCK - - - 1000 -> end() - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(2439) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ChieftanBrundtDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ChieftanBrundtDialogue.kt deleted file mode 100644 index 2da22cb59..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ChieftanBrundtDialogue.kt +++ /dev/null @@ -1,78 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.node.entity.player.Player -import core.game.node.entity.player.info.PlayerDetails -import core.plugin.Initializable -import core.game.content.dialogue.DialoguePlugin - -@Initializable -class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ - override fun open(vararg args: Any?): Boolean { - if(player?.questRepository?.getStage("Fremennik Trials")!! == 0) { - npc("Greetings outlander!") - stage = 0 - } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - //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 -> TODO("Not yet implemented") - 2 -> TODO("Not yet implemented") - 3 -> {player("Do you have any quests?");stage = 300} - 4 -> TODO("Not yet implemented") - } - - //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++} - 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){ - 1 -> {player("Actually, I would be very interested to hear what you","have to offer.");stage = 310} - 2 -> {player("No, I'm not interested.");stage = 340} - } - - //Yes, I am interested in what you have to say. - 310 -> {npc("You would? These are unusual sentiments to hear from","an outerlander! My suggestion was going to be that if","you crave adventure and battle,"); stage++} - 311 -> {npc("and your heart sings for glory, then perhaps you would","be interested in joining our clan, and becoming a","Fremennik yourself?");stage++} - 312 -> {player("What would that involve exactly?");stage++} - 313 -> {npc("Well, there are two ways to become a member of our","clan and call yourself a Fremennik: be born a","Fremennik, or be voted in by our council of elders.");stage++} - 314 -> {player("Well, I think I've missed the first way, but how can I","get the council of elders to vote to let me join your","clan?");stage++} - 315 -> {npc("Well, that I cannot answer myself. You will need to","speak to each of them and see what they require of you","as proof of your dedication.");stage++} - 316 -> {npc("There are twelve council members around this village;","you will need to gain a majority vote of at least seven","councillors in your favor.");stage++} - 317 -> {npc("So, what say you? Give me the word and I will tell all","of my tribe of your intentions, be they yes or nay.");stage++} - 318 -> {options("I want to become a Fremennik!","I don't want to become a Fremennik.");stage++} - 319 -> when(buttonId){ - 1 -> {player("I think I would enjoy the challenge of becoming an","honorary Fremennik. Where and how do I start?");stage = 320} - 2 -> {player("That sounds too complicated to me.");stage = 322} - } - - //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)} - - //That sounds too complicated for me. - 322 -> {npc("Well, that's what I expect from an outerlander.");stage = 1000} - - //No, I'm not interested - 340 -> TODO("Not implemented yet") - - - 1000 -> end() - } - return true - } - - - override fun newInstance(player: Player?): DialoguePlugin { - return ChieftanBrundt(player) - } - - override fun getIds(): IntArray { - return intArrayOf(1294) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/CouncilWorkerFTDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/CouncilWorkerFTDialogue.kt deleted file mode 100644 index d9502019d..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/CouncilWorkerFTDialogue.kt +++ /dev/null @@ -1,42 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE - -class CouncilWorkerFTDialogue(val questStage: Int,var isBeerInteraction: Boolean = false) : DialogueFile(){ - - override fun handle(componentID: Int, buttonID: Int) { - - if(isBeerInteraction){ - when(stage){ - START_DIALOGUE -> {npc("Oh, thank you much ${if(player?.appearance?.isMale!!) "sir" else "miss"}");stage++} - 1 -> {npc("Ta very much like. That'll hit the spot nicely.. Here,","You can have this. I picked it up as a souvenir on me","last holz");player?.inventory?.add( - Item(3713) - ); player?.inventory?.remove(Item(1917)); stage = END_DIALOGUE} - } - } - - else if(questStage in 1..99){ - when(stage){ - START_DIALOGUE -> if(player?.questRepository?.getStage("Fremennik Trials")!! > 0){ - player("I know this is an odd question, but are you","a member of the elder council?") - } else { - end() - } - 1 -> {npc("'fraid not, ${if(player?.appearance?.isMale!!) "sir" else "miss"}"); stage++} - 2 -> {npc("Say, would you do me a favor? I'm quite parched.","If you bring me a beer, I'll make it worthwhile.");stage++} - 3 -> if(player?.inventory?.containsItem(Item(1917))!!){ - player("Oh, I have one here! Take it.") - stage = 0 - isBeerInteraction = true - } else { - stage = END_DIALOGUE - } - } - } - - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/Draugen.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/Draugen.kt deleted file mode 100644 index 35612e108..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/Draugen.kt +++ /dev/null @@ -1,29 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -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 - -class Draugen(val player: Player?) : NPC(1279,player?.location?.transform(1,0,0)){ - - override fun init() { - super.init() - isRespawn = false - } - - override fun handleTickActions() { - super.handleTickActions() - if(!properties.combatPulse.isAttacking){ - properties.combatPulse.attack(player) - } - } - - override fun finalizeDeath(killer: Entity?) { - super.finalizeDeath(killer) - player?.setAttribute("/save:fremtrials:draugen-killed",true) - player?.removeAttribute("fremtrials:draugen-loc") - player?.inventory?.remove(Item(3696)) - player?.inventory?.add(Item(3697)) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/FremennikTrials.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/FremennikTrials.kt deleted file mode 100644 index b0ea9d5a8..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/FremennikTrials.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* -package core.game.content.quest.fremtrials - -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 - -@Initializable -class FremennikTrials : Quest("Fremennik Trials",64,63,3,347,0,1,10){ - - class SkillRequirement(val skill: Int?, val level: Int?) - - val requirements = arrayListOf() - - override fun drawJournal(player: Player?, stage: Int) { - super.drawJournal(player, stage) - var line = 11 - val started = player?.questRepository?.getStage("Fremennik Trials")!! > 0 - - if(!started){ - line(player,"Requirements to complete quest:",line++) - line += 1 - line(player,"Level 40 Woodcutting",line++, player.skills.getStaticLevel(Skills.WOODCUTTING) >= 40) - line(player,"Level 40 Crafting",line++,player.skills.getStaticLevel(Skills.CRAFTING) >= 40) - line(player,"Level 25 Fletching",line++,player.skills.getStaticLevel(Skills.FLETCHING) >= 25) - line(player,"I must also be able to defeat a !!level 69 enemy?? and must",line++) - line(player,"not be afraid of !!combat without any weapons or armour.",line++) - line += 1 - line(player,"I can start this quest by speaking to !!Chieftan Brundt?? on",line++) - line(player,"the !!Fremennik Longhall,?? which is in the town of !!Rellekka?? to",line++) - line(player,"the north of !!Sinclair Mansion??.",line) - } else { - line(player,"In order to join the Fremenniks, I need to",line++) - line(player,"!!earn the approval?? of !!7 members?? of the elder council.",line++) - line(player,"I've written down the members who I can try to help:",line++) - line(player,"Manni the Reveller",line++,player.getAttribute("fremtrials:manni-vote",false)) - line(player,"Swensen the Navigator",line++,player.getAttribute("fremtrials:swensen-vote",false)) - line(player,"Sigli the Huntsman",line++,player.getAttribute("fremtrials:sigli-vote",false)) - line(player,"Olaf the Bard",line++,player.getAttribute("fremtrials:olaf-vote",false)) - line(player,"So far I have gotten ${player.getAttribute("fremtrials:votes",0)} votes.",line++) - } - } - - override fun newInstance(`object`: Any?): Quest { - requirements.add(SkillRequirement(Skills.FLETCHING,25)) - requirements.add(SkillRequirement(Skills.CRAFTING,40)) - requirements.add(SkillRequirement(Skills.WOODCUTTING,40)) - return this - } - -}*/ diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/HunterTalismanListener.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/HunterTalismanListener.kt deleted file mode 100644 index a52a0d5ec..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/HunterTalismanListener.kt +++ /dev/null @@ -1,101 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.node.entity.Entity -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.map.Location -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld.Pulser -import kotlin.math.abs -import kotlin.math.atan2 - -class HunterTalismanListener : InteractionListener() { - - val TALISMAN = Items.HUNTERS_TALISMAN_3696 - - override fun defineListeners() { - - on(TALISMAN,ITEM,"locate"){player,_ -> - var locationString = player.getAttribute("fremtrials:draugen-loc","none") - if(locationString == "none"){ - val newLoc = possibleLocations.random() - player.setAttribute("/save:fremtrials:draugen-loc","${newLoc.x},${newLoc.y}") - locationString = "${newLoc.x},${newLoc.y}" - } - val locationComponents = locationString?.split(",") - val draugenLoc = Location(Integer.parseInt(locationComponents?.get(0)),Integer.parseInt(locationComponents?.get(1))) - - if(player.location?.withinDistance(draugenLoc,5)!!){ - player.dialogueInterpreter.sendDialogue("The Draugen is nearby, be careful!") - Pulser.submit(DraugenPulse(player)) - } else { - val neededDirection = draugenLoc.getDirection(player as Entity) - player.sendMessage("The talisman pulls you to the $neededDirection") - } - return@on true - } - - } - - class DraugenPulse(val player: Player?) : Pulse(){ - var count = 0 - override fun pulse(): Boolean { - when(count++){ - 3 -> Draugen(player).init().also { return true } - } - return false - } - } - - val possibleLocations = listOf(Location(2625,3608), - Location(2602,3628), - Location(2668,3714), - Location(2711,3602), - Location(2664,3592)) - - - fun Location.getDirection(entity: Entity): String{ - val loc: Location = this - val difX: Double = (loc.x - entity.location.x).toDouble() - val difY: Double = (loc.y - entity.location.y).toDouble() - val angle = Math.toDegrees(atan2(difX,difY)) - val NORTH = 0.toDouble() - val SOUTH = 180.toDouble() - val EAST = (-90).toDouble() - val WEST = 90.toDouble() - val NORTHEAST = (-135).toDouble() - val NORTHWEST = 135.toDouble() - val SOUTHEAST = (-45).toDouble() - val SOUTHWEST = 45.toDouble() - if(diff(angle,NORTH) < 3){ - return "north" - } - if(diff(angle,SOUTH) < 3){ - return "south" - } - if(diff(angle,EAST) < 3){ - return "west" - } - if(diff(angle,WEST) < 3){ - return "east" - } - if(diff(angle,SOUTHEAST) < 45){ - return "north-west" - } - if(diff(angle,SOUTHWEST) < 45){ - return "north-east" - } - if(diff(angle,NORTHEAST) < 45){ - return "south-west" - } - if(diff(angle,NORTHWEST) < 45){ - return "south-east" - } - return "Dunno. ${angle}" - } - - fun diff(x: Double,y: Double): Double{ - return abs(x - y) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/LalliDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/LalliDialogue.kt deleted file mode 100644 index 0e77e3458..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/LalliDialogue.kt +++ /dev/null @@ -1,73 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.node.entity.player.Player -import core.plugin.Initializable -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression - -@Initializable -class LalliDialogue(player: Player? = null) : DialoguePlugin(player){ - override fun open(vararg args: Any?): Boolean { - player?.let { - if (it.questRepository.getStage("Fremennik Trials") > 0) { - player("Hello there.").also { stage = 0; return true } - } - if (it.getAttribute("fremtrials:askeladden-talkedto", false)!!) { - player("Hello there.") - stage = 50 - return true - } - } - return true - } - - - - override fun newInstance(player: Player?): DialoguePlugin { - return LalliDialogue(player) - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(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(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 } - 2 -> player("No, honest, you're REALLY clever!").also { stage = 20 } - 3 -> player("Can I give you a present.").also { stage = 30 } - } - - //Other human? - 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++ } - - //Honest, you're clever - 20 -> npc(FacialExpression.OLD_SNEAKY,"Me no believe you tell truth. Go away.").also { stage = 1000 } - - //Can I give you a present - 30 -> npc(FacialExpression.OLD_SNEAKY,"Me think about that.").also { stage = 1000 } - - 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(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 } - - - 1000 -> end() - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(1270) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ManniDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ManniDialogue.kt deleted file mode 100644 index c20afe869..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ManniDialogue.kt +++ /dev/null @@ -1,120 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -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.info.PlayerDetails -import core.game.node.item.Item -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import core.plugin.Initializable -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression - -@Initializable -class ManniDialogue(player: Player? = null) : 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?.getAttribute("fremtrials:manni-accepted",false) == true) { - if (player?.inventory?.containsItem(Item(3712)) == true || player?.inventory?.containsItem(Item(3711)) == true) { - npc("Ah, I see you have your keg of beer. Are ye ready to", "drink against each other?") - stage = 101 - return true - } else { - npc("Come back when you're ready to begin", "the contest.") - stage = 1000; - return true - } - } else if(player?.getAttribute("fremtrials:manni-vote",false) == true){ - npc("e have my vote!") - stage = 1000 - return true - } - player("Hello there!") - stage = 0 - return true - } else { - TODO("Uncertain what should go here") - } - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> {npc("Hello outerlander. I overheard your conversation with","Brundt just now. You wish to become a member of the","Fremennik?");stage++} - 1 -> {player("That's right! Why, are you on the counsel?");stage++} - 2 -> {npc("Do not let my drink-ssussed appearance fool you, I","earnt my place on the council many years past.I am","always glad to see new blood enter our tribe, and will","happily vote for you.");stage++} - 3 -> {player("Great!");stage++} - 4 -> {npc("Providing you can pass a little test for me. As a","Fremennik, you will need to show cunning, stamina,","fastitude, and an iron constitution. I know of only one","way to test all of these.");stage++} - 5 -> {player("And what's that?");stage++} - 6 -> {npc("Why, a drinking contest!");stage++} - 7 -> {npc("The task is simple enough! You versus me, a stiff drink","each, last man standing wins the trial. So what say you?");stage++} - 8 -> {options("Yes","No");stage++} - 9 -> when(buttonId){ - 1 -> {player("A drinking contest? Easy. Set them up, and I'll knock","them back.");stage++} - 2 -> {player("I don't like the sound of that.");stage = 12} - } - - //Yes - 10 -> {npc("When you are ready to begin, go and pick up a keg","from that table over there, and come back here.");stage++} - 11 -> {npc("We start when you have your keg of beer with you","and finish when one of us can drink no more and","yields.");stage = 1000;player?.setAttribute("/save:fremtrials:manni-accepted",true)} - - //No - 12 -> {npc("That's a shame."); stage = 1000} - - - //Begin drinking competition - 101 -> {options("Yes","No");stage++} - 102 -> when(buttonId){ - 1 -> {player("Yes, let's start this drinking contest!");stage++} - 2 -> {player("No, I don't think I am.");stage = 1000} - } - 103 -> {npc("As you wish outerlander; I will drink first, then you will","drink.");stage++} - 104 -> {GameWorld.submit(DrinkingPulse(player,curNPC,player?.getAttribute("fremtrials:keg-mixed",false))); end()} - - 1000 -> end() - } - return true - } - - class DrinkingPulse(val player: Player?,val npc: NPC?, val lowAlcohol: Boolean? = false) : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - if(!lowAlcohol!!){ - when(counter++){ - 0 -> {player?.lock();npc?.lock(); npc?.isNeverWalks = true} - 1 -> {player?.face(npc); npc?.face(player)} - 3 -> npc?.animator?.animate(Animation(1330,Animator.Priority.HIGH)) - 5 -> {player?.animator?.animate(Animation(1330,Animator.Priority.HIGH)); player?.inventory?.remove(Item(3711))} - 7 -> player?.dialogueInterpreter?.sendDialogues(player,FacialExpression.DRUNK,"Ish no fair!! I canna drink another drop! I alsho","feel veddy, veddy ill...") - 15 -> player?.dialogueInterpreter?.sendDialogues(npc,FacialExpression.DRUNK,"I guessh I win then ouddaladder! (hic) Niche try,","anyway!") - 16 -> {player?.unlock(); npc?.unlock(); player?.face(player);npc?.face(npc); npc?.isNeverWalks = false; return true} - } - } else { - when(counter++){ - 0 -> {player?.lock();npc?.lock(); npc?.isNeverWalks = true} - 1 -> {player?.face(npc); npc?.face(player)} - 3 -> npc?.animator?.animate(Animation(1330,Animator.Priority.HIGH)) - 5 -> {player?.animator?.animate(Animation(1330,Animator.Priority.HIGH)); player?.inventory?.remove(Item(3711))} - 7 -> player?.dialogueInterpreter?.sendDialogues(player,FacialExpression.HAPPY,"Aaaah, lovely stuff. So you want to get the next round","in, or shall I? You don't look so good there!") - 15 -> player?.dialogueInterpreter?.sendDialogues(npc,FacialExpression.DRUNK,"Wassha? Guh? You drank that whole keg! But it dinnna","affect you at all! I conshede! You can probably","outdrink me!") - 21 -> player?.dialogueInterpreter?.sendDialogues(npc,FacialExpression.DRUNK,"I jusht can't (hic) believe it! Thatsh shome might fine","drinking legs you got! Anyone who can drink like","THAT getsh my vote atta somsh.... coumah... gets my","vote!") - 22 -> {player?.unlock(); npc?.unlock(); player?.face(player);npc?.face(npc); npc?.isNeverWalks = false;player?.removeAttribute("fremtrials:cherrybomb");player?.removeAttribute("fremtrials:manni-accepted");player?.removeAttribute("fremtrials:keg-mixed"); player?.setAttribute("/save:fremtrials:manni-vote",true); player?.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1); return true} - } - } - return false - } - } - - override fun newInstance(player: Player?): DialoguePlugin { - return ManniDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(1286) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/OlafTheBard.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/OlafTheBard.kt deleted file mode 100644 index 2fb7d7a21..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/OlafTheBard.kt +++ /dev/null @@ -1,70 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.plugin.Initializable - -@Initializable -class OlafTheBard(player: Player? = null) : DialoguePlugin(player){ - override fun open(vararg args: Any?): Boolean { - if(player?.getAttribute("fremtrials:olaf-accepted",false)!!){ - npc("I can't wait to see your performance.") - stage = 1000 - return true - } - npc("Hello? Yes? You want something outerlander?") - stage = 0; - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> player("Are you a member of the council?").also { stage++ } - 1 -> npc("Why, indeed I am, outerlander! My talents as an","exemplary musician made it difficult for them not to","accept me! Why do you wish to know this?").also { stage++ } - 2 -> player("Well, I ask because I am currently doing the","Fremennik trials so as to join you clan. I need seven","of the twelve council of elders to vote for me.").also { stage++ } - 3 -> npc("Ahhh... and you wish to earn my vote? I will gladly","accept you as a Fremennik should you be able to prove","yourself to have a little musical ability!").also { stage++ } - 4 -> player("So how would I do that?").also { stage++ } - 5 -> npc("Why, by playing in our longhall! All you need to do is","impress the revellers there with a few verses of an epic","of your own creation!").also { stage++ } - 6 -> npc("So what say you outerlander? Are you up for the","challenge?").also { stage++ } - 7 -> options("Yes","No").also { stage++ } - 8 -> when(buttonId){ - 1 -> player("Sure! This certainly sounds pretty easy to accomplish.","I'll have your vote in no time!").also { stage++ } - 2 -> player("No, that sounds like too much work.").also { stage = 1000 } - } - - //Yes - 9 -> npc("That is great news outerlander! We always need more","music lovers here!").also { stage++;player?.setAttribute("/save:fremtrials:olaf-accepted",true) } - 10 -> player("So how would I go about writing this epic?").also { stage++ } - 11 -> npc("Well, first of all you are going to need an instrument.","Like all true bards you are going to have to make this","yourself.").also { stage++ } - 12 -> player("How do I make an instrument?").also { stage++ } - 13 -> npc("Well, it is a long and drawn-out process. Just east of","this village there is an unusually musical tree that can","be used to make very high quality instruments.").also { stage++ } - 14 -> npc("Cut a piece from it, and then carve it into a special","shape that will allow you to string it. Using a knife as","you would craft any other wooden object would be best","for this.").also { stage++ } - 15 -> player("Then what do I need to do?").also { stage++ } - 16 -> npc("Next you will need to string your lyre. There is a troll","to the South-east who has some golden wool. I would","not recommend using anything else to string your lyre","with.").also { stage++ } - 17 -> player("Anything else?").also { stage++ } - 18 -> npc("Well, when you have crafted your lyre you will need","the blessing of the Fossegrimen to tune your lyre to","perfection before you even consider a public","performance.").also { stage++ } - 19 -> player("Who or what is Fossegrimen?").also { stage++ } - 20 -> npc("Fossegrimen is a lake spirit that lives just a little way","South-west of this village. Make her an offering of fish,","and you will then be ready for your performance.").also { stage++ } - 21 -> npc("Make sure you give her a suitable offering however. If","the offering is found to be unworthy, then you may","find yourself unable to play your lyre with any skill at","all!").also { stage++ } - 22 -> player("So what would be a worthy offering?").also { stage++ } - 23 -> npc("A raw shark, manta ray, or sea turtle should be","sufficient as an offering.").also { stage++ } - 24 -> player("Ok, what else do I need to do?").also { stage++ } - 25 -> npc("When you have crafted your lyre and been blessed by","Fossegrimen, then you will finally be ready to make","your performance to the revellers at the longhall.").also { stage++ } - 26 -> npc("Head past the bouncers and onto the stage, and then","begin to play. If all goes well, you should find the music","spring to your mind and sing your own epic on the","spot").also { stage++ } - 27 -> npc("I will observe both you and the audience, and if you","show enough talent, I will happily vote in your favour at","the council of elders.").also { stage++ } - 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 } - - 1000 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return OlafTheBard(player) - } - - override fun getIds(): IntArray { - return intArrayOf(1269) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/PoisonSalesman.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/PoisonSalesman.kt deleted file mode 100644 index d2166607d..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/PoisonSalesman.kt +++ /dev/null @@ -1,101 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.plugin.Initializable -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE - -@Initializable -class PoisonSalesman(player: Player? = null) : DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - options("Talk about the Murder Mystery Quest","Talk about the Fremennik Trials") - 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") - - when (stage) { - START_DIALOGUE -> when (buttonId) { - 1 -> { player("Err... nevermind"); stage = END_DIALOGUE } - 2 -> { player("Hello."); stage = 10 } - } - - //Fremennik Trials - 10 -> { - when (fremennikTrialsStage) { - 0 -> { npc("Come see me if you ever need low-alcohol beer!"); stage = END_DIALOGUE } - } - } -// if (fremennikTrialsStage == 0) -// else if(fremennikTrialsStage > 30) { npc("Thanks for buying out all that low-alcohol beer!"); stage = END_DIALOGUE } -// else if(fremennikTrialsStage > 0) { npc("Howdy! You seem like someone with discerning taste!","Howsabout you try my brand new range of alcohol?"); stage++ } - - 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++ } - 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++ } - 16 -> { npc("Well I would, but I ran out of stock. Business wasn't","helped with that stuff that happened up at the Sinclair","Mansion much either, I'll be honest."); stage++ } - 17 -> { npc("So, being the man of enterprise that I am I decided to","branch out a little bit!"); stage++ } - 18 -> { player("Into alcohol?"); stage++ } - 19 -> { npc("Absolutely! The basic premise between alcohol and poison","is pretty much the same, after all! The difference is that","my alcohol has a unique property others do not!"); stage++ } - 20 -> { player("And what is that?"); stage++ } - 21 -> { sendDialogue("The salesman takes a deep breath."); stage++ } - 22 -> { npc("Ever been too drunk to find your own home? Ever","wished that you could party away all night long, and","still wake up fresh as a daisy the next morning?"); stage++ } - 23 -> { npc("Thanks to the miracles of modern magic we have come","up with just the solution you need! Peter Potter's","Patented Party Potions!"); stage++ } - 24 -> { npc("It looks just like beer! It tastes just like beer! It smells","just like beer! But... it's not beer!"); stage++ } - 25 -> { npc("Our mages have mused for many moments to bring","you this miracle of modern magic! It has all the great","tastes you'd expect, but contains absolutely no alcohol!"); stage++ } - 26 -> { npc("That's right! You can drink Peter Potter's Patented","Party Potion as much as you want, and suffer","absolutely no ill effects whatsoever!"); stage++ } - 27 -> { npc("The clean fresh taste you know you can trust, from","the people who brought you: Peter Potters Patented","multipurpose poison, Pete Potters peculiar paint packs"); stage++ } - 28 -> { npc("and Peter Potters paralyzing panic pins. Available now","from all good stockists! Ask your local bartender now,","and experience the taste revolution of the century!"); stage++ } - 29 -> { sendDialogue("He seems to have finished for the time being."); stage++ } - 30 -> { player("So.. when you say 'all good stockists'..."); stage++ } - 31 -> { npc("Yes?"); stage++ } - 32 -> { player("How many inns actually sell this stuff?"); stage++ } - 33 -> { npc("Well.. nobody has actually bought any yet. Everyone I","try and sell it to always asks me what exactly the point","of beer that has absolutely no effect on you is."); stage++ } - 34 -> { player("So what is the point?"); stage++ } - 35 -> { npc("Well... Um... Er... Hmmm. You, er, don't get drunk."); stage++ } - 36 -> { player("I see..."); stage++ } - 37 -> { npc("Aw man.. you don't want any now do you? I've really","tried to push this product, but I just don't think the","world is ready for beer that doesn't get you drunk."); stage++ } - 38 -> { npc("I'm a man ahead of my time I tell you! It's not that","my products are bad, it's that they're too good for the","market!"); stage++ } - 39 -> { player("Actually, I would like some. How much do you want for it?"); stage++ } - 40 -> { npc("Y-you would??? Um, okay! I knew I still had the old","salesmans skills going on!"); stage++ } - 41 -> { npc("I will sell you a keg of it for only 250 gold pieces! So","What do you say?"); stage++ } - 42 -> { options("Yes","No"); stage++ } - 43 -> when(buttonId){ - 1 -> { player("Yes please!"); stage++ } - 2 -> { player("No, thanks."); stage = END_DIALOGUE } - } - //Yes - 44 -> if(player?.inventory?.containsItem(Item(995,250)) == true){ - player?.inventory?.remove(Item(995,250)) - player?.inventory?.add(Item(3712)) - npc("Here you go.") - stage = END_DIALOGUE - } else { - player("I don't seem to have enough.") - stage++ - } - 45 -> { npc("Well come back when you do!"); stage = END_DIALOGUE } - - - END_DIALOGUE -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return PoisonSalesman(player) - } - - override fun getIds(): IntArray { - return intArrayOf(820) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SigliTheHuntsman.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SigliTheHuntsman.kt deleted file mode 100644 index 57bf7d8ef..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SigliTheHuntsman.kt +++ /dev/null @@ -1,88 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -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.content.dialogue.DialoguePlugin - -@Initializable -class SigliTheHuntsman(player: Player? = null) : DialoguePlugin(player){ - override fun open(vararg args: Any?): Boolean { - if(player?.getAttribute("fremtrials:sigli-vote",false)!!){ - npc("You have my vote!") - stage = 1000 - return true - } - if(player?.getAttribute("fremtrials:draugen-killed",false)!!){ - npc("I saw the entire hunt. Let me take that talisman from","you, I would be honored to speak out for you to our","council of elders after such a hunt, outerlander.") - stage = 100 - return true - } - npc("What do you want outerlander?") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> player("Are you a member of the council?").also{stage++} - 1 -> npc("The Fremennik council of elders? I am pleased to say","that I am. My value as a huntsman is recognized by","my position there.").also { stage++ } - 2 -> player("I was wondering if I could persuade you to vouch for","me as a member of your clan?").also { stage++ } - 3 -> npc("You? ... well... I am not totally against the idea","outerlander. If you can demonstrate some hunting skills","then perhaps I may offer my vote.").also { stage++ } - 4 -> player("How can I prove my hunting skills to you? I can go","kill, like, a hundred chickens for you right now!").also { stage++ } - 5 -> npc("Chickens? You think that would impress me?").also { stage++ } - 6 -> player("Cows then? I can kill cows until, er, the cows come","home.").also { stage++ } - 7 -> npc("No. The prey I have in mind for you to prove your","worth to me is something far more dangerous. Far","more difficult. Far more deadly.").also { stage++ } - 8 -> player("Not... Giant Rats?!?!").also { stage++ } - 9 -> npc("I suspect you are mocking me outerlander. You will","need to prove your skill as a hunter to me by tracking","and defeating... The Draugen.").also { stage++ } - 10 -> options("What's a Draugen?","Forget it.").also { stage++ } - 11 -> when(buttonId){ - 1 -> player("What's a Draugen? Some kind of cheap rip-","off of a dragon?").also { stage++ } - 2 -> player("Forget it.").also { stage = 1000 } - } - - //What's a Draugen - 12 -> npc("Hmmm.. No, the words are slightly similar I suppose,","but they are very different creatures.").also { stage++ } - 13 -> npc("The Draugen is an evil ghost from Fremennik","mythology, that devours souls of those brave","warriors who meet their ends at sea.").also { stage++ } - 14 -> npc("It stalks the coastlines, invisible to all. It brings us bad","fortunes, and curses our journeys across the seas. It is","also unkillable by normal means.").also { stage++ } - 15 -> player("...Let me get this straight; You want me to hunt an","unkillable, invincible, and invisible enemy? How am I","supposed to do that?").also { stage++ } - 16 -> npc("Well outerlander, should you accept my challenge I will","show you a special hunter's trick that will help you. Do","you accept the challenge?").also { stage++ } - 17 -> options("Yes","No").also { stage++ } - 18 -> when(buttonId){ - 1 -> player("Well, I need every vote I can get in the council of","elders, but this certainly sounds impossible to do...").also { stage++ } - 2 -> player("No, I don't think I will.").also { stage = 1000 } - } - - //Yes - 19 -> npc("Not at all outerlander. The Draugen is indeed","impossible to kill but that is not the same as being","impossible to fight against.").also { stage++ } - 20 -> npc("Every time he takes a Fremennik life, he gains in","power, so to keep it from becoming too powerful we","hunters hunt it and steal its life force.").also { stage++ } - 21 -> npc("We do this with a special talisman. Here, take it, it will","let you track the Draugen while it's invisible, and when","you defeat it will absorb its essence.").also { stage++; player?.inventory?.add(Item(3696)) } - 22 -> npc("I want you to track the Draugen, defeat it, and store","its essence in that talisman for me. If you can do this","important task for my clan, I will vote for you.").also { stage++ } - 23 -> npc("Take care of the talisman, and see me when you have","completed this task.").also{stage = 1000; player?.setAttribute("/save:fremtrials:sigli-accepted",true)} - - //Draugen killed - 100 -> player("Thanks!").also { - player.removeAttribute("fremtrials:draugen-killed"); - player.setAttribute("fremtrials:sigli-vote",true) - player?.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1) - player?.inventory?.remove(Item(3697)) - stage = 1000 - } - - 1000 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return SigliTheHuntsman(player) - } - - - - override fun getIds(): IntArray { - return intArrayOf(1281) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SwensenTheNavigator.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SwensenTheNavigator.kt deleted file mode 100644 index 8c8c33f94..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SwensenTheNavigator.kt +++ /dev/null @@ -1,72 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.node.entity.player.Player -import core.game.node.entity.player.info.PlayerDetails -import core.plugin.Initializable -import core.game.content.dialogue.DialoguePlugin - -@Initializable -class SwensenTheNavigator(player: Player? = null) : DialoguePlugin(player){ - override fun open(vararg args: Any?): Boolean { - if(player?.getAttribute("fremtrials:maze-complete",false) == true){ - npc("Outerlander! You have finished my maze!","I am genuinely impressed!") - stage = 100 - return true - } else if(player?.getAttribute("fremtrials:swensen-vote",false) == true){ - npc("You have my vote!") - stage = 1000 - return true - } - player("Hello!") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> {player("I am trying to become a member of the Fremennik","clan! The Chieftan told me that I may be able to gain","your vote at the council of elders?");stage++} - 1 -> {npc("You wish to stop being an outerlander? I can","understand that! I have no reason why I would prevent","you becoming a Fremennik...");stage++} - 2 -> {npc("...but you must first pass a little test for me to prove","you are worthy.");stage++} - 3 -> {player("What kind of test?");stage++} - 4 -> {npc("Well, I serve our clan as a navigator. The seas can be","a fearful place when you know not where you are","heading.");stage++} - 5 -> {npc("Should something happen to me, all members of our","tribe have some basic sense of direction so that they","may always return safely home.");stage++} - 6 -> {npc("If you are able to demonstrate to me that you too have","a good sense of direction then I will recommend you to","the rest of the council of elders immediately.");stage++} - 7 -> {player("Well, how would I go about showing that?");stage++} - 8 -> {npc("Ah, a simple task! Below this building I have constructed","a maze; should you be able to walk from one side to the","other that will be proof to me.");stage++} - 9 -> {npc("You wish to try my challenge?");stage++} - 10 -> {options("Yes","No");stage++} - 11 -> when(buttonId){ - 1 -> {player("A maze? Is that all? Sure, it sounds simple enough.");stage++} - 2 -> {player("No, that sounds too hard.");stage = 1000} - } - - //Yes - 12 -> {npc("I will warn you outerlander, this maze was designed by","myself, and is of the most fiendish complexity!");stage++} - 13 -> {player("Oh really? Watch and learn...");stage = 1000;player?.setAttribute("/save:fremtrials:swensen-accepted",true)} - - //After maze - 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?.removeAttribute("fremtrials:swensen-accepted") - player?.setAttribute("/save:fremtrials:swensen-vote",true) - player?.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1) - stage = 1000 - } - - 1000 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return SwensenTheNavigator(player) - } - - override fun getIds(): IntArray { - return intArrayOf(1283) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/TFTInteractionListeners.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/TFTInteractionListeners.kt deleted file mode 100644 index d2c781728..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/TFTInteractionListeners.kt +++ /dev/null @@ -1,226 +0,0 @@ -package rs09.game.content.quest.members.thefremenniktrials - -import core.game.content.dialogue.FacialExpression -import core.game.node.scenery.Scenery -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.gather.SkillingTool -import core.game.node.item.Item -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.game.world.update.flag.context.Graphics -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import org.rs09.primextends.getNext -import org.rs09.primextends.isLast -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld -import rs09.game.world.GameWorld.Pulser - -class TFTInteractionListeners : InteractionListener(){ - - val BEER = Items.BEER_1917 - val WORKER = NPCs.COUNCIL_WORKMAN_1287 - val FISH_ALTAR = 4141 - val LYRE = Items.LYRE_3689 - val FISH = intArrayOf(Items.RAW_SHARK_383,Items.RAW_SEA_TURTLE_395,Items.RAW_MANTA_RAY_389) - val LOW_ALC_KEG = Items.LOW_ALCOHOL_KEG_3712 - val KEG = Items.KEG_OF_BEER_3711 - val TINDERBOX = Items.TINDERBOX_590 - val CHERRY_BOMB = Items.STRANGE_OBJECT_3713 - val LIT_BOMB = Items.LIT_STRANGE_OBJECT_3714 - val PIPE = 4162 - val PORTALIDs = intArrayOf(2273,2274,2506,2507,2505,2503,2504,5138) - val SWENSEN_LADDER = 4158 - val SWAYING_TREE = 4142 - val KNIFE = Items.KNIFE_946 - val TREE_BRANCH = Items.BRANCH_3692 - val LYRE_IDs = intArrayOf(14591,14590,6127,6126,6125,3691,3690) - - - override fun defineListeners() { - onUseWith(NPC,BEER,WORKER){ player, _, _ -> - player.dialogueInterpreter.open(CouncilWorkerFTDialogue(0,true), NPC(WORKER)) - return@onUseWith true - } - - onUseWith(SCENERY,FISH_ALTAR,*FISH){ player, _, fish -> - if(player.inventory.contains(LYRE,1)) { - Pulser.submit(spiritPulse(player, fish.id)) - } else { - player.sendMessage("I should probably have my lyre with me.") - } - return@onUseWith true - } - - onUseWith(ITEM,LOW_ALC_KEG,KEG){player,lowKeg,_ -> - if(!player.getAttribute("fremtrials:keg-mixed",false)!!){ - if(player.getAttribute("fremtrials:cherrybomb",false)) { - player.inventory?.remove(lowKeg.asItem()) - player.setAttribute("/save:fremtrials:keg-mixed", true) - player.sendMessage("The cherry bomb in the pipe goes off.") - RegionManager.getLocalEntitys(player).stream().forEach { e -> e.sendChat("What was THAT??") } - player.sendMessage("You mix the kegs together.") - return@onUseWith true - } else { - player.dialogueInterpreter?.sendDialogue("I can't do this right now. I should create","a distraction.") - return@onUseWith true - } - } else return@onUseWith false - } - - onUseWith(ITEM,TINDERBOX,CHERRY_BOMB){player,_,bomb -> - player.inventory.remove(bomb.asItem()) - player.inventory.add(Item(LIT_BOMB)) - player.sendMessage("You light the strange object.") - return@onUseWith true - } - - onUseWith(ITEM,KNIFE,TREE_BRANCH){player,knife,_ -> - if (player.inventory.containsItem(knife.asItem())) { - GameWorld.submit(BranchFletchingPulse(player)) - } else { - player.sendMessage("You need a knife to do this.") - } - return@onUseWith true - } - - on(LYRE_IDs,ITEM,"play"){player,lyre -> - if(player.questRepository?.getStage("Fremennik Trials")!! < 20){ - player.sendMessage("You lack the knowledge to play this.") - return@on true - } - if(LYRE_IDs.isLast(lyre.id)){ - player.sendMessage("Your lyre is out of charges!") - } else { - player.inventory?.remove(lyre.asItem()) - player.inventory?.add(Item(LYRE_IDs.getNext(lyre.id))) - player.lock() - Pulser.submit(telePulse(player)) - } - return@on true - } - - on(PIPE,SCENERY,"put-inside"){ player, _ -> - val bombItem = Item(LIT_BOMB) - if(player.inventory.containsItem(bombItem)){ - player.sendMessage("You stuff the lit object into the pipe.") - player.setAttribute("/save:fremtrials:cherrybomb",true) - player.inventory.remove(bombItem) - } else { - player.sendMessage("What am I supposed to put in there? A shoe?") - } - return@on true - } - - on(PORTALIDs,SCENERY,"use"){ player, portal -> - val toLocation = - when(portal.id){ - 2273 -> destRoom(2639, 10012, 2645, 10018).getCenter() - 2274 -> destRoom(2650, 10034, 2656, 10040).getCenter() - 2506 -> destRoom(2662, 10023, 2669, 10029).getCenter() - 2507 -> destRoom(2626, 10023, 2633, 10029).getCenter() - 2505 -> destRoom(2650, 10001, 2656, 10007).getCenter() - 2503 -> destRoom(2662, 10012, 2668, 10018).getCenter() - 2504 -> {player.setAttribute("/save:fremtrials:maze-complete",true); destRoom(2662, 10034, 2668, 10039).getCenter()} - else -> getRandomLocation(player) - } - player.properties?.teleportLocation = toLocation - return@on true; - } - - on(SWENSEN_LADDER,SCENERY,"climb"){ player, _ -> - if(player.getAttribute("fremtrials:swensen-accepted",false) == false){ - player.dialogueInterpreter?.sendDialogues(1283,FacialExpression.ANGRY,"Where do you think you're going?") - return@on true - } - return@on true - } - - on(SWAYING_TREE,SCENERY,"chop down"){ player, _ -> - SkillingTool.getHatchet(player)?.let { Pulser.submit(ChoppingPulse(player)).also { return@on true } } - - player.sendMessage("You need an axe which you have the woodcutting level to use to do this.") - return@on true - } - } - - class destRoom(val swx: Int, val swy: Int, val nex: Int, val ney: Int) - - fun destRoom.getCenter(): Location { - return Location((swx + nex) / 2, (swy + ney) / 2).transform(1,0,0) - } - - fun getRandomLocation(player: Player?): Location{ - var obj: Scenery? = null - - while(obj?.id != 5138) { - val objects = player?.viewport?.chunks?.random()?.random()?.objects - obj = objects?.random()?.random() - if(obj == null || obj.location?.equals(Location(0,0,0))!!){ - continue - } - } - return obj.location - } - - class spiritPulse(val player: Player?, val fish: Int) : Pulse(){ - var counter = 0 - val npc = NPC(1273,player?.location) - override fun pulse(): Boolean { - when(counter++){ - 0 -> npc.init().also { player?.lock() }.also { player?.inventory?.remove(Item(fish)) } - 1 -> npc.moveStep() - 2 -> npc.face(player).also { player?.face(npc) } - 3 -> player?.dialogueInterpreter?.sendDialogues(npc, - FacialExpression.HAPPY,"I will kindly accept this offering, and","bestow upon you a gift in return.") - 4 -> player?.inventory?.remove(Item(3689)) - 5 -> when(fish){ - 383 -> player?.inventory?.add(Item(6125)) - 389 -> player?.inventory?.add(Item(6126)) - 395 -> player?.inventory?.add(Item(6127)) - } - 6 -> player?.unlock() - 10 -> npc.clear().also { return true } - } - return false - } - } - - class ChoppingPulse(val player: Player) : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> player.animator.animate(SkillingTool.getHatchet(player).animation) - 4 -> player.animator.reset().also { player.inventory.add(Item(3692));return true} - } - return false - } - } - - class BranchFletchingPulse(val player: Player?) : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> player?.animator?.animate(Animation(1248)).also { player!!.lock() } - 3 -> player?.inventory?.remove(Item(Items.BRANCH_3692)).also { player?.inventory?.add(Item(Items.UNSTRUNG_LYRE_3688)); player!!.unlock(); return true} - } - return false - } - } - - class telePulse(val player: Player) : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> player.animator.animate(Animation(9600, Animator.Priority.VERY_HIGH), Graphics(1682)) - 6 -> player.properties.teleportLocation = Location.create(2661, 3646, 0) - 7 -> player.unlock().also { return true } - } - return false - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelostcity/DramenTreeListener.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thelostcity/DramenTreeListener.kt deleted file mode 100644 index 5b920e067..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelostcity/DramenTreeListener.kt +++ /dev/null @@ -1,44 +0,0 @@ -package rs09.game.content.quest.members.thelostcity - -import core.game.content.quest.members.lostcity.TreeSpiritNPC -import core.game.node.scenery.Scenery -import core.game.node.entity.skill.gather.GatheringSkillPulse -import core.game.node.entity.skill.gather.SkillingTool -import core.game.world.map.Location -import rs09.game.interaction.InteractionListener - -class DramenTreeListener : InteractionListener() { - - val DRAMEN_TREE = 1292 - - override fun defineListeners() { - - on(DRAMEN_TREE,SCENERY,"chop down"){ player, node -> - val quest = player.questRepository.getQuest("Lost City") - if (SkillingTool.getHatchet(player) == null) { - player.getPacketDispatch().sendMessage("You do not have an axe which you have the level to use.") - return@on true - } - if (quest.getStage(player) < 20) { - return@on true - } - if (quest.getStage(player) == 20) { - if (player.getAttribute("treeSpawned", false)) { - return@on true - } - val spirit = TreeSpiritNPC.create(655, Location.create(2862, 9734, 0)) as TreeSpiritNPC - spirit.player = player - spirit.isRespawn = false - spirit.init() - spirit.attack(player) - player.setAttribute("treeSpawned", true) - spirit.sendChat("You must defeat me before touching the tree!") - return@on true - } - player.getPulseManager().run(GatheringSkillPulse(player, node as Scenery?)) - return@on true - } - - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/DorgCbowEquipPlugin.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/DorgCbowEquipPlugin.kt deleted file mode 100644 index 88e8382e3..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/DorgCbowEquipPlugin.kt +++ /dev/null @@ -1,32 +0,0 @@ -package rs09.game.content.quest.members.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/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribeCutscene.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribeCutscene.kt deleted file mode 100644 index dd2207d5b..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/LostTribeCutscene.kt +++ /dev/null @@ -1,239 +0,0 @@ -package rs09.game.content.quest.members.thelosttribe - -import core.game.content.activity.ActivityPlugin -import core.game.content.activity.CutscenePlugin -import core.game.content.dialogue.FacialExpression -import core.game.content.global.action.DoorActionHandler -import core.game.interaction.MovementPulse -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.emote.Emotes -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.map.build.DynamicRegion -import core.game.world.update.flag.context.Animation -import core.plugin.Initializable -import rs09.game.camerautils.PlayerCamera -import rs09.game.world.GameWorld.Pulser - -private const val DUKE = 2088 -private const val MISTAG = 2089 -private const val SIGMUND = 2090 -private const val URTAG = 5858 -private const val BOW_ANIM = 6000 -@Initializable -/** - * Final cutscene for the lost tribe - * @author Ceikry - */ -class LostTribeCutscene(val pl: Player? = null) : CutscenePlugin("Lost Tribe Cutscene",true) { - var camera: PlayerCamera = PlayerCamera(null) - - init { - this.player = pl - } - - override fun configure() { - region = DynamicRegion.create(12850) - setRegionBase() - registerRegion(region.id) - } - - override fun getStartLocation(): Location { - return base.transform(7,17,0) - } - - override fun newInstance(p: Player?): ActivityPlugin { - return LostTribeCutscene(player) - } - - override fun getSpawnLocation(): Location { - return Location.create(3208, 3213, 0) - } - - override fun open() { - player.setAttribute("cutscene:original-loc",player.location) - player.setAttribute("real-end",Location.create(3207, 3221, 0)) - player.interfaceManager.hideTabs(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14) - if(player.settings.isRunToggled){ - player.settings.toggleRun() - } - - camera = PlayerCamera(player) - var loc = coords(8,26) - camera.panTo(loc.x,loc.y,300,100) - loc = coords(5,18) - camera.rotateTo(loc.x,loc.y,300,100) - - player.faceLocation(coords(7,18)) - - val mistag = npcs.get(1) - val urtag = npcs.get(2) - val duke = npcs.get(0) - val sigmund = npcs.get(3) - - Pulser.submit(object: Pulse(){ - var counter = 0 - var stage = 0 - override fun pulse(): Boolean { - if(!player.isPlaying || !player.isActive) return true - when(counter++){ - 5 -> DoorActionHandler.handleDoor(player, RegionManager.getObject(coords(7,17))) - 7 -> { - player.pulseManager.run(object: MovementPulse(player,coords(7,22),false){ - override fun pulse(): Boolean { - return true - } - }, "movement") - mistag.pulseManager.run(object : MovementPulse(mistag,coords(7,19)){//coords(7,20)){ - override fun pulse(): Boolean { - return true - } - }, "movement") - urtag.pulseManager.run(object : MovementPulse(urtag,coords(7,18)){ - override fun pulse(): Boolean { - return true - } - }) - } - 12 -> { - mistag.pulseManager.run(object : MovementPulse(mistag,coords(7,20)){//coords(7,20)){ - override fun pulse(): Boolean { - return true - } - }, "movement") - urtag.pulseManager.run(object : MovementPulse(urtag,coords(6,21)){ - override fun pulse(): Boolean { - return true - } - }) - } - 15 -> { - player.faceLocation(duke.location) - player.dialogueInterpreter.sendDialogues(player,FacialExpression.FRIENDLY,"Your grace, I present Ur-tag, headman of the","Dorgeshuun.") - Emotes.BOW.play(player) - player.dialogueInterpreter.addAction { player, buttonId -> - Pulser.submit(object: MovementPulse(player,coords(7,24)){ - override fun pulse(): Boolean { - return true - } - }) - Pulser.submit(object : Pulse(5){ - var counter = 0 - override fun pulse(): Boolean { - duke.animator.animate(Emotes.BOW.animation) - urtag.animator.animate(Animation(BOW_ANIM)) - player.dialogueInterpreter.sendDialogues(duke,FacialExpression.FRIENDLY,"Welcome, Ur-tag. I am sorry that your race came","under suspicion.") - player.dialogueInterpreter.addAction { player, buttonId -> - player.dialogueInterpreter.sendDialogues(duke,FacialExpression.ANGRY,"I assure you that the warmongering element has been","dealt with.") - loc = coords(9,22) - camera.panTo(loc.x,loc.y,300,2) - loc = coords(6,22) - camera.rotateTo(loc.x,loc.y,300,2) - player.dialogueInterpreter.addAction { player, buttonId -> - player.dialogueInterpreter.sendDialogues(urtag,FacialExpression.OLD_NORMAL,"I apologize for the damage to your cellar. I will send","workers to repair the hole.") - player.dialogueInterpreter.addAction { player, buttonId -> - player.dialogueInterpreter.sendDialogues(duke,FacialExpression.FRIENDLY,"No, let it stay. It can be a route of commerce between","our lands.") - player.dialogueInterpreter.addAction { player, buttonId -> - duke.faceLocation(player.location) - player.faceLocation(duke.location) - camera.rotateTo(duke.location.x,duke.location.y,350,4) - loc = coords(11,22) - camera.panTo(loc.x,loc.y,325,3) - loc = coords(6,22) - camera.rotateTo(loc.x,loc.y,300,3) - player.dialogueInterpreter.sendDialogues(duke,FacialExpression.FRIENDLY,"${player.name}, Lumbridge is in your debt. Please accept","this ring as a token of my thanks.") - player.dialogueInterpreter.addAction { player, buttonId -> - player.dialogueInterpreter.sendDialogues(duke,FacialExpression.FRIENDLY,"It is enchanted to save you in your hour of need.") - player.dialogueInterpreter.addAction { player, buttonId -> - urtag.pulseManager.run(object: MovementPulse(urtag,coords(7,23)){ - override fun pulse(): Boolean { - return true - } - }) - urtag.faceLocation(player.location) - player.dialogueInterpreter.sendDialogues(urtag,FacialExpression.OLD_NORMAL,"I too thank you. Accept the freedom of the Dorgeshuun","mines.") - player.dialogueInterpreter.addAction { player, buttonId -> - player.dialogueInterpreter.sendDialogues(urtag,FacialExpression.OLD_NORMAL,"These are strange times. I never dreamed that I would","see the surface, still less that I would be on friendly","terms with its people.") - loc = coords(16,21) - camera.panTo(loc.x,loc.y,300,3) - loc = coords(6,22) - camera.rotateTo(loc.x,loc.y,300,2) - player.dialogueInterpreter.addAction { player, buttonId -> - Pulser.submit(object : Pulse(5) { - override fun pulse(): Boolean { - player.dialogueInterpreter.sendDialogues(sigmund,FacialExpression.ANGRY,"Prattle on, goblin.") - player.dialogueInterpreter.addAction { player, buttonId -> - player.dialogueInterpreter.sendDialogues(sigmund,FacialExpression.EVIL_LAUGH,"Soon you will be destroyed!") - sigmund.animator.animate(Emotes.LAUGH.animation) - player.dialogueInterpreter.addAction { player, buttonId -> - loc = coords(16,17) - sigmund.pulseManager.run(object: MovementPulse(sigmund,loc){ - override fun pulse(): Boolean { - return true - } - }) - Pulser.submit(object : Pulse(4){ - override fun pulse(): Boolean { - Pulser.submit(endPulse) - Pulser.submit(object : Pulse(7){ - override fun pulse(): Boolean { - player.questRepository.getQuest("Lost Tribe").setStage(player,100) - player.questRepository.getQuest("Lost Tribe").finish(player) - return true - } - }) - return true - } - }) - } - } - return true - } - }) - } - } - } - } - } - } - } - } - return true - } - }) - } - loc = coords(7,25) - camera.panTo(loc.x,loc.y,300,3) - return true - } - } - return false - } - }) - } - - override fun start(player: Player?, login: Boolean, vararg args: Any?): Boolean { - val duke = NPC(DUKE,coords(6,23)) - val mistag = NPC(MISTAG,coords(8,17)) - val urtag = NPC(URTAG,coords(8,15)) - val sigmund = NPC(SIGMUND,coords(13,22)) - duke.init() - duke.faceLocation(coords(6,22)) - mistag.init() - urtag.init() - sigmund.init() - sigmund.faceLocation(coords(11,18)) - npcs.add(duke) - npcs.add(mistag) - npcs.add(urtag) - npcs.add(sigmund) - return super.start(player, login, *args) - } - - fun coords(x: Int, y: Int): Location { - return base.transform(x,y,0) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/PickpocketSigmund.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/PickpocketSigmund.kt deleted file mode 100644 index 01fe466b3..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thelosttribe/PickpocketSigmund.kt +++ /dev/null @@ -1,44 +0,0 @@ -package rs09.game.content.quest.members.thelosttribe - -import core.game.content.dialogue.FacialExpression -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.NPCs -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld - -/** - * handles pickpocketing sigmund during the lost tribe quest - * @author Ceikry - */ -class PickpocketSigmund : InteractionListener(){ - val SIGMUND = NPCs.SIGMUND_2082 - - override fun defineListeners() { - on(SIGMUND,NPC,"pickpocket"){player, node -> - player.lock() - GameWorld.Pulser.submit(object : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - 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))){ - player.inventory.add(Item(Items.KEY_423)) - player.dialogueInterpreter.sendItemMessage(Items.KEY_423,"You find a small key on Sigmund.") - } else { - player.dialogueInterpreter.sendDialogues(2082,FacialExpression.ANGRY,"What do you think you're doing?!") - } - player.unlock() - return true - } - } - return false - } - }) - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/waterfall/WaterfallListeners.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/waterfall/WaterfallListeners.kt deleted file mode 100644 index a2d187a89..000000000 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/waterfall/WaterfallListeners.kt +++ /dev/null @@ -1,22 +0,0 @@ -package rs09.game.content.quest.members.waterfall - -import core.game.world.map.Location -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener - -class WaterfallListeners : InteractionListener(){ - - val HUDON = NPCs.HUDON_305 - override fun defineListeners() { - on(HUDON,NPC,"talk-to"){player, node -> - player.dialogueInterpreter.open(HUDON,node.asNpc()) - return@on true - } - } - - override fun defineDestinationOverrides() { - setDest(NPC,HUDON){_,_ -> - return@setDest Location.create(2512, 3481, 0) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/zone/FarmingPatchZone.kt b/Server/src/main/kotlin/rs09/game/content/zone/FarmingPatchZone.kt deleted file mode 100644 index 68145204b..000000000 --- a/Server/src/main/kotlin/rs09/game/content/zone/FarmingPatchZone.kt +++ /dev/null @@ -1,98 +0,0 @@ -package rs09.game.content.zone - -import api.ContentAPI -import core.game.content.dialogue.FacialExpression -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.system.task.Pulse -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 org.rs09.consts.NPCs -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.secondsToTicks -import java.util.concurrent.TimeUnit - -@Initializable -class FarmingPatchZone : MapZone("farming patch", true), Plugin { - - val playersInZone = hashMapOf() - - override fun configure() { - registerRegion(12083) - registerRegion(10548) - register(ZoneBorders(3594,3521,3608,3532)) - ContentAPI.submitWorldPulse(zonePulse) - } - - override fun newInstance(arg: Any?): Plugin { - ZoneBuilder.configure(this) - return this - } - - override fun fireEvent(identifier: String?, vararg args: Any?): Any { - return Unit - } - - override fun enter(e: Entity?): Boolean { - if(e is Player && playersInZone[e] == null && ContentAPI.getStatLevel(e, Skills.FARMING) <= 15) { - playersInZone[e] = 0 - } - return super.enter(e) - } - - override fun leave(e: Entity?, logout: Boolean): Boolean { - if(e is Player){ - playersInZone.remove(e) - } - return super.enter(e) - } - - val zonePulse = object : Pulse(){ - override fun pulse(): Boolean { - playersInZone.toList().forEach { (player, ticks) -> - if(ticks == secondsToTicks(TimeUnit.MINUTES.toSeconds(5).toInt())){ - val npc = NPC(NPCs.GITHAN_7122) - npc.location = player.location - npc.init() - npc.moveStep() - npc.face(player) - ContentAPI.openDialogue(player, SpiritDialogue(true), npc) - } else if (ticks == secondsToTicks(TimeUnit.MINUTES.toSeconds(10).toInt())){ - val npc = NPC(NPCs.GITHAN_7122) - npc.location = player.location - npc.init() - npc.moveStep() - npc.face(player) - ContentAPI.openDialogue(player, SpiritDialogue(false), npc) - playersInZone.remove(player) - } - - playersInZone[player] = ticks + 1 - } - return false - } - } - - internal class SpiritDialogue(val is5: Boolean) : DialogueFile(){ - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - 0 -> { - if(is5) 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() - } - } - - override fun end(){ - super.end() - ContentAPI.poofClear(npc ?: return) - } - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/DwarvenBoatmanDialogue.kt b/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/DwarvenBoatmanDialogue.kt deleted file mode 100644 index 78daf00b6..000000000 --- a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/DwarvenBoatmanDialogue.kt +++ /dev/null @@ -1,69 +0,0 @@ -package rs09.game.content.zone.keldagrim - -import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.plugin.Initializable -import org.rs09.consts.Components -import rs09.game.world.GameWorld - -@Initializable -class DwarvenBoatmanDialogue(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)) } - } - return true - } - - override fun open(vararg args: Any?): Boolean { - npc("Aye, what do you want?") - stage = 0 - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return DwarvenBoatmanDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(2205,1844,1843) - } - - override fun npc(vararg messages: String?): Component { - return super.npc(FacialExpression.OLD_NORMAL, *messages) - } -} - -class travelPulse(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)) - } - 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 diff --git a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/KeldagrimPlugin.kt b/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/KeldagrimPlugin.kt deleted file mode 100644 index 62fb99d16..000000000 --- a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/KeldagrimPlugin.kt +++ /dev/null @@ -1,107 +0,0 @@ -package rs09.game.content.zone.keldagrim - -import core.cache.def.impl.SceneryDefinition -import core.game.content.dialogue.DialoguePlugin -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 - -/** - * File that contains several plugins relating to Keldagrim, - * most notably the KeldagrimOptionHandlers and GETrapdoorDialogue - * Anything that wasn't significant enough for its own individual file, tbh. - * @author Ceikry - */ - -const val GETrapdoorDialogueID = 22381232 - -/** - * Handles various options around keldagrim that weren't significant enough for a separate file - */ -@Initializable -class KeldagrimOptionHandlers : OptionHandler() { - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - player ?: return false - node ?: return false - option ?: return false - when(option){ - "go-through" -> { - when (node.id) { - 5973 -> player.properties.teleportLocation = Location.create(2838, 10125) - 5998 -> player.properties.teleportLocation = Location.create(2780, 10161) - } - } - "climb-up" -> { - when(node.id){ - 9138 -> player.properties.teleportLocation = Location.create(2931, 10196, 0) - } - } - "climb-down" -> { - when(node.id){ - 9084 -> player.properties.teleportLocation = Location.create(1940, 4958, 0) - } - } - "open" -> { - when(node.id){ - 28094 -> player.dialogueInterpreter.open(GETrapdoorDialogueID) - } - } - "enter" -> { - when(node.id){ - 5014 -> player.properties.teleportLocation = Location.create(2730, 3713, 0) - } - } - } - return true - } - - override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.forId(5973).handlers["option:go-through"] = this - SceneryDefinition.forId(5998).handlers["option:go-through"] = this - SceneryDefinition.forId(9084).handlers["option:climb-down"] = this - SceneryDefinition.forId(9138).handlers["option:climb-up"] = this - SceneryDefinition.forId(28094).handlers["option:open"] = this - SceneryDefinition.forId(5014).handlers["option:enter"] = this - return this - } -} - -/** - * Dialogue used for the trapdoor in the grand exchange. - */ -@Initializable -class GETrapdoorDialogue(player: Player? = null) : DialoguePlugin(player){ - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> end() - 10 -> when(buttonId){ - 1 -> KeldagrimCartMethods.goToKeldagrim(player).also { end() } - 2 -> end() - } - } - return true - } - - override fun open(vararg args: Any?): Boolean { - val keldagrimVisited = player.getAttribute("keldagrim-visited",false) - if(keldagrimVisited){ - options("Travel to Keldagrim","Nevermind.") - stage = 10 - } else { - player.dialogueInterpreter.sendDialogue("Perhaps I should visit Keldagrim first.") - stage = 0 - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return GETrapdoorDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(GETrapdoorDialogueID) - } -} diff --git a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/Reinald.kt b/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/Reinald.kt deleted file mode 100644 index 64e8e5d24..000000000 --- a/Server/src/main/kotlin/rs09/game/content/zone/keldagrim/Reinald.kt +++ /dev/null @@ -1,59 +0,0 @@ -package rs09.game.content.zone.keldagrim - -import core.cache.def.impl.NPCDefinition -import core.game.component.Component -import core.game.content.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 - -/** - * File for handling Reinald's dialogue and right-click option - * @author Ceikry - */ -@Initializable -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)) } - 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?): DialoguePlugin { - return ReinaldDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(REINALD) - } - -} - -@Initializable -class ReinaldOptionHandler : OptionHandler(){ - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - player?.interfaceManager?.open(Component(593)) - return true - } - - override fun newInstance(arg: Any?): Plugin { - NPCDefinition.forId(REINALD).handlers["option:change-armguards"] = this - return this - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/zone/phasmatys/bonegrinder/BoneGrinderListener.kt b/Server/src/main/kotlin/rs09/game/content/zone/phasmatys/bonegrinder/BoneGrinderListener.kt deleted file mode 100644 index e7671417c..000000000 --- a/Server/src/main/kotlin/rs09/game/content/zone/phasmatys/bonegrinder/BoneGrinderListener.kt +++ /dev/null @@ -1,252 +0,0 @@ -package rs09.game.content.zone.phasmatys.bonegrinder - -import api.Container -import api.ContentAPI -import core.game.content.global.Bones -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.update.flag.context.Animation -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld.Pulser - -private const val LOADER = 11162 -private const val BONE_GRINDER = 11163 -private const val BIN = 11164 - -private const val LOADED_BONE_KEY = "/save:bonegrinder-bones" -private const val BONE_HOPPER_KEY = "/save:bonegrinder-hopper" -private const val BONE_BIN_KEY = "/save:bonegrinder-bin" - -private val FILL_ANIM = Animation(1649) -private val WIND_ANIM = Animation(1648) -private val SCOOP_ANIM = Animation(1650) - -class BoneGrinderListener : InteractionListener() { - - private val boneIDs = Bones.values().map { it.itemId }.toIntArray() - - override fun defineListeners() { - - /** - * Handle the bone loader/hopper fill option - */ - on(LOADER,SCENERY,"fill"){ player, _ -> - handleFill(player) - } - - /** - * Handle the wheel's wind option - */ - on(BONE_GRINDER,SCENERY,"wind"){ player, _ -> - handleWind(player) - } - - /** - * Handle the wheel's status option - */ - on(BONE_GRINDER,SCENERY,"status"){ player, _ -> - handleStatus(player) - } - - /** - * Handle the bin's empty option - */ - on(BIN,SCENERY,"empty"){ player, _ -> - handleEmpty(player) - } - - /** - * Handle Bone -> Hopper - */ - onUseWith(SCENERY,LOADER,*boneIDs){ player, _, _ -> - handleFill(player) - return@onUseWith true - } - - } - - fun handleFill(player: Player): Boolean{ - val bone = getBone(player) - if(bone == null){ - ContentAPI.sendMessage(player,"You have no bones to grind.") - return true - } - if(ContentAPI.getAttribute(player,BONE_HOPPER_KEY,false)){ - ContentAPI.sendMessage(player,"You already have some bones in the hopper.") - return true - } - if(ContentAPI.getAttribute(player,BONE_BIN_KEY,false)){ - ContentAPI.sendMessage(player,"You already have some bonemeal that needs to be collected.") - return true - } - - val fillPulse = object: Pulse(){ - var stage = 0 - override fun pulse(): Boolean { - when(stage++){ - 0 -> { - ContentAPI.lock(player, FILL_ANIM.duration) - ContentAPI.animate(player,FILL_ANIM) - } - FILL_ANIM.duration -> { - ContentAPI.sendMessage(player,"You fill the hopper with bones.") - ContentAPI.removeItem(player,Item(bone.itemId),Container.INVENTORY) - ContentAPI.setAttribute(player,LOADED_BONE_KEY,bone.ordinal) - ContentAPI.setAttribute(player,BONE_HOPPER_KEY,true) - return true - } - } - return false - } - } - - if(ContentAPI.inInventory(player,bone.itemId)){ - player.pulseManager.run(object : Pulse(){ - var stage = 0 - override fun pulse(): Boolean { - when(stage++){ - 0 -> Pulser.submit(fillPulse).also { delay = FILL_ANIM.duration + 1} - 1 -> { - ContentAPI.stopWalk(player) - ContentAPI.forceWalk(player,Location(3659,3524),"smart") - delay = 2 - } - 2 -> { - handleWind(player) - delay = WIND_ANIM.duration + 1 - } - 3 -> { - ContentAPI.stopWalk(player) - ContentAPI.forceWalk(player,Location(3658,3524),"smart") - delay = 2 - } - 4 -> { - if(!ContentAPI.inInventory(player,Items.EMPTY_POT_1931,1)){ - return handleEmpty(player) - } else { - handleEmpty(player) - delay = SCOOP_ANIM.duration + 1 - } - } - 5 -> { - ContentAPI.stopWalk(player) - ContentAPI.forceWalk(player,Location(3660,3524),"smart") - delay = 4 - } - 6 -> { - ContentAPI.face(player,Location(3660,3526)) - handleFill(player) - return true - } - } - return false - } - }) - } else { - Pulser.submit(fillPulse) - } - return true - } - - fun handleWind(player: Player): Boolean{ - if(!ContentAPI.getAttribute(player,BONE_HOPPER_KEY,false)){ - ContentAPI.sendMessage(player,"You have no bones loaded to grind.") - return true - } - - if(ContentAPI.getAttribute(player,BONE_BIN_KEY,false)){ - ContentAPI.sendMessage(player,"You already have some bonemeal which you need to collect.") - return true - } - - Pulser.submit(object : Pulse(){ - var stage = 0 - override fun pulse(): Boolean { - when(stage++){ - 0 -> { - ContentAPI.face(player,Location(3659, 3526, 1)) - ContentAPI.lock(player,WIND_ANIM.duration) - ContentAPI.animate(player,WIND_ANIM) - ContentAPI.sendMessage(player,"You wind the handle.") - } - WIND_ANIM.duration -> { - ContentAPI.sendMessage(player,"The bonemeal falls into the bin.") - ContentAPI.setAttribute(player,BONE_HOPPER_KEY,false) - ContentAPI.setAttribute(player,BONE_BIN_KEY,true) - return true - } - } - return false - } - }) - return true - } - - private fun handleStatus(player: Player): Boolean{ - val bonesLoaded = ContentAPI.getAttribute(player,BONE_HOPPER_KEY,false) - val boneMealReady = ContentAPI.getAttribute(player,BONE_BIN_KEY,false) - - if(bonesLoaded) ContentAPI.sendMessage(player,"There are bones waiting in the hopper.") - if(boneMealReady) ContentAPI.sendMessage(player,"There is bonemeal waiting in the bin to be collected.") - - if(!bonesLoaded && !boneMealReady){ - ContentAPI.sendMessage(player,"There is nothing loaded into the machine.") - } - - return true - } - - fun handleEmpty(player: Player): Boolean{ - if(!ContentAPI.getAttribute(player,BONE_BIN_KEY,false)){ - ContentAPI.sendMessage(player,"You have no bonemeal to collect.") - return true - } - - if(ContentAPI.getAttribute(player,BONE_HOPPER_KEY,false) && !ContentAPI.getAttribute(player,BONE_BIN_KEY,false)){ - ContentAPI.sendMessage(player,"You need to wind the wheel to grind the bones.") - return true - } - - if(!ContentAPI.inInventory(player,Items.EMPTY_POT_1931,1)){ - ContentAPI.sendMessage(player,"You don't have any pots to take the bonemeal with.") - return true - } - - val bone = Bones.values()[ContentAPI.getAttribute(player,LOADED_BONE_KEY,-1)] - - - Pulser.submit(object : Pulse(){ - var stage = 0 - override fun pulse(): Boolean { - when(stage++){ - 0 -> { - ContentAPI.face(player,Location(3658, 3525, 1)) - ContentAPI.lock(player, SCOOP_ANIM.duration) - ContentAPI.animate(player,SCOOP_ANIM) - } - SCOOP_ANIM.duration -> { - if(ContentAPI.removeItem(player,Item(Items.EMPTY_POT_1931),Container.INVENTORY)){ - ContentAPI.addItem(player,bone.boneMeal.id) - ContentAPI.setAttribute(player,BONE_BIN_KEY,false) - ContentAPI.setAttribute(player,BONE_HOPPER_KEY,false) - ContentAPI.setAttribute(player,LOADED_BONE_KEY,-1) - } - return true - } - } - return false - } - }) - return true - } - - fun getBone(player: Player): Bones? { - for(bone in Bones.values()){ - if(ContentAPI.inInventory(player,bone.itemId)) return bone - } - return null - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ge/GrandExchange.kt b/Server/src/main/kotlin/rs09/game/ge/GrandExchange.kt deleted file mode 100644 index b5e3a3ae5..000000000 --- a/Server/src/main/kotlin/rs09/game/ge/GrandExchange.kt +++ /dev/null @@ -1,51 +0,0 @@ -package rs09.game.ge - -import core.game.world.callback.CallBack -import rs09.game.system.SystemLogger -import rs09.tools.secondsToTicks - -object GrandExchange : CallBack { - /** - * Fallback safety check to make sure we don't start the GE twice under any circumstance - */ - var isRunning = false - - /** - * Initializes the offer manager and spawns an update thread. - * @param local whether or not the GE should be the local in-code server rather than some hypothetical remote implementation. - */ - fun boot(local: Boolean){ - if(isRunning) return - - if(!local){ - TODO("Remote GE server stuff") - } - - SystemLogger.logGE("Initializing GE...") - OfferManager.init() - SystemLogger.logGE("GE Initialized.") - - SystemLogger.logGE("Initializing GE Update Worker") - - val t = Thread { - Thread.currentThread().name = "GE Update Worker" - while(true) { - SystemLogger.logGE("Updating offers...") - OfferManager.update() - if(OfferManager.dumpDatabase){ - SystemLogger.logGE("Saving GE...") - OfferManager.save() - OfferManager.dumpDatabase = false - } - Thread.sleep(60_000) //sleep for 60 seconds - } - }.start() - - isRunning = true - } - - override fun call(): Boolean { - boot(true) - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ge/GrandExchangeOffer.kt b/Server/src/main/kotlin/rs09/game/ge/GrandExchangeOffer.kt deleted file mode 100644 index 6e1038e38..000000000 --- a/Server/src/main/kotlin/rs09/game/ge/GrandExchangeOffer.kt +++ /dev/null @@ -1,55 +0,0 @@ -package rs09.game.ge - -import core.cache.def.impl.ItemDefinition -import core.game.ge.OfferState -import core.game.node.entity.player.Player -import core.game.node.item.Item - - -/** - * A struct holding all the data for grand exchange offers as stored in json database. - * - * @author Angle - */ - -class GrandExchangeOffer() { - var itemID = 0 - var amount = 0 - var completedAmount = 0 - var offeredValue = 0 - var index = 0 - var sell = false - var offerState = OfferState.PENDING - var uid: Long = 0 - var timeStamp: Long = 0 - var withdraw = arrayOfNulls(2) - var totalCoinExchange = 0 - var player: Player? = null - var playerUID = 0 - var isLimitation = false - - /** - * Gets the total amount of money entered. - * @return The total value. - */ - val totalValue: Int - get() = offeredValue * amount - - /** - * Gets the amount of this item left to buy. - * @return The amount. - */ - val amountLeft: Int - get() = amount - completedAmount - - /** - * Checks if this offer is still active for dispatching. - * @return `True` if so. - */ - val isActive: Boolean - get() = offerState != OfferState.ABORTED && offerState != OfferState.PENDING && offerState != OfferState.COMPLETED && offerState != OfferState.REMOVED - - override fun toString(): String { - return "[name=" + ItemDefinition.forId(itemID).name + ", itemId=" + itemID + ", amount=" + amount + ", completedAmount=" + completedAmount + ", offeredValue=" + offeredValue + ", index=" + index + ", sell=" + sell + ", state=" + offerState + ", withdraw=" + withdraw.contentToString() + ", totalCoinExchange=" + totalCoinExchange + ", playerUID=" + playerUID + "]" - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ge/OfferManager.kt b/Server/src/main/kotlin/rs09/game/ge/OfferManager.kt deleted file mode 100644 index 2c779ee7c..000000000 --- a/Server/src/main/kotlin/rs09/game/ge/OfferManager.kt +++ /dev/null @@ -1,526 +0,0 @@ -package rs09.game.ge - -import api.ContentAPI -import rs09.ServerConstants -import core.cache.def.impl.ItemDefinition -import core.game.content.eco.EcoStatus -import core.game.content.eco.EconomyManagement -import core.game.ge.BuyingLimitation -import core.game.ge.GrandExchangeDatabase -import core.game.ge.OfferState -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.node.item.Item -import rs09.game.system.SystemLogger -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import core.game.world.callback.CallBack -import rs09.game.world.repository.Repository -import core.net.packet.PacketRepository -import core.net.packet.context.ContainerContext -import core.net.packet.out.ContainerPacket -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.game.ai.AIPlayer -import rs09.game.system.config.ItemConfigParser -import java.io.File -import java.io.FileReader -import java.io.FileWriter -import java.io.IOException -import java.lang.Integer.max -import java.lang.Integer.min -import java.util.* -import java.util.concurrent.locks.ReentrantLock -import javax.script.ScriptEngineManager -import kotlin.collections.ArrayList - -object OfferManager { - /** - * The update notification. - */ - private const val UPDATE_NOTIFICATION = "One or more of your grand exchange offers have been updated." - - /** - * The database path. - */ - private val DB_PATH = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "offer_dispatch.json" - - /** - * Bot DB path - */ - private val BOT_DB_PATH = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "bot_offers.json" - - /** - * The offset of the offer UIDs. - */ - private var offsetUID: Long = 1 - - /** - * The mapping of all current offers. Stored in multiple maps. - * - * First map is offsetID, Offer - * Second is itemID, offsetID, Offer - * Final is playerID, offsetID, Offer - */ - @JvmStatic - val OFFER_MAPPING: MutableMap = HashMap() - val OFFERS_BY_ITEMID: MutableMap> = HashMap() - private val GE_OFFER_LOCK = ReentrantLock() - - /** - * Bot offers are sorted by itemID. - * the second int shows the offer amount. Negative is buying positive selling. - */ - public val BOT_OFFERS: HashMap = HashMap() - - /** - * If the database should be dumped. - */ - public var dumpDatabase = false - - /** - * Initializes the Grand Exchange. - */ - fun init() { - GE_OFFER_LOCK.lock() - val file = File(DB_PATH) - - if(file.exists() && file.length() != 0L) { - val parser = JSONParser() - val reader: FileReader = FileReader(DB_PATH) - val saveFile = parser.parse(reader) as JSONObject - - offsetUID = saveFile["offsetUID"].toString().toLong() - - if (saveFile.containsKey("offers")) { - val offers = saveFile["offers"] as JSONArray - for (offer in offers) { - val o = offer as JSONObject - val no = GrandExchangeOffer() - no.itemID = o["itemId"].toString().toInt() - no.sell = o["sale"] as Boolean - no.offeredValue = o["offeredValue"].toString().toInt() - no.amount = o["amount"].toString().toInt() - no.timeStamp = o["timeStamp"].toString().toLong() - no.uid = o["uid"].toString().toLong() - no.completedAmount = o["completedAmount"].toString().toInt() - no.playerUID = o["playerUID"].toString().toInt() - no.offerState = OfferState.values()[o["offerState"].toString().toInt()] - no.totalCoinExchange = o["totalCoinExchange"].toString().toInt() - val withdrawData = o["withdrawItems"] as JSONArray - for ((index, data) in withdrawData.withIndex()) { - val item = data as JSONObject - val it = Item(item["id"].toString().toInt(), item["amount"].toString().toInt()) - no.withdraw[index] = it - } - addEntry(no) - } - } - } - - if(File(BOT_DB_PATH).exists()) { - try { - val botReader: FileReader = FileReader(BOT_DB_PATH) - val botSave = JSONParser().parse(botReader) as JSONObject - if (botSave.containsKey("offers")) { - val offers = botSave["offers"] as JSONArray - for (offer in offers) { - val o = offer as JSONObject - addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt()) - } - } - } catch (e: IOException) { - GE_OFFER_LOCK.unlock() - SystemLogger.logWarn("Unable to load bot offers. Perhaps it doesn't exist?") - } - } - GE_OFFER_LOCK.unlock() - } - - fun update(){ - for (offer in OFFER_MAPPING.values) { - if (offer.isActive) { - updateOffer(offer) - } - } - } - - fun buyFromBots(offer: GrandExchangeOffer) { - if (BOT_OFFERS[offer.itemID] == null) { - return - } - val botPrice = getRecommendedPrice(offer.itemID, true) - if (offer.offeredValue < botPrice) { - return - } - val amount = min(BOT_OFFERS[offer.itemID]!!, getBuylimitAmount(offer)) - val botOffer = GrandExchangeOffer() - botOffer.sell = true - botOffer.amount = amount - botOffer.offerState = OfferState.REGISTERED - botOffer.offeredValue = botPrice - exchange(offer, botOffer) - BOT_OFFERS[offer.itemID] = BOT_OFFERS[offer.itemID]!! - amount - } - - private fun buyFromBotsWithItem(itemID: Int) { - if (OFFERS_BY_ITEMID[itemID] == null || BOT_OFFERS[itemID] == null) { - return - } - for (trade in OFFERS_BY_ITEMID[itemID]!!) { - if (!trade.sell) { - buyFromBots(trade) - } - } - } - - fun addBotOffer(itemID: Int, qty: Int): Boolean { - if (GrandExchangeDatabase.getDatabase()[itemID] == null) { - SystemLogger.logWarn("Bot attempted to sell invalid item $itemID") - return false - } - - if (BOT_OFFERS[itemID] == null) { - BOT_OFFERS[itemID] = qty - } else { - BOT_OFFERS[itemID] = (qty + BOT_OFFERS[itemID]!!) - } - buyFromBotsWithItem(itemID) - return true - } - - fun amtBotsSelling(itemID: Int): Int { - if (BOT_OFFERS[itemID] == null) { - return 0 - } - if (BOT_OFFERS[itemID]!! <= 0) { - return 0 - } - return BOT_OFFERS[itemID]!! - } - - fun setIndex(offerID: Long, idx: Int) { - if (!OFFER_MAPPING.containsKey(offerID)) { - println("ERROR. GE Entry $offerID not found in database. Playerdata may be corrupted.") - return - } - OFFER_MAPPING[offerID]!!.index = idx - } - - fun removeEntry(offer: GrandExchangeOffer): Boolean{ - println("REMOVING ENTRY of ID " + offer.itemID) - GE_OFFER_LOCK.lock() - if (!OFFER_MAPPING.containsKey(offer.uid)){ - GE_OFFER_LOCK.unlock() - return false - } - OFFER_MAPPING.remove(offer.uid) - OFFERS_BY_ITEMID[offer.itemID]?.remove(offer) - GE_OFFER_LOCK.unlock() - return true - } - - @Synchronized fun addEntry(offer: GrandExchangeOffer){ - GE_OFFER_LOCK.lock() - OFFER_MAPPING[offer.uid] = offer - if (!OFFERS_BY_ITEMID.containsKey(offer.itemID)) { - OFFERS_BY_ITEMID[offer.itemID] = mutableListOf() - } - OFFERS_BY_ITEMID[offer.itemID]?.add(offer) - GE_OFFER_LOCK.unlock() - } - - fun getQuantitySoldForItem(item: Int): Int { - var qty = 0 - val offs = getOffersForItem(item) - for (o in offs) { - if (o.sell) { - qty += o.amountLeft - } - } - qty += amtBotsSelling(item) - return qty - } - - @JvmStatic - fun getOffersForItem(item: Int): MutableList { - if (OFFERS_BY_ITEMID.containsKey(item)) { - return OFFERS_BY_ITEMID[item]!! - } - return mutableListOf() - } - - @JvmStatic - fun save(){ - GE_OFFER_LOCK.lock() - val root = JSONObject() - val offers = JSONArray() - - if(OFFER_MAPPING.isEmpty() && BOT_OFFERS.isEmpty()){ - return - } - - for(entry in OFFER_MAPPING){ - val offer = entry.value - if (offer.offerState == OfferState.REMOVED) { - continue - } - val o = JSONObject() - o["uid"] = entry.key.toString() - o["itemId"] = offer.itemID.toString() - o["sale"] = offer.sell - o["amount"] = offer.amount.toString() - o["completedAmount"] = offer.completedAmount.toString() - o["offeredValue"] = offer.offeredValue.toString() - o["timeStamp"] = offer.timeStamp.toString() - o["offerState"] = offer.offerState.ordinal.toString() - o["totalCoinExchange"] = offer.totalCoinExchange.toString() - o["playerUID"] = offer.playerUID.toString() - val withdrawItems = JSONArray() - for(item in offer.withdraw){ - item ?: continue - val it = JSONObject() - it["id"] = item.id.toString() - it["amount"] = item.amount.toString() - withdrawItems.add(it) - } - o["withdrawItems"] = withdrawItems - offers.add(o) - } - root["offsetUID"] = offsetUID.toString() - root["offers"] = offers - - val manager = ScriptEngineManager() - val scriptEngine = manager.getEngineByName("JavaScript") - scriptEngine.put("jsonString", root.toJSONString()) - scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") - val prettyPrintedJson = scriptEngine["result"] as String - - val botRoot = JSONObject() - val botOffers = JSONArray() - - for ((item, qty) in BOT_OFFERS) { - val o = JSONObject() - o["item"] = item - o["qty"] = qty - botOffers.add(o) - } - botRoot["offers"] = botOffers - - scriptEngine.put("jsonString", botRoot.toJSONString()) - scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") - val botJson = scriptEngine["result"] as String - - - try { - FileWriter(DB_PATH).use { file -> - file.write(prettyPrintedJson) - file.flush() - file.close() - } - FileWriter(BOT_DB_PATH).use { file -> - file.write(botJson) - file.flush() - file.close() - } - } catch (e: Exception) { - e.printStackTrace() - GE_OFFER_LOCK.unlock() - } - GE_OFFER_LOCK.unlock() - } - - /** - * Dispatches an offer. - * @param player The player. - * @param offer The grand exchange offer. - * @return `True` if successful. - */ - @JvmStatic - fun dispatch(player: Player, offer: GrandExchangeOffer): Boolean { - if (offer.amount < 1) { - player.packetDispatch.sendMessage("You must choose the quantity you wish to buy!") - println("amountthing") - return false - } - if (offer.offeredValue < 1) { - player.packetDispatch.sendMessage("You must choose the price you wish to buy for!") - println("pricethng") - return false - } - if (offer.offerState != OfferState.PENDING || offer.uid != 0L) { - println("pendingthing") - return false - } - if (player.isArtificial) { - offer.playerUID = PlayerDetails.getDetails("2009scape").uid - // Repository.sendNews("2009scape wants " + offer.amount + " " + ItemDefinition.forId(offer.itemID).name.toLowerCase() + " for " + offer.offeredValue + "each.") - } else { - offer.playerUID = player.details.uid - } - offer.uid = nextUID() - offer.offerState = OfferState.REGISTERED - addEntry(offer) - offer.timeStamp = System.currentTimeMillis() - player.playerGrandExchange.update(offer) - if (offer.sell) { - Repository.sendNews(player.username + " just offered " + offer.amount + " " + ItemDefinition.forId(offer.itemID).name.toLowerCase() + " on the GE.") - } - if(player !is AIPlayer) { - SystemLogger.logTrade("[GE] ${player.username} ${if (offer.sell) "listed for sale" else "listed buy offer for"} ${offer.amount} ${ItemDefinition.forId(offer.itemID).name.toLowerCase()}") - } - dumpDatabase = true - return true - } - - /** - * Updates the offer. - * @param offer The G.E. offer to update. - */ - @JvmStatic - fun updateOffer(offer: GrandExchangeOffer) { - if (!offer.isActive) { - return - } - for (o in OFFERS_BY_ITEMID[offer.itemID]!!) { - if (o.sell != offer.sell && o.isActive) { - exchange(offer, o) - if (offer.offerState == OfferState.COMPLETED) { - break - } - } - } - buyFromBots(offer) - } - - private fun getBuylimitAmount(offer: GrandExchangeOffer): Int { - var left = offer.amountLeft - if (!offer.sell && left > 0) { - val maximum = BuyingLimitation.getMaximumBuy(offer.itemID, offer.playerUID) - if (left >= maximum) { - left = maximum - offer.isLimitation = true - } - } - return left - } - - /** - * Exchanges between 2 offers. - * @param offer The grand exchange offer to update. - * @param o The other offer to exchange with. - */ - private fun exchange(offer: GrandExchangeOffer, o: GrandExchangeOffer) { - if (o.sell == offer.sell) { - return - } - if (offer.sell && o.offeredValue < offer.offeredValue || !offer.sell && o.offeredValue > offer.offeredValue) { - return - } - var amount = min(getBuylimitAmount(offer), getBuylimitAmount(o)) - if (amount < 1) { - return - } - var coinDifference = if (offer.sell) o.offeredValue - offer.offeredValue else offer.offeredValue - o.offeredValue - if (coinDifference < 0) { - return - } - if (EconomyManagement.getEcoState() == EcoStatus.DRAINING) { - coinDifference *= (1.0 - EconomyManagement.getModificationRate()).toInt() - } - offer.completedAmount = offer.completedAmount + amount - o.completedAmount = o.completedAmount + amount - offer.offerState = if (offer.amountLeft < 1) OfferState.COMPLETED else OfferState.UPDATED - o.offerState = if (o.amountLeft < 1) OfferState.COMPLETED else OfferState.UPDATED - if (offer.sell) { - if (offer.amountLeft < 1 && offer.player != null) { - offer.player!!.audioManager.send(Audio(4042, 1, 1)) - } - addWithdraw(offer,995, amount * offer.offeredValue) - addWithdraw(o, o.itemID, amount) - BuyingLimitation.updateBoughtAmount(o.itemID, o.playerUID, amount) - } else { - if (o.amountLeft < 1 && o.player != null) { - o.player!!.audioManager.send(Audio(4042, 1, 1)) - } - addWithdraw(offer, offer.itemID, amount) - addWithdraw(o, 995, amount * o.offeredValue) - BuyingLimitation.updateBoughtAmount(offer.itemID, offer.playerUID, amount) - } - if (coinDifference > 0) { - if (offer.sell) { - addWithdraw(o, 995, coinDifference * amount) - } else { - addWithdraw(offer, 995, coinDifference * amount) - } - } - GrandExchangeDatabase.getDatabase()[o.itemID]?.influenceValue(o.offeredValue) - offer.player?.packetDispatch?.sendMessage(UPDATE_NOTIFICATION) - o.player?.packetDispatch?.sendMessage(UPDATE_NOTIFICATION) - o.player?.playerGrandExchange?.update(o) - offer.player?.playerGrandExchange?.update(offer) - dumpDatabase = true - } - - /** - * Adds a new item to withdraw. - * @param itemId The item id. - * @param amount The amount to add. - * @param abort If the item is added due to abort. - */ - fun addWithdraw(offer: GrandExchangeOffer, itemId: Int, amount: Int, abort: Boolean = false) { - if (!abort) { - if (offer.sell) { - if (itemId == 995) { - offer.totalCoinExchange += amount - } - } else { - if (itemId == 995) { - offer.totalCoinExchange -= amount - } else { - offer.totalCoinExchange += offer.offeredValue * amount - } - } - } - for (i in offer.withdraw.indices) { - if (offer.withdraw[i] == null) { - offer.withdraw[i] = Item(itemId, amount) - break - } - if (offer.withdraw[i]!!.id == itemId) { - offer.withdraw[i]!!.amount = offer.withdraw[i]!!.amount + amount - break - } - } - if (offer.player != null) { - PacketRepository.send( - ContainerPacket::class.java, - ContainerContext(offer.player, -1, -1757, 523 + offer.index, offer.withdraw, false) - ) - } - } - - /** - * Gets the next UID. - * @return The UID. - */ - private fun nextUID(): Long { - val id = offsetUID++ - return if (id == 0L) { - nextUID() - } else id - } - - private fun getItemDefPrice(itemID: Int): Int { - return max(ContentAPI.itemDefinition(itemID).getConfiguration(ItemConfigParser.GE_PRICE) ?: 0, ContentAPI.itemDefinition(itemID).value) - } - - @JvmStatic - fun getRecommendedPrice(itemID: Int, from_bot: Boolean = false): Int { - val base = max(GrandExchangeDatabase.getDatabase()[itemID]?.value ?: 0, getItemDefPrice(itemID)) - return if(from_bot) (base + (base * 0.1).toInt()) - else base - } -} diff --git a/Server/src/main/kotlin/rs09/game/ge/PlayerGrandExchange.kt b/Server/src/main/kotlin/rs09/game/ge/PlayerGrandExchange.kt deleted file mode 100644 index a2c31eeb3..000000000 --- a/Server/src/main/kotlin/rs09/game/ge/PlayerGrandExchange.kt +++ /dev/null @@ -1,700 +0,0 @@ -package rs09.game.ge - -import api.ContentAPI -import core.cache.def.impl.ItemDefinition -import core.game.component.CloseEvent -import core.game.component.Component -import core.game.component.InterfaceType -import core.game.container.Container -import core.game.container.ContainerEvent -import core.game.container.ContainerListener -import core.game.container.access.BitregisterAssembler -import core.game.container.access.InterfaceContainer -import core.game.ge.GEItemSet -import core.game.ge.GrandExchangeDatabase -import core.game.ge.GrandExchangeEntry -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.game.system.monitor.PlayerMonitor -import core.net.packet.PacketRepository -import core.net.packet.context.ConfigContext -import core.net.packet.context.ContainerContext -import core.net.packet.context.GrandExchangeContext -import core.net.packet.out.Config -import core.net.packet.out.ContainerPacket -import core.net.packet.out.GrandExchangePacket -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.rs09.consts.Components -import rs09.game.system.SystemLogger -import java.text.DecimalFormat -import java.text.NumberFormat -import java.util.* - - -/** - * Handles the grand exchange interfaces for the player. - * - * @author Emperor - * @author Angle - */ - -class PlayerGrandExchange(private val player: Player) { - - var history = arrayOfNulls(5) - - public val offers = arrayOfNulls(6) - - private var openedIndex = -1 - - public var temporaryOffer: GrandExchangeOffer? = null - - /** - * Opens the Grand Exchange menu. - * - * @NOTICE: The amount of GE boxes is limited at LoginWriteEvent.java (2 or 6 GE boxes, set it 0 for 2, 1 for 6) - */ - fun open() { - if (player.ironmanManager.checkRestriction()) { - return - } - if (!player.bankPinManager.isUnlocked) { - player.bankPinManager.openType(4) - return - } - player.interfaceManager.open(Component(Components.STOCKMARKET_105)).closeEvent = - CloseEvent { player, _ -> - temporaryOffer = null - player.packetDispatch.sendRunScript(571, "") - player.interfaceManager.closeChatbox() - player.interfaceManager.closeSingleTab() - true - } - player.packetDispatch.sendInterfaceConfig(105, 193, true) - player.packetDispatch.sendAccessMask(6, 211, 105, -1, -1) - player.packetDispatch.sendAccessMask(6, 209, 105, -1, -1) - toMainInterface() - } - - /** - * Opens the collection box. - */ - fun openCollectionBox() { - if (!player.bankPinManager.isUnlocked) { - player.bankPinManager.openType(3) - return - } - player.interfaceManager.openComponent(Components.STOCKCOLLECT_109) - player.packetDispatch.sendAccessMask(6, 18, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 23, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 28, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 36, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 44, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 52, 109, 0, 2) - for (offer in offers) { - if (offer != null) { - PacketRepository.send( - ContainerPacket::class.java, - ContainerContext(player, -1, -1757, 523 + offer.index, offer.withdraw, false) - ) - } - } - } - - /** - * Opens the history log. - * - * @param p The player to open it for. - */ - fun openHistoryLog(p: Player) { - p.interfaceManager.open(Component(643)) - for (i in history.indices) { - val o: GrandExchangeOffer? = history[i] - if (o == null) { - for (j in 0..3) { - p.packetDispatch.sendString("-", 643, 25 + i + j * 5) - } - continue - } - p.packetDispatch.sendString(if (o.sell) "You sold" else "You bought", 643, 25 + i) - p.packetDispatch.sendString( - NumberFormat.getNumberInstance(Locale.US).format(o.completedAmount.toLong()), - 643, - 30 + i - ) - p.packetDispatch.sendString(ItemDefinition.forId(o.itemID).name, 643, 35 + i) - p.packetDispatch.sendString( - NumberFormat.getNumberInstance(Locale.US).format(o.totalCoinExchange.toLong()) + " gp", 643, 40 + i - ) - } - } - - /** - * Opens the item sets interface. - */ - fun openItemSets() { - player.inventory.listeners.add(object : ContainerListener { - override fun update(c: Container, event: ContainerEvent) { - player.setAttribute( - "container-key", - InterfaceContainer.generateItems( - player, - player.inventory.toArray(), - arrayOf("Examine", "Exchange", "Components"), - 644, - 0, - 7, - 4 - ) - ) - InterfaceContainer.generateItems( - player, - GEItemSet.getItemArray(), - arrayOf("Examine", "Exchange", "Components"), - 645, - 16, - 15, - 10 - ) - } - - override fun refresh(c: Container) { - player.setAttribute( - "container-key", - InterfaceContainer.generateItems( - player, - player.inventory.toArray(), - arrayOf("Examine", "Exchange", "Components"), - 644, - 0, - 7, - 4 - ) - ) - InterfaceContainer.generateItems( - player, - GEItemSet.getItemArray(), - arrayOf("Examine", "Exchange", "Components"), - 645, - 16, - 15, - 10 - ) - } - }) - player.interfaceManager.open(Component(Components.EXCHANGE_ITEMSETS_645)).closeEvent = - CloseEvent { player, _ -> - player.inventory.listeners.removeAt(1) - player.interfaceManager.closeSingleTab() - player.removeAttribute("container-key") - true - } - player.interfaceManager.openSingleTab(Component(Components.EXCHANGE_SETS_SIDE_644)).open(player) - player.setAttribute( - "container-key", - InterfaceContainer.generateItems( - player, - player.inventory.toArray(), - arrayOf("Examine", "Exchange", "Components"), - 644, - 0, - 7, - 4 - ) - ) - InterfaceContainer.generateItems( - player, - GEItemSet.getItemArray(), - arrayOf("Examine", "Exchange", "Components"), - 645, - 16, - 15, - 10 - ) - } - - - /** - * Returns to the main interface. - */ - fun toMainInterface() { - PacketRepository.send(Config::class.java, ConfigContext(player, 1112, -1)) - PacketRepository.send(Config::class.java, ConfigContext(player, 1112, -1)) - player.interfaceManager.closeChatbox() - player.interfaceManager.closeSingleTab() - openedIndex = -1 - } - - fun parse(geData: JSONObject) { - val offersRaw = geData["offers"] - - if (offersRaw != null) { - val offersJSON = offersRaw as JSONArray - for (i in offersJSON.indices) { - val offer = offersJSON[i] as JSONObject - val index = offer["offerIndex"].toString().toInt() - if (index > offers.size - 1) { - SystemLogger.logAlert("Grand Exchange: INVALID OFFER INDEX FOR " + player.name + " INDEX: " + index + ", SKIPPING!") - SystemLogger.logAlert("IF YOU SEE THIS MESSAGE, THE GRAND EXCHANGE NEEDS TO BE FIXED.") - SystemLogger.logAlert("Check your logs, AVENGING ANGLE might have fucked up HARD and now " + player.name + "'s trade with index " + index + "is gone :(") - continue - } - OfferManager.setIndex(offer["offerUID"].toString().toLong(), index) - offers[index] = OfferManager.OFFER_MAPPING[offer["offerUID"].toString().toLong()] - update(offers[index]) - } - } - val historyRaw = geData["history"] - if(historyRaw != null){ - val history = historyRaw as JSONArray - for (i in history.indices) { - val offer = history[i] as JSONObject - var o = GrandExchangeOffer() - o.itemID = offer["itemId"].toString().toInt() - o.sell = offer["isSell"] as Boolean - o.totalCoinExchange = (offer["totalCoinExchange"].toString().toInt()) - o.completedAmount = (offer["completedAmount"].toString().toInt()) - history[i] = o - } - } - } - - /** - * Initializes the grand exchange. - */ - fun init() { - // Were trades made while gone? - var updated = false - for (offer in offers) { - if (offer != null) { - offer.player = player - if (!updated && (offer.withdraw[0] != null || offer.withdraw[1] != null)) { - updated = true - } - update(offer) - } - } - if (updated) { - player.packetDispatch.sendMessage("You have items from the Grand Exchange waiting in your collection box.") - } - } - - /** - * Updates the client with the grand exchange data. - */ - fun update() { - for (offer in offers) { - update(offer) - } - } - - /** - * Updates a grand exchange offer. - * - * @param offer The offer to update. - */ - fun update(offer: GrandExchangeOffer?) { - if (offer != null) { - PacketRepository.send( - GrandExchangePacket::class.java, - GrandExchangeContext(player, offer.index.toByte(), offer.offerState.ordinal.toByte(), offer.itemID.toShort(), - offer.sell, offer.offeredValue, offer.amount, offer.completedAmount, offer.totalCoinExchange) - ) - } - } - - /** - * Constructs a new buy offer. - * - * @param itemId The item id. - */ - fun constructBuy(itemId: Int) { - if (openedIndex < 0) { - return - } - temporaryOffer = GrandExchangeOffer() - temporaryOffer!!.itemID = itemId - temporaryOffer!!.sell = false - var itemDb = GrandExchangeDatabase.getDatabase()[itemId] - if (itemDb == null || !ContentAPI.itemDefinition(itemId).isTradeable) { - player.packetDispatch.sendMessage("This item has been blacklisted from the Grand Exchange.") - return - } - temporaryOffer!!.player = player - temporaryOffer!!.amount = 1 - temporaryOffer!!.offeredValue = OfferManager.getRecommendedPrice(itemId) - temporaryOffer!!.index = openedIndex - sendConfiguration(temporaryOffer, false) - } - - // GrandExchangeDatabase.getDatabase()[offer?.itemID] - - /** - * Constructs a new sale offer. - * - * @param item The item to sell. - */ - fun constructSale(item: Item) { - if (openedIndex < 0 || offers[openedIndex] != null) { - return - } - if (item.id == 995) { - player.packetDispatch.sendMessage("You can't offer money!") - return - } - var id = item.id - if (!item.definition.isUnnoted) { - id = item.noteChange - } - var itemDb = GrandExchangeDatabase.getDatabase()[id] - if (itemDb == null || !item.definition.isTradeable) { - player.packetDispatch.sendMessage("This item can't be sold on the Grand Exchange.") - return - } - temporaryOffer = GrandExchangeOffer() - temporaryOffer!!.itemID = id - temporaryOffer!!.sell = true - temporaryOffer!!.player = player - temporaryOffer!!.offeredValue = OfferManager.getRecommendedPrice(id) - temporaryOffer!!.amount = item.amount - temporaryOffer!!.index = openedIndex - sendConfiguration(temporaryOffer, true) - } - - companion object { - /** - * Gets the total amount of this item in the inventory (including noted - * version). - * - * @param player The player. - * @param itemId the item id. - * @return The amount of items + notes in the inventory. - */ - @JvmStatic - fun getInventoryAmount(player: Player, itemId: Int): Int { - val item = Item(itemId) - var amount = player.inventory.getAmount(item) - if (item.definition.noteId > -1) { - amount += player.inventory.getAmount(Item(item.definition.noteId)) - } - return amount - } - } - - /** - * Confirms the current offer. - */ - fun confirmOffer() { - if (openedIndex < 0 || temporaryOffer == null || temporaryOffer!!.amount == 0) { - return - } - if (temporaryOffer!!.offeredValue < 1) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You can't make an offer for 0 coins.") - return - } - if (temporaryOffer!!.amount > Int.MAX_VALUE / temporaryOffer!!.offeredValue) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You can't " + (if (temporaryOffer!!.sell) "sell " else "buy ") + " this much!") - return - } - temporaryOffer!!.index = openedIndex - if (temporaryOffer!!.sell) { - val maxAmount = getInventoryAmount(player, temporaryOffer!!.itemID) - if (temporaryOffer!!.amount > maxAmount) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You do not have enough of this item in your inventory to cover the") - player.packetDispatch.sendMessage("offer.") - return - } - var item: Item - val amountLeft: Int = - temporaryOffer!!.amount - player.inventory.getAmount(Item(temporaryOffer!!.itemID)) - val remove = player.inventory.remove(Item(temporaryOffer!!.itemID, temporaryOffer!!.amount).also { - item = it - }) - var note: Int - if (amountLeft > 0) { - if (item.noteChange.also { note = it } > 0) { - player.inventory.remove(Item(note, amountLeft)) - } else if (remove) { - player.inventory.add(Item(temporaryOffer!!.itemID, temporaryOffer!!.amount - amountLeft)) - return - } - } - if (OfferManager.dispatch(player, temporaryOffer!!)) { - offers[openedIndex] = temporaryOffer - OfferManager.updateOffer(temporaryOffer!!) - } - } else { - val total: Int = temporaryOffer!!.amount * temporaryOffer!!.offeredValue - if (total > player.inventory.getAmount(Item(995))) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You do not have enough coins to cover the offer.") - return - } - if (OfferManager.dispatch(player, temporaryOffer!!) && player.inventory.remove(Item(995, total))) { - offers[openedIndex] = temporaryOffer - OfferManager.updateOffer(temporaryOffer!!) - } - } - player.monitor.log( - (if (temporaryOffer!!.sell) "selling" else "buying") + " offer => item => " + ItemDefinition.forId( - temporaryOffer!!.itemID - ).name + " => amount => " + temporaryOffer!!.amount + " => price => " + temporaryOffer!!.offeredValue, - PlayerMonitor.GRAND_EXCHANGE_LOG - ) - toMainInterface() - player.audioManager.send(Audio(4043, 1, 1)) - temporaryOffer = null - } - - /** - * Aborts an offer. - * - * @param index The offer index. - */ - fun abort(index: Int) { - val offer: GrandExchangeOffer? = offers[index] - player.packetDispatch.sendMessage("Abort request acknowledged. Please be aware that your offer may") - player.packetDispatch.sendMessage("have already been completed.") - if (offer == null || !offer.isActive) { - return - } - offer.offerState = OfferState.ABORTED - if (offer.sell) { - OfferManager.addWithdraw(offer, offer.itemID, offer.amountLeft, true) - } else { - OfferManager.addWithdraw(offer, 995, offer.amountLeft * offer.offeredValue, true) - } - update(offer) - player.monitor.log( - "aborted offer => item => " + ItemDefinition.forId(offer.itemID).name + " => amount => " + offer.amount + "", - PlayerMonitor.GRAND_EXCHANGE_LOG - ) - OfferManager.dumpDatabase = true - } - - /** - * Removes an offer. - * - * @param index The offer index. - */ - fun remove(index: Int): Boolean { - if (offers[index] == null) { - return false - } - var offer: GrandExchangeOffer = offers[index]!! - if (offer.completedAmount > 0) { - val newHistory = arrayOfNulls(5) - newHistory[0] = offer - System.arraycopy(history, 0, newHistory, 1, 4) - history = newHistory - player.monitor.log( - "offer removed => item => " + ItemDefinition.forId(offer.itemID).name + " => amount => " + offer.amount + " => amount_left => " + offer.amountLeft + " => completed_amount => " + offer.completedAmount + "", - PlayerMonitor.GRAND_EXCHANGE_LOG - ) - } - offer.withdraw = arrayOfNulls(2) - var didExist = OfferManager.removeEntry(offer) - offer.uid = 0 - offer.offerState = OfferState.REMOVED - offers[index] = null - update(offer) - toMainInterface() - return didExist - } - - fun hasActiveOffer(): Boolean { - for (i in offers) { - if (i != null) - return true - } - return false - } - - - /** - * Sends the configuration packets for the offer. - * - * @param offer The grand exchange offer. - * @param sell If it's a selling offer. - */ - fun sendConfiguration(offer: GrandExchangeOffer?, sell: Boolean) { - var entry: GrandExchangeEntry? = GrandExchangeDatabase.getDatabase()[offer?.itemID] - var examine: String? = "" - val formatter = DecimalFormat("###,###,###,###") - val text = StringBuilder() - var lowestOfferValue = 0 - var totalAmounts = 0 - val foundOffers: MutableList = ArrayList() - val foundAmounts: MutableList = ArrayList() - if (offer != null) { - examine = ItemDefinition.forId(offer.itemID).examine - var count = 0 - if (!offer.sell) { - for (o in OfferManager.getOffersForItem(offer.itemID)) { - if (o != null) if (o.sell && o.isActive) { - foundOffers.add(o.offeredValue) - foundAmounts.add(o.amountLeft) - count++ - } - } - val botSales = OfferManager.amtBotsSelling(offer.itemID) - if (botSales > 0) { - foundAmounts.add(botSales) - foundOffers.add(OfferManager.getRecommendedPrice(offer.itemID, true)) - count++ - } - if (foundOffers.isNotEmpty()) { - lowestOfferValue = Collections.min(foundOffers) - totalAmounts = foundAmounts.stream().mapToInt { obj: Int -> obj }.sum() - } - if (count == 0) text.append("There are currently no sell offers for this item.") - else text.append("Lowest price: ") - .append(formatter.format(lowestOfferValue.toLong())).append(" gp") - .append("
Quantity: ") - .append(formatter.format(totalAmounts.toLong())).append("
Users: ") - .append(formatter.format(count.toLong())) - } - } - player.packetDispatch.sendString(if (offer != null && !offer.sell) text.toString() else examine, 105, 142) - var lowPrice = 0 - var highPrice = 0 - val recommendedPrice = OfferManager.getRecommendedPrice(entry?.itemId ?: 0) - if (entry != null) { - 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, openedIndex).send(player) - player.varpManager.get(1113).setVarbit(0, if (sell) 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) - if (offer != null) { - PacketRepository.send( - ContainerPacket::class.java, - ContainerContext(player, -1, -1757, 523 + offer.index, offer.withdraw, false) - ) - } - } - - /** - * Opens the search interface. - */ - fun openSearch() { - val c = Component(389) - c.definition.type = InterfaceType.CS_CHATBOX - c.closeEvent = CloseEvent { player, c -> - player.packetDispatch.sendRunScript(571, "") - true - } - player.packetDispatch.sendRunScript(570, "s", "Grand Exchange Item Search") - player.interfaceManager.openChatbox(c) - } - - /** - * Opens the buying screen. - * - * @param index The offer index. - */ - fun openBuy(index: Int) { - openedIndex = index - sendConfiguration(offers[index], false) - openSearch() - } - - /** - * Opens the selling screen. - */ - fun openSell(index: Int) { - openedIndex = index - sendConfiguration(offers[index], true) - player.interfaceManager.openSingleTab(Component(Components.STOCKSIDE_107)).open(player) - player.packetDispatch.sendRunScript( - 149, "IviiiIsssss", "", "", "", "Examine", "Offer", - -1, 0, 7, 4, 93, 7012370 - ) - BitregisterAssembler.send(player, 107, 18, 0, 27, BitregisterAssembler(0, 1)) - } - - /** - * Withdraws an item. - * - * @param offer The offer to withdraw from. - * @param index The item index. - */ - fun withdraw(offer: GrandExchangeOffer, index: Int) { - val item = offer.withdraw[index] ?: return - if (player.inventory.getMaximumAdd(item) < item.amount) { - val note = item.noteChange - if (note == -1 || player.inventory.getMaximumAdd(Item(note)) < item.amount) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You do not have enough room in your inventory.") - return - } - player.inventory.add(Item(note, item.amount)) - } else { - player.inventory.add(item) - } - offer.withdraw[index] = null - if (!offer.isActive && offer.withdraw[0] == null && offer.withdraw[1] == null) { - remove(offer.index) - } - player.audioManager.send(Audio(4040, 1, 1)) - PacketRepository.send( - ContainerPacket::class.java, - ContainerContext(player, -1, -1757, 523 + offer.index, offer.withdraw, false) - ) - OfferManager.dumpDatabase = true - } - - /** - * Gets the currently opened offer. - * - * @return The grand exchange offer currently opened. - */ - fun getOpenedOffer(): GrandExchangeOffer? { - return if (openedIndex < 0) { - null - } else offers[openedIndex] - } - - /** - * Views a registered offer. - * - * @param index The index. - */ - fun view(index: Int) { - if (offers[index] == null) { - return - } - openedIndex = index - sendConfiguration(offers[index], false) - } - - /** - * Formats the grand exchange. - * - * @return the formatted offer for the SQL database. - */ - fun format(): String? { - var log = "" - for (offer in offers) { - if (offer != null) { - log += offer.itemID.toString() + "," + offer.amount + "," + offer.sell + "|" - } - } - if (log.isNotEmpty() && log[log.length - 1] == '|') { - log = log.substring(0, log.length - 1) - } - return log - } - - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/CorporealBeastWarningInterface.kt b/Server/src/main/kotlin/rs09/game/interaction/CorporealBeastWarningInterface.kt deleted file mode 100644 index 4772e322a..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/CorporealBeastWarningInterface.kt +++ /dev/null @@ -1,35 +0,0 @@ -package rs09.game.interaction - -import core.game.component.Component -import core.game.node.entity.player.Player -import rs09.game.world.GameWorld - -/** - * Handles the corporeal beast warning interface - * @author Ceikry - */ -class CorporealBeastWarningInterface : InterfaceListener(){ - - val COMPONENT_ID = 650 - - override fun defineListeners() { - on(COMPONENT_ID,17){player,component,_,_,_,_ -> - 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) - } else { - close(player,component) - } - return@on true - } - - on(COMPONENT_ID){player, component, _, _, _, _ -> - close(player,component) - return@on true - } - } - - fun close(player: Player,component: Component){ - player.interfaceManager.close(component) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt deleted file mode 100644 index ffc9ae3c3..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt +++ /dev/null @@ -1,56 +0,0 @@ -package rs09.game.interaction - -import core.game.node.Node -import core.game.node.entity.Entity -import core.game.node.entity.player.Player -import core.game.world.map.Location - -abstract class InteractionListener : Listener{ - companion object { - val ITEM = 0 - val SCENERY = 1 - val NPC = 2 - val GROUNDITEM = 3 - } - fun on(id: Int, type: Int, vararg option: String,handler: (player: Player, node: Node) -> Boolean){ - InteractionListeners.add(id,type,option,handler) - } - fun on(ids: IntArray, type: Int, vararg option: String, handler: (player: Player, node: Node) -> Boolean){ - InteractionListeners.add(ids,type,option,handler) - } - fun on(option: String, type: Int, handler: (player: Player, node: Node) -> Boolean){ - InteractionListeners.add(option,type,handler) - } - fun on(type: Int, vararg option: String, handler: (player: Player, node: Node) -> Boolean){ - InteractionListeners.add(option,type,handler) - } - fun onUseWith(type: Int, used: Int, vararg with: Int, handler: (player: Player, used: Node, with: Node) -> Boolean){ - InteractionListeners.add(type,used,with,handler) - } - fun onUseWith(type: Int, used: IntArray, vararg with: Int, handler: (player: Player, used: Node, with: Node) -> Boolean){ - InteractionListeners.add(type,used,with,handler) - } - fun onEquip(id: Int, handler: (player: Player, node: Node) -> Boolean){ - InteractionListeners.addEquip(id,handler) - } - fun onUnequip(id:Int, handler: (player: Player, node: Node) -> Boolean){ - InteractionListeners.addUnequip(id,handler) - } - - open fun defineDestinationOverrides(){} - - fun setDest(type: Int, id: Int,handler: (Entity, Node) -> Location){ - InteractionListeners.addDestOverride(type,id,handler) - } - fun setDest(type:Int, vararg options: String, handler: (Entity, Node) -> Location){ - InteractionListeners.addDestOverrides(type,options,handler) - } - - fun setDest(type: Int, ids: IntArray, vararg options: String, handler: (Entity, Node) -> Location){ - InteractionListeners.addDestOverrides(type,ids,options,handler) - } - - fun onDig(location: Location,method: (player: Player) -> Unit){ - SpadeDigListener.registerListener(location,method) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt deleted file mode 100644 index 2a73fdeee..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt +++ /dev/null @@ -1,217 +0,0 @@ -package rs09.game.interaction - -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.interaction.Option -import core.game.node.Node -import core.game.node.entity.Entity -import core.game.node.entity.player.Player -import core.game.world.map.Location -import rs09.game.system.SystemLogger - -object InteractionListeners { - private val listeners = HashMap Boolean>(1000) - private val useWithListeners = HashMap Boolean>(1000) - private val destinationOverrides = HashMap Location>(100) - private val equipListeners = HashMap Boolean>(10) - - @JvmStatic - fun add(id: Int, type: Int, option: Array, method: (Player,Node) -> Boolean){ - for(opt in option) { - val key = "$id:$type:${opt.toLowerCase()}" - listeners[key] = method - } - } - - @JvmStatic - fun add(ids: IntArray, type: Int, option: Array, method: (Player,Node) -> Boolean){ - for(id in ids){ - add(id,type,option,method) - } - } - - @JvmStatic - fun add(option: String,type: Int, method: (Player,Node) -> Boolean){ - val key = "$type:${option.toLowerCase()}" - listeners[key] = method - } - - @JvmStatic - fun add(options: Array,type: Int,method: (Player, Node) -> Boolean){ - for(opt in options){ - add(opt.toLowerCase(),type,method) - } - } - - @JvmStatic - fun add(used: Int, with: Int, type: Int, method: (Player,Node,Node) -> Boolean){ - useWithListeners["$used:$with:$type"] = method - } - - @JvmStatic - fun add(type: Int, used: Int, with: IntArray, method: (Player, Node, Node) -> Boolean){ - for(id in with){ - useWithListeners["$used:$id:$type"] = method - } - } - - @JvmStatic - fun addEquip(id: Int,method: (Player, Node) -> Boolean){ - equipListeners["equip:$id"] = method - } - - @JvmStatic - fun addUnequip(id: Int, method: (Player,Node) -> Boolean){ - equipListeners["unequip:$id"] = method - } - - @JvmStatic - fun getEquip(id: Int): ((Player,Node) -> Boolean)? { - return equipListeners["equip:$id"] - } - - @JvmStatic - fun getUnequip(id: Int): ((Player,Node) -> Boolean)? { - return equipListeners["unequip:$id"] - } - - @JvmStatic - fun get(used: Int, with: Int, type: Int): ((Player,Node,Node) -> Boolean)?{ - return useWithListeners["$used:$with:$type"] - } - - @JvmStatic - fun get(id: Int, type: Int, option: String): ((Player,Node) -> Boolean)?{ - return listeners["$id:$type:${option.toLowerCase()}"] - } - - @JvmStatic - fun get(option: String,type: Int): ((Player,Node) -> Boolean)?{ - return listeners["$type:${option.toLowerCase()}"] - } - - @JvmStatic - fun addDestOverride(type: Int, id: Int, method: (Entity,Node) -> Location){ - destinationOverrides["$type:$id"] = method - } - - @JvmStatic - fun addDestOverrides(type: Int,options: Array, method: (Entity,Node) -> Location){ - for(opt in options){ - destinationOverrides["$type:${opt.toLowerCase()}"] = method - } - } - - @JvmStatic - fun addDestOverrides(type: Int, ids: IntArray,options: Array, method: (Entity,Node) -> Location){ - for(id in ids){ - for(opt in options){ - destinationOverrides["$type:$id:${opt.toLowerCase()}"] = method - } - } - } - - @JvmStatic - fun getOverride(type: Int, id:Int, option: String): ((Entity, Node) -> Location)?{ - return destinationOverrides["$type:$id:${option.toLowerCase()}"] - } - - @JvmStatic - fun getOverride(type: Int,id: Int): ((Entity,Node) -> Location)?{ - return destinationOverrides["$type:$id"] - } - - @JvmStatic - fun getOverride(type: Int,option: String): ((Entity,Node) -> Location)?{ - return destinationOverrides["$type:$option"] - } - - @JvmStatic - fun run(id: Int, player: Player, node: Node, isEquip: Boolean): Boolean{ - if(isEquip){ - return equipListeners["equip:$id"]?.invoke(player,node) ?: true - } else { - return equipListeners["unequip:$id"]?.invoke(player,node) ?: true - } - } - - @JvmStatic - fun run(used: Node, with: Node, type: Int,player: Player): Boolean{ - val flag = when(type){ - 2 -> DestinationFlag.ENTITY - 1 -> DestinationFlag.OBJECT - else -> DestinationFlag.OBJECT - } - - if(player.locks.isInteractionLocked) return false - - var flipped = false - - val method = get(used.id,with.id,type) ?: get(with.id,used.id,type).also { flipped = true } ?: return false - val destOverride = if(flipped) { - getOverride(type, used.id, "use") ?: getOverride(type, with.id) ?: getOverride(type, "use") - } else { - getOverride(type, with.id, "use") ?: getOverride(type, used.id) ?: getOverride(type, "use") - } - - - if(type != 0) { - if(player.locks.isMovementLocked) return false - player.pulseManager.run(object : MovementPulse(player, with, flag, destOverride) { - override fun pulse(): Boolean { - if (player.zoneMonitor.useWith(used.asItem(), with)) { - return true - } - player.faceLocation(with.location) - if(flipped) method.invoke(player,with,used) - else method.invoke(player,used,with) - return true - } - }) - } else { - if(flipped) method.invoke(player,with,used) - else method.invoke(player,used,with) - } - return true - } - - @JvmStatic - fun run(id: Int, type: Int, option: String, player: Player, node: Node): Boolean{ - val flag = when(type){ - 3 -> DestinationFlag.ITEM - 2 -> DestinationFlag.ENTITY - 1 -> DestinationFlag.OBJECT - else -> DestinationFlag.OBJECT - } - - if(player.locks.isInteractionLocked) return false - - val method = get(id,type,option) ?: get(option,type) ?: return false - val destOverride = getOverride(type, id, option) ?: getOverride(type,node.id) ?: getOverride(type,option.toLowerCase()) - - player.setAttribute("interact:option", option) - - if(type != 0) { - 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) - method.invoke(player,node) - return true - } - }) - } else { - method.invoke(player,node) - } - return true - } - - fun add(type: Int, used: IntArray, with: IntArray, handler: (Player, Node, Node) -> Boolean) { - for(u in used){ - for (w in with){ - useWithListeners["$u:$w:$type"] = handler - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/InterfaceListener.kt b/Server/src/main/kotlin/rs09/game/interaction/InterfaceListener.kt deleted file mode 100644 index b7f38e092..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/InterfaceListener.kt +++ /dev/null @@ -1,19 +0,0 @@ -package rs09.game.interaction - -import core.game.component.Component -import core.game.node.entity.player.Player - -abstract class InterfaceListener : Listener { - fun on(componentID: Int, buttonID: Int, handler: (player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int) -> Boolean){ - InterfaceListeners.add(componentID,buttonID,handler) - } - fun on(componentID: Int, handler: (player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int) -> Boolean){ - InterfaceListeners.add(componentID,handler) - } - fun onOpen(componentID: Int,handler: (player: Player, component: Component) -> Boolean){ - InterfaceListeners.addOpenListener(componentID, handler) - } - fun onClose(componentID: Int,handler: (player: Player, component: Component) -> Boolean){ - InterfaceListeners.addCloseListener(componentID,handler) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/ItemOnBankBooth.kt b/Server/src/main/kotlin/rs09/game/interaction/ItemOnBankBooth.kt deleted file mode 100644 index 80cbc35cd..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/ItemOnBankBooth.kt +++ /dev/null @@ -1,54 +0,0 @@ -package rs09.game.interaction - -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.interaction.NodeUsageEvent -import core.game.interaction.UseWithHandler -import core.game.node.item.Item -import core.plugin.Plugin - -class ItemOnBankBooth : UseWithHandler(0) { - - override fun handle(event: NodeUsageEvent?): Boolean { - event ?: return false - val player = event.player - player.pulseManager.run(object : MovementPulse(player, event.usedWith, DestinationFlag.OBJECT) { - override fun pulse(): Boolean { - return handleNote(event) - } - }, "movement") - return true - } - - fun handleNote(event: NodeUsageEvent?): Boolean { - event ?: return false - val item = event.usedItem - val player = event.player - if (item.noteChange != item.id) { - if (item.definition.isUnnoted) { - val amount = player.inventory.getAmount(item.id) - player.inventory.remove(Item(item.id, amount)) - player.inventory.add(Item(item.noteChange, amount)) - } else { - var amount = item.amount - if (amount > player.inventory.freeSlots()) { - amount = player.inventory.freeSlots() - } - player.inventory.remove(Item(item.id, amount)) - player.inventory.add(Item(item.noteChange, amount)) - } - return true - } else { - player.sendMessage("This item can't be noted.") - return true - } - } - - override fun newInstance(arg: Any?): Plugin { - return this - } - - override fun nodeAllowed(nodeId: Int): Boolean { - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/Listener.kt b/Server/src/main/kotlin/rs09/game/interaction/Listener.kt deleted file mode 100644 index 9d6f51fd4..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/Listener.kt +++ /dev/null @@ -1,5 +0,0 @@ -package rs09.game.interaction - -interface Listener { - fun defineListeners() -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/QCRepository.kt b/Server/src/main/kotlin/rs09/game/interaction/QCRepository.kt deleted file mode 100644 index 6bc7547d2..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/QCRepository.kt +++ /dev/null @@ -1,380 +0,0 @@ -package rs09.game.interaction - -import core.cache.Cache -import core.cache.def.impl.ItemDefinition -import core.cache.def.impl.NPCDefinition -import core.cache.misc.buffer.ByteBufferUtils -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.net.amsc.MSPacketRepository -import core.net.amsc.WorldCommunicator -import rs09.game.world.GameWorld.Pulser -import rs09.net.packet.`in`.QCPacketType -import java.nio.ByteBuffer - - -/** - * Handles the sending of quick chat messages and string-replacement for specific messages. - * @author Ceikry - */ -//TODO: A lot of the menus defined in the massive arrays at the bottom need to be rearranged because the order they appear in the menu and the order they are actually in is quite different. Start clicking through the "can you cook me..." menu to see what I mean. -//TODO: I've done the groundwork, Please someone else take up the task of rearranging these menus -object QCRepository { - - val skillIDs = mapOf( - 0 to "Agility", - 1 to "Attack", - 2 to "Construction", - 3 to "Cooking", - 4 to "Crafting", - 5 to "Defence", - 6 to "Farming", - 7 to "Firemaking", - 8 to "Fishing", - 9 to "Fletching", - 10 to "Herblore", - 11 to "Hitpoints", - 12 to "Hunter", - 13 to "Thieving", - 14 to "Summoning", - 15 to "Strength", - 16 to "Smithing", - 19 to "Slayer", - 20 to "Runecrafting", - 21 to "Ranged", - 22 to "Prayer", - 23 to "Mining", - 24 to "Magic", - 25 to "Woodcutting" - ) - - private val quickChatIndex = Cache.getIndexes()[24] - - /** - * The entry method that connects to the other more specific methods - */ - @JvmStatic - fun sendQC(player: Player?, multiplier: Int?, offset: Int?, packetType: QCPacketType, selection_a_index: Int, selection_b_index: Int, forClan: Boolean){ - val index = getIndex(offset, multiplier) - player?.setAttribute("qc_offset", offset) //Slapping this in an attribute because it's only useful for one or two messages - val qcString = when(packetType){ - QCPacketType.SINGLE -> getSingleQC(player, index, selection_a_index) - QCPacketType.DOUBLE -> getDoubleQC(player, index, selection_a_index, selection_b_index) - QCPacketType.STANDARD -> getStandardQC(player, index) - else -> "" - } - - - if(forClan) { - if (WorldCommunicator.isEnabled()) { - MSPacketRepository.sendClanMessage(player, qcString) - } else { - player?.communication?.clan?.message(player, qcString) - } - } else { - val ctx = ChatMessage(player, qcString, 0, qcString.length) - ctx.isQuickChat = true - Pulser.submit(object : Pulse(0, player) { - override fun pulse(): Boolean { - player!!.updateMasks.register(ChatFlag(ctx)) - return true - } - }) - } - } - - /** - * For standard quick chat messages with no string replacements - */ - @JvmStatic - fun getStandardQC(player: Player?, index: Int): String{ - var qcString = getQCString(index) - - //XP to next level - if (qcString.contains("to get my next")) { - val offset = player?.getAttribute("qc_offset", 0) ?: 0 - val skillName = skillIDs[offset - 103] - val skill = Skills.getSkillByName(skillName?.toUpperCase()) - val playerXP = player?.skills?.getExperience(skill) - val playerLevel = player?.skills?.getStaticLevel(skill) - val nextXP = player?.skills?.getExperienceByLevel(playerLevel?.plus(1) ?: 1) - qcString = qcString.replace("<", "${(nextXP?.minus(playerXP ?: 0.0) ?: 0.0).toInt()}") - } - - //My X level is - else if (qcString.contains("level is")) { - val skillName = qcString.split(" ")[1] - val skill = Skills.getSkillByName(skillName.toUpperCase()) - val level = player?.skills?.getStaticLevel(skill) - qcString = qcString.replace("<", level.toString()) - } - - //My current slayer assignment is - else if(qcString.contains("My current Slayer assignment is")){ - val amount = player?.slayer?.amount - val taskName = NPCDefinition.forId(player?.slayer?.task?.ids?.get(0) ?: 0).name.toLowerCase() - if(amount ?: 0 > 0){ - qcString = qcString.replace("complete", "$amount $taskName") - } - } - - return qcString - } - - - /** - * For Single-replacement quick chat messages with one replacement selection - */ - @JvmStatic - fun getSingleQC(player: Player?, index: Int, selectionIndex: Int): String{ - var qcString = getQCString(index) - - //Messages that include items - if ((qcString.contains("item") || qcString.contains("Can I buy your") || qcString.contains("What is the best world to buy") || qcString.contains("What is the best world to sell") || qcString.contains("Would you like to borrow") || qcString.contains("Could I please borrow") ) && qcString.contains("<") ){ - val itemName = ItemDefinition.forId(selectionIndex).name - qcString = qcString.replace("<", itemName) - } - - //Loan duration - else if (qcString.contains("I'd like the loan duration to be")){ - qcString = when(selectionIndex){ - 0 -> qcString.replace("<", "just until one of us logs out") - else -> qcString.replace("<", "${selectionIndex} hours") - } - } - - //Go to agility course / Try training your agility at - else if (qcString.contains("Let's go to Agility course:") || qcString.contains("Try training your Agility at:")){ - qcString = qcString.replace("<", AGILITY_COURSES[selectionIndex]) - } - - //Try training on - else if(qcString.contains("Try training on")){ - qcString = qcString.replace("<", TRAIN_MELEE_NPCS[selectionIndex]) - } - - //Try ranging - else if(qcString.contains("Try ranging")){ - qcString = qcString.replace("<", TRAIN_RANGE_NPCS[selectionIndex]) - } - - //flat-pack - else if(qcString.contains("flat-pack")){ - qcString = qcString.replace("<", FLATPAKS[selectionIndex]) - } - - //Cooking - else if(qcString.contains("I'm cooking") || qcString.contains("Would you please cook me") || qcString.contains("Try cooking")){ - qcString = qcString.replace("<", COOK_ITEMS[selectionIndex]) - } - - //Crafting - else if(qcString.contains("I am crafting") || qcString.contains("Try crafting") || qcString.contains("Would you please craft me")){ - qcString = qcString.replace("<", CRAFT_ITEMS[selectionIndex]) - } - - //Farming - else if(qcString.contains("I'm growing crop") || qcString.contains("Try growing crop")){ - qcString = qcString.replace("<", FARM_CROPS[selectionIndex]) - } - - //I'm burning logs... - else if(qcString.contains("I'm burning logs")){ - qcString = qcString.replace("<", FM_LOGS[selectionIndex]) - } - - //Try burning logs at - else if(qcString.contains("Try burning logs at")){ - qcString = qcString.replace("<", FM_LOCATIONS[selectionIndex]) - } - - //Fishing - else if(qcString.contains("I'm fishing") || qcString.contains("Would you please fish me") || qcString.contains("Try fishing for")){ - qcString = qcString.replace("<", FISH[selectionIndex]) - } - - //Fletching - else if(qcString.contains("I'm fletching") || qcString.contains("Try fletching") || qcString.contains("Would you please fetch me")){ - qcString = qcString.replace("<", FLETCH_ITEMS[selectionIndex]) - } - - //Herblore - else if(qcString.contains("I'm mixing potion") || qcString.contains("Would you please mix me a potion") || qcString.contains("Try mixing potion")){ - qcString = qcString.replace("<", HERB_POTIONS[selectionIndex]) - } - - //Herblore secondaries - else if(qcString.contains("Where can I get the secondary ingredient")){ - qcString = qcString.replace("<", HERB_SECONDARIES[selectionIndex]) - } - - //Hunter - else if(qcString.contains("Would you please hunt me") || qcString.contains("Try hunting")){ - qcString = qcString.replace("<", HUNTER_ANIMALS[selectionIndex]) - } - - //Casting spell - else if(qcString.contains("I'm casting spell") || qcString.contains("Would you please cast")){ - qcString = qcString.replace("<", MAGIC_SPELLS[selectionIndex]) - } - - //Spellbook - else if(qcString.contains("I am on spell book")){ - qcString = qcString.replace("<", MAGIC_BOOKS[selectionIndex]) - } - - //Mining ore - else if(qcString.contains("I'm mining ore")){ - qcString = qcString.replace("<", ORES[selectionIndex]) - } - - //Pickaxe using - else if(qcString.contains("I'm using a pick")){ - qcString = qcString.replace("<", PICKAXE_TYPES[selectionIndex]) - } - - //Try mining at - else if(qcString.contains("Try mining at")){ - qcString = qcString.replace("<", MINING_LOCATIONS[selectionIndex]) - } - - //Use your prayer - else if(qcString.contains("Use your prayer")){ - qcString = qcString.replace("<", PRAYERS[selectionIndex]) - } - - //i'm going to craft rune - else if(qcString.contains("I'm going to craft rune")){ - qcString = qcString.replace("<", RUNES[selectionIndex]) - } - - //try crafting runes at - else if(qcString.contains("Try crafting runes at")){ - qcString = qcString.replace("<", RC_LOCATIONS[selectionIndex]) - } - - //Slayer master in - else if(qcString.contains("You should use the Slayer master in")){ - qcString = qcString.replace("<", SLAYER_LOCATIONS[selectionIndex]) - } - - //Spare slayer equipment - else if(qcString.contains("Do you have spare Slayer equipment")){ - qcString = qcString.replace("<", SLAYER_EQUIPMENT[selectionIndex]) - } - - //familiars - else if(qcString.contains("I like the familiar") || qcString.contains("I can summon up to")){ - qcString = qcString.replace("<", FAMILIARS[selectionIndex]) - } - - //charm droppers - else if(qcString.contains("Good charm droppers are")){ - qcString = qcString.replace("<", CHARM_DROPPERS[selectionIndex]) - } - - //Thieving - else if(qcString.contains("Try thieving from")){ - qcString = qcString.replace("<", THIEVE_TARGETS[selectionIndex]) - } - - //Axe made of - else if(qcString.contains("I'm using a woodcutting axe made")){ - qcString = qcString.replace("<", AXE_TYPES[selectionIndex]) - } - - //Try training woodcutting at - else if(qcString.contains("Try training Woodcutting at")){ - qcString = qcString.replace("<", WOODCUTTING_LOCATIONS[selectionIndex]) - } - - //Nice level in - else if(qcString.contains("Nice level in")){ - qcString = qcString.replace("<", SKILL_NAMES[selectionIndex]) - } - - - return qcString - } - - - /** - * For Double-replacement quick chat messages with 2 selection replacements. - */ - @JvmStatic - fun getDoubleQC(player: Player?, index: Int, selection_a_index: Int, selection_b_index: Int): String{ - var qcString = getQCString(index) - - //Giving directions: That is _ of _ - if(qcString.contains("That is < of <")){ - qcString = qcString.replaceFirst("<", DIRECTIONS[selection_a_index]) - qcString = qcString.replaceFirst("<", DIR_LOCATIONS[selection_b_index]) - } - - //Smithing - else if(qcString.contains("I am smithing") || qcString.contains("Try smithing") || qcString.contains("Would you please smith me")){ - qcString = qcString.replaceFirst("<", SMITHING_BARTYPES[selection_a_index]) - qcString = qcString.replaceFirst("<", SMITHING_PRODUCTS[selection_b_index]) - } - - return qcString - } - - private fun getIndex(offset: Int?, multiplier: Int?): Int{ - offset ?: return 0 - multiplier ?: return 0 - //for some reason it subtracts only from 512 if it's negative (Jagex idk) or 256 if type is 0 but not from 768 if type is 3? - return if(offset >= 0) { - (256 * multiplier) + offset - }else { - when(multiplier){ - 0 -> 256 + offset - 1 -> 512 + offset - 2 -> 768 + offset - else -> 1024 + offset - } - } - } - - private fun getQCString(index: Int): String{ - return ByteBufferUtils.getString(ByteBuffer.wrap(QCRepository.quickChatIndex.getFileData(1, index))) - } -} - -private val AGILITY_COURSES: Array = arrayOf("Agility Pyramid", "Ape Atoll", "Barbarian Outpost", "Brimhaven Agility Arena", "Dorgesh-Kaan", "Gnome Stronghold Course", "Penguin Course", "Werewolf Agility Course", "Wilderness Course", "Gnome Ball", "Werewolf Skullball") -private val TRAIN_MELEE_NPCS: Array = arrayOf("black demons", "blue dragons", "chickens", "cows", "cyclopes", "dagannoths", "experiments", "fire giants", "goblins", "guards", "hill giants", "ice giants", "lesser demons", "moss giants", "Slayer creatures") -private val TRAIN_RANGE_NPCS: Array = arrayOf("barbarians", "black demons", "blue dragons", "chickens", "cows", "dark wizards", "dwarves", "fire giants", "goblins", "guards", "hill giants", "lesser demons", "moss giants", "rats", "zombies") -private val FLATPAKS: Array = arrayOf("Asgarnian Ale barrel", "beer barrel", "carved oak bench", "carved oak dining table", "carved oak magic wardrobe", "carved teak bench", "carved teak magic wardrobe", "carved teak table", "Chef's Delight barrel", "cider barrel", "crude wooden chair", "Dragon Bitter barrel", "fancy teak dresser", "four-poster bed", "gilded bench", "gilded cape rack", "gilded clock", "gilded dresser", "gilded four-poster bed", "gilded magic wardrobe", "gilded wardrobe", "Greenman's Ale barrel", "large oak bed", "large teak bed", "magical cape rack", "mahogany armchair", "mahogany armour case", "mahogany bench", "mahogany bookcase", "mahogany cape rack", "mahogany costume box", "mahogany dresser", "mahogany magic wardrobe", "mahogany table", "mahogany toy box", "mahogany treasure chest", "mahogany wardrobe", "marble cape rack", "marble magic wardrobe", "oak armchair", "oak armour case", "oak bed", "oak bench", "oak bookcase", "oak cape rack", "oak chair", "oak clock", "oak costume box", "oak dining table", "oak drawers", "oak dresser", "oak kitchen table", "oak magic wardrobe", "oak shaving stand", "oak toy box", "oak treasure chest", "oak wardrobe", "opulent table", "rocking chair", "shaving stand", "shoe box", "teak armchair", "teak armour case", "teak bed", "teak cape rack", "teak clock", "teak costume box", "teak dining bench", "teak drawers", "teak dresser", "teak kitchen table", "teak magic wardrobe", "teak table", "teak toy box", "teak treasure chest", "teak wardrobe", "wooden bed", "wooden bench", "wooden bookcase", "wooden chair", "wooden dining table", "wooden kitchen table") -private val COOK_ITEMS: Array = arrayOf("admiral pie", "anchovies", "anchovy pizza", "apple pie", "bass", "bear meat", "bread", "bird meat", "cake", "cave eel", "cheese and tomato batta", "chicken", "chilli con carne", "chilli potato", "choc chip crunchies", "chocolate bomb", "chocolate cake", "chompy", "cod", "cooked pawya", "cooked rabbit", "crab meat", "crayfish", "curry", "egg and tomato", "egg and tomato potato", "fat snail", "fish pie", "fishcake", "fried mushrooms", "fried onions", "fruit batta", "garden pie", "herring", "jubbly", "karambwan", "karambwanji", "lava eel", "lean snail", "leaping salmon", "leaping sturgeon", "leaping trout", "lobster", "mackerel", "manta ray", "meat", "meat pie", "meat pizza", "monkfish", "mud pie", "mushroom and onion", "mushroom and onion potato", "oomlie wrap", "pawya", "pike", "pineapple pizza", "pitta bread", "plain pizza", "potato", "rabbit", "rainbow fish", "rat meat", "redberry pie", "roasted beast meat", "roasted rabbit", "baked potato with butter", "salmon", "sardine", "baked potato", "scrambled egg", "sea turtle", "seaweed", "shark", "shrimp", "slimy eel", "spice crunchies", "spicy meat", "spicy sauce", "spider on a stick", "stew", "summer pie", "sweetcorn", "swordfish", "tangled toads' legs", "thin snail", "toad batta", "toad crunchies", "trout", "tuna", "tuna and corn", "tuna and corn potato", "ugthanki kebab", "ugthanki meat", "vegball", "vegetable ball", "vegetable batta", "wild pie", "worm batta", "worm crunchies", "worm hole") -private val CRAFT_ITEMS: Array = arrayOf("air battlestaves", "balls of wool", "baskets", "beer glasses", "black dragonhide bodies", "black dragonhide chaps", "black dragonhide vambraces", "black spiked vambraces", "blue dragonhide bodies", "blue dragonhide chaps", "blue dragonhide vambraces", "blue spiked vambraces", "bowls", "bowstrings", "bullseye lanterns", "candle lanterns", "cloth", "coifs", "crossbow strings", "diamond amulets", "diamond bracelets", "diamond necklaces", "diamond rings", "diamonds", "Dorgeshuun light orbs", "dragonstone amulets", "dragonstone bracelets", "dragonstone necklaces", "dragonstone rings", "dragonstones", "earth battlestaves", "emerald amulets", "emerald bracelets", "emerald necklaces", "emerald rings", "emeralds", "feather headdresses", "fire battlestaves", "fishbowls", "fremennik round shields", "glass orbs", "gold amulets", "gold bracelets", "gold necklaces", "gold rings", "green dragonhide bodies", "green dragonhide chaps", "green dragonhide vambraces", "green spiked vambraces", "hard leather bodies", "holy symbols", "leather bodies", "leather boots", "leather chaps", "leather cowls", "leather gloves", "leather vambraces", "lightning conductors", "limestone bricks", "magic string", "oil lamps", "oil lanterns", "onyx", "onyx amulets", "onyx bracelets", "onyx necklaces", "onyx rings", "pie dishes", "plant pots", "pot lids", "pots", "red dragonhide bodies", "red dragonhide chaps", "red dragonhide vambraces", "red spiked vambraces", "rope", "rubies", "ruby amulets", "ruby bracelets", "ruby necklaces", "ruby rings", "sacks", "sapphire amulets", "sapphire bracelets", "sapphire necklaces", "sapphire rings", "sapphires", "silver bolts", "silver sickles", "snakeskin bandannas", "snakeskin bodies", "snakeskin boots", "snakeskin chaps", "snakeskin vambraces", "snelms", "spiked vambraces", "studded bodies", "studded leather chaps", "tiaras", "unholy symbols", "unpowered orbs", "vials", "water battlestaves", "yak-hide bodies", "yak-hide legs") -private val FARM_CROPS: Array = arrayOf("apples", "Asgarnian hops", "avantoe", "bananas", "barley", "bittercap mushrooms", "cabbages", "cacti", "cadantine", "cadavaberries", "calquats", "coconuts", "curry leaves", "deadly nightshade", "dwarf weed", "dwellberries", "evil turnips", "goutweed", "guam", "hammerstone hops", "harralander", "irit", "jade vine", "jangerberries", "jute fiber", "Krandorian hops", "kwuarm", "lantadyme", "limpwurt roots", "magic trees", "maple trees", "marigolds", "marrentill", "nasturtium", "oak trees", "onions", "oranges", "papayas", "pineapples", "poison ivy berries", "potatoes", "ranarr", "redberries", "rosemary", "snapdragon", "spirit trees", "spirit weed", "strawberries", "sweetcorn", "tarromin", "toadflax", "tomatoes", "torstol", "watermelons", "whiteberries", "wildblood hops", "willow trees", "woad leaves", "Yanillian hops", "yew trees") -private val FM_LOGS: Array = arrayOf("achey", "arctic pine", "eucalyptus", "logs", "magic", "mahogany", "maple", "oak", "teak", "willow", "yew") -private val FM_LOCATIONS: Array = arrayOf("Draynor Village", "Falador east bank", "Fishing Colony", "Oo'glog", "Port Phasmatys", "Yanille") -private val FISH: Array = arrayOf("anchovies", "bass", "caskets", "cave eel", "cod", "crayfish", "frog spawn", "herring", "karambwan", "karambwanji", "lava eel", "leaping salmon", "leaping sturgeon", "leaping trout", "lobster", "mackerel", "manta ray", "monkfish", "oysters", "pike", "rainbow fish", "salmon", "sardine", "sea turtle", "seaweed", "shark", "shrimp", "slimy eel", "swordfish", "trout", "tuna") -private val FLETCH_ITEMS: Array = arrayOf("adamant arrows", "adamant bolts", "adamant brutal arrows", "adamant crossbows", "adamant darts", "arrow shafts", "barbed bolts", "black brutal arrows", "blurite bolts", "blurite crossbows", "bronze arrows", "bronze bolts", "bronze brutal arrows", "bronze crossbows", "bronze darts", "composite ogre bows", "crossbow stocks", "diamond bolts", "dragon arrows", "dragon darts", "dragonstone bolts", "emerald bolts", "flighted ogre arrows", "headless arrows", "iron arrows", "iron bolts", "iron brutal arrows", "iron crossbows", "iron darts", "jade bolts", "longbows", "magic longbows", "magic shortbows", "mahogany crossbow stocks", "maple crossbow stocks", "maple longbows", "maple shortbows", "mithril arrows", "mithril bolts", "mithril brutal arrows", "mithril crossbows", "mithril darts", "mithril grapples", "oak crossbow stocks", "oak longbows", "oak shortbows", "ogre arrow shafts", "ogre arrows", "onyx bolts", "opal bolts", "pearl bolts", "red topaz bolts", "ruby bolts", "rune arrows", "rune bolts", "rune brutal arrows", "rune crossbows", "rune darts", "sapphire bolts", "shortbows", "silver bolts", "steel arrows", "steel bolts", "steel brutal arrows", "steel crossbows", "steel darts", "teak crossbow stocks", "willow crossbow stocks", "willow longbows", "willow shortbows", "yew crossbow stocks", "yew longbows", "yew shortbows") -private val HERB_POTIONS: Array = arrayOf("Agility potion", "Anti fire-breath potion", "Antipoison", "Attack potion", "Blamish oil", "Combat potion", "Defence potion", "Energy potion", "Fishing potion", "Guthix Balance", "Hunter potion", "Magic essence potion", "Magic potion", "Prayer restore", "Ranging potion", "Relicym's Balm", "Restore potion", "Sanfew Serum", "Saradomin Brew", "Strength potion", "Summoning potion", "Super antipoison", "Super antipoison+", "Super antipoison++", "Super Attack potion", "Super Defence potion", " Super energy potion", "Super fishing explosive", "Super restore potion", "Super Strength potion", "Weapon poison", "Weapon poison+", "Weapon poison++", "Zamorak Brew") -private val HERB_SECONDARIES: Array = arrayOf("blamish snail slime", "cactus spine", "chocolate dust", "cockatrice egg", "crushed nest", "deadly nightshade", "eye of newt", "garlic", "ground blue dragon scale", "ground goat's horn", "ground gorak claw", "ground unicorn horn", "jangerberries", "kebbit teeth dust", "limpwurt root", "magic tree roots", "Mort Myre fungi", "nail beast nails", "poison ivy berries", "potato cactus", "red spiders' eggs", "rubium", "silver dust", "snake weed", "snape grass", "toad's legs", "white berries", "wine of Zamorak", "yew roots") -private val HUNTER_ANIMALS: Array = arrayOf("baby impling", "barbtailed kebbit", "black salamander", "black warlock", "cerulean twitch", "chinchompa", "common kebbit", "copper longtail", "crimson swift", "dark kebbit", "dashing kebbit", "desert devil", "dragon impling", "earth impling", "eclectic impling", "essence impling", "Feldip weasel", "ferret", "golden warbler", "gourmet impling", "grenwall", "horned graahk", "imp", "magpie impling", "nature impling", "ninja impling", "orange salamander", "pawya", "polar kebbit", "prickly kebbit", "rabbit", "razorbacked kebbit", "red chinchompa", "red salamander", "ruby harvest", "sabretooth kebbit", "sabretooth kyatt", "sapphire glacialis", "snowy knight", "spined larupia", "spotted kebbit", "swamp lizard", "tropical wagtail", "wild kebbit", "young impling") -private val MAGIC_SPELLS: Array = arrayOf("Annakarl Teleport", "Ape Atoll Teleport", "Ardougne Teleport", "Bake Pie", "Barbarian Teleport", "Bind", "Blood Barrage", "Blood Blitz", "Blood Burst", "Blood Rush", "Bones to Bananas", "Bones to Peaches", "Boost Potion Share", "Bounty Locate", "Camelot Teleport", "Carrallangar Teleport", "Catherby Teleport", "Charge", "Charge Air Orb", "Charge Earth Orb", "Charge Fire Orb", "Charge Water Orb", "Claws of Guthix", "Confuse", "Crumble Undead", "Cure Group", "Cure Me", "Cure Other", "Cure Plant", "Curse", "Dareeyak Teleport", "Dream", "Earth Blast", "Earth Bolt", "Earth Strike", "Earth Wave", "Enchant 1 - Sapphire", "Enchant 2 - Emerald", "Enchant 3 - Ruby", "Enchant 4 - Diamond", "Enchant 5 - Dragonstone", "Enchant 6 - Onyx", "Enchant Bolt", "Energy Transfer", "Enfeeble", "Entangle", "Falador Teleport", "Fertilise Soil", "Fire Blast", "Fire Bolt", "Fire Strike", "Fire Wave", "Fishing Guild Teleport", "Flames of Zamorak", "Ghorrock Teleport", "Heal Group", "Heal Other", "High Level Alchemy", "Home Teleport", "House Teleport", "Humidify", "Hunter Kit", "Iban Blast", "Ice Barrage", "Ice Blitz", "Ice Burst", "Ice Plateau Teleport", "Ice Rush", "Kharyll Teleport", "Khazard Teleport", "Lassar Teleport", "Low Level Alchemy", "Lumbridge Teleport", "Magic Dart", "Magic Imbue", "Monster Examine", "Moonclan Teleport", "NPC Contact", "Ourania Teleport", "Paddewwa Teleport", "Plank Make", "Saradomin Strike", "Senntisten Teleport", "Shadow Barrage", "Shadow Blitz", "Shadow Burst", "Shadow Rush", "Smoke Barrage", "Smoke Blitz", "Smoke Burst", "Smoke Rush", "Snare", "Spellbook Swap", "Stat Restore Pot Share", "Stat Spy", "String Jewellery", "Stun", "Superglass Make", "Superheat Item", "Tele Group Barbarian", "Tele Group Catherby", "Tele Group Fishing Guild", "Tele Group Ice Plateau", "Tele Group Khazard", "Tele Group Moonclan", "Tele Group Waterbirth", "Telegrab", "Teleother Camelot", "Teleother Falador", "Teleother Lumbridge", "Trollheim Teleport", "Varrock Teleport", "Vengeance", "Vengeance Other", "Vulnerability", "Watchtower Teleport", "Water Blast", "Water Bolt", "Water Strike", "Water Wave", "Waterbirth Teleport", "Weaken", "Wind Blast", "Wind Bolt", "Wind Strike", "Wind Wave") -private val MAGIC_BOOKS: Array = arrayOf("Standard", "Ancients", "Lunar") -private val ORES: Array = arrayOf("adamantite", "blurite", "clay", "coal", "copper", "elemental", "gold", "granite", "iron", "limestone", "mithril", "obsidian", "pure essence", "rubium", "rune essence", "runite", "sandstone", "silver", "tin") -private val MINING_LOCATIONS: Array = arrayOf("Ardougne south", "Barbarian village", "Brimhaven north-west", "Brimhaven south-west", "Brimhaven volcano gold mine", "Coal Trucks", "Crafting Guild", "Crandor Isle", "Dorgeshuun mine", "Dwarven Mine", "East of Ardougne", "Edgeville", "Edgeville dungeon mine", "Elemental Workshop", "Fremennik Isles mine", "Grand Tree", "Haunted Mine", "Heroes' Guild", "Ice Caves", "Keldagrim", "Kharid Scorpion chasm", "Lumbridge Caves", "Lumbridge swamp", "Lunar Isle", "Mining Guild", "North of Yanille", "Piscatoris", "Port Khazard", "Rimmington", "Shilo Village gem mine", "TzHaar City", "Varrock south-east", "Varrock south-west", "Wilderness Hobgoblins' mine", "Wilderness Lava Maze", "Wilderness Pirates' mine", "Wilderness runite mine") -private val PRAYERS: Array = arrayOf("Burst of Strength", "Chivalry", "Clarity of Thought", "Eagle Eye", "Hawk Eye", "Improved Reflexes", "Incredible Reflexes", "Mystic Lore", "Mystic Might", "Mystic Will", "Piety", "Protect from Magic", "Protect from Melee", "Protect from Missiles", "Protect from Summoning", "Protect Item", "Rapid Heal", "Rapid Restore", "Redemption", "Retribution", "Rock Skin", "Sharp Eye", "Smite", "Steel Skin", "Superhuman Strength", "Thick Skin", "Ultimate Strength") -private val RUNES: Array = arrayOf("air runes", "astral runes", "blood runes", "body runes", "chaos runes", "cosmic runes", "death runes", "dust runes", "earth runes", "fire runes", "lava runes", "law runes", "mind runes", "mist runes", "mud runes", "nature runes", "smoke runes", "soul runes", "steam runes", "water runes") -private val RC_LOCATIONS: Array = arrayOf("Air Altar", "Astral Altar", "Blood Altar", "Body Altar", "Chaos Altar", "Cosmic Altar", "Death Altar", "Earth Altar", "Fire Altar", "Law Altar", "Mind Altar", "Nature Altar", "Ourania Altar", "Water Altar") -private val SLAYER_LOCATIONS: Array = arrayOf("Burthorpe", "Canifis", "Edgeville Dungeon", "Pollnivneach", "Shilo Village", "Zanaris") -private val FAMILIARS: Array = arrayOf("abyssal lurker", "abyssal parasite", "abyssal titan", "adamant minotaur", "albino rat", "arctic bear", "barker toad", "beaver", "bloated leech", "bronze minotaur", "bull ant", "bunyip", "compost mound", "desert wyrm", "dreadfowl", "evil turnip", "fire titan", "forge regent", "fruit bat", "geyser titan", "giant chinchompa", "giant ent", "granite crab", "granite lobster", "honey badger", "hydra", "ibis", "ice titan", "iron minotaur", "iron titan", "karamthulhu overlord", "lava titan", "macaw", "magpie", "mithril minotaur", "moss titan", "obsidian golem", "pack yak", "phoenix", "praying mantis", "pyrelord", "ravenous locust", "rune minotaur", "smoke devil", "spirit cobra", "spirit cockatrice", "spirit coraxatrice", "spirit dagannoth", "spirit graahk", "spirit guthatrice", "spirit jelly", "spirit kalphite", "spirit kyatt", "spirit larupia", "spirit mosquito", "spirit pengatrice", "spirit saratrice", "spirit scorpion", "spirit spider", "spirit terrorbird", "spirit Tz-Kih", "spirit vulatrice", "spirit wolf", "spirit zamatrice", "steel minotaur", "steel titan", "stranger plant", "swamp titan", "talon beast", "thorny snail", "unicorn stallion", "vampire bat", "void ravager", "void shifter", "void spinner", "void torcher", "war tortoise", "wolpertinger") -private val CHARM_DROPPERS: Array = arrayOf("basilisks", "bloodvelds", "dust devils", "fire giants", "ice giants", "ice warriors", "jogres", "ogres", "rock crabs", "rock lobsters", "waterfiends", "wolves") -private val THIEVE_TARGETS: Array = arrayOf("bearded Pollnivnian bandits", "cave goblins", "chests", "desert bandits", "elves", "farmers", "Fremenniks", "gnomes", "guards", "H.A.M. members", "heroes", "knights", "master farmers", "men and women", "Menaphite thugs", "paladins", "Pollnivnian bandits", "rogues", "stalls", "warriors", "watchmen") -private val PICKAXE_TYPES: Array = arrayOf("adamant", "bronze", "iron", "mithril", "rune", "steel") -private val AXE_TYPES: Array = arrayOf("adamant", "black", "bronze", "dragon", "iron", "mithril", "rune", "steel") -private val WOODCUTTING_LOCATIONS: Array = arrayOf("Ape Atoll", "Draynor", "Edgeville", "Gnome Stronghold", "Isafdar", "Kharazi Jungle", "Mage Training Area", "Neitiznot", "Piscatoris", "Seers' Village", "Seers' Village cemetery", "Sinclair Mansion", "Sorcerer's Tower", "South of Castle Wars", "South of Falador", "South of the Lumberyard", "Tai Bwo Bwannai", "Uzer", "Varrock Palace", "West of Barbarian Assault", "West of Catherby", "West of Lumbridge Castle", "West of Oo'glog") -private val SLAYER_EQUIPMENT: Array = arrayOf("a bug lantern", "a crystal chime", "a face mask", "a mirror shield", "a nosepeg", "a Slayer bell", "a spiny helmet", "a witchwood icon", "an enchanted gem", "bags of salt", "broad arrows", "earmuffs", "Fishing explosives", "fungicide spray", "ice coolers", "insulated boots", "leaf-bladed spear", "rock hammers", "Slayer gloves", "Slayer staff", "super fishing explosive") -private val DIRECTIONS: Array = arrayOf("north", "south", "east", "west", "north-east", "north-west", "south-east", "south-west") -private val DIR_LOCATIONS: Array = arrayOf("Al Kharid", "Ape Atoll", "Ardougne (East)", "Ardougne (West)", "Barbarian Village", "Braindeath Island", "Brimhaven", "BUrdh de Rott", "Burthorpe", "Camelot", "Canifis", "Catherby", "Crandor", "Dorgesh-Kaan", "Draynor Manor", "Draynor Village", "Edgeville", "Entrana", "Etceteria", "Falador", "Goblin Village", "God Wars Dungeon", "Grand Exchange", "Gu'Tanoth", "Harmony Island", "Hemenster", "Isafdar", "Jatizso", "Kalphite Lair", "Karamja", "Keldagrim", "Lletya", "Lumbridge", "Lunar Isle", "Meiyerditch", "Miscellania", "Mort'ton", "Mos Le Harmless", "Nardah", "Neitiznot", "Oo'glog", "Paterdomus", "Piscatoris", "Pollnivneach", "Port Khazard", "Port Phasmatys", "Port Sarim", "Rellekka", "Rimmington", "Seers' Village", "Shilo Village", "Sophanem", "Tai Bwo Wannai Village", "Taverley", "Tree Gnome Stronghold", "Tree Gnome Village", "Troll Stronghold", "TzHaar city", "Varrock", "Waterbirth Island", "Witchaven", "Yanille", "Zanaris") -private val SKILL_NAMES: Array = arrayOf("Agility", "Attack", "Construction", "Cooking", "Crafting", "Defence", "Farming", "Firemaking", "Fishing", "Fletching", "Herblore", "Hitpoints", "Hunter", "Magic", "Mining", "Prayer", "Ranged", "Runecrafting", "Slayer", "Smithing", "Strength", "Summoning", "Thieving", "Woodcutting") -private val SMITHING_BARTYPES: Array = arrayOf("adamant", "blurite", "bronze", "iron", "item", "mithril", "rune", "steel") -private val SMITHING_PRODUCTS: Array = arrayOf("arrow tips", "axes", "battleaxes", "bolts", "bullseye lantern frames", "cannonballs", "chainbodies", "claws", "crossbow limbs", "daggers", "dart tips", "full helms", "grapple tips", "hastae", "kite shields", "longswords", "maces", "medium helms", "nails", "oil lantern frames", "pickaxes", "platebodies", "platelegs", "plateskirts", "scimitars", "short swords", "spears", "spits", "square shields", "studs", "throwing knives", "two-handed swords", "warhammers", "wire") \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/FairyRingInterface.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/FairyRingInterface.kt deleted file mode 100644 index 1d22efd58..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/FairyRingInterface.kt +++ /dev/null @@ -1,199 +0,0 @@ -package rs09.game.interaction.inter - -import api.ContentAPI -import core.game.component.Component -import core.game.content.dialogue.FacialExpression -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.map.Location -import core.game.world.map.RegionManager -import core.tools.RandomFunction -import rs09.game.interaction.InterfaceListener -import rs09.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') - -/** - * Handles the fairy ring interface - * @author Ceikry - */ -class FairyRingInterface : InterfaceListener(){ - - val RINGS = 734 - val TRAVEL_LOG = 735 - - override fun defineListeners() { - - 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) - return@onOpen true - } - - onClose(RINGS){player, _ -> - ContentAPI.closeTabInterface(player) - player.removeAttribute("fr:ring1") - player.removeAttribute("fr:ring2") - player.removeAttribute("fr:ring3") - ContentAPI.clearVarp(player, 816) - ContentAPI.setVarbit(player, 816, 0, 0) - ContentAPI.closeTabInterface(player) - return@onClose true - } - - on(RINGS){player, _, _, buttonID, _, _ -> - if(player.getAttribute("fr:time",0L) > System.currentTimeMillis()) return@on true - var delayIncrementer = 1750L - when(buttonID){ - 23 -> delayIncrementer += increment(player,1) - 25 -> delayIncrementer += increment(player,2) - 27 -> delayIncrementer += 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, _, _, _, _, _ -> - 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) { - ContentAPI.setVarbit(player, 816, 0, ring1index) - player.setAttribute("fr:ring2",0) - player.setAttribute("fr:ring3",0) - } - 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 - } - - 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) - } - - 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 code = "${RING_1[ring1index]}${RING_2[ring2index]}${RING_3[ring3index]}" - val ring: FairyRing? = FairyRing.valueOf(code.toUpperCase()) - var tile = ring?.tile - if(ring == FairyRing.CIP){ - ContentAPI.sendDialogue(player, "The ring seems to reject you.") - } - if (ring == null || tile == null) { - val center = Location(2412, 4434, 0) - tile = if (RandomFunction.random(2) == 1) { - center.transform(RandomFunction.random(2, 6), RandomFunction.random(2, 6), 0) - } else { - center.transform(RandomFunction.random(-2, -6), RandomFunction.random(-2, -6), 0) - } - if (!RegionManager.isTeleportPermitted(tile) || RegionManager.getObject(tile) != null) { - tile = Location.create(2412, 4431, 0) - } - GameWorld.Pulser.submit(object : Pulse(4, player) { - override fun pulse(): Boolean { - ContentAPI.sendPlayerDialogue(player, "Wow, fairy magic sure is useful, I hardly moved at all!", FacialExpression.AMAZED) - return true - } - }) - } else { - if (!player.savedData.globalData.hasTravelLog(ring.ordinal)) { - player.savedData.globalData.setTravelLog(ring.ordinal) - } - } - ContentAPI.closeInterface(player) - ContentAPI.teleport(player, tile!!, TeleportManager.TeleportType.FAIRY_RING) - } -} - -enum class FairyRing(val tile: Location?, val tip: String = "", val childId: Int = -1) { - AIQ(Location.create(2996, 3114, 0), "Asgarnia: Mudskipper Point", 15), - AIR(Location.create(2700, 3247, 0), "Islands: South of Witchaven", 16), - AJQ(Location.create(2735, 5221, 0), "Dungeons: Dark cave south of Dorgesh-Kaann", 19), - ALR(Location.create(3059, 4875, 0), "Other realms: Abyssal Area", 28), - AJR(Location.create(2780, 3613, 0), "Kandarin: Slayer cave south-east of Rellekka", 20), - AJS(Location.create(2500, 3896, 0), "Islands: Penguins near Miscellania", 21), - AKQ(Location.create(2319, 3619, 0), "Kandarin: Piscatoris Hunter area", 23), - 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), - ALS(Location.create(2644, 3495, 0), "Kandarin: McGrubor's Wood", 29), - BIP(Location.create(3410, 3324, 0), "Islands: River Salve", 30), - BIQ(Location.create(3251, 3095, 0), "Kharidian Desert: Near Kalphite hive", 31), - BIS(Location.create(2635, 3266, 0), "Kandarin: Ardougne Zoo unicorns", 33), - BJR(null, "Other Realms: Realm of the Fisher King", 36), - BKP(Location.create(2385, 3035, 0), "Feldip Hills: South of Castle Wars", 38), - BKQ(Location.create(3041, 4532, 0), "Other realms: Enchanted Valley", 39), - BKR(Location.create(3469, 3431, 0), "Morytania: Mort Myre, south of Canifis", 40), - BLP(Location.create(2437, 5126, 0), "Dungeons: TzHaar area", 42), - BLQ(null, "Yu'biusk", 43),//Location.create(2229, 4244, 1) - BLR(Location.create(2740, 3351, 0), "Kandarin: Legends' Guild", 44), - CIP(null, "Islands: Miscellania", 46), //Location.create(2513, 3884, 0) - CIQ(Location.create(2528, 3127, 0), "Kandarin: North-west of Yanille", 47), - CJR(Location.create(2705, 3576, 0), "Kandarin: Sinclair Mansion", 52), - CKP(Location.create(2075, 4848, 0), "Other realms: Cosmic Entity's plane", 54), - CKR(Location.create(2801, 3003, 0), "Karamja: South of Tai Bwo Wannai Village", 56), - CKS(Location.create(3447, 3470, 0), "Morytania: Canifis", 57), - CLP(Location.create(3082, 3206, 0), "Islands: South of Draynor Village", 58), - CLS(Location.create(2682, 3081, 0), "Islands: Jungle spiders near Yanille", 61), - DIR(Location.create(3038, 5348, 0), "Other realms: Goraks' Plane", 64), - DIS(Location.create(3108, 3149, 0), "Misthalin: Wizards' Tower", 65), - DJP(Location.create(2658, 3230, 0), "Kandarin: Tower of Life", 66), - DJR(Location.create(2676, 3587, 0), "Kandarin: Sinclair Mansion (west)", 68), - DKP(Location.create(2900, 3111, 0), "Karamja: South of Musa Point", 70), - DKR(Location.create(3129, 3496, 0), "Misthalin: Edgeville", 72), - DKS(Location.create(2744, 3719, 0), "Kandarin: Snowy Hunter area", 73), - DLQ(Location.create(3423, 3016, 0), "Kharidian Desert: North of Nardah", 75), - DLR(Location.create(2213, 3099, 0), "Islands: Poison Waste south of Isafdar", 76), - AIS(null), AIP(null), AKP(null), FAIRY_HOME(Location.create(2412, 4434, 0)); - - 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 - } - ContentAPI.setInterfaceText(player, "
${ring.tip}", 735, ring.childId) - } - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/MainGameInterface.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/MainGameInterface.kt deleted file mode 100644 index c4b36792a..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/MainGameInterface.kt +++ /dev/null @@ -1,113 +0,0 @@ -package rs09.game.interaction.inter - -import api.ContentAPI -import core.game.component.CloseEvent -import core.game.component.Component -import core.game.content.quest.tutorials.tutorialisland.TutorialSession -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 org.rs09.consts.Components -import rs09.game.interaction.InterfaceListener -import rs09.game.world.GameWorld.settings - -class MainGameInterface : InterfaceListener() { - val TOPLEVEL = Components.TOPLEVEL_548 - val TOPLEVEL_FS = Components.TOPLEVEL_FULLSCREEN_746 - val RUN_BUTTON = Components.TOPSTAT_RUN_750 - val FILTER_BUTTONS = Components.FILTERBUTTONS_751 - val REPORT_ABUSE = Components.SNAPSHOT_MAIN_553 - - override fun defineListeners() { - on(FILTER_BUTTONS){player, _, _, buttonID, _, _ -> - if(buttonID == 27) - openReport(player) - return@on true - } - - on(RUN_BUTTON){player, _, _, _, _, _ -> - player.settings.toggleRun() - return@on true - } - - on(TOPLEVEL_FS){player, _, _, buttonID, _, _ -> - when (buttonID) { - 12 -> ContentAPI.setInterfaceText(player, - "When you have finished playing " + settings!!.name + ", always use the button below to logout safely. ", - 182, - 0 - ) - 49 -> ContentAPI.setInterfaceText(player, - "Friends List - " + settings!!.name + " " + settings!!.worldId, - 550, - 3 - ) - 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 (TutorialSession.getExtension(player).stage > TutorialSession.MAX_STAGE) { - if (player.getExtension(WeaponInterface::class.java) === WeaponInterfaces.STAFF) { - val c = Component(WeaponInterfaces.STAFF.interfaceId) - player.interfaceManager.openTab(0, c) - val inter = player.getExtension(WeaponInterface::class.java) - inter.updateInterface() - } - } - } - 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 - ) - } - return@on true - } - } - - fun openReport(player: Player) { - player.interfaceManager.open(Component(REPORT_ABUSE)).closeEvent = - CloseEvent { player1: Player, c: Component? -> - player1.packetDispatch.sendRunScript(80, "") - player1.packetDispatch.sendRunScript(137, "") - true - } - player.packetDispatch.sendRunScript(508, "") - if (player.details.rights !== Rights.REGULAR_PLAYER) { - for (i in 0..17) { - player.packetDispatch.sendInterfaceConfig(553, i, false) - } - } - } - - private fun configureWorldMap(player: Player) { - if (player.inCombat()) { - player.packetDispatch.sendMessage("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.") - return - } - player.interfaceManager.openWindowsPane(Component(755)) - val posHash = player.location.z shl 28 or (player.location.x shl 14) or player.location.y - player.packetDispatch.sendScriptConfigs(622, posHash, "", 0) - player.packetDispatch.sendScriptConfigs(674, posHash, "", 0) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/NPCContactInterface.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/NPCContactInterface.kt deleted file mode 100644 index 0f3ded10f..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/NPCContactInterface.kt +++ /dev/null @@ -1,85 +0,0 @@ -package rs09.game.interaction.inter - -import core.game.node.entity.npc.NPC -import core.tools.RandomFunction -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InterfaceListener - -class NPCContactInterface : InterfaceListener() { - val NPCs = arrayOf(org.rs09.consts.NPCs.HONEST_JIMMY_4362, org.rs09.consts.NPCs.BERT_3108, org.rs09.consts.NPCs.ADVISOR_GHRIM_1375, org.rs09.consts.NPCs.TURAEL_8273, org.rs09.consts.NPCs.LANTHUS_1526, org.rs09.consts.NPCs.SUMONA_7780, org.rs09.consts.NPCs.MAZCHNA_8274, org.rs09.consts.NPCs.DURADEL_8275, org.rs09.consts.NPCs.VANNAKA_1597, org.rs09.consts.NPCs.MURPHY_463, org.rs09.consts.NPCs.CHAELDAR_1598, org.rs09.consts.NPCs.CYRISUS_432, org.rs09.consts.NPCs.LARRY_5424) - val DialogueFiles = arrayOf( - /*TODO("Honest Jimmy"), - TODO("Bert"), - TODO("ADVISOR GHRIM"), - TODO("Turael"), - TODO("LANTHUS"), - TODO("Sumona"), - TODO("Mazchna"), - TODO("Duradel"), - TODO("Vannaka"), - TODO("Murphy"), - TODO("Chaeldar"), - TODO("Cyrisus"), - TODO("Larry")*/ - ) - val INTER = 429 - override fun defineListeners() { - - //Remove a bunch of the buttons/heads so that people don't - //waste runes on spells that aren't implemented - //TODO: Re-enable NPCs as their respective content gets added - onOpen(INTER){player, component -> - //Honest Jimmy: Trouble Brewing - player.packetDispatch.sendInterfaceConfig(429,10,true) - player.packetDispatch.sendInterfaceConfig(429,38,true) - - //Bert the Sandman: Hand in the Sand quest - player.packetDispatch.sendInterfaceConfig(429,11,true) - player.packetDispatch.sendInterfaceConfig(429,39,true) - - //Advisor Ghrim: Kingdoms of Miscellania - player.packetDispatch.sendInterfaceConfig(429,12,true) - player.packetDispatch.sendInterfaceConfig(429,40,true) - - //Lanthus: Castle Wars - player.packetDispatch.sendInterfaceConfig(429,17,true) - player.packetDispatch.sendInterfaceConfig(429,43,true) - - //Sumona: Completion of Smoking Kills - player.packetDispatch.sendInterfaceConfig(429,27,true) - player.packetDispatch.sendInterfaceConfig(429,42,true) - - return@onOpen true - } - - on(INTER){player, _, _, buttonID, _, _ -> - var index = when(buttonID){ - 10,38 -> 0 - 11,39 -> 1 - 12,40 -> 2 - 13,41 -> 3 - 17,43 -> 4 - 27,42 -> 5 - 18,44 -> 6 - 23,45 -> 7 - 28,46 -> 8 - 30,47 -> 9 - 29,48 -> 10 - 32,33 -> 11 - 34,49 -> 12 - else -> -1 - } - if(index == -1) index = RandomFunction.random(NPCs.size) - val dialogueFile = DialogueFiles.getOrNull(index) - player.getAttribute<() -> Unit>("contact-caller")?.invoke() - player.interfaceManager.close() - if(dialogueFile != null){ - player.dialogueInterpreter.open(dialogueFile, NPC(NPCs[index])) - } else { - player.dialogueInterpreter.open(NPCs[index]) - } - return@on true - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/SilverInterface.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/SilverInterface.kt deleted file mode 100644 index dd86e0f0b..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/inter/SilverInterface.kt +++ /dev/null @@ -1,100 +0,0 @@ -package rs09.game.interaction.inter - -import api.ContentAPI -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.entity.skill.crafting.SilverProduct -import core.game.system.task.Pulse -import org.rs09.consts.Components -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InterfaceListener - -class SilverInterface : InterfaceListener() { - - val IFACE = Components.CRAFTING_SILVER_CASTING_438 - val ANIM = ContentAPI.getAnimation(899) - - override fun defineListeners() { - onOpen(IFACE){player, _ -> - ContentAPI.sendItemOnInterface(player, IFACE, 17, 1718) - ContentAPI.sendItemOnInterface(player, IFACE, 24, 1724) - ContentAPI.sendItemOnInterface(player, IFACE, 31, 2961) - ContentAPI.sendItemOnInterface(player, IFACE, 38, 4201) - ContentAPI.sendItemOnInterface(player, IFACE, 45, 5525) - ContentAPI.sendItemOnInterface(player, IFACE, 53, 7637) - ContentAPI.sendItemOnInterface(player, IFACE, 60, 6748) - ContentAPI.sendItemOnInterface(player, IFACE, 67, 9382) - ContentAPI.sendItemOnInterface(player, IFACE, 74, 13154) - return@onOpen true - } - - on(IFACE){player, _, opcode, buttonID, _, _ -> - val product = SilverProduct.forId(buttonID) ?: return@on true - - if(!ContentAPI.inInventory(player, product.needed)){ - ContentAPI.sendMessage(player, "You need a ${ContentAPI.itemDefinition(product.needed).name.toLowerCase()} to make this item.") - return@on true - } - - if(product == SilverProduct.SILVTHRIL_ROD || product == SilverProduct.SILVTHRIL_CHAIN){ - ContentAPI.sendMessage(player, "You can't do that yet.") - return@on true - } - - if(!ContentAPI.hasLevelDyn(player, Skills.CRAFTING, product.level)){ - ContentAPI.sendMessage(player, "You need a Crafting level of ${product.level} to make this.") - return@on true - } - - val amt = when(opcode){ - 155 -> 1 - 196 -> 5 - 124 -> ContentAPI.amountInInventory(player, Items.SILVER_BAR_2355) - 199 -> -1 //Make X - else -> return@on true - } - - if(amt == -1) ContentAPI.sendInputDialogue(player, true, "Enter the amount:"){value -> - make(player,product,value as Int) - } else make(player, product, amt) - return@on true - } - } - - fun make(player: Player, product: SilverProduct, amount: Int){ - var amt = amount - ContentAPI.closeInterface(player) - ContentAPI.submitWorldPulse(object : Pulse(){ - override fun pulse(): Boolean { - if(amt < 1) return true - if(!ContentAPI.inInventory(player, product.needed) || !ContentAPI.inInventory(player, Items.SILVER_BAR_2355)) return true; - ContentAPI.animate(player, ANIM) - - if(ContentAPI.removeItem(player, Items.SILVER_BAR_2355, api.Container.INVENTORY)){ - ContentAPI.addItem(player, product.product, if(product == SilverProduct.SILVER_BOLTS) 10 else 1) - ContentAPI.rewardXP(player, Skills.CRAFTING, product.exp) - - if(product == SilverProduct.UNBLESSED && player.location.withinDistance(ContentAPI.location(3226,3254,0))){ - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 8) - } - delay = 3 - } else return true - - amt-- - return false - } - }) - } -} - -class SilverBarUseWith : InteractionListener() { - val FURNACES = intArrayOf(2966, 3044, 3294, 4304, 6189, 11009, 11010, 11666, 12100, 12809, 18497, 18525, 18526, 21879, 22721, 26814, 28433, 28434, 30021, 30510, 36956, 37651) - override fun defineListeners() { - onUseWith(SCENERY, Items.SILVER_BAR_2355, *FURNACES){ player, _, _ -> - ContentAPI.openInterface(player, Components.CRAFTING_SILVER_CASTING_438) - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/BraceletOfClayPlugin.kt b/Server/src/main/kotlin/rs09/game/interaction/item/BraceletOfClayPlugin.kt deleted file mode 100644 index ab0107422..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/BraceletOfClayPlugin.kt +++ /dev/null @@ -1,24 +0,0 @@ -package rs09.game.interaction.item - -import api.ContentAPI -import rs09.game.interaction.InteractionListener - -/** - * Handles the bracelet of clay operate option. - * @author Ceikry - */ -class BraceletOfClayPlugin : InteractionListener() { - - val BRACELET = 11074 - - override fun defineListeners() { - - on(BRACELET,ITEM,"operate"){player,node -> - var charge = ContentAPI.getCharge(node) - if (charge > 28) ContentAPI.setCharge(node, 28).also { charge = 28 } - player.sendMessage("You have $charge uses left.") - return@on true - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/CulChestItems.kt b/Server/src/main/kotlin/rs09/game/interaction/item/CulChestItems.kt deleted file mode 100644 index 3d9a4efc4..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/CulChestItems.kt +++ /dev/null @@ -1,56 +0,0 @@ -package rs09.game.interaction.item - -import api.ContentAPI -import core.game.node.Node -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 rs09.game.interaction.InteractionListener - -class CulChestItems: InteractionListener() { - - override fun defineListeners() { - - onEquip(Items.CLEAVER_7451){player, node -> - alchemize(player,node) - } - - onEquip(Items.MEAT_TENDERISER_7449){player, node -> - alchemize(player,node) - } - - onEquip(Items.GLOVES_7458){player, node -> - alchemize(player,node) - } - - onEquip(Items.GLOVES_7459){player, node -> - alchemize(player,node) - } - - onEquip(Items.GLOVES_7460){player, node -> - alchemize(player,node) - } - - onEquip(Items.GLOVES_7461){player,node -> - alchemize(player,node) - } - - onEquip(Items.GLOVES_7462){player, node -> - alchemize(player,node) - } - - } - - fun alchemize(player: Player, node: Node): Boolean{ - val amount = ContentAPI.amountInInventory(player, node.id) + ContentAPI.amountInEquipment(player, node.id) - val coins = amount * ContentAPI.itemDefinition(node.id).value - - ContentAPI.removeAll(player, node.id, api.Container.INVENTORY) - ContentAPI.removeItem(player, node.id, api.Container.EQUIPMENT) - ContentAPI.addItemOrDrop(player, 995, coins) - - ContentAPI.sendDialogue(player, "The item instantly alchemized itself!") - return false //tell equip handler not to equip the gloves at all if they still even exist lel - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/EnchantJewelleryTabOption.kt b/Server/src/main/kotlin/rs09/game/interaction/item/EnchantJewelleryTabOption.kt deleted file mode 100644 index b92bd0976..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/EnchantJewelleryTabOption.kt +++ /dev/null @@ -1,97 +0,0 @@ -package rs09.game.interaction.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 rs09.game.system.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("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/kotlin/rs09/game/interaction/item/EnchantedJewelleryListener.kt b/Server/src/main/kotlin/rs09/game/interaction/item/EnchantedJewelleryListener.kt deleted file mode 100644 index f22c93e25..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/EnchantedJewelleryListener.kt +++ /dev/null @@ -1,48 +0,0 @@ -package rs09.game.interaction.item - -import core.game.content.global.EnchantedJewellery -import rs09.game.content.global.EnchantedJewelleryDialogueFile -import rs09.game.interaction.InteractionListener -import java.util.* - -/** - * Listener for enchanted jewellery options - * @author Ceikry - */ -class EnchantedJewelleryListener : InteractionListener() { - val IDs: IntArray - - init { - val idsList = ArrayList() - for (j in EnchantedJewellery.values()) { - for (id in j.ids) { - idsList.add(id) - } - } - IDs = idsList.toIntArray() - } - - override fun defineListeners() { - - on(IDs,ITEM,"rub","operate"){player,node -> - player.pulseManager.current.stop() - val item = node.asItem() - val jewellery = EnchantedJewellery.forItem(item) - if (jewellery.isLast(jewellery.getItemIndex(item))) { - player.packetDispatch.sendMessage("The " + jewellery.getNameType(item) + " has lost its charge.") - player.packetDispatch.sendMessage("It will need to be recharged before you can use it again.") - return@on true - } - player.packetDispatch.sendMessage("You rub the " + jewellery.getNameType(item) + "...") - - if (jewellery == EnchantedJewellery.DIGSITE_PENDANT) { - jewellery.use(player, item, 0, player.equipment.containsItem(item)) - return@on true - } else { - player.dialogueInterpreter.open(EnchantedJewelleryDialogueFile(jewellery,item)) - } - return@on true - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/GodBookListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/item/GodBookListeners.kt deleted file mode 100644 index 7e2b75ae0..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/GodBookListeners.kt +++ /dev/null @@ -1,134 +0,0 @@ -package rs09.game.interaction.item - -import api.ContentAPI -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListener -import rs09.tools.END_DIALOGUE - -class GodBookListeners : InteractionListener() { - - val GB_SARADOMIN = Items.HOLY_BOOK_3840 - val GB_ZAMORAK = Items.UNHOLY_BOOK_3842 - val GB_GUTHIX = Items.BOOK_OF_BALANCE_3844 - - override fun defineListeners() { - on(GB_SARADOMIN, ITEM, "preach"){player, _ -> - ContentAPI.openDialogue(player, HOLY_DIALOGUE(BOOK.SARA)) - return@on true - } - - on(GB_ZAMORAK, ITEM, "preach"){ player, _ -> - ContentAPI.openDialogue(player, HOLY_DIALOGUE(BOOK.ZAM)) - return@on true - } - - on(GB_GUTHIX, ITEM, "preach"){ player, _ -> - ContentAPI.openDialogue(player, HOLY_DIALOGUE(BOOK.GUTHIX)) - return@on true - } - } - - internal enum class BOOK { - SARA, - GUTHIX, - ZAM - } - - internal class HOLY_DIALOGUE(val book: BOOK) : DialogueFile(){ - - val WEDDINGS = arrayOf( - "In the name of Saradomin, protector of us all, I now join you in the eyes of Saradomin.", - "Light and dark, day and night, balance arises from contrast. I unify thee in the name of Guthix.", - "Two great warriors, joined by hand, to spread destruction across the land. In Zamorak's name, now two are one." - ) - - val LAST_RITES = arrayOf( - "Thy cause was false, thy skills did lack; See you in Lumbridge when you get back.", - "Thy death was not in vain, for it brought some balance to the world. May Guthix bring you rest.", - "The weak deserve to die, so the strong may flourish. This is the creed of Zamorak." - ) - - val BLESSINGS = arrayOf( - "Go in peace in the name of Saradomin; may his glory shine upon you like the sun.", - "May you walk the path, and never fall, for Guthix walks beside thee on thy journey. May Guthix bring you peace.", - "May your bloodthirst never be sated, and may all your battles be glorious. Zamorak bring you strength." - ) - - val PREACHINGS = arrayOf( - arrayOf( - "Protect your self, protect your friends. Mine is the glory that never ends.", - "The darkness in life may be avoided, by the light of wisdom shining.", - "Show love to your friends, and mercy to your enemies, and know that the wisdom of Saradomin will follow. ", - "A fight begun, when the cause is just, will prevail over all others.", - "The currency of goodness is honour; It retains its value through scarcity." - ), - arrayOf( - "All things must end, as all begin; Only Guthix knows the role thou must play.", - "In life, in death, in joy, in sorrow: May thine experience show thee balance.", - "Thou must do as thou must, no matter what. Thine actions bring balance to this world.", - "he river flows, the sun ignites, May you stand with Guthix in thy fights.", - "A journey of a single step, May take thee over a thousand miles." - ), - arrayOf( - "There is no opinion that cannot be proven true...by crushing those who choose to disagree with it.", - "Battles are not lost and won; They simply remove the weak from the equation.", - "Those who fight, then run away, shame Zamorak with their cowardice.", - "Battle is by those who choose to disagree with it.", - "Strike fast, strike hard, strike true: The strength of Zamorak will be with you." - ) - ) - - val PREACH_SARA_AMEN = "This is Saradomin's Wisdom." - val PREACH_ZAM_AMEN = "Zamorak give me strength!" - val PREACH_GUTH_AMEN = "May Guthix bring you balance." - - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - 0 -> options("Weddings","Last Rites","Blessings","Preaching").also { stage++ } - 1 -> when(buttonID){ - 1 -> say(player!!, WEDDINGS[book.ordinal], book, false) - 2 -> say(player!!, LAST_RITES[book.ordinal], book, false) - 3 -> say(player!!, BLESSINGS[book.ordinal], book, false) - 4 -> say(player!!, PREACHINGS[book.ordinal].random(), book, true) - else -> {} - }.also { end() } - } - } - - private fun say(player: Player, message: String, book: BOOK, preach: Boolean){ - val animation = when(book){ - BOOK.SARA -> Animation(1335) - BOOK.ZAM -> Animation(1336) - BOOK.GUTHIX -> Animation(1337) - } - - val preachText = when(book){ - BOOK.SARA -> PREACH_SARA_AMEN - BOOK.ZAM -> PREACH_ZAM_AMEN - BOOK.GUTHIX -> PREACH_GUTH_AMEN - } - - val lastTick = ContentAPI.animationDuration(animation) - - ContentAPI.lock(player, 100) - ContentAPI.animate(player, animation) - - ContentAPI.submitIndividualPulse(player, object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> ContentAPI.sendChat(player, message) - lastTick - 1 -> if(preach) ContentAPI.sendChat(player, preachText) - lastTick -> ContentAPI.unlock(player).also { return true } - } - return false - } - }) - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/GrandSeedPodHandler.kt b/Server/src/main/kotlin/rs09/game/interaction/item/GrandSeedPodHandler.kt deleted file mode 100644 index f029e81ca..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/GrandSeedPodHandler.kt +++ /dev/null @@ -1,73 +0,0 @@ -package rs09.game.interaction.item - -import api.ContentAPI -import core.cache.def.impl.ItemDefinition -import core.game.content.global.travel.glider.GliderPulse -import core.game.content.global.travel.glider.Gliders -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.system.task.Pulse -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics -import core.plugin.Initializable -import core.plugin.Plugin -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld - -private const val SQUASH_GRAPHICS_BEGIN = 767 -private const val SQUASH_GRAPHICS_END = 769 -private const val SQUASH_ANIM_BEGIN = 4544 -private const val SQUASH_ANIM_END = 4546 -private const val LAUNCH_GRAPHICS = 768 -private const val LAUNCH_ANIMATION = 4547 - -/** - * Handles the grand tree pod options - * @author Ceikry - */ -class GrandSeedPodHandler : InteractionListener() { - - override fun defineListeners() { - on(Items.GRAND_SEED_POD_9469, ITEM, "squash", "launch"){player, _ -> - when(ContentAPI.getUsedOption(player)){ - "launch" -> ContentAPI.submitWorldPulse(LaunchPulse(player)) - "squash" -> ContentAPI.submitWorldPulse(SquashPulse(player)) - } - ContentAPI.removeItem(player, Items.GRAND_SEED_POD_9469, api.Container.INVENTORY) - ContentAPI.lock(player, 50) - return@on true - } - } - - class LaunchPulse(val player: Player): Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 1 -> ContentAPI.visualize(player, LAUNCH_ANIMATION, LAUNCH_GRAPHICS) - 3 -> ContentAPI.rewardXP(player, Skills.FARMING, 100.0) - 4 -> ContentAPI.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 -> ContentAPI.visualize(player, SQUASH_ANIM_BEGIN, SQUASH_GRAPHICS_BEGIN) - 4 -> ContentAPI.animate(player, 1241, true) - 5 -> ContentAPI.teleport(player, Location.create(2464, 3494, 0)) - 6 -> ContentAPI.visualize(player, anim = 1241, gfx = SQUASH_GRAPHICS_END) - 8 -> ContentAPI.animate(player, SQUASH_ANIM_END, true).also { ContentAPI.adjustLevel(player, Skills.FARMING, -5) } - 9 -> ContentAPI.unlock(player).also { return true } - } - return false - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/LegendsCapeLock.kt b/Server/src/main/kotlin/rs09/game/interaction/item/LegendsCapeLock.kt deleted file mode 100644 index f4c4a86f8..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/LegendsCapeLock.kt +++ /dev/null @@ -1,15 +0,0 @@ -package rs09.game.interaction.item - -import api.ContentAPI -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener - -class LegendsCapeLock : InteractionListener() { - override fun defineListeners() { - onEquip(Items.CAPE_OF_LEGENDS_1052){player, node -> - val points = ContentAPI.getQP(player) - if(points < 40) ContentAPI.sendDialogue(player, "You need 40 QP to equip that.") - return@onEquip points >= 40 - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/QuestCapeListener.kt b/Server/src/main/kotlin/rs09/game/interaction/item/QuestCapeListener.kt deleted file mode 100644 index ed198eea1..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/QuestCapeListener.kt +++ /dev/null @@ -1,21 +0,0 @@ -package rs09.game.interaction.item - -import core.game.node.entity.player.link.quest.QuestRepository -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners.run - -class QuestCapeListener : InteractionListener() { - - val IDs = intArrayOf(9813,9814) - - override fun defineListeners() { - on(IDs,ITEM,"wear"){ player, item -> - val MAX_QP = QuestRepository.getQuests().values.sumBy { it.questPoints } - if (player.questRepository.points < MAX_QP) { - player.packetDispatch.sendMessage("You cannot wear this " + item.name.toLowerCase() + " yet.") - return@on true - } - return@on run(item.id, 0, "equip", player, item) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/StarRingListener.kt b/Server/src/main/kotlin/rs09/game/interaction/item/StarRingListener.kt deleted file mode 100644 index 6f56800e6..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/StarRingListener.kt +++ /dev/null @@ -1,94 +0,0 @@ -package rs09.game.interaction.item - -import api.ContentAPI -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.json.simple.JSONObject -import org.rs09.consts.Items -import rs09.ServerStore -import rs09.ServerStore.getBoolean -import rs09.game.content.dialogue.DialogueFile -import rs09.game.content.global.worldevents.WorldEvents -import rs09.game.content.global.worldevents.shootingstar.ShootingStar -import rs09.game.content.global.worldevents.shootingstar.ShootingStarEvent -import rs09.game.interaction.InteractionListener -import java.util.concurrent.TimeUnit - -class StarRingListener : InteractionListener(){ - - val RING = Items.RING_OF_THE_STAR_SPRITE_14652 - - override fun defineListeners() { - on(RING, ITEM, "rub", "operate"){player, node -> - val star = WorldEvents.get("shooting-stars") as? ShootingStarEvent - - if(star == null) ContentAPI.sendDialogue(player, "There is currently no active star.").also { return@on true } - - if(getStoreFile().getBoolean(player.username.toLowerCase())){ - ContentAPI.sendDialogue(player, "The ring is still recharging.") - return@on true - } - - val condition: (Player) -> Boolean = when(star?.star!!.location.toLowerCase()){ - "canifis bank" -> { p -> p.questRepository.isComplete("Priest in Peril")} - "crafting guild" -> {p -> ContentAPI.hasLevelStat(p, Skills.CRAFTING, 40) } - "south crandor mining site" -> {p -> p.questRepository.isComplete("Dragon Slayer")} - else -> {_ -> true} - } - - if(!condition.invoke(player) || player.skullManager.isWilderness){ - ContentAPI.sendDialogue(player, "Magical forces prevent your teleportation.") - return@on true - } - - val shouldWarn = when(star.star.location){ - "North Edgeville mining site", - "Southern wilderness mine", - "Pirates' Hideout mine", - "Lava Maze mining site", - "Mage Arena bank" -> true - else -> false - } - - ContentAPI.openDialogue(player, RingDialogue(shouldWarn, star.star)) - - return@on true - } - } - - internal class RingDialogue(val shouldWarn: Boolean, val star: ShootingStar) : DialogueFile(){ - override fun handle(componentID: Int, buttonID: Int) { - if(shouldWarn){ - when(stage) { - 0 -> dialogue("WARNING: That mining site is located in the wilderness.").also { stage++ } - 1 -> player!!.dialogueInterpreter.sendOptions("Continue?","Yes","No").also { stage++ } - 2 -> when(buttonID){ - 1 -> teleport(player!!, star).also { end() } - 2 -> end() - } - } - } else { - when(stage){ - 0 -> player!!.dialogueInterpreter.sendOptions("Teleport to the Star?", "Yes", "No").also { stage++ } - 1 -> when(buttonID){ - 1 -> teleport(player!!, star).also { end() } - 2 -> end() - } - } - } - } - - fun teleport(player: Player, star: ShootingStar){ - ContentAPI.teleport(player, star.crash_locations[star.location]!!.transform(0, -1, 0), TeleportManager.TeleportType.MINIGAME) - getStoreFile()[player.username.toLowerCase()] = true - } - } - - companion object { - fun getStoreFile(): JSONObject { - return ServerStore.getArchive("daily-star-ring") - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withitem/HaySackOnSpear.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withitem/HaySackOnSpear.kt deleted file mode 100644 index 331f80cd3..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withitem/HaySackOnSpear.kt +++ /dev/null @@ -1,22 +0,0 @@ -package rs09.game.interaction.item.withitem - -import api.Container -import api.ContentAPI -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener - -class HaySackOnSpear : InteractionListener() { - val HAYSACK = Items.HAY_SACK_6057 - val SPEAR = Items.BRONZE_SPEAR_1237 - - override fun defineListeners() { - onUseWith(ITEM, HAYSACK, SPEAR){ player, used, _ -> - if(ContentAPI.removeItem(player, used.asItem(), Container.INVENTORY)){ - ContentAPI.addItem(player, Items.HAY_SACK_6058, 1) - ContentAPI.sendMessage(player, "You stab the hay sack with a bronze spear") - ContentAPI.removeItem(player, SPEAR, Container.INVENTORY) - } - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withitem/TOTTHelmOnCape.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withitem/TOTTHelmOnCape.kt deleted file mode 100644 index 1eff3699f..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withitem/TOTTHelmOnCape.kt +++ /dev/null @@ -1,42 +0,0 @@ -package rs09.game.interaction.item.withitem - -import api.Container -import api.ContentAPI -import api.DialUtils -import org.rs09.consts.Items -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListener -import rs09.tools.END_DIALOGUE - -class TOTTHelmOnCape : InteractionListener() { - override fun defineListeners() { - onUseWith(ITEM, Items.SLAYER_HELMET_13263, Items.SLAYER_CAPE_9786, Items.SLAYER_CAPET_9787){ player, used, with -> - val alreadyHasHelm = ContentAPI.getAttribute(player, "cape_perks:tott:helmet-stored", false) - - if(alreadyHasHelm){ - ContentAPI.sendDialogue(player, "You've already stored the components of a helmet in this cape.") - } else { - ContentAPI.openDialogue(player, object : DialogueFile(){ - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - 0 -> dialogue("This will destroy your helmet permanently.").also { stage++ } - 1 -> interpreter!!.sendOptions("Continue?", "Yes", "No").also { stage++ } - 2 -> when(buttonID){ - 1 -> { - if(ContentAPI.removeItem(player, Items.SLAYER_HELMET_13263, Container.INVENTORY)) { - dialogue(*DialUtils.splitLines("You disassemble the helmet and place the components into their respective pockets on your cape.")) - ContentAPI.setAttribute(player, "/save:cape_perks:tott:helmet-stored", true) - stage = END_DIALOGUE - } - } - 2 -> end() - } - } - } - }) - } - - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withitem/WatermelonOnSack.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withitem/WatermelonOnSack.kt deleted file mode 100644 index be5dca0d7..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withitem/WatermelonOnSack.kt +++ /dev/null @@ -1,27 +0,0 @@ -package rs09.game.interaction.item.withitem - -import api.Container -import api.ContentAPI -import core.game.node.entity.skill.Skills -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener - -class WatermelonOnSack : InteractionListener() { - val SACK = Items.HAY_SACK_6058 - val WATERMELON = Items.WATERMELON_5982 - - override fun defineListeners() { - onUseWith(ITEM, SACK, WATERMELON){player, used, _ -> - if(ContentAPI.getStatLevel(player, Skills.FARMING) >= 23){ - ContentAPI.removeItem(player,SACK, Container.INVENTORY) - ContentAPI.removeItem(player,WATERMELON,Container.INVENTORY) - ContentAPI.addItem(player, Items.SCARECROW_6059) - ContentAPI.rewardXP(player, Skills.FARMING, 25.0) - ContentAPI.sendMessage(player, "You stab the watermelon on top of the spear, finishing your scarecrow") - }else{ - ContentAPI.sendMessage(player, "Your Farming level is not high enough to do this") - } - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/CatOnArdougneCivilian.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/CatOnArdougneCivilian.kt deleted file mode 100644 index f6480f30b..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/CatOnArdougneCivilian.kt +++ /dev/null @@ -1,55 +0,0 @@ -package rs09.game.interaction.item.withnpc - -import api.Container -import api.ContentAPI -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener - -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(NPC,cats,*civilians){player, used, _ -> - ContentAPI.sendItemDialogue(player,Items.DEATH_RUNE_560,"You hand over the cat.
You are given 100 Death Runes.") - player.familiarManager.removeDetails(used.id) - ContentAPI.removeItem(player,used,Container.INVENTORY) - ContentAPI.addItem(player,Items.DEATH_RUNE_560,100) - return@onUseWith true; - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/HatEasterEgg.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/HatEasterEgg.kt deleted file mode 100644 index d503b6aac..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/HatEasterEgg.kt +++ /dev/null @@ -1,51 +0,0 @@ -package rs09.game.interaction.item.withnpc - -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.item.Item -import core.game.world.update.flag.context.Graphics -import org.rs09.consts.Items -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListener - -val graphics = 482 -class HatEasterEgg : InteractionListener(){ - - val MACHINE = 20040 - val WIZ_HAT = Items.WIZARD_HAT_579 - val NEW_HAT = 14650 - - override fun defineListeners() { - onUseWith(SCENERY,WIZ_HAT,MACHINE){ player, used, with -> - player.dialogueInterpreter.open(HatDialogue(), NPC(872)) - return@onUseWith true - } - } - -} - -class HatDialogue : DialogueFile(){ - override fun handle(componentID: Int, buttonID: Int) { - when(stage++){ - 0 -> { - npc("WHAT HAVE YOU DONE?") - player!!.graphics(Graphics(graphics)) - } - 1 -> player(FacialExpression.AFRAID,"What do you mean?!") - 2 -> npc("You've disjointed the fabric assimilation matrix!") - 3 -> player(FacialExpression.THINKING,"W-what...?") - 4 -> npc("You've put us at risk of ripping Gielinor apart!") - 5 -> player(FacialExpression.HALF_GUILTY,"I.. I just wanted a hat...") - 6 -> npc("Damn you and damn your hat! You could kill us all!") - 7 -> player("I... I'm sorry....") - 8 -> npc("*sigh* I've managed to stabilize the flux material inductors.") - 9 -> npc("You may just be safe, yet.") - 10 -> npc("Here, take your damn hat and get out of here.") - 11 -> { - end() - player!!.inventory.remove(Item(Items.WIZARD_HAT_579)) - player!!.inventory.add(Item(14650)) - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/MistagEasterEgg.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/MistagEasterEgg.kt deleted file mode 100644 index ce131bc81..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withnpc/MistagEasterEgg.kt +++ /dev/null @@ -1,58 +0,0 @@ -package rs09.game.interaction.item.withnpc - -import core.game.component.Component -import core.game.content.dialogue.FacialExpression -import core.game.node.item.Item -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListener -import rs09.tools.END_DIALOGUE - -class MistagEasterEgg : InteractionListener() { - val DIAMOND = Items.DIAMOND_1601 - val MISTAG = NPCs.MISTAG_2084 - val ZANIK_RING = 14649 - - override fun defineListeners() { - onUseWith(NPC,DIAMOND,MISTAG){player, _, with -> - val alreadyHasRing = player.inventory.contains(ZANIK_RING,1) || player.bank.contains(ZANIK_RING,1) || player.equipment.contains(ZANIK_RING,1) - player.dialogueInterpreter.open(MistagEasterEggDialogue(alreadyHasRing),with.asNpc()) - return@onUseWith true - } - - onEquip(ZANIK_RING){player,_ -> - player.appearance.transformNPC(NPCs.ZANIK_3712) - return@onEquip true - } - - onUnequip(ZANIK_RING){player, _ -> - player.appearance.transformNPC(-1) - return@onUnequip true - } - } -} - -class MistagEasterEggDialogue(val hasRing: Boolean): DialogueFile(){ - override fun handle(componentID: Int, buttonID: Int) { - - if(hasRing){ - npc("Lovely gem, adventurer, but I have nothing for you.").also { stage = END_DIALOGUE } - return - } - - when(stage++){ - 0 -> npc("Well thank you adventurer! Here, take this.") - 1 -> { - end() - if(player!!.inventory.remove(Item(Items.DIAMOND_1601))){ - player!!.inventory.add(Item(14649)) - } - } - } - } - - override fun npc(vararg messages: String?): Component? { - return super.npc(FacialExpression.OLD_HAPPY,*messages) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/AxeOnTree.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withobject/AxeOnTree.kt deleted file mode 100644 index 9cec2668d..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/AxeOnTree.kt +++ /dev/null @@ -1,21 +0,0 @@ -package rs09.game.interaction.item.withobject - -import api.ContentAPI -import core.game.node.entity.skill.gather.SkillingTool -import core.game.node.entity.skill.gather.woodcutting.WoodcuttingNode -import core.game.node.entity.skill.gather.woodcutting.WoodcuttingSkillPulse -import org.rs09.consts.Items -import rs09.ServerConstants -import rs09.game.interaction.InteractionListener - -class AxeOnTree : InteractionListener(){ - val axes = intArrayOf(Items.BRONZE_AXE_1351, Items.MITHRIL_AXE_1355, Items.IRON_AXE_1349, Items.BLACK_AXE_1361, Items.STEEL_AXE_1353, Items.ADAMANT_AXE_1357, Items.RUNE_AXE_1359, Items.DRAGON_AXE_6739, Items.INFERNO_ADZE_13661) - val trees = WoodcuttingNode.values().map { it.id }.toIntArray() - - override fun defineListeners() { - onUseWith(SCENERY, axes, *trees){player, _, with -> - ContentAPI.submitIndividualPulse(player, WoodcuttingSkillPulse(player, with.asScenery())) - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/CoalTrucksHandler.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withobject/CoalTrucksHandler.kt deleted file mode 100644 index dfa6ab643..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/CoalTrucksHandler.kt +++ /dev/null @@ -1,80 +0,0 @@ -package rs09.game.interaction.item.withobject - -import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.item.Item -import core.game.world.map.zone.ZoneBorders -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener -import java.util.* - -/** - * Handles coal truck interactions - * @author ceik - */ -class CoalTruckInteractionListeners : InteractionListener() { - - val SEERS_VILLAGE_COAL_TRUCK_2114 = 2114 - val seersVillageTrucks = ZoneBorders(2690,3502,2699,3508) - val COAL_TRUCK_2114 = 2114 - val COAL = Items.COAL_453 - - override fun defineListeners() { - on(SEERS_VILLAGE_COAL_TRUCK_2114, SCENERY, "remove-coal") { player, node -> - - var coalInTruck = player.getAttribute("coal-truck-inventory", 0) - var freeSpace = player.inventory.freeSlots() - val toAdd: MutableList = ArrayList() - - if (coalInTruck == 0) { - player.dialogueInterpreter.sendDialogue("The coal truck is empty.") - return@on true - } - - var toRemove = player.inventory.freeSlots() - - if(toRemove > coalInTruck) toRemove = coalInTruck - - player.inventory.add(Item(Items.COAL_453,toRemove)) - - coalInTruck -= toRemove - player.setAttribute("/save:coal-truck-inventory", coalInTruck) - - if (!player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 1, 2) - && seersVillageTrucks.insideBorder(player) - && player.getAttribute("diary:seers:coal-truck-full", false)) { - player.removeAttribute("diary:seers:coal-truck-full") - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 1, 2) - } - return@on true - } - - on(SEERS_VILLAGE_COAL_TRUCK_2114, SCENERY, "investigate") { player, node -> - var coalInTruck = player.getAttribute("coal-truck-inventory", 0) - player.dialogueInterpreter.sendDialogue("There is currently $coalInTruck coal in the truck.", "The truck has space for " + (120 - coalInTruck) + " more coal.") - return@on true - } - - onUseWith(SCENERY,COAL,COAL_TRUCK_2114){ player, _, _ -> - var coalInTruck = player.getAttribute("coal-truck-inventory", 0) - - var coalInInventory = player.inventory.getAmount(Items.COAL_453) - - if(coalInInventory + coalInTruck >= 120){ - coalInInventory = 120 - coalInTruck - player.packetDispatch.sendMessage("You have filled up the coal truck.") - - //handle coal truck task for seer's village - if (!player.achievementDiaryManager.getDiary(DiaryType.SEERS_VILLAGE).isComplete(1, 2) - && player.viewport.region.id == 10294) { // region 10294 is at coal truck mine, region 10806 is in seers village - player.setAttribute("/save:diary:seers:coal-truck-full", true) - } - } - - player.inventory.remove(Item(Items.COAL_453,coalInInventory)) - coalInTruck += coalInInventory - - player.setAttribute("/save:coal-truck-inventory",coalInTruck) - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/FishEasterEgg.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withobject/FishEasterEgg.kt deleted file mode 100644 index 7646b98a5..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/FishEasterEgg.kt +++ /dev/null @@ -1,25 +0,0 @@ -package rs09.game.interaction.item.withobject - -import api.ContentAPI -import core.game.node.entity.skill.gather.woodcutting.WoodcuttingNode -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener - -class FishEasterEgg : InteractionListener() { - val TREE_IDs = WoodcuttingNode.values().map { it.id }.toIntArray() - - val fish = intArrayOf(Items.RAW_HERRING_345, Items.HERRING_347) - val doors = intArrayOf(1967,1968) - - override fun defineListeners() { - onUseWith(SCENERY, fish, *TREE_IDs){player, _, _ -> - ContentAPI.sendMessage(player, "This is not the mightiest tree in the forest.") - return@onUseWith true - } - - onUseWith(SCENERY, fish, *doors){player, _, _ -> - ContentAPI.sendMessage(player, "It can't be done!") - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/HatStand.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withobject/HatStand.kt deleted file mode 100644 index 72603cd32..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/HatStand.kt +++ /dev/null @@ -1,20 +0,0 @@ -package rs09.game.interaction.item.withobject - -import api.ContentAPI -import api.EquipmentSlot -import core.cache.def.impl.ItemDefinition -import rs09.game.interaction.InteractionListener -import rs09.game.system.SystemLogger - -class HatStand : InteractionListener() { - - val hats = ItemDefinition.getDefinitions().values.filter { it.getConfiguration("equipment_slot",0) == EquipmentSlot.HAT.ordinal }.map { it.id }.toIntArray() - val hat_stand = 374 - - override fun defineListeners() { - onUseWith(SCENERY, hats, hat_stand){player, used, with -> - ContentAPI.sendDialogue(player, "It'd probably fall off if I tried to do that.") - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/SackOnHay.kt b/Server/src/main/kotlin/rs09/game/interaction/item/withobject/SackOnHay.kt deleted file mode 100644 index 0de84caa7..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/withobject/SackOnHay.kt +++ /dev/null @@ -1,21 +0,0 @@ -package rs09.game.interaction.item.withobject - -import api.Container -import api.ContentAPI -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener - -class SackOnHay : InteractionListener() { - val SACK = Items.EMPTY_SACK_5418 - val HAY = intArrayOf(36892, 36894, 36896, 300, 34593, 298, 299) - - override fun defineListeners() { - onUseWith(SCENERY, SACK, *HAY){player, used, _ -> - if(ContentAPI.removeItem(player, used.asItem(), Container.INVENTORY)){ - ContentAPI.addItem(player, Items.HAY_SACK_6057, 1) - ContentAPI.sendMessage(player, "You fill the sack with hay.") - } - return@onUseWith true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/npc/BogrogPouchSwapper.kt b/Server/src/main/kotlin/rs09/game/interaction/npc/BogrogPouchSwapper.kt deleted file mode 100644 index f2824b973..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/npc/BogrogPouchSwapper.kt +++ /dev/null @@ -1,85 +0,0 @@ -package rs09.game.interaction.npc - -import api.ContentAPI -import core.game.component.Component -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.RunScript -import core.game.node.entity.skill.summoning.SummoningPouch -import core.game.node.entity.skill.summoning.SummoningScroll -import core.game.node.item.Item -import core.game.world.map.zone.ZoneBorders -import kotlin.math.ceil -import kotlin.math.floor - -/** - * Handles swapping pouches/scrolls for shards. - * We decided to use the GE set exchange interface for this because it's the only one we could find - * that allowed us to do what we needed. Jagex did the same thing as far as we can tell. - * @author Ceikry - */ -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 SPIRIT_SHARD = 12183 - - //GE Zone borders because shitty hack lol - 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) - OP_SWAP_X -> true.also{ - ContentAPI.sendInputDialogue(player, true, "Enter the amount:"){value -> - swap(player, value as Int, player.inventory.get(slot).id) - } - } - else -> false - } - } - - private fun swap(player: Player, amount: Int, itemID: Int): Boolean{ - var amt = amount - val value = getValue(itemID) - if(value == 0.0){ - return false - } - val inInventory = player.inventory.getAmount(itemID) - if(amount > inInventory) - 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 - } - - private fun sendValue(itemID: Int, player: Player): Boolean{ - val value = getValue(itemID) - if(value == 0.0){ - return false - } - - player.sendMessage("Bogrog will give you $value shards for that.") - return true - } - - private fun getValue(itemID: Int): Double{ - var item = SummoningPouch.get(itemID) - var isScroll = false - if(item == null) item = SummoningPouch.get(Item(itemID).noteChange) - 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 - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/npc/DecantListener.kt b/Server/src/main/kotlin/rs09/game/interaction/npc/DecantListener.kt deleted file mode 100644 index a633eccec..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/npc/DecantListener.kt +++ /dev/null @@ -1,55 +0,0 @@ -package rs09.game.interaction.npc - -import core.game.content.consumable.Consumables -import core.game.content.consumable.Potion -import core.game.node.entity.player.Player -import core.game.node.item.Item -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListener -import rs09.tools.END_DIALOGUE - -class DecantListener : InteractionListener() { - - override fun defineListeners() { - on(NPC,"decant"){player, node -> - decant(player) - player.dialogueInterpreter.open(DecantingDialogue(),node.asNpc()) - return@on true - } - } - - - 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))) - } - } - 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])) - } - } - - internal class DecantingDialogue : DialogueFile(){ - override fun handle(componentID: Int, buttonID: Int) { - when(stage++){ - 0 -> npc("There you go!") - 1 -> player("Thanks!").also { stage = END_DIALOGUE } - } - } - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt deleted file mode 100644 index 0537b31b7..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt +++ /dev/null @@ -1,68 +0,0 @@ -package rs09.game.interaction.npc - -import core.game.content.quest.miniquest.barcrawl.BarcrawlType -import core.game.node.entity.npc.NPC -import rs09.game.content.activity.gnomecooking.* -import rs09.game.content.ame.RandomEvents -import rs09.game.interaction.InteractionListener -import rs09.game.system.SystemLogger - -/** - * Handles the NPC talk-to option. - * @author Ceikry - */ -class NPCTalkListener : InteractionListener() { - - val barCrawlNPCs = intArrayOf(733,848,735,739,737,738,731,568,3217,736,734) - - override fun defineListeners() { - - on(barCrawlNPCs, NPC, "talk-to", "talk"){player, node -> - val type = BarcrawlType.forId(node.id) - - if (player.barcrawlManager.isFinished || !player.barcrawlManager.isStarted || player.barcrawlManager.isCompleted(type!!.ordinal)) { - player.dialogueInterpreter.open(node.id, node) - } else { - player.dialogueInterpreter.open("barcrawl dialogue", node.id, type) - } - return@on true - } - - on(NPC,"talk-to","talk"){player,node -> - val npc = node.asNpc() - if(RandomEvents.randomIDs.contains(node.id)){ - if(player.antiMacroHandler.event == null || player.antiMacroHandler.event!!.id != node.id){ - player.sendMessage("They aren't interested in talking to you.") - } else { - player.antiMacroHandler.event!!.talkTo(node.asNpc()) - } - return@on true - } - if (!npc.getAttribute("facing_booth", false)) { - npc.faceLocation(player.location) - } - //I'm sorry for this but it was honestly the best way to do this - if (player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_ORDINAL, -1) != -1) { - val job = GnomeCookingJob.values()[player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_ORDINAL, -1)] - if (node.getId() == job.npc_id && !player.getAttribute(GC_BASE_ATTRIBUTE + ":" + GC_JOB_COMPLETE, false)) { - player.dialogueInterpreter.open(GCCompletionDialogue(job)) - return@on true - } - } - return@on player.dialogueInterpreter.open(npc.id, npc) - } - - } - - override fun defineDestinationOverrides() { - setDest(NPC,"talk","talk-to"){_,node -> - val npc = node as NPC - if (npc.getAttribute("facing_booth", false)) { - val offsetX = npc.direction.stepX shl 1 - val offsetY = npc.direction.stepY shl 1 - return@setDest npc.location.transform(offsetX, offsetY, 0) - } - return@setDest node.location - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/DemonTauntHandler.kt b/Server/src/main/kotlin/rs09/game/interaction/object/DemonTauntHandler.kt deleted file mode 100644 index 0c763906c..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/object/DemonTauntHandler.kt +++ /dev/null @@ -1,28 +0,0 @@ -package rs09.game.interaction.`object` - -import api.ContentAPI -import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.entity.player.link.emote.Emotes -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener - -/** - * Handles taunting of the demon in the wizard's tower - * @author afaroutdude / Ceikry - */ -private const val BARS = 37668 -class DemonTauntHandler : InteractionListener(){ - - override fun defineListeners() { - on(BARS,SCENERY,"taunt-through"){ player, _ -> - ContentAPI.sendMessage(player, "You taunt the demon, making it growl.") - val demon = ContentAPI.findLocalNPC(player, NPCs.LESSER_DEMON_82) ?: return@on true - ContentAPI.sendChat(demon, "Graaagh!") - ContentAPI.face(demon, player, 3) - ContentAPI.emote(player, Emotes.RASPBERRY) - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 13) - return@on true - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/FairyRingPlugin.kt b/Server/src/main/kotlin/rs09/game/interaction/object/FairyRingPlugin.kt deleted file mode 100644 index 2706f2a45..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/object/FairyRingPlugin.kt +++ /dev/null @@ -1,55 +0,0 @@ -package rs09.game.interaction.`object` - -import core.game.component.Component -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.TeleportManager.TeleportType -import core.game.world.map.Location -import rs09.game.interaction.InteractionListener - -/** - * Handles interactions with fairy rings - * @author Ceikry - */ - -private val RINGS = intArrayOf(12003, 12094, 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 - - -class FairyRingPlugin : InteractionListener() { - - override fun defineListeners() { - - on(RINGS,SCENERY,"use"){ player, _ -> - if (!player.equipment.contains(772, 1) && !player.equipment.contains(9084, 1)) { - player.sendMessage("The fairy ring only works for those who wield fairy magic.") - return@on true - } - player.teleporter.send(Location.create(2412, 4434, 0), TeleportType.FAIRY_RING) - return@on true - } - - on(MAIN_RING,SCENERY,"use"){ player, _ -> - if (!player.equipment.contains(772, 1) && !player.equipment.contains(9084, 1)) { - player.sendMessage("The fairy ring only works for those who wield fairy magic.") - return@on true - } - openFairyRing(player) - return@on 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)) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/JungleBushHandler.kt b/Server/src/main/kotlin/rs09/game/interaction/object/JungleBushHandler.kt deleted file mode 100644 index d78b5dd97..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/object/JungleBushHandler.kt +++ /dev/null @@ -1,56 +0,0 @@ -package rs09.game.interaction.`object` - -import core.game.node.scenery.Scenery -import core.game.node.scenery.SceneryBuilder -import core.game.node.entity.player.Player -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 rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld - -/** - * Handles the chopping down of dense jungle, mainly to grant access to the Kharazi Jungle. - * @author Ceikry - */ -class JungleBushHandler : InteractionListener(){ - val chopped_bush = 2895 - val chop_a = Animation(910) - val chop_b = Animation(2382) - val ids = intArrayOf(2892,2893) - - override fun defineListeners() { - - on(ids,SCENERY,"chop-down"){ player, node -> - val toChop = node.asScenery() - if(checkRequirement(player)){ - GameWorld.Pulser.submit(object : Pulse(0){ - var ticks = 0 - override fun pulse(): Boolean { - when(ticks++){ - 0 -> player.animator.animate(chop_a).also { player.lock() } - 1 -> player.animator.animate(chop_b) - 2 -> SceneryBuilder.replace(toChop, Scenery(chopped_bush, toChop.location, toChop.rotation),20) - 3 -> {player.walkingQueue.reset(); player.walkingQueue.addPath(toChop.location.x, toChop.location.y,true)} - 4 -> player.unlock().also { return true } - } - return false - } - }) - } else { - player.sendMessage("You need a machete to get through this dense jungle.") - } - return@on true - } - - } - - 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) - } -} diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/VarrockGuardSignpost.kt b/Server/src/main/kotlin/rs09/game/interaction/object/VarrockGuardSignpost.kt deleted file mode 100644 index 0c3cf23a4..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/object/VarrockGuardSignpost.kt +++ /dev/null @@ -1,40 +0,0 @@ -package rs09.game.interaction.`object` - -import api.ContentAPI -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 rs09.GlobalStats -import rs09.game.interaction.InteractionListener -import rs09.game.system.SystemLogger - -class VarrockGuardSignpost : InteractionListener() { - override fun defineListeners() { - val zone = object : MapZone("Varrock Guards", true){ - override fun interact(e: Entity?, target: Node?, option: Option?): Boolean { - if(option != null && option.name.toLowerCase().contains("pickpocket") && target != null && target.name.toLowerCase().contains("guard")){ - GlobalStats.incrementGuardPickpockets() - } - return false - } - } - - ContentAPI.registerMapZone(zone, ZoneBorders(3225,3445,3198,3471)) - ContentAPI.registerMapZone(zone, ZoneBorders(3222,3375,3199,3387)) - ContentAPI.registerMapZone(zone, ZoneBorders(3180,3420,3165,3435)) - ContentAPI.registerMapZone(zone, ZoneBorders(3280,3422,3266,3435)) - - on(31298, SCENERY, "read"){player, _ -> - val pickpocketCount = GlobalStats.getDailyGuardPickpockets() - SystemLogger.logInfo("Is equal? ${pickpocketCount == 0}") - when(pickpocketCount){ - 0 -> ContentAPI.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 -> ContentAPI.sendDialogue(player, "One of the Varrock Palace guards was pickpocketed today. He was close to tears at having lost his last few gold pieces." ) - else -> ContentAPI.sendDialogue(player, "Guards in the Varrock Palace are on full alert due to increasing levels of pickpocketing. So far today, $pickpocketCount guards have had their money pickpocketed in the palace or at the city gates.") - } - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/WizardGuildPortals.kt b/Server/src/main/kotlin/rs09/game/interaction/object/WizardGuildPortals.kt deleted file mode 100644 index 960b9c88c..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/object/WizardGuildPortals.kt +++ /dev/null @@ -1,35 +0,0 @@ -package rs09.game.interaction.`object` - -import api.ContentAPI -import core.game.world.map.Location -import rs09.game.interaction.InteractionListener - -class WizardGuildPortals : InteractionListener() { - - val WTOWER_PORTAL = 2156 - val DWTOWER_PORTAL = 2157 - val SORC_TOWER_PORTAL = 2158 - - override fun defineListeners() { - on(WTOWER_PORTAL, SCENERY, "enter"){player, _ -> - ContentAPI.teleport(player, Location.create(3109, 3159, 0)) - ContentAPI.sendMessage(player, "You enter the magic portal...") - ContentAPI.sendMessage(player, "You teleport to the Wizards' tower.") - return@on true - } - - on(DWTOWER_PORTAL, SCENERY, "enter"){player, _ -> - ContentAPI.teleport(player, Location.create(2907, 3333, 0)) - ContentAPI.sendMessage(player, "You enter the magic portal...") - ContentAPI.sendMessage(player, "You teleport to the Dark Wizards' tower.") - return@on true - } - - on(SORC_TOWER_PORTAL, SCENERY, "enter"){player, _ -> - ContentAPI.teleport(player, Location.create(2703, 3406, 0)) - ContentAPI.sendMessage(player, "You enter the magic portal...") - ContentAPI.sendMessage(player, "You teleport to Thormac the Sorceror's house.") - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/canoestation/CanoeInterfaceListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/object/canoestation/CanoeInterfaceListeners.kt deleted file mode 100644 index 7b63d7a9a..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/object/canoestation/CanoeInterfaceListeners.kt +++ /dev/null @@ -1,146 +0,0 @@ -package rs09.game.interaction.`object`.canoestation - -import api.ContentAPI -import core.cache.def.impl.VarbitDefinition -import core.game.component.Component -import core.game.content.global.travel.canoe.Canoe -import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.gather.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 rs09.game.interaction.InterfaceListener -import kotlin.math.abs - -class CanoeInterfaceListeners : InterfaceListener() { - - val SHAPE_INTERFACE = Components.CANOE_52 - val DESTINATION_INTERFACE = Components.CANOE_STATIONS_MAP_53 - val EDGEVILLE_REGION = 12342 - private val boatChilds = intArrayOf(47, 48, 3, 6, 49) - private val locationChilds = intArrayOf(50, 47, 44, 36) - - - override fun defineListeners() { - - 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() - ContentAPI.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) { - if (player.location.regionId == EDGEVILLE_REGION && canoe == Canoe.WAKA) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 10) - } - player.varpManager.setVarbit(varbit,CanoeUtils.getCraftValue(canoe,false)) - player.skills.addExperience(Skills.WOODCUTTING, canoe.experience) - player.unlock() - return true - } - ContentAPI.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)) - ContentAPI.animateInterface(player, Components.CANOE_TRAVEL_758, 3, interfaceAnimationId) - } - 2 -> { - PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) - player.interfaceManager.hideTabs(0, 1, 2, 3, 4, 5, 6, 11, 12) - } - travelAnimDur -> player.properties.teleportLocation = dest - travelAnimDur+1 -> { - 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/kotlin/rs09/game/interaction/object/canoestation/CanoeStationListener.kt b/Server/src/main/kotlin/rs09/game/interaction/object/canoestation/CanoeStationListener.kt deleted file mode 100644 index 857ba683e..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/object/canoestation/CanoeStationListener.kt +++ /dev/null @@ -1,97 +0,0 @@ -package rs09.game.interaction.`object`.canoestation - -import core.game.component.Component -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.gather.SkillingTool -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Components -import rs09.game.interaction.InteractionListener - -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, 12144, 12146, 12149, 12150, 12157) - 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(SCENERY,STATION_IDs,"chop-down"){ _,node -> - return@setDest CanoeUtils.getChopLocation(node.location) - } - setDest(SCENERY,STATION_IDs,"shape-canoe","float canoe","float log","float waka"){ _,node -> - return@setDest CanoeUtils.getCraftFloatLocation(node.location) - } - } - - override fun defineListeners() { - on(STATION_IDs,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.configId).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,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,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,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/kotlin/rs09/game/interaction/object/canoestation/CanoeUtils.kt b/Server/src/main/kotlin/rs09/game/interaction/object/canoestation/CanoeUtils.kt deleted file mode 100644 index 5b466cf5e..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/object/canoestation/CanoeUtils.kt +++ /dev/null @@ -1,123 +0,0 @@ -package rs09.game.interaction.`object`.canoestation - -import core.cache.def.impl.VarbitDefinition -import core.game.content.global.travel.canoe.Canoe -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.gather.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/kotlin/rs09/game/interaction/player/PeltOptionPlugin.kt b/Server/src/main/kotlin/rs09/game/interaction/player/PeltOptionPlugin.kt deleted file mode 100644 index c9c5cff39..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/player/PeltOptionPlugin.kt +++ /dev/null @@ -1,83 +0,0 @@ -package rs09.game.interaction.player - -import core.game.interaction.Interaction -import core.game.interaction.OptionHandler -import core.game.node.Node -import core.game.node.entity.impl.Projectile -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.path.Pathfinder -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 rs09.game.world.GameWorld -import rs09.tools.stringtools.colorize - -val snowball = Item(Items.SNOWBALL_11951) -val THROW_ANIMATION = Animation(7530) -val THROW_GRAPHICS = Graphics(860) - -@Initializable -class PeltOptionPlugin : OptionHandler() { - override fun newInstance(arg: Any?): Plugin { - return this - } - - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - player ?: return false - val other = node?.asPlayer() - val delay = 41 - val speed = 60 - val distance = Location.getDistance(player.location, other?.getLocation()).toInt() - val projectileSpeed = delay + speed + distance * 5 - val hitDelay = (projectileSpeed * .02857).toInt() - - if(!Pathfinder.find(player,other,false, Pathfinder.PROJECTILE).isSuccessful){ - player.dialogueInterpreter.sendDialogue("You can't reach them!") - return true - } - - if(player.inventory.remove(snowball) || player.equipment.remove(snowball)){ - player.lock() - GameWorld.Pulser.submit(object : Pulse(hitDelay + 1){ - override fun pulse(): Boolean { - other?.animator?.graphics(Graphics(1282)) - return true - } - }) - GameWorld.Pulser.submit(object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 0 -> { - player.lock() - player.face(other) - player.animator.forceAnimation(THROW_ANIMATION) - } - 1 -> { - Projectile.create(player, other, 861, 30, 10).send() - player.face(player) - player.unlock() - } - 5 -> other?.sendMessage(colorize("%R${player.username} has pelted you with a snowball.")) - 6 -> return true - } - return false - } - }) - } else { - player.sendMessage("You have no more snowballs.") - Interaction.sendOption(player, 0, "null") - } - return true - } - - override fun isWalk(): Boolean { - return false - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/IsafdarListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/IsafdarListeners.kt deleted file mode 100644 index 18cd96443..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/IsafdarListeners.kt +++ /dev/null @@ -1,30 +0,0 @@ -package rs09.game.interaction.region - -import core.game.world.map.Location -import rs09.game.interaction.InteractionListener - -/** - * File to be used for anything Isafdar related. - * Handles the summoning/altar cave enter and exit in Isafdar. - * @author Sir Kermit - */ - -class IsafdarListeners : InteractionListener() { - - val CAVE_ENTRANCE = 4006 - val CAVE_EXIT = 4007 - val outside = Location.create(2312, 3217, 0) - val inside = Location.create(2314, 9624, 0) - - override fun defineListeners() { - on(CAVE_ENTRANCE,SCENERY,"enter"){ player, node -> - player.teleport(inside) - return@on true - } - - on(CAVE_EXIT,SCENERY,"exit"){ player, node -> - player.teleport(outside) - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/LumbridgeListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/LumbridgeListeners.kt deleted file mode 100644 index b4511f0cb..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/LumbridgeListeners.kt +++ /dev/null @@ -1,23 +0,0 @@ -package rs09.game.interaction.region - -import api.ContentAPI -import rs09.GlobalStats -import rs09.game.interaction.InteractionListener - -class LumbridgeListeners : InteractionListener() { - - val CHURCH_SIGN = 31299 - - override fun defineListeners() { - on(CHURCH_SIGN, SCENERY, "read"){player, _ -> - val deaths = GlobalStats.getDailyDeaths() - if(deaths > 0){ - ContentAPI.sendDialogue(player, "So far today $deaths unlucky adventurers have died on RuneScape and been sent to their respawn location. Be careful out there.") - } else { - ContentAPI.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.") - } - return@on true - } - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/MorytaniaListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/MorytaniaListeners.kt deleted file mode 100644 index b5f8dd5a6..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/MorytaniaListeners.kt +++ /dev/null @@ -1,99 +0,0 @@ -package rs09.game.interaction.region - -import api.ContentAPI -import core.game.content.global.action.DoorActionHandler -import core.game.node.entity.skill.agility.AgilityHandler -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.rs09.consts.NPCs -import org.rs09.consts.Scenery -import rs09.game.interaction.InteractionListener -import kotlin.random.Random - -/** - * File to be used for anything Morytania related. - * Handles the summoning/altar cave enter and exit in Morytania. - * @author Sir Kermit - */ - -class MorytaniaListeners : InteractionListener() { - - val SWAMP_GATES = intArrayOf(Scenery.GATE_3506, Scenery.GATE_3507) - val GROTTO_EXIT = intArrayOf(3525, 3526) - val GROTTO_BRIDGE = 3522 - val outside = Location.create(3439, 3337, 0) - val inside = Location.create(3442, 9734, 1) - - private val swimAnim = Animation(6988) - private val jumpAnim = Animation(1603) - private val failWater = Location(3439,3330) - private val failMessage = "You nearly drown in the disgusting swamp." - private val splashGFX = Graphics(68) - - override fun defineListeners() { - - on(SWAMP_GATES, SCENERY, "open"){player, node -> - if(player.location.y == 3457){ - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - GlobalScope.launch { - ContentAPI.findLocalNPC(player, NPCs.ULIZIUS_1054)?.sendChat("Oh my! You're still alive!", 2) - } - } else { - if (player.questRepository.hasStarted("Nature Spirit")) { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } else { - ContentAPI.sendNPCDialogue( - player, - NPCs.ULIZIUS_1054, - "I'm sorry, but I'm afraid it's too dangerous to let you through this gate right now." - ) - } - } - return@on true - } - - on(GROTTO_EXIT,SCENERY,"exit"){ player, node -> - player.teleport(outside) - return@on true - } - - on(GROTTO_BRIDGE,SCENERY,"jump"){ player, node -> - val start = node.location - var failLand = Location(3438,3331) - var failAnim = Animation(770) - var fromGrotto = false - - ContentAPI.lock(player,10) - - // Switch to south facing animations if jumping from Grotto - if (start.y == 3331) { - fromGrotto = true - failAnim = Animation(771) - failLand = Location(3438,3328) - } - if (AgilityHandler.hasFailed(player, 1, 0.1)) { - val end = if (fromGrotto) failWater else start - AgilityHandler.forceWalk(player, -1, start, end, failAnim, 15, 0.0, null,0).endAnimation = swimAnim - AgilityHandler.forceWalk(player, -1, failWater, failLand, swimAnim, 15, 2.0, null,3) - ContentAPI.submitIndividualPulse(player, object : Pulse(2){ - override fun pulse(): Boolean { - ContentAPI.visualize(player,failAnim,splashGFX) - ContentAPI.teleport(player,failWater) - // Deal 1-6 damage but wait until the player is back on land - AgilityHandler.fail(player,0,failLand,swimAnim,Random.nextInt(1,7),failMessage) - return true - } - }) - } - else{ - val end = if (fromGrotto) start.transform(0,-3,0) else start.transform(0,3,0) - AgilityHandler.forceWalk(player, -1, start, end, jumpAnim, 15, 15.0, null,0) - } - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/dungeons/brimhaven/BrimhavenDungeonListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/dungeons/brimhaven/BrimhavenDungeonListeners.kt deleted file mode 100644 index 311f0705f..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/dungeons/brimhaven/BrimhavenDungeonListeners.kt +++ /dev/null @@ -1,77 +0,0 @@ -package rs09.game.interaction.region.dungeons.brimhaven - -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.agility.AgilityHandler -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener - -class BrimhavenDungeonListeners : InteractionListener() { - - val EXIT = 5084 - val SANIBOCH = NPCs.SANIBOCH_1595 - val VINES = intArrayOf(5103,5104,5105,5106,5107) - val STEPPING_STONES = intArrayOf(5110,5111) - val STAIRS = intArrayOf(5094,5096,5097,5098) - val LOGS = intArrayOf(5088,5090) - - override fun defineListeners() { - on(EXIT,SCENERY,"leave"){ player, _ -> - player.properties.teleportLocation = Location.create(2745, 3152, 0) - return@on true - } - - on(STAIRS,SCENERY,"walk-up","walk-down"){ player, node -> - BrimhavenUtils.handleStairs(node.asScenery(),player) - return@on true - } - - on(STEPPING_STONES,SCENERY,"jump-from"){ player, node -> - BrimhavenUtils.handleSteppingStones(player,node.asScenery()) - return@on true - } - - on(VINES,SCENERY,"chop-down"){ player, node -> - BrimhavenUtils.handleVines(player,node.asScenery()) - return@on true - } - - on(SANIBOCH,NPC,"pay"){player, node -> - player.dialogueInterpreter.open(SANIBOCH,node.asNpc(),10) - return@on true - } - - on(LOGS,SCENERY,"walk-across"){ player, node -> - - if (player.skills.getLevel(Skills.AGILITY) < 30) { - player.packetDispatch.sendMessage("You need an agility level of 30 to cross this.") - return@on true - } - - if(node.id == 5088){ - AgilityHandler.walk( - player, - -1, - player.location, - Location.create(2687, 9506, 0), - Animation.create(155), - 0.0, - null - ) - } else { - AgilityHandler.walk( - player, - -1, - player.location, - Location.create(2682, 9506, 0), - Animation.create(155), - 0.0, - null - ) - } - - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/dungeons/taverley/TaverleyDungeonListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/dungeons/taverley/TaverleyDungeonListeners.kt deleted file mode 100644 index a1653061c..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/dungeons/taverley/TaverleyDungeonListeners.kt +++ /dev/null @@ -1,42 +0,0 @@ -package rs09.game.interaction.region.dungeons.taverley - -import api.ContentAPI -import core.game.content.global.action.DoorActionHandler -import org.rs09.consts.Items -import org.rs09.consts.Scenery -import rs09.game.interaction.InteractionListener - -class TaverleyDungeonListeners : InteractionListener() { - - val BD_GATE = Scenery.GATE_2623 - val JAIL_DOOR = Scenery.DOOR_31838 - - override fun defineListeners() { - - on(BD_GATE, SCENERY, "open"){player, node -> - if(!ContentAPI.inInventory(player, Items.DUSTY_KEY_1590)){ - ContentAPI.sendMessage(player, "This gate seems to be locked.") - } else { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } - return@on true - } - - - on(JAIL_DOOR, SCENERY, "open"){player, node -> - when(player.location.y){ - 9689 -> DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) //inside the cell going out - 9690 -> { //outside the cell going in - if(!ContentAPI.inInventory(player, Items.JAIL_KEY_1591)){ - ContentAPI.sendMessage(player, "This door is locked.") - } else { - DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) - } - } - } - - return@on true - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/falador/WineOfZamorakInteraction.kt b/Server/src/main/kotlin/rs09/game/interaction/region/falador/WineOfZamorakInteraction.kt deleted file mode 100644 index 839eea3f8..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/falador/WineOfZamorakInteraction.kt +++ /dev/null @@ -1,27 +0,0 @@ -package rs09.game.interaction.region.falador - -import core.game.node.item.GroundItem -import core.game.world.map.RegionManager -import org.rs09.consts.Items -import rs09.game.content.global.action.PickupHandler -import rs09.game.interaction.InteractionListener - -class WineOfZamorakInteraction : InteractionListener() { - - override fun defineListeners() { - on(Items.WINE_OF_ZAMORAK_245,ITEM,"take"){player, wine -> - if(player.location.regionId != 11574){ - PickupHandler.take(player, wine as GroundItem) - return@on true - } - val npcs = RegionManager.getLocalNpcs(player) - for (n in npcs) { - if (n.id == 188) { - n.sendChat("Hands off zamorak's wine!") - n.properties.combatPulse.attack(player) - } - } - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/JatizsoListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/JatizsoListeners.kt deleted file mode 100644 index 38d366122..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/JatizsoListeners.kt +++ /dev/null @@ -1,154 +0,0 @@ -package rs09.game.interaction.region.rellekka - -import api.ContentAPI -import core.game.content.dialogue.FacialExpression -import core.game.content.global.action.SpecialLadders -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 -import core.game.world.map.zone.ZoneBorders -import org.rs09.consts.NPCs -import rs09.game.content.dialogue.region.jatizso.LeftieRightieDialogue -import rs09.game.interaction.InteractionListener - -class JatizsoListeners : InteractionListener() { - val GATES_CLOSED = intArrayOf(21403,21405) - val NORTH_GATE_ZONE = ZoneBorders(2414,3822,2417,3825) - val WEST_GATE_ZONE = ZoneBorders(2386,3797,2390,3801) - val SOUTH_GAE_ZONE = ZoneBorders(2411,3795,2414,3799) - val BELL = 21394 - val TOWER_GUARDS = intArrayOf(NPCs.GUARD_5490, NPCs.GUARD_5489) - val GUARDS = intArrayOf(NPCs.GUARD_5491, NPCs.GUARD_5492) - val KING_CHEST = intArrayOf(21299,21300) - val LINES = arrayOf( - arrayOf( - "YOU WOULDN'T KNOW HOW TO FIGHT A TROLL IF IT CAME UP AND BIT YER ARM OFF", - "YAK LOVERS", - "CALL THAT A TOWN? I'VE SEEN BETTER HAMLETS!", - "AND YOUR FATHER SMELLED OF WINTERBERRIES!", - "WOODEN BRIDGES ARE RUBBISH!", - "OUR KING'S BETTER THAN YOUR BURGHER!", - ), - arrayOf( - "YOU WOULDN'T KNOW HOW TO SHAVE A YAK IF YOUR LIFE DEPENDED ON IT", - "MISERABLE LOSERS", - "YOUR MOTHER WAS A HAMSTER!", - "AT LEAST WE HAVE SOMETHING OTHER THAN SMELLY FISH!", - "AT LEAST WE CAN COOK!", - ) - ) - override fun defineListeners() { - on(GATES_CLOSED, SCENERY, "open"){player, node -> - if(NORTH_GATE_ZONE.insideBorder(player)){ - if(node.id == GATES_CLOSED.first()){ - val other = ContentAPI.getScenery(node.location.transform(1, 0, 0)) ?: return@on true - ContentAPI.replaceScenery(node.asScenery(), node.id + 1, -1, Direction.EAST) - ContentAPI.replaceScenery(other.asScenery(), other.id - 1, -1, Direction.NORTH_EAST) - } else { - val other = ContentAPI.getScenery(node.location.transform(-1, 0, 0)) ?: return@on true - ContentAPI.replaceScenery(node.asScenery(), node.id + 1, -1, Direction.NORTH_EAST) - ContentAPI.replaceScenery(other.asScenery(), other.id, -1, Direction.EAST) - } - } else if(WEST_GATE_ZONE.insideBorder(player)){ - if(node.id == GATES_CLOSED.first()){ - val other = ContentAPI.getScenery(node.location.transform(0, 1, 0)) ?: return@on true - ContentAPI.replaceScenery(node.asScenery(), node.id + 1, -1, Direction.WEST) - ContentAPI.replaceScenery(other.asScenery(), other.id + 1, -1, Direction.NORTH) - } else { - val other = ContentAPI.getScenery(node.location.transform(0, -1, 0)) ?: return@on true - ContentAPI.replaceScenery(node.asScenery(), node.id + 1, -1, Direction.NORTH) - ContentAPI.replaceScenery(other.asScenery(), other.id + 1, -1, Direction.WEST) - } - } else if(SOUTH_GAE_ZONE.insideBorder(player)){ - if(node.id == GATES_CLOSED.first()){ - val other = ContentAPI.getScenery(node.location.transform(-1, 0, 0)) ?: return@on true - ContentAPI.replaceScenery(node.asScenery(), node.id + 1, -1, Direction.SOUTH) - ContentAPI.replaceScenery(other.asScenery(), other.id - 1, -1, Direction.EAST) - } else { - val other = ContentAPI.getScenery(node.location.transform(1, 0, 0)) ?: return@on true - ContentAPI.replaceScenery(node.asScenery(), node.id + 1, -1, Direction.EAST) - ContentAPI.replaceScenery(other.asScenery(), other.id, -1, Direction.SOUTH) - } - } - ContentAPI.playAudio(player, ContentAPI.getAudio(81)) - return@on true - } - - on(TOWER_GUARDS, NPC, "watch-shouting"){player, _ -> - val local = ContentAPI.findLocalNPC(player, NPCs.GUARD_5489) - ContentAPI.lock(player, 200) - ContentAPI.face(local!!, Location.create(2371, 3801, 2)) - local.asNpc().isRespawn = false - ContentAPI.submitIndividualPulse(player, object : Pulse(4){ - var id = NPCs.GUARD_5489 - var counter = 0 - val other = ContentAPI.findLocalNPC(player, getOther(NPCs.GUARD_5489)) - - override fun start() { - other?.isRespawn = false - } - - override fun pulse(): Boolean { - val npc = ContentAPI.findLocalNPC(player, id) ?: return false - val index = when(id){ - NPCs.GUARD_5489 -> 0 - else -> 1 - } - if(index == 1 && counter == 5) return true - ContentAPI.sendChat(npc, LINES[index][counter]) - if(npc.id != NPCs.GUARD_5489) counter++ - id = getOther(id) - return false - } - - override fun stop() { - ContentAPI.unlock(player) - other?.isRespawn = true - local.asNpc().isRespawn = true - super.stop() - } - - fun getOther(npc: Int): Int{ - return when(npc){ - NPCs.GUARD_5489 -> NPCs.GUARD_5490 - NPCs.GUARD_5490 -> NPCs.GUARD_5489 - else -> NPCs.GUARD_5489 - } - } - }) - return@on true - } - - on(BELL, SCENERY, "ring-bell"){player, _ -> - ContentAPI.playAudio(player, Audio(15)) - ContentAPI.sendMessage(player, "You ring the warning bell, but everyone ignores it!") - return@on true - } - - on(GUARDS, NPC, "talk-to"){player, node -> - player.dialogueInterpreter.open(LeftieRightieDialogue(), NPC(NPCs.GUARD_5491)) - return@on true - } - - on(KING_CHEST, SCENERY, "open"){player, node -> - ContentAPI.sendPlayerDialogue(player, "I probably shouldn't mess with that.", FacialExpression.HALF_THINKING) - return@on true - } - - setDest(NPC, NPCs.MAGNUS_GRAM_5488){_,_ -> - return@setDest Location.create(2416, 3801, 0) - } - - //Climb handling for the ladders in the towers around the city walls. - ContentAPI.addClimbDest(Location.create(2388, 3804, 0),Location.create(2387, 3804, 2)) - ContentAPI.addClimbDest(Location.create(2388, 3804, 2),Location.create(2387, 3804, 0)) - ContentAPI.addClimbDest(Location.create(2388, 3793, 0),Location.create(2387, 3793, 2)) - ContentAPI.addClimbDest(Location.create(2388, 3793, 2),Location.create(2387, 3793, 0)) - ContentAPI.addClimbDest(Location.create(2410, 3823, 0),Location.create(2410, 3824, 2)) - ContentAPI.addClimbDest(Location.create(2410, 3823, 2),Location.create(2410, 3824, 0)) - ContentAPI.addClimbDest(Location.create(2421, 3823, 0),Location.create(2421, 3824, 2)) - ContentAPI.addClimbDest(Location.create(2421, 3823, 2),Location.create(2421, 3824, 0)) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/NeitiznotListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/NeitiznotListeners.kt deleted file mode 100644 index 2526bdbde..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/NeitiznotListeners.kt +++ /dev/null @@ -1,39 +0,0 @@ -package rs09.game.interaction.region.rellekka - -import api.ContentAPI -import core.game.content.dialogue.FacialExpression -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.map.Location -import core.game.world.map.zone.MapZone -import core.game.world.map.zone.ZoneBorders -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener - -class NeitiznotListeners : InteractionListener() { - val STUMP = 21305 - - override fun defineListeners() { - - on(STUMP, SCENERY, "cut-wood"){player, _ -> - ContentAPI.sendPlayerDialogue(player, "I should probably leave this alone.", FacialExpression.HALF_THINKING) - return@on true - } - - val zone = object : MapZone("Yakzone", true){ - override fun handleUseWith(player: Player, used: Item?, with: Node?): Boolean { - if(with is NPC && with.id == NPCs.YAK_5529){ - ContentAPI.sendMessage(player, "The cow doesn't want that.") - return true - } - return false - } - } - - ContentAPI.registerMapZone(zone, ZoneBorders(2313,3786,2331,3802)) - ContentAPI.addClimbDest(Location.create(2363, 3799, 0), Location.create(2364, 3799, 2)) - ContentAPI.addClimbDest(Location.create(2363, 3799, 2), Location.create(2362, 3799, 0)) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/RellekkaListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/RellekkaListeners.kt deleted file mode 100644 index b330b22c6..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/RellekkaListeners.kt +++ /dev/null @@ -1,70 +0,0 @@ -package rs09.game.interaction.region.rellekka - -import core.game.world.map.Location -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener -import rs09.game.util.region.rellekka.RellekkaDestination -import rs09.game.util.region.rellekka.RellekkaUtils - -/** - * File to be used for anything Rellekka related. - * Handles the snow kebbit stairs up and down. - * @author Ceikry - */ - -class RellekkaListeners : InteractionListener() { - - val UP1A = Location.create(2715, 3798, 0) - val UP1B = Location.create(2716, 3798, 0) - val UP2A = Location.create(2726, 3801, 0) - val UP2B = Location.create(2727, 3801, 0) - - val DOWN1A = Location.create(2715, 3802, 1) - val DOWN1B = Location.create(2716, 3802, 1) - val DOWN2A = Location.create(2726, 3805, 1) - val DOWN2B = Location.create(2727, 3805, 1) - val STAIRS = intArrayOf(19690,19691) - - override fun defineListeners() { - on(STAIRS,SCENERY,"ascend","descend"){ player, node -> - if(player.location.y < 3802){ - player.properties.teleportLocation = when(player.location.x){ - 2715 -> DOWN1A - 2716 -> DOWN1B - 2726 -> DOWN2A - 2727 -> DOWN2B - else -> player.location - } - } else { - player.properties.teleportLocation = when(player.location.x) { - 2715 -> UP1A - 2716 -> UP1B - 2726 -> UP2A - 2727 -> UP2B - else -> player.location - } - } - return@on true - } - - on(NPCs.MARIA_GUNNARS_5508, NPC, "ferry-neitiznot"){player, _ -> - RellekkaUtils.sail(player, RellekkaDestination.RELLEKKA_TO_NEITIZNOT) - return@on true - } - - on(NPCs.MARIA_GUNNARS_5507, NPC, "ferry-rellekka"){player, node -> - RellekkaUtils.sail(player, RellekkaDestination.NEITIZNOT_TO_RELLEKKA) - return@on true - } - - on(NPCs.MORD_GUNNARS_5481, NPC, "ferry-jatizso"){player, node -> - RellekkaUtils.sail(player, RellekkaDestination.RELLEKKA_TO_JATIZSO) - return@on true - } - - on(NPCs.MORD_GUNNARS_5482, NPC, "ferry-rellekka"){player, node -> - RellekkaUtils.sail(player, RellekkaDestination.JATIZSO_TO_RELLEKKA) - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/wilderness/RoguesCastleListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/wilderness/RoguesCastleListeners.kt deleted file mode 100644 index 53af624dd..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/region/wilderness/RoguesCastleListeners.kt +++ /dev/null @@ -1,143 +0,0 @@ -package rs09.game.interaction.region.wilderness - -import api.ContentAPI -import core.game.node.scenery.Scenery -import core.game.node.entity.combat.ImpactHandler -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.tools.RandomFunction -import org.rs09.consts.Items -import rs09.game.content.global.WeightBasedTable -import rs09.game.content.global.WeightedItem -import rs09.game.interaction.InteractionListener - -class RoguesCastleListeners : InteractionListener() { - - val CHEST_ANIM = ContentAPI.getAnimation(536) - val FLOOR_1_CHESTS = intArrayOf(14773, 14774) - val FLOOR_2_CHESTS = intArrayOf(38834, 38835) - - override fun defineListeners() { - on(FLOOR_1_CHESTS, SCENERY, "open"){ player, node -> - val scenery = node.asScenery() - openChest(player, scenery) - return@on true - } - - on(FLOOR_1_CHESTS, SCENERY, "search"){player, node -> - val scenery = node.asScenery() - if(ContentAPI.getCharge(scenery) == 0){ - ContentAPI.sendMessage(player, "This chest has already been looted.") - return@on true - } - - if(ContentAPI.freeSlots(player) == 0){ - ContentAPI.sendMessage(player, "You don't have enough space to do that.") - return@on true - } - - val item = FLOOR_1_LOOT.roll()[0] - addLoot(player, item) - ContentAPI.setCharge(scenery, 0) - return@on true - } - - on(FLOOR_2_CHESTS, SCENERY, "open"){player, node -> - ContentAPI.sendMessage(player, "This chest appears to be locked.") - return@on true - } - - on(FLOOR_2_CHESTS, SCENERY, "pick-lock"){player, node -> - val scenery = node.asScenery() - if(!ContentAPI.inInventory(player, Items.LOCKPICK_1523)){ - ContentAPI.sendMessage(player, "You need a lockpick in order to attempt this.") - return@on true - } - - if(!ContentAPI.hasLevelDyn(player, Skills.THIEVING, 13)){ - ContentAPI.sendMessage(player, "You need a Thieving level of 13 to attempt this.") - return@on true - } - - ContentAPI.sendMessage(player, "You attempt to pick the lock on the chest...") - ContentAPI.submitIndividualPulse(player, object : Pulse(2){ - override fun pulse(): Boolean { - val success = RandomFunction.roll(10) // 1/10 chance to succeed - if(success){ - ContentAPI.replaceScenery(scenery, scenery.id + 1, 20) - ContentAPI.rewardXP(player, Skills.THIEVING, 300.0) - } else { - val dealsDamage = RandomFunction.roll(10) // 1/10 chance to deal damage on a fail - if(dealsDamage) { - ContentAPI.impact(player, RandomFunction.random(1, 3), ImpactHandler.HitsplatType.NORMAL) - ContentAPI.sendMessage(player, "You activated a trap on the chest!") - } - } - - ContentAPI.sendMessage(player, "You ${if(success) "manage" else "fail"} to pick the lock on the chest.") - return true - } - }) - - return@on true - } - - on(FLOOR_2_CHESTS, SCENERY, "search"){player, node -> - val scenery = node.asScenery() - - if(ContentAPI.getCharge(scenery) == 0){ - ContentAPI.sendMessage(player, "This chest has already been looted.") - return@on true - } - - val loot = FLOOR_2_LOOT.roll()[0] - if(ContentAPI.freeSlots(player) > 0){ - ContentAPI.addItemOrDrop(player, loot.id, loot.amount) - ContentAPI.sendMessage(player, "In the chest you find some ${loot.name.toLowerCase() + if(!loot.name.endsWith("s")) "s" else ""}!") - ContentAPI.setCharge(scenery, 0) - ContentAPI.rewardXP(player, Skills.THIEVING, 60.0) - } - - return@on true - } - } - - fun openChest(player: Player, scenery: Scenery){ - ContentAPI.animate(player, CHEST_ANIM) - ContentAPI.submitIndividualPulse(player, object : Pulse(ContentAPI.animationDuration(CHEST_ANIM)){ - override fun pulse(): Boolean { - return true.also { ContentAPI.replaceScenery(scenery, scenery.id + 1, 20) } - } - }) - } - - fun addLoot(player: Player, item: Item){ - ContentAPI.sendMessage(player, "You search the chest...") - ContentAPI.submitIndividualPulse(player, object : Pulse(){ - override fun pulse(): Boolean { - ContentAPI.sendMessage(player, "... and find some ${item.name.toLowerCase() + if(!item.name.endsWith("s")) "s" else ""}!") - ContentAPI.addItemOrDrop(player, item.id, item.amount) - return true - } - }) - } - - val FLOOR_1_LOOT = WeightBasedTable.create( - WeightedItem(Items.COINS_995, 8, 25, 70.0), - WeightedItem(Items.NATURE_RUNE_561, 2, 3, 10.0), - WeightedItem(Items.BLOOD_RUNE_565, 2, 3, 10.0), - WeightedItem(Items.DEATH_RUNE_560, 3, 5, 10.0) - ) - - val FLOOR_2_LOOT = WeightBasedTable.create( - WeightedItem(Items.COINS_995, 4, 57, 75.0), - WeightedItem(Items.COINS_995, 107, 243, 5.0), - WeightedItem(Items.BLOOD_RUNE_565, 2, 5, 5.0), - WeightedItem(Items.GOLD_ORE_445, 1, 1, 5.0), - WeightedItem(Items.STEEL_MED_HELM_1141, 1, 1, 5.0), - WeightedItem(Items.STEEL_PLATELEGS_1069, 1, 1, 5.0) - ) - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatSwingHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatSwingHandler.kt deleted file mode 100644 index 80f8bbcd1..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatSwingHandler.kt +++ /dev/null @@ -1,579 +0,0 @@ -package rs09.game.node.entity.combat - -import core.game.component.Component -import core.game.container.Container -import core.game.container.impl.EquipmentContainer -import core.game.node.Node -import core.game.node.entity.Entity -import core.game.node.entity.combat.BattleState -import core.game.node.entity.combat.CombatStyle -import core.game.node.entity.combat.InteractionType -import core.game.node.entity.combat.equipment.* -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.player.link.prayer.PrayerType -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.summoning.familiar.Familiar -import core.game.world.map.RegionManager -import core.game.world.map.path.Pathfinder -import core.game.world.update.flag.context.Animation -import core.tools.RandomFunction -import rs09.game.system.SystemLogger -import rs09.game.system.config.ItemConfigParser -import java.util.* -import kotlin.math.floor - -/** - * Handles a combat swing. - * @author Emperor - * @author Ceikry - Kotlin refactoring, general cleanup - */ -abstract class CombatSwingHandler(var type: CombatStyle?) { - /** - * The mapping of the special attack handlers. - */ - private var specialHandlers: MutableMap? = null - - /** - * Starts the combat swing. - * @param entity The attacking entity. - * @param victim The victim. - * @param state The battle state instance. - * @return The amount of ticks before impact of the attack. - */ - abstract fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int - - /** - * Handles the impact of the combat swing (victim getting hit). - * @param entity The attacking entity. - * @param victim The victim. - * @param state The battle state instance. - */ - abstract fun impact(entity: Entity?, victim: Entity?, state: BattleState?) - - /** - * Visualizes the impact itself (end animation, end GFX, ...) - * @param entity The attacking entity. - * @param victim The victim. - * @param state The battle state instance. - */ - abstract fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) - - /** - * Calculates the maximum accuracy of the entity. - * @param entity The entity. - * @return The maximum accuracy value. - */ - abstract fun calculateAccuracy(entity: Entity?): Int - - /** - * Calculates the maximum strength of the entity. - * @param entity The entity. - * @param victim The victim. - * @param modifier The modifier. - * @return The maximum strength value. - */ - abstract fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int - - /** - * Calculates the maximum defence of the entity. - * @param victim The entity. - * @param attacker The entity to defend against. - * @return The maximum defence value. - */ - abstract fun calculateDefence(victim: Entity?, attacker: Entity?): Int - - /** - * Gets the void set multiplier. - * @param e The entity. - * @param skillId The skill id. - * @return The multiplier. - */ - abstract fun getSetMultiplier(e: Entity?, skillId: Int): Double - - /** - * Visualizes the combat swing (start animation, GFX, projectile, ...) - * @param entity The attacking entity. - * @param victim The victim. - * @param state The battle state instance. - */ - open fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { - entity.animate(getAttackAnimation(entity, type)) - } - - /** - * Method called when the impact method got called. - * @param entity The attacking entity. - * @param victim The victim. - * @param state The battle state. - */ - fun onImpact(entity: Entity?, victim: Entity?, state: BattleState?) { - if (entity is Player && victim != null) { - DegradableEquipment.degrade(entity as Player?, victim, true) - } - if (state == null) { - return - } - if (state.targets != null && state.targets.isNotEmpty()) { - if (!(state.targets.size == 1 && state.targets[0] == state)) { - for (s in state.targets) { - if (s != null && s != state) { - onImpact(entity, s.victim, s) - } - } - return - } - } - victim!!.onImpact(entity, state) - } - - /** - * Gets the currently worn armour set, if any. - * @param e The entity. - * @return The armour set worn. - */ - open fun getArmourSet(e: Entity?): ArmourSet? { - return null - } - - /** - * Checks if the hit will be accurate. - * @param entity The entity. - * @param victim The victim. - * @return `True` if the hit is accurate. - */ - fun isAccurateImpact(entity: Entity?, victim: Entity?): Boolean { - return isAccurateImpact(entity, victim, type, 1.0, 1.0) - } - - /** - * Checks if the hit will be accurate. - * @param entity The entity. - * @param victim The victim. - * @param style The combat style used. - * @return `True` if the hit is accurate. - */ - fun isAccurateImpact(entity: Entity?, victim: Entity?, style: CombatStyle?): Boolean { - return isAccurateImpact(entity, victim, style, 1.0, 1.0) - } - - /** - * Checks if the hit will be accurate. - * @param entity The entity. - * @param victim The victim. - * @param style The combat style (null to ignore prayers). - * @param accuracyMod The accuracy modifier. - * @param defenceMod The defence modifier. - * @return `True` if the hit is accurate. - */ - fun isAccurateImpact(entity: Entity?, victim: Entity?, style: CombatStyle?, accuracyMod: Double, defenceMod: Double): Boolean { - var mod = 1.33 - 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 chance: Double = if (attack > defence) { - 1 - ((defence + 2) / ((2 * attack) + 1)) - } else { - 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("Should hit: $it") } - } - - /** - * Checks if the entity can execute a combat swing at the victim. - * @param entity The entity. - * @param victim The victim. - * @return `True` if so. - */ - open fun canSwing(entity: Entity, victim: Entity): InteractionType? { - return isAttackable(entity, victim) - } - - /** - * Checks if the victim can be attacked by the entity. - * @param entity The attacking entity. - * @param victim The entity being attacked. - * @return `True` if so. - */ - open fun isAttackable(entity: Entity, victim: Entity): InteractionType? { - val comp = entity.getAttribute("autocast_component",null) as Component? - if((comp != null || type == CombatStyle.MAGIC) && (entity.properties.autocastSpell == null || entity.properties.autocastSpell.spellId == 0) && entity is Player){ - val weapEx = entity.getExtension(WeaponInterface::class.java) as WeaponInterface? - if(comp != null){ - entity.interfaceManager.close(comp) - entity.interfaceManager.openTab(weapEx) - entity.properties.combatPulse.stop() - entity.attack(victim) - entity.removeAttribute("autocast_component") - } - weapEx?.setAttackStyle(1) - weapEx?.updateInterface() - entity.debug("Adjusting attack style") - } - if (entity.location == victim.location) { - return if (entity.index < victim.index && victim.properties.combatPulse.getVictim() === entity) { - InteractionType.STILL_INTERACT - } else InteractionType.NO_INTERACT - } - val el = entity.location - val vl = victim.location - val evl = vl.transform(victim.size(), victim.size(), 0) - if (el.x >= vl.x && el.x < evl.x && el.y >= vl.y && el.y < evl.y || el.z != vl.z) { - return InteractionType.NO_INTERACT - } - return InteractionType.STILL_INTERACT - } - - /** - * Gets the dragonfire message. - * @param protection The protection value. - * @param fireName The fire breath name. - * @return The message to send. - */ - fun getDragonfireMessage(protection: Int, fireName: String): String { - if (protection and 0x4 != 0) { - 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." - } - 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 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." - } - - /** - * Visualizes the audio. - * @param entity the entity. - * @param victim the victim. - * @param state the state. - */ - fun visualizeAudio(entity: Entity, victim: Entity, state: BattleState) { - if (entity is Player) { - val styleIndex = entity.settings.attackStyleIndex - if (state.weapon != null && state.weapon.item != null) { - val weapon = state.weapon.item - val audios = weapon.definition.getConfiguration>(ItemConfigParser.ATTACK_AUDIO, null) - if (audios != null) { - var audio: Audio? = null - if (styleIndex < audios.size) { - audio = audios[styleIndex] - } - if (audio == null || audio.id == 0) { - audio = audios[0] - } - entity.asPlayer().audioManager.send(audio, true) - } - } else { - entity.asPlayer().audioManager.send(2564) - } - } else if (entity is NPC && victim is Player) { - val npc = entity.asNpc() - val audio = npc.getAudio(0) - audio?.send(victim.asPlayer(), true) - } - } - - /** - * Gets the combat distance. - * @param e The entity. - * @param v The victim. - * @param rawDistance The distance. - * @return The actual distance used for combat. - */ - open fun getCombatDistance(e: Entity, v: Entity, rawDistance: Int): Int { - var distance = rawDistance - if (e is NPC) { - if (e.definition.combatDistance > 0) { - distance = e.definition.combatDistance - } - } - return (e.size() shr 1) + (v.size() shr 1) + distance - } - - /** - * Formats the hit for the victim. (called as - * victim.getSwingHandler(false).formatHit(victim, hit)) - * @param victim The entity receiving the hit. - * @param rawHit The hit to format. - * @return The formatted hit. - */ - fun formatHit(victim: Entity, rawHit: Int): Int { - var hit = rawHit - if (hit < 1) { - return hit - } - if (hit > victim.skills.lifepoints) { - hit = victim.skills.lifepoints - } - return hit - } - - /** - * Adjusts the battle state object for this combat swing. - * @param entity The attacking entity. - * @param victim The victim. - * @param state The battle state. - */ - open fun adjustBattleState(entity: Entity, victim: Entity, state: BattleState) { - EXPERIENCE_MOD = 4.0 - var totalHit = 0 - if (entity is Player) { - entity.familiarManager.adjustBattleState(state) - } - entity.sendImpact(state) - victim.checkImpact(state) - //Prevents lumbridge dummies from dying (true to how rs3 / 2009scape in 2009 does it) - if (victim.id == 4474 && type == CombatStyle.MAGIC || victim.id == 7891 && type == CombatStyle.MELEE) { - EXPERIENCE_MOD = 0.1 - victim.fullRestore() - if (state.estimatedHit >= 15) { - state.estimatedHit = 14 - } - if (state.secondaryHit >= 15) { - state.secondaryHit = 14 - } - } - if (victim.id == 757) { - EXPERIENCE_MOD = 0.01 - } - // Recursively adjustBattleState targets so that multi-target attacks have protection prayers applied. - if (state.targets != null && state.targets.isNotEmpty()) { - if (!(state.targets.size == 1 && state.targets[0] == state)) { - for (s in state.targets) { - if (s != null && s != state) { - adjustBattleState(entity, victim, s); - } - } - } - } - if (state.estimatedHit > 0) { - state.estimatedHit = getFormattedHit(entity, victim, state, state.estimatedHit) - totalHit += state.estimatedHit - } - if (state.secondaryHit > 0) { - state.secondaryHit = getFormattedHit(entity, victim, state, state.secondaryHit) - totalHit += state.secondaryHit - } - if (entity is Player) { - entity.degrader.checkWeaponDegrades(entity) - if (totalHit > 0 && entity.prayer[PrayerType.SMITE] && victim.skills.prayerPoints > 0) { - victim.skills.decrementPrayerPoints(totalHit * 0.25) - } - if (entity.getAttribute("1hko", false)) { - state.estimatedHit = victim.skills.lifepoints - } - } - if (victim is NPC) { - if (victim.properties.protectStyle != null && state.style == victim.properties.protectStyle) { - state.neutralizeHits() - } - } - } - - /** - * Adds the experience for the current combat swing. - * @param entity The attacking entity. - * @param victim The victim. - * @param state The battle state. - */ - open fun addExperience(entity: Entity?, victim: Entity?, state: BattleState?) { - if (entity == null || (victim is Player && entity is Player && entity.asPlayer().ironmanManager.isIronman)) { - return - } - var player: Player - var attStyle: Int - - when(entity) - { - is Familiar -> {player = entity.owner; attStyle = entity.attackStyle} - is Player -> {player = entity; attStyle = entity.properties.attackStyle.style} - else -> return - } - if (state != null) { - val hit = state.totalDamage - var skill = -1 - when (attStyle) { - WeaponInterface.STYLE_DEFENSIVE -> skill = Skills.DEFENCE - WeaponInterface.STYLE_ACCURATE -> skill = Skills.ATTACK - WeaponInterface.STYLE_AGGRESSIVE -> skill = Skills.STRENGTH - WeaponInterface.STYLE_CONTROLLED -> { - var experience = hit * EXPERIENCE_MOD - experience /= 3.0 - player.skills.addExperience(Skills.ATTACK, experience, true) - player.skills.addExperience(Skills.STRENGTH, experience, true) - player.skills.addExperience(Skills.DEFENCE, experience, true) - return - } - - WeaponInterface.STYLE_RANGE_ACCURATE -> skill = Skills.RANGE - WeaponInterface.STYLE_RAPID -> skill = Skills.RANGE - WeaponInterface.STYLE_LONG_RANGE -> { - player.skills.addExperience(Skills.RANGE, hit * (EXPERIENCE_MOD / 2), true) - player.skills.addExperience(Skills.DEFENCE, hit * (EXPERIENCE_MOD / 2), true) - return - } - - 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) - return - } - } - if (skill < 0) return - player.skills.addExperience(skill, hit * EXPERIENCE_MOD, true) - player.skills.addExperience(Skills.HITPOINTS, hit * 1.33, true) - } - } - - /** - * Gets the formated hit. - * @param attacker The attacking entity. - * @param victim The victim. - * @param state The battle state. - * @param rawHit The hit to format. - * @return The formated hit. - */ - protected open fun getFormattedHit(attacker: Entity, victim: Entity, state: BattleState, rawHit: Int): Int { - var hit = rawHit - hit = attacker.getFormattedHit(state, hit).toInt() - if (victim is Player) { - val player = victim.asPlayer() - val shield = player.equipment[EquipmentContainer.SLOT_SHIELD] - if (shield != null && shield.id == 13742) { - if (RandomFunction.random(100) < 71) { - hit -= (hit.toDouble() * 0.25).toInt() - if (hit < 1) { - hit = 0 - } - } - } - if (shield != null && shield.id == 13740) { - val reduce = hit.toDouble() * 0.30 - var drain = hit * 0.15 - if (player.skills.prayerPoints > drain && drain > 0) { - if (drain < 1) { - drain = 1.0 - } - hit -= reduce.toInt() - if (hit < 1) { - hit = 0 - } - player.skills.decrementPrayerPoints(drain) - } - } - } - if (attacker is Player) { - val player = attacker.asPlayer() - if (player.equipment[3] != null && player.equipment[3].id == 14726 && state.style == CombatStyle.MAGIC) { - hit += (hit.toDouble() * 0.15).toInt() - } - } - if (attacker is Familiar && victim is Player) { - if (victim.prayer[PrayerType.PROTECT_FROM_SUMMONING]) { - hit = 0 - } - } - return formatHit(victim, hit) - } - - /** - * Gets the default animation of the entity. - * @param e The entity. - * @param style The combat style. - * @return The attack animation. - */ - fun getAttackAnimation(e: Entity, style: CombatStyle?): Animation { - var anim: Animation? = null - if (type != null && e is NPC) { - anim = e.properties.getCombatAnimation(style!!.ordinal % 3) - } - return anim ?: e.properties.attackAnimation - } - - /** - * Registers a special attack handler. - * @param itemId The item id. - * @param handler The combat swing handler. - * @return `True` if succesful. - */ - fun register(itemId: Int, handler: CombatSwingHandler): Boolean { - if (specialHandlers == null) { - specialHandlers = HashMap() - } - if (specialHandlers!!.containsKey(itemId)) { - SystemLogger.logErr("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 - } - - /** - * Gets the special attack handler for the given item id. - * @param itemId The item id. - * @return The special attack handler, or `null` if this item has no - * special attack handler. - */ - fun getSpecial(itemId: Int): CombatSwingHandler? { - if (specialHandlers == null) { - specialHandlers = HashMap() - } - return specialHandlers!![itemId] - } - - companion object { - /** - * The amount of experience to get per hit. - */ - @JvmField - var EXPERIENCE_MOD = 4.0 - - /** - * Checks if a projectile can be fired from the node location to the victim - * location. - * @param entity The node. - * @param victim The victim. - * @param checkClose If we are checking for a melee attack rather than a - * projectile. - * @return `True` if so. - */ - @JvmStatic - fun isProjectileClipped(entity: Node, victim: Node?, checkClose: Boolean): Boolean { - for(x1 in 0 until entity.size()) { - for(y1 in 0 until entity.size()) { - val src = entity.location.transform(x1, y1, 0) - 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) - if(path.isSuccessful && (!checkClose || path.points.size <= 1)) { - return true - } - } - } - } - } - return false - } - } - -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/equipment/EquipmentDegrader.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/equipment/EquipmentDegrader.kt deleted file mode 100644 index 76bea28ce..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/equipment/EquipmentDegrader.kt +++ /dev/null @@ -1,88 +0,0 @@ -package rs09.game.node.entity.combat.equipment - -import core.game.node.entity.player.Player -import core.game.node.item.Item -import org.rs09.primextends.getNext -import org.rs09.primextends.isLast -import org.rs09.primextends.isNextLast - -/** - * Handles equipment degrading - * @author ceik - */ -class EquipmentDegrader{ - - companion object StaticDegrader { //Use a static companion object for lists and registers so they persist across instances - val itemList = arrayListOf() - val setList = arrayListOf>() - fun register(item: Int){ - itemList.add(item) - } - - fun registerSet(items: Array){ - setList.add(ArrayList(items.map { item -> item.also { register(item) } })) - } - } - - - - var p: Player? = null - - fun Int.canDegrade(): Boolean { //extension function that checks if an item can degrade - return itemList.contains(this) - } - - fun checkArmourDegrades(player: Player?){ //loops the player slots and if the item in that slot is non-null, checks if it can degrade etc - p = player - for(slot in 0..12){ - if(slot == 3){ - continue - } - player?.equipment?.get(slot)?.let { if(it.id.canDegrade()) it.degrade(slot) } - } - } - - fun checkWeaponDegrades(player: Player?){ - p = player - player?.equipment?.get(3)?.let { if(it.id.canDegrade()) it.degrade(3) } - } - - fun Item.degrade(slot: Int){ //extension function that degrades items - val set = getDegradableSet(this.id) - this.charge-- - if(set?.indexOf(this.id) == 0 && !this.name.contains("100")){ - charge = 0 - } - if(this.charge <= 0) { - if (set?.size!! > 2) { - p?.equipment?.remove(this) - p?.sendMessage("Your $name has degraded.") - if (set.isNextLast(set.indexOf(this.id))) { - p?.inventory?.add(Item(set.getNext(this.id))) - p?.sendMessage("Your $name has broken.") - } else { - p?.equipment?.add(Item(set.getNext(this.id)), slot, false, false) - p?.equipment?.refresh() - } - } else if (set.size == 2) { - if(set.isLast(set.indexOf(this.id))){ - p?.equipment?.remove(this) - p?.sendMessage("Your $name degrades into dust.") - } else { - p?.equipment?.remove(this) - p?.sendMessage("Your $name has degraded.") - p?.equipment?.add(Item(set.getNext(this.id)), slot, false, false) - p?.equipment?.refresh() - } - } - } - } - - private fun getDegradableSet(item: Int): ArrayList?{ //gets the set of items this item belongs to - for(set in setList){ - if(set.contains(item)) - return set - } - return null - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MagicSwingHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MagicSwingHandler.kt deleted file mode 100644 index 5240d6c4a..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MagicSwingHandler.kt +++ /dev/null @@ -1,236 +0,0 @@ -package rs09.game.node.entity.combat.handlers - -import core.game.container.Container -import core.game.container.impl.EquipmentContainer -import core.game.content.quest.tutorials.tutorialisland.TutorialSession -import core.game.content.quest.tutorials.tutorialisland.TutorialStage -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.equipment.ArmourSet -import core.game.node.entity.combat.equipment.SpellType -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.tools.RandomFunction -import rs09.game.node.entity.combat.CombatSwingHandler -import kotlin.math.floor - -/** - * Handles the magic combat swings. - * @author Emperor - * @author Ceikry, Kotlin conversion + cleanup - */ -open class MagicSwingHandler - : CombatSwingHandler(CombatStyle.MAGIC) { - - override fun canSwing(entity: Entity, victim: Entity): InteractionType? { - if (!isProjectileClipped(entity, victim, false)) { - return InteractionType.NO_INTERACT - } - var distance = 10 - var type = InteractionType.STILL_INTERACT - var goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) - if (victim.walkingQueue.isMoving && !goodRange) { - goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) - type = InteractionType.MOVE_INTERACT - } - if (goodRange && isAttackable(entity, victim) != 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 spell = entity!!.properties.spell - if (spell == null) { - if (entity.properties.autocastSpell.also { spell = it } == null) { - return -1 - } - } - state!!.style = CombatStyle.MAGIC - if (!spell!!.meetsRequirements(entity, true, true)) { - entity.properties.spell = null - val inter = entity.getExtension(WeaponInterface::class.java) - if (inter != null) { - inter.selectAutoSpell(-1, true) - entity.properties.combatPulse.updateStyle() - } - return -1 - } - val max = calculateHit(entity, victim, spell!!.getMaximumImpact(entity, victim, state).toDouble()) - state.targets = spell!!.getTargets(entity, victim) - state.spell = spell - for (s in state.targets) { - var hit = -1 - s.spell = spell - if (isAccurateImpact(entity, s.victim, CombatStyle.MAGIC, 1.3, 1.0)) { - s.maximumHit = max - hit = RandomFunction.random(max) - } - s.style = CombatStyle.MAGIC - s.estimatedHit = hit - } - if (spell === entity.properties.spell) { - entity.properties.spell = null - if (entity.properties.autocastSpell == null) { - entity.properties.combatPulse.stop() - } - } - var ticks = 2 + floor(entity.location.getDistance(victim!!.location) * 0.5).toInt() - if (spell!!.type === SpellType.BLITZ) { - ticks++ - } - 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) - return ticks - } - - override fun adjustBattleState(entity: Entity, victim: Entity, state: BattleState) { - if (state.targets == null) { - super.adjustBattleState(entity, victim, state) - if (state.spell != null) { - state.spell.fireEffect(entity, victim, state) - } - return - } - for (s in state.targets) { - if (s != null) { - super.adjustBattleState(entity, s.victim, s) - if (s.spell != null) { - s.spell.fireEffect(entity, s.victim, s) - } - } - } - } - - override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { - state!!.spell.visualize(entity, victim) - } - - override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { - if (state!!.targets == null) { - if (state.estimatedHit > -1) { - victim!!.impactHandler.handleImpact(entity, state.estimatedHit, CombatStyle.MAGIC, state) - } - return - } - for (s in state.targets) { - if (s == null || s.estimatedHit < 0) { - continue - } - val hit = s.estimatedHit - s.victim.impactHandler.handleImpact(entity, hit, CombatStyle.MAGIC, s) - } - } - - override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { - if (entity is Player) { - if (TutorialSession.getExtension(entity as Player?).stage == 51) { - TutorialStage.load(entity as Player?, 52, false) - } - if (TutorialSession.getExtension(entity as Player?).stage == 52) { - TutorialStage.load(entity as Player?, 53, false) - } - if (TutorialSession.getExtension(entity as Player?).stage == 54) { - TutorialStage.load(entity as Player?, 55, false) - } - } - if (state!!.targets == null) { - if (state.spell != null) { - state.spell.visualizeImpact(entity, victim, state) - } - return - } - for (s in state.targets) { - if (s != null) { - state.spell.visualizeImpact(entity, s.victim, s) - } - } - } - - 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 - } - } - 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() - } - - override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { - var baseDamage = modifier - if (baseDamage < 2.0 || entity is NPC) { - if (baseDamage > 2.0) { - baseDamage = 1.0 - } - val level = entity!!.skills.getLevel(Skills.MAGIC, true) - val bonus = entity.properties.bonuses[if (entity is Player) 14 else 13] - val cumulativeStr = level.toDouble() - return 1 + ((14 + cumulativeStr + bonus.toDouble() / 8 + cumulativeStr * bonus * 0.016865) * baseDamage).toInt() / 10 - } - var levelMod = 1.0 - val entityMod = entity!!.getLevelMod(entity, victim) - if (entityMod != 0.0) { - levelMod += entityMod - } - return (baseDamage * levelMod).toInt() + 1 - } - - 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) - } - 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() - } - - override fun getSetMultiplier(e: Entity?, skillId: Int): Double { - if(skillId == Skills.MAGIC) { - if(e is Player && e.isWearingVoid(CombatStyle.MAGIC)) { - return 1.3 - } - } - return 1.0 - } - - override fun addExperience(entity: Entity?, victim: Entity?, state: BattleState?) { - if (state?.spell != null && entity is Player) { - state.spell.addExperience(entity, state.totalDamage) - return - } - - super.addExperience(entity, victim, state) - } - - - override fun getArmourSet(e: Entity?): ArmourSet? { - return if (ArmourSet.AHRIM.isUsing(e)) { - ArmourSet.AHRIM - } else super.getArmourSet(e) - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MeleeSwingHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MeleeSwingHandler.kt deleted file mode 100644 index 1284bb11f..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MeleeSwingHandler.kt +++ /dev/null @@ -1,319 +0,0 @@ -package rs09.game.node.entity.combat.handlers - -import api.ContentAPI -import api.EquipmentSlot -import core.game.container.impl.EquipmentContainer -import core.game.content.quest.tutorials.tutorialisland.TutorialSession -import core.game.content.quest.tutorials.tutorialisland.TutorialStage -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.equipment.ArmourSet -import core.game.node.entity.combat.equipment.Weapon -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.skill.slayer.SlayerEquipmentFlags -import core.game.node.entity.state.EntityState -import core.game.world.map.path.Pathfinder -import core.tools.RandomFunction -import rs09.game.node.entity.combat.CombatSwingHandler -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks -import kotlin.math.ceil -import kotlin.math.floor - -/** - * Handles a melee combat swing. - * @author Emperor - * @author Ceikry, Kotlin conversion + cleanup - */ -open class MeleeSwingHandler -/** - * 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 - } - - override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { - var hit = 0 - state!!.style = CombatStyle.MELEE - if (entity is Player) { - state.weapon = Weapon(entity.equipment[3]) - } - if (entity!!.properties.armourSet === ArmourSet.VERAC && RandomFunction.random(100) < 21) { - state.armourEffect = ArmourSet.VERAC - } - if (state.armourEffect === ArmourSet.VERAC || isAccurateImpact(entity, victim, CombatStyle.MELEE)) { - val max = calculateHit(entity, victim, 1.0) - state.maximumHit = max - hit = RandomFunction.random(max + 1) - } - state.estimatedHit = hit - if(victim != null) { - 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) - } - return 1 - } - - override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { - entity.animate(entity.properties.attackAnimation) - } - - override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { - var targets = state!!.targets - if (targets == null) { - targets = arrayOf(state) - } - for (s in targets) { - var hit = s!!.estimatedHit - if (hit > -1) { - victim!!.impactHandler.handleImpact(entity, hit, CombatStyle.MELEE, s) - } - hit = s.secondaryHit - if (hit > -1) { - victim!!.impactHandler.handleImpact(entity, hit, CombatStyle.MELEE, s) - } - } - } - - override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { - if (entity is Player) { - if (TutorialSession.getExtension(entity as Player?).stage == 51) { - TutorialStage.load(entity as Player?, 52, false) - } - if (TutorialSession.getExtension(entity as Player?).stage == 54) { - TutorialStage.load(entity as Player?, 55, false) - } - } - victim!!.animate(victim.properties.defenceAnimation) - } - - override fun adjustBattleState(entity: Entity, victim: Entity, state: BattleState) { - if (entity is Player) { - if (state.estimatedHit > 0) { - val name = entity.equipment.getNew(3).name - var damage = -1 - if (name.contains("(p++") || name.contains("(s)") || name.contains("(kp)")) { - damage = 68 - } else if (name.contains("(p+)")) { - damage = 58 - } else if (name.contains("(p)")) { - damage = 48 - } - if (damage > -1 && RandomFunction.random(10) < 4) { - victim.stateManager.register(EntityState.POISONED, false, damage, entity) - } - } - } - super.adjustBattleState(entity, victim, state) - } - - 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) ContentAPI.getItemFromEquipment(entity, EquipmentSlot.AMULET)?.name ?: "null" else "null").toLowerCase() - val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none" - - if(entity is Player && entity.slayer.task?.ids?.contains((entity.properties.combatPulse?.getVictim()?.id ?: 0)) == true) - effectiveAttackLevel *= SlayerEquipmentFlags.getDamAccBonus(entity) //Slayer Helm/ Black Mask/ Slayer cape - - else if (entity is Player //Salve amulet - && (amuletName.contains("salve")) - && checkUndead(victimName) - ) { - effectiveAttackLevel *= 1.2 - } - - return floor(effectiveAttackLevel).toInt() - } - - 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 - } - - //Strength skillcape perk - if(entity is Player && SkillcapePerks.isActive(SkillcapePerks.FINE_ATTUNEMENT, entity) && ContentAPI.getItemFromEquipment(entity, EquipmentSlot.WEAPON)?.definition?.getRequirement(Skills.STRENGTH) != 0) - bonus = ceil(bonus * 1.20).toInt() - - cumulativeStr *= getSetMultiplier(entity, Skills.STRENGTH) - - if(entity is Player && entity.slayer.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)).toInt() - } - - override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { - //authentic formula, taken from OSRS wiki: https://oldschool.runescape.wiki/w/Damage_per_second/Melee#Step_five:_Calculate_the_Defence_roll - victim ?: return 0 - attacker ?: return 0 - - 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 - effectiveDefenceLevel += 8 - effectiveDefenceLevel = floor(effectiveDefenceLevel) - return floor(effectiveDefenceLevel * (victim.properties.bonuses[attacker.properties.attackStyle.bonusType + 5] + 64)).toInt() - } - is NPC -> { - val defLevel = victim.skills.getLevel(Skills.DEFENCE) - val styleDefenceBonus = victim.properties.bonuses[attacker.properties.attackStyle.bonusType + 5] + 64 - return defLevel * styleDefenceBonus - } - } - - return 0 - } - - override fun getSetMultiplier(e: Entity?, skillId: Int): Double { - if (e!!.properties.armourSet === ArmourSet.DHAROK && skillId == Skills.STRENGTH) { -// System.out.println("Fiist number -> " + 1.0 + ((e.getSkills().getMaximumLifepoints() - e.getSkills().getLifepoints()) * 0.01)); - return 1.0 + (e!!.skills.maximumLifepoints - e.skills.lifepoints) * 0.01 - } - if(e is Player && e.isWearingVoid(CombatStyle.MELEE) && (skillId == Skills.ATTACK || skillId == Skills.STRENGTH)) { - return 1.1 - } - return 1.0 - } - - override fun getArmourSet(e: Entity?): ArmourSet? { - if (ArmourSet.DHAROK.isUsing(e)) { - return ArmourSet.DHAROK - } - if (ArmourSet.GUTHAN.isUsing(e)) { - return ArmourSet.GUTHAN - } - if (ArmourSet.VERAC.isUsing(e)) { - return ArmourSet.VERAC - } - return if (ArmourSet.TORAG.isUsing(e)) { - ArmourSet.TORAG - } else super.getArmourSet(e) - } - - /** - * Check to see whether an NPC is classified as undead. - * @param name - * @return true if so - */ - private fun checkUndead(name: String): Boolean { - return (name == "Zombie" || name.contains("rmoured") || name == "Ankou" || name == "Crawling Hand" || name == "Banshee" || name == "Ghost" || name == "Ghast" || name == "Mummy" || name.contains("Revenant") - || name == "Skeleton" || name == "Zogre" || name == "Spiritual Mage") - } - - companion object { - /** - * Checks if the entity is using a halberd. - * @param entity The entity. - * @return `True` if so. - */ - private fun usingHalberd(entity: Entity): Boolean { - if (entity is Player) { - val weapon = entity.equipment[EquipmentContainer.SLOT_WEAPON] - if (weapon != null) { - return weapon.id in 3190..3204 || weapon.id == 6599 - } - } else if (entity is NPC) { - return entity.id == 8612 - } - return false - } - - /** - * Checks if the entity can execute a melee swing from its current location. - * @param entity The attacking entity. - * @param victim The victim. - * @return `True` if so. - */ - fun canMelee(entity: Entity, victim: Entity?, distance: Int): Boolean { - val e = entity.location - if (victim == null) { - return false - } - if (entity.id == 7135 && entity.location.withinDistance(victim.location, 2)) { - return true - } - val x = victim.location.x - val y = victim.location.y - val size = entity.size() - if (distance == 1) { - for (i in 0 until size) { - if (Pathfinder.isStandingIn(e.x - 1, e.y + i, 1, 1, x, y, victim.size(), victim.size())) { - return true - } - if (Pathfinder.isStandingIn(e.x + size, e.y + i, 1, 1, x, y, victim.size(), victim.size())) { - return true - } - if (Pathfinder.isStandingIn(e.x + i, e.y - 1, 1, 1, x, y, victim.size(), victim.size())) { - return true - } - if (Pathfinder.isStandingIn(e.x + i, e.y + size, 1, 1, x, y, victim.size(), victim.size())) { - return true - } - } - if (e == victim.location) { - return true - } - return victim.getSwingHandler(false).type == CombatStyle.MELEE && e.withinDistance(victim.location, 1) && victim.properties.combatPulse.getVictim() === entity && entity.index < victim.index - } - return entity.centerLocation.withinDistance(victim.centerLocation, distance + (size shr 1) + (victim.size() shr 1)) - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/RangeSwingHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/RangeSwingHandler.kt deleted file mode 100644 index f2cf7fe41..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/RangeSwingHandler.kt +++ /dev/null @@ -1,391 +0,0 @@ -package rs09.game.node.entity.combat.handlers - -import api.ContentAPI -import api.EquipmentSlot -import core.game.container.Container -import core.game.container.impl.EquipmentContainer -import core.game.content.quest.tutorials.tutorialisland.TutorialSession -import core.game.content.quest.tutorials.tutorialisland.TutorialStage -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.equipment.* -import core.game.node.entity.combat.equipment.Weapon.WeaponType -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.task.Pulse -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.update.flag.context.Graphics -import core.tools.RandomFunction -import rs09.game.node.entity.combat.CombatSwingHandler -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks -import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld -import java.util.* -import kotlin.math.ceil -import kotlin.math.floor - -/** - * Handles the range combat swings. - * @author Emperor - * @author Ceikry, conversion to Kotlin + cleanup - */ -open class RangeSwingHandler -/** - * 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 - } - if (entity.getAttribute("tut-island", false)) { - if (TutorialSession.getExtension(entity as Player).stage == 70) { - return InteractionType.NO_INTERACT - } - } - var distance = 7 - if (entity is Player && (entity.getExtension(WeaponInterface::class.java) as WeaponInterface).weaponInterface.interfaceId == 91) { - distance -= 2 - } - if (entity.properties.attackStyle.style == WeaponInterface.STYLE_LONG_RANGE) { - distance += 2 - } - var goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) - var type = InteractionType.STILL_INTERACT - if (victim.walkingQueue.isMoving && !goodRange) { - goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) - type = InteractionType.MOVE_INTERACT - } - if (goodRange && super.canSwing(entity, victim) != 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 { - configureRangeData(entity, state) - if (state!!.weapon == null || !hasAmmo(entity, state)) { - entity!!.properties.combatPulse.stop() - 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("Damage: $it") } - 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)) - } - state.secondaryHit = hit - } - if (entity == null || victim!!.location == null) { - return -1 - } - 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() - } - - /** - * Configures the range data. - * @param entity The entity. - * @param state The battle state. - */ - fun configureRangeData(entity: Entity?, state: BattleState?) { - state!!.style = CombatStyle.RANGE - val w: Weapon? - if (entity is Player) { - val rw = RangeWeapon.get(entity.equipment.getNew(3).id) - if (rw == null) { - SystemLogger.logErr("Unhandled range weapon used - [item id=" + entity.equipment.getNew(3).id + "].") - return - } - w = Weapon(entity.equipment[3], rw.ammunitionSlot, entity.equipment.getNew(rw.ammunitionSlot)) - w.type = rw.weaponType - state.rangeWeapon = rw - state.ammunition = Ammunition.get(w.ammunition.id) - } else { - w = Weapon(null) - } - state.weapon = w - } - - override fun adjustBattleState(entity: Entity, victim: Entity, state: BattleState) { - 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) - } - } - super.adjustBattleState(entity, victim, state) - } - - override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { - var start: Graphics? = null - if (state!!.ammunition != null) { - start = state.ammunition.startGraphics - state.ammunition.projectile.copy(entity, victim, 5.0).send() - if (state.weapon.type == WeaponType.DOUBLE_SHOT && state.ammunition.darkBowGraphics != null) { - start = state.ammunition.darkBowGraphics - val speed = (55 + entity.location.getDistance(victim!!.location) * 10).toInt() - Projectile.create(entity, victim, state.ammunition.projectile.projectileId, 40, 36, 41, speed, 25).send() - } - } else if (entity is NPC) { - if (entity.definition.combatGraphics[0] != null) { - start = entity.definition.combatGraphics[0] - } - val g = entity.definition.combatGraphics[1] - if (g != null) { - Projectile.ranged(entity, victim, g.id, g.height, 36, 41, 5).send() - } - } - val weapon: RangeWeapon? = RangeWeapon.get(state.weapon.id) - val anim = entity.properties.attackAnimation.id - weapon?.let { - if ((anim == 422 || anim == 423)) { - entity.visualize(it.animation, start) - return - } - } - entity.visualize(entity.properties.attackAnimation, start) - } - - override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { - if (state!!.ammunition != null && state.ammunition.effect != null && state.ammunition.effect.canFire(state)) { - state.ammunition.effect.impact(state) - } - val hit = state.estimatedHit - if (entity is Player) { - if (TutorialSession.getExtension(entity as Player?).stage == 51) { - TutorialStage.load(entity as Player?, 52, false) - } - if (TutorialSession.getExtension(entity as Player?).stage == 52) { - TutorialStage.load(entity as Player?, 53, false) - } - } - victim!!.impactHandler.handleImpact(entity, hit, CombatStyle.RANGE, state) - if (state.secondaryHit > -1) { - val hitt = state.secondaryHit - GameWorld.Pulser.submit(object : Pulse(1, victim) { - override fun pulse(): Boolean { - victim.impactHandler.handleImpact(entity, hitt, CombatStyle.RANGE, state) - return true - } - }) - } - } - - override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { - victim!!.animate(victim.properties.defenceAnimation) - } - - 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) - - return floor(effectiveRangedLevel).toInt() - } - - 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) - } - 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)).toInt() - //return ((14 + cumulativeStr + bonus / 8 + cumulativeStr * bonus * 0.016865) * modifier).toInt() / 10 + 1 - } - - 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 - } - - 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.0 - } - - override fun getArmourSet(e: Entity?): ArmourSet? { - return if (ArmourSet.KARIL.isUsing(e)) { - ArmourSet.KARIL - } else super.getArmourSet(e) - } - - companion object { - /** - * Checks if the entity has the ammunition needed to proceed. - * @param e The entity. - * @param state The battle state. - * @return `True` if so. - */ - fun hasAmmo(e: Entity?, state: BattleState?): Boolean { - if (e !is Player) { - return true - } - val type = state!!.weapon.type - val amount = if (type == WeaponType.DOUBLE_SHOT) 2 else 1 - if (type == WeaponType.DEGRADING) { - return true - } - val item = e.equipment[state.weapon.ammunitionSlot] - if (item != null && item.amount >= amount) { - if (!state.rangeWeapon.ammunition.contains(item.id)) { - e.packetDispatch.sendMessage("You can't use this type of ammunition with this bow.") - return false - } - return true - } - if (type == WeaponType.DOUBLE_SHOT) { - state.weapon.type = WeaponType.DEFAULT - return hasAmmo(e, state) - } - e.packetDispatch.sendMessage("You do not have enough ammo left.") - return false - } - - /** - * Uses the ammunition for the range weapon. - * @param e The entity. - * @param state The battle state. - * @param location The drop location. - */ - fun useAmmo(e: Entity, state: BattleState, location: Location?) { - var dropLocation = location - if (e !is Player) { - return - } - 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 - if (state.weapon.ammunitionSlot == -1 || ammo == null) { - return - } - val dropRate = getDropRate(e) - if (dropRate == -1.0) { - return - } - e.equipment.replace(Item(ammo.id, ammo.amount - amount, ammo.charge), state.weapon.ammunitionSlot) - if (dropLocation == null) { - return - } - val flag = RegionManager.getClippingFlag(dropLocation) - if (flag and 0x200000 != 0) { //Water - dropLocation = null - } - if (dropLocation != null && state.rangeWeapon.isDropAmmo) { - val rate = 5 * (1.0 + e.skills.getLevel(Skills.RANGE) * 0.01) * dropRate - if (RandomFunction.randomize(rate.toInt()) != 0) { - val drop = GroundItemManager.increase(Item(ammo.id, amount), dropLocation, e) - if (drop != null) { - if (e.getAttribute("duel:ammo", null) != null) { - (e.getAttribute("duel:ammo") as ArrayList).add(drop) - } - } - } - } - if (e.equipment[state.rangeWeapon.ammunitionSlot] == null) { - e.packetDispatch.sendMessage("You have no ammo left in your quiver!") - } - } - - /** - * Checks if ammunition should be saved. - * @param e The entity. - * @return `True` if so. - */ - private fun getDropRate(e: Entity?): Double { - if (e is Player) { - val cape = e.equipment[EquipmentContainer.SLOT_CAPE] - val weapon = e.equipment[EquipmentContainer.SLOT_WEAPON] - if (cape != null && (cape.id == 10498 || cape.id == 10499) && weapon != null && weapon.id != 10034 && weapon.id != 10033) { - val rate = 80 - if (RandomFunction.random(100) < rate) { - val torso = e.equipment[EquipmentContainer.SLOT_CHEST] - val modelId = torso?.definition?.maleWornModelId1 ?: -1 - if (modelId == 301 || modelId == 306 || modelId == 3379) { - e.packetDispatch.sendMessage("Your armour interferes with Ava's device.") - return 1.0 - } - return (-1).toDouble() - } - return 0.33 - } - } - return 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/kotlin/rs09/game/node/entity/combat/handlers/SalamanderSwingHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/SalamanderSwingHandler.kt deleted file mode 100644 index 6c9a676f3..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/SalamanderSwingHandler.kt +++ /dev/null @@ -1,80 +0,0 @@ -package rs09.game.node.entity.combat.handlers - -import core.game.node.entity.Entity -import core.game.node.entity.combat.BattleState -import core.game.node.entity.combat.CombatStyle -import rs09.game.node.entity.combat.CombatSwingHandler -import core.game.node.entity.combat.InteractionType -import core.game.node.entity.skill.Skills -import core.game.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics - -/** - * Handles a combat swing using a salamander. - * @author Vexia - */ -class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandler(style) { - override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { - val index = entity!!.properties.attackStyle.style - style = when(index) { - 7 -> CombatStyle.MAGIC - 4 -> CombatStyle.RANGE - else -> CombatStyle.MELEE - } - return style.swingHandler.swing(entity, victim, state) - } - - override fun impact(entity: Entity?, victim: Entity?, state: BattleState?) { - style.swingHandler.impact(entity, victim, state) - } - - override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { - style.swingHandler.visualize(entity, victim, state) - entity.visualize(Animation.create(5247), Graphics(952, 100)) - } - - override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { - style.swingHandler.visualizeImpact(entity, victim, state) - } - - override fun calculateAccuracy(entity: Entity?): Int { - return style.swingHandler.calculateAccuracy(entity) - } - - override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { - return style.swingHandler.calculateHit(entity, victim, modifier) - } - - override fun addExperience(entity: Entity?, victim: Entity?, state: BattleState?) { - entity!!.skills.addExperience(Skills.HITPOINTS, state!!.estimatedHit * 1.33, true) - if (state.style == CombatStyle.MAGIC) { - entity.skills.addExperience(Skills.MAGIC, state.estimatedHit * 2.toDouble(), true) - } - if (state.style == CombatStyle.RANGE) { - entity.skills.addExperience(Skills.RANGE, state.estimatedHit * 4.toDouble(), true) - } - if (state.style == CombatStyle.MELEE) { - entity.skills.addExperience(Skills.STRENGTH, state.estimatedHit * 4.toDouble(), true) - } - } - - override fun calculateDefence(victim: Entity?, attacker: Entity?): Int { - return style.swingHandler.calculateDefence(victim, attacker) - } - - override fun getSetMultiplier(e: Entity?, skillId: Int): Double { - return style.swingHandler.getSetMultiplier(e, skillId) - } - - override fun canSwing(entity: Entity, victim: Entity): InteractionType? { - return style.swingHandler.canSwing(entity, victim) - } - - companion object { - /** - * The instance for the swing handler. - */ - val INSTANCE = SalamanderSwingHandler(CombatStyle.MELEE) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/equipment/BarrowsEquipmentRegister.kt b/Server/src/main/kotlin/rs09/game/node/entity/equipment/BarrowsEquipmentRegister.kt deleted file mode 100644 index b4f235e76..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/equipment/BarrowsEquipmentRegister.kt +++ /dev/null @@ -1,71 +0,0 @@ -package rs09.game.node.entity.equipment - -import rs09.game.node.entity.combat.equipment.EquipmentDegrader -import core.plugin.Initializable -import core.plugin.Plugin - -@Initializable -class BarrowsEquipmentRegister : Plugin{ - 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) - - override fun newInstance(arg: Any?): Plugin { - EquipmentDegrader.registerSet(AHRIM_HOOD) - EquipmentDegrader.registerSet(AHRIM_STAFF) - EquipmentDegrader.registerSet(AHRIM_TOP) - EquipmentDegrader.registerSet(AHRIM_SKIRT) - EquipmentDegrader.registerSet(KARIL_COIF) - EquipmentDegrader.registerSet(KARIL_CBOW) - EquipmentDegrader.registerSet(KARIL_TOP) - EquipmentDegrader.registerSet(KARIL_SKIRT) - EquipmentDegrader.registerSet(DHAROK_HELM) - EquipmentDegrader.registerSet(DHAROK_AXE) - EquipmentDegrader.registerSet(DHAROK_BODY) - EquipmentDegrader.registerSet(DHAROK_LEGS) - EquipmentDegrader.registerSet(GUTHAN_HELM) - EquipmentDegrader.registerSet(GUTHAN_SPEAR) - EquipmentDegrader.registerSet(GUTHAN_BODY) - EquipmentDegrader.registerSet(GUTHAN_SKIRT) - EquipmentDegrader.registerSet(TORAG_HELM) - EquipmentDegrader.registerSet(TORAG_HAMMER) - EquipmentDegrader.registerSet(TORAG_BODY) - EquipmentDegrader.registerSet(TORAG_LEGS) - EquipmentDegrader.registerSet(VERAC_HELM) - EquipmentDegrader.registerSet(VERAC_FLAIL) - EquipmentDegrader.registerSet(VERAC_BRASS) - EquipmentDegrader.registerSet(VERAC_SKIRT) - 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/kotlin/rs09/game/node/entity/equipment/PVPEquipmentRegister.kt b/Server/src/main/kotlin/rs09/game/node/entity/equipment/PVPEquipmentRegister.kt deleted file mode 100644 index 12a188f2d..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/equipment/PVPEquipmentRegister.kt +++ /dev/null @@ -1,52 +0,0 @@ -package rs09.game.node.entity.equipment - -import rs09.game.node.entity.combat.equipment.EquipmentDegrader -import core.plugin.Initializable -import core.plugin.Plugin - -@Initializable -class PVPEquipmentRegister : Plugin { - val VESTA_BODY = arrayOf(13887,13889) - val VESTA_SKIRT = arrayOf(13893,13895) - val VESTA_SWORD = arrayOf(13899,13901) - val VESTA_SPEAR = arrayOf(13905,13907) - - val STATIUS_BODY = arrayOf(13884,13886) - val STATIUS_LEGS = arrayOf(13890,13892) - val STATIUS_HELM = arrayOf(13896,13898) - val STATIUS_HAMMER = arrayOf(13902,13904) - - val ZURIEL_TOP = arrayOf(13858,13860) - val ZUREL_BOTTOM = arrayOf(13861,13863) - val ZURIEL_HOOD = arrayOf(13864,13866) - val ZURIEL_STAFF = arrayOf(13867, 13869) - - val MORRIGAN_BODY = arrayOf(13870,13872) - val MORRIGAN_CHAP = arrayOf(13873,13875) - val MORRIGAN_COIF = arrayOf(13876,13878) - - - override fun newInstance(arg: Any?): Plugin { - EquipmentDegrader.registerSet(VESTA_BODY) - EquipmentDegrader.registerSet(VESTA_SKIRT) - EquipmentDegrader.registerSet(VESTA_SWORD) - EquipmentDegrader.registerSet(VESTA_SPEAR) - EquipmentDegrader.registerSet(STATIUS_BODY) - EquipmentDegrader.registerSet(STATIUS_HAMMER) - EquipmentDegrader.registerSet(STATIUS_HELM) - EquipmentDegrader.registerSet(STATIUS_LEGS) - EquipmentDegrader.registerSet(ZUREL_BOTTOM) - EquipmentDegrader.registerSet(ZURIEL_HOOD) - EquipmentDegrader.registerSet(ZURIEL_STAFF) - EquipmentDegrader.registerSet(ZURIEL_TOP) - EquipmentDegrader.registerSet(MORRIGAN_BODY) - EquipmentDegrader.registerSet(MORRIGAN_CHAP) - EquipmentDegrader.registerSet(MORRIGAN_COIF) - 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/kotlin/rs09/game/node/entity/equipment/SnowballEquipmentPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/equipment/SnowballEquipmentPlugin.kt deleted file mode 100644 index 05020d4a5..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/equipment/SnowballEquipmentPlugin.kt +++ /dev/null @@ -1,31 +0,0 @@ -package rs09.game.node.entity.equipment - -import core.cache.def.impl.ItemDefinition -import core.game.interaction.Interaction -import core.game.interaction.Option -import rs09.game.interaction.player.PeltOptionPlugin -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.plugin.Initializable -import core.plugin.Plugin - -@Initializable -class SnowballEquipmentPlugin : Plugin { - override fun newInstance(arg: Item?): Plugin { - ItemDefinition.forId(11951).handlers["equipment"] = this - return this - } - - override fun fireEvent(identifier: String?, vararg args: Any?): Any { - val player = args[0] as Player - when(identifier){ - "equip" -> { - player.getInteraction().set(Option("Pelt", 0).setHandler(PeltOptionPlugin())) - } - "unequip" -> { - Interaction.sendOption(player, 0, "null") - } - } - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheFarmerDialogue.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheFarmerDialogue.kt deleted file mode 100644 index 32ed26420..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheFarmerDialogue.kt +++ /dev/null @@ -1,73 +0,0 @@ -package rs09.game.node.entity.npc.city.alkharid - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable - -/** - * @author qmqz - */ - -@Initializable -class AliTheFarmerDialogue(player: Player? = null) : DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(FacialExpression.FRIENDLY,"Hello there!") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> npc(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(FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } - 2 -> player(FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } - 3 -> player(FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } - 4 -> player(FacialExpression.ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } - - } - - 10 -> npc(FacialExpression.FRIENDLY,"There's not much farming land around here.", "Only that little patch outside.").also { stage++ } - 11 -> player(FacialExpression.ASKING,"Can you give me any advice on farming here in","the desert?").also { stage++ } - 12 -> npc(FacialExpression.FRIENDLY,"Like I said, I only know about cactuses...").also { stage++ } - 13 -> player(FacialExpression.FRIENDLY,"Just tell me about cactuses, then.").also { stage++ } - 14 -> npc(FacialExpression.FRIENDLY,"First you have to weed the patch using a rake.").also { stage++ } - 15 -> player(FacialExpression.ASKING,"Can you give me any other advice?").also { stage++ } - 16 -> npc(FacialExpression.FRIENDLY,"Not really. I've not done much farming recently.").also { stage++ } - 17 -> player(FacialExpression.ASKING,"Well, can you at least sell me any gardening", "tools or seeds?").also { stage++ } - 18 -> npc(FacialExpression.GUILTY,"Sorry. They haven't been delivered yet.").also { stage = 1 } - - 20 -> npc( - 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( - 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(FacialExpression.FRIENDLY,"I only know how to farm cactuses, so this spot", "is perfect.").also { stage++ } - 32 -> player(FacialExpression.THINKING,"Maybe I should talk to him...").also { stage++ } - 33 -> npc(FacialExpression.FRIENDLY,"Of course.", "He's always happy to talk to possible business", "partners.").also { stage = 1 } - - 40 -> npc(FacialExpression.FRIENDLY,"Now why would you think that?").also { stage++ } - 41 -> player(FacialExpression.FRIENDLY,"One of the shopkeepers told they were threatened", "by a man with a rake...").also { stage++ } - 42 -> npc(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?): DialoguePlugin { - return AliTheFarmerDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(2821) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheGuard.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheGuard.kt deleted file mode 100644 index 844eba919..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheGuard.kt +++ /dev/null @@ -1,70 +0,0 @@ -package rs09.game.node.entity.npc.city.alkharid - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable - -/** - * @author qmqz - */ - -@Initializable -class AliTheGuard(player: Player? = null) : DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(FacialExpression.FRIENDLY,"Hello there!") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> npc(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(FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } - 2 -> player(FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } - 3 -> player(FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } - 4 -> player(FacialExpression.ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } - } - - 10 -> npc(FacialExpression.FRIENDLY,"There's a lot of space here. More open space than back home.").also { stage++ } - 11 -> player(FacialExpression.ASKING,"So where is back home?").also { stage++ } - 12 -> npc(FacialExpression.FRIENDLY,"Pollnivneach. It's a town to the south of the", "Shantay Pass.").also { stage = 1 } - - 20 -> npc( - 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(FacialExpression.ASKING,"Why are you only guarding those buildings?").also { stage++ } - 22 -> npc(FacialExpression.SUSPICIOUS,"That's all I've been hired to guard.").also { stage = 1 } - - 30 -> npc(FacialExpression.FRIENDLY,"Yeah, he hired me.", "He owns this house and these two tents, too.").also { stage++ } - 31 -> player(FacialExpression.HALF_ASKING,"Is the work good?").also { stage++ } - 32 -> npc(FacialExpression.FRIENDLY,"It pays better than back home.").also { stage++ } - 33 -> player(FacialExpression.ASKING,"Why, what did you do back home?").also { stage++ } - 34 -> npc(FacialExpression.SUSPICIOUS,"Never you mind.").also { stage++ } - 35 -> npc(FacialExpression.FRIENDLY,"But Ali Morrisane pays us well, at least.").also { stage++ } - 36 -> player(FacialExpression.ASKING,"Maybe I should talk to him...").also { stage++ } - 37 -> npc(FacialExpression.FRIENDLY,"Why not?", "He always likes to meet potential business partners.").also { stage = 1 } - - 40 -> npc(FacialExpression.ANNOYED,"So? They talk too much.").also { stage++ } - 41 -> player(FacialExpression.HALF_ASKING,"You're not going to deny it?").also { stage++ } - 42 -> npc(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?): DialoguePlugin { - return AliTheGuard(player) - } - - override fun getIds(): IntArray { - return intArrayOf(2823) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheSmithDialogue.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheSmithDialogue.kt deleted file mode 100644 index ee0c5717e..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/city/alkharid/AliTheSmithDialogue.kt +++ /dev/null @@ -1,68 +0,0 @@ -package rs09.game.node.entity.npc.city.alkharid - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.plugin.Initializable - -/** - * @author qmqz - */ - -@Initializable -class AliTheSmithDialogue(player: Player? = null) : DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(FacialExpression.FRIENDLY,"Hello there!") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> npc(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(FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } - 2 -> player(FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } - 3 -> player(FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } - 4 -> player(FacialExpression.HALF_ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } - } - - 10 ->npc(FacialExpression.FRIENDLY,"Well, it's hot and full of sand.").also { stage++ } - 11 ->player(FacialExpression.ASKING,"Anything else?").also { stage++ } - 12 ->npc(FacialExpression.FRIENDLY,"Don't ask me, I'm not from around here.").also { stage++ } - 13 ->player(FacialExpression.HALF_ASKING,"So where are you from?").also { stage++ } - 14 ->npc(FacialExpression.HALF_GUILTY,"A town to the south of the Shantay Pass,","called Pollnivneach.").also { stage = 1 } - - 20 ->npc(FacialExpression.SAD,"Not very much, at the moment.", "I came from further south to set up a smithy here...").also { stage++ } - 21 ->npc(FacialExpression.SAD,"...but we still haven't set up the shops,", "so we have no customers yet.").also { stage = 1 } - - 30 -> npc(FacialExpression.FRIENDLY,"Yes, he's the one who persuaded us to come here.").also { stage++ } - 31 -> npc( - 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(FacialExpression.ASKING,"Maybe I should talk to him...").also { stage++ } - 33 -> npc(FacialExpression.FRIENDLY,"Of course.", "He's always happy to talk to possible business partners.").also { stage = 1 } - - 40 -> npc(FacialExpression.SUSPICIOUS,"What? Who's spreading that rumour?").also { stage++ } - 41 -> player(FacialExpression.THINKING,"Well, one of the shopkeepers told me a man with", "a large hammer came to threaten them.").also { stage++ } - 42 -> npc(FacialExpression.SUSPICIOUS,"Don't pay any attention to those people.").also { stage++ } - 43 -> npc(FacialExpression.LAUGH,"They're just worried that they'll lose money", "when we open our shops.").also { stage = 99 } - - 99 -> end() - } - return true - } - - override fun newInstance(player: Player?): DialoguePlugin { - return AliTheSmithDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(2820) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/AlKharidWarriorPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/other/AlKharidWarriorPlugin.kt deleted file mode 100644 index c896693a2..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/AlKharidWarriorPlugin.kt +++ /dev/null @@ -1,84 +0,0 @@ -package rs09.game.node.entity.npc.other - -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.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 rs09.plugin.PluginManager - -/** - * Handles the Al-Kharid Warrior - * @author Ceikry - */ -@Initializable -class AlKharidWarriorNPC : AbstractNPC { - var target: Player? = null - - //Constructor spaghetti because Arios I guess - constructor() : super(18, null, true) {} - private constructor(id: Int, location: Location) : super(id, location) {} - override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { - return AlKharidWarriorNPC(id, location) - } - - override fun newInstance(arg: Any?): Plugin { - PluginManager.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){ - attack(target) - } - super.tick() - } - - //Clear target on death - override fun finalizeDeath(killer: Entity?) { - super.finalizeDeath(killer) - target = null - } - - 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) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/BlastFurnaceOre.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/other/BlastFurnaceOre.kt deleted file mode 100644 index 31de1e91b..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/BlastFurnaceOre.kt +++ /dev/null @@ -1,76 +0,0 @@ -package rs09.game.node.entity.npc.other - -import api.ContentAPI -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 org.rs09.consts.NPCs - -@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) : super( - when(variant){ - 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.PERFECT_GOLD -> NPCs.PERFECT_GOLD_ORE_2563 - 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 = 2 - - override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { - return BlastFurnaceOre(id, location) - } - - override fun getIds(): IntArray { - return intArrayOf( - 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() { - delay-- - if(delay <= 0 && ContentAPI.getWorldTicks() % 2 == 0) { //run every other tick - if(counter > 0){ - properties.teleportLocation = location.transform(0, -1, 0) - counter-- - } else { - //increment ore count, update interface, whatever you need to do here. - clear() - } - } - } - -} - -enum class BFOreVariant { - COPPER, - TIN, - COAL, - MITHRIL, - ADAMANT, - SILVER, - GOLD, - PERFECT_GOLD, - RUNITE -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/FremennikGuards.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/other/FremennikGuards.kt deleted file mode 100644 index f57617276..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/FremennikGuards.kt +++ /dev/null @@ -1,32 +0,0 @@ -package rs09.game.node.entity.npc.other - -import api.ContentAPI -import core.game.node.entity.npc.AbstractNPC -import core.game.world.map.Location -import core.plugin.Initializable -import org.rs09.consts.NPCs - -@Initializable -class FremennikGuards : AbstractNPC { - //Constructor spaghetti because Arios I guess - constructor() : super(NPCs.GUARD_5489, null, true) {} - private constructor(id: Int, location: Location) : super(id, location) {} - - override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { - return FremennikGuards(id, location) - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.GUARD_5489, NPCs.GUARD_5490) - } - - override fun tick() { - if(this.isRespawn) { - when (id) { - NPCs.GUARD_5489 -> if (ContentAPI.getWorldTicks() % 5 == 0) ContentAPI.sendChat(this, "JATIZSO!") - NPCs.GUARD_5490 -> if (ContentAPI.getWorldTicks() % 8 == 0) ContentAPI.sendChat(this, "NEITIZNOT!") - } - } - super.tick() - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/MortMyreGhastNPC.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/other/MortMyreGhastNPC.kt deleted file mode 100644 index 5aa192d43..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/MortMyreGhastNPC.kt +++ /dev/null @@ -1,104 +0,0 @@ -package rs09.game.node.entity.npc.other - -import api.Container -import api.ContentAPI -import core.game.content.consumable.Consumables -import core.game.content.consumable.Food -import core.game.interaction.MovementPulse -import core.game.node.entity.Entity -import core.game.node.entity.combat.ImpactHandler -import core.game.node.entity.npc.AbstractNPC -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.update.flag.context.Animation -import core.plugin.Initializable -import core.tools.RandomFunction -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import rs09.game.content.quest.members.naturespirit.NSUtils - -@Initializable -class MortMyreGhastNPC : AbstractNPC { - //Constructor spaghetti because Arios I guess - constructor() : super(NPCs.GHAST_1052, null, true) {} - private constructor(id: Int, location: Location) : super(id, location) {} - - override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { - isAggressive = id != ids[0] - return MortMyreGhastNPC(id, location) - } - - override fun handleTickActions() { - super.handleTickActions() - if(id == ids[0] && RandomFunction.roll(35)){ - val players = RegionManager.getLocalPlayers(this, 5).filter { !it.inCombat() } - if(players.isNotEmpty()){ - val player = players.random() - ContentAPI.submitIndividualPulse(this, object : MovementPulse(this, player){ - override fun pulse(): Boolean { - animate(Animation(1093)) - attemptLifeSiphon(player) - return true - } - }) - } - } else { - val ticksTransformed = ContentAPI.getWorldTicks() - ContentAPI.getAttribute(this, "woke", 0) - if(!inCombat() && ticksTransformed > 10){ - reTransform() - } - } - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.GHAST_1052, NPCs.GHAST_1053) - } - - fun attemptLifeSiphon(player: Player){ - var hasFood = false - - if(NSUtils.activatePouch(player, this)) return - - GlobalScope.launch { - for(i in player.inventory.toArray()){ - if(i == null) continue - val consumable = Consumables.getConsumableById(i.id) - if(consumable != null && consumable is Food) { - hasFood = true - ContentAPI.removeItem(player, i, Container.INVENTORY) - ContentAPI.addItem(player, Items.ROTTEN_FOOD_2959) - ContentAPI.sendMessage(player, "You feel something attacking your backpack, and smell a terrible stench.") - break - } - } - - if(!hasFood && RandomFunction.roll(3)) { - ContentAPI.sendMessage(player, "An attacking Ghast just misses you.") - } else if(!hasFood){ - ContentAPI.impact(player, RandomFunction.random(3,6), ImpactHandler.HitsplatType.NORMAL) - ContentAPI.sendMessage(player, "A supernatural force draws energy from you.") - } - } - } - - override fun commenceDeath(killer: Entity?) { - super.commenceDeath(killer) - } - - override fun finalizeDeath(killer: Entity?) { - super.finalizeDeath(killer) - if(id == ids[1]) { - reTransform() - if(killer is Player){ - NSUtils.incrementGhastKC(killer) - ContentAPI.rewardXP(killer, Skills.PRAYER, 30.0) - removeAttribute("woke") - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/LoginParser.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/LoginParser.kt deleted file mode 100644 index 1bd578c62..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/LoginParser.kt +++ /dev/null @@ -1,241 +0,0 @@ -package rs09.game.node.entity.player.info.login - -import core.game.node.entity.player.Player -import core.game.node.entity.player.info.PlayerDetails -import core.game.node.entity.player.info.login.LoginConfiguration -import core.game.node.entity.player.info.login.LoginType -import core.game.node.entity.player.info.login.PlayerParser -import core.game.node.entity.player.info.login.Response -import core.game.system.SystemManager -import core.game.system.monitor.PlayerMonitor -import core.game.system.task.Pulse -import core.net.amsc.MSPacketRepository -import core.net.amsc.ManagementServerState -import core.net.amsc.WorldCommunicator -import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld -import rs09.game.world.repository.Repository -import java.util.concurrent.TimeUnit -import java.util.concurrent.locks.Lock -import java.util.concurrent.locks.ReentrantLock - -/** - * Parses the login of a player. - * @author Emperor - * @author Vexia - */ -class LoginParser( - /** - * The player details file. - */ - val details: PlayerDetails, - /** - * The login type. - */ - private val type: LoginType -) : Runnable { - /** - * Gets the player details. - * @return The player details. - */ - - /** - * The player in the game, used for reconnect login type. - */ - private var gamePlayer: Player? = null - - /** - * Gets the timeStamp. - * @return the timeStamp - */ - /** - * The time stamp. - */ - val timeStamp: Int - override fun run() { - try { - LOCK.tryLock(1000L, TimeUnit.MILLISECONDS) - } catch (e: Exception) { - println(e) - LOCK.unlock() - return - } - try { - if (validateRequest()) { - handleLogin() - } - } catch (t: Throwable) { - t.printStackTrace() - try { - flag(Response.ERROR_LOADING_PROFILE) - Repository.LOGGED_IN_PLAYERS.remove(details.username) - } catch (e: Throwable) { - e.printStackTrace() - } - } - LOCK.unlock() - } - - /** - * Handles the actual login. - */ - private fun handleLogin() { - val p = worldInstance - if (!details.parse()) { - flag(Response.INVALID_LOGIN_SERVER) - return - } - val player = p ?: Player(details) - player.setAttribute("login_type", type) - if (p != null) { // Reconnecting - p.updateDetails(details) - reconnect(p, type) - return - } - initialize(player, false) - } - - /** - * Initializes the player. - * @param player The player. - * @param reconnect If the player data should be parsed. - */ - fun initialize(player: Player, reconnect: Boolean) { - if (reconnect) { - reconnect(player, type) - return - } - if(!PlayerParser.parse(player)){ - Repository.players.remove(player) - Repository.LOGGED_IN_PLAYERS.remove(player.username) - Repository.lobbyPlayers.remove(player) - Repository.playerNames.remove(player.name) - MSPacketRepository.sendPlayerRemoval(player.name) - flag(Response.ERROR_LOADING_PROFILE) - return - } - //Repository.getPlayerNames().put(player.getName(), player); - GameWorld.Pulser.submit(object : Pulse(1) { - override fun pulse(): Boolean { - try { - if (details.session.isActive) { - val p = Repository.getPlayerByName(player.name) - if (p != null) { - p.clear() - Repository.playerNames.remove(p.name) - Repository.lobbyPlayers.remove(p) - Repository.players.remove(p) - } - if (!Repository.players.contains(player)) { - Repository.players.add(player) - } - player.details.session.setObject(player) - flag(Response.SUCCESSFUL) - player.init() - player.monitor.log(player.details.ipAddress, PlayerMonitor.ADDRESS_LOG) - player.monitor.log(player.details.serial, PlayerMonitor.ADDRESS_LOG) - player.monitor.log(player.details.macAddress, PlayerMonitor.ADDRESS_LOG) - } else { - Repository.playerNames.remove(player.name) - MSPacketRepository.sendPlayerRemoval(player.name) - } - } catch (t: Throwable) { - t.printStackTrace() - Repository.playerNames.remove(player.name) - MSPacketRepository.sendPlayerRemoval(player.name) - } - return true - } - }) - } - - /** - * Gets the player instance in the current world. - * @return The player instance, if found. - */ - private val worldInstance: Player? - private get() { - var player = Repository.disconnectionQueue[details.username] - if (player == null) { - player = gamePlayer - } - return player - } - - /** - * Initializes a reconnecting player. - * @param player The player. - * @param type The login type. - */ - private fun reconnect(player: Player, type: LoginType) { - Repository.disconnectionQueue.remove(details.username) - player.initReconnect() - player.isActive = true - flag(Response.SUCCESSFUL) - player.updateSceneGraph(true) - player.configManager.init() - LoginConfiguration.configureGameWorld(player) - Repository.playerNames[player.name] = player - GameWorld.Pulser.submit(object : Pulse(1) { - override fun pulse(): Boolean { - if (!Repository.players.contains(player)) { - Repository.players.add(player) - } - return true - } - }) - } - - /** - * Checks if the login request is valid. - * @return `True` if the request is valid. - */ - private fun validateRequest(): Boolean { - //This is supposed to prevent the double-logging issue. Will it work? Who knows. - if (Repository.LOGGED_IN_PLAYERS.contains(details.username)) { - SystemLogger.logWarn("LOGGED_IN_PLAYERS contains ${details.username}") - return flag(Response.ALREADY_ONLINE) - } - if (WorldCommunicator.getState() == ManagementServerState.CONNECTING) { - return flag(Response.LOGIN_SERVER_OFFLINE) - } - if (!details.session.isActive) { - return false - } - if (SystemManager.isUpdating()) { - return flag(Response.UPDATING) - } - if (Repository.getPlayerByName(details.username).also { gamePlayer = it } != null && gamePlayer!!.session.isActive) { - return flag(Response.ALREADY_ONLINE) - } - return if (details.isBanned) { - flag(Response.ACCOUNT_DISABLED) - } else true - } - - /** - * Flags a response. - * @param response the [Response]. - * @return `True` if successfully logged in. - */ - fun flag(response: Response): Boolean { - details.session.write(response, true) - return response == Response.SUCCESSFUL - } - - companion object { - /** - * The lock used to disable 2 of the same player being logged in. - */ - private val LOCK: Lock = ReentrantLock() - } - - /** - * Constructs a new `LoginParser` `Object`. - * @param details the player details. - * @param type The login type. - */ - init { - timeStamp = GameWorld.ticks - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaveParser.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaveParser.kt deleted file mode 100644 index be882969a..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaveParser.kt +++ /dev/null @@ -1,394 +0,0 @@ -package rs09.game.node.entity.player.info.login - -import core.game.interaction.item.brawling_gloves.BrawlingGloves -import core.game.node.entity.combat.CombatSpell -import core.game.node.entity.player.Player -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.grave.GraveType -import core.game.node.entity.player.link.music.MusicEntry -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.ServerConstants -import rs09.game.node.entity.skill.farming.CompostBins -import rs09.game.node.entity.skill.farming.FarmingPatch -import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld -import java.io.FileReader -import java.util.* - -/** - * Class used for parsing JSON player saves. - * @author Ceikry - * @param player: The player we are parsing. - */ -class PlayerSaveParser(val player: Player) { - var parser = JSONParser() - var reader: FileReader? = FileReader(ServerConstants.PLAYER_SAVE_PATH + player.name + ".json") - 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 { - reader - ?: SystemLogger.logWarn("Couldn't find save file for ${player.name}, or save is corrupted.").also { read = false } - if (read) { - saveFile = parser.parse(reader) as JSONObject - } - } - - fun parse() = GlobalScope.launch { - if (read) { - launch { - parseCore() - parseSkills() - parseSettings() - parseSlayer() - parseQuests() - parseAppearance() - parseGrave() - parseVarps() - parseStates() - } - launch { - parseSpellbook() - parseGrandExchange() - parseSavedData() - parseAutocastSpell() - parseFarming() - parseConfigs() - parseMonitor() - } - launch { - parseMusic() - parseFamiliars() - parseBarCrawl() - parseAntiMacro() - parseTT() - parseBankPin() - } - launch { - parseHouse() - parseIronman() - parseEmoteManager() - parseStatistics() - parseBrawlingGloves() - parseAttributes() - parseAchievements() - parsePouches() - } - parsePouches() - } - } - - fun parseVarps(){ - if(saveFile!!.containsKey("varps")) - player.varpManager.parse(saveFile!!["varps"] as JSONArray) - } - - fun parsePouches() { - if (saveFile!!.containsKey("pouches")) - player.pouchManager.parse(saveFile!!["pouches"] as JSONArray) - } - - fun parseAttributes() { - if (saveFile!!.containsKey("attributes")) { - val attrData = saveFile!!["attributes"] as JSONArray - for (a in attrData) { - val attr = a as JSONObject - val key = attr["key"].toString() - val type = attr["type"].toString() - val isExpirable = attr.getOrDefault("expirable",false) as Boolean - if(isExpirable){ - val expireTime = attr["expiration-time"].toString().toLong() - if(expireTime < System.currentTimeMillis()) continue - - player.gameAttributes.keyExpirations[key] = expireTime - } - player.gameAttributes.savedAttributes.add(key) - player.gameAttributes.attributes.put(key, when (type) { - "int" -> attr["value"].toString().toInt() - "str" -> attr["value"].toString() - "short" -> attr["value"].toString().toShort() - "long" -> attr["value"].toString().toLong() - "bool" -> attr["value"] as Boolean - "byte" -> Base64.getDecoder().decode(attr["value"].toString())[0] - else -> null.also { SystemLogger.logWarn("Invalid data type for key: $key in PlayerSaveParser.kt Line 115") } - }) - } - } else { - player.gameAttributes.parse(player.name + ".xml") - } - } - - fun parseBrawlingGloves() { - if (saveFile!!.containsKey("brawlingGloves")) { - val bgData: JSONArray = saveFile!!["brawlingGloves"] as JSONArray - for (bg in bgData) { - val glove = bg as JSONObject - player.brawlingGlovesManager.registerGlove(BrawlingGloves.forIndicator(glove.get("gloveId").toString().toInt()).id, glove.get("charges").toString().toInt()) - } - } - } - - fun parseStatistics() { - if (saveFile!!.containsKey("statistics")) { - val stats: JSONArray = saveFile!!["statistics"] as JSONArray - for (stat in stats) { - val s = stat as JSONObject - val index = (s.get("index") as String).toInt() - val value = (s.get("value") as String).toInt() - } - } - } - - fun parseEmoteManager() { - if (saveFile!!.containsKey("emoteData")) { - val emoteData: JSONArray = saveFile!!["emoteData"] as JSONArray - for (emote in emoteData) { - val e = Emotes.values()[(emote as String).toInt()] - if (!player.emoteManager.emotes.contains(e)) { - player.emoteManager.emotes.add(e) - } - } - } - } - - fun parseIronman() { - if (saveFile!!.containsKey("ironManMode")) { - val ironmanMode = (saveFile!!["ironManMode"] as String).toInt() - player.ironmanManager.mode = IronmanMode.values()[ironmanMode] - } - } - - fun parseAchievements() { - if (saveFile!!.containsKey("achievementDiaries")) { - val achvData = saveFile!!["achievementDiaries"] as JSONArray - player.achievementDiaryManager.parse(achvData) - } else { - player.achievementDiaryManager.resetRewards() - } - } - - fun parseHouse() { - val houseData = saveFile!!["houseData"] as JSONObject - player.houseManager.parse(houseData) - } - - fun parseBankPin() { - val bpData = saveFile!!["bankPinManager"] as JSONObject - player.bankPinManager.parse(bpData) - } - - fun parseTT() { - val ttData = saveFile!!["treasureTrails"] as JSONObject - player.treasureTrailManager.parse(ttData) - } - - fun parseAntiMacro() { - } - - fun parseStates() { - player.states.clear() - SystemLogger.logErr("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 parseBarCrawl() { - val barCrawlData = saveFile!!["barCrawl"] as JSONObject - player.barcrawlManager.parse(barCrawlData) - } - - fun parseFamiliars() { - val familiarData = saveFile!!["familiarManager"] as JSONObject - player.familiarManager.parse(familiarData) - } - - fun parseMusic() { - val unlockedSongs = saveFile!!["unlockedMusic"] as JSONArray - for (song in unlockedSongs) { - val s = (song as String).toInt() - val entry = MusicEntry.forId(s) - player.musicPlayer.unlocked.put(entry.index, entry) - } - } - - fun parseMonitor() { - val monitorData = saveFile!!["playerMonitor"] as JSONObject - if (monitorData.containsKey("duplicationFlag")) { - val duplicationFlag: Int = (monitorData.get("duplicationFlag") as String).toInt() - player.monitor.duplicationLog.flag(duplicationFlag) - } - if (monitorData.containsKey("macroFlag")) { - val macroFlag: Int = (monitorData.get("macroFlag") as String).toInt() - player.monitor.macroFlag = macroFlag - } - if (monitorData.containsKey("lastIncreaseFlag")) { - val lastIncreaseFlag: Long = (monitorData.get("lastIncreaseFlag") as String).toLong() - player.monitor.duplicationLog.lastIncreaseFlag = lastIncreaseFlag - } - } - - fun parseConfigs() { - val configs = saveFile!!["configs"] as JSONArray - 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 - } - } - - fun parseFarming() { - /*val farmingData = saveFile!!["farming"] as JSONObject - - if (farmingData.containsKey("equipment")) { - val equipmentData: JSONArray? = farmingData.get("equipment") as JSONArray - player.farmingManager.equipment.container.parse(equipmentData) - } - if (farmingData.containsKey("bins")) { - val compostData: JSONArray? = farmingData.get("bins") as JSONArray - player.farmingManager.compostManager.parse(compostData) - } - if (farmingData.containsKey("wrappers")) { - val wrapperData: JSONArray? = farmingData.get("wrappers") as JSONArray - player.farmingManager.parseWrappers(wrapperData) - } - if(farmingData.containsKey("seedlings")){ - val seedlingData = farmingData.get("seedlings") as JSONArray - player.farmingManager.seedlingManager.parse(seedlingData) - } - if(farmingData.containsKey("farmingAmuletWrapperID")){ - player.farmingManager.amuletBoundWrapper = player.farmingManager.getPatchWrapper(farmingData.get("farmingAmuletWrapperID").toString().toInt()) - }*/ - } - - fun parseAutocastSpell() { - val autocastRaw = saveFile!!["autocastSpell"] - autocastRaw ?: return - val autocast = autocastRaw as JSONObject - val book = (autocast.get("book") as String).toInt() - val spellId = (autocast.get("spellId") as String).toInt() - player.properties.autocastSpell = SpellBookManager.SpellBook.values()[book].getSpell(spellId) as CombatSpell - } - - fun parseSavedData() { - val activityData = saveFile!!["activityData"] as JSONObject - val questData = saveFile!!["questData"] as JSONObject - val globalData = saveFile!!["globalData"] as JSONObject - player.savedData.activityData.parse(activityData) - player.savedData.questData.parse(questData) - player.savedData.globalData.parse(globalData) - } - - fun parseGrandExchange() { - val geData: Any? = saveFile!!["grand_exchange"] - if (geData != null) { - player.playerGrandExchange.parse(geData as JSONObject) - } - - } - - fun parseSpellbook() { - val spellbookData = (saveFile!!["spellbook"] as String).toInt() - player.spellBookManager.setSpellBook(SpellBookManager.SpellBook.forInterface(spellbookData)) - } - - fun parseGrave() { - saveFile ?: return - val graveData = (saveFile!!["grave_type"] as String).toInt() - player.graveManager.type = GraveType.values()[graveData] - } - - fun parseAppearance() { - saveFile ?: return - val appearanceData = saveFile!!["appearance"] as JSONObject - player.appearance.parse(appearanceData) - } - - fun parseQuests() { - saveFile ?: return - val questData = saveFile!!["quests"] as JSONObject - player.questRepository.parse(questData) - } - - fun parseSlayer() { - saveFile ?: return - val slayerData = saveFile!!["slayer"] as JSONObject - player.slayer.parse(slayerData) - } - - fun parseCore() { - saveFile ?: return - val coreData = saveFile!!["core_data"] as JSONObject - val inventory = coreData["inventory"] as JSONArray - val bank = coreData["bank"] as JSONArray - val equipment = coreData["equipment"] as JSONArray - val location = coreData["location"] as String - val bankTabData = coreData["bankTabs"] - if (bankTabData != null) { - val tabData = bankTabData as JSONArray - for (i in tabData) { - i ?: continue - val tab = i as JSONObject - val index = tab["index"].toString().toInt() - val startSlot = tab["startSlot"].toString().toInt() - player.bank.tabStartSlot[index] = startSlot - } - } - player.inventory.parse(inventory) - player.bank.parse(bank) - player.equipment.parse(equipment) - player.location = rs09.JSONUtils.parseLocation(location) - } - - fun parseSkills() { - saveFile ?: return - 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!! - } - val divisor: Double - if(player.skills.experienceMutiplier >= 10){ - divisor = player.skills.experienceMutiplier / 5.0 - player.skills.correct(divisor) - } - if (saveFile!!.containsKey("milestone")) { - val milestone: JSONObject = saveFile!!["milestone"] as JSONObject - player.skills.combatMilestone = (milestone.get("combatMilestone")).toString().toInt() - player.skills.skillMilestone = (milestone.get("skillMilestone")).toString().toInt() - } - } - - fun parseSettings() { - saveFile ?: return - val settingsData = saveFile!!["settings"] as JSONObject - player.settings.parse(settingsData) - } - - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt deleted file mode 100644 index 2c8d65e41..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt +++ /dev/null @@ -1,809 +0,0 @@ -package rs09.game.node.entity.player.info.login - -import core.game.container.Container -import core.game.interaction.item.brawling_gloves.BrawlingGloves -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.IronmanMode -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.summoning.familiar.BurdenBeast -import core.game.node.entity.skill.summoning.pet.Pet -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import rs09.ServerConstants -import rs09.game.system.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 - - -/** - * Class used for saving the player's data in JSON format. - * Files are saved in the directory defined in ServerConstants.PLAYER_SAVE_PATH - * @param player: the player to save for. - * @author Ceikry - */ -class PlayerSaver (val player: Player){ - private fun populate(): JSONObject = runBlocking{ - val saveFile = JSONObject() - val a = launch { - saveCoreData(saveFile) - saveSkills(saveFile) - saveSettings(saveFile) - saveSlayer(saveFile) - saveQuests(saveFile) - saveAppearance(saveFile) - saveSpellbook(saveFile) - saveVarps(saveFile) - } - val b = launch { - saveGraveType(saveFile) - saveGrandExchangeData(saveFile) - saveSavedData(saveFile) - saveAutocast(saveFile) - saveFarming(saveFile) - saveConfigs(saveFile) - savePlayerMonitor(saveFile) - } - val c = launch { - saveMusicPlayer(saveFile) - saveFamiliarManager(saveFile) - saveBarCrawl(saveFile) - saveStateManager(saveFile) - saveAntiMacroHandler(saveFile) - saveTreasureTrails(saveFile) - saveBankPinData(saveFile) - } - val d = launch { - saveHouseData(saveFile) - saveAchievementData(saveFile) - saveIronManData(saveFile) - saveEmoteData(saveFile) - saveStatManager(saveFile) - saveBrawlingGloves(saveFile) - saveAttributes(saveFile) - } - a.join() - b.join() - c.join() - d.join() - savePouches(saveFile) - saveFile - } - fun save() = runBlocking { - val manager = ScriptEngineManager() - val scriptEngine = manager.getEngineByName("JavaScript") - scriptEngine.put("jsonString", populate().toJSONString()) - scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") - val prettyPrintedJson = scriptEngine["result"] as String - - 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() - } - FileWriter("${ServerConstants.PLAYER_SAVE_PATH}${player.name}.json").use { file -> - file.write(prettyPrintedJson) - file.flush() - } - } catch (e: IOException) { - e.printStackTrace() - } - } - - fun saveVarps(root: JSONObject){ - player.varpManager.save(root) - } - - fun savePouches(root: JSONObject){ - player.pouchManager.save(root) - } - - 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 attr = JSONObject() - val type = when(value){ - is Int -> "int" - is Boolean -> "bool" - is Long -> "long" - is Short -> "short" - is String -> "str" - is Byte -> "byte" - else -> "null".also { SystemLogger.logWarn("Invalid attribute type for key: $key in PlayerSaver.kt Line 115") } - } - attr.put("type",type) - attr.put("key",key) - if(value is Byte){ - val asString = Base64.getEncoder().encodeToString(byteArrayOf(value)) - attr.put("value",asString) - } else { - attr.put("value", if (value is Boolean) value else value.toString()) - } - if(isExpirable){ - attr.put("expirable",true) - attr.put("expiration-time",player.gameAttributes.keyExpirations[key].toString()) - } - attrs.add(attr) - } - root.put("attributes",attrs) - } - } - - fun saveBrawlingGloves(root: JSONObject){ - if(player.brawlingGlovesManager.GloveCharges.isNotEmpty()){ - val brawlingGloves = JSONArray() - player.brawlingGlovesManager.GloveCharges.map { - val brawlingGlove = JSONObject() - brawlingGlove.put("gloveId",BrawlingGloves.forId(it.key).indicator.toString()) - brawlingGlove.put("charges",it.value.toString()) - brawlingGloves.add(brawlingGlove) - } - root.put("brawlingGloves",brawlingGloves) - } - } - - fun saveStatManager(root: JSONObject){ - val statistics = JSONArray() - var index = 0 - root.put("statistics",statistics) - } - - fun saveEmoteData(root: JSONObject){ - if(player.emoteManager.isSaveRequired) { - val emoteData = JSONArray() - player.emoteManager.emotes.map { - emoteData.add(it.ordinal.toString()) - } - root.put("emoteData",emoteData) - } - } - - fun saveIronManData(root: JSONObject){ - if(player.ironmanManager.mode != IronmanMode.NONE){ - root.put("ironManMode",player.ironmanManager.mode.ordinal.toString()) - } - } - - fun saveAchievementData(root: JSONObject){ - val achievementData = JSONArray() - player.achievementDiaryManager.diarys.map { - val diary = JSONObject() - val startedLevels = JSONArray() - it.levelStarted.map { - startedLevels.add(it) - } - diary.put("startedLevels",startedLevels) - val completedLevels = JSONArray() - it.taskCompleted.map { - val level = JSONArray() - it.map { - level.add(it) - } - completedLevels.add(level) - } - diary.put("completedLevels",completedLevels) - val rewardedLevels = JSONArray() - it.levelRewarded.map { - rewardedLevels.add(it) - } - diary.put("rewardedLevels",rewardedLevels) - val diaryCollector = JSONObject() - diaryCollector.put(it.type.name, diary) - achievementData.add(diaryCollector) - } - root.put("achievementDiaries",achievementData) - } - - fun saveHouseData(root: JSONObject){ - val manager = player.houseManager - val houseData = JSONObject() - houseData.put("location",manager.location.ordinal.toString()) - houseData.put("style",manager.style.ordinal.toString()) - if(manager.hasServant()){ - val servant = JSONObject() - servant.put("type",manager.servant.type.ordinal.toString()) - servant.put("uses",manager.servant.uses.toString()) - if(manager.servant.item != null){ - val item = JSONObject() - item.put("id",manager.servant.item.id.toString()) - item.put("amount",manager.servant.item.amount.toString()) - servant.put("item",item) - } - servant.put("greet",manager.servant.isGreet) - houseData.put("servant",servant) - } - val rooms = JSONArray() - var z = 0 - for(room in player.houseManager.rooms){ - var x = 0 - for(xr in room){ - var y = 0 - for(yr in xr){ - if(yr != null) { - val r = JSONObject() - r.put("z", z.toString()) - r.put("x", x.toString()) - r.put("y", y.toString()) - r.put("properties", yr.properties.ordinal.toString()) - r.put("rotation", yr.rotation.toInteger().toString()) - val hotspots = JSONArray() - var hotspotIndex = 0 - yr.hotspots.map { - if (it.decorationIndex > -1) { - val hotspot = JSONObject() - hotspot.put("hotspotIndex", hotspotIndex.toString()) - hotspot.put("decorationIndex", it.decorationIndex.toString()) - hotspots.add(hotspot) - } - hotspotIndex++ - } - r.put("hotspots", hotspots) - rooms.add(r) - } - y++ - } - x++ - } - z++ - } - houseData.put("rooms",rooms) - root.put("houseData",houseData) - } - - fun saveBankPinData(root: JSONObject){ - val bankPinManager = JSONObject() - if(player.bankPinManager.hasPin()){ - bankPinManager.put("pin",player.bankPinManager.pin.toString()) - } - bankPinManager.put("longRecovery",player.bankPinManager.isLongRecovery) - if(player.bankPinManager.status.ordinal != 0){ - bankPinManager.put("status",player.bankPinManager.status.ordinal.toString()) - } - if(player.bankPinManager.pendingDelay != -1L && player.bankPinManager.pendingDelay > System.currentTimeMillis()){ - bankPinManager.put("pendingDelay",player.bankPinManager.pendingDelay.toString()) - } - if(player.bankPinManager.tryDelay > System.currentTimeMillis()){ - bankPinManager.put("tryDelay",player.bankPinManager.tryDelay.toString()) - } - root.put("bankPinManager",bankPinManager) - } - - fun saveTreasureTrails(root: JSONObject){ - val treasureTrailManager = JSONObject() - if(player.treasureTrailManager.hasTrail()){ - val trail = JSONObject() - trail.put("clueId",player.treasureTrailManager.clueId.toString()) - trail.put("length",player.treasureTrailManager.trailLength.toString()) - trail.put("stage",player.treasureTrailManager.trailStage.toString()) - treasureTrailManager.put("trail",trail) - } - val completedClues = JSONArray() - player.treasureTrailManager.completedClues.map { - completedClues.add(it.toString()) - } - treasureTrailManager.put("completedClues",completedClues) - root.put("treasureTrails",treasureTrailManager) - } - - fun saveAntiMacroHandler(root: JSONObject){ - /*if(player.antiMacroHandler.isSaveRequired){ - val antiMacroEvent = JSONObject() - if(player.antiMacroHandler.hasEvent()){ - antiMacroEvent.put("eventName",player.antiMacroHandler.event.name) - root.put("antiMacroEvent",antiMacroEvent) - } - }*/ - } - - 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 saveBarCrawl(root: JSONObject){ - val barCrawl = JSONObject() - barCrawl.put("started",player.barcrawlManager.isStarted) - val barsVisited = JSONArray() - player.barcrawlManager.bars.map { - barsVisited.add(it) - } - barCrawl.put("bars",barsVisited) - root.put("barCrawl",barCrawl) - } - - fun saveFamiliarManager(root: JSONObject){ - val familiarManager = JSONObject() - val petDetails = JSONArray() - 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) - } - familiarManager.put("petDetails",petDetails) - if(player.familiarManager.hasPet()){ - familiarManager.put("currentPet",(player.familiarManager.familiar as Pet).pet.babyItemId.toString()) - } else if (player.familiarManager.hasFamiliar()){ - val familiar = JSONObject() - familiar.put("originalId",player.familiarManager.familiar.originalId.toString()) - familiar.put("ticks",player.familiarManager.familiar.ticks.toString()) - familiar.put("specialPoints",player.familiarManager.familiar.specialPoints.toString()) - if(player.familiarManager.familiar.isBurdenBeast && !(player.familiarManager.familiar as BurdenBeast).container.isEmpty){ - val familiarInventory = saveContainer((player.familiarManager.familiar as BurdenBeast).container) - familiar.put("inventory",familiarInventory) - } - familiar.put("lifepoints",player.familiarManager.familiar.skills.lifepoints) - familiarManager.put("familiar",familiar) - - } - root.put("familiarManager",familiarManager) - } - - fun saveMusicPlayer(root: JSONObject){ - val unlockedMusic = JSONArray() - player.musicPlayer.unlocked.values.map { - unlockedMusic.add(it.id.toString()) - } - root.put("unlockedMusic",unlockedMusic) - } - - fun savePlayerMonitor(root: JSONObject){ - val playerMonitor = JSONObject() - if(player.monitor.duplicationLog != null && player.monitor.duplicationLog.flag != 0){ - playerMonitor.put("duplicationFlag",player.monitor.duplicationLog.flag.toString()) - } - if(player.monitor.macroFlag != 0){ - playerMonitor.put("macroFlag",player.monitor.macroFlag.toString()) - } - if(player.monitor.duplicationLog != null && player.monitor.duplicationLog.isLoggingFlagged){ - playerMonitor.put("lastIncreaseFlag",player.monitor.duplicationLog.lastIncreaseFlag.toString()) - } - root.put("playerMonitor",playerMonitor) - } - - 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 saveFarming(root: JSONObject){ - /*val farming = JSONObject() - if(player.farmingManager.equipment.container.itemCount() != 0) { - val equipment = saveContainer(player.farmingManager.equipment.container) - farming.put("equipment",equipment) - } - if(player.farmingManager.compostManager.bins.size != 0){ - val bins = JSONArray() - player.farmingManager.compostManager.bins.map { compostBin -> - val bin = JSONObject() - bin.put("wrapperId",compostBin.wrapperId.toString()) - if(compostBin.timeStamp != 0L){ - bin.put("timeStamp",compostBin.timeStamp.toString()) - } - if(compostBin.compostLevel > 0) { - bin.put("compostLevel",compostBin.compostLevel.toString()) - } - if(compostBin.container.itemCount() > 0) { - val compostContainer = saveContainer(compostBin.container) - bin.put("compostContainer", compostContainer) - } - bins.add(bin) - } - farming.put("bins",bins) - player.farmingManager.seedlingManager.save(farming) - } - - if(player.farmingManager.patches.size != 0){ - val wrappers = JSONArray() - player.farmingManager.patches.map { - val wrapper = JSONObject() - wrapper.put("wrapperId",it.wrapperId.toString()) - if(player.farmingManager.amuletBoundWrapper == player.farmingManager.getPatchWrapper(it.wrapperId)) - farming.put("farmingAmuletWrapperID",it.wrapperId) - val cycle = JSONObject() - cycle.put("compostThreshold",it.cycle.compostThreshold.toString()) - cycle.put("growthTime",it.cycle.growthTime.toString()) - cycle.put("harvestAmount",it.cycle.harvestAmount.toString()) - cycle.put("protection",it.cycle.isProtected) - wrapper.put("cycle",cycle) - if(it.node != null){ - wrapper.put("nodeId",it.patch.getNodePosition(it.node).toString()) - } - wrappers.add(wrapper) - } - farming.put("wrappers",wrappers) - } - root.put("farming",farming)*/ - } - - fun saveAutocast(root: JSONObject){ - player.properties.autocastSpell ?: return - val spell = JSONObject() - spell.put("book",player.properties.autocastSpell.book.ordinal.toString()) - spell.put("spellId",player.properties.autocastSpell.spellId.toString()) - root.put("autocastSpell",spell) - } - - fun saveSavedData(root: JSONObject) { - saveActivityData(root) - saveQuestData(root) - saveGlobalData(root) - } - - 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()) - globalData.put("assistTime",player.savedData.globalData.assistTime.toString()) - val assistExperience = JSONArray() - player.savedData.globalData.assistExperience.map { - assistExperience.add(it.toString()) - } - globalData.put("assistExperience",assistExperience) - val strongholdRewards = JSONArray() - player.savedData.globalData.strongHoldRewards.map { - strongholdRewards.add(it) - } - globalData.put("strongHoldRewards",strongholdRewards) - globalData.put("chatPing",player.savedData.globalData.chatPing.toString()) - globalData.put("tutorClaim",player.savedData.globalData.tutorClaim.toString()) - globalData.put("luthasTask",player.savedData.globalData.isLuthasTask) - globalData.put("karamjaBananas",player.savedData.globalData.karamjaBananas.toString()) - globalData.put("silkSteal",player.savedData.globalData.silkSteal.toString()) - globalData.put("zafAmount",player.savedData.globalData.zaffAmount.toString()) - globalData.put("zafTime",player.savedData.globalData.zafTime.toString()) - globalData.put("fritzGlass",player.savedData.globalData.isFritzGlass) - globalData.put("wydinEmployee",player.savedData.globalData.isWydinEmployee) - globalData.put("draynorRecording",player.savedData.globalData.isDraynorRecording) - globalData.put("geTutorial",player.savedData.globalData.isGeTutorial) - globalData.put("essenceTeleporter",player.savedData.globalData.essenceTeleporter.toString()) - globalData.put("recoilDamage",player.savedData.globalData.recoilDamage.toString()) - globalData.put("doubleExpDelay",player.savedData.globalData.doubleExpDelay.toString()) - globalData.put("joinedMonastery",player.savedData.globalData.isJoinedMonastery) - val readPlaques = JSONArray() - player.savedData.globalData.readPlaques.map { - readPlaques.add(it) - } - globalData.put("readPlaques",readPlaques) - globalData.put("forgingUses",player.savedData.globalData.forgingUses.toString()) - globalData.put("ectoCharges",player.savedData.globalData.ectoCharges.toString()) - globalData.put("braceletClayUses",player.savedData.globalData.braceletClayUses.toString()) - globalData.put("dropDelay",player.savedData.globalData.dropDelay.toString()) - val abyssData = JSONArray() - player.savedData.globalData.abyssData.map { - abyssData.add(it) - } - globalData.put("abyssData",abyssData) - val rcDecays = JSONArray() - player.savedData.globalData.rcDecays.map { - rcDecays.add(it.toString()) - } - globalData.put("rcDecays",rcDecays) - globalData.put("disableDeathScreen",player.savedData.globalData.isDeathScreenDisabled) - globalData.put("playerTestStage",player.savedData.globalData.playerTestStage.toString()) - globalData.put("charmingDelay",player.savedData.globalData.charmingDelay.toString()) - val travelLogs = JSONArray() - player.savedData.globalData.travelLogs.map { - travelLogs.add(it) - } - globalData.put("travelLogs",travelLogs) - val godBooks = JSONArray() - player.savedData.globalData.godBooks.map { - godBooks.add(it) - } - globalData.put("godBooks",godBooks) - globalData.put("disableNews",player.savedData.globalData.isDisableNews) - val godPages = JSONArray() - player.savedData.globalData.godPages.map { - godPages.add(it) - } - globalData.put("godPages",godPages) - globalData.put("overChargeDelay",player.savedData.globalData.overChargeDelay.toString()) - val bossCounters = JSONArray() - player.savedData.globalData.bossCounters.map { - bossCounters.add(it.toString()) - } - globalData.put("bossCounters",bossCounters) - globalData.put("barrowsLoots",player.savedData.globalData.barrowsLoots.toString()) - globalData.put("lootSharePoints",player.savedData.globalData.lootSharePoints.toString()) - globalData.put("lootShareDelay",player.savedData.globalData.lootShareDelay.toString()) - globalData.put("doubleExp",player.savedData.globalData.doubleExp.toString()) - globalData.put("avasDevice",player.savedData.globalData.isAvasDisabled) - globalData.put("globalTeleporterDelay",player.savedData.globalData.globalTeleporterDelay.toString()) - globalData.put("starSpriteDelay",player.savedData.globalData.starSpriteDelay.toString()) - globalData.put("runReplenishDelay",player.savedData.globalData.runReplenishDelay.toString()) - globalData.put("runReplenishCharges",player.savedData.globalData.runReplenishCharges.toString()) - globalData.put("lowAlchemyCharges",player.savedData.globalData.lowAlchemyCharges.toString()) - globalData.put("lowAlchemyDelay",player.savedData.globalData.lowAlchemyDelay.toString()) - globalData.put("magicSkillCapeDelay",player.savedData.globalData.magicSkillCapeDelay.toString()) - globalData.put("hunterCapeDelay",player.savedData.globalData.hunterCapeDelay.toString()) - globalData.put("hunterCapeCharges",player.savedData.globalData.hunterCapeCharges.toString()) - globalData.put("taskAmount",player.savedData.globalData.taskAmount.toString()) - globalData.put("taskPoints",player.savedData.globalData.taskPoints.toString()) - globalData.put("macroDisabled",player.savedData.globalData.macroDisabled) - root.put("globalData",globalData) - } - - fun saveQuestData(root: JSONObject){ - val questData = JSONObject() - val draynorLever = JSONArray() - player.savedData.questData.draynorLever.map { - draynorLever.add(it) - } - questData.put("draynorLever",draynorLever) - val dslayer = JSONArray() - player.savedData.questData.dragonSlayer.map { - dslayer.add(it) - } - questData.put("dragonSlayer",dslayer) - questData.put("dragonSlayerPlanks",player.savedData.questData.dragonSlayerPlanks.toString()) - val demonSlayer = JSONArray() - player.savedData.questData.demonSlayer.map { - demonSlayer.add(it) - } - questData.put("demonSlayer",demonSlayer) - val cooksAssistant = JSONArray() - player.savedData.questData.cooksAssistant.map { - cooksAssistant.add(it) - } - questData.put("cooksAssistant",cooksAssistant) - questData.put("gardenerAttack",player.savedData.questData.isGardenerAttack) - questData.put("talkedDrezel",player.savedData.questData.isTalkedDrezel) - val desertTreasureNode = JSONArray() - player.savedData.questData.desertTreasure.map { - val item = JSONObject() - item.put("id",it.id.toString()) - item.put("amount",it.amount.toString()) - desertTreasureNode.add(item) - } - questData.put("desertTreasureNode",desertTreasureNode) - questData.put("witchsExperimentStage",player.savedData.questData.witchsExperimentStage.toString()) - questData.put("witchsExperimentKilled",player.savedData.questData.isWitchsExperimentKilled) - root.put("questData",questData) - } - - fun saveActivityData(root: JSONObject){ - val activityData = JSONObject() - activityData.put("pestPoints",player.savedData.activityData.pestPoints.toString()) - activityData.put("warriorGuildTokens",player.savedData.activityData.warriorGuildTokens.toString()) - activityData.put("bountyHunterRate",player.savedData.activityData.bountyHunterRate.toString()) - activityData.put("bountyRogueRate",player.savedData.activityData.bountyRogueRate.toString()) - activityData.put("barrowKills",player.savedData.activityData.barrowKills.toString()) - val barrowBrothers = JSONArray() - player.savedData.activityData.barrowBrothers.map { - barrowBrothers.add(it) - } - activityData.put("barrowBrothers",barrowBrothers) - activityData.put("barrowTunnelIndex",player.savedData.activityData.barrowTunnelIndex.toString()) - activityData.put("kolodionStage",player.savedData.activityData.kolodionStage.toString()) - val godCasts = JSONArray() - player.savedData.activityData.godCasts.map { - godCasts.add(it.toString()) - } - activityData.put("godCasts",godCasts) - activityData.put("kolodionBoss",player.savedData.activityData.kolodionBoss.toString()) - activityData.put("elnockSupplies",player.savedData.activityData.isElnockSupplies) - activityData.put("lastBorkBattle",player.savedData.activityData.lastBorkBattle.toString()) - activityData.put("startedMta",player.savedData.activityData.isStartedMta) - activityData.put("lostCannon",player.savedData.activityData.isLostCannon) - val pizazzPoints = JSONArray() - player.savedData.activityData.pizazzPoints.map { - pizazzPoints.add(it.toString()) - } - activityData.put("pizazzPoints",pizazzPoints) - activityData.put("bonesToPeaches",player.savedData.activityData.isBonesToPeaches) - activityData.put("solvedMazes",player.savedData.activityData.solvedMazes.toString()) - activityData.put("fogRating",player.savedData.activityData.fogRating.toString()) - activityData.put("borkKills",player.savedData.activityData.borkKills.toString()) - activityData.put("hardcoreDeath",player.savedData.activityData.hardcoreDeath) - activityData.put("topGrabbed",player.savedData.activityData.isTopGrabbed) - root.put("activityData",activityData) - } - - - fun saveGrandExchangeData(root: JSONObject){ - val grandExchange = JSONObject() - if(player.playerGrandExchange.hasActiveOffer()){ - val offers = JSONArray() - player.playerGrandExchange.offers.map { - if(it != null){ - val offer = JSONObject() - offer["offerIndex"] = it.index.toString() - offer["offerUID"] = it.uid.toString() - offers.add(offer) - } - } - grandExchange["offers"] = offers - } - val history = JSONArray() - player.playerGrandExchange.history.map { - if(it != null){ - val historyEntry = JSONObject() - historyEntry["isSell"] = it.sell - historyEntry["itemId"] = it.itemID.toString() - historyEntry["totalCoinExchange"] = it.totalCoinExchange.toString() - historyEntry["completedAmount"] = it.completedAmount.toString() - history.add(historyEntry) - } - } - grandExchange["history"] = history - root["grand_exchange"] = grandExchange - } - - fun saveGraveType(root: JSONObject){ - root.put("grave_type",player.graveManager.type.ordinal.toString()) - } - - fun saveSpellbook(root: JSONObject){ - root.put("spellbook",player.spellBookManager.spellBook.toString()) - } - - fun saveAppearance(root: JSONObject){ - val appearance = JSONObject() - appearance.put("gender",player.appearance.gender.toByte().toString()) - val appearanceCache = JSONArray() - player.appearance.appearanceCache.map { - val bodyPart = JSONObject() - bodyPart.put("look",it.look.toString()) - bodyPart.put("color",it.color.toString()) - appearanceCache.add(bodyPart) - } - appearance.put("appearance_cache", appearanceCache) - root.put("appearance",appearance) - } - - fun saveQuests(root: JSONObject){ - val quests = JSONObject() - quests.put("points",player.questRepository.points.toString()) - val questStages = JSONArray() - player.questRepository.questList.map { - val quest = JSONObject() - quest.put("questId",it.key.toString()) - quest.put("questStage",it.value.toString()) - questStages.add(quest) - } - quests.put("questStages",questStages) - root.put("quests",quests) - } - - fun saveSlayer(root: JSONObject){ - val slayer = JSONObject() - val slayerManager = player.slayer - if(slayerManager.removed.isNotEmpty()) { - val removedTasks = JSONArray() - slayerManager.removed.map { - removedTasks.add(it.ordinal.toString()) - } - slayer.put("removedTasks",removedTasks) - } - slayer.put("taskStreak",slayerManager.taskCount.toString()) - slayer.put("totalTasks",slayerManager.totalTasks.toString()) - slayer.put("equipmentFlags",slayerManager.flags.equipmentFlags) - slayer.put("taskFlags", slayerManager.flags.taskFlags) - slayer.put("rewardFlags", slayerManager.flags.rewardFlags) - root.put("slayer",slayer) - } - - fun saveSettings(root: JSONObject){ - val settings = JSONObject() - settings.put("brightness",player.settings.brightness.toString()) - settings.put("musicVolume",player.settings.musicVolume.toString()) - settings.put("soundEffectVolume",player.settings.soundEffectVolume.toString()) - settings.put("areaSoundVolume",player.settings.areaSoundVolume.toString()) - settings.put("publicChatSetting",player.settings.publicChatSetting.toString()) - settings.put("privateChatSetting",player.settings.privateChatSetting.toString()) - settings.put("clanChatSetting",player.settings.clanChatSetting.toString()) - settings.put("tradeSetting",player.settings.tradeSetting.toString()) - settings.put("assistSetting",player.settings.assistSetting.toString()) - settings.put("runEnergy",player.settings.runEnergy.toString()) - settings.put("specialEnergy",player.settings.specialEnergy.toString()) - settings.put("attackStyle",player.settings.attackStyleIndex.toString()) - settings.put("singleMouse",player.settings.isSingleMouseButton) - settings.put("disableChatEffects",player.settings.isDisableChatEffects) - settings.put("splitPrivate",player.settings.isSplitPrivateChat) - settings.put("acceptAid",player.settings.isAcceptAid) - settings.put("runToggled",player.settings.isRunToggled) - settings.put("retaliation",player.properties.isRetaliating) - root.put("settings",settings) - } - - fun saveSkills(root: JSONObject){ - val skills = JSONArray() - for(i in 0 until 24){ - 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("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()) - if(player.skills.combatMilestone > 0 || player.skills.skillMilestone > 0){ - val milestone = JSONObject() - milestone.put("combatMilestone",player.skills.combatMilestone.toString()) - milestone.put("skillMilestone",player.skills.skillMilestone.toString()) - root.put("milestone",milestone) - } - } - - fun saveContainer(container: Container): JSONArray { - val json = JSONArray() - container.toArray().map{ - if (it != null) { - val item = JSONObject() - item.put("slot", it.slot.toString()) - item.put("id", it.id.toString()) - item.put("amount", it.amount.toString()) - item.put("charge", it.charge.toString()) - json.add(item) - } - } - return json - } - - fun saveCoreData(root: JSONObject){ - val coreData = JSONObject() - val inventory = saveContainer(player.inventory) - coreData.put("inventory",inventory) - - val bank = saveContainer(player.bank) - coreData.put("bank",bank) - - val bankTabs = JSONArray() - for(i in player.bank.tabStartSlot.indices){ - val tab = JSONObject() - tab.put("index",i.toString()) - tab.put("startSlot",player.bank.tabStartSlot[i].toString()) - bankTabs.add(tab) - } - coreData.put("bankTabs",bankTabs) - - val equipment = saveContainer(player.equipment) - coreData.put("equipment",equipment) - - val loctemp = player.location - val locStr = "${loctemp.x},${loctemp.y},${loctemp.z}" - coreData.put("location",locStr) - - root.put("core_data",coreData) - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/stats/StatAttributeKeys.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/stats/StatAttributeKeys.kt deleted file mode 100644 index ee08acd85..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/stats/StatAttributeKeys.kt +++ /dev/null @@ -1,13 +0,0 @@ -package rs09.game.node.entity.player.info.stats - -const val STATS_BASE = "stats_manager" -const val STATS_ENEMIES_KILLED = "enemies_killed" -const val STATS_DEATHS = "deaths" -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_ALKHARID_GATE = "alkharid_gate" -const val FISHING_TRAWLER_GAMES_WON = "fishing-trawler-games" -const val FISHING_TRAWLER_LEAKS_PATCHED = "fishing-trawler-leaks-patched" -const val FISHING_TRAWLER_SHIPS_SANK = "fishing-trawler-sank" diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/stats/StatsCommandSet.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/stats/StatsCommandSet.kt deleted file mode 100644 index 69d9a7e8b..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/stats/StatsCommandSet.kt +++ /dev/null @@ -1,218 +0,0 @@ -package rs09.game.node.entity.player.info.stats - -import core.game.component.Component -import core.game.content.global.BossKillCounter -import core.game.node.entity.player.Player -import core.plugin.Initializable -import org.rs09.consts.Items; -import org.rs09.consts.NPCs; -import rs09.game.content.global.GlobalKillCounter; -import rs09.game.interaction.InterfaceListener -import rs09.game.system.command.Command -import rs09.game.system.command.sets.CommandSet -import rs09.game.world.repository.Repository - -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) -val GARGOYLE_IDS = intArrayOf(NPCs.GARGOYLE_1610, NPCs.GARGOYLE_1827, NPCs.GARGOYLE_6389) -val SPIRITUAL_MAGE_IDS = intArrayOf(NPCs.SPIRITUAL_MAGE_6221, NPCs.SPIRITUAL_MAGE_6231, NPCs.SPIRITUAL_MAGE_6257, NPCs.SPIRITUAL_MAGE_6278) -val GREEN_DRAGON_IDS = intArrayOf(NPCs.GREEN_DRAGON_941, NPCs.GREEN_DRAGON_4677, NPCs.GREEN_DRAGON_4678, NPCs.GREEN_DRAGON_4679, NPCs.GREEN_DRAGON_4680, NPCs.BRUTAL_GREEN_DRAGON_5362) -val BLUE_DRAGON_IDS = intArrayOf(NPCs.BLUE_DRAGON_55, NPCs.BLUE_DRAGON_4681, NPCs.BLUE_DRAGON_4682, NPCs.BLUE_DRAGON_4683, NPCs.BLUE_DRAGON_4684, NPCs.BLUE_DRAGON_5178) -val RED_DRAGON_IDS = intArrayOf(NPCs.RED_DRAGON_53, NPCs.RED_DRAGON_4669, NPCs.RED_DRAGON_4670, NPCs.RED_DRAGON_4671, NPCs.RED_DRAGON_4672) -val BLACK_DRAGON_IDS = intArrayOf(NPCs.BLACK_DRAGON_54, NPCs.BLACK_DRAGON_4673, NPCs.BLACK_DRAGON_4674, NPCs.BLACK_DRAGON_4675, NPCs.BLACK_DRAGON_4676) -val BRONZE_DRAGON_IDS = intArrayOf(NPCs.BRONZE_DRAGON_1590) -val IRON_DRAGON_IDS = intArrayOf(NPCs.IRON_DRAGON_1591) -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: ${other.treasureTrailManager.completedClues[0]}",i) - 68 -> sendLine(player,"Medium Clues: ${other.treasureTrailManager.completedClues[1]}",i) - 69 -> sendLine(player,"Hard Clues: ${other.treasureTrailManager.completedClues[2]}",i) - 70 -> sendLine(player,SPACER,i) - 71 -> sendLine(player,"Slayer Tasks: ${other.slayer.totalTasks}",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) {} - -@Initializable -class StatsCommandSet : CommandSet(Command.Privilege.STANDARD) { - override fun defineCommands() { - define("stats"){player,args -> - if(args.size == 1){ - sendStats(player) - return@define - } - - if(args.size > 2){ - reject(player,"Usage: ::stats playername") - return@define - } - - val other = Repository.getPlayerByName(args[1]) - if(other == 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 defineListeners() { - 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 - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryLevel.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryLevel.kt deleted file mode 100644 index 33b2a4255..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryLevel.kt +++ /dev/null @@ -1,8 +0,0 @@ -package rs09.game.node.entity.player.link.diary - -enum class DiaryLevel { - BEGINNER, - EASY, - MEDIUM, - HARD -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/chapel/BurnerListener.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/chapel/BurnerListener.kt deleted file mode 100644 index 1f4c227a0..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/chapel/BurnerListener.kt +++ /dev/null @@ -1,43 +0,0 @@ -package rs09.game.node.entity.skill.construction.decoration.chapel - -import core.game.node.scenery.Scenery -import core.game.node.scenery.SceneryBuilder -import core.game.node.item.Item -import core.game.world.update.flag.context.Animation -import core.tools.RandomFunction -import rs09.game.interaction.InteractionListener - -/** - * Handles the lighting of the torches of the Chapel. - * @author Splinter - */ -class BurnerListener : InteractionListener() { - - val IDs = intArrayOf(13202,13203,13204,13205,13206,13207,13208,13209,13210,13211,13212,13213) - - override fun defineListeners() { - on(IDs,SCENERY,"light"){ player, node -> - if (player.ironmanManager.checkRestriction() && !player.houseManager.isInHouse(player)) { - return@on true - } - if (!player.inventory.containsItem(Item(590)) || !player.inventory.containsItem(Item(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))) { - player.lock(1) - player.animate(Animation.create(3687)) - player.sendMessage("You burn some marrentill in the incense burner.") - SceneryBuilder.replace( - node.asScenery(), - Scenery(node.asScenery().id + 1, node.location), - RandomFunction.random(100, 175) - ) - } - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/costume/ToyBoxPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/costume/ToyBoxPlugin.kt deleted file mode 100644 index 5c1d7818e..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/costume/ToyBoxPlugin.kt +++ /dev/null @@ -1,29 +0,0 @@ -package rs09.game.node.entity.skill.construction.decoration.costume - -import core.cache.def.impl.SceneryDefinition -import core.game.interaction.OptionHandler -import core.game.interaction.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/kotlin/rs09/game/node/entity/skill/construction/decoration/study/LecternPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/study/LecternPlugin.kt deleted file mode 100644 index 6fe7252d2..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/study/LecternPlugin.kt +++ /dev/null @@ -1,200 +0,0 @@ -package rs09.game.node.entity.skill.construction.decoration.study - -import core.cache.def.impl.SceneryDefinition -import core.game.component.Component -import core.game.component.ComponentDefinition -import core.game.component.ComponentPlugin -import core.game.interaction.OptionHandler -import core.game.interaction.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 core.game.node.entity.skill.construction.Decoration -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 rs09.game.world.GameWorld -import rs09.plugin.PluginManager.definePlugin - -/** - * Handles the lectern - * LecternPlugin - * @author Ceikry - */ -@Initializable -class LecternPlugin : OptionHandler() { - /** - * TeleTabButton - */ - private enum class TeleTabButton( - /** - * The button id - */ - val buttonId: Int, - /** - * The level and xp - */ - val level: Int, - val xp: Int, - /** - * The item - */ - val tabItem: Item, - /** - * The required decoration (lectern) - */ - private val requiredDecorations: Array, vararg requiredItems: Item) { - ARDOUGNE(2, 51, 66, 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, 32, 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, 36, Item(8015), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(561, 2), Item(557, 4), Item(555, 4)), - CAMELOT(5, 45, 56, 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, 62, Item(8019), arrayOf(Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(557, 10)), - ENCHANT_DRAGONSTONE(7, 68, 60, Item(8020), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(557, 15), Item(555, 15)), - ENCHANT_EMERALD(8, 27, 34, 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, 84, Item(8021), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(557, 20), Item(554, 20)), - ENCHANT_RUBY(10, 49, 52, Item(8018), arrayOf(Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(554, 5)), - ENCHANT_SAPPHIRE(11, 7, 12, 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, 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, 40, 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, Item(8013), arrayOf(Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(563), Item(557), Item(556)), - VARROCK(15, 25, 35, 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, 74, Item(TeleTabsOptionPlugin.TeleTabs.WATCH_TOWER_TELEPORT.item), arrayOf(Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(563, 2), Item(557, 2)); - - /** - * The required items - */ - val requiredItems: ArrayList = arrayListOf(*requiredItems) - - /** - * Checks if the player can make this tab - * @param player - * @return - */ - fun canMake(player: Player): Boolean { - val objectId = player.getAttribute("ttb:objectid") - if (player.skills.getLevel(Skills.MAGIC) < level && player.spellBookManager.spellBook == 192) { - player.sendMessage("You need a magic level of $level to make that!") - return false - } - if (this == BONES_TO_PEACHES && !player.savedData.activityData.isBonesToPeaches) { - 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 - } - var found = false - for (d in requiredDecorations) if (d.objectId == objectId) found = true - if (!found) { - player.sendMessage("You're unable to make this tab on this specific lectern.") - return false - } - for (item in requiredItems) { - if (!player.inventory.containsItem(item)) { - //TODO staffs - player.sendMessage("You don't have enough materials.") - return false - } - } - return true - } - - companion object { - /** - * Gets the button for the id - * @param id - * @return - */ - fun forId(id: Int): TeleTabButton? { - for (ttb in values()) { - if (ttb.buttonId == id) return ttb - } - return null - } - } - - } - - @Throws(Throwable::class) - override fun newInstance(arg: Any?): Plugin? { - for (i in 13642..13648) { - SceneryDefinition.forId(i).handlers["option:study"] = this - } - definePlugin(TeleTabInterface()) - return this - } - - override fun handle(player: Player, node: Node, option: String): Boolean { - val id = node.asScenery().id - player.setAttribute("ttb:objectid", id) - GameWorld.Pulser.submit(object : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> player.animator.animate(Animation(3649)).also { player.lock() } - 8 -> player.interfaceManager.open(Component(400)).also { player.unlock(); return true } - } - return false - } - }) - /*var bits = 0 We dumb so we comment it out haha code go brrrrr - for (t in TeleTabButton.values()) { - if (t.canMake(player)) bits = bits or (1 shl t.buttonId) - } - player.configManager[2005] = bits*/ - return true - } - - /** - * TeleTabInterface - * @author Ceikry - */ - private class TeleTabInterface : ComponentPlugin() { - @Throws(Throwable::class) - override fun newInstance(arg: Any?): Plugin? { - ComponentDefinition.put(400, this) - return this - } - - override fun handle(player: Player, component: Component, opcode: Int, button: Int, slot: Int, itemId: Int): Boolean { - val ttb = TeleTabButton.forId(button) - if (ttb != null && ttb.canMake(player)) { - player.interfaceManager.close() - player.pulseManager.run(object : Pulse(1) { - override fun pulse(): Boolean { - if (!ttb.canMake(player)) { - return true - } - if (player.inventory.freeSlots() == 0) { - player.sendMessage("You don't have enough space in your inventory to make this.") - return true - } - if (player.inventory.remove(*ttb.requiredItems.toTypedArray())) { - player.inventory.add(ttb.tabItem) - player.skills.addExperience(Skills.MAGIC, ttb.xp / 2.toDouble(), true) - //TODO Add correct lectern animation. - player.animate(Animation(4460)) - ////////////////////////////// - super.setDelay(9) - if (ttb == TeleTabButton.VARROCK - && (player.getAttribute("ttb:objectid", 0) == Decoration.MAHOGANY_EAGLE_LECTERN.objectId - || player.getAttribute("ttb:objectid", 0) == Decoration.MAHOGANY_DEMON_LECTERN.objectId)) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 8) - } - } - return false - } - }) - } - player.animate(Animation(-1)) - return true - } - } - - companion object { - /** - * Soft clay - */ - private val SOFT_CLAY = Item(1761, 1) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/study/TelescopePlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/study/TelescopePlugin.kt deleted file mode 100644 index fee31ef24..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/study/TelescopePlugin.kt +++ /dev/null @@ -1,54 +0,0 @@ -package rs09.game.node.entity.skill.construction.decoration.study - -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.scenery.Scenery -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 -import core.tools.RandomFunction -import rs09.game.content.global.worldevents.WorldEvents -import rs09.game.content.global.worldevents.shootingstar.ShootingStarEvent -import rs09.game.world.GameWorld.Pulser -import java.util.concurrent.TimeUnit - -@Initializable -class TelescopePlugin : OptionHandler() { - @Throws(Throwable::class) - override fun newInstance(arg: Any?): Plugin? { - SceneryDefinition.forId(13656).handlers["option:observe"] = this - SceneryDefinition.forId(13657).handlers["option:observe"] = this - SceneryDefinition.forId(13658).handlers["option:observe"] = this - return this - } - - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - val star = (WorldEvents.get("shooting-stars") as ShootingStarEvent).star - 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 - 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!") - return true - } - return true - } - }) - return true - } - } - - companion object { - private val ANIMATION = Animation.create(3649) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/workshop/ArmourStand.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/workshop/ArmourStand.kt deleted file mode 100644 index 9a04823c1..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/workshop/ArmourStand.kt +++ /dev/null @@ -1,105 +0,0 @@ -package rs09.game.node.entity.skill.construction.decoration.workshop - -import core.game.content.dialogue.DialoguePlugin -import core.game.content.global.RepairItem -import core.game.interaction.NodeUsageEvent -import core.game.interaction.UseWithHandler -import core.game.interaction.npc.bob.BobDialogue.BarrowsEquipment -import core.game.interaction.npc.bob.BobDialogue.BarrowsEquipment.BarrowsFullEquipment -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 - -@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){ - override fun newInstance(arg: Any?): Plugin { - addHandler(13715, OBJECT_TYPE, this) - return this - } - - override fun handle(event: NodeUsageEvent?): Boolean { - event ?: return false - val player = event.player - val repairItem = RepairItem.forId(event.used.id) - - var baseCost = 0.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 - } - - if((repairItem == null && baseCost == 0.0)){ - 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) - - return true - } - @Initializable - class RepairDialogue(player: Player? = null) : DialoguePlugin(player){ - override fun newInstance(player: Player?): DialoguePlugin { - return RepairDialogue(player) - } - var item: Item? = null - var cost: Int = 0 - var product: Item? = null - - 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?") - 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() } - 2 -> end() - } - } - return true - } - - fun exchangeItems(item: Item, cost: Int, product: Item) { - val coins = Item(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.") - } else { - player.sendMessage("You can't afford that.") - } - } - - override fun getIds(): IntArray { - return intArrayOf(58824213) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/cooking/CookingDialogue.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/cooking/CookingDialogue.kt deleted file mode 100644 index 284cbee7d..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/cooking/CookingDialogue.kt +++ /dev/null @@ -1,99 +0,0 @@ -package rs09.game.node.entity.skill.cooking - -import api.ContentAPI -import core.cache.def.impl.ItemDefinition -import core.game.node.scenery.Scenery -import core.game.node.entity.player.link.RunScript -import core.game.node.entity.skill.cooking.CookableItems -import core.game.node.entity.skill.cooking.CookingRewrite.Companion.cook -import core.net.packet.PacketRepository -import core.net.packet.context.ChildPositionContext -import core.net.packet.out.RepositionChild -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.START_DIALOGUE - -class CookingDialogue(vararg val args: Any) : DialogueFile(){ - var initial = 0 - var product = 0 - var `object`: Scenery? = null - var sinew = false - override fun handle(componentID: Int, buttonID: Int) { - when(stage){ - START_DIALOGUE -> { - when (args.size) { - 2 -> { - initial = args.get(0) as Int - if (CookableItems.intentionalBurn(initial)) { // checks intentional burning - product = CookableItems.getIntentionalBurn(initial).id - } else { - product = CookableItems.forId(initial).cooked - } - `object` = args.get(1) as Scenery - } - 4 -> { - initial = args.get(0) as Int - product = args.get(1) as Int - sinew = args.get(2) as Boolean - `object` = args.get(3) as Scenery - if (sinew) { - player!!.dialogueInterpreter.sendOptions( - "Select one", - "Dry the meat into sinew", - "Cook the meat" - ) - stage = 100 - return - } - } - } - display() - } - - 1 -> { - end() - val amount = getAmount(buttonID) - when (amount) { - -1 -> { - ContentAPI.sendInputDialogue(player!!, true, "Enter the amount:"){value -> - cook(player!!, `object`, initial, product, value as Int) - } - } - else -> { - end() - cook(player!!, `object`, initial, product, amount) - } - } - } - - 100 -> { - when (buttonID) { - 1 -> cook(player!!, `object`, initial, product, 1) - 2 -> { - product = CookableItems.forId(initial).cooked - display() - } - } - } - } - } - - private fun getAmount(buttonId: Int): Int { - when (buttonId) { - 5 -> return 1 - 4 -> return 5 - 3 -> return -1 - 2 -> return player!!.inventory.getAmount(initial) - } - return -1 - } - - fun display() { - 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(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/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSourceExtinguisher.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSourceExtinguisher.kt deleted file mode 100644 index 4f056fef8..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSourceExtinguisher.kt +++ /dev/null @@ -1,41 +0,0 @@ -package rs09.game.node.entity.skill.crafting.lightsources - -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 rs09.game.system.SystemLogger -import core.plugin.Initializable -import core.plugin.Plugin - - -/** - * Extinguishes light sources - * @author Ceikry - */ -@Initializable -class LightSourceExtinguisher : OptionHandler(){ - override fun newInstance(arg: Any?): Plugin { - ItemDefinition.setOptionHandler("extinguish",this) - return this - } - - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - node ?: return false - player ?: return false - - val lightSource = LightSources.forId(node.id) - - lightSource ?: return false.also { SystemLogger.logWarn("UNHANDLED EXTINGUISH OPTION: ID = ${node.id}") } - - player.inventory.replace(node.asItem(), Item(lightSource.fullID)) - return true - } - - fun Container.replace(item: Item, with: Item){ - remove(item) - add(with) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSourceLighter.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSourceLighter.kt deleted file mode 100644 index 835cfec99..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/crafting/lightsources/LightSourceLighter.kt +++ /dev/null @@ -1,89 +0,0 @@ -package rs09.game.node.entity.skill.crafting.lightsources - -import core.game.container.Container -import core.game.interaction.NodeUsageEvent -import core.game.interaction.UseWithHandler -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 - -/** - * Method used to light various light sources - * @author Ceikry - */ -@Initializable -class LightSourceLighter : UseWithHandler(590,36,38){ - /** - * For Candles: - * 36 + tinderbox = 33 - * 38 + tinderbox = 32 - * - * For torches: - * 596 + tinderbox = 594 - * - * For Candle lanterns: - * 4529 + tinderbox = 4531 - * 4532 + tinderbox = 4534 - * Required Level: 4 Firemaking - * - * For oil lamps: - * 4522 + tinderbox = 4524 - * Required Level: 12 Firemaking - * - * For oil lanterns: - * 4537 + tinderbox = 4539 - * Required Level: 26 Firemaking - * - * For bullseye lanterns: - * 4548 + tinderbox = 4550 - * 4701 + tinderbox = 4702 - * 9064 + tinderbox = 9065 - * Required Level: 49 Firemaking - */ - - override fun newInstance(arg: Any?): Plugin { - addHandler(590, ITEM_TYPE, this) - addHandler(596, ITEM_TYPE, this) - addHandler(4529, ITEM_TYPE, this) - addHandler(4532, ITEM_TYPE, this) - addHandler(4522, ITEM_TYPE, this) - addHandler(4537, ITEM_TYPE, this) - addHandler(4548, ITEM_TYPE, this) - addHandler(4701, ITEM_TYPE, this) - addHandler(9064, ITEM_TYPE, this) - return this - } - - override fun handle(event: NodeUsageEvent?): Boolean { - event ?: return false - val inventory = event.player.inventory - val used = if(event.used.id == 590) event.usedWith.asItem() else event.used.asItem() //compensation for bad two-way use with handler shit - - val lightSource = LightSources.forId(used.id) - - lightSource ?: return false - - if(!light(event.player,used,lightSource)){ - event.player.sendMessage("You need a Firemaking level of at least ${lightSource.levelRequired} to light this.") - } - - return true - } - - fun Container.replace(item: Item, with: Item){ - remove(item) - add(with) - } - - fun light(player: Player, item: Item, lightSource: LightSources): Boolean{ - val requiredLevel = lightSource.levelRequired - val playerLevel = player.skills.getLevel(Skills.FIREMAKING) - - if(playerLevel < requiredLevel) return false - - player.inventory.replace(item,Item(lightSource.litID)) - return true - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBin.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBin.kt deleted file mode 100644 index eaf15ab7b..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBin.kt +++ /dev/null @@ -1,175 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import api.ContentAPI -import core.game.node.entity.player.Player -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 rs09.game.system.SystemLogger -import java.util.concurrent.TimeUnit - -class CompostBin(val player: Player, val bin: CompostBins) { - private var items = ArrayList() - var isSuperCompost = true - var isTomatoes = true - var isClosed = false - var finishedTime = 0L - var isFinished = false - - fun isFull() : Boolean { - return items.size == 15 - } - - fun close() { - isClosed = true - finishedTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(RandomFunction.random(35,50).toLong()) - updateBit() - } - - fun open(){ - isClosed = false - updateBit() - } - - fun takeItem(): Item?{ - if(items.isEmpty()) return null - val item = items[0] - items.remove(item) - if(items.isEmpty()){ - isFinished = false - finishedTime = 0L - isTomatoes = true - isSuperCompost = true - isClosed = false - } - updateBit() - if(isSuperCompost) ContentAPI.rewardXP(player, Skills.FARMING, 8.5) - else ContentAPI.rewardXP(player, Skills.FARMING, 4.5) - return Item(item) - } - - fun isReady(): Boolean { - return System.currentTimeMillis() > finishedTime && finishedTime != 0L - } - - fun checkSuperCompostItem(id: Int): Boolean{ - return when (id){ - Items.WATERMELON_5982, - Items.PINEAPPLE_2114, - Items.CALQUAT_FRUIT_5980, - Items.OAK_ROOTS_6043, - Items.WILLOW_ROOTS_6045, - Items.MAPLE_ROOTS_6047, - Items.YEW_ROOTS_6049, - Items.MAGIC_ROOTS_6051, - Items.COCONUT_5974, - Items.COCONUT_SHELL_5978, - Items.PAPAYA_FRUIT_5972, - Items.JANGERBERRIES_247, - Items.WHITE_BERRIES_239, - Items.POISON_IVY_BERRIES_6018, - Items.CLEAN_TOADFLAX_2998, - Items.CLEAN_AVANTOE_261, - Items.CLEAN_KWUARM_263, - Items.CLEAN_CADANTINE_265, - Items.CLEAN_DWARF_WEED_267, - Items.CLEAN_TORSTOL_269, - Items.CLEAN_LANTADYME_2481, - Items.CLEAN_SNAPDRAGON_3000, - Items.GRIMY_TOADFLAX_3049, - Items.GRIMY_KWUARM_213, - Items.GRIMY_AVANTOE_211, - Items.GRIMY_TORSTOL_219, - Items.GRIMY_DWARF_WEED_217, - Items.GRIMY_LANTADYME_2485, - Items.GRIMY_SNAPDRAGON_3051, - Items.GRIMY_CADANTINE_215 -> true - else -> false - } - } - - fun addItem(item: Int){ - if(!isFull()) { - items.add(item) - if (!checkSuperCompostItem(item)) { - isSuperCompost = false - } - if(item != Items.TOMATO_1982) isTomatoes = false - } - updateBit() - } - - fun addItem(item: Item){ - val remaining = 15 - items.size - val amount = if(item.amount > remaining){ - remaining - } else item.amount - for(i in 0 until amount) { - 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) - } else { - player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest, items.size) - if (isTomatoes) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 7, 1) - } - if (isClosed) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 6, 1) - } - SystemLogger.logAlert("Sending " + player.varpManager.get(bin.varpIndex).getValue()) - player.varpManager.get(bin.varpIndex).send(player) - } - - fun save(root: JSONObject){ - val binObject = JSONObject() - binObject.put("isSuper",this.isSuperCompost) - val items = JSONArray() - for(id in this.items){ - items.add(id) - } - binObject.put("items",items) - binObject.put("finishTime",finishedTime) - binObject.put("isTomato",isTomatoes) - binObject.put("isClosed",isClosed) - binObject.put("isFinished",isFinished) - root.put("binData",binObject) - } - - fun parse(_data: JSONObject){ - val isSuper = if(_data.containsKey("isSuper")) (_data["isSuper"] as Boolean) else true - if(_data.containsKey("items")){ - (_data["items"] as JSONArray).forEach { - addItem(it.toString().toInt()) - } - } - if(_data.containsKey("finishTime")) finishedTime = _data["finishTime"].toString().toLong() - if(_data.containsKey("isTomato")) isTomatoes = _data["isTomato"] as Boolean - if(_data.containsKey("isClosed")) isClosed = _data["isClosed"] as Boolean - if(_data.containsKey("isFinished")) isFinished = _data["isFinished"] as Boolean - updateBit() - } - - fun finish(){ - if(isTomatoes) items = items.map { Items.ROTTEN_TOMATO_2518 } as ArrayList - else if(isSuperCompost) items = items.map { Items.SUPERCOMPOST_6034 } as ArrayList - else items = items.map { Items.COMPOST_6032 } as ArrayList - isFinished = true - } - - fun convert(){ - if(!isSuperCompost){ - items = items.map { Items.SUPERCOMPOST_6034 } as ArrayList - isSuperCompost = true - updateBit() - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBinOptionHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBinOptionHandler.kt deleted file mode 100644 index 3523a64c2..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBinOptionHandler.kt +++ /dev/null @@ -1,49 +0,0 @@ -package rs09.game.node.entity.skill.farming - -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 - -@Initializable -class CompostBinOptionHandler : OptionHandler() { - override fun newInstance(arg: Any?): Plugin { - for(i in 7836..7839) - SceneryDefinition.forId(i).childrenIds.forEach { - val def = SceneryDefinition.forId(it) - def.handlers["option:open"] = this - def.handlers["option:close"] = this - def.handlers["option:take-tomato"] = this - } - return this - } - - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - player ?: return false - node ?: return false - 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() - "take-tomato" -> { - if (!bin.isTomatoes || !bin.isFinished) { - player.sendMessage("This shouldn't be happening. Report this.") - } else { - if(player.inventory.isFull){ - player.sendMessage("You don't have enough inventory space to do this.") - } else { - val reward = bin.takeItem() - if (reward != null) - player.inventory.add(reward) - } - } - } - } - return true - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBins.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBins.kt deleted file mode 100644 index e6d0f6a48..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostBins.kt +++ /dev/null @@ -1,40 +0,0 @@ -package rs09.game.node.entity.skill.farming - -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 rs09.game.node.entity.state.newsys.states.FarmingState - -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); - - companion object { - @JvmField - val bins = values().map { (it.varpIndex shl it.varpOffest) to it }.toMap() - - @JvmStatic - fun forObject(obj: Scenery): CompostBins?{ - return forObjectID(obj.id) - } - - @JvmStatic - fun forObjectID(id: Int): CompostBins?{ - val objDef = SceneryDefinition.forId(id) - val def = VarbitDefinition.forObjectID(objDef.varbitID) - return bins[def.configId shl def.bitShift] - } - } - - 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) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostType.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostType.kt deleted file mode 100644 index 1658735dc..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CompostType.kt +++ /dev/null @@ -1,7 +0,0 @@ -package rs09.game.node.entity.skill.farming - -enum class CompostType { - NONE, - NORMAL, - SUPER -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CropHarvester.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CropHarvester.kt deleted file mode 100644 index 58652ab18..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/CropHarvester.kt +++ /dev/null @@ -1,114 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import api.ContentAPI -import core.cache.def.impl.SceneryDefinition -import core.game.interaction.OptionHandler -import core.game.node.Node -import core.game.node.entity.npc.familiar.GiantEntNPC -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 core.plugin.Initializable -import core.plugin.Plugin -import core.tools.RandomFunction -import org.rs09.consts.Items - -val livesBased = arrayOf(PatchType.HERB, PatchType.CACTUS, PatchType.BELLADONNA, PatchType.HOPS, PatchType.ALLOTMENT,PatchType.EVIL_TURNIP) - -val spadeAnim = Animation(830) - -@Initializable -class CropHarvester : OptionHandler() { - - override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.setOptionHandler("harvest",this) - SceneryDefinition.setOptionHandler("pick",this) - return this - } - - companion object { - @JvmStatic - fun harvestPulse(player: Player?, node: Node?, crop: Int): Pulse? { - player ?: return null - node ?: return null - val fPatch = FarmingPatch.forObject(node.asScenery()) - fPatch ?: return null - val patch = fPatch.getPatchFor(player) - val plantable = patch.plantable - plantable ?: return null - - return object : Pulse(0) { - override fun pulse(): Boolean { - var reward = Item(crop, 1) - - 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.") - return true - } - var requiredItem = when(fPatch.type){ - PatchType.HERB -> Items.SECATEURS_5329 - else -> Items.SPADE_952 - } - if(requiredItem == Items.SECATEURS_5329){ - if(player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){ - 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) - else -> Animation(0) - } - if(!player.inventory.containsItem(Item(requiredItem))){ - player.sendMessage("You lack the needed tool to harvest these crops.") - return true - } - player.animator.animate(anim) - delay = 2 - player.inventory.add(reward) - player.skills.addExperience(Skills.FARMING,plantable.harvestXP) - if(patch.patch.type in livesBased){ - patch.rollLivesDecrement( - ContentAPI.getDynLevel(player, Skills.FARMING), - requiredItem == Items.MAGIC_SECATEURS_7409 - ) - } else { - patch.harvestAmt-- - if(patch.harvestAmt <= 0 && crop == plantable.harvestItem){ - patch.clear() - } - } - return patch.cropLives <= 0 || patch.harvestAmt <= 0 - } - } - } - } - - 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(patch.isWeedy()){ - player.sendMessage("Something seems to have gone wrong here. Report this.") - return true - } - - val pulse = harvestPulse(player, node, plantable.harvestItem) ?: return false - player.pulseManager.run(pulse) - - return true - } - -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/DigUpPatchDialogue.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/DigUpPatchDialogue.kt deleted file mode 100644 index 9be3aed76..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/DigUpPatchDialogue.kt +++ /dev/null @@ -1,63 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import core.game.content.dialogue.DialoguePlugin -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.update.flag.context.Animation -import core.plugin.Initializable - -@Initializable -class DigUpPatchDialogue(player: Player? = null) : DialoguePlugin(player) { - var patch: Patch? = null - - 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.") - stage = 1000 - return true - } - player.dialogueInterpreter.sendOptions("Dig up this patch?","Yes","No") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> when(buttonId){ - 1 -> { - end() - player.animator.animate(Animation(830)) - 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) - } - } - } - patch?.clear() - return true - } - }) - } - else -> end() - } - - 1000 -> end() - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(67984003) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmerPayOptionDialogue.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmerPayOptionDialogue.kt deleted file mode 100644 index 0b9afcf98..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmerPayOptionDialogue.kt +++ /dev/null @@ -1,67 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import core.game.node.item.Item -import org.rs09.consts.Items -import rs09.game.content.dialogue.DialogueFile -import rs09.tools.END_DIALOGUE -import rs09.tools.START_DIALOGUE - -class FarmerPayOptionDialogue(val patch: Patch): 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.STRAWBERRIES5_5406 -> if(item?.amount == 1) "basket of strawberries" else "baskets of strawberries" - Items.BANANAS5_5416 -> if(item?.amount == 1) "basket of bananas" else "baskets of bananas" - else -> item?.name?.toLowerCase() - } - if(item == null) npc("Sorry, I won't protect that.").also { stage = END_DIALOGUE } - else{ - npc("I would like ${item?.amount} $protectionText","to protect that patch.") - stage++ - } - } - - 1 -> options("Sure!","No, thanks.").also { stage++ } - 2 -> { - if(player!!.inventory.containsItem(item)){ - player("Here you go.").also { stage = 10 } - } else { - item = Item(item!!.noteChange,item!!.amount) - if(player!!.inventory.containsItem(item)){ - player("Here you go.").also { stage = 10 } - } else { - player("I don't have that to give.").also { stage = 20 } - } - } - } - - 10 -> { - if(player!!.inventory.remove(item)){ - npc("Thank you! I'll keep an eye on this patch.").also { stage = END_DIALOGUE } - patch?.protectionPaid = true - } else { - npc("That stuff just... vanished....").also { stage = END_DIALOGUE } - } - } - - 20 -> { - npc("Come back when you do.") - stage = END_DIALOGUE - } - - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmerPayOptionHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmerPayOptionHandler.kt deleted file mode 100644 index 4fa8515ae..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmerPayOptionHandler.kt +++ /dev/null @@ -1,41 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import core.game.node.Node -import core.game.node.entity.player.Player -import rs09.game.interaction.InteractionListener - -class FarmerPayOptionHandler : InteractionListener() { - - override fun defineListeners() { - on(NPC,"pay","pay (north)","pay (north-west)"){player,node -> - return@on attemptPay(player,node,0) - } - - on(NPC,"pay (south)","pay (south-east)"){player,node -> - return@on attemptPay(player,node,1) - } - } - - 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()) - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmingPatch.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmingPatch.kt deleted file mode 100644 index 35844271a..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FarmingPatch.kt +++ /dev/null @@ -1,98 +0,0 @@ -package rs09.game.node.entity.skill.farming - -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 rs09.game.node.entity.state.newsys.states.FarmingState - -enum class FarmingPatch(val varpIndex: Int, val varpOffset: 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), - - //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), - - //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), - - //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), - - //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), - - //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), - - //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), - - //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), - - //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); - - - companion object { - @JvmField - val patches = FarmingPatch.values().map { (it.varpIndex shl it.varpOffset) to it }.toMap() - - @JvmStatic - fun forObject(obj: Scenery): FarmingPatch?{ - return forObjectID(obj.id) - } - - @JvmStatic - fun forObjectID(id: Int): FarmingPatch?{ - val objDef = SceneryDefinition.forId(id) - val def = VarbitDefinition.forObjectID(objDef.varbitID) - return patches[def.configId shl def.bitShift] - } - } - - 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) - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FruitAndBerryPicker.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FruitAndBerryPicker.kt deleted file mode 100644 index d2b2bc32c..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/FruitAndBerryPicker.kt +++ /dev/null @@ -1,82 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import api.ContentAPI -import core.cache.def.impl.SceneryDefinition -import core.game.interaction.OptionHandler -import core.game.node.Node -import core.game.node.entity.npc.familiar.GiantEntNPC -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 core.plugin.Initializable -import core.plugin.Plugin -import java.util.concurrent.TimeUnit - -@Initializable -class FruitAndBerryPicker : OptionHandler() { - override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.setOptionHandler("pick-coconut",this) - SceneryDefinition.setOptionHandler("pick-banana",this) - SceneryDefinition.setOptionHandler("pick-apple",this) - SceneryDefinition.setOptionHandler("pick-orange",this) - SceneryDefinition.setOptionHandler("pick-pineapple",this) - SceneryDefinition.setOptionHandler("pick-papaya",this) - SceneryDefinition.setOptionHandler("pick-leaf",this) - SceneryDefinition.setOptionHandler("pick-from",this) - SceneryDefinition.setOptionHandler("pick-fruit",this) - SceneryDefinition.setOptionHandler("pick-spine",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 - - val animation = Animation(2281) - - if(patch.getFruitOrBerryCount() <= 0){ - player.sendMessage("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.") - return true - } - - if(System.currentTimeMillis() - patch.nextGrowth > TimeUnit.MINUTES.toMillis(45)){ - patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(45) - } - - player.pulseManager.run(object : Pulse(animation.duration){ - override fun pulse(): Boolean { - val reward = Item(plantable.harvestItem, 1) - - val familiar = player.familiarManager.familiar - if(familiar != null && familiar is GiantEntNPC) { - familiar.modifyFarmingReward(fPatch, reward) - } - - player.animator.animate(animation) - ContentAPI.addItemOrDrop(player,reward.id,reward.amount) - player.skills.addExperience(Skills.FARMING,plantable.harvestXP) - patch.setCurrentState(patch.getCurrentState() - 1) - - return patch.getFruitOrBerryCount() == 0 - } - }) - - return true - } - -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/HealthChecker.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/HealthChecker.kt deleted file mode 100644 index 6c0c840b7..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/HealthChecker.kt +++ /dev/null @@ -1,54 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import core.cache.def.impl.SceneryDefinition -import core.game.interaction.OptionHandler -import core.game.node.Node -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import rs09.game.system.SystemLogger -import core.plugin.Initializable -import core.plugin.Plugin -import java.util.concurrent.TimeUnit - -@Initializable -class HealthChecker : OptionHandler(){ - override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.setOptionHandler("check-health",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 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.") - return true - } - - if(!patch.isCheckHealth) return true - - player.skills.addExperience(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("Unreachable patch type from when(type) switch in HealthChecker.kt line 36") - } - - if(type == PatchType.FRUIT_TREE){ - patch.nextGrowth = TimeUnit.MINUTES.toMillis(45) - } - - patch.update() - - return true - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/InspectionHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/InspectionHandler.kt deleted file mode 100644 index 9e77a6891..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/InspectionHandler.kt +++ /dev/null @@ -1,42 +0,0 @@ -package rs09.game.node.entity.skill.farming - -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 - -@Initializable -class InspectionHandler : OptionHandler() { - override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.setOptionHandler("inspect",this) - return this - } - - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - 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 "This patch could use some water." - } 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) - } - return true - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/LeprechaunNoter.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/LeprechaunNoter.kt deleted file mode 100644 index c7862fdb4..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/LeprechaunNoter.kt +++ /dev/null @@ -1,39 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import api.ContentAPI -import core.game.content.dialogue.DialoguePlugin -import core.game.content.dialogue.FacialExpression -import core.game.node.item.Item -import core.plugin.Initializable -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener - -class LeprechaunNoter : InteractionListener() { - - val CROPS = Plantable.values().map{ it.harvestItem }.toIntArray() - val LEPRECHAUNS = intArrayOf(NPCs.TOOL_LEPRECHAUN_3021,NPCs.GOTH_LEPRECHAUN_8000,NPCs.TOOL_LEPRECHAUN_4965,NPCs.TECLYN_2861) - - override fun defineListeners() { - onUseWith(NPC,CROPS,*LEPRECHAUNS){player, used, with -> - val usedItem = used.asItem() - val npc = with.asNpc() - val expr = when(npc.id){ - 3021 -> FacialExpression.OLD_NORMAL - else -> 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)) - } - ContentAPI.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 - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Patch.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Patch.kt deleted file mode 100644 index f186f6d3b..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Patch.kt +++ /dev/null @@ -1,331 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import core.game.node.entity.player.Player -import core.tools.RandomFunction -import org.rs09.consts.Items -import rs09.game.system.SystemLogger -import java.util.concurrent.TimeUnit -import kotlin.math.ceil -import kotlin.math.min - -class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantable?, var currentGrowthStage: Int, var isDiseased: Boolean, var isDead: Boolean, var isWatered: Boolean, var nextGrowth: Long, var harvestAmt: Int, var isCheckHealth: Boolean) { - constructor(player: Player, patch: FarmingPatch) : this(player,patch,null,0,false,false,false,0L,0,false) - - var diseaseMod = 0 - var compost = CompostType.NONE - var protectionPaid = false - var cropLives = 3 - - fun setNewHarvestAmount() { - val compostMod = when(compost) { - CompostType.NONE -> 0 - CompostType.NORMAL -> 1 - CompostType.SUPER -> 2 - } - harvestAmt = when (plantable) { - Plantable.LIMPWURT_SEED, Plantable.WOAD_SEED -> 3 - Plantable.MUSHROOM_SPORE -> 6 - Plantable.WILLOW_SAPLING -> 0 - else -> 1 - } - if(plantable != null && plantable?.applicablePatch != PatchType.FLOWER) { - harvestAmt += compostMod - } - cropLives = 3 + compostMod - } - - fun rollLivesDecrement(farmingLevel: Int, magicSecateurs: Boolean){ - if(patch.type == PatchType.HERB){ - //authentic formula thanks to released data. - var herbSaveLow = when(plantable){ - Plantable.GUAM_SEED -> min(24 + farmingLevel, 80) - Plantable.MARRENTILL_SEED -> min(28 + farmingLevel, 80) - Plantable.TARROMIN_SEED -> min(31 + farmingLevel, 80) - Plantable.HARRALANDER_SEED -> min(36 + farmingLevel, 80) - Plantable.GOUT_TUBER -> min(39 + farmingLevel, 80) - Plantable.RANARR_SEED -> min(39 + farmingLevel, 80) - Plantable.SPIRIT_WEED_SEED -> min(43 + farmingLevel, 80) - Plantable.TOADFLAX_SEED -> min(43 + farmingLevel, 80) - Plantable.IRIT_SEED -> min(46 + farmingLevel, 80) - Plantable.AVANTOE_SEED -> min(50 + farmingLevel, 80) - Plantable.KWUARM_SEED -> min(54 + farmingLevel, 80) - Plantable.SNAPDRAGON_SEED -> min(57 + farmingLevel, 80) - Plantable.CADANTINE_SEED -> min(60 + farmingLevel, 80) - Plantable.LANTADYME_SEED -> min(64 + farmingLevel, 80) - Plantable.DWARF_WEED_SEED -> min(67 + farmingLevel, 80) - Plantable.TORSTOL_SEED -> min(71 + farmingLevel, 80) - else -> -1 - } - - if(magicSecateurs) herbSaveLow = ceil(1.10 * herbSaveLow).toInt() - - val rand = RandomFunction.random(256) - - if(rand > herbSaveLow){ - cropLives -= 1 - } - } else { - //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 - else -> 0 // nothing should go here, but if it does, do not give extra crops amd decrement cropLives - } - - if(magicSecateurs) chance += ceil(1.10 * chance).toInt() //will increase average yield by roughly 3. - - if(RandomFunction.roll(chance)) cropLives -= 1 - } - - if(cropLives <= 0) clear() - } - - fun isWeedy(): Boolean { - return getCurrentState() in 0..3 - } - - fun getCurrentState(): Int{ - return player.varpManager.get(patch.varpIndex).getVarbitValue(patch.varpOffset) - } - - fun setCurrentState(state: Int){ - player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,state) - updateBit() - } - - fun isGrown(): Boolean{ - return currentGrowthStage == (plantable?.stages ?: 0) - } - - private 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("Invalid setting of isCheckHealth for patch type: " + patch.type.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.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.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.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.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.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.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) - } - else -> {} - } - } - player.varpManager.get(patch.varpIndex).send(player) - } - - fun cureDisease() { - 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() - } - - fun water() { - isWatered = true - updateBit() - } - - private fun getBushDiseaseValue(): Int{ - if(plantable == Plantable.POISON_IVY_SEED){ - return (plantable?.value ?: 0) + currentGrowthStage + 12 - } 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 - } - } - - private fun getFruitTreeDiseaseValue(): Int { - return (plantable?.value ?: 0) + currentGrowthStage + 12 - } - - private fun getFruitTreeDeathValue(): Int { - return (plantable?.value ?: 0) + currentGrowthStage + 18 - } - - private fun getBelladonnaDiseaseValue(): Int { - return (plantable?.value ?: 0) + currentGrowthStage + 4 - } - - private fun getBelladonnaDeathValue(): Int { - return (plantable?.value ?: 0) + currentGrowthStage + 7 - } - - private fun getCactusDiseaseValue(): Int { - return (plantable?.value ?: 0) + currentGrowthStage + 10 - } - - private fun getCactusDeathValue(): Int { - return (plantable?.value ?: 0) + currentGrowthStage + 16 - } - - private fun getHerbDiseaseValue(): Int { - return if (plantable?.value ?: -1 <= 103) { - 128 + (((plantable?.ordinal ?: 0) - Plantable.GUAM_SEED.ordinal) * 3) + currentGrowthStage - 1 - } else if (plantable == Plantable.SPIRIT_WEED_SEED) { - 211 + currentGrowthStage - 1 - } else { - 198 + currentGrowthStage -1 - } - } - - private fun getHerbDeathValue(): Int { - return if(plantable == Plantable.GOUT_TUBER){ - 201 + currentGrowthStage - 1 - } else 170 + currentGrowthStage - 1 - } - - private fun grow(){ - if(isWeedy() && getCurrentState() > 0) { - nextGrowth = System.currentTimeMillis() + 60000 - setCurrentState(getCurrentState() - 1) - currentGrowthStage-- - return - } - - if(isDiseased){ - isDead = true - return - } - - diseaseMod = when(compost){ - CompostType.NONE -> 0 - CompostType.NORMAL -> 8 - CompostType.SUPER -> 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.type == PatchType.FRUIT_TREE || patch.type == PatchType.TREE || patch.type == PatchType.BUSH || patch.type == PatchType.CACTUS) && 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)){ - setCurrentState(getCurrentState() + 1) - } - } - if(patch.type == PatchType.TREE) { - // Willow branches - if(harvestAmt < 6) { - harvestAmt++; - } - } - - if(plantable?.stages ?: 0 > currentGrowthStage && !isGrown()){ - currentGrowthStage++ - setCurrentState(getCurrentState() + 1) - isWatered = false - } - - regrowIfTreeStump() - } - - fun regrowIfTreeStump() { - if(patch.type == PatchType.TREE && 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) - isWatered = false - } - } - } - - fun update(){ - grow() - updateBit() - } - - fun plant(plantable: Plantable){ - nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(plantable.applicablePatch.stageGrowthTime.toLong()) - this.plantable = plantable - isDead = false - isDiseased = false - currentGrowthStage = 0 - setCurrentState(plantable.value) - } - - fun clear(){ - isCheckHealth = false - isDiseased = false - isDead = false - plantable = null - player.varpManager.get(patch.varpIndex).clearBitRange(patch.varpOffset,patch.varpOffset + 7) - nextGrowth = 0L - currentGrowthStage = 3 - setCurrentState(3) - compost = CompostType.NONE - protectionPaid = false - } - - fun getFruitOrBerryCount() : Int { - plantable ?: return 0 - return getCurrentState() - plantable!!.value - plantable!!.stages - } - - fun isFlowerProtected(): Boolean{ - if(patch.type != PatchType.ALLOTMENT) return false - - val fpatch = when(patch){ - FarmingPatch.S_FALADOR_ALLOTMENT_SE,FarmingPatch.S_FALADOR_ALLOTMENT_NW -> FarmingPatch.S_FALADOR_FLOWER_C - FarmingPatch.ARDOUGNE_ALLOTMENT_S,FarmingPatch.ARDOUGNE_ALLOTMENT_N -> FarmingPatch.ARDOUGNE_FLOWER_C - 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) - - return (fpatch.plantable != null && - (fpatch.plantable == plantable?.protectionFlower || fpatch.plantable == Plantable.forItemID(Items.WHITE_LILY_SEED_14589)) - && fpatch.isGrown()) - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/PatchRaker.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/PatchRaker.kt deleted file mode 100644 index 3abb24403..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/PatchRaker.kt +++ /dev/null @@ -1,33 +0,0 @@ -package rs09.game.node.entity.skill.farming - -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 - -object PatchRaker { - val RAKE_ANIM = Animation(2273) - - @JvmStatic - fun rake(player: Player, patch: FarmingPatch){ - player.pulseManager.run(object : Pulse(){ - override fun pulse(): Boolean { - var patchStage = patch.getPatchFor(player).getCurrentState() - if(patchStage <= 2){ - player.animator.animate(RAKE_ANIM) - } - 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) - } - return patchStage >= 3 - } - }) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/PatchType.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/PatchType.kt deleted file mode 100644 index 753de795e..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/PatchType.kt +++ /dev/null @@ -1,16 +0,0 @@ -package rs09.game.node.entity.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) -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Plantable.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Plantable.kt deleted file mode 100644 index 4aa16095b..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Plantable.kt +++ /dev/null @@ -1,102 +0,0 @@ -package rs09.game.node.entity.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) { - - //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), - - //Flower(Technically) - SCARECROW(6059,33,3,0.0,0.0,0.0,23,PatchType.FLOWER,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), - - //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)), - - //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), - MAGIC_SAPLING(5374,48,12,145.5,0.0,13768.3,75,PatchType.TREE,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)), - - //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), - - //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), - - //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) - ; - - 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) - companion object { - @JvmField - val plantables = values().map { it.itemID to it }.toMap() - - @JvmStatic - fun forItemID(id: Int): Plantable?{ - return plantables[id] - } - - @JvmStatic - fun forItem(item: Item): Plantable?{ - return forItemID(item.id) - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedOnPlantPot.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedOnPlantPot.kt deleted file mode 100644 index dfd02095a..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedOnPlantPot.kt +++ /dev/null @@ -1,50 +0,0 @@ -package rs09.game.node.entity.skill.farming - -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.Items - -@Initializable -class SeedOnPlantPot : UseWithHandler(Items.ACORN_5312, - Items.WILLOW_SEED_5313,Items.MAPLE_SEED_5314,Items.YEW_SEED_5315,Items.MAGIC_SEED_5316,Items.APPLE_TREE_SEED_5283,Items.BANANA_TREE_SEED_5284,Items.ORANGE_TREE_SEED_5285,Items.CURRY_TREE_SEED_5286,Items.PINEAPPLE_SEED_5287,Items.PAPAYA_TREE_SEED_5288,Items.PALM_TREE_SEED_5289) { - override fun newInstance(arg: Any?): Plugin { - addHandler(Items.PLANT_POT_5354, ITEM_TYPE,this) - return this - } - - override fun handle(event: NodeUsageEvent?): Boolean { - val player = event?.player ?: return false - val pot = event.usedItem ?: return false - val seed = event.usedWith.asItem() ?: return false - - if(!player.inventory.contains(Items.GARDENING_TROWEL_5325,1)){ - player.dialogueInterpreter.sendDialogue("You need a gardening trowel on you to do this.") - return false - } - - val seedling = when(seed.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 - else -> return false - } - - if(player.inventory.remove(pot) && player.inventory.remove(Item(seed.id,1))){ - player.inventory.add(Item(seedling)) - } - - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Seedling.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Seedling.kt deleted file mode 100644 index ae9c98994..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Seedling.kt +++ /dev/null @@ -1,5 +0,0 @@ -package rs09.game.node.entity.skill.farming - -class Seedling(val id: Int, val TTL: Long, val sapling: Int) { - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedlingWaterer.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedlingWaterer.kt deleted file mode 100644 index 1630ffcd0..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/SeedlingWaterer.kt +++ /dev/null @@ -1,54 +0,0 @@ -package rs09.game.node.entity.skill.farming - -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.Items -import rs09.game.node.entity.state.newsys.states.SeedlingState - -private val cans = arrayListOf(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) -private val seedlings = arrayListOf(Items.OAK_SEEDLING_5358,Items.WILLOW_SEEDLING_5359,Items.MAPLE_SEEDLING_5360,Items.YEW_SEEDLING_5361,Items.MAGIC_SEEDLING_5362,Items.APPLE_SEEDLING_5480,Items.BANANA_SEEDLING_5481,Items.ORANGE_SEEDLING_5482,Items.CURRY_SEEDLING_5483,Items.PINEAPPLE_SEEDLING_5484,Items.PAPAYA_SEEDLING_5485,Items.PALM_SEEDLING_5486) - -@Initializable -class SeedlingWaterer : UseWithHandler(*cans.toIntArray()) { - - override fun newInstance(arg: Any?): Plugin { - for(seed in seedlings) addHandler(seed, ITEM_TYPE,this) - return this - } - - override fun handle(event: NodeUsageEvent?): Boolean { - val player = event?.player ?: return false - val seedling = event.used.asItem() ?: return false - val can = event.usedWith.asItem() ?: return false - - val nextCan = can.id.getNext() - val wateredSeedling = if(seedling.id > 5400 ) seedling.id + 8 else seedling.id + 6 - - if(player.inventory.remove(can) && player.inventory.remove(seedling)){ - player.inventory.add(Item(wateredSeedling)) - player.inventory.add(Item(nextCan)) - - var state = player.states["seedling"] as SeedlingState? - if(state == null){ - state = player.registerState("seedling") as SeedlingState? - state?.addSeedling(wateredSeedling) - state?.init() - } else { - state.addSeedling(wateredSeedling) - } - - player.sendMessage("You water the seedling.") - } - return true - } - - private fun Int.getNext(): Int{ - var index = cans.indexOf(this) - if(index == -1) return Items.WATERING_CAN_5331 - if(index != cans.size -1) return cans[index + 1] else return Items.WATERING_CAN_5331 - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/ToolLeprechaunInterface.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/ToolLeprechaunInterface.kt deleted file mode 100644 index 29639a985..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/ToolLeprechaunInterface.kt +++ /dev/null @@ -1,356 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import api.ContentAPI -import core.game.component.Component -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.RunScript -import core.game.node.item.Item -import org.rs09.consts.Components -import org.rs09.consts.Items -import rs09.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 defineListeners() { - - onOpen(FARMING_TOOLS){player, component -> - player.varpManager?.flagSave(varp) - player.interfaceManager.openSingleTab(Component(TOOLS_SIDE)) - return@onOpen true - } - - onClose(FARMING_TOOLS){player, _ -> - player.interfaceManager.closeSingleTab() - } - - 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) - } - 37 -> { - if(!hasWateringCan(player)){ - player.dialogueInterpreter.sendDialogue("You don't have any of those stored.") - } else { - player.inventory.add(Item(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) - } - 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) - 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)) - setHasSecateurs(player,true) - setHasMagicSecateurs(player,true) - } else { - player.inventory.remove(Item(Items.SECATEURS_5329)) - setHasSecateurs(player,true) - setHasMagicSecateurs(player,false) - } - } else { - player.dialogueInterpreter.sendDialogue("You already have one of those stored.") - } - } - 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) - setWateringCan(player,can) - } else { - player.dialogueInterpreter.sendDialogue("You already have one of those stored.") - } - } - 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){ - player ?: return - if(!checkMethod.invoke(player)){ - player.dialogueInterpreter.sendDialogue("You don't have any of those stored.") - } else { - if(!player.inventory.hasSpaceFor(Item(item))){ - player.dialogueInterpreter.sendDialogue("You don't have enough space for that.") - return - } - withdrawMethod.invoke(player,false) - player.inventory.add(Item(item)) - } - } - - 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(!checkMethod.invoke(player)){ - depositMethod.invoke(player,true) - player.inventory.remove(Item(item)) - } else { - player.dialogueInterpreter.sendDialogue("You already have one of those stored.") - } - } - - private fun doStackedDeposit(player: Player?, item: Int, amount: Int, updateQuantityMethod: (Player?, Int) -> Unit, quantityCheckMethod: (Player?) -> Int){ - player ?: return - val hasAmount = player.inventory.getAmount(item) - var finalAmount = amount - 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.") - return - } - - if(amount == -2){ - ContentAPI.sendInputDialogue(player, true, "Enter the amount:"){value -> - var amt = value as Int - if(amt > hasAmount){ - amt = hasAmount - } - 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) - } - return - } - if(amount == -1){ - finalAmount = hasAmount - if(finalAmount > spaceLeft){ - finalAmount = spaceLeft - } - } - - 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.") - return - } - - if(finalAmount > spaceLeft){ - player.dialogueInterpreter.sendDialogue("You can't store any more of those.") - return - } - - player.inventory.remove(Item(item,finalAmount)) - updateQuantityMethod.invoke(player,finalAmount) - } - - 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){ - ContentAPI.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) - } - } - - fun getAmount(opcode: Int): Int{ - return when(opcode) { - 155 -> 1 - 196 -> 5 - 124 -> -1 - 199 -> -2 - else -> 0 - } - } - - private fun hasRake(player: Player?): Boolean{ - return checkBit(player,0) - } - - private fun setHasRake(player: Player?,hasRake: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(0,if(hasRake) 1 else 0) - } - - private fun hasDibber(player: Player?): Boolean{ - return checkBit(player,1) - } - - private fun setHasDibber(player: Player?,hasDibber: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(1,if(hasDibber) 1 else 0) - } - - private fun hasSpade(player: Player?): Boolean{ - return checkBit(player,2) - } - - private fun setHasSpade(player: Player?,hasSpade: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(2,if(hasSpade) 1 else 0) - } - - private fun hasSecateurs(player: Player?): Boolean{ - return checkBit(player,3) - } - - private fun setHasSecateurs(player: Player?,hasSecateurs: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(3,if(hasSecateurs) 1 else 0) - } - - private fun hasWateringCan(player: Player?): Boolean{ - return checkBitRange(player,4,7) > 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 - return Items.WATERING_CAN_5331 + can - } - - 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 - Items.WATERING_CAN3_5335 -> 4 - Items.WATERING_CAN4_5336 -> 5 - Items.WATERING_CAN5_5337 -> 6 - Items.WATERING_CAN6_5338 -> 7 - Items.WATERING_CAN7_5339 -> 8 - Items.WATERING_CAN8_5340 -> 9 - else -> 0 - } - player?.varpManager?.get(varp)?.setVarbit(4,can) - } - - private fun setNoWateringCan(player: Player?){ - player?.varpManager?.get(varp)?.setVarbit(4,0) - } - - private fun hasGardeningTrowel(player: Player?): Boolean { - return checkBit(player,8) - } - - private fun setHasGardeningTrowel(player: Player?,hasTrowel: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(8,if(hasTrowel) 1 else 0) - } - - private fun getNumBuckets(player: Player?): Int { - return checkBitRange(player,9,13) - } - - private fun updateBuckets(player: Player?,amount: Int){ - player?.varpManager?.get(varp)?.setVarbit(9,getNumBuckets(player) + amount) - } - - private fun getNumCompost(player: Player?): Int { - return checkBitRange(player,14,21) - } - - private fun updateCompost(player: Player?,amount: Int){ - player?.varpManager?.get(varp)?.setVarbit(14,getNumCompost(player) + amount) - } - - private fun getNumSuperCompost(player: Player?): Int { - return checkBitRange(player,22,29) - } - - private fun updateSuperCompost(player: Player?,amount: Int){ - player?.varpManager?.get(varp)?.setVarbit(22,getNumSuperCompost(player) + amount) - } - - private fun hasMagicSecateurs(player: Player?): Boolean { - return checkBit(player,30) - } - - private fun setHasMagicSecateurs(player: Player?,hasMagic: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(30,if(hasMagic) 1 else 0) - } - - 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?{ - 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 - } - } - return if(highestCan.id == 0) null else highestCan - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithBinHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithBinHandler.kt deleted file mode 100644 index c45cdc3b5..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithBinHandler.kt +++ /dev/null @@ -1,103 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import core.game.interaction.NodeUsageEvent -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 - -object UseWithBinHandler { - @JvmField - val allowedNodes = ArrayList(100) - val fillAnim = Animation(832) - val compostPotionAnimation = Animation(2259) - val scoopAnimation = Animation(8905) - - init { - loadNodes() - } - - fun loadNodes() { - for(p in Plantable.values()){ - if(p.harvestItem != Items.SCARECROW_6059) { - allowedNodes.add(p.harvestItem) - } - } - allowedNodes.add(Items.COCONUT_SHELL_5978) - allowedNodes.add(Items.WEEDS_6055) - allowedNodes.add(Items.COMPOST_POTION4_6470) - allowedNodes.add(Items.COMPOST_POTION3_6472) - allowedNodes.add(Items.COMPOST_POTION2_6474) - allowedNodes.add(Items.COMPOST_POTION1_6476) - allowedNodes.add(Items.BUCKET_1925) - } - - @JvmStatic - fun handle(event: NodeUsageEvent?): Boolean { - event ?: return false - val player = event.player - val used = event.used.id - val cBin = CompostBins.forObject(event.usedWith.asScenery()) ?: return false - val bin = cBin.getBinForPlayer(player) - - 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){ - override fun pulse(): Boolean { - if(player.inventory.remove(event.usedItem)) { - bin.convert() - player.inventory.add(Item(used.getNext())) - } - return true - } - }) - } else { - player.dialogueInterpreter.sendDialogue("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){ - override fun pulse(): Boolean { - if(!player.inventory.containsItem(event.usedItem)) return true - player.animator.animate(scoopAnimation) - val item = bin.takeItem() - if(item != null && player.inventory.remove(event.usedItem)){ - player.inventory.add(item) - } - return item == null || !player.inventory.containsItem(event.usedItem) - } - }) - } else { - player.dialogueInterpreter.sendDialogue("You can only scoop an opened bin of finished compost.") - } - } - - else -> - if(bin.isFull()){ - player.sendMessage("This compost bin is already full.") - return true - } else { - player.pulseManager.run(object : Pulse(fillAnim.duration){ - override fun pulse(): Boolean { - player.animator.animate(fillAnim) - if(player.inventory.remove(event.usedItem)){ - bin.addItem(event.usedItem) - } - return bin.isFull() || player.inventory.getAmount(event.usedItem) == 0 - } - }) - } - } - - return true - } - - 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/kotlin/rs09/game/node/entity/skill/farming/UseWithPatchHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithPatchHandler.kt deleted file mode 100644 index 872ad0001..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/UseWithPatchHandler.kt +++ /dev/null @@ -1,233 +0,0 @@ -package rs09.game.node.entity.skill.farming - -import api.ContentAPI -import core.game.interaction.NodeUsageEvent -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 rs09.game.node.entity.skill.farming.CropHarvester - -object UseWithPatchHandler{ - val RAKE = Items.RAKE_5341 - val SEED_DIBBER = Items.SEED_DIBBER_5343 - val SPADE = Items.SPADE_952 - val SECATEURS = Items.SECATEURS_5329 - val TROWEL = Items.GARDENING_TROWEL_5325 - val pourBucketAnim = Animation(2283) - val wateringCanAnim = Animation(2293) - val plantCureAnim = Animation(2288) - val secateursAnim = Animation(7227) - - @JvmField - val allowedNodes = ArrayList() - - init { - loadNodes() - } - - @JvmStatic - 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)) - } - - @JvmStatic - fun handle(event: NodeUsageEvent?): Boolean { - event ?: return false - val player = event.player - val usedItem = event.usedItem - val patch = FarmingPatch.forObject(event.usedWith.asScenery()) - patch ?: return false - - player.faceLocation(event.usedWith.location) - 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 -> { - val p = patch.getPatchFor(player) - if(patch.type == PatchType.TREE) { - if(p.isDiseased && !p.isDead) { - player.pulseManager.run(object: Pulse(){ - override fun pulse(): Boolean { - player.animator.animate(secateursAnim) - p.cureDisease() - return true - } - }) - } else if(p.plantable == Plantable.WILLOW_SAPLING && p.harvestAmt > 0) { - val pulse = CropHarvester.harvestPulse(player, event.usedWith, Items.WILLOW_BRANCH_5933) ?: return false - player.pulseManager.run(pulse) - } - } - } - TROWEL -> { - val p = patch.getPatchFor(player) - if(!p.isWeedy()){ - player.sendMessage("This patch has something growing in it.") - return true - } else if (p.currentGrowthStage != 3){ - player.sendMessage("I should clear this of weeds before trying to take some dirt.") - return true - } - - val potAmount = player.inventory.getAmount(Items.PLANT_POT_5356) - - if(potAmount == 0){ - player.sendMessage("You have no plant pots to fill.") - return true - } - - val anim = Animation(2272) - - player.pulseManager.run(object : Pulse(anim.duration){ - override fun pulse(): Boolean { - if(player.inventory.remove(Item(Items.PLANT_POT_5356))){ - player.animator.animate(anim) - player.inventory.add(Item(Items.PLANT_POT_5354)) - } else return true - return false - } - }) - } - - 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) - if(player.inventory.remove(event.usedItem)){ - player.inventory.add(Item(Items.VIAL_229)) - p.cureDisease() - } - return true - } - }) - } else { - player.sendMessage("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 -> { - 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(){ - override fun pulse(): Boolean { - if(p.isWeedy()){ - player.sendMessage("You should grow something first.") - return true - } - player.animator.animate(wateringCanAnim) - if(player.inventory.remove(event.usedItem)){ - player.inventory.add(Item(usedItem.id.getNext())) - p.water() - } - return true - } - }) - } - } - - Items.SUPERCOMPOST_6034, Items.COMPOST_6032 -> { - val p = patch.getPatchFor(player) - if(p.compost == CompostType.NONE) { - player.animator.animate(pourBucketAnim) - player.pulseManager.run(object : Pulse(){ - override fun pulse(): Boolean { - if(player.inventory.remove(event.usedItem,false)){ - p.compost = if(usedItem.id == Items.SUPERCOMPOST_6034) CompostType.SUPER else CompostType.NORMAL - if(p.compost == CompostType.SUPER) ContentAPI.rewardXP(player, Skills.FARMING, 26.0) else ContentAPI.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)) - } - return true - } - }) - } else { - player.sendMessage("This patch has already been treated with compost.") - } - } - - else -> { - val plantable = Plantable.forItemID(usedItem.id) - if(plantable == null) return false - - if(plantable.applicablePatch != patch.type){ - player.sendMessage("You can't plant that seed in this patch.") - return true - } - - if(plantable.requiredLevel > player.skills.getLevel(Skills.FARMING)){ - player.sendMessage("You need a Farming level of ${plantable.requiredLevel} to plant this.") - return 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.") - return true - } else if(p.getCurrentState() > 3){ - player.sendMessage("There is already something growing in this patch.") - return 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) - } - - if(patch.type == PatchType.ALLOTMENT){ - if(!player.inventory.containsItem(plantItem)){ - player.sendMessage("You need 3 seeds to plant an allotment patch.") - return 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 true - } - } else { - if(!player.inventory.contains(Items.SPADE_952,1)){ - player.sendMessage("You need a spade to plant that.") - return true - } - } - player.lock() - if(player.inventory.remove(plantItem)) { - player.animator.animate(Animation(2291)) - player.pulseManager.run(object : Pulse(3) { - override fun pulse(): Boolean { - 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_5356)) - } - player.unlock() - return true - } - }) - } - } - } - - return true - } - - private fun Int.getNext(): Int { - if(this == Items.WATERING_CAN1_5333) return Items.WATERING_CAN_5331 - else return this - 1 - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/fletching/FletchingListeners.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/fletching/FletchingListeners.kt deleted file mode 100644 index 97dde77f3..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/fletching/FletchingListeners.kt +++ /dev/null @@ -1,140 +0,0 @@ -package rs09.game.node.entity.skill.fletching - -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.fletching.Fletching -import core.game.node.entity.skill.fletching.items.arrow.ArrowHeadPulse -import core.game.node.entity.skill.fletching.items.arrow.HeadlessArrowPulse -import core.game.node.entity.skill.fletching.items.bow.StringPulse -import core.game.node.entity.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 rs09.game.content.dialogue.SkillDialogueHandler -import rs09.game.interaction.InteractionListener - -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(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(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(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(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(bolt.asItem(),tip.asItem())){ - player.inventory.add(Item(MITH_GRAPPLE)) - } - return@onUseWith true - } - - onUseWith(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(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/kotlin/rs09/game/node/entity/skill/fletching/items/gem/GemBoltCutPulse.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/fletching/items/gem/GemBoltCutPulse.kt deleted file mode 100644 index a4f0b69e9..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/fletching/items/gem/GemBoltCutPulse.kt +++ /dev/null @@ -1,68 +0,0 @@ -package rs09.game.node.entity.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 core.game.node.entity.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/kotlin/rs09/game/node/entity/skill/gather/GatheringSkillOptionListeners.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/GatheringSkillOptionListeners.kt deleted file mode 100644 index efc3903cb..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/GatheringSkillOptionListeners.kt +++ /dev/null @@ -1,48 +0,0 @@ -package rs09.game.node.entity.skill.gather - -import core.game.node.Node -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import rs09.game.node.entity.skill.gather.fishing.FishingPulse -import core.game.node.entity.skill.fishing.FishingSpot -import core.game.node.entity.skill.gather.woodcutting.WoodcuttingSkillPulse -import org.rs09.consts.NPCs -import rs09.game.content.dialogue.KjallakOnChopDialogue -import rs09.game.interaction.InteractionListener -import rs09.game.node.entity.skill.gather.mining.MiningSkillPulse - -class GatheringSkillOptionListeners : InteractionListener() { - - val ETCETERIA_REGION = 10300 - - override fun defineListeners() { - on(SCENERY,"chop-down","chop down","cut down"){ 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(SCENERY,"mine"){ player, node -> - player.pulseManager.run(MiningSkillPulse(player, node.asScenery())) - return@on true - } - - on(NPC,"net"){player,node -> return@on fish(player,node,"net")} - on(NPC,"lure"){player,node -> return@on fish(player,node,"lure")} - on(NPC,"bait"){player,node -> return@on fish(player,node,"bait")} - on(NPC,"harpoon"){player,node -> return@on fish(player,node,"harpoon")} - on(NPC,"cage"){player,node -> return@on fish(player,node,"cage")} - on(NPC,"fish"){player, node -> return@on fish(player,node,"fish") } - } - - fun fish(player: Player, node: Node, opt: String): Boolean{ - val npc = node as NPC - val spot = FishingSpot.forId(npc.id) ?: return false - val op = spot.getOptionByName(opt) ?: return false - player.pulseManager.run(FishingPulse(player, npc, op)) - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/FishingPulse.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/FishingPulse.kt deleted file mode 100644 index 8d3bfb1e7..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/FishingPulse.kt +++ /dev/null @@ -1,448 +0,0 @@ -package rs09.game.node.entity.skill.gather.fishing - -import core.game.content.global.SkillingPets -import core.game.content.quest.tutorials.tutorialisland.TutorialSession -import core.game.content.quest.tutorials.tutorialisland.TutorialStage -import core.game.container.Container -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.player.link.skillertasks.SkillTasks -import core.game.node.entity.skill.SkillPulse -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.fishing.Fish -import core.game.node.entity.skill.fishing.FishingOption -import core.game.node.entity.skill.summoning.familiar.Forager -import core.game.node.item.Item -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.Animation -import core.tools.RandomFunction -import rs09.game.node.entity.player.info.stats.STATS_BASE -import rs09.game.node.entity.player.info.stats.STATS_FISH -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks.Companion.isActive -import rs09.game.world.GameWorld.Pulser -import rs09.tools.stringtools.colorize - -/** - * Handles a fishing pulse. - * - * @author Ceikry - */ -class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption?) : SkillPulse(player, npc) { - /** - * Represents the fish type. - */ - private var fish: Fish? = null - - /** - * Represents the base location the npc was at. - */ - private val location: Location = npc.location - - override fun start() { - if (TutorialSession.getExtension(player).stage == 12) { - TutorialStage.load(player, 13, false) - } - if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar is Forager) { - val forager = player.familiarManager.familiar as Forager - val dest = player.location.transform(player.direction) - Pathfinder.find(forager.location, dest).walk(forager) - } - super.start() - } - - override fun checkRequirements(): Boolean { - 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.") - stop() - return false - } - if (!option.hasBait(player.inventory)) { - player.dialogueInterpreter.sendDialogue("You don't have any " + option.getBaitName().toLowerCase() + "s left.") - stop() - return false - } - if (player.skills.getLevel(Skills.FISHING) < fish!!.level) { - player.dialogueInterpreter.sendDialogue("You need a fishing level of " + fish!!.level + " to catch " + (if (fish == Fish.SHRIMP || fish == Fish.ANCHOVIE) "" else "a") + " " + fish!!.item.name.toLowerCase() + ".".trim { it <= ' ' }) - stop() - return false - } - if (player.inventory.freeSlots() == 0) { - player.dialogueInterpreter.sendDialogue("You don't have enough space in your inventory.") - stop() - return false - } - if (location !== node!!.location || !node!!.isActive || node!!.isInvisible) { - stop() - return false - } - return true - } - - override fun animate() { - if (isBareHanded(player)) { - player.animate(Animation(6709)) - Pulser.submit(object : Pulse(1) { - var counter = 0 - override fun pulse(): Boolean { - when (counter++) { - 5 -> getCatchAnimationAndLoot(player) - } - return false - } - }) - } else { - player.animate(option!!.animation) - } - } - - override fun reward(): Boolean { - if (delay == 1) { - super.setDelay(5) - return false - } - if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar is Forager) { - val forager = player.familiarManager.familiar as Forager - forager.handlePassiveAction() - } - if (success()) { - if (player.inventory.hasSpaceFor(fish!!.item) && option!!.removeBait(player.inventory)) { - if (player.skillTasks.hasTask()) { - updateSkillTask() - } - updateDiary() - SkillingPets.checkPetDrop(player, SkillingPets.HERON) - val item = fish!!.item - if (isActive(SkillcapePerks.GREAT_AIM, player) && RandomFunction.random(100) <= 5) { - player.inventory.add(item) - player.sendMessage(colorize("%RYour expert aim catches you a second fish.")) - } - player.inventory.add(item) - 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) - message(2) - if (TutorialSession.getExtension(player).stage == 13) { - TutorialStage.load(player, 14, false) - stop() - return true - } - fish = option!!.getRandomFish(player) - } - } - return player.inventory.freeSlots() == 0 - } - - fun updateDiary() { - when (fish) { - Fish.MACKEREL -> { - if (player.viewport.region.id == 11317) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 11) - } - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 1, 11) - ) { - player.setAttribute("/save:diary:seers:bass-caught", true) - } - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 7) - ) { - player.setAttribute( - "/save:diary:seers:caught-shark", - 1 + player.getAttribute("diary:seers:caught-shark", 0) - ) - if (player.getAttribute("diary:seers:caught-shark", 0) >= 5) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 7) - } - } - // Catch some shrimp in the Fishing spot to the east of

Lumbridge Swamp - if (player.location.withinDistance(Location.create(3241, 3149, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 13) - } - // Catch a pike in the river to the east of Lumbridge Castle - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - // Catch a salmon in the river to the east of Lumbridge Castle - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - // Catch a trout in the river to the east of Barbarian Village - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.BASS -> { - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 1, 11) - ) { - player.setAttribute("/save:diary:seers:bass-caught", true) - } - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 7) - ) { - player.setAttribute( - "/save:diary:seers:caught-shark", - 1 + player.getAttribute("diary:seers:caught-shark", 0) - ) - if (player.getAttribute("diary:seers:caught-shark", 0) >= 5) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 7) - } - } - if (player.location.withinDistance(Location.create(3241, 3149, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 13) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.SHARK -> { - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 7) - ) { - player.setAttribute( - "/save:diary:seers:caught-shark", - 1 + player.getAttribute("diary:seers:caught-shark", 0) - ) - if (player.getAttribute("diary:seers:caught-shark", 0) >= 5) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 7) - } - } - if (player.location.withinDistance(Location.create(3241, 3149, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 13) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.SHRIMP -> { - if (player.location.withinDistance(Location.create(3241, 3149, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 13) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.PIKE -> { - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.SALMON -> { - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.TROUT -> if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - // Use the Fishing spots north of the banana plantation - if (node!!.id == 333 && player.zoneMonitor.isInZone("karamja") - && player.location.withinDistance(Location(2924, 3178, 0), 10) - ) { - player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 0, 6) - } - } - - fun updateSkillTask() { - when (fish) { - Fish.ANCHOVIE -> { - player.skillTasks.decreaseTask(player, SkillTasks.FANCHOVIES1) - player.skillTasks.decreaseTask(player, SkillTasks.FANCHOVIES2) - } - Fish.HERRING -> { - player.skillTasks.decreaseTask(player, SkillTasks.FHERRING1) - player.skillTasks.decreaseTask(player, SkillTasks.FHERRING2) - } - Fish.LOBSTER -> { - player.skillTasks.decreaseTask(player, SkillTasks.FLOBSTER1) - player.skillTasks.decreaseTask(player, SkillTasks.FLOBSTER2) - } - Fish.SALMON -> { - player.skillTasks.decreaseTask(player, SkillTasks.FSALMON1) - player.skillTasks.decreaseTask(player, SkillTasks.FSALMON2) - } - Fish.SHARK -> { - player.skillTasks.decreaseTask(player, SkillTasks.FSHARK1) - player.skillTasks.decreaseTask(player, SkillTasks.FSHARK2) - player.skillTasks.decreaseTask(player, SkillTasks.FSHARK3) - } - Fish.SHRIMP -> { - player.skillTasks.decreaseTask(player, SkillTasks.FSHRIMP1) - player.skillTasks.decreaseTask(player, SkillTasks.FSHRIMP2) - } - Fish.SWORDFISH -> { - player.skillTasks.decreaseTask(player, SkillTasks.FSWORD1) - player.skillTasks.decreaseTask(player, SkillTasks.FSWORD2) - } - Fish.TROUT -> { - player.skillTasks.decreaseTask(player, SkillTasks.FTROUT1) - player.skillTasks.decreaseTask(player, SkillTasks.FTROUT2) - } - Fish.TUNA -> { - player.skillTasks.decreaseTask(player, SkillTasks.FTUNA1) - player.skillTasks.decreaseTask(player, SkillTasks.FTUNA2) - } - } - } - - private fun isBareHanded(p: Player): Boolean { - if (option == FishingOption.HARPOON) { - if (checkFish(p) > 0 && !(player.inventory.containsItem( - option.tool - ) || player.equipment.containsItem(option.tool)) - ) { - return true - } - if (checkFish(p) > 2 && !(player.inventory.containsItem( - option.tool - ) || player.equipment.containsItem(option.tool)) - ) { - return true - } - } - return false - } - - private fun getCatchAnimationAndLoot(p: Player): Int { - val fishingFor = checkFish(p) - when (node!!.id) { - 324 -> when (fishingFor) { - 1 -> { - p.animate(Animation(6710)) - p.skills.addExperience(Skills.FISHING, 80.0) - p.skills.addExperience(Skills.STRENGTH, 8.0) - p.inventory.add(Item(359)) - } - 2, 3 -> if (RandomFunction.random(1) == 1) { - p.animate(Animation(6710)) - p.skills.addExperience(Skills.FISHING, 80.0) - p.skills.addExperience(Skills.STRENGTH, 8.0) - p.inventory.add(Item(359)) - } else { - p.animate(Animation(6707)) - p.skills.addExperience(Skills.FISHING, 100.0) - p.skills.addExperience(Skills.STRENGTH, 10.0) - p.inventory.add(Item(371)) - } - } - 313 -> { - p.animate(Animation(6705)) - p.skills.addExperience(Skills.FISHING, 110.0) - p.skills.addExperience(Skills.STRENGTH, 11.0) - p.inventory.add(Item(383)) - } - } - return 0 - } - - /** - * Checks if they have the barb tail harpoon. - * - * @return `True` if so. - */ - private fun hasBarbTail(): Boolean { - if (option == FishingOption.HARPOON) { - if (player.inventory.containsItem(FishingOption.BARB_HARPOON.tool) || player.equipment.containsItem( - FishingOption.BARB_HARPOON.tool - ) - ) { - return true - } - } - return false - } - - override fun message(type: Int) { - when (type) { - 0 -> player.packetDispatch.sendMessage(option!!.startMessage) - 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 <= ' ' } + ".") - if (player.inventory.freeSlots() == 0) { - player.dialogueInterpreter.sendDialogue("You don't have enough space in your inventory.") - stop() - } - } - } - } - - /** - * Method used to check if the catch was a success. - * - * @return `True` if so. - */ - private fun success(): Boolean { - if (delay == 1) { - return false - } - val level = 1 + player.skills.getLevel(Skills.FISHING) + player.familiarManager.getBoost(Skills.FISHING) - val hostRatio = Math.random() * fish!!.level - val clientRatio = Math.random() * (level * 1.25 - fish!!.level) - return hostRatio < clientRatio - } - - companion object { - fun checkFish(p: Player): Int { - return if (p.skills.getLevel(Skills.FISHING) >= 55 && p.skills.getLevel(Skills.STRENGTH) >= 35) { - if (p.skills.getLevel(Skills.FISHING) >= 70 && p.skills.getLevel(Skills.STRENGTH) >= 50) { - if (p.skills.getLevel(Skills.FISHING) >= 96 && p.skills.getLevel(Skills.STRENGTH) >= 76) { - 3 - } else 2 - } else 1 - } else 0 - } - } - - /** - * Constructs a new `FishingPulse` `Object`. - * - * @param player the player. - * @param npc the fishing spot NPC. - * @param option The fishing option. - */ - init { - if (option != null) { - fish = option.getRandomFish(player) - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/BarbFishInteractionListeners.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/BarbFishInteractionListeners.kt deleted file mode 100644 index 9892ac65d..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/BarbFishInteractionListeners.kt +++ /dev/null @@ -1,30 +0,0 @@ -package rs09.game.node.entity.skill.gather.fishing.barbfishing - -import core.game.node.item.Item -import rs09.game.interaction.InteractionListener - -class BarbFishInteractionListeners : InteractionListener() { - override fun defineListeners() { - - on(25268,SCENERY,"search"){ player, _ -> - if(player.getAttribute("barbtraining:fishing",false) == true){ - if(!player.inventory.containsItem(Item(11323))){ - player.inventory.add(Item(11323)) - player.sendMessage("Under the bed you find a fishing rod.") - } else { - player.sendMessage("You find nothing under the bed") - } - } else { - player.sendMessage("Maybe I should speak to Otto before looking under his bed.") - } - return@on true - } - - on(1176,NPC,"fish"){player,_ -> - player.pulseManager.run(BarbFishingPulse(player)) - player.sendMessage("You attempt to catch a fish...") - return@on true - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/SpotManager.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/SpotManager.kt deleted file mode 100644 index 695c02450..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/barbfishing/SpotManager.kt +++ /dev/null @@ -1,82 +0,0 @@ -package rs09.game.node.entity.skill.gather.fishing.barbfishing - -import core.game.node.entity.npc.NPC -import core.game.world.map.Location -import core.plugin.CorePluginTypes.ManagerPlugin -import core.plugin.CorePluginTypes.Managers -import core.plugin.Initializable -import core.plugin.Plugin -import core.tools.RandomFunction -import rs09.game.node.entity.skill.gather.fishing.barbfishing.SpotManager.Companion.locations -import rs09.game.node.entity.skill.gather.fishing.barbfishing.SpotManager.Companion.usedLocations - -@Initializable -/** - * Manages fishing spot spawning and relocation - * @author Ceikry - */ -class SpotManager : ManagerPlugin() { - var ticks = 0 - val spots = ArrayList() - - companion object{ - val usedLocations = arrayListOf() - val locations = listOf( - Location.create(2506, 3494, 0), - Location.create(2504, 3497, 0), - Location.create(2504, 3497, 0), - Location.create(2500, 3506, 0), - Location.create(2500, 3509, 0), - Location.create(2500, 3512, 0), - Location.create(2504, 3516, 0) - ) - } - - override fun tick() { - if(ticks % 50 == 0){ - usedLocations.clear() - for(spot in spots) usedLocations.add(spot.loc ?: Location(0,0,0)) - } - } - - override fun newInstance(arg: Any?): Plugin { - for(i in 0 until 5){ - spots.add(BarbFishingSpot(getNewLoc(), getNewTTL()).also { it.init() }) - } - Managers.register(this) - return this - } -} - -fun getNewTTL(): Int{ - return RandomFunction.random(400,2000) -} - -fun getNewLoc(): Location { - val possibleLoc = locations.toTypedArray().toMutableList() - possibleLoc.removeAll(usedLocations) - val loc = possibleLoc.random() - usedLocations.add(loc) - return loc -} - -class BarbFishingSpot(var loc: Location? = null, var ttl: Int) : NPC(1176){ - init { - location = loc - } - val locations = listOf( - Location.create(2506, 3494, 0), - Location.create(2504, 3497, 0), - Location.create(2504, 3497, 0), - Location.create(2500, 3506, 0), - Location.create(2500, 3509, 0), - Location.create(2500, 3512, 0), - Location.create(2504, 3516, 0) - ) - override fun handleTickActions() { - if(location != loc) properties.teleportLocation = loc.also { ttl = getNewTTL() } - if(ttl-- <= 0){ - loc = getNewLoc() - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/mining/MiningSkillPulse.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/mining/MiningSkillPulse.kt deleted file mode 100644 index 79465394f..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/mining/MiningSkillPulse.kt +++ /dev/null @@ -1,324 +0,0 @@ -package rs09.game.node.entity.skill.gather.mining - -import api.ContentAPI -import core.cache.def.impl.ItemDefinition -import core.game.container.impl.EquipmentContainer -import core.game.content.dialogue.FacialExpression -import core.game.content.global.SkillingPets -import core.game.content.quest.tutorials.tutorialisland.TutorialSession -import core.game.content.quest.tutorials.tutorialisland.TutorialStage -import core.game.node.Node -import core.game.node.scenery.Scenery -import core.game.node.scenery.SceneryBuilder -import core.game.node.entity.impl.Animator -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.entity.skill.gather.SkillingTool -import core.game.node.entity.skill.gather.mining.MiningNode -import core.game.node.item.ChanceItem -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 core.tools.RandomFunction -import core.tools.StringUtils -import org.rs09.consts.Items -import rs09.game.node.entity.player.info.stats.STATS_BASE -import rs09.game.node.entity.player.info.stats.STATS_ROCKS -import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks - -/** - * Mining skill pulse - * @author ceik - */ -class MiningSkillPulse(private val player: Player, private val node: Node) : Pulse(1, player, node) { - private var resource: MiningNode? = null - private var isMiningEssence = false - private var isMiningGems = false - private var ticks = 0 - protected var resetAnimation = true - fun message(type: Int) { - if (type == 0) { - player.packetDispatch.sendMessage("You swing your pickaxe at the rock...") - } - } - - override fun pulse(): Boolean { - if (!checkRequirements()) { - return true - } - animate() - return reward() - } - - override fun stop() { - if (resetAnimation) { - player.animate(Animation(-1, Animator.Priority.HIGH)) - } - super.stop() - message(1) - } - - override fun start() { - resource = MiningNode.forId(node.id) - if (MiningNode.isEmpty(node.id)) { - player.packetDispatch.sendMessage("This rock contains no ore.") - } - if (resource == null) { - return - } - if (resource!!.id == 2491) { - isMiningEssence = true - } - if (resource!!.identifier == MiningNode.GEM_ROCK_0.identifier) { - isMiningGems = true - } - if (TutorialSession.getExtension(player).stage == 35) { - TutorialStage.load(player, 36, false) - } - if (checkRequirements()) { - super.start() - message(0) - } - } - - fun checkRequirements(): Boolean { - if (player.skills.getLevel(Skills.MINING) < resource!!.getLevel()) { - player.packetDispatch.sendMessage("You need a mining level of " + resource!!.getLevel() + " to mine this rock.") - return false - } - if (SkillingTool.getPickaxe(player) == null) { - player.packetDispatch.sendMessage("You do not have a pickaxe to use.") - return false - } - if (player.inventory.freeSlots() < 1) { - player.dialogueInterpreter.sendDialogue("Your inventory is too full to hold any more " + ItemDefinition.forId(resource!!.getReward()).name.toLowerCase() + ".") - return false - } - return true - } - - fun animate() { - player.animate(SkillingTool.getPickaxe(player).animation) - } - - fun reward(): Boolean { - if (++ticks % (if (isMiningEssence) 3 else 4) != 0) { - return false - } - if (node.id == 10041) { - player.dialogueInterpreter.sendDialogues(2574, FacialExpression.FURIOUS, if (RandomFunction.random(2) == 1) "You'll blow my cover! I'm meant to be hidden!" else "Will you stop that?") - return true - } - if (!checkReward()) { - return false - } - - //Handle tutorial stuff - val tutorialStage = TutorialSession.getExtension(player).stage - if (tutorialStage == 36 && node.id == 3042) { - TutorialStage.load(player, 38, false) - } else if (tutorialStage == 36 && node.id == 3043) { - TutorialStage.load(player, 37, false) - } - if (tutorialStage == 38 && node.id == 3043) { - TutorialStage.load(player, 39, false) - } else if (tutorialStage == 37 && node.id == 3042) { - TutorialStage.load(player, 39, false) - } - - //actual reward calculations - var reward = resource!!.getReward() - var rewardAmount = 0 - if (reward > 0) { - reward = calculateReward(reward) // calculate rewards - rewardAmount = calculateRewardAmount(reward) // calculate amount - - applyAchievementTask(reward) // apply achievements - SkillingPets.checkPetDrop(player, SkillingPets.GOLEM) // roll for pet - - //add experience - val experience = resource!!.getExperience() * rewardAmount - player.skills.addExperience(Skills.MINING, experience, true) - - //Handle bracelet of clay - if(reward == Items.CLAY_434){ - val bracelet = player.equipment.get(EquipmentContainer.SLOT_HANDS) - if(bracelet != null && bracelet.id == Items.BRACELET_OF_CLAY_11074){ - if(bracelet.charge > 28) bracelet.charge = 28 - bracelet.charge-- - reward = Items.SOFT_CLAY_1761 - player.sendMessage("Your bracelet of clay softens the clay for you.") - if(bracelet.charge <= 0){ - player.sendMessage("Your bracelet of clay crumbles to dust.") - player.equipment.remove(bracelet) - } - } - } - - //send the message for the resource reward - if (isMiningGems) { - val gemName = ItemDefinition.forId(reward).name.toLowerCase() - player.sendMessage("You get " + (if (StringUtils.isPlusN(gemName)) "an" else "a") + " " + gemName + ".") - } else { - player.packetDispatch.sendMessage("You get some " + ItemDefinition.forId(reward).name.toLowerCase() + ".") - } - //give the reward - player.inventory.add(Item(reward, rewardAmount)) - var rocksMined = player.getAttribute("$STATS_BASE:$STATS_ROCKS",0) - player.setAttribute("/save:$STATS_BASE:$STATS_ROCKS",++rocksMined) - - //calculate bonus gem for mining - if (!isMiningEssence) { - var chance = 282 - var altered = false - if (Item(player.equipment.getId(12)).name.toLowerCase().contains("ring of wealth") || ContentAPI.inEquipment(player, Items.RING_OF_THE_STAR_SPRITE_14652)) { - chance = (chance / 1.5).toInt() - altered = true - } - val necklace = player.equipment[EquipmentContainer.SLOT_AMULET] - if (necklace != null && necklace.id in 1705..1713) { - chance = (chance / 1.5).toInt() - altered = true - } - if (RandomFunction.roll(chance)) { - val gem = GEM_REWARDS.random() - player.packetDispatch.sendMessage("You find a " + gem.name + "!") - if (!player.inventory.add(gem, player)) { - player.packetDispatch.sendMessage("You do not have enough space in your inventory, so you drop the gem on the floor.") - } - } - } - - //transform to depleted version - if (!isMiningEssence && resource!!.getRespawnRate() != 0) { - SceneryBuilder.replace(node as Scenery, Scenery(resource!!.emptyId, node.getLocation(), node.type, node.rotation), resource!!.respawnDuration) - node.setActive(false) - return true - } - } - return false - } - - private fun calculateRewardAmount(reward: Int): Int { - var amount = 1 - - //checks for varrock armor from varrock diary and rolls 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) <= 10) { - amount += 1 - player.sendMessage("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) <= 10) { - amount += 1 - player.sendMessage("The Varrock armour allows you to mine an additional ore.") - } - Items.ADAMANTITE_ORE_449 -> if (player.achievementDiaryManager.armour >= 2 && RandomFunction.random(100) <= 10) { - amount += 1 - player.sendMessage("The Varrock armour allows you to mine an additional ore.") - } - } - } - - //check for bonus ore from shooting star buff - if (player.hasActiveState("shooting-star")) { - if (RandomFunction.getRandom(5) == 3) { - player.packetDispatch.sendMessage("...you manage to mine a second ore thanks to the Star Sprite.") - amount += 1 - } - } - return amount - } - - private fun calculateReward(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 - player.skills.addExperience(Skills.MINING, value * 10.toDouble(), true) - } else if (isMiningEssence && player.skills.getLevel(Skills.MINING) >= 30) { - reward = 7936 - } else if (isMiningGems) { - reward = RandomFunction.rollWeightedChanceTable(MiningNode.gemRockGems).id - } - return reward - } - - /** - * Checks if the has completed any achievements from their diary - */ - private fun applyAchievementTask(reward: Int) { - // Mine some Iron in the south east mining patch near Varrock - if (reward == Items.IRON_ORE_440 && player.location.withinDistance(Location.create(3285, 3363, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 2) - } - - // Mine some limestone near Paterdomus, the temple to the east

of Varrock - if (reward == Items.LIMESTONE_3211 && player.location.withinDistance(Location.create(3372, 3500, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 15) - } - - // Mine some gold from the rocks on the north-west

peninsula of Karamja - if (reward == Items.GOLD_ORE_444 && player.location.withinDistance(Location.create(2733, 3225, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 0, 2) - } - - // Mine a red topaz from a gem rock - if (reward == Items.UNCUT_RED_TOPAZ_1629 && (player.viewport.region.id == 11310 || player.viewport.region.id == 11410)) { - player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 18) - } - - // Mine some clay in the Mining patch north of the Champions'

Guild - if (reward == Items.CLAY_434 && player.viewport.region.id == 12596) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 5) - } - - // Mine some copper in the Mining spot to the south-east of

Lumbridge Swamp - if (reward == Items.COPPER_ORE_436 && player.viewport.region.id == 12849) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 12) - } - - // Mine some iron ore from the Al Kharid Mining spot - if (reward == Items.IRON_ORE_440 && player.viewport.region.id == 13107) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 0) - } - - // Mine some silver from the mining spot north of Al Kharid - if (reward == Items.SILVER_ORE_442 && player.viewport.region.id == 13107) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 10) - } - - // Mine some coal in the Mining spot south-west of Lumbridge

Swamp - if (reward == Items.COAL_453 && player.viewport.region.id == 12593) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 11) - } - } - - /** - * Checks if the player gets rewarded. - * @return `True` if so. - */ - private fun checkReward(): Boolean { - val skill = Skills.MINING - val level = 1 + player.skills.getLevel(skill) + player.familiarManager.getBoost(skill) - val hostRatio = Math.random() * (100.0 * resource!!.getRate()) - var toolRatio = SkillingTool.getPickaxe(player).ratio - if(SkillcapePerks.isActive(SkillcapePerks.PRECISION_MINER,player)){ - toolRatio += 0.075 - } - val clientRatio = Math.random() * ((level - resource!!.getLevel()) * (1.0 + toolRatio)) - return hostRatio < clientRatio - } - - companion object { - 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)) - } - - init { - super.stop() - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/herblore/GrindItemPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/herblore/GrindItemPlugin.kt deleted file mode 100644 index 4d119b1e9..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/herblore/GrindItemPlugin.kt +++ /dev/null @@ -1,73 +0,0 @@ -package rs09.game.node.entity.skill.herblore - -import core.game.interaction.NodeUsageEvent -import core.game.interaction.UseWithHandler -import core.game.node.entity.skill.SkillPulse -import core.game.node.entity.skill.herblore.GrindingItem -import core.game.node.item.Item -import core.game.world.update.flag.context.Animation -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 rs09.game.content.dialogue.SkillDialogueHandler - -/** - * plugin used to handle the grinding of an item. - * @author 'Ceikry - */ -@Initializable -class GrindItemPlugin : UseWithHandler(233) { - override fun newInstance(arg: Any?): Plugin? { - for (grind in GrindingItem.values()) { - for (i in grind.items) { - addHandler(i.id, ITEM_TYPE, this) - } - } - return this - } - - 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){ - override fun create(amount: Int, index: Int) { - 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) - } - super.setDelay(2) - } - override fun checkRequirements(): Boolean { - return true - } - - override fun animate() { - player.animator.animate(ANIMATION) - } - - override fun reward(): Boolean { - if(player.inventory.remove(node)){ - player.inventory.add(GrindingItem.forItem(node).product) - } - amt-- - return amt <= 0 - } - }) - } - } - handler.open() - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(event.player, 309, 2, 210, 15)) - return true - } - - companion object { - /** - * Represents the animation to use. - */ - private val ANIMATION = Animation(364) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/CommonKebbitEast.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/CommonKebbitEast.kt deleted file mode 100644 index ae484988b..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/CommonKebbitEast.kt +++ /dev/null @@ -1,74 +0,0 @@ -package rs09.game.node.entity.skill.hunter.tracking - -import core.cache.def.impl.SceneryDefinition -import core.game.node.item.Item -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 - -@Initializable -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)) - ), - 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)) - ) - ) - - 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)) - ) - experience = 36.0 - varp = 919 - trailLimit = 3 - attribute = "hunter:tracking:commontrail" - indexAttribute = "hunter:tracking:commonIndex" - rewards = arrayOf(Item(Items.COMMON_KEBBIT_FUR_10121), Item(Items.BONES_526),Item(Items.RAW_BEAST_MEAT_9986)) - KEBBIT_ANIM = Animation(5259) - } - - - override fun newInstance(arg: Any?): Plugin { - if(!linkingTrails.contains(initialMap.values.random()[0])){ - addExtraTrails() - } - SceneryDefinition.forId(19439).handlers["option:inspect"] = this - SceneryDefinition.forId(19440).handlers["option:inspect"] = this - SceneryDefinition.forId(19360).handlers["option:inspect"] = this - SceneryDefinition.forId(19361).handlers["option:inspect"] = this - SceneryDefinition.forId(19362).handlers["option:inspect"] = this - SceneryDefinition.forId(19363).handlers["option:inspect"] = this - SceneryDefinition.forId(19364).handlers["option:inspect"] = this - SceneryDefinition.forId(19365).handlers["option:inspect"] = this - SceneryDefinition.forId(19356).handlers["option:inspect"] = this - SceneryDefinition.forId(19357).handlers["option:inspect"] = this - SceneryDefinition.forId(19358).handlers["option:inspect"] = this - SceneryDefinition.forId(19359).handlers["option:inspect"] = this - SceneryDefinition.forId(19375).handlers["option:inspect"] = this - SceneryDefinition.forId(19376).handlers["option:inspect"] = this - SceneryDefinition.forId(19377).handlers["option:inspect"] = this - SceneryDefinition.forId(19378).handlers["option:inspect"] = this - SceneryDefinition.forId(19379).handlers["option:inspect"] = this - SceneryDefinition.forId(19372).handlers["option:inspect"] = this - SceneryDefinition.forId(19380).handlers["option:inspect"] = this - SceneryDefinition.forId(19374).handlers["option:inspect"] = this - SceneryDefinition.forId(19373).handlers["option:inspect"] = this - SceneryDefinition.forId(19428).handlers["option:search"] = this - SceneryDefinition.forId(19428).handlers["option:attack"] = this - - return this - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/CommonKebbitWest.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/CommonKebbitWest.kt deleted file mode 100644 index 5d2de79c3..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/CommonKebbitWest.kt +++ /dev/null @@ -1,4 +0,0 @@ -package rs09.game.node.entity.skill.hunter.tracking - -class CommonKebbitWest { -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/PolarKebbitHunting.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/PolarKebbitHunting.kt deleted file mode 100644 index bea33f3f1..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/PolarKebbitHunting.kt +++ /dev/null @@ -1,68 +0,0 @@ -package rs09.game.node.entity.skill.hunter.tracking - -import core.cache.def.impl.SceneryDefinition -import core.game.node.item.Item -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 - -@Initializable -class PolarKebbitHunting : HunterTracking() { - - init { - KEBBIT_ANIM = Animation(5256) - trailLimit = 3 - attribute = "hunter:tracking:polartrail" - indexAttribute = "hunter:tracking:polarindex" - rewards = arrayOf(Item(Items.RAW_BEAST_MEAT_9986),Item(Items.POLAR_KEBBIT_FUR_10117),Item(Items.BONES_526)) - tunnelEntrances = arrayOf( - Location.create(2711, 3819, 1), - Location.create(2714, 3821, 1), - Location.create(2718, 3829, 1), - Location.create(2721, 3827, 1), - Location.create(2718, 3832, 1), - Location.create(2715, 3820, 1) - ) - 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)) - ), - 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)) - ) - ) - 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)) - ) - experience = 30.0 - varp = 926 - requiredLevel = 1 - } - - override fun newInstance(arg: Any?): Plugin { - addExtraTrails() - SceneryDefinition.forId(19640).handlers["option:inspect"] = this - SceneryDefinition.forId(19641).handlers["option:inspect"] = this - SceneryDefinition.forId(19435).handlers["option:inspect"] = this - SceneryDefinition.forId(36689).handlers["option:inspect"] = this - SceneryDefinition.forId(36690).handlers["option:inspect"] = this - SceneryDefinition.forId(19421).handlers["option:inspect"] = this - SceneryDefinition.forId(19424).handlers["option:inspect"] = this - SceneryDefinition.forId(19426).handlers["option:inspect"] = this - SceneryDefinition.forId(19419).handlers["option:inspect"] = this - SceneryDefinition.forId(19420).handlers["option:inspect"] = this - SceneryDefinition.forId(19423).handlers["option:inspect"] = this - SceneryDefinition.forId(36688).handlers["option:inspect"] = this - SceneryDefinition.forId(19435).handlers["option:search"] = this - SceneryDefinition.forId(19435).handlers["option:attack"] = this - return this - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/TrailDefinition.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/TrailDefinition.kt deleted file mode 100644 index 84577e3d2..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/hunter/tracking/TrailDefinition.kt +++ /dev/null @@ -1,14 +0,0 @@ -package rs09.game.node.entity.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){ - override fun toString(): String { - return "$startLocation $endLocation [offset: $offset] [${type.name}] [inverted: $inverted]" - } -} -enum class TrailType{ - LINKING, - INITIAL, - TUNNEL -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/LunarListeners.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/LunarListeners.kt deleted file mode 100644 index 5c42bb50d..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/LunarListeners.kt +++ /dev/null @@ -1,256 +0,0 @@ -package rs09.game.node.entity.skill.magic - -import core.game.component.Component -import core.game.node.scenery.Scenery -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.skill.cooking.CookableItems -import core.game.node.item.Item -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.game.world.update.flag.context.Graphics -import org.rs09.consts.Components -import org.rs09.consts.Items -import rs09.game.node.entity.skill.farming.FarmingPatch -import rs09.game.node.entity.skill.magic.spellconsts.Lunar -import rs09.game.system.config.NPCConfigParser - -class LunarListeners : SpellListener("lunar") { - val BAKE_PIE_ANIM = Animation(4413) - val BAKE_PIE_GFX = Graphics(746,75) - val STATSPY_ANIM = Animation(6293) - val STATSPY_GFX = Graphics(1059) - val CURE_PLANT_ANIM = Animation(4409) - val CURE_PLANT_GFX = Graphics(742,100) - val NPC_CONTACT_ANIM = Animation(4413) - val NPC_CONTACT_GFX = Graphics(730,130) - - override fun defineListeners() { - - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - 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))) - sendGroupTeleport(player,99.0, "Ice Plateau", Location.create(2972, 3873, 0)) - } - - 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()) - } - - 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()) - } - - onCast(Lunar.NPC_CONTACT,NONE){player,node -> - 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) - } - } - } - - 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("It's not very effective....") - return - } - val patch = fPatch.getPatchFor(player) - if(!patch.isDiseased && !patch.isWeedy()){ - player.sendMessage("This patch is already growing fine.") - return - } - if(patch.isWeedy()){ - player.sendMessage("Trust me lad, the weeds are healthy enough as is.") - 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 - } - - patch.cureDisease() - removeRunes(player) - addXP(player,60.0) - visualizeSpell(player,CURE_PLANT_ANIM,CURE_PLANT_GFX) - setDelay(player,false) - } - - 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) - 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) - } - - fun bakePie(player: Player){ - val playerPies = ArrayList() - - for(item in player.inventory.toArray()){ - if(item == null) continue - val pie = CookableItems.forId(item.id) ?: continue - if(!pie.name.toLowerCase().contains("pie")) continue - if(player.skills.getLevel(Skills.COOKING) < pie.level) continue - playerPies.add(item) - } - - if(playerPies.isEmpty()){ - player.sendMessage("You have no pies which you have the level to cook.") - return - } - - 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) - val pie = CookableItems.forId(item.id) - player.visualize(BAKE_PIE_ANIM,BAKE_PIE_GFX) - addXP(player,60.0) - player.skills.addExperience(Skills.COOKING,pie.experience) - setDelay(player,false) - player.inventory.remove(item) - player.inventory.add(Item(pie.cooked)) - playerPies.remove(item) - if(playerPies.isNotEmpty()) removeRunes(player,false) else removeRunes(player,true) - return false - } - }) - } - - fun sendTeleport(player: Player, xp: Double, loc: Location){ - if(player.locks.isTeleportLocked){ - player.sendMessage("A magical force prevents you from teleporting.") - return - } - if(player.teleporter.send(loc,TeleportManager.TeleportType.LUNAR)) { - addXP(player, xp) - removeRunes(player) - setDelay(player, true) - } - } - - 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) - } - sendTeleport(player,xp,loc) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/ModernListeners.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/ModernListeners.kt deleted file mode 100644 index 0f538b218..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/ModernListeners.kt +++ /dev/null @@ -1,275 +0,0 @@ -package rs09.game.node.entity.skill.magic - -import core.game.content.activity.mta.impl.GraveyardZone -import core.game.content.global.Bones -import core.game.node.entity.Entity -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.player.link.TeleportManager -import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.smithing.smelting.Bar -import core.game.node.entity.skill.smithing.smelting.SmeltingPulse -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 core.game.world.update.flag.context.Graphics -import org.rs09.consts.Items -import rs09.ServerConstants -import rs09.game.node.entity.skill.magic.spellconsts.Modern - -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, Priority.HIGH) - private val HIGH_ANIMATION = Animation(729, 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, _ -> - requires(player) - player.teleporter.send(ServerConstants.HOME_LOCATION,TeleportManager.TeleportType.HOME) - setDelay(player,true) - } - - onCast(Modern.VARROCK_TELEPORT,NONE){player, _-> - requires(player,25, arrayOf(Item(Items.FIRE_RUNE_554),Item(Items.AIR_RUNE_556,3),Item(Items.LAW_RUNE_563))) - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK,1, 13) - sendTeleport(player,35.0,Location.create(3213, 3424, 0)) - } - - onCast(Modern.LUMBRIDGE_TELEPORT,NONE){player,_ -> - requires(player,31, arrayOf(Item(Items.EARTH_RUNE_557),Item(Items.AIR_RUNE_556,3),Item(Items.LAW_RUNE_563))) - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 2) - sendTeleport(player,41.0,Location.create(3221, 3219, 0)) - } - - onCast(Modern.FALADOR_TELEPORT,NONE){player, _ -> - requires(player,37, arrayOf(Item(Items.WATER_RUNE_555),Item(Items.AIR_RUNE_556,3),Item(Items.LAW_RUNE_563))) - sendTeleport(player,47.0,Location.create(2965, 3378, 0)) - } - - 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)) - } - - 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)) - } - - onCast(Modern.WATCHTOWER_TELEPORT,NONE){player,_ -> - 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, _ -> - 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,_ -> - 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)) - } - - onCast(Modern.TELEPORT_TO_HOUSE,NONE){player, _ -> - requires(player,40, arrayOf(Item(Items.LAW_RUNE_563), Item(Items.AIR_RUNE_556), Item(Items.EARTH_RUNE_557))) - attemptHouseTeleport(player) - } - - onCast(Modern.LOW_ALCHEMY,ITEM){player, node -> - val item = node?.asItem() ?: return@onCast - requires(player,21, arrayOf(Item(Items.FIRE_RUNE_554,3),Item(Items.NATURE_RUNE_561))) - alchemize(player,item,high = false) - } - - onCast(Modern.HIGH_ALCHEMY,ITEM){player, node -> - val item = node?.asItem() ?: return@onCast - requires(player,55, arrayOf(Item(Items.FIRE_RUNE_554,5),Item(Items.NATURE_RUNE_561,1))) - alchemize(player,item,high = true) - } - - onCast(Modern.SUPERHEAT,ITEM){player, node -> - val item = node?.asItem() ?: return@onCast - requires(player,43, arrayOf(Item(Items.FIRE_RUNE_554,4),Item(Items.NATURE_RUNE_561,1))) - superheat(player,item) - } - - onCast(Modern.BONES_TO_BANANAS,NONE){player, _ -> - requires(player,15, arrayOf(Item(Items.EARTH_RUNE_557,2), Item(Items.WATER_RUNE_555,2), Item(Items.NATURE_RUNE_561,1))) - boneConvert(player,true) - } - - onCast(Modern.BONES_TO_PEACHES,NONE){player, _ -> - 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) - } - } - - private fun boneConvert(player: Player,bananas: Boolean){ - val isInMTA = player.zoneMonitor.isInZone("Creature Graveyard") - if(isInMTA && player.getAttribute("tablet-spell",false)){ - player.sendMessage("You can not use this tablet in the Mage Training Arena.") - return - } - - if(!bananas && !player.savedData.activityData.isBonesToPeaches && !player.getAttribute("tablet-spell",false)){ - player.sendMessage("You can only learn this spell from the Mage Training Arena.") - return - } - - val bones = if(isInMTA) intArrayOf(6904,6905,6906,6907) else Bones.values().map { it.itemId }.toIntArray() - - for(item in player.inventory.toArray()){ - item ?: continue - if(isInMTA){ - if(bones.contains(item.id)){ - val inInventory = player.inventory.getAmount(item.id) - val amount = inInventory * GraveyardZone.BoneType.forItem(Item(item.id)).ordinal + 1 - if(amount > 0){ - player.inventory.remove(Item(item.id,inInventory)) - player.inventory.add(Item(if(bananas) Items.BANANA_1963 else Items.PEACH_6883,amount)) - } - } - } else { - if(bones.contains(item.id)){ - val inInventory = player.inventory.getAmount(item.id) - player.inventory.remove(Item(item.id,inInventory)) - player.inventory.add(Item(if(bananas) Items.BANANA_1963 else Items.PEACH_6883,inInventory)) - } - } - } - visualizeSpell(player,BONE_CONVERT_ANIM, BONE_CONVERT_GFX) - 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")){ - player.sendMessage("You can only cast this spell on ore.") - 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 - - 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) - 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 } - - if(player.zoneMonitor.isInZone("Alchemists' Playground")){ - player.sendMessage("You can only alch items from the cupboards!") - return - } - - val coins = Item(995, item.definition.getAlchemyValue(high)) - if (coins.amount > 0 && !player.inventory.hasSpaceFor(coins)) { - player.sendMessage("Not enough space in your inventory!") - return - } - - player.lock(3) - - if(player.inventory.remove(Item(item.id,1))) { - removeRunes(player) - if (high) { - visualizeSpell(player,HIGH_ALCH_ANIM,HIGH_ALCH_GFX,97) - } else { - visualizeSpell(player,LOW_ALCH_ANIM,LOW_ALCH_GFX,98) - } - - if(coins.amount > 0) - player.inventory.add(coins) - - if((item.id == Items.MAGIC_SHORTBOW_861 || item.id == Items.MAGIC_LONGBOW_859) && high && ZoneBorders(2721,3493,2730,3487).insideBorder(player)){ - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 6); - } - - addXP(player,if(high) 65.0 else 31.0) - showMagicTab(player) - setDelay(player,false) - } - } - - private fun sendTeleport(player: Player, xp: Double, location: Location){ - if(player.locks.isTeleportLocked){ - player.removeAttribute("spell:runes") - player.sendMessage("A magical force prevents you from teleporting.") - return - } - if(player.teleporter.send(location,TeleportManager.TeleportType.NORMAL)) { - removeRunes(player) - addXP(player, xp) - setDelay(player, true) - } - } - - private fun attemptHouseTeleport(player: Player){ - if(player.locks.isTeleportLocked){ - player.removeAttribute("spell:runes") - player.sendMessage("A magical force prevents you from teleporting.") - return - } - val loc = player.houseManager.location.exitLocation - if(loc == null){ - player.sendMessage("You do not have a house whose portal you can teleport to.") - return - } - - player.teleporter.send(loc,TeleportManager.TeleportType.NORMAL) - removeRunes(player) - addXP(player,30.0) - setDelay(player,true) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellListener.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellListener.kt deleted file mode 100644 index dad5e5c8b..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellListener.kt +++ /dev/null @@ -1,93 +0,0 @@ -package rs09.game.node.entity.skill.magic - -import core.cache.def.impl.ItemDefinition -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.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics -import rs09.game.interaction.Listener -import rs09.game.world.GameWorld - -abstract class SpellListener(val bookName: String) : Listener { - companion object { - @JvmField - val NPC = -1 - @JvmField - val OBJECT = -2 - @JvmField - val ITEM = -3 - @JvmField - val PLAYER = -4 - @JvmField - val NONE = -5 - @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, vararg ids: Int, method: (player: Player, node: Node?) -> Unit){ - SpellListeners.add(spellID,type,ids,bookName,method) - } - - fun requires(player: Player, magicLevel: Int = 0, runes: Array = arrayOf(), specialEquipment: IntArray = intArrayOf()){ - if(player.getAttribute("magic-delay",0) > GameWorld.ticks){ - throw IllegalStateException() - } - if(player.getAttribute("tablet-spell",false)){ - return - } - if(player.skills.getLevel(Skills.MAGIC) < magicLevel){ - 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.toLowerCase()}s to cast this spell.") - throw IllegalStateException() - } - } - for(item in specialEquipment){ - if(!player.equipment.contains(item,1)){ - player.sendMessage("You need a ${ItemDefinition.forId(item).name} to cast this.") - throw IllegalStateException() - } - } - } - - fun removeRunes(player: Player,removeAttr: Boolean = true){ - player.inventory.remove(*player.getAttribute("spell:runes",ArrayList()).toTypedArray()) - if(removeAttr) { - player.removeAttribute("spell:runes") - player.removeAttribute("tablet-spell") - } - } - - fun addXP(player: Player,amount:Double){ - if(player.getAttribute("tablet-spell",false)) return - player.skills.addExperience(Skills.MAGIC,amount) - } - - fun visualizeSpell(player: Player,anim:Animation,gfx: Graphics,soundID: Int = -1){ - if(player.getAttribute("tablet-spell",false)) return - player.visualize(anim,gfx) - if(soundID != -1){ - player.audioManager.send(soundID) - } - } - - fun setDelay(player: Player,isTeleport: Boolean = false){ - if(!isTeleport) player.setAttribute("magic-delay",GameWorld.ticks + 3) else player.setAttribute("magic-delay",GameWorld.ticks + 5) - } - - fun interrupt(player: Player){ - player.pulseManager.clear() - } - - fun showMagicTab(player: Player){ - player.interfaceManager.setViewedTab(6) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellListeners.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellListeners.kt deleted file mode 100644 index 36032a7ea..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellListeners.kt +++ /dev/null @@ -1,40 +0,0 @@ -package rs09.game.node.entity.skill.magic - -import core.game.node.Node -import core.game.node.entity.player.Player -import rs09.game.system.SystemLogger - -object SpellListeners { - val castMap = HashMap Unit>() - - fun add(spellID: Int, type: Int, book: String, method: (Player,Node?) -> Unit){ - castMap["$book:$spellID:$type"] = method - } - - fun add(spellID: Int, type: Int, ids: IntArray, book: String, method: (Player, Node?) -> Unit){ - for(id in ids) { - castMap["$book:$spellID:$type:$id"] = method - } - } - - fun get(spellID: Int, type: Int, book: String): ((Player,Node?) -> Unit)?{ - SystemLogger.logInfo("Getting $book:$spellID:$type") - return castMap["$book:$spellID:$type"] - } - - fun get(spellID: Int, type: Int, id: Int, book: String): ((Player,Node?) -> Unit)?{ - SystemLogger.logInfo("Getting $book:$spellID:$type:$id") - return 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 - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellTablets.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellTablets.kt deleted file mode 100644 index 39eefc17f..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellTablets.kt +++ /dev/null @@ -1,37 +0,0 @@ -package rs09.game.node.entity.skill.magic - -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 rs09.game.interaction.InteractionListener -import rs09.game.node.entity.skill.magic.spellconsts.Modern - -class SpellTablets : InteractionListener() { - val B2P_TABLET = Items.BONES_TO_PEACHES_8015 - val B2B_TABLET = Items.BONES_TO_BANANAS_8014 - override fun defineListeners() { - - on(B2B_TABLET,ITEM,"break"){player, node -> - breakTablet(player) - SpellListeners.run(Modern.BONES_TO_BANANAS,SpellListener.NONE,"modern",player) - player.inventory.remove(Item(node.id)) - return@on true - } - - on(B2P_TABLET,ITEM,"break"){player, node -> - breakTablet(player) - SpellListeners.run(Modern.BONES_TO_PEACHES,SpellListener.NONE,"modern",player) - player.inventory.remove(Item(node.id)) - return@on true - } - - } - - fun breakTablet(player: Player){ - player.audioManager.send(979) - player.animator.forceAnimation(Animation(4069)) - player.lock(5) - player.setAttribute("tablet-spell",true) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellUtils.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellUtils.kt deleted file mode 100644 index bb9d77526..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/SpellUtils.kt +++ /dev/null @@ -1,98 +0,0 @@ -package rs09.game.node.entity.skill.magic - -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.entity.skill.magic.CombinationRune -import core.game.node.entity.skill.magic.MagicStaff -import core.game.node.entity.skill.magic.Runes -import core.game.node.item.Item - -object SpellUtils { - fun usingStaff(p: Player, rune: Int): Boolean { - val weapon = p.equipment[3] ?: return false - val staff = MagicStaff.forId(rune) ?: return false - val staves = staff.staves - for (id in staves) { - if (weapon.id == id) { - return true - } - } - 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) - } - - 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) - } - } - 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 - } - } - toRemove.add(item) - return true - } - return true - } - - fun attackableNPC(npc: NPC): Boolean{ - return npc.definition.hasAction("attack") - } - - @JvmStatic - fun getBookFromInterface(id: Int): String{ - return when(id){ - 192 -> "modern" - 193 -> "ancient" - 430 -> "lunar" - else -> "none" - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/spellconsts/Lunar.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/spellconsts/Lunar.kt deleted file mode 100644 index 37a8d13cc..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/spellconsts/Lunar.kt +++ /dev/null @@ -1,24 +0,0 @@ -package rs09.game.node.entity.skill.magic.spellconsts - -object Lunar { - const val BARBARIAN_TELEPORT = 0 - const val NPC_CONTACT = 4 - const val MONSTER_EXAMINE = 6 - const val BAKE_PIE = 15 - const val HOME_TELEPORT = 16 - const val FISHING_GUILD_TELEPORT = 17 - const val KHAZARD_TELEPORT = 18 - const val MOONCLAN_TELEPORT = 20 - const val CATHERBY_TELEPORT = 21 - const val WATERBIRTH_TELEPORT = 24 - const val ICE_PLATEAU_TELEPORT = 28 - 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 -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/spellconsts/Modern.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/spellconsts/Modern.kt deleted file mode 100644 index 0cef959bc..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/magic/spellconsts/Modern.kt +++ /dev/null @@ -1,25 +0,0 @@ -package rs09.game.node.entity.skill.magic.spellconsts - -object Modern { - const val HOME_TELEPORT = 0 - const val CONFUSE = 2 - const val BONES_TO_BANANAS = 9 - const val WEAKEN = 11 - const val LOW_ALCHEMY = 13 - const val VARROCK_TELEPORT = 15 - const val LUMBRIDGE_TELEPORT = 18 - const val CURSE = 19 - const val FALADOR_TELEPORT = 21 - const val SUPERHEAT = 25 - const val CAMELOT_TELEPORT = 26 - const val TELEPORT_TO_HOUSE = 23 - const val ARDOUGNE_TELEPORT = 32 - const val HIGH_ALCHEMY = 34 - const val WATCHTOWER_TELEPORT = 37 - const val BONES_TO_PEACHES = 40 - const val TROLLHEIM_TELEPORT = 44 - const val APE_ATOLL_TELEPORT = 47 - const val VULNERABILITY = 50 - const val ENFEEBLE = 53 - const val STUN = 57 -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/PouchManager.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/PouchManager.kt deleted file mode 100644 index 150c4c25d..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/PouchManager.kt +++ /dev/null @@ -1,196 +0,0 @@ -package rs09.game.node.entity.skill.runecrafting - -import core.game.container.Container -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import core.game.node.item.Item -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.rs09.consts.Items -import rs09.tools.stringtools.colorize - -/** - * A class for managing rune pouches. - * @param player the player this manager instance belongs to. - * @author Ceikry - */ -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) - ) - - /** - * Method to add essence to a pouch - * @param pouchId the 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 - */ - fun addToPouch(pouchId: Int, amount: Int, essence: Int){ - if(!checkRequirement(pouchId)){ - player.sendMessage(colorize("%RYou lack the required level to use this pouch.")) - return - } - var amt = amount - val pouch = pouches[pouchId] - 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()){ - amt = pouch.container.freeSlots() - } - if(amt == 0){ - player.sendMessage("This pouch is already full.") - } - if(pouch.container.contains(otherEssence,1)){ - player.sendMessage("You can only store one type of essence in each pouch.") - return - } - player.inventory.remove(Item(essence,amt)) - pouch.container.add(Item(essence,amt)) - } - - - /** - * Method to withdraw rune essence from a pouch. - * @param pouchId the item ID of the pouch to withdraw from - * @author Ceikry - */ - fun withdrawFromPouch(pouchId: Int){ - val pouch = pouches[pouchId] - pouch ?: return - val playerFree = player.inventory.freeSlots() - 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.")) - } - } - - } - - - /** - * 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){ - val pouches = JSONArray() - - for(i in this.pouches){ - val pouch = JSONObject() - pouch.put("id",i.key.toString()) - val items = JSONArray() - for(item in i.value.container.toArray()){ - item ?: continue - val it = JSONObject() - it.put("itemId",item.id.toString()) - it.put("amount",item.amount.toString()) - items.add(it) - } - pouch.put("container",items) - pouch.put("charges",i.value.charges.toString()) - pouch.put("currentCap",i.value.currentCap.toString()) - pouches.add(pouch) - } - 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){ - val pouch = e as JSONObject - val id = pouch["id"].toString().toInt() - val p = pouches[id] - p ?: return - val charges = pouch["charges"].toString().toInt() - val currentCap = pouch["currentCap"].toString().toInt() - p.charges = charges - p.currentCap = currentCap - p.remakeContainer() - for(i in pouch["container"] as JSONArray){ - val it = i as JSONObject - it["itemId"] ?: continue - val item = it["itemId"].toString().toInt() - val amount = it["amount"].toString().toInt() - p.container.add(Item(item,amount)) - } - } - } - - - /** - * 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{ - 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 - */ - fun checkAmount(pouchId: Int){ - 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 - return pouches[pouchId - 1] != null - } - - /** - * A class that represents a runecrafting pouch. - * @author Ceikry - */ - class RCPouch(val capacity: Int, val levelRequirement: Int){ - var container = Container(capacity) - var currentCap = capacity - var charges = 10 - fun remakeContainer(){ - this.container = Container(currentCap) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/RunePouchPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/RunePouchPlugin.kt deleted file mode 100644 index e3f2227e0..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/RunePouchPlugin.kt +++ /dev/null @@ -1,62 +0,0 @@ -package rs09.game.node.entity.skill.runecrafting - -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.Plugin -import org.rs09.consts.Items -import rs09.tools.stringtools.colorize - -/** - * Handles the rune pouches. - * @author Ceikry - */ -class RunePouchPlugin : OptionHandler() { - @Throws(Throwable::class) - override fun newInstance(arg: Any?): Plugin? { - for (i in 5509..5515) { - ItemDefinition.forId(i).handlers["option:fill"] = this - ItemDefinition.forId(i).handlers["option:empty"] = this - ItemDefinition.forId(i).handlers["option:check"] = this - ItemDefinition.forId(i).handlers["option:drop"] = this - } - return this - } - - override fun handle(player: Player, node: Node, option: String): Boolean { - val rEssAmt = player.inventory.getAmount(Items.RUNE_ESSENCE_1436) - val pEssAmt = player.inventory.getAmount(Items.PURE_ESSENCE_7936) - var preferenceFlag = 0 //0 -> rune ess, 1 -> pure ess - if(rEssAmt - pEssAmt == 0 && option == "fill") return true - if(rEssAmt > pEssAmt) preferenceFlag = 0 else preferenceFlag = 1 - - val essence = Item( - if(preferenceFlag == 0) Items.RUNE_ESSENCE_1436 else Items.PURE_ESSENCE_7936, - 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)) - } - } - return true - } - - override fun isWalk(): Boolean { - return false - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/abyss/AbyssPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/abyss/AbyssPlugin.kt deleted file mode 100644 index 72c50b41d..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/runecrafting/abyss/AbyssPlugin.kt +++ /dev/null @@ -1,470 +0,0 @@ -package core.game.node.entity.skill.runecrafting.abyss - -import api.ContentAPI -import rs09.plugin.PluginManager.definePlugin -import rs09.tools.stringtools.colorize -import rs09.game.system.SystemLogger.logInfo -import core.game.node.scenery.Scenery -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.entity.skill.gather.SkillingTool -import core.game.node.entity.skill.runecrafting.Altar -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics -import core.tools.RandomFunction -import org.rs09.consts.NPCs -import rs09.game.interaction.InteractionListener -import rs09.game.world.GameWorld - -/** - * A plugin used to handle the abyss. - * @author cfunny - */ -class AbyssPlugin : InteractionListener() { - - val OBSTACLE = AbbysalObstacle.values().filter { it != AbbysalObstacle.MINE && it != AbbysalObstacle.SQUEEZE && it != AbbysalObstacle.PASSAGE && it != AbbysalObstacle.CHOP }.map { it.option }.toTypedArray() - val miningObstacle = 7158 - val agilityObstacle = 7164 - val passage = 7154 - val chopObstacle = 7161 - - override fun defineListeners() { - definePlugin(AbyssalNPC()) - definePlugin(DarkMageDialogue()) - definePlugin(ZamorakMageDialogue()) - on(NPCs.MAGE_OF_ZAMORAK_2259, NPC, "teleport"){ player, node -> - teleport(player, node as NPC) - return@on true - } - on(NPCs.DARK_MAGE_2262, NPC, "repair-pouches"){ player, node -> - player.dialogueInterpreter.open(node.id, node, true) - return@on true - } - on(SCENERY, "exit-through"){ player, node -> - val altar = Altar.forObject(node as Scenery) - altar?.enterRift(player) - return@on true - } - on(SCENERY, *OBSTACLE){ player, node -> - val obstacle = AbbysalObstacle.forObject(node as Scenery) - obstacle!!.handle(player, node as Scenery) - return@on true - } - on(miningObstacle, SCENERY, "mine"){ player, node -> - val obstacle = AbbysalObstacle.forObject(node as Scenery) - obstacle!!.handle(player, node as Scenery) - return@on true - } - on(agilityObstacle, SCENERY, "squeeze-through"){ player, node -> - val obstacle = AbbysalObstacle.forObject(node as Scenery) - obstacle!!.handle(player, node as Scenery) - return@on true - } - on(passage, SCENERY, "go-through",){ player, node -> - val obstacle = AbbysalObstacle.forObject(node as Scenery) - obstacle!!.handle(player, node as Scenery) - return@on true - } - on(chopObstacle, SCENERY, "chop"){ player, node -> - val obstacle = AbbysalObstacle.forObject(node as Scenery) - obstacle!!.handle(player, node as Scenery) - return@on true - } - - } - - /** - * Represents an obstacle in an abbsyal. - * @author cfunny - * @date 02/11/2013 - */ - enum class AbbysalObstacle( - /** - * Represents the option. - */ - val option: String, - /** - * Represents the corssing location. - */ - val locations: Array, - /** - * Represents the object id. - */ - vararg val objects: Int - ) { - BOIL("burn-down", arrayOf(Location.create(3024, 4833, 0), Location.create(3053, 4830, 0)), 7165) { - override fun handle(player: Player, `object`: Scenery?) { - `object` ?: return - if (!ContentAPI.inInventory(player, 590, 1)) { - ContentAPI.sendMessage(player, "You don't have a tinderbox to burn it.") - return - } - player.animate(Animation(733)) - player.lock() - GameWorld.Pulser.submit(object : Pulse(1, player) { - var count = 0 - override fun pulse(): Boolean { - when (count) { - 1 -> ContentAPI.sendMessage(player, "You attempt to burn your way through..") - 4 -> return if (RandomFunction.random(100) < ContentAPI.getStatLevel( - player, - Skills.FIREMAKING - ) - ) { - ContentAPI.sendMessage( - player, - colorize("%G...and manage to burn it down and get past.") - ) - player.properties.teleportLocation = locations[getIndex(`object`)] - player.unlock() - true - } else { - ContentAPI.sendMessage(player, colorize("%RYou fail to set it on fire.")) - player.unlock() - true - } - } - count++ - return false - } - - override fun stop() { - super.stop() - player.animate(Animation(-1, Animator.Priority.HIGH)) - } - }) - } - }, - MINE("mine", arrayOf(Location.create(3030, 4821, 0), Location.create(3048, 4822, 0)), 7158, 7153) { - override fun handle(player: Player, `object`: Scenery?) { - `object` ?: return - logInfo("handled abyss mine") - val tool: SkillingTool = ContentAPI.getTool(player, true) ?: return - if (tool == null) { - ContentAPI.sendMessage(player, "You need a pickaxe in order to do that.") - return - } - player.animate(tool.getAnimation()) - player.lock() - GameWorld.Pulser.submit(object : Pulse(1, player) { - var count = 0 - override fun pulse(): Boolean { - when (count) { - 1 -> ContentAPI.sendMessage(player, "You attempt to mine your way through..") - 4 -> return if (RandomFunction.random(100) < ContentAPI.getStatLevel( - player, - Skills.MINING - ) - ) { - ContentAPI.sendMessage(player, colorize("%G...and manage to break through the rock.")) - player.properties.teleportLocation = locations[getIndex(`object`)] - player.unlock() - true - } else { - ContentAPI.sendMessage(player, colorize("%R...but fail to break-up the rock.")) - player.unlock() - true - } - } - count++ - return false - } - - override fun stop() { - super.stop() - player.animate(Animation(-1, Animator.Priority.HIGH)) - } - }) - } - }, - CHOP("chop", arrayOf(Location.create(3050, 4824, 0), Location.create(3028, 4824, 0)), 7161, 7144) { - override fun handle(player: Player, `object`: Scenery?) { - `object` ?: return - val tool: SkillingTool? = setTool(false, player) - if (tool == null) { - player.packetDispatch.sendMessage("You need an axe in order to do that.") - return - } - player.animate(tool.getAnimation()) - player.lock() - GameWorld.Pulser.submit(object : Pulse(1, player) { - var count = 0 - override fun pulse(): Boolean { - when (count) { - 1 -> ContentAPI.sendMessage(player, "You attempt to chop your way through...") - 4 -> return if (RandomFunction.random(100) < ContentAPI.getStatLevel( - player, - Skills.WOODCUTTING - ) - ) { - ContentAPI.sendMessage(player, colorize("%G...and manage to chop down the tendrils.")) - player.properties.teleportLocation = locations[getIndex(`object`)] - player.unlock() - true - } else { - ContentAPI.sendMessage(player, colorize("%RYou fail to cut through the tendrils.")) - player.unlock() - true - } - } - count++ - return false - } - - override fun stop() { - super.stop() - player.animate(Animation(-1, Animator.Priority.HIGH)) - } - }) - } - }, - SQUEEZE( - "squeeze-through", - arrayOf(Location.create(3048, 4842, 0), Location.create(3031, 4842, 0)), - 7164, - 7147 - ) { - override fun handle(player: Player, `object`: Scenery?) { - `object` ?: return - player.animate(Animation(1331)) - player.lock() - GameWorld.Pulser.submit(object : Pulse(1, player) { - var count = 0 - override fun pulse(): Boolean { - when (count) { - 1 -> ContentAPI.sendMessage(player, "You attempt to squeeze through the narrow gap...") - 4 -> return if (RandomFunction.random(100) < ContentAPI.getStatLevel( - player, - Skills.AGILITY - ) - ) { - ContentAPI.sendMessage(player, colorize("%G...and you manage to crawl through.")) - player.properties.teleportLocation = locations[getIndex(`object`)] - player.unlock() - true - } else { - ContentAPI.sendMessage(player, colorize("%RYou fail to squeeze through the narrow gap")) - player.unlock() - true - } - } - count++ - return false - } - - override fun stop() { - super.stop() - player.animate(Animation(-1, Animator.Priority.HIGH)) - } - }) - } - }, - DISTRACT("distract", arrayOf(Location.create(3029, 4841, 0), Location.create(3051, 4838, 0)), 7168, 7150) { - override fun handle(player: Player, `object`: Scenery?) { - `object` ?: return - val emotes = intArrayOf( - 855, - 856, - 857, - 858, - 859, - 860, - 861, - 862, - 863, - 864, - 865, - 866, - 2113, - 2109, - 2111, - 2106, - 2107, - 2108, - 0x558, - 2105, - 2110, - 2112, - 0x84F, - 0x850, - 1131, - 1130, - 1129, - 1128, - 1745, - 3544, - 3543, - 2836 - ) - val index: Int = RandomFunction.random(emotes.size) - player.animate(Animation(emotes[index])) - player.lock() - GameWorld.Pulser.submit(object : Pulse(1, player) { - var count = 0 - override fun pulse(): Boolean { - when (count) { - 1 -> ContentAPI.sendMessage(player, "You use your thieving skills to misdirect the eyes...") - 4 -> return if (RandomFunction.random(100) < ContentAPI.getStatLevel( - player, - Skills.THIEVING - ) - ) { - ContentAPI.sendMessage( - player, - colorize("%G...and sneak past while they're not looking.") - ) - player.properties.teleportLocation = locations[getIndex(`object`)] - player.unlock() - true - } else { - ContentAPI.sendMessage(player, colorize("%RYou fail to distract the eyes.")) - player.unlock() - true - } - } - count++ - return false - } - - override fun stop() { - super.stop() - player.animate(Animation(-1, Animator.Priority.HIGH)) - } - }) - } - }, - PASSAGE("go-through", arrayOf(Location.create(3040, 4844, 0)), 7154) { - override fun handle(player: Player, `object`: Scenery?) { - player.properties.teleportLocation = locations[0] - } - }; - /** - * Gets the option. - * @return The option. - */ - /** - * Gets the locations. - * @return The locations. - */ - /** - * Gets the objects. - * @return The objects. - */ - - /** - * Method used to get the index. - * @param object the object. - * @return the index. - */ - fun getIndex(`object`: Scenery): Int { - for (i in objects.indices) { - if (objects[i] == `object`.getId()) { - return i - } - } - return 0 - } - - /** - * Method used to handle the obstacle. - * @param player the player. - * @param object the object. - */ - open fun handle(player: Player, `object`: Scenery?) {} - - companion object { - /** - * Method used to get the abbysal obstacle. - * @param object the object. - * @return the `AbbysalObstacle` or `Null`. - */ - fun forObject(`object`: Scenery): AbbysalObstacle? { - for (obstacle in values()) { - for (i in obstacle.objects) { - if (i == `object`.getId()) { - return obstacle - } - } - } - return null - } - - /** - * Sets the tool used. - */ - private fun setTool(mining: Boolean, player: Player): SkillingTool? { - var tool: SkillingTool? = null - if (!mining) { - if (checkTool(player, SkillingTool.DRAGON_AXE)) { - tool = SkillingTool.DRAGON_AXE - } else if (checkTool(player, SkillingTool.RUNE_AXE)) { - tool = SkillingTool.RUNE_AXE - } else if (checkTool(player, SkillingTool.ADAMANT_AXE)) { - tool = SkillingTool.ADAMANT_AXE - } else if (checkTool(player, SkillingTool.MITHRIL_AXE)) { - tool = SkillingTool.MITHRIL_AXE - } else if (checkTool(player, SkillingTool.BLACK_AXE)) { - tool = SkillingTool.BLACK_AXE - } else if (checkTool(player, SkillingTool.STEEL_AXE)) { - tool = SkillingTool.STEEL_AXE - } else if (checkTool(player, SkillingTool.IRON_AXE)) { - tool = SkillingTool.IRON_AXE - } else if (checkTool(player, SkillingTool.BRONZE_AXE)) { - tool = SkillingTool.BRONZE_AXE - } - } else { - if (checkTool(player, SkillingTool.RUNE_PICKAXE)) { - tool = SkillingTool.RUNE_PICKAXE - } else if (checkTool(player, SkillingTool.ADAMANT_PICKAXE)) { - tool = SkillingTool.ADAMANT_PICKAXE - } else if (checkTool(player, SkillingTool.MITHRIL_PICKAXE)) { - tool = SkillingTool.MITHRIL_PICKAXE - } else if (checkTool(player, SkillingTool.STEEL_PICKAXE)) { - tool = SkillingTool.STEEL_PICKAXE - } else if (checkTool(player, SkillingTool.IRON_PICKAXE)) { - tool = SkillingTool.IRON_PICKAXE - } else if (checkTool(player, SkillingTool.BRONZE_PICKAXE)) { - tool = SkillingTool.BRONZE_PICKAXE - } - } - return tool - } - - /** - * Checks if the player has a tool and if he can use it. - * @param tool The tool. - * @return `True` if the tool is usable. - */ - private fun checkTool(player: Player, tool: SkillingTool): Boolean { - return if (player.equipment.contains(tool.getId(), 1)) { - true - } else player.inventory.contains(tool.getId(), 1) - } - } - } - - companion object { - /** - * Represents the teleporting to the abyss. - * @param player the player. - */ - fun teleport(player: Player, npc: NPC) { - player.lock(3) - npc.visualize(Animation(1979), Graphics(4)) - npc.sendChat("Veniens! Sallakar! Rinnesset!") - player.skills.decrementPrayerPoints(100.0) - player.skullManager.checkSkull(player) - GameWorld.Pulser.submit(object : Pulse(2, player) { - override fun pulse(): Boolean { - player.properties.teleportLocation = Location.create(3021, 4847, 0) - npc.updateMasks.reset() - return true - } - }) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt deleted file mode 100644 index eb6cc6f62..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapeEquipmentPlugin.kt +++ /dev/null @@ -1,40 +0,0 @@ -package rs09.game.node.entity.skill.skillcapeperks - -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 - -@Initializable -class SkillcapeEquipmentPlugin : Plugin { - override fun newInstance(arg: Item?): Plugin { - for(cape in core.game.content.global.SkillcapePerks.values()){ - cape.skillcapeIds.forEach { - ItemDefinition.forId(it).handlers["equipment"] = this - } - } - return this - } - - override fun fireEvent(identifier: String?, vararg args: Any?): Any { - val player = args[0] as Player - val id = (args[1] as Item).id - - val skillcape = Skillcape.forId(id) - val perk = SkillcapePerks.forSkillcape(skillcape) - - when(identifier){ - - "equip" -> { - perk.activate(player) - } - - "unequip" -> { - perk.deactivate(player) - } - } - return true; - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapePerks.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapePerks.kt deleted file mode 100644 index 79802c385..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/skillcapeperks/SkillcapePerks.kt +++ /dev/null @@ -1,253 +0,0 @@ -package rs09.game.node.entity.skill.skillcapeperks - -import core.game.component.Component -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.SpellBookManager -import core.game.node.entity.player.link.TeleportManager -import core.game.node.entity.skill.runecrafting.Altar -import core.game.world.map.zone.impl.DarkZone -import core.plugin.Initializable -import rs09.game.world.GameWorld - -enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? = null) { - BAREFISTED_SMITHING("cape_perks:barefisted-smithing"), - DIVINE_FAVOR("cape_perks:divine-favor"), - CONSTANT_GLOW("cape_perks:eternal-glow"), - PRECISION_MINER("cape_perks:precision-miner"), - GREAT_AIM("cape_perks:great-aim"), - NEST_HUNTER("cape_perks:nest-hunter"), - PRECISION_STRIKES("cape_perks:precision-strikes"), - FINE_ATTUNEMENT("cape_perks:fine-attunement"), - GRAND_BULLWARK("cape_perks:grand-bullwark"), - ACCURATE_MARKSMAN("cape_perks:accurate-marksman"), - 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) - 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)) - } - }), - 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)) - } else { - player.dialogueInterpreter.sendDialogue("Your cape is still on cooldown.","Ready in " + java.util.concurrent.TimeUnit.MILLISECONDS.toMinutes(time - System.currentTimeMillis()) + " minutes.") - } - }), - SEED_ATTRACTION("cape_perks:seed_attract",{player -> - val time = player.getAttribute("cape_perks:seed_attract_timer",0L) - if(System.currentTimeMillis() > time){ - val possibleSeeds = rs09.game.node.entity.skill.farming.Plantable.values() - for(i in 0 until 10){ - var seed = possibleSeeds.random() - while(seed == rs09.game.node.entity.skill.farming.Plantable.SCARECROW || seed.applicablePatch == rs09.game.node.entity.skill.farming.PatchType.FRUIT_TREE || seed.applicablePatch == rs09.game.node.entity.skill.farming.PatchType.TREE || seed.applicablePatch == rs09.game.node.entity.skill.farming.PatchType.SPIRIT_TREE){ - seed = possibleSeeds.random() - } - val reward = core.game.node.item.Item(seed.itemID) - if(!player.inventory.add(reward)){ - core.game.node.item.GroundItemManager.create(reward,player) - } - } - 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.") - } - }), - TRICKS_OF_THE_TRADE("cape_perks:tott",{player -> - val hasHelmetBonus = api.ContentAPI.getAttribute(player, "cape_perks:tott:helmet-stored", false) - if(hasHelmetBonus){ - api.ContentAPI.sendDialogue(player, "Your cape's pockets are lined with all the utilities you need for slayer.") - } else { - api.ContentAPI.sendDialogue(player, "Your cape is lined with empty pockets shaped like various utilities needed for slayer.") - } - }), - NONE("cape_perks:none") - ; - - companion object{ - @JvmStatic - fun isActive(perk: SkillcapePerks, player: Player): Boolean{ - return player.getAttribute(perk.attribute,false) - } - - fun forSkillcape(skillcape: Skillcape): SkillcapePerks{ - return when(skillcape){ - Skillcape.ATTACK -> PRECISION_STRIKES - Skillcape.STRENGTH -> FINE_ATTUNEMENT - Skillcape.DEFENCE -> GRAND_BULLWARK - Skillcape.RANGING -> ACCURATE_MARKSMAN - Skillcape.PRAYER -> DIVINE_FAVOR - Skillcape.MAGIC -> LIBRARIAN_MAGUS - Skillcape.RUNECRAFTING -> ABYSS_WARPING - Skillcape.HITPOINTS -> DAMAGE_SPONG - Skillcape.AGILITY -> MARATHON_RUNNER - Skillcape.HERBLORE -> NONE - Skillcape.THIEVING -> NONE - Skillcape.CRAFTING -> NONE - Skillcape.FLETCHING -> NONE - Skillcape.SLAYER -> TRICKS_OF_THE_TRADE - Skillcape.CONSTRUCTION -> NONE - Skillcape.MINING -> PRECISION_MINER - Skillcape.SMITHING -> BAREFISTED_SMITHING - Skillcape.FISHING -> GREAT_AIM - Skillcape.COOKING -> NONE - Skillcape.FIREMAKING -> CONSTANT_GLOW - Skillcape.WOODCUTTING -> NONE - Skillcape.FARMING -> SEED_ATTRACTION - Skillcape.HUNTING -> NONE - Skillcape.SUMMONING -> NONE - else -> NONE - } - } - } - - fun activate(player: Player){ - if(GameWorld.settings?.skillcape_perks != true){ - return - } - if(!isActive(this,player)){ - player.setAttribute("/save:$attribute",true) - } - player.debug("Activated ${this.name}") - if(this == CONSTANT_GLOW) - DarkZone.checkDarkArea(player) - } - - fun operate(player: Player){ - if(GameWorld.settings?.skillcape_perks != true){ - player.sendMessage("This item can not be operated.") - return - } - effect?.invoke(player) - } - - fun deactivate(player: Player){ - player.removeAttribute(attribute) - if(this == CONSTANT_GLOW) - DarkZone.checkDarkArea(player) - } - - @Initializable - class MagicCapeDialogue(player: Player? = null): DialoguePlugin(player){ - override fun newInstance(player: Player?): 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 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.LUNAR.interfaceId -> { - when(buttonId){ - 1 -> SpellBookManager.SpellBook.MODERN - 2 -> SpellBookManager.SpellBook.ANCIENT - 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) - } - - } - - @Initializable - class RCCapeDialogue(player: Player? = null) : DialoguePlugin(player){ - override fun newInstance(player: Player?): DialoguePlugin { - return RCCapeDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - options("Air","Mind","Water","Earth","More...") - stage = 0; - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - 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++ } - } - 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++ } - } - 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++ } - } - 3 -> when(buttonId){ - 1 -> sendAltar(player,Altar.BLOOD) - 2 -> end() - } - } - return true - } - - fun sendAltar(player: Player,altar: Altar){ - end() - player.teleporter.send(altar.ruin.end,TeleportManager.TeleportType.TELE_OTHER) - player.incrementAttribute("/save:cape_perks:abyssal_warp",-1) - } - - override fun getIds(): IntArray { - return intArrayOf(509871233) - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerUtils.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerUtils.kt deleted file mode 100644 index cf4cd4170..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package rs09.game.node.entity.skill.slayer - -import core.game.node.entity.combat.BattleState -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.SpellBookManager.SpellBook -import org.rs09.consts.Items - -class SlayerUtils(val player: Player) { - - fun hasBroadWeaponEquipped(state: BattleState): Boolean { - return (state.weapon != null && state.weapon.id == Items.LEAF_BLADED_SPEAR_4158 || - state.weapon != null && state.weapon.id == Items.LEAF_BLADED_SWORD_13290 || - state.ammunition != null && (state.ammunition.itemId == Items.BROAD_ARROW_4160 || state.ammunition.itemId == Items.BROAD_TIPPED_BOLTS_13280) || - state.spell != null && state.spell.spellId == 31 && player.spellBookManager - .spellBook == SpellBook.MODERN.interfaceId - ) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/ThievingListeners.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/ThievingListeners.kt deleted file mode 100644 index 7550f392f..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/thieving/ThievingListeners.kt +++ /dev/null @@ -1,94 +0,0 @@ -package rs09.game.node.entity.skill.thieving - -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 rs09.game.interaction.InteractionListener -import rs09.game.system.SystemLogger -import rs09.tools.secondsToTicks - -class ThievingListeners : InteractionListener() { - - private val PICKPOCKET_ANIM = Animation(881,Animator.Priority.HIGH) - private val NPC_ANIM = Animation(422) - private val STUN_ANIMATION = Animation(424,Animator.Priority.VERY_HIGH) - private val SOUND = Audio(2727, 1, 0) - - override fun defineListeners() { - - on(NPC,"pickpocket","pick-pocket"){player, node -> - val pickpocketData = Pickpockets.forID(node.id) ?: return@on false - var successMod = 0 - SystemLogger.logInfo(pickpocketData.table.toString()) - - if(player.inCombat()){ - player.sendMessage("You can't pickpocket while in combat.") - return@on true - } - - if(player.skills.getLevel(Skills.THIEVING) < pickpocketData.requiredLevel){ - player.sendMessage("You need a Thieving level of ${pickpocketData.requiredLevel} to do that.") - return@on true - } - - if(!pickpocketData.table.canRoll(player)){ - player.sendMessage("You don't have enough inventory space to do that.") - return@on true - } - - if(pickpocketData == Pickpockets.FEMALE_HAM_MEMBER || pickpocketData == Pickpockets.MALE_HAM_MEMBER){ - successMod += getHAMItemCount(player) - } - - if(player.equipment.contains(Items.GLOVES_OF_SILENCE_10075,1)){ - successMod += 3 - } - - player.animator.animate(PICKPOCKET_ANIM) - val chance = RandomFunction.randomDouble(1.0,100.0) - val failTreshold = pickpocketData.getSuccessChance(player) + successMod - - if(chance > failTreshold){ - player.stateManager.set(EntityState.STUNNED, secondsToTicks(pickpocketData.stunTime)) - node.asNpc().face(player) - node.asNpc().animator.animate(NPC_ANIM) - player.animator.animate(STUN_ANIMATION) - player.audioManager.send(SOUND) - player.lock(secondsToTicks(pickpocketData.stunTime)) - player.impactHandler.manualHit(node.asNpc(),RandomFunction.random(pickpocketData.stunDamageMin,pickpocketData.stunDamageMax),ImpactHandler.HitsplatType.NORMAL) - node.asNpc().face(null) - } else { - player.lock(2) - pickpocketData.table.roll(player).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/kotlin/rs09/game/node/entity/state/newsys/PlayerState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/PlayerState.kt deleted file mode 100644 index 1facb3406..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/PlayerState.kt +++ /dev/null @@ -1,3 +0,0 @@ -package rs09.game.node.entity.state.newsys - -annotation class PlayerState(val key: String) diff --git a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/State.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/State.kt deleted file mode 100644 index b42bdfbf6..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/State.kt +++ /dev/null @@ -1,42 +0,0 @@ -package rs09.game.node.entity.state.newsys - -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import org.json.simple.JSONObject -import rs09.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/kotlin/rs09/game/node/entity/state/newsys/StateRepository.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/StateRepository.kt deleted file mode 100644 index 5617a48b6..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/StateRepository.kt +++ /dev/null @@ -1,34 +0,0 @@ -package rs09.game.node.entity.state.newsys - -import core.game.node.entity.player.Player -import io.github.classgraph.ClassGraph - -object StateRepository { - val states = HashMap() - - @JvmStatic - fun init() { - val result = ClassGraph().enableClassInfo().enableAnnotationInfo().acceptPackages("rs09.game.node.entity.state").scan() - result.getClassesWithAnnotation("rs09.game.node.entity.state.newsys.PlayerState").forEach{ - val key = it.getAnnotationInfo("rs09.game.node.entity.state.newsys.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/kotlin/rs09/game/node/entity/state/newsys/states/AvaDeviceState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/AvaDeviceState.kt deleted file mode 100644 index c752691f8..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/AvaDeviceState.kt +++ /dev/null @@ -1,97 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -import core.game.container.impl.EquipmentContainer -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.tools.RandomFunction -import org.json.simple.JSONObject -import org.rs09.consts.Items -import rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.State -import rs09.game.system.config.ItemConfigParser -import kotlin.math.ceil - -@PlayerState("avadevice") -class AvaDeviceState(player: Player? = null) : State(player) { - val TICKS = ceil(180 / 0.6).toInt() - var device = 0 - - private val ATTRACTOR_REWARDS = arrayOf(Items.IRON_BAR_2351, - Items.IRON_KNIFE_863,Items.IRON_DART_807,Items.IRON_DAGGER_1203,Items.IRON_BOLTS_9140,Items.IRON_ARROW_884,Items.IRON_ORE_440,Items.COPPER_ORE_436,Items.IRON_FULL_HELM_1153,Items.IRON_2H_SWORD_1309,Items.STEEL_BAR_2353) - private val ACCUMULATOR_REWARDS = arrayOf(Items.STEEL_BAR_2353,Items.STEEL_2H_SWORD_1311,Items.STEEL_KNIFE_865,Items.STEEL_DAGGER_1207,Items.STEEL_MED_HELM_1141,Items.STEEL_DART_808,Items.STEEL_BOLTS_9141,Items.STEEL_ARROW_886,Items.IRON_BAR_2351) - - override fun save(root: JSONObject) { - root.put("device-id",device) - } - - override fun parse(_data: JSONObject) { - if(_data.containsKey("device-id")){ - device = _data["device-id"].toString().toInt() - } - } - - override fun newInstance(player: Player?): State { - return AvaDeviceState(player) - } - - override fun createPulse() { - player ?: return - if(device == 0 && !hasDevice(player)) return - if(player.savedData.globalData.isAvasDisabled) return - - pulse = object : Pulse(TICKS){ - override fun pulse(): Boolean { - if(!hasDevice(player)){ - player.clearState("avadevice") - pulse = null - return true - } - - if(isInterfered()) return false - - if(RandomFunction.random(10) > 4) return false - - val rewards = if(isAccumulator()) ACCUMULATOR_REWARDS else ATTRACTOR_REWARDS - - val item = Item(rewards.random()) - - if (item.definition.getConfiguration(ItemConfigParser.EQUIP_SLOT, -1) == EquipmentContainer.SLOT_ARROWS) { - val arrowSlot = player.equipment[EquipmentContainer.SLOT_ARROWS] - if (arrowSlot == null || arrowSlot.id == item.getId()) { - player.equipment.add(item, true, EquipmentContainer.SLOT_ARROWS) - return false - } - } - player.inventory.add(item, player) - - return !hasDevice(player) - } - } - } - - fun hasDevice(player: Player?) : Boolean{ - player ?: return false - return player.equipment.containsAtLeastOneItem(intArrayOf(Items.AVAS_ACCUMULATOR_10499,Items.AVAS_ATTRACTOR_10498)) - } - - fun isAccumulator() : Boolean{ - return device == Items.AVAS_ACCUMULATOR_10499 - } - - //just took this method from the old arios pulse as well (I'm being lazy) - private fun isInterfered(): Boolean { - player ?: return true - val cape = player.equipment[EquipmentContainer.SLOT_CAPE] - if (cape != null && (cape.id == 10498 || cape.id == 10499)) { - val torso = player.equipment[EquipmentContainer.SLOT_CHEST] - val modelId = torso?.definition?.maleWornModelId1 ?: -1 - if (modelId == 301 || modelId == 306 || modelId == 3379) { - player.packetDispatch.sendMessage("Your armour interferes with Ava's device.") - return true - } - } - return false - } - -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/DiseasedState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/DiseasedState.kt deleted file mode 100644 index 5e3eae436..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/DiseasedState.kt +++ /dev/null @@ -1,53 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -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 rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.State -import rs09.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/kotlin/rs09/game/node/entity/state/newsys/states/FarmingState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/FarmingState.kt deleted file mode 100644 index c785b9712..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/FarmingState.kt +++ /dev/null @@ -1,173 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -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 rs09.game.node.entity.skill.farming.* -import rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.State -import rs09.game.system.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("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((patch.currentGrowthStage < patch.plantable?.stages ?: 0) && !patch.isWeedy()){ - val startTime = (patch.nextGrowth - TimeUnit.MINUTES.toMillis(patch.patch.type.stageGrowthTime * (patch.currentGrowthStage + 1).toLong())) - var expectedStage = Math.floor((System.currentTimeMillis() - startTime.toDouble()) / TimeUnit.MINUTES.toMillis(patch.patch.type.stageGrowthTime.toLong())).toInt() - SystemLogger.logErr("$expectedStage $startTime ${System.currentTimeMillis()}") - - if(!patchDiseased && !patchDead) { - if (expectedStage > patch.plantable?.stages ?: 0) { - expectedStage = patch.plantable?.stages ?: 0 - } - for(i in 0 until (expectedStage - patch.currentGrowthStage)){ - patch.update() - } - } - } - - 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 - } - } - } - } - - 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() - var minutes = patch.patch.type.stageGrowthTime.toLong() - if(patch.patch.type == PatchType.FRUIT_TREE && patch.isGrown()) { - // Fruit trees take 160 minutes per stage to grow, but - // restocking their fruit should take 40 minutes per fruit - minutes = 40 - } - patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(minutes) - } - - } - - for((_,bin) in binMap){ - if(bin.isReady() && !bin.isFinished){ - bin.finish() - } - } - } - - return false - } - } - } -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/GodspellChargedState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/GodspellChargedState.kt deleted file mode 100644 index dfa3be5d7..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/GodspellChargedState.kt +++ /dev/null @@ -1,44 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import org.json.simple.JSONObject -import rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.State -import rs09.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") - pulse = null - return true - } - } - } - -} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/IncubatorState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/IncubatorState.kt deleted file mode 100644 index e4a43c354..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/IncubatorState.kt +++ /dev/null @@ -1,53 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -import core.game.node.entity.player.Player -import core.game.node.entity.skill.summoning.pet.IncubatorEgg -import rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.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/kotlin/rs09/game/node/entity/state/newsys/states/SeedlingState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/SeedlingState.kt deleted file mode 100644 index e6ba38ab3..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/SeedlingState.kt +++ /dev/null @@ -1,84 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -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 rs09.game.node.entity.skill.farming.Seedling -import rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.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/kotlin/rs09/game/node/entity/state/newsys/states/ShootingStarState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/ShootingStarState.kt deleted file mode 100644 index eed3442ee..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/ShootingStarState.kt +++ /dev/null @@ -1,47 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -import core.game.node.entity.player.Player -import rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.State -import core.game.system.task.Pulse -import rs09.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/kotlin/rs09/game/node/entity/state/newsys/states/SkulledState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/SkulledState.kt deleted file mode 100644 index e8868370d..000000000 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/SkulledState.kt +++ /dev/null @@ -1,45 +0,0 @@ -package rs09.game.node.entity.state.newsys.states - -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import org.json.simple.JSONObject -import rs09.game.node.entity.state.newsys.PlayerState -import rs09.game.node.entity.state.newsys.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/kotlin/rs09/game/system/SystemLogger.kt b/Server/src/main/kotlin/rs09/game/system/SystemLogger.kt deleted file mode 100644 index a51547784..000000000 --- a/Server/src/main/kotlin/rs09/game/system/SystemLogger.kt +++ /dev/null @@ -1,89 +0,0 @@ -package rs09.game.system - -import com.github.ajalt.mordant.rendering.TextColors -import com.github.ajalt.mordant.terminal.Terminal -import rs09.ServerConstants -import java.io.* -import java.text.SimpleDateFormat -import java.util.* - -/** - * Handles server log printing - * @author Ceikry - * Thanks to the awesome library made by AJ Alt - */ -object SystemLogger { - val t = Terminal() - val formatter = SimpleDateFormat("HH:mm:ss") - var tradeLog: Writer? = null - var tradeLogWriter: BufferedWriter? = null - - @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{ - return "[" + formatter.format(Date(System.currentTimeMillis())) +"]" - } - - @JvmStatic - fun logInfo(vararg messages: String){ - for(m in messages){ - if(m.isNotBlank()) t.println("${getTime()}: [INFO] $m") - } - } - - @JvmStatic - fun logErr(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.brightRed("[ ERR] $message")}") - } - - @JvmStatic - fun logWarn(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.yellow("[WARN] $message")}") - } - - @JvmStatic - fun logAlert(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.brightYellow("[ALRT] $message")}") - } - - @JvmStatic - fun logAI(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[AIPL] $message")}") - } - - @JvmStatic - fun logRE(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[RAND] $message")}") - } - - @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("Trade Logger is null!") - tradeLogWriter?.write("${getTime()}: $message") - tradeLogWriter?.newLine() - } - } catch(ignored: Exception){} - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/Command.kt b/Server/src/main/kotlin/rs09/game/system/command/Command.kt deleted file mode 100644 index 44b11f517..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/Command.kt +++ /dev/null @@ -1,43 +0,0 @@ -package rs09.game.system.command - -import core.game.node.entity.player.Player -import rs09.game.world.GameWorld - -/** - * Base class for Commands in the new system. Can pass a lambda as part of the constructor or after the constructor. - * @author Ceikry - */ -class Command(val name: String, val privilege: Privilege, val handle: (Player, Array) -> Unit) { - fun attemptHandling(player: Player, args: Array?){ - args ?: return - if(player.rights.ordinal >= privilege.ordinal || GameWorld.settings?.isDevMode == true){ - handle(player,args) - } - } - - enum class Privilege{ - STANDARD, - MODERATOR, - ADMIN - } -} - -object CommandMapping { - private val mapping = hashMapOf() - - fun get(name: String): Command?{ - return mapping[name] - } - - fun register(command: Command){ - mapping[command.name] = command - } - - fun getCommands(): Array { - return mapping.values.toTypedArray() - } - - fun getNames(): Array { - return mapping.keys.toTypedArray() - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/oldsys/AIPCommandPlugin.kt b/Server/src/main/kotlin/rs09/game/system/command/oldsys/AIPCommandPlugin.kt deleted file mode 100644 index 71c61da18..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/oldsys/AIPCommandPlugin.kt +++ /dev/null @@ -1,329 +0,0 @@ -package rs09.game.system.command.oldsys - -//import rs09.game.ai.general.scriptrepository.ManThiever -import core.game.container.impl.EquipmentContainer -import core.game.interaction.Interaction -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.appearance.Gender -import core.game.node.entity.skill.Skills -import rs09.game.system.command.CommandPlugin -import core.game.system.command.CommandSet -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import rs09.game.world.ImmerseWorld -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.map.path.Pathfinder -import rs09.game.world.repository.Repository -import core.plugin.Initializable -import core.plugin.Plugin -import core.tools.RandomFunction -import rs09.game.ai.AIPBuilder -import rs09.game.ai.AIPlayer -import rs09.game.ai.general.GeneralBotCreator -import rs09.game.ai.general.scriptrepository.LobsterCatcher -import rs09.game.ai.pvmbots.PvMBotsBuilder -import rs09.game.ai.pvp.PVPAIPActions -import rs09.game.ai.pvp.PVPAIPBuilderUtils -import rs09.game.ai.resource.ResourceAIPActions -import rs09.game.ai.skillingbot.SkillingBotsBuilder -import rs09.game.ai.wilderness.PvPBotsBuilder -import java.util.* - -/** - * Handles the AIPlayer commands. - * These commands are for bots - * - * @author Emperor - */ -@Initializable -class AIPCommandPlugin : CommandPlugin() { - override fun newInstance(arg: Any?): Plugin? { - link(CommandSet.ADMINISTRATOR) - return this - } - - override fun parse(player: Player?, name: String?, args: Array?): Boolean { - var name = name - var legion = player!!.getAttribute>("aip_legion") - when (name) { - "desaip" -> { - player.removeAttribute("aip_select") - return true - } - "sellegion" -> { - if (legion != null && !legion.isEmpty()) { - player.setAttribute("aip_select", legion[0]) - } - return true - } - "regroup" -> { - var last = player - if (legion != null && !legion.isEmpty()) { - for (p in legion) { - p.follow(last) - last = p - } - } - player.removeAttribute("aip_select") - return true - } - "clearlegion" -> { - if (legion != null && !legion.isEmpty()) { - for (p in legion) { - AIPlayer.deregister(p.uid) - } - legion.clear() - } - player.removeAttribute("aip_select") - player.removeAttribute("aip_legion") - return true - } - "clearaips" -> { - for (p in Repository.players) { - if (p.isArtificial) { - p.clear() - } - } - return true - } - "aip" -> { - name = if (args!!.size < 2) player.name else args[1] - val p = AIPBuilder.copy(player, player.location.transform(0, 1, 0)) - p.init() - Interaction.sendOption(player, 7, "Control") - return true - } - "legion" -> { - val size = if (args!!.size < 2) 10 else args[1]!!.toInt() - var last = player - if (legion == null) { - player.setAttribute("aip_legion", ArrayList().also { legion = it }) - } - Interaction.sendOption(player, 7, "Control") - val joinClan = player.communication.clan != null && !player.communication.clan.isDefault - var i = 0 - while (i < size) { - val aip = AIPBuilder.copy(player, last!!.getLocation().transform(0, 1, 0)) - aip.init() - if (legion!!.isEmpty()) { - aip.setAttribute("aip_legion", legion) - } - legion!!.add(aip) - val l: Player = last!! - if (joinClan) { - if (player.communication.clan.enter(aip)) { - aip.communication.clan = player.communication.clan - } - if (player.communication.clan.clanWar != null) { - player.communication.clan.clanWar.fireEvent("join", aip) - } - } - GameWorld.Pulser.submit(object : Pulse(1) { - override fun pulse(): Boolean { - aip.follow(l) - return true - } - }) - last = aip - i++ - } - return true - } - "banktest" -> { - val bot = AIPlayer(player.location) - //Location.create(2719, 3431, 0) - //Location.create(2726, 3477, 0) - //Location.create(2726, 3491, 0) - Pathfinder.find(bot,Location.create(2726, 3491, 0)).walk(bot) - return true - } - "pvplegion" -> { - val size = if (args!!.size < 2) 10 else args[1]!!.toInt() - var last = player - if (PVPAIPActions.pvp_players == null) { - player.setAttribute("aip_legion", ArrayList().also { PVPAIPActions.pvp_players = it }) - } - var i = 0 - while (i < size) { - val aip = AIPBuilder.create(generateLocation(player)) - aip.controler = player - aip.appearance.gender = if (RandomFunction.random(3) == 1) Gender.FEMALE else Gender.MALE - aip.init() - PVPAIPBuilderUtils.generateClass(aip) - if (PVPAIPActions.pvp_players.isEmpty()) { - aip.setAttribute("aip_legion", PVPAIPActions.pvp_players) - } - PVPAIPActions.pvp_players.add(aip) - last = aip - i++ - } - return true - } - "resourcelegion" -> { - val size = if (args!!.size < 2) 10 else args[1]!!.toInt() - var last = player - if (ResourceAIPActions.resource_players == null) { - player.setAttribute("aip_legion", ArrayList().also { ResourceAIPActions.resource_players = it }) - } - var i = 0 - while (i < size) { - val aip = AIPBuilder.create(generateLocation(player)) - aip.controler = player - aip.appearance.gender = if (RandomFunction.random(3) == 1) Gender.FEMALE else Gender.MALE - aip.init() - PVPAIPBuilderUtils.generateClass(aip) - if (ResourceAIPActions.resource_players.isEmpty()) { - aip.setAttribute("aip_legion", ResourceAIPActions.resource_players) - } - ResourceAIPActions.resource_players.add(aip) - i++ - } - return true - } - "syncresource" -> ResourceAIPActions.syncBotThread(player) - "pvpfight" -> { - PVPAIPActions.syncBotThread(player) - return true - } - "bot" -> { - PvMBotsBuilder.spawnLowest(player.location) - return true - } - "molebot" -> { - PvMBotsBuilder.spawnGiantMoleBot(player.location) - return true - } - "slayerpoints" -> { - player.slayer.slayerPoints = 50000 - return true - } - "dragonbot" -> { - PvMBotsBuilder.spawnDragonKiller(player.location) - return true - } - "pure" -> { - player.skills.setStaticLevel(Skills.HITPOINTS, 60) - player.skills.setStaticLevel(Skills.RANGE, 95) - player.skills.setStaticLevel(Skills.MAGIC, 95) - player.skills.setStaticLevel(Skills.ATTACK, 50) - player.skills.setStaticLevel(Skills.STRENGTH, 93) - player.skills.setStaticLevel(Skills.DEFENCE, 1) - player.skills.setStaticLevel(Skills.PRAYER, 1) - player.skills.updateCombatLevel() - return true - } - "noobbot" -> { - PvMBotsBuilder.spawnNoob(player.location) - return true - } - "immerse", "immersiveworld", "immersive" -> { - ImmerseWorld.init() - player.sendMessage("Started immersive world, 2") - return true - } - "botdataform" -> { - //Dumps your current character info in the form used by data/botdata - //name:cblevel:helmet:cape:neck:weapon:chest:shield:unknown:legs:unknown:gloves:boots: - println(player.username + ":" - + player.properties.currentCombatLevel + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_HAT) + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_CAPE) + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_AMULET) + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_WEAPON) + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_CHEST) + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_SHIELD) + ":" - + "0" + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_LEGS) + ":" - + "0" + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_HANDS) + ":" - + player.equipment.getAsId(EquipmentContainer.SLOT_FEET) + ":" - ) - return true - } - "fishtest" -> { - SkillingBotsBuilder.spawnTroutLumbridge(Location(3241, 3242)) - return true - } - "varrockminebots" -> { - SkillingBotsBuilder.spawnClayBotVarrock(Location(3181, 3368)) - SkillingBotsBuilder.spawnSilverBotVarrock(Location(3181, 3368)) - SkillingBotsBuilder.spawnIronBotVarrock(Location(3181, 3368)) - SkillingBotsBuilder.spawnTinBotVarrock(Location(3181, 3368)) - return true - } - "pvpbot" -> { - PvPBotsBuilder.spawn(player.location) - return true - } - "pvpbots" -> { - var amountBots = 0 - while (amountBots < 50) { - PvPBotsBuilder.spawn(player.location) - amountBots++ - } - return true - } - "ranger" -> { - } - "removetask" -> return if (!player.slayer.hasTask()) { - player.sendMessage("You don't have an active task right now.") - true - } else { - player.slayer.clear() - player.sendMessage("You have canceled your current task.") - true - } - "testpest", "pcbots", "pestcontrolbots", "pest-test", "test-pest", "pesttest" -> { - player.sendMessage("Spawning some bots I think") - val arg2: Int - arg2 = try { - args!![1]!!.toInt() - } catch (e: Exception) { - 20 - } - var pestBotsAmount = 0 - while (pestBotsAmount < arg2) { - PvMBotsBuilder.createPestControlTestBot(player.location) - pestBotsAmount++ - } - return true - } - "bots" -> { - var arg = 1 - var xpos = 0 - var ypos = 0 - try { - arg = args!![1]!!.toInt() - } catch (e: Exception) { - println("Rip " + args!![1]) - } - var amountBots2 = 0 - while (amountBots2 < arg) { - xpos = 2500 + RandomFunction.getRandom(1000) - ypos = 3000 + RandomFunction.getRandom(500) - PvMBotsBuilder.spawnNoob(Location(xpos, ypos)) - amountBots2++ - } - println("$xpos $ypos") - return true - } - //"manthiev" -> GeneralBotCreator(player.location, ManThiever()) - "fish" -> GeneralBotCreator(Location.create(2805, 3435, 0), LobsterCatcher()) - } - return false - } - - private fun generateLocation(player: Player?): Location { - val random_location = player!!.location.transform(RandomFunction.random(-15, 15), RandomFunction.random(-15, 15), 0) - if (!RegionManager.isTeleportPermitted(random_location)) { - return generateLocation(player) - } - if (!Pathfinder.find(player, random_location, false, Pathfinder.DUMB).isSuccessful) { - return generateLocation(player) - } - return if (RegionManager.getObject(random_location) != null) { - generateLocation(player) - } else random_location - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt b/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt deleted file mode 100644 index cf30b98c1..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt +++ /dev/null @@ -1,43 +0,0 @@ -package rs09.game.system.command.rottenpotato - -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.plugin.Initializable -import core.plugin.Plugin -import org.rs09.consts.Items -import rs09.game.system.command.CommandSystem -import rs09.tools.stringtools.colorize - -/** - * Handles the Rotten Potato options - * @author Ceikry - */ -private val ROTTEN_POTATO_OPTIONS = arrayOf("RS HD","Heal","Extra","Commands") -@Initializable -class RottenPotatoOptionHandler : OptionHandler() { - override fun newInstance(arg: Any?): Plugin { - for(option in ROTTEN_POTATO_OPTIONS) { - ItemDefinition.forId(Items.ROTTEN_POTATO_5733).handlers["option:${option.toLowerCase()}"] = this - } - return this; - } - - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - player ?: return false - node ?: return false - option ?: return false - - when(option){ - "rs hd" -> player.dialogueInterpreter.open(RottenPotatoRSHDDialogue().ID) - "heal" -> player.fullRestore().also { player.sendMessage(colorize("%RAll healed!")) } - "extra" -> player.dialogueInterpreter.open(RottenPotatoExtraDialogue().ID) - "commands" -> { - CommandSystem.commandSystem.parse(player,"commands") - } - } - return true - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoRSHDDialogue.kt b/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoRSHDDialogue.kt deleted file mode 100644 index 0bf94afc6..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/rottenpotato/RottenPotatoRSHDDialogue.kt +++ /dev/null @@ -1,90 +0,0 @@ -package rs09.game.system.command.rottenpotato - -import api.ContentAPI -import api.InputType -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.game.node.entity.player.info.login.PlayerParser -import core.game.node.entity.player.link.RunScript -import rs09.game.world.ImmerseWorld -import rs09.game.world.repository.Repository -import core.plugin.Initializable -import rs09.tools.stringtools.colorize -import rs09.game.ai.AIPlayer -import rs09.game.ai.AIRepository - -@Initializable -/** - * Rotten Potato "RS HD" option menu - * @author Ceikry - */ -class RottenPotatoRSHDDialogue(player: Player? = null) : DialoguePlugin(player) { - val ID = 38575793 - override fun newInstance(player: Player?): DialoguePlugin { - return RottenPotatoRSHDDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - options("Wipe Bots", "Spawn Bots", "Force Log Players", "View Bank", "Copy Inventory") - stage = 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> { - when (buttonId) { - //Wipe Bots - 1 -> AIRepository.PulseRepository.toTypedArray().forEach { it.stop(); it.botScript.bot.clear(); AIPlayer.deregister((it.botScript.bot as AIPlayer).uid) }.also { player.sendMessage(colorize("%RBots wiped.")); end() } - //Spawn Bots - 2 -> ImmerseWorld.init().also { player.sendMessage(colorize("%RBots Respawning...")); end() } - //Force Log All Online Players - 3 -> { - Repository.disconnectionQueue.clear().also { end() } - Repository.players.toArray().forEach { - val p = it.asPlayer() - if (p != null && !p.isArtificial()) { // Should never be null. - p.removeAttribute("combat-time") - p.clear() - PlayerParser.save(p) - p.getDetails().save() - } - } - } - //View Bank - 4 -> { - end() - ContentAPI.sendInputDialogue(player, InputType.STRING_SHORT, "Enter player name:"){ value -> - val other = Repository.getPlayerByName(value.toString().toLowerCase().replace(" ","_")) - if(other == null){ - player.sendMessage(colorize("%RInvalid player name.")) - return@sendInputDialogue - } - - other.bank.open(player) - } - } - //View Inventory - 5 -> { - end() - ContentAPI.sendInputDialogue(player, InputType.STRING_SHORT, "Enter player name:"){ value -> - val other = Repository.getPlayerByName(value.toString().toLowerCase().replace(" ","_")) - if(other == null){ - player.sendMessage(colorize("%RInvalid player name.")) - return@sendInputDialogue - } - - player.inventory.clear() - player.inventory.addAll(other.inventory) - } - } - } - } - } - return true - } - - override fun getIds(): IntArray { - return intArrayOf(ID) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/AnimationCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/AnimationCommandSet.kt deleted file mode 100644 index bbd48b81a..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/AnimationCommandSet.kt +++ /dev/null @@ -1,78 +0,0 @@ -package rs09.game.system.command.sets - -import core.game.node.entity.npc.NPC -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation -import core.plugin.Initializable -import rs09.game.system.command.Command -import rs09.game.system.command.CommandPlugin.Companion.toInteger -import rs09.game.world.GameWorld -import java.util.* - -@Initializable -class AnimationCommandSet : CommandSet(Command.Privilege.ADMIN) { - - protected var npcs: List = ArrayList() - - override fun defineCommands() { - - /** - * Force the player to play animation - */ - define("anim"){ player, args -> - if (args.size < 2) { - reject(player, "Syntax error: ::anim ") - } - val animation = Animation(args[1].toInt()) - player.animate(animation) - } - - /** - * Force the player to loop animation - */ - define("loopanim"){ player, args -> - if (args.size < 2) { - reject(player, "Syntax error: ::loopanim ") - } - val start = toInteger(args[1]) - var end = toInteger((args[2])) - if (end > 25) { - notify(player, "Really...? $end times...? Looping 25 times instead.") - end = 25 - } - GameWorld.Pulser.submit(object : Pulse(3, player) { - var id = start - override fun pulse(): Boolean { - player.animate(Animation.create(id)) - return ++id >= end - } - }) - } - - /** - * Change the player's render animation to - */ - define("ranim"){ player, args -> - 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 ") - } - } - - - /** - * Reset the player's render animation to default - */ - define("ranimreset"){ player, _ -> - player.appearance.prepareBodyData(player) - player.appearance.setDefaultAnimations() - player.appearance.setAnimations() - player.appearance.sync() - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/ConfigCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/ConfigCommandSet.kt deleted file mode 100644 index 56fd094f9..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/ConfigCommandSet.kt +++ /dev/null @@ -1,52 +0,0 @@ -package rs09.game.system.command.sets - -import core.plugin.Initializable -import rs09.game.system.command.Command - -@Initializable -class ConfigCommandSet : CommandSet(Command.Privilege.ADMIN){ - override fun defineCommands() { - - /** - * Sets a range of configs to a maximum value - */ - define("sconfigrange"){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) - notify(player,"Config: $idsend value: " + Integer.MAX_VALUE) - } - } - - /** - * Sets a range of configs to 0 - */ - define("sconfigrange0"){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) - notify(player,"Config: $idsend value: 0") - } - } - - /** - * Opens an interface component - */ - define("iface"){player, args -> - if (args.size < 2) { - reject(player, "usage: iface id") - return@define - } - val id = args[1].toIntOrNull() ?: reject(player, "INVALID INTERFACE ID") - player.interfaceManager.openComponent(id as Int) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/DevelopmentCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/DevelopmentCommandSet.kt deleted file mode 100644 index 9507ac8ca..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/DevelopmentCommandSet.kt +++ /dev/null @@ -1,35 +0,0 @@ -package rs09.game.system.command.sets - -import core.game.node.entity.player.link.SpellBookManager -import core.game.node.item.Item -import core.plugin.Initializable -import org.rs09.consts.Items -import rs09.game.system.command.Command - -@Initializable -class DevelopmentCommandSet : CommandSet(Command.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) - - override fun defineCommands() { - - /** - * Gives the player a set of tools used to test farming stuff. - */ - define("farmkit"){player,_ -> - for(item in farmKitItems){ - player.inventory.add(Item(item)) - } - } - - define("spellbook"){player, args -> - if(args.size < 2){ - reject(player,"Usage: ::spellbook [int]. 0 = MODERN, 1 = ANCIENTS, 2 = LUNARS") - } - val spellBook = SpellBookManager.SpellBook.values()[args[1].toInt()] - player.spellBookManager.setSpellBook(spellBook) - player.spellBookManager.update(player) - } - - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/FunCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/FunCommandSet.kt deleted file mode 100644 index b3e77796a..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/FunCommandSet.kt +++ /dev/null @@ -1,166 +0,0 @@ -package rs09.game.system.command.sets - -import api.ContentAPI -import core.game.content.quest.tutorials.tutorialisland.CharacterDesign -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.update.flag.context.Animation -import core.plugin.Initializable -import rs09.game.content.dialogue.DialogueFile -import rs09.game.interaction.InteractionListeners -import rs09.game.interaction.SpadeDigListener -import rs09.game.system.command.Command -import rs09.game.world.GameWorld -import rs09.tools.END_DIALOGUE -import java.util.* - -@Initializable -class FunCommandSet : CommandSet(Command.Privilege.ADMIN) { - - var npcs: List = ArrayList() - - - override fun defineCommands() { - - /** - * Force animation + messages on all NPCs in a radius of 10 from the player. - */ - define("npcareaanim") { player, args -> - if (args.size < 3) { - reject(player, "Syntax error: ::npcareaanim ") - } - 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) - } - } - - - /** - * Transform a player's appearance into that of an NPC. - */ - define("pnpc", Command.Privilege.MODERATOR){ player, args -> - if(args.size < 2){ - reject(player, "Usage: ::pnpc ") - return@define - } - - val pnpc_id = args[1].toIntOrNull() - if(pnpc_id == null){ - reject(player, " must be a valid integer.") - } - - player.appearance.transformNPC(pnpc_id!!) - notify(player,"Transformed into NPC $pnpc_id") - } - - - /** - * Open bank - */ - define("bank"){ player, _ -> - player.getBank().open() - } - - /** - * Toggle invisibility - */ - define("invis"){ player, _ -> - player.isInvisible = !player.isInvisible - notify(player,"You are now ${if (player.isInvisible) "invisible" else "visible"} to others.") - } - - - /** - * Toggle 1-hit kills - */ - define("1hit"){ player, _ -> - player.setAttribute("1hko", !player.getAttribute("1hko", false)) - notify(player,"1-hit KO mode " + if (player.getAttribute("1hko", false)) "on." else "off.") - } - - - /** - * Toggle god mode - */ - define("god"){ player, _ -> - player.setAttribute("godMode", !player.getAttribute("godMode", false)) - notify(player,"God mode ${if (player.getAttribute("godMode", false)) "enabled." else "disabled."}") - } - - - /** - * 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) { - 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 - } - }) - } - } - - - /** - * Opens up the makeover interface - */ - define("makeover", Command.Privilege.MODERATOR){ player, _ -> - CharacterDesign.open(player) - } - - /** - * Bury inventory at current location - */ - define("bury"){player, _ -> - if(player.inventory.isEmpty){ - reject(player, "You have no items to bury.") - } - - 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++ } - 1 -> options("Yes","No").also { stage++ } - 2 -> when(buttonID){ - 1 -> bury(player).also { end() } - 2 -> stage = END_DIALOGUE - } - } - } - }) - - } - } - - fun bury(player: Player){ - val loc = Location.create(player.location) - val inv = player.inventory.toArray().filterNotNull() - SpadeDigListener.registerListener(player.location){p -> - for(item in inv){ - ContentAPI.addItemOrDrop(p, item.id, item.amount) - ContentAPI.sendMessage(p, "You dig and find ${if(item.amount > 1) "some" else "a"} ${item.name}") - } - ContentAPI.sendNews("${player.username} has found the hidden treasure! Congratulations!!!") - SpadeDigListener.listeners.remove(loc) - } - player.inventory.clear() - notify(player, "You have buried your loot at ${loc.toString()}") - } - -} diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt deleted file mode 100644 index 65ff7d150..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt +++ /dev/null @@ -1,638 +0,0 @@ -package rs09.game.system.command.sets - -import api.ContentAPI -import api.InputType -import core.cache.def.impl.ItemDefinition -import core.cache.def.impl.NPCDefinition -import core.cache.def.impl.SceneryDefinition -import core.cache.def.impl.VarbitDefinition -import core.game.component.Component -import core.game.ge.OfferState -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.communication.CommunicationInfo -import core.game.world.map.RegionManager -import core.game.world.map.build.DynamicRegion -import core.plugin.Initializable -import core.tools.StringUtils -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.rs09.consts.Components -import rs09.ServerConstants -import rs09.game.content.activity.fishingtrawler.TrawlerLoot -import rs09.game.content.ame.RandomEvents -import rs09.game.ge.OfferManager -import rs09.game.node.entity.state.newsys.states.FarmingState -import rs09.game.system.SystemLogger -import rs09.game.system.command.Command -import rs09.game.system.command.CommandMapping -import rs09.game.world.repository.Repository -import rs09.tools.stringtools.colorize -import java.awt.Toolkit -import java.awt.datatransfer.StringSelection -import java.util.* - -@Initializable -class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ - override fun defineCommands() { - - /** - * Toggles debug mode - */ - define("debug", Command.Privilege.STANDARD){ player, _ -> - player.toggleDebug() - } - - define("calc_accuracy", Command.Privilege.STANDARD){ player, args -> - val handler = player.getSwingHandler(false) - player.sendMessage("handler type: ${handler.type}") - player.sendMessage("calculateAccuracy: ${handler.calculateAccuracy(player)}") - - if (args.size > 1) - { - val npcId: Int = args[1].toInt() - 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)}") - } - } - - /** - * Prints player's current location - */ - define("loc", Command.Privilege.STANDARD){ player, _-> - val l = player.location - val r = player.viewport.region - var obj: Scenery? = null - notify(player,"Absolute: " + l + ", regional: [" + l.localX + ", " + l.localY + "], chunk: [" + l.chunkOffsetX + ", " + l.chunkOffsetY + "], flag: [" + RegionManager.isTeleportPermitted(l) + ", " + RegionManager.getClippingFlag(l) + ", " + RegionManager.isLandscape(l) + "].") - notify(player,"Region: [id=" + l.regionId + ", active=" + r.isActive + ", instanced=" + (r is DynamicRegion) + "], obj=" + RegionManager.getObject(l) + ".") - notify(player,"Object: " + RegionManager.getObject(l).also{obj = it} + ".") - notify(player,"Object Varp: " + obj?.definition?.configFile?.configId + " offset: " + obj?.definition?.configFile?.bitShift + " size: " + (obj?.definition?.configFile?.bitShift?.minus(obj?.definition?.configFile?.bitShift!!))) - SystemLogger.logInfo("Viewport: " + l.getSceneX(player.playerFlags.lastSceneGraph) + "," + l.getSceneY(player.playerFlags.lastSceneGraph)) - val loc = "Location.create(" + l.x + ", " + l.y + ", " + l.z + ")" - SystemLogger.logInfo(loc + "; " + player.playerFlags.lastSceneGraph + ", " + l.localX + ", " + l.localY) - val stringSelection = StringSelection(loc) - val clpbrd = Toolkit.getDefaultToolkit().systemClipboard - clpbrd.setContents(stringSelection, null) - } - - /** - * Tells the player to use loc - */ - define("pos", Command.Privilege.STANDARD){ player, _-> - notify(player, "Do you mean ::loc?") - } - - /** - * Tells the player to use loc - */ - define("coords", Command.Privilege.STANDARD){ player, _-> - notify(player, "Do you mean ::loc?") - } - - define("calcmaxhit", Command.Privilege.STANDARD) { player, _ -> - val swingHandler = player.getSwingHandler(false) - val hit = swingHandler.calculateHit(player, player, 1.0) - notify(player, "max hit (${(swingHandler as Object).getClass().getName()}): ${hit}") - } - - /** - * Empty a player's inventory - * ADMIN only (for obvious reasons) - */ - define("empty"){player,_-> - player.inventory.clear() - player.inventory.refresh() - } - - /** - * Announces a message in chat (NEWS) - */ - define("announce"){_,args -> - val message = args.slice(1 until args.size).joinToString(" ") - Repository.sendNews(message) - } - - /** - * Lists the players currently online - */ - define("players", Command.Privilege.MODERATOR){ player, _ -> - val rights = player.rights.ordinal - if (player!!.interfaceManager.isOpened && player.interfaceManager.opened.id != Components.QUESTJOURNAL_SCROLL_275 || player.locks.isMovementLocked || player.locks.isTeleportLocked) { - reject(player, "Please finish what you're doing first.") - } - player.interfaceManager.open(Component(Components.QUESTJOURNAL_SCROLL_275)) - var i = 0 - while (i < 257) { - player.packetDispatch.sendString("", 275, i) - i++ - } - val red = "" - player.packetDispatch.sendString("" + "Players" + "", 275, 2) - var lineStart = 11 - for(p in Repository.players){ - if(!p.isArtificial) - player.packetDispatch.sendString(red + "" + p.username + if(rights > 0) " [ip=" + p.details.ipAddress + ", name=" + p.details.compName + "]" else "",275,lineStart++) - } - } - /** - * =================================================================================== - */ - - - /** - * Opens the credit/voting shop - */ - define("shop", Command.Privilege.STANDARD){ player, _ -> - player.interfaceManager.open(Component(Components.CREDIT_SHOP)) - } - - /** - * Shows the player a list of currently active GE sell offers - */ - define("ge", Command.Privilege.STANDARD) { player, args -> - if(args.size < 2){ - reject(player, "Usage: ::ge mode", "Available modes: buying, selling, search") - } - - val mode = args[1] - when(mode){ - "buying" -> showGeBuy(player) - "selling" -> showGeSell(player) - "search" -> ContentAPI.sendInputDialogue(player, InputType.STRING_LONG, "Enter search term:"){value -> - showOffers(player, value as String) - } - else -> reject(player, "Invalid mode used. Available modes are: buying, selling, search") - } - } - /** - * ================================================================================== - */ - - - /** - * List all commands - */ - define("commands"){player,_ -> - for (i in 0..310) { - player.packetDispatch.sendString("", Components.QUESTJOURNAL_SCROLL_275, i) - } - var lineid = 11 - player.packetDispatch.sendString("Commands",Components.QUESTJOURNAL_SCROLL_275,2) - for(line in CommandMapping.getNames()) - player.packetDispatch.sendString(line,Components.QUESTJOURNAL_SCROLL_275,lineid++) - player.interfaceManager.open(Component(Components.QUESTJOURNAL_SCROLL_275)) - } - - /** - * Reply to PMs (also enables tab-to-reply) - */ - define("reply", Command.Privilege.STANDARD){ player, _ -> - if(player.interfaceManager.isOpened){ - reject(player, "Please finish what you're doing first.") - } - if (player.attributes.containsKey("replyTo")) { - player.setAttribute("keepDialogueAlive", true) - val replyTo = player.getAttribute("replyTo", "").replace("_".toRegex(), " ") - ContentAPI.sendInputDialogue(player, InputType.MESSAGE ,StringUtils.formatDisplayName(replyTo)){ value -> - CommunicationInfo.sendMessage(player, replyTo.toLowerCase(), value as String) - player.removeAttribute("keepDialogueAlive") - } - } else { - reject(player, "You have not recieved any recent messages to which you can reply.") - } - } - - - /** - * Max account stats - */ - define("max"){player,_ -> - var index = 0 - Skills.SKILL_NAME.forEach { - player.skills.setStaticLevel(index,99) - player.skills.setLevel(index,99) - index++ - } - player.skills.updateCombatLevel() - } - - define("noobme"){ 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"){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"){player,_ -> - player.achievementDiaryManager.diarys.forEach { - for(level in it.taskCompleted.indices){ - for(task in it.taskCompleted[level].indices){ - it.finishTask(player,level,task) - } - } - } - } - - define("log"){player,_ -> - var log: ArrayList? = player.getAttribute("loc-log") - log = log ?: ArrayList() - val locString = "{${player.location.x},${player.location.y},${player.location.z},1,0}" - log.add(locString) - player.setAttribute("loc-log",log) - } - - define("logdone"){player,_ -> - val log: ArrayList? = player.getAttribute("loc-log") - log ?: return@define - - val sb = StringBuilder() - var first = true - for(entry in log){ - if(!first) sb.append("-") - sb.append(entry) - first = false - } - - val stringSelection = StringSelection(sb.toString()) - val clpbrd = Toolkit.getDefaultToolkit().systemClipboard - clpbrd.setContents(stringSelection, null) - - log.clear() - player.setAttribute("loc-log",log) - } - - define("rolltrawlerloot"){player,args -> - val rolls = if(args.size < 2){ - 100 - } else { - args[1].toString().toInt() - } - player.bank.add(*TrawlerLoot.getLoot(rolls,false).toTypedArray()) - } - - define("fillbank"){player,_ -> - for(i in 0 until ServerConstants.BANK_SIZE){ - player.bank.add(Item(i)) - } - } - - define("emptybank"){player,_ -> - player.bank.clear() - player.bank.update() - } - - define("setconfig"){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) - } - - define("getobjectvarp"){player,args -> - if(args.size < 2){ - reject(player,"Syntax: ::getobjectvarp objectid") - } - val objectID = args[1].toInt() - notify(player, "${VarbitDefinition.forObjectID(SceneryDefinition.forId(objectID).varbitID).configId}") - } - - define("define_varbit"){ player, args -> - if(args.size < 2) { - reject(player, "Syntax: ::define_varbit varbitId") - } - val varbitID = args[1].toInt() - notify(player, "${varbitID}: ${VarbitDefinition.forId(varbitID, 0)}") - } - define("togglexp",Command.Privilege.STANDARD){ player, args -> - val enabled = player.varpManager.get(2501).getVarbit(0)?.value == 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",Command.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",Command.Privilege.STANDARD){player,_ -> - val disabled = player.varpManager.get(2502).getVarbit(0)?.value == 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",Command.Privilege.ADMIN){ - player,args -> - if(args.size < 4){ - reject(player,"Usage: ::setvarbit index offset value") - } - val index = args[1].toIntOrNull() - val offset = args[2].toIntOrNull() - val value = args[3].toIntOrNull() - - if(index == null || offset == null || value == null){ - reject(player,"Usage ::setvarbit index offset value") - } - - player.varpManager.get(index!!).setVarbit(offset!!, value!!).send(player) - } - - define("grow",Command.Privilege.ADMIN){player,_ -> - val state: FarmingState = player.states.get("farming") as FarmingState? ?: return@define - - for(patch in state.getPatches()){ - patch.nextGrowth = System.currentTimeMillis() - } - } - - define("finishbins",Command.Privilege.ADMIN){player,_ -> - val state: FarmingState = player.states.get("farming") as FarmingState? ?: return@define - - for(bin in state.getBins()){ - bin.finishedTime = System.currentTimeMillis() - } - } - - define("testlady",Command.Privilege.ADMIN){player,_ -> - player.antiMacroHandler.event = RandomEvents.EVIL_CHICKEN.npc.create(player,null,"sexam") - player.antiMacroHandler.event!!.init() - } - - define("addcredits",Command.Privilege.ADMIN){player,_ -> - player.details.credits += 100 - } - - define("resetgwdropes",Command.Privilege.STANDARD){player,_ -> - player.varpManager.get(1048).clearBitRange(0,31) - player.varpManager.get(1048).send(player) - } - - define("resetmistag",Command.Privilege.STANDARD){player,_ -> - player.removeAttribute("mistag-greeted") - } - - define("getnpcparent"){player,args -> - if(args.size < 2){ - reject(player,"Usage: ::getnpcparent npcid") - } - - val npcid = args[1].toIntOrNull() ?: reject(player,"Invalid NPC ID.") - - GlobalScope.launch { - for(def in NPCDefinition.getDefinitions().values){ - def ?: continue - def.childNPCIds ?: continue - for(id in def.childNPCIds){ - if(id == npcid){ - notify(player,"Parent NPC: ${def.id}") - return@launch - } - } - } - notify(player,"No parent NPC found.") - } - } - define("infinitespecial",Command.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("allow_aggro", Command.Privilege.ADMIN) { player, args -> - val usageStr = "Usage: ::allow_aggro true | false" - if(args.size < 2) { - reject(player, usageStr) - } - when(args[1]) { - "true" -> player.setAttribute("allow_admin_aggression", true) - "false" -> player.removeAttribute("allow_admin_aggression") - else -> reject(player, usageStr) - - } - } - } - - fun showGeSell(player: Player){ - val offers = HashMap() - for (offerIDs in OfferManager.OFFERS_BY_ITEMID) { - var totalOffered = 0 - for (offer in offerIDs.value) { - if (offer.offerState != OfferState.PENDING && offer.sell) { - totalOffered += offer.amountLeft - } - } - if (totalOffered != 0) { - offers[offerIDs.key] = totalOffered - } - } - for (offerIDs in OfferManager.BOT_OFFERS) { - if (offerIDs.value > 0) { - if (offers[offerIDs.key] == null) { - offers[offerIDs.key] = offerIDs.value - } else { - offers[offerIDs.key] = offers[offerIDs.key]!! + offerIDs.value - } - } - } - - val offerList = offers.keys.map { "${ItemDefinition.forId(it).name} - ${offers[it]}" }.sorted() - - var lineId = 11 - setScrollTitle(player, "Active Sell Offers") - for(i in 0..299) { - val offer = offerList.elementAtOrNull(i) - if (offer != null) - ContentAPI.setInterfaceText(player, offer, Components.QUESTJOURNAL_SCROLL_275, lineId++) - else - ContentAPI.setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) - } - ContentAPI.openInterface(player, Components.QUESTJOURNAL_SCROLL_275) - } - - fun showGeBuy(player: Player){ - val offers = HashMap() - for (offerIDs in OfferManager.OFFERS_BY_ITEMID) { - var totalOffered = 0 - for (offer in offerIDs.value) { - if (offer.offerState != OfferState.PENDING && !offer.sell) { - totalOffered += offer.amountLeft - } - } - if (totalOffered != 0) { - offers[offerIDs.key] = totalOffered - } - } - val offerList = offers.keys.map { "${ContentAPI.itemDefinition(it).name} - ${offers[it]}" }.sorted() - - var lineId = 11 - setScrollTitle(player, "Active Buy Offers") - for(i in 0..299) { - val offer = offerList.elementAtOrNull(i) - if (offer != null) - ContentAPI.setInterfaceText(player, offer, Components.QUESTJOURNAL_SCROLL_275, lineId++) - else - ContentAPI.setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) - } - ContentAPI.openInterface(player, Components.QUESTJOURNAL_SCROLL_275) - } - - fun showOffers(player: Player, searchTerm: String){ - val fakeOffers = ArrayList() - - OfferManager.OFFERS_BY_ITEMID.forEach { (id, offers) -> - if(searchTerm.toLowerCase().contains(ContentAPI.itemDefinition(id).name.toLowerCase()) || ContentAPI.itemDefinition(id).name.toLowerCase().contains(searchTerm.toLowerCase())){ - offers.forEach { - fakeOffers.add(FakeOffer(it.sell, ContentAPI.itemDefinition(id).name, it.amount)) - } - } - } - - OfferManager.BOT_OFFERS.forEach{ (id, amount) -> - val name = ContentAPI.getItemName(id) - if(searchTerm.toLowerCase().contains(name) || name.toLowerCase().contains(searchTerm.toLowerCase())) - fakeOffers.add(FakeOffer(true, ContentAPI.getItemName(id), amount)) - } - - val buyingList = fakeOffers.filter { !it.sell } - val sellingList = fakeOffers.filter { it.sell } - - val buyingMap = HashMap() - val sellingMap = HashMap() - - buyingList.forEach { - if(buyingMap[it.name] == null) buyingMap[it.name] = it.amount - else buyingMap[it.name] = buyingMap[it.name]?.plus(it.amount) ?: 0 - } - - sellingList.forEach { - if(sellingMap[it.name] == null) sellingMap[it.name] = it.amount - else sellingMap[it.name] = sellingMap[it.name]?.plus(it.amount) ?: 0 - } - - val buyList = buyingMap.map { (name,amount) -> "[Buying] $name - $amount" }.sortedBy { it.length }.toMutableList() - val sellList = sellingMap.map { (name, amount) -> "[Selling] $name - $amount" }.sortedBy { it.length }.toMutableList() - - sellList.reverse() - sellList.add(" ") - sellList.reverse() - - SystemLogger.logInfo("bl: ${buyList.size} sl: ${sellList.size}") - - setScrollTitle(player, "Results for \"$searchTerm\"") - - if(buyList.isEmpty()){ - buyList.add("[Buying] Nothing!") - } - if(sellList.size == 1){ - sellList.add("[Selling] Nothing!") - } - var lineId = 11 - for(i in 0..299) { - val offer = if(i < buyList.size) buyList.getOrNull(i) else sellList.getOrNull(i - (buyList.size)) - ContentAPI.setInterfaceText(player, offer ?: "", Components.QUESTJOURNAL_SCROLL_275, lineId++) - } - ContentAPI.openInterface(player, Components.QUESTJOURNAL_SCROLL_275) - } - - fun setScrollTitle(player: Player, text: String){ - ContentAPI.setInterfaceText(player, text, Components.QUESTJOURNAL_SCROLL_275, 2) - } - - class FakeOffer(val sell: Boolean,val name: String,val amount: Int) -} diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt deleted file mode 100644 index d6a9e47d6..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/ModerationCommandSet.kt +++ /dev/null @@ -1,88 +0,0 @@ -package rs09.game.system.command.sets - -import core.game.node.entity.player.Player -import core.game.node.entity.player.info.Rights -import rs09.game.system.command.Command -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import core.game.world.map.Location -import rs09.game.world.repository.Repository -import core.plugin.Initializable -import java.util.concurrent.TimeUnit - -@Initializable -/** - * Moderation commands - * @author Ceikry - */ -class ModerationCommandSet : CommandSet(Command.Privilege.MODERATOR){ - override fun defineCommands() { - val MAX_JAIL_TIME = 1800 //Max jail time (in seconds) - - - /** - * Kick a player - * ============================================================================================================= - */ - define("kick", Command.Privilege.ADMIN){ player, args -> - val playerToKick: Player? = Repository.getPlayerByName(args[1]) - if (playerToKick != null) { - playerToKick.clear(true) - notify(player, "Player ${playerToKick.username} was kicked.") - } else { - reject(player, "ERROR REMOVING PLAYER.") - } - } - /** - * ============================================================================================================= - */ - - - /** - * Jail a player - * ============================================================================================================= - */ - define("jail"){player,args -> - if(args.size < 3) { - reject(player,"Usage: ::jail ") - } - - val timeSeconds = args[1].toIntOrNull() - if(timeSeconds == null){ - reject(player, " Must be a valid integer!") - } - if(timeSeconds!! > MAX_JAIL_TIME){ - reject(player, "Maximum jail time is $MAX_JAIL_TIME seconds.") - } - val name = args.slice(2 until args.size).joinToString("_") - val otherPlayer = Repository.getPlayerByName(name) - if(otherPlayer == null){ - reject(player, "Can not find $name in the player list!") - } - - if (otherPlayer?.rights == Rights.ADMINISTRATOR){ - reject(player, "You cannot jail $name, they are a god. Nice try though ${player.username}!") - } - - notify(player, "Jailing ${otherPlayer!!.username} for $timeSeconds seconds.") - notify(otherPlayer, "${player.username} has jailed you for $timeSeconds seconds.") - GameWorld.Pulser.submit(object : Pulse(3){ - val originalLoc = otherPlayer.location - val releaseTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(timeSeconds.toLong()) - override fun pulse(): Boolean { - val done = System.currentTimeMillis() >= releaseTime - if(otherPlayer.location != Location.create(3228, 3407, 0)){ - otherPlayer.properties.teleportLocation = Location.create(3228, 3407, 0) - } - if(done){ - otherPlayer.properties.teleportLocation = originalLoc - } - return done - } - }) - } - /** - * ============================================================================================================= - */ - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/MusicCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/MusicCommandSet.kt deleted file mode 100644 index c4471a6c4..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/MusicCommandSet.kt +++ /dev/null @@ -1,36 +0,0 @@ -package rs09.game.system.command.sets - -import core.game.node.entity.player.link.music.MusicEntry -import core.plugin.Initializable -import rs09.game.system.command.Command - -@Initializable -class MusicCommandSet : CommandSet(Command.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 unlocks all music tracks for the player - * Restricted to ADMIN access only. - */ - define("allmusic", Command.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/kotlin/rs09/game/system/command/sets/QuestCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/QuestCommandSet.kt deleted file mode 100644 index 53d39880c..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/QuestCommandSet.kt +++ /dev/null @@ -1,84 +0,0 @@ -package rs09.game.system.command.sets - -import core.game.component.Component -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.quest.QuestRepository -import rs09.game.system.command.Command -import core.plugin.Initializable - -@Initializable -class QuestCommandSet : CommandSet(Command.Privilege.ADMIN){ - override fun defineCommands() { - /** - * Completes all quests - */ - define("allquest"){player,_-> - for (quest in QuestRepository.getQuests().values) { - quest.finish(player) - } - } - - /** - * Displays the currently implemented quests with debug information - */ - define("quest"){player,_ -> - sendQuestsDebug(player) - } - - /** - * Sets stage of quest - */ - define("setqueststage"){player,args -> - if (args.size < 2) { - 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.setStage(player.questRepository.forIndex(quest as Int), stage as Int) - notify(player, "Setting " + player.questRepository.forIndex(quest).name + " to stage $stage") - } - - /** - * Displays the currently implemented quests - */ - define("quests", Command.Privilege.STANDARD){ player, _ -> - sendQuests(player) - } - } - - /** - * Sends the list of quests. - * @param player the player. - */ - private fun sendQuests(player: Player?) { - player!!.interfaceManager.open(Component(275)) - for (i in 0..310) { - player.packetDispatch.sendString("", 275, i) - } - var lineId = 11 - 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++) - } - } - - /** - * Sends the list of quests with debug information - * @param player the player. - */ - private fun sendQuestsDebug(player: Player?) { - player!!.interfaceManager.open(Component(275)) - for (i in 0..310) { - player.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++) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/SlayerCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/SlayerCommandSet.kt deleted file mode 100644 index c841b30bd..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/SlayerCommandSet.kt +++ /dev/null @@ -1,38 +0,0 @@ -package rs09.game.system.command.sets - -import core.game.node.entity.npc.NPC -import core.plugin.Initializable -import rs09.game.system.command.Command - -@Initializable -class SlayerCommandSet : CommandSet(Command.Privilege.ADMIN){ - override fun defineCommands() { - /** - * Finishes a player's slayer task (the correct way) - */ - define("finishtask"){player,_ -> - notify(player, "Kill the npc that spawned to finish your task.") - player.slayer.amount = 1 - val finisher = NPC(player.slayer.task.npcs[0], player.location) - finisher.isRespawn = false - finisher.init() - } - - /** - * Set slayer points - */ - define("setslayerpoints"){player,args -> - if(args.size < 2){ - reject(player,"Usage: ::setslayerpoints amount") - } - - val amount = args[1].toIntOrNull() - if(amount == null){ - reject(player,"Amount needs to be a valid integer!") - } - - player.slayer.slayerPoints = amount!! - notify(player, "Set slayer points to $amount.") - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/SpawnCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/SpawnCommandSet.kt deleted file mode 100644 index 9f26c7e78..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/SpawnCommandSet.kt +++ /dev/null @@ -1,74 +0,0 @@ -package rs09.game.system.command.sets - -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 rs09.game.system.SystemLogger -import rs09.game.system.command.Command -import rs09.game.system.command.CommandPlugin -import core.plugin.Initializable -import java.awt.Toolkit -import java.awt.datatransfer.StringSelection - -@Initializable -class SpawnCommandSet : CommandSet(Command.Privilege.ADMIN){ - override fun defineCommands() { - /** - * Spawns an npc with the given ID - */ - define("npc"){player,args -> - if (args.size < 2) { - reject(player,"syntax error: id (optional) direction") - 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 clpbrd = Toolkit.getDefaultToolkit().systemClipboard - clpbrd.setContents(StringSelection(npcString), null) - println(npcString) - } - - /** - * Spawns an item with the given ID - */ - define("item"){player,args -> - if (args.size < 2) { - reject(player,"You must specify an item ID") - return@define - } - val id = args[1].toIntOrNull() ?: return@define - var amount = (args.getOrNull(2) ?: "1").toInt() - 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 -> - 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("object = $`object`") - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/SystemCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/SystemCommandSet.kt deleted file mode 100644 index 53a35f777..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/SystemCommandSet.kt +++ /dev/null @@ -1,242 +0,0 @@ -package rs09.game.system.command.sets - -import core.cache.Cache -import core.cache.def.Definition -import core.cache.def.impl.ItemDefinition -import core.game.node.entity.player.info.login.Response -import core.game.node.entity.player.info.portal.PlayerSQLManager -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 rs09.game.system.command.Command -import rs09.game.world.repository.Repository -import kotlin.system.exitProcess - -@Initializable -class SystemCommandSet : CommandSet(Command.Privilege.ADMIN) { - override fun defineCommands() { - /** - * Start an update countdown - */ - define("update") { player, args -> - if (args.size > 1) { - SystemManager.getUpdater().setCountdown(args[1].toInt()) - } - SystemManager.flag(SystemState.UPDATING) - } - - /** - * Cancel an update countdown - */ - define("cancelupdate") { player, _ -> - SystemManager.getUpdater().cancel() - } - - - /** - * Allows a player to reset their password - */ - define("resetpassword", Command.Privilege.STANDARD) { player, args -> - if (args.size != 3) { - reject(player, "Usage: ::resetpassword current new", "WARNING: THIS IS PERMANENT.", "WARNING: PASSWORD CAN NOT CONTAIN SPACES.") - } - val oldPass = args[1] - var newPass = args[2] - - if (PlayerSQLManager.getCredentialResponse(player.details.username, oldPass) != Response.SUCCESSFUL) { - reject(player, "INVALID PASSWORD!") - } - - if (newPass.length < 5 || newPass.length > 20) { - reject(player, "NEW PASSWORD MUST BE BETWEEN 5 AND 20 CHARACTERS") - } - - if (newPass == player.username) { - reject(player, "PASSWORD CAN NOT BE SAME AS USERNAME.") - } - - if (newPass == oldPass) { - reject(player, "PASSWORDS CAN NOT BE THE SAME") - } - - newPass = SystemManager.getEncryption().hashPassword(newPass) - PlayerSQLManager.updatePassword(player.username.toLowerCase().replace(" ", "_"), newPass) - notify(player, "Password updated successfully.") - } - - /** - * Allows an Administrator to reset a password - */ - define("setpasswordother", Command.Privilege.ADMIN) { player, args -> - if (args.size != 3) { - reject(player, "Usage: ::resetpasswordother user new", "WARNING: THIS IS PERMANENT.", "WARNING: PASSWORD CAN NOT CONTAIN SPACES.") - } - val otherUser = args[1] - var newPass = args[2] - - if (PlayerSQLManager.hasSqlAccount(otherUser, "username")) { - - if (newPass.length < 5 || newPass.length > 20) { - reject(player, "NEW PASSWORD MUST BE BETWEEN 5 AND 20 CHARACTERS") - } - - if (newPass == otherUser) { - reject(player, "PASSWORD CAN NOT BE SAME AS USERNAME.") - } - - newPass = SystemManager.getEncryption().hashPassword(newPass) - PlayerSQLManager.updatePassword(otherUser.toLowerCase().replace(" ","_"),newPass) - notify(player, "Password updated successfully.") - - } else { - reject(player, "USER DOES NOT EXIST!") - } - } - - define("giveitem", Command.Privilege.ADMIN) { player, args -> - if (args.size == 3 || args.size == 4) { - val victim = Repository.getPlayerByName(args[1]) - val itemID = args[2].toIntOrNull() - val amount = args.getOrNull(3)?.toIntOrNull() ?: 1 - - if (victim == null) { - reject(player, "INVALID TARGET USERNAME.") - } - - if (itemID == null || itemID <= 0 || itemID > ItemDefinition.getDefinitions().size) { - reject(player, "INVALID ITEM ID ENTERED.") - } - - if (amount > Int.MAX_VALUE || amount <= 0) { - reject(player, "INVALID ITEM ID ENTERED.") - } - - val item = Item(itemID!!, amount) - val invFull = victim!!.inventory.isFull - val syntax = if (amount > 1) "items" else "item" - - if (invFull) { - victim.bank.add(item) - } else { - victim.inventory.add(item) - } - - notify(player, "Successfully gave ${victim.username} $amount ${item.name}. ${if (invFull) "The $syntax were sent to their bank." else ""}") - notify(victim, "You received $amount ${item.name} from ${player.username}. ${if (invFull) "The $syntax were placed in your bank." else ""}") - - } else { - reject(player, "WRONG USAGE. USE giveitem target itemID || giveitem target itemID amt") - } - } - - define("removeitem", Command.Privilege.ADMIN) { player, args -> - if (args.size == 4 || args.size == 5) { - val itemLoc = args[1].toLowerCase() - val victim = Repository.getPlayerByName(args[2]) - val itemID = args[3].toIntOrNull() - var amount = args.getOrNull(4)?.toIntOrNull() ?: 1 - - if (victim == null) { - reject(player, "INVALID TARGET USERNAME.") - } - - if (itemID == null || itemID <= 0 || itemID > ItemDefinition.getDefinitions().size) { - reject(player, "INVALID ITEM ID ENTERED.") - } - - if (amount > Int.MAX_VALUE || amount <= 0) { - reject(player, "INVALID ITEM AMOUNT ENTERED.") - } - - val item = Item(itemID!!, amount) - var totalItemAmount = 0 - - when (itemLoc) { - "i", "inv", "inventory" -> { - totalItemAmount = victim!!.inventory.getItem(item).amount - victim.inventory.remove(item) - } - "b", "bk", "bank" -> { - totalItemAmount = victim!!.bank.getItem(item).amount - victim.bank.remove(item) - } - "e", "equip", "equipment" -> { - totalItemAmount = victim!!.equipment.getItem(item).amount - victim.equipment.remove(item) - } - else -> reject(player, "INVALID ITEM LOCATION ENTERED. USE: ", "i, inv, inventory | b, bk, bank | e, equip, equipment") - } - - if (amount > totalItemAmount) { - amount = totalItemAmount - } - - notify(player, "Successfully removed $amount ${item.name} from ${victim!!.username}.") - notify(victim, "${player.username} removed $amount ${item.name} from your inventory.") - - } else { - reject(player, "WRONG USAGE. USE removeitem itemLoc target itemID || removeitem itemLoc target itemID amt", - "ItemLoc: inv = inventory | equip = equipment | bank |") - } - } - - define("removeitemall", Command.Privilege.ADMIN) { player, args -> - if (args.size == 3) { - val victim = Repository.getPlayerByName(args[1]) - val itemID = args[2].toIntOrNull() - - if (victim == null) { - reject(player, "INVALID TARGET USERNAME.") - } - - if (itemID == null || itemID <= 0 || itemID > ItemDefinition.getDefinitions().size) { - reject(player, "INVALID ITEM ID ENTERED.") - } - - /* Handles removing the non noted version */ - val itemInv = Item(itemID!!) - /* Handles removing the noted version */ - val itemNote = Item(itemInv.noteChange) - - val untotal = victim!!.inventory.getAmount(itemID) + victim.bank.getAmount(itemID) + - victim.equipment.getAmount(itemID) - - val nototal = victim.inventory.getAmount(itemNote) + victim.bank.getAmount(itemNote) + - victim.equipment.getAmount(itemNote) - - val eqtotal = victim.equipment.getAmount(itemID) + victim.equipment.getAmount(itemNote) - - val total = untotal + nototal + eqtotal - - if (total == 0) { - reject(player, "USER HAS NONE OF THOSE ITEMS.") - } - - victim.inventory.remove(Item(itemID, victim.inventory.getAmount(itemID))) - victim.bank.remove(Item(itemID, victim.bank.getAmount(itemID))) - victim.equipment.remove(Item(itemID, victim.equipment.getAmount(itemID))) - - victim.inventory.remove(Item(itemInv.noteChange, victim.inventory.getAmount(itemNote))) - victim.bank.remove(Item(itemInv.noteChange, victim.bank.getAmount(itemNote))) - victim.equipment.remove(Item(itemInv.noteChange, victim.equipment.getAmount(itemNote))) - - notify(player, "Successfully removed $total ${itemInv.name} from ${victim.username}.") - notify(victim, "${player.username} removed $total ${itemInv.name} from your account.") - - } else { - reject(player, "WRONG USAGE. USE removeitemall target itemID") - } - } - - define("potato") { player, _ -> - player.inventory.add(Item(Items.ROTTEN_POTATO_5733)) - } - - define("shutdown",Command.Privilege.ADMIN) { player, _ -> - exitProcess(0) - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/TeleportCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/TeleportCommandSet.kt deleted file mode 100644 index ca0df7542..000000000 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/TeleportCommandSet.kt +++ /dev/null @@ -1,100 +0,0 @@ -package rs09.game.system.command.sets - -import core.game.node.entity.player.link.TeleportManager -import core.game.world.map.Location -import core.plugin.Initializable -import rs09.ServerConstants -import rs09.game.system.command.Command -import rs09.game.world.repository.Repository - -@Initializable -class TeleportCommandSet : CommandSet(Command.Privilege.ADMIN){ - override fun defineCommands() { - /** - * Allows teleporting by location name - */ - define("to"){player,args -> - var destination: Location? = null - val place = args.slice(1 until args.size).joinToString(" ") - for (destinations in ServerConstants.TELEPORT_DESTINATIONS) { - var i = 1 - while (i < destinations.size) { - if (place == destinations[i]) { - destination = destinations[0] as Location - break - } - i++ - } - } - if (destination != null) { - player.teleporter.send(destination, TeleportManager.TeleportType.NORMAL) - } else { - reject(player,"Could not locate teleport destination [name=$place]!") - } - } - - /** - * Teleport to location using coordinates - */ - define("tele"){player,args -> - if (args.size == 2 && args[1].contains(",")) { - val args2 = args[1].split(",".toRegex()).toTypedArray() - val x = args2[1].toInt() shl 6 or args2[3].toInt() - val y = args2[2].toInt() shl 6 or args2[4].toInt() - val z = args2[0].toInt() - player.properties.teleportLocation = Location.create(x, y, z) - return@define - } - if (args.size < 2) { - reject(player,"syntax error: x, y, (optional) z") - } - player.properties.teleportLocation = Location.create(args[1].toInt(), args[2].toInt(), if (args.size > 3) args[3].toInt() else 0) - } - - - /** - * Teleport to a specific player - */ - define("teleto"){player,args -> - if (args.size < 1) { - reject(player,"syntax error: name") - } - val n = args.slice(1 until args.size).joinToString("_") - val target = Repository.getPlayerByName(n) - if (target == null) { - reject(player,"syntax error: name") - } - if (target!!.getAttribute("fc_wave") != null) { - reject(player,"You cannot teleport to a player who is in the Fight Caves.") - } - player.properties.teleportLocation = target.location - } - - - /** - * Teleport a specific player to you - */ - define("teletome"){player,args -> - if (args.size < 1) { - reject(player,"syntax error: name") - } - val n = args.slice(1 until args.size).joinToString("_") - val target = Repository.getPlayerByName(n) - if (target == null) { - reject(player,"syntax error: name") - } - if (target!!.getAttribute("fc_wave") != null) { - reject(player,"You cannot teleport to a player who is in the Fight Caves.") - } - target.properties.teleportLocation = player.location - } - - - /** - * Teleports to the server's home location - */ - define("home"){player,_ -> - player.properties.teleportLocation = ServerConstants.HOME_LOCATION - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/ConfigParser.kt b/Server/src/main/kotlin/rs09/game/system/config/ConfigParser.kt deleted file mode 100644 index 99b15d2e1..000000000 --- a/Server/src/main/kotlin/rs09/game/system/config/ConfigParser.kt +++ /dev/null @@ -1,23 +0,0 @@ -package rs09.game.system.config - -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch - -class ConfigParser { - fun prePlugin() { - NPCConfigParser().load() - ItemConfigParser().load() - ObjectConfigParser().load() - XteaParser().load() - InterfaceConfigParser().load() - } - fun postPlugin() { - ShopParser().load() - DropTableParser().load() - NPCSpawner().load() - DoorConfigLoader().load() - GroundSpawnLoader().load() - MusicConfigLoader().load() - RangedConfigLoader().load() - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/DropTableParser.kt b/Server/src/main/kotlin/rs09/game/system/config/DropTableParser.kt deleted file mode 100644 index 78a715a65..000000000 --- a/Server/src/main/kotlin/rs09/game/system/config/DropTableParser.kt +++ /dev/null @@ -1,48 +0,0 @@ -package rs09.game.system.config - -import core.cache.def.impl.NPCDefinition -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.ServerConstants -import rs09.game.content.global.NPCDropTable -import rs09.game.content.global.WeightBasedTable -import rs09.game.content.global.WeightedItem -import rs09.game.system.SystemLogger -import java.io.FileReader - -class DropTableParser { - val parser = JSONParser() - var reader: FileReader? = null - fun load(){ - var count = 0 - reader = FileReader(ServerConstants.CONFIG_PATH + "drop_tables.json") - val obj = parser.parse(reader) as JSONArray - 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++ - } - } - SystemLogger.logInfo("Parsed $count drop tables.") - } - - private fun parseTable(data: JSONArray, destTable: NPCDropTable, isAlways: Boolean, 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() - val weight = item["weight"].toString().toDouble() - val newItem = WeightedItem(id,minAmount,maxAmount,weight.toDouble(),isAlways) - if(isCharms) destTable.addToCharms(newItem) - else destTable.add(newItem) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/GroundSpawnLoader.kt b/Server/src/main/kotlin/rs09/game/system/config/GroundSpawnLoader.kt deleted file mode 100644 index 391369675..000000000 --- a/Server/src/main/kotlin/rs09/game/system/config/GroundSpawnLoader.kt +++ /dev/null @@ -1,119 +0,0 @@ -package rs09.game.system.config - -import core.game.node.item.GroundItem -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.game.world.map.Location -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.ServerConstants -import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld -import rs09.game.world.repository.Repository -import java.io.FileReader -import java.nio.ByteBuffer - -class GroundSpawnLoader { - val parser = JSONParser() - var reader: FileReader? = null - - fun load() { - var count = 0 - 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++ - } - } - SystemLogger.logInfo("Initialized $count ground items.") - } - class GroundSpawn - /** - * Constructs a new `GroundItemParser` `Object`. - * @param item the item. - * @param location the location. - */( - /** - * Represents the rate at which a ground item will respawn. - */ - var respawnRate: Int, item: Item?, location: Location?) : GroundItem(item, location) { - /** - * Gets the respawn rate. - * @return the rate. - */ - - override fun toString(): String { - 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 { - return GroundItemManager.create(this) - } - - override fun isActive(): Boolean { - return true - } - - override fun isPrivate(): Boolean { - return false - } - - override fun isAutoSpawn(): Boolean { - return true - } - - override fun respawn() { - GameWorld.Pulser.submit(object : Pulse(respawnDuration) { - override fun pulse(): Boolean { - GroundItemManager.create(this@GroundSpawn) - return true - } - }) - } - - /** - * Method used to set the respawn rate. - * @param min the min. - * @param max the max. - */ - fun setRespawnRate(min: Int, max: Int) { - respawnRate = min or max shl 16 - } - - /** - * Gets the current respawn duration (in ticks). - * @return The respawn duration. - */ - val respawnDuration: Int - get() { - val minimum = respawnRate and 0xFFFF - val maximum = respawnRate shr 16 and 0xFFFF - val playerRatio = ServerConstants.MAX_PLAYERS / Repository.players.size.toDouble() - return (minimum + (maximum - minimum) / playerRatio).toInt() - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/MusicConfigLoader.kt b/Server/src/main/kotlin/rs09/game/system/config/MusicConfigLoader.kt deleted file mode 100644 index cd9c22f48..000000000 --- a/Server/src/main/kotlin/rs09/game/system/config/MusicConfigLoader.kt +++ /dev/null @@ -1,64 +0,0 @@ -package rs09.game.system.config - -import rs09.ServerConstants -import core.game.node.entity.player.link.music.MusicEntry -import core.game.node.entity.player.link.music.MusicZone -import rs09.game.system.SystemLogger -import core.game.world.map.RegionManager -import core.game.world.map.zone.ZoneBorders -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import java.io.FileReader - -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 - - for(config in configs){ - val e = config as JSONObject - val musicId = Integer.parseInt(e["id"].toString()) - val entry = MusicEntry( - musicId, - e["name"].toString(), - Integer.parseInt(e["indexId"].toString()) - ) - MusicEntry.getSongs().putIfAbsent(entry.id,entry) - val string = e["borders"].toString() - val borderArray = string.split("-") - 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 - var exceptions: String? = "" - for (i in 4 until tokens.size) { - exceptions += tokens[i] + if (!tokens[i].contains("]~")) "," else if (tokens[i].contains("[")) "," else "" - } - tokens = exceptions!!.split("~".toRegex()).toTypedArray() - var e: Array? = null - for (exception in tokens) { - 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()) { - RegionManager.forId(id!!).musicZones.add(zone) - } - } - count++ - } - SystemLogger.logInfo("Parsed $count music configs.") - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt b/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt deleted file mode 100644 index 1b82346bc..000000000 --- a/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt +++ /dev/null @@ -1,63 +0,0 @@ -package rs09.game.system.config - -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.ServerConstants -import rs09.game.world.GameSettings -import rs09.game.world.GameWorld -import rs09.plugin.PluginManager -import java.io.File -import java.io.FileReader -import kotlin.system.exitProcess - -/** - * Class for parsing the server config, I.E default.json - * @param path the path to the JSON file to parse. - * @author Ceikry - */ -class ServerConfigParser(path: String) { - val pathTo = rs09.JSONUtils.parsePath(path) - val confFile = File(pathTo) - val parser = JSONParser() - var reader: FileReader? = null - var data: JSONObject? = null - - init { - if(!confFile.canonicalFile.exists()){ - println("Could not find ${confFile.canonicalFile} - Double check your working directory!") - exitProcess(0) - } else if(!pathTo.contains(".json")) { - println("Config file MUST be a JSON file!!") - println("(Got $pathTo)") - } else { - reader = FileReader(pathTo) - data = parser.parse(reader) as JSONObject - ServerConstants.SERVER_NAME = data!!["name"] as String - parseGameSettings() - parseServerSettings() - parsePluginToggles() - } - } - - private fun parseGameSettings(){ - data ?: return - val gsData = data!!["GameSettings"] as JSONObject - GameWorld.settings = GameSettings.parse(gsData) - } - - private fun parseServerSettings(){ - data ?: return - val ssData = data!!["ServerSettings"] as JSONObject - ServerConstants.parse(ssData) - } - - private fun parsePluginToggles(){ - data ?: return - val dpData = data!!["PluginToggles"] as JSONObject - dpData.map { - if(!(it.value as Boolean)){ - PluginManager.disabledPlugins.put(it.key.toString(),it.value as Boolean) - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/ShopParser.kt b/Server/src/main/kotlin/rs09/game/system/config/ShopParser.kt deleted file mode 100644 index 3a0768eeb..000000000 --- a/Server/src/main/kotlin/rs09/game/system/config/ShopParser.kt +++ /dev/null @@ -1,70 +0,0 @@ -package rs09.game.system.config - -import core.game.content.global.shop.Shop -import core.game.node.entity.player.Player -import core.game.node.item.Item -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.ServerConstants -import rs09.game.system.SystemLogger -import java.io.FileReader - -class ShopParser{ - val parser = JSONParser() - var reader: FileReader? = null - - companion object{ - val SHOPS = HashMap() - val UID_SHOPS = HashMap() - fun openUid(player: Player,uid: Int): Boolean { - val shop = UID_SHOPS[uid] ?: return false; - shop.open(player); - return true; - } - } - - fun load(){ - var count = 0 - reader = FileReader(ServerConstants.CONFIG_PATH + "shops.json") - val configlist = parser.parse(reader) as JSONArray - for(config in configlist){ - var shop: Shop? - val e = config as JSONObject - val id = e["id"].toString().toInt() - val title = e["title"].toString() - val general = e["general_store"].toString().toBoolean() - val stock = parseStock(e["stock"].toString()).toTypedArray() - val npcs = if(e["npcs"].toString().isNotBlank()) e["npcs"].toString().split(",").map { it.toInt() }.toIntArray() else intArrayOf() - val currency = e["currency"].toString().toInt() - val highAlch = e["high_alch"].toString() == "1" - if(general && stock.isEmpty()){ - shop = Shop(title,true,currency,highAlch) - } else { - shop = Shop(title, stock, general, currency, highAlch) - } - if(npcs.isNotEmpty()){ - npcs.map { SHOPS[it] = shop } - } else { - UID_SHOPS[id] = shop - } - count++ - } - SystemLogger.logInfo("Parsed $count shops.") - } - - fun parseStock(stock: String): ArrayList{ - val items = ArrayList() - if(stock.isEmpty()){ - return items - } - stock.split('-').map { - val tokens = it.replace("{", "").replace("}", "").split(",".toRegex()).toTypedArray() - var amount = tokens[1].trim() - if(amount == "inf") - amount = "-1" - items.add(Item(tokens[0].trim().toInt(),amount.toInt())) - } - return items - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/config/XteaParser.kt b/Server/src/main/kotlin/rs09/game/system/config/XteaParser.kt deleted file mode 100644 index 5ff740263..000000000 --- a/Server/src/main/kotlin/rs09/game/system/config/XteaParser.kt +++ /dev/null @@ -1,42 +0,0 @@ -package rs09.game.system.config - -import rs09.ServerConstants -import rs09.game.system.SystemLogger -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import java.io.FileReader -import java.util.* -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. - } - } - val parser = JSONParser() - var reader: FileReader? = null - - - fun load() { - var count = 0 - reader = FileReader(ServerConstants.CONFIG_PATH + "xteas.json") - val obj = parser.parse(reader) as JSONObject - val configlist = obj["xteas"] as JSONArray - for(config in configlist){ - val e = config as JSONObject - val id = e["regionId"].toString().toInt() - val keys = e["keys"].toString().split(",").map(String::toInt) - REGION_XTEA[id] = intArrayOf(keys[0],keys[1],keys[2],keys[3]) - count++ - } - SystemLogger.logInfo("Parsed $count region keys.") - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/util/region/rellekka/RellekkaUtils.kt b/Server/src/main/kotlin/rs09/game/util/region/rellekka/RellekkaUtils.kt deleted file mode 100644 index 282ca96d7..000000000 --- a/Server/src/main/kotlin/rs09/game/util/region/rellekka/RellekkaUtils.kt +++ /dev/null @@ -1,35 +0,0 @@ -package rs09.game.util.region.rellekka - -import api.ContentAPI -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.map.Location -import org.rs09.consts.Components - -object RellekkaUtils { - @JvmStatic - fun sail(player: Player, destination: RellekkaDestination){ - ContentAPI.lock(player, 100) - ContentAPI.openOverlay(player, 115) - ContentAPI.openInterface(player, Components.MISC_SHIPJOURNEY_224) - ContentAPI.animateInterface(player, Components.MISC_SHIPJOURNEY_224, 7, destination.shipAnim) - - val animDuration = ContentAPI.animationDuration(ContentAPI.getAnimation(destination.shipAnim)) - ContentAPI.submitWorldPulse(object : Pulse(animDuration){ - override fun pulse(): Boolean { - ContentAPI.teleport(player, destination.destLoc) - ContentAPI.closeInterface(player) - ContentAPI.closeOverlay(player) - ContentAPI.unlock(player) - return true - } - }) - } -} - -enum class RellekkaDestination(val destName: String, val destLoc: Location, val shipAnim: Int){ - RELLEKKA_TO_JATIZSO("Jatizso", Location.create(2421, 3781, 0), 5766), - JATIZSO_TO_RELLEKKA("Rellekka", Location.create(2644, 3710, 0), 5767), - RELLEKKA_TO_NEITIZNOT("Neitiznot",Location(2310, 3782, 0), 5764), - NEITIZNOT_TO_RELLEKKA("Rellekka", Location(2644, 3710, 0), 5765) -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/DeadlockDetector.kt b/Server/src/main/kotlin/rs09/game/world/DeadlockDetector.kt deleted file mode 100644 index 69aebd430..000000000 --- a/Server/src/main/kotlin/rs09/game/world/DeadlockDetector.kt +++ /dev/null @@ -1,36 +0,0 @@ -package rs09.game.world - -import rs09.game.system.SystemLogger -import java.lang.management.ManagementFactory - -class DeadlockDetector : Runnable { - override fun run() { - val mbean = ManagementFactory.getThreadMXBean(); - val deadLockedThreads = mbean.findDeadlockedThreads() - - - - - fun `(┛◉Д◉)┛彡┻â”â”»`() { - val infos = mbean.getThreadInfo(deadLockedThreads) - infos.forEach { threadInfo -> - if (threadInfo != null) { - for (thread in Thread.getAllStackTraces().keys) { - if (thread.id == threadInfo.threadId) { - SystemLogger.logErr(threadInfo.toString().trim()) - for (ste in thread.stackTrace) { - SystemLogger.logErr("\t" + ste.toString().trim { it <= ' ' }) - } - } - } - } - } - } - - - if(deadLockedThreads != null){ - `(┛◉Д◉)┛彡┻â”â”»`() - } - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/GameWorld.kt b/Server/src/main/kotlin/rs09/game/world/GameWorld.kt deleted file mode 100644 index 5005ce006..000000000 --- a/Server/src/main/kotlin/rs09/game/world/GameWorld.kt +++ /dev/null @@ -1,187 +0,0 @@ -package rs09.game.world - -import core.cache.Cache -import core.cache.AriosStore -import core.cache.def.impl.SceneryDefinition -import core.game.ge.GrandExchangeDatabase -import core.game.node.entity.npc.drop.RareDropTable -import core.game.node.entity.player.Player -import core.game.system.SystemManager -import core.game.system.SystemState -import core.game.system.task.Pulse -import core.game.system.task.TaskExecutor -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.plugin.CorePluginTypes.StartupPlugin -import core.tools.RandomFunction -import core.tools.mysql.DatabaseManager -import rs09.game.ai.general.scriptrepository.PlayerScripts -import rs09.ServerConstants -import rs09.game.node.entity.state.newsys.StateRepository -import rs09.game.system.SystemLogger -import rs09.game.system.SystemLogger.logInfo -import rs09.game.system.config.ConfigParser -import rs09.game.world.callback.CallbackHub -import rs09.game.world.repository.Repository -import rs09.plugin.PluginManager -import rs09.worker.MajorUpdateWorker -import java.text.SimpleDateFormat -import java.util.* -import java.util.function.Consumer - -/** - * Represents the game world. - * @author Ceikry - */ -object GameWorld { - - /** - * The major update worker. - */ - @JvmStatic - val majorUpdateWorker = MajorUpdateWorker() - - @JvmStatic - val STARTUP_PLUGINS: List = ArrayList() - private val configParser = ConfigParser() - @JvmStatic - var PCBotsSpawned = false - @JvmStatic - var PCnBotsSpawned = false - @JvmStatic - var PCiBotsSpawned = false - /** - * The game settings to use. - */ - @JvmStatic - var settings: GameSettings? = null - /** - * The current amount of (600ms) cycles elapsed. - */ - @JvmStatic - var ticks = 0 - @JvmStatic - var databaseManager: DatabaseManager? = null - private set - - @JvmStatic - var Pulser = PulseRunner() - - /** - * Submits a pulse. - * - * @param pulse the pulse. - */ - @Deprecated("", ReplaceWith("Pulser.submit(pulse!!)", "core.game.world.GameWorld.Pulser")) - fun submit(pulse: Pulse?) { - Pulser.submit(pulse!!) - } - - fun pulse() { - ticks++ - if (ticks % 50 == 0) { - TaskExecutor.execute { - val player = Repository.players - try { - player.stream().filter { obj: Player? -> Objects.nonNull(obj) }.filter { p: Player -> !p.isArtificial && p.isPlaying }.forEach { p: Player? -> Repository.disconnectionQueue.save(p!!, false) } - } catch (t: Throwable) { - t.printStackTrace() - } - } - } - } - - fun checkDay(): Int { - val weeklySdf = SimpleDateFormat("u") - return weeklySdf.format(Date()).toInt() - } - - /** - * Prompts the [GameWorld] to begin it's initialization. - * - * @param directory the directory to the properties. - * @throws Throwable when the exception occurs. - */ - @Throws(Throwable::class) - fun prompt(directory: String?) { - prompt(true, directory) - } - - /** - * Prompts the game world. - * - * @param running if running. - * @throws Throwable the throwable. - */ - @Throws(Throwable::class) - @JvmStatic - fun prompt(running: Boolean) { - prompt(running, "server.properties") - } - - /** - * Prompts the [GameWorld] to begin its initialization. - * - * @param run If the server should be running. - * @param directory the path to the dir. - * @throws Throwable When an exception occurs. - */ - @Throws(Throwable::class) - fun prompt(run: Boolean, directory: String?){ - SystemLogger.logInfo("Prompting ${settings?.name} Game World...") - Cache.init(ServerConstants.CACHE_PATH) - AriosStore.init(ServerConstants.STORE_PATH) - databaseManager = DatabaseManager(ServerConstants.DATABASE) - databaseManager!!.connect() - GrandExchangeDatabase.init() - configParser.prePlugin() - PluginManager.init() - configParser.postPlugin() - RareDropTable.init() - logInfo("Initialized Rare Drop Table from " + ServerConstants.RDT_DATA_PATH) - if (settings!!.enable_bots) { - ImmerseWorld.init() - } - CallbackHub.call() - STARTUP_PLUGINS.forEach(Consumer { plugin: StartupPlugin? -> - plugin?.run() - }) - if (run) { - SystemManager.flag(if (settings?.isDevMode == true) SystemState.PRIVATE else SystemState.ACTIVE) - } - SceneryDefinition.getDefinitions().values.forEach(Consumer { obj: SceneryDefinition -> obj.examine }) - System.gc() - PlayerScripts.init() - StateRepository.init() - SystemLogger.initTradeLogger() - } - - /** - * Called when the server shuts down. - * - * @throws Throwable When an exception occurs. - */ - @Throws(Throwable::class) - fun shutdown() { - SystemManager.flag(SystemState.TERMINATED) - } - - /** - * Checks if its the economy world. - * - * @return `True` if so. - */ - @JvmStatic - val isEconomyWorld: Boolean - get() = false - - private fun generateLocation(): Location { - val random_location = Location(3075 + RandomFunction.random(-15, 15), 3954 + RandomFunction.random(-15, 15), 0) - if (!RegionManager.isTeleportPermitted(random_location)) { - return generateLocation() - } - return if (RegionManager.getObject(random_location) != null) { - generateLocation() - } else random_location - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/ImmerseWorld.kt b/Server/src/main/kotlin/rs09/game/world/ImmerseWorld.kt deleted file mode 100644 index 4d5af3e6a..000000000 --- a/Server/src/main/kotlin/rs09/game/world/ImmerseWorld.kt +++ /dev/null @@ -1,94 +0,0 @@ -package rs09.game.world - -import core.game.node.entity.combat.CombatStyle -import core.game.world.map.Location -import core.game.world.map.zone.ZoneBorders -import rs09.game.ai.general.GeneralBotCreator -import rs09.game.ai.general.scriptrepository.* -import rs09.game.ai.pvmbots.CombatBotAssembler -import rs09.game.ai.skillingbot.SkillingBotAssembler -import java.util.concurrent.Executors - -object ImmerseWorld { - var assembler = CombatBotAssembler() - var skillingBotAssembler = SkillingBotAssembler() - @JvmStatic - fun init() { - Executors.newSingleThreadExecutor().execute { - Thread.currentThread().name = "BotSpawner" - immerseSeersAndCatherby() - immerseLumbridgeDraynor() - immerseVarrock() - immerseWilderness() - immerseFishingGuild() - immerseAdventurer() - // immerseSlayer() - } - } - fun immerseAdventurer(){ - val lumbridge = Location.create(3221, 3219, 0) - for(i in (0..(GameWorld.settings?.max_adv_bots ?: 50))){ - GeneralBotCreator(Adventurer(CombatStyle.MELEE), assembler.MeleeAdventurer(CombatBotAssembler.Tier.LOW,lumbridge)) - GeneralBotCreator(Adventurer(CombatStyle.MELEE), assembler.MeleeAdventurer(CombatBotAssembler.Tier.MED,lumbridge)) - GeneralBotCreator(Adventurer(CombatStyle.RANGE), assembler.RangeAdventurer(CombatBotAssembler.Tier.MED,lumbridge)) - } - } - - fun immerseFishingGuild(){ - val fishingGuild = Location.create(2604, 3421, 0) - for (i in (0..4)) { - GeneralBotCreator(fishingGuild, SharkCatcher()) - } - } - - fun immerseSeersAndCatherby(){ - GeneralBotCreator(SeersMagicTrees(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(2702, 3397, 0))) - GeneralBotCreator(SeersFlax(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(2738, 3444, 0))) - GeneralBotCreator(FletchingBankstander(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(2722, 3493, 0))) - GeneralBotCreator(GlassBlowingBankstander(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(2807, 3441, 0))) - GeneralBotCreator(Location.create(2805, 3435, 0), LobsterCatcher()) - } - - fun immerseLumbridgeDraynor(){ - GeneralBotCreator(CowKiller(), assembler.produce(CombatBotAssembler.Type.RANGE, CombatBotAssembler.Tier.MED, Location.create(3261, 3269, 0))) - GeneralBotCreator(CowKiller(), assembler.produce(CombatBotAssembler.Type.MELEE, CombatBotAssembler.Tier.LOW, Location.create(3261, 3269, 0))) - GeneralBotCreator(CowKiller(), assembler.produce(CombatBotAssembler.Type.MELEE, CombatBotAssembler.Tier.MED, Location.create(3257, 3267, 0))) - GeneralBotCreator(ManThiever(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3235, 3213, 0))) - GeneralBotCreator(FarmerThiever(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3094,3243,0))) - GeneralBotCreator(FarmerThiever(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3094,3243,0))) - GeneralBotCreator(DraynorWillows(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.values().random(),Location.create(3094, 3245, 0))) - GeneralBotCreator(DraynorWillows(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.values().random(),Location.create(3094, 3245, 0))) - GeneralBotCreator(DraynorWillows(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.values().random(),Location.create(3094, 3245, 0))) - GeneralBotCreator(DraynorFisher(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3095, 3246, 0))) - GeneralBotCreator(DraynorFisher(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3095, 3246, 0))) - GeneralBotCreator(DraynorFisher(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3095, 3246, 0))) - } - - fun immerseVarrock(){ - val WestBankIdlerBorders = ZoneBorders(3184, 3435,3187, 3444) - GeneralBotCreator(GlassBlowingBankstander(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.RICH,Location.create(3189, 3435, 0))) - GeneralBotCreator(FletchingBankstander(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE,Location.create(3189, 3439, 0))) - GeneralBotCreator(Idler(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.RICH,WestBankIdlerBorders.randomLoc)) - GeneralBotCreator(GlassBlowingBankstander(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3256, 3420, 0))) - GeneralBotCreator(VarrockEssenceMiner(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3253, 3420, 0))) - GeneralBotCreator(VarrockSmither(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.RICH,Location.create(3189, 3436, 0))) - GeneralBotCreator(NonBankingMiner(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3182, 3374, 0))) - } - - fun immerseWilderness(){ - val wilderness = Location.create(2979, 3603, 0) - for (i in (0..1)) { - GeneralBotCreator(GreenDragonKiller(CombatStyle.MELEE), assembler.assembleMeleeDragonBot(CombatBotAssembler.Tier.HIGH,wilderness)) - GeneralBotCreator(GreenDragonKiller(CombatStyle.MELEE), assembler.assembleMeleeDragonBot(CombatBotAssembler.Tier.MED,wilderness)) - GeneralBotCreator(GreenDragonKiller(CombatStyle.RANGE), assembler.assembleRangedBot(CombatBotAssembler.Tier.HIGH,wilderness)) - } - } - - fun immerseFalador(){ - GeneralBotCreator(CoalMiner(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR,Location.create(3037, 9737, 0))) - } - - fun immerseSlayer(){ - GeneralBotCreator(GenericSlayerBot(), assembler.produce(CombatBotAssembler.Type.MELEE,CombatBotAssembler.Tier.HIGH,Location.create(2673, 3635, 0))) - } -} diff --git a/Server/src/main/kotlin/rs09/game/world/PulseRunner.kt b/Server/src/main/kotlin/rs09/game/world/PulseRunner.kt deleted file mode 100644 index 143986e89..000000000 --- a/Server/src/main/kotlin/rs09/game/world/PulseRunner.kt +++ /dev/null @@ -1,96 +0,0 @@ -package rs09.game.world - -import core.game.system.task.Pulse -import java.util.* - -/** new way of running pulses that multithreads based on core count automatically, should improve performance drastically. - * @author ceik - * @author angle - */ - -class PulseRunner { - val TASKS = ArrayList() - fun submit(pulse: Pulse){ - TASKS.add(pulse) - } -} -/* -class PulseeRunner { - val MAXIMUM_NUM_THREADS = 4 - val TARGET_PULSES_PER_THREAD = 100 - val ThreadPool = Executors.newFixedThreadPool(MAXIMUM_NUM_THREADS - 1) as ThreadPoolExecutor - val TASKS: MutableList = ArrayList() - var cores = Runtime.getRuntime().availableProcessors() - var EXECUTOR = Executors.newSingleThreadScheduledExecutor() - var lastPulse: Pulse? = null - fun init(tickTimeMS: Int) { - EXECUTOR.scheduleAtFixedRate(Runner(), 1200, tickTimeMS.toLong(), TimeUnit.MILLISECONDS) - } - - fun submit(pulse: Pulse) { - TASKS.add(pulse) - } - - inner class Runner : Runnable { - override fun run() { - val currTime = System.nanoTime() - var pulses: MutableList? = null - pulses = ArrayList(TASKS) - val pulseArray: Array = pulses.toTypedArray() - */ -/*var numThreads = 1 + pulseArray.size / TARGET_PULSES_PER_THREAD - if (numThreads > MAXIMUM_NUM_THREADS) numThreads = MAXIMUM_NUM_THREADS - val nowTime = System.nanoTime() - // Execute all the tasks not run on the first core - for (i in 1 until numThreads) { - val pulsesLengthStart = floor(pulseArray.size / numThreads * i.toDouble()).toInt() - var pulsesLengthEnd = floor(pulseArray.size / numThreads * (i + 1).toDouble()).toInt() - if (i + 1 == numThreads) pulsesLengthEnd = pulseArray.size - ThreadPool.execute(PulseThread(pulsesLengthStart, pulsesLengthEnd, pulseArray)) - } - - - // Execute the first core tasks all together just as before - val pulsesLengthStart = floor(pulseArray.size / numThreads.toDouble()).toInt()*//* - - - for (element in pulseArray) { - val pulse = element as Pulse? ?: continue - try { - if (TASKS.contains(pulse)) { - lastPulse = pulse - if (pulse.update()) { - TASKS.remove(pulse) - } - } - } catch (t: Throwable) { - t.printStackTrace() - pulse.stop() - TASKS.remove(pulse) - } - } - pulses.clear() - } - } - - inner class PulseThread(var threadStart: Int, var threadFinish: Int, var pulseArray: Array) : Runnable { - override fun run() { - for (i in threadStart until threadFinish) { - val pulse = pulseArray[i] as Pulse? ?: continue - try { - if (TASKS.contains(pulse)) { - lastPulse = pulse - if (pulse.update()) { - TASKS.remove(pulse) - } - } - } catch (t: Throwable) { - t.printStackTrace() - pulse.stop() - TASKS.remove(pulse) - } - } - } - - } -}*/ diff --git a/Server/src/main/kotlin/rs09/game/world/callback/CallbackHub.kt b/Server/src/main/kotlin/rs09/game/world/callback/CallbackHub.kt deleted file mode 100644 index c04369b71..000000000 --- a/Server/src/main/kotlin/rs09/game/world/callback/CallbackHub.kt +++ /dev/null @@ -1,31 +0,0 @@ -package rs09.game.world.callback - -import core.game.node.entity.skill.hunter.ImpetuousImpulses -import core.game.world.callback.CallBack -import core.game.world.map.zone.ZoneBuilder -import rs09.game.ge.GrandExchange -import rs09.game.ge.OfferManager -import rs09.game.system.SystemLogger -import java.util.* - -/** - * Initializes a few world pulses that need to continuously run - * @author Vexia - */ -object CallbackHub { - private var calls: MutableList = ArrayList() - - fun call(): Boolean { - calls.add(ZoneBuilder()) - calls.add(GrandExchange) - calls.add(ImpetuousImpulses()) - for (call in calls) { - if (!call.call()) { - SystemLogger.logErr("A callback was stopped, callback=" + call.javaClass.simpleName + ".") - return false - } - } - calls.clear() - return true - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/map/path/SmartPathfinder.kt b/Server/src/main/kotlin/rs09/game/world/map/path/SmartPathfinder.kt deleted file mode 100644 index 24bfebccc..000000000 --- a/Server/src/main/kotlin/rs09/game/world/map/path/SmartPathfinder.kt +++ /dev/null @@ -1,450 +0,0 @@ -package rs09.game.world.map.path - -import core.game.world.map.Location -import core.game.world.map.Point -import core.game.world.map.path.ClipMaskSupplier -import core.game.world.map.path.Path -import core.game.world.map.path.Pathfinder -import core.game.world.map.path.Pathfinder.* - -class SmartPathfinder -/** - * Constructs a new `SmartPathfinder` `Object`. - */ -internal constructor() : Pathfinder() { - /** - * The x-queue. - */ - private var queueX: IntArray = intArrayOf(0) - - /** - * The y-queue. - */ - private var queueY: IntArray = intArrayOf(0) - - /** - * The "via" array. - */ - private var via: Array = Array(104) { IntArray(104) } - - /** - * The cost array. - */ - private var cost: Array = Array(104) { IntArray(104) } - - /** - * The current writing position. - */ - private var writePathPosition = 0 - - /** - * The current x-coordinate. - */ - private var curX = 0 - - /** - * The current y-coordinate. - */ - private var curY = 0 - - /** - * The destination x-coordinate. - */ - private var dstX = 0 - - /** - * The destination y-coordinate. - */ - private var dstY = 0 - - /** - * If a path was found. - */ - private var foundPath = false - - /** - * Resets the pathfinder. - */ - fun reset() { - queueX = IntArray(4096) - queueY = IntArray(4096) - via = Array(104) { IntArray(104) } - cost = Array(104) { IntArray(104) } - writePathPosition = 0 - } - - /** - * Checks a tile. - * @param x The x-coordinate. - * @param y The y-coordinate. - * @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 - } - - override fun find(start: Location?, moverSize: Int, end: Location?, sizeX: Int, sizeY: Int, rotation: Int, type: Int, walkingFlag: Int, near: Boolean, clipMaskSupplier: ClipMaskSupplier?): Path { - reset() - assert(start != null && end != null) - val path = Path() - foundPath = false - for (x in 0..103) { - for (y in 0..103) { - via[x][y] = 0 - cost[x][y] = 99999999 - } - } - val z = start!!.z - val location = Location.create(start.regionX - 6 shl 3, start.regionY - 6 shl 3, z) - curX = start.sceneX - curY = start.sceneY - dstX = end!!.getSceneX(start) - dstY = end.getSceneY(start) - var attempts: Int - var readPosition: Int - check(curX, curY, 99, 0) - try { - if (moverSize < 2) { - checkSingleTraversal(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) {} - if (!foundPath) { - if (near) { - var fullCost = 1000 - var thisCost = 100 - val depth = 10 - for (x in dstX - depth..dstX + depth) { - for (y in dstY - depth..dstY + depth) { - if (x >= 0 && y >= 0 && x < 104 && y < 104 && cost[x][y] < 100) { - var diffX = 0 - if (x < dstX) { - diffX = dstX - x - } else if (x > dstX + sizeX - 1) { - diffX = x - (dstX + sizeX - 1) - } - var diffY = 0 - if (y < dstY) { - diffY = dstY - y - } else if (y > dstY + sizeY - 1) { - diffY = y - (dstY + sizeY - 1) - } - val totalCost = diffX * diffX + diffY * diffY - if (totalCost < fullCost || totalCost == fullCost && cost[x][y] < thisCost) { - fullCost = totalCost - thisCost = cost[x][y] - curX = x - curY = y - } - } - } - } - if (fullCost == 1000) { - return path - } - path.isMoveNear = true - } - } - readPosition = 0 - queueX[readPosition] = curX - queueY[readPosition++] = curY - var previousDirection: Int - attempts = 0 - var directionFlag = via[curX][curY].also { previousDirection = it } - while (curX != start.sceneX || curY != start.sceneY) { - if (++attempts > queueX.size) { - return path - } - if (directionFlag != previousDirection) { - previousDirection = directionFlag - queueX[readPosition] = curX - queueY[readPosition++] = curY - } - if (directionFlag and WEST_FLAG != 0) { - curX++ - } else if (directionFlag and EAST_FLAG != 0) { - curX-- - } - if (directionFlag and SOUTH_FLAG != 0) { - curY++ - } else if (directionFlag and NORTH_FLAG != 0) { - curY-- - } - directionFlag = via[curX][curY] - } - val size = readPosition-- - var absX = location.x + queueX[readPosition] - var absY = location.y + queueY[readPosition] - path.points.add(Point(absX, absY)) - for (i in 1 until size) { - readPosition-- - absX = location.x + queueX[readPosition] - absY = location.y + queueY[readPosition] - path.points.add(Point(absX, absY)) - } - path.setSuccesful(true) - return path - } - - /** - * Checks possible traversal for a size 1 entity. - * @param end The destination location. - * @param sizeX The x-size of the destination. - * @param sizeY The y-size of the destination. - * @param type The object type. - * @param rotation The object rotation. - * @param walkingFlag The walking flag. - * @param location The viewport location. - */ - private fun checkSingleTraversal(end: Location, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) { - var readPosition = 0 - val z = location.z - while (writePathPosition != readPosition) { - curX = queueX[readPosition] - curY = queueY[readPosition] - readPosition = readPosition + 1 and 0xfff - if (curX == dstX && curY == dstY) { - foundPath = true - break - } - try { - val absX = location.x + curX - val absY = location.y + curY - 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 thisCost = cost[curX][curY] + 1 - if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) { - check(curX, curY - 1, SOUTH_FLAG, thisCost) - } - if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0) { - check(curX - 1, curY, WEST_FLAG, thisCost) - } - if (curY < 103 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) { - check(curX, curY + 1, NORTH_FLAG, thisCost) - } - if (curX < 103 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0) { - check(curX + 1, curY, EAST_FLAG, thisCost) - } - if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) { - check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost) - } - if (curX > 0 && curY < 103 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0108 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) { - check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost) - } - if (curX < 103 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0102 == 0) { - check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost) - } - if (curX < 103 && curY < 103 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 1) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY) and 0x12c0180 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 1) and 0x12c0120 == 0) { - check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost) - } - } catch (e: Exception) { - // e.printStackTrace()println("curX " + curX + " curY" + curY + " via " + via[curX + 1] + via[curY + 1]) - } - } - } - - /** - * Checks possible traversal for a size 2 entity. - * @param end The destination location. - * @param sizeX The x-size of the destination. - * @param sizeY The y-size of the destination. - * @param type The object type. - * @param rotation The object rotation. - * @param walkingFlag The walking flag. - * @param location The viewport location. - */ - private fun checkDoubleTraversal(end: Location, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) { - var readPosition = 0 - val z = location.z - while (writePathPosition != readPosition) { - curX = queueX[readPosition] - curY = queueY[readPosition] - readPosition = readPosition + 1 and 0xfff - if (curX == dstX && curY == dstY) { - foundPath = true - break - } - val absX = location.x + curX - val absY = location.y + curY - if (type != 0) { - if ((type < 5 || type == 10) && canDoorInteract(absX, absY, 2, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { - foundPath = true - break - } - if (type < 10 && canDecorationInteract(absX, absY, 2, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { - foundPath = true - break - } - } - if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, 2, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) { - foundPath = true - break - } - val thisCost = cost[curX][curY] + 1 - if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c0183 == 0) { - check(curX, curY - 1, SOUTH_FLAG, thisCost) - } - if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c0138 == 0) { - check(curX - 1, curY, WEST_FLAG, thisCost) - } - if (curY < 102 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 2) and 0x12c01e0 == 0) { - check(curX, curY + 1, NORTH_FLAG, thisCost) - } - if (curX < 102 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 1) and 0x12c01e0 == 0) { - check(curX + 1, curY, EAST_FLAG, thisCost) - } - if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c0183 == 0) { - check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost) - } - if (curX > 0 && curY < 102 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + 2) and 0x12c01e0 == 0) { - check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost) - } - if (curX < 102 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY - 1) and 0x12c0183 == 0) { - check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost) - } - if (curX < 102 && curY < 102 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + 2) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 2) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + 2, absY + 1) and 0x12c0183 == 0) { - check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost) - } - } - } - - /** - * Checks possible traversal for any sized entity. - * @param end The destination location. - * @param size The mover size. - * @param sizeX The x-size of the destination. - * @param sizeY The y-size of the destination. - * @param type The object type. - * @param rotation The object rotation. - * @param walkingFlag The walking flag. - * @param location The viewport location. - */ - private fun checkVariableTraversal(end: Location, size: Int, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) { - var readPosition = 0 - val z = location.z - main@ while (writePathPosition != readPosition) { - curX = queueX[readPosition] - curY = queueY[readPosition] - readPosition = readPosition + 1 and 0xfff - if (curX == dstX && curY == dstY) { - foundPath = true - break - } - val absX = location.x + curX - val absY = location.y + curY - if (type != 0) { - if ((type < 5 || type == 10) && canDoorInteract(absX, absY, size, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { - foundPath = true - break - } - if (type < 10 && canDecorationInteract(absX, absY, size, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { - foundPath = true - break - } - } - if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, size, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) { - foundPath = true - break - } - val thisCost = cost[curX][curY] + 1 - south@ do { - if (curY > 0 && via[curX][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 1), absY - 1) and 0x12c0183 == 0) { - for (i in 1 until size - 1) { - if (clipMaskSupplier.getClippingFlag(z, absX + i, absY - 1) and 0x12c018f != 0) { - break@south - } - } - check(curX, curY - 1, SOUTH_FLAG, thisCost) - } - } while (false) - west@ do { - if (curX > 0 && via[curX - 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (size - 1)) and 0x12c0138 == 0) { - for (i in 1 until size - 1) { - if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + i) and 0x12c013e != 0) { - break@west - } - } - check(curX - 1, curY, WEST_FLAG, thisCost) - } - } while (false) - north@ do { - if (curY < 102 && via[curX][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 1), absY + size) and 0x12c01e0 == 0) { - for (i in 1 until size - 1) { - if (clipMaskSupplier.getClippingFlag(z, absX + i, absY + size) and 0x12c01f8 != 0) { - break@north - } - } - check(curX, curY + 1, NORTH_FLAG, thisCost) - } - } while (false) - east@ do { - if (curX < 102 && via[curX + 1][curY] == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + (size - 1)) and 0x12c01e0 == 0) { - for (i in 1 until size - 1) { - if (clipMaskSupplier.getClippingFlag(z, absX + size, absY + i) and 0x12c01e3 != 0) { - break@east - } - } - check(curX + 1, curY, EAST_FLAG, thisCost) - } - } while (false) - southWest@ do { - if (curX > 0 && curY > 0 && via[curX - 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (size - 2)) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + (size - 2), absY - 1) and 0x12c0183 == 0) { - for (i in 1 until size - 1) { - if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (i - 1)) and 0x12c013e != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i - 1), absY - 1) and 0x12c018f != 0) { - break@southWest - } - } - check(curX - 1, curY - 1, SOUTH_WEST_FLAG, thisCost) - } - } while (false) - northWest@ do { - if (curX > 0 && curY < 102 && via[curX - 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX - 1, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX, absY + size) and 0x12c01e0 == 0) { - for (i in 1 until size - 1) { - if (clipMaskSupplier.getClippingFlag(z, absX - 1, absY + (i + 1)) and 0x12c013e != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i - 1), absY + size) and 0x12c01f8 != 0) { - break@northWest - } - } - check(curX - 1, curY + 1, NORTH_WEST_FLAG, thisCost) - } - } while (false) - southEast@ do { - if (curX < 102 && curY > 0 && via[curX + 1][curY - 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY - 1) and 0x12c010e == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY - 1) and 0x12c0183 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + (size - 2)) and 0x12c01e0 == 0) { - for (i in 1 until size - 1) { - if (clipMaskSupplier.getClippingFlag(z, absX + size, absY + (i - 1)) and 0x12c01e3 != 0 || clipMaskSupplier.getClippingFlag(z, absX + (i + 1), absY - 1) and 0x12c018f != 0) { - break@southEast - } - } - check(curX + 1, curY - 1, SOUTH_EAST_FLAG, thisCost) - } - } while (false) - if (curX < 102 && curY < 102 && via[curX + 1][curY + 1] == 0 && clipMaskSupplier.getClippingFlag(z, absX + 1, absY + size) and 0x12c0138 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + size) and 0x12c01e0 == 0 && clipMaskSupplier.getClippingFlag(z, absX + size, absY + 1) and 0x12c0183 == 0) { - for (i in 1 until size - 1) { - if (clipMaskSupplier.getClippingFlag(z, absX + (i + 1), absY + size) and 0x12c01f8 != 0 || clipMaskSupplier.getClippingFlag(z, absX + size, absY + (i + 1)) and 0x12c01e3 != 0) { - continue@main - } - } - check(curX + 1, curY + 1, NORTH_EAST_FLAG, thisCost) - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/repository/DisconnectionQueue.kt b/Server/src/main/kotlin/rs09/game/world/repository/DisconnectionQueue.kt deleted file mode 100644 index 843fc2237..000000000 --- a/Server/src/main/kotlin/rs09/game/world/repository/DisconnectionQueue.kt +++ /dev/null @@ -1,215 +0,0 @@ -package rs09.game.world.repository - -import core.game.node.entity.player.Player -import core.game.node.entity.player.info.login.PlayerParser -import core.game.system.task.TaskExecutor -import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld -import java.util.* -import java.util.concurrent.ConcurrentHashMap - -/** - * Handles disconnecting players queuing. - * @author Emperor - */ -class DisconnectionQueue { - /**A - * The pending disconnections queue. - */ - private val queue: MutableMap - - /** - * Updates all entries. - */ - fun update() { - if (queue.isEmpty() || GameWorld.ticks % 3 != 0 && GameWorld.settings?.isDevMode != true) { - return - } - //make a copy of current entries as to avoid concurrency exceptions - val entries = ArrayList(queue.entries) - - //loop through entries and disconnect each - entries.forEach { - if(finish(it.value,false)) queue.remove(it.key) - } - } - - - fun isEmpty(): Boolean{ - return queue.isEmpty() - } - - /** - * Finishes a disconnection. - * @param entry The entry. - * @param force If finalization should be forced. - */ - private fun finish(entry: DisconnectionEntry?, force: Boolean): Boolean { - val player = entry!!.player - if (!force && player.allowRemoval()) { - return false - } - if (entry.isClear) { - SystemLogger.logInfo("Clearing player...") - player.clear() - } - Repository.playerNames.remove(player.name) - Repository.lobbyPlayers.remove(player) - Repository.players.remove(player) - Repository.LOGGED_IN_PLAYERS.remove(player.details.username) - SystemLogger.logInfo("Player cleared. Removed ${player.details.username}") - try { - if(player.communication.clan != null) - player.communication.clan.leave(player, false) - } catch (e: Exception) { - e.printStackTrace() - } - if (player.isArtificial) { - return true - } - if (!force) { - TaskExecutor.executeSQL { - Thread.currentThread().name = "PlayerSave SQL" - save(player, true) - } - return true - } - save(player, false) - return true - } - - /** - * Gets a queued player. - * @param name The player name. - * @return The player instance. - */ - operator fun get(name: String?): Player? { - val entry = queue[name] - return entry?.player - } - - /** - * Clears the queue. - */ - fun clear() { - for (entry in queue.values) { - 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) { - queue[player.name] = DisconnectionEntry(player, clear) - } - - /** - * 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) - } - - /** - * 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 - } - } - - /** - * Saves the player. - * @param player The player to be saved. - * @param sql If the sql database should be updated. - */ - fun save(player: Player, sql: Boolean): Boolean { - try { - PlayerParser.save(player) - if (sql) { - player.details.sqlManager.update(player) - player.details.save() - /*SQLEntryHandler.write(HighscoreSQLHandler(player)) - SQLEntryHandler.write(PlayerLogSQLHandler(player.monitor, player.name))*/ - } - return true - } 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/kotlin/rs09/game/world/repository/Repository.kt b/Server/src/main/kotlin/rs09/game/world/repository/Repository.kt deleted file mode 100644 index 039ecd6b9..000000000 --- a/Server/src/main/kotlin/rs09/game/world/repository/Repository.kt +++ /dev/null @@ -1,169 +0,0 @@ -package rs09.game.world.repository - -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.repository.NodeList -import rs09.ServerConstants -import java.util.* -import java.util.concurrent.CopyOnWriteArrayList - -/** - * The repository holding all node lists, etc in the game world. - * @author Emperor - */ -object Repository { - /** - * Represents the repository of active players. - */ - @JvmStatic - val players = NodeList(ServerConstants.MAX_PLAYERS) - /** - * Represents the repository of active npcs. - */ - @JvmStatic - val npcs = NodeList(ServerConstants.MAX_NPCS) - - /** - * List of player names who have logged in, quick and dirty work-around for the double-logging issue. - * This list needs to have values added to it before any sort of parsing is done for the login process due - * to the time it takes for the parsing to occur, which is the root of this issue to begin with. - */ - val LOGGED_IN_PLAYERS: MutableList = ArrayList(ServerConstants.MAX_PLAYERS) - - /** - * The renderable NPCs. - */ - private val RENDERABLE_NPCS: MutableList = CopyOnWriteArrayList() - - /** - * A mapping holding the players sorted by their names. - */ - @JvmStatic - val playerNames: MutableMap = HashMap() - /** - * Represents the list of players in lobby. - */ - @JvmStatic - val lobbyPlayers: MutableList = ArrayList() - /** - * The disconnection queue. - */ - @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. - * @param icon The icon. - */ - @JvmStatic - fun sendNews(string: String, icon: Int = 12, color: String = "CC6600") { - 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") - } - } - - /** - * This is another sendnews method which redirects to the one above. - * This needs to exist because java fails to recognize default arguments in Kotlin methods.... - * I fucking hate java. - */ - @JvmStatic - @Deprecated("Old and bad",ReplaceWith("ContentAPI.sendNews()"),DeprecationLevel.WARNING) - fun sendNews(string: String){ - sendNews(string,12) - } - - /** - * Adds a renderable NPC. - * @param npc The NPC. - */ - @JvmStatic - fun addRenderableNPC(npc: NPC) { - if (!RENDERABLE_NPCS.contains(npc)) { - RENDERABLE_NPCS.add(npc) - npc.isRenderable = true - } - } - - /** - * Removes an NPC from rendering. - * @param npc The NPC. - */ - @JvmStatic - fun removeRenderableNPC(npc: NPC) { - RENDERABLE_NPCS.remove(npc) - npc.isRenderable = false - } - - /** - * Finds the NPC on the given location. - * @param l The location. - * @return The NPC, or `null` if the NPC wasn't found. - */ - @JvmStatic - fun findNPC(l: Location): NPC? { - for (n in RegionManager.getRegionPlane(l).npcs) { - if (n.location == l) { - return n - } - } - return null - } - - /** - * Find a non-player character. - * @param npcId The non-player character's id. - * @return The non-player character's node. - */ - @JvmStatic - fun findNPC(npcId: Int): NPC? { - for (npc in npcs) { - if (npc == null) { - continue - } - if (npc.id == npcId) { - return npc - } - } - return null - } - - /** - * Get a player by its username. - * @param name The player's **username**. - * @return The player. - */ - @JvmStatic - fun getPlayerByName(name: String?): Player? { - return if (name == null) { - null - } else playerNames[name.toLowerCase().replace(" ".toRegex(), "_")] - } - - /** - * Gets the renderableNpcs. - * @return The renderableNpcs. - */ - @JvmStatic - val renderableNpcs: List - get() = RENDERABLE_NPCS -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/update/UpdateMasks.kt b/Server/src/main/kotlin/rs09/game/world/update/UpdateMasks.kt deleted file mode 100644 index b86573eab..000000000 --- a/Server/src/main/kotlin/rs09/game/world/update/UpdateMasks.kt +++ /dev/null @@ -1,222 +0,0 @@ -package rs09.game.world.update - -import core.game.node.entity.Entity -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.net.packet.IoBuffer -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. - */ - 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 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. - */ - @JvmOverloads - fun register(flag: UpdateFlag<*>, synced: Boolean = false): Boolean { - var synced = synced - if (updating.get()) { - return false - } - if (flag is AppearanceFlag) { - appearanceStamp = System.currentTimeMillis() - synced = true - } - if (synced) { - syncedMasks[flag.ordinal()] = flag - syncedMask = syncedMask or flag.data() - } - maskData = maskData or flag.data() - masks[flag.ordinal()] = flag - return true - } - - /** - * Unregisters a synced update mask. - * @param maskData The mask data. - * @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 - return true - } - return false - } - - /** - * Writes the flags. - * @param p The player. - * @param e The entity to update. - * @param buffer The buffer to write on. - */ - fun write(p: Player?, e: Entity?, buffer: IoBuffer) { - var maskData = maskData - 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) - } - } - - /** - * Writes the update masks on synchronization. - * @param p The player. - * @param e The entity to update. - * @param buffer The buffer to write on. - * @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() - } - maskData = maskData or synced - 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) { - var flag = masks[i] - if (flag == null) { - flag = syncedMasks[i] - if (!appearance && flag is AppearanceFlag) { - continue - } - } - flag?.writeDynamic(buffer, p) - } - } - - /** - * Adds the dynamic update flags. - * @param entity The entity. - */ - fun prepare(entity: Entity) { - val handler = entity.impactHandler - for (i in 0..1) { - if (handler.impactQueue.peek() == null) { - break - } - val impact = handler.impactQueue.poll() - registerHitUpdate(entity, impact, i == 1) - } - updating.set(true) - } - - /** - * Registers the hit update for the given [Impact]. - * @param e The entity. - * @param impact The impact to update. - * @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)) - } - return mark - } - - /** - * Resets the update masks. - */ - fun reset() { - for (i in masks.indices) { - masks[i] = null - } - maskData = 0 - updating.set(false) - } - - fun isUpdating(): Boolean { - return updating.get() - } - - /** - * Checks if an update is required. - * @return `True` if so. - */ - val isUpdateRequired: Boolean - get() = maskData != 0 - - /** - * Checks if synced update masks have been registered. - * @return `True` if so. - */ - fun hasSynced(): Boolean { - return syncedMask != 0 - } - - companion object { - /** - * The amount of update masks. - */ - const val SIZE = 11 - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/update/UpdateSequence.kt b/Server/src/main/kotlin/rs09/game/world/update/UpdateSequence.kt deleted file mode 100644 index 4a16fef80..000000000 --- a/Server/src/main/kotlin/rs09/game/world/update/UpdateSequence.kt +++ /dev/null @@ -1,108 +0,0 @@ -package rs09.game.world.update - -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.item.GroundItemManager -import core.game.world.map.RegionManager -import core.game.world.repository.InitializingNodeList -import core.net.packet.PacketRepository -import core.net.packet.context.PlayerContext -import core.net.packet.out.ClearMinimapFlag -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import rs09.game.world.repository.Repository -import java.util.concurrent.CountDownLatch -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit -import java.util.function.Consumer - -/** - * The entity update sequence. - * @author Emperor - */ -class UpdateSequence -/** - * Constructs a new `ParallelUpdatingSequence` `Object`. - */ -{ - var lobbyList: List? = null - var playersList: List? = null - var npcList: List? = null - - /** - * Starts the update sequence. - * @return `True` if we should continue. - */ - fun start() { - lobbyList = Repository.lobbyPlayers - playersList = renderablePlayers - npcList = Repository.renderableNpcs - lobbyList!!.map{ PacketRepository.send(ClearMinimapFlag::class.java, PlayerContext(it)) } - playersList!!.map{player -> - try { - player.tick() - } catch (e: Exception) { - e.printStackTrace() - } - } - npcList!!.map{npc -> - try { - npc.tick() - } catch (e: Exception) { - e.printStackTrace() - } - } - } - - /** - * Runs the updating part of the sequence. - */ - fun run() { - val latch = CountDownLatch(renderablePlayers.size) - playersList!!.forEach(Consumer { player: Player -> - GlobalScope.launch { - try { - player.update() - } catch (t: Throwable) { - t.printStackTrace() - } - latch.countDown() - } - }) - try { - latch.await(1000L, TimeUnit.MILLISECONDS) - } catch (e: InterruptedException) { - e.printStackTrace() - } - } - - /** - * Ends the sequence, calls the [Entity.reset] method.. - */ - fun end() { - playersList!!.forEach(Consumer { obj: Player -> obj.reset() }) - npcList!!.forEach(Consumer { obj: NPC -> obj.reset() }) - renderablePlayers.sync() - RegionManager.pulse() - GroundItemManager.pulse() - } - - /** - * Terminates the update sequence. - */ - fun terminate() { - } - - companion object { - /** - * Gets the renderablePlayers. - * @return The renderablePlayers. - */ - /** - * The list of active players. - */ - @JvmStatic - val renderablePlayers = InitializingNodeList() - - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/net/event/LoginReadEvent.kt b/Server/src/main/kotlin/rs09/net/event/LoginReadEvent.kt deleted file mode 100644 index 1dfcecf72..000000000 --- a/Server/src/main/kotlin/rs09/net/event/LoginReadEvent.kt +++ /dev/null @@ -1,174 +0,0 @@ -package rs09.net.event - -import core.cache.Cache -import core.cache.crypto.ISAACCipher -import core.cache.crypto.ISAACPair -import core.cache.misc.buffer.ByteBufferUtils -import core.game.node.entity.player.info.ClientInfo -import core.game.node.entity.player.info.PlayerDetails -import core.game.node.entity.player.info.UIDInfo -import core.game.node.entity.player.info.login.LoginType -import core.game.node.entity.player.info.login.Response -import core.game.node.entity.player.info.portal.PlayerSQLManager -import core.game.system.task.TaskExecutor -import core.net.Constants -import core.net.IoReadEvent -import core.net.IoSession -import core.net.amsc.WorldCommunicator -import core.tools.StringUtils -import rs09.ServerConstants -import rs09.game.node.entity.player.info.login.LoginParser -import rs09.game.system.SystemLogger -import rs09.game.world.repository.Repository -import java.lang.Runnable -import java.math.BigInteger -import java.nio.ByteBuffer - -/** - * Handles login reading events. - * @author Emperor - */ -class LoginReadEvent -/** - * Constructs a new `LoginReadEvent`. - * @param session The session. - * @param buffer The buffer with data to read from. - */ -(session: IoSession?, buffer: ByteBuffer?) : IoReadEvent(session, buffer) { - override fun read(session: IoSession, buffer: ByteBuffer) { - val opcode: Int = buffer.get().toInt() - if (buffer.short.toInt() != buffer.remaining()) { - session.write(Response.BAD_SESSION_ID) - return - } - val build = buffer.int - if (build != Constants.REVISION) { // || buffer.getInt() != Constants.CLIENT_BUILD) { - session.write(Response.UPDATED) - return - } - when (opcode) { - 12 -> println("User details event detected") - 16, 18 -> decodeWorld(opcode, session, buffer) - else -> { - SystemLogger.logErr("[Login] Unhandled login type [opcode=$opcode]!") - session.disconnect() - } - } - } - - companion object { - - /** - * Decodes a world login request. - * @param session The session. - * @param buffer The buffer to read from. - */ - private fun decodeWorld(opcode: Int, session: IoSession, buffer: ByteBuffer) { - var buffer = buffer - val d = buffer.get() // Memory? - val e = buffer.get() // no advertisement = 1 - val f = buffer.get() // 1 - val windowMode = buffer.get().toInt() // Screen size mode - val screenWidth = buffer.short.toInt() // Screen size Width - val screenHeight = buffer.short.toInt() // Screen size Height - val displayMode = buffer.get().toInt() // Display mode - val data = ByteArray(24) // random.dat data. - buffer[data] - ByteBufferUtils.getString(buffer) - buffer.int // Affiliate id - buffer.int // Hash containing a bunch of settings - val curpackets = buffer.short //Current interface packet counter. - for (i in Cache.getIndexes().indices) { - val crc = if (Cache.getIndexes()[i] == null) 0 else Cache.getIndexes()[i].information.informationContainer.crc - if (crc != buffer.int && crc != 0) { - /*session.write(Response.UPDATED); - return;*/ - } - } - buffer = getRSABlock(buffer) - buffer.rewind() - if(buffer.get().toInt() != 10){ - session.write(Response.COULD_NOT_LOGIN) - return - } - val isaacSeed = getISAACSeed(buffer) - val inCipher = ISAACCipher(isaacSeed) - for(i in 0..curpackets){ - inCipher.nextValue - } - for (i in 0..3) { - isaacSeed[i] += 50 - } - val outCipher = ISAACCipher(isaacSeed) - session.isaacPair = ISAACPair(inCipher, outCipher) - session.clientInfo = ClientInfo(displayMode, windowMode, screenWidth, screenHeight) - val b = buffer - TaskExecutor.executeSQL { - Thread.currentThread().name = "Login Password Response" - try { - val username = StringUtils.longToString(b.long) - val password = ByteBufferUtils.getString(b) - val response = PlayerSQLManager.getCredentialResponse(username, password) - if (response != Response.SUCCESSFUL) { - session.write(response, true) - return@executeSQL - } - login(PlayerDetails(username, password), session, b, opcode) - } catch (e: Exception) { - e.printStackTrace() - session.write(Response.COULD_NOT_LOGIN) - } - } - } - - /** - * Handles the login procedure after we check an acc is registered & certified. - * @param username the username. - * @param password the password. - * @param session the session. - * @param buffer the byte buffer. - * @param opcode the opcode. - */ - @JvmStatic - private fun login(details: PlayerDetails, session: IoSession, buffer: ByteBuffer, opcode: Int) { - if(!Repository.LOGGED_IN_PLAYERS.contains(details.username)) - Repository.LOGGED_IN_PLAYERS.add(details.username) - val parser = LoginParser(details, LoginType.fromType(opcode)) - details.session = session - details.info.translate(UIDInfo(details.ipAddress, ByteBufferUtils.getString(buffer), ByteBufferUtils.getString(buffer), ByteBufferUtils.getString(buffer))) - if (WorldCommunicator.isEnabled()) { - WorldCommunicator.register(parser) - } else { - TaskExecutor.executeSQL {parser.run()} - } - } - - /** - * Gets the ISAAC seed from the buffer. - * @param buffer The buffer to read from. - * @return The ISAAC seed. - */ - @JvmStatic - fun getISAACSeed(buffer: ByteBuffer): IntArray { - val seed = IntArray(4) - for (i in 0..3) { - seed[i] = buffer.int - } - return seed - } - - /** - * Gets the RSA block buffer. - * @param buffer The buffer to get the RSA block from. - * @return The RSA block buffer. - */ - @JvmStatic - fun getRSABlock(buffer: ByteBuffer): ByteBuffer { - val numBytes = 256 + buffer.get() - val encryptedByteArray = ByteArray(numBytes) - buffer.get(encryptedByteArray) - val encryptedBytes = BigInteger(encryptedByteArray) - return ByteBuffer.wrap(encryptedBytes.modPow(ServerConstants.EXPONENT, ServerConstants.MODULUS).toByteArray()) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/net/packet/PacketWriteQueue.kt b/Server/src/main/kotlin/rs09/net/packet/PacketWriteQueue.kt deleted file mode 100644 index 2262c397f..000000000 --- a/Server/src/main/kotlin/rs09/net/packet/PacketWriteQueue.kt +++ /dev/null @@ -1,49 +0,0 @@ -package rs09.net.packet - -import core.net.packet.OutgoingPacket -import core.net.packet.out.* -import rs09.game.system.SystemLogger -import java.util.* - -object PacketWriteQueue { - private val PacketsToWrite: Queue> = LinkedList>() - - @JvmStatic - fun handle(packet: OutgoingPacket, context: T){ - when(packet){ - //Dynamic packets need to be sent immediately - is UpdateSceneGraph, - is BuildDynamicScene, - is InstancedLocationUpdate, - is ClearRegionChunk -> packet.send(context) - //Rest get queued up and sent at the end of the tick (authentic) - else -> queue(packet,context) - } - } - - @JvmStatic - fun queue(packet: OutgoingPacket, context: T){ - PacketsToWrite.add(QueuedPacket(packet,context)) - } - - @JvmStatic - fun flush(){ - while(!PacketsToWrite.isEmpty()){ - val p = PacketsToWrite.poll() ?: continue - write(p.out,p.context) - } - } - - @Suppress("UNCHECKED_CAST") - fun write(out: OutgoingPacket<*>, context: T){ - val pack = out as? OutgoingPacket - val ctx = context as? T - if(pack == null || ctx == null){ - SystemLogger.logWarn("Failed packet casting") - return - } - pack.send(ctx) - } -} - -class QueuedPacket(val out: OutgoingPacket, val context: T) \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/net/packet/in/ItemOnGroundItemPacket.kt b/Server/src/main/kotlin/rs09/net/packet/in/ItemOnGroundItemPacket.kt deleted file mode 100644 index 32ba77aac..000000000 --- a/Server/src/main/kotlin/rs09/net/packet/in/ItemOnGroundItemPacket.kt +++ /dev/null @@ -1,62 +0,0 @@ -package rs09.net.packet.`in` - -import core.game.interaction.DestinationFlag -import core.game.interaction.MovementPulse -import core.game.node.entity.player.Player -import core.game.node.entity.skill.firemaking.FireMakingPulse -import core.game.node.item.GroundItemManager -import core.game.world.map.Location -import core.net.packet.IncomingPacket -import core.net.packet.IoBuffer -import org.rs09.consts.Items - -/** - * Handles the Item -> Ground Item packet - * Decided not to include it with the rest of ItemActionPacket because it functions a bit differently from the rest of that - * Will also require its own handlers instead of the normal UseWithHandler. Kind of annoying especially considering there's very few - * Interactions that actually make use of this packet. - * @author Ceikry - */ -class ItemOnGroundItemPacket : IncomingPacket { - override fun decode(player: Player?, opcode: Int, buffer: IoBuffer?) { - buffer ?: return - player ?: return - val x = buffer.leShortA - val slot = buffer.leShort - val usedItemId = buffer.leShort - val usedWithItemId = buffer.leShort - val y = buffer.leShortA - buffer.int //uncertain what the use for this int is. My suspicion was region ID but no variety of the int data type matches up with the region id - - val GILocation = Location.create(x,y,player.location.z) - - var usedWith = GroundItemManager.get(usedWithItemId, GILocation, null) ?: GroundItemManager.get(usedWithItemId, GILocation, player) - usedWith ?: return - - var used = player.inventory.get(slot) - if(used.id != usedItemId) return - - - /** - * I'm just gonna put the handler for tinderbox -> logs here. Yes I know it's lazy, etc, etc but frankly the number of interactions - * that are actually item -> ground item are so few it just doesn't make sense to make a whole system for it. - */ - player.pulseManager.run(object : MovementPulse(player,GILocation, DestinationFlag.ITEM){ - override fun pulse(): Boolean { - if(used.id == Items.TINDERBOX_590 || used.id == Items.GOLDEN_TINDERBOX_2946){ - when(usedWithItemId){ - Items.LOGS_1511,Items.ACHEY_TREE_LOGS_2862,Items.ARCTIC_PINE_LOGS_10810,Items.MAGIC_LOGS_1513,Items.MAHOGANY_LOGS_6332,Items.MAPLE_LOGS_1517,Items.YEW_LOGS_1515,Items.TEAK_LOGS_6333,Items.OAK_LOGS_1521, Items.WILLOW_LOGS_1519 - -> { - player.pulseManager.run(FireMakingPulse(player,usedWith.asItem(),usedWith)) - } - else -> player.sendMessage("Nothing interesting happens.") - } - } else { - player.sendMessage("Nothing interesting happens.") - } - return true - } - }) - } - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/plugin/PluginManager.kt b/Server/src/main/kotlin/rs09/plugin/PluginManager.kt deleted file mode 100644 index 879cd45b7..000000000 --- a/Server/src/main/kotlin/rs09/plugin/PluginManager.kt +++ /dev/null @@ -1,138 +0,0 @@ -package rs09.plugin - -import core.game.content.activity.ActivityManager -import core.game.content.activity.ActivityPlugin -import core.game.content.dialogue.DialoguePlugin -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.plugin.Plugin -import core.plugin.PluginManifest -import core.plugin.PluginType -import io.github.classgraph.ClassGraph -import io.github.classgraph.ClassInfo -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InterfaceListener -import rs09.game.node.entity.skill.magic.SpellListener -import rs09.game.system.SystemLogger -import rs09.game.system.command.Command -import java.util.* -import java.util.function.Consumer - -/** - * Represents a class used to handle the loading of all plugins. - * @author Ceikry - */ -object PluginManager { - var disabledPlugins = HashMap() - /** - * The amount of plugins loaded. - */ - var amountLoaded = 0 - private set - - /** - * The currently loaded plugin names. - */ - private var loadedPlugins: MutableList? = ArrayList() - - /** - * The last loaded plugin. - */ - private val lastLoaded: String? = null - - /** - * Initializes the plugin manager. - */ - @JvmStatic - fun init() { - try { - load() - loadedPlugins!!.clear() - loadedPlugins = null - SystemLogger.logInfo("Initialized $amountLoaded plugins...") - } catch (t: Throwable) { - SystemLogger.logErr("Error initializing Plugins -> " + t.localizedMessage + " for file -> " + lastLoaded) - t.printStackTrace() - } - } - - fun load() { - var result = ClassGraph().enableClassInfo().enableAnnotationInfo().scan() - result.getClassesWithAnnotation("core.plugin.Initializable").forEach(Consumer { p: ClassInfo -> - try { - definePlugin(p.loadClass().newInstance() as Plugin) - } catch (e: InstantiationException) { - e.printStackTrace() - } catch (e: IllegalAccessException) { - e.printStackTrace() - } - }) - result.getSubclasses("rs09.game.system.command.Command").forEach { - try { - definePlugin(it.loadClass().newInstance() as Plugin).also { System.out.println("Initializing $it") } - } catch (e: Exception) {e.printStackTrace()} - } - result.getSubclasses("rs09.game.interaction.InteractionListener").forEach { - val clazz = it.loadClass().newInstance() as InteractionListener - clazz.defineListeners() - clazz.defineDestinationOverrides() - } - result.getSubclasses("rs09.game.interaction.InterfaceListener").forEach { - val clazz = it.loadClass().newInstance() as InterfaceListener - clazz.defineListeners() - } - result.getSubclasses("rs09.game.node.entity.skill.magic.SpellListener").forEach { - val clazz = it.loadClass().newInstance() as SpellListener - clazz.defineListeners() - } - } - - /** - * Defines a list of plugins. - * @param plugins the plugins. - */ - @JvmStatic - fun definePlugins(vararg plugins: Plugin<*>) { - val pluginsLength = plugins.size - for (i in 0 until pluginsLength) { - val p = plugins[i] - definePlugin(p) - } - } - - /** - * Defines the plugin. - * @param plugin The plugin. - */ - @JvmStatic - fun definePlugin(plugin: Plugin<*>) { - try { - var manifest = plugin.javaClass.getAnnotation(PluginManifest::class.java) - if (manifest == null) { - manifest = plugin.javaClass.superclass.getAnnotation(PluginManifest::class.java) - } else { - if (disabledPlugins[manifest.name] != null) { - return - } - } - if (manifest == null || manifest.type == PluginType.ACTION) { - plugin.newInstance(null) - } else { - when (manifest.type) { - PluginType.DIALOGUE -> (plugin as DialoguePlugin).init() - PluginType.ACTIVITY -> ActivityManager.register(plugin as ActivityPlugin) - PluginType.LOGIN -> LoginConfiguration.getLoginPlugins().add(plugin as Plugin) - PluginType.QUEST -> { - plugin.newInstance(null) - QuestRepository.register(plugin as Quest) - } - else -> SystemLogger.logWarn("Unknown Manifest: " + manifest.type) - } - } - amountLoaded++ - } catch (e: Throwable) { - e.printStackTrace() - } - } -} diff --git a/Server/src/main/kotlin/rs09/tools/Globals.kt b/Server/src/main/kotlin/rs09/tools/Globals.kt deleted file mode 100644 index 16b460d3d..000000000 --- a/Server/src/main/kotlin/rs09/tools/Globals.kt +++ /dev/null @@ -1,41 +0,0 @@ -package rs09.tools.stringtools - -const val RED = "" -const val ORANGE = "" -const val YELLOW = "" -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 -} - -fun colorize(line: String, hexColor: String): String{ - return line.prepend("").append("") -} - -fun String.append(line: String): String{ - return this + line -} - -fun String.prepend(line: String): String{ - return line + this -} - -fun String.shuffle(): String{ - var new = "" - val old = this.split("").toMutableList() - for(i in this.indices){ - val c = old.random() - new += c.toString() - old.remove(c) - } - return new -} diff --git a/Server/src/main/kotlin/rs09/tools/TickUtils.kt b/Server/src/main/kotlin/rs09/tools/TickUtils.kt deleted file mode 100644 index db78fdea9..000000000 --- a/Server/src/main/kotlin/rs09/tools/TickUtils.kt +++ /dev/null @@ -1,14 +0,0 @@ -package rs09.tools - -const val tick = 600 //ms -const val second = 1000 //ms - -fun secondsToTicks(seconds: Int): Int { - val seconds = seconds * second //seconds -> ms - return seconds / tick //returns an int with the closest number of ticks to the desired number of seconds possible -} - -fun ticksToSeconds(ticks: Int): Int { - val ticksMs = ticks * tick - return ticksMs / 1000 -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/worker/MajorUpdateWorker.kt b/Server/src/main/kotlin/rs09/worker/MajorUpdateWorker.kt deleted file mode 100644 index b9042a7f9..000000000 --- a/Server/src/main/kotlin/rs09/worker/MajorUpdateWorker.kt +++ /dev/null @@ -1,106 +0,0 @@ -package rs09.worker - -import api.ContentAPI -import core.game.system.SystemManager -import core.game.system.SystemState -import core.game.system.task.Pulse -import core.plugin.CorePluginTypes.Managers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import rs09.Server -import rs09.ServerConstants -import rs09.ServerStore -import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld -import rs09.game.world.repository.Repository -import rs09.game.world.update.UpdateSequence -import rs09.net.packet.PacketWriteQueue -import rs09.tools.stringtools.colorize -import java.lang.Long.max -import java.lang.Long.min -import java.text.SimpleDateFormat -import java.util.* -import kotlin.collections.ArrayList -import kotlin.system.exitProcess - -/** - * Handles the running of pulses and writing of masks, etc - * @author Ceikry - */ -class MajorUpdateWorker { - var started = false - val sequence = UpdateSequence() - val sdf = SimpleDateFormat("HHmmss") - val worker = Thread { - Thread.currentThread().name = "Major Update Worker" - started = true - Thread.sleep(600L) - while(true){ - val start = System.currentTimeMillis() - val rmlist = ArrayList() - val list = ArrayList(GameWorld.Pulser.TASKS) - Server.heartbeat() - - //run our pulses - for(pulse in list) { - if (pulse == null || pulse.update()) rmlist.add(pulse) - } - - //remove all null or finished pulses from the list - rmlist.forEach { - if(GameWorld.Pulser.TASKS.contains(it)) GameWorld.Pulser.TASKS.remove(it) - } - - rmlist.clear() - //perform our update sequence where we write masks, etc - try { - sequence.start() - sequence.run() - sequence.end() - PacketWriteQueue.flush() - } catch (e: Exception){ - e.printStackTrace() - } - //increment global ticks variable - GameWorld.pulse() - //disconnect all players waiting to be disconnected - Repository.disconnectionQueue.update() - //tick all manager plugins - Managers.tick() - - //Handle daily restart if enabled - if(sdf.format(Date()).toInt() == 0){ - - if(GameWorld.checkDay() == 1) {//monday - ServerStore.clearWeeklyEntries() - } - - ServerStore.clearDailyEntries() - if(ServerConstants.DAILY_RESTART ) { - Repository.sendNews(colorize("%RSERVER GOING DOWN FOR DAILY RESTART IN 5 MINUTES!")) - ServerConstants.DAILY_RESTART = false - ContentAPI.submitWorldPulse(object : Pulse(100) { - var counter = 0 - override fun pulse(): Boolean { - counter++ - if (counter == 5) { - exitProcess(0) - } - Repository.sendNews(colorize("%RSERVER GOING DOWN FOR DAILY RESTART IN ${5 - counter} MINUTE${if (counter < 4) "S" else ""}!")) - return false - } - }) - } - } - - val end = System.currentTimeMillis() - Thread.sleep(max(600 - (end - start), 0)) - } - } - fun start() { - if(!started){ - worker.start() - } - } -} \ No newline at end of file diff --git a/Server/src/main/proto/management/BlockedUpdate.java b/Server/src/main/proto/management/BlockedUpdate.java new file mode 100644 index 000000000..d48c1a438 --- /dev/null +++ b/Server/src/main/proto/management/BlockedUpdate.java @@ -0,0 +1,971 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.BlockedUpdate} + */ +public final class BlockedUpdate extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.BlockedUpdate) + BlockedUpdateOrBuilder { +private static final long serialVersionUID = 0L; + // Use BlockedUpdate.newBuilder() to construct. + private BlockedUpdate(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private BlockedUpdate() { + type_ = 0; + username_ = ""; + friend_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new BlockedUpdate(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private BlockedUpdate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + int rawValue = input.readEnum(); + @SuppressWarnings("deprecation") + proto.management.BlockedUpdate.Type value = proto.management.BlockedUpdate.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = rawValue; + } + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + username_ = bs; + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + friend_ = bs; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_BlockedUpdate_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_BlockedUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.BlockedUpdate.class, proto.management.BlockedUpdate.Builder.class); + } + + /** + * Protobuf enum {@code management.BlockedUpdate.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * ADD = 0; + */ + ADD(0), + /** + * REMOVE = 1; + */ + REMOVE(1), + ; + + /** + * ADD = 0; + */ + public static final int ADD_VALUE = 0; + /** + * REMOVE = 1; + */ + public static final int REMOVE_VALUE = 1; + + + public final int getNumber() { + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static Type valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static Type forNumber(int value) { + switch (value) { + case 0: return ADD; + case 1: return REMOVE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + Type> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return proto.management.BlockedUpdate.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private Type(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:management.BlockedUpdate.Type) + } + + private int bitField0_; + public static final int TYPE_FIELD_NUMBER = 1; + private int type_; + /** + * required .management.BlockedUpdate.Type type = 1; + * @return Whether the type field is set. + */ + @java.lang.Override public boolean hasType() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required .management.BlockedUpdate.Type type = 1; + * @return The type. + */ + @java.lang.Override public proto.management.BlockedUpdate.Type getType() { + @SuppressWarnings("deprecation") + proto.management.BlockedUpdate.Type result = proto.management.BlockedUpdate.Type.valueOf(type_); + return result == null ? proto.management.BlockedUpdate.Type.ADD : result; + } + + public static final int USERNAME_FIELD_NUMBER = 2; + private volatile java.lang.Object username_; + /** + * required string username = 2; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string username = 2; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 2; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int FRIEND_FIELD_NUMBER = 3; + private volatile java.lang.Object friend_; + /** + * required string friend = 3; + * @return Whether the friend field is set. + */ + @java.lang.Override + public boolean hasFriend() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required string friend = 3; + * @return The friend. + */ + @java.lang.Override + public java.lang.String getFriend() { + java.lang.Object ref = friend_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + friend_ = s; + } + return s; + } + } + /** + * required string friend = 3; + * @return The bytes for friend. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getFriendBytes() { + java.lang.Object ref = friend_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + friend_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasType()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasFriend()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeEnum(1, type_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, username_); + } + if (((bitField0_ & 0x00000004) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, friend_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, username_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, friend_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.BlockedUpdate)) { + return super.equals(obj); + } + proto.management.BlockedUpdate other = (proto.management.BlockedUpdate) obj; + + if (hasType() != other.hasType()) return false; + if (hasType()) { + if (type_ != other.type_) return false; + } + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasFriend() != other.hasFriend()) return false; + if (hasFriend()) { + if (!getFriend() + .equals(other.getFriend())) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasType()) { + hash = (37 * hash) + TYPE_FIELD_NUMBER; + hash = (53 * hash) + type_; + } + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasFriend()) { + hash = (37 * hash) + FRIEND_FIELD_NUMBER; + hash = (53 * hash) + getFriend().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.BlockedUpdate parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.BlockedUpdate parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.BlockedUpdate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.BlockedUpdate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.BlockedUpdate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.BlockedUpdate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.BlockedUpdate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.BlockedUpdate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.BlockedUpdate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.BlockedUpdate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.BlockedUpdate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.BlockedUpdate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.BlockedUpdate prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.BlockedUpdate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.BlockedUpdate) + proto.management.BlockedUpdateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_BlockedUpdate_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_BlockedUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.BlockedUpdate.class, proto.management.BlockedUpdate.Builder.class); + } + + // Construct using proto.management.BlockedUpdate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + type_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + friend_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_BlockedUpdate_descriptor; + } + + @java.lang.Override + public proto.management.BlockedUpdate getDefaultInstanceForType() { + return proto.management.BlockedUpdate.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.BlockedUpdate build() { + proto.management.BlockedUpdate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.BlockedUpdate buildPartial() { + proto.management.BlockedUpdate result = new proto.management.BlockedUpdate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000004) != 0)) { + to_bitField0_ |= 0x00000004; + } + result.friend_ = friend_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.BlockedUpdate) { + return mergeFrom((proto.management.BlockedUpdate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.BlockedUpdate other) { + if (other == proto.management.BlockedUpdate.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasUsername()) { + bitField0_ |= 0x00000002; + username_ = other.username_; + onChanged(); + } + if (other.hasFriend()) { + bitField0_ |= 0x00000004; + friend_ = other.friend_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasType()) { + return false; + } + if (!hasUsername()) { + return false; + } + if (!hasFriend()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.BlockedUpdate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.BlockedUpdate) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int type_ = 0; + /** + * required .management.BlockedUpdate.Type type = 1; + * @return Whether the type field is set. + */ + @java.lang.Override public boolean hasType() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required .management.BlockedUpdate.Type type = 1; + * @return The type. + */ + @java.lang.Override + public proto.management.BlockedUpdate.Type getType() { + @SuppressWarnings("deprecation") + proto.management.BlockedUpdate.Type result = proto.management.BlockedUpdate.Type.valueOf(type_); + return result == null ? proto.management.BlockedUpdate.Type.ADD : result; + } + /** + * required .management.BlockedUpdate.Type type = 1; + * @param value The type to set. + * @return This builder for chaining. + */ + public Builder setType(proto.management.BlockedUpdate.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value.getNumber(); + onChanged(); + return this; + } + /** + * required .management.BlockedUpdate.Type type = 1; + * @return This builder for chaining. + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = 0; + onChanged(); + return this; + } + + private java.lang.Object username_ = ""; + /** + * required string username = 2; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string username = 2; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 2; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 2; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 2; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000002); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 2; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + username_ = value; + onChanged(); + return this; + } + + private java.lang.Object friend_ = ""; + /** + * required string friend = 3; + * @return Whether the friend field is set. + */ + public boolean hasFriend() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required string friend = 3; + * @return The friend. + */ + public java.lang.String getFriend() { + java.lang.Object ref = friend_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + friend_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string friend = 3; + * @return The bytes for friend. + */ + public com.google.protobuf.ByteString + getFriendBytes() { + java.lang.Object ref = friend_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + friend_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string friend = 3; + * @param value The friend to set. + * @return This builder for chaining. + */ + public Builder setFriend( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + friend_ = value; + onChanged(); + return this; + } + /** + * required string friend = 3; + * @return This builder for chaining. + */ + public Builder clearFriend() { + bitField0_ = (bitField0_ & ~0x00000004); + friend_ = getDefaultInstance().getFriend(); + onChanged(); + return this; + } + /** + * required string friend = 3; + * @param value The bytes for friend to set. + * @return This builder for chaining. + */ + public Builder setFriendBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + friend_ = value; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.BlockedUpdate) + } + + // @@protoc_insertion_point(class_scope:management.BlockedUpdate) + private static final proto.management.BlockedUpdate DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.BlockedUpdate(); + } + + public static proto.management.BlockedUpdate getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public BlockedUpdate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new BlockedUpdate(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.BlockedUpdate getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/BlockedUpdateOrBuilder.java b/Server/src/main/proto/management/BlockedUpdateOrBuilder.java new file mode 100644 index 000000000..2a0376221 --- /dev/null +++ b/Server/src/main/proto/management/BlockedUpdateOrBuilder.java @@ -0,0 +1,54 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface BlockedUpdateOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.BlockedUpdate) + com.google.protobuf.MessageOrBuilder { + + /** + * required .management.BlockedUpdate.Type type = 1; + * @return Whether the type field is set. + */ + boolean hasType(); + /** + * required .management.BlockedUpdate.Type type = 1; + * @return The type. + */ + proto.management.BlockedUpdate.Type getType(); + + /** + * required string username = 2; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 2; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 2; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required string friend = 3; + * @return Whether the friend field is set. + */ + boolean hasFriend(); + /** + * required string friend = 3; + * @return The friend. + */ + java.lang.String getFriend(); + /** + * required string friend = 3; + * @return The bytes for friend. + */ + com.google.protobuf.ByteString + getFriendBytes(); +} diff --git a/Server/src/main/proto/management/ClanJoinNotification.java b/Server/src/main/proto/management/ClanJoinNotification.java new file mode 100644 index 000000000..9b3cea563 --- /dev/null +++ b/Server/src/main/proto/management/ClanJoinNotification.java @@ -0,0 +1,864 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.ClanJoinNotification} + */ +public final class ClanJoinNotification extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.ClanJoinNotification) + ClanJoinNotificationOrBuilder { +private static final long serialVersionUID = 0L; + // Use ClanJoinNotification.newBuilder() to construct. + private ClanJoinNotification(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private ClanJoinNotification() { + username_ = ""; + clanName_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ClanJoinNotification(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ClanJoinNotification( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + clanName_ = bs; + break; + } + case 24: { + bitField0_ |= 0x00000004; + world_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_ClanJoinNotification_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_ClanJoinNotification_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.ClanJoinNotification.class, proto.management.ClanJoinNotification.Builder.class); + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CLANNAME_FIELD_NUMBER = 2; + private volatile java.lang.Object clanName_; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + @java.lang.Override + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + @java.lang.Override + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int WORLD_FIELD_NUMBER = 3; + private int world_; + /** + * required int32 world = 3; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required int32 world = 3; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasClanName()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasWorld()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, clanName_); + } + if (((bitField0_ & 0x00000004) != 0)) { + output.writeInt32(3, world_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, clanName_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, world_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.ClanJoinNotification)) { + return super.equals(obj); + } + proto.management.ClanJoinNotification other = (proto.management.ClanJoinNotification) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasClanName() != other.hasClanName()) return false; + if (hasClanName()) { + if (!getClanName() + .equals(other.getClanName())) return false; + } + if (hasWorld() != other.hasWorld()) return false; + if (hasWorld()) { + if (getWorld() + != other.getWorld()) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasClanName()) { + hash = (37 * hash) + CLANNAME_FIELD_NUMBER; + hash = (53 * hash) + getClanName().hashCode(); + } + if (hasWorld()) { + hash = (37 * hash) + WORLD_FIELD_NUMBER; + hash = (53 * hash) + getWorld(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.ClanJoinNotification parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanJoinNotification parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanJoinNotification parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanJoinNotification parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanJoinNotification parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanJoinNotification parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanJoinNotification parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.ClanJoinNotification parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.ClanJoinNotification parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.ClanJoinNotification parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.ClanJoinNotification parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.ClanJoinNotification parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.ClanJoinNotification prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.ClanJoinNotification} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.ClanJoinNotification) + proto.management.ClanJoinNotificationOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_ClanJoinNotification_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_ClanJoinNotification_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.ClanJoinNotification.class, proto.management.ClanJoinNotification.Builder.class); + } + + // Construct using proto.management.ClanJoinNotification.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + clanName_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + world_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_ClanJoinNotification_descriptor; + } + + @java.lang.Override + public proto.management.ClanJoinNotification getDefaultInstanceForType() { + return proto.management.ClanJoinNotification.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.ClanJoinNotification build() { + proto.management.ClanJoinNotification result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.ClanJoinNotification buildPartial() { + proto.management.ClanJoinNotification result = new proto.management.ClanJoinNotification(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.clanName_ = clanName_; + if (((from_bitField0_ & 0x00000004) != 0)) { + result.world_ = world_; + to_bitField0_ |= 0x00000004; + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.ClanJoinNotification) { + return mergeFrom((proto.management.ClanJoinNotification)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.ClanJoinNotification other) { + if (other == proto.management.ClanJoinNotification.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (other.hasClanName()) { + bitField0_ |= 0x00000002; + clanName_ = other.clanName_; + onChanged(); + } + if (other.hasWorld()) { + setWorld(other.getWorld()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + if (!hasClanName()) { + return false; + } + if (!hasWorld()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.ClanJoinNotification parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.ClanJoinNotification) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private java.lang.Object clanName_ = ""; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string clanName = 2; + * @param value The clanName to set. + * @return This builder for chaining. + */ + public Builder setClanName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @return This builder for chaining. + */ + public Builder clearClanName() { + bitField0_ = (bitField0_ & ~0x00000002); + clanName_ = getDefaultInstance().getClanName(); + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @param value The bytes for clanName to set. + * @return This builder for chaining. + */ + public Builder setClanNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + + private int world_ ; + /** + * required int32 world = 3; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required int32 world = 3; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + /** + * required int32 world = 3; + * @param value The world to set. + * @return This builder for chaining. + */ + public Builder setWorld(int value) { + bitField0_ |= 0x00000004; + world_ = value; + onChanged(); + return this; + } + /** + * required int32 world = 3; + * @return This builder for chaining. + */ + public Builder clearWorld() { + bitField0_ = (bitField0_ & ~0x00000004); + world_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.ClanJoinNotification) + } + + // @@protoc_insertion_point(class_scope:management.ClanJoinNotification) + private static final proto.management.ClanJoinNotification DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.ClanJoinNotification(); + } + + public static proto.management.ClanJoinNotification getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ClanJoinNotification parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ClanJoinNotification(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.ClanJoinNotification getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/ClanJoinNotificationOrBuilder.java b/Server/src/main/proto/management/ClanJoinNotificationOrBuilder.java new file mode 100644 index 000000000..69edea304 --- /dev/null +++ b/Server/src/main/proto/management/ClanJoinNotificationOrBuilder.java @@ -0,0 +1,54 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface ClanJoinNotificationOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.ClanJoinNotification) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + boolean hasClanName(); + /** + * required string clanName = 2; + * @return The clanName. + */ + java.lang.String getClanName(); + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + com.google.protobuf.ByteString + getClanNameBytes(); + + /** + * required int32 world = 3; + * @return Whether the world field is set. + */ + boolean hasWorld(); + /** + * required int32 world = 3; + * @return The world. + */ + int getWorld(); +} diff --git a/Server/src/main/proto/management/ClanLeaveNotification.java b/Server/src/main/proto/management/ClanLeaveNotification.java new file mode 100644 index 000000000..75d14cc81 --- /dev/null +++ b/Server/src/main/proto/management/ClanLeaveNotification.java @@ -0,0 +1,864 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.ClanLeaveNotification} + */ +public final class ClanLeaveNotification extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.ClanLeaveNotification) + ClanLeaveNotificationOrBuilder { +private static final long serialVersionUID = 0L; + // Use ClanLeaveNotification.newBuilder() to construct. + private ClanLeaveNotification(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private ClanLeaveNotification() { + username_ = ""; + clanName_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ClanLeaveNotification(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ClanLeaveNotification( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + clanName_ = bs; + break; + } + case 24: { + bitField0_ |= 0x00000004; + world_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_ClanLeaveNotification_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_ClanLeaveNotification_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.ClanLeaveNotification.class, proto.management.ClanLeaveNotification.Builder.class); + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CLANNAME_FIELD_NUMBER = 2; + private volatile java.lang.Object clanName_; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + @java.lang.Override + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + @java.lang.Override + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int WORLD_FIELD_NUMBER = 3; + private int world_; + /** + * required int32 world = 3; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required int32 world = 3; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasClanName()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasWorld()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, clanName_); + } + if (((bitField0_ & 0x00000004) != 0)) { + output.writeInt32(3, world_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, clanName_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, world_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.ClanLeaveNotification)) { + return super.equals(obj); + } + proto.management.ClanLeaveNotification other = (proto.management.ClanLeaveNotification) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasClanName() != other.hasClanName()) return false; + if (hasClanName()) { + if (!getClanName() + .equals(other.getClanName())) return false; + } + if (hasWorld() != other.hasWorld()) return false; + if (hasWorld()) { + if (getWorld() + != other.getWorld()) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasClanName()) { + hash = (37 * hash) + CLANNAME_FIELD_NUMBER; + hash = (53 * hash) + getClanName().hashCode(); + } + if (hasWorld()) { + hash = (37 * hash) + WORLD_FIELD_NUMBER; + hash = (53 * hash) + getWorld(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.ClanLeaveNotification parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanLeaveNotification parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanLeaveNotification parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanLeaveNotification parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanLeaveNotification parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanLeaveNotification parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanLeaveNotification parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.ClanLeaveNotification parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.ClanLeaveNotification parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.ClanLeaveNotification parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.ClanLeaveNotification parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.ClanLeaveNotification parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.ClanLeaveNotification prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.ClanLeaveNotification} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.ClanLeaveNotification) + proto.management.ClanLeaveNotificationOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_ClanLeaveNotification_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_ClanLeaveNotification_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.ClanLeaveNotification.class, proto.management.ClanLeaveNotification.Builder.class); + } + + // Construct using proto.management.ClanLeaveNotification.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + clanName_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + world_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_ClanLeaveNotification_descriptor; + } + + @java.lang.Override + public proto.management.ClanLeaveNotification getDefaultInstanceForType() { + return proto.management.ClanLeaveNotification.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.ClanLeaveNotification build() { + proto.management.ClanLeaveNotification result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.ClanLeaveNotification buildPartial() { + proto.management.ClanLeaveNotification result = new proto.management.ClanLeaveNotification(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.clanName_ = clanName_; + if (((from_bitField0_ & 0x00000004) != 0)) { + result.world_ = world_; + to_bitField0_ |= 0x00000004; + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.ClanLeaveNotification) { + return mergeFrom((proto.management.ClanLeaveNotification)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.ClanLeaveNotification other) { + if (other == proto.management.ClanLeaveNotification.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (other.hasClanName()) { + bitField0_ |= 0x00000002; + clanName_ = other.clanName_; + onChanged(); + } + if (other.hasWorld()) { + setWorld(other.getWorld()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + if (!hasClanName()) { + return false; + } + if (!hasWorld()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.ClanLeaveNotification parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.ClanLeaveNotification) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private java.lang.Object clanName_ = ""; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string clanName = 2; + * @param value The clanName to set. + * @return This builder for chaining. + */ + public Builder setClanName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @return This builder for chaining. + */ + public Builder clearClanName() { + bitField0_ = (bitField0_ & ~0x00000002); + clanName_ = getDefaultInstance().getClanName(); + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @param value The bytes for clanName to set. + * @return This builder for chaining. + */ + public Builder setClanNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + + private int world_ ; + /** + * required int32 world = 3; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required int32 world = 3; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + /** + * required int32 world = 3; + * @param value The world to set. + * @return This builder for chaining. + */ + public Builder setWorld(int value) { + bitField0_ |= 0x00000004; + world_ = value; + onChanged(); + return this; + } + /** + * required int32 world = 3; + * @return This builder for chaining. + */ + public Builder clearWorld() { + bitField0_ = (bitField0_ & ~0x00000004); + world_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.ClanLeaveNotification) + } + + // @@protoc_insertion_point(class_scope:management.ClanLeaveNotification) + private static final proto.management.ClanLeaveNotification DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.ClanLeaveNotification(); + } + + public static proto.management.ClanLeaveNotification getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ClanLeaveNotification parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ClanLeaveNotification(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.ClanLeaveNotification getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/ClanLeaveNotificationOrBuilder.java b/Server/src/main/proto/management/ClanLeaveNotificationOrBuilder.java new file mode 100644 index 000000000..4d52ca3e5 --- /dev/null +++ b/Server/src/main/proto/management/ClanLeaveNotificationOrBuilder.java @@ -0,0 +1,54 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface ClanLeaveNotificationOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.ClanLeaveNotification) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + boolean hasClanName(); + /** + * required string clanName = 2; + * @return The clanName. + */ + java.lang.String getClanName(); + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + com.google.protobuf.ByteString + getClanNameBytes(); + + /** + * required int32 world = 3; + * @return Whether the world field is set. + */ + boolean hasWorld(); + /** + * required int32 world = 3; + * @return The world. + */ + int getWorld(); +} diff --git a/Server/src/main/proto/management/ClanMessage.java b/Server/src/main/proto/management/ClanMessage.java new file mode 100644 index 000000000..65af29d19 --- /dev/null +++ b/Server/src/main/proto/management/ClanMessage.java @@ -0,0 +1,1036 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.ClanMessage} + */ +public final class ClanMessage extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.ClanMessage) + ClanMessageOrBuilder { +private static final long serialVersionUID = 0L; + // Use ClanMessage.newBuilder() to construct. + private ClanMessage(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private ClanMessage() { + sender_ = ""; + clanName_ = ""; + message_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ClanMessage(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ClanMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + sender_ = bs; + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + clanName_ = bs; + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + message_ = bs; + break; + } + case 32: { + bitField0_ |= 0x00000008; + rank_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_ClanMessage_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_ClanMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.ClanMessage.class, proto.management.ClanMessage.Builder.class); + } + + private int bitField0_; + public static final int SENDER_FIELD_NUMBER = 1; + private volatile java.lang.Object sender_; + /** + * required string sender = 1; + * @return Whether the sender field is set. + */ + @java.lang.Override + public boolean hasSender() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string sender = 1; + * @return The sender. + */ + @java.lang.Override + public java.lang.String getSender() { + java.lang.Object ref = sender_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sender_ = s; + } + return s; + } + } + /** + * required string sender = 1; + * @return The bytes for sender. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getSenderBytes() { + java.lang.Object ref = sender_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CLANNAME_FIELD_NUMBER = 2; + private volatile java.lang.Object clanName_; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + @java.lang.Override + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + @java.lang.Override + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int MESSAGE_FIELD_NUMBER = 3; + private volatile java.lang.Object message_; + /** + * required string message = 3; + * @return Whether the message field is set. + */ + @java.lang.Override + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required string message = 3; + * @return The message. + */ + @java.lang.Override + public java.lang.String getMessage() { + java.lang.Object ref = message_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + message_ = s; + } + return s; + } + } + /** + * required string message = 3; + * @return The bytes for message. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getMessageBytes() { + java.lang.Object ref = message_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int RANK_FIELD_NUMBER = 4; + private int rank_; + /** + * required int32 rank = 4; + * @return Whether the rank field is set. + */ + @java.lang.Override + public boolean hasRank() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * required int32 rank = 4; + * @return The rank. + */ + @java.lang.Override + public int getRank() { + return rank_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasSender()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasClanName()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasMessage()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasRank()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, sender_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, clanName_); + } + if (((bitField0_ & 0x00000004) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, message_); + } + if (((bitField0_ & 0x00000008) != 0)) { + output.writeInt32(4, rank_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, sender_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, clanName_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, message_); + } + if (((bitField0_ & 0x00000008) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(4, rank_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.ClanMessage)) { + return super.equals(obj); + } + proto.management.ClanMessage other = (proto.management.ClanMessage) obj; + + if (hasSender() != other.hasSender()) return false; + if (hasSender()) { + if (!getSender() + .equals(other.getSender())) return false; + } + if (hasClanName() != other.hasClanName()) return false; + if (hasClanName()) { + if (!getClanName() + .equals(other.getClanName())) return false; + } + if (hasMessage() != other.hasMessage()) return false; + if (hasMessage()) { + if (!getMessage() + .equals(other.getMessage())) return false; + } + if (hasRank() != other.hasRank()) return false; + if (hasRank()) { + if (getRank() + != other.getRank()) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasSender()) { + hash = (37 * hash) + SENDER_FIELD_NUMBER; + hash = (53 * hash) + getSender().hashCode(); + } + if (hasClanName()) { + hash = (37 * hash) + CLANNAME_FIELD_NUMBER; + hash = (53 * hash) + getClanName().hashCode(); + } + if (hasMessage()) { + hash = (37 * hash) + MESSAGE_FIELD_NUMBER; + hash = (53 * hash) + getMessage().hashCode(); + } + if (hasRank()) { + hash = (37 * hash) + RANK_FIELD_NUMBER; + hash = (53 * hash) + getRank(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.ClanMessage parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanMessage parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.ClanMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.ClanMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.ClanMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.ClanMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.ClanMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.ClanMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.ClanMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.ClanMessage prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.ClanMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.ClanMessage) + proto.management.ClanMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_ClanMessage_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_ClanMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.ClanMessage.class, proto.management.ClanMessage.Builder.class); + } + + // Construct using proto.management.ClanMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + sender_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + clanName_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + message_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + rank_ = 0; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_ClanMessage_descriptor; + } + + @java.lang.Override + public proto.management.ClanMessage getDefaultInstanceForType() { + return proto.management.ClanMessage.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.ClanMessage build() { + proto.management.ClanMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.ClanMessage buildPartial() { + proto.management.ClanMessage result = new proto.management.ClanMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.sender_ = sender_; + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.clanName_ = clanName_; + if (((from_bitField0_ & 0x00000004) != 0)) { + to_bitField0_ |= 0x00000004; + } + result.message_ = message_; + if (((from_bitField0_ & 0x00000008) != 0)) { + result.rank_ = rank_; + to_bitField0_ |= 0x00000008; + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.ClanMessage) { + return mergeFrom((proto.management.ClanMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.ClanMessage other) { + if (other == proto.management.ClanMessage.getDefaultInstance()) return this; + if (other.hasSender()) { + bitField0_ |= 0x00000001; + sender_ = other.sender_; + onChanged(); + } + if (other.hasClanName()) { + bitField0_ |= 0x00000002; + clanName_ = other.clanName_; + onChanged(); + } + if (other.hasMessage()) { + bitField0_ |= 0x00000004; + message_ = other.message_; + onChanged(); + } + if (other.hasRank()) { + setRank(other.getRank()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasSender()) { + return false; + } + if (!hasClanName()) { + return false; + } + if (!hasMessage()) { + return false; + } + if (!hasRank()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.ClanMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.ClanMessage) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object sender_ = ""; + /** + * required string sender = 1; + * @return Whether the sender field is set. + */ + public boolean hasSender() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string sender = 1; + * @return The sender. + */ + public java.lang.String getSender() { + java.lang.Object ref = sender_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sender_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string sender = 1; + * @return The bytes for sender. + */ + public com.google.protobuf.ByteString + getSenderBytes() { + java.lang.Object ref = sender_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string sender = 1; + * @param value The sender to set. + * @return This builder for chaining. + */ + public Builder setSender( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + /** + * required string sender = 1; + * @return This builder for chaining. + */ + public Builder clearSender() { + bitField0_ = (bitField0_ & ~0x00000001); + sender_ = getDefaultInstance().getSender(); + onChanged(); + return this; + } + /** + * required string sender = 1; + * @param value The bytes for sender to set. + * @return This builder for chaining. + */ + public Builder setSenderBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + + private java.lang.Object clanName_ = ""; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string clanName = 2; + * @param value The clanName to set. + * @return This builder for chaining. + */ + public Builder setClanName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @return This builder for chaining. + */ + public Builder clearClanName() { + bitField0_ = (bitField0_ & ~0x00000002); + clanName_ = getDefaultInstance().getClanName(); + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @param value The bytes for clanName to set. + * @return This builder for chaining. + */ + public Builder setClanNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + + private java.lang.Object message_ = ""; + /** + * required string message = 3; + * @return Whether the message field is set. + */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required string message = 3; + * @return The message. + */ + public java.lang.String getMessage() { + java.lang.Object ref = message_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + message_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string message = 3; + * @return The bytes for message. + */ + public com.google.protobuf.ByteString + getMessageBytes() { + java.lang.Object ref = message_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string message = 3; + * @param value The message to set. + * @return This builder for chaining. + */ + public Builder setMessage( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + message_ = value; + onChanged(); + return this; + } + /** + * required string message = 3; + * @return This builder for chaining. + */ + public Builder clearMessage() { + bitField0_ = (bitField0_ & ~0x00000004); + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + /** + * required string message = 3; + * @param value The bytes for message to set. + * @return This builder for chaining. + */ + public Builder setMessageBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + message_ = value; + onChanged(); + return this; + } + + private int rank_ ; + /** + * required int32 rank = 4; + * @return Whether the rank field is set. + */ + @java.lang.Override + public boolean hasRank() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * required int32 rank = 4; + * @return The rank. + */ + @java.lang.Override + public int getRank() { + return rank_; + } + /** + * required int32 rank = 4; + * @param value The rank to set. + * @return This builder for chaining. + */ + public Builder setRank(int value) { + bitField0_ |= 0x00000008; + rank_ = value; + onChanged(); + return this; + } + /** + * required int32 rank = 4; + * @return This builder for chaining. + */ + public Builder clearRank() { + bitField0_ = (bitField0_ & ~0x00000008); + rank_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.ClanMessage) + } + + // @@protoc_insertion_point(class_scope:management.ClanMessage) + private static final proto.management.ClanMessage DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.ClanMessage(); + } + + public static proto.management.ClanMessage getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ClanMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ClanMessage(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.ClanMessage getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/ClanMessageOrBuilder.java b/Server/src/main/proto/management/ClanMessageOrBuilder.java new file mode 100644 index 000000000..a9b48b64f --- /dev/null +++ b/Server/src/main/proto/management/ClanMessageOrBuilder.java @@ -0,0 +1,71 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface ClanMessageOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.ClanMessage) + com.google.protobuf.MessageOrBuilder { + + /** + * required string sender = 1; + * @return Whether the sender field is set. + */ + boolean hasSender(); + /** + * required string sender = 1; + * @return The sender. + */ + java.lang.String getSender(); + /** + * required string sender = 1; + * @return The bytes for sender. + */ + com.google.protobuf.ByteString + getSenderBytes(); + + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + boolean hasClanName(); + /** + * required string clanName = 2; + * @return The clanName. + */ + java.lang.String getClanName(); + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + com.google.protobuf.ByteString + getClanNameBytes(); + + /** + * required string message = 3; + * @return Whether the message field is set. + */ + boolean hasMessage(); + /** + * required string message = 3; + * @return The message. + */ + java.lang.String getMessage(); + /** + * required string message = 3; + * @return The bytes for message. + */ + com.google.protobuf.ByteString + getMessageBytes(); + + /** + * required int32 rank = 4; + * @return Whether the rank field is set. + */ + boolean hasRank(); + /** + * required int32 rank = 4; + * @return The rank. + */ + int getRank(); +} diff --git a/Server/src/main/proto/management/ClanSettingsUpdateOrBuilder.java b/Server/src/main/proto/management/ClanSettingsUpdateOrBuilder.java new file mode 100644 index 000000000..61150bd9e --- /dev/null +++ b/Server/src/main/proto/management/ClanSettingsUpdateOrBuilder.java @@ -0,0 +1,9 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface ClanSettingsUpdateOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.ClanSettingsUpdate) + com.google.protobuf.MessageOrBuilder { +} diff --git a/Server/src/main/proto/management/FriendUpdate.java b/Server/src/main/proto/management/FriendUpdate.java new file mode 100644 index 000000000..b8c13f277 --- /dev/null +++ b/Server/src/main/proto/management/FriendUpdate.java @@ -0,0 +1,971 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.FriendUpdate} + */ +public final class FriendUpdate extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.FriendUpdate) + FriendUpdateOrBuilder { +private static final long serialVersionUID = 0L; + // Use FriendUpdate.newBuilder() to construct. + private FriendUpdate(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private FriendUpdate() { + type_ = 0; + username_ = ""; + friend_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new FriendUpdate(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private FriendUpdate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + int rawValue = input.readEnum(); + @SuppressWarnings("deprecation") + proto.management.FriendUpdate.Type value = proto.management.FriendUpdate.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = rawValue; + } + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + username_ = bs; + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + friend_ = bs; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_FriendUpdate_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_FriendUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.FriendUpdate.class, proto.management.FriendUpdate.Builder.class); + } + + /** + * Protobuf enum {@code management.FriendUpdate.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * ADD = 0; + */ + ADD(0), + /** + * REMOVE = 1; + */ + REMOVE(1), + ; + + /** + * ADD = 0; + */ + public static final int ADD_VALUE = 0; + /** + * REMOVE = 1; + */ + public static final int REMOVE_VALUE = 1; + + + public final int getNumber() { + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static Type valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static Type forNumber(int value) { + switch (value) { + case 0: return ADD; + case 1: return REMOVE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + Type> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return proto.management.FriendUpdate.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private Type(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:management.FriendUpdate.Type) + } + + private int bitField0_; + public static final int TYPE_FIELD_NUMBER = 1; + private int type_; + /** + * required .management.FriendUpdate.Type type = 1; + * @return Whether the type field is set. + */ + @java.lang.Override public boolean hasType() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required .management.FriendUpdate.Type type = 1; + * @return The type. + */ + @java.lang.Override public proto.management.FriendUpdate.Type getType() { + @SuppressWarnings("deprecation") + proto.management.FriendUpdate.Type result = proto.management.FriendUpdate.Type.valueOf(type_); + return result == null ? proto.management.FriendUpdate.Type.ADD : result; + } + + public static final int USERNAME_FIELD_NUMBER = 2; + private volatile java.lang.Object username_; + /** + * required string username = 2; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string username = 2; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 2; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int FRIEND_FIELD_NUMBER = 3; + private volatile java.lang.Object friend_; + /** + * required string friend = 3; + * @return Whether the friend field is set. + */ + @java.lang.Override + public boolean hasFriend() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required string friend = 3; + * @return The friend. + */ + @java.lang.Override + public java.lang.String getFriend() { + java.lang.Object ref = friend_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + friend_ = s; + } + return s; + } + } + /** + * required string friend = 3; + * @return The bytes for friend. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getFriendBytes() { + java.lang.Object ref = friend_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + friend_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasType()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasFriend()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeEnum(1, type_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, username_); + } + if (((bitField0_ & 0x00000004) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, friend_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, username_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, friend_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.FriendUpdate)) { + return super.equals(obj); + } + proto.management.FriendUpdate other = (proto.management.FriendUpdate) obj; + + if (hasType() != other.hasType()) return false; + if (hasType()) { + if (type_ != other.type_) return false; + } + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasFriend() != other.hasFriend()) return false; + if (hasFriend()) { + if (!getFriend() + .equals(other.getFriend())) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasType()) { + hash = (37 * hash) + TYPE_FIELD_NUMBER; + hash = (53 * hash) + type_; + } + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasFriend()) { + hash = (37 * hash) + FRIEND_FIELD_NUMBER; + hash = (53 * hash) + getFriend().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.FriendUpdate parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.FriendUpdate parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.FriendUpdate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.FriendUpdate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.FriendUpdate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.FriendUpdate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.FriendUpdate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.FriendUpdate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.FriendUpdate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.FriendUpdate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.FriendUpdate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.FriendUpdate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.FriendUpdate prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.FriendUpdate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.FriendUpdate) + proto.management.FriendUpdateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_FriendUpdate_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_FriendUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.FriendUpdate.class, proto.management.FriendUpdate.Builder.class); + } + + // Construct using proto.management.FriendUpdate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + type_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + friend_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_FriendUpdate_descriptor; + } + + @java.lang.Override + public proto.management.FriendUpdate getDefaultInstanceForType() { + return proto.management.FriendUpdate.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.FriendUpdate build() { + proto.management.FriendUpdate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.FriendUpdate buildPartial() { + proto.management.FriendUpdate result = new proto.management.FriendUpdate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000004) != 0)) { + to_bitField0_ |= 0x00000004; + } + result.friend_ = friend_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.FriendUpdate) { + return mergeFrom((proto.management.FriendUpdate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.FriendUpdate other) { + if (other == proto.management.FriendUpdate.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasUsername()) { + bitField0_ |= 0x00000002; + username_ = other.username_; + onChanged(); + } + if (other.hasFriend()) { + bitField0_ |= 0x00000004; + friend_ = other.friend_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasType()) { + return false; + } + if (!hasUsername()) { + return false; + } + if (!hasFriend()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.FriendUpdate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.FriendUpdate) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int type_ = 0; + /** + * required .management.FriendUpdate.Type type = 1; + * @return Whether the type field is set. + */ + @java.lang.Override public boolean hasType() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required .management.FriendUpdate.Type type = 1; + * @return The type. + */ + @java.lang.Override + public proto.management.FriendUpdate.Type getType() { + @SuppressWarnings("deprecation") + proto.management.FriendUpdate.Type result = proto.management.FriendUpdate.Type.valueOf(type_); + return result == null ? proto.management.FriendUpdate.Type.ADD : result; + } + /** + * required .management.FriendUpdate.Type type = 1; + * @param value The type to set. + * @return This builder for chaining. + */ + public Builder setType(proto.management.FriendUpdate.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value.getNumber(); + onChanged(); + return this; + } + /** + * required .management.FriendUpdate.Type type = 1; + * @return This builder for chaining. + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = 0; + onChanged(); + return this; + } + + private java.lang.Object username_ = ""; + /** + * required string username = 2; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string username = 2; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 2; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 2; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 2; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000002); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 2; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + username_ = value; + onChanged(); + return this; + } + + private java.lang.Object friend_ = ""; + /** + * required string friend = 3; + * @return Whether the friend field is set. + */ + public boolean hasFriend() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required string friend = 3; + * @return The friend. + */ + public java.lang.String getFriend() { + java.lang.Object ref = friend_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + friend_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string friend = 3; + * @return The bytes for friend. + */ + public com.google.protobuf.ByteString + getFriendBytes() { + java.lang.Object ref = friend_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + friend_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string friend = 3; + * @param value The friend to set. + * @return This builder for chaining. + */ + public Builder setFriend( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + friend_ = value; + onChanged(); + return this; + } + /** + * required string friend = 3; + * @return This builder for chaining. + */ + public Builder clearFriend() { + bitField0_ = (bitField0_ & ~0x00000004); + friend_ = getDefaultInstance().getFriend(); + onChanged(); + return this; + } + /** + * required string friend = 3; + * @param value The bytes for friend to set. + * @return This builder for chaining. + */ + public Builder setFriendBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + friend_ = value; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.FriendUpdate) + } + + // @@protoc_insertion_point(class_scope:management.FriendUpdate) + private static final proto.management.FriendUpdate DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.FriendUpdate(); + } + + public static proto.management.FriendUpdate getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public FriendUpdate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new FriendUpdate(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.FriendUpdate getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/FriendUpdateOrBuilder.java b/Server/src/main/proto/management/FriendUpdateOrBuilder.java new file mode 100644 index 000000000..2f63a1db9 --- /dev/null +++ b/Server/src/main/proto/management/FriendUpdateOrBuilder.java @@ -0,0 +1,54 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface FriendUpdateOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.FriendUpdate) + com.google.protobuf.MessageOrBuilder { + + /** + * required .management.FriendUpdate.Type type = 1; + * @return Whether the type field is set. + */ + boolean hasType(); + /** + * required .management.FriendUpdate.Type type = 1; + * @return The type. + */ + proto.management.FriendUpdate.Type getType(); + + /** + * required string username = 2; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 2; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 2; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required string friend = 3; + * @return Whether the friend field is set. + */ + boolean hasFriend(); + /** + * required string friend = 3; + * @return The friend. + */ + java.lang.String getFriend(); + /** + * required string friend = 3; + * @return The bytes for friend. + */ + com.google.protobuf.ByteString + getFriendBytes(); +} diff --git a/Server/src/main/proto/management/JoinClanRequest.java b/Server/src/main/proto/management/JoinClanRequest.java new file mode 100644 index 000000000..c578b69d7 --- /dev/null +++ b/Server/src/main/proto/management/JoinClanRequest.java @@ -0,0 +1,769 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.JoinClanRequest} + */ +public final class JoinClanRequest extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.JoinClanRequest) + JoinClanRequestOrBuilder { +private static final long serialVersionUID = 0L; + // Use JoinClanRequest.newBuilder() to construct. + private JoinClanRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private JoinClanRequest() { + username_ = ""; + clanName_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new JoinClanRequest(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private JoinClanRequest( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + clanName_ = bs; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_JoinClanRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_JoinClanRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.JoinClanRequest.class, proto.management.JoinClanRequest.Builder.class); + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CLANNAME_FIELD_NUMBER = 2; + private volatile java.lang.Object clanName_; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + @java.lang.Override + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + @java.lang.Override + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasClanName()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, clanName_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, clanName_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.JoinClanRequest)) { + return super.equals(obj); + } + proto.management.JoinClanRequest other = (proto.management.JoinClanRequest) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasClanName() != other.hasClanName()) return false; + if (hasClanName()) { + if (!getClanName() + .equals(other.getClanName())) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasClanName()) { + hash = (37 * hash) + CLANNAME_FIELD_NUMBER; + hash = (53 * hash) + getClanName().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.JoinClanRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.JoinClanRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.JoinClanRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.JoinClanRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.JoinClanRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.JoinClanRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.JoinClanRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.JoinClanRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.JoinClanRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.JoinClanRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.JoinClanRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.JoinClanRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.JoinClanRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.JoinClanRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.JoinClanRequest) + proto.management.JoinClanRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_JoinClanRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_JoinClanRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.JoinClanRequest.class, proto.management.JoinClanRequest.Builder.class); + } + + // Construct using proto.management.JoinClanRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + clanName_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_JoinClanRequest_descriptor; + } + + @java.lang.Override + public proto.management.JoinClanRequest getDefaultInstanceForType() { + return proto.management.JoinClanRequest.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.JoinClanRequest build() { + proto.management.JoinClanRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.JoinClanRequest buildPartial() { + proto.management.JoinClanRequest result = new proto.management.JoinClanRequest(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.clanName_ = clanName_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.JoinClanRequest) { + return mergeFrom((proto.management.JoinClanRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.JoinClanRequest other) { + if (other == proto.management.JoinClanRequest.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (other.hasClanName()) { + bitField0_ |= 0x00000002; + clanName_ = other.clanName_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + if (!hasClanName()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.JoinClanRequest parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.JoinClanRequest) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private java.lang.Object clanName_ = ""; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string clanName = 2; + * @param value The clanName to set. + * @return This builder for chaining. + */ + public Builder setClanName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @return This builder for chaining. + */ + public Builder clearClanName() { + bitField0_ = (bitField0_ & ~0x00000002); + clanName_ = getDefaultInstance().getClanName(); + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @param value The bytes for clanName to set. + * @return This builder for chaining. + */ + public Builder setClanNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.JoinClanRequest) + } + + // @@protoc_insertion_point(class_scope:management.JoinClanRequest) + private static final proto.management.JoinClanRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.JoinClanRequest(); + } + + public static proto.management.JoinClanRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public JoinClanRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new JoinClanRequest(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.JoinClanRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/JoinClanRequestOrBuilder.java b/Server/src/main/proto/management/JoinClanRequestOrBuilder.java new file mode 100644 index 000000000..ab5ddd72d --- /dev/null +++ b/Server/src/main/proto/management/JoinClanRequestOrBuilder.java @@ -0,0 +1,43 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface JoinClanRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.JoinClanRequest) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + boolean hasClanName(); + /** + * required string clanName = 2; + * @return The clanName. + */ + java.lang.String getClanName(); + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + com.google.protobuf.ByteString + getClanNameBytes(); +} diff --git a/Server/src/main/proto/management/LeaveClanRequest.java b/Server/src/main/proto/management/LeaveClanRequest.java new file mode 100644 index 000000000..ab8252878 --- /dev/null +++ b/Server/src/main/proto/management/LeaveClanRequest.java @@ -0,0 +1,769 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.LeaveClanRequest} + */ +public final class LeaveClanRequest extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.LeaveClanRequest) + LeaveClanRequestOrBuilder { +private static final long serialVersionUID = 0L; + // Use LeaveClanRequest.newBuilder() to construct. + private LeaveClanRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private LeaveClanRequest() { + username_ = ""; + clanName_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new LeaveClanRequest(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private LeaveClanRequest( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + clanName_ = bs; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_LeaveClanRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_LeaveClanRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.LeaveClanRequest.class, proto.management.LeaveClanRequest.Builder.class); + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CLANNAME_FIELD_NUMBER = 2; + private volatile java.lang.Object clanName_; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + @java.lang.Override + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + @java.lang.Override + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasClanName()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, clanName_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, clanName_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.LeaveClanRequest)) { + return super.equals(obj); + } + proto.management.LeaveClanRequest other = (proto.management.LeaveClanRequest) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasClanName() != other.hasClanName()) return false; + if (hasClanName()) { + if (!getClanName() + .equals(other.getClanName())) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasClanName()) { + hash = (37 * hash) + CLANNAME_FIELD_NUMBER; + hash = (53 * hash) + getClanName().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.LeaveClanRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.LeaveClanRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.LeaveClanRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.LeaveClanRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.LeaveClanRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.LeaveClanRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.LeaveClanRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.LeaveClanRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.LeaveClanRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.LeaveClanRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.LeaveClanRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.LeaveClanRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.LeaveClanRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.LeaveClanRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.LeaveClanRequest) + proto.management.LeaveClanRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_LeaveClanRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_LeaveClanRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.LeaveClanRequest.class, proto.management.LeaveClanRequest.Builder.class); + } + + // Construct using proto.management.LeaveClanRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + clanName_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_LeaveClanRequest_descriptor; + } + + @java.lang.Override + public proto.management.LeaveClanRequest getDefaultInstanceForType() { + return proto.management.LeaveClanRequest.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.LeaveClanRequest build() { + proto.management.LeaveClanRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.LeaveClanRequest buildPartial() { + proto.management.LeaveClanRequest result = new proto.management.LeaveClanRequest(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.clanName_ = clanName_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.LeaveClanRequest) { + return mergeFrom((proto.management.LeaveClanRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.LeaveClanRequest other) { + if (other == proto.management.LeaveClanRequest.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (other.hasClanName()) { + bitField0_ |= 0x00000002; + clanName_ = other.clanName_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + if (!hasClanName()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.LeaveClanRequest parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.LeaveClanRequest) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private java.lang.Object clanName_ = ""; + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + public boolean hasClanName() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanName = 2; + * @return The clanName. + */ + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string clanName = 2; + * @param value The clanName to set. + * @return This builder for chaining. + */ + public Builder setClanName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @return This builder for chaining. + */ + public Builder clearClanName() { + bitField0_ = (bitField0_ & ~0x00000002); + clanName_ = getDefaultInstance().getClanName(); + onChanged(); + return this; + } + /** + * required string clanName = 2; + * @param value The bytes for clanName to set. + * @return This builder for chaining. + */ + public Builder setClanNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanName_ = value; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.LeaveClanRequest) + } + + // @@protoc_insertion_point(class_scope:management.LeaveClanRequest) + private static final proto.management.LeaveClanRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.LeaveClanRequest(); + } + + public static proto.management.LeaveClanRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public LeaveClanRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new LeaveClanRequest(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.LeaveClanRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/LeaveClanRequestOrBuilder.java b/Server/src/main/proto/management/LeaveClanRequestOrBuilder.java new file mode 100644 index 000000000..2a25695e3 --- /dev/null +++ b/Server/src/main/proto/management/LeaveClanRequestOrBuilder.java @@ -0,0 +1,43 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface LeaveClanRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.LeaveClanRequest) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required string clanName = 2; + * @return Whether the clanName field is set. + */ + boolean hasClanName(); + /** + * required string clanName = 2; + * @return The clanName. + */ + java.lang.String getClanName(); + /** + * required string clanName = 2; + * @return The bytes for clanName. + */ + com.google.protobuf.ByteString + getClanNameBytes(); +} diff --git a/Server/src/main/proto/management/ManagementProtos.java b/Server/src/main/proto/management/ManagementProtos.java new file mode 100644 index 000000000..78a9004e2 --- /dev/null +++ b/Server/src/main/proto/management/ManagementProtos.java @@ -0,0 +1,234 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public final class ManagementProtos { + private ManagementProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_PlayerStatusUpdate_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_PlayerStatusUpdate_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_ClanMessage_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_ClanMessage_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_PrivateMessage_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_PrivateMessage_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_RequestContactInfo_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_RequestContactInfo_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_SendContactInfo_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_SendContactInfo_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_SendContactInfo_Contact_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_SendContactInfo_Contact_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_FriendUpdate_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_FriendUpdate_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_BlockedUpdate_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_BlockedUpdate_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_RequestClanInfo_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_RequestClanInfo_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_SendClanInfo_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_SendClanInfo_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_SendClanInfo_ClanMember_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_SendClanInfo_ClanMember_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_JoinClanRequest_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_JoinClanRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_LeaveClanRequest_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_LeaveClanRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_ClanJoinNotification_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_ClanJoinNotification_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_management_ClanLeaveNotification_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_management_ClanLeaveNotification_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\020Management.proto\022\nmanagement\"P\n\022Player" + + "StatusUpdate\022\020\n\010username\030\001 \002(\t\022\r\n\005world\030" + + "\002 \002(\005\022\031\n\021notifyFriendsOnly\030\003 \002(\010\"N\n\013Clan" + + "Message\022\016\n\006sender\030\001 \002(\t\022\020\n\010clanName\030\002 \002(" + + "\t\022\017\n\007message\030\003 \002(\t\022\014\n\004rank\030\004 \002(\005\"Q\n\016Priv" + + "ateMessage\022\016\n\006sender\030\001 \002(\t\022\020\n\010receiver\030\002" + + " \002(\t\022\017\n\007message\030\003 \002(\t\022\014\n\004rank\030\004 \002(\005\"5\n\022R" + + "equestContactInfo\022\020\n\010username\030\001 \002(\t\022\r\n\005w" + + "orld\030\002 \002(\005\"\245\001\n\017SendContactInfo\022\020\n\010userna" + + "me\030\001 \002(\t\0225\n\010contacts\030\002 \003(\0132#.management." + + "SendContactInfo.Contact\022\017\n\007blocked\030\003 \003(\t" + + "\0328\n\007Contact\022\020\n\010username\030\001 \002(\t\022\r\n\005world\030\002" + + " \001(\005\022\014\n\004rank\030\003 \001(\005\"z\n\014FriendUpdate\022+\n\004ty" + + "pe\030\001 \002(\0162\035.management.FriendUpdate.Type\022" + + "\020\n\010username\030\002 \002(\t\022\016\n\006friend\030\003 \002(\t\"\033\n\004Typ" + + "e\022\007\n\003ADD\020\000\022\n\n\006REMOVE\020\001\"|\n\rBlockedUpdate\022" + + ",\n\004type\030\001 \002(\0162\036.management.BlockedUpdate" + + ".Type\022\020\n\010username\030\002 \002(\t\022\016\n\006friend\030\003 \002(\t\"" + + "\033\n\004Type\022\007\n\003ADD\020\000\022\n\n\006REMOVE\020\001\"3\n\017RequestC" + + "lanInfo\022\r\n\005world\030\001 \002(\005\022\021\n\tclanOwner\030\002 \002(" + + "\t\"\236\002\n\014SendClanInfo\022\021\n\tclanOwner\030\001 \002(\t\022\017\n" + + "\007hasInfo\030\002 \002(\010\022\020\n\010clanName\030\003 \001(\t\022\027\n\017join" + + "Requirement\030\004 \001(\005\022\027\n\017kickRequirement\030\005 \001" + + "(\005\022\032\n\022messageRequirement\030\006 \001(\005\022\027\n\017lootRe" + + "quirement\030\007 \001(\005\0224\n\007members\030\010 \003(\0132#.manag" + + "ement.SendClanInfo.ClanMember\032;\n\nClanMem" + + "ber\022\020\n\010username\030\001 \002(\t\022\r\n\005world\030\002 \002(\005\022\014\n\004" + + "rank\030\003 \002(\005\"5\n\017JoinClanRequest\022\020\n\010usernam" + + "e\030\001 \002(\t\022\020\n\010clanName\030\002 \002(\t\"6\n\020LeaveClanRe" + + "quest\022\020\n\010username\030\001 \002(\t\022\020\n\010clanName\030\002 \002(" + + "\t\"I\n\024ClanJoinNotification\022\020\n\010username\030\001 " + + "\002(\t\022\020\n\010clanName\030\002 \002(\t\022\r\n\005world\030\003 \002(\005\"J\n\025" + + "ClanLeaveNotification\022\020\n\010username\030\001 \002(\t\022" + + "\020\n\010clanName\030\002 \002(\t\022\r\n\005world\030\003 \002(\005B&\n\020prot" + + "o.managementB\020ManagementProtosP\001" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_management_PlayerStatusUpdate_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_management_PlayerStatusUpdate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_PlayerStatusUpdate_descriptor, + new java.lang.String[] { "Username", "World", "NotifyFriendsOnly", }); + internal_static_management_ClanMessage_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_management_ClanMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_ClanMessage_descriptor, + new java.lang.String[] { "Sender", "ClanName", "Message", "Rank", }); + internal_static_management_PrivateMessage_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_management_PrivateMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_PrivateMessage_descriptor, + new java.lang.String[] { "Sender", "Receiver", "Message", "Rank", }); + internal_static_management_RequestContactInfo_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_management_RequestContactInfo_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_RequestContactInfo_descriptor, + new java.lang.String[] { "Username", "World", }); + internal_static_management_SendContactInfo_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_management_SendContactInfo_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_SendContactInfo_descriptor, + new java.lang.String[] { "Username", "Contacts", "Blocked", }); + internal_static_management_SendContactInfo_Contact_descriptor = + internal_static_management_SendContactInfo_descriptor.getNestedTypes().get(0); + internal_static_management_SendContactInfo_Contact_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_SendContactInfo_Contact_descriptor, + new java.lang.String[] { "Username", "World", "Rank", }); + internal_static_management_FriendUpdate_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_management_FriendUpdate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_FriendUpdate_descriptor, + new java.lang.String[] { "Type", "Username", "Friend", }); + internal_static_management_BlockedUpdate_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_management_BlockedUpdate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_BlockedUpdate_descriptor, + new java.lang.String[] { "Type", "Username", "Friend", }); + internal_static_management_RequestClanInfo_descriptor = + getDescriptor().getMessageTypes().get(7); + internal_static_management_RequestClanInfo_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_RequestClanInfo_descriptor, + new java.lang.String[] { "World", "ClanOwner", }); + internal_static_management_SendClanInfo_descriptor = + getDescriptor().getMessageTypes().get(8); + internal_static_management_SendClanInfo_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_SendClanInfo_descriptor, + new java.lang.String[] { "ClanOwner", "HasInfo", "ClanName", "JoinRequirement", "KickRequirement", "MessageRequirement", "LootRequirement", "Members", }); + internal_static_management_SendClanInfo_ClanMember_descriptor = + internal_static_management_SendClanInfo_descriptor.getNestedTypes().get(0); + internal_static_management_SendClanInfo_ClanMember_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_SendClanInfo_ClanMember_descriptor, + new java.lang.String[] { "Username", "World", "Rank", }); + internal_static_management_JoinClanRequest_descriptor = + getDescriptor().getMessageTypes().get(9); + internal_static_management_JoinClanRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_JoinClanRequest_descriptor, + new java.lang.String[] { "Username", "ClanName", }); + internal_static_management_LeaveClanRequest_descriptor = + getDescriptor().getMessageTypes().get(10); + internal_static_management_LeaveClanRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_LeaveClanRequest_descriptor, + new java.lang.String[] { "Username", "ClanName", }); + internal_static_management_ClanJoinNotification_descriptor = + getDescriptor().getMessageTypes().get(11); + internal_static_management_ClanJoinNotification_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_ClanJoinNotification_descriptor, + new java.lang.String[] { "Username", "ClanName", "World", }); + internal_static_management_ClanLeaveNotification_descriptor = + getDescriptor().getMessageTypes().get(12); + internal_static_management_ClanLeaveNotification_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_management_ClanLeaveNotification_descriptor, + new java.lang.String[] { "Username", "ClanName", "World", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/Server/src/main/proto/management/PlayerStatusUpdate.java b/Server/src/main/proto/management/PlayerStatusUpdate.java new file mode 100644 index 000000000..7ab0532a7 --- /dev/null +++ b/Server/src/main/proto/management/PlayerStatusUpdate.java @@ -0,0 +1,788 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.PlayerStatusUpdate} + */ +public final class PlayerStatusUpdate extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.PlayerStatusUpdate) + PlayerStatusUpdateOrBuilder { +private static final long serialVersionUID = 0L; + // Use PlayerStatusUpdate.newBuilder() to construct. + private PlayerStatusUpdate(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private PlayerStatusUpdate() { + username_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new PlayerStatusUpdate(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PlayerStatusUpdate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 16: { + bitField0_ |= 0x00000002; + world_ = input.readInt32(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + notifyFriendsOnly_ = input.readBool(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_PlayerStatusUpdate_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_PlayerStatusUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.PlayerStatusUpdate.class, proto.management.PlayerStatusUpdate.Builder.class); + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int WORLD_FIELD_NUMBER = 2; + private int world_; + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required int32 world = 2; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + + public static final int NOTIFYFRIENDSONLY_FIELD_NUMBER = 3; + private boolean notifyFriendsOnly_; + /** + * required bool notifyFriendsOnly = 3; + * @return Whether the notifyFriendsOnly field is set. + */ + @java.lang.Override + public boolean hasNotifyFriendsOnly() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required bool notifyFriendsOnly = 3; + * @return The notifyFriendsOnly. + */ + @java.lang.Override + public boolean getNotifyFriendsOnly() { + return notifyFriendsOnly_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasWorld()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasNotifyFriendsOnly()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeInt32(2, world_); + } + if (((bitField0_ & 0x00000004) != 0)) { + output.writeBool(3, notifyFriendsOnly_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, world_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(3, notifyFriendsOnly_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.PlayerStatusUpdate)) { + return super.equals(obj); + } + proto.management.PlayerStatusUpdate other = (proto.management.PlayerStatusUpdate) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasWorld() != other.hasWorld()) return false; + if (hasWorld()) { + if (getWorld() + != other.getWorld()) return false; + } + if (hasNotifyFriendsOnly() != other.hasNotifyFriendsOnly()) return false; + if (hasNotifyFriendsOnly()) { + if (getNotifyFriendsOnly() + != other.getNotifyFriendsOnly()) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasWorld()) { + hash = (37 * hash) + WORLD_FIELD_NUMBER; + hash = (53 * hash) + getWorld(); + } + if (hasNotifyFriendsOnly()) { + hash = (37 * hash) + NOTIFYFRIENDSONLY_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getNotifyFriendsOnly()); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.PlayerStatusUpdate parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.PlayerStatusUpdate parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.PlayerStatusUpdate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.PlayerStatusUpdate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.PlayerStatusUpdate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.PlayerStatusUpdate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.PlayerStatusUpdate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.PlayerStatusUpdate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.PlayerStatusUpdate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.PlayerStatusUpdate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.PlayerStatusUpdate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.PlayerStatusUpdate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.PlayerStatusUpdate prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.PlayerStatusUpdate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.PlayerStatusUpdate) + proto.management.PlayerStatusUpdateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_PlayerStatusUpdate_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_PlayerStatusUpdate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.PlayerStatusUpdate.class, proto.management.PlayerStatusUpdate.Builder.class); + } + + // Construct using proto.management.PlayerStatusUpdate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + world_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + notifyFriendsOnly_ = false; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_PlayerStatusUpdate_descriptor; + } + + @java.lang.Override + public proto.management.PlayerStatusUpdate getDefaultInstanceForType() { + return proto.management.PlayerStatusUpdate.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.PlayerStatusUpdate build() { + proto.management.PlayerStatusUpdate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.PlayerStatusUpdate buildPartial() { + proto.management.PlayerStatusUpdate result = new proto.management.PlayerStatusUpdate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.world_ = world_; + to_bitField0_ |= 0x00000002; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.notifyFriendsOnly_ = notifyFriendsOnly_; + to_bitField0_ |= 0x00000004; + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.PlayerStatusUpdate) { + return mergeFrom((proto.management.PlayerStatusUpdate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.PlayerStatusUpdate other) { + if (other == proto.management.PlayerStatusUpdate.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (other.hasWorld()) { + setWorld(other.getWorld()); + } + if (other.hasNotifyFriendsOnly()) { + setNotifyFriendsOnly(other.getNotifyFriendsOnly()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + if (!hasWorld()) { + return false; + } + if (!hasNotifyFriendsOnly()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.PlayerStatusUpdate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.PlayerStatusUpdate) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private int world_ ; + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required int32 world = 2; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + /** + * required int32 world = 2; + * @param value The world to set. + * @return This builder for chaining. + */ + public Builder setWorld(int value) { + bitField0_ |= 0x00000002; + world_ = value; + onChanged(); + return this; + } + /** + * required int32 world = 2; + * @return This builder for chaining. + */ + public Builder clearWorld() { + bitField0_ = (bitField0_ & ~0x00000002); + world_ = 0; + onChanged(); + return this; + } + + private boolean notifyFriendsOnly_ ; + /** + * required bool notifyFriendsOnly = 3; + * @return Whether the notifyFriendsOnly field is set. + */ + @java.lang.Override + public boolean hasNotifyFriendsOnly() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required bool notifyFriendsOnly = 3; + * @return The notifyFriendsOnly. + */ + @java.lang.Override + public boolean getNotifyFriendsOnly() { + return notifyFriendsOnly_; + } + /** + * required bool notifyFriendsOnly = 3; + * @param value The notifyFriendsOnly to set. + * @return This builder for chaining. + */ + public Builder setNotifyFriendsOnly(boolean value) { + bitField0_ |= 0x00000004; + notifyFriendsOnly_ = value; + onChanged(); + return this; + } + /** + * required bool notifyFriendsOnly = 3; + * @return This builder for chaining. + */ + public Builder clearNotifyFriendsOnly() { + bitField0_ = (bitField0_ & ~0x00000004); + notifyFriendsOnly_ = false; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.PlayerStatusUpdate) + } + + // @@protoc_insertion_point(class_scope:management.PlayerStatusUpdate) + private static final proto.management.PlayerStatusUpdate DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.PlayerStatusUpdate(); + } + + public static proto.management.PlayerStatusUpdate getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public PlayerStatusUpdate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PlayerStatusUpdate(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.PlayerStatusUpdate getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/PlayerStatusUpdateOrBuilder.java b/Server/src/main/proto/management/PlayerStatusUpdateOrBuilder.java new file mode 100644 index 000000000..e43cd3835 --- /dev/null +++ b/Server/src/main/proto/management/PlayerStatusUpdateOrBuilder.java @@ -0,0 +1,48 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface PlayerStatusUpdateOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.PlayerStatusUpdate) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + boolean hasWorld(); + /** + * required int32 world = 2; + * @return The world. + */ + int getWorld(); + + /** + * required bool notifyFriendsOnly = 3; + * @return Whether the notifyFriendsOnly field is set. + */ + boolean hasNotifyFriendsOnly(); + /** + * required bool notifyFriendsOnly = 3; + * @return The notifyFriendsOnly. + */ + boolean getNotifyFriendsOnly(); +} diff --git a/Server/src/main/proto/management/PrivateMessage.java b/Server/src/main/proto/management/PrivateMessage.java new file mode 100644 index 000000000..e86cea55a --- /dev/null +++ b/Server/src/main/proto/management/PrivateMessage.java @@ -0,0 +1,1036 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.PrivateMessage} + */ +public final class PrivateMessage extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.PrivateMessage) + PrivateMessageOrBuilder { +private static final long serialVersionUID = 0L; + // Use PrivateMessage.newBuilder() to construct. + private PrivateMessage(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private PrivateMessage() { + sender_ = ""; + receiver_ = ""; + message_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new PrivateMessage(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PrivateMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + sender_ = bs; + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + receiver_ = bs; + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + message_ = bs; + break; + } + case 32: { + bitField0_ |= 0x00000008; + rank_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_PrivateMessage_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_PrivateMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.PrivateMessage.class, proto.management.PrivateMessage.Builder.class); + } + + private int bitField0_; + public static final int SENDER_FIELD_NUMBER = 1; + private volatile java.lang.Object sender_; + /** + * required string sender = 1; + * @return Whether the sender field is set. + */ + @java.lang.Override + public boolean hasSender() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string sender = 1; + * @return The sender. + */ + @java.lang.Override + public java.lang.String getSender() { + java.lang.Object ref = sender_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sender_ = s; + } + return s; + } + } + /** + * required string sender = 1; + * @return The bytes for sender. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getSenderBytes() { + java.lang.Object ref = sender_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int RECEIVER_FIELD_NUMBER = 2; + private volatile java.lang.Object receiver_; + /** + * required string receiver = 2; + * @return Whether the receiver field is set. + */ + @java.lang.Override + public boolean hasReceiver() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string receiver = 2; + * @return The receiver. + */ + @java.lang.Override + public java.lang.String getReceiver() { + java.lang.Object ref = receiver_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + receiver_ = s; + } + return s; + } + } + /** + * required string receiver = 2; + * @return The bytes for receiver. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getReceiverBytes() { + java.lang.Object ref = receiver_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + receiver_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int MESSAGE_FIELD_NUMBER = 3; + private volatile java.lang.Object message_; + /** + * required string message = 3; + * @return Whether the message field is set. + */ + @java.lang.Override + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required string message = 3; + * @return The message. + */ + @java.lang.Override + public java.lang.String getMessage() { + java.lang.Object ref = message_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + message_ = s; + } + return s; + } + } + /** + * required string message = 3; + * @return The bytes for message. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getMessageBytes() { + java.lang.Object ref = message_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int RANK_FIELD_NUMBER = 4; + private int rank_; + /** + * required int32 rank = 4; + * @return Whether the rank field is set. + */ + @java.lang.Override + public boolean hasRank() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * required int32 rank = 4; + * @return The rank. + */ + @java.lang.Override + public int getRank() { + return rank_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasSender()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasReceiver()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasMessage()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasRank()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, sender_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, receiver_); + } + if (((bitField0_ & 0x00000004) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, message_); + } + if (((bitField0_ & 0x00000008) != 0)) { + output.writeInt32(4, rank_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, sender_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, receiver_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, message_); + } + if (((bitField0_ & 0x00000008) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(4, rank_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.PrivateMessage)) { + return super.equals(obj); + } + proto.management.PrivateMessage other = (proto.management.PrivateMessage) obj; + + if (hasSender() != other.hasSender()) return false; + if (hasSender()) { + if (!getSender() + .equals(other.getSender())) return false; + } + if (hasReceiver() != other.hasReceiver()) return false; + if (hasReceiver()) { + if (!getReceiver() + .equals(other.getReceiver())) return false; + } + if (hasMessage() != other.hasMessage()) return false; + if (hasMessage()) { + if (!getMessage() + .equals(other.getMessage())) return false; + } + if (hasRank() != other.hasRank()) return false; + if (hasRank()) { + if (getRank() + != other.getRank()) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasSender()) { + hash = (37 * hash) + SENDER_FIELD_NUMBER; + hash = (53 * hash) + getSender().hashCode(); + } + if (hasReceiver()) { + hash = (37 * hash) + RECEIVER_FIELD_NUMBER; + hash = (53 * hash) + getReceiver().hashCode(); + } + if (hasMessage()) { + hash = (37 * hash) + MESSAGE_FIELD_NUMBER; + hash = (53 * hash) + getMessage().hashCode(); + } + if (hasRank()) { + hash = (37 * hash) + RANK_FIELD_NUMBER; + hash = (53 * hash) + getRank(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.PrivateMessage parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.PrivateMessage parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.PrivateMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.PrivateMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.PrivateMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.PrivateMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.PrivateMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.PrivateMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.PrivateMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.PrivateMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.PrivateMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.PrivateMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.PrivateMessage prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.PrivateMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.PrivateMessage) + proto.management.PrivateMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_PrivateMessage_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_PrivateMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.PrivateMessage.class, proto.management.PrivateMessage.Builder.class); + } + + // Construct using proto.management.PrivateMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + sender_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + receiver_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + message_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + rank_ = 0; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_PrivateMessage_descriptor; + } + + @java.lang.Override + public proto.management.PrivateMessage getDefaultInstanceForType() { + return proto.management.PrivateMessage.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.PrivateMessage build() { + proto.management.PrivateMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.PrivateMessage buildPartial() { + proto.management.PrivateMessage result = new proto.management.PrivateMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.sender_ = sender_; + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.receiver_ = receiver_; + if (((from_bitField0_ & 0x00000004) != 0)) { + to_bitField0_ |= 0x00000004; + } + result.message_ = message_; + if (((from_bitField0_ & 0x00000008) != 0)) { + result.rank_ = rank_; + to_bitField0_ |= 0x00000008; + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.PrivateMessage) { + return mergeFrom((proto.management.PrivateMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.PrivateMessage other) { + if (other == proto.management.PrivateMessage.getDefaultInstance()) return this; + if (other.hasSender()) { + bitField0_ |= 0x00000001; + sender_ = other.sender_; + onChanged(); + } + if (other.hasReceiver()) { + bitField0_ |= 0x00000002; + receiver_ = other.receiver_; + onChanged(); + } + if (other.hasMessage()) { + bitField0_ |= 0x00000004; + message_ = other.message_; + onChanged(); + } + if (other.hasRank()) { + setRank(other.getRank()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasSender()) { + return false; + } + if (!hasReceiver()) { + return false; + } + if (!hasMessage()) { + return false; + } + if (!hasRank()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.PrivateMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.PrivateMessage) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object sender_ = ""; + /** + * required string sender = 1; + * @return Whether the sender field is set. + */ + public boolean hasSender() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string sender = 1; + * @return The sender. + */ + public java.lang.String getSender() { + java.lang.Object ref = sender_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sender_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string sender = 1; + * @return The bytes for sender. + */ + public com.google.protobuf.ByteString + getSenderBytes() { + java.lang.Object ref = sender_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string sender = 1; + * @param value The sender to set. + * @return This builder for chaining. + */ + public Builder setSender( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + /** + * required string sender = 1; + * @return This builder for chaining. + */ + public Builder clearSender() { + bitField0_ = (bitField0_ & ~0x00000001); + sender_ = getDefaultInstance().getSender(); + onChanged(); + return this; + } + /** + * required string sender = 1; + * @param value The bytes for sender to set. + * @return This builder for chaining. + */ + public Builder setSenderBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + + private java.lang.Object receiver_ = ""; + /** + * required string receiver = 2; + * @return Whether the receiver field is set. + */ + public boolean hasReceiver() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string receiver = 2; + * @return The receiver. + */ + public java.lang.String getReceiver() { + java.lang.Object ref = receiver_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + receiver_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string receiver = 2; + * @return The bytes for receiver. + */ + public com.google.protobuf.ByteString + getReceiverBytes() { + java.lang.Object ref = receiver_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + receiver_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string receiver = 2; + * @param value The receiver to set. + * @return This builder for chaining. + */ + public Builder setReceiver( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + receiver_ = value; + onChanged(); + return this; + } + /** + * required string receiver = 2; + * @return This builder for chaining. + */ + public Builder clearReceiver() { + bitField0_ = (bitField0_ & ~0x00000002); + receiver_ = getDefaultInstance().getReceiver(); + onChanged(); + return this; + } + /** + * required string receiver = 2; + * @param value The bytes for receiver to set. + * @return This builder for chaining. + */ + public Builder setReceiverBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + receiver_ = value; + onChanged(); + return this; + } + + private java.lang.Object message_ = ""; + /** + * required string message = 3; + * @return Whether the message field is set. + */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required string message = 3; + * @return The message. + */ + public java.lang.String getMessage() { + java.lang.Object ref = message_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + message_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string message = 3; + * @return The bytes for message. + */ + public com.google.protobuf.ByteString + getMessageBytes() { + java.lang.Object ref = message_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string message = 3; + * @param value The message to set. + * @return This builder for chaining. + */ + public Builder setMessage( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + message_ = value; + onChanged(); + return this; + } + /** + * required string message = 3; + * @return This builder for chaining. + */ + public Builder clearMessage() { + bitField0_ = (bitField0_ & ~0x00000004); + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + /** + * required string message = 3; + * @param value The bytes for message to set. + * @return This builder for chaining. + */ + public Builder setMessageBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + message_ = value; + onChanged(); + return this; + } + + private int rank_ ; + /** + * required int32 rank = 4; + * @return Whether the rank field is set. + */ + @java.lang.Override + public boolean hasRank() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * required int32 rank = 4; + * @return The rank. + */ + @java.lang.Override + public int getRank() { + return rank_; + } + /** + * required int32 rank = 4; + * @param value The rank to set. + * @return This builder for chaining. + */ + public Builder setRank(int value) { + bitField0_ |= 0x00000008; + rank_ = value; + onChanged(); + return this; + } + /** + * required int32 rank = 4; + * @return This builder for chaining. + */ + public Builder clearRank() { + bitField0_ = (bitField0_ & ~0x00000008); + rank_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.PrivateMessage) + } + + // @@protoc_insertion_point(class_scope:management.PrivateMessage) + private static final proto.management.PrivateMessage DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.PrivateMessage(); + } + + public static proto.management.PrivateMessage getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public PrivateMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PrivateMessage(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.PrivateMessage getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/PrivateMessageOrBuilder.java b/Server/src/main/proto/management/PrivateMessageOrBuilder.java new file mode 100644 index 000000000..071557fa2 --- /dev/null +++ b/Server/src/main/proto/management/PrivateMessageOrBuilder.java @@ -0,0 +1,71 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface PrivateMessageOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.PrivateMessage) + com.google.protobuf.MessageOrBuilder { + + /** + * required string sender = 1; + * @return Whether the sender field is set. + */ + boolean hasSender(); + /** + * required string sender = 1; + * @return The sender. + */ + java.lang.String getSender(); + /** + * required string sender = 1; + * @return The bytes for sender. + */ + com.google.protobuf.ByteString + getSenderBytes(); + + /** + * required string receiver = 2; + * @return Whether the receiver field is set. + */ + boolean hasReceiver(); + /** + * required string receiver = 2; + * @return The receiver. + */ + java.lang.String getReceiver(); + /** + * required string receiver = 2; + * @return The bytes for receiver. + */ + com.google.protobuf.ByteString + getReceiverBytes(); + + /** + * required string message = 3; + * @return Whether the message field is set. + */ + boolean hasMessage(); + /** + * required string message = 3; + * @return The message. + */ + java.lang.String getMessage(); + /** + * required string message = 3; + * @return The bytes for message. + */ + com.google.protobuf.ByteString + getMessageBytes(); + + /** + * required int32 rank = 4; + * @return Whether the rank field is set. + */ + boolean hasRank(); + /** + * required int32 rank = 4; + * @return The rank. + */ + int getRank(); +} diff --git a/Server/src/main/proto/management/RequestClanInfo.java b/Server/src/main/proto/management/RequestClanInfo.java new file mode 100644 index 000000000..761bcf476 --- /dev/null +++ b/Server/src/main/proto/management/RequestClanInfo.java @@ -0,0 +1,692 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.RequestClanInfo} + */ +public final class RequestClanInfo extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.RequestClanInfo) + RequestClanInfoOrBuilder { +private static final long serialVersionUID = 0L; + // Use RequestClanInfo.newBuilder() to construct. + private RequestClanInfo(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private RequestClanInfo() { + clanOwner_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new RequestClanInfo(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private RequestClanInfo( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + bitField0_ |= 0x00000001; + world_ = input.readInt32(); + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + clanOwner_ = bs; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_RequestClanInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_RequestClanInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.RequestClanInfo.class, proto.management.RequestClanInfo.Builder.class); + } + + private int bitField0_; + public static final int WORLD_FIELD_NUMBER = 1; + private int world_; + /** + * required int32 world = 1; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required int32 world = 1; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + + public static final int CLANOWNER_FIELD_NUMBER = 2; + private volatile java.lang.Object clanOwner_; + /** + * required string clanOwner = 2; + * @return Whether the clanOwner field is set. + */ + @java.lang.Override + public boolean hasClanOwner() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanOwner = 2; + * @return The clanOwner. + */ + @java.lang.Override + public java.lang.String getClanOwner() { + java.lang.Object ref = clanOwner_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanOwner_ = s; + } + return s; + } + } + /** + * required string clanOwner = 2; + * @return The bytes for clanOwner. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClanOwnerBytes() { + java.lang.Object ref = clanOwner_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanOwner_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasWorld()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasClanOwner()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeInt32(1, world_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, clanOwner_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, world_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, clanOwner_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.RequestClanInfo)) { + return super.equals(obj); + } + proto.management.RequestClanInfo other = (proto.management.RequestClanInfo) obj; + + if (hasWorld() != other.hasWorld()) return false; + if (hasWorld()) { + if (getWorld() + != other.getWorld()) return false; + } + if (hasClanOwner() != other.hasClanOwner()) return false; + if (hasClanOwner()) { + if (!getClanOwner() + .equals(other.getClanOwner())) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasWorld()) { + hash = (37 * hash) + WORLD_FIELD_NUMBER; + hash = (53 * hash) + getWorld(); + } + if (hasClanOwner()) { + hash = (37 * hash) + CLANOWNER_FIELD_NUMBER; + hash = (53 * hash) + getClanOwner().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.RequestClanInfo parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.RequestClanInfo parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.RequestClanInfo parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.RequestClanInfo parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.RequestClanInfo parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.RequestClanInfo parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.RequestClanInfo parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.RequestClanInfo parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.RequestClanInfo parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.RequestClanInfo parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.RequestClanInfo parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.RequestClanInfo parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.RequestClanInfo prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.RequestClanInfo} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.RequestClanInfo) + proto.management.RequestClanInfoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_RequestClanInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_RequestClanInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.RequestClanInfo.class, proto.management.RequestClanInfo.Builder.class); + } + + // Construct using proto.management.RequestClanInfo.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + world_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + clanOwner_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_RequestClanInfo_descriptor; + } + + @java.lang.Override + public proto.management.RequestClanInfo getDefaultInstanceForType() { + return proto.management.RequestClanInfo.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.RequestClanInfo build() { + proto.management.RequestClanInfo result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.RequestClanInfo buildPartial() { + proto.management.RequestClanInfo result = new proto.management.RequestClanInfo(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.world_ = world_; + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + to_bitField0_ |= 0x00000002; + } + result.clanOwner_ = clanOwner_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.RequestClanInfo) { + return mergeFrom((proto.management.RequestClanInfo)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.RequestClanInfo other) { + if (other == proto.management.RequestClanInfo.getDefaultInstance()) return this; + if (other.hasWorld()) { + setWorld(other.getWorld()); + } + if (other.hasClanOwner()) { + bitField0_ |= 0x00000002; + clanOwner_ = other.clanOwner_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasWorld()) { + return false; + } + if (!hasClanOwner()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.RequestClanInfo parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.RequestClanInfo) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int world_ ; + /** + * required int32 world = 1; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required int32 world = 1; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + /** + * required int32 world = 1; + * @param value The world to set. + * @return This builder for chaining. + */ + public Builder setWorld(int value) { + bitField0_ |= 0x00000001; + world_ = value; + onChanged(); + return this; + } + /** + * required int32 world = 1; + * @return This builder for chaining. + */ + public Builder clearWorld() { + bitField0_ = (bitField0_ & ~0x00000001); + world_ = 0; + onChanged(); + return this; + } + + private java.lang.Object clanOwner_ = ""; + /** + * required string clanOwner = 2; + * @return Whether the clanOwner field is set. + */ + public boolean hasClanOwner() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required string clanOwner = 2; + * @return The clanOwner. + */ + public java.lang.String getClanOwner() { + java.lang.Object ref = clanOwner_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanOwner_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string clanOwner = 2; + * @return The bytes for clanOwner. + */ + public com.google.protobuf.ByteString + getClanOwnerBytes() { + java.lang.Object ref = clanOwner_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanOwner_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string clanOwner = 2; + * @param value The clanOwner to set. + * @return This builder for chaining. + */ + public Builder setClanOwner( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanOwner_ = value; + onChanged(); + return this; + } + /** + * required string clanOwner = 2; + * @return This builder for chaining. + */ + public Builder clearClanOwner() { + bitField0_ = (bitField0_ & ~0x00000002); + clanOwner_ = getDefaultInstance().getClanOwner(); + onChanged(); + return this; + } + /** + * required string clanOwner = 2; + * @param value The bytes for clanOwner to set. + * @return This builder for chaining. + */ + public Builder setClanOwnerBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + clanOwner_ = value; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.RequestClanInfo) + } + + // @@protoc_insertion_point(class_scope:management.RequestClanInfo) + private static final proto.management.RequestClanInfo DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.RequestClanInfo(); + } + + public static proto.management.RequestClanInfo getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public RequestClanInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new RequestClanInfo(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.RequestClanInfo getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/RequestClanInfoOrBuilder.java b/Server/src/main/proto/management/RequestClanInfoOrBuilder.java new file mode 100644 index 000000000..ae0b9bdbe --- /dev/null +++ b/Server/src/main/proto/management/RequestClanInfoOrBuilder.java @@ -0,0 +1,37 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface RequestClanInfoOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.RequestClanInfo) + com.google.protobuf.MessageOrBuilder { + + /** + * required int32 world = 1; + * @return Whether the world field is set. + */ + boolean hasWorld(); + /** + * required int32 world = 1; + * @return The world. + */ + int getWorld(); + + /** + * required string clanOwner = 2; + * @return Whether the clanOwner field is set. + */ + boolean hasClanOwner(); + /** + * required string clanOwner = 2; + * @return The clanOwner. + */ + java.lang.String getClanOwner(); + /** + * required string clanOwner = 2; + * @return The bytes for clanOwner. + */ + com.google.protobuf.ByteString + getClanOwnerBytes(); +} diff --git a/Server/src/main/proto/management/RequestContactInfo.java b/Server/src/main/proto/management/RequestContactInfo.java new file mode 100644 index 000000000..b7eb7c2a0 --- /dev/null +++ b/Server/src/main/proto/management/RequestContactInfo.java @@ -0,0 +1,692 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.RequestContactInfo} + */ +public final class RequestContactInfo extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.RequestContactInfo) + RequestContactInfoOrBuilder { +private static final long serialVersionUID = 0L; + // Use RequestContactInfo.newBuilder() to construct. + private RequestContactInfo(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private RequestContactInfo() { + username_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new RequestContactInfo(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private RequestContactInfo( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 16: { + bitField0_ |= 0x00000002; + world_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_RequestContactInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_RequestContactInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.RequestContactInfo.class, proto.management.RequestContactInfo.Builder.class); + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int WORLD_FIELD_NUMBER = 2; + private int world_; + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required int32 world = 2; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasWorld()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeInt32(2, world_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, world_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.RequestContactInfo)) { + return super.equals(obj); + } + proto.management.RequestContactInfo other = (proto.management.RequestContactInfo) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasWorld() != other.hasWorld()) return false; + if (hasWorld()) { + if (getWorld() + != other.getWorld()) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasWorld()) { + hash = (37 * hash) + WORLD_FIELD_NUMBER; + hash = (53 * hash) + getWorld(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.RequestContactInfo parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.RequestContactInfo parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.RequestContactInfo parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.RequestContactInfo parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.RequestContactInfo parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.RequestContactInfo parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.RequestContactInfo parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.RequestContactInfo parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.RequestContactInfo parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.RequestContactInfo parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.RequestContactInfo parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.RequestContactInfo parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.RequestContactInfo prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.RequestContactInfo} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.RequestContactInfo) + proto.management.RequestContactInfoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_RequestContactInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_RequestContactInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.RequestContactInfo.class, proto.management.RequestContactInfo.Builder.class); + } + + // Construct using proto.management.RequestContactInfo.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + world_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_RequestContactInfo_descriptor; + } + + @java.lang.Override + public proto.management.RequestContactInfo getDefaultInstanceForType() { + return proto.management.RequestContactInfo.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.RequestContactInfo build() { + proto.management.RequestContactInfo result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.RequestContactInfo buildPartial() { + proto.management.RequestContactInfo result = new proto.management.RequestContactInfo(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.world_ = world_; + to_bitField0_ |= 0x00000002; + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.RequestContactInfo) { + return mergeFrom((proto.management.RequestContactInfo)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.RequestContactInfo other) { + if (other == proto.management.RequestContactInfo.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (other.hasWorld()) { + setWorld(other.getWorld()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + if (!hasWorld()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.RequestContactInfo parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.RequestContactInfo) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private int world_ ; + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required int32 world = 2; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + /** + * required int32 world = 2; + * @param value The world to set. + * @return This builder for chaining. + */ + public Builder setWorld(int value) { + bitField0_ |= 0x00000002; + world_ = value; + onChanged(); + return this; + } + /** + * required int32 world = 2; + * @return This builder for chaining. + */ + public Builder clearWorld() { + bitField0_ = (bitField0_ & ~0x00000002); + world_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.RequestContactInfo) + } + + // @@protoc_insertion_point(class_scope:management.RequestContactInfo) + private static final proto.management.RequestContactInfo DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.RequestContactInfo(); + } + + public static proto.management.RequestContactInfo getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public RequestContactInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new RequestContactInfo(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.RequestContactInfo getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/RequestContactInfoOrBuilder.java b/Server/src/main/proto/management/RequestContactInfoOrBuilder.java new file mode 100644 index 000000000..d1e44b868 --- /dev/null +++ b/Server/src/main/proto/management/RequestContactInfoOrBuilder.java @@ -0,0 +1,37 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface RequestContactInfoOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.RequestContactInfo) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + boolean hasWorld(); + /** + * required int32 world = 2; + * @return The world. + */ + int getWorld(); +} diff --git a/Server/src/main/proto/management/SendClanInfo.java b/Server/src/main/proto/management/SendClanInfo.java new file mode 100644 index 000000000..8579ed488 --- /dev/null +++ b/Server/src/main/proto/management/SendClanInfo.java @@ -0,0 +1,2394 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.SendClanInfo} + */ +public final class SendClanInfo extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.SendClanInfo) + SendClanInfoOrBuilder { +private static final long serialVersionUID = 0L; + // Use SendClanInfo.newBuilder() to construct. + private SendClanInfo(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private SendClanInfo() { + clanOwner_ = ""; + clanName_ = ""; + members_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new SendClanInfo(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SendClanInfo( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + clanOwner_ = bs; + break; + } + case 16: { + bitField0_ |= 0x00000002; + hasInfo_ = input.readBool(); + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + clanName_ = bs; + break; + } + case 32: { + bitField0_ |= 0x00000008; + joinRequirement_ = input.readInt32(); + break; + } + case 40: { + bitField0_ |= 0x00000010; + kickRequirement_ = input.readInt32(); + break; + } + case 48: { + bitField0_ |= 0x00000020; + messageRequirement_ = input.readInt32(); + break; + } + case 56: { + bitField0_ |= 0x00000040; + lootRequirement_ = input.readInt32(); + break; + } + case 66: { + if (!((mutable_bitField0_ & 0x00000080) != 0)) { + members_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000080; + } + members_.add( + input.readMessage(proto.management.SendClanInfo.ClanMember.PARSER, extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000080) != 0)) { + members_ = java.util.Collections.unmodifiableList(members_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.SendClanInfo.class, proto.management.SendClanInfo.Builder.class); + } + + public interface ClanMemberOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.SendClanInfo.ClanMember) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + boolean hasWorld(); + /** + * required int32 world = 2; + * @return The world. + */ + int getWorld(); + + /** + * required int32 rank = 3; + * @return Whether the rank field is set. + */ + boolean hasRank(); + /** + * required int32 rank = 3; + * @return The rank. + */ + int getRank(); + } + /** + * Protobuf type {@code management.SendClanInfo.ClanMember} + */ + public static final class ClanMember extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.SendClanInfo.ClanMember) + ClanMemberOrBuilder { + private static final long serialVersionUID = 0L; + // Use ClanMember.newBuilder() to construct. + private ClanMember(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private ClanMember() { + username_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ClanMember(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ClanMember( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 16: { + bitField0_ |= 0x00000002; + world_ = input.readInt32(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + rank_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_ClanMember_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_ClanMember_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.SendClanInfo.ClanMember.class, proto.management.SendClanInfo.ClanMember.Builder.class); + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int WORLD_FIELD_NUMBER = 2; + private int world_; + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required int32 world = 2; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + + public static final int RANK_FIELD_NUMBER = 3; + private int rank_; + /** + * required int32 rank = 3; + * @return Whether the rank field is set. + */ + @java.lang.Override + public boolean hasRank() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required int32 rank = 3; + * @return The rank. + */ + @java.lang.Override + public int getRank() { + return rank_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasWorld()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasRank()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeInt32(2, world_); + } + if (((bitField0_ & 0x00000004) != 0)) { + output.writeInt32(3, rank_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, world_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, rank_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.SendClanInfo.ClanMember)) { + return super.equals(obj); + } + proto.management.SendClanInfo.ClanMember other = (proto.management.SendClanInfo.ClanMember) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasWorld() != other.hasWorld()) return false; + if (hasWorld()) { + if (getWorld() + != other.getWorld()) return false; + } + if (hasRank() != other.hasRank()) return false; + if (hasRank()) { + if (getRank() + != other.getRank()) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasWorld()) { + hash = (37 * hash) + WORLD_FIELD_NUMBER; + hash = (53 * hash) + getWorld(); + } + if (hasRank()) { + hash = (37 * hash) + RANK_FIELD_NUMBER; + hash = (53 * hash) + getRank(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.SendClanInfo.ClanMember parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendClanInfo.ClanMember parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendClanInfo.ClanMember parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendClanInfo.ClanMember parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendClanInfo.ClanMember parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendClanInfo.ClanMember parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendClanInfo.ClanMember parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.SendClanInfo.ClanMember parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.SendClanInfo.ClanMember parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.SendClanInfo.ClanMember parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.SendClanInfo.ClanMember parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.SendClanInfo.ClanMember parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.SendClanInfo.ClanMember prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.SendClanInfo.ClanMember} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.SendClanInfo.ClanMember) + proto.management.SendClanInfo.ClanMemberOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_ClanMember_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_ClanMember_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.SendClanInfo.ClanMember.class, proto.management.SendClanInfo.ClanMember.Builder.class); + } + + // Construct using proto.management.SendClanInfo.ClanMember.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + world_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + rank_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_ClanMember_descriptor; + } + + @java.lang.Override + public proto.management.SendClanInfo.ClanMember getDefaultInstanceForType() { + return proto.management.SendClanInfo.ClanMember.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.SendClanInfo.ClanMember build() { + proto.management.SendClanInfo.ClanMember result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.SendClanInfo.ClanMember buildPartial() { + proto.management.SendClanInfo.ClanMember result = new proto.management.SendClanInfo.ClanMember(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.world_ = world_; + to_bitField0_ |= 0x00000002; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.rank_ = rank_; + to_bitField0_ |= 0x00000004; + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.SendClanInfo.ClanMember) { + return mergeFrom((proto.management.SendClanInfo.ClanMember)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.SendClanInfo.ClanMember other) { + if (other == proto.management.SendClanInfo.ClanMember.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (other.hasWorld()) { + setWorld(other.getWorld()); + } + if (other.hasRank()) { + setRank(other.getRank()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + if (!hasWorld()) { + return false; + } + if (!hasRank()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.SendClanInfo.ClanMember parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.SendClanInfo.ClanMember) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private int world_ ; + /** + * required int32 world = 2; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required int32 world = 2; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + /** + * required int32 world = 2; + * @param value The world to set. + * @return This builder for chaining. + */ + public Builder setWorld(int value) { + bitField0_ |= 0x00000002; + world_ = value; + onChanged(); + return this; + } + /** + * required int32 world = 2; + * @return This builder for chaining. + */ + public Builder clearWorld() { + bitField0_ = (bitField0_ & ~0x00000002); + world_ = 0; + onChanged(); + return this; + } + + private int rank_ ; + /** + * required int32 rank = 3; + * @return Whether the rank field is set. + */ + @java.lang.Override + public boolean hasRank() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * required int32 rank = 3; + * @return The rank. + */ + @java.lang.Override + public int getRank() { + return rank_; + } + /** + * required int32 rank = 3; + * @param value The rank to set. + * @return This builder for chaining. + */ + public Builder setRank(int value) { + bitField0_ |= 0x00000004; + rank_ = value; + onChanged(); + return this; + } + /** + * required int32 rank = 3; + * @return This builder for chaining. + */ + public Builder clearRank() { + bitField0_ = (bitField0_ & ~0x00000004); + rank_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.SendClanInfo.ClanMember) + } + + // @@protoc_insertion_point(class_scope:management.SendClanInfo.ClanMember) + private static final proto.management.SendClanInfo.ClanMember DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.SendClanInfo.ClanMember(); + } + + public static proto.management.SendClanInfo.ClanMember getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ClanMember parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ClanMember(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.SendClanInfo.ClanMember getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private int bitField0_; + public static final int CLANOWNER_FIELD_NUMBER = 1; + private volatile java.lang.Object clanOwner_; + /** + * required string clanOwner = 1; + * @return Whether the clanOwner field is set. + */ + @java.lang.Override + public boolean hasClanOwner() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string clanOwner = 1; + * @return The clanOwner. + */ + @java.lang.Override + public java.lang.String getClanOwner() { + java.lang.Object ref = clanOwner_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanOwner_ = s; + } + return s; + } + } + /** + * required string clanOwner = 1; + * @return The bytes for clanOwner. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClanOwnerBytes() { + java.lang.Object ref = clanOwner_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanOwner_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int HASINFO_FIELD_NUMBER = 2; + private boolean hasInfo_; + /** + * required bool hasInfo = 2; + * @return Whether the hasInfo field is set. + */ + @java.lang.Override + public boolean hasHasInfo() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required bool hasInfo = 2; + * @return The hasInfo. + */ + @java.lang.Override + public boolean getHasInfo() { + return hasInfo_; + } + + public static final int CLANNAME_FIELD_NUMBER = 3; + private volatile java.lang.Object clanName_; + /** + * optional string clanName = 3; + * @return Whether the clanName field is set. + */ + @java.lang.Override + public boolean hasClanName() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional string clanName = 3; + * @return The clanName. + */ + @java.lang.Override + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } + } + /** + * optional string clanName = 3; + * @return The bytes for clanName. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int JOINREQUIREMENT_FIELD_NUMBER = 4; + private int joinRequirement_; + /** + * optional int32 joinRequirement = 4; + * @return Whether the joinRequirement field is set. + */ + @java.lang.Override + public boolean hasJoinRequirement() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * optional int32 joinRequirement = 4; + * @return The joinRequirement. + */ + @java.lang.Override + public int getJoinRequirement() { + return joinRequirement_; + } + + public static final int KICKREQUIREMENT_FIELD_NUMBER = 5; + private int kickRequirement_; + /** + * optional int32 kickRequirement = 5; + * @return Whether the kickRequirement field is set. + */ + @java.lang.Override + public boolean hasKickRequirement() { + return ((bitField0_ & 0x00000010) != 0); + } + /** + * optional int32 kickRequirement = 5; + * @return The kickRequirement. + */ + @java.lang.Override + public int getKickRequirement() { + return kickRequirement_; + } + + public static final int MESSAGEREQUIREMENT_FIELD_NUMBER = 6; + private int messageRequirement_; + /** + * optional int32 messageRequirement = 6; + * @return Whether the messageRequirement field is set. + */ + @java.lang.Override + public boolean hasMessageRequirement() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + * optional int32 messageRequirement = 6; + * @return The messageRequirement. + */ + @java.lang.Override + public int getMessageRequirement() { + return messageRequirement_; + } + + public static final int LOOTREQUIREMENT_FIELD_NUMBER = 7; + private int lootRequirement_; + /** + * optional int32 lootRequirement = 7; + * @return Whether the lootRequirement field is set. + */ + @java.lang.Override + public boolean hasLootRequirement() { + return ((bitField0_ & 0x00000040) != 0); + } + /** + * optional int32 lootRequirement = 7; + * @return The lootRequirement. + */ + @java.lang.Override + public int getLootRequirement() { + return lootRequirement_; + } + + public static final int MEMBERS_FIELD_NUMBER = 8; + private java.util.List members_; + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + @java.lang.Override + public java.util.List getMembersList() { + return members_; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + @java.lang.Override + public java.util.List + getMembersOrBuilderList() { + return members_; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + @java.lang.Override + public int getMembersCount() { + return members_.size(); + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + @java.lang.Override + public proto.management.SendClanInfo.ClanMember getMembers(int index) { + return members_.get(index); + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + @java.lang.Override + public proto.management.SendClanInfo.ClanMemberOrBuilder getMembersOrBuilder( + int index) { + return members_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasClanOwner()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasHasInfo()) { + memoizedIsInitialized = 0; + return false; + } + for (int i = 0; i < getMembersCount(); i++) { + if (!getMembers(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, clanOwner_); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeBool(2, hasInfo_); + } + if (((bitField0_ & 0x00000004) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, clanName_); + } + if (((bitField0_ & 0x00000008) != 0)) { + output.writeInt32(4, joinRequirement_); + } + if (((bitField0_ & 0x00000010) != 0)) { + output.writeInt32(5, kickRequirement_); + } + if (((bitField0_ & 0x00000020) != 0)) { + output.writeInt32(6, messageRequirement_); + } + if (((bitField0_ & 0x00000040) != 0)) { + output.writeInt32(7, lootRequirement_); + } + for (int i = 0; i < members_.size(); i++) { + output.writeMessage(8, members_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, clanOwner_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(2, hasInfo_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, clanName_); + } + if (((bitField0_ & 0x00000008) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(4, joinRequirement_); + } + if (((bitField0_ & 0x00000010) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(5, kickRequirement_); + } + if (((bitField0_ & 0x00000020) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(6, messageRequirement_); + } + if (((bitField0_ & 0x00000040) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(7, lootRequirement_); + } + for (int i = 0; i < members_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, members_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.SendClanInfo)) { + return super.equals(obj); + } + proto.management.SendClanInfo other = (proto.management.SendClanInfo) obj; + + if (hasClanOwner() != other.hasClanOwner()) return false; + if (hasClanOwner()) { + if (!getClanOwner() + .equals(other.getClanOwner())) return false; + } + if (hasHasInfo() != other.hasHasInfo()) return false; + if (hasHasInfo()) { + if (getHasInfo() + != other.getHasInfo()) return false; + } + if (hasClanName() != other.hasClanName()) return false; + if (hasClanName()) { + if (!getClanName() + .equals(other.getClanName())) return false; + } + if (hasJoinRequirement() != other.hasJoinRequirement()) return false; + if (hasJoinRequirement()) { + if (getJoinRequirement() + != other.getJoinRequirement()) return false; + } + if (hasKickRequirement() != other.hasKickRequirement()) return false; + if (hasKickRequirement()) { + if (getKickRequirement() + != other.getKickRequirement()) return false; + } + if (hasMessageRequirement() != other.hasMessageRequirement()) return false; + if (hasMessageRequirement()) { + if (getMessageRequirement() + != other.getMessageRequirement()) return false; + } + if (hasLootRequirement() != other.hasLootRequirement()) return false; + if (hasLootRequirement()) { + if (getLootRequirement() + != other.getLootRequirement()) return false; + } + if (!getMembersList() + .equals(other.getMembersList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasClanOwner()) { + hash = (37 * hash) + CLANOWNER_FIELD_NUMBER; + hash = (53 * hash) + getClanOwner().hashCode(); + } + if (hasHasInfo()) { + hash = (37 * hash) + HASINFO_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getHasInfo()); + } + if (hasClanName()) { + hash = (37 * hash) + CLANNAME_FIELD_NUMBER; + hash = (53 * hash) + getClanName().hashCode(); + } + if (hasJoinRequirement()) { + hash = (37 * hash) + JOINREQUIREMENT_FIELD_NUMBER; + hash = (53 * hash) + getJoinRequirement(); + } + if (hasKickRequirement()) { + hash = (37 * hash) + KICKREQUIREMENT_FIELD_NUMBER; + hash = (53 * hash) + getKickRequirement(); + } + if (hasMessageRequirement()) { + hash = (37 * hash) + MESSAGEREQUIREMENT_FIELD_NUMBER; + hash = (53 * hash) + getMessageRequirement(); + } + if (hasLootRequirement()) { + hash = (37 * hash) + LOOTREQUIREMENT_FIELD_NUMBER; + hash = (53 * hash) + getLootRequirement(); + } + if (getMembersCount() > 0) { + hash = (37 * hash) + MEMBERS_FIELD_NUMBER; + hash = (53 * hash) + getMembersList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.SendClanInfo parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendClanInfo parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendClanInfo parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendClanInfo parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendClanInfo parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendClanInfo parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendClanInfo parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.SendClanInfo parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.SendClanInfo parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.SendClanInfo parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.SendClanInfo parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.SendClanInfo parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.SendClanInfo prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.SendClanInfo} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.SendClanInfo) + proto.management.SendClanInfoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.SendClanInfo.class, proto.management.SendClanInfo.Builder.class); + } + + // Construct using proto.management.SendClanInfo.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getMembersFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + clanOwner_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + hasInfo_ = false; + bitField0_ = (bitField0_ & ~0x00000002); + clanName_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + joinRequirement_ = 0; + bitField0_ = (bitField0_ & ~0x00000008); + kickRequirement_ = 0; + bitField0_ = (bitField0_ & ~0x00000010); + messageRequirement_ = 0; + bitField0_ = (bitField0_ & ~0x00000020); + lootRequirement_ = 0; + bitField0_ = (bitField0_ & ~0x00000040); + if (membersBuilder_ == null) { + members_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000080); + } else { + membersBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_SendClanInfo_descriptor; + } + + @java.lang.Override + public proto.management.SendClanInfo getDefaultInstanceForType() { + return proto.management.SendClanInfo.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.SendClanInfo build() { + proto.management.SendClanInfo result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.SendClanInfo buildPartial() { + proto.management.SendClanInfo result = new proto.management.SendClanInfo(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.clanOwner_ = clanOwner_; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.hasInfo_ = hasInfo_; + to_bitField0_ |= 0x00000002; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + to_bitField0_ |= 0x00000004; + } + result.clanName_ = clanName_; + if (((from_bitField0_ & 0x00000008) != 0)) { + result.joinRequirement_ = joinRequirement_; + to_bitField0_ |= 0x00000008; + } + if (((from_bitField0_ & 0x00000010) != 0)) { + result.kickRequirement_ = kickRequirement_; + to_bitField0_ |= 0x00000010; + } + if (((from_bitField0_ & 0x00000020) != 0)) { + result.messageRequirement_ = messageRequirement_; + to_bitField0_ |= 0x00000020; + } + if (((from_bitField0_ & 0x00000040) != 0)) { + result.lootRequirement_ = lootRequirement_; + to_bitField0_ |= 0x00000040; + } + if (membersBuilder_ == null) { + if (((bitField0_ & 0x00000080) != 0)) { + members_ = java.util.Collections.unmodifiableList(members_); + bitField0_ = (bitField0_ & ~0x00000080); + } + result.members_ = members_; + } else { + result.members_ = membersBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.SendClanInfo) { + return mergeFrom((proto.management.SendClanInfo)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.SendClanInfo other) { + if (other == proto.management.SendClanInfo.getDefaultInstance()) return this; + if (other.hasClanOwner()) { + bitField0_ |= 0x00000001; + clanOwner_ = other.clanOwner_; + onChanged(); + } + if (other.hasHasInfo()) { + setHasInfo(other.getHasInfo()); + } + if (other.hasClanName()) { + bitField0_ |= 0x00000004; + clanName_ = other.clanName_; + onChanged(); + } + if (other.hasJoinRequirement()) { + setJoinRequirement(other.getJoinRequirement()); + } + if (other.hasKickRequirement()) { + setKickRequirement(other.getKickRequirement()); + } + if (other.hasMessageRequirement()) { + setMessageRequirement(other.getMessageRequirement()); + } + if (other.hasLootRequirement()) { + setLootRequirement(other.getLootRequirement()); + } + if (membersBuilder_ == null) { + if (!other.members_.isEmpty()) { + if (members_.isEmpty()) { + members_ = other.members_; + bitField0_ = (bitField0_ & ~0x00000080); + } else { + ensureMembersIsMutable(); + members_.addAll(other.members_); + } + onChanged(); + } + } else { + if (!other.members_.isEmpty()) { + if (membersBuilder_.isEmpty()) { + membersBuilder_.dispose(); + membersBuilder_ = null; + members_ = other.members_; + bitField0_ = (bitField0_ & ~0x00000080); + membersBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getMembersFieldBuilder() : null; + } else { + membersBuilder_.addAllMessages(other.members_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasClanOwner()) { + return false; + } + if (!hasHasInfo()) { + return false; + } + for (int i = 0; i < getMembersCount(); i++) { + if (!getMembers(i).isInitialized()) { + return false; + } + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.SendClanInfo parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.SendClanInfo) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object clanOwner_ = ""; + /** + * required string clanOwner = 1; + * @return Whether the clanOwner field is set. + */ + public boolean hasClanOwner() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string clanOwner = 1; + * @return The clanOwner. + */ + public java.lang.String getClanOwner() { + java.lang.Object ref = clanOwner_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanOwner_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string clanOwner = 1; + * @return The bytes for clanOwner. + */ + public com.google.protobuf.ByteString + getClanOwnerBytes() { + java.lang.Object ref = clanOwner_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanOwner_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string clanOwner = 1; + * @param value The clanOwner to set. + * @return This builder for chaining. + */ + public Builder setClanOwner( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + clanOwner_ = value; + onChanged(); + return this; + } + /** + * required string clanOwner = 1; + * @return This builder for chaining. + */ + public Builder clearClanOwner() { + bitField0_ = (bitField0_ & ~0x00000001); + clanOwner_ = getDefaultInstance().getClanOwner(); + onChanged(); + return this; + } + /** + * required string clanOwner = 1; + * @param value The bytes for clanOwner to set. + * @return This builder for chaining. + */ + public Builder setClanOwnerBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + clanOwner_ = value; + onChanged(); + return this; + } + + private boolean hasInfo_ ; + /** + * required bool hasInfo = 2; + * @return Whether the hasInfo field is set. + */ + @java.lang.Override + public boolean hasHasInfo() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * required bool hasInfo = 2; + * @return The hasInfo. + */ + @java.lang.Override + public boolean getHasInfo() { + return hasInfo_; + } + /** + * required bool hasInfo = 2; + * @param value The hasInfo to set. + * @return This builder for chaining. + */ + public Builder setHasInfo(boolean value) { + bitField0_ |= 0x00000002; + hasInfo_ = value; + onChanged(); + return this; + } + /** + * required bool hasInfo = 2; + * @return This builder for chaining. + */ + public Builder clearHasInfo() { + bitField0_ = (bitField0_ & ~0x00000002); + hasInfo_ = false; + onChanged(); + return this; + } + + private java.lang.Object clanName_ = ""; + /** + * optional string clanName = 3; + * @return Whether the clanName field is set. + */ + public boolean hasClanName() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional string clanName = 3; + * @return The clanName. + */ + public java.lang.String getClanName() { + java.lang.Object ref = clanName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + clanName_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string clanName = 3; + * @return The bytes for clanName. + */ + public com.google.protobuf.ByteString + getClanNameBytes() { + java.lang.Object ref = clanName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clanName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string clanName = 3; + * @param value The clanName to set. + * @return This builder for chaining. + */ + public Builder setClanName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + clanName_ = value; + onChanged(); + return this; + } + /** + * optional string clanName = 3; + * @return This builder for chaining. + */ + public Builder clearClanName() { + bitField0_ = (bitField0_ & ~0x00000004); + clanName_ = getDefaultInstance().getClanName(); + onChanged(); + return this; + } + /** + * optional string clanName = 3; + * @param value The bytes for clanName to set. + * @return This builder for chaining. + */ + public Builder setClanNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + clanName_ = value; + onChanged(); + return this; + } + + private int joinRequirement_ ; + /** + * optional int32 joinRequirement = 4; + * @return Whether the joinRequirement field is set. + */ + @java.lang.Override + public boolean hasJoinRequirement() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * optional int32 joinRequirement = 4; + * @return The joinRequirement. + */ + @java.lang.Override + public int getJoinRequirement() { + return joinRequirement_; + } + /** + * optional int32 joinRequirement = 4; + * @param value The joinRequirement to set. + * @return This builder for chaining. + */ + public Builder setJoinRequirement(int value) { + bitField0_ |= 0x00000008; + joinRequirement_ = value; + onChanged(); + return this; + } + /** + * optional int32 joinRequirement = 4; + * @return This builder for chaining. + */ + public Builder clearJoinRequirement() { + bitField0_ = (bitField0_ & ~0x00000008); + joinRequirement_ = 0; + onChanged(); + return this; + } + + private int kickRequirement_ ; + /** + * optional int32 kickRequirement = 5; + * @return Whether the kickRequirement field is set. + */ + @java.lang.Override + public boolean hasKickRequirement() { + return ((bitField0_ & 0x00000010) != 0); + } + /** + * optional int32 kickRequirement = 5; + * @return The kickRequirement. + */ + @java.lang.Override + public int getKickRequirement() { + return kickRequirement_; + } + /** + * optional int32 kickRequirement = 5; + * @param value The kickRequirement to set. + * @return This builder for chaining. + */ + public Builder setKickRequirement(int value) { + bitField0_ |= 0x00000010; + kickRequirement_ = value; + onChanged(); + return this; + } + /** + * optional int32 kickRequirement = 5; + * @return This builder for chaining. + */ + public Builder clearKickRequirement() { + bitField0_ = (bitField0_ & ~0x00000010); + kickRequirement_ = 0; + onChanged(); + return this; + } + + private int messageRequirement_ ; + /** + * optional int32 messageRequirement = 6; + * @return Whether the messageRequirement field is set. + */ + @java.lang.Override + public boolean hasMessageRequirement() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + * optional int32 messageRequirement = 6; + * @return The messageRequirement. + */ + @java.lang.Override + public int getMessageRequirement() { + return messageRequirement_; + } + /** + * optional int32 messageRequirement = 6; + * @param value The messageRequirement to set. + * @return This builder for chaining. + */ + public Builder setMessageRequirement(int value) { + bitField0_ |= 0x00000020; + messageRequirement_ = value; + onChanged(); + return this; + } + /** + * optional int32 messageRequirement = 6; + * @return This builder for chaining. + */ + public Builder clearMessageRequirement() { + bitField0_ = (bitField0_ & ~0x00000020); + messageRequirement_ = 0; + onChanged(); + return this; + } + + private int lootRequirement_ ; + /** + * optional int32 lootRequirement = 7; + * @return Whether the lootRequirement field is set. + */ + @java.lang.Override + public boolean hasLootRequirement() { + return ((bitField0_ & 0x00000040) != 0); + } + /** + * optional int32 lootRequirement = 7; + * @return The lootRequirement. + */ + @java.lang.Override + public int getLootRequirement() { + return lootRequirement_; + } + /** + * optional int32 lootRequirement = 7; + * @param value The lootRequirement to set. + * @return This builder for chaining. + */ + public Builder setLootRequirement(int value) { + bitField0_ |= 0x00000040; + lootRequirement_ = value; + onChanged(); + return this; + } + /** + * optional int32 lootRequirement = 7; + * @return This builder for chaining. + */ + public Builder clearLootRequirement() { + bitField0_ = (bitField0_ & ~0x00000040); + lootRequirement_ = 0; + onChanged(); + return this; + } + + private java.util.List members_ = + java.util.Collections.emptyList(); + private void ensureMembersIsMutable() { + if (!((bitField0_ & 0x00000080) != 0)) { + members_ = new java.util.ArrayList(members_); + bitField0_ |= 0x00000080; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + proto.management.SendClanInfo.ClanMember, proto.management.SendClanInfo.ClanMember.Builder, proto.management.SendClanInfo.ClanMemberOrBuilder> membersBuilder_; + + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public java.util.List getMembersList() { + if (membersBuilder_ == null) { + return java.util.Collections.unmodifiableList(members_); + } else { + return membersBuilder_.getMessageList(); + } + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public int getMembersCount() { + if (membersBuilder_ == null) { + return members_.size(); + } else { + return membersBuilder_.getCount(); + } + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public proto.management.SendClanInfo.ClanMember getMembers(int index) { + if (membersBuilder_ == null) { + return members_.get(index); + } else { + return membersBuilder_.getMessage(index); + } + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder setMembers( + int index, proto.management.SendClanInfo.ClanMember value) { + if (membersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.set(index, value); + onChanged(); + } else { + membersBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder setMembers( + int index, proto.management.SendClanInfo.ClanMember.Builder builderForValue) { + if (membersBuilder_ == null) { + ensureMembersIsMutable(); + members_.set(index, builderForValue.build()); + onChanged(); + } else { + membersBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder addMembers(proto.management.SendClanInfo.ClanMember value) { + if (membersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(value); + onChanged(); + } else { + membersBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder addMembers( + int index, proto.management.SendClanInfo.ClanMember value) { + if (membersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(index, value); + onChanged(); + } else { + membersBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder addMembers( + proto.management.SendClanInfo.ClanMember.Builder builderForValue) { + if (membersBuilder_ == null) { + ensureMembersIsMutable(); + members_.add(builderForValue.build()); + onChanged(); + } else { + membersBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder addMembers( + int index, proto.management.SendClanInfo.ClanMember.Builder builderForValue) { + if (membersBuilder_ == null) { + ensureMembersIsMutable(); + members_.add(index, builderForValue.build()); + onChanged(); + } else { + membersBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder addAllMembers( + java.lang.Iterable values) { + if (membersBuilder_ == null) { + ensureMembersIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, members_); + onChanged(); + } else { + membersBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder clearMembers() { + if (membersBuilder_ == null) { + members_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000080); + onChanged(); + } else { + membersBuilder_.clear(); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public Builder removeMembers(int index) { + if (membersBuilder_ == null) { + ensureMembersIsMutable(); + members_.remove(index); + onChanged(); + } else { + membersBuilder_.remove(index); + } + return this; + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public proto.management.SendClanInfo.ClanMember.Builder getMembersBuilder( + int index) { + return getMembersFieldBuilder().getBuilder(index); + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public proto.management.SendClanInfo.ClanMemberOrBuilder getMembersOrBuilder( + int index) { + if (membersBuilder_ == null) { + return members_.get(index); } else { + return membersBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public java.util.List + getMembersOrBuilderList() { + if (membersBuilder_ != null) { + return membersBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(members_); + } + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public proto.management.SendClanInfo.ClanMember.Builder addMembersBuilder() { + return getMembersFieldBuilder().addBuilder( + proto.management.SendClanInfo.ClanMember.getDefaultInstance()); + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public proto.management.SendClanInfo.ClanMember.Builder addMembersBuilder( + int index) { + return getMembersFieldBuilder().addBuilder( + index, proto.management.SendClanInfo.ClanMember.getDefaultInstance()); + } + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + public java.util.List + getMembersBuilderList() { + return getMembersFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + proto.management.SendClanInfo.ClanMember, proto.management.SendClanInfo.ClanMember.Builder, proto.management.SendClanInfo.ClanMemberOrBuilder> + getMembersFieldBuilder() { + if (membersBuilder_ == null) { + membersBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + proto.management.SendClanInfo.ClanMember, proto.management.SendClanInfo.ClanMember.Builder, proto.management.SendClanInfo.ClanMemberOrBuilder>( + members_, + ((bitField0_ & 0x00000080) != 0), + getParentForChildren(), + isClean()); + members_ = null; + } + return membersBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.SendClanInfo) + } + + // @@protoc_insertion_point(class_scope:management.SendClanInfo) + private static final proto.management.SendClanInfo DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.SendClanInfo(); + } + + public static proto.management.SendClanInfo getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SendClanInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SendClanInfo(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.SendClanInfo getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/SendClanInfoOrBuilder.java b/Server/src/main/proto/management/SendClanInfoOrBuilder.java new file mode 100644 index 000000000..d313ad7c8 --- /dev/null +++ b/Server/src/main/proto/management/SendClanInfoOrBuilder.java @@ -0,0 +1,122 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface SendClanInfoOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.SendClanInfo) + com.google.protobuf.MessageOrBuilder { + + /** + * required string clanOwner = 1; + * @return Whether the clanOwner field is set. + */ + boolean hasClanOwner(); + /** + * required string clanOwner = 1; + * @return The clanOwner. + */ + java.lang.String getClanOwner(); + /** + * required string clanOwner = 1; + * @return The bytes for clanOwner. + */ + com.google.protobuf.ByteString + getClanOwnerBytes(); + + /** + * required bool hasInfo = 2; + * @return Whether the hasInfo field is set. + */ + boolean hasHasInfo(); + /** + * required bool hasInfo = 2; + * @return The hasInfo. + */ + boolean getHasInfo(); + + /** + * optional string clanName = 3; + * @return Whether the clanName field is set. + */ + boolean hasClanName(); + /** + * optional string clanName = 3; + * @return The clanName. + */ + java.lang.String getClanName(); + /** + * optional string clanName = 3; + * @return The bytes for clanName. + */ + com.google.protobuf.ByteString + getClanNameBytes(); + + /** + * optional int32 joinRequirement = 4; + * @return Whether the joinRequirement field is set. + */ + boolean hasJoinRequirement(); + /** + * optional int32 joinRequirement = 4; + * @return The joinRequirement. + */ + int getJoinRequirement(); + + /** + * optional int32 kickRequirement = 5; + * @return Whether the kickRequirement field is set. + */ + boolean hasKickRequirement(); + /** + * optional int32 kickRequirement = 5; + * @return The kickRequirement. + */ + int getKickRequirement(); + + /** + * optional int32 messageRequirement = 6; + * @return Whether the messageRequirement field is set. + */ + boolean hasMessageRequirement(); + /** + * optional int32 messageRequirement = 6; + * @return The messageRequirement. + */ + int getMessageRequirement(); + + /** + * optional int32 lootRequirement = 7; + * @return Whether the lootRequirement field is set. + */ + boolean hasLootRequirement(); + /** + * optional int32 lootRequirement = 7; + * @return The lootRequirement. + */ + int getLootRequirement(); + + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + java.util.List + getMembersList(); + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + proto.management.SendClanInfo.ClanMember getMembers(int index); + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + int getMembersCount(); + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + java.util.List + getMembersOrBuilderList(); + /** + * repeated .management.SendClanInfo.ClanMember members = 8; + */ + proto.management.SendClanInfo.ClanMemberOrBuilder getMembersOrBuilder( + int index); +} diff --git a/Server/src/main/proto/management/SendContactInfo.java b/Server/src/main/proto/management/SendContactInfo.java new file mode 100644 index 000000000..f2698f4fd --- /dev/null +++ b/Server/src/main/proto/management/SendContactInfo.java @@ -0,0 +1,1958 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +/** + * Protobuf type {@code management.SendContactInfo} + */ +public final class SendContactInfo extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.SendContactInfo) + SendContactInfoOrBuilder { +private static final long serialVersionUID = 0L; + // Use SendContactInfo.newBuilder() to construct. + private SendContactInfo(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private SendContactInfo() { + username_ = ""; + contacts_ = java.util.Collections.emptyList(); + blocked_ = com.google.protobuf.LazyStringArrayList.EMPTY; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new SendContactInfo(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SendContactInfo( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000002) != 0)) { + contacts_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + contacts_.add( + input.readMessage(proto.management.SendContactInfo.Contact.PARSER, extensionRegistry)); + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + if (!((mutable_bitField0_ & 0x00000004) != 0)) { + blocked_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000004; + } + blocked_.add(bs); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000002) != 0)) { + contacts_ = java.util.Collections.unmodifiableList(contacts_); + } + if (((mutable_bitField0_ & 0x00000004) != 0)) { + blocked_ = blocked_.getUnmodifiableView(); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.SendContactInfo.class, proto.management.SendContactInfo.Builder.class); + } + + public interface ContactOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.SendContactInfo.Contact) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * optional int32 world = 2; + * @return Whether the world field is set. + */ + boolean hasWorld(); + /** + * optional int32 world = 2; + * @return The world. + */ + int getWorld(); + + /** + * optional int32 rank = 3; + * @return Whether the rank field is set. + */ + boolean hasRank(); + /** + * optional int32 rank = 3; + * @return The rank. + */ + int getRank(); + } + /** + * Protobuf type {@code management.SendContactInfo.Contact} + */ + public static final class Contact extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:management.SendContactInfo.Contact) + ContactOrBuilder { + private static final long serialVersionUID = 0L; + // Use Contact.newBuilder() to construct. + private Contact(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private Contact() { + username_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new Contact(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Contact( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + username_ = bs; + break; + } + case 16: { + bitField0_ |= 0x00000002; + world_ = input.readInt32(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + rank_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_Contact_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_Contact_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.SendContactInfo.Contact.class, proto.management.SendContactInfo.Contact.Builder.class); + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int WORLD_FIELD_NUMBER = 2; + private int world_; + /** + * optional int32 world = 2; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * optional int32 world = 2; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + + public static final int RANK_FIELD_NUMBER = 3; + private int rank_; + /** + * optional int32 rank = 3; + * @return Whether the rank field is set. + */ + @java.lang.Override + public boolean hasRank() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional int32 rank = 3; + * @return The rank. + */ + @java.lang.Override + public int getRank() { + return rank_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeInt32(2, world_); + } + if (((bitField0_ & 0x00000004) != 0)) { + output.writeInt32(3, rank_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, world_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, rank_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.SendContactInfo.Contact)) { + return super.equals(obj); + } + proto.management.SendContactInfo.Contact other = (proto.management.SendContactInfo.Contact) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (hasWorld() != other.hasWorld()) return false; + if (hasWorld()) { + if (getWorld() + != other.getWorld()) return false; + } + if (hasRank() != other.hasRank()) return false; + if (hasRank()) { + if (getRank() + != other.getRank()) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (hasWorld()) { + hash = (37 * hash) + WORLD_FIELD_NUMBER; + hash = (53 * hash) + getWorld(); + } + if (hasRank()) { + hash = (37 * hash) + RANK_FIELD_NUMBER; + hash = (53 * hash) + getRank(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.SendContactInfo.Contact parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendContactInfo.Contact parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendContactInfo.Contact parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendContactInfo.Contact parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendContactInfo.Contact parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendContactInfo.Contact parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendContactInfo.Contact parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.SendContactInfo.Contact parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.SendContactInfo.Contact parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.SendContactInfo.Contact parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.SendContactInfo.Contact parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.SendContactInfo.Contact parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.SendContactInfo.Contact prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.SendContactInfo.Contact} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.SendContactInfo.Contact) + proto.management.SendContactInfo.ContactOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_Contact_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_Contact_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.SendContactInfo.Contact.class, proto.management.SendContactInfo.Contact.Builder.class); + } + + // Construct using proto.management.SendContactInfo.Contact.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + world_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + rank_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_Contact_descriptor; + } + + @java.lang.Override + public proto.management.SendContactInfo.Contact getDefaultInstanceForType() { + return proto.management.SendContactInfo.Contact.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.SendContactInfo.Contact build() { + proto.management.SendContactInfo.Contact result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.SendContactInfo.Contact buildPartial() { + proto.management.SendContactInfo.Contact result = new proto.management.SendContactInfo.Contact(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.world_ = world_; + to_bitField0_ |= 0x00000002; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.rank_ = rank_; + to_bitField0_ |= 0x00000004; + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.SendContactInfo.Contact) { + return mergeFrom((proto.management.SendContactInfo.Contact)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.SendContactInfo.Contact other) { + if (other == proto.management.SendContactInfo.Contact.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (other.hasWorld()) { + setWorld(other.getWorld()); + } + if (other.hasRank()) { + setRank(other.getRank()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.SendContactInfo.Contact parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.SendContactInfo.Contact) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private int world_ ; + /** + * optional int32 world = 2; + * @return Whether the world field is set. + */ + @java.lang.Override + public boolean hasWorld() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * optional int32 world = 2; + * @return The world. + */ + @java.lang.Override + public int getWorld() { + return world_; + } + /** + * optional int32 world = 2; + * @param value The world to set. + * @return This builder for chaining. + */ + public Builder setWorld(int value) { + bitField0_ |= 0x00000002; + world_ = value; + onChanged(); + return this; + } + /** + * optional int32 world = 2; + * @return This builder for chaining. + */ + public Builder clearWorld() { + bitField0_ = (bitField0_ & ~0x00000002); + world_ = 0; + onChanged(); + return this; + } + + private int rank_ ; + /** + * optional int32 rank = 3; + * @return Whether the rank field is set. + */ + @java.lang.Override + public boolean hasRank() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional int32 rank = 3; + * @return The rank. + */ + @java.lang.Override + public int getRank() { + return rank_; + } + /** + * optional int32 rank = 3; + * @param value The rank to set. + * @return This builder for chaining. + */ + public Builder setRank(int value) { + bitField0_ |= 0x00000004; + rank_ = value; + onChanged(); + return this; + } + /** + * optional int32 rank = 3; + * @return This builder for chaining. + */ + public Builder clearRank() { + bitField0_ = (bitField0_ & ~0x00000004); + rank_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.SendContactInfo.Contact) + } + + // @@protoc_insertion_point(class_scope:management.SendContactInfo.Contact) + private static final proto.management.SendContactInfo.Contact DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.SendContactInfo.Contact(); + } + + public static proto.management.SendContactInfo.Contact getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Contact parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Contact(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.SendContactInfo.Contact getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private int bitField0_; + public static final int USERNAME_FIELD_NUMBER = 1; + private volatile java.lang.Object username_; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + @java.lang.Override + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + @java.lang.Override + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CONTACTS_FIELD_NUMBER = 2; + private java.util.List contacts_; + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + @java.lang.Override + public java.util.List getContactsList() { + return contacts_; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + @java.lang.Override + public java.util.List + getContactsOrBuilderList() { + return contacts_; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + @java.lang.Override + public int getContactsCount() { + return contacts_.size(); + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + @java.lang.Override + public proto.management.SendContactInfo.Contact getContacts(int index) { + return contacts_.get(index); + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + @java.lang.Override + public proto.management.SendContactInfo.ContactOrBuilder getContactsOrBuilder( + int index) { + return contacts_.get(index); + } + + public static final int BLOCKED_FIELD_NUMBER = 3; + private com.google.protobuf.LazyStringList blocked_; + /** + * repeated string blocked = 3; + * @return A list containing the blocked. + */ + public com.google.protobuf.ProtocolStringList + getBlockedList() { + return blocked_; + } + /** + * repeated string blocked = 3; + * @return The count of blocked. + */ + public int getBlockedCount() { + return blocked_.size(); + } + /** + * repeated string blocked = 3; + * @param index The index of the element to return. + * @return The blocked at the given index. + */ + public java.lang.String getBlocked(int index) { + return blocked_.get(index); + } + /** + * repeated string blocked = 3; + * @param index The index of the value to return. + * @return The bytes of the blocked at the given index. + */ + public com.google.protobuf.ByteString + getBlockedBytes(int index) { + return blocked_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasUsername()) { + memoizedIsInitialized = 0; + return false; + } + for (int i = 0; i < getContactsCount(); i++) { + if (!getContacts(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, username_); + } + for (int i = 0; i < contacts_.size(); i++) { + output.writeMessage(2, contacts_.get(i)); + } + for (int i = 0; i < blocked_.size(); i++) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, blocked_.getRaw(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, username_); + } + for (int i = 0; i < contacts_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, contacts_.get(i)); + } + { + int dataSize = 0; + for (int i = 0; i < blocked_.size(); i++) { + dataSize += computeStringSizeNoTag(blocked_.getRaw(i)); + } + size += dataSize; + size += 1 * getBlockedList().size(); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof proto.management.SendContactInfo)) { + return super.equals(obj); + } + proto.management.SendContactInfo other = (proto.management.SendContactInfo) obj; + + if (hasUsername() != other.hasUsername()) return false; + if (hasUsername()) { + if (!getUsername() + .equals(other.getUsername())) return false; + } + if (!getContactsList() + .equals(other.getContactsList())) return false; + if (!getBlockedList() + .equals(other.getBlockedList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasUsername()) { + hash = (37 * hash) + USERNAME_FIELD_NUMBER; + hash = (53 * hash) + getUsername().hashCode(); + } + if (getContactsCount() > 0) { + hash = (37 * hash) + CONTACTS_FIELD_NUMBER; + hash = (53 * hash) + getContactsList().hashCode(); + } + if (getBlockedCount() > 0) { + hash = (37 * hash) + BLOCKED_FIELD_NUMBER; + hash = (53 * hash) + getBlockedList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static proto.management.SendContactInfo parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendContactInfo parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendContactInfo parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendContactInfo parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendContactInfo parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static proto.management.SendContactInfo parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static proto.management.SendContactInfo parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.SendContactInfo parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.SendContactInfo parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static proto.management.SendContactInfo parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static proto.management.SendContactInfo parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static proto.management.SendContactInfo parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(proto.management.SendContactInfo prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code management.SendContactInfo} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:management.SendContactInfo) + proto.management.SendContactInfoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + proto.management.SendContactInfo.class, proto.management.SendContactInfo.Builder.class); + } + + // Construct using proto.management.SendContactInfo.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getContactsFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + username_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + if (contactsBuilder_ == null) { + contacts_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + } else { + contactsBuilder_.clear(); + } + blocked_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return proto.management.ManagementProtos.internal_static_management_SendContactInfo_descriptor; + } + + @java.lang.Override + public proto.management.SendContactInfo getDefaultInstanceForType() { + return proto.management.SendContactInfo.getDefaultInstance(); + } + + @java.lang.Override + public proto.management.SendContactInfo build() { + proto.management.SendContactInfo result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public proto.management.SendContactInfo buildPartial() { + proto.management.SendContactInfo result = new proto.management.SendContactInfo(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + to_bitField0_ |= 0x00000001; + } + result.username_ = username_; + if (contactsBuilder_ == null) { + if (((bitField0_ & 0x00000002) != 0)) { + contacts_ = java.util.Collections.unmodifiableList(contacts_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.contacts_ = contacts_; + } else { + result.contacts_ = contactsBuilder_.build(); + } + if (((bitField0_ & 0x00000004) != 0)) { + blocked_ = blocked_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.blocked_ = blocked_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof proto.management.SendContactInfo) { + return mergeFrom((proto.management.SendContactInfo)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(proto.management.SendContactInfo other) { + if (other == proto.management.SendContactInfo.getDefaultInstance()) return this; + if (other.hasUsername()) { + bitField0_ |= 0x00000001; + username_ = other.username_; + onChanged(); + } + if (contactsBuilder_ == null) { + if (!other.contacts_.isEmpty()) { + if (contacts_.isEmpty()) { + contacts_ = other.contacts_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureContactsIsMutable(); + contacts_.addAll(other.contacts_); + } + onChanged(); + } + } else { + if (!other.contacts_.isEmpty()) { + if (contactsBuilder_.isEmpty()) { + contactsBuilder_.dispose(); + contactsBuilder_ = null; + contacts_ = other.contacts_; + bitField0_ = (bitField0_ & ~0x00000002); + contactsBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getContactsFieldBuilder() : null; + } else { + contactsBuilder_.addAllMessages(other.contacts_); + } + } + } + if (!other.blocked_.isEmpty()) { + if (blocked_.isEmpty()) { + blocked_ = other.blocked_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureBlockedIsMutable(); + blocked_.addAll(other.blocked_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasUsername()) { + return false; + } + for (int i = 0; i < getContactsCount(); i++) { + if (!getContacts(i).isInitialized()) { + return false; + } + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + proto.management.SendContactInfo parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (proto.management.SendContactInfo) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object username_ = ""; + /** + * required string username = 1; + * @return Whether the username field is set. + */ + public boolean hasUsername() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * required string username = 1; + * @return The username. + */ + public java.lang.String getUsername() { + java.lang.Object ref = username_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + username_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string username = 1; + * @return The bytes for username. + */ + public com.google.protobuf.ByteString + getUsernameBytes() { + java.lang.Object ref = username_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + username_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string username = 1; + * @param value The username to set. + * @return This builder for chaining. + */ + public Builder setUsername( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + /** + * required string username = 1; + * @return This builder for chaining. + */ + public Builder clearUsername() { + bitField0_ = (bitField0_ & ~0x00000001); + username_ = getDefaultInstance().getUsername(); + onChanged(); + return this; + } + /** + * required string username = 1; + * @param value The bytes for username to set. + * @return This builder for chaining. + */ + public Builder setUsernameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + username_ = value; + onChanged(); + return this; + } + + private java.util.List contacts_ = + java.util.Collections.emptyList(); + private void ensureContactsIsMutable() { + if (!((bitField0_ & 0x00000002) != 0)) { + contacts_ = new java.util.ArrayList(contacts_); + bitField0_ |= 0x00000002; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + proto.management.SendContactInfo.Contact, proto.management.SendContactInfo.Contact.Builder, proto.management.SendContactInfo.ContactOrBuilder> contactsBuilder_; + + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public java.util.List getContactsList() { + if (contactsBuilder_ == null) { + return java.util.Collections.unmodifiableList(contacts_); + } else { + return contactsBuilder_.getMessageList(); + } + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public int getContactsCount() { + if (contactsBuilder_ == null) { + return contacts_.size(); + } else { + return contactsBuilder_.getCount(); + } + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public proto.management.SendContactInfo.Contact getContacts(int index) { + if (contactsBuilder_ == null) { + return contacts_.get(index); + } else { + return contactsBuilder_.getMessage(index); + } + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder setContacts( + int index, proto.management.SendContactInfo.Contact value) { + if (contactsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContactsIsMutable(); + contacts_.set(index, value); + onChanged(); + } else { + contactsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder setContacts( + int index, proto.management.SendContactInfo.Contact.Builder builderForValue) { + if (contactsBuilder_ == null) { + ensureContactsIsMutable(); + contacts_.set(index, builderForValue.build()); + onChanged(); + } else { + contactsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder addContacts(proto.management.SendContactInfo.Contact value) { + if (contactsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContactsIsMutable(); + contacts_.add(value); + onChanged(); + } else { + contactsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder addContacts( + int index, proto.management.SendContactInfo.Contact value) { + if (contactsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContactsIsMutable(); + contacts_.add(index, value); + onChanged(); + } else { + contactsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder addContacts( + proto.management.SendContactInfo.Contact.Builder builderForValue) { + if (contactsBuilder_ == null) { + ensureContactsIsMutable(); + contacts_.add(builderForValue.build()); + onChanged(); + } else { + contactsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder addContacts( + int index, proto.management.SendContactInfo.Contact.Builder builderForValue) { + if (contactsBuilder_ == null) { + ensureContactsIsMutable(); + contacts_.add(index, builderForValue.build()); + onChanged(); + } else { + contactsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder addAllContacts( + java.lang.Iterable values) { + if (contactsBuilder_ == null) { + ensureContactsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, contacts_); + onChanged(); + } else { + contactsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder clearContacts() { + if (contactsBuilder_ == null) { + contacts_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + } else { + contactsBuilder_.clear(); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public Builder removeContacts(int index) { + if (contactsBuilder_ == null) { + ensureContactsIsMutable(); + contacts_.remove(index); + onChanged(); + } else { + contactsBuilder_.remove(index); + } + return this; + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public proto.management.SendContactInfo.Contact.Builder getContactsBuilder( + int index) { + return getContactsFieldBuilder().getBuilder(index); + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public proto.management.SendContactInfo.ContactOrBuilder getContactsOrBuilder( + int index) { + if (contactsBuilder_ == null) { + return contacts_.get(index); } else { + return contactsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public java.util.List + getContactsOrBuilderList() { + if (contactsBuilder_ != null) { + return contactsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(contacts_); + } + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public proto.management.SendContactInfo.Contact.Builder addContactsBuilder() { + return getContactsFieldBuilder().addBuilder( + proto.management.SendContactInfo.Contact.getDefaultInstance()); + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public proto.management.SendContactInfo.Contact.Builder addContactsBuilder( + int index) { + return getContactsFieldBuilder().addBuilder( + index, proto.management.SendContactInfo.Contact.getDefaultInstance()); + } + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + public java.util.List + getContactsBuilderList() { + return getContactsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + proto.management.SendContactInfo.Contact, proto.management.SendContactInfo.Contact.Builder, proto.management.SendContactInfo.ContactOrBuilder> + getContactsFieldBuilder() { + if (contactsBuilder_ == null) { + contactsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + proto.management.SendContactInfo.Contact, proto.management.SendContactInfo.Contact.Builder, proto.management.SendContactInfo.ContactOrBuilder>( + contacts_, + ((bitField0_ & 0x00000002) != 0), + getParentForChildren(), + isClean()); + contacts_ = null; + } + return contactsBuilder_; + } + + private com.google.protobuf.LazyStringList blocked_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureBlockedIsMutable() { + if (!((bitField0_ & 0x00000004) != 0)) { + blocked_ = new com.google.protobuf.LazyStringArrayList(blocked_); + bitField0_ |= 0x00000004; + } + } + /** + * repeated string blocked = 3; + * @return A list containing the blocked. + */ + public com.google.protobuf.ProtocolStringList + getBlockedList() { + return blocked_.getUnmodifiableView(); + } + /** + * repeated string blocked = 3; + * @return The count of blocked. + */ + public int getBlockedCount() { + return blocked_.size(); + } + /** + * repeated string blocked = 3; + * @param index The index of the element to return. + * @return The blocked at the given index. + */ + public java.lang.String getBlocked(int index) { + return blocked_.get(index); + } + /** + * repeated string blocked = 3; + * @param index The index of the value to return. + * @return The bytes of the blocked at the given index. + */ + public com.google.protobuf.ByteString + getBlockedBytes(int index) { + return blocked_.getByteString(index); + } + /** + * repeated string blocked = 3; + * @param index The index to set the value at. + * @param value The blocked to set. + * @return This builder for chaining. + */ + public Builder setBlocked( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureBlockedIsMutable(); + blocked_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string blocked = 3; + * @param value The blocked to add. + * @return This builder for chaining. + */ + public Builder addBlocked( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureBlockedIsMutable(); + blocked_.add(value); + onChanged(); + return this; + } + /** + * repeated string blocked = 3; + * @param values The blocked to add. + * @return This builder for chaining. + */ + public Builder addAllBlocked( + java.lang.Iterable values) { + ensureBlockedIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, blocked_); + onChanged(); + return this; + } + /** + * repeated string blocked = 3; + * @return This builder for chaining. + */ + public Builder clearBlocked() { + blocked_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + * repeated string blocked = 3; + * @param value The bytes of the blocked to add. + * @return This builder for chaining. + */ + public Builder addBlockedBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureBlockedIsMutable(); + blocked_.add(value); + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:management.SendContactInfo) + } + + // @@protoc_insertion_point(class_scope:management.SendContactInfo) + private static final proto.management.SendContactInfo DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new proto.management.SendContactInfo(); + } + + public static proto.management.SendContactInfo getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SendContactInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SendContactInfo(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public proto.management.SendContactInfo getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/Server/src/main/proto/management/SendContactInfoOrBuilder.java b/Server/src/main/proto/management/SendContactInfoOrBuilder.java new file mode 100644 index 000000000..0c61cd740 --- /dev/null +++ b/Server/src/main/proto/management/SendContactInfoOrBuilder.java @@ -0,0 +1,75 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Management.proto + +package proto.management; + +public interface SendContactInfoOrBuilder extends + // @@protoc_insertion_point(interface_extends:management.SendContactInfo) + com.google.protobuf.MessageOrBuilder { + + /** + * required string username = 1; + * @return Whether the username field is set. + */ + boolean hasUsername(); + /** + * required string username = 1; + * @return The username. + */ + java.lang.String getUsername(); + /** + * required string username = 1; + * @return The bytes for username. + */ + com.google.protobuf.ByteString + getUsernameBytes(); + + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + java.util.List + getContactsList(); + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + proto.management.SendContactInfo.Contact getContacts(int index); + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + int getContactsCount(); + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + java.util.List + getContactsOrBuilderList(); + /** + * repeated .management.SendContactInfo.Contact contacts = 2; + */ + proto.management.SendContactInfo.ContactOrBuilder getContactsOrBuilder( + int index); + + /** + * repeated string blocked = 3; + * @return A list containing the blocked. + */ + java.util.List + getBlockedList(); + /** + * repeated string blocked = 3; + * @return The count of blocked. + */ + int getBlockedCount(); + /** + * repeated string blocked = 3; + * @param index The index of the element to return. + * @return The blocked at the given index. + */ + java.lang.String getBlocked(int index); + /** + * repeated string blocked = 3; + * @param index The index of the value to return. + * @return The bytes of the blocked at the given index. + */ + com.google.protobuf.ByteString + getBlockedBytes(int index); +} diff --git a/Server/src/test/kotlin/APITests.kt b/Server/src/test/kotlin/APITests.kt new file mode 100644 index 000000000..e76e85852 --- /dev/null +++ b/Server/src/test/kotlin/APITests.kt @@ -0,0 +1,373 @@ +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 { + var testPlayer: MockPlayer + var testPlayer2: MockPlayer + + init { + TestUtils.preTestSetup() + testPlayer = TestUtils.getMockPlayer("test") + testPlayer2 = TestUtils.getMockPlayer("test2") + } + + @Test fun testIfaceSettings(){ + var builder = IfaceSettingsBuilder() + val testOptions = builder.enableOptions(0..9).build() + Assertions.assertEquals(2046, testOptions, "Testing option flags") + + builder = IfaceSettingsBuilder() + val testSlotSwitch = builder.enableSlotSwitch().build() + Assertions.assertEquals(2097152, testSlotSwitch, "Testing slot switch flag") + + builder = IfaceSettingsBuilder() + val testNullSlot = builder.enableNullSlotSwitch().build() + Assertions.assertEquals(8388608, testNullSlot, "Testing null slotswitch flag") + + builder = IfaceSettingsBuilder() + val testUseWithFlags = builder.setUseOnSettings(true, true, true, true, true, true).build() + Assertions.assertEquals(129024, testUseWithFlags, "Testing usewith flags") + + builder = IfaceSettingsBuilder() + val testIfaceDepth = builder.setInterfaceEventsDepth(2).build() + Assertions.assertEquals(2 shl 18, testIfaceDepth and 1835008, "Testing events depth") + + builder = IfaceSettingsBuilder() + val testUseOption = builder.enableUseOption().build() + Assertions.assertEquals(1 shl 17, testUseOption, "Testing use option") + + builder = IfaceSettingsBuilder() + val testUseOnSelf = builder.enableUseOnSelf().build() + Assertions.assertEquals(1 shl 22, testUseOnSelf, "Testing use on self flag") + + builder = IfaceSettingsBuilder() + val testAllOptions = builder + .enableAllOptions() + .enableExamine() + .enableNullSlotSwitch() + .enableSlotSwitch() + .enableUseOnSelf() + .enableUseOption() + .setInterfaceEventsDepth(2) + .setUseOnSettings(true, true, true, true, true, true) + .build() + Assertions.assertEquals(15466494, testAllOptions, "Testing all options") + } + + @Test fun testSlayerManagerSaveAndLoadAndSaveProducesEquivalentJSON() { + var manager = SlayerManager() + manager.login(testPlayer) + manager.login(testPlayer2) + manager = SlayerManager.getInstance(testPlayer) + manager.flags.setPoints(20) + manager.flags.setMaster(Master.CHAELDAR) + manager.flags.setTask(Tasks.SKELETAL_WYVERN) + manager.flags.setTaskAmount(500) + + val manager2 = SlayerManager.getInstance(testPlayer2) + + val jsonFirst = JSONObject() + manager.savePlayer(testPlayer, jsonFirst) + manager.parsePlayer(testPlayer2, jsonFirst) + + val jsonSecond = JSONObject() + manager2.savePlayer(testPlayer2, jsonSecond) + Assertions.assertEquals(jsonFirst.toJSONString(), jsonSecond.toJSONString()) + } + + @Test fun testSlayerSaveAndParseProducesEquivalent() { + var manager = SlayerManager() + manager.login(testPlayer) + manager = SlayerManager.getInstance(testPlayer) + manager.flags.setPoints(500) + manager.flags.unlockHelm() + manager.flags.unlockBroads() + manager.flags.unlockRing() + + val json = JSONObject() + manager.savePlayer(testPlayer, json) + manager.flags.fullClear() + manager.parsePlayer(testPlayer, json) + Assertions.assertEquals(500, manager.flags.getPoints(), "Points were not 500!") + Assertions.assertEquals(true, manager.flags.isHelmUnlocked(), "Helm was not unlocked!") + Assertions.assertEquals(true, manager.flags.isBroadsUnlocked(), "Broads were not unlocked!") + Assertions.assertEquals(true, manager.flags.isRingUnlocked(), "Ring was not unlocked!") + } + + @Test fun testSlayerDecrementTaskAmountHasNoSideEffects() { + var manager = SlayerManager() + manager.login(testPlayer) + manager = SlayerManager.getInstance(testPlayer) + manager.flags.setTask(Tasks.CAVE_BUG) + manager.flags.setTaskAmount(100) + manager.flags.taskStreak = 4 + manager.flags.completedTasks = 4 + manager.flags.setMaster(Master.MAZCHNA) + + while(manager.hasTask()) manager.decrementAmount(1) + manager.flags.taskStreak += 1 + manager.flags.completedTasks += 1 + manager.flags.flagCanEarnPoints() + + Assertions.assertEquals(0, manager.flags.getTaskAmount(), "Task amount was not 0!") + Assertions.assertEquals(5, manager.flags.taskStreak, "Task streak was not 5!") + Assertions.assertEquals(Tasks.CAVE_BUG, manager.flags.getTask(), "Task was not cave bugs!") + Assertions.assertEquals(Master.MAZCHNA, manager.flags.getMaster(), "Master was not Mazchna!") + } + + @Test fun testKnownProblemSaveParsesCorrectly() { + val jsonString = "{\"slayer\": {\n" + + " \"taskStreak\": \"21\",\n" + + " \"rewardFlags\": 17301511,\n" + + " \"equipmentFlags\": 31,\n" + + " \"taskFlags\": 307220,\n" + + " \"removedTasks\": [\n" + + " \"73\"\n" + + " ],\n" + + " \"totalTasks\": \"108\"\n" + + " }}" + + val slayerData = JSONParser().parse(jsonString) as JSONObject + var manager = SlayerManager() + manager.login(testPlayer) + manager = SlayerManager.getInstance(testPlayer) + manager.parsePlayer(testPlayer, slayerData) + + Assertions.assertEquals(21, manager.flags.taskStreak) + Assertions.assertNotEquals(0, manager.flags.getPoints()) + Assertions.assertEquals(true, manager.flags.isHelmUnlocked()) + } + + @Test fun lineSplitShouldSplitAtLimitAndPreserveAllWords() { + var testCase = "The monks are running a ship from Port Sarim to Entrana, I hear too. Now leave me alone yer elephant!" + var expectedLine1 = "The monks are running a ship from Port Sarim to" + var expectedLine2 = "Entrana, I hear too. Now leave me alone yer elephant!" + var lines = splitLines(testCase, 54) + Assertions.assertEquals(expectedLine1, lines.getOrNull(0) ?: "") + Assertions.assertEquals(expectedLine2, lines.getOrNull(1) ?: "") + Assertions.assertEquals(2, lines.size) + + testCase = "Dramenwood staffs are crafted from branches of the Dramen tree, so they are. I hear there's a Dramen tree over on the island of Entrana in a cave." + expectedLine1 = "Dramenwood staffs are crafted from branches of the" + expectedLine2 = "Dramen tree, so they are. I hear there's a Dramen tree" + var expectedLine3 = "over on the island of Entrana in a cave." + lines = splitLines(testCase, 54) + Assertions.assertEquals(expectedLine1, lines.getOrNull(0) ?: "") + Assertions.assertEquals(expectedLine2, lines.getOrNull(1) ?: "") + Assertions.assertEquals(expectedLine3, lines.getOrNull(2) ?: "") + Assertions.assertEquals(3, lines.size) + + testCase = "This should be one line." + lines = splitLines(testCase) + Assertions.assertEquals(testCase, lines[0]) + Assertions.assertEquals(1, lines.size) + + testCase = "I just told you: from the Seer. You will need to persuade him to take the time to make a forecast somehow." + lines = splitLines(testCase) + expectedLine1 = "I just told you: from the Seer. You will need to" + expectedLine2 = "persuade him to take the time to make a forecast" + expectedLine3 = "somehow." + Assertions.assertEquals(expectedLine1, lines.getOrNull(0) ?: "") + 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 new file mode 100644 index 000000000..baac462fd --- /dev/null +++ b/Server/src/test/kotlin/ExchangeTests.kt @@ -0,0 +1,206 @@ +import content.global.handlers.iface.ge.StockMarket +import core.game.ge.OfferState +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +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 org.junit.jupiter.api.fail +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 { + companion object { + private const val TEST_DB_PATH = "ge_test.db" + init { + TestUtils.preTestSetup() + GEDB.init(TEST_DB_PATH) + } + + 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 + 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.writeNew() + return offer + } + + 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() + } + } + + @Test fun testPlaceOffer() { + val offer = generateOffer(4151, 1, 100000, true) + val uid = offer.playerUID + + GEDB.run { conn -> + val stmt = conn.createStatement() + val result = stmt.executeQuery("select * from player_offers where player_uid = $uid") + val thisOffer = if(result.next()) GrandExchangeOffer.fromQuery(result) else fail("Offer did not exist!") + Assertions.assertEquals(offer.itemID, thisOffer.itemID) + } + } + + @Test fun testBuyFirstSellAfterFavorsSell() { + val buyOffer = generateOffer(4151, 1, 100000, false) + val sellOffer = generateOffer(4151, 1, 85000, true) + GrandExchange.exchange(buyOffer, sellOffer) + Assertions.assertEquals(null, buyOffer.withdraw[1], "Buyer got coins back on sell bias") //should get no coins back + } + + @Test fun testSellFirstBuyAfterFavorsBuy() { + val sellOffer = generateOffer(4151, 1, 85000, true) + val buyOffer = generateOffer(4151, 1, 100000, false) + GrandExchange.exchange(sellOffer,buyOffer) + Assertions.assertNotEquals(null, buyOffer.withdraw[1], "Buyer did not get coins back on buy bias") //should get coins back + } + + @Test fun buyerCannotBuyHigherSellOffer() { + val sellOffer = generateOffer(4151, 1, 125000, true) + val buyOffer = generateOffer(4151, 1, 100000, false) + GrandExchange.exchange(sellOffer,buyOffer) + Assertions.assertEquals(null, buyOffer.withdraw[0]) //Buyer should not get any items, sell offer is higher + } + + @Test fun manyCompletedOffersAboveDefaultPriceShouldInfluencePriceUpwards() { + val defaultPrice = GrandExchange.getRecommendedPrice(4151) + val modifiedPrice = defaultPrice * 1.15 + + for(i in 0 until 100) { + val sellOffer = generateOffer(4151, 1, modifiedPrice.toInt(), true, "test1") + val buyOffer = generateOffer(4151, 1, modifiedPrice.toInt(), false, "test2") + GrandExchange.exchange(sellOffer,buyOffer) + } + + val newPrice = GrandExchange.getRecommendedPrice(4151) + + Assertions.assertEquals(true, newPrice > defaultPrice, "Price was not influenced in the expected way! New Price: $newPrice, default: $defaultPrice") + } + + @Test fun playerTradingWithThemselvesShouldNotBeAbleToInfluencePrices() { + val defaultPrice = GrandExchange.getRecommendedPrice(4151) + val modifiedPrice = defaultPrice * 1.15 + + for(i in 0 until 100) { + val sellOffer = generateOffer(4151, 1, modifiedPrice.toInt(), true, "test") + val buyOffer = generateOffer(4151, 1, modifiedPrice.toInt(), false, "test") + GrandExchange.exchange(sellOffer,buyOffer) + } + + Assertions.assertEquals(defaultPrice, GrandExchange.getRecommendedPrice(4151)) + } + + @Test fun concurrentlySubmittedOffersShouldNotThrowExceptions(){ + runBlocking { + val a = GlobalScope.launch { for(i in 0 until 5) { + PriceIndex.allowItem(i); GrandExchange.addBotOffer(i, 1)} } + val b = GlobalScope.launch { for(i in 0 until 5) { + PriceIndex.allowItem(i); GrandExchange.addBotOffer(i, 1)} } + a.join() + b.join() + Assertions.assertEquals(false, a.isCancelled) + 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/ItemDefinitionTests.kt b/Server/src/test/kotlin/ItemDefinitionTests.kt new file mode 100644 index 000000000..3237ea20a --- /dev/null +++ b/Server/src/test/kotlin/ItemDefinitionTests.kt @@ -0,0 +1,54 @@ +package core.cache + +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 kotlin.math.roundToInt + +class ItemDefinitionTests { + companion object { + init {TestUtils.preTestSetup()} + } + + @Test fun itemHighAlchIs60PercentValueRandomItem1() { + val natureRune = Item(Items.NATURE_RUNE_561) + Assertions.assertEquals((natureRune.definition.value * 0.6).roundToInt(), natureRune.definition.getAlchemyValue(true)) + } + + @Test fun itemHighAlchIs60PercentValueRandomItem2() { + val tuna = Item(Items.TUNA_361) + Assertions.assertEquals((tuna.definition.value * 0.6).roundToInt(), tuna.definition.getAlchemyValue(true)) + } + + @Test fun itemHighAlchIs60PercentValueRandomItem3() { + val bass = Item(Items.BASS_365) + Assertions.assertEquals((bass.definition.value * 0.6).roundToInt(), bass.definition.getAlchemyValue(true)) + } + + @Test fun itemHighAlchIs60PercentValueRandomItem4() { + val goldBar = Item(Items.GOLD_BAR_2357) + Assertions.assertEquals((goldBar.definition.value * 0.6).roundToInt(), goldBar.definition.getAlchemyValue(true)) + } + + @Test fun itemLowAlchIs40PercentValueRandomItem1() { + val natureRune = Item(Items.NATURE_RUNE_561) + Assertions.assertEquals((natureRune.definition.value * 0.4).roundToInt(), natureRune.definition.getAlchemyValue(false), natureRune.name) + } + + @Test fun itemLowAlchIs40PercentValueRandomItem2() { + val tuna = Item(Items.TUNA_361) + Assertions.assertEquals((tuna.definition.value * 0.4).roundToInt(), tuna.definition.getAlchemyValue(false), tuna.name) + } + + @Test fun itemLowAlchIs40PercentValueRandomItem3() { + val bass = Item(Items.BASS_365) + Assertions.assertEquals((bass.definition.value * 0.4).roundToInt(), bass.definition.getAlchemyValue(false), bass.name) + } + + @Test fun itemLowAlchIs40PercentValueRandomItem4() { + val goldBar = Item(Items.GOLD_BAR_2357) + Assertions.assertEquals((goldBar.definition.value * 0.4).roundToInt(), goldBar.definition.getAlchemyValue(false), goldBar.name) + } +} 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/PriceIndexTests.kt b/Server/src/test/kotlin/PriceIndexTests.kt new file mode 100644 index 000000000..f358e3046 --- /dev/null +++ b/Server/src/test/kotlin/PriceIndexTests.kt @@ -0,0 +1,37 @@ +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import core.game.ge.GEDB +import core.game.ge.PriceIndex + +class PriceIndexTests { + companion object {init {TestUtils.preTestSetup(); GEDB.init("ge_test.db")}} + + @Test fun shouldAllowCheckingIfItemCanBeSoldOnGE() { + PriceIndex.allowItem(4151) + val canSellWhip = PriceIndex.canTrade(4151) + Assertions.assertEquals(true, canSellWhip) + } + + @Test fun shouldAllowBanningItems() { + PriceIndex.allowItem(1333) + Assertions.assertEquals(true, PriceIndex.canTrade(1333)) + PriceIndex.banItem(1333) + Assertions.assertEquals(false, PriceIndex.canTrade(1333)) + } + + @Test fun shouldAllowValueToBeInfluenced() { + PriceIndex.allowItem(1333) + val defaultValue = PriceIndex.getValue(1333) + PriceIndex.addTrade(1333, 100, (defaultValue * 1.15).toInt()) + Assertions.assertNotEquals(defaultValue, PriceIndex.getValue(1333)) + } + + @Test fun shouldAllowValueToGoDownWithHighVolumeOfLowerValueTrades() { + PriceIndex.allowItem(1334) + val defaultValue = PriceIndex.getValue(1334) + PriceIndex.addTrade(1334, 1000, (defaultValue * 1.15).toInt()) + Assertions.assertEquals(true, PriceIndex.getValue(1334) > defaultValue) + PriceIndex.addTrade(1334, 2000, (defaultValue * 0.85).toInt()) + Assertions.assertEquals(true, PriceIndex.getValue(1334) < defaultValue, "Old: $defaultValue, New: ${PriceIndex.getValue(1334)}") + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/QuestTests.kt b/Server/src/test/kotlin/QuestTests.kt new file mode 100644 index 000000000..0a4fa7bc8 --- /dev/null +++ b/Server/src/test/kotlin/QuestTests.kt @@ -0,0 +1,62 @@ +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 { + 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 + } + } + val testQuest = TestQuest() + + @Test fun getIndexShouldNotThrowException() { + Assertions.assertDoesNotThrow { + testQuest.index + } + } + + @Test fun registerShouldMakeQuestImmediatelyAvailable() { + QuestRepository.register(testQuest) + Assertions.assertNotNull(QuestRepository.getQuests()[testQuest.quest]) + } + + @Test fun registerShouldMakeQuestImmediatelyAvailableToInstances() { + QuestRepository.register(testQuest) + val instance = QuestRepository(testPlayer) + Assertions.assertNotNull(instance.getQuest(testQuest.quest)) + } + + @Test fun getStageOnUnstartedQuestShouldNotThrowException() { + QuestRepository.register(testQuest) + val instance = QuestRepository(testPlayer) + Assertions.assertDoesNotThrow { + instance.getStage(testQuest) + } + } + + @Test fun setStageOnUnstartedQuestShouldNotThrowException() { + QuestRepository.register(testQuest) + val instance = QuestRepository(testPlayer) + Assertions.assertDoesNotThrow { + instance.setStage(testQuest, 10) + } + } + + @Test fun completeQuestShouldThrowExceptionIfAlreadyComplete() { + Assertions.assertThrows(IllegalStateException::class.java, { + QuestRepository.register(testQuest) + val repo = QuestRepository(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/RegionSpecificationTests.kt b/Server/src/test/kotlin/RegionSpecificationTests.kt new file mode 100644 index 000000000..52b6d630d --- /dev/null +++ b/Server/src/test/kotlin/RegionSpecificationTests.kt @@ -0,0 +1,130 @@ +import core.api.regionspec.* +import core.api.regionspec.contracts.FillChunkContract +import core.game.world.map.BuildRegionChunk +import core.game.world.map.Region +import core.game.world.map.RegionChunk +import core.game.world.map.RegionManager +import core.game.world.map.build.DynamicRegion +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class RegionSpecificationTests { + companion object { + init { + TestUtils.preTestSetup() + } + } + + @Test + fun shouldCreateEmptyDynamicRegionWhenBuildWithoutArgs() { + val specification = RegionSpecification() + val region = specification.build() + Assertions.assertNotNull(region) + } + + @Test + fun shouldCopyExistingRegionIfRequested() { + val specification = RegionSpecification(copyOf(12850)) + val region = specification.build() + Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(23, 17, 0))?.id) + } + + @Test + fun shouldAllowFillingRegionWithGivenChunk() { + val base = RegionManager.forId(12850) + Region.load(base) + val chunk = base.planes[0].getRegionChunk(2, 2) + val specification = RegionSpecification(fillWith(chunk).from(base).onPlanes(0)) + val region = specification.build() + Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(23, 17, 0))?.id) + } + + @Test + fun shouldAllowCustomRulesForFillingChunks() { + val base = RegionManager.forId(12850) + Region.load(base) + val chunk = base.planes[0].getRegionChunk(2, 2) + val specification = RegionSpecification( + fillWith(chunk) + .from(base) + .onPlanes(0) + .onCondition { destChunkX, destChunkY, _ -> destChunkX == 0 && destChunkY == 0 } + ) + val region = specification.build() + Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(7, 1, 0))?.id) + Assertions.assertNull(RegionManager.getObject(region.baseLocation.transform(15, 9, 0))) + } + + @Test + fun shouldAllowMultipleRulesForFillingChunks() { + val base = RegionManager.forId(12850) + Region.load(base) + val chunk = base.planes[0].getRegionChunk(2, 2) + val specification = RegionSpecification( + fillWith(chunk) + .from(base) + .onPlanes(0) + .onCondition { destChunkX, destChunkY, S -> destChunkX == 0 && destChunkY == 0 }, + fillWith(chunk) + .from(base) + .onPlanes(1, 2, 3) + ) + val region = specification.build() + Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(7, 1, 0))?.id) + Assertions.assertEquals(36782, RegionManager.getObject(region.baseLocation.transform(7, 1, 1))?.id) + Assertions.assertNull(RegionManager.getObject(region.baseLocation.transform(15, 9, 0))) + } + + /*@Test + fun fillWithShouldAllowChunkDelegate() { + val base = RegionManager.forId(12850) + Region.load(base) + val specification = RegionSpecification( + fillWith { destChunkX, destChunkY, destPlane, _ -> + base.planes[destPlane].getRegionChunk(destChunkX, destChunkY) + }.from(base).onPlanes(0, 1, 2, 3) + ) + val region = specification.build() + Assertions.assertEquals( + base.planes[0].chunks[1][1].objects[1][3]?.id, + region.planes[0].chunks[1][1].objects[1][3]?.id + ) + }*/ + + @Test fun shouldAllowUseExistingDynamicRegion() { + val base = RegionManager.forId(12850) + Region.load(base) + val chunk = base.planes[0].getRegionChunk(2, 2) + val dyn = DynamicRegion.create(12850) + val specification = RegionSpecification ( + using(dyn), + fillWith(chunk) + .from(base) + .onPlanes(0) + ) + specification.build() + Assertions.assertEquals(36782, RegionManager.getObject(dyn.baseLocation.transform(7, 1, 0))?.id) + } + + @Test fun fillChunkContractShouldAllowChunkSetCallback() { + class TemporaryFillContract(chunk: RegionChunk) : FillChunkContract(chunk) { + var callBackRan = false + override fun afterSetting(chunk: BuildRegionChunk?, x: Int, y: Int, plane: Int, dyn: DynamicRegion) { + callBackRan = true + } + } + val base = RegionManager.forId(12850) + Region.load(base) + val chunk = base.planes[0].getRegionChunk(2, 2) + val dyn = DynamicRegion.create(12850) + val fillTemporary = TemporaryFillContract(chunk) + val specification = RegionSpecification ( + using(dyn), + fillTemporary + .from(base) + .onPlanes(0) + ) + specification.build() + Assertions.assertEquals(true, fillTemporary.callBackRan) + } +} diff --git a/Server/src/test/kotlin/RegionTests.kt b/Server/src/test/kotlin/RegionTests.kt new file mode 100644 index 000000000..7e48c338a --- /dev/null +++ b/Server/src/test/kotlin/RegionTests.kt @@ -0,0 +1,113 @@ +import core.cache.Cache +import core.game.world.map.Region +import core.game.world.map.RegionChunk +import core.game.world.map.RegionManager +import core.game.world.map.build.DynamicRegion +import core.game.world.map.build.LandscapeParser +import org.junit.Assert +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import core.game.system.config.ServerConfigParser +import core.game.system.config.XteaParser + +class RegionTests { + companion object { + init {TestUtils.preTestSetup();} + } + + @Test fun testRegionLoad() { + val region = RegionManager.forId(12850) + Region.load(region) + Assertions.assertNotEquals(0, region.objectCount, "Region has no objects! (Failed to parse?)") + } + + @Test fun shouldHaveDifferentIdForDynamicCopy() { + val region = RegionManager.forId(12850) + val dynamic = DynamicRegion.create(12850) + Assertions.assertNotEquals(region.regionId, (dynamic.x shl 8) or dynamic.y) + } + + @Test fun dynamicRegionCreationShouldNotReplaceOriginal() { + val region = RegionManager.forId(12850) + val dynamic = DynamicRegion.create(12850) + Assertions.assertEquals(false, RegionManager.forId(12850) is DynamicRegion) + } + + @Test fun testDynamicRegionHasSameObjects() { + val base = RegionManager.forId(12850) + Region.load(base) + val dynamic = DynamicRegion.create(12850) + Region.load(dynamic) + + Assertions.assertEquals(true, dynamic.objectCount > 0, "Dynamic and standard have differing object counts!") + } + + @Test fun testObjectExistsInStandardRegion() { + val base = RegionManager.forId(12850) + Region.load(base) + + val targetLoc = base.baseLocation.transform(23, 17, 0) //location of a bush + Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") + } + + @Test fun testObjectExistsInDynamicRegion() { + val base = RegionManager.forId(12850) + val dynamic = DynamicRegion.create(12850) + Region.load(base) + Region.load(dynamic) + + val targetLoc = dynamic.baseLocation.transform(23, 17, 0) + Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") + } + + @Test fun testObjectExistsInCopiedChunk() { + val base = RegionManager.forId(12850) + val dynamic = DynamicRegion.create(12851) + Region.load(base) + Region.load(dynamic) + val targetLoc = dynamic.baseLocation.transform(23, 17, 0) + Assertions.assertEquals(null, RegionManager.getObject(targetLoc), "Object exists pre-copy?") + val replacement = base.planes[0].getRegionChunk(2, 2) + dynamic.replaceChunk(0, 2, 2, replacement.copy(dynamic.planes[0]), base) + Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") + } + + @Test fun testObjectExistsInCopiedChunkUsingBuildFlag() { + val base = RegionManager.forId(12850) + val dynamic = DynamicRegion.create(12851) + Region.load(base, true) + Region.load(dynamic, true) + val targetLoc = dynamic.baseLocation.transform(23, 17, 0) + Assertions.assertEquals(null, RegionManager.getObject(targetLoc), "Object exists pre-copy?") + val replacement = base.planes[0].getRegionChunk(2, 2) + dynamic.replaceChunk(0, 2, 2, replacement.copy(dynamic.planes[0]), base) + Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") + } + + @Test fun testObjectExistsInCopiedChunkCopiedIntoBlankRegion() { + val base = RegionManager.forId(12850) + val borders = DynamicRegion.reserveArea(8,8) + val dynamic = DynamicRegion(-1, borders.southWestX shr 6, borders.southWestY shr 6) + dynamic.borders = borders + dynamic.isUpdateAllPlanes = true + RegionManager.addRegion(dynamic.id, dynamic) + val targetLoc = dynamic.baseLocation.transform(23, 17, 0) + val replacement = base.planes[0].getRegionChunk(2,2) + dynamic.replaceChunk(0, 2, 2, replacement.copy(dynamic.planes[0]), base) + Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") + } + + @Test fun testObjectExistsInCopiedChunkInLinkedRegion() { + val base = DynamicRegion.create(12850) + val borders = DynamicRegion.reserveArea(8,8) + val dynamic = DynamicRegion(-1, borders.southWestX shr 6, borders.southWestY shr 6) + dynamic.borders = borders + dynamic.isUpdateAllPlanes = true + RegionManager.addRegion(dynamic.id, dynamic) + val targetLoc = dynamic.baseLocation.transform(23, 17, 0) + val replacement = base.planes[0].getRegionChunk(2,2) + dynamic.replaceChunk(0, 2, 2, replacement.copy(dynamic.planes[0]), base) + base.link(dynamic) + Assertions.assertEquals(36782, RegionManager.getObject(targetLoc)?.id ?: -1, "Object does not exist at expected location!") + } +} \ 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 new file mode 100644 index 000000000..025fd13f1 --- /dev/null +++ b/Server/src/test/kotlin/ShopTests.kt @@ -0,0 +1,366 @@ + +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 kotlin.math.min +import kotlin.math.roundToInt + +class ShopTests { + companion object { + init {TestUtils.preTestSetup()} + } + + private val testPlayer = TestUtils.getMockPlayer("test") + private val testIronman = TestUtils.getMockPlayer("test2", IronmanMode.STANDARD) + private var nonGeneral = TestUtils.getMockShop("Not General", false, false, Item(4151, 1)) + private var general = TestUtils.getMockShop("General", true, false, Item(4151, 1)) + private var highAlch = TestUtils.getMockShop("High(af) Alch", true, true, Item(4151, 1)) + private var tokkulShop = TestUtils.getMockTokkulShop("Tokkul", Item(Items.DEATH_RUNE_560, 10)) + + @BeforeEach fun beforeEach() { + val testPlayers = arrayOf(testPlayer, testIronman) + for (player in testPlayers) { + player.inventory.clear() + player.attributes.remove("shop-cont") + } + nonGeneral.playerStock.clear() + general.playerStock.clear() + highAlch.playerStock.clear() + tokkulShop.playerStock.clear() + } + + private fun assertTransactionSuccess(status: Shop.TransactionStatus) { + Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) status.reason else ""}") + } + + @Test fun shouldSellItemToStore() { + testPlayer.inventory.add(Item(4151, 1)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + val status = general.sell(testPlayer, 0, 1) + assertTransactionSuccess(status) + } + + @Test fun shouldNotSellUnstockedItemToStandardStore() { + testPlayer.inventory.add(Item(1511, 1)) + testPlayer.setAttribute("shop-cont", nonGeneral.getContainer(testPlayer)) + val status = nonGeneral.sell(testPlayer, 0, 1) + Assertions.assertEquals(true, status is Shop.TransactionStatus.Failure) + } + + @Test fun shouldSellUnstockedItemToGeneralStore() { + testPlayer.inventory.add(Item(1511, 1)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + val status = general.sell(testPlayer, 0, 1) + assertTransactionSuccess(status) + } + + @Test fun shouldNotSellDestroyable() { + testPlayer.inventory.add(Item(1, 795)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + val status = general.sell(testPlayer, 0, 1) + Assertions.assertEquals(true, status is Shop.TransactionStatus.Failure) + } + + @Test fun shouldNotSellUntradeable() { + testPlayer.inventory.add(Item(1, 1799)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + val status = general.sell(testPlayer, 0, 1) + Assertions.assertEquals(true, status is Shop.TransactionStatus.Failure) + } + + @Test fun shouldSellUnstockedItemToGeneralStoreUsingLowAlchBaseValue() { + //starts at 40% value - a.k.a. low alch price + //drops 3% value per item stocked + //bottoms out at 10% value (25% of the low alch price) after 10 items stocked + val saleItemId = Items.RUNE_MED_HELM_1147 + val shopContainer = general.getContainer(testPlayer) + Assertions.assertFalse(shopContainer.containItems(saleItemId), "Pre-assertion, shop container should not have the unstocked item to begin with.") + testPlayer.setAttribute("shop-cont", shopContainer) + val playerStock = general.playerStock + Assertions.assertFalse(playerStock.containItems(saleItemId), "Pre-assertion, player stock should not have the unstocked item to begin with.") + val saleItem = Item(saleItemId, 1) + val alchValue = saleItem.definition.getAlchemyValue(false) + val value = saleItem.definition.value + Assertions.assertEquals((value * 0.4).roundToInt(), alchValue, "Pre-assertion, low alch value should be 40% value.") + + for (i in 0..14) { + val expectedCoins = (alchValue.toDouble() - value * min(0.03 * i, 0.30)).roundToInt() + testPlayer.inventory.add(saleItem.copy()) + Assertions.assertEquals(saleItemId, testPlayer.inventory.getId(0), "Pre-assertion, should have item in inventory slot 0.") + + val status = general.sell(testPlayer, 0, 1) + + assertTransactionSuccess(status) + val coinItem = testPlayer.inventory[0] + Assertions.assertEquals(Items.COINS_995, coinItem.id) + Assertions.assertEquals( + expectedCoins, + coinItem.amount, + "Selling item $i should yield the expected price." + ) + testPlayer.inventory.clear() + } + } + + @Test fun shouldSellUnstockedItemToHighAlchStoreUsingHighAlchBaseValue() { + //starts at 60% value - a.k.a. high alch price + //drops 3% value per item stocked + //bottoms out at 30% value (50% of the high alch price) after 10 items stocked + val saleItemId = Items.RUNE_MED_HELM_1147 + val shopContainer = highAlch.getContainer(testPlayer) + Assertions.assertFalse(shopContainer.containItems(saleItemId), "Pre-assertion, shop container should not have the unstocked item to begin with.") + testPlayer.setAttribute("shop-cont", shopContainer) + val playerStock = highAlch.playerStock + Assertions.assertFalse(playerStock.containItems(saleItemId), "Pre-assertion, player stock should not have the unstocked item to begin with.") + val saleItem = Item(saleItemId, 1) + val alchValue = saleItem.definition.getAlchemyValue(true) + val value = saleItem.definition.value + Assertions.assertEquals((value * 0.6).roundToInt(), alchValue, "Pre-assertion, high alch value should be 60% value.") + + for (i in 0..14) { + val expectedCoins = (alchValue.toDouble() - value * min(0.03 * i, 0.30)).roundToInt() + testPlayer.inventory.add(saleItem.copy()) + Assertions.assertEquals(saleItemId, testPlayer.inventory.getId(0), "Pre-assertion, should have item in inventory slot 0.") + + val status = highAlch.sell(testPlayer, 0, 1) + + assertTransactionSuccess(status) + val coinItem = testPlayer.inventory[0] + Assertions.assertEquals(Items.COINS_995, coinItem.id) + Assertions.assertEquals( + expectedCoins, + coinItem.amount, + "Selling item $i should yield the expected price." + ) + testPlayer.inventory.clear() + } + } + + @Test fun shouldSellNotedUnstockedItemForSamePriceAsUnnoted() { + val saleItemId = Items.RUNE_MED_HELM_1147 + var notedSaleItemId = Items.RUNE_MED_HELM_1148 + val shopContainer = highAlch.getContainer(testPlayer) + Assertions.assertFalse(shopContainer.containItems(saleItemId), "Pre-assertion, shop container should not have the unstocked item to begin with.") + testPlayer.setAttribute("shop-cont", shopContainer) + val playerStock = highAlch.playerStock + Assertions.assertFalse(playerStock.containItems(saleItemId), "Pre-assertion, player stock should not have the unstocked item to begin with.") + testPlayer.inventory.add(Item(saleItemId, 1)) + var status = highAlch.sell(testPlayer, 0, 1) + assertTransactionSuccess(status) + testPlayer.inventory.clear() + val notedSaleItem = Item(notedSaleItemId, 1) + testPlayer.inventory.add(notedSaleItem) + val alchValue = notedSaleItem.definition.getAlchemyValue(true) + val value = notedSaleItem.definition.value + status = highAlch.sell(testPlayer, 0, 1) + assertTransactionSuccess(status) + val expectedCoins = (alchValue.toDouble() - value * 0.03).roundToInt() + + val coinItem = testPlayer.inventory[0] + Assertions.assertEquals(Items.COINS_995, coinItem.id) + Assertions.assertEquals( + expectedCoins, + coinItem.amount, + "Selling noted item should yield the same devalued price as base item." + ) + } + + @Test fun minimumSellPriceShouldBe1Coin() { + testPlayer.inventory.add(Item(Items.EMPTY_POT_1931, 1)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + val status = general.sell(testPlayer, 0, 1) + assertTransactionSuccess(status) + val coinItem = testPlayer.inventory[0] + Assertions.assertEquals(Items.COINS_995, coinItem.id) + Assertions.assertEquals( + 1, + coinItem.amount, + "1 coin should be the minimum selling price." + ) + } + + @Test fun tokkulShouldBe10xLessValuableWhenSellingAStockedItem() { + val startingTokkul = 5000 + testPlayer.inventory.add(Item(Items.TOKKUL_6529, startingTokkul)) + testPlayer.setAttribute("shop-cont", tokkulShop.getContainer(testPlayer)) + testPlayer.setAttribute("shop-main", true) + var status = tokkulShop.buy(testPlayer, 0, 1) // 1 death rune + assertTransactionSuccess(status) + Assertions.assertEquals(Items.TOKKUL_6529, testPlayer.inventory[0].id, "Pre-assertion: First item should still be the currency used.") + val cost = startingTokkul - testPlayer.inventory[0].amount + status = tokkulShop.sell(testPlayer, 1, 1) // back to starting stock + assertTransactionSuccess(status) + testPlayer.inventory.clear() + testPlayer.inventory.add(Item(Items.DEATH_RUNE_560, 1)) + + status = tokkulShop.sell(testPlayer, 0, 1) // sell 1 death rune to fresh shop + assertTransactionSuccess(status) + + Assertions.assertEquals(Items.TOKKUL_6529, testPlayer.inventory[0].id, "Expected same currency back.") + Assertions.assertEquals((cost / 10.0).toInt(), testPlayer.inventory[0].amount, "Expected 10 times less for selling than for cost of buying.") + } + + @Test fun shouldSellUnstockedItemToGeneralStoreAsIronman() { + testIronman.inventory.add(Item(1511, 1)) + testIronman.setAttribute("shop-cont", general.getContainer(testIronman)) + val status = general.sell(testIronman, 0, 1) + assertTransactionSuccess(status) + } + + @Test fun shouldSellStackOfUnstockedItemsToPlayerStock() { + testPlayer.inventory.add(Item(1512, 20)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + val status = general.sell(testPlayer, 0, 20) + assertTransactionSuccess(status) + } + + @Test fun shouldPutSoldUnstockedItemsInPlayerStock() { + testPlayer.inventory.add(Item(2,1)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + val status = general.sell(testPlayer, 0, 1) + assertTransactionSuccess(status) + Assertions.assertEquals(1, general.playerStock.getAmount(2)) + Assertions.assertEquals(0, general.getContainer(testPlayer).getAmount(2)) + } + + @Test fun shouldAllowStandardPlayerToBuy() { + 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) + assertTransactionSuccess(status) + } + + @Test fun shouldAllowStandardPlayerToBuyOverstock() { + 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)) + val status = general.buy(testPlayer, 0, 1) + assertTransactionSuccess(status) + } + + @Test fun shouldAllowStandardPlayerToBuyPlayerStock() { + testPlayer.inventory.add(Item(995, 125000)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + testPlayer.setAttribute("shop-main", false) + general.playerStock.add(Item(4151, 100)) + val status = general.buy(testPlayer, 0, 1) + assertTransactionSuccess(status) + } + + @Test fun shouldNotAllowIronmanToBuyOverstock() { + testIronman.inventory.add(Item(995, 100000)) + testIronman.setAttribute("shop-cont", general.getContainer(testIronman)) + testIronman.setAttribute("shop-main", true) + general.getContainer(testIronman).add(Item(4151, 100)) + val status = general.buy(testIronman, 0, 1) + Assertions.assertEquals(true, status is Shop.TransactionStatus.Failure) + } + + @Test fun shouldNotAllowIronmanToBuyPlayerStock() { + testIronman.inventory.add(Item(995, 100000)) + testIronman.setAttribute("shop-cont", general.playerStock) + testIronman.setAttribute("shop-main", false) + general.playerStock.add(Item(4151, 1)) + val status = general.buy(testIronman, 0, 1) + Assertions.assertEquals(true, status is Shop.TransactionStatus.Failure) + } + + @Test fun openShopShouldNotThrowException() { + Assertions.assertDoesNotThrow { + general.openFor(testPlayer) + } + } + + @Test fun shouldNotThrowExceptionWhenRestockingStockWithNullSlot() { + Assertions.assertDoesNotThrow { + general.getContainer(testPlayer).add(Item(1, 100)) + general.getContainer(testPlayer).add(Item(2, 100)) + general.getContainer(testPlayer).replace(null, 0) //replace item in slot 0 with null + for ((k,_) in general.stockInstances) general.needsUpdate[k] = true + general.restock() + } + } + + @Test fun playerStockShouldNeverBeNull() { + Assertions.assertNotNull(general.playerStock) + } + + @Test fun shouldAllowBuyingFromPlayerStockOnMultipleRows() { + for(i in 0 until 100) { + general.playerStock.add(Item(i + 3100, 1)) //make sure we populate several rows of items + } + testPlayer.inventory.add(Item(995, 100000)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + testPlayer.setAttribute("shop-main", false) + var status: Shop.TransactionStatus = Shop.TransactionStatus.Failure("Test did not assign transaction status.") + Assertions.assertDoesNotThrow({ + status = general.buy(testPlayer, 36, 1) + }, "Error selling to shop: ${general.playerStock}") + assertTransactionSuccess(status) + } + + @Test fun invalidStockJsonShouldNotCauseItemShift() { + val invalidJson = "{1277,10,100}-{1277,10,100}-{1279,10,100}" + val stock = Shops.parseStock(invalidJson, -1) + Assertions.assertEquals(2, stock.size) + Assertions.assertEquals(20, stock[0].amount) + } + + @Test fun buying0StockItemFromNormalStockShouldNotSucceedNorDeductGold() { + testPlayer.inventory.clear() + testPlayer.inventory.add(Item(995, Integer.MAX_VALUE)) + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + testPlayer.setAttribute("shop-main", true) + val inStockAmount = general.getContainer(testPlayer).get(0).amount + + //Buy out existing stock + var status: Shop.TransactionStatus = Shop.TransactionStatus.Success() + + Assertions.assertDoesNotThrow { + status = general.buy(testPlayer, 0, inStockAmount) + } + Assertions.assertEquals(true, status is Shop.TransactionStatus.Success, "Transaction failure: ${if(status is Shop.TransactionStatus.Failure) (status as Shop.TransactionStatus.Failure).reason else ""}") + Assertions.assertEquals(0, general.getContainer(testPlayer).getAmount(0), "Buying all stock didn't... buy all stock.") + + testPlayer.inventory.replace(null, 1) //Remove the items we purchased + + val remainingGP = testPlayer.inventory.getAmount(995) + + Assertions.assertDoesNotThrow { + status = general.buy(testPlayer, 0, 10) + } + + Assertions.assertEquals(true, status is Shop.TransactionStatus.Failure, "Status was not Failure for buying 0-stock item!") + Assertions.assertEquals(remainingGP, testPlayer.inventory.getAmount(995), "Coins were deducted for buying 0-stock item!") + Assertions.assertNull(testPlayer.inventory.get(1), "Player received purchased item despite being 0-stock!") + } + + @Test fun buyingItemWithOtherwiseFullInventoryStillBuysTheItemAndTakesTheCoins() { + testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) + testPlayer.setAttribute("shop-main", false) + testPlayer.inventory.clear() + testPlayer.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 27)) //fill 27 slots + general.playerStock.add(Item(992, 1)) + var slot = general.getStockSlot(992).second + var price = general.getBuyPrice(testPlayer, slot) + + testPlayer.inventory.add(price) + + //Buy out existing stock + var status: Shop.TransactionStatus = Shop.TransactionStatus.Success() + + Assertions.assertDoesNotThrow { + status = general.buy(testPlayer, slot, 1) + } + Assertions.assertEquals(true, status is Shop.TransactionStatus.Success) + Assertions.assertEquals(0, testPlayer.inventory.getAmount(995)) + Assertions.assertEquals(1, testPlayer.inventory.getAmount(992)) + } +} diff --git a/Server/src/test/kotlin/TestUtils.kt b/Server/src/test/kotlin/TestUtils.kt new file mode 100644 index 000000000..b8d8d45cd --- /dev/null +++ b/Server/src/test/kotlin/TestUtils.kt @@ -0,0 +1,229 @@ +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.game.shops.Shop +import core.game.shops.ShopItem +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, 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) + p.details.rights = rights + return p + } + + fun getMockShop(name: String, general: Boolean, highAlch: Boolean, vararg stock: Item) : Shop { + return Shop( + name, + stock.map { ShopItem(it.id, it.amount, 100) }.toTypedArray(), + general, + highAlch = highAlch + ) + } + + fun getMockTokkulShop(name: String, vararg stock: Item) : Shop { + return Shop( + name, + stock.map { ShopItem(it.id, it.amount, 100) }.toTypedArray(), + currency = Items.TOKKUL_6529 + ) + } + + fun preTestSetup() { + if(ServerConstants.DATA_PATH == null) { + ServerConfigParser.parse(this::class.java.getResource("test.conf")) + 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) { + 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, 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) { + val receivedPackets = ArrayList() + var disconnected = false + + @Suppress("ArrayInDataClass") + data class Packet(val opcode: Int, val payload: ByteArray) + + override fun getRemoteAddress(): String? { + return "127.0.0.1" + } + + override fun write(context: Any, instant: Boolean) { + if(context is IoBuffer) { + receivedPackets.add(Packet(context.opcode(), context.array())) + } + } + + fun hasPacketReady(opcode: Int) : Boolean { + for(pkt in receivedPackets) if(pkt.opcode == opcode) return true + return false + } + + fun getPacketsWithOpcode(opcode: Int) : ArrayList { + val list = ArrayList() + for(pkt in receivedPackets) if(pkt.opcode == opcode) list.add(pkt.payload) + return list + } + + override fun getIsaacPair(): ISAACPair { + return ISAACPair(ISAACCipher(intArrayOf(0)), ISAACCipher(intArrayOf(0))) + } + + override fun queue(buffer: ByteBuffer?) {} + + override fun write() {} + + override fun disconnect() {disconnected = true} + + fun clear() { + receivedPackets.clear() + } + +} 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 new file mode 100644 index 000000000..e91c121c7 --- /dev/null +++ b/Server/src/test/kotlin/content/DeathTests.kt @@ -0,0 +1,342 @@ +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 +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items +import core.game.node.entity.combat.graves.GraveType +import core.game.node.entity.combat.graves.GraveController +import core.game.world.GameWorld +import core.tools.secondsToTicks +class DeathTests { + init { + //explicitly register the GraveController as a tick listener because tests don't run reflection + TestUtils.preTestSetup() // need cache parsed to properly evaluate item values + GameWorld.tickListeners.add(GraveController()) + } + //Grave requirements source: https://runescape.wiki/w/Gravestone?oldid=854455 + + @Test fun graveUtilsProduceGraveShouldProduceCorrectGrave() { + val type = GraveType.MEM_PLAQUE + val grave = GraveController.produceGrave(type) + + Assertions.assertEquals(type, grave.type) + } + + @Test fun graveInitializedWithItemsShouldInitializeCorrectly() { + val inventory = arrayOf( + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.YELLOW_BEAD_1472.asItem() + ) + val player = TestUtils.getMockPlayer("gravetest", IronmanMode.NONE, Rights.REGULAR_PLAYER) + + val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE) + grave.initialize(player, Location.create(0,0,0), inventory) + + for (item in grave.getItems()) { + Assertions.assertEquals(player, item.dropper) + Assertions.assertEquals(player.details.uid, item.dropperUid) + Assertions.assertEquals( + GameWorld.ticks + secondsToTicks(GraveType.MEM_PLAQUE.durationMinutes * 60), + item.decayTime + ) + Assertions.assertEquals(true, item.isRemainPrivate) + Assertions.assertEquals(true, item.id in inventory.map { it.id }.toIntArray()) + Assertions.assertEquals(true, grave.isActive) + } + } + + @Test fun graveInitializedWithNoItemsShouldNotSpawn() { + val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE) + val p = TestUtils.getMockPlayer("gravetest") + + grave.initialize(p, Location.create(0,0,0), arrayOf()) + Assertions.assertEquals(false, grave.isActive) + } + + @Test fun graveInitializedWithEctophialAndPouchesShouldNotKeepThem() { + val inventory = arrayOf( + Items.ECTOPHIAL_4251.asItem(), + Items.SMALL_POUCH_5509.asItem(), + Items.MEDIUM_POUCH_5510.asItem(), + Items.MEDIUM_POUCH_5511.asItem(), + Items.LARGE_POUCH_5512.asItem(), + Items.LARGE_POUCH_5513.asItem(), + Items.GIANT_POUCH_5514.asItem(), + Items.GIANT_POUCH_5515.asItem() + ) + val p = TestUtils.getMockPlayer("gravetest") + val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE) + grave.initialize(p, Location.create(0,0,0), inventory) + + Assertions.assertEquals(true, grave.getItems().isEmpty()) + Assertions.assertEquals(false, grave.isActive) + } + + @Test fun graveInitializedWithDroppableUntradablesShouldKeepThem() { + val inventory = arrayOf( + Items.FIRE_CAPE_6570.asItem(), + Items.RUNE_DEFENDER_8850.asItem() + ) + val p = TestUtils.getMockPlayer("gravetest") + val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE) + grave.initialize(p, Location.create(0,0,0), inventory) + + Assertions.assertEquals(2, grave.getItems().size) + Assertions.assertEquals(true, grave.isActive) + } + + @Test fun graveInitializedWithDestroyableItemsShouldNotKeepThem() { + val inventory = arrayOf( + Items.HOLY_GRAIL_19.asItem() + ) + val p = TestUtils.getMockPlayer("gravetest") + val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE) + grave.initialize(p, Location.create(0,0,0), inventory) + + Assertions.assertEquals(0, grave.getItems().size) + Assertions.assertEquals(false, grave.isActive) + } + + @Test fun graveInitializedWithReleasableItemsShouldNotKeepThem() { + val inventory = arrayOf( + Items.CHINCHOMPA_10033.asItem(), + Items.CHINCHOMPA_9976.asItem(), + Items.BABY_IMPLING_JAR_11238.asItem() + ) + val p = TestUtils.getMockPlayer("gravetest") + val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE) + grave.initialize(p, Location.create(0,0,0), inventory) + + Assertions.assertEquals(0, grave.getItems().size) + Assertions.assertEquals(false, grave.isActive) + } + + @Test fun graveInitializedWithItemThatHasDropTransformShouldContainTransformedItem() { + //We actually don't have any items that have this implemented yet, but we should test it once we do. + } + + @Test fun graveShouldSerializeAndDeserializeFromJsonCorrectly() { + val inventory = arrayOf( + Items.BRONZE_2H_SWORD_1307.asItem(), + Items.BRONZE_AXE_1351.asItem() + ) + + val startTime = GameWorld.ticks + val p = TestUtils.getMockPlayer("gravetest") + val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE) + grave.initialize(p, Location.create(0,0,0), inventory) + + TestUtils.advanceTicks(30, false) + 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() + + val newGrave = GraveController.activeGraves[p.details.uid] + Assertions.assertNotNull(newGrave) + Assertions.assertEquals(expectedTicksRemaining, newGrave?.ticksRemaining ?: -1) + Assertions.assertEquals(2, newGrave?.getItems()?.size ?: -1) + + val expectedItemDecayTick = GameWorld.ticks + expectedTicksRemaining + Assertions.assertEquals(expectedItemDecayTick, newGrave?.getItems()?.get(0)?.decayTime ?: -1) + 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(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), //none of these should be in the grave due to having higher value + Items.YELLOW_BEAD_1472.asItem(), + Items.RED_BEAD_1470.asItem() + ) + val p = TestUtils.getMockPlayer("gravetester", IronmanMode.NONE, Rights.REGULAR_PLAYER) + p.location = Location.create(0,0,0) + p.setAttribute("tutorial:complete", true) + + for (item in inventory) + p.inventory.add(item) + + p.finalizeDeath(null) + + val grave = GraveController.activeGraves[p.details.uid] + Assertions.assertNotNull(grave) + Assertions.assertEquals(2, grave?.getItems()?.size ?: -1) + Assertions.assertEquals(false, grave?.getItems()?.map { it.id }?.contains(Items.RUNE_SCIMITAR_1333)) + } + + @Test fun skulledDeathShouldNotSpawnGrave() { + val inventory = arrayOf( + Items.ABYSSAL_WHIP_4151.asItem(), + Items.ABYSSAL_WHIP_4151.asItem(), + Items.ABYSSAL_WHIP_4151.asItem(), + Items.ABYSSAL_WHIP_4151.asItem(), + Items.ABYSSAL_WHIP_4151.asItem() + ) + val p = TestUtils.getMockPlayer("gravetester", IronmanMode.NONE, Rights.REGULAR_PLAYER) + p.location = Location.create(0,0,0) + p.setAttribute("tutorial:complete", true) + + for (item in inventory) + p.inventory.add(item) + + p.skullManager.isSkulled = true + p.finalizeDeath(null) + + val grave = GraveController.activeGraves[p.details.uid] + Assertions.assertNull(grave) + } + + @Test fun creatingNewGraveWithGraveAlreadyActiveShouldDestroyOldGrave() { + val inventory1 = arrayOf( + Items.RUNE_SCIMITAR_1333.asItem() + ) + val grave1 = GraveController.produceGrave(GraveType.MEM_PLAQUE) + val p = TestUtils.getMockPlayer("gravetester") + grave1.initialize(p, Location.create(0,0,0), inventory1) + + Assertions.assertEquals(true, grave1.isActive) + Assertions.assertEquals(Items.RUNE_SCIMITAR_1333, GraveController.activeGraves[p.details.uid]?.getItems()?.getOrNull(0)?.id ?: -1) + + val inventory2 = arrayOf( + Items.ABYSSAL_WHIP_4151.asItem() + ) + val grave2 = GraveController.produceGrave(GraveType.MEM_PLAQUE) + grave2.initialize(p, Location.create(0,0,0), inventory2) + + Assertions.assertEquals(false, grave1.isActive) + Assertions.assertEquals(true, grave2.isActive) + Assertions.assertEquals(Items.ABYSSAL_WHIP_4151, GraveController.activeGraves[p.details.uid]?.getItems()?.getOrNull(0)?.id ?: -1) + } + + @Test fun deathWithOnly3ItemsShouldNotProduceAGrave() { + val inventory = arrayOf( + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem() + ) + val p = TestUtils.getMockPlayer("gtester", IronmanMode.NONE, Rights.REGULAR_PLAYER) + p.location = Location.create(0,0,0) + p.setAttribute("tutorial:complete", true) + + for (item in inventory) + p.inventory.add(item) + + p.finalizeDeath(null) + + Assertions.assertNull(GraveController.activeGraves[p.details.uid]) + } + + @Test fun deathInsideWildernessShouldNotProduceAGrave() { + val inventory = arrayOf( + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem() + ) + val p = TestUtils.getMockPlayer("tester3333", IronmanMode.NONE, Rights.REGULAR_PLAYER) + p.skullManager.isWilderness = true + + for (item in inventory) { + p.inventory.add(item) + } + + p.finalizeDeath(null) + + Assertions.assertNull(GraveController.activeGraves[p.details.uid]) + } + + @Test fun deathWithRFDGlovesShouldKeepRFDGloves() { + val inventory = arrayOf( + Items.GLOVES_7453.asItem(), + Items.GLOVES_7454.asItem(), + Items.GLOVES_7455.asItem(), + Items.GLOVES_7456.asItem(), + Items.GLOVES_7457.asItem(), + Items.GLOVES_7458.asItem(), + Items.GLOVES_7459.asItem(), + Items.GLOVES_7460.asItem(), + Items.GLOVES_7461.asItem(), + Items.GLOVES_7462.asItem() + ) + val p = TestUtils.getMockPlayer("glovetest", IronmanMode.NONE, Rights.REGULAR_PLAYER) + + for(item in inventory) + p.inventory.add(item) + + p.finalizeDeath(null) + + val g = GraveController.activeGraves[p.details.uid] + Assertions.assertNotNull(g) + Assertions.assertEquals(7, g?.getItems()?.size ?: -1) + } + + @Test fun shouldNotBeAbleToDropItemOnGrave() { + val inventory = arrayOf( + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem(), + Items.RUNE_SCIMITAR_1333.asItem() + ) + val p = TestUtils.getMockPlayer("droptest", IronmanMode.NONE, Rights.REGULAR_PLAYER) + + for (item in inventory) + p.inventory.add(item) + + p.finalizeDeath(null) + p.inventory.add(Items.RUNE_SCIMITAR_1333.asItem()) + + val g = GraveController.activeGraves[p.details.uid] + + Assertions.assertNotNull(g) + Assertions.assertEquals(p.location, g?.location) + + 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 new file mode 100644 index 000000000..96a903a8a --- /dev/null +++ b/Server/src/test/kotlin/content/EquipTests.kt @@ -0,0 +1,169 @@ +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 +import org.junit.jupiter.api.Test +import org.rs09.consts.Items +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 { + init {TestUtils.preTestSetup(); EquipHandler().defineListeners()} + } + + @Test fun equipShouldFireEquipListeners() { + var didRun = false + val listener = object : InteractionListener { + override fun defineListeners() { + onEquip(4151) {_,_ -> didRun = true; return@onEquip true} + } + } + listener.defineListeners() + + val p = TestUtils.getMockPlayer("bill") + p.inventory.add(Item(4151)) + InteractionListeners.run(4151, IntType.ITEM, "equip", p, p.inventory[0]) + + Assertions.assertEquals(true, didRun) + } + + @Test fun unequipShouldFireUnequipListeners() { + var didRun = false + val listener = object : InteractionListener { + override fun defineListeners() { + onUnequip(4151) {_,_ -> didRun = true; return@onUnequip true} + } + } + listener.defineListeners() + + val p = TestUtils.getMockPlayer("bill") + p.equipment.replace(Item(4151), EquipmentSlot.WEAPON.ordinal) + EquipHandler.unequip(p, EquipmentSlot.WEAPON.ordinal, 4151) + + Assertions.assertEquals(true, didRun) + } + + @Test fun equippingItemThatReplacesAnotherItemShouldCallUnequipListenersForTheReplacedItem() { + var didRun = false + val listener = object : InteractionListener { + override fun defineListeners() { + onUnequip(4151) {_,_ -> didRun = true; return@onUnequip true} + } + } + listener.defineListeners() + + val p = TestUtils.getMockPlayer("bill") + p.equipment.replace(Item(4151), EquipmentSlot.WEAPON.ordinal) + p.inventory.add(Item(1333)) + p.skills.staticLevels[Skills.ATTACK] = 40 + InteractionListeners.run(1333, IntType.ITEM, "equip", p, p.inventory[0]) + + Assertions.assertEquals(true, didRun, p.equipment.toString()) + } + + @Test fun equippingItemShouldAddUnequippedItemToExistingStackInInventory() { + val p = TestUtils.getMockPlayer("bill") + p.skills.staticLevels[Skills.ATTACK] = 99 + p.equipment.replace(Item(Items.BRONZE_DART_806, 1000), EquipmentSlot.WEAPON.ordinal) + p.inventory.add(Item(Items.BRONZE_DART_806, 1000)) + p.inventory.add(Item(Items.RUNE_SCIMITAR_1333)) + + InteractionListeners.run(Items.RUNE_SCIMITAR_1333, IntType.ITEM, "equip", p, p.inventory[1]) + Assertions.assertEquals(2000, p.inventory.getAmount(Items.BRONZE_DART_806), "\n" + p.inventory.toString() + "\n" + p.equipment.toString()) + } + + @Test fun equippingItemThatUnequipsTwoItemsShouldBeAllowedWithOnlyOneInitiallyFreeSlot() { + val p = TestUtils.getMockPlayer("bill") + p.equipment.replace(Item(Items.BRONZE_SWORD_1277), EquipmentSlot.WEAPON.ordinal) + p.equipment.replace(Item(Items.WOODEN_SHIELD_1171), EquipmentSlot.SHIELD.ordinal) + + p.inventory.add(Item(Items.CUP_OF_TEA_1978, 26)) + p.inventory.add(Item(Items.BRONZE_2H_SWORD_1307)) + + Assertions.assertEquals(1, p.inventory.freeSlots()) + + InteractionListeners.run(Items.BRONZE_2H_SWORD_1307, IntType.ITEM, "equip", p, p.inventory[26]) + Assertions.assertEquals(0, p.inventory.freeSlots()) + Assertions.assertEquals(Items.BRONZE_SWORD_1277, p.inventory[26].id) + Assertions.assertEquals(Items.WOODEN_SHIELD_1171, p.inventory[27].id) + } + + @Test fun shouldNotBeAbleToEquipA2HWeaponAndAShieldAtTheSameTime() { + val p = TestUtils.getMockPlayer("bill") + p.inventory.add(Item(Items.BRONZE_2H_SWORD_1307)) + p.inventory.add(Item(Items.WOODEN_SHIELD_1171)) + + InteractionListeners.run(Items.BRONZE_2H_SWORD_1307, IntType.ITEM, "equip", p, p.inventory[0]) + Assertions.assertEquals(Items.BRONZE_2H_SWORD_1307, p.equipment.get(EquipmentSlot.WEAPON.ordinal).id) + + InteractionListeners.run(Items.WOODEN_SHIELD_1171, IntType.ITEM, "equip", p, p.inventory[1]) + Assertions.assertEquals(Items.BRONZE_2H_SWORD_1307, p.inventory[1]?.id ?: -1) + } + + @Test fun equippingShieldShouldNotUnequipOneHandedWeapon() { + val p = TestUtils.getMockPlayer("bill") + p.inventory.add(Item(Items.BRONZE_SWORD_1277)) + p.inventory.add(Item(Items.WOODEN_SHIELD_1171)) + + InteractionListeners.run(Items.BRONZE_SWORD_1277, IntType.ITEM, "equip", p, p.inventory[0]) + Assertions.assertEquals(Items.BRONZE_SWORD_1277, p.equipment[EquipmentSlot.WEAPON.ordinal].id) + + InteractionListeners.run(Items.WOODEN_SHIELD_1171, IntType.ITEM, "equip", p, p.inventory[1]) + Assertions.assertEquals(Items.BRONZE_SWORD_1277, p.equipment[EquipmentSlot.WEAPON.ordinal]?.id ?: -1) + } + + @Test fun equippingStackableItemShouldAddToExistingStackInEquipmentIfApplicable() { + val p = TestUtils.getMockPlayer("bill") + p.equipment.replace(Item(Items.BRONZE_ARROW_882, 100), EquipmentSlot.AMMO.ordinal) + p.inventory.add(Item(Items.BRONZE_ARROW_882, 200)) + + InteractionListeners.run(Items.BRONZE_ARROW_882, IntType.ITEM, "equip", p, p.inventory[0]) + Assertions.assertEquals(300, p.equipment[EquipmentSlot.AMMO.ordinal].amount) + } + + @Test fun swappingEquipmentShouldPreserveInventorySlots() { + val p = TestUtils.getMockPlayer("bill") + p.skills.staticLevels[Skills.ATTACK] = 70 + p.skills.staticLevels[Skills.DEFENCE] = 40 + p.equipment.replace(Item(Items.ABYSSAL_WHIP_4151), EquipmentSlot.WEAPON.ordinal) + p.equipment.replace(Item(Items.RUNE_DEFENDER_8850), EquipmentSlot.SHIELD.ordinal) + p.inventory.add(Item(Items.DRAGON_CLAWS_14484), true, 25) + Assertions.assertEquals(p.inventory.getSlot(Item(Items.DRAGON_CLAWS_14484)), 25) + + InteractionListeners.run(Items.DRAGON_CLAWS_14484, IntType.ITEM, "equip", p, p.inventory[25]) + Assertions.assertEquals(Items.DRAGON_CLAWS_14484, p.equipment.get(EquipmentSlot.WEAPON.ordinal).id) + 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 new file mode 100644 index 000000000..b6316eda5 --- /dev/null +++ b/Server/src/test/kotlin/content/ListenerTests.kt @@ -0,0 +1,47 @@ +package content + +import TestUtils +import core.game.node.item.Item +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import core.game.interaction.InteractionListener +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) { + on(0, IntType.ITEM, "touch") { _, _ -> return@on true} + } + } + + @Test fun doubleDefinedUseWithShouldThrowIllegalStateException() { + onUseWith(IntType.SCENERY, 0, 1) { _, _, _ -> return@onUseWith true} + Assertions.assertThrows(IllegalStateException::class.java) { + onUseWith(IntType.SCENERY, 0, 1) { _, _, _ -> return@onUseWith true} + } + } + + @Test fun conflictingCatchallShouldThrowIllegalStateException() { + on(IntType.ITEM, "boop"){ _, _ -> return@on true} + Assertions.assertThrows(IllegalStateException::class.java) { + on(IntType.ITEM, "boop") { _, _ -> return@on true} + } + } + + @Test fun specificListenerShouldOverrideCatchallListener() { + var specificRan = false + var catchAllRan = false + on(IntType.ITEM, "zap") { _, _ -> catchAllRan = true; return@on true} + on(1, IntType.ITEM, "zap") { _, _ -> specificRan = true; return@on true} + + InteractionListeners.run(1, IntType.ITEM, "zap", TestUtils.getMockPlayer("bilbots"), Item(1)) + + Assertions.assertEquals(false, catchAllRan) + Assertions.assertEquals(true, specificRan) + } + + override fun defineListeners() {} +} \ 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 new file mode 100644 index 000000000..84f6c640c --- /dev/null +++ b/Server/src/test/kotlin/core/PathfinderTests.kt @@ -0,0 +1,260 @@ +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.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(); WoodcuttingListener().defineListeners() }; val NPC_TEST_LOC = ServerConstants.HOME_LOCATION!!.transform(2, 10, 0)} + + @Test fun getOccupiedTilesShouldReturnCorrectSetOfTilesThatAnObjectOccupiesAtAllRotations() { + //clay fireplace - 13609 - sizex: 1, sizey: 2 + val scenery = Scenery(13609, Location.create(50, 50, 0)) + + scenery.rotation = 0 + val occupiedAt0 = scenery.occupiedTiles.toTypedArray() + Assertions.assertArrayEquals(arrayOf(Location.create(50, 50), Location.create(50,51)), occupiedAt0) + + scenery.rotation = 1 + val occupiedAt1 = scenery.occupiedTiles.toTypedArray() + Assertions.assertArrayEquals(arrayOf(Location.create(50,50), Location.create(51,50)), occupiedAt1) + + scenery.rotation = 2 + val occupiedAt2 = scenery.occupiedTiles.toTypedArray() + Assertions.assertArrayEquals(arrayOf(Location.create(50,50), Location.create(50,49)), occupiedAt2) + + scenery.rotation = 3 + val occupiedAt3 = scenery.occupiedTiles.toTypedArray() + Assertions.assertArrayEquals(arrayOf(Location.create(50,50), Location.create(49,50)), occupiedAt3) + } + + @Test fun movementPulseShouldStopEarlyIfNextToATileOccupiedByTargetObject() { + val start = Location.create(2731, 3481) + val dest = RegionManager.getObject(0, 2720, 3475, 1307) + val p = TestUtils.getMockPlayer("treefindtest") + p.location = start + p.init() + + Assertions.assertEquals(true, InteractionListeners.run(1307, IntType.SCENERY, "chop-down", p, dest!!)) + 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 new file mode 100644 index 000000000..e14efb11e --- /dev/null +++ b/Server/src/test/kotlin/core/auth/DevelopmentAuthenticatorTests.kt @@ -0,0 +1,52 @@ +package core.auth + +import core.storage.InMemoryStorageProvider +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class DevelopmentAuthenticatorTests { + private val authProvider = DevelopmentAuthenticator() + private val storageProvider = InMemoryStorageProvider() + + init { + authProvider.configureFor(storageProvider) + } + + @Test fun shouldAllowCheckingIfAccountExists() { + val info = UserAccountInfo.createDefault() + info.username = "Billy" + Assertions.assertEquals(true, authProvider.canCreateAccountWith(info)) + authProvider.createAccountWith(info) + Assertions.assertEquals(false, authProvider.canCreateAccountWith(info)) + } + + @Test fun loginWithValidAccountInfoReturnsSuccess() { + val info = UserAccountInfo.createDefault() + info.username = "Billy" + authProvider.createAccountWith(info) + Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("Billy", "").first) + } + + //Development authenticator should work regardless if account exists or not. + @Test fun loginWithInvalidAccountInfoReturnsSuccess() { + val info = UserAccountInfo.createDefault() + info.username = "Billy" + authProvider.createAccountWith(info) + Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("Bilbo", "ebbeb").first) + } + + @Test fun loginUsernameIsNotCaseSensitive() { + val info = UserAccountInfo.createDefault() + info.username = "Billy" + authProvider.createAccountWith(info) + Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("Billy", "").first) + Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("billy", "").first) + } + + //Development authenticator should basically bypass needing/creating an account entirely. useful for SP too. + @Test fun loginToUnregisteredAccountCreatesIt() { + authProvider.checkLogin("masterbaggins", "whatever") + val info = storageProvider.getAccountInfo("masterbaggins") + Assertions.assertEquals(2, info.rights) + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/core/auth/ProductionAuthenticatorTests.kt b/Server/src/test/kotlin/core/auth/ProductionAuthenticatorTests.kt new file mode 100644 index 000000000..1355778bb --- /dev/null +++ b/Server/src/test/kotlin/core/auth/ProductionAuthenticatorTests.kt @@ -0,0 +1,62 @@ +package core.auth + +import org.junit.Assert +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import core.auth.AuthResponse +import core.auth.ProductionAuthenticator +import core.auth.UserAccountInfo +import core.storage.InMemoryStorageProvider + +class ProductionAuthenticatorTests { + companion object { + private val authProvider = ProductionAuthenticator() + private val storageProvider = InMemoryStorageProvider() + + init { + authProvider.configureFor(storageProvider) + } + + @BeforeAll @JvmStatic fun createTestAccount() { + val details = UserAccountInfo.createDefault() + details.username = "test" + details.password = "testing" + if(!storageProvider.checkUsernameTaken("test")) { + authProvider.createAccountWith(details) + } + } + } + + @Test fun shouldRejectLoginWithInvalidDetails() { + Assertions.assertEquals(AuthResponse.InvalidCredentials, authProvider.checkLogin("test", "test2").first) + } + + @Test fun loginUsernameIsNotCaseSensitive() { + Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("Test", "testing").first) + Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("test", "testing").first) + } + + @Test fun shouldHashPasswords() { + Assertions.assertNotEquals("testing", storageProvider.getAccountInfo("test").password) + } + + @Test fun shouldNotAllowBannedLogin() { + val info = storageProvider.getAccountInfo("test") + info.banEndTime = System.currentTimeMillis() + 1000L + storageProvider.update(info) + Assertions.assertEquals(AuthResponse.AccountDisabled, authProvider.checkLogin("test", "testing").first) + info.banEndTime = 0L + storageProvider.update(info) + Assertions.assertEquals(AuthResponse.Success, authProvider.checkLogin("test", "testing").first) + } + + @Test fun shouldNotAllowAlreadyOnlineLogin() { + val info = storageProvider.getAccountInfo("test") + info.online = true + storageProvider.update(info) + Assertions.assertEquals(AuthResponse.AlreadyOnline, authProvider.checkLogin("test", "testing").first) + info.online = false + storageProvider.update(info) + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/core/cache/ItemDefTests.kt b/Server/src/test/kotlin/core/cache/ItemDefTests.kt new file mode 100644 index 000000000..dc9a5fb43 --- /dev/null +++ b/Server/src/test/kotlin/core/cache/ItemDefTests.kt @@ -0,0 +1,27 @@ +package core.cache + +import TestUtils +import core.api.itemDefinition +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.io.File + +class ItemDefTests { + val knownGood = HashMap() + init { + TestUtils.preTestSetup() + } + + @Test fun itemDefsShouldHaveExpectedValues() { + File(TestUtils.loadFile("530_cache_item_values_from_client.csv")).useLines { lines -> + lines.forEach { + val toks = it.split(",") + knownGood[toks[0].toIntOrNull() ?: return@forEach] = toks[toks.size - 1].toInt() + } + } + for ((id, expectedValue) in knownGood) { + val def = itemDefinition(id) + Assertions.assertEquals(expectedValue, def.value, "Value of ${def.name} (${def.isUnnoted}) does not match known good values!") + } + } +} \ No newline at end of file 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/net/LoginTests.kt b/Server/src/test/kotlin/core/net/LoginTests.kt new file mode 100644 index 000000000..602167773 --- /dev/null +++ b/Server/src/test/kotlin/core/net/LoginTests.kt @@ -0,0 +1,40 @@ +package core.net + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import core.auth.AuthResponse +import core.net.packet.`in`.Login +import java.nio.ByteBuffer + +class LoginTests { + @Test fun shouldDecodeCorrectlyEncodedPacket() { + val localCopy = validLoginPacket.copyOf(validLoginPacket.size) + val loginInfo = Login.decodeFromBuffer(ByteBuffer.wrap(localCopy)) + Assertions.assertEquals(AuthResponse.Success, loginInfo.first, "Info: ${loginInfo.second}") + } + + @Test fun shouldNeverThrowExceptionButReturnUnexpectedErrorResponseInstead() { + val localCopy = validLoginPacket.copyOf(validLoginPacket.size) + for(i in 15 until validLoginPacket.size) { + localCopy[i] = 0 //corrupt some data on purpose + } + var response: AuthResponse = AuthResponse.Updating + Assertions.assertDoesNotThrow { + val (res, _) = Login.decodeFromBuffer(ByteBuffer.wrap(localCopy)) + response = res + } + + Assertions.assertEquals(AuthResponse.UnexpectedError, response) + } + + @Test fun loginPacketWithInvalidOpcodeShouldReturnAHelpfulResponse() { + val localCopy = validLoginPacket.copyOf(validLoginPacket.size) + localCopy[0] = 2 //set to invalid login opcode + val (response, _) = Login.decodeFromBuffer(ByteBuffer.wrap(localCopy)) + Assertions.assertEquals(AuthResponse.InvalidLoginServer, response) + } + + companion object { + val validLoginPacket = byteArrayOf(16,1,80,0,0,2,18,0,1,1,2,2,-3,1,-9,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,107,75,109,111,107,51,107,74,113,79,101,78,54,68,51,109,68,100,105,104,99,111,51,111,80,101,89,78,50,75,70,121,54,87,53,45,45,118,90,85,98,78,65,0,0,0,0,0,1,81,-73,-29,0,0,-47,-49,34,92,-124,110,-97,46,-33,-18,122,8,76,93,-82,16,53,23,-113,-73,101,126,-93,125,-65,115,-59,19,90,-54,-102,-76,-99,-5,-68,-51,95,-20,-27,10,60,60,108,5,76,9,-63,97,106,74,116,0,58,0,75,-111,-128,-34,12,64,47,-92,-33,-120,-109,-7,23,124,122,40,-107,56,-34,-93,64,-82,58,-90,7,127,-15,-85,125,43,15,0,112,60,4,75,72,55,18,83,-119,-39,32,-113,21,104,-49,66,-102,104,-13,32,117,-106,94,-30,37,-56,-67,21,77,70,-128,68,112,-78,-49,-58,-48,74,70,31,12,64,84,124,2,-61,71,104,55,73,62,-27,111,122,-56,52,93,1,-52,32,102,73,-21,-46,-79,45,58,88,-36,-113,31,78,77,-56,-126,-103,-33,21,110,47,-81,-70,-15,14,114,51,-41,-49,6,-112,-28,-28,-112,113,120,68,29,-123,-108,24,-58,6,113,54,55,21,-58,32,99,-120,13,8,-94,-83,121,123,20,16,-89,43,-51,-20,74,-114,-126,-86,-12,38,-107,12,25,-40,-79,-116,-18,-85,-83,95,-106,-81,-64,31,110,-65,-128,-74,70,80,-20,11,117,28,39,-102,75,67,98,-121,-28) + } +} \ No newline at end of file diff --git a/Server/src/test/resources/530_cache_item_values_from_client.csv b/Server/src/test/resources/530_cache_item_values_from_client.csv new file mode 100644 index 000000000..727bec9d8 --- /dev/null +++ b/Server/src/test/resources/530_cache_item_values_from_client.csv @@ -0,0 +1,14658 @@ +id,name,cost +0,Dwarf remains,1 +1,Toolkit,1 +2,Cannonball,5 +3,Nulodion's notes,1 +4,Ammo mould,5 +5,Instruction manual,10 +6,Cannon base,187500 +7,Cannon base,187500 +8,Cannon stand,187500 +9,Cannon stand,187500 +10,Cannon barrels,187500 +11,Cannon barrels,187500 +12,Cannon furnace,187500 +13,Cannon furnace,187500 +14,Railing,1 +15,Holy table napkin,10 +16,Magic whistle,10 +17,Grail bell,10 +18,Magic gold feather,2 +19,Holy grail,3 +20,White cog,1 +21,Black cog,1 +22,Blue cog,1 +23,Red cog,1 +24,Rat poison,1 +25,Red vine worm,1 +26,Fishing trophy,1 +27,Fishing pass,1 +28,Insect repellent,3 +29,Tinderbox,0 +30,Bucket of wax,6 +31,Tinderbox,0 +32,Lit black candle,3 +33,Lit candle,3 +34,Lit candle,3 +35,Excalibur,200 +36,Candle,3 +37,Candle,3 +38,Black candle,3 +39,Bronze arrowtips,10 +40,Iron arrowtips,26 +41,Steel arrowtips,36 +42,Mithril arrowtips,68 +43,Adamant arrowtips,160 +44,Rune arrowtips,460 +45,Opal bolt tips,7 +46,Pearl bolt tips,12 +47,Barb bolttips,95 +48,Longbow (u),60 +49,Longbow (u),60 +50,Shortbow (u),23 +51,Shortbow (u),23 +52,Arrow shaft,12 +53,Headless arrow,1 +54,Oak shortbow (u),50 +55,Oak shortbow (u),50 +56,Oak longbow (u),80 +57,Oak longbow (u),80 +58,Willow longbow (u),160 +59,Willow longbow (u),160 +60,Willow shortbow (u),100 +61,Willow shortbow (u),100 +62,Maple longbow (u),320 +63,Maple longbow (u),320 +64,Maple shortbow (u),200 +65,Maple shortbow (u),200 +66,Yew longbow (u),640 +67,Yew longbow (u),640 +68,Yew shortbow (u),400 +69,Yew shortbow (u),400 +70,Magic longbow (u),1280 +71,Magic longbow (u),1280 +72,Magic shortbow (u),800 +73,Magic shortbow (u),800 +74,Khazard helmet,10 +75,Khazard armour,12 +76,Khazard cell keys,1 +77,Khali brew,5 +78,Ice arrows,2 +79,null,1 +80,null,1 +81,null,1 +82,null,1 +83,Lever,15 +84,Staff of armadyl,15 +85,Shiny key,1 +86,Pendant of lucien,12 +87,Armadyl pendant,12 +88,Boots of lightness,6 +89,Boots of lightness,6 +90,Child's blanket,5 +91,Guam potion(unf),3 +92,Guam potion(unf),3 +93,Marrentill potion(unf),5 +94,Marrentill potion(unf),5 +95,Tarromin potion(unf),11 +96,Tarromin potion(unf),11 +97,Harralander potion(unf),20 +98,Harralander potion(unf),20 +99,Ranarr potion(unf),25 +100,Ranarr potion(unf),25 +101,Irit potion(unf),40 +102,Irit potion(unf),40 +103,Avantoe potion(unf),48 +104,Avantoe potion(unf),48 +105,Kwuarm potion(unf),54 +106,Kwuarm potion(unf),54 +107,Cadantine potion(unf),65 +108,Cadantine potion(unf),65 +109,Dwarf weed potion(unf),70 +110,Dwarf weed potion(unf),70 +111,Torstol potion(unf),25 +112,Torstol potion(unf),25 +113,Strength potion(4),1500 +114,Strength potion(4),1500 +115,Strength potion(3),1200 +116,Strength potion(3),1200 +117,Strength potion(2),900 +118,Strength potion(2),900 +119,Strength potion(1),600 +120,Strength potion(1),600 +121,Attack potion(3),560 +122,Attack potion(3),560 +123,Attack potion(2),420 +124,Attack potion(2),420 +125,Attack potion(1),280 +126,Attack potion(1),280 +127,Restore potion(3),88 +128,Restore potion(3),88 +129,Restore potion(2),66 +130,Restore potion(2),66 +131,Restore potion(1),44 +132,Restore potion(1),44 +133,Defence potion(3),880 +134,Defence potion(3),880 +135,Defence potion(2),660 +136,Defence potion(2),660 +137,Defence potion(1),440 +138,Defence potion(1),440 +139,Prayer potion(3),152 +140,Prayer potion(3),152 +141,Prayer potion(2),114 +142,Prayer potion(2),114 +143,Prayer potion(1),76 +144,Prayer potion(1),76 +145,Super attack(3),180 +146,Super attack(3),180 +147,Super attack(2),135 +148,Super attack(2),135 +149,Super attack(1),90 +150,Super attack(1),90 +151,Fishing potion(3),200 +152,Fishing potion(3),200 +153,Fishing potion(2),150 +154,Fishing potion(2),150 +155,Fishing potion(1),100 +156,Fishing potion(1),100 +157,Super strength(3),220 +158,Super strength(3),220 +159,Super strength(2),165 +160,Super strength(2),165 +161,Super strength(1),110 +162,Super strength(1),110 +163,Super defence(3),264 +164,Super defence(3),264 +165,Super defence(2),198 +166,Super defence(2),198 +167,Super defence(1),132 +168,Super defence(1),132 +169,Ranging potion(3),288 +170,Ranging potion(3),288 +171,Ranging potion(2),216 +172,Ranging potion(2),216 +173,Ranging potion(1),144 +174,Ranging potion(1),144 +175,Antipoison(3),288 +176,Antipoison(3),288 +177,Antipoison(2),216 +178,Antipoison(2),216 +179,Antipoison(1),144 +180,Antipoison(1),144 +181,Super antipoison(3),288 +182,Super antipoison(3),288 +183,Super antipoison(2),216 +184,Super antipoison(2),216 +185,Super antipoison(1),144 +186,Super antipoison(1),144 +187,Weapon poison,144 +188,Weapon poison,144 +189,Zamorak brew(3),175 +190,Zamorak brew(3),175 +191,Zamorak brew(2),150 +192,Zamorak brew(2),150 +193,Zamorak brew(1),125 +194,Zamorak brew(1),125 +195,Potion,1 +196,Potion,1 +197,Poison chalice,20 +198,Poison chalice,20 +199,Grimy guam,1 +200,Grimy guam,1 +201,Grimy marrentill,1 +202,Grimy marrentill,1 +203,Grimy tarromin,1 +204,Grimy tarromin,1 +205,Grimy harralander,1 +206,Grimy harralander,1 +207,Grimy ranarr,1 +208,Grimy ranarr,1 +209,Grimy irit,1 +210,Grimy irit,1 +211,Grimy avantoe,1 +212,Grimy avantoe,1 +213,Grimy kwuarm,1 +214,Grimy kwuarm,1 +215,Grimy cadantine,1 +216,Grimy cadantine,1 +217,Grimy dwarf weed,1 +218,Grimy dwarf weed,1 +219,Grimy torstol,1 +220,Grimy torstol,1 +221,Eye of newt,3 +222,Eye of newt,3 +223,Red spiders' eggs,7 +224,Red spiders' eggs,7 +225,Limpwurt root,7 +226,Limpwurt root,7 +227,Vial of water,10 +228,Vial of water,10 +229,Vial,5 +230,Vial,5 +231,Snape grass,10 +232,Snape grass,10 +233,Pestle and mortar,4 +234,Pestle and mortar,4 +235,Unicorn horn dust,20 +236,Unicorn horn dust,20 +237,Unicorn horn,20 +238,Unicorn horn,20 +239,White berries,10 +240,White berries,10 +241,Dragon scale dust,52 +242,Dragon scale dust,52 +243,Blue dragon scale,50 +244,Blue dragon scale,50 +245,Wine of zamorak,1 +246,Wine of zamorak,1 +247,Jangerberries,1 +248,Jangerberries,1 +249,Clean guam,3 +250,Clean guam,3 +251,Clean marrentill,5 +252,Clean marrentill,5 +253,Clean tarromin,11 +254,Clean tarromin,11 +255,Clean harralander,20 +256,Clean harralander,20 +257,Clean ranarr,25 +258,Clean ranarr,25 +259,Clean irit,40 +260,Clean irit,40 +261,Clean avantoe,48 +262,Clean avantoe,48 +263,Clean kwuarm,54 +264,Clean kwuarm,54 +265,Clean cadantine,65 +266,Clean cadantine,65 +267,Clean dwarf weed,70 +268,Clean dwarf weed,70 +269,Clean torstol,75 +270,Clean torstol,75 +271,Pressure gauge,1 +272,Fish food,1 +273,Poison,1 +274,Poisoned fish food,1 +275,Key,1 +276,Rubber tube,3 +277,Oil can,3 +278,Cattleprod,3 +279,Sheep feed,3 +280,Sheep bones (1),1 +281,Sheep bones (2),1 +282,Sheep bones (3),1 +283,Sheep bones (4),1 +284,Plague jacket,50 +285,Plague trousers,50 +286,Orange goblin mail,40 +287,Blue goblin mail,40 +288,Goblin mail,40 +289,Goblin mail,40 +290,Research package,2 +291,Notes,5 +292,Book on baxtorian,2 +293,A key,1 +294,Glarial's pebble,1 +295,Glarial's amulet,1 +296,Glarial's urn,1 +297,Glarial's urn,1 +298,A key,1 +299,Mithril seeds,300 +300,Rat's tail,3 +301,Lobster pot,20 +302,Lobster pot,20 +303,Small fishing net,5 +304,Small fishing net,5 +305,Big fishing net,20 +306,Big fishing net,20 +307,Fishing rod,5 +308,Fishing rod,5 +309,Fly fishing rod,5 +310,Fly fishing rod,5 +311,Harpoon,5 +312,Harpoon,5 +313,Fishing bait,3 +314,Feather,6 +315,Shrimps,5 +316,Shrimps,5 +317,Raw shrimps,6 +318,Raw shrimps,6 +319,Anchovies,15 +320,Anchovies,15 +321,Raw anchovies,16 +322,Raw anchovies,16 +323,Burnt fish,1 +324,Burnt fish,1 +325,Sardine,10 +326,Sardine,10 +327,Raw sardine,10 +328,Raw sardine,10 +329,Salmon,88 +330,Salmon,88 +331,Raw salmon,92 +332,Raw salmon,92 +333,Trout,68 +334,Trout,68 +335,Raw trout,20 +336,Raw trout,20 +337,Giant carp,50 +338,Raw giant carp,50 +339,Cod,26 +340,Cod,26 +341,Raw cod,34 +342,Raw cod,34 +343,Burnt fish,1 +344,Burnt fish,1 +345,Raw herring,16 +346,Raw herring,16 +347,Herring,15 +348,Herring,15 +349,Raw pike,25 +350,Raw pike,25 +351,Pike,25 +352,Pike,25 +353,Raw mackerel,26 +354,Raw mackerel,26 +355,Mackerel,17 +356,Mackerel,17 +357,Burnt fish,1 +358,Burnt fish,1 +359,Raw tuna,120 +360,Raw tuna,120 +361,Tuna,120 +362,Tuna,120 +363,Raw bass,280 +364,Raw bass,280 +365,Bass,270 +366,Bass,270 +367,Burnt fish,1 +368,Burnt fish,1 +369,Burnt fish,1 +370,Burnt fish,1 +371,Raw swordfish,340 +372,Raw swordfish,340 +373,Swordfish,400 +374,Swordfish,400 +375,Burnt swordfish,1 +376,Burnt swordfish,1 +377,Raw lobster,284 +378,Raw lobster,284 +379,Lobster,268 +380,Lobster,268 +381,Burnt lobster,1 +382,Burnt lobster,1 +383,Raw shark,300 +384,Raw shark,300 +385,Shark,300 +386,Shark,300 +387,Burnt shark,1 +388,Burnt shark,1 +389,Raw manta ray,500 +390,Raw manta ray,500 +391,Manta ray,500 +392,Manta ray,500 +393,Burnt manta ray,1 +394,Burnt manta ray,1 +395,Raw sea turtle,500 +396,Raw sea turtle,500 +397,Sea turtle,500 +398,Sea turtle,500 +399,Burnt sea turtle,1 +400,Burnt sea turtle,1 +401,Seaweed,2 +402,Seaweed,2 +403,Edible seaweed,2 +404,Edible seaweed,2 +405,Casket,50 +406,Casket,50 +407,Oyster,200 +408,Oyster,200 +409,Empty oyster,5 +410,Empty oyster,5 +411,Oyster pearl,112 +412,Oyster pearl,112 +413,Oyster pearls,1400 +414,Oyster pearls,1400 +415,Ethenea,10 +416,Liquid honey,0 +417,Sulphuric broline,1 +418,Plague sample,1 +419,Touch paper,1 +420,Distillator,1 +421,Lathas' amulet,10 +422,Bird feed,1 +423,Key,1 +424,Pigeon cage,1 +425,Pigeon cage,1 +426,Priest gown,5 +427,Priest gown,5 +428,Priest gown,5 +429,Priest gown,5 +430,Doctors' gown,40 +431,Karamjan rum,30 +432,Chest key,1 +433,Pirate message,1 +434,Clay,1 +435,Clay,1 +436,Copper ore,20 +437,Copper ore,20 +438,Tin ore,20 +439,Tin ore,20 +440,Iron ore,17 +441,Iron ore,17 +442,Silver ore,75 +443,Silver ore,75 +444,Gold ore,150 +445,Gold ore,150 +446,'perfect' gold ore,150 +447,Mithril ore,162 +448,Mithril ore,162 +449,Adamantite ore,400 +450,Adamantite ore,400 +451,Runite ore,3200 +452,Runite ore,3200 +453,Coal,45 +454,Coal,45 +455,Barcrawl card,10 +456,Scorpion cage,10 +457,Scorpion cage,10 +458,Scorpion cage,10 +459,Scorpion cage,10 +460,Scorpion cage,10 +461,Scorpion cage,10 +462,Scorpion cage,10 +463,Scorpion cage,10 +464,Strange fruit,1 +465,Strange fruit,1 +466,Pickaxe handle,1 +467,Pickaxe handle,1 +468,Broken pickaxe,1 +469,Broken pickaxe,1 +470,Broken pickaxe,1 +471,Broken pickaxe,1 +472,Broken pickaxe,17 +473,Broken pickaxe,17 +474,Broken pickaxe,43 +475,Broken pickaxe,43 +476,Broken pickaxe,107 +477,Broken pickaxe,107 +478,Broken pickaxe,1100 +479,Broken pickaxe,1100 +480,Bronze pick head,1 +481,Bronze pick head,1 +482,Iron pick head,139 +483,Iron pick head,139 +484,Steel pick head,499 +485,Steel pick head,499 +486,Mithril pick head,1299 +487,Mithril pick head,1299 +488,Adamant pick head,3199 +489,Adamant pick head,3199 +490,Rune pick head,31999 +491,Rune pick head,31999 +492,Picture,1 +493,Picture,1 +494,Broken axe,0 +495,Broken axe,0 +496,Broken axe,0 +497,Broken axe,0 +498,Broken axe,0 +499,Broken axe,0 +500,Broken axe,10 +501,Broken axe,10 +502,Broken axe,18 +503,Broken axe,18 +504,Broken axe,43 +505,Broken axe,43 +506,Broken axe,427 +507,Broken axe,427 +508,Picture,15 +509,Picture,15 +510,Picture,55 +511,Picture,55 +512,Picture,199 +513,Picture,199 +514,Picture,383 +515,Picture,383 +516,Picture,519 +517,Picture,519 +518,Picture,1279 +519,Picture,1279 +520,Picture,12799 +521,Picture,12799 +522,Enchanted beef,1 +523,Enchanted rat meat,1 +524,Enchanted bear meat,1 +525,Enchanted chicken,1 +526,Bones,1 +527,Bones,1 +528,Burnt bones,1 +529,Burnt bones,1 +530,Bat bones,1 +531,Bat bones,1 +532,Big bones,1 +533,Big bones,1 +534,Babydragon bones,1 +535,Babydragon bones,1 +536,Dragon bones,1 +537,Dragon bones,1 +538,Druid's robe,30 +539,Druid's robe,30 +540,Druid's robe,40 +541,Druid's robe,40 +542,Monk's robe,30 +543,Monk's robe,30 +544,Monk's robe,40 +545,Monk's robe,40 +546,Shade robe,40 +547,Picture,0 +548,Shade robe,30 +549,Picture,0 +550,Newcomer map,1 +551,Newcomer map,1 +552,Ghostspeak amulet,35 +553,Ghost's skull,1 +554,Fire rune,17 +555,Water rune,17 +556,Air rune,17 +557,Earth rune,17 +558,Mind rune,17 +559,Body rune,16 +560,Death rune,310 +561,Nature rune,372 +562,Chaos rune,140 +563,Law rune,378 +564,Cosmic rune,232 +565,Blood rune,550 +566,Soul rune,410 +567,Unpowered orb,100 +568,Unpowered orb,100 +569,Fire orb,300 +570,Fire orb,300 +571,Water orb,300 +572,Water orb,300 +573,Air orb,300 +574,Air orb,300 +575,Earth orb,300 +576,Earth orb,300 +577,Wizard robe,15 +578,Wizard robe,15 +579,Wizard hat,2 +580,Wizard hat,2 +581,Black robe,13 +582,Black robe,13 +583,Bailing bucket,10 +584,Bailing bucket,10 +585,Bailing bucket,10 +586,Bailing bucket,10 +587,Orb of protection,1 +588,Orbs of protection,1 +589,Gnome amulet,1 +590,Tinderbox,1 +591,Tinderbox,1 +592,Ashes,2 +593,Ashes,2 +594,Lit torch,4 +595,Torch,4 +596,Unlit torch,4 +597,Unlit torch,4 +598,Bronze fire arrows,10 +599,Picture,1 +600,Astronomy book,3 +601,Goblin kitchen key,1 +602,Lens mould,1 +603,Observatory lens,3 +604,Bone shard,1 +605,Bone key,1 +606,Stone-plaque,1 +607,Tattered scroll,1 +608,Crumpled scroll,1 +609,Rashiliyia corpse,1 +610,Zadimus corpse,1 +611,Locating crystal,1 +612,Locating crystal,1 +613,Locating crystal,1 +614,Locating crystal,1 +615,Locating crystal,1 +616,Beads of the dead,1 +617,Coins,1 +618,Bone beads,1 +619,Paramaya ticket,5 +620,Paramaya ticket,5 +621,Ship ticket,5 +622,Tinderbox,0 +623,Sword pommel,1 +624,Bervirius notes,1 +625,Wampum belt,5 +626,Boots,200 +627,Boots,200 +628,Boots,200 +629,Boots,200 +630,Boots,200 +631,Boots,200 +632,Boots,200 +633,Boots,200 +634,Boots,200 +635,Boots,200 +636,Robe top,180 +637,Robe top,180 +638,Robe top,180 +639,Robe top,180 +640,Robe top,180 +641,Robe top,180 +642,Robe top,180 +643,Robe top,180 +644,Robe top,180 +645,Robe top,180 +646,Robe bottoms,180 +647,Robe bottoms,180 +648,Robe bottoms,180 +649,Robe bottoms,180 +650,Robe bottoms,180 +651,Robe bottoms,180 +652,Robe bottoms,180 +653,Robe bottoms,180 +654,Robe bottoms,180 +655,Robe bottoms,180 +656,Hat,160 +657,Hat,160 +658,Hat,160 +659,Hat,160 +660,Hat,160 +661,Hat,160 +662,Hat,160 +663,Hat,160 +664,Hat,160 +665,Hat,160 +666,Portrait,3 +667,Blurite sword,200 +668,Blurite ore,3 +669,Specimen jar,1 +670,Specimen brush,1 +671,Animal skull,1 +672,Special cup,1 +673,Teddy,1 +674,Cracked sample,1 +675,Rock pick,1 +676,Trowel,1 +677,Panning tray,1 +678,Panning tray,1 +679,Panning tray,1 +680,Nuggets,1 +681,Ancient talisman,1 +682,Unstamped letter,1 +683,Sealed letter,1 +684,Belt buckle,1 +685,Old boot,1 +686,Rusty sword,1 +687,Broken arrow,1 +688,Buttons,1 +689,Broken staff,1 +690,Picture,1 +691,Level 1 certificate,1 +692,Level 2 certificate,1 +693,Level 3 certificate,1 +694,Ceramic remains,1 +695,Old tooth,1 +696,Invitation letter,1 +697,Damaged armour,1 +698,Broken armour,1 +699,Stone tablet,1 +700,Chemical powder,1 +701,Ammonium nitrate,1 +702,Unidentified liquid,1 +703,Nitroglycerin,1 +704,Ground charcoal,1 +705,Mixed chemicals,1 +706,Mixed chemicals,1 +707,Chemical compound,1 +708,Arcenia root,1 +709,Chest key,1 +710,Vase,1 +711,Book on chemicals,1 +712,Cup of tea,1 +713,Picture,1 +714,Radimus notes,1 +715,Radimus notes,1 +716,Bull roarer,1 +717,Scrawled note,1 +718,A scribbled note,1 +719,Scrumpled note,1 +720,Sketch,10 +721,Gold bowl,700 +722,Blessed gold bowl,700 +723,Golden bowl,700 +724,Golden bowl,700 +725,Golden bowl,700 +726,Golden bowl,700 +727,Hollow reed,2 +728,Hollow reed,1 +729,Shamans tome,1 +730,Binding book,1 +731,Enchanted vial,1 +732,Holy water,1 +733,Smashed glass,1 +734,Tinderbox,0 +735,Yommi tree seeds,1 +736,Yommi tree seeds,1 +737,Snakeweed mixture,1 +738,Ardrigal mixture,1 +739,Bravery potion,1 +740,Blue hat,2 +741,Chunk of crystal,1 +742,Hunk of crystal,1 +743,Lump of crystal,1 +744,Heart crystal,1 +745,Heart crystal,1 +746,Dark dagger,1 +747,Glowing dagger,1 +748,Holy force,1 +749,Yommi totem,1 +750,Gilded totem,1 +751,Gnomeball,1 +752,Gnomeball,1 +753,Cadava berries,1 +754,Cadava berries,1 +755,Message,5 +756,Cadava potion,1 +757,Book,1 +758,Phoenix hq key,1 +759,Weapon store key,1 +760,Weapon store key,1 +761,Intel report,5 +762,Picture,1 +763,Broken shield,1 +764,Picture,1 +765,Broken shield,1 +766,Picture,1 +767,Phoenix crossbow,4 +768,Phoenix crossbow,4 +769,Certificate,1 +770,Picture,1 +771,Dramen branch,15 +772,Dramen staff,15 +773,'perfect' ring,2025 +774,'perfect' necklace,2175 +775,Cooking gauntlets,1 +776,Goldsmith gauntlets,1 +777,Chaos gauntlets,1 +778,Family gauntlets,1 +779,Crest part,1 +780,Crest part,1 +781,Crest part,1 +782,Family crest,1 +783,Bark sample,2 +784,Translation book,2 +785,Glough's journal,2 +786,Hazelmere's scroll,2 +787,Lumber order,2 +788,Glough's key,1 +789,Twigs,1 +790,Twigs,1 +791,Twigs,1 +792,Twigs,1 +793,Daconia rock,1 +794,Invasion plans,2 +795,War ship,2 +796,Exploding vial,1 +797,Herb bowl,1 +798,Grinder,1 +799,null,1 +800,Bronze thrownaxe,4 +801,Iron thrownaxe,13 +802,Steel thrownaxe,38 +803,Mithril thrownaxe,105 +804,Adamant thrownaxe,262 +805,Rune thrownaxe,698 +806,Bronze dart,1 +807,Iron dart,2 +808,Steel dart,10 +809,Mithril dart,25 +810,Adamant dart,65 +811,Rune dart,350 +812,Bronze dart(p),1 +813,Iron dart(p),2 +814,Steel dart(p),10 +815,Mithril dart(p),25 +816,Adamant dart(p),65 +817,Rune dart(p),350 +818,Poisoned dart(p),1 +819,Bronze dart tip,1 +820,Iron dart tip,3 +821,Steel dart tip,5 +822,Mithril dart tip,12 +823,Adamant dart tip,36 +824,Rune dart tip,175 +825,Bronze javelin,6 +826,Iron javelin,8 +827,Steel javelin,37 +828,Mithril javelin,98 +829,Adamant javelin,246 +830,Rune javelin,624 +831,Bronze javelin(p),4 +832,Iron javelin(p),6 +833,Steel javelin(p),24 +834,Mithril javelin(p),64 +835,Adamant javelin(p),160 +836,Rune javelin(p),400 +837,Crossbow,70 +838,Crossbow,70 +839,Longbow,80 +840,Longbow,80 +841,Shortbow,50 +842,Shortbow,50 +843,Oak shortbow,100 +844,Oak shortbow,100 +845,Oak longbow,160 +846,Oak longbow,160 +847,Willow longbow,320 +848,Willow longbow,320 +849,Willow shortbow,200 +850,Willow shortbow,200 +851,Maple longbow,640 +852,Maple longbow,640 +853,Maple shortbow,400 +854,Maple shortbow,400 +855,Yew longbow,1280 +856,Yew longbow,1280 +857,Yew shortbow,800 +858,Yew shortbow,800 +859,Magic longbow,2560 +860,Magic longbow,2560 +861,Magic shortbow,1600 +862,Magic shortbow,1600 +863,Iron knife,58 +864,Bronze knife,14 +865,Steel knife,68 +866,Mithril knife,27 +867,Adamant knife,66 +868,Rune knife,167 +869,Black knife,19 +870,Bronze knife(p),1 +871,Iron knife(p),3 +872,Steel knife(p),10 +873,Mithril knife(p),27 +874,Black knife(p),18 +875,Adamant knife(p),66 +876,Rune knife(p),166 +877,Bronze bolts,3 +878,Bronze bolts(p),3 +879,Opal bolts,7 +880,Pearl bolts,13 +881,Barbed bolts,200 +882,Bronze arrow,7 +883,Bronze arrow(p),1 +884,Iron arrow,20 +885,Iron arrow(p),3 +886,Steel arrow,46 +887,Steel arrow(p),12 +888,Mithril arrow,76 +889,Mithril arrow(p),32 +890,Adamant arrow,172 +891,Adamant arrow(p),80 +892,Rune arrow,510 +893,Rune arrow(p),400 +894,null,1 +895,null,1 +896,null,1 +897,null,1 +898,null,1 +899,null,1 +900,null,1 +901,null,1 +902,null,1 +903,null,1 +904,null,1 +905,null,1 +906,null,1 +907,null,1 +908,null,1 +909,null,1 +910,null,1 +911,null,1 +912,null,1 +913,null,1 +914,null,1 +915,null,1 +916,null,1 +917,null,1 +918,null,1 +919,null,1 +920,null,1 +921,null,1 +922,null,1 +923,null,1 +924,null,1 +925,null,1 +926,null,1 +927,null,1 +928,null,1 +929,null,1 +930,null,1 +931,null,1 +932,null,1 +933,null,1 +934,null,1 +935,null,1 +936,null,1 +937,null,1 +938,null,1 +939,null,1 +940,null,1 +941,null,1 +942,Bronze fire arrows,10 +943,Worm,0 +944,Worm,0 +945,Throwing rope,1 +946,Knife,6 +947,Knife,6 +948,Bear fur,10 +949,Bear fur,10 +950,Silk,55 +951,Silk,55 +952,Spade,3 +953,Spade,3 +954,Rope,18 +955,Rope,18 +956,Flier,1 +957,Tinderbox,0 +958,Grey wolf fur,150 +959,Grey wolf fur,150 +960,Plank,100 +961,Plank,100 +962,Christmas cracker,1 +963,Christmas cracker,1 +964,Skull,1 +965,Skull,1 +966,Tile,1 +967,Tinderbox,0 +968,Rock,1 +969,Tinderbox,0 +970,Papyrus,10 +971,Papyrus,10 +972,Papyrus,1 +973,Charcoal,45 +974,Charcoal,45 +975,Machete,52 +976,Machete,52 +977,Cooking pot,1 +978,Cooking pot,1 +979,null,1 +980,null,1 +981,Disk of returning,12 +982,Disk of returning,12 +983,Brass key,1 +984,Brass key,1 +985,Tooth half of a key,1 +986,Tooth half of a key,1 +987,Loop half of a key,1 +988,Loop half of a key,1 +989,Crystal key,1 +990,Crystal key,1 +991,Muddy key,1 +992,Muddy key,1 +993,Sinister key,1 +994,Sinister key,1 +995,Coins,1 +996,null,1 +997,null,1 +998,null,1 +999,null,1 +1000,null,1 +1001,null,1 +1002,null,1 +1003,null,1 +1004,null,1 +1005,White apron,2 +1006,White apron,2 +1007,Cape,20 +1008,Cape,20 +1009,Brass necklace,30 +1010,Brass necklace,30 +1011,Blue skirt,2 +1012,Blue skirt,2 +1013,Pink skirt,2 +1014,Pink skirt,2 +1015,Black skirt,2 +1016,Black skirt,2 +1017,Wizard hat,2 +1018,Wizard hat,2 +1019,Cape,20 +1020,Cape,20 +1021,Cape,20 +1022,Cape,20 +1023,Cape,20 +1024,Cape,20 +1025,Eye patch,2 +1026,Eye patch,2 +1027,Cape,20 +1028,Cape,20 +1029,Cape,20 +1030,Cape,20 +1031,Cape,20 +1032,Cape,20 +1033,Zamorak robe,30 +1034,Zamorak robe,30 +1035,Zamorak robe,40 +1036,Zamorak robe,40 +1037,Bunny ears,1 +1038,Red partyhat,1 +1039,Red partyhat,1 +1040,Yellow partyhat,1 +1041,Yellow partyhat,1 +1042,Blue partyhat,1 +1043,Blue partyhat,1 +1044,Green partyhat,1 +1045,Green partyhat,1 +1046,Purple partyhat,1 +1047,Purple partyhat,1 +1048,White partyhat,1 +1049,White partyhat,1 +1050,Santa hat,160 +1051,Santa hat,160 +1052,Cape of legends,450 +1053,Green h'ween mask,15 +1054,Green h'ween mask,15 +1055,Blue h'ween mask,15 +1056,Blue h'ween mask,15 +1057,Red h'ween mask,15 +1058,Red h'ween mask,15 +1059,Leather gloves,6 +1060,Leather gloves,6 +1061,Leather boots,6 +1062,Leather boots,6 +1063,Leather vambraces,18 +1064,Leather vambraces,18 +1065,Green d'hide vamb,2500 +1066,Green d'hide vamb,2500 +1067,Iron platelegs,280 +1068,Iron platelegs,280 +1069,Steel platelegs,1000 +1070,Steel platelegs,1000 +1071,Mithril platelegs,2600 +1072,Mithril platelegs,2600 +1073,Adamant platelegs,6400 +1074,Adamant platelegs,6400 +1075,Bronze platelegs,80 +1076,Bronze platelegs,80 +1077,Black platelegs,1920 +1078,Black platelegs,1920 +1079,Rune platelegs,64000 +1080,Rune platelegs,64000 +1081,Iron plateskirt,280 +1082,Iron plateskirt,280 +1083,Steel plateskirt,1000 +1084,Steel plateskirt,1000 +1085,Mithril plateskirt,2600 +1086,Mithril plateskirt,2600 +1087,Bronze plateskirt,80 +1088,Bronze plateskirt,80 +1089,Black plateskirt,1920 +1090,Black plateskirt,1920 +1091,Adamant plateskirt,6400 +1092,Adamant plateskirt,6400 +1093,Rune plateskirt,64000 +1094,Rune plateskirt,64000 +1095,Leather chaps,20 +1096,Leather chaps,20 +1097,Studded chaps,750 +1098,Studded chaps,750 +1099,Green d'hide chaps,3900 +1100,Green d'hide chaps,3900 +1101,Iron chainbody,210 +1102,Iron chainbody,210 +1103,Bronze chainbody,60 +1104,Bronze chainbody,60 +1105,Steel chainbody,750 +1106,Steel chainbody,750 +1107,Black chainbody,1440 +1108,Black chainbody,1440 +1109,Mithril chainbody,1950 +1110,Mithril chainbody,1950 +1111,Adamant chainbody,4800 +1112,Adamant chainbody,4800 +1113,Rune chainbody,50000 +1114,Rune chainbody,50000 +1115,Iron platebody,560 +1116,Iron platebody,560 +1117,Bronze platebody,160 +1118,Bronze platebody,160 +1119,Steel platebody,2000 +1120,Steel platebody,2000 +1121,Mithril platebody,5200 +1122,Mithril platebody,5200 +1123,Adamant platebody,16640 +1124,Adamant platebody,16640 +1125,Black platebody,3840 +1126,Black platebody,3840 +1127,Rune platebody,65000 +1128,Rune platebody,65000 +1129,Leather body,21 +1130,Leather body,21 +1131,Hardleather body,170 +1132,Hardleather body,170 +1133,Studded body,850 +1134,Studded body,850 +1135,Green d'hide body,7800 +1136,Green d'hide body,7800 +1137,Iron med helm,84 +1138,Iron med helm,84 +1139,Bronze med helm,24 +1140,Bronze med helm,24 +1141,Steel med helm,300 +1142,Steel med helm,300 +1143,Mithril med helm,780 +1144,Mithril med helm,780 +1145,Adamant med helm,1920 +1146,Adamant med helm,1920 +1147,Rune med helm,19200 +1148,Rune med helm,19200 +1149,Dragon med helm,100000 +1150,Dragon med helm,100000 +1151,Black med helm,576 +1152,Black med helm,576 +1153,Iron full helm,154 +1154,Iron full helm,154 +1155,Bronze full helm,44 +1156,Bronze full helm,44 +1157,Steel full helm,550 +1158,Steel full helm,550 +1159,Mithril full helm,1430 +1160,Mithril full helm,1430 +1161,Adamant full helm,3520 +1162,Adamant full helm,3520 +1163,Rune full helm,35200 +1164,Rune full helm,35200 +1165,Black full helm,1372 +1166,Black full helm,1372 +1167,Leather cowl,24 +1168,Leather cowl,24 +1169,Coif,200 +1170,Coif,200 +1171,Wooden shield,20 +1172,Wooden shield,20 +1173,Bronze sq shield,48 +1174,Bronze sq shield,48 +1175,Iron sq shield,168 +1176,Iron sq shield,168 +1177,Steel sq shield,600 +1178,Steel sq shield,600 +1179,Black sq shield,1152 +1180,Black sq shield,1152 +1181,Mithril sq shield,1560 +1182,Mithril sq shield,1560 +1183,Adamant sq shield,3840 +1184,Adamant sq shield,3840 +1185,Rune sq shield,38400 +1186,Rune sq shield,38400 +1187,Dragon sq shield,500000 +1188,Dragon sq shield,500000 +1189,Bronze kiteshield,68 +1190,Bronze kiteshield,68 +1191,Iron kiteshield,238 +1192,Iron kiteshield,238 +1193,Steel kiteshield,850 +1194,Steel kiteshield,850 +1195,Black kiteshield,2121 +1196,Black kiteshield,2121 +1197,Mithril kiteshield,2210 +1198,Mithril kiteshield,2210 +1199,Adamant kiteshield,5440 +1200,Adamant kiteshield,5440 +1201,Rune kiteshield,54400 +1202,Rune kiteshield,54400 +1203,Iron dagger,35 +1204,Iron dagger,35 +1205,Bronze dagger,10 +1206,Bronze dagger,10 +1207,Steel dagger,125 +1208,Steel dagger,125 +1209,Mithril dagger,325 +1210,Mithril dagger,325 +1211,Adamant dagger,800 +1212,Adamant dagger,800 +1213,Rune dagger,8000 +1214,Rune dagger,8000 +1215,Dragon dagger,30000 +1216,Dragon dagger,30000 +1217,Black dagger,240 +1218,Black dagger,240 +1219,Iron dagger(p),35 +1220,Iron dagger(p),35 +1221,Bronze dagger(p),10 +1222,Bronze dagger(p),10 +1223,Steel dagger(p),125 +1224,Steel dagger(p),125 +1225,Mithril dagger(p),325 +1226,Mithril dagger(p),325 +1227,Adamant dagger(p),800 +1228,Adamant dagger(p),800 +1229,Rune dagger(p),8000 +1230,Rune dagger(p),8000 +1231,Dragon dagger(p),24000 +1232,Dragon dagger(p),24000 +1233,Black dagger(p),240 +1234,Black dagger(p),240 +1235,Poisoned dagger(p),565 +1236,Poisoned dagger(p),565 +1237,Bronze spear,26 +1238,Bronze spear,26 +1239,Iron spear,91 +1240,Iron spear,91 +1241,Steel spear,325 +1242,Steel spear,325 +1243,Mithril spear,845 +1244,Mithril spear,845 +1245,Adamant spear,2080 +1246,Adamant spear,2080 +1247,Rune spear,20800 +1248,Rune spear,20800 +1249,Dragon spear,62400 +1250,Dragon spear,62400 +1251,Bronze spear(p),26 +1252,Bronze spear(p),26 +1253,Iron spear(p),91 +1254,Iron spear(p),91 +1255,Steel spear(p),325 +1256,Steel spear(p),325 +1257,Mithril spear(p),845 +1258,Mithril spear(p),845 +1259,Adamant spear(p),2080 +1260,Adamant spear(p),2080 +1261,Rune spear(p),20800 +1262,Rune spear(p),20800 +1263,Dragon spear(p),62400 +1264,Dragon spear(p),62400 +1265,Bronze pickaxe,1 +1266,Bronze pickaxe,1 +1267,Iron pickaxe,140 +1268,Iron pickaxe,140 +1269,Steel pickaxe,500 +1270,Steel pickaxe,500 +1271,Adamant pickaxe,3200 +1272,Adamant pickaxe,3200 +1273,Mithril pickaxe,1300 +1274,Mithril pickaxe,1300 +1275,Rune pickaxe,32000 +1276,Rune pickaxe,32000 +1277,Bronze sword,26 +1278,Bronze sword,26 +1279,Iron sword,91 +1280,Iron sword,91 +1281,Steel sword,325 +1282,Steel sword,325 +1283,Black sword,624 +1284,Black sword,624 +1285,Mithril sword,845 +1286,Mithril sword,845 +1287,Adamant sword,2080 +1288,Adamant sword,2080 +1289,Rune sword,20800 +1290,Rune sword,20800 +1291,Bronze longsword,40 +1292,Bronze longsword,40 +1293,Iron longsword,140 +1294,Iron longsword,140 +1295,Steel longsword,500 +1296,Steel longsword,500 +1297,Black longsword,960 +1298,Black longsword,960 +1299,Mithril longsword,1300 +1300,Mithril longsword,1300 +1301,Adamant longsword,3200 +1302,Adamant longsword,3200 +1303,Rune longsword,32000 +1304,Rune longsword,32000 +1305,Dragon longsword,100000 +1306,Dragon longsword,100000 +1307,Bronze 2h sword,80 +1308,Bronze 2h sword,80 +1309,Iron 2h sword,280 +1310,Iron 2h sword,280 +1311,Steel 2h sword,1000 +1312,Steel 2h sword,1000 +1313,Black 2h sword,1920 +1314,Black 2h sword,1920 +1315,Mithril 2h sword,2600 +1316,Mithril 2h sword,2600 +1317,Adamant 2h sword,6400 +1318,Adamant 2h sword,6400 +1319,Rune 2h sword,64000 +1320,Rune 2h sword,64000 +1321,Bronze scimitar,32 +1322,Bronze scimitar,32 +1323,Iron scimitar,112 +1324,Iron scimitar,112 +1325,Steel scimitar,400 +1326,Steel scimitar,400 +1327,Black scimitar,768 +1328,Black scimitar,768 +1329,Mithril scimitar,1040 +1330,Mithril scimitar,1040 +1331,Adamant scimitar,2560 +1332,Adamant scimitar,2560 +1333,Rune scimitar,25600 +1334,Rune scimitar,25600 +1335,Iron warhammer,224 +1336,Iron warhammer,224 +1337,Bronze warhammer,61 +1338,Bronze warhammer,61 +1339,Steel warhammer,832 +1340,Steel warhammer,832 +1341,Black warhammer,1274 +1342,Black warhammer,1274 +1343,Mithril warhammer,2158 +1344,Mithril warhammer,2158 +1345,Adamant warhammer,5356 +1346,Adamant warhammer,5356 +1347,Rune warhammer,41500 +1348,Rune warhammer,41500 +1349,Iron axe,56 +1350,Iron axe,56 +1351,Bronze axe,16 +1352,Bronze axe,16 +1353,Steel axe,200 +1354,Steel axe,200 +1355,Mithril axe,520 +1356,Mithril axe,520 +1357,Adamant axe,1280 +1358,Adamant axe,1280 +1359,Rune axe,12800 +1360,Rune axe,12800 +1361,Black axe,384 +1362,Black axe,384 +1363,Iron battleaxe,182 +1364,Iron battleaxe,182 +1365,Steel battleaxe,650 +1366,Steel battleaxe,650 +1367,Black battleaxe,1248 +1368,Black battleaxe,1248 +1369,Mithril battleaxe,1690 +1370,Mithril battleaxe,1690 +1371,Adamant battleaxe,4160 +1372,Adamant battleaxe,4160 +1373,Rune battleaxe,41600 +1374,Rune battleaxe,41600 +1375,Bronze battleaxe,52 +1376,Bronze battleaxe,52 +1377,Dragon battleaxe,200000 +1378,Dragon battleaxe,200000 +1379,Staff,15 +1380,Staff,15 +1381,Staff of air,1500 +1382,Staff of air,1500 +1383,Staff of water,1500 +1384,Staff of water,1500 +1385,Staff of earth,1500 +1386,Staff of earth,1500 +1387,Staff of fire,1500 +1388,Staff of fire,1500 +1389,Magic staff,200 +1390,Magic staff,200 +1391,Battlestaff,7000 +1392,Battlestaff,7000 +1393,Fire battlestaff,15500 +1394,Fire battlestaff,15500 +1395,Water battlestaff,15500 +1396,Water battlestaff,15500 +1397,Air battlestaff,15500 +1398,Air battlestaff,15500 +1399,Earth battlestaff,15500 +1400,Earth battlestaff,15500 +1401,Mystic fire staff,42500 +1402,Mystic fire staff,42500 +1403,Mystic water staff,42500 +1404,Mystic water staff,42500 +1405,Mystic air staff,42500 +1406,Mystic air staff,42500 +1407,Mystic earth staff,42500 +1408,Mystic earth staff,42500 +1409,Iban's staff,42500 +1410,Iban's staff,20 +1411,Farmer's fork,1 +1412,Farmer's fork,1 +1413,Halberd,1 +1414,Halberd,1 +1415,Warhammer,1 +1416,Warhammer,1 +1417,Javelin,1 +1418,Javelin,1 +1419,Scythe,1 +1420,Iron mace,63 +1421,Iron mace,63 +1422,Bronze mace,18 +1423,Bronze mace,18 +1424,Steel mace,225 +1425,Steel mace,225 +1426,Black mace,432 +1427,Black mace,432 +1428,Mithril mace,585 +1429,Mithril mace,585 +1430,Adamant mace,1440 +1431,Adamant mace,1440 +1432,Rune mace,14400 +1433,Rune mace,14400 +1434,Dragon mace,50000 +1435,Dragon mace,50000 +1436,Rune essence,4 +1437,Rune essence,4 +1438,Air talisman,4 +1439,Air talisman,4 +1440,Earth talisman,4 +1441,Earth talisman,4 +1442,Fire talisman,4 +1443,Fire talisman,4 +1444,Water talisman,4 +1445,Water talisman,4 +1446,Body talisman,4 +1447,Body talisman,4 +1448,Mind talisman,4 +1449,Mind talisman,4 +1450,Blood talisman,4 +1451,Blood talisman,4 +1452,Chaos talisman,4 +1453,Chaos talisman,4 +1454,Cosmic talisman,4 +1455,Cosmic talisman,4 +1456,Death talisman,4 +1457,Death talisman,4 +1458,Law talisman,4 +1459,Law talisman,4 +1460,Soul talisman,4 +1461,Soul talisman,4 +1462,Nature talisman,4 +1463,Nature talisman,4 +1464,Archery ticket,25 +1465,Weapon poison,11 +1466,Sea slug,4 +1467,Damp sticks,1 +1468,Dry sticks,1 +1469,Broken glass,0 +1470,Red bead,4 +1471,Red bead,4 +1472,Yellow bead,4 +1473,Yellow bead,4 +1474,Black bead,4 +1475,Black bead,4 +1476,White bead,4 +1477,White bead,4 +1478,Amulet of accuracy,100 +1479,Amulet of accuracy,100 +1480,Rock,1 +1481,Orb of light,10 +1482,Orb of light,10 +1483,Orb of light,10 +1484,Orb of light,20 +1485,Damp cloth,10 +1486,Piece of railing,10 +1487,Unicorn horn,20 +1488,Paladin's badge,10 +1489,Paladin's badge,10 +1490,Paladin's badge,10 +1491,Witch's cat,1 +1492,Doll of iban,2 +1493,Old journal,1 +1494,History of iban,1 +1495,Klank's gauntlets,6 +1496,Iban's dove,1 +1497,Amulet of othanian,0 +1498,Amulet of doomion,0 +1499,Amulet of holthion,0 +1500,Iban's shadow,2 +1501,Dwarf brew,2 +1502,Iban's ashes,2 +1503,Warrant,5 +1504,Hangover cure,2 +1505,A magic scroll,1 +1506,Gas mask,2 +1507,A small key,1 +1508,A scruffy note,2 +1509,Book,1 +1510,Picture,1 +1511,Logs,4 +1512,Logs,4 +1513,Magic logs,320 +1514,Magic logs,320 +1515,Yew logs,160 +1516,Yew logs,160 +1517,Maple logs,80 +1518,Maple logs,80 +1519,Willow logs,40 +1520,Willow logs,40 +1521,Oak logs,20 +1522,Oak logs,20 +1523,Lockpick,20 +1524,Lockpick,20 +1525,Grimy snake weed,1 +1526,Clean snake weed,5 +1527,Grimy ardrigal,1 +1528,Clean ardrigal,5 +1529,Grimy sito foil,1 +1530,Clean sito foil,5 +1531,Grimy volencia moss,1 +1532,Clean volencia moss,5 +1533,Grimy rogue's purse,1 +1534,Clean rogue's purse,5 +1535,Map part,1 +1536,Map part,1 +1537,Map part,1 +1538,Crandor map,1 +1539,Steel nails,52 +1540,Anti-dragon shield,20 +1541,Anti-dragon shield,20 +1542,Maze key,1 +1543,Key,1 +1544,Key,1 +1545,Key,1 +1546,Key,1 +1547,Key,1 +1548,Key,1 +1549,Stake,8 +1550,Garlic,3 +1551,Garlic,3 +1552,Doogle sardine,10 +1553,Tinderbox,0 +1554,null,1 +1555,Pet kitten,1 +1556,Pet kitten,1 +1557,Pet kitten,1 +1558,Pet kitten,1 +1559,Pet kitten,1 +1560,Pet kitten,1 +1561,Pet cat,1 +1562,Pet cat,1 +1563,Pet cat,1 +1564,Pet cat,1 +1565,Pet cat,1 +1566,Pet cat,1 +1567,Overgrown cat,1 +1568,Overgrown cat,1 +1569,Overgrown cat,1 +1570,Overgrown cat,1 +1571,Overgrown cat,1 +1572,Overgrown cat,1 +1573,Doogle leaves,2 +1574,Tinderbox,0 +1575,Cat training medal,350 +1576,Tinderbox,0 +1577,Pete's candlestick,5 +1578,Pete's candlestick,5 +1579,Thieves' armband,2 +1580,Ice gloves,6 +1581,Blamish snail slime,5 +1582,Blamish oil,10 +1583,Fire feather,2 +1584,Id papers,1 +1585,Oily fishing rod,15 +1586,Miscellaneous key,1 +1587,Miscellaneous key,1 +1588,Grips' key ring,1 +1589,Picture,1 +1590,Dusty key,1 +1591,Jail key,1 +1592,Ring mould,5 +1593,Ring mould,5 +1594,Unholy mould,200 +1595,Amulet mould,5 +1596,Amulet mould,5 +1597,Necklace mould,5 +1598,Necklace mould,5 +1599,Holy mould,5 +1600,Holy mould,5 +1601,Diamond,2000 +1602,Diamond,2000 +1603,Ruby,1000 +1604,Ruby,1000 +1605,Emerald,500 +1606,Emerald,500 +1607,Sapphire,250 +1608,Sapphire,250 +1609,Opal,100 +1610,Opal,100 +1611,Jade,150 +1612,Jade,150 +1613,Red topaz,200 +1614,Red topaz,200 +1615,Dragonstone,10000 +1616,Dragonstone,10000 +1617,Uncut diamond,200 +1618,Uncut diamond,200 +1619,Uncut ruby,100 +1620,Uncut ruby,100 +1621,Uncut emerald,50 +1622,Uncut emerald,50 +1623,Uncut sapphire,25 +1624,Uncut sapphire,25 +1625,Uncut opal,20 +1626,Uncut opal,20 +1627,Uncut jade,30 +1628,Uncut jade,30 +1629,Uncut red topaz,40 +1630,Uncut red topaz,40 +1631,Uncut dragonstone,1000 +1632,Uncut dragonstone,1000 +1633,Crushed gem,1 +1634,Crushed gem,1 +1635,Gold ring,350 +1636,Gold ring,350 +1637,Sapphire ring,900 +1638,Sapphire ring,900 +1639,Emerald ring,1275 +1640,Emerald ring,1275 +1641,Ruby ring,2025 +1642,Ruby ring,2025 +1643,Diamond ring,3525 +1644,Diamond ring,3525 +1645,Dragonstone ring,17625 +1646,Dragonstone ring,17625 +1647,null,1 +1648,null,1 +1649,Sapphire ring,1 +1650,Emerald ring,1 +1651,Ruby ring,1 +1652,Diamond ring,1 +1653,Dragonstone ring,1 +1654,Gold necklace,450 +1655,Gold necklace,450 +1656,Sapphire necklace,1050 +1657,Sapphire necklace,1050 +1658,Emerald necklace,1425 +1659,Emerald necklace,1425 +1660,Ruby necklace,2175 +1661,Ruby necklace,2175 +1662,Diamond necklace,3675 +1663,Diamond necklace,3675 +1664,Dragon necklace,18375 +1665,Dragon necklace,18375 +1666,null,1 +1667,null,1 +1668,Sapphire necklace,1 +1669,Emerald necklace,1 +1670,Ruby necklace,1 +1671,Diamond necklace,1 +1672,Dragon necklace,1 +1673,Gold amulet,350 +1674,Gold amulet,350 +1675,Sapphire amulet,900 +1676,Sapphire amulet,900 +1677,Emerald amulet,1275 +1678,Emerald amulet,1275 +1679,Ruby amulet,2025 +1680,Ruby amulet,2025 +1681,Diamond amulet,3525 +1682,Diamond amulet,3525 +1683,Dragonstone ammy,17625 +1684,Dragonstone ammy,17625 +1685,null,1 +1686,null,1 +1687,Sapphire amulet,1 +1688,Emerald amulet,1 +1689,Ruby amulet,1 +1690,Diamond amulet,1 +1691,Dragonstone ammy,1 +1692,Gold amulet,350 +1693,Gold amulet,350 +1694,Sapphire amulet,900 +1695,Sapphire amulet,900 +1696,Emerald amulet,1275 +1697,Emerald amulet,1275 +1698,Ruby amulet,2025 +1699,Ruby amulet,2025 +1700,Diamond amulet,3525 +1701,Diamond amulet,3525 +1702,Dragonstone ammy,17625 +1703,Dragonstone ammy,17625 +1704,Amulet of glory,17625 +1705,Amulet of glory,17625 +1706,Amulet of glory(1),17625 +1707,Amulet of glory(1),17625 +1708,Amulet of glory(2),17625 +1709,Amulet of glory(2),17625 +1710,Amulet of glory(3),17625 +1711,Amulet of glory(3),17625 +1712,Amulet of glory(4),17625 +1713,Amulet of glory(4),17625 +1714,Unstrung symbol,200 +1715,Unstrung symbol,200 +1716,Unblessed symbol,200 +1717,Unblessed symbol,200 +1718,Holy symbol,300 +1719,Holy symbol,300 +1720,Unstrung emblem,200 +1721,Unstrung emblem,200 +1722,Unpowered symbol,200 +1723,Unpowered symbol,200 +1724,Unholy symbol,200 +1725,Amulet of strength,2025 +1726,Amulet of strength,2025 +1727,Amulet of magic,900 +1728,Amulet of magic,900 +1729,Amulet of defence,1275 +1730,Amulet of defence,1275 +1731,Amulet of power,3525 +1732,Amulet of power,3525 +1733,Needle,1 +1734,Thread,4 +1735,Shears,1 +1736,Shears,1 +1737,Wool,1 +1738,Wool,1 +1739,Cowhide,1 +1740,Cowhide,1 +1741,Leather,1 +1742,Leather,1 +1743,Hard leather,1 +1744,Hard leather,1 +1745,Green d-leather,50 +1746,Green d-leather,50 +1747,Black dragonhide,80 +1748,Black dragonhide,80 +1749,Red dragonhide,60 +1750,Red dragonhide,60 +1751,Blue dragonhide,40 +1752,Blue dragonhide,40 +1753,Green dragonhide,20 +1754,Green dragonhide,20 +1755,Chisel,1 +1756,Chisel,1 +1757,Brown apron,2 +1758,Brown apron,2 +1759,Ball of wool,5 +1760,Ball of wool,5 +1761,Soft clay,2 +1762,Soft clay,2 +1763,Red dye,6 +1764,Red dye,6 +1765,Yellow dye,6 +1766,Yellow dye,6 +1767,Blue dye,6 +1768,Blue dye,6 +1769,Orange dye,6 +1770,Orange dye,6 +1771,Green dye,6 +1772,Green dye,6 +1773,Purple dye,6 +1774,Purple dye,6 +1775,Molten glass,3 +1776,Molten glass,3 +1777,Bow string,10 +1778,Bow string,10 +1779,Flax,5 +1780,Flax,5 +1781,Soda ash,2 +1782,Soda ash,2 +1783,Bucket of sand,2 +1784,Bucket of sand,2 +1785,Glassblowing pipe,2 +1786,Glassblowing pipe,2 +1787,Unfired pot,1 +1788,Unfired pot,1 +1789,Unfired pie dish,3 +1790,Unfired pie dish,3 +1791,Unfired bowl,2 +1792,Unfired bowl,2 +1793,Woad leaf,1 +1794,Bronze wire,20 +1795,Bronze wire,20 +1796,Silver necklace,1 +1797,Silver necklace,1 +1798,Silver cup,1 +1799,Silver cup,1 +1800,Silver bottle,1 +1801,Silver bottle,1 +1802,Silver book,1 +1803,Silver book,1 +1804,Silver needle,1 +1805,Silver needle,1 +1806,Silver pot,1 +1807,Silver pot,1 +1808,Criminal's thread,1 +1809,Criminal's thread,1 +1810,Criminal's thread,1 +1811,Flypaper,1 +1812,Pungent pot,1 +1813,Criminal's dagger,1 +1814,Criminal's dagger,1 +1815,Killer's print,1 +1816,Anna's print,1 +1817,Bob's print,1 +1818,Carol's print,1 +1819,David's print,1 +1820,Elizabeth's print,1 +1821,Frank's print,1 +1822,Unknown print,1 +1823,Waterskin(4),30 +1824,Waterskin(4),30 +1825,Waterskin(3),27 +1826,Waterskin(3),27 +1827,Waterskin(2),24 +1828,Waterskin(2),24 +1829,Waterskin(1),18 +1830,Waterskin(1),18 +1831,Waterskin(0),15 +1832,Waterskin(0),15 +1833,Desert shirt,40 +1834,Desert shirt,40 +1835,Desert robe,40 +1836,Desert robe,40 +1837,Desert boots,20 +1838,Desert boots,20 +1839,Metal key,1 +1840,Cell door key,1 +1841,Barrel,1 +1842,Ana in a barrel,1 +1843,Wrought iron key,1 +1844,Slave shirt,40 +1845,Slave robe,40 +1846,Slave boots,1 +1847,Scrumpled paper,10 +1848,Shantay disclaimer,1 +1849,Prototype dart,2 +1850,Technical plans,1 +1851,Tenti pineapple,1 +1852,Bedabin key,1 +1853,Prototype dart tip,1 +1854,Shantay pass,5 +1855,Rock,1 +1856,Guide book,1 +1857,Totem,10 +1858,Address label,10 +1859,Raw ugthanki meat,2 +1860,Raw ugthanki meat,2 +1861,Ugthanki meat,5 +1862,Ugthanki meat,5 +1863,Pitta dough,4 +1864,Pitta dough,4 +1865,Pitta bread,10 +1866,Pitta bread,10 +1867,Burnt pitta bread,1 +1868,Burnt pitta bread,1 +1869,Chopped tomato,3 +1870,Chopped tomato,3 +1871,Chopped onion,3 +1872,Chopped onion,3 +1873,Chopped ugthanki,5 +1874,Chopped ugthanki,5 +1875,Onion & tomato,5 +1876,Onion & tomato,5 +1877,Ugthanki & onion,7 +1878,Ugthanki & onion,7 +1879,Ugthanki & tomato,7 +1880,Ugthanki & tomato,7 +1881,Kebab mix,9 +1882,Kebab mix,9 +1883,Ugthanki kebab,20 +1884,Ugthanki kebab,20 +1885,Ugthanki kebab,20 +1886,Ugthanki kebab,20 +1887,Cake tin,10 +1888,Cake tin,10 +1889,Uncooked cake,20 +1890,Uncooked cake,20 +1891,Cake,50 +1892,Cake,50 +1893,2/3 cake,30 +1894,2/3 cake,30 +1895,Slice of cake,10 +1896,Slice of cake,10 +1897,Chocolate cake,70 +1898,Chocolate cake,70 +1899,2/3 chocolate cake,50 +1900,2/3 chocolate cake,50 +1901,Chocolate slice,30 +1902,Chocolate slice,30 +1903,Burnt cake,1 +1904,Burnt cake,1 +1905,Asgarnian ale,2 +1906,Asgarnian ale,2 +1907,Wizard's mind bomb,2 +1908,Wizard's mind bomb,2 +1909,Greenman's ale,2 +1910,Greenman's ale,2 +1911,Dragon bitter,2 +1912,Dragon bitter,2 +1913,Dwarven stout,2 +1914,Dwarven stout,2 +1915,Grog,3 +1916,Grog,3 +1917,Beer,2 +1918,Beer,2 +1919,Beer glass,2 +1920,Beer glass,2 +1921,Bowl of water,4 +1922,Bowl of water,4 +1923,Bowl,4 +1924,Bowl,4 +1925,Bucket,2 +1926,Bucket,2 +1927,Bucket of milk,12 +1928,Bucket of milk,12 +1929,Bucket of water,6 +1930,Bucket of water,6 +1931,Empty pot,1 +1932,Empty pot,1 +1933,Pot of flour,14 +1934,Pot of flour,14 +1935,Jug,1 +1936,Jug,1 +1937,Jug of water,1 +1938,Jug of water,1 +1939,Swamp tar,1 +1940,Raw swamp paste,1 +1941,Swamp paste,31 +1942,Potato,16 +1943,Potato,16 +1944,Egg,4 +1945,Egg,4 +1946,Flour,2 +1947,Grain,2 +1948,Grain,2 +1949,Chef's hat,2 +1950,Chef's hat,2 +1951,Redberries,3 +1952,Redberries,3 +1953,Pastry dough,1 +1954,Pastry dough,1 +1955,Cooking apple,36 +1956,Cooking apple,36 +1957,Onion,12 +1958,Onion,12 +1959,Pumpkin,30 +1960,Pumpkin,30 +1961,Easter egg,10 +1962,Easter egg,10 +1963,Banana,2 +1964,Banana,2 +1965,Cabbage,1 +1966,Cabbage,1 +1967,Cabbage,1 +1968,Cabbage,1 +1969,Spinach roll,1 +1970,Tinderbox,0 +1971,Kebab,3 +1972,Kebab,3 +1973,Chocolate bar,20 +1974,Chocolate bar,20 +1975,Chocolate dust,20 +1976,Chocolate dust,20 +1977,Chocolatey milk,2 +1978,Cup of tea,10 +1979,Cup of tea,10 +1980,Empty cup,2 +1981,Empty cup,2 +1982,Tomato,14 +1983,Tomato,14 +1984,Rotten apple,1 +1985,Cheese,8 +1986,Cheese,8 +1987,Grapes,1 +1988,Grapes,1 +1989,Half full wine jug,1 +1990,Half full wine jug,1 +1991,Jug of bad wine,1 +1992,Jug of bad wine,1 +1993,Jug of wine,128 +1994,Jug of wine,128 +1995,Unfermented wine,10 +1996,Unfermented wine,10 +1997,Incomplete stew,4 +1998,Incomplete stew,4 +1999,Incomplete stew,4 +2000,Incomplete stew,4 +2001,Uncooked stew,10 +2002,Uncooked stew,10 +2003,Stew,100 +2004,Stew,100 +2005,Burnt stew,1 +2006,Burnt stew,1 +2007,Spice,230 +2008,Spice,230 +2009,Uncooked curry,10 +2010,Uncooked curry,10 +2011,Curry,20 +2012,Curry,20 +2013,Burnt curry,1 +2014,Burnt curry,1 +2015,Vodka,5 +2016,Vodka,5 +2017,Whisky,5 +2018,Whisky,5 +2019,Gin,5 +2020,Gin,5 +2021,Brandy,5 +2022,Brandy,5 +2023,Cocktail guide,2 +2024,Cocktail guide,2 +2025,Cocktail shaker,2 +2026,Cocktail glass,1 +2027,Cocktail glass,1 +2028,Premade blurb' sp.,30 +2029,Premade blurb' sp.,30 +2030,Premade choc s'dy,30 +2031,Premade choc s'dy,30 +2032,Premade dr' dragon,30 +2033,Premade dr' dragon,30 +2034,Premade fr' blast,30 +2035,Premade fr' blast,30 +2036,Premade p' punch,30 +2037,Premade p' punch,30 +2038,Premade sgg,30 +2039,Premade sgg,30 +2040,Premade wiz blz'd,30 +2041,Premade wiz blz'd,30 +2042,Picture,2 +2043,Picture,2 +2044,Picture,2 +2045,Picture,2 +2046,Picture,2 +2047,Picture,2 +2048,Pineapple punch,30 +2049,Pineapple punch,30 +2050,Picture,2 +2051,Picture,2 +2052,Picture,2 +2053,Picture,2 +2054,Wizard blizzard,30 +2055,Wizard blizzard,30 +2056,Picture,2 +2057,Picture,2 +2058,Picture,2 +2059,Picture,2 +2060,Picture,2 +2061,Picture,2 +2062,Picture,2 +2063,Picture,2 +2064,Blurberry special,30 +2065,Blurberry special,30 +2066,Picture,2 +2067,Picture,2 +2068,Picture,2 +2069,Picture,2 +2070,Picture,2 +2071,Picture,2 +2072,Picture,2 +2073,Picture,2 +2074,Choc saturday,30 +2075,Choc saturday,30 +2076,Picture,2 +2077,Picture,2 +2078,Picture,2 +2079,Picture,2 +2080,Short green guy,30 +2081,Short green guy,30 +2082,Picture,2 +2083,Picture,2 +2084,Fruit blast,30 +2085,Fruit blast,30 +2086,Picture,2 +2087,Picture,2 +2088,Picture,2 +2089,Picture,2 +2090,Picture,2 +2091,Picture,2 +2092,Drunk dragon,30 +2093,Drunk dragon,30 +2094,Picture,2 +2095,Picture,2 +2096,Picture,2 +2097,Picture,2 +2098,Picture,2 +2099,Picture,2 +2100,Picture,2 +2101,Picture,2 +2102,Lemon,2 +2103,Lemon,2 +2104,Lemon chunks,2 +2105,Lemon chunks,2 +2106,Lemon slices,2 +2107,Lemon slices,2 +2108,Orange,70 +2109,Orange,70 +2110,Orange chunks,2 +2111,Orange chunks,2 +2112,Orange slices,2 +2113,Orange slices,2 +2114,Pineapple,2 +2115,Pineapple,2 +2116,Pineapple chunks,1 +2117,Pineapple chunks,1 +2118,Pineapple ring,1 +2119,Pineapple ring,1 +2120,Lime,2 +2121,Lime,2 +2122,Lime chunks,1 +2123,Lime chunks,1 +2124,Lime slices,2 +2125,Lime slices,2 +2126,Dwellberries,4 +2127,Dwellberries,4 +2128,Equa leaves,2 +2129,Equa leaves,2 +2130,Pot of cream,19 +2131,Pot of cream,19 +2132,Raw beef,8 +2133,Raw beef,8 +2134,Raw rat meat,8 +2135,Raw rat meat,8 +2136,Raw bear meat,8 +2137,Raw bear meat,8 +2138,Raw chicken,9 +2139,Raw chicken,9 +2140,Cooked chicken,4 +2141,Cooked chicken,4 +2142,Cooked meat,4 +2143,Cooked meat,4 +2144,Burnt chicken,1 +2145,Burnt chicken,1 +2146,Burnt meat,1 +2147,Burnt meat,1 +2148,Raw lava eel,150 +2149,Lava eel,150 +2150,Swamp toad,2 +2151,Swamp toad,2 +2152,Toad's legs,2 +2153,Toad's legs,2 +2154,Picture,2 +2155,Picture,2 +2156,Spicy toad's legs,2 +2157,Spicy toad's legs,2 +2158,Seasoned legs,2 +2159,Seasoned legs,2 +2160,Spicy worm,2 +2161,Spicy worm,2 +2162,King worm,2 +2163,King worm,2 +2164,Batta tin,10 +2165,Crunchy tray,10 +2166,Gnomebowl mould,10 +2167,Gianne's cook book,2 +2168,Gianne's cook book,2 +2169,Gnome spice,2 +2170,Gnome spice,2 +2171,Gianne dough,2 +2172,Gianne dough,2 +2173,Picture,2 +2174,Picture,2 +2175,Burnt gnomebowl,1 +2176,Burnt gnomebowl,1 +2177,Half baked bowl,2 +2178,Raw gnomebowl,2 +2179,Picture,1 +2180,Picture,1 +2181,Picture,1 +2182,Picture,1 +2183,Picture,1 +2184,Picture,1 +2185,Chocolate bomb,2 +2186,Chocolate bomb,2 +2187,Tangled toads' legs,2 +2188,Tangled toads' legs,2 +2189,Picture,1 +2190,Picture,1 +2191,Worm hole,2 +2192,Worm hole,2 +2193,Picture,1 +2194,Picture,1 +2195,Veg ball,2 +2196,Veg ball,2 +2197,Odd crunchies,2 +2198,Odd crunchies,2 +2199,Burnt crunchies,1 +2200,Burnt crunchies,1 +2201,Half baked crunchy,2 +2202,Raw crunchies,2 +2203,Picture,1 +2204,Picture,1 +2205,Worm crunchies,2 +2206,Worm crunchies,2 +2207,Picture,1 +2208,Picture,1 +2209,Chocchip crunchies,2 +2210,Chocchip crunchies,2 +2211,Picture,1 +2212,Picture,1 +2213,Spicy crunchies,2 +2214,Spicy crunchies,2 +2215,Picture,1 +2216,Picture,1 +2217,Toad crunchies,2 +2218,Toad crunchies,2 +2219,Premade w'm batta,195 +2220,Premade w'm batta,195 +2221,Premade t'd batta,195 +2222,Premade t'd batta,195 +2223,Premade c+t batta,195 +2224,Premade c+t batta,195 +2225,Premade fr't batta,195 +2226,Premade fr't batta,195 +2227,Premade veg batta,195 +2228,Premade veg batta,195 +2229,Premade choc bomb,450 +2230,Premade choc bomb,450 +2231,Premade ttl,450 +2232,Premade ttl,450 +2233,Premade worm hole,270 +2234,Premade worm hole,270 +2235,Premade veg ball,270 +2236,Premade veg ball,270 +2237,Premade w'm crun',80 +2238,Premade w'm crun',80 +2239,Premade ch' crunch,70 +2240,Premade ch' crunch,70 +2241,Premade s'y crunch,70 +2242,Premade s'y crunch,70 +2243,Premade t'd crunch,80 +2244,Premade t'd crunch,80 +2245,Picture,2 +2246,Picture,2 +2247,Burnt batta,1 +2248,Burnt batta,1 +2249,Half baked batta,2 +2250,Raw batta,2 +2251,Picture,1 +2252,Picture,1 +2253,Worm batta,1 +2254,Worm batta,1 +2255,Toad batta,2 +2256,Toad batta,2 +2257,Picture,1 +2258,Picture,1 +2259,Cheese+tom batta,2 +2260,Cheese+tom batta,2 +2261,Unfinished batta,1 +2262,Unfinished batta,1 +2263,Unfinished batta,1 +2264,Unfinished batta,1 +2265,Unfinished batta,1 +2266,Unfinished batta,1 +2267,Unfinished batta,1 +2268,Unfinished batta,1 +2269,Unfinished batta,1 +2270,Unfinished batta,1 +2271,Unfinished batta,1 +2272,Unfinished batta,1 +2273,Unfinished batta,1 +2274,Unfinished batta,1 +2275,Picture,1 +2276,Picture,1 +2277,Fruit batta,2 +2278,Fruit batta,2 +2279,Picture,1 +2280,Picture,1 +2281,Vegetable batta,2 +2282,Vegetable batta,2 +2283,Pizza base,4 +2284,Pizza base,4 +2285,Incomplete pizza,10 +2286,Incomplete pizza,10 +2287,Uncooked pizza,25 +2288,Uncooked pizza,25 +2289,Plain pizza,200 +2290,Plain pizza,200 +2291,1/2 plain pizza,20 +2292,1/2 plain pizza,20 +2293,Meat pizza,50 +2294,Meat pizza,50 +2295,1/2 meat pizza,25 +2296,1/2 meat pizza,25 +2297,Anchovy pizza,60 +2298,Anchovy pizza,60 +2299,1/2 anchovy pizza,30 +2300,1/2 anchovy pizza,30 +2301,Pineapple pizza,100 +2302,Pineapple pizza,100 +2303,1/2 p'apple pizza,50 +2304,1/2 p'apple pizza,50 +2305,Burnt pizza,1 +2306,Burnt pizza,1 +2307,Bread dough,4 +2308,Bread dough,4 +2309,Bread,12 +2310,Bread,12 +2311,Burnt bread,1 +2312,Burnt bread,1 +2313,Pie dish,3 +2314,Pie dish,3 +2315,Pie shell,4 +2316,Pie shell,4 +2317,Uncooked apple pie,16 +2318,Uncooked apple pie,16 +2319,Uncooked meat pie,8 +2320,Uncooked meat pie,8 +2321,Uncooked berry pie,6 +2322,Uncooked berry pie,6 +2323,Apple pie,140 +2324,Apple pie,140 +2325,Redberry pie,150 +2326,Redberry pie,150 +2327,Meat pie,160 +2328,Meat pie,160 +2329,Burnt pie,1 +2330,Burnt pie,1 +2331,Half a meat pie,8 +2332,Half a meat pie,8 +2333,Half a redberry pie,6 +2334,Half a redberry pie,6 +2335,Half an apple pie,15 +2336,Half an apple pie,15 +2337,Raw oomlie,10 +2338,Raw oomlie,10 +2339,Palm leaf,5 +2340,Palm leaf,5 +2341,Wrapped oomlie,16 +2342,Wrapped oomlie,16 +2343,Cooked oomlie wrap,35 +2344,Cooked oomlie wrap,35 +2345,Burnt oomlie wrap,1 +2346,Burnt oomlie wrap,1 +2347,Hammer,1 +2348,Hammer,1 +2349,Bronze bar,8 +2350,Bronze bar,8 +2351,Iron bar,28 +2352,Iron bar,28 +2353,Steel bar,100 +2354,Steel bar,100 +2355,Silver bar,150 +2356,Silver bar,150 +2357,Gold bar,300 +2358,Gold bar,300 +2359,Mithril bar,300 +2360,Mithril bar,300 +2361,Adamantite bar,640 +2362,Adamantite bar,640 +2363,Runite bar,5000 +2364,Runite bar,5000 +2365,'perfect' gold bar,300 +2366,Shield left half,110000 +2367,Shield left half,110000 +2368,Shield right half,750000 +2369,Shield right half,750000 +2370,Steel studs,150 +2371,Steel studs,150 +2372,Ogre relic,1 +2373,Relic part 1,1 +2374,Relic part 2,1 +2375,Relic part 3,1 +2376,Skavid map,1 +2377,Ogre tooth,1 +2378,Toban's key,1 +2379,Rock cake,1 +2380,Crystal,1 +2381,Crystal,1 +2382,Crystal,1 +2383,Crystal,1 +2384,Fingernails,1 +2385,Old robe,1 +2386,Unusual armour,1 +2387,Damaged dagger,1 +2388,Tattered eye patch,1 +2389,Vial,1 +2390,Vial,1 +2391,Ground bat bones,20 +2392,Ground bat bones,20 +2393,Toban's gold,300 +2394,Potion,120 +2395,Magic ogre potion,120 +2396,Spell scroll,5 +2397,Shaman robe,40 +2398,Cave nightshade,30 +2399,Silverlight key,100 +2400,Silverlight key,100 +2401,Silverlight key,100 +2402,Silverlight,50 +2403,Hazeel scroll,1 +2404,Chest key,1 +2405,Carnillean armour,65 +2406,Hazeel's mark,1 +2407,Ball,1 +2408,Diary,1 +2409,Door key,1 +2410,Magnet,3 +2411,Key,1 +2412,Saradomin cape,100 +2413,Guthix cape,100 +2414,Zamorak cape,100 +2415,Saradomin staff,80000 +2416,Guthix staff,80000 +2417,Zamorak staff,80000 +2418,Bronze key,1 +2419,Wig,1 +2420,Picture,1 +2421,Wig,1 +2422,Blue partyhat,1 +2423,Key print,1 +2424,Paste,5 +2425,Picture,1 +2426,Burnt oomlie,1 +2427,Burnt oomlie,1 +2428,Attack potion(4),700 +2429,Attack potion(4),700 +2430,Restore potion(4),110 +2431,Restore potion(4),110 +2432,Defence potion(4),1100 +2433,Defence potion(4),1100 +2434,Prayer potion(4),190 +2435,Prayer potion(4),190 +2436,Super attack(4),225 +2437,Super attack(4),225 +2438,Fishing potion(4),250 +2439,Fishing potion(4),250 +2440,Super strength(4),275 +2441,Super strength(4),275 +2442,Super defence(4),330 +2443,Super defence(4),330 +2444,Ranging potion(4),360 +2445,Ranging potion(4),360 +2446,Antipoison(4),360 +2447,Antipoison(4),360 +2448,Super antipoison(4),360 +2449,Super antipoison(4),360 +2450,Zamorak brew(4),200 +2451,Zamorak brew(4),200 +2452,Antifire potion(4),330 +2453,Antifire potion(4),330 +2454,Antifire potion(3),264 +2455,Antifire potion(3),264 +2456,Antifire potion(2),198 +2457,Antifire potion(2),198 +2458,Antifire potion(1),132 +2459,Antifire potion(1),132 +2460,Flowers,100 +2461,Flowers,100 +2462,Flowers,100 +2463,Flowers,100 +2464,Flowers,100 +2465,Flowers,100 +2466,Flowers,100 +2467,Flowers,100 +2468,Flowers,100 +2469,Flowers,100 +2470,Flowers,100 +2471,Flowers,100 +2472,Flowers,100 +2473,Flowers,100 +2474,Flowers,100 +2475,Flowers,100 +2476,Flowers,100 +2477,Flowers,100 +2478,Tinderbox,0 +2479,Tinderbox,0 +2480,Picture,1 +2481,Clean lantadyme,68 +2482,Clean lantadyme,68 +2483,Lantadyme potion(unf),68 +2484,Lantadyme potion(unf),68 +2485,Grimy lantadyme,1 +2486,Grimy lantadyme,1 +2487,Blue d'hide vamb,3000 +2488,Blue d'hide vamb,3000 +2489,Red d'hide vamb,3600 +2490,Red d'hide vamb,3600 +2491,Black d'hide vamb,4320 +2492,Black d'hide vamb,4320 +2493,Blue d'hide chaps,4320 +2494,Blue d'hide chaps,4320 +2495,Red d'hide chaps,5180 +2496,Red d'hide chaps,5180 +2497,Black d'hide chaps,6220 +2498,Black d'hide chaps,6220 +2499,Blue d'hide body,9360 +2500,Blue d'hide body,9360 +2501,Red d'hide body,11230 +2502,Red d'hide body,11230 +2503,Black d'hide body,13480 +2504,Black d'hide body,13480 +2505,Blue d-leather,70 +2506,Blue d-leather,70 +2507,Red dragon leather,90 +2508,Red dragon leather,90 +2509,Black d-leather,110 +2510,Black d-leather,110 +2511,Logs,4 +2512,Picture,1 +2513,Dragon chainbody,6 +2514,Raw shrimps,5 +2515,Raw shrimps,5 +2516,Pot of flour,10 +2517,Pot of flour,10 +2518,Rotten tomato,1 +2519,Rotten tomato,1 +2520,Toy horsey,150 +2521,Toy horsey,150 +2522,Toy horsey,150 +2523,Toy horsey,150 +2524,Toy horsey,150 +2525,Toy horsey,150 +2526,Toy horsey,150 +2527,Toy horsey,150 +2528,Lamp,1 +2529,Orb of light,1 +2530,Bones,1 +2531,Bones,1 +2532,Iron fire arrows,3 +2533,Iron fire arrows,10 +2534,Steel fire arrows,3 +2535,Steel fire arrows,10 +2536,Mithril fire arrows,3 +2537,Mithril fire arrows,10 +2538,Adamant fire arrows,3 +2539,Adamant fire arrows,10 +2540,Rune fire arrows,3 +2541,Rune fire arrows,10 +2542,null,1 +2543,null,1 +2544,null,1 +2545,null,1 +2546,null,1 +2547,null,1 +2548,null,1 +2549,null,1 +2550,Ring of recoil,900 +2551,Ring of recoil,900 +2552,Ring of duelling(8),1275 +2553,Ring of duelling(8),1275 +2554,Ring of duelling(7),1275 +2555,Ring of duelling(7),1275 +2556,Ring of duelling(6),1275 +2557,Ring of duelling(6),1275 +2558,Ring of duelling(5),1275 +2559,Ring of duelling(5),1275 +2560,Ring of duelling(4),1275 +2561,Ring of duelling(4),1275 +2562,Ring of duelling(3),1275 +2563,Ring of duelling(3),1275 +2564,Ring of duelling(2),1275 +2565,Ring of duelling(2),1275 +2566,Ring of duelling(1),1275 +2567,Ring of duelling(1),1275 +2568,Ring of forging,2025 +2569,Ring of forging,2025 +2570,Ring of life,3525 +2571,Ring of life,3525 +2572,Ring of wealth,17625 +2573,Ring of wealth,17625 +2574,Sextant,50 +2575,Watch,100 +2576,Chart,2 +2577,Ranger boots,200 +2578,Ranger boots,200 +2579,Wizard boots,200 +2580,Wizard boots,200 +2581,Robin hood hat,450 +2582,Robin hood hat,450 +2583,Black platebody (t),3840 +2584,Black platebody (t),3840 +2585,Black platelegs (t),1920 +2586,Black platelegs (t),1920 +2587,Black full helm(t),1056 +2588,Black full helm(t),1056 +2589,Black kiteshield (t),1632 +2590,Black kiteshield (t),1632 +2591,Black platebody (g),3840 +2592,Black platebody (g),3840 +2593,Black platelegs (g),1920 +2594,Black platelegs (g),1920 +2595,Black full helm(g),1056 +2596,Black full helm(g),1056 +2597,Black kiteshield (g),1632 +2598,Black kiteshield (g),1632 +2599,Adam platebody (t),12800 +2600,Adam platebody (t),12800 +2601,Adam platelegs (t),6400 +2602,Adam platelegs (t),6400 +2603,Adam kiteshield (t),5440 +2604,Adam kiteshield (t),5440 +2605,Adam full helm(t),3520 +2606,Adam full helm(t),3520 +2607,Adam platebody (g),12800 +2608,Adam platebody (g),12800 +2609,Adam platelegs (g),6400 +2610,Adam platelegs (g),6400 +2611,Adam kiteshield (g),5440 +2612,Adam kiteshield (g),5440 +2613,Adam full helm(g),3520 +2614,Adam full helm(g),3520 +2615,Rune platebody (g),65000 +2616,Rune platebody (g),65000 +2617,Rune platelegs (g),64000 +2618,Rune platelegs (g),64000 +2619,Rune full helm(g),35200 +2620,Rune full helm(g),35200 +2621,Rune kiteshield (g),54400 +2622,Rune kiteshield (g),54400 +2623,Rune platebody (t),65000 +2624,Rune platebody (t),65000 +2625,Rune platelegs (t),64000 +2626,Rune platelegs (t),64000 +2627,Rune full helm (t),35200 +2628,Rune full helm (t),35200 +2629,Rune kiteshield (t),54400 +2630,Rune kiteshield (t),54400 +2631,Highwayman mask,40 +2632,Highwayman mask,40 +2633,Blue beret,80 +2634,Blue beret,80 +2635,Black beret,80 +2636,Black beret,80 +2637,White beret,80 +2638,White beret,80 +2639,Tan cavalier,200 +2640,Tan cavalier,200 +2641,Dark cavalier,200 +2642,Dark cavalier,200 +2643,Black cavalier,200 +2644,Black cavalier,200 +2645,Red headband,40 +2646,Red headband,40 +2647,Black headband,40 +2648,Black headband,40 +2649,Brown headband,40 +2650,Brown headband,40 +2651,Pirate's hat,180 +2652,Pirate's hat,180 +2653,Zamorak platebody,65000 +2654,Zamorak platebody,65000 +2655,Zamorak platelegs,64000 +2656,Zamorak platelegs,64000 +2657,Zamorak full helm,35200 +2658,Zamorak full helm,35200 +2659,Zamorak kiteshield,54400 +2660,Zamorak kiteshield,54400 +2661,Saradomin platebody,65000 +2662,Saradomin platebody,65000 +2663,Saradomin platelegs,64000 +2664,Saradomin platelegs,64000 +2665,Saradomin full helm,35200 +2666,Saradomin full helm,35200 +2667,Saradomin kiteshield,54400 +2668,Saradomin kiteshield,54400 +2669,Guthix platebody,65000 +2670,Guthix platebody,65000 +2671,Guthix platelegs,64000 +2672,Guthix platelegs,64000 +2673,Guthix full helm,35200 +2674,Guthix full helm,35200 +2675,Guthix kiteshield,54400 +2676,Guthix kiteshield,54400 +2677,Clue scroll,1 +2678,Clue scroll,1 +2679,Clue scroll,1 +2680,Clue scroll,1 +2681,Clue scroll,1 +2682,Clue scroll,1 +2683,Clue scroll,1 +2684,Clue scroll,1 +2685,Clue scroll,1 +2686,Clue scroll,1 +2687,Clue scroll,1 +2688,Clue scroll,1 +2689,Clue scroll,1 +2690,Clue scroll,1 +2691,Clue scroll,1 +2692,Clue scroll,1 +2693,Clue scroll,1 +2694,Clue scroll,1 +2695,Clue scroll,1 +2696,Clue scroll,1 +2697,Clue scroll,1 +2698,Clue scroll,1 +2699,Clue scroll,1 +2700,Clue scroll,1 +2701,Clue scroll,1 +2702,Clue scroll,1 +2703,Clue scroll,1 +2704,Clue scroll,1 +2705,Clue scroll,1 +2706,Clue scroll,1 +2707,Clue scroll,1 +2708,Clue scroll,1 +2709,Clue scroll,1 +2710,Clue scroll,1 +2711,Clue scroll,1 +2712,Clue scroll,1 +2713,Clue scroll,1 +2714,Casket,50 +2715,Casket,50 +2716,Clue scroll,1 +2717,Casket,50 +2718,Casket,50 +2719,Clue scroll,1 +2720,Casket,50 +2721,Casket,50 +2722,Clue scroll,1 +2723,Clue scroll,1 +2724,Casket,50 +2725,Clue scroll,1 +2726,Casket,50 +2727,Clue scroll,1 +2728,Casket,50 +2729,Clue scroll,1 +2730,Casket,50 +2731,Clue scroll,1 +2732,Casket,50 +2733,Clue scroll,1 +2734,Casket,50 +2735,Clue scroll,1 +2736,Casket,50 +2737,Clue scroll,1 +2738,Casket,50 +2739,Clue scroll,1 +2740,Casket,50 +2741,Clue scroll,1 +2742,Casket,50 +2743,Clue scroll,1 +2744,Casket,50 +2745,Clue scroll,1 +2746,Casket,50 +2747,Clue scroll,1 +2748,Casket,50 +2749,Sliding piece,1 +2750,Sliding piece,1 +2751,Sliding piece,1 +2752,Sliding piece,1 +2753,Sliding piece,1 +2754,Sliding piece,1 +2755,Sliding piece,1 +2756,Sliding piece,1 +2757,Sliding piece,1 +2758,Sliding piece,1 +2759,Sliding piece,1 +2760,Sliding piece,1 +2761,Sliding piece,1 +2762,Sliding piece,1 +2763,Sliding piece,1 +2764,Sliding piece,1 +2765,Sliding piece,1 +2766,Sliding piece,1 +2767,Sliding piece,1 +2768,Sliding piece,1 +2769,Sliding piece,1 +2770,Sliding piece,1 +2771,Sliding piece,1 +2772,Sliding piece,1 +2773,Clue scroll,1 +2774,Clue scroll,1 +2775,Casket,50 +2776,Clue scroll,1 +2777,Casket,50 +2778,Clue scroll,1 +2779,Casket,50 +2780,Clue scroll,1 +2781,Casket,50 +2782,Clue scroll,1 +2783,Clue scroll,1 +2784,Casket,50 +2785,Clue scroll,1 +2786,Clue scroll,1 +2787,Casket,50 +2788,Clue scroll,1 +2789,Casket,50 +2790,Clue scroll,1 +2791,Casket,50 +2792,Clue scroll,1 +2793,Clue scroll,1 +2794,Clue scroll,1 +2795,Puzzle box,100 +2796,Clue scroll,1 +2797,Clue scroll,1 +2798,Puzzle box,100 +2799,Clue scroll,1 +2800,Puzzle box,100 +2801,Clue scroll,1 +2802,Casket,50 +2803,Clue scroll,1 +2804,Casket,50 +2805,Clue scroll,1 +2806,Casket,50 +2807,Clue scroll,1 +2808,Casket,50 +2809,Clue scroll,1 +2810,Casket,50 +2811,Clue scroll,1 +2812,Casket,50 +2813,Clue scroll,1 +2814,Casket,50 +2815,Clue scroll,1 +2816,Casket,50 +2817,Clue scroll,1 +2818,Casket,50 +2819,Clue scroll,1 +2820,Casket,50 +2821,Clue scroll,1 +2822,Casket,50 +2823,Clue scroll,1 +2824,Casket,50 +2825,Clue scroll,1 +2826,Casket,50 +2827,Clue scroll,1 +2828,Casket,50 +2829,Clue scroll,1 +2830,Casket,50 +2831,Clue scroll,1 +2832,Key,1 +2833,Clue scroll,1 +2834,Key,1 +2835,Clue scroll,1 +2836,Key,1 +2837,Clue scroll,1 +2838,Key,1 +2839,Clue scroll,1 +2840,Key,1 +2841,Clue scroll,1 +2842,Challenge scroll,1 +2843,Clue scroll,1 +2844,Challenge scroll,1 +2845,Clue scroll,1 +2846,Challenge scroll,1 +2847,Clue scroll,1 +2848,Clue scroll,1 +2849,Clue scroll,1 +2850,Challenge scroll,1 +2851,Clue scroll,1 +2852,Challenge scroll,1 +2853,Clue scroll,1 +2854,Challenge scroll,1 +2855,Clue scroll,1 +2856,Clue scroll,1 +2857,Clue scroll,1 +2858,Clue scroll,1 +2859,Wolf bones,1 +2860,Wolf bones,1 +2861,Wolfbone arrowtips,3 +2862,Achey tree logs,4 +2863,Achey tree logs,4 +2864,Ogre arrow shaft,1 +2865,Flighted ogre arrow,1 +2866,Ogre arrow,25 +2867,null,1 +2868,null,1 +2869,null,1 +2870,null,1 +2871,Ogre bellows,1 +2872,Ogre bellows (3),1 +2873,Ogre bellows (2),1 +2874,Ogre bellows (1),1 +2875,Bloated toad,1 +2876,Raw chompy,85 +2877,Raw chompy,85 +2878,Cooked chompy,130 +2879,Cooked chompy,130 +2880,Ruined chompy,1 +2881,Ruined chompy,1 +2882,Seasoned chompy,10 +2883,Ogre bow,500 +2884,null,1 +2885,null,1 +2886,Battered book,10 +2887,Battered key,1 +2888,A stone bowl,1 +2889,A stone bowl,1 +2890,Elemental shield,20 +2891,Elemental shield,20 +2892,Elemental ore,5 +2893,Elemental metal,8 +2894,Boots,650 +2895,Boots,650 +2896,Robe top,650 +2897,Robe top,650 +2898,Robe bottoms,650 +2899,Robe bottoms,650 +2900,Hat,650 +2901,Hat,650 +2902,Gloves,650 +2903,Gloves,650 +2904,Boots,650 +2905,Boots,650 +2906,Robe top,650 +2907,Robe top,650 +2908,Robe bottoms,650 +2909,Robe bottoms,650 +2910,Hat,650 +2911,Hat,650 +2912,Gloves,650 +2913,Gloves,650 +2914,Boots,650 +2915,Boots,650 +2916,Robe top,650 +2917,Robe top,650 +2918,Robe bottoms,650 +2919,Robe bottoms,650 +2920,Hat,650 +2921,Hat,650 +2922,Gloves,650 +2923,Gloves,650 +2924,Boots,650 +2925,Boots,650 +2926,Robe top,650 +2927,Robe top,650 +2928,Robe bottoms,650 +2929,Robe bottoms,650 +2930,Hat,650 +2931,Hat,650 +2932,Gloves,650 +2933,Gloves,650 +2934,Boots,650 +2935,Boots,650 +2936,Robe top,650 +2937,Robe top,650 +2938,Robe bottoms,650 +2939,Robe bottoms,650 +2940,Hat,650 +2941,Hat,650 +2942,Gloves,650 +2943,Gloves,650 +2944,Golden key,300 +2945,Iron key,2 +2946,Golden tinderbox,300 +2947,Golden candle,300 +2948,Golden pot,300 +2949,Golden hammer,300 +2950,Golden feather,300 +2951,Golden needle,300 +2952,Wolfbane,300 +2953,Bucket of water,6 +2954,Bucket of water,6 +2955,Moonlight mead,5 +2956,Moonlight mead,5 +2957,Druid pouch,5 +2958,Druid pouch,5 +2959,Rotten food,1 +2960,Rotten food,1 +2961,Silver sickle,175 +2962,Silver sickle,175 +2963,Silver sickle(b),250 +2964,Washing bowl,4 +2965,Picture,0 +2966,Mirror,1 +2967,Journal,1 +2968,Druidic spell,1 +2969,A used spell,1 +2970,Mort myre fungus,1 +2971,Mort myre fungus,1 +2972,Mort myre stem,1 +2973,Mort myre stem,1 +2974,Mort myre pear,1 +2975,Mort myre pear,1 +2976,Sickle mould,10 +2977,Sickle mould,10 +2978,Chompy bird hat,1 +2979,Chompy bird hat,1 +2980,Chompy bird hat,1 +2981,Chompy bird hat,1 +2982,Chompy bird hat,1 +2983,Chompy bird hat,1 +2984,Chompy bird hat,1 +2985,Chompy bird hat,1 +2986,Chompy bird hat,1 +2987,Chompy bird hat,1 +2988,Chompy bird hat,1 +2989,Chompy bird hat,1 +2990,Chompy bird hat,1 +2991,Chompy bird hat,1 +2992,Chompy bird hat,1 +2993,Chompy bird hat,1 +2994,Chompy bird hat,1 +2995,Chompy bird hat,1 +2996,Agility arena ticket,1 +2997,Pirate's hook,89 +2998,Clean toadflax,48 +2999,Clean toadflax,48 +3000,Clean snapdragon,59 +3001,Clean snapdragon,59 +3002,Toadflax potion(unf),48 +3003,Toadflax potion(unf),48 +3004,Snapdragon potion(unf),59 +3005,Snapdragon potion(unf),59 +3006,Firework,60 +3007,Firework,60 +3008,Energy potion(4),146 +3009,Energy potion(4),146 +3010,Energy potion(3),110 +3011,Energy potion(3),110 +3012,Energy potion(2),72 +3013,Energy potion(2),72 +3014,Energy potion(1),36 +3015,Energy potion(1),36 +3016,Super energy(4),300 +3017,Super energy(4),300 +3018,Super energy(3),230 +3019,Super energy(3),230 +3020,Super energy(2),160 +3021,Super energy(2),160 +3022,Super energy(1),90 +3023,Super energy(1),90 +3024,Super restore(4),300 +3025,Super restore(4),300 +3026,Super restore(3),240 +3027,Super restore(3),240 +3028,Super restore(2),180 +3029,Super restore(2),180 +3030,Super restore(1),120 +3031,Super restore(1),120 +3032,Agility potion(4),200 +3033,Agility potion(4),200 +3034,Agility potion(3),150 +3035,Agility potion(3),150 +3036,Agility potion(2),100 +3037,Agility potion(2),100 +3038,Agility potion(1),50 +3039,Agility potion(1),50 +3040,Magic potion(4),300 +3041,Magic potion(4),300 +3042,Magic potion(3),250 +3043,Magic potion(3),250 +3044,Magic potion(2),200 +3045,Magic potion(2),200 +3046,Magic potion(1),150 +3047,Magic potion(1),150 +3048,Pirate's hook,89 +3049,Grimy toadflax,1 +3050,Grimy toadflax,1 +3051,Grimy snapdragon,1 +3052,Grimy snapdragon,1 +3053,Lava battlestaff,17000 +3054,Mystic lava staff,45000 +3055,Lava battlestaff,17000 +3056,Mystic lava staff,45000 +3057,Mime mask,1 +3058,Mime top,1 +3059,Mime legs,1 +3060,Mime gloves,1 +3061,Mime boots,1 +3062,Strange box,1 +3063,null,1 +3064,null,1 +3065,null,1 +3066,null,1 +3067,null,1 +3068,null,1 +3069,null,1 +3070,null,1 +3071,null,1 +3072,null,1 +3073,null,1 +3074,null,1 +3075,null,1 +3076,null,1 +3077,null,1 +3078,null,1 +3079,null,1 +3080,null,1 +3081,null,1 +3082,null,1 +3083,null,1 +3084,null,1 +3085,null,1 +3086,null,1 +3087,null,1 +3088,null,1 +3089,null,1 +3090,null,1 +3091,null,1 +3092,null,1 +3093,Black dart,1 +3094,Black dart(p),18 +3095,Bronze claws,15 +3096,Iron claws,50 +3097,Steel claws,175 +3098,Black claws,360 +3099,Mithril claws,475 +3100,Adamant claws,1200 +3101,Rune claws,12000 +3102,Combination,1 +3103,Iou,1 +3104,Secret way map,1 +3105,Climbing boots,12 +3106,Climbing boots,12 +3107,Spiked boots,30 +3108,Picture,0 +3109,Stone ball,1 +3110,Stone ball,1 +3111,Stone ball,1 +3112,Stone ball,1 +3113,Stone ball,1 +3114,Certificate,1 +3115,Bronze claws,15 +3116,Iron claws,50 +3117,Steel claws,175 +3118,Black claws,360 +3119,Mithril claws,475 +3120,Adamant claws,1200 +3121,Rune claws,12000 +3122,Granite shield,56000 +3123,Shaikahan bones,1 +3124,Shaikahan bones,1 +3125,Jogre bones,1 +3126,Jogre bones,1 +3127,Burnt jogre bones,1 +3128,Pasty jogre bones,1 +3129,Pasty jogre bones,1 +3130,Marinated j' bones,1 +3131,Pasty jogre bones,1 +3132,Pasty jogre bones,1 +3133,Marinated j' bones,1 +3134,Granite shield,56000 +3135,Prison key,1 +3136,Cell key 1,1 +3137,Cell key 2,1 +3138,Potato cactus,1 +3139,Potato cactus,1 +3140,Dragon chainbody,250000 +3141,Dragon chainbody,250000 +3142,Raw karambwan,440 +3143,Raw karambwan,440 +3144,Cooked karambwan,460 +3145,Cooked karambwan,460 +3146,Poison karambwan,250 +3147,Cooked karambwan,250 +3148,Burnt karambwan,1 +3149,Burnt karambwan,1 +3150,Raw karambwanji,10 +3151,Karambwanji,10 +3152,Karambwan paste,15 +3153,Karambwan paste,15 +3154,Karambwan paste,15 +3155,Karambwanji paste,15 +3156,Karambwanji paste,15 +3157,Karambwan vessel,5 +3158,Karambwan vessel,5 +3159,Karambwan vessel,40 +3160,Karambwan vessel,40 +3161,Crafting manual,5 +3162,Sliced banana,2 +3163,Sliced banana,2 +3164,Karamjan rum,30 +3165,Karamjan rum,30 +3166,Monkey corpse,1 +3167,Monkey skin,10 +3168,Seaweed sandwich,1 +3169,Stuffed monkey,1 +3170,Bronze spear(kp),26 +3171,Iron spear(kp),91 +3172,Steel spear(kp),325 +3173,Mithril spear(kp),845 +3174,Adamant spear(kp),2080 +3175,Rune spear(kp),20800 +3176,Dragon spear(kp),62400 +3177,Picture,2 +3178,Picture,2 +3179,Monkey bones,1 +3180,Monkey bones,1 +3181,Monkey bones,1 +3182,Monkey bones,1 +3183,Monkey bones,1 +3184,Monkey bones,1 +3185,Monkey bones,1 +3186,Monkey bones,1 +3187,Bones,0 +3188,Cleaning cloth,60 +3189,Cleaning cloth,60 +3190,Bronze halberd,104 +3191,Bronze halberd,104 +3192,Iron halberd,364 +3193,Iron halberd,364 +3194,Steel halberd,1300 +3195,Steel halberd,1300 +3196,Black halberd,2496 +3197,Black halberd,2496 +3198,Mithril halberd,3380 +3199,Mithril halberd,3380 +3200,Adamant halberd,8320 +3201,Adamant halberd,8320 +3202,Rune halberd,128000 +3203,Rune halberd,128000 +3204,Dragon halberd,325000 +3205,Dragon halberd,325000 +3206,King's message,1 +3207,Iorwerths message,1 +3208,Crystal pendant,10 +3209,Sulphur,1 +3210,Tinderbox,0 +3211,Limestone,10 +3212,Limestone,10 +3213,Quicklime,4 +3214,Pot of quicklime,5 +3215,Ground sulphur,5 +3216,Barrel,1 +3217,Barrel,1 +3218,Barrel bomb,1 +3219,Barrel bomb,1 +3220,Barrel of coal-tar,1 +3221,Barrel of naphtha,1 +3222,Naphtha mix,1 +3223,Naphtha mix,1 +3224,Strip of cloth,10 +3225,Tinderbox,0 +3226,Raw rabbit,20 +3227,Raw rabbit,20 +3228,Cooked rabbit,4 +3229,Cooked rabbit,4 +3230,Big book of bangs,1 +3231,Picture,1 +3232,Picture,1 +3233,Picture,1 +3234,Picture,1 +3235,Picture,1 +3236,Picture,1 +3237,Picture,1 +3238,Picture,1 +3239,Bark,1 +3240,Bark,1 +3241,Man,1 +3242,null,1 +3243,Farmer,1 +3244,null,1 +3245,Warrior woman,1 +3246,null,1 +3247,Rogue,1 +3248,null,1 +3249,Guard,1 +3250,null,1 +3251,Knight of ardougne,1 +3252,null,1 +3253,Watchman,1 +3254,null,1 +3255,Paladin,1 +3256,null,1 +3257,Gnome,1 +3258,null,1 +3259,Hero,1 +3260,null,1 +3261,Goutweed,1 +3262,Troll thistle,1 +3263,Dried thistle,1 +3264,Ground thistle,1 +3265,Troll potion,1 +3266,Drunk parrot,1 +3267,Dirty robe,1 +3268,Fake man,1 +3269,Storeroom key,1 +3270,Alco-chunks,1 +3271,null,1 +3272,null,1 +3273,null,1 +3274,null,1 +3275,null,1 +3276,null,1 +3277,null,1 +3278,null,1 +3279,null,1 +3280,null,1 +3281,null,1 +3282,null,1 +3283,null,1 +3284,null,1 +3285,null,1 +3286,null,1 +3287,null,1 +3288,null,1 +3289,null,1 +3290,null,1 +3291,null,1 +3292,null,1 +3293,null,1 +3294,null,1 +3295,null,1 +3296,null,1 +3297,null,1 +3298,null,1 +3299,null,1 +3300,null,1 +3301,null,1 +3302,null,1 +3303,null,1 +3304,null,1 +3305,null,1 +3306,null,1 +3307,null,1 +3308,null,1 +3309,null,1 +3310,null,1 +3311,null,1 +3312,null,1 +3313,null,1 +3314,null,1 +3315,null,1 +3316,null,1 +3317,null,1 +3318,null,1 +3319,null,1 +3320,null,1 +3321,null,1 +3322,null,1 +3323,null,1 +3324,null,1 +3325,Vampire dust,2 +3326,Vampire dust,2 +3327,Myre snelm,300 +3328,Myre snelm,300 +3329,Blood'n'tar snelm,300 +3330,Blood'n'tar snelm,300 +3331,Ochre snelm,300 +3332,Ochre snelm,300 +3333,Bruise blue snelm,300 +3334,Bruise blue snelm,300 +3335,Broken bark snelm,300 +3336,Broken bark snelm,300 +3337,Myre snelm,300 +3338,Myre snelm,300 +3339,Blood'n'tar snelm,300 +3340,Blood'n'tar snelm,300 +3341,Ochre snelm,300 +3342,Ochre snelm,300 +3343,Bruise blue snelm,300 +3344,Bruise blue snelm,300 +3345,Blamish myre shell,150 +3346,Blamish myre shell,150 +3347,Blamish red shell,150 +3348,Blamish red shell,150 +3349,Blamish ochre shell,150 +3350,Blamish ochre shell,150 +3351,Blamish blue shell,150 +3352,Blamish blue shell,150 +3353,Blamish bark shell,150 +3354,Blamish bark shell,150 +3355,Blamish myre shell,150 +3356,Blamish myre shell,150 +3357,Blamish red shell,150 +3358,Blamish red shell,150 +3359,Blamish ochre shell,150 +3360,Blamish ochre shell,150 +3361,Blamish blue shell,150 +3362,Blamish blue shell,150 +3363,Thin snail,60 +3364,Thin snail,60 +3365,Lean snail,10 +3366,Lean snail,10 +3367,Fat snail,15 +3368,Fat snail,15 +3369,Thin snail meat,10 +3370,Thin snail meat,10 +3371,Lean snail meat,20 +3372,Lean snail meat,20 +3373,Fat snail meat,30 +3374,Fat snail meat,30 +3375,Burnt snail,10 +3376,Burnt snail,10 +3377,Sample bottle,5 +3378,Sample bottle,5 +3379,Slimy eel,1 +3380,Slimy eel,1 +3381,Cooked slimy eel,1 +3382,Cooked slimy eel,1 +3383,Burnt eel,1 +3384,Tinderbox,0 +3385,Splitbark helm,10000 +3386,Splitbark helm,10000 +3387,Splitbark body,45000 +3388,Splitbark body,45000 +3389,Splitbark legs,40000 +3390,Splitbark legs,40000 +3391,Splitbark gauntlets,5000 +3392,Splitbark gauntlets,5000 +3393,Splitbark boots,5000 +3394,Splitbark boots,5000 +3395,Diary,1 +3396,Loar remains,1 +3397,Loar remains,1 +3398,Phrin remains,1 +3399,Phrin remains,1 +3400,Riyl remains,1 +3401,Riyl remains,1 +3402,Asyn remains,1 +3403,Asyn remains,1 +3404,Fiyr remains,1 +3405,Fiyr remains,1 +3406,Ash potion(unf),11 +3407,Ash potion(unf),11 +3408,Serum 207 (4),14 +3409,Tinderbox,0 +3410,Serum 207 (3),13 +3411,Tinderbox,0 +3412,Serum 207 (2),13 +3413,Tinderbox,0 +3414,Serum 207 (1),11 +3415,Tinderbox,0 +3416,Serum 208 (4),14 +3417,Serum 208 (3),13 +3418,Serum 208 (2),13 +3419,Serum 208 (1),11 +3420,Limestone brick,21 +3421,Limestone brick,21 +3422,Olive oil(4),22 +3423,Olive oil(4),22 +3424,Olive oil(3),26 +3425,Olive oil(3),26 +3426,Olive oil(2),17 +3427,Olive oil(2),17 +3428,Olive oil(1),14 +3429,Olive oil(1),14 +3430,Sacred oil(4),100 +3431,Sacred oil(4),100 +3432,Sacred oil(3),90 +3433,Sacred oil(3),90 +3434,Sacred oil(2),75 +3435,Sacred oil(2),75 +3436,Sacred oil(1),60 +3437,Sacred oil(1),60 +3438,Pyre logs,8 +3439,Pyre logs,8 +3440,Oak pyre logs,40 +3441,Oak pyre logs,40 +3442,Willow pyre logs,80 +3443,Willow pyre logs,80 +3444,Maple pyre logs,160 +3445,Maple pyre logs,160 +3446,Yew pyre logs,320 +3447,Yew pyre logs,320 +3448,Magic pyre logs,640 +3449,Magic pyre logs,640 +3450,Bronze key red,81 +3451,Bronze key brown,82 +3452,Bronze key crimson,83 +3453,Bronze key black,84 +3454,Bronze key purple,85 +3455,Steel key red,86 +3456,Steel key brown,87 +3457,Steel key crimson,88 +3458,Steel key black,89 +3459,Steel key purple,90 +3460,Black key red,91 +3461,Black key brown,92 +3462,Black key crimson,93 +3463,Black key black,94 +3464,Black key purple,95 +3465,Silver key red,96 +3466,Silver key brown,97 +3467,Silver key crimson,98 +3468,Silver key black,99 +3469,Silver key purple,100 +3470,Fine cloth,500 +3471,Fine cloth,500 +3472,Black plateskirt (t),1920 +3473,Black plateskirt (g),1920 +3474,Adam plateskirt (t),6400 +3475,Adam plateskirt (g),6400 +3476,Rune plateskirt (g),64000 +3477,Rune plateskirt (t),64000 +3478,Zamorak plateskirt,64000 +3479,Saradomin plateskirt,64000 +3480,Guthix plateskirt,64000 +3481,Gilded platebody,65000 +3482,Gilded platebody,65000 +3483,Gilded platelegs,64000 +3484,Gilded platelegs,64000 +3485,Gilded plateskirt,64000 +3486,Gilded full helm,35200 +3487,Gilded full helm,35200 +3488,Gilded kiteshield,54400 +3489,Gilded kiteshield,54400 +3490,Clue scroll,1 +3491,Clue scroll,1 +3492,Clue scroll,1 +3493,Clue scroll,1 +3494,Clue scroll,1 +3495,Clue scroll,1 +3496,Clue scroll,1 +3497,Clue scroll,1 +3498,Clue scroll,1 +3499,Clue scroll,1 +3500,Clue scroll,1 +3501,Clue scroll,1 +3502,Clue scroll,1 +3503,Clue scroll,1 +3504,Clue scroll,1 +3505,Clue scroll,1 +3506,Clue scroll,1 +3507,Clue scroll,1 +3508,Clue scroll,1 +3509,Clue scroll,1 +3510,Clue scroll,1 +3511,Casket,50 +3512,Clue scroll,1 +3513,Clue scroll,1 +3514,Clue scroll,1 +3515,Clue scroll,1 +3516,Clue scroll,1 +3517,Casket,50 +3518,Clue scroll,1 +3519,Casket,50 +3520,Clue scroll,1 +3521,Casket,50 +3522,Clue scroll,1 +3523,Casket,50 +3524,Clue scroll,1 +3525,Clue scroll,1 +3526,Clue scroll,1 +3527,Casket,50 +3528,Clue scroll,1 +3529,Casket,50 +3530,Clue scroll,1 +3531,Casket,50 +3532,Clue scroll,1 +3533,Casket,50 +3534,Clue scroll,1 +3535,Casket,50 +3536,Clue scroll,1 +3537,Casket,50 +3538,Clue scroll,1 +3539,Casket,50 +3540,Clue scroll,1 +3541,Casket,50 +3542,Clue scroll,1 +3543,Casket,50 +3544,Clue scroll,1 +3545,Casket,50 +3546,Clue scroll,1 +3547,Casket,50 +3548,Clue scroll,1 +3549,Casket,50 +3550,Clue scroll,1 +3551,Casket,50 +3552,Clue scroll,1 +3553,Casket,50 +3554,Clue scroll,1 +3555,Casket,50 +3556,Clue scroll,1 +3557,Casket,50 +3558,Clue scroll,1 +3559,Casket,50 +3560,Clue scroll,1 +3561,Casket,50 +3562,Clue scroll,1 +3563,Casket,50 +3564,Clue scroll,1 +3565,Puzzle box,100 +3566,Clue scroll,1 +3567,Puzzle box,100 +3568,Clue scroll,1 +3569,Puzzle box,100 +3570,Clue scroll,1 +3571,Puzzle box,100 +3572,Clue scroll,1 +3573,Clue scroll,1 +3574,Clue scroll,1 +3575,Clue scroll,1 +3576,Puzzle box,100 +3577,Clue scroll,1 +3578,Puzzle box,100 +3579,Clue scroll,1 +3580,Clue scroll,1 +3581,Casket,50 +3582,Clue scroll,1 +3583,Casket,50 +3584,Clue scroll,1 +3585,Casket,50 +3586,Clue scroll,1 +3587,Casket,50 +3588,Clue scroll,1 +3589,Casket,50 +3590,Clue scroll,1 +3591,Casket,50 +3592,Clue scroll,1 +3593,Casket,50 +3594,Clue scroll,1 +3595,Casket,50 +3596,Clue scroll,1 +3597,Casket,50 +3598,Clue scroll,1 +3599,Clue scroll,1 +3600,Casket,50 +3601,Clue scroll,1 +3602,Clue scroll,1 +3603,Casket,50 +3604,Clue scroll,1 +3605,Clue scroll,1 +3606,Key,1 +3607,Clue scroll,1 +3608,Key,1 +3609,Clue scroll,1 +3610,Clue scroll,1 +3611,Clue scroll,1 +3612,Clue scroll,1 +3613,Clue scroll,1 +3614,Clue scroll,1 +3615,Clue scroll,1 +3616,Clue scroll,1 +3617,Clue scroll,1 +3618,Clue scroll,1 +3619,Sliding piece,1 +3620,Sliding piece,1 +3621,Sliding piece,1 +3622,Sliding piece,1 +3623,Sliding piece,1 +3624,Sliding piece,1 +3625,Sliding piece,1 +3626,Sliding piece,1 +3627,Sliding piece,1 +3628,Sliding piece,1 +3629,Sliding piece,1 +3630,Sliding piece,1 +3631,Sliding piece,1 +3632,Sliding piece,1 +3633,Sliding piece,1 +3634,Sliding piece,1 +3635,Sliding piece,1 +3636,Sliding piece,1 +3637,Sliding piece,1 +3638,Sliding piece,1 +3639,Sliding piece,1 +3640,Sliding piece,1 +3641,Sliding piece,1 +3642,Sliding piece,1 +3643,Sliding piece,1 +3644,Sliding piece,1 +3645,Sliding piece,1 +3646,Sliding piece,1 +3647,Sliding piece,1 +3648,Sliding piece,1 +3649,Sliding piece,1 +3650,Sliding piece,1 +3651,Sliding piece,1 +3652,Sliding piece,1 +3653,Sliding piece,1 +3654,Sliding piece,1 +3655,Sliding piece,1 +3656,Sliding piece,1 +3657,Sliding piece,1 +3658,Sliding piece,1 +3659,Sliding piece,1 +3660,Sliding piece,1 +3661,Sliding piece,1 +3662,Sliding piece,1 +3663,Sliding piece,1 +3664,Sliding piece,1 +3665,Sliding piece,1 +3666,Sliding piece,1 +3667,Picture,1 +3668,Black plateskirt (t),1920 +3669,Black plateskirt (g),1920 +3670,Adam plateskirt (t),6400 +3671,Adam plateskirt (g),6400 +3672,Rune plateskirt (g),64000 +3673,Rune plateskirt (t),64000 +3674,Zamorak plateskirt,64000 +3675,Saradomin plateskirt,64000 +3676,Guthix plateskirt,64000 +3677,Gilded plateskirt,64000 +3678,Flamtaer hammer,13000 +3679,Picture,0 +3680,Picture,1 +3681,Picture,1 +3682,Picture,1 +3683,Picture,1 +3684,Picture,1 +3685,Picture,1 +3686,Fremennik,1 +3687,null,1 +3688,Unstrung lyre,1 +3689,Lyre,1 +3690,Enchanted lyre,1 +3691,Enchanted lyre(1),1000 +3692,Branch,1 +3693,Golden fleece,1 +3694,Golden wool,1 +3695,Pet rock,1 +3696,Hunters' talisman,4 +3697,Hunters' talisman,4 +3698,Exotic flower,1 +3699,Fremennik ballad,1 +3700,Sturdy boots,1 +3701,Tracking map,1 +3702,Custom bow string,1 +3703,Unusual fish,1 +3704,Sea fishing map,1 +3705,Weather forecast,1 +3706,Champions token,1 +3707,Legendary cocktail,1 +3708,Fiscal statement,1 +3709,Promissory note,1 +3710,Warriors' contract,1 +3711,Keg of beer,1 +3712,Low alcohol keg,1 +3713,Strange object,1 +3714,Lit strange object,1 +3715,Picture,1 +3716,Picture,1 +3717,null,1 +3718,Magnet,3 +3719,Thread,1 +3720,Pick,1 +3721,Ship toy,2 +3722,Full bucket,1 +3723,4/5ths full bucket,1 +3724,3/5ths full bucket,1 +3725,2/5ths full bucket,1 +3726,1/5ths full bucket,1 +3727,Empty bucket,1 +3728,Frozen bucket,1 +3729,Full jug,1 +3730,2/3rds full jug,1 +3731,1/3rds full jug,1 +3732,Empty jug,1 +3733,Frozen jug,1 +3734,Vase,1 +3735,Vase of water,1 +3736,Frozen vase,1 +3737,Vase lid,1 +3738,Sealed vase,1 +3739,Sealed vase,1 +3740,Sealed vase,1 +3741,Frozen key,1 +3742,Red herring,1 +3743,Red disk,1 +3744,Wooden disk,1 +3745,Seer's key,1 +3746,Sticky red goop,1 +3747,Sticky red goop,1 +3748,Fremennik helm,6500 +3749,Archer helm,78000 +3750,Archer helm,78000 +3751,Berserker helm,78000 +3752,Berserker helm,78000 +3753,Warrior helm,78000 +3754,Warrior helm,78000 +3755,Farseer helm,78000 +3756,Farseer helm,78000 +3757,Fremennik blade,5000 +3758,Fremennik shield,5000 +3759,Fremennik cloak,325 +3760,Fremennik cloak,325 +3761,Fremennik cloak,325 +3762,Fremennik cloak,325 +3763,Fremennik cloak,325 +3764,Fremennik cloak,325 +3765,Fremennik cloak,325 +3766,Fremennik cloak,325 +3767,Fremennik shirt,325 +3768,Fremennik shirt,325 +3769,Fremennik shirt,325 +3770,Fremennik shirt,325 +3771,Fremennik shirt,325 +3772,Fremennik shirt,325 +3773,Fremennik shirt,325 +3774,Fremennik shirt,325 +3775,Fremennik shirt,325 +3776,Fremennik shirt,325 +3777,Fremennik cloak,325 +3778,Fremennik cloak,325 +3779,Fremennik cloak,325 +3780,Fremennik cloak,325 +3781,Fremennik cloak,325 +3782,Fremennik cloak,325 +3783,Fremennik cloak,325 +3784,Fremennik cloak,325 +3785,Fremennik cloak,325 +3786,Fremennik cloak,325 +3787,Fremennik cloak,325 +3788,Fremennik cloak,325 +3789,Fremennik cloak,325 +3790,Fremennik cloak,325 +3791,Fremennik boots,650 +3792,Fremennik boots,650 +3793,Fremennik robe,650 +3794,Fremennik robe,650 +3795,Fremennik skirt,650 +3796,Fremennik skirt,650 +3797,Fremennik hat,650 +3798,Fremennik hat,650 +3799,Gloves,650 +3800,Gloves,650 +3801,Keg of beer,325 +3802,Keg of beer,325 +3803,Beer,26 +3804,Beer,26 +3805,Tankard,5 +3806,Tankard,5 +3807,null,1 +3808,null,1 +3809,null,1 +3810,null,1 +3811,null,1 +3812,null,1 +3813,null,1 +3814,null,1 +3815,null,1 +3816,null,1 +3817,null,1 +3818,null,1 +3819,null,1 +3820,null,1 +3821,null,1 +3822,null,1 +3823,null,1 +3824,null,1 +3825,null,1 +3826,null,1 +3827,Saradomin page 1,200 +3828,Saradomin page 2,200 +3829,Saradomin page 3,200 +3830,Saradomin page 4,200 +3831,Zamorak page 1,200 +3832,Zamorak page 2,200 +3833,Zamorak page 3,200 +3834,Zamorak page 4,200 +3835,Guthix page 1,200 +3836,Guthix page 2,200 +3837,Guthix page 3,200 +3838,Guthix page 4,200 +3839,Damaged book,200 +3840,Holy book,200 +3841,Damaged book,200 +3842,Unholy book,200 +3843,Damaged book,200 +3844,Book of balance,200 +3845,Journal,1 +3846,Diary,1 +3847,Manual,1 +3848,Lighthouse key,1 +3849,Rusty casket,1 +3850,null,1 +3851,null,1 +3852,Unholy symbol,200 +3853,Games necklace(8),1050 +3854,Games necklace(8),1050 +3855,Games necklace(7),1050 +3856,Games necklace(7),1050 +3857,Games necklace(6),1050 +3858,Games necklace(6),1050 +3859,Games necklace(5),1050 +3860,Games necklace(5),1050 +3861,Games necklace(4),1050 +3862,Games necklace(4),1050 +3863,Games necklace(3),1050 +3864,Games necklace(3),1050 +3865,Games necklace(2),1050 +3866,Games necklace(2),1050 +3867,Games necklace(1),1050 +3868,Games necklace(1),1050 +3869,Board game piece,1 +3870,Board game piece,1 +3871,Board game piece,1 +3872,Board game piece,1 +3873,Board game piece,1 +3874,Board game piece,1 +3875,Board game piece,1 +3876,Board game piece,1 +3877,Board game piece,1 +3878,Board game piece,1 +3879,Board game piece,1 +3880,Board game piece,1 +3881,null,1 +3882,null,1 +3883,null,1 +3884,null,1 +3885,null,1 +3886,null,1 +3887,null,1 +3888,null,1 +3889,null,1 +3890,null,1 +3891,null,1 +3892,null,1 +3893,Stool,800 +3894,Awful anthem,1 +3895,Good anthem,1 +3896,Treaty,1 +3897,Giant nib,1 +3898,Giant pen,1 +3899,Iron sickle,15 +3900,Picture,0 +3901,Ghrim's book,1 +3902,Picture,1 +3903,Picture,0 +3904,Sliding button,1 +3905,null,1 +3906,Sliding button,1 +3907,null,1 +3908,Sliding button,1 +3909,null,1 +3910,Sliding button,1 +3911,null,1 +3912,Sliding button,1 +3913,null,1 +3914,Sliding button,1 +3915,null,1 +3916,Sliding button,1 +3917,null,1 +3918,Sliding button,1 +3919,null,1 +3920,Sliding button,1 +3921,null,1 +3922,Sliding button,1 +3923,null,1 +3924,Sliding button,1 +3925,null,1 +3926,Sliding button,1 +3927,null,1 +3928,Sliding button,1 +3929,null,1 +3930,Sliding button,1 +3931,null,1 +3932,Sliding button,1 +3933,null,1 +3934,Sliding button,1 +3935,null,1 +3936,Sliding button,1 +3937,null,1 +3938,Sliding button,1 +3939,null,1 +3940,Sliding button,1 +3941,null,1 +3942,Sliding button,1 +3943,null,1 +3944,Sliding button,1 +3945,null,1 +3946,Sliding button,1 +3947,null,1 +3948,Sliding button,1 +3949,null,1 +3950,Sliding button,1 +3951,null,1 +3952,Sliding button,1 +3953,null,1 +3954,Sliding button,1 +3955,null,1 +3956,Sliding button,1 +3957,null,1 +3958,Sliding button,1 +3959,null,1 +3960,Sliding button,1 +3961,null,1 +3962,Sliding button,1 +3963,null,1 +3964,Sliding button,1 +3965,null,1 +3966,Sliding button,1 +3967,null,1 +3968,Sliding button,1 +3969,null,1 +3970,Sliding button,1 +3971,null,1 +3972,Sliding button,1 +3973,null,1 +3974,Sliding button,1 +3975,null,1 +3976,Sliding button,1 +3977,null,1 +3978,Sliding button,1 +3979,null,1 +3980,Sliding button,1 +3981,null,1 +3982,Sliding button,1 +3983,null,1 +3984,Sliding button,1 +3985,null,1 +3986,Sliding button,1 +3987,null,1 +3988,Sliding button,1 +3989,null,1 +3990,Sliding button,1 +3991,null,1 +3992,Sliding button,1 +3993,null,1 +3994,Sliding button,1 +3995,null,1 +3996,Sliding button,1 +3997,null,1 +3998,Sliding button,1 +3999,null,1 +4000,Picture,1 +4001,Hardy gout tuber,5 +4002,Spare controls,1 +4003,Picture,0 +4004,Gnome royal seal,1 +4005,Narnode's orders,1 +4006,Monkey dentures,10 +4007,Enchanted bar,800 +4008,Eye of gnome,3 +4009,Eye of gnome,3 +4010,Monkey magic,3 +4011,Picture,0 +4012,Monkey nuts,3 +4013,Picture,0 +4014,Monkey bar,50 +4015,Picture,0 +4016,Banana stew,300 +4017,Picture,0 +4018,Monkey wrench,10 +4019,Picture,0 +4020,M'amulet mould,10 +4021,M'speak amulet,550 +4022,M'speak amulet,500 +4023,Monkey talisman,1000 +4024,Monkey greegree,3000 +4025,Monkey greegree,6000 +4026,Monkey greegree,1000 +4027,Monkey greegree,1500 +4028,Monkey greegree,10000 +4029,Monkey greegree,500 +4030,Monkey greegree,2000 +4031,Monkey greegree,500 +4032,Dummy,1 +4033,Monkey,1 +4034,Monkey skull,1 +4035,10th squad sigil,1 +4036,null,1 +4037,Saradomin banner,1 +4038,Saradomin banner,1 +4039,Zamorak banner,1 +4040,Zamorak banner,1 +4041,Hooded cloak,1 +4042,Hooded cloak,1 +4043,Rock,1 +4044,Rock,1 +4045,Explosive potion,1 +4046,Explosive potion,1 +4047,Climbing rope,1 +4048,Climbing rope,1 +4049,Bandages,1 +4050,Bandages,1 +4051,Toolkit,1 +4052,Toolkit,1 +4053,Barricade,1 +4054,Barricade,1 +4055,Castlewars manual,5 +4056,null,1 +4057,null,1 +4058,null,1 +4059,null,1 +4060,null,1 +4061,null,1 +4062,null,1 +4063,null,1 +4064,null,1 +4065,null,1 +4066,null,1 +4067,Castle wars ticket,1 +4068,Decorative sword,500 +4069,Decorative armour,2000 +4070,Decorative armour,1000 +4071,Decorative helm,300 +4072,Decorative shield,850 +4073,Damp tinderbox,1 +4074,Damp tinderbox,1 +4075,Glowing fungus,1 +4076,Picture,1 +4077,Crystal-mine key,1 +4078,Zealot's key,1 +4079,Yo-yo,10 +4080,null,1 +4081,Salve amulet,1 +4082,Salve shard,1 +4083,Sled,150 +4084,Sled,175 +4085,Wax,15 +4086,Trollweiss,5 +4087,Dragon platelegs,270000 +4088,Dragon platelegs,270000 +4089,Mystic hat,15000 +4090,Mystic hat,15000 +4091,Mystic robe top,120000 +4092,Mystic robe top,120000 +4093,Mystic robe bottom,80000 +4094,Mystic robe bottom,80000 +4095,Mystic gloves,10000 +4096,Mystic gloves,10000 +4097,Mystic boots,10000 +4098,Mystic boots,10000 +4099,Mystic hat,15000 +4100,Mystic hat,15000 +4101,Mystic robe top,120000 +4102,Mystic robe top,120000 +4103,Mystic robe bottom,80000 +4104,Mystic robe bottom,80000 +4105,Mystic gloves,10000 +4106,Mystic gloves,10000 +4107,Mystic boots,10000 +4108,Mystic boots,10000 +4109,Mystic hat,15000 +4110,Mystic hat,15000 +4111,Mystic robe top,120000 +4112,Mystic robe top,120000 +4113,Mystic robe bottom,80000 +4114,Mystic robe bottom,80000 +4115,Mystic gloves,10000 +4116,Mystic gloves,10000 +4117,Mystic boots,10000 +4118,Mystic boots,10000 +4119,Bronze boots,24 +4120,Bronze boots,24 +4121,Iron boots,84 +4122,Iron boots,84 +4123,Steel boots,300 +4124,Steel boots,300 +4125,Black boots,576 +4126,Black boots,576 +4127,Mithril boots,780 +4128,Mithril boots,780 +4129,Adamant boots,1920 +4130,Adamant boots,1920 +4131,Rune boots,12500 +4132,Rune boots,12500 +4133,Crawling hand,1 +4134,Cave crawler,1 +4135,Banshee,1 +4136,Rockslug,1 +4137,Cockatrice,1 +4138,Pyrefiend,1 +4139,Basilisk,1 +4140,Infernal mage,1 +4141,Bloodveld,1 +4142,Jelly,1 +4143,Turoth,1 +4144,Abberant spectre,1 +4145,Dust devil,1 +4146,Kurask,1 +4147,Gargoyle,1 +4148,Nechryael,1 +4149,Abyssal demon,1 +4150,Broad arrows,1 +4151,Abyssal whip,120001 +4152,Abyssal whip,120001 +4153,Granite maul,50000 +4154,Granite maul,50000 +4155,Enchanted gem,1 +4156,Mirror shield,5000 +4157,Mirror shield,5000 +4158,Leaf-bladed spear,31000 +4159,Leaf-bladed spear,31000 +4160,Broad arrow,90 +4161,Bag of salt,10 +4162,Rock hammer,500 +4163,Rock hammer,500 +4164,Face mask,200 +4165,Face mask,200 +4166,Earmuffs,200 +4167,Earmuffs,200 +4168,Nose peg,200 +4169,Nose peg,200 +4170,Slayer's staff,21000 +4171,Slayer's staff,21000 +4172,Broad arrows,1 +4173,Broad arrows,1 +4174,Broad arrows,1 +4175,Broad arrows,1 +4176,null,1 +4177,Picture,1 +4178,Abyssal whip,6 +4179,Stick,1 +4180,Dragon platelegs,6 +4181,Mouth grip,1 +4182,Goutweed,6 +4183,Star amulet,1 +4184,Cavern key,1 +4185,Tower key,1 +4186,Shed key,1 +4187,Marble amulet,1 +4188,Obsidian amulet,1 +4189,Garden cane,1 +4190,Garden brush,1 +4191,Extended brush,1 +4192,Extended brush,1 +4193,Extended brush,1 +4194,Torso,1 +4195,Arms,1 +4196,Legs,1 +4197,Decapitated head,1 +4198,Decapitated head,1 +4199,Pickled brain,1 +4200,Conductor mould,1 +4201,Conductor,1 +4202,Ring of charos,1 +4203,Journal,1 +4204,Letter,1 +4205,Consecration seed,1 +4206,Consecration seed,1 +4207,Crystal seed,1 +4208,Crystal seed,1 +4209,Cadarn lineage,2 +4210,Tinderbox,0 +4211,Elf crystal,1 +4212,New crystal bow,900000 +4213,New crystal bow,900000 +4214,Crystal bow full,900000 +4215,Crystal bow 9/10,800000 +4216,Crystal bow 8/10,700000 +4217,Crystal bow 7/10,600000 +4218,Crystal bow 6/10,500000 +4219,Crystal bow 5/10,400000 +4220,Crystal bow 4/10,300000 +4221,Crystal bow 3/10,200000 +4222,Crystal bow 2/10,100000 +4223,Crystal bow 1/10,50000 +4224,New crystal shield,800000 +4225,Crystal shield full,750000 +4226,Crystal shield 9/10,670000 +4227,Crystal shield 8/10,590000 +4228,Crystal shield 7/10,510000 +4229,Crystal shield 6/10,430000 +4230,Crystal shield 5/10,350000 +4231,Crystal shield 4/10,270000 +4232,Crystal shield 3/10,190000 +4233,Crystal shield 2/10,100000 +4234,Crystal shield 1/10,25000 +4235,New crystal shield,800000 +4236,Signed oak bow,160 +4237,Nettle-water,4 +4238,Puddle of slime,1 +4239,Nettle tea,4 +4240,Nettle tea,4 +4241,Nettles,3 +4242,Cup of tea,10 +4243,Cup of tea,10 +4244,Porcelain cup,2 +4245,Cup of tea,10 +4246,Cup of tea,10 +4247,Mystical robes,1 +4248,Book of haricanto,1 +4249,Translation manual,1 +4250,Ghostspeak amulet,35 +4251,Ectophial,1 +4252,Ectophial,1 +4253,Model ship,1 +4254,Model ship,1 +4255,Bonemeal,1 +4256,Bonemeal,1 +4257,Bonemeal,1 +4258,Bonemeal,1 +4259,Bonemeal,1 +4260,Bonemeal,1 +4261,Bonemeal,1 +4262,Bonemeal,1 +4263,Bonemeal,1 +4264,Bonemeal,1 +4265,Bonemeal,1 +4266,Bonemeal,1 +4267,Bonemeal,1 +4268,Bonemeal,1 +4269,Bonemeal,1 +4270,Bonemeal,1 +4271,Bonemeal,1 +4272,Bone key,1 +4273,Chest key,1 +4274,Map scrap,1 +4275,Map scrap,1 +4276,Map scrap,1 +4277,Treasure map,1 +4278,Ecto-token,1 +4279,null,1 +4280,null,1 +4281,null,1 +4282,null,1 +4283,Petition form,1 +4284,Bedsheet,1 +4285,Bedsheet,1 +4286,Bucket of slime,1 +4287,Raw beef,1 +4288,Raw beef,1 +4289,Raw chicken,1 +4290,Raw chicken,1 +4291,Cooked chicken,4 +4292,Cooked chicken,4 +4293,Cooked meat,4 +4294,Cooked meat,4 +4295,Female h.a.m.,1 +4296,null,1 +4297,Male h.a.m.,1 +4298,Ham shirt,75 +4299,Ham shirt,75 +4300,Ham robe,75 +4301,Ham robe,75 +4302,Ham hood,75 +4303,Ham hood,75 +4304,Ham cloak,75 +4305,Ham cloak,75 +4306,H.a.m logo,75 +4307,H.a.m logo,75 +4308,Gloves,75 +4309,Gloves,75 +4310,Boots,75 +4311,Boots,75 +4312,null,1 +4313,Crystal of seren,2 +4314,Tinderbox,0 +4315,Team-1 cape,50 +4316,Team-1 cape,50 +4317,Team-2 cape,50 +4318,Team-2 cape,50 +4319,Team-3 cape,50 +4320,Team-3 cape,50 +4321,Team-4 cape,50 +4322,Team-4 cape,50 +4323,Team-5 cape,50 +4324,Team-5 cape,50 +4325,Team-6 cape,50 +4326,Team-6 cape,50 +4327,Team-7 cape,50 +4328,Team-7 cape,50 +4329,Team-8 cape,50 +4330,Team-8 cape,50 +4331,Team-9 cape,50 +4332,Team-9 cape,50 +4333,Team-10 cape,50 +4334,Team-10 cape,50 +4335,Team-11 cape,50 +4336,Team-11 cape,50 +4337,Team-12 cape,50 +4338,Team-12 cape,50 +4339,Team-13 cape,50 +4340,Team-13 cape,50 +4341,Team-14 cape,50 +4342,Team-14 cape,50 +4343,Team-15 cape,50 +4344,Team-15 cape,50 +4345,Team-16 cape,50 +4346,Team-16 cape,50 +4347,Team-17 cape,50 +4348,Team-17 cape,50 +4349,Team-18 cape,50 +4350,Team-18 cape,50 +4351,Team-19 cape,50 +4352,Team-19 cape,50 +4353,Team-20 cape,50 +4354,Team-20 cape,50 +4355,Team-21 cape,50 +4356,Team-21 cape,50 +4357,Team-22 cape,50 +4358,Team-22 cape,50 +4359,Team-23 cape,50 +4360,Team-23 cape,50 +4361,Team-24 cape,50 +4362,Team-24 cape,50 +4363,Team-25 cape,50 +4364,Team-25 cape,50 +4365,Team-26 cape,50 +4366,Team-26 cape,50 +4367,Team-27 cape,50 +4368,Team-27 cape,50 +4369,Team-28 cape,50 +4370,Team-28 cape,50 +4371,Team-29 cape,50 +4372,Team-29 cape,50 +4373,Team-30 cape,50 +4374,Team-30 cape,50 +4375,Team-31 cape,50 +4376,Team-31 cape,50 +4377,Team-32 cape,50 +4378,Team-32 cape,50 +4379,Team-33 cape,50 +4380,Team-33 cape,50 +4381,Team-34 cape,50 +4382,Team-34 cape,50 +4383,Team-35 cape,50 +4384,Team-35 cape,50 +4385,Team-36 cape,50 +4386,Team-36 cape,50 +4387,Team-37 cape,50 +4388,Team-37 cape,50 +4389,Team-38 cape,50 +4390,Team-38 cape,50 +4391,Team-39 cape,50 +4392,Team-39 cape,50 +4393,Team-40 cape,50 +4394,Team-40 cape,50 +4395,Team-41 cape,50 +4396,Team-41 cape,50 +4397,Team-42 cape,50 +4398,Team-42 cape,50 +4399,Team-43 cape,50 +4400,Team-43 cape,50 +4401,Team-44 cape,50 +4402,Team-44 cape,50 +4403,Team-45 cape,50 +4404,Team-45 cape,50 +4405,Team-46 cape,50 +4406,Team-46 cape,50 +4407,Team-47 cape,50 +4408,Team-47 cape,50 +4409,Team-48 cape,50 +4410,Team-48 cape,50 +4411,Team-49 cape,50 +4412,Team-49 cape,50 +4413,Team-50 cape,50 +4414,Team-50 cape,50 +4415,Blunt axe,56 +4416,Herbal tincture,100 +4417,Guthix rest(4),50 +4418,Guthix rest(4),50 +4419,Guthix rest(3),50 +4420,Guthix rest(3),50 +4421,Guthix rest(2),50 +4422,Guthix rest(2),50 +4423,Guthix rest(1),50 +4424,Guthix rest(1),50 +4425,Stodgy mattress,1 +4426,Comfy mattress,1 +4427,Iron oxide,5 +4428,Animate rock scroll,50 +4429,Broken vane part,1 +4430,Directionals,1 +4431,Broken vane part,1 +4432,Ornament,1 +4433,Broken vane part,1 +4434,Weathervane pillar,1 +4435,Weather report,1 +4436,Airtight pot,10 +4437,Airtight pot,10 +4438,Unfired pot lid,10 +4439,Unfired pot lid,10 +4440,Pot lid,15 +4441,Pot lid,15 +4442,Breathing salts,1 +4443,Chicken cage,1 +4444,Sharpened axe,56 +4445,Red mahogany log,1 +4446,Steel key ring,1 +4447,Antique lamp,1 +4448,null,1 +4449,null,1 +4450,null,1 +4451,null,1 +4452,null,1 +4453,null,1 +4454,null,1 +4455,null,1 +4456,Bowl of hot water,10 +4457,Bowl of hot water,10 +4458,Cup of water,10 +4459,Cup of water,10 +4460,Cup of hot water,10 +4461,Cup of hot water,10 +4462,Ruined herb tea,10 +4463,Ruined herb tea,10 +4464,Herb tea mix,10 +4465,Herb tea mix,10 +4466,Herb tea mix,10 +4467,Herb tea mix,10 +4468,Herb tea mix,10 +4469,Herb tea mix,10 +4470,Herb tea mix,15 +4471,Herb tea mix,15 +4472,Herb tea mix,15 +4473,Herb tea mix,15 +4474,Herb tea mix,15 +4475,Herb tea mix,15 +4476,Herb tea mix,15 +4477,Herb tea mix,15 +4478,Herb tea mix,30 +4479,Herb tea mix,30 +4480,Herb tea mix,30 +4481,Herb tea mix,30 +4482,Herb tea mix,30 +4483,Herb tea mix,30 +4484,Safety guarantee,1 +4485,White pearl,1 +4486,White pearl seed,1 +4487,Half a rock,1 +4488,Corpse of woman,1 +4489,Asleif's necklace,1 +4490,Mud,1 +4491,Tinderbox,0 +4492,Muddy rock,1 +4493,Tinderbox,0 +4494,Pole,1 +4495,Pole,1 +4496,Broken pole,1 +4497,Tinderbox,0 +4498,Rope,1 +4499,Rope,1 +4500,Pole,1 +4501,Pole,1 +4502,Bearhead,0 +4503,Decorative sword,1300 +4504,Decorative armour,5200 +4505,Decorative armour,2600 +4506,Decorative helm,780 +4507,Decorative shield,2210 +4508,Decorative sword,3200 +4509,Decorative armour,12800 +4510,Decorative armour,6400 +4511,Decorative helm,1920 +4512,Decorative shield,5440 +4513,Castlewars hood,1 +4514,Castlewars cloak,100 +4515,Castlewars hood,1 +4516,Castlewars cloak,100 +4517,Giant frog legs,100 +4518,Giant frog legs,100 +4519,Swamp wallbeast,1 +4520,Swamp cave slime,1 +4521,Swamp cave bug,1 +4522,Oil lamp,28 +4523,Oil lamp,28 +4524,Oil lamp,28 +4525,Oil lamp,25 +4526,Oil lamp,25 +4527,Candle lantern,10 +4528,Candle lantern,10 +4529,Candle lantern,15 +4530,Candle lantern,15 +4531,Candle lantern,15 +4532,Candle lantern,15 +4533,Candle lantern,15 +4534,Candle lantern,15 +4535,Oil lantern,120 +4536,Oil lantern,120 +4537,Oil lantern,125 +4538,Oil lantern,125 +4539,Oil lantern,125 +4540,Oil lantern frame,90 +4541,Oil lantern frame,90 +4542,Lantern lens,70 +4543,Lantern lens,70 +4544,Bullseye lantern,300 +4545,Bullseye lantern,300 +4546,Bullseye lantern,400 +4547,Bullseye lantern,400 +4548,Bullseye lantern,420 +4549,Bullseye lantern,420 +4550,Bullseye lantern,420 +4551,Spiny helmet,650 +4552,Spiny helmet,650 +4553,null,1 +4554,null,1 +4555,null,1 +4556,null,1 +4557,null,1 +4558,Blue sweets,0 +4559,Deep blue sweets,0 +4560,White sweets,0 +4561,Purple sweets,0 +4562,Red sweets,0 +4563,Green sweets,0 +4564,Pink sweets,0 +4565,Basket of eggs,0 +4566,Rubber chicken,0 +4567,Gold helmet,1 +4568,Dwarven lore,1 +4569,Book page 1,1 +4570,Book page 2,1 +4571,Book page 3,1 +4572,Pages,1 +4573,Pages,1 +4574,Base schematics,1 +4575,Schematic,1 +4576,Schematics,1 +4577,Schematics,1 +4578,Schematic,1 +4579,Cannon ball,1 +4580,Black spear,650 +4581,Black spear,650 +4582,Black spear(p),750 +4583,Black spear(p),750 +4584,Black spear(kp),750 +4585,Dragon plateskirt,270000 +4586,Dragon plateskirt,270000 +4587,Dragon scimitar,100000 +4588,Dragon scimitar,100000 +4589,Keys,1 +4590,Jewels,1 +4591,Karidian headpiece,1 +4592,Picture,0 +4593,Fake beard,1 +4594,Picture,0 +4595,Karidian disguise,1 +4596,Picture,0 +4597,Note,1 +4598,Note,1 +4599,Oak-blackjack,1 +4600,Willow-blackjack,600 +4601,Ugthanki dung,1 +4602,Ugthanki dung,1 +4603,Receipt,1 +4604,Hag's poison,1 +4605,Snake charm,1 +4606,Snake basket,1 +4607,Snake basket full,1 +4608,Super kebab,3 +4609,Super kebab,3 +4610,Red hot sauce,1 +4611,Desert disguise,1 +4612,Willow-blackjack,600 +4613,Spinning plate,75 +4614,Broken plate,0 +4615,Letter,1 +4616,Varmen's notes,2 +4617,Display cabinet key,1 +4618,Statuette,2 +4619,Strange implement,1 +4620,Black mushroom,1 +4621,Phoenix feather,1 +4622,Black mushroom ink,1 +4623,Phoenix quill pen,1 +4624,Golem program,1 +4625,Bandit,1 +4626,null,1 +4627,Bandit's brew,650 +4628,Bandit's brew,650 +4629,null,1 +4630,null,1 +4631,null,1 +4632,null,1 +4633,null,1 +4634,null,1 +4635,null,1 +4636,null,1 +4637,null,1 +4638,null,1 +4639,null,1 +4640,null,1 +4641,null,1 +4642,null,1 +4643,null,1 +4644,null,1 +4645,null,1 +4646,null,1 +4647,null,1 +4648,null,1 +4649,null,1 +4650,null,1 +4651,null,1 +4652,null,1 +4653,Fire,250 +4654,Etchings,250 +4655,Translation,250 +4656,Warm key,250 +4657,Ring of visibility,350 +4658,Silver pot,250 +4659,Blessed pot,250 +4660,Silver pot,250 +4661,Blessed pot,250 +4662,Silver pot,250 +4663,Blessed pot,250 +4664,Silver pot,250 +4665,Blessed pot,250 +4666,Silver pot,250 +4667,Blessed pot,250 +4668,Garlic powder,10 +4669,Tinderbox,0 +4670,Blood diamond,250 +4671,Ice diamond,250 +4672,Smoke diamond,250 +4673,Shadow diamond,250 +4674,Gilded cross,250 +4675,Ancient staff,100000 +4676,Ancient staff,100000 +4677,Catspeak amulet,1 +4678,Canopic jar,1 +4679,Canopic jar,1 +4680,Canopic jar,1 +4681,Canopic jar,1 +4682,Holy symbol,1 +4683,Unholy symbol,1 +4684,Linen,30 +4685,Picture,0 +4686,Embalming manual,0 +4687,Bucket of sap,30 +4688,Tinderbox,0 +4689,Pile of salt,20 +4690,Tinderbox,0 +4691,Sphinx's token,1 +4692,Gold leaf,1 +4693,Full bucket,6 +4694,Steam rune,20 +4695,Mist rune,20 +4696,Dust rune,20 +4697,Smoke rune,20 +4698,Mud rune,20 +4699,Lava rune,20 +4700,Sapphire lantern,600 +4701,Sapphire lantern,600 +4702,Sapphire lantern,600 +4703,Magic stone,1 +4704,Stone bowl,1 +4705,null,1 +4706,null,1 +4707,Crumbling tome,5 +4708,Ahrim's hood,13000 +4709,Ahrim's hood,13000 +4710,Ahrim's staff,85000 +4711,Ahrim's staff,85000 +4712,Ahrim's robetop,50000 +4713,Ahrim's robetop,50000 +4714,Ahrim's robeskirt,47000 +4715,Ahrim's robeskirt,47000 +4716,Dharok's helm,103000 +4717,Dharok's helm,103000 +4718,Dharok's greataxe,208000 +4719,Dharok's greataxe,208000 +4720,Dharok's platebody,280000 +4721,Dharok's platebody,280000 +4722,Dharok's platelegs,275000 +4723,Dharok's platelegs,275000 +4724,Guthan's helm,103000 +4725,Guthan's helm,103000 +4726,Guthan's warspear,100000 +4727,Guthan's warspear,100000 +4728,Guthan's platebody,280000 +4729,Guthan's platebody,280000 +4730,Guthan's chainskirt,275000 +4731,Guthan's chainskirt,275000 +4732,Karil's coif,13000 +4733,Karil's coif,13000 +4734,Karil's crossbow,160000 +4735,Karil's crossbow,160000 +4736,Karil's leathertop,50000 +4737,Karil's leathertop,50000 +4738,Karil's leatherskirt,47000 +4739,Karil's leatherskirt,47000 +4740,Bolt rack,420 +4741,null,1 +4742,null,1 +4743,null,1 +4744,null,1 +4745,Torag's helm,103000 +4746,Torag's helm,103000 +4747,Torag's hammers,160000 +4748,Torag's hammers,160000 +4749,Torag's platebody,280000 +4750,Torag's platebody,280000 +4751,Torag's platelegs,275000 +4752,Torag's platelegs,275000 +4753,Verac's helm,103000 +4754,Verac's helm,103000 +4755,Verac's flail,160000 +4756,Verac's flail,160000 +4757,Verac's brassard,280000 +4758,Verac's brassard,280000 +4759,Verac's plateskirt,275000 +4760,Verac's plateskirt,275000 +4761,null,1 +4762,null,1 +4763,null,1 +4764,null,1 +4765,null,1 +4766,null,1 +4767,null,1 +4768,null,1 +4769,null,1 +4770,null,1 +4771,null,1 +4772,null,1 +4773,Bronze brutal,5 +4774,null,1 +4775,null,1 +4776,null,1 +4777,null,1 +4778,Iron brutal,10 +4779,null,1 +4780,null,1 +4781,null,1 +4782,null,1 +4783,Steel brutal,20 +4784,null,1 +4785,null,1 +4786,null,1 +4787,null,1 +4788,Black brutal,35 +4789,null,1 +4790,null,1 +4791,null,1 +4792,null,1 +4793,Mithril brutal,50 +4794,null,1 +4795,null,1 +4796,null,1 +4797,null,1 +4798,Adamant brutal,95 +4799,null,1 +4800,null,1 +4801,null,1 +4802,null,1 +4803,Rune brutal,450 +4804,null,1 +4805,null,1 +4806,null,1 +4807,null,1 +4808,Black prism,1 +4809,Torn page,1 +4810,Ruined backpack,1 +4811,Dragon inn tankard,1 +4812,Zogre bones,1 +4813,Zogre bones,1 +4814,Sithik portrait,1 +4815,Sithik portrait,1 +4816,Signed portrait,1 +4817,Book of portraiture,1 +4818,Ogre artefact,1 +4819,Bronze nails,7 +4820,Iron nails,33 +4821,Black nails,12 +4822,Mithril nails,18 +4823,Adamantite nails,45 +4824,Rune nails,220 +4825,Unstrung comp bow,90 +4826,Unstrung comp bow,90 +4827,Comp ogre bow,180 +4828,Comp ogre bow,180 +4829,Book of 'h.a.m',1 +4830,Fayrg bones,1 +4831,Fayrg bones,1 +4832,Raurg bones,1 +4833,Raurg bones,1 +4834,Ourg bones,1 +4835,Ourg bones,1 +4836,Strange potion,1 +4837,Necromancy book,1 +4838,Cup of tea,10 +4839,Ogre gate key,1 +4840,Rogue's purse potion(unf),5 +4841,Rogue's purse potion(unf),5 +4842,Relicym's balm(4),225 +4843,Relicym's balm(4),225 +4844,Relicym's balm(3),200 +4845,Relicym's balm(3),200 +4846,Relicym's balm(2),150 +4847,Relicym's balm(2),150 +4848,Relicym's balm(1),75 +4849,Relicym's balm(1),75 +4850,Ogre coffin key,100 +4851,Ogre coffin key,100 +4852,Bonemeal,1 +4853,Bonemeal,1 +4854,Bonemeal,1 +4855,Bonemeal,1 +4856,Ahrim's hood 100,13000 +4857,Ahrim's hood 75,13000 +4858,Ahrim's hood 50,13000 +4859,Ahrim's hood 25,13000 +4860,Ahrim's hood 0,13000 +4861,Ahrim's hood 0,13000 +4862,Ahrim's staff 100,85000 +4863,Ahrim's staff 75,85000 +4864,Ahrim's staff 50,85000 +4865,Ahrim's staff 25,85000 +4866,Ahrim's staff 0,85000 +4867,Ahrim's staff 0,85000 +4868,Ahrim's top 100,50000 +4869,Ahrim's top 75,50000 +4870,Ahrim's top 50,50000 +4871,Ahrim's top 25,50000 +4872,Ahrim's top 0,50000 +4873,Ahrim's top 0,50000 +4874,Ahrim's skirt 100,47000 +4875,Ahrim's skirt 75,47000 +4876,Ahrim's skirt 50,47000 +4877,Ahrim's skirt 25,47000 +4878,Ahrim's skirt 0,47000 +4879,Ahrim's skirt 0,47000 +4880,Dharok's helm 100,103000 +4881,Dharok's helm 75,103000 +4882,Dharok's helm 50,103000 +4883,Dharok's helm 25,103000 +4884,Dharok's helm 0,103000 +4885,Dharok's helm 0,103000 +4886,Dharok's axe 100,208000 +4887,Dharok's axe 75,208000 +4888,Dharok's axe 50,208000 +4889,Dharok's axe 25,208000 +4890,Dharok's axe 0,208000 +4891,Dharok's axe 0,208000 +4892,Dharok's body 100,280000 +4893,Dharok's body 75,280000 +4894,Dharok's body 50,280000 +4895,Dharok's body 25,280000 +4896,Dharok's body 0,280000 +4897,Dharok's body 0,280000 +4898,Dharok's legs 100,275000 +4899,Dharok's legs 75,275000 +4900,Dharok's legs 50,275000 +4901,Dharok's legs 25,275000 +4902,Dharok's legs 0,275000 +4903,Dharok's legs 0,275000 +4904,Guthan's helm 100,103000 +4905,Guthan's helm 75,103000 +4906,Guthan's helm 50,103000 +4907,Guthan's helm 25,103000 +4908,Guthan's helm 0,103000 +4909,Guthan's helm 0,103000 +4910,Guthan's spear 100,100000 +4911,Guthan's spear 75,100000 +4912,Guthan's spear 50,100000 +4913,Guthan's spear 25,100000 +4914,Guthan's spear 0,100000 +4915,Guthan's spear 0,100000 +4916,Guthan's body 100,280000 +4917,Guthan's body 75,280000 +4918,Guthan's body 50,280000 +4919,Guthan's body 25,280000 +4920,Guthan's body 0,280000 +4921,Guthan's body 0,280000 +4922,Guthan's skirt 100,275000 +4923,Guthan's skirt 75,275000 +4924,Guthan's skirt 50,275000 +4925,Guthan's skirt 25,275000 +4926,Guthan's skirt 0,275000 +4927,Guthan's skirt 0,275000 +4928,Karil's coif 100,13000 +4929,Karil's coif 75,13000 +4930,Karil's coif 50,13000 +4931,Karil's coif 25,13000 +4932,Karil's coif 0,13000 +4933,Karil's coif 0,13000 +4934,Karil's x-bow 100,160000 +4935,Karil's x-bow 75,160000 +4936,Karil's x-bow 50,160000 +4937,Karil's x-bow 25,160000 +4938,Karil's x-bow 0,160000 +4939,Karil's x-bow 0,160000 +4940,Karil's top 100,50000 +4941,Karil's top 75,50000 +4942,Karil's top 50,50000 +4943,Karil's top 25,50000 +4944,Karil's top 0,50000 +4945,Karil's top 0,50000 +4946,Karil's skirt 100,47000 +4947,Karil's skirt 75,47000 +4948,Karil's skirt 50,47000 +4949,Karil's skirt 25,47000 +4950,Karil's skirt 0,47000 +4951,Karil's skirt 0,47000 +4952,Torag's helm 100,103000 +4953,Torag's helm 75,103000 +4954,Torag's helm 50,103000 +4955,Torag's helm 25,103000 +4956,Torag's helm 0,103000 +4957,Torag's helm 0,103000 +4958,Torag's hammer 100,160000 +4959,Torag's hammer 75,160000 +4960,Torag's hammer 50,160000 +4961,Torag's hammer 25,160000 +4962,Torag's hammer 0,160000 +4963,Torag's hammer 0,160000 +4964,Torag's body 100,280000 +4965,Torag's body 75,280000 +4966,Torag's body 50,280000 +4967,Torag's body 25,280000 +4968,Torag's body 0,280000 +4969,Torag's body 0,280000 +4970,Torag's legs 100,275000 +4971,Torag's legs 75,275000 +4972,Torag's legs 50,275000 +4973,Torag's legs 25,275000 +4974,Torag's legs 0,275000 +4975,Torag's legs 0,275000 +4976,Verac's helm 100,103000 +4977,Verac's helm 75,103000 +4978,Verac's helm 50,103000 +4979,Verac's helm 25,103000 +4980,Verac's helm 0,103000 +4981,Verac's helm 0,103000 +4982,Verac's flail 100,160000 +4983,Verac's flail 75,160000 +4984,Verac's flail 50,160000 +4985,Verac's flail 25,160000 +4986,Verac's flail 0,160000 +4987,Verac's flail 0,160000 +4988,Verac's top 100,280000 +4989,Verac's top 75,280000 +4990,Verac's top 50,280000 +4991,Verac's top 25,280000 +4992,Verac's top 0,280000 +4993,Verac's top 0,280000 +4994,Verac's skirt 100,275000 +4995,Verac's skirt 75,275000 +4996,Verac's skirt 50,275000 +4997,Verac's skirt 25,275000 +4998,Verac's skirt 0,275000 +4999,Verac's skirt 0,275000 +5000,null,1 +5001,Raw cave eel,15 +5002,Burnt cave eel,0 +5003,Cave eel,15 +5004,Frog spawn,5 +5005,Raw cave eel,15 +5006,Burnt cave eel,0 +5007,Cave eel,15 +5008,Brooch,1 +5009,Goblin symbol book,1 +5010,Key,1 +5011,Silverware,1 +5012,Peace treaty,1 +5013,Mining helmet,600 +5014,Mining helmet,900 +5015,Mining helmet,900 +5016,Bone spear,600 +5017,Bone spear,600 +5018,Bone club,600 +5019,Bone club,600 +5020,Minecart ticket,1 +5021,Minecart ticket,1 +5022,Minecart ticket,1 +5023,Minecart ticket,1 +5024,Woven top,650 +5025,Woven top,650 +5026,Woven top,812 +5027,Woven top,812 +5028,Woven top,845 +5029,Woven top,845 +5030,Shirt,585 +5031,Shirt,585 +5032,Shirt,780 +5033,Shirt,780 +5034,Shirt,812 +5035,Shirt,812 +5036,Trousers,715 +5037,Trousers,715 +5038,Trousers,910 +5039,Trousers,910 +5040,Trousers,975 +5041,Trousers,975 +5042,Shorts,364 +5043,Shorts,364 +5044,Shorts,468 +5045,Shorts,468 +5046,Shorts,507 +5047,Shorts,507 +5048,Skirt,455 +5049,Skirt,455 +5050,Skirt,715 +5051,Skirt,715 +5052,Skirt,812 +5053,Skirt,812 +5054,Dwarf,1 +5055,Dwarf,1 +5056,Dwarven battleaxe,1 +5057,Dwarven battleaxe,1 +5058,Dwarven battleaxe,1 +5059,Dwarven battleaxe,1 +5060,Dwarven battleaxe,1 +5061,Dwarven battleaxe,1 +5062,Left boot,1 +5063,Right boot,1 +5064,Exquisite boots,1 +5065,Book on costumes,1 +5066,Meeting notes,1 +5067,Exquisite clothes,1 +5068,Master farmer,1 +5069,null,1 +5070,Bird's nest,1 +5071,Bird's nest,1 +5072,Bird's nest,1 +5073,Bird's nest,1 +5074,Bird's nest,1 +5075,Bird's nest,1 +5076,Bird's egg,1 +5077,Bird's egg,1 +5078,Bird's egg,1 +5079,null,1 +5080,null,1 +5081,null,1 +5082,null,1 +5083,null,1 +5084,null,1 +5085,null,1 +5086,null,1 +5087,null,1 +5088,null,1 +5089,null,1 +5090,null,1 +5091,null,1 +5092,null,1 +5093,null,1 +5094,null,1 +5095,null,1 +5096,Marigold seed,84 +5097,Rosemary seed,98 +5098,Nasturtium seed,11 +5099,Woad seed,11 +5100,Limpwurt seed,12 +5101,Redberry seed,4 +5102,Cadavaberry seed,9 +5103,Dwellberry seed,25 +5104,Jangerberry seed,60 +5105,Whiteberry seed,133 +5106,Poison ivy seed,166 +5107,null,1 +5108,null,1 +5109,null,1 +5110,null,1 +5111,null,1 +5112,null,1 +5113,null,1 +5114,null,1 +5115,null,1 +5116,null,1 +5117,null,1 +5118,null,1 +5119,null,1 +5120,null,1 +5121,null,1 +5122,null,1 +5123,null,1 +5124,null,1 +5125,null,1 +5126,null,1 +5127,null,1 +5128,null,1 +5129,null,1 +5130,null,1 +5131,null,1 +5132,null,1 +5133,null,1 +5134,null,1 +5135,null,1 +5136,null,1 +5137,null,1 +5138,null,1 +5139,null,1 +5140,null,1 +5141,null,1 +5142,null,1 +5143,null,1 +5144,null,1 +5145,null,1 +5146,null,1 +5147,null,1 +5148,null,1 +5149,null,1 +5150,null,1 +5151,null,1 +5152,null,1 +5153,null,1 +5154,null,1 +5155,null,1 +5156,null,1 +5157,null,1 +5158,null,1 +5159,null,1 +5160,null,1 +5161,null,1 +5162,null,1 +5163,null,1 +5164,null,1 +5165,null,1 +5166,null,1 +5167,null,1 +5168,null,1 +5169,null,1 +5170,null,1 +5171,Seeds,1 +5172,null,1 +5173,null,1 +5174,null,1 +5175,null,1 +5176,null,1 +5177,null,1 +5178,null,1 +5179,null,1 +5180,null,1 +5181,null,1 +5182,null,1 +5183,null,1 +5184,null,1 +5185,null,1 +5186,null,1 +5187,null,1 +5188,null,1 +5189,null,1 +5190,null,1 +5191,null,1 +5192,null,1 +5193,null,1 +5194,null,1 +5195,null,1 +5196,null,1 +5197,null,1 +5198,null,1 +5199,null,1 +5200,null,1 +5201,null,1 +5202,null,1 +5203,null,1 +5204,null,1 +5205,null,1 +5206,null,1 +5207,null,1 +5208,null,1 +5209,null,1 +5210,null,1 +5211,null,1 +5212,null,1 +5213,null,1 +5214,null,1 +5215,null,1 +5216,null,1 +5217,null,1 +5218,null,1 +5219,null,1 +5220,null,1 +5221,null,1 +5222,null,1 +5223,null,1 +5224,null,1 +5225,null,1 +5226,null,1 +5227,null,1 +5228,null,1 +5229,null,1 +5230,null,1 +5231,null,1 +5232,null,1 +5233,null,1 +5234,null,1 +5235,null,1 +5236,null,1 +5237,null,1 +5238,null,1 +5239,null,1 +5240,null,1 +5241,null,1 +5242,null,1 +5243,null,1 +5244,null,1 +5245,null,1 +5246,null,1 +5247,null,1 +5248,null,1 +5249,null,1 +5250,null,1 +5251,null,1 +5252,null,1 +5253,null,1 +5254,null,1 +5255,null,1 +5256,null,1 +5257,null,1 +5258,null,1 +5259,null,1 +5260,null,1 +5261,null,1 +5262,null,1 +5263,null,1 +5264,null,1 +5265,null,1 +5266,null,1 +5267,null,1 +5268,null,1 +5269,null,1 +5270,null,1 +5271,null,1 +5272,null,1 +5273,null,1 +5274,null,1 +5275,null,1 +5276,null,1 +5277,null,1 +5278,null,1 +5279,null,1 +5280,Cactus seed,99 +5281,Belladonna seed,177 +5282,Mushroom spore,86 +5283,Apple tree seed,13 +5284,Banana tree seed,20 +5285,Orange tree seed,31 +5286,Curry tree seed,39 +5287,Pineapple seed,74 +5288,Papaya tree seed,115 +5289,Palm tree seed,254 +5290,Calquat tree seed,340 +5291,Guam seed,4 +5292,Marrentill seed,5 +5293,Tarromin seed,7 +5294,Harralander seed,12 +5295,Ranarr seed,19 +5296,Toadflax seed,34 +5297,Irit seed,64 +5298,Avantoe seed,64 +5299,Kwuarm seed,64 +5300,Snapdragon seed,64 +5301,Cadantine seed,64 +5302,Lantadyme seed,64 +5303,Dwarf weed seed,64 +5304,Torstol seed,64 +5305,Barley seed,38 +5306,Jute seed,86 +5307,Hammerstone seed,52 +5308,Asgarnian seed,60 +5309,Yanillian seed,210 +5310,Krandorian seed,8 +5311,Wildblood seed,14 +5312,Acorn,6 +5313,Willow seed,16 +5314,Maple seed,48 +5315,Yew seed,143 +5316,Magic seed,422 +5317,Spirit seed,1 +5318,Potato seed,8 +5319,Onion seed,10 +5320,Sweetcorn seed,8 +5321,Watermelon seed,56 +5322,Tomato seed,4 +5323,Strawberry seed,18 +5324,Cabbage seed,25 +5325,Gardening trowel,12 +5326,Gardening trowel,12 +5327,Spade handle,1 +5328,Spade head,1 +5329,Secateurs,5 +5330,Secateurs,5 +5331,Watering can,8 +5332,Watering can,8 +5333,Watering can(1),1 +5334,Watering can(2),1 +5335,Watering can(3),1 +5336,Watering can(4),1 +5337,Watering can(5),1 +5338,Watering can(6),1 +5339,Watering can(7),1 +5340,Watering can(8),1 +5341,Rake,6 +5342,Rake,6 +5343,Seed dibber,6 +5344,Seed dibber,6 +5345,Gardening boots,25 +5346,Gardening boots,25 +5347,Rake handle,1 +5348,Rake head,1 +5349,null,1 +5350,Plant pot,1 +5351,Plant pot,1 +5352,Unfired plant pot,1 +5353,Unfired plant pot,1 +5354,Plant pot,1 +5355,Plant pot,1 +5356,Plant pot,1 +5357,Plant pot,1 +5358,Oak seedling,1 +5359,Willow seedling,1 +5360,Maple seedling,1 +5361,Yew seedling,1 +5362,Magic seedling,1 +5363,Spirit seedling,1 +5364,Oak seedling,1 +5365,Willow seedling,1 +5366,Maple seedling,1 +5367,Yew seedling,1 +5368,Magic seedling,1 +5369,Spirit seedling,1 +5370,Oak sapling,1 +5371,Willow sapling,1 +5372,Maple sapling,1 +5373,Yew sapling,1 +5374,Magic sapling,1 +5375,Spirit sapling,1 +5376,Basket,1 +5377,Basket,1 +5378,Apples(1),1 +5379,Apples(1),1 +5380,Apples(2),1 +5381,Apples(2),1 +5382,Apples(3),1 +5383,Apples(3),1 +5384,Apples(4),1 +5385,Apples(4),1 +5386,Apples(5),1 +5387,Apples(5),1 +5388,Oranges(1),1 +5389,Oranges(1),1 +5390,Oranges(2),1 +5391,Oranges(2),1 +5392,Oranges(3),1 +5393,Oranges(3),1 +5394,Oranges(4),1 +5395,Oranges(4),1 +5396,Oranges(5),1 +5397,Oranges(5),1 +5398,Strawberries(1),1 +5399,Strawberries(1),1 +5400,Strawberries(2),1 +5401,Strawberries(2),1 +5402,Strawberries(3),1 +5403,Strawberries(3),1 +5404,Strawberries(4),1 +5405,Strawberries(4),1 +5406,Strawberries(5),1 +5407,Strawberries(5),1 +5408,Bananas(1),1 +5409,Bananas(1),1 +5410,Bananas(2),1 +5411,Bananas(2),1 +5412,Bananas(3),1 +5413,Bananas(3),1 +5414,Bananas(4),1 +5415,Bananas(4),1 +5416,Bananas(5),1 +5417,Bananas(5),1 +5418,Empty sack,1 +5419,Empty sack,1 +5420,Potatoes(1),1 +5421,Potatoes(1),1 +5422,Potatoes(2),1 +5423,Potatoes(2),1 +5424,Potatoes(3),1 +5425,Potatoes(3),1 +5426,Potatoes(4),1 +5427,Potatoes(4),1 +5428,Potatoes(5),1 +5429,Potatoes(5),1 +5430,Potatoes(6),1 +5431,Potatoes(6),1 +5432,Potatoes(7),1 +5433,Potatoes(7),1 +5434,Potatoes(8),1 +5435,Potatoes(8),1 +5436,Potatoes(9),1 +5437,Potatoes(9),1 +5438,Potatoes(10),1 +5439,Potatoes(10),1 +5440,Onions(1),1 +5441,Onions(1),1 +5442,Onions(2),1 +5443,Onions(2),1 +5444,Onions(3),1 +5445,Onions(3),1 +5446,Onions(4),1 +5447,Onions(4),1 +5448,Onions(5),1 +5449,Onions(5),1 +5450,Onions(6),1 +5451,Onions(6),1 +5452,Onions(7),1 +5453,Onions(7),1 +5454,Onions(8),1 +5455,Onions(8),1 +5456,Onions(9),1 +5457,Onions(9),1 +5458,Onions(10),1 +5459,Onions(10),1 +5460,Cabbages(1),1 +5461,Cabbages(1),1 +5462,Cabbages(2),1 +5463,Cabbages(2),1 +5464,Cabbages(3),1 +5465,Cabbages(3),1 +5466,Cabbages(4),1 +5467,Cabbages(4),1 +5468,Cabbages(5),1 +5469,Cabbages(5),1 +5470,Cabbages(6),1 +5471,Cabbages(6),1 +5472,Cabbages(7),1 +5473,Cabbages(7),1 +5474,Cabbages(8),1 +5475,Cabbages(8),1 +5476,Cabbages(9),1 +5477,Cabbages(9),1 +5478,Cabbages(10),1 +5479,Cabbages(10),1 +5480,Apple seedling,1 +5481,Banana seedling,1 +5482,Orange seedling,1 +5483,Curry seedling,1 +5484,Pineapple seedling,1 +5485,Papaya seedling,1 +5486,Palm seedling,1 +5487,Calquat seedling,1 +5488,Apple seedling,1 +5489,Banana seedling,1 +5490,Orange seedling,1 +5491,Curry seedling,1 +5492,Pineapple seedling,1 +5493,Papaya seedling,1 +5494,Palm seedling,1 +5495,Calquat seedling,1 +5496,Apple sapling,1 +5497,Banana sapling,1 +5498,Orange sapling,1 +5499,Curry sapling,1 +5500,Pineapple sapling,1 +5501,Papaya sapling,1 +5502,Palm sapling,1 +5503,Calquat sapling,1 +5504,Strawberry,17 +5505,Strawberry,17 +5506,Old man's message,1 +5507,Strange book,1 +5508,Book of folklore,1 +5509,Small pouch,150 +5510,Medium pouch,300 +5511,Medium pouch,300 +5512,Large pouch,600 +5513,Large pouch,600 +5514,Giant pouch,1200 +5515,Giant pouch,1200 +5516,Elemental talisman,500 +5517,Elemental talisman,500 +5518,Scrying orb,1 +5519,Scrying orb,1 +5520,Abyssal book,10 +5521,Binding necklace,1425 +5522,Binding necklace,1425 +5523,Tiara mould,100 +5524,Tiara mould,100 +5525,Tiara,100 +5526,Tiara,100 +5527,Air tiara,100 +5528,Air tiara,100 +5529,Mind tiara,100 +5530,Mind tiara,100 +5531,Water tiara,100 +5532,Water tiara,100 +5533,Body tiara,100 +5534,Body tiara,100 +5535,Earth tiara,100 +5536,Earth tiara,100 +5537,Fire tiara,100 +5538,Fire tiara,100 +5539,Cosmic tiara,100 +5540,Cosmic tiara,100 +5541,Nature tiara,100 +5542,Nature tiara,100 +5543,Chaos tiara,100 +5544,Chaos tiara,100 +5545,Law tiara,100 +5546,Law tiara,100 +5547,Death tiara,100 +5548,Death tiara,100 +5549,Blood tiara,100 +5550,Blood tiara,100 +5551,Soul tiara,100 +5552,Soul tiara,100 +5553,Rogue top,625 +5554,Rogue mask,375 +5555,Rogue trousers,500 +5556,Rogue gloves,250 +5557,Rogue boots,250 +5558,Rogue kit,50 +5559,Flash powder,1 +5560,Stethoscope,10 +5561,Mystic jewel,1 +5562,Picture,1 +5563,Picture,1 +5564,Picture,1 +5565,Picture,1 +5566,Picture,1 +5567,Picture,1 +5568,Picture,1 +5569,Picture,1 +5570,Picture,1 +5571,Picture,1 +5572,Dial,1 +5573,null,1 +5574,Initiate sallet,6000 +5575,Initiate hauberk,10000 +5576,Initiate cuisse,8000 +5577,Cupric sulphate,1 +5578,Acetic acid,1 +5579,Gypsum,1 +5580,Sodium chloride,1 +5581,Nitrous oxide,1 +5582,Vial of liquid,1 +5583,Tin ore powder,1 +5584,Cupric ore powder,1 +5585,Bronze key,1 +5586,Metal spade,1 +5587,Metal spade,1 +5588,Alchemical notes,1 +5589,??? mixture,1 +5590,??? mixture,1 +5591,??? mixture,1 +5592,Tin,10 +5593,Tin,20 +5594,Tin,20 +5595,Tin,20 +5596,Tin,20 +5597,Tin,20 +5598,Tin,20 +5599,Tin,20 +5600,Tin,20 +5601,Chisel,1 +5602,Bronze wire,20 +5603,Shears,1 +5604,Magnet,3 +5605,Knife,6 +5606,Makeover voucher,1 +5607,Grain,1 +5608,Fox,1 +5609,Chicken,1 +5610,Hourglass,1 +5611,Initiate sallet,6000 +5612,Initiate hauberk,10000 +5613,Initiate cuisse,8000 +5614,Magic carpet,1 +5615,Bonemeal,1 +5616,Bronze arrow(p+),1 +5617,Iron arrow(p+),3 +5618,Steel arrow(p+),12 +5619,Mithril arrow(p+),32 +5620,Adamant arrow(p+),80 +5621,Rune arrow(p+),400 +5622,Bronze arrow(p++),1 +5623,Iron arrow(p++),3 +5624,Steel arrow(p++),12 +5625,Mithril arrow(p++),32 +5626,Adamant arrow(p++),80 +5627,Rune arrow(p++),400 +5628,Bronze dart(p+),1 +5629,Iron dart(p+),2 +5630,Steel dart(p+),10 +5631,Black dart(p+),18 +5632,Mithril dart(p+),25 +5633,Adamant dart(p+),65 +5634,Rune dart(p+),350 +5635,Bronze dart(p++),1 +5636,Iron dart(p++),2 +5637,Steel dart(p++),10 +5638,Black dart(p++),18 +5639,Mithril dart(p++),25 +5640,Adamant dart(p++),65 +5641,Rune dart(p++),350 +5642,Bronze javelin(p+),4 +5643,Iron javelin(p+),6 +5644,Steel javelin(p+),24 +5645,Mithril javelin(p+),64 +5646,Adamant javelin(p+),160 +5647,Rune javelin(p+),400 +5648,Bronze jav'n(p++),4 +5649,Iron javelin(p++),6 +5650,Steel javelin(p++),24 +5651,Mithril javelin(p++),64 +5652,Adamant javelin(p++),160 +5653,Rune javelin(p++),400 +5654,Bronze knife(p+),1 +5655,Iron knife(p+),3 +5656,Steel knife(p+),10 +5657,Mithril knife(p+),27 +5658,Black knife(p+),18 +5659,Adamant knife(p+),66 +5660,Rune knife(p+),166 +5661,Bronze knife(p++),1 +5662,Iron knife(p++),3 +5663,Steel knife(p++),10 +5664,Mithril knife(p++),27 +5665,Black knife(p++),18 +5666,Adamant knife(p++),66 +5667,Rune knife(p++),166 +5668,Iron dagger(p+),35 +5669,Iron dagger(p+),35 +5670,Bronze dagger(p+),10 +5671,Bronze dagger(p+),10 +5672,Steel dagger(p+),125 +5673,Steel dagger(p+),125 +5674,Mithril dagger(p+),325 +5675,Mithril dagger(p+),325 +5676,Adamant dagger(p+),800 +5677,Adamant dagger(p+),800 +5678,Rune dagger(p+),8000 +5679,Rune dagger(p+),8000 +5680,Dragon dagger(p+),24000 +5681,Dragon dagger(p+),24000 +5682,Black dagger(p+),240 +5683,Black dagger(p+),240 +5684,Poison dagger(p+),565 +5685,Poison dagger(p+),565 +5686,Iron dagger(p++),35 +5687,Iron dagger(p++),35 +5688,Br'ze dagger(p++),10 +5689,Br'ze dagger(p++),10 +5690,Steel dagger(p++),125 +5691,Steel dagger(p++),125 +5692,Mithril dagger(p++),325 +5693,Mithril dagger(p++),325 +5694,Adamant dagger(p++),800 +5695,Adamant dagger(p++),800 +5696,Rune dagger(p++),8000 +5697,Rune dagger(p++),8000 +5698,Dragon dagger(p++),24000 +5699,Dragon dagger(p++),24000 +5700,Black dagger(p++),240 +5701,Black dagger(p++),240 +5702,Poison dagger(p++),565 +5703,Poison dagger(p++),565 +5704,Bronze spear(p+),26 +5705,Bronze spear(p+),26 +5706,Iron spear(p+),91 +5707,Iron spear(p+),91 +5708,Steel spear(p+),325 +5709,Steel spear(p+),325 +5710,Mithril spear(p+),845 +5711,Mithril spear(p+),845 +5712,Adamant spear(p+),2080 +5713,Adamant spear(p+),2080 +5714,Rune spear(p+),20800 +5715,Rune spear(p+),20800 +5716,Dragon spear(p+),62400 +5717,Dragon spear(p+),62400 +5718,Bronze spear(p++),26 +5719,Bronze spear(p++),26 +5720,Iron spear(p++),91 +5721,Iron spear(p++),91 +5722,Steel spear(p++),325 +5723,Steel spear(p++),325 +5724,Mithril spear(p++),845 +5725,Mithril spear(p++),845 +5726,Adamant spear(p++),2080 +5727,Adamant spear(p++),2080 +5728,Rune spear(p++),20800 +5729,Rune spear(p++),20800 +5730,Dragon spear(p++),62400 +5731,Dragon spear(p++),62400 +5732,Stool,1 +5733,Rotten potato,1 +5734,Black spear(p+),750 +5735,Black spear(p+),750 +5736,Black spear(p++),750 +5737,Black spear(p++),750 +5738,Woad leaf,1 +5739,Asgarnian ale(m),2 +5740,Asgarnian ale(m),2 +5741,Mature wmb,2 +5742,Mature wmb,2 +5743,Greenman's ale(m),2 +5744,Greenman's ale(m),2 +5745,Dragon bitter(m),2 +5746,Dragon bitter(m),2 +5747,Dwarven stout(m),2 +5748,Dwarven stout(m),2 +5749,Moonlight mead(m),5 +5750,Moonlight mead(m),5 +5751,Axeman's folly,2 +5752,Axeman's folly,2 +5753,Axeman's folly(m),2 +5754,Axeman's folly(m),2 +5755,Chef's delight,2 +5756,Chef's delight,2 +5757,Chef's delight(m),2 +5758,Chef's delight(m),2 +5759,Slayer's respite,2 +5760,Slayer's respite,2 +5761,Slayer's respite(m),2 +5762,Slayer's respite(m),2 +5763,Cider,2 +5764,Cider,2 +5765,Mature cider,2 +5766,Mature cider,2 +5767,Ale yeast,1 +5768,Ale yeast,1 +5769,Calquat keg,1 +5770,Calquat keg,1 +5771,Dwarven stout(1),1 +5772,Dwarven stout(1),1 +5773,Dwarven stout(2),1 +5774,Dwarven stout(2),1 +5775,Dwarven stout(3),1 +5776,Dwarven stout(3),1 +5777,Dwarven stout(4),1 +5778,Dwarven stout(4),1 +5779,Asgarnian ale(1),1 +5780,Asgarnian ale(1),1 +5781,Asgarnian ale(2),1 +5782,Asgarnian ale(2),1 +5783,Asgarnian ale(3),1 +5784,Asgarnian ale(3),1 +5785,Asgarnian ale(4),1 +5786,Asgarnian ale(4),1 +5787,Greenmans ale(1),1 +5788,Greenmans ale(1),1 +5789,Greenmans ale(2),1 +5790,Greenmans ale(2),1 +5791,Greenmans ale(3),1 +5792,Greenmans ale(3),1 +5793,Greenmans ale(4),1 +5794,Greenmans ale(4),1 +5795,Mind bomb(1),1 +5796,Mind bomb(1),1 +5797,Mind bomb(2),1 +5798,Mind bomb(2),1 +5799,Mind bomb(3),1 +5800,Mind bomb(3),1 +5801,Mind bomb(4),1 +5802,Mind bomb(4),1 +5803,Dragon bitter(1),1 +5804,Dragon bitter(1),1 +5805,Dragon bitter(2),1 +5806,Dragon bitter(2),1 +5807,Dragon bitter(3),1 +5808,Dragon bitter(3),1 +5809,Dragon bitter(4),1 +5810,Dragon bitter(4),1 +5811,Moonlight mead(1),1 +5812,Moonlight mead(1),1 +5813,Moonlight mead(2),1 +5814,Moonlight mead(2),1 +5815,Moonlight mead(3),1 +5816,Moonlight mead(3),1 +5817,Moonlight mead(4),1 +5818,Moonlight mead(4),1 +5819,Axeman's folly(1),1 +5820,Axeman's folly(1),1 +5821,Axeman's folly(2),1 +5822,Axeman's folly(2),1 +5823,Axeman's folly(3),1 +5824,Axeman's folly(3),1 +5825,Axeman's folly(4),1 +5826,Axeman's folly(4),1 +5827,Chef's delight(1),1 +5828,Chef's delight(1),1 +5829,Chef's delight(2),1 +5830,Chef's delight(2),1 +5831,Chef's delight(3),1 +5832,Chef's delight(3),1 +5833,Chef's delight(4),1 +5834,Chef's delight(4),1 +5835,Slayer's respite(1),1 +5836,Slayer's respite(1),1 +5837,Slayer's respite(2),1 +5838,Slayer's respite(2),1 +5839,Slayer's respite(3),1 +5840,Slayer's respite(3),1 +5841,Slayer's respite(4),1 +5842,Slayer's respite(4),1 +5843,Cider(1),1 +5844,Cider(1),1 +5845,Cider(2),1 +5846,Cider(2),1 +5847,Cider(3),1 +5848,Cider(3),1 +5849,Cider(4),1 +5850,Cider(4),1 +5851,Dwarven stout(m1),1 +5852,Dwarven stout(m1),1 +5853,Dwarven stout(m2),1 +5854,Dwarven stout(m2),1 +5855,Dwarven stout(m3),1 +5856,Dwarven stout(m3),1 +5857,Dwarven stout(m4),1 +5858,Dwarven stout(m4),1 +5859,Asgarnian ale(m1),1 +5860,Asgarnian ale(m1),1 +5861,Asgarnian ale(m2),1 +5862,Asgarnian ale(m2),1 +5863,Asgarnian ale(m3),1 +5864,Asgarnian ale(m3),1 +5865,Asgarnian ale(m4),1 +5866,Asgarnian ale(m4),1 +5867,Greenmans ale(m1),1 +5868,Greenmans ale(m1),1 +5869,Greenmans ale(m2),1 +5870,Greenmans ale(m2),1 +5871,Greenmans ale(m3),1 +5872,Greenmans ale(m3),1 +5873,Greenmans ale(m4),1 +5874,Greenmans ale(m4),1 +5875,Mind bomb(m1),1 +5876,Mind bomb(m1),1 +5877,Mind bomb(m2),1 +5878,Mind bomb(m2),1 +5879,Mind bomb(m3),1 +5880,Mind bomb(m3),1 +5881,Mind bomb(m4),1 +5882,Mind bomb(m4),1 +5883,Dragon bitter(m1),1 +5884,Dragon bitter(m1),1 +5885,Dragon bitter(m2),1 +5886,Dragon bitter(m2),1 +5887,Dragon bitter(m3),1 +5888,Dragon bitter(m3),1 +5889,Dragon bitter(m4),1 +5890,Dragon bitter(m4),1 +5891,M'light mead(m1),1 +5892,M'light mead(m1),1 +5893,M'light mead(m2),1 +5894,M'light mead(m2),1 +5895,M'light mead(m3),1 +5896,M'light mead(m3),1 +5897,M'light mead(m4),1 +5898,M'light mead(m4),1 +5899,Axeman's folly(m1),1 +5900,Axeman's folly(m1),1 +5901,Axeman's folly(m2),1 +5902,Axeman's folly(m2),1 +5903,Axeman's folly(m3),1 +5904,Axeman's folly(m3),1 +5905,Axeman's folly(m4),1 +5906,Axeman's folly(m4),1 +5907,Chef's delight(m1),1 +5908,Chef's delight(m1),1 +5909,Chef's delight(m2),1 +5910,Chef's delight(m2),1 +5911,Chef's delight(m3),1 +5912,Chef's delight(m3),1 +5913,Chef's delight(m4),1 +5914,Chef's delight(m4),1 +5915,Slayer respite(m1),1 +5916,Slayer respite(m1),1 +5917,Slayer respite(m2),1 +5918,Slayer respite(m2),1 +5919,Slayer respite(m3),1 +5920,Slayer respite(m3),1 +5921,Slayer respite(m4),1 +5922,Slayer respite(m4),1 +5923,Cider(m1),1 +5924,Cider(m1),1 +5925,Cider(m2),1 +5926,Cider(m2),1 +5927,Cider(m3),1 +5928,Cider(m3),1 +5929,Cider(m4),1 +5930,Cider(m4),1 +5931,Jute fibre,6 +5932,Jute fibre,6 +5933,Willow branch,2 +5934,Willow branch,2 +5935,Coconut milk,1 +5936,Weapon poison+(unf),1 +5937,Weapon poison+,288 +5938,Weapon poison+,288 +5939,Weapon poison++(unf),14 +5940,Weapon poison++,432 +5941,Weapon poison++,432 +5942,Antipoison+(unf),14 +5943,Antipoison+(4),360 +5944,Antipoison+(4),360 +5945,Antipoison+(3),288 +5946,Antipoison+(3),288 +5947,Antipoison+(2),216 +5948,Antipoison+(2),216 +5949,Antipoison+(1),144 +5950,Antipoison+(1),144 +5951,Antipoison++(unf),14 +5952,Antipoison++(4),360 +5953,Antipoison++(4),360 +5954,Antipoison++(3),288 +5955,Antipoison++(3),288 +5956,Antipoison++(2),216 +5957,Antipoison++(2),216 +5958,Antipoison++(1),144 +5959,Antipoison++(1),144 +5960,Tomatoes(1),1 +5961,Tomatoes(1),1 +5962,Tomatoes(2),1 +5963,Tomatoes(2),1 +5964,Tomatoes(3),1 +5965,Tomatoes(3),1 +5966,Tomatoes(4),1 +5967,Tomatoes(4),1 +5968,Tomatoes(5),1 +5969,Tomatoes(5),1 +5970,Curry leaf,19 +5971,Curry leaf,19 +5972,Papaya fruit,64 +5973,Papaya fruit,64 +5974,Coconut,87 +5975,Coconut,87 +5976,Coconut,87 +5977,Coconut,87 +5978,Coconut shell,1 +5979,Coconut shell,1 +5980,Calquat fruit,54 +5981,Calquat fruit,54 +5982,Watermelon,48 +5983,Watermelon,48 +5984,Watermelon slice,16 +5985,Watermelon slice,16 +5986,Sweetcorn,9 +5987,Sweetcorn,9 +5988,Cooked sweetcorn,9 +5989,Cooked sweetcorn,9 +5990,Burnt sweetcorn,1 +5991,Burnt sweetcorn,1 +5992,Apple mush,13 +5993,Apple mush,13 +5994,Hammerstone hops,4 +5995,Hammerstone hops,4 +5996,Asgarnian hops,5 +5997,Asgarnian hops,5 +5998,Yanillian hops,7 +5999,Yanillian hops,7 +6000,Krandorian hops,10 +6001,Krandorian hops,10 +6002,Wildblood hops,15 +6003,Wildblood hops,15 +6004,Mushroom,38 +6005,Mushroom,38 +6006,Barley,4 +6007,Barley,4 +6008,Barley malt,4 +6009,Barley malt,4 +6010,Marigolds,1 +6011,Marigolds,1 +6012,Nasturtiums,4 +6013,Nasturtiums,4 +6014,Rosemary,2 +6015,Rosemary,2 +6016,Cactus spine,1 +6017,Cactus spine,1 +6018,Poison ivy berries,65 +6019,Poison ivy berries,65 +6020,Leaves,1 +6021,Leaves,1 +6022,Leaves,1 +6023,Leaves,1 +6024,Leaves,1 +6025,Leaves,1 +6026,Leaves,1 +6027,Leaves,1 +6028,Leaves,1 +6029,Leaves,1 +6030,Leaves,1 +6031,Leaves,1 +6032,Compost,20 +6033,Compost,20 +6034,Supercompost,85 +6035,Supercompost,85 +6036,Plant cure,40 +6037,Plant cure,40 +6038,Magic string,1 +6039,Magic string,1 +6040,Amulet of nature,1 +6041,Pre-nature amulet,1 +6042,Pre-nature amulet,1 +6043,Oak roots,1 +6044,Oak roots,1 +6045,Willow roots,1 +6046,Willow roots,1 +6047,Maple roots,1 +6048,Maple roots,1 +6049,Yew roots,1 +6050,Yew roots,1 +6051,Magic roots,1 +6052,Magic roots,1 +6053,Spirit roots,1 +6054,Spirit roots,1 +6055,Weeds,1 +6056,Weeds,1 +6057,Hay sack,1 +6058,Hay sack,1 +6059,Scarecrow,1 +6060,Stool,1 +6061,Bronze bolts(p+),3 +6062,Bronze bolts(p++),3 +6063,Spirit tree,1 +6064,Bloody mourner top,20 +6065,Mourner top,150 +6066,Mourner trousers,20 +6067,Mourner trousers,100 +6068,Mourner gloves,20 +6069,Mourner boots,30 +6070,Mourner cloak,50 +6071,Mourner letter,1 +6072,Tegid's soap,10 +6073,Prifddinas' history,2 +6074,Picture,0 +6075,Eastern discovery,2 +6076,Picture,0 +6077,Eastern settlement,2 +6078,Picture,0 +6079,The great divide,2 +6080,Picture,0 +6081,Broken device,10 +6082,Fixed device,150 +6083,Tarnished key,1 +6084,Worn key,1 +6085,Red dye bellows,20 +6086,Blue dye bellows,20 +6087,Yellow dye bellows,20 +6088,Green dye bellows,20 +6089,Blue toad,1 +6090,Red toad,1 +6091,Yellow toad,1 +6092,Green toad,1 +6093,Rotten apples,1 +6094,Apple barrel,1 +6095,Naphtha apple mix,1 +6096,Toxic naphtha,1 +6097,Sieve,10 +6098,Toxic powder,50 +6099,Teleport crystal (4),50 +6100,Teleport crystal (3),45 +6101,Teleport crystal (2),35 +6102,Teleport crystal (1),15 +6103,Tiny elf crystal,1 +6104,New key,1 +6105,Elf,1 +6106,Ghostly boots,300 +6107,Ghostly robe,300 +6108,Ghostly robe,300 +6109,Ghostly hood,300 +6110,Ghostly gloves,300 +6111,Ghostly cloak,250 +6112,Kelda seed,2 +6113,Kelda hops,4 +6114,null,1 +6115,null,1 +6116,null,1 +6117,null,1 +6118,Kelda stout,2 +6119,Square stone,1 +6120,Square stone,1 +6121,Letter,1 +6122,A chair,1 +6123,Beer glass,1 +6124,Coconut milk,1 +6125,Enchanted lyre(2),1000 +6126,Enchanted lyre(3),1000 +6127,Enchanted lyre(4),1000 +6128,Rock-shell helm,35200 +6129,Rock-shell plate,65000 +6130,Rock-shell legs,64000 +6131,Spined helm,60000 +6132,Spined helm,60000 +6133,Spined body,7800 +6134,Spined body,7800 +6135,Spined chaps,3900 +6136,Spined chaps,3900 +6137,Skeletal helm,10000 +6138,Skeletal helm,10000 +6139,Skeletal top,45000 +6140,Skeletal top,45000 +6141,Skeletal bottoms,40000 +6142,Skeletal bottoms,40000 +6143,Spined boots,650 +6144,Spined boots,650 +6145,Rock-shell boots,650 +6146,Rock-shell boots,650 +6147,Skeletal boots,650 +6148,Skeletal boots,650 +6149,Spined gloves,650 +6150,Spined gloves,650 +6151,Rock-shell gloves,650 +6152,Rock-shell gloves,650 +6153,Skeletal gloves,650 +6154,Skeletal gloves,650 +6155,Dagannoth hide,60 +6156,Dagannoth hide,60 +6157,Rock-shell chunk,60 +6158,Rock-shell chunk,60 +6159,Rock-shell shard,60 +6160,Rock-shell shard,60 +6161,Rock-shell splinter,60 +6162,Rock-shell splinter,60 +6163,Skull piece,60 +6164,Skull piece,60 +6165,Ribcage piece,60 +6166,Ribcage piece,60 +6167,Fibula piece,60 +6168,Fibula piece,60 +6169,Circular hide,60 +6170,Circular hide,60 +6171,Flattened hide,60 +6172,Flattened hide,60 +6173,Stretched hide,60 +6174,Stretched hide,60 +6175,Rock-shell helm,35200 +6176,Rock-shell plate,65000 +6177,Rock-shell legs,64000 +6178,Raw pheasant,5 +6179,Raw pheasant,5 +6180,Lederhosen top,1 +6181,Lederhosen shorts,1 +6182,Lederhosen hat,1 +6183,Frog token,0 +6184,Prince tunic,0 +6185,Prince leggings,0 +6186,Princess blouse,0 +6187,Princess skirt,0 +6188,Frog mask,0 +6189,null,1 +6190,null,1 +6191,null,1 +6192,null,1 +6193,null,1 +6194,null,1 +6195,null,1 +6196,null,1 +6197,null,1 +6198,null,1 +6199,Mystery box,1 +6200,Raw fishlike thing,0 +6201,Picture,1 +6202,Fishlike thing,0 +6203,Picture,1 +6204,Raw fishlike thing,0 +6205,Picture,1 +6206,Fishlike thing,0 +6207,Picture,1 +6208,Man speak amulet,1 +6209,Small fishing net,5 +6210,Picture,1 +6211,Teak pyre logs,100 +6212,Teak pyre logs,100 +6213,Mahogany pyre log,200 +6214,Mahogany pyre log,200 +6215,Broodoo shield (10),3000 +6216,Broodoo shield (10),3000 +6217,Broodoo shield (9),2750 +6218,Broodoo shield (9),2750 +6219,Broodoo shield (8),3000 +6220,Broodoo shield (8),3000 +6221,Broodoo shield (7),3000 +6222,Broodoo shield (7),3000 +6223,Broodoo shield (6),3000 +6224,Broodoo shield (6),3000 +6225,Broodoo shield (5),3000 +6226,Broodoo shield (5),3000 +6227,Broodoo shield (4),3000 +6228,Broodoo shield (4),3000 +6229,Broodoo shield (3),3000 +6230,Broodoo shield (3),3000 +6231,Broodoo shield (2),3000 +6232,Broodoo shield (2),3000 +6233,Broodoo shield (1),3000 +6234,Broodoo shield (1),3000 +6235,Broodoo shield,3000 +6236,Broodoo shield,3000 +6237,Broodoo shield (10),3000 +6238,Broodoo shield (10),3000 +6239,Broodoo shield (9),3000 +6240,Broodoo shield (9),3000 +6241,Broodoo shield (8),3000 +6242,Broodoo shield (8),3000 +6243,Broodoo shield (7),3000 +6244,Broodoo shield (7),3000 +6245,Broodoo shield (6),3000 +6246,Broodoo shield (6),3000 +6247,Broodoo shield (5),3000 +6248,Broodoo shield (5),3000 +6249,Broodoo shield (4),3000 +6250,Broodoo shield (4),3000 +6251,Broodoo shield (3),3000 +6252,Broodoo shield (3),3000 +6253,Broodoo shield (2),3000 +6254,Broodoo shield (2),3000 +6255,Broodoo shield (1),3000 +6256,Broodoo shield (1),3000 +6257,Broodoo shield,3000 +6258,Broodoo shield,3000 +6259,Broodoo shield (10),3000 +6260,Broodoo shield (10),3000 +6261,Broodoo shield (9),3000 +6262,Broodoo shield (9),3000 +6263,Broodoo shield (8),3000 +6264,Broodoo shield (8),3000 +6265,Broodoo shield (7),3000 +6266,Broodoo shield (7),3000 +6267,Broodoo shield (6),3000 +6268,Broodoo shield (6),3000 +6269,Broodoo shield (5),3000 +6270,Broodoo shield (5),3000 +6271,Broodoo shield (4),3000 +6272,Broodoo shield (4),3000 +6273,Broodoo shield (3),3000 +6274,Broodoo shield (3),3000 +6275,Broodoo shield (2),3000 +6276,Broodoo shield (2),3000 +6277,Broodoo shield (1),3000 +6278,Broodoo shield (1),3000 +6279,Broodoo shield,3000 +6280,Broodoo shield,3000 +6281,Thatch spar light,10 +6282,Thatch spar light,10 +6283,Thatch spar med,15 +6284,Thatch spar med,15 +6285,Thatch spar dense,20 +6286,Thatch spar dense,20 +6287,Snake hide,35 +6288,Snake hide,35 +6289,Snakeskin,50 +6290,Snakeskin,50 +6291,Spider carcass,15 +6292,Spider carcass,15 +6293,Spider on stick,25 +6294,Spider on stick,25 +6295,Spider on shaft,20 +6296,Spider on shaft,20 +6297,Spider on stick,50 +6298,Spider on stick,50 +6299,Spider on shaft,40 +6300,Spider on shaft,40 +6301,Burnt spider,1 +6302,Burnt spider,1 +6303,Spider on shaft,1 +6304,Spider on shaft,1 +6305,Skewer stick,10 +6306,Trading sticks,1 +6307,null,1 +6308,null,1 +6309,null,1 +6310,null,1 +6311,Gout tuber,200 +6312,Gout tuber,200 +6313,Opal machete,500 +6314,Opal machete,500 +6315,Jade machete,1000 +6316,Jade machete,1000 +6317,Red topaz machete,2000 +6318,Red topaz machete,2000 +6319,Proboscis,5 +6320,null,5 +6321,null,1 +6322,Snakeskin body,1250 +6323,Snakeskin body,1250 +6324,Snakeskin chaps,1175 +6325,Snakeskin chaps,1175 +6326,Snakeskin bandana,300 +6327,Snakeskin bandana,300 +6328,Snakeskin boots,250 +6329,Snakeskin boots,250 +6330,Snakeskin v'brace,434 +6331,Snakeskin v'brace,434 +6332,Mahogany logs,50 +6333,Teak logs,30 +6334,Teak logs,30 +6335,Tribal mask,500 +6336,Tribal mask,500 +6337,Tribal mask,500 +6338,Tribal mask,500 +6339,Tribal mask,500 +6340,Tribal mask,500 +6341,Tribal top,300 +6342,Tribal top,300 +6343,Villager robe,250 +6344,Villager robe,250 +6345,Villager hat,200 +6346,Villager hat,200 +6347,Villager armband,150 +6348,Villager armband,150 +6349,Villager sandals,100 +6350,Villager sandals,100 +6351,Tribal top,300 +6352,Tribal top,300 +6353,Villager robe,250 +6354,Villager robe,250 +6355,Villager hat,200 +6356,Villager hat,200 +6357,Villager sandals,100 +6358,Villager sandals,100 +6359,Villager armband,150 +6360,Villager armband,150 +6361,Tribal top,300 +6362,Tribal top,300 +6363,Villager robe,250 +6364,Villager robe,250 +6365,Villager hat,200 +6366,Villager hat,200 +6367,Villager sandals,100 +6368,Villager sandals,100 +6369,Villager armband,150 +6370,Villager armband,150 +6371,Tribal top,300 +6372,Tribal top,300 +6373,Villager robe,250 +6374,Villager robe,250 +6375,Villager hat,200 +6376,Villager hat,200 +6377,Villager sandals,100 +6378,Villager sandals,100 +6379,Villager armband,150 +6380,Villager armband,150 +6381,Picture,1 +6382,Fez,20 +6383,Fez,20 +6384,Desert top,15 +6385,Desert top,15 +6386,Desert robes,25 +6387,Desert robes,25 +6388,Desert top,35 +6389,Desert top,35 +6390,Desert legs,25 +6391,Desert legs,25 +6392,Menap headgear,35 +6393,Menap headgear,35 +6394,Menaphite top,20 +6395,Menaphite top,20 +6396,Menaphite robe,40 +6397,Menaphite robe,40 +6398,Menap action kilt,20 +6399,Menap action kilt,20 +6400,Menap headgear,35 +6401,Menap headgear,35 +6402,Menaphite top,20 +6403,Menaphite top,20 +6404,Menaphite robe,40 +6405,Menaphite robe,40 +6406,Menap action kilt,20 +6407,Menap action kilt,20 +6408,Oak blackjack(o),400 +6409,Oak blackjack(o),400 +6410,Oak blackjack(d),400 +6411,Oak blackjack(d),400 +6412,Willow blackjack(o),800 +6413,Willow blackjack(o),800 +6414,Willow blackjack(d),800 +6415,Willow blackjack(d),800 +6416,Maple blackjack,1200 +6417,Maple blackjack,1200 +6418,Maple blackjack(o),1600 +6419,Maple blackjack(o),1600 +6420,Maple blackjack(d),1600 +6421,Maple blackjack(d),1600 +6422,Air rune,1 +6423,Air rune,1 +6424,Water rune,1 +6425,Water rune,1 +6426,Earth rune,1 +6427,Earth rune,1 +6428,Fire rune,1 +6429,Fire rune,1 +6430,Chaos rune,1 +6431,Chaos rune,1 +6432,Death rune,1 +6433,Death rune,1 +6434,Law rune,1 +6435,Law rune,1 +6436,Mind rune,1 +6437,Mind rune,1 +6438,Body rune,1 +6439,Body rune,1 +6440,null,1 +6441,null,1 +6442,null,1 +6443,null,1 +6444,null,1 +6445,null,1 +6446,null,1 +6447,null,1 +6448,Spadeful of coke,3 +6449,null,1 +6450,null,1 +6451,null,1 +6452,null,1 +6453,White rose seed,1 +6454,Red rose seed,1 +6455,Pink rose seed,1 +6456,Vine seed,1 +6457,Delphinium seed,1 +6458,Orchid seed,1 +6459,Orchid seed,1 +6460,Snowdrop seed,1 +6461,White tree shoot,1 +6462,White tree shoot,1 +6463,White tree shoot,1 +6464,White tree sapling,1 +6465,Ring of charos(a),1 +6466,Rune shards,1 +6467,Rune dust,1 +6468,Plant cure,1 +6469,White tree fruit,1 +6470,Compost potion(4),120 +6471,Compost potion(4),120 +6472,Compost potion(3),90 +6473,Compost potion(3),90 +6474,Compost potion(2),60 +6475,Compost potion(2),60 +6476,Compost potion(1),30 +6477,Compost potion(1),30 +6478,Trolley,1 +6479,List,1 +6480,null,1 +6481,null,1 +6482,null,1 +6483,null,1 +6484,null,1 +6485,null,1 +6486,null,1 +6487,null,1 +6488,null,1 +6489,null,1 +6490,null,1 +6491,null,1 +6492,null,1 +6493,null,1 +6494,null,1 +6495,null,1 +6496,null,1 +6497,null,1 +6498,null,1 +6499,null,1 +6500,null,1 +6501,null,1 +6502,null,1 +6503,null,1 +6504,null,1 +6505,null,1 +6506,null,1 +6507,null,1 +6508,null,1 +6509,null,1 +6510,null,1 +6511,null,1 +6512,null,1 +6513,null,1 +6514,Agility jump,1 +6515,Agility balance,1 +6516,Agility contortion,1 +6517,Agility climb,1 +6518,Agility jump,1 +6519,Agility balance,1 +6520,Agility contortion,1 +6521,Agility climb,1 +6522,Toktz-xil-ul,250 +6523,Toktz-xil-ak,40000 +6524,Toktz-ket-xil,45000 +6525,Toktz-xil-ek,25000 +6526,Toktz-mej-tal,35000 +6527,Tzhaar-ket-em,30000 +6528,Tzhaar-ket-om,50000 +6529,Tokkul,1 +6530,null,1 +6531,null,1 +6532,null,1 +6533,null,1 +6534,null,1 +6535,Toktz-xil-ak,40000 +6536,Toktz-ket-xil,45000 +6537,Toktz-xil-ek,25000 +6538,Toktz-mej-tal,35000 +6539,Tzhaar-ket-em,30000 +6540,Tzhaar-ket-om,50000 +6541,Mouse toy,1 +6542,Present,1 +6543,Antique lamp,1 +6544,Catspeak amulet(e),1 +6545,Chores,1 +6546,Recipe,1 +6547,Doctors hat,1 +6548,Nurse hat,1 +6549,Lazy cat,1 +6550,Lazy cat,1 +6551,Lazy cat,1 +6552,Lazy cat,1 +6553,Lazy cat,1 +6554,Lazy cat,1 +6555,Wily cat,1 +6556,Wily cat,1 +6557,Wily cat,1 +6558,Wily cat,1 +6559,Wily cat,1 +6560,Wily cat,1 +6561,Ahab's beer,2 +6562,Mud battlestaff,17000 +6563,Mystic mud staff,45000 +6564,Onyx ring,1 +6565,Onyx necklace,1 +6566,Onyx amulet,1 +6567,null,1 +6568,Obsidian cape,60000 +6569,Obsidian cape,60000 +6570,Fire cape,60000 +6571,Uncut onyx,300000 +6572,Uncut onyx,300000 +6573,Onyx,300000 +6574,Onyx,300000 +6575,Onyx ring,201000 +6576,Onyx ring,201000 +6577,Onyx necklace,201000 +6578,Onyx necklace,201000 +6579,Onyx amulet,201000 +6580,Onyx amulet,201000 +6581,Onyx amulet,201000 +6582,Onyx amulet,201000 +6583,Ring of stone,202000 +6584,Ring of stone,202000 +6585,Amulet of fury,202000 +6586,Amulet of fury,202000 +6587,White claws,360 +6588,White claws,360 +6589,White battleaxe,1248 +6590,White battleaxe,1248 +6591,White dagger,240 +6592,White dagger,240 +6593,White dagger(p),240 +6594,White dagger(p),240 +6595,White dagger(p+),240 +6596,White dagger(p+),240 +6597,White dagger(p++),240 +6598,White dagger(p++),240 +6599,White halberd,1920 +6600,White halberd,1920 +6601,White mace,432 +6602,White mace,432 +6603,White magic staff,200 +6604,White magic staff,200 +6605,White sword,624 +6606,White sword,624 +6607,White longsword,960 +6608,White longsword,960 +6609,White 2h sword,1920 +6610,White 2h sword,1920 +6611,White scimitar,768 +6612,White scimitar,768 +6613,White warhammer,980 +6614,White warhammer,980 +6615,White chainbody,1440 +6616,White chainbody,1440 +6617,White platebody,3840 +6618,White platebody,3840 +6619,White boots,576 +6620,White boots,576 +6621,White med helm,576 +6622,White med helm,576 +6623,White full helm,1056 +6624,White full helm,1056 +6625,White platelegs,1920 +6626,White platelegs,1920 +6627,White plateskirt,1920 +6628,White plateskirt,1920 +6629,White gloves,6 +6630,White gloves,6 +6631,White sq shield,1152 +6632,White sq shield,1152 +6633,White kiteshield,1632 +6634,White kiteshield,1632 +6635,Commorb,1 +6636,Solus's hat,1 +6637,Dark beast,1 +6638,Colour wheel,1 +6639,Hand mirror,1 +6640,Red crystal,1 +6641,Yellow crystal,1 +6642,Green crystal,1 +6643,Cyan crystal,1 +6644,Blue crystal,1 +6645,Magenta crystal,1 +6646,Fractured crystal,1 +6647,Fractured crystal,1 +6648,Item list,1 +6649,Edern's journal,1 +6650,Blackened crystal,1 +6651,Newly made crystal,1 +6652,Newly made crystal,1 +6653,Crystal trinket,1 +6654,Camo top,1 +6655,Camo bottoms,1 +6656,Camo helmet,1 +6657,Camo top,1 +6658,Camo bottoms,1 +6659,Camo helmet,1 +6660,Fishing explosive,1 +6661,Mogre,1 +6662,Broken fishing rod,0 +6663,Forlorn boot,0 +6664,Fishing explosive,60 +6665,Mudskipper hat,0 +6666,Flippers,1 +6667,Fishbowl,1 +6668,Fishbowl,0 +6669,Fishbowl,0 +6670,Fishbowl,0 +6671,Fishbowl,0 +6672,Fishbowl,0 +6673,Fishbowl and net,0 +6674,Tiny net,10 +6675,An empty box,1 +6676,Tinderbox,0 +6677,Guam in a box,5 +6678,Guam in a box?,5 +6679,Seaweed in a box,5 +6680,Seaweed in a box?,5 +6681,Ground guam,2 +6682,Ground guam,2 +6683,Ground seaweed,2 +6684,Ground seaweed,2 +6685,Saradomin brew(4),200 +6686,Saradomin brew(4),200 +6687,Saradomin brew(3),175 +6688,Saradomin brew(3),175 +6689,Saradomin brew(2),150 +6690,Saradomin brew(2),150 +6691,Saradomin brew(1),125 +6692,Saradomin brew(1),125 +6693,Crushed nest,1 +6694,Crushed nest,1 +6695,Desert lizard,1 +6696,Ice cooler,1 +6697,Pat of butter,4 +6698,Pat of butter,4 +6699,Burnt potato,1 +6700,Burnt potato,1 +6701,Baked potato,4 +6702,Baked potato,4 +6703,Potato with butter,8 +6704,Potato with butter,8 +6705,Potato with cheese,350 +6706,Potato with cheese,350 +6707,Camulet,1 +6708,Slayer gloves,1 +6709,Fever spider,1 +6710,Blindweed seed,166 +6711,Blindweed,1 +6712,Bucket of water,1 +6713,Wrench,5 +6714,Holy wrench,5 +6715,Sluglings,1 +6716,Karamthulhu,1 +6717,Karamthulhu,20000 +6718,Fever spider body,1 +6719,Unsanitary swill,1 +6720,Slayer gloves,200 +6721,Rusty scimitar,1 +6722,Zombie head,0 +6723,Fishbowl,1 +6724,Seercull,8000 +6725,Seercull,8000 +6726,Mud battlestaff,17000 +6727,Mystic mud staff,45000 +6728,Bonemeal,1 +6729,Dagannoth bones,1 +6730,Dagannoth bones,1 +6731,Seers ring,10000 +6732,Seers ring,10000 +6733,Archers ring,10000 +6734,Archers ring,10000 +6735,Warrior ring,10000 +6736,Warrior ring,10000 +6737,Berserker ring,10000 +6738,Berserker ring,10000 +6739,Dragon axe,55000 +6740,Dragon axe,55000 +6741,Broken axe,1800 +6742,Broken axe,1800 +6743,Picture,54999 +6744,Picture,54999 +6745,Silverlight,50 +6746,Darklight,500 +6747,Demonic sigil mould,0 +6748,Demonic sigil,0 +6749,Demonic tome,0 +6750,Black desert shirt,40 +6751,Tinderbox,0 +6752,Black desert robe,30 +6753,Picture,0 +6754,Enchanted key,1 +6755,Journal,1 +6756,Letter,1 +6757,Letter,1 +6758,Scroll,1 +6759,Chest,1 +6760,Guthix mjolnir,625 +6761,Guthix mjolnir,625 +6762,Saradomin mjolnir,625 +6763,Saradomin mjolnir,625 +6764,Zamorak mjolnir,625 +6765,Zamorak mjolnir,625 +6766,Cat antipoison,1 +6767,Book,1 +6768,Poisoned cheese,1 +6769,Music scroll,1 +6770,Directions,1 +6771,Pot of weeds,1 +6772,Smouldering pot,1 +6773,Rat pole,1 +6774,Rat pole,1 +6775,Rat pole,1 +6776,Rat pole,1 +6777,Rat pole,1 +6778,Rat pole,1 +6779,Rat pole,1 +6780,Menaphite thug,1 +6781,Bandit,1 +6782,Bandit,1 +6783,null,1 +6784,null,1 +6785,Statuette,1 +6786,Robe of elidinis,1 +6787,Robe of elidinis,1 +6788,Torn robe,1 +6789,Torn robe,1 +6790,Shoes,1 +6791,Sole,1 +6792,Ancestral key,1 +6793,Ballad,1 +6794,Choc-ice,30 +6795,Choc-ice,30 +6796,Lamp,1 +6797,Watering can,1 +6798,Champion scroll,1 +6799,Champion scroll,1 +6800,Champion scroll,1 +6801,Champion scroll,1 +6802,Champion scroll,1 +6803,Champion scroll,1 +6804,Champion scroll,1 +6805,Champion scroll,1 +6806,Champion scroll,1 +6807,Champion scroll,1 +6808,Champion scroll,1 +6809,Granite legs,66000 +6810,Bonemeal,1 +6811,Skeletal wyvern,1 +6812,Wyvern bones,1 +6813,Granite legs,66000 +6814,Fur,10 +6815,Fur,10 +6816,Wyvern bones,1 +6817,Slender blade,1 +6818,Bow-sword,1 +6819,Large pouch,1 +6820,Relic,1 +6821,Orb,1 +6822,Star bauble,1 +6823,Star bauble,1 +6824,Star bauble,1 +6825,Star bauble,1 +6826,Star bauble,1 +6827,Star bauble,1 +6828,Box bauble,1 +6829,Box bauble,1 +6830,Box bauble,1 +6831,Box bauble,1 +6832,Box bauble,1 +6833,Box bauble,1 +6834,Diamond bauble,1 +6835,Diamond bauble,1 +6836,Diamond bauble,1 +6837,Diamond bauble,1 +6838,Diamond bauble,1 +6839,Diamond bauble,1 +6840,Tree bauble,1 +6841,Tree bauble,1 +6842,Tree bauble,1 +6843,Tree bauble,1 +6844,Tree bauble,1 +6845,Tree bauble,1 +6846,Bell bauble,1 +6847,Bell bauble,1 +6848,Bell bauble,1 +6849,Bell bauble,1 +6850,Bell bauble,1 +6851,Bell bauble,1 +6852,Puppet box,1 +6853,Bauble box,1 +6854,Puppet box,1 +6855,Bauble box,1 +6856,Bobble hat,1 +6857,Bobble scarf,1 +6858,Jester hat,1 +6859,Jester scarf,1 +6860,Tri-jester hat,1 +6861,Tri-jester scarf,1 +6862,Woolly hat,1 +6863,Woolly scarf,1 +6864,Marionette handle,60000 +6865,Blue marionette,1 +6866,Green marionette,1 +6867,Red marionette,1 +6868,Blue marionette,1 +6869,Green marionette,1 +6870,Red marionette,1 +6871,Red marionette,1 +6872,Red marionette,1 +6873,Red marionette,1 +6874,Red marionette,1 +6875,Blue marionette,1 +6876,Blue marionette,1 +6877,Blue marionette,1 +6878,Blue marionette,1 +6879,Green marionette,1 +6880,Green marionette,1 +6881,Green marionette,1 +6882,Green marionette,1 +6883,Peach,1 +6884,null,1 +6885,Progress hat,1 +6886,Progress hat,1 +6887,Progress hat,1 +6888,Guardian statue,1 +6889,Mage's book,500 +6890,Mage's book,500 +6891,Arena book,1 +6892,Tinderbox,0 +6893,Leather boots,0 +6894,Adamant kiteshield,0 +6895,Adamant med helm,0 +6896,Emerald,0 +6897,Rune longsword,0 +6898,Cylinder,0 +6899,Cube,0 +6900,Icosahedron,0 +6901,Pentamid,0 +6902,Orb,1 +6903,Dragonstone,0 +6904,Animals' bones,1 +6905,Animals' bones,1 +6906,Animals' bones,1 +6907,Animals' bones,1 +6908,Beginner wand,1200 +6909,Beginner wand,1200 +6910,Apprentice wand,1500 +6911,Apprentice wand,1500 +6912,Teacher wand,2000 +6913,Teacher wand,2000 +6914,Master wand,2500 +6915,Master wand,2500 +6916,Infinity top,140000 +6917,Infinity top,140000 +6918,Infinity hat,17000 +6919,Infinity hat,17000 +6920,Infinity boots,12000 +6921,Infinity boots,12000 +6922,Infinity gloves,12000 +6923,Infinity gloves,12000 +6924,Infinity bottoms,90000 +6925,Infinity bottoms,90000 +6926,Bones to peaches,1 +6927,null,1 +6928,null,1 +6929,null,1 +6930,null,1 +6931,null,1 +6932,null,1 +6933,null,1 +6934,null,1 +6935,null,1 +6936,null,1 +6937,null,1 +6938,null,1 +6939,null,1 +6940,null,1 +6941,null,1 +6942,null,1 +6943,null,1 +6944,null,1 +6945,Sandy hand,1 +6946,Beer-soaked hand,1 +6947,Bert's rota,1 +6948,Sandy's rota,1 +6949,A magic scroll,1 +6950,Magical orb,1 +6951,Magical orb (a),1 +6952,Truth serum,1 +6953,Bottled water,1 +6954,Redberry juice,1 +6955,Pink dye,1 +6956,Rose-tinted lens,1 +6957,Wizard's head,1 +6958,Sand,1 +6959,Cape,1 +6960,Cape,1 +6961,Baguette,25 +6962,Triangle sandwich,25 +6963,Roll,25 +6964,Coins,6 +6965,Square sandwich,25 +6966,Prison key,1 +6967,Dragon med helm,6 +6968,Triangle sandwich,25 +6969,Shark,6 +6970,Pyramid top,1 +6971,Sandstone (1kg),1 +6972,Sandstone (1kg),1 +6973,Sandstone (2kg),1 +6974,Sandstone (2kg),1 +6975,Sandstone (5kg),1 +6976,Sandstone (5kg),1 +6977,Sandstone (10kg),1 +6978,Sandstone (10kg),1 +6979,Granite (500g),1 +6980,Granite (500g),1 +6981,Granite (2kg),1 +6982,Granite (2kg),1 +6983,Granite (5kg),1 +6984,Granite (5kg),1 +6985,Sandstone (20kg),1 +6986,Sandstone (32kg),1 +6987,Sandstone body,1 +6988,Sandstone base,1 +6989,Stone head,1 +6990,Stone head,1 +6991,Stone head,1 +6992,Stone head,1 +6993,Z sigil,1 +6994,M sigil,1 +6995,R sigil,1 +6996,K sigil,1 +6997,Stone left arm,1 +6998,Stone right arm,1 +6999,Stone left leg,1 +7000,Stone right leg,1 +7001,Camel mould (p),1 +7002,Stone head,1 +7003,Camel mask,1 +7004,Chisel,1 +7005,null,1 +7006,null,1 +7007,null,1 +7008,null,1 +7009,null,1 +7010,null,1 +7011,null,1 +7012,null,1 +7013,null,1 +7014,null,1 +7015,null,1 +7016,null,1 +7017,null,1 +7018,null,1 +7019,null,1 +7020,null,1 +7021,null,1 +7022,null,1 +7023,null,1 +7024,null,1 +7025,null,1 +7026,null,1 +7027,null,1 +7028,null,1 +7029,null,1 +7030,null,1 +7031,null,1 +7032,null,1 +7033,null,1 +7034,null,1 +7035,null,1 +7036,null,1 +7037,null,1 +7038,null,1 +7039,null,1 +7040,null,1 +7041,null,1 +7042,null,1 +7043,null,1 +7044,null,1 +7045,null,1 +7046,null,1 +7047,null,1 +7048,null,1 +7049,null,1 +7050,Swarm,1 +7051,Unlit bug lantern,130 +7052,Unlit bug lantern,130 +7053,Lit bug lantern,130 +7054,Chilli potato,13 +7055,Chilli potato,13 +7056,Egg potato,12 +7057,Egg potato,12 +7058,Mushroom potato,45 +7059,Mushroom potato,45 +7060,Tuna potato,113 +7061,Tuna potato,113 +7062,Chilli con carne,13 +7063,Chilli con carne,13 +7064,Egg and tomato,12 +7065,Egg and tomato,12 +7066,Mushroom & onion,45 +7067,Mushroom & onion,45 +7068,Tuna and corn,113 +7069,Tuna and corn,113 +7070,Minced meat,8 +7071,Minced meat,8 +7072,Spicy sauce,9 +7073,Spicy sauce,9 +7074,Chopped garlic,7 +7075,Chopped garlic,7 +7076,Uncooked egg,8 +7077,Uncooked egg,8 +7078,Scrambled egg,8 +7079,Scrambled egg,8 +7080,Sliced mushrooms,42 +7081,Sliced mushrooms,42 +7082,Fried mushrooms,42 +7083,Fried mushrooms,42 +7084,Fried onions,7 +7085,Fried onions,7 +7086,Chopped tuna,104 +7087,Chopped tuna,104 +7088,Sweetcorn,13 +7089,Sweetcorn,13 +7090,Burnt egg,5 +7091,Burnt egg,5 +7092,Burnt onion,5 +7093,Burnt onion,5 +7094,Burnt mushroom,5 +7095,Burnt mushroom,5 +7096,Board game piece,1 +7097,Board game piece,1 +7098,Board game piece,1 +7099,Board game piece,1 +7100,Board game piece,1 +7101,Board game piece,1 +7102,Board game piece,1 +7103,Board game piece,1 +7104,Board game piece,1 +7105,Board game piece,1 +7106,Board game piece,1 +7107,Board game piece,1 +7108,Gunpowder,52 +7109,Fuse,52 +7110,Stripy pirate shirt,300 +7111,Stripy pirate shirt,300 +7112,Pirate bandana,100 +7113,Pirate bandana,100 +7114,Pirate boots,350 +7115,Pirate boots,350 +7116,Pirate leggings,350 +7117,Pirate leggings,350 +7118,Canister,1 +7119,Cannon ball,1 +7120,Ramrod,1 +7121,Repair plank,1 +7122,Stripy pirate shirt,300 +7123,Stripy pirate shirt,300 +7124,Pirate bandana,100 +7125,Pirate bandana,100 +7126,Pirate leggings,350 +7127,Pirate leggings,350 +7128,Stripy pirate shirt,300 +7129,Stripy pirate shirt,300 +7130,Pirate bandana,100 +7131,Pirate bandana,100 +7132,Pirate leggings,350 +7133,Pirate leggings,350 +7134,Stripy pirate shirt,300 +7135,Stripy pirate shirt,300 +7136,Pirate bandana,100 +7137,Pirate bandana,100 +7138,Pirate leggings,350 +7139,Pirate leggings,350 +7140,Lucky cutlass,2560 +7141,Harry's cutlass,1040 +7142,Rapier,25600 +7143,Plunder,1 +7144,Book o' piracy,1 +7145,Cannon barrel,1 +7146,Broken cannon,1 +7147,Cannon balls,1 +7148,Repair plank,1 +7149,Canister,1 +7150,Tacks,1 +7151,null,1 +7152,null,1 +7153,null,1 +7154,null,1 +7155,Rope,18 +7156,Tinderbox,1 +7157,Braindeath 'rum',30 +7158,Dragon 2h sword,220000 +7159,Insulated boots,200 +7160,Killerwatt,1 +7161,Insulated boots,200 +7162,Pie recipe book,5 +7163,Pie recipe book,5 +7164,Part mud pie,22 +7165,Part mud pie,22 +7166,Part mud pie,26 +7167,Part mud pie,26 +7168,Raw mud pie,27 +7169,Raw mud pie,27 +7170,Mud pie,54 +7171,Mud pie,54 +7172,Part garden pie,8 +7173,Part garden pie,8 +7174,Part garden pie,11 +7175,Part garden pie,11 +7176,Raw garden pie,12 +7177,Raw garden pie,12 +7178,Garden pie,90 +7179,Garden pie,90 +7180,Half a garden pie,12 +7181,Half a garden pie,12 +7182,Part fish pie,24 +7183,Part fish pie,24 +7184,Part fish pie,49 +7185,Part fish pie,49 +7186,Raw fish pie,50 +7187,Raw fish pie,50 +7188,Fish pie,100 +7189,Fish pie,100 +7190,Half a fish pie,50 +7191,Half a fish pie,50 +7192,Part admiral pie,54 +7193,Part admiral pie,54 +7194,Part admiral pie,154 +7195,Part admiral pie,154 +7196,Raw admiral pie,155 +7197,Raw admiral pie,155 +7198,Admiral pie,310 +7199,Admiral pie,310 +7200,Half an admiral pie,155 +7201,Half an admiral pie,155 +7202,Part wild pie,5 +7203,Part wild pie,5 +7204,Part wild pie,90 +7205,Part wild pie,90 +7206,Raw wild pie,91 +7207,Raw wild pie,91 +7208,Wild pie,182 +7209,Wild pie,182 +7210,Half a wild pie,91 +7211,Half a wild pie,91 +7212,Part summer pie,21 +7213,Part summer pie,21 +7214,Part summer pie,69 +7215,Part summer pie,69 +7216,Raw summer pie,70 +7217,Raw summer pie,70 +7218,Summer pie,140 +7219,Summer pie,140 +7220,Half a summer pie,70 +7221,Half a summer pie,70 +7222,Burnt rabbit,1 +7223,Roast rabbit,20 +7224,Skewered rabbit,84 +7225,Iron spit,64 +7226,Burnt chompy,1 +7227,Burnt chompy,1 +7228,Cooked chompy,85 +7229,Cooked chompy,85 +7230,Skewered chompy,149 +7231,Burnt rabbit,1 +7232,Roast rabbit,20 +7233,Skewered rabbit,84 +7234,Iron spit,64 +7235,Skewered chompy,149 +7236,Clue scroll,1 +7237,Casket,50 +7238,Clue scroll,1 +7239,Clue scroll,1 +7240,Casket,50 +7241,Clue scroll,1 +7242,Casket,50 +7243,Clue scroll,1 +7244,Casket,50 +7245,Clue scroll,1 +7246,Casket,50 +7247,Clue scroll,1 +7248,Clue scroll,1 +7249,Clue scroll,1 +7250,Clue scroll,1 +7251,Clue scroll,1 +7252,Clue scroll,1 +7253,Clue scroll,1 +7254,Clue scroll,1 +7255,Clue scroll,1 +7256,Clue scroll,1 +7257,Casket,50 +7258,Clue scroll,1 +7259,Casket,50 +7260,Clue scroll,1 +7261,Casket,50 +7262,Clue scroll,1 +7263,Casket,50 +7264,Clue scroll,1 +7265,Casket,50 +7266,Clue scroll,1 +7267,Casket,50 +7268,Clue scroll,1 +7269,Challenge scroll,1 +7270,Clue scroll,1 +7271,Challenge scroll,1 +7272,Clue scroll,1 +7273,Challenge scroll,1 +7274,Clue scroll,1 +7275,Challenge scroll,1 +7276,Clue scroll,1 +7277,Challenge scroll,1 +7278,Clue scroll,1 +7279,Challenge scroll,1 +7280,Clue scroll,1 +7281,Challenge scroll,1 +7282,Clue scroll,1 +7283,Challenge scroll,1 +7284,Clue scroll,1 +7285,Challenge scroll,1 +7286,Clue scroll,1 +7287,Casket,50 +7288,Clue scroll,1 +7289,Casket,50 +7290,Clue scroll,1 +7291,Casket,50 +7292,Clue scroll,1 +7293,Casket,50 +7294,Clue scroll,1 +7295,Casket,50 +7296,Clue scroll,1 +7297,Key,1 +7298,Clue scroll,1 +7299,Key,1 +7300,Clue scroll,1 +7301,Clue scroll,1 +7302,Key,1 +7303,Clue scroll,1 +7304,Clue scroll,1 +7305,Clue scroll,1 +7306,Casket,50 +7307,Clue scroll,1 +7308,Casket,50 +7309,Clue scroll,1 +7310,Casket,50 +7311,Clue scroll,1 +7312,Casket,50 +7313,Clue scroll,1 +7314,Casket,50 +7315,Clue scroll,1 +7316,Casket,50 +7317,Clue scroll,1 +7318,Casket,50 +7319,Red boater,225 +7320,Red boater,225 +7321,Orange boater,225 +7322,Orange boater,225 +7323,Green boater,225 +7324,Green boater,225 +7325,Blue boater,225 +7326,Blue boater,225 +7327,Black boater,225 +7328,Black boater,225 +7329,Red firelighter,15 +7330,Green firelighter,15 +7331,Blue firelighter,15 +7332,Black shield(h1),1632 +7333,Black shield(h1),1632 +7334,Adamant shield(h1),5440 +7335,Adamant shield(h1),5440 +7336,Rune shield(h1),54400 +7337,Rune shield(h1),54400 +7338,Black shield(h2),16325 +7339,Black shield(h2),16325 +7340,Adamant shield(h2),5440 +7341,Adamant shield(h2),5440 +7342,Rune shield(h2),54400 +7343,Rune shield(h2),54400 +7344,Black shield(h3),1632 +7345,Black shield(h3),1632 +7346,Adamant shield(h3),5440 +7347,Adamant shield(h3),5440 +7348,Rune shield(h3),54400 +7349,Rune shield(h3),54400 +7350,Black shield(h4),1632 +7351,Black shield(h4),1632 +7352,Adamant shield(h4),5440 +7353,Adamant shield(h4),5440 +7354,Rune shield(h4),54400 +7355,Rune shield(h4),54400 +7356,Black shield(h5),1632 +7357,Black shield(h5),1632 +7358,Adamant shield(h5),5440 +7359,Adamant shield(h5),5440 +7360,Rune shield(h5),54400 +7361,Rune shield(h5),54400 +7362,Studded body (g),850 +7363,Studded body (g),850 +7364,Studded body (t),850 +7365,Studded body (t),850 +7366,Studded chaps (g),750 +7367,Studded chaps (g),750 +7368,Studded chaps (t),750 +7369,Studded chaps (t),750 +7370,D'hide body(g),7800 +7371,D'hide body(g),7800 +7372,D'hide body (t),7800 +7373,D'hide body (t),7800 +7374,D'hide body (g),9360 +7375,D'hide body (g),9360 +7376,D'hide body (t),9360 +7377,D'hide body (t),9360 +7378,D'hide chaps (g),3900 +7379,D'hide chaps (g),3900 +7380,D'hide chaps (t),3900 +7381,D'hide chaps (t),3900 +7382,D'hide chaps (g),4320 +7383,D'hide chaps (g),4320 +7384,D'hide chaps (t),4320 +7385,D'hide chaps (t),4320 +7386,Blue skirt (g),2 +7387,Blue skirt (g),2 +7388,Blue skirt (t),2 +7389,Blue skirt (t),2 +7390,Wizard robe (g),15 +7391,Wizard robe (g),15 +7392,Wizard robe (t),15 +7393,Wizard robe (t),15 +7394,Wizard hat (g),2 +7395,Wizard hat (g),2 +7396,Wizard hat (t),2 +7397,Wizard hat (t),2 +7398,Enchanted robe,80000 +7399,Enchanted top,120000 +7400,Enchanted hat,15000 +7401,Enchanted robe,80000 +7402,Enchanted top,120000 +7403,Enchanted hat,15000 +7404,Red logs,4 +7405,Green logs,4 +7406,Blue logs,4 +7407,Dragon 2h sword,220000 +7408,Draynor skull,1 +7409,Magic secateurs,1 +7410,Queen's secateurs,1 +7411,Symptoms list,1 +7412,null,1 +7413,Bird's nest,1 +7414,Paddle,1 +7415,Paddle,1 +7416,Mole claw,600 +7417,Mole claw,600 +7418,Mole skin,400 +7419,Mole skin,400 +7420,Mutated zygomite,1 +7421,Fungicide spray 10,300 +7422,Fungicide spray 9,1 +7423,Fungicide spray 8,1 +7424,Fungicide spray 7,1 +7425,Fungicide spray 6,1 +7426,Fungicide spray 5,1 +7427,Fungicide spray 4,1 +7428,Fungicide spray 3,1 +7429,Fungicide spray 2,1 +7430,Fungicide spray 1,1 +7431,Fungicide spray 0,1 +7432,Fungicide,10 +7433,Wooden spoon,35 +7434,Wooden spoon,35 +7435,Egg whisk,50 +7436,Egg whisk,50 +7437,Spork,325 +7438,Spork,325 +7439,Spatula,1920 +7440,Spatula,1920 +7441,Frying pan,1660 +7442,Frying pan,1660 +7443,Skewer,3200 +7444,Skewer,3200 +7445,Rolling pin,14400 +7446,Rolling pin,14400 +7447,Kitchen knife,8000 +7448,Kitchen knife,8000 +7449,Meat tenderiser,41500 +7450,Meat tenderiser,41500 +7451,Cleaver,25600 +7452,Cleaver,25600 +7453,Gloves,50 +7454,Gloves,100 +7455,Gloves,250 +7456,Gloves,500 +7457,Gloves,1000 +7458,Gloves,1500 +7459,Gloves,2500 +7460,Gloves,5000 +7461,Gloves,100000 +7462,Gloves,100000 +7463,Cornflour,2 +7464,Book on chickens,1 +7465,Vanilla pod,1 +7466,Cornflour,1 +7467,Tinderbox,0 +7468,Pot of cornflour,10 +7469,Tinderbox,0 +7470,Cornflour mixture,1 +7471,Milky mixture,1 +7472,Cinnamon,1 +7473,Brulee,1 +7474,Brulee,1 +7475,Brulee,1 +7476,Brulee supreme,1 +7477,Evil chicken's egg,1 +7478,Dragon token,1 +7479,Spicy stew,20 +7480,Red spice (4),1 +7481,Red spice (3),1 +7482,Red spice (2),1 +7483,Red spice (1),1 +7484,Orange spice (4),1 +7485,Orange spice (3),1 +7486,Orange spice (2),1 +7487,Orange spice (1),1 +7488,Brown spice (4),1 +7489,Brown spice (3),1 +7490,Brown spice (2),1 +7491,Brown spice (1),1 +7492,Yellow spice (4),1 +7493,Yellow spice (3),1 +7494,Yellow spice (2),1 +7495,Yellow spice (1),1 +7496,Empty spice shaker,1 +7497,Dirty blast,30 +7498,Antique lamp,1 +7499,Evil dave,1 +7500,Dwarf,1 +7501,Goblins,1 +7502,Lumbridge guide,1 +7503,Monkey,1 +7504,Osman,1 +7505,Pirate pete,1 +7506,Sir amik varze,1 +7507,Skrach,1 +7508,Asgoldian ale,2 +7509,Dwarven rock cake,1 +7510,Dwarven rock cake,1 +7511,Slop of compromise,10 +7512,Soggy bread,6 +7513,Spicy maggots,3 +7514,Dyed orange,2 +7515,Breadcrumbs,1 +7516,Kelp,1 +7517,Ground kelp,1 +7518,Crab meat,1 +7519,Crab meat,1 +7520,Burnt crab meat,1 +7521,Cooked crab meat,50 +7522,Cooked crab meat,50 +7523,Cooked crab meat,40 +7524,Cooked crab meat,30 +7525,Cooked crab meat,20 +7526,Cooked crab meat,10 +7527,Ground crab meat,1 +7528,Ground cod,1 +7529,Raw fishcake,100 +7530,Cooked fishcake,100 +7531,Burnt fishcake,1 +7532,Mudskipper hide,1 +7533,Rock,1 +7534,Fishbowl helmet,1 +7535,Diving apparatus,1 +7536,Fresh crab claw,1 +7537,Crab claw,1 +7538,Fresh crab shell,1 +7539,Crab helmet,1 +7540,Broken crab claw,1 +7541,Broken crab shell,1 +7542,Cake of guidance,50 +7543,Raw guide cake,20 +7544,Enchanted egg,4 +7545,Enchanted milk,6 +7546,Enchanted flour,10 +7547,Druid pouch,5 +7548,Potato seed,1 +7549,Potato seed,1 +7550,Onion seed,1 +7551,Onion seed,1 +7552,Mithril arrow,1 +7553,Mithril arrow,1 +7554,Fire rune,1 +7555,Fire rune,1 +7556,Water rune,1 +7557,Water rune,1 +7558,Air rune,1 +7559,Air rune,1 +7560,Chaos rune,1 +7561,Chaos rune,1 +7562,Tomato seed,1 +7563,Tomato seed,1 +7564,Balloon toad,1 +7565,Balloon toad,1 +7566,Raw jubbly,100 +7567,Raw jubbly,100 +7568,Cooked jubbly,150 +7569,Cooked jubbly,150 +7570,Burnt jubbly,1 +7571,Burnt jubbly,1 +7572,Red banana,5 +7573,Tchiki monkey nuts,5 +7574,Sliced red banana,5 +7575,Tchiki nut paste,5 +7576,Snake corpse,1 +7577,Raw stuffed snake,1 +7578,Odd stuffed snake,1 +7579,Stuffed snake,1 +7580,Snake over-cooked,1 +7581,Overgrown hellcat,1 +7582,Hell cat,1 +7583,Hell-kitten,1 +7584,Lazy hell cat,1 +7585,Wily hellcat,1 +7586,Dummy,1 +7587,Coffin,1 +7588,Coffin,1 +7589,Coffin,1 +7590,Coffin,1 +7591,Coffin,1 +7592,Zombie shirt,1 +7593,Zombie trousers,1 +7594,Zombie mask,1 +7595,Zombie gloves,1 +7596,Zombie boots,1 +7597,Item,1 +7598,Item,1 +7599,Item,1 +7600,Item,1 +7601,Item,1 +7602,Item,1 +7603,Item,1 +7604,Item,1 +7605,Item,1 +7606,Item,1 +7607,Item,1 +7608,Item,1 +7609,Item,1 +7610,Item,1 +7611,Item,1 +7612,Item,1 +7613,Item,1 +7614,Item,1 +7615,Item,1 +7616,Item,1 +7617,Item,1 +7618,Item,1 +7619,null,1 +7620,Silvthrill rod,1 +7621,null,1 +7622,Bucket of rubble,1 +7623,Tinderbox,0 +7624,Bucket of rubble,1 +7625,Tinderbox,0 +7626,Bucket of rubble,1 +7627,Tinderbox,0 +7628,Plaster fragment,1 +7629,Dusty scroll,1 +7630,Crate,1 +7631,Tinderbox,0 +7632,Temple library key,1 +7633,Ancient book,1 +7634,Battered tome,1 +7635,Leather book,1 +7636,Rod dust,1 +7637,Silvthrill rod,1 +7638,Silvthrill rod,1 +7639,Rod of ivandis(10),1000 +7640,Rod of ivandis(9),1000 +7641,Rod of ivandis(8),1000 +7642,Rod of ivandis(7),1000 +7643,Rod of ivandis(6),1000 +7644,Rod of ivandis(5),1000 +7645,Rod of ivandis(4),1000 +7646,Rod of ivandis(3),1000 +7647,Rod of ivandis(2),1000 +7648,Rod of ivandis(1),1000 +7649,Rod clay mould,100 +7650,Silver dust,200 +7651,Silver dust,200 +7652,Guthix balance(unf),110 +7653,Guthix balance(unf),110 +7654,Guthix balance(unf),110 +7655,Guthix balance(unf),110 +7656,Guthix balance(unf),110 +7657,Guthix balance(unf),110 +7658,Guthix balance(unf),110 +7659,Guthix balance(unf),110 +7660,Guthix balance(4),200 +7661,Guthix balance(4),200 +7662,Guthix balance(3),200 +7663,Guthix balance(3),200 +7664,Guthix balance(2),200 +7665,Guthix balance(2),200 +7666,Guthix balance(1),200 +7667,Guthix balance(1),200 +7668,Gadderhammer,3000 +7669,Gadderhammer,3000 +7670,null,1 +7671,Boxing gloves,0 +7672,Picture,1 +7673,Boxing gloves,0 +7674,Perfect,1 +7675,Wooden sword,1 +7676,Wooden shield,1 +7677,Treasure stone,1 +7678,Prize key,1 +7679,Pugel,1 +7680,Pugel,1 +7681,Game book,1 +7682,Hoop,1 +7683,Hoop,1 +7684,Dart,1 +7685,Dart,1 +7686,Bow and arrow,1 +7687,Bow and arrow,1 +7688,Kettle,1 +7689,Kettle,1 +7690,Full kettle,1 +7691,Hot kettle,1 +7692,Pot of tea (4),1 +7693,Pot of tea (4),1 +7694,Pot of tea (3),1 +7695,Pot of tea (3),1 +7696,Pot of tea (2),1 +7697,Pot of tea (2),1 +7698,Pot of tea (1),1 +7699,Pot of tea (1),1 +7700,Teapot with leaves,1 +7701,Teapot with leaves,1 +7702,Teapot,1 +7703,Teapot,1 +7704,Pot of tea (4),1 +7705,Pot of tea (4),1 +7706,Pot of tea (3),1 +7707,Pot of tea (3),1 +7708,Pot of tea (2),1 +7709,Pot of tea (2),1 +7710,Pot of tea (1),1 +7711,Pot of tea (1),1 +7712,Teapot with leaves,1 +7713,Teapot with leaves,1 +7714,Teapot,1 +7715,Teapot,1 +7716,Pot of tea (4),1 +7717,Pot of tea (4),1 +7718,Pot of tea (3),1 +7719,Pot of tea (3),1 +7720,Pot of tea (2),1 +7721,Pot of tea (2),1 +7722,Pot of tea (1),1 +7723,Pot of tea (1),1 +7724,Teapot with leaves,1 +7725,Teapot with leaves,1 +7726,Teapot,1 +7727,Teapot,1 +7728,Empty cup,2 +7729,Empty cup,2 +7730,Cup of tea,10 +7731,Cup of tea,10 +7732,Porcelain cup,2 +7733,Cup of tea,10 +7734,Cup of tea,10 +7735,Porcelain cup,2 +7736,Cup of tea,10 +7737,Cup of tea,10 +7738,Tea leaves,1 +7739,Tea leaves,1 +7740,Beer,2 +7741,Beer,2 +7742,Beer glass,2 +7743,Beer glass,2 +7744,Asgarnian ale,2 +7745,Asgarnian ale,2 +7746,Greenman's ale,2 +7747,Greenman's ale,2 +7748,Dragon bitter,2 +7749,Dragon bitter,2 +7750,Moonlight mead,5 +7751,Moonlight mead,5 +7752,Cider,2 +7753,Cider,2 +7754,Chef's delight,2 +7755,Chef's delight,2 +7756,Paintbrush,1 +7757,Paintbrush,1 +7758,Rusty sword,1 +7759,Toy soldier,10 +7760,Toy soldier,10 +7761,Toy soldier (wound),10 +7762,Toy soldier (wound),10 +7763,Toy doll,10 +7764,Toy doll,10 +7765,Toy doll (wound),10 +7766,Toy doll (wound),10 +7767,Toy mouse,10 +7768,Toy mouse,10 +7769,Toy mouse (wound),10 +7770,Toy mouse (wound),10 +7771,Clockwork cat,10 +7772,Clockwork cat,10 +7773,Branch,1 +7774,Reward token,1 +7775,Reward token,1 +7776,Reward token,1 +7777,Long vine,1 +7778,Short vine,1 +7779,Fishing tome,5000 +7780,Fishing tome,5000 +7781,Fishing tome,5000 +7782,Agility tome,5000 +7783,Agility tome,5000 +7784,Agility tome,5000 +7785,Thieving tome,5000 +7786,Thieving tome,5000 +7787,Thieving tome,5000 +7788,Slayer tome,5000 +7789,Slayer tome,5000 +7790,Slayer tome,5000 +7791,Mining tome,5000 +7792,Mining tome,5000 +7793,Mining tome,5000 +7794,Firemaking tome,5000 +7795,Firemaking tome,5000 +7796,Firemaking tome,5000 +7797,Woodcutting tome,5000 +7798,Woodcutting tome,5000 +7799,Woodcutting tome,5000 +7800,Snail shell,1 +7801,Snake hide,35 +7802,Snake hide,35 +7803,Yin yang amulet,1 +7804,Picture,625 +7805,Picture,625 +7806,Anger sword,26 +7807,Anger battleaxe,182 +7808,Anger mace,18 +7809,Anger spear,26 +7810,Jug of vinegar,1 +7811,Pot of vinegar,0 +7812,Goblin skull,1 +7813,Bone in vinegar,0 +7814,Goblin skull,1 +7815,Bear ribs,1 +7816,Bone in vinegar,0 +7817,Bear ribs,1 +7818,Ram skull,1 +7819,Bone in vinegar,0 +7820,Ram skull,1 +7821,Unicorn bone,1 +7822,Bone in vinegar,0 +7823,Unicorn bone,1 +7824,Giant rat bone,1 +7825,Bone in vinegar,0 +7826,Giant rat bone,1 +7827,Giant bat wing,1 +7828,Bone in vinegar,0 +7829,Giant bat wing,1 +7830,Wolf bone,1 +7831,Bone in vinegar,0 +7832,Wolf bone,1 +7833,Bat wing,1 +7834,Bone in vinegar,0 +7835,Bat wing,1 +7836,Rat bone,1 +7837,Bone in vinegar,0 +7838,Rat bone,1 +7839,Baby dragon bone,1 +7840,Bone in vinegar,0 +7841,Baby dragon bone,1 +7842,Ogre ribs,1 +7843,Bone in vinegar,0 +7844,Ogre ribs,1 +7845,Jogre bone,1 +7846,Bone in vinegar,0 +7847,Jogre bone,1 +7848,Zogre bone,1 +7849,Bone in vinegar,0 +7850,Zogre bone,1 +7851,Mogre bone,1 +7852,Bone in vinegar,0 +7853,Mogre bone,1 +7854,Monkey paw,1 +7855,Bone in vinegar,0 +7856,Monkey paw,1 +7857,Dagannoth ribs,1 +7858,Bone in vinegar,0 +7859,Dagannoth ribs,1 +7860,Snake spine,1 +7861,Bone in vinegar,0 +7862,Snake spine,1 +7863,Zombie bone,1 +7864,Bone in vinegar,0 +7865,Zombie bone,1 +7866,Werewolf bone,1 +7867,Bone in vinegar,0 +7868,Werewolf bone,1 +7869,Moss giant bone,1 +7870,Bone in vinegar,0 +7871,Moss giant bone,1 +7872,Fire giant bone,1 +7873,Bone in vinegar,0 +7874,Fire giant bone,1 +7875,Ice giant ribs,1 +7876,Bone in vinegar,0 +7877,Ice giant ribs,1 +7878,Terrorbird wing,1 +7879,Bone in vinegar,0 +7880,Terrorbird wing,1 +7881,Ghoul bone,1 +7882,Bone in vinegar,0 +7883,Ghoul bone,1 +7884,Troll bone,1 +7885,Bone in vinegar,0 +7886,Troll bone,1 +7887,Seagull wing,1 +7888,Bone in vinegar,0 +7889,Seagull wing,1 +7890,Undead cow ribs,1 +7891,Bone in vinegar,0 +7892,Undead cow ribs,1 +7893,Experiment bone,1 +7894,Bone in vinegar,0 +7895,Experiment bone,1 +7896,Rabbit bone,1 +7897,Bone in vinegar,0 +7898,Rabbit bone,1 +7899,Basilisk bone,1 +7900,Bone in vinegar,0 +7901,Basilisk bone,1 +7902,Desert lizard bone,1 +7903,Bone in vinegar,0 +7904,Desert lizard bone,1 +7905,Cave goblin skull,1 +7906,Bone in vinegar,0 +7907,Cave goblin skull,1 +7908,Big frog leg,1 +7909,Bone in vinegar,0 +7910,Big frog leg,1 +7911,Vulture wing,1 +7912,Bone in vinegar,0 +7913,Vulture wing,1 +7914,Jackal bone,1 +7915,Bone in vinegar,0 +7916,Jackal bone,1 +7917,Ram skull helm,1 +7918,Bonesack,1 +7919,Bottle of wine,500 +7920,Bottle of wine,500 +7921,Empty wine bottle,1 +7922,Al kharid flyer,1 +7923,null,1 +7924,null,1 +7925,null,1 +7926,null,1 +7927,Easter ring,1 +7928,Easter egg,1 +7929,Easter egg,1 +7930,Easter egg,1 +7931,Easter egg,1 +7932,Easter egg,1 +7933,Easter egg,1 +7934,Field ration,390 +7935,Field ration,390 +7936,Pure essence,4 +7937,Pure essence,4 +7938,Bob,4 +7939,Tortoise shell,1000 +7940,Tortoise shell,1000 +7941,Iron sheet,30 +7942,Fresh monkfish,10 +7943,Fresh monkfish,10 +7944,Raw monkfish,230 +7945,Raw monkfish,230 +7946,Monkfish,230 +7947,Monkfish,230 +7948,Burnt monkfish,3 +7949,Burnt monkfish,3 +7950,Bone seeds,30 +7951,Herman's book,1 +7952,Picture,1 +7953,Picture,1 +7954,Burnt shrimp,1 +7955,Burnt shrimp,1 +7956,Casket,50 +7957,White apron,1 +7958,Mining prop,10 +7959,Heavy box,10 +7960,Empty box,10 +7961,Burnt diary,10 +7962,Burnt diary,10 +7963,Burnt diary,10 +7964,Burnt diary,10 +7965,Burnt diary,10 +7966,Letter,10 +7967,Engine,5 +7968,Scroll,10 +7969,Pulley beam,5 +7970,Long pulley beam,5 +7971,Longer pulley beam,5 +7972,Lift manual,5 +7973,Beam,5 +7974,Servant bell,10 +7975,Crawling hand,1000 +7976,Cockatrice head,2000 +7977,Basilisk head,4000 +7978,Kurask head,6000 +7979,Abyssal head,12000 +7980,Kbd heads,50000 +7981,Kq head,50000 +7982,Crawling hand,1000 +7983,Cockatrice head,2000 +7984,Basilisk head,4000 +7985,Kurask head,6000 +7986,Abyssal head,12000 +7987,Kbd heads,50000 +7988,Kq head,50000 +7989,Big bass,1000 +7990,Big bass,1000 +7991,Big swordfish,2500 +7992,Big swordfish,2500 +7993,Big shark,5000 +7994,Big shark,5000 +7995,Arthur portrait,1000 +7996,Elena portrait,1000 +7997,Keldagrim portrait,1000 +7998,Misc. portrait,1000 +7999,Desert painting,2000 +8000,Isafdar painting,2000 +8001,Karamja painting,2000 +8002,Lumbridge painting,2000 +8003,Morytania painting,2000 +8004,Small map,1000 +8005,Medium map,1000 +8006,Large map,1000 +8007,Varrock teleport,1 +8008,Lumbridge teleport,1 +8009,Falador teleport,1 +8010,Camelot teleport,1 +8011,Ardougne teleport,1 +8012,Watchtower t'port,1 +8013,Teleport to house,1 +8014,Bones to bananas,1 +8015,Bones to peaches,1 +8016,Enchant sapphire,1 +8017,Enchant emerald,1 +8018,Enchant ruby,1 +8019,Enchant diamond,1 +8020,Enchant dragonstn.,1 +8021,Enchant onyx,1 +8022,Telekinetic grab,1 +8023,Boxing ring,1 +8024,Fencing ring,1 +8025,Combat ring,1 +8026,Ranging pedestals,1 +8027,Balance beam,1 +8028,Glove rack,1 +8029,Weapons rack,1 +8030,Extra weapons rack,1 +8031,Wooden bed,1 +8032,Oak bed,1 +8033,Large oak bed,1 +8034,Teak bed,1 +8035,Large teak bed,1 +8036,4-poster,1 +8037,Gilded 4-poster,1 +8038,Shoe box,1 +8039,Oak drawers,1 +8040,Oak wardrobe,1 +8041,Teak drawers,1 +8042,Teak wardrobe,1 +8043,Mahogany 'drobe,1 +8044,Gilded wardrobe,1 +8045,Shaving stand,1 +8046,Oak shaving stand,1 +8047,Oak dresser,1 +8048,Teak dresser,1 +8049,Fancy teak dresser,1 +8050,Mahogany dresser,1 +8051,Gilded dresser,1 +8052,Oak clock,1 +8053,Teak clock,1 +8054,Gilded clock,1 +8055,Saradomin symbol,1 +8056,Zamorak symbol,1 +8057,Guthix symbol,1 +8058,Saradomin icon,1 +8059,Zamorak icon,1 +8060,Guthix icon,1 +8061,Icon of bob,1 +8062,Oak altar,1 +8063,Teak altar,1 +8064,Cloth-cover'd altar,1 +8065,Mahogany altar,1 +8066,Limestone altar,1 +8067,Marble altar,1 +8068,Gilded altar,1 +8069,Wooden torches,1 +8070,Steel torches,1 +8071,Steel candlesticks,1 +8072,Gold candlesticks,1 +8073,Incense burners,1 +8074,Mahogany burners,1 +8075,Marble burners,1 +8076,Shuttered window,1 +8077,Decorative window,1 +8078,Stained glass,1 +8079,Windchimes,1 +8080,Bells,1 +8081,Organ,1 +8082,Small statues,1 +8083,Medium statues,1 +8084,Large statues,1 +8085,Suit of armour,1 +8086,Small portrait,1 +8087,Minor head,1 +8088,Medium head,1 +8089,Major head,1 +8090,Mounted sword,1 +8091,Small landscape,1 +8092,Guild trophy,1 +8093,Large portrait,1 +8094,Large landscape,1 +8095,Rune display case,1 +8096,Low-level plants,1 +8097,Mid-level plants,1 +8098,High-level plants,1 +8099,Rope bell-pull,1 +8100,Bell-pull,1 +8101,Posh bell-pull,1 +8102,Oak decoration,1 +8103,Teak decoration,1 +8104,Gilded decoration,1 +8105,Round shield,1 +8106,Square shield,1 +8107,Kite shield,1 +8108,Wooden bench,1 +8109,Oak bench,1 +8110,Carved oak bench,1 +8111,Teak dining bench,1 +8112,Carved teak bench,1 +8113,Mahogany bench,1 +8114,Gilded bench,1 +8115,Wood dining table,1 +8116,Oak dining table,1 +8117,Carved oak table,1 +8118,Teak table,1 +8119,Carved teak table,1 +8120,Mahogany table,1 +8121,Opulent table,1 +8122,Oak door,1 +8123,Steel-plated door,1 +8124,Marble door,1 +8125,Decorative blood,1 +8126,Decorative pipe,1 +8127,Hanging skeleton,1 +8128,Candles,1 +8129,Torches,1 +8130,Skull torches,1 +8131,Skeleton guard,1 +8132,Guard dog,1 +8133,Hobgoblin guard,1 +8134,Baby red dragon,1 +8135,Huge spider,1 +8136,Troll guard,1 +8137,Hellhound,1 +8138,Demon,1 +8139,Kalphite soldier,1 +8140,Tok-xil,1 +8141,Dagannoth,1 +8142,Steel dragon,1 +8143,Spike trap,1 +8144,Man trap,1 +8145,Tangle vine,1 +8146,Marble trap,1 +8147,Teleport trap,1 +8148,Wooden crate,1 +8149,Oak chest,1 +8150,Teak chest,1 +8151,Mahogany chest,1 +8152,Magic chest,1 +8153,Clay attack stone,1 +8154,Attack stone,1 +8155,Marble att. stone,1 +8156,Magical balance 1,1 +8157,Magical balance 2,1 +8158,Magical balance 3,1 +8159,Jester,1 +8160,Treasure hunt,1 +8161,Hangman game,1 +8162,Hoop and stick,1 +8163,Dartboard,1 +8164,Archery target,1 +8165,Oak prize chest,1 +8166,Teak prize chest,1 +8167,Mahogany chest,1 +8168,Exit portal,1 +8169,Decorative rock,1 +8170,Pond,1 +8171,Imp statue,1 +8172,Dungeon entrance,1 +8173,Tree,1 +8174,Nice tree,1 +8175,Oak tree,1 +8176,Willow tree,1 +8177,Maple tree,1 +8178,Yew tree,1 +8179,Magic tree,1 +8180,Plant,1 +8181,Small fern,1 +8182,Fern,1 +8183,Dock leaf,1 +8184,Thistle,1 +8185,Reeds,1 +8186,Fern,1 +8187,Bush,1 +8188,Tall plant,1 +8189,Short plant,1 +8190,Large-leaf plant,1 +8191,Huge plant,1 +8192,Gazebo,1 +8193,Small fountain,1 +8194,Large fountain,1 +8195,Posh fountain,1 +8196,Boundary stones,1 +8197,Wooden fence,1 +8198,Stone wall,1 +8199,Iron railings,1 +8200,Picket fence,1 +8201,Garden fence,1 +8202,Marble wall,1 +8203,Thorny hedge,1 +8204,Nice hedge,1 +8205,Small box hedge,1 +8206,Topiary hedge,1 +8207,Fancy hedge,1 +8208,Tall fancy hedge,1 +8209,Tall box hedge,1 +8210,Rosemary,1 +8211,Daffodils,1 +8212,Bluebells,1 +8213,Sunflower,1 +8214,Marigolds,1 +8215,Roses,1 +8216,Firepit,1 +8217,Firepit with hook,1 +8218,Firepit with pot,1 +8219,Small oven,1 +8220,Large oven,1 +8221,Steel range,1 +8222,Fancy range,1 +8223,Wooden shelves 1,1 +8224,Wooden shelves 2,1 +8225,Wooden shelves 3,1 +8226,Oak shelves 1,1 +8227,Oak shelves 2,1 +8228,Teak shelves 1,1 +8229,Teak shelves 2,1 +8230,Pump and drain,1 +8231,Pump and tub,1 +8232,Sink,1 +8233,Wooden larder,1 +8234,Oak larder,1 +8235,Teak larder,1 +8236,Cat blanket,1 +8237,Cat basket,1 +8238,Cushioned basket,1 +8239,Beer barrel,1 +8240,Cider barrel,1 +8241,Asgarnian ale,1 +8242,Greenman's ale,1 +8243,Dragon bitter,1 +8244,Chef's delight,1 +8245,Blank poh entity,1 +8246,Wood kitchen table,1 +8247,Oak kitchen table,1 +8248,Teak kitchen table,1 +8249,Oak staircase,1 +8250,Oak staircase,1 +8251,Oak staircase,1 +8252,Teak staircase,1 +8253,Teak staircase,1 +8254,Teak staircase,1 +8255,Marble staircase,1 +8256,Marble staircase,1 +8257,Marble staircase,1 +8258,Spiral staircase,1 +8259,Marble spiral,1 +8260,Crawling hand,1 +8261,Cockatrice head,1 +8262,Basilisk head,1 +8263,Kurask head,1 +8264,Abyssal head,1 +8265,Kbd heads,1 +8266,Kq head,1 +8267,Mounted bass,1 +8268,Mounted swordfish,1 +8269,Mounted shark,1 +8270,Mithril armour,1 +8271,Adamantite armour,1 +8272,Runite armour,1 +8273,Cw armour 1,1 +8274,Cw armour 2,1 +8275,Cw armour 3,1 +8276,Rune case 1,1 +8277,Rune case 2,1 +8278,Rune case 3,1 +8279,Silverlight,1 +8280,Excalibur,1 +8281,Darklight,1 +8282,Anti-dragon shield,1 +8283,Amulet of glory,1 +8284,Cape of legends,1 +8285,King arthur,1 +8286,Elena,1 +8287,Giant dwarf,1 +8288,Miscellanians,1 +8289,Lumbridge,1 +8290,The desert,1 +8291,Morytania,1 +8292,Karamja,1 +8293,Isafdar,1 +8294,Small map,1 +8295,Medium map,1 +8296,Large map,1 +8297,Oak cage,1 +8298,Oak and steel cage,1 +8299,Steel cage,1 +8300,Spiked cage,1 +8301,Bone cage,1 +8302,Spikes,1 +8303,Tentacle pool,1 +8304,Flame pit,1 +8305,Rocnar,1 +8306,Oak ladder,1 +8307,Teak ladder,1 +8308,Mahogany ladder,1 +8309,Crude wooden chair,1 +8310,Wooden chair,1 +8311,Rocking chair,1 +8312,Oak chair,1 +8313,Oak armchair,1 +8314,Teak armchair,1 +8315,Mahogany armchair,1 +8316,Brown rug,1 +8317,Rug,1 +8318,Opulent rug,1 +8319,Wooden bookcase,1 +8320,Oak bookcase,1 +8321,Mahogany b'kcase,1 +8322,Torn curtains,1 +8323,Curtains,1 +8324,Opulent curtains,1 +8325,Clay fireplace,1 +8326,Stone fireplace,1 +8327,Marble fireplace,1 +8328,Teak portal,1 +8329,Mahogany portal,1 +8330,Marble portal,1 +8331,Teleport focus,1 +8332,Greater focus,1 +8333,Scrying pool,1 +8334,Oak lectern,1 +8335,Eagle lectern,1 +8336,Demon lectern,1 +8337,Teak eagle lectern,1 +8338,Teak demon lectern,1 +8339,Mahogany eagle,1 +8340,Mahogany demon,1 +8341,Globe,1 +8342,Ornamental globe,1 +8343,Lunar globe,1 +8344,Celestial globe,1 +8345,Armillary sphere,1 +8346,Small orrery,1 +8347,Large orrery,1 +8348,Wooden telescope,1 +8349,Teak telescope,1 +8350,Mahogany 'scope,1 +8351,Crystal ball,1 +8352,Elemental sphere,1 +8353,Crystal of power,1 +8354,Alchemical chart,1 +8355,Astronomical chart,1 +8356,Infernal chart,1 +8357,Oak throne,1 +8358,Teak throne,1 +8359,Mahogany throne,1 +8360,Gilded throne,1 +8361,Skeleton throne,1 +8362,Crystal throne,1 +8363,Demonic throne,1 +8364,Oak lever,1 +8365,Teak lever,1 +8366,Mahogany lever,1 +8367,Trapdoor,1 +8368,Trapdoor,1 +8369,Trapdoor,1 +8370,Floor decoration,1 +8371,Steel cage,1 +8372,Trapdoor,1 +8373,Lesser magic cage,1 +8374,Greater magic cage,1 +8375,Wooden workbench,1 +8376,Oak workbench,1 +8377,Steel framed bench,1 +8378,Bench with vice,1 +8379,Bench with lathe,1 +8380,Crafting table 1,1 +8381,Crafting table 2,1 +8382,Crafting table 3,1 +8383,Crafting table 4,1 +8384,Tool store 1,1 +8385,Tool store 2,1 +8386,Tool store 3,1 +8387,Tool store 4,1 +8388,Tool store 5,1 +8389,Repair bench,1 +8390,Whetstone,1 +8391,Armour stand,1 +8392,Pluming stand,1 +8393,Shield easel,1 +8394,Banner easel,1 +8395,Parlour,1000 +8396,Kitchen,5000 +8397,Dining room,5000 +8398,Bedroom,10000 +8399,Games room,25000 +8400,Combat room,25000 +8401,Hall,15000 +8402,Hall,15000 +8403,Hall,25000 +8404,Hall,25000 +8405,Chapel,50000 +8406,Workshop,10000 +8407,Study,50000 +8408,Portal chamber,100000 +8409,Throne room,150000 +8410,Oubliette,150000 +8411,Dungeon corridor,7500 +8412,Dungeon cross,7500 +8413,Dungeon stairs,7500 +8414,Treasure room,250000 +8415,Garden,1000 +8416,Formal garden,75000 +8417,Bagged dead tree,1000 +8418,Bagged dead tree,1000 +8419,Bagged nice tree,2000 +8420,Bagged nice tree,2000 +8421,Bagged oak tree,5000 +8422,Bagged oak tree,5000 +8423,Bagged willow tree,10000 +8424,Bagged willow tree,10000 +8425,Bagged maple tree,15000 +8426,Bagged maple tree,15000 +8427,Bagged yew tree,20000 +8428,Bagged yew tree,20000 +8429,Bagged magic tree,50000 +8430,Bagged magic tree,50000 +8431,Bagged plant 1,1000 +8432,Bagged plant 1,1000 +8433,Bagged plant 2,5000 +8434,Bagged plant 2,5000 +8435,Bagged plant 3,10000 +8436,Bagged plant 3,10000 +8437,Thorny hedge,5000 +8438,Thorny hedge,5000 +8439,Nice hedge,10000 +8440,Nice hedge,10000 +8441,Small box hedge,15000 +8442,Small box hedge,15000 +8443,Topiary hedge,20000 +8444,Topiary hedge,20000 +8445,Fancy hedge,25000 +8446,Fancy hedge,25000 +8447,Tall fancy hedge,50000 +8448,Tall fancy hedge,50000 +8449,Tall box hedge,100000 +8450,Tall box hedge,100000 +8451,Bagged rosemary,5000 +8452,Bagged rosemary,5000 +8453,Bagged daffodils,10000 +8454,Bagged daffodils,10000 +8455,Bagged bluebells,15000 +8456,Bagged bluebells,15000 +8457,Bagged sunflower,5000 +8458,Bagged sunflower,5000 +8459,Bagged marigolds,10000 +8460,Bagged marigolds,10000 +8461,Bagged roses,15000 +8462,Bagged roses,15000 +8463,Construction guide,1 +8464,Rune heraldic helm,35200 +8465,null,1 +8466,Rune heraldic helm,35200 +8467,null,1 +8468,Rune heraldic helm,35200 +8469,null,1 +8470,Rune heraldic helm,35200 +8471,null,1 +8472,Rune heraldic helm,35200 +8473,null,1 +8474,Rune heraldic helm,35200 +8475,null,1 +8476,Rune heraldic helm,35200 +8477,null,1 +8478,Rune heraldic helm,35200 +8479,null,1 +8480,Rune heraldic helm,35200 +8481,null,1 +8482,Rune heraldic helm,35200 +8483,null,1 +8484,Rune heraldic helm,35200 +8485,null,1 +8486,Rune heraldic helm,35200 +8487,null,1 +8488,Rune heraldic helm,35200 +8489,null,1 +8490,Rune heraldic helm,35200 +8491,null,1 +8492,Rune heraldic helm,35200 +8493,null,1 +8494,Rune heraldic helm,35200 +8495,null,1 +8496,Crude wooden chair,10 +8497,Crude wooden chair,10 +8498,Wooden chair,10 +8499,Wooden chair,10 +8500,Rocking chair,10 +8501,Rocking chair,10 +8502,Oak chair,10 +8503,Oak chair,10 +8504,Oak armchair,10 +8505,Oak armchair,10 +8506,Teak armchair,10 +8507,Teak armchair,10 +8508,Mahogany armchair,10 +8509,Mahogany armchair,10 +8510,Wooden bookcase,10 +8511,Wooden bookcase,10 +8512,Oak bookcase,10 +8513,Oak bookcase,10 +8514,Mahogany b'kcase,10 +8515,Mahogany b'kcase,10 +8516,Beer barrel,10 +8517,Beer barrel,10 +8518,Cider barrel,10 +8519,Cider barrel,10 +8520,Asgarnian ale,10 +8521,Asgarnian ale,10 +8522,Greenman's ale,10 +8523,Greenman's ale,10 +8524,Dragon bitter,10 +8525,Dragon bitter,10 +8526,Chef's delight,10 +8527,Chef's delight,10 +8528,Wood kitchen table,10 +8529,Wood kitchen table,10 +8530,Oak kitchen table,10 +8531,Oak kitchen table,10 +8532,Teak kitchen table,10 +8533,Teak kitchen table,10 +8534,Oak lectern,10 +8535,Oak lectern,10 +8536,Eagle lectern,10 +8537,Eagle lectern,10 +8538,Demon lectern,10 +8539,Demon lectern,10 +8540,Teak eagle lectern,10 +8541,Teak eagle lectern,10 +8542,Teak demon lectern,10 +8543,Teak demon lectern,10 +8544,Mahogany eagle,10 +8545,Mahogany eagle,10 +8546,Mahogany demon,10 +8547,Mahogany demon,10 +8548,Wood dining table,10 +8549,Wood dining table,10 +8550,Oak dining table,10 +8551,Oak dining table,10 +8552,Carved oak table,10 +8553,Carved oak table,10 +8554,Teak table,10 +8555,Teak table,10 +8556,Carved teak table,10 +8557,Carved teak table,10 +8558,Mahogany table,10 +8559,Mahogany table,10 +8560,Opulent table,10 +8561,Opulent table,10 +8562,Wooden bench,10 +8563,Wooden bench,10 +8564,Oak bench,10 +8565,Oak bench,10 +8566,Carved oak bench,10 +8567,Carved oak bench,10 +8568,Teak dining bench,10 +8569,Teak dining bench,10 +8570,Carved teak bench,10 +8571,Carved teak bench,10 +8572,Mahogany bench,10 +8573,Mahogany bench,10 +8574,Gilded bench,10 +8575,Gilded bench,10 +8576,Wooden bed,10 +8577,Wooden bed,10 +8578,Oak bed,10 +8579,Oak bed,10 +8580,Large oak bed,10 +8581,Large oak bed,10 +8582,Teak bed,10 +8583,Teak bed,10 +8584,Large teak bed,10 +8585,Large teak bed,10 +8586,4-poster,10 +8587,4-poster,10 +8588,Gilded 4-poster,10 +8589,Gilded 4-poster,10 +8590,Oak clock,10 +8591,Oak clock,10 +8592,Teak clock,10 +8593,Teak clock,10 +8594,Gilded clock,10 +8595,Gilded clock,10 +8596,Shaving stand,10 +8597,Shaving stand,10 +8598,Oak shaving stand,10 +8599,Oak shaving stand,10 +8600,Oak dresser,10 +8601,Oak dresser,10 +8602,Teak dresser,10 +8603,Teak dresser,10 +8604,Fancy teak dresser,10 +8605,Fancy teak dresser,10 +8606,Mahogany dresser,10 +8607,Mahogany dresser,10 +8608,Gilded dresser,10 +8609,Gilded dresser,10 +8610,Shoe box,10 +8611,Shoe box,10 +8612,Oak drawers,10 +8613,Oak drawers,10 +8614,Oak wardrobe,10 +8615,Oak wardrobe,10 +8616,Teak drawers,10 +8617,Teak drawers,10 +8618,Teak wardrobe,10 +8619,Teak wardrobe,10 +8620,Mahogany 'drobe,10 +8621,Mahogany 'drobe,10 +8622,Gilded wardrobe,10 +8623,Gilded wardrobe,10 +8624,Crystal ball,10 +8625,Crystal ball,10 +8626,Elemental sphere,10 +8627,Elemental sphere,10 +8628,Crystal of power,10 +8629,Crystal of power,10 +8630,Globe,10 +8631,Globe,10 +8632,Ornamental globe,10 +8633,Ornamental globe,10 +8634,Lunar globe,10 +8635,Lunar globe,10 +8636,Celestial globe,10 +8637,Celestial globe,10 +8638,Armillary sphere,10 +8639,Armillary sphere,10 +8640,Small orrery,10 +8641,Small orrery,10 +8642,Large orrery,10 +8643,Large orrery,10 +8644,Wooden telescope,10 +8645,Wooden telescope,10 +8646,Teak telescope,10 +8647,Teak telescope,10 +8648,Mahogany 'scope,10 +8649,Mahogany 'scope,10 +8650,Banner,1 +8651,null,1 +8652,Banner,1 +8653,null,1 +8654,Banner,1 +8655,null,1 +8656,Banner,1 +8657,null,1 +8658,Banner,1 +8659,null,1 +8660,Banner,1 +8661,null,1 +8662,Banner,1 +8663,null,1 +8664,Banner,1 +8665,null,1 +8666,Banner,1 +8667,null,1 +8668,Banner,1 +8669,null,1 +8670,Banner,1 +8671,null,1 +8672,Banner,1 +8673,null,1 +8674,Banner,1 +8675,null,1 +8676,Banner,1 +8677,null,1 +8678,Banner,1 +8679,null,1 +8680,Banner,1 +8681,null,1 +8682,Steel heraldic helm,600 +8683,null,1 +8684,Steel heraldic helm,600 +8685,null,1 +8686,Steel heraldic helm,600 +8687,null,1 +8688,Steel heraldic helm,600 +8689,null,1 +8690,Steel heraldic helm,600 +8691,null,1 +8692,Steel heraldic helm,600 +8693,null,1 +8694,Steel heraldic helm,600 +8695,null,1 +8696,Steel heraldic helm,600 +8697,null,1 +8698,Steel heraldic helm,600 +8699,null,1 +8700,Steel heraldic helm,600 +8701,null,1 +8702,Steel heraldic helm,600 +8703,null,1 +8704,Steel heraldic helm,600 +8705,null,1 +8706,Steel heraldic helm,600 +8707,null,1 +8708,Steel heraldic helm,600 +8709,null,1 +8710,Steel heraldic helm,600 +8711,null,1 +8712,Steel heraldic helm,600 +8713,null,1 +8714,Rune kiteshield,54400 +8715,null,1 +8716,Rune kiteshield,54400 +8717,null,1 +8718,Rune kiteshield,54400 +8719,null,1 +8720,Rune kiteshield,54400 +8721,null,1 +8722,Rune kiteshield,54400 +8723,null,1 +8724,Rune kiteshield,54400 +8725,null,1 +8726,Rune kiteshield,54400 +8727,null,1 +8728,Rune kiteshield,54400 +8729,null,1 +8730,Rune kiteshield,54400 +8731,null,1 +8732,Rune kiteshield,54400 +8733,null,1 +8734,Rune kiteshield,54400 +8735,null,1 +8736,Rune kiteshield,54400 +8737,null,1 +8738,Rune kiteshield,54400 +8739,null,1 +8740,Rune kiteshield,54400 +8741,null,1 +8742,Rune kiteshield,54400 +8743,null,1 +8744,Rune kiteshield,54400 +8745,null,1 +8746,Steel kiteshield,900 +8747,null,1 +8748,Steel kiteshield,900 +8749,null,1 +8750,Steel kiteshield,900 +8751,null,1 +8752,Steel kiteshield,900 +8753,null,1 +8754,Steel kiteshield,900 +8755,null,1 +8756,Steel kiteshield,900 +8757,null,1 +8758,Steel kiteshield,900 +8759,null,1 +8760,Steel kiteshield,900 +8761,null,1 +8762,Steel kiteshield,900 +8763,null,1 +8764,Steel kiteshield,900 +8765,null,1 +8766,Steel kiteshield,900 +8767,null,1 +8768,Steel kiteshield,900 +8769,null,1 +8770,Steel kiteshield,900 +8771,null,1 +8772,Steel kiteshield,900 +8773,null,1 +8774,Steel kiteshield,900 +8775,null,1 +8776,Steel kiteshield,900 +8777,null,1 +8778,Oak plank,250 +8779,Oak plank,250 +8780,Teak plank,500 +8781,Teak plank,500 +8782,Mahogany plank,1500 +8783,Mahogany plank,1500 +8784,Gold leaf,130000 +8785,Gold leaf,130000 +8786,Marble block,325000 +8787,Marble block,325000 +8788,Magic stone,975000 +8789,Magic stone,975000 +8790,Bolt of cloth,650 +8791,Bolt of cloth,650 +8792,Clockwork,500 +8793,Clockwork,500 +8794,Saw,13 +8795,Saw,13 +8796,Thing,20 +8797,Picture,1 +8798,null,1 +8799,null,1 +8800,null,1 +8801,null,1 +8802,null,1 +8803,null,1 +8804,null,1 +8805,null,1 +8806,null,1 +8807,null,1 +8808,null,1 +8809,null,1 +8810,null,1 +8811,null,1 +8812,null,1 +8813,null,1 +8814,null,1 +8815,null,1 +8816,null,1 +8817,null,1 +8818,null,1 +8819,null,1 +8820,null,1 +8821,null,1 +8822,null,1 +8823,null,1 +8824,null,1 +8825,null,1 +8826,null,1 +8827,null,1 +8828,null,1 +8829,null,1 +8830,null,1 +8831,null,1 +8832,null,1 +8833,null,1 +8834,null,1 +8835,null,1 +8836,Mahogany logs,50 +8837,Timber beam,1 +8838,Timber beam,1 +8839,Void knight top,48000 +8840,Void knight robe,45000 +8841,Void knight mace,12000 +8842,Void knight gloves,6052 +8843,null,1 +8844,Bronze defender,1 +8845,Iron defender,1 +8846,Steel defender,1 +8847,Black defender,1 +8848,Mithril defender,1 +8849,Adamant defender,1 +8850,Rune defender,1 +8851,Warrior guild token,1 +8852,null,1 +8853,null,1 +8854,null,1 +8855,null,1 +8856,Defensive shield,1 +8857,Shot,1 +8858,18lb shot,1 +8859,22lb shot,1 +8860,One barrel,1 +8861,Two barrels,1 +8862,Three barrels,1 +8863,Four barrels,1 +8864,Five barrels,1 +8865,Ground ashes,2 +8866,Steel key,1 +8867,Bronze key,1 +8868,Silver key,1 +8869,Iron key,1 +8870,Zanik,1 +8871,Crate with zanik,1 +8872,Bone dagger,2000 +8873,Bone dagger,2000 +8874,Bone dagger (p),2000 +8875,Bone dagger (p),2000 +8876,Bone dagger (p+),2000 +8877,Bone dagger (p+),2000 +8878,Bone dagger (p++),2000 +8879,Bone dagger (p++),2000 +8880,Dorgeshuun c'bow,2000 +8881,Dorgeshuun c'bow,2000 +8882,Bone bolts,4 +8883,null,1 +8884,null,1 +8885,null,1 +8886,null,1 +8887,Zanik,1 +8888,Zanik (ham),1 +8889,Zanik (showdown),1 +8890,Coins,1 +8891,null,1 +8892,null,1 +8893,null,1 +8894,null,1 +8895,null,1 +8896,null,1 +8897,null,1 +8898,null,1 +8899,null,1 +8900,Cave horror,1 +8901,Black mask (10),3000 +8902,Black mask (10),3000 +8903,Black mask (9),3000 +8904,Black mask (9),3000 +8905,Black mask (8),3000 +8906,Black mask (8),3000 +8907,Black mask (7),3000 +8908,Black mask (7),3000 +8909,Black mask (6),3000 +8910,Black mask (6),3000 +8911,Black mask (5),3000 +8912,Black mask (5),3000 +8913,Black mask (4),3000 +8914,Black mask (4),3000 +8915,Black mask (3),3000 +8916,Black mask (3),3000 +8917,Black mask (2),3000 +8918,Black mask (2),3000 +8919,Black mask (1),3000 +8920,Black mask (1),3000 +8921,Black mask,3000 +8922,Black mask,3000 +8923,Witchwood icon,900 +8924,Bandana and eyepatch,100 +8925,Bandana and eyepatch,100 +8926,Bandana and eyepatch,100 +8927,Bandana and eyepatch,100 +8928,Hat and eyepatch,100 +8929,Crabclaw and hook,100 +8930,Pipe section,1 +8931,Pipe section,1 +8932,Lumber patch,1 +8933,Lumber patch,1 +8934,Scrapey tree logs,1 +8935,Scrapey tree logs,1 +8936,Blue flowers,1 +8937,Blue flowers,1 +8938,Red flowers,1 +8939,Red flowers,1 +8940,Rum,2 +8941,Rum,2 +8942,Monkey,1 +8943,Blue monkey,1 +8944,Blue monkey,1 +8945,Blue monkey,1 +8946,Red monkey,1 +8947,Red monkey,1 +8948,Red monkey,1 +8949,Pirate bandana,1 +8950,Pirate hat,1 +8951,Pieces of eight,1 +8952,Blue naval shirt,100 +8953,Green naval shirt,100 +8954,Red naval shirt,100 +8955,Brown naval shirt,100 +8956,Black naval shirt,100 +8957,Purple naval shirt,100 +8958,Grey naval shirt,100 +8959,Blue tricorn hat,50 +8960,Green tricorn hat,50 +8961,Red tricorn hat,50 +8962,Brown tricorn hat,50 +8963,Black tricorn hat,50 +8964,Purple tricorn hat,50 +8965,Grey tricorn hat,50 +8966,Cutthroat flag,200 +8967,Guilded smile flag,200 +8968,Bronze fist flag,300 +8969,Lucky shot flag,400 +8970,Treasure flag,500 +8971,Phasmatys flag,600 +8972,Bowl of red water,1 +8973,Bowl of red water,1 +8974,Bowl of blue water,1 +8975,Bowl of blue water,1 +8976,Bitternut,1 +8977,Scrapey bark,1 +8978,Scrapey bark,1 +8979,Bridge section,1 +8980,Bridge section,1 +8981,Sweetgrubs,1 +8982,Sweetgrubs,1 +8983,null,1 +8984,null,1 +8985,null,1 +8986,Bucket,1 +8987,Torch,1 +8988,The stuff,5 +8989,Brewin' guide,1 +8990,Brewin' guide,1 +8991,Blue navy slacks,100 +8992,Green navy slacks,100 +8993,Red navy slacks,100 +8994,Brown navy slacks,100 +8995,Black navy slacks,100 +8996,Purple navy slacks,100 +8997,Grey navy slacks,100 +8998,Bandana and eyepatch,100 +8999,Bandana and eyepatch,100 +9000,Bandana and eyepatch,100 +9001,Bandana and eyepatch,100 +9002,Hat and eyepatch,100 +9003,Security book,2 +9004,Stronghold notes,2 +9005,Fancy boots,1 +9006,Fighting boots,1 +9007,Right skull half,1 +9008,Left skull half,1 +9009,Strange skull,1 +9010,Top of sceptre,1 +9011,Bottom of sceptre,1 +9012,Runed sceptre,1 +9013,Skull sceptre,1 +9014,Security book,2 +9015,Stronghold notes,2 +9016,Gorak claws,150 +9017,Star flower,75 +9018,Gorak claw powder,150 +9019,Magic essence(unf),75 +9020,Queen's secateurs,1 +9021,Magic essence(4),280 +9022,Magic essence(3),230 +9023,Magic essence(2),180 +9024,Magic essence(1),130 +9025,Nuff's certificate,1 +9026,Ivory comb,10 +9027,Ivory comb,10 +9028,Golden scarab,30 +9029,Golden scarab,30 +9030,Stone scarab,20 +9031,Stone scarab,20 +9032,Pottery scarab,10 +9033,Pottery scarab,10 +9034,Golden statuette,30 +9035,Golden statuette,30 +9036,Pottery statuette,10 +9037,Pottery statuette,10 +9038,Stone statuette,20 +9039,Stone statuette,20 +9040,Gold seal,30 +9041,Gold seal,30 +9042,Stone seal,20 +9043,Stone seal,20 +9044,Pharaoh's sceptre,100 +9045,Pharaoh's sceptre,100 +9046,Pharaoh's sceptre,100 +9047,Pharaoh's sceptre,100 +9048,Pharaoh's sceptre,100 +9049,Pharaoh's sceptre,100 +9050,Pharaoh's sceptre,100 +9051,Pharaoh's sceptre,100 +9052,Locust meat,1 +9053,Locust meat,1 +9054,Red goblin mail,40 +9055,Black goblin mail,40 +9056,Yellow goblin mail,40 +9057,Green goblin mail,40 +9058,Purple goblin mail,40 +9059,Pink goblin mail,40 +9060,null,1 +9061,null,1 +9062,null,1 +9063,null,1 +9064,Emerald lantern,20 +9065,Emerald lantern,20 +9066,Emerald lens,10 +9067,Dream log,4 +9068,Moonclan helm,1000 +9069,Moonclan hat,1000 +9070,Moonclan armour,1000 +9071,Moonclan skirt,1000 +9072,Moonclan gloves,900 +9073,Moonclan boots,900 +9074,Moonclan cape,200 +9075,Astral rune,220 +9076,Lunar ore,3 +9077,Lunar bar,8 +9078,Moonclan manual,1 +9079,Suqah tooth,20 +9080,Suqah hide,60 +9081,Suqah leather,60 +9082,Ground tooth,20 +9083,Seal of passage,1 +9084,Lunar staff,1 +9085,Empty vial,1 +9086,Vial of water,1 +9087,Waking sleep vial,1 +9088,Guam vial,1 +9089,Marr vial,1 +9090,Guam-marr vial,1 +9091,Lunar staff - pt1,15 +9092,Lunar staff - pt2,15 +9093,Lunar staff - pt3,15 +9094,Kindling,1 +9095,Soaked kindling,1 +9096,Lunar helm,100 +9097,Lunar torso,100 +9098,Lunar legs,100 +9099,Lunar gloves,100 +9100,Lunar boots,100 +9101,Lunar cape,2 +9102,Lunar amulet,30 +9103,A special tiara,30 +9104,Lunar ring,100 +9105,Suqah monster,1 +9106,Astral tiara,100 +9107,null,1 +9108,null,1 +9109,null,1 +9110,null,1 +9111,null,1 +9112,null,1 +9113,null,1 +9114,null,1 +9115,null,1 +9116,null,1 +9117,null,1 +9118,null,1 +9119,null,1 +9120,null,1 +9121,null,1 +9122,null,1 +9123,null,1 +9124,null,1 +9125,null,1 +9126,null,1 +9127,null,1 +9128,null,1 +9129,null,1 +9130,null,1 +9131,null,1 +9132,null,1 +9133,null,1 +9134,null,1 +9135,null,1 +9136,null,1 +9137,null,1 +9138,null,1 +9139,Blurite bolts,2 +9140,Iron bolts,2 +9141,Steel bolts,8 +9142,Mithril bolts,20 +9143,Adamant bolts,58 +9144,Rune bolts,300 +9145,Silver bolts,5 +9146,null,1 +9147,null,1 +9148,null,1 +9149,null,1 +9150,null,1 +9151,null,1 +9152,null,1 +9153,null,1 +9154,null,1 +9155,null,1 +9156,null,1 +9157,null,1 +9158,null,1 +9159,null,1 +9160,null,1 +9161,null,1 +9162,null,1 +9163,null,1 +9164,null,1 +9165,null,1 +9166,null,1 +9167,null,1 +9168,null,1 +9169,null,1 +9170,null,1 +9171,null,1 +9172,null,1 +9173,null,1 +9174,Bronze crossbow,73 +9175,Bronze crossbow,73 +9176,Blurite crossbow,93 +9177,Iron crossbow,157 +9178,Iron crossbow,157 +9179,Steel crossbow,360 +9180,Steel crossbow,360 +9181,Mith crossbow,783 +9182,Mith crossbow,783 +9183,Adamant crossbow,1767 +9184,Adamant crossbow,1767 +9185,Rune crossbow,16200 +9186,Rune crossbow,16200 +9187,Jade bolt tips,10 +9188,Topaz bolt tips,13 +9189,Sapphire bolt tips,17 +9190,Emerald bolt tips,33 +9191,Ruby bolt tips,67 +9192,Diamond bolt tips,133 +9193,Dragon bolt tips,667 +9194,Onyx bolt tips,1000 +9195,null,1 +9196,null,1 +9197,null,1 +9198,null,1 +9199,null,1 +9200,null,1 +9201,null,1 +9202,null,1 +9203,null,1 +9204,null,1 +9205,null,1 +9206,null,1 +9207,null,1 +9208,null,1 +9209,null,1 +9210,null,1 +9211,null,1 +9212,null,1 +9213,null,1 +9214,null,1 +9215,null,1 +9216,null,1 +9217,null,1 +9218,null,1 +9219,null,1 +9220,null,1 +9221,null,1 +9222,null,1 +9223,null,1 +9224,null,1 +9225,null,1 +9226,null,1 +9227,null,1 +9228,null,1 +9229,null,1 +9230,null,1 +9231,null,1 +9232,null,1 +9233,null,1 +9234,null,1 +9235,null,1 +9236,Opal bolts (e),7 +9237,Jade bolts (e),13 +9238,Pearl bolts (e),15 +9239,Topaz bolts (e),24 +9240,Sapphire bolts (e),40 +9241,Emerald bolts (e),59 +9242,Ruby bolts (e),138 +9243,Diamond bolts (e),211 +9244,Dragon bolts (e),1063 +9245,Onyx bolts (e),15000 +9246,null,1 +9247,null,1 +9248,null,1 +9249,null,1 +9250,null,1 +9251,null,1 +9252,null,1 +9253,null,1 +9254,null,1 +9255,null,1 +9256,null,1 +9257,null,1 +9258,null,1 +9259,null,1 +9260,null,1 +9261,null,1 +9262,null,1 +9263,null,1 +9264,null,1 +9265,null,1 +9266,null,1 +9267,null,1 +9268,null,1 +9269,null,1 +9270,null,1 +9271,null,1 +9272,null,1 +9273,null,1 +9274,null,1 +9275,null,1 +9276,null,1 +9277,null,1 +9278,null,1 +9279,null,1 +9280,null,1 +9281,null,1 +9282,null,1 +9283,null,1 +9284,null,1 +9285,null,1 +9286,Blurite bolts(p),2 +9287,Iron bolts (p),2 +9288,Steel bolts (p),8 +9289,Mithril bolts (p),20 +9290,Adamant bolts (p),58 +9291,Runite bolts (p),300 +9292,Silver bolts (p),5 +9293,Blurite bolts(p+),2 +9294,Iron bolts(p+),2 +9295,Steel bolts(p+),8 +9296,Mithril bolts(p+),20 +9297,Adamant bolts(p+),58 +9298,Runite bolts(p+),300 +9299,Silver bolts(p+),5 +9300,Blurite bolts(p++),2 +9301,Iron bolts(p++),2 +9302,Steel bolts(p++),8 +9303,Mithril bolts(p++),20 +9304,Adamant bolts(p++),58 +9305,Runite bolts(p++),300 +9306,Silver bolts(p++),5 +9307,null,1 +9308,null,1 +9309,null,1 +9310,null,1 +9311,null,1 +9312,null,1 +9313,null,1 +9314,null,1 +9315,null,1 +9316,null,1 +9317,null,1 +9318,null,1 +9319,null,1 +9320,null,1 +9321,null,1 +9322,null,1 +9323,null,1 +9324,null,1 +9325,null,1 +9326,null,1 +9327,null,1 +9328,null,1 +9329,null,1 +9330,null,1 +9331,null,1 +9332,null,1 +9333,null,1 +9334,null,1 +9335,Jade bolts,12 +9336,Topaz bolts,22 +9337,Sapphire bolts,37 +9338,Emerald bolts,53 +9339,Ruby bolts,125 +9340,Diamond bolts,192 +9341,Dragon bolts,967 +9342,Onyx bolts,13633 +9343,null,1 +9344,null,1 +9345,null,1 +9346,null,1 +9347,null,1 +9348,null,1 +9349,null,1 +9350,null,1 +9351,null,1 +9352,null,1 +9353,null,1 +9354,null,1 +9355,null,1 +9356,null,1 +9357,null,1 +9358,null,1 +9359,null,1 +9360,null,1 +9361,null,1 +9362,null,1 +9363,null,1 +9364,null,1 +9365,null,1 +9366,null,1 +9367,null,1 +9368,null,1 +9369,null,1 +9370,null,1 +9371,null,1 +9372,null,1 +9373,null,1 +9374,null,1 +9375,Bronze bolts (unf),1 +9376,Blurite bolts (unf),1 +9377,Iron bolts (unf),1 +9378,Steel bolts (unf),1 +9379,Mithril bolts (unf),1 +9380,Adamant bolts(unf),1 +9381,Runite bolts (unf),1 +9382,Silver bolts (unf),1 +9383,null,1 +9384,null,1 +9385,null,1 +9386,null,1 +9387,null,1 +9388,null,1 +9389,null,1 +9390,null,1 +9391,null,1 +9392,null,1 +9393,null,1 +9394,null,1 +9395,null,1 +9396,null,1 +9397,null,1 +9398,null,1 +9399,null,1 +9400,null,1 +9401,null,1 +9402,null,1 +9403,null,1 +9404,null,1 +9405,null,1 +9406,null,1 +9407,null,1 +9408,null,1 +9409,null,1 +9410,null,1 +9411,null,1 +9412,null,1 +9413,null,1 +9414,null,1 +9415,Grapple,1 +9416,Mith grapple tip,30 +9417,Mith grapple tip,30 +9418,Mith grapple,48 +9419,Mith grapple,1 +9420,Bronze limbs,20 +9421,Bronze limbs,20 +9422,Blurite limbs,33 +9423,Iron limbs,70 +9424,Iron limbs,70 +9425,Steel limbs,250 +9426,Steel limbs,250 +9427,Mithril limbs,650 +9428,Mithril limbs,650 +9429,Adamantite limbs,1600 +9430,Adamantite limbs,1600 +9431,Runite limbs,16000 +9432,Runite limbs,16000 +9433,Bolt pouch,5000 +9434,Bolt mould,25 +9435,Bolt mould,25 +9436,Sinew,2 +9437,Sinew,2 +9438,Crossbow string,1 +9439,Crossbow string,1 +9440,Wooden stock,20 +9441,Wooden stock,20 +9442,Oak stock,27 +9443,Oak stock,27 +9444,Willow stock,53 +9445,Willow stock,53 +9446,Teak stock,77 +9447,Teak stock,77 +9448,Maple stock,100 +9449,Maple stock,100 +9450,Mahogany stock,133 +9451,Mahogany stock,133 +9452,Yew stock,167 +9453,Yew stock,167 +9454,Bronze c'bow (u),40 +9455,Bronze c'bow (u),40 +9456,Blurite c'bow (u),60 +9457,Iron c'bow (u),123 +9458,Iron c'bow (u),123 +9459,Steel c'bow (u),327 +9460,Steel c'bow (u),327 +9461,Mithril c'bow (u),750 +9462,Mithril c'bow (u),750 +9463,Adamant c'bow (u),1733 +9464,Adamant c'bow (u),1733 +9465,Runite c'bow (u),16167 +9466,Runite c'bow (u),16167 +9467,Blurite bar,18 +9468,Sawdust,1 +9469,Grand seed pod,250 +9470,Gnome scarf,1000 +9471,Gnome scarf,1000 +9472,Gnome goggles,1000 +9473,Gnome goggles,1000 +9474,Reward token,250 +9475,Mint cake,250 +9476,Mint cake,250 +9477,Aluft aloft box,1 +9478,Half made batta,2 +9479,Unfinished batta,2 +9480,Half made batta,2 +9481,Unfinished batta,2 +9482,Half made batta,2 +9483,Half made batta,2 +9484,Unfinished batta,2 +9485,Half made batta,2 +9486,Unfinished batta,2 +9487,Wizard blizzard,1 +9488,Wizard blizzard,1 +9489,Wizard blizzard,1 +9490,Picture,1 +9491,null,1 +9492,null,1 +9493,null,1 +9494,null,1 +9495,null,1 +9496,null,1 +9497,null,1 +9498,null,1 +9499,null,1 +9500,null,1 +9501,null,1 +9502,null,1 +9503,null,1 +9504,null,1 +9505,null,1 +9506,null,1 +9507,null,1 +9508,Wizard blizzard,1 +9509,Wizard blizzard,1 +9510,Short green guy,1 +9511,Short green guy,1 +9512,Pineapple punch,1 +9513,Pineapple punch,1 +9514,Fruit blast,1 +9515,Fruit blast,1 +9516,Drunk dragon,1 +9517,Drunk dragon,1 +9518,Choc saturday,1 +9519,Choc saturday,1 +9520,Blurberry special,1 +9521,Blurberry special,1 +9522,Batta tin,1 +9523,Batta tin,1 +9524,Batta tin,1 +9525,Batta tin,1 +9526,null,1 +9527,Fruit batta,1 +9528,Fruit batta,1 +9529,Toad batta,1 +9530,Toad batta,1 +9531,Worm batta,1 +9532,Worm batta,1 +9533,Vegetable batta,1 +9534,Vegetable batta,1 +9535,Cheese+tom batta,1 +9536,Cheese+tom batta,1 +9537,null,1 +9538,Toad crunchies,1 +9539,Toad crunchies,1 +9540,Spicy crunchies,1 +9541,Spicy crunchies,1 +9542,Worm crunchies,1 +9543,Worm crunchies,1 +9544,Chocchip crunchies,1 +9545,Chocchip crunchies,1 +9546,null,1 +9547,Worm hole,1 +9548,Worm hole,1 +9549,Veg ball,1 +9550,Veg ball,1 +9551,Tangled toads' legs,1 +9552,Tangled toads' legs,1 +9553,Chocolate bomb,1 +9554,Chocolate bomb,1 +9555,null,1 +9556,null,1 +9557,null,1 +9558,Half made bowl,2 +9559,Half made bowl,2 +9560,Unfinished bowl,2 +9561,Half made bowl,2 +9562,Unfinished bowl,2 +9563,Half made bowl,2 +9564,Unfinished bowl,2 +9565,Cocktail shaker,2 +9566,Mixed blizzard,2 +9567,Mixed sgg,2 +9568,Mixed blast,2 +9569,Mixed punch,2 +9570,Mixed blurberry special,2 +9571,Mixed saturday,2 +9572,Mixed saturday,2 +9573,Mixed saturday,2 +9574,Mixed dragon,2 +9575,Mixed dragon,2 +9576,Mixed dragon,2 +9577,Half made crunchy,2 +9578,Unfinished crunchy,2 +9579,Half made crunchy,2 +9580,Unfinished crunchy,2 +9581,Half made crunchy,2 +9582,Unfinished crunchy,2 +9583,Half made crunchy,2 +9584,Unfinished crunchy,2 +9585,Batta tin,10 +9586,Crunchy tray,10 +9587,Gnomebowl mould,10 +9588,Raw crunchies,2 +9589,Dossier,1 +9590,Dossier,1 +9591,Broken cauldron,1 +9592,Magic glue,1 +9593,Weird gloop,1 +9594,Ground mud runes,1 +9595,Hazelmere's book,1 +9596,Picture,10 +9597,Red circle,1 +9598,Red triangle,1 +9599,Red square,1 +9600,Red pentagon,1 +9601,Orange circle,1 +9602,Orange triangle,1 +9603,Orange square,1 +9604,Orange pentagon,1 +9605,Yellow circle,1 +9606,Yellow triangle,1 +9607,Yellow square,1 +9608,Yellow pentagon,1 +9609,Green circle,1 +9610,Green triangle,1 +9611,Green square,1 +9612,Green pentagon,1 +9613,Blue circle,1 +9614,Blue triangle,1 +9615,Blue square,1 +9616,Blue pentagon,1 +9617,Indigo circle,1 +9618,Indigo triangle,1 +9619,Indigo square,1 +9620,Indigo pentagon,1 +9621,Violet circle,1 +9622,Violet triangle,1 +9623,Violet square,1 +9624,Violet pentagon,1 +9625,Crystal saw,10 +9626,Small crystal seed,10000 +9627,A handwritten book,2 +9628,null,1 +9629,Tyras helm,1265 +9630,Tyras helm,1265 +9631,null,2 +9632,Daeyalt ore,100 +9633,Message,1 +9634,Vyrewatch top,650 +9635,Vyrewatch top,650 +9636,Vyrewatch legs,650 +9637,Vyrewatch legs,650 +9638,Vyrewatch shoes,650 +9639,Vyrewatch shoes,650 +9640,Citizen top,6 +9641,Citizen top,6 +9642,Citizen trousers,6 +9643,Citizen trousers,6 +9644,Citizen shoes,6 +9645,Citizen shoes,6 +9646,Castle sketch 1,1 +9647,Castle sketch 2,1 +9648,Castle sketch 3,1 +9649,Message,1 +9650,Blood tithe pouch,1 +9651,Large ornate key,1 +9652,Haemalchemy,1 +9653,Sealed message,1 +9654,Door key,1 +9655,Ladder top,1 +9656,Tome of xp (3),1 +9657,Tome of xp (2),1 +9658,Tome of xp (1),1 +9659,Bucket of water,1 +9660,Bucket,1 +9661,null,1 +9662,Shortcut key,1 +9663,null,1 +9664,null,1 +9665,Torch,1 +9666,Pros'yte harness m,25000 +9667,Pros'yte harness m,25000 +9668,Initiate harness m,20000 +9669,Initiate harness m,20000 +9670,Pros'yte harness f,25000 +9671,Pros'yte harness f,25000 +9672,Proselyte sallet,8000 +9673,Proselyte sallet,8000 +9674,Proselyte hauberk,12000 +9675,Proselyte hauberk,12000 +9676,Proselyte cuisse,10000 +9677,Proselyte cuisse,10000 +9678,Proselyte tasset,10000 +9679,Proselyte tasset,10000 +9680,Sea slug glue,1 +9681,Commorb v2,1 +9682,Door transcription,1 +9683,Dead sea slug,0 +9684,Page 1,1 +9685,Page 2,1 +9686,Page 3,1 +9687,Fragment 1,1 +9688,Fragment 2,1 +9689,Fragment 3,1 +9690,Blank water rune,1 +9691,Water rune,1 +9692,Blank air rune,1 +9693,Air rune,1 +9694,Blank earth rune,1 +9695,Earth rune,1 +9696,Blank mind rune,1 +9697,Mind rune,1 +9698,Blank fire rune,1 +9699,Fire rune,1 +9700,Picture,1 +9701,Torch,1 +9702,Stick,1 +9703,Training sword,1 +9704,Training shield,1 +9705,Training bow,1 +9706,Training arrows,1 +9707,null,1 +9708,null,1 +9709,null,1 +9710,null,1 +9711,null,1 +9712,null,1 +9713,null,1 +9714,null,1 +9715,Slashed book,10 +9716,Rock,1 +9717,Beaten book,1 +9718,Crane schematic,1 +9719,Lever schematic,1 +9720,Crane claw,1 +9721,Scroll,1 +9722,Key,1 +9723,Pipe,1 +9724,Large cog,1 +9725,Medium cog,1 +9726,Small cog,1 +9727,Primed bar,1 +9728,Elemental mind bar,1 +9729,Elemental helmet,20 +9730,Elemental helmet,20 +9731,Mind shield,3000 +9732,Mind shield,3000 +9733,Mind helmet,3000 +9734,Mind helmet,3000 +9735,Desert goat horn,12 +9736,Goat horn dust,12 +9737,Goat horn dust,12 +9738,Desert goat horn,12 +9739,Combat potion(4),215 +9740,Combat potion(4),215 +9741,Combat potion(3),160 +9742,Combat potion(3),160 +9743,Combat potion(2),105 +9744,Combat potion(2),105 +9745,Combat potion(1),52 +9746,Combat potion(1),52 +9747,Attack cape,99000 +9748,Attack cape(t),99000 +9749,Attack hood,1 +9750,Strength cape,99000 +9751,Strength cape(t),99000 +9752,Strength hood,1 +9753,Defence cape,99000 +9754,Defence cape(t),99000 +9755,Defence hood,1 +9756,Ranging cape,99000 +9757,Ranging cape(t),99000 +9758,Ranging hood,1 +9759,Prayer cape,99000 +9760,Prayer cape(t),99000 +9761,Prayer hood,1 +9762,Magic cape,99000 +9763,Magic cape(t),99000 +9764,Magic hood,1 +9765,Runecraft cape,99000 +9766,Runecraft cape(t),99000 +9767,Runecrafting hood,1 +9768,Hitpoints cape,99000 +9769,Hitpoints cape(t),99000 +9770,Hitpoints hood,1 +9771,Agility cape,99000 +9772,Agility cape(t),99000 +9773,Agility hood,1 +9774,Herblore cape,99000 +9775,Herblore cape(t),99000 +9776,Herblore hood,1 +9777,Thieving cape,99000 +9778,Thieving cape(t),99000 +9779,Thieving hood,1 +9780,Crafting cape,99000 +9781,Crafting cape(t),99000 +9782,Crafting hood,1 +9783,Fletching cape,99000 +9784,Fletching cape(t),99000 +9785,Fletching hood,1 +9786,Slayer cape,99000 +9787,Slayer cape(t),99000 +9788,Slayer hood,1 +9789,Construct. cape,99000 +9790,Construct. cape(t),99000 +9791,Construct. hood,1 +9792,Mining cape,99000 +9793,Mining cape(t),99000 +9794,Mining hood,1 +9795,Smithing cape,99000 +9796,Smithing cape(t),99000 +9797,Smithing hood,1 +9798,Fishing cape,99000 +9799,Fishing cape(t),99000 +9800,Fishing hood,1 +9801,Cooking cape,99000 +9802,Cooking cape(t),99000 +9803,Cooking hood,1 +9804,Firemaking cape,99000 +9805,Firemaking cape(t),99000 +9806,Firemaking hood,1 +9807,Woodcutting cape,99000 +9808,Woodcut. cape(t),99000 +9809,Woodcutting hood,1 +9810,Farming cape,99000 +9811,Farming cape(t),99000 +9812,Farming hood,1 +9813,Quest point cape,99000 +9814,Quest point hood,1 +9815,Bobble hat,1 +9816,Bobble scarf,1 +9817,Oak cape rack,1 +9818,Teak cape rack,1 +9819,M'gany cape rack,1 +9820,Gilded cape rack,1 +9821,Marble cape rack,1 +9822,Magical cape rack,1 +9823,Oak costume box,1 +9824,Teak costume box,1 +9825,Mahogany cos box,1 +9826,Oak armour case,1 +9827,Teak armour case,1 +9828,M'gany arm'r case,1 +9829,Oak magic wardrobe,1 +9830,Carved oak magic wardrobe,1 +9831,Teak magic wardrobe,1 +9832,Carved teak magic wardrobe,1 +9833,Mahogany magic wardrobe,1 +9834,Gilded magic wardrobe,1 +9835,Marble magic wardrobe,1 +9836,Oak toy box,1 +9837,Teak toy box,1 +9838,Mahogany toy box,1 +9839,Oak treasure chest,1 +9840,Teak treas' chest,1 +9841,M'gany treas' chest,1 +9842,Costume room,50000 +9843,Oak cape rack,10 +9844,Teak cape rack,10 +9845,M'gany cape rack,10 +9846,Gilded cape rack,10 +9847,Marble cape rack,10 +9848,Magical cape rack,10 +9849,Oak toy box,10 +9850,Teak toy box,10 +9851,Mahogany toy box,10 +9852,Oak magic wardrobe,10 +9853,Carved oak magic wardrobe,10 +9854,Teak magic wardrobe,10 +9855,Carved teak magic wardrobe,10 +9856,Mahogany magic wardrobe,10 +9857,Gilded magic wardrobe,10 +9858,Marble magic wardrobe,10 +9859,Oak armour case,10 +9860,Teak armour case,10 +9861,M'gany arm'r case,10 +9862,Oak treasure chest,10 +9863,Teak treas' chest,10 +9864,M'gany treas' chest,10 +9865,Oak costume box,10 +9866,Teak costume box,10 +9867,Mahogany cos box,10 +9868,Oak cape rack,10 +9869,Teak cape rack,10 +9870,M'gany cape rack,10 +9871,Gilded cape rack,10 +9872,Marble cape rack,10 +9873,Magical cape rack,10 +9874,Oak toy box,10 +9875,Teak toy box,10 +9876,Mahogany toy box,10 +9877,Oak magic wardrobe,10 +9878,Carved oak magic wardrobe,10 +9879,Teak magic wardrobe,10 +9880,Carved teak magic wardrobe,10 +9881,Mahogany magic wardrobe,10 +9882,Gilded magic wardrobe,10 +9883,Marble magic wardrobe,10 +9884,Oak armour case,10 +9885,Teak armour case,10 +9886,M'gany arm'r case,10 +9887,Oak treasure chest,10 +9888,Teak treas' chest,10 +9889,M'gany treas' chest,10 +9890,Oak costume box,10 +9891,Teak costume box,10 +9892,Mahogany cos box,10 +9893,null,1 +9894,null,1 +9895,null,1 +9896,null,1 +9897,null,1 +9898,null,1 +9899,null,1 +9900,null,1 +9901,Goutweedy lump,5 +9902,Hardy gout tubers,5 +9903,Farming manual,20 +9904,Sailing book,1 +9905,null,1 +9906,Ghost buster 500,1 +9907,Ghost buster 500,1 +9908,Ghost buster 500,1 +9909,Ghost buster 500,1 +9910,Ghost buster 500,1 +9911,Ghost buster 500,1 +9912,Ghost buster 500,1 +9913,White destabiliser,1 +9914,Red destabiliser,1 +9915,Blue destabiliser,1 +9916,Green destabiliser,1 +9917,Yellow destabiliser,1 +9918,Black destabiliser,1 +9919,Evil root,1 +9920,Jack lantern mask,1 +9921,Skeleton boots,1 +9922,Skeleton gloves,1 +9923,Skeleton leggings,1 +9924,Skeleton shirt,1 +9925,Skeleton mask,1 +9926,null,3 +9927,null,3 +9928,null,3 +9929,null,3 +9930,null,3 +9931,null,3 +9932,Auguste's sapling,1 +9933,Balloon structure,1 +9934,Origami balloon,1 +9935,Yellow balloon,1 +9936,Blue balloon,1 +9937,Red balloon,1 +9938,Orange balloon,1 +9939,Green balloon,1 +9940,Purple balloon,1 +9941,Pink balloon,1 +9942,Black balloon,1 +9943,Sandbag,1 +9944,Bomber jacket,1 +9945,Bomber cap,1 +9946,Cap and goggles,1 +9947,Old red disk,1 +9948,Hunter cape,99000 +9949,Hunter cape(t),99000 +9950,Hunter hood,1 +9951,Footprint,1 +9952,Imp,1 +9953,Kebbit,1 +9954,Kebbit,1 +9955,Kebbit,1 +9956,Kebbit,1 +9957,Kebbit,1 +9958,Kebbit,1 +9959,Kebbit,1 +9960,Kebbit,1 +9961,Kebbit,1 +9962,Kebbit,1 +9963,Kebbit,1 +9964,Kebbit,1 +9965,Crimson swift,1 +9966,Copper longtail,1 +9967,Cerulean twitch,1 +9968,Golden warbler,1 +9969,Tropical wagtail,1 +9970,Butterfly,1 +9971,Butterfly,1 +9972,Butterfly,1 +9973,Butterfly,1 +9974,Giant eagle,1 +9975,Rabbit,1 +9976,Chinchompa,1 +9977,Red chinchompa,1 +9978,Raw bird meat,17 +9979,Raw bird meat,17 +9980,Roast bird meat,17 +9981,Roast bird meat,17 +9982,Burnt bird meat,1 +9983,Burnt bird meat,1 +9984,Skewered bird meat,81 +9985,Skewered bird meat,81 +9986,Raw beast meat,25 +9987,Raw beast meat,25 +9988,Roast beast meat,25 +9989,Roast beast meat,25 +9990,Burnt beast meat,1 +9991,Burnt beast meat,1 +9992,Skewered beast,89 +9993,Skewered beast,89 +9994,Spicy tomato,3 +9995,Spicy tomato,3 +9996,Spicy minced meat,8 +9997,Spicy minced meat,8 +9998,Hunter potion(4),15 +9999,Hunter potion(4),15 +10000,Hunter potion(3),12 +10001,Hunter potion(3),12 +10002,Hunter potion(2),9 +10003,Hunter potion(2),9 +10004,Hunter potion(1),6 +10005,Hunter potion(1),6 +10006,Bird snare,6 +10007,Bird snare,6 +10008,Box trap,38 +10009,Box trap,38 +10010,Butterfly net,24 +10011,Butterfly net,24 +10012,Butterfly jar,1 +10013,Butterfly jar,1 +10014,Black warlock,70 +10015,Black warlock,70 +10016,Snowy knight,50 +10017,Snowy knight,50 +10018,Sapphire glacialis,30 +10019,Sapphire glacialis,30 +10020,Ruby harvest,18 +10021,Ruby harvest,18 +10022,null,1 +10023,Falconer's glove,1 +10024,Falconer's glove,1 +10025,Magic box,720 +10026,Magic box,720 +10027,Imp-in-a-box(2),600 +10028,Imp-in-a-box(1),600 +10029,Teasing stick,1 +10030,Teasing stick,1 +10031,Rabbit snare,18 +10032,Rabbit snare,18 +10033,Chinchompa,140 +10034,Red chinchompa,160 +10035,Kyatt legs,200 +10036,Kyatt legs,200 +10037,Kyatt top,200 +10038,Kyatt top,200 +10039,Kyatt hat,1000 +10040,Kyatt hat,1000 +10041,Larupia legs,100 +10042,Larupia legs,100 +10043,Larupia top,100 +10044,Larupia top,100 +10045,Larupia hat,500 +10046,Larupia hat,500 +10047,Graahk legs,150 +10048,Graahk legs,150 +10049,Graahk top,150 +10050,Graahk top,150 +10051,Graahk headdress,750 +10052,Graahk headdress,750 +10053,Wood camo top,20 +10054,Wood camo top,20 +10055,Wood camo legs,20 +10056,Wood camo legs,20 +10057,Jungle camo top,20 +10058,Jungle camo top,20 +10059,Jungle camo legs,20 +10060,Jungle camo legs,20 +10061,Desert camo top,20 +10062,Desert camo top,20 +10063,Desert camo legs,20 +10064,Desert camo legs,20 +10065,Polar camo top,20 +10066,Polar camo top,20 +10067,Polar camo legs,20 +10068,Polar camo legs,20 +10069,Spotted cape,400 +10070,Spotted cape,400 +10071,Spottier cape,800 +10072,Spottier cape,800 +10073,Spotted cape,400 +10074,Spottier cape,800 +10075,Gloves of silence,600 +10076,Gloves of silence,600 +10077,Spiky vambraces,160 +10078,Spiky vambraces,160 +10079,Green spiky vambs,2500 +10080,Green spiky vambs,2500 +10081,Blue spiky vambs,3000 +10082,Blue spiky vambs,3000 +10083,Red spiky vambs,3600 +10084,Red spiky vambs,3600 +10085,Black spiky vambs,4320 +10086,Black spiky vambs,4320 +10087,Stripy feather,20 +10088,Red feather,10 +10089,Blue feather,16 +10090,Yellow feather,13 +10091,Orange feather,15 +10092,Ferret,40 +10093,Tatty larupia fur,72 +10094,Tatty larupia fur,72 +10095,Larupia fur,96 +10096,Larupia fur,96 +10097,Tatty graahk fur,108 +10098,Tatty graahk fur,108 +10099,Graahk fur,144 +10100,Graahk fur,144 +10101,Tatty kyatt fur,144 +10102,Tatty kyatt fur,144 +10103,Kyatt fur,192 +10104,Kyatt fur,192 +10105,Kebbit spike,90 +10106,Kebbit spike,90 +10107,Long kebbit spike,150 +10108,Long kebbit spike,150 +10109,Kebbit teeth,160 +10110,Kebbit teeth,160 +10111,Kebbit teeth dust,160 +10112,Kebbit teeth dust,160 +10113,Kebbit claws,43 +10114,Kebbit claws,43 +10115,Dark kebbit fur,210 +10116,Dark kebbit fur,210 +10117,Polar kebbit fur,12 +10118,Polar kebbit fur,12 +10119,Feldip weasel fur,16 +10120,Feldip weasel fur,16 +10121,Common kebbit fur,14 +10122,Common kebbit fur,14 +10123,Desert devil fur,20 +10124,Desert devil fur,20 +10125,Spotted kebbit fur,150 +10126,Spotted kebbit fur,150 +10127,Dashing kebbit fur,270 +10128,Dashing kebbit fur,270 +10129,Barb-tail harpoon,36 +10130,Barb-tail harpoon,36 +10131,null,1 +10132,Strung rabbit foot,30 +10133,Strung rabbit foot,30 +10134,Rabbit foot,30 +10135,Rabbit foot,30 +10136,Rainbow fish,120 +10137,Rainbow fish,120 +10138,Raw rainbow fish,120 +10139,Raw rainbow fish,120 +10140,Burnt rainbow fish,1 +10141,Burnt rainbow fish,1 +10142,Guam tar,3 +10143,Marrentill tar,1 +10144,Tarromin tar,1 +10145,Harralander tar,1 +10146,Orange salamander,100 +10147,Red salamander,150 +10148,Black salamander,200 +10149,Swamp lizard,50 +10150,Noose wand,4 +10151,Noose wand,4 +10152,null,1 +10153,null,1 +10154,null,1 +10155,null,1 +10156,Hunters' crossbow,1300 +10157,Hunters' crossbow,1300 +10158,Kebbit bolts,18 +10159,Long kebbit bolts,31 +10160,Orange salamander,100 +10161,Red salamander,150 +10162,Black salamander,200 +10163,Swamp lizard,50 +10164,null,1 +10165,More...,1 +10166,Back...,1 +10167,Eagle feather,1 +10168,null,1 +10169,null,1 +10170,null,1 +10171,Eagle cape,1 +10172,Fake beak,1 +10173,Bird book,1 +10174,Metal feather,1 +10175,Golden feather,1 +10176,Silver feather,1 +10177,Bronze feather,1 +10178,Odd bird seed,1 +10179,Feathered journal,1 +10180,Clue scroll,1 +10181,Casket,50 +10182,Clue scroll,1 +10183,Casket,50 +10184,Clue scroll,1 +10185,Casket,50 +10186,Clue scroll,1 +10187,Casket,50 +10188,Clue scroll,1 +10189,Casket,50 +10190,Clue scroll,1 +10191,Casket,50 +10192,Clue scroll,1 +10193,Casket,50 +10194,Clue scroll,1 +10195,Casket,50 +10196,Clue scroll,1 +10197,Casket,50 +10198,Clue scroll,1 +10199,Casket,50 +10200,Clue scroll,1 +10201,Casket,50 +10202,Clue scroll,1 +10203,Casket,50 +10204,Clue scroll,1 +10205,Casket,50 +10206,Clue scroll,1 +10207,Casket,50 +10208,Clue scroll,1 +10209,Casket,50 +10210,Clue scroll,1 +10211,Casket,50 +10212,Clue scroll,1 +10213,Casket,50 +10214,Clue scroll,1 +10215,Casket,50 +10216,Clue scroll,1 +10217,Casket,50 +10218,Clue scroll,1 +10219,Casket,50 +10220,Clue scroll,1 +10221,Casket,50 +10222,Clue scroll,1 +10223,Casket,50 +10224,Clue scroll,1 +10225,Casket,50 +10226,Clue scroll,1 +10227,Casket,50 +10228,Clue scroll,1 +10229,Casket,50 +10230,Clue scroll,1 +10231,Casket,50 +10232,Clue scroll,1 +10233,Casket,50 +10234,Clue scroll,1 +10235,Casket,50 +10236,Clue scroll,1 +10237,Casket,50 +10238,Clue scroll,1 +10239,Casket,50 +10240,Clue scroll,1 +10241,Casket,50 +10242,Clue scroll,1 +10243,Casket,50 +10244,Clue scroll,1 +10245,Casket,50 +10246,Clue scroll,1 +10247,Casket,50 +10248,Clue scroll,1 +10249,Casket,50 +10250,Clue scroll,1 +10251,Casket,50 +10252,Clue scroll,1 +10253,Casket,50 +10254,Clue scroll,1 +10255,Casket,50 +10256,Clue scroll,1 +10257,Casket,50 +10258,Clue scroll,1 +10259,Casket,50 +10260,Clue scroll,1 +10261,Casket,50 +10262,Clue scroll,1 +10263,Casket,50 +10264,Clue scroll,1 +10265,Casket,50 +10266,Clue scroll,1 +10267,Casket,50 +10268,Clue scroll,1 +10269,Casket,50 +10270,Clue scroll,1 +10271,Casket,50 +10272,Clue scroll,1 +10273,Casket,50 +10274,Clue scroll,1 +10275,Casket,50 +10276,Clue scroll,1 +10277,Casket,50 +10278,Clue scroll,1 +10279,Casket,50 +10280,Willow comp bow,300 +10281,Willow comp bow,300 +10282,Yew comp bow,1800 +10283,Yew comp bow,1800 +10284,Magic comp bow,2500 +10285,Magic comp bow,2500 +10286,Rune helm (h1),35200 +10287,Rune helm (h1),35200 +10288,Rune helm (h2),35200 +10289,Rune helm (h2),35200 +10290,Rune helm (h3),35200 +10291,Rune helm (h3),35200 +10292,Rune helm (h4),35200 +10293,Rune helm (h4),35200 +10294,Rune helm (h5),35200 +10295,Rune helm (h5),35200 +10296,Adamant helm (h1),3520 +10297,Adamant helm (h1),3520 +10298,Adamant helm (h2),3520 +10299,Adamant helm (h2),3520 +10300,Adamant helm (h3),3520 +10301,Adamant helm (h3),3520 +10302,Adamant helm (h4),3520 +10303,Adamant helm (h4),3520 +10304,Adamant helm (h5),3520 +10305,Adamant helm (h5),3520 +10306,Black helm (h1),1056 +10307,Black helm (h1),1056 +10308,Black helm (h2),1056 +10309,Black helm (h2),1056 +10310,Black helm (h3),1056 +10311,Black helm (h3),1056 +10312,Black helm (h4),1056 +10313,Black helm (h4),1056 +10314,Black helm (h5),1056 +10315,Black helm (h5),1056 +10316,Bob shirt,3 +10317,Bob shirt,3 +10318,Bob shirt,3 +10319,Bob shirt,3 +10320,Bob shirt,3 +10321,Bob shirt,3 +10322,Bob shirt,3 +10323,Bob shirt,3 +10324,Bob shirt,3 +10325,Bob shirt,3 +10326,Purple firelighter,15 +10327,White firelighter,15 +10328,White logs,1 +10329,Purple logs,1 +10330,3rd age range top,45000 +10331,3rd age range top,45000 +10332,3rd age range legs,40000 +10333,3rd age range legs,40000 +10334,3rd age range coif,10000 +10335,3rd age range coif,10000 +10336,3rd age vambraces,15000 +10337,3rd age vambraces,15000 +10338,3rd age robe top,45000 +10339,3rd age robe top,45000 +10340,3rd age robe,40000 +10341,3rd age robe,40000 +10342,3rd age mage hat,10000 +10343,3rd age mage hat,10000 +10344,3rd age amulet,20000 +10345,3rd age amulet,20000 +10346,3rd age platelegs,200000 +10347,3rd age platelegs,200000 +10348,3rd age platebody,200000 +10349,3rd age platebody,200000 +10350,3rd age full helmet,90000 +10351,3rd age full helmet,90000 +10352,3rd age kiteshield,180000 +10353,3rd age kiteshield,180000 +10354,Amulet of glory(t4),17625 +10355,Amulet of glory(t4),17625 +10356,Amulet of glory(t3),17625 +10357,Amulet of glory(t3),17625 +10358,Amulet of glory(t2),17625 +10359,Amulet of glory(t2),17625 +10360,Amulet of glory(t1),17625 +10361,Amulet of glory(t1),17625 +10362,Amulet of glory(t),17625 +10363,Amulet of glory(t),17625 +10364,Strength amulet(t),2025 +10365,Strength amulet(t),2025 +10366,Amulet of magic(t),900 +10367,Amulet of magic(t),900 +10368,Zamorak bracers,4000 +10369,Zamorak bracers,4000 +10370,Zamorak d'hide,13000 +10371,Zamorak d'hide,13000 +10372,Zamorak chaps,6000 +10373,Zamorak chaps,6000 +10374,Zamorak coif,2000 +10375,Zamorak coif,2000 +10376,Guthix bracers,6000 +10377,Guthix bracers,6000 +10378,Guthix dragonhide,13000 +10379,Guthix dragonhide,13000 +10380,Guthix chaps,6000 +10381,Guthix chaps,6000 +10382,Guthix coif,2000 +10383,Guthix coif,2000 +10384,Saradomin bracers,4000 +10385,Saradomin bracers,4000 +10386,Saradomin d'hide,13000 +10387,Saradomin d'hide,13000 +10388,Saradomin chaps,6000 +10389,Saradomin chaps,6000 +10390,Saradomin coif,2000 +10391,Saradomin coif,2000 +10392,A powdered wig,2000 +10393,A powdered wig,2000 +10394,Flared trousers,2000 +10395,Flared trousers,2000 +10396,Pantaloons,2000 +10397,Pantaloons,2000 +10398,Sleeping cap,2000 +10399,Sleeping cap,2000 +10400,Black ele' shirt,2000 +10401,Black ele' shirt,2000 +10402,Black ele' legs,2000 +10403,Black ele' legs,2000 +10404,Red ele' shirt,2000 +10405,Red ele' shirt,2000 +10406,Red ele' legs,2000 +10407,Red ele' legs,2000 +10408,Blue ele' shirt,2000 +10409,Blue ele' shirt,2000 +10410,Blue ele' legs,2000 +10411,Blue ele' legs,2000 +10412,Green ele' shirt,2000 +10413,Green ele' shirt,2000 +10414,Green ele' legs,2000 +10415,Green ele' legs,2000 +10416,Purple ele' shirt,2000 +10417,Purple ele' shirt,2000 +10418,Purple ele' legs,2000 +10419,Purple ele' legs,2000 +10420,White ele' blouse,2000 +10421,White ele' blouse,2000 +10422,White ele' skirt,2000 +10423,White ele' skirt,2000 +10424,Red ele' blouse,2000 +10425,Red ele' blouse,2000 +10426,Red ele' skirt,2000 +10427,Red ele' skirt,2000 +10428,Blue ele' blouse,2000 +10429,Blue ele' blouse,2000 +10430,Blue ele' skirt,2000 +10431,Blue ele' skirt,2000 +10432,Green ele' blouse,2000 +10433,Green ele' blouse,2000 +10434,Green ele' skirt,2000 +10435,Green ele' skirt,2000 +10436,Purple ele' blouse,2000 +10437,Purple ele' blouse,2000 +10438,Purple ele' skirt,2000 +10439,Purple ele' skirt,2000 +10440,Saradomin crozier,5000 +10441,Saradomin crozier,5000 +10442,Guthix crozier,5000 +10443,Guthix crozier,5000 +10444,Zamorak crozier,5000 +10445,Zamorak crozier,5000 +10446,Saradomin cloak,2000 +10447,Saradomin cloak,2000 +10448,Guthix cloak,2000 +10449,Guthix cloak,2000 +10450,Zamorak cloak,2000 +10451,Zamorak cloak,2000 +10452,Saradomin mitre,5000 +10453,Saradomin mitre,5000 +10454,Guthix mitre,5000 +10455,Guthix mitre,5000 +10456,Zamorak mitre,5000 +10457,Zamorak mitre,5000 +10458,Saradomin robe top,7000 +10459,Saradomin robe top,7000 +10460,Zamorak robe top,7000 +10461,Zamorak robe top,7000 +10462,Guthix robe top,7000 +10463,Guthix robe top,7000 +10464,Saradomin robe legs,7000 +10465,Saradomin robe legs,7000 +10466,Guthix robe legs,7000 +10467,Guthix robe legs,7000 +10468,Zamorak robe legs,7000 +10469,Zamorak robe legs,7000 +10470,Saradomin stole,2500 +10471,Saradomin stole,2500 +10472,Guthix stole,2500 +10473,Guthix stole,2500 +10474,Zamorak stole,2500 +10475,Zamorak stole,2500 +10476,Purple sweets,15 +10477,null,1 +10478,null,1 +10479,null,1 +10480,null,1 +10481,null,1 +10482,null,1 +10483,null,1 +10484,null,1 +10485,Scroll,1 +10486,Empty sack,1 +10487,Undead chicken,100 +10488,Selected iron,10 +10489,Bar magnet,15 +10490,Undead twigs,1 +10491,Blessed axe,20 +10492,Research notes,1 +10493,Translated notes,2 +10494,A pattern,10 +10495,A container,10 +10496,Polished buttons,1 +10497,Tinderbox,0 +10498,Ava's attractor,769 +10499,Ava's accumulator,1000 +10500,Crone-made amulet,20 +10501,Snowball,0 +10502,null,1 +10503,null,1 +10504,null,1 +10505,null,1 +10506,Gublinch shards,1 +10507,Reindeer hat,1 +10508,Wintumber tree,1 +10509,Snowball,1 +10510,Snowball,1 +10511,Zanaris choir,1 +10512,Scroll,1 +10513,Crackers,1 +10514,Tofu,1 +10515,Worms,1 +10516,Attacker horn,1 +10517,Attacker horn,1 +10518,Attacker horn,1 +10519,Attacker horn,1 +10520,Attacker horn,1 +10521,Collection bag,1 +10522,Collection bag,1 +10523,Collection bag,1 +10524,Collection bag,1 +10525,Collection bag,1 +10526,Healer horn,1 +10527,Healer horn,1 +10528,Healer horn,1 +10529,Healer horn,1 +10530,Healer horn,1 +10531,Green egg,1 +10532,Red egg,1 +10533,Blue egg,1 +10534,Yellow egg,1 +10535,Poisoned egg,1 +10536,Spiked/pois. egg,1 +10537,Omega egg,1 +10538,Defender horn,1 +10539,Poisoned tofu,1 +10540,Poisoned worms,1 +10541,Poisoned meat,1 +10542,Healing vial(4),1 +10543,Healing vial(3),1 +10544,Healing vial(2),1 +10545,Healing vial(1),1 +10546,Healing vial,1 +10547,Healer hat,5000 +10548,Fighter hat,5000 +10549,Runner hat,5000 +10550,Ranger hat,5000 +10551,Fighter torso,20000 +10552,Runner boots,5000 +10553,Penance gloves,200 +10554,Penance gloves,200 +10555,Penance skirt,8000 +10556,Picture,1 +10557,Picture,1 +10558,Picture,1 +10559,Picture,1 +10560,Collector horn,1 +10561,Spikes,1 +10562,Queen help book,1 +10563,No eggs,1 +10564,Granite body,80000 +10565,Granite body,80000 +10566,Fire cape,60000 +10567,Picture,1 +10568,Picture,1 +10569,Picture,1 +10570,Picture,1 +10571,Picture,1 +10572,Picture,1 +10573,Picture,1 +10574,Picture,1 +10575,Picture,1 +10576,Picture,1 +10577,Picture,1 +10578,Picture,1 +10579,Picture,1 +10580,Picture,1 +10581,Keris,10000 +10582,Keris(p),10000 +10583,Keris(p+),10000 +10584,Keris(p++),10000 +10585,Parchment,1 +10586,Combat lamp,1 +10587,Tarn's diary,50 +10588,Salve amulet(e),300 +10589,Granite helm,46000 +10590,Granite helm,46000 +10591,Terror dog,1 +10592,Penguin bongos,1 +10593,Cowbells,1 +10594,Clockwork book,1 +10595,Clockwork suit,1 +10596,Clockwork suit,1 +10597,Mission report,1 +10598,Mission report,1 +10599,Mission report,1 +10600,Kgp id card,1 +10601,Mystic hat,15000 +10602,Mystic hat,15000 +10603,Mystic hat,15000 +10604,Skeletal helm,10000 +10605,Infinity top,140000 +10606,Splitbark helm,10000 +10607,Ghostly boots,300 +10608,Moonclan hat,1000 +10609,Lunar helm,100 +10610,Decorative armour,2000 +10611,Void knight top,48000 +10612,Rogue mask,375 +10613,Rock-shell helm,35200 +10614,Spined helm,60000 +10615,Tribal mask,500 +10616,Tribal mask,500 +10617,Tribal mask,500 +10618,White platebody,3840 +10619,Initiate hauberk,10000 +10620,Proselyte hauberk,12000 +10621,Mourner top,150 +10622,Kyatt top,200 +10623,Larupia top,100 +10624,Graahk top,150 +10625,Wood camo top,20 +10626,Jungle camo top,20 +10627,Desert camo top,20 +10628,Polar camo top,20 +10629,Mime mask,1 +10630,Princess blouse,0 +10631,Zombie shirt,1 +10632,Camo top,1 +10633,Lederhosen top,1 +10634,Shade robe,40 +10635,Cape of legends,450 +10636,Obsidian cape,60000 +10637,Fire cape,60000 +10638,Team-1 cape,50 +10639,Attack cape,99000 +10640,Strength cape,99000 +10641,Defence cape,99000 +10642,Ranging cape,99000 +10643,Prayer cape,99000 +10644,Magic cape,99000 +10645,Runecraft cape,99000 +10646,Hunter cape,99000 +10647,Hitpoints cape,99000 +10648,Agility cape,99000 +10649,Herblore cape,99000 +10650,Thieving cape,99000 +10651,Crafting cape,99000 +10652,Fletching cape,99000 +10653,Slayer cape,99000 +10654,Construct. cape,99000 +10655,Mining cape,99000 +10656,Smithing cape,99000 +10657,Fishing cape,99000 +10658,Cooking cape,99000 +10659,Firemaking cape,99000 +10660,Woodcutting cape,99000 +10661,Farming cape,99000 +10662,Quest point cape,99000 +10663,Spotted cape,400 +10664,Spottier cape,800 +10665,Black shield(h1),1632 +10666,Adamant shield(h1),5440 +10667,Rune shield(h1),54400 +10668,Black shield(h2),16325 +10669,Adamant shield(h2),5440 +10670,Rune shield(h2),54400 +10671,Black shield(h3),1632 +10672,Adamant shield(h3),5440 +10673,Rune shield(h3),54400 +10674,Black shield(h4),1632 +10675,Adamant shield(h4),5440 +10676,Rune shield(h4),54400 +10677,Black shield(h5),1632 +10678,Adamant shield(h5),5440 +10679,Rune shield(h5),54400 +10680,Studded body (g),850 +10681,Studded body (t),850 +10682,D'hide body(g),7800 +10683,D'hide body (t),7800 +10684,D'hide body (g),9360 +10685,D'hide body (t),9360 +10686,Wizard robe (g),15 +10687,Wizard robe (t),15 +10688,Enchanted top,120000 +10689,Wizard boots,200 +10690,Black platebody (t),3840 +10691,Black platebody (g),3840 +10692,Highwayman mask,40 +10693,Blue beret,80 +10694,Black beret,80 +10695,White beret,80 +10696,Ranger boots,200 +10697,Adam platebody (t),12800 +10698,Adam platebody (g),12800 +10699,Black helm (h1),1056 +10700,Black helm (h2),1056 +10701,Black helm (h3),1056 +10702,Black helm (h4),1056 +10703,Black helm (h5),1056 +10704,Rune helm (h1),35200 +10705,Rune helm (h2),35200 +10706,Rune helm (h3),35200 +10707,Rune helm (h4),35200 +10708,Rune helm (h5),35200 +10709,Adamant helm (h1),3520 +10710,Adamant helm (h2),3520 +10711,Adamant helm (h3),3520 +10712,Adamant helm (h4),3520 +10713,Adamant helm (h5),3520 +10714,Bob shirt,3 +10715,Bob shirt,3 +10716,Bob shirt,3 +10717,Bob shirt,3 +10718,Bob shirt,3 +10719,Amulet of glory(t),17625 +10720,Guthix cape,100 +10721,Frog mask,0 +10722,Reindeer hat,1 +10723,Jack lantern mask,1 +10724,Skeleton boots,1 +10725,Skeleton gloves,1 +10726,Skeleton leggings,1 +10727,Skeleton shirt,1 +10728,Skeleton mask,1 +10729,Easter ring,1 +10730,Blue marionette,1 +10731,Zombie head,0 +10732,Rubber chicken,0 +10733,Yo-yo,10 +10734,Bunny ears,1 +10735,Scythe,1 +10736,Strength amulet(t),2025 +10737,Picture,1 +10738,Amulet of magic(t),900 +10739,Picture,1 +10740,A powdered wig,2000 +10741,Picture,1 +10742,Flared trousers,2000 +10743,Picture,1 +10744,Pantaloons,2000 +10745,Picture,1 +10746,Sleeping cap,2000 +10747,Picture,1 +10748,Elegant shirt,2000 +10749,Picture,1 +10750,Elegant shirt,2000 +10751,Picture,1 +10752,Elegant shirt,2000 +10753,Picture,1 +10754,Elegant shirt,2000 +10755,Picture,1 +10756,Elegant shirt,2000 +10757,Picture,1 +10758,Red boater,225 +10759,Picture,1 +10760,Orange boater,225 +10761,Picture,1 +10762,Green boater,225 +10763,Picture,1 +10764,Blue boater,225 +10765,Picture,1 +10766,Black boater,225 +10767,Picture,1 +10768,Red headband,40 +10769,Picture,1 +10770,Black headband,40 +10771,Picture,1 +10772,Brown headband,40 +10773,Picture,1 +10774,Pirate's hat,180 +10775,Picture,1 +10776,Zamorak platebody,65000 +10777,Picture,1 +10778,Saradomin plate,65000 +10779,Picture,1 +10780,Guthix platebody,65000 +10781,Picture,1 +10782,Gilded platebody,65000 +10783,Picture,1 +10784,Saradomin robe top,7000 +10785,Picture,1 +10786,Zamorak robe top,7000 +10787,Picture,1 +10788,Guthix robe top,7000 +10789,Picture,1 +10790,Zamorak d'hide,13000 +10791,Picture,1 +10792,Saradomin d'hide,13000 +10793,Picture,1 +10794,Guthix dragonhide,13000 +10795,Picture,1 +10796,Robin hood hat,450 +10797,Picture,1 +10798,Rune platebody (g),65000 +10799,Picture,1 +10800,Rune platebody (t),65000 +10801,Picture,1 +10802,Tan cavalier,200 +10803,Picture,1 +10804,Dark cavalier,200 +10805,Picture,1 +10806,Black cavalier,200 +10807,Picture,1 +10808,Arctic pyre logs,200 +10809,Arctic pyre logs,200 +10810,Arctic pine logs,35 +10811,Arctic pine logs,35 +10812,Split log,90 +10813,Split log,90 +10814,Hair,120 +10815,Hair,120 +10816,Raw yak meat,2 +10817,Raw yak meat,2 +10818,Yak-hide,50 +10819,Yak-hide,50 +10820,Cured yak-hide,100 +10821,Cured yak-hide,100 +10822,Yak-hide armour,500 +10823,Yak-hide armour,500 +10824,Yak-hide armour,500 +10825,Yak-hide armour,500 +10826,Fremennik round shield,500 +10827,Fremennik round shield,500 +10828,Helm of neitiznot,50000 +10829,Documents,1 +10830,Royal decree,1 +10831,Empty tax bag,1 +10832,Light tax bag,1 +10833,Normal tax bag,1 +10834,Hefty tax bag,1 +10835,Bulging taxbag,1 +10836,Silly jester hat,100 +10837,Silly jester top,100 +10838,Silly jester tights,100 +10839,Silly jester boots,100 +10840,A jester stick,1000 +10841,Apricot cream pie,1 +10842,Decapitated head,1 +10843,Helm of neitiznot,50000 +10844,Spring sq'irk,1 +10845,Summer sq'irk,1 +10846,Autumn sq'irk,1 +10847,Winter sq'irk,1 +10848,Spring sq'irkjuice,1 +10849,Summer sq'irkjuice,1 +10850,Autumn sq'irkjuice,1 +10851,Winter sq'irkjuice,1 +10852,Summer garden,1 +10853,Spring garden,1 +10854,Autumn garden,1 +10855,Winter garden,1 +10856,Sin seer's note,5 +10857,Severed leg,0 +10858,Shadow sword,0 +10859,Tea flask,1 +10860,Tea flask,1 +10861,Tea flask,1 +10862,Hard hat,100 +10863,Builder's shirt,100 +10864,Builder's trousers,100 +10865,Builder's boots,100 +10866,Rivets,0 +10867,null,1 +10868,null,1 +10869,null,1 +10870,Binding fluid,1 +10871,Pipe,1 +10872,Pipe ring,1 +10873,Metal sheet,1 +10874,Coloured ball,1 +10875,Valve wheel,1 +10876,Metal bar,1 +10877,Plain satchel,100 +10878,Green satchel,100 +10879,Red satchel,100 +10880,Black satchel,100 +10881,Gold satchel,100 +10882,Rune satchel,100 +10883,Hard hat,100 +10884,Fuse,52 +10885,Keg,1 +10886,Prayer book,1 +10887,Barrelchest anchor,230000 +10888,Barrelchest anchor,1 +10889,Blessed lamp,1 +10890,Prayer book,1 +10891,Wooden cat,1 +10892,Wooden cat,1 +10893,Cranial clamp,1 +10894,Brain tongs,1 +10895,Bell jar,1 +10896,Wolf whistle,1 +10897,Shipping order,1 +10898,Keg,1 +10899,Crate part,0 +10900,null,1 +10901,null,1 +10902,null,1 +10903,null,1 +10904,Skull staple,0 +10905,null,1 +10906,null,1 +10907,null,1 +10908,null,1 +10909,Mixture - step 1(4),300 +10910,Mixture - step 1(4),300 +10911,Mixture - step 1(3),240 +10912,Mixture - step 1(3),240 +10913,Mixture - step 1(2),180 +10914,Mixture - step 1(2),180 +10915,Mixture - step 1(1),120 +10916,Mixture - step 1(1),120 +10917,Mixture - step 2(4),300 +10918,Mixture - step 2(4),300 +10919,Mixture - step 2(3),240 +10920,Mixture - step 2(3),240 +10921,Mixture - step 2(2),180 +10922,Mixture - step 2(2),180 +10923,Mixture - step 2(1),120 +10924,Mixture - step 2(1),120 +10925,Sanfew serum(4),300 +10926,Sanfew serum(4),300 +10927,Sanfew serum(3),240 +10928,Sanfew serum(3),240 +10929,Sanfew serum(2),180 +10930,Sanfew serum(2),180 +10931,Sanfew serum(1),120 +10932,Sanfew serum(1),120 +10933,Lumberjack boots,50 +10934,Reward token,1 +10935,Reward token,1 +10936,Reward token,1 +10937,Nail beast nails,300 +10938,Nail beast nails,300 +10939,Lumberjack top,50 +10940,Lumberjack legs,50 +10941,Lumberjack hat,50 +10942,Reward token,1 +10943,Reward token,1 +10944,Reward token,1 +10945,Lumberjack top,50 +10946,Pushup,1 +10947,Run,1 +10948,Situp,1 +10949,Starjump,1 +10950,Skull staples,0 +10951,Skull staples,0 +10952,Slayer bell,150 +10953,Slayer bell,150 +10954,Frog-leather body,1000 +10955,Frog-leather body,1000 +10956,Frog-leather chaps,900 +10957,Frog-leather chaps,900 +10958,Frog-leather boots,200 +10959,Frog-leather boots,200 +10960,Green gloop soup,10 +10961,Frogspawn gumbo,10 +10962,Frogburger,10 +10963,Coated frogs' legs,10 +10964,Bat shish,10 +10965,Fingers,10 +10966,Grubs à la mode,50 +10967,Roast frog,50 +10968,Mushrooms,50 +10969,Fillets,50 +10970,Loach,50 +10971,Eel sushi,50 +10972,Dorgesh-kaan sphere,20 +10973,Light orb,350 +10974,Light orb,350 +10975,Spanner,0 +10976,Long bone,5 +10977,Curved bone,7 +10978,Swamp weed,2 +10979,Swamp weed,2 +10980,Empty light orb,250 +10981,Cave goblin wire,20 +10982,Cave goblin wire,20 +10983,Cog,0 +10984,Cog,0 +10985,Fuse,0 +10986,Fuse,0 +10987,Meter,0 +10988,Meter,0 +10989,Capacitor,0 +10990,Capacitor,0 +10991,Lever,0 +10992,Lever,0 +10993,Powerbox,0 +10994,Powerbox,0 +10995,Perfect shell,1000 +10996,Perfect snail shell,1000 +10997,Molanisk,1 +10998,Cave goblin,1 +10999,Goblin book,1 +11000,Picture,0 +11001,Dagon'hai history,10 +11002,Sin'keth's diary,10 +11003,An empty folder,5 +11004,null,1 +11005,null,1 +11006,Used folder,10 +11007,Full folder,10 +11008,Rat's paper,1 +11009,Rat's letter,1 +11010,Surok's letter,1 +11011,Zaff's instructions,1 +11012,Wand,1 +11013,Infused wand,1 +11014,Beacon ring,0 +11015,Chicken head,1 +11016,Chicken feet,1 +11017,Chicken wings,1 +11018,Chicken legs,1 +11019,Chicken feet,1 +11020,Chicken wings,1 +11021,Chicken head,1 +11022,Chicken legs,1 +11023,Magic egg,1 +11024,Rabbit mould,1 +11025,Chocolate chunks,1 +11026,Chocolate kebbit,1 +11027,Easter egg,1 +11028,Easter egg,1 +11029,Easter egg,1 +11030,Easter egg,1 +11031,Damp planks,0 +11032,Crude carving,0 +11033,Cruder carving,0 +11034,Sven's last map,0 +11035,Windswept logs,0 +11036,Parchment,1 +11037,Brine sabre,26000 +11038,Brine sabre,26000 +11039,Key,0 +11040,Key,0 +11041,Key,0 +11042,Key,0 +11043,Key,0 +11044,Rotten barrel,0 +11045,Rotten barrel,0 +11046,Rope,0 +11047,Brine rat,1 +11048,Armour shard,1 +11049,Artefact,1 +11050,Axe head,1 +11051,Artefact,1 +11052,Helmet fragment,1 +11053,Artefact,1 +11054,Shield fragment,1 +11055,Artefact,1 +11056,Sword fragment,1 +11057,Artefact,1 +11058,Mace,1 +11059,Artefact,1 +11060,Goblin village sphere,20 +11061,Ancient mace,1000 +11062,Zanik (slice),1 +11063,null,1 +11064,Ancient mace,1000 +11065,Bracelet mould,5 +11066,Bracelet mould,5 +11067,null,1 +11068,null,1 +11069,Gold bracelet,550 +11070,Gold bracelet,550 +11071,null,1 +11072,Sapphire bracelet,1150 +11073,Sapphire bracelet,1150 +11074,Bracelet of clay,1265 +11075,Bracelet of clay,1265 +11076,Emerald bracelet,1525 +11077,Emerald bracelet,1525 +11078,null,1 +11079,Castlewar brace(3),1675 +11080,Castlewar brace(3),1675 +11081,Castlewar brace(2),1675 +11082,Castlewar brace(2),1675 +11083,Castlewar brace(1),1675 +11084,Castlewar brace(1),1675 +11085,Ruby bracelet,2325 +11086,Ruby bracelet,2325 +11087,null,1 +11088,Inoculation brace,2560 +11089,Inoculation brace,2560 +11090,Phoenix necklace,4050 +11091,Phoenix necklace,4050 +11092,Diamond bracelet,3825 +11093,Diamond bracelet,3825 +11094,null,1 +11095,Forinthry brace(5),4200 +11096,Forinthry brace(5),4200 +11097,Forinthry brace(4),4200 +11098,Forinthry brace(4),4200 +11099,Forinthry brace(3),4200 +11100,Forinthry brace(3),4200 +11101,Forinthry brace(2),4200 +11102,Forinthry brace(2),4200 +11103,Forinthry brace(1),4200 +11104,Forinthry brace(1),4200 +11105,Skills necklace(4),20200 +11106,Skills necklace(4),20200 +11107,Skills necklace(3),20200 +11108,Skills necklace(3),20200 +11109,Skills necklace(2),20200 +11110,Skills necklace(2),20200 +11111,Skills necklace(1),20200 +11112,Skills necklace(1),20200 +11113,Skills necklace,20200 +11114,Skills necklace,20200 +11115,Dragon bracelet,19125 +11116,Dragon bracelet,19125 +11117,null,1 +11118,Combat bracelet(4),21040 +11119,Combat bracelet(4),21040 +11120,Combat bracelet(3),21040 +11121,Combat bracelet(3),21040 +11122,Combat bracelet(2),21040 +11123,Combat bracelet(2),21040 +11124,Combat bracelet(1),21040 +11125,Combat bracelet(1),21040 +11126,Combat bracelet,21040 +11127,Combat bracelet,21040 +11128,Berserker necklace,202000 +11129,Berserker necklace,202000 +11130,Onyx bracelet,201000 +11131,Onyx bracelet,201000 +11132,null,1 +11133,Regen bracelet,202000 +11134,Regen bracelet,202000 +11135,Bomber jacket,1 +11136,Karamja gloves 1,0 +11137,Antique lamp,1 +11138,Karamja gloves 2,0 +11139,Antique lamp,1 +11140,Karamja gloves 3,0 +11141,Antique lamp,1 +11142,null,1 +11143,null,1 +11144,null,1 +11145,null,1 +11146,null,1 +11147,null,1 +11148,null,1 +11149,null,1 +11150,null,1 +11151,Dream vial (empty),1 +11152,Dream vial (water),1 +11153,Dream vial (herb),1 +11154,Dream potion,1 +11155,Ground astral rune,20 +11156,Astral rune shards,1 +11157,Dreamy lamp,1 +11158,Cyrisus's chest,1 +11159,Hunter kit,1 +11160,null,1 +11161,null,1 +11162,null,1 +11163,null,1 +11164,Restored shield,1 +11165,Phoenix crossbow,4 +11166,Phoenix crossbow,4 +11167,Phoenix crossbow,4 +11168,Phoenix crossbow,4 +11169,Newspaper,1 +11170,Newspaper,1 +11171,Newspaper,1 +11172,Newspaper,1 +11173,Half certificate,1 +11174,Half certificate,1 +11175,Uncleaned find,1 +11176,Arrowheads,1 +11177,Jewellery,1 +11178,Pottery,1 +11179,Old coin,1 +11180,Ancient coin,1 +11181,Ancient symbol,1 +11182,Old symbol,1 +11183,Old chipped vase,1 +11184,Museum map,0 +11185,Antique lamp,1 +11186,Antique lamp,1 +11187,Antique lamp,1 +11188,Antique lamp,1 +11189,Antique lamp,1 +11190,Digsite pendant (1),2400 +11191,Digsite pendant (2),2400 +11192,Digsite pendant (3),2400 +11193,Digsite pendant (4),2400 +11194,Digsite pendant (5),2400 +11195,Clean necklace,0 +11196,Griffin feather,1 +11197,Miazrqa's pendant,1 +11198,Music sheet,1 +11199,Rupert's helmet,1 +11200,Dwarven helmet,60000 +11201,Dwarven helmet,60000 +11202,Shrinking recipe,1 +11203,To-do list,1 +11204,Shrink-me-quick,1 +11205,Shrunk ogleroot,2 +11206,null,1 +11207,null,1 +11208,null,1 +11209,null,1 +11210,Golden goblin,1 +11211,Magic beans,1 +11212,Dragon arrow,800 +11213,null,1 +11214,null,1 +11215,null,1 +11216,null,1 +11217,Dragon fire arrows,3 +11218,null,1 +11219,null,1 +11220,null,1 +11221,null,1 +11222,Dragon fire arrows,10 +11223,null,1 +11224,null,1 +11225,null,1 +11226,null,1 +11227,Dragon arrow(p),800 +11228,Dragon arrow(p+),800 +11229,Dragon arrow(p++),800 +11230,Dragon dart,500 +11231,Dragon dart(p),500 +11232,Dragon dart tip,250 +11233,Dragon dart(p+),500 +11234,Dragon dart(p++),500 +11235,Dark bow,120002 +11236,Dark bow,120002 +11237,Dragon arrowtips,500 +11238,Baby impling jar,50 +11239,Baby impling jar,50 +11240,Young impling jar,50 +11241,Young impling jar,50 +11242,Gourm' impling jar,50 +11243,Gourm' impling jar,50 +11244,Earth impling jar,50 +11245,Earth impling jar,50 +11246,Ess' impling jar,50 +11247,Ess' impling jar,50 +11248,Eclectic impling jar,50 +11249,Eclectic impling jar,50 +11250,Nature impling jar,50 +11251,Nature impling jar,50 +11252,Magpie impling jar,50 +11253,Magpie impling jar,50 +11254,Ninja impling jar,50 +11255,Ninja impling jar,50 +11256,Dragon impling jar,50 +11257,Dragon impling jar,50 +11258,Jar generator,600 +11259,Magic butterfly net,1000 +11260,Impling jar,1 +11261,Impling jar,1 +11262,Imp repellent,50 +11263,Imp repellent,50 +11264,Anchovy oil,10 +11265,Anchovy oil,10 +11266,Anchovy paste,5 +11267,Picture,1 +11268,Picture,1000 +11269,Picture,1 +11270,Picture,1 +11271,Picture,50 +11272,Picture,50 +11273,Impling scroll,3 +11274,Ham shirt,75 +11275,Mith grapple,48 +11276,Mith grapple,1 +11277,Cavalier mask,200 +11278,Beret mask,80 +11279,Elvarg's head,1 +11280,Cavalier and mask,200 +11281,Cavalier and mask,200 +11282,Beret and mask,80 +11283,Dragonfire shield,2000000 +11284,Dragonfire shield,2000000 +11285,Dragonfire shield,2000000 +11286,Draconic visage,750000 +11287,Draconic visage,750000 +11288,null,40 +11289,null,40 +11290,null,40 +11291,null,40 +11292,null,40 +11293,null,40 +11294,null,40 +11295,null,40 +11296,null,40 +11297,null,40 +11298,null,40 +11299,null,40 +11300,null,40 +11301,null,40 +11302,null,40 +11303,null,40 +11304,null,40 +11305,null,40 +11306,null,1 +11307,null,1 +11308,null,1 +11309,null,1 +11310,null,1 +11311,null,1 +11312,null,1 +11313,null,1 +11314,null,1 +11315,null,1 +11316,null,1 +11317,null,1 +11318,null,1 +11319,null,1 +11320,null,3 +11321,null,3 +11322,null,3 +11323,Barbarian rod,1 +11324,Roe,20 +11325,Roe,20 +11326,Caviar,50 +11327,Caviar,50 +11328,Leaping trout,25 +11329,Leaping trout,25 +11330,Leaping salmon,50 +11331,Leaping salmon,50 +11332,Leaping sturgeon,75 +11333,Leaping sturgeon,75 +11334,Fish offcuts,1 +11335,Dragon full helm,150000 +11336,Dragon full helm,150000 +11337,Mangled bones,1000 +11338,Chewed bones,3500 +11339,My notes,0 +11340,Barbarian skills,0 +11341,Ancient page,0 +11342,Ancient page,0 +11343,Ancient page,0 +11344,Ancient page,0 +11345,Ancient page,0 +11346,Ancient page,0 +11347,Ancient page,0 +11348,Ancient page,0 +11349,Ancient page,0 +11350,Ancient page,0 +11351,Ancient page,0 +11352,Ancient page,0 +11353,Ancient page,0 +11354,Ancient page,0 +11355,Ancient page,0 +11356,Ancient page,0 +11357,Ancient page,0 +11358,Ancient page,0 +11359,Ancient page,0 +11360,Ancient page,0 +11361,Ancient page,0 +11362,Ancient page,0 +11363,Ancient page,0 +11364,Ancient page,0 +11365,Ancient page,0 +11366,Ancient page,0 +11367,Bronze hasta,26 +11368,Bronze hasta,26 +11369,Iron hasta,91 +11370,Iron hasta,91 +11371,Steel hasta,325 +11372,Steel hasta,325 +11373,Mithril hasta,845 +11374,Mithril hasta,845 +11375,Adamant hasta,2080 +11376,Adamant hasta,2080 +11377,Rune hasta,20800 +11378,Rune hasta,20800 +11379,Bronze hasta(p),26 +11380,Bronze hasta(p),26 +11381,Bronze hasta(kp),26 +11382,Bronze hasta(p+),26 +11383,Bronze hasta(p+),26 +11384,Bronze hasta(p++),26 +11385,Bronze hasta(p++),26 +11386,Iron hasta(p),91 +11387,Iron hasta(p),91 +11388,Iron hasta(kp),91 +11389,Iron hasta(p+),91 +11390,Iron hasta(p+),91 +11391,Iron hasta(p++),91 +11392,Iron hasta(p++),91 +11393,Steel hasta(p),325 +11394,Steel hasta(p),325 +11395,Steel hasta(kp),325 +11396,Steel hasta(p+),325 +11397,Steel hasta(p+),325 +11398,Steel hasta(p++),325 +11399,Steel hasta(p++),325 +11400,Mithril hasta(p),845 +11401,Mithril hasta(p),845 +11402,Mithril hasta(kp),845 +11403,Mithril hasta(p+),845 +11404,Mithril hasta(p+),845 +11405,Mithril hasta(p++),845 +11406,Mithril hasta(p++),845 +11407,Adamant hasta(p),2080 +11408,Adamant hasta(p),2080 +11409,Adamant hasta(kp),2080 +11410,Adamant hasta(p+),2080 +11411,Adamant hasta(p+),2080 +11412,Adamant hasta(p++),2080 +11413,Adamant hasta(p++),2080 +11414,Rune hasta(p),20800 +11415,Rune hasta(p),20800 +11416,Rune hasta(kp),20800 +11417,Rune hasta(p+),20800 +11418,Rune hasta(p+),20800 +11419,Rune hasta(p++),20800 +11420,Rune hasta(p++),20800 +11421,null,1 +11422,null,1 +11423,null,1 +11424,null,1 +11425,null,1 +11426,null,1 +11427,Fish vial,3 +11428,Fish vial,3 +11429,Attack mix(2),9 +11430,Attack mix(2),9 +11431,Attack mix(1),6 +11432,Attack mix(1),6 +11433,Antipoison mix(2),216 +11434,Antipoison mix(2),216 +11435,Antipoison mix(1),144 +11436,Antipoison mix(1),144 +11437,Relicym's mix(2),150 +11438,Relicym's mix(2),150 +11439,Relicym's mix(1),75 +11440,Relicym's mix(1),75 +11441,Strength mix(1),11 +11442,Strength mix(1),11 +11443,Strength mix(2),13 +11444,Strength mix(2),13 +11445,Combat mix(2),105 +11446,Combat mix(2),105 +11447,Combat mix(1),52 +11448,Combat mix(1),52 +11449,Restore mix(2),66 +11450,Restore mix(2),66 +11451,Restore mix(1),44 +11452,Restore mix(1),44 +11453,Energy mix(2),72 +11454,Energy mix(2),72 +11455,Energy mix(1),36 +11456,Energy mix(1),36 +11457,Defence mix(2),90 +11458,Defence mix(2),90 +11459,Defence mix(1),60 +11460,Defence mix(1),60 +11461,Agility mix(2),100 +11462,Agility mix(2),100 +11463,Agility mix(1),50 +11464,Agility mix(1),50 +11465,Prayer mix(2),114 +11466,Prayer mix(2),114 +11467,Prayer mix(1),76 +11468,Prayer mix(1),76 +11469,Superattack mix(2),135 +11470,Superattack mix(2),135 +11471,Superattack mix(1),90 +11472,Superattack mix(1),90 +11473,Anti-p supermix(2),216 +11474,Anti-p supermix(2),216 +11475,Anti-p supermix(1),144 +11476,Anti-p supermix(1),144 +11477,Fishing mix(2),150 +11478,Fishing mix(2),150 +11479,Fishing mix(1),100 +11480,Fishing mix(1),100 +11481,Sup. energy mix(2),160 +11482,Sup. energy mix(2),160 +11483,Sup. energy mix(1),90 +11484,Sup. energy mix(1),90 +11485,Sup. str. mix(2),165 +11486,Sup. str. mix(2),165 +11487,Sup. str. mix(1),110 +11488,Sup. str. mix(1),110 +11489,Magic ess. mix(2),180 +11490,Magic ess. mix(2),180 +11491,Magic ess. mix(1),130 +11492,Magic ess. mix(1),130 +11493,Sup. restore mix(2),180 +11494,Sup. restore mix(2),180 +11495,Sup. restore mix(1),120 +11496,Sup. restore mix(1),120 +11497,Sup. def. mix(2),198 +11498,Sup. def. mix(2),198 +11499,Sup. def. mix(1),132 +11500,Sup. def. mix(1),132 +11501,Antidote+ mix(2),216 +11502,Antidote+ mix(2),216 +11503,Antidote+ mix(1),144 +11504,Antidote+ mix(1),144 +11505,Antifire mix(2),198 +11506,Antifire mix(2),198 +11507,Antifire mix(1),132 +11508,Antifire mix(1),132 +11509,Ranging mix(2),216 +11510,Ranging mix(2),216 +11511,Ranging mix(1),144 +11512,Ranging mix(1),144 +11513,Magic mix(2),200 +11514,Magic mix(2),200 +11515,Magic mix(1),150 +11516,Magic mix(1),150 +11517,Hunting mix(2),9 +11518,Hunting mix(2),9 +11519,Hunting mix(1),6 +11520,Hunting mix(1),6 +11521,Zamorak mix(2),150 +11522,Zamorak mix(2),150 +11523,Zamorak mix(1),125 +11524,Zamorak mix(1),125 +11525,Wimpy feather,25 +11526,null,3 +11527,null,3 +11528,null,3 +11529,null,3 +11530,null,3 +11531,null,3 +11532,null,3 +11533,null,3 +11534,null,3 +11535,null,3 +11536,null,3 +11537,null,3 +11538,null,3 +11539,null,3 +11540,null,3 +11541,null,3 +11542,null,3 +11543,null,3 +11544,null,3 +11545,null,3 +11546,null,3 +11547,null,3 +11548,null,3 +11549,null,3 +11550,null,3 +11551,null,3 +11552,null,3 +11553,null,3 +11554,null,3 +11555,null,3 +11556,null,3 +11557,null,3 +11558,null,3 +11559,null,3 +11560,null,3 +11561,null,3 +11562,null,3 +11563,null,3 +11564,null,3 +11565,null,3 +11566,null,3 +11567,null,3 +11568,null,3 +11569,null,3 +11570,null,3 +11571,null,3 +11572,null,3 +11573,null,3 +11574,null,3 +11575,null,3 +11576,null,3 +11577,null,3 +11578,null,3 +11579,null,3 +11580,null,3 +11581,null,3 +11582,null,3 +11583,null,3 +11584,null,3 +11585,null,3 +11586,null,3 +11587,null,3 +11588,null,3 +11589,null,3 +11590,null,3 +11591,null,3 +11592,null,3 +11593,null,3 +11594,null,3 +11595,null,3 +11596,null,3 +11597,null,3 +11598,null,3 +11599,null,3 +11600,null,3 +11601,null,3 +11602,null,3 +11603,null,3 +11604,null,3 +11605,null,3 +11606,null,3 +11607,null,3 +11608,null,3 +11609,null,3 +11610,null,3 +11611,null,3 +11612,null,3 +11613,null,3 +11614,null,3 +11615,null,3 +11616,null,3 +11617,null,3 +11618,null,3 +11619,null,3 +11620,null,3 +11621,null,3 +11622,null,3 +11623,null,3 +11624,null,3 +11625,null,3 +11626,null,3 +11627,null,3 +11628,null,3 +11629,null,3 +11630,null,3 +11631,null,3 +11632,null,3 +11633,null,3 +11634,null,3 +11635,null,3 +11636,null,3 +11637,null,3 +11638,null,3 +11639,null,3 +11640,Book of knowledge,1 +11641,null,3 +11642,null,3 +11643,null,3 +11644,null,3 +11645,null,3 +11646,null,3 +11647,null,3 +11648,null,3 +11649,null,3 +11650,null,3 +11651,null,3 +11652,null,3 +11653,null,3 +11654,null,3 +11655,Astronomy book,3 +11656,Glassblowing book,3 +11657,Glassblowing book,3 +11658,null,1 +11659,null,1 +11660,null,1 +11661,null,1 +11662,null,1 +11663,Void mage helm,9851 +11664,Void ranger helm,9851 +11665,Void melee helm,9851 +11666,Void seal(8),100 +11667,Void seal(7),100 +11668,Void seal(6),100 +11669,Void seal(5),100 +11670,Void seal(4),100 +11671,Void seal(3),100 +11672,Void seal(2),100 +11673,Void seal(1),100 +11674,Void mage helm,9851 +11675,Void ranger helm,9851 +11676,Void melee helm,9851 +11677,Explorer's notes,6 +11678,Black knight helm,1 +11679,Antique lamp,1 +11680,Address form,1 +11681,Scrap paper,1 +11682,Hair clip,1 +11683,null,1 +11684,null,1 +11685,null,1 +11686,Godsword shards,500000 +11687,Godsword shards,500000 +11688,Godsword shards,500000 +11689,Godsword shards,500000 +11690,Godsword blade,750000 +11691,Godsword blade,750000 +11692,Godsword shards,500000 +11693,Godsword shards,500000 +11694,Armadyl godsword,1250000 +11695,Armadyl godsword,1250000 +11696,Bandos godsword,1250000 +11697,Bandos godsword,1250000 +11698,Saradomin godsword,1250000 +11699,Saradomin godsword,1250000 +11700,Zamorak godsword,1250000 +11701,Zamorak godsword,1250000 +11702,Armadyl hilt,500000 +11703,Armadyl hilt,500000 +11704,Bandos hilt,500000 +11705,Bandos hilt,500000 +11706,Saradomin hilt,500000 +11707,Saradomin hilt,500000 +11708,Zamorak hilt,500000 +11709,Zamorak hilt,500000 +11710,Godsword shard 1,250000 +11711,Godsword shard 1,250000 +11712,Godsword shard 2,250000 +11713,Godsword shard 2,250000 +11714,Godsword shard 3,250000 +11715,Godsword shard 3,250000 +11716,Zamorakian spear,100000 +11717,Zamorakian spear,100000 +11718,Armadyl helmet,20000 +11719,Armadyl helmet,20000 +11720,Armadyl chestplate,60000 +11721,Armadyl chestplate,60000 +11722,Armadyl plateskirt,50000 +11723,Armadyl plateskirt,50000 +11724,Bandos chestplate,265000 +11725,Bandos chestplate,265000 +11726,Bandos tassets,272500 +11727,Bandos tassets,272500 +11728,Bandos boots,25000 +11729,Bandos boots,25000 +11730,Saradomin sword,130000 +11731,Saradomin sword,130000 +11732,Dragon boots,20000 +11733,Dragon boots,20000 +11734,Knight's notes,0 +11735,Knight's notes,0 +11736,Steam battlestaff,17000 +11737,Steam battlestaff,17000 +11738,Mystic steam staff,45000 +11739,Mystic steam staff,45000 +11740,Picture,1 +11741,Picture,1 +11742,Picture,1 +11743,Picture,1 +11744,Picture,1 +11745,Picture,1 +11746,Agility jump,1 +11747,A key to a chest,1 +11748,Strongroom key,1 +11749,Crystal chime,10 +11750,Yewnock's notes,1 +11751,null,1 +11752,null,1 +11753,Antique lamp,1 +11754,Antique lamp,1 +11755,Antique lamp,1 +11756,Varrock armour 1,0 +11757,Varrock armour 2,0 +11758,Varrock armour 3,0 +11759,null,1 +11760,Logs,1 +11761,Locked diary,1 +11762,Unlocked diary,1 +11763,Hand,1 +11764,Foot,1 +11765,Torso,1 +11766,Left arm,1 +11767,Right arm,1 +11768,Left leg,1 +11769,Right leg,1 +11770,Root cutting,1 +11771,Root cutting,1 +11772,Root cutting,1 +11773,Root cutting,1 +11774,Root cutting,1 +11775,Wilted cutting,1 +11776,Potted root,1 +11777,Sealed pot,1 +11778,Jade vine seed,1 +11779,null,1 +11780,The grim reaper's diary,1 +11781,Grim's robe,5 +11782,Last will and testament,1 +11783,Human bones,1 +11784,Servant's skull,1 +11785,Hourglass,1 +11786,Scythe sharpener,1 +11787,Human eye,1 +11788,'voice of doom' potion,1 +11789,Grim reaper hood,1 +11790,Grim reaper hood,1 +11791,White goblin mail,40 +11792,Grubfoot,1 +11793,Zanik,1 +11794,Plain of mud sphere,20 +11795,Ekeleshuun key,1 +11796,Narogoshuun key,1 +11797,Huzamogaarb key,1 +11798,Saragorgak key,1 +11799,Horogothgar key,1 +11800,Yurkolgokh key,1 +11801,null,1 +11802,Snothead's bone,1 +11803,Snailfeet's bone,1 +11804,Mosschin's bone,1 +11805,Redeyes's bone,1 +11806,Strongbones's bone,1 +11807,Pharmakos berries,10 +11808,Whitefish,20 +11809,Goblin potion (4),14 +11810,Goblin potion (3),13 +11811,Goblin potion (2),13 +11812,Goblin potion (1),13 +11813,Tinderbox,0 +11814,Bronze armour set (l),20 +11815,Bronze armour set (l),20 +11816,Bronze armour set (sk),20 +11817,Bronze armour set (sk),20 +11818,Iron armour set (l),20 +11819,Iron armour set (l),20 +11820,Iron armour set (sk),20 +11821,Iron armour set (sk),20 +11822,Steel armour set (l),20 +11823,Steel armour set (l),20 +11824,Steel armour set (sk),20 +11825,Steel armour set (sk),20 +11826,Black armour set (l),20 +11827,Black armour set (l),20 +11828,Black armour set (sk),20 +11829,Black armour set (sk),20 +11830,Mithril armour set (l),20 +11831,Mithril armour set (l),20 +11832,Mithril armour set (sk),20 +11833,Mithril armour set (sk),20 +11834,Adamant armour set (l),20 +11835,Adamant armour set (l),20 +11836,Adamant armour set (sk),20 +11837,Adamant armour set (sk),20 +11838,Rune armour set (l),20 +11839,Rune armour set (l),20 +11840,Rune armour set (sk),20 +11841,Rune armour set (sk),20 +11842,Dragon chain armour set (l),20 +11843,Dragon chain armour set (l),20 +11844,Dragon chain armour set (sk),20 +11845,Dragon chain armour set (sk),20 +11846,Barrows - ahrim's set,20 +11847,Barrows - ahrim's set,20 +11848,Barrows - dharok's set,20 +11849,Barrows - dharok's set,20 +11850,Barrows - guthan's set,20 +11851,Barrows - guthan's set,20 +11852,Barrows - karil's set,20 +11853,Barrows - karil's set,20 +11854,Barrows - torag's set,20 +11855,Barrows - torag's set,20 +11856,Barrows - verac's set,20 +11857,Barrows - verac's set,20 +11858,Third age melee set,20 +11859,Third age melee set,20 +11860,Third age ranger set,20 +11861,Third age ranger set,20 +11862,Third age mage set,20 +11863,Third age mage set,20 +11864,Green dragonhide set,20 +11865,Green dragonhide set,20 +11866,Blue dragonhide set,20 +11867,Blue dragonhide set,20 +11868,Red dragonhide set,20 +11869,Red dragonhide set,20 +11870,Black dragonhide set,20 +11871,Black dragonhide set,20 +11872,Mystic robes set,20 +11873,Mystic robes set,20 +11874,Infinity robes set,20 +11875,Infinity robes set,20 +11876,Splitbark armour set,20 +11877,Splitbark armour set,20 +11878,Black trimmed armour set (l),20 +11879,Black trimmed armour set (l),20 +11880,Black trimmed armour set (sk),20 +11881,Black trimmed armour set (sk),20 +11882,Black gold-trimmed armour set (l),20 +11883,Black gold-trimmed armour set (l),20 +11884,Black gold-trimmed armour set (sk),20 +11885,Black gold-trimmed armour set (sk),20 +11886,Adamant trimmed armour set (l),20 +11887,Adamant trimmed armour set (l),20 +11888,Adamant trimmed armour set (sk),20 +11889,Adamant trimmed armour set (sk),20 +11890,Adamant gold-trimmed armour set (l),20 +11891,Adamant gold-trimmed armour set (l),20 +11892,Adamant gold-trimmed armour set (sk),20 +11893,Adamant gold-trimmed armour set (sk),20 +11894,Rune trimmed armour set (l),20 +11895,Rune trimmed armour set (l),20 +11896,Rune trimmed armour set (sk),20 +11897,Rune trimmed armour set (sk),20 +11898,Rune gold-trimmed armour set (l),20 +11899,Rune gold-trimmed armour set (l),20 +11900,Rune gold-trimmed armour set (sk),20 +11901,Rune gold-trimmed armour set (sk),20 +11902,Enchanted set,20 +11903,Enchanted set,20 +11904,Trimmed blue wizard set,20 +11905,Trimmed blue wizard set,20 +11906,Gold-trimmed blue wizard set,20 +11907,Gold-trimmed blue wizard set,20 +11908,Trimmed leather armour set,20 +11909,Trimmed leather armour set,20 +11910,Gold-trimmed leather armour set,20 +11911,Gold-trimmed leather armour set,20 +11912,Green d'hide trimmed set,20 +11913,Green d'hide trimmed set,20 +11914,Green d'hide gold-trimmed set,20 +11915,Green d'hide gold-trimmed set,20 +11916,Blue d'hide trimmed set,20 +11917,Blue d'hide trimmed set,20 +11918,Blue d'hide gold-trimmed set,20 +11919,Blue d'hide gold-trimmed set,20 +11920,Green d'hide blessed set,20 +11921,Green d'hide blessed set,20 +11922,Blue d'hide blessed set,20 +11923,Blue d'hide blessed set,20 +11924,Red d'hide blessed set,20 +11925,Red d'hide blessed set,20 +11926,Guthix armour set (l),20 +11927,Guthix armour set (l),20 +11928,Saradomin armour set (l),20 +11929,Saradomin armour set (l),20 +11930,Zamorak armour set (l),20 +11931,Zamorak armour set (l),20 +11932,Guthix armour set (sk),20 +11933,Guthix armour set (sk),20 +11934,Saradomin armour set (sk),20 +11935,Saradomin armour set (sk),20 +11936,Zamorak armour set (sk),20 +11937,Zamorak armour set (sk),20 +11938,Gilded armour set (l),20 +11939,Gilded armour set (l),20 +11940,Gilded armour set (sk),20 +11941,Gilded armour set (sk),20 +11942,Rockshell armour set,20 +11943,Rockshell armour set,20 +11944,Spined armour set,20 +11945,Spined armour set,20 +11946,Skeletal armour set,20 +11947,Skeletal armour set,20 +11948,null,1 +11949,Snow globe,1 +11950,Snow globe,1 +11951,Snowball,1 +11952,Ice sword,1 +11953,Winter staff,1 +11954,Holly bow,1 +11955,Barbarian snowman hat,1 +11956,Dragon snowman hat,1 +11957,Dwarf snowman hat,1 +11958,Pirate snowman hat,1 +11959,Snowman top hat,1 +11960,Light mystic robes set,20 +11961,Light mystic robes set,20 +11962,Dark mystic robes set,20 +11963,Dark mystic robes set,20 +11964,Raven egg,1 +11965,Vulture egg,1 +11966,Bird's nest,1 +11967,Dwarf cannon set,20 +11968,Dwarf cannon set,20 +11969,Enchanted water tiara,100 +11970,Smelly crate,1 +11971,Full crate,1 +11972,Empty crate,1 +11973,Scabaras research,1 +11974,Artefact receipt,1 +11975,Scabarite notes,1 +11976,Scabarite notes,1 +11977,Scabarite notes,1 +11978,Scabarite notes,1 +11979,Scabarite notes,1 +11980,Scabarite notes,1 +11981,Scabarite notes,1 +11982,Scabarite notes,1 +11983,Scabarite notes,1 +11984,Scabarite notes,1 +11985,Scabarite notes,1 +11986,Scabarite notes,1 +11987,Scabarite notes,1 +11988,Scabarite notes,1 +11989,Scabarite notes,1 +11990,Scabarite notes,1 +11991,Scabarite notes,1 +11992,Scabarite notes,1 +11993,Scabarite notes,1 +11994,Scabarite key,1 +11995,Oxidised dagger,1 +11996,Oxidised medium helm,1 +11997,Oxidised sword,1 +11998,Oxidised scimitar,1 +11999,Oxidised longsword,1 +12000,Oxidised full helm,1 +12001,Oxidised square shield,1 +12002,Oxidised chainbody,1 +12003,Oxidised kiteshield,1 +12004,Oxidised two-handed sword,1 +12005,Oxidised platelegs,1 +12006,Oxidised plateskirt,1 +12007,Spirit terrorbird pouch,724 +12008,Spirit terrorbird pouch,724 +12009,Granite crab pouch,599 +12010,Granite crab pouch,599 +12011,Praying mantis pouch,4624 +12012,Praying mantis pouch,4624 +12013,Giant ent pouch,3524 +12014,Giant ent pouch,3524 +12015,Spirit cobra pouch,3324 +12016,Spirit cobra pouch,3324 +12017,Spirit dagannoth pouch,449 +12018,Spirit dagannoth pouch,449 +12019,Thorny snail pouch,649 +12020,Thorny snail pouch,649 +12021,Beaver pouch,2224 +12022,Beaver pouch,2224 +12023,Karam. overlord pouch,4024 +12024,Karam. overlord pouch,4024 +12025,Hydra pouch,3624 +12026,Hydra pouch,3624 +12027,Spirit jelly pouch,4199 +12028,Spirit jelly pouch,4199 +12029,Bunyip pouch,3174 +12030,Bunyip pouch,3174 +12031,War tortoise pouch,449 +12032,War tortoise pouch,449 +12033,Fruit bat pouch,3674 +12034,Fruit bat pouch,3674 +12035,Abyssal parasite pouch,3074 +12036,Abyssal parasite pouch,3074 +12037,Abyssal lurker pouch,3399 +12038,Abyssal lurker pouch,3399 +12039,Unicorn stallion pouch,3924 +12040,Unicorn stallion pouch,3924 +12041,Magpie pouch,2624 +12042,Magpie pouch,2624 +12043,Dreadfowl pouch,624 +12044,Dreadfowl pouch,624 +12045,Stranger plant pouch,3624 +12046,Stranger plant pouch,3624 +12047,Spirit wolf pouch,599 +12048,Spirit wolf pouch,599 +12049,Desert wyrm pouch,1549 +12050,Desert wyrm pouch,1549 +12051,Evil turnip pouch,3024 +12052,Evil turnip pouch,3024 +12053,Vampire bat pouch,2449 +12054,Vampire bat pouch,2449 +12055,Spirit scorpion pouch,1849 +12056,Spirit scorpion pouch,1849 +12057,Arctic bear pouch,774 +12058,Arctic bear pouch,774 +12059,Spirit spider pouch,624 +12060,Spirit spider pouch,624 +12061,Bloated leech pouch,3349 +12062,Bloated leech pouch,3349 +12063,Spirit kalphite pouch,1699 +12064,Spirit kalphite pouch,1699 +12065,Honey badger pouch,2524 +12066,Honey badger pouch,2524 +12067,Albino rat pouch,2299 +12068,Albino rat pouch,2299 +12069,Granite lobster pouch,4574 +12070,Granite lobster pouch,4574 +12071,Macaw pouch,2374 +12072,Macaw pouch,2374 +12073,Bronze minotaur pouch,2974 +12074,Bronze minotaur pouch,2974 +12075,Iron minotaur pouch,3549 +12076,Iron minotaur pouch,3549 +12077,Steel minotaur pouch,3949 +12078,Steel minotaur pouch,3949 +12079,Mithril minotaur pouch,4224 +12080,Mithril minotaur pouch,4224 +12081,Adamant minotaur pouch,4024 +12082,Adamant minotaur pouch,4024 +12083,Rune minotaur pouch,449 +12084,Rune minotaur pouch,449 +12085,Smoke devil pouch,3949 +12086,Smoke devil pouch,3949 +12087,Bull ant pouch,699 +12088,Bull ant pouch,699 +12089,Wolpertinger pouch,5499 +12090,Wolpertinger pouch,5499 +12091,Compost mound pouch,1599 +12092,Compost mound pouch,1599 +12093,Pack yak pouch,5699 +12094,Pack yak pouch,5699 +12095,Sp. cockatrice pouch,2624 +12096,Sp. cockatrice pouch,2624 +12097,Sp. guthatrice pouch,2624 +12098,Sp. guthatrice pouch,2624 +12099,Sp. saratrice pouch,2624 +12100,Sp. saratrice pouch,2624 +12101,Sp. zamatrice pouch,2624 +12102,Sp. zamatrice pouch,2624 +12103,Sp. pengatrice pouch,2624 +12104,Sp. pengatrice pouch,2624 +12105,Sp. coraxatrice pouch,2624 +12106,Sp. coraxatrice pouch,2624 +12107,Sp. vulatrice pouch,2624 +12108,Sp. vulatrice pouch,2624 +12109,Cockatrice egg,100 +12110,Cockatrice egg,100 +12111,Guthatrice egg,1000 +12112,Guthatrice egg,1000 +12113,Saratrice egg,1000 +12114,Saratrice egg,1000 +12115,Zamatrice egg,1000 +12116,Zamatrice egg,1000 +12117,Pengatrice egg,1000 +12118,Pengatrice egg,1000 +12119,Coraxatrice egg,1000 +12120,Coraxatrice egg,1000 +12121,Vulatrice egg,1000 +12122,Vulatrice egg,1000 +12123,Barker toad pouch,699 +12124,Barker toad pouch,699 +12125,Flies,10 +12126,Flies,10 +12127,Beetle bits,10 +12128,Beetle bits,10 +12129,Ground fishing bait,1 +12130,Nuts,10 +12131,Nuts,10 +12132,Truffle,200 +12133,Truffle,200 +12134,Evil turnip,30 +12135,Evil turnip,30 +12136,2/3 evil turnip,20 +12137,2/3 evil turnip,20 +12138,1/3 evil turnip,10 +12139,1/3 evil turnip,10 +12140,Summoning potion(4),190 +12141,Summoning potion(4),190 +12142,Summoning potion(3),152 +12143,Summoning potion(3),152 +12144,Summoning potion(2),114 +12145,Summoning potion(2),114 +12146,Summoning potion(1),76 +12147,Summoning potion(1),76 +12148,Evil turnip seed,20 +12149,null,1 +12150,null,1 +12151,null,1 +12152,null,1 +12153,Carved evil turnip,50 +12154,Carved evil turnip,50 +12155,Pouch,1 +12156,Honeycomb,5 +12157,Honeycomb,5 +12158,Gold charm,100 +12159,Green charm,100 +12160,Crimson charm,100 +12161,Abyssal charm,1 +12162,Talon beast charm,1 +12163,Blue charm,100 +12164,Ravager charm,1 +12165,Shifter charm,1 +12166,Spinner charm,1 +12167,Torcher charm,1 +12168,Obsidian charm,1 +12169,Summoning cape,99000 +12170,Summoning cape(t),99000 +12171,Summoning hood,1 +12172,Clean spirit weed,24 +12173,Clean spirit weed,24 +12174,Grimy spirit weed,1 +12175,Grimy spirit weed,1 +12176,Spirit weed seed,18 +12177,null,1 +12178,null,1 +12179,null,1 +12180,null,1 +12181,Spirit weed potion(unf),23 +12182,Spirit weed potion(unf),23 +12183,Spirit shards,25 +12184,Gecko,1 +12185,Saradomin owl,1 +12186,Zamorak hawk,1 +12187,Guthix raptor,1 +12188,Penguin,1 +12189,Bird,1 +12190,Vulture,1 +12191,Terrier puppy,1 +12192,Greyhound puppy,1 +12193,Labrador puppy,1 +12194,Dalmatian puppy,1 +12195,Sheepdog puppy,1 +12196,Bulldog puppy,1 +12197,Dragon hatchling,1 +12198,Baby giant crab,1 +12199,Baby raccoon,1 +12200,Squirrel,1 +12201,Monkey,1 +12202,null,1 +12203,Chameleon,1 +12204,Antlers,100 +12205,Antlers,100 +12206,Antlers (charged),1 +12207,Lizard skull,200 +12208,Lizard skull,200 +12209,Lizard skull (charged),1 +12210,Feather headdress,400 +12211,Feather headdress,400 +12212,Feather headdress (charged),1 +12213,Feather headdress,400 +12214,Feather headdress,400 +12215,Feather headdress (charged),1 +12216,Feather headdress,400 +12217,Feather headdress,400 +12218,Feather headdress (charged),1 +12219,Feather headdress,400 +12220,Feather headdress,400 +12221,Feather headdress (charged),1 +12222,Feather headdress,400 +12223,Feather headdress,400 +12224,Feather headdress (charged),1 +12225,Pouch,1 +12226,Pouch,1 +12227,Pouch,1 +12228,Pouch,1 +12229,Pouch,1 +12230,Pouch,1 +12231,Pouch,1 +12232,Pouch,1 +12233,Pouch,1 +12234,Pouch,1 +12235,Pouch,1 +12236,Pouch,1 +12237,Pouch,1 +12238,Pouch,1 +12239,Pouch,1 +12240,Pouch,1 +12241,Pouch,1 +12242,Pouch,1 +12243,Pouch,1 +12244,Pouch,1 +12245,Pouch,1 +12246,Pouch,1 +12247,Pouch,1 +12248,Pouch,1 +12249,Pouch,1 +12250,Pouch,1 +12251,Pouch,1 +12252,Pouch,1 +12253,Pouch,1 +12254,Pouch,1 +12255,Pouch,1 +12256,Pouch,1 +12257,Pouch,1 +12258,Pouch,1 +12259,Pouch,1 +12260,Pouch,1 +12261,Pouch,1 +12262,Pouch,1 +12263,Pouch,1 +12264,Pouch,1 +12265,Pouch,1 +12266,Pouch,1 +12267,Pouch,1 +12268,Pouch,1 +12269,Pouch,1 +12270,Pouch,1 +12271,Pouch,1 +12272,Pouch,1 +12273,Pouch,1 +12274,Pouch,1 +12275,Pouch,1 +12276,Pouch,1 +12277,Pouch,1 +12278,Pouch,1 +12279,Pouch,1 +12280,Pouch,1 +12281,Pouch,1 +12282,Pouch,1 +12283,Pouch,1 +12284,Pouch,1 +12285,Pouch,1 +12286,Pouch,1 +12287,Pouch,1 +12288,Pouch,1 +12289,Pouch,1 +12290,Pouch,1 +12291,Pouch,1 +12292,Pouch,1 +12293,Pouch,1 +12294,Pouch,1 +12295,Pouch,1 +12296,Pouch,1 +12297,Pouch,1 +12298,Pouch,1 +12299,Pouch,1 +12300,Pouch,1 +12301,Pouch,1 +12302,Pouch,1 +12303,Pouch,1 +12304,Pouch,1 +12305,Pouch,1 +12306,Pouch,1 +12307,Pouch,1 +12308,Pouch,1 +12309,Pouch,1 +12310,Pouch,1 +12311,Pouch,1 +12312,Pouch,1 +12313,Pouch,1 +12314,Pouch,1 +12315,Pouch,1 +12316,Pouch,1 +12317,Pouch,1 +12318,Pouch,1 +12319,Pouch,1 +12320,Pouch,1 +12321,Pouch,1 +12322,Pouch,1 +12323,Pouch,1 +12324,Pouch,1 +12325,Pouch,1 +12326,Pouch,1 +12327,Pouch,1 +12328,Pouch,1 +12329,Pouch,1 +12330,Pouch,1 +12331,Pouch,1 +12332,Pouch,1 +12333,Pouch,1 +12334,Pouch,1 +12335,Pouch,1 +12336,Pouch,1 +12337,Pouch,1 +12338,Pouch,1 +12339,Pouch,1 +12340,Pouch,1 +12341,Pouch,1 +12342,Pouch,1 +12343,Pouch,1 +12344,Pouch,1 +12345,Pouch,1 +12346,Pouch,1 +12347,Pouch,1 +12348,Pouch,1 +12349,Pouch,1 +12350,Pouch,1 +12351,Pouch,1 +12352,Pouch,1 +12353,Pouch,1 +12354,Pouch,1 +12355,Pouch,1 +12356,Pouch,1 +12357,Pouch,1 +12358,Pouch,1 +12359,Pouch,1 +12360,Pouch,1 +12361,Pouch,1 +12362,Pouch,1 +12363,Pouch,1 +12364,Pouch,1 +12365,Pouch,1 +12366,Pouch,1 +12367,Pouch,1 +12368,Pouch,1 +12369,Pouch,1 +12370,Pouch,1 +12371,Pouch,1 +12372,Pouch,1 +12373,Pouch,1 +12374,Pouch,1 +12375,Pouch,1 +12376,Pouch,1 +12377,Pouch,1 +12378,Pouch,1 +12379,Pouch,1 +12380,Pouch,1 +12381,Pouch,1 +12382,Pouch,1 +12383,Pouch,1 +12384,Pouch,1 +12385,Pouch,1 +12386,Pouch,1 +12387,Pouch,1 +12388,Pouch,1 +12389,Pouch,1 +12390,Pouch,1 +12391,Pouch,1 +12392,Pouch,1 +12393,Pouch,1 +12394,Pouch,1 +12395,Pouch,1 +12396,Pouch,1 +12397,Pouch,1 +12398,Pouch,1 +12399,Pouch,1 +12400,Pouch,1 +12401,Pouch,1 +12402,Pouch,1 +12403,Pouch,1 +12404,Pouch,1 +12405,Pouch,1 +12406,Pouch,1 +12407,Pouch,1 +12408,Pouch,1 +12409,Pouch,1 +12410,Pouch,1 +12411,Pouch,1 +12412,Pouch,1 +12413,Pouch,1 +12414,Pouch,1 +12415,Pouch,1 +12416,Pouch,1 +12417,Pouch,1 +12418,Pouch,1 +12419,Pouch,1 +12420,Pouch,1 +12421,Scroll,1 +12422,Herbcall scroll,237 +12423,Fruitfall scroll,367 +12424,Fish rain scroll,314 +12425,Howl scroll,59 +12426,Thieving fingers scroll,262 +12427,Abyssal stealth scroll,339 +12428,Egg spawn scroll,62 +12429,Multichop scroll,222 +12430,Cheese feast scroll,229 +12431,Unburden scroll,69 +12432,Venom shot scroll,184 +12433,Insane ferocity scroll,252 +12434,Healing aura scroll,392 +12435,Winter storage scroll,569 +12436,Oph. incubation scroll,332 +12437,Magic focus scroll,549 +12438,Swallow whole scroll,317 +12439,Testudo scroll,44 +12440,Generate compost scroll,159 +12441,Tireless run scroll,72 +12442,Regrowth scroll,362 +12443,Call to arms scroll,227 +12444,Blood drain scroll,334 +12445,Dreadfowl strike scroll,62 +12446,Sandstorm scroll,169 +12447,Vampire touch scroll,244 +12448,Evil flames scroll,302 +12449,Crushing claw scroll,457 +12450,Mantis strike scroll,462 +12451,Arctic blast scroll,77 +12452,Toad bark scroll,69 +12453,Dissolve scroll,419 +12454,Abyssal drain scroll,307 +12455,Doomsphere scroll,402 +12456,Spike shot scroll,44 +12457,Acorn missile scroll,352 +12458,Petrifying gaze scroll,262 +12459,Slime spray scroll,64 +12460,Electric lash scroll,154 +12461,Bronze bull rush scroll,297 +12462,Iron bull rush scroll,354 +12463,Steel bull rush scroll,394 +12464,Mith bull rush scroll,422 +12465,Addy bull rush scroll,402 +12466,Rune bull rush scroll,44 +12467,Poisonous blast scroll,362 +12468,Dust cloud scroll,394 +12469,Hatchling dragon,1 +12470,Baby dragon,1 +12471,Hatchling dragon,1 +12472,Baby dragon,1 +12473,Hatchling dragon,1 +12474,Baby dragon,1 +12475,Hatchling dragon,1 +12476,Baby dragon,1 +12477,Red dragon egg,1 +12478,Blue dragon egg,1 +12479,Green dragon egg,1 +12480,Black dragon egg,1 +12481,Baby penguin,1 +12482,Penguin,1 +12483,Penguin egg,1 +12484,Raven chick,1 +12485,Raven,1 +12486,Baby raccoon,1 +12487,Raccoon,1 +12488,Baby gecko,1 +12489,Gecko,1 +12490,Baby squirrel,1 +12491,Squirrel,1 +12492,Baby chameleon,1 +12493,Chameleon,1 +12494,Chameleon egg,1 +12495,Chameleon egg,1 +12496,Baby monkey,1 +12497,Monkey,1 +12498,Vulture chick,1 +12499,Vulture,1 +12500,Baby giant crab,1 +12501,Giant crab,1 +12502,Crunchy claw token,1 +12503,Saradomin chick,1 +12504,Saradomin bird,1 +12505,Saradomin owl,1 +12506,Zamorak chick,1 +12507,Zamorak bird,1 +12508,Zamorak hawk,1 +12509,Guthix chick,1 +12510,Guthix bird,1 +12511,Guthix raptor,1 +12512,Terrier puppy,1 +12513,Terrier,1 +12514,Greyhound puppy,1 +12515,Greyhound,1 +12516,Labrador puppy,1 +12517,Labrador,1 +12518,Dalmatian puppy,1 +12519,Dalmatian,1 +12520,Sheepdog puppy,1 +12521,Sheepdog,1 +12522,Bulldog puppy,1 +12523,Bulldog,1 +12524,Summoning cape,99000 +12525,Pouch,0 +12526,Spirit wolf pouch,0 +12527,Gold charm,0 +12528,Trapdoor key,1 +12529,Howl scroll,0 +12530,Spirit shards,0 +12531,Ibis pouch,3149 +12532,Ibis pouch,3149 +12533,Stony shell scroll,59 +12534,null,1 +12535,Raw pawya meat,500 +12536,Raw pawya meat,500 +12537,Picture,1 +12538,Picture,1 +12539,Grenwall spikes,200 +12540,null,1 +12541,null,1 +12542,null,1 +12543,null,1 +12544,Picture,1 +12545,Picture,1 +12546,Enchanted pawya meat,1 +12547,Platypus,1 +12548,Platypus,0 +12549,Platypus,0 +12550,Platypus,0 +12551,Baby platypus,0 +12552,Baby platypus,0 +12553,Baby platypus,0 +12554,Patrick,0 +12555,Penelope,0 +12556,Peter,0 +12557,Peanut,0 +12558,Mud,10 +12559,Ogre wig,60 +12560,Ogre wig,60 +12561,Ogre kilt,60 +12562,Ogre kilt,60 +12563,Ogre top,60 +12564,Ogre top,60 +12565,Ogre boots,60 +12566,Ogre boots,60 +12567,Diseased kebbit fur,60 +12568,Davy kebbit hat,100 +12569,Davy kebbit hat,100 +12570,Ogre club,180 +12571,Ogre club,180 +12572,Lavender,4 +12573,Lavender,4 +12574,Fever grass,4 +12575,Fever grass,4 +12576,Tansymum,4 +12577,Tansymum,4 +12578,Smouldering fever grass,4 +12579,Smouldering lavender,4 +12580,Smouldering tansymum,4 +12581,Eucalyptus logs,126 +12582,Eucalyptus logs,126 +12583,Eucalyptus pyre logs,252 +12584,Eucalyptus pyre logs,252 +12585,null,1 +12586,null,1 +12587,null,1 +12588,Primweed,2 +12589,Primweed,2 +12590,Stinkbloom,2 +12591,Stinkbloom,2 +12592,Diseased kebbit fur,60 +12593,Catapult schematics,1 +12594,Engineer's letter,1 +12595,Sailor's hat,1 +12596,Metal catapult parts,1 +12597,Mahogany catapult part,1 +12598,Mahogany catapult part,1 +12599,Mahogany catapult part,1 +12600,Mahogany catapult part,1 +12601,Mahogany catapult part,1 +12602,Mahogany catapult part,1 +12603,Mahogany catapult part,1 +12604,Mahogany catapult part,1 +12605,Mahogany catapult part,1 +12606,Mahogany catapult part,1 +12607,Picture,1 +12608,Amulet of farming(1),200 +12609,Amulet of farming(1),200 +12610,Amulet of farming(2),200 +12611,Amulet of farming(2),200 +12612,Amulet of farming(3),200 +12613,Amulet of farming(3),200 +12614,Amulet of farming(4),200 +12615,Amulet of farming(4),200 +12616,Amulet of farming(5),200 +12617,Amulet of farming(5),200 +12618,Amulet of farming(6),200 +12619,Amulet of farming(6),200 +12620,Amulet of farming(7),200 +12621,Amulet of farming(7),200 +12622,Amulet of farming(8),200 +12623,Amulet of farming(8),200 +12624,Ogleroot,20 +12625,Flag,1 +12626,Test paper,2 +12627,Antique lamp,1 +12628,Antique lamp,1 +12629,Safety gloves,1 +12630,Rubium,0 +12631,Toy train,1 +12632,Note,0 +12633,Super fishing explosive,60 +12634,Chocatrice cape,1 +12635,Easter egg,1 +12636,Easter egg,1 +12637,Easter egg,1 +12638,Easter egg,1 +12639,Easter egg,1 +12640,Easter egg,1 +12641,Easter egg,1 +12642,Easter egg,1 +12643,Easter egg,1 +12644,Easter egg,1 +12645,Chocatrice cape,32 +12646,Chocolate egg,1 +12647,Chocolate egg,1 +12648,Chocolate egg,1 +12649,Bucket,1 +12650,Bucket of water,1 +12651,Bucket of water,1 +12652,Bucket of coal,1 +12653,Bucket of coal,1 +12654,Cockatrice egg,1 +12655,Chocatrice egg,1 +12656,Chocatrice object,1 +12657,Chocolate chunks,1 +12658,Adamant full helm (e),3520 +12659,Adamant full helm (charged),3520 +12660,Snakeskin bandana (e),300 +12661,Snakeskin bandana (charged),300 +12662,Splitbark helm (e),10000 +12663,Splitbark helm (charged),10000 +12664,Rune full helm (e),35200 +12665,Rune full helm (charged),35200 +12666,Dragon med helm (e),100000 +12667,Dragon med helm (charged),100000 +12668,Lunar helm (e),100 +12669,Lunar helm (charged),100 +12670,Armadyl helmet (e),20000 +12671,Armadyl helmet (charged),20000 +12672,Archer helm (e),78000 +12673,Archer helm (charged),78000 +12674,Berserker helm (e),78000 +12675,Berserker helm (charged),78000 +12676,Warrior helm (e),78000 +12677,Warrior helm (charged),78000 +12678,Farseer helm (e),78000 +12679,Farseer helm (charged),78000 +12680,Helm of neitiznot (e),50000 +12681,Helm of neitiznot (charged),50000 +12682,Baby monkey,1 +12683,Monkey,1 +12684,Baby monkey,1 +12685,Monkey,1 +12686,Baby monkey,1 +12687,Monkey,1 +12688,Baby monkey,1 +12689,Monkey,1 +12690,Baby monkey,1 +12691,Monkey,1 +12692,Baby monkey,1 +12693,Monkey,1 +12694,Baby monkey,1 +12695,Monkey,1 +12696,Baby monkey,1 +12697,Monkey,1 +12698,Baby monkey,1 +12699,Monkey,1 +12700,Terrier puppy,1 +12701,Terrier,1 +12702,Terrier puppy,1 +12703,Terrier,1 +12704,Greyhound puppy,1 +12705,Greyhound,1 +12706,Greyhound puppy,1 +12707,Greyhound,1 +12708,Labrador puppy,1 +12709,Labrador,1 +12710,Labrador puppy,1 +12711,Labrador,1 +12712,Dalmatian puppy,1 +12713,Dalmatian,1 +12714,Dalmatian puppy,1 +12715,Dalmatian,1 +12716,Sheepdog puppy,1 +12717,Sheepdog,1 +12718,Sheepdog puppy,1 +12719,Sheepdog,1 +12720,Bulldog puppy,1 +12721,Bulldog,1 +12722,Bulldog puppy,1 +12723,Bulldog,1 +12724,Raven chick,1 +12725,Raven,1 +12726,Raven chick,1 +12727,Raven,1 +12728,Raven chick,1 +12729,Raven,1 +12730,Raven chick,1 +12731,Raven,1 +12732,Raven chick,1 +12733,Raven,1 +12734,Baby raccoon,1 +12735,Raccoon,1 +12736,Baby raccoon,1 +12737,Raccoon,1 +12738,Baby gecko,1 +12739,Baby gecko,1 +12740,Baby gecko,1 +12741,Baby gecko,1 +12742,Gecko,1 +12743,Gecko,1 +12744,Gecko,1 +12745,Gecko,1 +12746,Baby giant crab,1 +12747,Giant crab,1 +12748,Baby giant crab,1 +12749,Giant crab,1 +12750,Baby giant crab,1 +12751,Giant crab,1 +12752,Baby giant crab,1 +12753,Giant crab,1 +12754,Baby squirrel,1 +12755,Squirrel,1 +12756,Baby squirrel,1 +12757,Squirrel,1 +12758,Baby squirrel,1 +12759,Squirrel,1 +12760,Baby squirrel,1 +12761,Squirrel,1 +12762,Penguin,1 +12763,Baby penguin,1 +12764,Penguin,1 +12765,Baby penguin,1 +12766,Vulture chick,1 +12767,Vulture,1 +12768,Vulture chick,1 +12769,Vulture,1 +12770,Vulture chick,1 +12771,Vulture,1 +12772,Vulture chick,1 +12773,Vulture,1 +12774,Vulture chick,1 +12775,Vulture,1 +12776,Swamp titan pouch,4174 +12777,Swamp titan pouch,4174 +12778,Spirit mosquito pouch,449 +12779,Spirit mosquito pouch,449 +12780,Void spinner pouch,2274 +12781,Void spinner pouch,2274 +12782,Forge regent pouch,3949 +12783,Forge regent pouch,3949 +12784,Spirit larupia pouch,4299 +12785,Spirit larupia pouch,4299 +12786,Geyser titan pouch,5974 +12787,Geyser titan pouch,5974 +12788,Lava titan pouch,5899 +12789,Lava titan pouch,5899 +12790,Steel titan pouch,4874 +12791,Steel titan pouch,4874 +12792,Obsidian golem pouch,5299 +12793,Obsidian golem pouch,5299 +12794,Talon beast pouch,4774 +12795,Talon beast pouch,4774 +12796,Abyssal titan pouch,3249 +12797,Abyssal titan pouch,3249 +12798,Void torcher pouch,2274 +12799,Void torcher pouch,2274 +12800,Giant chinchompa pouch,2524 +12801,Giant chinchompa pouch,2524 +12802,Fire titan pouch,5374 +12803,Fire titan pouch,5374 +12804,Moss titan pouch,5474 +12805,Moss titan pouch,5474 +12806,Ice titan pouch,5374 +12807,Ice titan pouch,5374 +12808,Spirit tz-kih pouch,2024 +12809,Spirit tz-kih pouch,2024 +12810,Spirit graahk pouch,4274 +12811,Spirit graahk pouch,4274 +12812,Spirit kyatt pouch,4249 +12813,Spirit kyatt pouch,4249 +12814,Void shifter pouch,2274 +12815,Void shifter pouch,2274 +12816,Pyrelord pouch,3199 +12817,Pyrelord pouch,3199 +12818,Void ravager pouch,2274 +12819,Void ravager pouch,2274 +12820,Ravenous locust pouch,2399 +12821,Ravenous locust pouch,2399 +12822,Iron titan pouch,5374 +12823,Iron titan pouch,5374 +12824,Titan's con. scroll,537 +12825,Steel of legends scroll,487 +12826,Volcanic str. scroll,529 +12827,Essence shipment scroll,324 +12828,Iron within scroll,537 +12829,Immense heat scroll,319 +12830,Famine scroll,239 +12831,Deadly claw scroll,477 +12832,Swamp plague scroll,417 +12833,Boil scroll,597 +12834,Explode scroll,252 +12835,Goad scroll,427 +12836,Ambush scroll,424 +12837,Ebon thunder scroll,529 +12838,Pester scroll,44 +12839,Fireball assault scroll,202 +12840,Rending scroll,429 +12841,Inferno scroll,394 +12842,Gnomecopter,0 +12843,Gnomecopter ticket,0 +12844,Toy kite,100 +12845,Stone of power,1 +12846,Stone of power,1 +12847,Stone of power,1 +12848,Stone of power,1 +12849,Stone of power,1 +12850,Elemental rune,2 +12851,Catalytic rune,2 +12852,Fist of guthix token,1 +12853,Bandages,1 +12854,null,1 +12855,Tele-orb,1 +12856,Irit gloves,1 +12857,Avantoe gloves,1 +12858,Kwuarm gloves,1 +12859,Cadantine gloves,1 +12860,Swordfish gloves,1 +12861,Shark gloves,1 +12862,Dragon slayer gloves,1 +12863,Air runecrafting gloves,1 +12864,Water runecrafting gloves,1 +12865,Earth runecrafting gloves,1 +12866,Battle hood 100,25000 +12867,Battle hood 80,25000 +12868,Battle hood 60,25000 +12869,Battle hood 40,25000 +12870,Battle hood 20,25000 +12871,Battle hood 0,25000 +12872,Battle hood 0,25000 +12873,Battle robe top 100,50000 +12874,Battle robe top 80,50000 +12875,Battle robe top 60,50000 +12876,Battle robe top 40,50000 +12877,Battle robe top 20,50000 +12878,Battle robe top 0,50000 +12879,Battle robe top 0,50000 +12880,Battle robe bottom 100,40000 +12881,Battle robe bottom 80,40000 +12882,Battle robe bottom 60,40000 +12883,Battle robe bottom 40,40000 +12884,Battle robe bottom 20,40000 +12885,Battle robe bottom 0,40000 +12886,Battle robe bottom 0,40000 +12887,Druidic mage hood 100,2000 +12888,Druidic mage hood 80,2000 +12889,Druidic mage hood 60,2000 +12890,Druidic mage hood 40,2000 +12891,Druidic mage hood 20,2000 +12892,Druidic mage hood 0,2000 +12893,Druidic mage hood 0,2000 +12894,Druidic mage top 100,6000 +12895,Druidic mage top 80,6000 +12896,Druidic mage top 60,6000 +12897,Druidic mage top 40,6000 +12898,Druidic mage top 20,6000 +12899,Druidic mage top 0,6000 +12900,Druidic mage top 0,6000 +12901,Druidic mage bottom 100,5000 +12902,Druidic mage bottom 80,5000 +12903,Druidic mage bottom 60,5000 +12904,Druidic mage bottom 40,5000 +12905,Druidic mage bottom 20,5000 +12906,Druidic mage bottom 0,5000 +12907,Druidic mage bottom 0,5000 +12908,Adamant spikeshield 100,4050 +12909,Adamant spikeshield 80,4050 +12910,Adamant spikeshield 60,4050 +12911,Adamant spikeshield 40,4050 +12912,Adamant spikeshield 20,4050 +12913,Adamant spikeshield 0,4050 +12914,Adamant spikeshield 0,4050 +12915,Adamant berserker shield 100,6140 +12916,Adamant berserker shield 80,6140 +12917,Adamant berserker shield 60,6140 +12918,Adamant berserker shield 40,6140 +12919,Adamant berserker shield 20,6140 +12920,Adamant berserker shield 0,6140 +12921,Adamant berserker shield 0,6140 +12922,Rune spikeshield 100,40000 +12923,Rune spikeshield 80,40000 +12924,Rune spikeshield 60,40000 +12925,Rune spikeshield 40,40000 +12926,Rune spikeshield 20,40000 +12927,Rune spikeshield 0,40000 +12928,Rune spikeshield 0,40000 +12929,Rune berserker shield 100,64400 +12930,Rune berserker shield 80,64400 +12931,Rune berserker shield 60,64400 +12932,Rune berserker shield 40,64400 +12933,Rune berserker shield 20,64400 +12934,Rune berserker shield 0,64400 +12935,Rune berserker shield 0,64400 +12936,Green d'hide coif 100,2000 +12937,Green d'hide coif 80,2000 +12938,Green d'hide coif 60,2000 +12939,Green d'hide coif 40,2000 +12940,Green d'hide coif 20,2000 +12941,Green d'hide coif 0,2000 +12942,Green d'hide coif 0,2000 +12943,Blue d'hide coif 100,3500 +12944,Blue d'hide coif 80,3500 +12945,Blue d'hide coif 60,3500 +12946,Blue d'hide coif 40,3500 +12947,Blue d'hide coif 20,3500 +12948,Blue d'hide coif 0,3500 +12949,Blue d'hide coif 0,3500 +12950,Red d'hide coif 100,3500 +12951,Red d'hide coif 80,3500 +12952,Red d'hide coif 60,3500 +12953,Red d'hide coif 40,3500 +12954,Red d'hide coif 20,3500 +12955,Red d'hide coif 0,3500 +12956,Red d'hide coif 0,3500 +12957,Black d'hide coif 100,5500 +12958,Black d'hide coif 80,5500 +12959,Black d'hide coif 60,5500 +12960,Black d'hide coif 40,5500 +12961,Black d'hide coif 20,5500 +12962,Black d'hide coif 0,5500 +12963,Black d'hide coif 0,5500 +12964,Combat hood 100,1000 +12965,Combat hood 80,1000 +12966,Combat hood 60,1000 +12967,Combat hood 40,1000 +12968,Combat hood 20,1000 +12969,Combat hood 0,1000 +12970,Combat hood 0,1000 +12971,Combat robe top 100,3000 +12972,Combat robe top 80,3000 +12973,Combat robe top 60,3000 +12974,Combat robe top 40,3000 +12975,Combat robe top 20,3000 +12976,Combat robe top 0,3000 +12977,Combat robe top 0,3000 +12978,Combat robe bottom 100,2500 +12979,Combat robe bottom 80,2500 +12980,Combat robe bottom 60,2500 +12981,Combat robe bottom 40,2500 +12982,Combat robe bottom 20,2500 +12983,Combat robe bottom 0,2500 +12984,Combat robe bottom 0,2500 +12985,Bronze gauntlets,100 +12986,Worn-out bronze gauntlets,100 +12987,Worn-out bronze gauntlets,100 +12988,Iron gauntlets,300 +12989,Worn-out iron gauntlets,300 +12990,Worn-out iron gauntlets,300 +12991,Steel gauntlets,1000 +12992,Worn-out steel gauntlets,1000 +12993,Worn-out steel gauntlets,1000 +12994,Black gauntlets,2000 +12995,Worn-out black gauntlets,2000 +12996,Worn-out black gauntlets,2000 +12997,Mithril gauntlets,3000 +12998,Worn-out mithril gauntlets,3000 +12999,Worn-out mithril gauntlets,3000 +13000,Adamant gauntlets,5000 +13001,Worn-out adamant gauntlets,5000 +13002,Worn-out adamant gauntlets,5000 +13003,Rune gauntlets,7500 +13004,Worn-out rune gauntlets,7500 +13005,Worn-out rune gauntlets,7500 +13006,Dragon gauntlets,50000 +13007,Worn-out dragon gauntlets,50000 +13008,Worn-out dragon gauntlets,50000 +13009,null,0 +13010,Clue scroll,1 +13011,Puzzle box,100 +13012,Clue scroll,1 +13013,Puzzle box,100 +13014,Clue scroll,1 +13015,Puzzle box,100 +13016,Clue scroll,1 +13017,Puzzle box,100 +13018,Clue scroll,1 +13019,Puzzle box,100 +13020,Clue scroll,1 +13021,Puzzle box,100 +13022,Clue scroll,1 +13023,Puzzle box,100 +13024,Clue scroll,1 +13025,Puzzle box,100 +13026,Clue scroll,1 +13027,Puzzle box,100 +13028,Clue scroll,1 +13029,Puzzle box,100 +13030,Clue scroll,1 +13031,Puzzle box,100 +13032,Clue scroll,1 +13033,Puzzle box,100 +13034,Clue scroll,1 +13035,Puzzle box,100 +13036,Clue scroll,1 +13037,Casket,50 +13038,Clue scroll,1 +13039,Casket,50 +13040,Clue scroll,1 +13041,Clue scroll,1 +13042,Clue scroll,1 +13043,Casket,50 +13044,Clue scroll,1 +13045,Casket,50 +13046,Clue scroll,1 +13047,Casket,50 +13048,Clue scroll,1 +13049,Clue scroll,1 +13050,Clue scroll,1 +13051,Clue scroll,1 +13052,Casket,50 +13053,Clue scroll,1 +13054,Casket,50 +13055,Clue scroll,1 +13056,Clue scroll,1 +13057,Casket,50 +13058,Clue scroll,1 +13059,Casket,50 +13060,Clue scroll,1 +13061,Clue scroll,1 +13062,Casket,50 +13063,Clue scroll,1 +13064,Casket,50 +13065,Clue scroll,1 +13066,Casket,50 +13067,Clue scroll,1 +13068,Clue scroll,1 +13069,Clue scroll,1 +13070,Clue scroll,1 +13071,Clue scroll,1 +13072,Clue scroll,1 +13073,Key,1 +13074,Clue scroll,1 +13075,Clue scroll,1 +13076,Clue scroll,1 +13077,Casket,50 +13078,Clue scroll,1 +13079,Clue scroll,1 +13080,Clue scroll,1 +13081,Black crossbow,800 +13082,Black crossbow,800 +13083,Black bolts,14 +13084,Black bolts(p),14 +13085,Black bolts(p+),14 +13086,Black bolts(p++),14 +13087,null,1 +13088,null,1 +13089,null,1 +13090,null,1 +13091,null,1 +13092,null,1 +13093,null,1 +13094,null,1 +13095,Black cane,432 +13096,Black cane,432 +13097,Adamant cane,1440 +13098,Adamant cane,1440 +13099,Rune cane,14400 +13100,Rune cane,14400 +13101,Top hat,200 +13102,Top hat,200 +13103,Pith helmet,250 +13104,Pith helmet,250 +13105,Spiked helmet,1000 +13106,Spiked helmet,1000 +13107,Sheep mask,800 +13108,Sheep mask,800 +13109,Penguin mask,800 +13110,Penguin mask,800 +13111,Bat mask,800 +13112,Bat mask,800 +13113,Cat mask,800 +13114,Cat mask,800 +13115,Wolf mask,800 +13116,Wolf mask,800 +13117,Ivandis flail (30),1000 +13118,Ivandis flail (29),1000 +13119,Ivandis flail(28),1000 +13120,Ivandis flail (27),1000 +13121,Ivandis flail (26),1000 +13122,Ivandis flail (25),1000 +13123,Ivandis flail (24),1000 +13124,Ivandis flail (23),1000 +13125,Ivandis flail (22),1000 +13126,Ivandis flail (21),1000 +13127,Ivandis flail (20),1000 +13128,Ivandis flail (19),1000 +13129,Ivandis flail (18),1000 +13130,Ivandis flail (17),1000 +13131,Ivandis flail (16),1000 +13132,Ivandis flail (15),1000 +13133,Ivandis flail (14),1000 +13134,Ivandis flail (13),1000 +13135,Ivandis flail (12),1000 +13136,Ivandis flail (11),1000 +13137,Ivandis flail (10),1000 +13138,Ivandis flail (9),1000 +13139,Ivandis flail (8),1000 +13140,Ivandis flail (7),1000 +13141,Ivandis flail (6),1000 +13142,Ivandis flail (5),1000 +13143,Ivandis flail (4),1000 +13144,Ivandis flail (3),1000 +13145,Ivandis flail (2),1000 +13146,Ivandis flail (1),1000 +13147,Flail dust,1 +13148,Glove,3 +13149,Book page,3 +13150,Crate,1 +13151,Combat book,3 +13152,null,3 +13153,Chain link mould,3 +13154,Silvthril chain,3 +13155,Silver sickle emerald(b),3 +13156,Enchanted sickle emerald(b),3 +13157,Vyre corpse,3 +13158,Columbarium key,3 +13159,Ornate tomb key,3 +13160,Tome of xp 2nd ed (3),1 +13161,Tome of xp 2nd ed (2),1 +13162,Tome of xp 2nd ed (1),1 +13163,Black cane,432 +13164,Adamant cane,1440 +13165,Rune cane,14400 +13166,Top hat,200 +13167,Pith helmet,250 +13168,Spiked helmet,1000 +13169,Sheep mask,800 +13170,Penguin mask,800 +13171,Bat mask,800 +13172,Cat mask,800 +13173,Wolf mask,800 +13174,Blue navy slacks,1 +13175,Green navy slacks,1 +13176,Red navy slacks,1 +13177,Brown navy slacks,1 +13178,Black navy slacks,1 +13179,Purple navy slacks,1 +13180,Grey navy slacks,1 +13181,Blue naval shirt,1 +13182,Green naval shirt,1 +13183,Red naval shirt,1 +13184,Brown naval shirt,1 +13185,Black naval shirt,1 +13186,Purple naval shirt,1 +13187,Grey naval shirt,1 +13188,Blue tricorn hat,1 +13189,Green tricorn hat,1 +13190,Red tricorn hat,1 +13191,Brown tricorn hat,1 +13192,Black tricorn hat,1 +13193,Purple tricorn hat,1 +13194,Grey tricorn hat,1 +13195,null,1 +13196,null,1 +13197,null,1 +13198,null,1 +13199,null,1 +13200,null,1 +13201,null,1 +13202,null,1 +13203,null,1 +13204,null,1 +13205,null,1 +13206,null,1 +13207,null,1 +13208,null,1 +13209,null,1 +13210,null,1 +13211,null,1 +13212,null,1 +13213,null,1 +13214,null,1 +13215,null,1 +13216,null,1 +13217,null,1 +13218,null,1 +13219,null,1 +13220,null,1 +13221,null,1 +13222,null,1 +13223,null,1 +13224,null,1 +13225,null,1 +13226,null,1 +13227,Mysterious lamp,1 +13228,Crate,1 +13229,Letter,1 +13230,Icefiend net,1 +13231,Baby icefiend,1 +13232,Tent,1 +13233,Plans,1 +13234,Dwarven key,1 +13235,null,1 +13236,Three little kittens,3 +13237,null,1 +13238,Treated oak plank,1 +13239,Chisel,1 +13240,.,1 +13241,Treated plank,1 +13242,.,1 +13243,Stone tablet,1 +13244,Tzhaar tourist guide,1 +13245,Stone slab,1 +13246,Pillar,1 +13247,Splat,1 +13248,Splat,1 +13249,Splat,1 +13250,Splat,1 +13251,null,1 +13252,null,1 +13253,null,1 +13254,null,1 +13255,null,1 +13256,null,1 +13257,null,1 +13258,null,1 +13259,Ranged path key,1 +13260,Magic path key,1 +13261,null,1 +13262,null,1 +13263,Slayer helmet,650 +13264,null,1 +13265,null,1 +13266,null,1 +13267,null,1 +13268,null,3 +13269,null,3 +13270,null,3 +13271,null,3 +13272,null,3 +13273,null,3 +13274,null,3 +13275,null,3 +13276,Banshee voice,1 +13277,Masked earmuffs,400 +13278,Broad arrow heads,50 +13279,Unfinished broad bolts,50 +13280,Broad-tipped bolts,50 +13281,Ring of slaying(8),150 +13282,Ring of slaying(7),150 +13283,Ring of slaying(6),150 +13284,Ring of slaying(5),150 +13285,Ring of slaying(4),150 +13286,Ring of slaying(3),150 +13287,Ring of slaying(2),150 +13288,Ring of slaying(1),150 +13289,null,3 +13290,Leaf-bladed sword,68000 +13291,Leaf-bladed sword,68000 +13292,Letter,0 +13293,Plans,0 +13294,Charcoal rubbing,0 +13295,Frank's mark,0 +13296,Crude lockpick,0 +13297,Customs shirt,0 +13298,Customs trousers,0 +13299,Customs hat,0 +13300,File,0 +13301,File,0 +13302,Brooch,0 +13303,Izzy's mark,0 +13304,Key,1 +13305,Crowbar,1 +13306,null,1 +13307,Idol of many heads,1 +13308,Bill teach's mark,0 +13309,Paper,0 +13310,Confession,0 +13311,Note,0 +13312,Ink bottle,0 +13313,Inky paper,0 +13314,Ink bottle,0 +13315,Smashed bottle,0 +13316,Prison uniform top,0 +13317,Prison uniform trousers,0 +13318,Fishy prison uniform top,0 +13319,Tin cup,0 +13320,Tin cup,0 +13321,Pipe,0 +13322,Accordion,0 +13323,Accordion,0 +13324,Accordion,0 +13325,Vacuum pump,0 +13326,Vacuum pump and gull,0 +13327,Quill,0 +13328,Inky quill,0 +13329,File,0 +13330,File,0 +13331,Ex-parrot,0 +13332,Magical cage,0 +13333,Ex-parrot in a magic cage,0 +13334,Ex-ex-parrot in a magic cage,0 +13335,Ex-ex-parrot,0 +13336,Ex-ex-parrot,0 +13337,Pirate impling jar,50 +13338,Pirate impling jar,50 +13339,Bandana and eyepatch,100 +13340,Bandana and eyepatches,100 +13341,Bandana and eyepatch,100 +13342,Bandana and eyepatches,100 +13343,Bandana and eyepatch,100 +13344,Bandana and eyepatches,100 +13345,Bandana and eyepatch,100 +13346,Bandana and eyepatches,100 +13347,Bandana and eyepatch,100 +13348,Bandana and eyepatches,100 +13349,Bandana and eyepatch,100 +13350,Bandana and eyepatches,100 +13351,Bandana and eyepatch,100 +13352,Bandana and eyepatches,100 +13353,Double eyepatches,100 +13354,Pirate hat and eyepatches,100 +13355,Left eyepatch,100 +13356,Left eyepatch,100 +13357,Pirate hat and eyepatch,100 +13358,Stripy pirate shirt,300 +13359,Stripy pirate shirt,300 +13360,Stripy pirate shirt,300 +13361,Stripy pirate shirt,300 +13362,Stripy pirate shirt,300 +13363,Stripy pirate shirt,300 +13364,Pirate leggings,350 +13365,Pirate leggings,350 +13366,Pirate leggings,350 +13367,Pirate leggings,350 +13368,Pirate leggings,350 +13369,Pirate leggings,350 +13370,Bandana,100 +13371,Bandana,100 +13372,Bandana,100 +13373,Bandana,100 +13374,Bandana,100 +13375,Bandana,100 +13376,Bandana and eyepatch,100 +13377,Bandana and eyepatch,100 +13378,Bandana and eyepatch,100 +13379,Rock fragments,0 +13380,Introduction letter,0 +13381,Broken hand,0 +13382,Broken hand,0 +13383,Broken hand,0 +13384,Broken hand,0 +13385,Broken hand,0 +13386,Fingers,0 +13387,Thumb,0 +13388,Hand,0 +13389,Wrist,0 +13390,Brace,0 +13391,Brass hand,0 +13392,Ink pad,0 +13393,Inky hand,0 +13394,Paper,0 +13395,Brass hand harry's mark,0 +13396,Fake beard,0 +13397,Fake moustache and nose,0 +13398,Fake monocle, moustache and nose,0 +13399,Curly wig,0 +13400,Straight wig,0 +13401,Order,0 +13402,Braindeath's mark,0 +13403,Golden tinderbox,300 +13404,null,1 +13405,Dark bow,0 +13406,Ancient staff,0 +13407,Archer helm,0 +13408,Berserker helm,0 +13409,Warrior helm,0 +13410,Farseer helm,0 +13411,Rock-shell helm,0 +13412,Rock-shell plate,0 +13413,Rock-shell legs,0 +13414,Spined helm,0 +13415,Spined body,0 +13416,Spined chaps,0 +13417,Skeletal helm,0 +13418,Skeletal top,0 +13419,Skeletal bottoms,0 +13420,Spined boots,0 +13421,Rock-shell boots,0 +13422,Skeletal boots,0 +13423,Spined gloves,0 +13424,Rock-shell gloves,0 +13425,Skeletal gloves,0 +13426,Seers ring,0 +13427,Archers ring,0 +13428,Warrior ring,0 +13429,Berserker ring,0 +13430,Dragon 2h sword,0 +13431,Crayfish cage,20 +13432,Crayfish cage,20 +13433,Crayfish,5 +13434,Crayfish,5 +13435,Raw crayfish,5 +13436,Raw crayfish,5 +13437,Burnt crayfish,1 +13438,Burnt crayfish,1 +13439,Antique lamp,1 +13440,Bag of runes,1 +13441,null,1 +13442,Amulet of fury,0 +13443,Obsidian cape,0 +13444,Abyssal whip,0 +13445,Granite maul,0 +13446,Antique lamp,1 +13447,Antique lamp,1 +13448,Antique lamp,1 +13449,Dragon full helm,0 +13450,Armadyl godsword,0 +13451,Bandos godsword,0 +13452,Saradomin godsword,0 +13453,Zamorak godsword,0 +13454,Zamorakian spear,0 +13455,Armadyl helmet,0 +13456,Armadyl chestplate,0 +13457,Armadyl plateskirt,0 +13458,Bandos chestplate,0 +13459,Bandos tassets,0 +13460,Bandos boots,0 +13461,Saradomin sword,0 +13462,Dragon boots,0 +13463,Antique lamp,1 +13464,Task list,1 +13465,Dragon dagger,0 +13466,Dragon dagger(p),0 +13467,Dragon dagger(p+),0 +13468,Dragon dagger(p++),0 +13469,Rune axe,0 +13470,Dragon axe,0 +13471,Rune battleaxe,0 +13472,Dragon battleaxe,0 +13473,Rune warhammer,0 +13474,Rune longsword,0 +13475,Dragon longsword,0 +13476,Rune scimitar,0 +13477,Dragon scimitar,0 +13478,Dragon halberd,0 +13479,Dragon mace,0 +13480,Rune pickaxe,0 +13481,Dragon chainbody,0 +13482,Rune platebody,0 +13483,Green d'hide body,0 +13484,Blue d'hide body,0 +13485,Red d'hide body,0 +13486,Black d'hide body,0 +13487,Rune platelegs,0 +13488,Dragon platelegs,0 +13489,Rune plateskirt,0 +13490,Dragon plateskirt,0 +13491,Green d'hide chaps,0 +13492,Blue d'hide chaps,0 +13493,Red d'hide chaps,0 +13494,Black d'hide chaps,0 +13495,Dragon med helm,0 +13496,Rune full helm,0 +13497,Green d'hide vamb,0 +13498,Blue d'hide vamb,0 +13499,Red d'hide vamb,0 +13500,Black d'hide vamb,0 +13501,Splitbark helm,0 +13502,Splitbark body,0 +13503,Splitbark legs,0 +13504,Splitbark gauntlets,0 +13505,Splitbark boots,0 +13506,Dragon sq shield,0 +13507,Rune kiteshield,0 +13508,Mystic hat,0 +13509,Mystic robe top,0 +13510,Mystic robe bottom,0 +13511,Mystic gloves,0 +13512,Mystic boots,0 +13513,Mystic hat,0 +13514,Mystic robe top,0 +13515,Mystic robe bottom,0 +13516,Mystic gloves,0 +13517,Mystic boots,0 +13518,Mystic hat,0 +13519,Mystic robe top,0 +13520,Mystic robe bottom,0 +13521,Mystic gloves,0 +13522,Mystic boots,0 +13523,Maple longbow,0 +13524,Maple shortbow,0 +13525,Yew longbow,0 +13526,Yew shortbow,0 +13527,Magic longbow,0 +13528,Magic shortbow,0 +13529,Seercull,0 +13530,Rune crossbow,0 +13531,Red partyhat,0 +13532,Yellow partyhat,0 +13533,Blue partyhat,0 +13534,Green partyhat,0 +13535,Purple partyhat,0 +13536,White partyhat,0 +13537,Santa hat,0 +13538,Green h'ween mask,0 +13539,Blue h'ween mask,0 +13540,Red h'ween mask,0 +13541,Willow comp bow,0 +13542,Yew comp bow,0 +13543,Magic comp bow,0 +13544,3rd age range top,0 +13545,3rd age range legs,0 +13546,3rd age range coif,0 +13547,3rd age vambraces,0 +13548,3rd age robe top,0 +13549,3rd age robe,0 +13550,3rd age mage hat,0 +13551,3rd age amulet,0 +13552,3rd age platelegs,0 +13553,3rd age platebody,0 +13554,3rd age full helmet,0 +13555,3rd age kiteshield,0 +13556,Ranger boots,0 +13557,Wizard boots,0 +13558,Robin hood hat,0 +13559,null,1 +13560,Explorer's ring 1,350 +13561,Explorer's ring 2,350 +13562,Explorer's ring 3,350 +13563,Button mushroom,38 +13564,Button mushroom,38 +13565,Doll,1 +13566,Jennica's ring,1 +13567,Cursed magic logs,1 +13568,Cursed magic logs,1 +13569,Enchanted key notes,1 +13570,Bowl,1 +13571,Bucket of milk,1 +13572,Clean guam,1 +13573,Bowl of honey,1 +13574,Bowl of milk and honey,1 +13575,Bowl of milk, honey and guam,1 +13576,Map of runescape,1 +13577,Papyrus,1 +13578,Charcoal,1 +13579,Spade,1 +13580,Plant pot,1 +13581,Pear tree seedling,1 +13582,Mango tree seedling,1 +13583,Quince tree seedling,1 +13584,Lemon tree seedling,1 +13585,Avocado tree seedling,1 +13586,Plum tree seedling,1 +13587,Pestle and mortar,4 +13588,Ground guam,1 +13589,Bucket,2 +13590,null,1 +13591,Enchanted key,1 +13592,A brooch,1 +13593,Old tome,1 +13594,null,1 +13595,null,1 +13596,null,1 +13597,null,1 +13598,Runecrafting guild teleport,100 +13599,Air altar teleport,100 +13600,Mind altar teleport,100 +13601,Water altar teleport,100 +13602,Earth altar teleport,100 +13603,Fire altar teleport,100 +13604,Body altar teleport,100 +13605,Cosmic altar teleport,100 +13606,Chaos altar teleport,100 +13607,Nature altar teleport,100 +13608,Law altar teleport,100 +13609,Death altar teleport,100 +13610,Blood altar teleport,100 +13611,Astral altar teleport,100 +13612,Runecrafter hat,20 +13613,Runecrafter hat,20 +13614,Runecrafter robe,75 +13615,Runecrafter hat,75 +13616,Runecrafter hat,75 +13617,Runecrafter skirt,75 +13618,Runecrafter gloves,75 +13619,Runecrafter robe,75 +13620,Runecrafter hat,75 +13621,Runecrafter hat,75 +13622,Runecrafter skirt,75 +13623,Runecrafter gloves,75 +13624,Runecrafter robe,75 +13625,Runecrafter hat,75 +13626,Runecrafter hat,75 +13627,Runecrafter skirt,75 +13628,Runecrafter gloves,75 +13629,Runecrafting staff,15 +13630,Air talisman staff,1500 +13631,Mind talisman staff,1500 +13632,Water talisman staff,1500 +13633,Earth talisman staff,1500 +13634,Fire talisman staff,1500 +13635,Body talisman staff,1500 +13636,Cosmic talisman staff,1500 +13637,Chaos talisman staff,1500 +13638,Nature talisman staff,1500 +13639,Law talisman staff,1500 +13640,Death talisman staff,1500 +13641,Blood talisman staff,1500 +13642,Omni-talisman staff,1500 +13643,Yellow attractor,20 +13644,Yellow repeller,20 +13645,Green attractor,20 +13646,Green repeller,20 +13647,Green barrier generator,20 +13648,Yellow barrier generator,20 +13649,Omni-talisman,4 +13650,Runecrafting guild token,0 +13651,Runecrafting guild token,0 +13652,Runecrafting guild token,0 +13653,Runecrafting guild token,0 +13654,Runecrafting guild token,0 +13655,Omni-tiara,100 +13656,Runecrafter hat,75 +13657,Runecrafter hat,75 +13658,Runecrafter hat,75 +13659,Ring of fire,100 +13660,Flame gloves,200 +13661,Inferno adze,300 +13662,Fire beacon,1 +13663,Circus ticket,10 +13664,null,10 +13665,null,10 +13666,Giant's hand,10 +13667,Clown hat,10 +13668,Clown shirt,10 +13669,Clown leggings,10 +13670,Clown shoes,10 +13671,Tambourine,10 +13672,Ringmaster hat,10 +13673,Ringmaster shirt,10 +13674,Ringmaster pants,10 +13675,Ringmaster boots,10 +13676,Mega-phonus,10 +13677,Acrobat shirt,10 +13678,Acrobat pants,10 +13679,Acrobat shoes,10 +13680,Acrobat hood,10 +13681,Acrobat shirt,10 +13682,Acrobat pants,10 +13683,Acrobat shoes,10 +13684,Acrobat hood,10 +13685,A stylish hat,10 +13686,A stylish hat,10 +13687,A stylish hat,10 +13688,A stylish hat,10 +13689,A stylish hat,10 +13690,A stylish hat,10 +13691,Shirt,10 +13692,Shirt,10 +13693,Shirt,10 +13694,Shirt,10 +13695,Shirt,10 +13696,Shirt,10 +13697,Leggings,10 +13698,Leggings,10 +13699,Leggings,10 +13700,Skirt,10 +13701,Skirt,10 +13702,Skirt,10 +13703,Shoes,10 +13704,Shoes,10 +13705,Shoes,10 +13706,Shoes,10 +13707,Tightrope 101,1 +13708,Tightrope 101,1 +13709,Balls (level 10 approx.),1 +13710,Plates (level 20 approx.),1 +13711,Eggs (level 30 approx.),1 +13712,Knives (level 40 approx.),1 +13713,Spades (level 50 approx.),1 +13714,Tuna (level 60 approx.),1 +13715,Soap (level 70 approx.),1 +13716,Cannon balls (level 80 approx.),1 +13717,Torches (level 90 approx.),1 +13718,Chinchompas (level 99 approx.),1 +13719,Performance shortbow,50 +13720,Performance knife,14 +13721,Performance throwing axe,4 +13722,Performance arrow,7 +13723,Teleport spell,100 +13724,Levitation spell,100 +13725,Alchemy spell,100 +13726,Elemental spell,100 +13727,Stardust,50 +13728,null,1 +13729,null,1 +13730,null,1 +13731,null,1 +13732,Spy notebook,1 +13733,null,1 +13734,Spirit shield,70000 +13735,Spirit shield,70000 +13736,Blessed spirit shield,1400000 +13737,Blessed spirit shield,1400000 +13738,Arcane spirit shield,2000000 +13739,Arcane spirit shield,2000000 +13740,Divine spirit shield,2000000 +13741,Divine spirit shield,2000000 +13742,Elysian spirit shield,2000000 +13743,Elysian spirit shield,2000000 +13744,Spectral spirit shield,2000000 +13745,Spectral spirit shield,2000000 +13746,Arcane sigil,750000 +13747,Arcane sigil,750000 +13748,Divine sigil,750000 +13749,Divine sigil,750000 +13750,Elysian sigil,750000 +13751,Elysian sigil,750000 +13752,Spectral sigil,750000 +13753,Spectral sigil,750000 +13754,Holy elixir,750000 +13755,Holy elixir,750000 +13756,Cursed willow logs,40 +13757,Hartwin,1 +13758,Grubby key,0 +13759,Scrap of paper,1 +13760,Bottle,0 +13761,Bottle of mist,0 +13762,List of elders,0 +13763,Cavalier and mask,0 +13764,Rune claws,0 +13765,Rune dagger,0 +13766,Rune dagger(p),0 +13767,Rune dagger(p+),0 +13768,Rune dagger(p++),0 +13769,Rune spear,0 +13770,Dragon spear,0 +13771,Rune spear(p),0 +13772,Dragon spear(p),0 +13773,Rune spear(p+),0 +13774,Dragon spear(p+),0 +13775,Rune spear(p++),0 +13776,Dragon spear(p++),0 +13777,Rune sword,0 +13778,Rune 2h sword,0 +13779,Rune halberd,0 +13780,Rune mace,0 +13781,Rune chainbody,0 +13782,Rune boots,0 +13783,Rune med helm,0 +13784,Granite helm,0 +13785,Granite body,0 +13786,Granite legs,0 +13787,Rune sq shield,0 +13788,Granite shield,0 +13789,A powdered wig,0 +13790,Flared trousers,0 +13791,Pantaloons,0 +13792,Sleeping cap,0 +13793,Rune cane,0 +13794,Top hat,0 +13795,Sheep mask,0 +13796,Penguin mask,0 +13797,Bat mask,0 +13798,Cat mask,0 +13799,Wolf mask,0 +13800,Rune platebody (g),0 +13801,Rune platelegs (g),0 +13802,Rune plateskirt (g),0 +13803,Rune full helm(g),0 +13804,Rune kiteshield (g),0 +13805,Rune platebody (t),0 +13806,Rune platelegs (t),0 +13807,Rune plateskirt (t),0 +13808,Rune full helm (t),0 +13809,Rune kiteshield (t),0 +13810,Highwayman mask,0 +13811,Blue beret,0 +13812,Black beret,0 +13813,White beret,0 +13814,Tan cavalier,0 +13815,Dark cavalier,0 +13816,Black cavalier,0 +13817,Red headband,0 +13818,Black headband,0 +13819,Brown headband,0 +13820,Zamorak platebody,0 +13821,Zamorak platelegs,0 +13822,Zamorak plateskirt,0 +13823,Zamorak full helm,0 +13824,Zamorak kiteshield,0 +13825,Saradomin platebody,0 +13826,Saradomin platelegs,0 +13827,Saradomin plateskirt,0 +13828,Saradomin full helm,0 +13829,Saradomin kiteshield,0 +13830,Guthix platebody,0 +13831,Guthix platelegs,0 +13832,Guthix plateskirt,0 +13833,Guthix full helm,0 +13834,Guthix kiteshield,0 +13835,Gilded platebody,0 +13836,Gilded platelegs,0 +13837,Gilded plateskirt,0 +13838,Gilded full helm,0 +13839,Gilded kiteshield,0 +13840,null,1 +13841,null,1 +13842,null,1 +13843,null,1 +13844,null,1 +13845,Brawling gloves (melee),50000 +13846,Brawling gloves (ranged),50000 +13847,Brawling gloves (magic),50000 +13848,Brawling gloves (prayer),50000 +13849,Brawling gloves (agility),50000 +13850,Brawling gloves (wc),50000 +13851,Brawling gloves (fm),50000 +13852,Brawling gloves (mining),50000 +13853,Brawling gloves (hunter),50000 +13854,Brawling gloves (thieving),50000 +13855,Brawling gloves (smithing),50000 +13856,Brawling gloves (fishing),50000 +13857,Brawling gloves (cooking),50000 +13858,Zuriel's robe top,500000 +13859,Zuriel's robe top,500000 +13860,Zuriel's robe top (deg),300000 +13861,Zuriel's robe bottom,500000 +13862,Zuriel's robe bottom,500000 +13863,Zuriel's robe bottom (deg),300000 +13864,Zuriel's hood,250000 +13865,Zuriel's hood,250000 +13866,Zuriel's hood (deg),150000 +13867,Zuriel's staff,300000 +13868,Zuriel's staff,300000 +13869,Zuriel's staff (deg),180000 +13870,Morrigan's leather body,500000 +13871,Morrigan's leather body,500000 +13872,Morrigan's leather body (deg),300000 +13873,Morrigan's leather chaps,500000 +13874,Morrigan's leather chaps,500000 +13875,Morrigan's leather chaps (deg),300000 +13876,Morrigan's coif,250000 +13877,Morrigan's coif,250000 +13878,Morrigan's coif (deg),150000 +13879,Morrigan's javelin,4000 +13880,Morrigan's javelin(p),10000 +13881,Morrigan's javelin(p+),10000 +13882,Morrigan's javelin(p++),10000 +13883,Morrigan's throwing axe,4000 +13884,Statius's platebody,500000 +13885,Statius's platebody,500000 +13886,Statius's platebody (deg),300000 +13887,Vesta's chainbody,500000 +13888,Vesta's chainbody,500000 +13889,Vesta's chainbody (deg),300000 +13890,Statius's platelegs,500000 +13891,Statius's platelegs,500000 +13892,Statius's platelegs (deg),300000 +13893,Vesta's plateskirt,500000 +13894,Vesta's plateskirt,500000 +13895,Vesta's plateskirt (deg),300000 +13896,Statius's full helm,250000 +13897,Statius's full helm,250000 +13898,Statius' full helm (deg),150000 +13899,Vesta's longsword,300000 +13900,Vesta's longsword,300000 +13901,Vesta's longsword (deg),180000 +13902,Statius's warhammer,300000 +13903,Statius's warhammer,300000 +13904,Statius' warhammer (deg),180000 +13905,Vesta's spear,300000 +13906,Vesta's spear,300000 +13907,Vesta's spear (deg),180000 +13908,Corrupt statius's platebody,125000 +13909,Corrupt statius's platebody,125000 +13910,Corrupt statius's platebody (deg),75000 +13911,Corrupt vesta's chainbody,125000 +13912,Corrupt vesta's chainbody,125000 +13913,Corrupt vesta's chainbody (deg),75000 +13914,Corrupt statius's platelegs,125000 +13915,Corrupt statius's platelegs,125000 +13916,Corrupt statius's platelegs (deg),75000 +13917,Corrupt vesta's plateskirt,125000 +13918,Corrupt vesta's plateskirt,125000 +13919,Corrupt vesta's plateskirt (deg),75000 +13920,Corrupt statius's full helm,50000 +13921,Corrupt statius's full helm,50000 +13922,Corrupt statius' full helm (deg),30000 +13923,Corrupt vesta's longsword,125000 +13924,Corrupt vesta's longsword,125000 +13925,C. vesta's longsword (deg),75000 +13926,Corrupt statius's warhammer,125000 +13927,Corrupt statius's warhammer,125000 +13928,C. statius's warhammer (deg),75000 +13929,Corrupt vesta's spear,125000 +13930,Corrupt vesta's spear,125000 +13931,Corrupt vesta's spear (deg),75000 +13932,Corrupt zuriel's robe top,125000 +13933,Corrupt zuriel's robe top,125000 +13934,Corrupt zuriel's robe top (deg),75000 +13935,Corrupt zuriel's robe bottom,125000 +13936,Corrupt zuriel's robe bottom,125000 +13937,Corrupt zuriel's robe bottom (deg),75000 +13938,Corrupt zuriel's hood,50000 +13939,Corrupt zuriel's hood,50000 +13940,Corrupt zuriel's hood (deg),30000 +13941,Corrupt zuriel's staff,125000 +13942,Corrupt zuriel's staff,125000 +13943,Corrupt zuriel's staff (deg),70000 +13944,Corrupt morrigan's leather body,125000 +13945,Corrupt morrigan's leather body,125000 +13946,Corrupt morrigan's leather body (deg),75000 +13947,Corrupt morrigan's leather chaps,125000 +13948,Corrupt morrigan's leather chaps,125000 +13949,Corrupt morrigan's leather chaps (deg),75000 +13950,Corrupt morrigan's coif,50000 +13951,Corrupt morrigan's coif,50000 +13952,Corrupt morrigan's coif (deg),30000 +13953,Corrupt morrigan's javelin,1000 +13954,C. morrigan's javelin (p),10000 +13955,C. morrigan's javelin (p+),10000 +13956,C. morrigan's javelin (p++),10000 +13957,C. morrigan's throwing axe,1000 +13958,Corrupt dragon chainbody,120000 +13959,Corrupt dragon chainbody,120000 +13960,Corrupt dragon chainbody (deg),72000 +13961,Corrupt dragon med helm,60000 +13962,Corrupt dragon med helm,60000 +13963,Corrupt dragon med helm (deg),36000 +13964,Corrupt dragon sq shield,120000 +13965,Corrupt dragon sq shield,120000 +13966,Corrupt dragon sq shield (deg),72000 +13967,Corrupt dragon plateskirt,120000 +13968,Corrupt dragon plateskirt,120000 +13969,Corrupt dragon plateskirt (deg),72000 +13970,Corrupt dragon platelegs,120000 +13971,Corrupt dragon platelegs,120000 +13972,Corrupt dragon platelegs (deg),72000 +13973,Corrupt dragon battleaxe,120000 +13974,Corrupt dragon battleaxe,120000 +13975,C. dragon battleaxe (deg),72000 +13976,Corrupt dragon dagger,30000 +13977,Corrupt dragon dagger,30000 +13978,C. dragon dagger (deg),18000 +13979,Corrupt dragon scimitar,100000 +13980,Corrupt dragon scimitar,100000 +13981,C. dragon scimitar (deg),60000 +13982,Corrupt dragon longsword,100000 +13983,Corrupt dragon longsword,100000 +13984,C. dragon longsword (deg),60000 +13985,Corrupt dragon mace,50000 +13986,Corrupt dragon mace,50000 +13987,Corrupt dragon mace (deg),30000 +13988,Corrupt dragon spear,40000 +13989,Corrupt dragon spear,40000 +13990,Corrupt dragon spear (deg),24000 +13991,null,3 +13992,null,3 +13993,null,3 +13994,null,3 +13995,null,3 +13996,null,3 +13997,null,3 +13998,null,3 +13999,null,3 +14000,null,3 +14001,null,3 +14002,null,3 +14003,null,3 +14004,null,3 +14005,null,3 +14006,null,3 +14007,null,3 +14008,null,3 +14009,null,3 +14010,null,3 +14011,null,3 +14012,null,3 +14013,null,3 +14014,null,3 +14015,null,3 +14016,null,3 +14017,null,3 +14018,null,3 +14019,null,3 +14020,null,3 +14021,null,3 +14022,null,3 +14023,null,3 +14024,null,3 +14025,null,3 +14026,null,3 +14027,null,3 +14028,null,3 +14029,null,3 +14030,null,3 +14031,null,3 +14032,null,3 +14033,null,3 +14034,null,3 +14035,null,3 +14036,null,3 +14037,null,3 +14038,null,3 +14039,null,3 +14040,null,3 +14041,null,3 +14042,null,3 +14043,null,3 +14044,null,3 +14045,null,3 +14046,null,3 +14047,null,3 +14048,null,3 +14049,null,3 +14050,null,3 +14051,null,3 +14052,null,3 +14053,null,3 +14054,null,3 +14055,null,3 +14056,Pvp worlds manual,5 +14057,Broomstick,25 +14058,Goulash,2 +14059,null,4 +14060,null,4 +14061,Magic unguent,2 +14062,Broom ointment,2 +14063,null,1 +14064,Newt,5 +14065,Newt label,1 +14066,Toad label,1 +14067,Newts and toads label,1 +14068,Betty's wand,15 +14069,Magic slate,2 +14070,Reptile,1 +14071,Blackbird,1 +14072,Bat,1 +14073,Spider,1 +14074,Rat,1 +14075,Snail,1 +14076,Warlock top,15 +14077,Warlock legs,15 +14078,Witch top,15 +14079,Witch skirt,15 +14080,Witch cloak,15 +14081,Warlock cloak,15 +14082,Popcorn ball,1 +14083,Chocolate drop,1 +14084,Wrapped candy,1 +14085,Armadyl communiqué,1 +14086,Witch top,15 +14087,Witch skirt,15 +14088,Witch cloak,15 +14089,Pet kitten,1 +14090,Pet cat,1 +14091,Lazy cat,1 +14092,Overgrown cat,1 +14093,Wily cat,1 +14094,Sacred clay platebody,43333 +14095,Sacred clay platelegs,42667 +14096,Sacred clay helm,23467 +14097,Sacred clay scimitar,17066 +14098,Volatile clay tool,21333 +14099,Volatile clay pickaxe,21333 +14100,Volatile clay hatchet,21333 +14101,Volatile clay harpoon,21333 +14102,Volatile clay butterfly net,21333 +14103,Volatile clay fletching knife,21333 +14104,Volatile clay hammer,21333 +14105,Volatile clay needle,21333 +14106,Proto-tool,21333 +14107,Sacred clay pickaxe,21333 +14108,Sacred clay hatchet,21333 +14109,Sacred clay harpoon,21333 +14110,Sacred clay butterfly net,1 +14111,Sacred clay fletching knife,21333 +14112,Sacred clay hammer,21333 +14113,Sacred clay needle,21333 +14114,Sacred clay robe top,43333 +14115,Sacred clay robe bottom,42667 +14116,Sacred clay hat,23467 +14117,Sacred clay staff,17066 +14118,Sacred clay body,43333 +14119,Sacred clay chaps,42667 +14120,Sacred clay coif,23467 +14121,Sacred clay bow,17066 +14122,Pickaxe (class 1),30 +14123,Pickaxe (class 1),30 +14124,Pickaxe (class 2),60 +14125,Pickaxe (class 2),60 +14126,Pickaxe (class 3),90 +14127,Pickaxe (class 3),90 +14128,Pickaxe (class 4),120 +14129,Pickaxe (class 4),120 +14130,Pickaxe (class 5),150 +14131,Pickaxe (class 5),150 +14132,Hatchet (class 1),30 +14133,Hatchet (class 1),30 +14134,Hatchet (class 2),60 +14135,Hatchet (class 2),60 +14136,Hatchet (class 3),90 +14137,Hatchet (class 3),90 +14138,Hatchet (class 4),120 +14139,Hatchet (class 4),120 +14140,Hatchet (class 5),150 +14141,Hatchet (class 5),150 +14142,Harpoon (class 1),30 +14143,Harpoon (class 1),30 +14144,Harpoon (class 2),60 +14145,Harpoon (class 2),60 +14146,Harpoon (class 3),90 +14147,Harpoon (class 3),90 +14148,Harpoon (class 4),120 +14149,Harpoon (class 4),120 +14150,Harpoon (class 5),150 +14151,Harpoon (class 5),150 +14152,Butterfly net (class 1),30 +14153,Butterfly net (class 1),30 +14154,Butterfly net (class 2),60 +14155,Butterfly net (class 2),60 +14156,Butterfly net (class 3),90 +14157,Butterfly net (class 3),90 +14158,Butterfly net (class 4),120 +14159,Butterfly net (class 4),120 +14160,Butterfly net (class 5),150 +14161,Butterfly net (class 5),150 +14162,Food (class 1),30 +14163,Food (class 1),30 +14164,Food (class 2),60 +14165,Food (class 2),60 +14166,Food (class 3),90 +14167,Food (class 3),90 +14168,Food (class 4),120 +14169,Food (class 4),120 +14170,Food (class 5),150 +14171,Food (class 5),150 +14172,Barrier (class 1),30 +14173,Barrier (class 1),30 +14174,Barrier (class 2),60 +14175,Barrier (class 2),60 +14176,Barrier (class 3),90 +14177,Barrier (class 3),90 +14178,Barrier (class 4),120 +14179,Barrier (class 4),120 +14180,Barrier (class 5),150 +14181,Barrier (class 5),150 +14182,Sacred clay (class 1),15 +14183,Sacred clay (class 1),15 +14184,Sacred clay (class 2),30 +14185,Sacred clay (class 2),30 +14186,Sacred clay (class 3),45 +14187,Sacred clay (class 3),45 +14188,Sacred clay (class 4),60 +14189,Sacred clay (class 4),60 +14190,Sacred clay (class 5),75 +14191,Sacred clay (class 5),75 +14192,Bow (class 1),30 +14193,Bow (class 1),30 +14194,Bow (class 2),60 +14195,Bow (class 2),60 +14196,Bow (class 3),90 +14197,Bow (class 3),90 +14198,Bow (class 4),120 +14199,Bow (class 4),120 +14200,Bow (class 5),150 +14201,Bow (class 5),150 +14202,Arrows (class 1),2 +14203,Arrows (class 2),4 +14204,Arrows (class 3),6 +14205,Arrows (class 4),8 +14206,Arrows (class 5),10 +14207,Prayer potion(5),150 +14208,Prayer potion(5),150 +14209,Prayer potion(4),120 +14210,Prayer potion(4),120 +14211,Prayer potion(3),90 +14212,Prayer potion(3),90 +14213,Prayer potion(2),60 +14214,Prayer potion(2),60 +14215,Prayer potion(1),30 +14216,Prayer potion(1),30 +14217,Energy potion (5),150 +14218,Energy potion (5),150 +14219,Energy potion (4),120 +14220,Energy potion (4),120 +14221,Energy potion (3),90 +14222,Energy potion (3),90 +14223,Energy potion (2),60 +14224,Energy potion (2),60 +14225,Energy potion (1),30 +14226,Energy potion (1),30 +14227,Super attack(5),150 +14228,Super attack(5),150 +14229,Super attack(4),120 +14230,Super attack(4),120 +14231,Super attack(3),90 +14232,Super attack(3),90 +14233,Super attack(2),60 +14234,Super attack(2),60 +14235,Super attack(1),30 +14236,Super attack(1),30 +14237,Super strength(5),150 +14238,Super strength(5),150 +14239,Super strength(4),120 +14240,Super strength(4),120 +14241,Super strength(3),90 +14242,Super strength(3),90 +14243,Super strength(2),60 +14244,Super strength(2),60 +14245,Super strength(1),30 +14246,Super strength(1),30 +14247,Ranging potion(5),150 +14248,Ranging potion(5),150 +14249,Ranging potion(4),120 +14250,Ranging potion(4),120 +14251,Ranging potion(3),90 +14252,Ranging potion(3),90 +14253,Ranging potion(2),60 +14254,Ranging potion(2),60 +14255,Ranging potion(1),30 +14256,Ranging potion(1),30 +14257,Defence potion(5),150 +14258,Defence potion(5),150 +14259,Defence potion(4),120 +14260,Defence potion(4),120 +14261,Defence potion(3),90 +14262,Defence potion(3),90 +14263,Defence potion(2),60 +14264,Defence potion(2),60 +14265,Defence potion(1),30 +14266,Defence potion(1),30 +14267,Magic potion(5),150 +14268,Magic potion(5),150 +14269,Magic potion(4),120 +14270,Magic potion(4),120 +14271,Magic potion(3),90 +14272,Magic potion(3),90 +14273,Magic potion(2),60 +14274,Magic potion(2),60 +14275,Magic potion(1),30 +14276,Magic potion(1),30 +14277,Summoning potion(5),150 +14278,Summoning potion(5),150 +14279,Summoning potion(4),120 +14280,Summoning potion(4),120 +14281,Summoning potion(3),90 +14282,Summoning potion(3),90 +14283,Summoning potion(2),60 +14284,Summoning potion(2),60 +14285,Summoning potion(1),30 +14286,Summoning potion(1),30 +14287,Scimitar (class 1),30 +14288,Scimitar (class 1),30 +14289,Scimitar (class 2),60 +14290,Scimitar (class 2),60 +14291,Scimitar (class 3),90 +14292,Scimitar (class 3),90 +14293,Scimitar (class 4),120 +14294,Scimitar (class 4),120 +14295,Scimitar (class 5),150 +14296,Scimitar (class 5),150 +14297,Dagger (class 1),30 +14298,Dagger (class 1),30 +14299,Dagger (class 2),60 +14300,Dagger (class 2),60 +14301,Dagger (class 3),90 +14302,Dagger (class 3),90 +14303,Dagger (class 4),120 +14304,Dagger (class 4),120 +14305,Dagger (class 5),150 +14306,Dagger (class 5),150 +14307,Warhammer (class 1),30 +14308,Warhammer (class 1),30 +14309,Warhammer (class 2),60 +14310,Warhammer (class 2),60 +14311,Warhammer (class 3),90 +14312,Warhammer (class 3),90 +14313,Warhammer (class 4),120 +14314,Warhammer (class 4),120 +14315,Warhammer (class 5),150 +14316,Warhammer (class 5),150 +14317,Robe top (class 1),30 +14318,Robe top (class 1),30 +14319,Robe top (class 2),60 +14320,Robe top (class 2),60 +14321,Robe top (class 3),90 +14322,Robe top (class 3),90 +14323,Robe top (class 4),120 +14324,Robe top (class 4),120 +14325,Robe top (class 5),150 +14326,Robe top (class 5),150 +14327,Robe bottom (class 1),30 +14328,Robe bottom (class 1),30 +14329,Robe bottom (class 2),60 +14330,Robe bottom (class 2),60 +14331,Robe bottom (class 3),90 +14332,Robe bottom (class 3),90 +14333,Robe bottom (class 4),120 +14334,Robe bottom (class 4),120 +14335,Robe bottom (class 5),150 +14336,Robe bottom (class 5),150 +14337,Hat (class 1),30 +14338,Hat (class 1),30 +14339,Hat (class 2),60 +14340,Hat (class 2),60 +14341,Hat (class 3),90 +14342,Hat (class 3),90 +14343,Hat (class 4),120 +14344,Hat (class 4),120 +14345,Hat (class 5),150 +14346,Hat (class 5),150 +14347,Platebody (class 1),30 +14348,Platebody (class 1),30 +14349,Platebody (class 2),60 +14350,Platebody (class 2),60 +14351,Platebody (class 3),90 +14352,Platebody (class 3),90 +14353,Platebody (class 4),120 +14354,Platebody (class 4),120 +14355,Platebody (class 5),150 +14356,Platebody (class 5),150 +14357,Platelegs (class 1),30 +14358,Platelegs (class 1),30 +14359,Platelegs (class 2),60 +14360,Platelegs (class 2),60 +14361,Platelegs (class 3),90 +14362,Platelegs (class 3),90 +14363,Platelegs (class 4),120 +14364,Platelegs (class 4),120 +14365,Platelegs (class 5),150 +14366,Platelegs (class 5),150 +14367,Helm (class 1),30 +14368,Helm (class 1),30 +14369,Helm (class 2),60 +14370,Helm (class 2),60 +14371,Helm (class 3),90 +14372,Helm (class 3),90 +14373,Helm (class 4),120 +14374,Helm (class 4),120 +14375,Helm (class 5),150 +14376,Helm (class 5),150 +14377,Staff (class 1),30 +14378,Staff (class 1),30 +14379,Staff (class 2),60 +14380,Staff (class 2),60 +14381,Staff (class 3),90 +14382,Staff (class 3),90 +14383,Staff (class 4),120 +14384,Staff (class 4),120 +14385,Staff (class 5),150 +14386,Staff (class 5),150 +14387,Cape,1 +14388,null,1 +14389,Cape,1 +14390,null,1 +14391,Leather body (class 1),30 +14392,Leather body (class 1),30 +14393,Leather body (class 2),60 +14394,Leather body (class 2),60 +14395,Leather body (class 3),90 +14396,Leather body (class 3),90 +14397,Leather body (class 4),120 +14398,Leather body (class 4),120 +14399,Leather body (class 5),150 +14400,Leather body (class 5),150 +14401,Chaps (class 1),30 +14402,Chaps (class 1),30 +14403,Chaps (class 2),60 +14404,Chaps (class 2),60 +14405,Chaps (class 3),90 +14406,Chaps (class 3),90 +14407,Chaps (class 4),120 +14408,Chaps (class 4),120 +14409,Chaps (class 5),150 +14410,Chaps (class 5),150 +14411,Coif (class 1),30 +14412,Coif (class 1),30 +14413,Coif (class 2),60 +14414,Coif (class 2),60 +14415,Coif (class 3),90 +14416,Coif (class 3),90 +14417,Coif (class 4),120 +14418,Coif (class 4),120 +14419,Coif (class 5),150 +14420,Coif (class 5),150 +14421,Clay deposit scroll,30 +14422,Sacred clay pouch (class 1),30 +14423,Sacred clay pouch (class 1),30 +14424,Sacred clay pouch (class 2),60 +14425,Sacred clay pouch (class 2),60 +14426,Sacred clay pouch (class 3),90 +14427,Sacred clay pouch (class 3),90 +14428,Sacred clay pouch (class 4),120 +14429,Sacred clay pouch (class 4),120 +14430,Sacred clay pouch (class 5),150 +14431,Sacred clay pouch (class 5),150 +14432,Null sacred clay,1 +14433,null,1 +14434,null,1 +14435,null,1 +14436,null,1 +14437,null,1 +14438,null,1 +14439,null,1 +14440,null,1 +14441,null,1 +14442,null,1 +14443,null,1 +14444,null,1 +14445,null,1 +14446,null,1 +14447,null,1 +14448,null,1 +14449,null,1 +14450,null,1 +14451,null,1 +14452,null,1 +14453,null,1 +14454,null,1 +14455,null,1 +14456,null,1 +14457,null,1 +14458,Vine flower,5 +14459,Unconscious broav,0 +14460,Dirty laundry,10 +14461,Waste-paper basket,1 +14462,Ruby key,1 +14463,Notes on pressure,1 +14464,Movario's notes (volume 1),0 +14465,Movario's notes (volume 2),1 +14466,Weight (1kg),1 +14467,Weight (2kg),1 +14468,Weight (5kg),1 +14469,Strange key loop,1 +14470,Strange key teeth,1 +14471,Dragonkin key,1 +14472,Ruined dragon armour lump,30000 +14473,Ruined dragon armour lump,30000 +14474,Ruined dragon armour slice,30000 +14475,Ruined dragon armour slice,30000 +14476,Ruined dragon armour shard,30000 +14477,Ruined dragon armour shard,30000 +14478,Blast fusion hammer,1 +14479,Dragon platebody,1760000 +14480,Dragon platebody,1760000 +14481,Dragon platebody,0 +14482,null,1 +14483,null,1 +14484,Dragon claws,135000 +14485,Dragon claws,135000 +14486,Dragon claws,0 +14487,Enriched snapdragon,1 +14488,Super truth serum,1 +14489,Suspect sketch,1 +14490,Elite black platelegs,65000 +14491,Elite black platelegs,65000 +14492,Elite black platebody,64000 +14493,Elite black platebody,64000 +14494,Elite black full helm,35200 +14495,Elite black full helm,35200 +14496,Cell key,1 +14497,Dagon'hai robe top,120000 +14498,Dagon'hai robe top,120000 +14499,Dagon'hai hat,15000 +14500,Dagon'hai hat,15000 +14501,Dagon'hai robe bottom,80000 +14502,Dagon'hai robe bottom,80000 +14503,Silif,1 +14504,Silif,1 +14505,Teleorb,1 +14506,Enriched snapdragon seed,1 +14507,Dolmen,1 +14508,Dolmen,1 +14509,Dolmen,1 +14510,Dolmen,1 +14511,Dolmen,1 +14512,Dolmen,1 +14513,Dolmen,1 +14514,Dolmen,1 +14515,Air key,1 +14516,Earth key,1 +14517,Fire key,1 +14518,Water key,1 +14519,Air key,1 +14520,Earth key,1 +14521,Fire key,1 +14522,Water key,1 +14523,null,1 +14524,null,1 +14525,Dagon'hai robes set,20 +14526,Dagon'hai robes set,20 +14527,Elite black armour set,20 +14528,Elite black armour set,20 +14529,Dragon plate armour set (l),20 +14530,Dragon plate armour set (l),20 +14531,Dragon plate armour set (sk),20 +14532,Dragon plate armour set (sk),20 +14533,Broav,0 +14534,Strange teleorb,1 +14535,null,1 +14536,Turkey book,1 +14537,Cornucopia,1 +14538,Cornucopia,1 +14539,Raw turkey,9 +14540,Cooked turkey,4 +14541,Burnt turkey,1 +14542,Raw turkey drumstick,9 +14543,Cooked turkey drumstick,4 +14544,Burnt turkey drumstick,1 +14545,null,1 +14546,null,1 +14547,null,1 +14548,null,1 +14549,null,1 +14550,null,1 +14551,null,1 +14552,null,1 +14553,null,1 +14554,null,1 +14555,null,1 +14556,null,1 +14557,null,1 +14558,null,1 +14559,null,1 +14560,null,1 +14561,null,1 +14562,null,1 +14563,null,1 +14564,null,1 +14565,null,1 +14566,null,1 +14567,null,1 +14568,null,1 +14569,null,1 +14570,Cornucopia,1 +14571,Fremennik sea boots 1,1 +14572,Fremennik sea boots 2,1 +14573,Fremennik sea boots 3,1 +14574,Antique lamp,1 +14575,Antique lamp,1 +14576,Antique lamp,1 +14577,Falador shield 1,100 +14578,Falador shield 2,200 +14579,Falador shield 3,300 +14580,Antique lamp,1000 +14581,Antique lamp,5000 +14582,Antique lamp,10000 +14583,White lily,1 +14584,Money crest voucher,500000 +14585,null,1 +14586,null,1 +14587,null,1 +14588,null,1 +14589,White lily seed,1250 +14590,Enchanted lyre(5),1000 +14591,Enchanted lyre(6),1000 +14592,Elite black platelegs,0 +14593,Elite black platebody,0 +14594,Elite black full helm,0 +14595,Santa costume top,1 +14596,Ice amulet,1 +14597,Stones,1 +14598,Enchanted stones,1 +14599,Ice amulet,1 +14600,Santa costume top,1 +14601,Santa costume top,1 +14602,Santa costume gloves,1 +14603,Santa costume legs,1 +14604,Santa costume legs,1 +14605,Santa costume boots,1 +14606,Cinnamon twigs,0 +14607,Sassafras twigs,0 +14608,Ailanthus twigs,0 +14609,Cedar twigs,0 +14610,Mastic twigs,0 +14611,Cinnamon weaving ribbon,0 +14612,Sassafras weaving ribbon,0 +14613,Ailanthus weaving ribbon,0 +14614,Cedar weaving ribbon,0 +14615,Mastic weaving ribbon,0 +14616,Phoenix quill,100 +14617,Phoenix quill,100 +14618,null,1 +14619,null,1 +14620,Pouch,1 +14621,Pouch,1 +14622,Rise from the ashes scroll,498 +14623,Phoenix pouch,4986 +14624,Phoenix pouch,4986 +14625,Pouch,1 +14626,Phoenix eggling,0 +14627,Phoenix eggling,0 +14628,null,1 +14629,Phoenix egg,0 +14630,Cracked phoenix egg,0 +14631,Seer's headband,1 +14632,Enhanced excalibur,200 +14633,Antique lamp,1 +14634,Antique lamp,1 +14635,Antique lamp,1 +14636,Slayer helmet (e),650 +14637,Slayer helmet (charged),650 +14638,Ring of wealth,17625 +14639,Ring of wealth,17625 +14640,Ring of wealth(1),17625 +14641,Ring of wealth(1),17625 +14642,Ring of wealth(2),17625 +14643,Ring of wealth(2),17625 +14644,Ring of wealth(3),17625 +14645,Ring of wealth(3),17625 +14646,Ring of wealth(4),17625 +14647,Ring of wealth(4),17625 +14648,Jangles the Monkey Backpack,1 +14649,Zanik ring,1 +14650,Wizard hat,1 +14651,Ancient blueprint,1 +14652,Ring of the Star Sprite,1 +14653,Ordinary chair,1 +14654,Staff of the raven,1500 +14655,Staff of the raven,1500 +14656,Staff of the raven,1500 diff --git a/Server/src/test/resources/cache b/Server/src/test/resources/cache new file mode 120000 index 000000000..7af909218 --- /dev/null +++ b/Server/src/test/resources/cache @@ -0,0 +1 @@ +../../../data/cache \ No newline at end of file diff --git a/Server/src/test/resources/test.conf b/Server/src/test/resources/test.conf new file mode 100644 index 000000000..e789bae24 --- /dev/null +++ b/Server/src/test/resources/test.conf @@ -0,0 +1,83 @@ +[server] +#Secret key - this is sent by the client during login. +#Client/Server MUST match or connection is refused. +secret_key = "2009scape_development" +write_logs = true +msip = "127.0.0.1" +noauth_default_admin = true #NOTE: If we are not using auth, this determines whether or not players are admins by default. + +[database] +database_name = "global" +database_username = "2009scapetest" +database_password = "2009scapetest" +database_address = "127.0.0.1" +database_port = "3306" + + +[world] +name = "2009Scape" +debug = true +dev = true +start_gui = false +daily_restart = true +#world number +world_id = "1" +country_id = "0" +members = true +#activity as displayed on the world list +activity = "2009Scape Classic." +pvp = 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 +#message of the week model ID, 0 for random +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 = "3094,3107,0" +#the location of home teleport +home_location = "3222,3218,0" +autostock_ge = false +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 = false +enable_global_chat = false +enable_castle_wars = false +personalized_shops = false + +[paths] +#path to the data folder, which contains the cache subfolder and such +data_path = "data" +#in the lines below, @data will be replaced with the value set for data_path +cache_path = "@data/cache" +store_path = "@data/serverstore" +save_path = "@data/players" +configs_path = "@data/configs" +#this is where economy/grand exchange data gets saved +grand_exchange_data_path = "@data/eco" +#path to file defining the rare drop table +rare_drop_table_path = "@data/configs/shared_tables/RDT.xml" +#path to file defining c.ele minor drop table +cele_drop_table_path = "@data/configs/shared_tables/CELEDT.xml" +#path to file defining the uncommon seed drop table +uncommon_seed_drop_table_path = "@data/configs/shared_tables/USDT.xml" +#path to file defining the herb drop table +herb_drop_table_path = "@data/configs/shared_tables/HDT.xml" +#path to file defining the gem drop table +gem_drop_table_path = "@data/configs/shared_tables/GDT.xml" +#path to file defining the rare seed drop table +rare_seed_drop_table_path = "@data/configs/shared_tables/RSDT.xml" +#path to file defining the allotment seed drop table +allotment_seed_drop_table_path = "@data/configs/shared_tables/ASDT.xml" +#path to file containing boot-time object changes +object_parser_path = "@data/ObjectParser.xml" +#path logs are written to +logs_path = "@data/logs" +bot_data = "@data/botdata" +eco_data = "@data/eco" diff --git a/Server/worldprops/default.conf b/Server/worldprops/default.conf new file mode 100644 index 000000000..308db7417 --- /dev/null +++ b/Server/worldprops/default.conf @@ -0,0 +1,172 @@ +[server] +#Log Level - the level of verbosity used for logs. +#"verbose" - ALL logs are shown +#"detailed" - FINE logs are hidden, which is generally bulk/debug info. +#"cautious" - FINE, INFO logs are hidden, meaning this level only shows warnings and errors. +#"silent" - FINE, INFO, WARN logs are hidden, meaning this level only shows errors. +log_level = "verbose" +#Secret key - this is sent by the client during login. +#Client/Server MUST match or connection is refused. +secret_key = "2009scape_development" +write_logs = true +msip = "127.0.0.1" +#preload the map (Increases memory usage by 2GB but makes game ticks smoother) +preload_map = false +#--------Note: If both of the below are false, no database is required to run the server.-------------- +#true = login requires password to be correct, passwords are hashed before stored. false = login does not care about the correctness of a password. +use_auth = false #NOTE: THIS MUST BE SET TO TRUE IN PRODUCTION! +#true - account data (credits, playtime, etc) is persisted, false - account data is purely temporary +#NOTE: this does not affect actual save data, like stats, inventory, etc. +persist_accounts = false #NOTE: THIS MUST BE SET TO TRUE IN PRODUCTION! +noauth_default_admin = true #NOTE: If we are not using auth, this determines whether or not players are admins by default. +#------------------------------------------------------------------------------------------------------ +#The limit on how many different accounts a player can log into per day. +daily_accounts_per_ip = 3 +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" +database_username = "root" +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 used for announcements of bots selling items on the GE +name_ge = "2009Scape" +debug = true +dev = true +start_gui = false +daily_restart = false +#world number +world_id = "1" +country_id = "0" +members = true +#activity as displayed on the world list +activity = "2009Scape Classic." +pvp = 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 +#message of the week model ID, 0 for random +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 = "3094,3107,0" +#the location of home teleport +home_location = "3222,3218,0" +autostock_ge = false +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 = 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 = 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 +data_path = "data" +#in the lines below, @data will be replaced with the value set for data_path +cache_path = "@data/cache" +store_path = "@data/serverstore" +save_path = "@data/players" +configs_path = "@data/configs" +#this is where economy/grand exchange data gets saved +grand_exchange_data_path = "@data/eco" +#path to file defining the rare drop table +rare_drop_table_path = "@data/configs/shared_tables/RDT.xml" +#path to file defining c.ele minor drop table +cele_drop_table_path = "@data/configs/shared_tables/CELEDT.xml" +#path to file defining the uncommon seed drop table +uncommon_seed_drop_table_path = "@data/configs/shared_tables/USDT.xml" +#path to file defining the herb drop table +herb_drop_table_path = "@data/configs/shared_tables/HDT.xml" +#path to file defining the gem drop table +gem_drop_table_path = "@data/configs/shared_tables/GDT.xml" +#path to file defining the rare seed drop table +rare_seed_drop_table_path = "@data/configs/shared_tables/RSDT.xml" +#path to file defining the allotment seed drop table +allotment_seed_drop_table_path = "@data/configs/shared_tables/ASDT.xml" +#path to file containing boot-time object changes +object_parser_path = "@data/ObjectParser.xml" +#path logs are written to +logs_path = "@data/logs" +bot_data = "@data/botdata" +eco_data = "@data/eco" diff --git a/Server/worldprops/default.json b/Server/worldprops/default.json deleted file mode 100644 index 9b14d3260..000000000 --- a/Server/worldprops/default.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "2009scape", - - "GameSettings": { - "debug": true, - "dev": true, - "startGui": false, - "worldID":"1", - "countryID": "0", - "activity": "2009scape classic.", - "pvpWorld": false, - "msip": "127.0.0.1", - "default_xp_rate": "5", - "allow_slayer_reroll": false, - "enable_default_clan": true, - "enable_bots": true, - "autostock_ge": true, - "allow_token_purchase": true, - "max_adv_bots": "0", - "message_of_the_week_identifier": "0", - "skillcape_perks": false, - "message_of_the_week_text": "Welcome to 2009Scape!
Nowin Technicolor!" - }, - - "ServerSettings": { - "writeLogs": true, - "max_players": "2000", - "max_npcs": "32000", - "new_player_location": "2524,5002,0", - "home_location": "3222,3218,0", - "data_path": "data", - "cache_path": "./data/cache", - "store_path": "./data/store", - "save_path": "./data/players", - "configs_path": "./data/configs", - "grand_exchange_data_path": "./data/eco/", - "rare_drop_table_path": "./data/RDT.xml", - "object_parser_path": "./data/ObjectParser.xml", - "scripts_path": "scripts/", - "dialogue_scripts_path": "scripts/dialogue/", - "logs_path": "data/logs/", - "bot_data_path": "data/botdata/", - "database_name": "global", - "database_username": "root", - "database_password": "", - "database_address": "127.0.0.1", - "database_port": "3306", - "ms_secret_key": "2009scape_development" - }, - - "PluginToggles": { - "Trivia": false, - "RPS": false, - "Boredom": false, - "ShootingStars": true, - "MysteryBox": true, - "PKZone": false, - "ObjectParser": true, - "BlackJack": false - } -} \ No newline at end of file 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 new file mode 100755 index 000000000..43f08b78b --- /dev/null +++ b/build @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +SCRIPT_DIR=$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P) + +CLEAN=0 +BUILD_GS=0 +SKIPTEST="" + +GS_SRC=$SCRIPT_DIR/Server + +_JAR_DIR="$SCRIPT_DIR/builddir" + +help() +{ + echo "Usage: $0 [-h] [-q] <-g|-c> [-o ]"; + echo " -h: Display this message."; + echo " -g: Build the game server."; + echo " -c: Clean previous build."; + echo " -o: Specify jar-file directory."; + echo " -q: Quick build - will skip tests."; +} + +error() +{ + echo "$1"; + exit 1; +} + +clean_gs() +{ + 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." +} + +build_gs() +{ + 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 "hqgc:o:d" arg; do + case $arg in + h) + help + exit 0 + ;; + g) + BUILD_GS=1 + ;; + c) + CLEAN=1 + ;; + o) + _JAR_DIR=${OPTARG} + ;; + d) + echo "_JAR_DIR=$_JAR_DIR" + ;; + q) + SKIPTEST="-DskipTests" + ;; + *) + error "Argument -$arg is not a known or valid argument." + ;; + esac +done + +if [ $BUILD_GS -eq 0 ] && [ $CLEAN -eq 0 ]; then + error "Need to either build or clean at least. See -h for details." +fi + +# Inside func is cd so mgmt prob fails if no condition +(( CLEAN )) && clean_gs + +# ----------- + +if [ -d "$_JAR_DIR" ]; then + rm -r "$_JAR_DIR" +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 +fi diff --git a/build-singleplayer.bat b/build-singleplayer.bat deleted file mode 100644 index 853633470..000000000 --- a/build-singleplayer.bat +++ /dev/null @@ -1,40 +0,0 @@ -@echo off - -mkdir Single-Player - -echo Building Client... -cd Client -call gradle jar -cd .. -copy /Y "Client\build\libs\client-1.0.0.jar" "Single-Player\client.jar" -echo. - -echo Building Management-Server... -cd Management-Server -call gradle jar -cd .. -copy /Y "Management-Server\build\libs\managementserver-1.0.0.jar" "Single-Player\ms.jar" -echo. - -echo Building Server... -cd Server -call gradle jar -cd .. -copy /Y "Server\build\libs\server-1.0.0.jar" "Single-Player\server.jar" -echo. - -echo Copying server data... -del /S /Q "Single-Player/data" -xcopy /E /I /Y "Server\data" "Single-Player\data" -xcopy /E /I /Y "Server\db_exports\*.sql" "Single-Player\data" -del /S /Q "Single-Player/worldprops" -xcopy /E /I /Y "Server\worldprops" "Single-Player\worldprops" -: Set Debug/Dev mode to false on single player server config -powershell -Command "(gc Single-Player\worldprops\default.json) -replace '\"debug\": true', '\"debug\": false' -replace '\"dev\": true', '\"dev\": false' | Out-File -Encoding Default Single-Player\worldprops\default.json" - -: Replace Live server addresses with localhost -powershell -Command "(gc Client\config.json) -replace 'play.2009scape.org', 'localhost' | Out-File -Encoding Default Single-Player\config.json" -echo. - -echo Done! -pause \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 86f984174..000000000 --- a/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' version '1.4.10' - id 'com.github.johnrengelman.shadow' version '6.0.0' -} - -allprojects { - apply plugin: 'idea' - apply plugin: 'java' - apply plugin: 'org.jetbrains.kotlin.jvm' - apply plugin: 'com.github.johnrengelman.shadow' - - group = 'org.rs09' - version = '1.0.0' - - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - - repositories { - mavenCentral() - maven { url 'https://jitpack.io' } - } - - dependencies { - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10' - implementation 'com.googlecode.json-simple:json-simple:1.1.1' - } - - compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - } - } -} \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100644 index dcdabc084..000000000 --- a/build.sh +++ /dev/null @@ -1,2 +0,0 @@ -cd Server -gradle jar 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/dumps/530/530_synth_debug_names.txt b/dumps/530/530_synth_debug_names.txt new file mode 100644 index 000000000..324fc9d85 --- /dev/null +++ b/dumps/530/530_synth_debug_names.txt @@ -0,0 +1,3816 @@ +# this list is sourced from the OSRS synth debug names/IDs. +# it only goes up to 3826, the exact point where OSRS diverged. +# see: quest_prefix_internal_names.txt to find content related to a quest. + +eyeglo_battle_fighting_1=0 +eyeglo_aspidistra=1 +eyeglo_battle_gnomes_scared=2 +eyeglo_battle_gnomes_1=3 +eyeglo_battle_gnomes_scared_running=4 +eyeglo_battle_gnomes_scared_2=5 +eyeglo_crystal_axe_recharge=6 +eyeglo_battle_goblins_1=7 +eyeglo_bowl_ring=8 +eyeglo_bowl_ring2=9 +eyeglo_coin=10 +eyeglo_creature_attack=11 +sword_hit2=12 +sword_hit2_mail=13 +steel_mail=14 +steel=15 +big_bar=16 +big_bar_mail=17 +swordclash4_mail=18 +dragon_armour=19 +dragon_mail=20 +swordclash4=21 +blade3_mail=22 +blade3=23 +leather_hit=24 +steel2=25 +steel2_mail=26 +swordclash3_mail=27 +blade2_mail=28 +blade2=29 +crystal_armour_4=30 +stone_armour1=31 +stone_armour2=32 +stone_armour3=33 +bamboo_door_close=34 +a_door_close=35 +a_door_open=36 +barrows_door_unlock=37 +bamboo_door_open=38 +bamboodoor_close=39 +bamboodoor_open=40 +barrows_door_open=41 +bookcase_open_and_close=42 +big_wooden_door_close=43 +big_wooden_door_open=44 +bigdoor_close=45 +bigdoor_open=46 +bone_door_close=47 +bone_door_open=48 +creakydoor_close=49 +casket_open=50 +chest_close=51 +chest_open=52 +coffin_close=53 +coffin_open=54 +small_creaky_door_close=55 +creakydoor_open=56 +cupboard_close=57 +cupboard_open=58 +curtain_open=59 +door_close=60 +door_creak=61 +door_open=62 +drawer_close=63 +drawer_open=64 +fire_door_pass=65 +gate_close=66 +gate_open=67 +grate_close=68 +grate_open=69 +iron_door_close=70 +iron_door_open=71 +irondoor_locked=72 +jar_open=73 +manhole_close=74 +manhole_open=75 +marble_door_close=76 +marble_door_open=77 +new_door_close=78 +new_door_open=79 +nicedoor_close=80 +nicedoor_open=81 +portcullis_close=82 +portcullis_open=83 +sliding_grate=84 +small_creaky_door_open=85 +stone_door=86 +stone_door_locked=87 +trap_door_close=88 +trap_door_open=89 +trapdoor_close=90 +trapdoor_open=91 +treedoor_close=92 +treedoor_open=93 +vent_door_open=94 +wardrobe_close=95 +wardrobe_open=96 +high_alchemy=97 +low_alchemy=98 +bind_impact=99 +bind_all=100 +bind_cast=101 +blood_barrage_impact=102 +blood_blitz_all=103 +blood_blitz_impact=104 +blood_burst_impact=105 +blood_cast=106 +blood_rush_all=107 +blood_rush_casting=108 +blood_rush_fail=109 +blood_rush_impact=110 +blood_rush_work=111 +blood_travel=112 +blood_travel_2=113 +bones_to_bananas_all=114 +charge_earth_orb=115 +charge_air_orb=116 +charge_fire_orb=117 +charge_water_orb=118 +confuse_cast_and_fire=119 +confuse_all=120 +confuse_hit=121 +crumble_cast_and_fire=122 +crumble_all=123 +crumble_hit=124 +curse_all=125 +curse_hit=126 +curse_cast_and_fire=127 +earthblast_cast_and_fire=128 +earthblast_hit=129 +earthbolt_cast_and_fire=130 +earthbolt_hit=131 +earthstrike_cast_and_fire=132 +earthstrike_hit=133 +earthwave_cast_and_fire=134 +earthwave_hit=135 +enchant_sapphire_amulet=136 +enchant_diamond_amulet=137 +enchant_diamond_ring=138 +enchant_dragon_amulet=139 +enchant_dragon_ring=140 +enchant_emerald_amulet=141 +enchant_emerald_ring=142 +enchant_onyx_amulet=143 +enchant_onyx_ring=144 +enchant_ruby_amulet=145 +enchant_ruby_ring=146 +enchant_sapphire_ring=147 +enfeeble_cast_and_fire=148 +enfeeble_all=149 +enfeeble_hit=150 +entangle_cast_and_fire=151 +entangle_all=152 +entangle_hit=153 +paralyze=154 +fireblast_cast_and_fire=155 +fireblast_hit=156 +firebolt_cast_and_fire=157 +firebolt_hit=158 +firebreath=159 +firestrike_cast_and_fire=160 +firestrike_hit=161 +firewave_cast_and_fire=162 +firewave_hit=163 +lesserdemon_fire_cast=164 +doubleheal=165 +heal=166 +selfheal=167 +ice_barrage_impact=168 +ice_blitz_impact=169 +ice_burst_impact=170 +ice_cast=171 +ice_magic_all=172 +ice_rush_impact=173 +magic_dart_hit=174 +shadow_barrage_impact=175 +shadow_blitz_impact=176 +shadow_burst_impact=177 +shadow_cast=178 +shadow_rush_impact=179 +smoke_barrage_impact=180 +smoke_blitz_impact=181 +smoke_burst_impact=182 +smoke_cast=183 +smoke_cast2=184 +smoke_rush_impact=185 +snare_all=186 +stun_all=187 +summon_npc=188 +summon_zombie=189 +superheat_all=190 +superheat_fail=191 +telegrab_all=192 +aide_teleport_chalk=193 +aide_teleport_book=194 +aide_teleport_portal=195 +aide_teleport_sitdown=196 +block_teleport=197 +quick_teleport=198 +tele_other_cast=199 +teleport_all=200 +teleport_reverse=201 +teleportblock_cast=202 +teleportblock_impact=203 +wilderness_teleport=204 +undead_rot=205 +vulnerability_all=206 +waterblast_cast_and_fire=207 +waterblast_hit=208 +waterbolt_cast_and_fire=209 +waterbolt_hit=210 +waterstrike_cast_and_fire=211 +waterstrike_hit=212 +waterwave_cast_and_fire=213 +waterwave_hit=214 +weaken_all=215 +windblast_cast_and_fire=216 +windblast_hit=217 +windbolt_cast_and_fire=218 +windbolt_hit=219 +windstrike_cast_and_fire=220 +windstrike_hit=221 +windwave_cast_and_fire=222 +windwave_hit=223 +zap=224 +darklight_weaken=225 +equip_magic=226 +spellfail=227 +wave_wand=228 +zamorak_flame=229 +abyssal_leech_death=230 +abyssal_leech_hit=231 +abyssal_leech_suck=232 +abyssal_walker_attack=233 +abyssal_walker_death=234 +abyssal_walker_hit=235 +fourface_attack=236 +fourface_death=237 +fourface_hit=238 +fourface_ranged=239 +underwater_mogre_attack=240 +mudskipper_attack=241 +mudskipper_death=242 +mudskipper_hit=243 +mugre_attack=244 +mugre_death=245 +mugre_hit=246 +underwater_mugre_attack=247 +underwater_mugre_death=248 +underwater_mugre_hit=249 +tzhaar_ket_attack_club=250 +tzhaar_hur_attack=251 +tzhaar_hur_death=252 +tzhaar_hur_hit=253 +tzhaar_ket_attack_multiclub=254 +tzhaar_ket_attack_punch=255 +tzhaar_ket_death=256 +tzhaar_ket_hit=257 +tzhaar_ket_hit_shield=258 +tzhaar_ket_hit_shield2=259 +tzhaar_mej_attack=260 +tzhaar_mej_attack_punch=261 +tzhaar_mej_attack_staff=262 +tzhaar_mej_death=263 +tzhaar_mej_hit=264 +tzhaar_staff=265 +tzhaar_xil_attack_blade=266 +tzhaar_xil_attack_fork=267 +tzhaar_xil_attack_multiblade=268 +tzhaar_xil_attack_ring=269 +tzhaar_xil_death=270 +tzhaar_xil_hit=271 +spectre_attack=272 +goo_hit=273 +spectre_death=274 +spectre_hit=275 +abyssal_attack=276 +abyssal_death=277 +abyssal_hit=278 +animated_death2=279 +animated_death=280 +animated_hit=281 +banshee_attack=282 +banshee_attack_earmuffs=283 +banshee_attack_normal=284 +banshee_death=285 +banshee_hit=286 +basilisk_attack=287 +basilisk_attack2=288 +basilisk_death=289 +basilisk_hit=290 +firebat_attack=291 +bat_attack=292 +bat_death=293 +bat_hit=294 +firebat_death=295 +firebat_hit=296 +anger_bear_attack=297 +anger_bear_death=298 +anger_bear_hit=299 +bear_attack=300 +bear_death=301 +bear_hit=302 +bearman_attack=303 +bearman_death=304 +bearman_hit=305 +bird_attack=306 +bird_death=307 +bird_hit=308 +seagull_attack=309 +seagull_death=310 +seagull_hit=311 +bloodveld_attack=312 +bloodveld_death=313 +bloodveld_hit=314 +large_boneguard_attack=315 +large_boneguard_death=316 +large_boneguard_hit=317 +small_boneguard_attack=318 +small_boneguard_death=319 +small_boneguard_hit=320 +boulder_rolling_loop=321 +boulder_loop=322 +brawler_attack=323 +brawler_cry=324 +brawler_death=325 +brawler_hit=326 +camel_disgruntled=327 +catablepon_attack=328 +catablepon_death=329 +catablepon_hit=330 +catablepon_hit2=331 +kitten_attack=332 +cat_attack=333 +cat_death=334 +cat_hiss=335 +cat_hit=336 +kitten_death=337 +kitten_hit=338 +kittens_mew=339 +purr=340 +cave_crawler_attack=341 +cave_crawler_death=342 +cave_crawler_hit=343 +chaos_elemental_confusion_cast=344 +chaos_elemental_attack=345 +chaos_elemental_confusion_impact=346 +chaos_elemental_death=347 +chaos_elemental_discord=348 +chaos_elemental_discord_cast=349 +chaos_elemental_discord_impact=350 +chaos_elemental_hit=351 +chaos_elemental_madness_cast=352 +chaos_elemental_madness_impact=353 +rubber_chicken_cluck=354 +chicken_attack=355 +chicken_death=356 +chicken_hit=357 +lay_egg=358 +chinchompa_attack=359 +chinchompa_explode=360 +chinchompa_hit=361 +chinchompa_trapped=362 +cockatrice_attack=363 +cockatrice_death=364 +cockatrice_hit=365 +calf_attack=366 +calf_death=367 +calf_hit=368 +cow_attack=369 +cow_death=370 +cow_hit=371 +milk_cow=372 +milk_cow2=373 +small_crab_attack=374 +small_crab_death=375 +small_crab_hit=376 +hand_attack=377 +hand_death=378 +hand_hit=379 +fake_creeper_death=380 +creeper_attack=381 +creeper_death=382 +creeper_hit=383 +creeper_impact=384 +creeper_spit=385 +crocodile_attack=386 +crocodile_death=387 +crocodile_hit=388 +dark_beast_attack=389 +dark_beast_death=390 +dark_beast_hit=391 +defiler_attack=392 +defiler_death=393 +defiler_hit=394 +defiler_spike=395 +demon_champion_attack=396 +black_demon_attack=397 +black_demon_death=398 +black_demon_hit=399 +demon_attack=400 +demon_champion_death=401 +demon_champion_hit=402 +demon_death=403 +demon_hit=404 +babydragon_attack=405 +babydragon_death=406 +babydragon_hit=407 +dragon_attack=408 +dragon_death=409 +dragon_hit=410 +duck_death=411 +duck_hit=412 +quack=413 +dust_devil_attack=414 +dust_devil_death=415 +dust_devil_hit=416 +dwarf_attack=417 +dwarf_death=418 +dwarf_hit=419 +dwarf_work=420 +eagle_attack=421 +eagle_attack2=422 +eagle_death=423 +eagle_hit=424 +elf_attack=425 +elf_death=426 +elf_hit=427 +gargoyle_attack=428 +gargoyle_death=429 +gargoyle_hit=430 +ghast_disappear=431 +ghast_appear=432 +ghast_attack=433 +ghast_death=434 +ghast_hit=435 +ghost_attack=436 +ghost_attack2=437 +ghost_death=438 +ghost_hit=439 +ghost_hit2=440 +ghoul_champion_attack=441 +ghoul_attack=442 +ghoul_death=443 +ghoul_hit=444 +giant_champion_attack=445 +earth_giant_attack=446 +fire_giant_attack=447 +giant_attack=448 +moss_giant_attack=449 +giant_death=450 +giant_hit=451 +gnome_glider_attack=452 +gnome_attack=453 +gnome_death=454 +gnome_hit=455 +gnome_tortoise_mounted_attack=456 +gnome_tortoise_attack=457 +gnome_tortoise_death=458 +gnome_tortoise_hit=459 +gnome_tortoise_mounted_death=460 +gnome_tortoise_mounted_hit=461 +anger_goblin_attack=462 +anger_goblin_death=463 +anger_goblin_hit=464 +cave_goblin_attack=465 +cave_goblin_death=466 +cave_goblin_hit=467 +goblin_armed=468 +goblin_attack=469 +goblin_champion_attack=470 +goblin_death=471 +goblin_hit=472 +hobgoblin_champion_hit=473 +golem_attack=474 +golem_death=475 +golem_hit=476 +gorilla_beat_loop=477 +gorilla_attack=478 +gorilla_beatchest=479 +gorilla_death=480 +gorilla_hit=481 +gorilla_hit2=482 +growl=483 +growl2=484 +growl3=485 +growl4=486 +growl5=487 +growl6=488 +half_werewolf_attack=489 +half_werewolf_death=490 +half_werewolf_hit=491 +hopeless_attack=492 +hopeless_death=493 +hopeless_hit=494 +hopeless_suck=495 +jungle_horror_attack=496 +cave_horror_howl=497 +jungle_horror_attack2=498 +jungle_horror_death=499 +jungle_horror_hit=500 +barbarian_female_hit=501 +barbarian_death=502 +human_underwater_death=503 +barbarian_hit=504 +female_death=505 +female_hit=506 +female_hit1=507 +female_hit2=508 +female_hit_1=509 +female_hit_2=510 +human_block_1=511 +human_death=512 +human_hit=513 +human_hit2=514 +human_hit3=515 +human_hit4=516 +human_hit5=517 +human_hit_1=518 +human_hit_2=519 +human_hit_3=520 +human_hit_4=521 +human_hit_6=522 +human_hit_7=523 +huntingbeast_death=524 +kebbit_attack=525 +kebbit_caught=526 +kebbit_death=527 +kebbit_hit=528 +ice_warrior_death=529 +ice_warrior_hit=530 +icefiend_attack=531 +icefiend_death=532 +icefiend_hit=533 +imp_attack=534 +imp_death=535 +imp_hit=536 +insect_attack=537 +insect_death=538 +insect_hit=539 +insect_trapped=540 +kilrag_attack=541 +kilrag_death=542 +kilrag_hit=543 +jackal_attack=544 +jackal_death=545 +jackal_hit=546 +jelly_attack=547 +jelly=548 +jelly_death=549 +jelly_hit=550 +kalphite_flyingqueen_attack=551 +kalphite_crack_open=552 +kalphite_flyingqueen_death=553 +kalphite_flyingqueen_hit=554 +kalphite_queen_spines=555 +kalthite_egg_crack=556 +kalthite_lightning=557 +kalthite_lightning_glow=558 +kalthite_lightning_impact=559 +kalthite_lord_attack=560 +kalthite_lord_death=561 +kalthite_lord_hit=562 +kalthite_queen_attack=563 +kalthite_queen_death=564 +kalthite_queen_hit=565 +kalthite_queen_hit2=566 +kalthite_soldier_attack=567 +kalthite_soldier_death=568 +kalthite_soldier_hit=569 +kalthite_squeal=570 +kalthite_worker_attack=571 +kalthite_worker_death=572 +kalthite_worker_hit=573 +mandibles=574 +killerwatt_ball_attack=575 +killerwatt_attack=576 +killerwatt_electrocutes=577 +killerwatt_ball_death=578 +killerwatt_ball_hit=579 +killerwatt_death=580 +killerwatt_electrocute=581 +killerwatt_transforms=582 +killerwatt_hit=583 +lightningbreath=584 +dragonbreath=585 +kingicebreath=586 +toxicbreath=587 +kurask_attack=588 +kurask_death=589 +kurask_hit=590 +lava_launch=591 +lava_hit=592 +lava_hit_2=593 +lava_launch_and_hit=594 +lavabeast_attack=595 +lavabeast_death=596 +lavabeast_hit=597 +magmaquiris_attack=598 +magmaquiris_death=599 +magmaquiris_hit=600 +magmaquiris_spines=601 +leech_death=602 +leech_hit=603 +leech_suck=604 +lizard_attack=605 +lizard_death=606 +lizard_hit=607 +lizard_cleric_attack=608 +lizard_cleric_death=609 +lizard_cleric_hit=610 +lizardman_attack=611 +lizardman_death=612 +lizardman_hit=613 +locust_attack=614 +locust_mage_attack=615 +locust_death=616 +locust_hit=617 +locust_mage_death=618 +locust_mage_hit=619 +locust_rider_death=620 +locust_rider_hit=621 +locust_rider_rear=622 +mammoth_attack=623 +mammoth_death=624 +mammoth_hit=625 +minotaur_attack=626 +minotaur_death=627 +minotaur_hit=628 +zombie_monkey_attack=629 +monkey_attack=630 +monkey_caught=631 +monkey_death=632 +monkey_escape=633 +monkey_hit=634 +zombie_monkey_death=635 +zombie_monkey_hit=636 +giant_mosquito_attack=637 +mummy_ash_death=638 +mummy_appear=639 +mummy_on_fire_attack=640 +mummy_attack=641 +mummy_death=642 +mummy_hit=643 +mummy_on_fire_death=644 +nechrayel_attack=645 +nechrayel_death=646 +nechrayel_hit=647 +orc_attack=648 +orc_death=649 +orc_hit=650 +nasty_tree_attack=651 +barklike=652 +birdlike=653 +cry1=654 +cry3=655 +goblin2=656 +horse_hit=657 +rock_spirit_appear=658 +rock_spirit=659 +scream2=660 +scream=661 +otherworld_attack=662 +otherworld_death=663 +otherworld_hit=664 +screech2=665 +panther_attack=666 +panther_death=667 +panther_hit=668 +penance_queen_range_impact=669 +penance_fighter_attack=670 +penance_fighter_death=671 +penance_fighter_hit=672 +penance_healer_heal=673 +penance_healer_poison=674 +penance_queen_attack=675 +penance_queen_death=676 +penance_queen_hit=677 +penance_queen_range=678 +penance_ranger_range_hit=679 +penance_ranger_attack=680 +penance_ranger_death=681 +penance_ranger_hit=682 +penance_spawn_death=683 +penance_spawn_spawn=684 +penance_spawn_hit=685 +penguinsheep_escape=686 +penguin_attack=687 +penguin_death=688 +penguin_hit=689 +pheasant_attack=690 +pheasant_death=691 +pheasant_hit=692 +pig_attack=693 +pig_death=694 +pig_hit=695 +pyrefiend_attack=696 +pyrefiend_death=697 +pyrefiend_hit=698 +rabbit_atmospheric=699 +rabbit_attack=700 +rabbit_death=701 +rabbit_hit=702 +anger_rat_attack=703 +anger_rat_death=704 +anger_rat_hit=705 +king_rat_attack=706 +king_rat_death=707 +king_rat_hiss=708 +king_rat_hit=709 +rat_attack=710 +rat_death=711 +rat_drown=712 +rat_hit=713 +ravager_attack=714 +ravager_death=715 +ravager_hit=716 +small_rock_crab_attack=717 +rock_crab_attack=718 +rock_crab_death=719 +rock_crab_hit=720 +underwater_rock_crab_attack=721 +small_rock_crab_death=722 +small_rock_crab_hit=723 +underwater_rock_crab_death=724 +underwater_rock_crab_hit=725 +rock_lobster_attack=726 +rock_lobster_death=727 +rock_lobster_hit=728 +rockslug_attack=729 +rockslug_death=730 +rockslug_hit=731 +equip_salamander=732 +salamander_attack=733 +salamander_belch=734 +salamander_blast=735 +salamander_blaze=736 +salamander_death=737 +salamander_flame=738 +salamander_hit=739 +salamander_scorch=740 +scarab_boss_attack=741 +scarab_boss_death=742 +scarab_boss_hit=743 +shade_appear=744 +shade_attack=745 +shade_death=746 +shade_hit=747 +shade_sigh=748 +shadow_beast_attack=749 +shadow_beast_death=750 +shadow_beast_hit=751 +shark_attack=752 +shark_death=753 +shark_hit=754 +angry_sheep=755 +cattleprod=756 +ram_attack=757 +ram_death=758 +ram_hit=759 +shear_once=760 +shear_sheep=761 +sheep_attack=762 +sheep_death=763 +sheep_hit=764 +sheepcall1=765 +shifter_attack=766 +shifter_death=767 +shifter_hit=768 +skwy_bite=769 +skwy_death=770 +skwy_hit=771 +skwy_ice=772 +skwy_tail=773 +armed_skeleton_hit=774 +armed_skeleton=775 +skeleton_attack=776 +skeleton_death=777 +skeletons_hiss=778 +skelly_hit=779 +slagilith_appear=780 +slagilith_attack=781 +slagilith_death=782 +slagilith_hit=783 +slime_attack=784 +slime_death=785 +slime_hit=786 +snail_death=787 +snail_spit2=788 +snail_hit=789 +snail_spit=790 +big_seasnake_attack=791 +big_seasnake_death=792 +big_seasnake_hit=793 +seasnake_attack=794 +seasnake_death=795 +seasnake_hit=796 +snake_attack=797 +snake_death=798 +snake_hiss=799 +snake_hit=800 +snaketest1=801 +sphinx_attack=802 +sphinx_death=803 +sphinx_hit=804 +spinner_attack=805 +spinner_death=806 +spinner_hit=807 +spinner_spin=808 +splatter_attack=809 +splatter_death=810 +splatter_hit=811 +stag_attack=812 +stag_death=813 +stag_hit=814 +sukqa_attack=815 +sukqa_death=816 +sukqa_hit=817 +swarm_appear=818 +swarm_attack=819 +swarm_death=820 +swarm_hit=821 +tanglewood_large_attack=822 +tanglewood_large_death=823 +tanglewood_large_hit=824 +tanglewood_small_attack=825 +tanglewood_small_death=826 +tanglewood_small_hit=827 +dragonkin_attack=828 +dragonkin_death=829 +dragonkin_hit=830 +terrorbird_appear=831 +terrorbird_attack=832 +terrorbird_death=833 +terrorbird_hit=834 +tiger_attack=835 +tiger_death=836 +tiger_hit=837 +giant_toad_attack=838 +giant_toad_death=839 +giant_toad_hit=840 +toad2=841 +toad_attack=842 +toad_death=843 +toad_hit=844 +torcher_attack=845 +torcher_death=846 +torcher_fire=847 +torcher_hit=848 +ice_troll_hit=849 +sea_troll_appear=850 +rock_impact=851 +sea_troll_queen_appear=852 +sea_troll_attack=853 +sea_troll_death=854 +sea_troll_general=855 +sea_troll_hit=856 +sea_troll_queen_attack=857 +sea_troll_queen_death=858 +sea_troll_queen_hit=859 +stone_impact=860 +troll_attack=861 +troll_champion_attack=862 +troll_champion_death=863 +troll_champion_hit=864 +troll_champion_swing=865 +troll_death=866 +troll_guard_attack=867 +troll_hit=868 +troll_snore=869 +troll_throw_rock=870 +troll_with_rock_attack=871 +trolls_shout=872 +turoth_attack=873 +turoth_death=874 +turoth_hit=875 +anger_unicorn_attack=876 +anger_unicorn_death=877 +anger_unicorn_hit=878 +vampire_attack=879 +flapping=880 +vampire_death_mist=881 +vampire_death=882 +vyrewatch_vampire_hit=883 +vampire_hit=884 +vampire_land=885 +vampire_suck=886 +vampire_weaken=887 +vyrewatch_vampire_attack=888 +vyrewatch_vampire_death=889 +vulture_attack=890 +vulture_death=891 +vulture_hit=892 +wall_beast_attack=893 +wall_beast_death=894 +wall_beast_foiled=895 +wall_beast_hit=896 +wall_beast_swipe=897 +water_creature_attack=898 +water_creature_death=899 +water_creature_hit=900 +water_creature_spines=901 +elemental_summon_skeleton=902 +elemental_summon_ghost=903 +elemental_summon_skeletonmage=904 +elemental_summon_zombie=905 +elemental_wizard_death=906 +human_into_mushroom=907 +mushroom_into_human=908 +wolf_attack=909 +wolf_attack2=910 +wolf_death=911 +wolf_hit=912 +zogre_axe_attack=913 +zogre_attack=914 +zogre_shield_hit=915 +zogre_death=916 +zogre_hit=917 +zombie_attack=918 +zombie2=919 +zombie_atmospheric1=920 +zombie_champion_attack=921 +zombie_death=922 +zombie_hit=923 +zombie_pirate_attack=924 +zombie_pirate_death=925 +zombie_pirate_hit=926 +zombie_pirate_limbfall=927 +zombie_pirate_punch=928 +npc_trapped=929 +poh_build_metal=930 +POH_man_trap=931 +bell_peal=932 +ping=933 +poh_bigcage=934 +poh_boil=935 +poh_oub_monster_attack=936 +poh_build_stone=937 +poh_build_wood=938 +poh_change_hair=939 +poh_clay_hit=940 +poh_clockwork=941 +poh_cloth=942 +poh_dart_land=943 +poh_dart_miss=944 +poh_dropcage=945 +poh_fairy_appear=946 +poh_forcedoor=947 +poh_hit_clay=948 +poh_hit_limestone=949 +poh_hit_marble=950 +poh_hoop_misses=951 +poh_jester_appear=952 +poh_limestone_hit=953 +poh_man_trap_avoid=954 +poh_marble_hit=955 +poh_marbles=956 +poh_marbles_avoid=957 +poh_offer_bones=958 +poh_pitfall_land_spikes=959 +poh_oub_monster_death=960 +poh_oub_monster_hit=961 +poh_pitfall_land_fire=962 +poh_pitfall_land_floor=963 +poh_pitfall_land_half=964 +poh_tablet_break_teleport=965 +poh_pitfall_land_water=966 +poh_portal_shrink=967 +poh_portal_unshrink=968 +poh_pugel=969 +poh_select=970 +poh_setup_stone=971 +poh_smallcage=972 +poh_sphere_balanced=973 +poh_sphere_powerdown=974 +poh_sphere_powerup=975 +poh_stick_on_hoop=976 +poh_stone_shatter=977 +poh_stonehit=978 +poh_tablet_break=979 +poh_tanglevine_dissappear=980 +poh_tanglevine=981 +poh_tentacle_dissappear=982 +poh_tanglevine_loop=983 +poh_teleport=984 +poh_tentacle=985 +poh_tentacle_appear=986 +poh_tentacle_hit=987 +poh_toy_fallover=988 +poh_wind=989 +poh_wrong=990 +100_jubbly_escape_work2=991 +100_jubbly_arrowhit=992 +100_jubbly_bounce=993 +100_jubbly_escape=994 +100_jubbly_escape_work3=995 +100_jubbly_escape_wprk=996 +100_jubbly_escapes=997 +100_ogre_swim=998 +100_rantz_kicks_tree=999 +100_underwater_passage=1000 +100_bubbles=1001 +100_divebomb=1002 +100_equip_claw=1003 +100_sink=1004 +100_iron_door_open_underwater=1005 +100_underwater_pick=1006 +100_fairy_dragon_appear=1007 +100_cat_into_hellcat=1008 +100_fairy_appear=1009 +100_fairy_disappear=1010 +100_fairy_dissappear=1011 +100_flambe=1012 +100_hellcat_into_cat=1013 +100_drop_rockcake=1014 +100_drop_coin=1015 +100_rockcake_burn_fingers=1016 +100_dwarf_cooking=1017 +100_eat_rockcake=1018 +100_sizzle_gloves=1019 +100_cauldron_outside_first=1020 +100_cauldron_explodes=1021 +100_cauldron_outside=1022 +100_cauldron_outside_second=1023 +100_cauldron_shake=1024 +100_cauldron_shake_initial=1025 +100_cauldron_shake_loop=1026 +100_cauldron_shake_loop2=1027 +100_goblin_falls=1028 +100_question_correct=1029 +100_egg_hatch=1030 +100_question_wrong=1031 +100_pull_tree=1032 +100_throw_rope=1033 +100_unfreeze=1034 +100_blowup=1035 +100_romancer_entangle=1036 +100_romancer_explode=1037 +100_summon=1038 +100_whiteout=1039 +PIN_pending=1040 +PIN_beep=1041 +PIN_cancel=1042 +PIN_pending2=1043 +barbassault_egg_explode=1044 +barbassault_fill_vial=1045 +barbassault_trap=1046 +barbassault_trapdoor=1047 +barbassault_turret_fire=1048 +BF_collect_coke=1049 +BF_conveyor_loop=1050 +BF_cooldown=1051 +BF_conveyor_loop_with_pedals=1052 +BF_drivebreaks=1053 +BF_oreinfurnace=1054 +BF_overheat=1055 +BF_pedal_loop=1056 +BF_pipebreaks=1057 +BF_pump_loop=1058 +BF_refuel_stove=1059 +BF_repair=1060 +BF_smelt=1061 +dagganoth_plate_body=1062 +dagganoth_egg_open=1063 +dagganoth_hide_hit=1064 +dagganoth_pressure_door_openandclose=1065 +dagganoth_pressure_door_fall=1066 +dagganoth_pressure_door_open=1067 +dagganoth_range_body=1068 +dagganoth_shell_body=1069 +dagganoth_splash=1070 +dagganoth_support_door=1071 +dagganoth_whirlpool=1072 +meganoth_death=1073 +meganoth_hit=1074 +meganoth_melee_attack=1075 +support_death=1076 +wallasalki_attack=1077 +wallasalki_death=1078 +wallasalki_hit=1079 +DTTD_bone_crossbow_sa=1080 +DTTD_bone_crossbow=1081 +DTTD_zanik_ladygoblin_attack=1082 +DTTD_bone_dagger_slash=1083 +DTTD_bone_dagger_stab=1084 +DTTD_drink=1085 +DTTD_zanik_ladygoblin_death=1086 +DTTD_zanik_ladygoblin_hit=1087 +branding_zanik=1088 +dttd_branding=1089 +dttd_branding_2=1090 +juna_meditates=1091 +pick_up_zaniks_body=1092 +smash_machine=1093 +zanik_gets_in_crate=1094 +zanik_resurrection=1095 +FT_chicken_into_human=1096 +FT_enchant_flower=1097 +FT_fairy_teleport=1098 +FT_female_to_male=1099 +FT_human_into_chicken=1100 +FT_human_into_piglet=1101 +FT_male_to_female=1102 +FT_piglet_into_human=1103 +FT_princess=1104 +FT_raincloud=1105 +FT_secateurs=1106 +FT_squeeze_past=1107 +female_to_male=1108 +forget_box_close=1109 +fall_off_chair=1110 +forget_box_open=1111 +forget_cart_move=1112 +forget_cart_turn=1113 +forget_foottramp=1114 +forget_glass_chink=1115 +forget_placestone=1116 +forget_shaman_all=1117 +forget_trackswitch=1118 +chesspiece_move=1119 +column_full=1120 +king=1121 +runelink_bottom=1122 +runelink_fall=1123 +GT_cart_bigpush=1124 +GT_cart_push=1125 +GT_cutscene_statue=1126 +GT_grind_rune=1127 +GT_shakegrass=1128 +GT_slap=1129 +GT_statue=1130 +grinder_grinding=1131 +fill_ectoplasm=1132 +fill_grinder=1133 +goobubble=1134 +goofade=1135 +grinder_empty=1136 +grinder_wind=1137 +stung=1138 +dwarf_spinning=1139 +clang1=1140 +clang2=1141 +dwarf_fillmachine=1142 +hammering1=1143 +hammering2=1144 +hammering3=1145 +steam_double=1146 +steam_filtered=1147 +steam_high=1148 +steam_low=1149 +steam_mid=1150 +steam_soft=1151 +steam_veryhigh=1152 +steamship=1153 +steamship_crashes=1154 +steamshunt=1155 +whistle1=1156 +whistle2=1157 +whistle3=1158 +collect_sap=1159 +boil_off=1160 +cat_angry=1161 +ics_spectre_appear=1162 +happy_meeoow=1163 +hypnotise=1164 +ics_spectre_attack=1165 +ics_spectre_death=1166 +ics_spectre_hit=1167 +ics_spectre_remove=1168 +meeoow=1169 +pet_cow=1170 +pit_fall=1171 +pit_jump=1172 +pit_jump_fall=1173 +pit_land=1174 +pit_shoot=1175 +sad_meeoow=1176 +scrape_glass=1177 +tile_move=1178 +tile_reset=1179 +vader_breath=1180 +wall_crushed=1181 +wall_crusher=1182 +grind_snail=1183 +acid_burn=1184 +skeletal_hellhound_hit=1185 +mist_appears=1186 +skeletal_hellhound_attack=1187 +skeletal_hellhound_death=1188 +summon_skeletalhound=1189 +vanstrom_appears=1190 +vanstrom_disappears=1191 +vanstrom_laughs=1192 +vanstrom_ldisappears=1193 +vanstrom_wings=1194 +carpet_descend=1195 +carpet_rise=1196 +history_key_burning=1197 +history_key_cold=1198 +history_key_freezing=1199 +history_key_hot=1200 +history_key_steaming=1201 +history_key_warm=1202 +mourning_bellowsfill=1203 +mourning_firegun=1204 +mourning_impact=1205 +mourning_impact_sheep=1206 +mourning_lightdoor=1207 +mourning_sneeze=1208 +mourning_tickle=1209 +mourning_toadfill=1210 +mourning_toadload=1211 +mourning_toadunload=1212 +squish_apples=1213 +NTK_door_with_mummy=1214 +NTK_chest_open=1215 +NTK_jar_with_snake=1216 +NTK_jar_open=1217 +NTK_sarcophagus_open=1218 +NTK_snake_charm=1219 +NTK_stone_door_locked=1220 +mousetrap_spring=1221 +create_smoke=1222 +poison_cheese=1223 +rat_lever=1224 +smoke_out_rats=1225 +smoke_rats=1226 +RT_unlock_casket=1227 +RT_picklock=1228 +RT_place_rune=1229 +RT_select_rune=1230 +RTplace_rune=1231 +rogue_gear_select=1232 +player_dive=1233 +rogue_timedblades_avoid=1234 +rogue_magic_door=1235 +rogue_pendulum_hit=1236 +rogue_placegear=1237 +rogue_safe_open=1238 +rogue_scythe_avoid=1239 +rogue_spinblades_2=1240 +rogue_squeeze=1241 +spring_floor=1242 +safe_crack=1243 +sos_deathdoor_close=1244 +sos_bigdoor=1245 +sos_choir=1246 +sos_creaks=1247 +sos_deathdoor_open_close=1248 +sos_famdoor_close=1249 +sos_famdoor_open_close=1250 +sos_pestdoor_close=1251 +sos_pestdoor_open_close=1252 +sos_sack=1253 +sos_wardoor_close=1254 +sos_wardoor_open=1255 +sos_wardoor_open_close=1256 +SE_remove_rock=1257 +SE_lassoo=1258 +SE_waterdoor_unlock=1259 +SE_waterpour=1260 +banana_slicing=1261 +beast_attack=1262 +beast_death=1263 +beast_hit=1264 +throwup1=1265 +TBCU_broodoo_hit=1266 +TBCU_broodoo=1267 +TBCU_diseasecure=1268 +TBCU_fencefail=1269 +TBCU_findgem=1270 +TBCU_mosquito=1271 +TBCU_mosquito_3=1272 +TBCU_mosquito_5=1273 +TBCU_prepare_wood=1274 +TBCU_repair_fence=1275 +TBCU_repair_middle=1276 +TBCU_repellant=1277 +TBCU_snake=1278 +TBCU_spider=1279 +TBCU_spider_stick=1280 +TBCU_tribesman=1281 +TBCU_tuber=1282 +TBCU_wooden_money=1283 +chippings=1284 +machete_fail=1285 +machete_slash=1286 +vegetation_shake=1287 +vegetation_sucess=1288 +fractured_light_beam=1289 +TL_mirrorfall_1=1290 +TL_mirrorfall_2=1291 +TL_mirrorfall_many=1292 +TL_place_crystal=1293 +TL_place_fractured=1294 +TL_place_mirror=1295 +TL_rebound=1296 +TL_rotate_crystal=1297 +TL_take_crystal=1298 +blue_light_beam=1299 +red_light_beam=1300 +green_light_beam=1301 +light_beam_start=1302 +orange_light_beam=1303 +purple_light_beam=1304 +white_light_beam_start=1305 +white_light_beam=1306 +yellow_light_beam=1307 +splash_and_river=1308 +bone_club=1309 +squeeze_through_rocks=1310 +agrith_portalclose=1311 +agrith_appear=1312 +agrith_circle=1313 +agrith_sandstorm=1314 +agrith_summon=1315 +dharok_axe_crush=1316 +ahrim_attack=1317 +ahrims_aura=1318 +dharok_attack=1319 +dharok_axe=1320 +dharok_axe_slash=1321 +flail=1322 +flail_crush=1323 +flail_stab=1324 +guthan_attack=1325 +guthan_breastplate=1326 +karil_attack=1327 +karil_spikebody=1328 +quarterstaff=1329 +torag_attack=1330 +torag_breastplate=1331 +torag_crush=1332 +verac_attack=1333 +verac_brassard=1334 +verac_crush=1335 +verac_slash=1336 +verac_stab=1337 +wight_death=1338 +wight_hit=1339 +wraith_death=1340 +iban lightning=1341 +barrel_incoming=1342 +catapult_fire=1343 +bigfire=1344 +crystal_armour_1=1345 +catapult_winch=1346 +coalfill=1347 +forest_doublesqueeze=1348 +crystal_armour_2=1349 +crystal_armour_3=1350 +crystal_bow=1351 +crystal_bow2=1352 +crystal_polearm=1353 +crystal_sword_1=1354 +crystal_sword_2=1355 +crystal_sword_3=1356 +crystal_sword_4=1357 +forest_lowwall=1358 +crystal_sword_initial_swipe=1359 +tripwire_with_arrows=1360 +forest_sidesqueeze=1361 +high_pressure=1362 +loom=1363 +low_pressure=1364 +mid_pressure=1365 +pressure_off=1366 +pressure_toohigh=1367 +springtrap=1368 +tripwire=1369 +valveturn=1370 +wom_bed_attack=1371 +wom_bed_death=1372 +wom_bed_hit=1373 +wom_bless=1374 +blade_swing_double=1375 +blade_hit=1376 +blade_swing=1377 +dart_fire_avoid=1378 +collapse_avoid=1379 +dart_fire=1380 +floor_spikes_avoid=1381 +dart_fire_hit=1382 +floor_spikes=1383 +roof_collapse=1384 +pillartag=1385 +saw_rise_and_spin=1386 +roof_collapse2=1387 +saw_fall=1388 +saw_hit=1389 +saw_rise=1390 +snap_and_fall=1391 +saw_spin=1392 +sliced=1393 +speartrap=1394 +pyramid_block=1395 +pyramid_scrape=1396 +pyramid_slip=1397 +skippy_throwglass=1398 +skippy_bucket=1399 +skippy_throwglass_impact=1400 +skippy_throwglass_throw=1401 +avatar_warrior_attack=1402 +avatar_mage_attack=1403 +avatar_warrior_death=1404 +demon_shakeroom=1405 +dwarf_kickrock=1406 +fire_jump=1407 +gold_cannonball=1408 +fly_and_land=1409 +rock_cannon_fire=1410 +light_cannon=1411 +paper_move=1412 +paper_turn=1413 +BK_cabbage_hit=1414 +BK_cauldron_bubbles=1415 +BK_cabbage_land=1416 +BK_explodsion=1417 +BK_explosion=1418 +BK_froth=1419 +BK_throw_cabbage=1420 +cf_swingacross_fail=1421 +cf_attach_cannon=1422 +cf_attach_fuse=1423 +cf_cannonfire=1424 +cf_cannonhit=1425 +cf_detach_cannon=1426 +cf_hammer=1427 +cf_jumpdown=1428 +cf_loadcannon=1429 +cf_pour_gunpowder=1430 +cf_ramrod=1431 +cf_roll_canister=1432 +cf_smear_paste=1433 +cf_splash=1434 +cf_strike=1435 +cf_swim=1436 +cf_swingacross=1437 +cf_swingacross_sucess=1438 +cf_tierope=1439 +castlewars_catapult_fire=1440 +castlewars_button=1441 +castlewars_catapult_destroyed=1442 +castlewars_catapult_repaired=1443 +castlewars_exploding_vial=1444 +castlewars_onfire=1445 +castlewars_putout=1446 +castlewars_setupbarricade=1447 +chompy_bird_screech=1448 +chompy_bird_attack=1449 +chompy_bird_death=1450 +chompy_bird_hit=1451 +ogre_bow=1452 +chompy_bird_squak=1453 +ogre_bellows_suck=1454 +ogre_bellows=1455 +spit_roast=1456 +toad_burst=1457 +toad_croak=1458 +toad_hiss=1459 +contact_darkness_impact=1460 +contact_archers_fire=1461 +contact_scarab_boss_dissappear=1462 +contact_extinguish_lights=1463 +contact_rumble=1464 +contact_rumble_long=1465 +contact_scarabs_disappear=1466 +contact_summon_scarabs=1467 +bookcasedoor=1468 +brushing=1469 +digspade=1470 +lightning=1471 +lightning2=1472 +unlock_and_move=1473 +dice_roll=1474 +dice_shake=1475 +place_crystal=1476 +pitfall=1477 +scarabs_appear=1478 +scarabs2=1479 +scarabs_attack2=1480 +scarabs_attack=1481 +scarabs_death=1482 +scarabs_hit=1483 +vampire_swipe=1484 +devious_grind=1485 +exploding_vial=1486 +explosion=1487 +fuse=1488 +bloom_branch=1489 +spirit_transform=1490 +bloom_mushroom=1491 +bloom_pears=1492 +cast_bloom=1493 +food_rot=1494 +spirit_transform_start=1495 +parrot1=1496 +dunk=1497 +parrot2=1498 +throw_club=1499 +washing=1500 +peak_giant_kebbit_appear=1501 +giant_kebbit_attack=1502 +giant_kebbit_death=1503 +giant_kebbit_hit=1504 +peak_bird_move=1505 +peak_bird_move_loop=1506 +peak_eagle_door=1507 +peak_empty_feeder=1508 +peak_fill_feeder=1509 +peak_nettrap_reset=1510 +peak_nettrap=1511 +peak_place_feather_rumble=1512 +peak_place_feather=1513 +peak_winch=1514 +broken_rune_altar_ambience=1515 +woodcutting_birdsnest=1516 +galvek_attack=1517 +vorkath_attack_2=1518 +vorkath_area=1519 +easter06_human_into_egg=1520 +vorkath_attack=1521 +vorkath_spawn=1522 +vorkath_death=1523 +galvek_air=1524 +Abo_death_2=1525 +Abo_death_1=1526 +air_elemental_attack=1527 +air_elemental_death=1528 +air_elemental_hit=1529 +big_bellows=1530 +earth_elemental_attack=1531 +earth_elemental_death=1532 +earth_elemental_hit=1533 +fire_elemental_attack=1534 +fire_elemental_death=1535 +fire_elemental_hit=1536 +lava_bowl_fill=1537 +lava_bowl_pour=1538 +shield_appear=1539 +sluice_gate=1540 +squeaky_valve=1541 +squeaky_valve_gush=1542 +water_elemental_attack=1543 +water_elemental_death=1544 +water_elemental_death2=1545 +water_elemental_hit=1546 +water_wheel_start=1547 +water_wheel_stop=1548 +enak_chisel_wall=1549 +enak_attach=1550 +enak_barrier=1551 +enak_blocked=1552 +enak_chisel=1553 +enak_place_clay=1554 +enak_door=1555 +enak_hiss=1556 +enak_interference_loop=1557 +enak_place_sigil=1558 +enak_push=1559 +enak_wind=1560 +enakh_statue=1561 +enakh_transform=1562 +enakh_wall_fall=1563 +chicken_into_human=1564 +ernest_clunk=1565 +ernest_sprinkle=1566 +end_whistle=1567 +catch=1568 +whistle=1569 +gnomeball_referee_gives_ball=1570 +gnomeball_tackled_gnome=1571 +gnomeball_tackled_gnome_miss=1572 +gnomeball_tackled_miss=1573 +gnomeball_tackled_player=1574 +score=1575 +throw=1576 +cave_bug_attack=1577 +cave_bug_death=1578 +cave_bug_death2=1579 +cave_bug_hit=1580 +insect_bites=1581 +insect_swarm=1582 +lantern_explodes=1583 +demon_approach=1584 +machine2=1585 +machinery=1586 +handsand_vialshatter=1587 +handsand_drop_hand=1588 +handsand_gulp=1589 +handsand_orb=1590 +handsand_sandswirl=1591 +handsand_serum=1592 +handsand_take_hand=1593 +crane_move_and_snap=1594 +bigghost_appear=1595 +bigghost_attack=1596 +bigghost_death=1597 +bigghost_disappear=1598 +bigghost_hit=1599 +bigghost_laugh=1600 +bigghost_reappear=1601 +cart_away=1602 +cart_hit=1603 +cart_into_turn=1604 +cart_loop=1605 +cart_loop_short=1606 +cart_turn=1607 +crane_jaws=1608 +crane_move=1609 +lift_fall=1610 +pickaxe_attack=1611 +points_change=1612 +shard_attack=1613 +shard_attack2=1614 +dagganoth_attack=1615 +dagganoth_critter_attack=1616 +dagganoth_changes=1617 +dagganoth_chitters=1618 +dagganoth_critter_death=1619 +dagganoth_critter_hit=1620 +dagganoth_death=1621 +dagganoth_hit=1622 +dagganoth_spines=1623 +meganoth_spines=1624 +strangedoor_close=1625 +strangedoor_open=1626 +strangedoor_sound=1627 +found_ice_arrows=1628 +fire_teleport=1629 +lava_bridge=1630 +lockeddoor=1631 +mizgog_placebeads=1632 +mizgog_beads=1633 +curse_lift=1634 +bling=1635 +bling2=1636 +bling3=1637 +bling4=1638 +summon_skeleton=1639 +bullroarer=1640 +mole_burrow_down=1641 +mole_attack=1642 +mole_burrow=1643 +mole_burrow_up=1644 +mole_death=1645 +mole_hit=1646 +mud_pie_impact=1647 +mud_pie_miss=1648 +mud_pie_throw=1649 +charge_gone=1650 +charge=1651 +claws_of_guthix_fail=1652 +claws_of_guthix=1653 +flames_of_zamorak_fail=1654 +flames_of_zamorak=1655 +saradomin_strike_fail=1656 +magical_barrier=1657 +pool_plop=1658 +saradomin_strike=1659 +mta_deposit_bone=1660 +mta_bonefall=1661 +mta_change=1662 +mta_deposit_fruit=1663 +mta_deposit_orb=1664 +mta_lock_statue=1665 +mta_move_statue=1666 +mcannon_fire=1667 +fire_loop_quiet=1668 +fire_loop=1669 +fire_loud_to_quiet=1670 +human_into_smallmonkey=1671 +fire_quiet_to_loud=1672 +gorilla_bigpunch=1673 +gorilla_into_human=1674 +heromonkey=1675 +human_into_gorilla=1676 +human_into_monkey=1677 +rumbling=1678 +ooks=1679 +human_into_zombiemonkey=1680 +monkey_into_human=1681 +monkeycalls=1682 +smallmonkey_into_human=1683 +ravinegate_open=1684 +rumbling_fade=1685 +rumbling_loop=1686 +skeleton_resurrect=1687 +snore=1688 +wing_unfold=1689 +zombiemonkey_into_human=1690 +moon_kick_letters=1691 +moon_babahouse=1692 +moon_dice_roll=1693 +moon_ejector=1694 +moon_fall_land=1695 +moon_hurdle=1696 +moon_jump=1697 +moon_press_number=1698 +moon_rubdown=1699 +moon_slip=1700 +moon_watering=1701 +moon_woodchop=1702 +place_stone=1703 +abseil=1704 +tree_climbing=1705 +polevault=1706 +scoopmud=1707 +split_rock=1708 +m2_fillcoal=1709 +m2_build=1710 +m2_rod_capture=1711 +m2_fillpot=1712 +m2_rod_of_command=1713 +m3_place_symbol=1714 +m3_electrocute=1715 +m3_kick_wall=1716 +m3_place_key=1717 +magic_dart_fire=1718 +m3_push_wall=1719 +m3_search_jars=1720 +m3_slash=1721 +m3_walk_rubble=1722 +m3_writing=1723 +animaterock_spell=1724 +fix_weathervane=1725 +water_boil=1726 +pest_portal_hit=1727 +pest_hammer=1728 +pest_portal_shrink=1729 +plague_pull_grill=1730 +plague_attach=1731 +plague_tomato=1732 +holy_water_pour=1733 +howl1=1734 +knock_knock=1735 +lycanthropy=1736 +rag_pour_vinegar=1737 +rag_boiling=1738 +rag_placelogs=1739 +rag_placepot=1740 +rag_put_in_pot=1741 +recruit_spade=1742 +RG_ghost_approach=1743 +RG_place_skull=1744 +RG_skeleton_attack=1745 +RG_skeleton_awake=1746 +crystal_tree_planting=1747 +royal_comedy_slip=1748 +royal_addrope=1749 +royal_build=1750 +royal_engine_chug=1751 +royal_engine_start=1752 +royal_footsteps=1753 +royal_steam=1754 +rumdeal_spirit_depart=1755 +deal_spirit_attack=1756 +deal_spirit_hit=1757 +kwamthulhu_walk=1758 +rumdeal_clank1=1759 +rumdeal_clank2=1760 +rumdeal_hiss1=1761 +rumdeal_hopper_1=1762 +rumdeal_pressurize=1763 +rumdeal_stuff_other=1764 +rumdeal_stuff_slugling=1765 +wrench_crush=1766 +hammer_and_build=1767 +brick_build=1768 +wall_destroy=1769 +hammering=1770 +log_down=1771 +oil_pour=1772 +bane_tolna_final_death=1773 +bane_attach_rope=1774 +bane_door_open=1775 +bane_fire_loop=1776 +bane_opening_close=1777 +bane_reaper_attack=1778 +bane_reaper_death=1779 +bane_reaper_hit=1780 +bane_summon_dragon=1781 +bane_tolna_attack=1782 +bane_tolna_death=1783 +bane_tolna_spit=1784 +bane_tolna_hit=1785 +swan_repair_wall=1786 +swan_kung_fu=1787 +swan_open_pot=1788 +swan_press=1789 +swan_rumble=1790 +swan_skeleton_rise=1791 +light_creature_down=1792 +collect_bad_tear=1793 +collect_good_tear=1794 +collect_tear=1795 +drink_and_magic=1796 +juna_hiss=1797 +light_creature_loop=1798 +light_creature_up=1799 +trek_bridge_placelog=1800 +trek_bog_leave=1801 +trek_bog_suck=1802 +trek_repair_bridge=1803 +trek_cutting=1804 +trek_heal_cast=1805 +trek_lasoo=1806 +trek_make_rope=1807 +trek_prod_firm=1808 +trek_prod_soft=1809 +trek_prod_suck=1810 +trek_tentacle_appear=1811 +trek_skin_snake=1812 +trek_swim_bog=1813 +trek_tentacle_head_appear=1814 +trek_walkaway=1815 +trek_walktoward=1816 +camelsnake=1817 +charmflute_a#=1818 +charmflute_a=1819 +charmflute_b=1820 +charmflute_c#=1821 +charmflute_c=1822 +charmflute_d#=1823 +charmflute_d=1824 +charmflute_e=1825 +charmflute_f#=1826 +charmflute_f=1827 +charmflute_g#=1828 +charmflute_g=1829 +charmflute_low_a#=1830 +charmflute_low_a=1831 +charmflute_low_b=1832 +charmflute_low_c#=1833 +charmflute_low_c=1834 +charmflute_low_d#=1835 +charmflute_low_d=1836 +charmflute_low_e=1837 +charmflute_low_f#=1838 +charmflute_low_f=1839 +charmflute_low_g#=1840 +charmflute_low_g=1841 +cudgel=1842 +drips=1843 +safeclang=1844 +safeclick=1845 +safeopen=1846 +snakehiss=1847 +golem_demondoor=1848 +golem_program=1849 +golem_repairclay=1850 +golem_teleport=1851 +turn_statue=1852 +parrotcall=1853 +kiss=1854 +penguin=1855 +writing=1856 +tradefloor_ore=1857 +leak=1858 +slide_puzzle=1859 +icegate_open=1860 +blizzard=1861 +windy=1862 +sled_start_longer=1863 +equip_sled=1864 +sled_jump=1865 +sled_loop=1866 +sled_loop2=1867 +sled_start=1868 +sled_stop=1869 +sledcrash=1870 +wax_sled=1871 +brew_conveyor_breaks=1872 +brew_collect_grubs=1873 +brew_conveyor=1874 +brew_release_pressure=1875 +brew_douse_flames=1876 +brew_drain_water=1877 +brew_fill_hopper=1878 +brew_monkey_climb=1879 +brew_monkey_combat=1880 +brew_monkey_talk=1881 +brew_monkeyclimb=1882 +brew_place_logs=1883 +brew_pump=1884 +brew_pump_single=1885 +brew_refuel_boiler=1886 +brew_refuel_empty=1887 +brew_strike_and_light=1888 +brew_repair_bridge=1889 +brew_repair_patch=1890 +brew_repair_pipe=1891 +brew_throw_coconut=1892 +twocats_shear=1893 +cut_coffin_vampire=1894 +garlic_grow=1895 +cutwrist=1896 +regular_vampire_appear=1897 +vampire_bats_appear=1898 +vampire_summon=1899 +wanted_scan=1900 +shotput_land=1901 +dust_hands=1902 +hiccup=1903 +warguild_animate_alive=1904 +shotput_spin_throw=1905 +shotput_step_throw=1906 +shotput_throw=1907 +take_shotput=1908 +warguild_animate=1909 +warguild_animator_activate=1910 +warguild_catapult_anvil=1911 +warguild_catapult=1912 +warguild_catapult_blades=1913 +warguild_catapult_ball=1914 +warguild_catapult_defend_anvil=1915 +warguild_catapult_defend_blades=1916 +warguild_catapult_defend_ball=1917 +warguild_catapult_defend_missile=1918 +warguild_catapult_missile=1919 +warguild_drop_barrels=1920 +warguild_drop_barrels_1=1921 +warguild_drop_barrels_2=1922 +warguild_dummies=1923 +warguild_dummies_down=1924 +warguild_dummies_up=1925 +warguild_iron_door_open=1926 +warguild_iron_door_push=1927 +warguild_take_barrel=1928 +warguild_take_barrel_grunt=1929 +smokepuff=1930 +smokepuff2=1931 +deathslide=1932 +bite=1933 +deathslide_first=1934 +deathslide_second=1935 +hurdle=1936 +skeletongoal=1937 +skullball_collide=1938 +skullball_roll=1939 +skullball_scratchy=1940 +skullball_tap=1941 +throw_stick=1942 +myq4_energy_2=1943 +Abo_hurt_2=1944 +myq4_energy_explosion=1945 +Abo_attack_1=1946 +Abo_drain_1=1947 +myq4_energy_1=1948 +Abo_attack_3=1949 +Abo_attack_2=1950 +Abo_attack_4=1951 +down_stone_stairs=1952 +bonewalk=1953 +ogre_destroy_barricade=1954 +drip_poison=1955 +up_stone_stairs=1956 +search_vine=1957 +zogre_writing=1958 +zogre_bell=1959 +adolescent_zygomite_attack=1960 +adolescent_zygomite_death=1961 +adolescent_zygomite_hit=1962 +zygo_grow=1963 +zygo_reload=1964 +zygo_spore_cast_and_fire=1965 +zygo_spore_impact=1966 +zygo_spray=1967 +zygomite_attack=1968 +zygomite_death=1969 +zygomite_hit=1970 +swordclash5=1971 +blade5=1972 +brassclash=1973 +dragon_shield=1974 +swordclash3=1975 +brassclash2=1976 +swordclash2=1977 +swordclash6=1978 +blade4=1979 +wood_armour3=1980 +eyeglo_creature_death=1981 +protect_items=1982 +eyeglo_creature_hit=1983 +bird_twitter_1=1984 +bird_twitter_2=1985 +bird_twitter_3=1986 +bird_twitter_4=1987 +bird_twitter_5=1988 +chick_cheep=1989 +cicadas_1=1990 +cicadas_2=1991 +crickets_1=1992 +crickets_2=1993 +croak_1=1994 +croak_2=1995 +croak_3=1996 +cuckoo_1=1997 +distant_bark_1=1998 +flapping_1=1999 +goblin_call_1=2000 +goblin_call_2=2001 +goblin_call_3=2002 +gull1=2003 +gull_1=2004 +gull_2=2005 +gull_3=2006 +gull_4=2007 +mosquitos_1=2008 +mosquitos_2=2009 +owl_part_1=2010 +owl_part_2=2011 +owl_part_3=2012 +peacock1=2013 +peacock2=2014 +pidgeon=2015 +screech_owl_1=2016 +screech_owl_2=2017 +toowit_1=2018 +warbler_1=2019 +warbler_2=2020 +bank_drawer=2021 +bank_bell=2022 +bank_shuffling_1=2023 +bank_locking=2024 +bank_shuffling_2=2025 +bank_stamp=2026 +bank_unlocking=2027 +battle_1=2028 +battle_2_troops=2029 +battle_1_test=2030 +battle_2=2031 +battle_4_terrorbird=2032 +battle_3_gnomes=2033 +battle_4_gnomes=2034 +battle_7_mediumfight=2035 +battle_5_birdkill=2036 +battle_6_archers=2037 +battle_7_smallfight=2038 +cave_collapse_1=2039 +cave_bats=2040 +cave_bubbling_loop_1=2041 +cave_growl_1=2042 +cave_insects_1=2043 +cave_insects_2=2044 +cave_rumbling_1=2045 +cave_rumbling_2=2046 +cave_rumbling_3=2047 +cave_rumbling_loop_1=2048 +cave_stalagtite_loop_1=2049 +stalagtite_drip=2050 +stalagtite_drip_2=2051 +stalagtite_drip_loop1=2052 +sheep_atmospheric1=2053 +clucks_1=2054 +clucks_2=2055 +cow_work=2056 +crows_1=2057 +crows_2=2058 +pig_call_1=2059 +pig_call_2=2060 +pig_call_3=2061 +sheep_atmospheric2=2062 +fire_crackling_fierce=2063 +fire_crackling_big=2064 +fire_crackling_small=2065 +fire_crackling_med=2066 +animal_growl_1=2067 +animal_growl_2=2068 +chipmunk=2069 +tree_creak_1=2070 +tree_creak_2=2071 +tree_creak_3=2072 +tree_rustle_1=2073 +tree_rustle_2=2074 +tree_wind_1=2075 +woodpecker=2076 +icy_wind_loop_1=2077 +icy_wind=2078 +icy_wind_loop_2=2079 +icy_wind_loop_3=2080 +wolfcall_1=2081 +wolfcall_2=2082 +jungle_ambience_loop=2083 +exotic_bird_1=2084 +exotic_bird_2=2085 +exotic_bird_3=2086 +exotic_bird_4=2087 +jungle_ambience_loop_1=2088 +jungle_insect_1=2089 +jungle_monkey_1=2090 +jungle_snake_1=2091 +kookaburra_1=2092 +parrot_1=2093 +parrot_2=2094 +warbler_3=2095 +hammer_and_smash=2096 +demolish_1=2097 +demolish_2=2098 +hammer_steel=2099 +hammer_stone=2100 +hammer_wood=2101 +hammering_1=2102 +hammering_2=2103 +hammering_3=2104 +sawing_1=2105 +sawing_2=2106 +snapwood_1=2107 +snapwood_2=2108 +thump_1=2109 +tidyup_1=2110 +tighten_1=2111 +coin_single_1=2112 +chopping=2113 +coins_jingle_1=2114 +coins=2115 +counting_coins_1=2116 +coins_jingle_2=2117 +coins_jingle_3=2118 +coins_jingle_4=2119 +market_ambience_1=2120 +morytania_creature_1=2121 +morytania_creature_2=2122 +morytania_creature_3=2123 +morytania_wind_loop_1=2124 +werewolf_call_1=2125 +werewolf_call_2=2126 +glass_break=2127 +glass_break2=2128 +glass_chink_1=2129 +glass_chink_2=2130 +glass_chink_3=2131 +pub_fillup=2132 +pub_jingle_1=2133 +pub_pourliquid=2134 +swamp_fart_1=2135 +swamp_fart_2=2136 +swamp_fart_3=2137 +swamp_gas=2138 +bubbling_low=2139 +bloop=2140 +underground_river_loop_1=2141 +bubbling_loop_1=2142 +bubbling_loop_2=2143 +bubbling_loop_3=2144 +bubbling_mid=2145 +bubbling_mid_loop=2146 +bubbling_stream_1=2147 +fish_splash=2148 +fountain_loop_1=2149 +fountain_loop_2=2150 +fountain_loop_3=2151 +high_drip=2152 +lapping_1=2153 +lapping_2=2154 +low_drip=2155 +mid2_drip=2156 +mid_drip=2157 +ocean_loop_1=2158 +ocean_loop_2=2159 +ocean_loop_3=2160 +ocean_wave1=2161 +ocean_wave2=2162 +ocean_wave3=2163 +ocean_wave4=2164 +ocean_wave5=2165 +river_loop_1=2166 +seashore_1=2167 +stream_loop_1=2168 +stream_loop_2=2169 +stream_loop_3=2170 +stream_loop_4=2171 +waterfall_loop_1=2172 +lava_flame_2=2173 +lava_flame=2174 +lava_gloop_loop_1=2175 +lava_flame_3=2176 +lava_gloop_loop_2=2177 +lava_gloop_loop_3=2178 +wilderness_wind_1=2179 +wilderness_wind_2=2180 +wolfcry_1=2181 +blustery_wind_loop_1=2182 +blustery_wind_loop_2=2183 +breeze_loop_1=2184 +breeze_loop_2=2185 +ocean_wind_1=2186 +ocean_wind_2=2187 +strong_wind_loop_1=2188 +fireplace_loop_1=2189 +ambient_bell_1=2190 +ambient_bell_2=2191 +bell=2192 +bell_peal_1=2193 +bubblin_loop=2194 +bubbling=2195 +bubbling4=2196 +bubbling_soup=2197 +cooking_loop_1=2198 +cooking_loop_2=2199 +cooking_loop_3=2200 +cosmic_star=2201 +droplet1=2202 +electric_drill=2203 +mid plop=2204 +fireplace_loop_2=2205 +foot_tramp_1=2206 +force_field_loop=2207 +furnace_loop_1=2208 +furnace_loop_2=2209 +furnace_loop_3=2210 +hi_plop=2211 +murmur_work=2212 +sea_bell_1=2213 +pop1=2214 +pop2=2215 +pour_sticky_liquid=2216 +sea_bell_2=2217 +sea_bell_3=2218 +windmill_blades_loop=2219 +spit_roast_loop=2220 +swamp=2221 +synth_crowd_1=2222 +tanning_vat=2223 +weak_distant_cry=2224 +windchimes_1=2225 +windmill_grindstones_loop=2226 +windmill_outside_loop=2227 +wood_creak_1=2228 +equip_axe=2229 +equip_elemental_staff=2230 +equip_banner=2231 +equip_baxe=2232 +equip_blunt=2233 +equip_body=2234 +equip_bolt=2235 +equip_hands=2236 +equip_feet=2237 +equip_fun=2238 +equip_metal_body=2239 +equip_helmet=2240 +equip_leather=2241 +equip_legs=2242 +equip_metal_legs=2243 +equip_ranged=2244 +equip_shield=2245 +equip_spiked=2246 +equip_staff=2247 +equip_sword=2248 +equip_whip=2249 +equip_wood=2250 +plate_break=2251 +human_roar=2252 +nethead=2253 +splitzombie_split=2254 +plate_spin=2255 +rubber_chicken_attack_faster=2256 +rubber_chicken_attack=2257 +scarekid=2258 +splitzombie_split_half=2259 +splitzombie_split_head=2260 +yoyo1=2261 +yoyo2=2262 +yoyo3=2263 +yoyo4=2264 +yoyo_wind=2265 +interface_select1=2266 +interface_wait=2267 +interface_wrong=2268 +interface_wrong2=2269 +twin_move_crane_loop=2270 +twin_crane_drop=2271 +twin_lower_crane=2272 +twin_move_crane=2273 +pillory_success=2274 +pillory_locked=2275 +pillory_unlock=2276 +pillory_wrong=2277 +pillartag_pinball=2278 +pinball_pillar1=2279 +pinball_pillar2=2280 +pinball_pillar3=2281 +pinball_pillar4=2282 +pinball_pillar5=2283 +troll_pinball=2284 +apple_splat=2285 +beehive_movepart=2286 +attach_handle=2287 +evil_chicken_attack=2288 +beehive_placepart=2289 +beehive_rotatepart=2290 +beehive_selectpart=2291 +candle_move=2292 +cat_into_hellcat=2293 +cavein=2294 +disgruntled_anvil=2295 +dryad_appear=2296 +dwarf_whistle=2297 +hypnotist_appear=2298 +gas_explosion=2299 +gas_hiss=2300 +genie_appear=2301 +hand_wave=2302 +piratechest_coins=2303 +knife_break=2304 +light_candle=2305 +lost_pickaxe=2306 +piratechest_turn=2307 +piratechest_unlock=2308 +quiz_correct=2309 +quiz_select=2310 +rake_breaks=2311 +sourhog_hit=2312 +sourhog_attack_ranged=2313 +sourhog_attack_melee=2314 +sourhog_death_cry=2315 +tanglevine_appear=2316 +sourhog_death_collapse=2317 +haloween_2020_pumpkin_head=2318 +tremor=2319 +triffid_appear=2320 +triffid_attack=2321 +uncooking=2322 +whirlpool=2323 +zombie_moan=2324 +ew2_extractor_loop2=2325 +ew2_dial_spin=2326 +dagganoth_plate_body_1=2327 +dagganoth_plate_body_2=2328 +dagganoth_plate_body_3=2329 +trailblazer_unlock_raise=2330 +trailblazer_unlock_twunkles=2331 +earth_wave_all=2332 +earth_wave_fail=2333 +dagganoth_plate_body_4=2334 +dagganoth_plate_body_5=2335 +fire_blast_all=2336 +dagganoth_range_body_1=2337 +dagganoth_range_body_2=2338 +dagganoth_range_body_3=2339 +fire_bolt_all=2340 +dagganoth_range_body_4=2341 +dagganoth_range_body_5=2342 +dagganoth_shell_body_1=2343 +trailblazer_unlock_power=2344 +dagganoth_shell_body_2=2345 +trailblazer_teleport_draw_symbol=2346 +dagganoth_shell_body_3=2347 +fire_wave_all=2348 +dagganoth_shell_body_4=2349 +fire_wave_fail=2350 +dagganoth_shell_body_5=2351 +guthan_breastplate_1=2352 +trailblazer_unlock_map=2353 +guthan_breastplate_2=2354 +guthan_breastplate_3=2355 +guthan_breastplate_4=2356 +guthan_breastplate_5=2357 +karil_spikebody_1=2358 +karil_spikebody_2=2359 +karil_spikebody_3=2360 +water_strike_fail=2361 +water_wave_all=2362 +water_wave_fail=2363 +karil_spikebody_4=2364 +karil_spikebody_5=2365 +torag_breastplate_1=2366 +torag_breastplate_2=2367 +torag_breastplate_3=2368 +torag_breastplate_4=2369 +torag_breastplate_5=2370 +verac_brassard1=2371 +drain=2372 +digsite_dig_pick=2373 +wind_wave_all=2374 +wind_wave_fail=2375 +digsite_dig_trowel=2376 +digsite_panning=2377 +panning=2378 +bigbang=2379 +bell1=2380 +destroy_object=2381 +biglever=2382 +break=2383 +burst=2384 +cavernous=2385 +disarm_trap_failure=2386 +disarm_trap=2387 +disease_hitsplat=2388 +doorslam=2389 +drink=2390 +drinkwork=2391 +drip_liquid=2392 +eat=2393 +errorsound=2394 +fill_glass=2395 +firework=2396 +trailblazer_teleport_flyaway=2397 +grim_reaper_appear=2398 +ice_troll_attack=2399 +lever=2400 +liquid=2401 +locked=2402 +lovely_lazer=2403 +trailblazer_teleport_spin=2404 +trailblazer_teleport_draw_line=2405 +bear_attack_undead_stomp_02=2406 +pick_lock=2407 +poison_hitsplat=2408 +scissors=2409 +scrape=2410 +silence=2411 +sizzle=2412 +small_cry=2413 +snap=2414 +strange_bird=2415 +bear_death_undead=2416 +turn_book_page=2417 +undead_appear=2418 +unlock=2419 +up_and_down_stairs=2420 +waterwheel_bubbes=2421 +waterwheel_loop=2422 +waterwheel_loop2=2423 +waterwheel_loop3=2424 +whiteout=2425 +bear_attack_undead_02=2426 +farming_compost=2427 +compost_close=2428 +compost_open=2429 +farming_amulet=2430 +farming_emptybarrel=2431 +farming_dibbing=2432 +farming_fillplantpot=2433 +farming_fill=2434 +farming_plant_scarecrow=2435 +farming_fillpot=2436 +farming_pick=2437 +farming_plantcure=2438 +farming_pourpint=2439 +farming_prune=2440 +farming_putin=2441 +farming_raking=2442 +farming_scoop=2443 +farming_sprinkle=2444 +farming_valve=2445 +farming_watering=2446 +fill_vat=2447 +pick_cactus=2448 +slice_watermelon=2449 +handholds_grab_to_second=2450 +balancing_ledge=2451 +climb_under=2452 +climb_wall=2453 +climbing_loop=2454 +fall_land=2455 +handholds_grab=2456 +handholds_grab2=2457 +jump_land_bridge=2458 +handholds_loop=2459 +handholds_off=2460 +jump=2461 +jump2=2462 +jump_and_fall=2463 +jump_blades=2464 +jump_further=2465 +monkeybars_loop=2466 +jump_no_land=2467 +jump_up=2468 +land_flat=2469 +log_balance=2470 +logtrap=2471 +pitfall_fall_half=2472 +monkeybars_off=2473 +monkeybars_on=2474 +pitfall2=2475 +pitfall_fall=2476 +springtrap_release=2477 +pitfall_open=2478 +plank_break=2479 +plankwalk=2480 +pressups=2481 +ropeclimb=2482 +ropeclimb_loop=2483 +runonspot=2484 +sidestep=2485 +situps=2486 +spear_jump=2487 +springtrap_reset=2488 +squeeze_in=2489 +squeeze_out=2490 +squeeze_thru_crack=2491 +star_jump=2492 +stumble_loop=2493 +swing_across=2494 +tightrope=2495 +watersplash=2496 +baxe_crush=2497 +baxe_slash=2498 +hacksword_crush=2499 +hacksword_slash=2500 +hacksword_stab=2501 +2H_crush=2502 +2H_slash=2503 +2H_stab=2504 +keris_crush=2505 +keris_slash=2506 +keris_stab=2507 +mace_crush=2508 +mace_stab=2509 +sword_hit=2510 +spiked_blackjack_crush=2511 +blackjack_crush=2512 +boxing_glove=2513 +broken_hatchet=2514 +crabclaw_stab=2515 +cudgel_2=2516 +farmersfork_stab=2517 +flower_attack=2518 +spanner_crush=2519 +tzhaar_ket_om_crush=2520 +weapon_miss=2521 +scythe_double=2522 +scythe_hit=2523 +scythe_slash=2524 +scythe_stab=2525 +sickle_slash=2526 +sickle_stab=2527 +chainshot=2528 +cleave=2529 +dragon_axe_thunder=2530 +clobber=2531 +clobber2=2532 +halberd_swipe=2533 +impale=2534 +neptune_blast=2535 +powershot=2536 +puncture=2537 +rampage=2538 +sanctuary=2539 +sever=2540 +shatter=2541 +shock=2542 +shock2=2543 +shove=2544 +snapshot=2545 +soulshot=2546 +stabsword_crush=2547 +stabsword_slash=2548 +stabsword_stab=2549 +bonespear_crush_testsound=2550 +bonespear_crush=2551 +staff_double_faster=2552 +bonespear_lunge=2553 +bonespear_stab=2554 +newstaff=2555 +staff_crush=2556 +staff_double=2557 +tzhaar_staff_crush=2558 +staff_faster=2559 +staff_hit=2560 +staff_lunge=2561 +staff_stab=2562 +wand_crush=2563 +human_attack=2564 +unarmed_kick=2565 +unarmed_punch=2566 +warhammer_crush=2567 +wood_sword_crush=2568 +wood_armour1=2569 +wood_armour2=2570 +wood_armour_2=2571 +wood_sword_slash=2572 +wood_sword_stab=2573 +churn=2574 +hopperlever=2575 +fill_grain=2576 +fry=2577 +spit_roasting=2578 +millstones=2579 +oven=2580 +pick=2581 +pick2=2582 +pour_tea=2583 +sand_bucket=2584 +attach_orb=2585 +chisel=2586 +loom_weave=2587 +pottery=2588 +smash_gem=2589 +spinning=2590 +stiching=2591 +stitching=2592 +stringing=2593 +strike_and_light=2594 +bunsen_burner=2595 +fire_lit=2596 +flint=2597 +fuse_light=2598 +tinderbox_strike=2599 +fishing_cast=2600 +fish_swim=2601 +lavacast=2602 +net=2603 +fletch_once=2604 +fletch=2605 +string_bow=2606 +dragon_potion_finished=2607 +grind=2608 +tap_fill=2609 +vial_empty=2610 +vial_mix=2611 +vial_mix_smokepuff=2612 +vial_pour=2613 +vialpour=2614 +well_fill=2615 +eagle_flap_loop=2616 +crow_1=2617 +eagle_cry=2618 +hunting_bigcat_jump=2619 +eagle_flap_loop2=2620 +eagle_fly=2621 +eagle_flyaway=2622 +hunting_butterflynet=2623 +hunting_birdcall=2624 +hunting_birdcaught=2625 +hunting_birdtrap=2626 +hunting_boxtrap=2627 +noose_wand=2628 +lasoo=2629 +hunting_collapsebranches=2630 +hunting_deadfall=2631 +hunting_dismantle=2632 +hunting_falcon_fly=2633 +hunting_falcon_swoop=2634 +hunting_jump=2635 +hunting_layboxtrap=2636 +hunting_noose=2637 +hunting_pitfall_collapse=2638 +hunting_placebranches=2639 +hunting_placeleaves=2640 +hunting_placeleaves2=2641 +hunting_release_butterfly=2642 +hunting_search=2643 +hunting_set_twitchnet=2644 +hunting_setdeadfall=2645 +hunting_setnoose=2646 +hunting_setsnare=2647 +hunting_smokepuff_escape=2648 +hunting_takebranches=2649 +hunting_takeleaves=2650 +hunting_tease_feline=2651 +hunting_twitchnet=2652 +hunting_twitchnet_smoke=2653 +hunting_twitchnet_smoke2=2654 +found_gem=2655 +mine=2656 +mine_3=2657 +mine_5=2658 +mine_quick=2659 +mining_3=2660 +prospect=2661 +improved_reflexes=2662 +cancel_prayer=2663 +clarity=2664 +eagle_eye=2665 +hawk_eye=2666 +incredible_reflexes=2667 +mystic_lore=2668 +mystic_might=2669 +mystic_will=2670 +prayer_boost=2671 +prayer_drain=2672 +prayer_off=2673 +prayer_recharge=2674 +protect_from_magic=2675 +protect_from_melee=2676 +protect_from_missiles=2677 +rapid_heal=2678 +rapid_restore=2679 +redemption=2680 +redemption_heal=2681 +retribution=2682 +retribution_test=2683 +rock_skin=2684 +sharp_eye=2685 +smite=2686 +steel_skin=2687 +strength_burst=2688 +superhuman_strength=2689 +thick_skin=2690 +ultimate_strength=2691 +arrow_launch=2692 +arrowlaunch2=2693 +bonehit=2694 +crossbow=2695 +dart=2696 +flashpowder=2697 +grease_hit=2698 +javelin=2699 +longbow=2700 +outofammo=2701 +shortbow=2702 +slayer_arrow=2703 +throw_grease=2704 +throw_grease2=2705 +throwingaxe=2706 +throwingknife=2707 +thrown=2708 +abyssal_squeezethrough=2709 +bind_runes=2710 +boil_burst=2711 +slayer_statuemove=2712 +energydrain=2713 +granite_maul=2714 +quicksmash=2715 +slayer_throwbucket=2716 +slayerdoors=2717 +slayerdoors_close=2718 +sprinkle_salt=2719 +whip=2720 +anvil_4=2721 +anvil=2722 +anvil2=2723 +glassblowing=2724 +furnace=2725 +new_anvil=2726 +stunned=2727 +canoe_paddle_loop=2728 +build_canoe=2729 +woodchop_quick=2730 +canoe_roll=2731 +canoe_sink=2732 +remove_axe=2733 +tree_fall=2734 +woodchop=2735 +woodchop_4=2736 +anothercry=2737 +bones_down=2738 +put_down=2739 +bear_hit_undead_02=2740 +bear_attack_undead_stomp=2742 +harpiebugswarm_attack=2743 +harpiebugswarm_death=2744 +harpiebugswarm_hit=2745 +swordclash6_1=2746 +swordclash6_2=2747 +swordclash6_3=2748 +swordclash6_4=2749 +swordclash6_5=2750 +blade4_1=2751 +blade4_2=2752 +blade4_3=2753 +blade4_4=2754 +blade4_5=2755 +wood_armour3_1=2756 +wood_armour3_2=2757 +wood_armour3_3=2758 +wood_armour3_4=2759 +wood_armour3_5=2760 +sword_hit2_mail_1=2761 +sword_hit2_mail_2=2762 +sword_hit2_mail_3=2763 +sword_hit2_mail_4=2764 +sword_hit2_mail_5=2765 +sword_hit2_1=2766 +sword_hit2_2=2767 +sword_hit2_3=2768 +sword_hit2_4=2769 +sword_hit2_5=2770 +steel_mail_1=2771 +steel_1=2772 +steel_mail_2=2773 +steel_mail_3=2774 +steel_mail_4=2775 +steel_mail_5=2776 +steel_2=2777 +steel_3=2778 +steel_4=2779 +steel_5=2780 +big_bar_mail_1=2781 +big_bar_1=2782 +big_bar_2=2783 +big_bar_3=2784 +big_bar_4=2785 +big_bar_mail_2=2786 +big_bar_mail_3=2787 +big_bar_mail_4=2788 +big_bar_mail_5=2789 +big_bar_5=2790 +dragon_armour_1=2791 +dragon_armour_2=2792 +dragon_armour_3=2793 +dragon_armour_4=2794 +dragon_armour_5=2795 +dragon_mail_1=2796 +dragon_mail_2=2797 +dragon_mail_3=2798 +dragon_mail_4=2799 +dragon_mail_5=2800 +blade3_mail_1=2801 +blade3_mail_2=2802 +blade3_mail_3=2803 +blade3_mail_4=2804 +blade3_mail_5=2805 +blade3_1=2806 +blade3_2=2807 +blade3_3=2808 +blade3_4=2809 +blade3_5=2810 +leather_hit_1=2811 +leather_hit_2=2812 +leather_hit_3=2813 +leather_hit_4=2814 +leather_hit_5=2815 +steel2_mail_1=2816 +steel2_mail_2=2817 +steel2_mail_3=2818 +steel2_mail_4=2819 +steel2_mail_5=2820 +steel2_1=2821 +steel2_2=2822 +steel2_3=2823 +steel2_4=2824 +steel2_5=2825 +swordclash3_mail_1=2826 +swordclash3_mail_2=2827 +swordclash3_mail_3=2828 +swordclash3_mail_4=2829 +swordclash3_mail_5=2830 +swordclash3_1=2831 +swordclash3_2=2832 +swordclash3_3=2833 +swordclash3_4=2834 +swordclash3_5=2835 +blade2_mail_1=2836 +blade2_mail_2=2837 +blade2_mail_3=2838 +blade2_mail_4=2839 +blade2_mail_5=2840 +blade2_1=2841 +blade2_2=2842 +blade2_3=2843 +blade2_4=2844 +blade2_5=2845 +stone_armour4=2846 +stone_armour5=2847 +brassclash_1=2848 +brassclash_2=2849 +brassclash_3=2850 +brassclash_4=2851 +brassclash_5=2852 +brassclash2_1=2853 +brassclash2_2=2854 +brassclash2_3=2855 +brassclash2_4=2856 +brassclash2_5=2857 +sos_through_door=2858 +swordclash2_1=2859 +swordclash2_2=2860 +swordclash2_3=2861 +swordclash2_4=2862 +swordclash2_5=2863 +cosmic_entity_death=2864 +cosmic_entity_hit=2865 +fairy_death=2866 +gorak_attack=2867 +gorak_death=2868 +gorak_hit=2869 +FT_fairy_appear=2870 +FT_fairyrings=2871 +FT_shooting_star=2872 +FT_starflower_die=2873 +FT_starflower_grow=2874 +dreamland_globes=2875 +mcannon_setup=2876 +mcannon_turn=2877 +underwater_macro_bubbles=2878 +lunar_bake_pie=2879 +lunar_cast_1=2880 +lunar_cast=2881 +lunar_cure_group=2882 +lunar_cast_2=2883 +lunar_cure=2884 +lunar_energy_transfer=2885 +lunar_cure_other=2886 +lunar_cure_other2=2887 +lunar_embue_runes=2888 +lunar_cure_other_individual=2889 +lunar_flax_to_bowstring=2890 +lunar_fertilize=2891 +lunar_heal_other_individual=2892 +lunar_heal=2893 +lunar_heal_group=2894 +lunar_heal_other=2895 +lunar_heatglass=2896 +lunar_stat_share_individual=2897 +lunar_initial_casting=2898 +lunar_stat_share=2899 +lunar_statheal_other=2900 +lunar_strength_share=2901 +lunar_strength_share2=2902 +lunar_string_amulet=2903 +lunar_strength_share_individual=2904 +lunar_string_amulet2=2905 +lunar_transfer_other=2906 +lunar_vengence=2907 +lunar_vengence_other=2908 +lunar_watering=2909 +armour_piercing=2910 +blood_sacrifice=2911 +clear_mind=2912 +crossbow_diamond=2913 +down_to_earth=2914 +dragons_breath=2915 +earths_fury=2916 +life_leech=2917 +lucky_lightning=2918 +magical_poison=2919 +sea_curse=2920 +enchanted_tipping=2921 +fletch_crossbow=2922 +flight_feathers=2923 +string_crossbow=2924 +tipping=2925 +mining_loop=2926 +mining_8=2927 +crossbow_grappling=2928 +crossbow_grapple_splash=2929 +fanfare=2930 +eyeglo_crystal_axe=2931 +eyeglo_crystal_axe_crush=2932 +eyeglo_crystal_axe_slash=2933 +eyeglo_damage_machine_1=2934 +eyeglo_damage_machine_2=2935 +eyeglo_damage_machine_low=2936 +eyeglo_destroy_machine=2937 +eyeglo_fluffy_evil_cry=2938 +eyeglo_mind_meld=2939 +eyeglo_repair_machine=2940 +eyeglo_thunder=2941 +eyeglo_tortoise_appear=2942 +eyeglo_transform=2943 +eyeglo_tree_reveal=2944 +eyeglo_treesplit_lightning=2945 +wood_armour4=2946 +wood_armour5=2947 +swordclash5_1=2948 +swordclash5_2=2949 +swordclash5_3=2950 +swordclash5_4=2951 +swordclash5_5=2952 +blade5_1=2953 +blade5_2=2954 +blade5_3=2955 +blade5_4=2956 +blade5_5=2957 +dragon_shield_1=2958 +dragon_shield_2=2959 +dragon_shield_3=2960 +dragon_shield_4=2961 +dragon_shield_5=2962 +granite_shield_1=2963 +granite_shield_2=2964 +granite_shield_3=2965 +granite_shield_4=2966 +granite_shield_5=2967 +crystal_armour_5=2968 +man_hole_close=2969 +swinging_sign_rusty=2970 +DS_crystal_ball_anim=2971 +DS_big_door_unlock=2972 +DS_bone_spell=2973 +DS_break_table=2974 +DS_crystal_ball_end=2975 +DS_crystal_ball_start=2976 +DS_cupboard_appear=2977 +DS_cupboard_dissappear=2978 +DS_cupboard_fall=2979 +DS_delrith_appear=2980 +DS_delrith_banished=2981 +DS_drain=2982 +DS_key_fall=2983 +DS_portal_close=2984 +DS_portal_open=2985 +DS_replace_table=2986 +DS_summon_demon=2987 +DS_table_explosion=2988 +DS_wally_sword=2989 +equip_silverlight=2990 +vampire_arrives=2991 +vampire_flap_loop=2992 +vampire_flies_away=2993 +vampire_transform=2994 +m3_climb_down_shelves=2995 +m3_climb_shelves=2996 +m3_climb_up_shelves=2997 +m3_mine_jas_ore=2998 +m3_place_ore=2999 +m3_push_boat=3000 +m3_repair=3001 +snare_impact=3002 +snare_cast_and_fire=3003 +stun_cast_and_fire=3004 +stun_impact=3005 +telegrab_cast_and_fire=3006 +telegrab_hit=3007 +vulnerability_impact=3008 +vulnerability_cast_and_fire=3009 +weaken_hit=3010 +weaken_cast_and_fire=3011 +zap_impact=3012 +leak_1=3013 +leak_2=3014 +leak_3=3015 +leak_4=3016 +leak_5=3017 +grail_bell=3018 +seaslug_hit=3019 +slug_crane_turn_and_lower=3020 +slug_crane_turn=3021 +slug_fisherman_attack=3022 +slug_dry_sticks=3023 +slug_kick_panel=3024 +slug_scoop_slug=3025 +slug_smear_paste=3026 +slug_torch_attack=3027 +slug_torch_lit=3028 +slug2_light_flash=3029 +slug2_bigdoor=3030 +slug2_teleport_fail=3031 +slug2_place_rune=3032 +slug2_rumbling=3033 +slug_prince_attack=3034 +slug_prince_death=3035 +slug_prince_hit=3036 +woodchop1=3037 +woodchop2=3038 +woodchop3=3039 +woodchop4=3040 +woodchop5=3041 +woodchop6=3042 +troll_champion_sword_attack=3043 +cow_atmospheric=3044 +bee_loop_1=3045 +bees_1=3046 +bird_twitter_6=3047 +crickets_slow=3048 +croak_slow=3049 +cave_rumbling_loop_quieter=3050 +cave_bubble_1=3051 +cave_bubble_2=3052 +cave_bubble_3=3053 +cave_bubble_4=3054 +cave_bubble_5=3055 +cluck_1=3056 +cluck_2=3057 +cluck_3=3058 +cluck_4=3059 +cluck_5=3060 +tree_wind_loop=3061 +human_chatter_1=3062 +human_chatter_2=3063 +human_chatter_3=3064 +human_chatter_4=3065 +human_chatter_5=3066 +human_chatter_6=3067 +human_cry_1=3068 +human_cry_2=3069 +human_cry_3=3070 +human_laugh_1=3071 +human_laugh_2=3072 +gas_loop_1=3073 +aquarium=3074 +air_bubbles_loop_1=3075 +air_bubbles_loop_2=3076 +bubbling_loop_4=3077 +bubbling_stream_2=3078 +drain_loop_1=3079 +high_drip_2=3080 +high_drip_3=3081 +leak_loop_1=3082 +ocean_gentle=3083 +ocean_gentle_1=3084 +ocean_gentle_2=3085 +ocean_gentle_3=3086 +rock_pool_loop=3087 +trickle_1=3088 +underwater_loop_1=3089 +lava_fire_loop_1=3090 +breeze_loop_3=3091 +breeze_loop_4=3092 +cave_wind_loop=3093 +coastal_wind_loop=3094 +swamp_wind_loop=3095 +windy_loop_1=3096 +abyss_1=3097 +abyss_2=3098 +abyss_3=3099 +abyss_4=3100 +rat_scuttle=3101 +rat_squeak_1=3102 +rat_squeak_2=3103 +rat_squeak_3=3104 +desert_animal_1=3105 +desert_animal_2=3106 +desert_wind_1=3107 +desert_wind_2=3108 +desert_wind_loop=3109 +rattlesnake_1=3110 +rattlesnake_2=3111 +vulture_1=3112 +vulture_2=3113 +cow_wheel=3114 +cauldron_loop_1=3115 +bubblin_loop_2=3116 +bubbling_vat_1=3117 +bubbling_vials=3118 +deal_control_shake=3119 +clock_tick_1=3120 +rat_wheel=3121 +rain_1=3122 +deserttreasure_wall_crusher=3123 +flag_flutter_1=3124 +flag_flutter_2=3125 +flag_flutter_3=3126 +flag_flutter_4=3127 +furnace_chimney_loop=3128 +heartbeat=3129 +heartbeat_breathing=3130 +light_beam_loop_1=3131 +lighthouse_loop=3132 +machine_loop_1=3133 +magic_tree_loop=3134 +magic_tree_loop_2=3135 +magic_tree_loop_3=3136 +musical_tree=3137 +pendulum_swing_loop=3138 +portal_loop_1=3139 +rune_altar_ambience=3140 +rune_temple_ambience=3141 +steam_loop_1=3142 +stone_grind_loop=3143 +stone_grinding_loop_1=3144 +wheel_loop_1=3145 +whetstone_grind=3146 +windchimes_work=3147 +elem_bg_steam_1=3148 +ew2_ambient_loop=3149 +ew2_machine_loop=3150 +tol_wolf_whistle=3151 +elem_bg_steam_2=3152 +big_bellows_loop=3153 +elem_bg_rumble_1=3154 +elem_bg_rumble_2=3155 +elem_bg_steam_3=3156 +elem_boiler_loop=3157 +tainted_shot=3158 +upass_bridgefall=3159 +flint1=3160 +flint2=3161 +flint3=3162 +flint4=3163 +flint5=3164 +flint6=3165 +ew2_extractor_operation=3166 +ew2_cart_loop=3167 +ew2_cool_bar=3168 +ew2_crane_lower=3169 +ew2_crane_turn=3170 +ew2_extractor_loop=3171 +ew2_mind_electrocute=3172 +ew2_extractor_zap=3173 +ew2_fanblade=3174 +ew2_machine_rumble=3175 +ew2_mind_control=3176 +ew2_place_bar=3177 +ew2_place_cog=3178 +ew2_place_pipe=3179 +ew2_press_bar=3180 +ew2_remove_pipe=3181 +ew2_screw_turn=3182 +ew2_water_flow=3183 +ew2_water_rack=3184 +ew2_water_valve=3185 +king_troll_hit=3186 +troll_armour=3187 +troll_with_stone_shield_hit=3188 +giant_roc_approaches=3189 +giant_roc_appear=3190 +giant_roc_boulder_attack=3191 +giant_roc_death=3192 +giant_roc_flap_attack=3193 +giant_roc_hit=3194 +giant_roc_peck_attack=3195 +my_arm_sealoop_finish=3196 +my_arm_sealoop=3197 +myarm_bubbling_cauldren=3198 +myarm_armour_thrown=3199 +myarm_male_scream=3200 +myarm_flyback=3201 +myarm_leprechaun_scared=3202 +myarm_patchgrown=3203 +myarm_pick_goutweed=3204 +myarm_roc_akka=3205 +myarm_roc_akka_once=3206 +myarm_roc_squeek=3207 +myarm_sealoop=3208 +myarm_spatula=3209 +myarm_tear_bush=3210 +myarm_troll_jumping=3211 +TT_winching_loop=3212 +TT_bend_bars=3213 +TT_cart_loop=3214 +TT_down_stairs=3215 +TT_fall_back=3216 +TT_knocking=3217 +TT_squeeze_out=3218 +TT_up_stairs=3219 +mine1=3220 +mine2=3221 +mine3=3222 +mine4=3223 +mine5=3224 +mine6=3225 +skill_light_candle=3226 +equip_halloween_pumpkin=3227 +equip_halloween_spray=3228 +Abo_hurt_1=3229 +tob_xarpus_exhumes_open=3230 +tob_xarpus_exhume_health_shots=3231 +tob_nylocas_range_death_bang=3232 +tob_sotetseg_redspiral_grid_playercollision=3233 +tob_maiden_shadow_attack_part_2=3234 +tob_verzik_vampire_spawn=3235 +halloween_ghost_attack=3236 +halloween_ghost_death=3237 +halloween_ghost_hit=3238 +zep_air_escape_fast=3239 +zep_air_blast=3240 +zep_air_blast_interface=3241 +zep_air_escape_slow=3242 +zep_balloon_burns=3243 +zep_balloon_crash=3244 +zep_balloon_float=3245 +zep_balloon_move=3246 +zep_breeze=3247 +zep_construct=3248 +zep_drop_ballast=3249 +zep_hammering_1=3250 +zep_use_logs=3251 +pop3=3252 +fishing_cast1=3253 +fishing_cast2=3254 +fishing_cast3=3255 +fishing_cast4=3256 +fishing_cast5=3257 +fishing_cast6=3258 +hunting_laymagicboxtrap=3259 +hunting_take_creature=3260 +imp_cleaver_attack=3261 +imp_knife_attack=3262 +lambcall1=3263 +ram_attack2=3264 +watchtower_machine=3265 +watchtower_dragon_attack=3266 +watchtower_make_relic=3267 +watchtower_shaman_cry=3268 +watchtower_smokepuff=3269 +watchtower_spell_cast=3270 +watchtower_spell_hit=3271 +anma_chicken_grab_2=3272 +anma_adorn_amulet=3273 +anma_axe_bounce=3274 +anma_chicken_catch=3275 +anma_chicken_grab=3276 +anma_equip_chicken_sack=3277 +anma_craft_container=3278 +anma_interface_button=3279 +anma_hammer_iron=3280 +anma_polish_buttons=3281 +anma_magnet=3282 +anma_puzzle_complete=3283 +equip_backpack=3284 +equip_jester=3285 +tob_sotetseg_death=3286 +gublinch_make_snowball=3287 +tob_bloat_jar_bgsound_loop=3288 +tob_magic_death_bang=3289 +tob_xarpus_attack_ranged_projectile_5=3290 +tob_pillar_hit=3291 +gublinch_hit=3292 +tob_maiden_shadow_attack=3293 +tob_nylocas_deathdetonate_shriek=3294 +gublinch_snowball=3295 +barbassault_atomic=3296 +barbassault_build=3297 +barbassault_build_trapdoor=3298 +barbassault_egg_dipping=3299 +barbassault_egglauncher=3300 +barbassault_fire_egg=3301 +barbassault_horn=3302 +barbassault_lay_egg=3303 +barbassault_load_egg=3304 +barbassault_poison=3305 +barbassault_smash=3306 +barbassault_spikes=3307 +barbassault_splat=3308 +barbassault_stun=3309 +barbassault_vending=3310 +penance_spawn_attack=3311 +hatchet_skeleton_attack=3312 +warhammer_skeleton_attack=3313 +mace_skeleton_attack=3314 +lotr_ghost_hit=3315 +lotr_click=3316 +lotr_random_bang=3317 +lotr_logtrap=3318 +lotr_random_skeleton=3319 +lotr_random_bark=3320 +lotr_random_metal=3321 +lotr_tarn_castandfire=3322 +lotr_random_slam=3323 +lotr_random_stone=3324 +lotr_summon_armour=3325 +lotr_terrordog_attack=3326 +lotr_tarn_impact=3327 +lotr_terrordog_death=3328 +lotr_terrordog_explode=3329 +lotr_terrordog_hit=3330 +lotr_trans_attack=3331 +lotr_trans_death=3332 +lotr_trans_hit=3333 +lotr_trans_speak=3334 +lotr_trans_speak1=3335 +lotr_trans_speak2=3336 +lotr_trans_speak3=3337 +lotr_trans_speak4=3338 +lotr_trans_speak5=3339 +lotr_trans_speak6=3340 +lotr_trans_speak7=3341 +lotr_trans_speak8=3342 +lotr_trans_speak9=3343 +lotr_trans_spell=3344 +lotr_trans_walk=3345 +lotr_transmute=3346 +lotr_zombie_attack=3347 +lotr_zombie_death=3348 +lotr_zombie_hit=3349 +skeletal_gorilla_attack=3350 +skeletal_gorilla_death=3351 +skeletal_gorilla_hit=3352 +peng_antimagic_bgsound=3353 +human_into_penguin=3354 +peng_machine_bgsound=3355 +peng_block_move=3356 +peng_fall_snow=3357 +peng_icicle_fall=3358 +peng_jump_slip=3359 +peng_lathe_bgsound=3360 +peng_rumble_bgsound=3361 +peng_penguin_call=3362 +peng_penguin_flap=3363 +peng_penguincall_1=3364 +peng_penguincall_2=3365 +peng_penguincall_3=3366 +peng_penguincall_4=3367 +peng_spinninglight_bgsound=3368 +peng_sled_forward=3369 +peng_sled_jump=3370 +peng_slip_snow=3371 +penguin_into_human=3372 +icelord_attack2=3373 +icelord_attack=3374 +icelord_death=3375 +icelord_hit=3376 +slip_up=3377 +yak_attack=3378 +yak_death=3379 +yak_hit=3380 +barbarian_grunt_attack=3381 +barbarian_grunt_death=3382 +barbarian_grunt_hit=3383 +FI_bell_work=3384 +FI_bell=3385 +FI_dripping=3386 +FI_clap=3387 +FI_fighting_closer=3388 +FI_fighting=3389 +FI_jester_dance_5=3390 +FI_footsteps=3391 +FI_jester_bow=3392 +FI_jester_clap=3393 +FI_jester_juggle_loop=3394 +FI_jester_jangle=3395 +FI_jester_jig=3396 +FI_jester_jig_loop=3397 +FI_jester_juggle=3398 +FI_jester_skip_loop=3399 +FI_jester_talk_stick=3400 +FI_jester_throwpie=3401 +FI_jesterstick=3402 +FI_rock_hit=3403 +FI_steel=3404 +FI_trollking_roar=3405 +coins_jingle_5=3406 +osman_pick_high=3407 +osman_drips=3408 +ghost_dog_attack=3409 +ghost_dog_death=3410 +ghost_dog_hit=3411 +ghost_sword_crush=3412 +ghost_sword_slash=3413 +ghost_sword_stab=3414 +severed_leg_attack=3415 +severed_leg_drop=3416 +tol_creature_appear=3417 +tol_break_cage=3418 +tol_door_open_and_close=3419 +tol_fix_cage=3420 +tol_fix_machine=3421 +tol_homonculus_appear=3422 +tol_homonculus_boo=3423 +tol_homonculus_crying=3424 +tol_liquid_flow=3425 +tol_long_wolf_whistle=3426 +tol_low_hum=3427 +tol_magic_explosions=3428 +tol_move_pipe=3429 +tol_newtroost_attack=3430 +tol_newtroost_death=3431 +tol_newtroost_hit=3432 +tol_place_pipe=3433 +tol_pumping_liquid=3434 +tol_rooster_synth=3435 +tol_valve=3436 +tol_whistle_dance=3437 +tol_whistle_runescape=3438 +tol_wolf_whistle2=3439 +whirlpool_loop=3440 +brain_barrelchest_attack=3453 +brain_anchor_attack=3454 +brain_barrelchest_normal_attack=3455 +brain_barrelchest_death=3456 +brain_barrelchest_hit=3457 +brain_pull_statue=3458 +brain_idea=3459 +brain_barrelchest_special_attack=3460 +brain_boat_sound_1=3461 +brain_boat_sound_2=3462 +brain_boat_sound_3=3463 +brain_boat_sound_4=3464 +brain_boat_sound_5=3465 +brain_clockwork_cat=3466 +brain_creaky_ship=3467 +brain_creaky_ship_2=3468 +brain_explosion=3469 +brain_fuse_burn=3470 +brain_fuse_light=3471 +brain_zombie_underwater_attack=3472 +brain_special_brine_saber=3473 +brain_whistle=3474 +brain_zombie_pirate_attack=3475 +brain_zombie_pirate_death=3476 +brain_zombie_pirate_hit=3477 +new_brain_zombie_underwater_attack=3478 +brain_zombie_underwater_death=3479 +brain_zombie_underwater_hit=3480 +anchor_sunder=3481 +ramble_nailbeast_attack=3482 +ramble_ghast_birth=3483 +ramble_nailbeast_death=3484 +ramble_nailbeast_hit=3485 +ramble_sploosh=3486 +treevillage_catabow_hit=3487 +treevillage_catabow_launch=3488 +giantcavebug_attack=3489 +giantcavebug_death=3490 +giantcavebug_hit=3491 +molanisk_attack=3492 +molanisk_death=3493 +molanisk_hit=3494 +dorg_machine_loop_bgsound=3495 +dorg_artifact_teleport=3496 +dorg_boiler_loop=3497 +dorg_break_part=3498 +dorg_bulb_pop=3499 +dorg_catch=3500 +dorg_clap=3501 +dorg_conductor_coil=3502 +dorg_crossbow_grapple=3503 +dorg_kick_ball=3504 +dorg_machine_loop=3505 +dorg_machine_spool=3506 +dorg_pat=3507 +dorg_remove_part=3508 +dorg_repair_bulb=3509 +dorg_spitroast=3510 +dorg_take_wire=3511 +dorg_zapper=3512 +dorg_zapper_bgsound=3513 +goblin_warhammer_attack=3514 +goblin_axe_attack=3515 +goblin_guard_attack=3516 +goblin_hit_shield=3517 +goblin_knife_attack=3518 +goblin_shield=3519 +goblin_spear_attack=3520 +goblin_staff_attack=3521 +slayer_molanisk_bell=3522 +surok_destroy_letter=3523 +surok_bindwand=3524 +surok_equip_ring=3525 +surok_mine_statue=3526 +surok_jump=3527 +surok_mindcontrol_impact=3528 +surok_mindcontrol_cast=3529 +surok_mindcontrol_spell=3530 +surok_monkspell=3531 +surok_outlaw_death=3532 +surok_outlaw_hit=3533 +surok_pickaxe_swing=3534 +surok_smash_statue=3535 +surok_spellfail=3536 +surok_teleport=3537 +surok_teleport_cast_only=3538 +tob_sotetseg_attack_ranged=3539 +tob_sotetseg_attack_melee_4=3540 +tob_xarpus_idle_wingflap_1=3541 +tob_verzik_vampire_death=3542 +tob_verzik_spark_1=3543 +tob_bloat_flies_attack_1=3544 +tob_bloat_shout=3545 +tob_nylocas_web_spawn=3546 +tob_maiden_blood_impact=3547 +tob_xarpus_fly_up_forward_wings_4=3548 +tob_xarpus_death_screech=3549 +tob_nylocas_deathdetonate_spinning_3=3550 +olaf2_brinesabre_slash=3551 +olaf2_brinesabre_stab=3552 +olaf2_fall_into_cave=3553 +olaf2_roll_boulder=3554 +olaf2_splash=3555 +olaf2_ulfric_rise=3556 +twocats_fry_noob=3557 +goblin_crowd_calls2=3558 +goblin_arrow_death=3559 +goblin_bolt_death=3560 +goblin_crowd=3561 +goblin_crowd2=3562 +goblin_crowd3=3563 +goblin_crowd4=3564 +goblin_crowd_calls=3565 +slice_sphere_teleport=3566 +goblin_crowd_cheer=3567 +goblin_crowd_loop=3568 +goblin_crowd_loop2=3569 +goblin_crowd_loop3=3570 +goblin_crowd_loop4=3571 +goblin_crowd_loop5=3572 +slice_arrow_hit=3573 +slice_bow_attack=3574 +slice_brush=3575 +slice_crossbow=3576 +slice_fire_arrow=3577 +slice_flare=3578 +slice_human_fall=3579 +slice_ladder=3580 +slice_mace_slash=3581 +slice_train_approach=3582 +slice_train=3583 +slice_train_journey=3584 +slice_train_arrive=3585 +slice_train_depart=3586 +slice_zanik_ladygoblin_hit=3587 +slice_train_old=3588 +slice_untie_zanic=3589 +slice_zanik_ladygoblin_attack=3590 +slice_zanik_ladygoblin_death=3591 +goblin_mace=3592 +vm_rockpick=3593 +vm_brush=3594 +vm_brush_1=3595 +vm_brush_2=3596 +vm_rockpick_1=3597 +vm_rockpick_2=3598 +vm_rockpick_3=3599 +mine_ore=3600 +atjun_medium_monkey=3601 +atjun_easy_parrot=3602 +atjun_hard_lion=3603 +small_spider_attack=3604 +big_spider_attack=3605 +big_spider_death=3606 +big_spider_hit=3607 +small_spider_death=3608 +small_spider_hit=3609 +scorpion_death=3610 +scorpion_attack=3611 +scorpion_hit=3612 +lunar_change_spellbook=3613 +lunar_humidify=3614 +lunar_hunter_kit=3615 +lunar_hunter_kit_inv=3616 +lunar_make_plank=3617 +lunar_npc_contact=3618 +lunar_sleep=3619 +lunar_stat_spy=3620 +lunar_stat_spy_impact=3621 +dream_everlasting_attack=3622 +dream_burrow_down=3623 +dream_burrow_up=3624 +dream_everlasting_death=3625 +dream_everlasting_hit=3626 +dream_heartbeat=3627 +dream_illusive_attack=3628 +dream_illusive_death=3629 +dream_illusive_hit=3630 +dream_inadequacy_attack=3631 +dream_inadequacy_death=3632 +dream_inadequacy_hit=3633 +dream_razorbeast_attack=3634 +dream_razorbeast_death=3635 +dream_razorbeast_hit=3636 +dream_squish_illusive=3637 +dream_untouchable_attack=3638 +dream_untouchable_death=3639 +dream_untouchable_hit=3640 +dream_whiteout=3641 +vm_barrier_open=3642 +vm_all_correct=3643 +vm_bg_digsite_cleaning=3644 +vm_bg_cleaning=3645 +vm_drop_plank=3646 +vm_camel=3647 +vm_correct=3648 +vm_dragon=3649 +vm_displaycase_open=3650 +vm_empty_wheelbarrow=3651 +vm_footsteps_ambience=3652 +vm_gain_kudos=3653 +vm_incorrect=3654 +vm_kalphite=3655 +vm_leech=3656 +vm_lizard=3657 +vm_mechanism_1=3658 +vm_mechanism_2=3659 +vm_mole_burrow_down=3660 +vm_mole_burrow_up=3661 +vm_monkey=3662 +vm_penguin=3663 +vm_push_button=3664 +vm_seaslug=3665 +vm_snail=3666 +vm_snake=3667 +vm_terrorbird=3668 +vm_tortoise=3669 +vm_wheelbarrow_squeek=3670 +vm_wyvern=3671 +drill_bgsound=3672 +lathe_bgsound=3673 +grim_giant_attack=3674 +grim_beanstalk_shrinking=3675 +grim_beanstalk_fall=3676 +grim_beanstalk_grow=3677 +grim_drink_shrinking_potion=3678 +grim_bgsound_cloudbreeze=3679 +grim_experiment2_attack=3680 +grim_experiment2_death=3681 +grim_experiment2_hit=3682 +grim_experiment2_range=3683 +grim_giant_death=3684 +grim_giant_hit=3685 +grim_giant_roar=3686 +grim_giant_taunt=3687 +grim_grimgnash_attack=3688 +grim_mouse_attack=3689 +grim_mouse_death=3690 +grim_mouse_hit=3691 +grim_piano_a4=3692 +grim_piano_a5=3693 +grim_piano_b4=3694 +grim_piano_b5=3695 +grim_piano_c4=3696 +grim_piano_c5=3697 +grim_piano_d4=3698 +grim_piano_d5=3699 +grim_piano_e4=3700 +grim_piano_e5=3701 +grim_piano_f4=3702 +grim_piano_f5=3703 +grim_piano_g4=3704 +grim_piano_g5=3705 +grim_potion_wears_off=3706 +grim_scared_gnome_1=3707 +grim_scared_gnome_2=3708 +grim_sewer_water=3709 +grim_throw_gnomehead=3710 +grim_wolf=3711 +grim_wolf2=3712 +grim_wolf3=3713 +grim_wolf_growl=3714 +dog_attack=3717 +dog_death=3718 +dog_hit=3719 +ii_clock_tick=3720 +ii_breeze=3721 +ii_impling_spawn=3722 +ii_imp_disgust=3723 +ii_imp_fail=3724 +ii_imp_kick=3725 +ii_jar_generator=3726 +ii_longwave=3727 +ii_push_thru_wheat=3728 +ii_wheat_growing=3729 +ii_wheat_wilt=3730 +darkbow_doublefire=3731 +darkbow_doublefire_new=3732 +darkbow_dragon_attack=3733 +darkbow_fire=3734 +darkbow_impact=3735 +darkbow_shadow_attack=3736 +darkbow_shadow_impact=3737 +equip_darkbow=3738 +dragonslayer_bgsound_magicdoor=3739 +dragonslayer_absorb_fire=3740 +dragonslayer_burningfire=3741 +dragonslayer_crash_ship=3742 +dragonslayer_cutoffhead=3743 +dragonslayer_cutoffhead1=3744 +dragonslayer_cutoffhead2=3745 +dragonslayer_demon_attack=3746 +dragonslayer_demon_death=3747 +dragonslayer_demon_hit=3748 +dragonslayer_dragonball=3749 +dragonslayer_dragonbreath=3750 +dragonslayer_dragonscream=3751 +dragonslayer_dragonstomp3=3752 +dragonslayer_dragonwalk=3753 +dragonslayer_flapwings=3754 +dragonslayer_ghost_attack=3755 +dragonslayer_ghost_death=3756 +dragonslayer_ghost_hit=3757 +dragonslayer_magicdoor=3758 +dragonslayer_melzar_spell=3759 +dragonslayer_shield_empty=3760 +dragonslayer_shieldfire=3761 +dragonslayer_thunder=3762 +dragonslayer_zombie_attack=3763 +dragonslayer_zombie_death=3764 +dragonslayer_zombie_hit=3765 +string_bow_pluck_string=3766 +string_bow_pull_string=3767 +string_bow_tension_string=3768 +string_bow_tie_string=3769 +anvil01=3770 +anvil02=3771 +waterfiend_death=3772 +waterfiend_hit=3773 +waterfiend_attack=3774 +remove_axe2=3775 +brutal_jump_into_whirlpool=3776 +brutal_hand_fishing=3777 +brutal_hand_mining_2=3778 +brutal_woodchop=3779 +brutal_hand_mining=3780 +brutal_hand_mining_1=3781 +brutal_fire_cloak_steam=3782 +brutal_decant=3783 +brutal_pulloutfish=3784 +brutal_bow_fire_2=3785 +brutal_bow_firemaking=3786 +brutal_bow_fire_1=3787 +brutal_hand_in_water=3788 +anvil05=3789 +anvil_single_1=3790 +anvil_single_2=3791 +anvil06=3792 +anvil03=3793 +anvil04=3794 +pog_mutated_terror_bird_attack=3795 +pog_mutated_terror_bird_death=3796 +pog_mutated_terror_bird_hit=3797 +goblin_snore2=3798 +goblin_snore=3799 +observatory_screwdriver=3800 +observatory_fix_lens=3801 +observatory_hammer=3802 +observatory_goblin_spit=3803 +observatory_planets=3804 +patterns_rockingchair=3805 +patterns_chair_tap_forward=3806 +patterns_chair_tap_backward=3807 +patterns_slamdesk=3808 +patterns_turncard=3809 +patterns_shuffle_desk=3810 +pest_portal_shield_down=3811 +pest_portal_shield=3812 +town_crier_ring_bell_up=3813 +town_crier_ring_bell=3814 +town_crier_ring_bell_up-old=3815 +town_crier_scratch_head=3816 +town_crier_ring_bell_down=3817 +town_crier_ring_bell_down-old=3818 +kr_check_lock=3819 +kr_bgsound_writing=3820 +kr_smash_window=3821 +kr_judge_hammer=3822 +kr_pick_lock=3823 +kr_jury_whisper=3824 +kr_piety=3825 +kr_chivalry=3826 diff --git a/dumps/530/anim-skeleton-gfx-relations.txt b/dumps/530/anim-skeleton-gfx-relations.txt new file mode 100644 index 000000000..4c600e1ca --- /dev/null +++ b/dumps/530/anim-skeleton-gfx-relations.txt @@ -0,0 +1,11216 @@ +Base: 0 +Linked animations: [3, 5, 13, 14, 15, 70, 71, 87, 88, 173, 226, 227, 228, 329, 330, 331, 346, 347, 352, 356, 363, 364, 365, 376, 377, 378, 379, 380, 381, 382, 383, 385, 386, 387, 388, 389, 390, 391, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 406, 407, 409, 410, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 440, 451, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 582, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 642, 645, 654, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 748, 749, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 791, 792, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 904, 905, 906, 908, 909, 910, 911, 915, 916, 918, 919, 920, 921, 923, 929, 931, 1026, 1034, 1035, 1036, 1037, 1056, 1057, 1058, 1060, 1062, 1064, 1067, 1068, 1074, 1077, 1078, 1084, 1100, 1105, 1106, 1110, 1113, 1114, 1115, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1128, 1129, 1130, 1131, 1132, 1133, 1137, 1144, 1145, 1146, 1147, 1148, 1156, 1157, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1191, 1192, 1193, 1194, 1195, 1196, 1203, 1205, 1206, 1207, 1208, 1209, 1210, 1236, 1237, 1238, 1239, 1240, 1241, 1248, 1249, 1252, 1258, 1263, 1264, 1265, 1266, 1267, 1268, 1275, 1280, 1306, 1307, 1309, 1318, 1319, 1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1335, 1336, 1337, 1350, 1351, 1352, 1353, 1354, 1363, 1364, 1365, 1366, 1367, 1368, 1369, 1370, 1371, 1374, 1375, 1379, 1407, 1408, 1409, 1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1440, 1441, 1457, 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1499, 1500, 1501, 1502, 1504, 1505, 1506, 1507, 1508, 1509, 1510, 1511, 1514, 1515, 1516, 1517, 1531, 1544, 1548, 1550, 1551, 1552, 1559, 1560, 1563, 1565, 1567, 1572, 1575, 1601, 1602, 1603, 1604, 1605, 1606, 1633, 1634, 1638, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1658, 1659, 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1670, 1671, 1672, 1673, 1705, 1722, 1723, 1724, 1725, 1728, 1734, 1739, 1740, 1743, 1744, 1745, 1746, 1755, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1809, 1810, 1811, 1816, 1818, 1819, 1820, 1830, 1831, 1832, 1833, 1834, 1835, 1836, 1837, 1850, 1851, 1852, 1871, 1872, 1877, 1878, 1879, 1880, 1894, 1895, 1897, 1902, 1903, 1904, 1905, 1906, 1907, 1914, 1950, 1952, 1953, 1954, 1955, 1972, 1978, 1979, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 2009, 2010, 2033, 2034, 2036, 2037, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112, 2113, 2127, 2128, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149, 2163, 2164, 2171, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2261, 2262, 2263, 2267, 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, 2288, 2289, 2290, 2291, 2292, 2293, 2295, 2304, 2305, 2306, 2311, 2313, 2316, 2317, 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2332, 2333, 2334, 2338, 2339, 2340, 2343, 2376, 2377, 2378, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2393, 2394, 2400, 2401, 2403, 2409, 2410, 2411, 2413, 2414, 2420, 2421, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, 2432, 2433, 2441, 2442, 2443, 2450, 2553, 2554, 2555, 2556, 2557, 2561, 2562, 2563, 2565, 2566, 2569, 2570, 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2584, 2585, 2586, 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, 2614, 2661, 2692, 2693, 2695, 2696, 2697, 2698, 2700, 2701, 2702, 2710, 2712, 2713, 2716, 2717, 2720, 2721, 2724, 2726, 2727, 2728, 2741, 2750, 2751, 2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2760, 2761, 2762, 2763, 2764, 2769, 2770, 2771, 2779, 2780, 2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, 2791, 2793, 2794, 2795, 2796, 2797, 2798, 2799, 2800, 2801, 2810, 2811, 2812, 2813, 2815, 2816, 2817, 2818, 2819, 2820, 2829, 2831, 2833, 2835, 2837, 2838, 2839, 2840, 2843, 2844, 2845, 2846, 2847, 2876, 2879, 2880, 2881, 2888, 2890, 2891, 2902, 2903, 2909, 2910, 2911, 2912, 2913, 2914, 2920, 2921, 2922, 2923, 2924, 2925, 2926, 2927, 2928, 2929, 2930, 2933, 2937, 2938, 2939, 2940, 2941, 2953, 2967, 2968, 2972, 2984, 2985, 2986, 2987, 2988, 2991, 2992, 2993, 2994, 2995, 2999, 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3032, 3036, 3039, 3040, 3041, 3042, 3043, 3044, 3045, 3053, 3054, 3055, 3056, 3057, 3058, 3059, 3060, 3061, 3062, 3063, 3064, 3065, 3066, 3067, 3068, 3069, 3071, 3090, 3091, 3094, 3102, 3103, 3108, 3109, 3110, 3115, 3116, 3128, 3129, 3130, 3131, 3132, 3133, 3134, 3135, 3136, 3137, 3140, 3141, 3142, 3143, 3157, 3170, 3171, 3175, 3176, 3177, 3178, 3181, 3182, 3184, 3185, 3186, 3187, 3191, 3192, 3194, 3195, 3238, 3239, 3240, 3243, 3254, 3255, 3256, 3257, 3265, 3266, 3269, 3270, 3271, 3272, 3274, 3275, 3276, 3277, 3278, 3279, 3280, 3281, 3282, 3283, 3284, 3285, 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, 3294, 3295, 3296, 3297, 3298, 3299, 3300, 3301, 3302, 3303, 3324, 3325, 3326, 3327, 3334, 3335, 3337, 3338, 3339, 3340, 3341, 3342, 3348, 3353, 3356, 3358, 3359, 3360, 3361, 3362, 3363, 3364, 3365, 3366, 3367, 3368, 3369, 3370, 3371, 3372, 3373, 3374, 3375, 3396, 3397, 3399, 3415, 3416, 3417, 3418, 3419, 3420, 3421, 3422, 3423, 3450, 3451, 3452, 3461, 3471, 3475, 3495, 3496, 3497, 3543, 3544, 3547, 3550, 3551, 3552, 3553, 3555, 3557, 3563, 3564, 3565, 3566, 3571, 3572, 3592, 3593, 3594, 3595, 3596, 3597, 3598, 3599, 3600, 3602, 3603, 3605, 3606, 3608, 3609, 3611, 3621, 3622, 3623, 3624, 3625, 3626, 3627, 3630, 3634, 3635, 3636, 3639, 3640, 3641, 3645, 3649, 3651, 3652, 3653, 3654, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662, 3663, 3664, 3665, 3666, 3667, 3668, 3669, 3670, 3671, 3672, 3673, 3674, 3675, 3676, 3677, 3678, 3679, 3680, 3683, 3684, 3685, 3686, 3687, 3688, 3689, 3690, 3691, 3692, 3693, 3694, 3695, 3696, 3697, 3701, 3702, 3703, 3704, 3705, 3719, 3734, 3735, 3739, 3740, 3741, 3745, 3747, 3804, 3806, 3807, 3838, 3839, 3844, 3845, 3846, 3855, 3857, 3859, 3860, 3861, 3862, 3863, 3865, 3866, 3873, 3926, 3931, 3945, 3950, 3970, 3971, 3972, 3973, 3996, 3997, 3999, 4001, 4003, 4004, 4018, 4019, 4023, 4024, 4028, 4029, 4030, 4031, 4036, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050, 4051, 4052, 4053, 4054, 4055, 4056, 4057, 4058, 4059, 4060, 4061, 4062, 4063, 4064, 4065, 4066, 4067, 4068, 4070, 4071, 4073, 4074, 4075, 4076, 4077, 4078, 4079, 4080, 4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, 4094, 4095, 4096, 4097, 4098, 4099, 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4110, 4111, 4112, 4113, 4114, 4115, 4116, 4117, 4136, 4141, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4166, 4167, 4168, 4169, 4170, 4171, 4172, 4173, 4174, 4175, 4177, 4178, 4179, 4180, 4181, 4182, 4183, 4184, 4185, 4188, 4189, 4190, 4191, 4192, 4193, 4194, 4195, 4196, 4197, 4198, 4199, 4200, 4207, 4208, 4209, 4226, 4227, 4228, 4230, 4238, 4250, 4254, 4255, 4256, 4257, 4258, 4275, 4276, 4278, 4280, 4282, 4283, 4285, 4293, 4316, 4329, 4330, 4340, 4341, 4342, 4343, 4344, 4345, 4348, 4349, 4350, 4352, 4365, 4366, 4367, 4368, 4369, 4370, 4371, 4378, 4379, 4380, 4381, 4382, 4400, 4401, 4402, 4406, 4409, 4410, 4411, 4412, 4413, 4424, 4425, 4426, 4427, 4428, 4429, 4430, 4432, 4433, 4434, 4435, 4436, 4437, 4438, 4439, 4440, 4441, 4442, 4454, 4455, 4456, 4457, 4458, 4459, 4460, 4462, 4464, 4465, 4466, 4467, 4468, 4470, 4471, 4479, 4480, 4481, 4482, 4483, 4484, 4485, 4486, 4487, 4488, 4489, 4490, 4491, 4492, 4493, 4494, 4495, 4496, 4497, 4498, 4499, 4500, 4501, 4502, 4503, 4504, 4505, 4506, 4507, 4508, 4509, 4510, 4511, 4512, 4513, 4514, 4515, 4516, 4517, 4518, 4519, 4520, 4521, 4522, 4523, 4524, 4525, 4526, 4527, 4528, 4529, 4530, 4531, 4532, 4533, 4534, 4540, 4544, 4546, 4547, 4549, 4551, 4552, 4553, 4558, 4581, 4586, 4591, 4592, 4593, 4594, 4596, 4597, 4598, 4602, 4603, 4604, 4605, 4606, 4607, 4611, 4612, 4613, 4614, 4615, 4617, 4632, 4633, 4634, 4636, 4637, 4639, 4640, 4641, 4643, 4645, 4646, 4682, 4709, 4710, 4712, 4713, 4718, 4719, 4720, 4721, 4722, 4723, 4724, 4726, 4727, 4728, 4731, 4750, 4757, 4758, 4759, 4760, 4761, 4762, 4763, 4764, 4765, 4766, 4771, 4772, 4779, 4780, 4785, 4786, 4787, 4788, 4790, 4791, 4793, 4794, 4795, 4797, 4804, 4806, 4807, 4809, 4824, 4825, 4826, 4834, 4835, 4838, 4839, 4841, 4847, 4848, 4849, 4850, 4851, 4852, 4853, 4855, 4857, 4858, 4859, 4861, 4862, 4863, 4873, 4874, 4884, 4885, 4905, 4909, 4937, 4939, 4941, 4943, 4945, 4947, 4949, 4951, 4953, 4955, 4957, 4959, 4961, 4963, 4965, 4967, 4969, 4973, 4975, 4977, 4979, 4981, 5006, 5015, 5037, 5038, 5039, 5040, 5041, 5042, 5043, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5054, 5056, 5057, 5059, 5063, 5067, 5074, 5075, 5078, 5079, 5080, 5083, 5088, 5103, 5108, 5140, 5142, 5146, 5147, 5148, 5149, 5150, 5151, 5152, 5153, 5155, 5158, 5160, 5161, 5162, 5163, 5164, 5165, 5166, 5167, 5168, 5206, 5207, 5208, 5209, 5210, 5211, 5212, 5213, 5215, 5216, 5217, 5236, 5243, 5244, 5245, 5246, 5247, 5248, 5249, 5250, 5251, 5252, 5253, 5254, 5255, 5256, 5257, 5258, 5259, 5293, 5298, 5299, 5300, 5311, 5312, 5313, 5315, 5316, 5349, 5352, 5354, 5355, 5361, 5362, 5363, 5364, 5365, 5366, 5367, 5368, 5369, 5370, 5371, 5372, 5373, 5374, 5375, 5376, 5377, 5378, 5379, 5383, 5384, 5400, 5407, 5416, 5417, 5418, 5419, 5428, 5429, 5436, 5438, 5439, 5440, 5441, 5442, 5443, 5444, 5471, 5472, 5473, 5474, 5475, 5476, 5524, 5525, 5602, 5606, 5607, 5608, 5609, 5611, 5620, 5633, 5637, 5713, 5714, 5716, 5718, 5732, 5746, 5748, 5749, 5752, 5753, 5754, 5755, 5756, 5759, 5760, 5761, 5762, 5763, 5776, 5777, 5778, 5796, 5799, 5800, 5812, 5814, 5815, 5816, 5817, 5818, 5819, 5821, 5822, 5823, 5827, 5845, 5846, 5853, 5860, 5862, 5863, 5864, 5865, 5866, 5867, 5868, 5869, 5870, 5907, 6063, 6064, 6066, 6067, 6068, 6075, 6076, 6083, 6085, 6086, 6087, 6095, 6096, 6098, 6099, 6100, 6101, 6102, 6103, 6104, 6106, 6111, 6112, 6113, 6118, 6122, 6124, 6128, 6129, 6130, 6131, 6132, 6143, 6146, 6147, 6197, 6198, 6209, 6213, 6217, 6280, 6281, 6282, 6283, 6284, 6285, 6286, 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, 6296, 6297, 6298, 6299, 6300, 6301, 6303, 6304, 6305, 6361, 6362, 6363, 6364, 6365, 6366, 6367, 6378, 6379, 6380, 6381, 6382, 6383, 6384, 6385, 6386, 6387, 6388, 6389, 6390, 6391, 6392, 6393, 6394, 6395, 6396, 6397, 6398, 6399, 6400, 6401, 6402, 6403, 6404, 6405, 6406, 6407, 6408, 6409, 6424, 6425, 6459, 6462, 6464, 6465, 6468, 6469, 6478, 6479, 6480, 6486, 6487, 6488, 6489, 6490, 6525, 6526, 6527, 6528, 6529, 6530, 6531, 6532, 6554, 6584, 6592, 6593, 6594, 6595, 6600, 6601, 6603, 6605, 6606, 6608, 6609, 6610, 6611, 6616, 6631, 6632, 6633, 6634, 6649, 6650, 6651, 6654, 6655, 6657, 6658, 6659, 6660, 6661, 6662, 6663, 6664, 6665, 6671, 6672, 6673, 6674, 6675, 6676, 6677, 6678, 6679, 6680, 6681, 6682, 6683, 6684, 6685, 6686, 6687, 6688, 6689, 6695, 6696, 6700, 6702, 6703, 6704, 6705, 6706, 6707, 6708, 6709, 6710, 6711, 6712, 6713, 6714, 6715, 6716, 6717, 6718, 6719, 6720, 6723, 6724, 6738, 6739, 6740, 6741, 6742, 6743, 6744, 6745, 6746, 6747, 6748, 6749, 6750, 6751, 6752, 6753, 6754, 6755, 6756, 6757, 6758, 6814, 6816, 6820, 6838, 6839, 6840, 6841, 6842, 6843, 6844, 6845, 6846, 6847, 6848, 6849, 6863, 6864, 6865, 6866, 6867, 6887, 6896, 6897, 6912, 6913, 6914, 6915, 6918, 6919, 6922, 6923, 6926, 6927, 6928, 6929, 6933, 6935, 6939, 6940, 6941, 6978, 6979, 6988, 6989, 6990, 6991, 6992, 6993, 6994, 6997, 6998, 6999, 7000, 7001, 7002, 7003, 7004, 7005, 7023, 7039, 7040, 7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7070, 7071, 7072, 7073, 7074, 7078, 7081, 7082, 7084, 7088, 7095, 7096, 7112, 7113, 7139, 7151, 7181, 7182, 7183, 7184, 7185, 7186, 7187, 7188, 7189, 7190, 7191, 7192, 7193, 7194, 7195, 7196, 7197, 7198, 7199, 7200, 7201, 7202, 7203, 7211, 7227, 7228, 7230, 7232, 7253, 7261, 7264, 7265, 7266, 7267, 7268, 7269, 7270, 7271, 7272, 7273, 7274, 7275, 7276, 7277, 7299, 7301, 7302, 7303, 7304, 7307, 7321, 7363, 7364, 7365, 7366, 7368, 7369, 7376, 7377, 7383, 7392, 7523, 7524, 7528, 7529, 7530, 7531, 7532, 7533, 7534, 7538, 7578, 7579, 7597, 7598, 7599, 7627, 7628, 7629, 7633, 7640, 7650, 7652, 7660, 7745, 7774, 7892, 8136, 8137, 8496, 8497, 8498, 8499, 8500, 8501, 8502, 8525, 8527, 8528, 8529, 8651, 8681, 8682, 8683, 8684, 8685, 8686, 8687, 8688, 8690, 8691, 8692, 8693, 8694, 8695, 8698, 8699, 8701, 8702, 8704, 8705, 8707, 8709, 8710, 8711, 8736, 8767, 8768, 8770, 8778, 8779, 8780, 8781, 8782, 8783, 8784, 8804, 8805, 8820, 8821, 8829, 8830, 8831, 8832, 8833, 8834, 8835, 8836, 8837, 8838, 8839, 8840, 8841, 8842, 8846, 8847, 8848, 8849, 8858, 8859, 8860, 8861, 8862, 8863, 8864, 8865, 8866, 8867, 8868, 8869, 8870, 8871, 8872, 8873, 8874, 8875, 8876, 8877, 8878, 8879, 8901, 8902, 8903, 8904, 8905, 8906, 8907, 8908, 8909, 8939, 8941, 8943, 8953, 8954, 8973, 8975, 8976, 8977, 8978, 8979, 8980, 8981, 8982, 8983, 8984, 8985, 8986, 8987, 8988, 8989, 8990, 8991, 8996, 8997, 8998, 8999, 9000, 9001, 9002, 9003, 9004, 9012, 9013, 9016, 9018, 9021, 9022, 9023, 9024, 9025, 9031, 9032, 9039, 9040, 9044, 9045, 9047, 9048, 9049, 9050, 9051, 9052, 9053, 9054, 9055, 9057, 9058, 9059, 9060, 9061, 9062, 9063, 9064, 9065, 9068, 9069, 9070, 9071, 9072, 9073, 9074, 9086, 9087, 9091, 9092, 9095, 9096, 9097, 9098, 9099, 9100, 9104, 9105, 9109, 9110, 9111, 9119, 9121, 9136, 9141, 9151, 9152, 9212, 9213, 9214, 9215, 9216, 9217, 9218, 9219, 9220, 9221, 9222, 9223, 9224, 9226, 9227, 9304, 9305, 9306, 9307, 9308, 9309, 9310, 9350, 9359, 9360, 9361, 9362, 9363, 9364, 9365, 9366, 9367, 9368, 9429, 9497, 9498, 9499, 9502, 9503, 9504, 9516, 9521, 9523, 9526, 9534, 9535, 9536, 9537, 9542, 9544, 9547, 9549, 9551, 9552, 9553, 9554, 9555, 9557, 9558, 9561, 9562, 9565, 9566, 9568, 9570, 9571, 9572, 9573, 9575, 9579, 9583, 9587, 9588, 9591, 9595, 9596, 9597, 9598, 9599, 9600, 9601, 9602, 9603, 9604, 9605, 9606, 9607, 9608, 9609, 9610, 9622, 9623, 9625, 9627, 9629, 9631, 9633, 9635, 9637, 9648, 9649, 9676, 9677, 9678, 9679, 9680, 9685, 9686, 9687, 9688, 9689, 9690, 9691, 9692, 9693, 9694, 9695, 9696, 9697, 9698, 9699, 9700, 9701, 9702, 9703, 9704, 9705, 9706, 9707, 9708, 9709, 9710, 9711, 9712, 9713, 9714, 9715, 9716, 9717, 9718, 9721, 9724, 9725, 9726, 9727, 9728, 9729, 9730, 9734, 9735, 9736, 9738, 9739, 9754, 9755, 9756, 9868, 9869, 9870, 9872, 9873, 9874, 9875, 9876, 9879, 9880, 9883, 9884, 9885, 9886, 9907, 9908, 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, 9976, 9977, 9978, 9984, 9986, 9988, 9990, 9992, 9993, 9994, 9995, 9996, 9997, 9998, 9999, 10000, 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011, 10012, 10013, 10014, 10015, 10016, 10017, 10018, 10019, 10020, 10021, 10022, 10023, 10044, 10045, 10069, 10070, 10071, 10072, 10073, 10074, 10075, 10076, 10077, 10078, 10080, 10081, 10082, 10100, 10101, 10102, 10103, 10104, 10105, 10106, 10107, 10108, 10109, 10110, 10111, 10112, 10113, 10114, 10115, 10119, 10132, 10172, 10173, 10180, 10182, 10184, 10186, 10202, 10210, 10211, 10212, 10213, 10222, 10223, 10224, 10225, 10226, 10227, 10228, 10246, 10247, 10248, 10249, 10250, 10251, 10252, 10253, 10254, 10255, 10256, 10257, 10258, 10260, 10261, 10262, 10265, 10266, 10267, 10268, 10269, 10270, 10271, 10288, 10289, 10290, 10291, 10292, 10293, 10294, 10295, 10296, 10297, 10298, 10299, 10300, 10301, 10303, 10305, 10312, 10313, 10335, 10336, 10337, 10338, 10339, 10340, 10341, 10342, 10343, 10344, 10345, 10346, 10347, 10348, 10355, 10374, 10377, 10416, 10417, 10418, 10434, 10435, 10436, 10452, 10453, 10454, 10455, 10456, 10457, 10458, 10459, 10466, 10467, 10468, 10470, 10471, 10473, 10475, 10476, 10477, 10478, 10482, 10483, 10485, 10490, 10491, 10492, 10493, 10494, 10495, 10498, 10499, 10501, 10502, 10503, 10504, 10505, 10513, 10516, 10518, 10524, 10530, 10532, 10534, 10535, 10537, 10538, 10542, 10543, 10544, 10545, 10546, 10550, 10551, 10564, 10565, 10566, 10567, 10568, 10569, 10570, 10571, 10572, 10573, 10574, 10575, 10576, 10577, 10578, 10579, 10580, 10583, 10584, 10587, 10588, 10589, 10590, 10602, 10603, 10604, 10605, 10606, 10607, 10608, 10609, 10610, 10611, 10612, 10613, 10614, 10615, 10616, 10617, 10618, 10619, 10620, 10621, 10622, 10623, 10627, 10646, 10647, 10650, 10651, 10653, 10654, 10662, 10663, 10704, 10705, 10706, 10709, 10711, 10715, 10716, 10717, 10718, 10719, 10720, 10721, 10722, 10724, 10725, 10726, 10727, 10728, 10729, 10730, 10731, 10732, 10733, 10734, 10735, 10737, 10738, 10739, 10740, 10741, 10742, 10743, 10744, 10745, 10747, 10749, 10753, 10755, 10756, 10758, 10768, 10793, 10794, 10795, 10796, 10797, 10798, 10799, 10800, 10801, 10822, 10823, 10824, 10825, 10830, 10836, 10841, 10842, 10843, 10844, 10845, 10846, 10847, 10848, 10849, 10850, 10851, 10852, 10853, 10854, 10855, 10856, 10857, 10858, 10865, 10866, 10867, 10868, 10869, 10870, 10871, 10872, 10873, 10876, 10881, 10883, 10885, 10886, 10887, 10898, 10899, 10913, 10914, 10915, 10916, 10961, 10963, 10968, 10969, 10970, 10971, 10972, 10973, 10974, 10975, 10977, 10980, 10981, 10982, 10987, 10988, 10990, 10992, 10993, 10994, 10995, 11007, 11008, 11010, 11011, 11013, 11019, 11020, 11021, 11022, 11023, 11024, 11025, 11026, 11028, 11029, 11030, 11031, 11033, 11041, 11042, 11043, 11044, 11048, 11051, 11052, 11053, 11057, 11060, 11061, 11062, 11063, 11064, 11065, 11066, 11067, 11068, 11069, 11070, 11071, 11088, 11089, 11091, 11141, 11142, 11143, 11144, 11145, 11146, 11147, 11148, 11150, 11151, 11152, 11153, 11154, 11155, 11156, 11157, 11158, 11159, 11160, 11161, 11162, 11163, 11164, 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, 11220, 11227, 11228, 11229, 11230, 11231, 11232, 11233, 11234, 11235, 11236, 11237, 11238, 11258, 11283, 11309, 11310, 11337, 11342, 11359, 11380, 11387, 11388, 11481, 11482, 11486, 11487, 11489, 11490, 11491, 11492, 11493, 11495, 11524, 11541, 11542, 11543, 11544, 11545, 11547, 11553, 11556, 11557, 11558, 11562, 11569, 11571, 11572, 11573, 11574, 11577, 11578, 11579, 11588, 11589, 11590, 11594, 11595, 11596, 11597, 11598, 11599, 11600, 11601, 11602, 11603, 11604, 11617, 11618, 11619, 11620, 11622, 11623, 11625, 11656, 11657, 11658, 11659, 11660, 11661, 11662, 11663, 11664, 11665, 11666, 11667, 11671, 11672, 11673, 11674, 11675, 11676, 11677, 11678, 11679, 11680, 11681, 11682, 11683, 11684, 11685, 11686, 11687, 11690, 11726, 11727, 11728, 11729, 11731, 11732, 11733, 11735, 11739, 11782, 11783, 11784, 11785, 11786, 11787, 11788, 11789, 11790, 11791, 11792, 11793, 11794, 11795, 11796, 11797, 11798, 11799, 11802, 11803, 11804, 11805, 11806, 11807, 11809, 11810, 11811, 11812, 11813, 11814, 11815, 11816, 11817, 11871, 11873, 11878, 11879, 11880, 11881, 11885, 11893, 11900, 11901, 11902, 11904, 11908, 11909, 11910, 11911, 11919, 11920, 11921, 11922, 11924, 11931, 11933, 11942, 11944, 11945, 11946, 11947, 11948, 11949, 11950, 11951, 11956, 11957, 11958, 11960, 11963, 11964, 11965, 11966, 11967, 11968, 11969, 11970, 11971, 11973, 11974, 11975, 11976, 11977, 11978, 11979, 11980, 11981, 11982, 11983, 11984, 11985, 11986, 11987, 11988, 11989, 11991, 11993, 11995, 11999, 12000, 12001, 12002, 12003, 12004, 12005, 12006, 12007, 12008, 12009, 12010, 12011, 12012, 12013, 12014, 12015, 12016, 12017, 12019, 12021, 12022, 12023, 12024, 12025, 12026, 12027, 12028, 12029, 12030, 12031, 12033, 12035, 12036, 12055, 12057, 12059, 12106, 12107, 12108, 12114, 12120, 12122, 12126, 12127, 12152, 12153, 12154, 12155, 12156, 12161, 12162, 12163, 12164, 12169, 12171, 12174, 12175, 12183, 12187, 12188, 12189, 12190, 12191, 12216, 12217, 12226, 12227, 12228, 12229, 12230, 12232, 12233, 12234, 12235, 12237, 12238, 12239, 12258, 12260, 12261, 12265, 12267, 12268, 12269, 12270, 12272, 12274, 12276, 12277, 12278, 12280, 12282, 12283, 12284, 12285, 12288, 12289, 12290, 12291, 12292, 12303, 12305, 12306, 12307, 12308, 12309, 12310, 12311, 12312, 12313, 12314, 12315, 12316, 12317, 12318, 12319, 12322, 12323, 12324, 12325, 12326, 12327, 12328, 12329, 12330, 12331, 12332, 12333, 12334, 12335, 12336, 12337, 12338, 12339, 12340, 12341, 12342, 12343, 12344, 12345, 12346, 12347, 12348, 12349, 12350, 12361, 12362, 12363, 12364, 12365, 12366, 12367, 12368, 12375, 12376, 12377, 12378, 12379, 12380, 12381, 12382, 12383, 12384, 12398, 12400, 12413, 12414, 12415, 12418, 12419, 12420, 12422, 12423, 12429, 12431, 12436, 12438, 12441, 12442, 12445, 12446, 12449, 12450, 12453, 12454, 12455, 12456, 12457, 12458, 12463, 12465, 12466, 12467, 12468, 12469, 12470, 12471, 12472, 12473, 12474, 12475, 12476, 12477, 12478, 12479, 12480, 12481, 12482, 12489, 12490, 12492, 12493, 12494, 12495, 12496, 12504, 12505, 12509, 12510, 12511, 12513, 12529, 12531, 12532, 12542, 12543, 12547, 12548, 12551, 12552, 12563, 12564, 12565, 12567, 12569, 12573, 12575, 12583, 12584, 12589, 12591, 12592, 12593, 12594, 12595, 12598, 12601, 12602, 12603, 12606, 12610, 12611, 12614, 12615, 12616, 12618, 12619, 12620, 12621, 12625, 12626, 12628, 12629, 12633, 12640, 12641, 12642, 12643, 12645, 12651, 12652, 12653, 12657, 12658, 12661, 12662, 12663, 12664, 12665, 12667, 12668, 12669, 12670, 12671, 12672, 12673, 12674, 12675, 12676, 12677, 12678, 12689, 12690, 12691, 12737, 12738, 12739, 12740, 12741, 12742, 12743, 12745, 12746, 12747, 12748, 12749, 12754, 12755, 12756, 12757, 12758, 12759, 12760, 12761, 12762, 12764, 12765, 12766, 12767, 12768, 12769, 12770, 12771, 12772, 12773, 12774, 12776, 12777, 12780, 12781, 12784, 12785, 12786, 12787, 12804, 12806, 12810, 12814, 12828, 12829, 12830, 12832, 12835, 12837, 12849, 12850, 12851, 12852, 12853, 12854, 12855, 12856, 12857, 12858, 12859, 12860, 12875, 12876, 12877, 12878, 12879, 12880, 12881, 12882, 12900, 12906, 12907, 12908, 12909, 12910, 12911, 12912, 12913, 12914, 12915, 12916, 12917, 12919, 12920, 12921, 12923, 12982, 13034, 13035, 13036, 13037, 13038, 13039, 13040, 13041, 13042, 13043, 13044, 13045, 13046, 13047, 13048, 13049, 13050, 13051, 13052, 13053, 13054, 13055, 13065, 13066, 13068, 13074, 13075, 13076, 13077, 13078, 13079, 13080, 13081, 13082, 13083, 13084, 13085, 13086, 13087, 13088, 13089, 13090, 13091, 13092, 13093, 13094, 13095, 13096, 13097, 13098, 13099, 13100, 13101, 13102, 13103, 13104, 13105, 13106, 13107, 13108, 13109, 13110, 13111, 13112, 13113, 13114, 13115, 13116, 13117, 13118, 13119, 13120, 13121, 13122, 13123, 13124, 13125, 13126, 13127, 13128, 13129, 13130, 13131, 13132, 13133, 13134, 13135, 13136, 13137, 13138, 13139, 13140, 13141, 13142, 13143, 13144, 13145, 13146, 13147, 13148, 13149, 13150, 13179, 13180, 13181, 13182, 13183, 13184, 13185, 13186, 13187, 13188, 13189, 13190, 13192, 13193, 13194, 13217, 13218, 13219, 13220, 13221, 13222, 13223, 13225, 13226, 13227, 13228, 13229, 13230, 13231, 13232, 13233, 13234, 13235, 13236, 13237, 13238, 13239, 13240, 13241, 13242, 13243, 13244, 13245, 13246, 13247, 13248, 13249, 13250, 13251, 13252, 13253, 13254, 13270, 13271, 13276, 13285, 13288, 13301, 13325, 13326, 13350, 13351, 13353, 13354, 13355, 13356, 13357, 13358, 13359, 13360, 13361, 13362, 13363, 13364, 13365, 13366, 13367, 13368, 13369, 13370, 13371, 13372, 13373, 13375, 13376, 13377, 13378, 13379, 13380, 13381, 13382, 13383, 13384, 13410, 13412, 13413, 13417, 13493, 13494, 13512, 13513, 13514, 13537, 13538, 13540, 13541, 13543, 13544, 13547, 13548, 13560, 13564, 13565, 13568, 13569, 13573, 13584, 13585, 13633, 13646, 13647, 13648, 13649, 13650, 13651, 13652, 13654, 13657, 13658, 13659, 13662, 13691, 13692, 13693, 13694, 13695, 13710, 13711, 13740, 13742, 13743, 13744, 13745, 13746, 13747, 13748, 13749, 13750, 13751, 13752, 13753, 13754, 13755, 13756, 13758, 13759, 13760, 13798, 13799, 13800, 13801, 13802, 13803, 13804, 13805, 13806, 13807, 13808, 13809, 13810, 13811, 13812, 13813, 13818, 13819, 13820, 13821, 13822, 13823, 13824, 13825, 13828, 13829, 13830, 13831, 13842, 13843, 13844, 13845, 13936, 13938, 13948, 13974, 13980, 14010, 14061, 14063, 14080, 14081, 14082, 14098, 14103, 14125, 14126, 14127, 14128, 14149, 14150, 14151, 14152, 14164, 14165, 14177, 14179, 14180, 14181, 14182, 14183, 14184, 14185, 14186, 14189, 14190, 14191, 14192, 14193, 14194, 14195, 14196, 14197, 14198, 14199, 14200, 14207, 14209, 14220, 14221, 14222, 14223, 14224, 14228, 14232, 14233, 14234, 14235, 14236, 14238, 14239, 14240, 14242, 14279, 14293, 14298, 14299, 14300, 14301, 14302, 14308, 14309, 14310, 14318, 14319, 14320, 14338, 14339, 14340, 14341, 14346, 14347, 14350, 14351, 14352, 14353, 14356, 14357, 14358, 14359, 14360, 14361, 14362, 14363, 14364, 14365, 14366, 14367, 14368, 14388, 14409, 14413, 14419, 14519, 14520, 14522, 14524, 14545, 14550, 14554, 14555, 14557, 14560, 14564, 14565, 14566, 14567, 14568, 14569, 14571, 14572, 14573, 14574, 14575, 14576, 14577, 14578, 14579, 14580, 14581, 14582, 14583, 14584, 14585, 14586, 14588, 14589, 14590, 14591, 14592, 14593, 14594, 14595, 14596, 14597, 14598, 14599, 14600, 14601, 14602, 14603, 14604, 14605, 14606, 14607, 14608, 14609, 14610, 14611, 14612, 14613, 14614, 14615, 14616, 14617] +Linked NPCs: Hans (0), Man (1), Man (2), Man (3), Woman (4), Woman (5), Woman (6), Farmer (7), Thief (8), Guard (9), Tramp (11), Barbarian (12), Wizard (13), Druid (14), Warrior woman (15), Man (16), Al-Kharid warrior (18), White Knight (19), Paladin (20), Hero (21), Forester (22), Knight of Ardougne (23), Man (24), Woman (25), Knight of Ardougne (26), Archer (27), Zookeeper (28), Musician (29), Musician (30), Priest (31), Guard (32), Door man (33), Watchman (34), Soldier (35), Wyson the gardener (36), Sigbert the Adventurer (37), Shipyard worker (38), Shipyard worker (39), Banker (44), Banker (45), null (70), Earth warrior (124), Ice warrior (125), Ice warrior (145), Shadow warrior (158), Man (170), Dark wizard (172), Invrigar the Necromancer (173), Dark wizard (174), Mugger (175), Witch (176), Witch (177), Black Knight (178), Black Knight (179), Highwayman (180), Chaos druid (181), Pirate (182), Pirate (183), Pirate (184), Pirate (185), Thug (186), Rogue (187), Monk of Zamorak (188), Monk of Zamorak (189), Monk of Zamorak (190), Tribesman (191), Dark warrior (192), Chaos druid warrior (193), Necromancer (194), Bandit (195), Guard Bandit (196), Barbarian guard (197), Guildmaster (198), Gunthor the Brave (199), Lord Daquarius (200), Jailer (201), Black Heather (202), Donny the Lad (203), Speedy Keith (204), Salarin the Twisted (205), Grail Maiden (210), Sir Percival (211), King Percival (212), Peasant (214), Peasant (215), High Priest (216), Crone (217), Galahad (218), Fisherman (219), The Fisher King (220), Monk (222), Brother Kojo (223), Bonzo (225), null (226), Morris (227), Big Dave (228), Joshua (229), Grandpa Jack (230), Forester (231), Renegade Knight (237), Sir Lancelot (239), Sir Gawain (240), Sir Kay (241), Sir Bedivere (242), Sir Tristram (243), Sir Pelleas (244), Sir Lucan (245), Beefy Bill (246), Sir Mordred (247), Morgan Le Faye (248), Merlin (249), The Lady of the Lake (250), King Arthur (251), Beggar (252), Khazard Guard (253), Khazard Guard (254), Khazard Guard (255), Khazard Guard (256), Khazard Guard (257), null (258), Angor (259), Kelvin (260), Joe (261), Slave fighter (262), Hengrad (263), null (267), Local (268), Lucien (272), Lucien (273), Guardian of Armadyl (274), Guardian of Armadyl (275), Winelda (276), Fire Warrior of Lesarkus (277), Cook (278), Brother Omad (279), Brother Cedric (280), Monk (281), Thief (282), Head Thief (283), Veronica (285), Professor Oddenstein (286), Ernest (287), Councillor Halgrive (289), Doctor Orbon (290), Farmer Brumty (291), Sedridor (300), Hadley (302), Gerald (303), Almera (304), Hetty (307), Master fisher (308), null (335), Da Vinci (336), Da Vinci (337), Chancy (338), Chancy (339), Hops (340), Hops (341), Guidor's wife (342), Guidor (343), Guard (344), Guard (345), Guard (346), Mourner (347), Mourner (348), Kilron (349), Omart (350), Man (351), Woman (352), Woman (353), Woman (354), Mourner (357), Priest (358), Man (359), Woman (360), Woman (361), Woman (362), Woman (363), King Lathas (364), Paladin (365), Jerico (366), Chemist (367), Guard (368), Mourner (369), Mourner (370), Mourner (371), Mourner (372), Nurse Sarah (373), Redbeard Frank (375), Luthas (379), Customs officer (380), null (381), Barbarian guard (384), Seer (388), Thormac (389), Legends guard (398), Legends guard (399), Jungle forester (401), Jungle forester (402), Eluned (404), null (405), Lokar Searunner (406), Captain Bentley (407), Captain Bentley (408), Genie (409), Mysterious Old Man (410), null (413), null (414), null (431), Cyrisus (432), Cyrisus (433), Cyrisus (434), Fallen Man (435), Fallen Man (436), Cap'n Izzy No-Beard (437), Jail guard (447), Jail guard (448), Jail guard (449), Seth Groats (452), Suit of armour (453), Sanfew (454), Kaqemeex (455), Father Aereck (456), Restless ghost (457), Father Urhney (458), Skeleton warlock (459), Wizard Frumscone (460), Magic Store owner (461), Wizard Distentor (462), Murphy (463), Murphy (464), Murphy (465), Murphy (466), Khazard trooper (475), Khazard trooper (476), Khazard warlord (477), Khazard commander (478), Observatory professor (488), Banker (494), Banker (495), Banker (496), Banker (497), Banker (499), Mosol Rei (500), Hajedy (510), Vigroy (511), Kaleb Paramaya (512), Yohnus (513), Seravel (514), Yanni Salika (515), Obli (516), Fernahei (517), Captain Shanks (518), Bob (519), Shopkeeper (520), Shop assistant (521), Shopkeeper (522), Shop assistant (523), Shopkeeper (524), Shop assistant (525), Shopkeeper (526), Shop assistant (527), Shopkeeper (528), Shop assistant (529), Shopkeeper (530), Shop assistant (531), Valaine (536), Scavvo (537), Peksa (538), Silk trader (539), Gem trader (540), Zeke (541), Louie Legs (542), Karim (543), Ranael (544), Dommik (545), Zaff (546), Baraek (547), Thessalia (548), Horvik (549), Lowe (550), Shopkeeper (551), Shop assistant (552), Aubury (553), Fancy-dress shop owner (554), Shopkeeper (555), Grum (556), Wydin (557), Gerrant (558), Brian (559), Jiminua (560), Shopkeeper (561), Candle-maker (562), Arhein (563), Zambo (568), Silver merchant (569), Gem merchant (570), Baker (571), Spice seller (572), Fur trader (573), Silk merchant (574), Hickton (575), Harry (576), Cassie (577), Frincos (578), Flynn (580), Wayne (581), Betty (583), Herquin (584), Rommik (585), Gaius (586), Jatix (587), Davon (588), Zenesha (589), Aemad (590), Kortan (591), Roachey (592), Frenita (593), Tea seller (595), Fat Tony (596), Noterazzo (597), Hairdresser (598), Make-over Mage (599), Sir Vyvin (605), Squire (606), Gunnjorn (607), Sir Amik Varze (608), Fortress Guard (609), Black Knight Captain (610), Witch (611), Digsite workman (613), Doug Deeping (614), Student (615), Student (616), Student (617), Examiner (618), Archaeological expert (619), Panning guide (620), Juliet (637), Apothecary (638), Romeo (639), Father Lawrence (640), Charlie the Tramp (641), Katrine (642), Weaponsmaster (643), Straven (644), Jonny the beard (645), Curator Haig Halen (646), null (647), King Roald (648), Archer (649), Warrior (650), Monk (651), Wizard (652), Cave monk (656), Monk of Entrana (657), Monk of Entrana (658), Party Pete (659), Knight (660), Megan (661), Lucy (662), Man (663), null (664), Caleb (666), Johnathon (668), Charlie (673), Foreman (674), Shipyard worker (675), Guard (678), Ranging Guild Doorman (679), Leatherworker (680), Held vampyre juvinate (681), Armour salesman (682), Bow and Arrow salesman (683), Tower Advisor (684), Tower Advisor (685), Tower Advisor (686), Tower Advisor (687), Tower Archer (688), Tower Archer (689), Tower Archer (690), Tower Archer (691), Tribal Weapon Salesman (692), Ticket Merchant (694), Bailey (695), Caroline (696), Holgart (698), Holgart (699), Holgart (700), Kent (701), Fisherman (702), Fisherman (703), Fisherman (704), Melee instructor (705), Wizard Mizgog (706), Wizard Grayzag (707), Alrena (710), Bravek (711), Carla (712), Clerk (713), Edmond (714), null (715), Head mourner (716), Mourner (717), Mourner (718), Mourner (719), Recruiter (720), Ted Rehnison (721), Martha Rehnison (722), Jethick (725), Registrar 1 (726), Registrar 2 (727), Man (728), Man (729), Registrar 3 (730), Bartender (731), Bartender (732), Bartender (735), Emily (736), Bartender (737), Bartender (738), Bartender (739), Trufitus (740), Duke Horacio (741), Ned (743), Klarense (744), Oracle (746), Oziach (747), null (748), Melzar the Mad (753), Morgan (755), Dr Harlow (756), Prince Brand (757), Fred the Farmer (758), Gertrude (780), Civilian (785), Civilian (786), Civilian (787), Garv (788), Grubor (789), Seth (791), Grip (792), Alfonse the waiter (793), Charlie the cook (794), Ice Queen (795), Achietties (796), Helemos (797), Velrak the explorer (798), Pirate Guard (799), Abbot Langley (801), Brother Jered (802), Monk (803), Tanner (804), Master Crafter (805), Donovan the Family Handyman (806), Pierre (807), Hobbes (808), Louisa (809), Mary (810), Stanford (811), Guard (812), Gossip (813), Anna (814), Bob (815), Carol (816), David (817), Elizabeth (818), Frank (819), Poison Salesman (820), Ana (822), Female slave (824), Male slave (825), Escaping slave (826), Rowdy slave (827), Mercenary Captain (830), Captain Siad (831), Al Shabim (832), Bedabin Nomad (833), Bedabin Nomad Guard (834), Irena (835), Shantay (836), Shantay Guard (837), Shantay Guard (838), Mine cart driver (841), Rowdy Guard (842), RPDT employee (843), null (844), Horacio (845), Kangai Mau (846), Head chef (847), null (871), Watchtower Wizard (872), Tower guard (877), Colonel Radick (878), Traiborn (881), Gypsy Aris (882), Sir Prysin (883), Captain Rovin (884), Ceril Carnillean (885), Claus the chef (886), Guard (887), Henryeta Carnillean (889), Butler Jones (890), Alomone (891), Hazeel (892), Clivet (893), Hazeel Cultist (894), Nora T. Hagg (896), Gundai (902), Lundail (903), Chamber guardian (904), Kolodion (905), Kolodion (906), Kolodion (907), Battle mage (912), Battle mage (913), Leela (915), Joe (916), Jail guard (917), Ned (918), Lady Keli (919), Prince Ali (920), Prince Ali (921), Aggie (922), Hassan (923), Osman (924), Border Guard (925), Border Guard (926), Gujuo (928), Ungadulu (929), Ungadulu (930), Jungle savage (931), Fionella (932), Siegfried Erkle (933), Viyeldi (935), Master chef (942), Survival expert (943), Combat instructor (944), RuneScape guide (945), Magic instructor (946), Financial advisor (947), Mining instructor (948), Quest guide (949), Banker (953), Brother Brace (954), Mubariz (957), Fadli (958), A'abla (959), Sabreen (960), Surgeon General Tafani (961), Jaraah (962), Zahwa (963), Ima (964), Sabeil (965), Jadid (966), Dalal (967), Afrah (968), Jeed (969), Diango (970), Chadwell (971), Koftik (972), Koftik (973), Koftik (974), Koftik (975), Koftik (976), Slave (979), Slave (980), Slave (981), Slave (982), Slave (983), Slave (984), Slave (985), Sir Jerro (988), Sir Carl (989), Sir Harry (990), Half-soulless (991), Kardia (992), Dark mage (1001), Disciple of Iban (1002), Lord Iban (1003), Zamorak wizard (1007), Hamid (1008), Vampire (1023), Banker (1036), Rufus (1038), Barker (1039), Fidelio (1040), Sbott (1041), Roavar (1042), Monk of Zamorak (1044), Monk of Zamorak (1045), Monk of Zamorak (1046), null (1047), null (1048), Drezel (1049), Filliman Tarlock (1050), Nature Spirit (1051), Ulizius (1054), Pirate Jackie the Fruit (1055), Mime (1056), Strange watcher (1057), Strange watcher (1058), Strange watcher (1059), Denulth (1060), Sergeant (1061), Sergeant (1062), Soldier (1063), Soldier (1064), Soldier (1065), Soldier (1066), Soldier (1067), Soldier (1068), Soldier (1069), Saba (1070), null (1071), Eadburg (1072), Archer (1073), Archer (1074), Archer (1075), Guard (1076), Guard (1077), Harold (1078), Tostig (1079), Eohric (1080), Servant (1081), Dunstan (1082), Wistan (1083), Breoca (1084), Ocga (1085), Man (1086), Penda (1087), Hygd (1088), Ceolburg (1089), Hild (1090), White Knight (1092), Eadgar (1113), Godric (1114), Weird Old Man (1152), Timfraku (1162), Tiadeche (1163), Tiadeche (1164), Tinsay (1165), Tinsay (1166), Tamayu (1167), Tamayu (1168), Tamayu (1169), Tamayu (1170), Lubufu (1171), Lord Iorwerth (1182), Elf warrior (1183), Elf warrior (1184), Elven city guard (1185), Idris (1186), Essyllt (1187), Morvran (1188), Elf Tracker (1199), Tyras guard (1200), Elf warrior (1201), Arianwyn (1202), Tyras guard (1203), Tyras guard (1204), null (1205), Tyras guard (1206), null (1207), Quartermaster (1208), Koftik (1209), King's messenger (1210), Tegid (1213), Parroty Pete (1216), Gardener (1217), Ghoul (1218), Vampire (1220), Vampire (1223), Vampire (1225), Bedabin Nomad Fighter (1239), Afflicted(Ulsquire) (1251), Ulsquire Shauncy (1252), Afflicted(Razmire) (1253), Razmire Keelgan (1254), Mort'ton Local (1255), Mort'ton Local (1256), Afflicted (1257), Afflicted (1258), Mort'ton local (1259), Mort'ton local (1260), Afflicted (1261), Afflicted (1262), Wizard (1263), Saradomin wizard (1264), Olaf the Bard (1269), Fossegrimen (1273), Ospak (1274), Styrmir (1275), Torbrund (1276), Fridgeir (1277), Longhall Bouncer (1278), The Draugen (1279), Sigli the Huntsman (1281), Sigmund The Merchant (1282), Swensen the Navigator (1283), Bjorn (1284), Eldgrim (1285), Manni the Reveller (1286), Council workman (1287), Peer the Seer (1288), Thorvald the Warrior (1289), Koschei the deathless (1290), Koschei the deathless (1291), Koschei the deathless (1292), Koschei the deathless (1293), Brundt the Chieftain (1294), Guard (1296), Guard (1297), Town Guard (1298), Town Guard (1299), Thora the Barkeep (1300), Yrsa (1301), Fisherman (1302), Skulgrimen (1303), Sailor (1304), Agnar (1305), Freidir (1306), Borrokar (1307), Lanzig (1308), Pontak (1309), Freygerd (1310), Lensa (1311), Jennella (1312), Sassilik (1313), Inga (1314), Fish monger (1315), Fur trader (1316), Market Guard (1317), Warrior (1318), Jossik (1334), Jossik (1335), Larrissa (1336), Larrissa (1337), Sam (1357), Rachael (1358), Banker (1360), Arnor (1361), Haming (1362), Moldof (1363), Helga (1364), Matilda (1365), Ashild (1366), Skraeling (1367), Skraeling (1368), Fishmonger (1369), Greengrocer (1370), null (1373), Guard (1374), Advisor Ghrim (1375), Derrik (1376), Farmer (1377), Flower Girl (1378), Ragnar (1379), Einar (1380), Alrik (1381), Thorhild (1382), Halla (1383), Yrsa (1384), Sailor (1385), Rannveig (1386), Thora (1387), Valgerd (1388), Skraeling (1389), Broddi (1390), Skraeling (1391), Ragnvald (1392), Fishmonger (1393), Greengrocer (1394), Lumberjack Leif (1395), Miner Magnus (1396), Fisherman Frodi (1397), Gardener Gunnhild (1398), Cook's brother (1430), Monkey minder (1469), Foreman (1470), Forester (1508), Woman-at-arms (1509), Apprentice (1510), Ranger (1511), Adventurer (1512), Mage (1513), Hiylik Myna (1514), null (1515), null (1516), null (1517), null (1518), null (1519), null (1520), Lanthus (1526), Zealot (1528), Santa (1552), Cyreg Paddlehorn (1567), Curpile Fyod (1568), Veliaf Hurtz (1569), Sani Piliu (1570), Harold Evans (1571), Radigad Ponfit (1572), Polmafi Ferdygris (1573), Ivan Strom (1574), Vanstrom Klause (1579), Vanstrom Klause (1580), Vanstrom Klause (1581), Saniboch (1595), null (1596), Vannaka (1597), null (1599), Infernal Mage (1643), Infernal Mage (1644), Infernal Mage (1645), Infernal Mage (1646), Infernal Mage (1647), Robe Store owner (1658), null (1666), null (1667), null (1668), null (1669), Gardener Ghost (1675), null (1679), Islwyn (1680), Velorina (1683), Necrovarus (1684), Gravingas (1685), Ghost disciple (1686), null (1687), Robin (1694), Old crone (1695), Old man (1696), Ghost villager (1697), Tortured soul (1698), Ghost shopkeeper (1699), Ghost innkeeper (1700), Ghost farmer (1701), Ghost banker (1702), Ghost sailor (1703), Ghost captain (1704), Ghost captain (1705), Ghost guard (1706), Johanhus Ulsbrecht (1709), H.A.M. Guard (1710), H.A.M. Guard (1711), H.A.M. Guard (1712), H.A.M. Deacon (1713), H.A.M. Member (1714), H.A.M. Member (1715), H.A.M. Member (1716), H.A.M. Member (1717), Jimmy the Chisel (1718), Sigmund (1730), Sigmund (1731), null (1732), Sigmund (1733), Sigmund (1734), Sigmund (1735), Sigmund (1736), Sigmund (1737), Sigmund (1738), Milton the Miller (1739), H.A.M. guard (1740), H.A.M. guard (1741), H.A.M. agent Brian (1742), H.A.M. agent William (1743), H.A.M. agent Charles (1744), H.A.M. agent Sam (1745), H.A.M. agent Christopher (1746), H.A.M. agent Daniel (1747), H.A.M. agent Michael (1748), Merchant Walton (1750), Farmer (1757), Farmer (1758), Farmer (1759), Farmer (1760), Farmer (1761), Ilfeen (1777), William (1778), Ian (1779), Larry (1780), Darren (1781), Edward (1782), Richard (1783), Neil (1784), Edmond (1785), Simon (1786), Sam (1787), H.A.M. agent Walton (1791), null (1792), Tassie Slipcast (1793), Phantuwti Fanstuwi Farsight (1798), Tindel Marchant (1799), Petra Fiyed (1801), Guard (1805), Guard (1806), Hamal the Chieftain (1807), Ragnar (1808), Svidi (1809), Jokul (1810), Camp dweller (1814), Camp dweller (1815), Camp dweller (1816), Camp dweller (1817), Camp dweller (1818), Candle seller (1834), Brian (1860), Ranged instructor (1861), Ali Morrisane (1862), Drunken Ali (1863), Ali the Barman (1864), Ali the Kebab seller (1865), Market seller (1866), Ali the Camel Man (1867), null (1869), Ali the Mayor (1870), Ali the Hag (1871), Ali the Snake Charmer (1872), null (1876), null (1877), Bandit Leader (1878), null (1879), Bandit (1880), Bandit (1881), Sir Palomedes (1882), Sir Palomedes (1883), Trobert (1884), Bandit champion (1885), Cowardly Bandit (1886), null (1887), Villager (1888), Villager (1889), Villager (1890), null (1891), Villager (1892), Villager (1893), Villager (1894), null (1895), Villager (1896), Villager (1897), Villager (1898), null (1899), null (1900), Ali the Operator (1902), null (1903), Menaphite Thug (1904), Menaphite Thug (1905), Tough Guy (1906), Elissa (1912), Kamil (1913), Dessous (1914), Dessous (1915), Ruantun (1916), Bandit shopkeeper (1917), Archaeologist (1918), Stranger (1919), Malak (1920), Bartender (1921), null (1922), Eblis (1923), null (1924), Eblis (1925), Bandit (1926), Bandit (1927), Bandit (1928), Bandit (1929), Bandit (1930), Bandit (1931), Rasolo (1972), Damis (1974), Damis (1975), Fareed (1977), Slave (1978), Slave (1979), Embalmer (1980), Carpenter (1981), Raetul (1982), Siamun (1983), null (1984), null (1985), High Priest (1986), null (1987), Priest (1988), Priest (1989), Possessed Priest (1991), null (2002), null (2003), null (2004), Wanderer (2005), Wanderer (2006), Het (2007), Apmeken (2008), Scabaras (2009), Crondis (2010), null (2011), Icthlarin (2012), Strange Old Man (2024), Ahrim the Blighted (2025), Dharok the Wretched (2026), Guthan the Infested (2027), Karil the Tainted (2028), Torag the Corrupted (2029), Verac the Defiled (2030), Zavistic Rarve (2059), Irwin Feaselbaum (2066), null (2080), null (2081), Sigmund (2082), Sigmund (2083), Duke Horacio (2088), Sigmund (2090), Customer (2168), Olivia (2233), Master Farmer (2234), Master Farmer (2235), Market Guard (2236), Gee (2237), Donie (2238), Khazard trooper (2245), Khazard trooper (2246), Wise Old Man (2253), Paladin (2256), null (2257), Mage of Zamorak (2258), Mage of Zamorak (2259), null (2260), Mage of Zamorak (2261), Dark mage (2262), Brian O'Richard (2266), Rogue Guard (2267), Rogue Guard (2268), Rogue Guard (2269), Martin Thwait (2270), Emerald Benedict (2271), Sir Spishyus (2282), Lady Table (2283), Sir Kuam Ferentse (2284), Sir Leye (2285), Sir Tinley (2286), Sir Ren Itchood (2287), Miss Cheevers (2288), Ms. Hynn Terprett (2289), Sir Tiffy Cashien (2290), Rug Merchant (2291), Rug Merchant (2292), Rug Merchant (2293), Rug Merchant (2294), null (2295), Rug Merchant (2296), null (2297), Rug Merchant (2298), null (2299), Rug Station Attendant (2300), Sarah (2304), Vanessa (2305), Richard (2306), Alice (2307), Cap'n Arnav (2308), Tanner (2320), Metarialus (2322), Elstan (2323), Dantaera (2324), Kragen (2325), Lyra (2326), Francis (2327), Garth (2330), Ellena (2331), Selena (2332), Vasquen (2333), Rhonen (2334), Dreven (2335), Taria (2336), Rhazien (2337), Torrell (2338), Alain (2339), Heskel (2340), Treznor (2341), Fayeth (2342), Gileth (2344), Mourner (2349), Mourner (2350), Mourner (2351), Eudav (2352), Oronwen (2353), Banker (2354), Banker (2355), Dalldav (2356), Gethin (2357), Arianwyn (2358), Elf warrior (2359), Elf warrior (2360), Elf warrior (2361), Elf warrior (2362), Goreu (2363), Ysgawyn (2364), Arvel (2365), Mawrth (2366), Kelyn (2367), Eoin (2368), Iona (2369), Head mourner (2372), Mourner (2373), Mourner (2374), null (2375), Eluned (2376), null (2381), null (2382), null (2383), null (2384), null (2385), null (2386), null (2387), null (2388), null (2389), null (2390), null (2391), null (2392), null (2393), null (2394), null (2395), null (2396), Mysterious ghost (2397), Mysterious ghost (2398), Mysterious ghost (2399), Mysterious ghost (2400), Mysterious ghost (2401), Mysterious ghost (2402), null (2435), Jarvald (2436), Jarvald (2437), Jarvald (2438), Freaky Forester (2458), Merchant Vernon (2463), Merchant Uriah (2464), Merchant Xenia (2465), Merchant Yuri (2466), Merchant Zakia (2467), H.A.M. agent Walton (2468), H.A.M. agent Walton (2469), H.A.M. agent Walton (2470), H.A.M. agent Walton (2471), H.A.M. agent Walton (2473), Frog Prince (2474), Frog Princess (2475), Rick Turpentine (2476), Quiz Master (2477), H.A.M. agent Walton (2480), Servant (2481), null (2483), null (2484), null (2485), null (2486), null (2487), null (2488), Tribesman (2496), Tribesman (2497), Broodoo victim (2498), Broodoo victim (2499), Broodoo victim (2500), Broodoo victim (2501), Broodoo victim (2502), Broodoo victim (2503), null (2504), null (2507), null (2510), null (2513), Karaday (2514), Karaday (2515), null (2516), Safta Doc (2517), Safta Doc (2518), null (2519), Gabooty (2520), Gabooty (2521), null (2522), Fanellaman (2523), Fanellaman (2524), null (2525), null (2528), Murcaily (2529), Murcaily (2530), null (2531), Rionasta (2532), Rionasta (2533), H.A.M. agent Walton (2534), Niles (2536), Miles (2537), Giles (2538), Cap'n Hand (2539), Dr Jekyll (2540), Chaos druid (2547), Blackjack seller (2548), Ali the dyer (2549), Wise Old Man (2566), Wise Old Man (2567), Banker (2568), Banker (2569), Banker (2570), Market Guard (2571), Olivia (2572), Pillory Guard (2573), Bank guard (2574), Purepker895 (2575), Qutiedoll (2576), 1337sp34kr (2577), Elfinlocks (2578), Cool Mom227 (2579), Bernald (2580), Ellamaria (2581), Billy, a guard of Falador (2586), Bob, another guard of Falador (2587), Brother Althric (2588), PKMaster0036 (2589), King Roald (2590), Olaf Hradson (2621), Wise Old Man (2633), Miss Schism (2634), Robert the Strong (2639), R4ng3rNo0b889 (2643), null (2654), Unferth (2655), Unferth (2656), Unferth (2657), Unferth (2658), Unferth (2659), Reldo (2660), Reldo (2661), Thief (2674), Man (2675), Make-over Mage (2676), Highwayman (2677), Hengel (2683), Anja (2684), Jack Seagull (2690), Longbow Ben (2691), Ahab (2692), Pirate (2695), Thief (2696), Mugger (2697), Black knight (2698), Guard (2699), Guard (2700), Guard (2701), Guard (2702), Guard (2703), Guard (2704), Guard (2705), Fire wizard (2709), Water wizard (2710), Earth wizard (2711), Air wizard (2712), Malignius Mortifer (2713), Betty (2718), Grum (2719), Gerrant (2720), Wydin (2721), Otto Godblessed (2725), Monk of Entrana (2728), Monk of Entrana (2729), Monk of Entrana (2730), Monk of Entrana (2731), Master Crafter (2732), Master Crafter (2733), Solus Dellagar (2747), Savant (2748), Lord Daquarius (2749), Solus Dellagar (2750), Black Knight (2751), Lord Daquarius (2752), Mage of Zamorak (2753), Mage of Zamorak (2754), Mage of Zamorak (2755), null (2756), null (2757), null (2758), null (2759), null (2760), null (2761), null (2762), null (2763), null (2764), null (2765), null (2766), null (2767), null (2768), null (2769), null (2770), null (2771), null (2772), null (2773), null (2774), null (2775), Woman (2776), Black Knight (2777), Black Knight (2778), Ranger (2779), Solus Dellagar (2780), Mourner (2784), Slave (2785), Slave (2786), Slave (2787), Sergeant Damien (2790), Pillory guard (2791), Tramp (2792), Tramp (2793), Tramp (2794), null (2795), null (2816), null (2817), null (2818), null (2819), Ali the Smith (2820), Ali the Farmer (2821), Ali the Tailor (2822), Ali the Guard (2823), Ellis (2824), Pirate Pete (2825), Pirate Pete (2826), Captain Braindeath (2827), Davey (2829), Brewer (2851), Brewer (2852), Brewer (2853), Brewer (2854), Brewer (2855), Brewer (2856), Brewer (2857), Brewer (2858), Amaethwr (2860), Teclyn (2861), Death (2862), Zombie (2863), Most of a Zombie (2864), Most of a Zombie (2865), Zombie (2866), Most of a Zombie (2867), Zombie Head (2868), Zombie (2869), Bardur (2879), null (2897), null (2898), Father Reen (2899), Father Reen (2900), null (2901), Father Badden (2902), Father Badden (2903), Denath (2904), Denath (2905), Eric (2906), Eric (2907), null (2908), Evil Dave (2909), Evil Dave (2910), Matthew (2911), Matthew (2912), Jennifer (2913), Jennifer (2914), Tanya (2915), Tanya (2916), Patrick (2917), Patrick (2918), null (2922), null (2923), null (2924), null (2925), Jorral (2932), null (2933), Melina (2935), null (2936), Droalak (2938), Dron (2939), Blanin (2940), Grimesquit (2946), Phingspet (2947), Hooknosed Jack (2948), Jimmy Dazzler (2949), The Face (2950), Felkrash (2951), Ceril Carnillean (2953), Councillor Halgrive (2954), Spice seller (2955), Fur trader (2956), Gem merchant (2957), Silver merchant (2958), Silk merchant (2959), Zenesha (2960), Ali Morrisane (2961), Guard (2962), Guard (2963), Guard (2964), Guard (2965), Guard (2966), Guard (2967), Guard (2968), Guard (2969), Guard (2970), Guard (2971), Guard (2972), Guard (2973), Gambler (2998), Gambler (2999), Barman (3000), Gambler (3001), Gambler (3002), Gambler (3003), Gambler (3004), Gambler (3005), Gambler (3006), Rug Merchant (3020), Genie (3022), Ghaslor the Elder (3029), Ali the Carter (3030), Usi (3031), Nkuku (3032), Garai (3033), Habibah (3034), Meskhenet (3035), Zahra (3036), Zahur (3037), Seddu (3038), Kazemde (3039), Awusah the Mayor (3040), Tarik (3041), Poltenip (3042), Radat (3043), Shiratti the Custodian (3044), Rokuh (3045), Nardah Banker (3046), Larxus (3050), Mystery figure (3051), Mystery figure (3056), Ghoul Champion (3059), Leon d'Cour (3067), Ice warrior (3073), null (3074), null (3076), Dead Monk (3077), High Priest (3078), Monk (3079), Monk (3080), Assassin (3081), Hooded Stranger (3092), Bert (3108), Guard Captain (3109), null (3110), null (3111), Sandy (3112), Sandy (3113), Mazion (3114), Blaec (3115), Reeso (3116), Prison Pete (3118), Simon Templeton (3123), Pentyn (3126), Aristarchus (3127), Desert Spirit (3131), null (3132), Furnace grate (3135), null (3136), Enakhra (3137), Enakhra (3138), null (3139), Akthanakos (3141), Akthanakos (3142), null (3143), null (3144), null (3145), null (3146), Lazim (3147), Enakhra (3148), Akthanakos (3149), Knight (3150), null (3152), Princess Astrid (3154), Bill Teach (3155), Bill Teach (3156), null (3157), Bill Teach (3158), Bill Teach (3159), Bill Teach (3160), Charley (3161), Smith (3162), Joe (3163), Mama (3164), Mama (3165), Mike (3166), Pirate (3167), Pirate (3168), Pirate (3169), Pirate (3170), Pirate (3171), Pirate (3172), Pirate (3173), Pirate (3174), Pirate (3175), Pirate (3176), Pirate (3177), Pirate (3178), Pirate (3179), Pirate (3180), Pirate (3181), Pirate (3182), Pirate (3183), Pirate (3184), Pirate (3185), Pirate (3186), Pirate (3187), Pirate (3188), Pirate (3189), Pirate (3190), Pirate (3191), Pirate (3192), Pirate (3193), Pirate (3194), Pirate (3195), Pirate (3196), Banker (3198), Banker (3199), Romily Weaklax (3205), Elena (3209), Alrena (3210), Alrena (3211), Bravek (3212), null (3213), null (3214), Elena (3215), Mourner (3216), Kaylee (3217), Tina (3218), Drunken man (3222), Man (3223), Man (3224), Man (3225), Woman (3226), Dell Monti (3227), Guard (3228), Guard (3229), Guard (3230), Guard (3231), Guard (3232), Guard (3233), Gardener (3234), Apprentice workman (3235), Workman (3236), Cuffs (3237), Narf (3238), Rusty (3239), Jeff (3240), Guard (3241), Dark wizard (3242), Dark wizard (3243), Dark wizard (3244), Dark wizard (3245), Barbarian (3246), Barbarian (3247), Barbarian (3248), Barbarian (3249), Barbarian (3250), Barbarian (3251), Barbarian (3252), Barbarian (3253), Hunding (3254), Barbarian (3255), Barbarian (3256), Barbarian (3257), Barbarian (3258), Barbarian (3259), Barbarian (3260), Barbarian (3261), Barbarian (3262), Barbarian (3263), null (3289), Maris (3292), Martin the Master Gardener (3299), Zandar Horfyre (3308), null (3323), Draul Leptoc (3324), Phillipa (3325), Martina Scorsby (3326), Jeremy Clerksin (3327), Tarquin (3328), Sigurd (3329), Hari (3330), Barfy Bill (3331), White Knight (3348), White Knight (3349), White Knight (3350), Genie (3351), Mysterious Old Man (3352), Niles (3356), Miles (3357), Giles (3358), Sir Amik Varze (3372), Sir Amik Varze (3374), Evil Dave (3378), Evil Dave (3380), Doris (3381), null (3383), null (3384), Gypsy (3385), Gypsy (3386), Culinaromancer (3387), Osman (3388), Pirate Pete (3389), Lumbridge Sage (3393), Evil Dave (3394), Sir Amik Varze (3395), Culinaromancer (3400), Pirate Pete (3416), Pirate Pete (3418), ? ? ? ? (3452), ? ? ? ? (3453), ? ? ? ? (3454), ? ? ? ? (3455), ? ? ? ? (3456), ? ? ? ? (3457), ? ? ? ? (3458), ? ? ? ? (3459), ? ? ? ? (3460), ? ? ? ? (3461), Culinaromancer (3485), Culinaromancer (3486), Culinaromancer (3487), Culinaromancer (3488), Culinaromancer (3489), Culinaromancer (3490), Culinaromancer (3491), Karamel (3495), Leo (3508), Sorin (3509), null (3510), Wiskit (3511), null (3512), null (3513), Vampyre Juvinate (3515), null (3516), Gadderanks (3517), Gadderanks (3518), Gadderanks (3519), Vampyre Juvinate (3520), Vampyre Juvinate (3521), Vampyre Juvinate (3522), Vampyre Juvinate (3523), Vampyre Juvinate (3524), Vampyre Juvinate (3525), Vampyre Juvinate (3526), Held Vampyre Juvinate (3527), Vampyre Juvinate (3528), Vampyre Juvinate (3529), Vampyre Juvenile (3531), Vampyre Juvenile (3532), Vampyre Juvenile (3533), Held Vampyre Juvenile (3534), Ivan Strom (3535), Ivan Strom (3536), Vampyre Juvinate (3537), Vampyre Juvinate (3538), Veliaf Hurtz (3539), Elisabeta (3540), Aurel (3541), Sorin (3542), Luscion (3543), Sergiu (3544), Radu (3545), Grigore (3546), Ileana (3547), Valeria (3548), Emilia (3549), Florin (3550), Teodor (3555), Gabriela (3557), Vladimir (3558), Calin (3559), Mihail (3560), Nicoleta (3561), Simona (3562), Vasile (3563), Razvan (3564), Luminata (3565), null (3566), null (3567), Cornelius (3568), Cornelius (3569), Benjamin (3570), Liam (3571), Miala (3572), Verak (3573), Juvinate (3576), Juvinate (3577), Juvinate (3578), null (3625), Rolayne Twickit (3626), null (3627), Jayene Kliyn (3628), null (3629), Valantay Eppel (3630), null (3631), Dalcian Fang (3632), null (3633), Fyiona Fray (3634), Abidor Crank (3635), null (3638), Launa (3639), Launa (3640), Brana (3641), Mawnis Burowgar (3642), Tolna (3643), Odd Old Man (3670), Fortunato (3671), Sinister Stranger (3677), Sinister Stranger (3678), Nigel (3681), Volf Olafson (3695), Ingrid Hradson (3696), Skeleton fremennik (3697), Skeleton fremennik (3698), Skeleton fremennik (3699), Skeleton fremennik (3700), Skeleton fremennik (3701), Skeleton fremennik (3702), Skeleton fremennik (3703), Skeleton fremennik (3704), Skeleton fremennik (3705), Ulfric (3706), Fisherman's wife (3709), Sigmund (3713), Guard (3715), Sigmund (3716), Sigmund (3717), Sigmund (3718), Sigmund (3719), Sigmund (3720), Doomsayer (3777), null (3778), Arthur (3779), null (3780), Squire (3781), Void Knight (3782), Void Knight (3784), Void Knight (3785), Void Knight (3786), Sir Palomedes (3787), Void Knight (3788), Void Knight (3789), Squire (3790), Squire (3791), Squire (3792), Squire (3793), Squire (3794), Squire (3795), Squire (3796), Squire (3797), Squire (3798), Squire (3799), Squire (3800), Squire (3801), Squire (Novice) (3802), Millie Miller (3806), Gillie Groats (3807), Wise Old Man (3821), Herman Caranos (3822), Franklin Caranos (3823), Arnold Lydspor (3824), Devin Mendelberg (3825), George Laxmeister (3826), Ramara du Croissant (3827), null (3828), null (3829), Kathy Corkat (3830), Kathy Corkat (3831), null (3832), null (3833), null (3834), Wise Old Man (3838), Wise Old Man (3839), Suspect (3852), Suspect (3853), Suspect (3854), Suspect (3855), Suspect (3856), Suspect (3857), Suspect (3858), Suspect (3859), Suspect (3860), Suspect (3861), Suspect (3862), Suspect (3863), Suspect (3864), Suspect (3865), Suspect (3866), Suspect (3867), Suspect (3868), Suspect (3869), Suspect (3870), Suspect (3871), Suspect (3872), Suspect (3873), Suspect (3874), Suspect (3875), Suspect (3876), Suspect (3877), Suspect (3878), Suspect (3879), Suspect (3880), Suspect (3881), Suspect (3882), Suspect (3883), Suspect (3884), Suspect (3885), Suspect (3886), Suspect (3887), Suspect (3888), Suspect (3889), Suspect (3890), Suspect (3891), Molly (3892), Molly (3893), Molly (3894), Molly (3895), Molly (3896), Molly (3897), Molly (3898), Molly (3899), Molly (3900), Molly (3901), Molly (3902), Molly (3903), Molly (3904), Molly (3905), Molly (3906), Molly (3907), Molly (3908), Molly (3909), Molly (3910), Molly (3911), Gardener (3914), Man (3915), Carpenter Kjallak (3916), Farmer Fromund (3917), Prince Brand (3918), Princess Astrid (3919), Runa (3920), Halla (3921), Finn (3922), Osvald (3923), Runolf (3924), Tjorvi (3925), Ingrid (3926), Thora (3927), Signy (3928), Hild (3929), Armod (3930), Beigarth (3931), Reinn (3932), Guard (3941), Guard (3942), Jacky Jester (3955), Rick (4235), null (4236), Maid (4237), null (4238), Cook (4239), null (4240), Butler (4241), null (4242), Demon butler (4243), null (4244), Chief servant (4245), Taxidermist (4246), Estate agent (4247), Sir Renitee (4249), Sawmill operator (4250), Garden supplier (4251), Animated Bronze Armour (4278), Animated Iron Armour (4279), Animated Steel Armour (4280), Animated Black Armour (4281), Animated Mithril Armour (4282), Animated Adamant Armour (4283), Animated Rune Armour (4284), Ghommal (4285), Ajjat (4288), Kamfreena (4289), Shanomi (4290), Lidio (4293), Lilly (4294), Anton (4295), Jade (4296), Sloane (4297), Jimmy (4298), Ref (4299), Ref (4300), Guard (4301), Guard (4302), Guard (4303), Guard (4304), Guard (4305), Guard (4306), Guard (4307), Guard (4308), Guard (4309), Guard (4310), Guard (4311), null (4317), H.A.M. Member (4318), null (4319), H.A.M. Member (4320), null (4321), HAM member (4327), Sigmund (4328), H.A.M. Deacon (4329), Johanhus Ulsbrecht (4330), Sigmund (4331), Sigmund (4332), Sigmund (4333), Sigmund (4334), Sigmund (4335), Guard (4336), Cavey Davey (4358), Patchy (4359), San Fan (4360), Fancy Dan (4361), Honest Jimmy (4362), Security Guard (4375), Litara (4376), Ankou (4381), Ankou (4382), Ankou (4383), Wood Dryad (4441), Held vampyre juvinate (4461), Held vampyre juvinate (4462), Angry juvinate (4463), Angry juvinate (4464), Angry juvinate (4465), Benjamin (4466), Liam (4467), Miala (4468), Verak (4469), Ned (4475), Tarik (4478), Ethereal Man (4501), Ethereal Lady (4502), Ethereal Numerator (4503), Ethereal Expert (4504), Ethereal Perceptive (4505), Ethereal Guide (4506), Ethereal Fluke (4507), Ethereal Mimic (4508), Me (4509), Me (4510), Oneiromancer (4511), Baba Yaga (4513), Pauline Polaris (4514), Meteora (4515), Melana Moonlander (4516), Selene (4517), Rimae Sirsalis (4518), Sirsal Banker (4519), Clan Guard (4520), Bouquet Mac Hyacinth (4526), Lokar Searunner (4537), 'Beefy' Burns (4541), 'Eagle-eye' Shultz (4542), First mate 'Davey-boy' (4543), 'Bird's-Eye' Jack (4544), 'Picarron' Pete (4545), Jake (4546), Bedread the bold (4547), Wilson (4548), Tommy 2-times (4549), Murky Pat (4550), Jack Sails (4551), Palmer (4552), 'Betty' B.Boppin (4553), 'Beedy-eye' Jones (4554), Jenny Blade (4555), 'Lecherous' Lee (4556), 'Sticky' Sanders (4557), Digsite workman (4564), Digsite workman (4565), Examiner (4566), Examiner (4567), Researcher (4568), Nick (4569), Fortress Guard (4603), Fortress Guard (4604), Fortress Guard (4605), Fortress Guard (4606), Monk (4608), Nisha (4648), Tyras guard (4649), Trader Stan (4650), Trader Crewmember (4651), Trader Crewmember (4652), Trader Crewmember (4653), Trader Crewmember (4654), Trader Crewmember (4655), Trader Crewmember (4656), Sir Prysin (4657), Dark wizard (4658), Dark wizard (4659), Dark wizard (4660), Dark wizard (4661), Denath (4663), Wally (4664), Magic instructor (4707), Old Man Ral (4708), Vertida Sefalatis (4709), Aeonisig Raispher (4710), null (4711), null (4712), Safalaan (4713), null (4714), Sarius Guile (4715), Trader Sven (4716), Meiyerditch citizen (4717), Meiyerditch citizen (4718), Meiyerditch citizen (4719), Meiyerditch citizen (4720), Meiyerditch citizen (4721), Meiyerditch citizen (4722), Meiyerditch citizen (4723), Meiyerditch citizen (4724), Meiyerditch citizen (4725), Meiyerditch citizen (4726), Meiyerditch citizen (4727), Meiyerditch citizen (4728), Meiyerditch citizen (4729), Meiyerditch citizen (4730), Meiyerditch citizen (4731), Meiyerditch citizen (4732), Meiyerditch citizen (4733), Meiyerditch citizen (4734), Meiyerditch citizen (4735), Meiyerditch citizen (4736), Meiyerditch citizen (4737), Meiyerditch citizen (4738), Meiyerditch citizen (4739), Meiyerditch citizen (4740), Meiyerditch citizen (4741), Meiyerditch citizen (4742), Meiyerditch citizen (4743), Meiyerditch citizen (4744), Meiyerditch citizen (4745), Meiyerditch miner (4756), Meiyerditch miner (4757), Meiyerditch miner (4758), Meiyerditch miner (4759), Meiyerditch miner (4760), Meiyerditch miner (4761), Former vampyre (4783), Former vampyre (4784), Former vampyre (4785), Former vampyre (4786), Former vampyre (4787), Former vampyre (4788), Angry vampyre (4789), Angry vampyre (4790), null (4854), null (4855), Ezekial Lovecraft (4856), null (4857), null (4858), null (4859), null (4860), Sarius Guile (4861), Holgart (4866), Holgart (4867), Holgart (4868), null (4869), Fisherman (4870), null (4871), Col. O'Niall (4872), Col. O'Niall (4873), Mayor Hobb (4874), Mayor Hobb (4875), null (4876), null (4877), Brother Maledict (4878), Brother Maledict (4879), null (4880), null (4881), null (4882), Witchaven villager (4883), Witchaven villager (4884), Witchaven villager (4885), Witchaven villager (4886), Witchaven villager (4887), Witchaven villager (4888), Jeb (4895), Jeb (4896), Sir Tinley (4897), Cordero (4899), Cadmus (4900), Finlay (4901), Monlum (4902), Priest Yauchomi (4903), Apprentice smith (4904), Sani (4905), Wilfred (4906), Banker (4907), Fritz the Glassblower (4909), Abidor Crank (4919), Ignatius Vulcan (4946), null (4961), Captain Barnaby (4962), Murcaily (4963), Jagbakoba (4964), Male slave (4975), Male slave (4976), Female slave (4977), Female slave (4978), Mercenary (4983), null (4984), null (4985), null (4986), null (4987), Irena (4988), Mercenary (4989), Mercenary (4990), Mercenary (4991), Mercenary (4992), Guard (4993), Guard (4994), Guard (4995), Guard (4996), Guard (4997), Guard (4998), Guard (4999), Guard (5000), Guard (5001), Guard (5002), Apprentice Clerval (5026), Commander Connad (5029), Captain Cain (5030), Private Paldo (5031), Private Pendron (5032), Private Pierreb (5033), Private Paldon (5034), Major Attack (5035), Major Collect (5036), Major Defend (5037), Major Heal (5038), Sergeant Sambur (5039), Auguste (5049), Auguste (5050), Auguste (5051), Auguste (5052), Assistant Serf (5053), Assistant Brock (5054), Assistant Marrow (5055), Assistant Stan (5057), Bob (5058), Curly (5059), Moe (5060), Larry (5061), null (5062), null (5063), null (5065), null (5066), Matthias (5092), Matthias (5093), Artimeus (5109), Aleck (5110), Leon (5111), Hunting expert (5112), Hunting expert (5113), null (5124), null (5125), Nickolaus (5126), Charlie (5138), Uri (5141), Uri (5142), Uri (5143), Double agent (5144), Double agent (5145), Wizard (5195), Wizard (5196), Wizard (5197), Ava (5198), null (5199), Witch (5200), null (5201), Alice's husband (5202), Alice's husband (5203), Alice's husband (5204), Alice's husband (5205), null (5206), Cow1337killr (5210), Alice (5212), null (5257), Banker (5258), null (5259), Banker (5260), null (5261), Stonemason (5262), null (5263), Nathifa (5264), null (5265), Urbi (5266), null (5267), Jamila (5268), null (5269), Sophanem guard (5270), null (5271), Sophanem guard (5272), null (5273), Sophanem guard (5274), null (5275), Sophanem guard (5276), Menaphite guard (5277), Coenus (5278), Jex (5279), null (5280), Maisa (5281), null (5282), null (5283), Osman (5284), Osman (5285), Osman (5286), Osman (5287), Embalmer (5288), Carpenter (5289), Linen worker (5290), Priest (5291), Confused barbarian (5364), Animated steel armour (5382), Odovacar (5383), Tarn (5419), Tarn (5420), null (5423), Larry (5424), Larry (5425), Larry (5426), Musician (5439), Musician (5442), Tolna (5462), Honour guard (5463), Honour guard (5464), Fridleif Shieldson (5465), Thakkrad Sigmundson (5466), Bork Sigmundson (5477), King Gjuki Sorvott IV (5478), Thorkel Silkbeard (5480), Mord Gunnars (5481), Mord Gunnars (5482), Hring Hring (5483), Flosi Dalksson (5484), Raum Urda-Stein (5485), Skuli Myrka (5486), Keepa Kettilon (5487), Magnus Gram (5488), Guard (5489), Guard (5490), Guard (5491), Guard (5492), Freygerd (5493), Lensa (5494), Vanligga Gastfrihet (5495), Sassilik (5496), Miner (5497), Miner (5498), Eric (5499), Gruva Patrull (5500), Mawnis Burowgar (5503), Mawnis Burowgar (5504), Fridleif Shieldson (5505), Thakkrad Sigmundson (5506), Maria Gunnars (5507), Maria Gunnars (5508), Jofridr Mordstatter (5509), Morten Holdstrom (5510), Gunnar Holdstrom (5511), Anne Isaakson (5512), Lisse Isaakson (5513), Honour guard (5514), Honour guard (5515), Honour guard (5516), Honour guard (5517), Kjedelig Uppsen (5518), Trogen Konungarde (5519), Slug Hemligssen (5520), null (5530), Sorceress (5531), Apprentice (5532), Osman (5559), Osman (5560), Osman (5561), General Khazard (5566), Scout (5567), Scout (5568), Scout (5569), Scout (5570), Ghost (5572), null (5573), null (5574), null (5575), null (5576), null (5577), Effigy (5578), Effigy (5579), Bonafido (5580), 'Transmute' The Alchemist (5585), 'Transmute' The Alchemist (5586), 'Currency' The Alchemist (5587), 'Currency' The Alchemist (5588), 'Black-eye' (5589), 'No fingers' (5590), 'Gummy' (5591), 'The Guns' (5592), null (5598), null (5601), null (5602), Elfinlocks (5604), null (5606), Rufus (5612), Movario (5825), Darve (5826), null (5830), null (5831), Rat Burgiss (5833), Surok Magis (5834), Surok Magis (5835), Zaff (5836), Anna Jones (5837), King Roald (5838), Mishkal'un Dorn (5839), Dakh'thoulan Aegis (5840), Sil'as Dahcsnu (5841), Outlaw (5842), Outlaw (5843), Outlaw (5844), Outlaw (5845), Outlaw (5846), Outlaw (5847), Outlaw (5848), Outlaw (5849), Outlaw (5850), Outlaw (5851), null (5853), Bench (5854), H.A.M. Archer (5859), H.A.M. Mage (5860), Cyrisus (5893), Cyrisus (5894), Cyrisus (5895), Cyrisus (5896), Cyrisus (5897), 'Bird's-Eye' Jack (5898), null (5899), null (5900), null (5901), Barbarian (5909), Cook (5910), Cook (5911), Banker (5912), Banker (5913), Iffie (5914), Elsie (5915), Cleaner (5916), Guard (5919), Guard (5920), Trainee Guard (5921), Captain (5922), Man (5923), Woman (5924), Benny (5925), Thief (5926), Thief (5927), Thief (5928), Thief (5929), Art Critic Jacques (5930), Historian Minas (5931), Information clerk (5938), Torrcs (5939), Marfet (5940), Museum guard (5941), Museum guard (5942), Museum guard (5943), Void Knight (5956), Digsite workman (5958), Ed Wood (5964), Orlando Smith (5965), Natural historian (5966), Natural historian (5967), Natural historian (5968), Natural historian (5969), Natural historian (5970), Sylas (5987), Miazrqa (5988), Boris (6026), Imre (6027), Yuri (6028), Joseph (6029), Nikolai (6030), Eduard (6031), Lev (6032), Georgy (6033), Svetlana (6034), Irina (6035), Alexis (6036), Milla (6037), Galina (6038), Sofiya (6039), Ksenia (6040), Yadviga (6041), Nikita (6042), Vera (6043), Zoja (6044), Liliya (6045), null (6075), null (6076), null (6077), null (6082), null (6083), Captain Ned (6084), Cabin boy Jenkins (6085), Cabin boy Jenkins (6086), Lost barbarian (6102), Skeleton hero (6103), Skeleton brute (6104), Skeleton warlord (6105), Skeleton heavy (6106), Skeleton thug (6107), Observatory assistant (6118), null (6119), null (6120), Observatory professor (6121), Town crier (6135), Town crier (6136), Town crier (6137), Town crier (6138), Town crier (6139), Squire (Intermediate) (6140), Squire (Veteran) (6141), Sir Lucan (6158), Sir Lancelot (6160), Sir Bedivere (6161), Sir Tristram (6162), Sir Pelleas (6163), Sir Gawain (6164), Sir Kay (6165), Sir Pelleas (6166), Sir Gawain (6167), Sir Kay (6168), Squire (6169), Sir Lancelot (6170), Sir Kay (6171), Sir Gawain (6172), Sir Lucan (6173), Bandit (6174), Sir Tristram (6175), Sir Pelleas (6176), Sir Bedivere (6177), Anna (6178), David (6179), Anna (6180), Court judge (6181), Jury (6182), Prosecutor (6185), null (6186), Morgan Le Faye (6187), Renegade knight (6188), Black Knight (6189), Guard (6190), null (6191), null (6192), null (6193), null (6194), null (6195), null (6196), null (6197), Sinclair (6198), null (6199), Banker (6200), Knight (6201), null (6202), Vampire (6214), Spiritual warrior (6219), Spiritual ranger (6220), Spiritual mage (6221), Saradomin priest (6254), Spiritual warrior (6255), Spiritual ranger (6256), Spiritual mage (6257), Knight of Saradomin (6258), Knight of Saradomin (6259), Jeffery (6298), Pirate (6346), Pirate (6347), Pirate (6348), Pirate (6349), Pirate (6350), Pirate (6351), Pirate (6352), Pirate (6353), Pirate (6354), Pirate (6355), Pirate (6356), Eniola (6362), Zamorak warrior (6363), Zamorak warrior (6364), Zamorak ranger (6365), Zamorak ranger (6366), Zamorak mage (6367), Zamorak mage (6368), Mage of Zamorak (6370), Zamorak crafter (6371), Zamorak crafter (6372), Wizard Cromperty (6375), null (6386), Bandit (6388), Jury (6510), Jury (6511), Spectator (6512), Spectator (6513), Spectator (6514), Spectator (6515), Spectator (6516), Spectator (6517), Spectator (6518), Spectator (6519), Head registrar (6520), Grand Exchange Tutor (6521), Brugsen Bursen (6522), Bob Barter (herbs) (6524), Murky Matt (runes) (6525), Relobo Blinyo (logs) (6526), Grand Exchange clerk (6528), Grand Exchange clerk (6529), Grand Exchange clerk (6530), Grand Exchange clerk (6531), Banker (6532), Banker (6533), Banker (6534), Banker (6535), Banker (6538), null (6539), Nastroth (6540), Canon the barbarian (6541), Queen of Snow (6731), Pet shop owner (6750), Doorman (6769), Maisa (6782), Maisa (6783), Priest (6784), Lead archaeologist Abigail (6786), Assistant archaeologist Kerner (6787), null (6793), Penguin keeper (6891), Pet shop owner (6892), Pet shop owner (6893), Pet shop owner (6898), Pikkupstix (6970), Pikkupstix (6971), Druid (6972), Druid (6973), Druid (6974), Druid (6975), Druid (6976), Druid (6977), Druid (6978), Druid (6979), Druid (6980), Druidess (6981), Druidess (6982), Druidess (6983), Druidess (6984), Druidess (6985), Druidess (6986), Druidess (6987), null (6988), null (6989), Dark Squall (7001), Surok Magis (7002), null (7006), null (7007), null (7008), null (7009), Balnea (7047), null (7065), null (7066), Balnea (7087), Wise Old Man (7090), Chaos druid (7105), Thug (7106), Thug (7107), Thug (7108), Thug (7109), Thug (7110), Thug (7111), Thug (7112), Thug (7113), Thug (7114), Drunken sailor (7116), Drunken sailor (7117), Catapult engineer (7118), null (7119), Tyras guard (7120), General Hining (7121), Surok Magis (7136), Dagon'hai Elite (7137), Dagon'hai Monk (7138), Dagon'hai Monk (7139), Dagon'hai Monk (7140), Professor Henry (7143), Student (7151), Student (7152), Student (7153), Student (7154), Student (7155), Student (7156), Student (7157), Mugger (7161), Mugger (7162), null (7163), Villager (7164), Villager (7165), Villager (7166), Villager (7167), null (7176), null (7177), null (7178), null (7179), null (7180), null (7181), null (7182), Villager (7183), Ezekial Lovecraft (7184), Clive (7185), Katherine (7186), Katherine (7187), Clive (7188), Kent (7189), null (7190), null (7195), Void Knight (7203), Mysterious mage (7383), Young Brundt (7384), Young Mawnis (7385), Prince Vargas (7386), Prince Vargas (7387), Asleif Hamalsdotter (7389), Nial Swiftfling (7390), Brundt the Chieftain (7394), Brundt the Chieftain (7395), Mawnis Burowgar (7396), Mawnis Burowgar (7397), Thora the barkeep (7398), King Vargas (7399), King Vargas (7400), Asleif Hamalsdotter (7401), null (7406), Highwayman (7443), Ali the Wise (7447), Jhallan (7448), Jhallan (7449), Jhallan (7450), Jhallan (7451), null (7468), Ali the Wise (7469), Pharaoh Queen (7470), Pharaoh Queen (7471), Pharaoh Queen (7472), Pharaoh Queen (7473), Pharaoh Queen (7474), null (7478), Bandit looter (7492), Bandit looter (7493), Bandit looter (7494), Bandit looter (7495), Bandit looter (7496), null (7499), Khazard Guard (7504), Khazard Guard (7505), Khazard Guard (7506), Khazard Guard (7507), Khazard Guard (7508), Khazard Guard (7509), Khazard Guard (7510), Khazard Guard (7511), Khazard Guard (7512), Khazard Guard (7513), Khazard Guard (7514), Khazard Guard (7515), Khazard Guard (7518), Khazard Guard (7519), Khazard Guard (7520), Khazard Guard (7528), Lady Servil (7529), Lady Servil (7530), Justin Servil (7535), Justin Servil (7536), null (7537), null (7538), null (7539), null (7540), null (7541), Justin Servil (7542), General Khazard (7551), General Khazard (7552), General Khazard (7553), Citizen (7554), Zookeeper (7570), Zookeeper (7571), Monkey minder (7572), Monkey minder (7573), null (7574), null (7575), null (7576), null (7577), null (7580), Chief Zookeeper (7581), Reggie (7601), Getorix (7602), Pontimer (7603), Alran (7604), Banker (7605), Zaromark Sliver (7644), Zaromark Sliver (7645), Zaromark Sliver (7646), Zaromark Sliver (7647), Zaromark Sliver (7648), Zaromark Sliver (7649), Zaromark Sliver (7650), Zaromark Sliver (7651), Zaromark Sliver (7652), Zaromark Sliver (7653), Fistandantilus (7654), Fistandantilus (7655), Fistandantilus (7656), Fistandantilus (7657), Fistandantilus (7658), Fistandantilus (7659), Fistandantilus (7660), Fistandantilus (7661), Fistandantilus (7662), Fistandantilus (7663), Mercenary Adventurer (7664), Ivan Strom (7665), Mysterious person (7666), Mysterious person (7667), Mysterious person (7668), Mysterious person (7669), Mysterious person (7670), Mysterious person (7671), Flaygian Screwte (7672), Mekritus A'hara (7673), Andiess Juip (7674), Kael Forshaw (7675), Andiess Juip (7676), Kael Forshaw (7677), Safalaan (7678), Andiess Juip (7679), Kael Forshaw (7680), Safalaan (7681), Safalaan (7686), null (7688), null (7689), Drezel (7690), Drezel (7707), null (7708), null (7709), null (7710), Brother Althric (7719), Monk (7727), Will (7737), Will (7738), Phil (7739), null (7779), Sumona (7780), Jesmona (7781), Catolax (7782), null (7783), A wanderer. (7821), A wanderer. (7822), Osman (7824), 50 Ships Mufassah (7825), Customs Officer (7837), Customs Officer (7838), Customs Officer (7839), Heavy-Handed Harry (7840), Locker Officer (7842), Locker Officer (7843), Captain Rabid Jack (7847), Jack (7848), Bosun Giles (7849), Pirate (7850), Lizzie (7851), Cap'n Izzy No-Beard (7852), Ralph (7853), Brass Hand Harry (7854), Bill Teach (7855), Pirate (7856), Brass Hand Harry (7857), Jack (7861), Iain (7868), Julian (7869), Lachtopher (7870), Samuel (7871), Victoria (7872), Man (7873), Man (7874), Man (7875), Man (7876), Man (7877), Man (7878), Man (7879), Woman (7880), Woman (7881), Woman (7882), Woman (7883), Woman (7884), null (7898), Torsteg (7907), Hilda (7908), Man (7909), Man (7910), Doomsayer (7930), Donie (7931), Gee (7932), Duke Horacio (7933), Sigmund (7934), Hans (7935), Cook (7936), Father Aereck (7937), Sir Vant (7938), Sir Vant (7939), Sir Vant (7940), Sir Vant (7941), Sir Vant (7942), Lumbridge Sage (7949), Melee Tutor (7950), Ranged Tutor (7951), Magic Tutor (7952), Cooking Tutor (7953), Crafting Tutor (7954), Chief Thief Robin (7955), Mining Tutor (7956), Prayer Tutor (7957), Smelting Tutor (7958), Smithing Tutor (7959), Woodcutting Tutor (7960), Bank Tutor (7961), Gee (7962), Chaos druid (7966), Shopkeeper (7967), Explorer Jack (7969), Spirit (7987), Spirit (7988), Erik Bonde (7989), Spirit (7990), Jallek Lenkin (7991), Spirit (7992), Meranek Thanatos (7993), Ghostly warrior (7994), Jack (8002), Sarah (8003), Laura (8004), Laura (8005), Roger (8006), Jorral (8007), Guthix (8008), Max the traveller (8009), Man (8010), Man (8011), Woman (8012), Woman (8013), Haluned (8014), Jack (8015), Baby Sarah (8016), Wizard (8033), Wizard (8034), Wizard (8035), Wizard (8036), Wizard (8037), Wizard (8038), Wizard (8039), Wizard (8040), Imiago (8041), null (8065), null (8066), null (8067), null (8068), null (8069), null (8070), null (8071), Maggie (8078), Audience (8086), Audience (8087), null (8089), Shooting star shadow (8092), Calladin (8111), Erik Bonde (8115), Erik Bonde (8116), Jallek Lenkin (8117), Jallek Lenkin (8118), Meranek Thanatos (8119), Meranek Thanatos (8120), Spirit (8121), Rogue (8122), null (8123), null (8124), Spirit (8134), Erik Bonde (8136), Jallek Lenkin (8137), Meranek Thanatos (8138), Hartwin (8139), Hartwin (8140), null (8141), Arrav (8168), Arrav (8169), Dimintheis (8171), Dimintheis (8172), Guard (8173), Clive (8174), Ellamaria (8175), Monk of Zamorak (8178), Acrobat (8185), Acrobat (8186), Acrobat (8187), Acrobat (8188), Acrobat (8189), Acrobat (8190), Acrobat (8191), Acrobat (8192), Acrobat (8193), Acrobat (8194), Acrobat (8195), Acrobat (8196), Acrobat (8197), Acrobat (8198), Acrobat (8199), Acrobat (8200), Wendy (8201), Gus (8205), Lottie (8206), Aggie (8207), Guardian of Armadyl (8226), Head mystic (8227), Rewards mystic (8228), Mystic (8229), Mystic (8230), Mystic (8234), Mystic (8235), Head mystic (8239), Grindxplox (8250), Grindxplox (8251), null (8266), null (8267), null (8269), Yadech Strongarm (8270), null (8271), Turael (8273), Mazchna (8274), Duradel (8275), null (8277), null (8278), Cyrisus (8279), Sloane (8280), Balance Elemental (8285), Harrallak Menarous (8299), Harrallak Menarous (8300), Harrallak Menarous (8301), Turael (8302), Turael (8303), Cyrisus (8304), Cyrisus (8305), Ghommal (8306), Mazchna (8307), Mazchna (8308), Mazchna (8309), Duradel (8310), Sloane (8311), Elite Dark Warrior (8312), Elite Dark Warrior (8313), Elite Dark Warrior (8314), Elite Dark Warrior (8315), Elite Dark Ranger (8316), Elite Dark Ranger (8317), Elite Dark Ranger (8318), Elite Dark Ranger (8319), Elite Dark Mage (8320), Elite Dark Mage (8321), Elite Dark Mage (8322), Elite Dark Mage (8323), Elite Black Knight (8324), Elite Black Knight (8325), Elite Black Knight (8326), Elite Black Knight (8327), Elite Black Knight (8328), Elite Black Knight (8329), Elite Black Knight (8330), Guardian of Armadyl (8331), Guardian of Armadyl (8332), Guardian of Armadyl (8333), Mercenary axeman (8334), Mercenary mage (8335), Idria (8336), Idria (8337), Akrisae (8338), Shady stranger (8339), Shady stranger (8340), Shady stranger (8341), null (8342), Guardian of Armadyl (8343), Guardian of Armadyl (8344), null (8345), Local thug (8346), null (8347), Local mage (8348), Vasador (8393), Valator (8394), Vaasdor (8395), Varosad (8396), Verisad (8397), Vudisor (8398), Vlatoad (8399), Vedolas (8400), Vundiar (8401), Versita (8402), Vislota (8403), Vanjuta (8404), Vasador (8405), Valator (8406), Vaasdor (8407), Varosad (8408), Verisad (8409), Vudisor (8410), Vlatoad (8411), Vedolas (8412), Vundiar (8413), Versita (8414), Vislota (8415), Vanjuta (8416), Ivy Sophista (8417), Thaerisk Cemphier (8418), Thaerisk Cemphier (8419), null (8420), null (8421), Assassin (8422), Assassin (8423), Khazard launderer (8428), Khazard cook (8429), Silif (8433), Silif (8434), Shady stranger (8435), Suspicious outsider (8436), Elite Khazard guard (8437), Elite Khazard guard (8438), Elite Khazard guard (8439), Elite Khazard guard (8440), Dark Squall (8441), Surok Magis (8442), Druid (8444), Druid (8445), Druid (8446), Druid bodyguard (8447), Druid bodyguard (8448), Movario (8449), Darve (8450), Movario (8452), Darve (8453), Druid spirit (8456), Druid spirit (8457), Druid spirit (8458), Druid spirit (8459), Turael (8461), Spria (8462), Mazchna (8464), Achtryn (8465), Duradel (8466), Lapalok (8467), null (8468), null (8469), null (8470), null (8471), null (8472), null (8474), null (8475), null (8476), null (8477), null (8478), null (8479), null (8480), null (8481), null (8482), null (8483), Cook's brother (8500), Jack Frost (8517), Jack Frost (8518), Jack Frost (8519), Competition Judge (8523), Competition Judge (8524), null (8525), Queen of Snow (8539), Isidor (8544), Mystic (8545), Priest of Guthix (8555), Brian Twitcher (8556), null (8579), Geoffrey (8590), Nomad (8591), null (8602), null (8603), null (8604), null (8605), null (8606), Nomad (8607), Nomad (8608), Sandwich lady (8629), null (8630), Sandwich lady (8631), Mysterious Old Man (8632), Niles (8633), Miles (8634), Giles (8635), Rick Turpentine (8641), Dr Jekyll (8642), Dr Jekyll (8643), Shade (8645), Bee keeper (8649), null (8693), null (8694), null (8695), Musician (8698), Musician (8699), Musician (8700), Musician (8701), Musician (8702), Musician (8703), Musician (8704), Musician (8705), Musician (8706), Musician (8707), Musician (8708), null (8709), Musician (8710), Musician (8711), Ghostly piper (8713), Ghostly piper (8714), Drunken musician (8715), Musician (8716), Musician (8717), Musician (8718), Drummer (8719), Drummer (8720), Musician (8721), Bill Blakey (8722), Elf musician (8723), Dark wizard (8830), Lumbridge Sage (8847), Sir Vant (8848), Sir Vant (8849), Sir Vant (8850), Sir Vant (8851), Sir Vant (8852), Sir Vant (8853), Squire (8857), Roddeck (8863), Hank (8864), Roddeck (8867), Dark wizard (8871), Dark wizard (8872), Dark wizard (8873), Dark wizard (8874), null (8878), Iconis (8879), Gwir (8894), Scotty (8901), Sanchez (8902), Overseer (8904), Arianwyn (8913), Arianwyn (8914), null (8915), null (8916), Junior Cadet Mal (8917), Junior Cadet Mal (8918), Junior Cadet Mal (8919), Junior Cadet Mal (8920), Junior Cadet Mal (8921), null (8922), Serjeant Cole (8923), null (8924), Commander Loman (8925), null (8926), War-chief Reeves (8927), null (8928), Lord Marshal Brogan (8929), null (8938), Junior Cadet Finda (8939), null (8940), Junior Cadet Bertol (8941), Operator Jen (8949), Arianwyn (8950), null (8951), null (8952), Mourner (8953), Operator Lil (8954), Operator Dor (8959), Operator Gron (8964), Arianwyn (8966), Registrar 4 (8967), Registrar 5 (8968), Junior Cadet Mina (8969), null (9036), null (9037), Cook's brother (9038), Assassin (9044), null (9045), Azzanadra (9047), Azzanadra (9048), Digsite workman (9049), Azzanadra (9051), Monk of Zamorak (9082), Kuradal (9084), Kuradal (9085), Queen Sigrid (9088), Baba Yaga (9093), Baba Yaga (9094), Silas (9095), King Vargas (9099), King Vargas (9102), Bardur (9105), Brundt the Chieftain (9118), Koschei the Deathless (9119), Market guard (9122), Fremennik warrior (9123), Agnar (9124), Pontak (9125), Jennella (9126), Sassilik (9127), null (9134), Brundt the Chieftain (9135), Prince Brand (9136), Princess Astrid (9137), Queen Sigrid (9138), Brundt the Chieftain (9139), King Vargas (9142), Prince Brand (9143), Princess Astrid (9144), Queen Sigrid (9145), Baba Yaga (9146), Prince Brand (9147), Princess Astrid (9148), Koschei the Deathless (9149), Faruq (9159), Arrav (9160), Arrav (9161), Marion (9166), Geoff (9168), Baba Yaga (9307), Prince Brand (9308), Princess Astrid (9309), Koschei the Deathless (9310), Brundt the Chieftain (9315), King Vargas (9316), Queen Sigrid (9317), Eir (9318), Asleif Hamalsdotter (9319), Nial Swiftfling (9320), Eir (9321), Eir (9322), Brundt the Chieftain (9323), Baba Yaga (9324), Koschei the Deathless (9325), Baba Yaga (9326), Koschei the Deathless (9327), Prince Brand (9328), Princess Astrid (9329), Baba Yaga (9330), Brundt the Chieftain (9331), Prince Brand (9332), Princess Astrid (9333), Honour guard (9335), Sigli the Huntsman (9336), Mawnis Burowgar (9337), Fridleif Shieldson (9338), Baba Yaga (9339), Silas (9340), Baba Yaga (9341), Silas (9342), Baba Yaga (9347), Oneiromancer (9348), Dr Harlow (9355), Edward Honda (9358), null (9359), Sir Prysin (9360), Gypsy Aris (9361), Jack Frost (9365), Cook (9367), Partygoer (9380), Partygoer (9381), Partygoer (9382), Partygoer (9384), Partygoer (9385), Partygoer (9387), Partygoer (9388), Partygoer (9390), Partygoer (9391), Partygoer (9393), Partygoer (9394), Betty (9395), Sir Amik Varze (9397), Queen of Snow (9398), Jack Frost (9403), Cook (9405), Sir Amik Varze (9416), Cook (9418), Partygoer (9420), Knight (9448), Knight (9449), null (9450), Elite Dark Warrior (9456), Elite Dark Ranger (9457), Elite Dark Mage (9458), Elite Dark Mage (9459), Pikkenmix (9492), Pikkenmix (9493), Reese (9623), Reese (9624), Reese (9625), Caitlin (9626), Caitlin (9627), Kayle (9628), Kayle (9629), Ilona (9630), Ilona (9631), Xenia (9634), Xenia (9635), Xenia (9636), Reese (9637), Caitlin (9638), Kayle (9639), Ilona (9640), Fisherman (9676), Fisherman (9677), Fisherman (9678), Fisherman's wife (9679), Fisherman's wife (9680), A strange courier (9684), Courier (9685), Hooded client (9691), Fremennik shipmaster (9707), Fremennik shipmaster (9708), Fremennik warrior (9709), Dungeoneering tutor (9712), Thok, Master of Dungeoneering (9713), Estrith (9714), Sonje (9715), Wiglaf (9716), null (9722), null (9723), Sagittare (9753), Sagittare (9754), Sagittare (9755), Sagittare (9756), Sagittare (9757), Sagittare (9758), Sagittare (9759), Sagittare (9760), Sagittare (9761), Sagittare (9762), Sagittare (9763), Sagittare (9764), Sagittare (9765), Sagittare (9766), Astea Frostweb (9965), Astea Frostweb (9966), Astea Frostweb (9967), Astea Frostweb (9968), Astea Frostweb (9969), Astea Frostweb (9970), Astea Frostweb (9971), Astea Frostweb (9972), Astea Frostweb (9973), Astea Frostweb (9974), Astea Frostweb (9975), Astea Frostweb (9976), Astea Frostweb (9977), Astea Frostweb (9978), Astea Frostweb (9979), Astea Frostweb (9980), Astea Frostweb (9981), Astea Frostweb (9982), Astea Frostweb (9983), Astea Frostweb (9984), Astea Frostweb (9985), Astea Frostweb (9986), Astea Frostweb (9987), Astea Frostweb (9988), Astea Frostweb (9989), Astea Frostweb (9990), Astea Frostweb (9991), Astea Frostweb (9992), Astea Frostweb (9993), Astea Frostweb (9994), Astea Frostweb (9995), Astea Frostweb (9996), Astea Frostweb (9997), Astea Frostweb (9998), Astea Frostweb (9999), Astea Frostweb (10000), Astea Frostweb (10001), Astea Frostweb (10002), Astea Frostweb (10003), Astea Frostweb (10004), Astea Frostweb (10005), Astea Frostweb (10006), Astea Frostweb (10007), Astea Frostweb (10008), Astea Frostweb (10009), Astea Frostweb (10010), Astea Frostweb (10011), Astea Frostweb (10012), Astea Frostweb (10013), Astea Frostweb (10014), Astea Frostweb (10015), Astea Frostweb (10016), Astea Frostweb (10017), Astea Frostweb (10018), Astea Frostweb (10019), Astea Frostweb (10020), Astea Frostweb (10021), Portal controller (10142), Earth warrior (10178), Earth warrior (10179), Earth warrior (10180), Earth warrior (10181), Earth warrior (10182), Earth warrior (10183), Earth warrior (10184), Earth warrior (10185), Earth warrior (10186), Earth warrior (10187), Necromancer (10196), Necromancer (10197), Necromancer (10198), Necromancer (10199), Necromancer (10200), Necromancer (10201), Necromancer (10202), Necromancer (10203), Necromancer (10204), Necromancer (10205), Necromancer (10206), Ice warrior (10225), Ice warrior (10226), Ice warrior (10227), Ice warrior (10228), Ice warrior (10229), Ice warrior (10230), Ice warrior (10231), Ice warrior (10232), Ice warrior (10233), Ice warrior (10234), Ice warrior (10235), Forgotten warrior (10246), Forgotten warrior (10247), Forgotten warrior (10248), Forgotten warrior (10249), Forgotten warrior (10250), Forgotten warrior (10251), Forgotten warrior (10252), Forgotten warrior (10253), Forgotten warrior (10254), Forgotten warrior (10255), Forgotten warrior (10256), Forgotten warrior (10257), Forgotten warrior (10258), Forgotten warrior (10259), Forgotten warrior (10260), Forgotten warrior (10261), Forgotten warrior (10262), Forgotten warrior (10263), Forgotten warrior (10264), Forgotten warrior (10265), Forgotten warrior (10266), Forgotten warrior (10267), Forgotten warrior (10268), Forgotten warrior (10269), Forgotten warrior (10270), Forgotten warrior (10271), Forgotten warrior (10272), Forgotten warrior (10273), Forgotten warrior (10274), Forgotten warrior (10275), Forgotten warrior (10276), Forgotten warrior (10277), Forgotten warrior (10278), Forgotten warrior (10279), Forgotten warrior (10280), Forgotten warrior (10281), Forgotten warrior (10282), Forgotten warrior (10283), Forgotten warrior (10284), Forgotten warrior (10285), Forgotten warrior (10286), Forgotten warrior (10287), Forgotten warrior (10288), Forgotten warrior (10289), Forgotten warrior (10290), Forgotten warrior (10291), Forgotten warrior (10292), Forgotten warrior (10293), Forgotten warrior (10294), Forgotten warrior (10295), Forgotten warrior (10296), Forgotten warrior (10297), Forgotten warrior (10298), Forgotten warrior (10299), Forgotten warrior (10300), Forgotten warrior (10301), Forgotten warrior (10302), Forgotten warrior (10303), Forgotten warrior (10304), Forgotten warrior (10305), Forgotten warrior (10306), Forgotten warrior (10307), Forgotten warrior (10308), Forgotten ranger (10320), Forgotten ranger (10321), Forgotten ranger (10322), Forgotten ranger (10323), Forgotten ranger (10324), Forgotten ranger (10325), Forgotten ranger (10326), Forgotten ranger (10327), Forgotten ranger (10328), Forgotten ranger (10329), Forgotten ranger (10330), Forgotten ranger (10331), Forgotten ranger (10332), Forgotten ranger (10333), Forgotten ranger (10334), Forgotten ranger (10335), Forgotten ranger (10336), Forgotten ranger (10337), Forgotten ranger (10338), Forgotten ranger (10339), Forgotten ranger (10340), Forgotten ranger (10341), Forgotten ranger (10342), Forgotten ranger (10343), Forgotten ranger (10344), Forgotten ranger (10345), Forgotten ranger (10346), Forgotten ranger (10347), Forgotten ranger (10348), Forgotten ranger (10349), Forgotten ranger (10350), Forgotten ranger (10351), Forgotten ranger (10352), Forgotten ranger (10353), Forgotten ranger (10354), Forgotten ranger (10355), Forgotten ranger (10356), Forgotten ranger (10357), Forgotten ranger (10358), Forgotten ranger (10359), Forgotten ranger (10360), Forgotten ranger (10361), Forgotten ranger (10362), Forgotten ranger (10363), Forgotten warrior (10397), Forgotten warrior (10398), Forgotten warrior (10399), Forgotten warrior (10400), Forgotten warrior (10401), Forgotten warrior (10402), Forgotten warrior (10403), Forgotten warrior (10404), Forgotten warrior (10405), Forgotten warrior (10406), Forgotten warrior (10407), Forgotten warrior (10408), Forgotten warrior (10409), Forgotten warrior (10410), Forgotten warrior (10411), Forgotten warrior (10412), Forgotten warrior (10413), Forgotten warrior (10414), Forgotten warrior (10415), Forgotten warrior (10416), Forgotten warrior (10417), Forgotten warrior (10418), Forgotten warrior (10419), Forgotten warrior (10420), Forgotten warrior (10421), Forgotten warrior (10422), Forgotten warrior (10423), Forgotten warrior (10424), Forgotten warrior (10425), Forgotten warrior (10426), Forgotten warrior (10427), Forgotten warrior (10428), Forgotten warrior (10429), Forgotten warrior (10430), Forgotten warrior (10431), Forgotten warrior (10432), Forgotten warrior (10433), Forgotten warrior (10434), Forgotten warrior (10435), Forgotten warrior (10436), Forgotten warrior (10437), Forgotten warrior (10438), Forgotten warrior (10439), Forgotten warrior (10440), Forgotten warrior (10441), Forgotten warrior (10442), Forgotten warrior (10443), Forgotten warrior (10444), Forgotten warrior (10445), Forgotten warrior (10446), Forgotten warrior (10447), Forgotten warrior (10448), Forgotten warrior (10449), Forgotten warrior (10450), Forgotten warrior (10451), Forgotten warrior (10452), Forgotten warrior (10453), Forgotten warrior (10454), Forgotten warrior (10455), Forgotten warrior (10456), Forgotten warrior (10457), Forgotten warrior (10458), Forgotten warrior (10459), Forgotten warrior (10507), Forgotten warrior (10508), Forgotten warrior (10509), Forgotten warrior (10510), Forgotten warrior (10511), Forgotten warrior (10512), Forgotten warrior (10513), Forgotten warrior (10514), Forgotten warrior (10515), Forgotten warrior (10516), Forgotten warrior (10517), Forgotten warrior (10518), Forgotten warrior (10519), Forgotten warrior (10520), Forgotten warrior (10521), Forgotten warrior (10522), Forgotten warrior (10523), Forgotten warrior (10524), Forgotten warrior (10525), Forgotten warrior (10526), Forgotten warrior (10527), Forgotten warrior (10528), Forgotten warrior (10529), Forgotten warrior (10530), Forgotten warrior (10531), Forgotten warrior (10532), Forgotten warrior (10533), Forgotten warrior (10534), Forgotten warrior (10535), Forgotten warrior (10536), Forgotten warrior (10537), Forgotten warrior (10538), Forgotten warrior (10539), Forgotten warrior (10540), Forgotten warrior (10541), Forgotten warrior (10542), Forgotten warrior (10543), Forgotten warrior (10544), Forgotten warrior (10545), Forgotten warrior (10546), Forgotten warrior (10547), Forgotten warrior (10548), Forgotten mage (10560), Forgotten mage (10561), Forgotten mage (10562), Forgotten mage (10563), Forgotten mage (10564), Forgotten mage (10565), Forgotten mage (10566), Forgotten mage (10567), Forgotten mage (10568), Forgotten mage (10569), Forgotten mage (10570), Forgotten mage (10571), Forgotten mage (10572), Forgotten mage (10573), Forgotten mage (10574), Forgotten mage (10575), Forgotten mage (10576), Forgotten mage (10577), Forgotten mage (10578), Forgotten mage (10579), Forgotten mage (10580), Forgotten mage (10581), Forgotten mage (10582), Forgotten mage (10583), Forgotten mage (10584), Forgotten mage (10585), Forgotten mage (10586), Forgotten mage (10587), Forgotten mage (10588), Forgotten mage (10589), Forgotten mage (10590), Forgotten mage (10591), Forgotten mage (10592), Forgotten mage (10593), Forgotten mage (10594), Forgotten mage (10595), Forgotten mage (10596), Forgotten mage (10597), Forgotten mage (10598), Forgotten mage (10599), Forgotten mage (10600), Forgotten mage (10601), Forgotten mage (10602), Forgotten mage (10603), Spiritual guardian (10700), Ankou (10736), Ankou (10737), Ankou (10738), Ankou (10739), Ankou (10740), Ankou (10741), Ankou (10742), Ankou (10743), Forgotten warrior (10843), Forgotten warrior (10844), Forgotten warrior (10845), Forgotten warrior (10846), Forgotten warrior (10847), Forgotten warrior (10848), Forgotten warrior (10849), Forgotten warrior (10850), Forgotten warrior (10851), Forgotten warrior (10852), Forgotten warrior (10853), Forgotten warrior (10854), Forgotten warrior (10855), Forgotten warrior (10856), Forgotten warrior (10857), Forgotten warrior (10858), Forgotten warrior (10859), Forgotten warrior (10860), Forgotten warrior (10861), Forgotten warrior (10862), Forgotten warrior (10863), Forgotten warrior (10864), Forgotten warrior (10865), Forgotten warrior (10866), Forgotten warrior (10867), Forgotten warrior (10868), Forgotten warrior (10869), Forgotten warrior (10870), Forgotten warrior (10871), Forgotten warrior (10872), Forgotten warrior (10873), Forgotten warrior (10874), Forgotten warrior (10875), Forgotten warrior (10876), Forgotten warrior (10877), Forgotten warrior (10878), Forgotten warrior (10879), Forgotten warrior (10880), Forgotten warrior (10881), Forgotten warrior (10882), Forgotten warrior (10883), Forgotten warrior (10884), Forgotten warrior (10885), Forgotten warrior (10886), Forgotten warrior (10887), Forgotten warrior (10888), Forgotten warrior (10889), Forgotten warrior (10890), Forgotten warrior (10891), Forgotten warrior (10892), Forgotten warrior (10893), Forgotten warrior (10894), Forgotten warrior (10895), Forgotten warrior (10896), Forgotten warrior (10897), Forgotten warrior (10898), Forgotten warrior (10899), Forgotten warrior (10900), Forgotten warrior (10901), Forgotten warrior (10902), Forgotten warrior (10903), Forgotten warrior (10904), Forgotten warrior (10905), Statue (10966), Statue (10967), Statue (10968), Fremennik scout (11001), Statue (11012), Statue (11013), Statue (11014), Statue (11015), Statue (11016), Statue (11017), Statue (11018), Statue (11019), Statue (11020), Statue (11021), Statue (11022), Statue (11023), Statue (11024), Statue (11025), Statue (11026), Statue (11027), Statue (11028), Statue (11029), Statue (11030), Statue (11031), Statue (11032), Statue (11033), Statue (11034), Statue (11035), Smuggler (11226), Poltergeist (11245), Astea Frostweb (11246), Mage statue (11247), Ranger statue (11248), Melee statue (11249), Mage statue (11250), Ranger statue (11251), Melee statue (11252), rand_skeletal_melee (11253), rand_skeletal_mage (11254), rand_skeletal_ranger (11255), Ravenous vampire (11264), Ravenous ghoul (11265), Stiles (11267), Market guard (11269), Guard (11271), Registrar (11272), Darren Lightfinger (11274), Guildmaster Darren Lightfinger (11275), Guildmaster Darren Lightfinger (11276), Guildmaster Darren Lightfinger (11277), Guildmaster Darren Lightfinger (11278), Chief Thief Robin (11280), Pickpocketing trainer (11281), Pickpocketing volunteer (11282), Pickpocketing volunteer (11284), Pickpocketing volunteer (11286), Coshing volunteer (11288), Coshing volunteer (11290), Coshing volunteer (11292), Locks and traps trainer (11294), Safe-cracking trainer (11295), Advanced pickpocketing trainer (11296), Dodgy Derek (11298), Mr Pinsworth (11299), Rick (11303), Maid (11304), Cook (11305), Butler (11306), Demon butler (11307), Mugger (level: 6) (11452), Roger Murray (11453), Moira (11455), Molly (11456), Botros (11458), Oliver B'Gard (11459), Richard Maney (11460), Clerk (11461), Court judge (11462), Jury (11463), Defender (11464), Prosecutor (11465), null (11466), null (11467), null (11468), null (11469), null (11470), null (11471), null (11472), null (11473), null (11474), Xenia (11476), null (11478), null (11479), Knight Bernard (11480), Knight Bernard (11481), null (11482), null (11483), Knight Ami (11484), Knight Ami (11485), null (11486), null (11487), Knight Diana (11488), Knight Diana (11489), Commodore Matthias (11493), Jessika (11494), Captain Tyr (11495), Knight Bernard (11496), null (11497), Commodore Matthias (11498), Jessika (11499), null (11500), null (11501), null (11502), null (11503), null (11504), null (11505), Commander Korasi (11506), Commander Korasi (11507), Commander Korasi (11508), null (11509), Captain Tyr (11510), Commodore Tyr (11511), Sir Tiffy (11512), null (11513), null (11514), null (11515), null (11516), null (11517), null (11518), null (11519), null (11520), null (11521), Knight Ami (11522), Knight Diana (11523), Terry Gord (11524), Mrs Gord (11525), Knight Mikhal (11526), Mariah (11527), Mariah (11528), Squire Sam (11529), Knight Bernard (11530), Mysterious figure (11538), Commodore Matthias (11539), Knight Diana (11541), Knight Bernard (11542), Knight Ami (11543), Squire Sam (11544), Knight Mikhal (11545), Milk seller (11547), Commodore Matthias (11548), Commander Korasi (11549), Knight Diana (11550), Knight Bernard (11551), Knight Ami (11552), Wise Old Man (11568), Wise Old Man (11569), Zenevivia (11571), Wise Old Man (11575), Zenevivia (11577), Zenevivia (11578), Zenevivia (11579), Zenevivia (11580), Zenevivia (11581), Zenevivia (11582), Wise Old Man (11583), Zenevivia (11599), Sedridor (11601), Wizard Mizgog (11602), Wizard Grayzag (11603), Traiborn (11604), Dionysius (11607), Dionysius (11608), Dionysius (11609), Dionysius (11610), Dionysius (11611), Dionysius (11612), Zenevivia (11613), Zenevivia (11614), Zenevivia (11615), Zenevivia (11616), Zenevivia (11617), Yambo (11618), Yambo (11619), My Name Rocks (11623), Pee Ker XXL (11624), Mabel (11625), Zenevivia (11628), Mabel (11631), Guthix wizard (11638), Guthix wizard (11639), Ali Tist (11642), Ali Tist (11643), Customer (11645), Customer (11647), Captain Korasi (11648), Jessika (11660), Shopkeeper Kofi (11674), Shopkeeper Kofi (11675), Bartender (11676), Shop assistant (11678), Shop assistant (11679), Chemist (11680), Commodore Tyr (11682), Captain Korasi (11683), Jessika (11684), Black Knight guard (11685), Indentured worker (11687), Laden indentured worker (11688), Laden indentured worker (11689), Black Knight doorkeeper (11690), Black Knight doorkeeper (11692), Indentured worker (11693), Indentured worker (11694), Indentured worker (11695), Black Knight guardian (11696), Black Knight guardian (11697), Void leech (11698), Gerrant (11699), Captain Tobias (11700), Seaman Lorris (11701), Seaman Thresnor (11702), Captain Tobias (11703), Seaman Lorris (11704), Seaman Thresnor (11705), Bartender (11706), Bartender (11707), Portal controller (11721), Necrolord (11737), Necrolord (11738), Necrolord (11739), Necrolord (11740), Necrolord (11741), Necrolord (11742), Necrolord (11743), Necrolord (11744), Necrolord (11745), Necrolord (11746), Necrolord (11747), Necrolord (11748), Necrolord (11749), Necrolord (11750), Necrolord (11751), Mysterious mage (11887), Mysterious mage (11888), Mysterious mage (11889), Mysterious mage (11890), Mysterious mage (11891), Mysterious mage (11892), Mysterious mage (11893), Mysterious mage (11894), Skeletal warrior (11940), Skeletal warrior (11941), Skeletal warrior (11942), Skeletal warrior (11943), Skeletal warrior (11944), Skeletal warrior (11945), Skeletal warrior (11946), Skeletal warrior (11947), Skeletal warrior (11948), Skeletal warrior (11949), Skeletal warrior (11950), Skeletal warrior (11951), Skeletal warrior (11952), Skeletal warrior (11953), Skeletal warrior (11954), Skeletal warrior (11955), Skeletal warrior (11956), Skeletal warrior (11957), Skeletal warrior (11958), Skeletal warrior (11959), Skeletal warrior (11960), Skeletal warrior (11961), Skeletal warrior (11962), Skeletal warrior (11963), Skeletal warrior (11964), Skeletal warrior (11965), Skeletal warrior (11966), Skeletal warrior (11967), Skeletal warrior (11968), Skeletal warrior (11969), Skeletal warrior (11970), Skeletal warrior (11971), Skeletal warrior (11972), Skeletal warrior (11973), Skeletal warrior (11974), Skeletal warrior (11975), Skeletal warrior (11976), Skeletal warrior (11977), Skeletal warrior (11978), Skeletal warrior (11979), Skeletal warrior (11980), Skeletal warrior (11981), Skeletal warrior (11982), Skeletal warrior (11983), Skeletal warrior (11984), Skeletal sorcerer (11999), Skeletal sorcerer (12000), Skeletal sorcerer (12001), Skeletal sorcerer (12002), Skeletal sorcerer (12003), Skeletal sorcerer (12004), Skeletal sorcerer (12005), Skeletal sorcerer (12006), Skeletal sorcerer (12007), Skeletal sorcerer (12008), Skeletal sorcerer (12009), Skeletal sorcerer (12010), Skeletal sorcerer (12011), Skeletal sorcerer (12012), Skeletal sorcerer (12013), Skeletal sorcerer (12014), Skeletal sorcerer (12015), Skeletal sorcerer (12016), Skeletal sorcerer (12017), Skeletal sorcerer (12018), Skeletal sorcerer (12019), Skeletal sorcerer (12020), Skeletal sorcerer (12021), Skeletal sorcerer (12022), Skeletal sorcerer (12023), Skeletal sorcerer (12024), Skeletal sorcerer (12025), Skeletal sorcerer (12026), Skeletal sorcerer (12027), Skeletal sorcerer (12028), Skeletal sorcerer (12029), Skeletal sorcerer (12030), Skeletal sorcerer (12031), Skeletal sorcerer (12032), Skeletal sorcerer (12033), Skeletal sorcerer (12034), Skeletal sorcerer (12035), Skeletal sorcerer (12036), Skeletal sorcerer (12037), Skeletal sorcerer (12038), Skeletal sorcerer (12039), Skeletal sorcerer (12040), Skeletal sorcerer (12041), Skeletal sorcerer (12042), Skeletal sorcerer (12043), Skeletal archer (12044), Skeletal archer (12045), Skeletal archer (12046), Skeletal archer (12047), Skeletal archer (12048), Skeletal archer (12049), Skeletal archer (12050), Skeletal archer (12051), Skeletal archer (12052), Skeletal archer (12053), Skeletal archer (12054), Skeletal archer (12055), Skeletal archer (12056), Skeletal archer (12057), Skeletal archer (12058), Skeletal archer (12059), Skeletal archer (12060), Skeletal archer (12061), Skeletal archer (12062), Skeletal archer (12063), Skeletal archer (12064), Skeletal archer (12065), Skeletal archer (12066), Skeletal archer (12067), Skeletal archer (12068), Skeletal archer (12069), Skeletal archer (12070), Skeletal archer (12071), Skeletal archer (12072), Skeletal archer (12073), Skeletal archer (12074), Skeletal archer (12075), Skeletal archer (12076), Skeletal archer (12077), Skeletal archer (12078), Skeletal archer (12079), Skeletal archer (12080), Skeletal archer (12081), Skeletal archer (12082), Skeletal archer (12083), Skeletal archer (12084), Skeletal archer (12085), Skeletal archer (12086), Skeletal archer (12087), Skeletal archer (12088), Statue (12106), Statue (12107), Statue (12108), Statue (12109), Statue (12110), Statue (12111), Statue (12112), Statue (12113), Statue (12114), Enemy Scout (12162), Enemy Foot Soldier (12163), Enemy Halberdier (12164), Archer (12178), Mage (12179), Knight (12180), Enemy Champion (12181), Enemy Archer (12182), Enemy Mage (12183), Enemy Knight (12184) +Linked graphics: 56, 432, 560, 720, 1174, 1557, 1578, 1956, 1957, 1971, 2205, 2685, 2693, 2694, 2695, 2696, 2697, 2743 +Linked objects: Sir Palomedes (2901), Commodore Matthias (4458), Korasi (4459), Diana (4460), Bernard (4461), Ami (4463), Commodore Matthias (4480), Commodore Matthias (4481), Commodore Matthias (4908), Commodore Matthias (4910), Commodore Matthias (4911), Commodore Matthias (4912), Korasi (7390), John Everyman (12962), null (12964), Clive Dresdon (12966), Burt McKrakon (12967), Mavis Grey (12969), Korasi (13495), Korasi (13496), Korasi (15385), Music appreciator (16037), Music appreciator (16038), Music appreciator (16039), Music appreciator (16040), Music appreciator (16041), Music appreciator (16045), Music appreciator (16046), Korasi (20322), Fremennik warrior (25365), Fremennik warrior (25366), Sir Lucan (25664), Sir Lancelot (25666), Sir Bedivere (25667), Korasi (25891), Diana (25892), Diana (25893), Diana (25894), Diana (25948), Diana (25949), Diana (25950), Bernard (25951), Bernard (25952), Bernard (25968), Bernard (25976), Bernard (25977), Bernard (26001), Ami (26003), Ami (26018), Ami (26019), Ami (26488), Ami (26489), Ami (26490), Ezekial Lovecraft (29738), Customs Sergeant (31459), Fremennik warrior (35929), Sir Vant (37582), Warrior woman (39184), Clive (39185), Guard (39215), Guard (39216), Guard (39217), Guard (39218), Guard (39219), Wounded guardian (41167), Unconscious guardian (41168), Dermot (44280), Ardal (44281), Frank (44282), Cookie (44317), Border guard (45856), Border guard (45857), Border guard (45858), Border guard (45859), Border guard (45862), Border guard (45863), Silas (47251) + +Base: 1 +Linked animations: [2055, 2056, 2057] +Linked objects: null (6657) + +Base: 2 +Linked animations: [2054] +Linked objects: Blue tears (6661), Green tears (6662), Blue tears (6665), Green tears (6666) + +Base: 3 +Linked animations: [2051, 2052, 2053, 10937, 10938] +Linked NPCs: Light creature (2021), Light creature (2022), Light creature (4325), Light creature (8454), Light creature (8455) +Linked graphics: 1932, 1933 + +Base: 4 +Linked animations: [2058] +Linked graphics: 397 + +Base: 5 +Linked animations: [1535, 2172, 2260, 2996, 12549] +Linked graphics: 409, 503, 504 +Linked objects: Abyssal rift (7171) + +Base: 6 +Linked animations: [2174, 2175, 2176, 2177] +Linked objects: null (7181) + +Base: 7 +Linked animations: [2178] +Linked objects: Fire rift (7129), Earth rift (7130), Body rift (7131), Cosmic rift (7132), Nature rift (7133), Chaos rift (7134), Law rift (7135), Death rift (7136), Water rift (7137), Soul rift (7138), Air rift (7139), Mind rift (7140), Blood rift (7141), Etching (41044), Etching (41046), Etching (41048), Etching (41050) + +Base: 8 +Linked animations: [2173] +Linked objects: Tendrils (7189), Tendrils (7190), Tendrils (7191), Stinking tendrils (16109), Dripping tendrils (16110), Tendrils (16111), Tendrils (27294), Tendrils (27295), Tendrils (27296) + +Base: 9 +Linked animations: [2179, 2180, 2181, 2182, 2183] +Linked NPCs: Abyssal leech (2263) + +Base: 10 +Linked animations: [2184, 2185, 2186, 2187, 2188, 2189] +Linked NPCs: Abyssal guardian (2264) + +Base: 11 +Linked animations: [2190, 2191, 2192, 2193, 2194] +Linked NPCs: Abyssal walker (2265) + +Base: 12 +Linked animations: [2871] +Linked objects: null (10186), null (10237), null (16650), null (16662) + +Base: 13 +Linked animations: [2346] +Linked objects: Viking Boat (8926), Viking Boat (25298), Brundt's boat (47261) + +Base: 14 +Linked animations: [2359, 2360] +Linked objects: Flag (8954), Flag (8955), Bank deposit box (39830) + +Base: 15 +Linked animations: [2362] +Linked NPCs: Egg (2450) + +Base: 16 +Linked animations: [2849, 2850, 2851, 2852, 2853, 2854, 2855, 2856] +Linked NPCs: Dagannoth Supreme (2881), Dagannoth Prime (2882), Dagannoth Rex (2883) + +Base: 17 +Linked animations: [2857, 2858, 2859, 2860, 2861, 2862] +Linked NPCs: Rock lobster (2889) + +Base: 18 +Linked animations: [2363, 2364, 2365, 2366, 2367] +Linked NPCs: Wallasalki (2457), Wallasalki (2884), Wallasalki (9096) + +Base: 19 +Linked animations: [2863, 2864, 2865, 2866, 2867, 2868, 2869] +Linked NPCs: Spinolyp (2892), Spinolyp (2894), Spinolyp (2896) + +Base: 20 +Linked animations: [2347, 2348, 2349, 2350] +Linked objects: Door (8958), Door (8959), Door (8960), Door (8961), Door (8962), Door (8963) + +Base: 21 +Linked animations: [2872] +Linked graphics: 473 + +Base: 22 +Linked animations: [2873] +Linked graphics: 474 + +Base: 23 +Linked animations: [2874] +Linked graphics: 475 + +Base: 24 +Linked animations: [2875] +Linked graphics: 476 + +Base: 25 +Linked animations: [2848] +Linked graphics: 477 + +Base: 26 +Linked animations: [9592] +Linked objects: Standing Torch (10178), Standing Torch (10179) + +Base: 27 +Linked animations: [2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358] +Linked NPCs: Door (2441), Door (2442), Door-support (2443), Door (2444), Door (2445), Door-support (2446), Door (2447), Door (2448) + +Base: 28 +Linked animations: [2131] +Linked objects: null (6949) + +Base: 29 +Linked animations: [2132] +Linked graphics: 406 + +Base: 30 +Linked animations: [1812] +Linked objects: null (5921) + +Base: 31 +Linked animations: [2126] +Linked NPCs: Fishing spot (2067), Fishing spot (2068), Fishing spot (5748), Fishing spot (5749), Fishing spot (7636) + +Base: 32 +Linked animations: [1774, 1775, 1776, 1777, 1778, 1779, 1780] + +Base: 33 +Linked animations: [1783, 1784, 1785, 1786, 1787, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 4210, 4211] +Linked NPCs: Nardok (4312) + +Base: 34 +Linked animations: [1781, 1782] +Linked NPCs: Cave bug larva (1833) + +Base: 35 +Linked animations: [1788, 1789, 1790, 1791, 1792] +Linked NPCs: Cave slime (1831), Cave slime (10696) + +Base: 36 +Linked animations: [1793, 1794, 1795, 1796, 1797, 2374, 2375] +Linked NPCs: null (390), null (391), null (392), null (393), null (394), null (395), null (396), null (403), Giant frog (1828), Big frog (1829), Frog (1830), Frog Herald (2472), Frog (2689), Frog (3300), Frog (8636), Frog (8637), Frog (8638) + +Base: 37 +Linked animations: [1798, 1799, 1800, 1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808] +Linked NPCs: Hole in the wall (2058), Wall beast (7823) + +Base: 38 +Linked animations: [3028] +Linked objects: null (10761) + +Base: 39 +Linked animations: [3022] +Linked objects: null (10715), null (10716), null (10717), null (10718) + +Base: 40 +Linked animations: [3030, 3031] +Linked objects: Telekinetic Teleport (10778), Enchanters Teleport (10779), Alchemists Teleport (10780), Graveyard Teleport (10781), Exit Teleport (10782) + +Base: 41 +Linked animations: [3029] +Linked objects: Statue (10738), Statue (10739) + +Base: 42 +Linked animations: [3023] +Linked NPCs: Flying Book (3094), Flying Book (3095), Animated book (10744), Animated book (10745), Animated book (10746), Animated book (10747), Animated book (10748), Animated book (10749), Animated book (10750), Animated book (10751), Animated book (10752), Animated book (10753) + +Base: 43 +Linked animations: [3025] + +Base: 44 +Linked animations: [3033, 3034, 3035] +Linked NPCs: Entrance Guardian (3097), Telekinetic Guardian (3098), Alchemy Guardian (3099), Enchantment Guardian (3100), Graveyard Guardian (3101), Maze Guardian (3102), Rewards Guardian (3103), null (9435), Rune guardian (9436), null (9438), Rune guardian (9439), Rune guardian (9460), Rune guardian (9461), Rune guardian (9642), Rune guardian (9643), Rune guardian (9644), Rune guardian (9645), Rune guardian (9646), Rune guardian (9647), Rune guardian (9648), Rune guardian (9649), Rune guardian (9650), Rune guardian (9651), Rune guardian (9652), Rune guardian (9653), Rune guardian (9654), Rune guardian (9655), Rune guardian (9656), Rune guardian (9657), Rune guardian (9658), Rune guardian (9659), Rune guardian (9660), Rune guardian (9661), Rune guardian (9662), Rune guardian (9663), Rune guardian (9664), Rune guardian (9665), Rune guardian (9666), Rune guardian (9667), Rune guardian (9668), Rune guardian (9669) + +Base: 45 +Linked animations: [3026, 3027, 3244, 3245] +Linked NPCs: Charmed Warrior (3104), Charmed Warrior (3105), Charmed Warrior (3106), Charmed Warrior (3107), Sweeper (3298) + +Base: 46 +Linked animations: [3021] +Linked graphics: 520, 521, 522, 523 + +Base: 47 +Linked animations: [3024] +Linked graphics: 519 + +Base: 48 +Linked animations: [2772, 2773] +Linked NPCs: Fishbowl (8737), Fishbowl (8738), Fishbowl (8739) + +Base: 49 +Linked animations: [2216] + +Base: 50 +Linked animations: [2202, 2203] +Linked objects: Floor (7230) + +Base: 51 +Linked animations: [2205] + +Base: 52 +Linked animations: [2204] +Linked objects: Pendulum (7241), Pendulum (49659), Pendulum (49660), Pendulum (49661), Pendulum (54309) + +Base: 53 +Linked animations: [2200, 2201] +Linked objects: Wall (7229) + +Base: 54 +Linked animations: [2212] +Linked NPCs: Spin Blades (2272), Spin Blades (2273) +Linked objects: Spin Blades (7253) + +Base: 55 +Linked animations: [2207] + +Base: 56 +Linked animations: [2211] + +Base: 57 +Linked animations: [2208, 2209] +Linked objects: Wall (7248), Trap (10034), Wall (49656), Wall (49657), Wall (49658), Wall (54308) + +Base: 58 +Linked animations: [2197, 2198] +Linked objects: Wall (7225) + +Base: 59 +Linked animations: [2195, 2196] +Linked objects: Wall (7228) + +Base: 60 +Linked animations: [2213, 2214, 2215] + +Base: 61 +Linked animations: [3940] +Linked objects: Banner (14755), Banner (14756), Banner (14757) + +Base: 62 +Linked animations: [3943] +Linked objects: Rock (14814), Rock (14815), Rock (14816), Rock (14817) + +Base: 63 + +Base: 64 +Linked animations: [3144, 3145, 3146, 3147, 3148, 3149] +Linked NPCs: Chaos Elemental (3200) + +Base: 65 +Linked animations: [3153] +Linked graphics: 554 + +Base: 66 +Linked animations: [3150] +Linked graphics: 550, 553, 556 + +Base: 67 +Linked animations: [3152, 5359, 5399] +Linked graphics: 552, 555, 865, 971 + +Base: 68 +Linked animations: [3151, 5358] +Linked graphics: 551, 557, 970 + +Base: 69 +Linked animations: [3154, 3155] + +Base: 70 +Linked animations: [3156] +Linked graphics: 558 + +Base: 71 +Linked animations: [3158, 7079] +Linked graphics: 559, 1225 + +Base: 72 +Linked animations: [3942, 4625] +Linked graphics: 660, 782 + +Base: 73 +Linked animations: [3941] +Linked graphics: 659, 783 + +Base: 74 +Linked animations: [3305] +Linked objects: A sinking canoe (12159), A sinking canoe (12160), A sinking canoe (12161), A sinking canoe (12162) + +Base: 75 +Linked animations: [3304] +Linked objects: Canoe station (12145), Canoe station (12151), Canoe station (12152), Canoe station (12153), Canoe station (12154) + +Base: 76 +Linked animations: [3306] +Linked objects: null (12168), null (12169), null (12170), null (12171) + +Base: 77 +Linked animations: [4119, 4120, 4121, 4122] + +Base: 78 +Linked animations: [1824, 1825, 1826, 1827, 3874, 3875, 3876, 3877] + +Base: 79 + +Base: 80 +Linked animations: [584, 585, 586, 587] + +Base: 81 +Linked animations: [580] + +Base: 82 +Linked animations: [554, 555, 556, 557, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 1637, 1854, 1991, 2674, 3321, 3510, 3512, 3513, 3514, 3515, 3516, 3517, 3518, 3519, 3520, 3521, 3522, 4221, 4587, 4588, 4589, 4590, 7651, 10084] + +Base: 83 +Linked animations: [1126] +Linked graphics: 1178 + +Base: 84 +Linked animations: [558, 559, 560, 561] + +Base: 85 +Linked animations: [1109] +Linked graphics: 270 + +Base: 86 +Linked animations: [1111, 1112, 1420, 5632, 10707] +Linked objects: Floor spikes (3582), Floor (7227), Strange floor (9294), Strange floor (9326), Floor (20271), Floor (20915), Floor (20916), Floor (20917), Floor (20918), Floor (20919), Floor (28525), Floor (31350), Floor spikes (36712), Floor (41381), Floor (41382) + +Base: 87 +Linked animations: [1104] +Linked objects: Plank (3577) + +Base: 88 +Linked animations: [1108] +Linked objects: Ticket Dispenser (3581) + +Base: 89 +Linked animations: [1107, 2199, 2206] +Linked objects: Spinning blades (7224), Spinning blades (49662), Spinning blades (49663), Spinning blades (49664), Spinning blades (54310) + +Base: 90 +Linked animations: [12596, 12597] +Linked graphics: 2267, 2268 + +Base: 91 +Linked animations: [1103, 2210] +Linked objects: Blade (3567), Blade (7252), null (37432) + +Base: 92 +Linked animations: [3070] +Linked objects: Stone block (10875), null (10877), null (10878), null (10879), null (10880), null (10881) + +Base: 93 +Linked animations: [2069, 2070, 2071, 2072, 2073] +Linked NPCs: Bloodworm (2031) + +Base: 94 +Linked animations: [2086, 2087] +Linked graphics: 400, 401 + +Base: 95 +Linked animations: [2083] +Linked graphics: 398 + +Base: 96 +Linked animations: [2084] +Linked graphics: 399 + +Base: 97 +Linked animations: [2439] +Linked objects: Smoke (9099), Smoke (9115), Pipes (9117), Pipes (9121), Smoke (9123) + +Base: 98 +Linked animations: [2435, 2437] +Linked objects: Conveyor belt (9100), Conveyor belt (9101) + +Base: 99 +Linked animations: [2436, 2438] + +Base: 100 +Linked animations: [2451] +Linked objects: Drive belt (9103), Cogs (9105) + +Base: 101 +Linked animations: [2434] + +Base: 102 +Linked animations: [2440] +Linked objects: Bar dispenser (9094) + +Base: 103 +Linked animations: [2452, 2453, 2454, 2455, 2456, 2457, 2458, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, 2471, 2472, 2473, 2474, 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2482, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2496, 2497, 2498, 2499, 2500, 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, 2512, 2513, 2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525, 2526, 2527, 2528, 2529, 2530, 2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541, 2542, 2543, 2544, 2545, 2546, 2547, 2548, 2549, 2550, 2551, 2552] + +Base: 104 +Linked animations: [2448, 2449] + +Base: 105 +Linked animations: [2444, 2445, 2446, 2447] + +Base: 106 +Linked animations: [1430] +Linked objects: Saradomin flag (4900), Zamorak flag (4901), Saradomin flag (4902), Zamorak flag (4903), null (4906), null (4907), Swordshop Sign (5826) + +Base: 107 +Linked animations: [14176] +Linked graphics: 2691 + +Base: 108 +Linked animations: [1433] +Linked objects: Energy Barrier (52760), Energy Barrier (52761) + +Base: 109 +Linked animations: [1432] + +Base: 110 +Linked animations: [2976] + +Base: 111 +Linked animations: [2945, 2946, 2947, 2948, 2949, 2950, 2951, 2952] +Linked NPCs: Earth Warrior Champion (3057) + +Base: 112 +Linked animations: [2931, 2932] + +Base: 113 + +Base: 114 +Linked animations: [2969, 2970] +Linked graphics: 495, 496 + +Base: 115 +Linked animations: [2957] +Linked graphics: 494 + +Base: 116 +Linked animations: [2965, 6133] +Linked graphics: 488 + +Base: 117 + +Base: 118 +Linked animations: [2960] +Linked graphics: 490 + +Base: 119 +Linked animations: [2956] +Linked graphics: 493 + +Base: 120 +Linked animations: [3320] + +Base: 121 +Linked animations: [3309, 3310, 3311, 3312, 3313, 3314, 3315] +Linked NPCs: Giant Mole (3340), Baby Mole (3341), Baby Mole (3342), Baby Mole (3343) + +Base: 122 +Linked animations: [3317] +Linked graphics: 571 + +Base: 123 +Linked animations: [3318] +Linked graphics: 572 + +Base: 124 +Linked animations: [3319] +Linked graphics: 573 + +Base: 125 +Linked animations: [3316] +Linked graphics: 570 + +Base: 126 +Linked animations: [3927] +Linked objects: Banner (14283) + +Base: 127 +Linked animations: [3939] +Linked objects: Cauldron of Thunder (2142), Cauldron (14360), Cauldron (17161), Cauldron (17162), null (17163), Cauldron (40104) + +Base: 128 +Linked animations: [3925] + +Base: 129 +Linked animations: [3905, 3928, 3929, 3930, 3933, 3934, 3935, 3936, 3937, 3938, 6878, 6879, 6880, 6881, 6882, 6883, 6884, 6885, 6886] +Linked NPCs: Portal (6142), Portal (6143), Portal (6144), Portal (6145), Portal (6146), Portal (6147), Portal (6148), Portal (6149), Portal (6150), Portal (6151), Portal (6152), Portal (6153), Portal (6154), Portal (6155), Portal (6156), Portal (6157) +Linked graphics: 654 + +Base: 130 +Linked animations: [3932] +Linked objects: null (14310), null (14311), null (14312), null (14313) + +Base: 131 +Linked animations: [3892, 3893, 3894, 3895, 3896, 3897, 6870] +Linked NPCs: Brawler (3772), Brawler (3773), Brawler (3774), Brawler (3775), Brawler (3776), Brawler (11531) + +Base: 132 +Linked animations: [3918, 3919, 3920, 3921, 3922, 6872] +Linked NPCs: Defiler (3762), Defiler (3763), Defiler (3764), Defiler (3765), Defiler (3766), Defiler (3767), Defiler (3768), Defiler (3769), Defiler (3770), Defiler (3771), Defiler (11536), Defiler (11537) + +Base: 133 +Linked animations: [3913, 3914, 3915, 3916, 3917, 6871] +Linked NPCs: Ravager (3742), Ravager (3743), Ravager (3744), Ravager (3745), Ravager (3746) + +Base: 134 +Linked animations: [3898, 3899, 3900, 3901, 3902, 3903, 3904] +Linked NPCs: Shifter (3732), Shifter (3733), Shifter (3734), Shifter (3735), Shifter (3736), Shifter (3737), Shifter (3738), Shifter (3739), Shifter (3740), Shifter (3741), Shifter (11532), Shifter (11533) + +Base: 135 +Linked animations: [3906, 3907, 3908, 3909, 3910, 6876] +Linked NPCs: Spinner (3747), Spinner (3748), Spinner (3749), Spinner (3750), Spinner (3751), Spinner (11491) + +Base: 136 +Linked animations: [3886, 3887, 3888, 3890, 3891, 6869] +Linked NPCs: Splatter (3727), Splatter (3728), Splatter (3729), Splatter (3730), Splatter (3731), Splatter (11490) + +Base: 137 +Linked animations: [3878, 3879, 3880, 3881, 3882, 6868] +Linked NPCs: Torcher (3752), Torcher (3753), Torcher (3754), Torcher (3755), Torcher (3756), Torcher (3757), Torcher (3758), Torcher (3759), Torcher (3760), Torcher (3761), Torcher (11492), Torcher (11534), Torcher (11535), Torcher (11546) + +Base: 138 +Linked animations: [3923, 3924] +Linked graphics: 655, 657 + +Base: 139 +Linked animations: [3911] + +Base: 140 +Linked animations: [3912] +Linked graphics: 658 + +Base: 141 +Linked animations: [3889] +Linked graphics: 649, 650, 651, 652, 653 + +Base: 142 +Linked animations: [3883] +Linked graphics: 646 + +Base: 143 +Linked animations: [3885] +Linked graphics: 648 + +Base: 144 +Linked animations: [3884] +Linked graphics: 647 + +Base: 145 +Linked animations: [3748, 3749, 3750, 3751, 3752, 3753, 3754, 3755, 3756, 3757, 3758, 3759, 3760, 3761, 3762, 3763, 3764, 3765, 3766, 3767, 3768, 3769, 3770, 3771, 3772, 3773, 3774, 3775, 3776, 3778, 3779, 3780, 3781, 3782, 3783, 3784, 3785, 3786, 3787, 3788, 3789, 3790, 3791, 3792, 3793, 3794, 3795, 3796, 3797, 3798, 3799, 3800, 3801, 3802, 5910, 5911, 5912, 5913, 5914, 5915, 5916, 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, 5946, 5947, 5948, 5949, 5950, 5951, 5952, 5953, 5954, 5955, 5956, 5957, 5958, 5959, 5960, 5961, 5962, 5963, 5964] + +Base: 146 +Linked animations: [3777] + +Base: 147 +Linked animations: [3742, 3743, 5769] +Linked objects: Row boat (13841), Ripples (13842), Rowboat (18187), null (18188), null (18323), null (18402), Plank (19126), Crate (20510), Barrel (20511), null (21100), null (21110), null (21111), Stepping Stone (21120), Stepping Stone (21121), Stepping Stone (21122), Stepping Stone (21123), Stepping Stone (21124), Boat (21176), Boat (21177), null (26413), Crate (30288), Large rowboat (30337), Ripples (40086) + +Base: 148 +Linked animations: [3721, 3722, 3723, 3724, 3726, 3727] +Linked NPCs: Giant snail (3612), Giant snail (3613), Giant snail (3614) + +Base: 149 +Linked animations: [3728, 3730, 3731, 3732, 3733] +Linked NPCs: Head (3619), Head (3620) + +Base: 150 +Linked animations: [3744] +Linked graphics: 625 + +Base: 151 +Linked animations: [3725] +Linked graphics: 627 + +Base: 152 +Linked animations: [2821, 2823, 2825, 2827] +Linked NPCs: Half-Zombie (2870) + +Base: 153 +Linked animations: [2822, 2824, 2826] +Linked NPCs: Other Half-Zombie (2871) + +Base: 154 +Linked animations: [2834] +Linked graphics: 469 + +Base: 155 +Linked animations: [2830] +Linked graphics: 467 + +Base: 156 +Linked animations: [2832] +Linked graphics: 468 + +Base: 157 +Linked animations: [2828] +Linked graphics: 466 + +Base: 158 +Linked animations: [2601, 2602, 2603, 2604, 2605, 2606, 2607, 2608, 2609, 2610, 2611, 2612, 2613] + +Base: 159 +Linked animations: [2650, 2651, 2652, 2653, 2654, 2655, 2656] + +Base: 160 +Linked animations: [2618, 2619, 2620, 2621, 2622] +Linked NPCs: Giant bat (3711) + +Base: 161 +Linked animations: [2642, 2643, 2644, 2645, 2646, 2647] + +Base: 162 +Linked animations: [2623, 2624, 2625, 2626, 2627] + +Base: 163 +Linked animations: [2634, 2635, 2636, 2637, 2638, 2639] + +Base: 164 +Linked animations: [2628, 2629, 2630, 2631, 2632, 2633] + +Base: 165 +Linked animations: [2616] +Linked graphics: 439 + +Base: 166 +Linked animations: [2648] +Linked graphics: 445 + +Base: 167 +Linked animations: [2649] +Linked graphics: 446 + +Base: 168 +Linked animations: [2659] +Linked graphics: 448, 449, 450 + +Base: 169 +Linked animations: [2658] +Linked graphics: 447 + +Base: 170 +Linked animations: [2615] +Linked graphics: 442 + +Base: 171 +Linked animations: [2640] +Linked graphics: 444 + +Base: 172 +Linked animations: [2719] +Linked graphics: 453 + +Base: 173 +Linked animations: [2660] +Linked graphics: 451 + +Base: 174 +Linked animations: [2598] +Linked objects: null (9353), Open hot vent door (9366), Hot vent door (9367), Hot vent door (9368), Hot vent door (9369) + +Base: 175 +Linked animations: [2641] +Linked objects: Lava forge (9390) + +Base: 176 +Linked animations: [6500] +Linked objects: Beanstalk (24736) + +Base: 177 +Linked animations: [2600] +Linked objects: Egg (9377), Egg (9378), Egg (9379) + +Base: 178 +Linked animations: [2599] + +Base: 179 +Linked animations: [2657, 10372, 10373] +Linked objects: Viewing orb (9391), null (9392), Viewing orb (28194), Low-level orb (28209), Mid-level orb (28210), High-level orb (28211), null (28212), Viewing orb (38693) + +Base: 180 +Linked animations: [1598, 1599, 1600] +Linked objects: Goal (5146) + +Base: 181 +Linked animations: [1614, 1615, 1616, 1617, 1618] +Linked NPCs: Experiment (1677) + +Base: 182 +Linked animations: [1609, 1610, 1611, 1612, 1613] +Linked NPCs: Experiment (1678) + +Base: 183 +Linked animations: [1607, 1608, 2567, 2568] +Linked NPCs: Skullball (1659) + +Base: 184 + +Base: 185 + +Base: 186 +Linked animations: [11550, 11551] +Linked NPCs: Easter Bird (7417), Easter Bird (7418), Easter Bird (7419), Easter Bird (9696) + +Base: 187 +Linked animations: [3853] +Linked NPCs: Egg (3682), Egg (3683), Egg (3684), Egg (3685) + +Base: 188 + +Base: 189 +Linked animations: [6125] + +Base: 190 +Linked animations: [3008, 3009, 3010, 3011, 3012, 3013, 3014] +Linked NPCs: Rosie (3082), Sorcha (3083), Cait (3084), Cormac (3085), Fionn (3086), Donnacha (3087), Ronan (3088) + +Base: 191 +Linked animations: [3016, 3017, 3018, 3019] +Linked graphics: 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518 + +Base: 192 +Linked animations: [3020] +Linked graphics: 506 + +Base: 193 + +Base: 194 + +Base: 195 + +Base: 196 + +Base: 197 + +Base: 198 + +Base: 199 +Linked animations: [1317] + +Base: 200 +Linked animations: [2675, 2676, 2677, 2678, 2679, 2680, 2681, 2682, 2683, 2684] + +Base: 201 +Linked animations: [3559, 3560, 3561, 3562] + +Base: 202 +Linked animations: [3307] + +Base: 203 +Linked animations: [3308] + +Base: 204 +Linked animations: [1149, 1150, 1151, 1152, 1153, 1154, 1155] + +Base: 205 +Linked animations: [2138] + +Base: 206 +Linked animations: [1848] + +Base: 207 +Linked animations: [1855] + +Base: 208 +Linked animations: [1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, 1867] + +Base: 209 +Linked animations: [1254, 1255, 1256] + +Base: 210 +Linked animations: [3093] + +Base: 211 +Linked animations: [1376, 1377, 1378, 2114, 2115, 2116, 2117, 2665, 3410, 3411, 3412, 3413, 3414, 3541, 9723, 10710, 10895, 10978] + +Base: 212 +Linked animations: [1882, 1883, 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893] + +Base: 213 +Linked animations: [1884] + +Base: 214 +Linked animations: [2085] + +Base: 215 +Linked animations: [785, 786] + +Base: 216 +Linked animations: [3457, 3458] + +Base: 217 +Linked animations: [3454] + +Base: 218 +Linked animations: [3456] + +Base: 219 +Linked animations: [3455] + +Base: 220 +Linked animations: [3453] + +Base: 221 +Linked animations: [2008] + +Base: 222 +Linked animations: [2003, 2004, 2005, 2006, 2007] + +Base: 223 +Linked animations: [2001, 2002] + +Base: 224 +Linked animations: [3166] + +Base: 225 + +Base: 226 +Linked animations: [2765, 2766, 2767] + +Base: 227 + +Base: 228 +Linked animations: [11224] + +Base: 229 +Linked animations: [1372, 1373, 2344, 2345, 5764, 5765, 5766, 5767] + +Base: 230 + +Base: 231 +Linked animations: [3092] + +Base: 232 + +Base: 233 + +Base: 234 +Linked animations: [2221, 2222, 2223, 2224, 2225, 2226, 2227, 2228, 2229] + +Base: 235 +Linked animations: [2230] + +Base: 236 +Linked animations: [2231] + +Base: 237 +Linked animations: [2232] + +Base: 238 +Linked animations: [2233] + +Base: 239 +Linked animations: [2234] + +Base: 240 +Linked animations: [2235] + +Base: 241 +Linked animations: [2236] + +Base: 242 +Linked animations: [2237] + +Base: 243 +Linked animations: [2238] + +Base: 244 +Linked animations: [2217, 2218, 2219, 2220] + +Base: 245 +Linked animations: [1355, 1356] +Linked objects: null (4668) + +Base: 246 +Linked animations: [1357, 1358, 1359, 1360, 1361] + +Base: 247 + +Base: 248 +Linked animations: [949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006] + +Base: 249 +Linked animations: [934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948] + +Base: 250 +Linked animations: [630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 3476, 3477, 3545, 3546] + +Base: 251 +Linked animations: [1921, 9681] + +Base: 252 +Linked animations: [1489] + +Base: 253 +Linked animations: [1490] + +Base: 254 +Linked animations: [1491, 1492] + +Base: 255 + +Base: 256 +Linked animations: [2690] + +Base: 257 +Linked animations: [646] + +Base: 258 +Linked animations: [902] + +Base: 259 +Linked animations: [788] + +Base: 260 +Linked animations: [789] + +Base: 261 +Linked animations: [1024] +Linked graphics: 241 + +Base: 262 +Linked animations: [903] + +Base: 263 +Linked animations: [787] + +Base: 264 +Linked animations: [1127] +Linked objects: Strange shrine (3634) + +Base: 265 +Linked animations: [1138] +Linked objects: null (3637) + +Base: 266 +Linked animations: [1135, 1136] + +Base: 267 +Linked animations: [2379] +Linked objects: null (8983), null (16119) + +Base: 269 +Linked animations: [3048, 3049, 3050, 3052] +Linked NPCs: Balloon animal (3119), Balloon animal (3120), Balloon animal (3121), Balloon animal (3122) + +Base: 270 +Linked animations: [3051] +Linked graphics: 524 + +Base: 271 +Linked animations: [4] +Linked objects: null (10060) + +Base: 272 + +Base: 273 +Linked animations: [29, 30, 31, 32, 33, 34, 35, 36] + +Base: 274 +Linked animations: [3952, 3953, 3954, 3955, 3956, 3957, 3958, 3959, 3960, 3961, 3962, 6448, 6449] +Linked NPCs: Tortoise (3808), Tortoise (3819), Healthorg and tortoise (4643), Battle tortoise display (5981) + +Base: 275 +Linked animations: [37, 38, 39, 40, 41, 42, 43, 44] + +Base: 276 +Linked animations: [341, 342, 343, 344, 345, 5288] +Linked graphics: 72, 960 + +Base: 277 +Linked animations: [6] +Linked NPCs: Boulder (939), Boulder (986) + +Base: 278 +Linked animations: [362] +Linked NPCs: Butterfly (153), Butterfly (154), Butterfly (155), Butterfly (156), Butterfly (157), Butterfly (1280), Butterfly (4449), Butterfly (4450), Black warlock (5082), Snowy knight (5083), Sapphire glacialis (5084), Ruby harvest (5085) + +Base: 279 +Linked animations: [45, 46, 47, 48, 49, 50, 51, 52, 1898] + +Base: 280 +Linked animations: [314, 315, 316, 317, 318, 319, 320, 1823, 2134, 2159, 2160, 2161, 2662, 2663, 2664, 2694] + +Base: 281 +Linked animations: [53, 54, 55, 56, 57, 5826] + +Base: 282 +Linked animations: [58, 59, 60, 61, 62, 2162, 2303, 2312] + +Base: 283 + +Base: 284 +Linked animations: [16, 17, 18, 19, 20] + +Base: 285 +Linked animations: [73] + +Base: 286 +Linked animations: [1418, 2887] + +Base: 287 + +Base: 288 +Linked animations: [74, 75, 76, 77, 78] + +Base: 289 +Linked animations: [2268] +Linked NPCs: Sheepdog (2311) + +Base: 290 +Linked animations: [4972, 12231, 12246, 12247, 12248, 12249, 12250, 12251, 12252, 12253, 12254, 12255, 12256, 12257, 12259, 14244, 14245, 14253, 14254, 14255, 14256, 14257, 14258, 14259, 14260, 14266] +Linked NPCs: Red dragon (53), Black dragon (54), Blue dragon (55), Elvarg (742), Green dragon (941), Red dragon (4669), Red dragon (4670), Red dragon (4671), Red dragon (4672), Black dragon (4673), Black dragon (4674), Black dragon (4675), Black dragon (4676), Green dragon (4677), Green dragon (4678), Green dragon (4679), Green dragon (4680), Blue dragon (4681), Blue dragon (4682), Blue dragon (4683), Blue dragon (4684), Blue dragon (5178), Brutal green dragon (5362), Dragon display (5979), Elvarg (6087), Mr. Mordaut (6117), Dragon (8858), Dragon (8859), Dragon (8860), Dragon (8861) + +Base: 291 +Linked animations: [14281] +Linked objects: Mysterious entrance (52849), Mysterious entrance (52850), Mysterious entrance (52851), Mysterious entrance (52852), Mysterious entrance (52853), Mysterious entrance (52854), Mysterious entrance (52855), Mysterious entrance (52856), Mysterious entrance (52857), Mysterious entrance (52858), Mysterious entrance (52859), Mysterious entrance (52860), Mysterious entrance (52861), Mysterious entrance (52862), Mysterious entrance (52863), Mysterious door (52864), Mysterious door (52865), Mysterious door (52866), Mysterious door (52867), Mysterious door (52868), Mysterious door (52869), Mysterious door (52870), Mysterious door (52871), Mysterious door (52872), Mysterious door (52873), Mysterious door (52874), Mysterious door (52875), Mysterious door (52876) + +Base: 292 +Linked animations: [14282, 14283, 14284, 14285, 14286] +Linked graphics: 833, 834, 2747, 2748, 2749 + +Base: 293 +Linked animations: [14305, 14306, 14307] +Linked graphics: 90, 91, 92 + +Base: 294 +Linked animations: [14304] +Linked graphics: 127, 128, 129 + +Base: 295 +Linked animations: [93, 94, 95, 96, 97] +Linked NPCs: Dryad (56), Tree spirit (4470) + +Base: 296 +Linked animations: [6917] +Linked objects: Alan (4419), Alan (25955) + +Base: 297 +Linked animations: [98, 99, 100, 101, 102, 104, 105, 900, 928, 1253, 1853, 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2315, 2335, 2336, 2337, 2341, 2342, 2942, 3072, 3207, 3208, 3209, 3376, 3377, 3378, 3379, 3398, 4021, 5809, 5810, 6160, 6170, 6205, 6206, 6207, 6208, 7370, 8706, 9138, 10464, 10465, 10469, 11736, 11737, 11738, 12118, 12119, 12121, 12128, 12129, 12131, 12132, 12133, 12134, 12139, 12140, 12141, 12142, 12143, 12144, 12145, 12173, 12177, 12179, 12180, 12181, 12184, 12185, 12186, 12211, 12212, 12300, 12421, 12435] +Linked NPCs: Dwarf (118), Chaos dwarf (119), Dwarf (120), Dwarf (121), Guard (206), Lollk (207), Captain Lawgof (208), Nulodion (209), Austri (232), Doric (284), Dwarf (382), Stankers (383), Drogo dwarf (579), Dwarf (582), Nurmof (594), Thurgo (604), Boot (665), Drunken Dwarf (956), Niloof (994), Klank (995), Kamen (996), Hammerspike Stoutbeard (1794), Dwarf gang member (1795), Dwarf gang member (1796), Dwarf gang member (1797), null (1836), Dondakan the Dwarf (1839), Dwarven Engineer (1840), Rolad (1841), Khorvak, a dwarven engineer (1842), Dwarven Ferryman (1843), Dwarven Ferryman (1844), null (1845), Dwarven Boatman (1846), Miodvetnir (1847), Dernu (1848), Derni (1849), Purple Pewter Secretary (2092), Yellow Fortune Secretary (2093), Blue Opal Secretary (2094), Green Gemstone Secretary (2095), White Chisel Secretary (2096), Silver Cog Secretary (2097), Brown Engine Secretary (2098), Red Axe Secretary (2099), Purple Pewter Director (2100), Blue Opal Director (2101), Yellow Fortune Director (2102), Green Gemstone Director (2103), White Chisel Director (2104), Silver Cog Director (2105), Brown Engine Director (2106), Red Axe Director (2107), Trader (2109), Trader (2110), Trader (2111), Trader (2112), Trader (2113), Trader (2114), Trader (2115), Trader (2116), Trader (2117), Trader (2118), Trader (2119), Trader (2120), Trader (2121), Trader (2122), null (2123), Trader (2124), null (2125), Trader (2126), Trade Referee (2127), Supreme Commander (2128), Black Guard (2130), Black Guard (2131), Black Guard (2132), Black Guard (2133), Black Guard Berserker (2134), Black Guard Berserker (2135), Black Guard Berserker (2136), Blasidar the sculptor (2141), null (2142), Riki the sculptor's model (2143), Riki the sculptor's model (2144), Riki the sculptor's model (2145), Riki the sculptor's model (2146), Riki the sculptor's model (2147), Riki the sculptor's model (2148), Riki the sculptor's model (2149), Riki the sculptor's model (2150), Vigr (2151), Santiri (2152), Saro (2153), Gunslik (2154), Wemund (2155), Randivor (2156), Hervi (2157), Nolar (2158), Gulldamar (2159), Tati (2160), Agmundi (2161), Vermundi (2162), Banker (2163), Banker (2164), Librarian (2165), Assistant (2166), Customer (2167), Dromund (2169), Rind the gardener (2170), Factory Manager (2171), Factory Worker (2172), Factory Worker (2173), Factory Worker (2174), Factory Worker (2175), Inn Keeper (2176), Inn Keeper (2177), Barmaid (2178), Barman (2179), Cart conductor (2180), Cart conductor (2181), Cart conductor (2182), Cart conductor (2183), Cart conductor (2184), Cart conductor (2185), Cart conductor (2186), Rowdy dwarf (2187), Hegir (2188), Haera (2189), Runvastr (2190), Sune (2191), Bentamir (2192), Ulifed (2193), Reinald (2194), Karl (2195), Gauss (2196), Myndill (2197), Kjut (2198), Tombar (2199), Odmar (2200), Audmann (2201), null (2202), Drunken Dwarf (2203), Drunken Dwarf (2204), Dwarven Boatman (2205), Dwarven Boatman (2206), Dwarven Miner (2207), Dwarven Miner (2208), Dwarven Miner (2209), Dwarven Miner (2210), Dwarven Miner (2211), Dwarven Miner (2212), Dwarven Miner (2213), Dwarven Miner (2214), Dwarven Miner (2215), Dwarven Miner (2216), Dwarven Miner (2217), Dwarven Miner (2218), Purple Pewter Director (2219), Purple Pewter Director (2220), Blue Opal Director (2221), Yellow Fortune Director (2222), Green Gemstone Director (2223), White Chisel Director (2224), Silver Cog Director (2225), Brown Engine Director (2226), Red Axe Director (2227), Commander Veldaban (2228), null (2231), Black Guard berserker (2232), Blandebir (2321), null (2403), null (2404), Cart conductor (2409), Red Axe Director (2410), Red Axe Director (2411), Red Axe Henchman (2412), Red Axe Henchman (2413), Red Axe Henchman (2414), Colonel Grimsson (2415), Colonel Grimsson (2416), Chaos dwarf (2423), Gunslik (2424), Nolar (2425), Factory Worker (2426), Cart conductor (2427), Gauss (2428), Drunken Dwarf (2429), Rowdy dwarf (2430), Ulifed (2431), Red Axe Henchman (2432), Red Axe Henchman (2433), Dwarven Miner (2550), Dwarven Miner (2551), Dwarven Miner (2552), Blast Furnace Foreman (2553), Ordan (2564), Jorzik (2565), null (2788), Thorgel (2789), Smokin' Joe (2952), Rauborn (2991), Vaeringk (2992), Oxi (2993), Fior (2994), Sagira (2995), Anleif (2996), Mystery figure (3052), Dwarf (3219), Dwarf (3220), Dwarf (3221), Dwarf (3268), Dwarf (3269), Dwarf (3270), Dwarf (3271), Dwarf (3272), Dwarf (3273), Dwarf (3274), Dwarf (3275), Black Guard (3276), Black Guard (3277), Black Guard (3278), Black Guard (3279), Engineering assistant (3280), Engineering assistant (3281), Engineer (3282), Dwarf (3294), Dwarf (3295), Mountain Dwarf (3390), null (3401), null (3404), Vestri (3679), Drunken Dwarf (3837), Alviss (3933), Fullangr (3934), Jari (3935), Thorodin (3936), Ferd (3937), Donal (3938), Stonemason (4248), Guard (4257), Guard (4258), Guard (4259), Guard (4260), Gamfred (4287), null (4315), Dwarf (4316), Hirko (4558), Holoy (4559), Hura (4563), Brendt (5501), Grundt (5502), Sin Seer (5571), null (5832), Builder (5864), Builder (5865), Builder (5866), Builder (5867), Dwarf (5880), Dwarf (5881), Dwarf (5882), Dwarf (5883), Dwarf (5884), Dwarf (5885), Ticket dwarf (5886), null (5887), null (5998), null (6000), Hofuthand (armour and weapons) (6527), Thaki the delivery dwarf (7115), Brother Bordiss (7718), Drorkar (7720), Nurmof (7721), Lakki the delivery dwarf (7722), Drorkar (7723), Brother Bordiss (7724), Assistant (7726), null (7728), null (7729), null (7730), Ramarno (8170), Mystic (8233), Mystic (8238), null (8680), null (8681), null (8682), null (8683), Glosur (8684), Baden (8685), Obert (8686), Medwin (8687), Lieutenant Brae (8749), Lieutenant Brae (8751), Black Guard (8752), Commander Veldaban (8754), Commander Veldaban (8757), Commander Veldaban (8758), Commander Veldaban (8759), Commander Veldaban (8761), Commander Veldaban (8762), Commander Veldaban (8763), Commander Veldaban (8767), Black Guard (8768), Black Guard (8769), Black Guard (8772), Black Guard (8773), Black Guard (8774), Chaos dwarf hand cannoneer (8776), Chaos dwarf hand cannoneer (8777), Chaos dwarf (8778), Chaos dwarf (8779), Black Guard crossbowdwarf (8781), Black Guard crossbowdwarf (8782), Black Guard berserker (8783), Black Guard berserker (8784), Thak (8786), Chaos dwarf (8788), Brunolt (8789), Chaos dwarf (8791), Chaos dwarf (8792), Chaos dwarf (8793), Red Axe Director (8794), Colonel Grimsson (8797), Chaos dwarf hand cannoneer (8799), Chaos dwarf hand cannoneer (8800), Fjoila (8801), Prisoner (8802), Prisoner (8803), Prisoner (8804), Prisoner (8807), Prisoner (8808), Prisoner (8809), Prisoner (8810), Prisoner (8811), Commander Veldaban (8812), Trade floor guard (8813), Colonel Grenda (8814), Colonel Grenda (8818), Supreme Commander (8819), Supreme Commander (8820), Commander Veldaban (8821), Orange Flame director (8822), Orange Flame secretary (8823), Trader (8824), Trader (8827), null (8896), Dwarf (8897), Urist Loric (11270) +Linked objects: null (23492), Commander Veldaban (45061), null (45062), null (45063), Farli (45091), null (46486), Thorgel (46487) + +Base: 298 +Linked animations: [332, 333] +Linked NPCs: Spirit tree (3637) +Linked objects: Spirit tree (1293), Spirit tree (1294), Spirit tree (1295) + +Base: 299 +Linked animations: [106, 107, 108, 109, 110, 111, 112, 113, 3248, 3249] +Linked NPCs: null (4453), null (4454), null (11378) + +Base: 300 + +Base: 301 +Linked animations: [119, 120, 121, 122, 123, 124, 125, 126, 1434, 1435, 1436, 1957] +Linked NPCs: null (1970), Azzanadra (1971) + +Base: 302 +Linked animations: [127, 128, 129, 130, 131, 132, 133, 134] +Linked NPCs: Black Knight Titan (221) + +Base: 303 +Linked animations: [135, 136] +Linked graphics: 191 + +Base: 304 + +Base: 305 + +Base: 306 +Linked animations: [142, 143, 144, 145, 146, 147, 148, 149, 2391, 2809] +Linked NPCs: Spidine (5594) + +Base: 307 +Linked animations: [150, 151] +Linked graphics: 190 + +Base: 308 +Linked animations: [308, 309, 310, 311, 312, 313, 1737, 1738, 3403, 4578, 6984, 6985] + +Base: 309 +Linked animations: [2167, 2168, 2169, 2170] +Linked NPCs: Bed (2254), Thing under the bed (2255) + +Base: 310 +Linked animations: [152, 153, 154, 155, 156, 5347, 5348] +Linked NPCs: Golem (85), Rock golem (8648) +Linked graphics: 968 + +Base: 311 +Linked animations: [157, 158, 159, 160, 161] + +Base: 312 +Linked animations: [162, 166, 2959, 2961] + +Base: 313 +Linked animations: [3015] + +Base: 314 + +Base: 315 +Linked animations: [1197, 1198, 1199, 1200, 1201, 1202] +Linked NPCs: The Shaikahan (1172), The Shaikahan (1173) + +Base: 316 +Linked animations: [176, 177, 178, 179, 180] +Linked NPCs: Lizard man (69) + +Base: 317 +Linked animations: [181, 182, 183, 184, 185, 186, 187, 188, 1445, 1446, 1448, 1449] +Linked NPCs: Magic axe (127), Iron pickaxe (1015), Possessed pickaxe (1536), Animated pickaxe (10168), Animated pickaxe (10169), Animated pickaxe (10170), Animated pickaxe (10171), Animated pickaxe (10172), Animated pickaxe (10173), Animated pickaxe (10174), Animated pickaxe (10175), Animated pickaxe (10176), Animated pickaxe (10177) +Linked graphics: 307, 309 + +Base: 318 +Linked animations: [303, 304, 305, 306, 307] +Linked NPCs: Mammoth (135) + +Base: 319 +Linked animations: [189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 1412, 1413, 1414, 1415, 2691, 2841, 2842, 2904, 2943, 3193, 3963, 3964, 3965, 3966, 3967, 3968, 3969, 4331, 4332, 4536, 4537, 4538, 4579, 4580, 4582, 4583, 4585, 5002, 5496, 6109, 6471, 6472, 6473, 6474, 6475, 6484, 6485, 6533, 6534, 6535, 6666, 7121, 7122, 7123, 7124, 7125, 7126, 7127, 7128, 7142, 7143, 7147, 8944, 9139, 9140, 10951, 10952] +Linked NPCs: Leprechaun (65), Gnome (66), Gnome (67), Gnome (68), Gnome child (159), Gnome child (160), Gnome child (161), Gnome trainer (162), Gnome guard (163), Gnome guard (164), Gnome shop keeper (165), Gnome banker (166), Gnome baller (167), Gnome woman (168), Gnome woman (169), Brimstail (171), Golrie (306), King Bolren (469), Commander Montai (470), Bolkoy (471), Remsai (472), null (473), null (474), Gnome troop (479), Gnome troop (480), Tracker gnome 1 (481), Tracker gnome 2 (482), Tracker gnome 3 (483), Local Gnome (484), Local Gnome (485), Kalron (486), Hudo (600), Rometti (601), Gulluck (602), Heckel Funch (603), Gnome baller (621), Gnome baller (622), Gnome baller (623), Gnome baller (624), Gnome baller (625), Gnome baller (626), Gnome baller (627), Gnome baller (628), Gnome baller (629), Gnome baller (630), Gnome baller (631), Gnome baller (632), Gnome winger (633), Gnome winger (634), Gnome ball referee (635), Cheerleader (636), King Narnode Shareen (670), Glough (671), Anita (672), Femi (676), Glophren (767), Farquie the Cleaner (790), Blurberry (848), Barman (849), Aluft Gianne snr. (850), Gnome Waiter (851), Battle mage (914), Prissy Scilla (1037), Daero (1407), Waydar (1408), Waydar (1409), Garkor (1411), Garkor (1412), Lumo (1413), Lumo (1414), Bunkdo (1415), Bunkdo (1416), Carado (1417), Carado (1418), Lumdo (1419), Karam (1420), Karam (1421), Karam (1422), Bunkwicket (1423), Waymottin (1424), Zooknock (1425), Zooknock (1426), G.L.O. Caranock (1427), G.L.O. Caranock (1428), Golrie (1682), Lumdo (1788), Bunkwicket (1789), Waymottin (1790), Gnormadium Avlafrim (1800), Gnome emissary (2137), Gnome traveller (2138), Gnome traveller (2139), Gnome troop (2247), Gnome troop (2248), Gnome (2249), Gnome (2250), Gnome (2251), Bolongo (2343), null (2370), Gnome (2371), null (2406), null (2407), null (2408), Gnome emissary (2420), Gnome companion (2421), Gnome companion (2422), Gnome (2649), Gnome (2650), Gnome guard (2781), Gnome Coach (2802), Child (2872), Child (2873), Child (2874), Child (2875), Child (2876), Child (2877), Mystery figure (3053), Captain Dalbur (3809), Captain Bleemadge (3810), Captain Errdo (3811), Captain Klemfoodle (3812), Gnome Archer (3814), Gnome Driver (3815), Gnome Mage (3816), Lieutenant Schepbur (3817), Trainer Nacklepen (3818), Frizzy Skernip (4560), Yulf Squecks (4561), Praistan Ebola (4562), Gianne jnr. (4572), Timble (4573), Tamble (4574), Spang (4575), Brambickle (4576), Wingstone (4577), Penwie (4578), Generic Diplomat (4579), Ambassador Gimblewap (4580), Ambassador Spanfipple (4581), Ambassador Ferrnook (4582), Professor Manglethorp (4583), Damwin (4584), Professor Onglewip (4585), Professor Imblewyn (4586), Perrdur (4587), Dalila (4588), Sorrn (4589), Mimm (4590), Eebel (4591), Ermin (4592), Portobello (4593), Captain Ninto (4594), Captain Daerkin (4595), Captain Lamdoo (4596), Meegle (4597), Wurbel (4598), Sarble (4599), Guard Vemmeldo (4600), Burkor (4601), Froono (4602), null (4609), Brimstail (4610), Saboteur (4611), Gnome shop keeper (4612), Gnome soldier (4638), Gnome soldier (4639), Gnome soldier (4640), Gnome soldier (4641), Gnome soldier (4642), Oaknock the Engineer (4644), Glouphrie the Untrusted (4645), King Healthorg (4646), Hazelmere (4647), Jack (5046), Jill (5047), Jeff (5048), Assistant Le Smith (5056), null (5064), Elkoy (5179), Elkoy (5182), Captain Errdo (5249), Gnome (6002), Winkin (6003), Gnome (6004), Elnock Inquisitor (6070), Yewnock the engineer (6303), Bolrie (6304), Hazelmere (6305), Bolrie (6306), Advisor (6307), King Argenthorg (6308), Prince Argenthorg (6309), Longramble (6318), Glough (6391), Professor Arblenap (7717), Professor Arblenap (7725), null (7731), null (8276), null (8460), Glophren (8724), Gnome technician (8787) + +Base: 320 +Linked animations: [219, 220, 221, 222, 223, 1419, 12321, 13937] +Linked NPCs: Monkey (132), Monkey (1463), Monkey (1464), Monkey (1487), Monkey (2301), null (2302), null (2303), Monkey (4344), Monkey (4363), Monkey (5852) + +Base: 321 +Linked animations: [357, 358, 359, 360, 361, 1025, 2104, 3382, 3383, 3436, 3437, 5357] +Linked NPCs: null (3462), Skrach Uglogwee (3463), Skrach Uglogwee (3464) + +Base: 322 +Linked animations: [229, 230, 231, 232, 233] +Linked NPCs: Orc (71) + +Base: 323 +Linked animations: [2666, 2667, 2668, 2669, 2670] +Linked NPCs: Odysseus (2640), Odysseus (2651) + +Base: 324 +Linked animations: [234, 235, 236, 237, 238] + +Base: 325 +Linked animations: [3210, 3211, 3212] +Linked NPCs: Squirrel (3283), Squirrel (3284), Squirrel (3285), Fluffy (7413), Beny (7414), Bubbles (7415), Big Ben (7416), Fluffy (9682), Fluffy (9692), Beny (9693), Bubbles (9694), Big Ben (9695) + +Base: 326 +Linked animations: [2703, 2704, 2705, 2706, 2707, 2894] +Linked NPCs: Rat (47), Rat (2682), null (2974), null (2975), null (2976), null (2977), null (2978), null (2979), Rat (2981), King rat (2982), Rat (3008), Rat (3009), Rat (3010), Rat (3011), Rat (3012), Rat (3013), Rat (3014), Rat (3015), Rat (3016), Rat (3017), Rat (3018), Hell-Rat (3382), Rat (4396), Rat (4415) + +Base: 327 +Linked animations: [2369, 2370, 2371, 2372, 2373] +Linked NPCs: Pheasant (2459), Pheasant (2460), Pheasant (2461), Pheasant (2462) + +Base: 328 +Linked animations: [3947, 3948, 3949] +Linked NPCs: Postie Pete (3805) + +Base: 329 +Linked animations: [1242, 1243, 1244, 1245, 1246, 3867, 3868, 3869, 3870, 3871, 3872] +Linked NPCs: Bunny (1320), Bunny (1321), Bunny (7198), Bunny (7199), Guard bunny (7200), Rabbit (7208) + +Base: 330 +Linked animations: [3213, 3214, 3215, 3216] + +Base: 331 +Linked animations: [239, 240, 241, 242, 243] +Linked NPCs: Mouse (901), Crypt rat (2032) + +Base: 332 +Linked animations: [2297, 2298, 2299, 2300, 2301, 2302] +Linked NPCs: Chicken (288), Rooster (2312), null (3290), Evil Chicken (3375), Newtroost (5597), Evil Chicken (8644) + +Base: 333 +Linked animations: [244, 245, 246, 247, 248] + +Base: 334 +Linked animations: [932, 933] +Linked NPCs: Sea slug (1006), Sea slug (4894) + +Base: 335 +Linked animations: [10, 11, 12] +Linked NPCs: Shark (40), Shark (467), Shark (468), Shark (5067), Shark (5068), Shark (5069), Shark (8625), Shark (8626), Shark (8627), Shark (8628) + +Base: 336 +Linked animations: [249, 250, 251, 252, 253, 1736, 3841, 3842, 4936] + +Base: 337 +Linked animations: [254, 255, 256, 257, 258, 5356] +Linked NPCs: Skavid (129), Scared skavid (863), Mad skavid (864), Skavid (865), Skavid (866), Skavid (867), Skavid (868), Skavid (869), Witch's experiment (897) + +Base: 338 +Linked animations: [9487, 9488, 9489, 9490, 9491, 9492] +Linked NPCs: Nechryael (1613), Nechryael (10702) + +Base: 339 +Linked animations: [269, 270, 271, 272, 273] + +Base: 340 +Linked animations: [274, 275, 276, 277, 278, 1899, 1900, 1901, 2392] +Linked NPCs: Snake (128), Snake (1479), Desert snake (1874), Bush snake (2489), Bush snake (2490), Snake (4343) + +Base: 341 +Linked animations: [336, 337, 338, 339, 340] +Linked NPCs: Soulless (140) + +Base: 342 +Linked animations: [279, 280, 281, 282] + +Base: 343 +Linked animations: [3241, 3242] +Linked NPCs: Swan (3296), Black swan (3297) + +Base: 344 +Linked animations: [0, 1, 2] +Linked NPCs: Swarm (411), Swarm (4364), null (4966), Bees! (8650) + +Base: 345 + +Base: 346 + +Base: 347 +Linked animations: [3568, 3569, 3570] +Linked NPCs: Sheep (3579) + +Base: 348 + +Base: 349 + +Base: 350 + +Base: 351 + +Base: 352 + +Base: 353 + +Base: 354 + +Base: 355 + +Base: 356 + +Base: 357 + +Base: 358 + +Base: 359 + +Base: 360 + +Base: 361 + +Base: 362 + +Base: 363 + +Base: 364 + +Base: 365 + +Base: 366 + +Base: 367 + +Base: 368 + +Base: 369 + +Base: 370 + +Base: 371 + +Base: 372 + +Base: 373 + +Base: 374 + +Base: 375 +Linked animations: [348, 349, 350] +Linked NPCs: Strange plant (8640) + +Base: 376 +Linked animations: [288, 289, 290, 291, 292, 3232, 3233, 3234, 3235, 3236] + +Base: 377 +Linked animations: [293, 294, 295, 296, 297] +Linked NPCs: Yeti (130) + +Base: 378 +Linked animations: [9506, 9507, 9508, 9509, 9510, 9511] +Linked NPCs: Rockslug (1631), Rockslug (1632) + +Base: 379 +Linked animations: [1016, 1017] +Linked objects: Spit roast (5608), null (6895) + +Base: 380 +Linked animations: [1018, 1019, 1020, 1021, 1022] +Linked NPCs: Swamp toad (1013), Bloated Toad (1014), Plague frog (1997) + +Base: 381 +Linked animations: [1023, 2329] +Linked graphics: 240, 417, 419, 421, 423, 425 + +Base: 382 +Linked animations: [1011, 3463, 3464, 3465, 3466] + +Base: 383 +Linked animations: [3610] +Linked graphics: 614 + +Base: 384 +Linked animations: [3575] + +Base: 385 +Linked animations: [3574] + +Base: 386 +Linked animations: [3706] +Linked graphics: 624 +Linked objects: Altar (44926), Altar (44927), Altar (44928), Altar (44929), Altar (44930), Altar (44931), Altar (44932), Altar (44933), Altar (44934), Altar (44935), Altar (44936), Altar (44937), Altar (44938), Altar (44939), Altar (44940), Altar (44941), Altar (44942), Altar (44943), Altar (44944), Altar (44945), Altar (44946) + +Base: 387 +Linked animations: [3613, 3614] + +Base: 388 +Linked animations: [3580, 3581] +Linked objects: Elemental sphere (13660), Crystal of power (13661) + +Base: 389 +Linked animations: [3607] +Linked graphics: 613 + +Base: 390 +Linked animations: [3681] + +Base: 391 +Linked animations: [3582, 3583, 3584, 3585] +Linked NPCs: Elemental balance (4021), Elemental balance (4022), Elemental balance (4023), Elemental balance (4024), Elemental balance (4025), Elemental balance (4026), Elemental balance (4027), Elemental balance (4028), Elemental balance (4029), Elemental balance (4030), Elemental balance (4031), Elemental balance (4032), Elemental balance (4033), Elemental balance (4034), Elemental balance (4035), Elemental balance (4036), Elemental balance (4037), Elemental balance (4038), Elemental balance (4039), Elemental balance (4040), Elemental balance (4041), Elemental balance (4042), Elemental balance (4043), Elemental balance (4044), Elemental balance (4045), Elemental balance (4046), Elemental balance (4047), Elemental balance (4048), Elemental balance (4049), Elemental balance (4050), Elemental balance (4051), Elemental balance (4052), Elemental balance (4053), Elemental balance (4054), Elemental balance (4055), Elemental balance (4056), Elemental balance (4057), Elemental balance (4058), Elemental balance (4059), Elemental balance (4060), Elemental balance (4061), Elemental balance (4062), Elemental balance (4063), Elemental balance (4064), Elemental balance (4065), Elemental balance (4066), Elemental balance (4067), Elemental balance (4068), Elemental balance (4069), Elemental balance (4070), Elemental balance (4071), Elemental balance (4072), Elemental balance (4073), Elemental balance (4074), Elemental balance (4075), Elemental balance (4076), Elemental balance (4077), Elemental balance (4078), Elemental balance (4079), Elemental balance (4080), Elemental balance (4081), Elemental balance (4082), Elemental balance (4083), Elemental balance (4084), Elemental balance (4085), Elemental balance (4086), Elemental balance (4087), Elemental balance (4088), Elemental balance (4089), Elemental balance (4090), Elemental balance (4091), Elemental balance (4092), Elemental balance (4093), Elemental balance (4094), Elemental balance (4095) +Linked objects: Elemental balance (13395), Elemental balance (13396), Elemental balance (13397) + +Base: 392 +Linked animations: [3105, 3107, 9586, 9983] +Linked objects: null (2148), null (2669), null (3478), null (4033), null (11011), null (11012), null (11013), null (11014), null (11015), null (11016), null (11816), null (12300), null (12934), null (13052), null (13053), null (13054), Torch (13205), Torch (13207), Fire (13337), Torch (13341), Candle (13342), Skull (13343), Firepit (13528), Firepit with hook (13529), Firepit with hook (13530), Firepit with pot (13531), Firepit with pot (13532), Small oven (13533), Small Oven (13534), Small oven (13535), Large oven (13536), Large oven (13537), Large oven (13538), Clay fireplace (13610), null (14004), Pot boiler (14006), Pot boiler (14007), Pot boiler (14008), Pot boiler (14009), null (14996), null (15545), null (15689), null (18307), Fire (23046), null (23380), campfire (25374), Goblin stove (25440), Goblin stove (25441), null (25442), Candles (30752), null (31392), null (31393), null (31395), null (31704), null (33455), null (33795), null (33796), null (34378), null (34425), null (35124), null (35799), null (36810), null (38265), null (38268), null (41173), null (41423), Skull (41647), null (42360), null (44060), Candles (45271), null (45815), null (46256), null (48848) + +Base: 393 +Linked animations: [3579] +Linked objects: Large orrery (13655), Orrery (25401) + +Base: 394 +Linked animations: [3578] +Linked objects: Small orrery (13654) + +Base: 395 +Linked animations: [3511, 4610, 12507] +Linked objects: Grandfather clock (12293), Clock (13169), Clock (13170), Clock (13171), Clock (15547), Clock (22155), Clock (25763), Death clock (27896), Grandfather clock (34544), Grandfather clock (34545), Grandfather clock (36937), Clock (40029), Grandfather clock (45295), Death clock (46774), Death clock (46775), Death clock (46776), null (47652), Spooky clock (47655), Grandfather clock (47900), Grandfather clock (49059), Clock (52809) + +Base: 396 +Linked animations: [3601] + +Base: 397 +Linked animations: [3573] +Linked objects: Incense burner (13209), Incense burner (13211), Incense burner (13213), Incense burner (52824) + +Base: 398 +Linked animations: [3587, 3588, 3589, 3590, 3591] +Linked graphics: 610, 611 +Linked objects: Jacky Jester (13390), Jacky Jester (13726) + +Base: 399 +Linked animations: [3612] + +Base: 400 +Linked animations: [3616] +Linked objects: Greater magic circle (13683) + +Base: 401 +Linked animations: [3615] +Linked objects: Magic circle (13682) + +Base: 402 +Linked animations: [3586] +Linked objects: Magic barrier (13145) + +Base: 403 +Linked animations: [3629] +Linked graphics: 616 + +Base: 404 +Linked animations: [3637, 3638] +Linked graphics: 620, 621 + +Base: 405 +Linked animations: [3577] +Linked objects: Bells (13215) + +Base: 406 +Linked animations: [3631, 3632, 3633] +Linked graphics: 617, 618, 619 + +Base: 407 +Linked animations: [3650] + +Base: 408 +Linked animations: [3174, 3642, 14289] +Linked objects: Interdimensional rift (11354), null (11355), Portal Home (11356), null (11359), Portal (12260), Portal (13405), Varrock Portal (13615), Lumbridge Portal (13616), Falador Portal (13617), Camelot Portal (13618), Ardougne Portal (13619), Yanille Portal (13620), Kharyrll Portal (13621), Varrock Portal (13622), Lumbridge Portal (13623), Falador Portal (13624), Camelot Portal (13625), Ardougne Portal (13626), Yanille Portal (13627), Kharyrll Portal (13628), Varrock Portal (13629), Lumbridge Portal (13630), Falador Portal (13631), Camelot Portal (13632), Ardougne Portal (13633), Yanille Portal (13634), Kharyrll Portal (13635), Portal (15477), Portal (15478), Portal (15479), Portal (15480), Portal (15481), Portal (15482), Portal (15996), Dark portal (27254), Portal (28139), Portal (28213), Portal (28214), Portal (29534), Portal (38694), Portal (38695), Portal (38696), Free-for-all (safe) (38698), Free-for-all (dangerous) (38699), Portal (38700), Portal (42021), Portal (42022), Soul Wars portal (42219), Edgeville portal (42220), Portal (42278), Portal (42279), Dark portal (46931), Portal (46935) + +Base: 409 +Linked animations: [3647] +Linked objects: Statue (13408), Ornamental fountain (13480), Ornamental fountain (52800) + +Base: 410 +Linked animations: [3644] +Linked objects: Scrying pool (13639) + +Base: 411 +Linked animations: [3628] +Linked graphics: 615 + +Base: 412 +Linked animations: [3604] +Linked graphics: 612 + +Base: 413 +Linked animations: [3682] +Linked graphics: 623 + +Base: 414 +Linked animations: [3646] +Linked objects: Teleportation focus (13640) + +Base: 415 +Linked animations: [3648] +Linked objects: Greater teleport focus (13641) + +Base: 416 +Linked animations: [3617, 3618, 3619, 3620, 3729] +Linked NPCs: Tentacle (3580), Tentacle (3618), Tentacle (3621) + +Base: 417 +Linked animations: [3117, 4118] +Linked objects: Magic chest (13291) + +Base: 418 +Linked animations: [3118, 3119, 3120] +Linked objects: Magic chest (13292) + +Base: 419 +Linked animations: [3121, 3122] + +Base: 420 +Linked animations: [3720] +Linked objects: Pump and drain (13560), Pump and tub (13562), Sink (13564) + +Base: 421 +Linked animations: [2997, 2998] +Linked objects: Doric's Whetstone (10641) + +Base: 422 +Linked animations: [3576] + +Base: 423 +Linked animations: [3707, 3708, 3709, 3710, 3711] +Linked NPCs: Rocnar (3594) +Linked objects: Rocnar (13373) + +Base: 424 +Linked animations: [3716, 3717] +Linked NPCs: Toy Doll (3596) + +Base: 425 +Linked animations: [3718] +Linked NPCs: Toy Mouse (3597) + +Base: 426 +Linked animations: [3712, 3713, 3714, 3715] +Linked NPCs: Toy Soldier (3595) + +Base: 427 +Linked animations: [3698] +Linked objects: Power saw (15468) + +Base: 428 +Linked animations: [4072] +Linked graphics: 678 + +Base: 429 +Linked animations: [3699] +Linked objects: Stone crusher (15466) + +Base: 430 +Linked animations: [3700] +Linked objects: Embalming tube (15469), null (15470) + +Base: 431 +Linked animations: [366, 1075, 7624, 7642, 10263, 10264, 10942, 10953] +Linked graphics: 18, 19, 20, 21, 22, 23, 24, 25, 26, 243, 250, 256, 325, 403, 472, 806, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1116, 1293, 1294, 1801, 1802, 1872, 1873, 1874, 1875, 1876, 1904, 1918, 2466, 2468, 2470, 2472, 2474, 2476, 2478, 2480, 2482, 2484, 2486, 2488, 2490, 2492, 2494, 2496, 2498, 2500, 2502, 2504, 2506, 2508 + +Base: 432 + +Base: 433 +Linked animations: [367, 6074, 7204] +Linked graphics: 28, 699, 1036, 1233 + +Base: 434 + +Base: 435 + +Base: 436 +Linked animations: [922] +Linked graphics: 33 + +Base: 437 +Linked animations: [373] +Linked graphics: 54, 438 + +Base: 438 +Linked animations: [368] +Linked graphics: 206, 207, 208, 209, 210, 211, 1855 + +Base: 439 + +Base: 440 +Linked animations: [371] +Linked graphics: 42, 43, 44, 45, 46, 47, 48, 290, 338, 1856 + +Base: 441 +Linked animations: [1069] +Linked graphics: 257 + +Base: 442 +Linked animations: [372, 1070] +Linked graphics: 35, 36, 37, 38, 39, 40, 41, 258 + +Base: 443 +Linked animations: [369] +Linked graphics: 219, 220, 221, 222, 223, 224, 225 + +Base: 444 +Linked animations: [374] +Linked graphics: 30, 50, 52, 193 + +Base: 445 +Linked animations: [375] +Linked graphics: 29, 49, 51, 55, 192 + +Base: 446 +Linked animations: [1657] +Linked objects: Ectofuntus (5282), Ectofuntus (5283) + +Base: 447 + +Base: 448 + +Base: 449 +Linked animations: [1644, 1645] +Linked NPCs: null (1689) + +Base: 450 +Linked animations: [1639, 1640] +Linked NPCs: Ghost (?) (1707), Ghost (?) (1708) + +Base: 451 +Linked animations: [1646, 1647, 4833] +Linked NPCs: null (1690) +Linked graphics: 793, 794 + +Base: 452 + +Base: 453 +Linked animations: [3558] +Linked objects: Bucket (12785) + +Base: 454 +Linked animations: [3106] +Linked objects: null (3490), null (5060), null (11008), Furnace (11010), null (12805), Furnace (12809), Furnace (30510) + +Base: 455 +Linked animations: [3104] +Linked objects: null (11158), null (12810), null (12811), null (30519) + +Base: 456 +Linked animations: [3567] +Linked graphics: 609 + +Base: 457 +Linked animations: [3548] +Linked graphics: 604 + +Base: 458 +Linked animations: [2892, 3549, 6604] +Linked graphics: 482, 605, 606, 1121 + +Base: 459 +Linked animations: [2893, 3554] +Linked NPCs: Melina (2934), Droalak (2937) +Linked graphics: 607 + +Base: 460 +Linked animations: [3556] +Linked graphics: 608 + +Base: 461 +Linked animations: [283, 284, 285, 286, 287, 925, 926, 927, 1139, 1140, 1141, 1142, 1158, 1159, 1259, 1474, 1475, 1476, 1477, 1478, 1480, 1488, 4007, 4008, 4009, 4010, 4983, 4985, 4986, 4987, 4988, 4989, 4990, 4991, 4992, 4993, 4994, 4995, 4996, 4997, 4998, 4999, 5000, 5001, 5016, 5775, 5779, 10236, 10243] +Linked NPCs: Troll (72), Rock (1095), Stick (1096), Pee Hat (1097), Kraka (1098), Dung (1099), Ash (1100), Thrower Troll (1101), Thrower Troll (1102), Thrower Troll (1103), Thrower Troll (1104), Thrower Troll (1105), Mountain troll (1106), Mountain troll (1107), Mountain troll (1108), Mountain troll (1109), Mountain troll (1110), Mountain troll (1111), Mountain troll (1112), Troll general (1115), Troll general (1116), Troll general (1117), Troll spectator (1118), Troll spectator (1119), Troll spectator (1120), Troll spectator (1121), Troll spectator (1122), Troll spectator (1123), Troll spectator (1124), Dad (1125), Twig (1126), Berry (1127), Twig (1128), Berry (1129), Thrower troll (1130), Thrower troll (1131), Thrower troll (1132), Thrower troll (1133), Thrower troll (1134), Cook (1135), Cook (1136), Cook (1137), Mountain troll (1138), Mushroom (1139), Guard (1142), Guard (1143), Guard (1144), Guard (1145), Guard (1146), Guard (1147), Guard (1148), Guard (1149), Guard (1150), Burntmeat (1151), Lalli (1270), Ug (1553), Aga (1554), Arrg (1555), Arrg (1556), Ug (1557), Ice Troll (1560), Ice Troll (1561), Ice Troll (1562), Ice Troll (1563), Ice Troll (1564), Ice Troll (1565), Ice Troll (1566), null (1932), Troll child (1933), Troll child (1934), Ice troll (1935), Ice troll (1936), Ice troll (1937), Ice troll (1938), Ice troll (1939), Ice troll (1940), Ice troll (1941), Ice troll (1942), null (1947), Troll father (1948), null (1949), Troll mother (1950), Troll (3584), Flippa (3912), Tilt (3913), My Arm (4947), My Arm (4948), My Arm (4949), null (4950), null (4951), null (4952), null (4953), null (4954), null (4955), null (4956), My Arm (4957), My Arm (4958), My Arm (4959), Unnamed troll child (4968), Drunken dwarf's leg (4969), Ice Troll King (5472), Ice troll runt (5473), Ice troll male (5474), Ice troll female (5475), Ice troll grunt (5476), Ice troll runt (5521), Ice troll male (5522), Ice troll female (5523), Ice troll grunt (5524), Ice troll runt (5525), Ice troll male (5526), Ice troll female (5527), Ice troll grunt (5528), null (8074), null (8075), River troll (8646) +Linked graphics: 837, 838 +Linked objects: Troll (13369) + +Base: 462 +Linked animations: [1143, 5017] +Linked graphics: 275, 850 + +Base: 463 +Linked animations: [1940, 1941, 1942] +Linked objects: null (6495) + +Base: 464 +Linked animations: [1943] +Linked objects: null (6482), Obelisk (6483), Obelisk (6484), null (6485), Obelisk (6486), Obelisk (6487), null (6488), Obelisk (6489), Obelisk (6490), null (6491), Obelisk (6492), Obelisk (6493) + +Base: 465 +Linked animations: [1939, 7291] +Linked objects: Pitfall (6521), null (6522), Pitfall (27240) + +Base: 466 +Linked animations: [1938, 2905] +Linked objects: null (6520), null (10395), null (10398), null (10401), null (10403) + +Base: 467 +Linked animations: [1944] +Linked objects: Wall Crusher (6629) + +Base: 468 +Linked animations: [1945, 1946, 1947, 1948, 1949, 5464] +Linked NPCs: Scarabs (1969), Scarab swarm (2001), Scarab swarm (4500) + +Base: 469 +Linked animations: [1925, 1926, 1927, 1928, 1929, 1934] + +Base: 470 +Linked animations: [1935] + +Base: 471 +Linked animations: [1936, 1937] +Linked graphics: 355, 356 +Linked objects: null (6518), null (6519) + +Base: 472 +Linked animations: [1951] +Linked graphics: 354 + +Base: 473 +Linked animations: [1845, 1846] +Linked objects: null (5963), Cannonball (5964), Gold cannonball (5965) + +Base: 474 +Linked animations: [1847] +Linked objects: Fire (5981) + +Base: 475 +Linked animations: [2135] +Linked objects: Crusher (6985) + +Base: 476 +Linked animations: [2136] +Linked objects: Bookcase (6972) + +Base: 477 +Linked animations: [1869] +Linked objects: Fireplace (6093), Fireplace (6094), Fireplace (6095), Fireplace (6096) + +Base: 478 +Linked animations: [1868] +Linked objects: Table (6197), Table (6199), Table (6201), Lamp (6202), Lamp (6203), Lantern (11468), null (23507), null (23514) + +Base: 479 +Linked animations: [2137] +Linked objects: Plant (7013), Plant (7014), Plant (7015) + +Base: 480 +Linked animations: [1870] + +Base: 481 +Linked animations: [1838, 1839, 1840, 1841, 1842, 1843, 1844] +Linked NPCs: Arzinian Avatar of Strength (1850), Arzinian Avatar of Strength (1851), Arzinian Avatar of Strength (1852), Arzinian Avatar of Ranging (1853), Arzinian Avatar of Ranging (1854), Arzinian Avatar of Ranging (1855), Arzinian Avatar of Magic (1856), Arzinian Avatar of Magic (1857), Arzinian Avatar of Magic (1858), Arzinian Being of Bordanzan (1859) + +Base: 482 +Linked animations: [1049] +Linked objects: null (3407) + +Base: 483 +Linked animations: [1051] +Linked objects: null (3405), Water wheel (18511), Water wheel (18512) + +Base: 484 +Linked animations: [1052] +Linked objects: null (3411) + +Base: 485 +Linked animations: [1039, 1040, 1041, 1042] +Linked NPCs: Air elemental (1021) + +Base: 486 + +Base: 487 +Linked animations: [1027, 1029, 1030, 1031] +Linked NPCs: Fire elemental (1019) + +Base: 488 +Linked animations: [1044, 1045, 1046, 1047, 1048] +Linked NPCs: Water elemental (1022) + +Base: 489 +Linked animations: [1053] +Linked graphics: 244, 809, 2683 + +Base: 490 +Linked animations: [2915] + +Base: 491 +Linked animations: [2906, 2907, 2908] +Linked NPCs: Nirrie (3023), Tirrie (3024), Hallak (3025), Githan (7122), Amulet of Nature (7123) + +Base: 492 +Linked animations: [2916] +Linked graphics: 487 + +Base: 493 +Linked animations: [3098] + +Base: 494 +Linked animations: [3100] +Linked objects: Statue (10966), Statue (10967), Statue (10968), Statue (10969) + +Base: 495 +Linked animations: [3099] +Linked objects: Statue (10962), Statue (10963), Statue (10964), Statue (10965) + +Base: 496 +Linked animations: [3097] +Linked objects: Magic spell (11141) + +Base: 497 +Linked animations: [3095, 3096] +Linked objects: Magic barrier (11005) + +Base: 498 +Linked animations: [3101] +Linked objects: Light (11079), Light (12203), Rope (12601), Light (12605), Light (14179), Warren exit (23118), null (23145), Rabbit hole (29829), Rabbit hole (30074), null (41840), null (42907) + +Base: 499 +Linked animations: [3073, 3074, 3075, 3076, 3077, 3078, 3088] +Linked NPCs: null (3128), Boneguard (3129), Boneguard (3140) + +Base: 500 +Linked animations: [3079, 3080, 3081, 3082, 3083, 3084, 3085, 3086, 3087, 3089] +Linked graphics: 525, 526, 527, 528, 529, 530, 531, 532, 533, 535 + +Base: 501 +Linked animations: [3246] +Linked objects: A fairy workbench (12066), Fairy workbench (16213) + +Base: 502 +Linked animations: [3247] +Linked objects: A fairy workbench (12067), Fairy workbench (16215) + +Base: 503 +Linked animations: [3264] +Linked objects: Chicken Shrine (12093) + +Base: 504 +Linked animations: [13899] + +Base: 505 +Linked animations: [13889, 13890] +Linked NPCs: Zanaris choir (3312) + +Base: 506 +Linked animations: [3259, 3260, 3261, 3262, 3263] + +Base: 507 +Linked animations: [3258] +Linked graphics: 568, 569 + +Base: 508 +Linked animations: [1621] + +Base: 509 +Linked animations: [1620] + +Base: 510 +Linked animations: [1629] +Linked objects: Fireplace (5165), Fireplace (10824) + +Base: 511 +Linked animations: [1630] +Linked objects: Fireplace surround (5178), Fireplace surround (5179), null (10825), null (10826) + +Base: 512 +Linked animations: [1632] +Linked objects: Lightning conductor (5177) + +Base: 513 +Linked animations: [1631] +Linked objects: Signpost (5164) + +Base: 514 +Linked animations: [1622, 1623] +Linked NPCs: null (1671), null (1672) + +Base: 515 +Linked animations: [1624, 1625, 1626, 1627, 1628] +Linked NPCs: Experiment (1676) + +Base: 516 +Linked animations: [3124] +Linked graphics: 542 + +Base: 517 +Linked animations: [3127] +Linked graphics: 547 + +Base: 518 +Linked animations: [3125, 3126] +Linked graphics: 545, 546 + +Base: 519 +Linked animations: [3123] + +Base: 520 +Linked animations: [3843] +Linked objects: Fishing Contest Banner (14035), null (48836) + +Base: 521 +Linked animations: [1920] + +Base: 522 +Linked animations: [1924] +Linked graphics: 353, 358 + +Base: 523 +Linked animations: [1922, 1923] +Linked objects: null (6422), null (6424), Mystical mirror (6425), null (6426), Mystical mirror (6427), null (6428), Mystical mirror (6429), null (6430), Mystical mirror (6431), null (6432), Mystical mirror (6433) + +Base: 524 + +Base: 525 + +Base: 526 +Linked animations: [1912, 1913, 2884, 2885, 2886, 2917, 2918, 2919, 7623] +Linked NPCs: null (1907), Broken clay golem (1908), Damaged clay golem (1909), Clay golem (1910), null (2926), Clay golem (2927), Clay golem (2928), Black golem (3026), White golem (3027), Grey golem (3028), Clay golem (6785) + +Base: 527 +Linked animations: [1908, 2878] +Linked objects: Portal (6282), Portal (10251) + +Base: 528 +Linked animations: [1910, 1911] + +Base: 529 +Linked animations: [1909] +Linked objects: Door (6363) + +Base: 531 +Linked animations: [3037, 3038] +Linked objects: Sandpit (10814) + +Base: 532 +Linked animations: [1452] + +Base: 533 +Linked animations: [1453, 1454, 1455, 1456] + +Base: 534 +Linked animations: [1450, 1451] +Linked NPCs: Innocent-looking key (1543) + +Base: 535 +Linked animations: [1347] +Linked objects: Lighting mechanism (4587) + +Base: 536 +Linked animations: [1349] +Linked objects: Lighting mechanism (4589) + +Base: 537 +Linked animations: [1348] +Linked objects: Lighting mechanism (4590), Lighting mechanism (21945) + +Base: 538 +Linked animations: [1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1527, 1528, 1529, 1530, 1540, 1541, 2361] +Linked NPCs: Dagannoth (1338), Dagannoth (1339), Dagannoth (1340), Dagannoth (1341), Dagannoth (1342), Dagannoth (1343), Dagannoth (1344), Dagannoth (1345), Dagannoth (1346), Dagannoth (1347), Dagannoth mother (1348), Dagannoth mother (1349), Dagannoth mother (1350), Dagannoth mother (1351), Dagannoth mother (1352), Dagannoth mother (1353), Dagannoth mother (1354), Dagannoth mother (1355), Dagannoth mother (1356), Dagannoth spawn (2454), Dagannoth (2455), Dagannoth (2456), Dagannoth (2887), Dagannoth (2888), Gelatinnoth Mother (3497), Gelatinnoth Mother (3498), Gelatinnoth Mother (3499), Gelatinnoth Mother (3500), Gelatinnoth Mother (3501), Gelatinnoth Mother (3502), Dagannoth (3591), Dagannoth sentinel (9089), Dagannoth guardian (9092), Dagannoth (9097), Dagannoth (9098), Dagannoth (9103), Dagannoth Mother (9106), Dagannoth Mother (9107), Dagannoth Mother (9108), Dagannoth Mother (9109), Dagannoth Mother (9112), Dagannoth Mother (9113), Dagannoth Mother (9114), Dagannoth Mother (9115), Dagannoth Mother (9116), Dagannoth Mother (9117), Dagannoth (9128), Dagannoth (9129), Dagannoth (9132), Dagannoth (9133), Dagannoth Mother (9343), Dagannoth (9344), Dagannoth (9345), Dagannoth (9346) +Linked objects: Dagannoth (13376) + +Base: 539 +Linked animations: [1310, 1311, 1312, 1313, 1314, 1315, 1316, 2368] +Linked NPCs: Rock Crab (1265), Rocks (1266), Rock Crab (1267), Rocks (1268), Giant Rock Crab (2452), Crab (2706), Giant Rock Crab (2885) + +Base: 540 +Linked animations: [3408, 3409] +Linked graphics: 582 +Linked objects: null (12380) + +Base: 541 +Linked animations: [3404, 3405, 3406, 3407] +Linked graphics: 581 +Linked objects: Cauldron (12400), Cauldron (12401), Explosion (12402) + +Base: 542 +Linked animations: [3445] +Linked objects: null (12532), null (42908) + +Base: 543 +Linked animations: [3446, 3447, 3448, 3449] +Linked graphics: 583, 584, 585, 586 + +Base: 544 +Linked animations: [3478] +Linked objects: Red Banana Tree (12608) + +Base: 545 +Linked animations: [3527, 3528, 3529, 3530, 3531, 3532, 3533, 3534] +Linked objects: Snake (12594), Snake (12595), Snake (12596), Hole (12597), Snake (12598), Snake (12599), Snake (12600) + +Base: 546 +Linked animations: [3349] +Linked objects: Barrier (12351), Barrier (12352), Barrier (12353), null (12357), null (12358), null (12359), null (12360), null (12361), null (12362), null (12363), null (12364) + +Base: 547 +Linked animations: [3350, 3351, 3352] +Linked graphics: 601 +Linked objects: null (12354), Portal (12355), Portal (12356), null (12365), Portal (46500) + +Base: 548 +Linked animations: [3498, 3499, 3500, 3501, 3502, 3503] +Linked NPCs: Agrith-Na-Na (3493) + +Base: 549 +Linked animations: [3504, 3505, 3506, 3507, 3508, 3509] +Linked NPCs: Dessourt (3496) + +Base: 550 +Linked animations: [7263] +Linked objects: Chair (12312), Chair (12313) + +Base: 551 +Linked animations: [3357] +Linked NPCs: Culinaromancer (3492) +Linked graphics: 591 + +Base: 552 +Linked animations: [3354, 3355] +Linked graphics: 589, 590, 592, 593, 594, 595, 596, 597, 598, 599, 600, 602, 836, 839, 853, 854 + +Base: 553 +Linked animations: [3459, 11447] +Linked objects: Old tree (12551) + +Base: 554 +Linked animations: [3469, 3472] +Linked NPCs: Balloon Toad (3473), Balloon Toad (3474), Balloon Toad (3475) +Linked objects: Rock and string (12556), Rock and string (12557) + +Base: 555 +Linked animations: [3473, 3474] + +Base: 556 +Linked animations: [3470] +Linked graphics: 587 + +Base: 557 +Linked animations: [3440] +Linked graphics: 603 +Linked objects: Bubbles (12531), null (26484), null (31565) + +Base: 558 +Linked animations: [3439] + +Base: 559 +Linked animations: [3424, 3425, 3426, 3427, 3428, 3429, 3430, 8098, 8099, 8100, 8101, 8102, 8103, 8104, 8105, 8106, 8107, 8108, 8109, 8370, 8541, 14276, 14277] +Linked NPCs: Crab (3421), Crab (3424), Crab (4346), Granite crab (6796), Granite crab (6797), Baby giant crab (6947), Giant crab (6948), Baby giant crab (7293), Giant crab (7294), Baby giant crab (7295), Giant crab (7296), Baby giant crab (7297), Giant crab (7298), Baby giant crab (7299), Giant crab (7300), Giant crab (7829), Giant crab (9218), Giant crab (9219), Giant crab (9220), Giant crab (9221), Giant crab (9222) + +Base: 560 +Linked animations: [3441, 3442] +Linked NPCs: Fish (3425), Fish (3426), Fish (3427), Fish (3428), Fish (3429), Fish (3430), Fish (3431), Fish (3432), Fish (3433), Fish (3434), Fish (3435), Fish (3436), Fish (3437), Fish (3438), Fish (3439), Fish (3440), Fish (3441), Fish (3442) + +Base: 561 +Linked animations: [3438] +Linked objects: Kelp (12477), null (31598) + +Base: 562 +Linked animations: [3431, 3432, 3433, 3434, 3435] +Linked NPCs: Mudskipper (3422), Mudskipper (3423) + +Base: 563 +Linked animations: [3443, 3444] +Linked NPCs: Fish (3443), Fish (3444), Fish (3445), Fish (3446), Fish (3447), Fish (3448) + +Base: 564 +Linked animations: [3347] +Linked graphics: 579 +Linked objects: Dragonfire (12258) + +Base: 565 +Linked animations: [3343] +Linked objects: Nest (12256), Nest (12259) + +Base: 566 +Linked animations: [3344] + +Base: 567 +Linked animations: [3345, 3346] +Linked NPCs: K'klik (3377) + +Base: 568 +Linked animations: [1997, 1998] +Linked objects: (6642) + +Base: 569 +Linked animations: [1999, 2000] +Linked objects: Doorway (6643) + +Base: 570 +Linked animations: [2031, 2032, 2035, 2038, 2039] + +Base: 571 +Linked animations: [2011, 2012, 2013, 2014, 2015] +Linked NPCs: Locust (1995) + +Base: 572 +Linked animations: [2027, 2028, 2029, 2030] + +Base: 573 +Linked animations: [2018] + +Base: 574 +Linked animations: [1174, 1175] + +Base: 575 +Linked animations: [1183, 1184, 1186, 1187, 1189, 1190, 1251] +Linked NPCs: Kalphite Queen (1159), Flesh Crawler (4389), Flesh Crawler (4390), Flesh Crawler (4391) + +Base: 576 + +Base: 577 +Linked animations: [1176, 1177, 1179, 1180, 1181, 1182] + +Base: 578 + +Base: 579 +Linked animations: [3542] +Linked objects: null (12701), null (12702), null (12703), null (12704) + +Base: 580 +Linked animations: [1761] + +Base: 581 +Linked animations: [1756, 1757, 1758, 1759, 1760] +Linked NPCs: null (1811), The Kendal (1812), The Kendal (1813) + +Base: 582 +Linked animations: [1398] +Linked objects: Awowogei (4771) + +Base: 583 + +Base: 584 +Linked animations: [1410, 1411] +Linked objects: Gnome Glider (4866) + +Base: 585 + +Base: 586 +Linked animations: [1416] + +Base: 587 +Linked animations: [1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 3479] +Linked NPCs: Trefaji (1431), Aberab (1432), Hafuba (1438), Monkey Guard (1459), Monkey Guard (1460), Elder Guard (1461), Elder Guard (1462), Gorilla (1482), Bearded gorilla (1483), Ancient monkey (1484), null (9034), null (9035) + +Base: 588 +Linked animations: [1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 3390, 3392, 3393, 3394, 3480, 3481, 3482, 3484, 3485, 3486, 3487, 3488, 3489, 3490, 3491, 3492, 3493, 3494, 3523, 3524, 3525, 3526, 11952, 13941, 13942, 13943, 13944, 13945] +Linked NPCs: Dugopul (1429), Solihib (1433), Daga (1434), Tutab (1435), Ifaba (1436), Hamab (1437), Denadu (1439), Lofu (1440), Kruk (1441), Duke (1442), Oipuis (1443), Uyoro (1444), Ouhai (1445), Uodai (1446), Padulah (1447), Awowogei (1448), Uwogo (1449), Muruwoi (1450), Sleeping Monkey (1451), Monkey Child (1452), The Monkey's Uncle (1453), The Monkey's Aunt (1454), Monkey Guard (1455), Monkey Archer (1456), Monkey Archer (1457), Monkey Archer (1458), Monkey Zombie (1465), Monkey Zombie (1466), Monkey Zombie (1467), Bonzara (1468), Small ninja monkey (1480), Medium ninja monkey (1481), Small zombie monkey (1485), Large zombie monkey (1486), Awowogei (3396), Awowogei (3397), King Awowogei (3478), Mizaru (3481), Kikazaru (3482), Iwazaru (3483), Blue Monkey (4371), Red Monkey (4372), King Awowogei (7446) + +Base: 589 +Linked animations: [2307] +Linked graphics: 412, 413, 414, 415 + +Base: 590 +Linked animations: [12109] +Linked objects: Tree (8743) + +Base: 591 +Linked animations: [2330, 2331] +Linked objects: An empty rack (9329), null (36309) + +Base: 592 +Linked animations: [2743] +Linked objects: null (9992), null (46405), null (46443) + +Base: 593 +Linked animations: [2744] +Linked objects: Crystal (10014) + +Base: 594 +Linked animations: [2746] +Linked objects: Light door (9768), Light door (9769), Light door (9770), Light door (9771), Light door (9772), Light door (9773), Light door (9774), Light door (9775) + +Base: 595 +Linked animations: [2747, 2748] +Linked objects: null (9799), null (9800), null (9801), null (9802), null (9803), null (9804), null (9805), null (9806), null (9807), null (9808), null (9809), null (9810), null (9811), null (9812), Light beam (46511), Light beam (46512), Light beam (46513), Light beam (46514), Light beam (46515), Light beam (46516), Light beam (46517) + +Base: 596 +Linked animations: [2734] +Linked objects: Crystal collector (9749) + +Base: 597 +Linked animations: [2742] +Linked objects: null (9813), null (9814), null (9815), null (9816), null (9817), null (9818), null (9819) + +Base: 598 +Linked animations: [2745] + +Base: 599 +Linked animations: [2735, 2736, 2737, 2738, 2739, 2740] +Linked NPCs: Shadow (2782) + +Base: 600 +Linked animations: [2327, 2328] +Linked graphics: 416, 418, 420, 422, 424 + +Base: 601 +Linked animations: [1098] +Linked graphics: 262 +Linked objects: Budding branch (3511) + +Base: 602 +Linked animations: [1096] +Linked graphics: 260 +Linked objects: Fungi on log (3509) + +Base: 603 +Linked animations: [1097] +Linked graphics: 261 +Linked objects: A golden pear bush (3513) + +Base: 604 +Linked animations: [1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095] +Linked NPCs: Ghast (1052), Ghast (1053), Ghast (3606), Ghast (3607), Ghast (3608), Ghast (3609), Ghast (3610), Ghast (3611) +Linked graphics: 265 + +Base: 605 +Linked animations: [1099] +Linked graphics: 263 + +Base: 606 +Linked animations: [1101] +Linked graphics: 264 + +Base: 607 +Linked animations: [1102] +Linked graphics: 255, 259 + +Base: 608 +Linked animations: [1747] +Linked objects: Gnome landing light (5815), Flashing landing light (5816), Flashing landing light (5817), Flashing landing light (5818), Flashing landing light (5819), null (5820), null (5821), null (5822), null (5823) + +Base: 609 +Linked animations: [1748, 1749, 1750, 1751, 1752, 1753, 1754, 9731, 9732, 9733] +Linked NPCs: Slagilith (1802), Rock pile (1803), Slagilith (1804), Flambeed (3494) + +Base: 610 +Linked animations: [3190] + +Base: 611 +Linked animations: [3188, 3189] + +Base: 612 +Linked animations: [3840] +Linked NPCs: Bones (3674) + +Base: 613 +Linked animations: [2895] +Linked graphics: 486 + +Base: 614 +Linked animations: [2900] +Linked objects: Lantern (10307), null (11815), null (12542) + +Base: 615 +Linked animations: [2897] +Linked objects: Rat trap (10292) + +Base: 616 +Linked animations: [2898] +Linked objects: Rat barrel (10301), Rat barrel (10310), Rat barrel (10317), Rat barrel (35151) + +Base: 617 +Linked animations: [2901] +Linked objects: Rat barn (10300), Rat barn (10312) + +Base: 618 +Linked animations: [2896] +Linked graphics: 484, 485 + +Base: 619 +Linked animations: [2899] +Linked objects: Rat lever (10299), Rat lever (10311) + +Base: 620 +Linked animations: [1212] +Linked objects: null (3878), Elven lamp (3918), Elven lamp (8767), null (8774), Elven lamp (32897) + +Base: 621 +Linked animations: [1234] +Linked objects: Tent wall (3997) + +Base: 622 +Linked animations: [1211] +Linked objects: null (3872) + +Base: 623 +Linked animations: [1230] +Linked graphics: 287 + +Base: 624 + +Base: 625 +Linked animations: [1229] +Linked graphics: 286 + +Base: 626 + +Base: 627 + +Base: 628 +Linked animations: [1231, 1232] +Linked objects: Barrel (4024) + +Base: 629 +Linked animations: [1233] +Linked objects: Barrel (3987), Barrel (4025) + +Base: 630 +Linked animations: [1235] +Linked objects: Well (4004), Well (4005) + +Base: 631 +Linked animations: [1213] + +Base: 632 +Linked animations: [1217, 1218] + +Base: 633 +Linked animations: [1215, 1216] +Linked objects: null (3926) + +Base: 634 +Linked animations: [1214] + +Base: 635 +Linked animations: [1219] + +Base: 636 + +Base: 637 +Linked animations: [1493, 1494, 1495, 1496, 1497, 1498] + +Base: 638 +Linked animations: [1274, 1276, 1277, 1278, 1279] +Linked NPCs: Myre Blamish Snail (1227), Blood Blamish Snail (1228), Ochre Blamish Snail (1229), Bruise Blamish Snail (1230), Bark Blamish Snail (1231), Myre Blamish Snail (1232), Blood Blamish Snail (1233), Ochre Blamish Snail (1234), Bruise Blamish Snail (1235) + +Base: 639 +Linked animations: [1503] +Linked graphics: 311 + +Base: 640 +Linked animations: [1635, 1636] +Linked objects: Crystal growth (5248) + +Base: 641 +Linked animations: [3535, 3536, 3537, 3538, 3539, 3540, 3736, 3737, 3738] +Linked NPCs: Big Snake (3484), Swamp snake (3599), Swamp snake (3600), Swamp snake (3601), Swamp snake (3602), Dead swamp snake (3603), Dead swamp snake (3604), Dead swamp snake (3605), Sea Snake Young (3939), Sea Snake Hatchling (3940) + +Base: 642 +Linked animations: [2807] +Linked objects: null (10104), Brewing control (10142), Brewing control (10143), Brewing control (10144) + +Base: 643 +Linked animations: [2808] + +Base: 644 +Linked animations: [2802, 2803, 2804, 2805, 2806] +Linked NPCs: Evil spirit (2849) + +Base: 646 +Linked animations: [1293, 1294, 1295, 1296, 1297, 1298, 1299, 1300, 1301, 1302] +Linked objects: Temple wall (4069), Temple wall (4070), Temple wall (4071), Temple wall (4072), Temple wall (4073), Temple wall (4074), Temple wall (4075), Temple wall (4076), Temple wall (4077), Temple wall (4078), Temple wall (4080), Temple wall (4081), Temple wall (4082), Temple wall (4083), Temple wall (4084), Temple wall (4085), Temple wall (4086), Temple wall (4087), Temple wall (4088), Temple wall (4089) + +Base: 647 +Linked animations: [1303, 1304] +Linked objects: Funeral Pyre (4093), Funeral pyre (4094), Funeral pyre (4095), Funeral pyre (4096), Funeral pyre (4097), Funeral pyre (4098), Funeral pyre (9006), Funeral pyre (9007), Funeral pyre (21271), Funeral pyre (29166), Funeral pyre (29181), Funeral pyre (30467), Funeral pyre (30468), Funeral pyre (30469), Funeral pyre (30470), Funeral pyre (30471), Funeral pyre (30472), Funeral pyre (30473), Funeral pyre (30474), Funeral pyre (30475), Funeral pyre (30476), Funeral pyre (30477) + +Base: 648 +Linked animations: [1305, 9066] +Linked graphics: 292, 1597 + +Base: 649 +Linked animations: [1281, 1282, 1283, 1284, 1286, 1287, 1288, 1308] +Linked NPCs: Loar Shade (1241), Shade Spirit (1242), Phrin Shade (1244), Riyl Shade (1246), Asyn Shade (1248), Fiyr Shade (1250), Riyl shadow (3615), Asyn shadow (3616), Shade (3617), Loar Shadow (7900) + +Base: 650 +Linked animations: [1285] +Linked graphics: 293 + +Base: 651 +Linked animations: [1289, 1290, 1291, 1292] +Linked NPCs: Loar Shadow (1240), Phrin Shadow (1243), Riyl Shadow (1245), Asyn Shadow (1247), Fiyr Shadow (1249), Loar Shadow (7900), Loar Shadow (7901) + +Base: 652 +Linked animations: [3835] +Linked objects: Hole (13925) + +Base: 653 +Linked animations: [3815, 3816, 3817, 3818, 3819, 3820, 3821] +Linked NPCs: Confusion beast (3650), Confusion beast (3651), Confusion beast (3652), Confusion beast (3653), Confusion beast (3654), Confusion beast (3666) + +Base: 654 +Linked animations: [3809, 3810, 3811, 3812, 3813, 3814] +Linked NPCs: Fear reaper (3649), Fear reaper (3665) + +Base: 655 +Linked animations: [3822, 3823, 3824, 3825, 3826, 3827] +Linked NPCs: Hopeless creature (3655), Hopeless creature (3656), Hopeless creature (3657), Hopeless creature (3667), Hopeless beast (3668), Hopeless beast (3669) + +Base: 656 +Linked animations: [3828, 3829, 3830, 3831, 3832, 3833, 3834] +Linked NPCs: Tolna (3658), Tolna (3659), Tolna (3660) +Linked graphics: 632 + +Base: 657 +Linked animations: [3808] +Linked graphics: 631 + +Base: 658 +Linked animations: [3805] +Linked graphics: 630 + +Base: 659 +Linked animations: [2882] +Linked NPCs: null (2920), Sand storm (2921) + +Base: 660 +Linked animations: [2814] +Linked graphics: 465, 480 + +Base: 661 +Linked animations: [2889] +Linked graphics: 483 + +Base: 662 +Linked animations: [2883] +Linked graphics: 481 +Linked objects: Magic circle (10252) + +Base: 663 +Linked animations: [3976] +Linked objects: null (14955), Firebox (14960) + +Base: 664 +Linked animations: [3983, 3984, 3985, 3986, 3987, 3988] +Linked NPCs: Sea troll (3840), Sea troll (3841), Sea troll (3842), Sea troll (3843), Sea troll (3845) + +Base: 665 +Linked animations: [3977, 3978, 3979, 3980, 3981, 3982] +Linked NPCs: Sea Troll General (3846) + +Base: 666 +Linked animations: [3989, 3990, 3991, 3992, 3993] +Linked NPCs: Sea Troll Queen (3847) + +Base: 667 +Linked animations: [3974] +Linked graphics: 663 + +Base: 668 +Linked animations: [3975] +Linked graphics: 664 + +Base: 669 +Linked animations: [3995] +Linked graphics: 662 + +Base: 670 +Linked animations: [1875, 1876] +Linked objects: Table (6246) + +Base: 671 +Linked animations: [1881] +Linked objects: Dung (6257), Dung (6259), null (18844) + +Base: 672 +Linked animations: [2571] +Linked NPCs: Trolley (2582), null (2585) + +Base: 673 +Linked animations: [1160, 6302, 11689] +Linked graphics: 277, 349, 1056, 2048 + +Base: 674 +Linked animations: [1479] +Linked graphics: 574 + +Base: 675 +Linked animations: [2672, 2673] + +Base: 676 +Linked animations: [2685, 2686, 2687, 2688, 2689] +Linked NPCs: Dragonkin (2641), Sithaph (8493), Strisath (8494), Sakirth (8495) + +Base: 677 +Linked animations: [2671] + +Base: 678 +Linked animations: [1269, 1270, 1271, 1272, 1273, 6428, 6429] +Linked NPCs: Leech (1219), Leech display (5971) + +Base: 679 +Linked animations: [1260] +Linked objects: null (4046) + +Base: 680 +Linked animations: [1261, 1262] +Linked NPCs: Mist (1222), Mist (1578), Mist (3530) +Linked graphics: 310 +Linked objects: null (4047) + +Base: 681 + +Base: 682 +Linked animations: [1915] +Linked objects: Vampire tomb (6438) + +Base: 683 +Linked animations: [1917, 1918, 1919] +Linked graphics: 351, 352 + +Base: 684 + +Base: 685 +Linked animations: [1916] +Linked graphics: 350 + +Base: 686 +Linked animations: [1334] +Linked objects: Spit roast (4267) + +Base: 689 +Linked animations: [2722] +Linked graphics: 454 + +Base: 690 +Linked animations: [2091] +Linked objects: null (6889) + +Base: 691 +Linked animations: [2103] + +Base: 692 +Linked animations: [3273] + +Base: 693 +Linked animations: [2133] +Linked objects: null (6967), null (6968) + +Base: 694 +Linked animations: [473] +Linked objects: null (725) + +Base: 696 +Linked animations: [474] +Linked objects: Bridge (3240) + +Base: 697 +Linked animations: [454, 455] +Linked graphics: 57, 58 + +Base: 698 +Linked animations: [2711] + +Base: 699 +Linked animations: [453] + +Base: 700 +Linked animations: [2714] +Linked objects: Instruction sign (29461), Instruction sign (29462), Instruction sign (29463), Instruction sign (29464), Signpost (31298), Signpost (31300), Signpost (31301) + +Base: 701 +Linked animations: [503] +Linked objects: null (1887), Altar (2478), Altar (2479), Altar (2480), Altar (2481), Altar (2482), Altar (2483), Altar (2484), Altar (2485), Altar (2486), Altar (2487), Altar (2488), Altar (17010), Altar (30624) + +Base: 702 +Linked animations: [502] +Linked objects: Mysterious glow (2498), Mysterious glow (2500), Mysterious glow (2501), Mysterious glow (3752), Mysterious glow (44441) + +Base: 703 +Linked animations: [505] +Linked objects: null (515) + +Base: 704 +Linked animations: [3217] +Linked objects: Gallows (11495) + +Base: 705 +Linked animations: [3172, 3173] +Linked objects: Portal machine (11357), Strange machine (11548), Strange machine (11549), Strange machine (47608), Strange machine (47609), Portal machine (47611) + +Base: 706 +Linked animations: [3218] +Linked objects: Chair (11496) + +Base: 707 +Linked animations: [3219] +Linked objects: Spooky picture (11497) + +Base: 708 +Linked animations: [3220] +Linked NPCs: null (3293), null (9350), null (9351), null (9352) + +Base: 709 + +Base: 710 +Linked animations: [3237] +Linked objects: Canary (11875) + +Base: 711 +Linked animations: [3230] +Linked objects: Rat wheel (11370), Treadmill (11677) + +Base: 712 +Linked animations: [504, 1055] +Linked graphics: 246, 247, 332, 626 +Linked objects: null (740), Portal (2465), Portal (2466), Portal (2467), Portal (2469), Portal (2470), Portal (2471), Portal (2472), Portal (2473), Portal (2474), Portal (2476), Portal (2477), Portal (2492), null (6550), Portal (6551), Portal (7272), Portal (7273), Portal (7288), Portal (7289), Portal (7315), Portal (7316), Portal (7318), Portal (7319), Portal (7321), Portal (7322), Portal (7324), Portal (7325), Portal (7352), Portal (7353), Portal (8972), Portal (8987), Portal (16050), Portal (16082), Portal (16116), Portal (16150), Portal (16686), Portal (23093), Floor (23094), Portal (23095), null (23098), Portal (28140), Portal (28779), Portal (28780), null (28888), Portal (29537), Portal (30146), Exit (36339), Exit (36340), Exit (36341), Portal (38697), Portal (41681) + +Base: 713 +Linked animations: [480] +Linked graphics: 470 +Linked objects: Swamp bubbles (684), Swamp bubbles (735), Swamp (3263), null (3961), Mud (5883), Swamp (5884), null (10169) + +Base: 714 +Linked animations: [2564] +Linked objects: Candles (9159), Candles (9160), Candles (9161), Candles (11472), Candles (11473), Candles (12301), Candles (12302), Candles (12980), Candles (12981), Candles (19127), Candles (24348), Candles (34615), Candles (39841) + +Base: 715 +Linked animations: [479] +Linked NPCs: Adventurer (4960) +Linked objects: Cauldron (692), Cauldron (2043), Cooking pot (3662), Troll Stew (3824), Lalli's Stew (4149), Betty's cauldron (9515), Aggie's Cauldron (9736), Cauldron (14338), Cauldron (14339) + +Base: 716 +Linked animations: [2708] +Linked objects: Hot Coals (9549) + +Base: 717 +Linked animations: [482, 483, 484, 485, 486, 487] + +Base: 718 +Linked animations: [477] +Linked objects: Fireplace (2724), Cooking pot (2727), Furnace (3294), Fireplace (9439), null (9442), Limestone fireplace (13612), Marble fireplace (13614), Limestone fireplace (17325), Fireplace (24285), Oven (24313), Furnace (26828), Fireplace (27251), Cooking pot (33499), Fireplace (36815), Fireplace (37426), Fireplace (40195), Fireplace (42350), Fireplace (46801), Fireplace (46802), Fireplace (46803), Fireplace (47573), null (52336) + +Base: 719 +Linked animations: [478] +Linked objects: Fireplace (2725), Fireplace (2726), Fireplace (8712), null (26192), Fireplace (33498) + +Base: 720 +Linked animations: [466] + +Base: 721 +Linked animations: [467] +Linked objects: Lamp (201) + +Base: 722 +Linked animations: [1731] +Linked objects: Millstones (1778), null (1784), Millstones (22419), null (22423) + +Base: 723 +Linked animations: [472] +Linked objects: Sails (1779), null (5626), null (22425) + +Base: 724 + +Base: 725 +Linked animations: [449] +Linked graphics: 239 +Linked objects: Broken barrel (672), null (2933) + +Base: 726 +Linked animations: [441, 442, 443, 444, 445] + +Base: 727 +Linked animations: [1732] +Linked objects: Fire (5631), Fire (5632), Uncooking pot (8985) + +Base: 728 +Linked animations: [475] +Linked objects: Fire remains (714), Fire (2732), Fire (3038), Fire (3775), Burning bones (3862), Flaming Fire altar (4090), null (4192), Fire (4265), Fire (4266), Fireplace (4618), Fireplace (4650), Fire (4766), Fire (5249), Fire remains (5252), Fire (5499), null (6405), null (6407), null (6409), null (6411), Standing Torch (6413), Standing Torch (6414), Standing Torch (6415), Standing Torch (6416), null (6818), Fire (10433), Fire (11404), Fire (11405), Fire (11406), null (12795), Fire (12796), Fire (14169), null (14172), Fire (15156), Fire remains (15206), Fire remains (15207), Fire remains (15208), Fire remains (15209), Fire remains (15210), null (19785), null (19786), Campfire (19881), null (19882), null (19883), Campfire (19884), Fire (20000), Fire (20001), Fire (21620), Camp fire (23063), Warm campfire (28486), Fire remains (33050), Standing torch (38618), Fire (49940), Fire (49941), Fire (49942), Fire (49943), Fire (49944), Fire (49945), Fire (49946), Fire (49947), Fire (49948), Fire (49949) + +Base: 729 +Linked animations: [476, 901, 1849, 1956] +Linked graphics: 78, 197, 357 +Linked objects: null (715), null (716), null (3357), null (3358), Wall of flame (5977), Wall of flame (5978), Wall of flame (5979), Wall of flame (5980), Blue Fire (6009) + +Base: 730 +Linked animations: [471] +Linked objects: Flagpole (860), Flagpole (861), Flagpole (862), Flagpole (869), Flagpole (3680), Flagpole (4692), Flagpole (4693), Flagpole (10057), Flagpole (11699) + +Base: 731 +Linked animations: [2699, 4431] +Linked objects: Hammock (9508), Hammock (16861), Hammock (17236), Hammock (18172), Hammock (34932) + +Base: 732 +Linked animations: [493] +Linked objects: null (731), null (3609), null (13858), null (13859), null (13860), Pool of Slime (17116), Pool of Slime (17117), Pool of Slime (17118), null (26831), null (26832) + +Base: 733 +Linked animations: [494] +Linked objects: null (732) + +Base: 734 +Linked animations: [495] + +Base: 735 +Linked animations: [525, 9719] +Linked objects: Fishing spot (14428) + +Base: 736 +Linked animations: [1032, 1033, 1730] +Linked objects: Standard (5628), Standard (5629), Standard (11615), Standard (11861), Standard (16568), Standard (16569), Standard (21635), Standard (21636), Standard (21637), null (21638) + +Base: 737 + +Base: 738 +Linked animations: [1727] +Linked objects: Rock (5604), Rock (5605), Rock (5606), null (5607), Rock (5985), Small rock (5986), Rock (5987), null (5988), Rock (8725), Rock (8726), Rocks (8727), Rock (15817), Small rock (15818), Rock (15819), Rock (16136), Small rock (16303), Rock (17364), Small rock (17365), Rock (17366), Rock (21571), Small rock (21572), Rock (21573), null (22347), null (22348), null (22349), Rock (23165), Rock (36626), Rock (36627), Rocks (36628), Rock (37619), Rock (37620), Rock (37621), null (40787), null (40789), null (40791) + +Base: 739 +Linked animations: [7255] + +Base: 740 +Linked animations: [1729] +Linked objects: Water wheel (5589), Water wheel (5590), Water wheel (5591), null (5593), Water wheel (5594), Water wheel (36867), Water wheel (36868), Water wheel (36869), null (36870), Water wheel (36871), Water wheel (45502), Water wheel (46022) + +Base: 741 +Linked animations: [1733] +Linked objects: Weather vane (5597), null (5811), Weather vane (33755), Weather vane (34578) + +Base: 742 +Linked animations: [488] +Linked objects: Weather vane (685) + +Base: 743 +Linked animations: [492, 1247] +Linked objects: Fly trap (1395) + +Base: 744 +Linked animations: [2749] +Linked objects: Tree (39328) + +Base: 745 +Linked animations: [496] +Linked graphics: 8 + +Base: 746 +Linked animations: [499] +Linked objects: Party Balloon (123), Party Balloon (124), Party Balloon (125), Party Balloon (126), Party Balloon (127), Party Balloon (128) + +Base: 747 +Linked animations: [501] +Linked objects: Party Balloon (130) + +Base: 748 +Linked animations: [500] +Linked objects: Party Balloon (129) + +Base: 749 +Linked animations: [498] +Linked objects: Party Balloon (115), Party Balloon (116), Party Balloon (117), Party Balloon (118), Party Balloon (119), Party Balloon (120), Party Balloon (121), Party Balloon (122) + +Base: 750 +Linked animations: [450] + +Base: 751 +Linked animations: [730] + +Base: 752 +Linked animations: [732] +Linked graphics: 183 + +Base: 753 +Linked animations: [731] +Linked graphics: 182 + +Base: 754 +Linked animations: [490] + +Base: 755 +Linked animations: [447, 448] + +Base: 756 +Linked animations: [912] +Linked objects: Flowers (2980), Flowers (2981), Flowers (2982), Flowers (2983), Flowers (2984), Flowers (2985), Flowers (2986), Flowers (2987), Flowers (2988) + +Base: 757 +Linked animations: [523] +Linked objects: null (673), null (675), null (676), null (677), null (3966), Shining pool (5897), Gas hole (5917), Gas hole (5918), null (12654), Steam Vent (15214) + +Base: 758 +Linked animations: [446] +Linked objects: Magical shield (2842) + +Base: 759 +Linked animations: [506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 3222, 3223, 3224, 3225, 3226, 3227, 3228, 3229] + +Base: 760 +Linked animations: [489] +Linked graphics: 67 + +Base: 761 + +Base: 762 +Linked animations: [522] +Linked objects: null (2016), null (2017), null (2018), null (5911), null (18513), null (18514) + +Base: 763 +Linked animations: [465, 914] +Linked graphics: 31, 68, 69, 456, 541, 622, 858 + +Base: 764 +Linked animations: [464] +Linked objects: Leak (2167), null (11221), null (11222), null (11223), null (11224), null (11225), null (11226), Ship hull (11317), Ship hull (11318), null (23553) + +Base: 765 +Linked animations: [526] +Linked objects: null (288) + +Base: 766 +Linked animations: [527] +Linked objects: null (289), null (290) + +Base: 767 +Linked animations: [907, 3231] +Linked objects: null (470), Spinning pole (11678) + +Base: 768 +Linked animations: [1726] +Linked objects: Grandfather clock (683), Grandfather clock (11493), Grandfather clock (25731), null (27050), Grandfather clock (27051), Grandfather clock (34493) + +Base: 769 +Linked animations: [470] +Linked objects: Spooky picture (955), Spooky picture (11469) + +Base: 770 +Linked animations: [462, 463] +Linked graphics: 65, 66 + +Base: 771 +Linked animations: [457] +Linked graphics: 60, 271, 302, 405, 1628 + +Base: 772 +Linked animations: [459] +Linked graphics: 62 +Linked objects: Speartrap (16517), Speartrap (20273), Spear trap (28527), Speartrap (30558) + +Base: 773 +Linked animations: [460, 461] +Linked graphics: 63, 64 + +Base: 774 + +Base: 775 +Linked animations: [458] +Linked graphics: 61 + +Base: 776 +Linked animations: [456] +Linked graphics: 59 +Linked objects: Whirlpool (2019) + +Base: 777 + +Base: 778 + +Base: 779 +Linked animations: [1362] +Linked objects: null (4619) + +Base: 780 +Linked animations: [491] +Linked objects: Portal (1812) + +Base: 781 +Linked animations: [469] +Linked objects: null (704) + +Base: 782 +Linked animations: [481, 4792, 9585] +Linked graphics: 789 +Linked objects: null (196), null (197), null (198), null (199), Standing torch (724), null (1914), null (4517), null (4694), Pillar (5072), null (5208), null (5247), null (6365), null (6537), Standing torch (6896), null (7376), null (8949), null (10116), null (10185), null (11129), null (11143), null (11817), null (11909), Torch (13201), Torch (13203), null (14361), null (15159), null (15161), null (16469), null (16661), null (16681), Torch (17237), null (28204), null (30286), null (30850), null (31706), null (31746), null (31765), Pillar (33398), Pillar (33399), null (33456), null (33464), null (33465), null (33466), null (33467), null (33468), null (33478), null (33493), null (33494), null (33799), Pillar (34317), null (34377), null (34424), Torch (35101), Torch (35102), null (35800), null (35801), null (36686), Standing torch (37820), null (38033), null (39354), null (41446), null (43034), Wall torch (43919), Wall torch (43928), Torch (44108), null (44131), null (44134), null (44159), null (44160), null (44172), null (48847), null (52294) + +Base: 783 +Linked animations: [468] +Linked objects: Charms (907), Charms (5559), Charms (14362), Charms (24677), Charms (25654), Charms (40075) + +Base: 784 +Linked animations: [524] +Linked objects: null (14437), null (14438) + +Base: 785 +Linked animations: [3946] +Linked graphics: 661 + +Base: 786 +Linked animations: [3944] +Linked objects: Obelisk (14825) + +Base: 787 +Linked animations: [3179] +Linked graphics: 561 + +Base: 788 + +Base: 789 +Linked animations: [3180] +Linked graphics: 562, 563, 564, 565, 566 +Linked objects: Raw chompy bird (11361), Raw Rabbit (11362), Iron spit (11363), Raw bird (19172), Raw hunting beast (19173) + +Base: 790 +Linked animations: [2165, 2166] +Linked NPCs: Pig (2239), Pig (2240), Piglet (2241), Piglet (2242), Piglet (2243), Pig (2316), Pig (2317), Piglet (2318), Piglet (2319), Piglet (8907) + +Base: 791 +Linked animations: [2296] +Linked graphics: 411 + +Base: 792 +Linked animations: [2294] +Linked graphics: 410 + +Base: 793 +Linked animations: [1773] +Linked NPCs: Bald Headed Eagle (1821), Eagle (5120), Eagle (5121), Eagle (5122), Eagle (5123), Karamjan Jungle Eagle (6384) + +Base: 794 + +Base: 795 +Linked animations: [1534, 1536, 1537, 1538, 2309] +Linked NPCs: Abyssal demon (1615), Abyssal minion (8624), Abyssal demon (9086), Abyssal minion (9245) + +Base: 796 + +Base: 797 +Linked animations: [1546] + +Base: 798 + +Base: 799 + +Base: 800 +Linked animations: [1562] + +Base: 801 + +Base: 802 + +Base: 803 + +Base: 804 +Linked animations: [1554, 1555, 1556, 1557, 1558] +Linked NPCs: Dust devil (1624), Smokedevil (1625) + +Base: 805 + +Base: 806 + +Base: 807 + +Base: 808 +Linked animations: [3159] + +Base: 809 +Linked animations: [3160, 3161, 3162, 3163, 3164, 3165] + +Base: 810 + +Base: 811 + +Base: 812 +Linked animations: [1564, 1566] + +Base: 813 +Linked animations: [1582, 1583, 1584, 1585] +Linked NPCs: Harpie Bug Swarm (3153) + +Base: 814 +Linked animations: [1577, 1578, 1579, 1580, 1581, 6436, 6437] +Linked NPCs: Lizard (2803), Desert Lizard (2804), Desert Lizard (2805), Desert Lizard (2806), Small Lizard (2807), Small Lizard (2808), Dagannoth fledgeling (2880), Lizard display (5975) + +Base: 815 + +Base: 816 + +Base: 817 +Linked animations: [1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593] +Linked NPCs: Skeletal Wyvern (3068), Skeletal Wyvern (3069), Skeletal Wyvern (3070), Skeletal Wyvern (3071) + +Base: 818 +Linked animations: [1597, 2310, 2774, 2775, 2776, 2777, 2778, 2982, 2983] +Linked NPCs: Fungi (3344), Fungi (3345), Zygomite (3346), Zygomite (3347) +Linked graphics: 577, 578 + +Base: 819 +Linked animations: [224] +Linked graphics: 73 + +Base: 820 +Linked animations: [1573] +Linked graphics: 337 + +Base: 821 +Linked animations: [1569, 1570, 1571] +Linked graphics: 334, 335, 336, 633, 634, 635, 636, 637, 638, 639, 640, 641, 2184, 2185, 2186 + +Base: 822 +Linked animations: [1595] +Linked graphics: 500 + +Base: 823 +Linked animations: [1594] +Linked graphics: 499 + +Base: 824 +Linked animations: [1596] +Linked graphics: 501 + +Base: 825 +Linked animations: [2989] +Linked graphics: 575, 576 + +Base: 826 +Linked animations: [1576] +Linked graphics: 328, 330 + +Base: 827 +Linked animations: [1668] +Linked graphics: 340 + +Base: 828 +Linked animations: [1574] +Linked graphics: 327 + +Base: 829 + +Base: 830 + +Base: 831 +Linked animations: [664] +Linked graphics: 346 + +Base: 832 +Linked animations: [663] +Linked NPCs: null (3093), null (4420), null (4421) +Linked graphics: 185, 268 + +Base: 833 +Linked animations: [1966, 1967] +Linked graphics: 376, 377, 538 + +Base: 834 +Linked animations: [1970, 1971] +Linked graphics: 373, 375 + +Base: 835 +Linked animations: [1968, 1969] +Linked graphics: 372, 374, 537 + +Base: 836 +Linked animations: [662] +Linked graphics: 502 + +Base: 837 +Linked animations: [661] + +Base: 838 +Linked animations: [680] +Linked graphics: 141 + +Base: 839 +Linked animations: [174, 705] +Linked graphics: 81, 781 + +Base: 840 +Linked animations: [688] +Linked graphics: 149, 150, 151, 152 + +Base: 841 +Linked animations: [3169] +Linked NPCs: Storm Cloud (3203), Storm cloud (3301) + +Base: 842 +Linked animations: [3167, 3168, 4308, 4309] +Linked NPCs: Storm Cloud (3204) +Linked objects: Lightning (16190), Lightning (16191) + +Base: 843 +Linked animations: [668] +Linked graphics: 102 + +Base: 844 +Linked animations: [670] +Linked graphics: 104 + +Base: 845 +Linked animations: [669] +Linked graphics: 103 + +Base: 846 +Linked animations: [684] +Linked graphics: 145 + +Base: 847 +Linked animations: [686] +Linked graphics: 147 + +Base: 848 +Linked animations: [685] +Linked graphics: 146 + +Base: 849 +Linked animations: [674] +Linked graphics: 108 + +Base: 850 +Linked animations: [676] +Linked graphics: 110 + +Base: 851 +Linked animations: [675] +Linked graphics: 109, 168 + +Base: 852 + +Base: 853 +Linked animations: [677, 678, 679] +Linked graphics: 114, 115, 116 + +Base: 854 +Linked animations: [689, 690, 2718] +Linked graphics: 153, 154, 452 + +Base: 855 +Linked animations: [930] +Linked graphics: 238 + +Base: 856 +Linked animations: [696] +Linked graphics: 170 + +Base: 857 +Linked animations: [698] +Linked graphics: 172 + +Base: 858 +Linked animations: [697] +Linked graphics: 171 + +Base: 859 +Linked animations: [702] +Linked graphics: 177 + +Base: 860 +Linked animations: [704] +Linked graphics: 179, 180, 181 + +Base: 861 +Linked animations: [703] +Linked graphics: 178 + +Base: 862 +Linked animations: [701] +Linked graphics: 176 + +Base: 863 +Linked animations: [653, 3746] +Linked graphics: 85, 339, 629 + +Base: 864 +Linked animations: [650] +Linked graphics: 79 + +Base: 865 +Linked animations: [648] +Linked graphics: 77, 187 + +Base: 866 +Linked animations: [652] +Linked graphics: 84 + +Base: 867 +Linked animations: [655, 13177, 13612, 14386] +Linked graphics: 87, 2441, 2589 + +Base: 868 +Linked animations: [657] +Linked graphics: 89 + +Base: 869 +Linked animations: [656, 14387] +Linked graphics: 88 + +Base: 870 +Linked animations: [649] +Linked graphics: 83 + +Base: 871 +Linked animations: [1964, 1965] +Linked graphics: 368, 369, 370, 371, 539 + +Base: 872 +Linked animations: [1961] +Linked graphics: 363, 364, 365 + +Base: 873 +Linked animations: [1960] +Linked graphics: 362 + +Base: 874 +Linked animations: [1962, 1963] +Linked graphics: 366, 367, 540 + +Base: 875 +Linked animations: [1959] +Linked graphics: 361 + +Base: 876 +Linked animations: [1958] +Linked graphics: 360 + +Base: 877 +Linked animations: [913] +Linked graphics: 199 + +Base: 878 +Linked animations: [3138, 3139] +Linked graphics: 548, 549 + +Base: 879 +Linked animations: [2596] +Linked graphics: 436 + +Base: 880 +Linked animations: [2597, 2617] +Linked graphics: 437, 441 + +Base: 881 +Linked animations: [790] +Linked graphics: 186 + +Base: 882 +Linked animations: [647] +Linked graphics: 76 + +Base: 883 +Linked animations: [1976, 1977] +Linked graphics: 382, 383 + +Base: 884 +Linked animations: [1973] +Linked graphics: 379, 381 + +Base: 885 +Linked animations: [1974] +Linked graphics: 378 + +Base: 886 +Linked animations: [1975] +Linked graphics: 380, 536 + +Base: 887 +Linked animations: [1985, 1987] +Linked graphics: 389, 391 + +Base: 888 +Linked animations: [1984, 1986] +Linked graphics: 388, 390 + +Base: 889 +Linked animations: [1982, 1983] +Linked graphics: 385, 387 + +Base: 890 +Linked animations: [1980, 1981] +Linked graphics: 384, 386 + +Base: 891 +Linked animations: [2709, 5205] +Linked graphics: 931 +Linked objects: null (674) + +Base: 892 +Linked animations: [103, 334, 335, 351, 1717, 1718, 5018, 5482, 5484, 5486, 5488, 5490, 5492, 5804, 5805, 5806, 5807, 5808, 6268, 11691, 11692, 11693, 11694, 11695, 11696, 11697, 11698, 11699, 11700, 11701, 11702, 11703, 11704, 11705] +Linked NPCs: Leprechaun (418), Leprechaun (419), Shamus (654), Tool leprechaun (3021), Tool Leprechaun (4965), Tool leprechaun (7557), Tool leprechaun (7558), Tool leprechaun (7559), Tool leprechaun (7560), Tool leprechaun (7561), Tool leprechaun (7562), Tool leprechaun (7563), Tool leprechaun (7564), Tool leprechaun (7565), null (7566), Tool leprechaun (7567), null (7568), Tool leprechaun (7569), Goth leprechaun (8000), null (8001) + +Base: 893 + +Base: 894 +Linked animations: [1059, 12034] +Linked graphics: 248, 2117 + +Base: 895 +Linked animations: [924] +Linked graphics: 274 + +Base: 896 +Linked animations: [1063] +Linked graphics: 252, 306 + +Base: 897 +Linked animations: [1061] +Linked graphics: 251 + +Base: 898 +Linked animations: [1065, 12018] +Linked graphics: 253, 2116 + +Base: 899 +Linked animations: [1204] +Linked graphics: 282, 283, 284, 285 + +Base: 900 +Linked animations: [658] +Linked graphics: 194 + +Base: 901 +Linked animations: [660] +Linked graphics: 196, 269, 329, 331, 429, 430, 431, 677, 1883 + +Base: 902 +Linked animations: [659] +Linked graphics: 195, 676 + +Base: 903 +Linked animations: [699] +Linked graphics: 173 + +Base: 904 +Linked animations: [700] +Linked graphics: 174 + +Base: 905 +Linked animations: [706, 1054, 1066, 1257, 1874, 5437, 9968] +Linked graphics: 80, 245, 254, 291, 348, 426, 981, 1724 + +Base: 906 + +Base: 907 +Linked animations: [1822] +Linked graphics: 345 + +Base: 908 +Linked animations: [1821] +Linked graphics: 344 + +Base: 909 +Linked animations: [681] +Linked graphics: 142 + +Base: 910 +Linked animations: [683] +Linked graphics: 144 + +Base: 911 +Linked animations: [682] +Linked graphics: 143 + +Base: 912 +Linked animations: [1447] +Linked graphics: 308 + +Base: 913 +Linked animations: [1817] +Linked graphics: 343 + +Base: 914 +Linked animations: [1813, 1814, 1815] +Linked graphics: 342 + +Base: 915 +Linked animations: [694] +Linked graphics: 167 + +Base: 916 +Linked animations: [695] +Linked graphics: 169 + +Base: 917 +Linked animations: [2558, 2559, 2560] +Linked graphics: 433, 434, 435 + +Base: 918 +Linked animations: [691] + +Base: 919 +Linked animations: [693, 12724, 12725] +Linked graphics: 71, 198, 266, 305, 359, 505, 2278, 2279, 2282 + +Base: 920 +Linked animations: [692] + +Base: 921 +Linked animations: [671, 5529] +Linked graphics: 105, 998 + +Base: 922 +Linked animations: [673] +Linked graphics: 107, 489, 492, 1725 + +Base: 923 +Linked animations: [672] +Linked graphics: 106 + +Base: 924 +Linked animations: [651] +Linked graphics: 82 + +Base: 925 +Linked animations: [2725] +Linked graphics: 455 + +Base: 926 +Linked animations: [1076] +Linked graphics: 272 + +Base: 927 +Linked animations: [1072] +Linked objects: Monument (3493), Monument (3494), Monument (3495), Monument (3496), Monument (3497), Monument (3498), Monument (3499) + +Base: 928 +Linked animations: [1073] +Linked objects: null (3434), null (3474), Golden lantern (9145), Lantern (16901), null (31775), null (31776), Lantern (34937), Lantern (34938) + +Base: 929 +Linked animations: [1079, 1080, 1081, 1082, 1083, 1619] + +Base: 930 +Linked animations: [1071] +Linked NPCs: Will o' the wisp (1043), Will o' the wisp (1211), Will o' the wisp (1212) +Linked objects: null (726) + +Base: 931 +Linked animations: [2380, 5013] +Linked objects: Light jungle (9010), Light jungle (9011), Light jungle (9012), Light jungle (9013), Light jungle (9014), Medium jungle (9015), Medium jungle (9016), Medium jungle (9017), Medium jungle (9018), Medium jungle (9019), Dense jungle (9020), Dense jungle (9021), Dense jungle (9022), Dense jungle (9023), Dense jungle (9024) + +Base: 932 +Linked animations: [2395, 2396, 2397, 2398, 2399] +Linked NPCs: Large mosquito (2493), Mosquito swarm (2494), Mosquito swarm (2495), Giant mosquito (4347) + +Base: 933 +Linked animations: [2404] +Linked graphics: 428 + +Base: 934 +Linked animations: [2381] +Linked graphics: 427 + +Base: 935 +Linked animations: [3998, 4000, 4002] +Linked graphics: 666, 667 +Linked objects: Claw (14976) + +Base: 936 +Linked animations: [4011, 4012, 4013, 4014, 4015] +Linked objects: null (15017), null (15018), null (15021) + +Base: 937 +Linked animations: [4016] + +Base: 938 +Linked animations: [4005, 4006] +Linked objects: Pinball Post (15000), Pinball Post (15001), Pinball Post (15002), Pinball Post (15003), Pinball Post (15004), Pinball Post (15005), Pinball Post (15006), Pinball Post (15007), Pinball Post (15008), Pinball Post (15009) + +Base: 939 +Linked animations: [4020] +Linked graphics: 668 + +Base: 940 +Linked animations: [4022] +Linked objects: Lift Engine (15229), null (15238) + +Base: 941 +Linked animations: [4032, 4033, 4034, 4035] +Linked graphics: 672, 673, 674, 675 + +Base: 942 +Linked animations: [4037, 4038, 4039, 4040, 4041, 4042] +Linked NPCs: Giant Sea Snake (3943) + +Base: 943 +Linked animations: [4025, 4026] +Linked graphics: 669, 670 + +Base: 944 +Linked animations: [4027] +Linked graphics: 671 + +Base: 945 + +Base: 946 +Linked animations: [4126] +Linked objects: null (15538), null (15539), Flowers (16729), Flowers (16730), Flowers (16731), null (18315) + +Base: 947 +Linked animations: [4125] +Linked objects: Oil lamp (24033), null (24414), Oil lamp (33472), Oil lamp (43171), null (44951) + +Base: 948 + +Base: 949 +Linked animations: [4134, 4135] + +Base: 950 +Linked animations: [4137, 4138] + +Base: 951 +Linked animations: [3196, 3197, 3198, 3199, 3200, 3201, 3202, 3203, 3204, 3205, 4155, 4156] + +Base: 952 +Linked animations: [4161] +Linked objects: Magical Animator (15621) + +Base: 953 +Linked animations: [4157, 4158, 4159, 4160] +Linked objects: Catapult (15617), Catapult (15618), Catapult (15619), Catapult (15620) + +Base: 954 +Linked animations: [4162, 4163, 4164] +Linked objects: Dummy (15624), Dummy (15625), Dummy (15626), Dummy (15627), Dummy (15628), Dummy (15629), Dummy (15630) + +Base: 955 +Linked animations: [4165] +Linked graphics: 679, 680, 681, 682 + +Base: 956 +Linked animations: [4186] +Linked graphics: 689 + +Base: 957 +Linked animations: [4176] +Linked graphics: 683 + +Base: 958 +Linked animations: [4187] +Linked graphics: 684, 685, 686, 687, 688 + +Base: 959 +Linked animations: [3206] +Linked objects: Crate (1), Crate (15599), Crates (15600) + +Base: 960 + +Base: 961 +Linked animations: [6094] +Linked objects: Cow wheel (12114), Cow wheel (52530) + +Base: 962 +Linked animations: [4222] + +Base: 963 +Linked animations: [4217, 4218] +Linked objects: Drip (15727), Drips (15728), Drip (23172), Drips (23173) + +Base: 964 +Linked animations: [4224] +Linked graphics: 701, 702, 703 + +Base: 965 +Linked animations: [4215] +Linked graphics: 693 + +Base: 966 +Linked animations: [4212] +Linked graphics: 695 + +Base: 967 +Linked animations: [4214] +Linked graphics: 692 + +Base: 968 +Linked animations: [4225] +Linked graphics: 704 + +Base: 969 +Linked animations: [4223] +Linked graphics: 700 + +Base: 970 +Linked animations: [4213] +Linked graphics: 691 + +Base: 971 +Linked animations: [4216] +Linked graphics: 694 + +Base: 972 +Linked animations: [4229] +Linked graphics: 705 + +Base: 973 +Linked animations: [4284] +Linked objects: Portal of Death (16043), Portal of Death (16044), Rickety door (16065), Rickety door (16066), Oozing barrier (16089), Oozing barrier (16090), Gate of War (16123), Gate of War (16124) + +Base: 974 +Linked animations: [4231, 4232, 4233, 4234, 4235, 4236, 4237] +Linked NPCs: Jungle horror (4348), Jungle horror (4349), Jungle horror (4350), Jungle horror (4351), Jungle horror (4352), Cave horror (4353), Cave horror (4354), Cave horror (4355), Cave horror (4356), Cave horror (4357) + +Base: 975 +Linked animations: [4262] + +Base: 976 +Linked animations: [4244, 4245, 4246, 4247, 4253] +Linked objects: Bitternut Tree (15962), Bitternut Tree (15963), Bitternut Tree (15964), Bitternut Tree (15965), Bitternut Tree (15966), Bitternut Tree (15967), Bitternut Tree (15968), Bitternut Tree (15969) + +Base: 977 +Linked animations: [4260] +Linked objects: Bottle machine (15923) + +Base: 978 +Linked animations: [4240, 4241] +Linked objects: Conveyor Belt (15925), Conveyor Belt (15927) + +Base: 979 +Linked animations: [4239] +Linked objects: Bamboo Pipes (15838), Bamboo Pipes (15839), Hopper (15848), Hopper (15849), Bamboo Pipes (15864), Bamboo Pipes (15865), Hopper (15874), Hopper (15875), Boiler (15905), Boiler (15908), Boiler (15911), Boiler (15914), Water Pump (15937), Red Flowers (15941), Blue Flowers (15942), Jungle Tree (15949), Jungle Tree (15952), Jungle Tree (15955), Burning tree (41857), Burning tree (41858), Burning tree (41859), null (42133), Burning tree (42134), Burning tree (42135), Volcanic habitat (44826), Burning tree (52716), Burning tree (52717) + +Base: 980 +Linked animations: [4242, 4243] +Linked objects: Steam Pump (15929) + +Base: 981 +Linked animations: [4259] +Linked NPCs: Parrot (4373), Parrot (4374) + +Base: 982 +Linked animations: [4248, 4249] +Linked graphics: 706, 707 + +Base: 983 +Linked animations: [4261] +Linked graphics: 711 + +Base: 984 +Linked animations: [4252] +Linked graphics: 709, 710 + +Base: 985 +Linked animations: [4251] +Linked graphics: 708 + +Base: 986 +Linked animations: [4274] +Linked objects: null (16032) + +Base: 987 + +Base: 988 +Linked animations: [4281] + +Base: 989 +Linked animations: [4268, 4269, 4270, 4271, 4272, 4273] +Linked NPCs: Catablepon (4397), Catablepon (4398), Catablepon (4399) + +Base: 990 +Linked animations: [4263, 4264, 4265, 4266, 4267] +Linked NPCs: Minotaur (4404), Minotaur (4405), Minotaur (4406), Minotaur (11258) + +Base: 991 +Linked animations: [4279] +Linked graphics: 713 + +Base: 992 + +Base: 993 +Linked animations: [4286] +Linked graphics: 714 + +Base: 994 +Linked animations: [4323] +Linked objects: null (16356) + +Base: 995 +Linked animations: [4324] +Linked objects: null (16357) + +Base: 996 +Linked animations: [4325] +Linked objects: null (16361), null (16362) + +Base: 997 + +Base: 998 +Linked animations: [4312, 4313, 4314, 4315] +Linked NPCs: Starflower (4451), Starflower (4452) + +Base: 999 +Linked animations: [7054] +Linked graphics: 1215 + +Base: 1000 +Linked animations: [4303, 4304, 4305, 4306, 4307, 4326, 4327] +Linked NPCs: Cosmic Being (4419) + +Base: 1001 +Linked animations: [4298, 4299, 4300, 4301, 4302] +Linked NPCs: Gorak (4418), Gorak (6218), Gorak (10207), Gorak (10208), Gorak (10209), Gorak (10210), Gorak (10211) + +Base: 1003 +Linked animations: [4318, 4319, 4320, 4321, 4322, 7051, 7052, 7058, 7059, 7060, 7061, 7062, 7063, 7064, 11293, 11298, 11301, 11357, 11373, 11399, 11400, 11401, 11402, 11406, 11407, 11408, 11409, 11410] +Linked NPCs: General Graardor (6260), Spiritual ranger (6276), Spiritual warrior (6277), Spiritual mage (6278), Ork statue (7084), Ourg statue (7086) +Linked objects: Ork statue (42589), Ork statue (42590), Ork statue (42604), Ork statue (42607) + +Base: 1004 +Linked animations: [4333] + +Base: 1005 + +Base: 1006 +Linked animations: [4339] +Linked objects: An anonymous looking door (16483), An anonymous looking door (16484), An anonymous looking door (16485), An anonymous looking door (16486), An anonymous looking door (16487), An anonymous looking door (16488), An anonymous looking door (16489), An anonymous looking door (16490), An anonymous looking door (16491), An anonymous looking door (16492), An anonymous looking door (16493), An anonymous looking door (16494) + +Base: 1007 +Linked animations: [4336, 4337] +Linked objects: Sarcophagus (16495), Sarcophagus (16496), Sarcophagus (16497), Sarcophagus (16498), Sarcophagus (16499), Sarcophagus (16500) + +Base: 1008 +Linked animations: [4338] +Linked objects: Tomb Door (16476), null (16538), null (16539), null (16540), null (16541), null (16542), null (16543), null (16544), null (16545), null (16546) + +Base: 1009 +Linked animations: [4335] +Linked objects: Urn (16501), Urn (16502), Urn (16503), Urn (16504), Urn (16505), Urn (16506), Urn (16507), Urn (16508), Urn (16509), Urn (16510), Urn (16511), Urn (16512), Urn (16513), Urn (16514), Urn (16515), Urn (16516) + +Base: 1010 +Linked animations: [4334] +Linked graphics: 715 + +Base: 1011 +Linked animations: [4346, 4347] + +Base: 1012 +Linked animations: [4351] +Linked graphics: 716, 717, 718 + +Base: 1013 +Linked animations: [4399] +Linked objects: Hanging pirate (16859) + +Base: 1014 +Linked animations: [4383, 4384, 4385, 4386, 4387, 4388, 4389] +Linked NPCs: Suqah (4527), Suqah (4528), Suqah (4529), Suqah (4530), Suqah (4531), Suqah (4532), Suqah (4533) + +Base: 1015 +Linked animations: [4408] +Linked objects: Ceremonial Brazier (16810), Ceremonial Brazier (16811), null (17025) + +Base: 1016 +Linked animations: [4377] +Linked objects: Table (16827), Table (16828) + +Base: 1017 +Linked animations: [4392] +Linked objects: Light (16701) + +Base: 1018 +Linked animations: [4393] +Linked objects: Hat rack (16691), Hat rack (16692) + +Base: 1019 +Linked animations: [4394, 4395, 4396, 4397, 4398] +Linked objects: Cooker (16812), Rack (16813), Stool (16814), Table (16815), null (16816) + +Base: 1020 +Linked animations: [4390, 4391] +Linked NPCs: House (4512) + +Base: 1021 +Linked animations: [4372] +Linked NPCs: Enchanted Broom (4521), Enchanted Broom (4522), Enchanted Broom (4523) + +Base: 1022 +Linked animations: [4373, 4374, 4375, 4376] +Linked NPCs: Enchanted Bucket (4524), Enchanted Bucket (4525) + +Base: 1023 +Linked animations: [4353] +Linked graphics: 719 + +Base: 1024 +Linked animations: [4403, 4404, 4405] + +Base: 1025 +Linked animations: [4355] +Linked objects: Dream tree (16604) + +Base: 1026 +Linked animations: [4358, 4359] +Linked objects: Platform (16632), Platform (16633), Platform (16634), Platform (16635), Platform (16636), Platform (16637), null (16830), null (16832), null (16834), null (16836), null (16838), null (16840), null (16842) + +Base: 1027 +Linked animations: [4357] +Linked objects: Hurdle (16600), Note (16601), Note (16602) + +Base: 1028 +Linked animations: [4356] +Linked objects: Zero (16619), One (16620), Two (16621), Three (16622), Four (16623), Five (16624), Six (16625), Seven (16626), Eight (16627), Nine (16628), null (29447), null (29448), null (29449), null (29450), null (29451), null (29452), null (29453), null (29454), null (29455) + +Base: 1029 +Linked animations: [4354] +Linked objects: Our lives (11398), My life (16599), Our Lives (47255) + +Base: 1030 +Linked animations: [4362, 4363, 4364] +Linked objects: Dream puff (16856), Dream puff (16857) + +Base: 1031 +Linked animations: [4360, 4361] +Linked objects: Dice (16849), Dice (16850), Dice (16851), Dice (16852), Dice (16853), Dice (16854), Dice (16855) + +Base: 1032 +Linked animations: [4422] +Linked graphics: 746 + +Base: 1033 +Linked animations: [4418, 4420, 4421] +Linked graphics: 727, 728, 729, 730, 731, 732, 733, 742, 744, 745, 748 + +Base: 1034 +Linked animations: [4414] +Linked graphics: 734, 736, 738 + +Base: 1035 +Linked animations: [4419] +Linked graphics: 722, 735, 737, 739, 740, 741, 743 + +Base: 1036 +Linked animations: [4423] +Linked graphics: 747 + +Base: 1037 +Linked animations: [4417] +Linked graphics: 725, 726 + +Base: 1038 +Linked animations: [4415, 4416] +Linked graphics: 723, 724 + +Base: 1039 +Linked animations: [4407, 4735, 4736, 4737] +Linked graphics: 721, 784, 785, 786 + +Base: 1040 + +Base: 1041 +Linked animations: [4448] +Linked graphics: 754 + +Base: 1042 +Linked animations: [4445] +Linked graphics: 751 + +Base: 1043 +Linked animations: [4451] +Linked graphics: 757 + +Base: 1044 +Linked animations: [4450] +Linked graphics: 756 + +Base: 1045 +Linked animations: [4449] +Linked graphics: 755 + +Base: 1046 +Linked animations: [4463] +Linked graphics: 759 + +Base: 1047 +Linked animations: [4453, 4461, 12437] +Linked graphics: 760, 2171 + +Base: 1048 +Linked animations: [4447] +Linked graphics: 753 + +Base: 1049 +Linked animations: [4443, 4575] +Linked graphics: 281, 749, 1715 + +Base: 1050 +Linked animations: [4446] +Linked graphics: 752 + +Base: 1051 +Linked animations: [4444] +Linked graphics: 750 + +Base: 1052 +Linked animations: [4452] +Linked graphics: 758 + +Base: 1053 +Linked animations: [4469] + +Base: 1054 +Linked animations: [4472, 4473, 4474, 4475, 4476] +Linked NPCs: null (4613), null (4616), null (4619), null (4622), null (4625), null (4628), fluffie (4631), fluffie (4632), null (6310), null (6333) + +Base: 1055 +Linked animations: [4477] +Linked objects: null (13113) + +Base: 1056 +Linked animations: [4478] +Linked graphics: 776 + +Base: 1057 +Linked animations: [4577] +Linked objects: null (17208) + +Base: 1058 +Linked animations: [4565, 4567, 4569] +Linked objects: Gnome crowd (17197), Gnome crowd (17198), Gnome crowd (17201), Gnome crowd (17202), Gnome crowd (17205), Gnome crowd (17206) + +Base: 1059 +Linked animations: [4564, 4566, 4568, 6161, 6162, 6163] +Linked objects: Goblin crowd (13115), Goblin crowd (17196), Goblin crowd (17199), Goblin crowd (17200), Goblin crowd (17203), Goblin crowd (17204), Goblin crowd (23306), Goblin crowd (23307), Goblin crowd (23308), Goblin crowd (23309), Goblin crowd (23310), Goblin crowd (23311), Goblin crowd (23312), Goblin crowd (23313), Goblin crowd (23314), null (29183), null (29184) + +Base: 1060 +Linked animations: [4559] +Linked objects: Aspidistra plant (17238) + +Base: 1061 +Linked animations: [4557] +Linked objects: Table (17235) + +Base: 1062 +Linked animations: [4563] +Linked objects: Broken cogs (17242), Cogs (17243), null (17281), Cogs (26789) + +Base: 1063 +Linked animations: [4560] +Linked objects: Oaknock's machine (17240), Oaknock's machine (17241), null (17280), Yewnock's machine (26788) + +Base: 1064 +Linked animations: [4561] +Linked objects: Oaknock's Machine (17245), Oaknock's Machine (17246), Oaknock's Machine (17247), null (17282), Yewnock's machine (26791) + +Base: 1065 +Linked animations: [4562] +Linked objects: Oaknock's exchanger (17248), Oaknock's exchanger (17249), null (17283), Yewnock's exchanger (26792) + +Base: 1066 +Linked animations: [4576] + +Base: 1067 +Linked animations: [4570, 4571, 4572, 4573] +Linked objects: Argento (17274), Argento (17279) + +Base: 1068 +Linked animations: [4584] +Linked graphics: 774 + +Base: 1069 +Linked animations: [4535] +Linked objects: Office table (17130) + +Base: 1070 +Linked animations: [4541, 4542, 6760] +Linked graphics: 765, 766, 1170 + +Base: 1071 +Linked animations: [4543, 4545, 7149, 7150] +Linked graphics: 767, 769, 1230, 1232 + +Base: 1072 +Linked animations: [4548] +Linked graphics: 768 + +Base: 1073 +Linked animations: [4554, 4555] +Linked graphics: 771, 772 + +Base: 1074 +Linked animations: [4550] +Linked graphics: 770 + +Base: 1075 +Linked animations: [4556] +Linked graphics: 773 + +Base: 1076 +Linked animations: [4574] + +Base: 1077 +Linked animations: [4595] +Linked objects: null (17324) + +Base: 1078 +Linked animations: [63, 64, 65, 66, 67, 68, 69, 4619, 4620, 4623, 4624, 4629, 4630, 4644, 4674, 4675, 4676, 4677, 4678, 4679, 4680, 4681, 12608, 12627, 12983, 13278, 13283, 13597, 13598, 13599, 13600, 13601, 13602, 13603, 13604, 13605, 13606, 13611, 13761, 13762, 13763, 14237] +Linked NPCs: Lesser demon (82), Greater demon (83), Black demon (84), Chronozon (667), Black demon (677), Delrith (879), Weakened Delrith (880), Kolodion (911), Nezikchened (934), Othainian (998), Doomion (999), Holthion (1000), Jungle demon (1472), Agrith Naar (2919), Lesser Demon Champion (3064), Demon (3593), Lesser demon (4694), Lesser demon (4695), Lesser demon (4696), Lesser demon (4697), Greater demon (4698), Greater demon (4699), Greater demon (4700), Greater demon (4701), Black demon (4702), Black demon (4703), Black demon (4704), Black demon (4705), Lesser demon (6101), Tstanon Karlak (6204), Zakl'n Gritch (6206), Balfrug Kreeyath (6208), Har'Lakk the Riftsplitter (9898), Har'Lakk the Riftsplitter (9899), Har'Lakk the Riftsplitter (9900), Har'Lakk the Riftsplitter (9901), Har'Lakk the Riftsplitter (9902), Har'Lakk the Riftsplitter (9903), Har'Lakk the Riftsplitter (9904), Har'Lakk the Riftsplitter (9905), Har'Lakk the Riftsplitter (9906), Har'Lakk the Riftsplitter (9907), Har'Lakk the Riftsplitter (9908), Har'Lakk the Riftsplitter (9909), Har'Lakk the Riftsplitter (9910), Har'Lakk the Riftsplitter (9911), To'Kash the Bloodchiller (10024), To'Kash the Bloodchiller (10025), To'Kash the Bloodchiller (10026), To'Kash the Bloodchiller (10027), To'Kash the Bloodchiller (10028), To'Kash the Bloodchiller (10029), To'Kash the Bloodchiller (10030), To'Kash the Bloodchiller (10031), To'Kash the Bloodchiller (10032), To'Kash the Bloodchiller (10033), To'Kash the Bloodchiller (10034), To'Kash the Bloodchiller (10035), To'Kash the Bloodchiller (10036), To'Kash the Bloodchiller (10037), To'Kash the Bloodchiller (10038), To'Kash the Bloodchiller (10039), Bal'lak the Pummeller (10128), Bal'lak the Pummeller (10129), Bal'lak the Pummeller (10130), Bal'lak the Pummeller (10131), Bal'lak the Pummeller (10132), Bal'lak the Pummeller (10133), Bal'lak the Pummeller (10134), Bal'lak the Pummeller (10135), Bal'lak the Pummeller (10136), Bal'lak the Pummeller (10137), Bal'lak the Pummeller (10138), Bal'lak the Pummeller (10139), Bal'lak the Pummeller (10140), Bal'lak the Pummeller (10141), Lesser demon (10492), Lesser demon (10493), Lesser demon (10494), Lesser demon (10495), Greater demon (10718), Greater demon (10719), Greater demon (10720), Greater demon (10721), Black demon (10722), Black demon (10723), Black demon (10724), Black demon (10725), Pit black demon (11566), Pit black demon (11592), Pit black demon (11593) +Linked objects: Pit black demon (39264), Pit black demon (41607) + +Base: 1079 +Linked animations: [4631] +Linked graphics: 497 + +Base: 1080 +Linked animations: [21, 25, 26, 27, 28, 79, 80, 81, 82, 83, 84, 89, 90, 91, 92, 2265, 2266, 4635, 4638, 4642, 6446, 6447, 6496, 6642, 6643, 6722, 6825, 8342, 9912, 9913, 9914, 9915, 9916, 9917, 9918, 9919, 9920, 9921, 9922, 9923, 9924, 9925, 9926, 9927, 9928, 9929, 9930, 9931, 9932, 9933, 9934, 10939] +Linked NPCs: King Black Dragon (50), King Black Dragon (2642), Baby red dragon (3588), Steel dragon (3590), Hatchling dragon (6900), Baby dragon (6901), Hatchling dragon (6902), Baby dragon (6903), Hatchling dragon (6904), Baby dragon (6905), Hatchling dragon (6906), Baby dragon (6907), Dragon (7943), Dragon (7944), Dragon (7945), Dragon (7946), Dragon (7947), Dragon (7948), Baby dragon (9208), Baby dragon (9209), Baby dragon (9210), Baby dragon (9211) +Linked objects: Baby red dragon (13372), Steel dragon (13375) + +Base: 1081 +Linked animations: [4649, 4650, 4651, 4652, 4653, 4654, 4655, 4656, 4657, 4658, 4659, 4660, 4661, 4662, 4663, 4664, 4665, 4666, 4667, 4668, 4669, 4670, 4671, 4672, 4673, 6368, 6369, 6370, 6371, 6372] +Linked NPCs: Fire giant (110), Ice giant (111), Moss giant (112), Cyclops (116), Hill Giant (117), Fire giant (1582), Fire giant (1583), Fire giant (1584), Fire giant (1585), Fire giant (1586), Moss giant (1587), Moss giant (1588), Moss giant (1681), Giant Champion (3058), Ice giant (3072), Cyclops (4291), Cyclops (4292), Moss giant (4534), Ice giant (4685), Ice giant (4686), Ice giant (4687), Moss giant (4688), Hill Giant (4689), Hill Giant (4690), Hill Giant (4691), Hill Giant (4692), Hill Giant (4693), Moss giant (4706), Cyclops (6078), Cyclops (6079), Cyclops (6080), Cyclops (6081), Cyclops (6269), Cyclops (6270), Fire giant (7003), Fire giant (7004), Hill giant (10706), Hill giant (10707), Hill giant (10708), Hill giant (10709), Hill giant (10710), Hill giant (10711), Hill giant (10712), Hill giant (10713), Hill giant (10714), Hill giant (10715), Hill giant (10716), Hill giant (10717), Fire giant (10754), Fire giant (10755), Fire giant (10756), Fire giant (10757), Fire giant (10758), Fire giant (10759), Fire giant (10760), Fire giant (10761), Ice giant (10762), Ice giant (10763), Ice giant (10764), Ice giant (10765), Ice giant (10766), Ice giant (10767), Ice giant (10768), Ice giant (10769), Fire giant (11622) + +Base: 1082 + +Base: 1083 +Linked animations: [4621, 4622, 12609] +Linked objects: Stone table (17436), Stone table (17437) + +Base: 1084 +Linked animations: [4599, 4600] +Linked objects: Wardrobe (17434) + +Base: 1085 +Linked animations: [4601] +Linked graphics: 777 + +Base: 1086 + +Base: 1087 + +Base: 1088 + +Base: 1089 + +Base: 1090 +Linked animations: [86, 1990, 2264] +Linked graphics: 1, 393, 394, 395, 396, 440, 498, 1718 + +Base: 1091 +Linked animations: [4755, 4756] +Linked NPCs: Boat (4770), Boat (4771) + +Base: 1092 +Linked animations: [4744] +Linked objects: null (17632), Wall (18075), Falling door (18102), null (18128), null (18134) + +Base: 1093 +Linked animations: [4745] +Linked objects: Wall (18080), null (18131) + +Base: 1094 +Linked animations: [4743] +Linked objects: Decorated wall (18145) + +Base: 1095 +Linked animations: [4746] +Linked objects: Waste pipes (17509) + +Base: 1096 +Linked animations: [4747] +Linked objects: Waste pipes (17511) + +Base: 1097 +Linked animations: [4683, 4685, 4687, 4689, 4691, 4693, 4695, 4697, 4706, 9106, 9107, 9108, 9112, 9113, 9114, 9115, 9116, 9117] +Linked NPCs: Flying female vampire (4809), Flying female vampire (4810), Flying female vampire (4811), Flying female vampire (4812), Vyrewatch (4813), Vyrewatch (4814), Vyrewatch (4815), Vyrewatch (4816), Vyrewatch (4821), Vyrewatch (4822), Vyrewatch (4823), Vyrewatch (4824), Vyrewatch (4825), Vyrewatch (4826), Vyrewatch (4827), Vyrewatch (4828), Vyrewatch (4829), Vyrewatch (4830), Vyrewatch (4831), Vyrewatch (4832), Vyrewatch (4833), Vyrewatch (4834), Vyrewatch (4835), Vyrewatch (4836), Flying female vampire (4845), Flying female vampire (4846), Flying female vampire (4847), Flying female vampire (4848), Vyrewatch (4849), Vyrewatch (4850), Vyrewatch (4851), Vyrewatch (4852), Flying female vampire (7610), Flying female vampire (7611), Flying female vampire (7612), Flying female vampire (7613), Vyrewatch (7614), Vyrewatch (7615), Vyrewatch (7616), Vyrewatch (7617), Vyrewatch (7622), Vyrewatch (7623), Vyrewatch (7624), Vyrewatch (7625), Vyrewatch (7626), Vyrewatch (7627), Vyrewatch (7628), Vyrewatch (7629), Vyrewatch (7630), Vyrewatch (7631), Vyrewatch (7632), Vyrewatch (7633), Vyrewatch (7634), Vyrewatch (7635), Vyrewatch (7682), Vyrewatch (7683), Vyrewatch (7684), Vyrewatch (7685), Vyrewatch (7691), Vyrewatch (7692), Vyrewatch (7693), Vyrewatch (7694), Vyrewatch (7695), Vyrewatch (7696), Vyrewatch (7697), Vyrewatch (7698), Vyrewatch (7699), Vyrewatch (7700), Vyrewatch (7701), Vyrewatch (7702), Vyrewatch (7703), Vyrewatch (7704), Vyrewatch (7705), Vyrewatch (7706) + +Base: 1098 +Linked animations: [4699, 4700, 4701, 4702, 4703, 4704, 4705, 4708, 4711, 4714, 4715, 4716, 4717, 4725, 4729, 4732, 4733, 4734, 4767, 4768, 4769, 4770, 9020, 9118] +Linked NPCs: Juvinate guard (4772), Juvinate guard (4773), Vampyre juvenile (4774), Vampyre juvenile (4775), Vampyre juvinate (4776), Vampyre juvinate (4777), Held vampyre juvenile (4778), Held vampyre juvenile (4779), Held vampyre juvinate (4780), Held vampyre juvinate (4781), Vampyre juvinate (4782), Vanstrom Klause (4791), Vanstrom Klause (4792), Vanstrom Klause (4793), Vanescula Drakan (4797), Vanescula Drakan (4798), Ranis Drakan (4801), Ranis Drakan (4802), Vyrewatch (4805), Vyrewatch (4806), Vyrewatch (4807), Vyrewatch (4808), Vyrewatch (4817), Vyrewatch (4818), Vyrewatch (4819), Vyrewatch (4820), Vyrewatch (4837), Vyrewatch (4838), Vyrewatch (4839), Vyrewatch (4840), Vyrewatch (4841), Vyrewatch (4842), Vyrewatch (4843), Vyrewatch (4844), Vanstrom Klause (4862), Vyrewatch (7606), Vyrewatch (7607), Vyrewatch (7608), Vyrewatch (7609), Vyrewatch (7618), Vyrewatch (7619), Vyrewatch (7620), Vyrewatch (7621) + +Base: 1099 +Linked animations: [4684, 4686, 4688, 4690, 4692, 4694, 4696, 4698, 4707] +Linked NPCs: Vanstrom Klause (4794), Vanstrom Klause (4795), Vanstrom Klause (4796), Vanescula Drakan (4799), Vanescula Drakan (4800), Ranis Drakan (4803), Ranis Drakan (4804) + +Base: 1100 +Linked animations: [4753, 4754] +Linked NPCs: null (4765), null (5562) + +Base: 1101 +Linked animations: [4738, 4739, 4740, 4741, 4742] +Linked NPCs: Shanty Claws (828) + +Base: 1102 +Linked animations: [4730] +Linked graphics: 787 + +Base: 1103 +Linked animations: [4751, 4752] + +Base: 1104 +Linked animations: [4748, 4749, 9026, 9027] + +Base: 1105 +Linked animations: [4773, 4774, 4775, 4776, 4777] +Linked NPCs: Stray dog (4766), Stray dog (4767), Stray dog (5917), Stray dog (5918) + +Base: 1107 +Linked animations: [6123] +Linked objects: null (23215) + +Base: 1108 +Linked animations: [4782] +Linked objects: null (25885) + +Base: 1109 +Linked animations: [4781] + +Base: 1110 +Linked animations: [4783] +Linked objects: null (7173), null (18358) + +Base: 1111 +Linked animations: [4837] +Linked graphics: 798 + +Base: 1112 +Linked animations: [4822, 4823] + +Base: 1113 +Linked animations: [4811, 4812, 4813, 4814, 4815, 4816, 4817, 4818] + +Base: 1114 +Linked animations: [4821, 4828, 4829, 4830, 4831, 4832] +Linked NPCs: Slug Prince (4890) +Linked graphics: 796 + +Base: 1115 +Linked animations: [4819, 4820, 4842] +Linked NPCs: Mother Mallum (4889) + +Base: 1116 +Linked animations: [4827] +Linked graphics: 792, 795 + +Base: 1117 +Linked animations: [4840] +Linked graphics: 799 + +Base: 1118 +Linked animations: [4836] +Linked graphics: 797 + +Base: 1119 +Linked animations: [4799, 4800, 4801, 4802, 4803] + +Base: 1120 +Linked animations: [4796, 4798] +Linked objects: Crane (18326) + +Base: 1121 + +Base: 1122 +Linked animations: [4810] +Linked graphics: 791 + +Base: 1123 +Linked animations: [4808] +Linked graphics: 790 + +Base: 1124 +Linked animations: [4843, 4844, 4845, 4846, 8388, 8389, 8390, 8391, 8392, 8393, 8394, 8403, 8404, 8405, 8406, 8898, 8899, 8900, 10555] + +Base: 1125 + +Base: 1126 + +Base: 1127 + +Base: 1128 + +Base: 1129 +Linked animations: [4860] +Linked objects: null (3410), null (11669), null (12101), null (18524), null (21942) + +Base: 1130 +Linked animations: [4864, 4865, 4866, 4867, 4868, 4869, 4870] +Linked NPCs: Earth elemental (1020), Earth elemental (4910), Elemental rock (4911) + +Base: 1131 +Linked animations: [4912] + +Base: 1132 +Linked animations: [4887, 4888, 4889, 4890] + +Base: 1133 +Linked animations: [4899] +Linked objects: null (18689) + +Base: 1134 +Linked animations: [4960] +Linked graphics: 823 + +Base: 1135 +Linked animations: [4897, 4900] +Linked objects: Extractor gun (18694), Extractor gun (18695), null (18725) + +Base: 1136 +Linked animations: [4882, 4883] +Linked graphics: 807 +Linked objects: Extractor hat (18690) + +Base: 1137 +Linked animations: [4898] +Linked objects: null (18723) + +Base: 1139 +Linked animations: [4908] + +Base: 1140 +Linked animations: [4962] +Linked graphics: 824 + +Base: 1141 +Linked animations: [4894] +Linked objects: Press support (18642) + +Base: 1142 +Linked animations: [14243] +Linked graphics: 2745, 2746 + +Base: 1143 +Linked animations: [1050] +Linked graphics: 813 + +Base: 1144 +Linked animations: [4895, 4902, 4903] +Linked objects: Water Tank (18661) + +Base: 1145 +Linked animations: [4875, 4876, 4877, 4878, 4879, 4880, 4881] +Linked objects: Door (18652), Door (18653), Door (18654), Door (18655), Door (18656), Door (18657), Door (18658), Door (18659) + +Base: 1146 +Linked animations: [4896, 4906, 4907] +Linked objects: Cog (18668), Cog (18672), Cog (18674) + +Base: 1147 +Linked animations: [4966] +Linked graphics: 826 + +Base: 1148 +Linked animations: [4901] +Linked objects: Tunnel wall (18760) + +Base: 1149 +Linked animations: [4886] +Linked graphics: 808 + +Base: 1150 +Linked animations: [4893] +Linked graphics: 810, 811, 2686 + +Base: 1151 +Linked animations: [4919, 4920, 4921, 4922, 4923, 4924, 4925, 4926, 4927, 4928, 4929, 4930, 5719, 5720, 8522, 8523, 8524, 8566, 10234, 10242] +Linked NPCs: Grizzly bear (105), Black bear (106), Witch's experiment (third form) (899), Grizzly bear (1195), Grizzly bear cub (1196), Grizzly bear cub (1197), Bear Cub (1326), Bear Cub (1327), Angry bear (3645), Angry bear (3664), Arctic bear (6839), Arctic bear (6840), null (8076), null (8677), Chuck (8678), Chuck (12188) + +Base: 1152 +Linked animations: [4931, 4932, 4933, 4934, 4935, 7904, 7905, 7906, 7907, 8545] +Linked NPCs: Giant rat (86), Giant rat (87), Dungeon rat (88), Dungeon rat (224), Giant rat (446), Giant rat (950), Blessed giant rat (978), Giant crypt rat (2033), Giant rat (2091), Angry giant rat (3647), Angry giant rat (3662), Giant rat (4395), Giant crypt rat (4920), Giant crypt rat (4921), Giant rat (4922), Giant rat (4923), Giant rat (4924), Giant rat (4925), Giant rat (4926), Giant rat (4927), Dungeon rat (4928), Dungeon rat (4929), Dungeon rat (4936), Dungeon rat (4937), Angry giant rat (4938), Angry giant rat (4939), Angry giant rat (4940), Blessed giant rat (4941), Giant rat (4942), Giant rat (4943), Giant rat (4944), Giant rat (4945), Zombie rat (6088), Zombie rat (6089), Zombie rat (6090), Albino rat (6847), Albino rat (6848), Rat (7202), Rat (7204), Rat (7461), Warped rat (7920), Giant rat (8828), Giant rat (8829), Albino rat (9472), Giant rat (10480), Giant rat (10481), Giant rat (10482), Giant rat (10483), Giant rat (10484), Giant rat (10485), Giant rat (10486) + +Base: 1153 +Linked animations: [4913, 4914, 4915, 4916, 4917, 4918, 8271, 8272, 8273, 8274, 8275, 8276, 8277, 8279, 8320, 8321, 8563, 12459, 12460, 12461, 12462] +Linked NPCs: Giant bat (78), Death wing (79), Bat (412), Giant bat (1005), Giant bat (2482), Albino bat (4345), Fruit bat (6817), Vampire bat (6835), Vampire bat (6836), Warped bat (7922), Vampire bat (9474), Bat (10906), Bat (10907), Bat (10908), Bat (10909), Giant bat (10910), Giant bat (10911), Giant bat (10912), Giant bat (10913), Giant bat (10914) + +Base: 1154 +Linked animations: [4948] +Linked graphics: 817 + +Base: 1155 +Linked animations: [4978] +Linked graphics: 830 + +Base: 1156 + +Base: 1157 +Linked animations: [4954] +Linked graphics: 820 + +Base: 1158 +Linked animations: [1043, 4956] +Linked graphics: 821, 2684 + +Base: 1159 +Linked animations: [4950] +Linked graphics: 818 + +Base: 1160 + +Base: 1161 +Linked animations: [4964] +Linked graphics: 825 + +Base: 1162 +Linked animations: [4976] +Linked graphics: 831 + +Base: 1163 +Linked animations: [4952] +Linked graphics: 819 + +Base: 1164 +Linked animations: [4938] +Linked graphics: 812 + +Base: 1165 +Linked animations: [4970] +Linked graphics: 835 + +Base: 1166 + +Base: 1167 + +Base: 1168 +Linked animations: [4942] +Linked graphics: 814 + +Base: 1169 +Linked animations: [4980] +Linked graphics: 829 + +Base: 1170 +Linked animations: [4946] +Linked graphics: 816 + +Base: 1171 +Linked animations: [4974] +Linked graphics: 832 + +Base: 1172 +Linked animations: [2130, 4871, 4872, 4968] +Linked NPCs: Ballista (11450), Ballista (11451) + +Base: 1173 +Linked animations: [4944] +Linked graphics: 815 + +Base: 1174 +Linked animations: [4982] +Linked graphics: 828 + +Base: 1175 + +Base: 1176 +Linked animations: [4958, 10484] +Linked graphics: 822 + +Base: 1177 +Linked animations: [5012] +Linked objects: Dolphin (18841), Fish (18842) + +Base: 1178 +Linked animations: [5021, 5022, 5023, 5024, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, 5033] +Linked NPCs: Baby Roc (4971), Giant Roc (4972) + +Base: 1179 +Linked animations: [5019, 5020] +Linked graphics: 851, 852 + +Base: 1180 +Linked animations: [5014] +Linked graphics: 849 + +Base: 1181 +Linked animations: [5004] +Linked graphics: 841 + +Base: 1182 +Linked animations: [5010, 5011] +Linked graphics: 846, 847 + +Base: 1183 +Linked animations: [5003] +Linked graphics: 840 + +Base: 1184 +Linked animations: [4984] +Linked graphics: 848 + +Base: 1185 +Linked animations: [5005, 5007, 5008, 5009] +Linked graphics: 842, 843, 844, 845 + +Base: 1186 +Linked animations: [5035, 5036] +Linked graphics: 856, 857 + +Base: 1187 +Linked animations: [5034] +Linked graphics: 855 + +Base: 1188 +Linked animations: [5421] + +Base: 1189 +Linked animations: [5415, 8845] +Linked objects: Lava crater (20232), Poison crater (20233), Lava crater (20234), Poison crater (20235), null (26604), null (26605), null (26829), null (26830), Lava crater (28220), null (29271), null (29272), Steam vent (29741), Steam vent (29742), Volcanic habitat (44827) + +Base: 1190 +Linked animations: [5430] +Linked objects: Egg launcher (20133) + +Base: 1191 +Linked animations: [5431, 5432] +Linked objects: Egg hopper (20264), Egg hopper (20265), Egg hopper (20266), null (20267) + +Base: 1192 +Linked animations: [5424, 5425, 5426] +Linked NPCs: Egg launcher (5028) + +Base: 1193 +Linked animations: [5423] +Linked objects: Horn of glory (20247) + +Base: 1194 +Linked animations: [5076] + +Base: 1195 +Linked animations: [5077] + +Base: 1196 + +Base: 1197 + +Base: 1198 +Linked animations: [5420] + +Base: 1199 +Linked animations: [5055] +Linked objects: Cactus (18875) + +Base: 1200 +Linked animations: [5053] + +Base: 1201 +Linked animations: [5044, 5045] +Linked NPCs: Cart Camel (4979) + +Base: 1202 + +Base: 1203 +Linked animations: [5181, 5182, 5183, 5184, 5185] +Linked NPCs: Chinchompa (5079), Carnivorous chinchompa (5080) + +Base: 1204 + +Base: 1205 +Linked animations: [5187, 5188, 5189, 5190, 5191, 5192] +Linked NPCs: Ferret (5081), null (5139) + +Base: 1206 +Linked animations: [5225, 5226, 5227, 5228, 5229, 5230, 5231, 5232, 5233, 5234, 5235, 7014, 7015, 7016, 7017, 7018, 7019, 7909, 7910, 7911, 7912, 7913, 7914, 7915, 7916, 7918, 7919, 8917, 12088, 12823] +Linked NPCs: Sabre-toothed kyatt (5103), Spined larupia (5104), Horned graahk (5105), Growler (6250), Dawg (7104), Spirit larupia (7337), Spirit larupia (7338), Spirit graahk (7363), Spirit graahk (7364), Spirit kyatt (7365), Spirit kyatt (7366), Spirit kyatt (9478), Spirit larupia (9479), Rogue spirit larupia (9583), Rogue spirit larupia (9584), Rogue spirit larupia (9585), Rogue spirit larupia (9586) +Linked graphics: 937, 938, 939, 940, 941, 942, 1365, 1366, 1367, 1369 +Linked objects: null (19253), null (19254), null (19255), null (19256), null (19257), null (19258), null (19259), null (19260), null (19261), null (19262), null (19263), null (19264), null (19265), null (19266), null (19267), null (19268), null (43614) + +Base: 1207 +Linked animations: [5279, 5280, 5281, 5282] +Linked NPCs: Desert eagle (5130), Jungle eagle (5131), Polar eagle (5132), Eagle (5133), null (5134), null (5135), null (5136), Karamjan Jungle eagle (6385), null (6387) + +Base: 1208 +Linked animations: [5204, 5272, 5273, 5275, 5276, 5277, 6110, 8689] +Linked NPCs: Chocolate kebbit (3686), Chocolate kebbit (3687), Prickly kebbit (5086), Sabre-toothed kebbit (5087), Barb-tailed kebbit (5088), Wild kebbit (5089), Spotted kebbit (5098), Dark kebbit (5099), Dashing kebbit (5100), Diseased kebbit (7039), null (7040), null (7041), null (7042), null (7043) +Linked graphics: 928, 929, 930, 1046, 1047 + +Base: 1209 +Linked animations: [5262, 5263] +Linked NPCs: Orange salamander (5114), Red salamander (5115), Black salamander (5116), Swamp lizard (5117) + +Base: 1210 +Linked animations: [5265] +Linked graphics: 954 + +Base: 1211 +Linked animations: [5264] +Linked graphics: 952, 953 + +Base: 1212 + +Base: 1213 +Linked animations: [5081, 5214] +Linked graphics: 910, 911, 912, 913, 914, 915, 916, 917, 2299, 2300, 2301, 2302 + +Base: 1214 +Linked animations: [5186] +Linked graphics: 908, 909 + +Base: 1215 + +Base: 1216 + +Base: 1217 +Linked animations: [5110, 5111, 5112, 5113, 5114, 5115, 5116, 5117, 5118, 5119, 5120, 5121, 5122, 5123, 5124, 5125, 5126, 5127, 5128, 5129, 5130, 5131, 5132, 5133, 5134, 5135, 5136, 5137, 5138, 5139] + +Base: 1218 +Linked animations: [5141] +Linked objects: Basket (19128), Basket (19129), Air balloon (19130), null (19133), null (19134), null (19135), null (19136), null (19137), null (19138), null (19139), null (19140), null (19141), null (19142), null (19143), null (19144) + +Base: 1219 +Linked animations: [5109] +Linked objects: Basket (19156), Basket (19164) + +Base: 1220 +Linked animations: [5157] + +Base: 1221 +Linked animations: [5154] + +Base: 1222 +Linked animations: [5143, 5144] +Linked graphics: 877, 878, 880, 881, 883, 884, 886, 887, 889, 890, 892, 893, 895, 896, 898, 899, 901, 902, 904, 905 + +Base: 1223 +Linked animations: [5145, 5156] +Linked graphics: 879, 882, 885, 888, 891, 894, 897, 900, 903, 906 + +Base: 1224 +Linked animations: [5223, 5224] +Linked graphics: 933, 934, 935, 936 + +Base: 1225 +Linked animations: [5169, 5170] +Linked objects: Bird snare (19174), Bird snare (19176) + +Base: 1226 +Linked animations: [12115] + +Base: 1227 +Linked animations: [5260, 5261] +Linked objects: null (19441), null (19442), null (19443), null (19444), null (19445), null (19446), null (19447), null (19448), null (19449), null (19450), null (19451), null (19452), null (19453), null (19454), null (19455), null (19456), null (19457), null (19458), null (19459), null (19460), null (19461), null (19462), null (19463), null (19464), null (19465), null (19466), null (19467), null (19468), null (19469), null (19470), null (19471), null (19472), null (19473), null (19474), null (19475), null (19476), null (19477), null (19478), null (19479), null (19480), null (19481), null (19482), null (19483), null (19484), null (19485), null (19486), null (19487), null (19488), null (19489), null (19490), null (19491), null (19495), null (19496), null (19497), null (19498), null (19499), null (19500), null (19501), null (19502), null (19503), null (19505), null (19506), null (19508), null (19509), null (19511), null (19512), null (19514), null (19515), null (19517), null (19518), null (19520), null (19521), null (19523), null (19524), null (19526), null (19527), null (19529), null (19530), null (19532), null (19533), null (19535), null (19536), null (19538), null (19539), null (19541), null (19542), null (19544), null (19545), null (19547), null (19548), null (19550), null (19551), null (19554), null (19555), null (19556), null (19557), null (19558), null (19559), null (19560), null (19561), null (19562), null (19564), null (19565), null (19567), null (19568), null (19570), null (19571), null (19573), null (19574), null (19576), null (19577), null (19579), null (19580), null (19582), null (19583), null (19585), null (19586), null (19588), null (19589), null (19591), null (19592), null (19642), null (19643), null (19644), null (19645), null (19646), null (19647), null (19648), null (19649), null (19951), null (19952), null (19953), null (43280), null (43281), null (43282), null (43283), null (43284), null (43285), null (43286), null (43287), null (43288), null (43289), null (43290), null (43291), null (43292), null (43293), null (43294), null (43295), null (43296), null (43297), null (43298), null (43299), null (43300), null (43301), null (43302), null (43303), null (43304), null (43305), null (43306), null (43307), null (43308), null (43309), null (43310), null (43311), null (43312), null (43313), null (43314), null (43315), null (43316), null (43317), null (43318), null (43319), null (43320), null (43321), null (43322), null (43323), null (43324), null (43325), null (43326), null (43327), null (43328), null (43329), null (43330), null (43331), null (43332), null (43333), null (43334), null (43335), null (43336), null (43337), null (43338), null (43339), null (43340), null (43341), null (43342), null (43343), null (43344), null (43345), null (43346), null (43347), null (43348), null (43349), null (43350), null (43351), null (43352), null (43353), null (43354), null (43355), null (43356), null (43357), null (43358), null (43359), null (43360), null (43361), null (43362), null (43363), null (43364), null (43365), null (43366), null (43367), null (43368), null (43369) + +Base: 1228 +Linked animations: [5219, 5220, 5221, 5222] +Linked objects: Magic box (19223), Magic box failed (19224), Magic box (19225), Magic box (19226) + +Base: 1229 +Linked animations: [5278, 5770] +Linked NPCs: Iceberg (5467), Iceberg (5468) + +Base: 1230 + +Base: 1231 +Linked animations: [5175, 5176, 5177, 5178, 5179, 11771, 11772, 11773, 11774, 11775, 11776, 11777, 11778] +Linked objects: Box trap (19188), Shaking box (19189), Shaking box (19190), Shaking box (19191), Box trap (19193), Box trap (19194), Box trap (19195), Box trap (19196), Box trap (19197), Box trap (19198), Box trap (19199), Box trap (19200), Box trap (19201), Box trap (19202), Box trap (19203), Box trap (19204), Shaking box (28557), Shaking box (28558), Shaking box (28567), Box trap (28568), Box trap (28569), Box trap (28570), Box trap (28571), Box trap (28754), Box trap (28755), Box trap (28756), Box trap (28757), Box trap (28758), Box trap (28759), Box trap (28760), Box trap (28777), Shaking box (28906), Box trap (28908), Box trap (28909), Box trap (28910), Box trap (28911), Shaking box (28913), Box trap (28915), Box trap (28916), Box trap (28917), Box trap (28918), Shaking box (28920), Shaking box (28921), Shaking box (28922), Shaking box (28923), Shaking box (28924), Shaking box (28925), Shaking box (28926), Shaking box (28927), Shaking box (28928), Shaking box (28929), Box trap (29185), Box trap (29186), Box trap (29187), Box trap (29188), Box trap (29189), Box trap (29190), Box trap (29191), Box trap (29192), Shaking box (29887), Shaking box (29888), Box trap (29889), Box trap (29890), Box trap (29891), Box trap (29892), Box trap (29893), Box trap (29894), Box trap (29895), Box trap (29896), Shaking box (29897), Shaking box (29898), Shaking box (29899), Shaking box (29900), Shaking box (29901), Shaking box (29902), Shaking box (29903), Shaking box (29904), Shaking box (29905), Box trap (29906), Box trap (29907), Box trap (29908), Box trap (29909), Shaking box (29910), Shaking box (29911), Shaking box (29912), Shaking box (29913), Box trap (29914), Box trap (29915), Box trap (29916), Box trap (29917), Box trap (29918), Box trap (29919), Box trap (29920), Box trap (29921), Box trap (29922), Box trap (29923), Box trap (29924), Box trap (29925), Box trap (29926), Box trap (29927), Box trap (29928), Box trap (29929), Box trap (29960), Box trap (29961), Box trap (29962), Box trap (29963), Box trap (29964), Box trap (29965), Box trap (29966), Box trap (29967), Box trap (29968), Box trap (29969), Box trap (29970), Box trap (29971), Box trap (29972), Box trap (29973), Box trap (29974), Box trap (29975), Box trap (29976), Box trap (29977), Box trap (29978), Box trap (29979), Box trap (29980), Box trap (29981), Box trap (29982), Box trap (29983), Box trap (29984), Box trap (29985), Box trap (29986), Box trap (29987), Box trap (29988), Box trap (29989), Box trap (29990), Box trap (29991), Shaking box (43470), Shaking box (43471), Shaking box (43472), Shaking box (43473), Box trap (43474), Box trap (43475), Box trap (43476), Box trap (43477) + +Base: 1232 +Linked animations: [5193, 5194, 5195, 5196, 5197, 5274, 5664, 5679, 5683, 5686, 5687, 5711] +Linked graphics: 947, 948, 949, 950, 951 +Linked objects: Boulder (19205), Deadfall (19206), Deadfall (19207), Deadfall (19208), Deadfall (19209), Deadfall (19210), Deadfall (19211), Deadfall (19212), Deadfall (19213), Deadfall (19214), Boulder (19215), Boulder (19216), Boulder (19217), Boulder (19218), Deadfall (19219), Boulder (19851), Boulder (28935), Boulder (28936), Deadfall trap (28937), Deadfall trap (28938), Deadfall trap (28939), Deadfall trap (28940), Boulder (28941), Boulder (28942), Boulder (28943), Boulder (28944), Boulder (28945), Deadfall (29161), Deadfall (29162), Boulder (29163), Boulder (43462), Boulder (43463), Deadfall (43464), Deadfall (43465), Deadfall (43466), Deadfall (43467), Boulder (43468), Boulder (43469) + +Base: 1233 + +Base: 1234 + +Base: 1235 + +Base: 1236 +Linked animations: [5266, 5267, 5268, 5269, 5270, 5271] +Linked objects: Young tree (19650), Net trap (19654), Net trap (19655), Net trap (19656), Net trap (19657), Net trap (19658), Net trap (19659), Net trap (19660), Net trap (19661), Young tree (19662), Net trap (19666), Net trap (19667), Net trap (19668), Net trap (19669), Young tree (19670), Net trap (19674), Net trap (19675), Net trap (19676), Net trap (19677), Young tree (19678), Net trap (28559), Net trap (28560), Net trap (28561), Net trap (28562), Young tree (28563), Net trap (28750), Net trap (28751), Net trap (28752), Net trap (28753), Net trap (29930), Net trap (29931), Net trap (29932), Net trap (29933), Net trap (29934), Net trap (29935), Net trap (29936), Net trap (29937), Net trap (43478), Net trap (43479), Net trap (43480), Net trap (43481), Old fishing rod (43482) + +Base: 1237 + +Base: 1238 +Linked animations: [4133, 5198, 5199, 5200, 5201, 5202, 5203, 5283, 5284] +Linked NPCs: Gyr Falcon (5094), Gyr Falcon (5095), Gyr Falcon (5096), Gyr Falcon (5097) +Linked graphics: 918, 919, 920, 921, 922, 923, 924, 925, 926, 927 +Linked objects: Pigeon (15486), Occupied perch (19221) + +Base: 1239 +Linked animations: [5159] +Linked graphics: 907 + +Base: 1240 +Linked animations: [168, 169, 170, 171, 172, 2954, 2955, 4287, 4288, 4289, 4616, 4618, 4626, 4627, 4648, 5218, 5285, 6628, 6629, 6630, 6826, 6986, 7574, 11059, 11903] +Linked NPCs: Imp (708), Imp (709), Imp (1531), Imp Champion (3062), null (6069), Imp defender (6074), Dunce (6134), Imp (6211), Snow imp (6732), Snow imp (6733), Snow imp (6734), Snow imp (6735), Snow imp (6736), Snow imp (6737), Snow imp (6738), Snow imp (6739), Marvin (8512), Marius (8513), Benny (8514), Zimberfizz (8526), null (8527), Zimberfizz (8533), Zimberfizz (8534), Snow imp (8536), Snow imp (8537), Snow imp (8538), Head snow imp (8541), Head snow imp (8542), Head snow imp (8543), Zimberfizz (8592), null (8609), Zimberfizz (8610), null (8611), null (8612), Zimberfizz (8613), Snow imp (9364), null (9372), null (9373), null (9374), null (9375), Snow imp (9376), Snow imp (9377), Snow imp (9378), Snow imp (9379), Imp (11605) +Linked graphics: 932 + +Base: 1241 +Linked animations: [5286, 5477, 7080] +Linked graphics: 697, 955, 993, 1226 + +Base: 1242 +Linked animations: [5306, 5307, 5308] +Linked objects: Bird feeder (19936), Bird feeder (19937), Bird feeder (19938), Bird feeder (19939), Bird feeder (19940), Bird feeder (19941), Bird feeder (19942), Bird feeder (19943), Bird feeder (19944) + +Base: 1243 +Linked animations: [5295, 5296, 5297] +Linked objects: Pedestal (19981), Pedestal (19982), Pedestal (19983) + +Base: 1244 +Linked animations: [5309, 5310] +Linked NPCs: Boulder (5140) + +Base: 1245 +Linked animations: [5287, 5289, 5290, 5291, 5292] +Linked graphics: 956, 957, 958, 959, 961, 962, 963, 964 + +Base: 1246 +Linked animations: [5294, 5301, 5302, 5303, 5304, 5305] +Linked NPCs: Kebbit (5137) +Linked graphics: 965, 966 + +Base: 1247 +Linked animations: [5334, 5335, 5336, 5337, 5338, 5339, 5340, 5341, 5342, 5343, 5344, 5345, 5346] +Linked NPCs: Sheep (42), Sheep (43), null (292), null (293), null (294), null (295), Billy (1093), Mountain goat (1094), Mountain goat (1140), Mountain goat (1141), Golden sheep (1271), Golden sheep (1272), Sheep (1529), Sheep (1762), Sheep (1763), Sheep (1764), Sheep (1765), Mountain Goat (1819), Mountain Goat (1820), Sick-looking sheep (1) (2345), Sick-looking sheep (2) (2346), Sick-looking sheep (3) (2347), Sick-looking sheep (4) (2348), Sick-looking sheep (1) (2377), Sick-looking sheep (2) (2378), Sick-looking sheep (3) (2379), Sick-looking sheep (4) (2380), Sheep (3310), Sheep (3311), Ram (3672), Ram (3673), null (4253), null (4254), null (4255), null (4256), Goat (4930), Goat (4931), Billy Goat (4932), Goat (4933), Goat (4934), Billy Goat (4935), Li'l lamb (5146), Lamb (5147), Sheep (5148), Sheep (5149), Sheep (5150), Sheep (5151), Sheep (5152), Sheep (5153), Sheep (5154), Sheep (5155), Sheep (5156), Sheep (5157), Sheep (5158), Sheep (5159), Sheep (5160), Sheep (5161), Sheep (5162), Sheep (5163), Sheep (5164), Mountain Goat (5166), Mountain Goat (5167), Ram (5168), Ram (5169), Ram (5170), Mountain goat (5171), Golden sheep (5172), Golden sheep (5173), Sheep (8876), Sheep (8877) + +Base: 1248 +Linked animations: [5317, 5318, 5319, 5320, 5321, 5322, 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, 5331, 5332, 5333, 6862, 8162, 8163, 8164, 8165, 8170, 8544, 12464] +Linked NPCs: Shadow spider (58), Giant spider (59), Giant spider (60), Jungle spider (62), Deadly red spider (63), Ice spider (64), Poison spider (134), Witch's experiment (second form) (898), Blessed spider (977), Kalrag (997), Poison spider (1009), Jungle spider (1478), Giant crypt spider (2035), Jungle spider (2491), Jungle spider (2492), Fever spider (2850), Huge spider (3585), Giant spider (4400), Jungle spider (6376), Jungle spider (6377), Knight of the Eight-Fold Seal (6507), Grand Chamberlain of the Web (6508), Marshall of the Teeming Horde (6509), Master Web-Spinner (6536), Spirit spider (6841), Spirit spider (6842), Spider (7207), Royal Fly-Catcher (8973), Keeper of the Royal Eggs (8974), Ice spider (10157), Ice spider (10158), Ice spider (10159), Ice spider (10160), Ice spider (10161), Ice spider (10162), Ice spider (10163), Ice spider (10164), Ice spider (10165), Ice spider (10166), Ice spider (10167), Dungeon spider (10496), Dungeon spider (10497), Dungeon spider (10498), Dungeon spider (10499), Dungeon spider (10500), Dungeon spider (10501), Dungeon spider (10502), Dungeon spider (10503), Dungeon spider (10504), Dungeon spider (10505), Dungeon spider (10506), Night spider (10698) +Linked objects: Huge Spider (13370), null (46654), null (46655), null (46656) + +Base: 1249 +Linked animations: [5350] +Linked objects: Pillar (20022), Pillar (20023), Pillar (20024), Pillar (20026), Pillar (20027), Pillar (20028), Pillar (20030), Pillar (20031), Pillar (20032), Pillar (21546), Pillar (21547), Pillar (21548) + +Base: 1250 +Linked animations: [5351] +Linked objects: Machine (20040) + +Base: 1251 +Linked animations: [5360] +Linked objects: null (20066), null (20067), null (20068), null (20069), null (20070), null (20071), null (20072), null (20073), null (20074), null (20075), null (20076), null (20077), null (20078), null (20079), null (20080), null (20081), null (20082), null (20083) + +Base: 1252 +Linked animations: [5353] +Linked graphics: 969 + +Base: 1253 +Linked animations: [5382] +Linked graphics: 972 + +Base: 1254 +Linked animations: [5381] +Linked graphics: 973 + +Base: 1255 +Linked animations: [3951, 5380, 5385, 5386, 5387, 5388, 5389, 7807, 7808, 7809, 7810, 7813, 8555] +Linked NPCs: Chicken (41), Oomlie bird (48), Chicken (951), Chicken (1017), Rooster (1018), Chicken (1401), Chicken (1402), Rooster (1403), Undead chicken (1692), Chicken (2313), Chicken (2314), Chicken (2315), Sneaky undead fowl (5209), Swordchick (5595), Dreadfowl (6825), Dreadfowl (6826), Chicken (11256), Chicken (11257) + +Base: 1256 +Linked animations: [5062] + +Base: 1257 +Linked animations: [5068] +Linked objects: Childerkin slaves (20100), Childerkin slaves (20117) + +Base: 1258 +Linked animations: [5058] +Linked objects: Wintumber tree (19038) + +Base: 1259 +Linked animations: [5061, 5069, 5070, 5071, 5072, 5073, 8771, 8772, 8773, 8774, 8775, 8776, 8777] +Linked NPCs: null (829), null (5003), null (5004), null (5005), null (5006), null (5007), null (5008), null (5009), null (5010), null (5011), null (5012), null (5013), null (5014), null (5015), null (5016), Gublinch (5017), Gublinch (5018), Gublinch (5019), Gublinch jailmate (7144), Gublinchette jailmate (7145), Gublinchette jailmate (7146), Gublinch jailmate (7147), Gublinch jailmate (7148), Gublinch jailmate (7149), Gublinch jailmate (7150) +Linked objects: Cage with gublinch (19037), null (20119), null (20120), null (20121), null (20122), null (20123), null (20124), null (20125), null (20126), null (20127), null (20128) + +Base: 1260 +Linked animations: [5060] +Linked graphics: 859 + +Base: 1261 +Linked animations: [5064, 5065, 5066] +Linked graphics: 860, 861, 862, 1281, 1282 + +Base: 1262 +Linked animations: [5094, 5095, 5096, 5097, 5098] +Linked NPCs: Penance Fighter (5040), Penance Fighter (5044), Penance Fighter (5045), Penance Fighter (5213), Penance Fighter (5214), Penance Fighter (5215), Penance Fighter (5216), Penance Fighter (5217), Penance Fighter (5218), Penance Fighter (5219) + +Base: 1263 +Linked animations: [5104, 5105, 5106, 5107] +Linked NPCs: Penance Healer (5043), Penance Healer (5238), Penance Healer (5239), Penance Healer (5240), Penance Healer (5241), Penance Healer (5242), Penance Healer (5243), Penance Healer (5244), Penance Healer (5245), Penance Healer (5246) + +Base: 1264 +Linked animations: [5082, 5086] + +Base: 1265 +Linked animations: [5408, 5409, 5410, 5411, 5412, 5413, 5414] +Linked NPCs: Penance Queen (5247) + +Base: 1266 +Linked animations: [5089, 5090, 5091, 5092, 5093] +Linked NPCs: Queen spawn (5248) + +Base: 1267 +Linked animations: [5393, 5394, 5395, 5396, 5397] +Linked NPCs: Penance Ranger (5041), Penance Ranger (5229), Penance Ranger (5230), Penance Ranger (5231), Penance Ranger (5232), Penance Ranger (5233), Penance Ranger (5234), Penance Ranger (5235), Penance Ranger (5236), Penance Ranger (5237) + +Base: 1268 +Linked animations: [5099, 5100, 5101, 5102] +Linked NPCs: Penance Runner (5042), Penance Runner (5220), Penance Runner (5221), Penance Runner (5222), Penance Runner (5223), Penance Runner (5224), Penance Runner (5225), Penance Runner (5226), Penance Runner (5227), Penance Runner (5228) + +Base: 1269 +Linked animations: [5433] +Linked graphics: 975 + +Base: 1270 +Linked animations: [5434] +Linked graphics: 976, 980 + +Base: 1271 +Linked animations: [5435] +Linked graphics: 977, 978, 979 + +Base: 1272 + +Base: 1273 +Linked animations: [5422] +Linked objects: null (20246) + +Base: 1274 +Linked animations: [5392] +Linked graphics: 863, 864 + +Base: 1275 +Linked animations: [5403, 5404, 5405] +Linked graphics: 870, 871, 872 + +Base: 1276 +Linked animations: [5398] +Linked graphics: 866 + +Base: 1277 +Linked animations: [5401, 5402] +Linked graphics: 867, 868, 869, 876, 974 + +Base: 1278 +Linked animations: [5445, 5446, 5447, 5448, 5449, 5450, 5451, 7581, 7582, 7583, 7584, 7585, 7586] +Linked NPCs: Locust rider (5251), Locust rider (5252), Locust rider (5255), Locust rider (5256), Scabaras locust (6776), Locust lancer (6777), Locust ranger (6778) + +Base: 1279 +Linked animations: [5452, 5453, 5454, 5455, 5456, 5457, 5458, 7592, 7593, 7594, 7595, 7596, 7635, 11631, 11632, 11633, 11634, 11635] +Linked NPCs: Giant scarab (5253), Giant scarab (5292), Giant scarab (6770), Giant scarab (6771), Small scarab (7500), Small scarab (7501), Small scarab (7502), Small scarab (7503) + +Base: 1280 +Linked animations: [5459] +Linked graphics: 982 + +Base: 1281 +Linked animations: [5460] +Linked graphics: 984 + +Base: 1282 +Linked animations: [5461] +Linked graphics: 985 + +Base: 1283 +Linked animations: [5462] +Linked graphics: 986 + +Base: 1284 +Linked animations: [5465, 5466, 5467, 5468, 5469, 5470] +Linked graphics: 987, 988, 989, 990, 991, 992 + +Base: 1285 +Linked animations: [5463] +Linked graphics: 983 + +Base: 1286 +Linked animations: [1930, 1931, 1932, 1933, 5504, 5548, 5549, 5550, 5551, 5552, 5553, 5554, 5555, 5556, 5557, 5558, 5559, 5560, 5561, 5562, 5563, 9392, 12514, 12515, 12516, 12517, 12518] +Linked NPCs: Mummy (1958), Mummy (1959), Mummy ashes (1960), Mummy (1961), Mummy (1962), Mummy (1963), Mummy (1964), Mummy (1965), Mummy (1966), Mummy (1967), Mummy (1968), Mummy (2015), Mummy (2016), Mummy (2017), Mummy (2018), Mummy (2019), Guardian mummy (4476), Annoyed guardian mummy (4477), Mummy ashes (5360), Mummy (6753), Mummy (6754), Mummy (6755), Mummy (6756), Mummy (6757), Mummy (6758), Mummy (6759), Mummy (6760), Mummy warrior (7806), Mummy warrior (7807), Mummy warrior (7808), Mummy Champion (8988), Mummy Champion (8989), Mummy Champion (8992) + +Base: 1287 +Linked animations: [5564] +Linked objects: Mummy (20985), Mummy (20986), Mummy (31310) + +Base: 1288 +Linked animations: [5565, 5566, 5567, 5568, 5569, 5570, 5571, 5572, 5573, 5574, 5575, 5576, 5577, 5578, 5579, 5580, 5581, 5582, 5583, 5584, 5587, 5788, 5789, 5790, 5791, 5792, 5793, 6932, 9395, 10415, 10420, 10474, 14499, 14500, 14501, 14502, 14503, 14504, 14506, 14507, 14509] +Linked NPCs: Zombie (73), Zombie (74), Zombie (75), Zombie (76), Summoned Zombie (77), Undead one (504), Undead one (505), Nazastarool (507), Gravecreeper (532), Gravecreeper (533), Zombie (1826), Zombie (2714), Zombie protester (2831), Zombie protester (2832), Zombie protester (2833), Zombie protester (2834), Zombie protester (2835), Zombie protester (2836), Zombie Champion (3066), Zombie (3622), Zombie (4392), Zombie (4393), Zombie (4394), Zombie (5293), Zombie (5294), Zombie (5295), Zombie (5296), Zombie (5297), Zombie (5298), Zombie (5299), Zombie (5300), Zombie (5301), Zombie (5302), Zombie (5303), Zombie (5304), Zombie (5305), Zombie (5306), Zombie (5307), Zombie (5308), Zombie (5309), Zombie (5310), Zombie (5311), Zombie (5312), Zombie (5313), Zombie (5314), Zombie (5315), Zombie (5316), Zombie (5317), Zombie (5318), Zombie (5319), Zombie (5320), Zombie (5321), Zombie (5322), Zombie (5323), Zombie (5324), Zombie (5325), Zombie (5326), Zombie (5327), Zombie (5328), Zombie (5329), Zombie (5330), Zombie (5331), Undead one (5355), Undead one (5356), Undead one (5357), Undead one (5358), Zombie (5375), Zombie (5376), Zombie (5377), Zombie (5378), Zombie (5379), Zombie (5380), Zombie (5393), Zombie (5394), Zombie (5395), Zombie (5396), Zombie (5397), Zombie (5398), Zombie (5399), Zombie (5400), Zombie (5401), Zombie (5402), Zombie (5403), Zombie (5404), Zombie (5405), Zombie (5406), Zombie (5407), Zombie (5408), Zombie (5409), Zombie (5410), Zombie (6099), Zombie (6100), Dried zombie (6761), Dried zombie (6762), Dried zombie (6763), Zombie (7789), Zombie (7790), Zombie (7791), Zombie (7792), Zombie (7817), Zombie (7818), Zombie (7819), Zombie (7820), Zombie (8142), Zombie (8143), Zombie (8144), Zombie (8145), Armoured zombie (8149), Armoured zombie (8150), Armoured zombie (8151), Armoured zombie (8152), Armoured zombie (8153), Armoured zombie (8154), Armoured zombie (8155), Armoured zombie (8156), Armoured zombie (8157), Armoured zombie (8158), Armoured zombie (8159), Armoured zombie (8160), Armoured zombie (8161), Armoured zombie (8162), Armoured zombie (8163), Armoured zombie (8164), Armoured zombie (9162), Armoured zombie (9163), Armoured zombie (9165), Zombie (10364), Zombie (10365), Zombie (10366), Zombie (10367), Zombie (10368), Zombie (10369), Zombie (10370), Zombie (10371), Zombie (10372), Zombie (10373), Zombie (10374), Zombie (10375), Zombie (10376), Zombie (10377), Zombie (10378), Zombie (10379), Zombie (10380), Zombie (10381), Zombie (10382), Zombie (10383), Zombie (10384), Zombie (10385), Gravecreeper (11708), Gravecreeper (11709), Gravecreeper (11710), Gravecreeper (11711), Gravecreeper (11712), Gravecreeper (11713), Gravecreeper (11714), Gravecreeper (11715), Gravecreeper (11716), Gravecreeper (11717), Gravecreeper (11718), Gravecreeper (11719), Gravecreeper (11720) + +Base: 1289 +Linked animations: [5478, 5530, 5531, 5532, 5533, 5534, 5535, 5536, 5537, 5544, 5546, 5794, 5795, 7439, 7440, 7441, 7442, 7443, 7444, 7445, 7508, 7522, 11221] +Linked NPCs: Ghost (104), Spirit of Zadimus (501), Rashiliyia (506), Nazastarool (509), Tree spirit (655), Mischievous ghost (1551), Ghost (2716), null (2930), Ghost (2931), Ghost (4388), Ghost (5342), Ghost (5343), Ghost (5344), Ghost (5345), Ghost (5346), Ghost (5347), Ghost (5348), Ghost (5349), Ghost (5350), Ghost (5351), Ghost (5352), Ghost (5369), Ghost (5370), Ghost (5371), Ghost (5372), Ghost (5373), Ghost (5374), Ghost (6095), Ghost (6096), Ghost (6097), Ghost (6098), Revenant knight (6611), Revenant knight (6619), Revenant knight (6627), Revenant knight (6630), Revenant knight (6634), Revenant knight (6650), Revenant knight (6654), Revenant knight (6665), Revenant knight (6673), Revenant knight (6676), Revenant knight (6692), Revenant knight (6714), Revenant knight (6726), Revenant knight (6730), Ghost (7998), Ghost (7999), Tormented wraith (8125), Ghost (8623), Spectral tender (9451), Spectral cultivator (9452), Spectral worshipper (9453), Spectral attendant (9454), Ghost (10822), Ghost (10823), Ghost (10824), Ghost (10825), Ghost (10826), Ghost (10827), Ghost (10828), Ghost (10830) +Linked graphics: 1997 + +Base: 1290 +Linked animations: [1437, 1438, 1439, 4647, 5538, 5539, 5540, 5541, 5542, 5543, 5545, 5547, 9028, 13395, 13396, 13397, 13398, 13405, 13406, 13407] +Linked NPCs: Ghost (103), Thrantax the Mighty (238), Ghost (491), Spirit of Scorpius (492), Echned Zekin (940), Haze (1537), Treus Dayth (1540), Ghost (1541), Ghost (1549), null (2013), Klenter (2014), Ghost (4387), Ghost (6094), Spectral Vyrewatch (7687), null (9721), Ghost (10821), Ghost (10829), Mysterious shade (10831), Mysterious shade (10832), Mysterious shade (10833), Mysterious shade (10834), Mysterious shade (10835), Mysterious shade (10836), Mysterious shade (10837), Mysterious shade (10838), Mysterious shade (10839), Mysterious shade (10840), Mysterious shade (10841), Mysterious shade (10842), Ghost (10981), Ghost (10982), Ghost (10983), Ghost (10984), Ghost (10985), Ghost (10986), Ghost (10987), Ghost (10988), Ghost (10989), Ghost (10990), Ghost (10991), Ghost (10992), Ghost (10993), Ghost (10994), Ghost (10995), Ghost (10996), Ghost (10997), Ghost (10998), Ghost (10999), Ghost (11000) + +Base: 1291 +Linked animations: [259, 1442, 1443, 1444, 3994, 4017, 5479, 5481, 5483, 5485, 5487, 5489, 5491, 5493, 5495, 5497, 5499, 5501, 5503, 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5517, 5518, 5519, 5520, 5521, 5522, 5523, 5527, 5528, 9398, 9402, 9407, 10943, 10949, 10950, 11636, 11637, 12632, 13462, 13463, 13464, 14549] +Linked NPCs: Skeleton (90), Skeleton (91), Skeleton (92), Skeleton (93), Skeleton Mage (94), Undead one (502), Undead one (503), Nazastarool (508), San Tojalon (936), Irvig Senay (937), Ranalph Devere (938), Skeleton (1471), Corpse (1538), Skeletal miner (1539), Giant skeleton (1973), Skeleton (2036), Skeleton (2037), Skeleton (2715), Skeleton mage (2717), Skeleton Champion (3065), Skeleton (3151), Skeleton (3291), Skeleton (3581), Skeleton Mage (3844), Skeleton Mage (3851), Skeleton (4384), Skeleton (4385), Skeleton (4386), Skeleton (5332), Skeleton (5333), Skeleton (5334), Skeleton (5335), Skeleton (5336), Skeleton (5337), Skeleton (5338), Skeleton (5339), Skeleton (5340), Skeleton (5341), Undead one (5353), Undead one (5354), Giant skeleton (5359), Skeleton (5365), Skeleton (5366), Skeleton (5367), Skeleton (5368), Skeleton (5381), Giant skeleton (5384), Skeleton (5385), Skeleton (5386), Skeleton (5387), Skeleton (5388), Skeleton (5389), Skeleton (5390), Skeleton (5391), Skeleton (5392), Skeleton (5411), Skeleton (5412), Skeleton (5422), Skeleton (6091), Skeleton (6092), Skeleton (6093), Skeleton (6764), Skeleton (6765), Skeleton (6766), Skeleton (6767), Skeleton (6768), Skeleton looter (7481), Skeleton looter (7482), Skeleton looter (7483), Skeleton looter (7484), Skeleton looter (7485), Skeleton looter (7486), Skeleton looter (7487), Skeleton looter (7488), Skeleton looter (7489), Skeleton looter (7490), Skeleton looter (7491), Skeleton (7774), Skeleton (7775), Skeleton (7776), Skeleton (7777), Skeleton (7778), Skeleton (7788), Skeleton (7815), Undead mage (8486), Giant skeleton (10469), Giant skeleton (10470), Giant skeleton (10471), Giant skeleton (10472), Giant skeleton (10473), Giant skeleton (10474), Giant skeleton (10475), Giant skeleton (10476), Giant skeleton (10477), Giant skeleton (10478), Giant skeleton (10479), Skeleton (10629), Skeleton (10630), Skeleton (10631), Skeleton (10632), Skeleton (10633), Skeleton (10634), Skeleton (10635), Skeleton (10636), Skeleton (10637), Skeleton (10638), Skeleton (10639), Skeleton (10640), Skeleton (10641), Skeleton (10642), Skeleton (10643), Skeleton (10644), Skeleton (10645), Skeleton (10646), Skeleton (10647), Skeleton (10648), Skeleton (10649), Skeleton (10650), Skeleton (10651), Skeleton (10652), Skeleton (10653), Skeleton (10654), Skeleton (10655), Skeleton (10656), Skeleton (10657), Skeleton (10658), Skeleton (10659), Skeleton (10660), Skeleton (10661), Skeleton (10662), Skeleton (10663), Skeleton (10664), Skeleton (10665), Skeleton (10666), Skeleton (10667), Skeleton (10668), Skeleton (10669), Skeleton (10670), Skeleton (10671), Skeleton (10672), Skeleton (10673), Skeleton (10674), Skeleton (10675), Skeleton (10676), Skeleton (10677), Skeleton (10678), Skeleton (10679), Skeleton (10680), Skeleton (10681), Skeleton (10682), Skeleton (10683), Skeleton (10684), Skeleton (10685), Skeleton (10686), Skeleton (10687), Skeleton (10688), Skeleton (10689), Skeleton (10690), Skeleton (10691), Skeleton (10692), Skeleton (10693), Skeletal minion (11722), Skeletal minion (11723), Skeletal minion (11724), Skeletal minion (11725), Skeletal minion (11726), Skeletal minion (11727), Skeletal minion (11728), Skeletal minion (11729), Skeletal minion (11730), Skeletal minion (11731), Skeletal minion (11732), Skeletal minion (11733), Skeletal minion (11734), Skeletal minion (11735), Skeletal minion (11736) +Linked objects: Bones (13366) + +Base: 1292 +Linked animations: [5515, 5516] +Linked graphics: 994, 995 + +Base: 1293 +Linked animations: [5526] +Linked graphics: 996, 997 + +Base: 1294 +Linked animations: [5603, 5604, 9405, 9406] +Linked objects: Armour (20453), Skeleton (20455), Skeleton (20457), null (20972), null (20973), null (20974), null (20975), null (20976), Skeleton (31312), Skeleton (31313) + +Base: 1295 +Linked animations: [5599, 5600, 5601] +Linked objects: Passageway (20468), Passageway (20469), Bars (20532), Bars (20533), Bars (20534) + +Base: 1296 +Linked animations: [5605] +Linked objects: null (20580), null (20660), null (20725), null (20790) + +Base: 1297 +Linked animations: [5588] + +Base: 1298 +Linked animations: [5631] +Linked objects: Wall (20942), Wall (20943), Wall (20946), Wall (20947), Wall (20950), Wall (20951), Wall (20954), Wall (20955) + +Base: 1299 +Linked animations: [5612, 5613, 5615, 5616, 5617, 5618, 5619] +Linked NPCs: Mutant tarn (5421) +Linked graphics: 1004 + +Base: 1300 +Linked animations: [5589, 5590, 5591, 5592, 5593, 5594, 5595, 5596, 5597] +Linked NPCs: Possessed pickaxe (5413), Animated spade (5414) + +Base: 1301 +Linked animations: [5621, 5622, 5623, 5624, 5625, 5626, 5627, 5628, 5629] +Linked NPCs: Terror dog (5417), Terror dog (5418), Terror dog (11365) + +Base: 1302 +Linked animations: [5610, 5614, 5660, 7661] +Linked graphics: 1002, 1003, 1013, 1316 + +Base: 1303 +Linked animations: [5630] +Linked graphics: 1005 + +Base: 1304 +Linked animations: [2715, 5585, 5586, 5641, 5642, 5643, 5644, 5645, 5646, 5647, 5648, 5649, 5650, 5651, 5652, 5653, 5654, 5655, 5656, 5657, 5658, 5875, 5876, 5877, 5878, 5879, 5880, 5881, 5882, 5883, 5884, 5885, 5886, 5887, 5888, 5889, 5890, 5891, 7389, 7390, 9560] +Linked NPCs: 50% Luke (2828), Captain Donnie (2830), Zombie pirate (2837), Zombie pirate (2838), Zombie pirate (2839), Zombie pirate (2840), Zombie pirate (2841), Zombie pirate (2842), Zombie swab (2843), Zombie swab (2844), Zombie swab (2845), Zombie swab (2846), Zombie swab (2847), Zombie swab (2848), Monk (5608), Monk (5609), Monk (5610), Monk (5611), Brother Tranquility (5616), Brother Tranquility (5617), Brother Tranquility (5618), Monk (5619), Monk (5620), Monk (5621), Monk (5622), Zombie monk (5623), Zombie monk (5624), Zombie monk (5625), Zombie monk (5626), Sorebones (5627), Sorebones (5628), Zombie pirate (5629), Zombie pirate (5630), Zombie pirate (5631), Zombie pirate (5632), Zombie pirate (5633), Zombie pirate (5634), Zombie pirate (5635), Zombie pirate (5636), Zombie pirate (5637), Zombie pirate (5638), Zombie pirate (5639), Zombie pirate (5640), Zombie pirate (5641), Zombie pirate (5642), Zombie pirate (5643), Zombie pirate (5644), Zombie pirate (5645), Zombie pirate (5646), Zombie pirate (5647), Zombie pirate (5648), Zombie pirate (5649), Zombie pirate (5650), Zombie pirate (5651), Zombie pirate (5652), Zombie pirate (5653), Zombie pirate (5654), Zombie pirate (5655), Zombie pirate (5656), Zombie pirate (5657), Zombie pirate (5658), Zombie pirate (5659), Zombie pirate (5660), Zombie pirate (5661), Zombie pirate (5662), Zombie pirate (5663), Zombie pirate (5664), Zombie pirate (5665), null (5667), null (5668), null (5669), null (5670), null (5671), null (5672), null (5673), null (5674), null (5675), null (5676), null (5677), null (6537), Mandrith (8725) + +Base: 1305 +Linked animations: [5638, 5639, 5640, 6097, 9120] +Linked graphics: 1009, 1010, 1011, 1037, 1604 + +Base: 1306 +Linked animations: [5634, 5635, 5636] +Linked graphics: 999, 1000, 1001, 1006, 1007, 1008 + +Base: 1307 +Linked animations: [3329, 5598, 6653] +Linked objects: Candlestick (2146), null (20256), null (20262), null (20537), null (20538), null (20621), null (20622), null (20686), null (20687), null (20751), null (20752), Flames (25201), null (25659), Torch (26195), Grave (27283), Grave (27284), Grave (27285), Grave (27286), Grave (27287), Grave (27288), Torch (27891), Druid's Circle torch (28699), Flames (28848), Oil lamp (29132), Oil lamp (29133), null (29302), null (30240), null (30395), Torch (30569), null (30642), Flames (30934), null (30940), Fire (37597), null (38223), Flames (38267), null (38371), null (39220), Torch (39763), null (40454), null (40568), null (41356), null (41357), null (41358), null (42491), null (42677), null (42678), null (42720), null (44087), null (49192), null (49193), null (49238), null (53572), null (55200) + +Base: 1308 +Linked animations: [5659] +Linked graphics: 1012 + +Base: 1309 +Linked animations: [5661, 5662, 5663, 5665] + +Base: 1310 +Linked animations: [4139, 4140, 5666, 5667, 5668, 5669, 5670, 5671, 5672, 5673, 5674, 5675, 5676, 5677, 5678, 5680, 5681, 5682, 5684, 5685, 5688, 5689, 5690, 5691, 5692, 5693, 5694, 5695, 5696, 5697, 5698, 5699, 5700, 5701, 5702, 5703, 5704, 5705, 5706, 5707, 5708, 5709, 5710, 5715, 5717, 6438, 6439, 8314, 8315, 8316, 8317, 8318, 8319, 11730, 11742, 11743, 11744, 11745, 11746, 11747, 11748, 11750, 11751, 11752, 11753, 11754, 11755, 11756, 11757, 11758, 11779, 11780, 11781] +Linked NPCs: Penguin (131), Macaroni Penguin (4252), null (5427), Penguin (5428), Penguin (5429), Penguin (5430), KGP Guard (5431), Pescaling Pax (5432), Ping (5433), Ping (5434), Pong (5435), Pong (5436), Ping (5437), Pong (5438), KGP Agent (5441), null (5443), Noodle (5444), Penguin (5445), Penguin suit (5446), Agility Instructor (5447), Army Commander (5448), Penguin (5449), Penguin (5450), Penguin (5451), Penguin display (5976), Baby penguin (6908), Penguin (6909), Penguin (6910), Baby penguin (7313), Penguin (7314), Penguin (7315), Baby penguin (7316), Penguin (7317), Penguin (7318), KGP Interrogator (7582), Captain Marlin (7583), Captain Marlin (7584), Office penguin (7585), Office penguin (7586), Office penguin (7587), Office penguin (7588), Office penguin (7589), Office penguin (7590), Office penguin (7591), Office penguin (7592), Trainee KGP agent (7593), null (7594), KGP agent (7595), Penguin suit (7596), Penguin suit (7597), Penguin suit (7598), Penguin suit (7599), Penguin (7841), Penguin (7894), Penguin (7895), Penguin (7896), Penguin (7897), Penguin (8094), Penguin (8095), Penguin (8096), Penguin (8097), Penguin (8098), Penguin (8099), Penguin (8100), Penguin (8101), Penguin (8102), Penguin (8103), Penguin (8663), Penguin (8664), Penguin (8665), Penguin (8666), Penguin (8667), Penguin (8668), Penguin (8669), Penguin (8670), Penguin (8671), null (8672), null (8673), Penguin (8688), Penguin (8689), Penguin (8690), Penguin (8691), Penguin (8692), null (8696), KGP Agent (8697), Penguin (8882), Penguin (8883), Penguin (8884), Penguin (8886), Penguin (8887), Penguin (8888), Penguin (8889), Penguin (8891), Penguin (8892), Penguin (8893), Penguin (9233), Penguin (9234), Penguin (9235) +Linked graphics: 1015, 1016, 2050 +Linked objects: null (24570) + +Base: 1311 +Linked animations: [5741] +Linked objects: null (21146) + +Base: 1312 +Linked animations: [5739] +Linked objects: null (21108), null (21109) + +Base: 1313 +Linked animations: [5734] +Linked objects: Ice (21148), Ice (21149), Ice (21150), Ice (21151), Ice (21152), Ice (21153), Ice (21154), Ice (21155), Ice (21156) + +Base: 1314 +Linked animations: [5729] +Linked objects: null (21091), Crazy penguin (21094) + +Base: 1315 +Linked animations: [5728, 9577, 9581] +Linked objects: null (21089), Crazy penguin (21092), Cannon (31681) + +Base: 1316 +Linked animations: [5730] +Linked objects: null (21090), Crazy penguin (21093) + +Base: 1317 +Linked animations: [5735, 5736] + +Base: 1318 +Linked animations: [5740, 11724] +Linked objects: Captured penguin (43394), Captured penguin (43395), Captured penguin (43396), Captured penguin (43397), Interrogation chair (43398) + +Base: 1319 +Linked animations: [5737] +Linked objects: Steam generator (21002) + +Base: 1320 +Linked animations: [5731, 5733] + +Base: 1321 +Linked animations: [5738] +Linked objects: Spinning light (21085), Spinning light (43169), Spinning light (43170) + +Base: 1322 +Linked animations: [5742] +Linked objects: Table (20996) + +Base: 1323 +Linked animations: [5721, 5722, 5723, 5724, 5725, 5726] +Linked NPCs: Icelord (5452), Icelord (5453), Icelord (5454), Icelord (5455) + +Base: 1324 +Linked animations: [5712] +Linked graphics: 1014 + +Base: 1325 +Linked animations: [5727] +Linked graphics: 1017 + +Base: 1326 + +Base: 1327 +Linked animations: [5747] +Linked graphics: 1018 + +Base: 1328 +Linked animations: [5743] +Linked objects: Drill (23962) + +Base: 1329 +Linked animations: [5744] +Linked objects: Lathe (23961) + +Base: 1330 +Linked animations: [5745, 8735] +Linked objects: null (21003), Smoke (22733), Smoke (22975), null (29535), null (40156) + +Base: 1331 + +Base: 1332 +Linked animations: [5768] +Linked objects: Banner (21500), Banner (21501) + +Base: 1333 +Linked animations: [5771, 5772, 11485] +Linked objects: Large Geyser (21355), Small Geyser (21356), Large geyser (26414), null (30067) + +Base: 1334 +Linked animations: [5773, 5774] +Linked NPCs: Arctic Pine (5469) +Linked objects: Arctic Pine (21273), null (28238), null (28240), null (28243), null (28245), null (28248), null (28250), Tree (39430) + +Base: 1335 +Linked animations: [5758] +Linked graphics: 1020 + +Base: 1336 +Linked animations: [5757] +Linked graphics: 1021 + +Base: 1337 +Linked animations: [5780] +Linked graphics: 1022 + +Base: 1338 +Linked animations: [1735, 5781, 5782, 5783, 5784, 5785, 5786, 5787, 5848, 5849, 5850, 5851, 5852, 5854, 5855, 5856, 8056, 8058, 8059, 8536, 12827] +Linked NPCs: Cow (81), Cow (397), Cow (955), Undead cow (1691), Cow calf (1766), Cow (1767), Cow calf (1768), Plague cow (1998), Plague cow (1999), Plague cow (2000), Cow calf (2310), Cow (3309), Undead cow (5211), Yak (5529), Unicow (5603), Pack yak (6873), Pack yak (6874), Pack yak (9491), Rogue pack yak (9603), Rogue pack yak (9604), Rogue pack yak (9605), Rogue pack yak (9606) +Linked objects: Dairy cow (8689), Dairy Cow (12111), Prized dairy cow (47721) + +Base: 1339 +Linked animations: [5797] +Linked objects: Fountain (21764) + +Base: 1340 +Linked animations: [5801, 5802, 5803] +Linked NPCs: Autumn Elemental (5533), Autumn Elemental (5534), Autumn Elemental (5535), Autumn Elemental (5536), Autumn Elemental (5537), Autumn Elemental (5538), Spring Elemental (5539), Spring Elemental (5540), Spring Elemental (5541), Spring Elemental (5542), Spring Elemental (5543), Spring Elemental (5544), Spring Elemental (5545), Spring Elemental (5546), Summer Elemental (5547), Summer Elemental (5548), Summer Elemental (5549), Summer Elemental (5550), Summer Elemental (5551), Summer Elemental (5552), Winter Elemental (5553), Winter Elemental (5554), Winter Elemental (5555), Winter Elemental (5556), Winter Elemental (5557), Winter Elemental (5558) + +Base: 1341 +Linked animations: [5798] +Linked objects: Fireplace (21795) + +Base: 1342 + +Base: 1343 +Linked animations: [5811] +Linked graphics: 1024 + +Base: 1344 +Linked animations: [5813] + +Base: 1345 +Linked animations: [5844] +Linked objects: Symbol of life (21893), Symbol of life (21894) + +Base: 1346 +Linked animations: [5830] +Linked objects: null (21852), null (21856), null (21860), null (21864) + +Base: 1347 +Linked animations: [5847] +Linked objects: Cage (21930), Cage (21931) + +Base: 1348 +Linked animations: [5829] +Linked objects: Water pipes (21844), Water pipes (21846) + +Base: 1349 +Linked animations: [5825] +Linked objects: Spinning pipe (21920) + +Base: 1350 +Linked animations: [5824] +Linked objects: Magic table (21918) + +Base: 1351 +Linked animations: [5828] +Linked objects: null (21832) + +Base: 1352 +Linked animations: [5820] +Linked graphics: 1025 + +Base: 1353 +Linked animations: [5835] +Linked graphics: 1026 + +Base: 1354 +Linked animations: [5839, 5840, 5841, 5842, 5843] +Linked NPCs: Frogeel (5593) + +Base: 1355 +Linked animations: [5831, 5832, 5833, 5834] +Linked NPCs: Homunculus (5581), Homunculus (5582), Homunculus (5583), null (5599), null (5600) + +Base: 1356 +Linked animations: [5836, 5837, 5838] + +Base: 1357 +Linked animations: [5892, 5893, 5894, 5895, 5896, 5897, 5898] +Linked NPCs: Barrelchest (5666) + +Base: 1358 +Linked animations: [5858, 5859] +Linked NPCs: Mi-Gor (5613) + +Base: 1359 +Linked animations: [5872, 5873] +Linked NPCs: Puffin (5614), Puffin (5615) + +Base: 1360 +Linked animations: [5857] +Linked objects: Sails (22418) + +Base: 1361 +Linked animations: [5861] + +Base: 1362 +Linked animations: [5909] +Linked objects: Fuse (22484) + +Base: 1363 +Linked animations: [5905, 5906] +Linked objects: Stand (22123), Stand (22124), Candle stand (22125), Stand (36982), Stand (36983) + +Base: 1364 +Linked animations: [5900, 5901] +Linked objects: Wall (22105), Wall (22106), Wall (22107), Wall (22108), Collapsed wall (22109), Collapsed wall (22110) + +Base: 1365 +Linked animations: [5902] + +Base: 1366 +Linked animations: [5903, 5904] + +Base: 1367 +Linked animations: [5874] +Linked objects: null (22338), null (22339), null (42887), null (42889) + +Base: 1368 +Linked animations: [5908] + +Base: 1369 +Linked animations: [5871] +Linked graphics: 1027 + +Base: 1370 +Linked animations: [5899] +Linked graphics: 1028 + +Base: 1371 +Linked animations: [5974, 5975, 5976, 5977, 5978, 5979, 5980, 5981, 5982, 5983, 5984, 5985] +Linked objects: Marv (sick) (22497), Marv (very sick) (22498), Marv (ghastly!) (22499), Marv (sick) (22500), Marv (very sick) (22501), Marv (ghastly!) (22502), Marv (Well) (22503), Hank (sick) (22504), Hank (very sick) (22505), Hank (ghastly!) (22506), Hank (sick) (22507), Hank (very sick) (22508), Hank (ghastly!) (22509), Hank (Well) (22510), Wilf (sick) (22511), Wilf (very sick) (22512), Wilf (ghastly!) (22513), Wilf (sick) (22514), Wilf (very sick) (22515), Wilf (ghastly!) (22516), Wilf (Well) (22517), Sarah (sick!) (22518), Sarah (very sick!) (22519), Sarah (ghastly!) (22520), Sarah (sick!) (22521), Sarah (very sick!) (22522), Sarah (ghastly!) (22523), Sarah (Well) (22524), Rachel (sick) (22525), Rachel (very sick) (22526), Rachel (ghastly!) (22527), Rachel (sick) (22528), Rachel (very sick) (22529), Rachel (ghastly!) (22530), Rachel (Well) (22531) + +Base: 1372 +Linked animations: [5986, 5987, 5988, 5989, 5990, 8043, 8044, 8045, 8933] +Linked NPCs: Nail beast (1521), Nail beast (1522), Nail beast (1523), Talon beast (7347), Talon beast (7348) + +Base: 1373 +Linked animations: [5965, 5966, 5967, 5968, 5969, 5970, 5971, 5972, 5973] +Linked NPCs: Undead Lumberjack (1524), Undead Lumberjack (1525), Undead Lumberjack (5678), Undead Lumberjack (5679), Undead Lumberjack (5680), Undead Lumberjack (5681), Undead Lumberjack (5682), Undead Lumberjack (5683), Undead Lumberjack (5684), Undead Lumberjack (5685), Undead Lumberjack (5686), Undead Lumberjack (5687), Undead Lumberjack (5688), Undead Lumberjack (5689), Undead Lumberjack (5690), Undead Lumberjack (5691), Undead Lumberjack (5692), Undead Lumberjack (5693), Undead Lumberjack (5694), Undead Lumberjack (5695), Undead Lumberjack (5696), Undead Lumberjack (5697), Undead Lumberjack (5698), Undead Lumberjack (5699), Undead Lumberjack (5700), Undead Lumberjack (5701), Undead Lumberjack (5702), Undead Lumberjack (5703), Undead Lumberjack (5704), Undead Lumberjack (5705), Undead Lumberjack (5706), Undead Lumberjack (5707), Undead Lumberjack (5708), Undead Lumberjack (5709), Undead Lumberjack (5710), Undead Lumberjack (5711), Undead Lumberjack (5712), Undead Lumberjack (5713), Undead Lumberjack (5714), Undead Lumberjack (5715), Undead Lumberjack (5716), Undead Lumberjack (5717), Undead Lumberjack (5718), Undead Lumberjack (5719), Undead Lumberjack (5720), Undead Lumberjack (5721), Undead Lumberjack (5722), Undead Lumberjack (5723), Undead Lumberjack (5724), Undead Lumberjack (5725), Undead Lumberjack (5726), Undead Lumberjack (5727), Undead Lumberjack (5728), Undead Lumberjack (5729), Undead Lumberjack (5730), Undead Lumberjack (5731), Undead Lumberjack (5732), Undead Lumberjack (5733), Undead Lumberjack (5734), Undead Lumberjack (5735), Undead Lumberjack (5736), Undead Lumberjack (5737), Undead Lumberjack (5738), Undead Lumberjack (5739), Undead Lumberjack (5740), Undead Lumberjack (5741), Undead Lumberjack (5742), Undead Lumberjack (5743), Undead Lumberjack (5744), Undead Lumberjack (5745), Undead Lumberjack (5746), Undead Lumberjack (5747) + +Base: 1374 +Linked animations: [6069, 6070, 6071, 6072, 6073] +Linked objects: Pylon (22663), Pillar (26302), Tree (26724) + +Base: 1375 +Linked animations: [6023] +Linked objects: Sinclair family fountain (2654), Fountain (22973), Fountain (24161), Fountain (28662), Fountain (30223), Fountain (30820), Fountain (34579), Fountain of Heroes (36695), Fountain (36781), Fountain (41448) + +Base: 1376 +Linked animations: [6039] +Linked objects: Frog spit (22974) + +Base: 1377 +Linked animations: [6037] +Linked objects: Sink (22715) + +Base: 1378 +Linked animations: [6029, 6030, 6031, 6032] +Linked objects: Lamp (22760), Lamp (22761), Lamp (22762), Empty lamp (22763), Lamp (22764), Lamp (22976), Broken lamp (22977), null (22978), null (22979), null (22980), null (22981), null (22982), null (22983), null (22984), null (22985), null (22986), null (22987), null (22988), null (22989), null (22990), null (22991), null (22992), null (22993), null (22994), null (22995), null (22996), null (22997), null (22998), null (22999), null (23000), null (23001), null (23002), null (23003), null (23004), null (23005), null (23006), null (23007), null (23008), null (23009), null (23010), null (23011), null (23012), null (23013), null (23014), null (23015), null (23016), null (23017), null (23018), null (23019), null (23020), null (23021), null (23022), null (23023), null (23024), null (23025), null (23026), null (23027), null (23028), null (23029), null (23030), null (23031), null (23032), null (23033), null (23034), null (23035), null (23036), null (23037), null (23038), null (23039), null (23040), null (23041) + +Base: 1379 +Linked animations: [6033, 6034, 6035, 6036] +Linked NPCs: Moths (5827) +Linked objects: Moths (22734), Zapper (22735), Zapper (22736), Zapper (22737) + +Base: 1380 +Linked animations: [6027] +Linked objects: Thingymajig (22823) + +Base: 1381 +Linked animations: [6028] +Linked objects: Magic ball (22826) + +Base: 1382 +Linked animations: [6038] +Linked objects: Chimney (22727), Wire machine (22728), Wire machine (22729), Wire (22730), Cogs (22732) + +Base: 1383 +Linked animations: [2308, 2402, 2405, 2406, 2407, 2408, 2412, 2768, 3183, 4201, 4202, 4203, 4204, 4205, 4206, 5750, 5751, 5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999, 6000, 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, 6048, 6049, 6050, 6051, 6052, 6053, 6054, 6055, 6060, 6084, 6107, 6149, 6159, 6194, 6210, 6212, 6214, 6215, 6359, 6360, 7312, 7313, 7314, 7322, 7328, 7329, 10940, 10941, 11225, 11226, 11259, 11260, 11261, 11262, 11263, 11264, 11265, 11266, 11267, 11268, 11269, 11270, 11271, 11272, 11273, 11274, 11288, 11289, 11290, 11291, 11299, 11304, 11306, 11307, 11308, 11327, 11328, 11329, 11330, 11331, 11332, 11341, 11362, 11363, 11364, 11365, 11367, 11368, 11378, 11381, 11386, 11390, 11391, 11392, 11393] +Linked NPCs: Captain Undak (422), null (423), null (424), null (425), Captain Undak (426), Captain Undak (427), Captain Undak (428), Captain Undak (429), Captain Undak (430), Torzek (438), null (439), Andil (440), null (441), Delgon (442), null (443), Gerdi (1719), null (1720), Jobo (1721), null (1722), Andil (1723), Delgon (1724), Gerdi (1725), Jobo (1726), Torzek (1727), null (1728), Oldak (1729), Goblin scribe (1749), Cave goblin (1822), Cave goblin (1823), Cave goblin (1824), Cave goblin (1825), Cave goblin miner (2069), Cave goblin miner (2070), Cave goblin miner (2071), Cave goblin miner (2072), Cave goblin guard (2073), Cave goblin guard (2074), Cave goblin miner (2075), Cave goblin miner (2076), Cave goblin miner (2077), Cave goblin miner (2078), Mistag (2084), null (2085), Kazgar (2086), Ur-tag (2087), Mistag (2089), Zanik (2542), Zanik (2543), Zanik (2544), null (2545), Zanik (2546), Zanik (2723), Zanik (3117), Zanik (3206), Zanik (3207), Zanik (3353), Zanik (3354), Zanik (3355), Zanik (3359), Zanik (3360), Zanik (3361), Zanik (3362), null (3363), Zanik (3364), Zanik (3365), Zanik (3366), Zanik (3367), Zanik (3369), Zanik (3370), Zanik (3371), Zanik (3712), Zanik (3714), Zanik (3721), Zanik (3722), null (4313), Dartog (4314), Zanik (4322), Zanik (4323), Zanik (4324), Zanik (4326), Zanik (4337), null (4338), Zanik (4339), Zanik (4340), Zanik (4341), Zanik (4342), Cave goblin (5752), Cave goblin (5753), Cave goblin (5754), Cave goblin (5755), Cave goblin (5756), Cave goblin (5757), Cave goblin (5758), Cave goblin (5759), Cave goblin (5760), Cave goblin (5761), Cave goblin (5762), Cave goblin (5763), Cave goblin (5764), Cave goblin (5765), Cave goblin (5766), Cave goblin (5767), Cave goblin (5768), Cave goblin (5769), Ur-zek (5770), Ur-vass (5771), Ur-taal (5772), Ur-meg (5773), Ur-lun (5774), Ur-pel (5775), Banker (5776), Banker (5777), Bartak (5778), Turgall (5779), Reldak (5780), Miltog (5781), Mernik (5782), Cave goblin (5783), Crate goblin (5784), Cave goblin (5785), Goblin scribe (5786), null (5787), Gourmet (5788), Gourmet (5789), Gourmet (5790), Gourmet (5791), Turgok (5792), Markog (5793), Durgok (5794), Tindar (5795), Gundik (5796), Zenkog (5797), Lurgon (5798), Ur-tag (5799), Guard (5800), Guard (5801), null (5802), Bozin (5807), Bozin (5808), Fandon (5809), Fandon (5810), Horgon (5811), Horgon (5812), Lorzan (5813), Lorzan (5814), Caldi (5815), Caldi (5816), Elzik (5817), Elzik (5818), Inglin (5819), Inglin (5820), Kenti (5821), Kenti (5822), Spit goblin (5823), Barlak (5828), Zanik (5857), Ur-tag (5858), Zanik (5861), Tegdak (5868), null (5869), Zanik (5870), Cave goblin (5873), Cave goblin (5874), Cave goblin (5875), Cave goblin (5876), Cave goblin (5877), Cave goblin (5878), Ticket goblin (5879), null (5888), Oldak (6392), Zanik (6393), null (6394), Zanik (6395), null (6396), Zanik (6397), null (6398), Zanik (6399), null (6400), Oldak (6401), Cave goblin (8451), null (8473), null (8651), null (8652), null (8653), null (8654), null (8655) +Linked graphics: 478 +Linked objects: Cave goblin (21257), Cave goblin (21258), Cave goblin (21259), Cave goblin (21260), Cave goblin (21261), Cave goblin (21262), Cave goblin (21263), Cave goblin (21264), Cave goblin (21265), Cave goblin (21266), Cave goblin (21267), Cave goblin (21268), Cave goblin (21269), Cave goblin (21270), Cave goblin (23551), Cave goblin (23552), Cave goblin (23554), Cave goblin (23555), Cave goblin (23556), Cave goblin (23557), Cave goblin (23558), Cave goblin (23559), Cave goblin (23560), Cave goblin (23561), Cave goblin (23562), Cave goblin (23563), Cave goblin (23564), Cave goblin (23565), Cave goblin (23566), Cave goblin (23567), Cave goblin (23568), Cave goblin (23569), Cave goblin (23570), Cave goblin (23571), Cave goblin (25590), Cave goblin (26191), Cave goblin (26884), Cave goblin (26888), Cave goblin (29194), Cave goblin (29195), Cave goblin (29196), Cave goblin (29197), Cave goblin (29198), Cave goblin (29199), Cave goblin (29200), Cave goblin (29201), Cave goblin (29202), Cave goblin (29203), Cave goblin (29204), Cave goblin (29205), Cave goblin (29206), Cave goblin (29207), Cave goblin (29208), Cave goblin (29209), Cave goblin (29210), Cave goblin (29211), Cave goblin (29212), Cave goblin (29213), Cave goblin (29214), Cave goblin (31094), Cave goblin (31095), Cave goblin (31096), Cave goblin (31097), Cave goblin (31098), Cave goblin (31099), Cave goblin (31100), Cave goblin (31101), Cave goblin (31102), Cave goblin (31103), Cave goblin (31104), Cave goblin (31105), Cave goblin (31106), Cave goblin (31107), Cave goblin (31108), Cave goblin (31109), Cave goblin (31110), Cave goblin (31111), Cave goblin (31112), Cave goblin (31113), Cave goblin (31114), Cave goblin (31115), Cave goblin (31116), Cave goblin (31117), Cave goblin (31118), Cave goblin (31197), Cave goblin (31198), Cave goblin (31199), Cave goblin (31200), Cave goblin (31201), Cave goblin (31202), Cave goblin (31203), Cave goblin (31204), Cave goblin (31205), Cave goblin (36713), Cave goblin (42372), Cave goblin (42373), Cave goblin (42374), Cave goblin (42375), Cave goblin (42376), Cave goblin (42377), Cave goblin (42378), Cave goblin (42379), Cave goblin (42380), Cave goblin (42381), Cave goblin (42382), Cave goblin (42383), Cave goblin (42384), Cave goblin (42385), Cave goblin (42386), Cave goblin (42387), Cave goblin (42388), Cave goblin (42389), Cave goblin (42390), Cave goblin (42391), Cave goblin (42392), Cave goblin (42393) + +Base: 1384 +Linked animations: [6057, 6058, 6059] +Linked NPCs: Young 'un (5803), Tyke (5804), Nipper (5805), Nipper (5806) + +Base: 1385 +Linked animations: [6061, 6062] +Linked NPCs: Goblin fish (5824) + +Base: 1386 +Linked animations: [6065, 11382, 11383] +Linked graphics: 1034, 2003, 2004 + +Base: 1387 +Linked animations: [6056] +Linked graphics: 1035 + +Base: 1388 +Linked animations: [6025] +Linked objects: null (22875) + +Base: 1389 +Linked animations: [6024] +Linked objects: null (22862), null (22863), Water (22864), Water (22865), null (32963) + +Base: 1390 +Linked animations: [6026] +Linked objects: Stairs (22943) + +Base: 1391 +Linked animations: [6009] +Linked graphics: 1029 + +Base: 1392 +Linked animations: [6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022] +Linked NPCs: Molanisk (5751) +Linked graphics: 1030, 1031, 1032, 1033 +Linked objects: Molanisk (22545) + +Base: 1393 +Linked animations: [6077, 6078, 6079, 6080, 6081, 6082] +Linked NPCs: Cave bug (1832), Cave bug (5750) + +Base: 1394 +Linked animations: [4219, 4220] +Linked objects: Zanik (15709), null (15710) + +Base: 1395 +Linked animations: [1828, 1829, 3847, 3848, 3849, 3854, 3856, 6088, 6089, 6090, 6091, 6092, 6093, 8717, 8718, 8734, 8737, 8738, 8739, 8740] +Linked NPCs: Rabbit (1192), Rabbit (1193), Rabbit (1194), Rabbit (1404), Rabbit (1530), Easter Bunny (3688), Rabbit (4447), Rabbit (4448), Rabbit (5829), Rabbit (7125), Rabbit (7126), Rabbit (7127) +Linked graphics: 645, 943, 1039 + +Base: 1396 +Linked animations: [5237, 5238, 5239, 5240, 5241, 5242] +Linked graphics: 944, 945, 946 +Linked objects: Broken snare (19334), Rabbit snare (19335), Rabbit snare (19336) + +Base: 1397 +Linked animations: [6105] +Linked graphics: 1038 + +Base: 1398 +Linked animations: [3851, 3852] +Linked NPCs: Easter Bunny (1835), Easter Bunny (7197) + +Base: 1399 +Linked animations: [3864] +Linked graphics: 643 + +Base: 1400 +Linked animations: [3850] +Linked graphics: 642 + +Base: 1401 +Linked animations: [6108] +Linked graphics: 644 + +Base: 1402 +Linked animations: [3858] +Linked graphics: 1040, 1041, 1042, 1043, 1044, 1045 + +Base: 1403 +Linked animations: [6120, 11411] +Linked NPCs: Boulder (9670), Boulder (9671) + +Base: 1404 +Linked animations: [6114, 6115, 6116, 6117, 6127] +Linked NPCs: Brine rat (3707) + +Base: 1405 +Linked animations: [6126] +Linked NPCs: Ulfric (3710) + +Base: 1406 +Linked animations: [6119] +Linked graphics: 1048 + +Base: 1407 +Linked animations: [6121] + +Base: 1408 +Linked animations: [6167, 6168] +Linked objects: Council members (23318), Cave gobins (23319) + +Base: 1409 +Linked animations: [6164, 6165, 6166] +Linked objects: Crowd of dwarves (23315), Crowd of dwarves (23316), Crowd of dwarves (23317), Crowd of dwarves (29118), null (29119), null (29120) + +Base: 1410 +Linked animations: [6211] +Linked objects: Zanik (23284) + +Base: 1411 +Linked animations: [6171, 6172, 6173, 6174, 6175, 6176, 6177, 6178, 6179] + +Base: 1412 +Linked animations: [6169] + +Base: 1413 +Linked animations: [6150, 6151] +Linked NPCs: Sigmund and Zanik (5862) + +Base: 1414 +Linked animations: [6152, 6153, 6154, 6155, 6156, 6157, 6158, 7027, 7029] +Linked NPCs: Sergeant Mossfists (5871), Sergeant Slimetoes (5872), null (5889), null (5890), null (5891), null (5892), Sergeant Strongstack (6261), Sergeant Steelwill (6263), Sergeant Grimspike (6265) +Linked graphics: 1201, 1205 + +Base: 1415 +Linked animations: [2963, 2964, 3221, 3384, 3385, 3386, 3387, 3388, 3389, 3391, 3395, 3400, 3401, 3402, 6134, 6135, 6136, 6137, 6138, 6139, 6140, 6141, 6180, 6181, 6182, 6183, 6184, 6185, 6186, 6187, 6188, 6189, 6190, 6192, 6193, 6195, 6199, 6200, 6201, 6202, 6203, 6204, 6827, 6829, 6830, 6833, 6834, 6835, 6836, 6837, 7024, 7305, 7306, 7308, 7309, 7310, 7311, 7315, 7316, 7317, 7318, 7319, 7320, 7323, 7324, 7325, 7326, 7327, 7330, 7331, 7332, 7333, 9650, 9963, 9964, 9965, 9966, 10233, 10240, 11275, 11278, 11279, 11285, 11294, 11297, 11303, 11320, 11321, 11338, 11361, 11374, 11394, 11395, 11396, 11397, 11398, 11800, 11801, 11848, 12301, 12637, 12638, 12654, 12655] +Linked NPCs: Goblin (100), Goblin (101), Goblin (102), High Priest (420), High Priest (421), Goblin (444), Goblin (445), Goblin guard (489), Greldo (612), Wormbrain (745), Goblin (1769), Goblin (1770), Goblin (1771), Goblin (1772), Goblin (1773), Goblin (1774), Goblin (1775), Goblin (1776), Goblin (2274), Goblin (2275), Goblin (2276), Goblin (2277), Goblin (2278), Goblin (2279), Goblin (2280), Goblin (2281), High Priest (2535), Bandos avatar (2541), Goblin (2678), Goblin (2679), Goblin (2680), Goblin (2681), Goblin Champion (3060), Goblin (3264), Goblin (3265), Goblin (3266), Goblin (3267), General Wartface (3391), General Bentnoze (3392), Goblin Cook (3413), Goblin Cook (3414), Goblin Cook (3415), General Wartface (3479), Angry goblin (3648), Angry goblin (3663), General Bentnoze (3723), General Wartface (3724), Grubfoot (3725), Goblin (3726), Grubfoot (3849), Goblin (4261), Goblin (4262), Goblin (4263), Goblin (4264), Goblin (4265), Goblin (4266), Goblin (4267), Goblin (4268), Goblin (4269), Goblin (4270), Goblin (4271), Goblin (4272), Goblin (4273), Goblin (4274), Goblin (4275), Goblin (4276), Goblin (4407), Goblin (4408), Goblin (4409), Goblin (4410), Goblin (4411), Goblin (4412), Prisoner (4416), Grubfoot (4417), Goblin (4479), Goblin (4480), Goblin (4481), Goblin (4482), Goblin (4483), Goblin (4484), Goblin (4485), Goblin (4486), Goblin (4487), Goblin (4488), Goblin (4489), Goblin (4490), Goblin (4491), Goblin (4492), General Bentnoze (4493), General Wartface (4494), null (4495), Grubfoot (4496), Grubfoot (4497), Grubfoot (4498), Goblin (4499), Grubfoot (4536), Goblin (4633), Goblin (4634), Goblin (4635), Goblin (4636), Goblin (4637), Goblin (5855), Goblin (5856), Goblin statue (5908), Sleeping guard (6122), Naghead (6123), Wagchin (6124), Goblin (6125), Goblin (6126), Smellytoes (6128), Creakyknees (6129), Clothears (6130), Goblin (6279), Goblin (6280), Goblin (6281), Goblin (6282), Goblin (6283), Goblin (6402), Goblin (6403), Goblin (6404), Goblin (6405), Goblin (6406), Goblin (6407), Goblin (6408), Goblin (6409), Goblin (6410), Goblin (6411), Goblin (6412), Goblin (6413), Goblin (6414), Goblin (6415), Goblin (6416), Goblin (6417), Goblin (6418), Goblin (6419), Goblin (6420), Goblin (6421), Goblin (6422), Goblin (6423), Goblin (6424), Goblin (6425), Goblin (6426), Goblin (6427), Goblin (6428), Goblin (6429), Goblin (6430), Goblin (6431), Goblin (6432), Goblin (6433), Goblin (6434), Goblin (6435), Goblin (6436), Goblin (6437), Goblin (6438), Goblin (6439), Goblin (6440), Goblin (6441), Goblin (6442), Goblin (6443), Goblin (6444), Goblin (6445), Goblin (6446), Goblin (6447), Goblin (6448), Goblin (6449), Goblin (6450), Goblin (6451), Goblin (6452), Goblin (6453), Goblin (6454), Goblin (6455), Goblin (6456), Goblin (6457), Goblin (6458), Goblin (6459), Goblin (6460), Goblin (6461), Goblin (6462), Goblin (6463), Goblin (6464), Goblin (6465), Goblin (6466), Goblin (6467), Skoblin (6468), Snothead (6469), Snailfeet (6470), Mosschin (6471), Redeyes (6472), Strongbones (6473), Snothead (6474), Snailfeet (6475), Mosschin (6476), Redeyes (6477), Strongbones (6478), Grubfoot (6479), null (6480), Grubfoot (6481), Priest (6482), Priest (6483), Priest (6484), Priest (6485), Priest (6486), Priest (6487), Preacher (6488), null (6489), Goblin (6490), Goblin (6491), Goblin (6492), Goblin (6493), Goblin (6494), Goblin (6495), Goblin guard (6496), Goblin guard (6497), Guard (6498), Guard (6499), Guard (6500), Guard (6501), Guard (6502), Guard (6503), General Bentnoze (7444), General Wartface (7892), General Bentnoze (7893), Goblin (7964), Goblin (7965), null (8072), Mystic (8231), Mystic (8236), null (8656), null (8657), Goblin musician (8712), Goblin (8862), null (8936), Junior Cadet Grimeface (8937), General Wartface (9408), General Bentnoze (9410), General Bentnoze (9412), General Wartface (9414), Skoblin (9611), Goblin courier (9683), Phlegm Knee (9690), Goblin (11259), Goblin (11260), Goblin (11261) +Linked graphics: 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259 +Linked objects: Goblin statue (42586), Goblin statue (42587), Goblin statue (42603), Goblin statue (42606), Goblin cook (45315) + +Base: 1416 +Linked animations: [6142, 6191] +Linked graphics: 1049, 1053 + +Base: 1417 +Linked animations: [6144, 6145] +Linked graphics: 1050, 1051 + +Base: 1418 +Linked animations: [6148] +Linked graphics: 1052 + +Base: 1419 +Linked animations: [6196, 6461] +Linked objects: Sample table (23526), Sample table (23527), null (24552), null (24553), null (24554) + +Base: 1420 +Linked animations: [6216, 6460] +Linked graphics: 1054, 1076 + +Base: 1421 +Linked animations: [6243, 6244, 6245, 6246, 7630, 7631, 7632, 7636, 7637, 7638, 7639, 7641, 8380, 8381, 8382, 8383, 8384, 8385, 8386, 8387, 8475, 8476, 8477, 8478, 9358, 10553, 11605, 11606, 11607, 11608] + +Base: 1422 +Linked animations: [1172, 1173, 1185, 1188, 6218, 6219, 6220, 6221, 6222, 6223, 6224, 6225, 6226, 6227, 6228, 6229, 6230, 6231, 6232, 6238, 6239, 6240, 6241, 6242, 6476, 6477, 8516, 8517, 8518, 8519, 8531] +Linked NPCs: Kalphite Worker (1153), Kalphite Soldier (1154), Kalphite Guardian (1155), Kalphite Worker (1156), Kalphite Guardian (1157), Kalphite Queen (1158), Kalphite Larva (1161), Kalphite Soldier (3589), Kalphite Queen (3835), Spirit kalphite (6994), Spirit kalphite (6995), Kalphite worker (11621) +Linked graphics: 278 +Linked objects: Kalphite Soldier (13374), Kalphite Queen (15490) + +Base: 1423 +Linked animations: [6269] +Linked graphics: 1055 +Linked objects: Kalphite queen legs (23591) + +Base: 1424 +Linked animations: [6271] +Linked graphics: 280 + +Base: 1425 +Linked animations: [1170, 1171, 1178, 1250, 6233, 6234, 6235, 6236, 6237, 6270] +Linked NPCs: Kalphite Queen (1160), Kalphite Queen (3836) +Linked graphics: 279 + +Base: 1426 +Linked animations: [6252, 6253, 6254, 6255, 6256, 6257, 6258, 6259, 6260, 6261, 6262, 6263, 6264, 6265, 6266, 6267, 8123, 8124, 8127, 8546] +Linked NPCs: Scorpion (107), Poison Scorpion (108), Pit Scorpion (109), King Scorpion (144), Khazard scorpion (271), Kharid Scorpion (385), Kharid Scorpion (386), Kharid Scorpion (387), Grave scorpion (493), Scorpion (1477), Giant lobster (1693), Scorpion (4402), Scorpion (4403), null (4891), Giant lobster (4892), Giant Lobster (4893), Spirit scorpion (6837), Spirit scorpion (6838), null (7523), Giant lobster (7828), Lobster (9720) + +Base: 1427 +Linked animations: [6247, 6248, 6249, 6250, 6251] +Linked NPCs: Spider (61), Spider (1004), Spider (1221), Spider (1473), Spider (1474), Crypt spider (2034), Spider (4401), Spider Courtier (8972), Eek the Spider (8985) + +Base: 1428 +Linked animations: [6272, 6273, 6274] +Linked objects: Cocoon (23611) + +Base: 1429 +Linked animations: [6275, 6276, 6277, 6278, 6279] + +Base: 1430 +Linked animations: [4129] +Linked objects: Sign (23980) + +Base: 1431 +Linked animations: [4127] +Linked objects: Lantern (24026) + +Base: 1432 +Linked animations: [6466] +Linked objects: Swings (24094), Swings (34572) + +Base: 1433 +Linked animations: [6467] +Linked objects: Washing line (24095), null (34575), null (43044) + +Base: 1434 +Linked animations: [4130] +Linked objects: Hearth (23963) + +Base: 1435 +Linked animations: [6492] +Linked objects: Fountain (24265) + +Base: 1436 +Linked animations: [4124] +Linked objects: Cooking pot (24328), Fireplace (24329), Fireplace (35449), Fireplace (36816) + +Base: 1437 +Linked animations: [6491] +Linked objects: null (24017) + +Base: 1438 +Linked animations: [4131] +Linked objects: null (23979) + +Base: 1439 +Linked animations: [4132] +Linked objects: Chimney (15487), Smoking vent (24194), Smoking vent (24195) + +Base: 1440 +Linked animations: [6343, 6344, 6345, 6346, 6347, 6356] +Linked NPCs: The Everlasting (5903) + +Base: 1441 +Linked animations: [6338, 6339, 6340, 6341, 6342, 6348, 6349, 6350, 6351] +Linked NPCs: The Illusive (5905), The Illusive (5907) + +Base: 1442 +Linked animations: [6317, 6318, 6319, 6320, 6321, 6322, 6324, 6325, 6354] +Linked NPCs: The Inadequacy (5902) + +Base: 1443 +Linked animations: [6310, 6311, 6312, 6313, 6314, 6315] +Linked NPCs: A Doubt (5906) + +Base: 1444 +Linked animations: [6328, 6329, 6330, 6331, 6332] +Linked NPCs: The Untouchable (5904) + +Base: 1445 +Linked animations: [6307] +Linked graphics: 1061 + +Base: 1446 +Linked animations: [6353] +Linked graphics: 1074 + +Base: 1447 +Linked animations: [6308] +Linked graphics: 1063 + +Base: 1448 +Linked animations: [6309] +Linked graphics: 1062 + +Base: 1449 +Linked animations: [6327] +Linked graphics: 1067 + +Base: 1450 +Linked animations: [6326] +Linked graphics: 1066 + +Base: 1451 +Linked animations: [6352] +Linked graphics: 1075 + +Base: 1452 +Linked animations: [6306] +Linked graphics: 1059, 1060 + +Base: 1453 +Linked animations: [6316] +Linked graphics: 1064 + +Base: 1454 +Linked animations: [6335, 6336] +Linked graphics: 1071, 1072 + +Base: 1455 +Linked animations: [6337] +Linked graphics: 1073 + +Base: 1456 +Linked animations: [6323, 6355] +Linked graphics: 1065, 1068 + +Base: 1457 +Linked animations: [6333, 6334] +Linked graphics: 1069, 1070 + +Base: 1458 +Linked animations: [6357, 6358, 6498] +Linked graphics: 1057, 1058, 1077 + +Base: 1459 +Linked animations: [6481, 6482, 6483] +Linked objects: Dummy (23921), Dummy (25648), Dummy (46957) + +Base: 1460 +Linked animations: [4128, 12647] +Linked objects: Sign (23967), Sign (23968), Sign (23969), Sign (23970), null (23971), Sign (23972), null (23973), Sign (23974), null (23975), null (23976), Sign (23977), Sign (23978), null (23981), Sign (23982), Sign (25762), Sign (26971), Pet shop (28351), Sign (28623), Signpost (31297), Signpost (31299), Sign (35649), null (36901), null (36902), Poison Arrow pub (44292), Sign (45179), Sign (45180), Sign (45181), null (47749), null (47750), null (47751), null (47752), null (47753), null (47754), null (52460), null (52461), null (52462) + +Base: 1461 +Linked animations: [6470] +Linked objects: Wheat (15506), Wheat (15508) + +Base: 1462 +Linked animations: [6493] +Linked objects: Mill (24067), null (36875) + +Base: 1463 +Linked animations: [6494] +Linked objects: Mill (24068), Mill (36877), Flour bin (52549), Flour bin (52550) + +Base: 1464 +Linked animations: [6495] +Linked objects: Sails (24065), Sails (36872), Sails (36873), Sails (46009), null (46010) + +Base: 1465 +Linked animations: [6426] +Linked objects: null (24417), null (24418) + +Base: 1466 +Linked animations: [6427] + +Base: 1467 +Linked animations: [6450, 6451] +Linked NPCs: Mole display (5982) + +Base: 1468 +Linked animations: [6430, 6431] +Linked NPCs: Snail display (5973) + +Base: 1469 +Linked animations: [6452, 6453] +Linked objects: Snake (24569) + +Base: 1470 +Linked animations: [6444, 6445] +Linked NPCs: Wyvern display (5980) + +Base: 1471 +Linked animations: [6432, 6433] +Linked NPCs: Sea slugs display (5972) + +Base: 1472 +Linked animations: [6373, 6374, 6375, 6376, 6377, 8265, 8266, 8267, 8565] +Linked NPCs: Unicorn (89), Black unicorn (133), Unicorn (987), Unicorn Foal (1328), Black unicorn Foal (1329), Angry unicorn (3646), Angry unicorn (3661), Stag (4440), Starlight (6248), Unicorn stallion (6822), Unicorn stallion (6823) + +Base: 1473 +Linked animations: [6434, 6435] +Linked NPCs: Monkey display (5974) + +Base: 1474 +Linked animations: [6463] + +Base: 1475 +Linked animations: [6412, 6413] +Linked NPCs: Teacher and pupil (5947) + +Base: 1476 +Linked animations: [6410, 6411] +Linked NPCs: Teacher and pupil (5944) + +Base: 1477 +Linked animations: [6414, 6415, 6416, 6417, 6418, 6454, 6455, 6456, 6457, 6458] +Linked NPCs: Workman (5952), Workman (5953), Barge workman (5959), Barge workman (5960), Barge workman (5961), Barge workman (5962), Barge foreman (5963) + +Base: 1478 +Linked animations: [6419, 6420, 6421, 6422, 6423] +Linked NPCs: Workman (5954), Workman (5955), Workman (5986), Workman (6159) + +Base: 1479 +Linked animations: [4123] +Linked objects: null (9375) + +Base: 1480 +Linked animations: [6497, 6499] +Linked objects: Beanstalk (24732), Beanstalk (24734) + +Base: 1481 +Linked animations: [6522] +Linked objects: Gramophone (24724), Gramophone (24725), Gramophone (24726), Gramophone (24727), null (24882) + +Base: 1482 +Linked animations: [6523] +Linked objects: Floor (24843), Floor (24844), Floor (24845), Floor (24846), Floor (24847) + +Base: 1483 +Linked animations: [6509] +Linked objects: Rupert (24771), null (24773), null (24774) + +Base: 1484 +Linked animations: [6510, 6511, 6512, 6513, 6514, 6515] +Linked NPCs: Experiment No.2 (5993) + +Base: 1485 +Linked animations: [6501, 6502, 6503, 6504, 6505, 6524] +Linked NPCs: Glod (5996) + +Base: 1486 +Linked animations: [6517, 6518, 6519, 6520, 6521, 11546] +Linked NPCs: Mouse (5994), Mouse (5995), Mouse (7205) + +Base: 1487 +Linked animations: [6506, 6507, 6508] +Linked NPCs: Grimgnash (5989), null (5997) +Linked objects: Grimgnash (24839), null (24840) + +Base: 1488 +Linked animations: [6516] +Linked graphics: 1078 + +Base: 1489 +Linked animations: [225, 1518, 6550, 6551, 6552, 6553, 6555, 8371, 8372, 8373, 8374, 8375, 8581, 8582, 9178, 9179, 9180, 9181, 9182, 9183, 9184, 9185, 9186, 9187, 9188, 9189, 9190, 9191, 9192, 9193, 9201, 9202, 9203, 9225, 10552, 11536, 11710] + +Base: 1490 +Linked animations: [6536, 6537, 6538, 6539, 6540, 6541, 6542, 6543, 6544, 6545, 6546, 6547, 6548, 6549] +Linked NPCs: Skullball Boss (1660), Agility Boss (1661), Skullball Trainer (1662), Agility Trainer (1663), Agility Trainer (1664), Werewolf (1665), Mystery figure (3055), Shadowy figure (4762), Shadowy figure (4763), Shadowy figure (4764), Werewolf (6006), Werewolf (6007), Werewolf (6008), Werewolf (6009), Werewolf (6010), Werewolf (6011), Werewolf (6012), Werewolf (6013), Werewolf (6014), Werewolf (6015), Werewolf (6016), Werewolf (6017), Werewolf (6018), Werewolf (6019), Werewolf (6020), Werewolf (6021), Werewolf (6022), Werewolf (6023), Werewolf (6024), Werewolf (6025), Werewolf (6212), Werewolf (6213) +Linked graphics: 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098 + +Base: 1491 +Linked animations: [6556, 6557, 6558, 6559, 6560, 6561, 6562, 6563, 6564, 6565, 6566, 6567, 6568, 6569, 6570, 6571, 6572, 6573, 6574, 6575, 6576, 6577, 6578, 6579, 6580, 6581, 6582, 6583, 7254, 7575, 7576, 8291, 8292, 8293, 8294, 8295, 8296, 8297, 8298, 8329, 8330, 8331, 8332, 8333, 8334, 8335, 8336, 8337, 8338, 8339, 8340, 8341, 8532, 12297] +Linked NPCs: Hellhound (49), Wolf (95), White wolf (96), White wolf (97), Dog (98), Guard dog (99), Big Wolf (141), Wolf (142), Jungle Wolf (143), Bouncer (269), Sinclair Guard dog (821), Desert Wolf (839), Witch's experiment (fourth form) (900), Dire Wolf (1198), Vampyric hound (1224), Fox (1319), Fenris wolf (1330), Ice wolf (1558), Ice wolf (1559), Skeleton Hellhound (1575), Wild dog (1593), Wild dog (1594), Ice wolf (1951), Ice wolf (1952), Ice wolf (1953), Ice wolf (1954), Ice wolf (1955), Ice wolf (1956), Shadow Hound (1976), Jackal (1994), Muncher (2329), Guard dog (3582), Hellhound (3586), Wolf (4413), Wolf (4414), Bouncer (5564), Bouncer (5565), Big Wolf (6046), Wolf (6047), Wolf (6048), Wolf (6049), Desert Wolf (6050), Desert Wolf (6051), Ice wolf (6052), Hellhound (6210), Guard dog (6374), Bulldog (6752), Spirit wolf (6829), Spirit wolf (6830), Terrier (6894), Labrador (6895), Dalmatian (6896), Bulldog (6897), Bulldog (6899), Terrier puppy (6958), Terrier (6959), Greyhound puppy (6960), Greyhound (6961), Labrador puppy (6962), Labrador (6963), Dalmatian puppy (6964), Dalmatian (6965), Sheepdog puppy (6966), Sheepdog (6967), Bulldog (6968), Bulldog puppy (6969), Big wolf (7005), Terrier puppy (7237), Terrier (7238), Terrier puppy (7239), Terrier (7240), Greyhound puppy (7241), Greyhound (7242), Greyhound puppy (7243), Greyhound (7244), Labrador puppy (7245), Labrador (7246), Labrador puppy (7247), Labrador (7248), Dalmatian puppy (7249), Dalmatian (7250), Dalmatian puppy (7251), Dalmatian (7252), Sheepdog puppy (7253), Sheepdog (7254), Sheepdog puppy (7255), Sheepdog (7256), Bulldog (7257), Bulldog (7258), Bulldog puppy (7259), Bulldog puppy (7260), null (7521), Temple guardian (7711), Snowy (8017), Muncher (8868), Muncher (8983), Terrier (9190), Terrier (9191), Terrier (9192), Greyhound (9193), Greyhound (9194), Greyhound (9195), Labrador (9196), Labrador (9197), Labrador (9198), Dalmatian (9199), Dalmatian (9200), Dalmatian (9201), Sheepdog (9202), Sheepdog (9203), Sheepdog (9204), Bulldog (9205), Bulldog (9206), Bulldog (9207), Spirit wolf (9468), Hellhound (10188), Hellhound (10189), Hellhound (10190), Hellhound (10191), Hellhound (10192), Hellhound (10193), Hellhound (10194), Hellhound (10195), Guard dog (10726), Guard dog (10727), Guard dog (10728), Guard dog (10729), Guard dog (10730), Guard dog (10731), Guard dog (10732), Guard dog (10733), Guard dog (10734), Guard dog (10735), Wolf (11262), Wolf (11263), Pit dog (11562), Pit dog (11584), Pit dog (11585) +Linked objects: Hellhound (2715), Guard dog (13367), Pit dog (39260), Pit dog (41589) + +Base: 1492 +Linked animations: [370, 581] +Linked graphics: 232, 233, 234, 235, 236, 237, 273, 1123, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1242 + +Base: 1493 +Linked animations: [6623, 6624, 6625, 6626, 6627] +Linked objects: Crop circle (24984), Crop circle (24985), Crop circle (24986), Crop circle (24987), Centre of crop circle (24988), Magic wheat (24989), Magic wheat (24990), Centre of crop circle (24991) + +Base: 1494 +Linked animations: [6613, 6614, 6615, 6617, 6618, 11511, 11515, 12263, 12264, 12831, 12833, 12834] +Linked NPCs: Baby impling (1028), Young impling (1029), Gourmet impling (1030), Earth impling (1031), Essence impling (1032), Eclectic impling (1033), Nature impling (1034), Magpie impling (1035), Ninja impling (6053), Dragon impling (6054), Baby impling (6055), Young impling (6056), Gourmet impling (6057), Earth impling (6058), Essence impling (6059), Eclectic impling (6060), Nature impling (6061), Magpie impling (6062), Ninja impling (6063), Dragon impling (6064), Immenizz (6071), Wandering impling (6073), Easter impling (7420), Impling worker (7421), Impling worker (7422), Impling worker (7424), Impling manager (7425), Impling worker (7426), Pirate impling (7845), Pirate impling (7846), Spirit impling (7866), Pirate impling (7867), Zombie impling (7902), Kingly impling (7903), Spirit impling (7904), Zombie impling (7905), Kingly impling (7906), null (9698), null (9699), null (9702), Impling manager (9703), Impling manager (9704), Impling manager (9705), Impling worker (9706) +Linked objects: Impling worker (45799), Impling worker (49102), Impling worker (49103), Impling worker (49104), Impling worker (49105), Impling manager (49106) + +Base: 1495 +Linked animations: [6622] +Linked graphics: 1120, 1122 + +Base: 1496 +Linked animations: [6619, 6620, 6621] +Linked graphics: 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 2327, 2328, 2329, 2330, 2331, 2332, 2333, 2334, 2335, 2336, 2337, 2338 + +Base: 1497 +Linked animations: [6596, 6597, 6598, 6599] +Linked objects: Growing wheat (25022), Wilting wheat (25023) + +Base: 1498 +Linked animations: [6591] +Linked graphics: 1117 + +Base: 1499 +Linked animations: [6602] +Linked graphics: 1118 + +Base: 1500 +Linked animations: [6612] +Linked graphics: 1119 + +Base: 1501 +Linked animations: [6585, 6586] +Linked graphics: 1099, 1100 + +Base: 1502 +Linked animations: [6587, 6588, 6589, 6590] +Linked graphics: 1101, 1102, 1103 + +Base: 1503 +Linked animations: [2729, 2730, 2731, 2732, 2733, 13840, 13841] +Linked NPCs: Dark beast (2783), Kolodion (11302) + +Base: 1504 +Linked animations: [6656, 9647] +Linked objects: Flag (25205), Flag (35145) + +Base: 1505 +Linked animations: [6635, 6636, 6637] +Linked objects: Magic door (25115), Magic door (25116), Magic door (25118) + +Base: 1506 +Linked animations: [6638] +Linked objects: Candle stand (25122), Candle stand (25123) + +Base: 1507 +Linked animations: [6639] +Linked objects: Vines (25136) + +Base: 1508 +Linked animations: [6644] + +Base: 1509 +Linked animations: [6641] + +Base: 1510 +Linked animations: [6645] +Linked objects: Fire (25155) + +Base: 1511 +Linked animations: [6646, 6853, 12320, 12373] +Linked objects: Fire (25156), Fire (25465), Fire (27297), Altar (27306), null (30452), Torch (30794), Fire (41687), null (41688), Torch (42148), Torch (42149), Torch (43030), Torch (46329), Torch (48115) + +Base: 1512 +Linked animations: [6852] +Linked graphics: 1173 + +Base: 1513 +Linked animations: [6652] +Linked objects: Jenkins (25157) + +Base: 1514 +Linked animations: [6640] + +Base: 1515 +Linked animations: [6648] +Linked graphics: 1155 + +Base: 1516 +Linked animations: [6647] +Linked graphics: 1154 + +Base: 1517 +Linked animations: [6698] +Linked graphics: 1166 + +Base: 1518 +Linked animations: [6699] +Linked graphics: 1167 + +Base: 1519 +Linked animations: [6697] +Linked graphics: 1165 + +Base: 1520 +Linked animations: [6690, 6691, 6692, 6693, 6694, 6701] +Linked graphics: 1160, 1161, 1162, 1163, 1164, 1168 + +Base: 1521 +Linked animations: [6667, 6668, 6669, 6670] +Linked graphics: 1156, 1157, 1158, 1159 + +Base: 1522 +Linked animations: [6737] +Linked objects: Whirlpool (25274), Whirlpool (25275) + +Base: 1523 + +Base: 1524 +Linked animations: [6731, 6732] +Linked objects: Mighty torrent (25276), Mighty torrent (25277), Mighty torrent (25278), Mighty torrent (25279), null (25280), null (25281), null (25282), null (25283), null (25284), null (25285), null (33484), null (33485), null (33486), null (33487) + +Base: 1525 +Linked animations: [6733, 6734, 6735, 6736, 9640] +Linked objects: funeral pyre (25267), Pyre boat (25292), Pyre boat (25293), Pyre boat (25294), Pyre boat (25295) + +Base: 1526 +Linked animations: [6725, 6726, 6727, 6728, 6729, 6730] +Linked NPCs: Angry barbarian spirit (749), Enraged barbarian spirit (750), Berserk barbarian spirit (751), Ferocious barbarian spirit (752) + +Base: 1527 +Linked animations: [6721] +Linked graphics: 1169 + +Base: 1528 + +Base: 1529 + +Base: 1530 + +Base: 1531 + +Base: 1532 + +Base: 1533 +Linked animations: [1007, 1008, 1009, 1010, 1012, 1013, 6442, 6443, 6759, 6769, 6770, 6789, 6790, 6791, 6792, 6793, 6794, 6795, 6796, 6797, 6798, 6799, 8229, 8230, 8231, 8557] +Linked NPCs: Mounted terrorchick gnome (136), Terrorchick gnome (137), Terrorbird (138), Terrorbird (139), Terrorbird (1751), Mounted terrorbird gnome (1752), Mounted terrorbird gnome (1753), Terrorbird display (5978), Mounted terrorbird gnome (6109), Mounted terrorbird gnome (6110), Mounted terrorbird gnome (6111), Spirit terrorbird (6794), Spirit terrorbird (6795) + +Base: 1534 +Linked animations: [6784, 6785, 6786, 6787, 6788] +Linked NPCs: Crow (1754), Crow (1755), Crow (1756), Crow (2252), Crow (4570), Crow (4571), Rook (9719) + +Base: 1535 +Linked animations: [6808, 6809, 6810, 6811, 6812] +Linked NPCs: Desert Phoenix (1911), Entrana firebird (6108) + +Base: 1536 +Linked animations: [6761, 6762, 6763, 6764, 6765, 6766, 6767, 6768, 6800, 6801, 6802, 6803, 6804, 6805, 6806, 6807, 6823] +Linked NPCs: Chompy bird (1016), Chompy bird (1550), Jubbly bird (3476), Jubbly bird (3477), Jubster (5596) + +Base: 1537 +Linked animations: [8005, 8007, 8008, 8009, 8010, 8011, 8012, 8013, 8014, 8020, 8310, 8311, 8312, 8553, 10230, 10232] +Linked NPCs: Magpie (6824), Macaw (6851), Raven chick (6911), Raven (6912), Vulture chick (6945), Saradomin chick (6949), Saradomin bird (6950), Saradomin owl (6951), Zamorak chick (6952), Zamorak bird (6953), Zamorak hawk (6954), Guthix chick (6955), Guthix bird (6956), Guthix raptor (6957), Raven chick (7261), Raven (7262), Raven chick (7263), Raven (7264), Raven chick (7265), Raven (7266), Raven chick (7267), Raven (7268), Raven chick (7269), Raven (7270), Vulture chick (7319), Vulture chick (7321), Vulture chick (7323), Vulture chick (7325), Vulture chick (7327), Saradomin owl (7816), Ex-ex-parrot (7844), Zamorak hawk (9186), Guthix raptor (9187), Ex-ex-parrot (9212), Raven (9239), Raven (9240), Raven (9241), Raven (9242), Raven (9243), Raven (9244) +Linked graphics: 1778 + +Base: 1538 +Linked animations: [5171, 5172, 5173, 5174, 6771, 6772, 6773, 6774, 6775, 6776, 6777, 6778, 6779, 6780, 6781, 6782, 6783, 8006] +Linked NPCs: Gull (146), Cormorant (147), Pelican (148), Gull (149), Gull (150), Gull (450), Gull (451), Gull (1179), Cormorant (1180), Pelican (1181), Gull (1322), Gull (1323), Gull (1324), Gull (1325), Gull (1400), Bird (1475), Bird (1476), Seagull (2707), Seagull (2708), Gull (2726), Gull (2727), Gull (3197), Tropical wagtail (5072), Crimson swift (5073), Cerulean twitch (5074), Golden warbler (5075), Copper longtail (5076), Tenacious toucan (6378), Pernicious parrot (6382), Macaw (6852), Wimpy bird (7031), null (7032), null (7033), null (7034), null (7035), null (7036), Gull (7860), Gull (9334), Gull (9717), Cormorant (9718) +Linked objects: Bird snare (19177), Bird snare (19178), Bird snare (19179), Bird snare (19180), Bird snare (19181), Bird snare (19182), Bird snare (19183), Bird snare (19184), Bird snare (19185), Bird snare (19186), Bird snare (28930), Bird snare (28931), Bird snare (28932), Bird snare (28933), Bird snare (28934), Bird snare (29164), Bird snare (29165) + +Base: 1539 +Linked animations: [747, 750, 1014, 1015, 3268, 3467, 3468, 6813, 6815, 6817, 6818, 6819, 6821, 6822] +Linked NPCs: Duck (46), Duck (2693), Duckling (2694), Ducklings (6112), Duck (6113), Drake (6114), Seagull (6115), Seagull (6116) + +Base: 1540 +Linked animations: [7, 8, 9, 6824, 10556, 11540] + +Base: 1541 +Linked animations: [6850, 6851] + +Base: 1542 +Linked animations: [6857] +Linked NPCs: Giant (487) + +Base: 1543 +Linked animations: [6858, 6859] +Linked NPCs: Goblin (6132), Goblin (6133) + +Base: 1544 +Linked animations: [6856] +Linked NPCs: Mummy (490) + +Base: 1545 +Linked animations: [6855] +Linked NPCs: Zombie (6131) + +Base: 1546 +Linked animations: [6854] +Linked objects: Rocking unicorn (25426) + +Base: 1547 +Linked animations: [6860] + +Base: 1548 +Linked animations: [6861] + +Base: 1549 +Linked animations: [6888, 6889] +Linked graphics: 1176, 1177 + +Base: 1550 +Linked animations: [6877] +Linked graphics: 1175 + +Base: 1551 +Linked animations: [6873, 6874, 6875] +Linked objects: Novice Flag (25620), Intermediate Flag (25621), Veteran Flag (25622), Novice flag (25623), Intermediate Flag (25624), Veteran Flag (25625), null (25626), null (25627), null (25628), null (25634), null (25635), Novice Flag (41519), Intermediate Flag (41520), Veteran Flag (41521) + +Base: 1552 +Linked animations: [1431, 6900, 6901, 6902, 6903, 6904, 6905, 6906, 6907, 6908, 6909, 6910, 6911, 14153, 14154, 14155, 14156, 14157, 14158, 14159, 14160, 14161, 14162, 14163, 14166, 14167, 14168, 14169, 14170, 14171, 14172, 14173, 14174] +Linked objects: The Jury (4414), The Jury (4416), The Jury (4417), The Jury (4418), The jury (25944), The jury (25945), The jury (25946), The jury (25947) + +Base: 1553 +Linked animations: [6916, 6925] +Linked objects: Sir Tristram (25889) + +Base: 1554 +Linked animations: [6898, 6899] +Linked objects: Judge (4420), Court judge (25956) + +Base: 1555 +Linked animations: [6921] + +Base: 1556 +Linked animations: [6890, 6891, 6892, 6893, 6894, 6895] +Linked objects: Citizen (26045), Citizen (26046), Citizen (26047), Citizen (26048), Citizen (26049), Citizen (26050) + +Base: 1557 +Linked animations: [6920] + +Base: 1558 +Linked animations: [6924] +Linked graphics: 1179 + +Base: 1559 +Linked animations: [6930] +Linked graphics: 1180 + +Base: 1560 + +Base: 1561 +Linked animations: [6931] +Linked objects: Fly paper (26137) + +Base: 1562 +Linked animations: [1549, 6934, 7625, 7626, 9386, 9387] +Linked objects: Lever (31358), Candle sconce (47405), Lever (47406) + +Base: 1563 +Linked animations: [6936, 6937, 6938] +Linked graphics: 1181, 1182, 1183 + +Base: 1564 +Linked animations: [4310, 4311, 7009, 7010, 7011, 14089, 14090] +Linked NPCs: Centaur (4438), Centaur (4439), Bree (6252) + +Base: 1565 +Linked animations: [7076, 12020] +Linked graphics: 1224, 2115 + +Base: 1566 +Linked animations: [7025, 7026, 7028] +Linked graphics: 1202, 1203, 1204 + +Base: 1567 +Linked animations: [7037, 7038] +Linked graphics: 1212, 1213 + +Base: 1568 +Linked animations: [6969] +Linked graphics: 1195 + +Base: 1569 +Linked animations: [6987] + +Base: 1570 +Linked animations: [7035, 7036] +Linked graphics: 1210, 1211 + +Base: 1571 +Linked animations: [6968] +Linked graphics: 1194 + +Base: 1572 +Linked animations: [7032, 7034] +Linked graphics: 1208, 1209 + +Base: 1573 +Linked animations: [7031] +Linked graphics: 1207 + +Base: 1574 +Linked animations: [7021] +Linked graphics: 1185 + +Base: 1575 +Linked animations: [6970] +Linked graphics: 1196, 1197, 1198 + +Base: 1576 +Linked animations: [6958] +Linked graphics: 1191 + +Base: 1577 +Linked animations: [6957] +Linked graphics: 1190 + +Base: 1578 +Linked animations: [6959, 6960] +Linked graphics: 1192, 1199 + +Base: 1579 +Linked animations: [6961] +Linked graphics: 1193 + +Base: 1580 +Linked animations: [7020, 7022] +Linked graphics: 1184, 1186 + +Base: 1581 +Linked animations: [7030] +Linked graphics: 1206 + +Base: 1582 +Linked animations: [7068, 11994] +Linked graphics: 1220, 2109 + +Base: 1583 +Linked animations: [7006] +Linked graphics: 1200 + +Base: 1584 +Linked animations: [7077, 11992] +Linked graphics: 1223, 2114 + +Base: 1585 +Linked animations: [7012, 7013] +Linked graphics: 1187, 1188 + +Base: 1586 + +Base: 1587 +Linked animations: [7075, 11990] +Linked graphics: 1222, 2113 + +Base: 1588 +Linked animations: [6948] +Linked graphics: 1189 + +Base: 1589 +Linked animations: [6972, 6973, 6974, 6975, 6976, 6977] +Linked NPCs: Kree'arra (6222) + +Base: 1590 +Linked animations: [6949, 6950, 6951, 6952, 6953, 6954, 6955, 6956, 6962, 6963, 6964, 6965, 6966, 6967, 6971, 7432, 7433, 7434, 7435, 7436, 7437, 7438] +Linked NPCs: Wingman Skree (6223), Flockleader Geerin (6225), Flight Kilisa (6227), Spiritual warrior (6229), Spiritual ranger (6230), Spiritual mage (6231), Aviansie (6232), Aviansie (6233), Aviansie (6234), Aviansie (6235), Aviansie (6236), Aviansie (6237), Aviansie (6238), Aviansie (6239), Aviansie (6240), Aviansie (6241), Aviansie (6242), Aviansie (6243), Aviansie (6244), Aviansie (6245), Aviansie (6246), Commander Zilyana (6247) + +Base: 1591 +Linked animations: [7067] +Linked objects: null (26472), null (26478), null (26481) + +Base: 1592 +Linked animations: [6983] + +Base: 1593 +Linked animations: [7007, 7008] +Linked objects: Waterfall (26418), Waterfall (26419), Waterfall (26420), Waterfall (26421), Waterfall (26422) + +Base: 1594 +Linked animations: [6996] +Linked objects: Snowfall (26417) + +Base: 1595 +Linked animations: [6982] +Linked objects: Big hole (26291) + +Base: 1596 +Linked animations: [7066] +Linked objects: null (26468), null (26471), null (26473), null (26476), null (26477), null (26479), null (26482), null (26483) + +Base: 1597 +Linked animations: [6980, 6981] + +Base: 1598 +Linked animations: [6995] +Linked objects: Snow (26415) + +Base: 1599 +Linked animations: [7053] +Linked graphics: 1214 + +Base: 1600 +Linked animations: [7057] +Linked graphics: 1218 + +Base: 1601 +Linked animations: [7056] +Linked graphics: 1217 + +Base: 1602 +Linked animations: [7055] +Linked graphics: 1216 + +Base: 1603 +Linked animations: [7065, 8119] +Linked graphics: 1219, 1351 + +Base: 1604 +Linked animations: [7131, 7134, 7136] + +Base: 1605 +Linked animations: [7140, 7141, 10905] + +Base: 1606 +Linked animations: [7130] + +Base: 1607 +Linked animations: [7132, 7133, 7137] + +Base: 1608 +Linked animations: [7129, 7135] + +Base: 1609 + +Base: 1610 +Linked animations: [7144] +Linked objects: null (26740) + +Base: 1611 +Linked animations: [7097] +Linked objects: null (26597), null (26598), null (26599), null (26600), Pipe (26708), Pipe (26709), Pipe (26710), Pipe (26711), null (26736), null (29242), Wall Pipe (48844) + +Base: 1612 +Linked animations: [7086, 7087, 7145, 7146, 7362] +Linked graphics: 1271 +Linked objects: Gas (26578), Gas jet (26579), null (26741) + +Base: 1613 +Linked animations: [7138] +Linked objects: Spirit tree (26731) + +Base: 1614 +Linked animations: [7119, 7120] +Linked objects: null (26730) + +Base: 1615 +Linked animations: [7114, 7115] +Linked objects: Human food (26727) + +Base: 1616 +Linked animations: [7116, 7117] +Linked objects: Human food (26728), Human food (26729) + +Base: 1617 +Linked animations: [7118] +Linked objects: Healthorg (26726) + +Base: 1618 + +Base: 1619 + +Base: 1620 +Linked animations: [7083, 7085] +Linked graphics: 1228, 1229 + +Base: 1621 +Linked animations: [7107, 8511, 8512] +Linked graphics: 1227, 1467, 1468, 1518 + +Base: 1622 +Linked animations: [7094, 7105, 7106, 7108, 7109, 7110] +Linked NPCs: Warped terrorbird (6285), Warped terrorbird (6286), Warped terrorbird (6287), Warped terrorbird (6288), Warped terrorbird (6289), Warped terrorbird (6290), Warped terrorbird (6291), Warped terrorbird (6292), Warped terrorbird (6293), Warped terrorbird (6294), Warped terrorbird (6295), Warped terrorbird (6322), Warped terrorbird (6323), Warped terrorbird (6324), Warped terrorbird (6325), Warped terrorbird (6326), Warped terrorbird (6327), Warped terrorbird (6328), Warped terrorbird (6329), Warped terrorbird (6330), Warped terrorbird (6331), Warped terrorbird (6332) + +Base: 1623 +Linked animations: [7098, 7099, 7100, 7101, 7102, 7103, 7104, 7111] +Linked NPCs: Terrorbird servant (6313), Guard no. 21 (6314), Guard no. 72 (6315), Guard no. 21 (6316), Guard no. 72 (6317) + +Base: 1624 +Linked animations: [7089, 7090, 7091, 7092, 7093] +Linked NPCs: Warped tortoise (6296), Warped tortoise (6297) + +Base: 1625 +Linked animations: [7148, 8307] +Linked graphics: 1231, 1464 + +Base: 1626 +Linked animations: [7152] +Linked objects: Furnace (26814) + +Base: 1627 +Linked animations: [7168, 7169, 7170, 7171, 7172, 7173, 7174, 7175, 7176, 7177, 7178, 7179, 7180, 8814, 8815, 8824, 9682, 9683, 9684, 9737] + +Base: 1628 +Linked animations: [3803, 3836, 3837, 4789, 4805, 7153, 7154, 7155, 7156, 7157, 7158, 7159, 7160, 7161, 7162, 7163, 7164, 7165, 7166, 7167, 7297, 7298, 7367, 7384, 8719, 8720, 8721, 8722, 8723, 8724, 8725, 8726, 8727, 8728, 8729, 8730, 8731, 8732, 8733, 8807, 8808, 8809, 8817, 8852, 10032, 10033, 10036, 10037, 10038, 10041, 10049, 10050, 10079, 10116, 10117, 10944, 10945, 10946, 10947, 10948, 10958, 10959, 10960, 10962, 11609, 11610, 11611, 11612, 11613, 11614, 11615] +Linked NPCs: Schoolgirl (10), Schoolgirl (17), null (264), null (265), null (266), Hudon (305), Child (355), Child (356), null (697), Billy Rehnison (723), Milli Rehnison (724), Shilop (781), Philop (782), Wilough (783), Kanel (784), Philipe Carnillean (888), Boy (895), Askeladden (1295), Street urchin (1868), Askeladden (2439), Catalina (3551), Ivan (3552), Victor (3553), Helena (3554), null (3623), Smiddi Ryak (3624), Tolna (3644), Ali the Leaflet Dropper (3680), null (4538), Cabin boy (4539), A Meiyerditch child (4746), A Meiyerditch child (4747), A Meiyerditch child (4748), A Meiyerditch child (4749), A Meiyerditch child (4750), A Meiyerditch child (4751), A Meiyerditch child (4752), A Meiyerditch child (4753), A Meiyerditch child (4754), A Meiyerditch child (4755), Kennith (4863), Kennith (4864), null (4865), null (5020), null (5021), null (5022), Jack (5023), Jill (5024), Jeff (5025), Schoolboy (5945), Schoolboy (5946), Schoolgirl (5957), Schoolgirl (5983), Schoolgirl (5984), Schoolgirl (5985), Child (6334), Child (6335), Child (6336), Child (6337), Child (6338), Child (6339), Child (6340), Child (6341), Child (6342), Child (6343), Child (6344), Child (6345), Street urchin (6357), Street urchin (6358), Street urchin (6359), Street urchin (6360), Street urchin (6361), Kennith (6373), Farid Morrisane (ores) (6523), Summer Bonde (6542), Farmer (7128), Farmer (7129), Farmer (7130), Farmer Blinkin (7131), Mrs. Winkin (7132), Kimberly (7168), Kimberly (7169), Kennith (7171), Kennith (7172), Kennith (7173), Kennith (7175), null (7191), null (7192), null (7193), null (7194), null (7404), null (7405), Erjolf (7462), Erjolf (7463), Erjolf (7464), Erjolf (7465), Erjolf (7466), Erjolf (7467), Jeremy Servil (7531), Jeremy Servil (7532), null (7533), Jeremy Servil (7534), null (7543), null (7544), null (7545), null (7546), null (7547), null (7548), null (7549), Spirit (7963), null (7972), null (7973), null (7974), null (7975), Spirit (7981), Spirit (7982), Spirit (7983), Spirit (7984), null (7985), Summer Bonde (7986), null (8112), Summer Bonde (8113), Summer Bonde (8114), Summer Bonde (8135), Laidee Gnonock (8484), Hazelmere (8485), Hazelmere (8489), Hazelmere (8490), null (8492), Junior Cadet Nirol (8932), null (8933), Junior Cadet Boff (8934), null (8935), Zambo (11620) +Linked objects: Kennith (29737), null (38164) + +Base: 1629 +Linked animations: [7205, 7207, 7208, 7209, 7210, 8351, 8352, 8353, 8354, 8355, 8356, 8357, 8358, 8359, 8360, 8361, 8362] +Linked NPCs: Cave lizard (6369), Baby gecko (6915), Gecko (6916), Baby gecko (6917), Gecko (6918), Baby gecko (7277), Baby gecko (7278), Baby gecko (7279), Baby gecko (7280), Gecko (7281), Gecko (7282), Gecko (7283), Gecko (7284), Baby gecko (7285), Baby gecko (7286), Baby gecko (7287), Baby gecko (7288), Gecko (7289), Gecko (7290), Gecko (7291), Gecko (7292), Gecko (9213), Gecko (9214), Gecko (9215), Gecko (9216), Gecko (9217) + +Base: 1630 +Linked animations: [7225, 7226] +Linked objects: null (26881), null (26882) + +Base: 1631 +Linked animations: [22, 23, 24, 85, 583, 7212, 7213, 7214, 7215, 7216, 7217, 7218, 7219, 7220, 7221, 7222, 7223, 7224, 8800] +Linked NPCs: Guard (296), Guard (297), Guard (298), Guard (299), Guard (3407), Guard (3408), Guard (6183), Guard (6184), Guard (7142), Guardsman Dante (7885), Guardsman DeShawn (7886), Guardsman Brawn (7887), Sergeant Abram (7888), Guardsman Pazel (7889), Guardsman Peale (7890), Arresting officer (11457) +Linked objects: Border guard (45860), Border guard (45861) + +Base: 1632 +Linked animations: [7233] + +Base: 1633 + +Base: 1634 +Linked animations: [7240, 7252] +Linked objects: Tree (27089) + +Base: 1635 +Linked animations: [7231] +Linked objects: null (27190) + +Base: 1636 +Linked animations: [7241, 7242, 7243, 7244] +Linked NPCs: Giant wasp (6381), Giant wasp (6383), Warped fly (7915) + +Base: 1637 +Linked animations: [7245, 7246, 7247, 7248, 7249] +Linked NPCs: Wild jade vine (3409), Wild jade vine (3410), Wild jade vine (3411), Wild jade vine (3412) +Linked objects: Jade vine (27039), Wild jade vine (27076), Wild jade vine (27077), Wild jade vine (27078), null (27196), null (27197) + +Base: 1638 +Linked animations: [7235, 7236, 7237, 7238, 7239] +Linked NPCs: Giant ant worker (6379), Giant ant soldier (6380) + +Base: 1639 +Linked animations: [7234] +Linked graphics: 1172 + +Base: 1640 +Linked animations: [7229] +Linked graphics: 1171 + +Base: 1641 +Linked animations: [7251] +Linked graphics: 1243 + +Base: 1642 +Linked animations: [7250] +Linked graphics: 1241 + +Base: 1643 +Linked animations: [7256, 7257, 7258, 7259, 7260, 7700, 7701, 7702, 7703, 7704, 7705, 8539] +Linked NPCs: Frog (3783), Barker toad (6889), Barker toad (6890), Barker toad (9482) + +Base: 1644 +Linked animations: [7280, 7281, 7282] + +Base: 1645 +Linked animations: [7294] +Linked graphics: 1246 + +Base: 1646 +Linked animations: [7279] +Linked graphics: 534, 1244, 1544, 1545 + +Base: 1647 + +Base: 1648 +Linked animations: [7284, 7285] +Linked objects: Gargoyle (27274), Gargoyle (27275), Doorway (27276), null (27277) + +Base: 1649 +Linked animations: [7278] + +Base: 1650 +Linked animations: [7283, 7293] +Linked objects: Fishtank (27253) + +Base: 1651 +Linked animations: [7292] +Linked graphics: 1245 + +Base: 1652 +Linked animations: [7286, 7287, 7288, 7289, 7290] +Linked objects: Torch (27241) + +Base: 1653 +Linked animations: [7296] + +Base: 1654 +Linked animations: [7295] +Linked NPCs: Grim Reaper (6390) +Linked objects: Grim Reaper (45802) + +Base: 1655 +Linked animations: [7300] +Linked graphics: 1247 + +Base: 1656 +Linked animations: [7342, 7343] +Linked NPCs: Ghost (6504), null (6505) + +Base: 1657 +Linked animations: [7354] +Linked objects: null (27317), Machine (27320) + +Base: 1658 +Linked animations: [7349, 7350, 7351, 7352, 7353] +Linked objects: null (27316), Fairy portal (27329), Fairy portal (27330) + +Base: 1659 +Linked animations: [7346, 7347, 7348] +Linked objects: Door (27333) + +Base: 1660 + +Base: 1661 +Linked animations: [7334, 7335, 7336] +Linked graphics: 1260, 1261, 1262 + +Base: 1662 +Linked animations: [7345] +Linked graphics: 1269 + +Base: 1663 +Linked animations: [7344] +Linked graphics: 1263 + +Base: 1664 +Linked animations: [7355] +Linked graphics: 1270 + +Base: 1665 +Linked animations: [7337, 7338] +Linked graphics: 1264, 1265 + +Base: 1666 +Linked animations: [7339] +Linked graphics: 1266 + +Base: 1667 +Linked animations: [7340] +Linked graphics: 1267 + +Base: 1668 +Linked animations: [7341] +Linked graphics: 1268 + +Base: 1669 +Linked animations: [7358, 7359, 7360] +Linked objects: Strange box (27654), Strange box (27655), Strange box (27656) + +Base: 1670 +Linked animations: [7356] +Linked objects: null (27570), null (27653), null (27660), null (30338) + +Base: 1671 +Linked animations: [7361] +Linked objects: null (27331), Portal (27567), Portal (27568), null (27571) + +Base: 1672 +Linked animations: [7357] +Linked objects: null (27569), Oozing rock (27615) + +Base: 1673 +Linked animations: [7372] + +Base: 1674 +Linked animations: [7374, 8806] + +Base: 1675 +Linked animations: [7371] + +Base: 1676 +Linked animations: [7373] + +Base: 1677 +Linked animations: [7375, 7396, 7490] +Linked NPCs: Grave marker (6568), Broken grave marker (6569), Collapsing grave marker (6570) +Linked objects: null (27997) + +Base: 1678 + +Base: 1679 +Linked animations: [7511] +Linked graphics: 1278 + +Base: 1680 +Linked animations: [7494, 7495, 8597] +Linked graphics: 1276, 1277 + +Base: 1681 +Linked animations: [7509] +Linked graphics: 1279 + +Base: 1682 +Linked animations: [7386, 7387, 7388] + +Base: 1683 +Linked animations: [7378, 7379, 7380, 7381] +Linked objects: null (28135), null (28136), null (28137), null (28138) + +Base: 1684 + +Base: 1685 +Linked animations: [7411, 7412, 7413, 7414, 7415, 7416, 7417, 7505, 7518] +Linked NPCs: Revenant ork (6610), Revenant ork (6615), Revenant ork (6618), Revenant ork (6624), Revenant ork (6626), Revenant ork (6629), Revenant ork (6633), Revenant ork (6648), Revenant ork (6653), Revenant ork (6664), Revenant ork (6670), Revenant ork (6672), Revenant ork (6690), Revenant ork (6696), Revenant ork (6702), Revenant ork (6713), Revenant ork (6725), Revenant ork (6729) + +Base: 1686 +Linked animations: [7453, 7454, 7455, 7456, 7457, 7458, 7459, 7497, 7510] +Linked NPCs: Revenant cyclops (6645), Revenant cyclops (6687) + +Base: 1687 +Linked animations: [7397, 7398, 7399, 7400, 7401, 7402, 7403, 7496, 7521] +Linked NPCs: Revenant werewolf (6607), Revenant werewolf (6609), Revenant werewolf (6614), Revenant werewolf (6617), Revenant werewolf (6625), Revenant werewolf (6632), Revenant werewolf (6644), Revenant werewolf (6663), Revenant werewolf (6675), Revenant werewolf (6686), Revenant werewolf (6701), Revenant werewolf (6712), Revenant werewolf (6724), Revenant werewolf (6728) + +Base: 1688 +Linked animations: [7404, 7405, 7406, 7407, 7408, 7409, 7410, 7504, 7517] +Linked NPCs: Revenant imp (6604), Revenant imp (6635), Revenant imp (6655), Revenant imp (6666), Revenant imp (6677), Revenant imp (6697), Revenant imp (6703), Revenant imp (6715) + +Base: 1689 +Linked animations: [7460, 7461, 7462, 7463, 7464, 7465, 7466, 7467, 7468, 7469, 7470, 7471, 7472, 7473, 7500, 7501, 7502, 7514, 7515] +Linked NPCs: Revenant hellhound (6646), Revenant dark beast (6649), Revenant hellhound (6688), Revenant dark beast (6691) + +Base: 1690 +Linked animations: [7446, 7447, 7448, 7449, 7450, 7451, 7452, 7499, 7513] +Linked NPCs: Revenant goblin (6605), Revenant goblin (6612), Revenant goblin (6616), Revenant goblin (6620), Revenant goblin (6636), Revenant goblin (6637), Revenant goblin (6638), Revenant goblin (6639), Revenant goblin (6651), Revenant goblin (6656), Revenant goblin (6657), Revenant goblin (6658), Revenant goblin (6667), Revenant goblin (6678), Revenant goblin (6679), Revenant goblin (6680), Revenant goblin (6681), Revenant goblin (6693), Revenant goblin (6698), Revenant goblin (6699), Revenant goblin (6704), Revenant goblin (6705), Revenant goblin (6706), Revenant goblin (6707), Revenant goblin (6716), Revenant goblin (6717), Revenant goblin (6718), Revenant goblin (6719) + +Base: 1691 +Linked animations: [7474, 7475, 7476, 7477, 7478, 7479, 7480, 7498, 7512] +Linked NPCs: Revenant demon (6647), Revenant demon (6689) + +Base: 1692 +Linked animations: [7425, 7426, 7427, 7428, 7429, 7430, 7431, 7507, 7520] +Linked NPCs: Revenant vampire (6613), Revenant vampire (6623), Revenant vampire (6643), Revenant vampire (6652), Revenant vampire (6662), Revenant vampire (6669), Revenant vampire (6671), Revenant vampire (6674), Revenant vampire (6685), Revenant vampire (6695), Revenant vampire (6700), Revenant vampire (6711), Revenant vampire (6723) + +Base: 1693 +Linked animations: [7481, 7482, 7483, 7484, 7485, 7486, 7487, 7506, 7519] +Linked NPCs: Revenant icefiend (6606), Revenant icefiend (6621), Revenant pyrefiend (6622), Revenant icefiend (6628), Revenant pyrefiend (6631), Revenant icefiend (6640), Revenant pyrefiend (6641), Revenant icefiend (6659), Revenant pyrefiend (6660), Revenant pyrefiend (6668), Revenant icefiend (6682), Revenant pyrefiend (6683), Revenant icefiend (6694), Revenant icefiend (6708), Revenant pyrefiend (6709), Revenant icefiend (6720), Revenant pyrefiend (6721) + +Base: 1694 +Linked animations: [7418, 7419, 7420, 7421, 7422, 7423, 7424, 7503, 7516] +Linked NPCs: Revenant hobgoblin (6608), Revenant hobgoblin (6642), Revenant hobgoblin (6661), Revenant hobgoblin (6684), Revenant hobgoblin (6710), Revenant hobgoblin (6722), Revenant hobgoblin (6727) + +Base: 1695 +Linked animations: [7394, 7395, 7489] + +Base: 1696 +Linked animations: [7491, 7492, 7493] + +Base: 1697 +Linked animations: [7488] +Linked graphics: 1274 + +Base: 1698 +Linked animations: [7539, 7540, 7541, 7542, 8447, 8448, 8449, 8450, 11371, 11372] + +Base: 1699 + +Base: 1700 +Linked animations: [7566] +Linked graphics: 1287 + +Base: 1701 +Linked animations: [7569, 7570] +Linked graphics: 1290, 1291 + +Base: 1702 +Linked animations: [7561] +Linked graphics: 1286 + +Base: 1703 +Linked animations: [7547] +Linked graphics: 1283 + +Base: 1704 +Linked animations: [7567] +Linked graphics: 1288 + +Base: 1705 +Linked animations: [7549, 7572] +Linked graphics: 1285, 1292 + +Base: 1706 +Linked animations: [7568] +Linked graphics: 1289 + +Base: 1707 +Linked animations: [7544, 7545, 7548] +Linked graphics: 1284 +Linked objects: Snow (28297) + +Base: 1708 +Linked animations: [7550, 7551] + +Base: 1709 +Linked animations: [7546] +Linked objects: null (28305), null (28306), null (28307), null (28308), null (28309), null (28310), null (28311), null (30336) + +Base: 1710 +Linked animations: [7552] +Linked objects: Stags (28318), Stags (28319), Reindeer (41723), Reindeer (41724) + +Base: 1711 +Linked animations: [7556, 7557, 7558, 7559, 7560, 7562, 7563, 7564, 7565, 7571, 7573, 10480] +Linked NPCs: Barbarian snowman (6742), Dragon snowman (6743), Dwarf snowman (6744), Pirate snowman (6745), Snowman (6746), Snow ranger (6747), Snow warrior (6748), Snow mage (6749) + +Base: 1712 +Linked animations: [7543, 7553, 7554, 7555] +Linked NPCs: Snow (6740), Snow (6741) + +Base: 1713 +Linked animations: [7577] +Linked objects: Cat (28356) + +Base: 1714 +Linked animations: [7876, 7877, 7878, 7879, 7880, 7881, 7882, 7883, 7887, 7891, 8916] +Linked NPCs: Geyser titan (7339), Geyser titan (7340), Geyser titan (9490) + +Base: 1715 +Linked animations: [7955, 7956, 7957, 7958, 7959] + +Base: 1716 +Linked animations: [8301, 8302, 8303, 8304, 8305, 8306, 8308, 8309, 8534] +Linked NPCs: Wolpertinger (6869), Wolpertinger (6870) + +Base: 1717 +Linked animations: [8245, 8246, 8247, 8248, 8249, 8250, 8251, 8252, 12820] +Linked NPCs: Evil turnip (6833), Evil turnip (6834), Rogue evil turnip (9571), Rogue evil turnip (9572), Rogue evil turnip (9573), Rogue evil turnip (9574) + +Base: 1718 +Linked animations: [8021, 8022, 8023, 8024, 8025, 8026, 8029, 8031, 8549, 12826] +Linked NPCs: Bronze minotaur (6853), Bronze minotaur (6854), Iron minotaur (6855), Iron minotaur (6856), Steel minotaur (6857), Steel minotaur (6858), Mithril minotaur (6859), Mithril minotaur (6860), Adamant minotaur (6861), Adamant minotaur (6862), Rune minotaur (6863), Rune minotaur (6864), Rogue adamant minotaur (9599), Rogue adamant minotaur (9600), Rogue adamant minotaur (9601), Rogue adamant minotaur (9602) + +Base: 1719 +Linked animations: [8204, 8205, 8206, 8207, 8208, 8209, 8211, 8212, 8214, 8216, 8533, 12824] +Linked NPCs: Stranger plant (6827), Stranger plant (6828), Rogue stranger plant (9587), Rogue stranger plant (9588), Rogue stranger plant (9589), Rogue stranger plant (9590) + +Base: 1720 +Linked animations: [8281, 8282, 8283, 8284, 8285, 8286, 8287, 8288, 8542] +Linked NPCs: War tortoise (6815), War tortoise (6816), War tortoise (9483) + +Base: 1721 +Linked animations: [7728, 7729, 7730, 7731, 7732, 10954, 10955, 10956, 10957] +Linked NPCs: Wild broav (8487), null (8488), Broav (8491), Broav (9188) + +Base: 1722 +Linked animations: [7863, 7864, 7865, 7866, 7867, 7868, 7869, 7870, 7871, 8255, 8256, 8257, 8258, 8259, 8260, 8927, 8934] +Linked NPCs: Forge regent (7335), Forge regent (7336), Spirit Tz-Kih (7361), Spirit Tz-Kih (7362), Spirit Tz-Kih (9471), Forge regent (9486) + +Base: 1723 +Linked animations: [7961, 7962, 7963, 7964, 7967, 7968, 7969, 7970, 7972, 7974, 8543] +Linked NPCs: Karamthulhu overlord (6809), Karamthulhu overlord (6810), Karamthulhu overlord (9480) + +Base: 1724 +Linked animations: [7923, 7924, 7925, 7926, 7927, 7928, 7929, 7930, 8568] +Linked NPCs: Honey badger (6845), Honey badger (6846) + +Base: 1725 +Linked animations: [7792, 7793, 7794, 7795, 7796, 7797, 7798, 7800, 7803, 7804, 8152, 8153, 8154, 8155, 8156, 8157, 8158, 8159, 8537, 8550] +Linked NPCs: Spirit cobra (6802), Spirit cobra (6803), Desert wyrm (6831), Desert wyrm (6832), Desert wyrm (9470), Spirit cobra (9481) + +Base: 1726 +Linked animations: [7653, 7654, 7655, 7656, 7657, 7711, 7712, 7715, 7717, 8567] +Linked NPCs: Scarab larva (6772), Bloated leech (6843), Bloated leech (6844) + +Base: 1727 +Linked animations: [8076, 8077, 8078, 8079, 8080, 8081, 8082, 8084, 8085, 8930, 9145, 9153, 11211, 12539, 12544] +Linked NPCs: Pyrefiend (1633), Pyrefiend (1634), Pyrefiend (1635), Pyrefiend (1636), Icefiend (3406), Pyrefiend (6216), Icefiend (6217), Pyrelord (7377), Pyrelord (7378), Baby icefiend (7713), Icefiend (7714), Icefiend (7715), null (7716), null (7732), null (7733), null (7734), null (7735), Baby icefiend (7736), Pyrefiend (8598), Pyrefiend (8616), Icefiend (9066), Icefiend (9067), Icefiend (9068), Icefiend (9069), Icefiend (9072), Icefiend (9073), Icefiend (9074), Icefiend (9075), Icefiend (9076), Pyrelord (9477), Icefiend (10212), Icefiend (10213), Icefiend (10214), Icefiend (10215), Icefiend (10216), Icefiend (10217), Icefiend (10218), Pyrefiend (10697) + +Base: 1728 +Linked animations: [7768, 7769, 7770, 7771, 7772, 7773, 7775, 7776, 7777, 8368, 8540, 12817] +Linked NPCs: Compost mound (6871), Compost mound (6872), Compost mound (9473), Rogue compost mound (9563), Rogue compost mound (9564), Rogue compost mound (9565), Rogue compost mound (9566) + +Base: 1729 +Linked animations: [8046, 8047, 8048, 8049, 8050, 8051, 8052, 8053, 8924] +Linked NPCs: Obsidian golem (7345), Obsidian golem (7346), Obsidian golem (9485) + +Base: 1730 +Linked animations: [8141, 8142, 8143, 8144, 8145, 8146, 8147, 8148, 8561, 8911, 12816] +Linked NPCs: Thorny snail (6806), Thorny snail (6807), Snail (7209), Thorny snail (9469), Rogue thorny snail (9559), Rogue thorny snail (9560), Rogue thorny snail (9561), Rogue thorny snail (9562), Ravenous snail (11266) + +Base: 1731 +Linked animations: [7848, 7849, 7850, 7851, 7852, 7853, 7854, 7855, 7858, 8559, 12825] +Linked NPCs: Giant ent (6800), Giant ent (6801), Rogue giant ent (9591), Rogue giant ent (9592), Rogue giant ent (9593), Rogue giant ent (9594) + +Base: 1732 +Linked animations: [7667, 7668, 7669, 7671, 7672, 7675, 7676, 8556, 8910] +Linked NPCs: Abyssal parasite (6818), Abyssal parasite (6819) + +Base: 1733 +Linked animations: [7735, 7736, 7737, 7738, 7739, 7740, 7741, 7746, 7747, 8547] +Linked NPCs: Bunyip (6813), Bunyip (6814) + +Base: 1734 +Linked animations: [7977, 7978, 7979, 7980, 7981, 7982, 7986, 7987, 8928] +Linked NPCs: Lava titan (7341), Lava titan (7342), Lava titan (9489) + +Base: 1735 +Linked animations: [8233, 8234, 8235, 8236, 8237, 8238, 8239, 8243, 8921] +Linked NPCs: Void torcher (7351), Void torcher (7352) + +Base: 1736 +Linked animations: [8112, 8113, 8114, 8115, 8116, 8117, 8118, 8122, 8548] +Linked NPCs: Granite lobster (6849), Granite lobster (6850) + +Base: 1737 +Linked animations: [7780, 7781, 7782, 7783, 7784, 7785, 7786, 7787, 7791, 8560] +Linked NPCs: Spirit dagannoth (6804), Spirit dagannoth (6805) + +Base: 1738 +Linked animations: [8343, 8344, 8345, 8346, 8347, 8348, 8349, 8350] +Linked NPCs: Baby monkey (6942), Monkey (6943), Baby monkey (6944), Baby monkey (7210), Monkey (7211), Baby monkey (7212), Monkey (7213), Baby monkey (7214), Monkey (7215), Baby monkey (7216), Monkey (7217), Baby monkey (7218), Monkey (7219), Baby monkey (7220), Monkey (7221), Baby monkey (7222), Monkey (7223), Baby monkey (7224), Monkey (7225), Baby monkey (7226), Monkey (7227), Baby monkey (7228), Baby monkey (7229), Baby monkey (7230), Baby monkey (7231), Baby monkey (7232), Baby monkey (7233), Baby monkey (7234), Baby monkey (7235), Baby monkey (7236), Monkey (9223), Monkey (9224), Monkey (9225), Monkey (9226), Monkey (9227), Monkey (9228), Monkey (9229), Monkey (9230), Monkey (9231), Monkey (9232), Dealga (11475) + +Base: 1739 +Linked animations: [8064, 8065, 8066, 8067, 8068, 8069, 8070, 8071, 8072, 8075, 8558] +Linked NPCs: Praying mantis (6798), Praying mantis (6799) + +Base: 1740 +Linked animations: [7687, 7688, 7689, 7690, 7691, 7692, 7693, 7694, 7698, 7837, 7838, 7839, 7840, 7841, 7842, 7843, 7844, 7845, 7846, 7847, 7946, 7947, 7948, 7949, 7950, 7951, 7952, 7954, 8183, 8184, 8185, 8186, 8187, 8188, 8189, 8190, 8196, 8914, 8918, 8923, 8926, 12818] +Linked NPCs: Steel titan (7343), Steel titan (7344), Abyssal titan (7349), Abyssal titan (7350), Moss titan (7357), Moss titan (7358), Ice titan (7359), Ice titan (7360), Iron titan (7375), Iron titan (7376), Rogue ice titan (9595), Rogue ice titan (9596), Rogue ice titan (9597), Rogue ice titan (9598) +Linked graphics: 1443 + +Base: 1741 +Linked animations: [7814, 7815, 7816, 7817, 7818, 7819, 7820, 7821, 7824, 8530] +Linked NPCs: Smoke devil (6865), Smoke devil (6866) + +Base: 1742 +Linked animations: [8128, 8129, 8130, 8131, 8132, 8133, 8134, 8139, 8919] +Linked NPCs: Void shifter (7367), Void shifter (7368), Void shifter (7369) + +Base: 1743 +Linked animations: [2016, 2017, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 7643, 7644, 7645, 7646, 7647, 7648, 7649, 7658, 7659, 7677, 7678, 7679, 7680, 7681, 7682, 7683, 7684, 8197, 8198, 8199, 8200, 8201, 8202, 8313, 8552, 8564] +Linked NPCs: Vulture (3675), Vulture (3676), Vulture (6792), Abyssal lurker (6820), Abyssal lurker (6821), Vulture (6946), Ibis (6991), Vulture (7320), Vulture (7322), Vulture (7324), Vulture (7326), Vulture (7328), Vulture (9256), Vulture (9257), Vulture (9258), Vulture (9259), Vulture (9260), Vulture (9261) + +Base: 1744 +Linked animations: [7670, 8171, 8172, 8173, 8174, 8175, 8176, 8179, 8181, 8182, 8920, 12819] +Linked NPCs: Void spinner (7333), Void spinner (7334), Rogue void spinner (9567), Rogue void spinner (9568), Rogue void spinner (9569), Rogue void spinner (9570) + +Base: 1745 +Linked animations: [7893, 7894, 7895, 7896, 7897, 7898, 7899, 7900, 7903, 8032, 8033, 8034, 8035, 8036, 8037, 8038, 8039, 8040, 8041, 8042, 8554, 8915] +Linked NPCs: Bull ant (6867), Bull ant (6868), Spirit mosquito (7331), Spirit mosquito (7332) +Linked graphics: 1439, 1440, 1441, 1442 + +Base: 1746 +Linked animations: [7934, 7935, 7936, 7937, 7938, 7939, 7940, 7941, 7945, 8538] +Linked NPCs: Hydra (6811), Hydra (6812), Hydra (9488), Hydra (10610), Hydra (10611), Hydra (10612), Hydra (10613), Hydra (10614), Hydra (10615), Hydra (10616), Hydra (10617), Hydra (10618) + +Base: 1747 +Linked animations: [8086, 8087, 8088, 8089, 8090, 8091, 8092, 8093, 8094, 8097, 8929, 8937] +Linked NPCs: Void ravager (7370), Void ravager (7371), Void ravager (9476) + +Base: 1748 +Linked animations: [7718, 7719, 7720, 7721, 7722, 7724, 7726, 7727, 8535] +Linked NPCs: Raccoon (3286), Raccoon (3287), Raccoon (3288), Beaver (6808), Baby raccoon (6913), Raccoon (6914), Baby raccoon (6997), Baby raccoon (7271), Raccoon (7272), Baby raccoon (7273), Raccoon (7274), Baby Raccoon (7275), Baby raccoon (7276), Raccoon (9236), Raccoon (9237), Raccoon (9238), Beaver (9475) + +Base: 1749 +Linked animations: [7750, 7751, 7752, 7753, 7754, 7755, 7756, 7758, 8922, 12822] +Linked NPCs: Giant chinchompa (7353), Giant chinchompa (7354), Rogue giant chinchompa (9579), Rogue giant chinchompa (9580), Rogue giant chinchompa (9581), Rogue giant chinchompa (9582) + +Base: 1750 +Linked animations: [384, 392, 405, 8324, 8325, 8326, 8327, 8328, 12266] +Linked NPCs: Squirrel (3284), Squirrel (3285), Baby squirrel (6919), Squirrel (6920), Baby squirrel (6921), Baby squirrel (7301), Squirrel (7302), Baby squirrel (7303), Squirrel (7304), Baby squirrel (7305), Squirrel (7306), Baby squirrel (7307), Squirrel (7308), Baby squirrel (7309), Baby squirrel (7310), Baby squirrel (7311), Baby squirrel (7312), Fluffy (7413), Beny (7414), Bubbles (7415), Big Ben (7416), Sparky (9170), Squirrel (9250), Squirrel (9251), Squirrel (9252), Squirrel (9253), Squirrel (9254), Charlie the squirrel (9681), Fluffy (9682), Charlie the squirrel (9686), Fluffy (9692), Beny (9693), Bubbles (9694), Big Ben (9695) +Linked graphics: 2145 + +Base: 1751 +Linked animations: [7828, 7829, 7830, 7831, 7832, 7833, 7834, 7835, 8925] +Linked NPCs: Fire titan (7355), Fire titan (7356), Fire titan (9487) + +Base: 1752 +Linked animations: [7991, 7992, 7993, 7994, 7995, 7996, 7997, 7998, 7999, 8000, 8001, 8931] +Linked NPCs: Ravenous locust (7372), Ravenous locust (7373), Ravenous locust (7374), Ravenous locust (9484) + +Base: 1753 +Linked animations: [8219, 8220, 8221, 8222, 8223, 8224, 8225, 8226, 8932] +Linked NPCs: Swamp titan (7329), Swamp titan (7330) + +Base: 1754 +Linked animations: [8363, 8364, 8365] +Linked NPCs: Baby chameleon (6922), Chameleon (6923), Baby chameleon (6924), Chameleon (6925), Baby chameleon (6926), Chameleon (6927), Baby chameleon (6928), Chameleon (6929), Baby chameleon (6930), Chameleon (6931), Baby chameleon (6932), Chameleon (6933), Baby chameleon (6934), Chameleon (6935), Baby chameleon (6936), Chameleon (6937), Baby chameleon (6938), Chameleon (6939), Baby chameleon (6940), Chameleon (6941), Karma the chameleon (8580), Karma the chameleon (8581), Karma the chameleon (8582), Karma the chameleon (8583), Karma the chameleon (8584), Karma the chameleon (8585), Karma the chameleon (8586), Karma the chameleon (8587), Karma the chameleon (8588), Karma the chameleon (8589), Chameleon (9262), Chameleon (9263), Chameleon (9264), Chameleon (9265), Chameleon (9266), Chameleon (9267), Chameleon (9268), Chameleon (9269), Chameleon (9270), Chameleon (9271) + +Base: 1755 +Linked animations: [7759, 7760, 7761, 7762, 7763, 7764, 7765, 7766, 8551, 12821] +Linked NPCs: Cockatrice (1620), Cockatrice (1621), Spirit cockatrice (6875), Spirit cockatrice (6876), Spirit guthatrice (6877), Spirit guthatrice (6878), Spirit saratrice (6879), Spirit saratrice (6880), Spirit zamatrice (6881), Spirit zamatrice (6882), Spirit pengatrice (6883), Spirit pengatrice (6884), Spirit coraxatrice (6885), Spirit coraxatrice (6886), Spirit vulatrice (6887), Spirit vulatrice (6888), Chocatrice (7201), Minitrice (8620), Minitrice (9247), Rogue spirit coraxatrice (9575), Rogue spirit coraxatrice (9576), Rogue spirit coraxatrice (9577), Rogue spirit coraxatrice (9578) + +Base: 1756 +Linked animations: [7602] +Linked objects: Power indicator (28521), Power indicator (28522), Power indicator (28523), Power indicator (28524), null (31352) + +Base: 1757 +Linked animations: [7603] +Linked objects: null (28371), null (32576) + +Base: 1758 +Linked animations: [7634] + +Base: 1759 +Linked animations: [7600] +Linked objects: Steam (28532), Steam (28533) + +Base: 1760 +Linked animations: [7601] +Linked objects: Furnace (28433), null (28534) + +Base: 1761 +Linked animations: [7604, 7605, 7606, 7607, 7608, 7609, 7610, 7611, 7612, 7613, 7614, 7615, 7616, 7617, 7618, 7619, 7620, 7621, 7622, 9376] +Linked NPCs: Scarab mage (5250), Scarab mage (5254), Scabaras ranger (6773), Scabaras lancer (6774), Scabaras priest (6775), Scabaras mage (6780), Scabaras mage (6781), High Priest of Scabaras (6788), High Priest of Scabaras (6789), High Priest of Scabaras (6790), High Priest of Scabaras (6791), Insectoid assassin (7795), Insectoid assassin (7796), Pit scarabite (11565), Pit scabarite (11590), Pit scabarite (11591) +Linked objects: Pit scabarite (39263), Pit scabarite (41606) + +Base: 1762 +Linked animations: [7587, 7588, 7589, 7590, 7591] +Linked NPCs: Crocodile (1993), Crocodile (6779) + +Base: 1763 +Linked animations: [8624, 8647, 12299] +Linked objects: Fire (3769), Sacrificial Pyre (4767), Cooking fire (29094), null (29098), Fire (29139), Fire pit (29155), null (29176), null (29177), null (29178), null (29179), null (29180), null (31692), null (31693), Tomb (34299), Fire (37726), Campfire (38165), null (38167), null (44166), null (44178), Fire pit (44371), null (44392), Spit-roast (45090), Spitroast (45313), Sacrificial Pyre (46399), null (46400), Campfire (52709) + +Base: 1764 +Linked animations: [7953] +Linked graphics: 1450 + +Base: 1765 +Linked animations: [7920, 7921] +Linked graphics: 1370, 1371 + +Base: 1766 +Linked animations: [7983] +Linked graphics: 1490 + +Base: 1767 +Linked animations: [7917] +Linked graphics: 1368 + +Base: 1768 +Linked animations: [8588] +Linked graphics: 1523 + +Base: 1769 +Linked animations: [7960] +Linked graphics: 1359 + +Base: 1770 +Linked animations: [8367] +Linked graphics: 1360 + +Base: 1771 +Linked animations: [8015] +Linked graphics: 1321 + +Base: 1772 +Linked animations: [7985, 8121, 8194] +Linked graphics: 1353, 1381, 1448, 1455 + +Base: 1773 +Linked animations: [8210] +Linked graphics: 1509 + +Base: 1774 +Linked animations: [8213] +Linked graphics: 1508 + +Base: 1775 +Linked animations: [7662] +Linked graphics: 1295, 1296, 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, 1311, 1313 + +Base: 1776 +Linked animations: [8289, 8290] +Linked graphics: 1414, 1415 + +Base: 1777 +Linked animations: [8030] +Linked graphics: 1498 + +Base: 1778 +Linked animations: [8027] +Linked graphics: 1496 + +Base: 1779 +Linked animations: [7734] +Linked graphics: 1345 + +Base: 1780 +Linked animations: [8278, 8280] +Linked graphics: 1324, 1325 + +Base: 1781 +Linked animations: [8191, 8192] +Linked graphics: 1444, 1445 + +Base: 1782 +Linked animations: [8073, 8074] +Linked graphics: 1379, 1380 + +Base: 1783 +Linked animations: [8063] +Linked graphics: 1408 + +Base: 1784 +Linked animations: [8111] +Linked graphics: 1327 + +Base: 1785 +Linked animations: [8203, 8322] +Linked graphics: 1331, 1337 + +Base: 1786 +Linked animations: [7856] +Linked graphics: 1361 + +Base: 1787 +Linked animations: [7872, 7873, 7874] +Linked graphics: 1393, 1394, 1395 + +Base: 1788 +Linked animations: [7901, 7902] +Linked graphics: 1382, 1383 + +Base: 1789 +Linked animations: [8299, 8300] +Linked graphics: 1333, 1334, 1349, 1350 + +Base: 1790 +Linked animations: [7975, 7976] +Linked graphics: 1478, 1479 + +Base: 1791 +Linked animations: [8264] +Linked graphics: 1486 + +Base: 1792 +Linked animations: [8262] +Linked graphics: 1329, 1484 + +Base: 1793 +Linked animations: [7699] +Linked graphics: 1457 + +Base: 1794 +Linked animations: [7965, 7966, 7971, 7973] +Linked graphics: 1474, 1475, 1476, 1477 + +Base: 1795 +Linked animations: [7922] +Linked graphics: 1372 + +Base: 1796 +Linked animations: [8323] +Linked graphics: 1332 + +Base: 1797 +Linked animations: [7931, 7932, 7933] +Linked graphics: 1397, 1398, 1399 + +Base: 1798 +Linked animations: [7713, 7714, 7716] +Linked graphics: 1416, 1417, 1418, 1419, 1420, 1421 + +Base: 1799 +Linked animations: [8083] +Linked graphics: 1463 + +Base: 1800 +Linked animations: [7757] +Linked graphics: 1364 + +Base: 1801 +Linked animations: [7875] +Linked graphics: 1396 + +Base: 1802 +Linked animations: [8938] +Linked graphics: 1575 + +Base: 1803 +Linked animations: [7733] +Linked graphics: 1344 + +Base: 1804 +Linked animations: [8110] +Linked graphics: 1326 + +Base: 1805 +Linked animations: [8195] +Linked graphics: 1449 + +Base: 1806 +Linked animations: [7778, 7779] +Linked graphics: 1424, 1425 + +Base: 1807 +Linked animations: [8160] +Linked graphics: 1389 + +Base: 1808 +Linked animations: [7988, 7989, 7990] +Linked graphics: 1492, 1493, 1494 + +Base: 1809 +Linked animations: [8180] +Linked graphics: 1507 + +Base: 1810 +Linked animations: [7827] +Linked graphics: 1473 + +Base: 1811 +Linked animations: [8096] +Linked graphics: 1438 + +Base: 1812 +Linked animations: [8166] +Linked graphics: 1340 + +Base: 1813 +Linked animations: [7790] +Linked graphics: 1427 + +Base: 1814 +Linked animations: [8055] +Linked graphics: 1466 + +Base: 1815 +Linked animations: [8054] +Linked graphics: 1465 + +Base: 1816 +Linked animations: [7685, 7686] +Linked graphics: 1338, 1339 + +Base: 1817 +Linked animations: [8193] +Linked graphics: 1446, 1447, 1495 + +Base: 1818 +Linked animations: [7788, 7789] +Linked graphics: 1426, 1428 + +Base: 1819 +Linked animations: [8270] +Linked graphics: 1323 + +Base: 1820 +Linked animations: [8028] +Linked graphics: 1497 + +Base: 1821 +Linked animations: [7942] +Linked graphics: 1488 + +Base: 1822 +Linked animations: [8227, 8228] +Linked graphics: 1521 + +Base: 1823 +Linked animations: [7673, 7674] +Linked graphics: 1422, 1423 + +Base: 1824 +Linked animations: [8369] +Linked graphics: 1312 + +Base: 1825 +Linked animations: [7908] +Linked graphics: 1384 + +Base: 1826 +Linked animations: [7748] +Linked graphics: 1481 + +Base: 1827 +Linked animations: [8261] +Linked graphics: 1483 + +Base: 1828 +Linked animations: [8060, 8061, 8062] +Linked graphics: 1405, 1406, 1407 + +Base: 1829 +Linked animations: [8018, 8019] +Linked graphics: 1335, 1336 + +Base: 1830 +Linked animations: [7799, 7805, 8095, 8616, 8617] +Linked graphics: 1409, 1412, 1437, 1524, 1525 + +Base: 1831 +Linked animations: [8232, 8240, 8241, 8242, 8244] +Linked graphics: 1429, 1430, 1431, 1432, 1433 + +Base: 1832 +Linked animations: [8215] +Linked graphics: 1510 + +Base: 1833 +Linked animations: [8218] +Linked graphics: 1462 + +Base: 1834 +Linked animations: [8168] +Linked graphics: 1342 + +Base: 1835 +Linked animations: [7695, 7696] +Linked graphics: 1451, 1452, 1453, 1454 + +Base: 1836 +Linked animations: [7663, 7664, 7665, 7666, 10067, 10231] +Linked graphics: 1314, 1315, 1319, 1738, 1739, 1777, 2744 + +Base: 1837 +Linked animations: [7943] +Linked graphics: 1489 + +Base: 1838 +Linked animations: [7749] +Linked graphics: 1482 + +Base: 1839 +Linked animations: [7944] +Linked graphics: 1487 + +Base: 1840 +Linked animations: [7885] +Linked graphics: 1374 + +Base: 1841 +Linked animations: [8177] +Linked graphics: 1505 + +Base: 1842 +Linked animations: [8366] +Linked graphics: 1480, 1652 + +Base: 1843 +Linked animations: [8268, 8269] +Linked graphics: 1356, 1357 + +Base: 1844 +Linked animations: [7825] +Linked graphics: 1472 + +Base: 1845 +Linked animations: [7822, 7823, 7826] +Linked graphics: 1470, 1471 + +Base: 1846 +Linked animations: [7812] +Linked graphics: 1318 + +Base: 1847 +Linked animations: [7706, 7707, 7708] +Linked graphics: 1400, 1401, 1402 + +Base: 1848 +Linked animations: [8057] +Linked graphics: 1358 + +Base: 1849 +Linked animations: [8135, 8138, 8140] +Linked graphics: 1499, 1500, 1501, 1502, 1503, 1506, 2006 + +Base: 1850 +Linked animations: [8016] +Linked graphics: 1322 + +Base: 1851 +Linked animations: [8120] +Linked graphics: 1352 + +Base: 1852 +Linked animations: [8017] +Linked graphics: 1320 + +Base: 1853 +Linked animations: [7860] +Linked graphics: 1390 + +Base: 1854 +Linked animations: [8169] +Linked graphics: 1343 + +Base: 1855 +Linked animations: [7709] +Linked graphics: 1403 + +Base: 1856 +Linked animations: [8167] +Linked graphics: 1341 + +Base: 1857 +Linked animations: [7697] +Linked graphics: 1456 + +Base: 1858 +Linked animations: [8178] +Linked graphics: 1504 + +Base: 1859 +Linked animations: [8253, 8254] +Linked graphics: 1328, 1330 + +Base: 1860 +Linked animations: [7710] +Linked graphics: 1404, 1511 + +Base: 1861 +Linked animations: [7723, 7725] +Linked graphics: 1458, 1459 + +Base: 1862 +Linked animations: [7801, 7802, 7806] +Linked graphics: 1410, 1411, 1413 + +Base: 1863 +Linked animations: [8149, 8150, 8151] +Linked graphics: 1385, 1386, 1387 + +Base: 1864 +Linked animations: [8263] +Linked graphics: 1485 + +Base: 1865 +Linked animations: [8125, 8126] +Linked graphics: 1354, 1355 + +Base: 1866 +Linked animations: [7836] +Linked graphics: 1512, 1513, 1514 + +Base: 1867 +Linked animations: [8002, 8003, 8004] +Linked graphics: 1346, 1347, 1348 + +Base: 1868 +Linked animations: [7742, 7743, 7744] +Linked graphics: 1434, 1435, 1436 + +Base: 1869 +Linked animations: [7811] +Linked graphics: 1317 + +Base: 1870 +Linked animations: [8217] +Linked graphics: 1460, 1461 + +Base: 1871 +Linked animations: [7884, 7886, 7888, 7889, 7890] +Linked graphics: 1373, 1375, 1376, 1377, 1378 + +Base: 1872 +Linked animations: [7857, 7859] +Linked graphics: 1362, 1363 + +Base: 1873 +Linked animations: [7984] +Linked graphics: 1491 + +Base: 1874 +Linked animations: [8161] +Linked graphics: 1388 + +Base: 1875 +Linked animations: [7861, 7862] +Linked graphics: 1391, 1392 + +Base: 1876 +Linked animations: [7767] +Linked graphics: 1469 + +Base: 1877 +Linked animations: [1642, 8495] +Linked graphics: 1515 + +Base: 1878 +Linked animations: [8415, 8416, 8417, 8418] + +Base: 1879 +Linked animations: [8399, 8400, 8401, 8402] + +Base: 1880 +Linked animations: [8427, 8428, 8429, 8430] + +Base: 1881 +Linked animations: [8376, 8377, 8378, 8379] + +Base: 1882 +Linked animations: [8411, 8412, 8413, 8414] + +Base: 1883 +Linked animations: [8451, 8452, 8453, 8454, 8491, 8492, 8493, 8494] + +Base: 1884 +Linked animations: [8423, 8424, 8425, 8426] + +Base: 1885 +Linked animations: [8395, 8396, 8397, 8398, 10554] + +Base: 1886 +Linked animations: [8431, 8432, 8433, 8434] + +Base: 1887 +Linked animations: [8443, 8444, 8445, 8446, 9315, 9316, 9317, 9318, 9319] + +Base: 1888 +Linked animations: [8419, 8420, 8421, 8422] + +Base: 1889 +Linked animations: [8479, 8480, 8481, 8482] + +Base: 1890 +Linked animations: [8467, 8468, 8469, 8470] + +Base: 1891 +Linked animations: [8435, 8436, 8437, 8438, 8439, 8440, 8441, 8442, 8455, 8456, 8457, 8458, 8459, 8460, 8461, 8462] + +Base: 1892 +Linked animations: [8463, 8464, 8465, 8466] + +Base: 1893 +Linked animations: [8471, 8472, 8473, 8474] + +Base: 1894 +Linked animations: [8487, 8488, 8489, 8490] + +Base: 1895 +Linked animations: [8483, 8484, 8485, 8486] + +Base: 1896 +Linked animations: [8407, 8408, 8409, 8410] + +Base: 1897 +Linked animations: [8504, 8505, 8506, 8507] +Linked NPCs: Giant wolpertinger (6990) + +Base: 1898 +Linked animations: [8503, 8508] +Linked graphics: 1516, 1517 + +Base: 1899 +Linked animations: [8587] +Linked graphics: 1522 + +Base: 1900 +Linked animations: [8526] +Linked objects: Wishing well (28715) + +Base: 1901 +Linked animations: [8509, 8510] +Linked objects: Obelisk (28716), Inert obelisk (28717), Inert obelisk (28718), Obelisk (28719), Inert obelisk (28720), Inert obelisk (28721), Obelisk (28722), Inert obelisk (28723), Inert obelisk (28724), Obelisk (28725), Inert obelisk (28726), Inert obelisk (28727), Obelisk (28728), Inert obelisk (28729), Inert obelisk (28730), Obelisk (28731), Inert obelisk (28732), Inert obelisk (28733), Obelisk (28734), Inert obelisk (28735), Inert obelisk (28736), Small obelisk (29882), Small obelisk (29938), Small obelisk (29939), Small obelisk (29940), Small obelisk (29941), Small obelisk (29942), Small obelisk (29943), Small obelisk (29944), Small obelisk (29945), Small obelisk (29946), Small obelisk (29947), Small obelisk (29948), Small obelisk (29949), Small obelisk (29950), Small obelisk (29951), Small obelisk (29952), Small obelisk (29953), Small obelisk (29954), Small obelisk (29955), Small obelisk (29956), Small obelisk (29957), Small obelisk (29958), Small obelisk (29959), Small obelisk (44837), Small obelisk (44838), Small obelisk (44839), Small obelisk (44840), Small obelisk (44841), Small obelisk (44842), PLACEHOLDER: Summoning Obelisk (50204) + +Base: 1902 +Linked animations: [8521] +Linked graphics: 1520 + +Base: 1903 +Linked animations: [8513, 8514, 8515, 8562, 8569, 8570, 8571, 8572, 8575, 11210] +Linked NPCs: Jelly (1637), Jelly (1638), Jelly (1639), Jelly (1640), Jelly (1641), Jelly (1642), Spirit jelly (6992), Spirit jelly (6993), Jelly (7459), Jelly (7460), Jelly (8599), Jelly (8617), Jelly (10699) + +Base: 1904 +Linked animations: [8520] +Linked graphics: 1519 + +Base: 1905 +Linked animations: [2090, 2100, 2101, 2102, 3380, 3381, 3460, 3462, 8573, 8574, 8576, 8577, 8578, 8620, 8621, 8622, 8626, 8627, 8628, 8629, 8630, 8631, 8632, 8633, 8634, 8635, 8636, 8637, 8638, 8639, 8640, 8641, 8642, 8643, 8644, 8649, 8650, 8655, 10625, 10626, 11295, 11296, 11302, 11358, 11375, 11403, 11404, 11405, 11415, 11416, 11417, 11418, 11419, 11420, 11421, 11422, 11423, 11424, 11425, 11426, 11427, 11428, 11431, 11432, 11433, 11434, 11435, 11436, 11437, 11438, 11439, 11441, 11442, 11443, 11444, 11445, 11446, 11448, 11449, 11450, 11454, 11457, 11458, 11459, 11460, 11461, 12302, 12512, 13837, 13838, 13839] +Linked NPCs: Jogre (113), Mogre (114), Ogre (115), Khazard ogre (270), Ogre (374), Og (853), Grew (854), Toban (855), Gorad (856), Ogre guard (857), Ogre guard (858), Ogre guard (859), Ogre guard (860), Ogre guard (861), City guard (862), Enclave guard (870), Ogre trader (873), Ogre merchant (874), Ogre trader (875), Ogre trader (876), Kolodion (908), Rantz (1010), Fycie (1011), Bugs (1012), Grish (2038), Uglug Nar (2039), Pilg (2040), Grug (2041), Ogre guard (2042), Ogre guard (2043), Zogre (2044), Zogre (2045), Zogre (2046), Zogre (2047), Zogre (2048), Zogre (2049), Skogre (2050), Zogre (2051), Zogre (2052), Zogre (2053), Zogre (2054), Zogre (2055), Skogre (2056), Skogre (2057), Slash Bash (2060), Gargh (2063), Scarg (2064), Gruh (2065), Ogre shaman (2417), Ogre shaman (2418), Grunsh (2419), Ogre shaman (2434), Ogre (2801), Jogre Champion (3063), Skrach Uglogwee (3398), Ogre (3419), Mogre Guard (3420), Jogre (3449), Jogre (3450), Ogre (3451), Nung (3465), Ogre (3466), Rantz (3467), Rantz (3468), Rantz (3587), null (4471), Bogrog (4472), Ogre chieftain (5174), Jogre (6268), Frawd (7048), Ogress banker (7049), Ogress banker (7050), Chief Tess (7051), Seegud (7052), Chargurr (7053), Chargurr (7054), null (7055), null (7056), Snurgh (7057), Kringk (7058), Thump (7061), Muggh (7062), Snert (7067), Tyke (7068), Snarrl (7069), Snarrk (7070), I'rk (7071), Thuddley (7072), Grr'bah (7073), Chomp (7074), Grubb (7075), Grunther (7076), Glum (7077), Ogress champion (7078), Ogress warrior (7079), Ogress warrior (7080), Ogress (7081), Ogress (7082), Ogre statue (7085), Chief Tess (7088), Chargurr (7089), null (7099), null (7101), null (7522), Mystic (8232), Mystic (8237), Ogre (8658), null (8659), Ogre shaman (8798), Ogre (9184), Ogre (9185), Pit ogre (11563), Pit ogre (11586), Pit ogre (11587) +Linked objects: Pit ogre (39261), Pit ogre (41604), Ogre statue (42592), Ogre statue (42593), Ogre statue (42605), Ogre statue (42608) + +Base: 1906 +Linked animations: [8579, 8580, 8583, 8584, 8585, 8586, 8598, 8656, 8657, 8658, 8659, 8660, 8661, 8662] + +Base: 1907 +Linked animations: [8589, 8590, 8591, 8592, 8593, 8594, 8595, 8596] + +Base: 1908 +Linked animations: [8599, 8600, 8601, 8602, 8603, 8604, 8605, 8606, 8607] +Linked NPCs: Grenwall (7010), Grenwall (7011) + +Base: 1909 +Linked animations: [8608, 8609, 8610, 8611, 8612, 8613, 8614, 8615] +Linked NPCs: Pawya (7012), Pawya (7014) + +Base: 1910 +Linked animations: [8625] + +Base: 1911 +Linked animations: [8645] + +Base: 1912 +Linked animations: [8668, 8669, 8670, 8671, 8672, 8673, 8680] +Linked NPCs: Platypus (7015), Platypus (7016), Platypus (7017), Baby platypus (7018), Baby platypus (7019), Baby platypus (7020), Platypus (7021), Platypus (7022), Platypus (7023), Baby platypus (7024), Baby platypus (7025), Baby platypus (7026), Patrick (7027), Penelope (7028), Peter (7029), Peanut (7030), null (7091), null (7092), null (7093), null (7094), null (7095), null (7096), null (7097), null (7098), Platypus (9304), Platypus (9305), Platypus (9306) + +Base: 1913 +Linked animations: [8619] +Linked NPCs: Thump (7059) + +Base: 1914 +Linked animations: [8618] +Linked objects: null (4105), Salon customer (29103) + +Base: 1915 +Linked animations: [8646] +Linked objects: Grimechin (29106), null (29108) + +Base: 1916 +Linked animations: [8654] +Linked objects: null (29121) + +Base: 1917 +Linked animations: [8653] +Linked objects: null (29083) + +Base: 1918 +Linked animations: [8664] +Linked objects: Sulphur spring (29016), Sulphur spring (29017), Rocks (29019), Sulphur spring (29021), Sulphur spring (29023), Sulphur spring (29026), Sulphur spring (29028) + +Base: 1919 +Linked animations: [8648, 8667] +Linked NPCs: Flying bugs (7083) +Linked objects: null (29170), null (29171), null (29172), null (29173), null (29174), null (29175) + +Base: 1920 +Linked animations: [8665] +Linked objects: Thermal bath (29043), Rocks (29045), Thermal bath (29047), Thermal bath (29049), Thermal bath (29052), Thermal bath (29054) + +Base: 1921 +Linked animations: [8666] +Linked objects: Salt-water spring (29030), Rocks (29032), Salt-water spring (29034), Salt-water spring (29036), Salt-water spring (29039), Salt-water spring (29041), Bandos pool (29056), Rocks (29058), Bandos pool (29060), Bandos pool (29062), Bandos pool (29063), Bandos pool (29065), Bandos pool (29066), Bandos pool (29067) + +Base: 1922 +Linked animations: [8663] +Linked objects: Mud bath (29002), Mud bath (29003), Rocks (29004), Rocks (29005), Mud bath (29007), Mud bath (29009), Mud bath (29012), Mud bath (29014) + +Base: 1923 +Linked animations: [11455, 11456] +Linked graphics: 2025, 2026 + +Base: 1924 +Linked animations: [8696] +Linked graphics: 1535 + +Base: 1925 +Linked animations: [8623, 8652] +Linked graphics: 1527, 1528 + +Base: 1926 +Linked animations: [8674, 8675, 8676, 8677, 8678, 8679] +Linked graphics: 1529, 1530, 1531, 1532, 1533, 1534 + +Base: 1927 +Linked animations: [8697] +Linked graphics: 1536 + +Base: 1928 +Linked animations: [11452, 11453] +Linked NPCs: null (3469), null (3470), Ogre boat (3471), Ogre boat (3472) + +Base: 1929 +Linked animations: [11451] + +Base: 1930 +Linked animations: [2415, 11818] +Linked graphics: 1019, 1537 +Linked objects: null (43520) + +Base: 1931 +Linked animations: [2419, 8703] +Linked graphics: 1539, 1540 + +Base: 1932 +Linked animations: [1221] + +Base: 1933 +Linked animations: [1220] + +Base: 1934 +Linked animations: [1222, 1223, 1224, 1225, 1226, 1227, 1228, 9664, 9722] +Linked objects: Catapult (2781), null (3976), Catapult (29436), Rotten Catapult (29437), Catapult (29438) + +Base: 1935 +Linked animations: [2416, 2417, 2418, 8700, 9576] +Linked graphics: 1538, 1669 +Linked objects: Explosion (18504), null (29429), null (29430), null (29431), Target rock (29433) + +Base: 1936 +Linked animations: [8714] +Linked objects: null (29521) + +Base: 1937 +Linked animations: [8713] + +Base: 1938 +Linked animations: [8708, 8712] +Linked graphics: 1541 +Linked objects: Flag (29457), Flag (29459) + +Base: 1939 +Linked animations: [8716] +Linked graphics: 1543 + +Base: 1940 +Linked animations: [8715] +Linked graphics: 1542 + +Base: 1941 +Linked animations: [8752, 8753, 8754, 8755, 8756, 8757, 8758] +Linked NPCs: Bork (7133), Bork (7134) + +Base: 1942 + +Base: 1943 +Linked animations: [8751, 8760, 8761, 8762, 8763, 8764, 8765] +Linked NPCs: Ork legion (7135) + +Base: 1944 +Linked animations: [8741, 8742, 8743] +Linked graphics: 1546, 1547, 1548 + +Base: 1945 +Linked animations: [8749] +Linked graphics: 1549 + +Base: 1946 +Linked animations: [8759] +Linked graphics: 1551 + +Base: 1947 +Linked animations: [8750] +Linked graphics: 1550 + +Base: 1948 +Linked animations: [8748, 8766, 10964] +Linked graphics: 1552, 1951 + +Base: 1949 +Linked animations: [8745, 8746] + +Base: 1950 +Linked animations: [8744] + +Base: 1951 +Linked animations: [8747] + +Base: 1952 +Linked animations: [8802, 8803] + +Base: 1953 +Linked animations: [8769] +Linked graphics: 1553 + +Base: 1954 +Linked animations: [8801] +Linked graphics: 1554 + +Base: 1955 +Linked animations: [8785, 8786, 8787, 8788, 8789, 8790, 8791, 8792, 8793, 8794, 8795, 8796, 8797, 8798, 8799] +Linked NPCs: Cockroach drone (7158), Cockroach worker (7159), Cockroach soldier (7160), Cockroach (7206), Warped cockroach (7913) + +Base: 1956 +Linked animations: [8857] +Linked objects: null (29800), null (29801), null (29802), Barrel (29803), Broken lobster trap (29804), null (29805) + +Base: 1957 +Linked animations: [8880, 8881] +Linked objects: Villagers (29739) + +Base: 1958 +Linked animations: [8825, 8826, 8827, 8828] + +Base: 1959 + +Base: 1960 +Linked animations: [8816, 8818, 8819] +Linked NPCs: Kimberly (7170) + +Base: 1961 +Linked animations: [8811, 8812, 8822] +Linked NPCs: Kennith (7174) + +Base: 1962 +Linked animations: [8856] +Linked graphics: 1564 + +Base: 1963 +Linked animations: [8823, 8850, 8851] +Linked graphics: 1558, 1559, 1560 + +Base: 1964 +Linked animations: [8853, 8854, 8855] +Linked graphics: 1561, 1562, 1563 + +Base: 1965 +Linked animations: [8810] +Linked graphics: 1555 + +Base: 1966 +Linked animations: [8813] +Linked graphics: 1556 + +Base: 1967 +Linked animations: [8882] +Linked graphics: 1567 + +Base: 1968 +Linked animations: [8896] +Linked graphics: 1568 + +Base: 1969 +Linked animations: [8886] +Linked graphics: 1570 + +Base: 1970 + +Base: 1971 +Linked animations: [8885] +Linked graphics: 1569 + +Base: 1972 +Linked animations: [8895] + +Base: 1973 +Linked animations: [8883] +Linked graphics: 1566 + +Base: 1974 +Linked animations: [8887, 8897] +Linked objects: Chocolate pool (29869) + +Base: 1975 +Linked animations: [8888, 8889, 8890, 8891, 8892, 8893, 11513] +Linked objects: Incubator coal scuttle (29855), Incubator coal scuttle (29856), Incubator (29857), Incubator (29858), Incubator (29859), Incubator (29860), Incubator (29861), Incubator (29862), Incubator (29863), null (29878), null (29879), null (29880), Incubator (42743), Oven (49130) + +Base: 1976 +Linked animations: [8894] +Linked objects: Chocolate mixer (29840), Chocolate mixer (29841), null (29842) + +Base: 1977 +Linked animations: [8884] + +Base: 1978 +Linked animations: [8912, 8913] +Linked graphics: 1571, 1572 + +Base: 1979 +Linked animations: [8935, 8936] +Linked graphics: 1573, 1574 + +Base: 1980 +Linked animations: [8940, 8942] +Linked graphics: 1576, 1577, 1587, 1588, 1678, 1679 + +Base: 1981 +Linked animations: [11484] + +Base: 1982 +Linked animations: [8955, 8956, 8957, 8958, 8959, 8960, 8961, 8969, 8971, 8972, 11488] +Linked NPCs: Gnoeals (7381), Gnoeals (7382), Gnoeals (7392), Gnoeals (7393) + +Base: 1983 +Linked animations: [1539, 8945] +Linked objects: Fremennik warrior (25307), Fremennik warrior (25313), Fremennik warrior (25358), null (30019) + +Base: 1984 +Linked animations: [8950] +Linked graphics: 2028 + +Base: 1985 +Linked animations: [8974] +Linked objects: null (38178), null (38179), null (38180), null (38181) + +Base: 1986 +Linked animations: [8970] +Linked objects: Windsock (30070) + +Base: 1987 +Linked animations: [8962, 8963, 8964] +Linked graphics: 1580, 1581, 1582, 2027 + +Base: 1988 +Linked animations: [11567, 11568] +Linked objects: Fallen log (42855), Fallen log (42856), Canoe (42857) + +Base: 1989 +Linked animations: [11552] + +Base: 1990 + +Base: 1991 +Linked animations: [9014, 9015, 9611] +Linked graphics: 1589, 1590, 1680 + +Base: 1992 +Linked animations: [8992] +Linked graphics: 1583 + +Base: 1993 +Linked animations: [8994] +Linked graphics: 1585 + +Base: 1994 +Linked animations: [8995] +Linked graphics: 1586 + +Base: 1995 +Linked animations: [8993] +Linked graphics: 1584 + +Base: 1996 +Linked animations: [9017, 9019] +Linked graphics: 1591, 1592 + +Base: 1997 +Linked animations: [9007, 9008] +Linked objects: null (30193), null (30194), null (30195), null (30196), null (30197), null (30198), null (30199), null (30200), null (30201), null (30202), null (30242) + +Base: 1998 +Linked animations: [9005, 9006, 10446, 10449] +Linked objects: Energy barrier (30141) + +Base: 1999 +Linked animations: [9010] +Linked objects: Scoreboard (30205) + +Base: 2000 +Linked animations: [9011] +Linked objects: Stone dispenser (30143) + +Base: 2001 +Linked animations: [9009] +Linked NPCs: Fiara (7600) + +Base: 2002 +Linked animations: [328, 9124, 12526] +Linked graphics: 74, 86, 184, 188, 267, 1605, 2190 + +Base: 2003 +Linked animations: [9041, 9042, 9043] +Linked objects: null (30622) + +Base: 2004 +Linked animations: [9083, 9084, 9085, 9088, 9089, 9090] +Linked objects: Safalaan (30460), Andiess Juip (30462), Kael Forshaw (30463), Safalaan (30464), Andiess Juip (30465), Kael Forshaw (30466) + +Base: 2005 +Linked animations: [9036] +Linked objects: null (30568) + +Base: 2006 +Linked animations: [9056] + +Base: 2007 +Linked animations: [9101] +Linked objects: null (2464) + +Base: 2008 +Linked animations: [9033, 9034, 9035] +Linked objects: Door (30526), Door (30527) + +Base: 2009 +Linked animations: [9122] + +Base: 2010 +Linked animations: [9103] +Linked graphics: 1603 + +Base: 2011 +Linked animations: [9046] +Linked graphics: 1594 + +Base: 2012 +Linked animations: [9067] +Linked graphics: 1598 + +Base: 2013 +Linked animations: [9029, 9030] +Linked graphics: 1595, 1596 + +Base: 2014 +Linked animations: [9079, 9080, 9081, 9082] +Linked graphics: 1599, 1600, 1601, 1602 + +Base: 2015 +Linked animations: [9123] +Linked objects: Holy barrier (3443) + +Base: 2016 +Linked animations: [9075, 9076, 9077, 9078] + +Base: 2017 +Linked animations: [7580, 9102, 9130, 9131, 9132, 9133, 9134, 9430, 9431, 9432, 9433, 9434, 9435, 9436, 9437, 9438] +Linked NPCs: Bloodveld (1618), Bloodveld (1619), Bloodveld (6215), Mutated bloodveld (7642), Mutated bloodveld (7643) + +Base: 2018 +Linked animations: [9037, 9093, 9094, 9125, 9126, 9127, 9128, 9129, 9444, 9445, 9446, 9447, 9448, 9493, 9494, 9495, 9496] +Linked NPCs: Crawling Hand (1648), Crawling Hand (1649), Crawling Hand (1650), Crawling Hand (1651), Crawling Hand (1652), Crawling Hand (1653), Crawling Hand (1654), Crawling Hand (1655), Crawling Hand (1656), Crawling Hand (1657), null (7637), null (7638), null (7639), Skeletal hand (7640), Zombie hand (7641), Creeping hand (8619), Creeping hand (9246), Crawling hand (10694) + +Base: 2019 +Linked animations: [9038] +Linked graphics: 1593 + +Base: 2020 +Linked animations: [9135] +Linked objects: null (30755), A stream of light (37280) + +Base: 2021 +Linked animations: [2935, 9911, 10120, 10121] +Linked objects: null (2452), null (2453), null (2454), null (2455), null (2456), null (2457), null (2458), null (2459), null (2460), null (2461), null (2462), Mysterious ruins (7103), Mysterious ruins (7104), Mysterious ruins (7105), Mysterious ruins (7106), Mysterious ruins (7107), Mysterious ruins (7108), Mysterious ruins (7109), Mysterious ruins (7110), Mysterious ruins (7111), Mysterious ruins (7112), Mysterious ruins (7113), Mysterious ruins (7114), Mysterious ruins (7115), Mysterious ruins (7116), Mysterious ruins (7117), Mysterious ruins (7118), Mysterious ruins (7119), Mysterious ruins (7120), Mysterious ruins (7121), Mysterious ruins (7122), Mysterious ruins (7123), Mysterious ruins (7124) + +Base: 2022 +Linked animations: [9148] + +Base: 2023 +Linked animations: [9147] + +Base: 2024 +Linked animations: [9142] + +Base: 2025 +Linked animations: [4292, 9146, 9149] +Linked objects: Parachuting gnome (31029) + +Base: 2026 +Linked animations: [4294] + +Base: 2027 +Linked animations: [9137] +Linked objects: Convertor (30812), Machine (30961) + +Base: 2028 +Linked animations: [9144] +Linked objects: null (31008), null (31009) + +Base: 2029 +Linked animations: [9150] +Linked objects: Remains (31015), Tent (31016), Tent (31017), null (31032) + +Base: 2030 +Linked animations: [4295, 4297] +Linked objects: Dragon machine (30808), Machine (30810), null (31033) + +Base: 2031 +Linked animations: [4296] +Linked objects: Machine (30809), Machine (30811), null (31034) + +Base: 2032 +Linked animations: [4290, 4291] +Linked objects: null (9260), Dying roses (30804), Growing roses (30805), Roses (30806), Roses (30807) + +Base: 2033 +Linked animations: [9143, 10122] +Linked objects: Nesting bird (31006), Nesting bird (31007), null (31040), Pillar (38420) + +Base: 2034 +Linked animations: [2870, 9154, 9512, 9589, 10237, 10976, 11961] +Linked objects: null (206), null (207), null (11372), null (26196), null (31143), null (31145), null (31747), null (31795), null (31796), null (31930), null (32018), null (32019), null (32020), null (32021), Fire (32099), null (32100), Cooking pot (32105), null (32682), null (32683), null (32684), null (32740), null (32976), null (33073), null (33074), null (33160), null (33469), null (33470), null (33471), null (37024), null (37899), Unlit beacon (38429), Unlit beacon (38433), Unlit beacon (38437), Warning beacon (38441), Emergency beacon (38442), Warning beacon (38443), Emergency beacon (38444), Warning beacon (38445), Emergency beacon (38446), null (38448), null (38449), null (38450), null (38451), null (38452), null (38453), null (38454), null (38455), null (38456), null (38457), null (38458), null (38459), null (38460), null (38461), Cave wall (41009), null (41362), Orb (41371), null (41376), null (41377), null (41378), null (41379), null (41380), null (41511), null (41752), null (41778), null (41834), null (42492), Spit (44277), null (45559), null (46213), null (47044), null (47045), null (47046), null (48504), null (48544), null (48732), null (48770), null (48771), Pillar of light (49266), Pillar of light (49267), null (50605), null (50606), null (50607), null (50608), null (50758), null (50843), null (50844), null (50845), null (50846), null (50847), null (50848), null (50849), Torch (50991), Brazier (50993), null (50995), Brazier (50997), null (51062), null (51114), null (51115), null (51148), null (51157), null (51158), null (51159), null (51160), null (51309), null (51389), null (51390), null (51391), null (51392), null (51393), null (51394), null (51395), Torch (51537), Brazier (51539), null (51541), Brazier (51543), null (51610), Tangle gum fire (51663), Seeping elm fire (51664), Blood spindle fire (51665), Utuku fire (51666), Spinebeam fire (51667), Bovistrangler fire (51668), Thigat fire (51669), Corpsethorn fire (51670), Entgallow fire (51671), Grave creeper fire (51672), Fire remains (51673), Fire remains (51674), Fire remains (51675), Fire remains (51676), Fire remains (51677), Fire remains (51678), Fire remains (51679), Fire remains (51680), Fire remains (51681), Fire remains (51682), null (51705), null (51706), null (51707), null (51708), null (51857), null (51937), null (51938), null (51939), null (51940), null (51941), null (51942), null (51943), Torch (52085), Brazier (52087), null (52089), Brazier (52091), null (52155), Pillar of light (52250), null (52264), null (52271), null (52333), null (53040), null (53041), null (53369), null (53377), null (53385), null (53740), Censer (54102), Censer (54103), Censer (54104), Censer (54105), null (54701), null (54702), null (54703), null (54710), null (54711), null (54720), null (54731), null (54732), null (54764), null (54765), null (54766), Torch (54958), Candle (54960), Candle (54962), Candle (54964), null (55087), null (55100), null (55101), null (55102), null (55103), null (55104), null (55105), null (55106), null (55107), null (55108), Altar (55127) + +Base: 2035 +Linked animations: [9156, 9157, 9158, 9159, 9160, 9161, 9162, 9163, 9164, 9165, 9166, 9167, 9168, 9169, 9170, 9171, 9172, 9173, 9174, 9175, 9200, 9205, 9206, 9522, 9524, 9525, 9527, 9528, 9529, 10235, 10241] +Linked NPCs: null (759), Kitten (761), Kitten (762), Kitten (763), Kitten (764), Kitten (765), Kitten (766), Cat (768), Cat (769), Cat (770), Cat (771), Cat (772), Cat (773), Overgrown cat (774), Overgrown cat (775), Overgrown cat (776), Overgrown cat (777), Overgrown cat (778), Overgrown cat (779), Witch's cat (993), Bob (1091), Neite (1992), Red Axe Cat (2108), Dromund's cat (2140), Red Axe Cat (2229), Red Axe Cat (2230), null (2405), Evil Bob (2478), Evil Bob (2479), Bob (2635), Bob (2636), Neite (2638), Neite (2646), Bob (2647), Beite (2648), null (2652), Neite (2653), Lazy cat (2662), Lazy cat (2663), Lazy cat (2664), Lazy cat (2665), Lazy cat (2666), Lazy cat (2667), Wily cat (2668), Wily cat (2669), Wily cat (2670), Wily cat (2671), Wily cat (2672), Wily cat (2673), The Beast (2941), Bellemorde (2942), Pox (2943), Pox (2944), Bones (2945), Turbogroomer (2983), Pusskins (2984), Loki (2985), Captain Tom (2986), Treacle (2987), Mittens (2988), Claude (2989), Topsy (2990), Gertrude's cat (2997), Overgrown hellcat (3503), Hellcat (3504), Hell-kitten (3505), Lazy hellcat (3506), Wily hellcat (3507), Clockwork cat (3598), Black Cat (4607), Cat (4768), Cat (4769), HRH Hrafn (5479), Del-Monty (5563), Clockwork cat (5605), Clockwork cat (5607), Fluffs (7742), Fluffs (7743), null (7744), null (7784), Ali Cat (7785), null (8073), Trogs (8202), Cat (8214), Lazy cat (8215), Overgrown cat (8216), Kitten (8217), Wily cat (8218), Clockwork cat (8736), Cat (9272), Cat (9273), Cat (9274), Cat (9275), Cat (9276), Cat (9277), Hellcat (9278), Cat (9279), Lazy cat (9280), Lazy cat (9281), Lazy cat (9282), Lazy cat (9283), Lazy cat (9284), Lazy cat (9285), Lazy hellcat (9286), Lazy cat (9287), Overgrown cat (9288), Overgrown cat (9289), Overgrown cat (9290), Overgrown cat (9291), Overgrown cat (9292), Overgrown cat (9293), Overgrown hellcat (9294), Overgrown cat (9295), Wily cat (9296), Wily cat (9297), Wily cat (9298), Wily cat (9299), Wily cat (9300), Wily cat (9301), Wily hellcat (9302), Wily cat (9303) +Linked graphics: 1606, 1610, 1611, 1612, 1613, 1614, 1615, 1660, 1661 + +Base: 2036 +Linked animations: [9194, 9195, 9196, 9197, 9204, 9207, 9208, 9209, 9210, 9211, 9584] +Linked NPCs: Kittens (760), Love Cats (2644), Love Cats (2645), Three little kittens (7741), Kittens (7745) + +Base: 2037 +Linked animations: [9198] +Linked NPCs: Jiggling crate (7740) + +Base: 2038 +Linked animations: [9176] +Linked graphics: 1608 + +Base: 2039 +Linked animations: [9155, 9177] +Linked graphics: 1607, 1609 + +Base: 2040 +Linked animations: [9199, 12394, 12395, 12396, 12397] +Linked objects: null (31160), null (31161), Conveyor belt (46298), Conveyor belt (46299), Plank stack (empty) (46310), Planks (46311), Planks (46312), Planks (46313), Planks (46314), Planks (46315), Planks (46316) + +Base: 2041 +Linked animations: [9246, 9247, 9248, 9249, 9250, 9251, 9252, 9253, 9254, 9255, 9256, 9257] +Linked NPCs: Yt-MejKot (2741), Yt-MejKot (2742), Yt-HurKot (2746) + +Base: 2042 +Linked animations: [9233, 9234, 9235, 9236, 9237, 9238] +Linked NPCs: Tz-Kek (2629), Tz-Kek (2630), Tz-Kek (2736), Tz-Kek (2737), Tz-Kek (2738) + +Base: 2043 +Linked animations: [9273, 9274, 9276, 9277, 9278, 9279, 9300] +Linked NPCs: TzTok-Jad (2745) + +Base: 2044 +Linked animations: [9228, 9229, 9230, 9231, 9232] +Linked NPCs: Tz-Kih (2627), Tz-Kih (2628), Tz-Kih (2734), Tz-Kih (2735) + +Base: 2045 +Linked animations: [9260, 9280, 9281, 9282, 9283, 9284, 9285, 9286, 9287, 9288, 9289, 9290, 9291, 9292, 9293, 9294, 9295, 9296, 9297, 9298, 9299, 9311, 9312, 9313, 9314, 9320, 9321, 9322, 9323, 9324, 9325, 9326, 9345, 9349] +Linked NPCs: TzHaar-Mej (2591), TzHaar-Mej (2592), TzHaar-Mej (2593), TzHaar-Mej (2594), TzHaar-Mej (2595), TzHaar-Mej (2596), TzHaar-Mej (2597), TzHaar-Hur (2598), TzHaar-Hur (2599), TzHaar-Hur (2600), TzHaar-Hur (2601), TzHaar-Hur (2602), TzHaar-Hur (2603), TzHaar-Xil (2604), TzHaar-Xil (2605), TzHaar-Xil (2606), TzHaar-Xil (2607), TzHaar-Xil (2608), TzHaar-Xil (2609), TzHaar-Ket (2610), TzHaar-Ket (2611), TzHaar-Ket (2612), TzHaar-Ket (2613), TzHaar-Ket (2614), TzHaar-Ket (2615), TzHaar-Ket (2616), TzHaar-Mej-Jal (2617), TzHaar-Mej-Kah (2618), TzHaar-Ket-Zuh (2619), TzHaar-Hur-Tel (2620), TzHaar-Hur-Lek (2622), TzHaar-Mej-Roh (2623), TzHaar-Ket (2624), TzHaar-Ket (2625), Mystery figure (3054), TzHaar-Mej-Malk (7746), TzHaar-Xil-Tog (7747), TzHaar-Hur-Frok (7748), TzHaar-Ket-Grol (7749), TzHaar-Ket-Rok (7750), TzHaar-Ket-Lurk (7751), TzHaar-Mej-Lor (7752), TzHaar-Mej (7753), TzHaar-Hur (7754), TzHaar-Xil (7755), Library assistant (7756), TzHaar-Hur-Brekt (7757), TzHaar-Hur-Brekt (7758), TzHaar-Ket-Jok (7759), TzHaar-Ket-Jok (7760), TzHaar-Xil-Mor (7761), TzHaar-Xil-Mor (7762), TzHaar-Mej-Kol (7763), TzHaar-Mej-Kol (7764), TzHaar-Hur-Klag (7765), TzHaar-Hur-Klag (7766), TzHaar-Hur (7767), TzHaar-Hur (7768), TzHaar-Hur (7769) + +Base: 2046 +Linked animations: [9264, 9265, 9266, 9267, 9268, 9269] +Linked NPCs: Ket-Zek (2743), Ket-Zek (2744) + +Base: 2047 +Linked animations: [9239, 9240, 9241, 9242, 9243, 9245] +Linked NPCs: Tok-Xil (2631), Tok-Xil (2632), Tok-Xil (2739), Tok-Xil (2740), Tok-Xil (3592) +Linked objects: Tok-Xil (13377) + +Base: 2048 +Linked animations: [9258] +Linked graphics: 1617 + +Base: 2049 +Linked animations: [9244] +Linked graphics: 1616 + +Base: 2050 +Linked animations: [9270, 9271, 9272] +Linked graphics: 1622, 1623, 1624 + +Base: 2051 +Linked animations: [9301, 9302] +Linked graphics: 1626, 1627 + +Base: 2052 +Linked animations: [9303, 9348] +Linked objects: null (31177), null (31178), null (31179), null (31180), null (31181), null (31182), null (31183), null (31184), null (31185), null (31186), null (31187), null (31188), null (31189), null (31190), null (31191), null (31192), null (31193), null (31194), null (31195), null (31196), Stepping stone (31214), Stepping stone (31215), Stepping stone (31216), Stepping stone (31217), Stepping stone (31218), Stepping stone (31219), Stepping stone (31220) + +Base: 2053 +Linked animations: [9259] +Linked graphics: 1618 + +Base: 2054 +Linked animations: [9275, 10497] +Linked graphics: 1625, 1834 + +Base: 2055 +Linked animations: [9261, 9262] +Linked graphics: 1619, 1620 + +Base: 2056 +Linked animations: [9263] +Linked graphics: 1621 + +Base: 2057 +Linked animations: [9327, 9330] +Linked objects: Rock (31257) + +Base: 2058 +Linked animations: [9328, 9329] +Linked objects: Pillar (31250) + +Base: 2059 +Linked animations: [9331, 9332, 9333, 9334, 9335, 9336] +Linked NPCs: TokTz-Ket-Dill (7770), TokTz-Ket-Dill (7771) + +Base: 2060 +Linked animations: [9341, 9342, 9343, 9344] +Linked NPCs: Fire monster (7773) + +Base: 2061 +Linked animations: [9337, 9338, 9339, 9340] +Linked NPCs: Lava monster (7772) + +Base: 2062 +Linked animations: [9346] +Linked graphics: 1629 + +Base: 2063 +Linked animations: [9347] +Linked objects: null (31285) + +Base: 2064 +Linked animations: [9351] +Linked objects: Signpost (31296) + +Base: 2065 + +Base: 2066 +Linked animations: [9352] +Linked graphics: 1630, 1631, 1632 + +Base: 2067 +Linked animations: [1716, 9353, 9354, 9355, 9356, 9357] +Linked graphics: 312, 1633, 1634, 1635, 1636, 1637 + +Base: 2068 + +Base: 2069 +Linked animations: [9369, 9370, 9371, 9372] +Linked objects: Broken door (31336), Broken door (31337), Broken door (31338), Broken door (31339) + +Base: 2070 +Linked animations: [9397] + +Base: 2071 +Linked animations: [9388, 9389] +Linked objects: Sticks (31306) + +Base: 2072 +Linked animations: [9390, 9391] + +Base: 2073 +Linked animations: [9426, 9427] + +Base: 2074 +Linked animations: [9424, 9425] +Linked objects: Wavering mystic barrier (31314), Wavering mystic barrier (31315) + +Base: 2075 +Linked animations: [9520] +Linked objects: Bucket rope (31316) + +Base: 2076 +Linked animations: [9518, 9519] +Linked objects: Mystic barrier (31435), Mystic barrier (31436) + +Base: 2077 +Linked animations: [9408] +Linked objects: Subterranean spring (31421) + +Base: 2078 +Linked animations: [9383, 9384] +Linked objects: Pulsing markings (31363) + +Base: 2079 +Linked animations: [9377, 9423] +Linked graphics: 1640 +Linked objects: Flames (31317) + +Base: 2080 +Linked animations: [9373] +Linked graphics: 1638, 1639 + +Base: 2081 +Linked animations: [9404] +Linked graphics: 1651 + +Base: 2082 +Linked animations: [9399, 9400, 9401] +Linked graphics: 1647, 1648, 1649 + +Base: 2083 +Linked animations: [9530, 9531, 9532] +Linked graphics: 1662, 1663, 1664 + +Base: 2084 +Linked animations: [9420, 9421, 9422] +Linked graphics: 1653, 1654, 1655 + +Base: 2085 +Linked animations: [9385] +Linked graphics: 1644 + +Base: 2086 +Linked animations: [9379, 9380, 9381] +Linked graphics: 1641, 1642, 1643 + +Base: 2087 +Linked animations: [9517] +Linked graphics: 1659 + +Base: 2088 +Linked animations: [9403] +Linked graphics: 1650 + +Base: 2089 +Linked animations: [1553, 9396] +Linked graphics: 1646, 2274 + +Base: 2090 +Linked animations: [9393, 9394] +Linked graphics: 1645 + +Base: 2091 +Linked animations: [265, 266, 267, 268, 298, 9414, 9415, 9416, 9417, 9418, 9419] +Linked NPCs: Cave crawler (1600), Cave crawler (1601), Cave crawler (1602), Cave crawler (1603), Cave crawler (7787), Monstrous cave crawler (7798), Cave crawler (7812), Cave crawler (10695) + +Base: 2092 +Linked animations: [1532, 1533, 9454, 9455, 9456, 9457, 9458, 10423, 10428, 10429, 10450] +Linked NPCs: Gargoyle (1610), Sharathteerk (8165), null (8166), Sharathteerk (8167), null (8184), Gargoyle (9087) +Linked objects: Statue (5116), Statue (5117) + +Base: 2093 +Linked animations: [9375, 9409, 9410, 9411, 9413, 9471, 9472, 9473, 9474, 9475, 9476, 9477, 9478, 9479, 9480, 9481, 9482, 9483, 9484, 9485, 9486] +Linked NPCs: Swarming turoth (1611), Turoth (1622), Turoth (1623), Turoth (1626), Turoth (1627), Turoth (1628), Turoth (1629), Turoth (1630), Mightiest turoth (7800), Turoth (7814) +Linked graphics: 1656 + +Base: 2094 +Linked animations: [260, 261, 262, 263, 264, 9533, 11218] +Linked NPCs: Basilisk (1616), Basilisk (1617), Basilisk boss (7799), Basilisk (7813), Baby basilisk (8621), Baby basilisk (9248) + +Base: 2095 +Linked animations: [9466, 9467, 9468, 9469, 9470, 12519] +Linked NPCs: Aberrant spectre (1604), Aberrant spectre (1605), Aberrant spectre (1606), Aberrant spectre (1607), Aberrant spectre (7801), Aberrant spectre (7802), Aberrant spectre (7803), Aberrant spectre (7804), Aberrant Spectre Champion (8987) + +Base: 2096 +Linked animations: [9459, 9460, 9461, 9462, 9463, 9464] +Linked NPCs: Death spawn (1614), Death spawn (10703) + +Base: 2097 +Linked animations: [9465] +Linked graphics: 827 + +Base: 2098 +Linked animations: [9374, 9412, 9439, 9440, 9441, 9442, 9443] +Linked NPCs: Kurask (1608), Kurask (1609), Kurask overlord (7797), Kurask minion (7805), Kurask (7811) + +Base: 2099 +Linked animations: [5498, 9378, 9382, 9449, 9450, 9451, 9452, 9453] +Linked NPCs: Banshee (1612), Mighty banshee (7786), Banshee mistress (7793), Banshee mistress (7794), Banshee (7810), Banshee Champion (8986) + +Base: 2100 +Linked animations: [299, 300, 301, 302, 1417] +Linked NPCs: Waterfiend (5361), Waterfiend (9054), Waterfiend (9056), Waterfiend (9057), Waterfiend (9058), Waterfiend (9059), Waterfiend (9061), Waterfiend (9062), Waterfiend (9063), Waterfiend (9064), Waterfiend (9077) + +Base: 2101 +Linked animations: [9513] + +Base: 2102 +Linked animations: [9574] + +Base: 2103 +Linked animations: [9564] +Linked objects: Pipes (31538) + +Base: 2104 +Linked animations: [9563] +Linked objects: Perch rock (31559), Perch rock (31560), Perch rock (31561), null (31691) + +Base: 2105 +Linked animations: [9538, 9539, 9540, 9541] +Linked NPCs: Karamthulhu (7826), Karamthulhu (7827), null (7858) + +Base: 2106 +Linked animations: [9546] +Linked NPCs: Young Ralph (7832), Young Ralph (7833), Young Ralph (7834), Young Ralph (7835), Young Ralph (7836), null (7859) + +Base: 2107 +Linked animations: [9543] + +Base: 2108 +Linked animations: [9582] + +Base: 2109 +Linked animations: [9569] +Linked graphics: 1668 + +Base: 2110 +Linked animations: [9559] +Linked graphics: 1666 + +Base: 2111 +Linked animations: [9545] +Linked graphics: 1672 + +Base: 2112 +Linked animations: [9578] +Linked graphics: 1670 + +Base: 2113 +Linked animations: [9580] +Linked graphics: 1671 + +Base: 2114 +Linked animations: [9550] +Linked graphics: 1674 + +Base: 2115 +Linked animations: [9548] +Linked graphics: 1673 + +Base: 2116 +Linked animations: [9567] +Linked graphics: 1667 + +Base: 2117 +Linked animations: [9556] +Linked graphics: 1665 + +Base: 2118 +Linked animations: [9660] +Linked objects: Carved fountain (35469), Fountain (47747) + +Base: 2119 +Linked animations: [9659] +Linked objects: Gem Stall (6162), Gem stall (34385), null (35436), Gem stall (35437) + +Base: 2120 +Linked animations: [9661] +Linked objects: null (35440), null (35443), null (35445), null (42334), null (43439), null (43441), null (43444) + +Base: 2121 +Linked animations: [9871, 9982] +Linked objects: Organ (36979) + +Base: 2122 +Linked animations: [9881, 9909] + +Base: 2123 +Linked animations: [9867] +Linked objects: Standard (851), Standard (11614), Standard (32295), Standard (36924), Standard (38808), Standard (41372), Standard (45324) + +Base: 2124 +Linked animations: [9910, 9979, 9980, 12905] +Linked objects: Flag (37335), Flag (37504) + +Base: 2125 +Linked animations: [9644] +Linked objects: null (33740), null (33741), null (33742), null (33743), null (34725), null (34726), null (34727), null (34728), null (39960), null (39961), null (39962), null (39963) + +Base: 2126 +Linked animations: [9643] +Linked objects: null (33669), null (34656), null (39885) + +Base: 2127 +Linked animations: [4856] +Linked objects: Bell (36977) + +Base: 2128 + +Base: 2129 +Linked animations: [9642] +Linked objects: null (8720), null (33758), null (33759), null (33760), null (34351), null (34352), null (36548), null (36549), null (36550), null (36551), Standard (36925), Standard (36926), Standard (41373), Standard (41374), Flag (45182), Flag (45183), Flag (45184), Flag (45185), null (45454) + +Base: 2130 +Linked animations: [9645, 9646, 10991, 12214, 12215] +Linked NPCs: Fishing spot (233), Fishing spot (234), Fishing spot (235), Fishing spot (236), Fishing spot (309), Fishing spot (310), Fishing spot (311), Fishing spot (312), Fishing spot (313), Fishing spot (314), Fishing spot (315), Fishing spot (316), Fishing spot (317), Fishing spot (318), Fishing spot (319), Fishing spot (320), Fishing spot (321), Fishing spot (322), Fishing spot (323), Fishing spot (324), Fishing spot (325), Fishing spot (326), Fishing spot (327), Fishing spot (328), Fishing spot (329), Fishing spot (330), Fishing spot (331), Fishing spot (332), Fishing spot (333), Fishing spot (334), Fishing spot (800), Fishing spot (927), Fishing spot (952), Fishing spot (1174), Fishing spot (1175), Fishing spot (1176), Fishing spot (1177), Fishing spot (1178), Fishing spot (1189), Fishing spot (1190), Fishing spot (1191), Fishing spot (1236), Fishing spot (1237), Fishing spot (1238), Fishing spot (1331), Fishing spot (1332), Fishing spot (1333), Fishing spot (1399), Fishing spot (1405), Fishing spot (1406), Fishing spot (2722), Fishing spot (2724), Fishing spot (2859), Fishing spot (3019), Fishing spot (3574), Fishing spot (3575), Fishing spot (3804), Fishing spot (3848), Fishing spot (4908), Fishing spot (5470), Fishing spot (5471), Fishing spot (6267), Fishing spot (6996), Fishing spot (7044), Fishing spot (7045), Fishing spot (7046), Fishing spot (7862), Fishing spot (7863), Fishing spot (7864), Fishing spot (8647), Cavefish shoal (8841), Rocktail shoal (8842) +Linked objects: Water (43), Fishing spot (2026), Panning point (2363), Fishing spot (3032), Fishing spot (8986), Ominous Fishing Spot (10087), Ominous Fishing Spot (10088), Ominous Fishing Spot (10089), Aquarium (10091), Fishing spot (14938), null (14939), Fishing spot (49561), Fishing spot (49562), Fishing spot (49563), Heim crabs (49923), Red-eye (49924), Dusk eels (49925), Giant flatfish (49926), Short-finned eels (49927), Web snippers (49928), Bouldabass (49929), Salve eels (49930), Blue crabs (49931), Cave morays (49932), Fishing spot (54298) + +Base: 2131 + +Base: 2132 + +Base: 2133 +Linked animations: [9663] +Linked objects: null (37211) + +Base: 2134 + +Base: 2135 +Linked animations: [687, 9665] +Linked objects: null (526), Waterfall (2228), null (16302), null (21579), Waterfall (34915), null (34916), null (37212), null (37213), null (37214), null (37215), null (37216), null (37217), null (37218), null (37219), null (37220), null (37221), null (37222), null (37223), null (37224), null (37225), null (37226), null (37227), null (37228), null (37229), null (37230), null (37231), null (37232), null (37233), null (37234), null (37235), null (37236), null (37237), null (37238), null (37239), null (37240), null (37241), null (37242), null (37243), null (37244), Door (37247), null (37456) + +Base: 2136 + +Base: 2137 +Linked animations: [452] + +Base: 2138 +Linked animations: [9590] +Linked objects: Obelisk of Fire (2153) + +Base: 2139 +Linked animations: [1521, 1525, 1713, 7537, 9593, 11854, 11855, 11856, 11857, 12157, 12158, 12522] +Linked NPCs: Astrid's pyre ship (9311), Brand's pyre ship (9312) +Linked graphics: 2135, 2136, 2189 +Linked objects: Fire Wall (2908), Fire Wall (2909), null (13880), Fire (13881), null (14887), null (14888), null (14889), null (14890), null (15253), null (15254), null (15255), null (15491), null (33394), null (33395), null (43778), null (43779), null (43795), null (47079), null (47081) + +Base: 2140 +Linked animations: [9616] +Linked graphics: 1685 + +Base: 2141 +Linked animations: [9618] +Linked graphics: 1687 + +Base: 2142 +Linked animations: [9619] +Linked graphics: 1688 + +Base: 2143 +Linked animations: [9612] +Linked graphics: 1681 + +Base: 2144 +Linked animations: [9614] +Linked graphics: 1683 + +Base: 2145 +Linked animations: [9617] +Linked graphics: 1686 + +Base: 2146 +Linked animations: [9613] +Linked graphics: 1682 + +Base: 2147 +Linked animations: [9621] +Linked graphics: 1690 + +Base: 2148 +Linked animations: [9620] +Linked graphics: 1689 + +Base: 2149 +Linked animations: [9615] +Linked graphics: 1684 + +Base: 2150 +Linked animations: [665, 666, 667, 9624, 9626, 9628, 9630, 9632, 9634, 9636] +Linked graphics: 111, 112, 113, 763, 764, 1675, 1676, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 1698, 1699, 1727, 1728, 1729, 1730 + +Base: 2151 + +Base: 2152 +Linked animations: [9594] +Linked graphics: 1677, 1955 + +Base: 2153 +Linked animations: [9638] +Linked graphics: 1700 + +Base: 2154 +Linked animations: [9641] +Linked objects: Bridge (15856), Bridge (15857), Bridge (15882), Bridge (15883), Bridge (15944) + +Base: 2155 +Linked animations: [9662] +Linked objects: null (36068), null (36071), null (36072), null (36073), null (36074), null (36075), null (36076), null (36077), null (36078), null (36081), null (36082), null (36083), null (36086), null (36087), null (36250) + +Base: 2156 +Linked animations: [9655] +Linked objects: Catapult (4904), Catapult (4905) + +Base: 2157 +Linked animations: [9657] +Linked objects: Rocks (4437) + +Base: 2158 +Linked animations: [9654, 9658] +Linked NPCs: Barricade (1532), Barricade (1533), Barricade (1534), Barricade (1535), Barricade (8600), Barricade (8601), Barricade (8618) + +Base: 2159 +Linked animations: [9651, 9652, 11219] +Linked objects: Energy barrier (4469), Energy barrier (4470), Door (20199), Door (20200), Door (20201), Door (20202), Door (20203), Door (20204), Door (20205), Door (20206), Door (20207), Door (20208), Door (20209), Blue barrier (42013), Blue barrier (42014), Blue barrier (42015), Red barrier (42016), Red barrier (42017), Red barrier (42018), Neutral barrier (42019), Neutral barrier (42020), Blue barrier (42029), Red barrier (42030), Blue barrier (42276), Red barrier (42277), Energy barrier (47236), Auto-matching barrier (52221), Challenge barrier (52245) + +Base: 2160 +Linked animations: [9653] +Linked objects: Portal (2273), Portal (2274), Portal (2468), Portal (2475), Portal (2503), Portal (2504), Portal (2505), Portal (2506), Portal (2507), Portal (3826), Saradomin portal (4387), Zamorak portal (4388), Exit portal (4389), Exit portal (4390), Exit portal (4406), Exit portal (4407), Guthix portal (4408), Portal (5138), Exit portal (11368), Exit portal (11369), Exit portal (11371), Exit portal (11373), Balance portal (42031), Exit portal (45803) + +Base: 2161 +Linked animations: [9656] +Linked objects: Climbing rope (36312) + +Base: 2162 +Linked animations: [7262] +Linked objects: Chair (36726) + +Base: 2163 +Linked animations: [1896, 6440, 6441, 9666, 9667, 9668, 9669, 9670, 9671, 9672, 9673, 9674, 9675] +Linked NPCs: Camel (80), Ugthanki (840), Ali the Camel (1873), Al the Camel (2809), Elly the Camel (2810), Ollie the Camel (2811), Cam the Camel (2812), null (2813), Alice the Camel (2814), Neferti the Camel (2815), Camel display (5977), Louis the Camel (9368), Louis the Camel (9406) +Linked objects: Cart Camel (36748) + +Base: 2164 + +Base: 2165 +Linked animations: [9740, 9741, 9742, 9743, 9744, 9745, 9746, 9747, 9748, 9749, 9750, 9751, 9752, 9753, 9757, 9758, 9759, 9760, 9761, 9762, 9763, 9764, 9765, 9766, 9767, 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, 9877, 9878, 10083, 10479, 11009, 12411, 12554, 12648, 12649, 12650, 13656, 14004, 14005, 14006, 14007, 14079] + +Base: 2166 +Linked animations: [9969] +Linked graphics: 1719, 1720, 1721, 1722, 1723 + +Base: 2168 +Linked animations: [9887, 9888, 9889, 9890, 9891, 9892, 9893, 9894, 9895, 9896, 9897, 9898, 9899, 9900, 9901, 9902, 9903, 9904, 9905, 9906] + +Base: 2169 +Linked animations: [9720] +Linked graphics: 1702 + +Base: 2170 +Linked animations: [4277] +Linked graphics: 712 + +Base: 2171 + +Base: 2172 + +Base: 2173 + +Base: 2174 + +Base: 2175 + +Base: 2176 +Linked animations: [497, 11943] + +Base: 2177 +Linked animations: [9975] +Linked graphics: 1726 + +Base: 2178 +Linked animations: [5314] +Linked graphics: 967 + +Base: 2179 +Linked animations: [10024, 10488] + +Base: 2180 +Linked animations: [114, 115, 116, 117, 118, 137, 138, 139, 140, 141, 321, 322, 323, 324, 325, 326, 327] +Linked graphics: 775, 800, 801, 802, 803, 804, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713 + +Base: 2181 +Linked animations: [4854] +Linked graphics: 148, 1716 + +Base: 2182 +Linked animations: [9967] +Linked graphics: 1717 + +Base: 2183 +Linked animations: [9970] +Linked objects: Water barrel (37585), Puddle (37598), Puddle (37599), Puddle (37600), Puddle (37601), Puddle (37602), null (37603), Puddle (37964), Puddle (37965), Puddle (37966), null (47039), null (47040) + +Base: 2185 +Linked animations: [9971, 9974, 9981] +Linked objects: Mine entrance (37632), null (37635) + +Base: 2186 +Linked animations: [9972, 9973] +Linked objects: Rock column (37657) + +Base: 2187 + +Base: 2188 +Linked animations: [9991] +Linked graphics: 1734 + +Base: 2189 +Linked animations: [9985, 9987] +Linked graphics: 1731, 1732 + +Base: 2190 +Linked animations: [9989] +Linked graphics: 1733 + +Base: 2191 +Linked animations: [10051, 10052] + +Base: 2192 +Linked animations: [10053, 10055, 10056, 10057, 10058, 10059, 10064, 10065, 10066, 10378, 10379, 10384, 10385, 10386, 10388, 10389, 10400, 10405, 10409, 10410, 10496] +Linked NPCs: Spirit Beast (7995), Spirit Beast (7996), Spirit Beast (7997), Spirit Beast (8128), Spirit Beast (8129), Spirit Beast (8130), Spirit Beast (8131), Spirit Beast (8132), Corporeal Beast (8133) + +Base: 2193 + +Base: 2194 +Linked animations: [7385, 10404] +Linked graphics: 1822 +Linked objects: Fire pit (38827), Fire pit (38828) + +Base: 2195 +Linked animations: [10039, 10040] +Linked objects: null (37840), null (37841) + +Base: 2196 +Linked animations: [10025, 10026, 10027, 10028, 10029, 10030, 10031, 10043] +Linked NPCs: Spirit (7976), Spirit (7977), Spirit (7978), Spirit (7979), Spirit (7980) + +Base: 2197 +Linked animations: [10042] +Linked objects: Portal (37924), Portal (37925), null (37926), null (37927), null (38011), Portal (38843) + +Base: 2198 +Linked animations: [10034] +Linked objects: Hollow log (37831) + +Base: 2199 +Linked animations: [10068] +Linked graphics: 1742 + +Base: 2200 +Linked animations: [7393, 10375] +Linked graphics: 1273, 1804, 1809 + +Base: 2201 +Linked animations: [10061] +Linked graphics: 1741 + +Base: 2202 +Linked animations: [10380] +Linked graphics: 1805, 1806, 1807, 1808 + +Base: 2203 +Linked animations: [10060] +Linked graphics: 1740 + +Base: 2204 +Linked animations: [10062, 10063] +Linked graphics: 1743, 1744 + +Base: 2205 +Linked animations: [10048] +Linked graphics: 1736 + +Base: 2206 +Linked animations: [10376] +Linked graphics: 1272 + +Base: 2207 +Linked animations: [10035] +Linked graphics: 1735 + +Base: 2208 +Linked animations: [10396, 10397, 10398, 10399, 10401, 10402, 10403, 10406, 10407, 10408, 13401, 13402, 13403, 13404] +Linked graphics: 1813, 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1823, 1824, 1825, 2512, 2513, 2514, 2515 + +Base: 2209 +Linked animations: [10054] +Linked graphics: 1737 + +Base: 2210 +Linked animations: [7391, 10395] +Linked objects: Entrance (38814) + +Base: 2211 +Linked animations: [10047] + +Base: 2212 +Linked animations: [10046] +Linked objects: Altar (37902), Altar (37903), Altar (37904), Altar (37905), Altar (37906), Altar (37907), Altar (37908), Altar (37909), Altar (37910), Altar (37911), Altar (37912), null (37913) + +Base: 2213 +Linked animations: [10381, 10382, 10383] +Linked graphics: 1810, 1811, 1812 + +Base: 2214 +Linked animations: [10095, 10096] + +Base: 2215 +Linked animations: [10086, 10087, 10097] +Linked graphics: 1745, 1746, 1747 + +Base: 2216 +Linked animations: [10091] +Linked graphics: 1748 + +Base: 2217 +Linked animations: [10098, 10099] +Linked objects: Portal (38150) + +Base: 2218 +Linked animations: [10090] +Linked NPCs: Roger (8018) + +Base: 2219 +Linked animations: [10088, 10089] +Linked objects: Basket (38121), Basket (38122) + +Base: 2220 +Linked animations: [10085] +Linked objects: Barrier (38144), Barrier (38145), Barrier. (38146), Barrier. (38147), Barrier. (38148), Barrier. (38149) + +Base: 2221 +Linked animations: [10092, 10093, 10094] + +Base: 2222 +Linked animations: [10118, 10750, 10769, 10874, 10875, 13976] +Linked graphics: 1954, 1958, 2631 +Linked objects: Mysterious stone (41160), Mysterious stone (41161), Stone of Jas (41162), Stone of Jas (41163), Stone of Jas (41164) + +Base: 2223 +Linked animations: [10125, 10126, 10204, 10205, 10206, 10207, 10208, 10209] +Linked NPCs: Wizard Korvak (8029), Wizard Vief (8030), Wizard Elriss (8032) + +Base: 2224 +Linked animations: [10135, 10136, 10137, 10138, 10192, 10193, 10194, 10195, 10196, 10197, 10198, 10199] +Linked NPCs: null (8021), Yellow orb (8022), Yellow orb (8023), Yellow orb (8024), null (8025), Green orb (8026), Green orb (8027), Green orb (8028) +Linked objects: Switch (20222), Switch (20223), Tube (38325), Tube (38326), Containment unit (38327), Storage unit (38328), Runestone accelerator (38329), Tube (48170), Tube (48171) + +Base: 2225 +Linked animations: [10188, 10189] +Linked NPCs: Wizard Acantha (8031) + +Base: 2226 +Linked animations: [10139, 10140, 10141, 10142, 10143, 10144, 10145, 10146, 10147, 10148, 10149, 10150, 10151, 10152, 10153, 10154, 10155, 10156, 10157, 10158, 10159, 10160, 10161, 10162, 10163, 10164, 10165, 10166, 10167, 10168, 10169, 10170, 10171, 10216, 10217, 10218, 10219, 10220] + +Base: 2227 +Linked animations: [10191] + +Base: 2228 +Linked animations: [10214] +Linked graphics: 1774 + +Base: 2229 +Linked animations: [10179, 10181, 12782] +Linked graphics: 1771, 1772, 2309 + +Base: 2230 +Linked animations: [10190] +Linked objects: Portal (3078), Portal (20224), Portal (20225), Portal (20244), Portal (20248), Portal (38279) + +Base: 2231 +Linked animations: [10183, 10185, 10187] +Linked graphics: 1749, 1750, 1751, 1752 +Linked objects: Barrier (38377), Barrier (38378) + +Base: 2232 +Linked animations: [10130, 10131, 10133, 10174, 10175] +Linked graphics: 1753, 1754, 1755, 1756, 1757, 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770 + +Base: 2233 +Linked animations: [10203] +Linked graphics: 1773 + +Base: 2234 +Linked animations: [10134, 10176, 10177, 10178] +Linked NPCs: Portal (8019), Portal (8020) + +Base: 2235 +Linked animations: [10215] +Linked objects: null (38336), null (38337), null (38338) + +Base: 2236 +Linked animations: [10123, 10124] +Linked objects: null (38396), null (38397), null (38398), null (38399), null (38400), null (38401), null (38403), null (38404), null (38405), null (38406), null (38407), null (38408) + +Base: 2237 +Linked animations: [10128, 10129] +Linked objects: Glass spheres (38331) + +Base: 2238 +Linked animations: [10201] +Linked objects: Bookcase (539), null (38333), Workbench (42211) + +Base: 2239 +Linked animations: [10127, 10200] +Linked objects: Gyroscope (38330), Gyroscope (48172) + +Base: 2240 +Linked animations: [10221] + +Base: 2241 +Linked animations: [10229] + +Base: 2242 +Linked animations: [10244, 10245] +Linked graphics: 1779, 1780 + +Base: 2243 +Linked animations: [10238, 10239] +Linked graphics: 1775, 1776 + +Base: 2244 +Linked animations: [10356, 10357, 10358, 10359, 10360, 10361, 10362, 10363, 10364, 10365, 10366, 10367, 10563, 11002, 11003, 11004, 11005, 11006] +Linked NPCs: Snowman (4662), Pumpkin (8077), Barrel (8104), Bush (8105), Bush (8106), Cactus (8107), Crate (8108), Rock (8109), Toadstool (8110), Cactus (8502), Cactus (8503), Bush (8504), Toadstool (8505), Barrel (8506), Bush (8507), Crate (8508), Bush (8509), Rock (8510), Bush (8511) + +Base: 2245 +Linked animations: [10322, 10323, 10324, 10325, 10326, 10327, 10328, 10329, 10330, 10331] +Linked objects: Rat (level 10 approx.) (38649), Cat (level 20 approx.) (38650), Penguin (level 40 approx.) (38652), Ork (level 50 approx.) (38653) + +Base: 2246 +Linked animations: [10332, 10333, 10334] +Linked objects: Armour (level 60 approx.) (38654), Stool (level 70 approx.) (38655), Dumb-bells (level 80 approx.) (38656), Assistant (level 90 approx.) (38657), Anvil (level 99 approx.) (38658) + +Base: 2247 +Linked animations: [10259] +Linked objects: null (38505), Target (38506), Target (38507), Target (38508), Target (38509), Target (38510), Target (38511), Target (38512), Target (38513), Target (38514), Target (38515), Target (38516), Target (38517), Target (38518), Target (38519), Target (38520), Target (38521), Target (38522) + +Base: 2248 +Linked animations: [10319, 10320, 10321] +Linked NPCs: Ticket vendor (8088) + +Base: 2249 +Linked animations: [10315, 10316] + +Base: 2250 +Linked animations: [10314, 10317, 10318] +Linked NPCs: Ringmaster (8085) + +Base: 2251 +Linked animations: [10272] +Linked graphics: 1803 + +Base: 2252 +Linked animations: [10310, 10311] +Linked graphics: 1799, 1800 + +Base: 2253 +Linked animations: [10302, 10304, 10306, 10309] +Linked graphics: 1781, 1782, 1783, 1784, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798 + +Base: 2254 +Linked animations: [10307, 10308] +Linked graphics: 1785, 1786 + +Base: 2255 +Linked animations: [10282, 10283, 10284] +Linked objects: The audience (38576), The audience (38577), The audience (38578), The audience (38579), The audience (38580), The audience (38581) + +Base: 2256 +Linked animations: [10285, 10286, 10287] +Linked objects: The audience (38582), The audience (38583), The audience (38584), The audience (38585), The audience (38586), The audience (38587) + +Base: 2257 +Linked animations: [10276, 10277, 10278] +Linked objects: The audience (38558), The audience (38559), The audience (38560), The audience (38561), The audience (38562), The audience (38563) + +Base: 2258 +Linked animations: [10273, 10274, 10275] +Linked objects: The audience (38570), The audience (38571), The audience (38572), The audience (38573), The audience (38574), The audience (38575) + +Base: 2259 +Linked animations: [10279, 10280, 10281] +Linked objects: The audience (38564), The audience (38565), The audience (38566), The audience (38567), The audience (38568), The audience (38569) + +Base: 2260 +Linked animations: [10352, 10354] +Linked NPCs: Star sprite (8091) + +Base: 2261 +Linked animations: [10350] + +Base: 2262 +Linked animations: [10349, 10351, 10353] +Linked objects: null (38659), Crashed star (38661), Crashed star (38662), Crashed star (38663), Crashed star (38664), Crashed star (38665), Crashed star (38666), Crashed star (38667), Crashed star (38668) + +Base: 2263 +Linked animations: [10368, 10369, 10370, 10371] + +Base: 2264 +Linked animations: [7382, 12723, 12735] +Linked objects: Non-combat zone (38692), Force field (39489), Force field (39490), Force field (39491), Force field (39492), Force field (39493), Force field (39494), Barrier door (39639), Barrier door (39640), Barrier door (39641), Barrier door (39642), Barrier door (39643), Barrier door (39644), Barrier door (39645), Barrier door (39646), Barrier door (39647), Barrier door (39648), Barrier door (39649), Barrier door (39650), Barrier door (39651), Barrier door (39652), Barrier door (39653), Barrier door (39654), Barrier door (39655), Barrier door (39656), Barrier door (39657), Barrier door (39658), Barrier door (39659), Barrier door (39660), Barrier door (39661), Barrier door (39662), Barrier door (39663), Barrier door (39664), Barrier door (39665), Barrier door (39666), Barrier door (39667), Barrier door (39668), Barrier door (39689), Barrier door (39690), Barrier door (39691), Barrier door (39692), Barrier door (39693), Barrier door (39694), Barrier door (39695), Barrier door (39696), Barrier door (39697), Barrier door (39698), Barrier door (39699), Barrier door (39700), Barrier door (39701), Barrier door (39702), Barrier door (39703), Barrier door (39704), Barrier door (39705), Barrier door (39706), Barrier door (39707), Barrier door (39708), Barrier door (39709), Barrier door (39710), Barrier door (39711), Barrier door (39712), Barrier door (39713), Barrier door (39714), Barrier door (39715), Barrier door (39716), Barrier door (39717), Barrier door (39718), Barrier door (39719), Barrier door (39720), null (39766), null (39767), null (39768), null (39769), null (39778), null (39779), null (39780), null (39781), null (39790), null (39791), null (39792), null (39793), null (39802), null (39803), null (39804), null (39805), null (39814), null (39815), null (39816), null (39817), null (39826), null (39827), null (39828), null (39829), Arcane doorway (47974), Arcane doorway (47975), Arcane doorway (47976), Arcane doorway (47981), Arcane doorway (47982), Arcane doorway (47983) + +Base: 2265 +Linked animations: [10586] +Linked objects: Entry portal (39515) + +Base: 2266 +Linked animations: [10387, 10390, 10391, 10392, 10393, 10394] +Linked NPCs: Dark energy core (8126), Dark energy core (8127) +Linked graphics: 1826, 1827, 1828 +Linked objects: Hole (37967) + +Base: 2267 +Linked animations: [10411] +Linked graphics: 1829 + +Base: 2268 +Linked animations: [10412] +Linked graphics: 1830 + +Base: 2269 +Linked animations: [10413, 10414] + +Base: 2270 +Linked animations: [10489] + +Base: 2271 +Linked animations: [10481] + +Base: 2272 +Linked animations: [10424] +Linked objects: Red mist (39214) + +Base: 2273 +Linked animations: [10426, 10427] +Linked NPCs: Fish (8176), Fish (8177) + +Base: 2274 +Linked animations: [10432, 10433, 10441, 10451] +Linked NPCs: Zemouregal (8146), null (8147), Zemouregal (8148), null (8183) + +Base: 2275 +Linked animations: [10460, 10461, 10462, 10463] +Linked objects: null (39208), null (39209), null (39210), null (39211) + +Base: 2276 +Linked animations: [10425, 10431] +Linked objects: The Sacred Forge (39207) + +Base: 2277 + +Base: 2278 +Linked animations: [10419] + +Base: 2279 +Linked animations: [10472] +Linked graphics: 1833 + +Base: 2280 +Linked animations: [10421] +Linked graphics: 1831 + +Base: 2281 +Linked animations: [10422, 14505] +Linked graphics: 1832, 2753 + +Base: 2282 +Linked animations: [10444, 10445] + +Base: 2283 +Linked animations: [10437, 10438, 10439, 10440, 10442, 10443] + +Base: 2284 +Linked animations: [10447, 10448] + +Base: 2285 +Linked animations: [10430] + +Base: 2286 +Linked animations: [10486, 10487] + +Base: 2287 +Linked animations: [10529] +Linked graphics: 1857, 1858, 1859, 1860, 1861, 1862 + +Base: 2288 +Linked animations: [10510] +Linked graphics: 1844, 2000 + +Base: 2289 +Linked animations: [10522, 10523] +Linked graphics: 1853, 1854 + +Base: 2290 +Linked animations: [10509] +Linked graphics: 1837 + +Base: 2291 +Linked animations: [10506, 10507, 10508] +Linked graphics: 1841, 1842, 1843 + +Base: 2292 +Linked animations: [10511, 10512, 10514] +Linked graphics: 1845, 1846, 1847 + +Base: 2293 +Linked animations: [10519, 10520, 10521] +Linked graphics: 1850, 1851, 1852 + +Base: 2294 +Linked animations: [10515, 10517] +Linked graphics: 1848, 1849 + +Base: 2295 +Linked animations: [10500] +Linked graphics: 1839 + +Base: 2296 +Linked animations: [1542, 1543, 1873, 2877, 10525, 10526, 10527, 10528, 10965, 11243, 11253, 11923, 12032, 12192, 13173] +Linked graphics: 347, 479, 1835, 1836, 1838, 1840, 1950, 2008, 2100, 2118, 2144, 2272, 2273, 2439 + +Base: 2297 +Linked animations: [10531] +Linked graphics: 1864 + +Base: 2298 +Linked animations: [10536] +Linked graphics: 1865 + +Base: 2299 +Linked animations: [10533] +Linked graphics: 1866 + +Base: 2300 +Linked animations: [10539, 10540] +Linked graphics: 1867, 1868 + +Base: 2301 +Linked animations: [14212] +Linked graphics: 457 + +Base: 2302 +Linked animations: [2789, 10559] + +Base: 2303 +Linked animations: [14213, 14218] +Linked graphics: 458, 459, 460, 461, 462, 463, 464, 1863, 2699, 2700 + +Base: 2304 +Linked animations: [10561] +Linked objects: Hat stand (39245), Light (39246) + +Base: 2305 +Linked animations: [10541] +Linked objects: null (39228) + +Base: 2306 +Linked animations: [10560] +Linked objects: Caravan (39237) + +Base: 2307 +Linked animations: [10557, 10558, 12894, 12895, 12896] +Linked NPCs: Corpse Spider (7914), Bat (8208), Rat (8209), Lizard (8210), Blackbird (8211), Spider (8212), Snail (8213), null (8219), null (8220), null (8221), null (8222), null (8223), null (8224), null (8225) + +Base: 2308 +Linked animations: [10547, 10548, 10549] +Linked objects: null (39238), null (39239), Hitching post (39240), null (40192) + +Base: 2309 +Linked animations: [10585] +Linked graphics: 1869, 1870, 1871 + +Base: 2310 +Linked animations: [10598] +Linked graphics: 1882 + +Base: 2311 +Linked animations: [10624] +Linked objects: Post (39762) + +Base: 2312 +Linked animations: [10582] +Linked objects: Pool (class 5) (39562), Pool (class 4) (39563), Pool (class 3) (39564), Pool (class 2) (39565), Pool (class 5) (39566), Pool (class 4) (39567), Pool (class 3) (39568), Pool (class 2) (39569), Pool (empty) (39570), Pool (empty) (39571), Pool (empty) (39572), Pool (empty) (39573) + +Base: 2313 +Linked animations: [10599, 10600] +Linked objects: null (39598), null (39599), null (39601) + +Base: 2314 +Linked animations: [10601] +Linked objects: null (39600) + +Base: 2315 +Linked animations: [10581] +Linked objects: Swarm (class 5) (39574), Swarm (class 4) (39575), Swarm (class 3) (39576), Swarm (class 2) (39577), Swarm (class 5) (39578), Swarm (class 4) (39579), Swarm (class 3) (39580), Swarm (class 2) (39581), Swarm (empty) (39582), Swarm (empty) (39583), Swarm (empty) (39584), Swarm (empty) (39585) + +Base: 2316 +Linked animations: [10591, 10592, 10593, 10594, 10595, 10596, 10597] +Linked NPCs: Clay familiar (class 1) (8240), Clay familiar (class 1) (8241), Clay familiar (class 2) (8242), Clay familiar (class 2) (8243), Clay familiar (class 3) (8244), Clay familiar (class 3) (8245), Clay familiar (class 4) (8246), Clay familiar (class 4) (8247), Clay familiar (class 5) (8248), Clay familiar (class 5) (8249) + +Base: 2317 +Linked animations: [10746] + +Base: 2318 +Linked animations: [10966] +Linked objects: Vine flower (40889) + +Base: 2319 +Linked animations: [10648, 10649] +Linked objects: Rocks (41041), Rocks (41042) + +Base: 2320 +Linked animations: [5084, 5085, 5087, 10751] +Linked graphics: 873, 874, 875 +Linked objects: Treasure pile (40951) + +Base: 2321 +Linked animations: [10761] +Linked objects: null (40203) + +Base: 2322 +Linked animations: [10702, 10703] +Linked objects: Stairs (40956) + +Base: 2323 +Linked animations: [10765] + +Base: 2324 +Linked animations: [10877, 10878, 10879, 10880] + +Base: 2325 +Linked animations: [10908, 10909] +Linked objects: Floor wire (40914), Floor wire (40915), Floor wire (40916), null (41307), null (41308), null (41309), null (41310), null (41311), null (41312), null (41313), null (41314), null (41315), null (41316), null (41317), null (41318), null (41319), null (41320), null (41321), null (41322), null (41323), null (41324), null (41325), null (41326), null (41327), null (41328), null (41329), null (41330), null (41331), null (41332), null (41333), null (41334) + +Base: 2326 +Linked animations: [10893] + +Base: 2327 +Linked animations: [10890, 10891, 10892] +Linked objects: Dolmen (40871), Dolmen (40872), Dolmen (40873), Dolmen (40874), Dolmen (40875), Dolmen (40876), Dolmen (40877), Dolmen (40878), Dolmen (40879), Dolmen (40880), Dolmen (40881), Dolmen (40882), Dolmen (40883), Dolmen (40884), Dolmen (40885), Dolmen (40886) + +Base: 2328 +Linked animations: [10862, 10863, 10864] +Linked objects: null (41346), null (41347), null (41348) + +Base: 2329 + +Base: 2330 +Linked animations: [10826, 10827] + +Base: 2331 +Linked animations: [10821] + +Base: 2332 +Linked animations: [10754] + +Base: 2333 +Linked animations: [10894] + +Base: 2334 +Linked animations: [10708, 10712] + +Base: 2335 +Linked animations: [10785, 10786] +Linked objects: Statue (40828) + +Base: 2336 +Linked animations: [10789, 10790] +Linked objects: Statue (40829) + +Base: 2337 +Linked animations: [10787, 10788] +Linked objects: Statue (40834) + +Base: 2338 +Linked animations: [10804, 10805] +Linked objects: Statue (40839) + +Base: 2339 +Linked animations: [10808, 10809] +Linked objects: Statue (40835) + +Base: 2340 +Linked animations: [10806, 10807] +Linked objects: Statue (40836) + +Base: 2341 +Linked animations: [10779, 10780] +Linked objects: Statue (40833) + +Base: 2342 +Linked animations: [10783, 10784] +Linked objects: Statue (40831) + +Base: 2343 +Linked animations: [10781, 10782] +Linked objects: Statue (40827) + +Base: 2344 +Linked animations: [10791, 10792] +Linked objects: Statue (40838) + +Base: 2345 +Linked animations: [10810, 10811] +Linked objects: Statue (40830) + +Base: 2346 +Linked animations: [10802, 10803] +Linked objects: Statue (40837) + +Base: 2347 +Linked animations: [10777, 10778] +Linked objects: Statue (40832) + +Base: 2348 +Linked animations: [10760] +Linked objects: Dragon head (40204), Dragon head (40699) + +Base: 2349 + +Base: 2350 +Linked animations: [10766, 10767, 10907] +Linked NPCs: Dragon head (8426), Dragon head (8427) +Linked objects: Brazier (41035), Brazier (41036) + +Base: 2351 +Linked animations: [10967] +Linked objects: null (41335) + +Base: 2352 + +Base: 2353 +Linked animations: [10682, 10683, 10684] +Linked graphics: 1936, 1937, 1938 + +Base: 2354 +Linked animations: [10837] +Linked graphics: 1913, 1914 + +Base: 2355 +Linked animations: [10774, 11370] +Linked graphics: 1899, 2001 + +Base: 2356 +Linked animations: [10629] +Linked graphics: 1906, 2632, 2633 + +Base: 2357 +Linked animations: [10925, 10929] +Linked graphics: 1887, 1888 + +Base: 2358 +Linked animations: [10723] +Linked graphics: 1949 + +Base: 2359 +Linked animations: [10840] +Linked graphics: 1915 + +Base: 2360 +Linked animations: [10984, 10985] +Linked graphics: 1952, 1953 + +Base: 2361 +Linked animations: [10838, 10839] + +Base: 2362 +Linked animations: [10748] +Linked graphics: 1892 + +Base: 2363 +Linked animations: [10834, 10835] +Linked NPCs: Hazelmere's hat (8496) +Linked graphics: 1928 + +Base: 2364 +Linked animations: [10685, 10686, 10687] +Linked graphics: 1939, 1940, 1941 + +Base: 2365 +Linked animations: [10928] +Linked graphics: 1886 + +Base: 2366 +Linked animations: [10643, 10645] +Linked graphics: 1909, 1910 + +Base: 2367 +Linked animations: [10630, 10631] +Linked graphics: 1907, 1908 + +Base: 2368 +Linked animations: [10860] +Linked graphics: 1919 + +Base: 2369 + +Base: 2370 +Linked animations: [10628] +Linked graphics: 1905 + +Base: 2371 +Linked animations: [10884] +Linked graphics: 1929 + +Base: 2372 +Linked animations: [10927] +Linked graphics: 1885 + +Base: 2373 +Linked animations: [10906] +Linked graphics: 1934 + +Base: 2374 + +Base: 2375 +Linked animations: [10831, 10832] +Linked graphics: 1920, 1921, 1922, 1923, 1924, 1925, 1926 + +Base: 2376 +Linked animations: [10714] +Linked graphics: 1890 + +Base: 2377 +Linked animations: [10888, 10889] +Linked graphics: 1930, 1931 + +Base: 2378 +Linked animations: [10736, 11932] +Linked graphics: 1891, 2102 + +Base: 2379 +Linked animations: [10904] +Linked graphics: 1889 + +Base: 2380 +Linked animations: [10861] +Linked graphics: 1916 + +Base: 2381 +Linked animations: [10652, 10655] +Linked graphics: 1911, 1912 + +Base: 2382 +Linked animations: [10691, 10694, 10695, 10696, 10699, 10700] +Linked graphics: 1942, 1943, 1944, 1945, 1946, 1947 + +Base: 2383 +Linked animations: [10859] +Linked graphics: 1917 + +Base: 2384 + +Base: 2385 +Linked animations: [10833] +Linked graphics: 1927 + +Base: 2386 +Linked animations: [10773, 10775, 10902] +Linked graphics: 1898, 1900 + +Base: 2387 +Linked animations: [10757] +Linked graphics: 1903 +Linked objects: Gas cloud (41166) + +Base: 2388 +Linked animations: [10926] +Linked graphics: 1884 + +Base: 2389 +Linked animations: [10752] +Linked graphics: 1894, 1895 + +Base: 2390 +Linked animations: [10820] +Linked graphics: 1902 + +Base: 2391 +Linked animations: [10701] +Linked graphics: 1948 + +Base: 2392 + +Base: 2393 +Linked animations: [10776] +Linked graphics: 1901 + +Base: 2394 +Linked animations: [10771, 10897] +Linked graphics: 1896, 1897 + +Base: 2395 +Linked animations: [10912] +Linked graphics: 1935 + +Base: 2396 +Linked animations: [10759] +Linked graphics: 1893 + +Base: 2397 +Linked animations: [10664, 10665, 10666, 10667, 10668, 10669, 10670, 10671, 10672, 10673, 10674, 10675, 10676, 10677, 10678, 10679, 10680, 10681, 10688, 10689, 10690, 10692, 10693, 10697, 10698, 10882] +Linked NPCs: Balance Elemental (8281), Balance Elemental (8282), Balance Elemental (8283), Balance Elemental (8284), Balance Elemental (8546) + +Base: 2398 +Linked animations: [10632, 10633, 10634, 10635, 10636, 10637, 10638, 10639, 10640, 10641, 10656, 10657, 10658, 10659, 10660, 10661] +Linked NPCs: Undead hero (8286), Undead hero (8287), Undead hero (8288), Undead hero (8289), Undead hero (8290), Undead hero (8291), Undead hero (8292), Undead hero (8293), Undead hero (8294), Undead hero (8295), Undead hero (8296), Undead hero (8297) + +Base: 2399 +Linked animations: [10713, 10762, 10763, 10764, 10770, 10772, 10828, 10829, 10896, 10900, 10901, 10903, 10910, 10911, 10983, 10986, 10989] +Linked NPCs: Lucien (8443), Lucien (8497) + +Base: 2400 +Linked animations: [10812, 10813, 10814, 10815, 10816, 10817, 10818, 10819, 13624, 13628, 13631, 13635, 13636, 13785, 13786, 13787, 13788, 13789, 13790, 13791, 13792, 13793, 13794, 13795, 13796, 13832, 13833, 13836] +Linked NPCs: Undead troll (8377), Undead troll (8378), Undead troll (8379), Undead troll (8380), Undead troll (8381), Undead troll (8382), Undead troll (8383), Undead troll (8384), Undead troll (8385), Undead troll (8386), Undead troll (8387), Undead troll (8388), Undead troll (8389), Undead troll (8390), Undead troll (8391), Undead troll (8392), Easta Buni (9689), Icy Bones (10040), Icy Bones (10041), Icy Bones (10042), Icy Bones (10043), Icy Bones (10044), Icy Bones (10045), Icy Bones (10046), Icy Bones (10047), Icy Bones (10048), Icy Bones (10049), Icy Bones (10050), Icy Bones (10051), Icy Bones (10052), Icy Bones (10053), Icy Bones (10054), Icy Bones (10055), Icy Bones (10056), Icy Bones (10057), Ice troll (10782), Ice troll (10783), Ice troll (10784), Ice troll (10785), Ice troll (10786), Ice troll (10787), Ice troll (10788), Ice troll (10789), Ice troll (10790), Thrower troll (10791), Thrower troll (10792), Thrower troll (10793), Thrower troll (10794), Thrower troll (10795), Thrower troll (10796), Kolodion (11301), River troll (11454) + +Base: 2401 +Linked animations: [10642, 10644, 10917, 10918, 10919, 10920, 10921, 10922, 10923, 10924] +Linked NPCs: Tormented demon (8349), Tormented demon (8350), Tormented demon (8351), Tormented demon (8352), Tormented demon (8353), Tormented demon (8354), Tormented demon (8355), Tormented demon (8356), Tormented demon (8357), Tormented demon (8358), Tormented demon (8359), Tormented demon (8360), Tormented demon (8361), Tormented demon (8362), Tormented demon (8363), Tormented demon (8364), Tormented demon (8365), Tormented demon (8366) + +Base: 2402 +Linked animations: [10931, 10932] + +Base: 2403 +Linked animations: [10933, 10934, 10935] + +Base: 2404 +Linked animations: [10930, 10936] +Linked objects: null (10812) + +Base: 2405 +Linked animations: [10979, 11668, 11669, 11670] +Linked NPCs: Lazy Khazard Guard (7524), Lazy Khazard Guard (7525), Lazy Khazard Guard (7526), Lazy Khazard Guard (7527) +Linked objects: Lazy Khazard Guard (41494), Lazy Khazard Guard (41495), Lazy Khazard Guard (41496), Lazy Khazard Guard (41497) + +Base: 2406 +Linked animations: [10996, 10997, 10998, 10999, 11000, 11001] +Linked NPCs: Turkey (8501), Turkey (9039) + +Base: 2407 +Linked animations: [11017, 11018] +Linked graphics: 1965, 1966 + +Base: 2408 +Linked animations: [11014, 11015, 11016] +Linked graphics: 1959, 1960, 1961, 1962, 1963, 1964 + +Base: 2409 +Linked animations: [11050] +Linked graphics: 1970 + +Base: 2410 +Linked animations: [11049, 11058] +Linked graphics: 1969, 1972 + +Base: 2411 +Linked animations: [11045, 13634] +Linked graphics: 1973, 2597 + +Base: 2412 +Linked animations: [11034] +Linked graphics: 1967 + +Base: 2413 +Linked animations: [11046, 11047, 12686] +Linked NPCs: Santa Claus (8540), Santa Claus (9400) +Linked objects: Santa (47857) + +Base: 2414 +Linked animations: [11054, 11055, 11056] +Linked objects: Reindeer (41725), Reindeer (41726), Reindeer (41727), Reindeer (41728), Reindeer (41729), Reindeer (41730), Reindeer (41731), Reindeer (41732), Sleigh (41735), Sleigh (41736), Sleigh (41737), Sleigh (41738), Sleigh (41739), Sleigh (41740), Sleigh (41741), Sleigh (41742), Sleigh (41743), Sleigh (41744) + +Base: 2415 +Linked animations: [11035] +Linked graphics: 1968 + +Base: 2416 +Linked animations: [8946, 8947, 8948, 8949, 8951, 8952, 8965, 8966, 8967, 8968, 11027, 11036, 11037, 11038, 11039, 11040] +Linked NPCs: Nial Swiftfling (5188), Yeti (7379), Yeti (7380), Prince Vargas (7388), Nial Swiftfling (7391), null (7403), Yeti (8515), Yeti (8516) +Linked graphics: 1579 +Linked objects: Yeti (41685) + +Base: 2417 +Linked animations: [11032] + +Base: 2418 +Linked animations: [11105, 11106, 11109] +Linked graphics: 1982, 1983, 1984 + +Base: 2419 +Linked animations: [11080] +Linked graphics: 1978 + +Base: 2420 +Linked animations: [11128] +Linked graphics: 1988 + +Base: 2421 +Linked animations: [11127] +Linked graphics: 1986 + +Base: 2422 +Linked animations: [11086] +Linked graphics: 1974 + +Base: 2423 +Linked animations: [11101, 11102, 11103, 11222] +Linked graphics: 1979, 1980, 1981, 1998 + +Base: 2424 +Linked animations: [11134, 11135] +Linked graphics: 1990, 1991 + +Base: 2425 +Linked animations: [11137] +Linked graphics: 1992 + +Base: 2426 +Linked animations: [11126] +Linked graphics: 1987, 1989 + +Base: 2427 +Linked animations: [11110] +Linked graphics: 1985 + +Base: 2428 +Linked animations: [11098, 11099, 11100] +Linked graphics: 1975, 1976, 1977 + +Base: 2429 +Linked animations: [11090, 11129] +Linked objects: Pyre (41909), Pyre (41910) + +Base: 2430 +Linked animations: [11072, 11073, 11074, 11075, 11076, 11077, 11078, 11079, 11081, 11092, 11093, 11094, 11095, 11096, 11097, 11104, 11107, 11108] +Linked NPCs: Wounded phoenix (8547), Phoenix (8548), Phoenix (8549), Phoenix (8575), Phoenix (8576), Guardian (8906), Guardian (9724) + +Base: 2431 +Linked animations: [11085] +Linked NPCs: Large egg (8552) + +Base: 2432 +Linked animations: [11111, 11112, 11113, 11114, 11115, 11116, 11117, 11118, 11119, 11120, 11121, 11122, 11123, 11124, 11125, 11130, 11131, 11132, 11133, 11136] +Linked NPCs: Lesser reborn warrior (8557), Lesser reborn warrior (8558), Greater reborn warrior (8559), Greater reborn warrior (8560), Lesser reborn ranger (8561), Lesser reborn ranger (8562), Greater reborn ranger (8563), Greater reborn ranger (8564), Lesser reborn mage (8565), Lesser reborn mage (8566), Greater reborn mage (8567), Greater reborn mage (8568), Lesser reborn warrior (8569), Greater reborn warrior (8570), Lesser reborn ranger (8571), Greater reborn ranger (8572), Lesser reborn mage (8573), Greater reborn mage (8574) + +Base: 2433 +Linked animations: [11082, 11083, 11084, 11087, 11138, 11139, 11140] +Linked NPCs: Phoenix eggling (8550), Phoenix eggling (8551), Phoenix eggling (8577), Phoenix eggling (8578), Phoenix eggling (9189), Phoenix eggling (9255) + +Base: 2434 +Linked animations: [11149] +Linked graphics: 788 + +Base: 2435 +Linked animations: [11205] +Linked objects: Soul obelisk (42010), Soul obelisk (42011), Soul obelisk (42012), null (42273), Soul obelisk (42274), Soul obelisk (42275) + +Base: 2436 +Linked animations: [11200, 11201, 11202, 11203, 11204, 12731] +Linked NPCs: Avatar of Creation (8597), Avatar of Creation (8614), Decaying avatar (9437) + +Base: 2437 +Linked animations: [11195, 11196, 11197, 11198, 11199] +Linked NPCs: Avatar of Destruction (8596), Avatar of Destruction (8615) + +Base: 2438 +Linked animations: [11212, 11213, 11214, 11215, 11216, 11217] +Linked NPCs: Baby kurask (8622), Baby kurask (9249) + +Base: 2439 +Linked animations: [11206, 11207, 11208, 11209] +Linked graphics: 1993, 1994, 1995, 1996 + +Base: 2440 +Linked animations: [11223] +Linked graphics: 1999 + +Base: 2441 + +Base: 2442 + +Base: 2443 +Linked animations: [1714] +Linked graphics: 313 + +Base: 2444 +Linked animations: [1712] +Linked graphics: 319 + +Base: 2445 +Linked animations: [1134, 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, 1687, 1688, 1689, 1690, 1691, 1692, 1693, 1699, 1700, 1701, 1702, 1703, 1704, 1706, 1707, 1715] +Linked objects: Evil tree (11434), Evil tree (11435), Evil tree (11436), Evil oak tree (11437), Evil oak tree (11438), Evil oak tree (11439), Evil willow tree (11440), Evil willow tree (11441), Evil willow tree (11442), Evil maple tree (11443), Evil maple tree (11444), Evil maple tree (11915), Evil yew tree (11916), Evil yew tree (11917), Evil yew tree (11918), Evil magic tree (11919), Evil magic tree (11920), Evil magic tree (11921), Elder evil tree (11922), Elder evil tree (11923), Elder evil tree (11924), null (11925), null (11926), null (11927), null (11928), null (11929), null (12711), null (12712), null (14846), null (14847), null (14848), null (14849), null (14883), null (14884), null (14885) + +Base: 2446 +Linked animations: [1694, 1695, 1696, 1697, 1698] +Linked objects: Seedling (11391), Sapling (11392), Sapling (11393), Young tree (11394), Young tree (11395) + +Base: 2447 +Linked animations: [353, 354, 355, 644, 1708] +Linked graphics: 314 +Linked objects: Evil root (11426), Evil root (11427), Evil root (11428), Evil root (11429), Evil root (11430), Evil root (11431), Evil root (11432), Evil root (11433), Root (47961), Root (47962), Root (47963), Root (47964), Root (47965), Root (47966), Root (47967) + +Base: 2448 +Linked animations: [643, 1709, 1710, 1711] +Linked graphics: 315, 316, 317, 318 + +Base: 2449 +Linked animations: [11514] +Linked objects: Egg-painting machine (30120), Egg-painting machine (49143) + +Base: 2450 +Linked animations: [11376, 11377] +Linked objects: Glyph (18980) + +Base: 2451 +Linked animations: [11333, 11334, 11335, 11336] +Linked objects: Portal (16577), Portal (42611) + +Base: 2452 +Linked animations: [11286, 11287] +Linked objects: Hole (18977) + +Base: 2453 +Linked animations: [175, 11284] +Linked objects: Dirty plates (18975) + +Base: 2454 +Linked animations: [11318, 11319, 11384, 11385] + +Base: 2455 +Linked animations: [11339, 11340] +Linked objects: Spear (42623) + +Base: 2456 +Linked animations: [11311, 11312, 11317] + +Base: 2457 +Linked animations: [11292, 11305, 11483] +Linked objects: Statue with bowl (42602) + +Base: 2458 +Linked animations: [11315, 11316] + +Base: 2459 +Linked animations: [11313, 11314] + +Base: 2460 +Linked animations: [1719, 1720, 1721, 1741, 1742] +Linked objects: Ur-taal (18987), Ur-meg (18988), Ur-lun (18989), Ur-pel (18990), Ur-zek (19956), Ur-vass (21251), Ur-tag (21252) + +Base: 2461 +Linked animations: [11343, 11344, 11345, 11346, 11347, 11348, 11349, 11350] +Linked objects: Rocks (42584) + +Base: 2462 +Linked animations: [11249, 11250] +Linked graphics: 2012, 2013 + +Base: 2463 +Linked animations: [11389] +Linked graphics: 2005 + +Base: 2464 +Linked animations: [11276, 11379] +Linked graphics: 2002, 2014 + +Base: 2465 +Linked animations: [11366] +Linked graphics: 580 + +Base: 2466 +Linked animations: [11277] +Linked graphics: 2015 + +Base: 2467 +Linked animations: [11280, 11281, 11282] +Linked graphics: 2016, 2017, 2018 + +Base: 2468 +Linked animations: [11257] +Linked graphics: 2007 + +Base: 2469 +Linked animations: [11360, 11369] +Linked graphics: 665, 1714 + +Base: 2470 +Linked animations: [11322, 11323, 11324, 11325, 11326] +Linked graphics: 320, 321, 322, 323, 402 + +Base: 2471 +Linked animations: [11246, 11247, 11248] +Linked graphics: 2009, 2010, 2011 + +Base: 2472 +Linked animations: [11351, 11352, 11353, 11354, 11355, 11356] +Linked objects: Fighters (16579), Fighters (16580), Fighters (16581), Fighters (16582), Fighters (16583), Fighters (16584), Fighters (17383) + +Base: 2473 +Linked animations: [11239, 11240, 11241, 11242, 11244, 11245, 11251, 11252, 11254, 11255, 11256, 11300] +Linked NPCs: Bandos avatar (4540), Bandos avatar (5460), Bandos avatar (5461) + +Base: 2474 +Linked animations: [11412] +Linked objects: null (42630) + +Base: 2475 +Linked animations: [11414, 11464, 11465, 11466, 11467, 11468, 11469, 11470, 11471, 11472, 11473, 11474, 11475, 11476, 11477, 11478, 11479, 11480] +Linked NPCs: Ogre chieftain (852), null (5175), Ogre shaman (5176), Ogre shaman (5177), null (5180), Ogre shaman (5181), null (5183), Ogre shaman (5184), null (5186), Ogre shaman (5187), null (5189), Ogre shaman (5190), null (5192), Ogre shaman (5193), Ogre chieftain (8660) + +Base: 2476 +Linked animations: [9882, 11429, 11430, 11440] +Linked graphics: 588, 2022, 2023, 2024 + +Base: 2477 +Linked animations: [2088] +Linked graphics: 2019 + +Base: 2478 +Linked animations: [2099] +Linked graphics: 2021 + +Base: 2479 +Linked animations: [2089] +Linked graphics: 2020 + +Base: 2480 +Linked animations: [11506, 11520, 11522] +Linked graphics: 2034, 2035 +Linked objects: null (30078), null (30079) + +Base: 2481 +Linked animations: [11554] +Linked graphics: 2038 + +Base: 2482 +Linked animations: [11517, 11526] +Linked graphics: 2032, 2033 + +Base: 2483 +Linked animations: [11523] +Linked graphics: 2036 + +Base: 2484 +Linked animations: [11555] +Linked graphics: 2039 + +Base: 2485 +Linked animations: [11538] +Linked graphics: 2037 + +Base: 2486 +Linked animations: [11533, 11534, 11535] +Linked objects: Nut machine (30124), Nut machine (30125), Nut machine (30126), Nut machine (49148), Nut machine (49149) + +Base: 2487 +Linked animations: [11537] +Linked objects: Clock (42725), Clock (49243) + +Base: 2488 +Linked animations: [11527, 11528, 11529] +Linked objects: Nut machine (30121), Nut machine (30122), Nut machine (30123), Nut machine (49145), Nut machine (49146) + +Base: 2489 +Linked animations: [11512] +Linked objects: Egg-painting machine (30118), Egg-painting machine (49138) + +Base: 2490 +Linked animations: [11530, 11531, 11532] +Linked objects: Nut machine (30127), Nut machine (30128), Nut machine (30129), Nut machine (49151), Nut machine (49152) + +Base: 2491 +Linked animations: [11548, 11549] +Linked objects: Wheel (30084), Wheel (30085), null (49110) + +Base: 2492 +Linked animations: [11507, 11508, 11509, 11539] +Linked objects: Easter Bird (30086), Easter Bird (30087), Easter Bird (30088), null (49111) + +Base: 2493 +Linked animations: [11501, 11502, 11503, 11504, 11505, 11510, 11516, 11518, 11519, 11521, 11525] +Linked NPCs: Easter Bunny (7407), Easter Bunny (7408), Easter Bunny (7409), Easter Bunny Jr (7410), Easter Bunny Jr (7411), Easter Bunny Jr (7412), Easter Bunny (9687) + +Base: 2494 +Linked animations: [11583, 11584, 11585] +Linked objects: null (42882), null (42883), null (42884) + +Base: 2495 +Linked animations: [11616] +Linked objects: null (42752), null (42753), null (42767) + +Base: 2496 +Linked animations: [11559, 11560, 11561, 11580, 11581] +Linked objects: Ice block (42805), Ice block (42806), Ice block (42807), Ice (42809), Ice (42811), Ice (42813), Ice (42815), Ice (42817), Ice (42819), Ice (42821), Ice (42823), Ice (42825), Ice (42827), Ice (42829), Ice (42831), Ice (42832), Ice (42833), Ice (42834), Ice (42835), Ice (42836), Ice (42837), Ice (42838), Ice (42839), Ice (42840), Ice (42841), Ice (42842), Ice (42843), Ice block (42879), Ice block (42880) + +Base: 2497 +Linked animations: [11565, 11566] +Linked objects: Blocked icy stream (42799) + +Base: 2498 +Linked animations: [11494] +Linked objects: Crumbling statue (42852) + +Base: 2499 +Linked animations: [11497, 11720, 11721, 11722] +Linked objects: null (42765), Fish (43201), Fish (43202), Fish (43203) + +Base: 2500 +Linked animations: [11591, 11592] +Linked graphics: 2045, 2046 + +Base: 2501 +Linked animations: [11563, 11564] +Linked graphics: 2030, 2031 + +Base: 2502 +Linked animations: [11582] +Linked graphics: 2040, 2041, 2042, 2043, 2044 + +Base: 2503 +Linked animations: [11575, 11576] + +Base: 2504 +Linked animations: [11586, 11587] +Linked NPCs: null (7457), Natural historian (7458) + +Base: 2505 +Linked animations: [11498, 11499, 11500, 11593] +Linked NPCs: null (7454), Muspah (7455) +Linked graphics: 2029 + +Base: 2506 +Linked animations: [11496, 11570] +Linked NPCs: Fire raft (7452), Fire raft (7453) + +Base: 2507 + +Base: 2508 +Linked animations: [11621] +Linked objects: Rubble (32719) + +Base: 2509 +Linked animations: [11624] +Linked graphics: 2047 + +Base: 2510 +Linked animations: [11645] +Linked objects: Blue block (42939), Red block (42940), Green block (42941), Yellow block (42942) + +Base: 2511 +Linked animations: [11626, 11627, 11628, 11629, 11630, 11643] +Linked NPCs: Tumeken's shadow (7475), Tumeken's shadow (7476), Tumeken's shadow (7477), Tumeken's shadow (7479), Tumeken's shadow (7480) + +Base: 2512 +Linked animations: [11638, 11639, 11640, 11641, 11642] +Linked NPCs: Golem guard (7497), Ice elemental (10460), Ice elemental (10461), Ice elemental (10462), Ice elemental (10463), Ice elemental (10464), Ice elemental (10465), Ice elemental (10466), Ice elemental (10467), Ice elemental (10468) + +Base: 2513 +Linked animations: [11653] +Linked objects: Enchanted sconce (42990) + +Base: 2514 +Linked animations: [11654, 11655] + +Base: 2515 +Linked animations: [11652] +Linked objects: Tumeken's image (42986) + +Base: 2516 +Linked animations: [11644] + +Base: 2517 +Linked animations: [11646, 11647, 11648, 11649, 11650, 11651] +Linked objects: Amascut (42974), Apmeken (42975), Crondis (42976), Het (42977), Icthlarin (42978), Scabaras (42979) + +Base: 2518 +Linked animations: [3046, 3047, 5480, 11688] +Linked objects: Cage (9091) + +Base: 2519 +Linked animations: [11706, 11716] +Linked objects: null (43066) + +Base: 2520 +Linked animations: [11707, 11708, 11709] + +Base: 2521 +Linked animations: [11725] +Linked objects: null (43425) + +Base: 2522 +Linked animations: [11723] +Linked objects: Tickertape machine (43406) + +Base: 2523 +Linked animations: [11717, 11718, 11719] +Linked objects: Engine (43159), Engine (43163), Broken engine (43164) + +Base: 2524 +Linked animations: [11713] +Linked objects: null (43094), null (43095), null (43096), null (43097), null (43098), null (43099), Well (43100), Well (43101), Well (43102), Well (43103), Well (43104), Well (43105) + +Base: 2525 +Linked animations: [11749] +Linked graphics: 2049 + +Base: 2526 +Linked animations: [11759, 11760, 11761, 11762, 11763, 11764, 11765] + +Base: 2527 +Linked animations: [11766] + +Base: 2528 +Linked animations: [11767, 11768, 11769, 11770] + +Base: 2529 +Linked animations: [11711, 11712] +Linked NPCs: null (8674), Polar bear (8675), Jim (8676) + +Base: 2530 +Linked animations: [11714] +Linked NPCs: Chuck (8679) + +Base: 2531 +Linked animations: [11715] +Linked objects: Chuck (43424) + +Base: 2532 +Linked animations: [11734, 11740, 11741] +Linked NPCs: Larry (7578), Larry (7579) + +Base: 2533 + +Base: 2534 +Linked animations: [11808] +Linked NPCs: Snake (1875) +Linked objects: Snake basket (21249) + +Base: 2535 +Linked animations: [7525, 7526, 11819, 11820] +Linked objects: Spring device (43587) + +Base: 2536 +Linked animations: [7527] + +Base: 2537 +Linked animations: [11872, 11874, 11886] +Linked graphics: 2060, 2061, 2062 + +Base: 2538 +Linked animations: [11858] +Linked graphics: 2057 + +Base: 2539 +Linked animations: [11884, 11888, 11889, 11890, 11891] +Linked graphics: 2063, 2064, 2065, 2066, 2067 + +Base: 2540 +Linked animations: [11824, 11853] +Linked graphics: 2051, 2056 + +Base: 2541 +Linked animations: [11877] +Linked objects: null (43691), null (43692), null (43693), null (43694), null (43695), null (43696), null (43697), null (43698), Green command tent (43791), Blue command tent (43792), Yellow command tent (43793), Red command tent (43794), null (43839), null (43840) + +Base: 2542 +Linked animations: [11876] +Linked objects: Huge flag (43768), null (43769), null (43770) + +Base: 2543 +Linked animations: [11847, 11894] +Linked objects: null (43827), null (43828), Map table (43829), Map table (43830) + +Base: 2544 +Linked animations: [11859, 11860] +Linked objects: Castle turret (43730), Castle turret (43731), Castle turret (43732) + +Base: 2545 +Linked animations: [11875] +Linked objects: Cave entrance (43666) + +Base: 2546 +Linked animations: [11887] +Linked objects: Pile of wood (43748), Pile of wheels (43749), Pile of metal (43750), Pile of ropes (43751), Pile of rocks (43752), null (43763), null (43764), null (43765), null (43766) + +Base: 2547 +Linked animations: [11861, 11862] + +Base: 2548 +Linked animations: [11882, 11883] +Linked objects: Catapult (43744) + +Base: 2549 +Linked animations: [11849, 11850, 11851, 11852] +Linked graphics: 2052, 2053, 2054, 2055 + +Base: 2550 +Linked animations: [11892] +Linked objects: null (43736) + +Base: 2551 +Linked animations: [11863, 11864] +Linked graphics: 2058, 2059 + +Base: 2552 +Linked animations: [11823] +Linked graphics: 1280 + +Base: 2553 +Linked animations: [11822] +Linked graphics: 1275 + +Base: 2554 +Linked animations: [11825, 11826, 11827, 11828, 11829, 11830, 11831, 11832, 11833, 11834, 11835, 11836, 11837, 11838, 11839, 11840, 11841, 11842, 11843, 11844, 11845, 11846] +Linked NPCs: Dwarf squad (light) (red) (8729), Elf squad (light) (red) (8730), Goblin squad (light) (red) (8731), Dwarf squad (light) (blue) (8732), Elf squad (light) (blue) (8733), Goblin squad (light) (blue) (8734), null (8735), null (8740), null (8745), null (8750), null (8755), null (8760), null (8765), null (8770), null (8775), null (8780), null (8785), null (8790), null (8875), null (8880), null (8885), null (8890), null (8895), null (8900), null (8905), null (8910), Dwarf squad (red) (8931), null (9050), null (9055), null (9060), null (9065) +Linked objects: Rescued TzHaar (43635), Rescued TzHaar (43636), Rescued TzHaar (43637), Rescued TzHaar (43638), Rescued TzHaar (43639), Rescued TzHaar (43640), Rescued TzHaar (43641), Rescued TzHaar (43642), Rescued TzHaar (43643) + +Base: 2555 +Linked animations: [2587, 11821] +Linked objects: TzHaar (43661) + +Base: 2556 +Linked animations: [11865, 11866, 11867, 11868, 11869, 11870] +Linked NPCs: null (8942), Dwarven recruiter (8943), null (8944), Elven recruiter (8945), null (8946), Goblin recruiter (8947), Banker (8948) + +Base: 2557 +Linked animations: [11895, 11896, 11897, 11898, 11899] +Linked NPCs: Sphinx (1990), Sphinx (2637) + +Base: 2558 +Linked animations: [11905, 11906, 11907, 11912, 11913, 11914] +Linked NPCs: Marker plant (9150), Marker plant (S) (9151), Marker plant (NE) (9152), Marker plant (NW) (9153), Marker plant (N) (9154), Marker plant (SE) (9155), Marker plant (SW) (9156), Marker plant (E) (9157), Marker plant (W) (9158) + +Base: 2559 +Linked animations: [11915, 11916, 11917, 11918] +Linked graphics: 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099 + +Base: 2560 +Linked animations: [11925] +Linked objects: Fire trap (44221) + +Base: 2561 +Linked animations: [11928] +Linked objects: Human detection spell (44220) + +Base: 2562 +Linked animations: [11927] +Linked objects: null (44237), Pedestal (44238), Pedestal (44239) + +Base: 2563 +Linked animations: [11934] +Linked objects: Puddle (44097) + +Base: 2564 +Linked animations: [11926] +Linked objects: Rubble (44199) + +Base: 2565 +Linked animations: [11935, 11936, 11937, 11938, 11939, 11940, 11941] +Linked NPCs: Killerwatt (3201), Killerwatt (3202), Elemental creature (9432) +Linked objects: Cage (44244) + +Base: 2566 +Linked animations: [11930] +Linked graphics: 2101 + +Base: 2567 +Linked animations: [11929] +Linked graphics: 2103 + +Base: 2568 +Linked animations: [917, 14287, 14288] +Linked objects: Entrance (3108), Entrance (3111), Entrance (3112), Entrance (3113), Entrance (3114), null (6254), Human flag (10541), Human flag (10542), Dwarf flag (10543), Dwarf flag (10544), Elf flag (10545), Elf flag (10546), Gnome flag (10547), Gnome flag (10548), Werewolf flag (10549), Werewolf flag (10550), TzHaar flag (10551), TzHaar flag (10552), Entrance (27688), Entrance (27690) + +Base: 2569 +Linked animations: [3483, 4539, 7069, 11996, 11997, 11998] +Linked graphics: 1221, 2104, 2105, 2110, 2111, 2112 + +Base: 2570 +Linked animations: [408, 11962] +Linked objects: Geoff (44341), Geoff (44342) + +Base: 2571 +Linked animations: [411] + +Base: 2572 +Linked animations: [11953, 11954, 11955, 11959] +Linked graphics: 2106, 2107 +Linked objects: Dartboard (44301) + +Base: 2573 +Linked animations: [1669, 11972] +Linked graphics: 341, 2108 + +Base: 2574 +Linked animations: [12043, 12044, 12045, 12046, 13437] +Linked graphics: 2119, 2120, 2121, 2122, 2386 + +Base: 2575 +Linked animations: [12037, 12038, 12039, 12040, 12041, 12042] +Linked NPCs: Aquanite (9172) + +Base: 2576 +Linked animations: [12047] +Linked graphics: 2123 + +Base: 2577 +Linked animations: [12056, 12058] +Linked graphics: 2133, 2134 + +Base: 2578 +Linked animations: [12082, 12083, 12084, 12085, 12086, 12087] +Linked graphics: 2124, 2125, 2126, 2127, 2128, 2129 + +Base: 2579 +Linked animations: [12060, 12061, 12062, 12063, 12064, 12065, 12066, 12067, 12068, 12069, 12070, 12071, 12072, 12073, 12074, 12075, 12076, 12077, 12078, 12079, 12080, 12081, 12104, 12105] +Linked NPCs: Skeletal horror (9176), Skeletal horror (9177), Skeletal horror (9178), Skeletal horror (9179), Skeletal horror (9180) + +Base: 2580 +Linked animations: [12048, 12049, 12050, 12051, 12052, 12053, 12054] +Linked NPCs: Odd Old Man (9173), Odd Old Man (9174) + +Base: 2581 +Linked animations: [12099, 12100, 12101, 12102, 12103] +Linked NPCs: Tail (9183) +Linked graphics: 2132 + +Base: 2582 +Linked animations: [12089, 12090, 12091, 12092, 12093, 12094, 12095, 12096, 12097, 12098] +Linked NPCs: Right hand (9181), Left hand (9182) +Linked graphics: 2130, 2131 + +Base: 2583 +Linked animations: [12113] + +Base: 2584 +Linked animations: [12116, 12117] + +Base: 2585 +Linked animations: [12135, 12136, 12137, 12138] +Linked objects: Machine (7193), Machine (44979), null (44980) + +Base: 2586 +Linked animations: [12123, 12124, 12125, 12130] +Linked objects: Sorting machine (45028), Sorting machine (45034) + +Base: 2587 +Linked animations: [2314, 12111] + +Base: 2588 +Linked animations: [12166] +Linked objects: Bowl (45055) + +Base: 2589 +Linked animations: [12165, 12167, 12168] +Linked objects: Fire pit (45056), Rod (45057) + +Base: 2590 +Linked animations: [12112] + +Base: 2591 +Linked animations: [12146, 12147, 12148, 12149, 12150, 12151] +Linked NPCs: Chaos dwogre (8771) + +Base: 2592 +Linked animations: [12178] +Linked graphics: 2142 + +Base: 2593 +Linked animations: [12182] +Linked graphics: 2143 + +Base: 2594 +Linked animations: [12172] +Linked graphics: 2140 + +Base: 2595 +Linked animations: [12159, 12160, 12176] +Linked graphics: 2137, 2138, 2141 + +Base: 2596 +Linked animations: [12225] + +Base: 2597 +Linked animations: [12218, 12219, 12223] +Linked objects: Collapsed mineral deposit (5990), Collapsed mineral deposit (45075) + +Base: 2598 +Linked animations: [12224] + +Base: 2599 +Linked animations: [12213] +Linked objects: Pulley lift (45079) + +Base: 2600 +Linked animations: [12293] +Linked objects: Blacksmith working the bellows (45311) + +Base: 2601 +Linked animations: [1561] +Linked graphics: 2275, 2276 + +Base: 2602 +Linked animations: [12304] +Linked objects: Weather vane (46020) + +Base: 2603 +Linked animations: [12262, 12273, 12924] + +Base: 2604 +Linked animations: [12275] +Linked objects: Ladder (45786) + +Base: 2605 +Linked animations: [12240, 12241, 12242, 12243] +Linked objects: Wooden pillar (45569), Wooden pillar (45570) + +Base: 2606 +Linked animations: [12281] +Linked objects: Roots (45599) + +Base: 2607 +Linked animations: [12279] +Linked objects: Rock (45596) + +Base: 2608 +Linked animations: [12286, 12287] + +Base: 2609 +Linked animations: [5502, 12271, 13736, 13737] +Linked graphics: 2148, 2183, 2614, 2615 + +Base: 2610 +Linked animations: [12236] +Linked graphics: 2151 + +Base: 2611 + +Base: 2612 + +Base: 2613 +Linked animations: [12244, 12245] +Linked graphics: 2149, 2150, 2271 + +Base: 2614 +Linked animations: [8843, 8844, 12170, 12193, 12194, 12195, 12196, 12197, 12198, 12199, 12200, 12201, 12202, 12203, 12204, 12205, 12206, 12207, 12208, 12209, 12210, 12220, 12221, 12222] +Linked NPCs: Living rock protector (8832), Living rock striker (8833), Living rock patriarch (8834), Living rock remains (8837), Living rock remains (8838), Living rock remains (8839), Rock critter (8840), Pit rock protector (11564), Pit rock protector (11588), Pit rock protector (11589) +Linked graphics: 1565, 2139 +Linked objects: Pit rock protector (39262), Pit rock protector (41605) + +Base: 2615 +Linked animations: [12298] +Linked objects: null (3181), null (3193), null (3198), null (3201), null (4140), null (27662), null (27671), null (27674), null (27675), null (27678), null (27679), null (27680), null (27681), null (27683), Staircase (27866), null (27875), null (27876), null (27883), null (45630), null (45631), null (45632), Candlestand (46551), null (46552), Table (46564), Candlestand (46807), Candlestand (46808), Candlestand (46809), Candlestand (46831), Candlestand (46832), Candlestand (46833), Staircase (47364), null (47451), null (47452), null (47457), null (47458), null (47462), null (47463), null (47591), null (47592), null (47593), null (47647), null (47653), null (47658), null (47659), Table (47677), Table (47681), Table (47684), Table (47685), Table (47686), null (47711), null (47860), null (47861), Candlestand (47872), Table (47873), null (47874), null (47875), null (53261), null (53262), null (53263), null (54693) + +Base: 2616 +Linked animations: [12294, 12295, 12296] +Linked NPCs: Lumbridge Sage (2244) + +Base: 2617 + +Base: 2618 +Linked animations: [3336, 9428, 12599, 12600] +Linked objects: Open chest (49896), Open chest (49897), Locked chest (49900), Open chest (49908), Open chest (49909), Open chest (49920), Open chest (49921), Open chest (53821), Open chest (53822) + +Base: 2619 +Linked animations: [12359, 12360] +Linked objects: Ivy (46318), Ivy stump (46319), Ivy (46320), Ivy stump (46321), Ivy (46322), Ivy stump (46323), Ivy (46324), Ivy stump (46325) + +Base: 2620 +Linked animations: [12385, 12386, 12387, 12388, 12389, 12390, 12391, 12392, 12393] +Linked graphics: 2157, 2158, 2159, 2160, 2161, 2162, 2163, 2164 + +Base: 2621 +Linked animations: [12351, 12352, 12353, 12354, 12355, 12356, 12357] +Linked graphics: 2, 3 +Linked objects: Woodcutting stump (21305), Stump (46328) + +Base: 2622 +Linked animations: [12399, 12401, 12402] +Linked objects: Conveyor belt hopper (46304) + +Base: 2623 +Linked animations: [12374] +Linked graphics: 2156 + +Base: 2624 +Linked animations: [12369, 12370, 12371, 12372] +Linked graphics: 2152, 2153, 2154, 2155 + +Base: 2625 +Linked animations: [12358] +Linked graphics: 0 + +Base: 2626 +Linked animations: [163, 164, 165, 167, 2958, 4784, 12403, 12404, 12405, 12406, 12407, 12408, 12409, 12410, 12984, 12985, 12986, 12987, 12988, 12989, 12990, 12991, 12992, 12997, 14621] +Linked NPCs: Hobgoblin (122), Hobgoblin (123), Hobgoblin (2685), Hobgoblin (2686), Hobgoblin (2687), Hobgoblin (2688), Hobgoblin Champion (3061), Hobgoblin (3583), Hobgoblin (4898), Hobgoblin (6275), Hobgoblin Geomancer (10059), Hobgoblin Geomancer (10060), Hobgoblin Geomancer (10061), Hobgoblin Geomancer (10062), Hobgoblin Geomancer (10063), Hobgoblin Geomancer (10064), Hobgoblin Geomancer (10065), Hobgoblin Geomancer (10066), Hobgoblin Geomancer (10067), Hobgoblin Geomancer (10068), Hobgoblin Geomancer (10069), Hobgoblin Geomancer (10070), Hobgoblin Geomancer (10071), Hobgoblin Geomancer (10072), Hobgoblin (10236), Hobgoblin (10237), Hobgoblin (10238), Hobgoblin (10239), Hobgoblin (10240), Hobgoblin (10241), Hobgoblin (10242), Hobgoblin (10243), Hobgoblin (10244), Hobgoblin (10245) +Linked graphics: 2777 +Linked objects: Hobgoblin (13368) + +Base: 2627 +Linked animations: [12412] +Linked objects: Waterlily (1175), Waterlily (1176), Waterlily (1177) + +Base: 2628 +Linked animations: [12417] + +Base: 2629 +Linked animations: [12416] +Linked graphics: 2165 + +Base: 2630 +Linked animations: [12432] + +Base: 2631 +Linked animations: [12434] +Linked objects: null (46501), null (46502), null (46503), null (46504), Light receiver (46505), Light receiver (46506), Light receiver (46507), Light receiver (46508) + +Base: 2632 +Linked animations: [12433] + +Base: 2633 +Linked animations: [12427] +Linked NPCs: Crystal core (8960), Crystal core (8961), Crystal core (8963), Crystal core (8965) + +Base: 2634 +Linked animations: [12426] +Linked graphics: 2170 + +Base: 2635 +Linked animations: [12424] +Linked objects: Energised pillar (46519) + +Base: 2636 +Linked animations: [12428] +Linked graphics: 2167, 2168 + +Base: 2637 +Linked animations: [12425, 12430] +Linked graphics: 2166, 2169 + +Base: 2638 +Linked animations: [12439, 12440, 12447, 12448] +Linked graphics: 761, 762, 2174, 2175 + +Base: 2639 +Linked animations: [12443, 12444, 12451, 12452] +Linked graphics: 2172, 2173, 2176, 2177 + +Base: 2640 +Linked animations: [12501, 12502, 12503] + +Base: 2641 +Linked animations: [12485, 12488] +Linked objects: Portal (46933), Portal (46934) + +Base: 2642 +Linked animations: [12483, 12484] +Linked NPCs: Grim Reaper (8977) + +Base: 2643 +Linked animations: [12486, 12487] +Linked NPCs: Spider herald (8976) + +Base: 2644 +Linked animations: [12498] +Linked NPCs: Spider (8979) + +Base: 2645 +Linked animations: [12499, 12500] +Linked NPCs: Spider Queen (8975) + +Base: 2646 +Linked animations: [12491, 12497, 12506] +Linked graphics: 2178, 2179, 2180 + +Base: 2647 +Linked animations: [12508] +Linked objects: null (27884), null (47590) + +Base: 2648 +Linked animations: [1545, 1547] +Linked objects: Armour (47408) + +Base: 2650 +Linked animations: [12523, 12524, 12525, 12527, 12528] +Linked NPCs: Imp (8994), Banner carrier (8995), Banner carrier (8996), Banner carrier (8997), Banner carrier (8998), Banner carrier (8999), Banner carrier (9002), Banner carrier (9003), Banner carrier (9004), Banner carrier (9005), Banner carrier (9006), Banner carrier (9007), Banner carrier (9008), Banner carrier (9009), Banner carrier (9012), Banner carrier (9013), Banner carrier (9014), Banner carrier (9015), Banner carrier (9016), Banner carrier (9017), Banner carrier (9018), Banner carrier (9019), Banner carrier (9022), Banner carrier (9023), Banner carrier (9024), Banner carrier (9025), Banner carrier (9026), Banner carrier (9027), Banner carrier (9028), Banner carrier (9029), Banner carrier (9032), Banner carrier (9033) + +Base: 2651 +Linked animations: [5500, 12520] +Linked graphics: 2182, 2188 + +Base: 2652 +Linked animations: [5494, 12521] +Linked graphics: 2181, 2187 + +Base: 2653 +Linked animations: [2934] +Linked graphics: 2192, 2194, 2196, 2198 + +Base: 2654 +Linked animations: [1116] +Linked graphics: 2191, 2193, 2195, 2197, 2283 + +Base: 2655 +Linked animations: [9639] +Linked objects: null (47119), Altar of Zaros (47120), PLACEHOLDER: Runecrafting Altar (50034) + +Base: 2656 +Linked animations: [7535] +Linked objects: Communion portal (47077) + +Base: 2657 +Linked animations: [7536] +Linked NPCs: Altar (9053) +Linked objects: Smashed pedestal (47198), Smashed pedestal (47199) + +Base: 2658 +Linked animations: [12561, 12562] +Linked objects: Trapdoor (4575), Trapdoor (4576) + +Base: 2659 +Linked animations: [2981, 7206, 12533, 12534, 12535, 12536, 12537, 12538] +Linked objects: Ice block (47131), Ice block (47132), Ice block (47165), Ice block (47167) + +Base: 2660 +Linked animations: [12555] +Linked objects: Radiant pedestal (47136), Radiant pedestal (47175), Radiant pedestal (47187), Radiant pedestal (47188), Radiant pedestal (47189), Radiant pedestal (47190), Radiant pedestal (47191), Radiant pedestal (47192), Radiant pedestal (47193), Radiant pedestal (47194), Radiant pedestal (47195) + +Base: 2661 +Linked animations: [2975, 2977, 2978, 2979, 2980, 12556, 12559] +Linked NPCs: Ice demon (9052) + +Base: 2662 +Linked animations: [12550] + +Base: 2663 +Linked animations: [2944, 2962, 2966, 2971, 2973, 2974] +Linked graphics: 1701, 2199, 2200, 2201, 2202, 2203 + +Base: 2664 +Linked animations: [12557, 12558] +Linked graphics: 2210, 2211 + +Base: 2665 +Linked animations: [12560] +Linked graphics: 2212 + +Base: 2666 +Linked animations: [12545, 12546] +Linked graphics: 2208, 2209 + +Base: 2667 +Linked animations: [12530] +Linked graphics: 2204 + +Base: 2668 +Linked animations: [12566] +Linked graphics: 2226 + +Base: 2669 +Linked animations: [12574] +Linked graphics: 2227, 2228, 2229, 2230 + +Base: 2670 +Linked animations: [12576, 12577, 12578, 12579] +Linked graphics: 2231, 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258 + +Base: 2671 + +Base: 2672 +Linked animations: [12553, 12590] +Linked graphics: 2266 + +Base: 2673 +Linked animations: [12570, 12571, 12572] +Linked graphics: 2214, 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, 2223, 2224, 2225 + +Base: 2674 +Linked animations: [12585, 12586, 12587, 12588] +Linked graphics: 2262, 2263, 2264, 2265 + +Base: 2675 +Linked animations: [12580, 12581, 12582] +Linked graphics: 2259, 2260, 2261 + +Base: 2676 +Linked animations: [12568] +Linked graphics: 2213 + +Base: 2677 +Linked animations: [12540, 12541] +Linked graphics: 2206, 2207 + +Base: 2678 +Linked animations: [1512, 1513] + +Base: 2679 +Linked animations: [1519] +Linked objects: Mast (47340) + +Base: 2680 +Linked animations: [1520] +Linked objects: null (47270) + +Base: 2681 +Linked animations: [1524] +Linked graphics: 2270 + +Base: 2682 +Linked animations: [1522, 1523] +Linked graphics: 333, 2269 + +Base: 2683 +Linked animations: [1526] + +Base: 2684 +Linked animations: [12624] +Linked objects: Sign (14869) + +Base: 2685 +Linked animations: [12623] +Linked objects: Card table (12975) + +Base: 2686 +Linked animations: [72, 3113, 3114, 12617, 12630, 12631] +Linked objects: Sword case (12983) + +Base: 2687 +Linked animations: [12612] +Linked objects: Magic field (12990), Magic field (12991), Magic field (12992), null (14874) + +Base: 2688 +Linked animations: [1568, 2990, 3111, 3112, 3322, 3323, 3328, 3330, 3331, 3332, 3333, 12604] +Linked NPCs: Count Draynor (9356), Count Draynor (9357) +Linked objects: Coffin (158), Coffin (159), Coffin (162) + +Base: 2689 +Linked animations: [12605] +Linked graphics: 567 + +Base: 2690 +Linked animations: [12607, 12613] +Linked graphics: 778 +Linked objects: null (12971), null (12973) + +Base: 2691 +Linked animations: [12622, 12646] +Linked graphics: 779, 2277 + +Base: 2692 +Linked animations: [4608, 4609] + +Base: 2693 +Linked animations: [12660] +Linked objects: Scourge's grave (47785) + +Base: 2694 +Linked animations: [12666, 12687, 12688] + +Base: 2695 +Linked animations: [12644, 12679, 12680, 12681, 12682, 12683] +Linked NPCs: Ebenezer Scourge (9363), null (9369), Ebenezer Scourge (9371) +Linked objects: Scourge's bed (47856) + +Base: 2696 + +Base: 2697 +Linked animations: [12639, 12656, 12684, 12685] +Linked NPCs: Tiny Thom (9366), Tiny Thom (9402), Tiny Thom (9422) + +Base: 2698 +Linked animations: [12659] +Linked graphics: 780 + +Base: 2699 +Linked animations: [12634, 12635, 12636] +Linked graphics: 5 +Linked objects: General Wartface (47790), General Wartface (47791), General Bentnoze (47793), General Bentnoze (47794), Sir Amik Varze (47796), Sir Amik Varze (47797), Cook (47799), Cook (47800), Partygoer (47802), Partygoer (47803), Tiny Thom (47805), Tiny Thom (47806) + +Base: 2700 + +Base: 2701 +Linked animations: [4628] +Linked graphics: 4 + +Base: 2702 +Linked animations: [12705, 12706, 12707, 12708, 12709, 12710] +Linked objects: Doorway (47921), Doorway (47922), null (47923), Doorway (47924), Doorway (47925), null (47926), Doorway (47940), Doorway (47941), null (47942), Doorway (47943), Doorway (47944), null (47945), Doorway (47955), Doorway (47956), null (47957), Doorway (47958), Doorway (47959), null (47960), Doorway (47994), Doorway (47995), null (47996), Doorway (47997), Doorway (47998), null (47999), null (48131), null (48132), null (48133), null (48134), null (48135), null (48136), null (48137), null (48138), null (48139), null (48140), null (48141), null (48142), null (48143), null (48144), null (48145), null (48146), null (48147), null (48148), null (48149), null (48150), null (48151), null (48152), null (48153), null (48154), null (48155), null (48156), null (48157), null (48158), null (48159), null (48160), null (48161), null (48162), null (48163), null (48164), null (48165), null (48166) + +Base: 2703 +Linked animations: [12732, 12733] +Linked objects: Lever (47931), Lever (47932) + +Base: 2704 +Linked animations: [12711, 12712, 12713, 12714, 12715, 12716, 12753] +Linked objects: Empty channel (41715), Full channel (41716), Empty channel (41722), Empty channel (47910), Full channel (47911), Full channel (47912), Full channel (47914), Full channel (47915), Empty channel (47918), Empty channel (47919), Waterfall (48027), Rubble (54130), Rubble (54131), Rubble (54132), Rubble (54133), null (54146), null (54147), null (54148), null (54149), null (54154), null (54155), null (54156), null (54157) + +Base: 2705 +Linked animations: [12734, 12736, 12744] +Linked objects: Drips (47946), Waterfall (47947), null (47948), Full reservoir (48030), null (48031), null (48032), Full reservoir (48033) + +Base: 2706 +Linked animations: [12751, 12752] +Linked objects: Strange device (47934), Broken device (47935), Strange device (47949), Strange device (47950), Strange device (47971), Broken device (47972), Strange device (48000), Broken device (48001) + +Base: 2707 +Linked animations: [12719, 12726, 12727, 12728] +Linked graphics: 2280, 2281 +Linked objects: Soul energy (47979) + +Base: 2708 +Linked animations: [2287, 2723, 9500, 9501, 9505, 9514] +Linked NPCs: Corruption beast (9455) + +Base: 2709 +Linked animations: [9515, 12692, 12693, 12694, 12695, 12696, 12697, 12698, 12699, 12700, 12701, 12702, 12703, 12704, 12729, 12730, 12750] +Linked NPCs: Nomad (8528), Nomad (8529), Nomad (8530), Nomad (8531), Nomad (8532) +Linked objects: Nomad (48072), Nomad's throne (48073), null (48074), null (48075), null (48076), null (48077), null (48078) + +Base: 2710 +Linked animations: [12720, 12721, 12722] +Linked NPCs: Flame vortex (9441) + +Base: 2711 +Linked animations: [12717, 12718] +Linked graphics: 1657, 1658 + +Base: 2712 +Linked animations: [12763] +Linked graphics: 2284, 2285, 2286, 2287, 2288, 2289, 2290, 2291, 2292, 2293, 2294, 2295, 2296, 2297, 2298 + +Base: 2713 +Linked animations: [5390, 5391, 5406, 5427] +Linked graphics: 2303, 2304, 2305, 2306 + +Base: 2714 +Linked animations: [12775, 12779, 12783] +Linked graphics: 2308, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359 +Linked objects: Animation table (547), Animation table (20220), Animation table (20221) + +Base: 2715 +Linked animations: [12778] +Linked graphics: 2307 + +Base: 2716 +Linked animations: [12788, 12789, 12790, 12791, 12792, 12793, 12794, 12795, 12796] +Linked NPCs: Mound (9462), Ice strykewyrm (9463), Mound (9464), Desert strykewyrm (9465), Mound (9466), Jungle strykewyrm (9467) + +Base: 2717 +Linked animations: [12805] +Linked graphics: 2319, 2320 + +Base: 2718 +Linked animations: [12799, 12800] +Linked graphics: 2312, 2313 + +Base: 2719 +Linked animations: [12797, 12798, 12802, 12803] +Linked graphics: 2310, 2311, 2315, 2316, 2317, 2318 + +Base: 2720 +Linked animations: [12801] +Linked graphics: 2314 + +Base: 2721 +Linked animations: [12808, 12809] +Linked graphics: 2322, 2323 + +Base: 2722 +Linked animations: [12811, 12812, 12813] +Linked graphics: 2324, 2325, 2326 + +Base: 2723 +Linked animations: [12807, 12815] +Linked NPCs: Raw shards (9494), null (9495), null (9496), null (9497), null (9498), null (9499), null (9500), null (9501), null (9502), null (9503), null (9504), null (9505), null (9506), null (9507), null (9508), null (9509), null (9510), null (9511), null (9512), null (9513), null (9514), null (9515), null (9516), null (9517), null (9518), null (9519), null (9520), null (9521), null (9522), null (9523), null (9524), null (9525), null (9526), null (9527), null (9528), null (9529), null (9530), null (9531), null (9532), null (9533), null (9534), null (9535), null (9536), null (9537), null (9538), null (9539), null (9540), null (9541), null (9542), null (9543), null (9544), null (9545), null (9546), null (9547), null (9548), null (9549), null (9550), null (9551), null (9552), null (9553), null (9554), null (9555), null (9556), null (9557), null (9558) +Linked graphics: 2321 + +Base: 2724 +Linked animations: [13385, 13386, 13387, 13388, 13389, 13390, 13391, 13392, 13393, 13394] +Linked objects: null (48382), null (48383), null (48386), null (48401), null (48402), null (48403), Tent (48404), null (48405), null (48406), null (48595), null (48596), null (48597), null (48599) + +Base: 2725 +Linked animations: [13178] +Linked objects: Fremennik ship (48579) + +Base: 2726 +Linked animations: [6607] + +Base: 2727 +Linked animations: [12836, 12838] +Linked objects: Statue of Dahmaroc (48660) + +Base: 2728 +Linked animations: [12898, 12899] +Linked objects: Jade demon statuette (48665), Topaz demon statuette (48667), Sapphire demon statuette (48669), Emerald demon statuette (48671), Ruby demon statuette (48673), Diamond demon statuette (48675), Diamond demon statuette (48676) + +Base: 2729 +Linked animations: [12888] + +Base: 2730 +Linked animations: [12901, 12902, 12903] +Linked graphics: 2344, 2345 +Linked objects: Tomb (48686) + +Base: 2731 +Linked animations: [12897] +Linked objects: Stairs (48687) + +Base: 2732 +Linked animations: [12886, 12887] +Linked objects: Tomb door (48786), Tomb door (48787), Tomb door (48795), Tomb door (48796) + +Base: 2733 +Linked animations: [12839, 12841, 12844, 12847, 12861, 12862, 12863, 12864, 12865, 12866, 12867, 12868, 12869, 12870, 12871, 12872, 12873, 12874, 12883, 12884, 12885, 12889, 12890, 12891, 12892, 12893, 12904] +Linked NPCs: Crawling corpse torso (7916), Crawling corpse torso (7917), Crawling corpse torso (7918), Crawling corpse torso (7919), Skeleton (7921), Corpse archer (7923), Corpse archer (7924), Corpse archer (7925), Corpse archer (7926), Corpse archer (7927), Corpse archer (7928), Corpse archer (9607), Corpse archer (9608), Corpse archer (9609), Corpse archer (9610), Corpse mage (9612), Corpse mage (9613), Corpse mage (9614), Corpse mage (9615), Corpse mage (9616), Corpse mage (9617), Corpse mage (9618), Corpse mage (9619), Corpse mage (9620), Corpse mage (9621), Dragith Nurn (9622), Dragith Nurn (9641) + +Base: 2734 +Linked animations: [12845, 12846] +Linked graphics: 2341, 2342 + +Base: 2735 +Linked animations: [12848] +Linked graphics: 2343 + +Base: 2736 +Linked animations: [12840] +Linked graphics: 392 + +Base: 2737 +Linked animations: [12842, 12843] +Linked graphics: 2339, 2340 + +Base: 2738 +Linked animations: [12922] +Linked objects: Row boat (49053) + +Base: 2739 +Linked animations: [12918] + +Base: 2740 +Linked animations: [439] +Linked graphics: 2360, 2361, 2362, 2363, 2364, 2365, 2366 + +Base: 2741 +Linked animations: [12925, 12926, 12927, 12928, 12929, 12930, 12931, 12932, 12933, 12934, 12935, 12936, 12937, 12938, 12939, 12940, 12941, 12942, 12943, 12944, 12945, 12946, 12947, 12948, 12949, 12950, 12951, 12952, 12953, 12954, 12955, 12956, 12957, 12958, 12959, 12960, 12961, 12962, 12963, 12964, 12965, 12966, 12967, 12968, 12969, 12970, 12971, 12972, 12973, 12974, 12975, 12976, 12977, 12978, 12979] + +Base: 2742 +Linked animations: [12980] +Linked objects: null (49260), null (49261), null (49262), null (49263), null (49264), null (53248), null (53249), null (53250), null (53251), null (53252), null (53253), null (54427), null (54428), null (54429) + +Base: 2743 +Linked animations: [13408, 13409] +Linked objects: null (48407), null (48408) + +Base: 2744 +Linked animations: [13527] +Linked objects: null (52277), null (52278), null (52279), null (52280), null (52281) + +Base: 2745 +Linked animations: [13765] +Linked objects: Guardian door (50345), Guardian door (50346), Guardian door (50347), Guardian door (50348), Guardian door (53949) + +Base: 2746 +Linked animations: [13542, 13589, 13590, 13593, 13595, 13596] +Linked objects: Strange wall (49695), Ladder (49696), Strange wall (49697), Ladder (49698), Strange wall (49699), Ladder (49700), Strange wall (53747), Ladder (53748) + +Base: 2747 +Linked animations: [13411] +Linked objects: null (48392) + +Base: 2748 +Linked animations: [13661, 13663, 13664] +Linked objects: Runecrafting altar (50035), Runecrafting altar (50036), Runecrafting altar (50037), Runecrafting altar (53844) + +Base: 2750 +Linked animations: [13327, 13328, 13329, 13330, 13331, 13332] +Linked graphics: 2518, 2519, 2520, 2521, 2522, 2523 + +Base: 2751 +Linked animations: [13757] +Linked objects: Rocky debris (49615), Rocky debris (49616), Rocky debris (49617), Rocky debris (49618) + +Base: 2752 +Linked animations: [13529, 13530, 13531, 13532, 13533, 13644, 13645] +Linked objects: Inactive lodestone (49270), Inactive lodestone (49271), Activated lodestone (49319), Blue crystal (49468), Blue crystal (49469), Blue crystal (49470), Red crystal (49477), Red crystal (49478), Red crystal (49479), Green crystal (49486), Green crystal (49487), Green crystal (49488), Yellow crystal (49495), Yellow crystal (49496), Yellow crystal (49497), Large crystal (49510), Large crystal (49511), Large crystal (49512), Large crystal (49513), Large crystal (49514), Large crystal (49515), Active lodestone (49573), Activated lodestone (50785), Activated lodestone (51053), Activated lodestone (51099), Activated lodestone (51601), Activated lodestone (51647), Blue crystal (54262), Red crystal (54265), Green crystal (54268), Yellow crystal (54271), Large crystal (54276), Large crystal (54277) + +Base: 2753 +Linked animations: [13312, 13313, 13314, 13315, 13319, 13519] +Linked objects: Hole (49549), Hole (49550), Hole (49551), Hole (49552), Hole (49553), Hole (49554), Hole (54294), Hole (54295) + +Base: 2754 +Linked animations: [13515, 13516, 13517, 13534, 13535, 13536] +Linked NPCs: Barrel (11072), Barrel (11073), Barrel (11074), Barrel (11075) + +Base: 2755 +Linked animations: [13781, 13782, 13783, 13784] +Linked objects: Tile (49634), Tile (49635), Tile (49636), Tile (49637), Green tile (49638), Green tile (49639), Green tile (49640), Yellow tile (49641), Yellow tile (49642), Yellow tile (49643), Green tile (54065), Yellow tile (54066) + +Base: 2756 +Linked animations: [13069, 13070, 13071, 13072] +Linked NPCs: Monolith (10978), Monolith (10979), Monolith (10980), Monolith (12176) + +Base: 2757 +Linked animations: [13641, 13642, 13643] + +Base: 2758 +Linked animations: [13722] + +Base: 2759 +Linked animations: [13723] +Linked objects: Fountain (49355), Fountain (49357), Fountain (49359), Fountain (54411) + +Base: 2760 +Linked animations: [13224] +Linked objects: Furnace (50191), Furnace (50192), Furnace (50193), Furnace (54884) + +Base: 2761 +Linked animations: [13414] +Linked objects: Skillcape stand (50547) + +Base: 2762 +Linked animations: [13730] + +Base: 2763 +Linked animations: [13545, 13546, 13588] +Linked graphics: 2411, 2412 +Linked objects: Locked door (50287), Locked door (50288), Locked door (50289), Locked door (53956) + +Base: 2764 +Linked animations: [13571, 13572] + +Base: 2765 +Linked animations: [13561, 13562, 13563] +Linked objects: Flammable debris (50314), Flammable debris (50315), Flammable debris (50316), Flammable debris (53965) + +Base: 2766 +Linked animations: [13578, 13579, 13580, 13581] +Linked objects: Liquid lock door (50335), Liquid lock door (50336), Liquid lock door (50337), Door (50338), Door (50339), Door (50340), Liquid lock door (53972), Door (53973) + +Base: 2767 +Linked animations: [13549, 13550, 13551, 13586, 13594] +Linked objects: Orange triangle door (50208), Orange diamond door (50209), Orange rectangle door (50210), Orange pentagon door (50211), Orange corner door (50212), Orange crescent door (50213), Orange wedge door (50214), Orange shield door (50215), Silver triangle door (50216), Silver diamond door (50217), Silver rectangle door (50218), Silver pentagon door (50219), Silver corner door (50220), Silver crescent door (50221), Silver wedge door (50222), Silver shield door (50223), Yellow triangle door (50224), Yellow diamond door (50225), Yellow rectangle door (50226), Yellow pentagon door (50227), Yellow corner door (50228), Yellow crescent door (50229), Yellow wedge door (50230), Yellow shield door (50231), Green triangle door (50232), Green diamond door (50233), Green rectangle door (50234), Green pentagon door (50235), Green corner door (50236), Green crescent door (50237), Green wedge door (50238), Green shield door (50239), Blue triangle door (50240), Blue diamond door (50241), Blue rectangle door (50242), Blue pentagon door (50243), Blue corner door (50244), Blue crescent door (50245), Blue wedge door (50246), Blue shield door (50247), Purple triangle door (50248), Purple diamond door (50249), Purple rectangle door (50250), Purple pentagon door (50251), Purple corner door (50252), Purple crescent door (50253), Purple wedge door (50254), Purple shield door (50255), Crimson triangle door (50256), Crimson diamond door (50257), Crimson rectangle door (50258), Crimson pentagon door (50259), Crimson corner door (50260), Crimson crescent door (50261), Crimson wedge door (50262), Crimson shield door (50263), Gold triangle door (50264), Gold diamond door (50265), Gold rectangle door (50266), Gold pentagon door (50267), Gold corner door (50268), Gold crescent door (50269), Gold wedge door (50270), Gold shield door (50271), Magical barrier (50329), Magical barrier (50330), Magical barrier (50331), Magical barrier (53970) + +Base: 2768 +Linked animations: [13558, 13559] +Linked objects: Pile of rocks (50305), Pile of rocks (50306), Pile of rocks (50307), Pile of rocks (53962) + +Base: 2769 +Linked animations: [13555, 13556, 13557] +Linked objects: Dark spirit (50332), Dark spirit (50333), Dark spirit (50334), Dark spirit (53971) + +Base: 2770 +Linked animations: [13552, 13553, 13554] +Linked objects: Runed door (50278), Runed door (50279), Runed door (50280), Runed door (53953) + +Base: 2771 +Linked animations: [13566, 13567, 13587] +Linked objects: Barred door (50272), Barred door (50273), Barred door (50274), Door (50275), Door (50276), Door (50277), Barred door (53951), Door (53952) + +Base: 2772 +Linked animations: [13374, 13574, 13575, 13576, 13577, 13591, 13592] +Linked objects: Suspicious grooves (49442), Padlocked door (50293), Padlocked door (50294), Padlocked door (50295), Padlocked door (53958) + +Base: 2773 +Linked animations: [13582, 13583] +Linked objects: Wooden barricade (50317), Wooden barricade (50318), Wooden barricade (50319), Wooden barricade (53966) + +Base: 2774 +Linked animations: [13712, 13713, 13714, 13715, 13716, 13717, 13718, 13719, 13720, 13721] +Linked NPCs: Gluttonous behemoth (9948), Gluttonous behemoth (9949), Gluttonous behemoth (9950), Gluttonous behemoth (9951), Gluttonous behemoth (9952), Gluttonous behemoth (9953), Gluttonous behemoth (9954), Gluttonous behemoth (9955), Gluttonous behemoth (9956), Gluttonous behemoth (9957), Gluttonous behemoth (9958), Gluttonous behemoth (9959), Gluttonous behemoth (9960), Gluttonous behemoth (9961), Gluttonous behemoth (9962), Gluttonous behemoth (9963), Gluttonous behemoth (9964) + +Base: 2775 +Linked animations: [12998, 12999, 13000, 13001, 13002, 13003, 13004, 13005, 13006, 13007] +Linked NPCs: Bulwark beast (10073), Bulwark beast (10074), Bulwark beast (10075), Bulwark beast (10076), Bulwark beast (10077), Bulwark beast (10078), Bulwark beast (10079), Bulwark beast (10080), Bulwark beast (10081), Bulwark beast (10082), Bulwark beast (10083), Bulwark beast (10084), Bulwark beast (10085), Bulwark beast (10086), Bulwark beast (10087), Bulwark beast (10088), Bulwark beast (10089), Bulwark beast (10090), Bulwark beast (10091), Bulwark beast (10092), Bulwark beast (10093), Bulwark beast (10094), Bulwark beast (10095), Bulwark beast (10096), Bulwark beast (10097), Bulwark beast (10098), Bulwark beast (10099), Bulwark beast (10100), Bulwark beast (10101), Bulwark beast (10102), Bulwark beast (10103), Bulwark beast (10104), Bulwark beast (10105), Bulwark beast (10106), Bulwark beast (10107), Bulwark beast (10108), Bulwark beast (10109), Bulwark beast (10110), Behemoth (11244) + +Base: 2776 +Linked animations: [13073] +Linked graphics: 2383 + +Base: 2777 +Linked animations: [13487, 13488, 13489, 13490, 13491, 13492] +Linked NPCs: Guardian sphere (11009) +Linked graphics: 2435, 2436, 2437 + +Base: 2778 +Linked animations: [13445, 13446, 13447, 13448, 13449, 13450, 13451, 13459, 13460, 13461, 13507, 13508, 13509, 13510, 13511] +Linked NPCs: Stomp (9782), Stomp (9783), Stomp (9784), Stomp (9785), Stomp (9786), Stomp (9787), Stomp (9788), Stomp (9789), Stomp (9790), Stomp (9791), Stomp (9792), Stomp (9793), Stomp (9794), Stomp (9795), Stomp (9796), Stomp (9797), Stomp (9798), Stomp (9799), Stomp (9800), Stomp (9801), Stomp (9802), Stomp (9803), Stomp (9804), Stomp (9805), Stomp (9806), Stomp (9807), Stomp (9808), Stomp (9809), Stomp (9810), Stomp (9811), Stomp (9812), Stomp (9813), Stomp (9814), Stomp (9815), Stomp (9816), Stomp (9817), Stomp (9818), Stomp (9819), Stomp (9820), Stomp (9821), Stomp (9822), Stomp (9823), Stomp (9824), Stomp (9825), Stomp (9826), Stomp (9827), Stomp (9828), Stomp (9829), Stomp (9830), Stomp (9831), Stomp (9832), Stomp (9833), Stomp (9834), Stomp (9835), Stomp (9836), Stomp (9837), Stomp (9838), Stomp (9839), Stomp (9840), Stomp (9841) + +Base: 2779 +Linked animations: [13255, 13256, 13257, 13258, 13259, 13260, 13261, 13262, 13264, 13349] +Linked NPCs: Protomastyx (11086), Submastyx (11087), Paramastyx (11088), Archaemastyx (11089), Dromomastyx (11090), Spinomastyx (11091), Gallimastyx (11092), Stegomastyx (11093), Megamastyx (11094), Tyrannomastyx (11095), Protomastyx corpse (11096), Submastyx corpse (11097), Paramastyx corpse (11098), Archaemastyx corpse (11099), Dromomastyx corpse (11100), Spinomastyx corpse (11101), Gallimastyx corpse (11102), Stegomastyx corpse (11103), Megamastyx corpse (11104), Tyrannomastyx corpse (11105), Bovimastyx (11230) + +Base: 2780 +Linked animations: [13158, 13159, 13160, 13161, 13162, 13163, 13164] +Linked NPCs: Iron dragon (10776), Iron dragon (10777), Iron dragon (10778), Iron dragon (10779), Iron dragon (10780), Iron dragon (10781) + +Base: 2781 +Linked animations: [13151, 13152, 13153, 13154, 13155, 13156, 13157] +Linked NPCs: Frost dragon (51), Black dragon (10219), Black dragon (10220), Black dragon (10221), Black dragon (10222), Black dragon (10223), Black dragon (10224), Green dragon (10604), Green dragon (10605), Green dragon (10606), Green dragon (10607), Green dragon (10608), Green dragon (10609), Frost dragon (10770), Frost dragon (10771), Frost dragon (10772), Frost dragon (10773), Frost dragon (10774), Frost dragon (10775), Red dragon (10815), Red dragon (10816), Red dragon (10817), Red dragon (10818), Red dragon (10819), Red dragon (10820), Frost dragon (11633), Frost dragon (11634), Frost dragon (11635), Frost dragon (11636) + +Base: 2782 +Linked animations: [13731, 13732, 13733, 13734, 13735, 13738, 13739] +Linked NPCs: Edimmu (10704) + +Base: 2783 +Linked animations: [13057, 13058, 13059, 13060, 13061, 13062, 13063, 13064, 13167, 13168, 13169, 13170, 13171, 13172, 13175, 13176, 13415, 13416, 13639, 13696, 13697, 13698, 13699, 13700, 13701, 13702, 13703, 13704, 13705, 13706, 13707, 14620] +Linked NPCs: Rammernaut (9767), Rammernaut (9768), Rammernaut (9769), Rammernaut (9770), Rammernaut (9771), Rammernaut (9772), Rammernaut (9773), Rammernaut (9774), Rammernaut (9775), Rammernaut (9776), Rammernaut (9777), Rammernaut (9778), Rammernaut (9779), Rammernaut (9780), Unholy cursebearer (10111), Unholy cursebearer (10112), Unholy cursebearer (10113), Unholy cursebearer (10114), Unholy cursebearer (10115), Unholy cursebearer (10116), Unholy cursebearer (10117), Unholy cursebearer (10118), Unholy cursebearer (10119), Unholy cursebearer (10120), Unholy cursebearer (10121), Unholy cursebearer (10122), Unholy cursebearer (10123), Unholy cursebearer (10124), Unholy cursebearer (10125), Unholy cursebearer (10126), Unholy cursebearer (10127), Brute (10797), Brute (10798), Brute (10799), Brute (10800), Brute (10801), Brute (10802), Brute (10803), Brute (10804), Brute (10805), Brute (10806), Brute (10807), Brute (10808), Brute (10809), Brute (10810), Brute (10811), Brute (10812), Brute (10813), Brute (10814), Guard (10915), Guard (10916), Guard (10917), Guard (10918), Guard (10919), Guard (10920), Guard (10921), Guard (10922), Guard (10923), Guard (10924), Guard (10925), Guard (10926), Guard (10927), Guard (10928), Guard (10929), Guard (10930), Guard (10931), Guard (10932), Magic construct (11006), Guardian (11231), Brute (11441), Brute (11442), Brute (11443), Brute (11444), Brute (11445), Brute (11446), Brute (11447), Brute (11448), Brute (11449) +Linked graphics: 2780 + +Base: 2784 +Linked animations: [13198, 13200, 13202, 13203, 13206, 13207, 13208, 13209, 13213, 13214, 13289, 13290, 13613, 13614, 13615, 13617, 13618, 13619, 13620, 13665, 13666, 13667, 13668, 13669, 13670, 13671, 13672, 13673, 13674, 13675, 13676, 13677, 13678, 13679, 13680, 13681, 13682, 13683, 13684, 13685] +Linked NPCs: Divine skinweaver (10058), Enigmatic hoardstalker (11011), Cub bloodrager (11106), Cub bloodrager (11107), Little bloodrager (11108), Little bloodrager (11109), Na￯ve bloodrager (11110), Na￯ve bloodrager (11111), Keen bloodrager (11112), Keen bloodrager (11113), Brave bloodrager (11114), Brave bloodrager (11115), Brah bloodrager (11116), Brah bloodrager (11117), Naabe bloodrager (11118), Naabe bloodrager (11119), Wise bloodrager (11120), Wise bloodrager (11121), Adept bloodrager (11122), Adept bloodrager (11123), Sachem bloodrager (11124), Sachem bloodrager (11125), Cub stormbringer (11126), Cub stormbringer (11127), Little stormbringer (11128), Little stormbringer (11129), Na￯ve stormbringer (11130), Na￯ve stormbringer (11131), Keen stormbringer (11132), Keen stormbringer (11133), Brave stormbringer (11134), Brave stormbringer (11135), Brah stormbringer (11136), Brah stormbringer (11137), Naabe stormbringer (11138), Naabe stormbringer (11139), Wise stormbringer (11140), Wise stormbringer (11141), Adept stormbringer (11142), Adept stormbringer (11143), Sachem stormbringer (11144), Sachem stormbringer (11145), Cub hoardstalker (11146), Cub hoardstalker (11147), Little hoardstalker (11148), Little hoardstalker (11149), Na￯ve hoardstalker (11150), Na￯ve hoardstalker (11151), Keen hoardstalker (11152), Keen hoardstalker (11153), Brave hoardstalker (11154), Brave hoardstalker (11155), Brah hoardstalker (11156), Brah hoardstalker (11157), Naabe hoardstalker (11158), Naabe hoardstalker (11159), Wise hoardstalker (11160), Wise hoardstalker (11161), Adept hoardstalker (11162), Adept hoardstalker (11163), Sachem hoardstalker (11164), Sachem hoardstalker (11165), Cub skinweaver (11166), Cub skinweaver (11167), Little skinweaver (11168), Little skinweaver (11169), Na￯ve skinweaver (11170), Na￯ve skinweaver (11171), Keen skinweaver (11172), Keen skinweaver (11173), Brave skinweaver (11174), Brave skinweaver (11175), Brah skinweaver (11176), Brah skinweaver (11177), Naabe skinweaver (11178), Naabe skinweaver (11179), Wise skinweaver (11180), Wise skinweaver (11181), Adept skinweaver (11182), Adept skinweaver (11183), Sachem skinweaver (11184), Sachem skinweaver (11185), Cub worldbearer (11186), Cub worldbearer (11187), Little worldbearer (11188), Little worldbearer (11189), Na￯ve worldbearer (11190), Na￯ve worldbearer (11191), Keen worldbearer (11192), Keen worldbearer (11193), Brave worldbearer (11194), Brave worldbearer (11195), Brah worldbearer (11196), Brah worldbearer (11197), Naabe worldbearer (11198), Naabe worldbearer (11199), Wise worldbearer (11200), Wise worldbearer (11201), Adept worldbearer (11202), Adept worldbearer (11203), Sachem worldbearer (11204), Sachem worldbearer (11205), Cub deathslinger (11206), Cub deathslinger (11207), Little deathslinger (11208), Little deathslinger (11209), Na￯ve deathslinger (11210), Na￯ve deathslinger (11211), Keen deathslinger (11212), Keen deathslinger (11213), Brave deathslinger (11214), Brave deathslinger (11215), Brah deathslinger (11216), Brah deathslinger (11217), Naabe deathslinger (11218), Naabe deathslinger (11219), Wise deathslinger (11220), Wise deathslinger (11221), Adept deathslinger (11222), Adept deathslinger (11223), Sachem deathslinger (11224), Sachem deathslinger (11225) +Linked objects: Ramokee exile (50326), Ramokee exile (50327), Ramokee exile (50328), Ramokee exile (53969) + +Base: 2785 +Linked animations: [13302, 13303, 13304, 13305, 13306, 13307, 13308, 13309, 13310, 13316, 13317, 13318, 13518, 13797, 14517, 14518] +Linked NPCs: Ferret (11007), Ferret (11010), Ferret (12157), Ferret (12158), Ferret (12159), Ferret (12160), Ferret (12161), Ferret (12165), Ferret (12166), Ferret (12167), Ferret (12168), Ferret (12169), Ferret (12170), Ferret (12171), Ferret (12172), Ferret (12173), Ferret (12174) + +Base: 2786 +Linked animations: [13333, 13334, 13335, 13336, 13337, 13338, 13352] +Linked NPCs: Luminescent icefiend (9912), Luminescent icefiend (9913), Luminescent icefiend (9914), Luminescent icefiend (9915), Luminescent icefiend (9916), Luminescent icefiend (9917), Luminescent icefiend (9918), Luminescent icefiend (9919), Luminescent icefiend (9920), Luminescent icefiend (9921), Luminescent icefiend (9922), Luminescent icefiend (9923), Luminescent icefiend (9924), Luminescent icefiend (9925), Luminescent icefiend (9926), Luminescent icefiend (9927), Luminescent icefiend (9928) + +Base: 2787 +Linked animations: [13465, 13466, 13467, 13468, 13469, 13470, 13471, 13472, 13499, 13500] +Linked NPCs: Lexicus Runewright (9842), Lexicus Runewright (9843), Lexicus Runewright (9844), Lexicus Runewright (9845), Lexicus Runewright (9846), Lexicus Runewright (9847), Lexicus Runewright (9848), Lexicus Runewright (9849), Lexicus Runewright (9850), Lexicus Runewright (9851), Lexicus Runewright (9852), Lexicus Runewright (9853), Lexicus Runewright (9854), Lexicus Runewright (9855) + +Base: 2788 +Linked animations: [13476, 13477, 13478, 13479, 13480, 13481, 13482, 13483, 13484, 13495, 13501, 13502, 13503, 13506, 14435, 14436, 14437, 14439, 14440, 14441] +Linked NPCs: Tome of Strength (9856), Tome of Ranging (9857), Tome of Magic (9858), Tome of Strength (9859), Tome of Ranging (9860), Tome of Magic (9861), Tome of Strength (9862), Tome of Ranging (9863), Tome of Magic (9864), Tome of Strength (9865), Tome of Ranging (9866), Tome of Magic (9867), Tome of Strength (9868), Tome of Ranging (9869), Tome of Magic (9870), Tome of Strength (9871), Tome of Ranging (9872), Tome of Magic (9873), Tome of Strength (9874), Tome of Ranging (9875), Tome of Magic (9876), Tome of Strength (9877), Tome of Ranging (9878), Tome of Magic (9879), Tome of Strength (9880), Tome of Ranging (9881), Tome of Magic (9882), Tome of Strength (9883), Tome of Ranging (9884), Tome of Magic (9885), Tome of Strength (9886), Tome of Ranging (9887), Tome of Magic (9888), Tome of Strength (9889), Tome of Ranging (9890), Tome of Magic (9891), Tome of Strength (9892), Tome of Ranging (9893), Tome of Magic (9894), Tome of Strength (9895), Tome of Ranging (9896), Tome of Magic (9897) +Linked graphics: 139, 140, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 2421, 2422, 2423 + +Base: 2789 +Linked animations: [13291, 13292, 13293, 13294, 13295, 13296, 13297] +Linked NPCs: Seeker spawn (10933), Seeker spawn (10934), Seeker spawn (10935), Seeker spawn (10936), Seeker spawn (10937), Seeker spawn (10938), Seeker spawn (10939), Seeker spawn (10940) + +Base: 2790 +Linked animations: [13298, 13299, 13767, 13768, 13769, 13770, 13771, 13772, 13773, 13774, 13775, 13779] +Linked NPCs: Plane-freezer Lakhrahnaz (9929), Plane-freezer Lakhrahnaz (9930), Plane-freezer Lakhrahnaz (9931), Plane-freezer Lakhrahnaz (9932), Plane-freezer Lakhrahnaz (9933), Plane-freezer Lakhrahnaz (9934), Plane-freezer Lakhrahnaz (9935), Plane-freezer Lakhrahnaz (9936), Plane-freezer Lakhrahnaz (9937), Plane-freezer Lakhrahnaz (9938), Plane-freezer Lakhrahnaz (9939), Plane-freezer Lakhrahnaz (9940), Plane-freezer Lakhrahnaz (9941), Plane-freezer Lakhrahnaz (9942), Plane-freezer Lakhrahnaz (9943), Plane-freezer Lakhrahnaz (9944), Plane-freezer Lakhrahnaz (9945), Plane-freezer Lakhrahnaz (9946), Plane-freezer Lakhrahnaz (9947), Seeker (10701), Soulgazer (10705), Seeker sentinel (10941) + +Base: 2791 +Linked animations: [14468, 14469, 14470, 14471, 14472] +Linked NPCs: Flesh-Spoiler Haasghenahk (11895), Flesh-Spoiler Haasghenahk (11896), Flesh-Spoiler Haasghenahk (11897), Flesh-Spoiler Haasghenahk (11898), Flesh-Spoiler Haasghenahk (11899), Flesh-Spoiler Haasghenahk (11900), Flesh-Spoiler Haasghenahk (11901), Flesh-Spoiler Haasghenahk (11902), Flesh-Spoiler Haasghenahk (11903), Flesh-Spoiler Haasghenahk (11904), Flesh-Spoiler Haasghenahk (11905), Flesh-Spoiler Haasghenahk (11906), Flesh-Spoiler Haasghenahk (11907), Flesh-Spoiler Haasghenahk (11908), Flesh-Spoiler Haasghenahk (11909) + +Base: 2792 +Linked animations: [13320, 14463, 14464, 14465, 14466, 14467, 14483, 14484, 14485] +Linked NPCs: Flesh-Spoiler Haasghenahk (11925), Flesh-Spoiler Haasghenahk (11926), Flesh-Spoiler Haasghenahk (11927), Flesh-Spoiler Haasghenahk (11928), Flesh-Spoiler Haasghenahk (11929), Flesh-Spoiler Haasghenahk (11930), Flesh-Spoiler Haasghenahk (11931), Flesh-Spoiler Haasghenahk (11932), Flesh-Spoiler Haasghenahk (11933), Flesh-Spoiler Haasghenahk (11934), Flesh-Spoiler Haasghenahk (11935), Flesh-Spoiler Haasghenahk (11936), Flesh-Spoiler Haasghenahk (11937), Flesh-Spoiler Haasghenahk (11938), Flesh-Spoiler Haasghenahk (11939) + +Base: 2793 +Linked animations: [14474, 14475, 14476, 14477, 14478, 14479, 14480, 14481, 14482, 14487, 14618] +Linked NPCs: Flesh-Spoiler spawn (11910), Flesh-Spoiler spawn (11911), Flesh-Spoiler spawn (11912), Flesh-Spoiler spawn (11913), Flesh-Spoiler spawn (11914), Flesh-Spoiler spawn (11915), Flesh-Spoiler spawn (11916), Flesh-Spoiler spawn (11917), Flesh-Spoiler spawn (11918), Flesh-Spoiler spawn (11919), Flesh-Spoiler spawn (11920), Flesh-Spoiler spawn (11921), Flesh-Spoiler spawn (11922), Flesh-Spoiler spawn (11923), Flesh-Spoiler spawn (11924) +Linked graphics: 2765, 2779 + +Base: 2794 +Linked animations: [13015, 13016, 13017, 13018, 13019, 13025, 13029, 13030, 13033, 13528] +Linked NPCs: Shadow-Forger Ihlakhizan (10143), Shadow-Forger Ihlakhizan (10144), Shadow-Forger Ihlakhizan (10145), Shadow-Forger Ihlakhizan (10146), Shadow-Forger Ihlakhizan (10147), Shadow-Forger Ihlakhizan (10148), Shadow-Forger Ihlakhizan (10149), Shadow-Forger Ihlakhizan (10150), Shadow-Forger Ihlakhizan (10151), Shadow-Forger Ihlakhizan (10152), Shadow-Forger Ihlakhizan (10153), Shadow-Forger Ihlakhizan (10154), Shadow-Forger Ihlakhizan (10155), Shadow-Forger Ihlakhizan (10156) +Linked objects: null (49299), null (49300), Pillar (51110) + +Base: 2795 +Linked animations: [13418, 13419, 13420, 13421, 13422, 13423, 13424, 13425, 13426, 13427, 13428, 13429, 13430, 13431, 13432, 13433, 13434, 13496, 13764] +Linked NPCs: Night-gazer Khighorahk (9725), Night-gazer Khighorahk (9726), Night-gazer Khighorahk (9727), Night-gazer Khighorahk (9728), Night-gazer Khighorahk (9729), Night-gazer Khighorahk (9730), Night-gazer Khighorahk (9731), Night-gazer Khighorahk (9732), Night-gazer Khighorahk (9733), Night-gazer Khighorahk (9734), Night-gazer Khighorahk (9735), Night-gazer Khighorahk (9736), Night-gazer Khighorahk (9737), Night-gazer Khighorahk (9738), Night-gazer Khighorahk (9739), Night-gazer Khighorahk (9740), Night-gazer Khighorahk (9741), Night-gazer Khighorahk (9742), Night-gazer Khighorahk (9743), Night-gazer Khighorahk (9744), Night-gazer Khighorahk (9745), Night-gazer Khighorahk (9746), Night-gazer Khighorahk (9747), Night-gazer Khighorahk (9748), Night-gazer Khighorahk (9749), Night-gazer Khighorahk (9750), Night-gazer Khighorahk (9751), Night-gazer Khighorahk (9752) + +Base: 2796 +Linked animations: [12981, 13443, 13444, 13505] +Linked graphics: 2146, 2392, 2393, 2427 + +Base: 2797 +Linked animations: [13056] +Linked graphics: 2379, 2381 + +Base: 2798 +Linked animations: [13011, 13012, 13013] +Linked graphics: 2397, 2398, 2399 + +Base: 2799 +Linked animations: [13008, 13009, 13010] +Linked graphics: 2394, 2395, 2396 + +Base: 2800 +Linked animations: [13014, 13485, 13486, 13520, 13521, 13522, 13523, 13524, 13525, 13526, 13570, 13638] +Linked graphics: 2400, 2420, 2428, 2429, 2430, 2431, 2432, 2433, 2434, 2438, 2599 +Linked objects: Debris (49268) + +Base: 2801 +Linked animations: [13728, 13729] +Linked graphics: 2612, 2613 + +Base: 2802 +Linked animations: [13455, 13456, 13457, 13458, 13497, 13498, 14434] +Linked graphics: 2404, 2405, 2406, 2407, 2413, 2414, 2415, 2416, 2417, 2772, 2773, 2774, 2775 + +Base: 2803 +Linked animations: [13452, 13453, 13454] +Linked graphics: 2401, 2402, 2403 + +Base: 2804 +Linked animations: [13263, 13265, 13311] +Linked graphics: 2551, 2552, 2553, 2554, 2555, 2556, 2557, 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570 + +Base: 2805 +Linked animations: [13688, 13689] +Linked graphics: 2577, 2578 + +Base: 2806 +Linked animations: [13776, 13777, 13778] +Linked graphics: 2574, 2575, 2576 + +Base: 2807 +Linked animations: [13653, 13655] +Linked graphics: 2602, 2603 + +Base: 2808 +Linked animations: [13609, 13610, 14451] +Linked graphics: 132, 133, 2587, 2588 + +Base: 2809 +Linked animations: [13607, 13608] +Linked graphics: 2579, 2580 + +Base: 2810 +Linked animations: [13690, 13780] +Linked graphics: 2581, 2582, 2583, 2584, 2585, 2586 + +Base: 2811 +Linked animations: [13165] +Linked graphics: 2464 + +Base: 2812 +Linked animations: [13166] +Linked graphics: 2465 + +Base: 2813 +Linked animations: [13640] +Linked graphics: 2601 + +Base: 2814 +Linked animations: [13708, 13709] +Linked graphics: 2604, 2605 + +Base: 2815 +Linked animations: [13215, 13216, 13686, 13687] +Linked graphics: 2452, 2453, 2455, 2456, 2572, 2573 + +Base: 2816 +Linked animations: [13195, 13196, 13199, 13201, 14291] +Linked graphics: 93, 2443, 2445, 2446, 2454, 2457, 2458, 2459, 2460, 2461, 2462, 2463 + +Base: 2817 +Linked animations: [13210, 13211, 13212, 13621, 13622, 13623] +Linked graphics: 2449, 2450, 2451, 2591, 2592, 2593 + +Base: 2818 +Linked animations: [13197, 13204] +Linked graphics: 2444, 2447 + +Base: 2819 +Linked animations: [13205, 13616] +Linked graphics: 2448, 2590 + +Base: 2820 +Linked animations: [13279, 13280, 13281, 13284, 13348] +Linked NPCs: Frozen adventurer (10022), Frozen adventurer (10023) +Linked graphics: 2545, 2546, 2547, 2548, 2550 + +Base: 2821 +Linked animations: [13277, 13282] +Linked graphics: 2544, 2549 + +Base: 2822 +Linked animations: [13286, 13287] +Linked graphics: 2516, 2517 + +Base: 2823 +Linked animations: [13067, 13399, 13400] +Linked graphics: 2382, 2510, 2511 + +Base: 2824 +Linked animations: [12993, 12994, 12995] +Linked graphics: 2147, 2367, 2368 + +Base: 2825 +Linked animations: [12996] +Linked graphics: 2369 + +Base: 2826 +Linked animations: [13341, 13346] +Linked graphics: 2526, 2531 + +Base: 2827 +Linked animations: [13339, 13340, 13345, 13347, 13726, 13727] +Linked graphics: 2524, 2525, 2530, 2543, 2610, 2611 + +Base: 2828 +Linked animations: [13343, 13344] +Linked graphics: 2528, 2529 + +Base: 2829 +Linked animations: [13342] +Linked graphics: 2527 + +Base: 2830 +Linked animations: [13021, 13022, 13023, 13024] +Linked graphics: 2371, 2372, 2373, 2374 + +Base: 2831 +Linked animations: [13020] +Linked graphics: 2370 + +Base: 2832 +Linked animations: [13031] +Linked graphics: 2600 + +Base: 2833 +Linked animations: [13027, 13028] +Linked graphics: 2376, 2377 + +Base: 2834 +Linked animations: [13026] +Linked graphics: 2375 + +Base: 2835 +Linked animations: [13504] +Linked graphics: 2425 + +Base: 2836 +Linked animations: [13473, 13474, 13475] +Linked graphics: 2408, 2409, 2410, 2424, 2426 + +Base: 2837 +Linked animations: [13266, 13267, 13268, 14290] +Linked graphics: 2538, 2541 + +Base: 2838 +Linked animations: [13269, 13272, 13273, 13274, 13275, 14619] +Linked graphics: 2532, 2533, 2534, 2535, 2536, 2537, 2539, 2540, 2542, 2687, 2781 + +Base: 2839 +Linked animations: [13435, 13436] +Linked graphics: 2384, 2385, 2418, 2419 + +Base: 2840 +Linked animations: [13441, 13442, 14486] +Linked graphics: 2390, 2391, 2764 + +Base: 2841 +Linked animations: [13438, 13439, 13440] +Linked graphics: 2387, 2388, 2389 + +Base: 2842 +Linked animations: [13174] +Linked graphics: 2440 + +Base: 2843 +Linked animations: [13725] +Linked graphics: 2607, 2608, 2609 + +Base: 2844 +Linked animations: [13724, 13741] +Linked graphics: 2606, 2616 + +Base: 2845 +Linked animations: [13629, 13630] +Linked graphics: 2594, 2595 + +Base: 2846 +Linked animations: [13632] +Linked graphics: 2596 + +Base: 2847 +Linked animations: [13637] +Linked graphics: 2598 + +Base: 2848 +Linked animations: [13625, 13626, 13627] +Linked objects: Stalagmite (52285), Stalagmite (52286), Stalagmite (52287) + +Base: 2849 +Linked animations: [13660] +Linked graphics: 2571 + +Base: 2850 +Linked animations: [13191, 14292, 14624] +Linked graphics: 2442, 2783 + +Base: 2851 +Linked animations: [13032] +Linked graphics: 2378 + +Base: 2852 +Linked animations: [13321, 13322, 13323, 13324] + +Base: 2853 +Linked animations: [14278, 14280] +Linked objects: Group gatestone portal (7528), Group gatestone portal (53124), Group gatestone portal (53125), Group gatestone portal (53126), null (53410), null (53414), null (53420) + +Base: 2854 +Linked animations: [13300] + +Base: 2855 +Linked animations: [13539, 14452] +Linked objects: null (54692), null (54709) + +Base: 2856 +Linked animations: [13766] + +Base: 2857 +Linked animations: [13815, 13816, 13817] +Linked objects: Ornate cuckoo clock (51661), Ornate cuckoo clock (51662) + +Base: 2858 +Linked animations: [13814] + +Base: 2859 +Linked animations: [13826, 13827] +Linked NPCs: Flagstaff of Festivities (11300) + +Base: 2860 +Linked animations: [13834, 13835] +Linked graphics: 2617, 2618, 2619, 2620, 2621, 2622 + +Base: 2861 +Linked animations: [13894] +Linked objects: Fountain (52568) + +Base: 2862 +Linked animations: [13881, 13882, 13883, 13884, 13885, 13886, 13887, 13888, 13891, 13892, 13893, 13908, 13909, 13913, 13914, 13915, 13916, 13917, 13918, 13919, 13923, 13924, 13925, 13926, 13930, 13931, 13932, 13933, 13934, 13935, 13946, 13950, 13951, 13952, 13953, 13961, 13962, 13984, 13985, 13993, 14000, 14001, 14002, 14013, 14019, 14020, 14021, 14022, 14023, 14024, 14025, 14026, 14027, 14028, 14029, 14030, 14031, 14032, 14033, 14034, 14035, 14036, 14037, 14038, 14039, 14040, 14041, 14084, 14085, 14097] +Linked NPCs: Ork (4456), Ork (4457), Ork (4458), Ork (4459), Ork (6271), Ork (6272), Ork (6273), Ork (6274), General Gromblod (11312), General Shredflesh (11314), General Shredflesh (11315), General Shredflesh (11316), General Bre'egth (11318), K'Chunk (11320), K'Chunk (11321), General Gromblod (11329), General Gromblod (11330), General Shredflesh (11331), General Bre'egth (11332), null (11333), Ivory Shredflesh (11334), Ivory Shredflesh (11335), null (11336), Ivory Gromblod (11337), Ivory Gromblod (11338), null (11339), Ivory Bre'egth (11340), Ivory Bre'egth (11341), General Gromblod (11342), General Gromblod (11343), General Shredflesh (11344), General Bre'egth (11345), Ork warrior (11348), Ork warrior (11349), Ork warrior (11350), Ork warrior (11351), Ivory Shredflesh (cursed) (11352), Ivory Gromblod (cursed) (11353), Ivory Bre'egth (cursed) (11354), Ork (11355), Ork (11356), Ork (11357), Ork (11358), Ork warrior (11359), Ork warrior (11360), Ork warrior (11361), Ork warrior (11362), Ivory Shredflesh (cursed) (11371), Ivory Gromblod (cursed) (11372), Ivory Bre'egth (cursed) (11373), Ork (11423), Ork (11424) + +Base: 2863 +Linked animations: [793, 3250, 3251, 3267, 4317, 4328, 11012, 13846, 13847, 13848, 13849, 13850, 13851, 13852, 13853, 13854, 13855, 13856, 13857, 13858, 13859, 13860, 13861, 13862, 13863, 13864, 13865, 13905, 13910, 13968, 13969, 13970, 13971, 13972, 13975, 13977, 13982, 13983, 13989, 13992, 14017, 14018, 14044, 14060, 14068, 14091] +Linked NPCs: Fairy (57), Banker (498), Fairy shopkeeper (534), Fairy shop assistant (535), Jukat (564), Lunderwin (565), Fairy (567), Fairy Queen (653), Banker (909), Fairy Very Wise (910), Chaeldar (1598), Co-ordinator (3302), Fairy Nuff (3303), Fairy Godfather (3304), Fat Rocco (3305), Slim Louie (3306), null (3314), null (3315), null (3316), null (3317), Fairy chef (3322), Treasure fairy (3954), null (4422), null (4423), null (4426), null (4427), null (4428), null (4429), null (4430), null (4431), null (4432), Fairy Godfather (4433), Fairy Nuff (4434), null (4435), null (4436), Fairy Queen (4437), Fairy Very Wise (4442), Fairy (4443), Fairy (4444), Fairy (4445), Fairy (4446), Fairy Fixit (4455), Fairy (5165), Fairy Aeryka (6072), Circus barker (8079), Circus barker (8080), Circus barker (8081), Agility assistant (8082), Magic assistant (8083), Ranged assistant (8084), Agility assistant (8726), Magic assistant (8727), Ranged assistant (8728), Fairy Godfather (11323), Fairy Godfather (11324), Fairy Godfather (11325), Fairy Godfather (11326), Fairy Godfather (11327), Fairy Godfather (11328), Fairy Godfather (11346), Fairy Godfather (11347), Slim Louie (11363), Fat Rocco (11364), Fairy tooth hunter (11366), Fairy stitcher (11367), Fairy stitcher (11368), Fairy doctor (11369), Fairy tooth farmer (11370), Tooth Fairy (11376), null (11379), Fairy Queen (11380), Fairy Queen (11381), Fairy builder (11382), null (11415), null (11416), null (11417), null (11418), null (11419), null (11420), null (11421), null (11422), Fairy (11427), Fairy (11428), Fairy (11429), Fairy (11430), Fairy (11431), Fairy (11432), Fairy (11433), Fairy (11434), Fairy Very Wise (11435), Fairy Queen (11436) +Linked graphics: 2658 + +Base: 2864 +Linked animations: [13895, 13896] +Linked NPCs: Gatekeeper (3307), null (3318), Gatekeeper (3321) + +Base: 2865 +Linked animations: [14092, 14093, 14094, 14095, 14096] +Linked NPCs: Otherworldly being (126), Irksol (566) + +Base: 2866 +Linked animations: [13866, 13867, 13868, 13869, 13870, 13871, 13872, 13873, 13874, 13875, 13876, 13877, 13878, 13879, 13880] +Linked NPCs: Tanglefoot (3313), Baby tanglefoot (3319), Baby tanglefoot (3320) + +Base: 2867 +Linked animations: [13927, 13928, 13929, 13997, 13998, 13999] +Linked graphics: 2626, 2627, 2628, 2643, 2644, 2645 + +Base: 2868 +Linked animations: [13978, 13979] +Linked graphics: 2655, 2656, 2657 + +Base: 2869 +Linked animations: [13920, 13921, 13922, 13994, 13995, 13996] +Linked graphics: 2623, 2624, 2625, 2640, 2641, 2642 + +Base: 2870 +Linked animations: [13960, 13966, 13967] + +Base: 2871 +Linked animations: [13958, 13959, 13963, 13964] + +Base: 2872 +Linked animations: [13965, 14083] + +Base: 2873 +Linked animations: [13939] + +Base: 2874 +Linked animations: [13940] + +Base: 2875 +Linked animations: [13954, 13955, 13956, 14012] +Linked objects: null (52646), null (52647), null (52648), Projector screen (52650), Projector screen (52651) + +Base: 2876 +Linked animations: [14048, 14049, 14050, 14051, 14053, 14054, 14058] +Linked objects: Rift (52733) + +Base: 2877 +Linked animations: [14059] +Linked objects: null (52732) + +Base: 2878 +Linked animations: [14088] +Linked objects: null (52737) + +Base: 2879 +Linked animations: [14052, 14055, 14056, 14057] +Linked objects: null (52745), null (52746), null (52747), null (52748), null (52749), null (52750), null (52751), null (52752), null (52753), null (52754) + +Base: 2880 +Linked animations: [13973] + +Base: 2881 +Linked animations: [13906, 13907] +Linked NPCs: Extractor fairies (11322) + +Base: 2882 +Linked animations: [13900, 13901, 13902, 13903, 13904, 14011, 14065] +Linked NPCs: Gnarly (11309), Gnarly (11310), Gnarly (11409), Gnarly (11410), Milk tooth creature (11411), Milk tooth creature (11412), Tooth creature (healthy) (11413), Tooth creature (decayed) (11414), Wisdom tooth creature (healthy) (11425), Wisdom tooth creature (decayed) (11426) + +Base: 2883 +Linked animations: [13990, 13991, 14008, 14009] +Linked graphics: 2637, 2638, 2639 + +Base: 2884 +Linked animations: [14062] +Linked graphics: 2659 + +Base: 2885 +Linked animations: [14064] +Linked graphics: 2660 + +Base: 2886 +Linked animations: [13911, 13912, 14003] +Linked graphics: 6, 7, 2646 + +Base: 2887 +Linked animations: [14069, 14070, 14071, 14072, 14073, 14086] +Linked graphics: 2669 +Linked objects: Medical table (52701), Wounded fairy (52702) + +Base: 2888 +Linked animations: [3252, 3253] +Linked graphics: 2670, 2671 + +Base: 2889 +Linked animations: [14045] +Linked graphics: 2652 + +Base: 2890 +Linked animations: [13947, 13949, 14014, 14015, 14016] +Linked graphics: 2629, 2630, 2647, 2648, 2649 + +Base: 2891 +Linked animations: [14042, 14043] +Linked graphics: 2650, 2651 + +Base: 2892 +Linked animations: [14046, 14047] +Linked graphics: 2653, 2654 + +Base: 2893 +Linked animations: [14087] +Linked graphics: 189 + +Base: 2894 +Linked animations: [14067] +Linked graphics: 2663, 2664, 2665, 2666, 2667, 2668 + +Base: 2895 +Linked animations: [13986, 13987, 13988] +Linked graphics: 2634, 2635, 2636 + +Base: 2896 +Linked animations: [14066] +Linked graphics: 2661, 2662 + +Base: 2897 +Linked animations: [13897] + +Base: 2898 +Linked animations: [13898] + +Base: 2899 +Linked animations: [14074, 14075, 14076, 14077, 14078] + +Base: 2900 +Linked animations: [13957, 13981] +Linked objects: Rift (52676) + +Base: 2901 +Linked animations: [14099, 14100] +Linked objects: Low mechanism slider (12027), Low mechanism slider (12028), Mid mechanism slider (12037), Mid mechanism slider (12040), Tall mechanism slider (12044), Tall mechanism slider (12065), Drive mechanism (12070), Mechanism (12073), Mechanism (12075), Mechanism (12077), Mechanism (12079), Mechanism (12081) + +Base: 2902 +Linked animations: [14121, 14122, 14129, 14130, 14131, 14132, 14133, 14134, 14135, 14136, 14137, 14138, 14139, 14140, 14141, 14142, 14143, 14144, 14145, 14146, 14147, 14148] +Linked objects: Counter (14158), Counter (16182), Counter (16183), Counter (16188), Counter (16198), Counter (16199), Counter (16204), Counter (16205), Counter (16206), Counter (16207), Counter (16208), Counter (16209), Counter (16279), Counter (16281), Counter (16285), Counter (16287), Counter (16295), Counter (16296), Counter (16308), Counter (16309), Counter (16310), Counter (16311), Counter (16313), Counter (16314), Counter (16323), Counter (16324), Counter (16325), Counter (16326), Counter (16327), Counter (16328), Counter (16329), Counter (16330), Counter (16332), Counter (16333), Counter (16334), Counter (16335), Counter (16336), Counter (16337), Counter (16338), Counter (16339), Counter (16340), Counter (16341) + +Base: 2903 +Linked animations: [1038] + +Base: 2904 +Linked animations: [14104, 14105, 14106, 14112, 14113, 14114, 14115, 14116, 14117, 14123, 14124] +Linked objects: Machine (12109), Machine (14124), Machine (14139), Powered machine (14157) + +Base: 2905 +Linked animations: [14101, 14102] + +Base: 2906 +Linked animations: [14107, 14108, 14109, 14118, 14119, 14120] +Linked graphics: 2672, 2673, 2674, 2680, 2681, 2682 + +Base: 2907 +Linked animations: [14110, 14111] +Linked graphics: 2675, 2676, 2677, 2678, 2679 + +Base: 2908 +Linked animations: [1656, 2129] +Linked objects: Catapult (4381), Catapult (4382), Catapult (4385), Catapult (4386) + +Base: 2909 +Linked animations: [4891, 4911] +Linked graphics: 303, 2690 + +Base: 2910 +Linked animations: [4910] +Linked graphics: 2688, 2689 + +Base: 2911 +Linked animations: [14178] +Linked graphics: 2692 + +Base: 2912 +Linked animations: [14187, 14188] +Linked objects: Memory energy (4477), Memory energy (4478), Memory energy (4479) + +Base: 2913 +Linked animations: [14202, 14203, 14204, 14205, 14206] +Linked objects: Toll booth (4453), Toll booth (36471), Toll booth (36472) + +Base: 2914 + +Base: 2915 +Linked animations: [14201] +Linked graphics: 2698 + +Base: 2916 +Linked animations: [14208] + +Base: 2917 +Linked animations: [14215, 14216, 14219] +Linked graphics: 2713, 2714, 2715, 2716, 2717, 2718, 2719, 2720, 2721, 2722, 2723, 2724, 2725, 2726, 2727 + +Base: 2918 +Linked animations: [2792, 14210, 14217, 14623] +Linked graphics: 2728, 2729, 2730, 2731, 2732, 2733, 2734, 2735, 2736, 2737, 2738, 2739, 2740, 2741, 2782 + +Base: 2919 +Linked animations: [14211] +Linked graphics: 2701, 2702 + +Base: 2920 +Linked animations: [10562, 14214] +Linked graphics: 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2710, 2711, 2712 + +Base: 2921 +Linked animations: [14225, 14226, 14227, 14229] +Linked graphics: 2742 +Linked objects: Minor pit trap (39266), Minor pit trap (39267), Major pit trap (39268), Major pit trap (39269), Superior pit trap (39270), Superior pit trap (39271), null (39347), null (39348), null (41571), Fire (52767) + +Base: 2922 +Linked animations: [1641, 14230, 14231] +Linked NPCs: null (11596) + +Base: 2923 +Linked animations: [14241] +Linked objects: Mabel in a sack (52837) + +Base: 2924 +Linked animations: [14267, 14268, 14269, 14270, 14271] +Linked NPCs: Baby blue dragon (52), Baby red dragon (1589), Baby black dragon (3376), Baby blue dragon (4665), Baby blue dragon (4666), Baby red dragon (4667), Baby red dragon (4668) + +Base: 2925 +Linked animations: [14272, 14273] + +Base: 2926 +Linked animations: [3643, 4940, 4971, 14261, 14262, 14263, 14264, 14265] +Linked NPCs: Revenant dragon (6998), Revenant dragon (6999) + +Base: 2927 +Linked animations: [14274, 14275] + +Base: 2928 +Linked animations: [1643, 14246, 14247, 14248, 14249, 14250, 14251, 14252] +Linked NPCs: Bronze dragon (1590), Iron dragon (1591), Steel dragon (1592), Mithril dragon (5363), Mithril dragon (8424), Pit iron dragon (11567), Pit iron dragon (11594), Pit iron dragon (11595) +Linked objects: Pit iron dragon (39265), Pit iron dragon (41608) + +Base: 2929 +Linked animations: [14295, 14296, 14297] +Linked graphics: 95, 96, 97, 98, 99, 100, 121, 122, 123, 124, 125, 126 + +Base: 2930 +Linked animations: [14294] +Linked graphics: 94 + +Base: 2931 +Linked animations: [14303] +Linked graphics: 101, 117, 118, 119, 120 + +Base: 2932 +Linked animations: [14311, 14312, 14313, 14314, 14315, 14316, 14317] +Linked NPCs: Meerkats (11640) + +Base: 2933 +Linked animations: [14354, 14355] +Linked objects: Boxes (53695) + +Base: 2934 +Linked animations: [14343, 14345] +Linked objects: Machinery (53677), Machinery (53678) + +Base: 2935 +Linked animations: [14321, 14322, 14323, 14324, 14325, 14326, 14327, 14328, 14329, 14330, 14331, 14332, 14333, 14334, 14335, 14336] +Linked objects: Component box (53282), Furnace (53283), Furnace vent (53392), Furnace vent (53393), Furnace vent (53394), Flywheel (53395), Flywheel brake (53396), Flywheel brake (53397), Flywheel brake (53398), Sample hopper (53399), Sample reagent (solid) (53403), Sample reagent (powder) (53404), Sample reagent (liquid) (53405), Sample reagent (gaseous) (53406) + +Base: 2936 +Linked animations: [14344] +Linked objects: Pest cage (53680) + +Base: 2937 +Linked animations: [14348] +Linked objects: Cracked wall (53642), Cracked wall (53643), null (53645), null (53646) + +Base: 2938 +Linked animations: [14337] +Linked graphics: 131 + +Base: 2939 +Linked animations: [14342] +Linked graphics: 130 + +Base: 2940 +Linked animations: [14349] + +Base: 2941 +Linked animations: [14521, 14523, 14627] +Linked objects: Unfinished bridge (54009), Unfinished bridge (54010), Unfinished bridge (54011), Unfinished bridge (54012), null (54013), null (54014), null (54015), null (54016) + +Base: 2942 +Linked animations: [14559, 14562, 14563] + +Base: 2943 +Linked animations: [14561] + +Base: 2944 +Linked animations: [14516] +Linked objects: Red pressure plate (54365), Red pressure plate (54367), Red pressure plate (54369), Red pressure plate (54371), Blue pressure plate (54373), Blue pressure plate (54375), Blue pressure plate (54377), Blue pressure plate (54379), Green pressure plate (54381), Green pressure plate (54383), Green pressure plate (54385), Green pressure plate (54387), Yellow pressure plate (54389), Yellow pressure plate (54391), Yellow pressure plate (54393), Yellow pressure plate (54395), Orange pressure plate (54397), Orange pressure plate (54399), Orange pressure plate (54401), Orange pressure plate (54403) + +Base: 2945 +Linked animations: [14542, 14543] +Linked objects: Rubble (54138), Rubble (54139), Rubble (54140), Rubble (54141), Rubble (54142), Rubble (54143), Rubble (54144), Rubble (54145), null (54150), null (54151), null (54152), null (54153), null (54158), null (54159), null (54160), null (54161) + +Base: 2946 +Linked animations: [14449, 14498, 14544] +Linked objects: Pedestal (54110), Pedestal (54111), Pedestal (54112), Pedestal (54113), Pedestal (54114), Pedestal (54115), Pedestal (54116), Pedestal (54117) + +Base: 2947 +Linked animations: [14552, 14553, 14556, 14626] +Linked objects: Tightrope (54241), Tightrope (54242), Tightrope (54243), Tightrope (54244), Tightrope (54245), Tightrope (54246), Tightrope (54247), Tightrope (54248) + +Base: 2948 +Linked animations: [14514, 14515] +Linked objects: Tomb (54439), Tomb (54441), Tomb (54445), Tomb (54447), Tomb (54451), Tomb (54453), Tomb (54455) + +Base: 2949 +Linked animations: [14446, 14447, 14448] +Linked objects: Crystal of the Warrior (Active) (53977), Crystal of the Mage (Active) (53978), Crystal of the Ranger (Active) (53979), Crystal of the Warrior (Inactive) (53980), Crystal of the Mage (Inactive) (53981), Crystal of the Ranger (Inactive) (53982) + +Base: 2950 +Linked animations: [14438] + +Base: 2951 +Linked animations: [14488, 14489, 14490, 14491, 14492, 14493, 14494, 14495, 14496, 14497] +Linked objects: null (54462), null (54463), null (54464), null (54465), null (54466), null (54467), null (54468), null (54469), null (54470), null (54471), null (54472), null (54473), null (54474), null (54475), null (54476), null (54477) + +Base: 2952 +Linked animations: [14414, 14415] + +Base: 2953 +Linked animations: [14410, 14625] +Linked objects: Damaged bridge (54186), Damaged bridge (54187), Damaged bridge (54188), Damaged bridge (54189), null (54190), null (54191), null (54192), null (54193) + +Base: 2954 +Linked animations: [14528, 14529, 14530, 14531, 14532, 14533, 14534] + +Base: 2955 +Linked animations: [14570] +Linked objects: null (54722), null (54727), null (54728), null (54729), null (54730) + +Base: 2956 +Linked animations: [14453, 14454, 14455, 14456, 14457, 14458, 14459, 14460] + +Base: 2957 +Linked animations: [14420, 14421, 14422, 14423, 14424, 14425, 14426, 14427, 14428, 14429, 14430] +Linked NPCs: Runebound behemoth (11752), Runebound behemoth (11753), Runebound behemoth (11754), Runebound behemoth (11755), Runebound behemoth (11756), Runebound behemoth (11757), Runebound behemoth (11758), Runebound behemoth (11759), Runebound behemoth (11760), Runebound behemoth (11761), Runebound behemoth (11762), Runebound behemoth (11763), Runebound behemoth (11764), Runebound behemoth (11765), Runebound behemoth (11766), Runebound behemoth (11767), Runebound behemoth (11768), Runebound behemoth (11769), Runebound behemoth (11770), Runebound behemoth (11771), Runebound behemoth (11772), Runebound behemoth (11773), Runebound behemoth (11774), Runebound behemoth (11775), Runebound behemoth (11776), Runebound behemoth (11777), Runebound behemoth (11778), Runebound behemoth (11779), Runebound behemoth (11780), Runebound behemoth (11781), Runebound behemoth (11782), Runebound behemoth (11783), Runebound behemoth (11784), Runebound behemoth (11785), Runebound behemoth (11786), Runebound behemoth (11787), Runebound behemoth (11788), Runebound behemoth (11789), Runebound behemoth (11790), Runebound behemoth (11791), Runebound behemoth (11792), Runebound behemoth (11793), Runebound behemoth (11794), Runebound behemoth (11795), Runebound behemoth (11796), Runebound behemoth (11797), Runebound behemoth (11798), Runebound behemoth (11799), Runebound behemoth (11800), Runebound behemoth (11801), Runebound behemoth (11802), Runebound behemoth (11803), Runebound behemoth (11804), Runebound behemoth (11805), Runebound behemoth (11806), Runebound behemoth (11807), Runebound behemoth (11808), Runebound behemoth (11809), Runebound behemoth (11810), Runebound behemoth (11811), Runebound behemoth (11812), Runebound behemoth (11813), Runebound behemoth (11814), Runebound behemoth (11815), Runebound behemoth (11816), Runebound behemoth (11817), Runebound behemoth (11818), Runebound behemoth (11819), Runebound behemoth (11820), Runebound behemoth (11821), Runebound behemoth (11822), Runebound behemoth (11823), Runebound behemoth (11824), Runebound behemoth (11825), Runebound behemoth (11826), Runebound behemoth (11827), Runebound behemoth (11828), Runebound behemoth (11829), Runebound behemoth (11830), Runebound behemoth (11831), Runebound behemoth (11832), Runebound behemoth (11833), Runebound behemoth (11834), Runebound behemoth (11835), Runebound behemoth (11836), Runebound behemoth (11837), Runebound behemoth (11838), Runebound behemoth (11839), Runebound behemoth (11840), Runebound behemoth (11841), Runebound behemoth (11842), Runebound behemoth (11843), Runebound behemoth (11844), Runebound behemoth (11845), Runebound behemoth (11846), Runebound behemoth (11847), Runebound behemoth (11848), Runebound behemoth (11849), Runebound behemoth (11850), Runebound behemoth (11851), Runebound behemoth (11852), Runebound behemoth (11853), Runebound behemoth (11854), Runebound behemoth (11855), Runebound behemoth (11856), Runebound behemoth (11857), Runebound behemoth (11858), Runebound behemoth (11859), Runebound behemoth (11860), Runebound behemoth (11861), Runebound behemoth (11862), Runebound behemoth (11863), Runebound behemoth (11864), Runebound behemoth (11865), Runebound behemoth (11866), Runebound behemoth (11867), Runebound behemoth (11868), Runebound behemoth (11869), Runebound behemoth (11870), Runebound behemoth (11871) + +Base: 2958 +Linked animations: [14405, 14406, 14407, 14408] +Linked NPCs: Pondskater (12089), Pondskater (12090), Pondskater (12091), Pondskater (12092), Pondskater (12093) + +Base: 2959 +Linked animations: [14473] +Linked graphics: 2766 + +Base: 2960 + +Base: 2961 +Linked animations: [14511] +Linked graphics: 2750 + +Base: 2962 +Linked animations: [14512, 14513] +Linked graphics: 2751, 2752 + +Base: 2963 +Linked animations: [14510] +Linked graphics: 301 + +Base: 2964 +Linked animations: [14508] +Linked graphics: 175 + +Base: 2965 +Linked animations: [14431, 14432, 14433] +Linked graphics: 2769, 2770, 2771 + +Base: 2966 +Linked animations: [14540, 14541] + +Base: 2967 +Linked animations: [14402, 14403, 14404] + +Base: 2968 +Linked animations: [14444, 14445] +Linked graphics: 165, 166 + +Base: 2969 +Linked animations: [14411] +Linked graphics: 2776 + +Base: 2970 +Linked animations: [14389, 14391] +Linked graphics: 2767, 2768 + +Base: 2971 +Linked animations: [14526, 14527] +Linked graphics: 2754, 2755 + +Base: 2972 +Linked animations: [14535, 14536] +Linked graphics: 2756, 2757, 2758, 2759, 2760, 2761, 2762, 2763 + +Base: 2973 +Linked animations: [14369, 14370, 14371, 14372, 14373, 14374, 14375, 14376, 14377, 14378, 14379, 14380, 14381, 14382, 14383, 14384, 14385, 14390, 14392, 14393, 14395, 14396, 14397, 14398, 14412, 14416, 14417, 14418, 14442, 14443, 14450, 14525, 14537, 14538, 14539] +Linked NPCs: Yk'Lagor the Thunderous (11872), Yk'Lagor the Thunderous (11873), Yk'Lagor the Thunderous (11874), Yk'Lagor the Thunderous (11875), Yk'Lagor the Thunderous (11876), Yk'Lagor the Thunderous (11877), Yk'Lagor the Thunderous (11878), Yk'Lagor the Thunderous (11879), Yk'Lagor the Thunderous (11880), Yk'Lagor the Thunderous (11881), Yk'Lagor the Thunderous (11882), Yk'Lagor the Thunderous (11883), Yk'Lagor the Thunderous (11884), Yk'Lagor the Thunderous (11885), Yk'Lagor the Thunderous (11886) + +Base: 2974 +Linked animations: [14399, 14400, 14401] + +Base: 2975 +Linked animations: [14394] + +Base: 2976 +Linked animations: [14461, 14462] + +Base: 2977 +Linked animations: [14546, 14547, 14548] +Linked graphics: 134, 135, 136 + +Base: 2978 +Linked animations: [14551, 14558] +Linked graphics: 137, 138 + +Base: 2979 +Linked animations: [14587] + +Base: 2980 +Linked animations: [14622] +Linked graphics: 2778 + diff --git a/dumps/530/quest_prefix_internal_names.txt b/dumps/530/quest_prefix_internal_names.txt new file mode 100644 index 000000000..56424c637 --- /dev/null +++ b/dumps/530/quest_prefix_internal_names.txt @@ -0,0 +1,313 @@ +This is an exhaustive list of internal names/quests up to RS3. +530 came out Feb 2009, so check for quests up to there if you're looking for a name. +You'll notice some of the earlier reworked quests have been renamed in RS3... +follow the "pre-rework" column instead and check the RS wiki to see what it was originally. + +Year Quest Name Quest Prefix Formerly (eg pre-rework) +2001 Cook's Assistant cook + Demon Slayer dslay demonslayer or demon + The Restless Ghost priest + Sheep Shearer sheep + Shield of Arrav phoenixgang blackarmgang + Ernest the Chicken haunted + Vampyre Slayer vampire + Imp Catcher imp + Stolen Hearts ozan1 prince + Diamond in the Rough ozan2 + What's Mine Is Yours pnr doric + Witch's Potion hetty + The Knight's Sword squire + Goblin Diplomacy gobdip + Pirate's Treasure hunt + Dragon Slayer dragon +2002 Druidic Ritual druid_quest_2 druid + Lost City zanaris + Witch's House ball + Merlin's Crystal arthur + Heroes' Quest hero + Bar Crawl barcrawl + Scorpion Catcher scorpcatcher + Family Crest crest + Tribal Totem totem + Fishing Contest fishingcompo + Monk's Friend drunkmonk + Temple of Ikov ikov + Clock Tower cog + Holy Grail grail + Tree Gnome Village tree + Fight Arena arena + Hazeel Cult hazeelcult + Sheep Herder sheepherder + Plague City elena + Sea Slug seaslug + Waterfall Quest waterfall + Biohazard biohazard + Jungle Potion junglepotion + The Grand Tree grandtree +2003 Shilo Village zombiequeen + Underground Pass upass + Observatory Quest itgronigen + The Tourist Trap desertrescue + Watch Tower itwatchtower + Dwarf Cannon mcannon + Murder Mystery murder + The Dig Site itexam + Gertrude's Cat fluffs + Legends' Quest legends + Rune Mysteries ariane1 runemysteries + Rune Memories ariane2 +2004 Big Chompy Bird Hunting chompybird + Elemental Workshop I elemental_workshop + Priest in Peril priest + Nature Spirit druidspirit + Death Plateau death + Troll Stronghold troll + Tai Bwo Wannai Trio tbwt + Regicide regicide + Eadgar's Ruse eadgar + Shades of Mort'ton morttonquest + The Fremennik Trials viking + Horror from the Deep horror + Throne of Miscellania misc + Monkey Madness mm + Haunted Mine hauntedmine +2005 Troll Romance troll_love + In Search of the Myreque route + Creature of Fenkenstrain fenk + Roving Elves roving_elves + Ghosts Ahoy ahoy + One Small Favour onesmallfavour + Mountain Daughter mdaughter + Between a Rock� dwarfrock + The Feud feud + The Golem golem + Desert Treasure deserttreasure + Icthlarin's Little Helper ics + Tears of Guthix tog + Zogre Flesh Eaters zogre + The Lost Tribe lost_tribe + Giant Dwarf giantdwarf + Enter the Abyss abyss + Recruitment Drive rd + Mourning's End mourning + Forgettable Tale of a Drunken Dwarf forget + The Curse of Zaros secret_ghost + Garden of Tranquillity garden + A Tail of Two Cats twocats + Wanted! wanted + Mourning's End Part II mourning + Rum Deal deal + Shadow of the Storm agrith + Making History makinghistory + Rat Catchers ratcatch + Spirits of the Elid elid + Devious Minds devious +2006 Hand in the Sand handsand + Enakhra's Lament enakh + Cabin Fever fever + A Fairy Tale I - Growing Pains fairy + Recipe for Disaster hundred + Recipe for Disaster: Freeing the Mountain Dwahundred_dwarf + Recipe for Disaster: Freeing the Goblin Gener100goblin + Recipe for Disaster: Freeing Pirate Pete 100_pirate + Recipe for Disaster: Freeing the Lumbridge Sa100guide + Recipe for Disaster: Freeing Evil Dave hundred_dave + Recipe for Disaster: Freeing Skrach Uglogwee 100_ogre + Recipe for Disaster: Freeing Sir Amik Varze chicken + Recipe for Disaster: Freeing King Awowogei hundred_ilm + In Aid of the Myreque myreque_2 + A Soul's Bane soulbane + Rag and Bone Man rag + Swan Song swansong + Royal Trouble royal + Death to the Dorgeshuun dttd + A Fairy Tale II - Cure a Queen fairy2 + Lunar Diplomacy lunar + The Eyes of Glouphrie eyeglo + The Darkness of Hallowvale myq3 + The Slug Menace slug2 + Elemental Workshop II elemental_quest_2 + My Arm's Big Adventure myarm + Enlightened Journey zep + Eagles' Peak eaglepeak + Animal Magnetism anma +2007 Contact! contact + Lair of Tarn Razorlor lotr + Cold War peng + The Fremennik Isles fris + The General's Shadow shadow_maj + Tower of Life tol + The Great Brain Robbery brain + What Lies Below surok + The Hunt for Surok dsd + Olaf's Quest olaf + Another Slice of H.A.M. slice + Dream Mentor dream + Grim Tales grim + King's Ransom kr + The Path of Glouphrie pog + Back to my Roots roots + One Foot in the Grave handsand2 + Land of the Goblins lotg + Hopespear's Will hopespears_will +2008 Dealing with Scabaras scab + Wolf Whistle wolf_quest_2 wolf + As a First Resort afr + Catapult Construction catcon + Kennith's Concerns ken + Legacy of Seergaze myq4 + Perils of Ice Mountain icemt + TokTz-Ket-Dill dillo + Smoking Kills smki + Desert Slayer Dungeon smki_miniquest + Rocking Out rocko + Spirit of Summer sum1 + Meeting History hist + All Fired Up firedup + Summer's End sum2 + Defender of Varrock zemo + Swept Away swept + Purple Cat swept_cat + While Guthix Sleeps luc2 + Myths of the White Lands motwl +2009 In Pyre Need phoenix + The Chosen Commander chosen + Glorious Memories glomem + The Tale of the Muspah mah1 + Missing My Mummy mom2 + Hunt for Red Raktuber peng_rak + The Curse of Arrav mah3 + Fur 'n Seek rag2 + Forgiveness of a Chaos Dwarf ra3 + Within the Light wtl + The Temple at Senntisten mah4 + Blood Runs Deep evq (Epic Viking Quest) +2010 Nomad's Requiem nom + Rune Mechanics rm + Blood Pact lumbcat + Buyers and Cellars thigui (Thieves' Guild) + From Tiny Acorns caper_acorns + Lost Her Marbles caper_marbles + A Guild of Our Own caper_ourown + A Fairy Tale III - Battle at Ork's Rift ft3 + Elemental Workshop III elem_3 + Quiet Before the Swarm vkq1 + Love Story love + A Void Dance vkq2 + Gunnar's Ground romcom + The Void Stares Back vkq3 + Do No Evil apmeken +2011 King of the Dwarves ra4 + The Prisoner of Glouphrie glo3 + Elemental Workshop IV elem_4 + A Clockwork Syringe acs + Three's Company fremsaga_signature + Vengeance fremsaga_vengeance + Thok It To 'Em fremsaga_thok + Deadliest Catch deadly + Salt in the Wound slug3 + The Branches of Darkmeyer myq5 + Ritual of the Mahjarrat mah5 + One Piercing Note opn +2012 The Firemaker's Curse fmc + Let Them Eat Pie ltep + The Elder Kiln tzhaar2 + Nadir fremsaga_bilrach + Thok Your Block Off fremsaga_thok2 + Song from the Depths qbd1 + Carnillean Rising carni + Some Like It Cold peng_pow + Doric's Task I pnr_artisan_task + Doric's Task II pnr_denulth_task + Doric's Task III pnr_goblin_task + Doric's Task IV pnr_artisan_task2 + Doric's Task V pnr_gnome_task + Doric's Task VI pnr_skulgrimen_task + Doric's Task VII pnr_santiri_task + Doric's Task VIII pnr_monkey_task + Boric's Task I pnr_boric_coal_task + Boric's Task II pnr_boric_ore_task + Boric's Task III pnr_boric_guild_task + The Brink of Extinction tzhaar3 + Wandering Ga'al tzhaar3_eegg +2013 Koschei's Troubles kharshai + The World Wakes tsd (The Sleeping Dog) + Bringing Home the Bacon bacon + The Death of Chivalry owen1 spy + Birthright of the Dwarves ra5 + Missing, Presumed Death sliske1 +2014 One of a Kind ooak + Mahjarrat Memories mahmem + Fate of the Gods tpr (The Purple Rose) + A Shadow over Ashdale ash1 + The Mighty Fall tmf + Plague's End me3 (Mourning's End Part III) + Broken Home house + Heart of Stone egq (Elder God Quest) +2015 Dishonour among Thieves sliske2 + Dimension of Disaster q200 + Dimension of Disaster: Shield of Arrav q200_soa + Dimension of Disaster: Demon Slayer q200_ds + Dimension of Disaster: Defender of Varrock q200_dov + Dimension of Disaster: Curse of Arrav q200_coa + Hero's Welcome dov (Death of V) + The Light Within seren (Seren quest) + The Lord of Vampyrium myq6 + Call of the Ancestors goebie1 + Beneath Cursed Tides tutisland +2016 Nomad's Elegy nomel + Tales of the God Wars gwd2_miniquest + Benedict's World Tour nxtwt (NXT World Tour) + In Memory of the Myreque myq7_memorial + The Lost Toys myq7_lost_toys + River of Blood myq7 + Kindred Spirits sister (Barrows Sister quest) + Gower Quest gower + Tales of Nomad nomel_pq + Children of Mah fotg2 + Impressing the Locals elr0 + Flag Fall elr1_minq_flag + Head of the Family elr1_minq_head + Spiritual Enlightenment elr1_minq_slayer + Jed Hunter elr1_minq_jed + Eye for an Eye elr2_minq_cyclosis + Harbinger elr2_minq_harbinger + Tuai Leit's Own elr2_minq_tuaileit + Ghosts from the Past elr2_minq_exturtles + Damage Control elr2_minq_goshima + Final Destination elr2_minq_finale + Sliske's Endgame endgame +2017 Back to the Freezer peng_bttf + The Jack of Spades ozan3 + Crocodile Tears crondis + Our Man in the North het + 'Phite Club pharoah + Evil Dave's Big Day Out dave +2018 Pieces of Hate piratefinale + Rebuilding Edgeville mah5_edge + You Are It trail + The Needle Skips tns + Violet is Blue xmas18 +2019 Chef's Assistant cheesecake + Curse of the Black Stone elitedungeons + Desperate Times charos + Helping Laniakea dino_laniakea_fetch + Father and Son dino_hunter_master +2020 Once Upon a Slime valentines2020 + Desperate Measures dm + Sins of the Father kerapac_dnd + Raksha, the Shadow Colossus dino_boss + Violet is Blue Too vib2 +2021 The Vault of Shadows trindine + Foreshadowing out1 + Tortle Combat tortle_combat + Heartstealer heartstealer + Azzanadra's Quest azzanadra + Flashback out2 + Battle of the Monolith egwd_prerelease + City of Senntisten senntisten + Fortunes out3 + Finale out4 + Once Upon a Time in Gielinor out0 +2022 Eye of Het I duel_arena_removal + Eye of Het II duel_arena_removal_part2 diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index e0fa37628..000000000 --- a/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -org.gradle.jvmargs=-Xmx2g -Dkotlin.daemon.jvm.options=-Xmx2g \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c02..000000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index be52383ef..000000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100644 index fbd7c5158..000000000 --- a/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 5093609d5..000000000 --- a/gradlew.bat +++ /dev/null @@ -1,104 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/make-release.py b/make-release.py new file mode 100755 index 000000000..4b898b4e2 --- /dev/null +++ b/make-release.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import subprocess +import datetime +import os + + +WEB_REPO = '../2009scape.github.io' +NEWS_DIR = 'updates/_posts' +LOG_DELIMITER = ';;;;;' +DEBUG = False + + +class Tag: + def __init__(self, tag_name, last_tag): + self.tag_name = tag_name + self.last_tag = last_tag + + +def make_tag() -> Tag: + tag_name = datetime.datetime.now().strftime('%b-%d-%Y') + print('new release tag:', tag_name) + last_tag = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0']).decode('utf8').strip() + print('last release tag:', last_tag) + if not DEBUG: + subprocess.run(['git', 'tag', tag_name]) + return Tag(tag_name, last_tag) + + +def get_changelog_html(tag: Tag) -> str: + if DEBUG: + log_period = f'{tag.last_tag}..HEAD' + 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}' + print('generated changelog:', changelog_html, sep='\n') + return changelog_html + + +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 = f'{current_date}-More-changes-in-Gielinor.md' + print('news filename:', news_filename) + news_post = news_template(current_date, changelog_html) + if DEBUG: + print('generated news post:', news_post) + else: + if news_filename in os.listdir(): + raise FileExistsError(news_filename) + with open(news_filename, 'wt') as news_handle: + news_handle.write(news_post) + + +def news_template(current_date: str, changelog: str) -> str: + return f'''--- +title: More changes in Gielinor +tags: news +layout: newspost +date: {current_date} 00:00:00 +0000 +authors: "Update Bot" +excerpt: "There have been some more changes in Gielinor..." +modtype: "Bot" +avatar: avatar8fa9.gif +--- +Greetings Explorers + +There have been some more changes in Gielinor: + +{changelog} +''' + + +def main() -> None: + tag = make_tag() + make_news_post(tag) + + +if __name__ == '__main__': + main() 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 new file mode 100755 index 000000000..a854c3200 --- /dev/null +++ b/run @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +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 + +FORCE_REBUILD=0 +REFRESH_BUILD=1 + +GS_EXEC="cd $GS_SRC && java -Dnashorn.args=--no-deprecation-warning -jar $BUILD_DIR/server.jar" + +# 0: parallel +# 1: fancy tmux +# 2: tests only +RUN_MODE=0 + +help() +{ + 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 " -e: Write STDERR to 'logs/filename' as well as the terminal."; + echo " -o: Write STDOUT to 'logs/filename' as well as the terminal." +} + +error() +{ + echo "$1"; + exit 1; +} + +rebuild_project() +{ + "$SCRIPT_DIR/build" -qgc +} + +refresh_project() +{ + "$SCRIPT_DIR/build" -qg +} + +verify_binaries_exist() +{ + if [ ! -f "$SCRIPT_DIR/build/server.jar" ]; then + error "Game server binary does not exist."; + fi +} + +run_server_parallel() +{ + if ! [ -x "$(command -v java)" ]; then + error "Java is not installed." + fi + + echo "Running in parallel. All logs redirected to proper files." + + sh -c "$GS_EXEC" & wait +} + +run_server_tmux() +{ + if ! [ -x "$(command -v tmux)" ]; then + error "tmux is not installed. Install tmux or don't use this option." + fi + + tmux new-session "$GS_EXEC" +} + +run_server_tests() +{ + cd "$GS_SRC" || error "Could not change to game server source directory." + sh mvnw test +} + +while getopts ":hqrtxe:o:" arg; do + case $arg in + h) + help + exit 0 + ;; + q) + REFRESH_BUILD=0 + ;; + r) + FORCE_REBUILD=1 + ;; + t) + RUN_MODE=2 + ;; + o) + GS_EXEC="$GS_EXEC > >(tee $LOG_DIR/${OPTARG})" + ;; + e) + GS_EXEC="$GS_EXEC 2> >(tee $LOG_DIR/${OPTARG})" + ;; + x) + RUN_MODE=1 + ;; + *) + error "Argument -$arg is not a known or valid argument." + ;; + esac +done + +if [ ! -d $LOG_DIR ]; then + mkdir -p $LOG_DIR +fi + +if [ ! -d $GS_SRC/target ]; then + rebuild_project || error "Failed to perform a clean build." +else + if [ $FORCE_REBUILD -eq 1 ]; then + rebuild_project || error "Failed to perform a clean build." + else + if [ $REFRESH_BUILD -eq 1 ]; then + refresh_project || error "Failed to perform an incremental build." + fi + fi +fi; + +cd "$SCRIPT_DIR/builddir" || error "Failed to navigate to the build directory." + +case $RUN_MODE in + 0) + # The trap will allow the script to kill the background + # Java instances upon exiting. + trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT + + run_server_parallel + ;; + 1) + run_server_tmux + ;; + 2) + run_server_tests + ;; +esac diff --git a/run-server.bat b/run-server.bat new file mode 100644 index 000000000..871c0d2f3 --- /dev/null +++ b/run-server.bat @@ -0,0 +1,14 @@ +@echo off +cd Server + +if NOT exist hasRan.txt ( + .\mvnw.cmd clean + copy NUL hasRan.txt + .\mvnw.cmd package -DskipTests + xcopy /Y target\*-with-dependencies.jar server.jar* + java -jar server.jar +) ELSE ( + .\mvnw.cmd package -DskipTests + xcopy /Y target\*-with-dependencies.jar server.jar* + java -jar server.jar +) \ No newline at end of file diff --git a/run.sh b/run.sh deleted file mode 100755 index 6d3861c11..000000000 --- a/run.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -echo "Make sure you have built the server before running this command." -if [ $# -eq 0 ]; then - echo "Usage: $0 [--build] type" - echo "Example: $0 ms" - echo "Example: $0 server Server/worldprops/default.json" - echo "Example: $0 client" - echo "Example: $0 --build server Server/worldprops/default.json" - exit -fi -readIndex=1 -propsFile="$1" -doBuild=false -function run_server { - Server/bin/Server "$1" -} -run_ms(){ - exec "Management-Server/bin/Management-Server" -} -run_client(){ - exec "Client/bin/Client" -} -if [ "$1" = "--build" ]; then - readIndex="$((readIndex + 1))" - doBuild=true - if [ $# -lt 2 ]; then - echo "You must supply a type." - echo "If you just want to build without running, use build.sh" - exit - fi -fi - -type="${!readIndex}" -if [ $doBuild = true ]; then - ./build.sh "$type" -fi -if [ "$type" = "server" ]; then - readIndex="$((readIndex + 1))" - propsFile="${!readIndex}" - echo $propsFile - run_server "$propsFile" -elif [ "$type" = "ms" ]; then - run_ms -elif [ "$type" = "client" ]; then - run_client -fi diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 5251a7a7a..000000000 --- a/settings.gradle +++ /dev/null @@ -1,5 +0,0 @@ -rootProject.name = 'RS09' -include 'Client' -include 'Management-Server' -include 'Server' -include 'File-Server' \ No newline at end of file diff --git a/telecoordinates b/telecoordinates deleted file mode 100644 index 1c21db96c..000000000 --- a/telecoordinates +++ /dev/null @@ -1,1326 +0,0 @@ -Big water fall ::tele 2534,3511 -Very back of wild ::tele 3100,3957 -Varrock east bank ::tele 3250,3423 -Varrock ::tele 3210,3424 -Falador ::tele 2964,3378 -Lumbridge ::tele 3222,3218 -Camelot ::tele 2757,3477 -East Ardougne ::tele 2662,3305 -West Ardougne ::tele 2529,3307 -King Lathas Training Grounds ::tele 2516,3369 -Tree Gnome Stronghold ::tele 2461,3443 -Al Kharid ::tele 3293,3174 -Shantays Pass ::tele 3304,3116 -Kalphite Lair ::tele 3226,3107 -Pyramid ::tele 3233,2902 -Pollnivneach ::tele 3359,2910 -Menaphos/Sophanem ::tele 3274,2784 -Yanille ::tele 2606,3093 -Gul'Tanoth ::tele 2516,3044 -Tutorial Island ::tele 3094,3107 -Barbarian Village ::tele 3082,3420 -Entrana ::tele 2834,3335 -Heroes Guild ::tele 2902,3510 -Rangers Guild ::tele 2658,3439 -Coal Trucks ::tele 2582,3481 -Goblin Village ::tele 2956,3506 -Druids Circle ::tele 2926,3482 -Burthorpe ::tele 2926,3559 -White wolf mountain ::tele 2848,3498 -Catherby ::tele 2813,3447 -Seers village ::tele 2708,3492 -Fishing guild ::tele 2603,3414 -Barbarian Agility Course ::tele 2541,3546 -Prifddinas ::tele 2242,3278 -Elf camp (Tirannwn) ::tele 2197,3252 -Isafdar (Tirannwn) ::tele 2241,3238 -Duel arena ::tele 3360,3213 -Desert mining camp ::tele 3286,3023 -Bedabin camp ::tele 3171,3026 -Bandit camp ::tele 3176,2987 -Sophanem ::tele 3305,2755 -Ruins of Uzer ::tele 3490,3090 -Mort'ton ::tele 3489,3288 -Canifis ::tele 3506,3496 -Port Phasmatys ::tele 3687,3502 -Fenkenstrain's castle ::tele 3550,3548 -Dig site ::tele 3354,3402 -Exam centre ::tele 3354,3344 -Edgeville ::tele 3093,3493 -Crafting guild ::tele 2933,3285 -Port Sarim ::tele 3023,3208 -Rimmington ::tele 2957,3214 -Draynor village ::tele 3093,3244 -Fight arena ::tele 2585,3150 -Tree gnome village ::tele 2525,3167 -Port Khazard ::tele 2665,3161 -Monastery ::tele 3051,3490 -Karamja ::tele 2948,3147 -Crandor ::tele 2851,3238 -King Black Dragon Lair ::tele 2717,9816 -KQ Lair ::tele 3487,9493 -Underground pass level 2 ::tele 2337,9798 -Weird water place ::tele 2676,3008 -Members Karajama ::tele 2815,3182 -Grave island ::tele 3504,3575 -HAM camp ::tele 3165,9629 -Pyramid under Sophanem ::tele 3277,9171 -Juna the snake (Press Burst of Strength a few times to get to it) ::tele 3251,9517 -Smoky tunnels/dark room (This place needs more tests) 3206,9379 -Underground pass level 1 ::tele 2495,9715 -Mage Arena ::tele 3107,3937 -Ape Atoll ::tele 2755,2784 -Jungle demon area bottom level ::tele 2714,9183 -Jungle demon area top level (use Burst of Strength) ::tele 2703,9178 -Wilderness Agility Course ::tele 3003,3934 -Keldagram ::tele 2937,9999 (and run north for a little while) -(Alternate) Keldagram, if you tele to 2937,3763 then Press Clarity of Thought then you will be able to walk around in Keldagrim! -Agility arena on Karamja ::tele 2761,9557 -Metal dragon dungeon (back) ::tele 2713,9459 -Metal dragon dungeon (front) ::tele 2713,9564 -Inside the desert treasure pyramid ::tele 3233,9315 -Tree gnome hanger ::tele 2390,9886 -Fishing platform ::tele 2782,3273 -Boat crash island place ::tele 2795,3321 -Legends quest jungle dungeon ::tele 2772,9341 -TzHaar ::tele 2480,5175 -Essence mine ::tele 2911,4832 -Morings end part 1 mine ::tele 2044,4649 -Dwarf cut scene meeting area ::tele 2035,4529 -New KBD lair ::tele 2273,4695 -Elven forest cut scene area ::tele 2309,4597 -Gold mine rock ::tele 2358,4959 -White knight tasks area ::tele 2443,4956 -Rouge maze (Press Burst of Strength) ::tele 3050,5071 -The shadow guys dungeon ::tele 2738,5081 -Barrows top ::tele 3564,3288 -Barrows tunnel ::tele 3568,9695 (Press Burst of Strength a few times to get to the barrows bros coffins) -Barrows chest ::tele 3551,9694 -CW bank= 2441,3090 -CW center= 2400,3103 -Blue dragons= 2910,9801 -Black demons= 2860 9775 -Hell Hounds= 2867,9844 -Black dragons= 2829,9826 -Chaos rune crafting alter ::tele 2269,4843 -Legends dungeon prt2 ::tele 2375,4705 -Mage arena dungeon ::tele 2519,4719 -Evil Bobs island "scaperune" ::tele 2525,4776 -Distant kingdom (alive) 2576,4655 -Distant kingdom (dead) 2803,4723 -Forester random event ::tele 2602,4775 -Alt area ::tele 2527,4547 -Fremmy maze ::tele 2652,9999 -Death rune altar ::tele 2207,4836 -Cosmic alter ::tele 2162,4833 -Middle of under ground pass (Press Burst of Strength) ::tele 2168 4726 -Quiz random event ::tele 1952,4768 -Trawler boat ::tele 2013,4820 -Sunk trawler mini game boat ::tele 1951,4825 -Trawler boat empty of water ::tele 1886,4830 -Mime stage ::tele 2008,4762 -"Draynor" ::tele 2130,4913 -Game room ::tele 2196,4961 -Mind alter ::tele 2796,4818 -Air Alter ::tele 2845,4832 -Water Alter ::tele 2713,4836 -Earth Alter ::tele 2660,4839 -Fire Alter ::tele 2584,4836 -Body Alter ::tele 2527,4833 -Law Alter ::tele 2464,4834 -Nature Alter ::tele 2398,4841 -Gas hole ::tele 2464,4782 -Edge of maze ::tele 2885,4550 -Center of maze ::tele 2912,4576 -Falador mine ::tele 3038,9800 -2867 9955 - Ice Queen Lair -3429 3538 - Slayer Tower -2710 9466 - Brimhaven Dungeon -2856 3812 - Ice Path -2480 3437 - Gnome Agility Training -3281 2765 - Pyramid Plunder room -3345 3251 - Duel Arena -2717 9808 - King Black Dragon Cave -3044 4973 - Rouges Den -2979 3611 - Green Dragons -Big water fall ::tele 2534,3511 -Very back of wild ::tele 3100,3957 -Varrock east bank ::tele 3250,3423 -Varrock ::tele 3210,3424 -Falador ::tele 2964,3378 -Lumbridge ::tele 3222,3218 -Camelot ::tele 2757,3477 -East Ardougne ::tele 2662,3305 -West Ardougne ::tele 2529,3307 -King Lathas Training Grounds ::tele 2516,3369 -Tree Gnome Stronghold ::tele 2461,3443 -Al Kharid ::tele 3293,3174 -Shantays Pass ::tele 3304,3116 -Kalphite Lair ::tele 3226,3107 -Pyramid ::tele 3233,2902 -Pollnivneach ::tele 3359,2910 -Menaphos/Sophanem ::tele 3274,2784 -Yanille ::tele 2606,3093 -Gul'Tanoth ::tele 2516,3044 -Tutorial Island ::tele 3094,3107 -Barbarian Village ::tele 3082,3420 -Entrana ::tele 2834,3335 -Heroes Guild ::tele 2902,3510 -Rangers Guild ::tele 2658,3439 -Coal Trucks ::tele 2582,3481 -Goblin Village ::tele 2956,3506 -Druids Circle ::tele 2926,3482 -Burthorpe ::tele 2926,3559 -White wolf mountain ::tele 2848,3498 -Catherby ::tele 2813,3447 -Seers village ::tele 2708,3492 -Fishing guild ::tele 2603,3414 -Barbarian Agility Course ::tele 2541,3546 -Prifddinas ::tele 2242,3278 -Elf camp (Tirannwn) ::tele 2197,3252 -Isafdar (Tirannwn) ::tele 2241,3238 -Duel arena ::tele 3360,3213 -Desert mining camp ::tele 3286,3023 -Bedabin camp ::tele 3171,3026 -Bandit camp ::tele 3176,2987 -Sophanem ::tele 3305,2755 -Ruins of Uzer ::tele 3490,3090 -Mort'ton ::tele 3489,3288 -Canifis ::tele 3506,3496 -Port Phasmatys ::tele 3687,3502 -Fenkenstrain's castle ::tele 3550,3548 -Dig site ::tele 3354,3402 -Exam centre ::tele 3354,3344 -Edgeville ::tele 3093,3493 -Crafting guild ::tele 2933,3285 -Port Sarim ::tele 3023,3208 -Rimmington ::tele 2957,3214 -Draynor village ::tele 3093,3244 -Fight arena ::tele 2585,3150 -Tree gnome village ::tele 2525,3167 -Port Khazard ::tele 2665,3161 -Monastery ::tele 3051,3490 -Karamja ::tele 2948,3147 -Crandor ::tele 2851,3238 -King Black Dragon Lair ::tele 2717,9816 -KQ Lair ::tele 3487,9493 -Underground pass level 2 ::tele 2337,9798 -Weird water place ::tele 2676,3008 -Members Karajama ::tele 2815,3182 -Grave island ::tele 3504,3575 -HAM camp ::tele 3165,9629 -Pyramid under Sophanem ::tele 3277,9171 -Juna the snake ::tele 3251,9517 -Smoky tunnels/dark room (This place needs more tests) 3206,9379 -Underground pass level 1 ::tele 2495,9715 -Mage Arena ::tele 3107,3937 -Ape Atoll ::tele 2755,2784 -Jungle demon area bottom level ::tele 2714,9183 -Jungle demon area top level (use Burst of Strength) ::tele 2703,9178 -Wilderness Agility Course ::tele 3003,3934 -Keldagram ::tele 2937,9999 -Agility arena on Karamja ::tele 2761,9557 -Metal dragon dungeon (back) ::tele 2713,9459 -Metal dragon dungeon (front) ::tele 2713,9564 -Inside the desert treasure pyramid ::tele 3233,9315 -Tree gnome hanger ::tele 2390,9886 -Fishing platform ::tele 2782,3273 -Boat crash island place ::tele 2795,3321 -Legends quest jungle dungeon ::tele 2772,9341 -TzHaar ::tele 2480,5175 -Essence mine ::tele 2911,4832 -Morings end part 1 mine ::tele 2044,4649 -Dwarf cut scene meeting area ::tele 2035,4529 -New KBD lair ::tele 2273,4695 -Elven forest cut scene area ::tele 2309,4597 -Gold mine rock ::tele 2358,4959 -White knight tasks area ::tele 2443,4956 -Rouge maze (Press Burst of Strength) ::tele 3050,5071 -The shadow guys dungeon ::tele 2738,5081 -Barrows top ::tele 3564,3288 -Barrows tunnel ::tele 3568,9695 -Barrows chest ::tele 3551,9694 -CW bank= 2441,3090 -CW center= 2400,3103 -Blue dragons= 2910,9801 -Black demons= 2860 9775 -Hell Hounds= 2867,9844 -Black dragons= 2829,9826 -Chaos rune crafting alter ::tele 2269,4843 -Legends dungeon prt2 ::tele 2375,4705 -Mage arena dungeon ::tele 2519,4719 -Evil Bobs island "scaperune" ::tele 2525,4776 -Distant kingdom (alive) 2576,4655 -Distant kingdom (dead) 2803,4723 -Forester random event ::tele 2602,4775 -Alt area ::tele 2527,4547 -Fremmy maze ::tele 2652,9999 -Death rune altar ::tele 2207,4836 -Cosmic alter ::tele 2162,4833 -Middle of under ground pass ::tele 2168 4726 -Quiz random event ::tele 1952,4768 -Trawler boat ::tele 2013,4820 -Sunk trawler mini game boat ::tele 1951,4825 -Trawler boat empty of water ::tele 1886,4830 -Mime stage ::tele 2008,4762 -"Draynor" ::tele 2130,4913 -Game room ::tele 2196,4961 -Mind alter ::tele 2796,4818 -Air Alter ::tele 2845,4832 -Water Alter ::tele 2713,4836 -Earth Alter ::tele 2660,4839 -Fire Alter ::tele 2584,4836 -Body Alter ::tele 2527,4833 -Law Alter ::tele 2464,4834 -Nature Alter ::tele 2398,4841 -Gas hole ::tele 2464,4782 -Edge of maze ::tele 2885,4550 -Center of maze ::tele 2912,4576 -Falador mine ::tele 3038,9800 -::tele 3157 4822 = drill deamon camp - -::tele 2477 4768 = frog cave - -::tele 2596 4780 = freaky forester - -::tele 2095 4428 = prison pete - -::tele 1952 4764 = quiz show - -::tele 2338 4747 = Lost and found - -::tele 1960 4824 = Twarler Mini-Game - -::tele 2015 4826 = twarler mini-game inside - -::tele 1971 5001 = WIERD CHRUCH! NOT ON RUNESCAPE! - -::tele 1928 5002 = Grave digger - -::tele 2584 4838 = fire alter - -::tele 2966 9633 = somekind of rat-hole never seen this - -::tele 2929 9649 = dragon slayer mace - -::tele 2907 9705 = quest place (dont remember (or newer seen) what quest) - -::tele 2981 9914 = recipes goblin cook place - -::tele 2916 9912 = heroes guild cave - -::tele 2932 9848 = chaos druids - -::tele 2898 9766 = baby bluedragons - -::tele 3048 9582 = ice-warriors & giants - -::tele 2977 9515 = recipes crabs - -::tele 2973 9505 = recipe water "mini-quest" - -::tele 2783 9574 = agility arena - -::tele 2835 9562 = karamja lessers - -::tele 1891 4947 = quest place (dont remember (or newer seen) what quest) - -::tele 2007 4431 = x-mas workshop event wooot! - -::tele 2400 4850 = nature altar! - -::tele 3421 9622 = quest place (dont remember (or newer seen) what quest) - -::tele 1865 5330 = recipe the lum kitchen when started recipe near the secret sky must check if it is possible to see in rs. - -::tele 1860 5345 = same as above but no barrier & no chairs expet one why? - -::tele 2393 4713 = i don't know prob: quest place (dont remember (or newer seen) what quest) - -::tele 2385 4685 = have a relation to the place above what is that? - -::tele 2525 4776 = evil bob's island - -::tele 2633 4700 = quest place (dont remember (or newer seen) what quest) - -::tele 2650 4565 = monkey sadness fight the jungle demon? - -::tele 2650 4508 = monkey sadness the gnome glider puzzle! - -::tele 3300 9825 = A Soul's Bane quest - -::tele 3345 9715 = mage training arena (telegrab) ftw - -::tele 3350 9680 = mage training arena (telegrab) ftw another one - -::tele 3345 9712 = mage training arena (telegrab) ftw another one NOTE: if you see the same arenas try changin height level! - -::tele 2152 5095 = Secret island - -::tele 2721 4828 = water alter - -::tele 2784 4840 = mind alter - -::tele 2842 4835 = air alter - -::tele 2898 4819 = rune essent mining - -::tele 3029 4834 = abyss - -::tele 2528 4833 = wierd alter - -::tele 2471 4838 = law alter - -::tele 1823 4835 = two ships - -::tele 3105 3930 = mage arena - -::tele 1894 5333 = secret sky - -::tele 1865 5341 = recipie for desaster feast - -::tele 3424 9891 = under passage way to canfis - -::tele 3681 9889 = ecto plasm floor - -::tele 2470 9899 = under gnomes grand tree - -::tele 2319 9804 = i'bans trap drop - -::tele 2856 3809 = slippery ice path - -::tele 3551 9693 = barrows chest - -::tele 3177 9753 = under draynore mannor - -::tele 3040 9741 = faladore mining guild - -::tele 3079 9505 = toturial mining - -::tele 3110 9512 = toturial combat train - -::tele 3232 2896 = top of piramid for desert tresure - -::tele 3291 2764 = desert city - -::tele 3298 9179 = under desert city - -::tele 2861 3165 = volcano - -::tele 2863 9570 = under volcano - -::tele 2835 3268 = candor - -::tele 2766 3277 = fishing docks - -::tele 2446 10147 = daganoth cave ( go to "::tele 2446 9999" then keep walking the right direction then ul get there ) - -::tele 2438 5172 = volcano lava - -::tele 2395 5154 = fight pits - -::tele 3234 9896 = wierd place - -::tele 3132 9909 = under edgevill - -::tele 3233 9313 = under piramid at acent alter - -::tele 3363 9639 = mage train up ( shapes ) - -::tele 3363 9637 = mage train up ( bones ) - -::tele 3365 9637 = mage train up ( alching ) - -::tele 3363 2984 = a desert city - -::tele 2396 5093 = fight caves - -::tele 3559 9895 = football traning corse - -::tele 3541 9891 = jumping corse - -::tele 3528 9871 = zip line - -::tele 3539 9872 = jumping steps - -::tele 3555 9947 = cave - -::tele 2972 9507 = Under water - -::tele 2545 4715 = mage arena room - -::tele 2505 4707 = mage arena statues - -::tele 3551 9712 = barrows coffins - -::tele 2716 9816 = black king dragon - -::tele 3487 9493 = kaphite queen place - -::tele 2744 3444 = camalot flax - -::tele 2273 4695 = king black drags - -::tele 2516 4646 = daganoth quest place mother - -::tele 2121 4918 = Draynor private from map - -::tele 2142 4834 = cosmic alter - -::tele 2134 4717 = slave cages ( look up to see them,,cannot be walked on ) - -::tele 2091 3210 = wierd grass land?? - - -/*Guilds*/ -rangeguild = 2657 3439 -rangingguild = 2657 3439 - -fishguild = 2611 3393 -fishingguild = 2611 3393 - -mineguild = 3016 3339 -miningguild = 3016 3339 - -craftguild = 2933 3289 -craftingguild = 2933 3289 - -heroguild = 2902 3510 -herosguild = 2902 3510 -heroesguild = 2902 3510 - -/*End Guilds*/ - -crystalchest = 2914 3450 - -/*Altars*/ -deathaltar = 2207 4836 -cosmicaltar = 2162 4833 -chaosaltar = 2269 4843 -mindaltar = 2796 4818 -airaltar = 2845 4832 -wateraltar = 2713 4836 -earthaltar = 2660 4839 -firealtar = 2584 4836 -bodyaltar = 2527 4833 -lawaltar = 2464 4834 -naturealtar = 2398 4841 - -/*End Altars*/ - -falador = 2965 3380 -goblinvillage = 2956 3506 -druidscircle = 2926 3482 -burthrope = 2926 3561 - -whitewolf = 2848 3498 -catherby = 2815 3447 -camelotcastle = 2758 3495 -seersvillage = 2708 3492 - - -barbagil = 2541 3546 -grandtree1 = 2480 3488 -grandtree2 = 2466 3490 -treegnome = 2441 3429 -westardy = 2535 3305 -eastardy = 2659 3308 -prifddinas = 2242 3278 -elfcamp = 2197 3252 -isafdar = 2241 3238 -duelarena = 3360 3213 -alkharid = 3288 3189 -desertmine = 3286 3023 -bedabincamp = 3171 3026 -banditcamp = 3176 2987 -pollnivneach = 3365 2970 -pyramid = 3233 2901 -sophanem = 3305 2755 -uzer = 3490 3090 -mortton = 3489 3288 -canifis = 3506 3496 -phasmatys = 3687 3502 -fenkencastle = 3550 3548 -digsite = 3354 3402 -examcentre = 3354 3344 -varrock = 3214 3424 -barbvillage = 3081 3424 -edgeville = 3093 3493 - -sarim = 3023 3208 -rimmington = 2957 3214 -draynor = 3093 3244 -lumbridge = 3223 3218 -fightarena = 2585 3150 -gnomevillage1 = 2521 3177 -gnomevillage2 = 2525 3167 -khazard = 2665 3161 -yanille = 2569 3098 -monestary = 3051 3490 -karamja = 2814 3182 -crandor = 2851 3238 -tutisland = 3123 3100 -tzhaar = 2480 5175 -mineess = 2911 4832 -bobsiland = 2526 4777 - -membersdungeon = 2884 9798 -dramentree = 2861 9736 -karamjadungeon = 2843 9636 -steeldragons = 2719 9433 -bluedragons = 2591 9439 -theogre = 2584 9737 -shadowwarrior = 2705 9758 -subwhitewolf = 2822 9882 -gloryfountain = 2918 9895 -spampdungeon = 3168 9572 -barrowscenter = 3551 9693 -canifis = 3477 9845 -werewol***il = 3549 9865 -eledungeon = 2715 9889 -blurite = 3007 9550 -miniagil = 2759 9557 -runerocks = 3061 3886 -kalphites = 3485 9509 -smokedungeon = 3207 9377 -pyramid4 = 3233 9313 -crashisland = 2893 2724 -apeatoll = 2801 2704 -marimpray = 2807 9201 -marimcreate = 2764 9164 -apeatolldungeon = 2805 9142 -kharazi = 2822 2911 -jungledemon = 2657 4568 -hangar = 2584 4516 -gnomedungeon = 2596 4435 -purplecrystal = 2792 4427 -submage = 2538 4717 -godpray = 2507 4718 -lighttower = 2519 4642 -bigdwarfs = 2776 10162 -bigdwarfs2 = 2838 10126 -bigdwarfs3 = 2923 10191 -waterfall = 2534 3511 -endwild = 3100 3957 -eastbank = 3250 3423 -varrock = 3210 3424 -falador2 = 2964 3378 -lumbridge = 3222 3218 -camelot = 2757 3477 -eardy2 = 2662 3305 -wardy2 = 2529 3307 -lathas = 2516 3369 -gnomestrong = 2461 3443 -kharid = 3293 3174 -shantay = 3304 3116 -kalphlair = 3226 3107 -pyramid2 = 3233 2902 -pollnivneach = 3359 2910 -menaphos = 3274 2784 -yanille = 2606 3093 -gultanoth = 2516 3044 -tutisland2 = 3094 3107 -barbarins = 3082 3420 -entrana = 2834 3335 - -coaltrucks = 2582 3481 -gvillage = 2956 3506 -stonehenge2 = 2926 3482 -burth = 2926 3559 -wwolf = 2848 3498 -catherb = 2813 3447 -seers = 2708 3492 -barbyagil = 2541 3546 -prifdinas = 2242 3278 -elfcamp2 = 2197 3252 -isafdar2 = 2241 3238 -darena = 3360 3213 -dminecamp = 3286 3023 -bedcamp = 3171 3026 -bandcamp = 3176 2987 -soph = 3305 2755 -uzerruins = 3490 3090 -morton = 3489 3288 -canif = 3506 3496 -portp = 3687 3502 -fenken = 3550 3548 -dsite = 3354 3402 -ecentre = 3354 3344 -edgeville = 3093 3493 - -psarim = 3023 3208 -rimm = 2957 3214 -farena = 2585 3150 -tgvill = 2525 3167 -pkhazard = 2665 3161 -monkies = 3051 3490 -karamj = 2948 3147 -crandor2 = 2851 3238 -thekbd = 2717 9816 -thebigkq = 3487 9493 -thepass2 = 2337 9798 -wiredwater = 2676 3008 -karamja4memb = 2815 3182 -meetjuna = 3251 9517 -darkroom = 3206 9379 -thepass1 = 2495 9715 -magicarena = 3107 3937 -aatoll = 2755 2784 -jungledemonb = 2714 9183 -jungledemont = 2703 9178 -wagility = 3003 3934 -keldagrim = 2937 9999 -karamjaagil = 2761 9557 -metaldragback = 2713 9459 -metaldragfront = 2713 9564 -desertpyramid = 3233 9315 -thehangar = 2390 9886 -fishplatform = 2782 3273 -boatcrash = 2795 3321 -legendsjungle = 2772 9341 -tzhaar2 = 2480 5175 -mineess2 = 2911 4832 -morningsend = 2044 4649 -dwarfs = 2035 4529 -kbdlair = 2273 4695 -elvenforest = 2309 4597 -goldmine = 2358 4959 -initiate = 2443 4956 -shadowguys = 2738 5081 -barrowstop = 3564 3288 -barrowstunnel = 3568 9695 -barrowschest = 3551 9694 -cwbank = 2441 3090 -castlewars = 2400 3103 -bluedrags = 2910 9801 -blackdemons = 2860 9775 -hellhounds = 2867 9844 -blackdrags = 2829 9826 - -legends2 = 2377 4705 -magearena = 2519 4719 -scaperune = 2525 4776 -forester = 2602 4775 -theisland = 2527 4547 -fremmymaze = 2652 9999 - -quizgame = 1952 4768 -trawler = 2013 4820 -sunktrawler = 1951 4825 -emptytrawler = 1886 4830 -mimestage = 2008 4762 -draynor = 2130 4913 -gameroom = 2196 4961 -gashole = 2464 4782 -shilo = 2827 2995 -karamja1 = 2973 2890 -karamja2 = 2973 2910 -greater = 3288 3886 -wildpalace = 3288 3886 -slayertower = 3428 3535 -misc = 2512 3860 -shilo = 2852 2955 - -party = 2739 3472 -rellaka = 2659 3661 -trollheim = 2910 3612 -tears of guthix = 3257 9517 - -Pest Control Game: X 2667 Y 2594 -Pest Control Bank: X 2658 Y 2649 - -2867 9955 - Ice Queen Lair -3429 3538 - Slayer Tower -2710 9466 - Brimhaven Dungeon -2856 3812 - Ice Path -2480 3437 - Gnome Agility Training -3281 2765 - Pyramid Plunder room -3345 3251 - Duel Arena -2717 9808 - King Black Dragon Cave -3044 4973 - Rouges Den -2979 3611 - Green Dragons -Big water fall ::tele 2534,3511 -Very back of wild ::tele 3100,3957 -Varrock east bank ::tele 3250,3423 -Varrock ::tele 3210,3424 -Falador ::tele 2964,3378 -Lumbridge ::tele 3222,3218 -Camelot ::tele 2757,3477 -East Ardougne ::tele 2662,3305 -West Ardougne ::tele 2529,3307 -King Lathas Training Grounds ::tele 2516,3369 -Tree Gnome Stronghold ::tele 2461,3443 -Al Kharid ::tele 3293,3174 -Shantays Pass ::tele 3304,3116 -Kalphite Lair ::tele 3226,3107 -Pyramid ::tele 3233,2902 -Pollnivneach ::tele 3359,2910 -Menaphos/Sophanem ::tele 3274,2784 -Yanille ::tele 2606,3093 -Gul'Tanoth ::tele 2516,3044 -Tutorial Island ::tele 3094,3107 -Barbarian Village ::tele 3082,3420 -Entrana ::tele 2834,3335 -Heroes Guild ::tele 2902,3510 -Rangers Guild ::tele 2658,3439 -Coal Trucks ::tele 2582,3481 -Goblin Village ::tele 2956,3506 -Druids Circle ::tele 2926,3482 -Burthorpe ::tele 2926,3559 -White wolf mountain ::tele 2848,3498 -Catherby ::tele 2813,3447 -Seers village ::tele 2708,3492 -Fishing guild ::tele 2603,3414 -Barbarian Agility Course ::tele 2541,3546 -Prifddinas ::tele 2242,3278 -Elf camp (Tirannwn) ::tele 2197,3252 -Isafdar (Tirannwn) ::tele 2241,3238 -Duel arena ::tele 3360,3213 -Desert mining camp ::tele 3286,3023 -Bedabin camp ::tele 3171,3026 -Bandit camp ::tele 3176,2987 -Sophanem ::tele 3305,2755 -Ruins of Uzer ::tele 3490,3090 -Mort'ton ::tele 3489,3288 -Canifis ::tele 3506,3496 -Port Phasmatys ::tele 3687,3502 -Fenkenstrain's castle ::tele 3550,3548 -Dig site ::tele 3354,3402 -Exam centre ::tele 3354,3344 -Edgeville ::tele 3093,3493 -Crafting guild ::tele 2933,3285 -Port Sarim ::tele 3023,3208 -Rimmington ::tele 2957,3214 -Draynor village ::tele 3093,3244 -Fight arena ::tele 2585,3150 -Tree gnome village ::tele 2525,3167 -Port Khazard ::tele 2665,3161 -Monastery ::tele 3051,3490 -Karamja ::tele 2948,3147 -Crandor ::tele 2851,3238 -King Black Dragon Lair ::tele 2717,9816 -KQ Lair ::tele 3487,9493 -Underground pass level 2 ::tele 2337,9798 -Weird water place ::tele 2676,3008 -Members Karajama ::tele 2815,3182 -Grave island ::tele 3504,3575 -HAM camp ::tele 3165,9629 -Pyramid under Sophanem ::tele 3277,9171 -Juna the snake ::tele 3251,9517 -Smoky tunnels/dark room (This place needs more tests) 3206,9379 -Underground pass level 1 ::tele 2495,9715 -Mage Arena ::tele 3107,3937 -Ape Atoll ::tele 2755,2784 -Jungle demon area bottom level ::tele 2714,9183 -Jungle demon area top level (use Burst of Strength) ::tele 2703,9178 -Wilderness Agility Course ::tele 3003,3934 -Keldagram ::tele 2937,9999 -Agility arena on Karamja ::tele 2761,9557 -Metal dragon dungeon (back) ::tele 2713,9459 -Metal dragon dungeon (front) ::tele 2713,9564 -Inside the desert treasure pyramid ::tele 3233,9315 -Tree gnome hanger ::tele 2390,9886 -Fishing platform ::tele 2782,3273 -Boat crash island place ::tele 2795,3321 -Legends quest jungle dungeon ::tele 2772,9341 -TzHaar ::tele 2480,5175 -Essence mine ::tele 2911,4832 -Morings end part 1 mine ::tele 2044,4649 -Dwarf cut scene meeting area ::tele 2035,4529 -New KBD lair ::tele 2273,4695 -Elven forest cut scene area ::tele 2309,4597 -Gold mine rock ::tele 2358,4959 -White knight tasks area ::tele 2443,4956 -Rouge maze (Press Burst of Strength) ::tele 3050,5071 -The shadow guys dungeon ::tele 2738,5081 -Barrows top ::tele 3564,3288 -Barrows tunnel ::tele 3568,9695 -Barrows chest ::tele 3551,9694 -CW bank= 2441,3090 -CW center= 2400,3103 -Blue dragons= 2910,9801 -Black demons= 2860 9775 -Hell Hounds= 2867,9844 -Black dragons= 2829,9826 -Chaos rune crafting alter ::tele 2269,4843 -Legends dungeon prt2 ::tele 2375,4705 -Mage arena dungeon ::tele 2519,4719 -Evil Bobs island "scaperune" ::tele 2525,4776 -Distant kingdom (alive) 2576,4655 -Distant kingdom (dead) 2803,4723 -Forester random event ::tele 2602,4775 -Alt area ::tele 2527,4547 -Fremmy maze ::tele 2652,9999 -Death rune altar ::tele 2207,4836 -Cosmic alter ::tele 2162,4833 -Middle of under ground pass ::tele 2168 4726 -Quiz random event ::tele 1952,4768 -Trawler boat ::tele 2013,4820 -Sunk trawler mini game boat ::tele 1951,4825 -Trawler boat empty of water ::tele 1886,4830 -Mime stage ::tele 2008,4762 -"Draynor" ::tele 2130,4913 -Game room ::tele 2196,4961 -Mind alter ::tele 2796,4818 -Air Alter ::tele 2845,4832 -Water Alter ::tele 2713,4836 -Earth Alter ::tele 2660,4839 -Fire Alter ::tele 2584,4836 -Body Alter ::tele 2527,4833 -Law Alter ::tele 2464,4834 -Nature Alter ::tele 2398,4841 -Gas hole ::tele 2464,4782 -Edge of maze ::tele 2885,4550 -Center of maze ::tele 2912,4576 -Falador mine ::tele 3038,9800 -::tele 3157 4822 = drill deamon camp - -::tele 2477 4768 = frog cave - -::tele 2596 4780 = freaky forester - -::tele 2095 4428 = prison pete - -::tele 1952 4764 = quiz show - -::tele 2338 4747 = Lost and found - -::tele 1960 4824 = Twarler Mini-Game - -::tele 2015 4826 = twarler mini-game inside - -::tele 1971 5001 = WIERD CHRUCH! NOT ON RUNESCAPE! - -::tele 1928 5002 = Grave digger - -::tele 2584 4838 = fire alter - -::tele 2966 9633 = somekind of rat-hole never seen this - -::tele 2929 9649 = dragon slayer mace - -::tele 2907 9705 = quest place (dont remember (or newer seen) what quest) - -::tele 2981 9914 = recipes goblin cook place - -::tele 2916 9912 = heroes guild cave - -::tele 2932 9848 = chaos druids - -::tele 2898 9766 = baby bluedragons - -::tele 3048 9582 = ice-warriors & giants - -::tele 2977 9515 = recipes crabs - -::tele 2973 9505 = recipe water "mini-quest" - -::tele 2783 9574 = agility arena - -::tele 2835 9562 = karamja lessers - -::tele 1891 4947 = quest place (dont remember (or newer seen) what quest) - -::tele 2007 4431 = x-mas workshop event wooot! - -::tele 2400 4850 = nature altar! - -::tele 3421 9622 = quest place (dont remember (or newer seen) what quest) - -::tele 1865 5330 = recipe the lum kitchen when started recipe near the secret sky must check if it is possible to see in rs. - -::tele 1860 5345 = same as above but no barrier & no chairs expet one why? - -::tele 2393 4713 = i don't know prob: quest place (dont remember (or newer seen) what quest) - -::tele 2385 4685 = have a relation to the place above what is that? - -::tele 2525 4776 = evil bob's island - -::tele 2633 4700 = quest place (dont remember (or newer seen) what quest) - -::tele 2650 4565 = monkey sadness fight the jungle demon? - -::tele 2650 4508 = monkey sadness the gnome glider puzzle! - -::tele 3300 9825 = A Soul's Bane quest - -::tele 3345 9715 = mage training arena (telegrab) ftw - -::tele 3350 9680 = mage training arena (telegrab) ftw another one - -::tele 3345 9712 = mage training arena (telegrab) ftw another one NOTE: if you see the same arenas try changin height level! - -::tele 2152 5095 = Secret island - -::tele 2721 4828 = water alter - -::tele 2784 4840 = mind alter - -::tele 2842 4835 = air alter - -::tele 2898 4819 = rune essent mining - -::tele 3029 4834 = abyss - -::tele 2528 4833 = wierd alter - -::tele 2471 4838 = law alter - -::tele 1823 4835 = two ships - -::tele 3105 3930 = mage arena - -::tele 1894 5333 = secret sky - -::tele 1865 5341 = recipie for desaster feast - -::tele 3424 9891 = under passage way to canfis - -::tele 3681 9889 = ecto plasm floor - -::tele 2470 9899 = under gnomes grand tree - -::tele 2319 9804 = i'bans trap drop - -::tele 2856 3809 = slippery ice path - -::tele 3551 9693 = barrows chest - -::tele 3177 9753 = under draynore mannor - -::tele 3040 9741 = faladore mining guild - -::tele 3079 9505 = toturial mining - -::tele 3110 9512 = toturial combat train - -::tele 3232 2896 = top of piramid for desert tresure - -::tele 3291 2764 = desert city - -::tele 3298 9179 = under desert city - -::tele 2861 3165 = volcano - -::tele 2863 9570 = under volcano - -::tele 2835 3268 = candor - -::tele 2766 3277 = fishing docks - -::tele 2446 10147 = daganoth cave ( go to "::tele 2446 9999" then keep walking the right direction then ul get there ) - -::tele 2438 5172 = volcano lava - -::tele 2395 5154 = fight pits - -::tele 3234 9896 = wierd place - -::tele 3132 9909 = under edgevill - -::tele 3233 9313 = under piramid at acent alter - -::tele 3363 9639 = mage train up ( shapes ) - -::tele 3363 9637 = mage train up ( bones ) - -::tele 3365 9637 = mage train up ( alching ) - -::tele 3363 2984 = a desert city - -::tele 2396 5093 = fight caves - -::tele 3559 9895 = football traning corse - -::tele 3541 9891 = jumping corse - -::tele 3528 9871 = zip line - -::tele 3539 9872 = jumping steps - -::tele 3555 9947 = cave - -::tele 2972 9507 = Under water - -::tele 2545 4715 = mage arena room - -::tele 2505 4707 = mage arena statues - -::tele 3551 9712 = barrows coffins - -::tele 2716 9816 = black king dragon - -::tele 3487 9493 = kaphite queen place - -::tele 2744 3444 = camalot flax - -::tele 2273 4695 = king black drags - -::tele 2516 4646 = daganoth quest place mother - -::tele 2121 4918 = Draynor private from map - -::tele 2142 4834 = cosmic alter - -::tele 2134 4717 = slave cages ( look up to see them,,cannot be walked on ) - -::tele 2091 3210 = wierd grass land?? - - -/*Guilds*/ -rangeguild = 2657 3439 -rangingguild = 2657 3439 - -fishguild = 2611 3393 -fishingguild = 2611 3393 - -mineguild = 3016 3339 -miningguild = 3016 3339 - -craftguild = 2933 3289 -craftingguild = 2933 3289 - -heroguild = 2902 3510 -herosguild = 2902 3510 -heroesguild = 2902 3510 - -/*End Guilds*/ - -crystalchest = 2914 3450 - -/*Altars*/ -deathaltar = 2207 4836 -cosmicaltar = 2162 4833 -chaosaltar = 2269 4843 -mindaltar = 2796 4818 -airaltar = 2845 4832 -wateraltar = 2713 4836 -earthaltar = 2660 4839 -firealtar = 2584 4836 -bodyaltar = 2527 4833 -lawaltar = 2464 4834 -naturealtar = 2398 4841 - -/*End Altars*/ - -falador = 2965 3380 -goblinvillage = 2956 3506 -druidscircle = 2926 3482 -burthrope = 2926 3561 - -whitewolf = 2848 3498 -catherby = 2815 3447 -camelotcastle = 2758 3495 -seersvillage = 2708 3492 - - -barbagil = 2541 3546 -grandtree1 = 2480 3488 -grandtree2 = 2466 3490 -treegnome = 2441 3429 -westardy = 2535 3305 -eastardy = 2659 3308 -prifddinas = 2242 3278 -elfcamp = 2197 3252 -isafdar = 2241 3238 -duelarena = 3360 3213 -alkharid = 3288 3189 -desertmine = 3286 3023 -bedabincamp = 3171 3026 -banditcamp = 3176 2987 -pollnivneach = 3365 2970 -pyramid = 3233 2901 -sophanem = 3305 2755 -uzer = 3490 3090 -mortton = 3489 3288 -canifis = 3506 3496 -phasmatys = 3687 3502 -fenkencastle = 3550 3548 -digsite = 3354 3402 -examcentre = 3354 3344 -varrock = 3214 3424 -barbvillage = 3081 3424 -edgeville = 3093 3493 - -sarim = 3023 3208 -rimmington = 2957 3214 -draynor = 3093 3244 -lumbridge = 3223 3218 -fightarena = 2585 3150 -gnomevillage1 = 2521 3177 -gnomevillage2 = 2525 3167 -khazard = 2665 3161 -yanille = 2569 3098 -monestary = 3051 3490 -karamja = 2814 3182 -crandor = 2851 3238 -tutisland = 3123 3100 -tzhaar = 2480 5175 -mineess = 2911 4832 -bobsiland = 2526 4777 - -membersdungeon = 2884 9798 -dramentree = 2861 9736 -karamjadungeon = 2843 9636 -steeldragons = 2719 9433 -bluedragons = 2591 9439 -theogre = 2584 9737 -shadowwarrior = 2705 9758 -subwhitewolf = 2822 9882 -gloryfountain = 2918 9895 -spampdungeon = 3168 9572 -barrowscenter = 3551 9693 -canifis = 3477 9845 -werewol***il = 3549 9865 -eledungeon = 2715 9889 -blurite = 3007 9550 -miniagil = 2759 9557 -runerocks = 3061 3886 -kalphites = 3485 9509 -smokedungeon = 3207 9377 -pyramid4 = 3233 9313 -crashisland = 2893 2724 -apeatoll = 2801 2704 -marimpray = 2807 9201 -marimcreate = 2764 9164 -apeatolldungeon = 2805 9142 -kharazi = 2822 2911 -jungledemon = 2657 4568 -hangar = 2584 4516 -gnomedungeon = 2596 4435 -purplecrystal = 2792 4427 -submage = 2538 4717 -godpray = 2507 4718 -lighttower = 2519 4642 -bigdwarfs = 2776 10162 -bigdwarfs2 = 2838 10126 -bigdwarfs3 = 2923 10191 -waterfall = 2534 3511 -endwild = 3100 3957 -eastbank = 3250 3423 -varrock = 3210 3424 -falador2 = 2964 3378 -lumbridge = 3222 3218 -camelot = 2757 3477 -eardy2 = 2662 3305 -wardy2 = 2529 3307 -lathas = 2516 3369 -gnomestrong = 2461 3443 -kharid = 3293 3174 -shantay = 3304 3116 -kalphlair = 3226 3107 -pyramid2 = 3233 2902 -pollnivneach = 3359 2910 -menaphos = 3274 2784 -yanille = 2606 3093 -gultanoth = 2516 3044 -tutisland2 = 3094 3107 -barbarins = 3082 3420 -entrana = 2834 3335 - -coaltrucks = 2582 3481 -gvillage = 2956 3506 -stonehenge2 = 2926 3482 -burth = 2926 3559 -wwolf = 2848 3498 -catherb = 2813 3447 -seers = 2708 3492 -barbyagil = 2541 3546 -prifdinas = 2242 3278 -elfcamp2 = 2197 3252 -isafdar2 = 2241 3238 -darena = 3360 3213 -dminecamp = 3286 3023 -bedcamp = 3171 3026 -bandcamp = 3176 2987 -soph = 3305 2755 -uzerruins = 3490 3090 -morton = 3489 3288 -canif = 3506 3496 -portp = 3687 3502 -fenken = 3550 3548 -dsite = 3354 3402 -ecentre = 3354 3344 -edgeville = 3093 3493 - -psarim = 3023 3208 -rimm = 2957 3214 -farena = 2585 3150 -tgvill = 2525 3167 -pkhazard = 2665 3161 -monkies = 3051 3490 -karamj = 2948 3147 -crandor2 = 2851 3238 -thekbd = 2717 9816 -thebigkq = 3487 9493 -thepass2 = 2337 9798 -wiredwater = 2676 3008 -karamja4memb = 2815 3182 -meetjuna = 3251 9517 -darkroom = 3206 9379 -thepass1 = 2495 9715 -magicarena = 3107 3937 -aatoll = 2755 2784 -jungledemonb = 2714 9183 -jungledemont = 2703 9178 -wagility = 3003 3934 -keldagrim = 2937 9999 -karamjaagil = 2761 9557 -metaldragback = 2713 9459 -metaldragfront = 2713 9564 -desertpyramid = 3233 9315 -thehangar = 2390 9886 -fishplatform = 2782 3273 -boatcrash = 2795 3321 -legendsjungle = 2772 9341 -tzhaar2 = 2480 5175 -mineess2 = 2911 4832 -morningsend = 2044 4649 -dwarfs = 2035 4529 -kbdlair = 2273 4695 -elvenforest = 2309 4597 -goldmine = 2358 4959 -initiate = 2443 4956 -shadowguys = 2738 5081 -barrowstop = 3564 3288 -barrowstunnel = 3568 9695 -barrowschest = 3551 9694 -cwbank = 2441 3090 -castlewars = 2400 3103 -bluedrags = 2910 9801 -blackdemons = 2860 9775 -hellhounds = 2867 9844 -blackdrags = 2829 9826 - -legends2 = 2377 4705 -magearena = 2519 4719 -scaperune = 2525 4776 -forester = 2602 4775 -theisland = 2527 4547 -fremmymaze = 2652 9999 - -quizgame = 1952 4768 -trawler = 2013 4820 -sunktrawler = 1951 4825 -emptytrawler = 1886 4830 -mimestage = 2008 4762 -draynor = 2130 4913 -gameroom = 2196 4961 -gashole = 2464 4782 -shilo = 2827 2995 -karamja1 = 2973 2890 -karamja2 = 2973 2910 -greater = 3288 3886 -wildpalace = 3288 3886 -slayertower = 3428 3535 -misc = 2512 3860 -shilo = 2852 2955 - -party = 2739 3472 -rellaka = 2659 3661 -trollheim = 2910 3612 -tears of guthix = 3257 9517 - -Pest Control Game: X 2667 Y 2594 -Pest Control Bank: X 2658 Y 2649 diff --git a/test b/test deleted file mode 100644 index 9b79d200a..000000000 --- a/test +++ /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("109343860332353211924482133180092752644123222251570035818592399718899543340247882615012834369709881454157761178411602406231525266135486523035643931381979724348736863559735931163013508521663192459153115214061347330689961869574877386212674606945486858607548180282077056338048348202722564364196572081666257569141") - - //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